aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosh Rahm <rahm@google.com>2022-07-18 19:37:18 +0000
committerJosh Rahm <rahm@google.com>2022-07-18 19:37:18 +0000
commit308e1940dcd64aa6c344c403d4f9e0dda58d9c5c (patch)
tree35fe43e01755e0f312650667004487a44d6b7941
parent96a00c7c588b2f38a2424aeeb4ea3581d370bf2d (diff)
parente8c94697bcbe23a5c7b07c292b90a6b70aadfa87 (diff)
downloadrneovim-308e1940dcd64aa6c344c403d4f9e0dda58d9c5c.tar.gz
rneovim-308e1940dcd64aa6c344c403d4f9e0dda58d9c5c.tar.bz2
rneovim-308e1940dcd64aa6c344c403d4f9e0dda58d9c5c.zip
Merge remote-tracking branch 'upstream/master' into rahm
-rw-r--r--.builds/freebsd.yml19
-rw-r--r--.builds/openbsd.yml10
-rw-r--r--.clang-format55
-rw-r--r--.editorconfig1
-rw-r--r--.git-blame-ignore-revs17
-rw-r--r--.github/labeler.yml9
-rw-r--r--.github/scripts/remove-reviewers.js16
-rw-r--r--.github/scripts/reviews.js94
-rw-r--r--.github/workflows/api-docs-check.yml18
-rw-r--r--.github/workflows/api-docs.yml35
-rw-r--r--.github/workflows/backport.yml8
-rw-r--r--.github/workflows/ci.yml292
-rw-r--r--.github/workflows/commitlint.yml14
-rw-r--r--.github/workflows/coverity-scan.yml8
-rwxr-xr-x.github/workflows/env.sh19
-rw-r--r--.github/workflows/labeler.yml87
-rw-r--r--.github/workflows/notes.md37
-rw-r--r--.github/workflows/release.yml121
-rw-r--r--.github/workflows/remove-reviewers-on-draft.yml17
-rw-r--r--.github/workflows/reviews.yml18
-rw-r--r--.github/workflows/vim-patches.yml8
-rw-r--r--.gitignore1
-rw-r--r--.mailmap134
-rw-r--r--.stylua.toml6
-rw-r--r--.styluaignore3
-rw-r--r--.travis.yml220
-rw-r--r--CMakeLists.txt192
-rw-r--r--CONTRIBUTING.md6
-rw-r--r--LICENSE.txt (renamed from LICENSE)0
-rw-r--r--MAINTAIN.md18
-rw-r--r--Makefile72
-rw-r--r--README.md15
-rwxr-xr-xci/before_cache.sh4
-rw-r--r--ci/build.ps1326
-rw-r--r--ci/common/build.sh11
-rwxr-xr-xci/common/submit_coverage.sh6
-rw-r--r--ci/common/suite.sh74
-rw-r--r--ci/common/test.sh44
-rwxr-xr-xci/install.sh12
-rwxr-xr-xci/run_lint.sh31
-rwxr-xr-xci/run_tests.sh47
-rwxr-xr-xci/script.sh12
-rwxr-xr-xci/snap/deploy.sh4
-rw-r--r--cmake.config/CMakeLists.txt (renamed from config/CMakeLists.txt)25
-rw-r--r--cmake.config/config.h.in (renamed from config/config.h.in)10
-rw-r--r--cmake.config/pathdef.c.in (renamed from config/pathdef.c.in)0
-rw-r--r--cmake.config/versiondef.h.in (renamed from config/versiondef.h.in)0
-rw-r--r--cmake.deps/CMakeLists.txt (renamed from third-party/CMakeLists.txt)89
-rw-r--r--cmake.deps/cmake/BuildGettext.cmake (renamed from third-party/cmake/BuildGettext.cmake)1
-rw-r--r--cmake.deps/cmake/BuildLibiconv.cmake (renamed from third-party/cmake/BuildLibiconv.cmake)1
-rw-r--r--cmake.deps/cmake/BuildLibtermkey.cmake (renamed from third-party/cmake/BuildLibtermkey.cmake)1
-rw-r--r--cmake.deps/cmake/BuildLibuv.cmake (renamed from third-party/cmake/BuildLibuv.cmake)16
-rw-r--r--cmake.deps/cmake/BuildLibvterm.cmake (renamed from third-party/cmake/BuildLibvterm.cmake)1
-rw-r--r--cmake.deps/cmake/BuildLua.cmake (renamed from third-party/cmake/BuildLua.cmake)0
-rw-r--r--cmake.deps/cmake/BuildLuajit.cmake (renamed from third-party/cmake/BuildLuajit.cmake)94
-rw-r--r--cmake.deps/cmake/BuildLuarocks.cmake (renamed from third-party/cmake/BuildLuarocks.cmake)10
-rw-r--r--cmake.deps/cmake/BuildLuv.cmake (renamed from third-party/cmake/BuildLuv.cmake)16
-rw-r--r--cmake.deps/cmake/BuildMsgpack.cmake (renamed from third-party/cmake/BuildMsgpack.cmake)21
-rw-r--r--cmake.deps/cmake/BuildTreesitter.cmake (renamed from third-party/cmake/BuildTreesitter.cmake)1
-rw-r--r--cmake.deps/cmake/BuildTreesitterParsers.cmake (renamed from third-party/cmake/BuildTreesitterParsers.cmake)6
-rw-r--r--cmake.deps/cmake/BuildUnibilium.cmake (renamed from third-party/cmake/BuildUnibilium.cmake)1
-rw-r--r--cmake.deps/cmake/CopyFilesGlob.cmake (renamed from third-party/cmake/CopyFilesGlob.cmake)0
-rw-r--r--cmake.deps/cmake/DownloadAndExtractFile.cmake (renamed from third-party/cmake/DownloadAndExtractFile.cmake)0
-rw-r--r--cmake.deps/cmake/GetBinaryDeps.cmake (renamed from third-party/cmake/GetBinaryDeps.cmake)0
-rw-r--r--cmake.deps/cmake/GettextCMakeLists.txt (renamed from third-party/cmake/GettextCMakeLists.txt)0
-rw-r--r--cmake.deps/cmake/LibiconvCMakeLists.txt (renamed from third-party/cmake/LibiconvCMakeLists.txt)0
-rw-r--r--cmake.deps/cmake/LibuvCMakeLists.txt (renamed from third-party/cmake/LibuvCMakeLists.txt)0
-rw-r--r--cmake.deps/cmake/Libvterm-tbl2inc_c.cmake (renamed from third-party/cmake/Libvterm-tbl2inc_c.cmake)0
-rw-r--r--cmake.deps/cmake/LibvtermCMakeLists.txt (renamed from third-party/cmake/LibvtermCMakeLists.txt)0
-rw-r--r--cmake.deps/cmake/RemoveFiles.cmake (renamed from third-party/cmake/RemoveFiles.cmake)0
-rw-r--r--cmake.deps/cmake/TargetArch.cmake (renamed from third-party/cmake/TargetArch.cmake)0
-rw-r--r--cmake.deps/cmake/TreesitterCMakeLists.txt (renamed from third-party/cmake/TreesitterCMakeLists.txt)0
-rw-r--r--cmake.deps/cmake/TreesitterParserCMakeLists.txt (renamed from third-party/cmake/TreesitterParserCMakeLists.txt)0
-rw-r--r--cmake.deps/cmake/UnibiliumCMakeLists.txt (renamed from third-party/cmake/UnibiliumCMakeLists.txt)0
-rw-r--r--cmake.deps/cmake/libtermkeyCMakeLists.txt (renamed from third-party/cmake/libtermkeyCMakeLists.txt)0
-rw-r--r--cmake.deps/patches/libvterm-Remove-VLAs-for-MSVC.patch (renamed from third-party/patches/libvterm-Remove-VLAs-for-MSVC.patch)0
-rw-r--r--cmake.packaging/CMakeLists.txt64
-rw-r--r--cmake.packaging/WixPatch.xml14
-rw-r--r--cmake.packaging/neovim.icnsbin0 -> 86517 bytes
-rw-r--r--cmake.packaging/neovim.icobin0 -> 122355 bytes
-rw-r--r--cmake.packaging/neovim.pngbin0 -> 6758 bytes
-rw-r--r--cmake.packaging/neovim.svg147
-rw-r--r--cmake/FindLibIntl.cmake13
-rw-r--r--cmake/FindLua.cmake197
-rw-r--r--cmake/FindWinpty.cmake10
-rw-r--r--cmake/GenerateVersion.cmake48
-rw-r--r--cmake/RunTests.cmake5
-rw-r--r--cmake/UninstallHelper.cmake13
-rw-r--r--cmake/UninstallHelper.cmake.in21
-rw-r--r--cmake/Util.cmake145
-rwxr-xr-xcontrib/asan.sh28
-rw-r--r--contrib/flake.lock12
-rw-r--r--contrib/flake.nix4
-rw-r--r--contrib/luarc.json1
-rw-r--r--runtime/autoload/clojurecomplete.vim8
-rw-r--r--runtime/autoload/dist/ft.vim241
-rw-r--r--runtime/autoload/freebasic.vim40
-rw-r--r--runtime/autoload/health.vim16
-rw-r--r--runtime/autoload/health/nvim.vim28
-rw-r--r--runtime/autoload/health/provider.vim61
-rw-r--r--runtime/autoload/man.vim23
-rw-r--r--runtime/autoload/provider/python.vim45
-rw-r--r--runtime/autoload/provider/pythonx.vim20
-rw-r--r--runtime/autoload/python3complete.vim5
-rw-r--r--runtime/colors/README.txt94
-rw-r--r--runtime/colors/blue.vim607
-rw-r--r--runtime/colors/darkblue.vim511
-rw-r--r--runtime/colors/delek.vim453
-rw-r--r--runtime/colors/desert.vim484
-rw-r--r--runtime/colors/elflord.vim489
-rw-r--r--runtime/colors/evening.vim551
-rw-r--r--runtime/colors/industry.vim434
-rw-r--r--runtime/colors/koehler.vim491
-rw-r--r--runtime/colors/morning.vim438
-rw-r--r--runtime/colors/murphy.vim428
-rw-r--r--runtime/colors/pablo.vim453
-rw-r--r--runtime/colors/peachpuff.vim449
-rw-r--r--runtime/colors/ron.vim479
-rw-r--r--runtime/colors/shine.vim455
-rw-r--r--runtime/colors/slate.vim463
-rw-r--r--runtime/colors/torte.vim457
-rw-r--r--runtime/colors/zellner.vim453
-rw-r--r--runtime/compiler/icon.vim33
-rw-r--r--runtime/compiler/jest.vim8
-rw-r--r--runtime/compiler/perl.vim18
-rw-r--r--runtime/compiler/perlcritic.vim27
-rw-r--r--runtime/compiler/podchecker.vim28
-rw-r--r--runtime/compiler/scdoc.vim3
-rw-r--r--runtime/compiler/sml.vim8
-rw-r--r--runtime/delmenu.vim1
-rw-r--r--runtime/doc/api.txt1260
-rw-r--r--runtime/doc/arabic.txt2
-rw-r--r--runtime/doc/autocmd.txt128
-rw-r--r--runtime/doc/builtin.txt9087
-rw-r--r--runtime/doc/change.txt49
-rw-r--r--runtime/doc/channel.txt4
-rw-r--r--runtime/doc/cmdline.txt25
-rw-r--r--runtime/doc/deprecated.txt21
-rw-r--r--runtime/doc/dev_style.txt144
-rw-r--r--runtime/doc/develop.txt15
-rw-r--r--runtime/doc/diagnostic.txt127
-rw-r--r--runtime/doc/diff.txt19
-rw-r--r--runtime/doc/digraph.txt3
-rw-r--r--runtime/doc/editing.txt29
-rw-r--r--runtime/doc/eval.txt8956
-rw-r--r--runtime/doc/filetype.txt88
-rw-r--r--runtime/doc/fold.txt6
-rw-r--r--runtime/doc/ft_ada.txt24
-rw-r--r--runtime/doc/ft_raku.txt24
-rw-r--r--runtime/doc/ft_rust.txt4
-rw-r--r--runtime/doc/ft_sql.txt20
-rw-r--r--runtime/doc/gui.txt47
-rw-r--r--runtime/doc/help.txt1
-rw-r--r--runtime/doc/helphelp.txt2
-rw-r--r--runtime/doc/if_pyth.txt71
-rw-r--r--runtime/doc/indent.txt17
-rw-r--r--runtime/doc/index.txt43
-rw-r--r--runtime/doc/insert.txt66
-rw-r--r--runtime/doc/intro.txt27
-rw-r--r--runtime/doc/lsp.txt676
-rw-r--r--runtime/doc/lua.txt1076
-rw-r--r--runtime/doc/map.txt180
-rw-r--r--runtime/doc/message.txt5
-rw-r--r--runtime/doc/motion.txt10
-rw-r--r--runtime/doc/nvim_terminal_emulator.txt137
-rw-r--r--runtime/doc/options.txt501
-rw-r--r--runtime/doc/pattern.txt94
-rw-r--r--runtime/doc/pi_health.txt24
-rw-r--r--runtime/doc/pi_msgpack.txt2
-rw-r--r--runtime/doc/pi_netrw.txt14
-rw-r--r--runtime/doc/print.txt6
-rw-r--r--runtime/doc/provider.txt26
-rw-r--r--runtime/doc/quickfix.txt83
-rw-r--r--runtime/doc/quickref.txt8
-rw-r--r--runtime/doc/recover.txt2
-rw-r--r--runtime/doc/remote.txt131
-rw-r--r--runtime/doc/repeat.txt29
-rw-r--r--runtime/doc/scroll.txt18
-rw-r--r--runtime/doc/sign.txt2
-rw-r--r--runtime/doc/spell.txt4
-rw-r--r--runtime/doc/starting.txt369
-rw-r--r--runtime/doc/syntax.txt259
-rw-r--r--runtime/doc/tabpage.txt37
-rw-r--r--runtime/doc/tagsrch.txt5
-rw-r--r--runtime/doc/term.txt54
-rw-r--r--runtime/doc/testing.txt2
-rw-r--r--runtime/doc/tips.txt28
-rw-r--r--runtime/doc/treesitter.txt212
-rw-r--r--runtime/doc/uganda.txt11
-rw-r--r--runtime/doc/ui.txt60
-rw-r--r--runtime/doc/undo.txt18
-rw-r--r--runtime/doc/usr_04.txt8
-rw-r--r--runtime/doc/usr_05.txt12
-rw-r--r--runtime/doc/usr_06.txt24
-rw-r--r--runtime/doc/usr_07.txt4
-rw-r--r--runtime/doc/usr_08.txt2
-rw-r--r--runtime/doc/usr_10.txt4
-rw-r--r--runtime/doc/usr_21.txt4
-rw-r--r--runtime/doc/usr_29.txt19
-rw-r--r--runtime/doc/usr_40.txt11
-rw-r--r--runtime/doc/usr_41.txt61
-rw-r--r--runtime/doc/usr_42.txt3
-rw-r--r--runtime/doc/various.txt47
-rw-r--r--runtime/doc/vim_diff.txt117
-rw-r--r--runtime/doc/visual.txt12
-rw-r--r--runtime/doc/windows.txt18
-rw-r--r--runtime/filetype.lua57
-rw-r--r--runtime/filetype.vim292
-rw-r--r--runtime/ftplugin/abaqus.vim8
-rw-r--r--runtime/ftplugin/ant.vim6
-rw-r--r--runtime/ftplugin/aspvbs.vim6
-rw-r--r--runtime/ftplugin/basic.vim46
-rw-r--r--runtime/ftplugin/c.vim5
-rw-r--r--runtime/ftplugin/clojure.vim12
-rw-r--r--runtime/ftplugin/config.vim6
-rw-r--r--runtime/ftplugin/confini.vim10
-rw-r--r--runtime/ftplugin/cpp.vim1
-rw-r--r--runtime/ftplugin/cs.vim44
-rw-r--r--runtime/ftplugin/csc.vim6
-rw-r--r--runtime/ftplugin/csh.vim2
-rw-r--r--runtime/ftplugin/dtd.vim6
-rw-r--r--runtime/ftplugin/elixir.vim11
-rw-r--r--runtime/ftplugin/elm.vim4
-rw-r--r--runtime/ftplugin/fennel.vim18
-rw-r--r--runtime/ftplugin/fetchmail.vim15
-rw-r--r--runtime/ftplugin/fortran.vim8
-rw-r--r--runtime/ftplugin/freebasic.vim76
-rw-r--r--runtime/ftplugin/html.vim6
-rw-r--r--runtime/ftplugin/icon.vim36
-rw-r--r--runtime/ftplugin/java.vim6
-rw-r--r--runtime/ftplugin/jsp.vim6
-rw-r--r--runtime/ftplugin/liquid.vim4
-rw-r--r--runtime/ftplugin/m3build.vim9
-rw-r--r--runtime/ftplugin/m3quake.vim10
-rw-r--r--runtime/ftplugin/man.vim6
-rw-r--r--runtime/ftplugin/masm.vim15
-rw-r--r--runtime/ftplugin/modula3.vim18
-rw-r--r--runtime/ftplugin/pascal.vim2
-rw-r--r--runtime/ftplugin/perl.vim60
-rw-r--r--runtime/ftplugin/php.vim9
-rw-r--r--runtime/ftplugin/pod.vim71
-rw-r--r--runtime/ftplugin/qb64.vim26
-rw-r--r--runtime/ftplugin/query.lua6
-rw-r--r--runtime/ftplugin/reva.vim4
-rw-r--r--runtime/ftplugin/ruby.vim44
-rw-r--r--runtime/ftplugin/scdoc.vim10
-rw-r--r--runtime/ftplugin/sgml.vim6
-rw-r--r--runtime/ftplugin/sh.vim6
-rw-r--r--runtime/ftplugin/spec.vim5
-rw-r--r--runtime/ftplugin/svg.vim6
-rw-r--r--runtime/ftplugin/tap.vim27
-rw-r--r--runtime/ftplugin/tcsh.vim2
-rw-r--r--runtime/ftplugin/tt2html.vim8
-rw-r--r--runtime/ftplugin/vue.vim24
-rw-r--r--runtime/ftplugin/wget.vim29
-rw-r--r--runtime/ftplugin/wget2.vim29
-rw-r--r--runtime/ftplugin/xhtml.vim6
-rw-r--r--runtime/ftplugin/xml.vim2
-rw-r--r--runtime/ftplugin/xs.vim7
-rw-r--r--runtime/ftplugin/xsd.vim6
-rw-r--r--runtime/ftplugin/xslt.vim16
-rw-r--r--runtime/indent/README.txt2
-rw-r--r--runtime/indent/ada.vim3
-rw-r--r--runtime/indent/awk.vim3
-rw-r--r--runtime/indent/basic.vim11
-rw-r--r--runtime/indent/bst.vim6
-rw-r--r--runtime/indent/cdl.vim4
-rw-r--r--runtime/indent/chaiscript.vim3
-rw-r--r--runtime/indent/clojure.vim2
-rw-r--r--runtime/indent/cmake.vim4
-rw-r--r--runtime/indent/cs.vim22
-rw-r--r--runtime/indent/d.vim4
-rw-r--r--runtime/indent/dictconf.vim4
-rw-r--r--runtime/indent/dictdconf.vim2
-rw-r--r--runtime/indent/dtd.vim6
-rw-r--r--runtime/indent/dylan.vim5
-rw-r--r--runtime/indent/falcon.vim3
-rw-r--r--runtime/indent/fennel.vim12
-rw-r--r--runtime/indent/fortran.vim7
-rw-r--r--runtime/indent/freebasic.vim11
-rw-r--r--runtime/indent/gitolite.vim4
-rw-r--r--runtime/indent/haml.vim4
-rw-r--r--runtime/indent/html.vim15
-rw-r--r--runtime/indent/idlang.vim4
-rw-r--r--runtime/indent/julia.vim8
-rw-r--r--runtime/indent/krl.vim130
-rw-r--r--runtime/indent/liquid.vim23
-rw-r--r--runtime/indent/make.vim4
-rw-r--r--runtime/indent/mma.vim3
-rw-r--r--runtime/indent/nginx.vim4
-rw-r--r--runtime/indent/objc.vim6
-rw-r--r--runtime/indent/occam.vim4
-rw-r--r--runtime/indent/perl.vim7
-rw-r--r--runtime/indent/postscr.vim8
-rw-r--r--runtime/indent/pov.vim3
-rw-r--r--runtime/indent/prolog.vim3
-rw-r--r--runtime/indent/qb64.vim (renamed from runtime/indent/systemd.vim)7
-rw-r--r--runtime/indent/query.lua6
-rw-r--r--runtime/indent/ruby.vim48
-rw-r--r--runtime/indent/sas.vim4
-rw-r--r--runtime/indent/sass.vim4
-rw-r--r--runtime/indent/sml.vim5
-rw-r--r--runtime/indent/systemverilog.vim3
-rw-r--r--runtime/indent/testdir/html.in13
-rw-r--r--runtime/indent/testdir/html.ok13
-rw-r--r--runtime/indent/testdir/krl.in148
-rw-r--r--runtime/indent/testdir/krl.ok148
-rw-r--r--runtime/indent/testdir/vim.in6
-rw-r--r--runtime/indent/testdir/vim.ok6
-rw-r--r--runtime/indent/testdir/yaml.in5
-rw-r--r--runtime/indent/testdir/yaml.ok5
-rw-r--r--runtime/indent/tf.vim4
-rw-r--r--runtime/indent/tilde.vim5
-rw-r--r--runtime/indent/treetop.vim9
-rw-r--r--runtime/indent/vim.vim31
-rw-r--r--runtime/indent/xf86conf.vim9
-rw-r--r--runtime/indent/xinetd.vim9
-rw-r--r--runtime/indent/yacc.vim9
-rw-r--r--runtime/indent/yaml.vim40
-rw-r--r--runtime/lua/health.lua29
-rw-r--r--runtime/lua/man.lua28
-rw-r--r--runtime/lua/vim/F.lua10
-rw-r--r--runtime/lua/vim/_editor.lua (renamed from src/nvim/lua/vim.lua)525
-rw-r--r--runtime/lua/vim/_init_packages.lua83
-rw-r--r--runtime/lua/vim/_meta.lua297
-rw-r--r--runtime/lua/vim/compat.lua4
-rw-r--r--runtime/lua/vim/diagnostic.lua571
-rw-r--r--runtime/lua/vim/filetype.lua3671
-rw-r--r--runtime/lua/vim/filetype/detect.lua1630
-rw-r--r--runtime/lua/vim/fs.lua209
-rw-r--r--runtime/lua/vim/health.lua49
-rw-r--r--runtime/lua/vim/highlight.lua134
-rw-r--r--runtime/lua/vim/inspect.lua383
-rw-r--r--runtime/lua/vim/keymap.lua57
-rw-r--r--runtime/lua/vim/lsp.lua985
-rw-r--r--runtime/lua/vim/lsp/_snippet.lua247
-rw-r--r--runtime/lua/vim/lsp/buf.lua545
-rw-r--r--runtime/lua/vim/lsp/codelens.lua66
-rw-r--r--runtime/lua/vim/lsp/diagnostic.lua503
-rw-r--r--runtime/lua/vim/lsp/handlers.lua263
-rw-r--r--runtime/lua/vim/lsp/health.lua17
-rw-r--r--runtime/lua/vim/lsp/log.lua118
-rw-r--r--runtime/lua/vim/lsp/protocol.lua584
-rw-r--r--runtime/lua/vim/lsp/rpc.lua272
-rw-r--r--runtime/lua/vim/lsp/sync.lua107
-rw-r--r--runtime/lua/vim/lsp/tagfunc.lua7
-rw-r--r--runtime/lua/vim/lsp/util.lua929
-rw-r--r--runtime/lua/vim/shared.lua323
-rw-r--r--runtime/lua/vim/treesitter.lua51
-rw-r--r--runtime/lua/vim/treesitter/health.lua14
-rw-r--r--runtime/lua/vim/treesitter/highlighter.lua195
-rw-r--r--runtime/lua/vim/treesitter/language.lua8
-rw-r--r--runtime/lua/vim/treesitter/languagetree.lua177
-rw-r--r--runtime/lua/vim/treesitter/query.lua195
-rw-r--r--runtime/lua/vim/ui.lua41
-rw-r--r--runtime/lua/vim/uri.lua36
-rw-r--r--runtime/menu.vim9
-rw-r--r--runtime/nvim.appdata.xml1
-rw-r--r--runtime/nvim.desktop3
-rw-r--r--runtime/optwin.vim10
-rw-r--r--runtime/pack/dist/opt/matchit/autoload/matchit.vim4
-rw-r--r--runtime/pack/dist/opt/termdebug/plugin/termdebug.vim256
-rw-r--r--runtime/plugin/rplugin.vim4
-rw-r--r--runtime/scripts.vim18
-rw-r--r--runtime/spell/cleanadd.vim32
-rw-r--r--runtime/syntax/apache.vim3
-rw-r--r--runtime/syntax/basic.vim482
-rw-r--r--runtime/syntax/c.vim79
-rw-r--r--runtime/syntax/chordpro.vim170
-rw-r--r--runtime/syntax/clojure.vim42
-rw-r--r--runtime/syntax/coco.vim3
-rw-r--r--runtime/syntax/confini.vim12
-rw-r--r--runtime/syntax/cs.vim276
-rw-r--r--runtime/syntax/debchangelog.vim6
-rw-r--r--runtime/syntax/debcontrol.vim4
-rw-r--r--runtime/syntax/debsources.vim6
-rw-r--r--runtime/syntax/dep3patch.vim4
-rw-r--r--runtime/syntax/dirpager.vim2
-rw-r--r--runtime/syntax/doxygen.vim18
-rw-r--r--runtime/syntax/dts.vim3
-rw-r--r--runtime/syntax/eruby.vim12
-rw-r--r--runtime/syntax/fetchmail.vim110
-rw-r--r--runtime/syntax/freebasic.vim685
-rw-r--r--runtime/syntax/i3config.vim15
-rw-r--r--runtime/syntax/icon.vim336
-rw-r--r--runtime/syntax/initng.vim1
-rw-r--r--runtime/syntax/ipfilter.vim3
-rw-r--r--runtime/syntax/java.vim51
-rw-r--r--runtime/syntax/javascript.vim13
-rw-r--r--runtime/syntax/krl.vim458
-rw-r--r--runtime/syntax/liquid.vim24
-rw-r--r--runtime/syntax/lsp_markdown.vim27
-rw-r--r--runtime/syntax/lua.vim11
-rw-r--r--runtime/syntax/m4.vim6
-rw-r--r--runtime/syntax/man.vim2
-rw-r--r--runtime/syntax/masm.vim3
-rw-r--r--runtime/syntax/mason.vim16
-rw-r--r--runtime/syntax/mupad.vim1
-rw-r--r--runtime/syntax/neomuttrc.vim365
-rw-r--r--runtime/syntax/openscad.vim129
-rw-r--r--runtime/syntax/perl.vim420
-rw-r--r--runtime/syntax/plsql.vim940
-rw-r--r--runtime/syntax/pod.vim139
-rw-r--r--runtime/syntax/qb64.vim409
-rw-r--r--runtime/syntax/query.lua6
-rw-r--r--runtime/syntax/rc.vim21
-rw-r--r--runtime/syntax/ruby.vim16
-rw-r--r--runtime/syntax/sass.vim3
-rw-r--r--runtime/syntax/scala.vim110
-rw-r--r--runtime/syntax/scdoc.vim63
-rw-r--r--runtime/syntax/sendpr.vim4
-rw-r--r--runtime/syntax/sh.vim50
-rw-r--r--runtime/syntax/sml.vim7
-rw-r--r--runtime/syntax/strace.vim3
-rw-r--r--runtime/syntax/structurizr.vim9
-rw-r--r--runtime/syntax/synload.vim2
-rw-r--r--runtime/syntax/syntax.vim2
-rw-r--r--runtime/syntax/tap.vim23
-rw-r--r--runtime/syntax/tar.vim2
-rw-r--r--runtime/syntax/tex.vim106
-rw-r--r--runtime/syntax/tmux.vim127
-rw-r--r--runtime/syntax/trustees.vim4
-rw-r--r--runtime/syntax/tt2.vim17
-rw-r--r--runtime/syntax/tt2html.vim8
-rw-r--r--runtime/syntax/tt2js.vim8
-rw-r--r--runtime/syntax/vim.vim44
-rw-r--r--runtime/syntax/webmacro.vim6
-rw-r--r--runtime/syntax/wget.vim340
-rw-r--r--runtime/syntax/wget2.vim250
-rw-r--r--runtime/syntax/xs.vim13
-rw-r--r--runtime/tutor/en/vim-01-beginner.tutor165
-rw-r--r--runtime/tutor/en/vim-01-beginner.tutor.json50
-rwxr-xr-xscripts/bump-deps.sh104
-rw-r--r--scripts/bump_deps.lua343
-rwxr-xr-xscripts/download-unicode-files.sh4
-rwxr-xr-xscripts/gen_vimdoc.py183
-rw-r--r--scripts/genvimvim.lua10
-rw-r--r--scripts/lintcommit.lua44
-rw-r--r--scripts/lua2dox.lua25
-rwxr-xr-xscripts/pvscheck.sh2
-rwxr-xr-xscripts/stripdecls.py140
-rwxr-xr-xscripts/uncrustify.sh11
-rwxr-xr-xscripts/update_terminfo.sh6
-rwxr-xr-xscripts/update_version_stamp.lua54
-rwxr-xr-xscripts/vim-patch.sh61
-rw-r--r--scripts/windows.ti4
-rw-r--r--src/Doxyfile2614
-rw-r--r--src/cjson/lua_cjson.c22
-rwxr-xr-xsrc/clint.py588
-rw-r--r--src/coverity-model.c69
-rw-r--r--src/man/Makefile (renamed from man/Makefile)0
-rw-r--r--src/man/nvim.1 (renamed from man/nvim.1)14
-rw-r--r--src/mpack/lmpack.c2
-rw-r--r--src/mpack/mpack_core.c10
-rw-r--r--src/mpack/mpack_core.h2
-rwxr-xr-x[-rw-r--r--]src/nvim/CMakeLists.txt180
-rw-r--r--src/nvim/README.md15
-rw-r--r--src/nvim/api/autocmd.c1016
-rw-r--r--src/nvim/api/autocmd.h11
-rw-r--r--src/nvim/api/buffer.c407
-rw-r--r--src/nvim/api/command.c1189
-rw-r--r--src/nvim/api/command.h11
-rw-r--r--src/nvim/api/deprecated.c27
-rw-r--r--src/nvim/api/extmark.c460
-rw-r--r--src/nvim/api/extmark.h1
-rw-r--r--src/nvim/api/keysets.lua130
-rw-r--r--src/nvim/api/options.c554
-rw-r--r--src/nvim/api/options.h9
-rw-r--r--src/nvim/api/private/converter.c24
-rw-r--r--src/nvim/api/private/defs.h15
-rw-r--r--src/nvim/api/private/dispatch.c49
-rw-r--r--src/nvim/api/private/dispatch.h1
-rw-r--r--src/nvim/api/private/helpers.c833
-rw-r--r--src/nvim/api/private/helpers.h32
-rw-r--r--src/nvim/api/tabpage.c7
-rw-r--r--src/nvim/api/ui.c556
-rw-r--r--src/nvim/api/ui.h1
-rw-r--r--src/nvim/api/ui_events.in.h12
-rw-r--r--src/nvim/api/vim.c586
-rw-r--r--src/nvim/api/vimscript.c42
-rw-r--r--src/nvim/api/win_config.c37
-rw-r--r--src/nvim/api/window.c55
-rw-r--r--src/nvim/arabic.c1069
-rw-r--r--src/nvim/arabic.h7
-rw-r--r--src/nvim/ascii.h15
-rw-r--r--src/nvim/assert.h2
-rw-r--r--src/nvim/aucmd.c123
-rw-r--r--src/nvim/aucmd.h11
-rw-r--r--src/nvim/auevents.lua10
-rw-r--r--src/nvim/autocmd.c1683
-rw-r--r--src/nvim/autocmd.h65
-rw-r--r--src/nvim/buffer.c1401
-rw-r--r--src/nvim/buffer.h12
-rw-r--r--src/nvim/buffer_defs.h142
-rw-r--r--src/nvim/buffer_updates.c11
-rw-r--r--src/nvim/buffer_updates.h4
-rw-r--r--src/nvim/change.c390
-rw-r--r--src/nvim/change.h11
-rw-r--r--src/nvim/channel.c38
-rw-r--r--src/nvim/channel.h4
-rw-r--r--src/nvim/charset.c183
-rw-r--r--src/nvim/context.c9
-rw-r--r--src/nvim/cursor.c210
-rw-r--r--src/nvim/cursor_shape.c100
-rw-r--r--src/nvim/debugger.c88
-rw-r--r--src/nvim/decoration.c432
-rw-r--r--src/nvim/decoration.h56
-rw-r--r--src/nvim/decoration_provider.c228
-rw-r--r--src/nvim/decoration_provider.h26
-rw-r--r--src/nvim/diff.c332
-rw-r--r--src/nvim/digraph.c358
-rw-r--r--src/nvim/digraph.h1
-rw-r--r--src/nvim/edit.c936
-rw-r--r--src/nvim/eval.c2839
-rw-r--r--src/nvim/eval.h12
-rw-r--r--src/nvim/eval.lua33
-rw-r--r--src/nvim/eval/decode.c20
-rw-r--r--src/nvim/eval/encode.c67
-rw-r--r--src/nvim/eval/executor.c4
-rw-r--r--src/nvim/eval/funcs.c3312
-rw-r--r--src/nvim/eval/funcs.h7
-rw-r--r--src/nvim/eval/typval.c135
-rw-r--r--src/nvim/eval/typval.h28
-rw-r--r--src/nvim/eval/typval_encode.c.h9
-rw-r--r--src/nvim/eval/typval_encode.h4
-rw-r--r--src/nvim/eval/userfunc.c456
-rw-r--r--src/nvim/event/loop.c5
-rw-r--r--src/nvim/event/loop.h7
-rw-r--r--src/nvim/event/multiqueue.c1
-rw-r--r--src/nvim/event/multiqueue.h1
-rw-r--r--src/nvim/event/process.c6
-rw-r--r--src/nvim/event/process.h1
-rw-r--r--src/nvim/event/rstream.c21
-rw-r--r--src/nvim/event/rstream.h1
-rw-r--r--src/nvim/event/signal.c1
-rw-r--r--src/nvim/event/socket.c21
-rw-r--r--src/nvim/event/time.c1
-rw-r--r--src/nvim/ex_cmds.c1733
-rw-r--r--src/nvim/ex_cmds.h2
-rw-r--r--src/nvim/ex_cmds.lua708
-rw-r--r--src/nvim/ex_cmds2.c651
-rw-r--r--src/nvim/ex_cmds2.h5
-rw-r--r--src/nvim/ex_cmds_defs.h139
-rw-r--r--src/nvim/ex_docmd.c3570
-rw-r--r--src/nvim/ex_docmd.h9
-rw-r--r--src/nvim/ex_eval.c401
-rw-r--r--src/nvim/ex_eval.h12
-rw-r--r--src/nvim/ex_getln.c1129
-rw-r--r--src/nvim/ex_getln.h7
-rw-r--r--src/nvim/ex_session.c210
-rw-r--r--src/nvim/ex_session.h1
-rw-r--r--src/nvim/extmark.c365
-rw-r--r--src/nvim/extmark.h7
-rw-r--r--src/nvim/extmark_defs.h12
-rw-r--r--src/nvim/file_search.c189
-rw-r--r--src/nvim/fileio.c1251
-rw-r--r--src/nvim/fileio.h4
-rw-r--r--src/nvim/fold.c999
-rw-r--r--src/nvim/fold.h1
-rw-r--r--src/nvim/func_attr.h2
-rw-r--r--src/nvim/garray.c2
-rw-r--r--src/nvim/generators/c_grammar.lua1
-rw-r--r--src/nvim/generators/gen_api_dispatch.lua43
-rwxr-xr-x[-rw-r--r--]src/nvim/generators/gen_api_ui_events.lua99
-rw-r--r--src/nvim/generators/gen_char_blob.lua20
-rw-r--r--src/nvim/generators/gen_eval.lua45
-rw-r--r--src/nvim/generators/gen_ex_cmds.lua15
-rw-r--r--src/nvim/generators/gen_keysets.lua3
-rw-r--r--src/nvim/generators/hashy.lua24
-rw-r--r--src/nvim/getchar.c2702
-rw-r--r--src/nvim/getchar.h44
-rw-r--r--src/nvim/globals.h178
-rw-r--r--src/nvim/grid.c785
-rw-r--r--src/nvim/grid.h57
-rw-r--r--src/nvim/grid_defs.h27
-rw-r--r--src/nvim/hardcopy.c111
-rw-r--r--src/nvim/hardcopy.h4
-rw-r--r--src/nvim/hashtab.c4
-rw-r--r--src/nvim/highlight.c290
-rw-r--r--src/nvim/highlight_defs.h36
-rw-r--r--src/nvim/highlight_group.c2827
-rw-r--r--src/nvim/highlight_group.h19
-rw-r--r--src/nvim/if_cscope.c112
-rw-r--r--src/nvim/indent.c66
-rw-r--r--src/nvim/indent_c.c1464
-rw-r--r--src/nvim/input.c14
-rw-r--r--src/nvim/keycodes.c (renamed from src/nvim/keymap.c)305
-rw-r--r--src/nvim/keycodes.h (renamed from src/nvim/keymap.h)170
-rw-r--r--src/nvim/lib/kbtree.h165
-rw-r--r--src/nvim/lib/khash.h1
-rw-r--r--src/nvim/lib/klist.h1
-rw-r--r--src/nvim/lib/kvec.h27
-rw-r--r--src/nvim/lib/queue.h1
-rw-r--r--src/nvim/log.c174
-rw-r--r--src/nvim/log.h48
-rw-r--r--src/nvim/lua/converter.c62
-rw-r--r--src/nvim/lua/executor.c898
-rw-r--r--src/nvim/lua/executor.h25
-rw-r--r--src/nvim/lua/spell.c8
-rw-r--r--src/nvim/lua/stdlib.c136
-rw-r--r--src/nvim/lua/treesitter.c48
-rw-r--r--src/nvim/lua/xdiff.c9
-rw-r--r--src/nvim/macros.h32
-rw-r--r--src/nvim/main.c286
-rw-r--r--src/nvim/main.h8
-rw-r--r--src/nvim/map.c25
-rw-r--r--src/nvim/map.h19
-rw-r--r--src/nvim/mapping.c2547
-rw-r--r--src/nvim/mapping.h54
-rw-r--r--src/nvim/mark.c737
-rw-r--r--src/nvim/mark.h17
-rw-r--r--src/nvim/mark_defs.h45
-rw-r--r--src/nvim/marktree.c343
-rw-r--r--src/nvim/marktree.h96
-rw-r--r--src/nvim/match.c1213
-rw-r--r--src/nvim/match.h12
-rw-r--r--src/nvim/math.c4
-rw-r--r--src/nvim/mbyte.c254
-rw-r--r--src/nvim/memfile.c3
-rw-r--r--src/nvim/memline.c698
-rw-r--r--src/nvim/memory.c110
-rw-r--r--src/nvim/memory.h18
-rw-r--r--src/nvim/menu.c546
-rw-r--r--src/nvim/menu.h1
-rw-r--r--src/nvim/message.c874
-rw-r--r--src/nvim/message.h2
-rw-r--r--src/nvim/mouse.c212
-rw-r--r--src/nvim/mouse.h5
-rw-r--r--src/nvim/move.c110
-rw-r--r--src/nvim/msgpack_rpc/channel.c274
-rw-r--r--src/nvim/msgpack_rpc/channel.h1
-rw-r--r--src/nvim/msgpack_rpc/channel_defs.h6
-rw-r--r--src/nvim/msgpack_rpc/helpers.c2
-rw-r--r--src/nvim/msgpack_rpc/helpers.h1
-rw-r--r--src/nvim/msgpack_rpc/server.c116
-rw-r--r--src/nvim/msgpack_rpc/unpacker.c322
-rw-r--r--src/nvim/msgpack_rpc/unpacker.h46
-rw-r--r--src/nvim/normal.c3089
-rw-r--r--src/nvim/normal.h1
-rw-r--r--src/nvim/ops.c1425
-rw-r--r--src/nvim/ops.h14
-rw-r--r--src/nvim/option.c1162
-rw-r--r--src/nvim/option.h20
-rw-r--r--src/nvim/option_defs.h66
-rw-r--r--src/nvim/options.lua47
-rw-r--r--src/nvim/os/env.c77
-rw-r--r--src/nvim/os/fs.c65
-rw-r--r--src/nvim/os/fs.h10
-rw-r--r--src/nvim/os/input.c67
-rw-r--r--src/nvim/os/lang.c60
-rw-r--r--src/nvim/os/os.h3
-rw-r--r--src/nvim/os/os_defs.h2
-rw-r--r--src/nvim/os/os_win_console.c1
-rw-r--r--src/nvim/os/pty_process_unix.c32
-rw-r--r--src/nvim/os/pty_process_win.c168
-rw-r--r--src/nvim/os/pty_process_win.h15
-rw-r--r--src/nvim/os/shell.c41
-rw-r--r--src/nvim/os/signal.c32
-rw-r--r--src/nvim/os/stdpaths.c44
-rw-r--r--src/nvim/os/stdpaths_defs.h1
-rw-r--r--src/nvim/os/time.c1
-rw-r--r--src/nvim/os/users.c35
-rw-r--r--src/nvim/os/win_defs.h2
-rw-r--r--src/nvim/os_unix.c1
-rw-r--r--src/nvim/path.c243
-rw-r--r--src/nvim/plines.c24
-rw-r--r--src/nvim/po/af.po11
-rw-r--r--src/nvim/po/check.vim6
-rw-r--r--src/nvim/po/cleanup.vim4
-rw-r--r--src/nvim/po/de.po7
-rw-r--r--src/nvim/po/eo.po7
-rw-r--r--src/nvim/po/es.po7
-rw-r--r--src/nvim/po/fi.po8
-rw-r--r--src/nvim/po/fr.po6
-rw-r--r--src/nvim/po/ga.po15
-rw-r--r--src/nvim/po/it.po1
-rw-r--r--src/nvim/po/ja.euc-jp.po14
-rw-r--r--src/nvim/po/ja.po14
-rw-r--r--src/nvim/po/ru.po33
-rw-r--r--src/nvim/po/sr.po3
-rw-r--r--src/nvim/po/uk.po845
-rw-r--r--src/nvim/po/zh_CN.UTF-8.po6
-rw-r--r--src/nvim/po/zh_TW.UTF-8.po5
-rw-r--r--src/nvim/popupmnu.c293
-rw-r--r--src/nvim/pos.h36
-rw-r--r--src/nvim/quickfix.c1424
-rw-r--r--src/nvim/quickfix.h1
-rw-r--r--src/nvim/rbuffer.c18
-rw-r--r--src/nvim/rbuffer.h17
-rw-r--r--src/nvim/regexp.c6110
-rw-r--r--src/nvim/regexp.h2
-rw-r--r--src/nvim/regexp_bt.c5728
-rw-r--r--src/nvim/regexp_defs.h16
-rw-r--r--src/nvim/regexp_nfa.c3150
-rw-r--r--src/nvim/runtime.c132
-rw-r--r--src/nvim/runtime.h3
-rw-r--r--src/nvim/screen.c2910
-rw-r--r--src/nvim/screen.h62
-rw-r--r--src/nvim/search.c898
-rw-r--r--src/nvim/search.h3
-rw-r--r--src/nvim/sha256.c19
-rw-r--r--src/nvim/shada.c154
-rw-r--r--src/nvim/sign.c489
-rw-r--r--src/nvim/sign.h2
-rw-r--r--src/nvim/sign_defs.h2
-rw-r--r--src/nvim/spell.c365
-rw-r--r--src/nvim/spell_defs.h7
-rw-r--r--src/nvim/spellfile.c162
-rw-r--r--src/nvim/state.c168
-rw-r--r--src/nvim/strings.c125
-rw-r--r--src/nvim/strings.h3
-rw-r--r--src/nvim/syntax.c3056
-rw-r--r--src/nvim/syntax.h6
-rw-r--r--src/nvim/syntax_defs.h18
-rw-r--r--src/nvim/tag.c205
-rw-r--r--src/nvim/tag.h11
-rw-r--r--src/nvim/terminal.c137
-rw-r--r--src/nvim/terminal.h2
-rw-r--r--src/nvim/testdir/check.vim41
-rwxr-xr-xsrc/nvim/testdir/runnvim.sh10
-rw-r--r--src/nvim/testdir/runtest.vim16
-rw-r--r--src/nvim/testdir/setup.vim59
-rw-r--r--src/nvim/testdir/shared.vim9
-rw-r--r--src/nvim/testdir/test_alot.vim39
-rw-r--r--src/nvim/testdir/test_alot_utf8.vim1
-rw-r--r--src/nvim/testdir/test_arabic.vim28
-rw-r--r--src/nvim/testdir/test_arglist.vim42
-rw-r--r--src/nvim/testdir/test_assert.vim214
-rw-r--r--src/nvim/testdir/test_autochdir.vim71
-rw-r--r--src/nvim/testdir/test_autocmd.vim360
-rw-r--r--src/nvim/testdir/test_backspace_opt.vim4
-rw-r--r--src/nvim/testdir/test_blockedit.vim48
-rw-r--r--src/nvim/testdir/test_breakindent.vim6
-rw-r--r--src/nvim/testdir/test_buffer.vim197
-rw-r--r--src/nvim/testdir/test_bufline.vim35
-rw-r--r--src/nvim/testdir/test_bufwintabinfo.vim34
-rw-r--r--src/nvim/testdir/test_cd.vim24
-rw-r--r--src/nvim/testdir/test_cdo.vim216
-rw-r--r--src/nvim/testdir/test_changelist.vim2
-rw-r--r--src/nvim/testdir/test_charsearch.vim39
-rw-r--r--src/nvim/testdir/test_charsearch_utf8.vim2
-rw-r--r--src/nvim/testdir/test_cindent.vim5256
-rw-r--r--src/nvim/testdir/test_clientserver.vim8
-rw-r--r--src/nvim/testdir/test_cmdline.vim748
-rw-r--r--src/nvim/testdir/test_command_count.vim2
-rw-r--r--src/nvim/testdir/test_compiler.vim7
-rw-r--r--src/nvim/testdir/test_conceal.vim4
-rw-r--r--src/nvim/testdir/test_cscope.vim12
-rw-r--r--src/nvim/testdir/test_cursor_func.vim263
-rw-r--r--src/nvim/testdir/test_cursorline.vim83
-rw-r--r--src/nvim/testdir/test_debugger.vim2
-rw-r--r--src/nvim/testdir/test_delete.vim114
-rw-r--r--src/nvim/testdir/test_diffmode.vim96
-rw-r--r--src/nvim/testdir/test_digraph.vim97
-rw-r--r--src/nvim/testdir/test_display.vim50
-rw-r--r--src/nvim/testdir/test_edit.vim284
-rw-r--r--src/nvim/testdir/test_eval_stuff.vim30
-rw-r--r--src/nvim/testdir/test_ex_mode.vim135
-rw-r--r--src/nvim/testdir/test_excmd.vim252
-rw-r--r--src/nvim/testdir/test_exec_while_if.vim18
-rw-r--r--src/nvim/testdir/test_execute_func.vim27
-rw-r--r--src/nvim/testdir/test_exists.vim4
-rw-r--r--src/nvim/testdir/test_exit.vim22
-rw-r--r--src/nvim/testdir/test_expand.vim128
-rw-r--r--src/nvim/testdir/test_expand_func.vim31
-rw-r--r--src/nvim/testdir/test_expr.vim65
-rw-r--r--src/nvim/testdir/test_feedkeys.vim23
-rw-r--r--src/nvim/testdir/test_file_perm.vim30
-rw-r--r--src/nvim/testdir/test_filechanged.vim130
-rw-r--r--src/nvim/testdir/test_filetype.vim1134
-rw-r--r--src/nvim/testdir/test_filetype_lua.vim2
-rw-r--r--src/nvim/testdir/test_filter_cmd.vim43
-rw-r--r--src/nvim/testdir/test_filter_map.vim10
-rw-r--r--src/nvim/testdir/test_findfile.vim28
-rw-r--r--src/nvim/testdir/test_float_func.vim5
-rw-r--r--src/nvim/testdir/test_fnamemodify.vim19
-rw-r--r--src/nvim/testdir/test_fold.vim108
-rw-r--r--src/nvim/testdir/test_functions.vim542
-rw-r--r--src/nvim/testdir/test_gf.vim64
-rw-r--r--src/nvim/testdir/test_global.vim42
-rw-r--r--src/nvim/testdir/test_gn.vim18
-rw-r--r--src/nvim/testdir/test_goto.vim59
-rw-r--r--src/nvim/testdir/test_help.vim51
-rw-r--r--src/nvim/testdir/test_help_tagjump.vim62
-rw-r--r--src/nvim/testdir/test_highlight.vim84
-rw-r--r--src/nvim/testdir/test_history.vim130
-rw-r--r--src/nvim/testdir/test_increment.vim10
-rw-r--r--src/nvim/testdir/test_indent.vim279
-rw-r--r--src/nvim/testdir/test_ins_complete.vim230
-rw-r--r--src/nvim/testdir/test_interrupt.vim13
-rw-r--r--src/nvim/testdir/test_join.vim8
-rw-r--r--src/nvim/testdir/test_jumplist.vim41
-rw-r--r--src/nvim/testdir/test_jumps.vim11
-rw-r--r--src/nvim/testdir/test_lambda.vim163
-rw-r--r--src/nvim/testdir/test_langmap.vim30
-rw-r--r--src/nvim/testdir/test_legacy_filetype.vim4
-rw-r--r--src/nvim/testdir/test_lispwords.vim15
-rw-r--r--src/nvim/testdir/test_listchars.vim185
-rw-r--r--src/nvim/testdir/test_listdict.vim53
-rw-r--r--src/nvim/testdir/test_listlbr.vim6
-rw-r--r--src/nvim/testdir/test_listlbr_utf8.vim17
-rw-r--r--src/nvim/testdir/test_makeencoding.vim3
-rw-r--r--src/nvim/testdir/test_maparg.vim39
-rw-r--r--src/nvim/testdir/test_mapping.vim569
-rw-r--r--src/nvim/testdir/test_marks.vim71
-rw-r--r--src/nvim/testdir/test_matchadd_conceal.vim23
-rw-r--r--src/nvim/testdir/test_matchadd_conceal_utf8.vim16
-rw-r--r--src/nvim/testdir/test_matchfuzzy.vim271
-rw-r--r--src/nvim/testdir/test_menu.vim94
-rw-r--r--src/nvim/testdir/test_messages.vim195
-rw-r--r--src/nvim/testdir/test_mksession.vim156
-rw-r--r--src/nvim/testdir/test_mksession_utf8.vim5
-rw-r--r--src/nvim/testdir/test_move.vim1
-rw-r--r--src/nvim/testdir/test_normal.vim883
-rw-r--r--src/nvim/testdir/test_number.vim29
-rw-r--r--src/nvim/testdir/test_options.vim258
-rw-r--r--src/nvim/testdir/test_packadd.vim361
-rw-r--r--src/nvim/testdir/test_perl.vim6
-rw-r--r--src/nvim/testdir/test_plus_arg_edit.vim15
-rw-r--r--src/nvim/testdir/test_popup.vim57
-rw-r--r--src/nvim/testdir/test_profile.vim31
-rw-r--r--src/nvim/testdir/test_put.vim113
-rw-r--r--src/nvim/testdir/test_python2.vim5
-rw-r--r--src/nvim/testdir/test_python3.vim6
-rw-r--r--src/nvim/testdir/test_pyx2.vim5
-rw-r--r--src/nvim/testdir/test_pyx3.vim5
-rw-r--r--src/nvim/testdir/test_quickfix.vim749
-rw-r--r--src/nvim/testdir/test_quotestar.vim7
-rw-r--r--src/nvim/testdir/test_random.vim51
-rw-r--r--src/nvim/testdir/test_regex_char_classes.vim2
-rw-r--r--src/nvim/testdir/test_regexp_latin.vim270
-rw-r--r--src/nvim/testdir/test_regexp_utf8.vim43
-rw-r--r--src/nvim/testdir/test_registers.vim528
-rw-r--r--src/nvim/testdir/test_reltime.vim6
-rw-r--r--src/nvim/testdir/test_rename.vim4
-rw-r--r--src/nvim/testdir/test_retab.vim24
-rw-r--r--src/nvim/testdir/test_scriptnames.vim6
-rw-r--r--src/nvim/testdir/test_search.vim530
-rw-r--r--src/nvim/testdir/test_search_stat.vim109
-rw-r--r--src/nvim/testdir/test_searchpos.vim30
-rw-r--r--src/nvim/testdir/test_selectmode.vim57
-rw-r--r--src/nvim/testdir/test_set.vim29
-rw-r--r--src/nvim/testdir/test_sha256.vim6
-rw-r--r--src/nvim/testdir/test_smartindent.vim107
-rw-r--r--src/nvim/testdir/test_sort.vim20
-rw-r--r--src/nvim/testdir/test_source.vim42
-rw-r--r--src/nvim/testdir/test_source_utf8.vim27
-rw-r--r--src/nvim/testdir/test_spell.vim174
-rw-r--r--src/nvim/testdir/test_spell_utf8.vim67
-rw-r--r--src/nvim/testdir/test_spellfile.vim745
-rw-r--r--src/nvim/testdir/test_stat.vim42
-rw-r--r--src/nvim/testdir/test_statusline.vim73
-rw-r--r--src/nvim/testdir/test_substitute.vim483
-rw-r--r--src/nvim/testdir/test_suspend.vim4
-rw-r--r--src/nvim/testdir/test_swap.vim61
-rw-r--r--src/nvim/testdir/test_syntax.vim150
-rw-r--r--src/nvim/testdir/test_system.vim30
-rw-r--r--src/nvim/testdir/test_tabline.vim59
-rw-r--r--src/nvim/testdir/test_tabpage.vim150
-rw-r--r--src/nvim/testdir/test_tagjump.vim487
-rw-r--r--src/nvim/testdir/test_taglist.vim20
-rw-r--r--src/nvim/testdir/test_termcodes.vim60
-rw-r--r--src/nvim/testdir/test_textformat.vim318
-rw-r--r--src/nvim/testdir/test_textobjects.vim203
-rw-r--r--src/nvim/testdir/test_timers.vim133
-rw-r--r--src/nvim/testdir/test_trycatch.vim160
-rw-r--r--src/nvim/testdir/test_undo.vim162
-rw-r--r--src/nvim/testdir/test_usercommands.vim129
-rw-r--r--src/nvim/testdir/test_utf8.vim54
-rw-r--r--src/nvim/testdir/test_utf8_comparisons.vim11
-rw-r--r--src/nvim/testdir/test_vartabs.vim59
-rw-r--r--src/nvim/testdir/test_vimscript.vim222
-rw-r--r--src/nvim/testdir/test_virtualedit.vim265
-rw-r--r--src/nvim/testdir/test_visual.vim572
-rw-r--r--src/nvim/testdir/test_winbuf_close.vim19
-rw-r--r--src/nvim/testdir/test_window_cmd.vim501
-rw-r--r--src/nvim/testdir/test_writefile.vim156
-rw-r--r--src/nvim/testing.c625
-rw-r--r--src/nvim/testing.h10
-rw-r--r--src/nvim/tui/input.c287
-rw-r--r--src/nvim/tui/input.h11
-rw-r--r--src/nvim/tui/input_defs.h118
-rw-r--r--src/nvim/tui/terminfo_defs.h92
-rw-r--r--src/nvim/tui/tui.c206
-rw-r--r--src/nvim/tui/tui.h2
-rw-r--r--src/nvim/types.h2
-rw-r--r--src/nvim/ugrid.c7
-rw-r--r--src/nvim/ui.c76
-rw-r--r--src/nvim/ui.h4
-rw-r--r--src/nvim/ui_bridge.c2
-rw-r--r--src/nvim/ui_bridge.h1
-rw-r--r--src/nvim/ui_client.c207
-rw-r--r--src/nvim/ui_client.h17
-rw-r--r--src/nvim/ui_compositor.c190
-rw-r--r--src/nvim/undo.c66
-rw-r--r--src/nvim/undo_defs.h6
-rw-r--r--src/nvim/version.c48
-rw-r--r--src/nvim/vim.h96
-rw-r--r--src/nvim/viml/parser/expressions.c38
-rw-r--r--src/nvim/viml/parser/parser.c1
-rw-r--r--src/nvim/viml/parser/parser.h6
-rw-r--r--src/nvim/window.c1628
-rw-r--r--src/nvim/window.h70
-rw-r--r--src/uncrustify.cfg266
-rw-r--r--src/unicode/CaseFolding.txt (renamed from unicode/CaseFolding.txt)46
-rw-r--r--src/unicode/Copyright.txt (renamed from unicode/Copyright.txt)0
-rw-r--r--src/unicode/EastAsianWidth.txt (renamed from unicode/EastAsianWidth.txt)149
-rw-r--r--src/unicode/UnicodeData.txt (renamed from unicode/UnicodeData.txt)837
-rw-r--r--src/unicode/emoji-data.txt (renamed from unicode/emoji-data.txt)96
-rw-r--r--test/README.md45
-rw-r--r--test/benchmark/treesitter_spec.lua53
-rw-r--r--test/busted/outputHandlers/nvim.lua34
-rw-r--r--test/cmakeconfig/paths.lua.in (renamed from test/config/paths.lua.in)0
-rw-r--r--test/functional/api/autocmd_spec.lua1249
-rw-r--r--test/functional/api/buffer_spec.lua100
-rw-r--r--test/functional/api/buffer_updates_spec.lua5
-rw-r--r--test/functional/api/command_spec.lua305
-rw-r--r--test/functional/api/extmark_spec.lua279
-rw-r--r--test/functional/api/highlight_spec.lua103
-rw-r--r--test/functional/api/keymap_spec.lua78
-rw-r--r--test/functional/api/proc_spec.lua6
-rw-r--r--test/functional/api/server_requests_spec.lua8
-rw-r--r--test/functional/api/version_spec.lua3
-rw-r--r--test/functional/api/vim_spec.lua1286
-rw-r--r--test/functional/api/window_spec.lua44
-rw-r--r--test/functional/autocmd/autocmd_oldtest_spec.lua86
-rw-r--r--test/functional/autocmd/autocmd_spec.lua223
-rw-r--r--test/functional/autocmd/cursormoved_spec.lua1
-rw-r--r--test/functional/autocmd/dirchanged_spec.lua128
-rw-r--r--test/functional/autocmd/focus_spec.lua2
-rw-r--r--test/functional/autocmd/show_spec.lua183
-rw-r--r--test/functional/autocmd/signal_spec.lua6
-rw-r--r--test/functional/autocmd/termxx_spec.lua26
-rw-r--r--test/functional/autocmd/winscrolled_spec.lua103
-rw-r--r--test/functional/core/channels_spec.lua53
-rw-r--r--test/functional/core/fileio_spec.lua75
-rw-r--r--test/functional/core/job_spec.lua89
-rw-r--r--test/functional/core/log_spec.lua57
-rw-r--r--test/functional/core/main_spec.lua12
-rw-r--r--test/functional/core/remote_spec.lua142
-rw-r--r--test/functional/core/startup_spec.lua51
-rw-r--r--test/functional/editor/K_spec.lua23
-rw-r--r--test/functional/editor/completion_spec.lua60
-rw-r--r--test/functional/editor/ctrl_c_spec.lua (renamed from test/functional/ex_cmds/ctrl_c_spec.lua)54
-rw-r--r--test/functional/editor/jump_spec.lua143
-rw-r--r--test/functional/editor/langmap_spec.lua36
-rw-r--r--test/functional/editor/macro_spec.lua55
-rw-r--r--test/functional/editor/mark_spec.lua401
-rw-r--r--test/functional/editor/meta_key_spec.lua54
-rw-r--r--test/functional/editor/mode_cmdline_spec.lua105
-rw-r--r--test/functional/editor/mode_insert_spec.lua58
-rw-r--r--test/functional/editor/put_spec.lua26
-rw-r--r--test/functional/editor/tabpage_spec.lua28
-rw-r--r--test/functional/editor/undo_spec.lua67
-rw-r--r--test/functional/ex_cmds/cd_spec.lua2
-rw-r--r--test/functional/ex_cmds/cmd_map_spec.lua96
-rw-r--r--test/functional/ex_cmds/drop_spec.lua32
-rw-r--r--test/functional/ex_cmds/echo_spec.lua14
-rw-r--r--test/functional/ex_cmds/ls_spec.lua4
-rw-r--r--test/functional/ex_cmds/make_spec.lua6
-rw-r--r--test/functional/ex_cmds/map_spec.lua233
-rw-r--r--test/functional/ex_cmds/menu_spec.lua3
-rw-r--r--test/functional/ex_cmds/mksession_spec.lua109
-rw-r--r--test/functional/ex_cmds/normal_spec.lua27
-rw-r--r--test/functional/ex_cmds/quickfix_commands_spec.lua13
-rw-r--r--test/functional/ex_cmds/script_spec.lua16
-rw-r--r--test/functional/ex_cmds/sign_spec.lua9
-rw-r--r--test/functional/ex_cmds/source_spec.lua20
-rw-r--r--test/functional/ex_cmds/swapfile_preserve_recover_spec.lua3
-rw-r--r--test/functional/ex_cmds/verbose_spec.lua168
-rw-r--r--test/functional/fixtures/CMakeLists.txt4
-rw-r--r--test/functional/fixtures/api_level_9.mpackbin0 -> 30127 bytes
-rw-r--r--test/functional/fixtures/autoload/provider/python3.vim (renamed from test/functional/fixtures/autoload/provider/python.vim)2
-rw-r--r--test/functional/fixtures/fake-lsp-server.lua256
-rw-r--r--test/functional/fixtures/lua/test_plug/health/init.lua9
-rw-r--r--test/functional/fixtures/lua/test_plug/submodule/health.lua9
-rw-r--r--test/functional/fixtures/lua/test_plug/submodule_empty/health.lua7
-rw-r--r--test/functional/fixtures/lua/test_plug/submodule_failed/health.lua7
-rw-r--r--test/functional/fixtures/pack/foo/start/bar/lua/baz-quux.lua1
-rw-r--r--test/functional/helpers.lua115
-rw-r--r--test/functional/legacy/003_cindent_spec.lua4774
-rw-r--r--test/functional/legacy/008_autocommands_spec.lua3
-rw-r--r--test/functional/legacy/012_directory_spec.lua3
-rw-r--r--test/functional/legacy/031_close_commands_spec.lua3
-rw-r--r--test/functional/legacy/arglist_spec.lua47
-rw-r--r--test/functional/legacy/assert_spec.lua245
-rw-r--r--test/functional/legacy/autochdir_spec.lua94
-rw-r--r--test/functional/legacy/cdo_spec.lua228
-rw-r--r--test/functional/legacy/cmdline_spec.lua68
-rw-r--r--test/functional/legacy/cpoptions_spec.lua34
-rw-r--r--test/functional/legacy/delete_spec.lua58
-rw-r--r--test/functional/legacy/display_spec.lua90
-rw-r--r--test/functional/legacy/edit_spec.lua41
-rw-r--r--test/functional/legacy/eval_spec.lua2
-rw-r--r--test/functional/legacy/ex_mode_spec.lua125
-rw-r--r--test/functional/legacy/excmd_spec.lua188
-rw-r--r--test/functional/legacy/expand_spec.lua129
-rw-r--r--test/functional/legacy/file_perm_spec.lua43
-rw-r--r--test/functional/legacy/filechanged_spec.lua131
-rw-r--r--test/functional/legacy/fixeol_spec.lua11
-rw-r--r--test/functional/legacy/function_sort_spec.lua2
-rw-r--r--test/functional/legacy/listchars_spec.lua127
-rw-r--r--test/functional/legacy/mapping_spec.lua113
-rw-r--r--test/functional/legacy/memory_usage_spec.lua17
-rw-r--r--test/functional/legacy/messages_spec.lua421
-rw-r--r--test/functional/legacy/packadd_spec.lua507
-rw-r--r--test/functional/legacy/prompt_buffer_spec.lua19
-rw-r--r--test/functional/legacy/put_spec.lua45
-rw-r--r--test/functional/legacy/search_spec.lua32
-rw-r--r--test/functional/legacy/search_stat_spec.lua184
-rw-r--r--test/functional/legacy/searchpos_spec.lua35
-rw-r--r--test/functional/legacy/set_spec.lua30
-rw-r--r--test/functional/legacy/statusline_spec.lua71
-rw-r--r--test/functional/legacy/utf8_spec.lua2
-rw-r--r--test/functional/legacy/visual_mode_spec.lua44
-rw-r--r--test/functional/legacy/wordcount_spec.lua66
-rw-r--r--test/functional/lua/api_spec.lua7
-rw-r--r--test/functional/lua/buffer_updates_spec.lua50
-rw-r--r--test/functional/lua/command_line_completion_spec.lua8
-rw-r--r--test/functional/lua/diagnostic_spec.lua56
-rw-r--r--test/functional/lua/filetype_spec.lua24
-rw-r--r--test/functional/lua/fs_spec.lua101
-rw-r--r--test/functional/lua/highlight_spec.lua15
-rw-r--r--test/functional/lua/luaeval_spec.lua1
-rw-r--r--test/functional/lua/overrides_spec.lua38
-rw-r--r--test/functional/lua/thread_spec.lua408
-rw-r--r--test/functional/lua/ui_spec.lua16
-rw-r--r--test/functional/lua/uri_spec.lua2
-rw-r--r--test/functional/lua/vim_spec.lua461
-rw-r--r--test/functional/lua/xdiff_spec.lua42
-rw-r--r--test/functional/options/autochdir_spec.lua31
-rw-r--r--test/functional/options/cursorbind_spec.lua91
-rw-r--r--test/functional/options/defaults_spec.lua107
-rw-r--r--test/functional/options/keymap_spec.lua22
-rw-r--r--test/functional/options/mousescroll_spec.lua151
-rw-r--r--test/functional/options/num_options_spec.lua2
-rw-r--r--test/functional/options/pastetoggle_spec.lua86
-rw-r--r--test/functional/plugin/health_spec.lua35
-rw-r--r--test/functional/plugin/lsp/diagnostic_spec.lua42
-rw-r--r--test/functional/plugin/lsp/incremental_sync_spec.lua48
-rw-r--r--test/functional/plugin/lsp/snippet_spec.lua134
-rw-r--r--test/functional/plugin/lsp_spec.lua815
-rw-r--r--test/functional/plugin/man_spec.lua14
-rw-r--r--test/functional/plugin/matchparen_spec.lua2
-rw-r--r--test/functional/plugin/shada_spec.lua8
-rw-r--r--test/functional/preload.lua28
-rw-r--r--test/functional/provider/clipboard_spec.lua16
-rw-r--r--test/functional/provider/nodejs_spec.lua4
-rw-r--r--test/functional/provider/perl_spec.lua4
-rw-r--r--test/functional/provider/provider_spec.lua4
-rw-r--r--test/functional/provider/python3_spec.lua51
-rw-r--r--test/functional/provider/python_spec.lua123
-rw-r--r--test/functional/shada/buffers_spec.lua15
-rw-r--r--test/functional/shada/history_spec.lua21
-rw-r--r--test/functional/shada/marks_spec.lua15
-rw-r--r--test/functional/shada/registers_spec.lua29
-rw-r--r--test/functional/shada/shada_spec.lua2
-rw-r--r--test/functional/shada/variables_spec.lua7
-rw-r--r--test/functional/terminal/api_spec.lua8
-rw-r--r--test/functional/terminal/buffer_spec.lua1
-rw-r--r--test/functional/terminal/channel_spec.lua87
-rw-r--r--test/functional/terminal/cursor_spec.lua60
-rw-r--r--test/functional/terminal/edit_spec.lua5
-rw-r--r--test/functional/terminal/ex_terminal_spec.lua17
-rw-r--r--test/functional/terminal/helpers.lua10
-rw-r--r--test/functional/terminal/highlight_spec.lua25
-rw-r--r--test/functional/terminal/mouse_spec.lua30
-rw-r--r--test/functional/terminal/scrollback_spec.lua47
-rw-r--r--test/functional/terminal/tui_spec.lua260
-rw-r--r--test/functional/terminal/window_spec.lua1
-rw-r--r--test/functional/treesitter/highlight_spec.lua129
-rw-r--r--test/functional/treesitter/language_spec.lua12
-rw-r--r--test/functional/treesitter/parser_spec.lua55
-rw-r--r--test/functional/ui/bufhl_spec.lua28
-rw-r--r--test/functional/ui/cmdline_highlight_spec.lua13
-rw-r--r--test/functional/ui/cmdline_spec.lua263
-rw-r--r--test/functional/ui/cursor_spec.lua2
-rw-r--r--test/functional/ui/decorations_spec.lua484
-rw-r--r--test/functional/ui/diff_spec.lua1373
-rw-r--r--test/functional/ui/embed_spec.lua48
-rw-r--r--test/functional/ui/float_spec.lua975
-rw-r--r--test/functional/ui/fold_spec.lua65
-rw-r--r--test/functional/ui/highlight_spec.lua553
-rw-r--r--test/functional/ui/hlstate_spec.lua9
-rw-r--r--test/functional/ui/inccommand_spec.lua367
-rw-r--r--test/functional/ui/inccommand_user_spec.lua356
-rw-r--r--test/functional/ui/input_spec.lua240
-rw-r--r--test/functional/ui/messages_spec.lua339
-rw-r--r--test/functional/ui/mouse_spec.lua141
-rw-r--r--test/functional/ui/multibyte_spec.lua34
-rw-r--r--test/functional/ui/multigrid_spec.lua584
-rw-r--r--test/functional/ui/options_spec.lua57
-rw-r--r--test/functional/ui/output_spec.lua10
-rw-r--r--test/functional/ui/popupmenu_spec.lua434
-rw-r--r--test/functional/ui/screen.lua77
-rw-r--r--test/functional/ui/screen_basic_spec.lua250
-rw-r--r--test/functional/ui/searchhl_spec.lua409
-rw-r--r--test/functional/ui/sign_spec.lua34
-rw-r--r--test/functional/ui/statusline_spec.lua342
-rw-r--r--test/functional/ui/syntax_conceal_spec.lua90
-rw-r--r--test/functional/ui/wildmode_spec.lua4
-rw-r--r--test/functional/ui/winbar_spec.lua580
-rw-r--r--test/functional/vimscript/api_functions_spec.lua3
-rw-r--r--test/functional/vimscript/eval_spec.lua2
-rw-r--r--test/functional/vimscript/executable_spec.lua15
-rw-r--r--test/functional/vimscript/execute_spec.lua4
-rw-r--r--test/functional/vimscript/functions_spec.lua2
-rw-r--r--test/functional/vimscript/has_spec.lua7
-rw-r--r--test/functional/vimscript/input_spec.lua14
-rw-r--r--test/functional/vimscript/lang_spec.lua1
-rw-r--r--test/functional/vimscript/let_spec.lua45
-rw-r--r--test/functional/vimscript/msgpack_functions_spec.lua6
-rw-r--r--test/functional/vimscript/reltime_spec.lua4
-rw-r--r--test/functional/vimscript/screenchar_spec.lua69
-rw-r--r--test/functional/vimscript/server_spec.lua55
-rw-r--r--test/functional/vimscript/setpos_spec.lua22
-rw-r--r--test/functional/vimscript/system_spec.lua109
-rw-r--r--test/functional/vimscript/timer_spec.lua10
-rw-r--r--test/helpers.lua105
-rw-r--r--test/symbolic/klee/nvim/keymap.c2
-rwxr-xr-xtest/symbolic/klee/run.sh2
-rw-r--r--test/symbolic/klee/viml_expressions_lexer.c2
-rw-r--r--test/symbolic/klee/viml_expressions_parser.c2
-rw-r--r--test/unit/buffer_spec.lua187
-rw-r--r--test/unit/fixtures/rbuffer.c2
-rw-r--r--test/unit/helpers.lua6
-rw-r--r--test/unit/keycodes_spec.lua (renamed from test/unit/keymap_spec.lua)25
-rw-r--r--test/unit/marktree_spec.lua44
-rw-r--r--test/unit/os/env_spec.lua4
-rw-r--r--test/unit/os/users_spec.lua12
-rw-r--r--test/unit/path_spec.lua16
-rw-r--r--test/unit/search_spec.lua24
-rw-r--r--test/unit/strings_spec.lua47
-rw-r--r--test/unit/tempfile_spec.lua12
-rw-r--r--test/unit/viml/expressions/parser_spec.lua1
-rw-r--r--third-party/cmake/BuildGperf.cmake67
-rw-r--r--third-party/cmake/GperfCMakeLists.txt30
-rw-r--r--third-party/patches/libuv-disable-typedef-MinGW.patch19
1138 files changed, 134318 insertions, 73071 deletions
diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml
index d5809c42cf..4be49fd153 100644
--- a/.builds/freebsd.yml
+++ b/.builds/freebsd.yml
@@ -1,11 +1,10 @@
-image: freebsd/12.x
+image: freebsd/latest
packages:
- cmake
- gmake
- ninja
- libtool
-- sha
- automake
- pkgconf
- unzip
@@ -13,6 +12,7 @@ packages:
- gettext
- python
- libffi
+- gdb
sources:
- https://github.com/neovim/neovim
@@ -23,6 +23,11 @@ environment:
CMAKE_EXTRA_FLAGS: -DCI_BUILD=ON -DMIN_LOG_LEVEL=3
tasks:
+- should-run: |
+ if ! git -C neovim diff --name-only HEAD^! | grep -E -v "^(.github|runtime/doc/.*)" >/dev/null; then
+ echo "Skipping build because only ignored files were changed"
+ complete-build
+ fi
- build-deps: |
cd neovim
gmake deps
@@ -35,10 +40,6 @@ tasks:
- unittest: |
cd neovim
gmake unittest
-
-# Unfortunately, oldtest is tanking hard on sourcehut's FreeBSD instance
-# and not producing any logs as a result. So don't do this task for now.
-# Ref: https://github.com/neovim/neovim/pull/11477#discussion_r352095005.
-# - test-oldtest: |
-# cd neovim
-# gmake oldtest
+- oldtest: |
+ cd neovim
+ gmake oldtest
diff --git a/.builds/openbsd.yml b/.builds/openbsd.yml
index 422fa366b6..60548f4f58 100644
--- a/.builds/openbsd.yml
+++ b/.builds/openbsd.yml
@@ -1,6 +1,6 @@
# sourcehut CI: https://builds.sr.ht/~jmk/neovim
-image: openbsd/6.9
+image: openbsd/latest
packages:
- autoconf-2.71
@@ -12,6 +12,7 @@ packages:
- libtool
- ninja-1.10.2p0
- unzip-6.0p14
+- gdb
sources:
- https://github.com/neovim/neovim
@@ -22,12 +23,17 @@ environment:
CMAKE_EXTRA_FLAGS: -DCI_BUILD=ON -DMIN_LOG_LEVEL=3
tasks:
+- should-run: |
+ if ! git -C neovim diff --name-only HEAD^! | grep -E -v "^(.github|runtime/doc/.*)" >/dev/null; then
+ echo "Skipping build because only ignored files were changed"
+ complete-build
+ fi
- build-deps: |
export AUTOCONF_VERSION=2.71
export AUTOMAKE_VERSION=1.16
mkdir neovim/.deps
cd neovim/.deps
- cmake -G Ninja ../third-party/
+ cmake -G Ninja ../cmake.deps/
cmake --build . --config RelWithDebInfo
- build: |
mkdir neovim/build
diff --git a/.clang-format b/.clang-format
index c86f5a3ddf..a31d753217 100644
--- a/.clang-format
+++ b/.clang-format
@@ -2,10 +2,10 @@ BasedOnStyle: Google
Language: Cpp
ColumnLimit: 100
IndentWidth: 2
-TabWidth: 2
+TabWidth: 8
UseTab: Never
-IndentCaseLabels: true
-BreakBeforeBraces: Linux
+IndentCaseLabels: false
+BreakBeforeBraces: Custom
AlignEscapedNewlinesLeft: false
AllowShortFunctionsOnASingleLine: false
AlignTrailingComments: true
@@ -17,4 +17,51 @@ AllowShortLoopsOnASingleLine: false
BinPackParameters: false
BreakBeforeBinaryOperators: true
BreakBeforeTernaryOperators: true
-ContinuationIndentWidth: 4
+ContinuationIndentWidth: 2
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: No
+AlwaysBreakTemplateDeclarations: No
+AlignEscapedNewlines: DontAlign
+BinPackArguments: false
+BraceWrapping:
+ AfterClass: false
+ AfterControlStatement: false
+ AfterEnum: false
+ AfterFunction: true
+ AfterObjCDeclaration: false
+ AfterStruct: false
+ AfterUnion: false
+ BeforeCatch: false
+ BeforeElse: false
+ IndentBraces: false
+PointerAlignment: Right
+SortIncludes: true
+Cpp11BracedListStyle: false
+IncludeCategories:
+ - Regex: '<[/[:alnum:].]+>'
+ Priority: 0
+ - Regex: '^"(nvim|vim)/'
+ Priority: 1
+ SortPriority: 1
+ CaseSensitive: false
+AlignConsecutiveMacros: AcrossEmptyLines
+IndentPPDirectives: AfterHash
+SpaceBeforeParens: ControlStatementsExceptControlMacros
+PPIndentWidth: 1
+ForEachMacros:
+ - FOR_ALL_AUEVENTS
+ - FOR_ALL_AUPATS_IN_EVENT
+ - FOR_ALL_BUFFERS
+ - FOR_ALL_BUFFERS_BACKWARDS
+ - FOR_ALL_FRAMES
+ - FOR_ALL_QFL_ITEMS
+ - FOR_ALL_SIGNS_IN_BUF
+ - FOR_ALL_TABS
+ - FOR_ALL_TAB_WINDOWS
+ - FOR_ALL_WINDOWS_IN_TAB
+ - RBUFFER_EACH
+ - RBUFFER_EACH_REVERSE
+ - RBUFFER_UNTIL_EMPTY
+ - RBUFFER_UNTIL_FULL
+ - kl_iter
diff --git a/.editorconfig b/.editorconfig
index 22fee54b22..2aa956b1fc 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -6,7 +6,6 @@ indent_size = 2
tab_width = 8
end_of_line = lf
insert_final_newline = true
-charset = utf-8
[*.{c,h,in,lua}]
max_line_length = 100
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
index bbca6b3339..11f5f17bba 100644
--- a/.git-blame-ignore-revs
+++ b/.git-blame-ignore-revs
@@ -36,3 +36,20 @@ d90fb1c0bfc1e64c783c385a79e7de87013dadba
9c268263b1792d00b3ffdfd7495af2575862656e
8c74c895b300bcee5fa937a2329d1d4756567b42
40be47e0faef7aa015eb4ba44ceb1ee1a03e97cf
+4472c56d54f447040f6e8610b261b7efa0d04eb6
+a68faed02dc8e37b8f10da14dc02e33e6ed93947
+725cbe7d414f609e769081276f2a034e32a4337b
+7e3bdc75e44b9139d8afaea4381b53ae78b15746
+4ba12b3dda34472c193c9fa8ffd7d3bd5b6c04d6
+849f104c2789c884428fd45501912c6591a78e12
+38dd53c525054daf83dba27d7d46e90e8b41fa50
+6059784770c4c88fb6fe528b9f7634192fa1164e
+ee031eb5256bb83e0d6add2bae6fd943a4186ffe
+69e11b58b4db0952f11a5ff85aa7150b5f5b8db8
+271bb32855853b011fceaf0ad2f829bce66b2a19
+aefdc6783cb77f09786542c90901a9e7120bea42
+aa4f9c5341f5280f16cce0630ea54b84eef717b3
+
+# typos
+d238b8f6003d34cae7f65ff7585b48a2cd9449fb
+4547137aaff32b20172870a549d3a28a3c7adf1c
diff --git a/.github/labeler.yml b/.github/labeler.yml
index 52998c65cf..04e7b281d1 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -18,7 +18,7 @@
- runtime/lua/vim/diagnostic.lua
"dependencies":
- - third-party/**/*
+ - cmake.deps/**/*
"spell":
- src/nvim/spell*
@@ -39,6 +39,7 @@
"documentation":
- all: ["runtime/doc/*"]
+ - all: ["**/*.md"]
"clipboard":
- runtime/autoload/provider/clipboard.vim
@@ -49,9 +50,10 @@
"build":
- CMakeLists.txt
- "**/CMakeLists.txt"
+ - "**/Makefile"
- "**/*.cmake"
-"tests":
+"test":
- all: ["test/**/*"]
"ci":
@@ -59,3 +61,6 @@
- .github/workflows/**/*
- .builds/*
- ci/**/*
+
+"filetype":
+ - runtime/lua/vim/filetype.lua
diff --git a/.github/scripts/remove-reviewers.js b/.github/scripts/remove-reviewers.js
new file mode 100644
index 0000000000..631f08e57d
--- /dev/null
+++ b/.github/scripts/remove-reviewers.js
@@ -0,0 +1,16 @@
+module.exports = async ({github, context}) => {
+ const requestedReviewers = await github.rest.pulls.listRequestedReviewers({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ pull_number: context.issue.number
+ });
+
+ const reviewers = requestedReviewers.data.users.map(e => e.login)
+
+ github.rest.pulls.removeRequestedReviewers({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ pull_number: context.issue.number,
+ reviewers: reviewers
+ });
+}
diff --git a/.github/scripts/reviews.js b/.github/scripts/reviews.js
new file mode 100644
index 0000000000..6e9a829353
--- /dev/null
+++ b/.github/scripts/reviews.js
@@ -0,0 +1,94 @@
+module.exports = async ({github, context}) => {
+ const pr_data = await github.rest.pulls.get({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ pull_number: context.issue.number
+ })
+ const labels = pr_data.data.labels.map(e => e.name)
+
+ const reviewers = new Set()
+ if (labels.includes('api')) {
+ reviewers.add("bfredl")
+ reviewers.add("gpanders")
+ reviewers.add("muniter")
+ }
+
+ if (labels.includes('build')) {
+ reviewers.add("jamessan")
+ }
+
+ if (labels.includes('ci')) {
+ reviewers.add("jamessan")
+ }
+
+ if (labels.includes('column')) {
+ reviewers.add("lewis6991")
+ }
+
+ if (labels.includes('diagnostic')) {
+ reviewers.add("gpanders")
+ }
+
+ if (labels.includes('diff')) {
+ reviewers.add("lewis6991")
+ }
+
+ if (labels.includes('dependencies')) {
+ reviewers.add("jamessan")
+ }
+
+ if (labels.includes('distribution')) {
+ reviewers.add("jamessan")
+ }
+
+ if (labels.includes('documentation')) {
+ reviewers.add("clason")
+ }
+
+ if (labels.includes('extmarks')) {
+ reviewers.add("bfredl")
+ }
+
+ if (labels.includes('filetype')) {
+ reviewers.add("clason")
+ reviewers.add("gpanders")
+ }
+
+ if (labels.includes('gui')) {
+ reviewers.add("glacambre")
+ reviewers.add("smolck")
+ }
+
+ if (labels.includes('lsp')) {
+ reviewers.add("mfussenegger")
+ }
+
+ if (labels.includes('treesitter')) {
+ reviewers.add("bfredl")
+ reviewers.add("vigoux")
+ }
+
+ if (labels.includes('typo')) {
+ reviewers.add("dundargoc")
+ }
+
+ if (labels.includes('ui')) {
+ reviewers.add("bfredl")
+ }
+
+ if (labels.includes('vim-patch')) {
+ reviewers.add("seandewar")
+ reviewers.add("zeertzjq")
+ }
+
+ // Remove person that opened the PR since they can't review themselves
+ const pr_opener = pr_data.data.user.login
+ reviewers.delete(pr_opener)
+
+ github.rest.pulls.requestReviewers({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ pull_number: context.issue.number,
+ reviewers: Array.from(reviewers)
+ });
+}
diff --git a/.github/workflows/api-docs-check.yml b/.github/workflows/api-docs-check.yml
new file mode 100644
index 0000000000..0a57df7c33
--- /dev/null
+++ b/.github/workflows/api-docs-check.yml
@@ -0,0 +1,18 @@
+name: Missing API docs
+on:
+ pull_request:
+ branches-ignore:
+ - 'marvim/api-doc-update**'
+ paths:
+ - 'src/nvim/api/*.[ch]'
+ - 'runtime/lua/**.lua'
+ - 'runtime/doc/**'
+
+jobs:
+ call-regen-api-docs:
+ permissions:
+ contents: write
+ pull-requests: write
+ uses: ./.github/workflows/api-docs.yml
+ with:
+ check_only: true
diff --git a/.github/workflows/api-docs.yml b/.github/workflows/api-docs.yml
index ee72cf5f01..83554e2a3d 100644
--- a/.github/workflows/api-docs.yml
+++ b/.github/workflows/api-docs.yml
@@ -1,34 +1,44 @@
+# Autogenerate the API docs on new commit to important branches
+# Also work as a check for PR's to not forget committing their doc changes
+# called from api-docs-check.yml
name: Autogenerate API docs
on:
push:
paths:
- 'src/nvim/api/*.[ch]'
- - 'src/nvim/**.lua'
- 'runtime/lua/**.lua'
+ - 'runtime/doc/**'
branches:
- 'master'
- 'release-[0-9]+.[0-9]+'
workflow_dispatch:
+ workflow_call:
+ inputs:
+ check_only:
+ type: boolean
+ default: false
+ required: false
jobs:
regen-api-docs:
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
permissions:
contents: write
pull-requests: write
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
with:
+ # Fetch depth 0 is required if called through workflow_call. In order
+ # to create a PR we need to access other branches, which requires a
+ # full clone.
fetch-depth: 0
- name: Install dependencies
run: |
sudo apt-get update
- sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y python3 luajit
- conda install -c conda-forge doxygen=1.9.2 msgpack-python
- echo "$CONDA/bin" >> $GITHUB_PATH
+ sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y doxygen python3 python3-msgpack luajit
- name: Setup git config
run: |
@@ -44,10 +54,17 @@ jobs:
python3 scripts/gen_vimdoc.py
printf '::set-output name=UPDATED_DOCS::%s\n' $([ -z "$(git diff)" ]; echo $?)
+ - name: FAIL, PR has not committed doc changes
+ if: ${{ steps.docs.outputs.UPDATED_DOCS != 0 && inputs.check_only }}
+ run: |
+ echo "Job failed, run ./scripts/gen_vimdoc.py and commit your doc changes"
+ echo "The doc generation produces the following changes:"
+ git diff --color --exit-code
+
- name: Automatic PR
- if: ${{ steps.docs.outputs.UPDATED_DOCS != 0 }}
+ if: ${{ steps.docs.outputs.UPDATED_DOCS != 0 && !inputs.check_only }}
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/backport.yml b/.github/workflows/backport.yml
index d281bb1404..b5fd22d036 100644
--- a/.github/workflows/backport.yml
+++ b/.github/workflows/backport.yml
@@ -2,8 +2,6 @@ name: Backport
on:
pull_request_target:
types: [closed, labeled]
- issue_comment:
- types: [created]
jobs:
backport:
permissions:
@@ -14,14 +12,10 @@ jobs:
github.repository_owner == 'neovim' && (
github.event_name == 'pull_request_target' &&
github.event.pull_request.merged
- ) || (
- github.event_name == 'issue_comment' &&
- github.event.issue.pull_request &&
- contains(github.event.comment.body, '/backport')
)
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
with:
# required to find all branches
fetch-depth: 0
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index b650c5dd85..d670db5fac 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -5,10 +5,11 @@ on:
- 'master'
- 'release-[0-9]+.[0-9]+'
pull_request:
- types: [opened, synchronize, reopened, ready_for_review]
branches:
- 'master'
- 'release-[0-9]+.[0-9]+'
+ paths-ignore:
+ - 'runtime/doc/*'
# Cancel any in-progress CI runs for a PR if it is updated
concurrency:
@@ -17,44 +18,131 @@ concurrency:
jobs:
lint:
- if: (github.event_name == 'pull_request' && github.base_ref == 'master' && !github.event.pull_request.draft) || (github.event_name == 'push' && github.ref == 'refs/heads/master')
+ # This job tests two things: it lints the code but also builds neovim using
+ # system dependencies instead of bundled dependencies. This is to make sure
+ # we are able to build neovim without pigeonholing ourselves into specifics
+ # of the bundled dependencies.
+
+ if: (github.event_name == 'pull_request' && github.base_ref == 'master') || (github.event_name == 'push' && github.ref == 'refs/heads/master')
runs-on: ubuntu-20.04
+ timeout-minutes: 10
env:
CC: gcc
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Setup common environment variables
run: ./.github/workflows/env.sh lint
- name: Install apt packages
run: |
+ sudo add-apt-repository ppa:neovim-ppa/stable
sudo apt-get update
- sudo apt-get install -y autoconf automake build-essential ccache cmake cpanminus gettext gperf language-pack-tr libtool-bin locales ninja-build pkg-config python3 python3-pip python3-setuptools unzip flake8
+ sudo apt-get install -y \
+ autoconf \
+ automake \
+ build-essential \
+ ccache \
+ cmake \
+ flake8 \
+ gettext \
+ libluajit-5.1-dev \
+ libmsgpack-dev \
+ libtermkey-dev \
+ libtool-bin \
+ libtree-sitter-dev \
+ libunibilium-dev \
+ libuv1-dev \
+ libvterm-dev \
+ locales \
+ lua-busted \
+ lua-check \
+ lua-filesystem \
+ lua-inspect \
+ lua-lpeg \
+ lua-luv-dev \
+ lua-nvim \
+ luajit \
+ ninja-build \
+ pkg-config
+
+
+ - name: Cache uncrustify
+ id: cache-uncrustify
+ uses: actions/cache@v3
+ with:
+ path: ${{ env.CACHE_UNCRUSTIFY }}
+ key: ${{ env.UNCRUSTIFY_VERSION }}
- - name: Setup interpreter packages
- run: |
- ./ci/install.sh
+ - name: Clone uncrustify
+ if: steps.cache-uncrustify.outputs.cache-hit != 'true'
+ uses: actions/checkout@v3
+ with:
+ repository: uncrustify/uncrustify
+ ref: ${{ env.UNCRUSTIFY_VERSION }}
+ path: uncrustify
- - name: Cache dependencies
- uses: actions/cache@v2
+ - name: Install uncrustify
+ if: steps.cache-uncrustify.outputs.cache-hit != 'true'
+ run: |
+ source_dir=uncrustify
+ build_dir=uncrustify/build
+ cmake -S $source_dir -B $build_dir -G Ninja -DCMAKE_BUILD_TYPE=Release
+ cmake --build $build_dir
+ mkdir -p $HOME/.cache
+ cp $build_dir/uncrustify ${{ env.CACHE_UNCRUSTIFY }}
+
+ - name: Cache artifacts
+ uses: actions/cache@v3
with:
path: |
${{ env.CACHE_NVIM_DEPS_DIR }}
~/.ccache
- key: ${{ matrix.runner }}-lint-${{ matrix.cc }}-${{ hashFiles('cmake/*', 'third-party/**', '**/CMakeLists.txt') }}-${{ github.base_ref }}
+ key: lint-${{ hashFiles('cmake/*', '**/CMakeLists.txt', '!cmake.deps/**CMakeLists.txt') }}-${{ github.base_ref }}
- - name: Build third-party
+ - name: Build third-party deps
run: ./ci/before_script.sh
- - name: Run lint
- run: ./ci/script.sh
+ - name: Build nvim
+ run: ./ci/run_tests.sh build_nvim
+
+ - if: "!cancelled()"
+ name: lintcfull
+ run: make lintcfull
+
+ - if: "!cancelled()"
+ name: lintstylua
+ uses: JohnnyMorganz/stylua-action@1.0.0
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ args: --check runtime/
+
+ - if: "!cancelled()"
+ name: uncrustify
+ run: |
+ ${{ env.CACHE_UNCRUSTIFY }} -c ./src/uncrustify.cfg -q --replace --no-backup $(find ./src/nvim -name "*.[ch]")
+ git diff --color --exit-code
+
+ - if: "!cancelled()"
+ name: lintlua
+ run: make lintlua
+
+ - if: "!cancelled()"
+ name: lintpy
+ run: make lintpy
+
+ - if: "!cancelled()"
+ name: lintsh
+ run: make lintsh
+
+ - if: "!cancelled()"
+ name: check-single-includes
+ run: make check-single-includes
- name: Cache dependencies
- if: ${{ success() }}
run: ./ci/before_cache.sh
- unixish:
+ posix:
name: ${{ matrix.runner }} ${{ matrix.flavor }} (cc=${{ matrix.cc }})
strategy:
fail-fast: false
@@ -68,20 +156,32 @@ jobs:
cc: clang-13
runner: ubuntu-20.04
os: linux
+ - flavor: uchar
+ cc: gcc
+ runner: ubuntu-20.04
+ os: linux
- cc: clang
- runner: macos-10.15
- os: osx
- - cc: clang
- runner: macos-11.0
+ runner: macos-11
os: osx
+
+ # functionaltest-lua is our dumping ground for non-mainline configurations.
+ # 1. Check that the tests pass with PUC Lua instead of LuaJIT.
+ # 2. Use as oldest/minimum versions of dependencies/build tools we
+ # still explicitly support so we don't accidentally rely on
+ # features that is only available on later versions.
+ # 3. No treesitter parsers installed.
+ - flavor: functionaltest-lua
+ cc: gcc
+ runner: ubuntu-20.04
+ os: linux
+ cmake: minimum_required
runs-on: ${{ matrix.runner }}
timeout-minutes: 45
- if: github.event.pull_request.draft == false
env:
CC: ${{ matrix.cc }}
CI_OS_NAME: ${{ matrix.os }}
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Setup common environment variables
run: ./.github/workflows/env.sh ${{ matrix.flavor }}
@@ -90,7 +190,25 @@ jobs:
if: matrix.os == 'linux'
run: |
sudo apt-get update
- sudo apt-get install -y autoconf automake build-essential ccache cmake cpanminus cscope gcc-multilib gdb gettext gperf language-pack-tr libtool-bin locales ninja-build pkg-config python3 python3-pip python3-setuptools unzip valgrind xclip
+ sudo apt-get install -y autoconf automake build-essential ccache cmake cpanminus cscope gcc-multilib gdb gettext language-pack-tr libtool-bin locales ninja-build pkg-config python3 python3-pip python3-setuptools unzip valgrind xclip
+
+ - name: Install minimum required version of cmake
+ if: matrix.cmake == 'minimum_required'
+ env:
+ CMAKE_URL: 'https://cmake.org/files/v3.10/cmake-3.10.0-Linux-x86_64.sh'
+ CMAKE_VERSION: '3.10.0'
+ shell: bash
+ run: |
+ curl --retry 5 --silent --show-error --fail -o /tmp/cmake-installer.sh "$CMAKE_URL"
+ mkdir -p "$HOME/.local/bin" /opt/cmake-custom
+ chmod a+x /tmp/cmake-installer.sh
+ /tmp/cmake-installer.sh --prefix=/opt/cmake-custom --skip-license
+ ln -sfn /opt/cmake-custom/bin/cmake "$HOME/.local/bin/cmake"
+ cmake_version="$(cmake --version | head -1)"
+ echo "$cmake_version" | grep -qF "cmake version $CMAKE_VERSION" || {
+ echo "Unexpected CMake version: $cmake_version"
+ exit 1
+ }
- name: Install new clang
if: matrix.flavor == 'asan' || matrix.flavor == 'tsan'
@@ -103,117 +221,75 @@ jobs:
- name: Install brew packages
if: matrix.os == 'osx'
run: |
- brew update >/dev/null
- brew install automake ccache perl cpanminus ninja
+ brew update --quiet
+ brew install automake ccache cpanminus ninja
- name: Setup interpreter packages
- run: |
- ./ci/install.sh
+ run: ./ci/install.sh
- name: Cache dependencies
- uses: actions/cache@v2
+ uses: actions/cache@v3
with:
path: |
${{ env.CACHE_NVIM_DEPS_DIR }}
~/.ccache
- key: ${{ matrix.runner }}-${{ matrix.flavor }}-${{ matrix.cc }}-${{ hashFiles('cmake/*', 'third-party/**', '**/CMakeLists.txt') }}-${{ github.base_ref }}
+ key: ${{ matrix.runner }}-${{ matrix.flavor }}-${{ matrix.cc }}-${{ hashFiles('cmake/*', 'cmake.deps/**', '**/CMakeLists.txt') }}-${{ github.base_ref }}
- - name: Build third-party
+ - name: Build third-party deps
run: ./ci/before_script.sh
- - name: Build and test
- run: ./ci/script.sh
+ - name: Build
+ run: ./ci/run_tests.sh build_nvim
+
+ - if: matrix.flavor != 'tsan' && matrix.flavor != 'functionaltest-lua' && !cancelled()
+ name: Unittests
+ run: ./ci/run_tests.sh unittests
+
+ - if: matrix.flavor != 'tsan' && !cancelled()
+ name: Functionaltests
+ run: ./ci/run_tests.sh functionaltests
+
+ - if: "!cancelled()"
+ name: Oldtests
+ run: ./ci/run_tests.sh oldtests
+
+ - if: "!cancelled()"
+ name: Install nvim
+ run: ./ci/run_tests.sh install_nvim
- name: Cache dependencies
- if: ${{ success() }}
run: ./ci/before_cache.sh
windows:
- runs-on: windows-2016
- if: github.event.pull_request.draft == false
+ runs-on: windows-2019
+ timeout-minutes: 45
env:
DEPS_BUILD_DIR: ${{ format('{0}/nvim-deps', github.workspace) }}
DEPS_PREFIX: ${{ format('{0}/nvim-deps/usr', github.workspace) }}
-
- strategy:
- fail-fast: false
- matrix:
- config: [ MINGW_64-gcov, MSVC_64 ]
- name: windows (${{ matrix.config }})
+ CMAKE_BUILD_TYPE: "RelWithDebInfo"
+ name: windows (MSVC_64)
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- - uses: actions/cache@v2
+ - uses: actions/cache@v3
with:
path: ${{ env.DEPS_BUILD_DIR }}
- key: ${{ matrix.config }}-${{ hashFiles('third-party\**') }}
+ key: ${{ hashFiles('cmake.deps\**') }}
- - name: Run CI
- run: powershell ci\build.ps1
- env:
- CONFIGURATION: ${{ matrix.config }}
-
- functionaltest:
- name: ${{ matrix.runner }} ${{ matrix.flavor }} (cc=${{ matrix.cc }})
- strategy:
- fail-fast: false
- matrix:
- include:
- - flavor: functionaltest-lua
- cc: gcc
- runner: ubuntu-20.04
- os: linux
- runs-on: ${{ matrix.runner }}
- timeout-minutes: 45
- env:
- CC: ${{ matrix.cc }}
- CI_OS_NAME: ${{ matrix.os }}
- steps:
- - uses: actions/checkout@v2
-
- - name: Setup common environment variables
- run: ./.github/workflows/env.sh ${{ matrix.flavor }}
-
- - name: Install apt packages
- run: |
- sudo apt-get update
- sudo apt-get install -y autoconf automake build-essential ccache cmake cpanminus cscope gcc-multilib gdb gettext gperf language-pack-tr libtool-bin locales ninja-build pkg-config python3 python3-pip python3-setuptools unzip valgrind xclip
-
- - name: Install minimum required version of cmake
- env:
- CMAKE_URL: 'https://cmake.org/files/v3.10/cmake-3.10.0-Linux-x86_64.sh'
- CMAKE_VERSION: '3.10.0'
- shell: bash
- run: |
- curl --retry 5 --silent --show-error --fail -o /tmp/cmake-installer.sh "$CMAKE_URL"
- mkdir -p "$HOME/.local/bin" /opt/cmake-custom
- chmod a+x /tmp/cmake-installer.sh
- /tmp/cmake-installer.sh --prefix=/opt/cmake-custom --skip-license
- ln -sfn /opt/cmake-custom/bin/cmake "$HOME/.local/bin/cmake"
- cmake_version="$(cmake --version | head -1)"
- echo "$cmake_version" | grep -qF "cmake version $CMAKE_VERSION" || {
- echo "Unexpected CMake version: $cmake_version"
- exit 1
- }
+ - name: Build deps
+ run: .\ci\build.ps1 -BuildDeps
- - name: Setup interpreter packages
- run: |
- ./ci/install.sh
+ - name: Build nvim
+ run: .\ci\build.ps1 -Build
- - name: Cache dependencies
- uses: actions/cache@v2
- with:
- path: |
- ${{ env.CACHE_NVIM_DEPS_DIR }}
- ~/.ccache
- key: ${{ matrix.runner }}-${{ matrix.flavor }}-${{ matrix.cc }}-${{ hashFiles('cmake/*', 'third-party/**', '**/CMakeLists.txt') }}-${{ github.base_ref }}
-
- - name: Build third-party
- run: ./ci/before_script.sh
+ - name: Install test deps
+ continue-on-error: false
+ run: .\ci\build.ps1 -EnsureTestDeps
- - name: Build and test
- run: ./ci/script.sh
+ - if: "!cancelled()"
+ name: Run tests
+ run: .\ci\build.ps1 -Test
- - name: Cache dependencies
- if: ${{ success() }}
- run: ./ci/before_cache.sh
+ - if: "!cancelled()"
+ name: Run old tests
+ run: .\ci\build.ps1 -TestOld
diff --git a/.github/workflows/commitlint.yml b/.github/workflows/commitlint.yml
index 6c74ec99d3..68be5436f6 100644
--- a/.github/workflows/commitlint.yml
+++ b/.github/workflows/commitlint.yml
@@ -1,19 +1,23 @@
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]
+ branches:
+ - 'master'
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
+ - uses: actions/checkout@v3
with:
fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha }}
+ path: pr_nvim
- uses: rhysd/action-setup-vim@v1
with:
neovim: true
- - run: nvim --clean -es +"lua require('scripts.lintcommit').main({trace=true})"
+ - run: wget https://raw.githubusercontent.com/neovim/neovim/master/scripts/lintcommit.lua
+ - run: nvim --clean -es +"cd pr_nvim" +"lua dofile('../lintcommit.lua').main({trace=true})"
diff --git a/.github/workflows/coverity-scan.yml b/.github/workflows/coverity-scan.yml
index 7c885f8b88..ce7822b5c1 100644
--- a/.github/workflows/coverity-scan.yml
+++ b/.github/workflows/coverity-scan.yml
@@ -1,19 +1,19 @@
name: Coverity
on:
schedule:
- - cron: '0 10 * * 1' # Run every Monday at 00:10
+ - cron: '10 0 * * *' # Run every day at 00:10
workflow_dispatch:
jobs:
scan:
- runs-on: ubuntu-18.04
+ runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Install dependencies
run: |
sudo apt-get update
- sudo apt-get install -y autoconf automake build-essential cmake gettext gperf libtool-bin locales ninja-build pkg-config unzip
+ sudo apt-get install -y autoconf automake build-essential cmake gettext libtool-bin locales ninja-build pkg-config unzip
- name: Download Coverity
run: |
diff --git a/.github/workflows/env.sh b/.github/workflows/env.sh
index acdbc5da1e..e7c9d19f3a 100755
--- a/.github/workflows/env.sh
+++ b/.github/workflows/env.sh
@@ -9,7 +9,6 @@ EOF
cat <<EOF >> "$GITHUB_ENV"
CACHE_ENABLE=true
-CI_TARGET=tests
CI_BUILD_DIR=$GITHUB_WORKSPACE
BUILD_DIR=$GITHUB_WORKSPACE/build
DEPS_BUILD_DIR=$HOME/nvim-deps
@@ -19,13 +18,15 @@ NVIM_LOG_FILE=$GITHUB_WORKSPACE/build/.nvimlog
VALGRIND_LOG=$GITHUB_WORKSPACE/build/log/valgrind-%p.log
CACHE_NVIM_DEPS_DIR=$HOME/.cache/nvim-deps
CACHE_MARKER=$HOME/.cache/nvim-deps/.ci_cache_marker
+CACHE_UNCRUSTIFY=$HOME/.cache/uncrustify
+UNCRUSTIFY_VERSION=uncrustify-0.75.0
CCACHE_BASEDIR=$GITHUB_WORKSPACE
CCACHE_COMPRESS=1
CCACHE_SLOPPINESS=time_macros,file_macro
CCACHE_DIR=$HOME/.ccache
EOF
-DEPS_CMAKE_FLAGS=-DUSE_BUNDLED_GPERF=OFF
+DEPS_CMAKE_FLAGS=
FUNCTIONALTEST=functionaltest
BUILD_FLAGS="CMAKE_FLAGS=-DCI_BUILD=ON -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX:PATH=$HOME/nvim-install -DBUSTED_OUTPUT_TYPE=nvim -DDEPS_PREFIX=$HOME/nvim-deps/usr -DMIN_LOG_LEVEL=3"
@@ -35,7 +36,7 @@ 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
;;
@@ -45,15 +46,23 @@ TSAN_OPTIONS=log_path=$GITHUB_WORKSPACE/build/log/tsan
CLANG_SANITIZER=TSAN
EOF
;;
+ uchar)
+ cat <<EOF >> "$GITHUB_ENV"
+BUILD_UCHAR=1
+EOF
+ ;;
lint)
+# Re-enable once system deps are available
+# BUILD_FLAGS="$BUILD_FLAGS -DLIBLUV_LIBRARY:FILEPATH=/usr/lib/$(dpkg-architecture -qDEB_HOST_MULTIARCH)/lua/5.1/luv.so -DLIBLUV_INCLUDE_DIR:PATH=/usr/include/lua5.1"
+ DEPS_CMAKE_FLAGS="$DEPS_CMAKE_FLAGS -DUSE_BUNDLED_LUV=ON"
cat <<EOF >> "$GITHUB_ENV"
-CI_TARGET=lint
+USE_BUNDLED=OFF
EOF
;;
functionaltest-lua)
BUILD_FLAGS="$BUILD_FLAGS -DPREFER_LUA=ON"
FUNCTIONALTEST=functionaltest-lua
- DEPS_CMAKE_FLAGS="$DEPS_CMAKE_FLAGS -DUSE_BUNDLED_LUAJIT=OFF"
+ DEPS_CMAKE_FLAGS="$DEPS_CMAKE_FLAGS -DUSE_BUNDLED_LUAJIT=OFF -DUSE_BUNDLED_TS_PARSERS=OFF"
;;
*)
;;
diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml
index a2e0566eb1..f85f9d0cda 100644
--- a/.github/workflows/labeler.yml
+++ b/.github/workflows/labeler.yml
@@ -10,13 +10,13 @@ jobs:
contents: read
pull-requests: write
steps:
- - uses: actions/labeler@main
+ - uses: actions/labeler@v4
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
+ sync-labels: ""
type-scope:
runs-on: ubuntu-latest
- needs: ["triage"]
permissions:
contents: write
pull-requests: write
@@ -27,80 +27,23 @@ jobs:
PR_TITLE: ${{ github.event.pull_request.title }}
steps:
- 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|')"
+ run: gh pr edit "$PR_NUMBER" --add-label "$(echo "$PR_TITLE" | sed -E 's|([[:alpha:]]+)(\(.*\))?!?:.*|\1|')" || true
- 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|')"
+ run: gh pr edit "$PR_NUMBER" --add-label "$(echo "$PR_TITLE" | sed -E 's|[[:alpha:]]+\((.+)\)!?:.*|\1|')" || true
+ - name: "Extract if the PR is a breaking change and add it as label"
+ run: gh pr edit "$PR_NUMBER" --add-label "$(echo "$PR_TITLE" | sed -E 's|[[:alpha:]]+(\(.*\))?!:.*|breaking-change|')" || true
- add-reviewer:
- # This job currently doesn't work due to a bug in GitHub CLI.
- #
- # Issue: https://github.com/cli/cli/issues/4844
- # PR that will fix it: https://github.com/cli/cli/pull/4876
- #
- # The current workaround is to temporarily add "continue-on-error" flag so
- # the whole workflow doesn't get flagged as a failure.
+ request-reviewer:
+ if: github.event.pull_request.state == 'open' && github.event.pull_request.draft == false
runs-on: ubuntu-latest
needs: ["triage", "type-scope"]
permissions:
- contents: write
pull-requests: write
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- GH_REPO: ${{ github.repository }}
- PR_NUMBER: ${{ github.event.pull_request.number }}
- continue-on-error: true
steps:
- - name: Get labels
- id: labels
- run: echo "::set-output name=labels::$(gh pr view $PR_NUMBER --json labels --jq '.labels.[].name' | tr '\n' '%')"
-
- # The % at the end of the label is a hack to avoid selecting a label that
- # is substring of another label. So if there is a label "cinema" then we
- # don't accidentally want to interpret that as the label "ci"
-
- - if: contains(steps.labels.outputs.labels, 'api%')
- run: gh pr edit $PR_NUMBER --add-reviewer bfredl,gpanders,muniter
-
- - if: contains(steps.labels.outputs.labels, 'ci%')
- run: gh pr edit $PR_NUMBER --add-reviewer jamessan
-
- - if: contains(steps.labels.outputs.labels, 'diagnostic%')
- run: gh pr edit $PR_NUMBER --add-reviewer gpanders
-
- - if: contains(steps.labels.outputs.labels, 'distribution%')
- run: gh pr edit $PR_NUMBER --add-reviewer jamessan
-
- - if: contains(steps.labels.outputs.labels, 'documentation%')
- run: gh pr edit $PR_NUMBER --add-reviewer clason
-
- - if: contains(steps.labels.outputs.labels, 'extmarks%')
- run: gh pr edit $PR_NUMBER --add-reviewer bfredl
-
- - if: contains(steps.labels.outputs.labels, 'filetype%')
- run: gh pr edit $PR_NUMBER --add-reviewer clason,gpanders
-
- - if: contains(steps.labels.outputs.labels, 'gui%')
- run: gh pr edit $PR_NUMBER --add-reviewer glacambre,smolck
-
- - if: contains(steps.labels.outputs.labels, 'platform:windows%')
- run: gh pr edit $PR_NUMBER --add-reviewer erw7
-
- - if: contains(steps.labels.outputs.labels, 'lsp%')
- run: gh pr edit $PR_NUMBER --add-reviewer mfussenegger,mjlbach
-
- - if: contains(steps.labels.outputs.labels, 'terminal%')
- run: gh pr edit $PR_NUMBER --add-reviewer erw7
-
- - if: contains(steps.labels.outputs.labels, 'treesitter%')
- run: gh pr edit $PR_NUMBER --add-reviewer bfredl,vigoux
-
- - if: contains(steps.labels.outputs.labels, 'typo%')
- run: gh pr edit $PR_NUMBER --add-reviewer dundargoc
-
- - if: contains(steps.labels.outputs.labels, 'ui%')
- run: gh pr edit $PR_NUMBER --add-reviewer bfredl
-
- - if: contains(steps.labels.outputs.labels, 'vim-patch%')
- run: gh pr edit $PR_NUMBER --add-reviewer janlazo,seandewar
+ - uses: actions/checkout@v3
+ - name: 'Request reviewers'
+ uses: actions/github-script@v6
+ with:
+ script: |
+ const script = require('./.github/scripts/reviews.js')
+ await script({github, context})
diff --git a/.github/workflows/notes.md b/.github/workflows/notes.md
index 9928d472b3..d752f10609 100644
--- a/.github/workflows/notes.md
+++ b/.github/workflows/notes.md
@@ -6,17 +6,40 @@ ${NVIM_VERSION}
### Windows
-1. Extract **nvim-win64.zip**
-2. Run `nvim-qt.exe`
+#### Zip
+
+1. Download **nvim-win64.zip**
+2. Extract the zip.
+3. Run `nvim-qt.exe`
+
+#### MSI
+
+1. Download **nvim-win64.msi**
+2. Run the MSI
+3. Search and run `nvim-qt.exe` or run `nvim.exe` on your CLI of choice.
### macOS
1. Download **nvim-macos.tar.gz**
-2. Extract: `tar xzvf nvim-macos.tar.gz`
-3. Run `./nvim-osx64/bin/nvim`
+2. Run `xattr -c ./nvim-macos.tar.gz` (to avoid "unknown developer" warning)
+3. Extract: `tar xzvf nvim-macos.tar.gz`
+4. Run `./nvim-macos/bin/nvim`
### Linux (x64)
+#### Tarball
+
+1. Download **nvim-linux64.tar.gz**
+2. Extract: `tar xzvf nvim-linux64.tar.gz`
+3. Run `./nvim-linux64/bin/nvim`
+
+#### Debian Package
+
+1. Download **nvim-linux64.deb**
+2. Install the package using `sudo apt install ./nvim-linux64.deb`
+3. Run `nvim`
+
+#### AppImage
1. Download **nvim.appimage**
2. Run `chmod u+x nvim.appimage && ./nvim.appimage`
- If your system does not have FUSE you can [extract the appimage](https://github.com/AppImage/AppImageKit/wiki/FUSE#type-2-appimage):
@@ -32,9 +55,11 @@ ${NVIM_VERSION}
## SHA256 Checksums
```
-${SHA_LINUX_64}
+${SHA_LINUX_64_TAR}
+${SHA_LINUX_64_DEB}
${SHA_APP_IMAGE}
${SHA_APP_IMAGE_ZSYNC}
${SHA_MACOS}
-${SHA_WIN_64}
+${SHA_WIN_64_ZIP}
+${SHA_WIN_64_MSI}
```
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 5ad4c90eaf..2d57c09ab8 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -21,13 +21,13 @@ jobs:
version: ${{ steps.build.outputs.version }}
release: ${{ steps.build.outputs.release }}
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Install dependencies
run: |
sudo apt-get update
- sudo apt-get install -y autoconf automake build-essential cmake gcc-11 gettext gperf libtool-bin locales ninja-build pkg-config unzip
+ sudo apt-get install -y autoconf automake build-essential cmake gcc-11 gettext libtool-bin locales ninja-build pkg-config unzip
- if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.tag_name != 'nightly')
run: printf 'NVIM_BUILD_TYPE=Release\n' >> $GITHUB_ENV
- if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.tag_name == 'nightly')
@@ -39,85 +39,95 @@ jobs:
printf '::set-output name=version::%s\n' "$(./build/bin/nvim --version | head -n 3 | sed -z 's/\n/%0A/g')"
printf '::set-output name=release::%s\n' "$(./build/bin/nvim --version | head -n 1)"
make DESTDIR="$GITHUB_WORKSPACE/build/release/nvim-linux64" install
- cd "$GITHUB_WORKSPACE/build/release"
- tar cfz nvim-linux64.tar.gz nvim-linux64
- - uses: actions/upload-artifact@v2
+ cd "$GITHUB_WORKSPACE/build/"
+ cpack -C $NVIM_BUILD_TYPE
+ - uses: actions/upload-artifact@v3
with:
name: nvim-linux64
- path: build/release/nvim-linux64.tar.gz
+ path: |
+ build/nvim-linux64.tar.gz
+ build/nvim-linux64.deb
retention-days: 1
appimage:
runs-on: ubuntu-18.04
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Install dependencies
run: |
sudo apt-get update
- sudo apt-get install -y autoconf automake build-essential cmake gcc-11 gettext gperf libtool-bin locales ninja-build pkg-config unzip
+ sudo apt-get install -y autoconf automake build-essential cmake gcc-11 gettext libtool-bin locales ninja-build pkg-config unzip
- if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.tag_name != 'nightly')
run: CC=gcc-11 make appimage-latest
- if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.tag_name == 'nightly')
run: CC=gcc-11 make appimage-nightly
- - uses: actions/upload-artifact@v2
+ - uses: actions/upload-artifact@v3
with:
name: appimage
- path: build/bin/nvim.appimage
- retention-days: 1
- - uses: actions/upload-artifact@v2
- with:
- name: appimage
- path: build/bin/nvim.appimage.zsync
+ path: |
+ build/bin/nvim.appimage
+ build/bin/nvim.appimage.zsync
retention-days: 1
macOS:
- runs-on: macos-10.15
+ runs-on: macos-11
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Install brew packages
run: |
- brew update >/dev/null
+ brew update --quiet
brew install automake ninja
- if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.tag_name != 'nightly')
run: printf 'NVIM_BUILD_TYPE=Release\n' >> $GITHUB_ENV
- if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.tag_name == 'nightly')
run: printf 'NVIM_BUILD_TYPE=RelWithDebInfo\n' >> $GITHUB_ENV
+ - name: Provision universal `libintl`
+ run: |
+ GETTEXT_PREFIX="$(brew --prefix gettext)"
+ printf 'GETTEXT_PREFIX=%s\n' "$GETTEXT_PREFIX" >> $GITHUB_ENV
+ bottle_tag="arm64_big_sur"
+ brew fetch --bottle-tag="$bottle_tag" gettext
+ cd "$(mktemp -d)"
+ tar xf "$(brew --cache)"/**/*gettext*${bottle_tag}*.tar.gz
+ lipo gettext/*/lib/libintl.a "${GETTEXT_PREFIX}/lib/libintl.a" -create -output libintl.a
+ mv -f libintl.a /usr/local/lib/
+ - name: Ensure static linkage to `libintl`
+ run: |
+ # We're about to mangle `gettext`, so let's remove any potentially broken
+ # installs (e.g. curl, git) as those could interfere with our build.
+ brew uninstall $(brew uses --installed --recursive gettext)
+ brew unlink gettext
+ ln -sf "$(brew --prefix)/opt/$(readlink "${GETTEXT_PREFIX}")/bin"/* /usr/local/bin/
+ ln -sf "$(brew --prefix)/opt/$(readlink "${GETTEXT_PREFIX}")/include"/* /usr/local/include/
+ rm -f "$GETTEXT_PREFIX"
- name: Build release
run: |
- make CMAKE_BUILD_TYPE=${NVIM_BUILD_TYPE} CMAKE_EXTRA_FLAGS="-DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_OSX_DEPLOYMENT_TARGET=10.11"
- make DESTDIR="$GITHUB_WORKSPACE/build/release/nvim-osx64" install
+ export MACOSX_DEPLOYMENT_TARGET="$(sw_vers -productVersion | cut -f1 -d.)"
+ OSX_FLAGS="-DCMAKE_OSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} -DCMAKE_OSX_ARCHITECTURES=arm64\;x86_64"
+ make CMAKE_BUILD_TYPE=${NVIM_BUILD_TYPE} \
+ CMAKE_EXTRA_FLAGS="-DCMAKE_INSTALL_PREFIX:PATH= $OSX_FLAGS" \
+ DEPS_CMAKE_FLAGS="$OSX_FLAGS"
+ make DESTDIR="$GITHUB_WORKSPACE/build/release/nvim-macos" install
- name: Create package
run: |
cd "$GITHUB_WORKSPACE/build/release"
- mkdir -p nvim-osx64/libs
- libs=($(otool -L nvim-osx64/bin/nvim | sed 1d | sed -E -e 's|^[[:space:]]*||' -e 's| .*||'))
- echo "libs:"
- for lib in "${libs[@]}"; do
- if echo "$lib" | grep -q -E 'libSystem|CoreFoundation' 2>/dev/null; then
- echo " [skipped] $lib"
- else
- echo " $lib"
- relname="libs/${lib##*/}"
- cp -L "$lib" "nvim-osx64/$relname"
- install_name_tool -change "$lib" "@executable_path/../$relname" nvim-osx64/bin/nvim
- fi
- done
- tar cfz nvim-macos.tar.gz nvim-osx64
- - uses: actions/upload-artifact@v2
+ tar cfz nvim-macos.tar.gz nvim-macos
+ - uses: actions/upload-artifact@v3
with:
name: nvim-macos
path: build/release/nvim-macos.tar.gz
retention-days: 1
windows:
- runs-on: windows-2016
+ runs-on: windows-2019
env:
DEPS_BUILD_DIR: ${{ format('{0}/nvim-deps', github.workspace) }}
DEPS_PREFIX: ${{ format('{0}/nvim-deps/usr', github.workspace) }}
+ CMAKE_BUILD_TYPE: "RelWithDebInfo"
strategy:
matrix:
include:
@@ -125,17 +135,19 @@ jobs:
archive: nvim-win64
name: windows (${{ matrix.config }})
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
with:
fetch-depth: 0
- - run: powershell ci\build.ps1 -NoTests
- env:
- CONFIGURATION: ${{ matrix.config }}
- - run: move build\Neovim.zip build\${{ matrix.archive }}.zip
- - uses: actions/upload-artifact@v2
+ - name: Build deps
+ run: .\ci\build.ps1 -BuildDeps
+ - name: build package
+ run: .\ci\build.ps1 -Package
+ - uses: actions/upload-artifact@v3
with:
name: ${{ matrix.archive }}
- path: build/${{ matrix.archive }}.zip
+ path: |
+ build/${{ matrix.archive }}.msi
+ build/${{ matrix.archive }}.zip
retention-days: 1
publish:
@@ -143,19 +155,18 @@ jobs:
runs-on: ubuntu-20.04
env:
GH_REPO: ${{ github.repository }}
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
permissions:
contents: write
steps:
# Must perform checkout first, since it deletes the target directory
# before running, and would therefore delete the downloaded artifacts
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- - uses: actions/download-artifact@v2
+ - uses: actions/download-artifact@v3
- name: Install dependencies
- run: |
- sudo apt-get update
- sudo apt-get install -y gettext-base
+ run: sudo apt-get update && sudo apt-get install -y gettext-base
- if: github.event_name == 'workflow_dispatch'
run: echo "TAG_NAME=${{ github.event.inputs.tag_name }}" >> $GITHUB_ENV
@@ -166,16 +177,12 @@ jobs:
TAG_NAME=${{ github.ref }}
echo "TAG_NAME=${TAG_NAME#refs/tags/}" >> $GITHUB_ENV
- if: env.TAG_NAME == 'nightly'
- env:
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
(echo 'SUBJECT=Nvim development (prerelease) build';
echo 'PRERELEASE=--prerelease') >> $GITHUB_ENV
gh release delete nightly --yes || true
git push origin :nightly || true
- if: env.TAG_NAME != 'nightly'
- env:
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
(echo 'SUBJECT=Nvim release build';
echo 'PRERELEASE=') >> $GITHUB_ENV
@@ -187,7 +194,9 @@ jobs:
run: |
cd ./nvim-linux64
sha256sum nvim-linux64.tar.gz > nvim-linux64.tar.gz.sha256sum
- echo "SHA_LINUX_64=$(cat nvim-linux64.tar.gz.sha256sum)" >> $GITHUB_ENV
+ echo "SHA_LINUX_64_TAR=$(cat nvim-linux64.tar.gz.sha256sum)" >> $GITHUB_ENV
+ sha256sum nvim-linux64.deb > nvim-linux64.deb.sha256sum
+ echo "SHA_LINUX_64_DEB=$(cat nvim-linux64.deb.sha256sum)" >> $GITHUB_ENV
- name: Generate App Image SHA256 checksums
run: |
cd ./appimage
@@ -207,11 +216,13 @@ jobs:
run: |
cd ./nvim-win64
sha256sum nvim-win64.zip > nvim-win64.zip.sha256sum
- echo "SHA_WIN_64=$(cat nvim-win64.zip.sha256sum)" >> $GITHUB_ENV
+ echo "SHA_WIN_64_ZIP=$(cat nvim-win64.zip.sha256sum)" >> $GITHUB_ENV
+ sha256sum nvim-win64.msi > nvim-win64.msi.sha256sum
+ echo "SHA_WIN_64_MSI=$(cat nvim-win64.msi.sha256sum)" >> $GITHUB_ENV
- name: Publish release
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/remove-reviewers-on-draft.yml b/.github/workflows/remove-reviewers-on-draft.yml
new file mode 100644
index 0000000000..f707f79737
--- /dev/null
+++ b/.github/workflows/remove-reviewers-on-draft.yml
@@ -0,0 +1,17 @@
+name: "Remove reviewers"
+on:
+ pull_request_target:
+ types: [converted_to_draft, closed]
+jobs:
+ remove-reviewers:
+ runs-on: ubuntu-latest
+ permissions:
+ pull-requests: write
+ steps:
+ - uses: actions/checkout@v3
+ - name: 'Remove reviewers'
+ uses: actions/github-script@v6
+ with:
+ script: |
+ const script = require('./.github/scripts/remove-reviewers.js')
+ await script({github, context})
diff --git a/.github/workflows/reviews.yml b/.github/workflows/reviews.yml
new file mode 100644
index 0000000000..34ce19d830
--- /dev/null
+++ b/.github/workflows/reviews.yml
@@ -0,0 +1,18 @@
+name: "Request reviews"
+on:
+ pull_request_target:
+ types: [labeled, ready_for_review]
+jobs:
+ request-reviewer:
+ if: github.event.pull_request.state == 'open' && github.event.pull_request.draft == false
+ runs-on: ubuntu-latest
+ permissions:
+ pull-requests: write
+ steps:
+ - uses: actions/checkout@v3
+ - name: 'Request reviewers'
+ uses: actions/github-script@v6
+ with:
+ script: |
+ const script = require('./.github/scripts/reviews.js')
+ await script({github, context})
diff --git a/.github/workflows/vim-patches.yml b/.github/workflows/vim-patches.yml
index 5742b51158..df8c8116b5 100644
--- a/.github/workflows/vim-patches.yml
+++ b/.github/workflows/vim-patches.yml
@@ -5,7 +5,7 @@ on:
jobs:
update-vim-patches:
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
@@ -14,11 +14,11 @@ jobs:
VERSION_BRANCH: marvim/ci-version-update
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
with:
fetch-depth: 0
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
with:
repository: vim/vim
path: ${{ env.VIM_SOURCE_DIR }}
@@ -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/.gitignore b/.gitignore
index 0988a51cd9..11479346c7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,6 +15,7 @@ compile_commands.json
/.clangd/
/.cache/clangd/
/.ccls-cache/
+/.clang-tidy
.DS_Store
*.mo
diff --git a/.mailmap b/.mailmap
new file mode 100644
index 0000000000..7e154b4644
--- /dev/null
+++ b/.mailmap
@@ -0,0 +1,134 @@
+Abdelhakeem <abdelhakeem.osama@hotmail.com> Abdelhakeem Osama
+Alvaro Muñoz <alvaro@pwntester.com> Alvaro Muñoz
+Andreas Johansson <andreas@ndrs.xyz> <ndreas@users.noreply.github.com>
+Andrew Pyatkov <mrbiggfoot@gmail.com> <mrbiggfoot@users.noreply.github.com>
+Anmol Sethi <hi@nhooyr.io> <anmol@aubble.com>
+Anmol Sethi <hi@nhooyr.io> <me@anmol.io>
+Anmol Sethi <hi@nhooyr.io> <nhooyr@users.noreply.github.com>
+BK1603 <chouhan.shreyansh2702@gmail.com> Shreyansh Chouhan
+Billy Su <g4691821@gmail.com> Billy SU
+Billy Vong <billyvg@gmail.com> <billyvg@users.noreply.github.com>
+bfredl <bjorn.linse@gmail.com>
+Carlos Hernandez <carlos@techbyte.ca> <hurricanehrndz@users.noreply.github.com>
+Chris Kipp <ckipp@pm.me> ckipp01
+Christian Clason <c.clason@uni-graz.at> <christian.clason@uni-due.de>
+Cédric Barreteau <> <cbarrete@users.noreply.github.com>
+Dan Aloni <alonid@gmail.com> <dan@kernelim.com>
+Daniel Hahler <git@thequod.de> <github@thequod.de>
+Eisuke Kawashima <e-kwsm@users.noreply.github.com> E Kawashima
+ElPiloto <luis.r.piloto@gmail.com> Luis Piloto
+Eliseo Martínez <eliseomarmol@gmail.com> Eliseo Martínez
+Fabian Viöl <f.vioel@googlemail.com> Fabian
+Florian Walch <florian@fwalch.com> <fwalch@users.noreply.github.com>
+Gabriel Cruz <gabs.oficial98@gmail.com> <LTKills@users.noreply.github.com>
+Gaelan Steele <gbs@canishe.com> Gaelan
+Gavin D. Howard <gavin@schedmd.com> <yzena.tech@gmail.com>
+George Zhao <zhaozg@gmail.com> <zhaozg@aliyun.com>
+George Zhao <zhaozg@gmail.com> George Zhao
+Gregory Anders <greg@gpanders.com> <8965202+gpanders@users.noreply.github.com>
+Gregory Anders <greg@gpanders.com> Greg Anders
+Grzegorz Milka <grzegorzmilka@gmail.com> Grzegorz
+Harm te Hennepe <dhtehennepe@gmail.com> <d.h.tehennepe@student.utwente.nl>
+Harm te Hennepe <dhtehennepe@gmail.com> <harm@tehennepe.org>
+Hirokazu Hata <h.hata.ai.t@gmail.com> <h-michael@users.noreply.github.com>
+Ihor Antonov <ngortheone@gmail.com> <ngortheone@users.noreply.github.com>
+J Phani Mahesh <phanimahesh@gmail.com> <github@phanimahesh.me>
+Jack Bracewell <FriedSock@users.noreply.github.com> <jack.bracewell@unboxedconsulting.com>
+Jack Bracewell <FriedSock@users.noreply.github.com> <jbtwentythree@gmail.com>
+Jacques Germishuys <jacquesg@striata.com> <jacquesg@users.noreply.github.com>
+Jakub Łuczyński <doubleloop@o2.pl> <doubleloop@users.noreply.github.com>
+James McCoy <jamessan@jamessan.com> <vega.james@gmail.com>
+Jan Edmund Lazo <jan.lazo@mail.utoronto.ca> <janedmundlazo@hotmail.com>
+Jan Viljanen <jan.a.viljanen@gmail.com> <jan.viljanen@greenpeace.org>
+Javier Lopez <graulopezjavier@gmail.com> Javier López
+Jit Yao Yap <jityao@gmail.com> <jityao+github@gmail.com>
+Jit Yao Yap <jityao@gmail.com> Jit
+John Gehrig <jdg.gehrig@gmail.com> <jgehrig@users.noreply.github.com>
+John Schmidt <john.schmidt.h@gmail.com> John
+John Szakmeister <john@szakmeister.net> <jszakmeister@users.noreply.github.com>
+Jonathan de Boyne Pollard <J.deBoynePollard-newsgroups@NTLWorld.com> <jdebp@users.noreply.github.com>
+Jonathan de Boyne Pollard <J.deBoynePollard-newsgroups@NTLWorld.com> <postmaster@localhost>
+Jurica Bradaric <jbradaric@gmail.com> <jbradaric@users.noreply.github.com>
+Jurica Bradaric <jbradaric@gmail.com> <jurica.bradaric@avl.com>
+KillTheMule <KillTheMule@users.noreply.github.com> <github@pipsfrank.de>
+Kwon-Young Choi <kwon-young.choi@hotmail.fr> Kwon-Young
+Lucas Hoffmann <l-m-h@web.de> <lucc@posteo.de>
+Lucas Hoffmann <l-m-h@web.de> <lucc@users.noreply.github.com>
+Marco Hinz <mh.codebro@gmail.com> <mh.codebro+github@gmail.com>
+Marvim the Paranoid Android <marvim@users.noreply.github.com> marvim
+Mateusz Czapliński <czapkofan@gmail.com> Mateusz Czaplinski
+Mathias Fussenegger <f.mathias@zignar.net> <mfussenegger@users.noreply.github.com>
+Mathias Fussenegger <f.mathias@zignar.net> Mathias Fußenegger
+Matt Wozniski <godlygeek@gmail.com> <godlygeek+git@gmail.com>
+Matthieu Coudron <mattator@gmail.com> <coudron@iij.ad.jp>
+Matthieu Coudron <mattator@gmail.com> <matthieu.coudron@upmc.fr>
+Matthieu Coudron <mattator@gmail.com> <mcoudron@hotmail.com>
+Matthieu Coudron <mattator@gmail.com> <teto@users.noreply.github.com>
+MichaHoffmann <michoffmann.potsdam@gmail.com> Michael Hoffmann
+MichaHoffmann <michoffmann.potsdam@gmail.com> micha
+Michael Ennen <mike.ennen@gmail.com> <brcolow@users.noreply.github.com>
+Michael Ennen <mike.ennen@gmail.com> brcolow
+Michael Reed <m.reed@mykolab.com> <Pyrohh@users.noreply.github.com>
+Michael Schupikov <michael@schupikov.de> <DarkDeepBlue@users.noreply.github.com>
+Nicolas Hillegeer <nicolas@hillegeer.com> <nicolashillegeer@gmail.com>
+Panashe M. Fundira <fundirap@gmail.com> Panashe Fundira
+Patrice Peterson <patrice.peterson@mailbox.org> runiq
+Pavel Platto <hinidu@gmail.com> Hinidu
+Petter Wahlman <petter@wahlman.no> <pwahlman@cisco.com>
+Poh Zi How <poh.zihow@gmail.com> pohzipohzi
+Rich Wareham <rjw57@cam.ac.uk> <rjw57@cantab.net>
+Rui Abreu Ferreira <equalsraf@users.noreply.github.com> @equalsraf
+Rui Abreu Ferreira <raf-ep@gmx.com> <equalsraf@users.noreply.github.com>
+Rui Abreu Ferreira <raf-ep@gmx.com> <rap-ep@gmx.com>
+Sam Wilson <tecywiz121@hotmail.com> <sawilson@akamai.com>
+Sander Bosma <sanderbosma@gmail.com> sander2
+Santos Gallegos <stsewd@protonmail.com> <santos_g@outlook.com>
+Sebastian Parborg <darkdefende@gmail.com> DarkDefender
+Shirasaka <tk.shirasaka@gmail.com> tk-shirasaka
+Shota <shotat@users.noreply.github.com> shotat
+Shougo Matsushita <Shougo.Matsu@gmail.com> Shougo
+Stephan Seitz <stephan.seitz@fau.de> <stephan.lauf@yahoo.de>
+Steven Sojka <Steven.Sojka@tdameritrade.com> <steelsojka@gmail.com>
+Steven Sojka <steelsojka@gmail.com> <steelsojka@users.noreply.github.com>
+TJ DeVries <devries.timothyj@gmail.com> <timothydvrs1234@gmail.com>
+Thomas Fehér <thomas.feher@yahoo.de> <thomasfeher@web.de>
+Thomas Vigouroux <tomvig38@gmail.com> <39092278+vigoux@users.noreply.github.com>
+Utkarsh Maheshwari <UtkarshME96@gmail.com> UTkarsh Maheshwari
+Utkarsh Maheshwari <utkarshme96@gmail.com> <UtkarshME96@gmail.com>
+VVKot <volodymyr.kot.ua@gmail.com> Volodymyr Kot
+Victor Adam <victor.adam@cofelyineo-gdfsuez.com> <Victor.Adam@derpymail.org>
+Wang Shidong <wsdjeg@outlook.com> <wsdjeg@users.noreply.github.com>
+Wei Huang <daviseago@gmail.com> davix
+Xu Cheng <xucheng@me.com> <xu-cheng@users.noreply.github.com>
+Yamakaky <yamakaky@gmail.com> <yamakaky@yamaworld.fr>
+Yegappan Lakshmanan <yegappan@yahoo.com> <4298407+yegappan@users.noreply.github.com>
+Yichao Zhou <broken.zhoug@gmail.com> Yichao Zhou <broken.zhou@gmail.com>
+Yichao Zhou <broken.zhoug@gmail.com> zhou13 <broken.zhou@gmail.com>
+Yorick Peterse <git@yorickpeterse.com> <yorick@yorickpeterse.com>
+ZyX <kp-pav@yandex.ru> <kp-pav@ya.ru>
+ZyX <kp-pav@yandex.ru> Nikolai Aleksandrovich Pavlov
+aph <a.hewson@gmail.com> Ashley Hewson
+butwerenotthereyet <58348703+butwerenotthereyet@users.noreply.github.com> We're Yet
+chemzqm <chemzqm@gmail.com> Qiming zhao
+chentau <tchen1998@gmail.com> Tony Chen
+dedmass <carlo.abelli@gmail.com> Carlo Abelli
+equal-l2 <eng.equall2@gmail.com> <equal-l2@users.noreply.github.com>
+francisco souza <fsouza@users.noreply.github.com> <108725+fsouza@users.noreply.github.com>
+glacambre <code@lacamb.re> <me@r4>
+glacambre <code@lacamb.re> Ghjuvan Lacambre
+ii14 <ii14@users.noreply.github.com> <59243201+ii14@users.noreply.github.com>
+jdrouhard <john@jmdtech.org> <github@jmdtech.org>
+kuuote <znmxodq1@gmail.com> <36663503+kuuote@users.noreply.github.com>
+matveyt <matthewtarasov@gmail.com> <35012635+matveyt@users.noreply.github.com>
+nate <nateozemon@gmail.com> nateozem
+ray-x <rayx.cn@gmail.com> rayx
+relnod <mail@paul-schiffers.de> <relnod@users.noreply.github.com>
+rockerBOO <rockerboo@gmail.com> Dave Lage
+rpigott <rpigott@berkeley.edu> Ronan Pigott
+sach1t <sach0010t@gmail.com> <sach1t@users.noreply.github.com>
+shade-of-noon <73705427+shade-of-noon@users.noreply.github.com> Edwin Pujols
+shadmansaleh <shadmansaleh3@gmail.com> <13149513+shadmansaleh@users.noreply.github.com>
+shadmansaleh <shadmansaleh3@gmail.com> Shadman
+sohnryang <loop.infinitely@gmail.com> 손량
+timeyyy <timeyyy_da_man@hotmail.com> Timothy C Eichler
+timeyyy <timeyyy_da_man@hotmail.com> timothy eichler
diff --git a/.stylua.toml b/.stylua.toml
new file mode 100644
index 0000000000..a2b3447506
--- /dev/null
+++ b/.stylua.toml
@@ -0,0 +1,6 @@
+column_width = 100
+line_endings = "Unix"
+indent_type = "Spaces"
+indent_width = 2
+quote_style = "AutoPreferSingle"
+call_parentheses = "Always"
diff --git a/.styluaignore b/.styluaignore
new file mode 100644
index 0000000000..c1871de90a
--- /dev/null
+++ b/.styluaignore
@@ -0,0 +1,3 @@
+/scripts
+/src
+/test
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index ec7e19b2f9..0000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,220 +0,0 @@
-dist: bionic
-
-language: c
-
-env:
- global:
- # Encrypted environment variables, see
- # http://docs.travis-ci.com/user/encryption-keys/
- #
- # SNAP_SECRET_KEY: generated by:
- # travis encrypt SNAP_SECRET_KEY=xx --add
- # https://github.com/neovim/neovim/pull/11428
- # snapcraft key expires after 1 year. Steps to refresh it:
- # 1. snapcraft enable-ci travis --refresh
- # 2. mv .snapcraft/travis_snapcraft.cfg ci/snap/travis_snapcraft.cfg
- # 3. Copy after_success command to ci/snap/deploy.sh from .travis.yml
- # 4. Undo changes to .travis.yml
- - secure: hd0qn2u8ABbJg5Bx4pBRcUQbKYFmcSHoecyHIPTCnGJT+NI41Bvm/IkN/N5DhBF+LbD3Q2nmR/dzI5H/dqS7RxMFvEx1DuFLendFHHX3MYf0AuKpXYY3gwgMTmqx8p/v6srlU7RBGWNGzHCWqksAem+EIWCe3I7WvfdKo1/DV/Y=
-
- - PATH="$HOME/.local/bin:$PATH"
- # Set "false" to force rebuild of third-party dependencies.
- - CACHE_ENABLE=true
- # Build directory for Neovim.
- - BUILD_DIR="$TRAVIS_BUILD_DIR/build"
- # Build directory for third-party dependencies.
- - DEPS_BUILD_DIR="$HOME/nvim-deps"
- # Install directory for Neovim.
- - INSTALL_PREFIX="$HOME/nvim-install"
- # Log directory for Clang sanitizers and Valgrind.
- - LOG_DIR="$BUILD_DIR/log"
- # Nvim log file.
- - NVIM_LOG_FILE="$BUILD_DIR/.nvimlog"
- # Default CMake flags.
- - CMAKE_FLAGS="-DCI_BUILD=ON
- -DCMAKE_BUILD_TYPE=Debug
- -DCMAKE_INSTALL_PREFIX:PATH=$INSTALL_PREFIX
- -DBUSTED_OUTPUT_TYPE=nvim
- -DDEPS_PREFIX=$DEPS_BUILD_DIR/usr
- -DMIN_LOG_LEVEL=3"
- - DEPS_CMAKE_FLAGS="-DUSE_BUNDLED_GPERF=OFF"
- # Environment variables for Clang sanitizers.
- - ASAN_OPTIONS="detect_leaks=1:check_initialization_order=1:log_path=$LOG_DIR/asan"
- - TSAN_OPTIONS="log_path=$LOG_DIR/tsan"
- - UBSAN_OPTIONS="print_stacktrace=1 log_path=$LOG_DIR/ubsan"
- # Environment variables for Valgrind.
- - VALGRIND_LOG="$LOG_DIR/valgrind-%p.log"
- - CACHE_NVIM_DEPS_DIR="$HOME/.cache/nvim-deps"
- # If this file exists, the cache is valid (compile was successful).
- - CACHE_MARKER="$CACHE_NVIM_DEPS_DIR/.ci_cache_marker"
- # default target name for functional tests
- - FUNCTIONALTEST=functionaltest
- - CI_TARGET=tests
- # Environment variables for ccache
- - CCACHE_COMPRESS=1
- - CCACHE_SLOPPINESS=time_macros,file_macro
- - CCACHE_BASEDIR="$TRAVIS_BUILD_DIR"
- - CI_OS_NAME="$TRAVIS_OS_NAME"
-
-anchors:
- envs: &common-job-env
- # Do not fall back to cache for "master" for PR on "release" branch:
- # adds the target branch to the cache key.
- FOR_TRAVIS_CACHE=v1-$TRAVIS_BRANCH
-
-addons:
- apt:
- packages: &common-apt-packages
- - apport
- - autoconf
- - automake
- - build-essential
- - clang
- - cmake
- - cpanminus
- - cscope
- - gcc-multilib
- - gdb
- - gperf
- - language-pack-tr
- - libtool-bin
- - locales
- - ninja-build
- - pkg-config
- - unzip
- - valgrind
- - xclip
- homebrew:
- update: true
- casks:
- - powershell
- packages:
- - ccache
- - cpanminus
- - ninja
- - perl
-
-jobs:
- include:
- - name: gcc-coverage (gcc 9)
- os: linux
- compiler: gcc-9
- env:
- - GCOV=gcov-9
- - CMAKE_FLAGS="$CMAKE_FLAGS -DUSE_GCOV=ON"
- - GCOV_ERROR_FILE="/tmp/libgcov-errors.log"
- - USE_LUACOV=1
- - BUSTED_ARGS="--coverage"
- - *common-job-env
- addons:
- snaps:
- - name: powershell
- confinement: classic
- apt:
- sources:
- - sourceline: 'ppa:ubuntu-toolchain-r/test'
- packages:
- - *common-apt-packages
- - gcc-9
- - name: gcc-functionaltest-lua
- os: linux
- compiler: gcc
- env:
- - FUNCTIONALTEST=functionaltest-lua
- - CMAKE_FLAGS="$CMAKE_FLAGS -DPREFER_LUA=ON"
- - DEPS_CMAKE_FLAGS="$DEPS_CMAKE_FLAGS -DUSE_BUNDLED_LUAJIT=OFF"
- - *common-job-env
- - name: gcc-32bit
- os: linux
- # Travis creates a cache per compiler. Set a different value here to
- # store 32-bit dependencies in a separate cache.
- compiler: gcc
- env:
- - BUILD_32BIT=ON
- - CMAKE_FLAGS="$CMAKE_FLAGS -m32 -DCMAKE_TOOLCHAIN_FILE=$TRAVIS_BUILD_DIR/cmake/i386-linux-gnu.toolchain.cmake"
- - DEPS_CMAKE_FLAGS="$DEPS_CMAKE_FLAGS -m32 -DCMAKE_TOOLCHAIN_FILE=$TRAVIS_BUILD_DIR/cmake/i386-linux-gnu.toolchain.cmake"
- - *common-job-env
- - name: big-endian
- os: linux
- arch: s390x
- compiler: gcc
- env:
- - FUNCTIONALTEST=functionaltest-lua
- - CMAKE_FLAGS="$CMAKE_FLAGS -DPREFER_LUA=ON"
- - DEPS_CMAKE_FLAGS="$DEPS_CMAKE_FLAGS -DUSE_BUNDLED_LUAJIT=OFF"
- - *common-job-env
- addons:
- apt:
- packages:
- - *common-apt-packages
- - gettext
- - python-pip
- - python3-pip
- - python-setuptools
- - python3-setuptools
- - python-dev
- - python3-dev
- - if: type != pull_request
- name: snap
- os: linux
- env:
- - LC_ALL: C.UTF-8
- - LANG: C.UTF-8
- - SNAPCRAFT_ENABLE_SILENT_REPORT: y
- - SNAPCRAFT_ENABLE_DEVELOPER_DEBUG: y
- - SNAPCRAFT_BUILD_ENVIRONMENT: lxd
- addons:
- snaps:
- - name: snapcraft
- channel: stable
- classic: true
- - name: http
- - name: transfer
- - name: lxd
- channel: stable
- # Override default before_install, before_cache.
- before_install: /bin/true
- before_cache: /bin/true
- install: ci/snap/install.sh
- before_script: echo "Building snap..."
- script: ci/snap/script.sh
- after_success: ci/snap/after_success.sh
- deploy:
- skip_cleanup: true
- provider: script
- script: ci/snap/deploy.sh
- on:
- branch: master
- allow_failures:
- - env:
- - LC_ALL: C.UTF-8
- - LANG: C.UTF-8
- - SNAPCRAFT_ENABLE_SILENT_REPORT: y
- - SNAPCRAFT_ENABLE_DEVELOPER_DEBUG: y
- - SNAPCRAFT_BUILD_ENVIRONMENT: lxd
- fast_finish: true
-
-before_install: ci/before_install.sh
-install: ci/install.sh
-before_script: ci/before_script.sh
-script: ci/script.sh
-before_cache: ci/before_cache.sh
-
-branches:
- only:
- - master
- - /^release-\d+\.\d+$/
-
-cache:
- apt: true
- ccache: true
- directories:
- - "$CACHE_NVIM_DEPS_DIR"
-
-git:
- quiet: true
-
-notifications:
- webhooks:
- urls:
- - https://webhooks.gitter.im/e/b5c38c99f9677aa3d031
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e0f05e1205..b996a8fb0c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -12,12 +12,18 @@ endif()
if(POLICY CMP0060)
cmake_policy(SET CMP0060 NEW)
endif()
+if(POLICY CMP0075)
+ cmake_policy(SET CMP0075 NEW)
+endif()
# Point CMake at any custom modules we may ship
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
# We don't support building in-tree.
include(PreventInTreeBuilds)
+include(Util)
+
+set(TOUCHES_DIR ${PROJECT_BINARY_DIR}/touches)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
@@ -27,7 +33,7 @@ if(DEFINED ENV{DEPS_BUILD_DIR})
# pkg-config 29.2 has a bug on OpenBSD which causes it to drop any paths that
# *contain* system include paths. To avoid this, we prefix what would be
# "/usr/include" as "/_usr/include".
- # This check is also performed in the third-party/CMakeLists.txt and in the
+ # This check is also performed in the cmake.deps/CMakeLists.txt and in the
# else clause following here.
# https://github.com/neovim/neovim/pull/14745#issuecomment-860201794
set(DEPS_PREFIX "$ENV{DEPS_BUILD_DIR}/_usr" CACHE PATH "Path prefix for finding dependencies")
@@ -59,7 +65,7 @@ else()
-DCMAKE_C_FLAGS_RELWITHDEBINFO=${CMAKE_C_FLAGS_RELWITHDEBINFO}
-DCMAKE_C_FLAGS_RELEASE=${CMAKE_C_FLAGS_RELEASE}
-DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}
- ${PROJECT_SOURCE_DIR}/third-party
+ ${PROJECT_SOURCE_DIR}/cmake.deps
WORKING_DIRECTORY ${DEPS_BUILD_DIR})
execute_process(
COMMAND ${CMAKE_COMMAND} --build ${DEPS_BUILD_DIR}
@@ -68,13 +74,8 @@ else()
endif()
endif()
-if(CMAKE_CROSSCOMPILING AND NOT UNIX)
- list(INSERT CMAKE_FIND_ROOT_PATH 0 ${DEPS_PREFIX})
- list(INSERT CMAKE_PREFIX_PATH 0 ${DEPS_PREFIX}/../host/bin)
-else()
- list(INSERT CMAKE_PREFIX_PATH 0 ${DEPS_PREFIX})
- set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:${DEPS_PREFIX}/lib/pkgconfig")
-endif()
+list(INSERT CMAKE_PREFIX_PATH 0 ${DEPS_PREFIX})
+set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:${DEPS_PREFIX}/lib/pkgconfig")
# used for check_c_compiler_flag
include(CheckCCompilerFlag)
@@ -136,12 +137,12 @@ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY
# If not in a git repo (e.g., a tarball) these tokens define the complete
# version string, else they are combined with the result of `git describe`.
set(NVIM_VERSION_MAJOR 0)
-set(NVIM_VERSION_MINOR 7)
+set(NVIM_VERSION_MINOR 8)
set(NVIM_VERSION_PATCH 0)
set(NVIM_VERSION_PRERELEASE "-dev") # for package maintainers
# API level
-set(NVIM_API_LEVEL 9) # Bump this after any API change.
+set(NVIM_API_LEVEL 10) # Bump this after any API change.
set(NVIM_API_LEVEL_COMPAT 0) # Adjust this after a _breaking_ API change.
set(NVIM_API_PRERELEASE true)
@@ -273,6 +274,7 @@ int main(void)
}
" HAVE_BUILTIN_ADD_OVERFLOW)
+option(ENABLE_COMPILER_SUGGESTIONS "Enable -Wsuggest compiler warnings" OFF)
if(MSVC)
# XXX: /W4 gives too many warnings. #3241
add_compile_options(/W3)
@@ -281,6 +283,9 @@ if(MSVC)
else()
add_compile_options(-Wall -Wextra -pedantic -Wno-unused-parameter
-Wstrict-prototypes -std=gnu99 -Wshadow -Wconversion
+ -Wdouble-promotion
+ -Wmissing-noreturn
+ -Wmissing-format-attribute
-Wmissing-prototypes)
check_c_compiler_flag(-Wimplicit-fallthrough HAVE_WIMPLICIT_FALLTHROUGH_FLAG)
@@ -288,6 +293,29 @@ else()
add_compile_options(-Wimplicit-fallthrough)
endif()
+ if(ENABLE_COMPILER_SUGGESTIONS)
+ # Clang doesn't have -Wsuggest-attribute so check for each one.
+ check_c_compiler_flag(-Wsuggest-attribute=pure HAVE_WSUGGEST_ATTRIBUTE_PURE)
+ if(HAVE_WSUGGEST_ATTRIBUTE_PURE)
+ add_compile_options(-Wsuggest-attribute=pure)
+ endif()
+
+ check_c_compiler_flag(-Wsuggest-attribute=const HAVE_WSUGGEST_ATTRIBUTE_CONST)
+ if(HAVE_WSUGGEST_ATTRIBUTE_CONST)
+ add_compile_options(-Wsuggest-attribute=const)
+ endif()
+
+ check_c_compiler_flag(-Wsuggest-attribute=malloc HAVE_WSUGGEST_ATTRIBUTE_MALLOC)
+ if(HAVE_WSUGGEST_ATTRIBUTE_MALLOC)
+ add_compile_options(-Wsuggest-attribute=malloc)
+ endif()
+
+ check_c_compiler_flag(-Wsuggest-attribute=cold HAVE_WSUGGEST_ATTRIBUTE_COLD)
+ if(HAVE_WSUGGEST_ATTRIBUTE_COLD)
+ add_compile_options(-Wsuggest-attribute=cold)
+ endif()
+ endif()
+
# On FreeBSD 64 math.h uses unguarded C11 extension, which taints clang
# 3.4.1 used there.
if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" AND CMAKE_C_COMPILER_ID MATCHES "Clang")
@@ -343,7 +371,7 @@ option(CI_BUILD "CI, extra flags will be set" OFF)
if(CI_BUILD)
message(STATUS "CI build enabled")
add_compile_options(-Werror)
- if(DEFINED ENV{BUILD_32BIT})
+ if(DEFINED ENV{BUILD_UCHAR})
# Get some test coverage for unsigned char
add_compile_options(-funsigned-char)
endif()
@@ -378,7 +406,7 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_SIZEOF_VOID_P EQUAL 8 AND NOT P
"${CMAKE_MODULE_LINKER_FLAGS} -image_base 100000000")
endif()
-include_directories("${PROJECT_BINARY_DIR}/config")
+include_directories("${PROJECT_BINARY_DIR}/cmake.config")
include_directories("${PROJECT_SOURCE_DIR}/src")
find_package(LibUV 1.28.0 REQUIRED)
@@ -387,7 +415,7 @@ include_directories(SYSTEM ${LIBUV_INCLUDE_DIRS})
find_package(Msgpack 1.0.0 REQUIRED)
include_directories(SYSTEM ${MSGPACK_INCLUDE_DIRS})
-find_package(LibLUV 1.30.0 REQUIRED)
+find_package(LibLUV 1.43.0 REQUIRED)
include_directories(SYSTEM ${LIBLUV_INCLUDE_DIRS})
find_package(TreeSitter REQUIRED)
@@ -422,11 +450,11 @@ 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.
+# The unit test lib requires LuaJIT; it will be skipped if LuaJIT is missing.
option(PREFER_LUA "Prefer Lua over LuaJIT in the nvim executable." OFF)
if(PREFER_LUA)
- find_package(Lua 5.1 REQUIRED)
+ find_package(Lua 5.1 EXACT REQUIRED)
set(LUA_PREFERRED_INCLUDE_DIRS ${LUA_INCLUDE_DIR})
set(LUA_PREFERRED_LIBRARIES ${LUA_LIBRARIES})
# Passive (not REQUIRED): if LUAJIT_FOUND is not set, nvim-test is skipped.
@@ -482,11 +510,6 @@ endif()
find_package(LIBVTERM 0.1 REQUIRED)
include_directories(SYSTEM ${LIBVTERM_INCLUDE_DIRS})
-if(WIN32)
- find_package(Winpty 0.4.3 REQUIRED)
- include_directories(SYSTEM ${WINPTY_INCLUDE_DIRS})
-endif()
-
option(CLANG_ASAN_UBSAN "Enable Clang address & undefined behavior sanitizer for nvim binary." OFF)
option(CLANG_MSAN "Enable Clang memory sanitizer for nvim binary." OFF)
option(CLANG_TSAN "Enable Clang thread sanitizer for nvim binary." OFF)
@@ -556,11 +579,7 @@ endif()
message(STATUS "Using Lua interpreter: ${LUA_PRG}")
-if(DEBUG)
- option(COMPILE_LUA "Pre-compile Lua sources into bytecode (for sources that are included in the binary)" OFF)
-else()
- option(COMPILE_LUA "Pre-compile Lua sources into bytecode (for sources that are included in the binary)" ON)
-endif()
+option(COMPILE_LUA "Pre-compile Lua sources into bytecode (for sources that are included in the binary)" ON)
if(COMPILE_LUA AND NOT WIN32)
if(PREFER_LUA)
@@ -590,17 +609,64 @@ if(NOT BUSTED_OUTPUT_TYPE)
set(BUSTED_OUTPUT_TYPE "nvim")
endif()
+#
+# Lint
+#
find_program(LUACHECK_PRG luacheck)
+find_program(STYLUA_PRG stylua)
find_program(FLAKE8_PRG flake8)
-find_program(GPERF_PRG gperf)
+find_program(UNCRUSTIFY_PRG uncrustify)
+find_program(SHELLCHECK_PRG shellcheck)
+
+add_glob_targets(
+ REQUIRED
+ TARGET lintlua-luacheck
+ COMMAND ${LUACHECK_PRG}
+ FLAGS -q
+ GLOB_DIRS runtime/ scripts/ src/ test/
+ GLOB_PAT *.lua
+ TOUCH_STRATEGY SINGLE
+ )
+
+add_glob_targets(
+ TARGET lintlua-stylua
+ COMMAND ${STYLUA_PRG}
+ FLAGS --color=always --check
+ GLOB_DIRS runtime/
+ GLOB_PAT *.lua
+ TOUCH_STRATEGY SINGLE
+ )
+
+add_custom_target(lintlua)
+add_dependencies(lintlua lintlua-luacheck lintlua-stylua)
include(InstallHelpers)
+add_glob_targets(
+ TARGET lintpy
+ COMMAND ${FLAKE8_PRG}
+ GLOB_DIRS contrib scripts src test
+ GLOB_PAT *.py
+ TOUCH_STRATEGY SINGLE
+ )
+
+add_glob_targets(
+ TARGET lintsh
+ COMMAND ${SHELLCHECK_PRG}
+ FILES scripts/vim-patch.sh
+ TOUCH_STRATEGY SINGLE
+ )
+
+add_custom_target(lintcommit
+ COMMAND ${PROJECT_BINARY_DIR}/bin/nvim -u NONE -es -c [[lua require('scripts.lintcommit').main({trace=false})]]
+ WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
+ VERBATIM)
+add_dependencies(lintcommit nvim)
+
+add_custom_target(lint)
+add_dependencies(lint check-single-includes lintc lintlua lintpy lintsh lintcommit lintuncrustify)
-file(GLOB MANPAGES
- RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
- man/nvim.1)
install_helper(
- FILES ${MANPAGES}
+ FILES ${CMAKE_SOURCE_DIR}/src/man/nvim.1
DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
#
@@ -610,7 +676,7 @@ install_helper(
add_subdirectory(src/nvim)
get_directory_property(NVIM_VERSION_CFLAGS DIRECTORY src/nvim DEFINITION NVIM_VERSION_CFLAGS)
add_subdirectory(test/includes)
-add_subdirectory(config)
+add_subdirectory(cmake.config)
add_subdirectory(test/functional/fixtures) # compile test programs
add_subdirectory(runtime)
get_directory_property(GENERATED_HELP_TAGS DIRECTORY runtime DEFINITION GENERATED_HELP_TAGS)
@@ -633,20 +699,9 @@ if(BUSTED_PRG)
list(APPEND TEST_TARGET_ARGS "USES_TERMINAL")
set(UNITTEST_PREREQS nvim-test unittest-headers)
- set(FUNCTIONALTEST_PREREQS nvim printenv-test printargs-test shell-test streams-test tty-test ${GENERATED_HELP_TAGS})
+ set(FUNCTIONALTEST_PREREQS nvim printenv-test printargs-test shell-test pwsh-test streams-test tty-test ${GENERATED_HELP_TAGS})
set(BENCHMARK_PREREQS nvim tty-test)
- # Useful for automated build systems, if they want to manually run the tests.
- add_custom_target(unittest-prereqs
- DEPENDS ${UNITTEST_PREREQS})
- set_target_properties(unittest-prereqs PROPERTIES FOLDER test)
-
- add_custom_target(functionaltest-prereqs
- DEPENDS ${FUNCTIONALTEST_PREREQS})
-
- add_custom_target(benchmark-prereqs
- DEPENDS ${BENCHMARK_PREREQS})
-
check_lua_module(${LUA_PRG} "ffi" LUA_HAS_FFI)
if(LUA_HAS_FFI)
add_custom_target(unittest
@@ -672,11 +727,11 @@ if(BUSTED_PRG)
set(TEST_LIBNVIM_PATH "")
endif()
configure_file(
- ${CMAKE_SOURCE_DIR}/test/config/paths.lua.in
- ${CMAKE_BINARY_DIR}/test/config/paths.lua.gen)
+ ${CMAKE_SOURCE_DIR}/test/cmakeconfig/paths.lua.in
+ ${CMAKE_BINARY_DIR}/test/cmakeconfig/paths.lua.gen)
file(GENERATE
- OUTPUT ${CMAKE_BINARY_DIR}/test/config/paths.lua
- INPUT ${CMAKE_BINARY_DIR}/test/config/paths.lua.gen)
+ OUTPUT ${CMAKE_BINARY_DIR}/test/cmakeconfig/paths.lua
+ INPUT ${CMAKE_BINARY_DIR}/test/cmakeconfig/paths.lua.gen)
add_custom_target(functionaltest
COMMAND ${CMAKE_COMMAND}
@@ -691,8 +746,7 @@ if(BUSTED_PRG)
-P ${PROJECT_SOURCE_DIR}/cmake/RunTests.cmake
DEPENDS ${FUNCTIONALTEST_PREREQS}
${TEST_TARGET_ARGS})
- set_target_properties(functionaltest functionaltest-prereqs
- PROPERTIES FOLDER test)
+ set_target_properties(functionaltest PROPERTIES FOLDER test)
add_custom_target(benchmark
COMMAND ${CMAKE_COMMAND}
@@ -707,7 +761,7 @@ if(BUSTED_PRG)
-P ${PROJECT_SOURCE_DIR}/cmake/RunTests.cmake
DEPENDS ${BENCHMARK_PREREQS}
${TEST_TARGET_ARGS})
- set_target_properties(benchmark benchmark-prereqs PROPERTIES FOLDER test)
+ set_target_properties(benchmark PROPERTIES FOLDER test)
endif()
if(BUSTED_LUA_PRG)
@@ -727,34 +781,10 @@ if(BUSTED_LUA_PRG)
set_target_properties(functionaltest-lua PROPERTIES FOLDER test)
endif()
-if(LUACHECK_PRG)
- add_custom_target(lualint
- COMMAND ${LUACHECK_PRG} -q runtime/ scripts/ src/ test/
- WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
-else()
- add_custom_target(lualint false
- COMMENT "lualint: LUACHECK_PRG not defined")
-endif()
-
-set(CPACK_PACKAGE_NAME "Neovim")
-set(CPACK_PACKAGE_VENDOR "neovim.io")
-set(CPACK_PACKAGE_VERSION ${NVIM_VERSION_MEDIUM})
-set(CPACK_PACKAGE_INSTALL_DIRECTORY "Neovim")
-# Set toplevel directory/installer name as Neovim
-set(CPACK_PACKAGE_FILE_NAME "Neovim")
-set(CPACK_TOPLEVEL_TAG "Neovim")
-set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE")
-set(CPACK_NSIS_MODIFY_PATH ON)
-set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON)
-include(CPack)
-
-#add uninstall target
-if(NOT TARGET uninstall)
- configure_file(
- "cmake/UninstallHelper.cmake.in"
- "${CMAKE_CURRENT_BINARY_DIR}/UninstallHelper.cmake"
- IMMEDIATE @ONLY)
-
- add_custom_target(uninstall
- COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/UninstallHelper.cmake)
+add_custom_target(uninstall
+ COMMAND ${CMAKE_COMMAND} -P ${PROJECT_SOURCE_DIR}/cmake/UninstallHelper.cmake)
+
+if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR})
+ add_subdirectory(cmake.packaging)
endif()
+
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index b02aeb1ed1..e26d0d63c5 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
------------------
@@ -92,7 +92,7 @@ the VCS/git logs more valuable. The general structure of a commit message is:
```
- Prefix the commit subject with one of these [_types_](https://github.com/commitizen/conventional-commit-types/blob/master/index.json):
- - `build`, `ci`, `docs`, `feat`, `fix`, `perf`, `refactor`, `revert`, `test`, `vim-patch`, `chore`
+ - `build`, `ci`, `docs`, `feat`, `fix`, `perf`, `refactor`, `revert`, `test`, `vim-patch`, `dist`
- You can **ignore this for "fixup" commits** or any commits you expect to be squashed.
- Append optional scope to _type_ such as `(lsp)`, `(treesitter)`, `(float)`, …
- _Description_ shouldn't start with a capital letter or end in a period.
@@ -289,7 +289,7 @@ as context, use the `-W` argument as well.
[complexity:low]: https://github.com/neovim/neovim/issues?q=is%3Aopen+is%3Aissue+label%3Acomplexity%3Alow
[master error list]: https://raw.githubusercontent.com/neovim/doc/gh-pages/reports/clint/errors.json
[wiki-contribute-help]: https://github.com/neovim/neovim/wiki/contribute-%3Ahelp
-[pr-draft]: https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request
+[pr-draft]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request
[pr-ready]: https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-stage-of-a-pull-request
[uncrustify]: http://uncrustify.sourceforge.net/
[lua-language-server]: https://github.com/sumneko/lua-language-server/
diff --git a/LICENSE b/LICENSE.txt
index c1b8286c4b..c1b8286c4b 100644
--- a/LICENSE
+++ b/LICENSE.txt
diff --git a/MAINTAIN.md b/MAINTAIN.md
index 7df91b7d57..9a8531fbd9 100644
--- a/MAINTAIN.md
+++ b/MAINTAIN.md
@@ -52,21 +52,18 @@ has a major bug:
3. Cut a release from `release-x.y`.
- Run `./scripts/release.sh`
- Update (force-push) the remote `stable` tag.
- - The [nightly job](https://github.com/neovim/bot-ci/blob/master/ci/nightly.sh)
+ - The [CI job](https://github.com/neovim/neovim/blob/3d45706478cd030c3ee05b4f336164bb96138095/.github/workflows/release.yml#L11-L13)
will update the release assets based on the `stable` tag.
The neovim repository includes a backport [github action](https://github.com/zeebe-io/backport-action).
In order to trigger the action, a PR must be labeled with a label matching the
-form `backport release-0.X`. If the label is applied before the PR is merged,
-the backport will be filed automatically against the target branch. Otherwise,
-comment `\backport` on the merged PR *after* the label has been applied to trigger
-a backport. Note, the PR must have a description in the issue body, or the backport
-will fail.
+form `backport release-0.X`. Note, the PR must have a description in the issue body,
+or the backport will fail.
Third-party dependencies
--------------
-These "bundled" dependencies can be updated by bumping their versions in `third-party/CMakeLists.txt`:
+These "bundled" dependencies can be updated by bumping their versions in `cmake.deps/CMakeLists.txt`:
- [Lua](https://www.lua.org/download.html)
- [LuaJIT](https://github.com/LuaJIT/LuaJIT)
- [Luv](https://github.com/luvit/luv)
@@ -76,11 +73,18 @@ These "bundled" dependencies can be updated by bumping their versions in `third-
- [lua-compat](https://github.com/keplerproject/lua-compat-5.3)
- [tree-sitter](https://github.com/tree-sitter/tree-sitter)
+`scripts/bump-dep.sh` is a script that can automate this process for `LuaJIT`, `Luv`, `libuv` & `tree-sitter`. See usage guide:
+ - Run `./scripts/bump-deps.sh --dep Luv --version 1.43.0-0` to update a dependency.
+ See `./scripts/bump-deps.sh -h` for more detailed usage
+ - Run `./scripts/bump-deps.sh --pr` to create a pr
+ To generate the default PR title and body, the script uses the most recent commit (not in `master`) with prefix `build(deps): `
+
These dependencies are "vendored" (inlined), we need to update the sources manually:
- [libmpack](https://github.com/libmpack/libmpack)
- [xdiff](https://github.com/git/git/tree/master/xdiff)
- [lua-cjson](https://github.com/openresty/lua-cjson)
- [Klib](https://github.com/attractivechaos/klib)
+ - [inspect.lua](https://github.com/kikito/inspect.lua)
We also maintain some forks, particularly for Windows, if we are waiting on upstream changes:
https://github.com/neovim/neovim/wiki/Deps
diff --git a/Makefile b/Makefile
index 86e723df67..e9a59f0941 100644
--- a/Makefile
+++ b/Makefile
@@ -47,13 +47,7 @@ endif
ifeq (,$(BUILD_TOOL))
ifeq (Ninja,$(CMAKE_GENERATOR))
- ifneq ($(shell $(CMAKE_PRG) --help 2>/dev/null | grep Ninja),)
- BUILD_TOOL = ninja
- else
- # User's version of CMake doesn't support Ninja
- BUILD_TOOL = $(MAKE)
- CMAKE_GENERATOR := Unix Makefiles
- endif
+ BUILD_TOOL = ninja
else
BUILD_TOOL = $(MAKE)
endif
@@ -102,7 +96,7 @@ build/.ran-cmake: | deps
cd build && $(CMAKE_PRG) -G '$(CMAKE_GENERATOR)' $(CMAKE_FLAGS) $(CMAKE_EXTRA_FLAGS) $(MAKEFILE_DIR)
touch $@
-deps: | build/.ran-third-party-cmake
+deps: | build/.ran-deps-cmake
ifeq ($(call filter-true,$(USE_BUNDLED)),)
+$(BUILD_TOOL) -C $(DEPS_BUILD_DIR)
endif
@@ -110,12 +104,12 @@ endif
ifeq ($(call filter-true,$(USE_BUNDLED)),)
$(DEPS_BUILD_DIR):
mkdir -p "$@"
-build/.ran-third-party-cmake:: $(DEPS_BUILD_DIR)
+build/.ran-deps-cmake:: $(DEPS_BUILD_DIR)
cd $(DEPS_BUILD_DIR) && \
$(CMAKE_PRG) -G '$(CMAKE_GENERATOR)' $(BUNDLED_CMAKE_FLAG) $(BUNDLED_LUA_CMAKE_FLAG) \
- $(DEPS_CMAKE_FLAGS) $(MAKEFILE_DIR)/third-party
+ $(DEPS_CMAKE_FLAGS) $(MAKEFILE_DIR)/cmake.deps
endif
-build/.ran-third-party-cmake::
+build/.ran-deps-cmake::
mkdir -p build
touch $@
@@ -140,43 +134,11 @@ build/runtime/doc/tags helptags: | nvim
helphtml: | nvim build/runtime/doc/tags
+$(BUILD_TOOL) -C build doc_html
-functionaltest: | nvim
- +$(BUILD_TOOL) -C build functionaltest
+functionaltest functionaltest-lua unittest benchmark: | nvim
+ $(BUILD_TOOL) -C build $@
-functionaltest-lua: | nvim
- +$(BUILD_TOOL) -C build functionaltest-lua
-
-lualint: | build/.ran-cmake deps
- $(BUILD_TOOL) -C build lualint
-
-shlint:
- @shellcheck --version | head -n 2
- shellcheck scripts/vim-patch.sh
-
-_opt_shlint:
- @command -v shellcheck && { $(MAKE) shlint; exit $$?; } \
- || echo "SKIP: shlint (shellcheck not found)"
-
-pylint:
- flake8 contrib/ scripts/ src/ test/
-
-# Run pylint only if flake8 is installed.
-_opt_pylint:
- @command -v flake8 && { $(MAKE) pylint; exit $$?; } \
- || echo "SKIP: pylint (flake8 not found)"
-
-commitlint:
- $(NVIM_PRG) --clean -es +"lua require('scripts.lintcommit').main({trace=false})"
-
-_opt_commitlint:
- @test -x build/bin/nvim && { $(MAKE) commitlint; exit $$?; } \
- || echo "SKIP: commitlint (build/bin/nvim not found)"
-
-unittest: | nvim
- +$(BUILD_TOOL) -C build unittest
-
-benchmark: | nvim
- +$(BUILD_TOOL) -C build benchmark
+lintlua lintsh lintpy lintuncrustify lintc lintcfull check-single-includes generated-sources lintcommit lint: | build/.ran-cmake
+ $(CMAKE_PRG) --build build --target $@
test: functionaltest unittest
@@ -193,18 +155,6 @@ distclean:
install: checkprefix nvim
+$(BUILD_TOOL) -C build install
-clint: build/.ran-cmake
- +$(BUILD_TOOL) -C build clint
-
-clint-full: build/.ran-cmake
- +$(BUILD_TOOL) -C build clint-full
-
-check-single-includes: build/.ran-cmake
- +$(BUILD_TOOL) -C build check-single-includes
-
-generated-sources: build/.ran-cmake
- +$(BUILD_TOOL) -C build generated-sources
-
appimage:
bash scripts/genappimage.sh
@@ -214,8 +164,6 @@ appimage:
appimage-%:
bash scripts/genappimage.sh $*
-lint: check-single-includes clint lualint _opt_pylint _opt_shlint _opt_commitlint
-
# Generic pattern rules, allowing for `make build/bin/nvim` etc.
# Does not work with "Unix Makefiles".
ifeq ($(CMAKE_GENERATOR),Ninja)
@@ -226,4 +174,4 @@ $(DEPS_BUILD_DIR)/%: phony_force
$(BUILD_TOOL) -C $(DEPS_BUILD_DIR) $(patsubst $(DEPS_BUILD_DIR)/%,%,$@)
endif
-.PHONY: test lualint pylint shlint functionaltest unittest lint clint clean distclean nvim libnvim cmake deps install appimage checkprefix commitlint
+.PHONY: test lintlua lintpy lintsh functionaltest unittest lint lintc clean distclean nvim libnvim cmake deps install appimage checkprefix lintcommit
diff --git a/README.md b/README.md
index 8342c27fd7..e3c3c12104 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,6 @@
-[![Neovim](https://raw.githubusercontent.com/neovim/neovim.github.io/master/logos/neovim-logo-300x87.png)](https://neovim.io)
+<h1 align="center">
+ <img src="https://raw.githubusercontent.com/neovim/neovim.github.io/master/logos/neovim-logo-300x87.png" alt="Neovim">
+</h1>
[Documentation](https://neovim.io/doc/general/) |
[Chat](https://app.element.io/#/room/#neovim:matrix.org) |
@@ -61,7 +63,7 @@ After installing the dependencies, run the following command.
To install to a non-default location:
- make CMAKE_INSTALL_PREFIX=/full/path/
+ make CMAKE_BUILD_TYPE=RelWithDebInfo CMAKE_INSTALL_PREFIX=/full/path/
make install
CMake hints for inspecting the build:
@@ -79,8 +81,10 @@ Project layout
--------------
├─ ci/ build automation
- ├─ cmake/ build scripts
- ├─ runtime/ user plugins/docs
+ ├─ cmake/ CMake utils
+ ├─ cmake.config/ CMake defines
+ ├─ cmake.deps/ subproject to fetch and build dependencies (optional)
+ ├─ runtime/ plugins and docs
├─ src/nvim/ application source code (see src/nvim/README.md)
│ ├─ api/ API subsystem
│ ├─ eval/ VimL subsystem
@@ -91,7 +95,6 @@ Project layout
│ ├─ msgpack_rpc/ RPC subsystem
│ ├─ os/ low-level platform code
│ └─ tui/ built-in UI
- ├─ third-party/ CMake subproject to build dependencies
└─ test/ tests (see test/README.md)
License
@@ -119,7 +122,7 @@ Apache 2.0 license, except for contributions copied from Vim (identified by the
[Managed packages]: https://github.com/neovim/neovim/wiki/Installing-Neovim#install-from-package
[Debian]: https://packages.debian.org/testing/neovim
[Ubuntu]: http://packages.ubuntu.com/search?keywords=neovim
-[Fedora]: https://apps.fedoraproject.org/packages/neovim
+[Fedora]: https://packages.fedoraproject.org/pkgs/neovim/neovim/
[Arch Linux]: https://www.archlinux.org/packages/?q=neovim
[Void Linux]: https://voidlinux.org/packages/?arch=x86_64&q=neovim
[Gentoo]: https://packages.gentoo.org/packages/app-editors/neovim
diff --git a/ci/before_cache.sh b/ci/before_cache.sh
index c86109168e..bec6c37bbe 100755
--- a/ci/before_cache.sh
+++ b/ci/before_cache.sh
@@ -7,6 +7,8 @@ CI_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${CI_DIR}/common/build.sh"
source "${CI_DIR}/common/suite.sh"
+mkdir -p "${HOME}/.cache"
+
echo "before_cache.sh: cache size"
du -chd 1 "${HOME}/.cache" | sort -rh | head -20
@@ -16,7 +18,7 @@ ccache -s 2>/dev/null || true
find "${HOME}/.ccache" -name stats -delete
# Update the third-party dependency cache only if the build was successful.
-if ended_successfully; then
+if ended_successfully && [ -d "${DEPS_BUILD_DIR}" ]; then
# Do not cache downloads. They should not be needed with up-to-date deps.
rm -rf "${DEPS_BUILD_DIR}/build/downloads"
rm -rf "${CACHE_NVIM_DEPS_DIR}"
diff --git a/ci/build.ps1 b/ci/build.ps1
index a81d351bc6..6c042f9116 100644
--- a/ci/build.ps1
+++ b/ci/build.ps1
@@ -1,182 +1,144 @@
-param([switch]$NoTests)
-Set-StrictMode -Version Latest
-$ErrorActionPreference = 'Stop'
-$ProgressPreference = 'SilentlyContinue'
-
-$env:CONFIGURATION -match '^(?<compiler>\w+)_(?<bits>32|64)(?:-(?<option>\w+))?$'
-$compiler = $Matches.compiler
-$compileOption = if ($Matches -contains 'option') {$Matches.option} else {''}
-$bits = $Matches.bits
-$cmakeBuildType = $(if ($env:CMAKE_BUILD_TYPE -ne $null) {$env:CMAKE_BUILD_TYPE} else {'RelWithDebInfo'});
-$buildDir = [System.IO.Path]::GetFullPath("$(pwd)")
-$depsCmakeVars = @{
- CMAKE_BUILD_TYPE = $cmakeBuildType;
-}
-$nvimCmakeVars = @{
- CMAKE_BUILD_TYPE = $cmakeBuildType;
- BUSTED_OUTPUT_TYPE = 'nvim';
- DEPS_PREFIX=$(if ($env:DEPS_PREFIX -ne $null) {$env:DEPS_PREFIX} else {".deps/usr"});
-}
-if ($env:DEPS_BUILD_DIR -eq $null) {
- $env:DEPS_BUILD_DIR = ".deps";
-}
-$uploadToCodeCov = $false
-
-function exitIfFailed() {
- if ($LastExitCode -ne 0) {
- exit $LastExitCode
- }
-}
-
-if (-not $NoTests) {
- node --version
- npm.cmd --version
-}
-
-if (-Not (Test-Path -PathType container $env:DEPS_BUILD_DIR)) {
- write-host "cache dir not found: $($env:DEPS_BUILD_DIR)"
- mkdir $env:DEPS_BUILD_DIR
-} else {
- write-host "cache dir $($env:DEPS_BUILD_DIR) size: $(Get-ChildItem $env:DEPS_BUILD_DIR -recurse | Measure-Object -property length -sum | Select -expand sum)"
-}
-
-if ($compiler -eq 'MINGW') {
- if ($bits -eq 32) {
- $arch = 'i686'
- }
- elseif ($bits -eq 64) {
- $arch = 'x86_64'
- }
- if ($compileOption -eq 'gcov') {
- $nvimCmakeVars['USE_GCOV'] = 'ON'
- $uploadToCodecov = $true
- $env:GCOV = "C:\msys64\mingw$bits\bin\gcov"
-
- # Setup/build Lua coverage.
- $env:USE_LUACOV = 1
- $env:BUSTED_ARGS = "--coverage"
- }
- # These are native MinGW builds, but they use the toolchain inside
- # MSYS2, this allows using all the dependencies and tools available
- # in MSYS2, but we cannot build inside the MSYS2 shell.
- $cmakeGenerator = 'Ninja'
- $cmakeGeneratorArgs = '-v'
- $mingwPackages = @('ninja', 'cmake', 'diffutils').ForEach({
- "mingw-w64-$arch-$_"
- })
-
- # Add MinGW to the PATH
- $env:PATH = "C:\msys64\mingw$bits\bin;$env:PATH"
-
- # Avoid pacman "warning" which causes non-zero return code. https://github.com/open62541/open62541/issues/2068
- & C:\msys64\usr\bin\mkdir -p /var/cache/pacman/pkg
-
- # Build third-party dependencies
- C:\msys64\usr\bin\bash -lc "pacman --verbose --noconfirm -Syu" ; exitIfFailed
- C:\msys64\usr\bin\bash -lc "pacman --verbose --noconfirm --needed -S $mingwPackages" ; exitIfFailed
-}
-elseif ($compiler -eq 'MSVC') {
- $cmakeGeneratorArgs = '/verbosity:normal'
- if ($bits -eq 32) {
- $cmakeGenerator = 'Visual Studio 15 2017'
- }
- elseif ($bits -eq 64) {
- $cmakeGenerator = 'Visual Studio 15 2017 Win64'
- }
-}
-
-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 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
-
- npm.cmd install -g neovim
- Get-Command -CommandType Application neovim-node-host.cmd
- npm.cmd link neovim
-}
-
-if ($compiler -eq 'MSVC') {
- # Required for LuaRocks (https://github.com/luarocks/luarocks/issues/1039#issuecomment-507296940).
- $env:VCINSTALLDIR = "C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/Tools/MSVC/14.16.27023/"
-}
-
-function convertToCmakeArgs($vars) {
- return $vars.GetEnumerator() | foreach { "-D$($_.Key)=$($_.Value)" }
-}
-
-cd $env:DEPS_BUILD_DIR
-cmake -G $cmakeGenerator $(convertToCmakeArgs($depsCmakeVars)) "$buildDir/third-party/" ; exitIfFailed
-cmake --build . --config $cmakeBuildType -- $cmakeGeneratorArgs ; exitIfFailed
-cd $buildDir
-
-# Build Neovim
-mkdir build
-cd build
-cmake -G $cmakeGenerator $(convertToCmakeArgs($nvimCmakeVars)) .. ; exitIfFailed
-cmake --build . --config $cmakeBuildType -- $cmakeGeneratorArgs ; exitIfFailed
-.\bin\nvim --version ; exitIfFailed
-
-# Ensure that the "win32" feature is set.
-.\bin\nvim -u NONE --headless -c 'exe !has(\"win32\").\"cq\"' ; exitIfFailed
-
-if ($env:USE_LUACOV -eq 1) {
- & $env:DEPS_PREFIX\luarocks\luarocks.bat install cluacov
-}
-
-if (-not $NoTests) {
- # Functional tests
- # The $LastExitCode from MSBuild can't be trusted
- $failed = $false
-
- # Run only this test file:
- # $env:TEST_FILE = "test\functional\foo.lua"
- cmake --build . --config $cmakeBuildType --target functionaltest -- $cmakeGeneratorArgs 2>&1 |
- foreach { $failed = $failed -or
- $_ -match 'functional tests failed with error'; $_ }
-
- if ($uploadToCodecov) {
- if ($env:USE_LUACOV -eq 1) {
- & $env:DEPS_PREFIX\bin\luacov.bat
- }
- bash -l /c/projects/neovim/ci/common/submit_coverage.sh functionaltest
- }
- if ($failed) {
- exit $LastExitCode
- }
-
- # Old tests
- # Add MSYS to path, required for e.g. `find` used in test scripts.
- # But would break functionaltests, where its `more` would be used then.
- $OldPath = $env:PATH
- $env:PATH = "C:\msys64\usr\bin;$env:PATH"
- & "C:\msys64\mingw$bits\bin\mingw32-make.exe" -C $(Convert-Path ..\src\nvim\testdir) VERBOSE=1 ; exitIfFailed
- $env:PATH = $OldPath
-
- if ($uploadToCodecov) {
- bash -l /c/projects/neovim/ci/common/submit_coverage.sh oldtest
- }
-}
-
-# Ensure choco's cpack is not in PATH otherwise, it conflicts with CMake's
-if (Test-Path -Path $env:ChocolateyInstall\bin\cpack.exe) {
- Remove-Item -Path $env:ChocolateyInstall\bin\cpack.exe -Force
-}
-
-# Build artifacts
-cpack -G ZIP -C RelWithDebInfo
-if ($env:APPVEYOR_REPO_TAG_NAME -ne $null) {
- cpack -G NSIS -C RelWithDebInfo
-}
+[CmdletBinding(DefaultParameterSetName = "Build")]
+param(
+ [Parameter(ParameterSetName="Build")][switch]$Build,
+ [Parameter(ParameterSetName="BuildDeps")][switch]$BuildDeps,
+ [Parameter(ParameterSetName="EnsureTestDeps")][switch]$EnsureTestDeps,
+ [Parameter(ParameterSetName="Package")][switch]$Package,
+ [Parameter(ParameterSetName="Test")][switch]$Test,
+ [Parameter(ParameterSetName="TestOld")][switch]$TestOld
+)
+
+Set-StrictMode -Version Latest
+$ErrorActionPreference = 'Stop'
+$ProgressPreference = 'SilentlyContinue'
+
+$projectDir = [System.IO.Path]::GetFullPath("$(Get-Location)")
+$buildDir = Join-Path -Path $projectDir -ChildPath "build"
+
+# $env:CMAKE_BUILD_TYPE is ignored by cmake when not using ninja
+$cmakeBuildType = $(if ($null -ne $env:CMAKE_BUILD_TYPE) {$env:CMAKE_BUILD_TYPE} else {'RelWithDebInfo'});
+$depsCmakeVars = @{
+ CMAKE_BUILD_TYPE=$cmakeBuildType;
+}
+$nvimCmakeVars = @{
+ CMAKE_BUILD_TYPE=$cmakeBuildType;
+ BUSTED_OUTPUT_TYPE = 'nvim';
+ DEPS_PREFIX=$(if ($null -ne $env:DEPS_PREFIX) {$env:DEPS_PREFIX} else {".deps/usr"});
+}
+if ($null -eq $env:DEPS_BUILD_DIR) {
+ $env:DEPS_BUILD_DIR = Join-Path -Path $projectDir -ChildPath ".deps"
+}
+$uploadToCodeCov = $false
+
+function exitIfFailed() {
+ if ($LastExitCode -ne 0) {
+ exit $LastExitCode
+ }
+}
+
+function convertToCmakeArgs($vars) {
+ return $vars.GetEnumerator() | ForEach-Object { "-D$($_.Key)=$($_.Value)" }
+}
+
+$installationPath = vswhere.exe -latest -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath
+if ($installationPath -and (Test-Path "$installationPath\Common7\Tools\vsdevcmd.bat")) {
+ & "${env:COMSPEC}" /s /c "`"$installationPath\Common7\Tools\vsdevcmd.bat`" -arch=x64 -no_logo && set" | ForEach-Object {
+ $name, $value = $_ -split '=', 2
+ Set-Content env:\"$name" $value
+ }
+}
+
+function BuildDeps {
+
+ if (Test-Path -PathType container $env:DEPS_BUILD_DIR) {
+ $cachedBuildTypeStr = $(Get-Content $env:DEPS_BUILD_DIR\CMakeCache.txt | Select-String -Pattern "CMAKE_BUILD_TYPE.*=($cmakeBuildType)")
+ if (-not $cachedBuildTypeStr) {
+ Write-Warning " unable to validate build type from cache dir ${env:DEPS_BUILD_DIR}"
+ }
+ }
+
+ # we currently can't use ninja for cmake.deps, see #19405
+ $depsCmakeGenerator = "Visual Studio 16 2019"
+ $depsCmakeGeneratorPlf = "x64"
+ cmake -S "$projectDir\cmake.deps" -B $env:DEPS_BUILD_DIR -G $depsCmakeGenerator -A $depsCmakeGeneratorPlf $(convertToCmakeArgs($depsCmakeVars)); exitIfFailed
+
+ $depsCmakeNativeToolOptions= @('/verbosity:normal', '/m')
+ cmake --build $env:DEPS_BUILD_DIR --config $cmakeBuildType -- $depsCmakeNativeToolOptions; exitIfFailed
+}
+
+function Build {
+ cmake -S $projectDir -B $buildDir $(convertToCmakeArgs($nvimCmakeVars)) -G Ninja; exitIfFailed
+ cmake --build $buildDir --config $cmakeBuildType; exitIfFailed
+}
+
+function EnsureTestDeps {
+ & $buildDir\bin\nvim.exe "--version"; exitIfFailed
+
+ # Ensure that the "win32" feature is set.
+ & $buildDir\bin\nvim -u NONE --headless -c 'exe !has(\"win32\").\"cq\"' ; exitIfFailed
+
+ python -m pip install pynvim
+ # Sanity check
+ python -c "import pynvim; print(str(pynvim))"; exitIfFailed
+
+ gem.cmd install --pre neovim
+ Get-Command -CommandType Application neovim-ruby-host.bat; exitIfFailed
+
+ node --version
+ npm.cmd --version
+
+ npm.cmd install -g neovim; exitIfFailed
+ Get-Command -CommandType Application neovim-node-host.cmd; exitIfFailed
+ npm.cmd link neovim
+
+ if ($env:USE_LUACOV -eq 1) {
+ & $env:DEPS_PREFIX\luarocks\luarocks.bat install cluacov
+ }
+}
+
+function Test {
+ # Functional tests
+ # The $LastExitCode from MSBuild can't be trusted
+ $failed = $false
+
+ cmake --build $buildDir --target functionaltest 2>&1 |
+ ForEach-Object { $failed = $failed -or
+ $_ -match 'functional tests failed with error'; $_ }
+
+ if ($failed) {
+ exit $LastExitCode
+ }
+
+ if (-not $uploadToCodecov) {
+ return
+ }
+ if ($env:USE_LUACOV -eq 1) {
+ & $env:DEPS_PREFIX\bin\luacov.bat
+ }
+ bash -l /c/projects/neovim/ci/common/submit_coverage.sh functionaltest
+}
+
+function TestOld {
+ # Old tests
+ # Add MSYS to path, required for e.g. `find` used in test scripts.
+ # But would break functionaltests, where its `more` would be used then.
+ $OldPath = $env:PATH
+ $env:PATH = "C:\msys64\usr\bin;$env:PATH"
+ & "C:\msys64\mingw64\bin\mingw32-make.exe" -C $(Convert-Path $projectDir\src\nvim\testdir) VERBOSE=1; exitIfFailed
+ $env:PATH = $OldPath
+
+ if ($uploadToCodecov) {
+ bash -l /c/projects/neovim/ci/common/submit_coverage.sh oldtest
+ }
+}
+
+
+function Package {
+ cmake -S $projectDir -B $buildDir $(convertToCmakeArgs($nvimCmakeVars)) -G Ninja; exitIfFailed
+ cmake --build $buildDir --target package; exitIfFailed
+}
+
+if ($PSCmdlet.ParameterSetName) {
+ & (Get-ChildItem "Function:$($PSCmdlet.ParameterSetName)")
+ exit
+}
diff --git a/ci/common/build.sh b/ci/common/build.sh
index 0ee4b7493f..f083796a28 100644
--- a/ci/common/build.sh
+++ b/ci/common/build.sh
@@ -8,8 +8,6 @@ _stat() {
top_make() {
printf '%78s\n' | tr ' ' '='
- # Travis has 1.5 virtual cores according to:
- # http://docs.travis-ci.com/user/speeding-up-the-build/#Paralellizing-your-build-on-one-VM
ninja "$@"
}
@@ -37,7 +35,7 @@ build_deps() {
# update CMake configuration and update to newer deps versions.
cd "${DEPS_BUILD_DIR}"
echo "Configuring with '${DEPS_CMAKE_FLAGS}'."
- CC= cmake -G Ninja ${DEPS_CMAKE_FLAGS} "${CI_BUILD_DIR}/third-party/"
+ CC= cmake -G Ninja ${DEPS_CMAKE_FLAGS} "${CI_BUILD_DIR}/cmake.deps/"
if ! top_make; then
exit 1
@@ -46,7 +44,9 @@ build_deps() {
cd "${CI_BUILD_DIR}"
}
-prepare_build() {
+build_nvim() {
+ check_core_dumps --delete quiet
+
if test -n "${CLANG_SANITIZER}" ; then
CMAKE_FLAGS="${CMAKE_FLAGS} -DCLANG_${CLANG_SANITIZER}=ON"
fi
@@ -55,9 +55,8 @@ prepare_build() {
cd "${BUILD_DIR}"
echo "Configuring with '${CMAKE_FLAGS} $@'."
cmake -G Ninja ${CMAKE_FLAGS} "$@" "${CI_BUILD_DIR}"
-}
-build_nvim() {
+
echo "Building nvim."
if ! top_make nvim ; then
exit 1
diff --git a/ci/common/submit_coverage.sh b/ci/common/submit_coverage.sh
index 9c7887de0b..f781ca8e5e 100755
--- a/ci/common/submit_coverage.sh
+++ b/ci/common/submit_coverage.sh
@@ -4,7 +4,7 @@
# Args:
# $1: Flag(s) for codecov, separated by comma.
-set -ex
+set -e
# Change to grandparent dir (POSIXly).
CDPATH='' cd -P -- "$(dirname -- "$0")/../.." || exit
@@ -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/common/suite.sh b/ci/common/suite.sh
index 561849ce2d..0320ac15c3 100644
--- a/ci/common/suite.sh
+++ b/ci/common/suite.sh
@@ -1,9 +1,3 @@
-# HACK: get newline for use in strings given that "\n" and $'' do not work.
-NL="$(printf '\nE')"
-NL="${NL%E}"
-
-FAIL_SUMMARY=""
-
# Test success marker. If END_MARKER file exists, we know that all tests
# finished. If FAIL_SUMMARY_FILE exists we know that some tests failed, this
# file will contain information about failed tests. Build is considered
@@ -11,81 +5,27 @@ FAIL_SUMMARY=""
END_MARKER="$BUILD_DIR/.tests_finished"
FAIL_SUMMARY_FILE="$BUILD_DIR/.test_errors"
-ci_fold() {
- if test "$GITHUB_ACTIONS" = "true"; then
- local action="$1"
- local name="$2"
- case "$action" in
- start)
- echo "::group::${name}"
- ;;
- end)
- echo "::endgroup::"
- ;;
- *)
- :;;
- esac
- fi
-}
-
-enter_suite() {
- set +x
- FAILED=0
- rm -f "${END_MARKER}"
- local suite_name="$1"
- export NVIM_TEST_CURRENT_SUITE="${NVIM_TEST_CURRENT_SUITE}/$suite_name"
- ci_fold "start" "$suite_name"
- set -x
-}
-
-exit_suite() {
- set +x
- if test $FAILED -ne 0 ; then
- echo "Suite ${NVIM_TEST_CURRENT_SUITE} failed, summary:"
- echo "${FAIL_SUMMARY}"
- else
- ci_fold "end" ""
- fi
- export NVIM_TEST_CURRENT_SUITE="${NVIM_TEST_CURRENT_SUITE%/*}"
- if test "$1" != "--continue" ; then
- exit $FAILED
- else
- local saved_failed=$FAILED
- FAILED=0
- return $saved_failed
- fi
-}
-
fail() {
local test_name="$1"
- local fail_char="$2"
- local message="$3"
+ local message="$2"
- : ${fail_char:=F}
: ${message:=Test $test_name failed}
- local full_msg="$fail_char $NVIM_TEST_CURRENT_SUITE|$test_name :: $message"
- FAIL_SUMMARY="${FAIL_SUMMARY}${NL}${full_msg}"
+ local full_msg="$test_name :: $message"
echo "${full_msg}" >> "${FAIL_SUMMARY_FILE}"
echo "Failed: $full_msg"
FAILED=1
}
-run_test() {
- local cmd="$1"
- test $# -gt 0 && shift
- local test_name="$1"
- : ${test_name:=$cmd}
- test $# -gt 0 && shift
- if ! eval "$cmd" ; then
- fail "${test_name}" "$@"
- fi
-}
-
ended_successfully() {
if test -f "${FAIL_SUMMARY_FILE}" ; then
echo 'Test failed, complete summary:'
cat "${FAIL_SUMMARY_FILE}"
+
+ if [[ "$GITHUB_ACTIONS" == "true" ]]; then
+ rm -f "$FAIL_SUMMARY_FILE"
+ fi
+
return 1
fi
if ! test -f "${END_MARKER}" ; then
diff --git a/ci/common/test.sh b/ci/common/test.sh
index 92c15c8ba1..7db39a0e5f 100644
--- a/ci/common/test.sh
+++ b/ci/common/test.sh
@@ -51,7 +51,7 @@ check_core_dumps() {
fi
done
if test "$app" != quiet ; then
- fail 'cores' E 'Core dumps found'
+ fail 'cores' 'Core dumps found'
fi
}
@@ -72,7 +72,7 @@ check_logs() {
rm "${log}"
done
if test -n "${err}" ; then
- fail 'logs' E 'Runtime errors detected.'
+ fail 'logs' 'Runtime errors detected.'
fi
}
@@ -86,46 +86,39 @@ check_sanitizer() {
fi
}
-run_unittests() {(
- enter_suite unittests
+unittests() {(
ulimit -c unlimited || true
if ! build_make unittest ; then
- fail 'unittests' F 'Unit tests failed'
+ fail 'unittests' 'Unit tests failed'
fi
submit_coverage unittest
check_core_dumps "$(command -v luajit)"
- exit_suite
)}
-run_functionaltests() {(
- enter_suite functionaltests
+functionaltests() {(
ulimit -c unlimited || true
if ! build_make ${FUNCTIONALTEST}; then
- fail 'functionaltests' F 'Functional tests failed'
+ fail 'functionaltests' 'Functional tests failed'
fi
submit_coverage functionaltest
check_sanitizer "${LOG_DIR}"
valgrind_check "${LOG_DIR}"
check_core_dumps
- exit_suite
)}
-run_oldtests() {(
- enter_suite oldtests
+oldtests() {(
ulimit -c unlimited || true
if ! make oldtest; then
reset
- fail 'oldtests' F 'Legacy tests failed'
+ fail 'oldtests' 'Legacy tests failed'
fi
submit_coverage oldtest
check_sanitizer "${LOG_DIR}"
valgrind_check "${LOG_DIR}"
check_core_dumps
- exit_suite
)}
check_runtime_files() {(
- set +x
local test_name="$1" ; shift
local message="$1" ; shift
local tst="$1" ; shift
@@ -136,27 +129,25 @@ check_runtime_files() {(
# Prefer failing the build over using more robust construct because files
# with IFS are not welcome.
if ! test -e "$file" ; then
- fail "$test_name" E \
- "It appears that $file is only a part of the file name"
+ fail "$test_name" "It appears that $file is only a part of the file name"
fi
if ! test "$tst" "$INSTALL_PREFIX/share/nvim/runtime/$file" ; then
- fail "$test_name" F "$(printf "$message" "$file")"
+ fail "$test_name" "$(printf "$message" "$file")"
fi
done
)}
install_nvim() {(
- enter_suite 'install_nvim'
if ! build_make install ; then
- fail 'install' E 'make install failed'
- exit_suite
+ fail 'install' 'make install failed'
+ exit 1
fi
"${INSTALL_PREFIX}/bin/nvim" --version
if ! "${INSTALL_PREFIX}/bin/nvim" -u NONE -e -c ':help' -c ':qall' ; then
echo "Running ':help' in the installed nvim failed."
echo "Maybe the helptags have not been generated properly."
- fail 'help' F 'Failed running :help'
+ fail 'help' 'Failed running :help'
fi
# Check that all runtime files were installed
@@ -177,13 +168,6 @@ install_nvim() {(
local genvimsynf=syntax/vim/generated.vim
local gpat='syn keyword vimFuncName .*eval'
if ! grep -q "$gpat" "${INSTALL_PREFIX}/share/nvim/runtime/$genvimsynf" ; then
- fail 'funcnames' F "It appears that $genvimsynf does not contain $gpat."
+ fail 'funcnames' "It appears that $genvimsynf does not contain $gpat."
fi
-
- exit_suite
)}
-
-csi_clean() {
- find "${BUILD_DIR}/bin" -name 'test-includes-*' -delete
- find "${BUILD_DIR}" -name '*test-include*.o' -delete
-}
diff --git a/ci/install.sh b/ci/install.sh
index bd42274b49..5925cc7b02 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."
+echo "Install neovim module for Python."
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 RubyGem."
gem install --no-document --bindir "$HOME/.local/bin" --user-install --pre neovim
@@ -18,5 +14,7 @@ echo "Install neovim npm package"
npm install -g neovim
npm link neovim
-sudo cpanm -n Neovim::Ext || cat "$HOME/.cpanm/build.log"
-perl -W -e 'use Neovim::Ext; print $Neovim::Ext::VERSION'
+if [[ $CI_OS_NAME != osx ]]; then
+ sudo cpanm -n Neovim::Ext || cat "$HOME/.cpanm/build.log"
+ perl -W -e 'use Neovim::Ext; print $Neovim::Ext::VERSION'
+fi
diff --git a/ci/run_lint.sh b/ci/run_lint.sh
deleted file mode 100755
index 314976edc2..0000000000
--- a/ci/run_lint.sh
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -o pipefail
-
-CI_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
-
-source "${CI_DIR}/common/build.sh"
-source "${CI_DIR}/common/suite.sh"
-
-enter_suite 'clint'
-run_test 'make clint-full' clint
-exit_suite --continue
-
-enter_suite 'lualint'
-run_test 'make lualint' lualint
-exit_suite --continue
-
-enter_suite 'pylint'
-run_test 'make pylint' pylint
-exit_suite --continue
-
-enter_suite 'shlint'
-run_test 'make shlint' shlint
-exit_suite --continue
-
-enter_suite single-includes
-run_test 'make check-single-includes' single-includes
-exit_suite --continue
-
-end_tests
diff --git a/ci/run_tests.sh b/ci/run_tests.sh
index c175910da5..da72d09506 100755
--- a/ci/run_tests.sh
+++ b/ci/run_tests.sh
@@ -8,29 +8,34 @@ source "${CI_DIR}/common/build.sh"
source "${CI_DIR}/common/test.sh"
source "${CI_DIR}/common/suite.sh"
-enter_suite build
-
-check_core_dumps --delete quiet
-
-prepare_build
-build_nvim
-
-exit_suite --continue
-
-enter_suite tests
-
-if test "$CLANG_SANITIZER" != "TSAN" ; then
- # Additional threads are only created when the builtin UI starts, which
- # doesn't happen in the unit/functional tests
- if test "${FUNCTIONALTEST}" != "functionaltest-lua"; then
- run_test run_unittests
+rm -f "$END_MARKER"
+
+# Run all tests (with some caveats) if no input argument is given
+if (($# == 0)); then
+ tests=('build_nvim')
+
+ if test "$CLANG_SANITIZER" != "TSAN"; then
+ # Additional threads are only created when the builtin UI starts, which
+ # doesn't happen in the unit/functional tests
+ if test "${FUNCTIONALTEST}" != "functionaltest-lua"; then
+ tests+=('unittests')
+ fi
+ tests+=('functionaltests')
fi
- run_test run_functionaltests
-fi
-run_test run_oldtests
-run_test install_nvim
+ tests+=('oldtests' 'install_nvim')
+else
+ tests=("$@")
+fi
-exit_suite --continue
+for i in "${tests[@]}"; do
+ eval "$i" || fail "$i"
+done
end_tests
+
+if [[ -s "${GCOV_ERROR_FILE}" ]]; then
+ echo '=== Unexpected gcov errors: ==='
+ cat "${GCOV_ERROR_FILE}"
+ exit 1
+fi
diff --git a/ci/script.sh b/ci/script.sh
deleted file mode 100755
index 74fc4eda6c..0000000000
--- a/ci/script.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -o pipefail
-
-ci/run_${CI_TARGET}.sh
-
-if [[ -s "${GCOV_ERROR_FILE}" ]]; then
- echo '=== Unexpected gcov errors: ==='
- cat "${GCOV_ERROR_FILE}"
- exit 1
-fi
diff --git a/ci/snap/deploy.sh b/ci/snap/deploy.sh
index 8429059e22..1794fc61d9 100755
--- a/ci/snap/deploy.sh
+++ b/ci/snap/deploy.sh
@@ -8,7 +8,7 @@ WEBHOOK_PAYLOAD="$(cat "${SNAP_DIR}/.snapcraft_payload")"
PAYLOAD_SIG="${SECRET_SNAP_SIG}"
-snap_realease_needed() {
+snap_release_needed() {
last_committed_tag="$(git tag -l --sort=refname|head -1)"
last_snap_release="$(snap info nvim | awk '$1 == "latest/edge:" { print $2 }' | perl -lpe 's/v\d.\d.\d-//g')"
git fetch -f --tags
@@ -33,7 +33,7 @@ trigger_snapcraft_webhook() {
}
-if $(snap_realease_needed); then
+if $(snap_release_needed); then
echo "New snap release required"
trigger_snapcraft_webhook
fi
diff --git a/config/CMakeLists.txt b/cmake.config/CMakeLists.txt
index 8f93e1eb27..8c86b89e43 100644
--- a/config/CMakeLists.txt
+++ b/cmake.config/CMakeLists.txt
@@ -5,12 +5,13 @@ include(CheckIncludeFiles)
include(CheckCSourceRuns)
include(CheckCSourceCompiles)
-check_type_size("int" SIZEOF_INT)
-check_type_size("long" SIZEOF_LONG)
-check_type_size("intmax_t" SIZEOF_INTMAX_T)
-check_type_size("size_t" SIZEOF_SIZE_T)
-check_type_size("long long" SIZEOF_LONG_LONG)
-check_type_size("void *" SIZEOF_VOID_PTR)
+check_type_size("int" SIZEOF_INT LANGUAGE C)
+check_type_size("long" SIZEOF_LONG LANGUAGE C)
+check_type_size("intmax_t" SIZEOF_INTMAX_T LANGUAGE C)
+check_type_size("int32_t" SIZEOF_INT32_T LANGUAGE C)
+check_type_size("size_t" SIZEOF_SIZE_T LANGUAGE C)
+check_type_size("long long" SIZEOF_LONG_LONG LANGUAGE C)
+check_type_size("void *" SIZEOF_VOID_PTR LANGUAGE C)
check_symbol_exists(_NSGetEnviron crt_externs.h HAVE__NSGETENVIRON)
@@ -127,14 +128,14 @@ endif()
# generate configuration header and update include directories
configure_file (
- "${PROJECT_SOURCE_DIR}/config/config.h.in"
- "${PROJECT_BINARY_DIR}/config/auto/config.h"
+ "${PROJECT_SOURCE_DIR}/cmake.config/config.h.in"
+ "${PROJECT_BINARY_DIR}/cmake.config/auto/config.h"
)
# generate version definitions
configure_file (
- "${PROJECT_SOURCE_DIR}/config/versiondef.h.in"
- "${PROJECT_BINARY_DIR}/config/auto/versiondef.h"
+ "${PROJECT_SOURCE_DIR}/cmake.config/versiondef.h.in"
+ "${PROJECT_BINARY_DIR}/cmake.config/auto/versiondef.h"
)
# generate pathdef.c
@@ -157,6 +158,6 @@ elseif (EXISTS ${HOSTNAME_PROG})
endif()
configure_file (
- "${PROJECT_SOURCE_DIR}/config/pathdef.c.in"
- "${PROJECT_BINARY_DIR}/config/auto/pathdef.c"
+ "${PROJECT_SOURCE_DIR}/cmake.config/pathdef.c.in"
+ "${PROJECT_BINARY_DIR}/cmake.config/auto/pathdef.c"
ESCAPE_QUOTES)
diff --git a/config/config.h.in b/cmake.config/config.h.in
index b44b7238d2..59be83fb5e 100644
--- a/config/config.h.in
+++ b/cmake.config/config.h.in
@@ -3,8 +3,12 @@
#cmakedefine DEBUG
-#define SIZEOF_INT @SIZEOF_INT@
-#define SIZEOF_LONG @SIZEOF_LONG@
+#cmakedefine SIZEOF_INT @SIZEOF_INT@
+#cmakedefine SIZEOF_INTMAX_T @SIZEOF_INTMAX_T@
+#cmakedefine SIZEOF_INT32_T @SIZEOF_INT32_T@
+#cmakedefine SIZEOF_LONG @SIZEOF_LONG@
+#cmakedefine SIZEOF_LONG_LONG @SIZEOF_LONG_LONG@
+#cmakedefine SIZEOF_SIZE_T @SIZEOF_SIZE_T@
#if @SIZEOF_VOID_PTR@ == 8
#define ARCH_64
@@ -12,7 +16,7 @@
#define ARCH_32
#endif
-#define PROJECT_NAME "@PROJECT_NAME@"
+#cmakedefine PROJECT_NAME "@PROJECT_NAME@"
#cmakedefine HAVE__NSGETENVIRON
#cmakedefine HAVE_FD_CLOEXEC
diff --git a/config/pathdef.c.in b/cmake.config/pathdef.c.in
index 6a8a2b205a..6a8a2b205a 100644
--- a/config/pathdef.c.in
+++ b/cmake.config/pathdef.c.in
diff --git a/config/versiondef.h.in b/cmake.config/versiondef.h.in
index 22cad87249..22cad87249 100644
--- a/config/versiondef.h.in
+++ b/cmake.config/versiondef.h.in
diff --git a/third-party/CMakeLists.txt b/cmake.deps/CMakeLists.txt
index 613b39c833..5427382783 100644
--- a/third-party/CMakeLists.txt
+++ b/cmake.deps/CMakeLists.txt
@@ -39,7 +39,6 @@ set(DEPS_DOWNLOAD_DIR "${DEPS_BUILD_DIR}/downloads" CACHE PATH "Dependencies dow
option(USE_BUNDLED "Use bundled dependencies." ON)
-option(USE_BUNDLED_GPERF "Use the bundled version of gperf." ${USE_BUNDLED})
option(USE_BUNDLED_UNIBILIUM "Use the bundled unibilium." ${USE_BUNDLED})
option(USE_BUNDLED_LIBTERMKEY "Use the bundled libtermkey." ${USE_BUNDLED})
option(USE_BUNDLED_LIBVTERM "Use the bundled libvterm." ${USE_BUNDLED})
@@ -109,10 +108,6 @@ else()
set(DEPS_C_COMPILER "${CMAKE_C_COMPILER}")
endif()
-if(USE_BUNDLED_GPERF)
- enable_language(CXX)
-endif()
-
if(CMAKE_CXX_COMPILER)
set(DEPS_CXX_COMPILER "${CMAKE_CXX_COMPILER}")
endif()
@@ -124,35 +119,36 @@ if(CMAKE_OSX_SYSROOT)
endif()
endif()
-# Cross compiling: use these for dependencies built for the
-# HOST system, when not crosscompiling these should be the
-# same as DEPS_*. Except when targeting Unix in which case
-# want all the dependencies to use the same compiler.
-if(CMAKE_CROSSCOMPILING AND NOT UNIX)
- set(HOSTDEPS_INSTALL_DIR "${CMAKE_BINARY_DIR}/host")
- set(HOSTDEPS_BIN_DIR "${HOSTDEPS_INSTALL_DIR}/bin")
- set(HOSTDEPS_LIB_DIR "${HOSTDEPS_INSTALL_DIR}/lib")
- set(HOSTDEPS_C_COMPILER "${HOST_C_COMPILER}")
- set(HOSTDEPS_CXX_COMPILER "${HOST_CXX_COMPILER}")
-else()
- set(HOSTDEPS_INSTALL_DIR "${DEPS_INSTALL_DIR}")
- set(HOSTDEPS_BIN_DIR "${DEPS_BIN_DIR}")
- set(HOSTDEPS_LIB_DIR "${DEPS_LIB_DIR}")
- set(HOSTDEPS_C_COMPILER "${DEPS_C_COMPILER}")
- set(HOSTDEPS_CXX_COMPILER "${DEPS_CXX_COMPILER}")
+if(CMAKE_OSX_ARCHITECTURES)
+ string(REPLACE ";" "|" CMAKE_OSX_ARCHITECTURES_ALT_SEP "${CMAKE_OSX_ARCHITECTURES}")
+ # The LuaJIT build does not like being passed multiple `-arch` flags
+ # so we handle a universal build the old-fashioned way.
+ set(LUAJIT_C_COMPILER "${DEPS_C_COMPILER}")
+ foreach(ARCH IN LISTS CMAKE_OSX_ARCHITECTURES)
+ set(DEPS_C_COMPILER "${DEPS_C_COMPILER} -arch ${ARCH}")
+ if(DEPS_CXX_COMPILER)
+ set(DEPS_CXX_COMPILER "${DEPS_CXX_COMPILER} -arch ${ARCH}")
+ endif()
+ endforeach()
endif()
+set(HOSTDEPS_INSTALL_DIR "${DEPS_INSTALL_DIR}")
+set(HOSTDEPS_BIN_DIR "${DEPS_BIN_DIR}")
+set(HOSTDEPS_LIB_DIR "${DEPS_LIB_DIR}")
+set(HOSTDEPS_C_COMPILER "${DEPS_C_COMPILER}")
+set(HOSTDEPS_CXX_COMPILER "${DEPS_CXX_COMPILER}")
+
include(ExternalProject)
-set(LIBUV_URL https://github.com/libuv/libuv/archive/v1.42.0.tar.gz)
-set(LIBUV_SHA256 371e5419708f6aaeb8656671f89400b92a9bba6443369af1bb70bcd6e4b3c764)
+set(LIBUV_URL https://github.com/libuv/libuv/archive/v1.44.2.tar.gz)
+set(LIBUV_SHA256 e6e2ba8b4c349a4182a33370bb9be5e23c51b32efb9b9e209d0e8556b73a48da)
-set(MSGPACK_URL https://github.com/msgpack/msgpack-c/releases/download/cpp-3.0.0/msgpack-3.0.0.tar.gz)
-set(MSGPACK_SHA256 bfbb71b7c02f806393bc3cbc491b40523b89e64f83860c58e3e54af47de176e4)
+set(MSGPACK_URL https://github.com/msgpack/msgpack-c/releases/download/c-4.0.0/msgpack-c-4.0.0.tar.gz)
+set(MSGPACK_SHA256 420fe35e7572f2a168d17e660ef981a589c9cbe77faa25eb34a520e1fcc032c8)
# https://github.com/LuaJIT/LuaJIT/tree/v2.1
-set(LUAJIT_URL https://github.com/LuaJIT/LuaJIT/archive/1d20f33d2905db55fb7191076bbac10f570f9175.tar.gz)
-set(LUAJIT_SHA256 4b6e2fc726ed8bf51fe461d90db9fffbc5c894bbdc862a67d17a70190f1fb07f)
+set(LUAJIT_URL https://github.com/LuaJIT/LuaJIT/archive/50936d784474747b4569d988767f1b5bab8bb6d0.tar.gz)
+set(LUAJIT_SHA256 4d44e4709130b031c1c2c81cf5c102dfce877bf454409dabba03249e18870e66)
set(LUA_URL https://www.lua.org/ftp/lua-5.1.5.tar.gz)
set(LUA_SHA256 2640fc56a795f29d28ef15e13c34a47e223960b0240e8cb0a82d9b0738695333)
@@ -163,22 +159,19 @@ set(LUAROCKS_SHA256 ab6612ca9ab87c6984871d2712d05525775e8b50172701a0a1cabddf76de
set(UNIBILIUM_URL https://github.com/neovim/unibilium/archive/92d929f.tar.gz)
set(UNIBILIUM_SHA256 29815283c654277ef77a3adcc8840db79ddbb20a0f0b0c8f648bd8cd49a02e4b)
-set(LIBTERMKEY_URL http://www.leonerd.org.uk/code/libtermkey/libtermkey-0.22.tar.gz)
+set(LIBTERMKEY_URL https://www.leonerd.org.uk/code/libtermkey/libtermkey-0.22.tar.gz)
set(LIBTERMKEY_SHA256 6945bd3c4aaa83da83d80a045c5563da4edd7d0374c62c0d35aec09eb3014600)
-set(LIBVTERM_URL http://www.leonerd.org.uk/code/libvterm/libvterm-0.1.4.tar.gz)
+set(LIBVTERM_URL https://www.leonerd.org.uk/code/libvterm/libvterm-0.1.4.tar.gz)
set(LIBVTERM_SHA256 bc70349e95559c667672fc8c55b9527d9db9ada0fb80a3beda533418d782d3dd)
-set(LUV_VERSION 1.42.0-1)
-set(LUV_URL https://github.com/luvit/luv/archive/${LUV_VERSION}.tar.gz)
-set(LUV_SHA256 a55563e6da9294ea26f77a2111598aa49188c5806b8bd1e1f4bdf402f340713e)
+set(LUV_VERSION 1.44.2-0)
+set(LUV_URL https://github.com/luvit/luv/archive/1.44.2-0.tar.gz)
+set(LUV_SHA256 44ccda27035bfe683e6325a2a93f2c254be1eb76bde6efc2bd37c56a7af7b00a)
set(LUA_COMPAT53_URL https://github.com/keplerproject/lua-compat-5.3/archive/v0.9.tar.gz)
set(LUA_COMPAT53_SHA256 ad05540d2d96a48725bb79a1def35cf6652a4e2ec26376e2617c8ce2baa6f416)
-set(GPERF_URL https://github.com/neovim/deps/raw/ff5b4b18a87397a8564016071ae64f64bcd8c635/opt/gperf-3.1.tar.gz)
-set(GPERF_SHA256 588546b945bba4b70b6a3a616e80b4ab466e3f33024a352fc2198112cdbb3ae2)
-
# cat.exe curl.exe curl-ca-bundle.crt diff.exe tee.exe xxd.exe
set(WINTOOLS_URL https://github.com/neovim/deps/raw/d66e306abf5b846484b4f2adffd896bce7e065d2/opt/win32tools.zip)
set(WINTOOLS_SHA256 2fb2f8d69070b3f16e029913fb95008e6be33893d77fc358012396c275a0fdb7)
@@ -191,9 +184,6 @@ set(WIN32YANK_X86_SHA256 62f34e5a46c5d4a7b3f3b512e1ff7b77fedd432f42581cbe825233a
set(WIN32YANK_X86_64_URL https://github.com/equalsraf/win32yank/releases/download/v0.0.4/win32yank-x64.zip)
set(WIN32YANK_X86_64_SHA256 33a747a92da60fb65e668edbf7661d3d902411a2d545fe9dc08623cecd142a20)
-set(WINPTY_URL https://github.com/rprichard/winpty/releases/download/0.4.3/winpty-0.4.3-msvc2015.zip)
-set(WINPTY_SHA256 35a48ece2ff4acdcbc8299d4920de53eb86b1fb41e64d2fe5ae7898931bcee89)
-
set(GETTEXT_URL https://ftp.gnu.org/pub/gnu/gettext/gettext-0.20.1.tar.gz)
set(GETTEXT_SHA256 66415634c6e8c3fa8b71362879ec7575e27da43da562c798a8a2f223e6e47f5c)
@@ -203,8 +193,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/1f1b1eb4501ed0a2d195d37f7de15f72aa10acd0.tar.gz)
+set(TREESITTER_SHA256 a324bdb7ff507cd5157a36b908224d60ba638f8e3363070dd51aa383c7cbd790)
if(USE_BUNDLED_UNIBILIUM)
include(BuildUnibilium)
@@ -233,7 +223,7 @@ if(USE_BUNDLED_LUAJIT)
include(BuildLuajit)
endif()
-if(USE_BUNDLED_LUA AND NOT CMAKE_CROSSCOMPILING)
+if(USE_BUNDLED_LUA)
include(BuildLua)
endif()
@@ -245,10 +235,6 @@ if(USE_BUNDLED_LUV)
include(BuildLuv)
endif()
-if(USE_BUNDLED_GPERF)
- include(BuildGperf)
-endif()
-
if(USE_BUNDLED_GETTEXT)
include(BuildGettext)
endif()
@@ -284,18 +270,6 @@ if(WIN32)
elseif(TARGET_ARCH STREQUAL "X86")
set(TARGET_ARCH ia32)
endif()
-
- GetBinaryDep(TARGET winpty
- INSTALL_COMMAND ${CMAKE_COMMAND} -E make_directory ${DEPS_INSTALL_DIR}/bin
- COMMAND ${CMAKE_COMMAND} -DFROM_GLOB=${DEPS_BUILD_DIR}/src/winpty/${TARGET_ARCH}/bin/*
- -DTO=${DEPS_INSTALL_DIR}/bin/
- -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/CopyFilesGlob.cmake
- COMMAND ${CMAKE_COMMAND} -DFROM_GLOB=${DEPS_BUILD_DIR}/src/winpty/include/*
- -DTO=${DEPS_INSTALL_DIR}/include/
- -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/CopyFilesGlob.cmake
- COMMAND ${CMAKE_COMMAND} -DFROM_GLOB=${DEPS_BUILD_DIR}/src/winpty/${TARGET_ARCH}/lib/*
- -DTO=${DEPS_INSTALL_DIR}/lib/
- -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/CopyFilesGlob.cmake)
endif()
# clean-shared-libraries removes ${DEPS_INSTALL_DIR}/lib/nvim/parser/c.dll,
@@ -312,6 +286,7 @@ else()
set(ALL_DEPS clean-shared-libraries)
endif()
+# TODO(justinmk): does anyone use this target?
add_custom_target(third-party ALL
COMMAND ${CMAKE_COMMAND} -E touch .third-party
DEPENDS ${ALL_DEPS}
diff --git a/third-party/cmake/BuildGettext.cmake b/cmake.deps/cmake/BuildGettext.cmake
index 9357456343..6128ecfa69 100644
--- a/third-party/cmake/BuildGettext.cmake
+++ b/cmake.deps/cmake/BuildGettext.cmake
@@ -21,6 +21,7 @@ if(MSVC)
-DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN}
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DCMAKE_GENERATOR=${CMAKE_GENERATOR}
+ -DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM}
-DLIBICONV_INCLUDE_DIRS=${DEPS_INSTALL_DIR}/include
-DLIBICONV_LIBRARIES=${DEPS_LIB_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}libcharset${CMAKE_STATIC_LIBRARY_SUFFIX}$<SEMICOLON>${DEPS_LIB_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}libiconv${CMAKE_STATIC_LIBRARY_SUFFIX}
BUILD_COMMAND ${CMAKE_COMMAND} --build . --config ${CMAKE_BUILD_TYPE}
diff --git a/third-party/cmake/BuildLibiconv.cmake b/cmake.deps/cmake/BuildLibiconv.cmake
index dc3d8fe4c3..5ff33e0cd3 100644
--- a/third-party/cmake/BuildLibiconv.cmake
+++ b/cmake.deps/cmake/BuildLibiconv.cmake
@@ -21,6 +21,7 @@ if(MSVC)
-DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN}
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DCMAKE_GENERATOR=${CMAKE_GENERATOR}
+ -DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM}
BUILD_COMMAND ${CMAKE_COMMAND} --build . --config ${CMAKE_BUILD_TYPE}
INSTALL_COMMAND ${CMAKE_COMMAND} --build . --target install --config ${CMAKE_BUILD_TYPE})
diff --git a/third-party/cmake/BuildLibtermkey.cmake b/cmake.deps/cmake/BuildLibtermkey.cmake
index 10e98fbab3..d44e09d734 100644
--- a/third-party/cmake/BuildLibtermkey.cmake
+++ b/cmake.deps/cmake/BuildLibtermkey.cmake
@@ -22,6 +22,7 @@ ExternalProject_Add(libtermkey
# Hack to avoid -rdynamic in Mingw
-DCMAKE_SHARED_LIBRARY_LINK_C_FLAGS=""
-DCMAKE_GENERATOR=${CMAKE_GENERATOR}
+ -DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM}
-DUNIBILIUM_INCLUDE_DIRS=${DEPS_INSTALL_DIR}/include
-DUNIBILIUM_LIBRARIES=${DEPS_LIB_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}unibilium${CMAKE_STATIC_LIBRARY_SUFFIX}
BUILD_COMMAND ${CMAKE_COMMAND} --build . --config ${CMAKE_BUILD_TYPE}
diff --git a/third-party/cmake/BuildLibuv.cmake b/cmake.deps/cmake/BuildLibuv.cmake
index dce64c777b..ba5de38714 100644
--- a/third-party/cmake/BuildLibuv.cmake
+++ b/cmake.deps/cmake/BuildLibuv.cmake
@@ -45,17 +45,6 @@ if(UNIX)
CONFIGURE_COMMAND ${UNIX_CFGCMD} MAKE=${MAKE_PRG}
INSTALL_COMMAND ${MAKE_PRG} V=1 install)
-elseif(MINGW AND CMAKE_CROSSCOMPILING)
- # Build libuv for the host
- BuildLibuv(TARGET libuv_host
- CONFIGURE_COMMAND sh ${DEPS_BUILD_DIR}/src/libuv_host/autogen.sh && ${DEPS_BUILD_DIR}/src/libuv_host/configure --with-pic --disable-shared --prefix=${HOSTDEPS_INSTALL_DIR} CC=${HOST_C_COMPILER}
- INSTALL_COMMAND ${MAKE_PRG} V=1 install)
-
- # Build libuv for the target
- BuildLibuv(
- CONFIGURE_COMMAND ${UNIX_CFGCMD} --host=${CROSS_TARGET}
- INSTALL_COMMAND ${MAKE_PRG} V=1 install)
-
elseif(WIN32)
set(UV_OUTPUT_DIR ${DEPS_BUILD_DIR}/src/libuv/${CMAKE_BUILD_TYPE})
@@ -63,10 +52,6 @@ elseif(WIN32)
set(BUILD_SHARED ON)
elseif(MINGW)
set(BUILD_SHARED OFF)
- set(LIBUV_PATCH_COMMAND
- ${GIT_EXECUTABLE} -C ${DEPS_BUILD_DIR}/src/libuv init
- COMMAND ${GIT_EXECUTABLE} -C ${DEPS_BUILD_DIR}/src/libuv apply --ignore-whitespace
- ${CMAKE_CURRENT_SOURCE_DIR}/patches/libuv-disable-typedef-MinGW.patch)
else()
message(FATAL_ERROR "Trying to build libuv in an unsupported system ${CMAKE_SYSTEM_NAME}/${CMAKE_C_COMPILER_ID}")
endif()
@@ -77,6 +62,7 @@ elseif(WIN32)
COMMAND ${CMAKE_COMMAND} ${DEPS_BUILD_DIR}/src/libuv/CMakeLists.txt
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
-DCMAKE_GENERATOR=${CMAKE_GENERATOR}
+ -DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM}
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DBUILD_SHARED_LIBS=${BUILD_SHARED}
-DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
diff --git a/third-party/cmake/BuildLibvterm.cmake b/cmake.deps/cmake/BuildLibvterm.cmake
index 09f2ba7f2c..2c300dda7c 100644
--- a/third-party/cmake/BuildLibvterm.cmake
+++ b/cmake.deps/cmake/BuildLibvterm.cmake
@@ -47,6 +47,7 @@ if(WIN32)
COMMAND ${CMAKE_COMMAND} ${DEPS_BUILD_DIR}/src/libvterm
-DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
+ -DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM}
"-DCMAKE_C_FLAGS:STRING=${CMAKE_C_COMPILER_ARG1} -fPIC"
-DCMAKE_GENERATOR=${CMAKE_GENERATOR})
set(LIBVTERM_BUILD_COMMAND ${CMAKE_COMMAND} --build . --config ${CMAKE_BUILD_TYPE})
diff --git a/third-party/cmake/BuildLua.cmake b/cmake.deps/cmake/BuildLua.cmake
index a40cb7dcb2..a40cb7dcb2 100644
--- a/third-party/cmake/BuildLua.cmake
+++ b/cmake.deps/cmake/BuildLua.cmake
diff --git a/third-party/cmake/BuildLuajit.cmake b/cmake.deps/cmake/BuildLuajit.cmake
index e02d7fe609..c8d5b39398 100644
--- a/third-party/cmake/BuildLuajit.cmake
+++ b/cmake.deps/cmake/BuildLuajit.cmake
@@ -5,7 +5,7 @@ function(BuildLuajit)
cmake_parse_arguments(_luajit
""
"TARGET"
- "CONFIGURE_COMMAND;BUILD_COMMAND;INSTALL_COMMAND"
+ "CONFIGURE_COMMAND;BUILD_COMMAND;INSTALL_COMMAND;DEPENDS"
${ARGN})
if(NOT _luajit_CONFIGURE_COMMAND AND NOT _luajit_BUILD_COMMAND
AND NOT _luajit_INSTALL_COMMAND)
@@ -30,13 +30,14 @@ function(BuildLuajit)
CONFIGURE_COMMAND "${_luajit_CONFIGURE_COMMAND}"
BUILD_IN_SOURCE 1
BUILD_COMMAND "${_luajit_BUILD_COMMAND}"
- INSTALL_COMMAND "${_luajit_INSTALL_COMMAND}")
+ INSTALL_COMMAND "${_luajit_INSTALL_COMMAND}"
+ DEPENDS "${_luajit_DEPENDS}")
# Create symlink for development version manually.
if(UNIX)
add_custom_command(
TARGET ${_luajit_TARGET}
- COMMAND ${CMAKE_COMMAND} -E create_symlink luajit-2.1.0-beta3 ${DEPS_BIN_DIR}/luajit)
+ COMMAND ${CMAKE_COMMAND} -E create_symlink luajit-2.1.0-beta3 ${DEPS_BIN_DIR}/${_luajit_TARGET})
endif()
endfunction()
@@ -51,53 +52,64 @@ if(CMAKE_SYSTEM_NAME MATCHES "OpenBSD")
else()
set(AMD64_ABI "")
endif()
-set(INSTALLCMD_UNIX ${MAKE_PRG} CFLAGS=-fPIC
- CFLAGS+=-DLUA_USE_APICHECK
- CFLAGS+=-funwind-tables
- ${NO_STACK_CHECK}
- ${AMD64_ABI}
- CCDEBUG+=-g
- Q=
- install)
+set(BUILDCMD_UNIX ${MAKE_PRG} CFLAGS=-fPIC
+ CFLAGS+=-DLUA_USE_APICHECK
+ CFLAGS+=-funwind-tables
+ ${NO_STACK_CHECK}
+ ${AMD64_ABI}
+ CCDEBUG+=-g
+ Q=)
-if(UNIX)
- if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
- if(CMAKE_OSX_DEPLOYMENT_TARGET)
- set(DEPLOYMENT_TARGET "MACOSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}")
- else()
- # Use the same target as our nightly builds
- set(DEPLOYMENT_TARGET "MACOSX_DEPLOYMENT_TARGET=10.11")
- endif()
+if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
+ if(CMAKE_OSX_DEPLOYMENT_TARGET)
+ set(DEPLOYMENT_TARGET "MACOSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}")
else()
- set(DEPLOYMENT_TARGET "")
+ execute_process(COMMAND sw_vers -productVersion
+ OUTPUT_VARIABLE MACOS_VERSION
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+ set(DEPLOYMENT_TARGET "MACOSX_DEPLOYMENT_TARGET=${MACOS_VERSION}")
endif()
+else()
+ set(DEPLOYMENT_TARGET "")
+endif()
- BuildLuaJit(INSTALL_COMMAND ${INSTALLCMD_UNIX}
+if((UNIX AND NOT APPLE) OR (APPLE AND NOT CMAKE_OSX_ARCHITECTURES))
+ BuildLuaJit(INSTALL_COMMAND ${BUILDCMD_UNIX}
CC=${DEPS_C_COMPILER} PREFIX=${DEPS_INSTALL_DIR}
- ${DEPLOYMENT_TARGET})
+ ${DEPLOYMENT_TARGET} install)
-elseif(MINGW AND CMAKE_CROSSCOMPILING)
+elseif(CMAKE_OSX_ARCHITECTURES AND APPLE)
- # Build luajit for the host
- BuildLuaJit(TARGET luajit_host
- CONFIGURE_COMMAND ""
- BUILD_COMMAND ""
- INSTALL_COMMAND ${INSTALLCMD_UNIX}
- CC=${HOST_C_COMPILER} PREFIX=${HOSTDEPS_INSTALL_DIR})
+ # Passing multiple `-arch` flags to the LuaJIT build will cause it to fail.
+ # To get a working universal build, we build each requested architecture slice
+ # individually then `lipo` them all up.
+ set(LUAJIT_SRC_DIR "${DEPS_BUILD_DIR}/src/luajit")
+ foreach(ARCH IN LISTS CMAKE_OSX_ARCHITECTURES)
+ set(STATIC_CC "${LUAJIT_C_COMPILER} -arch ${ARCH}")
+ set(DYNAMIC_CC "${LUAJIT_C_COMPILER} -arch ${ARCH} -fPIC")
+ set(TARGET_LD "${LUAJIT_C_COMPILER} -arch ${ARCH}")
+ list(APPEND LUAJIT_THIN_EXECUTABLES "${LUAJIT_SRC_DIR}-${ARCH}/src/luajit")
+ list(APPEND LUAJIT_THIN_STATIC_LIBS "${LUAJIT_SRC_DIR}-${ARCH}/src/libluajit.a")
+ list(APPEND LUAJIT_THIN_DYLIBS "${LUAJIT_SRC_DIR}-${ARCH}/src/libluajit.so")
+ list(APPEND LUAJIT_THIN_TARGETS "luajit-${ARCH}")
- # Build luajit for the target
+ # See https://luajit.org/install.html#cross.
+ BuildLuaJit(TARGET "luajit-${ARCH}"
+ BUILD_COMMAND ${BUILDCMD_UNIX}
+ CC=${LUAJIT_C_COMPILER} STATIC_CC=${STATIC_CC}
+ DYNAMIC_CC=${DYNAMIC_CC} TARGET_LD=${TARGET_LD}
+ PREFIX=${DEPS_INSTALL_DIR}
+ ${DEPLOYMENT_TARGET})
+ endforeach()
BuildLuaJit(
- # Similar to Unix + cross - fPIC
- INSTALL_COMMAND
- ${MAKE_PRG} PREFIX=${DEPS_INSTALL_DIR}
- BUILDMODE=static install
- TARGET_SYS=${CMAKE_SYSTEM_NAME}
- CROSS=${CROSS_TARGET}-
- HOST_CC=${HOST_C_COMPILER} HOST_CFLAGS=${HOST_C_FLAGS}
- HOST_LDFLAGS=${HOST_EXE_LINKER_FLAGS}
- FILE_T=luajit.exe
- Q=
- INSTALL_TSYMNAME=luajit.exe)
+ CONFIGURE_COMMAND ${BUILDCMD_UNIX} CC=${LUAJIT_C_COMPILER} PREFIX=${DEPS_INSTALL_DIR} ${DEPLOYMENT_TARGET}
+ COMMAND ${CMAKE_COMMAND} -E rm -f ${LUAJIT_SRC_DIR}/src/luajit ${LUAJIT_SRC_DIR}/src/libluajit.so ${LUAJIT_SRC_DIR}/src/libluajit.a
+ BUILD_COMMAND lipo ${LUAJIT_THIN_EXECUTABLES} -create -output ${LUAJIT_SRC_DIR}/src/luajit
+ COMMAND lipo ${LUAJIT_THIN_STATIC_LIBS} -create -output ${LUAJIT_SRC_DIR}/src/libluajit.a
+ COMMAND lipo ${LUAJIT_THIN_DYLIBS} -create -output ${LUAJIT_SRC_DIR}/src/libluajit.so
+ INSTALL_COMMAND ${BUILDCMD_UNIX} CC=${LUAJIT_C_COMPILER} PREFIX=${DEPS_INSTALL_DIR} ${DEPLOYMENT_TARGET} install
+ DEPENDS ${LUAJIT_THIN_TARGETS}
+ )
elseif(MINGW)
diff --git a/third-party/cmake/BuildLuarocks.cmake b/cmake.deps/cmake/BuildLuarocks.cmake
index 0cef37736d..6933d263f2 100644
--- a/third-party/cmake/BuildLuarocks.cmake
+++ b/cmake.deps/cmake/BuildLuarocks.cmake
@@ -56,7 +56,7 @@ endif()
# Defaults to 5.1 for bundled LuaJIT/Lua.
set(LUA_VERSION "5.1")
-if(UNIX OR (MINGW AND CMAKE_CROSSCOMPILING))
+if(UNIX)
if(USE_BUNDLED_LUAJIT)
list(APPEND LUAROCKS_OPTS
@@ -123,9 +123,6 @@ list(APPEND THIRD_PARTY_DEPS luarocks)
if(USE_BUNDLED_LUAJIT)
add_dependencies(luarocks luajit)
- if(MINGW AND CMAKE_CROSSCOMPILING)
- add_dependencies(luarocks luajit_host)
- endif()
elseif(USE_BUNDLED_LUA)
add_dependencies(luarocks lua)
endif()
@@ -196,9 +193,6 @@ if(USE_BUNDLED_BUSTED)
set(LUV_DEPS luacheck)
if(USE_BUNDLED_LUV)
list(APPEND LUV_DEPS luv-static lua-compat-5.3)
- if(MINGW AND CMAKE_CROSSCOMPILING)
- list(APPEND LUV_DEPS libuv_host)
- endif()
set(LUV_ARGS "CFLAGS=-O0 -g3 -fPIC")
if(USE_BUNDLED_LIBUV)
list(APPEND LUV_ARGS LIBUV_DIR=${HOSTDEPS_INSTALL_DIR})
@@ -225,7 +219,7 @@ if(USE_BUNDLED_BUSTED)
# nvim-client: https://github.com/neovim/lua-client
add_custom_command(OUTPUT ${ROCKS_DIR}/nvim-client
COMMAND ${LUAROCKS_BINARY}
- ARGS build nvim-client 0.2.2-1 ${LUAROCKS_BUILDARGS}
+ ARGS build nvim-client 0.2.4-1 ${LUAROCKS_BUILDARGS}
DEPENDS luv)
add_custom_target(nvim-client DEPENDS ${ROCKS_DIR}/nvim-client)
diff --git a/third-party/cmake/BuildLuv.cmake b/cmake.deps/cmake/BuildLuv.cmake
index 69f3b60ecf..6e9a333dc8 100644
--- a/third-party/cmake/BuildLuv.cmake
+++ b/cmake.deps/cmake/BuildLuv.cmake
@@ -48,7 +48,8 @@ function(BuildLuv)
PATCH_COMMAND "${_luv_PATCH_COMMAND}"
CONFIGURE_COMMAND "${_luv_CONFIGURE_COMMAND}"
BUILD_COMMAND "${_luv_BUILD_COMMAND}"
- INSTALL_COMMAND "${_luv_INSTALL_COMMAND}")
+ INSTALL_COMMAND "${_luv_INSTALL_COMMAND}"
+ LIST_SEPARATOR |)
endfunction()
set(LUV_SRC_DIR ${DEPS_BUILD_DIR}/src/luv)
@@ -65,6 +66,7 @@ set(LUV_CONFIGURE_COMMAND_COMMON
-DCMAKE_GENERATOR=${CMAKE_GENERATOR}
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
+ -DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES_ALT_SEP}
-DLUA_BUILD_TYPE=System
-DLUA_COMPAT53_DIR=${DEPS_BUILD_DIR}/src/lua-compat-5.3
-DWITH_SHARED_LIBUV=ON
@@ -91,19 +93,11 @@ if(USE_BUNDLED_LIBUV)
-DCMAKE_PREFIX_PATH=${DEPS_INSTALL_DIR})
endif()
-if(MINGW AND CMAKE_CROSSCOMPILING)
- get_filename_component(TOOLCHAIN ${CMAKE_TOOLCHAIN_FILE} REALPATH)
- set(LUV_CONFIGURE_COMMAND
- ${LUV_CONFIGURE_COMMAND_COMMON}
- # Pass toolchain
- -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN}
- "-DCMAKE_C_FLAGS:STRING=${LUV_INCLUDE_FLAGS} -D_WIN32_WINNT=0x0600"
- # Hack to avoid -rdynamic in Mingw
- -DCMAKE_SHARED_LIBRARY_LINK_C_FLAGS="")
-elseif(MSVC)
+if(MSVC)
set(LUV_CONFIGURE_COMMAND
${LUV_CONFIGURE_COMMAND_COMMON}
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
+ -DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM}
# Same as Unix without fPIC
"-DCMAKE_C_FLAGS:STRING=${CMAKE_C_COMPILER_ARG1} ${LUV_INCLUDE_FLAGS}"
# Make sure we use the same generator, otherwise we may
diff --git a/third-party/cmake/BuildMsgpack.cmake b/cmake.deps/cmake/BuildMsgpack.cmake
index 39a8a64d23..10bf1c8e37 100644
--- a/third-party/cmake/BuildMsgpack.cmake
+++ b/cmake.deps/cmake/BuildMsgpack.cmake
@@ -27,42 +27,31 @@ function(BuildMsgpack)
-P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake
CONFIGURE_COMMAND "${_msgpack_CONFIGURE_COMMAND}"
BUILD_COMMAND "${_msgpack_BUILD_COMMAND}"
- INSTALL_COMMAND "${_msgpack_INSTALL_COMMAND}")
+ INSTALL_COMMAND "${_msgpack_INSTALL_COMMAND}"
+ LIST_SEPARATOR |)
endfunction()
set(MSGPACK_CONFIGURE_COMMAND ${CMAKE_COMMAND} ${DEPS_BUILD_DIR}/src/msgpack
- -DMSGPACK_ENABLE_CXX=OFF
-DMSGPACK_BUILD_TESTS=OFF
-DMSGPACK_BUILD_EXAMPLES=OFF
-DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
+ -DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES_ALT_SEP}
"-DCMAKE_C_FLAGS:STRING=${CMAKE_C_COMPILER_ARG1} -fPIC"
-DCMAKE_GENERATOR=${CMAKE_GENERATOR})
set(MSGPACK_BUILD_COMMAND ${CMAKE_COMMAND} --build . --config ${CMAKE_BUILD_TYPE})
set(MSGPACK_INSTALL_COMMAND ${CMAKE_COMMAND} --build . --target install --config ${CMAKE_BUILD_TYPE})
-if(MINGW AND CMAKE_CROSSCOMPILING)
- get_filename_component(TOOLCHAIN ${CMAKE_TOOLCHAIN_FILE} REALPATH)
- set(MSGPACK_CONFIGURE_COMMAND ${CMAKE_COMMAND} ${DEPS_BUILD_DIR}/src/msgpack
- -DMSGPACK_ENABLE_CXX=OFF
- -DMSGPACK_BUILD_TESTS=OFF
- -DMSGPACK_BUILD_EXAMPLES=OFF
- -DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
- # Pass toolchain
- -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN}
- -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
- # Hack to avoid -rdynamic in Mingw
- -DCMAKE_SHARED_LIBRARY_LINK_C_FLAGS="")
-elseif(MSVC)
+if(MSVC)
# Same as Unix without fPIC
set(MSGPACK_CONFIGURE_COMMAND ${CMAKE_COMMAND} ${DEPS_BUILD_DIR}/src/msgpack
- -DMSGPACK_ENABLE_CXX=OFF
-DMSGPACK_BUILD_TESTS=OFF
-DMSGPACK_BUILD_EXAMPLES=OFF
-DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
+ -DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM}
"-DCMAKE_C_FLAGS:STRING=${CMAKE_C_COMPILER_ARG1}"
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
# Make sure we use the same generator, otherwise we may
diff --git a/third-party/cmake/BuildTreesitter.cmake b/cmake.deps/cmake/BuildTreesitter.cmake
index 0aa2706d7d..01fdb837e2 100644
--- a/third-party/cmake/BuildTreesitter.cmake
+++ b/cmake.deps/cmake/BuildTreesitter.cmake
@@ -42,6 +42,7 @@ if(MSVC)
COMMAND ${CMAKE_COMMAND} ${DEPS_BUILD_DIR}/src/tree-sitter/CMakeLists.txt
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
-DCMAKE_GENERATOR=${CMAKE_GENERATOR}
+ -DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM}
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
BUILD_COMMAND ${CMAKE_COMMAND} --build . --config ${CMAKE_BUILD_TYPE}
diff --git a/third-party/cmake/BuildTreesitterParsers.cmake b/cmake.deps/cmake/BuildTreesitterParsers.cmake
index 5284a7fd62..11ffb792de 100644
--- a/third-party/cmake/BuildTreesitterParsers.cmake
+++ b/cmake.deps/cmake/BuildTreesitterParsers.cmake
@@ -15,13 +15,15 @@ PATCH_COMMAND ${CMAKE_COMMAND} -E copy
${DEPS_BUILD_DIR}/src/treesitter-c/CMakeLists.txt
CMAKE_ARGS
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
- -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
-DCMAKE_GENERATOR=${CMAKE_GENERATOR}
+ -DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM}
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
+ -DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES_ALT_SEP}
# Pass toolchain
-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}
-DPARSERLANG=c
BUILD_COMMAND ${CMAKE_COMMAND} --build . --config ${CMAKE_BUILD_TYPE}
-INSTALL_COMMAND ${CMAKE_COMMAND} --build . --target install --config ${CMAKE_BUILD_TYPE})
+INSTALL_COMMAND ${CMAKE_COMMAND} --build . --target install --config ${CMAKE_BUILD_TYPE}
+LIST_SEPARATOR |)
diff --git a/third-party/cmake/BuildUnibilium.cmake b/cmake.deps/cmake/BuildUnibilium.cmake
index 74c1cbddb0..2f940bdfd3 100644
--- a/third-party/cmake/BuildUnibilium.cmake
+++ b/cmake.deps/cmake/BuildUnibilium.cmake
@@ -18,6 +18,7 @@ if(WIN32)
-DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
# Pass toolchain
-DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN}
+ -DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM}
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DCMAKE_GENERATOR=${CMAKE_GENERATOR}
BUILD_COMMAND ${CMAKE_COMMAND} --build . --config ${CMAKE_BUILD_TYPE}
diff --git a/third-party/cmake/CopyFilesGlob.cmake b/cmake.deps/cmake/CopyFilesGlob.cmake
index 8950ead1e5..8950ead1e5 100644
--- a/third-party/cmake/CopyFilesGlob.cmake
+++ b/cmake.deps/cmake/CopyFilesGlob.cmake
diff --git a/third-party/cmake/DownloadAndExtractFile.cmake b/cmake.deps/cmake/DownloadAndExtractFile.cmake
index abb1ddc81a..abb1ddc81a 100644
--- a/third-party/cmake/DownloadAndExtractFile.cmake
+++ b/cmake.deps/cmake/DownloadAndExtractFile.cmake
diff --git a/third-party/cmake/GetBinaryDeps.cmake b/cmake.deps/cmake/GetBinaryDeps.cmake
index 04e3f95a29..04e3f95a29 100644
--- a/third-party/cmake/GetBinaryDeps.cmake
+++ b/cmake.deps/cmake/GetBinaryDeps.cmake
diff --git a/third-party/cmake/GettextCMakeLists.txt b/cmake.deps/cmake/GettextCMakeLists.txt
index c3f78716d0..c3f78716d0 100644
--- a/third-party/cmake/GettextCMakeLists.txt
+++ b/cmake.deps/cmake/GettextCMakeLists.txt
diff --git a/third-party/cmake/LibiconvCMakeLists.txt b/cmake.deps/cmake/LibiconvCMakeLists.txt
index 8ad3cc9352..8ad3cc9352 100644
--- a/third-party/cmake/LibiconvCMakeLists.txt
+++ b/cmake.deps/cmake/LibiconvCMakeLists.txt
diff --git a/third-party/cmake/LibuvCMakeLists.txt b/cmake.deps/cmake/LibuvCMakeLists.txt
index 0432319834..0432319834 100644
--- a/third-party/cmake/LibuvCMakeLists.txt
+++ b/cmake.deps/cmake/LibuvCMakeLists.txt
diff --git a/third-party/cmake/Libvterm-tbl2inc_c.cmake b/cmake.deps/cmake/Libvterm-tbl2inc_c.cmake
index 7a82f4248b..7a82f4248b 100644
--- a/third-party/cmake/Libvterm-tbl2inc_c.cmake
+++ b/cmake.deps/cmake/Libvterm-tbl2inc_c.cmake
diff --git a/third-party/cmake/LibvtermCMakeLists.txt b/cmake.deps/cmake/LibvtermCMakeLists.txt
index 16c4d542c4..16c4d542c4 100644
--- a/third-party/cmake/LibvtermCMakeLists.txt
+++ b/cmake.deps/cmake/LibvtermCMakeLists.txt
diff --git a/third-party/cmake/RemoveFiles.cmake b/cmake.deps/cmake/RemoveFiles.cmake
index 88e2bc70a6..88e2bc70a6 100644
--- a/third-party/cmake/RemoveFiles.cmake
+++ b/cmake.deps/cmake/RemoveFiles.cmake
diff --git a/third-party/cmake/TargetArch.cmake b/cmake.deps/cmake/TargetArch.cmake
index 71ea44ec59..71ea44ec59 100644
--- a/third-party/cmake/TargetArch.cmake
+++ b/cmake.deps/cmake/TargetArch.cmake
diff --git a/third-party/cmake/TreesitterCMakeLists.txt b/cmake.deps/cmake/TreesitterCMakeLists.txt
index 9e3ba3eeda..9e3ba3eeda 100644
--- a/third-party/cmake/TreesitterCMakeLists.txt
+++ b/cmake.deps/cmake/TreesitterCMakeLists.txt
diff --git a/third-party/cmake/TreesitterParserCMakeLists.txt b/cmake.deps/cmake/TreesitterParserCMakeLists.txt
index 2808a9ee14..2808a9ee14 100644
--- a/third-party/cmake/TreesitterParserCMakeLists.txt
+++ b/cmake.deps/cmake/TreesitterParserCMakeLists.txt
diff --git a/third-party/cmake/UnibiliumCMakeLists.txt b/cmake.deps/cmake/UnibiliumCMakeLists.txt
index 08a8599352..08a8599352 100644
--- a/third-party/cmake/UnibiliumCMakeLists.txt
+++ b/cmake.deps/cmake/UnibiliumCMakeLists.txt
diff --git a/third-party/cmake/libtermkeyCMakeLists.txt b/cmake.deps/cmake/libtermkeyCMakeLists.txt
index af54c1daec..af54c1daec 100644
--- a/third-party/cmake/libtermkeyCMakeLists.txt
+++ b/cmake.deps/cmake/libtermkeyCMakeLists.txt
diff --git a/third-party/patches/libvterm-Remove-VLAs-for-MSVC.patch b/cmake.deps/patches/libvterm-Remove-VLAs-for-MSVC.patch
index e999c0fa9b..e999c0fa9b 100644
--- a/third-party/patches/libvterm-Remove-VLAs-for-MSVC.patch
+++ b/cmake.deps/patches/libvterm-Remove-VLAs-for-MSVC.patch
diff --git a/cmake.packaging/CMakeLists.txt b/cmake.packaging/CMakeLists.txt
new file mode 100644
index 0000000000..8538075388
--- /dev/null
+++ b/cmake.packaging/CMakeLists.txt
@@ -0,0 +1,64 @@
+set(CPACK_PACKAGE_NAME "Neovim")
+set(CPACK_PACKAGE_VENDOR "neovim.io")
+set(CPACK_PACKAGE_FILE_NAME "nvim")
+
+# From the GitHub About section
+set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Vim-fork focused on extensibility and usability.")
+
+set(CPACK_PACKAGE_INSTALL_DIRECTORY ${CPACK_PACKAGE_NAME})
+
+# Pull the versions defined with the top level CMakeLists.txt
+set(CPACK_PACKAGE_VERSION_MAJOR ${NVIM_VERSION_MAJOR})
+set(CPACK_PACKAGE_VERSION_MINOR ${NVIM_VERSION_MINOR})
+set(CPACK_PACKAGE_VERSION_PATCH ${NVIM_VERSION_PATCH})
+
+# CPACK_VERBATIM_VARIABLES ensures that the variables prefixed with *CPACK_*
+# are correctly passed to the cpack program.
+# This should always be set to true.
+set(CPACK_VERBATIM_VARIABLES TRUE)
+
+set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE.txt")
+set(CPACK_RESOURCE_FILE_README ${PROJECT_SOURCE_DIR}/README.md)
+
+
+if(WIN32)
+ set(CPACK_PACKAGE_FILE_NAME "nvim-win64")
+ set(CPACK_GENERATOR ZIP WIX)
+
+ # WIX
+ # CPACK_WIX_UPGRADE_GUID should be set, but should never change.
+ # CPACK_WIX_PRODUCT_GUID should not be set (leave as default to auto-generate).
+
+ # The following guid is just a randomly generated guid that's been pasted here.
+ # It has no special meaning other than to supply it to WIX.
+ set(CPACK_WIX_UPGRADE_GUID "207A1A70-7B0C-418A-A153-CA6883E38F4D")
+ set(CPACK_WIX_PRODUCT_ICON ${CMAKE_CURRENT_LIST_DIR}/neovim.ico)
+
+ # We use a wix patch to add further options to the installer. At present, it's just to add neovim to the path
+ # on installation, however, it can be extended.
+ # See: https://cmake.org/cmake/help/v3.7/module/CPackWIX.html#variable:CPACK_WIX_PATCH_FILE
+ list(APPEND CPACK_WIX_EXTENSIONS WixUtilExtension)
+ list(APPEND CPACK_WIX_PATCH_FILE ${CMAKE_CURRENT_LIST_DIR}/WixPatch.xml)
+elseif(APPLE)
+ set(CPACK_PACKAGE_FILE_NAME "nvim-macos")
+ set(CPACK_GENERATOR TGZ)
+ set(CPACK_PACKAGE_ICON ${CMAKE_CURRENT_LIST_DIR}/neovim.icns)
+elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+ set(CPACK_PACKAGE_FILE_NAME "nvim-linux64")
+ set(CPACK_GENERATOR TGZ DEB)
+ set(CPACK_DEBIAN_PACKAGE_NAME "Neovim") # required
+ set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Neovim.io") # required
+
+ # Automatically compute required shared lib dependencies.
+ # Unfortunately, you "just need to know" that this has a hidden
+ # dependency on dpkg-shlibdeps whilst using a debian based host.
+ set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS TRUE)
+else()
+ set(CPACK_GENERATOR TGZ)
+endif()
+
+# CPack variables are loaded in on the call to include(CPack). If you set
+# variables *after* the inclusion, they don't get updated within the CPack
+# config. Note that some CPack commands should still be run after it, such
+# as cpack_add_component().
+include(CPack)
diff --git a/cmake.packaging/WixPatch.xml b/cmake.packaging/WixPatch.xml
new file mode 100644
index 0000000000..3cbbb04850
--- /dev/null
+++ b/cmake.packaging/WixPatch.xml
@@ -0,0 +1,14 @@
+<CPackWiXPatch>
+ <!-- Fragment ID is from: <your build dir>/_CPack_Packages/win64/WIX/files.wxs -->
+ <CPackWiXFragment Id="CM_CP_bin.nvim.exe">
+ <Environment
+ Id='UpdatePath'
+ Name='PATH'
+ Action='set'
+ Permanent='no'
+ System='yes'
+ Part='last'
+ Value='[INSTALL_ROOT]bin'
+ />
+ </CPackWiXFragment>
+</CPackWiXPatch>
diff --git a/cmake.packaging/neovim.icns b/cmake.packaging/neovim.icns
new file mode 100644
index 0000000000..df0e982369
--- /dev/null
+++ b/cmake.packaging/neovim.icns
Binary files differ
diff --git a/cmake.packaging/neovim.ico b/cmake.packaging/neovim.ico
new file mode 100644
index 0000000000..e0c151c966
--- /dev/null
+++ b/cmake.packaging/neovim.ico
Binary files differ
diff --git a/cmake.packaging/neovim.png b/cmake.packaging/neovim.png
new file mode 100644
index 0000000000..a3960b41bd
--- /dev/null
+++ b/cmake.packaging/neovim.png
Binary files differ
diff --git a/cmake.packaging/neovim.svg b/cmake.packaging/neovim.svg
new file mode 100644
index 0000000000..d82ad667c1
--- /dev/null
+++ b/cmake.packaging/neovim.svg
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="256"
+ height="256"
+ viewBox="0 0 256 256"
+ version="1.1"
+ id="svg4612"
+ sodipodi:docname="neovim.svg"
+ inkscape:version="0.92.4 5da689c313, 2019-01-14">
+ <metadata
+ id="metadata4616">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title>neovim-mark@2x</dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="2560"
+ inkscape:window-height="1333"
+ id="namedview4614"
+ showgrid="false"
+ inkscape:zoom="2.1945358"
+ inkscape:cx="132.84232"
+ inkscape:cy="196.34741"
+ inkscape:window-x="0"
+ inkscape:window-y="34"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg4612" />
+ <title
+ id="title4587">neovim-mark@2x</title>
+ <description>Created with Sketch (http://www.bohemiancoding.com/sketch)</description>
+ <defs
+ id="defs4604">
+ <linearGradient
+ x1="167.95833"
+ y1="-0.46142399"
+ x2="167.95833"
+ y2="335.45523"
+ id="linearGradient-1"
+ gradientTransform="scale(0.46142398,2.1672042)"
+ gradientUnits="userSpaceOnUse">
+ <stop
+ stop-color="#16B0ED"
+ stop-opacity="0.800235524"
+ offset="0%"
+ id="stop4589" />
+ <stop
+ stop-color="#0F59B2"
+ stop-opacity="0.83700023"
+ offset="100%"
+ id="stop4591" />
+ </linearGradient>
+ <linearGradient
+ x1="1118.3427"
+ y1="-0.46586797"
+ x2="1118.3427"
+ y2="338.68604"
+ id="linearGradient-2"
+ gradientTransform="scale(0.46586797,2.1465309)"
+ gradientUnits="userSpaceOnUse">
+ <stop
+ stop-color="#7DB643"
+ offset="0%"
+ id="stop4594" />
+ <stop
+ stop-color="#367533"
+ offset="100%"
+ id="stop4596" />
+ </linearGradient>
+ <linearGradient
+ x1="356.33795"
+ y1="0"
+ x2="356.33795"
+ y2="612.90131"
+ id="linearGradient-3"
+ gradientTransform="scale(0.84189739,1.1877932)"
+ gradientUnits="userSpaceOnUse">
+ <stop
+ stop-color="#88C649"
+ stop-opacity="0.8"
+ offset="0%"
+ id="stop4599" />
+ <stop
+ stop-color="#439240"
+ stop-opacity="0.84"
+ offset="100%"
+ id="stop4601" />
+ </linearGradient>
+ </defs>
+ <g
+ id="Page-1"
+ sketch:type="MSPage"
+ style="fill:none;fill-rule:evenodd;stroke:none;stroke-width:1"
+ transform="matrix(0.34367476,0,0,0.34367476,25.312651,1.7737533)">
+ <g
+ id="mark-copy"
+ sketch:type="MSLayerGroup"
+ transform="translate(2,3)">
+ <path
+ d="M 0,155.5704 155,-1 V 727 L 0,572.23792 Z"
+ id="Left---green"
+ sketch:type="MSShapeGroup"
+ inkscape:connector-curvature="0"
+ style="fill:url(#linearGradient-1)" />
+ <path
+ d="M 443.0604,156.9824 600,-1 596.81879,727 442,572.21994 Z"
+ id="Right---blue"
+ sketch:type="MSShapeGroup"
+ transform="matrix(-1,0,0,1,1042,0)"
+ inkscape:connector-curvature="0"
+ style="fill:url(#linearGradient-2)" />
+ <path
+ d="M 154.98629,0 558,615.1897 445.2246,728 42,114.17202 Z"
+ id="Cross---blue"
+ sketch:type="MSShapeGroup"
+ inkscape:connector-curvature="0"
+ style="fill:url(#linearGradient-3)" />
+ <path
+ d="M 155,283.83232 154.78675,308 31,124.71061 42.461949,113 Z"
+ id="Shadow"
+ sketch:type="MSShapeGroup"
+ inkscape:connector-curvature="0"
+ style="fill:#000000;fill-opacity:0.12999998" />
+ </g>
+ </g>
+</svg>
diff --git a/cmake/FindLibIntl.cmake b/cmake/FindLibIntl.cmake
index 09eafb786a..8cc5cb7dd2 100644
--- a/cmake/FindLibIntl.cmake
+++ b/cmake/FindLibIntl.cmake
@@ -41,6 +41,16 @@ endif()
if (MSVC)
list(APPEND CMAKE_REQUIRED_LIBRARIES ${ICONV_LIBRARY})
endif()
+
+# On macOS, if libintl is a static library then we also need
+# to link libiconv and CoreFoundation.
+get_filename_component(LibIntl_EXT "${LibIntl_LIBRARY}" EXT)
+if (APPLE AND (LibIntl_EXT STREQUAL ".a"))
+ set(LibIntl_STATIC TRUE)
+ find_library(CoreFoundation_FRAMEWORK CoreFoundation)
+ list(APPEND CMAKE_REQUIRED_LIBRARIES "${ICONV_LIBRARY}" "${CoreFoundation_FRAMEWORK}")
+endif()
+
check_c_source_compiles("
#include <libintl.h>
@@ -54,6 +64,9 @@ int main(int argc, char** argv) {
if (MSVC)
list(REMOVE_ITEM CMAKE_REQUIRED_LIBRARIES ${ICONV_LIBRARY})
endif()
+if (LibIntl_STATIC)
+ list(REMOVE_ITEM CMAKE_REQUIRED_LIBRARIES "${ICONV_LIBRARY}" "${CoreFoundation_FRAMEWORK}")
+endif()
if (LibIntl_INCLUDE_DIR)
list(REMOVE_ITEM CMAKE_REQUIRED_INCLUDES "${LibIntl_INCLUDE_DIR}")
endif()
diff --git a/cmake/FindLua.cmake b/cmake/FindLua.cmake
deleted file mode 100644
index 7ba13e1f56..0000000000
--- a/cmake/FindLua.cmake
+++ /dev/null
@@ -1,197 +0,0 @@
-# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
-# file Copyright.txt or https://cmake.org/licensing for details.
-
-#.rst:
-# FindLua
-# -------
-#
-#
-#
-# Locate Lua library This module defines
-#
-# ::
-#
-# LUA_FOUND - if false, do not try to link to Lua
-# LUA_LIBRARIES - both lua and lualib
-# LUA_INCLUDE_DIR - where to find lua.h
-# LUA_VERSION_STRING - the version of Lua found
-# LUA_VERSION_MAJOR - the major version of Lua
-# LUA_VERSION_MINOR - the minor version of Lua
-# LUA_VERSION_PATCH - the patch version of Lua
-#
-#
-#
-# Note that the expected include convention is
-#
-# ::
-#
-# #include "lua.h"
-#
-# and not
-#
-# ::
-#
-# #include <lua/lua.h>
-#
-# This is because, the lua location is not standardized and may exist in
-# locations other than lua/
-
-unset(_lua_include_subdirs)
-unset(_lua_library_names)
-unset(_lua_append_versions)
-
-# this is a function only to have all the variables inside go away automatically
-function(_lua_set_version_vars)
- set(LUA_VERSIONS5 5.4 5.3 5.2 5.1 5.0)
-
- if (Lua_FIND_VERSION_EXACT)
- if (Lua_FIND_VERSION_COUNT GREATER 1)
- set(_lua_append_versions ${Lua_FIND_VERSION_MAJOR}.${Lua_FIND_VERSION_MINOR})
- endif ()
- elseif (Lua_FIND_VERSION)
- # once there is a different major version supported this should become a loop
- if (NOT Lua_FIND_VERSION_MAJOR GREATER 5)
- if (Lua_FIND_VERSION_COUNT EQUAL 1)
- set(_lua_append_versions ${LUA_VERSIONS5})
- else ()
- foreach (subver IN LISTS LUA_VERSIONS5)
- if (NOT subver VERSION_LESS ${Lua_FIND_VERSION})
- list(APPEND _lua_append_versions ${subver})
- endif ()
- endforeach ()
- endif ()
- endif ()
- else ()
- # once there is a different major version supported this should become a loop
- set(_lua_append_versions ${LUA_VERSIONS5})
- endif ()
-
- list(APPEND _lua_include_subdirs "include/lua" "include")
-
- foreach (ver IN LISTS _lua_append_versions)
- string(REGEX MATCH "^([0-9]+)\\.([0-9]+)$" _ver "${ver}")
- list(APPEND _lua_include_subdirs
- include/lua${CMAKE_MATCH_1}${CMAKE_MATCH_2}
- include/lua${CMAKE_MATCH_1}.${CMAKE_MATCH_2}
- include/lua-${CMAKE_MATCH_1}.${CMAKE_MATCH_2}
- )
- endforeach ()
-
- set(_lua_include_subdirs "${_lua_include_subdirs}" PARENT_SCOPE)
- set(_lua_append_versions "${_lua_append_versions}" PARENT_SCOPE)
-endfunction(_lua_set_version_vars)
-
-function(_lua_check_header_version _hdr_file)
- # At least 5.[012] have different ways to express the version
- # so all of them need to be tested. Lua 5.2 defines LUA_VERSION
- # and LUA_RELEASE as joined by the C preprocessor, so avoid those.
- file(STRINGS "${_hdr_file}" lua_version_strings
- REGEX "^#define[ \t]+LUA_(RELEASE[ \t]+\"Lua [0-9]|VERSION([ \t]+\"Lua [0-9]|_[MR])).*")
-
- string(REGEX REPLACE ".*;#define[ \t]+LUA_VERSION_MAJOR[ \t]+\"([0-9])\"[ \t]*;.*" "\\1" LUA_VERSION_MAJOR ";${lua_version_strings};")
- if (LUA_VERSION_MAJOR MATCHES "^[0-9]+$")
- string(REGEX REPLACE ".*;#define[ \t]+LUA_VERSION_MINOR[ \t]+\"([0-9])\"[ \t]*;.*" "\\1" LUA_VERSION_MINOR ";${lua_version_strings};")
- string(REGEX REPLACE ".*;#define[ \t]+LUA_VERSION_RELEASE[ \t]+\"([0-9])\"[ \t]*;.*" "\\1" LUA_VERSION_PATCH ";${lua_version_strings};")
- set(LUA_VERSION_STRING "${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}.${LUA_VERSION_PATCH}")
- else ()
- string(REGEX REPLACE ".*;#define[ \t]+LUA_RELEASE[ \t]+\"Lua ([0-9.]+)\"[ \t]*;.*" "\\1" LUA_VERSION_STRING ";${lua_version_strings};")
- if (NOT LUA_VERSION_STRING MATCHES "^[0-9.]+$")
- string(REGEX REPLACE ".*;#define[ \t]+LUA_VERSION[ \t]+\"Lua ([0-9.]+)\"[ \t]*;.*" "\\1" LUA_VERSION_STRING ";${lua_version_strings};")
- endif ()
- string(REGEX REPLACE "^([0-9]+)\\.[0-9.]*$" "\\1" LUA_VERSION_MAJOR "${LUA_VERSION_STRING}")
- string(REGEX REPLACE "^[0-9]+\\.([0-9]+)[0-9.]*$" "\\1" LUA_VERSION_MINOR "${LUA_VERSION_STRING}")
- string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.([0-9]).*" "\\1" LUA_VERSION_PATCH "${LUA_VERSION_STRING}")
- endif ()
- foreach (ver IN LISTS _lua_append_versions)
- if (ver STREQUAL "${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}")
- set(LUA_VERSION_MAJOR ${LUA_VERSION_MAJOR} PARENT_SCOPE)
- set(LUA_VERSION_MINOR ${LUA_VERSION_MINOR} PARENT_SCOPE)
- set(LUA_VERSION_PATCH ${LUA_VERSION_PATCH} PARENT_SCOPE)
- set(LUA_VERSION_STRING ${LUA_VERSION_STRING} PARENT_SCOPE)
- return()
- endif ()
- endforeach ()
-endfunction(_lua_check_header_version)
-
-_lua_set_version_vars()
-
-if (LUA_INCLUDE_DIR AND EXISTS "${LUA_INCLUDE_DIR}/lua.h")
- _lua_check_header_version("${LUA_INCLUDE_DIR}/lua.h")
-endif ()
-
-if (NOT LUA_VERSION_STRING)
- foreach (subdir IN LISTS _lua_include_subdirs)
- unset(LUA_INCLUDE_PREFIX CACHE)
- find_path(LUA_INCLUDE_PREFIX ${subdir}/lua.h
- HINTS
- ENV LUA_DIR
- PATHS
- ~/Library/Frameworks
- /Library/Frameworks
- /sw # Fink
- /opt/local # DarwinPorts
- /opt/csw # Blastwave
- /opt
- )
- if (LUA_INCLUDE_PREFIX)
- _lua_check_header_version("${LUA_INCLUDE_PREFIX}/${subdir}/lua.h")
- if (LUA_VERSION_STRING)
- set(LUA_INCLUDE_DIR "${LUA_INCLUDE_PREFIX}/${subdir}")
- break()
- endif ()
- endif ()
- endforeach ()
-endif ()
-unset(_lua_include_subdirs)
-unset(_lua_append_versions)
-
-if (LUA_VERSION_STRING)
- set(_lua_library_names
- lua${LUA_VERSION_MAJOR}${LUA_VERSION_MINOR}
- lua${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}
- lua-${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}
- lua.${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}
- )
-endif ()
-
-find_library(LUA_LIBRARY
- NAMES ${_lua_library_names} lua
- HINTS
- ENV LUA_DIR
- PATH_SUFFIXES lib
- PATHS
- ~/Library/Frameworks
- /Library/Frameworks
- /sw
- /opt/local
- /opt/csw
- /opt
-)
-unset(_lua_library_names)
-
-if (LUA_LIBRARY)
- # include the math library for Unix
- if (UNIX AND NOT APPLE AND NOT BEOS)
- find_library(LUA_MATH_LIBRARY m)
- set(LUA_LIBRARIES "${LUA_LIBRARY};${LUA_MATH_LIBRARY}")
-
- # include dl library for statically-linked Lua library
- get_filename_component(LUA_LIB_EXT ${LUA_LIBRARY} EXT)
- if(LUA_LIB_EXT STREQUAL CMAKE_STATIC_LIBRARY_SUFFIX)
- list(APPEND LUA_LIBRARIES ${CMAKE_DL_LIBS})
- endif()
-
- # For Windows and Mac, don't need to explicitly include the math library
- else ()
- set(LUA_LIBRARIES "${LUA_LIBRARY}")
- endif ()
-endif ()
-
-include(FindPackageHandleStandardArgs)
-# handle the QUIETLY and REQUIRED arguments and set LUA_FOUND to TRUE if
-# all listed variables are TRUE
-FIND_PACKAGE_HANDLE_STANDARD_ARGS(Lua
- REQUIRED_VARS LUA_LIBRARIES LUA_INCLUDE_DIR
- VERSION_VAR LUA_VERSION_STRING)
-
-mark_as_advanced(LUA_INCLUDE_DIR LUA_LIBRARY LUA_MATH_LIBRARY)
diff --git a/cmake/FindWinpty.cmake b/cmake/FindWinpty.cmake
deleted file mode 100644
index 8feafc58a8..0000000000
--- a/cmake/FindWinpty.cmake
+++ /dev/null
@@ -1,10 +0,0 @@
-include(LibFindMacros)
-
-find_path(WINPTY_INCLUDE_DIR winpty.h)
-set(WINPTY_INCLUDE_DIRS ${WINPTY_INCLUDE_DIR})
-
-find_library(WINPTY_LIBRARY winpty)
-find_program(WINPTY_AGENT_EXE winpty-agent.exe)
-set(WINPTY_LIBRARIES ${WINPTY_LIBRARY})
-
-find_package_handle_standard_args(Winpty DEFAULT_MSG WINPTY_LIBRARY WINPTY_INCLUDE_DIR)
diff --git a/cmake/GenerateVersion.cmake b/cmake/GenerateVersion.cmake
new file mode 100644
index 0000000000..b9313f2498
--- /dev/null
+++ b/cmake/GenerateVersion.cmake
@@ -0,0 +1,48 @@
+# Handle generating version from Git.
+set(use_git_version 0)
+if(NVIM_VERSION_MEDIUM)
+ message(STATUS "USING NVIM_VERSION_MEDIUM: ${NVIM_VERSION_MEDIUM}")
+ return()
+endif()
+
+find_program(GIT_EXECUTABLE git)
+if(NOT GIT_EXECUTABLE)
+ message(AUTHOR_WARNING "Skipping version-string generation (cannot find git)")
+ file(WRITE "${OUTPUT}" "")
+ return()
+endif()
+
+execute_process(
+ COMMAND git describe --first-parent --tags --always --dirty
+ OUTPUT_VARIABLE GIT_TAG
+ ERROR_VARIABLE ERR
+ RESULT_VARIABLE RES
+)
+
+if("${RES}" EQUAL 1)
+ if(EXISTS ${OUTPUT})
+ message(STATUS "Unable to extract version-string from git: keeping the last known version")
+ else()
+ # this will only be executed once since the file will get generated afterwards
+ message(AUTHOR_WARNING "Git tag extraction failed with: " "${ERR}")
+ file(WRITE "${OUTPUT}" "")
+ endif()
+ return()
+endif()
+
+string(STRIP "${GIT_TAG}" GIT_TAG)
+string(REGEX REPLACE "^v[0-9]+.[0-9]+.[0-9]+-" "" NVIM_VERSION_GIT "${GIT_TAG}")
+set(NVIM_VERSION_MEDIUM
+ "v${NVIM_VERSION_MAJOR}.${NVIM_VERSION_MINOR}.${NVIM_VERSION_PATCH}-dev-${NVIM_VERSION_GIT}"
+)
+set(NVIM_VERSION_STRING "#define NVIM_VERSION_MEDIUM \"${NVIM_VERSION_MEDIUM}\"\n")
+string(SHA1 CURRENT_VERSION_HASH "${NVIM_VERSION_STRING}")
+
+if(EXISTS ${OUTPUT})
+ file(SHA1 "${OUTPUT}" NVIM_VERSION_HASH)
+endif()
+
+if(NOT "${NVIM_VERSION_HASH}" STREQUAL "${CURRENT_VERSION_HASH}")
+ message(STATUS "Updating NVIM_VERSION_MEDIUM: ${NVIM_VERSION_MEDIUM}")
+ file(WRITE "${OUTPUT}" "${NVIM_VERSION_STRING}")
+endif()
diff --git a/cmake/RunTests.cmake b/cmake/RunTests.cmake
index 3adbcbbfc5..e07c6dd174 100644
--- a/cmake/RunTests.cmake
+++ b/cmake/RunTests.cmake
@@ -11,6 +11,7 @@ set(ENV{NVIM_RPLUGIN_MANIFEST} ${BUILD_DIR}/Xtest_rplugin_manifest)
set(ENV{XDG_CONFIG_HOME} ${BUILD_DIR}/Xtest_xdg/config)
set(ENV{XDG_DATA_HOME} ${BUILD_DIR}/Xtest_xdg/share)
unset(ENV{XDG_DATA_DIRS})
+unset(ENV{NVIM}) # Clear $NVIM in case tests are running from Nvim. #11009
if(NOT DEFINED ENV{NVIM_LOG_FILE})
set(ENV{NVIM_LOG_FILE} ${BUILD_DIR}/.nvimlog)
@@ -50,8 +51,8 @@ if(DEFINED ENV{TEST_FILTER_OUT} AND NOT "$ENV{TEST_FILTER_OUT}" STREQUAL "")
list(APPEND BUSTED_ARGS --filter-out $ENV{TEST_FILTER_OUT})
endif()
-# TMPDIR: use relative test path (for parallel test runs / isolation).
-set(ENV{TMPDIR} "${BUILD_DIR}/Xtest_tmpdir/${TEST_PATH}")
+# TMPDIR: for helpers.tmpname() and Nvim tempname().
+set(ENV{TMPDIR} "${BUILD_DIR}/Xtest_tmpdir")
execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory $ENV{TMPDIR})
# HISTFILE: do not write into user's ~/.bash_history
diff --git a/cmake/UninstallHelper.cmake b/cmake/UninstallHelper.cmake
new file mode 100644
index 0000000000..9a3d30af59
--- /dev/null
+++ b/cmake/UninstallHelper.cmake
@@ -0,0 +1,13 @@
+if(NOT EXISTS "${CMAKE_BINARY_DIR}/install_manifest.txt")
+ message(FATAL_ERROR "Cannot find install manifest: ${CMAKE_BINARY_DIR}/install_manifest.txt")
+endif()
+
+file(STRINGS "${CMAKE_BINARY_DIR}/install_manifest.txt" files)
+foreach(file ${files})
+ message(STATUS "Uninstalling $ENV{DESTDIR}${file}")
+ if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
+ file(REMOVE $ENV{DESTDIR}${file})
+ else()
+ message(STATUS "File $ENV{DESTDIR}${file} does not exist.")
+ endif()
+endforeach()
diff --git a/cmake/UninstallHelper.cmake.in b/cmake/UninstallHelper.cmake.in
deleted file mode 100644
index c2d34d4796..0000000000
--- a/cmake/UninstallHelper.cmake.in
+++ /dev/null
@@ -1,21 +0,0 @@
-if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt")
- message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt")
-endif()
-
-file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files)
-string(REGEX REPLACE "\n" ";" files "${files}")
-foreach(file ${files})
- message(STATUS "Uninstalling $ENV{DESTDIR}${file}")
- if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
- exec_program(
- "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
- OUTPUT_VARIABLE rm_out
- RETURN_VALUE rm_retval
- )
- if(NOT "${rm_retval}" STREQUAL 0)
- message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}")
- endif()
- else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
- message(STATUS "File $ENV{DESTDIR}${file} does not exist.")
- endif()
-endforeach()
diff --git a/cmake/Util.cmake b/cmake/Util.cmake
new file mode 100644
index 0000000000..b485f4606b
--- /dev/null
+++ b/cmake/Util.cmake
@@ -0,0 +1,145 @@
+# Defines a target that depends on FILES and the files found by globbing
+# when using GLOB_PAT and GLOB_DIRS. The target will rerun if any files it
+# depends on has changed. Which files the target will run the command on
+# depends on the value of TOUCH_STRATEGY.
+#
+# Options:
+# REQUIRED - Abort if COMMAND doesn't exist.
+#
+# Single value arguments:
+# TARGET - Name of the target
+# COMMAND - Path of the command to be run
+# GLOB_PAT - Glob pattern to use. Only used if GLOB_DIRS is specified
+# TOUCH_STRATEGY - Specify touch strategy, meaning decide how to group files
+# and connect them to a specific touch file.
+#
+# For example, let us say we have file A and B and that we create a touch file
+# for each of them, TA and TB. This would essentially make file A and B
+# independent of each other, meaning that if I change file A and run the
+# target, then the target will only run its commands for file A and ignore
+# file B.
+#
+# Another example: let's say we have file A and B, but now we create only a
+# single touch file T for both of them. This would mean that if I change
+# either file A or B, then the target will run its commands on both A and B.
+# Meaning that even if I only change file A, the target will still run
+# commands on both A and B.
+#
+# The more touch files we create for a target, the fewer commands we'll need
+# to rerun, and by extension, the more time we'll save. Unfortunately, the
+# more touch files we create the more intermediary targets will be created,
+# one for each touch file. This makes listing all targets with
+# `cmake --build build --target help` less useful since each touch file will
+# be listed. The tradeoff that needs to be done here is between performance
+# and "discoverability". As a general guideline: the more popular a target is
+# and the more time it takes to run it, the more granular you want your touch
+# files to be. Conversely, if a target rarely needs to be run or if it's fast,
+# then you should create fewer targets.
+#
+# Possible values for TOUCH_STRATEGY:
+# "SINGLE": create a single touch file for all files.
+# "PER_FILE": create a touch file for each file. Defaults to this if
+# TOUCH_STRATEGY isn't specified.
+# "PER_DIR": create a touch file for each directory.
+#
+# List arguments:
+# FLAGS - List of flags to use after COMMAND
+# FILES - List of files to use COMMAND on. It's possible to combine this
+# with GLOB_PAT and GLOB_DIRS; the files found by globbing will
+# simple be added to FILES
+# GLOB_DIRS - The directories to recursively search for files with extension
+# GLOB_PAT
+#
+function(add_glob_targets)
+ cmake_parse_arguments(ARG
+ "REQUIRED"
+ "TARGET;COMMAND;GLOB_PAT;TOUCH_STRATEGY"
+ "FLAGS;FILES;GLOB_DIRS"
+ ${ARGN}
+ )
+
+ if(NOT ARG_COMMAND)
+ add_custom_target(${ARG_TARGET})
+ if(ARG_REQUIRED)
+ add_custom_command(TARGET ${ARG_TARGET}
+ COMMAND ${CMAKE_COMMAND} -E echo "${ARG_TARGET}: ${ARG_COMMAND} not found"
+ COMMAND false)
+ else()
+ add_custom_command(TARGET ${ARG_TARGET}
+ COMMAND ${CMAKE_COMMAND} -E echo "${ARG_TARGET} SKIP: ${ARG_COMMAND} not found")
+ endif()
+ return()
+ endif()
+
+ foreach(gd ${ARG_GLOB_DIRS})
+ file(GLOB_RECURSE globfiles ${PROJECT_SOURCE_DIR}/${gd}/${ARG_GLOB_PAT})
+ list(APPEND ARG_FILES ${globfiles})
+ endforeach()
+
+ if(NOT ARG_TOUCH_STRATEGY)
+ set(ARG_TOUCH_STRATEGY PER_FILE)
+ endif()
+ set(POSSIBLE_TOUCH_STRATEGIES SINGLE PER_FILE PER_DIR)
+ if(NOT ARG_TOUCH_STRATEGY IN_LIST POSSIBLE_TOUCH_STRATEGIES)
+ message(FATAL_ERROR "Unrecognized value for TOUCH_STRATEGY: ${ARG_TOUCH_STRATEGY}")
+ endif()
+
+ if(ARG_TOUCH_STRATEGY STREQUAL SINGLE)
+ set(touch_file ${TOUCHES_DIR}/ran-${ARG_TARGET})
+ add_custom_command(
+ OUTPUT ${touch_file}
+ COMMAND ${CMAKE_COMMAND} -E touch ${touch_file}
+ COMMAND ${ARG_COMMAND} ${ARG_FLAGS} ${ARG_FILES}
+ WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
+ DEPENDS ${ARG_FILES})
+ list(APPEND touch_list ${touch_file})
+ elseif(ARG_TOUCH_STRATEGY STREQUAL PER_FILE)
+ set(touch_dir ${TOUCHES_DIR}/${ARG_TARGET})
+ file(MAKE_DIRECTORY ${touch_dir})
+ foreach(f ${ARG_FILES})
+ string(REGEX REPLACE "^${PROJECT_SOURCE_DIR}/" "" tf ${f})
+ string(REGEX REPLACE "[/.]" "-" tf ${tf})
+ set(touch_file ${touch_dir}/ran-${tf})
+ add_custom_command(
+ OUTPUT ${touch_file}
+ COMMAND ${CMAKE_COMMAND} -E touch ${touch_file}
+ COMMAND ${ARG_COMMAND} ${ARG_FLAGS} ${f}
+ WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
+ DEPENDS ${f})
+ list(APPEND touch_list ${touch_file})
+ endforeach()
+ elseif(ARG_TOUCH_STRATEGY STREQUAL PER_DIR)
+ set(touch_dirs)
+ foreach(f ${ARG_FILES})
+ get_filename_component(out ${f} DIRECTORY)
+ list(APPEND touch_dirs ${out})
+ endforeach()
+ list(REMOVE_DUPLICATES touch_dirs)
+
+ foreach(touch_dir ${touch_dirs})
+ set(relevant_files)
+ foreach(f ${ARG_FILES})
+ get_filename_component(out ${f} DIRECTORY)
+ if(${touch_dir} STREQUAL ${out})
+ list(APPEND relevant_files ${f})
+ endif()
+ endforeach()
+
+ set(td ${TOUCHES_DIR}/${ARG_TARGET})
+ file(MAKE_DIRECTORY ${td})
+ string(REGEX REPLACE "^${PROJECT_SOURCE_DIR}/" "" tf ${touch_dir})
+ string(REGEX REPLACE "[/.]" "-" tf ${tf})
+ set(touch_file ${td}/ran-${tf})
+
+ add_custom_command(
+ OUTPUT ${touch_file}
+ COMMAND ${CMAKE_COMMAND} -E touch ${touch_file}
+ COMMAND ${ARG_COMMAND} ${ARG_FLAGS} ${relevant_files}
+ WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
+ DEPENDS ${relevant_files})
+ list(APPEND touch_list ${touch_file})
+ endforeach()
+ endif()
+
+ add_custom_target(${ARG_TARGET} DEPENDS ${touch_list})
+endfunction()
diff --git a/contrib/asan.sh b/contrib/asan.sh
new file mode 100755
index 0000000000..7e7dffa1af
--- /dev/null
+++ b/contrib/asan.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+# Helper script to build and run neovim with Address Sanitizer enabled.
+# You may read more information in src/nvim/README.md in the section "Build
+# with ASAN".
+
+shopt -s nullglob
+
+root_path=$(git rev-parse --show-toplevel)
+log_path=$(mktemp -d)
+export CC='clang'
+
+# Change to detect_leaks=1 to detect memory leaks (slower).
+export ASAN_OPTIONS="detect_leaks=0:log_path=$log_path/asan"
+
+# Show backtraces in the logs.
+export UBSAN_OPTIONS="print_stacktrace=1"
+
+make -C "$root_path" CMAKE_EXTRA_FLAGS="-DCLANG_ASAN_UBSAN=ON"
+VIMRUNTIME="$root_path"/runtime "$root_path"/build/bin/nvim
+
+# Need to manually reset terminal to avoid mangled output, nvim does not
+# properly restore the terminal when it crashes.
+tput reset
+
+for i in "$log_path"/*; do
+ cat "$i"
+done
diff --git a/contrib/flake.lock b/contrib/flake.lock
index c4d7f120ba..b72e1f8d5f 100644
--- a/contrib/flake.lock
+++ b/contrib/flake.lock
@@ -2,11 +2,11 @@
"nodes": {
"flake-utils": {
"locked": {
- "lastModified": 1629481132,
- "narHash": "sha256-JHgasjPR0/J1J3DRm4KxM4zTyAj4IOJY8vIl75v/kPI=",
+ "lastModified": 1644229661,
+ "narHash": "sha256-1YdnJAsNy69bpcjuoKdOYQX0YxZBiCYZo4Twxerqv7k=",
"owner": "numtide",
"repo": "flake-utils",
- "rev": "997f7efcb746a9c140ce1f13c72263189225f482",
+ "rev": "3cecb5b042f7f209c56ffd8371b2711a290ec797",
"type": "github"
},
"original": {
@@ -17,11 +17,11 @@
},
"nixpkgs": {
"locked": {
- "lastModified": 1630074300,
- "narHash": "sha256-BFM7OiXRs0RvSUZd6NCGAKWVPn3VodgYQ4TUQXxbMBU=",
+ "lastModified": 1646254136,
+ "narHash": "sha256-8nQx02tTzgYO21BP/dy5BCRopE8OwE8Drsw98j+Qoaw=",
"owner": "nixos",
"repo": "nixpkgs",
- "rev": "21c937f8cb1e6adcfeb36dfd6c90d9d9bfab1d28",
+ "rev": "3e072546ea98db00c2364b81491b893673267827",
"type": "github"
},
"original": {
diff --git a/contrib/flake.nix b/contrib/flake.nix
index 848c52d208..a1072674ba 100644
--- a/contrib/flake.nix
+++ b/contrib/flake.nix
@@ -16,6 +16,10 @@
neovim = pkgs.neovim-unwrapped.overrideAttrs (oa: {
version = "master";
src = ../.;
+
+ buildInputs = oa.buildInputs ++ pkgs.lib.optionals pkgs.stdenv.isDarwin (with pkgs.darwin.apple_sdk.frameworks; [
+ CoreServices
+ ]);
});
# a development binary to help debug issues
diff --git a/contrib/luarc.json b/contrib/luarc.json
index 770b023ac6..8d76d1261d 100644
--- a/contrib/luarc.json
+++ b/contrib/luarc.json
@@ -5,6 +5,7 @@
"globals": [
"vim",
"describe",
+ "pending",
"it",
"before_each",
"after_each",
diff --git a/runtime/autoload/clojurecomplete.vim b/runtime/autoload/clojurecomplete.vim
index 9f2c39081a..02262a6f91 100644
--- a/runtime/autoload/clojurecomplete.vim
+++ b/runtime/autoload/clojurecomplete.vim
@@ -4,12 +4,12 @@
" Former Maintainers: Sung Pae <self@sungpae.com>
" URL: https://github.com/clojure-vim/clojure.vim
" License: Vim (see :h license)
-" Last Change: 2021-10-26
+" Last Change: 2022-03-24
" -*- COMPLETION WORDS -*-
-" Generated from https://github.com/clojure-vim/clojure.vim/blob/62b215f079ce0f3834fd295c7a7f6bd8cc54bcc3/clj/src/vim_clojure_static/generate.clj
-" Clojure version 1.10.3
-let s:words = ["*","*'","*1","*2","*3","*agent*","*allow-unresolved-vars*","*assert*","*clojure-version*","*command-line-args*","*compile-files*","*compile-path*","*compiler-options*","*data-readers*","*default-data-reader-fn*","*e","*err*","*file*","*flush-on-newline*","*fn-loader*","*in*","*math-context*","*ns*","*out*","*print-dup*","*print-length*","*print-level*","*print-meta*","*print-namespace-maps*","*print-readably*","*read-eval*","*reader-resolver*","*source-path*","*suppress-read*","*unchecked-math*","*use-context-classloader*","*verbose-defrecords*","*warn-on-reflection*","+","+'","-","-'","->","->>","->ArrayChunk","->Eduction","->Vec","->VecNode","->VecSeq","-cache-protocol-fn","-reset-methods",".","..","/","<","<=","=","==",">",">=","EMPTY-NODE","Inst","PrintWriter-on","StackTraceElement->vec","Throwable->map","accessor","aclone","add-classpath","add-tap","add-watch","agent","agent-error","agent-errors","aget","alength","alias","all-ns","alter","alter-meta!","alter-var-root","amap","ancestors","and","any?","apply","areduce","array-map","as->","aset","aset-boolean","aset-byte","aset-char","aset-double","aset-float","aset-int","aset-long","aset-short","assert","assoc!","assoc","assoc-in","associative?","atom","await","await-for","await1","bases","bean","bigdec","bigint","biginteger","binding","bit-and","bit-and-not","bit-clear","bit-flip","bit-not","bit-or","bit-set","bit-shift-left","bit-shift-right","bit-test","bit-xor","boolean","boolean-array","boolean?","booleans","bound-fn","bound-fn*","bound?","bounded-count","butlast","byte","byte-array","bytes","bytes?","case","cast","cat","catch","char","char-array","char-escape-string","char-name-string","char?","chars","chunk","chunk-append","chunk-buffer","chunk-cons","chunk-first","chunk-next","chunk-rest","chunked-seq?","class","class?","clear-agent-errors","clojure-version","coll?","comment","commute","comp","comparator","compare","compare-and-set!","compile","complement","completing","concat","cond","cond->","cond->>","condp","conj!","conj","cons","constantly","construct-proxy","contains?","count","counted?","create-ns","create-struct","cycle","dec","dec'","decimal?","declare","dedupe","def","default-data-readers","definline","definterface","defmacro","defmethod","defmulti","defn","defn-","defonce","defprotocol","defrecord","defstruct","deftype","delay","delay?","deliver","denominator","deref","derive","descendants","destructure","disj!","disj","dissoc!","dissoc","distinct","distinct?","do","doall","dorun","doseq","dosync","dotimes","doto","double","double-array","double?","doubles","drop","drop-last","drop-while","eduction","empty","empty?","ensure","ensure-reduced","enumeration-seq","error-handler","error-mode","eval","even?","every-pred","every?","ex-cause","ex-data","ex-info","ex-message","extend","extend-protocol","extend-type","extenders","extends?","false?","ffirst","file-seq","filter","filterv","finally","find","find-keyword","find-ns","find-protocol-impl","find-protocol-method","find-var","first","flatten","float","float-array","float?","floats","flush","fn","fn","fn?","fnext","fnil","for","force","format","frequencies","future","future-call","future-cancel","future-cancelled?","future-done?","future?","gen-class","gen-interface","gensym","get","get-in","get-method","get-proxy-class","get-thread-bindings","get-validator","group-by","halt-when","hash","hash-combine","hash-map","hash-ordered-coll","hash-set","hash-unordered-coll","ident?","identical?","identity","if","if-let","if-not","if-some","ifn?","import","in-ns","inc","inc'","indexed?","init-proxy","inst-ms","inst-ms*","inst?","instance?","int","int-array","int?","integer?","interleave","intern","interpose","into","into-array","ints","io!","isa?","iterate","iterator-seq","juxt","keep","keep-indexed","key","keys","keyword","keyword?","last","lazy-cat","lazy-seq","let","let","letfn","line-seq","list","list*","list?","load","load-file","load-reader","load-string","loaded-libs","locking","long","long-array","longs","loop","loop","macroexpand","macroexpand-1","make-array","make-hierarchy","map","map-entry?","map-indexed","map?","mapcat","mapv","max","max-key","memfn","memoize","merge","merge-with","meta","method-sig","methods","min","min-key","mix-collection-hash","mod","monitor-enter","monitor-exit","munge","name","namespace","namespace-munge","nat-int?","neg-int?","neg?","new","newline","next","nfirst","nil?","nnext","not","not-any?","not-empty","not-every?","not=","ns","ns-aliases","ns-imports","ns-interns","ns-map","ns-name","ns-publics","ns-refers","ns-resolve","ns-unalias","ns-unmap","nth","nthnext","nthrest","num","number?","numerator","object-array","odd?","or","parents","partial","partition","partition-all","partition-by","pcalls","peek","persistent!","pmap","pop!","pop","pop-thread-bindings","pos-int?","pos?","pr","pr-str","prefer-method","prefers","primitives-classnames","print","print-ctor","print-dup","print-method","print-simple","print-str","printf","println","println-str","prn","prn-str","promise","proxy","proxy-call-with-super","proxy-mappings","proxy-name","proxy-super","push-thread-bindings","pvalues","qualified-ident?","qualified-keyword?","qualified-symbol?","quot","quote","rand","rand-int","rand-nth","random-sample","range","ratio?","rational?","rationalize","re-find","re-groups","re-matcher","re-matches","re-pattern","re-seq","read","read+string","read-line","read-string","reader-conditional","reader-conditional?","realized?","record?","recur","reduce","reduce-kv","reduced","reduced?","reductions","ref","ref-history-count","ref-max-history","ref-min-history","ref-set","refer","refer-clojure","reify","release-pending-sends","rem","remove","remove-all-methods","remove-method","remove-ns","remove-tap","remove-watch","repeat","repeatedly","replace","replicate","require","requiring-resolve","reset!","reset-meta!","reset-vals!","resolve","rest","restart-agent","resultset-seq","reverse","reversible?","rseq","rsubseq","run!","satisfies?","second","select-keys","send","send-off","send-via","seq","seq?","seqable?","seque","sequence","sequential?","set!","set","set-agent-send-executor!","set-agent-send-off-executor!","set-error-handler!","set-error-mode!","set-validator!","set?","short","short-array","shorts","shuffle","shutdown-agents","simple-ident?","simple-keyword?","simple-symbol?","slurp","some","some->","some->>","some-fn","some?","sort","sort-by","sorted-map","sorted-map-by","sorted-set","sorted-set-by","sorted?","special-symbol?","spit","split-at","split-with","str","string?","struct","struct-map","subs","subseq","subvec","supers","swap!","swap-vals!","symbol","symbol?","sync","tagged-literal","tagged-literal?","take","take-last","take-nth","take-while","tap>","test","the-ns","thread-bound?","throw","time","to-array","to-array-2d","trampoline","transduce","transient","tree-seq","true?","try","type","unchecked-add","unchecked-add-int","unchecked-byte","unchecked-char","unchecked-dec","unchecked-dec-int","unchecked-divide-int","unchecked-double","unchecked-float","unchecked-inc","unchecked-inc-int","unchecked-int","unchecked-long","unchecked-multiply","unchecked-multiply-int","unchecked-negate","unchecked-negate-int","unchecked-remainder-int","unchecked-short","unchecked-subtract","unchecked-subtract-int","underive","unquote","unquote-splicing","unreduced","unsigned-bit-shift-right","update","update-in","update-proxy","uri?","use","uuid?","val","vals","var","var-get","var-set","var?","vary-meta","vec","vector","vector-of","vector?","volatile!","volatile?","vreset!","vswap!","when","when-first","when-let","when-not","when-some","while","with-bindings","with-bindings*","with-in-str","with-loading-context","with-local-vars","with-meta","with-open","with-out-str","with-precision","with-redefs","with-redefs-fn","xml-seq","zero?","zipmap"]
+" Generated from https://github.com/clojure-vim/clojure.vim/blob/fd280e33e84c88e97860930557dba3ff80b1a82d/clj/src/vim_clojure_static/generate.clj
+" Clojure version 1.11.0
+let s:words = ["&","*","*'","*1","*2","*3","*agent*","*allow-unresolved-vars*","*assert*","*clojure-version*","*command-line-args*","*compile-files*","*compile-path*","*compiler-options*","*data-readers*","*default-data-reader-fn*","*e","*err*","*file*","*flush-on-newline*","*fn-loader*","*in*","*math-context*","*ns*","*out*","*print-dup*","*print-length*","*print-level*","*print-meta*","*print-namespace-maps*","*print-readably*","*read-eval*","*reader-resolver*","*source-path*","*suppress-read*","*unchecked-math*","*use-context-classloader*","*verbose-defrecords*","*warn-on-reflection*","+","+'","-","-'","->","->>","->ArrayChunk","->Eduction","->Vec","->VecNode","->VecSeq","-cache-protocol-fn","-reset-methods",".","..","/","<","<=","=","==",">",">=","EMPTY-NODE","Inst","NaN?","PrintWriter-on","StackTraceElement->vec","Throwable->map","abs","accessor","aclone","add-classpath","add-tap","add-watch","agent","agent-error","agent-errors","aget","alength","alias","all-ns","alter","alter-meta!","alter-var-root","amap","ancestors","and","any?","apply","areduce","array-map","as->","aset","aset-boolean","aset-byte","aset-char","aset-double","aset-float","aset-int","aset-long","aset-short","assert","assoc","assoc!","assoc-in","associative?","atom","await","await-for","await1","bases","bean","bigdec","bigint","biginteger","binding","bit-and","bit-and-not","bit-clear","bit-flip","bit-not","bit-or","bit-set","bit-shift-left","bit-shift-right","bit-test","bit-xor","boolean","boolean-array","boolean?","booleans","bound-fn","bound-fn*","bound?","bounded-count","butlast","byte","byte-array","bytes","bytes?","case","case*","cast","cat","catch","char","char-array","char-escape-string","char-name-string","char?","chars","chunk","chunk-append","chunk-buffer","chunk-cons","chunk-first","chunk-next","chunk-rest","chunked-seq?","class","class?","clear-agent-errors","clojure-version","coll?","comment","commute","comp","comparator","compare","compare-and-set!","compile","complement","completing","concat","cond","cond->","cond->>","condp","conj","conj!","cons","constantly","construct-proxy","contains?","count","counted?","create-ns","create-struct","cycle","dec","dec'","decimal?","declare","dedupe","def","default-data-readers","definline","definterface","defmacro","defmethod","defmulti","defn","defn-","defonce","defprotocol","defrecord","defstruct","deftype","deftype*","delay","delay?","deliver","denominator","deref","derive","descendants","destructure","disj","disj!","dissoc","dissoc!","distinct","distinct?","do","doall","dorun","doseq","dosync","dotimes","doto","double","double-array","double?","doubles","drop","drop-last","drop-while","eduction","empty","empty?","ensure","ensure-reduced","enumeration-seq","error-handler","error-mode","eval","even?","every-pred","every?","ex-cause","ex-data","ex-info","ex-message","extend","extend-protocol","extend-type","extenders","extends?","false","false?","ffirst","file-seq","filter","filterv","finally","find","find-keyword","find-ns","find-protocol-impl","find-protocol-method","find-var","first","flatten","float","float-array","float?","floats","flush","fn","fn*","fn?","fnext","fnil","for","force","format","frequencies","future","future-call","future-cancel","future-cancelled?","future-done?","future?","gen-class","gen-interface","gensym","get","get-in","get-method","get-proxy-class","get-thread-bindings","get-validator","group-by","halt-when","hash","hash-combine","hash-map","hash-ordered-coll","hash-set","hash-unordered-coll","ident?","identical?","identity","if","if-let","if-not","if-some","ifn?","import","in-ns","inc","inc'","indexed?","infinite?","init-proxy","inst-ms","inst-ms*","inst?","instance?","int","int-array","int?","integer?","interleave","intern","interpose","into","into-array","ints","io!","isa?","iterate","iteration","iterator-seq","juxt","keep","keep-indexed","key","keys","keyword","keyword?","last","lazy-cat","lazy-seq","let","let*","letfn","letfn*","line-seq","list","list*","list?","load","load-file","load-reader","load-string","loaded-libs","locking","long","long-array","longs","loop","loop*","macroexpand","macroexpand-1","make-array","make-hierarchy","map","map-entry?","map-indexed","map?","mapcat","mapv","max","max-key","memfn","memoize","merge","merge-with","meta","method-sig","methods","min","min-key","mix-collection-hash","mod","monitor-enter","monitor-exit","munge","name","namespace","namespace-munge","nat-int?","neg-int?","neg?","new","newline","next","nfirst","nil","nil?","nnext","not","not-any?","not-empty","not-every?","not=","ns","ns-aliases","ns-imports","ns-interns","ns-map","ns-name","ns-publics","ns-refers","ns-resolve","ns-unalias","ns-unmap","nth","nthnext","nthrest","num","number?","numerator","object-array","odd?","or","parents","parse-boolean","parse-double","parse-long","parse-uuid","partial","partition","partition-all","partition-by","pcalls","peek","persistent!","pmap","pop","pop!","pop-thread-bindings","pos-int?","pos?","pr","pr-str","prefer-method","prefers","primitives-classnames","print","print-ctor","print-dup","print-method","print-simple","print-str","printf","println","println-str","prn","prn-str","promise","proxy","proxy-call-with-super","proxy-mappings","proxy-name","proxy-super","push-thread-bindings","pvalues","qualified-ident?","qualified-keyword?","qualified-symbol?","quot","quote","rand","rand-int","rand-nth","random-sample","random-uuid","range","ratio?","rational?","rationalize","re-find","re-groups","re-matcher","re-matches","re-pattern","re-seq","read","read+string","read-line","read-string","reader-conditional","reader-conditional?","realized?","record?","recur","reduce","reduce-kv","reduced","reduced?","reductions","ref","ref-history-count","ref-max-history","ref-min-history","ref-set","refer","refer-clojure","reify","reify*","release-pending-sends","rem","remove","remove-all-methods","remove-method","remove-ns","remove-tap","remove-watch","repeat","repeatedly","replace","replicate","require","requiring-resolve","reset!","reset-meta!","reset-vals!","resolve","rest","restart-agent","resultset-seq","reverse","reversible?","rseq","rsubseq","run!","satisfies?","second","select-keys","send","send-off","send-via","seq","seq-to-map-for-destructuring","seq?","seqable?","seque","sequence","sequential?","set","set!","set-agent-send-executor!","set-agent-send-off-executor!","set-error-handler!","set-error-mode!","set-validator!","set?","short","short-array","shorts","shuffle","shutdown-agents","simple-ident?","simple-keyword?","simple-symbol?","slurp","some","some->","some->>","some-fn","some?","sort","sort-by","sorted-map","sorted-map-by","sorted-set","sorted-set-by","sorted?","special-symbol?","spit","split-at","split-with","str","string?","struct","struct-map","subs","subseq","subvec","supers","swap!","swap-vals!","symbol","symbol?","sync","tagged-literal","tagged-literal?","take","take-last","take-nth","take-while","tap>","test","the-ns","thread-bound?","throw","time","to-array","to-array-2d","trampoline","transduce","transient","tree-seq","true","true?","try","type","unchecked-add","unchecked-add-int","unchecked-byte","unchecked-char","unchecked-dec","unchecked-dec-int","unchecked-divide-int","unchecked-double","unchecked-float","unchecked-inc","unchecked-inc-int","unchecked-int","unchecked-long","unchecked-multiply","unchecked-multiply-int","unchecked-negate","unchecked-negate-int","unchecked-remainder-int","unchecked-short","unchecked-subtract","unchecked-subtract-int","underive","unquote","unquote-splicing","unreduced","unsigned-bit-shift-right","update","update-in","update-keys","update-proxy","update-vals","uri?","use","uuid?","val","vals","var","var-get","var-set","var?","vary-meta","vec","vector","vector-of","vector?","volatile!","volatile?","vreset!","vswap!","when","when-first","when-let","when-not","when-some","while","with-bindings","with-bindings*","with-in-str","with-loading-context","with-local-vars","with-meta","with-open","with-out-str","with-precision","with-redefs","with-redefs-fn","xml-seq","zero?","zipmap"]
" Simple word completion for special forms and public vars in clojure.core
function! clojurecomplete#Complete(findstart, base)
diff --git a/runtime/autoload/dist/ft.vim b/runtime/autoload/dist/ft.vim
index 342731b272..9e30ae1f51 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 Apr 13
" These functions are moved here from runtime/filetype.vim to make startup
" faster.
@@ -67,14 +67,41 @@ 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\)'
- setf vb
- else
- exe "setf " . a:alt
+let s:ft_visual_basic_content = '\cVB_Name\|Begin VB\.\(Form\|MDIForm\|UserControl\)'
+
+" See FTfrm() for Visual Basic form file detection
+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*\%(' ..
+ \ '#\s*\a\+\|' ..
+ \ 'option\s\+\%(byval\|dynamic\|escape\|\%(no\)\=gosub\|nokeyword\|private\|static\)\>\|' ..
+ \ '\%(''\|rem\)\s*\$lang\>\|' ..
+ \ 'def\%(byte\|longint\|short\|ubyte\|uint\|ulongint\|ushort\)\>' ..
+ \ '\)'
+ 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\)\>\)'
+
+ for lnum in range(1, min([line("$"), 100]))
+ let line = getline(lnum)
+ if line =~ s:ft_visual_basic_content
+ setf vb
+ return
+ elseif line =~ fb_preproc || line =~ fb_comment || line =~ fb_keywords
+ setf freebasic
+ return
+ elseif line =~ qb64_preproc
+ setf qb64
+ return
+ endif
+ endfor
+ setf basic
endfunc
func dist#ft#FTbtm()
@@ -93,6 +120,42 @@ func dist#ft#BindzoneCheck(default)
endif
endfunc
+" Returns true if file content looks like RAPID
+func IsRapid(sChkExt = "")
+ if a:sChkExt == "cfg"
+ return getline(1) =~? '\v^%(EIO|MMC|MOC|PROC|SIO|SYS):CFG'
+ endif
+ " called from FTmod, FTprg or FTsys
+ return getline(nextnonblank(1)) =~? '\v^\s*%(\%{3}|module\s+\k+\s*%(\(|$))'
+endfunc
+
+func dist#ft#FTcfg()
+ if exists("g:filetype_cfg")
+ exe "setf " .. g:filetype_cfg
+ elseif IsRapid("cfg")
+ setf rapid
+ else
+ setf cfg
+ endif
+endfunc
+
+func dist#ft#FTcls()
+ if exists("g:filetype_cls")
+ exe "setf " .. g:filetype_cls
+ return
+ endif
+
+ if getline(1) =~ '^%'
+ setf tex
+ elseif getline(1)[0] == '#' && getline(1) =~ 'rexx'
+ setf rexx
+ elseif getline(1) == 'VERSION 1.0 CLASS'
+ setf vb
+ else
+ setf st
+ endif
+endfunc
+
func dist#ft#FTlpc()
if exists("g:lpc_syntax_for_c")
let lnum = 1
@@ -154,7 +217,7 @@ endfunc
func dist#ft#FTent()
" This function checks for valid cl syntax in the first five lines.
- " Look for either an opening comment, '#', or a block start, '{".
+ " Look for either an opening comment, '#', or a block start, '{'.
" If not found, assume SGML.
let lnum = 1
while lnum < 6
@@ -192,6 +255,10 @@ func dist#ft#EuphoriaCheck()
endfunc
func dist#ft#DtraceCheck()
+ if did_filetype()
+ " Filetype was already detected
+ return
+ endif
let lines = getline(1, min([line("$"), 100]))
if match(lines, '^module\>\|^import\>') > -1
" D files often start with a module and/or import statement.
@@ -219,6 +286,21 @@ func dist#ft#FTe()
endif
endfunc
+func dist#ft#FTfrm()
+ if exists("g:filetype_frm")
+ exe "setf " . g:filetype_frm
+ return
+ endif
+
+ let lines = getline(1, min([line("$"), 5]))
+
+ if match(lines, s:ft_visual_basic_content) > -1
+ setf vb
+ else
+ setf form
+ endif
+endfunc
+
" Distinguish between Forth and F#.
" Provided by Doug Kearns.
func dist#ft#FTfs()
@@ -377,6 +459,36 @@ func dist#ft#FTmm()
setf nroff
endfunc
+" Returns true if file content looks like LambdaProlog module
+func IsLProlog()
+ " skip apparent comments and blank lines, what looks like
+ " LambdaProlog comment may be RAPID header
+ let l = nextnonblank(1)
+ while l > 0 && l < line('$') && getline(l) =~ '^\s*%' " LambdaProlog comment
+ let l = nextnonblank(l + 1)
+ endwhile
+ " this pattern must not catch a go.mod file
+ return getline(l) =~ '\<module\s\+\w\+\s*\.\s*\(%\|$\)'
+endfunc
+
+" Determine if *.mod is ABB RAPID, LambdaProlog, Modula-2, Modsim III or go.mod
+func dist#ft#FTmod()
+ if exists("g:filetype_mod")
+ exe "setf " .. g:filetype_mod
+ elseif IsLProlog()
+ setf lprolog
+ elseif getline(nextnonblank(1)) =~ '\%(\<MODULE\s\+\w\+\s*;\|^\s*(\*\)'
+ setf modula2
+ elseif IsRapid()
+ setf rapid
+ elseif expand("<afile>") =~ '\<go.mod$'
+ setf gomod
+ else
+ " Nothing recognized, assume modsim3
+ setf modsim3
+ endif
+endfunc
+
func dist#ft#FTpl()
if exists("g:filetype_pl")
exe "setf " . g:filetype_pl
@@ -407,12 +519,14 @@ func dist#ft#FTinc()
" headers so assume POV-Ray
elseif lines =~ '^\s*\%({\|(\*\)' || lines =~? s:ft_pascal_keywords
setf pascal
+ elseif lines =~# '\<\%(require\|inherit\)\>' || lines =~# '\w\+ = '
+ setf bitbake
else
call dist#ft#FTasmsyntax()
if exists("b:asmsyntax")
- exe "setf " . fnameescape(b:asmsyntax)
+ exe "setf " . fnameescape(b:asmsyntax)
else
- setf pov
+ setf pov
endif
endif
endif
@@ -493,6 +607,18 @@ func dist#ft#FTpp()
endif
endfunc
+" Determine if *.prg is ABB RAPID. Can also be Clipper, FoxPro or eviews
+func dist#ft#FTprg()
+ if exists("g:filetype_prg")
+ exe "setf " .. g:filetype_prg
+ elseif IsRapid()
+ setf rapid
+ else
+ " Nothing recognized, assume Clipper
+ setf clipper
+ endif
+endfunc
+
func dist#ft#FTr()
let max = line("$") > 50 ? 50 : line("$")
@@ -674,6 +800,28 @@ func dist#ft#SQL()
endif
endfunc
+" This function checks the first 25 lines of file extension "sc" to resolve
+" detection between scala and SuperCollider
+func dist#ft#FTsc()
+ for lnum in range(1, min([line("$"), 25]))
+ if getline(lnum) =~# '[A-Za-z0-9]*\s:\s[A-Za-z0-9]\|var\s<\|classvar\s<\|\^this.*\||\w*|\|+\s\w*\s{\|\*ar\s'
+ setf supercollider
+ return
+ endif
+ endfor
+ setf scala
+endfunc
+
+" This function checks the first line of file extension "scd" to resolve
+" detection between scdoc and SuperCollider
+func dist#ft#FTscd()
+ if getline(1) =~# '\%^\S\+(\d[0-9A-Za-z]*)\%(\s\+\"[^"]*\"\%(\s\+\"[^"]*\"\)\=\)\=$'
+ setf scdoc
+ else
+ setf supercollider
+ endif
+endfunc
+
" If the file has an extension of 't' and is in a directory 't' or 'xt' then
" it is almost certainly a Perl test file.
" If the first line starts with '#' and contains 'perl' it's probably a Perl
@@ -692,7 +840,7 @@ func dist#ft#FTperl()
endif
let save_cursor = getpos('.')
call cursor(1,1)
- let has_use = search('^use\s\s*\k', 'c', 30)
+ let has_use = search('^use\s\s*\k', 'c', 30) > 0
call setpos('.', save_cursor)
if has_use
setf perl
@@ -701,6 +849,37 @@ func dist#ft#FTperl()
return 0
endfunc
+" LambdaProlog and Standard ML signature files
+func dist#ft#FTsig()
+ if exists("g:filetype_sig")
+ exe "setf " .. g:filetype_sig
+ return
+ endif
+
+ let lprolog_comment = '^\s*\%(/\*\|%\)'
+ let lprolog_keyword = '^\s*sig\s\+\a'
+ let sml_comment = '^\s*(\*'
+ let sml_keyword = '^\s*\%(signature\|structure\)\s\+\a'
+
+ let line = getline(nextnonblank(1))
+
+ if line =~ lprolog_comment || line =~# lprolog_keyword
+ setf lprolog
+ elseif line =~ sml_comment || line =~# sml_keyword
+ setf sml
+ endif
+endfunc
+
+func dist#ft#FTsys()
+ if exists("g:filetype_sys")
+ exe "setf " .. g:filetype_sys
+ elseif IsRapid()
+ setf rapid
+ else
+ setf bat
+ endif
+endfunc
+
" Choose context, plaintex, or tex (LaTeX) based on these rules:
" 1. Check the first line of the file for "%&<format>".
" 2. Check the first 1000 non-comment lines for LaTeX or ConTeXt keywords.
@@ -724,7 +903,8 @@ func dist#ft#FTtex()
let save_cursor = getpos('.')
call cursor(1,1)
let firstNC = search('^\s*[^[:space:]%]', 'c', 1000)
- if firstNC " Check the next thousand lines for a LaTeX or ConTeXt keyword.
+ if firstNC > 0
+ " Check the next thousand lines for a LaTeX or ConTeXt keyword.
let lpat = 'documentclass\>\|usepackage\>\|begin{\|newcommand\>\|renewcommand\>'
let cpat = 'start\a\+\|setup\a\+\|usemodule\|enablemode\|enableregime\|setvariables\|useencoding\|usesymbols\|stelle\a\+\|verwende\a\+\|stel\a\+\|gebruik\a\+\|usa\a\+\|imposta\a\+\|regle\a\+\|utilisemodule\>'
let kwline = search('^\s*\\\%(' . lpat . '\)\|^\s*\\\(' . cpat . '\)',
@@ -846,6 +1026,41 @@ func dist#ft#FTfoam()
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
+
+let s:ft_krl_header = '\&\w+'
+" Determine if a *.src file is Kuka Robot Language
+func dist#ft#FTsrc()
+ let ft_krl_def_or_deffct = '%(global\s+)?def%(fct)?>'
+ if exists("g:filetype_src")
+ exe "setf " .. g:filetype_src
+ elseif getline(nextnonblank(1)) =~? '\v^\s*%(' .. s:ft_krl_header .. '|' .. ft_krl_def_or_deffct .. ')'
+ setf krl
+ endif
+endfunc
+
+" Determine if a *.dat file is Kuka Robot Language
+func dist#ft#FTdat()
+ let ft_krl_defdat = 'defdat>'
+ if exists("g:filetype_dat")
+ exe "setf " .. g:filetype_dat
+ elseif getline(nextnonblank(1)) =~? '\v^\s*%(' .. s:ft_krl_header .. '|' .. ft_krl_defdat .. ')'
+ setf krl
+ endif
+endfunc
+
" Restore 'cpoptions'
let &cpo = s:cpo_save
unlet s:cpo_save
diff --git a/runtime/autoload/freebasic.vim b/runtime/autoload/freebasic.vim
new file mode 100644
index 0000000000..6c94cd34ea
--- /dev/null
+++ b/runtime/autoload/freebasic.vim
@@ -0,0 +1,40 @@
+" Vim filetype plugin file
+" Language: FreeBASIC
+" Maintainer: Doug Kearns <dougkearns@gmail.com>
+" Last Change: 2022 June 24
+
+" Dialects can be one of fb, qb, fblite, or deprecated
+" Precedence is forcelang > #lang > lang
+function! freebasic#GetDialect() abort
+ if exists("g:freebasic_forcelang")
+ return g:freebasic_forcelang
+ endif
+
+ if exists("g:freebasic_lang")
+ let dialect = g:freebasic_lang
+ else
+ let dialect = "fb"
+ endif
+
+ " override with #lang directive or metacommand
+
+ let pat = '\c^\s*\%(#\s*lang\s\+\|''\s*$lang\s*:\s*\)"\([^"]*\)"'
+
+ let save_cursor = getcurpos()
+ call cursor(1, 1)
+ let lnum = search(pat, 'cn')
+ call setpos('.', save_cursor)
+
+ if lnum
+ let word = matchlist(getline(lnum), pat)[1]
+ if word =~? '\<\%(fb\|deprecated\|fblite\|qb\)\>'
+ let dialect = word
+ else
+ echomsg "freebasic#GetDialect: Invalid lang, found '" .. word .. "' at line " .. lnum .. " " .. getline(lnum)
+ endif
+ endif
+
+ return dialect
+endfunction
+
+" vim: nowrap sw=2 sts=2 ts=8 noet fdm=marker:
diff --git a/runtime/autoload/health.vim b/runtime/autoload/health.vim
index 1d462ad02c..a693868381 100644
--- a/runtime/autoload/health.vim
+++ b/runtime/autoload/health.vim
@@ -21,10 +21,17 @@ function! health#check(plugin_names) abort
throw 'healthcheck_not_found'
endif
eval type == 'v' ? call(func, []) : luaeval(func)
+ " in the event the healthcheck doesn't return anything
+ " (the plugin author should avoid this possibility)
+ if len(s:output) == 0
+ throw 'healthcheck_no_return_value'
+ endif
catch
let s:output = [] " Clear the output
if v:exception =~# 'healthcheck_not_found'
call health#report_error('No healthcheck found for "'.name.'" plugin.')
+ elseif v:exception =~# 'healthcheck_no_return_value'
+ call health#report_error('The healthcheck report for "'.name.'" plugin is empty.')
else
call health#report_error(printf(
\ "Failed to run healthcheck for \"%s\" plugin. Exception:\n%s\n%s",
@@ -127,7 +134,7 @@ endfunction " }}}
" From a path return a list [{name}, {func}, {type}] representing a healthcheck
function! s:filepath_to_healthcheck(path) abort
- if a:path =~# 'vim$'
+ if a:path =~# 'vim$'
let name = matchstr(a:path, '\zs[^\/]*\ze\.vim$')
let func = 'health#'.name.'#check'
let type = 'v'
@@ -164,6 +171,11 @@ function! s:get_healthcheck(plugin_names) abort
for v in values(healthchecks)
let output[v[0]] = v[1:]
endfor
+ try
+ " vim.health is not a healthcheck, skip it
+ call remove(output, 'vim')
+ catch
+ endtry
return output
endfunction
@@ -181,7 +193,7 @@ function! s:get_healthcheck_list(plugin_names) abort
\ + nvim_get_runtime_file('lua/**/'.p.'/health/init.lua', v:true)
\ + nvim_get_runtime_file('lua/**/'.p.'/health.lua', v:true)
if len(paths) == 0
- let healthchecks += [[p, '', '']] " healthchek not found
+ let healthchecks += [[p, '', '']] " healthcheck not found
else
let healthchecks += map(uniq(sort(paths)),
\'<SID>filepath_to_healthcheck(v:val)')
diff --git a/runtime/autoload/health/nvim.vim b/runtime/autoload/health/nvim.vim
index 0bb343e198..9b387095ee 100644
--- a/runtime/autoload/health/nvim.vim
+++ b/runtime/autoload/health/nvim.vim
@@ -45,7 +45,7 @@ function! s:check_config() abort
let shadafile = empty(&shada) ? &shada : substitute(matchstr(
\ split(&shada, ',')[-1], '^n.\+'), '^n', '', '')
let shadafile = empty(&shadafile) ? empty(shadafile) ?
- \ stdpath('data').'/shada/main.shada' : expand(shadafile)
+ \ stdpath('state').'/shada/main.shada' : expand(shadafile)
\ : &shadafile ==# 'NONE' ? '' : &shadafile
if !empty(shadafile) && empty(glob(shadafile))
" Since this may be the first time neovim has been run, we will try to
@@ -104,8 +104,8 @@ function! s:check_rplugin_manifest() abort
if !has_key(existing_rplugins, script)
let msg = printf('"%s" is not registered.', fnamemodify(path, ':t'))
if python_version ==# 'pythonx'
- if !has('python2') && !has('python3')
- let msg .= ' (python2 and python3 not available)'
+ if !has('python3')
+ let msg .= ' (python3 not available)'
endif
elseif !has(python_version)
let msg .= printf(' (%s not available)', python_version)
@@ -144,18 +144,28 @@ function! s:check_performance() abort
\ ['Install a different Nvim package, or rebuild with `CMAKE_BUILD_TYPE=RelWithDebInfo`.',
\ s:suggest_faq])
endif
+
+ " check for slow shell invocation
+ let slow_cmd_time = 1.5
+ let start_time = reltime()
+ call system('echo')
+ let elapsed_time = reltimefloat(reltime(start_time))
+ if elapsed_time > slow_cmd_time
+ call health#report_warn(
+ \ 'Slow shell invocation (took '.printf('%.2f', elapsed_time).' seconds).')
+ endif
endfunction
function! s:get_tmux_option(option) abort
let cmd = 'tmux show-option -qvg '.a:option " try global scope
- let out = system(cmd)
+ let out = system(split(cmd))
let val = substitute(out, '\v(\s|\r|\n)', '', 'g')
if v:shell_error
call health#report_error('command failed: '.cmd."\n".out)
return 'error'
elseif empty(val)
let cmd = 'tmux show-option -qvgs '.a:option " try session scope
- let out = system(cmd)
+ let out = system(split(cmd))
let val = substitute(out, '\v(\s|\r|\n)', '', 'g')
if v:shell_error
call health#report_error('command failed: '.cmd."\n".out)
@@ -202,11 +212,11 @@ function! s:check_tmux() abort
" check default-terminal and $TERM
call health#report_info('$TERM: '.$TERM)
let cmd = 'tmux show-option -qvg default-terminal'
- let out = system(cmd)
+ let out = system(split(cmd))
let tmux_default_term = substitute(out, '\v(\s|\r|\n)', '', 'g')
if empty(tmux_default_term)
let cmd = 'tmux show-option -qvgs default-terminal'
- let out = system(cmd)
+ let out = system(split(cmd))
let tmux_default_term = substitute(out, '\v(\s|\r|\n)', '', 'g')
endif
@@ -225,7 +235,7 @@ function! s:check_tmux() abort
endif
" check for RGB capabilities
- let info = system('tmux server-info')
+ let info = system(['tmux', 'server-info'])
let has_tc = stridx(info, " Tc: (flag) true") != -1
let has_rgb = stridx(info, " RGB: (flag) true") != -1
if !has_tc && !has_rgb
@@ -242,7 +252,7 @@ function! s:check_terminal() abort
endif
call health#report_start('terminal')
let cmd = 'infocmp -L'
- let out = system(cmd)
+ let out = system(split(cmd))
let kbs_entry = matchstr(out, 'key_backspace=[^,[:space:]]*')
let kdch1_entry = matchstr(out, 'key_dc=[^,[:space:]]*')
diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim
index e6523aa215..5cda7cfd03 100644
--- a/runtime/autoload/health/provider.vim
+++ b/runtime/autoload/health/provider.vim
@@ -203,7 +203,7 @@ function! s:version_info(python) abort
let nvim_path = s:trim(s:system([
\ a:python, '-c',
\ 'import sys; ' .
- \ 'sys.path = list(filter(lambda x: x != "", sys.path)); ' .
+ \ 'sys.path = [p for p in sys.path if p != ""]; ' .
\ 'import neovim; print(neovim.__file__)']))
if s:shell_error || empty(nvim_path)
return [python_version, 'unable to load neovim Python module', pypi_version,
@@ -282,10 +282,10 @@ function! s:disabled_via_loaded_var(provider) abort
return 0
endfunction
-function! s:check_python(version) abort
- call health#report_start('Python ' . a:version . ' provider (optional)')
+function! s:check_python() abort
+ call health#report_start('Python 3 provider (optional)')
- let pyname = 'python'.(a:version == 2 ? '' : '3')
+ let pyname = 'python3'
let python_exe = ''
let venv = exists('$VIRTUAL_ENV') ? resolve($VIRTUAL_ENV) : ''
let host_prog_var = pyname.'_host_prog'
@@ -301,7 +301,7 @@ function! s:check_python(version) abort
call health#report_info(printf('Using: g:%s = "%s"', host_prog_var, get(g:, host_prog_var)))
endif
- let [pyname, pythonx_errors] = provider#pythonx#Detect(a:version)
+ let [pyname, pythonx_warnings] = provider#pythonx#Detect(3)
if empty(pyname)
call health#report_warn('No Python executable found that can `import neovim`. '
@@ -311,8 +311,9 @@ function! s:check_python(version) abort
endif
" No Python executable could `import neovim`, or host_prog_var was used.
- if !empty(pythonx_errors)
- call health#report_error('Python provider error:', pythonx_errors)
+ if !empty(pythonx_warnings)
+ call health#report_warn(pythonx_warnings, ['See :help provider-python for more information.',
+ \ 'You may disable this provider (and warning) by adding `let g:loaded_python3_provider = 0` to your init.vim'])
elseif !empty(pyname) && empty(python_exe)
if !exists('g:'.host_prog_var)
@@ -405,7 +406,7 @@ function! s:check_python(version) abort
" can import 'pynvim'. If so, that Python failed to import 'neovim' as
" well, which is most probably due to a failed pip upgrade:
" https://github.com/neovim/neovim/wiki/Following-HEAD#20181118
- let [pynvim_exe, errors] = provider#pythonx#DetectByModule('pynvim', a:version)
+ let [pynvim_exe, errors] = provider#pythonx#DetectByModule('pynvim', 3)
if !empty(pynvim_exe)
call health#report_error(
\ 'Detected pip upgrade failure: Python executable can import "pynvim" but '
@@ -416,14 +417,14 @@ function! s:check_python(version) abort
\ . pynvim_exe ." -m pip install neovim # only if needed by third-party software")
endif
else
- let [pyversion, current, latest, status] = s:version_info(python_exe)
+ let [majorpyversion, current, latest, status] = s:version_info(python_exe)
- if a:version != str2nr(pyversion)
+ if 3 != str2nr(majorpyversion)
call health#report_warn('Unexpected Python version.' .
\ ' This could lead to confusing error messages.')
endif
- call health#report_info('Python version: ' . pyversion)
+ call health#report_info('Python version: ' . majorpyversion)
if s:is_bad_response(status)
call health#report_info(printf('pynvim version: %s (%s)', current, status))
@@ -565,7 +566,7 @@ function! s:check_ruby() abort
\ ['Install Ruby and verify that `ruby` and `gem` commands work.'])
return
endif
- call health#report_info('Ruby: '. s:system('ruby -v'))
+ call health#report_info('Ruby: '. s:system(['ruby', '-v']))
let [host, err] = provider#ruby#Detect()
if empty(host)
@@ -573,7 +574,8 @@ function! s:check_ruby() abort
\ ['Run `gem install neovim` to ensure the neovim RubyGem is installed.',
\ 'Run `gem environment` to ensure the gem bin directory is in $PATH.',
\ 'If you are using rvm/rbenv/chruby, try "rehashing".',
- \ 'See :help g:ruby_host_prog for non-standard gem installations.'])
+ \ 'See :help g:ruby_host_prog for non-standard gem installations.',
+ \ 'You may disable this provider (and warning) by adding `let g:loaded_ruby_provider = 0` to your init.vim'])
return
endif
call health#report_info('Host: '. host)
@@ -588,11 +590,11 @@ function! s:check_ruby() abort
endif
let latest_gem = get(split(latest_gem, 'neovim (\|, \|)$' ), 0, 'not found')
- let current_gem_cmd = host .' --version'
+ let current_gem_cmd = [host, '--version']
let current_gem = s:system(current_gem_cmd)
if s:shell_error
- call health#report_error('Failed to run: '. current_gem_cmd,
- \ ['Report this issue with the output of: ', current_gem_cmd])
+ call health#report_error('Failed to run: '. join(current_gem_cmd),
+ \ ['Report this issue with the output of: ', join(current_gem_cmd)])
return
endif
@@ -619,7 +621,7 @@ function! s:check_node() abort
\ ['Install Node.js and verify that `node` and `npm` (or `yarn`) commands work.'])
return
endif
- let node_v = get(split(s:system('node -v'), "\n"), 0, '')
+ let node_v = get(split(s:system(['node', '-v']), "\n"), 0, '')
call health#report_info('Node.js: '. node_v)
if s:shell_error || s:version_cmp(node_v[1:], '6.0.0') < 0
call health#report_warn('Nvim node.js host does not support '.node_v)
@@ -634,7 +636,8 @@ function! s:check_node() abort
if empty(host)
call health#report_warn('Missing "neovim" npm (or yarn) package.',
\ ['Run in shell: npm install -g neovim',
- \ 'Run in shell (if you use yarn): yarn global add neovim'])
+ \ 'Run in shell (if you use yarn): yarn global add neovim',
+ \ 'You may disable this provider (and warning) by adding `let g:loaded_node_provider = 0` to your init.vim'])
return
endif
call health#report_info('Nvim node.js host: '. host)
@@ -660,8 +663,8 @@ function! s:check_node() abort
let current_npm_cmd = ['node', host, '--version']
let current_npm = s:system(current_npm_cmd)
if s:shell_error
- call health#report_error('Failed to run: '. string(current_npm_cmd),
- \ ['Report this issue with the output of: ', string(current_npm_cmd)])
+ call health#report_error('Failed to run: '. join(current_npm_cmd),
+ \ ['Report this issue with the output of: ', join(current_npm_cmd)])
return
endif
@@ -683,14 +686,15 @@ function! s:check_perl() abort
return
endif
- let [perl_exec, perl_errors] = provider#perl#Detect()
+ let [perl_exec, perl_warnings] = provider#perl#Detect()
if empty(perl_exec)
- if !empty(perl_errors)
- call health#report_error('perl provider error:', perl_errors)
- else
+ if !empty(perl_warnings)
+ call health#report_warn(perl_warnings, ['See :help provider-perl for more information.',
+ \ 'You may disable this provider (and warning) by adding `let g:loaded_perl_provider = 0` to your init.vim'])
+ else
call health#report_warn('No usable perl executable found')
endif
- return
+ return
endif
call health#report_info('perl executable: '. perl_exec)
@@ -734,8 +738,8 @@ function! s:check_perl() abort
let current_cpan_cmd = [perl_exec, '-W', '-MNeovim::Ext', '-e', 'print $Neovim::Ext::VERSION']
let current_cpan = s:system(current_cpan_cmd)
if s:shell_error
- call health#report_error('Failed to run: '. string(current_cpan_cmd),
- \ ['Report this issue with the output of: ', string(current_cpan_cmd)])
+ call health#report_error('Failed to run: '. join(current_cpan_cmd),
+ \ ['Report this issue with the output of: ', join(current_cpan_cmd)])
return
endif
@@ -751,8 +755,7 @@ endfunction
function! health#provider#check() abort
call s:check_clipboard()
- call s:check_python(2)
- call s:check_python(3)
+ call s:check_python()
call s:check_virtualenv()
call s:check_ruby()
call s:check_node()
diff --git a/runtime/autoload/man.vim b/runtime/autoload/man.vim
index a954e1b1a3..b8a73a64c9 100644
--- a/runtime/autoload/man.vim
+++ b/runtime/autoload/man.vim
@@ -123,12 +123,10 @@ function! s:system(cmd, ...) abort
endfunction
function! s:set_options(pager) abort
- setlocal filetype=man
setlocal noswapfile buftype=nofile bufhidden=hide
setlocal nomodified readonly nomodifiable
- if a:pager
- nnoremap <silent> <buffer> <nowait> q :lclose<CR>:q<CR>
- endif
+ let b:pager = a:pager
+ setlocal filetype=man
endfunction
function! s:get_page(path) abort
@@ -173,6 +171,12 @@ function! man#show_toc() abort
while lnum && lnum < last_line
let text = getline(lnum)
if text =~# '^\%( \{3\}\)\=\S.*$'
+ " if text is a section title
+ call add(toc, {'bufnr': bufnr('%'), 'lnum': lnum, 'text': text})
+ elseif text =~# '^\s\+\%(+\|-\)\S\+'
+ " if text is a flag title. we strip whitespaces and prepend two
+ " spaces to have a consistent format in the loclist.
+ let text = ' ' .. substitute(text, '^\s*\(.\{-}\)\s*$', '\1', '')
call add(toc, {'bufnr': bufnr('%'), 'lnum': lnum, 'text': text})
endif
let lnum = nextnonblank(lnum + 1)
@@ -232,7 +236,11 @@ function! s:get_path(sect, name) abort
"
" 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.
- let results = split(s:system(['man', s:find_arg, a:sect, a:name]))
+ if empty(a:sect)
+ 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 ''
@@ -469,10 +477,6 @@ endfunction
" Called when Nvim is invoked as $MANPAGER.
function! man#init_pager() abort
- " https://github.com/neovim/neovim/issues/6828
- let og_modifiable = &modifiable
- setlocal modifiable
-
if getline(1) =~# '^\s*$'
silent keepjumps 1delete _
else
@@ -492,7 +496,6 @@ function! man#init_pager() abort
endif
call s:set_options(v:true)
- let &l:modifiable = og_modifiable
endfunction
function! man#goto_tag(pattern, flags, info) abort
diff --git a/runtime/autoload/provider/python.vim b/runtime/autoload/provider/python.vim
deleted file mode 100644
index 8a1d162784..0000000000
--- a/runtime/autoload/provider/python.vim
+++ /dev/null
@@ -1,45 +0,0 @@
-" The Python provider uses a Python host to emulate an environment for running
-" python-vim plugins. :help provider
-"
-" Associating the plugin with the Python host is the first step because plugins
-" will be passed as command-line arguments
-
-if exists('g:loaded_python_provider')
- finish
-endif
-let [s:prog, s:err] = provider#pythonx#Detect(2)
-let g:loaded_python_provider = empty(s:prog) ? 1 : 2
-
-function! provider#python#Prog() abort
- return s:prog
-endfunction
-
-function! provider#python#Error() abort
- return s:err
-endfunction
-
-" The Python provider plugin will run in a separate instance of the Python
-" host.
-call remote#host#RegisterClone('legacy-python-provider', 'python')
-call remote#host#RegisterPlugin('legacy-python-provider', 'script_host.py', [])
-
-function! provider#python#Call(method, args) abort
- if s:err != ''
- return
- endif
- if !exists('s:host')
- let s:rpcrequest = function('rpcrequest')
-
- " Ensure that we can load the Python host before bootstrapping
- try
- let s:host = remote#host#Require('legacy-python-provider')
- catch
- let s:err = v:exception
- echohl WarningMsg
- echomsg v:exception
- echohl None
- return
- endtry
- endif
- return call(s:rpcrequest, insert(insert(a:args, 'python_'.a:method), s:host))
-endfunction
diff --git a/runtime/autoload/provider/pythonx.vim b/runtime/autoload/provider/pythonx.vim
index 0eeb35cba8..6211b457d6 100644
--- a/runtime/autoload/provider/pythonx.vim
+++ b/runtime/autoload/provider/pythonx.vim
@@ -6,11 +6,9 @@ endif
let s:loaded_pythonx_provider = 1
function! provider#pythonx#Require(host) abort
- let ver = (a:host.orig_name ==# 'python') ? 2 : 3
-
" Python host arguments
- let prog = (ver == '2' ? provider#python#Prog() : provider#python3#Prog())
- let args = [prog, '-c', 'import sys; sys.path = list(filter(lambda x: x != "", sys.path)); import neovim; neovim.start_host()']
+ let prog = provider#python3#Prog()
+ let args = [prog, '-c', 'import sys; sys.path = [p for p in sys.path if p != ""]; import neovim; neovim.start_host()']
" Collect registered Python plugins into args
@@ -23,14 +21,12 @@ function! provider#pythonx#Require(host) abort
endfunction
function! s:get_python_executable_from_host_var(major_version) abort
- return expand(get(g:, 'python'.(a:major_version == 3 ? '3' : '').'_host_prog', ''), v:true)
+ return expand(get(g:, 'python'.(a:major_version == 3 ? '3' : execute("throw 'unsupported'")).'_host_prog', ''), v:true)
endfunction
function! s:get_python_candidates(major_version) abort
return {
- \ 2: ['python2', 'python2.7', 'python2.6', 'python'],
- \ 3: ['python3', 'python3.10', 'python3.9', 'python3.8', 'python3.7',
- \ 'python3.6', 'python']
+ \ 3: ['python3', 'python3.10', 'python3.9', 'python3.8', 'python3.7', 'python']
\ }[a:major_version]
endfunction
@@ -60,14 +56,14 @@ function! provider#pythonx#DetectByModule(module, major_version) abort
endfor
" No suitable Python executable found.
- return ['', 'provider/pythonx: Could not load Python '.a:major_version.":\n".join(errors, "\n")]
+ return ['', 'Could not load Python '.a:major_version.":\n".join(errors, "\n")]
endfunction
" Returns array: [prog_exitcode, prog_version]
function! s:import_module(prog, module) abort
let prog_version = system([a:prog, '-c' , printf(
\ 'import sys; ' .
- \ 'sys.path = list(filter(lambda x: x != "", sys.path)); ' .
+ \ 'sys.path = [p for p in sys.path if p != ""]; ' .
\ 'sys.stdout.write(str(sys.version_info[0]) + "." + str(sys.version_info[1])); ' .
\ 'import pkgutil; ' .
\ 'exit(2*int(pkgutil.get_loader("%s") is None))',
@@ -82,7 +78,7 @@ function! provider#pythonx#CheckForModule(prog, module, major_version) abort
return [0, a:prog . ' not found in search path or not executable.']
endif
- let min_version = (a:major_version == 2) ? '2.6' : '3.3'
+ let min_version = '3.7'
" Try to load module, and output Python version.
" Exit codes:
@@ -103,7 +99,7 @@ function! provider#pythonx#CheckForModule(prog, module, major_version) abort
endif
if prog_exitcode == 2
- return [0, prog_path.' does not have the "' . a:module . '" module. :help provider-python']
+ return [0, prog_path.' does not have the "' . a:module . '" module.']
elseif prog_exitcode == 127
" This can happen with pyenv's shims.
return [0, prog_path . ' does not exist: ' . prog_version]
diff --git a/runtime/autoload/python3complete.vim b/runtime/autoload/python3complete.vim
index 192e9e6df8..b0781864c5 100644
--- a/runtime/autoload/python3complete.vim
+++ b/runtime/autoload/python3complete.vim
@@ -2,7 +2,7 @@
" Maintainer: <vacancy>
" Previous Maintainer: Aaron Griffin <aaronmgriffin@gmail.com>
" Version: 0.9
-" Last Updated: 2020 Oct 9
+" Last Updated: 2022 Mar 30
"
" Roland Puntaier: this file contains adaptations for python3 and is parallel to pythoncomplete.vim
"
@@ -91,6 +91,9 @@ endfunction
function! s:DefPython()
py3 << PYTHONEOF
+import warnings
+warnings.simplefilter(action='ignore', category=FutureWarning)
+
import sys, tokenize, io, types
from token import NAME, DEDENT, NEWLINE, STRING
diff --git a/runtime/colors/README.txt b/runtime/colors/README.txt
index 70aacb5384..4ea8e5e640 100644
--- a/runtime/colors/README.txt
+++ b/runtime/colors/README.txt
@@ -1,21 +1,27 @@
README.txt for color scheme files
-These files are used for the ":colorscheme" command. They appear in the
-Edit/Color Scheme menu in the GUI.
+These files are used for the `:colorscheme` command. They appear in the
+"Edit/Color Scheme" menu in the GUI.
+
+The colorschemes were updated for the Vim 9 release. If you don't like the
+changes you can find the old ones here:
+https://github.com/vim/colorschemes/tree/master/legacy_colors
Hints for writing a color scheme file:
There are two basic ways to define a color scheme:
-1. Define a new Normal color and set the 'background' option accordingly.
+1. Define a new Normal color and set the 'background' option accordingly. >
+
set background={light or dark}
highlight clear
highlight Normal ...
...
2. Use the default Normal color and automatically adjust to the value of
- 'background'.
+ 'background'. >
+
highlight clear Normal
set background&
highlight clear
@@ -27,25 +33,28 @@ There are two basic ways to define a color scheme:
...
endif
-You can use ":highlight clear" to reset everything to the defaults, and then
-change the groups that you want differently. This also will work for groups
+You can use `:highlight clear` to reset everything to the defaults, and then
+change the groups that you want differently. This will also work for groups
that are added in later versions of Vim.
-Note that ":highlight clear" uses the value of 'background', thus set it
+Note that `:highlight clear` uses the value of 'background', thus set it
before this command.
Some attributes (e.g., bold) might be set in the defaults that you want
removed in your color scheme. Use something like "gui=NONE" to remove the
attributes.
In case you want to set 'background' depending on the colorscheme selected,
-this autocmd might be useful:
+this autocmd might be useful: >
+
autocmd SourcePre */colors/blue_sky.vim set background=dark
+
Replace "blue_sky" with the name of the colorscheme.
In case you want to tweak a colorscheme after it was loaded, check out the
ColorScheme autocommand event.
To clean up just before loading another colorscheme, use the ColorSchemePre
-autocommand event. For example:
+autocommand event. For example: >
+
let g:term_ansi_colors = ...
augroup MyColorscheme
au!
@@ -54,36 +63,83 @@ autocommand event. For example:
augroup END
To customize a colorscheme use another name, e.g. "~/.vim/colors/mine.vim",
-and use `:runtime` to load the original colorscheme:
+and use ":runtime" to load the original colorscheme: >
+
" load the "evening" colorscheme
runtime colors/evening.vim
" change the color of statements
hi Statement ctermfg=Blue guifg=Blue
-To see which highlight group is used where, find the help for
-"highlight-groups" and "group-name".
+To see which highlight group is used where, see `:help highlight-groups` and
+`:help group-name` .
You can use ":highlight" to find out the current colors. Exception: the
ctermfg and ctermbg values are numbers, which are only valid for the current
-terminal. Use the color names instead. See ":help cterm-colors".
+terminal. Use the color names instead for better portability. See
+`:help cterm-colors` .
-The default color settings can be found in the source file src/highlight.c.
-Search for "highlight_init".
+The default color settings can be found in the source file
+"src/nvim/highlight_group.c". Search for "highlight_init".
If you think you have a color scheme that is good enough to be used by others,
please check the following items:
-- Source the $VIMRUNTIME/tools/check_colors.vim script to check for common mistakes.
-- Does it work in a color terminal as well as in the GUI?
+- Source the $VIMRUNTIME/colors/tools/check_colors.vim script to check for
+ common mistakes.
+
+- Does it work in a color terminal as well as in the GUI? Is it consistent?
+
- Is "g:colors_name" set to a meaningful value? In case of doubt you can do
- it this way:
+ it this way: >
+
let g:colors_name = expand('<sfile>:t:r')
+
- Is 'background' either used or appropriately set to "light" or "dark"?
+
- Try setting 'hlsearch' and searching for a pattern, is the match easy to
spot?
+
- Split a window with ":split" and ":vsplit". Are the status lines and
vertical separators clearly visible?
+
- In the GUI, is it easy to find the cursor, also in a file with lots of
syntax highlighting?
+
+- In general, test your color scheme against as many filetypes, Vim features,
+ environments, etc. as possible.
+
- Do not use hard coded escape sequences, these will not work in other
- terminals. Always use color names or #RRGGBB for the GUI.
+ terminals. Always use #RRGGBB for the GUI.
+
+- When targetting 8-16 colors terminals, don't count on "darkblue" to be blue
+ and dark, or on "2" to be even vaguely reddish. Names are more portable
+ than numbers, though.
+
+- When targetting 256 colors terminals, prefer colors 16-255 to colors 0-15
+ for the same reason.
+
+- Typographic attributes (bold, italic, underline, reverse, etc.) are not
+ universally supported. Don't count on any of them.
+
+- Is "g:terminal_ansi_colors" set to a list of 16 #RRGGBB values?
+
+- Try to keep your color scheme simple by avoiding unnecessary logic and
+ refraining from adding options. The best color scheme is one that only
+ requires: >
+
+ colorscheme foobar
+
+The color schemes distributed with Vim are built with lifepillar/colortemplate
+(https://github.com/lifepillar/vim-colortemplate). It is therefore highly
+recommended.
+
+If you would like your color scheme to be distributed with Vim, make sure
+that:
+
+- it satisfies the guidelines above,
+- it was made with colortemplate,
+
+and join us at vim/colorschemes: (https://github.com/vim/colorschemes).
+
+
+vim: set ft=help :
diff --git a/runtime/colors/blue.vim b/runtime/colors/blue.vim
index 86de8a4dc4..d072ce6058 100644
--- a/runtime/colors/blue.vim
+++ b/runtime/colors/blue.vim
@@ -1,55 +1,562 @@
-" local syntax file - set colors on a per-machine basis:
-" vim: tw=0 ts=4 sw=4
-" Vim color file
-" Maintainer: Steven Vertigan <steven@vertigan.wattle.id.au>
-" Last Change: 2006 Sep 23
-" Revision #5: Switch main text from white to yellow for easier contrast,
-" fixed some problems with terminal backgrounds.
+" Name: blue
+" Description: Colorscheme with a blue background
+" Author: Original author Steven Vertigan <steven@vertigan.wattle.id.au>
+" Maintainer: Original maintainer Steven Vertigan <steven@vertigan.wattle.id.au>
+" Website: https://github.com/vim/colorschemes
+" License: Same as Vim
+" Last Updated: Sat 11 Jun 2022 11:16:14 MSK
+
+" Generated by Colortemplate v2.2.0
set background=dark
+
hi clear
-if exists("syntax_on")
- syntax reset
+let g:colors_name = 'blue'
+
+let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co > 1 ? &t_Co : 1
+
+if (has('termguicolors') && &termguicolors) || has('gui_running')
+ let g:terminal_ansi_colors = ['#000000', '#870000', '#006400', '#878700', '#000087', '#870087', '#008787', '#bcbcbc', '#878787', '#d70000', '#00ff00', '#ffdf00', '#5fafff', '#d787d7', '#5fffff', '#ffffff']
+endif
+hi Normal guifg=#ffdf00 guibg=#000087 gui=NONE cterm=NONE
+hi CursorLine guifg=NONE guibg=#005faf gui=NONE cterm=NONE
+hi Pmenu guifg=#ffffff guibg=#008787 gui=NONE cterm=NONE
+hi PmenuSel guifg=#008787 guibg=#ffffff gui=NONE cterm=NONE
+hi QuickFixLine guifg=#000000 guibg=#d787d7 gui=NONE cterm=NONE
+hi ColorColumn guifg=NONE guibg=#870087 gui=NONE cterm=NONE
+hi Conceal guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
+hi Cursor guifg=#000000 guibg=#00ff00 gui=NONE cterm=NONE
+hi CursorColumn guifg=NONE guibg=#005faf gui=NONE cterm=NONE
+hi CursorIM guifg=#000000 guibg=#ffdf00 gui=NONE cterm=NONE
+hi CursorLineNr guifg=#ffdf00 guibg=#005faf gui=bold cterm=NONE
+hi EndOfBuffer guifg=#ffdf00 guibg=#000087 gui=NONE cterm=NONE
+hi Error guifg=#ff7f50 guibg=#000087 gui=reverse cterm=reverse
+hi ErrorMsg guifg=#ffffff guibg=#d70000 gui=NONE cterm=NONE
+hi FoldColumn guifg=#008787 guibg=NONE gui=NONE cterm=NONE
+hi Folded guifg=#000087 guibg=#878700 gui=NONE cterm=NONE
+hi IncSearch guifg=#d787d7 guibg=#000000 gui=standout cterm=reverse
+hi LineNr guifg=#5fffff guibg=NONE gui=NONE cterm=NONE
+hi MatchParen guifg=NONE guibg=NONE gui=reverse ctermfg=NONE ctermbg=NONE cterm=reverse
+hi ModeMsg guifg=#000087 guibg=#00ff00 gui=NONE cterm=NONE
+hi MoreMsg guifg=#5fffff guibg=NONE gui=NONE cterm=NONE
+hi NonText guifg=#d787d7 guibg=NONE gui=NONE cterm=NONE
+hi PmenuSbar guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
+hi PmenuThumb guifg=NONE guibg=#ffffff gui=NONE cterm=NONE
+hi Question guifg=#00ff00 guibg=NONE gui=NONE cterm=NONE
+hi Search guifg=#ffdf00 guibg=#000000 gui=reverse cterm=reverse
+hi SignColumn guifg=#008787 guibg=NONE gui=NONE cterm=NONE
+hi SpecialKey guifg=#5fffff guibg=NONE gui=NONE cterm=NONE
+hi SpellBad guifg=#d70000 guibg=NONE guisp=#d70000 gui=undercurl cterm=underline
+hi SpellCap guifg=#00ff00 guibg=NONE guisp=#00ff00 gui=undercurl cterm=underline
+hi SpellLocal guifg=#ffffff guibg=NONE guisp=#ffffff gui=undercurl cterm=underline
+hi SpellRare guifg=#d787d7 guibg=NONE guisp=#d787d7 gui=undercurl cterm=underline
+hi StatusLine guifg=#000087 guibg=#5fffff gui=NONE cterm=NONE
+hi StatusLineNC guifg=#000087 guibg=#008787 gui=NONE cterm=NONE
+hi ToolbarButton guifg=#ffffff guibg=#005faf gui=NONE cterm=NONE
+hi ToolbarLine guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
+hi VertSplit guifg=#008787 guibg=NONE gui=NONE cterm=NONE
+hi Visual guifg=#ffffff guibg=#008787 gui=NONE cterm=NONE
+hi VisualNOS guifg=#008787 guibg=#ffffff gui=NONE cterm=NONE
+hi WarningMsg guifg=#d70000 guibg=NONE gui=NONE cterm=NONE
+hi WildMenu guifg=#000087 guibg=#ffdf00 gui=NONE cterm=NONE
+hi debugBreakpoint guifg=#00ff00 guibg=#000087 gui=reverse cterm=reverse
+hi debugPC guifg=#5fffff guibg=#000087 gui=reverse cterm=reverse
+hi Directory guifg=#5fffff guibg=NONE gui=NONE cterm=NONE
+hi Title guifg=#d787d7 guibg=NONE gui=NONE cterm=NONE
+hi Comment guifg=#878787 guibg=NONE gui=bold cterm=NONE
+hi Constant guifg=#5fffff guibg=NONE gui=NONE cterm=NONE
+hi Identifier guifg=#bcbcbc guibg=NONE gui=NONE cterm=NONE
+hi Ignore guifg=#878787 guibg=NONE gui=NONE cterm=NONE
+hi PreProc guifg=#00ff00 guibg=NONE gui=NONE cterm=NONE
+hi Special guifg=#d787d7 guibg=NONE gui=NONE cterm=NONE
+hi Statement guifg=#ffffff guibg=NONE gui=NONE cterm=NONE
+hi Todo guifg=NONE guibg=NONE gui=reverse ctermfg=NONE ctermbg=NONE cterm=reverse
+hi Type guifg=#ffa500 guibg=NONE gui=bold cterm=NONE
+hi Underlined guifg=NONE guibg=NONE gui=underline ctermfg=NONE ctermbg=NONE cterm=underline
+hi Label guifg=#ffdf00 guibg=NONE gui=NONE cterm=NONE
+hi! link Terminal Normal
+hi! link Debug Special
+hi! link diffAdded String
+hi! link diffRemoved WarningMsg
+hi! link diffOnly WarningMsg
+hi! link diffNoEOL WarningMsg
+hi! link diffIsA WarningMsg
+hi! link diffIdentical WarningMsg
+hi! link diffDiffer WarningMsg
+hi! link diffCommon WarningMsg
+hi! link diffBDiffer WarningMsg
+hi! link lCursor Cursor
+hi! link LineNrAbove LineNr
+hi! link LineNrBelow LineNr
+hi! link CurSearch Search
+hi! link CursorLineNr CursorLine
+hi! link CursorLineFold CursorLine
+hi! link CursorLineSign CursorLine
+hi! link StatusLineTerm StatusLine
+hi! link StatusLineTermNC StatusLineNC
+hi! link TabLine StatusLineNC
+hi! link TabLineFill StatusLineNC
+hi! link TabLineSel StatusLine
+hi! link Boolean Constant
+hi! link Character Constant
+hi! link Conditional Statement
+hi! link Define PreProc
+hi! link Delimiter Special
+hi! link Exception Statement
+hi! link Float Constant
+hi! link Function Identifier
+hi! link Include PreProc
+hi! link Keyword Statement
+hi! link Macro PreProc
+hi! link Number Constant
+hi! link Operator Type
+hi! link PreCondit PreProc
+hi! link Repeat Statement
+hi! link SpecialChar Special
+hi! link SpecialComment Special
+hi! link StorageClass Type
+hi! link String Constant
+hi! link Structure Type
+hi! link Tag Special
+hi! link Typedef Type
+hi! link Terminal Normal
+hi DiffAdd guifg=#ffffff guibg=#5f875f gui=NONE cterm=NONE
+hi DiffChange guifg=#ffffff guibg=#5f87af gui=NONE cterm=NONE
+hi DiffText guifg=#000000 guibg=#c6c6c6 gui=NONE cterm=NONE
+hi DiffDelete guifg=#ffffff guibg=#af5faf gui=NONE cterm=NONE
+
+if s:t_Co >= 256
+ hi Normal ctermfg=220 ctermbg=18 cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=25 cterm=NONE
+ hi Pmenu ctermfg=231 ctermbg=30 cterm=NONE
+ hi PmenuSel ctermfg=30 ctermbg=231 cterm=NONE
+ hi QuickFixLine ctermfg=16 ctermbg=176 cterm=NONE
+ hi ColorColumn ctermfg=NONE ctermbg=90 cterm=NONE
+ hi Conceal ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Cursor ctermfg=16 ctermbg=46 cterm=NONE
+ hi CursorColumn ctermfg=NONE ctermbg=25 cterm=NONE
+ hi CursorIM ctermfg=16 ctermbg=220 cterm=NONE
+ hi CursorLineNr ctermfg=220 ctermbg=25 cterm=NONE
+ hi EndOfBuffer ctermfg=220 ctermbg=18 cterm=NONE
+ hi Error ctermfg=209 ctermbg=18 cterm=reverse
+ hi ErrorMsg ctermfg=231 ctermbg=160 cterm=NONE
+ hi FoldColumn ctermfg=30 ctermbg=NONE cterm=NONE
+ hi Folded ctermfg=18 ctermbg=100 cterm=NONE
+ hi IncSearch ctermfg=176 ctermbg=16 cterm=reverse
+ hi LineNr ctermfg=87 ctermbg=NONE cterm=NONE
+ hi MatchParen ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi ModeMsg ctermfg=18 ctermbg=46 cterm=NONE
+ hi MoreMsg ctermfg=87 ctermbg=NONE cterm=NONE
+ hi NonText ctermfg=176 ctermbg=NONE cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=231 cterm=NONE
+ hi Question ctermfg=46 ctermbg=NONE cterm=NONE
+ hi Search ctermfg=220 ctermbg=16 cterm=reverse
+ hi SignColumn ctermfg=30 ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=87 ctermbg=NONE cterm=NONE
+ hi SpellBad ctermfg=160 ctermbg=NONE cterm=underline
+ hi SpellCap ctermfg=46 ctermbg=NONE cterm=underline
+ hi SpellLocal ctermfg=231 ctermbg=NONE cterm=underline
+ hi SpellRare ctermfg=176 ctermbg=NONE cterm=underline
+ hi StatusLine ctermfg=18 ctermbg=87 cterm=NONE
+ hi StatusLineNC ctermfg=18 ctermbg=30 cterm=NONE
+ hi ToolbarButton ctermfg=231 ctermbg=25 cterm=NONE
+ hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi VertSplit ctermfg=30 ctermbg=NONE cterm=NONE
+ hi Visual ctermfg=231 ctermbg=30 cterm=NONE
+ hi VisualNOS ctermfg=30 ctermbg=231 cterm=NONE
+ hi WarningMsg ctermfg=160 ctermbg=NONE cterm=NONE
+ hi WildMenu ctermfg=18 ctermbg=220 cterm=NONE
+ hi debugBreakpoint ctermfg=46 ctermbg=18 cterm=reverse
+ hi debugPC ctermfg=87 ctermbg=18 cterm=reverse
+ hi Directory ctermfg=87 ctermbg=NONE cterm=NONE
+ hi Title ctermfg=176 ctermbg=NONE cterm=NONE
+ hi Comment ctermfg=102 ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=87 ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=250 ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=102 ctermbg=NONE cterm=NONE
+ hi PreProc ctermfg=46 ctermbg=NONE cterm=NONE
+ hi Special ctermfg=176 ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=231 ctermbg=NONE cterm=NONE
+ hi Todo ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi Type ctermfg=214 ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline
+ hi Label ctermfg=220 ctermbg=NONE cterm=NONE
+ hi! link Terminal Normal
+ hi! link Debug Special
+ hi! link diffAdded String
+ hi! link diffRemoved WarningMsg
+ hi! link diffOnly WarningMsg
+ hi! link diffNoEOL WarningMsg
+ hi! link diffIsA WarningMsg
+ hi! link diffIdentical WarningMsg
+ hi! link diffDiffer WarningMsg
+ hi! link diffCommon WarningMsg
+ hi! link diffBDiffer WarningMsg
+ hi! link lCursor Cursor
+ hi! link LineNrAbove LineNr
+ hi! link LineNrBelow LineNr
+ hi! link CurSearch Search
+ hi! link CursorLineNr CursorLine
+ hi! link CursorLineFold CursorLine
+ hi! link CursorLineSign CursorLine
+ hi! link StatusLineTerm StatusLine
+ hi! link StatusLineTermNC StatusLineNC
+ hi! link TabLine StatusLineNC
+ hi! link TabLineFill StatusLineNC
+ hi! link TabLineSel StatusLine
+ hi! link Boolean Constant
+ hi! link Character Constant
+ hi! link Conditional Statement
+ hi! link Define PreProc
+ hi! link Delimiter Special
+ hi! link Exception Statement
+ hi! link Float Constant
+ hi! link Function Identifier
+ hi! link Include PreProc
+ hi! link Keyword Statement
+ hi! link Macro PreProc
+ hi! link Number Constant
+ hi! link Operator Type
+ hi! link PreCondit PreProc
+ hi! link Repeat Statement
+ hi! link SpecialChar Special
+ hi! link SpecialComment Special
+ hi! link StorageClass Type
+ hi! link String Constant
+ hi! link Structure Type
+ hi! link Tag Special
+ hi! link Typedef Type
+ hi! link Terminal Normal
+ hi DiffAdd ctermfg=231 ctermbg=65 cterm=NONE
+ hi DiffChange ctermfg=231 ctermbg=67 cterm=NONE
+ hi DiffText ctermfg=16 ctermbg=251 cterm=NONE
+ hi DiffDelete ctermfg=231 ctermbg=133 cterm=NONE
+ unlet s:t_Co
+ finish
+endif
+
+if s:t_Co >= 16
+ hi Normal ctermfg=yellow ctermbg=darkblue cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
+ hi Pmenu ctermfg=black ctermbg=darkcyan cterm=NONE
+ hi PmenuSel ctermfg=black ctermbg=white cterm=NONE
+ hi QuickFixLine ctermfg=black ctermbg=magenta cterm=NONE
+ hi ColorColumn ctermfg=NONE ctermbg=darkmagenta cterm=NONE
+ hi Conceal ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Cursor ctermfg=black ctermbg=green cterm=NONE
+ hi CursorColumn ctermfg=NONE ctermbg=blue cterm=NONE
+ hi CursorIM ctermfg=black ctermbg=yellow cterm=NONE
+ hi CursorLineNr ctermfg=yellow ctermbg=blue cterm=NONE
+ hi EndOfBuffer ctermfg=yellow ctermbg=darkblue cterm=NONE
+ hi Error ctermfg=red ctermbg=darkblue cterm=reverse
+ hi ErrorMsg ctermfg=white ctermbg=red cterm=NONE
+ hi FoldColumn ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Folded ctermfg=darkblue ctermbg=darkyellow cterm=NONE
+ hi IncSearch ctermfg=magenta ctermbg=black cterm=reverse
+ hi LineNr ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi MatchParen ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi ModeMsg ctermfg=darkblue ctermbg=green cterm=NONE
+ hi MoreMsg ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi NonText ctermfg=magenta ctermbg=NONE cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=white cterm=NONE
+ hi Question ctermfg=green ctermbg=NONE cterm=NONE
+ hi Search ctermfg=yellow ctermbg=black cterm=reverse
+ hi SignColumn ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi SpellBad ctermfg=red ctermbg=NONE cterm=underline
+ hi SpellCap ctermfg=green ctermbg=NONE cterm=underline
+ hi SpellLocal ctermfg=white ctermbg=NONE cterm=underline
+ hi SpellRare ctermfg=magenta ctermbg=NONE cterm=underline
+ hi StatusLine ctermfg=darkblue ctermbg=cyan cterm=NONE
+ hi StatusLineNC ctermfg=darkblue ctermbg=darkcyan cterm=NONE
+ hi ToolbarButton ctermfg=white ctermbg=blue cterm=NONE
+ hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi VertSplit ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Visual ctermfg=white ctermbg=darkcyan cterm=NONE
+ hi VisualNOS ctermfg=darkcyan ctermbg=white cterm=NONE
+ hi WarningMsg ctermfg=red ctermbg=NONE cterm=NONE
+ hi WildMenu ctermfg=darkblue ctermbg=yellow cterm=NONE
+ hi debugBreakpoint ctermfg=green ctermbg=darkblue cterm=reverse
+ hi debugPC ctermfg=cyan ctermbg=darkblue cterm=reverse
+ hi Directory ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi Title ctermfg=magenta ctermbg=NONE cterm=NONE
+ hi Comment ctermfg=darkgray ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=gray ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=darkgray ctermbg=NONE cterm=NONE
+ hi PreProc ctermfg=green ctermbg=NONE cterm=NONE
+ hi Special ctermfg=magenta ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=white ctermbg=NONE cterm=NONE
+ hi Todo ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi Type ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline
+ hi Label ctermfg=yellow ctermbg=NONE cterm=NONE
+ hi! link Terminal Normal
+ hi! link Debug Special
+ hi! link diffAdded String
+ hi! link diffRemoved WarningMsg
+ hi! link diffOnly WarningMsg
+ hi! link diffNoEOL WarningMsg
+ hi! link diffIsA WarningMsg
+ hi! link diffIdentical WarningMsg
+ hi! link diffDiffer WarningMsg
+ hi! link diffCommon WarningMsg
+ hi! link diffBDiffer WarningMsg
+ hi! link lCursor Cursor
+ hi! link LineNrAbove LineNr
+ hi! link LineNrBelow LineNr
+ hi! link CurSearch Search
+ hi! link CursorLineNr CursorLine
+ hi! link CursorLineFold CursorLine
+ hi! link CursorLineSign CursorLine
+ hi! link StatusLineTerm StatusLine
+ hi! link StatusLineTermNC StatusLineNC
+ hi! link TabLine StatusLineNC
+ hi! link TabLineFill StatusLineNC
+ hi! link TabLineSel StatusLine
+ hi! link Boolean Constant
+ hi! link Character Constant
+ hi! link Conditional Statement
+ hi! link Define PreProc
+ hi! link Delimiter Special
+ hi! link Exception Statement
+ hi! link Float Constant
+ hi! link Function Identifier
+ hi! link Include PreProc
+ hi! link Keyword Statement
+ hi! link Macro PreProc
+ hi! link Number Constant
+ hi! link Operator Type
+ hi! link PreCondit PreProc
+ hi! link Repeat Statement
+ hi! link SpecialChar Special
+ hi! link SpecialComment Special
+ hi! link StorageClass Type
+ hi! link String Constant
+ hi! link Structure Type
+ hi! link Tag Special
+ hi! link Typedef Type
+ hi! link Terminal Normal
+ hi DiffAdd ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi DiffChange ctermfg=white ctermbg=blue cterm=NONE
+ hi DiffText ctermfg=black ctermbg=grey cterm=NONE
+ hi DiffDelete ctermfg=white ctermbg=magenta cterm=NONE
+ unlet s:t_Co
+ finish
+endif
+
+if s:t_Co >= 8
+ hi Normal ctermfg=darkyellow ctermbg=blue cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
+ hi Pmenu ctermfg=black ctermbg=cyan cterm=NONE
+ hi PmenuSel ctermfg=black ctermbg=gray cterm=NONE
+ hi QuickFixLine ctermfg=black ctermbg=magenta cterm=NONE
+ hi ColorColumn ctermfg=NONE ctermbg=magenta cterm=NONE
+ hi Conceal ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Cursor ctermfg=black ctermbg=green cterm=NONE
+ hi CursorColumn ctermfg=blue ctermbg=yellow cterm=bold,reverse
+ hi CursorIM ctermfg=black ctermbg=yellow cterm=NONE
+ hi EndOfBuffer ctermfg=yellow ctermbg=blue cterm=NONE
+ hi Error ctermfg=red ctermbg=blue cterm=reverse
+ hi ErrorMsg ctermfg=gray ctermbg=red cterm=NONE
+ hi FoldColumn ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi Folded ctermfg=blue ctermbg=yellow cterm=NONE
+ hi IncSearch ctermfg=magenta ctermbg=black cterm=reverse
+ hi LineNr ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi MatchParen ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi ModeMsg ctermfg=blue ctermbg=green cterm=NONE
+ hi MoreMsg ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi NonText ctermfg=magenta ctermbg=NONE cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=white cterm=NONE
+ hi Question ctermfg=green ctermbg=NONE cterm=NONE
+ hi Search ctermfg=yellow ctermbg=black cterm=reverse
+ hi SignColumn ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi SpellBad ctermfg=red ctermbg=yellow cterm=reverse
+ hi SpellCap ctermfg=green ctermbg=NONE cterm=reverse
+ hi SpellLocal ctermfg=gray ctermbg=NONE cterm=reverse
+ hi SpellRare ctermfg=magenta ctermbg=yellow cterm=reverse
+ hi StatusLine ctermfg=cyan ctermbg=blue cterm=bold,reverse
+ hi StatusLineNC ctermfg=blue ctermbg=cyan cterm=NONE
+ hi ToolbarButton ctermfg=blue ctermbg=gray cterm=bold,reverse
+ hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi VertSplit ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi Visual ctermfg=black ctermbg=cyan cterm=NONE
+ hi VisualNOS ctermfg=cyan ctermbg=gray cterm=NONE
+ hi WarningMsg ctermfg=red ctermbg=NONE cterm=NONE
+ hi WildMenu ctermfg=blue ctermbg=yellow cterm=NONE
+ hi debugBreakpoint ctermfg=green ctermbg=blue cterm=reverse
+ hi debugPC ctermfg=cyan ctermbg=blue cterm=reverse
+ hi Directory ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi Title ctermfg=magenta ctermbg=NONE cterm=NONE
+ hi Comment ctermfg=black ctermbg=NONE cterm=bold
+ hi Constant ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=gray ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=gray ctermbg=NONE cterm=NONE
+ hi PreProc ctermfg=green ctermbg=NONE cterm=NONE
+ hi Special ctermfg=magenta ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=gray ctermbg=NONE cterm=NONE
+ hi Todo ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi Type ctermfg=red ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline
+ hi Label ctermfg=yellow ctermbg=NONE cterm=NONE
+ hi! link Terminal Normal
+ hi! link Debug Special
+ hi! link diffAdded String
+ hi! link diffRemoved WarningMsg
+ hi! link diffOnly WarningMsg
+ hi! link diffNoEOL WarningMsg
+ hi! link diffIsA WarningMsg
+ hi! link diffIdentical WarningMsg
+ hi! link diffDiffer WarningMsg
+ hi! link diffCommon WarningMsg
+ hi! link diffBDiffer WarningMsg
+ hi! link lCursor Cursor
+ hi! link LineNrAbove LineNr
+ hi! link LineNrBelow LineNr
+ hi! link CurSearch Search
+ hi! link CursorLineNr CursorLine
+ hi! link CursorLineFold CursorLine
+ hi! link CursorLineSign CursorLine
+ hi! link StatusLineTerm StatusLine
+ hi! link StatusLineTermNC StatusLineNC
+ hi! link TabLine StatusLineNC
+ hi! link TabLineFill StatusLineNC
+ hi! link TabLineSel StatusLine
+ hi! link Boolean Constant
+ hi! link Character Constant
+ hi! link Conditional Statement
+ hi! link Define PreProc
+ hi! link Delimiter Special
+ hi! link Exception Statement
+ hi! link Float Constant
+ hi! link Function Identifier
+ hi! link Include PreProc
+ hi! link Keyword Statement
+ hi! link Macro PreProc
+ hi! link Number Constant
+ hi! link Operator Type
+ hi! link PreCondit PreProc
+ hi! link Repeat Statement
+ hi! link SpecialChar Special
+ hi! link SpecialComment Special
+ hi! link StorageClass Type
+ hi! link String Constant
+ hi! link Structure Type
+ hi! link Tag Special
+ hi! link Typedef Type
+ hi! link Terminal Normal
+ hi DiffAdd ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi DiffChange ctermfg=white ctermbg=darkblue cterm=NONE
+ hi DiffText ctermfg=black ctermbg=grey cterm=NONE
+ hi DiffDelete ctermfg=white ctermbg=darkmagenta cterm=NONE
+ unlet s:t_Co
+ finish
+endif
+
+if s:t_Co >= 0
+ hi Normal term=NONE
+ hi ColorColumn term=reverse
+ hi Conceal term=NONE
+ hi Cursor term=reverse
+ hi CursorColumn term=NONE
+ hi CursorLine term=underline
+ hi CursorLineNr term=bold
+ hi DiffAdd term=reverse
+ hi DiffChange term=NONE
+ hi DiffDelete term=reverse
+ hi DiffText term=reverse
+ hi Directory term=NONE
+ hi EndOfBuffer term=NONE
+ hi ErrorMsg term=bold,reverse
+ hi FoldColumn term=NONE
+ hi Folded term=NONE
+ hi IncSearch term=bold,reverse,underline
+ hi LineNr term=NONE
+ hi MatchParen term=bold,underline
+ hi ModeMsg term=bold
+ hi MoreMsg term=NONE
+ hi NonText term=NONE
+ hi Pmenu term=reverse
+ hi PmenuSbar term=reverse
+ hi PmenuSel term=bold
+ hi PmenuThumb term=NONE
+ hi Question term=standout
+ hi Search term=reverse
+ hi SignColumn term=reverse
+ hi SpecialKey term=bold
+ hi SpellBad term=underline
+ hi SpellCap term=underline
+ hi SpellLocal term=underline
+ hi SpellRare term=underline
+ hi StatusLine term=bold,reverse
+ hi StatusLineNC term=bold,underline
+ hi TabLine term=bold,underline
+ hi TabLineFill term=NONE
+ hi Terminal term=NONE
+ hi TabLineSel term=bold,reverse
+ hi Title term=NONE
+ hi VertSplit term=NONE
+ hi Visual term=reverse
+ hi VisualNOS term=NONE
+ hi WarningMsg term=standout
+ hi WildMenu term=bold
+ hi CursorIM term=NONE
+ hi ToolbarLine term=reverse
+ hi ToolbarButton term=bold,reverse
+ hi CurSearch term=reverse
+ hi CursorLineFold term=underline
+ hi CursorLineSign term=underline
+ hi Comment term=bold
+ hi Constant term=NONE
+ hi Error term=bold,reverse
+ hi Identifier term=NONE
+ hi Ignore term=NONE
+ hi PreProc term=NONE
+ hi Special term=NONE
+ hi Statement term=NONE
+ hi Todo term=bold,reverse
+ hi Type term=NONE
+ hi Underlined term=underline
+ unlet s:t_Co
+ finish
endif
-let g:colors_name = "blue"
-hi Normal guifg=yellow guibg=darkBlue ctermfg=yellow ctermbg=darkBlue
-hi NonText guifg=magenta ctermfg=lightMagenta
-hi comment guifg=gray ctermfg=gray ctermbg=darkBlue gui=bold
-hi constant guifg=cyan ctermfg=cyan
-hi identifier guifg=gray ctermfg=red
-hi statement guifg=white ctermfg=white ctermbg=darkBlue gui=none
-hi preproc guifg=green ctermfg=green
-hi type guifg=orange ctermfg=lightRed ctermbg=darkBlue
-hi special guifg=magenta ctermfg=lightMagenta ctermbg=darkBlue
-hi Underlined guifg=cyan ctermfg=cyan gui=underline cterm=underline
-hi label guifg=yellow ctermfg=yellow
-hi operator guifg=orange gui=bold ctermfg=lightRed ctermbg=darkBlue
-
-hi ErrorMsg guifg=orange guibg=darkBlue ctermfg=lightRed
-hi WarningMsg guifg=cyan guibg=darkBlue ctermfg=cyan gui=bold
-hi ModeMsg guifg=yellow gui=NONE ctermfg=yellow
-hi MoreMsg guifg=yellow gui=NONE ctermfg=yellow
-hi Error guifg=red guibg=darkBlue gui=underline ctermfg=red
-
-hi Todo guifg=black guibg=orange ctermfg=black ctermbg=darkYellow
-hi Cursor guifg=black guibg=white ctermfg=black ctermbg=white
-hi Search guifg=black guibg=orange ctermfg=black ctermbg=darkYellow
-hi IncSearch guifg=black guibg=yellow ctermfg=black ctermbg=darkYellow
-hi LineNr guifg=cyan ctermfg=cyan
-hi title guifg=white gui=bold cterm=bold
-
-hi StatusLineNC gui=NONE guifg=black guibg=blue ctermfg=black ctermbg=blue
-hi StatusLine gui=bold guifg=cyan guibg=blue ctermfg=cyan ctermbg=blue
-hi VertSplit gui=none guifg=blue guibg=blue ctermfg=blue ctermbg=blue
-
-hi Visual term=reverse ctermfg=black ctermbg=darkCyan guifg=black guibg=darkCyan
-
-hi DiffChange guibg=darkGreen guifg=black ctermbg=darkGreen ctermfg=black
-hi DiffText guibg=olivedrab guifg=black ctermbg=lightGreen ctermfg=black
-hi DiffAdd guibg=slateblue guifg=black ctermbg=blue ctermfg=black
-hi DiffDelete guibg=coral guifg=black ctermbg=cyan ctermfg=black
-
-hi Folded guibg=orange guifg=black ctermbg=yellow ctermfg=black
-hi FoldColumn guibg=gray30 guifg=black ctermbg=gray ctermfg=black
-hi cIf0 guifg=gray ctermfg=gray
+" Background: dark
+" Color: black #000000 16 black
+" Color: darkred #870000 88 darkred
+" Color: darkyellow #878700 100 darkyellow
+" Color: darkblue #000087 18 darkblue
+" Color: darkmagenta #870087 90 darkmagenta
+" Color: darkcyan #008787 30 darkcyan
+" Color: gray #bcbcbc 250 gray
+" Color: comment #878787 102 darkgray
+" Color: darkgray #878787 102 darkgray
+" Color: red #d70000 160 red
+" Color: green #00ff00 46 green
+" Color: yellow #ffdf00 220 yellow
+" Color: blue #005faf 25 blue
+" Color: magenta #d787d7 176 magenta
+" Color: cyan #5fffff 87 cyan
+" Color: white #ffffff 231 white
+" Color: xtermblue #5fafff 75 blue
+" Color: xtermdarkblue #0087af 31 darkblue
+" Color: orange #ffa500 214 darkyellow
+" Color: darkgreen #006400 22 darkgreen
+" Color: coral #ff7f50 209 red
+" Color: olivedrab #6b8e23 64 green
+" Color: slateblue #6a5acd 62 darkmagenta
+" Term colors: black darkred darkgreen darkyellow darkblue darkmagenta darkcyan gray
+" Term colors: darkgray red green yellow xtermblue magenta cyan white
+" Color: bgDiffA #5F875F 65 darkgreen
+" Color: bgDiffC #5F87AF 67 blue
+" Color: bgDiffD #AF5FAF 133 magenta
+" Color: bgDiffT #C6C6C6 251 grey
+" Color: fgDiffW #FFFFFF 231 white
+" Color: fgDiffB #000000 16 black
+" Color: bgDiffC8 #5F87AF 67 darkblue
+" Color: bgDiffD8 #AF5FAF 133 darkmagenta
+" vim: et ts=2 sw=2
diff --git a/runtime/colors/darkblue.vim b/runtime/colors/darkblue.vim
index 88f0bd73b9..970a8cb060 100644
--- a/runtime/colors/darkblue.vim
+++ b/runtime/colors/darkblue.vim
@@ -1,65 +1,466 @@
-" Vim color file
-" Maintainer: Bohdan Vlasyuk <bohdan@vstu.edu.ua>
-" Last Change: 2008 Jul 18
+" Name: darkblue
+" Description: For those who prefer dark background
+" Author: Original author Bohdan Vlasyuk <bohdan@vstu.edu.ua>
+" Maintainer: Original author Bohdan Vlasyuk <bohdan@vstu.edu.ua>
+" Website: https://github.com/vim/colorschemes
+" License: Same as Vim
+" Last Updated: Sat 11 Jun 2022 14:37:41 MSK
-" darkblue -- for those who prefer dark background
-" [note: looks bit uglier with come terminal palettes,
-" but is fine on default linux console palette.]
+" Generated by Colortemplate v2.2.0
-set bg=dark
-hi clear
-if exists("syntax_on")
- syntax reset
-endif
-
-let colors_name = "darkblue"
+set background=dark
-hi Normal guifg=#c0c0c0 guibg=#000040 ctermfg=gray ctermbg=black
-hi ErrorMsg guifg=#ffffff guibg=#287eff ctermfg=white ctermbg=lightblue
-hi Visual guifg=#8080ff guibg=fg gui=reverse ctermfg=lightblue ctermbg=fg cterm=reverse
-hi Todo guifg=#d14a14 guibg=#1248d1 ctermfg=red ctermbg=darkblue
-hi Search guifg=#90fff0 guibg=#2050d0 ctermfg=white ctermbg=darkblue cterm=underline term=underline
-hi IncSearch guifg=#b0ffff guibg=#2050d0 ctermfg=darkblue ctermbg=gray
-
-hi SpecialKey guifg=cyan ctermfg=darkcyan
-hi Directory guifg=cyan ctermfg=cyan
-hi Title guifg=magenta gui=none ctermfg=magenta cterm=bold
-hi WarningMsg guifg=red ctermfg=red
-hi WildMenu guifg=yellow guibg=black ctermfg=yellow ctermbg=black cterm=none term=none
-hi ModeMsg guifg=#22cce2 ctermfg=lightblue
-hi MoreMsg ctermfg=darkgreen ctermfg=darkgreen
-hi Question guifg=green gui=none ctermfg=green cterm=none
-hi NonText guifg=#0030ff ctermfg=darkblue
+hi clear
+let g:colors_name = 'darkblue'
-hi StatusLine guifg=blue guibg=darkgray gui=none ctermfg=blue ctermbg=gray term=none cterm=none
-hi StatusLineNC guifg=black guibg=darkgray gui=none ctermfg=black ctermbg=gray term=none cterm=none
-hi VertSplit guifg=black guibg=darkgray gui=none ctermfg=black ctermbg=gray term=none cterm=none
+let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co > 1 ? &t_Co : 1
-hi Folded guifg=#808080 guibg=#000040 ctermfg=darkgrey ctermbg=black cterm=bold term=bold
-hi FoldColumn guifg=#808080 guibg=#000040 ctermfg=darkgrey ctermbg=black cterm=bold term=bold
-hi LineNr guifg=#90f020 ctermfg=green cterm=none
+if (has('termguicolors') && &termguicolors) || has('gui_running')
+ let g:terminal_ansi_colors = ['#000000', '#8b0000', '#90f020', '#ffa500', '#00008b', '#8b008b', '#008b8b', '#c0c0c0', '#808080', '#ffa0a0', '#90f020', '#ffff60', '#0030ff', '#ff00ff', '#90fff0', '#ffffff']
+endif
+hi! link Terminal Normal
+hi! link CursorColumn CursorLine
+hi! link CursorIM Cursor
+hi! link EndOfBuffer NonText
+hi! link ErrorMsg Error
+hi! link LineNrAbove LineNr
+hi! link LineNrBelow LineNr
+hi! link StatusLineTerm StatusLine
+hi! link StatusLineTermNC StatusLineNC
+hi! link CursorLineFold CursorLine
+hi! link CursorLineSign CursorLine
+hi! link TabLineFill TabLine
+hi! link Boolean Constant
+hi! link Character Constant
+hi! link Conditional Statement
+hi! link Define PreProc
+hi! link Delimiter Special
+hi! link Exception Statement
+hi! link Float Constant
+hi! link Function Identifier
+hi! link Include PreProc
+hi! link Keyword Statement
+hi! link Label Statement
+hi! link Macro PreProc
+hi! link Number Constant
+hi! link Operator Statement
+hi! link PreCondit PreProc
+hi! link Repeat Statement
+hi! link SpecialChar Special
+hi! link SpecialComment Special
+hi! link StorageClass Type
+hi! link String Constant
+hi! link Structure Type
+hi! link Tag Special
+hi! link Typedef Type
+hi! link Debug Special
+hi! link diffAdded String
+hi! link diffRemoved WarningMsg
+hi! link diffOnly WarningMsg
+hi! link diffNoEOL WarningMsg
+hi! link diffIsA WarningMsg
+hi! link diffIdentical WarningMsg
+hi! link diffDiffer WarningMsg
+hi! link diffCommon WarningMsg
+hi! link diffBDiffer WarningMsg
+hi! link lCursor Cursor
+hi! link CurSearch Search
+hi Normal guifg=#c0c0c0 guibg=#000040 gui=NONE cterm=NONE
+hi Conceal guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
+hi ColorColumn guifg=#c0c0c0 guibg=#8b0000 gui=NONE cterm=NONE
+hi Cursor guifg=#000000 guibg=#ffff60 gui=NONE cterm=NONE
+hi QuickFixLine guifg=#000000 guibg=#ff80ff gui=NONE cterm=NONE
+hi Error guifg=#ffa0a0 guibg=NONE gui=reverse cterm=reverse
+hi FoldColumn guifg=#808080 guibg=NONE gui=bold cterm=NONE
+hi Folded guifg=#808080 guibg=NONE gui=bold cterm=NONE
+hi IncSearch guifg=#ffffff guibg=#0030ff gui=reverse cterm=reverse
+hi LineNr guifg=#90f020 guibg=NONE gui=NONE cterm=NONE
+hi MatchParen guifg=NONE guibg=NONE gui=reverse ctermfg=NONE ctermbg=NONE cterm=reverse
+hi ModeMsg guifg=#90fff0 guibg=NONE gui=NONE cterm=NONE
+hi MoreMsg guifg=#006400 guibg=NONE gui=NONE cterm=NONE
+hi NonText guifg=#0030ff guibg=NONE gui=NONE cterm=NONE
+hi Pmenu guifg=#ffffff guibg=#0030ff gui=NONE cterm=NONE
+hi PmenuSbar guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
+hi PmenuSel guifg=#0030ff guibg=#ffffff gui=NONE cterm=NONE
+hi PmenuThumb guifg=NONE guibg=#ffffff gui=NONE cterm=NONE
+hi Question guifg=#90f020 guibg=NONE gui=NONE cterm=NONE
+hi SignColumn guifg=#808080 guibg=NONE gui=NONE cterm=NONE
+hi SpecialKey guifg=#008b8b guibg=NONE gui=NONE cterm=NONE
+hi SpellBad guifg=#ff0000 guibg=NONE guisp=#ff0000 gui=undercurl cterm=underline
+hi SpellCap guifg=#90f020 guibg=NONE guisp=#90f020 gui=undercurl cterm=underline
+hi SpellLocal guifg=#90fff0 guibg=NONE guisp=#90fff0 gui=undercurl cterm=underline
+hi SpellRare guifg=#ff00ff guibg=NONE guisp=#ff00ff gui=undercurl cterm=underline
+hi StatusLine guifg=#000040 guibg=#c0c0c0 gui=NONE cterm=NONE
+hi StatusLineNC guifg=#000000 guibg=#808080 gui=NONE cterm=NONE
+hi TabLine guifg=#000000 guibg=#808080 gui=NONE cterm=NONE
+hi ToolbarButton guifg=#ffffff guibg=#0030ff gui=NONE cterm=NONE
+hi ToolbarLine guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
+hi VertSplit guifg=#000000 guibg=#808080 gui=NONE cterm=NONE
+hi Visual guifg=#8080ff guibg=#ffffff gui=reverse cterm=reverse
+hi VisualNOS guifg=#8080ff guibg=#c0c0c0 gui=reverse,underline cterm=reverse,underline
+hi WarningMsg guifg=#ff0000 guibg=NONE gui=NONE cterm=NONE
+hi WildMenu guifg=#ffff60 guibg=#000000 gui=NONE cterm=NONE
+hi debugBreakpoint guifg=#90f020 guibg=#00008b gui=reverse cterm=reverse
+hi debugPC guifg=#90fff0 guibg=#00008b gui=reverse cterm=reverse
+hi Directory guifg=#008b8b guibg=NONE gui=NONE cterm=NONE
+hi Title guifg=#ff00ff guibg=NONE gui=NONE cterm=NONE
+hi Comment guifg=#80a0ff guibg=NONE gui=NONE cterm=NONE
+hi Constant guifg=#ffa0a0 guibg=NONE gui=NONE cterm=NONE
+hi Identifier guifg=#90fff0 guibg=NONE gui=NONE cterm=NONE
+hi Ignore guifg=#000040 guibg=#c0c0c0 gui=NONE cterm=NONE
+hi PreProc guifg=#ff80ff guibg=NONE gui=NONE cterm=NONE
+hi Special guifg=#ffa500 guibg=NONE gui=NONE cterm=NONE
+hi Statement guifg=#ffff60 guibg=NONE gui=NONE cterm=NONE
+hi Todo guifg=NONE guibg=NONE gui=reverse ctermfg=NONE ctermbg=NONE cterm=reverse
+hi Type guifg=#90f020 guibg=NONE gui=NONE cterm=NONE
+hi Underlined guifg=#80a0ff guibg=NONE gui=underline cterm=underline
+hi TabLineSel guifg=#000040 guibg=#c0c0c0 gui=NONE cterm=NONE
+hi CursorLine guifg=NONE guibg=#666666 gui=NONE cterm=NONE
+hi CursorLineNr guifg=#ffff60 guibg=#666666 gui=NONE cterm=NONE
+hi Search guifg=#90fff0 guibg=#0030ff gui=NONE cterm=NONE
+hi DiffAdd guifg=#ffffff guibg=#5f875f gui=NONE cterm=NONE
+hi DiffChange guifg=#ffffff guibg=#5f87af gui=NONE cterm=NONE
+hi DiffText guifg=#000000 guibg=#c6c6c6 gui=NONE cterm=NONE
+hi DiffDelete guifg=#ffffff guibg=#af5faf gui=NONE cterm=NONE
-hi DiffAdd guibg=darkblue ctermbg=darkblue term=none cterm=none
-hi DiffChange guibg=darkmagenta ctermbg=magenta cterm=none
-hi DiffDelete ctermfg=blue ctermbg=cyan gui=bold guifg=Blue guibg=DarkCyan
-hi DiffText cterm=bold ctermbg=red gui=bold guibg=Red
+if s:t_Co >= 256
+ hi! link Terminal Normal
+ hi! link CursorColumn CursorLine
+ hi! link CursorIM Cursor
+ hi! link EndOfBuffer NonText
+ hi! link ErrorMsg Error
+ hi! link LineNrAbove LineNr
+ hi! link LineNrBelow LineNr
+ hi! link StatusLineTerm StatusLine
+ hi! link StatusLineTermNC StatusLineNC
+ hi! link CursorLineFold CursorLine
+ hi! link CursorLineSign CursorLine
+ hi! link TabLineFill TabLine
+ hi! link Boolean Constant
+ hi! link Character Constant
+ hi! link Conditional Statement
+ hi! link Define PreProc
+ hi! link Delimiter Special
+ hi! link Exception Statement
+ hi! link Float Constant
+ hi! link Function Identifier
+ hi! link Include PreProc
+ hi! link Keyword Statement
+ hi! link Label Statement
+ hi! link Macro PreProc
+ hi! link Number Constant
+ hi! link Operator Statement
+ hi! link PreCondit PreProc
+ hi! link Repeat Statement
+ hi! link SpecialChar Special
+ hi! link SpecialComment Special
+ hi! link StorageClass Type
+ hi! link String Constant
+ hi! link Structure Type
+ hi! link Tag Special
+ hi! link Typedef Type
+ hi! link Debug Special
+ hi! link diffAdded String
+ hi! link diffRemoved WarningMsg
+ hi! link diffOnly WarningMsg
+ hi! link diffNoEOL WarningMsg
+ hi! link diffIsA WarningMsg
+ hi! link diffIdentical WarningMsg
+ hi! link diffDiffer WarningMsg
+ hi! link diffCommon WarningMsg
+ hi! link diffBDiffer WarningMsg
+ hi! link lCursor Cursor
+ hi! link CurSearch Search
+ hi Normal ctermfg=252 ctermbg=17 cterm=NONE
+ hi Conceal ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi ColorColumn ctermfg=252 ctermbg=88 cterm=NONE
+ hi Cursor ctermfg=16 ctermbg=227 cterm=NONE
+ hi QuickFixLine ctermfg=16 ctermbg=213 cterm=NONE
+ hi Error ctermfg=217 ctermbg=NONE cterm=reverse
+ hi FoldColumn ctermfg=102 ctermbg=NONE cterm=NONE
+ hi Folded ctermfg=102 ctermbg=NONE cterm=NONE
+ hi IncSearch ctermfg=231 ctermbg=27 cterm=reverse
+ hi LineNr ctermfg=118 ctermbg=NONE cterm=NONE
+ hi MatchParen ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi ModeMsg ctermfg=123 ctermbg=NONE cterm=NONE
+ hi MoreMsg ctermfg=22 ctermbg=NONE cterm=NONE
+ hi NonText ctermfg=27 ctermbg=NONE cterm=NONE
+ hi Pmenu ctermfg=231 ctermbg=27 cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi PmenuSel ctermfg=27 ctermbg=231 cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=231 cterm=NONE
+ hi Question ctermfg=118 ctermbg=NONE cterm=NONE
+ hi SignColumn ctermfg=102 ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=30 ctermbg=NONE cterm=NONE
+ hi SpellBad ctermfg=196 ctermbg=NONE cterm=underline
+ hi SpellCap ctermfg=118 ctermbg=NONE cterm=underline
+ hi SpellLocal ctermfg=123 ctermbg=NONE cterm=underline
+ hi SpellRare ctermfg=201 ctermbg=NONE cterm=underline
+ hi StatusLine ctermfg=17 ctermbg=252 cterm=NONE
+ hi StatusLineNC ctermfg=16 ctermbg=102 cterm=NONE
+ hi TabLine ctermfg=16 ctermbg=102 cterm=NONE
+ hi ToolbarButton ctermfg=231 ctermbg=27 cterm=NONE
+ hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi VertSplit ctermfg=16 ctermbg=102 cterm=NONE
+ hi Visual ctermfg=105 ctermbg=231 cterm=reverse
+ hi VisualNOS ctermfg=105 ctermbg=252 cterm=reverse,underline
+ hi WarningMsg ctermfg=196 ctermbg=NONE cterm=NONE
+ hi WildMenu ctermfg=227 ctermbg=16 cterm=NONE
+ hi debugBreakpoint ctermfg=118 ctermbg=18 cterm=reverse
+ hi debugPC ctermfg=123 ctermbg=18 cterm=reverse
+ hi Directory ctermfg=30 ctermbg=NONE cterm=NONE
+ hi Title ctermfg=201 ctermbg=NONE cterm=NONE
+ hi Comment ctermfg=111 ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=217 ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=123 ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=17 ctermbg=252 cterm=NONE
+ hi PreProc ctermfg=213 ctermbg=NONE cterm=NONE
+ hi Special ctermfg=214 ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=227 ctermbg=NONE cterm=NONE
+ hi Todo ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi Type ctermfg=118 ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=111 ctermbg=NONE cterm=underline
+ hi TabLineSel ctermfg=17 ctermbg=252 cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=59 cterm=NONE
+ hi CursorLineNr ctermfg=227 ctermbg=59 cterm=NONE
+ hi Search ctermfg=123 ctermbg=27 cterm=NONE
+ hi DiffAdd ctermfg=231 ctermbg=65 cterm=NONE
+ hi DiffChange ctermfg=231 ctermbg=67 cterm=NONE
+ hi DiffText ctermfg=16 ctermbg=251 cterm=NONE
+ hi DiffDelete ctermfg=231 ctermbg=133 cterm=NONE
+ unlet s:t_Co
+ finish
+endif
-hi Cursor guifg=black guibg=yellow ctermfg=black ctermbg=yellow
-hi lCursor guifg=black guibg=white ctermfg=black ctermbg=white
+if s:t_Co >= 16
+ hi Normal ctermfg=grey ctermbg=black cterm=NONE
+ hi Conceal ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi ColorColumn ctermfg=grey ctermbg=darkred cterm=NONE
+ hi Cursor ctermfg=black ctermbg=yellow cterm=NONE
+ hi QuickFixLine ctermfg=black ctermbg=magenta cterm=NONE
+ hi Error ctermfg=red ctermbg=NONE cterm=reverse
+ hi FoldColumn ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi Folded ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi IncSearch ctermfg=white ctermbg=blue cterm=reverse
+ hi LineNr ctermfg=green ctermbg=NONE cterm=NONE
+ hi MatchParen ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi ModeMsg ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi MoreMsg ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi NonText ctermfg=blue ctermbg=NONE cterm=NONE
+ hi Pmenu ctermfg=white ctermbg=blue cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi PmenuSel ctermfg=blue ctermbg=white cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=white cterm=NONE
+ hi Question ctermfg=green ctermbg=NONE cterm=NONE
+ hi SignColumn ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi SpellBad ctermfg=red ctermbg=NONE cterm=underline
+ hi SpellCap ctermfg=green ctermbg=NONE cterm=underline
+ hi SpellLocal ctermfg=cyan ctermbg=NONE cterm=underline
+ hi SpellRare ctermfg=darkmagenta ctermbg=NONE cterm=underline
+ hi StatusLine ctermfg=black ctermbg=grey cterm=NONE
+ hi StatusLineNC ctermfg=black ctermbg=darkgrey cterm=NONE
+ hi TabLine ctermfg=black ctermbg=darkgrey cterm=NONE
+ hi ToolbarButton ctermfg=white ctermbg=blue cterm=NONE
+ hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi VertSplit ctermfg=black ctermbg=darkgrey cterm=NONE
+ hi Visual ctermfg=blue ctermbg=white cterm=reverse
+ hi VisualNOS ctermfg=blue ctermbg=grey cterm=reverse,underline
+ hi WarningMsg ctermfg=red ctermbg=NONE cterm=NONE
+ hi WildMenu ctermfg=yellow ctermbg=black cterm=NONE
+ hi debugBreakpoint ctermfg=green ctermbg=darkblue cterm=reverse
+ hi debugPC ctermfg=cyan ctermbg=darkblue cterm=reverse
+ hi Directory ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Title ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi Comment ctermfg=blue ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=red ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=black ctermbg=grey cterm=NONE
+ hi PreProc ctermfg=magenta ctermbg=NONE cterm=NONE
+ hi Special ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=yellow ctermbg=NONE cterm=NONE
+ hi Todo ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi Type ctermfg=green ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=blue ctermbg=NONE cterm=underline
+ hi TabLineSel ctermfg=white ctermbg=black cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorLineNr ctermfg=yellow ctermbg=NONE cterm=underline
+ hi Search ctermfg=white ctermbg=blue cterm=NONE
+ hi DiffAdd ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi DiffChange ctermfg=white ctermbg=blue cterm=NONE
+ hi DiffText ctermfg=black ctermbg=grey cterm=NONE
+ hi DiffDelete ctermfg=white ctermbg=magenta cterm=NONE
+ unlet s:t_Co
+ finish
+endif
+if s:t_Co >= 8
+ hi Normal ctermfg=grey ctermbg=black cterm=NONE
+ hi Conceal ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi ColorColumn ctermfg=grey ctermbg=darkred cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorLineNr ctermfg=darkyellow ctermbg=NONE cterm=bold
+ hi QuickFixLine ctermfg=grey ctermbg=darkmagenta cterm=NONE
+ hi Error ctermfg=darkred ctermbg=grey cterm=reverse
+ hi FoldColumn ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Folded ctermfg=darkblue ctermbg=NONE cterm=bold,reverse
+ hi IncSearch ctermfg=grey ctermbg=darkblue cterm=reverse
+ hi LineNr ctermfg=green ctermbg=NONE cterm=bold
+ hi MatchParen ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi ModeMsg ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi MoreMsg ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi NonText ctermfg=blue ctermbg=NONE cterm=NONE
+ hi Pmenu ctermfg=grey ctermbg=blue cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi PmenuSel ctermfg=blue ctermbg=grey cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=grey cterm=NONE
+ hi Question ctermfg=darkgreen ctermbg=NONE cterm=bold
+ hi Search ctermfg=darkcyan ctermbg=blue cterm=NONE
+ hi SignColumn ctermfg=grey ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi SpellBad ctermfg=darkred ctermbg=darkyellow cterm=reverse
+ hi SpellCap ctermfg=darkgreen ctermbg=NONE cterm=reverse
+ hi SpellLocal ctermfg=darkcyan ctermbg=NONE cterm=reverse
+ hi SpellRare ctermfg=darkmagenta ctermbg=darkyellow cterm=reverse
+ hi StatusLine ctermfg=darkblue ctermbg=grey cterm=bold,reverse
+ hi StatusLineNC ctermfg=grey ctermbg=black cterm=reverse
+ hi VertSplit ctermfg=grey ctermbg=black cterm=reverse
+ hi TabLine ctermfg=grey ctermbg=black cterm=reverse
+ hi TabLineSel ctermfg=darkblue ctermbg=grey cterm=bold,reverse
+ hi ToolbarButton ctermfg=darkblue ctermbg=grey cterm=bold,reverse
+ hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Visual ctermfg=blue ctermbg=grey cterm=reverse
+ hi VisualNOS ctermfg=blue ctermbg=grey cterm=reverse,underline
+ hi WarningMsg ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi WildMenu ctermfg=yellow ctermbg=black cterm=NONE
+ hi debugBreakpoint ctermfg=darkgreen ctermbg=darkblue cterm=reverse
+ hi debugPC ctermfg=darkcyan ctermbg=darkblue cterm=reverse
+ hi Directory ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Title ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi Comment ctermfg=darkblue ctermbg=NONE cterm=bold
+ hi Constant ctermfg=darkred ctermbg=NONE cterm=bold
+ hi Identifier ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi PreProc ctermfg=darkmagenta ctermbg=NONE cterm=bold
+ hi Special ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=darkyellow ctermbg=NONE cterm=bold
+ hi Todo ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi Type ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline
+ hi DiffAdd ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi DiffChange ctermfg=white ctermbg=darkblue cterm=NONE
+ hi DiffText ctermfg=black ctermbg=grey cterm=NONE
+ hi DiffDelete ctermfg=white ctermbg=darkmagenta cterm=NONE
+ unlet s:t_Co
+ finish
+endif
-hi Comment guifg=#80a0ff ctermfg=darkred
-hi Constant ctermfg=magenta guifg=#ffa0a0 cterm=none
-hi Special ctermfg=brown guifg=Orange cterm=none gui=none
-hi Identifier ctermfg=cyan guifg=#40ffff cterm=none
-hi Statement ctermfg=yellow cterm=none guifg=#ffff60 gui=none
-hi PreProc ctermfg=magenta guifg=#ff80ff gui=none cterm=none
-hi type ctermfg=green guifg=#60ff60 gui=none cterm=none
-hi Underlined cterm=underline term=underline
-hi Ignore guifg=bg ctermfg=bg
+if s:t_Co >= 0
+ hi Normal term=NONE
+ hi ColorColumn term=reverse
+ hi Conceal term=NONE
+ hi Cursor term=reverse
+ hi CursorColumn term=NONE
+ hi CursorLine term=underline
+ hi CursorLineNr term=bold
+ hi DiffAdd term=reverse
+ hi DiffChange term=NONE
+ hi DiffDelete term=reverse
+ hi DiffText term=reverse
+ hi Directory term=NONE
+ hi EndOfBuffer term=NONE
+ hi ErrorMsg term=bold,reverse
+ hi FoldColumn term=NONE
+ hi Folded term=NONE
+ hi IncSearch term=bold,reverse,underline
+ hi LineNr term=NONE
+ hi MatchParen term=bold,underline
+ hi ModeMsg term=bold
+ hi MoreMsg term=NONE
+ hi NonText term=NONE
+ hi Pmenu term=reverse
+ hi PmenuSbar term=reverse
+ hi PmenuSel term=bold
+ hi PmenuThumb term=NONE
+ hi Question term=standout
+ hi Search term=reverse
+ hi SignColumn term=reverse
+ hi SpecialKey term=bold
+ hi SpellBad term=underline
+ hi SpellCap term=underline
+ hi SpellLocal term=underline
+ hi SpellRare term=underline
+ hi StatusLine term=bold,reverse
+ hi StatusLineNC term=bold,underline
+ hi TabLine term=bold,underline
+ hi TabLineFill term=NONE
+ hi Terminal term=NONE
+ hi TabLineSel term=bold,reverse
+ hi Title term=NONE
+ hi VertSplit term=NONE
+ hi Visual term=reverse
+ hi VisualNOS term=NONE
+ hi WarningMsg term=standout
+ hi WildMenu term=bold
+ hi CursorIM term=NONE
+ hi ToolbarLine term=reverse
+ hi ToolbarButton term=bold,reverse
+ hi CurSearch term=reverse
+ hi CursorLineFold term=underline
+ hi CursorLineSign term=underline
+ hi Comment term=bold
+ hi Constant term=NONE
+ hi Error term=bold,reverse
+ hi Identifier term=NONE
+ hi Ignore term=NONE
+ hi PreProc term=NONE
+ hi Special term=NONE
+ hi Statement term=NONE
+ hi Todo term=bold,reverse
+ hi Type term=NONE
+ hi Underlined term=underline
+ unlet s:t_Co
+ finish
+endif
-" suggested by tigmoid, 2008 Jul 18
-hi Pmenu guifg=#c0c0c0 guibg=#404080
-hi PmenuSel guifg=#c0c0c0 guibg=#2050d0
-hi PmenuSbar guifg=blue guibg=darkgray
-hi PmenuThumb guifg=#c0c0c0
+" Background: dark
+" Color: white #FFFFFF 231 white
+" Color: black #000000 16 black
+" Color: diffc #0087af 31 darkcyan
+" Color: difft #005f87 24 cyan
+" Color: cursorl #666666 59 darkgrey
+" Color: ogDeepBlue #000040 17 black
+" Color: ogDarkBlue #00008b 18 darkblue
+" Color: ogBlue #0030ff 27 blue
+" Color: ogLightBlue #287eff 33 blue
+" Color: ogDarkGrey #a9a9a9 248 grey
+" Color: ogGrey #808080 102 darkgrey
+" Color: ogLightGrey #c0c0c0 252 grey
+" Color: ogDarkViolet #404080 61 darkblue
+" Color: ogViolet #8080ff 105 blue
+" Color: ogLightViolet #80a0ff 111 blue
+" Color: ogDarkGreen #006400 22 darkgreen
+" Color: ogGreen #90f020 118 green
+" Color: ogDarkCyan #008b8b 30 darkcyan
+" Color: ogCyan #90fff0 123 cyan
+" Color: ogLightCyan #b0ffff 159 cyan
+" Color: ogDarkerRed #8b0000 88 darkred
+" Color: ogDarkRed #d14a14 166 darkred
+" Color: ogRed #ff0000 196 red
+" Color: ogLightRed #ffa0a0 217 red
+" Color: ogDarkMag #8b008b 90 darkmagenta
+" Color: ogMagenta #ff00ff 201 darkmagenta
+" Color: ogLightMag #ff80ff 213 magenta
+" Color: ogOrange #ffa500 214 darkyellow
+" Color: ogYellow #ffff60 227 yellow
+" Term colors: black ogDarkerRed ogGreen ogOrange ogDarkBlue ogDarkMag ogDarkCyan ogLightGrey
+" Term colors: ogGrey ogLightRed ogGreen ogYellow ogBlue ogMagenta ogCyan white
+" Color: bgDiffA #5F875F 65 darkgreen
+" Color: bgDiffC #5F87AF 67 blue
+" Color: bgDiffD #AF5FAF 133 magenta
+" Color: bgDiffT #C6C6C6 251 grey
+" Color: fgDiffW #FFFFFF 231 white
+" Color: fgDiffB #000000 16 black
+" Color: bgDiffC8 #5F87AF 67 darkblue
+" Color: bgDiffD8 #AF5FAF 133 darkmagenta
+" vim: et ts=2 sw=2
diff --git a/runtime/colors/delek.vim b/runtime/colors/delek.vim
index dd3a33a9e8..6bc1d1f699 100644
--- a/runtime/colors/delek.vim
+++ b/runtime/colors/delek.vim
@@ -1,54 +1,407 @@
-" Vim color file
-" Maintainer: David Schweikert <david@schweikert.ch>
-" Last Change: 2014 Mar 19
+" Name: delek
+" Description: Light background colorscheme.
+" Author: Original author David Schweikert <david@schweikert.ch>
+" Maintainer: Original maintainer David Schweikert <david@schweikert.ch>
+" Website: https://github.com/vim/colorschemes
+" License: Same as Vim
+" Last Updated: Sun Jun 12 10:50:30 2022
+
+" Generated by Colortemplate v2.2.0
+
+set background=light
hi clear
+let g:colors_name = 'delek'
+
+let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co > 1 ? &t_Co : 1
+
+if (has('termguicolors') && &termguicolors) || has('gui_running')
+ let g:terminal_ansi_colors = ['#ffffff', '#0000ff', '#00cd00', '#cd00cd', '#008b8b', '#0000ff', '#ff1493', '#bcbcbc', '#ee0000', '#0000ff', '#00cd00', '#cd00cd', '#008b8b', '#0000ff', '#ff1493', '#000000']
+endif
+hi! link Terminal Normal
+hi! link LineNrAbove LineNr
+hi! link LineNrBelow LineNr
+hi! link CurSearch Search
+hi! link CursorLineFold CursorLine
+hi! link CursorLineSign CursorLine
+hi! link ErrorMsg Error
+hi Normal guifg=#000000 guibg=#ffffff gui=NONE cterm=NONE
+hi EndOfBuffer guifg=#bcbcbc guibg=NONE gui=NONE cterm=NONE
+hi StatusLine guifg=#ffff00 guibg=#00008b gui=bold cterm=bold
+hi StatusLineNC guifg=#ffd700 guibg=#00008b gui=NONE cterm=NONE
+hi StatusLineTerm guifg=#ffff00 guibg=#0000ff gui=bold cterm=bold
+hi StatusLineTermNC guifg=#e4e4e4 guibg=#0000ff gui=NONE cterm=NONE
+hi VertSplit guifg=#e4e4e4 guibg=#00008b gui=NONE cterm=NONE
+hi Pmenu guifg=#000000 guibg=#add8e6 gui=NONE cterm=NONE
+hi PmenuSel guifg=#ffffff guibg=#00008b gui=NONE cterm=NONE
+hi PmenuSbar guifg=NONE guibg=#ffffff gui=NONE cterm=NONE
+hi PmenuThumb guifg=NONE guibg=#008b8b gui=NONE cterm=NONE
+hi TabLine guifg=#000000 guibg=#e4e4e4 gui=NONE cterm=NONE
+hi TabLineFill guifg=NONE guibg=#bcbcbc gui=NONE cterm=NONE
+hi TabLineSel guifg=#000000 guibg=#ffffff gui=bold cterm=bold
+hi ToolbarLine guifg=NONE guibg=#e4e4e4 gui=NONE cterm=NONE
+hi ToolbarButton guifg=#ffffff guibg=#bcbcbc gui=bold cterm=bold
+hi NonText guifg=#bcbcbc guibg=NONE gui=NONE cterm=NONE
+hi SpecialKey guifg=#bcbcbc guibg=NONE gui=NONE cterm=NONE
+hi Folded guifg=#00008b guibg=#e4e4e4 gui=NONE cterm=NONE
+hi Visual guifg=#000000 guibg=#d0d0d0 gui=NONE cterm=NONE
+hi VisualNOS guifg=NONE guibg=#ee0000 gui=NONE cterm=NONE
+hi LineNr guifg=#a52a2a guibg=NONE gui=NONE cterm=NONE
+hi FoldColumn guifg=#00008b guibg=NONE gui=NONE cterm=NONE
+hi CursorColumn guifg=NONE guibg=#e4e4e4 gui=NONE cterm=NONE
+hi CursorLineNr guifg=#a52a2a guibg=NONE gui=bold cterm=bold
+hi QuickFixLine guifg=#ffffff guibg=#008b8b gui=NONE cterm=NONE
+hi SignColumn guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
+hi Underlined guifg=#6a5acd guibg=NONE gui=underline cterm=underline
+hi Error guifg=#ff0000 guibg=#ffffff gui=reverse cterm=reverse
+hi WarningMsg guifg=#cd00cd guibg=#ffffff gui=NONE cterm=NONE
+hi MoreMsg guifg=#000000 guibg=#ffffff gui=bold cterm=bold
+hi ModeMsg guifg=#000000 guibg=#ffffff gui=bold cterm=bold
+hi Question guifg=#00cd00 guibg=NONE gui=bold cterm=bold
+hi Todo guifg=#000000 guibg=#ffff00 gui=NONE cterm=NONE
+hi MatchParen guifg=#ffffff guibg=#ff1493 gui=NONE cterm=NONE
+hi Search guifg=#ffffff guibg=#cd00cd gui=NONE cterm=NONE
+hi IncSearch guifg=#00cd00 guibg=NONE gui=reverse cterm=reverse
+hi WildMenu guifg=#00008b guibg=#ffd700 gui=bold cterm=bold
+hi ColorColumn guifg=#000000 guibg=#e4e4e4 gui=NONE cterm=NONE
+hi Cursor guifg=#ffffff guibg=#000000 gui=NONE cterm=NONE
+hi lCursor guifg=#000000 guibg=#00cd00 gui=NONE cterm=NONE
+hi SpellBad guifg=#ff0000 guibg=NONE guisp=#ff0000 gui=undercurl cterm=underline
+hi SpellCap guifg=#008b8b guibg=NONE guisp=#008b8b gui=undercurl cterm=underline
+hi SpellLocal guifg=#0000ff guibg=NONE guisp=#0000ff gui=undercurl cterm=underline
+hi SpellRare guifg=#cd00cd guibg=NONE guisp=#cd00cd gui=undercurl cterm=underline
+hi debugBreakpoint guifg=#ffff00 guibg=#0000ff gui=NONE cterm=NONE
+hi debugPC guifg=#ff1493 guibg=#0000ff gui=NONE cterm=NONE
+hi Comment guifg=#ee0000 guibg=NONE gui=NONE cterm=NONE
+hi Constant guifg=#00cd00 guibg=NONE gui=NONE cterm=NONE
+hi Identifier guifg=#008b8b guibg=NONE gui=NONE cterm=NONE
+hi Statement guifg=#0000ff guibg=NONE gui=bold cterm=bold
+hi PreProc guifg=#cd00cd guibg=NONE gui=NONE cterm=NONE
+hi Type guifg=#0000ff guibg=NONE gui=bold cterm=bold
+hi Special guifg=#ff1493 guibg=NONE gui=NONE cterm=NONE
+hi Directory guifg=#008b8b guibg=NONE gui=bold cterm=bold
+hi Conceal guifg=#ee0000 guibg=NONE gui=NONE cterm=NONE
+hi Ignore guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
+hi Title guifg=#cd00cd guibg=NONE gui=bold cterm=bold
+hi CursorLine guifg=NONE guibg=#e4e4e4 gui=NONE cterm=NONE
+hi DiffAdd guifg=#ffffff guibg=#5f875f gui=NONE cterm=NONE
+hi DiffChange guifg=#ffffff guibg=#5f87af gui=NONE cterm=NONE
+hi DiffText guifg=#000000 guibg=#c6c6c6 gui=NONE cterm=NONE
+hi DiffDelete guifg=#ffffff guibg=#af5faf gui=NONE cterm=NONE
+
+if s:t_Co >= 256
+ hi! link Terminal Normal
+ hi! link LineNrAbove LineNr
+ hi! link LineNrBelow LineNr
+ hi! link CurSearch Search
+ hi! link CursorLineFold CursorLine
+ hi! link CursorLineSign CursorLine
+ hi! link ErrorMsg Error
+ hi Normal ctermfg=16 ctermbg=231 cterm=NONE
+ hi EndOfBuffer ctermfg=250 ctermbg=NONE cterm=NONE
+ hi StatusLine ctermfg=226 ctermbg=18 cterm=bold
+ hi StatusLineNC ctermfg=220 ctermbg=18 cterm=NONE
+ hi StatusLineTerm ctermfg=226 ctermbg=21 cterm=bold
+ hi StatusLineTermNC ctermfg=254 ctermbg=21 cterm=NONE
+ hi VertSplit ctermfg=254 ctermbg=18 cterm=NONE
+ hi Pmenu ctermfg=16 ctermbg=152 cterm=NONE
+ hi PmenuSel ctermfg=231 ctermbg=18 cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=231 cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=30 cterm=NONE
+ hi TabLine ctermfg=16 ctermbg=254 cterm=NONE
+ hi TabLineFill ctermfg=NONE ctermbg=250 cterm=NONE
+ hi TabLineSel ctermfg=16 ctermbg=231 cterm=bold
+ hi ToolbarLine ctermfg=NONE ctermbg=254 cterm=NONE
+ hi ToolbarButton ctermfg=231 ctermbg=250 cterm=bold
+ hi NonText ctermfg=250 ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=250 ctermbg=NONE cterm=NONE
+ hi Folded ctermfg=18 ctermbg=254 cterm=NONE
+ hi Visual ctermfg=16 ctermbg=252 cterm=NONE
+ hi VisualNOS ctermfg=NONE ctermbg=196 cterm=NONE
+ hi LineNr ctermfg=124 ctermbg=NONE cterm=NONE
+ hi FoldColumn ctermfg=18 ctermbg=NONE cterm=NONE
+ hi CursorColumn ctermfg=NONE ctermbg=254 cterm=NONE
+ hi CursorLineNr ctermfg=124 ctermbg=NONE cterm=bold
+ hi QuickFixLine ctermfg=231 ctermbg=30 cterm=NONE
+ hi SignColumn ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=62 ctermbg=NONE cterm=underline
+ hi Error ctermfg=196 ctermbg=231 cterm=reverse
+ hi WarningMsg ctermfg=164 ctermbg=231 cterm=NONE
+ hi MoreMsg ctermfg=16 ctermbg=231 cterm=bold
+ hi ModeMsg ctermfg=16 ctermbg=231 cterm=bold
+ hi Question ctermfg=40 ctermbg=NONE cterm=bold
+ hi Todo ctermfg=16 ctermbg=226 cterm=NONE
+ hi MatchParen ctermfg=231 ctermbg=198 cterm=NONE
+ hi Search ctermfg=231 ctermbg=164 cterm=NONE
+ hi IncSearch ctermfg=40 ctermbg=NONE cterm=reverse
+ hi WildMenu ctermfg=18 ctermbg=220 cterm=bold
+ hi ColorColumn ctermfg=16 ctermbg=254 cterm=NONE
+ hi Cursor ctermfg=231 ctermbg=16 cterm=NONE
+ hi lCursor ctermfg=16 ctermbg=40 cterm=NONE
+ hi SpellBad ctermfg=196 ctermbg=NONE cterm=underline
+ hi SpellCap ctermfg=30 ctermbg=NONE cterm=underline
+ hi SpellLocal ctermfg=21 ctermbg=NONE cterm=underline
+ hi SpellRare ctermfg=164 ctermbg=NONE cterm=underline
+ hi debugBreakpoint ctermfg=226 ctermbg=21 cterm=NONE
+ hi debugPC ctermfg=198 ctermbg=21 cterm=NONE
+ hi Comment ctermfg=196 ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=40 ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=30 ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=21 ctermbg=NONE cterm=bold
+ hi PreProc ctermfg=164 ctermbg=NONE cterm=NONE
+ hi Type ctermfg=21 ctermbg=NONE cterm=bold
+ hi Special ctermfg=198 ctermbg=NONE cterm=NONE
+ hi Directory ctermfg=30 ctermbg=NONE cterm=bold
+ hi Conceal ctermfg=196 ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Title ctermfg=164 ctermbg=NONE cterm=bold
+ hi CursorLine ctermfg=NONE ctermbg=254 cterm=NONE
+ hi DiffAdd ctermfg=231 ctermbg=65 cterm=NONE
+ hi DiffChange ctermfg=231 ctermbg=67 cterm=NONE
+ hi DiffText ctermfg=16 ctermbg=251 cterm=NONE
+ hi DiffDelete ctermfg=231 ctermbg=133 cterm=NONE
+ unlet s:t_Co
+ finish
+endif
+
+if s:t_Co >= 16
+ hi Normal ctermfg=black ctermbg=white cterm=NONE
+ hi EndOfBuffer ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi StatusLine ctermfg=yellow ctermbg=darkblue cterm=bold
+ hi StatusLineNC ctermfg=darkyellow ctermbg=darkblue cterm=NONE
+ hi StatusLineTerm ctermfg=yellow ctermbg=blue cterm=bold
+ hi StatusLineTermNC ctermfg=grey ctermbg=blue cterm=NONE
+ hi VertSplit ctermfg=grey ctermbg=darkblue cterm=NONE
+ hi Pmenu ctermfg=black ctermbg=grey cterm=NONE
+ hi PmenuSel ctermfg=white ctermbg=darkblue cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=white cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=darkcyan cterm=NONE
+ hi TabLine ctermfg=black ctermbg=grey cterm=NONE
+ hi TabLineFill ctermfg=NONE ctermbg=darkgrey cterm=NONE
+ hi TabLineSel ctermfg=black ctermbg=white cterm=bold
+ hi ToolbarLine ctermfg=NONE ctermbg=grey cterm=NONE
+ hi ToolbarButton ctermfg=white ctermbg=darkgrey cterm=bold
+ hi NonText ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi Folded ctermfg=darkblue ctermbg=grey cterm=NONE
+ hi Visual ctermfg=black ctermbg=darkgrey cterm=NONE
+ hi VisualNOS ctermfg=NONE ctermbg=darkred cterm=NONE
+ hi LineNr ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi FoldColumn ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi CursorColumn ctermfg=NONE ctermbg=grey cterm=NONE
+ hi CursorLineNr ctermfg=darkgrey ctermbg=NONE cterm=bold
+ hi QuickFixLine ctermfg=white ctermbg=darkcyan cterm=NONE
+ hi SignColumn ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=blue ctermbg=NONE cterm=underline
+ hi Error ctermfg=red ctermbg=white cterm=reverse
+ hi WarningMsg ctermfg=darkmagenta ctermbg=white cterm=NONE
+ hi MoreMsg ctermfg=black ctermbg=white cterm=bold
+ hi ModeMsg ctermfg=black ctermbg=white cterm=bold
+ hi Question ctermfg=darkgreen ctermbg=NONE cterm=bold
+ hi Todo ctermfg=black ctermbg=yellow cterm=NONE
+ hi MatchParen ctermfg=white ctermbg=magenta cterm=NONE
+ hi Search ctermfg=white ctermbg=darkmagenta cterm=NONE
+ hi IncSearch ctermfg=darkgreen ctermbg=NONE cterm=reverse
+ hi WildMenu ctermfg=darkblue ctermbg=darkyellow cterm=bold
+ hi ColorColumn ctermfg=black ctermbg=grey cterm=NONE
+ hi Cursor ctermfg=white ctermbg=black cterm=NONE
+ hi lCursor ctermfg=black ctermbg=darkgreen cterm=NONE
+ hi SpellBad ctermfg=red ctermbg=NONE cterm=underline
+ hi SpellCap ctermfg=darkcyan ctermbg=NONE cterm=underline
+ hi SpellLocal ctermfg=darkblue ctermbg=NONE cterm=underline
+ hi SpellRare ctermfg=darkmagenta ctermbg=NONE cterm=underline
+ hi debugBreakpoint ctermfg=yellow ctermbg=darkblue cterm=NONE
+ hi debugPC ctermfg=magenta ctermbg=darkblue cterm=NONE
+ hi Comment ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=darkblue ctermbg=NONE cterm=bold
+ hi PreProc ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi Type ctermfg=blue ctermbg=NONE cterm=bold
+ hi Special ctermfg=magenta ctermbg=NONE cterm=NONE
+ hi Directory ctermfg=darkcyan ctermbg=NONE cterm=bold
+ hi Conceal ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Title ctermfg=darkmagenta ctermbg=NONE cterm=bold
+ hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
+ hi Visual ctermfg=white ctermbg=darkgrey cterm=NONE
+ hi DiffAdd ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi DiffChange ctermfg=white ctermbg=blue cterm=NONE
+ hi DiffText ctermfg=black ctermbg=grey cterm=NONE
+ hi DiffDelete ctermfg=white ctermbg=magenta cterm=NONE
+ unlet s:t_Co
+ finish
+endif
-let g:colors_name = "delek"
-
-" Normal should come first
-hi Normal guifg=Black guibg=White
-hi Cursor guifg=bg guibg=fg
-hi lCursor guifg=NONE guibg=Cyan
-
-" Note: we never set 'term' because the defaults for B&W terminals are OK
-hi DiffAdd ctermbg=LightBlue guibg=LightBlue
-hi DiffChange ctermbg=LightMagenta guibg=LightMagenta
-hi DiffDelete ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan
-hi DiffText ctermbg=Red cterm=bold gui=bold guibg=Red
-hi Directory ctermfg=DarkBlue guifg=Blue
-hi ErrorMsg ctermfg=White ctermbg=DarkRed guibg=Red guifg=White
-hi FoldColumn ctermfg=DarkBlue ctermbg=Grey guibg=Grey guifg=DarkBlue
-hi Folded ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue
-hi IncSearch cterm=reverse gui=reverse
-hi LineNr ctermfg=Brown guifg=Brown
-hi ModeMsg cterm=bold gui=bold
-hi MoreMsg ctermfg=DarkGreen gui=bold guifg=SeaGreen
-hi NonText ctermfg=Blue gui=bold guifg=gray guibg=white
-hi Pmenu guibg=LightBlue
-hi PmenuSel ctermfg=White ctermbg=DarkBlue guifg=White guibg=DarkBlue
-hi Question ctermfg=DarkGreen gui=bold guifg=SeaGreen
-if &background == "light"
- hi Search ctermfg=NONE ctermbg=Yellow guibg=Yellow guifg=NONE
-else
- hi Search ctermfg=Black ctermbg=Yellow guibg=Yellow guifg=Black
+if s:t_Co >= 8
+ hi Normal ctermfg=black ctermbg=gray cterm=NONE
+ hi EndOfBuffer ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi StatusLine ctermfg=darkblue ctermbg=darkyellow cterm=bold,reverse
+ hi StatusLineNC ctermfg=darkblue ctermbg=darkyellow cterm=reverse
+ hi StatusLineTerm ctermfg=darkblue ctermbg=darkyellow cterm=bold,reverse
+ hi StatusLineTermNC ctermfg=darkblue ctermbg=darkyellow cterm=reverse
+ hi VertSplit ctermfg=darkblue ctermbg=darkyellow cterm=reverse
+ hi Pmenu ctermfg=black ctermbg=darkcyan cterm=NONE
+ hi PmenuSel ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=darkgreen cterm=NONE
+ hi TabLine ctermfg=gray ctermbg=black cterm=NONE
+ hi TabLineFill ctermfg=NONE ctermbg=black cterm=NONE
+ hi TabLineSel ctermfg=black ctermbg=gray cterm=NONE
+ hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi ToolbarButton ctermfg=black ctermbg=gray cterm=bold,reverse
+ hi NonText ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi Folded ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi VisualNOS ctermfg=NONE ctermbg=NONE cterm=underline
+ hi LineNr ctermfg=NONE ctermbg=NONE cterm=bold
+ hi FoldColumn ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi CursorColumn ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi CursorLineNr ctermfg=NONE ctermbg=NONE cterm=bold
+ hi QuickFixLine ctermfg=black ctermbg=darkcyan cterm=NONE
+ hi SignColumn ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=darkblue ctermbg=NONE cterm=underline
+ hi Error ctermfg=red ctermbg=gray cterm=reverse
+ hi WarningMsg ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi MoreMsg ctermfg=NONE ctermbg=NONE cterm=bold
+ hi ModeMsg ctermfg=NONE ctermbg=NONE cterm=bold
+ hi Question ctermfg=darkgreen ctermbg=NONE cterm=bold
+ hi Todo ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi MatchParen ctermfg=black ctermbg=darkmagenta cterm=bold
+ hi Search ctermfg=black ctermbg=darkmagenta cterm=NONE
+ hi IncSearch ctermfg=darkgreen ctermbg=NONE cterm=reverse
+ hi WildMenu ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi ColorColumn ctermfg=black ctermbg=darkred cterm=NONE
+ hi SpellBad ctermfg=darkred ctermbg=NONE cterm=reverse
+ hi SpellCap ctermfg=darkcyan ctermbg=black cterm=reverse
+ hi SpellLocal ctermfg=darkblue ctermbg=NONE cterm=reverse
+ hi SpellRare ctermfg=darkmagenta ctermbg=NONE cterm=reverse
+ hi debugBreakpoint ctermfg=darkyellow ctermbg=darkblue cterm=NONE
+ hi debugPC ctermfg=darkmagenta ctermbg=darkblue cterm=NONE
+ hi Comment ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=darkblue ctermbg=NONE cterm=bold
+ hi PreProc ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi Type ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi Special ctermfg=darkmagenta ctermbg=NONE cterm=bold
+ hi Directory ctermfg=darkcyan ctermbg=NONE cterm=bold
+ hi Conceal ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Title ctermfg=darkmagenta ctermbg=NONE cterm=bold
+ hi DiffAdd ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi DiffChange ctermfg=white ctermbg=darkblue cterm=NONE
+ hi DiffText ctermfg=black ctermbg=grey cterm=NONE
+ hi DiffDelete ctermfg=white ctermbg=darkmagenta cterm=NONE
+ unlet s:t_Co
+ finish
endif
-hi SpecialKey ctermfg=DarkBlue guifg=Blue
-hi StatusLine cterm=bold ctermbg=blue ctermfg=yellow guibg=gold guifg=blue
-hi StatusLineNC cterm=bold ctermbg=blue ctermfg=black guibg=gold guifg=blue
-hi Title ctermfg=DarkMagenta gui=bold guifg=Magenta
-hi VertSplit cterm=reverse gui=reverse
-hi Visual ctermbg=NONE cterm=reverse gui=reverse guifg=Grey guibg=fg
-hi WarningMsg ctermfg=DarkRed guifg=Red
-hi WildMenu ctermfg=Black ctermbg=Yellow guibg=Yellow guifg=Black
-
-" syntax highlighting
-hi Comment cterm=NONE ctermfg=DarkRed gui=NONE guifg=red2
-hi Constant cterm=NONE ctermfg=DarkGreen gui=NONE guifg=green3
-hi Identifier cterm=NONE ctermfg=DarkCyan gui=NONE guifg=cyan4
-hi PreProc cterm=NONE ctermfg=DarkMagenta gui=NONE guifg=magenta3
-hi Special cterm=NONE ctermfg=LightRed gui=NONE guifg=deeppink
-hi Statement cterm=bold ctermfg=Blue gui=bold guifg=blue
-hi Type cterm=NONE ctermfg=Blue gui=bold guifg=blue
-
-" vim: sw=2
+
+if s:t_Co >= 0
+ hi Normal term=NONE
+ hi ColorColumn term=reverse
+ hi Conceal term=NONE
+ hi Cursor term=reverse
+ hi CursorColumn term=NONE
+ hi CursorLine term=underline
+ hi CursorLineNr term=bold
+ hi DiffAdd term=reverse
+ hi DiffChange term=NONE
+ hi DiffDelete term=reverse
+ hi DiffText term=reverse
+ hi Directory term=NONE
+ hi EndOfBuffer term=NONE
+ hi ErrorMsg term=bold,reverse
+ hi FoldColumn term=NONE
+ hi Folded term=NONE
+ hi IncSearch term=bold,reverse,underline
+ hi LineNr term=NONE
+ hi MatchParen term=bold,underline
+ hi ModeMsg term=bold
+ hi MoreMsg term=NONE
+ hi NonText term=NONE
+ hi Pmenu term=reverse
+ hi PmenuSbar term=reverse
+ hi PmenuSel term=bold
+ hi PmenuThumb term=NONE
+ hi Question term=standout
+ hi Search term=reverse
+ hi SignColumn term=reverse
+ hi SpecialKey term=bold
+ hi SpellBad term=underline
+ hi SpellCap term=underline
+ hi SpellLocal term=underline
+ hi SpellRare term=underline
+ hi StatusLine term=bold,reverse
+ hi StatusLineNC term=bold,underline
+ hi TabLine term=bold,underline
+ hi TabLineFill term=NONE
+ hi Terminal term=NONE
+ hi TabLineSel term=bold,reverse
+ hi Title term=NONE
+ hi VertSplit term=NONE
+ hi Visual term=reverse
+ hi VisualNOS term=NONE
+ hi WarningMsg term=standout
+ hi WildMenu term=bold
+ hi CursorIM term=NONE
+ hi ToolbarLine term=reverse
+ hi ToolbarButton term=bold,reverse
+ hi CurSearch term=reverse
+ hi CursorLineFold term=underline
+ hi CursorLineSign term=underline
+ hi Comment term=bold
+ hi Constant term=NONE
+ hi Error term=bold,reverse
+ hi Identifier term=NONE
+ hi Ignore term=NONE
+ hi PreProc term=NONE
+ hi Special term=NONE
+ hi Statement term=NONE
+ hi Todo term=bold,reverse
+ hi Type term=NONE
+ hi Underlined term=underline
+ unlet s:t_Co
+ finish
+endif
+
+" Background: light
+" Color: comment #ee0000 196 darkred
+" Color: constant #00cd00 40 darkgreen
+" Color: identifier #008b8b 30 darkcyan
+" Color: statement #0000ff 21 darkblue
+" Color: preproc #cd00cd 164 darkmagenta
+" Color: type #0000ff 21 blue
+" Color: special #ff1493 198 magenta
+" Color: fg0 #000000 16 black
+" Color: bg0 #ffffff 231 white
+" Color: bg1 #bcbcbc 250 darkgrey
+" Color: visual #d0d0d0 252 darkgrey
+" Color: folded #e4e4e4 254 grey
+" Color: pmenu #add8e6 152 grey
+" Color: wildmenu #00008b 18 darkblue
+" Color: error #ff0000 196 red
+" Color: status_fg #ffd700 220 darkyellow
+" Color: linenr #a52a2a 124 darkgrey
+" Color: Yellow #ffff00 226 yellow
+" Color: SlateBlue #6a5acd 62 blue
+" Color: black #000000 16 black
+" Color: darkred #870000 88 darkred
+" Color: darkgreen #008700 28 darkgreen
+" Color: darkyellow #878700 100 darkyellow
+" Color: darkblue #000087 18 darkblue
+" Color: darkmagenta #870087 18 darkmagenta
+" Color: darkcyan #008787 30 darkcyan
+" Color: gray #878787 102 gray
+" Term colors: bg0 statement constant preproc identifier type special bg1
+" Term colors: comment statement constant preproc identifier type special fg0
+" Color: bgDiffA #5F875F 65 darkgreen
+" Color: bgDiffC #5F87AF 67 blue
+" Color: bgDiffD #AF5FAF 133 magenta
+" Color: bgDiffT #C6C6C6 251 grey
+" Color: fgDiffW #FFFFFF 231 white
+" Color: fgDiffB #000000 16 black
+" Color: bgDiffC8 #5F87AF 67 darkblue
+" Color: bgDiffD8 #AF5FAF 133 darkmagenta
+" vim: et ts=2 sw=2
diff --git a/runtime/colors/desert.vim b/runtime/colors/desert.vim
index 542e5ae015..6cc7c21ceb 100644
--- a/runtime/colors/desert.vim
+++ b/runtime/colors/desert.vim
@@ -1,106 +1,396 @@
-" Vim color file
-" Maintainer: Hans Fugal <hans@fugal.net>
-" Last Change: $Date: 2004/06/13 19:30:30 $
-" Last Change: $Date: 2004/06/13 19:30:30 $
-" URL: http://hans.fugal.net/vim/colors/desert.vim
-" Version: $Id: desert.vim,v 1.1 2004/06/13 19:30:30 vimboss Exp $
+" Name: desert
+" Description: Light background colorscheme.
+" Author: Original author Hans Fugal <hans@fugal.net>
+" Maintainer: Original maintainer Hans Fugal <hans@fugal.net>
+" Website: https://github.com/vim/colorschemes
+" License: Same as Vim
+" Last Updated: Sun Jun 12 10:51:36 2022
-" cool help screens
-" :he group-name
-" :he highlight-groups
-" :he cterm-colors
+" Generated by Colortemplate v2.2.0
set background=dark
-if version > 580
- " no guarantees for version 5.8 and below, but this makes it stop
- " complaining
- hi clear
- if exists("syntax_on")
- syntax reset
- endif
-endif
-let g:colors_name="desert"
-hi Normal guifg=White guibg=grey20
+hi clear
+let g:colors_name = 'desert'
+
+let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co > 1 ? &t_Co : 1
+
+if (has('termguicolors') && &termguicolors) || has('gui_running')
+ let g:terminal_ansi_colors = ['#7f7f8c', '#cd5c5c', '#9acd32', '#bdb76b', '#75a0ff', '#eeee00', '#cd853f', '#666666', '#8a7f7f', '#ff0000', '#89fb98', '#f0e68c', '#6dceeb', '#ffde9b', '#ffa0a0', '#c2bfa5']
+endif
+hi! link Terminal Normal
+hi! link LineNrAbove LineNr
+hi! link LineNrBelow LineNr
+hi! link CurSearch Search
+hi! link CursorLineFold CursorLine
+hi! link CursorLineSign CursorLine
+hi! link EndOfBuffer NonText
+hi Normal guifg=#ffffff guibg=#333333 gui=NONE cterm=NONE
+hi StatusLine guifg=#333333 guibg=#c2bfa5 gui=NONE cterm=NONE
+hi StatusLineNC guifg=#7f7f8c guibg=#c2bfa5 gui=NONE cterm=NONE
+hi StatusLineTerm guifg=#333333 guibg=#c2bfa5 gui=NONE cterm=NONE
+hi StatusLineTermNC guifg=#ffffff guibg=#c2bfa5 gui=NONE cterm=NONE
+hi VertSplit guifg=#7f7f8c guibg=#c2bfa5 gui=NONE cterm=NONE
+hi Pmenu guifg=#ffffff guibg=#666666 gui=NONE cterm=NONE
+hi PmenuSel guifg=#333333 guibg=#f0e68c gui=NONE cterm=NONE
+hi PmenuSbar guifg=NONE guibg=#333333 gui=NONE cterm=NONE
+hi PmenuThumb guifg=NONE guibg=#c2bfa5 gui=NONE cterm=NONE
+hi TabLine guifg=#333333 guibg=#c2bfa5 gui=NONE cterm=NONE
+hi TabLineFill guifg=NONE guibg=#c2bfa5 gui=NONE cterm=NONE
+hi TabLineSel guifg=#333333 guibg=#f0e68c gui=NONE cterm=NONE
+hi ToolbarLine guifg=NONE guibg=#666666 gui=NONE cterm=NONE
+hi ToolbarButton guifg=#333333 guibg=#ffde9b gui=bold cterm=bold
+hi NonText guifg=#6dceeb guibg=NONE gui=NONE cterm=NONE
+hi SpecialKey guifg=#9acd32 guibg=NONE gui=NONE cterm=NONE
+hi Folded guifg=#eeee00 guibg=#4d4d4d gui=NONE cterm=NONE
+hi Visual guifg=#f0e68c guibg=#6b8e24 gui=NONE cterm=NONE
+hi VisualNOS guifg=#f0e68c guibg=#6dceeb gui=NONE cterm=NONE
+hi LineNr guifg=#eeee00 guibg=NONE gui=NONE cterm=NONE
+hi FoldColumn guifg=#eeee00 guibg=#4d4d4d gui=NONE cterm=NONE
+hi CursorLine guifg=NONE guibg=#666666 gui=NONE cterm=NONE
+hi CursorColumn guifg=NONE guibg=#666666 gui=NONE cterm=NONE
+hi CursorLineNr guifg=#eeee00 guibg=NONE gui=bold cterm=bold
+hi QuickFixLine guifg=#333333 guibg=#f0e68c gui=NONE cterm=NONE
+hi SignColumn guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
+hi Underlined guifg=#75a0ff guibg=NONE gui=underline cterm=underline
+hi Error guifg=#ff0000 guibg=#ffffff gui=reverse cterm=reverse
+hi ErrorMsg guifg=#ff0000 guibg=#ffffff gui=reverse cterm=reverse
+hi ModeMsg guifg=#ffde9b guibg=NONE gui=bold cterm=bold
+hi WarningMsg guifg=#cd5c5c guibg=NONE gui=bold cterm=bold
+hi MoreMsg guifg=#9acd32 guibg=NONE gui=bold cterm=bold
+hi Question guifg=#89fb98 guibg=NONE gui=bold cterm=bold
+hi Todo guifg=#ff0000 guibg=#eeee00 gui=NONE cterm=NONE
+hi MatchParen guifg=#7f7f8c guibg=#bdb76b gui=NONE cterm=NONE
+hi Search guifg=#f0e68c guibg=#7f7f8c gui=NONE cterm=NONE
+hi IncSearch guifg=#f0e68c guibg=#cd853f gui=NONE cterm=NONE
+hi WildMenu guifg=#333333 guibg=#eeee00 gui=NONE cterm=NONE
+hi ColorColumn guifg=#ffffff guibg=#cd5c5c gui=NONE cterm=NONE
+hi Cursor guifg=#333333 guibg=#f0e68c gui=NONE cterm=NONE
+hi lCursor guifg=#333333 guibg=#ff0000 gui=NONE cterm=NONE
+hi debugPC guifg=#666666 guibg=NONE gui=reverse cterm=reverse
+hi debugBreakpoint guifg=#ffa0a0 guibg=NONE gui=reverse cterm=reverse
+hi SpellBad guifg=#cd5c5c guibg=NONE guisp=#cd5c5c gui=undercurl cterm=underline
+hi SpellCap guifg=#75a0ff guibg=NONE guisp=#75a0ff gui=undercurl cterm=underline
+hi SpellLocal guifg=#ffde9b guibg=NONE guisp=#ffde9b gui=undercurl cterm=underline
+hi SpellRare guifg=#9acd32 guibg=NONE guisp=#9acd32 gui=undercurl cterm=underline
+hi Comment guifg=#6dceeb guibg=NONE gui=NONE cterm=NONE
+hi Identifier guifg=#89fb98 guibg=NONE gui=NONE cterm=NONE
+hi Statement guifg=#f0e68c guibg=NONE gui=bold cterm=bold
+hi Constant guifg=#ffa0a0 guibg=NONE gui=NONE cterm=NONE
+hi PreProc guifg=#cd5c5c guibg=NONE gui=NONE cterm=NONE
+hi Type guifg=#bdb76b guibg=NONE gui=bold cterm=bold
+hi Special guifg=#ffde9b guibg=NONE gui=NONE cterm=NONE
+hi Directory guifg=#6dceeb guibg=NONE gui=NONE cterm=NONE
+hi Conceal guifg=#666666 guibg=NONE gui=NONE cterm=NONE
+hi Ignore guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
+hi Title guifg=#cd5c5c guibg=NONE gui=bold cterm=bold
+hi DiffAdd guifg=#ffffff guibg=#5f875f gui=NONE cterm=NONE
+hi DiffChange guifg=#ffffff guibg=#5f87af gui=NONE cterm=NONE
+hi DiffText guifg=#000000 guibg=#c6c6c6 gui=NONE cterm=NONE
+hi DiffDelete guifg=#ffffff guibg=#af5faf gui=NONE cterm=NONE
-" highlight groups
-hi Cursor guibg=khaki guifg=slategrey
-"hi CursorIM
-"hi Directory
-"hi DiffAdd
-"hi DiffChange
-"hi DiffDelete
-"hi DiffText
-"hi ErrorMsg
-hi VertSplit guibg=#c2bfa5 guifg=grey50 gui=none
-hi Folded guibg=grey30 guifg=gold
-hi FoldColumn guibg=grey30 guifg=tan
-hi IncSearch guifg=slategrey guibg=khaki
-"hi LineNr
-hi ModeMsg guifg=goldenrod
-hi MoreMsg guifg=SeaGreen
-hi NonText guifg=LightBlue guibg=grey30
-hi Question guifg=springgreen
-hi Search guibg=peru guifg=wheat
-hi SpecialKey guifg=yellowgreen
-hi StatusLine guibg=#c2bfa5 guifg=black gui=none
-hi StatusLineNC guibg=#c2bfa5 guifg=grey50 gui=none
-hi Title guifg=indianred
-hi Visual gui=none guifg=khaki guibg=olivedrab
-hi WarningMsg guifg=salmon
-"hi WildMenu
-"hi Menu
-"hi Scrollbar
-"hi Tooltip
+if s:t_Co >= 256
+ hi! link Terminal Normal
+ hi! link LineNrAbove LineNr
+ hi! link LineNrBelow LineNr
+ hi! link CurSearch Search
+ hi! link CursorLineFold CursorLine
+ hi! link CursorLineSign CursorLine
+ hi! link EndOfBuffer NonText
+ hi Normal ctermfg=231 ctermbg=236 cterm=NONE
+ hi StatusLine ctermfg=236 ctermbg=144 cterm=NONE
+ hi StatusLineNC ctermfg=242 ctermbg=144 cterm=NONE
+ hi StatusLineTerm ctermfg=236 ctermbg=144 cterm=NONE
+ hi StatusLineTermNC ctermfg=231 ctermbg=144 cterm=NONE
+ hi VertSplit ctermfg=242 ctermbg=144 cterm=NONE
+ hi Pmenu ctermfg=231 ctermbg=241 cterm=NONE
+ hi PmenuSel ctermfg=236 ctermbg=186 cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=236 cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=144 cterm=NONE
+ hi TabLine ctermfg=236 ctermbg=144 cterm=NONE
+ hi TabLineFill ctermfg=NONE ctermbg=144 cterm=NONE
+ hi TabLineSel ctermfg=236 ctermbg=186 cterm=NONE
+ hi ToolbarLine ctermfg=NONE ctermbg=241 cterm=NONE
+ hi ToolbarButton ctermfg=236 ctermbg=222 cterm=bold
+ hi NonText ctermfg=81 ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=112 ctermbg=NONE cterm=NONE
+ hi Folded ctermfg=226 ctermbg=239 cterm=NONE
+ hi Visual ctermfg=186 ctermbg=64 cterm=NONE
+ hi VisualNOS ctermfg=186 ctermbg=81 cterm=NONE
+ hi LineNr ctermfg=226 ctermbg=NONE cterm=NONE
+ hi FoldColumn ctermfg=226 ctermbg=239 cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=241 cterm=NONE
+ hi CursorColumn ctermfg=NONE ctermbg=241 cterm=NONE
+ hi CursorLineNr ctermfg=226 ctermbg=NONE cterm=bold
+ hi QuickFixLine ctermfg=236 ctermbg=186 cterm=NONE
+ hi SignColumn ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=111 ctermbg=NONE cterm=underline
+ hi Error ctermfg=196 ctermbg=231 cterm=reverse
+ hi ErrorMsg ctermfg=196 ctermbg=231 cterm=reverse
+ hi ModeMsg ctermfg=222 ctermbg=NONE cterm=bold
+ hi WarningMsg ctermfg=167 ctermbg=NONE cterm=bold
+ hi MoreMsg ctermfg=112 ctermbg=NONE cterm=bold
+ hi Question ctermfg=120 ctermbg=NONE cterm=bold
+ hi Todo ctermfg=196 ctermbg=226 cterm=NONE
+ hi MatchParen ctermfg=242 ctermbg=143 cterm=NONE
+ hi Search ctermfg=186 ctermbg=242 cterm=NONE
+ hi IncSearch ctermfg=186 ctermbg=172 cterm=NONE
+ hi WildMenu ctermfg=236 ctermbg=226 cterm=NONE
+ hi ColorColumn ctermfg=231 ctermbg=167 cterm=NONE
+ hi debugPC ctermfg=241 ctermbg=NONE cterm=reverse
+ hi debugBreakpoint ctermfg=217 ctermbg=NONE cterm=reverse
+ hi SpellBad ctermfg=167 ctermbg=NONE cterm=underline
+ hi SpellCap ctermfg=111 ctermbg=NONE cterm=underline
+ hi SpellLocal ctermfg=222 ctermbg=NONE cterm=underline
+ hi SpellRare ctermfg=112 ctermbg=NONE cterm=underline
+ hi Comment ctermfg=81 ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=120 ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=186 ctermbg=NONE cterm=bold
+ hi Constant ctermfg=217 ctermbg=NONE cterm=NONE
+ hi PreProc ctermfg=167 ctermbg=NONE cterm=NONE
+ hi Type ctermfg=143 ctermbg=NONE cterm=bold
+ hi Special ctermfg=222 ctermbg=NONE cterm=NONE
+ hi Directory ctermfg=81 ctermbg=NONE cterm=NONE
+ hi Conceal ctermfg=241 ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Title ctermfg=167 ctermbg=NONE cterm=bold
+ hi DiffAdd ctermfg=231 ctermbg=65 cterm=NONE
+ hi DiffChange ctermfg=231 ctermbg=67 cterm=NONE
+ hi DiffText ctermfg=16 ctermbg=251 cterm=NONE
+ hi DiffDelete ctermfg=231 ctermbg=133 cterm=NONE
+ unlet s:t_Co
+ finish
+endif
-" syntax highlighting groups
-hi Comment guifg=SkyBlue
-hi Constant guifg=#ffa0a0
-hi Identifier guifg=palegreen
-hi Statement guifg=khaki
-hi PreProc guifg=indianred
-hi Type guifg=darkkhaki
-hi Special guifg=navajowhite
-"hi Underlined
-hi Ignore guifg=grey40
-"hi Error
-hi Todo guifg=orangered guibg=yellow2
+if s:t_Co >= 16
+ hi Normal ctermfg=white ctermbg=black cterm=NONE
+ hi StatusLine ctermfg=black ctermbg=grey cterm=NONE
+ hi StatusLineNC ctermfg=darkgrey ctermbg=grey cterm=NONE
+ hi StatusLineTerm ctermfg=black ctermbg=grey cterm=NONE
+ hi StatusLineTermNC ctermfg=darkgrey ctermbg=grey cterm=NONE
+ hi VertSplit ctermfg=darkgrey ctermbg=grey cterm=NONE
+ hi Pmenu ctermfg=NONE ctermbg=darkgrey cterm=NONE
+ hi PmenuSel ctermfg=black ctermbg=yellow cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=black cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=white cterm=NONE
+ hi TabLine ctermfg=black ctermbg=grey cterm=NONE
+ hi TabLineFill ctermfg=NONE ctermbg=white cterm=NONE
+ hi TabLineSel ctermfg=white ctermbg=black cterm=NONE
+ hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi ToolbarButton ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi NonText ctermfg=blue ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi Folded ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi Visual ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi VisualNOS ctermfg=NONE ctermbg=NONE cterm=underline
+ hi LineNr ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi FoldColumn ctermfg=darkyellow ctermbg=darkgrey cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorColumn ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi CursorLineNr ctermfg=darkyellow ctermbg=NONE cterm=bold
+ hi QuickFixLine ctermfg=black ctermbg=yellow cterm=NONE
+ hi SignColumn ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=darkblue ctermbg=NONE cterm=underline
+ hi Error ctermfg=red ctermbg=white cterm=reverse
+ hi ErrorMsg ctermfg=red ctermbg=white cterm=reverse
+ hi ModeMsg ctermfg=magenta ctermbg=NONE cterm=bold
+ hi WarningMsg ctermfg=darkred ctermbg=NONE cterm=bold
+ hi MoreMsg ctermfg=darkgreen ctermbg=NONE cterm=bold
+ hi Question ctermfg=green ctermbg=NONE cterm=bold
+ hi Todo ctermfg=red ctermbg=darkmagenta cterm=NONE
+ hi MatchParen ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi Search ctermfg=black ctermbg=darkgreen cterm=NONE
+ hi IncSearch ctermfg=black ctermbg=yellow cterm=NONE
+ hi WildMenu ctermfg=black ctermbg=darkmagenta cterm=NONE
+ hi ColorColumn ctermfg=white ctermbg=darkred cterm=NONE
+ hi debugPC ctermfg=grey ctermbg=NONE cterm=reverse
+ hi debugBreakpoint ctermfg=cyan ctermbg=NONE cterm=reverse
+ hi SpellBad ctermfg=darkred ctermbg=darkyellow cterm=reverse
+ hi SpellCap ctermfg=darkblue ctermbg=grey cterm=reverse
+ hi SpellLocal ctermfg=darkyellow ctermbg=NONE cterm=reverse
+ hi SpellRare ctermfg=darkgreen ctermbg=NONE cterm=reverse
+ hi Comment ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=green ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=yellow ctermbg=NONE cterm=bold
+ hi Constant ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi PreProc ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi Type ctermfg=darkyellow ctermbg=NONE cterm=bold
+ hi Special ctermfg=magenta ctermbg=NONE cterm=NONE
+ hi Directory ctermfg=blue ctermbg=NONE cterm=NONE
+ hi Conceal ctermfg=grey ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Title ctermfg=darkred ctermbg=NONE cterm=bold
+ hi DiffAdd ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi DiffChange ctermfg=white ctermbg=blue cterm=NONE
+ hi DiffText ctermfg=black ctermbg=grey cterm=NONE
+ hi DiffDelete ctermfg=white ctermbg=magenta cterm=NONE
+ unlet s:t_Co
+ finish
+endif
-" color terminal definitions
-hi SpecialKey ctermfg=darkgreen
-hi NonText cterm=bold ctermfg=darkblue
-hi Directory ctermfg=darkcyan
-hi ErrorMsg cterm=bold ctermfg=7 ctermbg=1
-hi IncSearch cterm=NONE ctermfg=yellow ctermbg=green
-hi Search cterm=NONE ctermfg=grey ctermbg=blue
-hi MoreMsg ctermfg=darkgreen
-hi ModeMsg cterm=NONE ctermfg=brown
-hi LineNr ctermfg=3
-hi Question ctermfg=green
-hi StatusLine cterm=bold,reverse
-hi StatusLineNC cterm=reverse
-hi VertSplit cterm=reverse
-hi Title ctermfg=5
-hi Visual cterm=reverse
-hi WarningMsg ctermfg=1
-hi WildMenu ctermfg=0 ctermbg=3
-hi Folded ctermfg=darkgrey ctermbg=NONE
-hi FoldColumn ctermfg=darkgrey ctermbg=NONE
-hi DiffAdd ctermbg=4
-hi DiffChange ctermbg=5
-hi DiffDelete cterm=bold ctermfg=4 ctermbg=6
-hi DiffText cterm=bold ctermbg=1
-hi Comment ctermfg=darkcyan
-hi Constant ctermfg=brown
-hi Special ctermfg=5
-hi Identifier ctermfg=6
-hi Statement ctermfg=3
-hi PreProc ctermfg=5
-hi Type ctermfg=2
-hi Underlined cterm=underline ctermfg=5
-hi Ignore cterm=bold ctermfg=7
-hi Ignore ctermfg=darkgrey
-hi Error cterm=bold ctermfg=7 ctermbg=1
+if s:t_Co >= 8
+ hi Normal ctermfg=grey ctermbg=black cterm=NONE
+ hi StatusLine ctermfg=grey ctermbg=black cterm=bold,reverse
+ hi StatusLineNC ctermfg=grey ctermbg=black cterm=reverse
+ hi StatusLineTerm ctermfg=grey ctermbg=black cterm=bold,reverse
+ hi StatusLineTermNC ctermfg=grey ctermbg=black cterm=reverse
+ hi VertSplit ctermfg=grey ctermbg=black cterm=reverse
+ hi Pmenu ctermfg=black ctermbg=darkcyan cterm=NONE
+ hi PmenuSel ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=black cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=grey cterm=NONE
+ hi TabLine ctermfg=black ctermbg=grey cterm=NONE
+ hi TabLineFill ctermfg=NONE ctermbg=grey cterm=NONE
+ hi TabLineSel ctermfg=grey ctermbg=black cterm=NONE
+ hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi ToolbarButton ctermfg=grey ctermbg=black cterm=bold,reverse
+ hi NonText ctermfg=darkblue ctermbg=NONE cterm=bold
+ hi SpecialKey ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi Folded ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi VisualNOS ctermfg=NONE ctermbg=NONE cterm=underline
+ hi LineNr ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi FoldColumn ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorColumn ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi CursorLineNr ctermfg=darkyellow ctermbg=NONE cterm=bold
+ hi QuickFixLine ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi SignColumn ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline
+ hi Error ctermfg=darkred ctermbg=grey cterm=reverse
+ hi ErrorMsg ctermfg=darkred ctermbg=grey cterm=reverse
+ hi ModeMsg ctermfg=darkmagenta ctermbg=NONE cterm=bold
+ hi WarningMsg ctermfg=darkred ctermbg=NONE cterm=bold
+ hi MoreMsg ctermfg=darkgreen ctermbg=NONE cterm=bold
+ hi Question ctermfg=darkgreen ctermbg=NONE cterm=bold
+ hi Todo ctermfg=darkred ctermbg=darkmagenta cterm=NONE
+ hi MatchParen ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi Search ctermfg=black ctermbg=darkgreen cterm=NONE
+ hi IncSearch ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi WildMenu ctermfg=black ctermbg=darkmagenta cterm=NONE
+ hi ColorColumn ctermfg=grey ctermbg=darkred cterm=NONE
+ hi debugPC ctermfg=grey ctermbg=NONE cterm=reverse
+ hi debugBreakpoint ctermfg=darkcyan ctermbg=NONE cterm=reverse
+ hi SpellBad ctermfg=darkred ctermbg=darkyellow cterm=reverse
+ hi SpellCap ctermfg=darkblue ctermbg=grey cterm=reverse
+ hi SpellLocal ctermfg=darkyellow ctermbg=NONE cterm=reverse
+ hi SpellRare ctermfg=darkgreen ctermbg=NONE cterm=reverse
+ hi Comment ctermfg=darkcyan ctermbg=NONE cterm=bold
+ hi Identifier ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=darkyellow ctermbg=NONE cterm=bold
+ hi Constant ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi PreProc ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi Type ctermfg=darkyellow ctermbg=NONE cterm=bold
+ hi Special ctermfg=darkmagenta ctermbg=NONE cterm=bold
+ hi Directory ctermfg=darkblue ctermbg=NONE cterm=bold
+ hi Conceal ctermfg=grey ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Title ctermfg=darkred ctermbg=NONE cterm=bold
+ hi DiffAdd ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi DiffChange ctermfg=white ctermbg=darkblue cterm=NONE
+ hi DiffText ctermfg=black ctermbg=grey cterm=NONE
+ hi DiffDelete ctermfg=white ctermbg=darkmagenta cterm=NONE
+ unlet s:t_Co
+ finish
+endif
+if s:t_Co >= 0
+ hi Normal term=NONE
+ hi ColorColumn term=reverse
+ hi Conceal term=NONE
+ hi Cursor term=reverse
+ hi CursorColumn term=NONE
+ hi CursorLine term=underline
+ hi CursorLineNr term=bold
+ hi DiffAdd term=reverse
+ hi DiffChange term=NONE
+ hi DiffDelete term=reverse
+ hi DiffText term=reverse
+ hi Directory term=NONE
+ hi EndOfBuffer term=NONE
+ hi ErrorMsg term=bold,reverse
+ hi FoldColumn term=NONE
+ hi Folded term=NONE
+ hi IncSearch term=bold,reverse,underline
+ hi LineNr term=NONE
+ hi MatchParen term=bold,underline
+ hi ModeMsg term=bold
+ hi MoreMsg term=NONE
+ hi NonText term=NONE
+ hi Pmenu term=reverse
+ hi PmenuSbar term=reverse
+ hi PmenuSel term=bold
+ hi PmenuThumb term=NONE
+ hi Question term=standout
+ hi Search term=reverse
+ hi SignColumn term=reverse
+ hi SpecialKey term=bold
+ hi SpellBad term=underline
+ hi SpellCap term=underline
+ hi SpellLocal term=underline
+ hi SpellRare term=underline
+ hi StatusLine term=bold,reverse
+ hi StatusLineNC term=bold,underline
+ hi TabLine term=bold,underline
+ hi TabLineFill term=NONE
+ hi Terminal term=NONE
+ hi TabLineSel term=bold,reverse
+ hi Title term=NONE
+ hi VertSplit term=NONE
+ hi Visual term=reverse
+ hi VisualNOS term=NONE
+ hi WarningMsg term=standout
+ hi WildMenu term=bold
+ hi CursorIM term=NONE
+ hi ToolbarLine term=reverse
+ hi ToolbarButton term=bold,reverse
+ hi CurSearch term=reverse
+ hi CursorLineFold term=underline
+ hi CursorLineSign term=underline
+ hi Comment term=bold
+ hi Constant term=NONE
+ hi Error term=bold,reverse
+ hi Identifier term=NONE
+ hi Ignore term=NONE
+ hi PreProc term=NONE
+ hi Special term=NONE
+ hi Statement term=NONE
+ hi Todo term=bold,reverse
+ hi Type term=NONE
+ hi Underlined term=underline
+ unlet s:t_Co
+ finish
+endif
-"vim: sw=4
+" Background: dark
+" Color: foreground #ffffff 231 white
+" Color: background #333333 236 black
+" Color: color00 #7f7f8c 242 black
+" Color: color08 #8a7f7f 244 darkgrey
+" Color: color01 #cd5c5c 167 darkred
+" Color: color09 #ff0000 196 red
+" Color: color02 #9acd32 112 darkgreen
+" Color: color10 #89fb98 120 green
+" Color: color03 #bdb76b 143 darkyellow
+" Color: color11 #f0e68c 186 yellow
+" Color: color04 #75a0ff 111 darkblue
+" Color: color12 #6dceeb 81 blue
+" Color: color05 #eeee00 226 darkmagenta
+" Color: color13 #ffde9b 222 magenta
+" Color: color06 #cd853f 172 darkcyan
+" Color: color14 #ffa0a0 217 cyan
+" Color: color07 #666666 241 grey
+" Color: color15 #c2bfa5 144 white
+" Color: color16 #6b8e24 64 darkgreen
+" Color: color17 #4d4d4d 239 grey
+" Term colors: color00 color01 color02 color03 color04 color05 color06 color07
+" Term colors: color08 color09 color10 color11 color12 color13 color14 color15
+" Color: bgDiffA #5F875F 65 darkgreen
+" Color: bgDiffC #5F87AF 67 blue
+" Color: bgDiffD #AF5FAF 133 magenta
+" Color: bgDiffT #C6C6C6 251 grey
+" Color: fgDiffW #FFFFFF 231 white
+" Color: fgDiffB #000000 16 black
+" Color: bgDiffC8 #5F87AF 67 darkblue
+" Color: bgDiffD8 #AF5FAF 133 darkmagenta
+" vim: et ts=2 sw=2
diff --git a/runtime/colors/elflord.vim b/runtime/colors/elflord.vim
index f74a828947..54a6afbd79 100644
--- a/runtime/colors/elflord.vim
+++ b/runtime/colors/elflord.vim
@@ -1,50 +1,447 @@
-" local syntax file - set colors on a per-machine basis:
-" vim: tw=0 ts=4 sw=4
-" Vim color file
-" Maintainer: Ron Aaron <ron@ronware.org>
-" Last Change: 2003 May 02
+" Name: elflord
+" Author: original author Ron Aaron <ron@ronware.org>
+" Maintainer: original maintainer Ron Aaron <ron@ronware.org>
+" Website: https://www.github.com/vim/colorschemes
+" License: Same as Vim
+" Last Updated: Sun Jun 12 10:48:00 2022
+
+" Generated by Colortemplate v2.2.0
set background=dark
+
hi clear
-if exists("syntax_on")
- syntax reset
+let g:colors_name = 'elflord'
+
+let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co > 1 ? &t_Co : 1
+
+hi! link Terminal Normal
+hi! link Boolean Constant
+hi! link Character Constant
+hi! link Conditional Repeat
+hi! link Debug Special
+hi! link Define PreProc
+hi! link Delimiter Special
+hi! link Exception Statement
+hi! link Float Number
+hi! link Include PreProc
+hi! link Keyword Statement
+hi! link Label Statement
+hi! link Macro PreProc
+hi! link Number Constant
+hi! link PopupSelected PmenuSel
+hi! link PreCondit PreProc
+hi! link SpecialChar Special
+hi! link SpecialComment Special
+hi! link StatusLineTerm StatusLine
+hi! link StatusLineTermNC StatusLineNC
+hi! link StorageClass Type
+hi! link String Constant
+hi! link Structure Type
+hi! link Tag Special
+hi! link Typedef Type
+hi! link lCursor Cursor
+hi! link CurSearch Search
+hi! link CursorLineFold CursorLine
+hi! link CursorLineSign CursorLine
+
+if (has('termguicolors') && &termguicolors) || has('gui_running')
+ let g:terminal_ansi_colors = ['#000000', '#cd0000', '#00cd00', '#cdcd00', '#0000ee', '#cd00cd', '#00cdcd', '#e5e5e5', '#7f7f7f', '#ff0000', '#00ff00', '#ffff00', '#5c5cff', '#ff00ff', '#00ffff', '#ffffff']
+endif
+hi Normal guifg=#00ffff guibg=#000000 gui=NONE cterm=NONE
+hi QuickFixLine guifg=#ffffff guibg=#2e8b57 gui=NONE cterm=NONE
+hi ColorColumn guifg=NONE guibg=#cd0000 gui=NONE cterm=NONE
+hi CursorColumn guifg=NONE guibg=#3a3a3a gui=NONE cterm=NONE
+hi CursorLine guifg=NONE guibg=#3a3a3a gui=NONE cterm=NONE
+hi CursorLineNr guifg=#ffff00 guibg=NONE gui=bold cterm=bold
+hi Folded guifg=#00ffff guibg=#666666 gui=NONE cterm=NONE
+hi Conceal guifg=#666666 guibg=NONE gui=NONE cterm=NONE
+hi Cursor guifg=#000000 guibg=#00ffff gui=NONE cterm=NONE
+hi Directory guifg=#00ffff guibg=#000000 gui=NONE cterm=NONE
+hi EndOfBuffer guifg=#0000ff guibg=#000000 gui=bold cterm=NONE
+hi ErrorMsg guifg=#ffffff guibg=#cd0000 gui=NONE cterm=NONE
+hi FoldColumn guifg=#00ffff guibg=NONE gui=NONE cterm=NONE
+hi IncSearch guifg=NONE guibg=#000000 gui=reverse cterm=reverse
+hi LineNr guifg=#ffff00 guibg=NONE gui=NONE cterm=NONE
+hi MatchParen guifg=NONE guibg=#008b8b gui=NONE cterm=NONE
+hi ModeMsg guifg=NONE guibg=NONE gui=bold ctermfg=NONE ctermbg=NONE cterm=bold
+hi MoreMsg guifg=#2e8b57 guibg=NONE gui=bold cterm=bold
+hi NonText guifg=#0000ff guibg=NONE gui=bold cterm=bold
+hi Pmenu guifg=#ffffff guibg=#444444 gui=NONE cterm=NONE
+hi PmenuSbar guifg=NONE guibg=#bebebe gui=NONE cterm=NONE
+hi PmenuSel guifg=#000000 guibg=#00cdcd gui=NONE cterm=NONE
+hi PmenuThumb guifg=NONE guibg=#ffffff gui=NONE cterm=NONE
+hi Question guifg=#00ff00 guibg=NONE gui=bold cterm=bold
+hi Search guifg=#000000 guibg=#ffff00 gui=NONE cterm=NONE
+hi SignColumn guifg=#00ffff guibg=NONE gui=NONE cterm=NONE
+hi SpecialKey guifg=#00ffff guibg=NONE gui=NONE cterm=NONE
+hi SpellBad guifg=#ff0000 guibg=NONE guisp=#ff0000 gui=undercurl cterm=underline
+hi SpellCap guifg=#0000ff guibg=NONE guisp=#0000ff gui=undercurl cterm=underline
+hi SpellLocal guifg=#ffff00 guibg=NONE guisp=#ffff00 gui=undercurl cterm=underline
+hi SpellRare guifg=#ff00ff guibg=NONE guisp=#ff00ff gui=undercurl cterm=underline
+hi StatusLine guifg=#000000 guibg=#00ffff gui=bold cterm=bold
+hi StatusLineNC guifg=#000000 guibg=#00cdcd gui=NONE cterm=NONE
+hi TabLine guifg=#000000 guibg=#008b8b gui=NONE cterm=NONE
+hi TabLineFill guifg=NONE guibg=#000000 gui=reverse cterm=reverse
+hi TabLineSel guifg=#00ffff guibg=#000000 gui=bold cterm=bold
+hi Terminal guifg=#00ffff guibg=#000000 gui=NONE cterm=NONE
+hi Title guifg=#ff00ff guibg=NONE gui=bold cterm=bold
+hi VertSplit guifg=#000000 guibg=#00cdcd gui=NONE cterm=NONE
+hi Visual guifg=#000000 guibg=#a9a9a9 gui=NONE cterm=NONE
+hi VisualNOS guifg=NONE guibg=#000000 gui=bold,underline cterm=underline
+hi WarningMsg guifg=#ff0000 guibg=NONE gui=NONE cterm=NONE
+hi WildMenu guifg=#000000 guibg=#ffff00 gui=NONE cterm=NONE
+hi Comment guifg=#80a0ff guibg=NONE gui=NONE cterm=NONE
+hi Constant guifg=#ff00ff guibg=NONE gui=NONE cterm=NONE
+hi Error guifg=#ffffff guibg=#ff0000 gui=NONE cterm=NONE
+hi Function guifg=#ffffff guibg=NONE gui=NONE cterm=NONE
+hi Identifier guifg=#40ffff guibg=NONE gui=NONE cterm=NONE
+hi Ignore guifg=#000000 guibg=#000000 gui=NONE cterm=NONE
+hi Operator guifg=#ff0000 guibg=NONE gui=NONE cterm=NONE
+hi PreProc guifg=#ff80ff guibg=NONE gui=NONE cterm=NONE
+hi Repeat guifg=#ffffff guibg=NONE gui=NONE cterm=NONE
+hi Special guifg=#ff0000 guibg=NONE gui=NONE cterm=NONE
+hi Statement guifg=#aa4444 guibg=NONE gui=bold cterm=bold
+hi Todo guifg=#0000ff guibg=#ffff00 gui=NONE cterm=NONE
+hi Type guifg=#60ff60 guibg=NONE gui=bold cterm=bold
+hi Underlined guifg=#80a0ff guibg=NONE gui=underline cterm=underline
+hi CursorIM guifg=NONE guibg=fg gui=NONE cterm=NONE
+hi ToolbarLine guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
+hi ToolbarButton guifg=#000000 guibg=#e5e5e5 gui=bold cterm=bold
+hi! link LineNrAbove LineNr
+hi! link LineNrBelow LineNr
+hi DiffAdd guifg=#ffffff guibg=#5f875f gui=NONE cterm=NONE
+hi DiffChange guifg=#ffffff guibg=#5f87af gui=NONE cterm=NONE
+hi DiffText guifg=#000000 guibg=#c6c6c6 gui=NONE cterm=NONE
+hi DiffDelete guifg=#ffffff guibg=#af5faf gui=NONE cterm=NONE
+
+if s:t_Co >= 256
+ hi Normal ctermfg=51 ctermbg=16 cterm=NONE
+ hi QuickFixLine ctermfg=231 ctermbg=29 cterm=NONE
+ hi ColorColumn ctermfg=NONE ctermbg=160 cterm=NONE
+ hi CursorColumn ctermfg=NONE ctermbg=237 cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=237 cterm=NONE
+ hi CursorLineNr ctermfg=226 ctermbg=NONE cterm=bold
+ hi Folded ctermfg=51 ctermbg=59 cterm=NONE
+ hi Conceal ctermfg=59 ctermbg=NONE cterm=NONE
+ hi Cursor ctermfg=16 ctermbg=51 cterm=NONE
+ hi Directory ctermfg=51 ctermbg=16 cterm=NONE
+ hi EndOfBuffer ctermfg=21 ctermbg=16 cterm=NONE
+ hi ErrorMsg ctermfg=231 ctermbg=160 cterm=NONE
+ hi FoldColumn ctermfg=51 ctermbg=NONE cterm=NONE
+ hi IncSearch ctermfg=NONE ctermbg=16 cterm=reverse
+ hi LineNr ctermfg=226 ctermbg=NONE cterm=NONE
+ hi MatchParen ctermfg=NONE ctermbg=30 cterm=NONE
+ hi ModeMsg ctermfg=NONE ctermbg=NONE cterm=bold
+ hi MoreMsg ctermfg=29 ctermbg=NONE cterm=bold
+ hi NonText ctermfg=21 ctermbg=NONE cterm=bold
+ hi Pmenu ctermfg=231 ctermbg=238 cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=250 cterm=NONE
+ hi PmenuSel ctermfg=16 ctermbg=44 cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=231 cterm=NONE
+ hi Question ctermfg=46 ctermbg=NONE cterm=bold
+ hi Search ctermfg=16 ctermbg=226 cterm=NONE
+ hi SignColumn ctermfg=51 ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=51 ctermbg=NONE cterm=NONE
+ hi SpellBad ctermfg=196 ctermbg=NONE cterm=underline
+ hi SpellCap ctermfg=21 ctermbg=NONE cterm=underline
+ hi SpellLocal ctermfg=226 ctermbg=NONE cterm=underline
+ hi SpellRare ctermfg=201 ctermbg=NONE cterm=underline
+ hi StatusLine ctermfg=16 ctermbg=51 cterm=bold
+ hi StatusLineNC ctermfg=16 ctermbg=44 cterm=NONE
+ hi TabLine ctermfg=16 ctermbg=30 cterm=NONE
+ hi TabLineFill ctermfg=NONE ctermbg=16 cterm=reverse
+ hi TabLineSel ctermfg=51 ctermbg=16 cterm=bold
+ hi Terminal ctermfg=51 ctermbg=16 cterm=NONE
+ hi Title ctermfg=201 ctermbg=NONE cterm=bold
+ hi VertSplit ctermfg=16 ctermbg=44 cterm=NONE
+ hi Visual ctermfg=16 ctermbg=145 cterm=NONE
+ hi VisualNOS ctermfg=NONE ctermbg=16 cterm=underline
+ hi WarningMsg ctermfg=196 ctermbg=NONE cterm=NONE
+ hi WildMenu ctermfg=16 ctermbg=226 cterm=NONE
+ hi Comment ctermfg=111 ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=201 ctermbg=NONE cterm=NONE
+ hi Error ctermfg=231 ctermbg=196 cterm=NONE
+ hi Function ctermfg=231 ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=87 ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=16 ctermbg=16 cterm=NONE
+ hi Operator ctermfg=196 ctermbg=NONE cterm=NONE
+ hi PreProc ctermfg=213 ctermbg=NONE cterm=NONE
+ hi Repeat ctermfg=231 ctermbg=NONE cterm=NONE
+ hi Special ctermfg=196 ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=131 ctermbg=NONE cterm=bold
+ hi Todo ctermfg=21 ctermbg=226 cterm=NONE
+ hi Type ctermfg=83 ctermbg=NONE cterm=bold
+ hi Underlined ctermfg=111 ctermbg=NONE cterm=underline
+ hi CursorIM ctermfg=NONE ctermbg=fg cterm=NONE
+ hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi ToolbarButton ctermfg=16 ctermbg=254 cterm=bold
+ hi! link LineNrAbove LineNr
+ hi! link LineNrBelow LineNr
+ hi DiffAdd ctermfg=231 ctermbg=65 cterm=NONE
+ hi DiffChange ctermfg=231 ctermbg=67 cterm=NONE
+ hi DiffText ctermfg=16 ctermbg=251 cterm=NONE
+ hi DiffDelete ctermfg=231 ctermbg=133 cterm=NONE
+ unlet s:t_Co
+ finish
endif
-let g:colors_name = "elflord"
-hi Normal guifg=cyan guibg=black
-hi Comment term=bold ctermfg=DarkCyan guifg=#80a0ff
-hi Constant term=underline ctermfg=Magenta guifg=Magenta
-hi Special term=bold ctermfg=DarkMagenta guifg=Red
-hi Identifier term=underline cterm=bold ctermfg=Cyan guifg=#40ffff
-hi Statement term=bold ctermfg=Yellow gui=bold guifg=#aa4444
-hi PreProc term=underline ctermfg=LightBlue guifg=#ff80ff
-hi Type term=underline ctermfg=LightGreen guifg=#60ff60 gui=bold
-hi Function term=bold ctermfg=White guifg=White
-hi Repeat term=underline ctermfg=White guifg=white
-hi Operator ctermfg=Red guifg=Red
-hi Ignore ctermfg=black guifg=bg
-hi Error term=reverse ctermbg=Red ctermfg=White guibg=Red guifg=White
-hi Todo term=standout ctermbg=Yellow ctermfg=Black guifg=Blue guibg=Yellow
-
-" Common groups that link to default highlighting.
-" You can specify other highlighting easily.
-hi link String Constant
-hi link Character Constant
-hi link Number Constant
-hi link Boolean Constant
-hi link Float Number
-hi link Conditional Repeat
-hi link Label Statement
-hi link Keyword Statement
-hi link Exception Statement
-hi link Include PreProc
-hi link Define PreProc
-hi link Macro PreProc
-hi link PreCondit PreProc
-hi link StorageClass Type
-hi link Structure Type
-hi link Typedef Type
-hi link Tag Special
-hi link SpecialChar Special
-hi link Delimiter Special
-hi link SpecialComment Special
-hi link Debug Special
+
+if s:t_Co >= 16
+ hi Normal ctermfg=cyan ctermbg=black cterm=NONE
+ hi QuickFixLine ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi ColorColumn ctermfg=cyan ctermbg=darkred cterm=NONE
+ hi CursorColumn ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorLineNr ctermfg=yellow ctermbg=NONE cterm=underline
+ hi Folded ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Conceal ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi Cursor ctermfg=black ctermbg=cyan cterm=NONE
+ hi Directory ctermfg=cyan ctermbg=black cterm=NONE
+ hi EndOfBuffer ctermfg=darkblue ctermbg=black cterm=NONE
+ hi ErrorMsg ctermfg=white ctermbg=darkred cterm=NONE
+ hi FoldColumn ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi IncSearch ctermfg=NONE ctermbg=black cterm=reverse
+ hi LineNr ctermfg=yellow ctermbg=NONE cterm=NONE
+ hi MatchParen ctermfg=NONE ctermbg=darkcyan cterm=NONE
+ hi ModeMsg ctermfg=NONE ctermbg=NONE cterm=bold
+ hi MoreMsg ctermfg=darkgreen ctermbg=NONE cterm=bold
+ hi NonText ctermfg=darkblue ctermbg=NONE cterm=bold
+ hi Pmenu ctermfg=white ctermbg=darkgrey cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=grey cterm=NONE
+ hi PmenuSel ctermfg=black ctermbg=darkcyan cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=white cterm=NONE
+ hi Question ctermfg=green ctermbg=NONE cterm=bold
+ hi Search ctermfg=black ctermbg=yellow cterm=NONE
+ hi SignColumn ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi SpellBad ctermfg=red ctermbg=NONE cterm=underline
+ hi SpellCap ctermfg=darkblue ctermbg=NONE cterm=underline
+ hi SpellLocal ctermfg=yellow ctermbg=NONE cterm=underline
+ hi SpellRare ctermfg=magenta ctermbg=NONE cterm=underline
+ hi StatusLine ctermfg=black ctermbg=cyan cterm=bold
+ hi StatusLineNC ctermfg=black ctermbg=darkcyan cterm=NONE
+ hi TabLine ctermfg=black ctermbg=darkcyan cterm=NONE
+ hi TabLineFill ctermfg=NONE ctermbg=black cterm=reverse
+ hi TabLineSel ctermfg=cyan ctermbg=black cterm=bold
+ hi Terminal ctermfg=cyan ctermbg=black cterm=NONE
+ hi Title ctermfg=magenta ctermbg=NONE cterm=bold
+ hi VertSplit ctermfg=black ctermbg=darkcyan cterm=NONE
+ hi Visual ctermfg=black ctermbg=darkgrey cterm=NONE
+ hi VisualNOS ctermfg=NONE ctermbg=black cterm=underline
+ hi WarningMsg ctermfg=red ctermbg=NONE cterm=NONE
+ hi WildMenu ctermfg=black ctermbg=yellow cterm=NONE
+ hi Comment ctermfg=blue ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=magenta ctermbg=NONE cterm=NONE
+ hi Error ctermfg=white ctermbg=red cterm=NONE
+ hi Function ctermfg=white ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=black ctermbg=black cterm=NONE
+ hi Operator ctermfg=red ctermbg=NONE cterm=NONE
+ hi PreProc ctermfg=magenta ctermbg=NONE cterm=NONE
+ hi Repeat ctermfg=white ctermbg=NONE cterm=NONE
+ hi Special ctermfg=red ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=darkred ctermbg=NONE cterm=bold
+ hi Todo ctermfg=blue ctermbg=yellow cterm=NONE
+ hi Type ctermfg=green ctermbg=NONE cterm=bold
+ hi Underlined ctermfg=blue ctermbg=NONE cterm=underline
+ hi CursorIM ctermfg=NONE ctermbg=fg cterm=NONE
+ hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi ToolbarButton ctermfg=black ctermbg=grey cterm=bold
+ hi! link LineNrAbove LineNr
+ hi! link LineNrBelow LineNr
+ hi DiffAdd ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi DiffChange ctermfg=white ctermbg=blue cterm=NONE
+ hi DiffText ctermfg=black ctermbg=grey cterm=NONE
+ hi DiffDelete ctermfg=white ctermbg=magenta cterm=NONE
+ unlet s:t_Co
+ finish
+endif
+
+if s:t_Co >= 8
+ hi Normal ctermfg=darkcyan ctermbg=black cterm=NONE
+ hi QuickFixLine ctermfg=grey ctermbg=darkgreen cterm=NONE
+ hi ColorColumn ctermfg=darkcyan ctermbg=darkred cterm=NONE
+ hi CursorColumn ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorLineNr ctermfg=darkyellow ctermbg=NONE cterm=underline
+ hi Folded ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Conceal ctermfg=grey ctermbg=NONE cterm=NONE
+ hi Directory ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi EndOfBuffer ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi ErrorMsg ctermfg=grey ctermbg=darkred cterm=NONE
+ hi FoldColumn ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi IncSearch ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi LineNr ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi MatchParen ctermfg=black ctermbg=darkcyan cterm=NONE
+ hi ModeMsg ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi MoreMsg ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi NonText ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi Pmenu ctermfg=grey ctermbg=NONE cterm=NONE
+ hi PmenuSbar ctermfg=grey ctermbg=grey cterm=NONE
+ hi PmenuSel ctermfg=black ctermbg=darkcyan cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=darkcyan cterm=NONE
+ hi Question ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi Search ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi SignColumn ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi SpellBad ctermfg=darkred ctermbg=darkyellow cterm=reverse
+ hi SpellCap ctermfg=darkblue ctermbg=darkyellow cterm=reverse
+ hi SpellLocal ctermfg=darkyellow ctermbg=NONE cterm=reverse
+ hi SpellRare ctermfg=darkmagenta ctermbg=darkyellow cterm=reverse
+ hi StatusLine ctermfg=darkcyan ctermbg=NONE cterm=bold,reverse
+ hi StatusLineNC ctermfg=black ctermbg=darkcyan cterm=NONE
+ hi TabLine ctermfg=black ctermbg=darkcyan cterm=NONE
+ hi TabLineFill ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi TabLineSel ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Terminal ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Title ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi VertSplit ctermfg=black ctermbg=darkcyan cterm=NONE
+ hi Visual ctermfg=black ctermbg=grey cterm=NONE
+ hi VisualNOS ctermfg=NONE ctermbg=NONE cterm=underline
+ hi WarningMsg ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi WildMenu ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi Comment ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi Error ctermfg=grey ctermbg=darkred cterm=NONE
+ hi Function ctermfg=grey ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=black ctermbg=NONE cterm=NONE
+ hi Operator ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi PreProc ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi Repeat ctermfg=grey ctermbg=NONE cterm=NONE
+ hi Special ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi Todo ctermfg=darkblue ctermbg=darkyellow cterm=NONE
+ hi Type ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=darkblue ctermbg=NONE cterm=underline
+ hi CursorIM ctermfg=NONE ctermbg=fg cterm=NONE
+ hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi ToolbarButton ctermfg=black ctermbg=grey cterm=NONE
+ hi! link LineNrAbove LineNr
+ hi! link LineNrBelow LineNr
+ hi DiffAdd ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi DiffChange ctermfg=white ctermbg=darkblue cterm=NONE
+ hi DiffText ctermfg=black ctermbg=grey cterm=NONE
+ hi DiffDelete ctermfg=white ctermbg=darkmagenta cterm=NONE
+ unlet s:t_Co
+ finish
+endif
+
+if s:t_Co >= 0
+ hi Normal term=NONE
+ hi ColorColumn term=reverse
+ hi Conceal term=NONE
+ hi Cursor term=reverse
+ hi CursorColumn term=NONE
+ hi CursorLine term=underline
+ hi CursorLineNr term=bold
+ hi DiffAdd term=reverse
+ hi DiffChange term=NONE
+ hi DiffDelete term=reverse
+ hi DiffText term=reverse
+ hi Directory term=NONE
+ hi EndOfBuffer term=NONE
+ hi ErrorMsg term=bold,reverse
+ hi FoldColumn term=NONE
+ hi Folded term=NONE
+ hi IncSearch term=bold,reverse,underline
+ hi LineNr term=NONE
+ hi MatchParen term=bold,underline
+ hi ModeMsg term=bold
+ hi MoreMsg term=NONE
+ hi NonText term=NONE
+ hi Pmenu term=reverse
+ hi PmenuSbar term=reverse
+ hi PmenuSel term=bold
+ hi PmenuThumb term=NONE
+ hi Question term=standout
+ hi Search term=reverse
+ hi SignColumn term=reverse
+ hi SpecialKey term=bold
+ hi SpellBad term=underline
+ hi SpellCap term=underline
+ hi SpellLocal term=underline
+ hi SpellRare term=underline
+ hi StatusLine term=bold,reverse
+ hi StatusLineNC term=bold,underline
+ hi TabLine term=bold,underline
+ hi TabLineFill term=NONE
+ hi Terminal term=NONE
+ hi TabLineSel term=bold,reverse
+ hi Title term=NONE
+ hi VertSplit term=NONE
+ hi Visual term=reverse
+ hi VisualNOS term=NONE
+ hi WarningMsg term=standout
+ hi WildMenu term=bold
+ hi CursorIM term=NONE
+ hi ToolbarLine term=reverse
+ hi ToolbarButton term=bold,reverse
+ hi CurSearch term=reverse
+ hi CursorLineFold term=underline
+ hi CursorLineSign term=underline
+ hi Comment term=bold
+ hi Constant term=NONE
+ hi Error term=bold,reverse
+ hi Identifier term=NONE
+ hi Ignore term=NONE
+ hi PreProc term=NONE
+ hi Special term=NONE
+ hi Statement term=NONE
+ hi Todo term=bold,reverse
+ hi Type term=NONE
+ hi Underlined term=underline
+ unlet s:t_Co
+ finish
+endif
+
+" Background: dark
+" Color: xterm0 #000000 16 black
+" Color: xterm1 #cd0000 160 darkred
+" Color: xterm2 #00cd00 40 darkgreen
+" Color: xterm3 #cdcd00 184 darkyellow
+" Color: xterm4 #0000ee 20 darkblue
+" Color: xterm5 #cd00cd 164 darkmagenta
+" Color: xterm6 #00cdcd 44 darkcyan
+" Color: xterm7 #e5e5e5 254 grey
+" Color: xterm8 #7f7f7f 102 darkgrey
+" Color: xterm9 #ff0000 196 red
+" Color: xterm10 #00ff00 46 green
+" Color: xterm11 #ffff00 226 yellow
+" Color: xterm12 #5c5cff 63 blue
+" Color: xterm13 #ff00ff 201 magenta
+" Color: xterm14 #00ffff 51 cyan
+" Color: xterm15 #ffffff 231 white
+" Color: Pmenu #444444 238 darkgrey
+" Color: CursorLine #3a3a3a 237 darkgrey
+" Color: rgbGrey40 #666666 59 darkgrey
+" Color: rgbDarkGrey #a9a9a9 145 darkgrey
+" Color: rgbBlue #0000ff 21 darkblue
+" Color: rgbDarkCyan #008b8b 30 darkcyan
+" Color: Directory #00ffff 51 cyan
+" Color: rgbSeaGreen #2e8b57 29 darkgreen
+" Color: rgbGrey #bebebe 250 grey
+" Color: Question #00ff00 46 green
+" Color: SignColumn #a9a9a9 248 grey
+" Color: SpecialKey #00ffff 51 cyan
+" Color: Title #ff00ff 201 magenta
+" Color: WarningMsg #ff0000 196 red
+" Color: ToolbarLine #7f7f7f 244 darkgrey
+" Color: Underlined #80a0ff 111 blue
+" Color: elfComment #80a0ff 111 blue
+" Color: elfIdentifier #40ffff 87 cyan
+" Color: elfStatement #aa4444 131 darkred
+" Color: elfPreProc #ff80ff 213 magenta
+" Color: elfType #60ff60 83 green
+" Color: elfBlue #0000ff 21 blue
+" Term colors: xterm0 xterm1 xterm2 xterm3 xterm4 xterm5 xterm6 xterm7
+" Term colors: xterm8 xterm9 xterm10 xterm11 xterm12 xterm13
+" Term colors: xterm14 xterm15
+" Color: bgDiffA #5F875F 65 darkgreen
+" Color: bgDiffC #5F87AF 67 blue
+" Color: bgDiffD #AF5FAF 133 magenta
+" Color: bgDiffT #C6C6C6 251 grey
+" Color: fgDiffW #FFFFFF 231 white
+" Color: fgDiffB #000000 16 black
+" Color: bgDiffC8 #5F87AF 67 darkblue
+" Color: bgDiffD8 #AF5FAF 133 darkmagenta
+" vim: et ts=2 sw=2
diff --git a/runtime/colors/evening.vim b/runtime/colors/evening.vim
index 5257307593..777c37e3ae 100644
--- a/runtime/colors/evening.vim
+++ b/runtime/colors/evening.vim
@@ -1,55 +1,512 @@
-" Vim color file
-" Maintainer: Bram Moolenaar <Bram@vim.org>
-" Last Change: 2016 Oct 10
+" Name: evening
+" Description: This color scheme uses a dark grey background.
+" Author: Original author Bram Moolenaar <Bram@vim.org>
+" Maintainer: Original maintainer Steven Vertigan <steven@vertigan.wattle.id.au>
+" Website: https://github.com/vim/colorschemes
+" License: Same as Vim
+" Last Updated: Sun Jun 12 10:52:56 2022
-" This color scheme uses a dark grey background.
+" Generated by Colortemplate v2.2.0
-" First remove all existing highlighting.
set background=dark
+
hi clear
-if exists("syntax_on")
- syntax reset
+let g:colors_name = 'evening'
+
+let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co > 1 ? &t_Co : 1
+
+if (has('termguicolors') && &termguicolors) || has('gui_running')
+ let g:terminal_ansi_colors = ['#000000', '#ffa500', '#2e8b57', '#ffff00', '#006faf', '#8b008b', '#008b8b', '#bebebe', '#4d4d4d', '#ff5f5f', '#00ff00', '#ffff60', '#0087ff', '#ff80ff', '#00ffff', '#ffffff']
+endif
+hi! link VertSplit StatusLineNC
+hi! link StatusLineTerm StatusLine
+hi! link StatusLineTermNC StatusLineNC
+hi! link TabLineFill TabLine
+hi! link Terminal Normal
+hi! link CursorColumn CursorLine
+hi! link CursorIM Cursor
+hi! link LineNrAbove LineNr
+hi! link LineNrBelow LineNr
+hi! link Debug Special
+hi! link diffAdded String
+hi! link diffRemoved WarningMsg
+hi! link diffOnly WarningMsg
+hi! link diffNoEOL WarningMsg
+hi! link diffIsA WarningMsg
+hi! link diffIdentical WarningMsg
+hi! link diffDiffer WarningMsg
+hi! link diffCommon WarningMsg
+hi! link diffBDiffer WarningMsg
+hi! link lCursor Cursor
+hi! link CurSearch Search
+hi! link CursorLineFold CursorLine
+hi! link CursorLineSign CursorLine
+hi! link Boolean Constant
+hi! link Character Constant
+hi! link Conditional Statement
+hi! link Define PreProc
+hi! link Delimiter Special
+hi! link Exception Statement
+hi! link Float Constant
+hi! link Function Identifier
+hi! link Include PreProc
+hi! link Keyword Statement
+hi! link Label Statement
+hi! link Macro PreProc
+hi! link Number Constant
+hi! link Operator Statement
+hi! link PreCondit PreProc
+hi! link Repeat Statement
+hi! link SpecialChar Special
+hi! link SpecialComment Special
+hi! link StorageClass Type
+hi! link String Constant
+hi! link Structure Type
+hi! link Tag Special
+hi! link Typedef Type
+hi Normal guifg=#ffffff guibg=#333333 gui=NONE cterm=NONE
+hi ColorColumn guifg=NONE guibg=#8b0000 gui=NONE cterm=NONE
+hi CursorLine guifg=NONE guibg=#666666 gui=NONE cterm=NONE
+hi CursorLineNr guifg=#ffff00 guibg=#666666 gui=NONE cterm=NONE
+hi EndOfBuffer guifg=#add8e6 guibg=#4d4d4d gui=NONE cterm=NONE
+hi StatusLine guifg=#333333 guibg=#ffffff gui=bold cterm=bold
+hi StatusLineNC guifg=#333333 guibg=#d3d3d3 gui=NONE cterm=NONE
+hi TabLineSel guifg=#333333 guibg=#ffffff gui=bold cterm=bold
+hi TabLine guifg=#333333 guibg=#d3d3d3 gui=NONE cterm=NONE
+hi Pmenu guifg=#ffffff guibg=#4d4d4d gui=NONE cterm=NONE
+hi PmenuSbar guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
+hi PmenuSel guifg=#000000 guibg=#bebebe gui=NONE cterm=NONE
+hi PmenuThumb guifg=NONE guibg=#ffffff gui=NONE cterm=NONE
+hi QuickFixLine guifg=#ffffff guibg=#8b008b gui=NONE cterm=NONE
+hi Cursor guifg=#000000 guibg=#00ff00 gui=NONE cterm=NONE
+hi Error guifg=#ff0000 guibg=#ffffff gui=reverse cterm=reverse
+hi ErrorMsg guifg=#ffffff guibg=#ff0000 gui=NONE cterm=NONE
+hi LineNr guifg=#ffff00 guibg=NONE gui=NONE cterm=NONE
+hi Conceal guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
+hi FoldColumn guifg=#add8e6 guibg=NONE gui=NONE cterm=NONE
+hi Folded guifg=#00008b guibg=#d3d3d3 gui=bold cterm=bold
+hi IncSearch guifg=#00ff00 guibg=NONE gui=reverse cterm=reverse
+hi MatchParen guifg=NONE guibg=#008b8b gui=NONE cterm=NONE
+hi ModeMsg guifg=NONE guibg=NONE gui=bold ctermfg=NONE ctermbg=NONE cterm=bold
+hi MoreMsg guifg=#2e8b57 guibg=NONE gui=NONE cterm=NONE
+hi NonText guifg=#add8e6 guibg=#4d4d4d gui=NONE cterm=NONE
+hi Question guifg=#00ff00 guibg=NONE gui=NONE cterm=NONE
+hi Search guifg=#000000 guibg=#ffff00 gui=NONE cterm=NONE
+hi SignColumn guifg=#008b8b guibg=NONE gui=NONE cterm=NONE
+hi SpecialKey guifg=#00ffff guibg=NONE gui=NONE cterm=NONE
+hi ToolbarButton guifg=NONE guibg=#999999 gui=bold cterm=bold
+hi ToolbarLine guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
+hi Visual guifg=#ffffff guibg=#999999 gui=NONE cterm=NONE
+hi VisualNOS guifg=NONE guibg=NONE gui=bold,underline ctermfg=NONE ctermbg=NONE cterm=bold,underline
+hi WarningMsg guifg=#8b0000 guibg=NONE gui=NONE cterm=NONE
+hi WildMenu guifg=#000000 guibg=#ffff00 gui=bold cterm=bold
+hi debugBreakpoint guifg=#00008b guibg=#ff0000 gui=NONE cterm=NONE
+hi debugPC guifg=#00008b guibg=#0000ff gui=NONE cterm=NONE
+hi SpellBad guifg=#ff0000 guibg=NONE guisp=#ff0000 gui=undercurl cterm=underline
+hi SpellCap guifg=#00ff00 guibg=NONE guisp=#00ff00 gui=undercurl cterm=underline
+hi SpellLocal guifg=#00ffff guibg=NONE guisp=#00ffff gui=undercurl cterm=underline
+hi SpellRare guifg=#ff80ff guibg=NONE guisp=#ff80ff gui=undercurl cterm=underline
+hi Directory guifg=#00ffff guibg=NONE gui=NONE cterm=NONE
+hi Title guifg=#ff80ff guibg=NONE gui=NONE cterm=NONE
+hi Comment guifg=#80a0ff guibg=NONE gui=NONE cterm=NONE
+hi Constant guifg=#ffa0a0 guibg=NONE gui=NONE cterm=NONE
+hi Identifier guifg=#00ffff guibg=NONE gui=NONE cterm=NONE
+hi Ignore guifg=#333333 guibg=NONE gui=NONE cterm=NONE
+hi PreProc guifg=#ff80ff guibg=NONE gui=NONE cterm=NONE
+hi Special guifg=#ffa500 guibg=NONE gui=NONE cterm=NONE
+hi Statement guifg=#ffff60 guibg=NONE gui=bold cterm=bold
+hi Todo guifg=#ffff00 guibg=#0000ff gui=reverse cterm=reverse
+hi Type guifg=#00ff00 guibg=NONE gui=bold cterm=bold
+hi Underlined guifg=#80a0ff guibg=NONE gui=underline cterm=underline
+hi DiffAdd guifg=#ffffff guibg=#5f875f gui=NONE cterm=NONE
+hi DiffChange guifg=#ffffff guibg=#5f87af gui=NONE cterm=NONE
+hi DiffText guifg=#000000 guibg=#c6c6c6 gui=NONE cterm=NONE
+hi DiffDelete guifg=#ffffff guibg=#af5faf gui=NONE cterm=NONE
+
+if s:t_Co >= 256
+ hi! link VertSplit StatusLineNC
+ hi! link StatusLineTerm StatusLine
+ hi! link StatusLineTermNC StatusLineNC
+ hi! link TabLineFill TabLine
+ hi! link Terminal Normal
+ hi! link CursorColumn CursorLine
+ hi! link CursorIM Cursor
+ hi! link LineNrAbove LineNr
+ hi! link LineNrBelow LineNr
+ hi! link Debug Special
+ hi! link diffAdded String
+ hi! link diffRemoved WarningMsg
+ hi! link diffOnly WarningMsg
+ hi! link diffNoEOL WarningMsg
+ hi! link diffIsA WarningMsg
+ hi! link diffIdentical WarningMsg
+ hi! link diffDiffer WarningMsg
+ hi! link diffCommon WarningMsg
+ hi! link diffBDiffer WarningMsg
+ hi! link lCursor Cursor
+ hi! link CurSearch Search
+ hi! link CursorLineFold CursorLine
+ hi! link CursorLineSign CursorLine
+ hi! link Boolean Constant
+ hi! link Character Constant
+ hi! link Conditional Statement
+ hi! link Define PreProc
+ hi! link Delimiter Special
+ hi! link Exception Statement
+ hi! link Float Constant
+ hi! link Function Identifier
+ hi! link Include PreProc
+ hi! link Keyword Statement
+ hi! link Label Statement
+ hi! link Macro PreProc
+ hi! link Number Constant
+ hi! link Operator Statement
+ hi! link PreCondit PreProc
+ hi! link Repeat Statement
+ hi! link SpecialChar Special
+ hi! link SpecialComment Special
+ hi! link StorageClass Type
+ hi! link String Constant
+ hi! link Structure Type
+ hi! link Tag Special
+ hi! link Typedef Type
+ hi Normal ctermfg=231 ctermbg=236 cterm=NONE
+ hi ColorColumn ctermfg=NONE ctermbg=88 cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=241 cterm=NONE
+ hi CursorLineNr ctermfg=226 ctermbg=241 cterm=NONE
+ hi EndOfBuffer ctermfg=153 ctermbg=239 cterm=NONE
+ hi StatusLine ctermfg=236 ctermbg=231 cterm=bold
+ hi StatusLineNC ctermfg=236 ctermbg=252 cterm=NONE
+ hi TabLineSel ctermfg=236 ctermbg=231 cterm=bold
+ hi TabLine ctermfg=236 ctermbg=252 cterm=NONE
+ hi Pmenu ctermfg=231 ctermbg=239 cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi PmenuSel ctermfg=16 ctermbg=250 cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=231 cterm=NONE
+ hi QuickFixLine ctermfg=231 ctermbg=90 cterm=NONE
+ hi Cursor ctermfg=16 ctermbg=46 cterm=NONE
+ hi Error ctermfg=196 ctermbg=231 cterm=reverse
+ hi ErrorMsg ctermfg=231 ctermbg=196 cterm=NONE
+ hi LineNr ctermfg=226 ctermbg=NONE cterm=NONE
+ hi Conceal ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi FoldColumn ctermfg=153 ctermbg=NONE cterm=NONE
+ hi Folded ctermfg=18 ctermbg=252 cterm=bold
+ hi IncSearch ctermfg=46 ctermbg=NONE cterm=reverse
+ hi MatchParen ctermfg=NONE ctermbg=30 cterm=NONE
+ hi ModeMsg ctermfg=NONE ctermbg=NONE cterm=bold
+ hi MoreMsg ctermfg=29 ctermbg=NONE cterm=NONE
+ hi NonText ctermfg=153 ctermbg=239 cterm=NONE
+ hi Question ctermfg=46 ctermbg=NONE cterm=NONE
+ hi Search ctermfg=16 ctermbg=226 cterm=NONE
+ hi SignColumn ctermfg=30 ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=51 ctermbg=NONE cterm=NONE
+ hi ToolbarButton ctermfg=NONE ctermbg=246 cterm=bold
+ hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Visual ctermfg=231 ctermbg=246 cterm=NONE
+ hi VisualNOS ctermfg=NONE ctermbg=NONE cterm=bold,underline
+ hi WarningMsg ctermfg=88 ctermbg=NONE cterm=NONE
+ hi WildMenu ctermfg=16 ctermbg=226 cterm=bold
+ hi debugBreakpoint ctermfg=18 ctermbg=196 cterm=NONE
+ hi debugPC ctermfg=18 ctermbg=21 cterm=NONE
+ hi SpellBad ctermfg=196 ctermbg=NONE cterm=underline
+ hi SpellCap ctermfg=46 ctermbg=NONE cterm=underline
+ hi SpellLocal ctermfg=51 ctermbg=NONE cterm=underline
+ hi SpellRare ctermfg=201 ctermbg=NONE cterm=underline
+ hi Directory ctermfg=51 ctermbg=NONE cterm=NONE
+ hi Title ctermfg=201 ctermbg=NONE cterm=NONE
+ hi Comment ctermfg=111 ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=217 ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=51 ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=236 ctermbg=NONE cterm=NONE
+ hi PreProc ctermfg=201 ctermbg=NONE cterm=NONE
+ hi Special ctermfg=214 ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=227 ctermbg=NONE cterm=bold
+ hi Todo ctermfg=226 ctermbg=21 cterm=reverse
+ hi Type ctermfg=46 ctermbg=NONE cterm=bold
+ hi Underlined ctermfg=111 ctermbg=NONE cterm=underline
+ hi DiffAdd ctermfg=231 ctermbg=65 cterm=NONE
+ hi DiffChange ctermfg=231 ctermbg=67 cterm=NONE
+ hi DiffText ctermfg=16 ctermbg=251 cterm=NONE
+ hi DiffDelete ctermfg=231 ctermbg=133 cterm=NONE
+ unlet s:t_Co
+ finish
+endif
+
+if s:t_Co >= 16
+ hi! link VertSplit StatusLineNC
+ hi! link StatusLineTerm StatusLine
+ hi! link StatusLineTermNC StatusLineNC
+ hi! link TabLineFill TabLine
+ hi! link Terminal Normal
+ hi! link CursorColumn CursorLine
+ hi! link CursorIM Cursor
+ hi! link LineNrAbove LineNr
+ hi! link LineNrBelow LineNr
+ hi! link Debug Special
+ hi! link diffAdded String
+ hi! link diffRemoved WarningMsg
+ hi! link diffOnly WarningMsg
+ hi! link diffNoEOL WarningMsg
+ hi! link diffIsA WarningMsg
+ hi! link diffIdentical WarningMsg
+ hi! link diffDiffer WarningMsg
+ hi! link diffCommon WarningMsg
+ hi! link diffBDiffer WarningMsg
+ hi! link lCursor Cursor
+ hi! link CurSearch Search
+ hi! link CursorLineFold CursorLine
+ hi! link CursorLineSign CursorLine
+ hi! link Boolean Constant
+ hi! link Character Constant
+ hi! link Conditional Statement
+ hi! link Define PreProc
+ hi! link Delimiter Special
+ hi! link Exception Statement
+ hi! link Float Constant
+ hi! link Function Identifier
+ hi! link Include PreProc
+ hi! link Keyword Statement
+ hi! link Label Statement
+ hi! link Macro PreProc
+ hi! link Number Constant
+ hi! link Operator Statement
+ hi! link PreCondit PreProc
+ hi! link Repeat Statement
+ hi! link SpecialChar Special
+ hi! link SpecialComment Special
+ hi! link StorageClass Type
+ hi! link String Constant
+ hi! link Structure Type
+ hi! link Tag Special
+ hi! link Typedef Type
+ hi Normal ctermfg=white ctermbg=black cterm=NONE
+ hi ColorColumn ctermfg=white ctermbg=darkred cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorLineNr ctermfg=NONE ctermbg=NONE cterm=underline
+ hi EndOfBuffer ctermfg=lightblue ctermbg=NONE cterm=NONE
+ hi StatusLine ctermfg=black ctermbg=white cterm=NONE
+ hi StatusLineNC ctermfg=black ctermbg=gray cterm=NONE
+ hi TabLineSel ctermfg=black ctermbg=white cterm=NONE
+ hi TabLine ctermfg=black ctermbg=gray cterm=NONE
+ hi Pmenu ctermfg=white ctermbg=darkgray cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi PmenuSel ctermfg=black ctermbg=gray cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=white cterm=NONE
+ hi QuickFixLine ctermfg=white ctermbg=darkmagenta cterm=NONE
+ hi Cursor ctermfg=black ctermbg=green cterm=NONE
+ hi Error ctermfg=red ctermbg=white cterm=reverse
+ hi ErrorMsg ctermfg=white ctermbg=red cterm=NONE
+ hi LineNr ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi Conceal ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi FoldColumn ctermfg=lightblue ctermbg=NONE cterm=NONE
+ hi Folded ctermfg=darkblue ctermbg=gray cterm=bold
+ hi IncSearch ctermfg=green ctermbg=NONE cterm=reverse
+ hi MatchParen ctermfg=NONE ctermbg=darkcyan cterm=NONE
+ hi ModeMsg ctermfg=NONE ctermbg=NONE cterm=bold
+ hi MoreMsg ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi NonText ctermfg=lightblue ctermbg=darkgray cterm=NONE
+ hi Question ctermfg=green ctermbg=NONE cterm=NONE
+ hi Search ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi SignColumn ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi ToolbarButton ctermfg=NONE ctermbg=darkgray cterm=bold
+ hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Visual ctermfg=white ctermbg=darkgray cterm=NONE
+ hi VisualNOS ctermfg=NONE ctermbg=NONE cterm=bold,underline
+ hi WarningMsg ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi WildMenu ctermfg=black ctermbg=darkyellow cterm=bold
+ hi debugBreakpoint ctermfg=darkblue ctermbg=red cterm=NONE
+ hi debugPC ctermfg=darkblue ctermbg=blue cterm=NONE
+ hi SpellBad ctermfg=red ctermbg=NONE cterm=underline
+ hi SpellCap ctermfg=green ctermbg=NONE cterm=underline
+ hi SpellLocal ctermfg=cyan ctermbg=NONE cterm=underline
+ hi SpellRare ctermfg=darkmagenta ctermbg=NONE cterm=underline
+ hi Directory ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi Title ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi Comment ctermfg=lightblue ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=magenta ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=gray ctermbg=NONE cterm=NONE
+ hi PreProc ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi Special ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=yellow ctermbg=NONE cterm=bold
+ hi Todo ctermfg=darkyellow ctermbg=blue cterm=reverse
+ hi Type ctermfg=green ctermbg=NONE cterm=bold
+ hi Underlined ctermfg=lightblue ctermbg=NONE cterm=underline
+ hi DiffAdd ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi DiffChange ctermfg=white ctermbg=blue cterm=NONE
+ hi DiffText ctermfg=black ctermbg=grey cterm=NONE
+ hi DiffDelete ctermfg=white ctermbg=magenta cterm=NONE
+ unlet s:t_Co
+ finish
+endif
+
+if s:t_Co >= 8
+ hi Normal ctermfg=gray ctermbg=black cterm=NONE
+ hi ColorColumn ctermfg=gray ctermbg=darkred cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorLineNr ctermfg=darkyellow ctermbg=NONE cterm=bold
+ hi EndOfBuffer ctermfg=blue ctermbg=NONE cterm=bold
+ hi StatusLine ctermfg=gray ctermbg=black cterm=bold,reverse
+ hi StatusLineNC ctermfg=gray ctermbg=black cterm=reverse
+ hi TabLineSel ctermfg=gray ctermbg=black cterm=bold,reverse
+ hi TabLine ctermfg=gray ctermbg=black cterm=reverse
+ hi Pmenu ctermfg=black ctermbg=darkcyan cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=gray cterm=NONE
+ hi PmenuSel ctermfg=black ctermbg=gray cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=black cterm=NONE
+ hi QuickFixLine ctermfg=gray ctermbg=darkmagenta cterm=NONE
+ hi Error ctermfg=red ctermbg=gray cterm=reverse
+ hi ErrorMsg ctermfg=gray ctermbg=red cterm=NONE
+ hi LineNr ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi Conceal ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi FoldColumn ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi Folded ctermfg=darkblue ctermbg=gray cterm=NONE
+ hi IncSearch ctermfg=darkgreen ctermbg=NONE cterm=reverse
+ hi MatchParen ctermfg=NONE ctermbg=darkcyan cterm=NONE
+ hi ModeMsg ctermfg=NONE ctermbg=NONE cterm=bold
+ hi MoreMsg ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi NonText ctermfg=blue ctermbg=NONE cterm=bold
+ hi Question ctermfg=darkgreen ctermbg=NONE cterm=bold
+ hi Search ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi SignColumn ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi ToolbarButton ctermfg=gray ctermbg=black cterm=bold,reverse
+ hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi VisualNOS ctermfg=NONE ctermbg=NONE cterm=underline
+ hi WarningMsg ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi WildMenu ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi debugBreakpoint ctermfg=darkblue ctermbg=darkred cterm=NONE
+ hi debugPC ctermfg=darkblue ctermbg=darkyellow cterm=NONE
+ hi SpellBad ctermfg=darkred ctermbg=darkyellow cterm=reverse
+ hi SpellCap ctermfg=darkgreen ctermbg=NONE cterm=reverse
+ hi SpellLocal ctermfg=darkcyan ctermbg=NONE cterm=reverse
+ hi SpellRare ctermfg=darkmagenta ctermbg=darkyellow cterm=reverse
+ hi Directory ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Title ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi Comment ctermfg=darkblue ctermbg=NONE cterm=bold
+ hi Constant ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=gray ctermbg=NONE cterm=NONE
+ hi PreProc ctermfg=darkmagenta ctermbg=NONE cterm=bold
+ hi Special ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=darkyellow ctermbg=NONE cterm=bold
+ hi Todo ctermfg=darkyellow ctermbg=blue cterm=reverse
+ hi Type ctermfg=darkgreen ctermbg=NONE cterm=bold
+ hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline
+ hi DiffAdd ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi DiffChange ctermfg=white ctermbg=darkblue cterm=NONE
+ hi DiffText ctermfg=black ctermbg=grey cterm=NONE
+ hi DiffDelete ctermfg=white ctermbg=darkmagenta cterm=NONE
+ unlet s:t_Co
+ finish
endif
-let colors_name = "evening"
-
-hi Normal ctermbg=DarkGrey ctermfg=White guifg=White guibg=grey20
-
-" Groups used in the 'highlight' and 'guicursor' options default value.
-hi ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White
-hi IncSearch term=reverse cterm=reverse gui=reverse
-hi ModeMsg term=bold cterm=bold gui=bold
-hi StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold
-hi StatusLineNC term=reverse cterm=reverse gui=reverse
-hi VertSplit term=reverse cterm=reverse gui=reverse
-hi Visual term=reverse ctermbg=black guibg=grey60
-hi DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red
-hi Cursor guibg=Green guifg=Black
-hi lCursor guibg=Cyan guifg=Black
-hi Directory term=bold ctermfg=LightCyan guifg=Cyan
-hi LineNr term=underline ctermfg=Yellow guifg=Yellow
-hi MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen
-hi NonText term=bold ctermfg=LightBlue gui=bold guifg=LightBlue guibg=grey30
-hi Question term=standout ctermfg=LightGreen gui=bold guifg=Green
-hi Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black
-hi SpecialKey term=bold ctermfg=LightBlue guifg=Cyan
-hi Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta
-hi WarningMsg term=standout ctermfg=LightRed guifg=Red
-hi WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black
-hi Folded term=standout ctermbg=LightGrey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue
-hi FoldColumn term=standout ctermbg=LightGrey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue
-hi DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue
-hi DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta
-hi DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan
-hi CursorColumn term=reverse ctermbg=Black guibg=grey40
-hi CursorLine term=underline cterm=underline guibg=grey40
-
-" Groups for syntax highlighting
-hi Constant term=underline ctermfg=Magenta guifg=#ffa0a0
-hi Special term=bold ctermfg=LightRed guifg=Orange
-if &t_Co > 8
- hi Statement term=bold cterm=bold ctermfg=Yellow guifg=#ffff60 gui=bold
+if s:t_Co >= 0
+ hi Normal term=NONE
+ hi ColorColumn term=reverse
+ hi Conceal term=NONE
+ hi Cursor term=reverse
+ hi CursorColumn term=NONE
+ hi CursorLine term=underline
+ hi CursorLineNr term=bold
+ hi DiffAdd term=reverse
+ hi DiffChange term=NONE
+ hi DiffDelete term=reverse
+ hi DiffText term=reverse
+ hi Directory term=NONE
+ hi EndOfBuffer term=NONE
+ hi ErrorMsg term=bold,reverse
+ hi FoldColumn term=NONE
+ hi Folded term=NONE
+ hi IncSearch term=bold,reverse,underline
+ hi LineNr term=NONE
+ hi MatchParen term=bold,underline
+ hi ModeMsg term=bold
+ hi MoreMsg term=NONE
+ hi NonText term=NONE
+ hi Pmenu term=reverse
+ hi PmenuSbar term=reverse
+ hi PmenuSel term=bold
+ hi PmenuThumb term=NONE
+ hi Question term=standout
+ hi Search term=reverse
+ hi SignColumn term=reverse
+ hi SpecialKey term=bold
+ hi SpellBad term=underline
+ hi SpellCap term=underline
+ hi SpellLocal term=underline
+ hi SpellRare term=underline
+ hi StatusLine term=bold,reverse
+ hi StatusLineNC term=bold,underline
+ hi TabLine term=bold,underline
+ hi TabLineFill term=NONE
+ hi Terminal term=NONE
+ hi TabLineSel term=bold,reverse
+ hi Title term=NONE
+ hi VertSplit term=NONE
+ hi Visual term=reverse
+ hi VisualNOS term=NONE
+ hi WarningMsg term=standout
+ hi WildMenu term=bold
+ hi CursorIM term=NONE
+ hi ToolbarLine term=reverse
+ hi ToolbarButton term=bold,reverse
+ hi CurSearch term=reverse
+ hi CursorLineFold term=underline
+ hi CursorLineSign term=underline
+ hi Comment term=bold
+ hi Constant term=NONE
+ hi Error term=bold,reverse
+ hi Identifier term=NONE
+ hi Ignore term=NONE
+ hi PreProc term=NONE
+ hi Special term=NONE
+ hi Statement term=NONE
+ hi Todo term=bold,reverse
+ hi Type term=NONE
+ hi Underlined term=underline
+ unlet s:t_Co
+ finish
endif
-hi Ignore ctermfg=DarkGrey guifg=grey20
-" vim: sw=2
+" Background: dark
+" Color: lightmagenta #ffa0a0 217 magenta
+" Color: blue #0000ff 21 blue
+" Color: cyan #00ffff 51 cyan
+" Color: yellow #ffff00 226 darkyellow
+" Color: white #ffffff 231 white
+" Color: black #000000 16 black
+" Color: green #00ff00 46 green
+" Color: magenta #ff80ff 201 darkmagenta
+" Color: red #ff0000 196 red
+" Color: lightyellow #ffff60 227 yellow
+" Color: darkblue #00008b 18 darkblue
+" Color: darkcyan #008b8b 30 darkcyan
+" Color: darkmagenta #8b008b 90 darkmagenta
+" Color: lightblue #add8e6 153 lightblue
+" Color: orange #ffa500 214 darkred
+" Color: seagreen #2e8b57 29 darkgreen
+" Color: lightgrey #d3d3d3 252 gray
+" Color: grey #bebebe 250 gray
+" Color: grey20 #333333 236 gray
+" Color: grey30 #4d4d4d 239 darkgray
+" Color: grey40 #666666 241 darkgray
+" Color: grey60 #999999 246 darkgray
+" Color: xtermblue #0087ff 33 blue
+" Color: xtermdarkblue #006faf 25 darkblue
+" Color: xtermred #ff5f5f 203 red
+" Color: comment #80a0ff 111 lightblue
+" Color: darkred #8b0000 88 darkred
+" Term colors: black orange seagreen yellow xtermdarkblue darkmagenta darkcyan grey
+" Term colors: grey30 xtermred green lightyellow xtermblue magenta cyan white
+" Color: bgDiffA #5F875F 65 darkgreen
+" Color: bgDiffC #5F87AF 67 blue
+" Color: bgDiffD #AF5FAF 133 magenta
+" Color: bgDiffT #C6C6C6 251 grey
+" Color: fgDiffW #FFFFFF 231 white
+" Color: fgDiffB #000000 16 black
+" Color: bgDiffC8 #5F87AF 67 darkblue
+" Color: bgDiffD8 #AF5FAF 133 darkmagenta
+" vim: et ts=2 sw=2
diff --git a/runtime/colors/industry.vim b/runtime/colors/industry.vim
index ac9103b5c5..41bfe129e7 100644
--- a/runtime/colors/industry.vim
+++ b/runtime/colors/industry.vim
@@ -1,40 +1,406 @@
-" Vim color file
-" Maintainer: Shian Lee
-" Last Change: 2014 Mar 6 (for vim 7.4)
-" Remark: "industry" stands for 'industrial' color scheme. In industrial
-" HMI (Human-Machine-Interface) programming, using a standard color
-" scheme is mandatory in many cases (in traffic-lights for example):
-" LIGHT_RED is 'Warning'
-" LIGHT_YELLOW is 'Attention'
-" LIGHT_GREEN is 'Normal'
-" LIGHT_MAGENTA is 'Warning-Attention' (light RED-YELLOW)
-" LIGHT_CYAN is 'Attention-Normal' (light YELLOW-GREEN).
-" BLACK is Dark-High-Contrast Background for maximum safety.
-" BLUE is Shade of BLACK (not supposed to get attention).
-"
-" Industrial color scheme is by nature clear, safe and productive.
-" Yet, depends on the file type's syntax, it might appear incorrect.
-
-" Reset to dark background, then reset everything to defaults:
+" Name: industry
+" Description: "industry" stands for 'industrial' color scheme.
+" Author: Original author Shian Lee.
+" Maintainer: Original maintainer Shian Lee.
+" Website: https://github.com/vim/colorschemes
+" License: Same as Vim
+" Last Updated: Sun Jun 12 10:53:58 2022
+
+" Generated by Colortemplate v2.2.0
+
set background=dark
-highlight clear
-if exists("syntax_on")
- syntax reset
+
+hi clear
+let g:colors_name = 'industry'
+
+let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co > 1 ? &t_Co : 1
+
+if (has('termguicolors') && &termguicolors) || has('gui_running')
+ let g:terminal_ansi_colors = ['#303030', '#870000', '#5fd75f', '#afaf00', '#87afff', '#af00af', '#00afaf', '#6c6c6c', '#444444', '#ff0000', '#00ff00', '#ffff00', '#005fff', '#ff00ff', '#00ffff', '#ffffff']
endif
+hi Normal guifg=#dadada guibg=#000000 gui=NONE cterm=NONE
+hi EndOfBuffer guifg=#444444 guibg=#000000 gui=NONE cterm=NONE
+hi StatusLine guifg=#000000 guibg=#dadada gui=bold cterm=bold
+hi StatusLineNC guifg=#000000 guibg=#6c6c6c gui=NONE cterm=NONE
+hi StatusLineTerm guifg=#000000 guibg=#00ff00 gui=bold cterm=bold
+hi StatusLineTermNC guifg=#000000 guibg=#5fd75f gui=NONE cterm=NONE
+hi VertSplit guifg=#000000 guibg=#6c6c6c gui=NONE cterm=NONE
+hi Pmenu guifg=#dadada guibg=#444444 gui=NONE cterm=NONE
+hi PmenuSel guifg=#000000 guibg=#ffff00 gui=NONE cterm=NONE
+hi PmenuSbar guifg=NONE guibg=#000000 gui=NONE cterm=NONE
+hi PmenuThumb guifg=NONE guibg=#6c6c6c gui=NONE cterm=NONE
+hi TabLine guifg=#dadada guibg=#444444 gui=NONE cterm=NONE
+hi TabLineFill guifg=NONE guibg=#6c6c6c gui=NONE cterm=NONE
+hi TabLineSel guifg=#ffffff guibg=#000000 gui=bold cterm=bold
+hi ToolbarButton guifg=#dadada guibg=#6c6c6c gui=bold cterm=bold
+hi ToolbarLine guifg=NONE guibg=#303030 gui=NONE cterm=NONE
+hi NonText guifg=#00afaf guibg=NONE gui=NONE cterm=NONE
+hi SpecialKey guifg=#00afaf guibg=NONE gui=NONE cterm=NONE
+hi Folded guifg=#00afaf guibg=#303030 gui=NONE cterm=NONE
+hi Visual guifg=#dadada guibg=#6c6c6c gui=NONE cterm=NONE
+hi CursorLine guifg=NONE guibg=#6c6c6c gui=NONE cterm=NONE
+hi CursorColumn guifg=NONE guibg=#6c6c6c gui=NONE cterm=NONE
+hi CursorLineNr guifg=#ffff00 guibg=NONE gui=bold cterm=bold
+hi ColorColumn guifg=NONE guibg=#444444 gui=NONE cterm=NONE
+hi QuickFixLine guifg=#000000 guibg=#ff00ff gui=NONE cterm=NONE
+hi VisualNOS guifg=#dadada guibg=#6c6c6c gui=NONE cterm=NONE
+hi LineNr guifg=#ffff00 guibg=NONE gui=NONE cterm=NONE
+hi FoldColumn guifg=#00afaf guibg=NONE gui=NONE cterm=NONE
+hi SignColumn guifg=#00afaf guibg=NONE gui=NONE cterm=NONE
+hi Underlined guifg=#87afff guibg=NONE gui=underline cterm=underline
+hi Error guifg=#ffffff guibg=#ff0000 gui=NONE cterm=NONE
+hi ErrorMsg guifg=#ffffff guibg=#ff0000 gui=NONE cterm=NONE
+hi ModeMsg guifg=#ffffff guibg=NONE gui=bold cterm=bold
+hi WarningMsg guifg=#870000 guibg=NONE gui=bold cterm=bold
+hi MoreMsg guifg=#5fd75f guibg=NONE gui=bold cterm=bold
+hi Question guifg=#00ff00 guibg=NONE gui=bold cterm=bold
+hi Todo guifg=#005fff guibg=#ffff00 gui=NONE cterm=NONE
+hi MatchParen guifg=#303030 guibg=#afaf00 gui=NONE cterm=NONE
+hi Search guifg=#000000 guibg=#ffff00 gui=NONE cterm=NONE
+hi IncSearch guifg=#000000 guibg=#00ff00 gui=NONE cterm=NONE
+hi WildMenu guifg=#000000 guibg=#ffff00 gui=NONE cterm=NONE
+hi Cursor guifg=#000000 guibg=#dadada gui=NONE cterm=NONE
+hi lCursor guifg=#000000 guibg=#ff0000 gui=NONE cterm=NONE
+hi SpellBad guifg=#ff0000 guibg=NONE guisp=#ff0000 gui=undercurl cterm=underline
+hi SpellCap guifg=#005fff guibg=NONE guisp=#005fff gui=undercurl cterm=underline
+hi SpellLocal guifg=#ff00ff guibg=NONE guisp=#ff00ff gui=undercurl cterm=underline
+hi SpellRare guifg=#00ff00 guibg=NONE guisp=#00ff00 gui=undercurl cterm=underline
+hi Comment guifg=#00afaf guibg=NONE gui=NONE cterm=NONE
+hi Identifier guifg=#ff00ff guibg=NONE gui=NONE cterm=NONE
+hi Function guifg=#00ff00 guibg=NONE gui=NONE cterm=NONE
+hi Statement guifg=#ffffff guibg=NONE gui=bold cterm=bold
+hi Constant guifg=#00ffff guibg=NONE gui=NONE cterm=NONE
+hi PreProc guifg=#ffff00 guibg=NONE gui=NONE cterm=NONE
+hi Type guifg=#00ff00 guibg=NONE gui=bold cterm=bold
+hi Special guifg=#ff0000 guibg=NONE gui=NONE cterm=NONE
+hi Delimiter guifg=#ffff00 guibg=NONE gui=NONE cterm=NONE
+hi Directory guifg=#00ffff guibg=NONE gui=NONE cterm=NONE
+hi Conceal guifg=#6c6c6c guibg=NONE gui=NONE cterm=NONE
+hi Ignore guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
+hi Title guifg=#ff00ff guibg=NONE gui=bold cterm=bold
+hi! link Terminal Normal
+hi! link LineNrAbove LineNr
+hi! link LineNrBelow LineNr
+hi! link CurSearch Search
+hi! link CursorLineFold CursorLine
+hi! link CursorLineSign CursorLine
+hi DiffAdd guifg=#ffffff guibg=#5f875f gui=NONE cterm=NONE
+hi DiffChange guifg=#ffffff guibg=#5f87af gui=NONE cterm=NONE
+hi DiffText guifg=#000000 guibg=#c6c6c6 gui=NONE cterm=NONE
+hi DiffDelete guifg=#ffffff guibg=#af5faf gui=NONE cterm=NONE
-let colors_name = "industry"
+if s:t_Co >= 256
+ hi Normal ctermfg=253 ctermbg=16 cterm=NONE
+ hi EndOfBuffer ctermfg=238 ctermbg=16 cterm=NONE
+ hi StatusLine ctermfg=16 ctermbg=253 cterm=bold
+ hi StatusLineNC ctermfg=16 ctermbg=242 cterm=NONE
+ hi StatusLineTerm ctermfg=16 ctermbg=46 cterm=bold
+ hi StatusLineTermNC ctermfg=16 ctermbg=77 cterm=NONE
+ hi VertSplit ctermfg=16 ctermbg=242 cterm=NONE
+ hi Pmenu ctermfg=253 ctermbg=238 cterm=NONE
+ hi PmenuSel ctermfg=16 ctermbg=226 cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=16 cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=242 cterm=NONE
+ hi TabLine ctermfg=253 ctermbg=238 cterm=NONE
+ hi TabLineFill ctermfg=NONE ctermbg=242 cterm=NONE
+ hi TabLineSel ctermfg=231 ctermbg=16 cterm=bold
+ hi ToolbarButton ctermfg=253 ctermbg=242 cterm=bold
+ hi ToolbarLine ctermfg=NONE ctermbg=236 cterm=NONE
+ hi NonText ctermfg=37 ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=37 ctermbg=NONE cterm=NONE
+ hi Folded ctermfg=37 ctermbg=236 cterm=NONE
+ hi Visual ctermfg=253 ctermbg=242 cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=242 cterm=NONE
+ hi CursorColumn ctermfg=NONE ctermbg=242 cterm=NONE
+ hi CursorLineNr ctermfg=226 ctermbg=NONE cterm=bold
+ hi ColorColumn ctermfg=NONE ctermbg=238 cterm=NONE
+ hi QuickFixLine ctermfg=16 ctermbg=201 cterm=NONE
+ hi VisualNOS ctermfg=253 ctermbg=242 cterm=NONE
+ hi LineNr ctermfg=226 ctermbg=NONE cterm=NONE
+ hi FoldColumn ctermfg=37 ctermbg=NONE cterm=NONE
+ hi SignColumn ctermfg=37 ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=111 ctermbg=NONE cterm=underline
+ hi Error ctermfg=231 ctermbg=196 cterm=NONE
+ hi ErrorMsg ctermfg=231 ctermbg=196 cterm=NONE
+ hi ModeMsg ctermfg=231 ctermbg=NONE cterm=bold
+ hi WarningMsg ctermfg=88 ctermbg=NONE cterm=bold
+ hi MoreMsg ctermfg=77 ctermbg=NONE cterm=bold
+ hi Question ctermfg=46 ctermbg=NONE cterm=bold
+ hi Todo ctermfg=27 ctermbg=226 cterm=NONE
+ hi MatchParen ctermfg=236 ctermbg=142 cterm=NONE
+ hi Search ctermfg=16 ctermbg=226 cterm=NONE
+ hi IncSearch ctermfg=16 ctermbg=46 cterm=NONE
+ hi WildMenu ctermfg=16 ctermbg=226 cterm=NONE
+ hi Cursor ctermfg=16 ctermbg=253 cterm=NONE
+ hi lCursor ctermfg=16 ctermbg=196 cterm=NONE
+ hi SpellBad ctermfg=196 ctermbg=NONE cterm=underline
+ hi SpellCap ctermfg=27 ctermbg=NONE cterm=underline
+ hi SpellLocal ctermfg=201 ctermbg=NONE cterm=underline
+ hi SpellRare ctermfg=46 ctermbg=NONE cterm=underline
+ hi Comment ctermfg=37 ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=201 ctermbg=NONE cterm=NONE
+ hi Function ctermfg=46 ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=231 ctermbg=NONE cterm=bold
+ hi Constant ctermfg=51 ctermbg=NONE cterm=NONE
+ hi PreProc ctermfg=226 ctermbg=NONE cterm=NONE
+ hi Type ctermfg=46 ctermbg=NONE cterm=bold
+ hi Special ctermfg=196 ctermbg=NONE cterm=NONE
+ hi Delimiter ctermfg=226 ctermbg=NONE cterm=NONE
+ hi Directory ctermfg=51 ctermbg=NONE cterm=NONE
+ hi Conceal ctermfg=242 ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Title ctermfg=201 ctermbg=NONE cterm=bold
+ hi! link Terminal Normal
+ hi! link LineNrAbove LineNr
+ hi! link LineNrBelow LineNr
+ hi! link CurSearch Search
+ hi! link CursorLineFold CursorLine
+ hi! link CursorLineSign CursorLine
+ hi DiffAdd ctermfg=231 ctermbg=65 cterm=NONE
+ hi DiffChange ctermfg=231 ctermbg=67 cterm=NONE
+ hi DiffText ctermfg=16 ctermbg=251 cterm=NONE
+ hi DiffDelete ctermfg=231 ctermbg=133 cterm=NONE
+ unlet s:t_Co
+ finish
+endif
-" First set Normal to regular white on black text colors:
-hi Normal ctermfg=LightGray ctermbg=Black guifg=#dddddd guibg=Black
+if s:t_Co >= 16
+ hi Normal ctermfg=white ctermbg=black cterm=NONE
+ hi EndOfBuffer ctermfg=darkgrey ctermbg=black cterm=NONE
+ hi StatusLine ctermfg=black ctermbg=white cterm=bold
+ hi StatusLineNC ctermfg=black ctermbg=grey cterm=NONE
+ hi StatusLineTerm ctermfg=black ctermbg=green cterm=bold
+ hi StatusLineTermNC ctermfg=black ctermbg=darkgreen cterm=NONE
+ hi VertSplit ctermfg=black ctermbg=grey cterm=NONE
+ hi Pmenu ctermfg=white ctermbg=darkgrey cterm=NONE
+ hi PmenuSel ctermfg=black ctermbg=yellow cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=black cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=grey cterm=NONE
+ hi TabLine ctermfg=white ctermbg=darkgrey cterm=NONE
+ hi TabLineFill ctermfg=NONE ctermbg=grey cterm=NONE
+ hi TabLineSel ctermfg=white ctermbg=black cterm=bold
+ hi ToolbarButton ctermfg=white ctermbg=darkgrey cterm=NONE
+ hi ToolbarLine ctermfg=NONE ctermbg=black cterm=NONE
+ hi NonText ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Folded ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Visual ctermfg=black ctermbg=grey cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorColumn ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorLineNr ctermfg=yellow ctermbg=NONE cterm=underline
+ hi ColorColumn ctermfg=white ctermbg=darkgrey cterm=NONE
+ hi QuickFixLine ctermfg=black ctermbg=magenta cterm=NONE
+ hi VisualNOS ctermfg=white ctermbg=grey cterm=NONE
+ hi LineNr ctermfg=yellow ctermbg=NONE cterm=NONE
+ hi FoldColumn ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi SignColumn ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=darkblue ctermbg=NONE cterm=underline
+ hi Error ctermfg=white ctermbg=red cterm=NONE
+ hi ErrorMsg ctermfg=white ctermbg=red cterm=NONE
+ hi ModeMsg ctermfg=white ctermbg=NONE cterm=bold
+ hi WarningMsg ctermfg=darkred ctermbg=NONE cterm=bold
+ hi MoreMsg ctermfg=darkgreen ctermbg=NONE cterm=bold
+ hi Question ctermfg=green ctermbg=NONE cterm=bold
+ hi Todo ctermfg=blue ctermbg=yellow cterm=NONE
+ hi MatchParen ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi Search ctermfg=black ctermbg=yellow cterm=NONE
+ hi IncSearch ctermfg=black ctermbg=green cterm=NONE
+ hi WildMenu ctermfg=black ctermbg=yellow cterm=NONE
+ hi Cursor ctermfg=black ctermbg=white cterm=NONE
+ hi lCursor ctermfg=black ctermbg=red cterm=NONE
+ hi SpellBad ctermfg=red ctermbg=NONE cterm=underline
+ hi SpellCap ctermfg=blue ctermbg=NONE cterm=underline
+ hi SpellLocal ctermfg=magenta ctermbg=NONE cterm=underline
+ hi SpellRare ctermfg=green ctermbg=NONE cterm=underline
+ hi Comment ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=magenta ctermbg=NONE cterm=NONE
+ hi Function ctermfg=green ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=white ctermbg=NONE cterm=bold
+ hi Constant ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi PreProc ctermfg=yellow ctermbg=NONE cterm=NONE
+ hi Type ctermfg=green ctermbg=NONE cterm=bold
+ hi Special ctermfg=red ctermbg=NONE cterm=NONE
+ hi Delimiter ctermfg=yellow ctermbg=NONE cterm=NONE
+ hi Directory ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi Conceal ctermfg=grey ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Title ctermfg=magenta ctermbg=NONE cterm=bold
+ hi! link Terminal Normal
+ hi! link LineNrAbove LineNr
+ hi! link LineNrBelow LineNr
+ hi! link CurSearch Search
+ hi! link CursorLineFold CursorLine
+ hi! link CursorLineSign CursorLine
+ hi DiffAdd ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi DiffChange ctermfg=white ctermbg=blue cterm=NONE
+ hi DiffText ctermfg=black ctermbg=grey cterm=NONE
+ hi DiffDelete ctermfg=white ctermbg=magenta cterm=NONE
+ unlet s:t_Co
+ finish
+endif
-" Syntax highlighting (other color-groups using default, see :help group-name):
-hi Comment cterm=NONE ctermfg=DarkCyan gui=NONE guifg=#00aaaa
-hi Constant cterm=NONE ctermfg=LightCyan gui=NONE guifg=#00ffff
-hi Identifier cterm=NONE ctermfg=LightMagenta gui=NONE guifg=#ff00ff
-hi Function cterm=NONE ctermfg=LightGreen gui=NONE guifg=#00ff00
-hi Statement cterm=NONE ctermfg=White gui=bold guifg=#ffffff
-hi PreProc cterm=NONE ctermfg=Yellow gui=NONE guifg=#ffff00
-hi Type cterm=NONE ctermfg=LightGreen gui=bold guifg=#00ff00
-hi Special cterm=NONE ctermfg=LightRed gui=NONE guifg=#ff0000
-hi Delimiter cterm=NONE ctermfg=Yellow gui=NONE guifg=#ffff00
+if s:t_Co >= 8
+ hi Normal ctermfg=grey ctermbg=black cterm=NONE
+ hi EndOfBuffer ctermfg=grey ctermbg=black cterm=bold
+ hi StatusLine ctermfg=grey ctermbg=black cterm=bold,reverse
+ hi StatusLineNC ctermfg=grey ctermbg=black cterm=reverse
+ hi StatusLineTerm ctermfg=darkgreen ctermbg=black cterm=bold,reverse
+ hi StatusLineTermNC ctermfg=darkgreen ctermbg=black cterm=reverse
+ hi VertSplit ctermfg=grey ctermbg=black cterm=reverse
+ hi Pmenu ctermfg=black ctermbg=grey cterm=NONE
+ hi PmenuSel ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=black cterm=NONE
+ hi PmenuThumb ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi TabLine ctermfg=black ctermbg=grey cterm=NONE
+ hi TabLineFill ctermfg=NONE ctermbg=grey cterm=NONE
+ hi TabLineSel ctermfg=grey ctermbg=black cterm=NONE
+ hi ToolbarButton ctermfg=grey ctermbg=black cterm=bold,reverse
+ hi ToolbarLine ctermfg=NONE ctermbg=black cterm=NONE
+ hi NonText ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Folded ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorColumn ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorLineNr ctermfg=darkyellow ctermbg=NONE cterm=bold
+ hi ColorColumn ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi QuickFixLine ctermfg=black ctermbg=darkmagenta cterm=NONE
+ hi VisualNOS ctermfg=black ctermbg=grey cterm=NONE
+ hi LineNr ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi FoldColumn ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi SignColumn ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline
+ hi Error ctermfg=grey ctermbg=darkred cterm=NONE
+ hi ErrorMsg ctermfg=grey ctermbg=darkred cterm=NONE
+ hi ModeMsg ctermfg=grey ctermbg=NONE cterm=NONE
+ hi WarningMsg ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi MoreMsg ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi Question ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi Todo ctermfg=darkblue ctermbg=darkyellow cterm=NONE
+ hi MatchParen ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi Search ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi IncSearch ctermfg=black ctermbg=darkgreen cterm=NONE
+ hi WildMenu ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi SpellBad ctermfg=darkred ctermbg=darkyellow cterm=reverse
+ hi SpellCap ctermfg=darkblue ctermbg=darkyellow cterm=reverse
+ hi SpellLocal ctermfg=darkmagenta ctermbg=darkyellow cterm=reverse
+ hi SpellRare ctermfg=darkgreen ctermbg=NONE cterm=reverse
+ hi Comment ctermfg=darkcyan ctermbg=NONE cterm=bold
+ hi Identifier ctermfg=magenta ctermbg=NONE cterm=NONE
+ hi Function ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=grey ctermbg=NONE cterm=bold
+ hi Constant ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi PreProc ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi Type ctermfg=darkgreen ctermbg=NONE cterm=bold
+ hi Special ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi Delimiter ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi Directory ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Conceal ctermfg=grey ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Title ctermfg=darkmagenta ctermbg=NONE cterm=bold
+ hi DiffAdd ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi DiffChange ctermfg=white ctermbg=darkblue cterm=NONE
+ hi DiffText ctermfg=black ctermbg=grey cterm=NONE
+ hi DiffDelete ctermfg=white ctermbg=darkmagenta cterm=NONE
+ unlet s:t_Co
+ finish
+endif
+
+if s:t_Co >= 0
+ hi Normal term=NONE
+ hi ColorColumn term=reverse
+ hi Conceal term=NONE
+ hi Cursor term=reverse
+ hi CursorColumn term=NONE
+ hi CursorLine term=underline
+ hi CursorLineNr term=bold
+ hi DiffAdd term=reverse
+ hi DiffChange term=NONE
+ hi DiffDelete term=reverse
+ hi DiffText term=reverse
+ hi Directory term=NONE
+ hi EndOfBuffer term=NONE
+ hi ErrorMsg term=bold,reverse
+ hi FoldColumn term=NONE
+ hi Folded term=NONE
+ hi IncSearch term=bold,reverse,underline
+ hi LineNr term=NONE
+ hi MatchParen term=bold,underline
+ hi ModeMsg term=bold
+ hi MoreMsg term=NONE
+ hi NonText term=NONE
+ hi Pmenu term=reverse
+ hi PmenuSbar term=reverse
+ hi PmenuSel term=bold
+ hi PmenuThumb term=NONE
+ hi Question term=standout
+ hi Search term=reverse
+ hi SignColumn term=reverse
+ hi SpecialKey term=bold
+ hi SpellBad term=underline
+ hi SpellCap term=underline
+ hi SpellLocal term=underline
+ hi SpellRare term=underline
+ hi StatusLine term=bold,reverse
+ hi StatusLineNC term=bold,underline
+ hi TabLine term=bold,underline
+ hi TabLineFill term=NONE
+ hi Terminal term=NONE
+ hi TabLineSel term=bold,reverse
+ hi Title term=NONE
+ hi VertSplit term=NONE
+ hi Visual term=reverse
+ hi VisualNOS term=NONE
+ hi WarningMsg term=standout
+ hi WildMenu term=bold
+ hi CursorIM term=NONE
+ hi ToolbarLine term=reverse
+ hi ToolbarButton term=bold,reverse
+ hi CurSearch term=reverse
+ hi CursorLineFold term=underline
+ hi CursorLineSign term=underline
+ hi Comment term=bold
+ hi Constant term=NONE
+ hi Error term=bold,reverse
+ hi Identifier term=NONE
+ hi Ignore term=NONE
+ hi PreProc term=NONE
+ hi Special term=NONE
+ hi Statement term=NONE
+ hi Todo term=bold,reverse
+ hi Type term=NONE
+ hi Underlined term=underline
+ unlet s:t_Co
+ finish
+endif
+" Background: dark
+" Color: foreground #dadada 253 white
+" Color: background #000000 16 black
+" Color: color00 #303030 236 black
+" Color: color08 #444444 238 darkgrey
+" Color: color01 #870000 88 darkred
+" Color: color09 #FF0000 196 red
+" Color: color02 #5FD75F 77 darkgreen
+" Color: color10 #00FF00 46 green
+" Color: color03 #AFAF00 142 darkyellow
+" Color: color11 #FFFF00 226 yellow
+" Color: color04 #87AFFF 111 darkblue
+" Color: color12 #005FFF 27 blue
+" Color: color05 #AF00AF 127 darkmagenta
+" Color: color13 #FF00FF 201 magenta
+" Color: color06 #00AFAF 37 darkcyan
+" Color: color14 #00FFFF 51 cyan
+" Color: color07 #6C6C6C 242 grey
+" Color: color15 #FFFFFF 231 white
+" Term colors: color00 color01 color02 color03 color04 color05 color06 color07
+" Term colors: color08 color09 color10 color11 color12 color13 color14 color15
+" Color: bgDiffA #5F875F 65 darkgreen
+" Color: bgDiffC #5F87AF 67 blue
+" Color: bgDiffD #AF5FAF 133 magenta
+" Color: bgDiffT #C6C6C6 251 grey
+" Color: fgDiffW #FFFFFF 231 white
+" Color: fgDiffB #000000 16 black
+" Color: bgDiffC8 #5F87AF 67 darkblue
+" Color: bgDiffD8 #AF5FAF 133 darkmagenta
+" vim: et ts=2 sw=2
diff --git a/runtime/colors/koehler.vim b/runtime/colors/koehler.vim
index 250472a162..ecbc854030 100644
--- a/runtime/colors/koehler.vim
+++ b/runtime/colors/koehler.vim
@@ -1,73 +1,424 @@
-" local syntax file - set colors on a per-machine basis:
-" vim: tw=0 ts=4 sw=4
-" Vim color file
-" Maintainer: Ron Aaron <ron@ronware.org>
-" Last Change: 2016 Sep 04
+" Name: koehler
+" Author: original author Ron Aaron <ron@ronware.org>
+" Maintainer: original maintainer Ron Aaron <ron@ronware.org>
+" Website: https://www.github.com/vim/colorschemes
+" License: Same as Vim
+" Last Updated: Sat 11 Jun 2022 11:24:58 MSK
+
+" Generated by Colortemplate v2.2.0
-hi clear
set background=dark
-if exists("syntax_on")
- syntax reset
+
+hi clear
+let g:colors_name = 'koehler'
+
+let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co > 1 ? &t_Co : 1
+
+hi! link Terminal Normal
+hi! link Boolean Constant
+hi! link Character Constant
+hi! link Conditional Statement
+hi! link Debug Special
+hi! link Define PreProc
+hi! link Delimiter Special
+hi! link Exception Statement
+hi! link Float Number
+hi! link Function Identifier
+hi! link Include PreProc
+hi! link IncSearch Visual
+hi! link Keyword Statement
+hi! link Label Statement
+hi! link LineNrAbove LineNr
+hi! link LineNrBelow LineNr
+hi! link Macro PreProc
+hi! link Number Constant
+hi! link Operator Statement
+hi! link PopupSelected PmenuSel
+hi! link PreCondit PreProc
+hi! link Repeat Statement
+hi! link SpecialChar Special
+hi! link SpecialComment Special
+hi! link StatusLineTerm StatusLine
+hi! link StatusLineTermNC StatusLineNC
+hi! link StorageClass Type
+hi! link String Constant
+hi! link Structure Type
+hi! link Tag Special
+hi! link Typedef Type
+hi! link lCursor Cursor
+hi! link CurSearch Search
+hi! link CursorLineFold CursorLine
+hi! link CursorLineSign CursorLine
+
+if (has('termguicolors') && &termguicolors) || has('gui_running')
+ let g:terminal_ansi_colors = ['#000000', '#cd0000', '#00cd00', '#cdcd00', '#0000ee', '#cd00cd', '#00cdcd', '#e5e5e5', '#7f7f7f', '#ff0000', '#00ff00', '#ffff00', '#5c5cff', '#ff00ff', '#00ffff', '#ffffff']
+endif
+hi Normal guifg=#ffffff guibg=#000000 gui=NONE cterm=NONE
+hi ColorColumn guifg=NONE guibg=#8b0000 gui=NONE cterm=NONE
+hi CursorColumn guifg=NONE guibg=#555555 gui=NONE cterm=NONE
+hi CursorLine guifg=NONE guibg=#555555 gui=NONE cterm=NONE
+hi CursorLineNr guifg=#ffff00 guibg=NONE gui=bold cterm=bold
+hi Folded guifg=#00cdcd guibg=#666666 gui=NONE cterm=NONE
+hi QuickFixLine guifg=#000000 guibg=#ffff00 gui=NONE cterm=NONE
+hi Conceal guifg=#e5e5e5 guibg=#a9a9a9 gui=NONE cterm=NONE
+hi Cursor guifg=#000000 guibg=#00ff00 gui=NONE cterm=NONE
+hi Directory guifg=#cc8000 guibg=NONE gui=NONE cterm=NONE
+hi EndOfBuffer guifg=#cd0000 guibg=NONE gui=bold cterm=bold
+hi ErrorMsg guifg=#cd0000 guibg=#ffffff gui=reverse cterm=reverse
+hi FoldColumn guifg=#00cdcd guibg=NONE gui=NONE cterm=NONE
+hi LineNr guifg=#ffff00 guibg=NONE gui=NONE cterm=NONE
+hi MatchParen guifg=NONE guibg=#0000ff gui=NONE cterm=NONE
+hi ModeMsg guifg=#ffffff guibg=#0000ff gui=bold cterm=bold
+hi MoreMsg guifg=#2e8b57 guibg=NONE gui=bold cterm=bold
+hi NonText guifg=#cd0000 guibg=NONE gui=bold cterm=bold
+hi Pmenu guifg=#ffffff guibg=#444444 gui=NONE cterm=NONE
+hi PmenuSbar guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
+hi PmenuSel guifg=#000000 guibg=#00cdcd gui=NONE cterm=NONE
+hi PmenuThumb guifg=NONE guibg=#ffffff gui=NONE cterm=NONE
+hi Question guifg=#5c5cff guibg=NONE gui=bold cterm=bold
+hi Search guifg=#ffffff guibg=#ff0000 gui=NONE cterm=NONE
+hi SignColumn guifg=#00ffff guibg=NONE gui=NONE cterm=NONE
+hi SpecialKey guifg=#cd0000 guibg=NONE gui=NONE cterm=NONE
+hi SpellBad guifg=#ff0000 guibg=NONE guisp=#ff0000 gui=undercurl cterm=underline
+hi SpellCap guifg=#60ff60 guibg=NONE guisp=#60ff60 gui=undercurl cterm=underline
+hi SpellLocal guifg=#00ffff guibg=NONE guisp=#00ffff gui=undercurl cterm=underline
+hi SpellRare guifg=#ff00ff guibg=NONE guisp=#ff00ff gui=undercurl cterm=underline
+hi StatusLine guifg=#0000ff guibg=#ffffff gui=bold cterm=bold
+hi StatusLineNC guifg=#0000ff guibg=#e5e5e5 gui=NONE cterm=NONE
+hi TabLine guifg=#0000ff guibg=#ffffff gui=bold cterm=bold
+hi TabLineFill guifg=#0000ff guibg=#ffffff gui=bold cterm=bold
+hi TabLineSel guifg=#ffffff guibg=#0000ff gui=bold cterm=bold
+hi Title guifg=#ff00ff guibg=NONE gui=bold cterm=bold
+hi VertSplit guifg=#0000ff guibg=#e5e5e5 gui=NONE cterm=NONE
+hi Visual guifg=NONE guibg=#666666 gui=reverse cterm=reverse
+hi VisualNOS guifg=NONE guibg=#000000 gui=bold,underline cterm=underline
+hi WarningMsg guifg=#ff0000 guibg=NONE gui=NONE cterm=NONE
+hi WildMenu guifg=#000000 guibg=#ffff00 gui=NONE cterm=NONE
+hi Comment guifg=#80a0ff guibg=NONE gui=NONE cterm=NONE
+hi Constant guifg=#ffa0a0 guibg=NONE gui=NONE cterm=NONE
+hi Error guifg=#cd0000 guibg=#ffffff gui=reverse cterm=reverse
+hi Identifier guifg=#40ffff guibg=NONE gui=NONE cterm=NONE
+hi Ignore guifg=#000000 guibg=#000000 gui=NONE cterm=NONE
+hi PreProc guifg=#ff80ff guibg=NONE gui=NONE cterm=NONE
+hi Special guifg=#ffa500 guibg=NONE gui=NONE cterm=NONE
+hi Statement guifg=#ffff60 guibg=NONE gui=bold cterm=bold
+hi Todo guifg=#0000ff guibg=#ffff00 gui=NONE cterm=NONE
+hi Type guifg=#60ff60 guibg=NONE gui=bold cterm=bold
+hi Underlined guifg=#add8e6 guibg=NONE gui=bold,underline cterm=underline
+hi CursorIM guifg=NONE guibg=fg gui=NONE cterm=NONE
+hi ToolbarLine guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
+hi ToolbarButton guifg=#000000 guibg=#e5e5e5 gui=bold cterm=bold
+hi DiffAdd guifg=#ffffff guibg=#5f875f gui=NONE cterm=NONE
+hi DiffChange guifg=#ffffff guibg=#5f87af gui=NONE cterm=NONE
+hi DiffText guifg=#000000 guibg=#c6c6c6 gui=NONE cterm=NONE
+hi DiffDelete guifg=#ffffff guibg=#af5faf gui=NONE cterm=NONE
+
+if s:t_Co >= 256
+ hi Normal ctermfg=231 ctermbg=16 cterm=NONE
+ hi ColorColumn ctermfg=NONE ctermbg=88 cterm=NONE
+ hi CursorColumn ctermfg=NONE ctermbg=240 cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=240 cterm=NONE
+ hi CursorLineNr ctermfg=226 ctermbg=NONE cterm=bold
+ hi Folded ctermfg=44 ctermbg=59 cterm=NONE
+ hi QuickFixLine ctermfg=16 ctermbg=226 cterm=NONE
+ hi Conceal ctermfg=254 ctermbg=145 cterm=NONE
+ hi Cursor ctermfg=16 ctermbg=46 cterm=NONE
+ hi Directory ctermfg=172 ctermbg=NONE cterm=NONE
+ hi EndOfBuffer ctermfg=160 ctermbg=NONE cterm=bold
+ hi ErrorMsg ctermfg=160 ctermbg=231 cterm=reverse
+ hi FoldColumn ctermfg=44 ctermbg=NONE cterm=NONE
+ hi LineNr ctermfg=226 ctermbg=NONE cterm=NONE
+ hi MatchParen ctermfg=NONE ctermbg=21 cterm=NONE
+ hi ModeMsg ctermfg=231 ctermbg=21 cterm=bold
+ hi MoreMsg ctermfg=29 ctermbg=NONE cterm=bold
+ hi NonText ctermfg=160 ctermbg=NONE cterm=bold
+ hi Pmenu ctermfg=231 ctermbg=238 cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi PmenuSel ctermfg=16 ctermbg=44 cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=231 cterm=NONE
+ hi Question ctermfg=63 ctermbg=NONE cterm=bold
+ hi Search ctermfg=231 ctermbg=196 cterm=NONE
+ hi SignColumn ctermfg=51 ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=160 ctermbg=NONE cterm=NONE
+ hi SpellBad ctermfg=196 ctermbg=NONE cterm=underline
+ hi SpellCap ctermfg=83 ctermbg=NONE cterm=underline
+ hi SpellLocal ctermfg=51 ctermbg=NONE cterm=underline
+ hi SpellRare ctermfg=201 ctermbg=NONE cterm=underline
+ hi StatusLine ctermfg=21 ctermbg=231 cterm=bold
+ hi StatusLineNC ctermfg=21 ctermbg=254 cterm=NONE
+ hi TabLine ctermfg=21 ctermbg=231 cterm=bold
+ hi TabLineFill ctermfg=21 ctermbg=231 cterm=bold
+ hi TabLineSel ctermfg=231 ctermbg=21 cterm=bold
+ hi Title ctermfg=201 ctermbg=NONE cterm=bold
+ hi VertSplit ctermfg=21 ctermbg=254 cterm=NONE
+ hi Visual ctermfg=NONE ctermbg=59 cterm=reverse
+ hi VisualNOS ctermfg=NONE ctermbg=16 cterm=underline
+ hi WarningMsg ctermfg=196 ctermbg=NONE cterm=NONE
+ hi WildMenu ctermfg=16 ctermbg=226 cterm=NONE
+ hi Comment ctermfg=111 ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=217 ctermbg=NONE cterm=NONE
+ hi Error ctermfg=160 ctermbg=231 cterm=reverse
+ hi Identifier ctermfg=87 ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=16 ctermbg=16 cterm=NONE
+ hi PreProc ctermfg=213 ctermbg=NONE cterm=NONE
+ hi Special ctermfg=214 ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=227 ctermbg=NONE cterm=bold
+ hi Todo ctermfg=21 ctermbg=226 cterm=NONE
+ hi Type ctermfg=83 ctermbg=NONE cterm=bold
+ hi Underlined ctermfg=153 ctermbg=NONE cterm=underline
+ hi CursorIM ctermfg=NONE ctermbg=fg cterm=NONE
+ hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi ToolbarButton ctermfg=16 ctermbg=254 cterm=bold
+ hi DiffAdd ctermfg=231 ctermbg=65 cterm=NONE
+ hi DiffChange ctermfg=231 ctermbg=67 cterm=NONE
+ hi DiffText ctermfg=16 ctermbg=251 cterm=NONE
+ hi DiffDelete ctermfg=231 ctermbg=133 cterm=NONE
+ unlet s:t_Co
+ finish
+endif
+
+if s:t_Co >= 16
+ hi Normal ctermfg=white ctermbg=black cterm=NONE
+ hi ColorColumn ctermfg=white ctermbg=darkred cterm=NONE
+ hi CursorLineNr ctermfg=yellow ctermbg=NONE cterm=underline
+ hi CursorColumn ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
+ hi Folded ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi QuickFixLine ctermfg=black ctermbg=yellow cterm=NONE
+ hi Conceal ctermfg=grey ctermbg=grey cterm=NONE
+ hi Cursor ctermfg=black ctermbg=green cterm=NONE
+ hi Directory ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi EndOfBuffer ctermfg=darkred ctermbg=NONE cterm=bold
+ hi ErrorMsg ctermfg=darkred ctermbg=white cterm=reverse
+ hi FoldColumn ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi LineNr ctermfg=yellow ctermbg=NONE cterm=NONE
+ hi MatchParen ctermfg=NONE ctermbg=darkblue cterm=NONE
+ hi ModeMsg ctermfg=white ctermbg=darkblue cterm=bold
+ hi MoreMsg ctermfg=darkgreen ctermbg=NONE cterm=bold
+ hi NonText ctermfg=darkred ctermbg=NONE cterm=bold
+ hi Pmenu ctermfg=white ctermbg=darkgrey cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi PmenuSel ctermfg=black ctermbg=darkcyan cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=white cterm=NONE
+ hi Question ctermfg=blue ctermbg=NONE cterm=bold
+ hi Search ctermfg=white ctermbg=red cterm=NONE
+ hi SignColumn ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi SpellBad ctermfg=red ctermbg=NONE cterm=underline
+ hi SpellCap ctermfg=green ctermbg=NONE cterm=underline
+ hi SpellLocal ctermfg=cyan ctermbg=NONE cterm=underline
+ hi SpellRare ctermfg=magenta ctermbg=NONE cterm=underline
+ hi StatusLine ctermfg=darkblue ctermbg=white cterm=bold
+ hi StatusLineNC ctermfg=darkblue ctermbg=grey cterm=NONE
+ hi TabLine ctermfg=darkblue ctermbg=white cterm=bold
+ hi TabLineFill ctermfg=darkblue ctermbg=white cterm=bold
+ hi TabLineSel ctermfg=white ctermbg=darkblue cterm=bold
+ hi Title ctermfg=magenta ctermbg=NONE cterm=bold
+ hi VertSplit ctermfg=darkblue ctermbg=grey cterm=NONE
+ hi Visual ctermfg=NONE ctermbg=darkgrey cterm=reverse
+ hi VisualNOS ctermfg=NONE ctermbg=black cterm=underline
+ hi WarningMsg ctermfg=red ctermbg=NONE cterm=NONE
+ hi WildMenu ctermfg=black ctermbg=yellow cterm=NONE
+ hi Comment ctermfg=blue ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi Error ctermfg=darkred ctermbg=white cterm=reverse
+ hi Identifier ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=black ctermbg=black cterm=NONE
+ hi PreProc ctermfg=magenta ctermbg=NONE cterm=NONE
+ hi Special ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=yellow ctermbg=NONE cterm=bold
+ hi Todo ctermfg=darkblue ctermbg=yellow cterm=NONE
+ hi Type ctermfg=green ctermbg=NONE cterm=bold
+ hi Underlined ctermfg=blue ctermbg=NONE cterm=underline
+ hi CursorIM ctermfg=NONE ctermbg=fg cterm=NONE
+ hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi ToolbarButton ctermfg=black ctermbg=grey cterm=bold
+ hi DiffAdd ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi DiffChange ctermfg=white ctermbg=blue cterm=NONE
+ hi DiffText ctermfg=black ctermbg=grey cterm=NONE
+ hi DiffDelete ctermfg=white ctermbg=magenta cterm=NONE
+ unlet s:t_Co
+ finish
+endif
+
+if s:t_Co >= 8
+ hi Normal ctermfg=grey ctermbg=black cterm=NONE
+ hi ColorColumn ctermfg=grey ctermbg=darkred cterm=NONE
+ hi CursorLineNr ctermfg=darkyellow ctermbg=NONE cterm=underline
+ hi CursorColumn ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
+ hi Folded ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi QuickFixLine ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi Conceal ctermfg=grey ctermbg=NONE cterm=NONE
+ hi Directory ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi EndOfBuffer ctermfg=darkred ctermbg=NONE cterm=bold
+ hi ErrorMsg ctermfg=darkred ctermbg=grey cterm=bold,reverse
+ hi FoldColumn ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi LineNr ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi MatchParen ctermfg=NONE ctermbg=darkblue cterm=NONE
+ hi ModeMsg ctermfg=white ctermbg=darkblue cterm=bold
+ hi MoreMsg ctermfg=darkgreen ctermbg=NONE cterm=bold
+ hi NonText ctermfg=darkred ctermbg=NONE cterm=bold
+ hi Pmenu ctermfg=grey ctermbg=darkgrey cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=grey cterm=NONE
+ hi PmenuSel ctermfg=black ctermbg=darkcyan cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=darkcyan cterm=NONE
+ hi Question ctermfg=darkblue ctermbg=NONE cterm=bold
+ hi Search ctermfg=grey ctermbg=darkred cterm=NONE
+ hi SignColumn ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi SpellBad ctermfg=darkred ctermbg=darkyellow cterm=reverse
+ hi SpellCap ctermfg=darkgreen ctermbg=NONE cterm=reverse
+ hi SpellLocal ctermfg=darkcyan ctermbg=NONE cterm=reverse
+ hi SpellRare ctermfg=darkmagenta ctermbg=darkyellow cterm=reverse
+ hi StatusLine ctermfg=grey ctermbg=darkblue cterm=bold,reverse
+ hi StatusLineNC ctermfg=grey ctermbg=darkblue cterm=reverse
+ hi TabLine ctermfg=grey ctermbg=darkblue cterm=bold,reverse
+ hi TabLineFill ctermfg=darkblue ctermbg=grey cterm=bold
+ hi TabLineSel ctermfg=grey ctermbg=darkblue cterm=bold
+ hi Title ctermfg=darkmagenta ctermbg=NONE cterm=bold
+ hi VertSplit ctermfg=darkblue ctermbg=grey cterm=NONE
+ hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi VisualNOS ctermfg=NONE ctermbg=black cterm=underline
+ hi WarningMsg ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi WildMenu ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi Comment ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi Error ctermfg=darkred ctermbg=grey cterm=reverse
+ hi Identifier ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=black ctermbg=black cterm=NONE
+ hi PreProc ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi Special ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=darkyellow ctermbg=NONE cterm=bold
+ hi Todo ctermfg=darkblue ctermbg=darkyellow cterm=NONE
+ hi Type ctermfg=darkgreen ctermbg=NONE cterm=bold
+ hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline
+ hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi ToolbarButton ctermfg=grey ctermbg=black cterm=bold,reverse
+ hi DiffAdd ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi DiffChange ctermfg=white ctermbg=darkblue cterm=NONE
+ hi DiffText ctermfg=black ctermbg=grey cterm=NONE
+ hi DiffDelete ctermfg=white ctermbg=darkmagenta cterm=NONE
+ unlet s:t_Co
+ finish
+endif
+
+if s:t_Co >= 0
+ hi Normal term=NONE
+ hi ColorColumn term=reverse
+ hi Conceal term=NONE
+ hi Cursor term=reverse
+ hi CursorColumn term=NONE
+ hi CursorLine term=underline
+ hi CursorLineNr term=bold
+ hi DiffAdd term=reverse
+ hi DiffChange term=NONE
+ hi DiffDelete term=reverse
+ hi DiffText term=reverse
+ hi Directory term=NONE
+ hi EndOfBuffer term=NONE
+ hi ErrorMsg term=bold,reverse
+ hi FoldColumn term=NONE
+ hi Folded term=NONE
+ hi IncSearch term=bold,reverse,underline
+ hi LineNr term=NONE
+ hi MatchParen term=bold,underline
+ hi ModeMsg term=bold
+ hi MoreMsg term=NONE
+ hi NonText term=NONE
+ hi Pmenu term=reverse
+ hi PmenuSbar term=reverse
+ hi PmenuSel term=bold
+ hi PmenuThumb term=NONE
+ hi Question term=standout
+ hi Search term=reverse
+ hi SignColumn term=reverse
+ hi SpecialKey term=bold
+ hi SpellBad term=underline
+ hi SpellCap term=underline
+ hi SpellLocal term=underline
+ hi SpellRare term=underline
+ hi StatusLine term=bold,reverse
+ hi StatusLineNC term=bold,underline
+ hi TabLine term=bold,underline
+ hi TabLineFill term=NONE
+ hi Terminal term=NONE
+ hi TabLineSel term=bold,reverse
+ hi Title term=NONE
+ hi VertSplit term=NONE
+ hi Visual term=reverse
+ hi VisualNOS term=NONE
+ hi WarningMsg term=standout
+ hi WildMenu term=bold
+ hi CursorIM term=NONE
+ hi ToolbarLine term=reverse
+ hi ToolbarButton term=bold,reverse
+ hi CurSearch term=reverse
+ hi CursorLineFold term=underline
+ hi CursorLineSign term=underline
+ hi Comment term=bold
+ hi Constant term=NONE
+ hi Error term=bold,reverse
+ hi Identifier term=NONE
+ hi Ignore term=NONE
+ hi PreProc term=NONE
+ hi Special term=NONE
+ hi Statement term=NONE
+ hi Todo term=bold,reverse
+ hi Type term=NONE
+ hi Underlined term=underline
+ unlet s:t_Co
+ finish
endif
-let g:colors_name = "koehler"
-hi Normal guifg=white guibg=black
-hi Scrollbar guifg=darkcyan guibg=cyan
-hi Menu guifg=black guibg=cyan
-hi SpecialKey term=bold cterm=bold ctermfg=darkred guifg=#cc0000
-hi NonText term=bold cterm=bold ctermfg=darkred gui=bold guifg=#cc0000
-hi Directory term=bold cterm=bold ctermfg=brown guifg=#cc8000
-hi ErrorMsg term=standout cterm=bold ctermfg=grey ctermbg=red guifg=White guibg=Red
-hi Search term=reverse ctermfg=white ctermbg=red guifg=white guibg=Red
-hi MoreMsg term=bold cterm=bold ctermfg=darkgreen gui=bold guifg=SeaGreen
-hi ModeMsg term=bold cterm=bold gui=bold guifg=White guibg=Blue
-hi LineNr term=underline cterm=bold ctermfg=darkcyan guifg=Yellow
-hi Question term=standout cterm=bold ctermfg=darkgreen gui=bold guifg=Green
-hi StatusLine term=bold,reverse cterm=bold ctermfg=lightblue ctermbg=white gui=bold guifg=blue guibg=white
-hi StatusLineNC term=reverse ctermfg=white ctermbg=lightblue guifg=white guibg=blue
-hi Title term=bold cterm=bold ctermfg=darkmagenta gui=bold guifg=Magenta
-hi Visual term=reverse cterm=reverse gui=reverse
-hi WarningMsg term=standout cterm=bold ctermfg=darkred guifg=Red
-hi Cursor guifg=bg guibg=Green
-hi Comment term=bold cterm=bold ctermfg=cyan guifg=#80a0ff
-hi Constant term=underline cterm=bold ctermfg=magenta guifg=#ffa0a0
-hi Special term=bold cterm=bold ctermfg=red guifg=Orange
-hi Identifier term=underline ctermfg=brown guifg=#40ffff
-hi Statement term=bold cterm=bold ctermfg=yellow gui=bold guifg=#ffff60
-hi PreProc term=underline ctermfg=darkmagenta guifg=#ff80ff
-hi Type term=underline cterm=bold ctermfg=lightgreen gui=bold guifg=#60ff60
-hi Error term=reverse ctermfg=darkcyan ctermbg=black guifg=Red guibg=Black
-hi Todo term=standout ctermfg=black ctermbg=darkcyan guifg=Blue guibg=Yellow
-hi CursorLine term=underline guibg=#555555 cterm=underline
-hi CursorColumn term=underline guibg=#555555 cterm=underline
-hi MatchParen term=reverse ctermfg=blue guibg=Blue
-hi TabLine term=bold,reverse cterm=bold ctermfg=lightblue ctermbg=white gui=bold guifg=blue guibg=white
-hi TabLineFill term=bold,reverse cterm=bold ctermfg=lightblue ctermbg=white gui=bold guifg=blue guibg=white
-hi TabLineSel term=reverse ctermfg=white ctermbg=lightblue guifg=white guibg=blue
-hi Underlined term=underline cterm=bold,underline ctermfg=lightblue guifg=lightblue gui=bold,underline
-hi Ignore ctermfg=black ctermbg=black guifg=black guibg=black
-hi EndOfBuffer term=bold cterm=bold ctermfg=darkred guifg=#cc0000 gui=bold
-hi link IncSearch Visual
-hi link String Constant
-hi link Character Constant
-hi link Number Constant
-hi link Boolean Constant
-hi link Float Number
-hi link Function Identifier
-hi link Conditional Statement
-hi link Repeat Statement
-hi link Label Statement
-hi link Operator Statement
-hi link Keyword Statement
-hi link Exception Statement
-hi link Include PreProc
-hi link Define PreProc
-hi link Macro PreProc
-hi link PreCondit PreProc
-hi link StorageClass Type
-hi link Structure Type
-hi link Typedef Type
-hi link Tag Special
-hi link SpecialChar Special
-hi link Delimiter Special
-hi link SpecialComment Special
-hi link Debug Special
+
+" Background: dark
+" Color: xterm0 #000000 16 black
+" Color: xterm1 #cd0000 160 darkred
+" Color: xterm2 #00cd00 40 darkgreen
+" Color: xterm3 #cdcd00 184 darkyellow
+" Color: xterm4 #0000ee 20 darkblue
+" Color: xterm5 #cd00cd 164 darkmagenta
+" Color: xterm6 #00cdcd 44 darkcyan
+" Color: xterm7 #e5e5e5 254 grey
+" Color: xterm8 #7f7f7f 102 darkgrey
+" Color: xterm9 #ff0000 196 red
+" Color: xterm10 #00ff00 46 green
+" Color: xterm11 #ffff00 226 yellow
+" Color: xterm12 #5c5cff 63 blue
+" Color: xterm13 #ff00ff 201 magenta
+" Color: xterm14 #00ffff 51 cyan
+" Color: xterm15 #ffffff 231 white
+" Color: Pmenu #444444 238 darkgrey
+" Color: rgbGrey40 #666666 59 darkgrey
+" Color: rgbDarkGrey #a9a9a9 145 grey
+" Color: rgbDarkBlue #00008b 20 darkblue
+" Color: rgbDarkMagenta #8b008b 90 darkmagenta
+" Color: rgbBlue #0000ff 21 darkblue
+" Color: rgbDarkCyan #008b8b 44 darkcyan
+" Color: rgbSeaGreen #2e8b57 29 darkgreen
+" Color: rgbGrey #bebebe 250 grey
+" Color: StatusLineTerm #90ee90 120 darkgreen
+" Color: ToolbarLine #7f7f7f 244 darkgrey
+" Color: Comment #80a0ff 111 blue
+" Color: Constant #ffa0a0 217 darkred
+" Color: Special #ffa500 214 darkyellow
+" Color: Identifier #40ffff 87 cyan
+" Color: Statement #ffff60 227 yellow
+" Color: PreProc #ff80ff 213 magenta
+" Color: Type #60ff60 83 green
+" Color: koeDirectory #cc8000 172 darkyellow
+" Color: koeCursorLine #555555 240 black
+" Color: koeLightBlue #ADD8E6 153 blue
+" Color: koeDarkRed #8b0000 88 darkred
+" Term colors: xterm0 xterm1 xterm2 xterm3 xterm4 xterm5 xterm6 xterm7
+" Term colors: xterm8 xterm9 xterm10 xterm11 xterm12 xterm13
+" Term colors: xterm14 xterm15
+" Color: bgDiffA #5F875F 65 darkgreen
+" Color: bgDiffC #5F87AF 67 blue
+" Color: bgDiffD #AF5FAF 133 magenta
+" Color: bgDiffT #C6C6C6 251 grey
+" Color: fgDiffW #FFFFFF 231 white
+" Color: fgDiffB #000000 16 black
+" Color: bgDiffC8 #5F87AF 67 darkblue
+" Color: bgDiffD8 #AF5FAF 133 darkmagenta
+" vim: et ts=2 sw=2
diff --git a/runtime/colors/morning.vim b/runtime/colors/morning.vim
index fca9c2a742..8a76fdf928 100644
--- a/runtime/colors/morning.vim
+++ b/runtime/colors/morning.vim
@@ -1,55 +1,399 @@
-" Vim color file
-" Maintainer: Bram Moolenaar <Bram@vim.org>
-" Last Change: 2006 Apr 15
+" Name: morning
+" Description: Colorscheme with light grey background.
+" Author: Original author Bram Moolenaar <Bram@vim.org>
+" Maintainer: Original maintainer Bram Moolenaar <Bram@vim.org>
+" Website: https://github.com/vim/colorschemes
+" License: Same as Vim
+" Last Updated: Sun Jun 12 10:55:30 2022
-" This color scheme uses a light grey background.
+" Generated by Colortemplate v2.2.0
-" First remove all existing highlighting.
set background=light
+
hi clear
-if exists("syntax_on")
- syntax reset
+let g:colors_name = 'morning'
+
+let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co > 1 ? &t_Co : 1
+
+if (has('termguicolors') && &termguicolors) || has('gui_running')
+ let g:terminal_ansi_colors = ['#e4e4e4', '#a52a2a', '#ff00ff', '#6a0dad', '#008787', '#2e8b57', '#6a5acd', '#bcbcbc', '#0000ff', '#a52a2a', '#ff00ff', '#6a0dad', '#008787', '#2e8b57', '#6a5acd', '#000000']
+endif
+hi! link Terminal Normal
+hi! link LineNrAbove LineNr
+hi! link LineNrBelow LineNr
+hi! link CurSearch Search
+hi! link CursorLineFold CursorLine
+hi! link CursorLineSign CursorLine
+hi! link StatuslineTerm Statusline
+hi! link StatuslineTermNC StatuslineNC
+hi Normal guifg=#000000 guibg=#e4e4e4 gui=NONE cterm=NONE
+hi EndOfBuffer guifg=#0000ff guibg=#cccccc gui=bold cterm=bold
+hi Folded guifg=#00008b guibg=#d3d3d3 gui=NONE cterm=NONE
+hi CursorLine guifg=NONE guibg=#d3d3d3 gui=NONE cterm=NONE
+hi CursorColumn guifg=NONE guibg=#d3d3d3 gui=NONE cterm=NONE
+hi CursorLineNr guifg=#a52a2a guibg=NONE gui=bold cterm=bold
+hi QuickFixLine guifg=#000000 guibg=#ffff00 gui=NONE cterm=NONE
+hi StatusLine guifg=#eeeeee guibg=#000000 gui=bold cterm=bold
+hi StatusLineNC guifg=#bcbcbc guibg=#000000 gui=NONE cterm=NONE
+hi VertSplit guifg=#bcbcbc guibg=#000000 gui=NONE cterm=NONE
+hi Pmenu guifg=#000000 guibg=#b2b2b2 gui=NONE cterm=NONE
+hi PmenuSel guifg=#000000 guibg=#ffff00 gui=NONE cterm=NONE
+hi PmenuSbar guifg=NONE guibg=#e4e4e4 gui=NONE cterm=NONE
+hi PmenuThumb guifg=NONE guibg=#000000 gui=NONE cterm=NONE
+hi TabLine guifg=#000000 guibg=#bcbcbc gui=underline cterm=underline
+hi TabLineFill guifg=NONE guibg=NONE gui=reverse ctermfg=NONE ctermbg=NONE cterm=reverse
+hi TabLineSel guifg=#000000 guibg=#e4e4e4 gui=bold cterm=bold
+hi ToolbarLine guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
+hi ToolbarButton guifg=NONE guibg=#bcbcbc gui=bold cterm=bold
+hi NonText guifg=#0000ff guibg=#bcbcbc gui=bold cterm=bold
+hi SpecialKey guifg=#bcbcbc guibg=NONE gui=NONE cterm=NONE
+hi Visual guifg=NONE guibg=#d0d0d0 gui=NONE cterm=NONE
+hi VisualNOS guifg=NONE guibg=#0000ff gui=NONE cterm=NONE
+hi LineNr guifg=#a52a2a guibg=NONE gui=NONE cterm=NONE
+hi FoldColumn guifg=#00008b guibg=NONE gui=NONE cterm=NONE
+hi SignColumn guifg=#00008b guibg=NONE gui=NONE cterm=NONE
+hi Underlined guifg=#6a5acd guibg=NONE gui=underline cterm=underline
+hi Error guifg=#ff0000 guibg=#e4e4e4 gui=reverse cterm=reverse
+hi ErrorMsg guifg=#ff0000 guibg=#e4e4e4 gui=reverse cterm=reverse
+hi WarningMsg guifg=#6a0dad guibg=NONE gui=bold cterm=bold
+hi MoreMsg guifg=#2e8b57 guibg=NONE gui=bold cterm=bold
+hi ModeMsg guifg=#000000 guibg=NONE gui=bold cterm=bold
+hi Question guifg=#008787 guibg=NONE gui=bold cterm=bold
+hi Todo guifg=#000000 guibg=#ffff00 gui=NONE cterm=NONE
+hi MatchParen guifg=#e4e4e4 guibg=#6a5acd gui=NONE cterm=NONE
+hi Search guifg=#e4e4e4 guibg=#6a0dad gui=NONE cterm=NONE
+hi IncSearch guifg=#2e8b57 guibg=NONE gui=reverse cterm=reverse
+hi WildMenu guifg=#000000 guibg=#ffff00 gui=bold cterm=bold
+hi ColorColumn guifg=#000000 guibg=#ffffff gui=NONE cterm=NONE
+hi Cursor guifg=#e4e4e4 guibg=#2e8b57 gui=NONE cterm=NONE
+hi lCursor guifg=#e4e4e4 guibg=#a52a2a gui=NONE cterm=NONE
+hi SpellBad guifg=#ff0000 guibg=NONE guisp=#ff0000 gui=undercurl cterm=underline
+hi SpellCap guifg=#00d700 guibg=NONE guisp=#00d700 gui=undercurl cterm=underline
+hi SpellLocal guifg=#a52a2a guibg=NONE guisp=#a52a2a gui=undercurl cterm=underline
+hi SpellRare guifg=#2e8b57 guibg=NONE guisp=#2e8b57 gui=undercurl cterm=underline
+hi Comment guifg=#0000ff guibg=NONE gui=NONE cterm=NONE
+hi Constant guifg=#ff00ff guibg=#eeeeee gui=NONE cterm=NONE
+hi Identifier guifg=#008787 guibg=NONE gui=NONE cterm=NONE
+hi Statement guifg=#a52a2a guibg=NONE gui=bold cterm=bold
+hi PreProc guifg=#6a0dad guibg=NONE gui=NONE cterm=NONE
+hi Type guifg=#2e8b57 guibg=NONE gui=bold cterm=bold
+hi Special guifg=#6a5acd guibg=NONE gui=NONE cterm=NONE
+hi Ignore guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
+hi Directory guifg=#008787 guibg=NONE gui=bold cterm=bold
+hi Conceal guifg=#0000ff guibg=NONE gui=NONE cterm=NONE
+hi Title guifg=#a52a2a guibg=NONE gui=bold cterm=bold
+hi DiffAdd guifg=#ffffff guibg=#5f875f gui=NONE cterm=NONE
+hi DiffChange guifg=#ffffff guibg=#5f87af gui=NONE cterm=NONE
+hi DiffText guifg=#000000 guibg=#c6c6c6 gui=NONE cterm=NONE
+hi DiffDelete guifg=#ffffff guibg=#af5faf gui=NONE cterm=NONE
+
+if s:t_Co >= 256
+ hi! link Terminal Normal
+ hi! link LineNrAbove LineNr
+ hi! link LineNrBelow LineNr
+ hi! link CurSearch Search
+ hi! link CursorLineFold CursorLine
+ hi! link CursorLineSign CursorLine
+ hi! link StatuslineTerm Statusline
+ hi! link StatuslineTermNC StatuslineNC
+ hi Normal ctermfg=16 ctermbg=254 cterm=NONE
+ hi EndOfBuffer ctermfg=21 ctermbg=252 cterm=bold
+ hi Folded ctermfg=18 ctermbg=252 cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=252 cterm=NONE
+ hi CursorColumn ctermfg=NONE ctermbg=252 cterm=NONE
+ hi CursorLineNr ctermfg=124 ctermbg=NONE cterm=bold
+ hi QuickFixLine ctermfg=16 ctermbg=226 cterm=NONE
+ hi StatusLine ctermfg=255 ctermbg=16 cterm=bold
+ hi StatusLineNC ctermfg=250 ctermbg=16 cterm=NONE
+ hi VertSplit ctermfg=250 ctermbg=16 cterm=NONE
+ hi Pmenu ctermfg=16 ctermbg=249 cterm=NONE
+ hi PmenuSel ctermfg=16 ctermbg=226 cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=254 cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=16 cterm=NONE
+ hi TabLine ctermfg=16 ctermbg=250 cterm=underline
+ hi TabLineFill ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi TabLineSel ctermfg=16 ctermbg=254 cterm=bold
+ hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi ToolbarButton ctermfg=NONE ctermbg=250 cterm=bold
+ hi NonText ctermfg=21 ctermbg=250 cterm=bold
+ hi SpecialKey ctermfg=250 ctermbg=NONE cterm=NONE
+ hi Visual ctermfg=NONE ctermbg=252 cterm=NONE
+ hi VisualNOS ctermfg=NONE ctermbg=21 cterm=NONE
+ hi LineNr ctermfg=124 ctermbg=NONE cterm=NONE
+ hi FoldColumn ctermfg=18 ctermbg=NONE cterm=NONE
+ hi SignColumn ctermfg=18 ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=62 ctermbg=NONE cterm=underline
+ hi Error ctermfg=196 ctermbg=254 cterm=reverse
+ hi ErrorMsg ctermfg=196 ctermbg=254 cterm=reverse
+ hi WarningMsg ctermfg=55 ctermbg=NONE cterm=bold
+ hi MoreMsg ctermfg=29 ctermbg=NONE cterm=bold
+ hi ModeMsg ctermfg=16 ctermbg=NONE cterm=bold
+ hi Question ctermfg=30 ctermbg=NONE cterm=bold
+ hi Todo ctermfg=16 ctermbg=226 cterm=NONE
+ hi MatchParen ctermfg=254 ctermbg=62 cterm=NONE
+ hi Search ctermfg=254 ctermbg=55 cterm=NONE
+ hi IncSearch ctermfg=29 ctermbg=NONE cterm=reverse
+ hi WildMenu ctermfg=16 ctermbg=226 cterm=bold
+ hi ColorColumn ctermfg=16 ctermbg=231 cterm=NONE
+ hi Cursor ctermfg=254 ctermbg=29 cterm=NONE
+ hi lCursor ctermfg=254 ctermbg=124 cterm=NONE
+ hi SpellBad ctermfg=196 ctermbg=NONE cterm=underline
+ hi SpellCap ctermfg=40 ctermbg=NONE cterm=underline
+ hi SpellLocal ctermfg=124 ctermbg=NONE cterm=underline
+ hi SpellRare ctermfg=29 ctermbg=NONE cterm=underline
+ hi Comment ctermfg=21 ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=201 ctermbg=255 cterm=NONE
+ hi Identifier ctermfg=30 ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=124 ctermbg=NONE cterm=bold
+ hi PreProc ctermfg=55 ctermbg=NONE cterm=NONE
+ hi Type ctermfg=29 ctermbg=NONE cterm=bold
+ hi Special ctermfg=62 ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Directory ctermfg=30 ctermbg=NONE cterm=bold
+ hi Conceal ctermfg=21 ctermbg=NONE cterm=NONE
+ hi Title ctermfg=124 ctermbg=NONE cterm=bold
+ hi DiffAdd ctermfg=231 ctermbg=65 cterm=NONE
+ hi DiffChange ctermfg=231 ctermbg=67 cterm=NONE
+ hi DiffText ctermfg=16 ctermbg=251 cterm=NONE
+ hi DiffDelete ctermfg=231 ctermbg=133 cterm=NONE
+ unlet s:t_Co
+ finish
+endif
+
+if s:t_Co >= 16
+ hi Normal ctermfg=black ctermbg=grey cterm=NONE
+ hi EndOfBuffer ctermfg=blue ctermbg=NONE cterm=NONE
+ hi Folded ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorColumn ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorLineNr ctermfg=darkred ctermbg=NONE cterm=underline
+ hi QuickFixLine ctermfg=black ctermbg=yellow cterm=NONE
+ hi StatusLine ctermfg=white ctermbg=black cterm=bold
+ hi StatusLineNC ctermfg=darkgrey ctermbg=black cterm=NONE
+ hi VertSplit ctermfg=darkgrey ctermbg=black cterm=NONE
+ hi Pmenu ctermfg=black ctermbg=white cterm=NONE
+ hi PmenuSel ctermfg=black ctermbg=yellow cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=grey cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=black cterm=NONE
+ hi TabLine ctermfg=black ctermbg=white cterm=underline
+ hi TabLineFill ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi TabLineSel ctermfg=black ctermbg=grey cterm=bold
+ hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi ToolbarButton ctermfg=NONE ctermbg=white cterm=bold
+ hi NonText ctermfg=blue ctermbg=white cterm=bold
+ hi SpecialKey ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi Visual ctermfg=NONE ctermbg=white cterm=NONE
+ hi VisualNOS ctermfg=NONE ctermbg=blue cterm=NONE
+ hi LineNr ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi FoldColumn ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi SignColumn ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=darkblue ctermbg=NONE cterm=underline
+ hi Error ctermfg=red ctermbg=grey cterm=reverse
+ hi ErrorMsg ctermfg=red ctermbg=grey cterm=reverse
+ hi WarningMsg ctermfg=darkmagenta ctermbg=NONE cterm=bold
+ hi MoreMsg ctermfg=darkgreen ctermbg=NONE cterm=bold
+ hi ModeMsg ctermfg=black ctermbg=NONE cterm=bold
+ hi Question ctermfg=darkcyan ctermbg=NONE cterm=bold
+ hi Todo ctermfg=black ctermbg=yellow cterm=NONE
+ hi MatchParen ctermfg=grey ctermbg=darkblue cterm=NONE
+ hi Search ctermfg=grey ctermbg=darkmagenta cterm=NONE
+ hi IncSearch ctermfg=darkgreen ctermbg=NONE cterm=reverse
+ hi WildMenu ctermfg=black ctermbg=yellow cterm=bold
+ hi ColorColumn ctermfg=black ctermbg=white cterm=NONE
+ hi Cursor ctermfg=grey ctermbg=darkgreen cterm=NONE
+ hi lCursor ctermfg=grey ctermbg=darkred cterm=NONE
+ hi SpellBad ctermfg=red ctermbg=NONE cterm=underline
+ hi SpellCap ctermfg=darkcyan ctermbg=NONE cterm=underline
+ hi SpellLocal ctermfg=darkred ctermbg=NONE cterm=underline
+ hi SpellRare ctermfg=darkgreen ctermbg=NONE cterm=underline
+ hi Comment ctermfg=blue ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=magenta ctermbg=white cterm=NONE
+ hi Identifier ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=darkred ctermbg=NONE cterm=bold
+ hi PreProc ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi Type ctermfg=darkgreen ctermbg=NONE cterm=bold
+ hi Special ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Directory ctermfg=darkcyan ctermbg=NONE cterm=bold
+ hi Conceal ctermfg=blue ctermbg=NONE cterm=NONE
+ hi Title ctermfg=darkred ctermbg=NONE cterm=bold
+ hi DiffAdd ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi DiffChange ctermfg=white ctermbg=blue cterm=NONE
+ hi DiffText ctermfg=black ctermbg=grey cterm=NONE
+ hi DiffDelete ctermfg=white ctermbg=magenta cterm=NONE
+ unlet s:t_Co
+ finish
+endif
+
+if s:t_Co >= 8
+ hi Normal ctermfg=black ctermbg=gray cterm=NONE
+ hi EndOfBuffer ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi Folded ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorColumn ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi CursorLineNr ctermfg=NONE ctermbg=NONE cterm=bold
+ hi QuickFixLine ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi StatusLine ctermfg=black ctermbg=gray cterm=bold,reverse
+ hi StatusLineNC ctermfg=black ctermbg=gray cterm=reverse
+ hi VertSplit ctermfg=black ctermbg=gray cterm=reverse
+ hi Pmenu ctermfg=black ctermbg=darkcyan cterm=NONE
+ hi PmenuSel ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=darkgreen cterm=NONE
+ hi TabLine ctermfg=gray ctermbg=black cterm=NONE
+ hi TabLineFill ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi TabLineSel ctermfg=black ctermbg=gray cterm=NONE
+ hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi ToolbarButton ctermfg=black ctermbg=gray cterm=bold,reverse
+ hi NonText ctermfg=darkblue ctermbg=NONE cterm=bold
+ hi SpecialKey ctermfg=darkblue ctermbg=NONE cterm=bold
+ hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi VisualNOS ctermfg=NONE ctermbg=NONE cterm=underline
+ hi LineNr ctermfg=NONE ctermbg=NONE cterm=bold
+ hi FoldColumn ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi SignColumn ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline
+ hi Error ctermfg=darkred ctermbg=gray cterm=reverse
+ hi ErrorMsg ctermfg=darkred ctermbg=gray cterm=reverse
+ hi WarningMsg ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi MoreMsg ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi ModeMsg ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Question ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Todo ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi MatchParen ctermfg=gray ctermbg=darkblue cterm=NONE
+ hi Search ctermfg=gray ctermbg=darkmagenta cterm=NONE
+ hi IncSearch ctermfg=darkgreen ctermbg=NONE cterm=reverse
+ hi WildMenu ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi ColorColumn ctermfg=black ctermbg=darkred cterm=NONE
+ hi SpellBad ctermfg=darkred ctermbg=NONE cterm=reverse
+ hi SpellCap ctermfg=darkcyan ctermbg=NONE cterm=reverse
+ hi SpellLocal ctermfg=darkmagenta ctermbg=NONE cterm=reverse
+ hi SpellRare ctermfg=darkgreen ctermbg=NONE cterm=reverse
+ hi Comment ctermfg=darkblue ctermbg=NONE cterm=bold
+ hi Constant ctermfg=darkmagenta ctermbg=NONE cterm=bold
+ hi Identifier ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=darkred ctermbg=NONE cterm=bold
+ hi PreProc ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi Type ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi Special ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Directory ctermfg=darkcyan ctermbg=NONE cterm=bold
+ hi Conceal ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Title ctermfg=darkred ctermbg=NONE cterm=bold
+ hi DiffAdd ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi DiffChange ctermfg=white ctermbg=darkblue cterm=NONE
+ hi DiffText ctermfg=black ctermbg=grey cterm=NONE
+ hi DiffDelete ctermfg=white ctermbg=darkmagenta cterm=NONE
+ unlet s:t_Co
+ finish
endif
-let colors_name = "morning"
-
-hi Normal ctermfg=Black ctermbg=LightGrey guifg=Black guibg=grey90
-
-" Groups used in the 'highlight' and 'guicursor' options default value.
-hi ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White
-hi IncSearch term=reverse cterm=reverse gui=reverse
-hi ModeMsg term=bold cterm=bold gui=bold
-hi StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold
-hi StatusLineNC term=reverse cterm=reverse gui=reverse
-hi VertSplit term=reverse cterm=reverse gui=reverse
-hi Visual term=reverse ctermbg=grey guibg=grey80
-hi DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red
-hi Cursor guibg=Green guifg=NONE
-hi lCursor guibg=Cyan guifg=NONE
-hi Directory term=bold ctermfg=DarkBlue guifg=Blue
-hi LineNr term=underline ctermfg=Brown guifg=Brown
-hi MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen
-hi NonText term=bold ctermfg=Blue gui=bold guifg=Blue guibg=grey80
-hi Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen
-hi Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE
-hi SpecialKey term=bold ctermfg=DarkBlue guifg=Blue
-hi Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta
-hi WarningMsg term=standout ctermfg=DarkRed guifg=Red
-hi WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black
-hi Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue
-hi FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue
-hi DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue
-hi DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta
-hi DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan
-hi CursorLine term=underline cterm=underline guibg=grey80
-hi CursorColumn term=reverse ctermbg=grey guibg=grey80
-
-" Colors for syntax highlighting
-hi Constant term=underline ctermfg=DarkRed guifg=Magenta guibg=grey95
-hi Special term=bold ctermfg=DarkMagenta guifg=SlateBlue guibg=grey95
-if &t_Co > 8
- hi Statement term=bold cterm=bold ctermfg=Brown gui=bold guifg=Brown
+if s:t_Co >= 0
+ hi Normal term=NONE
+ hi ColorColumn term=reverse
+ hi Conceal term=NONE
+ hi Cursor term=reverse
+ hi CursorColumn term=NONE
+ hi CursorLine term=underline
+ hi CursorLineNr term=bold
+ hi DiffAdd term=reverse
+ hi DiffChange term=NONE
+ hi DiffDelete term=reverse
+ hi DiffText term=reverse
+ hi Directory term=NONE
+ hi EndOfBuffer term=NONE
+ hi ErrorMsg term=bold,reverse
+ hi FoldColumn term=NONE
+ hi Folded term=NONE
+ hi IncSearch term=bold,reverse,underline
+ hi LineNr term=NONE
+ hi MatchParen term=bold,underline
+ hi ModeMsg term=bold
+ hi MoreMsg term=NONE
+ hi NonText term=NONE
+ hi Pmenu term=reverse
+ hi PmenuSbar term=reverse
+ hi PmenuSel term=bold
+ hi PmenuThumb term=NONE
+ hi Question term=standout
+ hi Search term=reverse
+ hi SignColumn term=reverse
+ hi SpecialKey term=bold
+ hi SpellBad term=underline
+ hi SpellCap term=underline
+ hi SpellLocal term=underline
+ hi SpellRare term=underline
+ hi StatusLine term=bold,reverse
+ hi StatusLineNC term=bold,underline
+ hi TabLine term=bold,underline
+ hi TabLineFill term=NONE
+ hi Terminal term=NONE
+ hi TabLineSel term=bold,reverse
+ hi Title term=NONE
+ hi VertSplit term=NONE
+ hi Visual term=reverse
+ hi VisualNOS term=NONE
+ hi WarningMsg term=standout
+ hi WildMenu term=bold
+ hi CursorIM term=NONE
+ hi ToolbarLine term=reverse
+ hi ToolbarButton term=bold,reverse
+ hi CurSearch term=reverse
+ hi CursorLineFold term=underline
+ hi CursorLineSign term=underline
+ hi Comment term=bold
+ hi Constant term=NONE
+ hi Error term=bold,reverse
+ hi Identifier term=NONE
+ hi Ignore term=NONE
+ hi PreProc term=NONE
+ hi Special term=NONE
+ hi Statement term=NONE
+ hi Todo term=bold,reverse
+ hi Type term=NONE
+ hi Underlined term=underline
+ unlet s:t_Co
+ finish
endif
-hi Ignore ctermfg=LightGrey guifg=grey90
-" vim: sw=2
+" Background: light
+" Color: comment #0000ff 21 blue
+" Color: constant #ff00ff 201 magenta
+" Color: identifier #008787 30 darkcyan
+" Color: statement #a52a2a 124 darkred
+" Color: preproc #6a0dad 55 darkmagenta
+" Color: type #2e8b57 29 darkgreen
+" Color: special #6a5acd 62 darkblue
+" Color: fg0 #000000 16 black
+" Color: bg0 #e4e4e4 254 grey
+" Color: bg1 #bcbcbc 250 white
+" Color: status #bcbcbc 250 darkgrey
+" Color: bg2 #eeeeee 255 white
+" Color: endofbuffer #cccccc 252 darkgrey
+" Color: visual #d0d0d0 252 white
+" Color: folded #d3d3d3 252 darkgrey
+" Color: folded_fg #00008b 18 darkblue
+" Color: pmenu #b2b2b2 249 white
+" Color: wildmenu #ffff00 226 yellow
+" Color: error #ff0000 196 red
+" Color: colorcolumn #ffffff 231 white
+" Color: spellcap #00d700 40 green
+" Color: black #000000 16 black
+" Color: darkred #870000 88 darkred
+" Color: darkgreen #008700 28 darkgreen
+" Color: darkyellow #878700 100 darkyellow
+" Color: darkblue #000087 18 darkblue
+" Color: darkmagenta #870087 18 darkmagenta
+" Color: darkcyan #008787 30 darkcyan
+" Color: gray #878787 102 gray
+" Term colors: bg0 statement constant preproc identifier type special bg1
+" Term colors: comment statement constant preproc identifier type special fg0
+" Color: bgDiffA #5F875F 65 darkgreen
+" Color: bgDiffC #5F87AF 67 blue
+" Color: bgDiffD #AF5FAF 133 magenta
+" Color: bgDiffT #C6C6C6 251 grey
+" Color: fgDiffW #FFFFFF 231 white
+" Color: fgDiffB #000000 16 black
+" Color: bgDiffC8 #5F87AF 67 darkblue
+" Color: bgDiffD8 #AF5FAF 133 darkmagenta
+" vim: et ts=2 sw=2
diff --git a/runtime/colors/murphy.vim b/runtime/colors/murphy.vim
index 1f439964ef..1ba096ecec 100644
--- a/runtime/colors/murphy.vim
+++ b/runtime/colors/murphy.vim
@@ -1,41 +1,393 @@
-" local syntax file - set colors on a per-machine basis:
-" vim: tw=0 ts=4 sw=4
-" Vim color file
-" Maintainer: Ron Aaron <ron@ronware.org>
-" Last Change: 2003 May 02
+" Name: murphy
+" Description: Green foreground black background.
+" Author: Original author Ron Aaron <ron@ronware.org>.
+" Maintainer: Original maintainer Ron Aaron <ron@ronware.org>.
+" Website: https://github.com/vim/colorschemes
+" License: Same as Vim
+" Last Updated: Sun Jun 12 10:56:21 2022
+
+" Generated by Colortemplate v2.2.0
-hi clear
set background=dark
-if exists("syntax_on")
- syntax reset
+
+hi clear
+let g:colors_name = 'murphy'
+
+let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co > 1 ? &t_Co : 1
+
+if (has('termguicolors') && &termguicolors) || has('gui_running')
+ let g:terminal_ansi_colors = ['#303030', '#ffa700', '#005f00', '#ffd7af', '#87afff', '#ffafaf', '#00afaf', '#bcbcbc', '#444444', '#ff0000', '#00875f', '#ffff00', '#005fff', '#ff00ff', '#00ffff', '#ffffff']
+endif
+hi! link Terminal Normal
+hi! link LineNrAbove LineNr
+hi! link LineNrBelow LineNr
+hi! link CurSearch Search
+hi! link CursorLineFold CursorLine
+hi! link CursorLineSign CursorLine
+hi! link StatusLineTerm StatusLine
+hi! link StatusLineTermNC StatusLineNC
+hi Normal guifg=#87ff87 guibg=#000000 gui=NONE cterm=NONE
+hi EndOfBuffer guifg=#0000ff guibg=#000000 gui=NONE cterm=NONE
+hi StatusLine guifg=#ffffff guibg=#00008b gui=NONE cterm=NONE
+hi StatusLineNC guifg=#ffffff guibg=#3a3a3a gui=NONE cterm=NONE
+hi VertSplit guifg=#ffffff guibg=#3a3a3a gui=NONE cterm=NONE
+hi Pmenu guifg=#ffffff guibg=#444444 gui=NONE cterm=NONE
+hi PmenuSel guifg=#000000 guibg=#ffff00 gui=NONE cterm=NONE
+hi PmenuSbar guifg=NONE guibg=#303030 gui=NONE cterm=NONE
+hi PmenuThumb guifg=NONE guibg=#bcbcbc gui=NONE cterm=NONE
+hi TabLineFill guifg=NONE guibg=#303030 gui=NONE cterm=NONE
+hi TabLine guifg=#87ff87 guibg=#444444 gui=NONE cterm=NONE
+hi TabLineSel guifg=#ffffff guibg=#000000 gui=NONE cterm=NONE
+hi ToolbarLine guifg=NONE guibg=#303030 gui=NONE cterm=NONE
+hi ToolbarButton guifg=#ffffff guibg=#444444 gui=bold cterm=bold
+hi NonText guifg=#00afaf guibg=NONE gui=NONE cterm=NONE
+hi SpecialKey guifg=#00afaf guibg=NONE gui=NONE cterm=NONE
+hi QuickFixLine guifg=#303030 guibg=#00afaf gui=NONE cterm=NONE
+hi Folded guifg=#00afaf guibg=#303030 gui=NONE cterm=NONE
+hi CursorLine guifg=NONE guibg=#444444 gui=NONE cterm=NONE
+hi CursorColumn guifg=NONE guibg=#444444 gui=NONE cterm=NONE
+hi CursorLineNr guifg=#ffff00 guibg=NONE gui=bold cterm=bold
+hi ColorColumn guifg=NONE guibg=#262626 gui=NONE cterm=NONE
+hi Visual guifg=#ffffff guibg=#005f00 gui=NONE cterm=NONE
+hi VisualNOS guifg=#ffffff guibg=#005f00 gui=NONE cterm=NONE
+hi LineNr guifg=#ffff00 guibg=NONE gui=NONE cterm=NONE
+hi FoldColumn guifg=#00afaf guibg=NONE gui=NONE cterm=NONE
+hi SignColumn guifg=#00afaf guibg=NONE gui=NONE cterm=NONE
+hi Underlined guifg=#00afaf guibg=NONE gui=underline cterm=underline
+hi Error guifg=#ffffff guibg=#ff0000 gui=NONE cterm=NONE
+hi ErrorMsg guifg=#ffffff guibg=#ff0000 gui=NONE cterm=NONE
+hi ModeMsg guifg=#87ff87 guibg=NONE gui=bold cterm=bold
+hi WarningMsg guifg=#ffa700 guibg=NONE gui=bold cterm=bold
+hi MoreMsg guifg=#005f00 guibg=NONE gui=bold cterm=bold
+hi Question guifg=#00ffff guibg=NONE gui=bold cterm=bold
+hi Todo guifg=#0000ff guibg=#ffff00 gui=NONE cterm=NONE
+hi MatchParen guifg=#303030 guibg=#ffd7af gui=NONE cterm=NONE
+hi Search guifg=#ffffff guibg=#0000ff gui=NONE cterm=NONE
+hi IncSearch guifg=#ffa700 guibg=NONE gui=reverse cterm=reverse
+hi WildMenu guifg=#000000 guibg=#ffff00 gui=NONE cterm=NONE
+hi Cursor guifg=#000000 guibg=#87ff87 gui=NONE cterm=NONE
+hi lCursor guifg=#000000 guibg=#ff0000 gui=NONE cterm=NONE
+hi SpellBad guifg=#ff0000 guibg=NONE guisp=#ff0000 gui=undercurl cterm=underline
+hi SpellCap guifg=#00ffff guibg=NONE guisp=#00ffff gui=undercurl cterm=underline
+hi SpellLocal guifg=#ffd7af guibg=NONE guisp=#ffd7af gui=undercurl cterm=underline
+hi SpellRare guifg=#ffff00 guibg=NONE guisp=#ffff00 gui=undercurl cterm=underline
+hi Comment guifg=#ffa700 guibg=NONE gui=NONE cterm=NONE
+hi Identifier guifg=#00ffff guibg=NONE gui=NONE cterm=NONE
+hi Statement guifg=#ffff00 guibg=NONE gui=NONE cterm=NONE
+hi Constant guifg=#ffffff guibg=NONE gui=NONE cterm=NONE
+hi PreProc guifg=#ffd7af guibg=NONE gui=NONE cterm=NONE
+hi Type guifg=#bcbcbc guibg=NONE gui=NONE cterm=NONE
+hi Special guifg=#ff00ff guibg=NONE gui=NONE cterm=NONE
+hi Directory guifg=#00ffff guibg=NONE gui=NONE cterm=NONE
+hi Conceal guifg=#bcbcbc guibg=NONE gui=NONE cterm=NONE
+hi Ignore guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
+hi Title guifg=#ff00ff guibg=NONE gui=bold cterm=bold
+hi DiffAdd guifg=#ffffff guibg=#5f875f gui=NONE cterm=NONE
+hi DiffChange guifg=#ffffff guibg=#5f87af gui=NONE cterm=NONE
+hi DiffText guifg=#000000 guibg=#c6c6c6 gui=NONE cterm=NONE
+hi DiffDelete guifg=#ffffff guibg=#af5faf gui=NONE cterm=NONE
+
+if s:t_Co >= 256
+ hi! link Terminal Normal
+ hi! link LineNrAbove LineNr
+ hi! link LineNrBelow LineNr
+ hi! link CurSearch Search
+ hi! link CursorLineFold CursorLine
+ hi! link CursorLineSign CursorLine
+ hi! link StatusLineTerm StatusLine
+ hi! link StatusLineTermNC StatusLineNC
+ hi Normal ctermfg=120 ctermbg=16 cterm=NONE
+ hi EndOfBuffer ctermfg=21 ctermbg=16 cterm=NONE
+ hi StatusLine ctermfg=231 ctermbg=18 cterm=NONE
+ hi StatusLineNC ctermfg=231 ctermbg=237 cterm=NONE
+ hi VertSplit ctermfg=231 ctermbg=237 cterm=NONE
+ hi Pmenu ctermfg=231 ctermbg=238 cterm=NONE
+ hi PmenuSel ctermfg=16 ctermbg=226 cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=236 cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=250 cterm=NONE
+ hi TabLineFill ctermfg=NONE ctermbg=236 cterm=NONE
+ hi TabLine ctermfg=120 ctermbg=238 cterm=NONE
+ hi TabLineSel ctermfg=231 ctermbg=16 cterm=NONE
+ hi ToolbarLine ctermfg=NONE ctermbg=236 cterm=NONE
+ hi ToolbarButton ctermfg=231 ctermbg=238 cterm=bold
+ hi NonText ctermfg=37 ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=37 ctermbg=NONE cterm=NONE
+ hi QuickFixLine ctermfg=236 ctermbg=37 cterm=NONE
+ hi Folded ctermfg=37 ctermbg=236 cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=238 cterm=NONE
+ hi CursorColumn ctermfg=NONE ctermbg=238 cterm=NONE
+ hi CursorLineNr ctermfg=226 ctermbg=NONE cterm=bold
+ hi ColorColumn ctermfg=NONE ctermbg=235 cterm=NONE
+ hi Visual ctermfg=231 ctermbg=22 cterm=NONE
+ hi VisualNOS ctermfg=231 ctermbg=22 cterm=NONE
+ hi LineNr ctermfg=226 ctermbg=NONE cterm=NONE
+ hi FoldColumn ctermfg=37 ctermbg=NONE cterm=NONE
+ hi SignColumn ctermfg=37 ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=37 ctermbg=NONE cterm=underline
+ hi Error ctermfg=231 ctermbg=196 cterm=NONE
+ hi ErrorMsg ctermfg=231 ctermbg=196 cterm=NONE
+ hi ModeMsg ctermfg=120 ctermbg=NONE cterm=bold
+ hi WarningMsg ctermfg=214 ctermbg=NONE cterm=bold
+ hi MoreMsg ctermfg=22 ctermbg=NONE cterm=bold
+ hi Question ctermfg=51 ctermbg=NONE cterm=bold
+ hi Todo ctermfg=21 ctermbg=226 cterm=NONE
+ hi MatchParen ctermfg=236 ctermbg=223 cterm=NONE
+ hi Search ctermfg=231 ctermbg=21 cterm=NONE
+ hi IncSearch ctermfg=214 ctermbg=NONE cterm=reverse
+ hi WildMenu ctermfg=16 ctermbg=226 cterm=NONE
+ hi Cursor ctermfg=16 ctermbg=120 cterm=NONE
+ hi lCursor ctermfg=16 ctermbg=196 cterm=NONE
+ hi SpellBad ctermfg=196 ctermbg=NONE cterm=underline
+ hi SpellCap ctermfg=51 ctermbg=NONE cterm=underline
+ hi SpellLocal ctermfg=223 ctermbg=NONE cterm=underline
+ hi SpellRare ctermfg=226 ctermbg=NONE cterm=underline
+ hi Comment ctermfg=214 ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=51 ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=226 ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=231 ctermbg=NONE cterm=NONE
+ hi PreProc ctermfg=223 ctermbg=NONE cterm=NONE
+ hi Type ctermfg=250 ctermbg=NONE cterm=NONE
+ hi Special ctermfg=201 ctermbg=NONE cterm=NONE
+ hi Directory ctermfg=51 ctermbg=NONE cterm=NONE
+ hi Conceal ctermfg=250 ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Title ctermfg=201 ctermbg=NONE cterm=bold
+ hi DiffAdd ctermfg=231 ctermbg=65 cterm=NONE
+ hi DiffChange ctermfg=231 ctermbg=67 cterm=NONE
+ hi DiffText ctermfg=16 ctermbg=251 cterm=NONE
+ hi DiffDelete ctermfg=231 ctermbg=133 cterm=NONE
+ unlet s:t_Co
+ finish
endif
-let g:colors_name = "murphy"
-
-hi Normal ctermbg=Black ctermfg=lightgreen guibg=Black guifg=lightgreen
-hi Comment term=bold ctermfg=LightRed guifg=Orange
-hi Constant term=underline ctermfg=LightGreen guifg=White gui=NONE
-hi Identifier term=underline ctermfg=LightCyan guifg=#00ffff
-hi Ignore ctermfg=black guifg=bg
-hi PreProc term=underline ctermfg=LightBlue guifg=Wheat
-hi Search term=reverse guifg=white guibg=Blue
-hi Special term=bold ctermfg=LightRed guifg=magenta
-hi Statement term=bold ctermfg=Yellow guifg=#ffff00 gui=NONE
-hi Type ctermfg=LightGreen guifg=grey gui=none
-hi Error term=reverse ctermbg=Red ctermfg=White guibg=Red guifg=White
-hi Todo term=standout ctermbg=Yellow ctermfg=Black guifg=Blue guibg=Yellow
-" From the source:
-hi Cursor guifg=Orchid guibg=fg
-hi Directory term=bold ctermfg=LightCyan guifg=Cyan
-hi ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White
-hi IncSearch term=reverse cterm=reverse gui=reverse
-hi LineNr term=underline ctermfg=Yellow guifg=Yellow
-hi ModeMsg term=bold cterm=bold gui=bold
-hi MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen
-hi NonText term=bold ctermfg=Blue gui=bold guifg=Blue
-hi Question term=standout ctermfg=LightGreen gui=bold guifg=Cyan
-hi SpecialKey term=bold ctermfg=LightBlue guifg=Cyan
-hi StatusLine term=reverse,bold cterm=reverse gui=NONE guifg=White guibg=darkblue
-hi StatusLineNC term=reverse cterm=reverse gui=NONE guifg=white guibg=#333333
-hi Title term=bold ctermfg=LightMagenta gui=bold guifg=Pink
-hi WarningMsg term=standout ctermfg=LightRed guifg=Red
-hi Visual term=reverse cterm=reverse gui=NONE guifg=white guibg=darkgreen
+
+if s:t_Co >= 16
+ hi Normal ctermfg=green ctermbg=black cterm=NONE
+ hi EndOfBuffer ctermfg=blue ctermbg=black cterm=NONE
+ hi StatusLine ctermfg=white ctermbg=darkblue cterm=NONE
+ hi StatusLineNC ctermfg=white ctermbg=darkgrey cterm=NONE
+ hi VertSplit ctermfg=white ctermbg=darkgrey cterm=NONE
+ hi Pmenu ctermfg=white ctermbg=darkgrey cterm=NONE
+ hi PmenuSel ctermfg=black ctermbg=yellow cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=black cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=grey cterm=NONE
+ hi TabLineFill ctermfg=NONE ctermbg=grey cterm=NONE
+ hi TabLine ctermfg=green ctermbg=darkgrey cterm=NONE
+ hi TabLineSel ctermfg=white ctermbg=black cterm=NONE
+ hi ToolbarLine ctermfg=NONE ctermbg=black cterm=NONE
+ hi ToolbarButton ctermfg=white ctermbg=darkgrey cterm=bold
+ hi NonText ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi QuickFixLine ctermfg=black ctermbg=darkcyan cterm=NONE
+ hi Folded ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorColumn ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorLineNr ctermfg=yellow ctermbg=NONE cterm=underline
+ hi ColorColumn ctermfg=green ctermbg=darkgrey cterm=NONE
+ hi Visual ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi VisualNOS ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi LineNr ctermfg=yellow ctermbg=NONE cterm=NONE
+ hi FoldColumn ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi SignColumn ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=darkcyan ctermbg=NONE cterm=underline
+ hi Error ctermfg=white ctermbg=red cterm=NONE
+ hi ErrorMsg ctermfg=white ctermbg=red cterm=NONE
+ hi ModeMsg ctermfg=green ctermbg=NONE cterm=bold
+ hi WarningMsg ctermfg=darkred ctermbg=NONE cterm=bold
+ hi MoreMsg ctermfg=darkgreen ctermbg=NONE cterm=bold
+ hi Question ctermfg=cyan ctermbg=NONE cterm=bold
+ hi Todo ctermfg=blue ctermbg=yellow cterm=NONE
+ hi MatchParen ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi Search ctermfg=white ctermbg=blue cterm=NONE
+ hi IncSearch ctermfg=darkred ctermbg=NONE cterm=reverse
+ hi WildMenu ctermfg=black ctermbg=yellow cterm=NONE
+ hi Cursor ctermfg=black ctermbg=green cterm=NONE
+ hi lCursor ctermfg=black ctermbg=red cterm=NONE
+ hi SpellBad ctermfg=red ctermbg=NONE cterm=underline
+ hi SpellCap ctermfg=cyan ctermbg=NONE cterm=underline
+ hi SpellLocal ctermfg=darkyellow ctermbg=NONE cterm=underline
+ hi SpellRare ctermfg=yellow ctermbg=NONE cterm=underline
+ hi Comment ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=yellow ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=white ctermbg=NONE cterm=NONE
+ hi PreProc ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi Type ctermfg=grey ctermbg=NONE cterm=NONE
+ hi Special ctermfg=magenta ctermbg=NONE cterm=NONE
+ hi Directory ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi Conceal ctermfg=grey ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Title ctermfg=magenta ctermbg=NONE cterm=bold
+ hi DiffAdd ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi DiffChange ctermfg=white ctermbg=blue cterm=NONE
+ hi DiffText ctermfg=black ctermbg=grey cterm=NONE
+ hi DiffDelete ctermfg=white ctermbg=magenta cterm=NONE
+ unlet s:t_Co
+ finish
+endif
+
+if s:t_Co >= 8
+ hi Normal ctermfg=darkgreen ctermbg=black cterm=NONE
+ hi EndOfBuffer ctermfg=darkblue ctermbg=NONE cterm=bold
+ hi StatusLine ctermfg=darkblue ctermbg=grey cterm=bold,reverse
+ hi StatusLineNC ctermfg=grey ctermbg=black cterm=reverse
+ hi VertSplit ctermfg=grey ctermbg=black cterm=reverse
+ hi Pmenu ctermfg=black ctermbg=darkcyan cterm=NONE
+ hi PmenuSel ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=black cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=grey cterm=NONE
+ hi TabLineFill ctermfg=NONE ctermbg=grey cterm=NONE
+ hi TabLine ctermfg=grey ctermbg=black cterm=reverse
+ hi TabLineSel ctermfg=grey ctermbg=black cterm=NONE
+ hi ToolbarLine ctermfg=NONE ctermbg=black cterm=NONE
+ hi ToolbarButton ctermfg=grey ctermbg=black cterm=bold,reverse
+ hi NonText ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi QuickFixLine ctermfg=black ctermbg=darkcyan cterm=NONE
+ hi Folded ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorColumn ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorLineNr ctermfg=darkyellow ctermbg=NONE cterm=bold
+ hi ColorColumn ctermfg=grey ctermbg=darkred cterm=NONE
+ hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline
+ hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi VisualNOS ctermfg=NONE ctermbg=NONE cterm=reverse,underline
+ hi LineNr ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi FoldColumn ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi SignColumn ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline
+ hi Error ctermfg=grey ctermbg=darkred cterm=NONE
+ hi ErrorMsg ctermfg=grey ctermbg=darkred cterm=NONE
+ hi ModeMsg ctermfg=grey ctermbg=NONE cterm=bold
+ hi WarningMsg ctermfg=darkred ctermbg=NONE cterm=bold
+ hi MoreMsg ctermfg=darkgreen ctermbg=NONE cterm=bold
+ hi Question ctermfg=darkcyan ctermbg=NONE cterm=bold
+ hi Todo ctermfg=darkblue ctermbg=darkyellow cterm=NONE
+ hi MatchParen ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi Search ctermfg=grey ctermbg=darkblue cterm=NONE
+ hi IncSearch ctermfg=darkred ctermbg=NONE cterm=reverse
+ hi WildMenu ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi SpellBad ctermfg=darkred ctermbg=darkyellow cterm=reverse
+ hi SpellCap ctermfg=darkcyan ctermbg=NONE cterm=reverse
+ hi SpellLocal ctermfg=darkyellow ctermbg=NONE cterm=reverse
+ hi SpellRare ctermfg=darkmagenta ctermbg=darkyellow cterm=reverse
+ hi Comment ctermfg=darkred ctermbg=NONE cterm=bold
+ hi Identifier ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=grey ctermbg=NONE cterm=bold
+ hi PreProc ctermfg=darkmagenta ctermbg=NONE cterm=bold
+ hi Type ctermfg=grey ctermbg=NONE cterm=NONE
+ hi Special ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi Directory ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Conceal ctermfg=grey ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Title ctermfg=darkmagenta ctermbg=NONE cterm=bold
+ hi DiffAdd ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi DiffChange ctermfg=white ctermbg=darkblue cterm=NONE
+ hi DiffText ctermfg=black ctermbg=grey cterm=NONE
+ hi DiffDelete ctermfg=white ctermbg=darkmagenta cterm=NONE
+ unlet s:t_Co
+ finish
+endif
+
+if s:t_Co >= 0
+ hi Normal term=NONE
+ hi ColorColumn term=reverse
+ hi Conceal term=NONE
+ hi Cursor term=reverse
+ hi CursorColumn term=NONE
+ hi CursorLine term=underline
+ hi CursorLineNr term=bold
+ hi DiffAdd term=reverse
+ hi DiffChange term=NONE
+ hi DiffDelete term=reverse
+ hi DiffText term=reverse
+ hi Directory term=NONE
+ hi EndOfBuffer term=NONE
+ hi ErrorMsg term=bold,reverse
+ hi FoldColumn term=NONE
+ hi Folded term=NONE
+ hi IncSearch term=bold,reverse,underline
+ hi LineNr term=NONE
+ hi MatchParen term=bold,underline
+ hi ModeMsg term=bold
+ hi MoreMsg term=NONE
+ hi NonText term=NONE
+ hi Pmenu term=reverse
+ hi PmenuSbar term=reverse
+ hi PmenuSel term=bold
+ hi PmenuThumb term=NONE
+ hi Question term=standout
+ hi Search term=reverse
+ hi SignColumn term=reverse
+ hi SpecialKey term=bold
+ hi SpellBad term=underline
+ hi SpellCap term=underline
+ hi SpellLocal term=underline
+ hi SpellRare term=underline
+ hi StatusLine term=bold,reverse
+ hi StatusLineNC term=bold,underline
+ hi TabLine term=bold,underline
+ hi TabLineFill term=NONE
+ hi Terminal term=NONE
+ hi TabLineSel term=bold,reverse
+ hi Title term=NONE
+ hi VertSplit term=NONE
+ hi Visual term=reverse
+ hi VisualNOS term=NONE
+ hi WarningMsg term=standout
+ hi WildMenu term=bold
+ hi CursorIM term=NONE
+ hi ToolbarLine term=reverse
+ hi ToolbarButton term=bold,reverse
+ hi CurSearch term=reverse
+ hi CursorLineFold term=underline
+ hi CursorLineSign term=underline
+ hi Comment term=bold
+ hi Constant term=NONE
+ hi Error term=bold,reverse
+ hi Identifier term=NONE
+ hi Ignore term=NONE
+ hi PreProc term=NONE
+ hi Special term=NONE
+ hi Statement term=NONE
+ hi Todo term=bold,reverse
+ hi Type term=NONE
+ hi Underlined term=underline
+ unlet s:t_Co
+ finish
+endif
+
+" Background: dark
+" Color: foreground #87FF87 120 green
+" Color: background #000000 16 black
+" Color: color00 #303030 236 black
+" Color: color08 #444444 238 darkgrey
+" Color: color01 #FFA700 214 darkred
+" Color: color09 #FF0000 196 red
+" Color: color02 #005F00 22 darkgreen
+" Color: color10 #00875F 29 green
+" Color: color03 #FFD7AF 223 darkyellow
+" Color: color11 #FFFF00 226 yellow
+" Color: color04 #00008B 18 darkblue
+" Color: color12 #0000FF 21 blue
+" Color: color05 #FFAFAF 217 darkmagenta
+" Color: color13 #FF00FF 201 magenta
+" Color: color06 #00AFAF 37 darkcyan
+" Color: color14 #00FFFF 51 cyan
+" Color: color07 #BCBCBC 250 grey
+" Color: color15 #FFFFFF 231 white
+" Color: color16 #262626 235 black
+" Color: color17 #3A3A3A 237 darkgrey
+" Color: color04t #87AFFF 111 darkblue
+" Color: color12t #005FFF 27 blue
+" Term colors: color00 color01 color02 color03 color04t color05 color06 color07
+" Term colors: color08 color09 color10 color11 color12t color13 color14 color15
+" Color: bgDiffA #5F875F 65 darkgreen
+" Color: bgDiffC #5F87AF 67 blue
+" Color: bgDiffD #AF5FAF 133 magenta
+" Color: bgDiffT #C6C6C6 251 grey
+" Color: fgDiffW #FFFFFF 231 white
+" Color: fgDiffB #000000 16 black
+" Color: bgDiffC8 #5F87AF 67 darkblue
+" Color: bgDiffD8 #AF5FAF 133 darkmagenta
+" vim: et ts=2 sw=2
diff --git a/runtime/colors/pablo.vim b/runtime/colors/pablo.vim
index e6bf737046..1dc086d820 100644
--- a/runtime/colors/pablo.vim
+++ b/runtime/colors/pablo.vim
@@ -1,26 +1,433 @@
-" local syntax file - set colors on a per-machine basis:
-" vim: tw=0 ts=4 sw=4
-" Vim color file
-" Maintainer: Ron Aaron <ron@ronware.org>
-" Last Change: 2003 May 02
+" Name: pablo
+" Author: Ron Aaron <ron@ronware.org>
+" Maintainer: Original maintainerRon Aaron <ron@ronware.org>
+" Website: https://github.com/vim/colorschemes
+" License: Same as Vim
+" Last Updated: Sun Jun 12 10:57:11 2022
+
+" Generated by Colortemplate v2.2.0
-hi clear
set background=dark
-if exists("syntax_on")
- syntax reset
+
+hi clear
+let g:colors_name = 'pablo'
+
+let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co > 1 ? &t_Co : 1
+
+if (has('termguicolors') && &termguicolors) || has('gui_running')
+ let g:terminal_ansi_colors = ['#000000', '#cd0000', '#00cd00', '#cdcd00', '#0000ee', '#cd00cd', '#00cdcd', '#e5e5e5', '#7f7f7f', '#ff0000', '#00ff00', '#ffff00', '#5c5cff', '#ff00ff', '#00ffff', '#ffffff']
+endif
+hi Normal guifg=#ffffff guibg=#000000 gui=NONE cterm=NONE
+hi! link Terminal Normal
+hi! link StatusLineTerm StatusLine
+hi! link StatusLineTermNC StatusLineNC
+hi! link CurSearch Search
+hi! link CursorLineFold CursorLine
+hi! link CursorLineSign CursorLine
+hi Comment guifg=#808080 guibg=NONE gui=NONE cterm=NONE
+hi Constant guifg=#00ffff guibg=NONE gui=NONE cterm=NONE
+hi Identifier guifg=#00c0c0 guibg=NONE gui=NONE cterm=NONE
+hi Statement guifg=#c0c000 guibg=NONE gui=bold cterm=bold
+hi PreProc guifg=#00ff00 guibg=NONE gui=NONE cterm=NONE
+hi Type guifg=#00c000 guibg=NONE gui=NONE cterm=NONE
+hi Special guifg=#0000ff guibg=NONE gui=NONE cterm=NONE
+hi Underlined guifg=#80a0ff guibg=NONE gui=underline cterm=underline
+hi Ignore guifg=#000000 guibg=#000000 gui=NONE cterm=NONE
+hi Error guifg=#ffffff guibg=#ff0000 gui=NONE cterm=NONE
+hi Todo guifg=#000000 guibg=#c0c000 gui=NONE cterm=NONE
+hi Conceal guifg=#e5e5e5 guibg=#a9a9a9 gui=NONE cterm=NONE
+hi Cursor guifg=#000000 guibg=#ffffff gui=NONE cterm=NONE
+hi lCursor guifg=#000000 guibg=#ffffff gui=NONE cterm=NONE
+hi CursorIM guifg=NONE guibg=fg gui=NONE cterm=NONE
+hi Title guifg=#ff00ff guibg=NONE gui=bold cterm=bold
+hi Directory guifg=#00c000 guibg=NONE gui=NONE cterm=NONE
+hi Search guifg=#000000 guibg=#c0c000 gui=NONE cterm=NONE
+hi IncSearch guifg=#ffffff guibg=NONE gui=reverse cterm=reverse
+hi NonText guifg=#0000ff guibg=NONE gui=bold cterm=bold
+hi EndOfBuffer guifg=#0000ff guibg=NONE gui=bold cterm=bold
+hi ErrorMsg guifg=#ffffff guibg=#cd0000 gui=NONE cterm=NONE
+hi WarningMsg guifg=#ff0000 guibg=NONE gui=NONE cterm=NONE
+hi SignColumn guifg=#00ffff guibg=#a9a9a9 gui=NONE cterm=NONE
+hi ColorColumn guifg=NONE guibg=#4d4d4d gui=NONE cterm=NONE
+hi FoldColumn guifg=#7f7f7f guibg=#303030 gui=NONE cterm=NONE
+hi Folded guifg=#7f7f7f guibg=#303030 gui=NONE cterm=NONE
+hi CursorColumn guifg=NONE guibg=#3a3a3a gui=NONE cterm=NONE
+hi CursorLine guifg=NONE guibg=#3a3a3a gui=NONE cterm=NONE
+hi CursorLineNr guifg=#ffff00 guibg=#3a3a3a gui=bold cterm=bold
+hi Visual guifg=#00008b guibg=#a9a9a9 gui=NONE cterm=NONE
+hi VisualNOS guifg=NONE guibg=#000000 gui=bold,underline cterm=underline
+hi LineNr guifg=#7f7f7f guibg=NONE gui=NONE cterm=NONE
+hi! link LineNrAbove LineNr
+hi! link LineNrBelow LineNr
+hi MatchParen guifg=NONE guibg=#008b8b gui=NONE cterm=NONE
+hi ModeMsg guifg=NONE guibg=NONE gui=bold ctermfg=NONE ctermbg=NONE cterm=bold
+hi MoreMsg guifg=#5c5cff guibg=NONE gui=bold cterm=bold
+hi Question guifg=#00ff00 guibg=NONE gui=bold cterm=bold
+hi SpecialKey guifg=#00ffff guibg=NONE gui=NONE cterm=NONE
+hi WildMenu guifg=#000000 guibg=#ffff00 gui=NONE cterm=NONE
+hi QuickFixLine guifg=#000000 guibg=#00cdcd gui=NONE cterm=NONE
+hi SpellBad guifg=#ff0000 guibg=NONE guisp=#ff0000 gui=undercurl cterm=underline
+hi SpellCap guifg=#5c5cff guibg=NONE guisp=#5c5cff gui=undercurl cterm=underline
+hi SpellLocal guifg=#ff00ff guibg=NONE guisp=#ff00ff gui=undercurl cterm=underline
+hi SpellRare guifg=#ffff00 guibg=NONE guisp=#ffff00 gui=undercurl cterm=underline
+hi StatusLine guifg=#ffff00 guibg=#0000ee gui=NONE cterm=NONE
+hi StatusLineNC guifg=#000000 guibg=#ffffff gui=NONE cterm=NONE
+hi VertSplit guifg=#000000 guibg=#ffffff gui=NONE cterm=NONE
+hi TabLine guifg=#ffffff guibg=#7f7f7f gui=NONE cterm=NONE
+hi TabLineFill guifg=NONE guibg=#000000 gui=reverse cterm=reverse
+hi TabLineSel guifg=#ffffff guibg=#000000 gui=bold cterm=bold
+hi ToolbarLine guifg=NONE guibg=#000000 gui=NONE cterm=NONE
+hi ToolbarButton guifg=#000000 guibg=#e5e5e5 gui=bold cterm=bold
+hi Pmenu guifg=fg guibg=#303030 gui=NONE cterm=NONE
+hi PmenuSbar guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
+hi PmenuSel guifg=#000000 guibg=#e5e5e5 gui=NONE cterm=NONE
+hi PmenuThumb guifg=NONE guibg=#ffffff gui=NONE cterm=NONE
+hi DiffAdd guifg=#ffffff guibg=#5f875f gui=NONE cterm=NONE
+hi DiffChange guifg=#ffffff guibg=#5f87af gui=NONE cterm=NONE
+hi DiffText guifg=#000000 guibg=#c6c6c6 gui=NONE cterm=NONE
+hi DiffDelete guifg=#ffffff guibg=#af5faf gui=NONE cterm=NONE
+
+if s:t_Co >= 256
+ hi Normal ctermfg=231 ctermbg=16 cterm=NONE
+ hi! link Terminal Normal
+ hi! link StatusLineTerm StatusLine
+ hi! link StatusLineTermNC StatusLineNC
+ hi! link CurSearch Search
+ hi! link CursorLineFold CursorLine
+ hi! link CursorLineSign CursorLine
+ hi Comment ctermfg=244 ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=51 ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=37 ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=142 ctermbg=NONE cterm=bold
+ hi PreProc ctermfg=46 ctermbg=NONE cterm=NONE
+ hi Type ctermfg=34 ctermbg=NONE cterm=NONE
+ hi Special ctermfg=21 ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=111 ctermbg=NONE cterm=underline
+ hi Ignore ctermfg=16 ctermbg=16 cterm=NONE
+ hi Error ctermfg=231 ctermbg=196 cterm=NONE
+ hi Todo ctermfg=16 ctermbg=142 cterm=NONE
+ hi Conceal ctermfg=254 ctermbg=248 cterm=NONE
+ hi Cursor ctermfg=16 ctermbg=231 cterm=NONE
+ hi lCursor ctermfg=16 ctermbg=231 cterm=NONE
+ hi CursorIM ctermfg=NONE ctermbg=fg cterm=NONE
+ hi Title ctermfg=225 ctermbg=NONE cterm=bold
+ hi Directory ctermfg=34 ctermbg=NONE cterm=NONE
+ hi Search ctermfg=16 ctermbg=142 cterm=NONE
+ hi IncSearch ctermfg=231 ctermbg=NONE cterm=reverse
+ hi NonText ctermfg=63 ctermbg=NONE cterm=bold
+ hi EndOfBuffer ctermfg=63 ctermbg=NONE cterm=bold
+ hi ErrorMsg ctermfg=231 ctermbg=160 cterm=NONE
+ hi WarningMsg ctermfg=224 ctermbg=NONE cterm=NONE
+ hi SignColumn ctermfg=51 ctermbg=248 cterm=NONE
+ hi ColorColumn ctermfg=NONE ctermbg=239 cterm=NONE
+ hi FoldColumn ctermfg=102 ctermbg=236 cterm=NONE
+ hi Folded ctermfg=102 ctermbg=236 cterm=NONE
+ hi CursorColumn ctermfg=NONE ctermbg=237 cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=237 cterm=NONE
+ hi CursorLineNr ctermfg=226 ctermbg=237 cterm=bold
+ hi Visual ctermfg=20 ctermbg=248 cterm=NONE
+ hi VisualNOS ctermfg=NONE ctermbg=16 cterm=underline
+ hi LineNr ctermfg=102 ctermbg=NONE cterm=NONE
+ hi! link LineNrAbove LineNr
+ hi! link LineNrBelow LineNr
+ hi MatchParen ctermfg=NONE ctermbg=44 cterm=NONE
+ hi ModeMsg ctermfg=NONE ctermbg=NONE cterm=bold
+ hi MoreMsg ctermfg=63 ctermbg=NONE cterm=bold
+ hi Question ctermfg=121 ctermbg=NONE cterm=bold
+ hi SpecialKey ctermfg=81 ctermbg=NONE cterm=NONE
+ hi WildMenu ctermfg=16 ctermbg=226 cterm=NONE
+ hi QuickFixLine ctermfg=16 ctermbg=44 cterm=NONE
+ hi SpellBad ctermfg=196 ctermbg=NONE cterm=underline
+ hi SpellCap ctermfg=63 ctermbg=NONE cterm=underline
+ hi SpellLocal ctermfg=201 ctermbg=NONE cterm=underline
+ hi SpellRare ctermfg=226 ctermbg=NONE cterm=underline
+ hi StatusLine ctermfg=226 ctermbg=20 cterm=NONE
+ hi StatusLineNC ctermfg=16 ctermbg=231 cterm=NONE
+ hi VertSplit ctermfg=16 ctermbg=231 cterm=NONE
+ hi TabLine ctermfg=231 ctermbg=102 cterm=NONE
+ hi TabLineFill ctermfg=NONE ctermbg=16 cterm=reverse
+ hi TabLineSel ctermfg=231 ctermbg=16 cterm=bold
+ hi ToolbarLine ctermfg=NONE ctermbg=16 cterm=NONE
+ hi ToolbarButton ctermfg=16 ctermbg=254 cterm=bold
+ hi Pmenu ctermfg=fg ctermbg=236 cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi PmenuSel ctermfg=16 ctermbg=254 cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=231 cterm=NONE
+ hi DiffAdd ctermfg=231 ctermbg=65 cterm=NONE
+ hi DiffChange ctermfg=231 ctermbg=67 cterm=NONE
+ hi DiffText ctermfg=16 ctermbg=251 cterm=NONE
+ hi DiffDelete ctermfg=231 ctermbg=133 cterm=NONE
+ unlet s:t_Co
+ finish
endif
-let g:colors_name = "pablo"
-
-highlight Comment ctermfg=8 guifg=#808080
-highlight Constant ctermfg=14 cterm=none guifg=#00ffff gui=none
-highlight Identifier ctermfg=6 guifg=#00c0c0
-highlight Statement ctermfg=3 cterm=bold guifg=#c0c000 gui=bold
-highlight PreProc ctermfg=10 guifg=#00ff00
-highlight Type ctermfg=2 guifg=#00c000
-highlight Special ctermfg=12 guifg=#0000ff
-highlight Error ctermbg=9 guibg=#ff0000
-highlight Todo ctermfg=4 ctermbg=3 guifg=#000080 guibg=#c0c000
-highlight Directory ctermfg=2 guifg=#00c000
-highlight StatusLine ctermfg=11 ctermbg=12 cterm=none guifg=#ffff00 guibg=#0000ff gui=none
-highlight Normal guifg=#ffffff guibg=#000000
-highlight Search ctermbg=3 guibg=#c0c000
+
+if s:t_Co >= 16
+ hi Normal ctermfg=white ctermbg=black cterm=NONE
+ hi! link Terminal Normal
+ hi! link StatusLineTerm StatusLine
+ hi! link StatusLineTermNC StatusLineNC
+ hi! link CurSearch Search
+ hi! link CursorLineFold CursorLine
+ hi! link CursorLineSign CursorLine
+ hi Comment ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=darkyellow ctermbg=NONE cterm=bold
+ hi PreProc ctermfg=green ctermbg=NONE cterm=NONE
+ hi Type ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi Special ctermfg=blue ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=darkgreen ctermbg=NONE cterm=underline
+ hi Ignore ctermfg=black ctermbg=black cterm=NONE
+ hi Error ctermfg=white ctermbg=red cterm=NONE
+ hi Todo ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi Conceal ctermfg=grey ctermbg=grey cterm=NONE
+ hi Cursor ctermfg=black ctermbg=white cterm=NONE
+ hi lCursor ctermfg=black ctermbg=white cterm=NONE
+ hi CursorIM ctermfg=NONE ctermbg=fg cterm=NONE
+ hi Title ctermfg=magenta ctermbg=NONE cterm=bold
+ hi Directory ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi Search ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi IncSearch ctermfg=white ctermbg=NONE cterm=reverse
+ hi NonText ctermfg=blue ctermbg=NONE cterm=bold
+ hi EndOfBuffer ctermfg=blue ctermbg=NONE cterm=bold
+ hi ErrorMsg ctermfg=white ctermbg=darkred cterm=NONE
+ hi WarningMsg ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi SignColumn ctermfg=cyan ctermbg=black cterm=NONE
+ hi ColorColumn ctermfg=white ctermbg=darkgrey cterm=NONE
+ hi FoldColumn ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Folded ctermfg=blue ctermbg=NONE cterm=NONE
+ hi CursorColumn ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorLineNr ctermfg=yellow ctermbg=NONE cterm=underline
+ hi Visual ctermfg=darkblue ctermbg=grey cterm=NONE
+ hi VisualNOS ctermfg=NONE ctermbg=black cterm=underline
+ hi LineNr ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi! link LineNrAbove LineNr
+ hi! link LineNrBelow LineNr
+ hi MatchParen ctermfg=NONE ctermbg=darkcyan cterm=NONE
+ hi ModeMsg ctermfg=NONE ctermbg=NONE cterm=bold
+ hi MoreMsg ctermfg=blue ctermbg=NONE cterm=bold
+ hi Question ctermfg=green ctermbg=NONE cterm=bold
+ hi SpecialKey ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi WildMenu ctermfg=black ctermbg=yellow cterm=NONE
+ hi QuickFixLine ctermfg=black ctermbg=darkcyan cterm=NONE
+ hi SpellBad ctermfg=red ctermbg=NONE cterm=underline
+ hi SpellCap ctermfg=blue ctermbg=NONE cterm=underline
+ hi SpellLocal ctermfg=magenta ctermbg=NONE cterm=underline
+ hi SpellRare ctermfg=yellow ctermbg=NONE cterm=underline
+ hi StatusLine ctermfg=yellow ctermbg=darkblue cterm=NONE
+ hi StatusLineNC ctermfg=black ctermbg=white cterm=NONE
+ hi VertSplit ctermfg=black ctermbg=white cterm=NONE
+ hi TabLine ctermfg=white ctermbg=darkgrey cterm=NONE
+ hi TabLineFill ctermfg=NONE ctermbg=black cterm=reverse
+ hi TabLineSel ctermfg=white ctermbg=black cterm=bold
+ hi ToolbarLine ctermfg=NONE ctermbg=black cterm=NONE
+ hi ToolbarButton ctermfg=black ctermbg=grey cterm=bold
+ hi Pmenu ctermfg=fg ctermbg=darkgrey cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi PmenuSel ctermfg=black ctermbg=grey cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=white cterm=NONE
+ hi DiffAdd ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi DiffChange ctermfg=white ctermbg=blue cterm=NONE
+ hi DiffText ctermfg=black ctermbg=grey cterm=NONE
+ hi DiffDelete ctermfg=white ctermbg=magenta cterm=NONE
+ unlet s:t_Co
+ finish
+endif
+
+if s:t_Co >= 8
+ hi Normal ctermfg=grey ctermbg=black cterm=NONE
+ hi EndOfBuffer ctermfg=grey ctermbg=black cterm=bold
+ hi StatusLine ctermfg=darkblue ctermbg=grey cterm=reverse
+ hi StatusLineNC ctermfg=grey ctermbg=black cterm=reverse
+ hi StatusLineTerm ctermfg=darkblue ctermbg=grey cterm=reverse
+ hi StatusLineTermNC ctermfg=grey ctermbg=black cterm=reverse
+ hi VertSplit ctermfg=grey ctermbg=black cterm=reverse
+ hi Pmenu ctermfg=black ctermbg=grey cterm=NONE
+ hi PmenuSel ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=black cterm=NONE
+ hi PmenuThumb ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi TabLine ctermfg=black ctermbg=grey cterm=NONE
+ hi TabLineFill ctermfg=NONE ctermbg=grey cterm=NONE
+ hi TabLineSel ctermfg=grey ctermbg=black cterm=NONE
+ hi ToolbarButton ctermfg=grey ctermbg=black cterm=bold,reverse
+ hi ToolbarLine ctermfg=NONE ctermbg=black cterm=NONE
+ hi NonText ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Folded ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorColumn ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorLineNr ctermfg=darkyellow ctermbg=NONE cterm=bold
+ hi ColorColumn ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi QuickFixLine ctermfg=black ctermbg=darkmagenta cterm=NONE
+ hi VisualNOS ctermfg=black ctermbg=grey cterm=NONE
+ hi LineNr ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi FoldColumn ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi SignColumn ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline
+ hi Error ctermfg=grey ctermbg=darkred cterm=NONE
+ hi ErrorMsg ctermfg=grey ctermbg=darkred cterm=NONE
+ hi ModeMsg ctermfg=grey ctermbg=NONE cterm=NONE
+ hi WarningMsg ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi MoreMsg ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi Question ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi Todo ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi MatchParen ctermfg=black ctermbg=darkcyan cterm=NONE
+ hi Search ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi IncSearch ctermfg=black ctermbg=darkgreen cterm=NONE
+ hi WildMenu ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi SpellBad ctermfg=darkred ctermbg=darkyellow cterm=reverse
+ hi SpellCap ctermfg=darkblue ctermbg=darkyellow cterm=reverse
+ hi SpellLocal ctermfg=darkmagenta ctermbg=darkyellow cterm=reverse
+ hi SpellRare ctermfg=darkgreen ctermbg=NONE cterm=reverse
+ hi Comment ctermfg=grey ctermbg=NONE cterm=bold
+ hi Comment ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=darkcyan ctermbg=NONE cterm=bold
+ hi Identifier ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=darkyellow ctermbg=NONE cterm=bold
+ hi PreProc ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi Type ctermfg=darkgreen ctermbg=NONE cterm=bold
+ hi Special ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline
+ hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Error ctermfg=grey ctermbg=darkred cterm=NONE
+ hi Todo ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi Directory ctermfg=darkgreen ctermbg=NONE cterm=bold
+ hi Conceal ctermfg=grey ctermbg=NONE cterm=NONE
+ hi Title ctermfg=darkmagenta ctermbg=NONE cterm=bold
+ hi DiffAdd ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi DiffChange ctermfg=white ctermbg=darkblue cterm=NONE
+ hi DiffText ctermfg=black ctermbg=grey cterm=NONE
+ hi DiffDelete ctermfg=white ctermbg=darkmagenta cterm=NONE
+ unlet s:t_Co
+ finish
+endif
+
+if s:t_Co >= 0
+ hi Normal term=NONE
+ hi ColorColumn term=reverse
+ hi Conceal term=NONE
+ hi Cursor term=reverse
+ hi CursorColumn term=NONE
+ hi CursorLine term=underline
+ hi CursorLineNr term=bold
+ hi DiffAdd term=reverse
+ hi DiffChange term=NONE
+ hi DiffDelete term=reverse
+ hi DiffText term=reverse
+ hi Directory term=NONE
+ hi EndOfBuffer term=NONE
+ hi ErrorMsg term=bold,reverse
+ hi FoldColumn term=NONE
+ hi Folded term=NONE
+ hi IncSearch term=bold,reverse,underline
+ hi LineNr term=NONE
+ hi MatchParen term=bold,underline
+ hi ModeMsg term=bold
+ hi MoreMsg term=NONE
+ hi NonText term=NONE
+ hi Pmenu term=reverse
+ hi PmenuSbar term=reverse
+ hi PmenuSel term=bold
+ hi PmenuThumb term=NONE
+ hi Question term=standout
+ hi Search term=reverse
+ hi SignColumn term=reverse
+ hi SpecialKey term=bold
+ hi SpellBad term=underline
+ hi SpellCap term=underline
+ hi SpellLocal term=underline
+ hi SpellRare term=underline
+ hi StatusLine term=bold,reverse
+ hi StatusLineNC term=bold,underline
+ hi TabLine term=bold,underline
+ hi TabLineFill term=NONE
+ hi Terminal term=NONE
+ hi TabLineSel term=bold,reverse
+ hi Title term=NONE
+ hi VertSplit term=NONE
+ hi Visual term=reverse
+ hi VisualNOS term=NONE
+ hi WarningMsg term=standout
+ hi WildMenu term=bold
+ hi CursorIM term=NONE
+ hi ToolbarLine term=reverse
+ hi ToolbarButton term=bold,reverse
+ hi CurSearch term=reverse
+ hi CursorLineFold term=underline
+ hi CursorLineSign term=underline
+ hi Comment term=bold
+ hi Constant term=NONE
+ hi Error term=bold,reverse
+ hi Identifier term=NONE
+ hi Ignore term=NONE
+ hi PreProc term=NONE
+ hi Special term=NONE
+ hi Statement term=NONE
+ hi Todo term=bold,reverse
+ hi Type term=NONE
+ hi Underlined term=underline
+ unlet s:t_Co
+ finish
+endif
+
+" Background: dark
+" Color: color00 #000000 16 black
+" Color: color08 #7f7f7f 102 darkgrey
+" Color: color01 #cd0000 160 darkred
+" Color: color09 #ff0000 196 red
+" Color: color02 #00cd00 40 darkgreen
+" Color: color10 #00ff00 46 green
+" Color: color03 #cdcd00 184 darkyellow
+" Color: color11 #ffff00 226 yellow
+" Color: color04 #0000ee 20 darkblue
+" Color: color12 #5c5cff 63 blue
+" Color: color05 #cd00cd 164 darkmagenta
+" Color: color13 #ff00ff 201 magenta
+" Color: color06 #00cdcd 44 darkcyan
+" Color: color14 #00ffff 51 cyan
+" Color: color07 #e5e5e5 254 grey
+" Color: color15 #ffffff 231 white
+" Term colors: color00 color01 color02 color03 color04 color05 color06 color07
+" Term colors: color08 color09 color10 color11 color12 color13 color14 color15
+" Color: rgbGrey30 #4d4d4d 239 darkgrey
+" Color: rgbGrey40 #666666 241 darkgrey
+" Color: rgbDarkGrey #a9a9a9 248 grey
+" Color: rgbDarkBlue #00008b 20 darkblue
+" Color: rgbDarkMagenta #8b008b 164 darkmagenta
+" Color: rgbBlue #0000ff 63 blue
+" Color: rgbDarkCyan #008b8b 44 darkcyan
+" Color: rgbSeaGreen #2e8b57 121 darkgreen
+" Color: rgbGrey #bebebe 248 grey
+" Color: Question #00ff00 121 green
+" Color: SignColumn #a9a9a9 248 black
+" Color: SpecialKey #00ffff 81 cyan
+" Color: StatusLineTerm #90ee90 121 darkgreen
+" Color: Title #ff00ff 225 magenta
+" Color: WarningMsg #ff0000 224 darkred
+" Color: ToolbarLine #7f7f7f 242 darkgrey
+" Color: ToolbarButton #d3d3d3 254 grey
+" Color: Underlined #80a0ff 111 darkgreen
+" Color: Comment #808080 244 darkgrey
+" Color: Constant #00ffff 51 cyan
+" Color: Special #0000ff 21 blue
+" Color: Identifier #00c0c0 37 darkcyan
+" Color: Search #c0c000 142 darkyellow
+" Color: Statement #c0c000 142 darkyellow
+" Color: Todo #c0c000 142 darkyellow
+" Color: PreProc #00ff00 46 green
+" Color: Type #00c000 34 darkgreen
+" Color: Directory #00c000 34 darkgreen
+" Color: Pmenu #303030 236 darkgrey
+" Color: Folded #303030 236 darkgrey
+" Color: Cursorline #3a3a3a 237 darkgrey
+" Color: bgDiffA #5F875F 65 darkgreen
+" Color: bgDiffC #5F87AF 67 blue
+" Color: bgDiffD #AF5FAF 133 magenta
+" Color: bgDiffT #C6C6C6 251 grey
+" Color: fgDiffW #FFFFFF 231 white
+" Color: fgDiffB #000000 16 black
+" Color: bgDiffC8 #5F87AF 67 darkblue
+" Color: bgDiffD8 #AF5FAF 133 darkmagenta
+" vim: et ts=2 sw=2
diff --git a/runtime/colors/peachpuff.vim b/runtime/colors/peachpuff.vim
index 1c0c2390f8..a540be2734 100644
--- a/runtime/colors/peachpuff.vim
+++ b/runtime/colors/peachpuff.vim
@@ -1,59 +1,404 @@
-" Vim color file
-" Maintainer: David Ne\v{c}as (Yeti) <yeti@physics.muni.cz>
-" Last Change: 2003-04-23
-" URL: http://trific.ath.cx/Ftp/vim/colors/peachpuff.vim
+" Name: peachpuff
+" Description: This color scheme uses a peachpuff background (what you've expected when it's " called peachpuff?).
+" Author: Original author David Ne\v{c}as (Yeti) <yeti@physics.muni.cz>
+" Maintainer: Original maintainer David Ne\v{c}as (Yeti) <yeti@physics.muni.cz>
+" Website: https://github.com/vim/colorschemes
+" License: Same as Vim
+" Last Updated: Sun Jun 12 10:58:17 2022
-" This color scheme uses a peachpuff background (what you've expected when it's
-" called peachpuff?).
-"
-" Note: Only GUI colors differ from default, on terminal it's just `light'.
+" Generated by Colortemplate v2.2.0
-" First remove all existing highlighting.
set background=light
+
hi clear
-if exists("syntax_on")
- syntax reset
+let g:colors_name = 'peachpuff'
+
+let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co > 1 ? &t_Co : 1
+
+if (has('termguicolors') && &termguicolors) || has('gui_running')
+ let g:terminal_ansi_colors = ['#ffdab9', '#a52a2a', '#c00058', '#cd00cd', '#008b8b', '#2e8b57', '#6a5acd', '#737373', '#406090', '#a52a2a', '#c00058', '#cd00cd', '#008b8b', '#2e8b57', '#6a5acd', '#000000']
+endif
+hi! link Terminal Normal
+hi! link LineNrAbove LineNr
+hi! link LineNrBelow LineNr
+hi! link CurSearch Search
+hi! link CursorLineFold CursorLine
+hi! link CursorLineSign CursorLine
+hi Normal guifg=#000000 guibg=#ffdab9 gui=NONE cterm=NONE
+hi Folded guifg=#000000 guibg=#e3c1a5 gui=NONE cterm=NONE
+hi CursorLine guifg=NONE guibg=#f5c195 gui=NONE cterm=NONE
+hi CursorColumn guifg=NONE guibg=#f5c195 gui=NONE cterm=NONE
+hi CursorLineNr guifg=#cd0000 guibg=NONE gui=bold cterm=bold
+hi QuickFixLine guifg=#000000 guibg=#ffaf87 gui=NONE cterm=NONE
+hi EndOfBuffer guifg=#737373 guibg=NONE gui=NONE cterm=NONE
+hi StatusLine guifg=#ffffff guibg=#000000 gui=bold cterm=bold
+hi StatusLineNC guifg=#ffdab9 guibg=#737373 gui=bold cterm=bold
+hi StatusLineTerm guifg=#ffffff guibg=#2e8b57 gui=bold cterm=bold
+hi StatusLineTermNC guifg=#ffdab9 guibg=#008b8b gui=bold cterm=bold
+hi VertSplit guifg=#ffdab9 guibg=#737373 gui=NONE cterm=NONE
+hi Pmenu guifg=#000000 guibg=#ffaf87 gui=NONE cterm=NONE
+hi PmenuSel guifg=#000000 guibg=#f5c195 gui=bold cterm=bold
+hi PmenuSbar guifg=NONE guibg=#ffdab9 gui=NONE cterm=NONE
+hi PmenuThumb guifg=NONE guibg=#737373 gui=NONE cterm=NONE
+hi TabLine guifg=#ffdab9 guibg=#737373 gui=underline cterm=underline
+hi TabLineFill guifg=NONE guibg=NONE gui=reverse ctermfg=NONE ctermbg=NONE cterm=reverse
+hi TabLineSel guifg=#000000 guibg=#ffdab9 gui=bold cterm=bold
+hi ToolbarLine guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
+hi ToolbarButton guifg=#ffdab9 guibg=#737373 gui=bold cterm=bold
+hi NonText guifg=#737373 guibg=NONE gui=NONE cterm=NONE
+hi SpecialKey guifg=#737373 guibg=NONE gui=NONE cterm=NONE
+hi Visual guifg=#000000 guibg=#cccccc gui=NONE cterm=NONE
+hi VisualNOS guifg=NONE guibg=#406090 gui=NONE cterm=NONE
+hi LineNr guifg=#cd0000 guibg=NONE gui=NONE cterm=NONE
+hi FoldColumn guifg=#406090 guibg=NONE gui=NONE cterm=NONE
+hi SignColumn guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
+hi Underlined guifg=#6a5acd guibg=NONE gui=underline cterm=underline
+hi Error guifg=#ff0000 guibg=#ffffff gui=bold,reverse cterm=bold,reverse
+hi ErrorMsg guifg=#ff0000 guibg=#ffffff gui=bold,reverse cterm=bold,reverse
+hi WarningMsg guifg=#cd00cd guibg=#ffdab9 gui=bold cterm=bold
+hi MoreMsg guifg=#2e8b57 guibg=#ffdab9 gui=bold cterm=bold
+hi ModeMsg guifg=#000000 guibg=#ffdab9 gui=bold cterm=bold
+hi Question guifg=#c00058 guibg=NONE gui=bold cterm=bold
+hi Todo guifg=#6a5acd guibg=#ffff00 gui=NONE cterm=NONE
+hi MatchParen guifg=#ffdab9 guibg=#6a5acd gui=NONE cterm=NONE
+hi Search guifg=#ffdab9 guibg=#cd00cd gui=NONE cterm=NONE
+hi IncSearch guifg=#2e8b57 guibg=NONE gui=reverse cterm=reverse
+hi WildMenu guifg=#000000 guibg=#ffff00 gui=bold cterm=bold
+hi ColorColumn guifg=NONE guibg=#eeeeee gui=NONE cterm=NONE
+hi Cursor guifg=#ffdab9 guibg=#000000 gui=NONE cterm=NONE
+hi lCursor guifg=#000000 guibg=#c00058 gui=NONE cterm=NONE
+hi SpellBad guifg=#ff0000 guibg=NONE guisp=#ff0000 gui=undercurl cterm=underline
+hi SpellCap guifg=#008b8b guibg=NONE guisp=#008b8b gui=undercurl cterm=underline
+hi SpellLocal guifg=#cd00cd guibg=NONE guisp=#cd00cd gui=undercurl cterm=underline
+hi SpellRare guifg=#6a5acd guibg=NONE guisp=#6a5acd gui=undercurl cterm=underline
+hi Comment guifg=#406090 guibg=NONE gui=NONE cterm=NONE
+hi Constant guifg=#c00058 guibg=NONE gui=NONE cterm=NONE
+hi Identifier guifg=#008b8b guibg=NONE gui=NONE cterm=NONE
+hi Statement guifg=#a52a2a guibg=NONE gui=bold cterm=bold
+hi PreProc guifg=#cd00cd guibg=NONE gui=NONE cterm=NONE
+hi Type guifg=#2e8b57 guibg=NONE gui=bold cterm=bold
+hi Special guifg=#6a5acd guibg=NONE gui=NONE cterm=NONE
+hi Directory guifg=#008b8b guibg=NONE gui=bold cterm=bold
+hi Conceal guifg=#406090 guibg=NONE gui=NONE cterm=NONE
+hi Ignore guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
+hi Title guifg=#cd00cd guibg=NONE gui=bold cterm=bold
+hi DiffAdd guifg=#ffffff guibg=#5f875f gui=NONE cterm=NONE
+hi DiffChange guifg=#ffffff guibg=#5f87af gui=NONE cterm=NONE
+hi DiffText guifg=#000000 guibg=#c6c6c6 gui=NONE cterm=NONE
+hi DiffDelete guifg=#ffffff guibg=#af5faf gui=NONE cterm=NONE
+
+if s:t_Co >= 256
+ hi! link Terminal Normal
+ hi! link LineNrAbove LineNr
+ hi! link LineNrBelow LineNr
+ hi! link CurSearch Search
+ hi! link CursorLineFold CursorLine
+ hi! link CursorLineSign CursorLine
+ hi Normal ctermfg=16 ctermbg=223 cterm=NONE
+ hi Folded ctermfg=16 ctermbg=252 cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=180 cterm=NONE
+ hi CursorColumn ctermfg=NONE ctermbg=180 cterm=NONE
+ hi CursorLineNr ctermfg=160 ctermbg=NONE cterm=bold
+ hi QuickFixLine ctermfg=16 ctermbg=216 cterm=NONE
+ hi EndOfBuffer ctermfg=243 ctermbg=NONE cterm=NONE
+ hi StatusLine ctermfg=231 ctermbg=16 cterm=bold
+ hi StatusLineNC ctermfg=223 ctermbg=243 cterm=bold
+ hi StatusLineTerm ctermfg=231 ctermbg=29 cterm=bold
+ hi StatusLineTermNC ctermfg=223 ctermbg=30 cterm=bold
+ hi VertSplit ctermfg=223 ctermbg=243 cterm=NONE
+ hi Pmenu ctermfg=16 ctermbg=216 cterm=NONE
+ hi PmenuSel ctermfg=16 ctermbg=180 cterm=bold
+ hi PmenuSbar ctermfg=NONE ctermbg=223 cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=243 cterm=NONE
+ hi TabLine ctermfg=223 ctermbg=243 cterm=underline
+ hi TabLineFill ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi TabLineSel ctermfg=16 ctermbg=223 cterm=bold
+ hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi ToolbarButton ctermfg=223 ctermbg=243 cterm=bold
+ hi NonText ctermfg=243 ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=243 ctermbg=NONE cterm=NONE
+ hi Visual ctermfg=16 ctermbg=252 cterm=NONE
+ hi VisualNOS ctermfg=NONE ctermbg=25 cterm=NONE
+ hi LineNr ctermfg=160 ctermbg=NONE cterm=NONE
+ hi FoldColumn ctermfg=25 ctermbg=NONE cterm=NONE
+ hi SignColumn ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=62 ctermbg=NONE cterm=underline
+ hi Error ctermfg=196 ctermbg=231 cterm=bold,reverse
+ hi ErrorMsg ctermfg=196 ctermbg=231 cterm=bold,reverse
+ hi WarningMsg ctermfg=164 ctermbg=223 cterm=bold
+ hi MoreMsg ctermfg=29 ctermbg=223 cterm=bold
+ hi ModeMsg ctermfg=16 ctermbg=223 cterm=bold
+ hi Question ctermfg=161 ctermbg=NONE cterm=bold
+ hi Todo ctermfg=62 ctermbg=226 cterm=NONE
+ hi MatchParen ctermfg=223 ctermbg=62 cterm=NONE
+ hi Search ctermfg=223 ctermbg=164 cterm=NONE
+ hi IncSearch ctermfg=29 ctermbg=NONE cterm=reverse
+ hi WildMenu ctermfg=16 ctermbg=226 cterm=bold
+ hi ColorColumn ctermfg=NONE ctermbg=255 cterm=NONE
+ hi Cursor ctermfg=223 ctermbg=16 cterm=NONE
+ hi lCursor ctermfg=16 ctermbg=161 cterm=NONE
+ hi SpellBad ctermfg=196 ctermbg=NONE cterm=underline
+ hi SpellCap ctermfg=30 ctermbg=NONE cterm=underline
+ hi SpellLocal ctermfg=164 ctermbg=NONE cterm=underline
+ hi SpellRare ctermfg=62 ctermbg=NONE cterm=underline
+ hi Comment ctermfg=25 ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=161 ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=30 ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=124 ctermbg=NONE cterm=bold
+ hi PreProc ctermfg=164 ctermbg=NONE cterm=NONE
+ hi Type ctermfg=29 ctermbg=NONE cterm=bold
+ hi Special ctermfg=62 ctermbg=NONE cterm=NONE
+ hi Directory ctermfg=30 ctermbg=NONE cterm=bold
+ hi Conceal ctermfg=25 ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Title ctermfg=164 ctermbg=NONE cterm=bold
+ hi DiffAdd ctermfg=231 ctermbg=65 cterm=NONE
+ hi DiffChange ctermfg=231 ctermbg=67 cterm=NONE
+ hi DiffText ctermfg=16 ctermbg=251 cterm=NONE
+ hi DiffDelete ctermfg=231 ctermbg=133 cterm=NONE
+ unlet s:t_Co
+ finish
endif
-let colors_name = "peachpuff"
-
-hi Normal guibg=PeachPuff guifg=Black
-
-hi SpecialKey term=bold ctermfg=4 guifg=Blue
-hi NonText term=bold cterm=bold ctermfg=4 gui=bold guifg=Blue
-hi Directory term=bold ctermfg=4 guifg=Blue
-hi ErrorMsg term=standout cterm=bold ctermfg=7 ctermbg=1 gui=bold guifg=White guibg=Red
-hi IncSearch term=reverse cterm=reverse gui=reverse
-hi Search term=reverse ctermbg=3 guibg=Gold2
-hi MoreMsg term=bold ctermfg=2 gui=bold guifg=SeaGreen
-hi ModeMsg term=bold cterm=bold gui=bold
-hi LineNr term=underline ctermfg=3 guifg=Red3
-hi Question term=standout ctermfg=2 gui=bold guifg=SeaGreen
-hi StatusLine term=bold,reverse cterm=bold,reverse gui=bold guifg=White guibg=Black
-hi StatusLineNC term=reverse cterm=reverse gui=bold guifg=PeachPuff guibg=Gray45
-hi VertSplit term=reverse cterm=reverse gui=bold guifg=White guibg=Gray45
-hi Title term=bold ctermfg=5 gui=bold guifg=DeepPink3
-hi Visual term=reverse cterm=reverse gui=reverse guifg=Grey80 guibg=fg
-hi WarningMsg term=standout ctermfg=1 gui=bold guifg=Red
-hi WildMenu term=standout ctermfg=0 ctermbg=3 guifg=Black guibg=Yellow
-hi Folded term=standout ctermfg=4 ctermbg=7 guifg=Black guibg=#e3c1a5
-hi FoldColumn term=standout ctermfg=4 ctermbg=7 guifg=DarkBlue guibg=Gray80
-hi DiffAdd term=bold ctermbg=4 guibg=White
-hi DiffChange term=bold ctermbg=5 guibg=#edb5cd
-hi DiffDelete term=bold cterm=bold ctermfg=4 ctermbg=6 gui=bold guifg=LightBlue guibg=#f6e8d0
-hi DiffText term=reverse cterm=bold ctermbg=1 gui=bold guibg=#ff8060
-hi Cursor guifg=bg guibg=fg
-hi lCursor guifg=bg guibg=fg
-
-" Colors for syntax highlighting
-hi Comment term=bold ctermfg=4 guifg=#406090
-hi Constant term=underline ctermfg=1 guifg=#c00058
-hi Special term=bold ctermfg=5 guifg=SlateBlue
-hi Identifier term=underline ctermfg=6 guifg=DarkCyan
-hi Statement term=bold ctermfg=3 gui=bold guifg=Brown
-hi PreProc term=underline ctermfg=5 guifg=Magenta3
-hi Type term=underline ctermfg=2 gui=bold guifg=SeaGreen
-hi Ignore cterm=bold ctermfg=7 guifg=bg
-hi Error term=reverse cterm=bold ctermfg=7 ctermbg=1 gui=bold guifg=White guibg=Red
-hi Todo term=standout ctermfg=0 ctermbg=3 guifg=Blue guibg=Yellow
+if s:t_Co >= 16
+ hi Normal ctermfg=black ctermbg=white cterm=NONE
+ hi Folded ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorColumn ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorLineNr ctermfg=NONE ctermbg=NONE cterm=underline
+ hi QuickFixLine ctermfg=black ctermbg=grey cterm=NONE
+ hi EndOfBuffer ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi StatusLine ctermfg=white ctermbg=black cterm=bold
+ hi StatusLineNC ctermfg=white ctermbg=darkgrey cterm=bold
+ hi StatusLineTerm ctermfg=white ctermbg=darkgreen cterm=bold
+ hi StatusLineTermNC ctermfg=white ctermbg=darkcyan cterm=bold
+ hi VertSplit ctermfg=white ctermbg=darkgrey cterm=NONE
+ hi Pmenu ctermfg=black ctermbg=grey cterm=NONE
+ hi PmenuSel ctermfg=black ctermbg=yellow cterm=bold
+ hi PmenuSbar ctermfg=NONE ctermbg=white cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=darkgrey cterm=NONE
+ hi TabLine ctermfg=white ctermbg=darkgrey cterm=underline
+ hi TabLineFill ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi TabLineSel ctermfg=black ctermbg=white cterm=bold
+ hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi ToolbarButton ctermfg=white ctermbg=darkgrey cterm=bold
+ hi NonText ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi Visual ctermfg=black ctermbg=grey cterm=NONE
+ hi VisualNOS ctermfg=NONE ctermbg=darkgrey cterm=NONE
+ hi LineNr ctermfg=red ctermbg=NONE cterm=NONE
+ hi FoldColumn ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi SignColumn ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=darkblue ctermbg=NONE cterm=underline
+ hi Error ctermfg=darkred ctermbg=white cterm=bold,reverse
+ hi ErrorMsg ctermfg=darkred ctermbg=white cterm=bold,reverse
+ hi WarningMsg ctermfg=darkmagenta ctermbg=white cterm=bold
+ hi MoreMsg ctermfg=darkgreen ctermbg=white cterm=bold
+ hi ModeMsg ctermfg=black ctermbg=white cterm=bold
+ hi Question ctermfg=darkred ctermbg=NONE cterm=bold
+ hi Todo ctermfg=darkblue ctermbg=yellow cterm=NONE
+ hi MatchParen ctermfg=white ctermbg=darkblue cterm=NONE
+ hi Search ctermfg=white ctermbg=darkmagenta cterm=NONE
+ hi IncSearch ctermfg=darkgreen ctermbg=NONE cterm=reverse
+ hi WildMenu ctermfg=black ctermbg=yellow cterm=bold
+ hi ColorColumn ctermfg=NONE ctermbg=grey cterm=NONE
+ hi Cursor ctermfg=white ctermbg=black cterm=NONE
+ hi lCursor ctermfg=black ctermbg=darkred cterm=NONE
+ hi SpellBad ctermfg=darkred ctermbg=NONE cterm=underline
+ hi SpellCap ctermfg=darkcyan ctermbg=NONE cterm=underline
+ hi SpellLocal ctermfg=darkmagenta ctermbg=NONE cterm=underline
+ hi SpellRare ctermfg=darkblue ctermbg=NONE cterm=underline
+ hi Comment ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=darkred ctermbg=NONE cterm=bold
+ hi PreProc ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi Type ctermfg=darkgreen ctermbg=NONE cterm=bold
+ hi Special ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi Directory ctermfg=darkcyan ctermbg=NONE cterm=bold
+ hi Conceal ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Title ctermfg=darkmagenta ctermbg=NONE cterm=bold
+ hi DiffAdd ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi DiffChange ctermfg=white ctermbg=blue cterm=NONE
+ hi DiffText ctermfg=black ctermbg=grey cterm=NONE
+ hi DiffDelete ctermfg=white ctermbg=magenta cterm=NONE
+ unlet s:t_Co
+ finish
+endif
+
+if s:t_Co >= 8
+ hi Normal ctermfg=black ctermbg=gray cterm=NONE
+ hi Folded ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorColumn ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorLineNr ctermfg=NONE ctermbg=NONE cterm=underline
+ hi QuickFixLine ctermfg=gray ctermbg=darkcyan cterm=NONE
+ hi EndOfBuffer ctermfg=darkblue ctermbg=NONE cterm=bold
+ hi StatusLine ctermfg=black ctermbg=gray cterm=bold,reverse
+ hi StatusLineNC ctermfg=black ctermbg=gray cterm=reverse
+ hi StatusLineTerm ctermfg=black ctermbg=gray cterm=bold,reverse
+ hi StatusLineTermNC ctermfg=black ctermbg=gray cterm=reverse
+ hi VertSplit ctermfg=black ctermbg=gray cterm=reverse
+ hi Pmenu ctermfg=black ctermbg=darkcyan cterm=NONE
+ hi PmenuSel ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=darkgreen cterm=NONE
+ hi TabLine ctermfg=gray ctermbg=black cterm=NONE
+ hi TabLineFill ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi TabLineSel ctermfg=black ctermbg=white cterm=NONE
+ hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi ToolbarButton ctermfg=black ctermbg=gray cterm=bold,reverse
+ hi NonText ctermfg=darkblue ctermbg=NONE cterm=bold
+ hi SpecialKey ctermfg=darkblue ctermbg=NONE cterm=bold
+ hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi VisualNOS ctermfg=NONE ctermbg=NONE cterm=underline
+ hi LineNr ctermfg=black ctermbg=NONE cterm=bold
+ hi FoldColumn ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi SignColumn ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline
+ hi Error ctermfg=darkred ctermbg=gray cterm=bold,reverse
+ hi ErrorMsg ctermfg=darkred ctermbg=gray cterm=bold,reverse
+ hi WarningMsg ctermfg=darkmagenta ctermbg=NONE cterm=bold
+ hi MoreMsg ctermfg=darkgreen ctermbg=NONE cterm=bold
+ hi ModeMsg ctermfg=black ctermbg=NONE cterm=bold
+ hi Question ctermfg=darkred ctermbg=NONE cterm=bold
+ hi Todo ctermfg=darkblue ctermbg=darkyellow cterm=NONE
+ hi MatchParen ctermfg=gray ctermbg=darkblue cterm=NONE
+ hi Search ctermfg=gray ctermbg=darkmagenta cterm=NONE
+ hi IncSearch ctermfg=darkgreen ctermbg=NONE cterm=reverse
+ hi WildMenu ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi ColorColumn ctermfg=black ctermbg=darkred cterm=NONE
+ hi SpellBad ctermfg=darkred ctermbg=NONE cterm=reverse
+ hi SpellCap ctermfg=darkcyan ctermbg=NONE cterm=reverse
+ hi SpellLocal ctermfg=darkmagenta ctermbg=NONE cterm=reverse
+ hi SpellRare ctermfg=darkblue ctermbg=NONE cterm=reverse
+ hi Comment ctermfg=NONE ctermbg=NONE cterm=bold
+ hi Constant ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=darkred ctermbg=NONE cterm=bold
+ hi PreProc ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi Type ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi Special ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi Directory ctermfg=darkcyan ctermbg=NONE cterm=bold
+ hi Conceal ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Title ctermfg=darkmagenta ctermbg=NONE cterm=bold
+ hi DiffAdd ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi DiffChange ctermfg=white ctermbg=darkblue cterm=NONE
+ hi DiffText ctermfg=black ctermbg=grey cterm=NONE
+ hi DiffDelete ctermfg=white ctermbg=darkmagenta cterm=NONE
+ unlet s:t_Co
+ finish
+endif
+
+if s:t_Co >= 0
+ hi Normal term=NONE
+ hi ColorColumn term=reverse
+ hi Conceal term=NONE
+ hi Cursor term=reverse
+ hi CursorColumn term=NONE
+ hi CursorLine term=underline
+ hi CursorLineNr term=bold
+ hi DiffAdd term=reverse
+ hi DiffChange term=NONE
+ hi DiffDelete term=reverse
+ hi DiffText term=reverse
+ hi Directory term=NONE
+ hi EndOfBuffer term=NONE
+ hi ErrorMsg term=bold,reverse
+ hi FoldColumn term=NONE
+ hi Folded term=NONE
+ hi IncSearch term=bold,reverse,underline
+ hi LineNr term=NONE
+ hi MatchParen term=bold,underline
+ hi ModeMsg term=bold
+ hi MoreMsg term=NONE
+ hi NonText term=NONE
+ hi Pmenu term=reverse
+ hi PmenuSbar term=reverse
+ hi PmenuSel term=bold
+ hi PmenuThumb term=NONE
+ hi Question term=standout
+ hi Search term=reverse
+ hi SignColumn term=reverse
+ hi SpecialKey term=bold
+ hi SpellBad term=underline
+ hi SpellCap term=underline
+ hi SpellLocal term=underline
+ hi SpellRare term=underline
+ hi StatusLine term=bold,reverse
+ hi StatusLineNC term=bold,underline
+ hi TabLine term=bold,underline
+ hi TabLineFill term=NONE
+ hi Terminal term=NONE
+ hi TabLineSel term=bold,reverse
+ hi Title term=NONE
+ hi VertSplit term=NONE
+ hi Visual term=reverse
+ hi VisualNOS term=NONE
+ hi WarningMsg term=standout
+ hi WildMenu term=bold
+ hi CursorIM term=NONE
+ hi ToolbarLine term=reverse
+ hi ToolbarButton term=bold,reverse
+ hi CurSearch term=reverse
+ hi CursorLineFold term=underline
+ hi CursorLineSign term=underline
+ hi Comment term=bold
+ hi Constant term=NONE
+ hi Error term=bold,reverse
+ hi Identifier term=NONE
+ hi Ignore term=NONE
+ hi PreProc term=NONE
+ hi Special term=NONE
+ hi Statement term=NONE
+ hi Todo term=bold,reverse
+ hi Type term=NONE
+ hi Underlined term=underline
+ unlet s:t_Co
+ finish
+endif
+" Background: light
+" Color: comment #406090 25 darkgrey
+" Color: constant #c00058 161 darkred
+" Color: identifier #008b8b 30 darkcyan
+" Color: statement #a52a2a 124 darkred
+" Color: preproc #cd00cd 164 darkmagenta
+" Color: type #2e8b57 29 darkgreen
+" Color: special #6a5acd 62 darkblue
+" Color: fg0 #000000 16 black
+" Color: bg0 #ffdab9 223 white
+" Color: fg1 #ffffff 231 white
+" Color: bg1 #737373 243 darkgrey
+" Color: visual #cccccc 252 grey
+" Color: folded #e3c1a5 252 cyan
+" Color: folded_fg #000000 16 black
+" Color: cursorline #f5c195 180 yellow
+" Color: pmenu #ffaf87 216 grey
+" Color: wildmenu #ffff00 226 yellow
+" Color: error #ff0000 196 darkred
+" Color: linenr #cd0000 160 red
+" Color: blue #0000ff 21 darkblue
+" Color: red #ff0000 196 darkred
+" Color: colorcolumn #eeeeee 255 grey
+" Color: black #000000 16 black
+" Color: darkred #870000 88 darkred
+" Color: darkgreen #008700 28 darkgreen
+" Color: darkyellow #878700 100 darkyellow
+" Color: darkblue #000087 18 darkblue
+" Color: darkmagenta #870087 18 darkmagenta
+" Color: darkcyan #008787 30 darkcyan
+" Color: gray #878787 102 gray
+" Term colors: bg0 statement constant preproc identifier type special bg1
+" Term colors: comment statement constant preproc identifier type special fg0
+" Color: bgDiffA #5F875F 65 darkgreen
+" Color: bgDiffC #5F87AF 67 blue
+" Color: bgDiffD #AF5FAF 133 magenta
+" Color: bgDiffT #C6C6C6 251 grey
+" Color: fgDiffW #FFFFFF 231 white
+" Color: fgDiffB #000000 16 black
+" Color: bgDiffC8 #5F87AF 67 darkblue
+" Color: bgDiffD8 #AF5FAF 133 darkmagenta
+" vim: et ts=2 sw=2
diff --git a/runtime/colors/ron.vim b/runtime/colors/ron.vim
index 1e9caa3150..6aa810e54e 100644
--- a/runtime/colors/ron.vim
+++ b/runtime/colors/ron.vim
@@ -1,45 +1,442 @@
-" local syntax file - set colors on a per-machine basis:
-" vim: tw=0 ts=4 sw=4
-" Vim color file
-" Maintainer: Ron Aaron <ron@ronware.org>
-" Last Change: 2013 May 24
+" Name: ron
+" Author: original author Ron Aaron <ron@ronware.org>
+" Maintainer: original maintainer Ron Aaron <ron@ronware.org>
+" Website: https://www.github.com/vim/colorschemes
+" License: Same as Vim
+" Last Updated: Sat 11 Jun 2022 11:29:07 MSK
+
+" Generated by Colortemplate v2.2.0
set background=dark
+
hi clear
-if exists("syntax_on")
- syntax reset
+let g:colors_name = 'ron'
+
+let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co > 1 ? &t_Co : 1
+
+hi! link Terminal Normal
+hi! link Boolean Constant
+hi! link Character Constant
+hi! link Conditional Statement
+hi! link Debug Special
+hi! link Define PreProc
+hi! link Delimiter Special
+hi! link Exception Statement
+hi! link Float Constant
+hi! link Function Identifier
+hi! link Include PreProc
+hi! link Keyword Statement
+hi! link Macro PreProc
+hi! link Number Constant
+hi! link PopupSelected PmenuSel
+hi! link PreCondit PreProc
+hi! link Repeat Statement
+hi! link SpecialChar Special
+hi! link SpecialComment Special
+hi! link StatusLineTerm StatusLine
+hi! link StatusLineTermNC StatusLineNC
+hi! link StorageClass Type
+hi! link String Constant
+hi! link Structure Type
+hi! link Tag Special
+hi! link Typedef Type
+hi! link lCursor Cursor
+hi! link CurSearch Search
+hi! link CursorLineFold CursorLine
+hi! link CursorLineSign CursorLine
+hi! link LineNrAbove LineNr
+hi! link LineNrBelow LineNr
+
+if (has('termguicolors') && &termguicolors) || has('gui_running')
+ let g:terminal_ansi_colors = ['#000000', '#cd0000', '#00cd00', '#cdcd00', '#0000ee', '#cd00cd', '#00cdcd', '#e5e5e5', '#7f7f7f', '#ff0000', '#00ff00', '#ffff00', '#5c5cff', '#ff00ff', '#00ffff', '#ffffff']
+endif
+hi Normal guifg=#00ffff guibg=#000000 gui=NONE cterm=NONE
+hi ColorColumn guifg=NONE guibg=#cd0000 gui=NONE cterm=NONE
+hi CursorColumn guifg=NONE guibg=#666666 gui=NONE cterm=NONE
+hi CursorLine guifg=NONE guibg=#666666 gui=NONE cterm=NONE
+hi CursorLineNr guifg=#ffff00 guibg=NONE gui=bold cterm=NONE
+hi QuickFixLine guifg=#000000 guibg=#00cdcd gui=NONE cterm=NONE
+hi Conceal guifg=#e5e5e5 guibg=#a9a9a9 gui=NONE cterm=NONE
+hi Cursor guifg=#ffffff guibg=#60a060 gui=NONE cterm=NONE
+hi Directory guifg=#00ffff guibg=NONE gui=NONE cterm=NONE
+hi EndOfBuffer guifg=#ffff00 guibg=#303030 gui=NONE cterm=NONE
+hi ErrorMsg guifg=#ff0000 guibg=#ffffff gui=reverse cterm=reverse
+hi FoldColumn guifg=#ffffff guibg=NONE gui=NONE cterm=NONE
+hi Folded guifg=NONE guibg=#4d4d4d gui=NONE cterm=NONE
+hi IncSearch guifg=NONE guibg=#4682b4 gui=NONE cterm=NONE
+hi LineNr guifg=#a9a9a9 guibg=NONE gui=NONE cterm=NONE
+hi MatchParen guifg=NONE guibg=#008b8b gui=NONE cterm=NONE
+hi ModeMsg guifg=NONE guibg=NONE gui=bold ctermfg=NONE ctermbg=NONE cterm=NONE
+hi MoreMsg guifg=#2e8b57 guibg=NONE gui=bold cterm=NONE
+hi NonText guifg=#ffff00 guibg=#303030 gui=NONE cterm=NONE
+hi Pmenu guifg=#ffffff guibg=#444444 gui=NONE cterm=NONE
+hi PmenuSbar guifg=NONE guibg=#000000 gui=NONE cterm=NONE
+hi PmenuSel guifg=#000000 guibg=#00cdcd gui=NONE cterm=NONE
+hi PmenuThumb guifg=NONE guibg=#e5e5e5 gui=NONE cterm=NONE
+hi Question guifg=#00ff00 guibg=#000000 gui=bold cterm=NONE
+hi Search guifg=#000000 guibg=#a9a9a9 gui=bold cterm=NONE
+hi SignColumn guifg=#00ffff guibg=NONE gui=NONE cterm=NONE
+hi SpecialKey guifg=#00ffff guibg=NONE gui=NONE cterm=NONE
+hi StatusLine guifg=#00ffff guibg=#0000ff gui=bold cterm=NONE
+hi StatusLineNC guifg=#add8e6 guibg=#00008b gui=NONE cterm=NONE
+hi VertSplit guifg=#add8e6 guibg=#00008b gui=NONE cterm=NONE
+hi TabLine guifg=#000000 guibg=#008b8b gui=NONE cterm=NONE
+hi TabLineFill guifg=#a9a9a9 guibg=#7f7f7f gui=NONE cterm=NONE
+hi TabLineSel guifg=#00ffff guibg=#000000 gui=bold cterm=NONE
+hi Terminal guifg=#00ffff guibg=#000000 gui=NONE cterm=NONE
+hi Title guifg=#a9a9a9 guibg=NONE gui=NONE cterm=NONE
+hi Visual guifg=NONE guibg=NONE gui=reverse ctermfg=NONE ctermbg=NONE cterm=reverse
+hi VisualNOS guifg=NONE guibg=#000000 gui=bold,underline cterm=underline
+hi WarningMsg guifg=#ffff00 guibg=NONE gui=NONE cterm=NONE
+hi WildMenu guifg=#000000 guibg=#ffff00 gui=NONE cterm=NONE
+hi SpellBad guifg=#ff0000 guibg=NONE guisp=#ff0000 gui=undercurl cterm=underline
+hi SpellCap guifg=#ffffff guibg=NONE guisp=#ffffff gui=undercurl cterm=underline
+hi SpellLocal guifg=#ffff00 guibg=NONE guisp=#ffff00 gui=undercurl cterm=underline
+hi SpellRare guifg=#ff00ff guibg=NONE guisp=#ff00ff gui=undercurl cterm=underline
+hi Comment guifg=#00ff00 guibg=NONE gui=NONE cterm=NONE
+hi Constant guifg=#00ffff guibg=NONE gui=bold cterm=NONE
+hi Error guifg=#ff0000 guibg=#ffffff gui=reverse cterm=reverse
+hi Identifier guifg=#00ffff guibg=NONE gui=NONE cterm=NONE
+hi Ignore guifg=#000000 guibg=#000000 gui=NONE cterm=NONE
+hi Label guifg=#eec900 guibg=NONE gui=NONE cterm=NONE
+hi Operator guifg=#ffa500 guibg=NONE gui=NONE cterm=NONE
+hi PreProc guifg=#eea9b8 guibg=NONE gui=NONE cterm=NONE
+hi Special guifg=#ffff00 guibg=NONE gui=NONE cterm=NONE
+hi Statement guifg=#add8e6 guibg=NONE gui=NONE cterm=NONE
+hi Todo guifg=#000000 guibg=#ffa500 gui=NONE cterm=NONE
+hi Type guifg=#2e8b57 guibg=NONE gui=bold cterm=NONE
+hi Underlined guifg=#80a0ff guibg=NONE gui=underline cterm=underline
+hi CursorIM guifg=NONE guibg=fg gui=NONE cterm=NONE
+hi ToolbarLine guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
+hi ToolbarButton guifg=#000000 guibg=#e5e5e5 gui=bold cterm=NONE
+hi DiffAdd guifg=#ffffff guibg=#5f875f gui=NONE cterm=NONE
+hi DiffChange guifg=#ffffff guibg=#5f87af gui=NONE cterm=NONE
+hi DiffText guifg=#000000 guibg=#c6c6c6 gui=NONE cterm=NONE
+hi DiffDelete guifg=#ffffff guibg=#af5faf gui=NONE cterm=NONE
+
+if s:t_Co >= 256
+ hi Normal ctermfg=51 ctermbg=16 cterm=NONE
+ hi ColorColumn ctermfg=NONE ctermbg=160 cterm=NONE
+ hi CursorColumn ctermfg=NONE ctermbg=59 cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=59 cterm=NONE
+ hi CursorLineNr ctermfg=226 ctermbg=NONE cterm=NONE
+ hi QuickFixLine ctermfg=16 ctermbg=44 cterm=NONE
+ hi Conceal ctermfg=254 ctermbg=145 cterm=NONE
+ hi Cursor ctermfg=231 ctermbg=71 cterm=NONE
+ hi Directory ctermfg=51 ctermbg=NONE cterm=NONE
+ hi EndOfBuffer ctermfg=226 ctermbg=236 cterm=NONE
+ hi ErrorMsg ctermfg=196 ctermbg=231 cterm=reverse
+ hi FoldColumn ctermfg=231 ctermbg=NONE cterm=NONE
+ hi Folded ctermfg=NONE ctermbg=239 cterm=NONE
+ hi IncSearch ctermfg=NONE ctermbg=67 cterm=NONE
+ hi LineNr ctermfg=145 ctermbg=NONE cterm=NONE
+ hi MatchParen ctermfg=NONE ctermbg=44 cterm=NONE
+ hi ModeMsg ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi MoreMsg ctermfg=29 ctermbg=NONE cterm=NONE
+ hi NonText ctermfg=226 ctermbg=236 cterm=NONE
+ hi Pmenu ctermfg=231 ctermbg=238 cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=16 cterm=NONE
+ hi PmenuSel ctermfg=16 ctermbg=44 cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=254 cterm=NONE
+ hi Question ctermfg=46 ctermbg=16 cterm=NONE
+ hi Search ctermfg=16 ctermbg=145 cterm=NONE
+ hi SignColumn ctermfg=51 ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=51 ctermbg=NONE cterm=NONE
+ hi StatusLine ctermfg=51 ctermbg=21 cterm=NONE
+ hi StatusLineNC ctermfg=152 ctermbg=20 cterm=NONE
+ hi VertSplit ctermfg=152 ctermbg=20 cterm=NONE
+ hi TabLine ctermfg=16 ctermbg=44 cterm=NONE
+ hi TabLineFill ctermfg=145 ctermbg=102 cterm=NONE
+ hi TabLineSel ctermfg=51 ctermbg=16 cterm=NONE
+ hi Terminal ctermfg=51 ctermbg=16 cterm=NONE
+ hi Title ctermfg=145 ctermbg=NONE cterm=NONE
+ hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi VisualNOS ctermfg=NONE ctermbg=16 cterm=underline
+ hi WarningMsg ctermfg=226 ctermbg=NONE cterm=NONE
+ hi WildMenu ctermfg=16 ctermbg=226 cterm=NONE
+ hi SpellBad ctermfg=196 ctermbg=NONE cterm=underline
+ hi SpellCap ctermfg=231 ctermbg=NONE cterm=underline
+ hi SpellLocal ctermfg=226 ctermbg=NONE cterm=underline
+ hi SpellRare ctermfg=201 ctermbg=NONE cterm=underline
+ hi Comment ctermfg=46 ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=51 ctermbg=NONE cterm=NONE
+ hi Error ctermfg=196 ctermbg=231 cterm=reverse
+ hi Identifier ctermfg=51 ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=16 ctermbg=16 cterm=NONE
+ hi Label ctermfg=220 ctermbg=NONE cterm=NONE
+ hi Operator ctermfg=214 ctermbg=NONE cterm=NONE
+ hi PreProc ctermfg=217 ctermbg=NONE cterm=NONE
+ hi Special ctermfg=226 ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=152 ctermbg=NONE cterm=NONE
+ hi Todo ctermfg=16 ctermbg=214 cterm=NONE
+ hi Type ctermfg=29 ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=111 ctermbg=NONE cterm=underline
+ hi CursorIM ctermfg=NONE ctermbg=fg cterm=NONE
+ hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi ToolbarButton ctermfg=16 ctermbg=254 cterm=NONE
+ hi DiffAdd ctermfg=231 ctermbg=65 cterm=NONE
+ hi DiffChange ctermfg=231 ctermbg=67 cterm=NONE
+ hi DiffText ctermfg=16 ctermbg=251 cterm=NONE
+ hi DiffDelete ctermfg=231 ctermbg=133 cterm=NONE
+ unlet s:t_Co
+ finish
endif
-let g:colors_name = "ron"
-hi Normal guifg=cyan guibg=black
-hi NonText guifg=yellow guibg=#303030
-hi comment guifg=green
-hi constant guifg=cyan gui=bold
-hi identifier guifg=cyan gui=NONE
-hi statement guifg=lightblue gui=NONE
-hi preproc guifg=Pink2
-hi type guifg=seagreen gui=bold
-hi special guifg=yellow
-hi ErrorMsg guifg=Black guibg=Red
-hi WarningMsg guifg=Black guibg=Green
-hi Error guibg=Red
-hi Todo guifg=Black guibg=orange
-hi Cursor guibg=#60a060 guifg=#00ff00
-hi Search guibg=darkgray guifg=black gui=bold
-hi IncSearch gui=NONE guibg=steelblue
-hi LineNr guifg=darkgrey
-hi title guifg=darkgrey
-hi ShowMarksHL ctermfg=cyan ctermbg=lightblue cterm=bold guifg=yellow guibg=black gui=bold
-hi StatusLineNC gui=NONE guifg=lightblue guibg=darkblue
-hi StatusLine gui=bold guifg=cyan guibg=blue
-hi label guifg=gold2
-hi operator guifg=orange
-hi clear Visual
-hi Visual term=reverse cterm=reverse gui=reverse
-hi DiffChange guibg=darkgreen
-hi DiffText guibg=olivedrab
-hi DiffAdd guibg=slateblue
-hi DiffDelete guibg=coral
-hi Folded guibg=gray30
-hi FoldColumn guibg=gray30 guifg=white
-hi cIf0 guifg=gray
-hi diffOnly guifg=red gui=bold
+
+if s:t_Co >= 16
+ hi Normal ctermfg=cyan ctermbg=black cterm=NONE
+ hi ColorColumn ctermfg=cyan ctermbg=darkred cterm=NONE
+ hi CursorColumn ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorLineNr ctermfg=yellow ctermbg=NONE cterm=underline
+ hi QuickFixLine ctermfg=black ctermbg=darkcyan cterm=NONE
+ hi Conceal ctermfg=grey ctermbg=grey cterm=NONE
+ hi Cursor ctermfg=white ctermbg=green cterm=NONE
+ hi Directory ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi EndOfBuffer ctermfg=yellow ctermbg=darkgrey cterm=NONE
+ hi ErrorMsg ctermfg=red ctermbg=white cterm=reverse
+ hi FoldColumn ctermfg=white ctermbg=NONE cterm=NONE
+ hi Folded ctermfg=NONE ctermbg=darkgrey cterm=NONE
+ hi IncSearch ctermfg=NONE ctermbg=blue cterm=NONE
+ hi LineNr ctermfg=grey ctermbg=NONE cterm=NONE
+ hi MatchParen ctermfg=NONE ctermbg=darkcyan cterm=NONE
+ hi ModeMsg ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi MoreMsg ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi NonText ctermfg=yellow ctermbg=darkgrey cterm=NONE
+ hi Pmenu ctermfg=white ctermbg=darkgrey cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=black cterm=NONE
+ hi PmenuSel ctermfg=black ctermbg=darkcyan cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=grey cterm=NONE
+ hi Question ctermfg=green ctermbg=black cterm=NONE
+ hi Search ctermfg=black ctermbg=grey cterm=NONE
+ hi SignColumn ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi StatusLine ctermfg=cyan ctermbg=darkblue cterm=NONE
+ hi StatusLineNC ctermfg=darkcyan ctermbg=darkblue cterm=NONE
+ hi VertSplit ctermfg=darkcyan ctermbg=darkblue cterm=NONE
+ hi TabLine ctermfg=black ctermbg=darkcyan cterm=NONE
+ hi TabLineFill ctermfg=grey ctermbg=darkgrey cterm=NONE
+ hi TabLineSel ctermfg=cyan ctermbg=black cterm=NONE
+ hi Terminal ctermfg=cyan ctermbg=black cterm=NONE
+ hi Title ctermfg=grey ctermbg=NONE cterm=NONE
+ hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi VisualNOS ctermfg=NONE ctermbg=black cterm=underline
+ hi WarningMsg ctermfg=yellow ctermbg=NONE cterm=NONE
+ hi WildMenu ctermfg=black ctermbg=yellow cterm=NONE
+ hi SpellBad ctermfg=red ctermbg=NONE cterm=underline
+ hi SpellCap ctermfg=white ctermbg=NONE cterm=underline
+ hi SpellLocal ctermfg=yellow ctermbg=NONE cterm=underline
+ hi SpellRare ctermfg=magenta ctermbg=NONE cterm=underline
+ hi Comment ctermfg=green ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi Error ctermfg=red ctermbg=white cterm=reverse
+ hi Identifier ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=black ctermbg=black cterm=NONE
+ hi Label ctermfg=yellow ctermbg=NONE cterm=NONE
+ hi Operator ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi PreProc ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi Special ctermfg=yellow ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Todo ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi Type ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=blue ctermbg=NONE cterm=underline
+ hi CursorIM ctermfg=NONE ctermbg=fg cterm=NONE
+ hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi ToolbarButton ctermfg=black ctermbg=grey cterm=NONE
+ hi DiffAdd ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi DiffChange ctermfg=white ctermbg=blue cterm=NONE
+ hi DiffText ctermfg=black ctermbg=grey cterm=NONE
+ hi DiffDelete ctermfg=white ctermbg=magenta cterm=NONE
+ unlet s:t_Co
+ finish
+endif
+
+if s:t_Co >= 8
+ hi Normal ctermfg=darkcyan ctermbg=black cterm=NONE
+ hi ColorColumn ctermfg=darkcyan ctermbg=darkred cterm=NONE
+ hi CursorColumn ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorLineNr ctermfg=darkyellow ctermbg=NONE cterm=underline
+ hi QuickFixLine ctermfg=black ctermbg=darkcyan cterm=NONE
+ hi Conceal ctermfg=grey ctermbg=grey cterm=NONE
+ hi Directory ctermfg=darkcyan ctermbg=NONE cterm=bold
+ hi EndOfBuffer ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi ErrorMsg ctermfg=darkred ctermbg=grey cterm=reverse
+ hi FoldColumn ctermfg=grey ctermbg=NONE cterm=NONE
+ hi Folded ctermfg=darkcyan ctermbg=black cterm=reverse
+ hi IncSearch ctermfg=darkyellow ctermbg=darkblue cterm=NONE
+ hi LineNr ctermfg=grey ctermbg=NONE cterm=bold
+ hi MatchParen ctermfg=NONE ctermbg=darkcyan cterm=bold
+ hi ModeMsg ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi MoreMsg ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi NonText ctermfg=darkgreen ctermbg=NONE cterm=bold
+ hi Pmenu ctermfg=grey ctermbg=darkgrey cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=black cterm=NONE
+ hi PmenuSel ctermfg=black ctermbg=darkcyan cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=grey cterm=NONE
+ hi Question ctermfg=darkgreen ctermbg=black cterm=bold
+ hi Search ctermfg=black ctermbg=grey cterm=NONE
+ hi SignColumn ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi StatusLine ctermfg=darkblue ctermbg=grey cterm=bold,reverse
+ hi StatusLineNC ctermfg=darkblue ctermbg=grey cterm=reverse
+ hi VertSplit ctermfg=darkblue ctermbg=darkblue cterm=NONE
+ hi TabLine ctermfg=black ctermbg=darkcyan cterm=NONE
+ hi TabLineFill ctermfg=darkblue ctermbg=darkblue cterm=NONE
+ hi TabLineSel ctermfg=darkcyan ctermbg=black cterm=bold
+ hi Terminal ctermfg=darkcyan ctermbg=black cterm=NONE
+ hi Title ctermfg=grey ctermbg=NONE cterm=bold
+ hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi VisualNOS ctermfg=NONE ctermbg=black cterm=underline
+ hi WarningMsg ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi WildMenu ctermfg=black ctermbg=darkgreen cterm=NONE
+ hi SpellBad ctermfg=darkred ctermbg=darkyellow cterm=reverse
+ hi SpellCap ctermfg=darkyellow ctermbg=NONE cterm=reverse
+ hi SpellLocal ctermfg=darkgreen ctermbg=NONE cterm=reverse
+ hi SpellRare ctermfg=darkmagenta ctermbg=darkyellow cterm=reverse
+ hi Comment ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=darkcyan ctermbg=NONE cterm=bold
+ hi Error ctermfg=darkred ctermbg=grey cterm=bold,reverse
+ hi Identifier ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=black ctermbg=black cterm=NONE
+ hi Label ctermfg=darkyellow ctermbg=NONE cterm=bold
+ hi Operator ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi PreProc ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi Special ctermfg=darkyellow ctermbg=NONE cterm=bold
+ hi Statement ctermfg=darkblue ctermbg=NONE cterm=bold
+ hi Todo ctermfg=black ctermbg=darkyellow cterm=bold
+ hi Type ctermfg=darkgreen ctermbg=NONE cterm=bold
+ hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline
+ hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi ToolbarButton ctermfg=grey ctermbg=black cterm=bold,reverse
+ hi DiffAdd ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi DiffChange ctermfg=white ctermbg=darkblue cterm=NONE
+ hi DiffText ctermfg=black ctermbg=grey cterm=NONE
+ hi DiffDelete ctermfg=white ctermbg=darkmagenta cterm=NONE
+ unlet s:t_Co
+ finish
+endif
+
+if s:t_Co >= 0
+ hi Normal term=NONE
+ hi ColorColumn term=reverse
+ hi Conceal term=NONE
+ hi Cursor term=reverse
+ hi CursorColumn term=NONE
+ hi CursorLine term=underline
+ hi CursorLineNr term=bold
+ hi DiffAdd term=reverse
+ hi DiffChange term=NONE
+ hi DiffDelete term=reverse
+ hi DiffText term=reverse
+ hi Directory term=NONE
+ hi EndOfBuffer term=NONE
+ hi ErrorMsg term=bold,reverse
+ hi FoldColumn term=NONE
+ hi Folded term=NONE
+ hi IncSearch term=bold,reverse,underline
+ hi LineNr term=NONE
+ hi MatchParen term=bold,underline
+ hi ModeMsg term=bold
+ hi MoreMsg term=NONE
+ hi NonText term=NONE
+ hi Pmenu term=reverse
+ hi PmenuSbar term=reverse
+ hi PmenuSel term=bold
+ hi PmenuThumb term=NONE
+ hi Question term=standout
+ hi Search term=reverse
+ hi SignColumn term=reverse
+ hi SpecialKey term=bold
+ hi SpellBad term=underline
+ hi SpellCap term=underline
+ hi SpellLocal term=underline
+ hi SpellRare term=underline
+ hi StatusLine term=bold,reverse
+ hi StatusLineNC term=bold,underline
+ hi TabLine term=bold,underline
+ hi TabLineFill term=NONE
+ hi Terminal term=NONE
+ hi TabLineSel term=bold,reverse
+ hi Title term=NONE
+ hi VertSplit term=NONE
+ hi Visual term=reverse
+ hi VisualNOS term=NONE
+ hi WarningMsg term=standout
+ hi WildMenu term=bold
+ hi CursorIM term=NONE
+ hi ToolbarLine term=reverse
+ hi ToolbarButton term=bold,reverse
+ hi CurSearch term=reverse
+ hi CursorLineFold term=underline
+ hi CursorLineSign term=underline
+ hi Comment term=bold
+ hi Constant term=NONE
+ hi Error term=bold,reverse
+ hi Identifier term=NONE
+ hi Ignore term=NONE
+ hi PreProc term=NONE
+ hi Special term=NONE
+ hi Statement term=NONE
+ hi Todo term=bold,reverse
+ hi Type term=NONE
+ hi Underlined term=underline
+ unlet s:t_Co
+ finish
+endif
+
+" Background: dark
+" Color: xterm0 #000000 16 black
+" Color: xterm1 #cd0000 160 darkred
+" Color: xterm2 #00cd00 40 darkgreen
+" Color: xterm3 #cdcd00 184 darkyellow
+" Color: xterm4 #0000ee 20 darkblue
+" Color: xterm5 #cd00cd 164 darkmagenta
+" Color: xterm6 #00cdcd 44 darkcyan
+" Color: xterm7 #e5e5e5 254 grey
+" Color: xterm8 #7f7f7f 102 darkgrey
+" Color: xterm9 #ff0000 196 red
+" Color: xterm10 #00ff00 46 green
+" Color: xterm11 #ffff00 226 yellow
+" Color: xterm12 #5c5cff 63 blue
+" Color: xterm13 #ff00ff 201 magenta
+" Color: xterm14 #00ffff 51 cyan
+" Color: xterm15 #ffffff 231 white
+" Color: rgbGrey40 #666666 59 darkgrey
+" Color: rgbDarkGrey #a9a9a9 145 grey
+" Color: rgbDarkBlue #00008b 20 darkblue
+" Color: rgbBlue #0000ff 21 darkblue
+" Color: rgbDarkCyan #008b8b 44 darkcyan
+" Color: Directory #00ffff 51 cyan
+" Color: rgbSeaGreen #2e8b57 29 darkgreen
+" Color: rgbGrey #bebebe 250 grey
+" Color: Question #00ff00 46 green
+" Color: SignColumn #a9a9a9 248 grey
+" Color: SpecialKey #00ffff 51 cyan
+" Color: StatusLineTerm #90ee90 120 darkgreen
+" Color: ToolbarLine #7f7f7f 244 darkgrey
+" Color: Underlined #80a0ff 111 blue
+" Color: Pmenu #444444 238 darkgrey
+" Color: ron303030 #303030 236 darkgrey
+" Color: ronLightBlue #add8e6 152 darkcyan
+" Color: ronPink2 #eea9b8 217 darkmagenta
+" Color: ronOrange #ffa500 214 darkyellow
+" Color: ronCursor #60a060 71 green
+" Color: ronSteelBlue #4682b4 67 blue
+" Color: ronGold2 #eec900 220 yellow
+" Color: ronDarkGreen #006400 22 darkgreen
+" Color: ronSlateBlue #6a5acd 62 blue
+" Color: ronOliveDrab #6b8e23 64 green
+" Color: ronCoral #ff7f50 209 red
+" Color: ronGray30 #4d4d4d 239 darkgrey
+" Term colors: xterm0 xterm1 xterm2 xterm3 xterm4 xterm5 xterm6 xterm7
+" Term colors: xterm8 xterm9 xterm10 xterm11 xterm12 xterm13
+" Term colors: xterm14 xterm15
+" Color: bgDiffA #5F875F 65 darkgreen
+" Color: bgDiffC #5F87AF 67 blue
+" Color: bgDiffD #AF5FAF 133 magenta
+" Color: bgDiffT #C6C6C6 251 grey
+" Color: fgDiffW #FFFFFF 231 white
+" Color: fgDiffB #000000 16 black
+" Color: bgDiffC8 #5F87AF 67 darkblue
+" Color: bgDiffD8 #AF5FAF 133 darkmagenta
+" vim: et ts=2 sw=2
diff --git a/runtime/colors/shine.vim b/runtime/colors/shine.vim
index eedb9c9b25..5ae6d12111 100644
--- a/runtime/colors/shine.vim
+++ b/runtime/colors/shine.vim
@@ -1,59 +1,412 @@
-" Vim color file
-" Maintainer: Yasuhiro Matsumoto <mattn@mail.goo.ne.jp>
-" Last Change: 2001 May 25
+" Name: shine
+" Description: Light colorscheme inspired by normal text editors.
+" Author: Original author is Yasuhiro Matsumoto <mattn@mail.goo.ne.jp>
+" Maintainer: Original maintainer is Yasuhiro Matsumoto <mattn@mail.goo.ne.jp>
+" Website: https://github.com/vim/colorschemes
+" License: Same as Vim
+" Last Updated: Sun Jun 12 11:02:11 2022
-" This look like normal text editor.
-" This color scheme uses a light background.
+" Generated by Colortemplate v2.2.0
-" First remove all existing highlighting.
set background=light
+
hi clear
-if exists("syntax_on")
- syntax reset
+let g:colors_name = 'shine'
+
+let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co > 1 ? &t_Co : 1
+
+if (has('termguicolors') && &termguicolors) || has('gui_running')
+ let g:terminal_ansi_colors = ['#000000', '#8b0000', '#006400', '#ffff00', '#00008b', '#6a0dad', '#008b8b', '#dadada', '#767676', '#ffafaf', '#90ee90', '#ffff60', '#add8e6', '#ff00ff', '#00ffff', '#ffffff']
+endif
+hi! link Terminal Normal
+hi! link LineNrAbove LineNr
+hi! link LineNrBelow LineNr
+hi! link CurSearch Search
+hi! link CursorLineFold CursorLine
+hi! link CursorLineSign CursorLine
+hi! link EndOfBuffer NonText
+hi! link ErrorMsg Error
+hi! link Tag Special
+hi! link Operator Statement
+hi Normal guifg=#000000 guibg=#ffffff gui=NONE cterm=NONE
+hi Folded guifg=#00008b guibg=#dadada gui=NONE cterm=NONE
+hi CursorLine guifg=NONE guibg=#dadada gui=NONE cterm=NONE
+hi CursorColumn guifg=NONE guibg=#dadada gui=NONE cterm=NONE
+hi CursorLineNr guifg=NONE guibg=#dadada gui=NONE cterm=NONE
+hi QuickFixLine guifg=#000000 guibg=#ff8c00 gui=NONE cterm=NONE
+hi StatusLine guifg=#ffffff guibg=#000000 gui=bold cterm=bold
+hi StatusLineNC guifg=#ffffff guibg=#a8a8a8 gui=NONE cterm=NONE
+hi StatusLineTerm guifg=#000000 guibg=#90ee90 gui=bold cterm=bold
+hi StatusLineTermNC guifg=#dadada guibg=#006400 gui=NONE cterm=NONE
+hi VertSplit guifg=#a8a8a8 guibg=#a8a8a8 gui=NONE cterm=NONE
+hi Pmenu guifg=#000000 guibg=#a8a8a8 gui=NONE cterm=NONE
+hi PmenuSel guifg=#000000 guibg=#ffff60 gui=NONE cterm=NONE
+hi PmenuSbar guifg=#ffffff guibg=#ffffff gui=NONE cterm=NONE
+hi PmenuThumb guifg=#767676 guibg=#767676 gui=NONE cterm=NONE
+hi TabLine guifg=#000000 guibg=#dadada gui=underline cterm=underline
+hi TabLineFill guifg=NONE guibg=NONE gui=reverse ctermfg=NONE ctermbg=NONE cterm=reverse
+hi TabLineSel guifg=#000000 guibg=#ffffff gui=bold cterm=bold
+hi ToolbarLine guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
+hi ToolbarButton guifg=NONE guibg=#a8a8a8 gui=bold cterm=bold
+hi NonText guifg=#add8e6 guibg=#ffffff gui=NONE cterm=NONE
+hi SpecialKey guifg=#add8e6 guibg=NONE gui=NONE cterm=NONE
+hi Visual guifg=#000000 guibg=#a8a8a8 gui=NONE cterm=NONE
+hi VisualNOS guifg=NONE guibg=NONE gui=bold,underline ctermfg=NONE ctermbg=NONE cterm=underline
+hi LineNr guifg=#767676 guibg=NONE gui=NONE cterm=NONE
+hi FoldColumn guifg=#767676 guibg=NONE gui=NONE cterm=NONE
+hi SignColumn guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
+hi Underlined guifg=#6a0dad guibg=NONE gui=underline cterm=underline
+hi Error guifg=#ff0000 guibg=#ffffff gui=reverse cterm=reverse
+hi WarningMsg guifg=#6a0dad guibg=#ffffff gui=NONE cterm=NONE
+hi MoreMsg guifg=#006400 guibg=#ffffff gui=NONE cterm=NONE
+hi ModeMsg guifg=#000000 guibg=#ffffff gui=NONE cterm=NONE
+hi Question guifg=#a07070 guibg=NONE gui=NONE cterm=NONE
+hi Todo guifg=#000000 guibg=#ffff00 gui=NONE cterm=NONE
+hi MatchParen guifg=#ffffff guibg=#ffafaf gui=NONE cterm=NONE
+hi Search guifg=#ffffff guibg=#6a0dad gui=NONE cterm=NONE
+hi IncSearch guifg=#008b8b guibg=#ffff00 gui=reverse cterm=reverse
+hi WildMenu guifg=#000000 guibg=#ffff00 gui=bold cterm=bold
+hi ColorColumn guifg=#000000 guibg=#ffafaf gui=NONE cterm=NONE
+hi Cursor guifg=#ffffff guibg=#000000 gui=NONE cterm=NONE
+hi lCursor guifg=#000000 guibg=#a07070 gui=NONE cterm=NONE
+hi SpellBad guifg=#ff0000 guibg=NONE guisp=#ff0000 gui=undercurl cterm=underline
+hi SpellCap guifg=#2e8b57 guibg=NONE guisp=#2e8b57 gui=undercurl cterm=underline
+hi SpellLocal guifg=#ff00ff guibg=NONE guisp=#ff00ff gui=undercurl cterm=underline
+hi SpellRare guifg=#00008b guibg=NONE guisp=#00008b gui=undercurl cterm=underline
+hi Statement guifg=#2e8b57 guibg=NONE gui=bold cterm=bold
+hi Type guifg=#2e8b57 guibg=NONE gui=bold cterm=bold
+hi Comment guifg=#a8a8a8 guibg=NONE gui=bold cterm=NONE
+hi StorageClass guifg=#ff0000 guibg=NONE gui=bold cterm=bold
+hi Conceal guifg=#dadada guibg=#767676 gui=NONE cterm=NONE
+hi Identifier guifg=#008b8b guibg=NONE gui=NONE cterm=NONE
+hi Constant guifg=#a07070 guibg=NONE gui=NONE cterm=NONE
+hi Number guifg=#a07070 guibg=NONE gui=bold cterm=bold
+hi Float guifg=#a07070 guibg=NONE gui=bold cterm=bold
+hi PreProc guifg=#6a0dad guibg=NONE gui=NONE cterm=NONE
+hi Special guifg=#ff8c00 guibg=NONE gui=NONE cterm=NONE
+hi SpecialChar guifg=#00008b guibg=NONE gui=NONE cterm=NONE
+hi Directory guifg=#008b8b guibg=NONE gui=bold cterm=bold
+hi Ignore guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
+hi Title guifg=#006400 guibg=NONE gui=bold cterm=bold
+hi DiffAdd guifg=#ffffff guibg=#5f875f gui=NONE cterm=NONE
+hi DiffChange guifg=#ffffff guibg=#5f87af gui=NONE cterm=NONE
+hi DiffText guifg=#000000 guibg=#c6c6c6 gui=NONE cterm=NONE
+hi DiffDelete guifg=#ffffff guibg=#af5faf gui=NONE cterm=NONE
+
+if s:t_Co >= 256
+ hi! link Terminal Normal
+ hi! link LineNrAbove LineNr
+ hi! link LineNrBelow LineNr
+ hi! link CurSearch Search
+ hi! link CursorLineFold CursorLine
+ hi! link CursorLineSign CursorLine
+ hi! link EndOfBuffer NonText
+ hi! link ErrorMsg Error
+ hi! link Tag Special
+ hi! link Operator Statement
+ hi Normal ctermfg=16 ctermbg=231 cterm=NONE
+ hi Folded ctermfg=18 ctermbg=253 cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=253 cterm=NONE
+ hi CursorColumn ctermfg=NONE ctermbg=253 cterm=NONE
+ hi CursorLineNr ctermfg=NONE ctermbg=253 cterm=NONE
+ hi QuickFixLine ctermfg=16 ctermbg=208 cterm=NONE
+ hi StatusLine ctermfg=231 ctermbg=16 cterm=bold
+ hi StatusLineNC ctermfg=231 ctermbg=248 cterm=NONE
+ hi StatusLineTerm ctermfg=16 ctermbg=120 cterm=bold
+ hi StatusLineTermNC ctermfg=253 ctermbg=22 cterm=NONE
+ hi VertSplit ctermfg=248 ctermbg=248 cterm=NONE
+ hi Pmenu ctermfg=16 ctermbg=248 cterm=NONE
+ hi PmenuSel ctermfg=16 ctermbg=228 cterm=NONE
+ hi PmenuSbar ctermfg=231 ctermbg=231 cterm=NONE
+ hi PmenuThumb ctermfg=243 ctermbg=243 cterm=NONE
+ hi TabLine ctermfg=16 ctermbg=253 cterm=underline
+ hi TabLineFill ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi TabLineSel ctermfg=16 ctermbg=231 cterm=bold
+ hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi ToolbarButton ctermfg=NONE ctermbg=248 cterm=bold
+ hi NonText ctermfg=153 ctermbg=231 cterm=NONE
+ hi SpecialKey ctermfg=153 ctermbg=NONE cterm=NONE
+ hi Visual ctermfg=16 ctermbg=248 cterm=NONE
+ hi VisualNOS ctermfg=NONE ctermbg=NONE cterm=underline
+ hi LineNr ctermfg=243 ctermbg=NONE cterm=NONE
+ hi FoldColumn ctermfg=243 ctermbg=NONE cterm=NONE
+ hi SignColumn ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=55 ctermbg=NONE cterm=underline
+ hi Error ctermfg=196 ctermbg=231 cterm=reverse
+ hi WarningMsg ctermfg=55 ctermbg=231 cterm=NONE
+ hi MoreMsg ctermfg=22 ctermbg=231 cterm=NONE
+ hi ModeMsg ctermfg=16 ctermbg=231 cterm=NONE
+ hi Question ctermfg=95 ctermbg=NONE cterm=NONE
+ hi Todo ctermfg=16 ctermbg=226 cterm=NONE
+ hi MatchParen ctermfg=231 ctermbg=217 cterm=NONE
+ hi Search ctermfg=231 ctermbg=55 cterm=NONE
+ hi IncSearch ctermfg=30 ctermbg=226 cterm=reverse
+ hi WildMenu ctermfg=16 ctermbg=226 cterm=bold
+ hi ColorColumn ctermfg=16 ctermbg=217 cterm=NONE
+ hi Cursor ctermfg=231 ctermbg=16 cterm=NONE
+ hi lCursor ctermfg=16 ctermbg=95 cterm=NONE
+ hi SpellBad ctermfg=196 ctermbg=NONE cterm=underline
+ hi SpellCap ctermfg=29 ctermbg=NONE cterm=underline
+ hi SpellLocal ctermfg=201 ctermbg=NONE cterm=underline
+ hi SpellRare ctermfg=18 ctermbg=NONE cterm=underline
+ hi Statement ctermfg=29 ctermbg=NONE cterm=bold
+ hi Type ctermfg=29 ctermbg=NONE cterm=bold
+ hi Comment ctermfg=248 ctermbg=NONE cterm=NONE
+ hi StorageClass ctermfg=196 ctermbg=NONE cterm=bold
+ hi Conceal ctermfg=253 ctermbg=243 cterm=NONE
+ hi Identifier ctermfg=30 ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=95 ctermbg=NONE cterm=NONE
+ hi Number ctermfg=95 ctermbg=NONE cterm=bold
+ hi Float ctermfg=95 ctermbg=NONE cterm=bold
+ hi PreProc ctermfg=55 ctermbg=NONE cterm=NONE
+ hi Special ctermfg=208 ctermbg=NONE cterm=NONE
+ hi SpecialChar ctermfg=18 ctermbg=NONE cterm=NONE
+ hi Directory ctermfg=30 ctermbg=NONE cterm=bold
+ hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Title ctermfg=22 ctermbg=NONE cterm=bold
+ hi DiffAdd ctermfg=231 ctermbg=65 cterm=NONE
+ hi DiffChange ctermfg=231 ctermbg=67 cterm=NONE
+ hi DiffText ctermfg=16 ctermbg=251 cterm=NONE
+ hi DiffDelete ctermfg=231 ctermbg=133 cterm=NONE
+ unlet s:t_Co
+ finish
+endif
+
+if s:t_Co >= 16
+ hi Normal ctermfg=black ctermbg=white cterm=NONE
+ hi Folded ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorColumn ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi CursorLineNr ctermfg=NONE ctermbg=NONE cterm=bold
+ hi QuickFixLine ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi StatusLine ctermfg=white ctermbg=black cterm=bold
+ hi StatusLineNC ctermfg=white ctermbg=darkgrey cterm=NONE
+ hi StatusLineTerm ctermfg=black ctermbg=green cterm=bold
+ hi StatusLineTermNC ctermfg=grey ctermbg=darkgreen cterm=NONE
+ hi VertSplit ctermfg=darkgrey ctermbg=darkgrey cterm=NONE
+ hi Pmenu ctermfg=black ctermbg=darkgrey cterm=NONE
+ hi PmenuSel ctermfg=black ctermbg=yellow cterm=NONE
+ hi PmenuSbar ctermfg=white ctermbg=white cterm=NONE
+ hi PmenuThumb ctermfg=darkgrey ctermbg=darkgrey cterm=NONE
+ hi TabLine ctermfg=black ctermbg=grey cterm=underline
+ hi TabLineFill ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi TabLineSel ctermfg=black ctermbg=white cterm=bold
+ hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi ToolbarButton ctermfg=NONE ctermbg=darkgrey cterm=bold
+ hi NonText ctermfg=blue ctermbg=white cterm=NONE
+ hi SpecialKey ctermfg=blue ctermbg=NONE cterm=NONE
+ hi Visual ctermfg=black ctermbg=darkgrey cterm=NONE
+ hi VisualNOS ctermfg=NONE ctermbg=NONE cterm=underline
+ hi LineNr ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi FoldColumn ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi SignColumn ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=darkmagenta ctermbg=NONE cterm=underline
+ hi Error ctermfg=red ctermbg=white cterm=reverse
+ hi WarningMsg ctermfg=darkmagenta ctermbg=white cterm=NONE
+ hi MoreMsg ctermfg=darkgreen ctermbg=white cterm=NONE
+ hi ModeMsg ctermfg=black ctermbg=white cterm=NONE
+ hi Question ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi Todo ctermfg=black ctermbg=yellow cterm=NONE
+ hi MatchParen ctermfg=white ctermbg=red cterm=NONE
+ hi Search ctermfg=white ctermbg=darkmagenta cterm=NONE
+ hi IncSearch ctermfg=darkcyan ctermbg=yellow cterm=reverse
+ hi WildMenu ctermfg=black ctermbg=yellow cterm=bold
+ hi ColorColumn ctermfg=black ctermbg=red cterm=NONE
+ hi Cursor ctermfg=white ctermbg=black cterm=NONE
+ hi lCursor ctermfg=black ctermbg=darkred cterm=NONE
+ hi SpellBad ctermfg=red ctermbg=NONE cterm=underline
+ hi SpellCap ctermfg=darkgreen ctermbg=NONE cterm=underline
+ hi SpellLocal ctermfg=magenta ctermbg=NONE cterm=underline
+ hi SpellRare ctermfg=darkblue ctermbg=NONE cterm=underline
+ hi Statement ctermfg=darkgreen ctermbg=NONE cterm=bold
+ hi Type ctermfg=darkgreen ctermbg=NONE cterm=bold
+ hi Comment ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi StorageClass ctermfg=red ctermbg=NONE cterm=bold
+ hi Conceal ctermfg=grey ctermbg=darkgrey cterm=NONE
+ hi Identifier ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi Number ctermfg=darkred ctermbg=NONE cterm=bold
+ hi Float ctermfg=darkred ctermbg=NONE cterm=bold
+ hi PreProc ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi Special ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi SpecialChar ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi Directory ctermfg=darkcyan ctermbg=NONE cterm=bold
+ hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Title ctermfg=darkgreen ctermbg=NONE cterm=bold
+ hi DiffAdd ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi DiffChange ctermfg=white ctermbg=blue cterm=NONE
+ hi DiffText ctermfg=black ctermbg=grey cterm=NONE
+ hi DiffDelete ctermfg=white ctermbg=magenta cterm=NONE
+ unlet s:t_Co
+ finish
+endif
+
+if s:t_Co >= 8
+ hi Normal ctermfg=black ctermbg=grey cterm=NONE
+ hi Folded ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorColumn ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi CursorLineNr ctermfg=NONE ctermbg=NONE cterm=bold
+ hi QuickFixLine ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi StatusLine ctermfg=black ctermbg=grey cterm=bold,reverse
+ hi StatusLineNC ctermfg=black ctermbg=grey cterm=reverse
+ hi StatusLineTerm ctermfg=black ctermbg=darkgreen cterm=bold,reverse
+ hi StatusLineTermNC ctermfg=grey ctermbg=darkgreen cterm=reverse
+ hi VertSplit ctermfg=black ctermbg=grey cterm=reverse
+ hi Pmenu ctermfg=black ctermbg=darkcyan cterm=NONE
+ hi PmenuSel ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi PmenuSbar ctermfg=grey ctermbg=grey cterm=NONE
+ hi PmenuThumb ctermfg=black ctermbg=black cterm=NONE
+ hi TabLine ctermfg=black ctermbg=grey cterm=reverse
+ hi TabLineFill ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi TabLineSel ctermfg=grey ctermbg=black cterm=reverse
+ hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi ToolbarButton ctermfg=black ctermbg=grey cterm=bold,reverse
+ hi NonText ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi VisualNOS ctermfg=NONE ctermbg=NONE cterm=underline
+ hi LineNr ctermfg=NONE ctermbg=NONE cterm=bold
+ hi FoldColumn ctermfg=grey ctermbg=NONE cterm=bold
+ hi SignColumn ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline
+ hi Error ctermfg=darkred ctermbg=NONE cterm=bold,reverse
+ hi WarningMsg ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi MoreMsg ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi ModeMsg ctermfg=black ctermbg=NONE cterm=NONE
+ hi Question ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi Todo ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi MatchParen ctermfg=grey ctermbg=darkred cterm=NONE
+ hi Search ctermfg=grey ctermbg=darkmagenta cterm=NONE
+ hi IncSearch ctermfg=darkcyan ctermbg=darkyellow cterm=reverse
+ hi WildMenu ctermfg=black ctermbg=darkyellow cterm=bold
+ hi ColorColumn ctermfg=black ctermbg=darkred cterm=NONE
+ hi SpellBad ctermfg=darkred ctermbg=NONE cterm=reverse
+ hi SpellCap ctermfg=darkgreen ctermbg=NONE cterm=reverse
+ hi SpellLocal ctermfg=darkmagenta ctermbg=NONE cterm=reverse
+ hi SpellRare ctermfg=darkblue ctermbg=NONE cterm=reverse
+ hi Statement ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi Type ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi Comment ctermfg=black ctermbg=NONE cterm=bold
+ hi StorageClass ctermfg=darkred ctermbg=NONE cterm=bold
+ hi Conceal ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi Number ctermfg=darkred ctermbg=NONE cterm=bold
+ hi Float ctermfg=darkred ctermbg=NONE cterm=bold
+ hi PreProc ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi Special ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi SpecialChar ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi Directory ctermfg=darkcyan ctermbg=NONE cterm=bold
+ hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Title ctermfg=darkgreen ctermbg=NONE cterm=bold
+ hi DiffAdd ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi DiffChange ctermfg=white ctermbg=darkblue cterm=NONE
+ hi DiffText ctermfg=black ctermbg=grey cterm=NONE
+ hi DiffDelete ctermfg=white ctermbg=darkmagenta cterm=NONE
+ unlet s:t_Co
+ finish
endif
-let colors_name = "shine"
-
-hi Normal ctermbg=White ctermfg=Black guifg=Black guibg=White
-
-" Groups used in the 'highlight' and 'guicursor' options default value.
-hi ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White
-hi IncSearch term=reverse cterm=reverse gui=reverse
-hi ModeMsg term=bold cterm=bold gui=bold
-hi StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold
-hi StatusLineNC term=reverse cterm=reverse gui=reverse
-hi VertSplit term=reverse cterm=reverse gui=reverse
-hi Visual term=reverse cterm=reverse gui=reverse guifg=Grey guibg=fg
-hi DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red
-hi Cursor ctermbg=Green guibg=Green guifg=Black
-hi lCursor guibg=Cyan guifg=Black
-hi Directory term=bold ctermfg=LightRed guifg=Red
-hi LineNr term=underline ctermfg=Yellow guifg=Yellow
-hi MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen
-hi NonText term=bold ctermfg=LightBlue gui=bold guifg=LightBlue guibg=grey90
-hi Question term=standout ctermfg=LightGreen gui=bold guifg=Green
-hi Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black
-hi SpecialKey term=bold ctermfg=LightBlue guifg=Blue
-hi Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta
-hi WarningMsg term=standout ctermfg=LightRed guifg=Red
-hi WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black
-hi Folded term=standout ctermbg=LightGrey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue
-hi FoldColumn term=standout ctermbg=LightGrey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue
-hi DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue
-hi DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta
-hi DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan
-
-hi Comment ctermfg=DarkGrey ctermbg=White guifg=DarkGrey gui=bold
-hi SpecialChar ctermfg=DarkGrey ctermbg=White guifg=DarkGrey gui=bold
-hi StorageClass ctermfg=Red ctermbg=White guifg=Red gui=bold
-hi Number ctermfg=LightRed ctermbg=White guifg=LightRed gui=bold
-
-" Groups for syntax highlighting
-hi Constant term=underline ctermfg=Magenta guifg=#a07070 guibg=grey80
-hi Special term=bold ctermfg=LightRed guifg=DarkOrange guibg=grey80
-if &t_Co > 8
- hi Statement term=bold cterm=bold ctermfg=DarkGreen ctermbg=White guifg=#ffff60 gui=bold
+if s:t_Co >= 0
+ hi Normal term=NONE
+ hi ColorColumn term=reverse
+ hi Conceal term=NONE
+ hi Cursor term=reverse
+ hi CursorColumn term=NONE
+ hi CursorLine term=underline
+ hi CursorLineNr term=bold
+ hi DiffAdd term=reverse
+ hi DiffChange term=NONE
+ hi DiffDelete term=reverse
+ hi DiffText term=reverse
+ hi Directory term=NONE
+ hi EndOfBuffer term=NONE
+ hi ErrorMsg term=bold,reverse
+ hi FoldColumn term=NONE
+ hi Folded term=NONE
+ hi IncSearch term=bold,reverse,underline
+ hi LineNr term=NONE
+ hi MatchParen term=bold,underline
+ hi ModeMsg term=bold
+ hi MoreMsg term=NONE
+ hi NonText term=NONE
+ hi Pmenu term=reverse
+ hi PmenuSbar term=reverse
+ hi PmenuSel term=bold
+ hi PmenuThumb term=NONE
+ hi Question term=standout
+ hi Search term=reverse
+ hi SignColumn term=reverse
+ hi SpecialKey term=bold
+ hi SpellBad term=underline
+ hi SpellCap term=underline
+ hi SpellLocal term=underline
+ hi SpellRare term=underline
+ hi StatusLine term=bold,reverse
+ hi StatusLineNC term=bold,underline
+ hi TabLine term=bold,underline
+ hi TabLineFill term=NONE
+ hi Terminal term=NONE
+ hi TabLineSel term=bold,reverse
+ hi Title term=NONE
+ hi VertSplit term=NONE
+ hi Visual term=reverse
+ hi VisualNOS term=NONE
+ hi WarningMsg term=standout
+ hi WildMenu term=bold
+ hi CursorIM term=NONE
+ hi ToolbarLine term=reverse
+ hi ToolbarButton term=bold,reverse
+ hi CurSearch term=reverse
+ hi CursorLineFold term=underline
+ hi CursorLineSign term=underline
+ hi Comment term=bold
+ hi Constant term=NONE
+ hi Error term=bold,reverse
+ hi Identifier term=NONE
+ hi Ignore term=NONE
+ hi PreProc term=NONE
+ hi Special term=NONE
+ hi Statement term=NONE
+ hi Todo term=bold,reverse
+ hi Type term=NONE
+ hi Underlined term=underline
+ unlet s:t_Co
+ finish
endif
-hi Ignore ctermfg=LightGrey guifg=grey90
-" vim: sw=2
+" Background: light
+" Color: Black #000000 16 black
+" Color: DarkGrey #767676 243 darkgrey
+" Color: MediumGrey #A8A8A8 248 darkgrey
+" Color: LightGrey #DADADA 253 grey
+" Color: White #ffffff 231 white
+" Color: Cyan #00ffff 51 cyan
+" Color: Magenta #ff00ff 201 magenta
+" Color: Red #ff0000 196 red
+" Color: Yellow #ffff00 226 yellow
+" Color: SeaGreen #2e8b57 29 darkgreen
+" Color: DarkBlue #00008b 18 darkblue
+" Color: DarkCyan #008b8b 30 darkcyan
+" Color: DarkGreen #006400 22 darkgreen
+" Color: DarkOrange #ff8c00 208 darkyellow
+" Color: DarkRed #8b0000 88 darkred
+" Color: LightBlue #add8e6 153 blue
+" Color: LightGreen #90ee90 120 green
+" Color: LightMagenta#ff83fa 213 magenta
+" Color: LightRed #ffafaf 217 red
+" Color: MutedRed #a07070 95 darkred
+" Color: MutedYellow #ffff60 228 yellow
+" Color: Purple #6a0dad 55 darkmagenta
+" Term colors: Black DarkRed DarkGreen Yellow DarkBlue Purple DarkCyan LightGrey
+" Term colors: DarkGrey LightRed LightGreen MutedYellow LightBlue Magenta Cyan White
+" Color: bgDiffA #5F875F 65 darkgreen
+" Color: bgDiffC #5F87AF 67 blue
+" Color: bgDiffD #AF5FAF 133 magenta
+" Color: bgDiffT #C6C6C6 251 grey
+" Color: fgDiffW #FFFFFF 231 white
+" Color: fgDiffB #000000 16 black
+" Color: bgDiffC8 #5F87AF 67 darkblue
+" Color: bgDiffD8 #AF5FAF 133 darkmagenta
+" vim: et ts=2 sw=2
diff --git a/runtime/colors/slate.vim b/runtime/colors/slate.vim
index ffc13b822a..192d4162ee 100644
--- a/runtime/colors/slate.vim
+++ b/runtime/colors/slate.vim
@@ -1,55 +1,410 @@
-"%% SiSU Vim color file
-" Slate Maintainer: Ralph Amissah <ralph@amissah.com>
-" (originally looked at desert Hans Fugal <hans@fugal.net> http://hans.fugal.net/vim/colors/desert.vim (2003/05/06)
-:set background=dark
-:highlight clear
-if version > 580
- hi clear
- if exists("syntax_on")
- syntax reset
- endif
+" Name: slate
+" Description: Remake of slate
+" Author: Original maintainer Ralph Amissah <ralph@amissah.com>
+" Maintainer: Original maintainer Ralph Amissah <ralph@amissah.com>
+" Website: https://github.com/vim/colorschemes
+" License: Same as Vim
+" Last Updated: Sun Jun 12 11:03:10 2022
+
+" Generated by Colortemplate v2.2.0
+
+set background=dark
+
+hi clear
+let g:colors_name = 'slate'
+
+let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co > 1 ? &t_Co : 1
+
+if (has('termguicolors') && &termguicolors) || has('gui_running')
+ let g:terminal_ansi_colors = ['#000000', '#ff0000', '#5f8700', '#ffff00', '#87d7ff', '#d7d787', '#ffd7af', '#666666', '#333333', '#ffafaf', '#00875f', '#ffd700', '#5f87d7', '#afaf87', '#ff8787', '#ffffff']
endif
-let colors_name = "slate"
-:hi Normal guifg=White guibg=grey15
-:hi Cursor guibg=khaki guifg=slategrey
-:hi VertSplit guibg=#c2bfa5 guifg=grey40 gui=none cterm=reverse
-:hi Folded guibg=black guifg=grey40 ctermfg=grey ctermbg=darkgrey
-:hi FoldColumn guibg=black guifg=grey20 ctermfg=4 ctermbg=7
-:hi IncSearch guifg=green guibg=black cterm=none ctermfg=yellow ctermbg=green
-:hi ModeMsg guifg=goldenrod cterm=none ctermfg=brown
-:hi MoreMsg guifg=SeaGreen ctermfg=darkgreen
-:hi NonText guifg=RoyalBlue guibg=grey15 cterm=bold ctermfg=blue
-:hi Question guifg=springgreen ctermfg=green
-:hi Search guibg=peru guifg=wheat cterm=none ctermfg=grey ctermbg=blue
-:hi SpecialKey guifg=yellowgreen ctermfg=darkgreen
-:hi StatusLine guibg=#c2bfa5 guifg=black gui=none cterm=bold,reverse
-:hi StatusLineNC guibg=#c2bfa5 guifg=grey40 gui=none cterm=reverse
-:hi Title guifg=gold gui=bold cterm=bold ctermfg=yellow
-:hi Statement guifg=CornflowerBlue ctermfg=lightblue
-:hi Visual gui=none guifg=khaki guibg=olivedrab cterm=reverse
-:hi WarningMsg guifg=salmon ctermfg=1
-:hi String guifg=SkyBlue ctermfg=darkcyan
-:hi Comment term=bold ctermfg=11 guifg=grey40
-:hi Constant guifg=#ffa0a0 ctermfg=brown
-:hi Special guifg=darkkhaki ctermfg=brown
-:hi Identifier guifg=salmon ctermfg=red
-:hi Include guifg=red ctermfg=red
-:hi PreProc guifg=red guibg=white ctermfg=red
-:hi Operator guifg=Red ctermfg=Red
-:hi Define guifg=gold gui=bold ctermfg=yellow
-:hi Type guifg=CornflowerBlue ctermfg=2
-:hi Function guifg=navajowhite ctermfg=brown
-:hi Structure guifg=green ctermfg=green
-:hi LineNr guifg=grey50 ctermfg=3
-:hi Ignore guifg=grey40 cterm=bold ctermfg=7
-:hi Todo guifg=orangered guibg=yellow2
-:hi Directory ctermfg=darkcyan
-:hi ErrorMsg cterm=bold guifg=White guibg=Red cterm=bold ctermfg=7 ctermbg=1
-:hi WildMenu ctermfg=0 ctermbg=3
-:hi DiffAdd ctermbg=4
-:hi DiffChange ctermbg=5
-:hi DiffDelete cterm=bold ctermfg=4 ctermbg=6
-:hi DiffText cterm=bold ctermbg=1
-:hi Underlined cterm=underline ctermfg=5
-:hi Error guifg=White guibg=Red cterm=bold ctermfg=7 ctermbg=1
-:hi SpellErrors guifg=White guibg=Red cterm=bold ctermfg=7 ctermbg=1
+hi! link Terminal Normal
+hi! link LineNrAbove LineNr
+hi! link LineNrBelow LineNr
+hi! link CurSearch Search
+hi! link CursorLineFold CursorLine
+hi! link CursorLineSign CursorLine
+hi Normal guifg=#ffffff guibg=#262626 gui=NONE cterm=NONE
+hi EndOfBuffer guifg=#5f87d7 guibg=NONE gui=NONE cterm=NONE
+hi StatusLine guifg=#000000 guibg=#afaf87 gui=NONE cterm=NONE
+hi StatusLineNC guifg=#666666 guibg=#afaf87 gui=NONE cterm=NONE
+hi StatusLineTerm guifg=#000000 guibg=#afaf87 gui=NONE cterm=NONE
+hi StatusLineTermNC guifg=#666666 guibg=#afaf87 gui=NONE cterm=NONE
+hi VertSplit guifg=#666666 guibg=#afaf87 gui=NONE cterm=NONE
+hi PmenuSel guifg=#262626 guibg=#d7d787 gui=NONE cterm=NONE
+hi Pmenu guifg=NONE guibg=#4a4a4a gui=NONE cterm=NONE
+hi PmenuSbar guifg=NONE guibg=#262626 gui=NONE cterm=NONE
+hi PmenuThumb guifg=NONE guibg=#ffd700 gui=NONE cterm=NONE
+hi TabLineSel guifg=#000000 guibg=#afaf87 gui=NONE cterm=NONE
+hi TabLine guifg=#666666 guibg=#333333 gui=NONE cterm=NONE
+hi TabLineFill guifg=#ff8787 guibg=#333333 gui=NONE cterm=NONE
+hi ToolbarLine guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
+hi ToolbarButton guifg=#262626 guibg=#d7d787 gui=NONE cterm=NONE
+hi NonText guifg=#5f87d7 guibg=NONE gui=NONE cterm=NONE
+hi SpecialKey guifg=#00875f guibg=NONE gui=NONE cterm=NONE
+hi QuickFixLine guifg=#000000 guibg=#5f87d7 gui=NONE cterm=NONE
+hi Folded guifg=#666666 guibg=#000000 gui=NONE cterm=NONE
+hi FoldColumn guifg=#5f87d7 guibg=#000000 gui=NONE cterm=NONE
+hi CursorLine guifg=NONE guibg=#333333 gui=NONE cterm=NONE
+hi CursorColumn guifg=NONE guibg=#333333 gui=NONE cterm=NONE
+hi ColorColumn guifg=NONE guibg=#000000 gui=NONE cterm=NONE
+hi CursorLineNr guifg=NONE guibg=#333333 gui=NONE cterm=NONE
+hi Visual guifg=#d7d787 guibg=#5f8700 gui=NONE cterm=NONE
+hi SignColumn guifg=NONE guibg=#262626 gui=NONE cterm=NONE
+hi VisualNOS guifg=#d7d787 guibg=#5f8700 gui=NONE cterm=NONE
+hi LineNr guifg=#666666 guibg=NONE gui=NONE cterm=NONE
+hi Underlined guifg=#5f87d7 guibg=NONE gui=underline cterm=underline
+hi Error guifg=#ff0000 guibg=#ffffff gui=reverse cterm=reverse
+hi ErrorMsg guifg=#ff0000 guibg=#000000 gui=reverse cterm=reverse
+hi ModeMsg guifg=#262626 guibg=#ffd700 gui=NONE cterm=NONE
+hi WarningMsg guifg=#ff8787 guibg=NONE gui=NONE cterm=NONE
+hi MoreMsg guifg=#00875f guibg=NONE gui=NONE cterm=NONE
+hi Question guifg=#ffd700 guibg=NONE gui=NONE cterm=NONE
+hi Todo guifg=#ff0000 guibg=#ffff00 gui=NONE cterm=NONE
+hi MatchParen guifg=#000000 guibg=#ffd700 gui=NONE cterm=NONE
+hi Search guifg=#000000 guibg=#d7875f gui=NONE cterm=NONE
+hi IncSearch guifg=#000000 guibg=#00ff00 gui=NONE cterm=NONE
+hi WildMenu guifg=#262626 guibg=#d7d787 gui=NONE cterm=NONE
+hi Cursor guifg=#333333 guibg=#d7d787 gui=NONE cterm=NONE
+hi lCursor guifg=#262626 guibg=#ffafaf gui=NONE cterm=NONE
+hi SpellBad guifg=#ff0000 guibg=NONE guisp=#ff0000 gui=undercurl cterm=underline
+hi SpellCap guifg=#ffff00 guibg=NONE guisp=#ffff00 gui=undercurl cterm=underline
+hi SpellLocal guifg=#ffafaf guibg=NONE guisp=#ffafaf gui=undercurl cterm=underline
+hi SpellRare guifg=#ffd7af guibg=NONE guisp=#ffd7af gui=undercurl cterm=underline
+hi Comment guifg=#666666 guibg=NONE gui=NONE cterm=NONE
+hi String guifg=#87d7ff guibg=NONE gui=NONE cterm=NONE
+hi Identifier guifg=#ff8787 guibg=NONE gui=NONE cterm=NONE
+hi Function guifg=#ffd7af guibg=NONE gui=NONE cterm=NONE
+hi Special guifg=#d7d787 guibg=NONE gui=NONE cterm=NONE
+hi Statement guifg=#5f87d7 guibg=NONE gui=bold cterm=bold
+hi Constant guifg=#ffafaf guibg=NONE gui=NONE cterm=NONE
+hi PreProc guifg=#d7875f guibg=NONE gui=NONE cterm=NONE
+hi Type guifg=#5f87d7 guibg=NONE gui=bold cterm=bold
+hi Operator guifg=#d7875f guibg=NONE gui=NONE cterm=NONE
+hi Define guifg=#ffd700 guibg=NONE gui=bold cterm=bold
+hi Structure guifg=#00ff00 guibg=NONE gui=NONE cterm=NONE
+hi Directory guifg=#00875f guibg=NONE gui=bold cterm=bold
+hi Conceal guifg=#666666 guibg=NONE gui=NONE cterm=NONE
+hi Ignore guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
+hi Title guifg=#ffd700 guibg=NONE gui=bold cterm=bold
+hi DiffAdd guifg=#ffffff guibg=#5f875f gui=NONE cterm=NONE
+hi DiffChange guifg=#ffffff guibg=#5f87af gui=NONE cterm=NONE
+hi DiffText guifg=#000000 guibg=#c6c6c6 gui=NONE cterm=NONE
+hi DiffDelete guifg=#ffffff guibg=#af5faf gui=NONE cterm=NONE
+
+if s:t_Co >= 256
+ hi! link Terminal Normal
+ hi! link LineNrAbove LineNr
+ hi! link LineNrBelow LineNr
+ hi! link CurSearch Search
+ hi! link CursorLineFold CursorLine
+ hi! link CursorLineSign CursorLine
+ hi Normal ctermfg=231 ctermbg=235 cterm=NONE
+ hi EndOfBuffer ctermfg=68 ctermbg=NONE cterm=NONE
+ hi StatusLine ctermfg=16 ctermbg=144 cterm=NONE
+ hi StatusLineNC ctermfg=241 ctermbg=144 cterm=NONE
+ hi StatusLineTerm ctermfg=16 ctermbg=144 cterm=NONE
+ hi StatusLineTermNC ctermfg=241 ctermbg=144 cterm=NONE
+ hi VertSplit ctermfg=241 ctermbg=144 cterm=NONE
+ hi PmenuSel ctermfg=235 ctermbg=186 cterm=NONE
+ hi Pmenu ctermfg=NONE ctermbg=239 cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=235 cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=220 cterm=NONE
+ hi TabLineSel ctermfg=16 ctermbg=144 cterm=NONE
+ hi TabLine ctermfg=241 ctermbg=236 cterm=NONE
+ hi TabLineFill ctermfg=210 ctermbg=236 cterm=NONE
+ hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi ToolbarButton ctermfg=235 ctermbg=186 cterm=NONE
+ hi NonText ctermfg=68 ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=29 ctermbg=NONE cterm=NONE
+ hi QuickFixLine ctermfg=16 ctermbg=68 cterm=NONE
+ hi Folded ctermfg=241 ctermbg=16 cterm=NONE
+ hi FoldColumn ctermfg=68 ctermbg=16 cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=236 cterm=NONE
+ hi CursorColumn ctermfg=NONE ctermbg=236 cterm=NONE
+ hi ColorColumn ctermfg=NONE ctermbg=16 cterm=NONE
+ hi CursorLineNr ctermfg=NONE ctermbg=236 cterm=NONE
+ hi Visual ctermfg=186 ctermbg=64 cterm=NONE
+ hi SignColumn ctermfg=NONE ctermbg=235 cterm=NONE
+ hi VisualNOS ctermfg=186 ctermbg=64 cterm=NONE
+ hi LineNr ctermfg=241 ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=68 ctermbg=NONE cterm=underline
+ hi Error ctermfg=196 ctermbg=231 cterm=reverse
+ hi ErrorMsg ctermfg=196 ctermbg=16 cterm=reverse
+ hi ModeMsg ctermfg=235 ctermbg=220 cterm=NONE
+ hi WarningMsg ctermfg=210 ctermbg=NONE cterm=NONE
+ hi MoreMsg ctermfg=29 ctermbg=NONE cterm=NONE
+ hi Question ctermfg=220 ctermbg=NONE cterm=NONE
+ hi Todo ctermfg=196 ctermbg=226 cterm=NONE
+ hi MatchParen ctermfg=16 ctermbg=220 cterm=NONE
+ hi Search ctermfg=16 ctermbg=173 cterm=NONE
+ hi IncSearch ctermfg=16 ctermbg=46 cterm=NONE
+ hi WildMenu ctermfg=235 ctermbg=186 cterm=NONE
+ hi SpellBad ctermfg=196 ctermbg=NONE cterm=underline
+ hi SpellCap ctermfg=226 ctermbg=NONE cterm=underline
+ hi SpellLocal ctermfg=217 ctermbg=NONE cterm=underline
+ hi SpellRare ctermfg=223 ctermbg=NONE cterm=underline
+ hi Comment ctermfg=241 ctermbg=NONE cterm=NONE
+ hi String ctermfg=117 ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=210 ctermbg=NONE cterm=NONE
+ hi Function ctermfg=223 ctermbg=NONE cterm=NONE
+ hi Special ctermfg=186 ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=68 ctermbg=NONE cterm=bold
+ hi Constant ctermfg=217 ctermbg=NONE cterm=NONE
+ hi PreProc ctermfg=173 ctermbg=NONE cterm=NONE
+ hi Type ctermfg=68 ctermbg=NONE cterm=bold
+ hi Operator ctermfg=173 ctermbg=NONE cterm=NONE
+ hi Define ctermfg=220 ctermbg=NONE cterm=bold
+ hi Structure ctermfg=46 ctermbg=NONE cterm=NONE
+ hi Directory ctermfg=29 ctermbg=NONE cterm=bold
+ hi Conceal ctermfg=241 ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Title ctermfg=220 ctermbg=NONE cterm=bold
+ hi DiffAdd ctermfg=231 ctermbg=65 cterm=NONE
+ hi DiffChange ctermfg=231 ctermbg=67 cterm=NONE
+ hi DiffText ctermfg=16 ctermbg=251 cterm=NONE
+ hi DiffDelete ctermfg=231 ctermbg=133 cterm=NONE
+ unlet s:t_Co
+ finish
+endif
+
+if s:t_Co >= 16
+ hi Normal ctermfg=white ctermbg=black cterm=NONE
+ hi EndOfBuffer ctermfg=blue ctermbg=NONE cterm=NONE
+ hi StatusLine ctermfg=black ctermbg=grey cterm=NONE
+ hi StatusLineNC ctermfg=darkgrey ctermbg=grey cterm=NONE
+ hi StatusLineTerm ctermfg=black ctermbg=grey cterm=NONE
+ hi StatusLineTermNC ctermfg=darkgrey ctermbg=grey cterm=NONE
+ hi VertSplit ctermfg=darkgrey ctermbg=grey cterm=NONE
+ hi PmenuSel ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi Pmenu ctermfg=NONE ctermbg=darkgrey cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=black cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=yellow cterm=NONE
+ hi TabLineSel ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi TabLine ctermfg=grey ctermbg=darkgrey cterm=NONE
+ hi TabLineFill ctermfg=cyan ctermbg=darkgrey cterm=NONE
+ hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi ToolbarButton ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi NonText ctermfg=blue ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=green ctermbg=NONE cterm=NONE
+ hi QuickFixLine ctermfg=black ctermbg=blue cterm=NONE
+ hi Folded ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi FoldColumn ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorColumn ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorLineNr ctermfg=yellow ctermbg=NONE cterm=bold
+ hi ColorColumn ctermfg=white ctermbg=darkgrey cterm=NONE
+ hi Visual ctermfg=yellow ctermbg=darkgreen cterm=NONE
+ hi SignColumn ctermfg=NONE ctermbg=black cterm=NONE
+ hi VisualNOS ctermfg=darkmagenta ctermbg=darkgreen cterm=NONE
+ hi LineNr ctermfg=grey ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=blue ctermbg=NONE cterm=underline
+ hi Error ctermfg=darkred ctermbg=white cterm=reverse
+ hi ErrorMsg ctermfg=darkred ctermbg=black cterm=reverse
+ hi ModeMsg ctermfg=black ctermbg=yellow cterm=NONE
+ hi WarningMsg ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi MoreMsg ctermfg=green ctermbg=NONE cterm=NONE
+ hi Question ctermfg=yellow ctermbg=NONE cterm=NONE
+ hi Todo ctermfg=darkred ctermbg=darkyellow cterm=NONE
+ hi MatchParen ctermfg=black ctermbg=yellow cterm=NONE
+ hi Search ctermfg=black ctermbg=darkmagenta cterm=NONE
+ hi IncSearch ctermfg=black ctermbg=darkgreen cterm=NONE
+ hi WildMenu ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi SpellBad ctermfg=darkred ctermbg=NONE cterm=underline
+ hi SpellCap ctermfg=darkyellow ctermbg=NONE cterm=underline
+ hi SpellLocal ctermfg=red ctermbg=NONE cterm=underline
+ hi SpellRare ctermfg=darkcyan ctermbg=NONE cterm=underline
+ hi Comment ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi String ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi Function ctermfg=yellow ctermbg=NONE cterm=NONE
+ hi Special ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=blue ctermbg=NONE cterm=bold
+ hi Constant ctermfg=red ctermbg=NONE cterm=NONE
+ hi PreProc ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi Type ctermfg=blue ctermbg=NONE cterm=bold
+ hi Operator ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi Define ctermfg=yellow ctermbg=NONE cterm=bold
+ hi Structure ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi Directory ctermfg=green ctermbg=NONE cterm=bold
+ hi Conceal ctermfg=grey ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Title ctermfg=yellow ctermbg=NONE cterm=bold
+ hi DiffAdd ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi DiffChange ctermfg=white ctermbg=blue cterm=NONE
+ hi DiffText ctermfg=black ctermbg=grey cterm=NONE
+ hi DiffDelete ctermfg=white ctermbg=magenta cterm=NONE
+ unlet s:t_Co
+ finish
+endif
+
+if s:t_Co >= 8
+ hi Normal ctermfg=grey ctermbg=black cterm=NONE
+ hi EndOfBuffer ctermfg=darkblue ctermbg=NONE cterm=bold
+ hi StatusLine ctermfg=grey ctermbg=black cterm=bold,reverse
+ hi StatusLineNC ctermfg=grey ctermbg=black cterm=reverse
+ hi StatusLineTerm ctermfg=grey ctermbg=black cterm=bold,reverse
+ hi StatusLineTermNC ctermfg=grey ctermbg=black cterm=reverse
+ hi VertSplit ctermfg=grey ctermbg=black cterm=reverse
+ hi PmenuSel ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi Pmenu ctermfg=black ctermbg=darkcyan cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=black cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=darkyellow cterm=NONE
+ hi TabLineSel ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi TabLine ctermfg=grey ctermbg=black cterm=reverse
+ hi TabLineFill ctermfg=grey ctermbg=black cterm=reverse
+ hi ToolbarButton ctermfg=grey ctermbg=black cterm=bold,reverse
+ hi NonText ctermfg=darkblue ctermbg=NONE cterm=bold
+ hi SpecialKey ctermfg=darkgreen ctermbg=NONE cterm=bold
+ hi QuickFixLine ctermfg=black ctermbg=darkblue cterm=NONE
+ hi Folded ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi FoldColumn ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorColumn ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorLineNr ctermfg=darkyellow ctermbg=NONE cterm=bold
+ hi ColorColumn ctermfg=grey ctermbg=darkred cterm=NONE
+ hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi SignColumn ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi VisualNOS ctermfg=darkmagenta ctermbg=darkgreen cterm=underline
+ hi LineNr ctermfg=grey ctermbg=NONE cterm=bold
+ hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline
+ hi Error ctermfg=darkred ctermbg=grey cterm=reverse
+ hi ErrorMsg ctermfg=darkred ctermbg=black cterm=reverse
+ hi ModeMsg ctermfg=NONE ctermbg=darkyellow cterm=NONE
+ hi WarningMsg ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi MoreMsg ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi Question ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi Todo ctermfg=darkred ctermbg=darkyellow cterm=NONE
+ hi MatchParen ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi Search ctermfg=black ctermbg=darkmagenta cterm=NONE
+ hi IncSearch ctermfg=black ctermbg=darkgreen cterm=NONE
+ hi WildMenu ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi SpellBad ctermfg=darkred ctermbg=darkyellow cterm=reverse
+ hi SpellCap ctermfg=darkyellow ctermbg=NONE cterm=reverse
+ hi SpellLocal ctermfg=darkmagenta ctermbg=darkyellow cterm=reverse
+ hi SpellRare ctermfg=darkcyan ctermbg=NONE cterm=reverse
+ hi Comment ctermfg=grey ctermbg=NONE cterm=bold
+ hi String ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi Function ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi Special ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=darkblue ctermbg=NONE cterm=bold
+ hi Constant ctermfg=darkred ctermbg=NONE cterm=bold
+ hi PreProc ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi Type ctermfg=darkblue ctermbg=NONE cterm=bold
+ hi Operator ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi Define ctermfg=darkyellow ctermbg=NONE cterm=bold
+ hi Structure ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi Directory ctermfg=darkgreen ctermbg=NONE cterm=bold
+ hi Conceal ctermfg=grey ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Title ctermfg=darkyellow ctermbg=NONE cterm=bold
+ hi DiffAdd ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi DiffChange ctermfg=white ctermbg=darkblue cterm=NONE
+ hi DiffText ctermfg=black ctermbg=grey cterm=NONE
+ hi DiffDelete ctermfg=white ctermbg=darkmagenta cterm=NONE
+ unlet s:t_Co
+ finish
+endif
+
+if s:t_Co >= 0
+ hi Normal term=NONE
+ hi ColorColumn term=reverse
+ hi Conceal term=NONE
+ hi Cursor term=reverse
+ hi CursorColumn term=NONE
+ hi CursorLine term=underline
+ hi CursorLineNr term=bold
+ hi DiffAdd term=reverse
+ hi DiffChange term=NONE
+ hi DiffDelete term=reverse
+ hi DiffText term=reverse
+ hi Directory term=NONE
+ hi EndOfBuffer term=NONE
+ hi ErrorMsg term=bold,reverse
+ hi FoldColumn term=NONE
+ hi Folded term=NONE
+ hi IncSearch term=bold,reverse,underline
+ hi LineNr term=NONE
+ hi MatchParen term=bold,underline
+ hi ModeMsg term=bold
+ hi MoreMsg term=NONE
+ hi NonText term=NONE
+ hi Pmenu term=reverse
+ hi PmenuSbar term=reverse
+ hi PmenuSel term=bold
+ hi PmenuThumb term=NONE
+ hi Question term=standout
+ hi Search term=reverse
+ hi SignColumn term=reverse
+ hi SpecialKey term=bold
+ hi SpellBad term=underline
+ hi SpellCap term=underline
+ hi SpellLocal term=underline
+ hi SpellRare term=underline
+ hi StatusLine term=bold,reverse
+ hi StatusLineNC term=bold,underline
+ hi TabLine term=bold,underline
+ hi TabLineFill term=NONE
+ hi Terminal term=NONE
+ hi TabLineSel term=bold,reverse
+ hi Title term=NONE
+ hi VertSplit term=NONE
+ hi Visual term=reverse
+ hi VisualNOS term=NONE
+ hi WarningMsg term=standout
+ hi WildMenu term=bold
+ hi CursorIM term=NONE
+ hi ToolbarLine term=reverse
+ hi ToolbarButton term=bold,reverse
+ hi CurSearch term=reverse
+ hi CursorLineFold term=underline
+ hi CursorLineSign term=underline
+ hi Comment term=bold
+ hi Constant term=NONE
+ hi Error term=bold,reverse
+ hi Identifier term=NONE
+ hi Ignore term=NONE
+ hi PreProc term=NONE
+ hi Special term=NONE
+ hi Statement term=NONE
+ hi Todo term=bold,reverse
+ hi Type term=NONE
+ hi Underlined term=underline
+ unlet s:t_Co
+ finish
+endif
+
+" Background: dark
+" Color: foreground #FFFFFF 231 white
+" Color: background #262626 235 black
+" Color: color00 #000000 16 black
+" Color: color08 #333333 236 darkgrey
+" Color: color01 #FF0000 196 darkred
+" Color: color09 #FFAFAF 217 red
+" Color: color02 #5F8700 64 darkgreen
+" Color: color10 #00875F 29 green
+" Color: color03 #ffff00 226 darkyellow
+" Color: color11 #FFD700 220 yellow
+" Color: color04 #87d7FF 117 darkblue
+" Color: color12 #5F87D7 68 blue
+" Color: color05 #d7d787 186 darkmagenta
+" Color: color13 #AFAF87 144 magenta
+" Color: color06 #FFD7AF 223 darkcyan
+" Color: color14 #FF8787 210 cyan
+" Color: color07 #666666 241 grey
+" Color: color15 #FFFFFF 231 white
+" Color: color16 #D7875F 173 darkmagenta
+" Color: color17 #00FF00 46 darkgreen
+" Color: Pmenu #4A4A4A 239 darkgrey
+" Term colors: color00 color01 color02 color03 color04 color05 color06 color07
+" Term colors: color08 color09 color10 color11 color12 color13 color14 color15
+" Color: bgDiffA #5F875F 65 darkgreen
+" Color: bgDiffC #5F87AF 67 blue
+" Color: bgDiffD #AF5FAF 133 magenta
+" Color: bgDiffT #C6C6C6 251 grey
+" Color: fgDiffW #FFFFFF 231 white
+" Color: fgDiffB #000000 16 black
+" Color: bgDiffC8 #5F87AF 67 darkblue
+" Color: bgDiffD8 #AF5FAF 133 darkmagenta
+" vim: et ts=2 sw=2
diff --git a/runtime/colors/torte.vim b/runtime/colors/torte.vim
index 0e7a916a1c..bddd1c4e74 100644
--- a/runtime/colors/torte.vim
+++ b/runtime/colors/torte.vim
@@ -1,50 +1,419 @@
-" Vim color file
-" Maintainer: Thorsten Maerz <info@netztorte.de>
-" Last Change: 2006 Dec 07
-" grey on black
-" optimized for TFT panels
+" Name: torte
+" Description: Remake of torte (grey on black)
+" Author: Original maintainer Thorsten Maerz <info@netztorte.de>
+" Maintainer: Original maintainer Thorsten Maerz <info@netztorte.de>
+" Website: https://github.com/vim/colorschemes
+" License: Same as Vim
+" Last Updated: Sun Jun 12 11:04:48 2022
+
+" Generated by Colortemplate v2.2.0
set background=dark
+
hi clear
-if exists("syntax_on")
- syntax reset
+let g:colors_name = 'torte'
+
+let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co > 1 ? &t_Co : 1
+
+if (has('termguicolors') && &termguicolors) || has('gui_running')
+ let g:terminal_ansi_colors = ['#000000', '#cd0000', '#00cd00', '#cdcd00', '#0000ee', '#cd00cd', '#00cdcd', '#e5e5e5', '#7f7f7f', '#ff0000', '#00ff00', '#ffff00', '#5c5cff', '#ff00ff', '#00ffff', '#ffffff']
+endif
+hi! link Terminal Normal
+hi! link LineNrAbove LineNr
+hi! link LineNrBelow LineNr
+hi! link CurSearch Search
+hi! link CursorLineFold CursorLine
+hi! link CursorLineSign CursorLine
+hi! link StatusLineTerm StatusLine
+hi! link StatusLineTermNC StatusLineNC
+hi Normal guifg=#cccccc guibg=#000000 gui=NONE cterm=NONE
+hi Comment guifg=#80a0ff guibg=NONE gui=NONE cterm=NONE
+hi Constant guifg=#ffa0a0 guibg=NONE gui=NONE cterm=NONE
+hi Identifier guifg=#40ffff guibg=NONE gui=NONE cterm=NONE
+hi Statement guifg=#ffff60 guibg=NONE gui=NONE cterm=NONE
+hi PreProc guifg=#ff80ff guibg=NONE gui=NONE cterm=NONE
+hi Type guifg=#60ff60 guibg=NONE gui=NONE cterm=NONE
+hi Special guifg=#ffa500 guibg=NONE gui=NONE cterm=NONE
+hi Underlined guifg=#80a0ff guibg=NONE gui=underline cterm=underline
+hi Ignore guifg=#000000 guibg=#000000 gui=NONE cterm=NONE
+hi Error guifg=#ffffff guibg=#ff0000 gui=NONE cterm=NONE
+hi Todo guifg=#ffff00 guibg=#000000 gui=reverse cterm=reverse
+hi Folded guifg=#00cdcd guibg=#3a3a3a gui=NONE cterm=NONE
+hi Visual guifg=#000000 guibg=#a9a9a9 gui=bold cterm=NONE
+hi CursorColumn guifg=NONE guibg=#666666 gui=NONE cterm=NONE
+hi CursorLine guifg=NONE guibg=#666666 gui=NONE cterm=NONE
+hi CursorLineNr guifg=#ffff00 guibg=#666666 gui=NONE cterm=NONE
+hi SignColumn guifg=#00ffff guibg=NONE gui=NONE cterm=NONE
+hi FoldColumn guifg=#00ffff guibg=NONE gui=NONE cterm=NONE
+hi ColorColumn guifg=#cccccc guibg=#8b0000 gui=NONE cterm=NONE
+hi Conceal guifg=#e5e5e5 guibg=#a9a9a9 gui=NONE cterm=NONE
+hi Cursor guifg=#000000 guibg=#00ff00 gui=bold cterm=NONE
+hi lCursor guifg=#000000 guibg=#e5e5e5 gui=NONE cterm=NONE
+hi CursorIM guifg=NONE guibg=fg gui=NONE cterm=NONE
+hi Title guifg=#ff00ff guibg=NONE gui=bold cterm=bold
+hi Directory guifg=#00ffff guibg=NONE gui=NONE cterm=NONE
+hi Search guifg=#ff0000 guibg=#000000 gui=reverse cterm=reverse
+hi IncSearch guifg=#00cd00 guibg=#000000 gui=reverse cterm=reverse
+hi NonText guifg=#0000ff guibg=NONE gui=bold cterm=NONE
+hi EndOfBuffer guifg=#0000ff guibg=NONE gui=bold cterm=NONE
+hi ErrorMsg guifg=#ffffff guibg=#cd0000 gui=NONE cterm=NONE
+hi WarningMsg guifg=#ff0000 guibg=NONE gui=NONE cterm=NONE
+hi LineNr guifg=#7f7f7f guibg=NONE gui=NONE cterm=NONE
+hi MatchParen guifg=NONE guibg=#008b8b gui=NONE cterm=NONE
+hi ModeMsg guifg=NONE guibg=NONE gui=bold ctermfg=NONE ctermbg=NONE cterm=bold
+hi MoreMsg guifg=#2e8b57 guibg=NONE gui=bold cterm=bold
+hi Question guifg=#00ff00 guibg=NONE gui=bold cterm=bold
+hi SpecialKey guifg=#00ffff guibg=NONE gui=NONE cterm=NONE
+hi VisualNOS guifg=NONE guibg=#000000 gui=bold,underline cterm=underline
+hi WildMenu guifg=#000000 guibg=#ffff00 gui=NONE cterm=NONE
+hi QuickFixLine guifg=#000000 guibg=#cdcd00 gui=NONE cterm=NONE
+hi SpellBad guifg=#ff0000 guibg=NONE guisp=#ff0000 gui=undercurl cterm=underline
+hi SpellCap guifg=#5c5cff guibg=NONE guisp=#5c5cff gui=undercurl cterm=underline
+hi SpellLocal guifg=#ff00ff guibg=NONE guisp=#ff00ff gui=undercurl cterm=underline
+hi SpellRare guifg=#00ffff guibg=NONE guisp=#00ffff gui=undercurl cterm=underline
+hi StatusLine guifg=#ffffff guibg=#0000ee gui=bold cterm=bold
+hi StatusLineNC guifg=#000000 guibg=#e5e5e5 gui=NONE cterm=NONE
+hi VertSplit guifg=#000000 guibg=#e5e5e5 gui=NONE cterm=NONE
+hi TabLine guifg=#ffffff guibg=#7f7f7f gui=NONE cterm=NONE
+hi TabLineFill guifg=NONE guibg=#000000 gui=reverse cterm=reverse
+hi TabLineSel guifg=#cccccc guibg=#000000 gui=bold cterm=bold
+hi ToolbarLine guifg=NONE guibg=#000000 gui=NONE cterm=NONE
+hi ToolbarButton guifg=#000000 guibg=#e5e5e5 gui=bold cterm=bold
+hi Pmenu guifg=fg guibg=#303030 gui=NONE cterm=NONE
+hi PmenuSbar guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
+hi PmenuSel guifg=#000000 guibg=#ffff00 gui=NONE cterm=NONE
+hi PmenuThumb guifg=NONE guibg=#bebebe gui=NONE cterm=NONE
+hi DiffAdd guifg=#ffffff guibg=#5f875f gui=NONE cterm=NONE
+hi DiffChange guifg=#ffffff guibg=#5f87af gui=NONE cterm=NONE
+hi DiffText guifg=#000000 guibg=#c6c6c6 gui=NONE cterm=NONE
+hi DiffDelete guifg=#ffffff guibg=#af5faf gui=NONE cterm=NONE
+
+if s:t_Co >= 256
+ hi! link Terminal Normal
+ hi! link LineNrAbove LineNr
+ hi! link LineNrBelow LineNr
+ hi! link CurSearch Search
+ hi! link CursorLineFold CursorLine
+ hi! link CursorLineSign CursorLine
+ hi! link StatusLineTerm StatusLine
+ hi! link StatusLineTermNC StatusLineNC
+ hi Normal ctermfg=251 ctermbg=16 cterm=NONE
+ hi Comment ctermfg=111 ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=217 ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=87 ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=227 ctermbg=NONE cterm=NONE
+ hi PreProc ctermfg=213 ctermbg=NONE cterm=NONE
+ hi Type ctermfg=83 ctermbg=NONE cterm=NONE
+ hi Special ctermfg=214 ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=111 ctermbg=NONE cterm=underline
+ hi Ignore ctermfg=16 ctermbg=16 cterm=NONE
+ hi Error ctermfg=231 ctermbg=196 cterm=NONE
+ hi Todo ctermfg=226 ctermbg=16 cterm=reverse
+ hi Folded ctermfg=44 ctermbg=237 cterm=NONE
+ hi Visual ctermfg=16 ctermbg=248 cterm=NONE
+ hi CursorColumn ctermfg=NONE ctermbg=242 cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=242 cterm=NONE
+ hi CursorLineNr ctermfg=226 ctermbg=242 cterm=NONE
+ hi SignColumn ctermfg=51 ctermbg=NONE cterm=NONE
+ hi FoldColumn ctermfg=51 ctermbg=NONE cterm=NONE
+ hi ColorColumn ctermfg=251 ctermbg=88 cterm=NONE
+ hi Conceal ctermfg=254 ctermbg=248 cterm=NONE
+ hi Cursor ctermfg=16 ctermbg=46 cterm=NONE
+ hi lCursor ctermfg=16 ctermbg=254 cterm=NONE
+ hi CursorIM ctermfg=NONE ctermbg=fg cterm=NONE
+ hi Title ctermfg=201 ctermbg=NONE cterm=bold
+ hi Directory ctermfg=51 ctermbg=NONE cterm=NONE
+ hi Search ctermfg=196 ctermbg=16 cterm=reverse
+ hi IncSearch ctermfg=40 ctermbg=16 cterm=reverse
+ hi NonText ctermfg=21 ctermbg=NONE cterm=NONE
+ hi EndOfBuffer ctermfg=21 ctermbg=NONE cterm=NONE
+ hi ErrorMsg ctermfg=231 ctermbg=160 cterm=NONE
+ hi WarningMsg ctermfg=196 ctermbg=NONE cterm=NONE
+ hi LineNr ctermfg=102 ctermbg=NONE cterm=NONE
+ hi MatchParen ctermfg=NONE ctermbg=30 cterm=NONE
+ hi ModeMsg ctermfg=NONE ctermbg=NONE cterm=bold
+ hi MoreMsg ctermfg=29 ctermbg=NONE cterm=bold
+ hi Question ctermfg=46 ctermbg=NONE cterm=bold
+ hi SpecialKey ctermfg=51 ctermbg=NONE cterm=NONE
+ hi VisualNOS ctermfg=NONE ctermbg=16 cterm=underline
+ hi WildMenu ctermfg=16 ctermbg=226 cterm=NONE
+ hi QuickFixLine ctermfg=16 ctermbg=184 cterm=NONE
+ hi SpellBad ctermfg=196 ctermbg=NONE cterm=underline
+ hi SpellCap ctermfg=63 ctermbg=NONE cterm=underline
+ hi SpellLocal ctermfg=201 ctermbg=NONE cterm=underline
+ hi SpellRare ctermfg=51 ctermbg=NONE cterm=underline
+ hi StatusLine ctermfg=231 ctermbg=20 cterm=bold
+ hi StatusLineNC ctermfg=16 ctermbg=254 cterm=NONE
+ hi VertSplit ctermfg=16 ctermbg=254 cterm=NONE
+ hi TabLine ctermfg=231 ctermbg=102 cterm=NONE
+ hi TabLineFill ctermfg=NONE ctermbg=16 cterm=reverse
+ hi TabLineSel ctermfg=251 ctermbg=16 cterm=bold
+ hi ToolbarLine ctermfg=NONE ctermbg=16 cterm=NONE
+ hi ToolbarButton ctermfg=16 ctermbg=254 cterm=bold
+ hi Pmenu ctermfg=fg ctermbg=236 cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi PmenuSel ctermfg=16 ctermbg=226 cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=250 cterm=NONE
+ hi DiffAdd ctermfg=231 ctermbg=65 cterm=NONE
+ hi DiffChange ctermfg=231 ctermbg=67 cterm=NONE
+ hi DiffText ctermfg=16 ctermbg=251 cterm=NONE
+ hi DiffDelete ctermfg=231 ctermbg=133 cterm=NONE
+ unlet s:t_Co
+ finish
endif
-"colorscheme default
-let g:colors_name = "torte"
-
-" hardcoded colors :
-" GUI Comment : #80a0ff = Light blue
-
-" GUI
-highlight Normal guifg=Grey80 guibg=Black
-highlight Search guifg=Black guibg=Red gui=bold
-highlight Visual guifg=#404040 gui=bold
-highlight Cursor guifg=Black guibg=Green gui=bold
-highlight Special guifg=Orange
-highlight Comment guifg=#80a0ff
-highlight StatusLine guifg=blue guibg=white
-highlight Statement guifg=Yellow gui=NONE
-highlight Type gui=NONE
-
-" Console
-highlight Normal ctermfg=LightGrey ctermbg=Black
-highlight Search ctermfg=Black ctermbg=Red cterm=NONE
-highlight Visual cterm=reverse
-highlight Cursor ctermfg=Black ctermbg=Green cterm=bold
-highlight Special ctermfg=Brown
-highlight Comment ctermfg=Blue
-highlight StatusLine ctermfg=blue ctermbg=white
-highlight Statement ctermfg=Yellow cterm=NONE
-highlight Type cterm=NONE
-
-" only for vim 5
-if has("unix")
- if v:version<600
- highlight Normal ctermfg=Grey ctermbg=Black cterm=NONE guifg=Grey80 guibg=Black gui=NONE
- highlight Search ctermfg=Black ctermbg=Red cterm=bold guifg=Black guibg=Red gui=bold
- highlight Visual ctermfg=Black ctermbg=yellow cterm=bold guifg=#404040 gui=bold
- highlight Special ctermfg=LightBlue cterm=NONE guifg=LightBlue gui=NONE
- highlight Comment ctermfg=Cyan cterm=NONE guifg=LightBlue gui=NONE
- endif
+
+if s:t_Co >= 16
+ hi Normal ctermfg=white ctermbg=black cterm=NONE
+ hi Comment ctermfg=blue ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=yellow ctermbg=NONE cterm=NONE
+ hi PreProc ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Type ctermfg=green ctermbg=NONE cterm=NONE
+ hi Special ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=blue ctermbg=NONE cterm=underline
+ hi Ignore ctermfg=black ctermbg=black cterm=NONE
+ hi Error ctermfg=white ctermbg=red cterm=NONE
+ hi Todo ctermfg=yellow ctermbg=black cterm=reverse
+ hi Folded ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Visual ctermfg=black ctermbg=grey cterm=NONE
+ hi CursorColumn ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorLineNr ctermfg=yellow ctermbg=NONE cterm=bold
+ hi SignColumn ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi FoldColumn ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi ColorColumn ctermfg=white ctermbg=darkred cterm=NONE
+ hi Conceal ctermfg=grey ctermbg=grey cterm=NONE
+ hi Cursor ctermfg=black ctermbg=green cterm=NONE
+ hi lCursor ctermfg=black ctermbg=grey cterm=NONE
+ hi CursorIM ctermfg=NONE ctermbg=fg cterm=NONE
+ hi Title ctermfg=magenta ctermbg=NONE cterm=bold
+ hi Directory ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi Search ctermfg=red ctermbg=black cterm=reverse
+ hi IncSearch ctermfg=darkgreen ctermbg=black cterm=reverse
+ hi NonText ctermfg=blue ctermbg=NONE cterm=NONE
+ hi EndOfBuffer ctermfg=blue ctermbg=NONE cterm=NONE
+ hi ErrorMsg ctermfg=white ctermbg=darkred cterm=NONE
+ hi WarningMsg ctermfg=red ctermbg=NONE cterm=NONE
+ hi LineNr ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi MatchParen ctermfg=NONE ctermbg=darkcyan cterm=NONE
+ hi ModeMsg ctermfg=NONE ctermbg=NONE cterm=bold
+ hi MoreMsg ctermfg=darkgreen ctermbg=NONE cterm=bold
+ hi Question ctermfg=green ctermbg=NONE cterm=bold
+ hi SpecialKey ctermfg=cyan ctermbg=NONE cterm=NONE
+ hi VisualNOS ctermfg=NONE ctermbg=black cterm=underline
+ hi WildMenu ctermfg=black ctermbg=yellow cterm=NONE
+ hi QuickFixLine ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi SpellBad ctermfg=red ctermbg=NONE cterm=underline
+ hi SpellCap ctermfg=blue ctermbg=NONE cterm=underline
+ hi SpellLocal ctermfg=magenta ctermbg=NONE cterm=underline
+ hi SpellRare ctermfg=cyan ctermbg=NONE cterm=underline
+ hi StatusLine ctermfg=white ctermbg=darkblue cterm=bold
+ hi StatusLineNC ctermfg=black ctermbg=grey cterm=NONE
+ hi VertSplit ctermfg=black ctermbg=grey cterm=NONE
+ hi TabLine ctermfg=white ctermbg=darkgrey cterm=NONE
+ hi TabLineFill ctermfg=NONE ctermbg=black cterm=reverse
+ hi TabLineSel ctermfg=white ctermbg=black cterm=bold
+ hi ToolbarLine ctermfg=NONE ctermbg=black cterm=NONE
+ hi ToolbarButton ctermfg=black ctermbg=grey cterm=bold
+ hi Pmenu ctermfg=fg ctermbg=darkgrey cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi PmenuSel ctermfg=black ctermbg=yellow cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=grey cterm=NONE
+ hi DiffAdd ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi DiffChange ctermfg=white ctermbg=blue cterm=NONE
+ hi DiffText ctermfg=black ctermbg=grey cterm=NONE
+ hi DiffDelete ctermfg=white ctermbg=magenta cterm=NONE
+ unlet s:t_Co
+ finish
+endif
+
+if s:t_Co >= 8
+ hi Normal ctermfg=grey ctermbg=black cterm=NONE
+ hi Comment ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=darkyellow ctermbg=NONE cterm=bold
+ hi PreProc ctermfg=darkcyan ctermbg=NONE cterm=bold
+ hi Type ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi Special ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline
+ hi Ignore ctermfg=black ctermbg=black cterm=NONE
+ hi Error ctermfg=grey ctermbg=darkred cterm=NONE
+ hi Todo ctermfg=darkyellow ctermbg=black cterm=reverse
+ hi Folded ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi CursorColumn ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorLineNr ctermfg=darkyellow ctermbg=NONE cterm=bold,underline
+ hi SignColumn ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi FoldColumn ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi ColorColumn ctermfg=white ctermbg=darkred cterm=NONE
+ hi Conceal ctermfg=grey ctermbg=NONE cterm=NONE
+ hi Title ctermfg=darkmagenta ctermbg=NONE cterm=bold
+ hi Directory ctermfg=darkcyan ctermbg=NONE cterm=bold
+ hi Search ctermfg=darkred ctermbg=black cterm=bold,reverse
+ hi IncSearch ctermfg=darkgreen ctermbg=black cterm=reverse
+ hi NonText ctermfg=darkblue ctermbg=NONE cterm=bold
+ hi EndOfBuffer ctermfg=darkblue ctermbg=NONE cterm=bold
+ hi ErrorMsg ctermfg=grey ctermbg=darkred cterm=NONE
+ hi WarningMsg ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi LineNr ctermfg=grey ctermbg=NONE cterm=bold
+ hi MatchParen ctermfg=grey ctermbg=darkcyan cterm=NONE
+ hi ModeMsg ctermfg=NONE ctermbg=NONE cterm=bold
+ hi MoreMsg ctermfg=darkgreen ctermbg=NONE cterm=bold
+ hi Question ctermfg=darkgreen ctermbg=NONE cterm=bold
+ hi SpecialKey ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi VisualNOS ctermfg=NONE ctermbg=black cterm=underline
+ hi WildMenu ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi QuickFixLine ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi SpellBad ctermfg=darkred ctermbg=darkyellow cterm=reverse
+ hi SpellCap ctermfg=darkblue ctermbg=darkyellow cterm=reverse
+ hi SpellLocal ctermfg=darkmagenta ctermbg=darkyellow cterm=reverse
+ hi SpellRare ctermfg=darkcyan ctermbg=NONE cterm=reverse
+ hi StatusLine ctermfg=grey ctermbg=darkblue cterm=bold
+ hi StatusLineNC ctermfg=black ctermbg=grey cterm=NONE
+ hi VertSplit ctermfg=black ctermbg=grey cterm=NONE
+ hi TabLine ctermfg=black ctermbg=grey cterm=NONE
+ hi TabLineFill ctermfg=NONE ctermbg=black cterm=reverse
+ hi TabLineSel ctermfg=white ctermbg=black cterm=bold
+ hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi ToolbarButton ctermfg=grey ctermbg=black cterm=bold,reverse
+ hi Pmenu ctermfg=black ctermbg=darkcyan cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi PmenuSel ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=darkmagenta cterm=NONE
+ hi DiffAdd ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi DiffChange ctermfg=white ctermbg=darkblue cterm=NONE
+ hi DiffText ctermfg=black ctermbg=grey cterm=NONE
+ hi DiffDelete ctermfg=white ctermbg=darkmagenta cterm=NONE
+ unlet s:t_Co
+ finish
+endif
+
+if s:t_Co >= 0
+ hi Normal term=NONE
+ hi ColorColumn term=reverse
+ hi Conceal term=NONE
+ hi Cursor term=reverse
+ hi CursorColumn term=NONE
+ hi CursorLine term=underline
+ hi CursorLineNr term=bold
+ hi DiffAdd term=reverse
+ hi DiffChange term=NONE
+ hi DiffDelete term=reverse
+ hi DiffText term=reverse
+ hi Directory term=NONE
+ hi EndOfBuffer term=NONE
+ hi ErrorMsg term=bold,reverse
+ hi FoldColumn term=NONE
+ hi Folded term=NONE
+ hi IncSearch term=bold,reverse,underline
+ hi LineNr term=NONE
+ hi MatchParen term=bold,underline
+ hi ModeMsg term=bold
+ hi MoreMsg term=NONE
+ hi NonText term=NONE
+ hi Pmenu term=reverse
+ hi PmenuSbar term=reverse
+ hi PmenuSel term=bold
+ hi PmenuThumb term=NONE
+ hi Question term=standout
+ hi Search term=reverse
+ hi SignColumn term=reverse
+ hi SpecialKey term=bold
+ hi SpellBad term=underline
+ hi SpellCap term=underline
+ hi SpellLocal term=underline
+ hi SpellRare term=underline
+ hi StatusLine term=bold,reverse
+ hi StatusLineNC term=bold,underline
+ hi TabLine term=bold,underline
+ hi TabLineFill term=NONE
+ hi Terminal term=NONE
+ hi TabLineSel term=bold,reverse
+ hi Title term=NONE
+ hi VertSplit term=NONE
+ hi Visual term=reverse
+ hi VisualNOS term=NONE
+ hi WarningMsg term=standout
+ hi WildMenu term=bold
+ hi CursorIM term=NONE
+ hi ToolbarLine term=reverse
+ hi ToolbarButton term=bold,reverse
+ hi CurSearch term=reverse
+ hi CursorLineFold term=underline
+ hi CursorLineSign term=underline
+ hi Comment term=bold
+ hi Constant term=NONE
+ hi Error term=bold,reverse
+ hi Identifier term=NONE
+ hi Ignore term=NONE
+ hi PreProc term=NONE
+ hi Special term=NONE
+ hi Statement term=NONE
+ hi Todo term=bold,reverse
+ hi Type term=NONE
+ hi Underlined term=underline
+ unlet s:t_Co
+ finish
endif
+" Background: dark
+" Color: foreground #CCCCCC 251 white
+" Color: background #000000 16 black
+" Color: color00 #000000 16 black
+" Color: color08 #7f7f7f 102 darkgrey
+" Color: color01 #cd0000 160 darkred
+" Color: color09 #ff0000 196 red
+" Color: color02 #00cd00 40 darkgreen
+" Color: color10 #00ff00 46 green
+" Color: color03 #cdcd00 184 darkyellow
+" Color: color11 #ffff00 226 yellow
+" Color: color04 #0000ee 20 darkblue
+" Color: color12 #5c5cff 63 blue
+" Color: color05 #cd00cd 164 darkmagenta
+" Color: color13 #ff00ff 201 magenta
+" Color: color06 #00cdcd 44 darkcyan
+" Color: color14 #00ffff 51 cyan
+" Color: color07 #e5e5e5 254 grey
+" Color: color15 #ffffff 231 white
+" Term colors: color00 color01 color02 color03 color04 color05 color06 color07
+" Term colors: color08 color09 color10 color11 color12 color13 color14 color15
+" Color: rgbGrey40 #666666 242 grey
+" Color: rgbDarkGrey #a9a9a9 248 grey
+" Color: rgbDarkBlue #00008b 18 darkblue
+" Color: rgbDarkMagenta #8b008b 90 darkmagenta
+" Color: rgbBlue #0000ff 21 blue
+" Color: rgbDarkCyan #008b8b 30 darkcyan
+" Color: Directory #00ffff 51 cyan
+" Color: rgbSeaGreen #2e8b57 29 darkgreen
+" Color: rgbGrey #bebebe 250 grey
+" Color: Question #00ff00 46 green
+" Color: SignColumn #a9a9a9 248 black
+" Color: SpecialKey #00ffff 51 cyan
+" Color: StatusLineTerm #90ee90 120 darkgreen
+" Color: Title #ff00ff 201 magenta
+" Color: WarningMsg #ff0000 196 red
+" Color: ToolbarLine #7f7f7f 243 black
+" Color: ToolbarButton #d3d3d3 252 darkgrey
+" Color: Comment #80a0ff 111 blue
+" Color: Constant #ffa0a0 217 darkmagenta
+" Color: Special #ffa500 214 darkred
+" Color: Identifier #40ffff 87 cyan
+" Color: Statement #ffff60 227 yellow
+" Color: PreProc #ff80ff 213 darkcyan
+" Color: Type #60ff60 83 green
+" Color: Underlined #80a0ff 111 blue
+" Color: FoldedBG #3a3a3a 237 darkgrey
+" Color: Pmenu #303030 236 darkgrey
+" Color: rgbDarkRed #8b0000 88 darkred
+" Color: bgDiffA #5F875F 65 darkgreen
+" Color: bgDiffC #5F87AF 67 blue
+" Color: bgDiffD #AF5FAF 133 magenta
+" Color: bgDiffT #C6C6C6 251 grey
+" Color: fgDiffW #FFFFFF 231 white
+" Color: fgDiffB #000000 16 black
+" Color: bgDiffC8 #5F87AF 67 darkblue
+" Color: bgDiffD8 #AF5FAF 133 darkmagenta
+" vim: et ts=2 sw=2
diff --git a/runtime/colors/zellner.vim b/runtime/colors/zellner.vim
index ab875825c9..6133538885 100644
--- a/runtime/colors/zellner.vim
+++ b/runtime/colors/zellner.vim
@@ -1,54 +1,407 @@
-" local syntax file - set colors on a per-machine basis:
-" vim: tw=0 ts=4 sw=4
-" Vim color file
-" Maintainer: Ron Aaron <ron@ronware.org>
-" Last Change: 2003 May 02
+" Name: zellner
+" Description: Light background colorscheme.
+" Author: Original author Ron Aaron <ron@ronware.org>
+" Maintainer: Original maintainer Ron Aaron <ron@ronware.org>
+" Website: https://github.com/vim/colorschemes
+" License: Same as Vim
+" Last Updated: Sun Jun 12 11:05:43 2022
+
+" Generated by Colortemplate v2.2.0
set background=light
+
hi clear
-if exists("syntax_on")
- syntax reset
+let g:colors_name = 'zellner'
+
+let s:t_Co = exists('&t_Co') && !empty(&t_Co) && &t_Co > 1 ? &t_Co : 1
+
+if (has('termguicolors') && &termguicolors) || has('gui_running')
+ let g:terminal_ansi_colors = ['#ffffff', '#a52a2a', '#ff00ff', '#a020f0', '#0000ff', '#0000ff', '#ff00ff', '#a9a9a9', '#ff0000', '#a52a2a', '#ff00ff', '#a020f0', '#0000ff', '#0000ff', '#ff00ff', '#000000']
endif
-let g:colors_name = "zellner"
-
-hi Comment term=bold ctermfg=Red guifg=Red
-hi Normal guifg=black guibg=white
-hi Constant term=underline ctermfg=Magenta guifg=Magenta
-hi Special term=bold ctermfg=Magenta guifg=Magenta
-hi Identifier term=underline ctermfg=Blue guifg=Blue
-hi Statement term=bold ctermfg=DarkRed gui=NONE guifg=Brown
-hi PreProc term=underline ctermfg=Magenta guifg=Purple
-hi Type term=underline ctermfg=Blue gui=NONE guifg=Blue
-hi Visual term=reverse ctermfg=Yellow ctermbg=Red gui=NONE guifg=Black guibg=Yellow
-hi Search term=reverse ctermfg=Black ctermbg=Cyan gui=NONE guifg=Black guibg=Cyan
-hi Tag term=bold ctermfg=DarkGreen guifg=DarkGreen
-hi Error term=reverse ctermfg=15 ctermbg=9 guibg=Red guifg=White
-hi Todo term=standout ctermbg=Yellow ctermfg=Black guifg=Blue guibg=Yellow
-hi StatusLine term=bold,reverse cterm=NONE ctermfg=Yellow ctermbg=DarkGray gui=NONE guifg=Yellow guibg=DarkGray
-hi! link MoreMsg Comment
-hi! link ErrorMsg Visual
-hi! link WarningMsg ErrorMsg
-hi! link Question Comment
-hi link String Constant
-hi link Character Constant
-hi link Number Constant
-hi link Boolean Constant
-hi link Float Number
-hi link Function Identifier
-hi link Conditional Statement
-hi link Repeat Statement
-hi link Label Statement
-hi link Operator Statement
-hi link Keyword Statement
-hi link Exception Statement
-hi link Include PreProc
-hi link Define PreProc
-hi link Macro PreProc
-hi link PreCondit PreProc
-hi link StorageClass Type
-hi link Structure Type
-hi link Typedef Type
-hi link SpecialChar Special
-hi link Delimiter Special
-hi link SpecialComment Special
-hi link Debug Special
+hi! link Terminal Normal
+hi! link LineNrAbove LineNr
+hi! link LineNrBelow LineNr
+hi! link CurSearch Search
+hi! link CursorLineFold CursorLine
+hi! link CursorLineSign CursorLine
+hi Normal guifg=#000000 guibg=#ffffff gui=NONE cterm=NONE
+hi Folded guifg=#00008b guibg=#d3d3d3 gui=NONE cterm=NONE
+hi CursorLine guifg=NONE guibg=#e5e5e5 gui=NONE cterm=NONE
+hi CursorColumn guifg=NONE guibg=#e5e5e5 gui=NONE cterm=NONE
+hi CursorLineNr guifg=#a52a2a guibg=NONE gui=bold cterm=bold
+hi QuickFixLine guifg=#ffffff guibg=#6a5acd gui=NONE cterm=NONE
+hi EndOfBuffer guifg=#a9a9a9 guibg=NONE gui=NONE cterm=NONE
+hi StatusLine guifg=#ffff00 guibg=#a9a9a9 gui=NONE cterm=NONE
+hi StatusLineNC guifg=#ffffff guibg=#000000 gui=NONE cterm=NONE
+hi StatusLineTerm guifg=#ffffff guibg=#006400 gui=NONE cterm=NONE
+hi StatusLineTermNC guifg=#ffffff guibg=#0000ff gui=NONE cterm=NONE
+hi VertSplit guifg=#ffffff guibg=#000000 gui=NONE cterm=NONE
+hi Pmenu guifg=#000000 guibg=#dadada gui=NONE cterm=NONE
+hi PmenuSel guifg=#000000 guibg=#ffff00 gui=NONE cterm=NONE
+hi PmenuSbar guifg=NONE guibg=#ffffff gui=NONE cterm=NONE
+hi PmenuThumb guifg=NONE guibg=#a9a9a9 gui=NONE cterm=NONE
+hi TabLine guifg=#000000 guibg=#a9a9a9 gui=underline cterm=underline
+hi TabLineFill guifg=NONE guibg=NONE gui=reverse ctermfg=NONE ctermbg=NONE cterm=reverse
+hi TabLineSel guifg=#000000 guibg=#ffffff gui=bold cterm=bold
+hi ToolbarLine guifg=NONE guibg=#d3d3d3 gui=NONE cterm=NONE
+hi ToolbarButton guifg=NONE guibg=#a9a9a9 gui=bold cterm=bold
+hi NonText guifg=#a9a9a9 guibg=NONE gui=NONE cterm=NONE
+hi SpecialKey guifg=#a9a9a9 guibg=NONE gui=NONE cterm=NONE
+hi Visual guifg=#000000 guibg=#ffff00 gui=NONE cterm=NONE
+hi VisualNOS guifg=NONE guibg=#ff0000 gui=NONE cterm=NONE
+hi LineNr guifg=#a52a2a guibg=NONE gui=NONE cterm=NONE
+hi FoldColumn guifg=#00008b guibg=NONE gui=NONE cterm=NONE
+hi SignColumn guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
+hi Underlined guifg=#6a5acd guibg=NONE gui=underline cterm=underline
+hi Error guifg=#ff0000 guibg=#ffffff gui=reverse cterm=reverse
+hi ErrorMsg guifg=#ff0000 guibg=#ffffff gui=reverse cterm=reverse
+hi WarningMsg guifg=#a020f0 guibg=#ffffff gui=NONE cterm=NONE
+hi MoreMsg guifg=#000000 guibg=#ffffff gui=bold cterm=bold
+hi ModeMsg guifg=#000000 guibg=#ffffff gui=bold cterm=bold
+hi Question guifg=#ff00ff guibg=NONE gui=bold cterm=bold
+hi Todo guifg=#000000 guibg=#ffff00 gui=NONE cterm=NONE
+hi MatchParen guifg=#ffffff guibg=#ff00ff gui=NONE cterm=NONE
+hi Search guifg=#ffffff guibg=#a020f0 gui=NONE cterm=NONE
+hi IncSearch guifg=#000000 guibg=NONE gui=reverse cterm=reverse
+hi WildMenu guifg=#000000 guibg=#ffff00 gui=NONE cterm=NONE
+hi ColorColumn guifg=NONE guibg=#eeeeee gui=NONE cterm=NONE
+hi Cursor guifg=#ffffff guibg=#000000 gui=NONE cterm=NONE
+hi lCursor guifg=#000000 guibg=#ff00ff gui=NONE cterm=NONE
+hi SpellBad guifg=#ff0000 guibg=NONE guisp=#ff0000 gui=undercurl cterm=underline
+hi SpellCap guifg=#0000ff guibg=NONE guisp=#0000ff gui=undercurl cterm=underline
+hi SpellLocal guifg=#878700 guibg=NONE guisp=#878700 gui=undercurl cterm=underline
+hi SpellRare guifg=#008787 guibg=NONE guisp=#008787 gui=undercurl cterm=underline
+hi Comment guifg=#ff0000 guibg=NONE gui=NONE cterm=NONE
+hi Constant guifg=#ff00ff guibg=NONE gui=NONE cterm=NONE
+hi Identifier guifg=#0000ff guibg=NONE gui=NONE cterm=NONE
+hi Statement guifg=#a52a2a guibg=NONE gui=NONE cterm=NONE
+hi PreProc guifg=#a020f0 guibg=NONE gui=NONE cterm=NONE
+hi Type guifg=#0000ff guibg=NONE gui=NONE cterm=NONE
+hi Special guifg=#ff00ff guibg=NONE gui=NONE cterm=NONE
+hi Tag guifg=#006400 guibg=NONE gui=NONE cterm=NONE
+hi Directory guifg=#0000ff guibg=NONE gui=bold cterm=bold
+hi Conceal guifg=#ff0000 guibg=NONE gui=NONE cterm=NONE
+hi Ignore guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
+hi Title guifg=#a020f0 guibg=NONE gui=bold cterm=bold
+hi DiffAdd guifg=#ffffff guibg=#5f875f gui=NONE cterm=NONE
+hi DiffChange guifg=#ffffff guibg=#5f87af gui=NONE cterm=NONE
+hi DiffText guifg=#000000 guibg=#c6c6c6 gui=NONE cterm=NONE
+hi DiffDelete guifg=#ffffff guibg=#af5faf gui=NONE cterm=NONE
+
+if s:t_Co >= 256
+ hi! link Terminal Normal
+ hi! link LineNrAbove LineNr
+ hi! link LineNrBelow LineNr
+ hi! link CurSearch Search
+ hi! link CursorLineFold CursorLine
+ hi! link CursorLineSign CursorLine
+ hi Normal ctermfg=16 ctermbg=231 cterm=NONE
+ hi Folded ctermfg=18 ctermbg=252 cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=254 cterm=NONE
+ hi CursorColumn ctermfg=NONE ctermbg=254 cterm=NONE
+ hi CursorLineNr ctermfg=124 ctermbg=NONE cterm=bold
+ hi QuickFixLine ctermfg=231 ctermbg=62 cterm=NONE
+ hi EndOfBuffer ctermfg=248 ctermbg=NONE cterm=NONE
+ hi StatusLine ctermfg=226 ctermbg=248 cterm=NONE
+ hi StatusLineNC ctermfg=231 ctermbg=16 cterm=NONE
+ hi StatusLineTerm ctermfg=231 ctermbg=22 cterm=NONE
+ hi StatusLineTermNC ctermfg=231 ctermbg=21 cterm=NONE
+ hi VertSplit ctermfg=231 ctermbg=16 cterm=NONE
+ hi Pmenu ctermfg=16 ctermbg=253 cterm=NONE
+ hi PmenuSel ctermfg=16 ctermbg=226 cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=231 cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=248 cterm=NONE
+ hi TabLine ctermfg=16 ctermbg=248 cterm=underline
+ hi TabLineFill ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi TabLineSel ctermfg=16 ctermbg=231 cterm=bold
+ hi ToolbarLine ctermfg=NONE ctermbg=252 cterm=NONE
+ hi ToolbarButton ctermfg=NONE ctermbg=248 cterm=bold
+ hi NonText ctermfg=248 ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=248 ctermbg=NONE cterm=NONE
+ hi Visual ctermfg=16 ctermbg=226 cterm=NONE
+ hi VisualNOS ctermfg=NONE ctermbg=196 cterm=NONE
+ hi LineNr ctermfg=124 ctermbg=NONE cterm=NONE
+ hi FoldColumn ctermfg=18 ctermbg=NONE cterm=NONE
+ hi SignColumn ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=62 ctermbg=NONE cterm=underline
+ hi Error ctermfg=196 ctermbg=231 cterm=reverse
+ hi ErrorMsg ctermfg=196 ctermbg=231 cterm=reverse
+ hi WarningMsg ctermfg=129 ctermbg=231 cterm=NONE
+ hi MoreMsg ctermfg=16 ctermbg=231 cterm=bold
+ hi ModeMsg ctermfg=16 ctermbg=231 cterm=bold
+ hi Question ctermfg=201 ctermbg=NONE cterm=bold
+ hi Todo ctermfg=16 ctermbg=226 cterm=NONE
+ hi MatchParen ctermfg=231 ctermbg=201 cterm=NONE
+ hi Search ctermfg=231 ctermbg=129 cterm=NONE
+ hi IncSearch ctermfg=16 ctermbg=NONE cterm=reverse
+ hi WildMenu ctermfg=16 ctermbg=226 cterm=NONE
+ hi ColorColumn ctermfg=NONE ctermbg=255 cterm=NONE
+ hi Cursor ctermfg=231 ctermbg=16 cterm=NONE
+ hi lCursor ctermfg=16 ctermbg=201 cterm=NONE
+ hi SpellBad ctermfg=196 ctermbg=NONE cterm=underline
+ hi SpellCap ctermfg=21 ctermbg=NONE cterm=underline
+ hi SpellLocal ctermfg=100 ctermbg=NONE cterm=underline
+ hi SpellRare ctermfg=30 ctermbg=NONE cterm=underline
+ hi Comment ctermfg=196 ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=201 ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=21 ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=124 ctermbg=NONE cterm=NONE
+ hi PreProc ctermfg=129 ctermbg=NONE cterm=NONE
+ hi Type ctermfg=21 ctermbg=NONE cterm=NONE
+ hi Special ctermfg=201 ctermbg=NONE cterm=NONE
+ hi Tag ctermfg=22 ctermbg=NONE cterm=NONE
+ hi Directory ctermfg=21 ctermbg=NONE cterm=bold
+ hi Conceal ctermfg=196 ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Title ctermfg=129 ctermbg=NONE cterm=bold
+ hi DiffAdd ctermfg=231 ctermbg=65 cterm=NONE
+ hi DiffChange ctermfg=231 ctermbg=67 cterm=NONE
+ hi DiffText ctermfg=16 ctermbg=251 cterm=NONE
+ hi DiffDelete ctermfg=231 ctermbg=133 cterm=NONE
+ unlet s:t_Co
+ finish
+endif
+
+if s:t_Co >= 16
+ hi Normal ctermfg=black ctermbg=white cterm=NONE
+ hi Folded ctermfg=darkblue ctermbg=NONE cterm=bold
+ hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorColumn ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorLineNr ctermfg=darkred ctermbg=NONE cterm=bold
+ hi QuickFixLine ctermfg=white ctermbg=blue cterm=NONE
+ hi EndOfBuffer ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi StatusLine ctermfg=yellow ctermbg=darkgrey cterm=NONE
+ hi StatusLineNC ctermfg=white ctermbg=black cterm=NONE
+ hi StatusLineTerm ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi StatusLineTermNC ctermfg=white ctermbg=blue cterm=NONE
+ hi VertSplit ctermfg=white ctermbg=black cterm=NONE
+ hi Pmenu ctermfg=black ctermbg=grey cterm=NONE
+ hi PmenuSel ctermfg=black ctermbg=yellow cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=white cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=darkgrey cterm=NONE
+ hi TabLine ctermfg=black ctermbg=grey cterm=underline
+ hi TabLineFill ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi TabLineSel ctermfg=black ctermbg=white cterm=bold
+ hi ToolbarLine ctermfg=NONE ctermbg=black cterm=NONE
+ hi ToolbarButton ctermfg=NONE ctermbg=grey cterm=bold
+ hi NonText ctermfg=grey ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=grey ctermbg=NONE cterm=NONE
+ hi Visual ctermfg=black ctermbg=yellow cterm=NONE
+ hi VisualNOS ctermfg=NONE ctermbg=red cterm=NONE
+ hi LineNr ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi FoldColumn ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi SignColumn ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=blue ctermbg=NONE cterm=underline
+ hi Error ctermfg=red ctermbg=white cterm=reverse
+ hi ErrorMsg ctermfg=red ctermbg=white cterm=reverse
+ hi WarningMsg ctermfg=darkmagenta ctermbg=white cterm=NONE
+ hi MoreMsg ctermfg=black ctermbg=white cterm=bold
+ hi ModeMsg ctermfg=black ctermbg=white cterm=bold
+ hi Question ctermfg=magenta ctermbg=NONE cterm=bold
+ hi Todo ctermfg=black ctermbg=yellow cterm=NONE
+ hi MatchParen ctermfg=white ctermbg=magenta cterm=NONE
+ hi Search ctermfg=white ctermbg=darkmagenta cterm=NONE
+ hi IncSearch ctermfg=black ctermbg=NONE cterm=reverse
+ hi WildMenu ctermfg=black ctermbg=yellow cterm=NONE
+ hi ColorColumn ctermfg=NONE ctermbg=grey cterm=NONE
+ hi Cursor ctermfg=white ctermbg=black cterm=NONE
+ hi lCursor ctermfg=black ctermbg=magenta cterm=NONE
+ hi SpellBad ctermfg=red ctermbg=NONE cterm=underline
+ hi SpellCap ctermfg=blue ctermbg=NONE cterm=underline
+ hi SpellLocal ctermfg=darkyellow ctermbg=NONE cterm=underline
+ hi SpellRare ctermfg=darkcyan ctermbg=NONE cterm=underline
+ hi Comment ctermfg=red ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=magenta ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=blue ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi PreProc ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi Type ctermfg=blue ctermbg=NONE cterm=NONE
+ hi Special ctermfg=magenta ctermbg=NONE cterm=NONE
+ hi Tag ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi Directory ctermfg=blue ctermbg=NONE cterm=bold
+ hi Conceal ctermfg=red ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Title ctermfg=darkmagenta ctermbg=NONE cterm=bold
+ hi DiffAdd ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi DiffChange ctermfg=white ctermbg=blue cterm=NONE
+ hi DiffText ctermfg=black ctermbg=grey cterm=NONE
+ hi DiffDelete ctermfg=white ctermbg=magenta cterm=NONE
+ unlet s:t_Co
+ finish
+endif
+
+if s:t_Co >= 8
+ hi Normal ctermfg=black ctermbg=gray cterm=NONE
+ hi Folded ctermfg=darkblue ctermbg=NONE cterm=bold
+ hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorColumn ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi CursorLineNr ctermfg=darkred ctermbg=NONE cterm=bold
+ hi QuickFixLine ctermfg=gray ctermbg=darkblue cterm=NONE
+ hi EndOfBuffer ctermfg=darkgrey ctermbg=NONE cterm=bold
+ hi StatusLine ctermfg=black ctermbg=gray cterm=bold,reverse
+ hi StatusLineNC ctermfg=black ctermbg=gray cterm=reverse
+ hi StatusLineTerm ctermfg=black ctermbg=gray cterm=bold,reverse
+ hi StatusLineTermNC ctermfg=black ctermbg=gray cterm=reverse
+ hi VertSplit ctermfg=black ctermbg=gray cterm=reverse
+ hi Pmenu ctermfg=black ctermbg=darkcyan cterm=NONE
+ hi PmenuSel ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=black cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=darkmagenta cterm=NONE
+ hi TabLine ctermfg=black ctermbg=gray cterm=reverse
+ hi TabLineFill ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi TabLineSel ctermfg=gray ctermbg=black cterm=bold,reverse
+ hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi ToolbarButton ctermfg=black ctermbg=gray cterm=bold,reverse
+ hi NonText ctermfg=gray ctermbg=NONE cterm=bold
+ hi SpecialKey ctermfg=gray ctermbg=NONE cterm=bold
+ hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse
+ hi VisualNOS ctermfg=NONE ctermbg=NONE cterm=underline
+ hi LineNr ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi FoldColumn ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi SignColumn ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline
+ hi Error ctermfg=darkred ctermbg=black cterm=reverse
+ hi ErrorMsg ctermfg=darkred ctermbg=black cterm=reverse
+ hi WarningMsg ctermfg=darkmagenta ctermbg=black cterm=NONE
+ hi MoreMsg ctermfg=gray ctermbg=black cterm=bold
+ hi ModeMsg ctermfg=gray ctermbg=black cterm=bold
+ hi Question ctermfg=darkmagenta ctermbg=NONE cterm=bold
+ hi Todo ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi MatchParen ctermfg=black ctermbg=darkmagenta cterm=NONE
+ hi Search ctermfg=black ctermbg=darkmagenta cterm=NONE
+ hi IncSearch ctermfg=gray ctermbg=NONE cterm=reverse
+ hi WildMenu ctermfg=gray ctermbg=darkyellow cterm=NONE
+ hi ColorColumn ctermfg=black ctermbg=darkred cterm=NONE
+ hi SpellBad ctermfg=darkred ctermbg=NONE cterm=reverse
+ hi SpellCap ctermfg=darkblue ctermbg=NONE cterm=reverse
+ hi SpellLocal ctermfg=darkyellow ctermbg=darkblue cterm=reverse
+ hi SpellRare ctermfg=darkcyan ctermbg=NONE cterm=reverse
+ hi Comment ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi Constant ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=darkred ctermbg=NONE cterm=bold
+ hi PreProc ctermfg=darkmagenta ctermbg=NONE cterm=bold
+ hi Type ctermfg=darkblue ctermbg=NONE cterm=bold
+ hi Special ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi Directory ctermfg=darkblue ctermbg=NONE cterm=bold
+ hi Conceal ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Title ctermfg=darkmagenta ctermbg=NONE cterm=bold
+ hi DiffAdd ctermfg=white ctermbg=darkgreen cterm=NONE
+ hi DiffChange ctermfg=white ctermbg=darkblue cterm=NONE
+ hi DiffText ctermfg=black ctermbg=grey cterm=NONE
+ hi DiffDelete ctermfg=white ctermbg=darkmagenta cterm=NONE
+ unlet s:t_Co
+ finish
+endif
+
+if s:t_Co >= 0
+ hi Normal term=NONE
+ hi ColorColumn term=reverse
+ hi Conceal term=NONE
+ hi Cursor term=reverse
+ hi CursorColumn term=NONE
+ hi CursorLine term=underline
+ hi CursorLineNr term=bold
+ hi DiffAdd term=reverse
+ hi DiffChange term=NONE
+ hi DiffDelete term=reverse
+ hi DiffText term=reverse
+ hi Directory term=NONE
+ hi EndOfBuffer term=NONE
+ hi ErrorMsg term=bold,reverse
+ hi FoldColumn term=NONE
+ hi Folded term=NONE
+ hi IncSearch term=bold,reverse,underline
+ hi LineNr term=NONE
+ hi MatchParen term=bold,underline
+ hi ModeMsg term=bold
+ hi MoreMsg term=NONE
+ hi NonText term=NONE
+ hi Pmenu term=reverse
+ hi PmenuSbar term=reverse
+ hi PmenuSel term=bold
+ hi PmenuThumb term=NONE
+ hi Question term=standout
+ hi Search term=reverse
+ hi SignColumn term=reverse
+ hi SpecialKey term=bold
+ hi SpellBad term=underline
+ hi SpellCap term=underline
+ hi SpellLocal term=underline
+ hi SpellRare term=underline
+ hi StatusLine term=bold,reverse
+ hi StatusLineNC term=bold,underline
+ hi TabLine term=bold,underline
+ hi TabLineFill term=NONE
+ hi Terminal term=NONE
+ hi TabLineSel term=bold,reverse
+ hi Title term=NONE
+ hi VertSplit term=NONE
+ hi Visual term=reverse
+ hi VisualNOS term=NONE
+ hi WarningMsg term=standout
+ hi WildMenu term=bold
+ hi CursorIM term=NONE
+ hi ToolbarLine term=reverse
+ hi ToolbarButton term=bold,reverse
+ hi CurSearch term=reverse
+ hi CursorLineFold term=underline
+ hi CursorLineSign term=underline
+ hi Comment term=bold
+ hi Constant term=NONE
+ hi Error term=bold,reverse
+ hi Identifier term=NONE
+ hi Ignore term=NONE
+ hi PreProc term=NONE
+ hi Special term=NONE
+ hi Statement term=NONE
+ hi Todo term=bold,reverse
+ hi Type term=NONE
+ hi Underlined term=underline
+ unlet s:t_Co
+ finish
+endif
+
+" Background: light
+" Color: comment #ff0000 196 red
+" Color: constant #ff00ff 201 magenta
+" Color: identifier #0000ff 21 blue
+" Color: statement #a52a2a 124 darkred
+" Color: preproc #a020f0 129 darkmagenta
+" Color: type #0000ff 21 blue
+" Color: special #ff00ff 201 magenta
+" Color: tag #006400 22 darkgreen
+" Color: fg0 #000000 16 black
+" Color: bg0 #ffffff 231 white
+" Color: bg1 #a9a9a9 248 grey
+" Color: status #a9a9a9 248 darkgrey
+" Color: visual #ffff00 226 yellow
+" Color: folded #d3d3d3 252 black
+" Color: folded_fg #00008b 18 darkblue
+" Color: cursorline #e5e5e5 254 black
+" Color: pmenu #dadada 253 grey
+" Color: wildmenu #ffff00 226 yellow
+" Color: error #ff0000 196 red
+" Color: linenr #a52a2a 124 darkred
+" Color: colorcolumn #eeeeee 255 grey
+" Color: slateblue #6a5acd 62 blue
+" Color: black #000000 16 black
+" Color: darkred #870000 88 darkred
+" Color: darkgreen #008700 28 darkgreen
+" Color: darkyellow #878700 100 darkyellow
+" Color: darkblue #000087 18 darkblue
+" Color: darkmagenta #870087 18 darkmagenta
+" Color: darkcyan #008787 30 darkcyan
+" Color: gray #878787 102 gray
+" Term colors: bg0 statement constant preproc identifier type special bg1
+" Term colors: comment statement constant preproc identifier type special fg0
+" Color: bgDiffA #5F875F 65 darkgreen
+" Color: bgDiffC #5F87AF 67 blue
+" Color: bgDiffD #AF5FAF 133 magenta
+" Color: bgDiffT #C6C6C6 251 grey
+" Color: fgDiffW #FFFFFF 231 white
+" Color: fgDiffB #000000 16 black
+" Color: bgDiffC8 #5F87AF 67 darkblue
+" Color: bgDiffD8 #AF5FAF 133 darkmagenta
+" vim: et ts=2 sw=2
diff --git a/runtime/compiler/icon.vim b/runtime/compiler/icon.vim
new file mode 100644
index 0000000000..40f67930f7
--- /dev/null
+++ b/runtime/compiler/icon.vim
@@ -0,0 +1,33 @@
+" Vim compiler file
+" Compiler: Icon Compiler
+" Maintainer: Doug Kearns <dougkearns@gmail.com>
+" Last Change: 2022 Jun 16
+
+if exists("current_compiler")
+ finish
+endif
+let current_compiler = "icont"
+
+if exists(":CompilerSet") != 2 " older Vim always used :setlocal
+ command -nargs=* CompilerSet setlocal <args>
+endif
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+CompilerSet makeprg=icont\ -s
+CompilerSet errorformat=%-G%\\d%\\+\ errors%\\=,
+ \%ERun-time\ error\ %n,
+ \%ERun-time\ error\ %n\ in\ %m,
+ \%ZTraceback:,
+ \%+Coffending\ value:\ %.%#,
+ \%CFile\ %f;\ Line\ %l,
+ \%EFile\ %f;\ Line\ %l\ #\ %m,
+ \%EFile\ %f;\ %m,
+ \%E%f:%l:\ #\ %m,
+ \%E%f:\ %m,
+ \%+C%.%#,
+ \%-G%.%#
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
diff --git a/runtime/compiler/jest.vim b/runtime/compiler/jest.vim
index fee70b7c55..a4bb549de1 100644
--- a/runtime/compiler/jest.vim
+++ b/runtime/compiler/jest.vim
@@ -1,7 +1,7 @@
" Vim compiler file
" Compiler: Jest
" Maintainer: Doug Kearns <dougkearns@gmail.com>
-" Last Change: 2018 May 15
+" Last Change: 2021 Nov 20
if exists("current_compiler")
finish
@@ -15,12 +15,14 @@ endif
let s:cpo_save = &cpo
set cpo&vim
-" CompilerSet makeprg=npx\ jest\ --no-colors
+" CompilerSet makeprg=npx\ --no-install\ jest\ --no-colors
CompilerSet makeprg=jest\ --no-colors
-CompilerSet errorformat=%E\ \ ●\ %m,
+CompilerSet errorformat=%-A\ \ ●\ Console,
+ \%E\ \ ●\ %m,
\%Z\ %\\{4}%.%#Error:\ %f:\ %m\ (%l:%c):%\\=,
\%Z\ %\\{6}at\ %\\S%#\ (%f:%l:%c),
+ \%Z\ %\\{6}at\ %\\S%#\ %f:%l:%c,
\%+C\ %\\{4}%\\w%.%#,
\%+C\ %\\{4}%[-+]%.%#,
\%-C%.%#,
diff --git a/runtime/compiler/perl.vim b/runtime/compiler/perl.vim
index a857caed8f..21e384fb85 100644
--- a/runtime/compiler/perl.vim
+++ b/runtime/compiler/perl.vim
@@ -1,7 +1,11 @@
-" Vim Compiler File
-" Compiler: Perl syntax checks (perl -Wc)
-" Maintainer: Christian J. Robinson <heptite@gmail.com>
-" Last Change: 2019 Jul 22
+" Vim compiler file
+" Compiler: Perl syntax checks (perl -Wc)
+" Maintainer: vim-perl <vim-perl@googlegroups.com>
+" Author: Christian J. Robinson <heptite@gmail.com>
+" Homepage: https://github.com/vim-perl/vim-perl
+" Bugs/requests: https://github.com/vim-perl/vim-perl/issues
+" License: Vim License (see :help license)
+" Last Change: 2021 Nov 2
if exists("current_compiler")
finish
@@ -15,10 +19,10 @@ endif
let s:savecpo = &cpo
set cpo&vim
-if exists('g:perl_compiler_force_warnings') && g:perl_compiler_force_warnings == 0
- let s:warnopt = 'w'
-else
+if get(g:, 'perl_compiler_force_warnings', 1)
let s:warnopt = 'W'
+else
+ let s:warnopt = 'w'
endif
if getline(1) =~# '-[^ ]*T'
diff --git a/runtime/compiler/perlcritic.vim b/runtime/compiler/perlcritic.vim
new file mode 100644
index 0000000000..cf0678eeb2
--- /dev/null
+++ b/runtime/compiler/perlcritic.vim
@@ -0,0 +1,27 @@
+" Vim compiler file
+" Compiler: perlcritic
+" Maintainer: vim-perl <vim-perl@googlegroups.com>
+" Author: Doug Kearns <dougkearns@gmail.com>
+" Homepage: https://github.com/vim-perl/vim-perl
+" Bugs/requests: https://github.com/vim-perl/vim-perl/issues
+" License: Vim License (see :help license)
+" Last Change: 2021 Oct 20
+
+if exists("current_compiler")
+ finish
+endif
+let current_compiler = "perlcritic"
+
+if exists(":CompilerSet") != 2 " older Vim always used :setlocal
+ command -nargs=* CompilerSet setlocal <args>
+endif
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+CompilerSet makeprg=perlcritic\ --nocolor\ --quiet\ --verbose\ \"\\%f:\\%l:\\%c:\\%s:\\%m\\n\"
+CompilerSet errorformat=%f:%l:%c:%n:%m,
+ \%-G%.%#
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
diff --git a/runtime/compiler/podchecker.vim b/runtime/compiler/podchecker.vim
new file mode 100644
index 0000000000..2cd4e13660
--- /dev/null
+++ b/runtime/compiler/podchecker.vim
@@ -0,0 +1,28 @@
+" Vim compiler file
+" Compiler: podchecker
+" Maintainer: vim-perl <vim-perl@googlegroups.com>
+" Author: Doug Kearns <dougkearns@gmail.com>
+" Homepage: https://github.com/vim-perl/vim-perl
+" Bugs/requests: https://github.com/vim-perl/vim-perl/issues
+" License: Vim License (see :help license)
+" Last Change: 2021 Oct 20
+
+if exists("current_compiler")
+ finish
+endif
+let current_compiler = "podchecker"
+
+if exists(":CompilerSet") != 2 " older Vim always used :setlocal
+ command -nargs=* CompilerSet setlocal <args>
+endif
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+CompilerSet makeprg=podchecker\ -w
+CompilerSet errorformat=\*\*\*\ %tRROR:\ %m\ at\ line\ %l\ in\ file\ %f,
+ \\*\*\*\ %tARNING:\ %m\ at\ line\ %l\ in\ file\ %f,
+ \%-G%.%#
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
diff --git a/runtime/compiler/scdoc.vim b/runtime/compiler/scdoc.vim
index 2f6edc6322..c37969f89c 100644
--- a/runtime/compiler/scdoc.vim
+++ b/runtime/compiler/scdoc.vim
@@ -1,7 +1,8 @@
" scdoc compiler for Vim
" Compiler: scdoc
-" Maintainer: Greg Anders <greg@gpanders.com>
+" Maintainer: Gregory Anders <contact@gpanders.com>
" Last Updated: 2019-10-24
+" Upstream: https://github.com/gpanders/vim-scdoc
if exists('current_compiler')
finish
diff --git a/runtime/compiler/sml.vim b/runtime/compiler/sml.vim
index c7e1b1bf16..a0b13b6c8a 100644
--- a/runtime/compiler/sml.vim
+++ b/runtime/compiler/sml.vim
@@ -1,7 +1,7 @@
" Vim compiler file
" Compiler: SML/NJ Compiler
" Maintainer: Doug Kearns <dougkearns@gmail.com>
-" Last Change: 2020 Feb 10
+" Last Change: 2022 Feb 09
if exists("current_compiler")
finish
@@ -16,10 +16,10 @@ let s:cpo_save = &cpo
set cpo&vim
CompilerSet makeprg=sml
-CompilerSet errorformat=%f:%l.%c-%\\d%\\+.%\\d%\\+\ %trror:\ %m,
+CompilerSet errorformat=%f:%l.%c-%e.%k\ %trror:\ %m,
\%f:%l.%c\ %trror:\ %m,
- \%trror:\ %m
- \%f:%l.%c-%\\d%\\+.%\\d%\\+\ %tarning:\ %m,
+ \%trror:\ %m,
+ \%f:%l.%c-%e.%k\ %tarning:\ %m,
\%f:%l.%c\ %tarning:\ %m,
\%tarning:\ %m,
\%-G%.%#
diff --git a/runtime/delmenu.vim b/runtime/delmenu.vim
index 5c20290152..040cc09aa9 100644
--- a/runtime/delmenu.vim
+++ b/runtime/delmenu.vim
@@ -5,6 +5,7 @@
" Last Change: 2019 Dec 10
aunmenu *
+tlunmenu *
unlet! g:did_install_default_menus
unlet! g:did_install_syntax_menu
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt
index 0daca0de53..c67187d857 100644
--- a/runtime/doc/api.txt
+++ b/runtime/doc/api.txt
@@ -59,7 +59,7 @@ Nvim instance:
# trailing '&' which is required since Nvim won't process events while
# running a blocking command):
#
- # :!./hello.rb &
+ # :!./hello.rb &
#
# Or from another shell by setting NVIM_LISTEN_ADDRESS:
# $ NVIM_LISTEN_ADDRESS=[address] ./hello.rb
@@ -148,9 +148,9 @@ indices, end-inclusive):
Most API functions are "deferred": they are queued on the main loop and
processed sequentially with normal input. So if the editor is waiting for
user input in a "modal" fashion (e.g. the |hit-enter-prompt|), the request
-will block. Non-deferred ({fast}) functions such as |nvim_get_mode()| and
+will block. Non-deferred (fast) functions such as |nvim_get_mode()| and
|nvim_input()| are served immediately (i.e. without waiting in the input
-queue). Lua code can use |vim.in_fast_event()| to detect a {fast} context.
+queue). Lua code can use |vim.in_fast_event()| to detect a fast context.
==============================================================================
API metadata *api-metadata*
@@ -186,7 +186,7 @@ About the `functions` map:
a type name, e.g. `nvim_buf_get_lines` is the `get_lines` method of
a Buffer instance. |dev-api|
- Global functions have the "method=false" flag and are prefixed with just
- `nvim_`, e.g. `nvim_get_buffers`.
+ `nvim_`, e.g. `nvim_list_bufs`.
*api-mapping*
External programs (clients) can use the metadata to discover the API, using
@@ -426,6 +426,14 @@ Two ways to create a floating window:
To close it use |nvim_win_close()| or a command such as |:close|.
+To check whether a window is floating, check whether the `relative` option in
+its config is non-empty: >
+
+ if vim.api.nvim_win_get_config(window_id).relative ~= '' then
+ -- window with this window_id is floating
+ end
+>
+
Buffer text can be highlighted by typical mechanisms (syntax highlighting,
|api-highlights|). The |hl-NormalFloat| group highlights normal text;
'winhighlight' can be used as usual to override groups locally. Floats inherit
@@ -434,6 +442,9 @@ to disable various visual features such as the 'number' column.
Currently, floating windows don't support some widgets like scrollbar.
+The output of |:mksession| does not include commands for restoring floating
+windows.
+
Example: create a float with scratch buffer: >
let buf = nvim_create_buf(v:false, v:true)
@@ -452,7 +463,7 @@ Extended marks (extmarks) represent buffer annotations that track text changes
in the buffer. They can represent cursors, folds, misspelled words, anything
that needs to track a logical location in the buffer over time. |api-indexing|
-Extmark position works like "bar" cursor: it exists between characters. Thus
+Extmark position works like "bar" cursor: it exists between characters. Thus,
the maximum extmark index on a line is 1 more than the character index: >
f o o b a r line contents
@@ -468,7 +479,7 @@ extmark position and enter some text, the extmark migrates forward. >
f o o z|b a r line (| = cursor)
4 extmark (after typing "z")
-If an extmark is on the last index of a line and you inputs a newline at that
+If an extmark is on the last index of a line and you input a newline at that
point, the extmark will accordingly migrate to the next line: >
f o o z b a r| line (| = cursor)
@@ -535,12 +546,12 @@ nvim__get_runtime({pat}, {all}, {*opts}) *nvim__get_runtime()*
Find files in runtime directories
Attributes: ~
- {fast}
+ |api-fast|
Parameters: ~
- {pat} pattern of files to search for
- {all} whether to return all matches or only the first
- {options} is_lua: only search lua subdirs
+ {pat} pattern of files to search for
+ {all} whether to return all matches or only the first
+ {opts} is_lua: only search lua subdirs
Return: ~
list of absolute paths to the found files
@@ -594,7 +605,8 @@ nvim__id_float({flt}) *nvim__id_float()*
its argument.
nvim__inspect_cell({grid}, {row}, {col}) *nvim__inspect_cell()*
- TODO: Documentation
+ NB: if your UI doesn't use hlstate, this will not return
+ hlstate first time.
nvim__runtime_inspect() *nvim__runtime_inspect()*
TODO: Documentation
@@ -603,7 +615,7 @@ nvim__screenshot({path}) *nvim__screenshot()*
TODO: Documentation
Attributes: ~
- {fast}
+ |api-fast|
nvim__set_hl_ns({ns_id}) *nvim__set_hl_ns()*
Set active namespace for highlights.
@@ -615,7 +627,7 @@ nvim__set_hl_ns({ns_id}) *nvim__set_hl_ns()*
cycle.
Attributes: ~
- {fast}
+ |api-fast|
Parameters: ~
{ns_id} the namespace to activate
@@ -626,58 +638,11 @@ nvim__stats() *nvim__stats()*
Return: ~
Map of various internal stats.
- *nvim_add_user_command()*
-nvim_add_user_command({name}, {command}, {*opts})
- Create a new user command |user-commands|
-
- {name} is the name of the new command. The name must begin
- with an uppercase letter.
-
- {command} is the replacement text or Lua function to execute.
-
- Example: >
- :call nvim_add_user_command('SayHello', 'echo "Hello world!"', {})
- :SayHello
- Hello world!
-<
+nvim__unpack({str}) *nvim__unpack()*
+ TODO: Documentation
- Parameters: ~
- {name} Name of the new user command. Must begin with
- an uppercase letter.
- {command} Replacement command to execute when this user
- command is executed. When called from Lua, the
- command can also be a Lua function. The
- function is called with a single table argument
- that contains the following keys:
- • args: (string) The args passed to the
- command, if any |<args>|
- • bang: (boolean) "true" if the command was
- executed with a ! modifier |<bang>|
- • line1: (number) The starting line of the
- command range |<line1>|
- • line2: (number) The final line of the command
- range |<line2>|
- • range: (number) The number of items in the
- command range: 0, 1, or 2 |<range>|
- • count: (number) Any count supplied |<count>|
- • reg: (string) The optional register, if
- specified |<reg>|
- • mods: (string) Command modifiers, if any
- |<mods>|
- {opts} Optional command attributes. See
- |command-attributes| for more details. To use
- boolean attributes (such as |:command-bang| or
- |:command-bar|) set the value to "true". In
- addition to the string options listed in
- |:command-complete|, the "complete" key also
- accepts a Lua function which works like the
- "customlist" completion mode
- |:command-completion-customlist|. Additional
- parameters:
- • desc: (string) Used for listing the command
- when a Lua function is used for {command}.
- • force: (boolean, default true) Override any
- previous definition.
+ Attributes: ~
+ |api-fast|
nvim_call_atomic({calls}) *nvim_call_atomic()*
Calls many API methods atomically.
@@ -691,6 +656,9 @@ nvim_call_atomic({calls}) *nvim_call_atomic()*
2. To minimize RPC overhead (roundtrips) of a sequence of many
requests.
+ Attributes: ~
+ |RPC| only
+
Parameters: ~
{calls} an array of calls, where each call is described
by an array with two elements: the request name,
@@ -706,17 +674,21 @@ nvim_call_atomic({calls}) *nvim_call_atomic()*
be returned.
nvim_chan_send({chan}, {data}) *nvim_chan_send()*
- Send data to channel `id` . For a job, it writes it to the
+ 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
RPC messages, use |vim.rpcnotify()| and |vim.rpcrequest()|
instead.
+ Attributes: ~
+ |RPC| only
+ |vim.api| only
+
Parameters: ~
{chan} id of the channel
{data} data to write. 8-bit clean: can contain NUL bytes.
@@ -767,12 +739,6 @@ nvim_del_mark({name}) *nvim_del_mark()*
|nvim_buf_del_mark()|
|nvim_get_mark()|
-nvim_del_user_command({name}) *nvim_del_user_command()*
- Delete a user-defined command.
-
- Parameters: ~
- {name} Name of the command to delete.
-
nvim_del_var({name}) *nvim_del_var()*
Removes a global (g:) variable.
@@ -812,7 +778,7 @@ nvim_eval_statusline({str}, {*opts}) *nvim_eval_statusline()*
Evaluates statusline string.
Attributes: ~
- {fast}
+ |api-fast|
Parameters: ~
{str} Statusline string (see 'statusline').
@@ -822,10 +788,14 @@ nvim_eval_statusline({str}, {*opts}) *nvim_eval_statusline()*
• maxwidth: (number) Maximum width of statusline.
• fillchar: (string) Character to fill blank
spaces in the statusline (see 'fillchars').
+ Treated as single-width even if it isn't.
• highlights: (boolean) Return highlight
information.
+ • use_winbar: (boolean) Evaluate winbar instead of
+ statusline.
• use_tabline: (boolean) Evaluate tabline instead
of statusline. When |TRUE|, {winid} is ignored.
+ Mutually exclusive with {use_winbar}.
Return: ~
Dictionary containing statusline information, with these
@@ -846,7 +816,10 @@ nvim_exec_lua({code}, {args}) *nvim_exec_lua()*
inside the chunk. The chunk can return a value.
Only statements are executed. To evaluate an expression,
- prefix it with `return` : return my_function(...)
+ prefix it with `return`: return my_function(...)
+
+ Attributes: ~
+ |RPC| only
Parameters: ~
{code} Lua code to execute
@@ -855,7 +828,7 @@ nvim_exec_lua({code}, {args}) *nvim_exec_lua()*
Return: ~
Return value of Lua code if present or NIL.
-nvim_feedkeys({keys}, {mode}, {escape_csi}) *nvim_feedkeys()*
+nvim_feedkeys({keys}, {mode}, {escape_ks}) *nvim_feedkeys()*
Sends input-keys to Nvim, subject to various quirks controlled
by `mode` flags. This is a blocking call, unlike
|nvim_input()|.
@@ -863,32 +836,25 @@ nvim_feedkeys({keys}, {mode}, {escape_csi}) *nvim_feedkeys()*
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
+ (typically with escape_ks=false) to replace |keycodes|, then
pass the result to nvim_feedkeys().
Example: >
: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)
<
Parameters: ~
- {keys} to be typed
- {mode} behavior flags, see |feedkeys()|
- {escape_csi} If true, escape K_SPECIAL/CSI bytes in
- `keys`
+ {keys} to be typed
+ {mode} behavior flags, see |feedkeys()|
+ {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 also: ~
feedkeys()
- vim_strsave_escape_csi
-
-nvim_get_all_options_info() *nvim_get_all_options_info()*
- Gets the option information for all options.
-
- The dictionary has the full option names as keys and option
- metadata dictionaries as detailed at |nvim_get_option_info|.
-
- Return: ~
- dictionary of all options
+ vim_strsave_escape_ks
nvim_get_api_info() *nvim_get_api_info()*
Returns a 2-tuple (Array), where item 0 is the current channel
@@ -898,7 +864,8 @@ nvim_get_api_info() *nvim_get_api_info()*
2-tuple [{channel-id}, {api-metadata}]
Attributes: ~
- {fast}
+ |api-fast|
+ |RPC| only
nvim_get_chan_info({chan}) *nvim_get_chan_info()*
Gets information about a channel.
@@ -922,7 +889,7 @@ nvim_get_chan_info({chan}) *nvim_get_chan_info()*
• "pty" (optional) Name of pseudoterminal. On a POSIX
system this is a device path like "/dev/pts/1". If the
name is unknown, the key will still be present if a pty
- is used (e.g. for winpty on Windows).
+ is used (e.g. for conpty on Windows).
• "buffer" (optional) Buffer with connected |terminal|
instance.
• "client" (optional) Info about the peer (client on the
@@ -953,19 +920,6 @@ nvim_get_color_map() *nvim_get_color_map()*
Return: ~
Map of color names and RGB values.
-nvim_get_commands({*opts}) *nvim_get_commands()*
- Gets a map of global (non-buffer-local) Ex commands.
-
- Currently only |user-commands| are supported, not builtin Ex
- commands.
-
- Parameters: ~
- {opts} Optional parameters. Currently only supports
- {"builtin":false}
-
- Return: ~
- Map of maps describing commands.
-
nvim_get_context({*opts}) *nvim_get_context()*
Gets a map of the current editor state.
@@ -1074,66 +1028,16 @@ nvim_get_mode() *nvim_get_mode()*
Dictionary { "mode": String, "blocking": Boolean }
Attributes: ~
- {fast}
-
-nvim_get_option({name}) *nvim_get_option()*
- Gets the global value of an option.
-
- Parameters: ~
- {name} Option name
-
- Return: ~
- Option value (global)
-
-nvim_get_option_info({name}) *nvim_get_option_info()*
- Gets the option information for one option
-
- Resulting dictionary has keys:
- • name: Name of the option (like 'filetype')
- • shortname: Shortened name of the option (like 'ft')
- • type: type of option ("string", "number" or "boolean")
- • default: The default value for the option
- • was_set: Whether the option was set.
- • last_set_sid: Last set script id (if any)
- • last_set_linenr: line number where option was set
- • last_set_chan: Channel where option was set (0 for local)
- • scope: one of "global", "win", or "buf"
- • global_local: whether win or buf option has a global value
- • commalist: List of comma separated values
- • flaglist: List of single char flags
-
- Parameters: ~
- {name} Option name
-
- Return: ~
- Option Information
-
-nvim_get_option_value({name}, {*opts}) *nvim_get_option_value()*
- Gets the value of an option. The behavior of this function
- matches that of |:set|: the local value of an option is
- returned if it exists; otherwise, the global value is
- returned. Local values always correspond to the current buffer
- or window. To get a buffer-local or window-local option for a
- specific buffer or window, use |nvim_buf_get_option()| or
- |nvim_win_get_option()|.
-
- Parameters: ~
- {name} Option name
- {opts} Optional parameters
- • scope: One of 'global' or 'local'. Analogous to
- |:setglobal| and |:setlocal|, respectively.
-
- Return: ~
- Option value
+ |api-fast|
nvim_get_proc({pid}) *nvim_get_proc()*
- Gets info describing process `pid` .
+ Gets info describing process `pid`.
Return: ~
Map of process properties, or NIL if process not found.
nvim_get_proc_children({pid}) *nvim_get_proc_children()*
- Gets the immediate children of process `pid` .
+ Gets the immediate children of process `pid`.
Return: ~
Array of child process ids, empty if process not found.
@@ -1150,7 +1054,7 @@ nvim_get_runtime_file({name}, {all}) *nvim_get_runtime_file()*
returned then.
Attributes: ~
- {fast}
+ |api-fast|
Parameters: ~
{name} pattern of files to search for
@@ -1194,7 +1098,7 @@ nvim_input({keys}) *nvim_input()*
|api-level| 6.
Attributes: ~
- {fast}
+ |api-fast|
Parameters: ~
{keys} to be typed
@@ -1219,7 +1123,7 @@ nvim_input_mouse({button}, {action}, {modifier}, {grid}, {row}, {col})
|nvim_input()| has the same limitation.
Attributes: ~
- {fast}
+ |api-fast|
Parameters: ~
{button} Mouse button: one of "left", "right",
@@ -1242,8 +1146,8 @@ nvim_input_mouse({button}, {action}, {modifier}, {grid}, {row}, {col})
nvim_list_bufs() *nvim_list_bufs()*
Gets the current list of buffer handles
- Includes unlisted (unloaded/deleted) buffers, like `:ls!` .
- Use |nvim_buf_is_loaded()| to check if a buffer is loaded.
+ Includes unlisted (unloaded/deleted) buffers, like `:ls!`. Use
+ |nvim_buf_is_loaded()| to check if a buffer is loaded.
Return: ~
List of buffer handles
@@ -1352,7 +1256,7 @@ nvim_paste({data}, {crlf}, {phase}) *nvim_paste()*
Errors ('nomodifiable', `vim.paste()` failure, …) are
reflected in `err` but do not affect the return value (which
- is strictly decided by `vim.paste()` ). On error, subsequent
+ is strictly decided by `vim.paste()`). On error, subsequent
calls are ignored ("drained") until the next paste is
initiated (phase 1 or -1).
@@ -1405,7 +1309,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: ~
@@ -1429,7 +1333,7 @@ nvim_select_popupmenu_item({item}, {insert}, {finish}, {opts})
{insert} Whether the selection should be inserted in the
buffer.
{finish} Finish the completion and dismiss the popupmenu.
- Implies `insert` .
+ Implies `insert`.
{opts} Optional parameters. Reserved for future use.
*nvim_set_client_info()*
@@ -1448,6 +1352,9 @@ nvim_set_client_info({name}, {version}, {type}, {methods}, {attributes})
"Something is better than nothing". You don't need to
include all the fields.
+ Attributes: ~
+ |RPC| only
+
Parameters: ~
{name} Short name for the connected client
{version} Dictionary describing the version, with
@@ -1538,25 +1445,58 @@ nvim_set_current_win({window}) *nvim_set_current_win()*
Parameters: ~
{window} Window handle
-nvim_set_hl({ns_id}, {name}, {val}) *nvim_set_hl()*
- Set a highlight group.
+nvim_set_hl({ns_id}, {name}, {*val}) *nvim_set_hl()*
+ Sets a highlight group.
- TODO: ns_id = 0, should modify :highlight namespace TODO val
- should take update vs reset flag
+ Note:
+ Unlike the `:highlight` command which can update a
+ highlight group, this function completely replaces the
+ definition. For example: `nvim_set_hl(0, 'Visual', {})`
+ will clear the highlight group 'Visual'.
- Parameters: ~
- {ns_id} number of namespace for this highlight
- {name} highlight group name, like ErrorMsg
- {val} highlight definition map, like
- |nvim_get_hl_by_name|. in addition the following
- keys are also recognized: `default` : don't
- override existing definition, like `hi default`
- `ctermfg` : sets foreground of cterm color
- `ctermbg` : sets background of cterm color
- `cterm` : cterm attribute map. sets attributed
- for cterm colors. similer to `hi cterm` Note: by
- default cterm attributes are same as attributes
- of gui color
+ Note:
+ The fg and bg keys also accept the string values `"fg"` or
+ `"bg"` which act as aliases to the corresponding
+ foreground and background values of the Normal group. If
+ the Normal group has not been defined, using these values
+ results in an error.
+
+ Parameters: ~
+ {ns_id} Namespace id for this highlight
+ |nvim_create_namespace()|. Use 0 to set a
+ highlight group globally |:highlight|.
+ {name} Highlight group name, e.g. "ErrorMsg"
+ {val} Highlight definition map, accepts the following
+ keys:
+ • fg (or foreground): color name or "#RRGGBB",
+ see note.
+ • bg (or background): color name or "#RRGGBB",
+ see note.
+ • sp (or special): color name or "#RRGGBB"
+ • blend: integer between 0 and 100
+ • bold: boolean
+ • standout: boolean
+ • underline: boolean
+ • undercurl: boolean
+ • underdouble: boolean
+ • underdotted: boolean
+ • underdashed: boolean
+ • strikethrough: boolean
+ • italic: boolean
+ • reverse: boolean
+ • nocombine: boolean
+ • link: name of another highlight group to link
+ to, see |:hi-link|.
+ • default: Don't override existing definition
+ |:hi-default|
+ • ctermfg: Sets foreground of cterm color
+ |highlight-ctermfg|
+ • ctermbg: Sets background of cterm color
+ |highlight-ctermbg|
+ • cterm: cterm attribute map, like
+ |highlight-args|. If not set, cterm attributes
+ will match those from the attribute map
+ documented above.
nvim_set_keymap({mode}, {lhs}, {rhs}, {*opts}) *nvim_set_keymap()*
Sets a global |mapping| for the given mode.
@@ -1581,34 +1521,15 @@ nvim_set_keymap({mode}, {lhs}, {rhs}, {*opts}) *nvim_set_keymap()*
for |:map|.
{lhs} Left-hand-side |{lhs}| of the mapping.
{rhs} Right-hand-side |{rhs}| of the mapping.
- {opts} Optional parameters map. Accepts all
- |:map-arguments| as keys excluding |<buffer>| but
- including |noremap| and "desc". |desc| can be used
- to give a description to keymap. When called from
- Lua, also accepts a "callback" key that takes a
- Lua function to call when the mapping is executed.
- Values are Booleans. Unknown key is an error.
-
-nvim_set_option({name}, {value}) *nvim_set_option()*
- Sets the global value of an option.
-
- Parameters: ~
- {name} Option name
- {value} New option value
-
- *nvim_set_option_value()*
-nvim_set_option_value({name}, {value}, {*opts})
- Sets the value of an option. The behavior of this function
- matches that of |:set|: for global-local options, both the
- global and local value are set unless otherwise specified with
- {scope}.
-
- Parameters: ~
- {name} Option name
- {value} New option value
- {opts} Optional parameters
- • scope: One of 'global' or 'local'. Analogous to
- |:setglobal| and |:setlocal|, respectively.
+ {opts} Optional parameters map: keys are
+ |:map-arguments|, values are booleans (default
+ false). Accepts all |:map-arguments| as keys
+ excluding |<buffer>| but including |noremap| and
+ "desc". Unknown key is an error. "desc" can be
+ used to give a description to the mapping. When
+ called from Lua, also accepts a "callback" key
+ that takes a Lua function to call when the mapping
+ is executed.
nvim_set_var({name}, {value}) *nvim_set_var()*
Sets a global (g:) variable.
@@ -1625,8 +1546,8 @@ nvim_set_vvar({name}, {value}) *nvim_set_vvar()*
{value} Variable value
nvim_strwidth({text}) *nvim_strwidth()*
- Calculates the number of display cells occupied by `text` .
- <Tab> counts as one cell.
+ Calculates the number of display cells occupied by `text`.
+ Control characters including <Tab> count as one cell.
Parameters: ~
{text} Some text
@@ -1637,12 +1558,18 @@ nvim_strwidth({text}) *nvim_strwidth()*
nvim_subscribe({event}) *nvim_subscribe()*
Subscribes to event broadcasts.
+ Attributes: ~
+ |RPC| only
+
Parameters: ~
{event} Event type string
nvim_unsubscribe({event}) *nvim_unsubscribe()*
Unsubscribes to event broadcasts.
+ Attributes: ~
+ |RPC| only
+
Parameters: ~
{event} Event type string
@@ -1654,8 +1581,7 @@ Vimscript Functions *api-vimscript*
nvim_call_dict_function({dict}, {fn}, {args})
Calls a VimL |Dictionary-function| with the given arguments.
- On execution error: fails with VimL error, does not update
- v:errmsg.
+ On execution error: fails with VimL error, updates v:errmsg.
Parameters: ~
{dict} Dictionary, or String evaluating to a VimL |self|
@@ -1669,8 +1595,7 @@ nvim_call_dict_function({dict}, {fn}, {args})
nvim_call_function({fn}, {args}) *nvim_call_function()*
Calls a VimL function with the given arguments.
- On execution error: fails with VimL error, does not update
- v:errmsg.
+ On execution error: fails with VimL error, updates v:errmsg.
Parameters: ~
{fn} Function to call
@@ -1680,23 +1605,25 @@ nvim_call_function({fn}, {args}) *nvim_call_function()*
Result of the function call
nvim_command({command}) *nvim_command()*
- Executes an ex-command.
+ Executes an Ex command.
- On execution error: fails with VimL error, does not update
- v:errmsg.
+ On execution error: fails with VimL error, updates v:errmsg.
- Parameters: ~
- {command} Ex-command string
+ Prefer using |nvim_cmd()| or |nvim_exec()| over this. To
+ evaluate multiple lines of Vim script or an Ex command
+ directly, use |nvim_exec()|. To construct an Ex command using
+ a structured format and then execute it, use |nvim_cmd()|. To
+ modify an Ex command before evaluating it, use
+ |nvim_parse_cmd()| in conjunction with |nvim_cmd()|.
- See also: ~
- |nvim_exec()|
+ Parameters: ~
+ {command} Ex command string
nvim_eval({expr}) *nvim_eval()*
Evaluates a VimL |expression|. Dictionaries and Lists are
recursively expanded.
- On execution error: fails with VimL error, does not update
- v:errmsg.
+ On execution error: fails with VimL error, updates v:errmsg.
Parameters: ~
{expr} VimL expression string
@@ -1705,14 +1632,13 @@ nvim_eval({expr}) *nvim_eval()*
Evaluation result or expanded object
nvim_exec({src}, {output}) *nvim_exec()*
- Executes Vimscript (multiline block of Ex-commands), like
+ Executes Vimscript (multiline block of Ex commands), like
anonymous |:source|.
Unlike |nvim_command()| this function supports heredocs,
script-scope (s:), etc.
- On execution error: fails with VimL error, does not update
- v:errmsg.
+ On execution error: fails with VimL error, updates v:errmsg.
Parameters: ~
{src} Vimscript code
@@ -1726,13 +1652,14 @@ nvim_exec({src}, {output}) *nvim_exec()*
See also: ~
|execute()|
|nvim_command()|
+ |nvim_cmd()|
*nvim_parse_expression()*
nvim_parse_expression({expr}, {flags}, {highlight})
Parse a VimL expression.
Attributes: ~
- {fast}
+ |api-fast|
Parameters: ~
{expr} Expression to parse. Always treated as a
@@ -1827,6 +1754,358 @@ nvim_parse_expression({expr}, {flags}, {highlight})
==============================================================================
+Command Functions *api-command*
+
+ *nvim_buf_create_user_command()*
+nvim_buf_create_user_command({buffer}, {name}, {command}, {*opts})
+ Create a new user command |user-commands| in the given buffer.
+
+ Parameters: ~
+ {buffer} Buffer handle, or 0 for current buffer.
+
+ See also: ~
+ nvim_create_user_command
+
+ *nvim_buf_del_user_command()*
+nvim_buf_del_user_command({buffer}, {name})
+ Delete a buffer-local user-defined command.
+
+ Only commands created with |:command-buffer| or
+ |nvim_buf_create_user_command()| can be deleted with this
+ function.
+
+ Parameters: ~
+ {buffer} Buffer handle, or 0 for current buffer.
+ {name} Name of the command to delete.
+
+nvim_buf_get_commands({buffer}, {*opts}) *nvim_buf_get_commands()*
+ Gets a map of buffer-local |user-commands|.
+
+ Parameters: ~
+ {buffer} Buffer handle, or 0 for current buffer
+ {opts} Optional parameters. Currently not used.
+
+ Return: ~
+ Map of maps describing commands.
+
+nvim_cmd({*cmd}, {*opts}) *nvim_cmd()*
+ Executes an Ex command.
+
+ Unlike |nvim_command()| this command takes a structured
+ Dictionary instead of a String. This allows for easier
+ construction and manipulation of an Ex command. This also
+ allows for things such as having spaces inside a command
+ argument, expanding filenames in a command that otherwise
+ doesn't expand filenames, etc.
+
+ On execution error: fails with VimL error, updates v:errmsg.
+
+ Parameters: ~
+ {cmd} Command to execute. Must be a Dictionary that can
+ contain the same values as the return value of
+ |nvim_parse_cmd()| except "addr", "nargs" and
+ "nextcmd" which are ignored if provided. All
+ values except for "cmd" are optional.
+ {opts} Optional parameters.
+ • output: (boolean, default false) Whether to
+ return command output.
+
+ Return: ~
+ Command output (non-error, non-shell |:!|) if `output` is
+ true, else empty string.
+
+ See also: ~
+ |nvim_exec()|
+ |nvim_command()|
+
+ *nvim_create_user_command()*
+nvim_create_user_command({name}, {command}, {*opts})
+ Create a new user command |user-commands|
+
+ {name} is the name of the new command. The name must begin
+ with an uppercase letter.
+
+ {command} is the replacement text or Lua function to execute.
+
+ Example: >
+ :call nvim_create_user_command('SayHello', 'echo "Hello world!"', {})
+ :SayHello
+ Hello world!
+<
+
+ Parameters: ~
+ {name} Name of the new user command. Must begin with
+ an uppercase letter.
+ {command} Replacement command to execute when this user
+ command is executed. When called from Lua, the
+ command can also be a Lua function. The
+ function is called with a single table argument
+ that contains the following keys:
+ • args: (string) The args passed to the
+ command, if any |<args>|
+ • fargs: (table) The args split by unescaped
+ whitespace (when more than one argument is
+ allowed), if any |<f-args>|
+ • bang: (boolean) "true" if the command was
+ executed with a ! modifier |<bang>|
+ • line1: (number) The starting line of the
+ command range |<line1>|
+ • line2: (number) The final line of the command
+ range |<line2>|
+ • range: (number) The number of items in the
+ command range: 0, 1, or 2 |<range>|
+ • count: (number) Any count supplied |<count>|
+ • reg: (string) The optional register, if
+ specified |<reg>|
+ • mods: (string) Command modifiers, if any
+ |<mods>|
+ • smods: (table) Command modifiers in a
+ structured format. Has the same structure as
+ the "mods" key of |nvim_parse_cmd()|.
+ {opts} Optional command attributes. See
+ |command-attributes| for more details. To use
+ boolean attributes (such as |:command-bang| or
+ |:command-bar|) set the value to "true". In
+ addition to the string options listed in
+ |:command-complete|, the "complete" key also
+ accepts a Lua function which works like the
+ "customlist" completion mode
+ |:command-completion-customlist|. Additional
+ parameters:
+ • desc: (string) Used for listing the command
+ when a Lua function is used for {command}.
+ • force: (boolean, default true) Override any
+ previous definition.
+ • preview: (function) Preview callback for
+ 'inccommand' |:command-preview|
+
+nvim_del_user_command({name}) *nvim_del_user_command()*
+ Delete a user-defined command.
+
+ Parameters: ~
+ {name} Name of the command to delete.
+
+nvim_get_commands({*opts}) *nvim_get_commands()*
+ Gets a map of global (non-buffer-local) Ex commands.
+
+ Currently only |user-commands| are supported, not builtin Ex
+ commands.
+
+ Parameters: ~
+ {opts} Optional parameters. Currently only supports
+ {"builtin":false}
+
+ Return: ~
+ Map of maps describing commands.
+
+nvim_parse_cmd({str}, {opts}) *nvim_parse_cmd()*
+ Parse command line.
+
+ Doesn't check the validity of command arguments.
+
+ Attributes: ~
+ |api-fast|
+
+ Parameters: ~
+ {str} Command line string to parse. Cannot contain "\n".
+ {opts} Optional parameters. Reserved for future use.
+
+ Return: ~
+ Dictionary containing command information, with these
+ keys:
+ • cmd: (string) Command name.
+ • range: (array) Command <range>. Can have 0-2 elements
+ depending on how many items the range contains. Has no
+ elements if command doesn't accept a range or if no
+ range was specified, one element if only a single range
+ item was specified and two elements if both range items
+ were specified.
+ • count: (number) Any |<count>| that was supplied to the
+ command. -1 if command cannot take a count.
+ • reg: (number) The optional command |<register>|, if
+ specified. Empty string if not specified or if command
+ cannot take a register.
+ • bang: (boolean) Whether command contains a |<bang>| (!)
+ modifier.
+ • args: (array) Command arguments.
+ • addr: (string) Value of |:command-addr|. Uses short
+ name.
+ • nargs: (string) Value of |:command-nargs|.
+ • nextcmd: (string) Next command if there are multiple
+ commands separated by a |:bar|. Empty if there isn't a
+ next command.
+ • magic: (dictionary) Which characters have special
+ meaning in the command arguments.
+ • file: (boolean) The command expands filenames. Which
+ means characters such as "%", "#" and wildcards are
+ expanded.
+ • bar: (boolean) The "|" character is treated as a
+ command separator and the double quote character (")
+ is treated as the start of a comment.
+
+ • mods: (dictionary) |:command-modifiers|.
+ • filter: (dictionary) |:filter|.
+ • pattern: (string) Filter pattern. Empty string if
+ there is no filter.
+ • force: (boolean) Whether filter is inverted or not.
+
+ • silent: (boolean) |:silent|.
+ • emsg_silent: (boolean) |:silent!|.
+ • unsilent: (boolean) |:unsilent|.
+ • sandbox: (boolean) |:sandbox|.
+ • noautocmd: (boolean) |:noautocmd|.
+ • browse: (boolean) |:browse|.
+ • confirm: (boolean) |:confirm|.
+ • hide: (boolean) |:hide|.
+ • keepalt: (boolean) |:keepalt|.
+ • keepjumps: (boolean) |:keepjumps|.
+ • keepmarks: (boolean) |:keepmarks|.
+ • keeppatterns: (boolean) |:keeppatterns|.
+ • lockmarks: (boolean) |:lockmarks|.
+ • noswapfile: (boolean) |:noswapfile|.
+ • tab: (integer) |:tab|.
+ • verbose: (integer) |:verbose|. -1 when omitted.
+ • vertical: (boolean) |:vertical|.
+ • split: (string) Split modifier string, is an empty
+ string when there's no split modifier. If there is a
+ split modifier it can be one of:
+ • "aboveleft": |:aboveleft|.
+ • "belowright": |:belowright|.
+ • "topleft": |:topleft|.
+ • "botright": |:botright|.
+
+
+==============================================================================
+Options Functions *api-options*
+
+nvim_buf_get_option({buffer}, {name}) *nvim_buf_get_option()*
+ Gets a buffer option value
+
+ Parameters: ~
+ {buffer} Buffer handle, or 0 for current buffer
+ {name} Option name
+
+ Return: ~
+ Option value
+
+nvim_buf_set_option({buffer}, {name}, {value}) *nvim_buf_set_option()*
+ Sets a buffer option value. Passing 'nil' as value deletes the
+ option (only works if there's a global fallback)
+
+ Parameters: ~
+ {buffer} Buffer handle, or 0 for current buffer
+ {name} Option name
+ {value} Option value
+
+nvim_get_all_options_info() *nvim_get_all_options_info()*
+ Gets the option information for all options.
+
+ The dictionary has the full option names as keys and option
+ metadata dictionaries as detailed at |nvim_get_option_info|.
+
+ Return: ~
+ dictionary of all options
+
+nvim_get_option({name}) *nvim_get_option()*
+ Gets the global value of an option.
+
+ Parameters: ~
+ {name} Option name
+
+ Return: ~
+ Option value (global)
+
+nvim_get_option_info({name}) *nvim_get_option_info()*
+ Gets the option information for one option
+
+ Resulting dictionary has keys:
+ • name: Name of the option (like 'filetype')
+ • shortname: Shortened name of the option (like 'ft')
+ • type: type of option ("string", "number" or "boolean")
+ • default: The default value for the option
+ • was_set: Whether the option was set.
+ • last_set_sid: Last set script id (if any)
+ • last_set_linenr: line number where option was set
+ • last_set_chan: Channel where option was set (0 for local)
+ • scope: one of "global", "win", or "buf"
+ • global_local: whether win or buf option has a global value
+ • commalist: List of comma separated values
+ • flaglist: List of single char flags
+
+ Parameters: ~
+ {name} Option name
+
+ Return: ~
+ Option Information
+
+nvim_get_option_value({name}, {*opts}) *nvim_get_option_value()*
+ Gets the value of an option. The behavior of this function
+ matches that of |:set|: the local value of an option is
+ returned if it exists; otherwise, the global value is
+ returned. Local values always correspond to the current buffer
+ or window, unless "buf" or "win" is set in {opts}.
+
+ Parameters: ~
+ {name} Option name
+ {opts} Optional parameters
+ • scope: One of "global" or "local". Analogous to
+ |:setglobal| and |:setlocal|, respectively.
+ • win: |window-ID|. Used for getting window local
+ options.
+ • buf: Buffer number. Used for getting buffer
+ local options. Implies {scope} is "local".
+
+ Return: ~
+ Option value
+
+nvim_set_option({name}, {value}) *nvim_set_option()*
+ Sets the global value of an option.
+
+ Parameters: ~
+ {name} Option name
+ {value} New option value
+
+ *nvim_set_option_value()*
+nvim_set_option_value({name}, {value}, {*opts})
+ Sets the value of an option. The behavior of this function
+ matches that of |:set|: for global-local options, both the
+ global and local value are set unless otherwise specified with
+ {scope}.
+
+ Note the options {win} and {buf} cannot be used together.
+
+ Parameters: ~
+ {name} Option name
+ {value} New option value
+ {opts} Optional parameters
+ • scope: One of 'global' or 'local'. Analogous to
+ |:setglobal| and |:setlocal|, respectively.
+ • win: |window-ID|. Used for setting window local
+ option.
+ • buf: Buffer number. Used for setting buffer
+ local option.
+
+nvim_win_get_option({window}, {name}) *nvim_win_get_option()*
+ Gets a window option value
+
+ Parameters: ~
+ {window} Window handle, or 0 for current window
+ {name} Option name
+
+ Return: ~
+ Option value
+
+nvim_win_set_option({window}, {name}, {value}) *nvim_win_set_option()*
+ Sets a window option value. Passing 'nil' as value deletes the
+ option(only works if there's a global fallback)
+
+ Parameters: ~
+ {window} Window handle, or 0 for current window
+ {name} Option name
+ {value} Option value
+
+
+==============================================================================
Buffer Functions *api-buffer*
@@ -1852,16 +2131,6 @@ nvim__buf_redraw_range({buffer}, {first}, {last})
nvim__buf_stats({buffer}) *nvim__buf_stats()*
TODO: Documentation
- *nvim_buf_add_user_command()*
-nvim_buf_add_user_command({buffer}, {name}, {command}, {*opts})
- Create a new user command |user-commands| in the given buffer.
-
- Parameters: ~
- {buffer} Buffer handle, or 0 for current buffer.
-
- See also: ~
- nvim_add_user_command
-
nvim_buf_attach({buffer}, {send_buffer}, {opts}) *nvim_buf_attach()*
Activates buffer-update events on a channel, or as Lua
callbacks.
@@ -1877,9 +2146,10 @@ nvim_buf_attach({buffer}, {send_buffer}, {opts}) *nvim_buf_attach()*
{buffer} Buffer handle, or 0 for current buffer
{send_buffer} True if the initial notification should
contain the whole buffer: first
- notification will be `nvim_buf_lines_event`
- . Else the first notification will be
- `nvim_buf_changedtick_event` . Not for Lua
+ notification will be
+ `nvim_buf_lines_event`. Else the first
+ notification will be
+ `nvim_buf_changedtick_event`. Not for Lua
callbacks.
{opts} Optional parameters.
• on_lines: Lua callback invoked on change.
@@ -1930,12 +2200,12 @@ nvim_buf_attach({buffer}, {send_buffer}, {opts}) *nvim_buf_attach()*
• on_reload: Lua callback invoked on
reload. The entire buffer content should
be considered changed. Args:
- • the string "detach"
+ • the string "reload"
• buffer handle
• utf_sizes: include UTF-32 and UTF-16 size
of the replaced region, as args to
- `on_lines` .
+ `on_lines`.
• preview: also attach to command preview
(i.e. 'inccommand') events.
@@ -1961,6 +2231,9 @@ nvim_buf_call({buffer}, {fun}) *nvim_buf_call()*
This is useful e.g. to call vimL functions that only work with
the current buffer/window currently, like |termopen()|.
+ Attributes: ~
+ |vim.api| only
+
Parameters: ~
{buffer} Buffer handle, or 0 for current buffer
{fun} Function to call inside the buffer (currently
@@ -1997,18 +2270,6 @@ nvim_buf_del_mark({buffer}, {name}) *nvim_buf_del_mark()*
|nvim_buf_set_mark()|
|nvim_del_mark()|
- *nvim_buf_del_user_command()*
-nvim_buf_del_user_command({buffer}, {name})
- Delete a buffer-local user-defined command.
-
- Only commands created with |:command-buffer| or
- |nvim_buf_add_user_command()| can be deleted with this
- function.
-
- Parameters: ~
- {buffer} Buffer handle, or 0 for current buffer.
- {name} Name of the command to delete.
-
nvim_buf_del_var({buffer}, {name}) *nvim_buf_del_var()*
Removes a buffer-scoped (b:) variable
@@ -2033,6 +2294,9 @@ nvim_buf_delete({buffer}, {opts}) *nvim_buf_delete()*
nvim_buf_detach({buffer}) *nvim_buf_detach()*
Deactivates buffer-update events on the channel.
+ Attributes: ~
+ |RPC| only
+
Parameters: ~
{buffer} Buffer handle, or 0 for current buffer
@@ -2053,16 +2317,6 @@ nvim_buf_get_changedtick({buffer}) *nvim_buf_get_changedtick()*
Return: ~
`b:changedtick` value.
-nvim_buf_get_commands({buffer}, {*opts}) *nvim_buf_get_commands()*
- Gets a map of buffer-local |user-commands|.
-
- Parameters: ~
- {buffer} Buffer handle, or 0 for current buffer
- {opts} Optional parameters. Currently not used.
-
- Return: ~
- Map of maps describing commands.
-
nvim_buf_get_keymap({buffer}, {mode}) *nvim_buf_get_keymap()*
Gets a list of buffer-local |mapping| definitions.
@@ -2088,7 +2342,7 @@ nvim_buf_get_lines({buffer}, {start}, {end}, {strict_indexing})
Parameters: ~
{buffer} Buffer handle, or 0 for current buffer
{start} First line index
- {end} Last line index (exclusive)
+ {end} Last line index, exclusive
{strict_indexing} Whether out-of-bounds should be an
error.
@@ -2141,15 +2395,30 @@ nvim_buf_get_offset({buffer}, {index}) *nvim_buf_get_offset()*
Return: ~
Integer byte offset, or -1 for unloaded buffer.
-nvim_buf_get_option({buffer}, {name}) *nvim_buf_get_option()*
- Gets a buffer option value
+ *nvim_buf_get_text()*
+nvim_buf_get_text({buffer}, {start_row}, {start_col}, {end_row}, {end_col},
+ {opts})
+ Gets a range from the buffer.
+
+ This differs from |nvim_buf_get_lines()| in that it allows
+ retrieving only portions of a line.
+
+ Indexing is zero-based. Row indices are end-inclusive, and
+ column indices are end-exclusive.
+
+ Prefer |nvim_buf_get_lines()| when retrieving entire lines.
Parameters: ~
- {buffer} Buffer handle, or 0 for current buffer
- {name} Option name
+ {buffer} Buffer handle, or 0 for current buffer
+ {start_row} First line index
+ {start_col} Starting column (byte offset) on first line
+ {end_row} Last line index, inclusive
+ {end_col} Ending column (byte offset) on last line,
+ exclusive
+ {opts} Optional parameters. Currently unused.
Return: ~
- Option value
+ Array of lines, or empty array for unloaded buffer.
nvim_buf_get_var({buffer}, {name}) *nvim_buf_get_var()*
Gets a buffer-scoped (b:) variable.
@@ -2185,7 +2454,7 @@ nvim_buf_is_valid({buffer}) *nvim_buf_is_valid()*
true if the buffer is valid, false otherwise.
nvim_buf_line_count({buffer}) *nvim_buf_line_count()*
- Gets the buffer line count
+ Returns the number of lines in the given buffer.
Parameters: ~
{buffer} Buffer handle, or 0 for current buffer
@@ -2225,7 +2494,7 @@ nvim_buf_set_lines({buffer}, {start}, {end}, {strict_indexing}, {replacement})
Parameters: ~
{buffer} Buffer handle, or 0 for current buffer
{start} First line index
- {end} Last line index (exclusive)
+ {end} Last line index, exclusive
{strict_indexing} Whether out-of-bounds should be an
error.
{replacement} Array of lines to use as replacement
@@ -2261,40 +2530,33 @@ nvim_buf_set_name({buffer}, {name}) *nvim_buf_set_name()*
{buffer} Buffer handle, or 0 for current buffer
{name} Buffer name
-nvim_buf_set_option({buffer}, {name}, {value}) *nvim_buf_set_option()*
- Sets a buffer option value. Passing 'nil' as value deletes the
- option (only works if there's a global fallback)
-
- Parameters: ~
- {buffer} Buffer handle, or 0 for current buffer
- {name} Option name
- {value} Option value
-
*nvim_buf_set_text()*
nvim_buf_set_text({buffer}, {start_row}, {start_col}, {end_row}, {end_col},
{replacement})
Sets (replaces) a range in the buffer
- This is recommended over nvim_buf_set_lines when only
+ This is recommended over |nvim_buf_set_lines()| when only
modifying parts of a line, as extmarks will be preserved on
non-modified parts of the touched lines.
- Indexing is zero-based and end-exclusive.
+ Indexing is zero-based. Row indices are end-inclusive, and
+ column indices are end-exclusive.
- To insert text at a given index, set `start` and `end` ranges
- to the same index. To delete a range, set `replacement` to an
- array containing an empty string, or simply an empty array.
+ To insert text at a given `(row, column)` location, use
+ `start_row = end_row = row` and `start_col = end_col = col`.
+ To delete the text in a range, use `replacement = {}`.
- Prefer nvim_buf_set_lines when adding or deleting entire lines
- only.
+ Prefer |nvim_buf_set_lines()| if you are only adding or
+ deleting entire lines.
Parameters: ~
- {buffer} Buffer handle, or 0 for current buffer
- {start_row} First line index
- {start_column} First column
- {end_row} Last line index
- {end_column} Last column
- {replacement} Array of lines to use as replacement
+ {buffer} Buffer handle, or 0 for current buffer
+ {start_row} First line index
+ {start_col} Starting column (byte offset) on first line
+ {end_row} Last line index, inclusive
+ {end_col} Ending column (byte offset) on last line,
+ exclusive
+ {replacement} Array of lines to use as replacement
nvim_buf_set_var({buffer}, {name}, {value}) *nvim_buf_set_var()*
Sets a buffer-scoped (b:) variable
@@ -2327,7 +2589,7 @@ nvim_buf_add_highlight({buffer}, {ns_id}, {hl_group}, {line}, {col_start},
namespace. All highlights in the same namespace can then be
cleared with single call to |nvim_buf_clear_namespace()|. If
the highlight never will be deleted by an API call, pass
- `ns_id = -1` .
+ `ns_id = -1`.
As a shorthand, `ns_id = 0` can be used to create a new
namespace for the highlight, the allocated id is then
@@ -2407,8 +2669,8 @@ nvim_buf_get_extmarks({buffer}, {ns_id}, {start}, {end}, {opts})
nvim_buf_get_extmarks(0, my_ns, [0,0], [-1,-1], {})
<
- If `end` is less than `start` , traversal works backwards.
- (Useful with `limit` , to get the first marks prior to a given
+ If `end` is less than `start`, traversal works backwards.
+ (Useful with `limit`, to get the first marks prior to a given
position.)
Example:
@@ -2417,9 +2679,9 @@ nvim_buf_get_extmarks({buffer}, {ns_id}, {start}, {end}, {opts})
local pos = a.nvim_win_get_cursor(0)
local ns = a.nvim_create_namespace('my-plugin')
-- Create new extmark at line 1, column 1.
- local m1 = a.nvim_buf_set_extmark(0, ns, 0, 0, 0, {})
+ local m1 = a.nvim_buf_set_extmark(0, ns, 0, 0, {})
-- Create new extmark at line 3, column 1.
- local m2 = a.nvim_buf_set_extmark(0, ns, 0, 2, 0, {})
+ local m2 = a.nvim_buf_set_extmark(0, ns, 0, 2, {})
-- Get extmarks only from line 3.
local ms = a.nvim_buf_get_extmarks(0, ns, {2,0}, {2,0}, {})
-- Get all marks in this buffer + namespace.
@@ -2448,13 +2710,12 @@ nvim_buf_get_extmarks({buffer}, {ns_id}, {start}, {end}, {opts})
nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {*opts})
Creates or updates an extmark.
- To create a new extmark, pass id=0. The extmark id will be
- returned. To move an existing mark, pass its id.
-
- It is also allowed to create a new mark by passing in a
- previously unused id, but the caller must then keep track of
- existing and unused ids itself. (Useful over RPC, to avoid
- waiting for the return value.)
+ By default a new extmark is created when no id is passed in,
+ but it is also possible to create a new mark by passing in a
+ previously unused id or move an existing mark by passing in
+ its id. The caller must then keep track of existing and unused
+ ids itself. (Useful over RPC, to avoid waiting for the return
+ value.)
Using the optional arguments, it is possible to use this to
highlight a range of text, and also to associate virtual text
@@ -2548,6 +2809,44 @@ 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.
+ • sign_text: string of length 1-2 used to
+ display in the sign column. Note: ranges are
+ unsupported and decorations are only applied
+ to start_row
+ • sign_hl_group: name of the highlight group
+ used to highlight the sign column text. Note:
+ ranges are unsupported and decorations are
+ only applied to start_row
+ • number_hl_group: name of the highlight group
+ used to highlight the number column. Note:
+ ranges are unsupported and decorations are
+ only applied to start_row
+ • line_hl_group: name of the highlight group
+ used to highlight the whole line. Note: ranges
+ are unsupported and decorations are only
+ applied to start_row
+ • cursorline_hl_group: name of the highlight
+ group used to highlight the line when the
+ cursor is on the same line as the mark and
+ 'cursorline' is enabled. Note: ranges are
+ unsupported and decorations are only applied
+ to start_row
+ • conceal: string which should be either empty
+ or a single character. Enable concealing
+ similar to |:syn-conceal|. When a character is
+ supplied it is used as |:syn-cchar|.
+ "hl_group" is used as highlight for the cchar
+ if provided, otherwise it defaults to
+ |hl-Conceal|.
+ • ui_watched: boolean that indicates the mark
+ should be drawn by a UI. When set, the UI will
+ receive win_extmark events. Note: the mark is
+ positioned by virt_text attributes. Can be
+ used together with virt_text.
Return: ~
Id of the created/updated extmark
@@ -2605,6 +2904,9 @@ nvim_set_decoration_provider({ns_id}, {opts})
`vim.rpcnotify` should be OK, but `vim.rpcrequest` is quite
dubious for the moment.
+ Attributes: ~
+ |vim.api| only
+
Parameters: ~
{ns_id} Namespace id from |nvim_create_namespace()|
{opts} Callbacks invoked during redraw:
@@ -2628,6 +2930,9 @@ Window Functions *api-window*
nvim_win_call({window}, {fun}) *nvim_win_call()*
Calls a function with window as temporary current window.
+ Attributes: ~
+ |vim.api| only
+
Parameters: ~
{window} Window handle, or 0 for current window
{fun} Function to call inside the window (currently
@@ -2698,16 +3003,6 @@ nvim_win_get_number({window}) *nvim_win_get_number()*
Return: ~
Window number
-nvim_win_get_option({window}, {name}) *nvim_win_get_option()*
- Gets a window option value
-
- Parameters: ~
- {window} Window handle, or 0 for current window
- {name} Option name
-
- Return: ~
- Option value
-
nvim_win_get_position({window}) *nvim_win_get_position()*
Gets the window position in display cells. First position is
zero.
@@ -2751,7 +3046,7 @@ nvim_win_hide({window}) *nvim_win_hide()*
|:hide| with a |window-ID|).
Like |:hide| the buffer becomes hidden unless another window
- is editing it, or 'bufhidden' is `unload` , `delete` or `wipe`
+ is editing it, or 'bufhidden' is `unload`, `delete` or `wipe`
as opposed to |:close| or |nvim_win_close|, which will close
the buffer.
@@ -2782,29 +3077,20 @@ nvim_win_set_buf({window}, {buffer}) *nvim_win_set_buf()*
nvim_win_set_cursor({window}, {pos}) *nvim_win_set_cursor()*
Sets the (1,0)-indexed cursor position in the window.
- |api-indexing|
+ |api-indexing| This scrolls the window even if it is not the
+ current one.
Parameters: ~
{window} Window handle, or 0 for current window
{pos} (row, col) tuple representing the new position
nvim_win_set_height({window}, {height}) *nvim_win_set_height()*
- Sets the window height. This will only succeed if the screen
- is split horizontally.
+ Sets the window height.
Parameters: ~
{window} Window handle, or 0 for current window
{height} Height as a count of rows
-nvim_win_set_option({window}, {name}, {value}) *nvim_win_set_option()*
- Sets a window option value. Passing 'nil' as value deletes the
- option(only works if there's a global fallback)
-
- Parameters: ~
- {window} Window handle, or 0 for current window
- {name} Option name
- {value} Option value
-
nvim_win_set_var({window}, {name}, {value}) *nvim_win_set_var()*
Sets a window-scoped (w:) variable
@@ -2967,9 +3253,10 @@ nvim_open_win({buffer}, {enter}, {*config}) *nvim_open_win()*
">", "", "", "", "<" ] will only make
vertical borders but not horizontal ones. By
default, `FloatBorder` highlight is used,
- which links to `VertSplit` when not defined.
- It could also be specified by character: [
- {"+", "MyCorner"}, {"x", "MyBorder"} ].
+ which links to `WinSeparator` when not
+ defined. It could also be specified by
+ character: [ {"+", "MyCorner"}, {"x",
+ "MyBorder"} ].
• noautocmd: If true then no buffer-related
autocommand events such as |BufEnter|,
@@ -2999,7 +3286,7 @@ nvim_win_set_config({window}, {*config}) *nvim_win_set_config()*
layouts).
When reconfiguring a floating window, absent option keys will
- not be changed. `row` / `col` and `relative` must be
+ not be changed. `row`/`col` and `relative` must be
reconfigured together.
Parameters: ~
@@ -3078,6 +3365,282 @@ nvim_tabpage_set_var({tabpage}, {name}, {value})
==============================================================================
+Autocmd Functions *api-autocmd*
+
+nvim_clear_autocmds({*opts}) *nvim_clear_autocmds()*
+ Clear all autocommands that match the corresponding {opts}. To
+ delete a particular autocmd, see |nvim_del_autocmd|.
+
+ Parameters: ~
+ {opts} Parameters
+ • event: (string|table) Examples:
+ • event: "pat1"
+ • event: { "pat1" }
+ • event: { "pat1", "pat2", "pat3" }
+
+ • pattern: (string|table)
+ • pattern or patterns to match exactly.
+ • For example, if you have `*.py` as that
+ pattern for the autocmd, you must pass
+ `*.py` exactly to clear it. `test.py` will
+ not match the pattern.
+
+ • defaults to clearing all patterns.
+ • NOTE: Cannot be used with {buffer}
+
+ • buffer: (bufnr)
+ • clear only |autocmd-buflocal| autocommands.
+ • NOTE: Cannot be used with {pattern}
+
+ • group: (string|int) The augroup name or id.
+ • NOTE: If not passed, will only delete autocmds not in any group.
+
+nvim_create_augroup({name}, {*opts}) *nvim_create_augroup()*
+ Create or get an autocommand group |autocmd-groups|.
+
+ To get an existing group id, do: >
+ local id = vim.api.nvim_create_augroup("MyGroup", {
+ clear = false
+ })
+<
+
+ Parameters: ~
+ {name} String: The name of the group
+ {opts} Dictionary Parameters
+ • clear (bool) optional: defaults to true. Clear
+ existing commands if the group already exists
+ |autocmd-groups|.
+
+ Return: ~
+ Integer id of the created group.
+
+ See also: ~
+ |autocmd-groups|
+
+nvim_create_autocmd({event}, {*opts}) *nvim_create_autocmd()*
+ Create an |autocommand|
+
+ The API allows for two (mutually exclusive) types of actions
+ to be executed when the autocommand triggers: a callback
+ function (Lua or Vimscript), or a command (like regular
+ autocommands).
+
+ Example using callback: >
+ -- Lua function
+ local myluafun = function() print("This buffer enters") end
+
+ -- Vimscript function name (as a string)
+ local myvimfun = "g:MyVimFunction"
+
+ vim.api.nvim_create_autocmd({"BufEnter", "BufWinEnter"}, {
+ pattern = {"*.c", "*.h"},
+ callback = myluafun, -- Or myvimfun
+ })
+<
+
+ Lua functions receive a table with information about the
+ autocmd event as an argument. To use a function which itself
+ accepts another (optional) parameter, wrap the function in a
+ lambda:
+>
+ -- Lua function with an optional parameter.
+ -- The autocmd callback would pass a table as argument but this
+ -- function expects number|nil
+ local myluafun = function(bufnr) bufnr = bufnr or vim.api.nvim_get_current_buf() end
+
+ vim.api.nvim_create_autocmd({"BufEnter", "BufWinEnter"}, {
+ pattern = {"*.c", "*.h"},
+ callback = function() myluafun() end,
+ })
+<
+
+ Example using command: >
+ vim.api.nvim_create_autocmd({"BufEnter", "BufWinEnter"}, {
+ pattern = {"*.c", "*.h"},
+ command = "echo 'Entering a C or C++ file'",
+ })
+<
+
+ Example values for pattern: >
+ pattern = "*.py"
+ pattern = { "*.py", "*.pyi" }
+<
+
+ Example values for event: >
+ "BufWritePre"
+ {"CursorHold", "BufWritePre", "BufWritePost"}
+<
+
+ Parameters: ~
+ {event} (string|array) The event or events to register
+ this autocommand
+ {opts} Dictionary of autocommand options:
+ • group (string|integer) optional: the
+ autocommand group name or id to match against.
+ • pattern (string|array) optional: pattern or
+ patterns to match against |autocmd-pattern|.
+ • buffer (integer) optional: buffer number for
+ buffer local autocommands |autocmd-buflocal|.
+ Cannot be used with {pattern}.
+ • desc (string) optional: description of the
+ autocommand.
+ • callback (function|string) optional: if a
+ string, the name of a Vimscript function to
+ call when this autocommand is triggered.
+ Otherwise, a Lua function which is called when
+ this autocommand is triggered. Cannot be used
+ with {command}. Lua callbacks can return true
+ to delete the autocommand; in addition, they
+ accept a single table argument with the
+ following keys:
+ • id: (number) the autocommand id
+ • event: (string) the name of the event that
+ triggered the autocommand |autocmd-events|
+ • group: (number|nil) the autocommand group id,
+ if it exists
+ • match: (string) the expanded value of
+ |<amatch>|
+ • buf: (number) the expanded value of |<abuf>|
+ • file: (string) the expanded value of
+ |<afile>|
+ • data: (any) arbitrary data passed to
+ |nvim_exec_autocmds()|
+
+ • command (string) optional: Vim command to
+ execute on event. Cannot be used with
+ {callback}
+ • once (boolean) optional: defaults to false. Run
+ the autocommand only once |autocmd-once|.
+ • nested (boolean) optional: defaults to false.
+ Run nested autocommands |autocmd-nested|.
+
+ Return: ~
+ Integer id of the created autocommand.
+
+ See also: ~
+ |autocommand|
+ |nvim_del_autocmd()|
+
+nvim_del_augroup_by_id({id}) *nvim_del_augroup_by_id()*
+ Delete an autocommand group by id.
+
+ To get a group id one can use |nvim_get_autocmds()|.
+
+ NOTE: behavior differs from |augroup-delete|. When deleting a
+ group, autocommands contained in this group will also be
+ deleted and cleared. This group will no longer exist.
+
+ Parameters: ~
+ {id} Integer The id of the group.
+
+ See also: ~
+ |nvim_del_augroup_by_name()|
+ |nvim_create_augroup()|
+
+nvim_del_augroup_by_name({name}) *nvim_del_augroup_by_name()*
+ Delete an autocommand group by name.
+
+ NOTE: behavior differs from |augroup-delete|. When deleting a
+ group, autocommands contained in this group will also be
+ deleted and cleared. This group will no longer exist.
+
+ Parameters: ~
+ {name} String The name of the group.
+
+ See also: ~
+ |autocommand-groups|
+
+nvim_del_autocmd({id}) *nvim_del_autocmd()*
+ Delete an autocommand by id.
+
+ NOTE: Only autocommands created via the API have an id.
+
+ Parameters: ~
+ {id} Integer The id returned by nvim_create_autocmd
+
+ See also: ~
+ |nvim_create_autocmd()|
+
+nvim_exec_autocmds({event}, {*opts}) *nvim_exec_autocmds()*
+ Execute all autocommands for {event} that match the
+ corresponding {opts} |autocmd-execute|.
+
+ Parameters: ~
+ {event} (String|Array) The event or events to execute
+ {opts} Dictionary of autocommand options:
+ • group (string|integer) optional: the
+ autocommand group name or id to match against.
+ |autocmd-groups|.
+ • pattern (string|array) optional: defaults to
+ "*" |autocmd-pattern|. Cannot be used with
+ {buffer}.
+ • buffer (integer) optional: buffer number
+ |autocmd-buflocal|. Cannot be used with
+ {pattern}.
+ • modeline (bool) optional: defaults to true.
+ Process the modeline after the autocommands
+ |<nomodeline>|.
+ • data (any): arbitrary data to send to the
+ autocommand callback. See
+ |nvim_create_autocmd()| for details.
+
+ See also: ~
+ |:doautocmd|
+
+nvim_get_autocmds({*opts}) *nvim_get_autocmds()*
+ Get all autocommands that match the corresponding {opts}.
+
+ These examples will get autocommands matching ALL the given
+ criteria: >
+ -- Matches all criteria
+ autocommands = vim.api.nvim_get_autocmds({
+ group = "MyGroup",
+ event = {"BufEnter", "BufWinEnter"},
+ pattern = {"*.c", "*.h"}
+ })
+
+ -- All commands from one group
+ autocommands = vim.api.nvim_get_autocmds({
+ group = "MyGroup",
+ })
+<
+
+ NOTE: When multiple patterns or events are provided, it will
+ find all the autocommands that match any combination of them.
+
+ Parameters: ~
+ {opts} Dictionary with at least one of the following:
+ • group (string|integer): the autocommand group
+ name or id to match against.
+ • event (string|array): event or events to match
+ against |autocmd-events|.
+ • pattern (string|array): pattern or patterns to
+ match against |autocmd-pattern|.
+
+ Return: ~
+ Array of autocommands matching the criteria, with each
+ item containing the following fields:
+ • id (number): the autocommand id (only when defined with
+ the API).
+ • group (integer): the autocommand group id.
+ • group_name (string): the autocommand group name.
+ • desc (string): the autocommand description.
+ • event (string): the autocommand event.
+ • command (string): the autocommand command. Note: this
+ will be empty if a callback is set.
+ • callback (function|string|nil): Lua function or name of
+ a Vim script function which is executed when this
+ autocommand is triggered.
+ • once (boolean): whether the autocommand is only run
+ once.
+ • pattern (string): the autocommand pattern. If the
+ autocommand is buffer local |autocmd-buffer-local|:
+ • buflocal (boolean): true if the autocommand is buffer
+ local.
+ • buffer (number): the buffer number.
+
+
+==============================================================================
UI Functions *api-ui*
nvim_ui_attach({width}, {height}, {options}) *nvim_ui_attach()*
@@ -3093,6 +3656,9 @@ nvim_ui_attach({width}, {height}, {options}) *nvim_ui_attach()*
A requests 80x40 but client B requests 200x100, the global
screen has size 80x40.
+ Attributes: ~
+ |RPC| only
+
Parameters: ~
{width} Requested screen columns
{height} Requested screen rows
@@ -3103,6 +3669,9 @@ nvim_ui_detach() *nvim_ui_detach()*
Removes the client from the list of UIs. |nvim_list_uis()|
+ Attributes: ~
+ |RPC| only
+
*nvim_ui_pum_set_bounds()*
nvim_ui_pum_set_bounds({width}, {height}, {row}, {col})
Tells Nvim the geometry of the popumenu, to align floating
@@ -3116,6 +3685,9 @@ nvim_ui_pum_set_bounds({width}, {height}, {row}, {col})
nor be anchored to exact grid corners, so one can set
floating-point numbers to the popup menu geometry.
+ Attributes: ~
+ |RPC| only
+
Parameters: ~
{width} Popupmenu width.
{height} Popupmenu height.
@@ -3126,15 +3698,24 @@ nvim_ui_pum_set_height({height}) *nvim_ui_pum_set_height()*
Tells Nvim the number of elements displaying in the popumenu,
to decide <PageUp> and <PageDown> movement.
+ Attributes: ~
+ |RPC| only
+
Parameters: ~
{height} Popupmenu height, must be greater than zero.
nvim_ui_set_option({name}, {value}) *nvim_ui_set_option()*
TODO: Documentation
+ Attributes: ~
+ |RPC| only
+
nvim_ui_try_resize({width}, {height}) *nvim_ui_try_resize()*
TODO: Documentation
+ Attributes: ~
+ |RPC| only
+
*nvim_ui_try_resize_grid()*
nvim_ui_try_resize_grid({grid}, {width}, {height})
Tell Nvim to resize a grid. Triggers a grid_resize event with
@@ -3143,6 +3724,9 @@ nvim_ui_try_resize_grid({grid}, {width}, {height})
On invalid grid handle, fails with error.
+ Attributes: ~
+ |RPC| only
+
Parameters: ~
{grid} The handle of the grid to be changed.
{width} The new requested width.
diff --git a/runtime/doc/arabic.txt b/runtime/doc/arabic.txt
index 5d3bf7a761..0df861111c 100644
--- a/runtime/doc/arabic.txt
+++ b/runtime/doc/arabic.txt
@@ -175,7 +175,7 @@ o Enable Arabic settings [short-cut]
vertical separator like "l" or "𝖨" may be used. It may also be
hidden by changing its color to the foreground color: >
:set fillchars=vert:l
- :hi VertSplit ctermbg=White
+ :hi WinSeparator ctermbg=White
< Note that this is a workaround, not a proper solution.
If, on the other hand, you'd like to be verbose and explicit and
diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt
index 46d9a3b57a..59e5c078a3 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
@@ -69,6 +69,7 @@ Or use `:execute`: >
:augroup mine | exe "au! BufRead *" | augroup END
:augroup mine | exe "au BufRead * set tw=70" | augroup END
+< *autocmd-expand*
Note that special characters (e.g., "%", "<cword>") in the ":autocmd"
arguments are not expanded when the autocommand is defined. These will be
expanded when the Event is recognized, and the {cmd} is executed. The only
@@ -119,19 +120,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 +152,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}
@@ -275,14 +276,14 @@ BufRead or BufReadPost When starting to edit a new buffer, after
processing modelines. See |BufWinEnter| to do
something after processing modelines.
Also triggered:
- - when writing an unnamed buffer such that the
- buffer gets a name
+ - when writing an unnamed buffer in a way that
+ the buffer gets a name
- after successfully recovering a file
- for the "filetypedetect" group when
executing ":filetype detect"
Not triggered:
- - for ":r file"
- - if the file doesn't exist
+ - for the `:read file` command
+ - when the file doesn't exist
*BufReadCmd*
BufReadCmd Before starting to edit a new buffer. Should
read the file into the buffer. |Cmd-event|
@@ -499,10 +500,10 @@ CursorMoved After the cursor was moved in Normal or Visual
mode or to another window. Also when the text
of the cursor line has been changed, e.g. with
"x", "rx" or "p".
- Not triggered when there is typeahead, while
- executing a script file, when an operator is
- pending, or when moving to another window while
- remaining at the same cursor position.
+ Not always triggered when there is typeahead,
+ while executing commands in a script file, or
+ when an operator is pending. Always triggered
+ when moving to another window.
For an example see |match-parens|.
Note: Cannot be skipped with |:noautocmd|.
Careful: This is triggered very often, don't
@@ -525,12 +526,32 @@ DirChanged After the |current-directory| was changed.
"global" to trigger on `:cd`
"auto" to trigger on 'autochdir'.
Sets these |v:event| keys:
- cwd: current working directory
- scope: "global", "tab", "window"
+ cwd: current working directory
+ scope: "global", "tabpage", "window"
changed_window: v:true if we fired the event
switching window (or tab)
<afile> is set to the new directory name.
Non-recursive (event cannot trigger itself).
+ *DirChangedPre*
+DirChangedPre When the |current-directory| is going to be
+ changed, as with |DirChanged|.
+ The pattern is like with |DirChanged|.
+ Sets these |v:event| keys:
+ directory: new working directory
+ scope: "global", "tabpage", "window"
+ changed_window: v:true if we fired the event
+ switching window (or tab)
+ <afile> is set to the new directory name.
+ Non-recursive (event cannot trigger itself).
+ *ExitPre*
+ExitPre When using `:quit`, `:wq` in a way it makes
+ Vim exit, or using `:qall`, just after
+ |QuitPre|. Can be used to close any
+ non-essential window. Exiting may still be
+ cancelled if there is a modified buffer that
+ isn't automatically saved, use |VimLeavePre|
+ for really exiting.
+ See also |QuitPre|, |WinClosed|.
*FileAppendCmd*
FileAppendCmd Before appending to a file. Should do the
appending to the file. Use the '[ and ']
@@ -557,15 +578,6 @@ FileChangedRO Before making the first change to a read-only
*E881*
If the number of lines changes saving for undo
may fail and the change will be aborted.
- *ExitPre*
-ExitPre When using `:quit`, `:wq` in a way it makes
- Vim exit, or using `:qall`, just after
- |QuitPre|. Can be used to close any
- non-essential window. Exiting may still be
- cancelled if there is a modified buffer that
- isn't automatically saved, use |VimLeavePre|
- for really exiting.
- See also |QuitPre|, |WinClosed|.
*FileChangedShell*
FileChangedShell When Vim notices that the modification time of
a file has changed since editing started.
@@ -663,15 +675,19 @@ FuncUndefined When a user function is used but it isn't
alternative is to use an autoloaded function.
See |autoload-functions|.
*UIEnter*
-UIEnter After a UI connects via |nvim_ui_attach()|,
- after VimEnter. Can be used for GUI-specific
- configuration.
+UIEnter After a UI connects via |nvim_ui_attach()|, or
+ after builtin TUI is started, after |VimEnter|.
Sets these |v:event| keys:
- chan
+ chan: 0 for builtin TUI
+ 1 for |--embed|
+ |channel-id| of the UI otherwise
*UILeave*
-UILeave After a UI disconnects from Nvim.
+UILeave After a UI disconnects from Nvim, or after
+ builtin TUI is stopped, after |VimLeave|.
Sets these |v:event| keys:
- chan
+ chan: 0 for builtin TUI
+ 1 for |--embed|
+ |channel-id| of the UI otherwise
*InsertChange*
InsertChange When typing <Insert> while in Insert or
Replace mode. The |v:insertmode| variable
@@ -711,13 +727,14 @@ MenuPopup Just before showing the popup menu (under the
right mouse button). Useful for adjusting the
menu for what is under the cursor or mouse
pointer.
- The pattern is matched against a single
- character representing the mode:
+ The pattern is matched against one or two
+ characters representing the mode:
n Normal
v Visual
o Operator-pending
i Insert
c Command line
+ tl Terminal
*ModeChanged*
ModeChanged After changing the mode. The pattern is
matched against `'old_mode:new_mode'`, for
@@ -855,7 +872,7 @@ ShellCmdPost After executing a shell command with |:!cmd|,
*Signal*
Signal After Nvim receives a signal. The pattern is
matched against the signal name. Only
- "SIGUSR1" is supported. Example: >
+ "SIGUSR1" and "SIGWINCH" are supported. Example: >
autocmd Signal SIGUSR1 call some#func()
< *ShellFilterPost*
ShellFilterPost After executing a shell command with
@@ -1064,27 +1081,36 @@ WinLeave Before leaving a window. If the window to be
executes the BufLeave autocommands before the
WinLeave autocommands (but not for ":new").
Not used for ":qa" or ":q" when exiting Vim.
- After WinClosed.
+ Before WinClosed.
*WinNew*
WinNew When a new window was created. Not done for
the first window, when Vim has just started.
Before WinEnter.
- *WinScrolled*
-WinScrolled After scrolling the viewport of the current
- window.
+ *WinScrolled*
+WinScrolled After scrolling the content of a window or
+ resizing a window.
+ The pattern is matched against the
+ |window-ID|. Both <amatch> and <afile> are
+ set to the |window-ID|.
+ Non-recursive (the event cannot trigger
+ itself). However, if the command causes the
+ window to scroll or change size another
+ WinScrolled event will be triggered later.
+ Does not trigger when the command is added,
+ only after the first scroll or resize.
==============================================================================
-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).
@@ -1395,7 +1421,7 @@ Examples for reading and writing compressed files: >
: autocmd BufReadPre,FileReadPre *.gz set bin
: autocmd BufReadPost,FileReadPost *.gz '[,']!gunzip
: autocmd BufReadPost,FileReadPost *.gz set nobin
- : autocmd BufReadPost,FileReadPost *.gz execute ":doautocmd BufReadPost " . expand("%:r")
+ : autocmd BufReadPost,FileReadPost *.gz execute ":doautocmd BufReadPost " .. expand("%:r")
: autocmd BufWritePost,FileWritePost *.gz !mv <afile> <afile>:r
: autocmd BufWritePost,FileWritePost *.gz !gzip <afile>:r
@@ -1494,7 +1520,7 @@ To insert the current date and time in a *.html file when writing it: >
: else
: let l = line("$")
: endif
- : exe "1," . l . "g/Last modified: /s/Last modified: .*/Last modified: " .
+ : exe "1," .. l .. "g/Last modified: /s/Last modified: .*/Last modified: " ..
: \ strftime("%Y %b %d")
:endfun
diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt
new file mode 100644
index 0000000000..df5a636070
--- /dev/null
+++ b/runtime/doc/builtin.txt
@@ -0,0 +1,9087 @@
+*builtin.txt* Nvim
+
+
+ VIM REFERENCE MANUAL by Bram Moolenaar
+
+
+Builtin functions *builtin-functions*
+
+1. Overview |builtin-function-list|
+2. Details |builtin-function-details|
+3. Matching a pattern in a String |string-match|
+
+==============================================================================
+1. Overview *builtin-function-list*
+
+Use CTRL-] on the function name to jump to the full explanation.
+
+USAGE RESULT DESCRIPTION ~
+
+abs({expr}) Float or Number absolute value of {expr}
+acos({expr}) Float arc cosine of {expr}
+add({object}, {item}) List/Blob append {item} to {object}
+and({expr}, {expr}) Number bitwise AND
+api_info() Dict api metadata
+append({lnum}, {text}) Number append {text} below line {lnum}
+appendbufline({expr}, {lnum}, {text})
+ Number append {text} below line {lnum}
+ in buffer {expr}
+argc([{winid}]) Number number of files in the argument list
+argidx() Number current index in the argument list
+arglistid([{winnr} [, {tabnr}]]) Number argument list id
+argv({nr} [, {winid}]) String {nr} entry of the argument list
+argv([-1, {winid}]) List the argument list
+asin({expr}) Float arc sine of {expr}
+assert_beeps({cmd}) Number assert {cmd} causes a beep
+assert_equal({exp}, {act} [, {msg}])
+ Number assert {exp} is equal to {act}
+assert_equalfile({fname-one}, {fname-two} [, {msg}])
+ Number assert file contents are equal
+assert_exception({error} [, {msg}])
+ Number assert {error} is in v:exception
+assert_fails({cmd} [, {error}]) Number assert {cmd} fails
+assert_false({actual} [, {msg}])
+ Number assert {actual} is false
+assert_inrange({lower}, {upper}, {actual} [, {msg}])
+ Number assert {actual} is inside the range
+assert_match({pat}, {text} [, {msg}])
+ Number assert {pat} matches {text}
+assert_nobeep({cmd}) Number assert {cmd} does not cause a beep
+assert_notequal({exp}, {act} [, {msg}])
+ Number assert {exp} is not equal {act}
+assert_notmatch({pat}, {text} [, {msg}])
+ Number assert {pat} not matches {text}
+assert_report({msg}) Number report a test failure
+assert_true({actual} [, {msg}]) Number assert {actual} is true
+atan({expr}) Float arc tangent of {expr}
+atan2({expr1}, {expr2}) Float arc tangent of {expr1} / {expr2}
+browse({save}, {title}, {initdir}, {default})
+ String put up a file requester
+browsedir({title}, {initdir}) String put up a directory requester
+bufadd({name}) Number add a buffer to the buffer list
+bufexists({expr}) Number |TRUE| if buffer {expr} exists
+buflisted({expr}) Number |TRUE| if buffer {expr} is listed
+bufload({expr}) Number load buffer {expr} if not loaded yet
+bufloaded({expr}) Number |TRUE| if buffer {expr} is loaded
+bufname([{expr}]) String Name of the buffer {expr}
+bufnr([{expr} [, {create}]]) Number Number of the buffer {expr}
+bufwinid({expr}) Number |window-ID| of buffer {expr}
+bufwinnr({expr}) Number window number of buffer {expr}
+byte2line({byte}) Number line number at byte count {byte}
+byteidx({expr}, {nr}) Number byte index of {nr}'th char in {expr}
+byteidxcomp({expr}, {nr}) Number byte index of {nr}'th char in {expr}
+call({func}, {arglist} [, {dict}])
+ any call {func} with arguments {arglist}
+ceil({expr}) Float round {expr} up
+changenr() Number current change number
+chanclose({id} [, {stream}]) Number Closes a channel or one of its streams
+chansend({id}, {data}) Number Writes {data} to channel
+char2nr({expr} [, {utf8}]) Number ASCII/UTF-8 value of first char in {expr}
+charcol({expr}) Number column number of cursor or mark
+charidx({string}, {idx} [, {countcc}])
+ Number char index of byte {idx} in {string}
+chdir({dir}) String change current working directory
+cindent({lnum}) Number C indent for line {lnum}
+clearmatches([{win}]) none clear all matches
+col({expr}) Number column byte index of cursor or mark
+complete({startcol}, {matches}) none set Insert mode completion
+complete_add({expr}) Number add completion match
+complete_check() Number check for key typed during completion
+complete_info([{what}]) Dict get current completion information
+confirm({msg} [, {choices} [, {default} [, {type}]]])
+ Number number of choice picked by user
+copy({expr}) any make a shallow copy of {expr}
+cos({expr}) Float cosine of {expr}
+cosh({expr}) Float hyperbolic cosine of {expr}
+count({comp}, {expr} [, {ic} [, {start}]])
+ Number count how many {expr} are in {comp}
+cscope_connection([{num}, {dbpath} [, {prepend}]])
+ Number checks existence of cscope connection
+ctxget([{index}]) Dict return the |context| dict at {index}
+ctxpop() none pop and restore |context| from the
+ |context-stack|
+ctxpush([{types}]) none push the current |context| to the
+ |context-stack|
+ctxset({context} [, {index}]) none set |context| at {index}
+ctxsize() Number return |context-stack| size
+cursor({lnum}, {col} [, {off}])
+ Number move cursor to {lnum}, {col}, {off}
+cursor({list}) Number move cursor to position in {list}
+debugbreak({pid}) Number interrupt process being debugged
+deepcopy({expr} [, {noref}]) any make a full copy of {expr}
+delete({fname} [, {flags}]) Number delete the file or directory {fname}
+deletebufline({buf}, {first} [, {last}])
+ Number delete lines from buffer {buf}
+dictwatcheradd({dict}, {pattern}, {callback})
+ Start watching a dictionary
+dictwatcherdel({dict}, {pattern}, {callback})
+ Stop watching a dictionary
+did_filetype() Number |TRUE| if FileType autocommand event used
+diff_filler({lnum}) Number diff filler lines about {lnum}
+diff_hlID({lnum}, {col}) Number diff highlighting at {lnum}/{col}
+digraph_get({chars}) String get the digraph of {chars}
+digraph_getlist([{listall}]) List get all |digraph|s
+digraph_set({chars}, {digraph}) Boolean register |digraph|
+digraph_setlist({digraphlist}) Boolean register multiple |digraph|s
+empty({expr}) Number |TRUE| if {expr} is empty
+environ() Dict return environment variables
+escape({string}, {chars}) String escape {chars} in {string} with '\'
+eval({string}) any evaluate {string} into its value
+eventhandler() Number |TRUE| if inside an event handler
+executable({expr}) Number 1 if executable {expr} exists
+execute({command}) String execute and capture output of {command}
+exepath({expr}) String full path of the command {expr}
+exists({expr}) Number |TRUE| if {expr} exists
+extend({expr1}, {expr2} [, {expr3}])
+ List/Dict insert items of {expr2} into {expr1}
+exp({expr}) Float exponential of {expr}
+expand({expr} [, {nosuf} [, {list}]])
+ any expand special keywords in {expr}
+expandcmd({expr}) String expand {expr} like with `:edit`
+feedkeys({string} [, {mode}]) Number add key sequence to typeahead buffer
+filereadable({file}) Number |TRUE| if {file} is a readable file
+filewritable({file}) Number |TRUE| if {file} is a writable file
+filter({expr1}, {expr2}) List/Dict remove items from {expr1} where
+ {expr2} is 0
+finddir({name} [, {path} [, {count}]])
+ String find directory {name} in {path}
+findfile({name} [, {path} [, {count}]])
+ String find file {name} in {path}
+flatten({list} [, {maxdepth}]) List flatten {list} up to {maxdepth} levels
+float2nr({expr}) Number convert Float {expr} to a Number
+floor({expr}) Float round {expr} down
+fmod({expr1}, {expr2}) Float remainder of {expr1} / {expr2}
+fnameescape({fname}) String escape special characters in {fname}
+fnamemodify({fname}, {mods}) String modify file name
+foldclosed({lnum}) Number first line of fold at {lnum} if closed
+foldclosedend({lnum}) Number last line of fold at {lnum} if closed
+foldlevel({lnum}) Number fold level at {lnum}
+foldtext() String line displayed for closed fold
+foldtextresult({lnum}) String text for closed fold at {lnum}
+fullcommand({name}) String get full command from {name}
+funcref({name} [, {arglist}] [, {dict}])
+ Funcref reference to function {name}
+function({name} [, {arglist}] [, {dict}])
+ Funcref named reference to function {name}
+garbagecollect([{atexit}]) none free memory, breaking cyclic references
+get({list}, {idx} [, {def}]) any get item {idx} from {list} or {def}
+get({dict}, {key} [, {def}]) any get item {key} from {dict} or {def}
+get({func}, {what}) any get property of funcref/partial {func}
+getbufinfo([{buf}]) List information about buffers
+getbufline({buf}, {lnum} [, {end}])
+ List lines {lnum} to {end} of buffer {buf}
+getbufvar({buf}, {varname} [, {def}])
+ any variable {varname} in buffer {buf}
+getchangelist([{buf}]) List list of change list items
+getchar([expr]) Number or String
+ get one character from the user
+getcharmod() Number modifiers for the last typed character
+getcharpos({expr}) List position of cursor, mark, etc.
+getcharsearch() Dict last character search
+getcharstr([expr]) String get one character from the user
+getcmdcompltype() String return the type of the current
+ command-line completion
+getcmdline() String return the current command-line
+getcmdpos() Number return cursor position in command-line
+getcmdscreenpos() Number return cursor screen position in
+ command-line
+getcmdtype() String return current command-line type
+getcmdwintype() String return current command-line window type
+getcompletion({pat}, {type} [, {filtered}])
+ List list of cmdline completion matches
+getcurpos([{winnr}]) List position of the cursor
+getcursorcharpos([{winnr}]) List character position of the cursor
+getcwd([{winnr} [, {tabnr}]]) String get the current working directory
+getenv({name}) String return environment variable
+getfontname([{name}]) String name of font being used
+getfperm({fname}) String file permissions of file {fname}
+getfsize({fname}) Number size in bytes of file {fname}
+getftime({fname}) Number last modification time of file
+getftype({fname}) String description of type of file {fname}
+getjumplist([{winnr} [, {tabnr}]])
+ List list of jump list items
+getline({lnum}) String line {lnum} of current buffer
+getline({lnum}, {end}) List lines {lnum} to {end} of current buffer
+getloclist({nr}) List list of location list items
+getloclist({nr}, {what}) Dict get specific location list properties
+getmarklist([{buf}]) List list of global/local marks
+getmatches([{win}]) List list of current matches
+getmousepos() Dict last known mouse position
+getpid() Number process ID of Vim
+getpos({expr}) List position of cursor, mark, etc.
+getqflist() List list of quickfix items
+getqflist({what}) Dict get specific quickfix list properties
+getreg([{regname} [, 1 [, {list}]]])
+ String or List contents of a register
+getreginfo([{regname}]) Dict information about a register
+getregtype([{regname}]) String type of a register
+gettabinfo([{expr}]) List list of tab pages
+gettabvar({nr}, {varname} [, {def}])
+ any variable {varname} in tab {nr} or {def}
+gettabwinvar({tabnr}, {winnr}, {name} [, {def}])
+ any {name} in {winnr} in tab page {tabnr}
+gettagstack([{nr}]) Dict get the tag stack of window {nr}
+getwininfo([{winid}]) List list of info about each window
+getwinpos([{timeout}]) List X and Y coord in pixels of the Vim window
+getwinposx() Number X coord in pixels of Vim window
+getwinposy() Number Y coord in pixels of Vim window
+getwinvar({nr}, {varname} [, {def}])
+ any variable {varname} in window {nr}
+glob({expr} [, {nosuf} [, {list} [, {alllinks}]]])
+ any expand file wildcards in {expr}
+glob2regpat({expr}) String convert a glob pat into a search pat
+globpath({path}, {expr} [, {nosuf} [, {list} [, {alllinks}]]])
+ String do glob({expr}) for all dirs in {path}
+has({feature}) Number |TRUE| if feature {feature} supported
+has_key({dict}, {key}) Number |TRUE| if {dict} has entry {key}
+haslocaldir([{winnr} [, {tabnr}]])
+ Number |TRUE| if the window executed |:lcd| or
+ the tab executed |:tcd|
+hasmapto({what} [, {mode} [, {abbr}]])
+ Number |TRUE| if mapping to {what} exists
+histadd({history}, {item}) String add an item to a history
+histdel({history} [, {item}]) String remove an item from a history
+histget({history} [, {index}]) String get the item {index} from a history
+histnr({history}) Number highest index of a history
+hlexists({name}) Number |TRUE| if highlight group {name} exists
+hlID({name}) Number syntax ID of highlight group {name}
+hostname() String name of the machine Vim is running on
+iconv({expr}, {from}, {to}) String convert encoding of {expr}
+indent({lnum}) Number indent of line {lnum}
+index({object}, {expr} [, {start} [, {ic}]])
+ Number index in {object} where {expr} appears
+input({prompt} [, {text} [, {completion}]])
+ String get input from the user
+inputlist({textlist}) Number let the user pick from a choice list
+inputrestore() Number restore typeahead
+inputsave() Number save and clear typeahead
+inputsecret({prompt} [, {text}])
+ String like input() but hiding the text
+insert({object}, {item} [, {idx}])
+ List insert {item} in {object} [before {idx}]
+interrupt() none interrupt script execution
+invert({expr}) Number bitwise invert
+isdirectory({directory}) Number |TRUE| if {directory} is a directory
+isinf({expr}) Number determine if {expr} is infinity value
+ (positive or negative)
+islocked({expr}) Number |TRUE| if {expr} is locked
+isnan({expr}) Number |TRUE| if {expr} is NaN
+id({expr}) String identifier of the container
+items({dict}) List key-value pairs in {dict}
+jobpid({id}) Number Returns pid of a job.
+jobresize({id}, {width}, {height})
+ Number Resize pseudo terminal window of a job
+jobstart({cmd} [, {opts}]) Number Spawns {cmd} as a job
+jobstop({id}) Number Stops a job
+jobwait({ids} [, {timeout}]) Number Wait for a set of jobs
+join({list} [, {sep}]) String join {list} items into one String
+json_decode({expr}) any Convert {expr} from JSON
+json_encode({expr}) String Convert {expr} to JSON
+keys({dict}) List keys in {dict}
+len({expr}) Number the length of {expr}
+libcall({lib}, {func}, {arg}) String call {func} in library {lib} with {arg}
+libcallnr({lib}, {func}, {arg}) Number idem, but return a Number
+line({expr} [, {winid}]) Number line nr of cursor, last line or mark
+line2byte({lnum}) Number byte count of line {lnum}
+lispindent({lnum}) Number Lisp indent for line {lnum}
+list2str({list} [, {utf8}]) String turn numbers in {list} into a String
+localtime() Number current time
+log({expr}) Float natural logarithm (base e) of {expr}
+log10({expr}) Float logarithm of Float {expr} to base 10
+luaeval({expr} [, {expr}]) any evaluate |Lua| expression
+map({expr1}, {expr2}) List/Dict change each item in {expr1} to {expr}
+maparg({name} [, {mode} [, {abbr} [, {dict}]]])
+ String or Dict
+ rhs of mapping {name} in mode {mode}
+mapcheck({name} [, {mode} [, {abbr}]])
+ String check for mappings matching {name}
+match({expr}, {pat} [, {start} [, {count}]])
+ Number position where {pat} matches in {expr}
+matchadd({group}, {pattern} [, {priority} [, {id} [, {dict}]]])
+ Number highlight {pattern} with {group}
+matchaddpos({group}, {pos} [, {priority} [, {id} [, {dict}]]])
+ Number highlight positions with {group}
+matcharg({nr}) List arguments of |:match|
+matchdelete({id} [, {win}]) Number delete match identified by {id}
+matchend({expr}, {pat} [, {start} [, {count}]])
+ Number position where {pat} ends in {expr}
+matchfuzzy({list}, {str} [, {dict}])
+ List fuzzy match {str} in {list}
+matchfuzzypos({list}, {str} [, {dict}])
+ List fuzzy match {str} in {list}
+matchlist({expr}, {pat} [, {start} [, {count}]])
+ List match and submatches of {pat} in {expr}
+matchstr({expr}, {pat} [, {start} [, {count}]])
+ String {count}'th match of {pat} in {expr}
+matchstrpos({expr}, {pat} [, {start} [, {count}]])
+ List {count}'th match of {pat} in {expr}
+max({expr}) Number maximum value of items in {expr}
+menu_get({path} [, {modes}]) List description of |menus| matched by {path}
+min({expr}) Number minimum value of items in {expr}
+mkdir({name} [, {path} [, {prot}]])
+ Number create directory {name}
+mode([expr]) String current editing mode
+msgpackdump({list} [, {type}]) List/Blob dump objects to msgpack
+msgpackparse({data}) List parse msgpack to a list of objects
+nextnonblank({lnum}) Number line nr of non-blank line >= {lnum}
+nr2char({expr} [, {utf8}]) String single char with ASCII/UTF-8 value {expr}
+nvim_...({args}...) any call nvim |api| functions
+or({expr}, {expr}) Number bitwise OR
+pathshorten({expr} [, {len}]) String shorten directory names in a path
+perleval({expr}) any evaluate |perl| expression
+pow({x}, {y}) Float {x} to the power of {y}
+prevnonblank({lnum}) Number line nr of non-blank line <= {lnum}
+printf({fmt}, {expr1}...) String format text
+prompt_getprompt({buf}) String get prompt text
+prompt_setcallback({buf}, {expr}) none set prompt callback function
+prompt_setinterrupt({buf}, {text}) none set prompt interrupt function
+prompt_setprompt({buf}, {text}) none set prompt text
+pum_getpos() Dict position and size of pum if visible
+pumvisible() Number whether popup menu is visible
+pyeval({expr}) any evaluate |Python| expression
+py3eval({expr}) any evaluate |python3| expression
+pyxeval({expr}) any evaluate |python_x| expression
+rand([{expr}]) Number get pseudo-random number
+range({expr} [, {max} [, {stride}]])
+ List items from {expr} to {max}
+readdir({dir} [, {expr}]) List file names in {dir} selected by {expr}
+readfile({fname} [, {type} [, {max}]])
+ List get list of lines from file {fname}
+reduce({object}, {func} [, {initial}])
+ any reduce {object} using {func}
+reg_executing() String get the executing register name
+reg_recorded() String get the last recorded register name
+reg_recording() String get the recording register name
+reltime([{start} [, {end}]]) List get time value
+reltimefloat({time}) Float turn the time value into a Float
+reltimestr({time}) String turn time value into a String
+remove({list}, {idx} [, {end}]) any/List
+ remove items {idx}-{end} from {list}
+remove({blob}, {idx} [, {end}]) Number/Blob
+ remove bytes {idx}-{end} from {blob}
+remove({dict}, {key}) any remove entry {key} from {dict}
+rename({from}, {to}) Number rename (move) file from {from} to {to}
+repeat({expr}, {count}) String repeat {expr} {count} times
+resolve({filename}) String get filename a shortcut points to
+reverse({list}) List reverse {list} in-place
+round({expr}) Float round off {expr}
+rubyeval({expr}) any evaluate |Ruby| expression
+rpcnotify({channel}, {event} [, {args}...])
+ Sends an |RPC| notification to {channel}
+rpcrequest({channel}, {method} [, {args}...])
+ Sends an |RPC| request to {channel}
+screenattr({row}, {col}) Number attribute at screen position
+screenchar({row}, {col}) Number character at screen position
+screenchars({row}, {col}) List List of characters at screen position
+screencol() Number current cursor column
+screenpos({winid}, {lnum}, {col}) Dict screen row and col of a text character
+screenrow() Number current cursor row
+screenstring({row}, {col}) String characters at screen position
+search({pattern} [, {flags} [, {stopline} [, {timeout} [, {skip}]]]])
+ Number search for {pattern}
+searchcount([{options}]) Dict Get or update the last search count
+searchdecl({name} [, {global} [, {thisblock}]])
+ Number search for variable declaration
+searchpair({start}, {middle}, {end} [, {flags} [, {skip} [...]]])
+ Number search for other end of start/end pair
+searchpairpos({start}, {middle}, {end} [, {flags} [, {skip} [...]]])
+ List search for other end of start/end pair
+searchpos({pattern} [, {flags} [, {stopline} [, {timeout} [, {skip}]]]])
+ List search for {pattern}
+serverlist() String get a list of available servers
+setbufline({expr}, {lnum}, {text})
+ Number set line {lnum} to {text} in buffer
+ {expr}
+setbufvar({buf}, {varname}, {val}) set {varname} in buffer {buf} to {val}
+setcharpos({expr}, {list}) Number set the {expr} position to {list}
+setcharsearch({dict}) Dict set character search from {dict}
+setcmdpos({pos}) Number set cursor position in command-line
+setcursorcharpos({list}) Number move cursor to position in {list}
+setenv({name}, {val}) none set environment variable
+setfperm({fname}, {mode} Number set {fname} file permissions to {mode}
+setline({lnum}, {line}) Number set line {lnum} to {line}
+setloclist({nr}, {list} [, {action}])
+ Number modify location list using {list}
+setloclist({nr}, {list}, {action}, {what})
+ Number modify specific location list props
+setmatches({list} [, {win}]) Number restore a list of matches
+setpos({expr}, {list}) Number set the {expr} position to {list}
+setqflist({list} [, {action}]) Number modify quickfix list using {list}
+setqflist({list}, {action}, {what})
+ Number modify specific quickfix list props
+setreg({n}, {v} [, {opt}]) Number set register to value and type
+settabvar({nr}, {varname}, {val}) set {varname} in tab page {nr} to {val}
+settabwinvar({tabnr}, {winnr}, {varname}, {val}) set {varname} in window
+ {winnr} in tab page {tabnr} to {val}
+settagstack({nr}, {dict} [, {action}])
+ Number modify tag stack using {dict}
+setwinvar({nr}, {varname}, {val}) set {varname} in window {nr} to {val}
+sha256({string}) String SHA256 checksum of {string}
+shellescape({string} [, {special}])
+ String escape {string} for use as shell
+ command argument
+shiftwidth([{col}]) Number effective value of 'shiftwidth'
+sign_define({name} [, {dict}]) Number define or update a sign
+sign_define({list}) List define or update a list of signs
+sign_getdefined([{name}]) List get a list of defined signs
+sign_getplaced([{buf} [, {dict}]])
+ List get a list of placed signs
+sign_jump({id}, {group}, {buf})
+ Number jump to a sign
+sign_place({id}, {group}, {name}, {buf} [, {dict}])
+ Number place a sign
+sign_placelist({list}) List place a list of signs
+sign_undefine([{name}]) Number undefine a sign
+sign_undefine({list}) List undefine a list of signs
+sign_unplace({group} [, {dict}])
+ Number unplace a sign
+sign_unplacelist({list}) List unplace a list of signs
+simplify({filename}) String simplify filename as much as possible
+sin({expr}) Float sine of {expr}
+sinh({expr}) Float hyperbolic sine of {expr}
+sockconnect({mode}, {address} [, {opts}])
+ Number Connects to socket
+sort({list} [, {func} [, {dict}]])
+ List sort {list}, using {func} to compare
+soundfold({word}) String sound-fold {word}
+spellbadword() String badly spelled word at cursor
+spellsuggest({word} [, {max} [, {capital}]])
+ List spelling suggestions
+split({expr} [, {pat} [, {keepempty}]])
+ List make |List| from {pat} separated {expr}
+sqrt({expr}) Float square root of {expr}
+srand([{expr}]) List get seed for |rand()|
+stdioopen({dict}) Number open stdio in a headless instance.
+stdpath({what}) String/List returns the standard path(s) for {what}
+str2float({expr} [, {quoted}]) Float convert String to Float
+str2list({expr} [, {utf8}]) List convert each character of {expr} to
+ ASCII/UTF-8 value
+str2nr({expr} [, {base} [, {quoted}]])
+ Number convert String to Number
+strchars({expr} [, {skipcc}]) Number character length of the String {expr}
+strcharpart({str}, {start} [, {len}])
+ String {len} characters of {str} at
+ character {start}
+strdisplaywidth({expr} [, {col}]) Number display length of the String {expr}
+strftime({format} [, {time}]) String format time with a specified format
+strgetchar({str}, {index}) Number get char {index} from {str}
+stridx({haystack}, {needle} [, {start}])
+ Number index of {needle} in {haystack}
+string({expr}) String String representation of {expr} value
+strlen({expr}) Number length of the String {expr}
+strpart({str}, {start} [, {len} [, {chars}]])
+ String {len} bytes/chars of {str} at
+ byte {start}
+strptime({format}, {timestring})
+ Number Convert {timestring} to unix timestamp
+strridx({haystack}, {needle} [, {start}])
+ Number last index of {needle} in {haystack}
+strtrans({expr}) String translate string to make it printable
+strwidth({expr}) Number display cell length of the String {expr}
+submatch({nr} [, {list}]) String or List
+ specific match in ":s" or substitute()
+substitute({expr}, {pat}, {sub}, {flags})
+ String all {pat} in {expr} replaced with {sub}
+swapinfo({fname}) Dict information about swap file {fname}
+swapname({buf}) String swap file of buffer {buf}
+synID({lnum}, {col}, {trans}) Number syntax ID at {lnum} and {col}
+synIDattr({synID}, {what} [, {mode}])
+ String attribute {what} of syntax ID {synID}
+synIDtrans({synID}) Number translated syntax ID of {synID}
+synconcealed({lnum}, {col}) List info about concealing
+synstack({lnum}, {col}) List stack of syntax IDs at {lnum} and {col}
+system({cmd} [, {input}]) String output of shell command/filter {cmd}
+systemlist({cmd} [, {input}]) List output of shell command/filter {cmd}
+tabpagebuflist([{arg}]) List list of buffer numbers in tab page
+tabpagenr([{arg}]) Number number of current or last tab page
+tabpagewinnr({tabarg} [, {arg}])
+ Number number of current window in tab page
+taglist({expr} [, {filename}]) List list of tags matching {expr}
+tagfiles() List tags files used
+tan({expr}) Float tangent of {expr}
+tanh({expr}) Float hyperbolic tangent of {expr}
+tempname() String name for a temporary file
+test_garbagecollect_now() none free memory right now for testing
+timer_info([{id}]) List information about timers
+timer_pause({id}, {pause}) none pause or unpause a timer
+timer_start({time}, {callback} [, {options}])
+ Number create a timer
+timer_stop({timer}) none stop a timer
+timer_stopall() none stop all timers
+tolower({expr}) String the String {expr} switched to lowercase
+toupper({expr}) String the String {expr} switched to uppercase
+tr({src}, {fromstr}, {tostr}) String translate chars of {src} in {fromstr}
+ to chars in {tostr}
+trim({text} [, {mask} [, {dir}]])
+ String trim characters in {mask} from {text}
+trunc({expr}) Float truncate Float {expr}
+type({name}) Number type of variable {name}
+undofile({name}) String undo file name for {name}
+undotree() List undo file tree
+uniq({list} [, {func} [, {dict}]])
+ List remove adjacent duplicates from a list
+values({dict}) List values in {dict}
+virtcol({expr}) Number screen column of cursor or mark
+visualmode([expr]) String last visual mode used
+wait({timeout}, {condition} [, {interval}])
+ Number Wait until {condition} is satisfied
+wildmenumode() Number whether 'wildmenu' mode is active
+win_execute({id}, {command} [, {silent}])
+ String execute {command} in window {id}
+win_findbuf({bufnr}) List find windows containing {bufnr}
+win_getid([{win} [, {tab}]]) Number get |window-ID| for {win} in {tab}
+win_gettype([{nr}]) String type of window {nr}
+win_gotoid({expr}) Number go to |window-ID| {expr}
+win_id2tabwin({expr}) List get tab and window nr from |window-ID|
+win_id2win({expr}) Number get window nr from |window-ID|
+win_move_separator({nr}) Number move window vertical separator
+win_move_statusline({nr}) Number move window status line
+win_screenpos({nr}) List get screen position of window {nr}
+win_splitmove({nr}, {target} [, {options}])
+ Number move window {nr} to split of {target}
+winbufnr({nr}) Number buffer number of window {nr}
+wincol() Number window column of the cursor
+windowsversion() String MS-Windows OS version
+winheight({nr}) Number height of window {nr}
+winlayout([{tabnr}]) List layout of windows in tab {tabnr}
+winline() Number window line of the cursor
+winnr([{expr}]) Number number of current window
+winrestcmd() String returns command to restore window sizes
+winrestview({dict}) none restore view of current window
+winsaveview() Dict save view of current window
+winwidth({nr}) Number width of window {nr}
+wordcount() Dict get byte/char/word statistics
+writefile({object}, {fname} [, {flags}])
+ Number write |Blob| or |List| of lines to file
+xor({expr}, {expr}) Number bitwise XOR
+
+==============================================================================
+2. Details *builtin-function-details*
+
+Not all functions are here, some have been moved to a help file covering the
+specific functionality.
+
+abs({expr}) *abs()*
+ Return the absolute value of {expr}. When {expr} evaluates to
+ a |Float| abs() returns a |Float|. When {expr} can be
+ converted to a |Number| abs() returns a |Number|. Otherwise
+ abs() gives an error message and returns -1.
+ Examples: >
+ echo abs(1.456)
+< 1.456 >
+ echo abs(-5.456)
+< 5.456 >
+ echo abs(-4)
+< 4
+
+ Can also be used as a |method|: >
+ Compute()->abs()
+
+acos({expr}) *acos()*
+ Return the arc cosine of {expr} measured in radians, as a
+ |Float| in the range of [0, pi].
+ {expr} must evaluate to a |Float| or a |Number| in the range
+ [-1, 1].
+ Returns NaN if {expr} is outside the range [-1, 1]. Returns
+ 0.0 if {expr} is not a |Float| or a |Number|.
+ Examples: >
+ :echo acos(0)
+< 1.570796 >
+ :echo acos(-0.5)
+< 2.094395
+
+ Can also be used as a |method|: >
+ Compute()->acos()
+
+add({object}, {expr}) *add()*
+ Append the item {expr} to |List| or |Blob| {object}. Returns
+ the resulting |List| or |Blob|. Examples: >
+ :let alist = add([1, 2, 3], item)
+ :call add(mylist, "woodstock")
+< Note that when {expr} is a |List| it is appended as a single
+ item. Use |extend()| to concatenate |Lists|.
+ When {object} is a |Blob| then {expr} must be a number.
+ Use |insert()| to add an item at another position.
+ Returns 1 if {object} is not a |List| or a |Blob|.
+
+ Can also be used as a |method|: >
+ mylist->add(val1)->add(val2)
+
+and({expr}, {expr}) *and()*
+ Bitwise AND on the two arguments. The arguments are converted
+ to a number. A List, Dict or Float argument causes an error.
+ Example: >
+ :let flag = and(bits, 0x80)
+< Can also be used as a |method|: >
+ :let flag = bits->and(0x80)
+
+api_info() *api_info()*
+ Returns Dictionary of |api-metadata|.
+
+ View it in a nice human-readable format: >
+ :lua print(vim.inspect(vim.fn.api_info()))
+
+append({lnum}, {text}) *append()*
+ When {text} is a |List|: Append each item of the |List| as a
+ text line below line {lnum} in the current buffer.
+ Otherwise append {text} as one text line below line {lnum} in
+ the current buffer.
+ {lnum} can be zero to insert a line before the first one.
+ {lnum} is used like with |getline()|.
+ Returns 1 for failure ({lnum} out of range or out of memory),
+ 0 for success. Example: >
+ :let failed = append(line('$'), "# THE END")
+ :let failed = append(0, ["Chapter 1", "the beginning"])
+
+< Can also be used as a |method| after a List: >
+ mylist->append(lnum)
+
+appendbufline({buf}, {lnum}, {text}) *appendbufline()*
+ Like |append()| but append the text in buffer {expr}.
+
+ This function works only for loaded buffers. First call
+ |bufload()| if needed.
+
+ For the use of {buf}, see |bufname()|.
+
+ {lnum} is used like with |append()|. Note that using |line()|
+ would use the current buffer, not the one appending to.
+ Use "$" to append at the end of the buffer.
+
+ On success 0 is returned, on failure 1 is returned.
+
+ If {buf} is not a valid buffer or {lnum} is not valid, an
+ error message is given. Example: >
+ :let failed = appendbufline(13, 0, "# THE START")
+<
+ Can also be used as a |method| after a List: >
+ mylist->appendbufline(buf, lnum)
+
+argc([{winid}]) *argc()*
+ The result is the number of files in the argument list. See
+ |arglist|.
+ If {winid} is not supplied, the argument list of the current
+ window is used.
+ If {winid} is -1, the global argument list is used.
+ Otherwise {winid} specifies the window of which the argument
+ list is used: either the window number or the window ID.
+ Returns -1 if the {winid} argument is invalid.
+
+ *argidx()*
+argidx() The result is the current index in the argument list. 0 is
+ the first file. argc() - 1 is the last one. See |arglist|.
+
+ *arglistid()*
+arglistid([{winnr} [, {tabnr}]])
+ Return the argument list ID. This is a number which
+ identifies the argument list being used. Zero is used for the
+ global argument list. See |arglist|.
+ Returns -1 if the arguments are invalid.
+
+ Without arguments use the current window.
+ With {winnr} only use this window in the current tab page.
+ With {winnr} and {tabnr} use the window in the specified tab
+ page.
+ {winnr} can be the window number or the |window-ID|.
+
+ *argv()*
+argv([{nr} [, {winid}]])
+ The result is the {nr}th file in the argument list. See
+ |arglist|. "argv(0)" is the first one. Example: >
+ :let i = 0
+ :while i < argc()
+ : let f = escape(fnameescape(argv(i)), '.')
+ : exe 'amenu Arg.' .. f .. ' :e ' .. f .. '<CR>'
+ : let i = i + 1
+ :endwhile
+< Without the {nr} argument, or when {nr} is -1, a |List| with
+ the whole |arglist| is returned.
+
+ The {winid} argument specifies the window ID, see |argc()|.
+ For the Vim command line arguments see |v:argv|.
+
+ Returns an empty string if {nr}th argument is not present in
+ the argument list. Returns an empty List if the {winid}
+ argument is invalid.
+
+asin({expr}) *asin()*
+ Return the arc sine of {expr} measured in radians, as a |Float|
+ in the range of [-pi/2, pi/2].
+ {expr} must evaluate to a |Float| or a |Number| in the range
+ [-1, 1].
+ Returns NaN if {expr} is outside the range [-1, 1]. Returns
+ 0.0 if {expr} is not a |Float| or a |Number|.
+ Examples: >
+ :echo asin(0.8)
+< 0.927295 >
+ :echo asin(-0.5)
+< -0.523599
+
+ Can also be used as a |method|: >
+ Compute()->asin()
+
+
+assert_ functions are documented here: |assert-functions-details|
+
+
+atan({expr}) *atan()*
+ Return the principal value of the arc tangent of {expr}, in
+ the range [-pi/2, +pi/2] radians, as a |Float|.
+ {expr} must evaluate to a |Float| or a |Number|.
+ Returns 0.0 if {expr} is not a |Float| or a |Number|.
+ Examples: >
+ :echo atan(100)
+< 1.560797 >
+ :echo atan(-4.01)
+< -1.326405
+
+ Can also be used as a |method|: >
+ Compute()->atan()
+
+atan2({expr1}, {expr2}) *atan2()*
+ Return the arc tangent of {expr1} / {expr2}, measured in
+ radians, as a |Float| in the range [-pi, pi].
+ {expr1} and {expr2} must evaluate to a |Float| or a |Number|.
+ Returns 0.0 if {expr1} or {expr2} is not a |Float| or a
+ |Number|.
+ Examples: >
+ :echo atan2(-1, 1)
+< -0.785398 >
+ :echo atan2(1, -1)
+< 2.356194
+
+ Can also be used as a |method|: >
+ Compute()->atan2(1)
+
+ *browse()*
+browse({save}, {title}, {initdir}, {default})
+ Put up a file requester. This only works when "has("browse")"
+ returns |TRUE| (only in some GUI versions).
+ The input fields are:
+ {save} when |TRUE|, select file to write
+ {title} title for the requester
+ {initdir} directory to start browsing in
+ {default} default file name
+ An empty string is returned when the "Cancel" button is hit,
+ something went wrong, or browsing is not possible.
+
+ *browsedir()*
+browsedir({title}, {initdir})
+ Put up a directory requester. This only works when
+ "has("browse")" returns |TRUE| (only in some GUI versions).
+ On systems where a directory browser is not supported a file
+ browser is used. In that case: select a file in the directory
+ to be used.
+ The input fields are:
+ {title} title for the requester
+ {initdir} directory to start browsing in
+ When the "Cancel" button is hit, something went wrong, or
+ browsing is not possible, an empty string is returned.
+
+bufadd({name}) *bufadd()*
+ Add a buffer to the buffer list with String {name}.
+ If a buffer for file {name} already exists, return that buffer
+ number. Otherwise return the buffer number of the newly
+ created buffer. When {name} is an empty string then a new
+ buffer is always created.
+ The buffer will not have 'buflisted' set and not be loaded
+ yet. To add some text to the buffer use this: >
+ let bufnr = bufadd('someName')
+ call bufload(bufnr)
+ call setbufline(bufnr, 1, ['some', 'text'])
+< Returns 0 on error.
+ Can also be used as a |method|: >
+ let bufnr = 'somename'->bufadd()
+
+bufexists({buf}) *bufexists()*
+ The result is a Number, which is |TRUE| if a buffer called
+ {buf} exists.
+ If the {buf} argument is a number, buffer numbers are used.
+ Number zero is the alternate buffer for the current window.
+
+ If the {buf} argument is a string it must match a buffer name
+ exactly. The name can be:
+ - Relative to the current directory.
+ - A full path.
+ - The name of a buffer with 'buftype' set to "nofile".
+ - A URL name.
+ Unlisted buffers will be found.
+ Note that help files are listed by their short name in the
+ output of |:buffers|, but bufexists() requires using their
+ long name to be able to find them.
+ bufexists() may report a buffer exists, but to use the name
+ with a |:buffer| command you may need to use |expand()|. Esp
+ for MS-Windows 8.3 names in the form "c:\DOCUME~1"
+ Use "bufexists(0)" to test for the existence of an alternate
+ file name.
+
+ Can also be used as a |method|: >
+ let exists = 'somename'->bufexists()
+
+buflisted({buf}) *buflisted()*
+ The result is a Number, which is |TRUE| if a buffer called
+ {buf} exists and is listed (has the 'buflisted' option set).
+ The {buf} argument is used like with |bufexists()|.
+
+ Can also be used as a |method|: >
+ let listed = 'somename'->buflisted()
+
+bufload({buf}) *bufload()*
+ Ensure the buffer {buf} is loaded. When the buffer name
+ refers to an existing file then the file is read. Otherwise
+ the buffer will be empty. If the buffer was already loaded
+ then there is no change.
+ If there is an existing swap file for the file of the buffer,
+ there will be no dialog, the buffer will be loaded anyway.
+ The {buf} argument is used like with |bufexists()|.
+
+ Can also be used as a |method|: >
+ eval 'somename'->bufload()
+
+bufloaded({buf}) *bufloaded()*
+ The result is a Number, which is |TRUE| if a buffer called
+ {buf} exists and is loaded (shown in a window or hidden).
+ The {buf} argument is used like with |bufexists()|.
+
+ Can also be used as a |method|: >
+ let loaded = 'somename'->bufloaded()
+
+bufname([{buf}]) *bufname()*
+ The result is the name of a buffer. Mostly as it is displayed
+ by the `:ls` command, but not using special names such as
+ "[No Name]".
+ If {buf} is omitted the current buffer is used.
+ If {buf} is a Number, that buffer number's name is given.
+ Number zero is the alternate buffer for the current window.
+ If {buf} is a String, it is used as a |file-pattern| to match
+ with the buffer names. This is always done like 'magic' is
+ set and 'cpoptions' is empty. When there is more than one
+ match an empty string is returned.
+ "" or "%" can be used for the current buffer, "#" for the
+ alternate buffer.
+ A full match is preferred, otherwise a match at the start, end
+ or middle of the buffer name is accepted. If you only want a
+ full match then put "^" at the start and "$" at the end of the
+ pattern.
+ Listed buffers are found first. If there is a single match
+ with a listed buffer, that one is returned. Next unlisted
+ buffers are searched for.
+ If the {buf} is a String, but you want to use it as a buffer
+ number, force it to be a Number by adding zero to it: >
+ :echo bufname("3" + 0)
+< Can also be used as a |method|: >
+ echo bufnr->bufname()
+
+< If the buffer doesn't exist, or doesn't have a name, an empty
+ string is returned. >
+ bufname("#") alternate buffer name
+ bufname(3) name of buffer 3
+ bufname("%") name of current buffer
+ bufname("file2") name of buffer where "file2" matches.
+<
+ *bufnr()*
+bufnr([{buf} [, {create}]])
+ The result is the number of a buffer, as it is displayed by
+ the `:ls` command. For the use of {buf}, see |bufname()|
+ above.
+ If the buffer doesn't exist, -1 is returned. Or, if the
+ {create} argument is present and TRUE, a new, unlisted,
+ buffer is created and its number is returned.
+ bufnr("$") is the last buffer: >
+ :let last_buffer = bufnr("$")
+< The result is a Number, which is the highest buffer number
+ of existing buffers. Note that not all buffers with a smaller
+ number necessarily exist, because ":bwipeout" may have removed
+ them. Use bufexists() to test for the existence of a buffer.
+
+ Can also be used as a |method|: >
+ echo bufref->bufnr()
+
+bufwinid({buf}) *bufwinid()*
+ The result is a Number, which is the |window-ID| of the first
+ window associated with buffer {buf}. For the use of {buf},
+ see |bufname()| above. If buffer {buf} doesn't exist or
+ there is no such window, -1 is returned. Example: >
+
+ echo "A window containing buffer 1 is " .. (bufwinid(1))
+<
+ Only deals with the current tab page.
+
+ Can also be used as a |method|: >
+ FindBuffer()->bufwinid()
+
+bufwinnr({buf}) *bufwinnr()*
+ Like |bufwinid()| but return the window number instead of the
+ |window-ID|.
+ If buffer {buf} doesn't exist or there is no such window, -1
+ is returned. Example: >
+
+ echo "A window containing buffer 1 is " .. (bufwinnr(1))
+
+< The number can be used with |CTRL-W_w| and ":wincmd w"
+ |:wincmd|.
+
+ Can also be used as a |method|: >
+ FindBuffer()->bufwinnr()
+
+byte2line({byte}) *byte2line()*
+ Return the line number that contains the character at byte
+ count {byte} in the current buffer. This includes the
+ end-of-line character, depending on the 'fileformat' option
+ for the current buffer. The first character has byte count
+ one.
+ Also see |line2byte()|, |go| and |:goto|.
+
+ Returns -1 if the {byte} value is invalid.
+
+ Can also be used as a |method|: >
+ GetOffset()->byte2line()
+
+byteidx({expr}, {nr}) *byteidx()*
+ Return byte index of the {nr}'th character in the String
+ {expr}. Use zero for the first character, it then returns
+ zero.
+ If there are no multibyte characters the returned value is
+ equal to {nr}.
+ Composing characters are not counted separately, their byte
+ length is added to the preceding base character. See
+ |byteidxcomp()| below for counting composing characters
+ separately.
+ Example : >
+ echo matchstr(str, ".", byteidx(str, 3))
+< will display the fourth character. Another way to do the
+ same: >
+ let s = strpart(str, byteidx(str, 3))
+ echo strpart(s, 0, byteidx(s, 1))
+< Also see |strgetchar()| and |strcharpart()|.
+
+ If there are less than {nr} characters -1 is returned.
+ If there are exactly {nr} characters the length of the string
+ in bytes is returned.
+
+ Can also be used as a |method|: >
+ GetName()->byteidx(idx)
+
+byteidxcomp({expr}, {nr}) *byteidxcomp()*
+ Like byteidx(), except that a composing character is counted
+ as a separate character. Example: >
+ let s = 'e' .. nr2char(0x301)
+ echo byteidx(s, 1)
+ echo byteidxcomp(s, 1)
+ echo byteidxcomp(s, 2)
+< The first and third echo result in 3 ('e' plus composing
+ character is 3 bytes), the second echo results in 1 ('e' is
+ one byte).
+
+ Can also be used as a |method|: >
+ GetName()->byteidxcomp(idx)
+
+call({func}, {arglist} [, {dict}]) *call()* *E699*
+ Call function {func} with the items in |List| {arglist} as
+ arguments.
+ {func} can either be a |Funcref| or the name of a function.
+ a:firstline and a:lastline are set to the cursor line.
+ Returns the return value of the called function.
+ {dict} is for functions with the "dict" attribute. It will be
+ used to set the local variable "self". |Dictionary-function|
+
+ Can also be used as a |method|: >
+ GetFunc()->call([arg, arg], dict)
+
+ceil({expr}) *ceil()*
+ Return the smallest integral value greater than or equal to
+ {expr} as a |Float| (round up).
+ {expr} must evaluate to a |Float| or a |Number|.
+ Examples: >
+ echo ceil(1.456)
+< 2.0 >
+ echo ceil(-5.456)
+< -5.0 >
+ echo ceil(4.0)
+< 4.0
+
+ Returns 0.0 if {expr} is not a |Float| or a |Number|.
+
+ Can also be used as a |method|: >
+ Compute()->ceil()
+
+changenr() *changenr()*
+ Return the number of the most recent change. This is the same
+ number as what is displayed with |:undolist| and can be used
+ with the |:undo| command.
+ When a change was made it is the number of that change. After
+ redo it is the number of the redone change. After undo it is
+ one less than the number of the undone change.
+ Returns 0 if the undo list is empty.
+
+chanclose({id} [, {stream}]) *chanclose()*
+ Close a channel or a specific stream associated with it.
+ For a job, {stream} can be one of "stdin", "stdout",
+ "stderr" or "rpc" (closes stdin/stdout for a job started
+ with `"rpc":v:true`) If {stream} is omitted, all streams
+ are closed. If the channel is a pty, this will then close the
+ pty master, sending SIGHUP to the job process.
+ For a socket, there is only one stream, and {stream} should be
+ ommited.
+
+chansend({id}, {data}) *chansend()*
+ 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. Returns the number of bytes
+ written if the write succeeded, 0 otherwise.
+ See |channel-bytes| for more information.
+
+ {data} may be a string, string convertible, |Blob|, or a list.
+ If {data} is a list, the items will be joined by newlines; any
+ newlines in an item will be sent as NUL. To send a final
+ newline, include a final empty string. Example: >
+ :call chansend(id, ["abc", "123\n456", ""])
+< will send "abc<NL>123<NUL>456<NL>".
+
+ chansend() writes raw data, not RPC messages. If the channel
+ was created with `"rpc":v:true` then the channel expects RPC
+ messages, use |rpcnotify()| and |rpcrequest()| instead.
+
+
+char2nr({string} [, {utf8}]) *char2nr()*
+ Return Number value of the first char in {string}.
+ Examples: >
+ char2nr(" ") returns 32
+ char2nr("ABC") returns 65
+ char2nr("á") returns 225
+ char2nr("á"[0]) returns 195
+ char2nr("\<M-x>") returns 128
+< Non-ASCII characters are always treated as UTF-8 characters.
+ {utf8} is ignored, it exists only for backwards-compatibility.
+ A combining character is a separate character.
+ |nr2char()| does the opposite.
+
+ Returns 0 if {string} is not a |String|.
+
+ Can also be used as a |method|: >
+ GetChar()->char2nr()
+<
+ *charcol()*
+charcol({expr}) Same as |col()| but returns the character index of the column
+ position given with {expr} instead of the byte position.
+
+ Example:
+ With the cursor on '세' in line 5 with text "여보세요": >
+ charcol('.') returns 3
+ col('.') returns 7
+
+< Can also be used as a |method|: >
+ GetPos()->col()
+<
+ *charidx()*
+charidx({string}, {idx} [, {countcc}])
+ Return the character index of the byte at {idx} in {string}.
+ The index of the first character is zero.
+ If there are no multibyte characters the returned value is
+ equal to {idx}.
+ When {countcc} is omitted or |FALSE|, then composing characters
+ are not counted separately, their byte length is
+ added to the preceding base character.
+ When {countcc} is |TRUE|, then composing characters are
+ counted as separate characters.
+ Returns -1 if the arguments are invalid or if {idx} is greater
+ than the index of the last byte in {string}. An error is
+ given if the first argument is not a string, the second
+ argument is not a number or when the third argument is present
+ and is not zero or one.
+ See |byteidx()| and |byteidxcomp()| for getting the byte index
+ from the character index.
+ Examples: >
+ echo charidx('áb́ć', 3) returns 1
+ echo charidx('áb́ć', 6, 1) returns 4
+ echo charidx('áb́ć', 16) returns -1
+<
+ Can also be used as a |method|: >
+ GetName()->charidx(idx)
+
+chdir({dir}) *chdir()*
+ Change the current working directory to {dir}. The scope of
+ the directory change depends on the directory of the current
+ window:
+ - If the current window has a window-local directory
+ (|:lcd|), then changes the window local directory.
+ - Otherwise, if the current tabpage has a local
+ directory (|:tcd|) then changes the tabpage local
+ directory.
+ - Otherwise, changes the global directory.
+ If successful, returns the previous working directory. Pass
+ this to another chdir() to restore the directory.
+ On failure, returns an empty string.
+
+ Example: >
+ let save_dir = chdir(newdir)
+ if save_dir
+ " ... do some work
+ call chdir(save_dir)
+ endif
+<
+cindent({lnum}) *cindent()*
+ Get the amount of indent for line {lnum} according the C
+ indenting rules, as with 'cindent'.
+ The indent is counted in spaces, the value of 'tabstop' is
+ relevant. {lnum} is used just like in |getline()|.
+ When {lnum} is invalid -1 is returned.
+ See |C-indenting|.
+
+ Can also be used as a |method|: >
+ GetLnum()->cindent()
+
+clearmatches([{win}]) *clearmatches()*
+ Clears all matches previously defined for the current window
+ by |matchadd()| and the |:match| commands.
+ If {win} is specified, use the window with this number or
+ window ID instead of the current window.
+
+ Can also be used as a |method|: >
+ GetWin()->clearmatches()
+<
+ *col()*
+col({expr}) The result is a Number, which is the byte index of the column
+ position given with {expr}. The accepted positions are:
+ . the cursor position
+ $ the end of the cursor line (the result is the
+ number of bytes in the cursor line plus one)
+ 'x position of mark x (if the mark is not set, 0 is
+ returned)
+ v In Visual mode: the start of the Visual area (the
+ cursor is the end). When not in Visual mode
+ returns the cursor position. Differs from |'<| in
+ that it's updated right away.
+ Additionally {expr} can be [lnum, col]: a |List| with the line
+ and column number. Most useful when the column is "$", to get
+ the last column of a specific line. When "lnum" or "col" is
+ out of range then col() returns zero.
+ To get the line number use |line()|. To get both use
+ |getpos()|.
+ For the screen column position use |virtcol()|. For the
+ character position use |charcol()|.
+ Note that only marks in the current file can be used.
+ Examples: >
+ col(".") column of cursor
+ col("$") length of cursor line plus one
+ col("'t") column of mark t
+ col("'" .. markname) column of mark markname
+< The first column is 1. Returns 0 if {expr} is invalid.
+ For an uppercase mark the column may actually be in another
+ buffer.
+ For the cursor position, when 'virtualedit' is active, the
+ column is one higher if the cursor is after the end of the
+ line. This can be used to obtain the column in Insert mode: >
+ :imap <F2> <C-O>:let save_ve = &ve<CR>
+ \<C-O>:set ve=all<CR>
+ \<C-O>:echo col(".") .. "\n" <Bar>
+ \let &ve = save_ve<CR>
+
+< Can also be used as a |method|: >
+ GetPos()->col()
+<
+
+complete({startcol}, {matches}) *complete()* *E785*
+ Set the matches for Insert mode completion.
+ Can only be used in Insert mode. You need to use a mapping
+ with CTRL-R = (see |i_CTRL-R|). It does not work after CTRL-O
+ or with an expression mapping.
+ {startcol} is the byte offset in the line where the completed
+ text start. The text up to the cursor is the original text
+ that will be replaced by the matches. Use col('.') for an
+ empty string. "col('.') - 1" will replace one character by a
+ match.
+ {matches} must be a |List|. Each |List| item is one match.
+ See |complete-items| for the kind of items that are possible.
+ "longest" in 'completeopt' is ignored.
+ Note that the after calling this function you need to avoid
+ inserting anything that would cause completion to stop.
+ The match can be selected with CTRL-N and CTRL-P as usual with
+ Insert mode completion. The popup menu will appear if
+ specified, see |ins-completion-menu|.
+ Example: >
+ inoremap <F5> <C-R>=ListMonths()<CR>
+
+ func! ListMonths()
+ call complete(col('.'), ['January', 'February', 'March',
+ \ 'April', 'May', 'June', 'July', 'August', 'September',
+ \ 'October', 'November', 'December'])
+ return ''
+ endfunc
+< This isn't very useful, but it shows how it works. Note that
+ an empty string is returned to avoid a zero being inserted.
+
+ Can also be used as a |method|, the base is passed as the
+ second argument: >
+ GetMatches()->complete(col('.'))
+
+complete_add({expr}) *complete_add()*
+ Add {expr} to the list of matches. Only to be used by the
+ function specified with the 'completefunc' option.
+ Returns 0 for failure (empty string or out of memory),
+ 1 when the match was added, 2 when the match was already in
+ the list.
+ See |complete-functions| for an explanation of {expr}. It is
+ the same as one item in the list that 'omnifunc' would return.
+
+ Can also be used as a |method|: >
+ GetMoreMatches()->complete_add()
+
+complete_check() *complete_check()*
+ Check for a key typed while looking for completion matches.
+ This is to be used when looking for matches takes some time.
+ Returns |TRUE| when searching for matches is to be aborted,
+ zero otherwise.
+ Only to be used by the function specified with the
+ 'completefunc' option.
+
+
+complete_info([{what}]) *complete_info()*
+ Returns a |Dictionary| with information about Insert mode
+ completion. See |ins-completion|.
+ The items are:
+ mode Current completion mode name string.
+ See |complete_info_mode| for the values.
+ pum_visible |TRUE| if popup menu is visible.
+ See |pumvisible()|.
+ items List of completion matches. Each item is a
+ dictionary containing the entries "word",
+ "abbr", "menu", "kind", "info" and "user_data".
+ See |complete-items|.
+ selected Selected item index. First index is zero.
+ Index is -1 if no item is selected (showing
+ typed text only, or the last completion after
+ no item is selected when using the <Up> or
+ <Down> keys)
+ inserted Inserted string. [NOT IMPLEMENT YET]
+
+ *complete_info_mode*
+ mode values are:
+ "" Not in completion mode
+ "keyword" Keyword completion |i_CTRL-X_CTRL-N|
+ "ctrl_x" Just pressed CTRL-X |i_CTRL-X|
+ "scroll" Scrolling with |i_CTRL-X_CTRL-E| or
+ |i_CTRL-X_CTRL-Y|
+ "whole_line" Whole lines |i_CTRL-X_CTRL-L|
+ "files" File names |i_CTRL-X_CTRL-F|
+ "tags" Tags |i_CTRL-X_CTRL-]|
+ "path_defines" Definition completion |i_CTRL-X_CTRL-D|
+ "path_patterns" Include completion |i_CTRL-X_CTRL-I|
+ "dictionary" Dictionary |i_CTRL-X_CTRL-K|
+ "thesaurus" Thesaurus |i_CTRL-X_CTRL-T|
+ "cmdline" Vim Command line |i_CTRL-X_CTRL-V|
+ "function" User defined completion |i_CTRL-X_CTRL-U|
+ "omni" Omni completion |i_CTRL-X_CTRL-O|
+ "spell" Spelling suggestions |i_CTRL-X_s|
+ "eval" |complete()| completion
+ "unknown" Other internal modes
+
+ If the optional {what} list argument is supplied, then only
+ the items listed in {what} are returned. Unsupported items in
+ {what} are silently ignored.
+
+ To get the position and size of the popup menu, see
+ |pum_getpos()|. It's also available in |v:event| during the
+ |CompleteChanged| event.
+
+ Returns an empty |Dictionary| on error.
+
+ Examples: >
+ " Get all items
+ call complete_info()
+ " Get only 'mode'
+ call complete_info(['mode'])
+ " Get only 'mode' and 'pum_visible'
+ call complete_info(['mode', 'pum_visible'])
+
+< Can also be used as a |method|: >
+ GetItems()->complete_info()
+<
+ *confirm()*
+confirm({msg} [, {choices} [, {default} [, {type}]]])
+ confirm() offers the user a dialog, from which a choice can be
+ made. It returns the number of the choice. For the first
+ choice this is 1.
+
+ {msg} is displayed in a dialog with {choices} as the
+ alternatives. When {choices} is missing or empty, "&OK" is
+ used (and translated).
+ {msg} is a String, use '\n' to include a newline. Only on
+ some systems the string is wrapped when it doesn't fit.
+
+ {choices} is a String, with the individual choices separated
+ by '\n', e.g. >
+ confirm("Save changes?", "&Yes\n&No\n&Cancel")
+< The letter after the '&' is the shortcut key for that choice.
+ Thus you can type 'c' to select "Cancel". The shortcut does
+ not need to be the first letter: >
+ confirm("file has been modified", "&Save\nSave &All")
+< For the console, the first letter of each choice is used as
+ the default shortcut key. Case is ignored.
+
+ The optional {type} String argument gives the type of dialog.
+ It can be one of these values: "Error", "Question", "Info",
+ "Warning" or "Generic". Only the first character is relevant.
+ When {type} is omitted, "Generic" is used.
+
+ The optional {type} argument gives the type of dialog. This
+ is only used for the icon of the Win32 GUI. It can be one of
+ these values: "Error", "Question", "Info", "Warning" or
+ "Generic". Only the first character is relevant.
+ When {type} is omitted, "Generic" is used.
+
+ If the user aborts the dialog by pressing <Esc>, CTRL-C,
+ or another valid interrupt key, confirm() returns 0.
+
+ An example: >
+ let choice = confirm("What do you want?",
+ \ "&Apples\n&Oranges\n&Bananas", 2)
+ if choice == 0
+ echo "make up your mind!"
+ elseif choice == 3
+ echo "tasteful"
+ else
+ echo "I prefer bananas myself."
+ endif
+< In a GUI dialog, buttons are used. The layout of the buttons
+ depends on the 'v' flag in 'guioptions'. If it is included,
+ the buttons are always put vertically. Otherwise, confirm()
+ tries to put the buttons in one horizontal line. If they
+ don't fit, a vertical layout is used anyway. For some systems
+ the horizontal layout is always used.
+
+ Can also be used as a |method|in: >
+ BuildMessage()->confirm("&Yes\n&No")
+<
+ *copy()*
+copy({expr}) Make a copy of {expr}. For Numbers and Strings this isn't
+ different from using {expr} directly.
+ When {expr} is a |List| a shallow copy is created. This means
+ that the original |List| can be changed without changing the
+ copy, and vice versa. But the items are identical, thus
+ changing an item changes the contents of both |Lists|.
+ A |Dictionary| is copied in a similar way as a |List|.
+ Also see |deepcopy()|.
+ Can also be used as a |method|: >
+ mylist->copy()
+
+cos({expr}) *cos()*
+ Return the cosine of {expr}, measured in radians, as a |Float|.
+ {expr} must evaluate to a |Float| or a |Number|.
+ Returns 0.0 if {expr} is not a |Float| or a |Number|.
+ Examples: >
+ :echo cos(100)
+< 0.862319 >
+ :echo cos(-4.01)
+< -0.646043
+
+ Can also be used as a |method|: >
+ Compute()->cos()
+
+cosh({expr}) *cosh()*
+ Return the hyperbolic cosine of {expr} as a |Float| in the range
+ [1, inf].
+ {expr} must evaluate to a |Float| or a |Number|.
+ Returns 0.0 if {expr} is not a |Float| or a |Number|.
+ Examples: >
+ :echo cosh(0.5)
+< 1.127626 >
+ :echo cosh(-0.5)
+< -1.127626
+
+ Can also be used as a |method|: >
+ Compute()->cosh()
+
+count({comp}, {expr} [, {ic} [, {start}]]) *count()*
+ Return the number of times an item with value {expr} appears
+ in |String|, |List| or |Dictionary| {comp}.
+
+ If {start} is given then start with the item with this index.
+ {start} can only be used with a |List|.
+
+ When {ic} is given and it's |TRUE| then case is ignored.
+
+ When {comp} is a string then the number of not overlapping
+ occurrences of {expr} is returned. Zero is returned when
+ {expr} is an empty string.
+
+ Can also be used as a |method|: >
+ mylist->count(val)
+<
+ *cscope_connection()*
+cscope_connection([{num} , {dbpath} [, {prepend}]])
+ Checks for the existence of a |cscope| connection. If no
+ parameters are specified, then the function returns:
+ 0, if there are no cscope connections;
+ 1, if there is at least one cscope connection.
+
+ If parameters are specified, then the value of {num}
+ determines how existence of a cscope connection is checked:
+
+ {num} Description of existence check
+ ----- ------------------------------
+ 0 Same as no parameters (e.g., "cscope_connection()").
+ 1 Ignore {prepend}, and use partial string matches for
+ {dbpath}.
+ 2 Ignore {prepend}, and use exact string matches for
+ {dbpath}.
+ 3 Use {prepend}, use partial string matches for both
+ {dbpath} and {prepend}.
+ 4 Use {prepend}, use exact string matches for both
+ {dbpath} and {prepend}.
+
+ Note: All string comparisons are case sensitive!
+
+ Examples. Suppose we had the following (from ":cs show"): >
+
+ # pid database name prepend path
+ 0 27664 cscope.out /usr/local
+<
+ Invocation Return Val ~
+ ---------- ---------- >
+ cscope_connection() 1
+ cscope_connection(1, "out") 1
+ cscope_connection(2, "out") 0
+ cscope_connection(3, "out") 0
+ cscope_connection(3, "out", "local") 1
+ cscope_connection(4, "out") 0
+ cscope_connection(4, "out", "local") 0
+ cscope_connection(4, "cscope.out", "/usr/local") 1
+<
+
+ctxget([{index}]) *ctxget()*
+ Returns a |Dictionary| representing the |context| at {index}
+ from the top of the |context-stack| (see |context-dict|).
+ If {index} is not given, it is assumed to be 0 (i.e.: top).
+
+ctxpop() *ctxpop()*
+ Pops and restores the |context| at the top of the
+ |context-stack|.
+
+ctxpush([{types}]) *ctxpush()*
+ Pushes the current editor state (|context|) on the
+ |context-stack|.
+ If {types} is given and is a |List| of |String|s, it specifies
+ which |context-types| to include in the pushed context.
+ Otherwise, all context types are included.
+
+ctxset({context} [, {index}]) *ctxset()*
+ Sets the |context| at {index} from the top of the
+ |context-stack| to that represented by {context}.
+ {context} is a Dictionary with context data (|context-dict|).
+ If {index} is not given, it is assumed to be 0 (i.e.: top).
+
+ctxsize() *ctxsize()*
+ Returns the size of the |context-stack|.
+
+cursor({lnum}, {col} [, {off}]) *cursor()*
+cursor({list})
+ Positions the cursor at the column (byte count) {col} in the
+ line {lnum}. The first column is one.
+
+ When there is one argument {list} this is used as a |List|
+ with two, three or four item:
+ [{lnum}, {col}]
+ [{lnum}, {col}, {off}]
+ [{lnum}, {col}, {off}, {curswant}]
+ This is like the return value of |getpos()| or |getcurpos()|,
+ but without the first item.
+
+ To position the cursor using the character count, use
+ |setcursorcharpos()|.
+
+ Does not change the jumplist.
+ If {lnum} is greater than the number of lines in the buffer,
+ the cursor will be positioned at the last line in the buffer.
+ If {lnum} is zero, the cursor will stay in the current line.
+ If {col} is greater than the number of bytes in the line,
+ the cursor will be positioned at the last character in the
+ line.
+ If {col} is zero, the cursor will stay in the current column.
+ If {curswant} is given it is used to set the preferred column
+ for vertical movement. Otherwise {col} is used.
+
+ When 'virtualedit' is used {off} specifies the offset in
+ screen columns from the start of the character. E.g., a
+ position within a <Tab> or after the last character.
+ Returns 0 when the position could be set, -1 otherwise.
+
+ Can also be used as a |method|: >
+ GetCursorPos()->cursor()
+
+deepcopy({expr} [, {noref}]) *deepcopy()* *E698*
+ Make a copy of {expr}. For Numbers and Strings this isn't
+ different from using {expr} directly.
+ When {expr} is a |List| a full copy is created. This means
+ that the original |List| can be changed without changing the
+ copy, and vice versa. When an item is a |List|, a copy for it
+ is made, recursively. Thus changing an item in the copy does
+ not change the contents of the original |List|.
+
+ When {noref} is omitted or zero a contained |List| or
+ |Dictionary| is only copied once. All references point to
+ this single copy. With {noref} set to 1 every occurrence of a
+ |List| or |Dictionary| results in a new copy. This also means
+ that a cyclic reference causes deepcopy() to fail.
+ *E724*
+ Nesting is possible up to 100 levels. When there is an item
+ that refers back to a higher level making a deep copy with
+ {noref} set to 1 will fail.
+ Also see |copy()|.
+
+ Can also be used as a |method|: >
+ GetObject()->deepcopy()
+
+delete({fname} [, {flags}]) *delete()*
+ Without {flags} or with {flags} empty: Deletes the file by the
+ name {fname}.
+
+ This also works when {fname} is a symbolic link. The symbolic
+ link itself is deleted, not what it points to.
+
+ When {flags} is "d": Deletes the directory by the name
+ {fname}. This fails when directory {fname} is not empty.
+
+ When {flags} is "rf": Deletes the directory by the name
+ {fname} and everything in it, recursively. BE CAREFUL!
+ Note: on MS-Windows it is not possible to delete a directory
+ that is being used.
+
+ The result is a Number, which is 0/false if the delete
+ operation was successful and -1/true when the deletion failed
+ or partly failed.
+
+ Can also be used as a |method|: >
+ GetName()->delete()
+
+deletebufline({buf}, {first} [, {last}]) *deletebufline()*
+ Delete lines {first} to {last} (inclusive) from buffer {buf}.
+ If {last} is omitted then delete line {first} only.
+ On success 0 is returned, on failure 1 is returned.
+
+ This function works only for loaded buffers. First call
+ |bufload()| if needed.
+
+ For the use of {buf}, see |bufname()| above.
+
+ {first} and {last} are used like with |getline()|. Note that
+ when using |line()| this refers to the current buffer. Use "$"
+ to refer to the last line in buffer {buf}.
+
+ Can also be used as a |method|: >
+ GetBuffer()->deletebufline(1)
+<
+dictwatcheradd({dict}, {pattern}, {callback}) *dictwatcheradd()*
+ Adds a watcher to a dictionary. A dictionary watcher is
+ identified by three components:
+
+ - A dictionary({dict});
+ - A key pattern({pattern}).
+ - A function({callback}).
+
+ After this is called, every change on {dict} and on keys
+ matching {pattern} will result in {callback} being invoked.
+
+ For example, to watch all global variables: >
+ silent! call dictwatcherdel(g:, '*', 'OnDictChanged')
+ function! OnDictChanged(d,k,z)
+ echomsg string(a:k) string(a:z)
+ endfunction
+ call dictwatcheradd(g:, '*', 'OnDictChanged')
+<
+ For now {pattern} only accepts very simple patterns that can
+ contain a '*' at the end of the string, in which case it will
+ match every key that begins with the substring before the '*'.
+ That means if '*' is not the last character of {pattern}, only
+ keys that are exactly equal as {pattern} will be matched.
+
+ The {callback} receives three arguments:
+
+ - The dictionary being watched.
+ - The key which changed.
+ - A dictionary containing the new and old values for the key.
+
+ The type of change can be determined by examining the keys
+ present on the third argument:
+
+ - If contains both `old` and `new`, the key was updated.
+ - If it contains only `new`, the key was added.
+ - If it contains only `old`, the key was deleted.
+
+ This function can be used by plugins to implement options with
+ validation and parsing logic.
+
+dictwatcherdel({dict}, {pattern}, {callback}) *dictwatcherdel()*
+ Removes a watcher added with |dictwatcheradd()|. All three
+ arguments must match the ones passed to |dictwatcheradd()| in
+ order for the watcher to be successfully deleted.
+
+ *did_filetype()*
+did_filetype() Returns |TRUE| when autocommands are being executed and the
+ FileType event has been triggered at least once. Can be used
+ to avoid triggering the FileType event again in the scripts
+ that detect the file type. |FileType|
+ Returns |FALSE| when `:setf FALLBACK` was used.
+ When editing another file, the counter is reset, thus this
+ really checks if the FileType event has been triggered for the
+ current buffer. This allows an autocommand that starts
+ editing another buffer to set 'filetype' and load a syntax
+ file.
+
+diff_filler({lnum}) *diff_filler()*
+ Returns the number of filler lines above line {lnum}.
+ These are the lines that were inserted at this point in
+ another diff'ed window. These filler lines are shown in the
+ display but don't exist in the buffer.
+ {lnum} is used like with |getline()|. Thus "." is the current
+ line, "'m" mark m, etc.
+ Returns 0 if the current window is not in diff mode.
+
+ Can also be used as a |method|: >
+ GetLnum()->diff_filler()
+
+diff_hlID({lnum}, {col}) *diff_hlID()*
+ Returns the highlight ID for diff mode at line {lnum} column
+ {col} (byte index). When the current line does not have a
+ diff change zero is returned.
+ {lnum} is used like with |getline()|. Thus "." is the current
+ line, "'m" mark m, etc.
+ {col} is 1 for the leftmost column, {lnum} is 1 for the first
+ line.
+ The highlight ID can be used with |synIDattr()| to obtain
+ syntax information about the highlighting.
+
+ Can also be used as a |method|: >
+ GetLnum()->diff_hlID(col)
+<
+
+digraph_get({chars}) *digraph_get()* *E1214*
+ Return the digraph of {chars}. This should be a string with
+ exactly two characters. If {chars} are not just two
+ characters, or the digraph of {chars} does not exist, an error
+ is given and an empty string is returned.
+
+ Also see |digraph_getlist()|.
+
+ Examples: >
+ " Get a built-in digraph
+ :echo digraph_get('00') " Returns '∞'
+
+ " Get a user-defined digraph
+ :call digraph_set('aa', 'あ')
+ :echo digraph_get('aa') " Returns 'あ'
+<
+ Can also be used as a |method|: >
+ GetChars()->digraph_get()
+<
+
+digraph_getlist([{listall}]) *digraph_getlist()*
+ Return a list of digraphs. If the {listall} argument is given
+ and it is TRUE, return all digraphs, including the default
+ digraphs. Otherwise, return only user-defined digraphs.
+
+ Also see |digraph_get()|.
+
+ Examples: >
+ " Get user-defined digraphs
+ :echo digraph_getlist()
+
+ " Get all the digraphs, including default digraphs
+ :echo digraph_getlist(1)
+<
+ Can also be used as a |method|: >
+ GetNumber()->digraph_getlist()
+<
+
+digraph_set({chars}, {digraph}) *digraph_set()*
+ Add digraph {chars} to the list. {chars} must be a string
+ with two characters. {digraph} is a string with one UTF-8
+ encoded character. *E1215*
+ Be careful, composing characters are NOT ignored. This
+ function is similar to |:digraphs| command, but useful to add
+ digraphs start with a white space.
+
+ The function result is v:true if |digraph| is registered. If
+ this fails an error message is given and v:false is returned.
+
+ If you want to define multiple digraphs at once, you can use
+ |digraph_setlist()|.
+
+ Example: >
+ call digraph_set(' ', 'あ')
+<
+ Can be used as a |method|: >
+ GetString()->digraph_set('あ')
+<
+
+digraph_setlist({digraphlist}) *digraph_setlist()*
+ Similar to |digraph_set()| but this function can add multiple
+ digraphs at once. {digraphlist} is a list composed of lists,
+ where each list contains two strings with {chars} and
+ {digraph} as in |digraph_set()|. *E1216*
+ Example: >
+ call digraph_setlist([['aa', 'あ'], ['ii', 'い']])
+<
+ It is similar to the following: >
+ for [chars, digraph] in [['aa', 'あ'], ['ii', 'い']]
+ call digraph_set(chars, digraph)
+ endfor
+< Except that the function returns after the first error,
+ following digraphs will not be added.
+
+ Can be used as a |method|: >
+ GetList()->digraph_setlist()
+<
+
+empty({expr}) *empty()*
+ Return the Number 1 if {expr} is empty, zero otherwise.
+ - A |List| or |Dictionary| is empty when it does not have any
+ items.
+ - A |String| is empty when its length is zero.
+ - A |Number| and |Float| are empty when their value is zero.
+ - |v:false| and |v:null| are empty, |v:true| is not.
+ - A |Blob| is empty when its length is zero.
+
+ Can also be used as a |method|: >
+ mylist->empty()
+
+environ() *environ()*
+ Return all of environment variables as dictionary. You can
+ check if an environment variable exists like this: >
+ :echo has_key(environ(), 'HOME')
+< Note that the variable name may be CamelCase; to ignore case
+ use this: >
+ :echo index(keys(environ()), 'HOME', 0, 1) != -1
+
+escape({string}, {chars}) *escape()*
+ Escape the characters in {chars} that occur in {string} with a
+ backslash. Example: >
+ :echo escape('c:\program files\vim', ' \')
+< results in: >
+ c:\\program\ files\\vim
+< Also see |shellescape()| and |fnameescape()|.
+
+ Can also be used as a |method|: >
+ GetText()->escape(' \')
+<
+ *eval()*
+eval({string}) Evaluate {string} and return the result. Especially useful to
+ turn the result of |string()| back into the original value.
+ This works for Numbers, Floats, Strings, Blobs and composites
+ of them. Also works for |Funcref|s that refer to existing
+ functions.
+
+ Can also be used as a |method|: >
+ argv->join()->eval()
+
+eventhandler() *eventhandler()*
+ Returns 1 when inside an event handler. That is that Vim got
+ interrupted while waiting for the user to type a character,
+ e.g., when dropping a file on Vim. This means interactive
+ commands cannot be used. Otherwise zero is returned.
+
+executable({expr}) *executable()*
+ This function checks if an executable with the name {expr}
+ exists. {expr} must be the name of the program without any
+ arguments.
+ executable() uses the value of $PATH and/or the normal
+ searchpath for programs. *PATHEXT*
+ On MS-Windows the ".exe", ".bat", etc. can optionally be
+ included. Then the extensions in $PATHEXT are tried. Thus if
+ "foo.exe" does not exist, "foo.exe.bat" can be found. If
+ $PATHEXT is not set then ".exe;.com;.bat;.cmd" is used. A dot
+ by itself can be used in $PATHEXT to try using the name
+ without an extension. When 'shell' looks like a Unix shell,
+ then the name is also tried without adding an extension.
+ On MS-Windows it only checks if the file exists and is not a
+ directory, not if it's really executable.
+ On Windows an executable in the same directory as Vim is
+ always found (it is added to $PATH at |startup|).
+ The result is a Number:
+ 1 exists
+ 0 does not exist
+ -1 not implemented on this system
+ |exepath()| can be used to get the full path of an executable.
+
+ Can also be used as a |method|: >
+ GetCommand()->executable()
+
+execute({command} [, {silent}]) *execute()*
+ Execute {command} and capture its output.
+ If {command} is a |String|, returns {command} output.
+ If {command} is a |List|, returns concatenated outputs.
+ Examples: >
+ echo execute('echon "foo"')
+< foo >
+ echo execute(['echon "foo"', 'echon "bar"'])
+< foobar
+
+ The optional {silent} argument can have these values:
+ "" no `:silent` used
+ "silent" `:silent` used
+ "silent!" `:silent!` used
+ The default is "silent". Note that with "silent!", unlike
+ `:redir`, error messages are dropped.
+
+ To get a list of lines use |split()| on the result: >
+ execute('args')->split("\n")
+
+< This function is not available in the |sandbox|.
+ Note: If nested, an outer execute() will not observe output of
+ the inner calls.
+ Note: Text attributes (highlights) are not captured.
+ To execute a command in another window than the current one
+ use `win_execute()`.
+
+ Can also be used as a |method|: >
+ GetCommand()->execute()
+
+exepath({expr}) *exepath()*
+ Returns the full path of {expr} if it is an executable and
+ given as a (partial or full) path or is found in $PATH.
+ Returns empty string otherwise.
+ If {expr} starts with "./" the |current-directory| is used.
+
+ Can also be used as a |method|: >
+ GetCommand()->exepath()
+<
+ *exists()*
+exists({expr}) The result is a Number, which is |TRUE| if {expr} is
+ defined, zero otherwise.
+
+ For checking for a supported feature use |has()|.
+ For checking if a file exists use |filereadable()|.
+
+ The {expr} argument is a string, which contains one of these:
+ varname internal variable (see
+ dict.key |internal-variables|). Also works
+ list[i] for |curly-braces-names|, |Dictionary|
+ entries, |List| items, etc.
+ Beware that evaluating an index may
+ cause an error message for an invalid
+ expression. E.g.: >
+ :let l = [1, 2, 3]
+ :echo exists("l[5]")
+< 0 >
+ :echo exists("l[xx]")
+< E121: Undefined variable: xx
+ 0
+ &option-name Vim option (only checks if it exists,
+ not if it really works)
+ +option-name Vim option that works.
+ $ENVNAME environment variable (could also be
+ done by comparing with an empty
+ string)
+ *funcname built-in function (see |functions|)
+ or user defined function (see
+ |user-function|). Also works for a
+ variable that is a Funcref.
+ :cmdname Ex command: built-in command, user
+ command or command modifier |:command|.
+ Returns:
+ 1 for match with start of a command
+ 2 full match with a command
+ 3 matches several user commands
+ To check for a supported command
+ always check the return value to be 2.
+ :2match The |:2match| command.
+ :3match The |:3match| command.
+ #event autocommand defined for this event
+ #event#pattern autocommand defined for this event and
+ pattern (the pattern is taken
+ literally and compared to the
+ autocommand patterns character by
+ character)
+ #group autocommand group exists
+ #group#event autocommand defined for this group and
+ event.
+ #group#event#pattern
+ autocommand defined for this group,
+ event and pattern.
+ ##event autocommand for this event is
+ supported.
+
+ Examples: >
+ exists("&mouse")
+ exists("$HOSTNAME")
+ exists("*strftime")
+ exists("*s:MyFunc")
+ exists("*MyFunc")
+ exists("bufcount")
+ exists(":Make")
+ exists("#CursorHold")
+ exists("#BufReadPre#*.gz")
+ exists("#filetypeindent")
+ exists("#filetypeindent#FileType")
+ exists("#filetypeindent#FileType#*")
+ exists("##ColorScheme")
+< There must be no space between the symbol (&/$/*/#) and the
+ name.
+ There must be no extra characters after the name, although in
+ a few cases this is ignored. That may become stricter in the
+ future, thus don't count on it!
+ Working example: >
+ exists(":make")
+< NOT working example: >
+ exists(":make install")
+
+< Note that the argument must be a string, not the name of the
+ variable itself. For example: >
+ exists(bufcount)
+< This doesn't check for existence of the "bufcount" variable,
+ but gets the value of "bufcount", and checks if that exists.
+
+ Can also be used as a |method|: >
+ Varname()->exists()
+
+exp({expr}) *exp()*
+ Return the exponential of {expr} as a |Float| in the range
+ [0, inf].
+ {expr} must evaluate to a |Float| or a |Number|.
+ Returns 0.0 if {expr} is not a |Float| or a |Number|.
+ Examples: >
+ :echo exp(2)
+< 7.389056 >
+ :echo exp(-1)
+< 0.367879
+
+ Can also be used as a |method|: >
+ Compute()->exp()
+
+debugbreak({pid}) *debugbreak()*
+ Specifically used to interrupt a program being debugged. It
+ will cause process {pid} to get a SIGTRAP. Behavior for other
+ processes is undefined. See |terminal-debugger|.
+ {Sends a SIGINT to a process {pid} other than MS-Windows}
+
+ Returns |TRUE| if successfully interrupted the program.
+ Otherwise returns |FALSE|.
+
+ Can also be used as a |method|: >
+ GetPid()->debugbreak()
+
+expand({string} [, {nosuf} [, {list}]]) *expand()*
+ Expand wildcards and the following special keywords in
+ {string}. 'wildignorecase' applies.
+
+ If {list} is given and it is |TRUE|, a List will be returned.
+ Otherwise the result is a String and when there are several
+ matches, they are separated by <NL> characters.
+
+ If the expansion fails, the result is an empty string. A name
+ for a non-existing file is not included, unless {string} does
+ not start with '%', '#' or '<', see below.
+
+ When {string} starts with '%', '#' or '<', the expansion is
+ done like for the |cmdline-special| variables with their
+ associated modifiers. Here is a short overview:
+
+ % current file name
+ # alternate file name
+ #n alternate file name n
+ <cfile> file name under the cursor
+ <afile> autocmd file name
+ <abuf> autocmd buffer number (as a String!)
+ <amatch> autocmd matched name
+ <sfile> sourced script file or function name
+ <slnum> sourced script line number or function
+ line number
+ <sflnum> script file line number, also when in
+ a function
+ <SID> "<SNR>123_" where "123" is the
+ current script ID |<SID>|
+ <cword> word under the cursor
+ <cWORD> WORD under the cursor
+ <client> the {clientid} of the last received
+ message
+ Modifiers:
+ :p expand to full path
+ :h head (last path component removed)
+ :t tail (last path component only)
+ :r root (one extension removed)
+ :e extension only
+
+ Example: >
+ :let &tags = expand("%:p:h") .. "/tags"
+< Note that when expanding a string that starts with '%', '#' or
+ '<', any following text is ignored. This does NOT work: >
+ :let doesntwork = expand("%:h.bak")
+< Use this: >
+ :let doeswork = expand("%:h") .. ".bak"
+< Also note that expanding "<cfile>" and others only returns the
+ referenced file name without further expansion. If "<cfile>"
+ is "~/.cshrc", you need to do another expand() to have the
+ "~/" expanded into the path of the home directory: >
+ :echo expand(expand("<cfile>"))
+<
+ There cannot be white space between the variables and the
+ following modifier. The |fnamemodify()| function can be used
+ to modify normal file names.
+
+ When using '%' or '#', and the current or alternate file name
+ is not defined, an empty string is used. Using "%:p" in a
+ buffer with no name, results in the current directory, with a
+ '/' added.
+
+ When {string} does not start with '%', '#' or '<', it is
+ expanded like a file name is expanded on the command line.
+ 'suffixes' and 'wildignore' are used, unless the optional
+ {nosuf} argument is given and it is |TRUE|.
+ Names for non-existing files are included. The "**" item can
+ be used to search in a directory tree. For example, to find
+ all "README" files in the current directory and below: >
+ :echo expand("**/README")
+<
+ expand() can also be used to expand variables and environment
+ variables that are only known in a shell. But this can be
+ slow, because a shell may be used to do the expansion. See
+ |expr-env-expand|.
+ The expanded variable is still handled like a list of file
+ names. When an environment variable cannot be expanded, it is
+ left unchanged. Thus ":echo expand('$FOOBAR')" results in
+ "$FOOBAR".
+
+ See |glob()| for finding existing files. See |system()| for
+ getting the raw output of an external command.
+
+ Can also be used as a |method|: >
+ Getpattern()->expand()
+
+expandcmd({string}) *expandcmd()*
+ Expand special items in String {string} like what is done for
+ an Ex command such as `:edit`. This expands special keywords,
+ like with |expand()|, and environment variables, anywhere in
+ {string}. "~user" and "~/path" are only expanded at the
+ start.
+ Returns the expanded string. Example: >
+ :echo expandcmd('make %<.o')
+
+< Can also be used as a |method|: >
+ GetCommand()->expandcmd()
+<
+extend({expr1}, {expr2} [, {expr3}]) *extend()*
+ {expr1} and {expr2} must be both |Lists| or both
+ |Dictionaries|.
+
+ If they are |Lists|: Append {expr2} to {expr1}.
+ If {expr3} is given insert the items of {expr2} before the
+ item with index {expr3} in {expr1}. When {expr3} is zero
+ insert before the first item. When {expr3} is equal to
+ len({expr1}) then {expr2} is appended.
+ Examples: >
+ :echo sort(extend(mylist, [7, 5]))
+ :call extend(mylist, [2, 3], 1)
+< When {expr1} is the same List as {expr2} then the number of
+ items copied is equal to the original length of the List.
+ E.g., when {expr3} is 1 you get N new copies of the first item
+ (where N is the original length of the List).
+ Use |add()| to concatenate one item to a list. To concatenate
+ two lists into a new list use the + operator: >
+ :let newlist = [1, 2, 3] + [4, 5]
+<
+ If they are |Dictionaries|:
+ Add all entries from {expr2} to {expr1}.
+ If a key exists in both {expr1} and {expr2} then {expr3} is
+ used to decide what to do:
+ {expr3} = "keep": keep the value of {expr1}
+ {expr3} = "force": use the value of {expr2}
+ {expr3} = "error": give an error message *E737*
+ When {expr3} is omitted then "force" is assumed.
+
+ {expr1} is changed when {expr2} is not empty. If necessary
+ make a copy of {expr1} first.
+ {expr2} remains unchanged.
+ When {expr1} is locked and {expr2} is not empty the operation
+ fails.
+ Returns {expr1}. Returns 0 on error.
+
+ Can also be used as a |method|: >
+ mylist->extend(otherlist)
+
+feedkeys({string} [, {mode}]) *feedkeys()*
+ Characters in {string} are queued for processing as if they
+ come from a mapping or were typed by the user.
+
+ By default the string is added to the end of the typeahead
+ buffer, thus if a mapping is still being executed the
+ characters come after them. Use the 'i' flag to insert before
+ other characters, they will be executed next, before any
+ characters from a mapping.
+
+ The function does not wait for processing of keys contained in
+ {string}.
+
+ To include special keys into {string}, use double-quotes
+ and "\..." notation |expr-quote|. For example,
+ feedkeys("\<CR>") simulates pressing of the <Enter> key. But
+ feedkeys('\<CR>') pushes 5 characters.
+ The |<Ignore>| keycode may be used to exit the
+ wait-for-character without doing anything.
+
+ {mode} is a String, which can contain these character flags:
+ 'm' Remap keys. This is default. If {mode} is absent,
+ keys are remapped.
+ 'n' Do not remap keys.
+ 't' Handle keys as if typed; otherwise they are handled as
+ if coming from a mapping. This matters for undo,
+ opening folds, etc.
+ 'i' Insert the string instead of appending (see above).
+ 'x' Execute commands until typeahead is empty. This is
+ similar to using ":normal!". You can call feedkeys()
+ several times without 'x' and then one time with 'x'
+ (possibly with an empty {string}) to execute all the
+ typeahead. Note that when Vim ends in Insert mode it
+ will behave as if <Esc> is typed, to avoid getting
+ stuck, waiting for a character to be typed before the
+ script continues.
+ Note that if you manage to call feedkeys() while
+ executing commands, thus calling it recursively, then
+ all typehead will be consumed by the last call.
+ '!' When used with 'x' will not end Insert mode. Can be
+ used in a test when a timer is set to exit Insert mode
+ a little later. Useful for testing CursorHoldI.
+
+ Return value is always 0.
+
+ Can also be used as a |method|: >
+ GetInput()->feedkeys()
+
+filereadable({file}) *filereadable()*
+ The result is a Number, which is |TRUE| when a file with the
+ name {file} exists, and can be read. If {file} doesn't exist,
+ or is a directory, the result is |FALSE|. {file} is any
+ expression, which is used as a String.
+ If you don't care about the file being readable you can use
+ |glob()|.
+ {file} is used as-is, you may want to expand wildcards first: >
+ echo filereadable('~/.vimrc')
+ 0
+ echo filereadable(expand('~/.vimrc'))
+ 1
+
+< Can also be used as a |method|: >
+ GetName()->filereadable()
+
+filewritable({file}) *filewritable()*
+ The result is a Number, which is 1 when a file with the
+ name {file} exists, and can be written. If {file} doesn't
+ exist, or is not writable, the result is 0. If {file} is a
+ directory, and we can write to it, the result is 2.
+
+ Can also be used as a |method|: >
+ GetName()->filewriteable()
+
+filter({expr1}, {expr2}) *filter()*
+ {expr1} must be a |List|, |Blob|, or a |Dictionary|.
+ For each item in {expr1} evaluate {expr2} and when the result
+ is zero remove the item from the |List| or |Dictionary|. For a
+ |Blob| each byte is removed.
+
+ {expr2} must be a |string| or |Funcref|.
+
+ If {expr2} is a |string|, inside {expr2} |v:val| has the value
+ of the current item. For a |Dictionary| |v:key| has the key
+ of the current item and for a |List| |v:key| has the index of
+ the current item. For a |Blob| |v:key| has the index of the
+ current byte.
+
+ Examples: >
+ call filter(mylist, 'v:val !~ "OLD"')
+< Removes the items where "OLD" appears. >
+ call filter(mydict, 'v:key >= 8')
+< Removes the items with a key below 8. >
+ call filter(var, 0)
+< Removes all the items, thus clears the |List| or |Dictionary|.
+
+ Note that {expr2} is the result of expression and is then
+ used as an expression again. Often it is good to use a
+ |literal-string| to avoid having to double backslashes.
+
+ If {expr2} is a |Funcref| it must take two arguments:
+ 1. the key or the index of the current item.
+ 2. the value of the current item.
+ The function must return |TRUE| if the item should be kept.
+ Example that keeps the odd items of a list: >
+ func Odd(idx, val)
+ return a:idx % 2 == 1
+ endfunc
+ call filter(mylist, function('Odd'))
+< It is shorter when using a |lambda|: >
+ call filter(myList, {idx, val -> idx * val <= 42})
+< If you do not use "val" you can leave it out: >
+ call filter(myList, {idx -> idx % 2 == 1})
+<
+ The operation is done in-place. If you want a |List| or
+ |Dictionary| to remain unmodified make a copy first: >
+ :let l = filter(copy(mylist), 'v:val =~ "KEEP"')
+
+< Returns {expr1}, the |List|, |Blob| or |Dictionary| that was
+ filtered. When an error is encountered while evaluating
+ {expr2} no further items in {expr1} are processed. When
+ {expr2} is a Funcref errors inside a function are ignored,
+ unless it was defined with the "abort" flag.
+
+ Can also be used as a |method|: >
+ mylist->filter(expr2)
+
+finddir({name} [, {path} [, {count}]]) *finddir()*
+ Find directory {name} in {path}. Supports both downwards and
+ upwards recursive directory searches. See |file-searching|
+ for the syntax of {path}.
+
+ Returns the path of the first found match. When the found
+ directory is below the current directory a relative path is
+ returned. Otherwise a full path is returned.
+ If {path} is omitted or empty then 'path' is used.
+
+ If the optional {count} is given, find {count}'s occurrence of
+ {name} in {path} instead of the first one.
+ When {count} is negative return all the matches in a |List|.
+
+ Returns an empty string if the directory is not found.
+
+ This is quite similar to the ex-command `:find`.
+
+ Can also be used as a |method|: >
+ GetName()->finddir()
+
+findfile({name} [, {path} [, {count}]]) *findfile()*
+ Just like |finddir()|, but find a file instead of a directory.
+ Uses 'suffixesadd'.
+ Example: >
+ :echo findfile("tags.vim", ".;")
+< Searches from the directory of the current file upwards until
+ it finds the file "tags.vim".
+
+ Can also be used as a |method|: >
+ GetName()->findfile()
+
+flatten({list} [, {maxdepth}]) *flatten()*
+ Flatten {list} up to {maxdepth} levels. Without {maxdepth}
+ the result is a |List| without nesting, as if {maxdepth} is
+ a very large number.
+ The {list} is changed in place, make a copy first if you do
+ not want that.
+ *E900*
+ {maxdepth} means how deep in nested lists changes are made.
+ {list} is not modified when {maxdepth} is 0.
+ {maxdepth} must be positive number.
+
+ If there is an error the number zero is returned.
+
+ Example: >
+ :echo flatten([1, [2, [3, 4]], 5])
+< [1, 2, 3, 4, 5] >
+ :echo flatten([1, [2, [3, 4]], 5], 1)
+< [1, 2, [3, 4], 5]
+
+ Can also be used as a |method|: >
+ mylist->flatten()
+<
+float2nr({expr}) *float2nr()*
+ Convert {expr} to a Number by omitting the part after the
+ decimal point.
+ {expr} must evaluate to a |Float| or a Number.
+ Returns 0 if {expr} is not a |Float| or a |Number|.
+ When the value of {expr} is out of range for a |Number| the
+ result is truncated to 0x7fffffff or -0x7fffffff (or when
+ 64-bit Number support is enabled, 0x7fffffffffffffff or
+ -0x7fffffffffffffff). NaN results in -0x80000000 (or when
+ 64-bit Number support is enabled, -0x8000000000000000).
+ Examples: >
+ echo float2nr(3.95)
+< 3 >
+ echo float2nr(-23.45)
+< -23 >
+ echo float2nr(1.0e100)
+< 2147483647 (or 9223372036854775807) >
+ echo float2nr(-1.0e150)
+< -2147483647 (or -9223372036854775807) >
+ echo float2nr(1.0e-100)
+< 0
+
+ Can also be used as a |method|: >
+ Compute()->float2nr()
+
+floor({expr}) *floor()*
+ Return the largest integral value less than or equal to
+ {expr} as a |Float| (round down).
+ {expr} must evaluate to a |Float| or a |Number|.
+ Returns 0.0 if {expr} is not a |Float| or a |Number|.
+ Examples: >
+ echo floor(1.856)
+< 1.0 >
+ echo floor(-5.456)
+< -6.0 >
+ echo floor(4.0)
+< 4.0
+
+ Can also be used as a |method|: >
+ Compute()->floor()
+
+fmod({expr1}, {expr2}) *fmod()*
+ Return the remainder of {expr1} / {expr2}, even if the
+ division is not representable. Returns {expr1} - i * {expr2}
+ for some integer i such that if {expr2} is non-zero, the
+ result has the same sign as {expr1} and magnitude less than
+ the magnitude of {expr2}. If {expr2} is zero, the value
+ returned is zero. The value returned is a |Float|.
+ {expr1} and {expr2} must evaluate to a |Float| or a |Number|.
+ Returns 0.0 if {expr1} or {expr2} is not a |Float| or a
+ |Number|.
+ Examples: >
+ :echo fmod(12.33, 1.22)
+< 0.13 >
+ :echo fmod(-12.33, 1.22)
+< -0.13
+
+ Can also be used as a |method|: >
+ Compute()->fmod(1.22)
+
+fnameescape({string}) *fnameescape()*
+ Escape {string} for use as file name command argument. All
+ characters that have a special meaning, such as '%' and '|'
+ are escaped with a backslash.
+ For most systems the characters escaped are
+ " \t\n*?[{`$\\%#'\"|!<". For systems where a backslash
+ appears in a filename, it depends on the value of 'isfname'.
+ A leading '+' and '>' is also escaped (special after |:edit|
+ and |:write|). And a "-" by itself (special after |:cd|).
+ Returns an empty string on error.
+ Example: >
+ :let fname = '+some str%nge|name'
+ :exe "edit " .. fnameescape(fname)
+< results in executing: >
+ edit \+some\ str\%nge\|name
+<
+ Can also be used as a |method|: >
+ GetName()->fnameescape()
+
+fnamemodify({fname}, {mods}) *fnamemodify()*
+ Modify file name {fname} according to {mods}. {mods} is a
+ string of characters like it is used for file names on the
+ command line. See |filename-modifiers|.
+ Example: >
+ :echo fnamemodify("main.c", ":p:h")
+< results in: >
+ /home/user/vim/vim/src
+< If {mods} is empty or an unsupported modifier is used then
+ {fname} is returned.
+ When {fname} is empty then with {mods} ":h" returns ".", so
+ that `:cd` can be used with it. This is different from
+ expand('%:h') without a buffer name, which returns an empty
+ string.
+ Note: Environment variables don't work in {fname}, use
+ |expand()| first then.
+
+ Can also be used as a |method|: >
+ GetName()->fnamemodify(':p:h')
+
+foldclosed({lnum}) *foldclosed()*
+ The result is a Number. If the line {lnum} is in a closed
+ fold, the result is the number of the first line in that fold.
+ If the line {lnum} is not in a closed fold, -1 is returned.
+ {lnum} is used like with |getline()|. Thus "." is the current
+ line, "'m" mark m, etc.
+
+ Can also be used as a |method|: >
+ GetLnum()->foldclosed()
+
+foldclosedend({lnum}) *foldclosedend()*
+ The result is a Number. If the line {lnum} is in a closed
+ fold, the result is the number of the last line in that fold.
+ If the line {lnum} is not in a closed fold, -1 is returned.
+ {lnum} is used like with |getline()|. Thus "." is the current
+ line, "'m" mark m, etc.
+
+ Can also be used as a |method|: >
+ GetLnum()->foldclosedend()
+
+foldlevel({lnum}) *foldlevel()*
+ The result is a Number, which is the foldlevel of line {lnum}
+ in the current buffer. For nested folds the deepest level is
+ returned. If there is no fold at line {lnum}, zero is
+ returned. It doesn't matter if the folds are open or closed.
+ When used while updating folds (from 'foldexpr') -1 is
+ returned for lines where folds are still to be updated and the
+ foldlevel is unknown. As a special case the level of the
+ previous line is usually available.
+ {lnum} is used like with |getline()|. Thus "." is the current
+ line, "'m" mark m, etc.
+
+ Can also be used as a |method|: >
+ GetLnum()->foldlevel()
+<
+ *foldtext()*
+foldtext() Returns a String, to be displayed for a closed fold. This is
+ the default function used for the 'foldtext' option and should
+ only be called from evaluating 'foldtext'. It uses the
+ |v:foldstart|, |v:foldend| and |v:folddashes| variables.
+ The returned string looks like this: >
+ +-- 45 lines: abcdef
+< The number of leading dashes depends on the foldlevel. The
+ "45" is the number of lines in the fold. "abcdef" is the text
+ in the first non-blank line of the fold. Leading white space,
+ "//" or "/*" and the text from the 'foldmarker' and
+ 'commentstring' options is removed.
+ When used to draw the actual foldtext, the rest of the line
+ will be filled with the fold char from the 'fillchars'
+ setting.
+ Returns an empty string when there is no fold.
+
+foldtextresult({lnum}) *foldtextresult()*
+ Returns the text that is displayed for the closed fold at line
+ {lnum}. Evaluates 'foldtext' in the appropriate context.
+ When there is no closed fold at {lnum} an empty string is
+ returned.
+ {lnum} is used like with |getline()|. Thus "." is the current
+ line, "'m" mark m, etc.
+ Useful when exporting folded text, e.g., to HTML.
+
+ Can also be used as a |method|: >
+ GetLnum()->foldtextresult()
+<
+fullcommand({name}) *fullcommand()*
+ Get the full command name from a short abbreviated command
+ name; see |20.2| for details on command abbreviations.
+
+ The string argument {name} may start with a `:` and can
+ include a [range], these are skipped and not returned.
+ Returns an empty string if a command doesn't exist or if it's
+ ambiguous (for user-defined commands).
+
+ For example `fullcommand('s')`, `fullcommand('sub')`,
+ `fullcommand(':%substitute')` all return "substitute".
+
+ Can also be used as a |method|: >
+ GetName()->fullcommand()
+<
+ *funcref()*
+funcref({name} [, {arglist}] [, {dict}])
+ Just like |function()|, but the returned Funcref will lookup
+ the function by reference, not by name. This matters when the
+ function {name} is redefined later.
+
+ Unlike |function()|, {name} must be an existing user 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.
+ Returns 0 on error.
+
+ Can also be used as a |method|: >
+ GetFuncname()->funcref([arg])
+<
+ *function()* *E700* *E922* *E923*
+function({name} [, {arglist}] [, {dict}])
+ Return a |Funcref| variable that refers to function {name}.
+ {name} can be a user defined function or an internal function.
+
+ {name} can also be a Funcref or a partial. When it is a
+ partial the dict stored in it will be used and the {dict}
+ argument is not allowed. E.g.: >
+ let FuncWithArg = function(dict.Func, [arg])
+ let Broken = function(dict.Func, [arg], dict)
+<
+ When using the Funcref the function will be found by {name},
+ also when it was redefined later. Use |funcref()| to keep the
+ same function.
+
+ When {arglist} or {dict} is present this creates a partial.
+ That means the argument list and/or the dictionary is stored in
+ the Funcref and will be used when the Funcref is called.
+
+ The arguments are passed to the function in front of other
+ arguments, but after any argument from |method|. Example: >
+ func Callback(arg1, arg2, name)
+ ...
+ let Partial = function('Callback', ['one', 'two'])
+ ...
+ call Partial('name')
+< Invokes the function as with: >
+ call Callback('one', 'two', 'name')
+
+< The Dictionary is only useful when calling a "dict" function.
+ In that case the {dict} is passed in as "self". Example: >
+ function Callback() dict
+ echo "called for " .. self.name
+ endfunction
+ ...
+ let context = {"name": "example"}
+ let Func = function('Callback', context)
+ ...
+ call Func() " will echo: called for example
+
+< The argument list and the Dictionary can be combined: >
+ function Callback(arg1, count) dict
+ ...
+ let context = {"name": "example"}
+ let Func = function('Callback', ['one'], context)
+ ...
+ call Func(500)
+< Invokes the function as with: >
+ call context.Callback('one', 500)
+<
+ Returns 0 on error.
+
+ Can also be used as a |method|: >
+ GetFuncname()->function([arg])
+
+garbagecollect([{atexit}]) *garbagecollect()*
+ Cleanup unused |Lists| and |Dictionaries| that have circular
+ references.
+
+ There is hardly ever a need to invoke this function, as it is
+ automatically done when Vim runs out of memory or is waiting
+ for the user to press a key after 'updatetime'. Items without
+ circular references are always freed when they become unused.
+ This is useful if you have deleted a very big |List| and/or
+ |Dictionary| with circular references in a script that runs
+ for a long time.
+
+ When the optional {atexit} argument is one, garbage
+ collection will also be done when exiting Vim, if it wasn't
+ done before. This is useful when checking for memory leaks.
+
+ The garbage collection is not done immediately but only when
+ it's safe to perform. This is when waiting for the user to
+ type a character.
+
+get({list}, {idx} [, {default}]) *get()*
+ Get item {idx} from |List| {list}. When this item is not
+ available return {default}. Return zero when {default} is
+ omitted.
+ Can also be used as a |method|: >
+ mylist->get(idx)
+get({blob}, {idx} [, {default}])
+ Get byte {idx} from |Blob| {blob}. When this byte is not
+ available return {default}. Return -1 when {default} is
+ omitted.
+get({dict}, {key} [, {default}])
+ Get item with key {key} from |Dictionary| {dict}. When this
+ item is not available return {default}. Return zero when
+ {default} is omitted. Useful example: >
+ let val = get(g:, 'var_name', 'default')
+< This gets the value of g:var_name if it exists, and uses
+ 'default' when it does not exist.
+get({func}, {what})
+ Get item {what} from Funcref {func}. Possible values for
+ {what} are:
+ "name" The function name
+ "func" The function
+ "dict" The dictionary
+ "args" The list with arguments
+ Returns zero on error.
+
+ *getbufinfo()*
+getbufinfo([{buf}])
+getbufinfo([{dict}])
+ Get information about buffers as a List of Dictionaries.
+
+ Without an argument information about all the buffers is
+ returned.
+
+ When the argument is a |Dictionary| only the buffers matching
+ the specified criteria are returned. The following keys can
+ be specified in {dict}:
+ buflisted include only listed buffers.
+ bufloaded include only loaded buffers.
+ bufmodified include only modified buffers.
+
+ Otherwise, {buf} specifies a particular buffer to return
+ information for. For the use of {buf}, see |bufname()|
+ above. If the buffer is found the returned List has one item.
+ Otherwise the result is an empty list.
+
+ Each returned List item is a dictionary with the following
+ entries:
+ bufnr Buffer number.
+ changed TRUE if the buffer is modified.
+ changedtick Number of changes made to the buffer.
+ hidden TRUE if the buffer is hidden.
+ lastused Timestamp in seconds, like
+ |localtime()|, when the buffer was
+ last used.
+ listed TRUE if the buffer is listed.
+ lnum Line number used for the buffer when
+ opened in the current window.
+ Only valid if the buffer has been
+ displayed in the window in the past.
+ If you want the line number of the
+ last known cursor position in a given
+ window, use |line()|: >
+ :echo line('.', {winid})
+<
+ linecount Number of lines in the buffer (only
+ valid when loaded)
+ loaded TRUE if the buffer is loaded.
+ name Full path to the file in the buffer.
+ signs List of signs placed in the buffer.
+ Each list item is a dictionary with
+ the following fields:
+ id sign identifier
+ lnum line number
+ name sign name
+ variables A reference to the dictionary with
+ buffer-local variables.
+ windows List of |window-ID|s that display this
+ buffer
+
+ Examples: >
+ for buf in getbufinfo()
+ echo buf.name
+ endfor
+ for buf in getbufinfo({'buflisted':1})
+ if buf.changed
+ ....
+ endif
+ endfor
+<
+ To get buffer-local options use: >
+ getbufvar({bufnr}, '&option_name')
+<
+ Can also be used as a |method|: >
+ GetBufnr()->getbufinfo()
+<
+ *getbufline()*
+getbufline({buf}, {lnum} [, {end}])
+ Return a |List| with the lines starting from {lnum} to {end}
+ (inclusive) in the buffer {buf}. If {end} is omitted, a
+ |List| with only the line {lnum} is returned.
+
+ For the use of {buf}, see |bufname()| above.
+
+ For {lnum} and {end} "$" can be used for the last line of the
+ buffer. Otherwise a number must be used.
+
+ When {lnum} is smaller than 1 or bigger than the number of
+ lines in the buffer, an empty |List| is returned.
+
+ When {end} is greater than the number of lines in the buffer,
+ it is treated as {end} is set to the number of lines in the
+ buffer. When {end} is before {lnum} an empty |List| is
+ returned.
+
+ This function works only for loaded buffers. For unloaded and
+ non-existing buffers, an empty |List| is returned.
+
+ Example: >
+ :let lines = getbufline(bufnr("myfile"), 1, "$")
+
+< Can also be used as a |method|: >
+ GetBufnr()->getbufline(lnum)
+
+getbufvar({buf}, {varname} [, {def}]) *getbufvar()*
+ The result is the value of option or local buffer variable
+ {varname} in buffer {buf}. Note that the name without "b:"
+ must be used.
+ The {varname} argument is a string.
+ When {varname} is empty returns a |Dictionary| with all the
+ buffer-local variables.
+ When {varname} is equal to "&" returns a |Dictionary| with all
+ the buffer-local options.
+ Otherwise, when {varname} starts with "&" returns the value of
+ a buffer-local option.
+ This also works for a global or buffer-local option, but it
+ doesn't work for a global variable, window-local variable or
+ window-local option.
+ For the use of {buf}, see |bufname()| above.
+ When the buffer or variable doesn't exist {def} or an empty
+ string is returned, there is no error message.
+ Examples: >
+ :let bufmodified = getbufvar(1, "&mod")
+ :echo "todo myvar = " .. getbufvar("todo", "myvar")
+
+< Can also be used as a |method|: >
+ GetBufnr()->getbufvar(varname)
+<
+getchangelist([{buf}]) *getchangelist()*
+ Returns the |changelist| for the buffer {buf}. For the use
+ of {buf}, see |bufname()| above. If buffer {buf} doesn't
+ exist, an empty list is returned.
+
+ The returned list contains two entries: a list with the change
+ locations and the current position in the list. Each
+ entry in the change list is a dictionary with the following
+ entries:
+ col column number
+ coladd column offset for 'virtualedit'
+ lnum line number
+ If buffer {buf} is the current buffer, then the current
+ position refers to the position in the list. For other
+ buffers, it is set to the length of the list.
+
+ Can also be used as a |method|: >
+ GetBufnr()->getchangelist()
+
+getchar([expr]) *getchar()*
+ Get a single character from the user or input stream.
+ If [expr] is omitted, wait until a character is available.
+ If [expr] is 0, only get a character when one is available.
+ Return zero otherwise.
+ If [expr] is 1, only check if a character is available, it is
+ not consumed. Return zero if no character available.
+ If you prefer always getting a string use |getcharstr()|.
+
+ Without [expr] and when [expr] is 0 a whole character or
+ special key is returned. If it is a single character, the
+ result is a Number. Use |nr2char()| to convert it to a String.
+ Otherwise a String is returned with the encoded character.
+ For a special key it's a String with a sequence of bytes
+ starting with 0x80 (decimal: 128). This is the same value as
+ the String "\<Key>", e.g., "\<Left>". The returned value is
+ also a String when a modifier (shift, control, alt) was used
+ that is not included in the character.
+
+ When [expr] is 0 and Esc is typed, there will be a short delay
+ while Vim waits to see if this is the start of an escape
+ sequence.
+
+ When [expr] is 1 only the first byte is returned. For a
+ one-byte character it is the character itself as a number.
+ Use nr2char() to convert it to a String.
+
+ Use getcharmod() to obtain any additional modifiers.
+
+ When the user clicks a mouse button, the mouse event will be
+ returned. The position can then be found in |v:mouse_col|,
+ |v:mouse_lnum|, |v:mouse_winid| and |v:mouse_win|.
+ |getmousepos()| can also be used. Mouse move events will be
+ ignored.
+ This example positions the mouse as it would normally happen: >
+ let c = getchar()
+ if c == "\<LeftMouse>" && v:mouse_win > 0
+ exe v:mouse_win .. "wincmd w"
+ exe v:mouse_lnum
+ exe "normal " .. v:mouse_col .. "|"
+ endif
+<
+ There is no prompt, you will somehow have to make clear to the
+ user that a character has to be typed. The screen is not
+ redrawn, e.g. when resizing the window.
+
+ There is no mapping for the character.
+ Key codes are replaced, thus when the user presses the <Del>
+ key you get the code for the <Del> key, not the raw character
+ sequence. Examples: >
+ getchar() == "\<Del>"
+ getchar() == "\<S-Left>"
+< This example redefines "f" to ignore case: >
+ :nmap f :call FindChar()<CR>
+ :function FindChar()
+ : let c = nr2char(getchar())
+ : while col('.') < col('$') - 1
+ : normal l
+ : if getline('.')[col('.') - 1] ==? c
+ : break
+ : endif
+ : endwhile
+ :endfunction
+<
+getcharmod() *getcharmod()*
+ The result is a Number which is the state of the modifiers for
+ the last obtained character with getchar() or in another way.
+ These values are added together:
+ 2 shift
+ 4 control
+ 8 alt (meta)
+ 16 meta (when it's different from ALT)
+ 32 mouse double click
+ 64 mouse triple click
+ 96 mouse quadruple click (== 32 + 64)
+ 128 command (Macintosh only)
+ Only the modifiers that have not been included in the
+ character itself are obtained. Thus Shift-a results in "A"
+ without a modifier. Returns 0 if no modifiers are used.
+
+ *getcharpos()*
+getcharpos({expr})
+ Get the position for String {expr}. Same as |getpos()| but the
+ column number in the returned List is a character index
+ instead of a byte index.
+
+ Example:
+ With the cursor on '세' in line 5 with text "여보세요": >
+ getcharpos('.') returns [0, 5, 3, 0]
+ getpos('.') returns [0, 5, 7, 0]
+<
+ Can also be used as a |method|: >
+ GetMark()->getcharpos()
+
+getcharsearch() *getcharsearch()*
+ Return the current character search information as a {dict}
+ with the following entries:
+
+ char character previously used for a character
+ search (|t|, |f|, |T|, or |F|); empty string
+ if no character search has been performed
+ forward direction of character search; 1 for forward,
+ 0 for backward
+ until type of character search; 1 for a |t| or |T|
+ character search, 0 for an |f| or |F|
+ character search
+
+ This can be useful to always have |;| and |,| search
+ forward/backward regardless of the direction of the previous
+ character search: >
+ :nnoremap <expr> ; getcharsearch().forward ? ';' : ','
+ :nnoremap <expr> , getcharsearch().forward ? ',' : ';'
+< Also see |setcharsearch()|.
+
+
+getcharstr([expr]) *getcharstr()*
+ Get a single character from the user or input stream as a
+ string.
+ If [expr] is omitted, wait until a character is available.
+ If [expr] is 0 or false, only get a character when one is
+ available. Return an empty string otherwise.
+ If [expr] is 1 or true, only check if a character is
+ available, it is not consumed. Return an empty string
+ if no character is available.
+ Otherwise this works like |getchar()|, except that a number
+ result is converted to a string.
+
+getcmdcompltype() *getcmdcompltype()*
+ Return the type of the current command-line completion.
+ Only works when the command line is being edited, thus
+ requires use of |c_CTRL-\_e| or |c_CTRL-R_=|.
+ See |:command-completion| for the return string.
+ Also see |getcmdtype()|, |setcmdpos()| and |getcmdline()|.
+ Returns an empty string when completion is not defined.
+
+getcmdline() *getcmdline()*
+ Return the current command-line. Only works when the command
+ line is being edited, thus requires use of |c_CTRL-\_e| or
+ |c_CTRL-R_=|.
+ Example: >
+ :cmap <F7> <C-\>eescape(getcmdline(), ' \')<CR>
+< Also see |getcmdtype()|, |getcmdpos()| and |setcmdpos()|.
+ Returns an empty string when entering a password or using
+ |inputsecret()|.
+
+getcmdpos() *getcmdpos()*
+ Return the position of the cursor in the command line as a
+ byte count. The first column is 1.
+ Only works when editing the command line, thus requires use of
+ |c_CTRL-\_e| or |c_CTRL-R_=| or an expression mapping.
+ Returns 0 otherwise.
+ Also see |getcmdtype()|, |setcmdpos()| and |getcmdline()|.
+
+getcmdscreenpos() *getcmdscreenpos()*
+ Return the screen position of the cursor in the command line
+ as a byte count. The first column is 1.
+ Instead of |getcmdpos()|, it adds the prompt position.
+ Only works when editing the command line, thus requires use of
+ |c_CTRL-\_e| or |c_CTRL-R_=| or an expression mapping.
+ Returns 0 otherwise.
+ Also see |getcmdpos()|, |setcmdpos()|.
+
+getcmdtype() *getcmdtype()*
+ Return the current command-line type. Possible return values
+ are:
+ : normal Ex command
+ > debug mode command |debug-mode|
+ / forward search command
+ ? backward search command
+ @ |input()| command
+ - |:insert| or |:append| command
+ = |i_CTRL-R_=|
+ Only works when editing the command line, thus requires use of
+ |c_CTRL-\_e| or |c_CTRL-R_=| or an expression mapping.
+ Returns an empty string otherwise.
+ Also see |getcmdpos()|, |setcmdpos()| and |getcmdline()|.
+
+getcmdwintype() *getcmdwintype()*
+ Return the current |command-line-window| type. Possible return
+ values are the same as |getcmdtype()|. Returns an empty string
+ when not in the command-line window.
+
+getcompletion({pat}, {type} [, {filtered}]) *getcompletion()*
+ Return a list of command-line completion matches. The String
+ {type} argument specifies what for. The following completion
+ types are supported:
+
+ arglist file names in argument list
+ augroup autocmd groups
+ buffer buffer names
+ behave :behave suboptions
+ cmdline |cmdline-completion| result
+ color color schemes
+ command Ex command
+ compiler compilers
+ cscope |:cscope| suboptions
+ diff_buffer |:diffget| and |:diffput| completion
+ dir directory names
+ environment environment variable names
+ event autocommand events
+ expression Vim expression
+ file file and directory names
+ file_in_path file and directory names in |'path'|
+ filetype filetype names |'filetype'|
+ function function name
+ help help subjects
+ highlight highlight groups
+ history :history suboptions
+ locale locale names (as output of locale -a)
+ mapclear buffer argument
+ mapping mapping name
+ menu menus
+ messages |:messages| suboptions
+ option options
+ packadd optional package |pack-add| names
+ shellcmd Shell command
+ sign |:sign| suboptions
+ syntax syntax file names |'syntax'|
+ syntime |:syntime| suboptions
+ tag tags
+ tag_listfiles tags, file names
+ user user names
+ var user variables
+
+ If {pat} is an empty string, then all the matches are
+ returned. Otherwise only items matching {pat} are returned.
+ See |wildcards| for the use of special characters in {pat}.
+
+ If the optional {filtered} flag is set to 1, then 'wildignore'
+ is applied to filter the results. Otherwise all the matches
+ are returned. The 'wildignorecase' option always applies.
+
+ If {type} is "cmdline", then the |cmdline-completion| result is
+ returned. For example, to complete the possible values after
+ a ":call" command: >
+ echo getcompletion('call ', 'cmdline')
+<
+ If there are no matches, an empty list is returned. An
+ invalid value for {type} produces an error.
+
+ Can also be used as a |method|: >
+ GetPattern()->getcompletion('color')
+<
+ *getcurpos()*
+getcurpos([{winid}])
+ Get the position of the cursor. This is like getpos('.'), but
+ includes an extra "curswant" in the list:
+ [0, lnum, col, off, curswant] ~
+ The "curswant" number is the preferred column when moving the
+ cursor vertically. Also see |getcursorcharpos()| and
+ |getpos()|.
+ The first "bufnum" item is always zero. The byte position of
+ the cursor is returned in 'col'. To get the character
+ position, use |getcursorcharpos()|.
+
+ The optional {winid} argument can specify the window. It can
+ be the window number or the |window-ID|. The last known
+ cursor position is returned, this may be invalid for the
+ current value of the buffer if it is not the current window.
+ If {winid} is invalid a list with zeroes is returned.
+
+ This can be used to save and restore the cursor position: >
+ let save_cursor = getcurpos()
+ MoveTheCursorAround
+ call setpos('.', save_cursor)
+< Note that this only works within the window. See
+ |winrestview()| for restoring more state.
+
+ Can also be used as a |method|: >
+ GetWinid()->getcurpos()
+<
+ *getcursorcharpos()*
+getcursorcharpos([{winid}])
+ Same as |getcurpos()| but the column number in the returned
+ List is a character index instead of a byte index.
+
+ Example:
+ With the cursor on '보' in line 3 with text "여보세요": >
+ getcursorcharpos() returns [0, 3, 2, 0, 3]
+ getcurpos() returns [0, 3, 4, 0, 3]
+<
+ Can also be used as a |method|: >
+ GetWinid()->getcursorcharpos()
+
+getcwd([{winnr} [, {tabnr}]]) *getcwd()*
+ With no arguments, returns the name of the effective
+ |current-directory|. With {winnr} or {tabnr} the working
+ directory of that scope is returned, and 'autochdir' is
+ ignored.
+ Tabs and windows are identified by their respective numbers,
+ 0 means current tab or window. Missing tab number implies 0.
+ Thus the following are equivalent: >
+ getcwd(0)
+ getcwd(0, 0)
+< If {winnr} is -1 it is ignored, only the tab is resolved.
+ {winnr} can be the window number or the |window-ID|.
+ If both {winnr} and {tabnr} are -1 the global working
+ directory is returned.
+ Throw error if the arguments are invalid. |E5000| |E5001| |E5002|
+
+ Can also be used as a |method|: >
+ GetWinnr()->getcwd()
+
+getenv({name}) *getenv()*
+ Return the value of environment variable {name}. The {name}
+ argument is a string, without a leading '$'. Example: >
+ myHome = getenv('HOME')
+
+< When the variable does not exist |v:null| is returned. That
+ is different from a variable set to an empty string.
+ See also |expr-env|.
+
+ Can also be used as a |method|: >
+ GetVarname()->getenv()
+
+getfontname([{name}]) *getfontname()*
+ Without an argument returns the name of the normal font being
+ used. Like what is used for the Normal highlight group
+ |hl-Normal|.
+ With an argument a check is done whether String {name} is a
+ valid font name. If not then an empty string is returned.
+ Otherwise the actual font name is returned, or {name} if the
+ GUI does not support obtaining the real name.
+ Only works when the GUI is running, thus not in your vimrc or
+ gvimrc file. Use the |GUIEnter| autocommand to use this
+ function just after the GUI has started.
+
+getfperm({fname}) *getfperm()*
+ The result is a String, which is the read, write, and execute
+ permissions of the given file {fname}.
+ If {fname} does not exist or its directory cannot be read, an
+ empty string is returned.
+ The result is of the form "rwxrwxrwx", where each group of
+ "rwx" flags represent, in turn, the permissions of the owner
+ of the file, the group the file belongs to, and other users.
+ If a user does not have a given permission the flag for this
+ is replaced with the string "-". Examples: >
+ :echo getfperm("/etc/passwd")
+ :echo getfperm(expand("~/.config/nvim/init.vim"))
+< This will hopefully (from a security point of view) display
+ the string "rw-r--r--" or even "rw-------".
+
+ Can also be used as a |method|: >
+ GetFilename()->getfperm()
+<
+ For setting permissions use |setfperm()|.
+
+getfsize({fname}) *getfsize()*
+ The result is a Number, which is the size in bytes of the
+ given file {fname}.
+ If {fname} is a directory, 0 is returned.
+ If the file {fname} can't be found, -1 is returned.
+ If the size of {fname} is too big to fit in a Number then -2
+ is returned.
+
+ Can also be used as a |method|: >
+ GetFilename()->getfsize()
+
+getftime({fname}) *getftime()*
+ The result is a Number, which is the last modification time of
+ the given file {fname}. The value is measured as seconds
+ since 1st Jan 1970, and may be passed to strftime(). See also
+ |localtime()| and |strftime()|.
+ If the file {fname} can't be found -1 is returned.
+
+ Can also be used as a |method|: >
+ GetFilename()->getftime()
+
+getftype({fname}) *getftype()*
+ The result is a String, which is a description of the kind of
+ file of the given file {fname}.
+ If {fname} does not exist an empty string is returned.
+ Here is a table over different kinds of files and their
+ results:
+ Normal file "file"
+ Directory "dir"
+ Symbolic link "link"
+ Block device "bdev"
+ Character device "cdev"
+ Socket "socket"
+ FIFO "fifo"
+ All other "other"
+ Example: >
+ getftype("/home")
+< Note that a type such as "link" will only be returned on
+ systems that support it. On some systems only "dir" and
+ "file" are returned.
+
+ Can also be used as a |method|: >
+ GetFilename()->getftype()
+
+getjumplist([{winnr} [, {tabnr}]]) *getjumplist()*
+ Returns the |jumplist| for the specified window.
+
+ Without arguments use the current window.
+ With {winnr} only use this window in the current tab page.
+ {winnr} can also be a |window-ID|.
+ With {winnr} and {tabnr} use the window in the specified tab
+ page. If {winnr} or {tabnr} is invalid, an empty list is
+ returned.
+
+ The returned list contains two entries: a list with the jump
+ locations and the last used jump position number in the list.
+ Each entry in the jump location list is a dictionary with
+ the following entries:
+ bufnr buffer number
+ col column number
+ coladd column offset for 'virtualedit'
+ filename filename if available
+ lnum line number
+
+ Can also be used as a |method|: >
+ GetWinnr()->getjumplist()
+
+< *getline()*
+getline({lnum} [, {end}])
+ Without {end} the result is a String, which is line {lnum}
+ from the current buffer. Example: >
+ getline(1)
+< When {lnum} is a String that doesn't start with a
+ digit, |line()| is called to translate the String into a Number.
+ To get the line under the cursor: >
+ getline(".")
+< When {lnum} is a number smaller than 1 or bigger than the
+ number of lines in the buffer, an empty string is returned.
+
+ When {end} is given the result is a |List| where each item is
+ a line from the current buffer in the range {lnum} to {end},
+ including line {end}.
+ {end} is used in the same way as {lnum}.
+ Non-existing lines are silently omitted.
+ When {end} is before {lnum} an empty |List| is returned.
+ Example: >
+ :let start = line('.')
+ :let end = search("^$") - 1
+ :let lines = getline(start, end)
+
+< Can also be used as a |method|: >
+ ComputeLnum()->getline()
+
+< To get lines from another buffer see |getbufline()|
+
+getloclist({nr} [, {what}]) *getloclist()*
+ Returns a |List| with all the entries in the location list for
+ window {nr}. {nr} can be the window number or the |window-ID|.
+ When {nr} is zero the current window is used.
+
+ For a location list window, the displayed location list is
+ returned. For an invalid window number {nr}, an empty list is
+ returned. Otherwise, same as |getqflist()|.
+
+ If the optional {what} dictionary argument is supplied, then
+ returns the items listed in {what} as a dictionary. Refer to
+ |getqflist()| for the supported items in {what}.
+
+ In addition to the items supported by |getqflist()| in {what},
+ the following item is supported by |getloclist()|:
+
+ filewinid id of the window used to display files
+ from the location list. This field is
+ applicable only when called from a
+ location list window. See
+ |location-list-file-window| for more
+ details.
+
+ Returns a |Dictionary| with default values if there is no
+ location list for the window {nr}.
+ Returns an empty Dictionary if window {nr} does not exist.
+
+ Examples (See also |getqflist-examples|): >
+ :echo getloclist(3, {'all': 0})
+ :echo getloclist(5, {'filewinid': 0})
+
+
+getmarklist([{buf}]) *getmarklist()*
+ Without the {buf} argument returns a |List| with information
+ about all the global marks. |mark|
+
+ If the optional {buf} argument is specified, returns the
+ local marks defined in buffer {buf}. For the use of {buf},
+ see |bufname()|. If {buf} is invalid, an empty list is
+ returned.
+
+ Each item in the returned List is a |Dict| with the following:
+ mark name of the mark prefixed by "'"
+ pos a |List| with the position of the mark:
+ [bufnum, lnum, col, off]
+ Refer to |getpos()| for more information.
+ file file name
+
+ Refer to |getpos()| for getting information about a specific
+ mark.
+
+ Can also be used as a |method|: >
+ GetBufnr()->getmarklist()
+
+getmatches([{win}]) *getmatches()*
+ Returns a |List| with all matches previously defined for the
+ current window by |matchadd()| and the |:match| commands.
+ |getmatches()| is useful in combination with |setmatches()|,
+ as |setmatches()| can restore a list of matches saved by
+ |getmatches()|.
+ If {win} is specified, use the window with this number or
+ window ID instead of the current window. If {win} is invalid,
+ an empty list is returned.
+ Example: >
+ :echo getmatches()
+< [{'group': 'MyGroup1', 'pattern': 'TODO',
+ 'priority': 10, 'id': 1}, {'group': 'MyGroup2',
+ 'pattern': 'FIXME', 'priority': 10, 'id': 2}] >
+ :let m = getmatches()
+ :call clearmatches()
+ :echo getmatches()
+< [] >
+ :call setmatches(m)
+ :echo getmatches()
+< [{'group': 'MyGroup1', 'pattern': 'TODO',
+ 'priority': 10, 'id': 1}, {'group': 'MyGroup2',
+ 'pattern': 'FIXME', 'priority': 10, 'id': 2}] >
+ :unlet m
+<
+getmousepos() *getmousepos()*
+ Returns a Dictionary with the last known position of the
+ mouse. This can be used in a mapping for a mouse click. The
+ items are:
+ screenrow screen row
+ screencol screen column
+ winid Window ID of the click
+ winrow row inside "winid"
+ wincol column inside "winid"
+ line text line inside "winid"
+ column text column inside "winid"
+ All numbers are 1-based.
+
+ If not over a window, e.g. when in the command line, then only
+ "screenrow" and "screencol" are valid, the others are zero.
+
+ When on the status line below a window or the vertical
+ separater right of a window, the "line" and "column" values
+ are zero.
+
+ When the position is after the text then "column" is the
+ length of the text in bytes plus one.
+
+ If the mouse is over a focusable floating window then that
+ window is used.
+
+ When using |getchar()| the Vim variables |v:mouse_lnum|,
+ |v:mouse_col| and |v:mouse_winid| also provide these values.
+
+ *getpid()*
+getpid() Return a Number which is the process ID of the Vim process.
+ This is a unique number, until Vim exits.
+
+ *getpos()*
+getpos({expr}) Get the position for String {expr}. For possible values of
+ {expr} see |line()|. For getting the cursor position see
+ |getcurpos()|.
+ The result is a |List| with four numbers:
+ [bufnum, lnum, col, off]
+ "bufnum" is zero, unless a mark like '0 or 'A is used, then it
+ is the buffer number of the mark.
+ "lnum" and "col" are the position in the buffer. The first
+ column is 1.
+ The "off" number is zero, unless 'virtualedit' is used. Then
+ it is the offset in screen columns from the start of the
+ character. E.g., a position within a <Tab> or after the last
+ character.
+ Note that for '< and '> Visual mode matters: when it is "V"
+ (visual line mode) the column of '< is zero and the column of
+ '> is a large number.
+ The column number in the returned List is the byte position
+ within the line. To get the character position in the line,
+ use |getcharpos()|.
+ The column number can be very large, e.g. 2147483647, in which
+ case it means "after the end of the line".
+ If {expr} is invalid, returns a list with all zeros.
+ This can be used to save and restore the position of a mark: >
+ let save_a_mark = getpos("'a")
+ ...
+ call setpos("'a", save_a_mark)
+< Also see |getcharpos()|, |getcurpos()| and |setpos()|.
+
+ Can also be used as a |method|: >
+ GetMark()->getpos()
+
+getqflist([{what}]) *getqflist()*
+ Returns a |List| with all the current quickfix errors. Each
+ list item is a dictionary with these entries:
+ bufnr number of buffer that has the file name, use
+ bufname() to get the name
+ module module name
+ lnum line number in the buffer (first line is 1)
+ end_lnum
+ end of line number if the item is multiline
+ col column number (first column is 1)
+ end_col end of column number if the item has range
+ vcol |TRUE|: "col" is visual column
+ |FALSE|: "col" is byte index
+ nr error number
+ pattern search pattern used to locate the error
+ text description of the error
+ type type of the error, 'E', '1', etc.
+ valid |TRUE|: recognized error message
+
+ When there is no error list or it's empty, an empty list is
+ returned. Quickfix list entries with a non-existing buffer
+ number are returned with "bufnr" set to zero (Note: some
+ functions accept buffer number zero for the alternate buffer,
+ you may need to explicitly check for zero).
+
+ Useful application: Find pattern matches in multiple files and
+ do something with them: >
+ :vimgrep /theword/jg *.c
+ :for d in getqflist()
+ : echo bufname(d.bufnr) ':' d.lnum '=' d.text
+ :endfor
+<
+ If the optional {what} dictionary argument is supplied, then
+ returns only the items listed in {what} as a dictionary. The
+ following string items are supported in {what}:
+ changedtick get the total number of changes made
+ to the list |quickfix-changedtick|
+ context get the |quickfix-context|
+ efm errorformat to use when parsing "lines". If
+ not present, then the 'errorformat' option
+ value is used.
+ id get information for the quickfix list with
+ |quickfix-ID|; zero means the id for the
+ current list or the list specified by "nr"
+ idx get information for the quickfix entry at this
+ index in the list specified by 'id' or 'nr'.
+ If set to zero, then uses the current entry.
+ See |quickfix-index|
+ items quickfix list entries
+ lines parse a list of lines using 'efm' and return
+ the resulting entries. Only a |List| type is
+ accepted. The current quickfix list is not
+ modified. See |quickfix-parse|.
+ nr get information for this quickfix list; zero
+ means the current quickfix list and "$" means
+ the last quickfix list
+ qfbufnr number of the buffer displayed in the quickfix
+ window. Returns 0 if the quickfix buffer is
+ not present. See |quickfix-buffer|.
+ size number of entries in the quickfix list
+ title get the list title |quickfix-title|
+ winid get the quickfix |window-ID|
+ all all of the above quickfix properties
+ Non-string items in {what} are ignored. To get the value of a
+ particular item, set it to zero.
+ If "nr" is not present then the current quickfix list is used.
+ If both "nr" and a non-zero "id" are specified, then the list
+ specified by "id" is used.
+ To get the number of lists in the quickfix stack, set "nr" to
+ "$" in {what}. The "nr" value in the returned dictionary
+ contains the quickfix stack size.
+ When "lines" is specified, all the other items except "efm"
+ are ignored. The returned dictionary contains the entry
+ "items" with the list of entries.
+
+ The returned dictionary contains the following entries:
+ changedtick total number of changes made to the
+ list |quickfix-changedtick|
+ context quickfix list context. See |quickfix-context|
+ If not present, set to "".
+ id quickfix list ID |quickfix-ID|. If not
+ present, set to 0.
+ idx index of the quickfix entry in the list. If not
+ present, set to 0.
+ items quickfix list entries. If not present, set to
+ an empty list.
+ nr quickfix list number. If not present, set to 0
+ qfbufnr number of the buffer displayed in the quickfix
+ window. If not present, set to 0.
+ size number of entries in the quickfix list. If not
+ present, set to 0.
+ title quickfix list title text. If not present, set
+ to "".
+ winid quickfix |window-ID|. If not present, set to 0
+
+ Examples (See also |getqflist-examples|): >
+ :echo getqflist({'all': 1})
+ :echo getqflist({'nr': 2, 'title': 1})
+ :echo getqflist({'lines' : ["F1:10:L10"]})
+<
+getreg([{regname} [, 1 [, {list}]]]) *getreg()*
+ The result is a String, which is the contents of register
+ {regname}. Example: >
+ :let cliptext = getreg('*')
+< When register {regname} was not set the result is an empty
+ string.
+ The {regname} argument must be a string.
+
+ getreg('=') returns the last evaluated value of the expression
+ register. (For use in maps.)
+ getreg('=', 1) returns the expression itself, so that it can
+ be restored with |setreg()|. For other registers the extra
+ argument is ignored, thus you can always give it.
+
+ If {list} is present and |TRUE|, the result type is changed
+ to |List|. Each list item is one text line. Use it if you care
+ about zero bytes possibly present inside register: without
+ third argument both NLs and zero bytes are represented as NLs
+ (see |NL-used-for-Nul|).
+ When the register was not set an empty list is returned.
+
+ If {regname} is not specified, |v:register| is used.
+
+ Can also be used as a |method|: >
+ GetRegname()->getreg()
+
+getreginfo([{regname}]) *getreginfo()*
+ Returns detailed information about register {regname} as a
+ Dictionary with the following entries:
+ regcontents List of lines contained in register
+ {regname}, like
+ |getreg|({regname}, 1, 1).
+ regtype the type of register {regname}, as in
+ |getregtype()|.
+ isunnamed Boolean flag, v:true if this register
+ is currently pointed to by the unnamed
+ register.
+ points_to for the unnamed register, gives the
+ single letter name of the register
+ currently pointed to (see |quotequote|).
+ For example, after deleting a line
+ with `dd`, this field will be "1",
+ which is the register that got the
+ deleted text.
+
+ The {regname} argument is a string. If {regname} is invalid
+ or not set, an empty Dictionary will be returned.
+ If {regname} is not specified, |v:register| is used.
+ The returned Dictionary can be passed to |setreg()|.
+
+ Can also be used as a |method|: >
+ GetRegname()->getreginfo()
+
+getregtype([{regname}]) *getregtype()*
+ The result is a String, which is type of register {regname}.
+ The value will be one of:
+ "v" for |charwise| text
+ "V" for |linewise| text
+ "<CTRL-V>{width}" for |blockwise-visual| text
+ "" for an empty or unknown register
+ <CTRL-V> is one character with value 0x16.
+ The {regname} argument is a string. If {regname} is not
+ specified, |v:register| is used.
+
+ Can also be used as a |method|: >
+ GetRegname()->getregtype()
+
+gettabinfo([{tabnr}]) *gettabinfo()*
+ If {tabnr} is not specified, then information about all the
+ tab pages is returned as a |List|. Each List item is a
+ |Dictionary|. Otherwise, {tabnr} specifies the tab page
+ number and information about that one is returned. If the tab
+ page does not exist an empty List is returned.
+
+ Each List item is a |Dictionary| with the following entries:
+ tabnr tab page number.
+ variables a reference to the dictionary with
+ tabpage-local variables
+ windows List of |window-ID|s in the tab page.
+
+ Can also be used as a |method|: >
+ GetTabnr()->gettabinfo()
+
+gettabvar({tabnr}, {varname} [, {def}]) *gettabvar()*
+ Get the value of a tab-local variable {varname} in tab page
+ {tabnr}. |t:var|
+ Tabs are numbered starting with one.
+ The {varname} argument is a string. When {varname} is empty a
+ dictionary with all tab-local variables is returned.
+ Note that the name without "t:" must be used.
+ When the tab or variable doesn't exist {def} or an empty
+ string is returned, there is no error message.
+
+ Can also be used as a |method|: >
+ GetTabnr()->gettabvar(varname)
+
+gettabwinvar({tabnr}, {winnr}, {varname} [, {def}]) *gettabwinvar()*
+ Get the value of window-local variable {varname} in window
+ {winnr} in tab page {tabnr}.
+ The {varname} argument is a string. When {varname} is empty a
+ dictionary with all window-local variables is returned.
+ When {varname} is equal to "&" get the values of all
+ window-local options in a |Dictionary|.
+ Otherwise, when {varname} starts with "&" get the value of a
+ window-local option.
+ Note that {varname} must be the name without "w:".
+ Tabs are numbered starting with one. For the current tabpage
+ use |getwinvar()|.
+ {winnr} can be the window number or the |window-ID|.
+ When {winnr} is zero the current window is used.
+ This also works for a global option, buffer-local option and
+ window-local option, but it doesn't work for a global variable
+ or buffer-local variable.
+ When the tab, window or variable doesn't exist {def} or an
+ empty string is returned, there is no error message.
+ Examples: >
+ :let list_is_on = gettabwinvar(1, 2, '&list')
+ :echo "myvar = " .. gettabwinvar(3, 1, 'myvar')
+<
+ To obtain all window-local variables use: >
+ gettabwinvar({tabnr}, {winnr}, '&')
+
+< Can also be used as a |method|: >
+ GetTabnr()->gettabwinvar(winnr, varname)
+
+gettagstack([{winnr}]) *gettagstack()*
+ The result is a Dict, which is the tag stack of window {winnr}.
+ {winnr} can be the window number or the |window-ID|.
+ When {winnr} is not specified, the current window is used.
+ When window {winnr} doesn't exist, an empty Dict is returned.
+
+ The returned dictionary contains the following entries:
+ curidx Current index in the stack. When at
+ top of the stack, set to (length + 1).
+ Index of bottom of the stack is 1.
+ items List of items in the stack. Each item
+ is a dictionary containing the
+ entries described below.
+ length Number of entries in the stack.
+
+ Each item in the stack is a dictionary with the following
+ entries:
+ bufnr buffer number of the current jump
+ from cursor position before the tag jump.
+ See |getpos()| for the format of the
+ returned list.
+ matchnr current matching tag number. Used when
+ multiple matching tags are found for a
+ name.
+ tagname name of the tag
+
+ See |tagstack| for more information about the tag stack.
+
+ Can also be used as a |method|: >
+ GetWinnr()->gettagstack()
+
+getwininfo([{winid}]) *getwininfo()*
+ Returns information about windows as a |List| with Dictionaries.
+
+ If {winid} is given Information about the window with that ID
+ is returned, as a |List| with one item. If the window does not
+ exist the result is an empty list.
+
+ Without {winid} information about all the windows in all the
+ tab pages is returned.
+
+ Each List item is a |Dictionary| with the following entries:
+ botline last complete displayed buffer line
+ bufnr number of buffer in the window
+ height window height (excluding winbar)
+ loclist 1 if showing a location list
+ quickfix 1 if quickfix or location list window
+ terminal 1 if a terminal window
+ tabnr tab page number
+ topline first displayed buffer line
+ variables a reference to the dictionary with
+ window-local variables
+ width window width
+ winbar 1 if the window has a toolbar, 0
+ otherwise
+ wincol leftmost screen column of the window;
+ "col" from |win_screenpos()|
+ textoff number of columns occupied by any
+ 'foldcolumn', 'signcolumn' and line
+ number in front of the text
+ winid |window-ID|
+ winnr window number
+ winrow topmost screen line of the window;
+ "row" from |win_screenpos()|
+
+ Can also be used as a |method|: >
+ GetWinnr()->getwininfo()
+
+getwinpos([{timeout}]) *getwinpos()*
+ The result is a |List| with two numbers, the result of
+ |getwinposx()| and |getwinposy()| combined:
+ [x-pos, y-pos]
+ {timeout} can be used to specify how long to wait in msec for
+ a response from the terminal. When omitted 100 msec is used.
+
+ Use a longer time for a remote terminal.
+ When using a value less than 10 and no response is received
+ within that time, a previously reported position is returned,
+ if available. This can be used to poll for the position and
+ do some work in the meantime: >
+ while 1
+ let res = getwinpos(1)
+ if res[0] >= 0
+ break
+ endif
+ " Do some work here
+ endwhile
+<
+ Can also be used as a |method|: >
+ GetTimeout()->getwinpos()
+<
+ *getwinposx()*
+getwinposx() The result is a Number, which is the X coordinate in pixels of
+ the left hand side of the GUI Vim window. The result will be
+ -1 if the information is not available.
+ The value can be used with `:winpos`.
+
+ *getwinposy()*
+getwinposy() The result is a Number, which is the Y coordinate in pixels of
+ the top of the GUI Vim window. The result will be -1 if the
+ information is not available.
+ The value can be used with `:winpos`.
+
+getwinvar({winnr}, {varname} [, {def}]) *getwinvar()*
+ Like |gettabwinvar()| for the current tabpage.
+ Examples: >
+ :let list_is_on = getwinvar(2, '&list')
+ :echo "myvar = " .. getwinvar(1, 'myvar')
+
+< Can also be used as a |method|: >
+ GetWinnr()->getwinvar(varname)
+<
+glob({expr} [, {nosuf} [, {list} [, {alllinks}]]]) *glob()*
+ Expand the file wildcards in {expr}. See |wildcards| for the
+ use of special characters.
+
+ Unless the optional {nosuf} argument is given and is |TRUE|,
+ the 'suffixes' and 'wildignore' options apply: Names matching
+ one of the patterns in 'wildignore' will be skipped and
+ 'suffixes' affect the ordering of matches.
+ 'wildignorecase' always applies.
+
+ When {list} is present and it is |TRUE| the result is a |List|
+ with all matching files. The advantage of using a List is,
+ you also get filenames containing newlines correctly.
+ Otherwise the result is a String and when there are several
+ matches, they are separated by <NL> characters.
+
+ If the expansion fails, the result is an empty String or List.
+
+ You can also use |readdir()| if you need to do complicated
+ things, such as limiting the number of matches.
+
+ A name for a non-existing file is not included. A symbolic
+ link is only included if it points to an existing file.
+ However, when the {alllinks} argument is present and it is
+ |TRUE| then all symbolic links are included.
+
+ For most systems backticks can be used to get files names from
+ any external command. Example: >
+ :let tagfiles = glob("`find . -name tags -print`")
+ :let &tags = substitute(tagfiles, "\n", ",", "g")
+< The result of the program inside the backticks should be one
+ item per line. Spaces inside an item are allowed.
+
+ See |expand()| for expanding special Vim variables. See
+ |system()| for getting the raw output of an external command.
+
+ Can also be used as a |method|: >
+ GetExpr()->glob()
+
+glob2regpat({string}) *glob2regpat()*
+ Convert a file pattern, as used by glob(), into a search
+ pattern. The result can be used to match with a string that
+ is a file name. E.g. >
+ if filename =~ glob2regpat('Make*.mak')
+< This is equivalent to: >
+ if filename =~ '^Make.*\.mak$'
+< When {string} is an empty string the result is "^$", match an
+ empty string.
+ Note that the result depends on the system. On MS-Windows
+ a backslash usually means a path separator.
+
+ Can also be used as a |method|: >
+ GetExpr()->glob2regpat()
+< *globpath()*
+globpath({path}, {expr} [, {nosuf} [, {list} [, {allinks}]]])
+ Perform glob() for String {expr} on all directories in {path}
+ and concatenate the results. Example: >
+ :echo globpath(&rtp, "syntax/c.vim")
+<
+ {path} is a comma-separated list of directory names. Each
+ directory name is prepended to {expr} and expanded like with
+ |glob()|. A path separator is inserted when needed.
+ To add a comma inside a directory name escape it with a
+ backslash. Note that on MS-Windows a directory may have a
+ trailing backslash, remove it if you put a comma after it.
+ If the expansion fails for one of the directories, there is no
+ error message.
+
+ Unless the optional {nosuf} argument is given and is |TRUE|,
+ the 'suffixes' and 'wildignore' options apply: Names matching
+ one of the patterns in 'wildignore' will be skipped and
+ 'suffixes' affect the ordering of matches.
+
+ When {list} is present and it is |TRUE| the result is a |List|
+ with all matching files. The advantage of using a List is, you
+ also get filenames containing newlines correctly. Otherwise
+ the result is a String and when there are several matches,
+ they are separated by <NL> characters. Example: >
+ :echo globpath(&rtp, "syntax/c.vim", 0, 1)
+<
+ {allinks} is used as with |glob()|.
+
+ The "**" item can be used to search in a directory tree.
+ For example, to find all "README.txt" files in the directories
+ in 'runtimepath' and below: >
+ :echo globpath(&rtp, "**/README.txt")
+< Upwards search and limiting the depth of "**" is not
+ supported, thus using 'path' will not always work properly.
+
+ Can also be used as a |method|, the base is passed as the
+ second argument: >
+ GetExpr()->globpath(&rtp)
+<
+ *has()*
+has({feature}) Returns 1 if {feature} is supported, 0 otherwise. The
+ {feature} argument is a feature name like "nvim-0.2.1" or
+ "win32", see below. See also |exists()|.
+
+ If the code has a syntax error, then Nvim may skip the rest
+ of the line and miss |:endif|. >
+ if has('feature') | let x = this->breaks->without->the->feature | endif
+<
+ Put |:if| and |:endif| on separate lines to avoid the
+ syntax error. >
+ if has('feature')
+ let x = this->breaks->without->the->feature
+ endif
+<
+ Vim's compile-time feature-names (prefixed with "+") are not
+ recognized because Nvim is always compiled with all possible
+ features. |feature-compile|
+
+ Feature names can be:
+ 1. Nvim version. For example the "nvim-0.2.1" feature means
+ that Nvim is version 0.2.1 or later: >
+ :if has("nvim-0.2.1")
+
+< 2. Runtime condition or other pseudo-feature. For example the
+ "win32" feature checks if the current system is Windows: >
+ :if has("win32")
+< *feature-list*
+ List of supported pseudo-feature names:
+ acl |ACL| support.
+ bsd BSD system (not macOS, use "mac" for that).
+ clipboard |clipboard| provider is available.
+ fname_case Case in file names matters (for Darwin and MS-Windows
+ this is not present).
+ iconv Can use |iconv()| for conversion.
+ linux Linux system.
+ mac MacOS system.
+ nvim This is Nvim.
+ python3 Legacy Vim |python3| interface. |has-python|
+ pythonx Legacy Vim |python_x| interface. |has-pythonx|
+ sun SunOS system.
+ ttyin input is a terminal (tty).
+ ttyout output is a terminal (tty).
+ unix Unix system.
+ *vim_starting* True during |startup|.
+ win32 Windows system (32 or 64 bit).
+ win64 Windows system (64 bit).
+ wsl WSL (Windows Subsystem for Linux) system.
+
+ *has-patch*
+ 3. Vim patch. For example the "patch123" feature means that
+ Vim patch 123 at the current |v:version| was included: >
+ :if v:version > 602 || v:version == 602 && has("patch148")
+
+< 4. Vim version. For example the "patch-7.4.237" feature means
+ that Nvim is Vim-compatible to version 7.4.237 or later. >
+ :if has("patch-7.4.237")
+
+
+has_key({dict}, {key}) *has_key()*
+ The result is a Number, which is TRUE if |Dictionary| {dict}
+ has an entry with key {key}. FALSE otherwise. The {key}
+ argument is a string.
+
+ Can also be used as a |method|: >
+ mydict->has_key(key)
+
+haslocaldir([{winnr} [, {tabnr}]]) *haslocaldir()*
+ The result is a Number, which is 1 when the window has set a
+ local path via |:lcd| or when {winnr} is -1 and the tabpage
+ has set a local path via |:tcd|, otherwise 0.
+
+ Tabs and windows are identified by their respective numbers,
+ 0 means current tab or window. Missing argument implies 0.
+ Thus the following are equivalent: >
+ haslocaldir()
+ haslocaldir(0)
+ haslocaldir(0, 0)
+< With {winnr} use that window in the current tabpage.
+ With {winnr} and {tabnr} use the window in that tabpage.
+ {winnr} can be the window number or the |window-ID|.
+ If {winnr} is -1 it is ignored, only the tab is resolved.
+ Throw error if the arguments are invalid. |E5000| |E5001| |E5002|
+
+ Can also be used as a |method|: >
+ GetWinnr()->haslocaldir()
+
+hasmapto({what} [, {mode} [, {abbr}]]) *hasmapto()*
+ The result is a Number, which is TRUE if there is a mapping
+ that contains {what} in somewhere in the rhs (what it is
+ mapped to) and this mapping exists in one of the modes
+ indicated by {mode}.
+ The arguments {what} and {mode} are strings.
+ When {abbr} is there and it is |TRUE| use abbreviations
+ instead of mappings. Don't forget to specify Insert and/or
+ Command-line mode.
+ Both the global mappings and the mappings local to the current
+ buffer are checked for a match.
+ If no matching mapping is found FALSE is returned.
+ The following characters are recognized in {mode}:
+ n Normal mode
+ v Visual and Select mode
+ x Visual mode
+ s Select mode
+ o Operator-pending mode
+ i Insert mode
+ l Language-Argument ("r", "f", "t", etc.)
+ c Command-line mode
+ When {mode} is omitted, "nvo" is used.
+
+ This function is useful to check if a mapping already exists
+ to a function in a Vim script. Example: >
+ :if !hasmapto('\ABCdoit')
+ : map <Leader>d \ABCdoit
+ :endif
+< This installs the mapping to "\ABCdoit" only if there isn't
+ already a mapping to "\ABCdoit".
+
+ Can also be used as a |method|: >
+ GetRHS()->hasmapto()
+
+histadd({history}, {item}) *histadd()*
+ Add the String {item} to the history {history} which can be
+ one of: *hist-names*
+ "cmd" or ":" command line history
+ "search" or "/" search pattern history
+ "expr" or "=" typed expression history
+ "input" or "@" input line history
+ "debug" or ">" debug command history
+ empty the current or last used history
+ The {history} string does not need to be the whole name, one
+ character is sufficient.
+ If {item} does already exist in the history, it will be
+ shifted to become the newest entry.
+ The result is a Number: TRUE if the operation was successful,
+ otherwise FALSE is returned.
+
+ Example: >
+ :call histadd("input", strftime("%Y %b %d"))
+ :let date=input("Enter date: ")
+< This function is not available in the |sandbox|.
+
+ Can also be used as a |method|, the base is passed as the
+ second argument: >
+ GetHistory()->histadd('search')
+
+histdel({history} [, {item}]) *histdel()*
+ Clear {history}, i.e. delete all its entries. See |hist-names|
+ for the possible values of {history}.
+
+ If the parameter {item} evaluates to a String, it is used as a
+ regular expression. All entries matching that expression will
+ be removed from the history (if there are any).
+ Upper/lowercase must match, unless "\c" is used |/\c|.
+ If {item} evaluates to a Number, it will be interpreted as
+ an index, see |:history-indexing|. The respective entry will
+ be removed if it exists.
+
+ The result is TRUE for a successful operation, otherwise FALSE
+ is returned.
+
+ Examples:
+ Clear expression register history: >
+ :call histdel("expr")
+<
+ Remove all entries starting with "*" from the search history: >
+ :call histdel("/", '^\*')
+<
+ The following three are equivalent: >
+ :call histdel("search", histnr("search"))
+ :call histdel("search", -1)
+ :call histdel("search", '^' .. histget("search", -1) .. '$')
+<
+ To delete the last search pattern and use the last-but-one for
+ the "n" command and 'hlsearch': >
+ :call histdel("search", -1)
+ :let @/ = histget("search", -1)
+<
+ Can also be used as a |method|: >
+ GetHistory()->histdel()
+
+histget({history} [, {index}]) *histget()*
+ The result is a String, the entry with Number {index} from
+ {history}. See |hist-names| for the possible values of
+ {history}, and |:history-indexing| for {index}. If there is
+ no such entry, an empty String is returned. When {index} is
+ omitted, the most recent item from the history is used.
+
+ Examples:
+ Redo the second last search from history. >
+ :execute '/' .. histget("search", -2)
+
+< Define an Ex command ":H {num}" that supports re-execution of
+ the {num}th entry from the output of |:history|. >
+ :command -nargs=1 H execute histget("cmd", 0+<args>)
+<
+ Can also be used as a |method|: >
+ GetHistory()->histget()
+
+histnr({history}) *histnr()*
+ The result is the Number of the current entry in {history}.
+ See |hist-names| for the possible values of {history}.
+ If an error occurred, -1 is returned.
+
+ Example: >
+ :let inp_index = histnr("expr")
+
+< Can also be used as a |method|: >
+ GetHistory()->histnr()
+<
+hlexists({name}) *hlexists()*
+ The result is a Number, which is TRUE if a highlight group
+ called {name} exists. This is when the group has been
+ defined in some way. Not necessarily when highlighting has
+ been defined for it, it may also have been used for a syntax
+ item.
+
+ Can also be used as a |method|: >
+ GetName()->hlexists()
+<
+ *hlID()*
+hlID({name}) The result is a Number, which is the ID of the highlight group
+ with name {name}. When the highlight group doesn't exist,
+ zero is returned.
+ This can be used to retrieve information about the highlight
+ group. For example, to get the background color of the
+ "Comment" group: >
+ :echo synIDattr(synIDtrans(hlID("Comment")), "bg")
+<
+ Can also be used as a |method|: >
+ GetName()->hlID()
+
+hostname() *hostname()*
+ The result is a String, which is the name of the machine on
+ which Vim is currently running. Machine names greater than
+ 256 characters long are truncated.
+
+iconv({string}, {from}, {to}) *iconv()*
+ The result is a String, which is the text {string} converted
+ from encoding {from} to encoding {to}.
+ When the conversion completely fails an empty string is
+ returned. When some characters could not be converted they
+ are replaced with "?".
+ The encoding names are whatever the iconv() library function
+ can accept, see ":!man 3 iconv".
+ Note that Vim uses UTF-8 for all Unicode encodings, conversion
+ from/to UCS-2 is automatically changed to use UTF-8. You
+ cannot use UCS-2 in a string anyway, because of the NUL bytes.
+
+ Can also be used as a |method|: >
+ GetText()->iconv('latin1', 'utf-8')
+<
+ *indent()*
+indent({lnum}) The result is a Number, which is indent of line {lnum} in the
+ current buffer. The indent is counted in spaces, the value
+ of 'tabstop' is relevant. {lnum} is used just like in
+ |getline()|.
+ When {lnum} is invalid -1 is returned.
+
+ Can also be used as a |method|: >
+ GetLnum()->indent()
+
+index({object}, {expr} [, {start} [, {ic}]]) *index()*
+ If {object} is a |List| return the lowest index where the item
+ has a value equal to {expr}. There is no automatic
+ conversion, so the String "4" is different from the Number 4.
+ And the Number 4 is different from the Float 4.0. The value
+ of 'ignorecase' is not used here, case always matters.
+
+ If {object} is a |Blob| return the lowest index where the byte
+ value is equal to {expr}.
+
+ If {start} is given then start looking at the item with index
+ {start} (may be negative for an item relative to the end).
+ When {ic} is given and it is |TRUE|, ignore case. Otherwise
+ case must match.
+ -1 is returned when {expr} is not found in {object}.
+ Example: >
+ :let idx = index(words, "the")
+ :if index(numbers, 123) >= 0
+
+< Can also be used as a |method|: >
+ GetObject()->index(what)
+
+input({prompt} [, {text} [, {completion}]]) *input()*
+input({opts})
+ The result is a String, which is whatever the user typed on
+ the command-line. The {prompt} argument is either a prompt
+ string, or a blank string (for no prompt). A '\n' can be used
+ in the prompt to start a new line.
+
+ In the second form it accepts a single dictionary with the
+ following keys, any of which may be omitted:
+
+ Key Default Description ~
+ prompt "" Same as {prompt} in the first form.
+ default "" Same as {text} in the first form.
+ completion nothing Same as {completion} in the first form.
+ cancelreturn "" The value returned when the dialog is
+ cancelled.
+ highlight nothing Highlight handler: |Funcref|.
+
+ The highlighting set with |:echohl| is used for the prompt.
+ The input is entered just like a command-line, with the same
+ editing commands and mappings. There is a separate history
+ for lines typed for input().
+ Example: >
+ :if input("Coffee or beer? ") == "beer"
+ : echo "Cheers!"
+ :endif
+<
+ If the optional {text} argument is present and not empty, this
+ is used for the default reply, as if the user typed this.
+ Example: >
+ :let color = input("Color? ", "white")
+
+< The optional {completion} argument specifies the type of
+ completion supported for the input. Without it completion is
+ not performed. The supported completion types are the same as
+ that can be supplied to a user-defined command using the
+ "-complete=" argument. Refer to |:command-completion| for
+ more information. Example: >
+ let fname = input("File: ", "", "file")
+
+< *input()-highlight* *E5400* *E5402*
+ The optional `highlight` key allows specifying function which
+ will be used for highlighting user input. This function
+ receives user input as its only argument and must return
+ a list of 3-tuples [hl_start_col, hl_end_col + 1, hl_group]
+ where
+ hl_start_col is the first highlighted column,
+ hl_end_col is the last highlighted column (+ 1!),
+ hl_group is |:hi| group used for highlighting.
+ *E5403* *E5404* *E5405* *E5406*
+ Both hl_start_col and hl_end_col + 1 must point to the start
+ of the multibyte character (highlighting must not break
+ multibyte characters), hl_end_col + 1 may be equal to the
+ input length. Start column must be in range [0, len(input)),
+ end column must be in range (hl_start_col, len(input)],
+ sections must be ordered so that next hl_start_col is greater
+ then or equal to previous hl_end_col.
+
+ Example (try some input with parentheses): >
+ highlight RBP1 guibg=Red ctermbg=red
+ highlight RBP2 guibg=Yellow ctermbg=yellow
+ highlight RBP3 guibg=Green ctermbg=green
+ highlight RBP4 guibg=Blue ctermbg=blue
+ let g:rainbow_levels = 4
+ function! RainbowParens(cmdline)
+ let ret = []
+ let i = 0
+ let lvl = 0
+ while i < len(a:cmdline)
+ if a:cmdline[i] is# '('
+ call add(ret, [i, i + 1, 'RBP' .. ((lvl % g:rainbow_levels) + 1)])
+ let lvl += 1
+ elseif a:cmdline[i] is# ')'
+ let lvl -= 1
+ call add(ret, [i, i + 1, 'RBP' .. ((lvl % g:rainbow_levels) + 1)])
+ endif
+ let i += 1
+ endwhile
+ return ret
+ endfunction
+ call input({'prompt':'>','highlight':'RainbowParens'})
+<
+ Highlight function is called at least once for each new
+ displayed input string, before command-line is redrawn. It is
+ expected that function is pure for the duration of one input()
+ call, i.e. it produces the same output for the same input, so
+ output may be memoized. Function is run like under |:silent|
+ modifier. If the function causes any errors, it will be
+ skipped for the duration of the current input() call.
+
+ Highlighting is disabled if command-line contains arabic
+ characters.
+
+ NOTE: This function must not be used in a startup file, for
+ the versions that only run in GUI mode (e.g., the Win32 GUI).
+ Note: When input() is called from within a mapping it will
+ consume remaining characters from that mapping, because a
+ mapping is handled like the characters were typed.
+ Use |inputsave()| before input() and |inputrestore()|
+ after input() to avoid that. Another solution is to avoid
+ that further characters follow in the mapping, e.g., by using
+ |:execute| or |:normal|.
+
+ Example with a mapping: >
+ :nmap \x :call GetFoo()<CR>:exe "/" .. Foo<CR>
+ :function GetFoo()
+ : call inputsave()
+ : let g:Foo = input("enter search pattern: ")
+ : call inputrestore()
+ :endfunction
+
+< Can also be used as a |method|: >
+ GetPrompt()->input()
+
+inputlist({textlist}) *inputlist()*
+ {textlist} must be a |List| of strings. This |List| is
+ displayed, one string per line. The user will be prompted to
+ enter a number, which is returned.
+ The user can also select an item by clicking on it with the
+ mouse, if the mouse is enabled in the command line ('mouse' is
+ "a" or includes "c"). For the first string 0 is returned.
+ When clicking above the first item a negative number is
+ returned. When clicking on the prompt one more than the
+ length of {textlist} is returned.
+ Make sure {textlist} has less than 'lines' entries, otherwise
+ it won't work. It's a good idea to put the entry number at
+ the start of the string. And put a prompt in the first item.
+ Example: >
+ let color = inputlist(['Select color:', '1. red',
+ \ '2. green', '3. blue'])
+
+< Can also be used as a |method|: >
+ GetChoices()->inputlist()
+
+inputrestore() *inputrestore()*
+ Restore typeahead that was saved with a previous |inputsave()|.
+ Should be called the same number of times inputsave() is
+ called. Calling it more often is harmless though.
+ Returns TRUE when there is nothing to restore, FALSE otherwise.
+
+inputsave() *inputsave()*
+ Preserve typeahead (also from mappings) and clear it, so that
+ a following prompt gets input from the user. Should be
+ followed by a matching inputrestore() after the prompt. Can
+ be used several times, in which case there must be just as
+ many inputrestore() calls.
+ Returns TRUE when out of memory, FALSE otherwise.
+
+inputsecret({prompt} [, {text}]) *inputsecret()*
+ This function acts much like the |input()| function with but
+ two exceptions:
+ a) the user's response will be displayed as a sequence of
+ asterisks ("*") thereby keeping the entry secret, and
+ b) the user's response will not be recorded on the input
+ |history| stack.
+ The result is a String, which is whatever the user actually
+ typed on the command-line in response to the issued prompt.
+ NOTE: Command-line completion is not supported.
+
+ Can also be used as a |method|: >
+ GetPrompt()->inputsecret()
+
+insert({object}, {item} [, {idx}]) *insert()*
+ When {object} is a |List| or a |Blob| insert {item} at the start
+ of it.
+
+ If {idx} is specified insert {item} before the item with index
+ {idx}. If {idx} is zero it goes before the first item, just
+ like omitting {idx}. A negative {idx} is also possible, see
+ |list-index|. -1 inserts just before the last item.
+
+ Returns the resulting |List| or |Blob|. Examples: >
+ :let mylist = insert([2, 3, 5], 1)
+ :call insert(mylist, 4, -1)
+ :call insert(mylist, 6, len(mylist))
+< The last example can be done simpler with |add()|.
+ Note that when {item} is a |List| it is inserted as a single
+ item. Use |extend()| to concatenate |Lists|.
+
+ Can also be used as a |method|: >
+ mylist->insert(item)
+
+interrupt() *interrupt()*
+ Interrupt script execution. It works more or less like the
+ user typing CTRL-C, most commands won't execute and control
+ returns to the user. This is useful to abort execution
+ from lower down, e.g. in an autocommand. Example: >
+ :function s:check_typoname(file)
+ : if fnamemodify(a:file, ':t') == '['
+ : echomsg 'Maybe typo'
+ : call interrupt()
+ : endif
+ :endfunction
+ :au BufWritePre * call s:check_typoname(expand('<amatch>'))
+
+invert({expr}) *invert()*
+ Bitwise invert. The argument is converted to a number. A
+ List, Dict or Float argument causes an error. Example: >
+ :let bits = invert(bits)
+< Can also be used as a |method|: >
+ :let bits = bits->invert()
+
+isdirectory({directory}) *isdirectory()*
+ The result is a Number, which is |TRUE| when a directory
+ with the name {directory} exists. If {directory} doesn't
+ exist, or isn't a directory, the result is |FALSE|. {directory}
+ is any expression, which is used as a String.
+
+ Can also be used as a |method|: >
+ GetName()->isdirectory()
+
+isinf({expr}) *isinf()*
+ Return 1 if {expr} is a positive infinity, or -1 a negative
+ infinity, otherwise 0. >
+ :echo isinf(1.0 / 0.0)
+< 1 >
+ :echo isinf(-1.0 / 0.0)
+< -1
+
+ Can also be used as a |method|: >
+ Compute()->isinf()
+
+islocked({expr}) *islocked()* *E786*
+ The result is a Number, which is |TRUE| when {expr} is the
+ name of a locked variable.
+ The string argument {expr} must be the name of a variable,
+ |List| item or |Dictionary| entry, not the variable itself!
+ Example: >
+ :let alist = [0, ['a', 'b'], 2, 3]
+ :lockvar 1 alist
+ :echo islocked('alist') " 1
+ :echo islocked('alist[1]') " 0
+
+< When {expr} is a variable that does not exist you get an error
+ message. Use |exists()| to check for existence.
+
+ Can also be used as a |method|: >
+ GetName()->islocked()
+
+id({expr}) *id()*
+ Returns a |String| which is a unique identifier of the
+ container type (|List|, |Dict|, |Blob| and |Partial|). It is
+ guaranteed that for the mentioned types `id(v1) ==# id(v2)`
+ returns true iff `type(v1) == type(v2) && v1 is v2`.
+ Note that |v:_null_string|, |v:_null_list|, |v:_null_dict| and
+ |v:_null_blob| have the same `id()` with different types
+ because they are internally represented as NULL pointers.
+ `id()` returns a hexadecimal representanion of the pointers to
+ the containers (i.e. like `0x994a40`), same as `printf("%p",
+ {expr})`, but it is advised against counting on the exact
+ format of the return value.
+
+ It is not guaranteed that `id(no_longer_existing_container)`
+ will not be equal to some other `id()`: new containers may
+ reuse identifiers of the garbage-collected ones.
+
+items({dict}) *items()*
+ Return a |List| with all the key-value pairs of {dict}. Each
+ |List| item is a list with two items: the key of a {dict}
+ entry and the value of this entry. The |List| is in arbitrary
+ order. Also see |keys()| and |values()|.
+ Example: >
+ for [key, value] in items(mydict)
+ echo key .. ': ' .. value
+ endfor
+
+< Can also be used as a |method|: >
+ mydict->items()
+
+isnan({expr}) *isnan()*
+ Return |TRUE| if {expr} is a float with value NaN. >
+ echo isnan(0.0 / 0.0)
+< 1
+
+ Can also be used as a |method|: >
+ Compute()->isnan()
+
+jobpid({job}) *jobpid()*
+ Return the PID (process id) of |job-id| {job}.
+
+jobresize({job}, {width}, {height}) *jobresize()*
+ Resize the pseudo terminal window of |job-id| {job} to {width}
+ columns and {height} rows.
+ Fails if the job was not started with `"pty":v:true`.
+
+jobstart({cmd} [, {opts}]) *jobstart()*
+ Spawns {cmd} as a job.
+ If {cmd} is a List it runs directly (no 'shell').
+ If {cmd} is a String it runs in the 'shell', like this: >
+ :call jobstart(split(&shell) + split(&shellcmdflag) + ['{cmd}'])
+< (See |shell-unquoting| for details.)
+
+ Example: >
+ :call jobstart('nvim -h', {'on_stdout':{j,d,e->append(line('.'),d)}})
+<
+ Returns |job-id| on success, 0 on invalid arguments (or job
+ table is full), -1 if {cmd}[0] or 'shell' is not executable.
+ The returned job-id is a valid |channel-id| representing the
+ job's stdio streams. Use |chansend()| (or |rpcnotify()| and
+ |rpcrequest()| if "rpc" was enabled) to send data to stdin and
+ |chanclose()| to close the streams without stopping the job.
+
+ See |job-control| and |RPC|.
+
+ NOTE: on Windows if {cmd} is a List:
+ - cmd[0] must be an executable (not a "built-in"). If it is
+ in $PATH it can be called by name, without an extension: >
+ :call jobstart(['ping', 'neovim.io'])
+< If it is a full or partial path, extension is required: >
+ :call jobstart(['System32\ping.exe', 'neovim.io'])
+< - {cmd} is collapsed to a string of quoted args as expected
+ by CommandLineToArgvW https://msdn.microsoft.com/bb776391
+ unless cmd[0] is some form of "cmd.exe".
+
+ *jobstart-env*
+ The job environment is initialized as follows:
+ $NVIM is set to |v:servername| of the parent Nvim
+ $NVIM_LISTEN_ADDRESS is unset
+ $NVIM_LOG_FILE is unset
+ $VIM is unset
+ $VIMRUNTIME is unset
+ You can set these with the `env` option.
+
+ *jobstart-options*
+ {opts} is a dictionary with these keys:
+ clear_env: (boolean) `env` defines the job environment
+ exactly, instead of merging current environment.
+ cwd: (string, default=|current-directory|) Working
+ directory of the job.
+ detach: (boolean) Detach the job process: it will not be
+ killed when Nvim exits. If the process exits
+ before Nvim, `on_exit` will be invoked.
+ env: (dict) Map of environment variable name:value
+ pairs extending (or replacing with |clear_env|)
+ the current environment. |jobstart-env|
+ height: (number) Height of the `pty` terminal.
+ |on_exit|: (function) Callback invoked when the job exits.
+ |on_stdout|: (function) Callback invoked when the job emits
+ stdout data.
+ |on_stderr|: (function) Callback invoked when the job emits
+ stderr data.
+ overlapped: (boolean) Set FILE_FLAG_OVERLAPPED for the
+ standard input/output passed to the child process.
+ Normally you do not need to set this.
+ (Only available on MS-Windows, On other
+ platforms, this option is silently ignored.)
+ pty: (boolean) Connect the job to a new pseudo
+ terminal, and its streams to the master file
+ descriptor. `on_stdout` receives all output,
+ `on_stderr` is ignored. |terminal-start|
+ rpc: (boolean) Use |msgpack-rpc| to communicate with
+ the job over stdio. Then `on_stdout` is ignored,
+ but `on_stderr` can still be used.
+ stderr_buffered: (boolean) Collect data until EOF (stream closed)
+ before invoking `on_stderr`. |channel-buffered|
+ stdout_buffered: (boolean) Collect data until EOF (stream
+ closed) before invoking `on_stdout`. |channel-buffered|
+ stdin: (string) Either "pipe" (default) to connect the
+ job's stdin to a channel or "null" to disconnect
+ stdin.
+ width: (number) Width of the `pty` terminal.
+
+ {opts} is passed as |self| dictionary to the callback; the
+ caller may set other keys to pass application-specific data.
+
+ Returns:
+ - |channel-id| on success
+ - 0 on invalid arguments
+ - -1 if {cmd}[0] is not executable.
+ See also |job-control|, |channel|, |msgpack-rpc|.
+
+jobstop({id}) *jobstop()*
+ Stop |job-id| {id} by sending SIGTERM to the job process. If
+ the process does not terminate after a timeout then SIGKILL
+ will be sent. When the job terminates its |on_exit| handler
+ (if any) will be invoked.
+ See |job-control|.
+
+ Returns 1 for valid job id, 0 for invalid id, including jobs have
+ exited or stopped.
+
+jobwait({jobs} [, {timeout}]) *jobwait()*
+ Waits for jobs and their |on_exit| handlers to complete.
+
+ {jobs} is a List of |job-id|s to wait for.
+ {timeout} is the maximum waiting time in milliseconds. If
+ omitted or -1, wait forever.
+
+ Timeout of 0 can be used to check the status of a job: >
+ let running = jobwait([{job-id}], 0)[0] == -1
+<
+ During jobwait() callbacks for jobs not in the {jobs} list may
+ be invoked. The screen will not redraw unless |:redraw| is
+ invoked by a callback.
+
+ Returns a list of len({jobs}) integers, where each integer is
+ the status of the corresponding job:
+ Exit-code, if the job exited
+ -1 if the timeout was exceeded
+ -2 if the job was interrupted (by |CTRL-C|)
+ -3 if the job-id is invalid
+
+join({list} [, {sep}]) *join()*
+ Join the items in {list} together into one String.
+ When {sep} is specified it is put in between the items. If
+ {sep} is omitted a single space is used.
+ Note that {sep} is not added at the end. You might want to
+ add it there too: >
+ let lines = join(mylist, "\n") .. "\n"
+< String items are used as-is. |Lists| and |Dictionaries| are
+ converted into a string like with |string()|.
+ The opposite function is |split()|.
+
+ Can also be used as a |method|: >
+ mylist->join()
+
+json_decode({expr}) *json_decode()*
+ Convert {expr} from JSON object. Accepts |readfile()|-style
+ list as the input, as well as regular string. May output any
+ Vim value. In the following cases it will output
+ |msgpack-special-dict|:
+ 1. Dictionary contains duplicate key.
+ 2. Dictionary contains empty key.
+ 3. String contains NUL byte. Two special dictionaries: for
+ dictionary and for string will be emitted in case string
+ with NUL byte was a dictionary key.
+
+ Note: function treats its input as UTF-8 always. The JSON
+ standard allows only a few encodings, of which UTF-8 is
+ recommended and the only one required to be supported.
+ Non-UTF-8 characters are an error.
+
+ Can also be used as a |method|: >
+ ReadObject()->json_decode()
+
+json_encode({expr}) *json_encode()*
+ Convert {expr} into a JSON string. Accepts
+ |msgpack-special-dict| as the input. Will not convert
+ |Funcref|s, mappings with non-string keys (can be created as
+ |msgpack-special-dict|), values with self-referencing
+ containers, strings which contain non-UTF-8 characters,
+ pseudo-UTF-8 strings which contain codepoints reserved for
+ surrogate pairs (such strings are not valid UTF-8 strings).
+ Non-printable characters are converted into "\u1234" escapes
+ or special escapes like "\t", other are dumped as-is.
+ |Blob|s are converted to arrays of the individual bytes.
+
+ Can also be used as a |method|: >
+ GetObject()->json_encode()
+
+keys({dict}) *keys()*
+ Return a |List| with all the keys of {dict}. The |List| is in
+ arbitrary order. Also see |items()| and |values()|.
+
+ Can also be used as a |method|: >
+ mydict->keys()
+
+< *len()* *E701*
+len({expr}) The result is a Number, which is the length of the argument.
+ When {expr} is a String or a Number the length in bytes is
+ used, as with |strlen()|.
+ When {expr} is a |List| the number of items in the |List| is
+ returned.
+ When {expr} is a |Blob| the number of bytes is returned.
+ When {expr} is a |Dictionary| the number of entries in the
+ |Dictionary| is returned.
+ Otherwise an error is given and returns zero.
+
+ Can also be used as a |method|: >
+ mylist->len()
+
+< *libcall()* *E364* *E368*
+libcall({libname}, {funcname}, {argument})
+ Call function {funcname} in the run-time library {libname}
+ with single argument {argument}.
+ This is useful to call functions in a library that you
+ especially made to be used with Vim. Since only one argument
+ is possible, calling standard library functions is rather
+ limited.
+ The result is the String returned by the function. If the
+ function returns NULL, this will appear as an empty string ""
+ to Vim.
+ If the function returns a number, use libcallnr()!
+ If {argument} is a number, it is passed to the function as an
+ int; if {argument} is a string, it is passed as a
+ null-terminated string.
+
+ libcall() allows you to write your own 'plug-in' extensions to
+ Vim without having to recompile the program. It is NOT a
+ means to call system functions! If you try to do so Vim will
+ very probably crash.
+
+ For Win32, the functions you write must be placed in a DLL
+ and use the normal C calling convention (NOT Pascal which is
+ used in Windows System DLLs). The function must take exactly
+ one parameter, either a character pointer or a long integer,
+ and must return a character pointer or NULL. The character
+ pointer returned must point to memory that will remain valid
+ after the function has returned (e.g. in static data in the
+ DLL). If it points to allocated memory, that memory will
+ leak away. Using a static buffer in the function should work,
+ it's then freed when the DLL is unloaded.
+
+ WARNING: If the function returns a non-valid pointer, Vim may
+ crash! This also happens if the function returns a number,
+ because Vim thinks it's a pointer.
+ For Win32 systems, {libname} should be the filename of the DLL
+ without the ".DLL" suffix. A full path is only required if
+ the DLL is not in the usual places.
+ For Unix: When compiling your own plugins, remember that the
+ object code must be compiled as position-independent ('PIC').
+ Examples: >
+ :echo libcall("libc.so", "getenv", "HOME")
+
+< Can also be used as a |method|, the base is passed as the
+ third argument: >
+ GetValue()->libcall("libc.so", "getenv")
+<
+ *libcallnr()*
+libcallnr({libname}, {funcname}, {argument})
+ Just like |libcall()|, but used for a function that returns an
+ int instead of a string.
+ Examples: >
+ :echo libcallnr("/usr/lib/libc.so", "getpid", "")
+ :call libcallnr("libc.so", "printf", "Hello World!\n")
+ :call libcallnr("libc.so", "sleep", 10)
+<
+ Can also be used as a |method|, the base is passed as the
+ third argument: >
+ GetValue()->libcallnr("libc.so", "printf")
+<
+line({expr} [, {winid}]) *line()*
+ The result is a Number, which is the line number of the file
+ position given with {expr}. The {expr} argument is a string.
+ The accepted positions are:
+ . the cursor position
+ $ the last line in the current buffer
+ 'x position of mark x (if the mark is not set, 0 is
+ returned)
+ w0 first line visible in current window (one if the
+ display isn't updated, e.g. in silent Ex mode)
+ w$ last line visible in current window (this is one
+ less than "w0" if no lines are visible)
+ v In Visual mode: the start of the Visual area (the
+ cursor is the end). When not in Visual mode
+ returns the cursor position. Differs from |'<| in
+ that it's updated right away.
+ Note that a mark in another file can be used. The line number
+ then applies to another buffer.
+ To get the column number use |col()|. To get both use
+ |getpos()|.
+ With the optional {winid} argument the values are obtained for
+ that window instead of the current window.
+ Returns 0 for invalid values of {expr} and {winid}.
+ Examples: >
+ line(".") line number of the cursor
+ line(".", winid) idem, in window "winid"
+ line("'t") line number of mark t
+ line("'" .. marker) line number of mark marker
+<
+ To jump to the last known position when opening a file see
+ |last-position-jump|.
+
+ Can also be used as a |method|: >
+ GetValue()->line()
+
+line2byte({lnum}) *line2byte()*
+ Return the byte count from the start of the buffer for line
+ {lnum}. This includes the end-of-line character, depending on
+ the 'fileformat' option for the current buffer. The first
+ line returns 1. UTF-8 encoding is used, 'fileencoding' is
+ ignored. This can also be used to get the byte count for the
+ line just below the last line: >
+ line2byte(line("$") + 1)
+< This is the buffer size plus one. If 'fileencoding' is empty
+ it is the file size plus one. {lnum} is used like with
+ |getline()|. When {lnum} is invalid -1 is returned.
+ Also see |byte2line()|, |go| and |:goto|.
+
+ Can also be used as a |method|: >
+ GetLnum()->line2byte()
+
+lispindent({lnum}) *lispindent()*
+ Get the amount of indent for line {lnum} according the lisp
+ indenting rules, as with 'lisp'.
+ The indent is counted in spaces, the value of 'tabstop' is
+ relevant. {lnum} is used just like in |getline()|.
+ When {lnum} is invalid, -1 is returned.
+
+ Can also be used as a |method|: >
+ GetLnum()->lispindent()
+
+list2str({list} [, {utf8}]) *list2str()*
+ Convert each number in {list} to a character string can
+ concatenate them all. Examples: >
+ list2str([32]) returns " "
+ list2str([65, 66, 67]) returns "ABC"
+< The same can be done (slowly) with: >
+ join(map(list, {nr, val -> nr2char(val)}), '')
+< |str2list()| does the opposite.
+
+ UTF-8 encoding is always used, {utf8} option has no effect,
+ and exists only for backwards-compatibility.
+ With UTF-8 composing characters work as expected: >
+ list2str([97, 769]) returns "á"
+<
+ Returns an empty string on error.
+
+ Can also be used as a |method|: >
+ GetList()->list2str()
+
+localtime() *localtime()*
+ Return the current time, measured as seconds since 1st Jan
+ 1970. See also |strftime()|, |strptime()| and |getftime()|.
+
+
+log({expr}) *log()*
+ Return the natural logarithm (base e) of {expr} as a |Float|.
+ {expr} must evaluate to a |Float| or a |Number| in the range
+ (0, inf].
+ Returns 0.0 if {expr} is not a |Float| or a |Number|.
+ Examples: >
+ :echo log(10)
+< 2.302585 >
+ :echo log(exp(5))
+< 5.0
+
+ Can also be used as a |method|: >
+ Compute()->log()
+
+log10({expr}) *log10()*
+ Return the logarithm of Float {expr} to base 10 as a |Float|.
+ {expr} must evaluate to a |Float| or a |Number|.
+ Returns 0.0 if {expr} is not a |Float| or a |Number|.
+ Examples: >
+ :echo log10(1000)
+< 3.0 >
+ :echo log10(0.01)
+< -2.0
+
+ Can also be used as a |method|: >
+ Compute()->log10()
+
+luaeval({expr} [, {expr}])
+ Evaluate Lua expression {expr} and return its result converted
+ to Vim data structures. See |lua-eval| for more details.
+
+ Can also be used as a |method|: >
+ GetExpr()->luaeval()
+
+map({expr1}, {expr2}) *map()*
+ {expr1} must be a |List|, |Blob| or |Dictionary|.
+ Replace each item in {expr1} with the result of evaluating
+ {expr2}. For a |Blob| each byte is replaced.
+
+ {expr2} must be a |string| or |Funcref|.
+
+ If {expr2} is a |string|, inside {expr2} |v:val| has the value
+ of the current item. For a |Dictionary| |v:key| has the key
+ of the current item and for a |List| |v:key| has the index of
+ the current item. For a |Blob| |v:key| has the index of the
+ current byte.
+ Example: >
+ :call map(mylist, '"> " .. v:val .. " <"')
+< This puts "> " before and " <" after each item in "mylist".
+
+ Note that {expr2} is the result of an expression and is then
+ used as an expression again. Often it is good to use a
+ |literal-string| to avoid having to double backslashes. You
+ still have to double ' quotes
+
+ If {expr2} is a |Funcref| it is called with two arguments:
+ 1. The key or the index of the current item.
+ 2. the value of the current item.
+ The function must return the new value of the item. Example
+ that changes each value by "key-value": >
+ func KeyValue(key, val)
+ return a:key .. '-' .. a:val
+ endfunc
+ call map(myDict, function('KeyValue'))
+< It is shorter when using a |lambda|: >
+ call map(myDict, {key, val -> key .. '-' .. val})
+< If you do not use "val" you can leave it out: >
+ call map(myDict, {key -> 'item: ' .. key})
+< If you do not use "key" you can use a short name: >
+ call map(myDict, {_, val -> 'item: ' .. val})
+<
+ The operation is done in-place. If you want a |List| or
+ |Dictionary| to remain unmodified make a copy first: >
+ :let tlist = map(copy(mylist), ' v:val .. "\t"')
+
+< Returns {expr1}, the |List|, |Blob| or |Dictionary| that was
+ filtered. When an error is encountered while evaluating
+ {expr2} no further items in {expr1} are processed. When
+ {expr2} is a Funcref errors inside a function are ignored,
+ unless it was defined with the "abort" flag.
+
+ Can also be used as a |method|: >
+ mylist->map(expr2)
+
+maparg({name} [, {mode} [, {abbr} [, {dict}]]]) *maparg()*
+ When {dict} is omitted or zero: Return the rhs of mapping
+ {name} in mode {mode}. The returned String has special
+ characters translated like in the output of the ":map" command
+ listing.
+
+ When there is no mapping for {name}, an empty String is
+ returned if {dict} is FALSE, otherwise returns an empty Dict.
+ When the mapping for {name} is empty, then "<Nop>" is
+ returned.
+
+ The {name} can have special key names, like in the ":map"
+ command.
+
+ {mode} can be one of these strings:
+ "n" Normal
+ "v" Visual (including Select)
+ "o" Operator-pending
+ "i" Insert
+ "c" Cmd-line
+ "s" Select
+ "x" Visual
+ "l" langmap |language-mapping|
+ "t" Terminal
+ "" Normal, Visual and Operator-pending
+ When {mode} is omitted, the modes for "" are used.
+
+ When {abbr} is there and it is |TRUE| use abbreviations
+ instead of mappings.
+
+ When {dict} is there and it is |TRUE| return a dictionary
+ containing all the information of the mapping with the
+ following items:
+ "lhs" The {lhs} of the mapping.
+ "rhs" The {rhs} of the mapping as typed.
+ "silent" 1 for a |:map-silent| mapping, else 0.
+ "noremap" 1 if the {rhs} of the mapping is not remappable.
+ "script" 1 if mapping was defined with <script>.
+ "expr" 1 for an expression mapping (|:map-<expr>|).
+ "buffer" 1 for a buffer local mapping (|:map-local|).
+ "mode" Modes for which the mapping is defined. In
+ addition to the modes mentioned above, these
+ characters will be used:
+ " " Normal, Visual and Operator-pending
+ "!" Insert and Commandline mode
+ (|mapmode-ic|)
+ "sid" The script local ID, used for <sid> mappings
+ (|<SID>|).
+ "lnum" The line number in "sid", zero if unknown.
+ "nowait" Do not wait for other, longer mappings.
+ (|:map-<nowait>|).
+
+ The mappings local to the current buffer are checked first,
+ then the global mappings.
+ This function can be used to map a key even when it's already
+ mapped, and have it do the original mapping too. Sketch: >
+ exe 'nnoremap <Tab> ==' .. maparg('<Tab>', 'n')
+
+< Can also be used as a |method|: >
+ GetKey()->maparg('n')
+
+mapcheck({name} [, {mode} [, {abbr}]]) *mapcheck()*
+ Check if there is a mapping that matches with {name} in mode
+ {mode}. See |maparg()| for {mode} and special names in
+ {name}.
+ When {abbr} is there and it is non-zero use abbreviations
+ instead of mappings.
+ A match happens with a mapping that starts with {name} and
+ with a mapping which is equal to the start of {name}.
+
+ matches mapping "a" "ab" "abc" ~
+ mapcheck("a") yes yes yes
+ mapcheck("abc") yes yes yes
+ mapcheck("ax") yes no no
+ mapcheck("b") no no no
+
+ The difference with maparg() is that mapcheck() finds a
+ mapping that matches with {name}, while maparg() only finds a
+ mapping for {name} exactly.
+ When there is no mapping that starts with {name}, an empty
+ String is returned. If there is one, the RHS of that mapping
+ is returned. If there are several mappings that start with
+ {name}, the RHS of one of them is returned. This will be
+ "<Nop>" if the RHS is empty.
+ The mappings local to the current buffer are checked first,
+ then the global mappings.
+ This function can be used to check if a mapping can be added
+ without being ambiguous. Example: >
+ :if mapcheck("_vv") == ""
+ : map _vv :set guifont=7x13<CR>
+ :endif
+< This avoids adding the "_vv" mapping when there already is a
+ mapping for "_v" or for "_vvv".
+
+ Can also be used as a |method|: >
+ GetKey()->mapcheck('n')
+
+match({expr}, {pat} [, {start} [, {count}]]) *match()*
+ When {expr} is a |List| then this returns the index of the
+ first item where {pat} matches. Each item is used as a
+ String, |Lists| and |Dictionaries| are used as echoed.
+
+ Otherwise, {expr} is used as a String. The result is a
+ Number, which gives the index (byte offset) in {expr} where
+ {pat} matches.
+
+ A match at the first character or |List| item returns zero.
+ If there is no match -1 is returned.
+
+ For getting submatches see |matchlist()|.
+ Example: >
+ :echo match("testing", "ing") " results in 4
+ :echo match([1, 'x'], '\a') " results in 1
+< See |string-match| for how {pat} is used.
+ *strpbrk()*
+ Vim doesn't have a strpbrk() function. But you can do: >
+ :let sepidx = match(line, '[.,;: \t]')
+< *strcasestr()*
+ Vim doesn't have a strcasestr() function. But you can add
+ "\c" to the pattern to ignore case: >
+ :let idx = match(haystack, '\cneedle')
+<
+ If {start} is given, the search starts from byte index
+ {start} in a String or item {start} in a |List|.
+ The result, however, is still the index counted from the
+ first character/item. Example: >
+ :echo match("testing", "ing", 2)
+< result is again "4". >
+ :echo match("testing", "ing", 4)
+< result is again "4". >
+ :echo match("testing", "t", 2)
+< result is "3".
+ For a String, if {start} > 0 then it is like the string starts
+ {start} bytes later, thus "^" will match at {start}. Except
+ when {count} is given, then it's like matches before the
+ {start} byte are ignored (this is a bit complicated to keep it
+ backwards compatible).
+ For a String, if {start} < 0, it will be set to 0. For a list
+ the index is counted from the end.
+ If {start} is out of range ({start} > strlen({expr}) for a
+ String or {start} > len({expr}) for a |List|) -1 is returned.
+
+ When {count} is given use the {count}'th match. When a match
+ is found in a String the search for the next one starts one
+ character further. Thus this example results in 1: >
+ echo match("testing", "..", 0, 2)
+< In a |List| the search continues in the next item.
+ Note that when {count} is added the way {start} works changes,
+ see above.
+
+ See |pattern| for the patterns that are accepted.
+ The 'ignorecase' option is used to set the ignore-caseness of
+ the pattern. 'smartcase' is NOT used. The matching is always
+ done like 'magic' is set and 'cpoptions' is empty.
+ Note that a match at the start is preferred, thus when the
+ pattern is using "*" (any number of matches) it tends to find
+ zero matches at the start instead of a number of matches
+ further down in the text.
+
+ Can also be used as a |method|: >
+ GetText()->match('word')
+ GetList()->match('word')
+<
+ *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
+ identification number (ID), which can be used to delete the
+ match using |matchdelete()|. The ID is bound to the window.
+ Matching is case sensitive and magic, unless case sensitivity
+ or magicness are explicitly overridden in {pattern}. The
+ 'magic', 'smartcase' and 'ignorecase' options are not used.
+ The "Conceal" value is special, it causes the match to be
+ concealed.
+
+ The optional {priority} argument assigns a priority to the
+ match. A match with a high priority will have its
+ highlighting overrule that of a match with a lower priority.
+ A priority is specified as an integer (negative numbers are no
+ exception). If the {priority} argument is not specified, the
+ default priority is 10. The priority of 'hlsearch' is zero,
+ hence all matches with a priority greater than zero will
+ overrule it. Syntax highlighting (see 'syntax') is a separate
+ mechanism, and regardless of the chosen priority a match will
+ always overrule syntax highlighting.
+
+ The optional {id} argument allows the request for a specific
+ match ID. If a specified ID is already taken, an error
+ message will appear and the match will not be added. An ID
+ is specified as a positive integer (zero excluded). IDs 1, 2
+ and 3 are reserved for |:match|, |:2match| and |:3match|,
+ respectively. If the {id} argument is not specified or -1,
+ |matchadd()| automatically chooses a free ID.
+
+ The optional {dict} argument allows for further custom
+ values. Currently this is used to specify a match specific
+ conceal character that will be shown for |hl-Conceal|
+ highlighted matches. The dict can have the following members:
+
+ conceal Special character to show instead of the
+ match (only for |hl-Conceal| highlighed
+ matches, see |:syn-cchar|)
+ window Instead of the current window use the
+ window with this number or window ID.
+
+ The number of matches is not limited, as it is the case with
+ the |:match| commands.
+
+ Returns -1 on error.
+
+ Example: >
+ :highlight MyGroup ctermbg=green guibg=green
+ :let m = matchadd("MyGroup", "TODO")
+< Deletion of the pattern: >
+ :call matchdelete(m)
+
+< A list of matches defined by |matchadd()| and |:match| are
+ available from |getmatches()|. All matches can be deleted in
+ one operation by |clearmatches()|.
+
+ Can also be used as a |method|: >
+ GetGroup()->matchadd('TODO')
+<
+ *matchaddpos()*
+matchaddpos({group}, {pos} [, {priority} [, {id} [, {dict}]]])
+ Same as |matchadd()|, but requires a list of positions {pos}
+ instead of a pattern. This command is faster than |matchadd()|
+ because it does not require to handle regular expressions and
+ sets buffer line boundaries to redraw screen. It is supposed
+ to be used when fast match additions and deletions are
+ required, for example to highlight matching parentheses.
+ *E5030* *E5031*
+ {pos} is a list of positions. Each position can be one of
+ these:
+ - A number. This whole line will be highlighted. The first
+ line has number 1.
+ - A list with one number, e.g., [23]. The whole line with this
+ number will be highlighted.
+ - A list with two numbers, e.g., [23, 11]. The first number is
+ the line number, the second one is the column number (first
+ column is 1, the value must correspond to the byte index as
+ |col()| would return). The character at this position will
+ be highlighted.
+ - A list with three numbers, e.g., [23, 11, 3]. As above, but
+ the third number gives the length of the highlight in bytes.
+
+ Entries with zero and negative line numbers are silently
+ ignored, as well as entries with negative column numbers and
+ lengths.
+
+ The maximum number of positions in {pos} is 8.
+
+ Returns -1 on error.
+
+ Example: >
+ :highlight MyGroup ctermbg=green guibg=green
+ :let m = matchaddpos("MyGroup", [[23, 24], 34])
+< Deletion of the pattern: >
+ :call matchdelete(m)
+
+< Matches added by |matchaddpos()| are returned by
+ |getmatches()|.
+
+ Can also be used as a |method|: >
+ GetGroup()->matchaddpos([23, 11])
+
+matcharg({nr}) *matcharg()*
+ Selects the {nr} match item, as set with a |:match|,
+ |:2match| or |:3match| command.
+ Return a |List| with two elements:
+ The name of the highlight group used
+ The pattern used.
+ When {nr} is not 1, 2 or 3 returns an empty |List|.
+ When there is no match item set returns ['', ''].
+ This is useful to save and restore a |:match|.
+ Highlighting matches using the |:match| commands are limited
+ to three matches. |matchadd()| does not have this limitation.
+
+ Can also be used as a |method|: >
+ GetMatch()->matcharg()
+
+matchdelete({id} [, {win}]) *matchdelete()* *E802* *E803*
+ Deletes a match with ID {id} previously defined by |matchadd()|
+ or one of the |:match| commands. Returns 0 if successful,
+ otherwise -1. See example for |matchadd()|. All matches can
+ be deleted in one operation by |clearmatches()|.
+ If {win} is specified, use the window with this number or
+ window ID instead of the current window.
+
+ Can also be used as a |method|: >
+ GetMatch()->matchdelete()
+
+matchend({expr}, {pat} [, {start} [, {count}]]) *matchend()*
+ Same as |match()|, but return the index of first character
+ after the match. Example: >
+ :echo matchend("testing", "ing")
+< results in "7".
+ *strspn()* *strcspn()*
+ Vim doesn't have a strspn() or strcspn() function, but you can
+ do it with matchend(): >
+ :let span = matchend(line, '[a-zA-Z]')
+ :let span = matchend(line, '[^a-zA-Z]')
+< Except that -1 is returned when there are no matches.
+
+ The {start}, if given, has the same meaning as for |match()|. >
+ :echo matchend("testing", "ing", 2)
+< results in "7". >
+ :echo matchend("testing", "ing", 5)
+< result is "-1".
+ When {expr} is a |List| the result is equal to |match()|.
+
+ Can also be used as a |method|: >
+ GetText()->matchend('word')
+
+matchfuzzy({list}, {str} [, {dict}]) *matchfuzzy()*
+ If {list} is a list of strings, then returns a |List| with all
+ the strings in {list} that fuzzy match {str}. The strings in
+ the returned list are sorted based on the matching score.
+
+ The optional {dict} argument always supports the following
+ items:
+ matchseq When this item is present return only matches
+ that contain the characters in {str} in the
+ given sequence.
+ limit Maximum number of matches in {list} to be
+ returned. Zero means no limit.
+
+ If {list} is a list of dictionaries, then the optional {dict}
+ argument supports the following additional items:
+ key Key of the item which is fuzzy matched against
+ {str}. The value of this item should be a
+ string.
+ text_cb |Funcref| that will be called for every item
+ in {list} to get the text for fuzzy matching.
+ This should accept a dictionary item as the
+ argument and return the text for that item to
+ use for fuzzy matching.
+
+ {str} is treated as a literal string and regular expression
+ matching is NOT supported. The maximum supported {str} length
+ is 256.
+
+ When {str} has multiple words each separated by white space,
+ then the list of strings that have all the words is returned.
+
+ If there are no matching strings or there is an error, then an
+ empty list is returned. If length of {str} is greater than
+ 256, then returns an empty list.
+
+ When {limit} is given, matchfuzzy() will find up to this
+ number of matches in {list} and return them in sorted order.
+
+ Refer to |fuzzy-matching| for more information about fuzzy
+ matching strings.
+
+ Example: >
+ :echo matchfuzzy(["clay", "crow"], "cay")
+< results in ["clay"]. >
+ :echo getbufinfo()->map({_, v -> v.name})->matchfuzzy("ndl")
+< results in a list of buffer names fuzzy matching "ndl". >
+ :echo getbufinfo()->matchfuzzy("ndl", {'key' : 'name'})
+< results in a list of buffer information dicts with buffer
+ names fuzzy matching "ndl". >
+ :echo getbufinfo()->matchfuzzy("spl",
+ \ {'text_cb' : {v -> v.name}})
+< results in a list of buffer information dicts with buffer
+ names fuzzy matching "spl". >
+ :echo v:oldfiles->matchfuzzy("test")
+< results in a list of file names fuzzy matching "test". >
+ :let l = readfile("buffer.c")->matchfuzzy("str")
+< results in a list of lines in "buffer.c" fuzzy matching "str". >
+ :echo ['one two', 'two one']->matchfuzzy('two one')
+< results in ['two one', 'one two']. >
+ :echo ['one two', 'two one']->matchfuzzy('two one',
+ \ {'matchseq': 1})
+< results in ['two one'].
+
+matchfuzzypos({list}, {str} [, {dict}]) *matchfuzzypos()*
+ Same as |matchfuzzy()|, but returns the list of matched
+ strings, the list of character positions where characters
+ in {str} matches and a list of matching scores. You can
+ use |byteidx()| to convert a character position to a byte
+ position.
+
+ If {str} matches multiple times in a string, then only the
+ positions for the best match is returned.
+
+ If there are no matching strings or there is an error, then a
+ list with three empty list items is returned.
+
+ Example: >
+ :echo matchfuzzypos(['testing'], 'tsg')
+< results in [['testing'], [[0, 2, 6]], [99]] >
+ :echo matchfuzzypos(['clay', 'lacy'], 'la')
+< results in [['lacy', 'clay'], [[0, 1], [1, 2]], [153, 133]] >
+ :echo [{'text': 'hello', 'id' : 10}]
+ \ ->matchfuzzypos('ll', {'key' : 'text'})
+< results in [[{'id': 10, 'text': 'hello'}], [[2, 3]], [127]]
+
+matchlist({expr}, {pat} [, {start} [, {count}]]) *matchlist()*
+ Same as |match()|, but return a |List|. The first item in the
+ list is the matched string, same as what matchstr() would
+ return. Following items are submatches, like "\1", "\2", etc.
+ in |:substitute|. When an optional submatch didn't match an
+ empty string is used. Example: >
+ echo matchlist('acd', '\(a\)\?\(b\)\?\(c\)\?\(.*\)')
+< Results in: ['acd', 'a', '', 'c', 'd', '', '', '', '', '']
+ When there is no match an empty list is returned.
+
+ You can pass in a List, but that is not very useful.
+
+ Can also be used as a |method|: >
+ GetText()->matchlist('word')
+
+matchstr({expr}, {pat} [, {start} [, {count}]]) *matchstr()*
+ Same as |match()|, but return the matched string. Example: >
+ :echo matchstr("testing", "ing")
+< results in "ing".
+ When there is no match "" is returned.
+ The {start}, if given, has the same meaning as for |match()|. >
+ :echo matchstr("testing", "ing", 2)
+< results in "ing". >
+ :echo matchstr("testing", "ing", 5)
+< result is "".
+ When {expr} is a |List| then the matching item is returned.
+ The type isn't changed, it's not necessarily a String.
+
+ Can also be used as a |method|: >
+ GetText()->matchstr('word')
+
+matchstrpos({expr}, {pat} [, {start} [, {count}]]) *matchstrpos()*
+ Same as |matchstr()|, but return the matched string, the start
+ position and the end position of the match. Example: >
+ :echo matchstrpos("testing", "ing")
+< results in ["ing", 4, 7].
+ When there is no match ["", -1, -1] is returned.
+ The {start}, if given, has the same meaning as for |match()|. >
+ :echo matchstrpos("testing", "ing", 2)
+< results in ["ing", 4, 7]. >
+ :echo matchstrpos("testing", "ing", 5)
+< result is ["", -1, -1].
+ When {expr} is a |List| then the matching item, the index
+ of first item where {pat} matches, the start position and the
+ end position of the match are returned. >
+ :echo matchstrpos([1, '__x'], '\a')
+< result is ["x", 1, 2, 3].
+ The type isn't changed, it's not necessarily a String.
+
+ Can also be used as a |method|: >
+ GetText()->matchstrpos('word')
+<
+ *max()*
+max({expr}) Return the maximum value of all items in {expr}. Example: >
+ echo max([apples, pears, oranges])
+
+< {expr} can be a |List| or a |Dictionary|. For a Dictionary,
+ it returns the maximum of all values in the Dictionary.
+ If {expr} is neither a List nor a Dictionary, or one of the
+ items in {expr} cannot be used as a Number this results in
+ an error. An empty |List| or |Dictionary| results in zero.
+
+ Can also be used as a |method|: >
+ mylist->max()
+
+menu_get({path} [, {modes}]) *menu_get()*
+ Returns a |List| of |Dictionaries| describing |menus| (defined
+ by |:menu|, |:amenu|, …), including |hidden-menus|.
+
+ {path} matches a menu by name, or all menus if {path} is an
+ empty string. Example: >
+ :echo menu_get('File','')
+ :echo menu_get('')
+<
+ {modes} is a string of zero or more modes (see |maparg()| or
+ |creating-menus| for the list of modes). "a" means "all".
+
+ Example: >
+ nnoremenu &Test.Test inormal
+ inoremenu Test.Test insert
+ vnoremenu Test.Test x
+ echo menu_get("")
+
+< returns something like this: >
+
+ [ {
+ "hidden": 0,
+ "name": "Test",
+ "priority": 500,
+ "shortcut": 84,
+ "submenus": [ {
+ "hidden": 0,
+ "mappings": {
+ i": {
+ "enabled": 1,
+ "noremap": 1,
+ "rhs": "insert",
+ "sid": 1,
+ "silent": 0
+ },
+ n": { ... },
+ s": { ... },
+ v": { ... }
+ },
+ "name": "Test",
+ "priority": 500,
+ "shortcut": 0
+ } ]
+ } ]
+<
+
+ *min()*
+min({expr}) Return the minimum value of all items in {expr}. Example: >
+ echo min([apples, pears, oranges])
+
+< {expr} can be a |List| or a |Dictionary|. For a Dictionary,
+ it returns the minimum of all values in the Dictionary.
+ If {expr} is neither a List nor a Dictionary, or one of the
+ items in {expr} cannot be used as a Number this results in
+ an error. An empty |List| or |Dictionary| results in zero.
+
+ Can also be used as a |method|: >
+ mylist->min()
+
+< *mkdir()* *E739*
+mkdir({name} [, {path} [, {prot}]])
+ Create directory {name}.
+
+ If {path} is "p" then intermediate directories are created as
+ necessary. Otherwise it must be "".
+
+ If {prot} is given it is used to set the protection bits of
+ the new directory. The default is 0o755 (rwxr-xr-x: r/w for
+ the user, readable for others). Use 0o700 to make it
+ unreadable for others.
+
+ {prot} is applied for all parts of {name}. Thus if you create
+ /tmp/foo/bar then /tmp/foo will be created with 0o700. Example: >
+ :call mkdir($HOME .. "/tmp/foo/bar", "p", 0o700)
+
+< This function is not available in the |sandbox|.
+
+ If you try to create an existing directory with {path} set to
+ "p" mkdir() will silently exit.
+
+ The function result is a Number, which is TRUE if the call was
+ successful or FALSE if the directory creation failed or partly
+ failed.
+
+ Can also be used as a |method|: >
+ GetName()->mkdir()
+<
+ *mode()*
+mode([expr]) Return a string that indicates the current mode.
+ If [expr] is supplied and it evaluates to a non-zero Number or
+ a non-empty String (|non-zero-arg|), then the full mode is
+ returned, otherwise only the first letter is returned.
+
+ n Normal
+ no Operator-pending
+ nov Operator-pending (forced charwise |o_v|)
+ noV Operator-pending (forced linewise |o_V|)
+ noCTRL-V Operator-pending (forced blockwise |o_CTRL-V|)
+ CTRL-V is one character
+ niI Normal using |i_CTRL-O| in |Insert-mode|
+ niR Normal using |i_CTRL-O| in |Replace-mode|
+ niV Normal using |i_CTRL-O| in |Virtual-Replace-mode|
+ nt Normal in |terminal-emulator| (insert goes to
+ Terminal mode)
+ v Visual by character
+ vs Visual by character using |v_CTRL-O| in Select mode
+ V Visual by line
+ Vs Visual by line using |v_CTRL-O| in Select mode
+ CTRL-V Visual blockwise
+ CTRL-Vs Visual blockwise using |v_CTRL-O| in Select mode
+ s Select by character
+ S Select by line
+ CTRL-S Select blockwise
+ i Insert
+ ic Insert mode completion |compl-generic|
+ ix Insert mode |i_CTRL-X| completion
+ R Replace |R|
+ Rc Replace mode completion |compl-generic|
+ Rx Replace mode |i_CTRL-X| completion
+ Rv Virtual Replace |gR|
+ Rvc Virtual Replace mode completion |compl-generic|
+ Rvx Virtual Replace mode |i_CTRL-X| completion
+ c Command-line editing
+ cv Vim Ex mode |gQ|
+ r Hit-enter prompt
+ rm The -- more -- prompt
+ r? A |:confirm| query of some sort
+ ! Shell or external command is executing
+ t Terminal mode: keys go to the job
+
+ This is useful in the 'statusline' option or RPC calls. In
+ most other places it always returns "c" or "n".
+ Note that in the future more modes and more specific modes may
+ be added. It's better not to compare the whole string but only
+ the leading character(s).
+ Also see |visualmode()|.
+
+ Can also be used as a |method|: >
+ DoFull()->mode()
+
+msgpackdump({list} [, {type}]) *msgpackdump()*
+ Convert a list of VimL objects to msgpack. Returned value is a
+ |readfile()|-style list. When {type} contains "B", a |Blob| is
+ returned instead. Example: >
+ call writefile(msgpackdump([{}]), 'fname.mpack', 'b')
+< or, using a |Blob|: >
+ call writefile(msgpackdump([{}], 'B'), 'fname.mpack')
+<
+ This will write the single 0x80 byte to a `fname.mpack` file
+ (dictionary with zero items is represented by 0x80 byte in
+ messagepack).
+
+ Limitations: *E5004* *E5005*
+ 1. |Funcref|s cannot be dumped.
+ 2. Containers that reference themselves cannot be dumped.
+ 3. Dictionary keys are always dumped as STR strings.
+ 4. Other strings and |Blob|s are always dumped as BIN strings.
+ 5. Points 3. and 4. do not apply to |msgpack-special-dict|s.
+
+msgpackparse({data}) *msgpackparse()*
+ Convert a |readfile()|-style list or a |Blob| to a list of
+ VimL objects.
+ Example: >
+ let fname = expand('~/.config/nvim/shada/main.shada')
+ let mpack = readfile(fname, 'b')
+ let shada_objects = msgpackparse(mpack)
+< This will read ~/.config/nvim/shada/main.shada file to
+ `shada_objects` list.
+
+ Limitations:
+ 1. Mapping ordering is not preserved unless messagepack
+ mapping is dumped using generic mapping
+ (|msgpack-special-map|).
+ 2. Since the parser aims to preserve all data untouched
+ (except for 1.) some strings are parsed to
+ |msgpack-special-dict| format which is not convenient to
+ use.
+ *msgpack-special-dict*
+ Some messagepack strings may be parsed to special
+ dictionaries. Special dictionaries are dictionaries which
+
+ 1. Contain exactly two keys: `_TYPE` and `_VAL`.
+ 2. `_TYPE` key is one of the types found in |v:msgpack_types|
+ variable.
+ 3. Value for `_VAL` has the following format (Key column
+ contains name of the key from |v:msgpack_types|):
+
+ Key Value ~
+ nil Zero, ignored when dumping. Not returned by
+ |msgpackparse()| since |v:null| was introduced.
+ boolean One or zero. When dumping it is only checked that
+ value is a |Number|. Not returned by |msgpackparse()|
+ since |v:true| and |v:false| were introduced.
+ integer |List| with four numbers: sign (-1 or 1), highest two
+ bits, number with bits from 62nd to 31st, lowest 31
+ bits. I.e. to get actual number one will need to use
+ code like >
+ _VAL[0] * ((_VAL[1] << 62)
+ & (_VAL[2] << 31)
+ & _VAL[3])
+< Special dictionary with this type will appear in
+ |msgpackparse()| output under one of the following
+ circumstances:
+ 1. |Number| is 32-bit and value is either above
+ INT32_MAX or below INT32_MIN.
+ 2. |Number| is 64-bit and value is above INT64_MAX. It
+ cannot possibly be below INT64_MIN because msgpack
+ C parser does not support such values.
+ float |Float|. This value cannot possibly appear in
+ |msgpackparse()| output.
+ string |readfile()|-style list of strings. This value will
+ appear in |msgpackparse()| output if string contains
+ zero byte or if string is a mapping key and mapping is
+ being represented as special dictionary for other
+ reasons.
+ binary |String|, or |Blob| if binary string contains zero
+ byte. This value cannot appear in |msgpackparse()|
+ output since blobs were introduced.
+ array |List|. This value cannot appear in |msgpackparse()|
+ output.
+ *msgpack-special-map*
+ map |List| of |List|s with two items (key and value) each.
+ This value will appear in |msgpackparse()| output if
+ parsed mapping contains one of the following keys:
+ 1. Any key that is not a string (including keys which
+ are binary strings).
+ 2. String with NUL byte inside.
+ 3. Duplicate key.
+ 4. Empty key.
+ ext |List| with two values: first is a signed integer
+ representing extension type. Second is
+ |readfile()|-style list of strings.
+
+nextnonblank({lnum}) *nextnonblank()*
+ Return the line number of the first line at or below {lnum}
+ that is not blank. Example: >
+ if getline(nextnonblank(1)) =~ "Java"
+< When {lnum} is invalid or there is no non-blank line at or
+ below it, zero is returned.
+ {lnum} is used like with |getline()|.
+ See also |prevnonblank()|.
+
+ Can also be used as a |method|: >
+ GetLnum()->nextnonblank()
+
+nr2char({expr} [, {utf8}]) *nr2char()*
+ Return a string with a single character, which has the number
+ value {expr}. Examples: >
+ nr2char(64) returns "@"
+ nr2char(32) returns " "
+< Example for "utf-8": >
+ nr2char(300) returns I with bow character
+< UTF-8 encoding is always used, {utf8} option has no effect,
+ and exists only for backwards-compatibility.
+ Note that a NUL character in the file is specified with
+ nr2char(10), because NULs are represented with newline
+ characters. nr2char(0) is a real NUL and terminates the
+ string, thus results in an empty string.
+
+ Can also be used as a |method|: >
+ GetNumber()->nr2char()
+
+nvim_...({...}) *E5555* *nvim_...()* *eval-api*
+ Call nvim |api| functions. The type checking of arguments will
+ be stricter than for most other builtins. For instance,
+ if Integer is expected, a |Number| must be passed in, a
+ |String| will not be autoconverted.
+ Buffer numbers, as returned by |bufnr()| could be used as
+ first argument to nvim_buf_... functions. All functions
+ expecting an object (buffer, window or tabpage) can
+ also take the numerical value 0 to indicate the current
+ (focused) object.
+
+or({expr}, {expr}) *or()*
+ Bitwise OR on the two arguments. The arguments are converted
+ to a number. A List, Dict or Float argument causes an error.
+ Example: >
+ :let bits = or(bits, 0x80)
+< Can also be used as a |method|: >
+ :let bits = bits->or(0x80)
+
+pathshorten({expr} [, {len}]) *pathshorten()*
+ Shorten directory names in the path {path} and return the
+ result. The tail, the file name, is kept as-is. The other
+ components in the path are reduced to {len} letters in length.
+ If {len} is omitted or smaller than 1 then 1 is used (single
+ letters). Leading '~' and '.' characters are kept. Examples: >
+ :echo pathshorten('~/.config/nvim/autoload/file1.vim')
+< ~/.c/n/a/file1.vim ~
+>
+ :echo pathshorten('~/.config/nvim/autoload/file2.vim', 2)
+< ~/.co/nv/au/file2.vim ~
+ It doesn't matter if the path exists or not.
+ Returns an empty string on error.
+
+ Can also be used as a |method|: >
+ GetDirectories()->pathshorten()
+
+perleval({expr}) *perleval()*
+ Evaluate |perl| expression {expr} and return its result
+ converted to Vim data structures.
+ Numbers and strings are returned as they are (strings are
+ copied though).
+ Lists are represented as Vim |List| type.
+ Dictionaries are represented as Vim |Dictionary| type,
+ non-string keys result in error.
+
+ Note: If you want an array or hash, {expr} must return a
+ reference to it.
+ Example: >
+ :echo perleval('[1 .. 4]')
+< [1, 2, 3, 4]
+
+ Can also be used as a |method|: >
+ GetExpr()->perleval()
+
+pow({x}, {y}) *pow()*
+ Return the power of {x} to the exponent {y} as a |Float|.
+ {x} and {y} must evaluate to a |Float| or a |Number|.
+ Returns 0.0 if {x} or {y} is not a |Float| or a |Number|.
+ Examples: >
+ :echo pow(3, 3)
+< 27.0 >
+ :echo pow(2, 16)
+< 65536.0 >
+ :echo pow(32, 0.20)
+< 2.0
+
+ Can also be used as a |method|: >
+ Compute()->pow(3)
+
+prevnonblank({lnum}) *prevnonblank()*
+ Return the line number of the first line at or above {lnum}
+ that is not blank. Example: >
+ let ind = indent(prevnonblank(v:lnum - 1))
+< When {lnum} is invalid or there is no non-blank line at or
+ above it, zero is returned.
+ {lnum} is used like with |getline()|.
+ Also see |nextnonblank()|.
+
+ Can also be used as a |method|: >
+ GetLnum()->prevnonblank()
+
+printf({fmt}, {expr1} ...) *printf()*
+ Return a String with {fmt}, where "%" items are replaced by
+ the formatted form of their respective arguments. Example: >
+ printf("%4d: E%d %.30s", lnum, errno, msg)
+< May result in:
+ " 99: E42 asdfasdfasdfasdfasdfasdfasdfas" ~
+
+ When used as a |method| the base is passed as the second
+ argument: >
+ Compute()->printf("result: %d")
+<
+ You can use `call()` to pass the items as a list.
+
+ Often used items are:
+ %s string
+ %6S string right-aligned in 6 display cells
+ %6s string right-aligned in 6 bytes
+ %.9s string truncated to 9 bytes
+ %c single byte
+ %d decimal number
+ %5d decimal number padded with spaces to 5 characters
+ %b binary number
+ %08b binary number padded with zeros to at least 8 characters
+ %B binary number using upper case letters
+ %x hex number
+ %04x hex number padded with zeros to at least 4 characters
+ %X hex number using upper case letters
+ %o octal number
+ %f floating point number as 12.23, inf, -inf or nan
+ %F floating point number as 12.23, INF, -INF or NAN
+ %e floating point number as 1.23e3, inf, -inf or nan
+ %E floating point number as 1.23E3, INF, -INF or NAN
+ %g floating point number, as %f or %e depending on value
+ %G floating point number, as %F or %E depending on value
+ %% the % character itself
+ %p representation of the pointer to the container
+
+ Conversion specifications start with '%' and end with the
+ conversion type. All other characters are copied unchanged to
+ the result.
+
+ The "%" starts a conversion specification. The following
+ arguments appear in sequence:
+
+ % [flags] [field-width] [.precision] type
+
+ flags
+ Zero or more of the following flags:
+
+ # The value should be converted to an "alternate
+ form". For c, d, and s conversions, this option
+ has no effect. For o conversions, the precision
+ of the number is increased to force the first
+ character of the output string to a zero (except
+ if a zero value is printed with an explicit
+ precision of zero).
+ For x and X conversions, a non-zero result has
+ the string "0x" (or "0X" for X conversions)
+ prepended to it.
+
+ 0 (zero) Zero padding. For all conversions the converted
+ value is padded on the left with zeros rather
+ than blanks. If a precision is given with a
+ numeric conversion (d, o, x, and X), the 0 flag
+ is ignored.
+
+ - A negative field width flag; the converted value
+ is to be left adjusted on the field boundary.
+ The converted value is padded on the right with
+ blanks, rather than on the left with blanks or
+ zeros. A - overrides a 0 if both are given.
+
+ ' ' (space) A blank should be left before a positive
+ number produced by a signed conversion (d).
+
+ + A sign must always be placed before a number
+ produced by a signed conversion. A + overrides
+ a space if both are used.
+
+ field-width
+ An optional decimal digit string specifying a minimum
+ field width. If the converted value has fewer bytes
+ than the field width, it will be padded with spaces on
+ the left (or right, if the left-adjustment flag has
+ been given) to fill out the field width. For the S
+ conversion the count is in cells.
+
+ .precision
+ An optional precision, in the form of a period '.'
+ followed by an optional digit string. If the digit
+ string is omitted, the precision is taken as zero.
+ This gives the minimum number of digits to appear for
+ d, o, x, and X conversions, the maximum number of
+ bytes to be printed from a string for s conversions,
+ or the maximum number of cells to be printed from a
+ string for S conversions.
+ For floating point it is the number of digits after
+ the decimal point.
+
+ type
+ A character that specifies the type of conversion to
+ be applied, see below.
+
+ A field width or precision, or both, may be indicated by an
+ asterisk '*' instead of a digit string. In this case, a
+ Number argument supplies the field width or precision. A
+ negative field width is treated as a left adjustment flag
+ followed by a positive field width; a negative precision is
+ treated as though it were missing. Example: >
+ :echo printf("%d: %.*s", nr, width, line)
+< This limits the length of the text used from "line" to
+ "width" bytes.
+
+ The conversion specifiers and their meanings are:
+
+ *printf-d* *printf-b* *printf-B* *printf-o* *printf-x* *printf-X*
+ dbBoxX The Number argument is converted to signed decimal (d),
+ unsigned binary (b and B), unsigned octal (o), or
+ unsigned hexadecimal (x and X) notation. The letters
+ "abcdef" are used for x conversions; the letters
+ "ABCDEF" are used for X conversions. The precision, if
+ any, gives the minimum number of digits that must
+ appear; if the converted value requires fewer digits, it
+ is padded on the left with zeros. In no case does a
+ non-existent or small field width cause truncation of a
+ numeric field; if the result of a conversion is wider
+ than the field width, the field is expanded to contain
+ the conversion result.
+ The 'h' modifier indicates the argument is 16 bits.
+ The 'l' modifier indicates the argument is 32 bits.
+ The 'L' modifier indicates the argument is 64 bits.
+ Generally, these modifiers are not useful. They are
+ ignored when type is known from the argument.
+
+ i alias for d
+ D alias for ld
+ U alias for lu
+ O alias for lo
+
+ *printf-c*
+ c The Number argument is converted to a byte, and the
+ resulting character is written.
+
+ *printf-s*
+ s The text of the String argument is used. If a
+ precision is specified, no more bytes than the number
+ specified are used.
+ If the argument is not a String type, it is
+ automatically converted to text with the same format
+ as ":echo".
+ *printf-S*
+ S The text of the String argument is used. If a
+ precision is specified, no more display cells than the
+ number specified are used.
+
+ *printf-f* *E807*
+ f F The Float argument is converted into a string of the
+ form 123.456. The precision specifies the number of
+ digits after the decimal point. When the precision is
+ zero the decimal point is omitted. When the precision
+ is not specified 6 is used. A really big number
+ (out of range or dividing by zero) results in "inf"
+ or "-inf" with %f (INF or -INF with %F).
+ "0.0 / 0.0" results in "nan" with %f (NAN with %F).
+ Example: >
+ echo printf("%.2f", 12.115)
+< 12.12
+ Note that roundoff depends on the system libraries.
+ Use |round()| when in doubt.
+
+ *printf-e* *printf-E*
+ e E The Float argument is converted into a string of the
+ form 1.234e+03 or 1.234E+03 when using 'E'. The
+ precision specifies the number of digits after the
+ decimal point, like with 'f'.
+
+ *printf-g* *printf-G*
+ g G The Float argument is converted like with 'f' if the
+ value is between 0.001 (inclusive) and 10000000.0
+ (exclusive). Otherwise 'e' is used for 'g' and 'E'
+ for 'G'. When no precision is specified superfluous
+ zeroes and '+' signs are removed, except for the zero
+ immediately after the decimal point. Thus 10000000.0
+ results in 1.0e7.
+
+ *printf-%*
+ % A '%' is written. No argument is converted. The
+ complete conversion specification is "%%".
+
+ When a Number argument is expected a String argument is also
+ accepted and automatically converted.
+ When a Float or String argument is expected a Number argument
+ is also accepted and automatically converted.
+ Any other argument type results in an error message.
+
+ *E766* *E767*
+ The number of {exprN} arguments must exactly match the number
+ of "%" items. If there are not sufficient or too many
+ arguments an error is given. Up to 18 arguments can be used.
+
+prompt_getprompt({buf}) *prompt_getprompt()*
+ Returns the effective prompt text for buffer {buf}. {buf} can
+ be a buffer name or number. See |prompt-buffer|.
+
+ If the buffer doesn't exist or isn't a prompt buffer, an empty
+ string is returned.
+
+ Can also be used as a |method|: >
+ GetBuffer()->prompt_getprompt()
+
+prompt_setcallback({buf}, {expr}) *prompt_setcallback()*
+ Set prompt callback for buffer {buf} to {expr}. When {expr}
+ is an empty string the callback is removed. This has only
+ effect if {buf} has 'buftype' set to "prompt".
+
+ The callback is invoked when pressing Enter. The current
+ buffer will always be the prompt buffer. A new line for a
+ prompt is added before invoking the callback, thus the prompt
+ for which the callback was invoked will be in the last but one
+ line.
+ If the callback wants to add text to the buffer, it must
+ insert it above the last line, since that is where the current
+ prompt is. This can also be done asynchronously.
+ The callback is invoked with one argument, which is the text
+ that was entered at the prompt. This can be an empty string
+ if the user only typed Enter.
+ Example: >
+ call prompt_setcallback(bufnr(''), function('s:TextEntered'))
+ func s:TextEntered(text)
+ if a:text == 'exit' || a:text == 'quit'
+ stopinsert
+ close
+ else
+ call append(line('$') - 1, 'Entered: "' .. a:text .. '"')
+ " Reset 'modified' to allow the buffer to be closed.
+ set nomodified
+ endif
+ endfunc
+
+< Can also be used as a |method|: >
+ GetBuffer()->prompt_setcallback(callback)
+
+prompt_setinterrupt({buf}, {expr}) *prompt_setinterrupt()*
+ Set a callback for buffer {buf} to {expr}. When {expr} is an
+ empty string the callback is removed. This has only effect if
+ {buf} has 'buftype' set to "prompt".
+
+ This callback will be invoked when pressing CTRL-C in Insert
+ mode. Without setting a callback Vim will exit Insert mode,
+ as in any buffer.
+
+ Can also be used as a |method|: >
+ GetBuffer()->prompt_setinterrupt(callback)
+
+prompt_setprompt({buf}, {text}) *prompt_setprompt()*
+ Set prompt for buffer {buf} to {text}. You most likely want
+ {text} to end in a space.
+ The result is only visible if {buf} has 'buftype' set to
+ "prompt". Example: >
+ call prompt_setprompt(bufnr(''), 'command: ')
+<
+ Can also be used as a |method|: >
+ GetBuffer()->prompt_setprompt('command: ')
+
+pum_getpos() *pum_getpos()*
+ If the popup menu (see |ins-completion-menu|) is not visible,
+ returns an empty |Dictionary|, otherwise, returns a
+ |Dictionary| with the following keys:
+ height nr of items visible
+ width screen cells
+ row top screen row (0 first row)
+ col leftmost screen column (0 first col)
+ size total nr of items
+ scrollbar |TRUE| if scrollbar is visible
+
+ The values are the same as in |v:event| during |CompleteChanged|.
+
+pumvisible() *pumvisible()*
+ Returns non-zero when the popup menu is visible, zero
+ otherwise. See |ins-completion-menu|.
+ This can be used to avoid some things that would remove the
+ popup menu.
+
+py3eval({expr}) *py3eval()*
+ Evaluate Python expression {expr} and return its result
+ converted to Vim data structures.
+ Numbers and strings are returned as they are (strings are
+ copied though, Unicode strings are additionally converted to
+ UTF-8).
+ Lists are represented as Vim |List| type.
+ Dictionaries are represented as Vim |Dictionary| type with
+ keys converted to strings.
+
+ Can also be used as a |method|: >
+ GetExpr()->py3eval()
+<
+ *E858* *E859*
+pyeval({expr}) *pyeval()*
+ Evaluate Python expression {expr} and return its result
+ converted to Vim data structures.
+ Numbers and strings are returned as they are (strings are
+ copied though).
+ Lists are represented as Vim |List| type.
+ Dictionaries are represented as Vim |Dictionary| type,
+ non-string keys result in error.
+
+ Can also be used as a |method|: >
+ GetExpr()->pyeval()
+
+pyxeval({expr}) *pyxeval()*
+ Evaluate Python expression {expr} and return its result
+ converted to Vim data structures.
+ Uses Python 2 or 3, see |python_x| and 'pyxversion'.
+ See also: |pyeval()|, |py3eval()|
+
+ Can also be used as a |method|: >
+ GetExpr()->pyxeval()
+<
+ *E726* *E727*
+range({expr} [, {max} [, {stride}]]) *range()*
+ Returns a |List| with Numbers:
+ - If only {expr} is specified: [0, 1, ..., {expr} - 1]
+ - If {max} is specified: [{expr}, {expr} + 1, ..., {max}]
+ - If {stride} is specified: [{expr}, {expr} + {stride}, ...,
+ {max}] (increasing {expr} with {stride} each time, not
+ producing a value past {max}).
+ When the maximum is one before the start the result is an
+ empty list. When the maximum is more than one before the
+ start this is an error.
+ Examples: >
+ range(4) " [0, 1, 2, 3]
+ range(2, 4) " [2, 3, 4]
+ range(2, 9, 3) " [2, 5, 8]
+ range(2, -2, -1) " [2, 1, 0, -1, -2]
+ range(0) " []
+ range(2, 0) " error!
+<
+ Can also be used as a |method|: >
+ GetExpr()->range()
+<
+rand([{expr}]) *rand()*
+ Return a pseudo-random Number generated with an xoshiro128**
+ algorithm using seed {expr}. The returned number is 32 bits,
+ also on 64 bits systems, for consistency.
+ {expr} can be initialized by |srand()| and will be updated by
+ rand(). If {expr} is omitted, an internal seed value is used
+ and updated.
+ Returns -1 if {expr} is invalid.
+
+ Examples: >
+ :echo rand()
+ :let seed = srand()
+ :echo rand(seed)
+ :echo rand(seed) % 16 " random number 0 - 15
+<
+ Can also be used as a |method|: >
+ seed->rand()
+<
+ *readdir()*
+readdir({directory} [, {expr}])
+ Return a list with file and directory names in {directory}.
+
+ When {expr} is omitted all entries are included.
+ When {expr} is given, it is evaluated to check what to do:
+ If {expr} results in -1 then no further entries will
+ be handled.
+ If {expr} results in 0 then this entry will not be
+ added to the list.
+ If {expr} results in 1 then this entry will be added
+ to the list.
+ Each time {expr} is evaluated |v:val| is set to the entry name.
+ When {expr} is a function the name is passed as the argument.
+ For example, to get a list of files ending in ".txt": >
+ readdir(dirname, {n -> n =~ '.txt$'})
+< To skip hidden and backup files: >
+ readdir(dirname, {n -> n !~ '^\.\|\~$'})
+
+< If you want to get a directory tree: >
+ function! s:tree(dir)
+ return {a:dir : map(readdir(a:dir),
+ \ {_, x -> isdirectory(x) ?
+ \ {x : s:tree(a:dir .. '/' .. x)} : x})}
+ endfunction
+ echo s:tree(".")
+<
+ Returns an empty List on error.
+
+ Can also be used as a |method|: >
+ GetDirName()->readdir()
+<
+ *readfile()*
+readfile({fname} [, {type} [, {max}]])
+ Read file {fname} and return a |List|, each line of the file
+ as an item. Lines are broken at NL characters. Macintosh
+ files separated with CR will result in a single long line
+ (unless a NL appears somewhere).
+ All NUL characters are replaced with a NL character.
+ When {type} contains "b" binary mode is used:
+ - When the last line ends in a NL an extra empty list item is
+ added.
+ - No CR characters are removed.
+ When {type} contains "B" a |Blob| is returned with the binary
+ data of the file unmodified.
+ Otherwise:
+ - CR characters that appear before a NL are removed.
+ - Whether the last line ends in a NL or not does not matter.
+ - Any UTF-8 byte order mark is removed from the text.
+ When {max} is given this specifies the maximum number of lines
+ to be read. Useful if you only want to check the first ten
+ lines of a file: >
+ :for line in readfile(fname, '', 10)
+ : if line =~ 'Date' | echo line | endif
+ :endfor
+< When {max} is negative -{max} lines from the end of the file
+ are returned, or as many as there are.
+ When {max} is zero the result is an empty list.
+ Note that without {max} the whole file is read into memory.
+ Also note that there is no recognition of encoding. Read a
+ file into a buffer if you need to.
+ When the file can't be opened an error message is given and
+ the result is an empty list.
+ Also see |writefile()|.
+
+ Can also be used as a |method|: >
+ GetFileName()->readfile()
+
+reduce({object}, {func} [, {initial}]) *reduce()* *E998*
+ {func} is called for every item in {object}, which can be a
+ |List| or a |Blob|. {func} is called with two arguments: the
+ result so far and current item. After processing all items
+ the result is returned.
+
+ {initial} is the initial result. When omitted, the first item
+ in {object} is used and {func} is first called for the second
+ item. If {initial} is not given and {object} is empty no
+ result can be computed, an E998 error is given.
+
+ Examples: >
+ echo reduce([1, 3, 5], { acc, val -> acc + val })
+ echo reduce(['x', 'y'], { acc, val -> acc .. val }, 'a')
+ echo reduce(0z1122, { acc, val -> 2 * acc + val })
+<
+ Can also be used as a |method|: >
+ echo mylist->reduce({ acc, val -> acc + val }, 0)
+
+reg_executing() *reg_executing()*
+ Returns the single letter name of the register being executed.
+ Returns an empty string when no register is being executed.
+ See |@|.
+
+reg_recorded() *reg_recorded()*
+ Returns the single letter name of the last recorded register.
+ Returns an empty string when nothing was recorded yet.
+ See |q| and |Q|.
+
+reg_recording() *reg_recording()*
+ Returns the single letter name of the register being recorded.
+ Returns an empty string when not recording. See |q|.
+
+reltime([{start} [, {end}]]) *reltime()*
+ Return an item that represents a time value. The item is a
+ list with items that depend on the system.
+ The item can be passed to |reltimestr()| to convert it to a
+ string or |reltimefloat()| to convert to a Float.
+
+ Without an argument it returns the current "relative time", an
+ implementation-defined value meaningful only when used as an
+ argument to |reltime()|, |reltimestr()| and |reltimefloat()|.
+
+ With one argument it returns the time passed since the time
+ specified in the argument.
+ With two arguments it returns the time passed between {start}
+ and {end}.
+
+ The {start} and {end} arguments must be values returned by
+ reltime(). Returns zero on error.
+
+ Can also be used as a |method|: >
+ GetStart()->reltime()
+<
+ Note: |localtime()| returns the current (non-relative) time.
+
+reltimefloat({time}) *reltimefloat()*
+ Return a Float that represents the time value of {time}.
+ Unit of time is seconds.
+ Example:
+ let start = reltime()
+ call MyFunction()
+ let seconds = reltimefloat(reltime(start))
+ See the note of reltimestr() about overhead.
+ Also see |profiling|.
+ If there is an error an empty string is returned
+
+ Can also be used as a |method|: >
+ reltime(start)->reltimefloat()
+
+reltimestr({time}) *reltimestr()*
+ Return a String that represents the time value of {time}.
+ This is the number of seconds, a dot and the number of
+ microseconds. Example: >
+ let start = reltime()
+ call MyFunction()
+ echo reltimestr(reltime(start))
+< Note that overhead for the commands will be added to the time.
+ Leading spaces are used to make the string align nicely. You
+ can use split() to remove it. >
+ echo split(reltimestr(reltime(start)))[0]
+< Also see |profiling|.
+ If there is an error an empty string is returned
+
+ Can also be used as a |method|: >
+ reltime(start)->reltimestr()
+<
+remove({list}, {idx} [, {end}]) *remove()*
+ Without {end}: Remove the item at {idx} from |List| {list} and
+ return the item.
+ With {end}: Remove items from {idx} to {end} (inclusive) and
+ return a |List| with these items. When {idx} points to the same
+ item as {end} a list with one item is returned. When {end}
+ points to an item before {idx} this is an error.
+ See |list-index| for possible values of {idx} and {end}.
+ Returns zero on error.
+ Example: >
+ :echo "last item: " .. remove(mylist, -1)
+ :call remove(mylist, 0, 9)
+<
+ Use |delete()| to remove a file.
+
+ Can also be used as a |method|: >
+ mylist->remove(idx)
+
+remove({blob}, {idx} [, {end}])
+ Without {end}: Remove the byte at {idx} from |Blob| {blob} and
+ return the byte.
+ With {end}: Remove bytes from {idx} to {end} (inclusive) and
+ return a |Blob| with these bytes. When {idx} points to the same
+ byte as {end} a |Blob| with one byte is returned. When {end}
+ points to a byte before {idx} this is an error.
+ Returns zero on error.
+ Example: >
+ :echo "last byte: " .. remove(myblob, -1)
+ :call remove(mylist, 0, 9)
+
+remove({dict}, {key})
+ Remove the entry from {dict} with key {key} and return it.
+ Example: >
+ :echo "removed " .. remove(dict, "one")
+< If there is no {key} in {dict} this is an error.
+ Returns zero on error.
+
+rename({from}, {to}) *rename()*
+ Rename the file by the name {from} to the name {to}. This
+ should also work to move files across file systems. The
+ result is a Number, which is 0 if the file was renamed
+ successfully, and non-zero when the renaming failed.
+ NOTE: If {to} exists it is overwritten without warning.
+ This function is not available in the |sandbox|.
+
+ Can also be used as a |method|: >
+ GetOldName()->rename(newname)
+
+repeat({expr}, {count}) *repeat()*
+ Repeat {expr} {count} times and return the concatenated
+ result. Example: >
+ :let separator = repeat('-', 80)
+< When {count} is zero or negative the result is empty.
+ When {expr} is a |List| the result is {expr} concatenated
+ {count} times. Example: >
+ :let longlist = repeat(['a', 'b'], 3)
+< Results in ['a', 'b', 'a', 'b', 'a', 'b'].
+
+ Can also be used as a |method|: >
+ mylist->repeat(count)
+
+resolve({filename}) *resolve()* *E655*
+ On MS-Windows, when {filename} is a shortcut (a .lnk file),
+ returns the path the shortcut points to in a simplified form.
+ On Unix, repeat resolving symbolic links in all path
+ components of {filename} and return the simplified result.
+ To cope with link cycles, resolving of symbolic links is
+ stopped after 100 iterations.
+ On other systems, return the simplified {filename}.
+ The simplification step is done as by |simplify()|.
+ resolve() keeps a leading path component specifying the
+ current directory (provided the result is still a relative
+ path name) and also keeps a trailing path separator.
+
+ Can also be used as a |method|: >
+ GetName()->resolve()
+<
+ *reverse()*
+reverse({object})
+ Reverse the order of items in {object} in-place.
+ {object} can be a |List| or a |Blob|.
+ Returns {object}.
+ Returns zero if {object} is not a List or a Blob.
+ If you want an object to remain unmodified make a copy first: >
+ :let revlist = reverse(copy(mylist))
+< Can also be used as a |method|: >
+ mylist->reverse()
+
+round({expr}) *round()*
+ Round off {expr} to the nearest integral value and return it
+ as a |Float|. If {expr} lies halfway between two integral
+ values, then use the larger one (away from zero).
+ {expr} must evaluate to a |Float| or a |Number|.
+ Returns 0.0 if {expr} is not a |Float| or a |Number|.
+ Examples: >
+ echo round(0.456)
+< 0.0 >
+ echo round(4.5)
+< 5.0 >
+ echo round(-4.5)
+< -5.0
+
+ Can also be used as a |method|: >
+ Compute()->round()
+
+rpcnotify({channel}, {event} [, {args}...]) *rpcnotify()*
+ Sends {event} to {channel} via |RPC| and returns immediately.
+ If {channel} is 0, the event is broadcast to all channels.
+ Example: >
+ :au VimLeave call rpcnotify(0, "leaving")
+
+rpcrequest({channel}, {method} [, {args}...]) *rpcrequest()*
+ Sends a request to {channel} to invoke {method} via
+ |RPC| and blocks until a response is received.
+ Example: >
+ :let result = rpcrequest(rpc_chan, "func", 1, 2, 3)
+
+rpcstart({prog} [, {argv}]) *rpcstart()*
+ Deprecated. Replace >
+ :let id = rpcstart('prog', ['arg1', 'arg2'])
+< with >
+ :let id = jobstart(['prog', 'arg1', 'arg2'], {'rpc': v:true})
+
+rubyeval({expr}) *rubyeval()*
+ Evaluate Ruby expression {expr} and return its result
+ converted to Vim data structures.
+ Numbers, floats and strings are returned as they are (strings
+ are copied though).
+ Arrays are represented as Vim |List| type.
+ Hashes are represented as Vim |Dictionary| type.
+ Other objects are represented as strings resulted from their
+ "Object#to_s" method.
+
+ Can also be used as a |method|: >
+ GetRubyExpr()->rubyeval()
+
+screenattr({row}, {col}) *screenattr()*
+ Like |screenchar()|, but return the attribute. This is a rather
+ arbitrary number that can only be used to compare to the
+ attribute at other positions.
+ Returns -1 when row or col is out of range.
+
+ Can also be used as a |method|: >
+ GetRow()->screenattr(col)
+
+screenchar({row}, {col}) *screenchar()*
+ The result is a Number, which is the character at position
+ [row, col] on the screen. This works for every possible
+ screen position, also status lines, window separators and the
+ command line. The top left position is row one, column one
+ The character excludes composing characters. For double-byte
+ encodings it may only be the first byte.
+ This is mainly to be used for testing.
+ Returns -1 when row or col is out of range.
+
+ Can also be used as a |method|: >
+ GetRow()->screenchar(col)
+
+screenchars({row}, {col}) *screenchars()*
+ The result is a List of Numbers. The first number is the same
+ as what |screenchar()| returns. Further numbers are
+ composing characters on top of the base character.
+ This is mainly to be used for testing.
+ Returns an empty List when row or col is out of range.
+
+ Can also be used as a |method|: >
+ GetRow()->screenchars(col)
+
+screencol() *screencol()*
+ The result is a Number, which is the current screen column of
+ the cursor. The leftmost column has number 1.
+ This function is mainly used for testing.
+
+ Note: Always returns the current screen column, thus if used
+ in a command (e.g. ":echo screencol()") it will return the
+ column inside the command line, which is 1 when the command is
+ executed. To get the cursor position in the file use one of
+ the following mappings: >
+ nnoremap <expr> GG ":echom " .. screencol() .. "\n"
+ nnoremap <silent> GG :echom screencol()<CR>
+ noremap GG <Cmd>echom screencol()<Cr>
+<
+screenpos({winid}, {lnum}, {col}) *screenpos()*
+ The result is a Dict with the screen position of the text
+ character in window {winid} at buffer line {lnum} and column
+ {col}. {col} is a one-based byte index.
+ The Dict has these members:
+ row screen row
+ col first screen column
+ endcol last screen column
+ curscol cursor screen column
+ If the specified position is not visible, all values are zero.
+ The "endcol" value differs from "col" when the character
+ occupies more than one screen cell. E.g. for a Tab "col" can
+ be 1 and "endcol" can be 8.
+ The "curscol" value is where the cursor would be placed. For
+ a Tab it would be the same as "endcol", while for a double
+ width character it would be the same as "col".
+ The |conceal| feature is ignored here, the column numbers are
+ as if 'conceallevel' is zero. You can set the cursor to the
+ right position and use |screencol()| to get the value with
+ |conceal| taken into account.
+ Returns an empty Dict if {winid} is invalid.
+
+ Can also be used as a |method|: >
+ GetWinid()->screenpos(lnum, col)
+
+screenrow() *screenrow()*
+ The result is a Number, which is the current screen row of the
+ cursor. The top line has number one.
+ This function is mainly used for testing.
+ Alternatively you can use |winline()|.
+
+ Note: Same restrictions as with |screencol()|.
+
+screenstring({row}, {col}) *screenstring()*
+ The result is a String that contains the base character and
+ any composing characters at position [row, col] on the screen.
+ This is like |screenchars()| but returning a String with the
+ characters.
+ This is mainly to be used for testing.
+ Returns an empty String when row or col is out of range.
+
+ Can also be used as a |method|: >
+ GetRow()->screenstring(col)
+<
+ *search()*
+search({pattern} [, {flags} [, {stopline} [, {timeout} [, {skip}]]]])
+ Search for regexp pattern {pattern}. The search starts at the
+ cursor position (you can use |cursor()| to set it).
+
+ When a match has been found its line number is returned.
+ If there is no match a 0 is returned and the cursor doesn't
+ move. No error message is given.
+
+ {flags} is a String, which can contain these character flags:
+ 'b' search Backward instead of forward
+ 'c' accept a match at the Cursor position
+ 'e' move to the End of the match
+ 'n' do Not move the cursor
+ 'p' return number of matching sub-Pattern (see below)
+ 's' Set the ' mark at the previous location of the cursor
+ 'w' Wrap around the end of the file
+ 'W' don't Wrap around the end of the file
+ 'z' start searching at the cursor column instead of Zero
+ If neither 'w' or 'W' is given, the 'wrapscan' option applies.
+
+ If the 's' flag is supplied, the ' mark is set, only if the
+ cursor is moved. The 's' flag cannot be combined with the 'n'
+ flag.
+
+ 'ignorecase', 'smartcase' and 'magic' are used.
+
+ When the 'z' flag is not given, forward searching always
+ starts in column zero and then matches before the cursor are
+ skipped. When the 'c' flag is present in 'cpo' the next
+ search starts after the match. Without the 'c' flag the next
+ search starts one column further. This matters for
+ overlapping matches.
+ When searching backwards and the 'z' flag is given then the
+ search starts in column zero, thus no match in the current
+ line will be found (unless wrapping around the end of the
+ file).
+
+ When the {stopline} argument is given then the search stops
+ after searching this line. This is useful to restrict the
+ search to a range of lines. Examples: >
+ let match = search('(', 'b', line("w0"))
+ let end = search('END', '', line("w$"))
+< When {stopline} is used and it is not zero this also implies
+ that the search does not wrap around the end of the file.
+ A zero value is equal to not giving the argument.
+
+ When the {timeout} argument is given the search stops when
+ more than this many milliseconds have passed. Thus when
+ {timeout} is 500 the search stops after half a second.
+ The value must not be negative. A zero value is like not
+ giving the argument.
+
+ If the {skip} expression is given it is evaluated with the
+ cursor positioned on the start of a match. If it evaluates to
+ non-zero this match is skipped. This can be used, for
+ example, to skip a match in a comment or a string.
+ {skip} can be a string, which is evaluated as an expression, a
+ function reference or a lambda.
+ When {skip} is omitted or empty, every match is accepted.
+ When evaluating {skip} causes an error the search is aborted
+ and -1 returned.
+ *search()-sub-match*
+ With the 'p' flag the returned value is one more than the
+ first sub-match in \(\). One if none of them matched but the
+ whole pattern did match.
+ To get the column number too use |searchpos()|.
+
+ The cursor will be positioned at the match, unless the 'n'
+ flag is used.
+
+ Example (goes over all files in the argument list): >
+ :let n = 1
+ :while n <= argc() " loop over all files in arglist
+ : exe "argument " .. n
+ : " start at the last char in the file and wrap for the
+ : " first search to find match at start of file
+ : normal G$
+ : let flags = "w"
+ : while search("foo", flags) > 0
+ : s/foo/bar/g
+ : let flags = "W"
+ : endwhile
+ : update " write the file if modified
+ : let n = n + 1
+ :endwhile
+<
+ Example for using some flags: >
+ :echo search('\<if\|\(else\)\|\(endif\)', 'ncpe')
+< This will search for the keywords "if", "else", and "endif"
+ under or after the cursor. Because of the 'p' flag, it
+ returns 1, 2, or 3 depending on which keyword is found, or 0
+ if the search fails. With the cursor on the first word of the
+ line:
+ if (foo == 0) | let foo = foo + 1 | endif ~
+ the function returns 1. Without the 'c' flag, the function
+ finds the "endif" and returns 3. The same thing happens
+ without the 'e' flag if the cursor is on the "f" of "if".
+ The 'n' flag tells the function not to move the cursor.
+
+ Can also be used as a |method|: >
+ GetPattern()->search()
+
+searchcount([{options}]) *searchcount()*
+ Get or update the last search count, like what is displayed
+ without the "S" flag in 'shortmess'. This works even if
+ 'shortmess' does contain the "S" flag.
+
+ This returns a Dictionary. The dictionary is empty if the
+ previous pattern was not set and "pattern" was not specified.
+
+ key type meaning ~
+ current |Number| current position of match;
+ 0 if the cursor position is
+ before the first match
+ exact_match |Boolean| 1 if "current" is matched on
+ "pos", otherwise 0
+ total |Number| total count of matches found
+ incomplete |Number| 0: search was fully completed
+ 1: recomputing was timed out
+ 2: max count exceeded
+
+ For {options} see further down.
+
+ To get the last search count when |n| or |N| was pressed, call
+ this function with `recompute: 0` . This sometimes returns
+ wrong information because |n| and |N|'s maximum count is 99.
+ If it exceeded 99 the result must be max count + 1 (100). If
+ you want to get correct information, specify `recompute: 1`: >
+
+ " result == maxcount + 1 (100) when many matches
+ let result = searchcount(#{recompute: 0})
+
+ " Below returns correct result (recompute defaults
+ " to 1)
+ let result = searchcount()
+<
+ The function is useful to add the count to |statusline|: >
+ function! LastSearchCount() abort
+ let result = searchcount(#{recompute: 0})
+ if empty(result)
+ return ''
+ endif
+ if result.incomplete ==# 1 " timed out
+ return printf(' /%s [?/??]', @/)
+ elseif result.incomplete ==# 2 " max count exceeded
+ if result.total > result.maxcount &&
+ \ result.current > result.maxcount
+ return printf(' /%s [>%d/>%d]', @/,
+ \ result.current, result.total)
+ elseif result.total > result.maxcount
+ return printf(' /%s [%d/>%d]', @/,
+ \ result.current, result.total)
+ endif
+ endif
+ return printf(' /%s [%d/%d]', @/,
+ \ result.current, result.total)
+ endfunction
+ let &statusline ..= '%{LastSearchCount()}'
+
+ " Or if you want to show the count only when
+ " 'hlsearch' was on
+ " let &statusline ..=
+ " \ '%{v:hlsearch ? LastSearchCount() : ""}'
+<
+ You can also update the search count, which can be useful in a
+ |CursorMoved| or |CursorMovedI| autocommand: >
+
+ autocmd CursorMoved,CursorMovedI *
+ \ let s:searchcount_timer = timer_start(
+ \ 200, function('s:update_searchcount'))
+ function! s:update_searchcount(timer) abort
+ if a:timer ==# s:searchcount_timer
+ call searchcount(#{
+ \ recompute: 1, maxcount: 0, timeout: 100})
+ redrawstatus
+ endif
+ endfunction
+<
+ This can also be used to count matched texts with specified
+ pattern in the current buffer using "pattern": >
+
+ " Count '\<foo\>' in this buffer
+ " (Note that it also updates search count)
+ let result = searchcount(#{pattern: '\<foo\>'})
+
+ " To restore old search count by old pattern,
+ " search again
+ call searchcount()
+<
+ {options} must be a Dictionary. It can contain:
+ key type meaning ~
+ recompute |Boolean| if |TRUE|, recompute the count
+ like |n| or |N| was executed.
+ otherwise returns the last
+ result by |n|, |N|, or this
+ function is returned.
+ (default: |TRUE|)
+ pattern |String| recompute if this was given
+ and different with |@/|.
+ this works as same as the
+ below command is executed
+ before calling this function >
+ let @/ = pattern
+< (default: |@/|)
+ timeout |Number| 0 or negative number is no
+ timeout. timeout milliseconds
+ for recomputing the result
+ (default: 0)
+ maxcount |Number| 0 or negative number is no
+ limit. max count of matched
+ text while recomputing the
+ result. if search exceeded
+ total count, "total" value
+ becomes `maxcount + 1`
+ (default: 0)
+ pos |List| `[lnum, col, off]` value
+ when recomputing the result.
+ this changes "current" result
+ value. see |cursor()|, |getpos()
+ (default: cursor's position)
+
+ Can also be used as a |method|: >
+ GetSearchOpts()->searchcount()
+<
+searchdecl({name} [, {global} [, {thisblock}]]) *searchdecl()*
+ Search for the declaration of {name}.
+
+ With a non-zero {global} argument it works like |gD|, find
+ first match in the file. Otherwise it works like |gd|, find
+ first match in the function.
+
+ With a non-zero {thisblock} argument matches in a {} block
+ that ends before the cursor position are ignored. Avoids
+ finding variable declarations only valid in another scope.
+
+ Moves the cursor to the found match.
+ Returns zero for success, non-zero for failure.
+ Example: >
+ if searchdecl('myvar') == 0
+ echo getline('.')
+ endif
+<
+ Can also be used as a |method|: >
+ GetName()->searchdecl()
+<
+ *searchpair()*
+searchpair({start}, {middle}, {end} [, {flags} [, {skip}
+ [, {stopline} [, {timeout}]]]])
+ Search for the match of a nested start-end pair. This can be
+ used to find the "endif" that matches an "if", while other
+ if/endif pairs in between are ignored.
+ The search starts at the cursor. The default is to search
+ forward, include 'b' in {flags} to search backward.
+ If a match is found, the cursor is positioned at it and the
+ line number is returned. If no match is found 0 or -1 is
+ returned and the cursor doesn't move. No error message is
+ given.
+
+ {start}, {middle} and {end} are patterns, see |pattern|. They
+ must not contain \( \) pairs. Use of \%( \) is allowed. When
+ {middle} is not empty, it is found when searching from either
+ direction, but only when not in a nested start-end pair. A
+ typical use is: >
+ searchpair('\<if\>', '\<else\>', '\<endif\>')
+< By leaving {middle} empty the "else" is skipped.
+
+ {flags} 'b', 'c', 'n', 's', 'w' and 'W' are used like with
+ |search()|. Additionally:
+ 'r' Repeat until no more matches found; will find the
+ outer pair. Implies the 'W' flag.
+ 'm' Return number of matches instead of line number with
+ the match; will be > 1 when 'r' is used.
+ Note: it's nearly always a good idea to use the 'W' flag, to
+ avoid wrapping around the end of the file.
+
+ When a match for {start}, {middle} or {end} is found, the
+ {skip} expression is evaluated with the cursor positioned on
+ the start of the match. It should return non-zero if this
+ match is to be skipped. E.g., because it is inside a comment
+ or a string.
+ When {skip} is omitted or empty, every match is accepted.
+ When evaluating {skip} causes an error the search is aborted
+ and -1 returned.
+ {skip} can be a string, a lambda, a funcref or a partial.
+ Anything else makes the function fail.
+
+ For {stopline} and {timeout} see |search()|.
+
+ The value of 'ignorecase' is used. 'magic' is ignored, the
+ patterns are used like it's on.
+
+ The search starts exactly at the cursor. A match with
+ {start}, {middle} or {end} at the next character, in the
+ direction of searching, is the first one found. Example: >
+ if 1
+ if 2
+ endif 2
+ endif 1
+< When starting at the "if 2", with the cursor on the "i", and
+ searching forwards, the "endif 2" is found. When starting on
+ the character just before the "if 2", the "endif 1" will be
+ found. That's because the "if 2" will be found first, and
+ then this is considered to be a nested if/endif from "if 2" to
+ "endif 2".
+ When searching backwards and {end} is more than one character,
+ it may be useful to put "\zs" at the end of the pattern, so
+ that when the cursor is inside a match with the end it finds
+ the matching start.
+
+ Example, to find the "endif" command in a Vim script: >
+
+ :echo searchpair('\<if\>', '\<el\%[seif]\>', '\<en\%[dif]\>', 'W',
+ \ 'getline(".") =~ "^\\s*\""')
+
+< The cursor must be at or after the "if" for which a match is
+ to be found. Note that single-quote strings are used to avoid
+ having to double the backslashes. The skip expression only
+ catches comments at the start of a line, not after a command.
+ Also, a word "en" or "if" halfway through a line is considered
+ a match.
+ Another example, to search for the matching "{" of a "}": >
+
+ :echo searchpair('{', '', '}', 'bW')
+
+< This works when the cursor is at or before the "}" for which a
+ match is to be found. To reject matches that syntax
+ highlighting recognized as strings: >
+
+ :echo searchpair('{', '', '}', 'bW',
+ \ 'synIDattr(synID(line("."), col("."), 0), "name") =~? "string"')
+<
+ *searchpairpos()*
+searchpairpos({start}, {middle}, {end} [, {flags} [, {skip}
+ [, {stopline} [, {timeout}]]]])
+ Same as |searchpair()|, but returns a |List| with the line and
+ column position of the match. The first element of the |List|
+ is the line number and the second element is the byte index of
+ the column position of the match. If no match is found,
+ returns [0, 0]. >
+
+ :let [lnum,col] = searchpairpos('{', '', '}', 'n')
+<
+ See |match-parens| for a bigger and more useful example.
+
+ *searchpos()*
+searchpos({pattern} [, {flags} [, {stopline} [, {timeout} [, {skip}]]]])
+ Same as |search()|, but returns a |List| with the line and
+ column position of the match. The first element of the |List|
+ is the line number and the second element is the byte index of
+ the column position of the match. If no match is found,
+ returns [0, 0].
+ Example: >
+ :let [lnum, col] = searchpos('mypattern', 'n')
+
+< When the 'p' flag is given then there is an extra item with
+ the sub-pattern match number |search()-sub-match|. Example: >
+ :let [lnum, col, submatch] = searchpos('\(\l\)\|\(\u\)', 'np')
+< In this example "submatch" is 2 when a lowercase letter is
+ found |/\l|, 3 when an uppercase letter is found |/\u|.
+
+ Can also be used as a |method|: >
+ GetPattern()->searchpos()
+
+serverlist() *serverlist()*
+ Returns a list of server addresses, or empty if all servers
+ were stopped. |serverstart()| |serverstop()|
+ Example: >
+ :echo serverlist()
+
+serverstart([{address}]) *serverstart()*
+ Opens a socket or named pipe at {address} and listens for
+ |RPC| messages. Clients can send |API| commands to the
+ returned address to control Nvim.
+
+ Returns the address string (may differ from the requested
+ {address}).
+
+ - If {address} contains a colon ":" it is interpreted as
+ a TCP/IPv4/IPv6 address where the last ":" separates host
+ and port (empty or zero assigns a random port).
+ - Else it is interpreted as a named pipe or Unix domain socket
+ path. If there are no slashes it is treated as a name and
+ appended to a generated path.
+ - If {address} is empty it generates a path.
+
+ Example named pipe: >
+ if has('win32')
+ echo serverstart('\\.\pipe\nvim-pipe-1234')
+ else
+ echo serverstart('nvim.sock')
+ endif
+<
+ Example TCP/IP address: >
+ echo serverstart('::1:12345')
+
+serverstop({address}) *serverstop()*
+ Closes the pipe or socket at {address}.
+ Returns TRUE if {address} is valid, else FALSE.
+ If |v:servername| is stopped it is set to the next available
+ address in |serverlist()|.
+
+setbufline({buf}, {lnum}, {text}) *setbufline()*
+ Set line {lnum} to {text} in buffer {buf}. This works like
+ |setline()| for the specified buffer.
+
+ This function works only for loaded buffers. First call
+ |bufload()| if needed.
+
+ To insert lines use |appendbufline()|.
+ Any text properties in {lnum} are cleared.
+
+ {text} can be a string to set one line, or a list of strings
+ to set multiple lines. If the list extends below the last
+ line then those lines are added.
+
+ For the use of {buf}, see |bufname()| above.
+
+ {lnum} is used like with |setline()|.
+ Use "$" to refer to the last line in buffer {buf}.
+ When {lnum} is just below the last line the {text} will be
+ added below the last line.
+ On success 0 is returned, on failure 1 is returned.
+
+ If {buf} is not a valid buffer or {lnum} is not valid, an
+ error message is given.
+
+ Can also be used as a |method|, the base is passed as the
+ third argument: >
+ GetText()->setbufline(buf, lnum)
+
+setbufvar({buf}, {varname}, {val}) *setbufvar()*
+ Set option or local variable {varname} in buffer {buf} to
+ {val}.
+ This also works for a global or local window option, but it
+ doesn't work for a global or local window variable.
+ For a local window option the global value is unchanged.
+ For the use of {buf}, see |bufname()| above.
+ The {varname} argument is a string.
+ Note that the variable name without "b:" must be used.
+ Examples: >
+ :call setbufvar(1, "&mod", 1)
+ :call setbufvar("todo", "myvar", "foobar")
+< This function is not available in the |sandbox|.
+
+ Can also be used as a |method|, the base is passed as the
+ third argument: >
+ GetValue()->setbufvar(buf, varname)
+
+setcharpos({expr}, {list}) *setcharpos()*
+ Same as |setpos()| but uses the specified column number as the
+ character index instead of the byte index in the line.
+
+ Example:
+ With the text "여보세요" in line 8: >
+ call setcharpos('.', [0, 8, 4, 0])
+< positions the cursor on the fourth character '요'. >
+ call setpos('.', [0, 8, 4, 0])
+< positions the cursor on the second character '보'.
+
+ Can also be used as a |method|: >
+ GetPosition()->setcharpos('.')
+
+setcharsearch({dict}) *setcharsearch()*
+ Set the current character search information to {dict},
+ which contains one or more of the following entries:
+
+ char character which will be used for a subsequent
+ |,| or |;| command; an empty string clears the
+ character search
+ forward direction of character search; 1 for forward,
+ 0 for backward
+ until type of character search; 1 for a |t| or |T|
+ character search, 0 for an |f| or |F|
+ character search
+
+ This can be useful to save/restore a user's character search
+ from a script: >
+ :let prevsearch = getcharsearch()
+ :" Perform a command which clobbers user's search
+ :call setcharsearch(prevsearch)
+< Also see |getcharsearch()|.
+
+ Can also be used as a |method|: >
+ SavedSearch()->setcharsearch()
+
+setcmdpos({pos}) *setcmdpos()*
+ Set the cursor position in the command line to byte position
+ {pos}. The first position is 1.
+ Use |getcmdpos()| to obtain the current position.
+ Only works while editing the command line, thus you must use
+ |c_CTRL-\_e|, |c_CTRL-R_=| or |c_CTRL-R_CTRL-R| with '='. For
+ |c_CTRL-\_e| and |c_CTRL-R_CTRL-R| with '=' the position is
+ set after the command line is set to the expression. For
+ |c_CTRL-R_=| it is set after evaluating the expression but
+ before inserting the resulting text.
+ When the number is too big the cursor is put at the end of the
+ line. A number smaller than one has undefined results.
+ Returns FALSE when successful, TRUE when not editing the
+ command line.
+
+ Can also be used as a |method|: >
+ GetPos()->setcmdpos()
+
+setcursorcharpos({lnum}, {col} [, {off}]) *setcursorcharpos()*
+setcursorcharpos({list})
+ Same as |cursor()| but uses the specified column number as the
+ character index instead of the byte index in the line.
+
+ Example:
+ With the text "여보세요" in line 4: >
+ call setcursorcharpos(4, 3)
+< positions the cursor on the third character '세'. >
+ call cursor(4, 3)
+< positions the cursor on the first character '여'.
+
+ Can also be used as a |method|: >
+ GetCursorPos()->setcursorcharpos()
+
+setenv({name}, {val}) *setenv()*
+ Set environment variable {name} to {val}. Example: >
+ call setenv('HOME', '/home/myhome')
+
+< When {val} is |v:null| the environment variable is deleted.
+ See also |expr-env|.
+
+ Can also be used as a |method|, the base is passed as the
+ second argument: >
+ GetPath()->setenv('PATH')
+
+setfperm({fname}, {mode}) *setfperm()* *chmod*
+ Set the file permissions for {fname} to {mode}.
+ {mode} must be a string with 9 characters. It is of the form
+ "rwxrwxrwx", where each group of "rwx" flags represent, in
+ turn, the permissions of the owner of the file, the group the
+ file belongs to, and other users. A '-' character means the
+ permission is off, any other character means on. Multi-byte
+ characters are not supported.
+
+ For example "rw-r-----" means read-write for the user,
+ readable by the group, not accessible by others. "xx-x-----"
+ would do the same thing.
+
+ Returns non-zero for success, zero for failure.
+
+ Can also be used as a |method|: >
+ GetFilename()->setfperm(mode)
+<
+ To read permissions see |getfperm()|.
+
+setline({lnum}, {text}) *setline()*
+ Set line {lnum} of the current buffer to {text}. To insert
+ lines use |append()|. To set lines in another buffer use
+ |setbufline()|.
+
+ {lnum} is used like with |getline()|.
+ When {lnum} is just below the last line the {text} will be
+ added below the last line.
+
+ If this succeeds, FALSE is returned. If this fails (most likely
+ because {lnum} is invalid) TRUE is returned.
+
+ Example: >
+ :call setline(5, strftime("%c"))
+
+< When {text} is a |List| then line {lnum} and following lines
+ will be set to the items in the list. Example: >
+ :call setline(5, ['aaa', 'bbb', 'ccc'])
+< This is equivalent to: >
+ :for [n, l] in [[5, 'aaa'], [6, 'bbb'], [7, 'ccc']]
+ : call setline(n, l)
+ :endfor
+
+< Note: The '[ and '] marks are not set.
+
+ Can also be used as a |method|, the base is passed as the
+ second argument: >
+ GetText()->setline(lnum)
+
+setloclist({nr}, {list} [, {action} [, {what}]]) *setloclist()*
+ Create or replace or add to the location list for window {nr}.
+ {nr} can be the window number or the |window-ID|.
+ When {nr} is zero the current window is used.
+
+ For a location list window, the displayed location list is
+ modified. For an invalid window number {nr}, -1 is returned.
+ Otherwise, same as |setqflist()|.
+ Also see |location-list|.
+
+ For {action} see |setqflist-action|.
+
+ If the optional {what} dictionary argument is supplied, then
+ only the items listed in {what} are set. Refer to |setqflist()|
+ for the list of supported keys in {what}.
+
+ Can also be used as a |method|, the base is passed as the
+ second argument: >
+ GetLoclist()->setloclist(winnr)
+
+setmatches({list} [, {win}]) *setmatches()*
+ Restores a list of matches saved by |getmatches() for the
+ current window|. Returns 0 if successful, otherwise -1. All
+ current matches are cleared before the list is restored. See
+ example for |getmatches()|.
+ If {win} is specified, use the window with this number or
+ window ID instead of the current window.
+
+ Can also be used as a |method|: >
+ GetMatches()->setmatches()
+<
+ *setpos()*
+setpos({expr}, {list})
+ Set the position for String {expr}. Possible values:
+ . the cursor
+ 'x mark x
+
+ {list} must be a |List| with four or five numbers:
+ [bufnum, lnum, col, off]
+ [bufnum, lnum, col, off, curswant]
+
+ "bufnum" is the buffer number. Zero can be used for the
+ current buffer. When setting an uppercase mark "bufnum" is
+ used for the mark position. For other marks it specifies the
+ buffer to set the mark in. You can use the |bufnr()| function
+ to turn a file name into a buffer number.
+ For setting the cursor and the ' mark "bufnum" is ignored,
+ since these are associated with a window, not a buffer.
+ Does not change the jumplist.
+
+ "lnum" and "col" are the position in the buffer. The first
+ column is 1. Use a zero "lnum" to delete a mark. If "col" is
+ smaller than 1 then 1 is used. To use the character count
+ instead of the byte count, use |setcharpos()|.
+
+ The "off" number is only used when 'virtualedit' is set. Then
+ it is the offset in screen columns from the start of the
+ character. E.g., a position within a <Tab> or after the last
+ character.
+
+ The "curswant" number is only used when setting the cursor
+ position. It sets the preferred column for when moving the
+ cursor vertically. When the "curswant" number is missing the
+ preferred column is not set. When it is present and setting a
+ mark position it is not used.
+
+ Note that for '< and '> changing the line number may result in
+ the marks to be effectively be swapped, so that '< is always
+ before '>.
+
+ Returns 0 when the position could be set, -1 otherwise.
+ An error message is given if {expr} is invalid.
+
+ Also see |setcharpos()|, |getpos()| and |getcurpos()|.
+
+ This does not restore the preferred column for moving
+ vertically; if you set the cursor position with this, |j| and
+ |k| motions will jump to previous columns! Use |cursor()| to
+ also set the preferred column. Also see the "curswant" key in
+ |winrestview()|.
+
+ Can also be used as a |method|: >
+ GetPosition()->setpos('.')
+
+setqflist({list} [, {action} [, {what}]]) *setqflist()*
+ Create or replace or add to the quickfix list.
+
+ If the optional {what} dictionary argument is supplied, then
+ only the items listed in {what} are set. The first {list}
+ argument is ignored. See below for the supported items in
+ {what}.
+ *setqflist-what*
+ When {what} is not present, the items in {list} are used. Each
+ item must be a dictionary. Non-dictionary items in {list} are
+ ignored. Each dictionary item can contain the following
+ entries:
+
+ bufnr buffer number; must be the number of a valid
+ buffer
+ filename name of a file; only used when "bufnr" is not
+ present or it is invalid.
+ module name of a module; if given it will be used in
+ quickfix error window instead of the filename.
+ lnum line number in the file
+ end_lnum end of lines, if the item spans multiple lines
+ pattern search pattern used to locate the error
+ col column number
+ vcol when non-zero: "col" is visual column
+ when zero: "col" is byte index
+ end_col end column, if the item spans multiple columns
+ nr error number
+ text description of the error
+ type single-character error type, 'E', 'W', etc.
+ valid recognized error message
+
+ The "col", "vcol", "nr", "type" and "text" entries are
+ optional. Either "lnum" or "pattern" entry can be used to
+ locate a matching error line.
+ If the "filename" and "bufnr" entries are not present or
+ neither the "lnum" or "pattern" entries are present, then the
+ item will not be handled as an error line.
+ If both "pattern" and "lnum" are present then "pattern" will
+ be used.
+ If the "valid" entry is not supplied, then the valid flag is
+ set when "bufnr" is a valid buffer or "filename" exists.
+ If you supply an empty {list}, the quickfix list will be
+ cleared.
+ Note that the list is not exactly the same as what
+ |getqflist()| returns.
+
+ {action} values: *setqflist-action* *E927*
+ 'a' The items from {list} are added to the existing
+ quickfix list. If there is no existing list, then a
+ new list is created.
+
+ 'r' The items from the current quickfix list are replaced
+ with the items from {list}. This can also be used to
+ clear the list: >
+ :call setqflist([], 'r')
+<
+ 'f' All the quickfix lists in the quickfix stack are
+ freed.
+
+ If {action} is not present or is set to ' ', then a new list
+ is created. The new quickfix list is added after the current
+ quickfix list in the stack and all the following lists are
+ freed. To add a new quickfix list at the end of the stack,
+ set "nr" in {what} to "$".
+
+ The following items can be specified in dictionary {what}:
+ context quickfix list context. See |quickfix-context|
+ efm errorformat to use when parsing text from
+ "lines". If this is not present, then the
+ 'errorformat' option value is used.
+ See |quickfix-parse|
+ id quickfix list identifier |quickfix-ID|
+ idx index of the current entry in the quickfix
+ list specified by 'id' or 'nr'. If set to '$',
+ then the last entry in the list is set as the
+ current entry. See |quickfix-index|
+ items list of quickfix entries. Same as the {list}
+ argument.
+ lines use 'errorformat' to parse a list of lines and
+ add the resulting entries to the quickfix list
+ {nr} or {id}. Only a |List| value is supported.
+ See |quickfix-parse|
+ nr list number in the quickfix stack; zero
+ means the current quickfix list and "$" means
+ the last quickfix list.
+ quickfixtextfunc
+ function to get the text to display in the
+ quickfix window. The value can be the name of
+ a function or a funcref or a lambda. Refer to
+ |quickfix-window-function| for an explanation
+ of how to write the function and an example.
+ title quickfix list title text. See |quickfix-title|
+ Unsupported keys in {what} are ignored.
+ If the "nr" item is not present, then the current quickfix list
+ is modified. When creating a new quickfix list, "nr" can be
+ set to a value one greater than the quickfix stack size.
+ When modifying a quickfix list, to guarantee that the correct
+ list is modified, "id" should be used instead of "nr" to
+ specify the list.
+
+ Examples (See also |setqflist-examples|): >
+ :call setqflist([], 'r', {'title': 'My search'})
+ :call setqflist([], 'r', {'nr': 2, 'title': 'Errors'})
+ :call setqflist([], 'a', {'id':qfid, 'lines':["F1:10:L10"]})
+<
+ Returns zero for success, -1 for failure.
+
+ This function can be used to create a quickfix list
+ independent of the 'errorformat' setting. Use a command like
+ `:cc 1` to jump to the first position.
+
+ Can also be used as a |method|, the base is passed as the
+ second argument: >
+ GetErrorlist()->setqflist()
+<
+ *setreg()*
+setreg({regname}, {value} [, {options}])
+ Set the register {regname} to {value}.
+ The {regname} argument is a string.
+
+ {value} may be any value returned by |getreg()| or
+ |getreginfo()|, including a |List| or |Dict|.
+ If {options} contains "a" or {regname} is upper case,
+ then the value is appended.
+
+ {options} can also contain a register type specification:
+ "c" or "v" |charwise| mode
+ "l" or "V" |linewise| mode
+ "b" or "<CTRL-V>" |blockwise-visual| mode
+ If a number immediately follows "b" or "<CTRL-V>" then this is
+ used as the width of the selection - if it is not specified
+ then the width of the block is set to the number of characters
+ in the longest line (counting a <Tab> as 1 character).
+ If {options} contains "u" or '"', then the unnamed register is
+ set to point to register {regname}.
+
+ If {options} contains no register settings, then the default
+ is to use character mode unless {value} ends in a <NL> for
+ string {value} and linewise mode for list {value}. Blockwise
+ mode is never selected automatically.
+ Returns zero for success, non-zero for failure.
+
+ *E883*
+ Note: you may not use |List| containing more than one item to
+ set search and expression registers. Lists containing no
+ items act like empty strings.
+
+ Examples: >
+ :call setreg(v:register, @*)
+ :call setreg('*', @%, 'ac')
+ :call setreg('a', "1\n2\n3", 'b5')
+ :call setreg('"', { 'points_to': 'a'})
+
+< This example shows using the functions to save and restore a
+ register: >
+ :let var_a = getreginfo()
+ :call setreg('a', var_a)
+< or: >
+ :let var_a = getreg('a', 1, 1)
+ :let var_amode = getregtype('a')
+ ....
+ :call setreg('a', var_a, var_amode)
+< Note: you may not reliably restore register value
+ without using the third argument to |getreg()| as without it
+ newlines are represented as newlines AND Nul bytes are
+ represented as newlines as well, see |NL-used-for-Nul|.
+
+ You can also change the type of a register by appending
+ nothing: >
+ :call setreg('a', '', 'al')
+
+< Can also be used as a |method|, the base is passed as the
+ second argument: >
+ GetText()->setreg('a')
+
+settabvar({tabnr}, {varname}, {val}) *settabvar()*
+ Set tab-local variable {varname} to {val} in tab page {tabnr}.
+ |t:var|
+ The {varname} argument is a string.
+ Note that the variable name without "t:" must be used.
+ Tabs are numbered starting with one.
+ This function is not available in the |sandbox|.
+
+ Can also be used as a |method|, the base is passed as the
+ third argument: >
+ GetValue()->settabvar(tab, name)
+
+settabwinvar({tabnr}, {winnr}, {varname}, {val}) *settabwinvar()*
+ Set option or local variable {varname} in window {winnr} to
+ {val}.
+ Tabs are numbered starting with one. For the current tabpage
+ use |setwinvar()|.
+ {winnr} can be the window number or the |window-ID|.
+ When {winnr} is zero the current window is used.
+ This also works for a global or local buffer option, but it
+ doesn't work for a global or local buffer variable.
+ For a local buffer option the global value is unchanged.
+ Note that the variable name without "w:" must be used.
+ Examples: >
+ :call settabwinvar(1, 1, "&list", 0)
+ :call settabwinvar(3, 2, "myvar", "foobar")
+< This function is not available in the |sandbox|.
+
+ Can also be used as a |method|, the base is passed as the
+ fourth argument: >
+ GetValue()->settabwinvar(tab, winnr, name)
+
+settagstack({nr}, {dict} [, {action}]) *settagstack()*
+ Modify the tag stack of the window {nr} using {dict}.
+ {nr} can be the window number or the |window-ID|.
+
+ For a list of supported items in {dict}, refer to
+ |gettagstack()|. "curidx" takes effect before changing the tag
+ stack.
+ *E962*
+ How the tag stack is modified depends on the {action}
+ argument:
+ - If {action} is not present or is set to 'r', then the tag
+ stack is replaced.
+ - If {action} is set to 'a', then new entries from {dict} are
+ pushed (added) onto the tag stack.
+ - If {action} is set to 't', then all the entries from the
+ current entry in the tag stack or "curidx" in {dict} are
+ removed and then new entries are pushed to the stack.
+
+ The current index is set to one after the length of the tag
+ stack after the modification.
+
+ Returns zero for success, -1 for failure.
+
+ Examples (for more examples see |tagstack-examples|):
+ Empty the tag stack of window 3: >
+ call settagstack(3, {'items' : []})
+
+< Save and restore the tag stack: >
+ let stack = gettagstack(1003)
+ " do something else
+ call settagstack(1003, stack)
+ unlet stack
+<
+ Can also be used as a |method|, the base is passed as the
+ second argument: >
+ GetStack()->settagstack(winnr)
+
+setwinvar({nr}, {varname}, {val}) *setwinvar()*
+ Like |settabwinvar()| for the current tab page.
+ Examples: >
+ :call setwinvar(1, "&list", 0)
+ :call setwinvar(2, "myvar", "foobar")
+
+< Can also be used as a |method|, the base is passed as the
+ third argument: >
+ GetValue()->setwinvar(winnr, name)
+
+sha256({string}) *sha256()*
+ Returns a String with 64 hex characters, which is the SHA256
+ checksum of {string}.
+
+ Can also be used as a |method|: >
+ GetText()->sha256()
+
+shellescape({string} [, {special}]) *shellescape()*
+ Escape {string} for use as a shell command argument.
+
+ On Windows when 'shellslash' is not set, encloses {string} in
+ double-quotes and doubles all double-quotes within {string}.
+ Otherwise encloses {string} in single-quotes and replaces all
+ "'" with "'\''".
+
+ If {special} is a |non-zero-arg|:
+ - Special items such as "!", "%", "#" and "<cword>" will be
+ preceded by a backslash. The backslash will be removed again
+ by the |:!| command.
+ - The <NL> character is escaped.
+
+ If 'shell' contains "csh" in the tail:
+ - The "!" character will be escaped. This is because csh and
+ tcsh use "!" for history replacement even in single-quotes.
+ - The <NL> character is escaped (twice if {special} is
+ a |non-zero-arg|).
+
+ If 'shell' contains "fish" in the tail, the "\" character will
+ be escaped because in fish it is used as an escape character
+ inside single quotes.
+
+ Example of use with a |:!| command: >
+ :exe '!dir ' .. shellescape(expand('<cfile>'), 1)
+< This results in a directory listing for the file under the
+ cursor. Example of use with |system()|: >
+ :call system("chmod +w -- " .. shellescape(expand("%")))
+< See also |::S|.
+
+ Can also be used as a |method|: >
+ GetCommand()->shellescape()
+
+shiftwidth([{col}]) *shiftwidth()*
+ Returns the effective value of 'shiftwidth'. This is the
+ 'shiftwidth' value unless it is zero, in which case it is the
+ 'tabstop' value. To be backwards compatible in indent
+ plugins, use this: >
+ if exists('*shiftwidth')
+ func s:sw()
+ return shiftwidth()
+ endfunc
+ else
+ func s:sw()
+ return &sw
+ endfunc
+ endif
+< And then use s:sw() instead of &sw.
+
+ When there is one argument {col} this is used as column number
+ for which to return the 'shiftwidth' value. This matters for the
+ 'vartabstop' feature. If no {col} argument is given, column 1
+ will be assumed.
+
+ Can also be used as a |method|: >
+ GetColumn()->shiftwidth()
+
+sign_ functions are documented here: |sign-functions-details|
+
+simplify({filename}) *simplify()*
+ Simplify the file name as much as possible without changing
+ the meaning. Shortcuts (on MS-Windows) or symbolic links (on
+ Unix) are not resolved. If the first path component in
+ {filename} designates the current directory, this will be
+ valid for the result as well. A trailing path separator is
+ not removed either. On Unix "//path" is unchanged, but
+ "///path" is simplified to "/path" (this follows the Posix
+ standard).
+ Example: >
+ simplify("./dir/.././/file/") == "./file/"
+< Note: The combination "dir/.." is only removed if "dir" is
+ a searchable directory or does not exist. On Unix, it is also
+ removed when "dir" is a symbolic link within the same
+ directory. In order to resolve all the involved symbolic
+ links before simplifying the path name, use |resolve()|.
+
+ Can also be used as a |method|: >
+ GetName()->simplify()
+
+sin({expr}) *sin()*
+ Return the sine of {expr}, measured in radians, as a |Float|.
+ {expr} must evaluate to a |Float| or a |Number|.
+ Returns 0.0 if {expr} is not a |Float| or a |Number|.
+ Examples: >
+ :echo sin(100)
+< -0.506366 >
+ :echo sin(-4.01)
+< 0.763301
+
+ Can also be used as a |method|: >
+ Compute()->sin()
+
+sinh({expr}) *sinh()*
+ Return the hyperbolic sine of {expr} as a |Float| in the range
+ [-inf, inf].
+ {expr} must evaluate to a |Float| or a |Number|.
+ Returns 0.0 if {expr} is not a |Float| or a |Number|.
+ Examples: >
+ :echo sinh(0.5)
+< 0.521095 >
+ :echo sinh(-0.9)
+< -1.026517
+
+ Can also be used as a |method|: >
+ Compute()->sinh()
+
+sockconnect({mode}, {address} [, {opts}]) *sockconnect()*
+ Connect a socket to an address. If {mode} is "pipe" then
+ {address} should be the path of a named pipe. If {mode} is
+ "tcp" then {address} should be of the form "host:port" where
+ the host should be an ip adderess or host name, and port the
+ port number.
+
+ Returns a |channel| ID. Close the socket with |chanclose()|.
+ Use |chansend()| to send data over a bytes socket, and
+ |rpcrequest()| and |rpcnotify()| to communicate with a RPC
+ socket.
+
+ {opts} is an optional dictionary with these keys:
+ |on_data| : callback invoked when data was read from socket
+ data_buffered : read socket data in |channel-buffered| mode.
+ rpc : If set, |msgpack-rpc| will be used to communicate
+ over the socket.
+ Returns:
+ - The channel ID on success (greater than zero)
+ - 0 on invalid arguments or connection failure.
+
+sort({list} [, {func} [, {dict}]]) *sort()* *E702*
+ Sort the items in {list} in-place. Returns {list}.
+
+ If you want a list to remain unmodified make a copy first: >
+ :let sortedlist = sort(copy(mylist))
+
+< When {func} is omitted, is empty or zero, then sort() uses the
+ string representation of each item to sort on. Numbers sort
+ after Strings, |Lists| after Numbers. For sorting text in the
+ current buffer use |:sort|.
+
+ When {func} is given and it is '1' or 'i' then case is
+ ignored.
+
+ When {func} is given and it is 'l' then the current collation
+ locale is used for ordering. Implementation details: strcoll()
+ is used to compare strings. See |:language| check or set the
+ collation locale. |v:collate| can also be used to check the
+ current locale. Sorting using the locale typically ignores
+ case. Example: >
+ " ö is sorted similarly to o with English locale.
+ :language collate en_US.UTF8
+ :echo sort(['n', 'o', 'O', 'ö', 'p', 'z'], 'l')
+< ['n', 'o', 'O', 'ö', 'p', 'z'] ~
+>
+ " ö is sorted after z with Swedish locale.
+ :language collate sv_SE.UTF8
+ :echo sort(['n', 'o', 'O', 'ö', 'p', 'z'], 'l')
+< ['n', 'o', 'O', 'p', 'z', 'ö'] ~
+ This does not work properly on Mac.
+
+ When {func} is given and it is 'n' then all items will be
+ sorted numerical (Implementation detail: this uses the
+ strtod() function to parse numbers, Strings, Lists, Dicts and
+ Funcrefs will be considered as being 0).
+
+ When {func} is given and it is 'N' then all items will be
+ sorted numerical. This is like 'n' but a string containing
+ digits will be used as the number they represent.
+
+ When {func} is given and it is 'f' then all items will be
+ sorted numerical. All values must be a Number or a Float.
+
+ When {func} is a |Funcref| or a function name, this function
+ is called to compare items. The function is invoked with two
+ items as argument and must return zero if they are equal, 1 or
+ bigger if the first one sorts after the second one, -1 or
+ smaller if the first one sorts before the second one.
+
+ {dict} is for functions with the "dict" attribute. It will be
+ used to set the local variable "self". |Dictionary-function|
+
+ The sort is stable, items which compare equal (as number or as
+ string) will keep their relative position. E.g., when sorting
+ on numbers, text strings will sort next to each other, in the
+ same order as they were originally.
+
+ Can also be used as a |method|: >
+ mylist->sort()
+
+< Also see |uniq()|.
+
+ Example: >
+ func MyCompare(i1, i2)
+ return a:i1 == a:i2 ? 0 : a:i1 > a:i2 ? 1 : -1
+ endfunc
+ eval mylist->sort("MyCompare")
+< A shorter compare version for this specific simple case, which
+ ignores overflow: >
+ func MyCompare(i1, i2)
+ return a:i1 - a:i2
+ endfunc
+< For a simple expression you can use a lambda: >
+ eval mylist->sort({i1, i2 -> i1 - i2})
+<
+ *soundfold()*
+soundfold({word})
+ Return the sound-folded equivalent of {word}. Uses the first
+ language in 'spelllang' for the current window that supports
+ soundfolding. 'spell' must be set. When no sound folding is
+ possible the {word} is returned unmodified.
+ This can be used for making spelling suggestions. Note that
+ the method can be quite slow.
+
+ Can also be used as a |method|: >
+ GetWord()->soundfold()
+<
+ *spellbadword()*
+spellbadword([{sentence}])
+ Without argument: The result is the badly spelled word under
+ or after the cursor. The cursor is moved to the start of the
+ bad word. When no bad word is found in the cursor line the
+ result is an empty string and the cursor doesn't move.
+
+ With argument: The result is the first word in {sentence} that
+ is badly spelled. If there are no spelling mistakes the
+ result is an empty string.
+
+ The return value is a list with two items:
+ - The badly spelled word or an empty string.
+ - The type of the spelling error:
+ "bad" spelling mistake
+ "rare" rare word
+ "local" word only valid in another region
+ "caps" word should start with Capital
+ Example: >
+ echo spellbadword("the quik brown fox")
+< ['quik', 'bad'] ~
+
+ The spelling information for the current window and the value
+ of 'spelllang' are used.
+
+ Can also be used as a |method|: >
+ GetText()->spellbadword()
+<
+ *spellsuggest()*
+spellsuggest({word} [, {max} [, {capital}]])
+ Return a |List| with spelling suggestions to replace {word}.
+ When {max} is given up to this number of suggestions are
+ returned. Otherwise up to 25 suggestions are returned.
+
+ When the {capital} argument is given and it's non-zero only
+ suggestions with a leading capital will be given. Use this
+ after a match with 'spellcapcheck'.
+
+ {word} can be a badly spelled word followed by other text.
+ This allows for joining two words that were split. The
+ suggestions also include the following text, thus you can
+ replace a line.
+
+ {word} may also be a good word. Similar words will then be
+ returned. {word} itself is not included in the suggestions,
+ although it may appear capitalized.
+
+ The spelling information for the current window is used. The
+ values of 'spelllang' and 'spellsuggest' are used.
+
+ Can also be used as a |method|: >
+ GetWord()->spellsuggest()
+
+split({string} [, {pattern} [, {keepempty}]]) *split()*
+ Make a |List| out of {string}. When {pattern} is omitted or
+ empty each white-separated sequence of characters becomes an
+ item.
+ Otherwise the string is split where {pattern} matches,
+ removing the matched characters. 'ignorecase' is not used
+ here, add \c to ignore case. |/\c|
+ When the first or last item is empty it is omitted, unless the
+ {keepempty} argument is given and it's non-zero.
+ Other empty items are kept when {pattern} matches at least one
+ character or when {keepempty} is non-zero.
+ Example: >
+ :let words = split(getline('.'), '\W\+')
+< To split a string in individual characters: >
+ :for c in split(mystring, '\zs')
+< If you want to keep the separator you can also use '\zs' at
+ the end of the pattern: >
+ :echo split('abc:def:ghi', ':\zs')
+< ['abc:', 'def:', 'ghi'] ~
+ Splitting a table where the first element can be empty: >
+ :let items = split(line, ':', 1)
+< The opposite function is |join()|.
+
+ Can also be used as a |method|: >
+ GetString()->split()
+
+sqrt({expr}) *sqrt()*
+ Return the non-negative square root of Float {expr} as a
+ |Float|.
+ {expr} must evaluate to a |Float| or a |Number|. When {expr}
+ is negative the result is NaN (Not a Number). Returns 0.0 if
+ {expr} is not a |Float| or a |Number|.
+ Examples: >
+ :echo sqrt(100)
+< 10.0 >
+ :echo sqrt(-4.01)
+< str2float('nan')
+ NaN may be different, it depends on system libraries.
+
+ Can also be used as a |method|: >
+ Compute()->sqrt()
+
+srand([{expr}]) *srand()*
+ Initialize seed used by |rand()|:
+ - If {expr} is not given, seed values are initialized by
+ reading from /dev/urandom, if possible, or using time(NULL)
+ a.k.a. epoch time otherwise; this only has second accuracy.
+ - If {expr} is given it must be a Number. It is used to
+ initialize the seed values. This is useful for testing or
+ when a predictable sequence is intended.
+
+ Examples: >
+ :let seed = srand()
+ :let seed = srand(userinput)
+ :echo rand(seed)
+<
+ Can also be used as a |method|: >
+ userinput->srand()
+
+stdioopen({opts}) *stdioopen()*
+ With |--headless| this opens stdin and stdout as a |channel|.
+ May be called only once. See |channel-stdio|. stderr is not
+ handled by this function, see |v:stderr|.
+
+ Close the stdio handles with |chanclose()|. Use |chansend()|
+ to send data to stdout, and |rpcrequest()| and |rpcnotify()|
+ to communicate over RPC.
+
+ {opts} is a dictionary with these keys:
+ |on_stdin| : callback invoked when stdin is written to.
+ on_print : callback invoked when Nvim needs to print a
+ message, with the message (whose type is string)
+ as sole argument.
+ stdin_buffered : read stdin in |channel-buffered| mode.
+ rpc : If set, |msgpack-rpc| will be used to communicate
+ over stdio
+ Returns:
+ - |channel-id| on success (value is always 1)
+ - 0 on invalid arguments
+
+
+stdpath({what}) *stdpath()* *E6100*
+ Returns |standard-path| locations of various default files and
+ directories.
+
+ {what} Type Description ~
+ cache String Cache directory: arbitrary temporary
+ storage for plugins, etc.
+ config String User configuration directory. |init.vim|
+ is stored here.
+ config_dirs List Other configuration directories.
+ data String User data directory. The |shada-file|
+ is stored here.
+ data_dirs List Other data directories.
+ log String Logs directory (for use by plugins too).
+ run String Run directory: temporary, local storage
+ for sockets, named pipes, etc.
+ state String Session state directory: storage for file
+ drafts, undo, shada, etc.
+
+ Example: >
+ :echo stdpath("config")
+
+
+str2float({string} [, {quoted}]) *str2float()*
+ Convert String {string} to a Float. This mostly works the
+ same as when using a floating point number in an expression,
+ see |floating-point-format|. But it's a bit more permissive.
+ E.g., "1e40" is accepted, while in an expression you need to
+ write "1.0e40". The hexadecimal form "0x123" is also
+ accepted, but not others, like binary or octal.
+ When {quoted} is present and non-zero then embedded single
+ quotes before the dot are ignored, thus "1'000.0" is a
+ thousand.
+ Text after the number is silently ignored.
+ The decimal point is always '.', no matter what the locale is
+ set to. A comma ends the number: "12,345.67" is converted to
+ 12.0. You can strip out thousands separators with
+ |substitute()|: >
+ let f = str2float(substitute(text, ',', '', 'g'))
+<
+ Returns 0.0 if the conversion fails.
+
+ Can also be used as a |method|: >
+ let f = text->substitute(',', '', 'g')->str2float()
+
+str2list({string} [, {utf8}]) *str2list()*
+ Return a list containing the number values which represent
+ each character in String {string}. Examples: >
+ str2list(" ") returns [32]
+ str2list("ABC") returns [65, 66, 67]
+< |list2str()| does the opposite.
+
+ UTF-8 encoding is always used, {utf8} option has no effect,
+ and exists only for backwards-compatibility.
+ With UTF-8 composing characters are handled properly: >
+ str2list("á") returns [97, 769]
+
+< Can also be used as a |method|: >
+ GetString()->str2list()
+
+str2nr({string} [, {base}]) *str2nr()*
+ Convert string {string} to a number.
+ {base} is the conversion base, it can be 2, 8, 10 or 16.
+ When {quoted} is present and non-zero then embedded single
+ quotes are ignored, thus "1'000'000" is a million.
+
+ When {base} is omitted base 10 is used. This also means that
+ a leading zero doesn't cause octal conversion to be used, as
+ with the default String to Number conversion. Example: >
+ let nr = str2nr('0123')
+<
+ When {base} is 16 a leading "0x" or "0X" is ignored. With a
+ different base the result will be zero. Similarly, when
+ {base} is 8 a leading "0", "0o" or "0O" is ignored, and when
+ {base} is 2 a leading "0b" or "0B" is ignored.
+ Text after the number is silently ignored.
+
+ Returns 0 if {string} is empty or on error.
+
+ Can also be used as a |method|: >
+ GetText()->str2nr()
+
+strcharpart({src}, {start} [, {len}]) *strcharpart()*
+ Like |strpart()| but using character index and length instead
+ of byte index and length. Composing characters are counted
+ separately.
+ When a character index is used where a character does not
+ exist it is assumed to be one character. For example: >
+ strcharpart('abc', -1, 2)
+< results in 'a'.
+
+ Returns an empty string on error.
+
+ Can also be used as a |method|: >
+ GetText()->strcharpart(5)
+
+strchars({string} [, {skipcc}]) *strchars()*
+ The result is a Number, which is the number of characters
+ in String {string}.
+ When {skipcc} is omitted or zero, composing characters are
+ counted separately.
+ When {skipcc} set to 1, Composing characters are ignored.
+
+ Returns zero on error.
+
+ Also see |strlen()|, |strdisplaywidth()| and |strwidth()|.
+
+ {skipcc} is only available after 7.4.755. For backward
+ compatibility, you can define a wrapper function: >
+ if has("patch-7.4.755")
+ function s:strchars(str, skipcc)
+ return strchars(a:str, a:skipcc)
+ endfunction
+ else
+ function s:strchars(str, skipcc)
+ if a:skipcc
+ return strlen(substitute(a:str, ".", "x", "g"))
+ else
+ return strchars(a:str)
+ endif
+ endfunction
+ endif
+<
+ Can also be used as a |method|: >
+ GetText()->strchars()
+
+strdisplaywidth({string} [, {col}]) *strdisplaywidth()*
+ The result is a Number, which is the number of display cells
+ String {string} occupies on the screen when it starts at {col}
+ (first column is zero). When {col} is omitted zero is used.
+ Otherwise it is the screen column where to start. This
+ matters for Tab characters.
+ The option settings of the current window are used. This
+ matters for anything that's displayed differently, such as
+ 'tabstop' and 'display'.
+ When {string} contains characters with East Asian Width Class
+ Ambiguous, this function's return value depends on 'ambiwidth'.
+ Returns zero on error.
+ Also see |strlen()|, |strwidth()| and |strchars()|.
+
+ Can also be used as a |method|: >
+ GetText()->strdisplaywidth()
+
+strftime({format} [, {time}]) *strftime()*
+ The result is a String, which is a formatted date and time, as
+ specified by the {format} string. The given {time} is used,
+ or the current time if no time is given. The accepted
+ {format} depends on your system, thus this is not portable!
+ See the manual page of the C function strftime() for the
+ format. The maximum length of the result is 80 characters.
+ See also |localtime()|, |getftime()| and |strptime()|.
+ The language can be changed with the |:language| command.
+ Examples: >
+ :echo strftime("%c") Sun Apr 27 11:49:23 1997
+ :echo strftime("%Y %b %d %X") 1997 Apr 27 11:53:25
+ :echo strftime("%y%m%d %T") 970427 11:53:55
+ :echo strftime("%H:%M") 11:55
+ :echo strftime("%c", getftime("file.c"))
+ Show mod time of file.c.
+
+< Can also be used as a |method|: >
+ GetFormat()->strftime()
+
+strgetchar({str}, {index}) *strgetchar()*
+ Get a Number corresponding to the character at {index} in
+ {str}. This uses a zero-based character index, not a byte
+ index. Composing characters are considered separate
+ characters here. Use |nr2char()| to convert the Number to a
+ String.
+ Returns -1 if {index} is invalid.
+ Also see |strcharpart()| and |strchars()|.
+
+ Can also be used as a |method|: >
+ GetText()->strgetchar(5)
+
+stridx({haystack}, {needle} [, {start}]) *stridx()*
+ The result is a Number, which gives the byte index in
+ {haystack} of the first occurrence of the String {needle}.
+ If {start} is specified, the search starts at index {start}.
+ This can be used to find a second match: >
+ :let colon1 = stridx(line, ":")
+ :let colon2 = stridx(line, ":", colon1 + 1)
+< The search is done case-sensitive.
+ For pattern searches use |match()|.
+ -1 is returned if the {needle} does not occur in {haystack}.
+ See also |strridx()|.
+ Examples: >
+ :echo stridx("An Example", "Example") 3
+ :echo stridx("Starting point", "Start") 0
+ :echo stridx("Starting point", "start") -1
+< *strstr()* *strchr()*
+ stridx() works similar to the C function strstr(). When used
+ with a single character it works similar to strchr().
+
+ Can also be used as a |method|: >
+ GetHaystack()->stridx(needle)
+
+ *string()*
+string({expr}) Return {expr} converted to a String. If {expr} is a Number,
+ Float, String, Blob or a composition of them, then the result
+ can be parsed back with |eval()|.
+ {expr} type result ~
+ String 'string'
+ Number 123
+ Float 123.123456 or 1.123456e8 or
+ `str2float('inf')`
+ Funcref `function('name')`
+ Blob 0z00112233.44556677.8899
+ List [item, item]
+ Dictionary {key: value, key: value}
+ Note that in String values the ' character is doubled.
+ Also see |strtrans()|.
+ Note 2: Output format is mostly compatible with YAML, except
+ for infinite and NaN floating-point values representations
+ which use |str2float()|. Strings are also dumped literally,
+ only single quote is escaped, which does not allow using YAML
+ for parsing back binary strings. |eval()| should always work for
+ strings and floats though and this is the only official
+ method, use |msgpackdump()| or |json_encode()| if you need to
+ share data with other application.
+
+ Can also be used as a |method|: >
+ mylist->string()
+
+strlen({string}) *strlen()*
+ The result is a Number, which is the length of the String
+ {string} in bytes.
+ If the argument is a Number it is first converted to a String.
+ For other types an error is given and zero is returned.
+ If you want to count the number of multibyte characters use
+ |strchars()|.
+ Also see |len()|, |strdisplaywidth()| and |strwidth()|.
+
+ Can also be used as a |method|: >
+ GetString()->strlen()
+
+strpart({src}, {start} [, {len} [, {chars}]]) *strpart()*
+ The result is a String, which is part of {src}, starting from
+ byte {start}, with the byte length {len}.
+ When {chars} is present and TRUE then {len} is the number of
+ characters positions (composing characters are not counted
+ separately, thus "1" means one base character and any
+ following composing characters).
+ To count {start} as characters instead of bytes use
+ |strcharpart()|.
+
+ When bytes are selected which do not exist, this doesn't
+ result in an error, the bytes are simply omitted.
+ If {len} is missing, the copy continues from {start} till the
+ end of the {src}. >
+ strpart("abcdefg", 3, 2) == "de"
+ strpart("abcdefg", -2, 4) == "ab"
+ strpart("abcdefg", 5, 4) == "fg"
+ strpart("abcdefg", 3) == "defg"
+
+< Note: To get the first character, {start} must be 0. For
+ example, to get the character under the cursor: >
+ strpart(getline("."), col(".") - 1, 1, v:true)
+<
+ Returns an empty string on error.
+
+ Can also be used as a |method|: >
+ GetText()->strpart(5)
+
+strptime({format}, {timestring}) *strptime()*
+ The result is a Number, which is a unix timestamp representing
+ the date and time in {timestring}, which is expected to match
+ the format specified in {format}.
+
+ The accepted {format} depends on your system, thus this is not
+ portable! See the manual page of the C function strptime()
+ for the format. Especially avoid "%c". The value of $TZ also
+ matters.
+
+ If the {timestring} cannot be parsed with {format} zero is
+ returned. If you do not know the format of {timestring} you
+ can try different {format} values until you get a non-zero
+ result.
+
+ See also |strftime()|.
+ Examples: >
+ :echo strptime("%Y %b %d %X", "1997 Apr 27 11:49:23")
+< 862156163 >
+ :echo strftime("%c", strptime("%y%m%d %T", "970427 11:53:55"))
+< Sun Apr 27 11:53:55 1997 >
+ :echo strftime("%c", strptime("%Y%m%d%H%M%S", "19970427115355") + 3600)
+< Sun Apr 27 12:53:55 1997
+
+ Can also be used as a |method|: >
+ GetFormat()->strptime(timestring)
+<
+strridx({haystack}, {needle} [, {start}]) *strridx()*
+ The result is a Number, which gives the byte index in
+ {haystack} of the last occurrence of the String {needle}.
+ When {start} is specified, matches beyond this index are
+ ignored. This can be used to find a match before a previous
+ match: >
+ :let lastcomma = strridx(line, ",")
+ :let comma2 = strridx(line, ",", lastcomma - 1)
+< The search is done case-sensitive.
+ For pattern searches use |match()|.
+ -1 is returned if the {needle} does not occur in {haystack}.
+ If the {needle} is empty the length of {haystack} is returned.
+ See also |stridx()|. Examples: >
+ :echo strridx("an angry armadillo", "an") 3
+< *strrchr()*
+ When used with a single character it works similar to the C
+ function strrchr().
+
+ Can also be used as a |method|: >
+ GetHaystack()->strridx(needle)
+
+strtrans({string}) *strtrans()*
+ The result is a String, which is {string} with all unprintable
+ characters translated into printable characters |'isprint'|.
+ Like they are shown in a window. Example: >
+ echo strtrans(@a)
+< This displays a newline in register a as "^@" instead of
+ starting a new line.
+
+ Returns an empty string on error.
+
+ Can also be used as a |method|: >
+ GetString()->strtrans()
+
+strwidth({string}) *strwidth()*
+ The result is a Number, which is the number of display cells
+ String {string} occupies. A Tab character is counted as one
+ cell, alternatively use |strdisplaywidth()|.
+ When {string} contains characters with East Asian Width Class
+ Ambiguous, this function's return value depends on 'ambiwidth'.
+ Returns zero on error.
+ Also see |strlen()|, |strdisplaywidth()| and |strchars()|.
+
+ Can also be used as a |method|: >
+ GetString()->strwidth()
+
+submatch({nr} [, {list}]) *submatch()* *E935*
+ Only for an expression in a |:substitute| command or
+ substitute() function.
+ Returns the {nr}'th submatch of the matched text. When {nr}
+ is 0 the whole matched text is returned.
+ Note that a NL in the string can stand for a line break of a
+ multi-line match or a NUL character in the text.
+ Also see |sub-replace-expression|.
+
+ If {list} is present and non-zero then submatch() returns
+ a list of strings, similar to |getline()| with two arguments.
+ NL characters in the text represent NUL characters in the
+ text.
+ Only returns more than one item for |:substitute|, inside
+ |substitute()| this list will always contain one or zero
+ items, since there are no real line breaks.
+
+ When substitute() is used recursively only the submatches in
+ the current (deepest) call can be obtained.
+
+ Returns an empty string or list on error.
+
+ Examples: >
+ :s/\d\+/\=submatch(0) + 1/
+ :echo substitute(text, '\d\+', '\=submatch(0) + 1', '')
+< This finds the first number in the line and adds one to it.
+ A line break is included as a newline character.
+
+ Can also be used as a |method|: >
+ GetNr()->submatch()
+
+substitute({string}, {pat}, {sub}, {flags}) *substitute()*
+ The result is a String, which is a copy of {string}, in which
+ the first match of {pat} is replaced with {sub}.
+ When {flags} is "g", all matches of {pat} in {string} are
+ replaced. Otherwise {flags} should be "".
+
+ This works like the ":substitute" command (without any flags).
+ But the matching with {pat} is always done like the 'magic'
+ option is set and 'cpoptions' is empty (to make scripts
+ portable). 'ignorecase' is still relevant, use |/\c| or |/\C|
+ if you want to ignore or match case and ignore 'ignorecase'.
+ 'smartcase' is not used. See |string-match| for how {pat} is
+ used.
+
+ A "~" in {sub} is not replaced with the previous {sub}.
+ Note that some codes in {sub} have a special meaning
+ |sub-replace-special|. For example, to replace something with
+ "\n" (two characters), use "\\\\n" or '\\n'.
+
+ When {pat} does not match in {string}, {string} is returned
+ unmodified.
+
+ Example: >
+ :let &path = substitute(&path, ",\\=[^,]*$", "", "")
+< This removes the last component of the 'path' option. >
+ :echo substitute("testing", ".*", "\\U\\0", "")
+< results in "TESTING".
+
+ When {sub} starts with "\=", the remainder is interpreted as
+ an expression. See |sub-replace-expression|. Example: >
+ :echo substitute(s, '%\(\x\x\)',
+ \ '\=nr2char("0x" .. submatch(1))', 'g')
+
+< When {sub} is a Funcref that function is called, with one
+ optional argument. Example: >
+ :echo substitute(s, '%\(\x\x\)', SubNr, 'g')
+< The optional argument is a list which contains the whole
+ matched string and up to nine submatches, like what
+ |submatch()| returns. Example: >
+ :echo substitute(s, '%\(\x\x\)', {m -> '0x' .. m[1]}, 'g')
+
+< Returns an empty string on error.
+
+ Can also be used as a |method|: >
+ GetString()->substitute(pat, sub, flags)
+
+swapinfo({fname}) *swapinfo()*
+ The result is a dictionary, which holds information about the
+ swapfile {fname}. The available fields are:
+ version Vim version
+ user user name
+ host host name
+ fname original file name
+ pid PID of the Vim process that created the swap
+ file
+ mtime last modification time in seconds
+ inode Optional: INODE number of the file
+ dirty 1 if file was modified, 0 if not
+ In case of failure an "error" item is added with the reason:
+ Cannot open file: file not found or in accessible
+ Cannot read file: cannot read first block
+ Not a swap file: does not contain correct block ID
+ Magic number mismatch: Info in first block is invalid
+
+ Can also be used as a |method|: >
+ GetFilename()->swapinfo()
+
+swapname({buf}) *swapname()*
+ The result is the swap file path of the buffer {buf}.
+ For the use of {buf}, see |bufname()| above.
+ If buffer {buf} is the current buffer, the result is equal to
+ |:swapname| (unless there is no swap file).
+ If buffer {buf} has no swap file, returns an empty string.
+
+ Can also be used as a |method|: >
+ GetBufname()->swapname()
+
+synID({lnum}, {col}, {trans}) *synID()*
+ The result is a Number, which is the syntax ID at the position
+ {lnum} and {col} in the current window.
+ The syntax ID can be used with |synIDattr()| and
+ |synIDtrans()| to obtain syntax information about text.
+
+ {col} is 1 for the leftmost column, {lnum} is 1 for the first
+ line. 'synmaxcol' applies, in a longer line zero is returned.
+ Note that when the position is after the last character,
+ that's where the cursor can be in Insert mode, synID() returns
+ zero. {lnum} is used like with |getline()|.
+
+ When {trans} is |TRUE|, transparent items are reduced to the
+ item that they reveal. This is useful when wanting to know
+ the effective color. When {trans} is |FALSE|, the transparent
+ item is returned. This is useful when wanting to know which
+ syntax item is effective (e.g. inside parens).
+ Warning: This function can be very slow. Best speed is
+ obtained by going through the file in forward direction.
+
+ Returns zero on error.
+
+ Example (echoes the name of the syntax item under the cursor): >
+ :echo synIDattr(synID(line("."), col("."), 1), "name")
+<
+
+synIDattr({synID}, {what} [, {mode}]) *synIDattr()*
+ The result is a String, which is the {what} attribute of
+ syntax ID {synID}. This can be used to obtain information
+ about a syntax item.
+ {mode} can be "gui", "cterm" or "term", to get the attributes
+ for that mode. When {mode} is omitted, or an invalid value is
+ used, the attributes for the currently active highlighting are
+ used (GUI, cterm or term).
+ Use synIDtrans() to follow linked highlight groups.
+ {what} result
+ "name" the name of the syntax item
+ "fg" foreground color (GUI: color name used to set
+ the color, cterm: color number as a string,
+ term: empty string)
+ "bg" background color (as with "fg")
+ "font" font name (only available in the GUI)
+ |highlight-font|
+ "sp" special color (as with "fg") |highlight-guisp|
+ "fg#" like "fg", but for the GUI and the GUI is
+ running the name in "#RRGGBB" form
+ "bg#" like "fg#" for "bg"
+ "sp#" like "fg#" for "sp"
+ "bold" "1" if bold
+ "italic" "1" if italic
+ "reverse" "1" if reverse
+ "inverse" "1" if inverse (= reverse)
+ "standout" "1" if standout
+ "underline" "1" if underlined
+ "undercurl" "1" if undercurled
+ "underdouble" "1" if double underlined
+ "underdotted" "1" if dotted underlined
+ "underdashed" "1" if dashed underlined
+ "strikethrough" "1" if struckthrough
+
+ Example (echoes the color of the syntax item under the
+ cursor): >
+ :echo synIDattr(synIDtrans(synID(line("."), col("."), 1)), "fg")
+<
+ Returns an empty string on error.
+
+ Can also be used as a |method|: >
+ :echo synID(line("."), col("."), 1)->synIDtrans()->synIDattr("fg")
+
+synIDtrans({synID}) *synIDtrans()*
+ The result is a Number, which is the translated syntax ID of
+ {synID}. This is the syntax group ID of what is being used to
+ highlight the character. Highlight links given with
+ ":highlight link" are followed.
+
+ Returns zero on error.
+
+ Can also be used as a |method|: >
+ :echo synID(line("."), col("."), 1)->synIDtrans()->synIDattr("fg")
+
+synconcealed({lnum}, {col}) *synconcealed()*
+ The result is a |List| with currently three items:
+ 1. The first item in the list is 0 if the character at the
+ position {lnum} and {col} is not part of a concealable
+ region, 1 if it is. {lnum} is used like with |getline()|.
+ 2. The second item in the list is a string. If the first item
+ is 1, the second item contains the text which will be
+ displayed in place of the concealed text, depending on the
+ current setting of 'conceallevel' and 'listchars'.
+ 3. The third and final item in the list is a number
+ representing the specific syntax region matched in the
+ line. When the character is not concealed the value is
+ zero. This allows detection of the beginning of a new
+ concealable region if there are two consecutive regions
+ with the same replacement character. For an example, if
+ the text is "123456" and both "23" and "45" are concealed
+ and replaced by the character "X", then:
+ call returns ~
+ synconcealed(lnum, 1) [0, '', 0]
+ synconcealed(lnum, 2) [1, 'X', 1]
+ synconcealed(lnum, 3) [1, 'X', 1]
+ synconcealed(lnum, 4) [1, 'X', 2]
+ synconcealed(lnum, 5) [1, 'X', 2]
+ synconcealed(lnum, 6) [0, '', 0]
+
+
+synstack({lnum}, {col}) *synstack()*
+ Return a |List|, which is the stack of syntax items at the
+ position {lnum} and {col} in the current window. {lnum} is
+ used like with |getline()|. Each item in the List is an ID
+ like what |synID()| returns.
+ The first item in the List is the outer region, following are
+ items contained in that one. The last one is what |synID()|
+ returns, unless not the whole item is highlighted or it is a
+ transparent item.
+ This function is useful for debugging a syntax file.
+ Example that shows the syntax stack under the cursor: >
+ for id in synstack(line("."), col("."))
+ echo synIDattr(id, "name")
+ endfor
+< When the position specified with {lnum} and {col} is invalid
+ an empty list is returned. The position just after the last
+ character in a line and the first column in an empty line are
+ valid positions.
+
+system({cmd} [, {input}]) *system()* *E677*
+ Gets the output of {cmd} as a |string| (|systemlist()| returns
+ a |List|) and sets |v:shell_error| to the error code.
+ {cmd} is treated as in |jobstart()|:
+ If {cmd} is a List it runs directly (no 'shell').
+ If {cmd} is a String it runs in the 'shell', like this: >
+ :call jobstart(split(&shell) + split(&shellcmdflag) + ['{cmd}'])
+
+< Not to be used for interactive commands.
+
+ Result is a String, filtered to avoid platform-specific quirks:
+ - <CR><NL> is replaced with <NL>
+ - NUL characters are replaced with SOH (0x01)
+
+ Example: >
+ :echo system(['ls', expand('%:h')])
+
+< If {input} is a string it is written to a pipe and passed as
+ stdin to the command. The string is written as-is, line
+ separators are not changed.
+ If {input} is a |List| it is written to the pipe as
+ |writefile()| does with {binary} set to "b" (i.e. with
+ a newline between each list item, and newlines inside list
+ items converted to NULs).
+ When {input} is given and is a valid buffer id, the content of
+ the buffer is written to the file line by line, each line
+ terminated by NL (and NUL where the text has NL).
+ *E5677*
+ Note: system() cannot write to or read from backgrounded ("&")
+ shell commands, e.g.: >
+ :echo system("cat - &", "foo")
+< which is equivalent to: >
+ $ echo foo | bash -c 'cat - &'
+< The pipes are disconnected (unless overridden by shell
+ redirection syntax) before input can reach it. Use
+ |jobstart()| instead.
+
+ Note: Use |shellescape()| or |::S| with |expand()| or
+ |fnamemodify()| to escape special characters in a command
+ argument. 'shellquote' and 'shellxquote' must be properly
+ configured. Example: >
+ :echo system('ls '..shellescape(expand('%:h')))
+ :echo system('ls '..expand('%:h:S'))
+
+< Unlike ":!cmd" there is no automatic check for changed files.
+ Use |:checktime| to force a check.
+
+ Can also be used as a |method|: >
+ :echo GetCmd()->system()
+
+systemlist({cmd} [, {input} [, {keepempty}]]) *systemlist()*
+ Same as |system()|, but returns a |List| with lines (parts of
+ output separated by NL) with NULs transformed into NLs. Output
+ is the same as |readfile()| will output with {binary} argument
+ set to "b", except that a final newline is not preserved,
+ unless {keepempty} is non-zero.
+ Note that on MS-Windows you may get trailing CR characters.
+
+ To see the difference between "echo hello" and "echo -n hello"
+ use |system()| and |split()|: >
+ echo split(system('echo hello'), '\n', 1)
+<
+ Returns an empty string on error.
+
+ Can also be used as a |method|: >
+ :echo GetCmd()->systemlist()
+
+tabpagebuflist([{arg}]) *tabpagebuflist()*
+ The result is a |List|, where each item is the number of the
+ buffer associated with each window in the current tab page.
+ {arg} specifies the number of the tab page to be used. When
+ omitted the current tab page is used.
+ When {arg} is invalid the number zero is returned.
+ To get a list of all buffers in all tabs use this: >
+ let buflist = []
+ for i in range(tabpagenr('$'))
+ call extend(buflist, tabpagebuflist(i + 1))
+ endfor
+< Note that a buffer may appear in more than one window.
+
+ Can also be used as a |method|: >
+ GetTabpage()->tabpagebuflist()
+
+tabpagenr([{arg}]) *tabpagenr()*
+ The result is a Number, which is the number of the current
+ tab page. The first tab page has number 1.
+
+ The optional argument {arg} supports the following values:
+ $ the number of the last tab page (the tab page
+ count).
+ # the number of the last accessed tab page
+ (where |g<Tab>| goes to). If there is no
+ previous tab page, 0 is returned.
+ The number can be used with the |:tab| command.
+
+ Returns zero on error.
+
+tabpagewinnr({tabarg} [, {arg}]) *tabpagewinnr()*
+ Like |winnr()| but for tab page {tabarg}.
+ {tabarg} specifies the number of tab page to be used.
+ {arg} is used like with |winnr()|:
+ - When omitted the current window number is returned. This is
+ the window which will be used when going to this tab page.
+ - When "$" the number of windows is returned.
+ - When "#" the previous window nr is returned.
+ Useful examples: >
+ tabpagewinnr(1) " current window of tab page 1
+ tabpagewinnr(4, '$') " number of windows in tab page 4
+< When {tabarg} is invalid zero is returned.
+
+ Can also be used as a |method|: >
+ GetTabpage()->tabpagewinnr()
+<
+ *tagfiles()*
+tagfiles() Returns a |List| with the file names used to search for tags
+ for the current buffer. This is the 'tags' option expanded.
+
+
+taglist({expr} [, {filename}]) *taglist()*
+ Returns a |List| of tags matching the regular expression {expr}.
+
+ If {filename} is passed it is used to prioritize the results
+ in the same way that |:tselect| does. See |tag-priority|.
+ {filename} should be the full path of the file.
+
+ Each list item is a dictionary with at least the following
+ entries:
+ name Name of the tag.
+ filename Name of the file where the tag is
+ defined. It is either relative to the
+ current directory or a full path.
+ cmd Ex command used to locate the tag in
+ the file.
+ kind Type of the tag. The value for this
+ entry depends on the language specific
+ kind values. Only available when
+ using a tags file generated by
+ Universal/Exuberant ctags or hdrtag.
+ static A file specific tag. Refer to
+ |static-tag| for more information.
+ More entries may be present, depending on the content of the
+ tags file: access, implementation, inherits and signature.
+ Refer to the ctags documentation for information about these
+ fields. For C code the fields "struct", "class" and "enum"
+ may appear, they give the name of the entity the tag is
+ contained in.
+
+ The ex-command "cmd" can be either an ex search pattern, a
+ line number or a line number followed by a byte number.
+
+ If there are no matching tags, then an empty list is returned.
+
+ To get an exact tag match, the anchors '^' and '$' should be
+ used in {expr}. This also make the function work faster.
+ Refer to |tag-regexp| for more information about the tag
+ search regular expression pattern.
+
+ Refer to |'tags'| for information about how the tags file is
+ located by Vim. Refer to |tags-file-format| for the format of
+ the tags file generated by the different ctags tools.
+
+ Can also be used as a |method|: >
+ GetTagpattern()->taglist()
+
+tempname() *tempname()* *temp-file-name*
+ The result is a String, which is the name of a file that
+ doesn't exist. It can be used for a temporary file. Example: >
+ :let tmpfile = tempname()
+ :exe "redir > " .. tmpfile
+< For Unix, the file will be in a private directory |tempfile|.
+ For MS-Windows forward slashes are used when the 'shellslash'
+ option is set or when 'shellcmdflag' starts with '-'.
+
+termopen({cmd} [, {opts}]) *termopen()*
+ Spawns {cmd} in a new pseudo-terminal session connected
+ to the current (unmodified) buffer. Parameters and behavior
+ are the same as |jobstart()| except "pty", "width", "height",
+ and "TERM" are ignored: "height" and "width" are taken from
+ the current window.
+ Returns the same values as |jobstart()|.
+
+ Terminal environment is initialized as in ||jobstart-env|,
+ except $TERM is set to "xterm-256color". Full behavior is
+ described in |terminal|.
+
+tan({expr}) *tan()*
+ Return the tangent of {expr}, measured in radians, as a |Float|
+ in the range [-inf, inf].
+ {expr} must evaluate to a |Float| or a |Number|.
+ Returns 0.0 if {expr} is not a |Float| or a |Number|.
+ Examples: >
+ :echo tan(10)
+< 0.648361 >
+ :echo tan(-4.01)
+< -1.181502
+
+ Can also be used as a |method|: >
+ Compute()->tan()
+
+tanh({expr}) *tanh()*
+ Return the hyperbolic tangent of {expr} as a |Float| in the
+ range [-1, 1].
+ {expr} must evaluate to a |Float| or a |Number|.
+ Returns 0.0 if {expr} is not a |Float| or a |Number|.
+ Examples: >
+ :echo tanh(0.5)
+< 0.462117 >
+ :echo tanh(-1)
+< -0.761594
+
+ Can also be used as a |method|: >
+ Compute()->tanh()
+<
+ *timer_info()*
+timer_info([{id}])
+ Return a list with information about timers.
+ When {id} is given only information about this timer is
+ returned. When timer {id} does not exist an empty list is
+ returned.
+ When {id} is omitted information about all timers is returned.
+
+ For each timer the information is stored in a |Dictionary| with
+ these items:
+ "id" the timer ID
+ "time" time the timer was started with
+ "repeat" number of times the timer will still fire;
+ -1 means forever
+ "callback" the callback
+
+ Can also be used as a |method|: >
+ GetTimer()->timer_info()
+<
+timer_pause({timer}, {paused}) *timer_pause()*
+ Pause or unpause a timer. A paused timer does not invoke its
+ callback when its time expires. Unpausing a timer may cause
+ the callback to be invoked almost immediately if enough time
+ has passed.
+
+ Pausing a timer is useful to avoid the callback to be called
+ for a short time.
+
+ If {paused} evaluates to a non-zero Number or a non-empty
+ String, then the timer is paused, otherwise it is unpaused.
+ See |non-zero-arg|.
+
+ Can also be used as a |method|: >
+ GetTimer()->timer_pause(1)
+<
+ *timer_start()* *timer* *timers*
+timer_start({time}, {callback} [, {options}])
+ Create a timer and return the timer ID.
+
+ {time} is the waiting time in milliseconds. This is the
+ minimum time before invoking the callback. When the system is
+ busy or Vim is not waiting for input the time will be longer.
+
+ {callback} is the function to call. It can be the name of a
+ function or a |Funcref|. It is called with one argument, which
+ is the timer ID. The callback is only invoked when Vim is
+ waiting for input.
+
+ {options} is a dictionary. Supported entries:
+ "repeat" Number of times to repeat the callback.
+ -1 means forever. Default is 1.
+ If the timer causes an error three times in a
+ row the repeat is cancelled.
+
+ Returns -1 on error.
+
+ Example: >
+ func MyHandler(timer)
+ echo 'Handler called'
+ endfunc
+ let timer = timer_start(500, 'MyHandler',
+ \ {'repeat': 3})
+< This invokes MyHandler() three times at 500 msec intervals.
+
+ Can also be used as a |method|: >
+ GetMsec()->timer_start(callback)
+
+< Not available in the |sandbox|.
+
+timer_stop({timer}) *timer_stop()*
+ Stop a timer. The timer callback will no longer be invoked.
+ {timer} is an ID returned by timer_start(), thus it must be a
+ Number. If {timer} does not exist there is no error.
+
+ Can also be used as a |method|: >
+ GetTimer()->timer_stop()
+<
+timer_stopall() *timer_stopall()*
+ Stop all timers. The timer callbacks will no longer be
+ invoked. Useful if some timers is misbehaving. If there are
+ no timers there is no error.
+
+tolower({expr}) *tolower()*
+ The result is a copy of the String given, with all uppercase
+ characters turned into lowercase (just like applying |gu| to
+ the string). Returns an empty string on error.
+
+ Can also be used as a |method|: >
+ GetText()->tolower()
+
+toupper({expr}) *toupper()*
+ The result is a copy of the String given, with all lowercase
+ characters turned into uppercase (just like applying |gU| to
+ the string). Returns an empty string on error.
+
+ Can also be used as a |method|: >
+ GetText()->toupper()
+
+tr({src}, {fromstr}, {tostr}) *tr()*
+ The result is a copy of the {src} string with all characters
+ which appear in {fromstr} replaced by the character in that
+ position in the {tostr} string. Thus the first character in
+ {fromstr} is translated into the first character in {tostr}
+ and so on. Exactly like the unix "tr" command.
+ This code also deals with multibyte characters properly.
+
+ Returns an empty string on error.
+
+ Examples: >
+ echo tr("hello there", "ht", "HT")
+< returns "Hello THere" >
+ echo tr("<blob>", "<>", "{}")
+< returns "{blob}"
+
+ Can also be used as a |method|: >
+ GetText()->tr(from, to)
+
+trim({text} [, {mask} [, {dir}]]) *trim()*
+ Return {text} as a String where any character in {mask} is
+ removed from the beginning and/or end of {text}.
+ If {mask} is not given, {mask} is all characters up to 0x20,
+ which includes Tab, space, NL and CR, plus the non-breaking
+ space character 0xa0.
+ The optional {dir} argument specifies where to remove the
+ characters:
+ 0 remove from the beginning and end of {text}
+ 1 remove only at the beginning of {text}
+ 2 remove only at the end of {text}
+ When omitted both ends are trimmed.
+ This function deals with multibyte characters properly.
+ Returns an empty string on error.
+
+ Examples: >
+ echo trim(" some text ")
+< returns "some text" >
+ echo trim(" \r\t\t\r RESERVE \t\n\x0B\xA0") .. "_TAIL"
+< returns "RESERVE_TAIL" >
+ echo trim("rm<Xrm<>X>rrm", "rm<>")
+< returns "Xrm<>X" (characters in the middle are not removed) >
+ echo trim(" vim ", " ", 2)
+< returns " vim"
+
+ Can also be used as a |method|: >
+ GetText()->trim()
+
+trunc({expr}) *trunc()*
+ Return the largest integral value with magnitude less than or
+ equal to {expr} as a |Float| (truncate towards zero).
+ {expr} must evaluate to a |Float| or a |Number|.
+ Returns 0.0 if {expr} is not a |Float| or a |Number|.
+ Examples: >
+ echo trunc(1.456)
+< 1.0 >
+ echo trunc(-5.456)
+< -5.0 >
+ echo trunc(4.0)
+< 4.0
+
+ Can also be used as a |method|: >
+ Compute()->trunc()
+
+type({expr}) *type()*
+ The result is a Number representing the type of {expr}.
+ Instead of using the number directly, it is better to use the
+ v:t_ variable that has the value:
+ Number: 0 (|v:t_number|)
+ String: 1 (|v:t_string|)
+ Funcref: 2 (|v:t_func|)
+ List: 3 (|v:t_list|)
+ Dictionary: 4 (|v:t_dict|)
+ Float: 5 (|v:t_float|)
+ Boolean: 6 (|v:true| and |v:false|)
+ Null: 7 (|v:null|)
+ Blob: 10 (|v:t_blob|)
+ For backward compatibility, this method can be used: >
+ :if type(myvar) == type(0)
+ :if type(myvar) == type("")
+ :if type(myvar) == type(function("tr"))
+ :if type(myvar) == type([])
+ :if type(myvar) == type({})
+ :if type(myvar) == type(0.0)
+ :if type(myvar) == type(v:true)
+< In place of checking for |v:null| type it is better to check
+ for |v:null| directly as it is the only value of this type: >
+ :if myvar is v:null
+< To check if the v:t_ variables exist use this: >
+ :if exists('v:t_number')
+
+< Can also be used as a |method|: >
+ mylist->type()
+
+undofile({name}) *undofile()*
+ Return the name of the undo file that would be used for a file
+ with name {name} when writing. This uses the 'undodir'
+ option, finding directories that exist. It does not check if
+ the undo file exists.
+ {name} is always expanded to the full path, since that is what
+ is used internally.
+ If {name} is empty undofile() returns an empty string, since a
+ buffer without a file name will not write an undo file.
+ Useful in combination with |:wundo| and |:rundo|.
+
+ Can also be used as a |method|: >
+ GetFilename()->undofile()
+
+undotree() *undotree()*
+ Return the current state of the undo tree in a dictionary with
+ the following items:
+ "seq_last" The highest undo sequence number used.
+ "seq_cur" The sequence number of the current position in
+ the undo tree. This differs from "seq_last"
+ when some changes were undone.
+ "time_cur" Time last used for |:earlier| and related
+ commands. Use |strftime()| to convert to
+ something readable.
+ "save_last" Number of the last file write. Zero when no
+ write yet.
+ "save_cur" Number of the current position in the undo
+ tree.
+ "synced" Non-zero when the last undo block was synced.
+ This happens when waiting from input from the
+ user. See |undo-blocks|.
+ "entries" A list of dictionaries with information about
+ undo blocks.
+
+ The first item in the "entries" list is the oldest undo item.
+ Each List item is a |Dictionary| with these items:
+ "seq" Undo sequence number. Same as what appears in
+ |:undolist|.
+ "time" Timestamp when the change happened. Use
+ |strftime()| to convert to something readable.
+ "newhead" Only appears in the item that is the last one
+ that was added. This marks the last change
+ and where further changes will be added.
+ "curhead" Only appears in the item that is the last one
+ that was undone. This marks the current
+ position in the undo tree, the block that will
+ be used by a redo command. When nothing was
+ undone after the last change this item will
+ not appear anywhere.
+ "save" Only appears on the last block before a file
+ write. The number is the write count. The
+ first write has number 1, the last one the
+ "save_last" mentioned above.
+ "alt" Alternate entry. This is again a List of undo
+ blocks. Each item may again have an "alt"
+ item.
+
+uniq({list} [, {func} [, {dict}]]) *uniq()* *E882*
+ Remove second and succeeding copies of repeated adjacent
+ {list} items in-place. Returns {list}. If you want a list
+ to remain unmodified make a copy first: >
+ :let newlist = uniq(copy(mylist))
+< The default compare function uses the string representation of
+ each item. For the use of {func} and {dict} see |sort()|.
+
+ Returns zero if {list} is not a |List|.
+
+ Can also be used as a |method|: >
+ mylist->uniq()
+
+values({dict}) *values()*
+ Return a |List| with all the values of {dict}. The |List| is
+ in arbitrary order. Also see |items()| and |keys()|.
+ Returns zero if {dict} is not a |Dict|.
+
+ Can also be used as a |method|: >
+ mydict->values()
+
+virtcol({expr}) *virtcol()*
+ The result is a Number, which is the screen column of the file
+ position given with {expr}. That is, the last screen position
+ occupied by the character at that position, when the screen
+ would be of unlimited width. When there is a <Tab> at the
+ position, the returned Number will be the column at the end of
+ the <Tab>. For example, for a <Tab> in column 1, with 'ts'
+ set to 8, it returns 8. |conceal| is ignored.
+ For the byte position use |col()|.
+ For the use of {expr} see |col()|.
+ When 'virtualedit' is used {expr} can be [lnum, col, off], where
+ "off" is the offset in screen columns from the start of the
+ character. E.g., a position within a <Tab> or after the last
+ character. When "off" is omitted zero is used.
+ When Virtual editing is active in the current mode, a position
+ beyond the end of the line can be returned. |'virtualedit'|
+ The accepted positions are:
+ . the cursor position
+ $ the end of the cursor line (the result is the
+ number of displayed characters in the cursor line
+ plus one)
+ 'x position of mark x (if the mark is not set, 0 is
+ returned)
+ v In Visual mode: the start of the Visual area (the
+ cursor is the end). When not in Visual mode
+ returns the cursor position. Differs from |'<| in
+ that it's updated right away.
+ Note that only marks in the current file can be used.
+ Examples: >
+ virtcol(".") with text "foo^Lbar", with cursor on the "^L", returns 5
+ virtcol("$") with text "foo^Lbar", returns 9
+ virtcol("'t") with text " there", with 't at 'h', returns 6
+< The first column is 1. 0 is returned for an error.
+ A more advanced example that echoes the maximum length of
+ all lines: >
+ echo max(map(range(1, line('$')), "virtcol([v:val, '$'])"))
+
+< Can also be used as a |method|: >
+ GetPos()->virtcol()
+
+visualmode([{expr}]) *visualmode()*
+ The result is a String, which describes the last Visual mode
+ used in the current buffer. Initially it returns an empty
+ string, but once Visual mode has been used, it returns "v",
+ "V", or "<CTRL-V>" (a single CTRL-V character) for
+ character-wise, line-wise, or block-wise Visual mode
+ respectively.
+ Example: >
+ :exe "normal " .. visualmode()
+< This enters the same Visual mode as before. It is also useful
+ in scripts if you wish to act differently depending on the
+ Visual mode that was used.
+ If Visual mode is active, use |mode()| to get the Visual mode
+ (e.g., in a |:vmap|).
+ If {expr} is supplied and it evaluates to a non-zero Number or
+ a non-empty String, then the Visual mode will be cleared and
+ the old value is returned. See |non-zero-arg|.
+
+wait({timeout}, {condition} [, {interval}]) *wait()*
+ Waits until {condition} evaluates to |TRUE|, where {condition}
+ is a |Funcref| or |string| containing an expression.
+
+ {timeout} is the maximum waiting time in milliseconds, -1
+ means forever.
+
+ Condition is evaluated on user events, internal events, and
+ every {interval} milliseconds (default: 200).
+
+ Returns a status integer:
+ 0 if the condition was satisfied before timeout
+ -1 if the timeout was exceeded
+ -2 if the function was interrupted (by |CTRL-C|)
+ -3 if an error occurred
+
+wildmenumode() *wildmenumode()*
+ Returns |TRUE| when the wildmenu is active and |FALSE|
+ otherwise. See 'wildmenu' and 'wildmode'.
+ This can be used in mappings to handle the 'wildcharm' option
+ gracefully. (Makes only sense with |mapmode-c| mappings).
+
+ For example to make <c-j> work like <down> in wildmode, use: >
+ :cnoremap <expr> <C-j> wildmenumode() ? "\<Down>\<Tab>" : "\<c-j>"
+<
+ (Note, this needs the 'wildcharm' option set appropriately).
+
+win_execute({id}, {command} [, {silent}]) *win_execute()*
+ Like `execute()` but in the context of window {id}.
+ The window will temporarily be made the current window,
+ without triggering autocommands or changing directory. When
+ executing {command} autocommands will be triggered, this may
+ have unexpected side effects. Use |:noautocmd| if needed.
+ Example: >
+ call win_execute(winid, 'syntax enable')
+<
+ Can also be used as a |method|, the base is passed as the
+ second argument: >
+ GetCommand()->win_execute(winid)
+
+win_findbuf({bufnr}) *win_findbuf()*
+ Returns a |List| with |window-ID|s for windows that contain
+ buffer {bufnr}. When there is none the list is empty.
+
+ Can also be used as a |method|: >
+ GetBufnr()->win_findbuf()
+
+win_getid([{win} [, {tab}]]) *win_getid()*
+ Get the |window-ID| for the specified window.
+ When {win} is missing use the current window.
+ With {win} this is the window number. The top window has
+ number 1.
+ Without {tab} use the current tab, otherwise the tab with
+ number {tab}. The first tab has number one.
+ Return zero if the window cannot be found.
+
+ Can also be used as a |method|: >
+ GetWinnr()->win_getid()
+
+win_gettype([{nr}]) *win_gettype()*
+ Return the type of the window:
+ "autocmd" autocommand window. Temporary window
+ used to execute autocommands.
+ "command" command-line window |cmdwin|
+ (empty) normal window
+ "loclist" |location-list-window|
+ "popup" floating window |api-floatwin|
+ "preview" preview window |preview-window|
+ "quickfix" |quickfix-window|
+ "unknown" window {nr} not found
+
+ When {nr} is omitted return the type of the current window.
+ When {nr} is given return the type of this window by number or
+ |window-ID|.
+
+ Also see the 'buftype' option.
+
+ Can also be used as a |method|: >
+ GetWinid()->win_gettype()
+<
+win_gotoid({expr}) *win_gotoid()*
+ Go to window with ID {expr}. This may also change the current
+ tabpage.
+ Return TRUE if successful, FALSE if the window cannot be found.
+
+ Can also be used as a |method|: >
+ GetWinid()->win_gotoid()
+
+win_id2tabwin({expr}) *win_id2tabwin()*
+ Return a list with the tab number and window number of window
+ with ID {expr}: [tabnr, winnr].
+ Return [0, 0] if the window cannot be found.
+
+ Can also be used as a |method|: >
+ GetWinid()->win_id2tabwin()
+
+win_id2win({expr}) *win_id2win()*
+ Return the window number of window with ID {expr}.
+ Return 0 if the window cannot be found in the current tabpage.
+
+ Can also be used as a |method|: >
+ GetWinid()->win_id2win()
+
+win_move_separator({nr}, {offset}) *win_move_separator()*
+ Move window {nr}'s vertical separator (i.e., the right border)
+ by {offset} columns, as if being dragged by the mouse. {nr}
+ can be a window number or |window-ID|. A positive {offset}
+ moves right and a negative {offset} moves left. Moving a
+ window's vertical separator will change the width of the
+ window and the width of other windows adjacent to the vertical
+ separator. The magnitude of movement may be smaller than
+ specified (e.g., as a consequence of maintaining
+ 'winminwidth'). Returns TRUE if the window can be found and
+ FALSE otherwise.
+ This will fail for the rightmost window and a full-width
+ window, since it has no separator on the right.
+
+ Can also be used as a |method|: >
+ GetWinnr()->win_move_separator(offset)
+
+win_move_statusline({nr}, {offset}) *win_move_statusline()*
+ Move window {nr}'s status line (i.e., the bottom border) by
+ {offset} rows, as if being dragged by the mouse. {nr} can be a
+ window number or |window-ID|. A positive {offset} moves down
+ and a negative {offset} moves up. Moving a window's status
+ line will change the height of the window and the height of
+ other windows adjacent to the status line. The magnitude of
+ movement may be smaller than specified (e.g., as a consequence
+ of maintaining 'winminheight'). Returns TRUE if the window can
+ be found and FALSE otherwise.
+
+ Can also be used as a |method|: >
+ GetWinnr()->win_move_statusline(offset)
+
+win_screenpos({nr}) *win_screenpos()*
+ Return the screen position of window {nr} as a list with two
+ numbers: [row, col]. The first window always has position
+ [1, 1], unless there is a tabline, then it is [2, 1].
+ {nr} can be the window number or the |window-ID|. Use zero
+ for the current window.
+ Returns [0, 0] if the window cannot be found in the current
+ tabpage.
+
+ Can also be used as a |method|: >
+ GetWinid()->win_screenpos()
+<
+win_splitmove({nr}, {target} [, {options}]) *win_splitmove()*
+ Move the window {nr} to a new split of the window {target}.
+ This is similar to moving to {target}, creating a new window
+ using |:split| but having the same contents as window {nr}, and
+ then closing {nr}.
+
+ Both {nr} and {target} can be window numbers or |window-ID|s.
+ Both must be in the current tab page.
+
+ Returns zero for success, non-zero for failure.
+
+ {options} is a |Dictionary| with the following optional entries:
+ "vertical" When TRUE, the split is created vertically,
+ like with |:vsplit|.
+ "rightbelow" When TRUE, the split is made below or to the
+ right (if vertical). When FALSE, it is done
+ above or to the left (if vertical). When not
+ present, the values of 'splitbelow' and
+ 'splitright' are used.
+
+ Can also be used as a |method|: >
+ GetWinid()->win_splitmove(target)
+<
+ *winbufnr()*
+winbufnr({nr}) The result is a Number, which is the number of the buffer
+ associated with window {nr}. {nr} can be the window number or
+ the |window-ID|.
+ When {nr} is zero, the number of the buffer in the current
+ window is returned.
+ When window {nr} doesn't exist, -1 is returned.
+ Example: >
+ :echo "The file in the current window is " .. bufname(winbufnr(0))
+<
+ Can also be used as a |method|: >
+ FindWindow()->winbufnr()->bufname()
+<
+ *wincol()*
+wincol() The result is a Number, which is the virtual column of the
+ cursor in the window. This is counting screen cells from the
+ left side of the window. The leftmost column is one.
+
+ *windowsversion()*
+windowsversion()
+ The result is a String. For MS-Windows it indicates the OS
+ version. E.g, Windows 10 is "10.0", Windows 8 is "6.2",
+ Windows XP is "5.1". For non-MS-Windows systems the result is
+ an empty string.
+
+winheight({nr}) *winheight()*
+ The result is a Number, which is the height of window {nr}.
+ {nr} can be the window number or the |window-ID|.
+ When {nr} is zero, the height of the current window is
+ returned. When window {nr} doesn't exist, -1 is returned.
+ An existing window always has a height of zero or more.
+ This excludes any window toolbar line.
+ Examples: >
+ :echo "The current window has " .. winheight(0) .. " lines."
+
+< Can also be used as a |method|: >
+ GetWinid()->winheight()
+<
+winlayout([{tabnr}]) *winlayout()*
+ The result is a nested List containing the layout of windows
+ in a tabpage.
+
+ Without {tabnr} use the current tabpage, otherwise the tabpage
+ with number {tabnr}. If the tabpage {tabnr} is not found,
+ returns an empty list.
+
+ For a leaf window, it returns:
+ ['leaf', {winid}]
+ For horizontally split windows, which form a column, it
+ returns:
+ ['col', [{nested list of windows}]]
+ For vertically split windows, which form a row, it returns:
+ ['row', [{nested list of windows}]]
+
+ Example: >
+ " Only one window in the tab page
+ :echo winlayout()
+ ['leaf', 1000]
+ " Two horizontally split windows
+ :echo winlayout()
+ ['col', [['leaf', 1000], ['leaf', 1001]]]
+ " The second tab page, with three horizontally split
+ " windows, with two vertically split windows in the
+ " middle window
+ :echo winlayout(2)
+ ['col', [['leaf', 1002], ['row', [['leaf', 1003],
+ ['leaf', 1001]]], ['leaf', 1000]]]
+<
+ Can also be used as a |method|: >
+ GetTabnr()->winlayout()
+<
+ *winline()*
+winline() The result is a Number, which is the screen line of the cursor
+ in the window. This is counting screen lines from the top of
+ the window. The first line is one.
+ If the cursor was moved the view on the file will be updated
+ first, this may cause a scroll.
+
+ *winnr()*
+winnr([{arg}]) The result is a Number, which is the number of the current
+ window. The top window has number 1.
+ Returns zero for a popup window.
+
+ The optional argument {arg} supports the following values:
+ $ the number of the last window (the window
+ count).
+ # the number of the last accessed window (where
+ |CTRL-W_p| goes to). If there is no previous
+ window or it is in another tab page 0 is
+ returned.
+ {N}j the number of the Nth window below the
+ current window (where |CTRL-W_j| goes to).
+ {N}k the number of the Nth window above the current
+ window (where |CTRL-W_k| goes to).
+ {N}h the number of the Nth window left of the
+ current window (where |CTRL-W_h| goes to).
+ {N}l the number of the Nth window right of the
+ current window (where |CTRL-W_l| goes to).
+ The number can be used with |CTRL-W_w| and ":wincmd w"
+ |:wincmd|.
+ When {arg} is invalid an error is given and zero is returned.
+ Also see |tabpagewinnr()| and |win_getid()|.
+ Examples: >
+ let window_count = winnr('$')
+ let prev_window = winnr('#')
+ let wnum = winnr('3k')
+
+< Can also be used as a |method|: >
+ GetWinval()->winnr()
+<
+ *winrestcmd()*
+winrestcmd() Returns a sequence of |:resize| commands that should restore
+ the current window sizes. Only works properly when no windows
+ are opened or closed and the current window and tab page is
+ unchanged.
+ Example: >
+ :let cmd = winrestcmd()
+ :call MessWithWindowSizes()
+ :exe cmd
+<
+ *winrestview()*
+winrestview({dict})
+ Uses the |Dictionary| returned by |winsaveview()| to restore
+ the view of the current window.
+ Note: The {dict} does not have to contain all values, that are
+ returned by |winsaveview()|. If values are missing, those
+ settings won't be restored. So you can use: >
+ :call winrestview({'curswant': 4})
+<
+ This will only set the curswant value (the column the cursor
+ wants to move on vertical movements) of the cursor to column 5
+ (yes, that is 5), while all other settings will remain the
+ same. This is useful, if you set the cursor position manually.
+
+ If you have changed the values the result is unpredictable.
+ If the window size changed the result won't be the same.
+
+ Can also be used as a |method|: >
+ GetView()->winrestview()
+<
+ *winsaveview()*
+winsaveview() Returns a |Dictionary| that contains information to restore
+ the view of the current window. Use |winrestview()| to
+ restore the view.
+ This is useful if you have a mapping that jumps around in the
+ buffer and you want to go back to the original view.
+ This does not save fold information. Use the 'foldenable'
+ option to temporarily switch off folding, so that folds are
+ not opened when moving around. This may have side effects.
+ The return value includes:
+ lnum cursor line number
+ col cursor column (Note: the first column
+ zero, as opposed to what getpos()
+ returns)
+ coladd cursor column offset for 'virtualedit'
+ curswant column for vertical movement
+ topline first line in the window
+ topfill filler lines, only in diff mode
+ leftcol first column displayed; only used when
+ 'wrap' is off
+ skipcol columns skipped
+ Note that no option values are saved.
+
+
+winwidth({nr}) *winwidth()*
+ The result is a Number, which is the width of window {nr}.
+ {nr} can be the window number or the |window-ID|.
+ When {nr} is zero, the width of the current window is
+ returned. When window {nr} doesn't exist, -1 is returned.
+ An existing window always has a width of zero or more.
+ Examples: >
+ :echo "The current window has " .. winwidth(0) .. " columns."
+ :if winwidth(0) <= 50
+ : 50 wincmd |
+ :endif
+< For getting the terminal or screen size, see the 'columns'
+ option.
+
+ Can also be used as a |method|: >
+ GetWinid()->winwidth()
+
+wordcount() *wordcount()*
+ The result is a dictionary of byte/chars/word statistics for
+ the current buffer. This is the same info as provided by
+ |g_CTRL-G|
+ The return value includes:
+ bytes Number of bytes in the buffer
+ chars Number of chars in the buffer
+ words Number of words in the buffer
+ cursor_bytes Number of bytes before cursor position
+ (not in Visual mode)
+ cursor_chars Number of chars before cursor position
+ (not in Visual mode)
+ cursor_words Number of words before cursor position
+ (not in Visual mode)
+ visual_bytes Number of bytes visually selected
+ (only in Visual mode)
+ visual_chars Number of chars visually selected
+ (only in Visual mode)
+ visual_words Number of words visually selected
+ (only in Visual mode)
+
+
+ *writefile()*
+writefile({object}, {fname} [, {flags}])
+ When {object} is a |List| write it to file {fname}. Each list
+ item is separated with a NL. Each list item must be a String
+ or Number.
+ When {flags} contains "b" then binary mode is used: There will
+ not be a NL after the last list item. An empty item at the
+ end does cause the last line in the file to end in a NL.
+
+ When {object} is a |Blob| write the bytes to file {fname}
+ unmodified.
+
+ When {flags} contains "a" then append mode is used, lines are
+ appended to the file: >
+ :call writefile(["foo"], "event.log", "a")
+ :call writefile(["bar"], "event.log", "a")
+<
+ When {flags} contains "S" fsync() call is not used, with "s"
+ it is used, 'fsync' option applies by default. No fsync()
+ means that writefile() will finish faster, but writes may be
+ left in OS buffers and not yet written to disk. Such changes
+ will disappear if system crashes before OS does writing.
+
+ All NL characters are replaced with a NUL character.
+ Inserting CR characters needs to be done before passing {list}
+ to writefile().
+ An existing file is overwritten, if possible.
+ When the write fails -1 is returned, otherwise 0. There is an
+ error message if the file can't be created or when writing
+ fails.
+ Also see |readfile()|.
+ To copy a file byte for byte: >
+ :let fl = readfile("foo", "b")
+ :call writefile(fl, "foocopy", "b")
+
+< Can also be used as a |method|: >
+ GetText()->writefile("thefile")
+
+xor({expr}, {expr}) *xor()*
+ Bitwise XOR on the two arguments. The arguments are converted
+ to a number. A List, Dict or Float argument causes an error.
+ Example: >
+ :let bits = xor(bits, 0x80)
+<
+ Can also be used as a |method|: >
+ :let bits = bits->xor(0x80)
+<
+==============================================================================
+3. Matching a pattern in a String *string-match*
+
+This is common between several functions. A regexp pattern as explained at
+|pattern| is normally used to find a match in the buffer lines. When a
+pattern is used to find a match in a String, almost everything works in the
+same way. The difference is that a String is handled like it is one line.
+When it contains a "\n" character, this is not seen as a line break for the
+pattern. It can be matched with a "\n" in the pattern, or with ".". Example:
+>
+ :let a = "aaaa\nxxxx"
+ :echo matchstr(a, "..\n..")
+ aa
+ xx
+ :echo matchstr(a, "a.x")
+ a
+ x
+
+Don't forget that "^" will only match at the first character of the String and
+"$" at the last character of the string. They don't match after or before a
+"\n".
+
+ vim:tw=78:ts=8:noet:ft=help:norl:
diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt
index 953f097a92..b905f53db7 100644
--- a/runtime/doc/change.txt
+++ b/runtime/doc/change.txt
@@ -42,8 +42,6 @@ For inserting text see |insert.txt|.
of the line and [count]-1 more lines [into register
x]; synonym for "d$".
(not |linewise|)
- When the '#' flag is in 'cpoptions' the count is
- ignored.
{Visual}["x]x or *v_x* *v_d* *v_<Del>*
{Visual}["x]d or
@@ -355,14 +353,14 @@ CTRL-A Add [count] to the number or alphabetic character at
*v_CTRL-A*
{Visual}CTRL-A Add [count] to the number or alphabetic character in
- the highlighted text. {not in Vi}
+ the highlighted text.
*v_g_CTRL-A*
{Visual}g CTRL-A Add [count] to the number or alphabetic character in
the highlighted text. If several lines are
highlighted, each one will be incremented by an
additional [count] (so effectively creating a
- [count] incrementing sequence). {not in Vi}
+ [count] incrementing sequence).
For Example, if you have this list of numbers:
1. ~
1. ~
@@ -381,14 +379,14 @@ CTRL-X Subtract [count] from the number or alphabetic
*v_CTRL-X*
{Visual}CTRL-X Subtract [count] from the number or alphabetic
- character in the highlighted text. {not in Vi}
+ character in the highlighted text.
*v_g_CTRL-X*
{Visual}g CTRL-X Subtract [count] from the number or alphabetic
character in the highlighted text. If several lines
are highlighted, each value will be decremented by an
additional [count] (so effectively creating a [count]
- decrementing sequence). {not in Vi}
+ decrementing sequence).
The CTRL-A and CTRL-X commands work for (signed) decimal numbers, unsigned
binary/octal/hexadecimal numbers and alphabetic characters.
@@ -585,7 +583,7 @@ attack or other people reading your file). When Vim exits the directory and
all files in it are deleted. When Vim has the setuid bit set this may cause
problems, the temp file is owned by the setuid user but the filter command
probably runs as the original user.
-Directory for temporary files is created in the first suitable directory of:
+Directory for temporary files is created in the first possible directory of:
Unix: $TMPDIR, /tmp, current-dir, $HOME.
Windows: $TMPDIR, $TMP, $TEMP, $USERPROFILE, current-dir.
@@ -635,6 +633,9 @@ Directory for temporary files is created in the first suitable directory of:
actually work differently. You can use `:&&` to keep
the flags.
+ *&-default*
+ Mapped to ":&&<CR>" by default. |default-mappings|
+
*g&*
g& Synonym for `:%s//~/&` (repeat last substitute with
last search pattern on all lines with the same flags).
@@ -906,7 +907,7 @@ Consider using a character like "@" or ":". There is no problem if the result
of the expression contains the separation character.
Examples: >
- :s@\n@\="\r" . expand("$HOME") . "\r"@
+ :s@\n@\="\r" .. expand("$HOME") .. "\r"@
This replaces an end-of-line with a new line containing the value of $HOME. >
s/E/\="\<Char-0x20ac>"/g
@@ -1018,7 +1019,7 @@ inside of strings can change! Also see 'softtabstop' option. >
in [range] (default: current line |cmdline-ranges|),
[into register x].
- *p* *put* *E353*
+ *p* *put* *E353* *E1240*
["x]p Put the text [from register x] after the cursor
[count] times.
@@ -1065,7 +1066,7 @@ inside of strings can change! Also see 'softtabstop' option. >
the command. You need to escape the '|' and '"'
characters to prevent them from terminating the
command. Example: >
- :put ='path' . \",/test\"
+ :put ='path' .. \",/test\"
< If there is no expression after '=', Vim uses the
previous expression. You can see it with ":dis =".
@@ -1118,11 +1119,15 @@ register. With blockwise selection it also depends on the size of the block
and whether the corners are on an existing character. (Implementation detail:
it actually works by first putting the register after the selection and then
deleting the selection.)
-The previously selected text is put in the unnamed register. If you want to
-put the same text into a Visual selection several times you need to use
-another register. E.g., yank the text to copy, Visually select the text to
-replace and use "0p . You can repeat this as many times as you like, the
-unnamed register will be changed each time.
+With |p| the previously selected text is put in the unnamed register (and
+possibly the selection and/or clipboard). This is useful if you want to put
+that text somewhere else. But you cannot repeat the same change.
+With |P| the unnamed register is not changed (and neither the selection or
+clipboard), you can repeat the same change. But the deleted text cannot be
+used. If you do need it you can use |p| with another register. E.g., yank
+the text to copy, Visually select the text to replace and use "0p . You can
+repeat this as many times as you like, and the unnamed register will be
+changed each time.
When you use a blockwise Visual mode command and yank only a single line into
a register, a paste on a visual selected area will paste that single line on
@@ -1205,7 +1210,7 @@ to their previous contents. When the '>' flag is present in 'cpoptions' then
a line break is inserted before the appended text.
5. Read-only registers ":, ". and "%
-These are '%', '#', ':' and '.'. You can use them only with the "p", "P",
+These are '%', ':' and '.'. You can use them only with the "p", "P",
and ":put" commands and with CTRL-R.
*quote_.* *quote.* *E29*
". Contains the last inserted text (the same as what is inserted
@@ -1584,11 +1589,10 @@ You can use the 'formatoptions' option to influence how Vim formats text.
can separate the option letters with commas for readability.
letter meaning when present in 'formatoptions' ~
-
*fo-t*
-t Auto-wrap text using textwidth
+t Auto-wrap text using 'textwidth'
*fo-c*
-c Auto-wrap comments using textwidth, inserting the current comment
+c Auto-wrap comments using 'textwidth', inserting the current comment
leader automatically.
*fo-r*
r Automatically insert the current comment leader after hitting
@@ -1597,6 +1601,9 @@ r Automatically insert the current comment leader after hitting
o Automatically insert the current comment leader after hitting 'o' or
'O' in Normal mode. In case comment is unwanted in a specific place
use CTRL-U to quickly delete it. |i_CTRL-U|
+ *fo-/*
+/ When 'o' is included: do not insert the comment leader for a //
+ comment after a statement, only when // is at the start of the line.
*fo-q*
q Allow formatting of comments with "gq".
Note that formatting will not change blank lines or lines containing
@@ -1659,8 +1666,8 @@ B When joining lines, don't insert a space between two multibyte
1 Don't break a line after a one-letter word. It's broken before it
instead (if possible).
*fo-]*
-] Respect textwidth rigorously. With this flag set, no line can be
- longer than textwidth, unless line-break-prohibition rules make this
+] Respect 'textwidth' rigorously. With this flag set, no line can be
+ longer than 'textwidth', unless line-break-prohibition rules make this
impossible. Mainly for CJK scripts and works only if 'encoding' is
"utf-8".
*fo-j*
diff --git a/runtime/doc/channel.txt b/runtime/doc/channel.txt
index e14427494d..d4bed7a5f2 100644
--- a/runtime/doc/channel.txt
+++ b/runtime/doc/channel.txt
@@ -229,12 +229,12 @@ prompt. >
call chansend(g:shell_job, [a:text, ''])
endfunc
- " Function handling output from the shell: Added above the prompt.
+ " Function handling output from the shell: Add it above the prompt.
func GotOutput(channel, msg, name)
call append(line("$") - 1, a:msg)
endfunc
- " Function handling the shell exit: close the window.
+ " Function handling the shell exits: close the window.
func JobExit(job, status, event)
quit!
endfunc
diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt
index 641cd93386..3f3f1e6ba3 100644
--- a/runtime/doc/cmdline.txt
+++ b/runtime/doc/cmdline.txt
@@ -67,12 +67,19 @@ CTRL-V Insert next non-digit literally. Up to three digits form the
decimal value of a single byte. The non-digit and the three
digits are not considered for mapping. This works the same
way as in Insert mode (see above, |i_CTRL-V|).
+ For special keys, the CTRL modifier may be included into the
+ key to produce a control character. If there is no control
+ character for the key then its |key-notation| is inserted.
Note: Under Windows CTRL-V is often mapped to paste text.
Use CTRL-Q instead then.
*c_CTRL-Q*
CTRL-Q Same as CTRL-V. But with some terminals it is used for
control flow, it doesn't work then.
+CTRL-SHIFT-V *c_CTRL-SHIFT-V* *c_CTRL-SHIFT-Q*
+CTRL-SHIFT-Q Works just like CTRL-V, but do not try to include the CTRL
+ modifier into the key.
+
*c_<Left>* *c_Left*
<Left> cursor left
*c_<Right>* *c_Right*
@@ -207,7 +214,7 @@ CTRL-\ e {expr} *c_CTRL-\_e*
Example: >
:cmap <F7> <C-\>eAppendSome()<CR>
:func AppendSome()
- :let cmd = getcmdline() . " Some()"
+ :let cmd = getcmdline() .. " Some()"
:" place the cursor on the )
:call setcmdpos(strlen(cmd))
:return cmd
@@ -679,7 +686,7 @@ If more line specifiers are given than required for the command, the first
one(s) will be ignored.
Line numbers may be specified with: *:range* *{address}*
- {number} an absolute line number
+ {number} an absolute line number *E1247*
. the current line *:.*
$ the last line in the file *:$*
% equal to 1,$ (the entire file) *:%*
@@ -870,7 +877,7 @@ Note: these are typed literally, they are not special keys!
When the match is with a file name, it is expanded to the
full path.
*:<sfile>* *<sfile>*
- <sfile> When executing a ":source" command, is replaced with the
+ <sfile> When executing a `:source` command, is replaced with the
file name of the sourced file. *E498*
When executing a function, is replaced with:
"function {function-name}[{lnum}]"
@@ -879,7 +886,7 @@ Note: these are typed literally, they are not special keys!
Note that filename-modifiers are useless when <sfile> is
used inside a function.
*:<slnum>* *<slnum>*
- <slnum> When executing a ":source" command, is replaced with the
+ <slnum> When executing a `:source` command, is replaced with the
line number. *E842*
When executing a function it's the line number relative to
the start of the function.
@@ -908,8 +915,7 @@ These modifiers can be given, in this order:
directory.
:. Reduce file name to be relative to current directory, if
possible. File name is unmodified if it is not below the
- current directory, but on MS-Windows the drive is removed if
- it is the current drive.
+ current directory.
For maximum shortness, use ":~:.".
:h Head of the file name (the last component and any separators
removed). Cannot be used with :e, :r or :t.
@@ -1059,8 +1065,7 @@ line contains the command as typed so far. The left column will show a
character that indicates the type of command-line being edited, see
|cmdwin-char|.
-Vim will be in Normal mode when the editor is opened, except when 'insertmode'
-is set.
+Vim will be in Normal mode when the editor is opened.
The height of the window is specified with 'cmdwinheight' (or smaller if there
is no room). The window is always full width and is positioned just above the
@@ -1131,8 +1136,8 @@ save the command-line history and read it back later.
If the 'wildchar' option is set to <Tab>, and the command-line window is used
for an Ex command, then two mappings will be added to use <Tab> for completion
in the command-line window, like this: >
- :imap <buffer> <Tab> <C-X><C-V>
- :nmap <buffer> <Tab> a<C-X><C-V>
+ :inoremap <buffer> <Tab> <C-X><C-V>
+ :nnoremap <buffer> <Tab> a<C-X><C-V>
Note that hitting <Tab> in Normal mode will do completion on the next
character. That way it works at the end of the line.
If you don't want these mappings, disable them with: >
diff --git a/runtime/doc/deprecated.txt b/runtime/doc/deprecated.txt
index 21a34178b3..eb6d9b6dc9 100644
--- a/runtime/doc/deprecated.txt
+++ b/runtime/doc/deprecated.txt
@@ -6,9 +6,8 @@
Nvim *deprecated*
-The items listed below are "deprecated". This means they will be removed in
-the future. They should not be used in new scripts, and old scripts should be
-updated.
+The items listed below are deprecated: they will be removed in the future.
+They should not be used in new scripts, and old scripts should be updated.
==============================================================================
@@ -25,8 +24,12 @@ Commands ~
*:wviminfo* Deprecated alias to |:wshada| command.
Environment Variables ~
-*$NVIM_LISTEN_ADDRESS* Deprecated in favor of |--listen|. If both are given,
- $NVIM_LISTEN_ADDRESS is ignored.
+*$NVIM_LISTEN_ADDRESS* Deprecated way to
+ * set the server name (use |--listen| instead)
+ * get the server name (use |v:servername| instead)
+ * detect a parent Nvim (use |$NVIM| instead)
+ Unset by |terminal| and |jobstart()| (unless explicitly
+ given by the "env" option). Ignored if --listen is given.
Events ~
*BufCreate* Use |BufAdd| instead.
@@ -55,6 +58,10 @@ Functions ~
without stopping the job. Use chanclose(id) to close
any socket.
+Highlights ~
+
+*hl-VertSplit* Use |hl-WinSeparator| instead.
+
LSP Diagnostics ~
For each of the functions below, use the corresponding function in
@@ -99,11 +106,13 @@ internally and are no longer exposed as part of the API. Instead, use
*vim.lsp.diagnostic.set_underline()*
*vim.lsp.diagnostic.set_virtual_text()*
-LSP Utility Functions ~
+LSP Functions ~
*vim.lsp.util.diagnostics_to_items()* Use |vim.diagnostic.toqflist()| instead.
*vim.lsp.util.set_qflist()* Use |setqflist()| instead.
*vim.lsp.util.set_loclist()* Use |setloclist()| instead.
+*vim.lsp.buf_get_clients()* Use |vim.lsp.get_active_clients()| with
+ {buffer = bufnr} instead.
Lua ~
*vim.register_keystroke_callback()* Use |vim.on_key()| instead.
diff --git a/runtime/doc/dev_style.txt b/runtime/doc/dev_style.txt
index 82f279e781..77253e7831 100644
--- a/runtime/doc/dev_style.txt
+++ b/runtime/doc/dev_style.txt
@@ -48,22 +48,6 @@ The format of the symbol name should be `NVIM_<DIRECTORY>_<FILE>_H`.
<
-Names and Order of Includes ~
-
-Use standard order for readability and to avoid hidden dependencies: C
-library, other libraries' `.h`, your project's `.h`.
-
- In foo.c order your includes as follows:
-
- 1. C system files.
- 2. Other libraries' `.h` files.
- 3. Your project's `.h` files.
-
- Exception: sometimes, system-specific code needs conditional includes.
- Such code can put conditional includes after other includes. Of course,
- keep your system-specific code small and localized.
-
-
Constants ~
Do not use macros to define constants in headers.
@@ -545,12 +529,12 @@ it is for and how it should be used. >
};
If the field comments are short, you can also put them next to the field. But
-be consistent within one struct. >
+be consistent within one struct, and follow the necessary doxygen style. >
struct wininfo_S {
- WinInfo *wi_next; /// Next entry or NULL for last entry.
- WinInfo *wi_prev; /// Previous entry or NULL for first entry.
- Win *wi_win; /// Pointer to window that did the wi_fpos.
+ WinInfo *wi_next; ///< Next entry or NULL for last entry.
+ WinInfo *wi_prev; ///< Previous entry or NULL for first entry.
+ Win *wi_win; ///< Pointer to window that did the wi_fpos.
...
};
@@ -787,15 +771,6 @@ getting used to, but it is important that all project contributors follow the
style rules so that they can all read and understand everyone's code easily.
-Line Length ~
-
-Each line of text in your code should be at most 100 characters long.
-
-Exception: if a comment line contains an example command or a literal URL
-longer than 100 characters, that line may be longer than 100 characters for ease
-of cut and paste.
-
-
Non-ASCII Characters ~
Non-ASCII characters should be rare, and must use UTF-8 formatting.
@@ -814,11 +789,6 @@ example, `"\uFEFF"`, is the Unicode zero-width no-break space character, which
would be invisible if included in the source as straight UTF-8.
-Spaces vs. Tabs ~
-
-Use only spaces, and indent 2 spaces at a time. Do not use tabs in your code.
-
-
Function Declarations and Definitions ~
Return type on the same line as function name, parameters on the same line if
@@ -928,7 +898,7 @@ no name, assume a zero-length name. >
Conditionals ~
-Don't use spaces inside parentheses. Always use curly braces. >
+Don't use spaces inside parentheses. >
if (condition) { // no spaces inside parentheses
... // 2 space indent.
@@ -938,19 +908,9 @@ Don't use spaces inside parentheses. Always use curly braces. >
...
}
-You must have a space between the `if` and the open parenthesis. You must also
-have a space between the close parenthesis and the curly brace, if you're
-using one. >
-
- if(condition) { // BAD: space missing after IF.
- if (condition){ // BAD: space missing before {.
- if (condition) { // GOOD: proper space after IF and before {.
-
-
Loops and Switch Statements ~
-Annotate non-trivial fall-through between cases. Empty loop bodies should use
-`{}` or `continue`.
+Annotate non-trivial fall-through between cases.
If not conditional on an enumerated value, switch statements should always
have a `default` case (in the case of an enumerated value, the compiler will
@@ -958,8 +918,8 @@ warn you if any values are not handled). If the default case should never
execute, simply `assert`: >
switch (var) {
- case 0: // 2 space indent
- ... // 4 space indent
+ case 0:
+ ...
break;
case 1:
...
@@ -968,16 +928,6 @@ execute, simply `assert`: >
assert(false);
}
-Empty loop bodies should use `{}` or `continue`, but not a single semicolon. >
-
- while (condition) {
- // Repeat test until it returns false.
- }
- for (int i = 0; i < kSomeNumber; i++) {} // GOOD: empty body.
- while (condition) continue; // GOOD: continue indicates no logic.
-
- while (condition); // BAD: looks like part of do/while loop.
-
Pointer Expressions ~
No spaces around period or arrow. Pointer operators do not have trailing
@@ -996,15 +946,6 @@ Note that:
- There are no spaces around the period or arrow when accessing a member.
- Pointer operators have no space after the * or &.
-When declaring a pointer variable or argument, place the asterisk adjacent to
-the variable name: >
-
- char *c;
-
- char * c; // BAD: spaces on both sides of *
- char* c; // BAD
-
-
Boolean Expressions ~
When you have a boolean expression that is longer than the standard line
@@ -1034,48 +975,12 @@ expr;`. >
return(result); // return is not a function!
-Preprocessor Directives ~
-
-The hash mark that starts a preprocessor directive should always be at the
-beginning of the line.
-
-Even when preprocessor directives are within the body of indented code, the
-directives should start at the beginning of the line.
-
-Nested directives should add one spaces after the hash mark for each level of
-indentation.
-
- // GOOD: directives at beginning of line >
- if (lopsided_score) {
- #if DISASTER_PENDING // Correct -- Starts at beginning of line
- drop_everything();
- # if NOTIFY // One space after #
- notify_client();
- # endif
- #endif
- BackToNormal();
- }
-
-< // BAD: indented directives >
- if (lopsided_score) {
- #if DISASTER_PENDING // Wrong! The "#if" should be at beginning of line
- drop_everything();
- #endif // Wrong! Do not indent "#endif"
- back_to_normal();
- }
-
-
Horizontal Whitespace ~
-Use of horizontal whitespace depends on location. Never put trailing
-whitespace at the end of a line.
+Use of horizontal whitespace depends on location.
General ~
>
- if (x) { // Open braces should always have a space before them.
- ...
- }
- int i = 0; // Semicolons usually have no space before them.
int x[] = { 0 }; // Spaces inside braces for braced-init-list.
<
@@ -1095,29 +1000,6 @@ whitespace at the end of a line.
};
<
- Macros ~
->
- #define FI(x) \ // Don't align \'s in macro definitions.
- foo(); \
- bar(); \
- ...
-<
-
- Loops and Conditionals ~
->
- if (b) { // Space after the keyword in condition.
- } else { // Spaces around else.
- }
- while (test) {} // There is usually no space inside parentheses.
- for (; i < 5; i++) { // For loops always have a space after the
- ... // semicolon and no a space before the
- ... // semicolon.
- }
- switch (i) {
- case 1: // No space before colon in a switch case.
- ...
- case 2: break; // Space after a colon if there's code after it.
-<
Operators ~
>
@@ -1127,8 +1009,6 @@ whitespace at the end of a line.
x++; // arguments.
if (x && !y)
...
- v = w*x + y/z; // Use spaces to indicate operator precedence.
- v = w * (x + z); // Parentheses should have no spaces inside them.
i = (int)d; // No spaces after a cast operator.
<
@@ -1136,12 +1016,6 @@ Vertical Whitespace ~
Minimize use of vertical whitespace.
-This is more a principle than a rule: don't use blank lines when you don't
-have to. In particular, don't put more than one or two blank lines between
-functions, resist starting functions with a blank line, don't end functions
-with a blank line, and be discriminating with your use of blank lines inside
-functions.
-
The basic principle is: The more code that fits on one screen, the easier it
is to follow and understand the control flow of the program. Of course,
readability can suffer from code being too dense as well as too spread out, so
diff --git a/runtime/doc/develop.txt b/runtime/doc/develop.txt
index 178b0dc62b..b31ac47bda 100644
--- a/runtime/doc/develop.txt
+++ b/runtime/doc/develop.txt
@@ -204,7 +204,7 @@ Docstring format:
- Use `<pre>` for code samples.
Example: the help for |vim.paste()| is generated from a docstring decorating
-vim.paste in src/nvim/lua/vim.lua like this: >
+vim.paste in runtime/lua/vim/_editor.lua like this: >
--- Paste handler, invoked by |nvim_paste()| when a conforming UI
--- (such as the |TUI|) pastes text into the editor.
@@ -243,12 +243,13 @@ If the function acts on an object then {thing} is the name of that object
with a {thing} that groups functions under a common concept).
Use existing common {action} names if possible:
- add Append to, or insert into, a collection
- del Delete a thing (or group of things)
- exec Execute code
- get Get a thing (or group of things by query)
- list Get all things
- set Set a thing (or group of things)
+ add Append to, or insert into, a collection
+ create Create a new thing
+ del Delete a thing (or group of things)
+ exec Execute code
+ get Get a thing (or group of things by query)
+ list Get all things
+ set Set a thing (or group of things)
Use consistent names for {thing} in all API functions. E.g. a buffer is called
"buf" everywhere, not "buffer" in some places and "buf" in others.
diff --git a/runtime/doc/diagnostic.txt b/runtime/doc/diagnostic.txt
index bb36fa46f6..2446506dec 100644
--- a/runtime/doc/diagnostic.txt
+++ b/runtime/doc/diagnostic.txt
@@ -39,16 +39,18 @@ modify the diagnostics for a buffer (e.g. |vim.diagnostic.set()|) then it
requires a namespace.
*diagnostic-structure*
-A diagnostic is a Lua table with the following keys:
+A diagnostic is a Lua table with the following keys. Required keys are
+indicated with (*):
bufnr: Buffer number
- lnum: The starting line of the diagnostic
+ lnum(*): The starting line of the diagnostic
end_lnum: The final line of the diagnostic
- col: The starting column of the diagnostic
+ col(*): The starting column of the diagnostic
end_col: The final column of the diagnostic
severity: The severity of the diagnostic |vim.diagnostic.severity|
- message: The diagnostic text
+ message(*): The diagnostic text
source: The source of the diagnostic
+ code: The diagnostic code
user_data: Arbitrary data plugins or users can add
Diagnostics use the same indexing as the rest of the Nvim API (i.e. 0-based
@@ -72,7 +74,7 @@ Functions that take a severity as an optional parameter (e.g.
2. A table with a "min" or "max" key (or both): >
- vim.diagnostic.get(0, { severity = {min=vim.diagnostic.severity.WARN})
+ vim.diagnostic.get(0, { severity = {min=vim.diagnostic.severity.WARN} })
The latter form allows users to specify a range of severities.
@@ -326,16 +328,17 @@ config({opts}, {namespace}) *vim.diagnostic.config()*
Note:
Each of the configuration options below accepts one of the
following:
- • `false` : Disable this feature
- • `true` : Enable this feature, use default settings.
- • `table` : Enable this feature with overrides. Use an
+ • `false`: Disable this feature
+ • `true`: Enable this feature, use default settings.
+ • `table`: Enable this feature with overrides. Use an
empty table to use default values.
- • `function` : Function with signature (namespace, bufnr)
+ • `function`: Function with signature (namespace, bufnr)
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
@@ -398,7 +401,7 @@ config({opts}, {namespace}) *vim.diagnostic.config()*
displayed before lower severities (e.g.
ERROR is displayed before WARN). Options:
• reverse: (boolean) Reverse sort order
- {namespace} number|nil Update the options for the given
+ {namespace} (number|nil) Update the options for the given
namespace. When omitted, update the global
diagnostic options.
@@ -406,28 +409,28 @@ disable({bufnr}, {namespace}) *vim.diagnostic.disable()*
Disable diagnostics in the given buffer.
Parameters: ~
- {bufnr} number|nil Buffer number, or 0 for current
+ {bufnr} (number|nil) Buffer number, or 0 for current
buffer. When omitted, disable diagnostics in
all buffers.
- {namespace} number|nil Only disable diagnostics for the
+ {namespace} (number|nil) Only disable diagnostics for the
given namespace.
enable({bufnr}, {namespace}) *vim.diagnostic.enable()*
Enable diagnostics in the given buffer.
Parameters: ~
- {bufnr} number|nil Buffer number, or 0 for current
+ {bufnr} (number|nil) Buffer number, or 0 for current
buffer. When omitted, enable diagnostics in
all buffers.
- {namespace} number|nil Only enable diagnostics for the
+ {namespace} (number|nil) Only enable diagnostics for the
given namespace.
fromqflist({list}) *vim.diagnostic.fromqflist()*
Convert a list of quickfix items to a list of diagnostics.
Parameters: ~
- {list} table A list of quickfix items from |getqflist()|
- or |getloclist()|.
+ {list} (table) A list of quickfix items from
+ |getqflist()| or |getloclist()|.
Return: ~
array of diagnostics |diagnostic-structure|
@@ -436,9 +439,10 @@ get({bufnr}, {opts}) *vim.diagnostic.get()*
Get current diagnostics.
Parameters: ~
- {bufnr} number|nil Buffer number to get diagnostics from.
- Use 0 for current buffer or nil for all buffers.
- {opts} table|nil A table with the following keys:
+ {bufnr} (number|nil) Buffer number to get diagnostics
+ from. Use 0 for current buffer or nil for all
+ buffers.
+ {opts} (table|nil) A table with the following keys:
• namespace: (number) Limit diagnostics to the
given namespace.
• lnum: (number) Limit diagnostics to the given
@@ -446,67 +450,68 @@ get({bufnr}, {opts}) *vim.diagnostic.get()*
• severity: See |diagnostic-severity|.
Return: ~
- table A list of diagnostic items |diagnostic-structure|.
+ (table) A list of diagnostic items |diagnostic-structure|.
get_namespace({namespace}) *vim.diagnostic.get_namespace()*
Get namespace metadata.
Parameters: ~
- {namespace} number Diagnostic namespace
+ {namespace} (number) Diagnostic namespace
Return: ~
- table Namespace metadata
+ (table) Namespace metadata
get_namespaces() *vim.diagnostic.get_namespaces()*
Get current diagnostic namespaces.
Return: ~
- table A list of active diagnostic namespaces
+ (table) A list of active diagnostic namespaces
|vim.diagnostic|.
get_next({opts}) *vim.diagnostic.get_next()*
Get the next diagnostic closest to the cursor position.
Parameters: ~
- {opts} table See |vim.diagnostic.goto_next()|
+ {opts} (table) See |vim.diagnostic.goto_next()|
Return: ~
- table Next diagnostic
+ (table) Next diagnostic
get_next_pos({opts}) *vim.diagnostic.get_next_pos()*
Return the position of the next diagnostic in the current
buffer.
Parameters: ~
- {opts} table See |vim.diagnostic.goto_next()|
+ {opts} (table) See |vim.diagnostic.goto_next()|
Return: ~
- table Next diagnostic position as a (row, col) tuple.
+ (table) Next diagnostic position as a (row, col) tuple.
get_prev({opts}) *vim.diagnostic.get_prev()*
Get the previous diagnostic closest to the cursor position.
Parameters: ~
- {opts} table See |vim.diagnostic.goto_next()|
+ {opts} (table) See |vim.diagnostic.goto_next()|
Return: ~
- table Previous diagnostic
+ (table) Previous diagnostic
get_prev_pos({opts}) *vim.diagnostic.get_prev_pos()*
Return the position of the previous diagnostic in the current
buffer.
Parameters: ~
- {opts} table See |vim.diagnostic.goto_next()|
+ {opts} (table) See |vim.diagnostic.goto_next()|
Return: ~
- table Previous diagnostic position as a (row, col) tuple.
+ (table) Previous diagnostic position as a (row, col)
+ tuple.
goto_next({opts}) *vim.diagnostic.goto_next()*
Move to the next diagnostic.
Parameters: ~
- {opts} table|nil Configuration table with the following
+ {opts} (table|nil) Configuration table with the following
keys:
• namespace: (number) Only consider diagnostics
from the given namespace.
@@ -530,7 +535,7 @@ goto_prev({opts}) *vim.diagnostic.goto_prev()*
Move to the previous diagnostic in the current buffer.
Parameters: ~
- {opts} table See |vim.diagnostic.goto_next()|
+ {opts} (table) See |vim.diagnostic.goto_next()|
hide({namespace}, {bufnr}) *vim.diagnostic.hide()*
Hide currently displayed diagnostics.
@@ -544,10 +549,10 @@ hide({namespace}, {bufnr}) *vim.diagnostic.hide()*
|vim.diagnostic.disable()|.
Parameters: ~
- {namespace} number|nil Diagnostic namespace. When
+ {namespace} (number|nil) Diagnostic namespace. When
omitted, hide diagnostics from all
namespaces.
- {bufnr} number|nil Buffer number, or 0 for current
+ {bufnr} (number|nil) Buffer number, or 0 for current
buffer. When omitted, hide diagnostics in all
buffers.
@@ -570,16 +575,16 @@ match({str}, {pat}, {groups}, {severity_map}, {defaults})
<
Parameters: ~
- {str} string String to parse diagnostics from.
- {pat} string Lua pattern with capture groups.
- {groups} table List of fields in a
+ {str} (string) String to parse diagnostics from.
+ {pat} (string) Lua pattern with capture groups.
+ {groups} (table) List of fields in a
|diagnostic-structure| to associate with
captures from {pat}.
- {severity_map} table A table mapping the severity field
+ {severity_map} (table) A table mapping the severity field
from {groups} with an item from
|vim.diagnostic.severity|.
- {defaults} table|nil Table of default values for any
- fields not listed in {groups}. When
+ {defaults} (table|nil) Table of default values for
+ any fields not listed in {groups}. When
omitted, numeric values default to 0 and
"severity" defaults to ERROR.
@@ -591,7 +596,7 @@ open_float({opts}, {...}) *vim.diagnostic.open_float()*
Show diagnostics in a floating window.
Parameters: ~
- {opts} table|nil Configuration table with the same keys
+ {opts} (table|nil) Configuration table with the same keys
as |vim.lsp.util.open_floating_preview()| in
addition to the following:
• bufnr: (number) Buffer number to show
@@ -662,10 +667,10 @@ reset({namespace}, {bufnr}) *vim.diagnostic.reset()*
|vim.diagnostic.hide()|.
Parameters: ~
- {namespace} number|nil Diagnostic namespace. When
+ {namespace} (number|nil) Diagnostic namespace. When
omitted, remove diagnostics from all
namespaces.
- {bufnr} number|nil Remove diagnostics for the given
+ {bufnr} (number|nil) Remove diagnostics for the given
buffer. When omitted, diagnostics are removed
for all buffers.
@@ -673,18 +678,18 @@ set({namespace}, {bufnr}, {diagnostics}, {opts}) *vim.diagnostic.set()*
Set diagnostics for the given namespace and buffer.
Parameters: ~
- {namespace} number The diagnostic namespace
- {bufnr} number Buffer number
- {diagnostics} table A list of diagnostic items
+ {namespace} (number) The diagnostic namespace
+ {bufnr} (number) Buffer number
+ {diagnostics} (table) A list of diagnostic items
|diagnostic-structure|
- {opts} table|nil Display options to pass to
+ {opts} (table|nil) Display options to pass to
|vim.diagnostic.show()|
setloclist({opts}) *vim.diagnostic.setloclist()*
Add buffer diagnostics to the location list.
Parameters: ~
- {opts} table|nil Configuration table with the following
+ {opts} (table|nil) Configuration table with the following
keys:
• namespace: (number) Only add diagnostics from
the given namespace.
@@ -700,7 +705,7 @@ setqflist({opts}) *vim.diagnostic.setqflist()*
Add all diagnostics to the quickfix list.
Parameters: ~
- {opts} table|nil Configuration table with the following
+ {opts} (table|nil) Configuration table with the following
keys:
• namespace: (number) Only add diagnostics from
the given namespace.
@@ -715,20 +720,20 @@ show({namespace}, {bufnr}, {diagnostics}, {opts})
Display diagnostics for the given namespace and buffer.
Parameters: ~
- {namespace} number|nil Diagnostic namespace. When
+ {namespace} (number|nil) Diagnostic namespace. When
omitted, show diagnostics from all
namespaces.
- {bufnr} number|nil Buffer number, or 0 for current
- buffer. When omitted, show diagnostics in
- all buffers.
- {diagnostics} table|nil The diagnostics to display. When
- omitted, use the saved diagnostics for the
- given namespace and buffer. This can be
+ {bufnr} (number|nil) Buffer number, or 0 for
+ current buffer. When omitted, show
+ diagnostics in all buffers.
+ {diagnostics} (table|nil) The diagnostics to display.
+ When omitted, use the saved diagnostics for
+ the given namespace and buffer. This can be
used to display a list of diagnostics
without saving them or to display only a
subset of diagnostics. May not be used when
{namespace} or {bufnr} is nil.
- {opts} table|nil Display options. See
+ {opts} (table|nil) Display options. See
|vim.diagnostic.config()|.
toqflist({diagnostics}) *vim.diagnostic.toqflist()*
@@ -736,7 +741,7 @@ toqflist({diagnostics}) *vim.diagnostic.toqflist()*
can be passed to |setqflist()| or |setloclist()|.
Parameters: ~
- {diagnostics} table List of diagnostics
+ {diagnostics} (table) List of diagnostics
|diagnostic-structure|.
Return: ~
diff --git a/runtime/doc/diff.txt b/runtime/doc/diff.txt
index 6115a5d235..9c5792dd43 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
@@ -370,13 +371,13 @@ Example (this does almost the same as 'diffexpr' being empty): >
function MyDiff()
let opt = ""
if &diffopt =~ "icase"
- let opt = opt . "-i "
+ let opt = opt .. "-i "
endif
if &diffopt =~ "iwhite"
- let opt = opt . "-b "
+ let opt = opt .. "-b "
endif
- silent execute "!diff -a --binary " . opt . v:fname_in . " " . v:fname_new .
- \ " > " . v:fname_out
+ silent execute "!diff -a --binary " .. opt .. v:fname_in .. " " .. v:fname_new ..
+ \ " > " .. v:fname_out
redraw!
endfunction
@@ -426,8 +427,8 @@ Example (this does the same as 'patchexpr' being empty): >
set patchexpr=MyPatch()
function MyPatch()
- :call system("patch -o " . v:fname_out . " " . v:fname_in .
- \ " < " . v:fname_diff)
+ :call system("patch -o " .. v:fname_out .. " " .. v:fname_in ..
+ \ " < " .. v:fname_diff)
endfunction
Make sure that using the "patch" program doesn't have unwanted side effects.
diff --git a/runtime/doc/digraph.txt b/runtime/doc/digraph.txt
index dd7e9a331a..eb3de0111f 100644
--- a/runtime/doc/digraph.txt
+++ b/runtime/doc/digraph.txt
@@ -35,6 +35,9 @@ An alternative is using the 'keymap' option.
< Avoid defining a digraph with '_' (underscore) as the
first character, it has a special meaning in the
future.
+ NOTE: This command cannot add a digraph that starts
+ with a white space. If you want to add such digraph,
+ you can use |digraph_set()| instead.
Example of the output of ":digraphs": >
TH Þ 222 ss ß 223 a! à 224 a' á 225 a> â 226 a? ã 227 a: ä 228
diff --git a/runtime/doc/editing.txt b/runtime/doc/editing.txt
index 14df41e6c8..c19d9f482b 100644
--- a/runtime/doc/editing.txt
+++ b/runtime/doc/editing.txt
@@ -196,7 +196,7 @@ If you want to keep the changed buffer without saving it, switch on the
Edit {file} always. Discard any changes to the
current buffer.
Also see |++opt| and |+cmd|.
-
+ *:edit_#* *:e#*
:e[dit] [++opt] [+cmd] #[count]
Edit the [count]th buffer (as shown by |:files|).
This command does the same as [count] CTRL-^. But ":e
@@ -356,7 +356,7 @@ as a wildcard when "[" is in the 'isfname' option. A simple way to avoid this
is to use "path\[[]abc]", this matches the file "path\[abc]".
*starstar-wildcard*
-Expanding "**" is possible on Unix, Win32, Mac OS/X and a few other systems.
+Expanding "**" is possible on Unix, Win32, macOS and a few other systems.
This allows searching a directory tree. This goes up to 100 directories deep.
Note there are some commands where this works slightly differently, see
|file-searching|.
@@ -411,9 +411,10 @@ does apply like to other wildcards.
Environment variables in the expression are expanded when evaluating the
expression, thus this works: >
- :e `=$HOME . '/.vimrc'`
-This does not work, $HOME is inside a string and used literally: >
- :e `='$HOME' . '/.vimrc'`
+ :e `=$HOME .. '/.vimrc'`
+This uses $HOME inside a string and it will be used literally, most likely not
+what you intended: >
+ :e `='$HOME' .. '/.vimrc'`
If the expression returns a string then names are to be separated with line
breaks. When the result is a |List| then each item is used as a name. Line
@@ -845,7 +846,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
@@ -1172,7 +1173,7 @@ Examples: >
If you want to always use ":confirm", set the 'confirm' option.
- *:browse* *:bro* *E338* *E614* *E615* *E616*
+ *:browse* *:bro* *E338* *E614* *E615* *E616*
:bro[wse] {command} Open a file selection dialog for an argument to
{command}. At present this works for |:e|, |:w|,
|:wall|, |:wq|, |:wqall|, |:x|, |:xall|, |:exit|,
@@ -1331,6 +1332,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.
@@ -1449,6 +1451,11 @@ If you don't get warned often enough you can use the following command.
if it exists now.
Once a file has been checked the timestamp is reset,
you will not be warned again.
+ Syntax highlighting, marks, diff status,
+ 'fileencoding', 'fileformat' and 'binary' options
+ are not changed. See |v:fcs_choice| to reload these
+ too (for example, if a code formatting tools has
+ changed the file).
:[N]checkt[ime] {filename}
:[N]checkt[ime] [N]
@@ -1488,8 +1495,8 @@ session or with another command (e.g., a filter command). Then you will know
which version of the file you want to keep.
The accuracy of the time check depends on the filesystem. On Unix it is
-usually sub-second. With old file sytems and on MS-Windows it is normally one
-second. Use has('nanotime') check if sub-second time stamp checks are
+usually sub-second. With old file systems and on MS-Windows it is normally one
+second. Use `has('nanotime')` to check if sub-second time stamp checks are
available.
There is one situation where you get the message while there is nothing wrong:
@@ -1568,6 +1575,10 @@ There are three different types of searching:
/u/user_x/work/include
/u/user_x/include
+< Note: If your 'path' setting includes a non-existing directory, Vim will
+ skip the non-existing directory, and also does not search in the parent of
+ the non-existing directory if upwards searching is used.
+
3) Combined up/downward search:
If Vim's current path is /u/user_x/work/release and you do >
set path=**;/u/user_x
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index fa75ead9a3..17af40bdb9 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -119,7 +119,7 @@ You will not get an error if you try to change the type of a variable.
1.2 Function references ~
- *Funcref* *E695* *E718*
+ *Funcref* *E695* *E718*
A Funcref variable is obtained with the |function()| function, the |funcref()|
function or created with the lambda expression |expr-lambda|. It can be used
in an expression in the place of a function name, before the parenthesis
@@ -196,7 +196,7 @@ position in the sequence.
List creation ~
*E696* *E697*
-A List is created with a comma separated list of items in square brackets.
+A List is created with a comma-separated list of items in square brackets.
Examples: >
:let mylist = [1, two, 3, "four"]
:let emptylist = []
@@ -372,8 +372,8 @@ Changing the order of items in a list: >
For loop ~
-The |:for| loop executes commands for each item in a |List| or |Blob|.
-A variable is set to each item in the sequence. Example with a List: >
+The |:for| loop executes commands for each item in a |List|, |String| or |Blob|.
+A variable is set to each item in sequence. Example with a List: >
:for item in mylist
: call Doit(item)
:endfor
@@ -390,7 +390,7 @@ If all you want to do is modify each item in the list then the |map()|
function will be a simpler method than a for loop.
Just like the |:let| command, |:for| also accepts a list of variables. This
-requires the argument to be a list of lists. >
+requires the argument to be a List of Lists. >
:for [lnum, col] in [[1, 3], [2, 8], [3, 0]]
: call Doit(lnum, col)
:endfor
@@ -402,12 +402,18 @@ It is also possible to put remaining items in a List variable: >
:for [i, j; rest] in listlist
: call Doit(i, j)
: if !empty(rest)
- : echo "remainder: " . string(rest)
+ : echo "remainder: " .. string(rest)
: endif
:endfor
For a Blob one byte at a time is used.
+For a String one character, including any composing characters, is used as a
+String. Example: >
+ for c in text
+ echo 'This character is ' .. c
+ endfor
+
List functions ~
*E714*
@@ -424,11 +430,11 @@ Functions that are useful with a List: >
:let list = split("a b c") " create list from items in a string
:let string = join(list, ', ') " create string from list items
:let s = string(list) " String representation of list
- :call map(list, '">> " . v:val') " prepend ">> " to each item
+ :call map(list, '">> " .. v:val') " prepend ">> " to each item
Don't forget that a combination of features can make things simple. For
example, to add up all the numbers in a list: >
- :exe 'let sum = ' . join(nrlist, '+')
+ :exe 'let sum = ' .. join(nrlist, '+')
1.4 Dictionaries ~
@@ -440,7 +446,7 @@ ordering.
Dictionary creation ~
*E720* *E721* *E722* *E723*
-A Dictionary is created with a comma separated list of entries in curly
+A Dictionary is created with a comma-separated list of entries in curly
braces. Each entry has a key and a value, separated by a colon. Each key can
only appear once. Examples: >
:let mydict = {1: 'one', 2: 'two', 3: 'three'}
@@ -449,8 +455,8 @@ only appear once. Examples: >
A key is always a String. You can use a Number, it will be converted to a
String automatically. Thus the String '4' and the number 4 will find the same
entry. Note that the String '04' and the Number 04 are different, since the
-Number will be converted to the String '4'. The empty string can also be used
-as a key.
+Number will be converted to the String '4', leading zeros are dropped. The
+empty string can also be used as a key.
*literal-Dict*
To avoid having to put quotes around every key the #{} form can be used. This
does require the key to consist only of ASCII letters, digits, '-' and '_'.
@@ -490,7 +496,7 @@ turn the Dictionary into a List and pass it to |:for|.
Most often you want to loop over the keys, using the |keys()| function: >
:for key in keys(mydict)
- : echo key . ': ' . mydict[key]
+ : echo key .. ': ' .. mydict[key]
:endfor
The List of keys is unsorted. You may want to sort them first: >
@@ -498,13 +504,13 @@ The List of keys is unsorted. You may want to sort them first: >
To loop over the values use the |values()| function: >
:for v in values(mydict)
- : echo "value: " . v
+ : echo "value: " .. v
:endfor
If you want both the key and the value use the |items()| function. It returns
a List in which each item is a List with two items, the key and the value: >
:for [key, value] in items(mydict)
- : echo key . ': ' . value
+ : echo key .. ': ' .. value
:endfor
@@ -599,7 +605,7 @@ Functions that can be used with a Dictionary: >
:let small = min(dict) " minimum value in dict
:let xs = count(dict, 'x') " count nr of times 'x' appears in dict
:let s = string(dict) " String representation of dict
- :call map(dict, '">> " . v:val') " prepend ">> " to each item
+ :call map(dict, '">> " .. v:val') " prepend ">> " to each item
1.5 Blobs ~
@@ -676,7 +682,7 @@ similar to -1. >
:let otherblob = myblob[:] " make a copy of the Blob
If the first index is beyond the last byte of the Blob or the second byte is
-before the first byte, the result is an empty Blob. There is no error
+before the first index, the result is an empty Blob. There is no error
message.
If the second index is equal to or greater than the length of the Blob the
@@ -834,7 +840,7 @@ Example: >
All expressions within one level are parsed from left to right.
-expr1 *expr1* *trinary* *E109*
+expr1 *expr1* *ternary* *E109*
-----
expr2 ? expr1 : expr1
@@ -1066,7 +1072,7 @@ expr7 *expr7*
For '!' |TRUE| becomes |FALSE|, |FALSE| becomes |TRUE| (one).
For '-' the sign of the number is changed.
-For '+' the number is unchanged.
+For '+' the number is unchanged. Note: "++" has no effect.
A String will be converted to a Number first.
@@ -1117,7 +1123,7 @@ Generally, if a |List| index is equal to or higher than the length of the
error.
-expr8[expr1a : expr1b] substring or sublist *expr-[:]*
+expr8[expr1a : expr1b] substring or |sublist| *expr-[:]* *substring*
If expr8 is a String this results in the substring with the bytes or
characters from expr1a to and including expr1b. expr8 is used as a String,
@@ -1228,8 +1234,8 @@ And NOT: >
number
------
number number constant *expr-number*
- *hex-number* *octal-number* *binary-number*
+ *0x* *hex-number* *0o* *octal-number* *binary-number*
Decimal, Hexadecimal (starting with 0x or 0X), Binary (starting with 0b or 0B)
and Octal (starting with 0, 0o or 0O).
@@ -1319,6 +1325,9 @@ A string constant accepts these special characters:
To use the double quote character it must be escaped: "<M-\">".
Don't use <Char-xxxx> to get a UTF-8 character, use \uxxxx as
mentioned above.
+\<*xxx> Like \<xxx> but prepends a modifier instead of including it in the
+ character. E.g. "\<C-w>" is one character 0x17 while "\<*C-w>" is four
+ bytes: 3 for the CTRL modifier and then character "W".
Note that "\xff" is stored as the byte 255, which may be invalid in some
encodings. Use "\u00ff" to store character 255 correctly as UTF-8.
@@ -1356,8 +1365,8 @@ option *expr-option* *E112* *E113*
&l:option local option value
Examples: >
- echo "tabstop is " . &tabstop
- if &insertmode
+ echo "tabstop is " .. &tabstop
+ if &expandtab
Any option name can be used here. See |options|. When using the local value
and there is no buffer-local or window-local value, the global value is used
@@ -1451,7 +1460,7 @@ the function returns: >
:let Bar = Foo(4)
:echo Bar(6)
< 5
-Note that the variables must exist in the outer scope before the lamba is
+Note that the variables must exist in the outer scope before the lambda is
defined for this to work. See also |:func-closure|.
Lambda and closure support can be checked with: >
@@ -1486,7 +1495,7 @@ Notice how execute() is used to execute an Ex command. That's ugly though.
Lambda expressions have internal names like '<lambda>42'. If you get an error
for a lambda expression, you can find what it is with the following command: >
- :function {'<lambda>42'}
+ :function <lambda>42
See also: |numbered-function|
==============================================================================
@@ -1631,7 +1640,7 @@ maintain a counter: >
echo "script executed for the first time"
else
let s:counter = s:counter + 1
- echo "script executed " . s:counter . " times now"
+ echo "script executed " .. s:counter .. " times now"
endif
Note that this means that filetype plugins don't get a different set of script
@@ -1645,6 +1654,7 @@ Some variables can be set by the user, but the type cannot be changed.
*v:argv* *argv-variable*
v:argv The command line arguments Vim was invoked with. This is a
list of strings. The first item is the Vim command.
+ See |v:progpath| for the command with full path.
*v:beval_col* *beval_col-variable*
v:beval_col The number of the column, over which the mouse pointer is.
@@ -1730,7 +1740,7 @@ v:completed_item
*v:count* *count-variable*
v:count The count given for the last Normal mode command. Can be used
to get the count before a mapping. Read-only. Example: >
- :map _x :<C-U>echo "the count is " . v:count<CR>
+ :map _x :<C-U>echo "the count is " .. v:count<CR>
< Note: The <C-U> is required to remove the line range that you
get when typing ':' after a count.
When there are two counts, as in "3d2w", they are multiplied,
@@ -1876,6 +1886,11 @@ v:fcs_choice What should happen after a |FileChangedShell| event was
do with the affected buffer:
reload Reload the buffer (does not work if
the file was deleted).
+ edit Reload the buffer and detect the
+ values for options such as
+ 'fileformat', 'fileencoding', 'binary'
+ (does not work if the file was
+ deleted).
ask Ask the user what to do, as if there
was no autocommand. Except that when
only the timestamp changed nothing
@@ -2016,6 +2031,9 @@ v:null Special value used to put "null" in JSON and NIL in msgpack.
used as a String (e.g. in |expr5| with string concatenation
operator) and to zero when used as a Number (e.g. in |expr5|
or |expr7| when used with numeric operators). Read-only.
+ In some places `v:null` can be used for a List, Dict, etc.
+ that is not set. That is slightly different than an empty
+ List, Dict, etc.
*v:numbermax* *numbermax-variable*
v:numbermax Maximum value of a number.
@@ -2120,9 +2138,19 @@ v:scrollstart String describing the script or function that caused the
*v:servername* *servername-variable*
v:servername Primary listen-address of the current Nvim instance, the first
item returned by |serverlist()|. Can be set by |--listen| or
- |$NVIM_LISTEN_ADDRESS| at startup. |serverstart()| |serverstop()|
+ |$NVIM_LISTEN_ADDRESS| (deprecated) at startup.
+ See also |serverstart()| |serverstop()|.
Read-only.
+ *$NVIM*
+ $NVIM is set by |terminal| and |jobstart()|, and is thus
+ a hint that the current environment is a subprocess of Nvim.
+ Example: >
+ if $NVIM
+ echo nvim_get_chan_info(v:parent)
+ endif
+
+< Note the contents of $NVIM may change in the future.
v:searchforward *v:searchforward* *searchforward-variable*
Search direction: 1 after a forward search, 0 after a
@@ -2267,8656 +2295,13 @@ v:windowid Application-specific window "handle" which may be set by any
==============================================================================
4. Builtin Functions *vim-function* *functions*
-The Vimscript subsystem (referred to as "eval" internally) provides the
-following builtin functions. Scripts can also define |user-function|s.
+The Vimscript subsystem (referred to as "eval" internally) provides builtin
+functions. Scripts can also define |user-function|s.
See |function-list| to browse functions by topic.
-(Use CTRL-] on the function name to jump to the full explanation.)
-
-USAGE RESULT DESCRIPTION ~
-
-abs({expr}) Float or Number absolute value of {expr}
-acos({expr}) Float arc cosine of {expr}
-add({object}, {item}) List/Blob append {item} to {object}
-and({expr}, {expr}) Number bitwise AND
-api_info() Dict api metadata
-append({lnum}, {string}) Number append {string} below line {lnum}
-append({lnum}, {list}) Number append lines {list} below line {lnum}
-argc([{winid}]) Number number of files in the argument list
-argidx() Number current index in the argument list
-arglistid([{winnr} [, {tabnr}]]) Number argument list id
-argv({nr} [, {winid}]) String {nr} entry of the argument list
-argv([-1, {winid}]) List the argument list
-asin({expr}) Float arc sine of {expr}
-assert_beeps({cmd}) Number assert {cmd} causes a beep
-assert_equal({exp}, {act} [, {msg}])
- Number assert {exp} is equal to {act}
-assert_equalfile({fname-one}, {fname-two} [, {msg}])
- Number assert file contents are equal
-assert_exception({error} [, {msg}])
- Number assert {error} is in v:exception
-assert_fails({cmd} [, {error}]) Number assert {cmd} fails
-assert_false({actual} [, {msg}])
- Number assert {actual} is false
-assert_inrange({lower}, {upper}, {actual} [, {msg}])
- Number assert {actual} is inside the range
-assert_match({pat}, {text} [, {msg}])
- Number assert {pat} matches {text}
-assert_nobeep({cmd}) Number assert {cmd} does not cause a beep
-assert_notequal({exp}, {act} [, {msg}])
- Number assert {exp} is not equal {act}
-assert_notmatch({pat}, {text} [, {msg}])
- Number assert {pat} not matches {text}
-assert_report({msg}) Number report a test failure
-assert_true({actual} [, {msg}]) Number assert {actual} is true
-atan({expr}) Float arc tangent of {expr}
-atan2({expr}, {expr}) Float arc tangent of {expr1} / {expr2}
-browse({save}, {title}, {initdir}, {default})
- String put up a file requester
-browsedir({title}, {initdir}) String put up a directory requester
-bufadd({name}) Number add a buffer to the buffer list
-bufexists({expr}) Number |TRUE| if buffer {expr} exists
-buflisted({expr}) Number |TRUE| if buffer {expr} is listed
-bufload({expr}) Number load buffer {expr} if not loaded yet
-bufloaded({expr}) Number |TRUE| if buffer {expr} is loaded
-bufname([{expr}]) String Name of the buffer {expr}
-bufnr([{expr} [, {create}]]) Number Number of the buffer {expr}
-bufwinid({expr}) Number |window-ID| of buffer {expr}
-bufwinnr({expr}) Number window number of buffer {expr}
-byte2line({byte}) Number line number at byte count {byte}
-byteidx({expr}, {nr}) Number byte index of {nr}'th char in {expr}
-byteidxcomp({expr}, {nr}) Number byte index of {nr}'th char in {expr}
-call({func}, {arglist} [, {dict}])
- any call {func} with arguments {arglist}
-ceil({expr}) Float round {expr} up
-changenr() Number current change number
-chanclose({id}[, {stream}]) Number Closes a channel or one of its streams
-chansend({id}, {data}) Number Writes {data} to channel
-char2nr({expr}[, {utf8}]) Number ASCII/UTF-8 value of first char in {expr}
-charidx({string}, {idx} [, {countcc}])
- Number char index of byte {idx} in {string}
-chdir({dir}) String change current working directory
-cindent({lnum}) Number C indent for line {lnum}
-clearmatches([{win}]) none clear all matches
-col({expr}) Number column nr of cursor or mark
-complete({startcol}, {matches}) none set Insert mode completion
-complete_add({expr}) Number add completion match
-complete_check() Number check for key typed during completion
-complete_info([{what}]) Dict get current completion information
-confirm({msg} [, {choices} [, {default} [, {type}]]])
- Number number of choice picked by user
-copy({expr}) any make a shallow copy of {expr}
-cos({expr}) Float cosine of {expr}
-cosh({expr}) Float hyperbolic cosine of {expr}
-count({list}, {expr} [, {ic} [, {start}]])
- Number count how many {expr} are in {list}
-cscope_connection([{num}, {dbpath} [, {prepend}]])
- Number checks existence of cscope connection
-ctxget([{index}]) Dict return the |context| dict at {index}
-ctxpop() none pop and restore |context| from the
- |context-stack|
-ctxpush([{types}]) none push the current |context| to the
- |context-stack|
-ctxset({context}[, {index}]) none set |context| at {index}
-ctxsize() Number return |context-stack| size
-cursor({lnum}, {col} [, {off}])
- Number move cursor to {lnum}, {col}, {off}
-cursor({list}) Number move cursor to position in {list}
-debugbreak({pid}) Number interrupt process being debugged
-deepcopy({expr} [, {noref}]) any make a full copy of {expr}
-delete({fname} [, {flags}]) Number delete the file or directory {fname}
-deletebufline({buf}, {first}[, {last}])
- Number delete lines from buffer {buf}
-dictwatcheradd({dict}, {pattern}, {callback})
- Start watching a dictionary
-dictwatcherdel({dict}, {pattern}, {callback})
- Stop watching a dictionary
-did_filetype() Number |TRUE| if FileType autocommand event used
-diff_filler({lnum}) Number diff filler lines about {lnum}
-diff_hlID({lnum}, {col}) Number diff highlighting at {lnum}/{col}
-empty({expr}) Number |TRUE| if {expr} is empty
-environ() Dict return environment variables
-escape({string}, {chars}) String escape {chars} in {string} with '\'
-eval({string}) any evaluate {string} into its value
-eventhandler() Number |TRUE| if inside an event handler
-executable({expr}) Number 1 if executable {expr} exists
-execute({command}) String execute and capture output of {command}
-exepath({expr}) String full path of the command {expr}
-exists({expr}) Number |TRUE| if {expr} exists
-extend({expr1}, {expr2} [, {expr3}])
- List/Dict insert items of {expr2} into {expr1}
-exp({expr}) Float exponential of {expr}
-expand({expr} [, {nosuf} [, {list}]])
- any expand special keywords in {expr}
-expandcmd({expr}) String expand {expr} like with `:edit`
-feedkeys({string} [, {mode}]) Number add key sequence to typeahead buffer
-filereadable({file}) Number |TRUE| if {file} is a readable file
-filewritable({file}) Number |TRUE| if {file} is a writable file
-filter({expr1}, {expr2}) List/Dict remove items from {expr1} where
- {expr2} is 0
-finddir({name} [, {path} [, {count}]])
- String find directory {name} in {path}
-findfile({name} [, {path} [, {count}]])
- String find file {name} in {path}
-flatten({list} [, {maxdepth}]) List flatten {list} up to {maxdepth} levels
-float2nr({expr}) Number convert Float {expr} to a Number
-floor({expr}) Float round {expr} down
-fmod({expr1}, {expr2}) Float remainder of {expr1} / {expr2}
-fnameescape({fname}) String escape special characters in {fname}
-fnamemodify({fname}, {mods}) String modify file name
-foldclosed({lnum}) Number first line of fold at {lnum} if closed
-foldclosedend({lnum}) Number last line of fold at {lnum} if closed
-foldlevel({lnum}) Number fold level at {lnum}
-foldtext() String line displayed for closed fold
-foldtextresult({lnum}) String text for closed fold at {lnum}
-foreground() Number bring the Vim window to the foreground
-fullcommand({name}) String get full command from {name}
-funcref({name} [, {arglist}] [, {dict}])
- Funcref reference to function {name}
-function({name} [, {arglist}] [, {dict}])
- Funcref named reference to function {name}
-garbagecollect([{atexit}]) none free memory, breaking cyclic references
-get({list}, {idx} [, {def}]) any get item {idx} from {list} or {def}
-get({dict}, {key} [, {def}]) any get item {key} from {dict} or {def}
-get({func}, {what}) any get property of funcref/partial {func}
-getbufinfo([{buf}]) List information about buffers
-getbufline({buf}, {lnum} [, {end}])
- List lines {lnum} to {end} of buffer {buf}
-getbufvar({buf}, {varname} [, {def}])
- any variable {varname} in buffer {buf}
-getchangelist([{buf}]) List list of change list items
-getchar([expr]) Number or String
- get one character from the user
-getcharmod() Number modifiers for the last typed character
-getcharsearch() Dict last character search
-getcharstr([expr]) String get one character from the user
-getcmdline() String return the current command-line
-getcmdpos() Number return cursor position in command-line
-getcmdtype() String return current command-line type
-getcmdwintype() String return current command-line window type
-getcompletion({pat}, {type} [, {filtered}])
- List list of cmdline completion matches
-getcurpos() List position of the cursor
-getcwd([{winnr} [, {tabnr}]]) String get the current working directory
-getenv({name}) String return environment variable
-getfontname([{name}]) String name of font being used
-getfperm({fname}) String file permissions of file {fname}
-getfsize({fname}) Number size in bytes of file {fname}
-getftime({fname}) Number last modification time of file
-getftype({fname}) String description of type of file {fname}
-getjumplist([{winnr} [, {tabnr}]])
- List list of jump list items
-getline({lnum}) String line {lnum} of current buffer
-getline({lnum}, {end}) List lines {lnum} to {end} of current buffer
-getloclist({nr}) List list of location list items
-getloclist({nr}, {what}) Dict get specific location list properties
-getmarklist([{buf}]) List list of global/local marks
-getmatches([{win}]) List list of current matches
-getmousepos() Dict last known mouse position
-getpid() Number process ID of Vim
-getpos({expr}) List position of cursor, mark, etc.
-getqflist() List list of quickfix items
-getqflist({what}) Dict get specific quickfix list properties
-getreg([{regname} [, 1 [, {list}]]])
- String or List contents of a register
-getreginfo([{regname}]) Dict information about a register
-getregtype([{regname}]) String type of a register
-gettabinfo([{expr}]) List list of tab pages
-gettabvar({nr}, {varname} [, {def}])
- any variable {varname} in tab {nr} or {def}
-gettabwinvar({tabnr}, {winnr}, {name} [, {def}])
- any {name} in {winnr} in tab page {tabnr}
-gettagstack([{nr}]) Dict get the tag stack of window {nr}
-getwininfo([{winid}]) List list of windows
-getwinpos([{timeout}]) List X and Y coord in pixels of the Vim window
-getwinposx() Number X coord in pixels of Vim window
-getwinposy() Number Y coord in pixels of Vim window
-getwinvar({nr}, {varname} [, {def}])
- any variable {varname} in window {nr}
-glob({expr} [, {nosuf} [, {list} [, {alllinks}]]])
- any expand file wildcards in {expr}
-glob2regpat({expr}) String convert a glob pat into a search pat
-globpath({path}, {expr} [, {nosuf} [, {list} [, {alllinks}]]])
- String do glob({expr}) for all dirs in {path}
-has({feature}) Number |TRUE| if feature {feature} supported
-has_key({dict}, {key}) Number |TRUE| if {dict} has entry {key}
-haslocaldir([{winnr} [, {tabnr}]])
- Number |TRUE| if the window executed |:lcd| or
- the tab executed |:tcd|
-hasmapto({what} [, {mode} [, {abbr}]])
- Number |TRUE| if mapping to {what} exists
-histadd({history}, {item}) String add an item to a history
-histdel({history} [, {item}]) String remove an item from a history
-histget({history} [, {index}]) String get the item {index} from a history
-histnr({history}) Number highest index of a history
-hlexists({name}) Number |TRUE| if highlight group {name} exists
-hlID({name}) Number syntax ID of highlight group {name}
-hostname() String name of the machine Vim is running on
-iconv({expr}, {from}, {to}) String convert encoding of {expr}
-indent({lnum}) Number indent of line {lnum}
-index({object}, {expr} [, {start} [, {ic}]])
- Number index in {object} where {expr} appears
-input({prompt} [, {text} [, {completion}]])
- String get input from the user
-inputlist({textlist}) Number let the user pick from a choice list
-inputrestore() Number restore typeahead
-inputsave() Number save and clear typeahead
-inputsecret({prompt} [, {text}])
- String like input() but hiding the text
-insert({object}, {item} [, {idx}])
- List insert {item} in {object} [before {idx}]
-interrupt() none interrupt script execution
-invert({expr}) Number bitwise invert
-isdirectory({directory}) Number |TRUE| if {directory} is a directory
-isinf({expr}) Number determine if {expr} is infinity value
- (positive or negative)
-islocked({expr}) Number |TRUE| if {expr} is locked
-isnan({expr}) Number |TRUE| if {expr} is NaN
-id({expr}) String identifier of the container
-items({dict}) List key-value pairs in {dict}
-jobpid({id}) Number Returns pid of a job.
-jobresize({id}, {width}, {height})
- Number Resize pseudo terminal window of a job
-jobstart({cmd}[, {opts}]) Number Spawns {cmd} as a job
-jobstop({id}) Number Stops a job
-jobwait({ids}[, {timeout}]) Number Wait for a set of jobs
-join({list} [, {sep}]) String join {list} items into one String
-json_decode({expr}) any Convert {expr} from JSON
-json_encode({expr}) String Convert {expr} to JSON
-keys({dict}) List keys in {dict}
-len({expr}) Number the length of {expr}
-libcall({lib}, {func}, {arg}) String call {func} in library {lib} with {arg}
-libcallnr({lib}, {func}, {arg}) Number idem, but return a Number
-line({expr} [, {winid}]) Number line nr of cursor, last line or mark
-line2byte({lnum}) Number byte count of line {lnum}
-lispindent({lnum}) Number Lisp indent for line {lnum}
-list2str({list} [, {utf8}]) String turn numbers in {list} into a String
-localtime() Number current time
-log({expr}) Float natural logarithm (base e) of {expr}
-log10({expr}) Float logarithm of Float {expr} to base 10
-luaeval({expr}[, {expr}]) any evaluate Lua expression
-map({expr1}, {expr2}) List/Dict change each item in {expr1} to {expr}
-maparg({name}[, {mode} [, {abbr} [, {dict}]]])
- String or Dict
- rhs of mapping {name} in mode {mode}
-mapcheck({name}[, {mode} [, {abbr}]])
- String check for mappings matching {name}
-match({expr}, {pat}[, {start}[, {count}]])
- Number position where {pat} matches in {expr}
-matchadd({group}, {pattern}[, {priority}[, {id}]])
- Number highlight {pattern} with {group}
-matchaddpos({group}, {list}[, {priority}[, {id}]])
- Number highlight positions with {group}
-matcharg({nr}) List arguments of |:match|
-matchdelete({id} [, {win}]) Number delete match identified by {id}
-matchend({expr}, {pat}[, {start}[, {count}]])
- Number position where {pat} ends in {expr}
-matchlist({expr}, {pat}[, {start}[, {count}]])
- List match and submatches of {pat} in {expr}
-matchstr({expr}, {pat}[, {start}[, {count}]])
- String {count}'th match of {pat} in {expr}
-matchstrpos({expr}, {pat}[, {start}[, {count}]])
- List {count}'th match of {pat} in {expr}
-max({expr}) Number maximum value of items in {expr}
-menu_get({path} [, {modes}]) List description of |menus| matched by {path}
-min({expr}) Number minimum value of items in {expr}
-mkdir({name} [, {path} [, {prot}]])
- Number create directory {name}
-mode([expr]) String current editing mode
-msgpackdump({list} [, {type}]) List/Blob dump objects to msgpack
-msgpackparse({data}) List parse msgpack to a list of objects
-nextnonblank({lnum}) Number line nr of non-blank line >= {lnum}
-nr2char({expr}[, {utf8}]) String single char with ASCII/UTF-8 value {expr}
-nvim_...({args}...) any call nvim |api| functions
-or({expr}, {expr}) Number bitwise OR
-pathshorten({expr}) String shorten directory names in a path
-perleval({expr}) any evaluate |perl| expression
-pow({x}, {y}) Float {x} to the power of {y}
-prevnonblank({lnum}) Number line nr of non-blank line <= {lnum}
-printf({fmt}, {expr1}...) String format text
-prompt_getprompt({buf}) String get prompt text
-prompt_setcallback({buf}, {expr}) none set prompt callback function
-prompt_setinterrupt({buf}, {text}) none set prompt interrupt function
-prompt_setprompt({buf}, {text}) none set prompt text
-pum_getpos() Dict position and size of pum if visible
-pumvisible() Number whether popup menu is visible
-pyeval({expr}) any evaluate |Python| expression
-py3eval({expr}) any evaluate |python3| expression
-pyxeval({expr}) any evaluate |python_x| expression
-range({expr} [, {max} [, {stride}]])
- List items from {expr} to {max}
-readdir({dir} [, {expr}]) List file names in {dir} selected by {expr}
-readfile({fname} [, {type} [, {max}]])
- List get list of lines from file {fname}
-reg_executing() String get the executing register name
-reg_recorded() String get the last recorded register name
-reg_recording() String get the recording register name
-reltime([{start} [, {end}]]) List get time value
-reltimefloat({time}) Float turn the time value into a Float
-reltimestr({time}) String turn time value into a String
-remote_expr({server}, {string} [, {idvar} [, {timeout}]])
- String send expression
-remote_foreground({server}) Number bring Vim server to the foreground
-remote_peek({serverid} [, {retvar}])
- Number check for reply string
-remote_read({serverid} [, {timeout}])
- String read reply string
-remote_send({server}, {string} [, {idvar}])
- String send key sequence
-remote_startserver({name}) none become server {name}
-remove({list}, {idx} [, {end}]) any/List
- remove items {idx}-{end} from {list}
-remove({blob}, {idx} [, {end}]) Number/Blob
- remove bytes {idx}-{end} from {blob}
-remove({dict}, {key}) any remove entry {key} from {dict}
-rename({from}, {to}) Number rename (move) file from {from} to {to}
-repeat({expr}, {count}) String repeat {expr} {count} times
-resolve({filename}) String get filename a shortcut points to
-reverse({list}) List reverse {list} in-place
-round({expr}) Float round off {expr}
-rubyeval({expr}) any evaluate |Ruby| expression
-rpcnotify({channel}, {event}[, {args}...])
- Sends an |RPC| notification to {channel}
-rpcrequest({channel}, {method}[, {args}...])
- Sends an |RPC| request to {channel}
-screenattr({row}, {col}) Number attribute at screen position
-screenchar({row}, {col}) Number character at screen position
-screenchars({row}, {col}) List List of characters at screen position
-screencol() Number current cursor column
-screenpos({winid}, {lnum}, {col}) Dict screen row and col of a text character
-screenrow() Number current cursor row
-screenstring({row}, {col}) String characters at screen position
-search({pattern} [, {flags} [, {stopline} [, {timeout}]]])
- Number search for {pattern}
-searchcount([{options}]) Dict Get or update the last search count
-searchdecl({name} [, {global} [, {thisblock}]])
- Number search for variable declaration
-searchpair({start}, {middle}, {end} [, {flags} [, {skip} [...]]])
- Number search for other end of start/end pair
-searchpairpos({start}, {middle}, {end} [, {flags} [, {skip} [...]]])
- List search for other end of start/end pair
-searchpos({pattern} [, {flags} [, {stopline} [, {timeout}]]])
- List search for {pattern}
-server2client({clientid}, {string})
- Number send reply string
-serverlist() String get a list of available servers
-setbufline( {expr}, {lnum}, {line})
- Number set line {lnum} to {line} in buffer
- {expr}
-setbufvar({buf}, {varname}, {val}) set {varname} in buffer {buf} to {val}
-setcharsearch({dict}) Dict set character search from {dict}
-setcmdpos({pos}) Number set cursor position in command-line
-setenv({name}, {val}) none set environment variable
-setfperm({fname}, {mode} Number set {fname} file permissions to {mode}
-setline({lnum}, {line}) Number set line {lnum} to {line}
-setloclist({nr}, {list} [, {action}])
- Number modify location list using {list}
-setloclist({nr}, {list}, {action}, {what})
- Number modify specific location list props
-setmatches({list} [, {win}]) Number restore a list of matches
-setpos({expr}, {list}) Number set the {expr} position to {list}
-setqflist({list} [, {action}]) Number modify quickfix list using {list}
-setqflist({list}, {action}, {what})
- Number modify specific quickfix list props
-setreg({n}, {v}[, {opt}]) Number set register to value and type
-settabvar({nr}, {varname}, {val}) set {varname} in tab page {nr} to {val}
-settabwinvar({tabnr}, {winnr}, {varname}, {val}) set {varname} in window
- {winnr} in tab page {tabnr} to {val}
-settagstack({nr}, {dict} [, {action}])
- Number modify tag stack using {dict}
-setwinvar({nr}, {varname}, {val}) set {varname} in window {nr} to {val}
-sha256({string}) String SHA256 checksum of {string}
-shellescape({string} [, {special}])
- String escape {string} for use as shell
- command argument
-shiftwidth([{col}]) Number effective value of 'shiftwidth'
-sign_define({name} [, {dict}]) Number define or update a sign
-sign_define({list}) List define or update a list of signs
-sign_getdefined([{name}]) List get a list of defined signs
-sign_getplaced([{buf} [, {dict}]])
- List get a list of placed signs
-sign_jump({id}, {group}, {buf})
- Number jump to a sign
-sign_place({id}, {group}, {name}, {buf} [, {dict}])
- Number place a sign
-sign_placelist({list}) List place a list of signs
-sign_undefine([{name}]) Number undefine a sign
-sign_undefine({list}) List undefine a list of signs
-sign_unplace({group} [, {dict}])
- Number unplace a sign
-sign_unplacelist({list}) List unplace a list of signs
-simplify({filename}) String simplify filename as much as possible
-sin({expr}) Float sine of {expr}
-sinh({expr}) Float hyperbolic sine of {expr}
-sockconnect({mode}, {address} [, {opts}])
- Number Connects to socket
-sort({list} [, {func} [, {dict}]])
- List sort {list}, using {func} to compare
-soundfold({word}) String sound-fold {word}
-spellbadword() String badly spelled word at cursor
-spellsuggest({word} [, {max} [, {capital}]])
- List spelling suggestions
-split({expr} [, {pat} [, {keepempty}]])
- List make |List| from {pat} separated {expr}
-sqrt({expr}) Float square root of {expr}
-stdioopen({dict}) Number open stdio in a headless instance.
-stdpath({what}) String/List returns the standard path(s) for {what}
-str2float({expr} [, {quoted}]) Float convert String to Float
-str2list({expr} [, {utf8}]) List convert each character of {expr} to
- ASCII/UTF-8 value
-str2nr({expr} [, {base} [, {quoted}]])
- Number convert String to Number
-strchars({expr} [, {skipcc}]) Number character length of the String {expr}
-strcharpart({str}, {start} [, {len}])
- String {len} characters of {str} at
- character {start}
-strdisplaywidth({expr} [, {col}]) Number display length of the String {expr}
-strftime({format} [, {time}]) String format time with a specified format
-strgetchar({str}, {index}) Number get char {index} from {str}
-stridx({haystack}, {needle} [, {start}])
- Number index of {needle} in {haystack}
-string({expr}) String String representation of {expr} value
-strlen({expr}) Number length of the String {expr}
-strpart({str}, {start} [, {len} [, {chars}]])
- String {len} bytes/chars of {str} at
- byte {start}
-strptime({format}, {timestring})
- Number Convert {timestring} to unix timestamp
-strridx({haystack}, {needle} [, {start}])
- Number last index of {needle} in {haystack}
-strtrans({expr}) String translate string to make it printable
-strwidth({expr}) Number display cell length of the String {expr}
-submatch({nr} [, {list}]) String or List
- specific match in ":s" or substitute()
-substitute({expr}, {pat}, {sub}, {flags})
- String all {pat} in {expr} replaced with {sub}
-swapinfo({fname}) Dict information about swap file {fname}
-swapname({buf}) String swap file of buffer {buf}
-synID({lnum}, {col}, {trans}) Number syntax ID at {lnum} and {col}
-synIDattr({synID}, {what} [, {mode}])
- String attribute {what} of syntax ID {synID}
-synIDtrans({synID}) Number translated syntax ID of {synID}
-synconcealed({lnum}, {col}) List info about concealing
-synstack({lnum}, {col}) List stack of syntax IDs at {lnum} and {col}
-system({cmd} [, {input}]) String output of shell command/filter {cmd}
-systemlist({cmd} [, {input}]) List output of shell command/filter {cmd}
-tabpagebuflist([{arg}]) List list of buffer numbers in tab page
-tabpagenr([{arg}]) Number number of current or last tab page
-tabpagewinnr({tabarg}[, {arg}])
- Number number of current window in tab page
-taglist({expr}[, {filename}]) List list of tags matching {expr}
-tagfiles() List tags files used
-tan({expr}) Float tangent of {expr}
-tanh({expr}) Float hyperbolic tangent of {expr}
-tempname() String name for a temporary file
-test_garbagecollect_now() none free memory right now for testing
-timer_info([{id}]) List information about timers
-timer_pause({id}, {pause}) none pause or unpause a timer
-timer_start({time}, {callback} [, {options}])
- Number create a timer
-timer_stop({timer}) none stop a timer
-timer_stopall() none stop all timers
-tolower({expr}) String the String {expr} switched to lowercase
-toupper({expr}) String the String {expr} switched to uppercase
-tr({src}, {fromstr}, {tostr}) String translate chars of {src} in {fromstr}
- to chars in {tostr}
-trim({text} [, {mask} [, {dir}]])
- String trim characters in {mask} from {text}
-trunc({expr}) Float truncate Float {expr}
-type({name}) Number type of variable {name}
-undofile({name}) String undo file name for {name}
-undotree() List undo file tree
-uniq({list} [, {func} [, {dict}]])
- List remove adjacent duplicates from a list
-values({dict}) List values in {dict}
-virtcol({expr}) Number screen column of cursor or mark
-visualmode([expr]) String last visual mode used
-wait({timeout}, {condition}[, {interval}])
- Number Wait until {condition} is satisfied
-wildmenumode() Number whether 'wildmenu' mode is active
-win_execute({id}, {command} [, {silent}])
- String execute {command} in window {id}
-win_findbuf({bufnr}) List find windows containing {bufnr}
-win_getid([{win} [, {tab}]]) Number get |window-ID| for {win} in {tab}
-win_gettype([{nr}]) String type of window {nr}
-win_gotoid({expr}) Number go to |window-ID| {expr}
-win_id2tabwin({expr}) List get tab and window nr from |window-ID|
-win_id2win({expr}) Number get window nr from |window-ID|
-win_screenpos({nr}) List get screen position of window {nr}
-win_splitmove({nr}, {target} [, {options}])
- Number move window {nr} to split of {target}
-winbufnr({nr}) Number buffer number of window {nr}
-wincol() Number window column of the cursor
-windowsversion() String MS-Windows OS version
-winheight({nr}) Number height of window {nr}
-winlayout([{tabnr}]) List layout of windows in tab {tabnr}
-winline() Number window line of the cursor
-winnr([{expr}]) Number number of current window
-winrestcmd() String returns command to restore window sizes
-winrestview({dict}) none restore view of current window
-winsaveview() Dict save view of current window
-winwidth({nr}) Number width of window {nr}
-wordcount() Dict get byte/char/word statistics
-writefile({object}, {fname} [, {flags}])
- Number write |Blob| or |List| of lines to file
-xor({expr}, {expr}) Number bitwise XOR
-
-
-abs({expr}) *abs()*
- Return the absolute value of {expr}. When {expr} evaluates to
- a |Float| abs() returns a |Float|. When {expr} can be
- converted to a |Number| abs() returns a |Number|. Otherwise
- abs() gives an error message and returns -1.
- Examples: >
- echo abs(1.456)
-< 1.456 >
- echo abs(-5.456)
-< 5.456 >
- echo abs(-4)
-< 4
-
- Can also be used as a |method|: >
- Compute()->abs()
-
-acos({expr}) *acos()*
- Return the arc cosine of {expr} measured in radians, as a
- |Float| in the range of [0, pi].
- {expr} must evaluate to a |Float| or a |Number| in the range
- [-1, 1].
- Examples: >
- :echo acos(0)
-< 1.570796 >
- :echo acos(-0.5)
-< 2.094395
-
- Can also be used as a |method|: >
- Compute()->acos()
-
-add({object}, {expr}) *add()*
- Append the item {expr} to |List| or |Blob| {object}. Returns
- the resulting |List| or |Blob|. Examples: >
- :let alist = add([1, 2, 3], item)
- :call add(mylist, "woodstock")
-< Note that when {expr} is a |List| it is appended as a single
- item. Use |extend()| to concatenate |Lists|.
- When {object} is a |Blob| then {expr} must be a number.
- Use |insert()| to add an item at another position.
-
- Can also be used as a |method|: >
- mylist->add(val1)->add(val2)
-
-and({expr}, {expr}) *and()*
- Bitwise AND on the two arguments. The arguments are converted
- to a number. A List, Dict or Float argument causes an error.
- Example: >
- :let flag = and(bits, 0x80)
-< Can also be used as a |method|: >
- :let flag = bits->and(0x80)
-
-api_info() *api_info()*
- Returns Dictionary of |api-metadata|.
-
- View it in a nice human-readable format: >
- :lua print(vim.inspect(vim.fn.api_info()))
-
-append({lnum}, {text}) *append()*
- When {text} is a |List|: Append each item of the |List| as a
- text line below line {lnum} in the current buffer.
- Otherwise append {text} as one text line below line {lnum} in
- the current buffer.
- {lnum} can be zero to insert a line before the first one.
- {lnum} is used like with |getline()|.
- Returns 1 for failure ({lnum} out of range or out of memory),
- 0 for success. Example: >
- :let failed = append(line('$'), "# THE END")
- :let failed = append(0, ["Chapter 1", "the beginning"])
-
-< Can also be used as a |method| after a List: >
- mylist->append(lnum)
-
-appendbufline({buf}, {lnum}, {text}) *appendbufline()*
- Like |append()| but append the text in buffer {expr}.
-
- This function works only for loaded buffers. First call
- |bufload()| if needed.
-
- For the use of {buf}, see |bufname()|.
-
- {lnum} is used like with |append()|. Note that using |line()|
- would use the current buffer, not the one appending to.
- Use "$" to append at the end of the buffer.
-
- On success 0 is returned, on failure 1 is returned.
-
- If {buf} is not a valid buffer or {lnum} is not valid, an
- error message is given. Example: >
- :let failed = appendbufline(13, 0, "# THE START")
-<
- Can also be used as a |method| after a List: >
- mylist->appendbufline(buf, lnum)
-
-argc([{winid}]) *argc()*
- The result is the number of files in the argument list. See
- |arglist|.
- If {winid} is not supplied, the argument list of the current
- window is used.
- If {winid} is -1, the global argument list is used.
- Otherwise {winid} specifies the window of which the argument
- list is used: either the window number or the window ID.
- Returns -1 if the {winid} argument is invalid.
-
- *argidx()*
-argidx() The result is the current index in the argument list. 0 is
- the first file. argc() - 1 is the last one. See |arglist|.
-
- *arglistid()*
-arglistid([{winnr} [, {tabnr}]])
- Return the argument list ID. This is a number which
- identifies the argument list being used. Zero is used for the
- global argument list. See |arglist|.
- Returns -1 if the arguments are invalid.
-
- Without arguments use the current window.
- With {winnr} only use this window in the current tab page.
- With {winnr} and {tabnr} use the window in the specified tab
- page.
- {winnr} can be the window number or the |window-ID|.
-
- *argv()*
-argv([{nr} [, {winid}]])
- The result is the {nr}th file in the argument list. See
- |arglist|. "argv(0)" is the first one. Example: >
- :let i = 0
- :while i < argc()
- : let f = escape(fnameescape(argv(i)), '.')
- : exe 'amenu Arg.' . f . ' :e ' . f . '<CR>'
- : let i = i + 1
- :endwhile
-< Without the {nr} argument, or when {nr} is -1, a |List| with
- the whole |arglist| is returned.
-
- The {winid} argument specifies the window ID, see |argc()|.
- For the Vim command line arguments see |v:argv|.
-
-asin({expr}) *asin()*
- Return the arc sine of {expr} measured in radians, as a |Float|
- in the range of [-pi/2, pi/2].
- {expr} must evaluate to a |Float| or a |Number| in the range
- [-1, 1].
- Examples: >
- :echo asin(0.8)
-< 0.927295 >
- :echo asin(-0.5)
-< -0.523599
-
- Can also be used as a |method|: >
- Compute()->asin()
-
-
-assert_ functions are documented here: |assert-functions-details|
-
-
-atan({expr}) *atan()*
- Return the principal value of the arc tangent of {expr}, in
- the range [-pi/2, +pi/2] radians, as a |Float|.
- {expr} must evaluate to a |Float| or a |Number|.
- Examples: >
- :echo atan(100)
-< 1.560797 >
- :echo atan(-4.01)
-< -1.326405
-
- Can also be used as a |method|: >
- Compute()->atan()
-
-atan2({expr1}, {expr2}) *atan2()*
- Return the arc tangent of {expr1} / {expr2}, measured in
- radians, as a |Float| in the range [-pi, pi].
- {expr1} and {expr2} must evaluate to a |Float| or a |Number|.
- Examples: >
- :echo atan2(-1, 1)
-< -0.785398 >
- :echo atan2(1, -1)
-< 2.356194
-
- Can also be used as a |method|: >
- Compute()->atan2(1)
-
- *browse()*
-browse({save}, {title}, {initdir}, {default})
- Put up a file requester. This only works when "has("browse")"
- returns |TRUE| (only in some GUI versions).
- The input fields are:
- {save} when |TRUE|, select file to write
- {title} title for the requester
- {initdir} directory to start browsing in
- {default} default file name
- An empty string is returned when the "Cancel" button is hit,
- something went wrong, or browsing is not possible.
-
- *browsedir()*
-browsedir({title}, {initdir})
- Put up a directory requester. This only works when
- "has("browse")" returns |TRUE| (only in some GUI versions).
- On systems where a directory browser is not supported a file
- browser is used. In that case: select a file in the directory
- to be used.
- The input fields are:
- {title} title for the requester
- {initdir} directory to start browsing in
- When the "Cancel" button is hit, something went wrong, or
- browsing is not possible, an empty string is returned.
-
-bufadd({name}) *bufadd()*
- Add a buffer to the buffer list with String {name}.
- If a buffer for file {name} already exists, return that buffer
- number. Otherwise return the buffer number of the newly
- created buffer. When {name} is an empty string then a new
- buffer is always created.
- The buffer will not have 'buflisted' set and not be loaded
- yet. To add some text to the buffer use this: >
- let bufnr = bufadd('someName')
- call bufload(bufnr)
- call setbufline(bufnr, 1, ['some', 'text'])
-< Can also be used as a |method|: >
- let bufnr = 'somename'->bufadd()
-
-bufexists({buf}) *bufexists()*
- The result is a Number, which is |TRUE| if a buffer called
- {buf} exists.
- If the {buf} argument is a number, buffer numbers are used.
- Number zero is the alternate buffer for the current window.
-
- If the {buf} argument is a string it must match a buffer name
- exactly. The name can be:
- - Relative to the current directory.
- - A full path.
- - The name of a buffer with 'buftype' set to "nofile".
- - A URL name.
- Unlisted buffers will be found.
- Note that help files are listed by their short name in the
- output of |:buffers|, but bufexists() requires using their
- long name to be able to find them.
- bufexists() may report a buffer exists, but to use the name
- with a |:buffer| command you may need to use |expand()|. Esp
- for MS-Windows 8.3 names in the form "c:\DOCUME~1"
- Use "bufexists(0)" to test for the existence of an alternate
- file name.
-
- Can also be used as a |method|: >
- let exists = 'somename'->bufexists()
-
-buflisted({buf}) *buflisted()*
- The result is a Number, which is |TRUE| if a buffer called
- {buf} exists and is listed (has the 'buflisted' option set).
- The {buf} argument is used like with |bufexists()|.
-
- Can also be used as a |method|: >
- let listed = 'somename'->buflisted()
-
-bufload({buf}) *bufload()*
- Ensure the buffer {buf} is loaded. When the buffer name
- refers to an existing file then the file is read. Otherwise
- the buffer will be empty. If the buffer was already loaded
- then there is no change.
- If there is an existing swap file for the file of the buffer,
- there will be no dialog, the buffer will be loaded anyway.
- The {buf} argument is used like with |bufexists()|.
-
- Can also be used as a |method|: >
- eval 'somename'->bufload()
-
-bufloaded({buf}) *bufloaded()*
- The result is a Number, which is |TRUE| if a buffer called
- {buf} exists and is loaded (shown in a window or hidden).
- The {buf} argument is used like with |bufexists()|.
-
- Can also be used as a |method|: >
- let loaded = 'somename'->bufloaded()
-
-bufname([{buf}]) *bufname()*
- The result is the name of a buffer. Mostly as it is displayed
- by the `:ls` command, but not using special names such as
- "[No Name]".
- If {buf} is omitted the current buffer is used.
- If {buf} is a Number, that buffer number's name is given.
- Number zero is the alternate buffer for the current window.
- If {buf} is a String, it is used as a |file-pattern| to match
- with the buffer names. This is always done like 'magic' is
- set and 'cpoptions' is empty. When there is more than one
- match an empty string is returned.
- "" or "%" can be used for the current buffer, "#" for the
- alternate buffer.
- A full match is preferred, otherwise a match at the start, end
- or middle of the buffer name is accepted. If you only want a
- full match then put "^" at the start and "$" at the end of the
- pattern.
- Listed buffers are found first. If there is a single match
- with a listed buffer, that one is returned. Next unlisted
- buffers are searched for.
- If the {buf} is a String, but you want to use it as a buffer
- number, force it to be a Number by adding zero to it: >
- :echo bufname("3" + 0)
-< Can also be used as a |method|: >
- echo bufnr->bufname()
-
-< If the buffer doesn't exist, or doesn't have a name, an empty
- string is returned. >
- bufname("#") alternate buffer name
- bufname(3) name of buffer 3
- bufname("%") name of current buffer
- bufname("file2") name of buffer where "file2" matches.
-<
- *bufnr()*
-bufnr([{buf} [, {create}]])
- The result is the number of a buffer, as it is displayed by
- the `:ls` command. For the use of {buf}, see |bufname()|
- above.
- If the buffer doesn't exist, -1 is returned. Or, if the
- {create} argument is present and TRUE, a new, unlisted,
- buffer is created and its number is returned.
- bufnr("$") is the last buffer: >
- :let last_buffer = bufnr("$")
-< The result is a Number, which is the highest buffer number
- of existing buffers. Note that not all buffers with a smaller
- number necessarily exist, because ":bwipeout" may have removed
- them. Use bufexists() to test for the existence of a buffer.
-
- Can also be used as a |method|: >
- echo bufref->bufnr()
-
-bufwinid({buf}) *bufwinid()*
- The result is a Number, which is the |window-ID| of the first
- window associated with buffer {buf}. For the use of {buf},
- see |bufname()| above. If buffer {buf} doesn't exist or
- there is no such window, -1 is returned. Example: >
-
- echo "A window containing buffer 1 is " . (bufwinid(1))
-<
- Only deals with the current tab page.
-
- Can also be used as a |method|: >
- FindBuffer()->bufwinid()
-
-bufwinnr({buf}) *bufwinnr()*
- Like |bufwinid()| but return the window number instead of the
- |window-ID|.
- If buffer {buf} doesn't exist or there is no such window, -1
- is returned. Example: >
-
- echo "A window containing buffer 1 is " . (bufwinnr(1))
-
-< The number can be used with |CTRL-W_w| and ":wincmd w"
- |:wincmd|.
-
- Can also be used as a |method|: >
- FindBuffer()->bufwinnr()
-
-byte2line({byte}) *byte2line()*
- Return the line number that contains the character at byte
- count {byte} in the current buffer. This includes the
- end-of-line character, depending on the 'fileformat' option
- for the current buffer. The first character has byte count
- one.
- Also see |line2byte()|, |go| and |:goto|.
-
- Can also be used as a |method|: >
- GetOffset()->byte2line()
-
-byteidx({expr}, {nr}) *byteidx()*
- Return byte index of the {nr}'th character in the String
- {expr}. Use zero for the first character, it then returns
- zero.
- If there are no multibyte characters the returned value is
- equal to {nr}.
- Composing characters are not counted separately, their byte
- length is added to the preceding base character. See
- |byteidxcomp()| below for counting composing characters
- separately.
- Example : >
- echo matchstr(str, ".", byteidx(str, 3))
-< will display the fourth character. Another way to do the
- same: >
- let s = strpart(str, byteidx(str, 3))
- echo strpart(s, 0, byteidx(s, 1))
-< Also see |strgetchar()| and |strcharpart()|.
-
- If there are less than {nr} characters -1 is returned.
- If there are exactly {nr} characters the length of the string
- in bytes is returned.
-
- Can also be used as a |method|: >
- GetName()->byteidx(idx)
-
-byteidxcomp({expr}, {nr}) *byteidxcomp()*
- Like byteidx(), except that a composing character is counted
- as a separate character. Example: >
- let s = 'e' . nr2char(0x301)
- echo byteidx(s, 1)
- echo byteidxcomp(s, 1)
- echo byteidxcomp(s, 2)
-< The first and third echo result in 3 ('e' plus composing
- character is 3 bytes), the second echo results in 1 ('e' is
- one byte).
- Only works differently from byteidx() when 'encoding' is set to
- a Unicode encoding.
-
- Can also be used as a |method|: >
- GetName()->byteidxcomp(idx)
-
-call({func}, {arglist} [, {dict}]) *call()* *E699*
- Call function {func} with the items in |List| {arglist} as
- arguments.
- {func} can either be a |Funcref| or the name of a function.
- a:firstline and a:lastline are set to the cursor line.
- Returns the return value of the called function.
- {dict} is for functions with the "dict" attribute. It will be
- used to set the local variable "self". |Dictionary-function|
-
- Can also be used as a |method|: >
- GetFunc()->call([arg, arg], dict)
-
-ceil({expr}) *ceil()*
- Return the smallest integral value greater than or equal to
- {expr} as a |Float| (round up).
- {expr} must evaluate to a |Float| or a |Number|.
- Examples: >
- echo ceil(1.456)
-< 2.0 >
- echo ceil(-5.456)
-< -5.0 >
- echo ceil(4.0)
-< 4.0
-
- Can also be used as a |method|: >
- Compute()->ceil()
-
-changenr() *changenr()*
- Return the number of the most recent change. This is the same
- number as what is displayed with |:undolist| and can be used
- with the |:undo| command.
- When a change was made it is the number of that change. After
- redo it is the number of the redone change. After undo it is
- one less than the number of the undone change.
-
-chanclose({id}[, {stream}]) *chanclose()*
- Close a channel or a specific stream associated with it.
- For a job, {stream} can be one of "stdin", "stdout",
- "stderr" or "rpc" (closes stdin/stdout for a job started
- with `"rpc":v:true`) If {stream} is omitted, all streams
- are closed. If the channel is a pty, this will then close the
- pty master, sending SIGHUP to the job process.
- For a socket, there is only one stream, and {stream} should be
- ommited.
-
-chansend({id}, {data}) *chansend()*
- 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. Returns the number of bytes
- written if the write succeeded, 0 otherwise.
- See |channel-bytes| for more information.
-
- {data} may be a string, string convertible, |Blob|, or a list.
- If {data} is a list, the items will be joined by newlines; any
- newlines in an item will be sent as NUL. To send a final
- newline, include a final empty string. Example: >
- :call chansend(id, ["abc", "123\n456", ""])
-< will send "abc<NL>123<NUL>456<NL>".
-
- chansend() writes raw data, not RPC messages. If the channel
- was created with `"rpc":v:true` then the channel expects RPC
- messages, use |rpcnotify()| and |rpcrequest()| instead.
-
-
-char2nr({string} [, {utf8}]) *char2nr()*
- Return number value of the first char in {string}.
- Examples: >
- char2nr(" ") returns 32
- char2nr("ABC") returns 65
- char2nr("á") returns 225
- char2nr("á"[0]) returns 195
- char2nr("\<M-x>") returns 128
-< Non-ASCII characters are always treated as UTF-8 characters.
- {utf8} is ignored, it exists only for backwards-compatibility.
- A combining character is a separate character.
- |nr2char()| does the opposite.
-
- Can also be used as a |method|: >
- GetChar()->char2nr()
-<
- *charidx()*
-charidx({string}, {idx} [, {countcc}])
- Return the character index of the byte at {idx} in {string}.
- The index of the first character is zero.
- If there are no multibyte characters the returned value is
- equal to {idx}.
- When {countcc} is omitted or |FALSE|, then composing characters
- are not counted separately, their byte length is
- added to the preceding base character.
- When {countcc} is |TRUE|, then composing characters are
- counted as separate characters.
- Returns -1 if the arguments are invalid or if {idx} is greater
- than the index of the last byte in {string}. An error is
- given if the first argument is not a string, the second
- argument is not a number or when the third argument is present
- and is not zero or one.
- See |byteidx()| and |byteidxcomp()| for getting the byte index
- from the character index.
- Examples: >
- echo charidx('áb́ć', 3) returns 1
- echo charidx('áb́ć', 6, 1) returns 4
- echo charidx('áb́ć', 16) returns -1
-<
- Can also be used as a |method|: >
- GetName()->charidx(idx)
-
-chdir({dir}) *chdir()*
- Change the current working directory to {dir}. The scope of
- the directory change depends on the directory of the current
- window:
- - If the current window has a window-local directory
- (|:lcd|), then changes the window local directory.
- - Otherwise, if the current tabpage has a local
- directory (|:tcd|) then changes the tabpage local
- directory.
- - Otherwise, changes the global directory.
- If successful, returns the previous working directory. Pass
- this to another chdir() to restore the directory.
- On failure, returns an empty string.
-
- Example: >
- let save_dir = chdir(newdir)
- if save_dir
- " ... do some work
- call chdir(save_dir)
- endif
-<
-cindent({lnum}) *cindent()*
- Get the amount of indent for line {lnum} according the C
- indenting rules, as with 'cindent'.
- The indent is counted in spaces, the value of 'tabstop' is
- relevant. {lnum} is used just like in |getline()|.
- When {lnum} is invalid -1 is returned.
- See |C-indenting|.
-
- Can also be used as a |method|: >
- GetLnum()->cindent()
-
-clearmatches([{win}]) *clearmatches()*
- Clears all matches previously defined for the current window
- by |matchadd()| and the |:match| commands.
- If {win} is specified, use the window with this number or
- window ID instead of the current window.
-
- Can also be used as a |method|: >
- GetWin()->clearmatches()
-<
- *col()*
-col({expr}) The result is a Number, which is the byte index of the column
- position given with {expr}. The accepted positions are:
- . the cursor position
- $ the end of the cursor line (the result is the
- number of bytes in the cursor line plus one)
- 'x position of mark x (if the mark is not set, 0 is
- returned)
- v In Visual mode: the start of the Visual area (the
- cursor is the end). When not in Visual mode
- returns the cursor position. Differs from |'<| in
- that it's updated right away.
- Additionally {expr} can be [lnum, col]: a |List| with the line
- and column number. Most useful when the column is "$", to get
- the last column of a specific line. When "lnum" or "col" is
- out of range then col() returns zero.
- To get the line number use |line()|. To get both use
- |getpos()|.
- For the screen column position use |virtcol()|.
- Note that only marks in the current file can be used.
- Examples: >
- col(".") column of cursor
- col("$") length of cursor line plus one
- col("'t") column of mark t
- col("'" . markname) column of mark markname
-< The first column is 1. 0 is returned for an error.
- For an uppercase mark the column may actually be in another
- buffer.
- For the cursor position, when 'virtualedit' is active, the
- column is one higher if the cursor is after the end of the
- line. This can be used to obtain the column in Insert mode: >
- :imap <F2> <C-O>:let save_ve = &ve<CR>
- \<C-O>:set ve=all<CR>
- \<C-O>:echo col(".") . "\n" <Bar>
- \let &ve = save_ve<CR>
-
-< Can also be used as a |method|: >
- GetPos()->col()
-<
-
-complete({startcol}, {matches}) *complete()* *E785*
- Set the matches for Insert mode completion.
- Can only be used in Insert mode. You need to use a mapping
- with CTRL-R = (see |i_CTRL-R|). It does not work after CTRL-O
- or with an expression mapping.
- {startcol} is the byte offset in the line where the completed
- text start. The text up to the cursor is the original text
- that will be replaced by the matches. Use col('.') for an
- empty string. "col('.') - 1" will replace one character by a
- match.
- {matches} must be a |List|. Each |List| item is one match.
- See |complete-items| for the kind of items that are possible.
- "longest" in 'completeopt' is ignored.
- Note that the after calling this function you need to avoid
- inserting anything that would cause completion to stop.
- The match can be selected with CTRL-N and CTRL-P as usual with
- Insert mode completion. The popup menu will appear if
- specified, see |ins-completion-menu|.
- Example: >
- inoremap <F5> <C-R>=ListMonths()<CR>
-
- func! ListMonths()
- call complete(col('.'), ['January', 'February', 'March',
- \ 'April', 'May', 'June', 'July', 'August', 'September',
- \ 'October', 'November', 'December'])
- return ''
- endfunc
-< This isn't very useful, but it shows how it works. Note that
- an empty string is returned to avoid a zero being inserted.
-
- Can also be used as a |method|, the base is passed as the
- second argument: >
- GetMatches()->complete(col('.'))
-
-complete_add({expr}) *complete_add()*
- Add {expr} to the list of matches. Only to be used by the
- function specified with the 'completefunc' option.
- Returns 0 for failure (empty string or out of memory),
- 1 when the match was added, 2 when the match was already in
- the list.
- See |complete-functions| for an explanation of {expr}. It is
- the same as one item in the list that 'omnifunc' would return.
-
- Can also be used as a |method|: >
- GetMoreMatches()->complete_add()
-
-complete_check() *complete_check()*
- Check for a key typed while looking for completion matches.
- This is to be used when looking for matches takes some time.
- Returns |TRUE| when searching for matches is to be aborted,
- zero otherwise.
- Only to be used by the function specified with the
- 'completefunc' option.
-
-
-complete_info([{what}]) *complete_info()*
- Returns a |Dictionary| with information about Insert mode
- completion. See |ins-completion|.
- The items are:
- mode Current completion mode name string.
- See |complete_info_mode| for the values.
- pum_visible |TRUE| if popup menu is visible.
- See |pumvisible()|.
- items List of completion matches. Each item is a
- dictionary containing the entries "word",
- "abbr", "menu", "kind", "info" and "user_data".
- See |complete-items|.
- selected Selected item index. First index is zero.
- Index is -1 if no item is selected (showing
- typed text only, or the last completion after
- no item is selected when using the <Up> or
- <Down> keys)
- inserted Inserted string. [NOT IMPLEMENT YET]
-
- *complete_info_mode*
- mode values are:
- "" Not in completion mode
- "keyword" Keyword completion |i_CTRL-X_CTRL-N|
- "ctrl_x" Just pressed CTRL-X |i_CTRL-X|
- "scroll" Scrolling with |i_CTRL-X_CTRL-E| or
- |i_CTRL-X_CTRL-Y|
- "whole_line" Whole lines |i_CTRL-X_CTRL-L|
- "files" File names |i_CTRL-X_CTRL-F|
- "tags" Tags |i_CTRL-X_CTRL-]|
- "path_defines" Definition completion |i_CTRL-X_CTRL-D|
- "path_patterns" Include completion |i_CTRL-X_CTRL-I|
- "dictionary" Dictionary |i_CTRL-X_CTRL-K|
- "thesaurus" Thesaurus |i_CTRL-X_CTRL-T|
- "cmdline" Vim Command line |i_CTRL-X_CTRL-V|
- "function" User defined completion |i_CTRL-X_CTRL-U|
- "omni" Omni completion |i_CTRL-X_CTRL-O|
- "spell" Spelling suggestions |i_CTRL-X_s|
- "eval" |complete()| completion
- "unknown" Other internal modes
-
- If the optional {what} list argument is supplied, then only
- the items listed in {what} are returned. Unsupported items in
- {what} are silently ignored.
-
- To get the position and size of the popup menu, see
- |pum_getpos()|. It's also available in |v:event| during the
- |CompleteChanged| event.
-
- Examples: >
- " Get all items
- call complete_info()
- " Get only 'mode'
- call complete_info(['mode'])
- " Get only 'mode' and 'pum_visible'
- call complete_info(['mode', 'pum_visible'])
-
-< Can also be used as a |method|: >
- GetItems()->complete_info()
-<
- *confirm()*
-confirm({msg} [, {choices} [, {default} [, {type}]]])
- Confirm() offers the user a dialog, from which a choice can be
- made. It returns the number of the choice. For the first
- choice this is 1.
-
- {msg} is displayed in a dialog with {choices} as the
- alternatives. When {choices} is missing or empty, "&OK" is
- used (and translated).
- {msg} is a String, use '\n' to include a newline. Only on
- some systems the string is wrapped when it doesn't fit.
-
- {choices} is a String, with the individual choices separated
- by '\n', e.g. >
- confirm("Save changes?", "&Yes\n&No\n&Cancel")
-< The letter after the '&' is the shortcut key for that choice.
- Thus you can type 'c' to select "Cancel". The shortcut does
- not need to be the first letter: >
- confirm("file has been modified", "&Save\nSave &All")
-< For the console, the first letter of each choice is used as
- the default shortcut key. Case is ignored.
-
- The optional {type} String argument gives the type of dialog.
- It can be one of these values: "Error", "Question", "Info",
- "Warning" or "Generic". Only the first character is relevant.
- When {type} is omitted, "Generic" is used.
-
- The optional {type} argument gives the type of dialog. This
- is only used for the icon of the Win32 GUI. It can be one of
- these values: "Error", "Question", "Info", "Warning" or
- "Generic". Only the first character is relevant.
- When {type} is omitted, "Generic" is used.
-
- If the user aborts the dialog by pressing <Esc>, CTRL-C,
- or another valid interrupt key, confirm() returns 0.
-
- An example: >
- :let choice = confirm("What do you want?", "&Apples\n&Oranges\n&Bananas", 2)
- :if choice == 0
- : echo "make up your mind!"
- :elseif choice == 3
- : echo "tasteful"
- :else
- : echo "I prefer bananas myself."
- :endif
-< In a GUI dialog, buttons are used. The layout of the buttons
- depends on the 'v' flag in 'guioptions'. If it is included,
- the buttons are always put vertically. Otherwise, confirm()
- tries to put the buttons in one horizontal line. If they
- don't fit, a vertical layout is used anyway. For some systems
- the horizontal layout is always used.
-
- Can also be used as a |method|in: >
- BuildMessage()->confirm("&Yes\n&No")
-<
- *copy()*
-copy({expr}) Make a copy of {expr}. For Numbers and Strings this isn't
- different from using {expr} directly.
- When {expr} is a |List| a shallow copy is created. This means
- that the original |List| can be changed without changing the
- copy, and vice versa. But the items are identical, thus
- changing an item changes the contents of both |Lists|.
- A |Dictionary| is copied in a similar way as a |List|.
- Also see |deepcopy()|.
- Can also be used as a |method|: >
- mylist->copy()
-
-cos({expr}) *cos()*
- Return the cosine of {expr}, measured in radians, as a |Float|.
- {expr} must evaluate to a |Float| or a |Number|.
- Examples: >
- :echo cos(100)
-< 0.862319 >
- :echo cos(-4.01)
-< -0.646043
-
- Can also be used as a |method|: >
- Compute()->cos()
-
-cosh({expr}) *cosh()*
- Return the hyperbolic cosine of {expr} as a |Float| in the range
- [1, inf].
- {expr} must evaluate to a |Float| or a |Number|.
- Examples: >
- :echo cosh(0.5)
-< 1.127626 >
- :echo cosh(-0.5)
-< -1.127626
-
- Can also be used as a |method|: >
- Compute()->cosh()
-
-count({comp}, {expr} [, {ic} [, {start}]]) *count()*
- Return the number of times an item with value {expr} appears
- in |String|, |List| or |Dictionary| {comp}.
-
- If {start} is given then start with the item with this index.
- {start} can only be used with a |List|.
-
- When {ic} is given and it's |TRUE| then case is ignored.
-
- When {comp} is a string then the number of not overlapping
- occurrences of {expr} is returned. Zero is returned when
- {expr} is an empty string.
-
- Can also be used as a |method|: >
- mylist->count(val)
-<
- *cscope_connection()*
-cscope_connection([{num} , {dbpath} [, {prepend}]])
- Checks for the existence of a |cscope| connection. If no
- parameters are specified, then the function returns:
- 0, if there are no cscope connections;
- 1, if there is at least one cscope connection.
-
- If parameters are specified, then the value of {num}
- determines how existence of a cscope connection is checked:
-
- {num} Description of existence check
- ----- ------------------------------
- 0 Same as no parameters (e.g., "cscope_connection()").
- 1 Ignore {prepend}, and use partial string matches for
- {dbpath}.
- 2 Ignore {prepend}, and use exact string matches for
- {dbpath}.
- 3 Use {prepend}, use partial string matches for both
- {dbpath} and {prepend}.
- 4 Use {prepend}, use exact string matches for both
- {dbpath} and {prepend}.
-
- Note: All string comparisons are case sensitive!
-
- Examples. Suppose we had the following (from ":cs show"): >
-
- # pid database name prepend path
- 0 27664 cscope.out /usr/local
-<
- Invocation Return Val ~
- ---------- ---------- >
- cscope_connection() 1
- cscope_connection(1, "out") 1
- cscope_connection(2, "out") 0
- cscope_connection(3, "out") 0
- cscope_connection(3, "out", "local") 1
- cscope_connection(4, "out") 0
- cscope_connection(4, "out", "local") 0
- cscope_connection(4, "cscope.out", "/usr/local") 1
-<
-
-ctxget([{index}]) *ctxget()*
- Returns a |Dictionary| representing the |context| at {index}
- from the top of the |context-stack| (see |context-dict|).
- If {index} is not given, it is assumed to be 0 (i.e.: top).
-
-ctxpop() *ctxpop()*
- Pops and restores the |context| at the top of the
- |context-stack|.
-
-ctxpush([{types}]) *ctxpush()*
- Pushes the current editor state (|context|) on the
- |context-stack|.
- If {types} is given and is a |List| of |String|s, it specifies
- which |context-types| to include in the pushed context.
- Otherwise, all context types are included.
-
-ctxset({context}[, {index}]) *ctxset()*
- Sets the |context| at {index} from the top of the
- |context-stack| to that represented by {context}.
- {context} is a Dictionary with context data (|context-dict|).
- If {index} is not given, it is assumed to be 0 (i.e.: top).
-
-ctxsize() *ctxsize()*
- Returns the size of the |context-stack|.
-
-cursor({lnum}, {col} [, {off}]) *cursor()*
-cursor({list})
- Positions the cursor at the column (byte count) {col} in the
- line {lnum}. The first column is one.
-
- When there is one argument {list} this is used as a |List|
- with two, three or four item:
- [{lnum}, {col}]
- [{lnum}, {col}, {off}]
- [{lnum}, {col}, {off}, {curswant}]
- This is like the return value of |getpos()| or |getcurpos()|,
- but without the first item.
-
- Does not change the jumplist.
- If {lnum} is greater than the number of lines in the buffer,
- the cursor will be positioned at the last line in the buffer.
- If {lnum} is zero, the cursor will stay in the current line.
- If {col} is greater than the number of bytes in the line,
- the cursor will be positioned at the last character in the
- line.
- If {col} is zero, the cursor will stay in the current column.
- If {curswant} is given it is used to set the preferred column
- for vertical movement. Otherwise {col} is used.
-
- When 'virtualedit' is used {off} specifies the offset in
- screen columns from the start of the character. E.g., a
- position within a <Tab> or after the last character.
- Returns 0 when the position could be set, -1 otherwise.
-
- Can also be used as a |method|: >
- GetCursorPos()->cursor()
-
-deepcopy({expr}[, {noref}]) *deepcopy()* *E698*
- Make a copy of {expr}. For Numbers and Strings this isn't
- different from using {expr} directly.
- When {expr} is a |List| a full copy is created. This means
- that the original |List| can be changed without changing the
- copy, and vice versa. When an item is a |List|, a copy for it
- is made, recursively. Thus changing an item in the copy does
- not change the contents of the original |List|.
-
- When {noref} is omitted or zero a contained |List| or
- |Dictionary| is only copied once. All references point to
- this single copy. With {noref} set to 1 every occurrence of a
- |List| or |Dictionary| results in a new copy. This also means
- that a cyclic reference causes deepcopy() to fail.
- *E724*
- Nesting is possible up to 100 levels. When there is an item
- that refers back to a higher level making a deep copy with
- {noref} set to 1 will fail.
- Also see |copy()|.
-
- Can also be used as a |method|: >
- GetObject()->deepcopy()
-
-delete({fname} [, {flags}]) *delete()*
- Without {flags} or with {flags} empty: Deletes the file by the
- name {fname}. This also works when {fname} is a symbolic link.
- A symbolic link itself is deleted, not what it points to.
-
- When {flags} is "d": Deletes the directory by the name
- {fname}. This fails when directory {fname} is not empty.
-
- When {flags} is "rf": Deletes the directory by the name
- {fname} and everything in it, recursively. BE CAREFUL!
- Note: on MS-Windows it is not possible to delete a directory
- that is being used.
-
- The result is a Number, which is 0/false if the delete
- operation was successful and -1/true when the deletion failed
- or partly failed.
-
- Can also be used as a |method|: >
- GetName()->delete()
-
-deletebufline({buf}, {first}[, {last}]) *deletebufline()*
- Delete lines {first} to {last} (inclusive) from buffer {buf}.
- If {last} is omitted then delete line {first} only.
- On success 0 is returned, on failure 1 is returned.
-
- This function works only for loaded buffers. First call
- |bufload()| if needed.
-
- For the use of {buf}, see |bufname()| above.
-
- {first} and {last} are used like with |setline()|. Note that
- when using |line()| this refers to the current buffer. Use "$"
- to refer to the last line in buffer {buf}.
-
- Can also be used as a |method|: >
- GetBuffer()->deletebufline(1)
-<
-dictwatcheradd({dict}, {pattern}, {callback}) *dictwatcheradd()*
- Adds a watcher to a dictionary. A dictionary watcher is
- identified by three components:
-
- - A dictionary({dict});
- - A key pattern({pattern}).
- - A function({callback}).
-
- After this is called, every change on {dict} and on keys
- matching {pattern} will result in {callback} being invoked.
-
- For example, to watch all global variables: >
- silent! call dictwatcherdel(g:, '*', 'OnDictChanged')
- function! OnDictChanged(d,k,z)
- echomsg string(a:k) string(a:z)
- endfunction
- call dictwatcheradd(g:, '*', 'OnDictChanged')
-<
- For now {pattern} only accepts very simple patterns that can
- contain a '*' at the end of the string, in which case it will
- match every key that begins with the substring before the '*'.
- That means if '*' is not the last character of {pattern}, only
- keys that are exactly equal as {pattern} will be matched.
-
- The {callback} receives three arguments:
-
- - The dictionary being watched.
- - The key which changed.
- - A dictionary containing the new and old values for the key.
-
- The type of change can be determined by examining the keys
- present on the third argument:
-
- - If contains both `old` and `new`, the key was updated.
- - If it contains only `new`, the key was added.
- - If it contains only `old`, the key was deleted.
-
- This function can be used by plugins to implement options with
- validation and parsing logic.
-
-dictwatcherdel({dict}, {pattern}, {callback}) *dictwatcherdel()*
- Removes a watcher added with |dictwatcheradd()|. All three
- arguments must match the ones passed to |dictwatcheradd()| in
- order for the watcher to be successfully deleted.
-
- *did_filetype()*
-did_filetype() Returns |TRUE| when autocommands are being executed and the
- FileType event has been triggered at least once. Can be used
- to avoid triggering the FileType event again in the scripts
- that detect the file type. |FileType|
- Returns |FALSE| when `:setf FALLBACK` was used.
- When editing another file, the counter is reset, thus this
- really checks if the FileType event has been triggered for the
- current buffer. This allows an autocommand that starts
- editing another buffer to set 'filetype' and load a syntax
- file.
-
-diff_filler({lnum}) *diff_filler()*
- Returns the number of filler lines above line {lnum}.
- These are the lines that were inserted at this point in
- another diff'ed window. These filler lines are shown in the
- display but don't exist in the buffer.
- {lnum} is used like with |getline()|. Thus "." is the current
- line, "'m" mark m, etc.
- Returns 0 if the current window is not in diff mode.
-
- Can also be used as a |method|: >
- GetLnum()->diff_filler()
-
-diff_hlID({lnum}, {col}) *diff_hlID()*
- Returns the highlight ID for diff mode at line {lnum} column
- {col} (byte index). When the current line does not have a
- diff change zero is returned.
- {lnum} is used like with |getline()|. Thus "." is the current
- line, "'m" mark m, etc.
- {col} is 1 for the leftmost column, {lnum} is 1 for the first
- line.
- The highlight ID can be used with |synIDattr()| to obtain
- syntax information about the highlighting.
-
- Can also be used as a |method|: >
- GetLnum()->diff_hlID(col)
-
-empty({expr}) *empty()*
- Return the Number 1 if {expr} is empty, zero otherwise.
- - A |List| or |Dictionary| is empty when it does not have any
- items.
- - A |String| is empty when its length is zero.
- - A |Number| and |Float| are empty when their value is zero.
- - |v:false| and |v:null| are empty, |v:true| is not.
- - A |Blob| is empty when its length is zero.
-
- Can also be used as a |method|: >
- mylist->empty()
-
-environ() *environ()*
- Return all of environment variables as dictionary. You can
- check if an environment variable exists like this: >
- :echo has_key(environ(), 'HOME')
-< Note that the variable name may be CamelCase; to ignore case
- use this: >
- :echo index(keys(environ()), 'HOME', 0, 1) != -1
-
-escape({string}, {chars}) *escape()*
- Escape the characters in {chars} that occur in {string} with a
- backslash. Example: >
- :echo escape('c:\program files\vim', ' \')
-< results in: >
- c:\\program\ files\\vim
-< Also see |shellescape()| and |fnameescape()|.
-
- Can also be used as a |method|: >
- GetText()->escape(' \')
-<
- *eval()*
-eval({string}) Evaluate {string} and return the result. Especially useful to
- turn the result of |string()| back into the original value.
- This works for Numbers, Floats, Strings, Blobs and composites
- of them. Also works for |Funcref|s that refer to existing
- functions.
-
- Can also be used as a |method|: >
- argv->join()->eval()
-
-eventhandler() *eventhandler()*
- Returns 1 when inside an event handler. That is that Vim got
- interrupted while waiting for the user to type a character,
- e.g., when dropping a file on Vim. This means interactive
- commands cannot be used. Otherwise zero is returned.
-
-executable({expr}) *executable()*
- This function checks if an executable with the name {expr}
- exists. {expr} must be the name of the program without any
- arguments.
- executable() uses the value of $PATH and/or the normal
- searchpath for programs. *PATHEXT*
- On MS-Windows the ".exe", ".bat", etc. can optionally be
- included. Then the extensions in $PATHEXT are tried. Thus if
- "foo.exe" does not exist, "foo.exe.bat" can be found. If
- $PATHEXT is not set then ".exe;.com;.bat;.cmd" is used. A dot
- by itself can be used in $PATHEXT to try using the name
- without an extension. When 'shell' looks like a Unix shell,
- then the name is also tried without adding an extension.
- On MS-Windows it only checks if the file exists and is not a
- directory, not if it's really executable.
- On Windows an executable in the same directory as Vim is
- always found (it is added to $PATH at |startup|).
- The result is a Number:
- 1 exists
- 0 does not exist
- -1 not implemented on this system
- |exepath()| can be used to get the full path of an executable.
-
- Can also be used as a |method|: >
- GetCommand()->executable()
-
-execute({command} [, {silent}]) *execute()*
- Execute {command} and capture its output.
- If {command} is a |String|, returns {command} output.
- If {command} is a |List|, returns concatenated outputs.
- Examples: >
- echo execute('echon "foo"')
-< foo >
- echo execute(['echon "foo"', 'echon "bar"'])
-< foobar
-
- The optional {silent} argument can have these values:
- "" no `:silent` used
- "silent" `:silent` used
- "silent!" `:silent!` used
- The default is "silent". Note that with "silent!", unlike
- `:redir`, error messages are dropped.
-
- To get a list of lines use |split()| on the result: >
- split(execute('args'), "\n")
-
-< This function is not available in the |sandbox|.
- Note: If nested, an outer execute() will not observe output of
- the inner calls.
- Note: Text attributes (highlights) are not captured.
- To execute a command in another window than the current one
- use `win_execute()`.
-
- Can also be used as a |method|: >
- GetCommand()->execute()
-
-exepath({expr}) *exepath()*
- Returns the full path of {expr} if it is an executable and
- given as a (partial or full) path or is found in $PATH.
- Returns empty string otherwise.
- If {expr} starts with "./" the |current-directory| is used.
-
- Can also be used as a |method|: >
- GetCommand()->exepath()
-<
- *exists()*
-exists({expr}) The result is a Number, which is |TRUE| if {expr} is
- defined, zero otherwise.
-
- For checking for a supported feature use |has()|.
- For checking if a file exists use |filereadable()|.
-
- The {expr} argument is a string, which contains one of these:
- &option-name Vim option (only checks if it exists,
- not if it really works)
- +option-name Vim option that works.
- $ENVNAME environment variable (could also be
- done by comparing with an empty
- string)
- *funcname built-in function (see |functions|)
- or user defined function (see
- |user-function|). Also works for a
- variable that is a Funcref.
- varname internal variable (see
- |internal-variables|). Also works
- for |curly-braces-names|, |Dictionary|
- entries, |List| items, etc. Beware
- that evaluating an index may cause an
- error message for an invalid
- expression. E.g.: >
- :let l = [1, 2, 3]
- :echo exists("l[5]")
-< 0 >
- :echo exists("l[xx]")
-< E121: Undefined variable: xx
- 0
- :cmdname Ex command: built-in command, user
- command or command modifier |:command|.
- Returns:
- 1 for match with start of a command
- 2 full match with a command
- 3 matches several user commands
- To check for a supported command
- always check the return value to be 2.
- :2match The |:2match| command.
- :3match The |:3match| command.
- #event autocommand defined for this event
- #event#pattern autocommand defined for this event and
- pattern (the pattern is taken
- literally and compared to the
- autocommand patterns character by
- character)
- #group autocommand group exists
- #group#event autocommand defined for this group and
- event.
- #group#event#pattern
- autocommand defined for this group,
- event and pattern.
- ##event autocommand for this event is
- supported.
-
- Examples: >
- exists("&mouse")
- exists("$HOSTNAME")
- exists("*strftime")
- exists("*s:MyFunc")
- exists("bufcount")
- exists(":Make")
- exists("#CursorHold")
- exists("#BufReadPre#*.gz")
- exists("#filetypeindent")
- exists("#filetypeindent#FileType")
- exists("#filetypeindent#FileType#*")
- exists("##ColorScheme")
-< There must be no space between the symbol (&/$/*/#) and the
- name.
- There must be no extra characters after the name, although in
- a few cases this is ignored. That may become more strict in
- the future, thus don't count on it!
- Working example: >
- exists(":make")
-< NOT working example: >
- exists(":make install")
-
-< Note that the argument must be a string, not the name of the
- variable itself. For example: >
- exists(bufcount)
-< This doesn't check for existence of the "bufcount" variable,
- but gets the value of "bufcount", and checks if that exists.
-
- Can also be used as a |method|: >
- Varname()->exists()
-
-exp({expr}) *exp()*
- Return the exponential of {expr} as a |Float| in the range
- [0, inf].
- {expr} must evaluate to a |Float| or a |Number|.
- Examples: >
- :echo exp(2)
-< 7.389056 >
- :echo exp(-1)
-< 0.367879
-
- Can also be used as a |method|: >
- Compute()->exp()
-
-debugbreak({pid}) *debugbreak()*
- Specifically used to interrupt a program being debugged. It
- will cause process {pid} to get a SIGTRAP. Behavior for other
- processes is undefined. See |terminal-debugger|.
- {Sends a SIGINT to a process {pid} other than MS-Windows}
-
- Can also be used as a |method|: >
- GetPid()->debugbreak()
-
-expand({string} [, {nosuf} [, {list}]]) *expand()*
- Expand wildcards and the following special keywords in
- {string}. 'wildignorecase' applies.
-
- If {list} is given and it is |TRUE|, a List will be returned.
- Otherwise the result is a String and when there are several
- matches, they are separated by <NL> characters.
-
- If the expansion fails, the result is an empty string. A name
- for a non-existing file is not included, unless {string} does
- not start with '%', '#' or '<', see below.
-
- When {string} starts with '%', '#' or '<', the expansion is
- done like for the |cmdline-special| variables with their
- associated modifiers. Here is a short overview:
-
- % current file name
- # alternate file name
- #n alternate file name n
- <cfile> file name under the cursor
- <afile> autocmd file name
- <abuf> autocmd buffer number (as a String!)
- <amatch> autocmd matched name
- <sfile> sourced script file or function name
- <slnum> sourced script line number or function
- line number
- <sflnum> script file line number, also when in
- a function
- <SID> "<SNR>123_" where "123" is the
- current script ID |<SID>|
- <cword> word under the cursor
- <cWORD> WORD under the cursor
- <client> the {clientid} of the last received
- message |server2client()|
- Modifiers:
- :p expand to full path
- :h head (last path component removed)
- :t tail (last path component only)
- :r root (one extension removed)
- :e extension only
-
- Example: >
- :let &tags = expand("%:p:h") . "/tags"
-< Note that when expanding a string that starts with '%', '#' or
- '<', any following text is ignored. This does NOT work: >
- :let doesntwork = expand("%:h.bak")
-< Use this: >
- :let doeswork = expand("%:h") . ".bak"
-< Also note that expanding "<cfile>" and others only returns the
- referenced file name without further expansion. If "<cfile>"
- is "~/.cshrc", you need to do another expand() to have the
- "~/" expanded into the path of the home directory: >
- :echo expand(expand("<cfile>"))
-<
- There cannot be white space between the variables and the
- following modifier. The |fnamemodify()| function can be used
- to modify normal file names.
-
- When using '%' or '#', and the current or alternate file name
- is not defined, an empty string is used. Using "%:p" in a
- buffer with no name, results in the current directory, with a
- '/' added.
-
- When {string} does not start with '%', '#' or '<', it is
- expanded like a file name is expanded on the command line.
- 'suffixes' and 'wildignore' are used, unless the optional
- {nosuf} argument is given and it is |TRUE|.
- Names for non-existing files are included. The "**" item can
- be used to search in a directory tree. For example, to find
- all "README" files in the current directory and below: >
- :echo expand("**/README")
-<
- expand() can also be used to expand variables and environment
- variables that are only known in a shell. But this can be
- slow, because a shell may be used to do the expansion. See
- |expr-env-expand|.
- The expanded variable is still handled like a list of file
- names. When an environment variable cannot be expanded, it is
- left unchanged. Thus ":echo expand('$FOOBAR')" results in
- "$FOOBAR".
-
- See |glob()| for finding existing files. See |system()| for
- getting the raw output of an external command.
-
- Can also be used as a |method|: >
- Getpattern()->expand()
-
-expandcmd({string}) *expandcmd()*
- Expand special items in String {string} like what is done for
- an Ex command such as `:edit`. This expands special keywords,
- like with |expand()|, and environment variables, anywhere in
- {string}. "~user" and "~/path" are only expanded at the
- start.
- Returns the expanded string. Example: >
- :echo expandcmd('make %<.o')
-
-< Can also be used as a |method|: >
- GetCommand()->expandcmd()
-<
-extend({expr1}, {expr2} [, {expr3}]) *extend()*
- {expr1} and {expr2} must be both |Lists| or both
- |Dictionaries|.
-
- If they are |Lists|: Append {expr2} to {expr1}.
- If {expr3} is given insert the items of {expr2} before the
- item with index {expr3} in {expr1}. When {expr3} is zero
- insert before the first item. When {expr3} is equal to
- len({expr1}) then {expr2} is appended.
- Examples: >
- :echo sort(extend(mylist, [7, 5]))
- :call extend(mylist, [2, 3], 1)
-< When {expr1} is the same List as {expr2} then the number of
- items copied is equal to the original length of the List.
- E.g., when {expr3} is 1 you get N new copies of the first item
- (where N is the original length of the List).
- Use |add()| to concatenate one item to a list. To concatenate
- two lists into a new list use the + operator: >
- :let newlist = [1, 2, 3] + [4, 5]
-<
- If they are |Dictionaries|:
- Add all entries from {expr2} to {expr1}.
- If a key exists in both {expr1} and {expr2} then {expr3} is
- used to decide what to do:
- {expr3} = "keep": keep the value of {expr1}
- {expr3} = "force": use the value of {expr2}
- {expr3} = "error": give an error message *E737*
- When {expr3} is omitted then "force" is assumed.
-
- {expr1} is changed when {expr2} is not empty. If necessary
- make a copy of {expr1} first.
- {expr2} remains unchanged.
- When {expr1} is locked and {expr2} is not empty the operation
- fails.
- Returns {expr1}.
-
- Can also be used as a |method|: >
- mylist->extend(otherlist)
-
-feedkeys({string} [, {mode}]) *feedkeys()*
- Characters in {string} are queued for processing as if they
- come from a mapping or were typed by the user.
-
- By default the string is added to the end of the typeahead
- buffer, thus if a mapping is still being executed the
- characters come after them. Use the 'i' flag to insert before
- other characters, they will be executed next, before any
- characters from a mapping.
-
- The function does not wait for processing of keys contained in
- {string}.
-
- To include special keys into {string}, use double-quotes
- and "\..." notation |expr-quote|. For example,
- feedkeys("\<CR>") simulates pressing of the <Enter> key. But
- feedkeys('\<CR>') pushes 5 characters.
- The |<Ignore>| keycode may be used to exit the
- wait-for-character without doing anything.
-
- {mode} is a String, which can contain these character flags:
- 'm' Remap keys. This is default. If {mode} is absent,
- keys are remapped.
- 'n' Do not remap keys.
- 't' Handle keys as if typed; otherwise they are handled as
- if coming from a mapping. This matters for undo,
- opening folds, etc.
- 'i' Insert the string instead of appending (see above).
- 'x' Execute commands until typeahead is empty. This is
- similar to using ":normal!". You can call feedkeys()
- several times without 'x' and then one time with 'x'
- (possibly with an empty {string}) to execute all the
- typeahead. Note that when Vim ends in Insert mode it
- will behave as if <Esc> is typed, to avoid getting
- stuck, waiting for a character to be typed before the
- script continues.
- Note that if you manage to call feedkeys() while
- executing commands, thus calling it recursively, then
- all typehead will be consumed by the last call.
- '!' When used with 'x' will not end Insert mode. Can be
- used in a test when a timer is set to exit Insert mode
- a little later. Useful for testing CursorHoldI.
-
- Return value is always 0.
-
- Can also be used as a |method|: >
- GetInput()->feedkeys()
-
-filereadable({file}) *filereadable()*
- The result is a Number, which is |TRUE| when a file with the
- name {file} exists, and can be read. If {file} doesn't exist,
- or is a directory, the result is |FALSE|. {file} is any
- expression, which is used as a String.
- If you don't care about the file being readable you can use
- |glob()|.
- {file} is used as-is, you may want to expand wildcards first: >
- echo filereadable('~/.vimrc')
- 0
- echo filereadable(expand('~/.vimrc'))
- 1
-
-< Can also be used as a |method|: >
- GetName()->filereadable()
-
-filewritable({file}) *filewritable()*
- The result is a Number, which is 1 when a file with the
- name {file} exists, and can be written. If {file} doesn't
- exist, or is not writable, the result is 0. If {file} is a
- directory, and we can write to it, the result is 2.
-
- Can also be used as a |method|: >
- GetName()->filewriteable()
-
-filter({expr1}, {expr2}) *filter()*
- {expr1} must be a |List|, |Blob|, or a |Dictionary|.
- For each item in {expr1} evaluate {expr2} and when the result
- is zero remove the item from the |List| or |Dictionary|. For a
- |Blob| each byte is removed.
-
- {expr2} must be a |string| or |Funcref|.
-
- If {expr2} is a |string|, inside {expr2} |v:val| has the value
- of the current item. For a |Dictionary| |v:key| has the key
- of the current item and for a |List| |v:key| has the index of
- the current item. For a |Blob| |v:key| has the index of the
- current byte.
-
- Examples: >
- call filter(mylist, 'v:val !~ "OLD"')
-< Removes the items where "OLD" appears. >
- call filter(mydict, 'v:key >= 8')
-< Removes the items with a key below 8. >
- call filter(var, 0)
-< Removes all the items, thus clears the |List| or |Dictionary|.
-
- Note that {expr2} is the result of expression and is then
- used as an expression again. Often it is good to use a
- |literal-string| to avoid having to double backslashes.
-
- If {expr2} is a |Funcref| it must take two arguments:
- 1. the key or the index of the current item.
- 2. the value of the current item.
- The function must return |TRUE| if the item should be kept.
- Example that keeps the odd items of a list: >
- func Odd(idx, val)
- return a:idx % 2 == 1
- endfunc
- call filter(mylist, function('Odd'))
-< It is shorter when using a |lambda|: >
- call filter(myList, {idx, val -> idx * val <= 42})
-< If you do not use "val" you can leave it out: >
- call filter(myList, {idx -> idx % 2 == 1})
-<
- The operation is done in-place. If you want a |List| or
- |Dictionary| to remain unmodified make a copy first: >
- :let l = filter(copy(mylist), 'v:val =~ "KEEP"')
-
-< Returns {expr1}, the |List|, |Blob| or |Dictionary| that was
- filtered. When an error is encountered while evaluating
- {expr2} no further items in {expr1} are processed. When
- {expr2} is a Funcref errors inside a function are ignored,
- unless it was defined with the "abort" flag.
-
- Can also be used as a |method|: >
- mylist->filter(expr2)
-
-finddir({name} [, {path} [, {count}]]) *finddir()*
- Find directory {name} in {path}. Supports both downwards and
- upwards recursive directory searches. See |file-searching|
- for the syntax of {path}.
-
- Returns the path of the first found match. When the found
- directory is below the current directory a relative path is
- returned. Otherwise a full path is returned.
- If {path} is omitted or empty then 'path' is used.
-
- If the optional {count} is given, find {count}'s occurrence of
- {name} in {path} instead of the first one.
- When {count} is negative return all the matches in a |List|.
-
- This is quite similar to the ex-command `:find`.
-
- Can also be used as a |method|: >
- GetName()->finddir()
-
-findfile({name} [, {path} [, {count}]]) *findfile()*
- Just like |finddir()|, but find a file instead of a directory.
- Uses 'suffixesadd'.
- Example: >
- :echo findfile("tags.vim", ".;")
-< Searches from the directory of the current file upwards until
- it finds the file "tags.vim".
-
- Can also be used as a |method|: >
- GetName()->findfile()
-
-flatten({list} [, {maxdepth}]) *flatten()*
- Flatten {list} up to {maxdepth} levels. Without {maxdepth}
- the result is a |List| without nesting, as if {maxdepth} is
- a very large number.
- The {list} is changed in place, make a copy first if you do
- not want that.
- *E900*
- {maxdepth} means how deep in nested lists changes are made.
- {list} is not modified when {maxdepth} is 0.
- {maxdepth} must be positive number.
-
- If there is an error the number zero is returned.
-
- Example: >
- :echo flatten([1, [2, [3, 4]], 5])
-< [1, 2, 3, 4, 5] >
- :echo flatten([1, [2, [3, 4]], 5], 1)
-< [1, 2, [3, 4], 5]
-
- Can also be used as a |method|: >
- mylist->flatten()
-<
-float2nr({expr}) *float2nr()*
- Convert {expr} to a Number by omitting the part after the
- decimal point.
- {expr} must evaluate to a |Float| or a Number.
- When the value of {expr} is out of range for a |Number| the
- result is truncated to 0x7fffffff or -0x7fffffff (or when
- 64-bit Number support is enabled, 0x7fffffffffffffff or
- -0x7fffffffffffffff). NaN results in -0x80000000 (or when
- 64-bit Number support is enabled, -0x8000000000000000).
- Examples: >
- echo float2nr(3.95)
-< 3 >
- echo float2nr(-23.45)
-< -23 >
- echo float2nr(1.0e100)
-< 2147483647 (or 9223372036854775807) >
- echo float2nr(-1.0e150)
-< -2147483647 (or -9223372036854775807) >
- echo float2nr(1.0e-100)
-< 0
-
- Can also be used as a |method|: >
- Compute()->float2nr()
-
-floor({expr}) *floor()*
- Return the largest integral value less than or equal to
- {expr} as a |Float| (round down).
- {expr} must evaluate to a |Float| or a |Number|.
- Examples: >
- echo floor(1.856)
-< 1.0 >
- echo floor(-5.456)
-< -6.0 >
- echo floor(4.0)
-< 4.0
-
- Can also be used as a |method|: >
- Compute()->floor()
-
-fmod({expr1}, {expr2}) *fmod()*
- Return the remainder of {expr1} / {expr2}, even if the
- division is not representable. Returns {expr1} - i * {expr2}
- for some integer i such that if {expr2} is non-zero, the
- result has the same sign as {expr1} and magnitude less than
- the magnitude of {expr2}. If {expr2} is zero, the value
- returned is zero. The value returned is a |Float|.
- {expr1} and {expr2} must evaluate to a |Float| or a |Number|.
- Examples: >
- :echo fmod(12.33, 1.22)
-< 0.13 >
- :echo fmod(-12.33, 1.22)
-< -0.13
-
- Can also be used as a |method|: >
- Compute()->fmod(1.22)
-
-fnameescape({string}) *fnameescape()*
- Escape {string} for use as file name command argument. All
- characters that have a special meaning, such as '%' and '|'
- are escaped with a backslash.
- For most systems the characters escaped are
- " \t\n*?[{`$\\%#'\"|!<". For systems where a backslash
- appears in a filename, it depends on the value of 'isfname'.
- A leading '+' and '>' is also escaped (special after |:edit|
- and |:write|). And a "-" by itself (special after |:cd|).
- Example: >
- :let fname = '+some str%nge|name'
- :exe "edit " . fnameescape(fname)
-< results in executing: >
- edit \+some\ str\%nge\|name
-<
- Can also be used as a |method|: >
- GetName()->fnameescape()
-
-fnamemodify({fname}, {mods}) *fnamemodify()*
- Modify file name {fname} according to {mods}. {mods} is a
- string of characters like it is used for file names on the
- command line. See |filename-modifiers|.
- Example: >
- :echo fnamemodify("main.c", ":p:h")
-< results in: >
- /home/mool/vim/vim/src
-< If {mods} is empty then {fname} is returned.
- Note: Environment variables don't work in {fname}, use
- |expand()| first then.
-
- Can also be used as a |method|: >
- GetName()->fnamemodify(':p:h')
-
-foldclosed({lnum}) *foldclosed()*
- The result is a Number. If the line {lnum} is in a closed
- fold, the result is the number of the first line in that fold.
- If the line {lnum} is not in a closed fold, -1 is returned.
- {lnum} is used like with |getline()|. Thus "." is the current
- line, "'m" mark m, etc.
-
- Can also be used as a |method|: >
- GetLnum()->foldclosed()
-
-foldclosedend({lnum}) *foldclosedend()*
- The result is a Number. If the line {lnum} is in a closed
- fold, the result is the number of the last line in that fold.
- If the line {lnum} is not in a closed fold, -1 is returned.
- {lnum} is used like with |getline()|. Thus "." is the current
- line, "'m" mark m, etc.
-
- Can also be used as a |method|: >
- GetLnum()->foldclosedend()
-
-foldlevel({lnum}) *foldlevel()*
- The result is a Number, which is the foldlevel of line {lnum}
- in the current buffer. For nested folds the deepest level is
- returned. If there is no fold at line {lnum}, zero is
- returned. It doesn't matter if the folds are open or closed.
- When used while updating folds (from 'foldexpr') -1 is
- returned for lines where folds are still to be updated and the
- foldlevel is unknown. As a special case the level of the
- previous line is usually available.
- {lnum} is used like with |getline()|. Thus "." is the current
- line, "'m" mark m, etc.
-
- Can also be used as a |method|: >
- GetLnum()->foldlevel()
-<
- *foldtext()*
-foldtext() Returns a String, to be displayed for a closed fold. This is
- the default function used for the 'foldtext' option and should
- only be called from evaluating 'foldtext'. It uses the
- |v:foldstart|, |v:foldend| and |v:folddashes| variables.
- The returned string looks like this: >
- +-- 45 lines: abcdef
-< The number of leading dashes depends on the foldlevel. The
- "45" is the number of lines in the fold. "abcdef" is the text
- in the first non-blank line of the fold. Leading white space,
- "//" or "/*" and the text from the 'foldmarker' and
- 'commentstring' options is removed.
- When used to draw the actual foldtext, the rest of the line
- will be filled with the fold char from the 'fillchars'
- setting.
-
-foldtextresult({lnum}) *foldtextresult()*
- Returns the text that is displayed for the closed fold at line
- {lnum}. Evaluates 'foldtext' in the appropriate context.
- When there is no closed fold at {lnum} an empty string is
- returned.
- {lnum} is used like with |getline()|. Thus "." is the current
- line, "'m" mark m, etc.
- Useful when exporting folded text, e.g., to HTML.
-
- Can also be used as a |method|: >
- GetLnum()->foldtextresult()
-<
- *foreground()*
-foreground() Move the Vim window to the foreground. Useful when sent from
- a client to a Vim server. |remote_send()|
- On Win32 systems this might not work, the OS does not always
- allow a window to bring itself to the foreground. Use
- |remote_foreground()| instead.
- {only in the Win32 GUI and console version}
-
-fullcommand({name}) *fullcommand()*
- Get the full command name from a short abbreviated command
- name; see |20.2| for details on command abbreviations.
-
- The string argument {name} may start with a `:` and can
- include a [range], these are skipped and not returned.
- Returns an empty string if a command doesn't exist or if it's
- ambiguous (for user-defined commands).
-
- For example `fullcommand('s')`, `fullcommand('sub')`,
- `fullcommand(':%substitute')` all return "substitute".
-
- Can also be used as a |method|: >
- GetName()->fullcommand()
-<
- *funcref()*
-funcref({name} [, {arglist}] [, {dict}])
- Just like |function()|, but the returned Funcref will lookup
- the function by reference, not by name. This matters when the
- function {name} is redefined later.
-
- Unlike |function()|, {name} must be an existing user 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])
-<
- *function()* *E700* *E922* *E923*
-function({name} [, {arglist}] [, {dict}])
- Return a |Funcref| variable that refers to function {name}.
- {name} can be a user defined function or an internal function.
-
- {name} can also be a Funcref or a partial. When it is a
- partial the dict stored in it will be used and the {dict}
- argument is not allowed. E.g.: >
- let FuncWithArg = function(dict.Func, [arg])
- let Broken = function(dict.Func, [arg], dict)
-<
- When using the Funcref the function will be found by {name},
- also when it was redefined later. Use |funcref()| to keep the
- same function.
-
- When {arglist} or {dict} is present this creates a partial.
- That means the argument list and/or the dictionary is stored in
- the Funcref and will be used when the Funcref is called.
-
- The arguments are passed to the function in front of other
- arguments, but after any argument from |method|. Example: >
- func Callback(arg1, arg2, name)
- ...
- let Partial = function('Callback', ['one', 'two'])
- ...
- call Partial('name')
-< Invokes the function as with: >
- call Callback('one', 'two', 'name')
-
-< The Dictionary is only useful when calling a "dict" function.
- In that case the {dict} is passed in as "self". Example: >
- function Callback() dict
- echo "called for " . self.name
- endfunction
- ...
- let context = {"name": "example"}
- let Func = function('Callback', context)
- ...
- call Func() " will echo: called for example
-
-< The argument list and the Dictionary can be combined: >
- function Callback(arg1, count) dict
- ...
- let context = {"name": "example"}
- let Func = function('Callback', ['one'], context)
- ...
- call Func(500)
-< Invokes the function as with: >
- call context.Callback('one', 500)
-<
- Can also be used as a |method|: >
- GetFuncname()->function([arg])
-
-garbagecollect([{atexit}]) *garbagecollect()*
- Cleanup unused |Lists| and |Dictionaries| that have circular
- references.
-
- There is hardly ever a need to invoke this function, as it is
- automatically done when Vim runs out of memory or is waiting
- for the user to press a key after 'updatetime'. Items without
- circular references are always freed when they become unused.
- This is useful if you have deleted a very big |List| and/or
- |Dictionary| with circular references in a script that runs
- for a long time.
-
- When the optional {atexit} argument is one, garbage
- collection will also be done when exiting Vim, if it wasn't
- done before. This is useful when checking for memory leaks.
-
- The garbage collection is not done immediately but only when
- it's safe to perform. This is when waiting for the user to
- type a character.
-
-get({list}, {idx} [, {default}]) *get()*
- Get item {idx} from |List| {list}. When this item is not
- available return {default}. Return zero when {default} is
- omitted.
- Can also be used as a |method|: >
- mylist->get(idx)
-get({blob}, {idx} [, {default}])
- Get byte {idx} from |Blob| {blob}. When this byte is not
- available return {default}. Return -1 when {default} is
- omitted.
-get({dict}, {key} [, {default}])
- Get item with key {key} from |Dictionary| {dict}. When this
- item is not available return {default}. Return zero when
- {default} is omitted. Useful example: >
- let val = get(g:, 'var_name', 'default')
-< This gets the value of g:var_name if it exists, and uses
- 'default' when it does not exist.
-get({func}, {what})
- Get item {what} from Funcref {func}. Possible values for
- {what} are:
- "name" The function name
- "func" The function
- "dict" The dictionary
- "args" The list with arguments
-
- *getbufinfo()*
-getbufinfo([{buf}])
-getbufinfo([{dict}])
- Get information about buffers as a List of Dictionaries.
-
- Without an argument information about all the buffers is
- returned.
-
- When the argument is a |Dictionary| only the buffers matching
- the specified criteria are returned. The following keys can
- be specified in {dict}:
- buflisted include only listed buffers.
- bufloaded include only loaded buffers.
- bufmodified include only modified buffers.
-
- Otherwise, {buf} specifies a particular buffer to return
- information for. For the use of {buf}, see |bufname()|
- above. If the buffer is found the returned List has one item.
- Otherwise the result is an empty list.
-
- Each returned List item is a dictionary with the following
- entries:
- bufnr Buffer number.
- changed TRUE if the buffer is modified.
- changedtick Number of changes made to the buffer.
- hidden TRUE if the buffer is hidden.
- lastused Timestamp in seconds, like
- |localtime()|, when the buffer was
- last used.
- listed TRUE if the buffer is listed.
- lnum Line number used for the buffer when
- opened in the current window.
- Only valid if the buffer has been
- displayed in the window in the past.
- If you want the line number of the
- last known cursor position in a given
- window, use |line()|: >
- :echo line('.', {winid})
-<
- linecount Number of lines in the buffer (only
- valid when loaded)
- loaded TRUE if the buffer is loaded.
- name Full path to the file in the buffer.
- signs List of signs placed in the buffer.
- Each list item is a dictionary with
- the following fields:
- id sign identifier
- lnum line number
- name sign name
- variables A reference to the dictionary with
- buffer-local variables.
- windows List of |window-ID|s that display this
- buffer
-
- Examples: >
- for buf in getbufinfo()
- echo buf.name
- endfor
- for buf in getbufinfo({'buflisted':1})
- if buf.changed
- ....
- endif
- endfor
-<
- To get buffer-local options use: >
- getbufvar({bufnr}, '&option_name')
-<
- Can also be used as a |method|: >
- GetBufnr()->getbufinfo()
-<
- *getbufline()*
-getbufline({buf}, {lnum} [, {end}])
- Return a |List| with the lines starting from {lnum} to {end}
- (inclusive) in the buffer {buf}. If {end} is omitted, a
- |List| with only the line {lnum} is returned.
-
- For the use of {buf}, see |bufname()| above.
-
- For {lnum} and {end} "$" can be used for the last line of the
- buffer. Otherwise a number must be used.
-
- When {lnum} is smaller than 1 or bigger than the number of
- lines in the buffer, an empty |List| is returned.
-
- When {end} is greater than the number of lines in the buffer,
- it is treated as {end} is set to the number of lines in the
- buffer. When {end} is before {lnum} an empty |List| is
- returned.
-
- This function works only for loaded buffers. For unloaded and
- non-existing buffers, an empty |List| is returned.
-
- Example: >
- :let lines = getbufline(bufnr("myfile"), 1, "$")
-
-< Can also be used as a |method|: >
- GetBufnr()->getbufline(lnum)
-
-getbufvar({buf}, {varname} [, {def}]) *getbufvar()*
- The result is the value of option or local buffer variable
- {varname} in buffer {buf}. Note that the name without "b:"
- must be used.
- The {varname} argument is a string.
- When {varname} is empty returns a |Dictionary| with all the
- buffer-local variables.
- When {varname} is equal to "&" returns a |Dictionary| with all
- the buffer-local options.
- Otherwise, when {varname} starts with "&" returns the value of
- a buffer-local option.
- This also works for a global or buffer-local option, but it
- doesn't work for a global variable, window-local variable or
- window-local option.
- For the use of {buf}, see |bufname()| above.
- When the buffer or variable doesn't exist {def} or an empty
- string is returned, there is no error message.
- Examples: >
- :let bufmodified = getbufvar(1, "&mod")
- :echo "todo myvar = " . getbufvar("todo", "myvar")
-
-< Can also be used as a |method|: >
- GetBufnr()->getbufvar(varname)
-<
-getchangelist([{buf}]) *getchangelist()*
- Returns the |changelist| for the buffer {buf}. For the use
- of {buf}, see |bufname()| above. If buffer {buf} doesn't
- exist, an empty list is returned.
-
- The returned list contains two entries: a list with the change
- locations and the current position in the list. Each
- entry in the change list is a dictionary with the following
- entries:
- col column number
- coladd column offset for 'virtualedit'
- lnum line number
- If buffer {buf} is the current buffer, then the current
- position refers to the position in the list. For other
- buffers, it is set to the length of the list.
-
- Can also be used as a |method|: >
- GetBufnr()->getchangelist()
-
-getchar([expr]) *getchar()*
- Get a single character from the user or input stream.
- If [expr] is omitted, wait until a character is available.
- If [expr] is 0, only get a character when one is available.
- Return zero otherwise.
- If [expr] is 1, only check if a character is available, it is
- not consumed. Return zero if no character available.
- If you prefer always getting a string use |getcharstr()|.
-
- Without [expr] and when [expr] is 0 a whole character or
- special key is returned. If it is a single character, the
- result is a number. Use nr2char() to convert it to a String.
- Otherwise a String is returned with the encoded character.
- For a special key it's a String with a sequence of bytes
- starting with 0x80 (decimal: 128). This is the same value as
- the String "\<Key>", e.g., "\<Left>". The returned value is
- also a String when a modifier (shift, control, alt) was used
- that is not included in the character.
-
- When [expr] is 0 and Esc is typed, there will be a short delay
- while Vim waits to see if this is the start of an escape
- sequence.
-
- When [expr] is 1 only the first byte is returned. For a
- one-byte character it is the character itself as a number.
- Use nr2char() to convert it to a String.
-
- Use getcharmod() to obtain any additional modifiers.
-
- When the user clicks a mouse button, the mouse event will be
- returned. The position can then be found in |v:mouse_col|,
- |v:mouse_lnum|, |v:mouse_winid| and |v:mouse_win|.
- |getmousepos()| can also be used. Mouse move events will be
- ignored.
- This example positions the mouse as it would normally happen: >
- let c = getchar()
- if c == "\<LeftMouse>" && v:mouse_win > 0
- exe v:mouse_win . "wincmd w"
- exe v:mouse_lnum
- exe "normal " . v:mouse_col . "|"
- endif
-<
- There is no prompt, you will somehow have to make clear to the
- user that a character has to be typed. The screen is not
- redrawn, e.g. when resizing the window.
-
- There is no mapping for the character.
- Key codes are replaced, thus when the user presses the <Del>
- key you get the code for the <Del> key, not the raw character
- sequence. Examples: >
- getchar() == "\<Del>"
- getchar() == "\<S-Left>"
-< This example redefines "f" to ignore case: >
- :nmap f :call FindChar()<CR>
- :function FindChar()
- : let c = nr2char(getchar())
- : while col('.') < col('$') - 1
- : normal l
- : if getline('.')[col('.') - 1] ==? c
- : break
- : endif
- : endwhile
- :endfunction
-<
-getcharmod() *getcharmod()*
- The result is a Number which is the state of the modifiers for
- the last obtained character with getchar() or in another way.
- These values are added together:
- 2 shift
- 4 control
- 8 alt (meta)
- 16 meta (when it's different from ALT)
- 32 mouse double click
- 64 mouse triple click
- 96 mouse quadruple click (== 32 + 64)
- 128 command (Macintosh only)
- Only the modifiers that have not been included in the
- character itself are obtained. Thus Shift-a results in "A"
- without a modifier.
-
-getcharsearch() *getcharsearch()*
- Return the current character search information as a {dict}
- with the following entries:
-
- char character previously used for a character
- search (|t|, |f|, |T|, or |F|); empty string
- if no character search has been performed
- forward direction of character search; 1 for forward,
- 0 for backward
- until type of character search; 1 for a |t| or |T|
- character search, 0 for an |f| or |F|
- character search
-
- This can be useful to always have |;| and |,| search
- forward/backward regardless of the direction of the previous
- character search: >
- :nnoremap <expr> ; getcharsearch().forward ? ';' : ','
- :nnoremap <expr> , getcharsearch().forward ? ',' : ';'
-< Also see |setcharsearch()|.
-
-
-getcharstr([expr]) *getcharstr()*
- Get a single character from the user or input stream as a
- string.
- If [expr] is omitted, wait until a character is available.
- If [expr] is 0 or false, only get a character when one is
- available. Return an empty string otherwise.
- If [expr] is 1 or true, only check if a character is
- available, it is not consumed. Return an empty string
- if no character is available.
- Otherwise this works like |getchar()|, except that a number
- result is converted to a string.
-
-
-getcmdline() *getcmdline()*
- Return the current command-line. Only works when the command
- line is being edited, thus requires use of |c_CTRL-\_e| or
- |c_CTRL-R_=|.
- Example: >
- :cmap <F7> <C-\>eescape(getcmdline(), ' \')<CR>
-< Also see |getcmdtype()|, |getcmdpos()| and |setcmdpos()|.
- Returns an empty string when entering a password or using
- |inputsecret()|.
-
-getcmdpos() *getcmdpos()*
- Return the position of the cursor in the command line as a
- byte count. The first column is 1.
- Only works when editing the command line, thus requires use of
- |c_CTRL-\_e| or |c_CTRL-R_=| or an expression mapping.
- Returns 0 otherwise.
- Also see |getcmdtype()|, |setcmdpos()| and |getcmdline()|.
-
-getcmdtype() *getcmdtype()*
- Return the current command-line type. Possible return values
- are:
- : normal Ex command
- > debug mode command |debug-mode|
- / forward search command
- ? backward search command
- @ |input()| command
- - |:insert| or |:append| command
- = |i_CTRL-R_=|
- Only works when editing the command line, thus requires use of
- |c_CTRL-\_e| or |c_CTRL-R_=| or an expression mapping.
- Returns an empty string otherwise.
- Also see |getcmdpos()|, |setcmdpos()| and |getcmdline()|.
-
-getcmdwintype() *getcmdwintype()*
- Return the current |command-line-window| type. Possible return
- values are the same as |getcmdtype()|. Returns an empty string
- when not in the command-line window.
-
-getcompletion({pat}, {type} [, {filtered}]) *getcompletion()*
- Return a list of command-line completion matches. The String
- {type} argument specifies what for. The following completion
- types are supported:
-
- arglist file names in argument list
- augroup autocmd groups
- buffer buffer names
- behave :behave suboptions
- cmdline |cmdline-completion| result
- color color schemes
- command Ex command
- compiler compilers
- cscope |:cscope| suboptions
- diff_buffer |:diffget| and |:diffput| completion
- dir directory names
- environment environment variable names
- event autocommand events
- expression Vim expression
- file file and directory names
- file_in_path file and directory names in |'path'|
- filetype filetype names |'filetype'|
- function function name
- help help subjects
- highlight highlight groups
- history :history suboptions
- locale locale names (as output of locale -a)
- mapclear buffer argument
- mapping mapping name
- menu menus
- messages |:messages| suboptions
- option options
- packadd optional package |pack-add| names
- shellcmd Shell command
- sign |:sign| suboptions
- syntax syntax file names |'syntax'|
- syntime |:syntime| suboptions
- tag tags
- tag_listfiles tags, file names
- user user names
- var user variables
-
- If {pat} is an empty string, then all the matches are
- returned. Otherwise only items matching {pat} are returned.
- See |wildcards| for the use of special characters in {pat}.
-
- If the optional {filtered} flag is set to 1, then 'wildignore'
- is applied to filter the results. Otherwise all the matches
- are returned. The 'wildignorecase' option always applies.
-
- If {type} is "cmdline", then the |cmdline-completion| result is
- returned. For example, to complete the possible values after
- a ":call" command: >
- echo getcompletion('call ', 'cmdline')
-<
- If there are no matches, an empty list is returned. An
- invalid value for {type} produces an error.
-
- Can also be used as a |method|: >
- GetPattern()->getcompletion('color')
-<
- *getcurpos()*
-getcurpos() Get the position of the cursor. This is like getpos('.'), but
- includes an extra "curswant" in the list:
- [0, lnum, col, off, curswant] ~
- The "curswant" number is the preferred column when moving the
- cursor vertically. Also see |getpos()|.
- The first "bufnum" item is always zero.
-
- This can be used to save and restore the cursor position: >
- let save_cursor = getcurpos()
- MoveTheCursorAround
- call setpos('.', save_cursor)
-< Note that this only works within the window. See
- |winrestview()| for restoring more state.
-
-getcwd([{winnr}[, {tabnr}]]) *getcwd()*
- With no arguments, returns the name of the effective
- |current-directory|. With {winnr} or {tabnr} the working
- directory of that scope is returned, and 'autochdir' is
- ignored.
- Tabs and windows are identified by their respective numbers,
- 0 means current tab or window. Missing tab number implies 0.
- Thus the following are equivalent: >
- getcwd(0)
- getcwd(0, 0)
-< If {winnr} is -1 it is ignored, only the tab is resolved.
- {winnr} can be the window number or the |window-ID|.
- If both {winnr} and {tabnr} are -1 the global working
- directory is returned.
- Throw error if the arguments are invalid. |E5000| |E5001| |E5002|
-
- Can also be used as a |method|: >
- GetWinnr()->getcwd()
-
-getenv({name}) *getenv()*
- Return the value of environment variable {name}. The {name}
- argument is a string, without a leading '$'. Example: >
- myHome = getenv('HOME')
-
-< When the variable does not exist |v:null| is returned. That
- is different from a variable set to an empty string.
- See also |expr-env|.
-
- Can also be used as a |method|: >
- GetVarname()->getenv()
-
-getfontname([{name}]) *getfontname()*
- Without an argument returns the name of the normal font being
- used. Like what is used for the Normal highlight group
- |hl-Normal|.
- With an argument a check is done whether String {name} is a
- valid font name. If not then an empty string is returned.
- Otherwise the actual font name is returned, or {name} if the
- GUI does not support obtaining the real name.
- Only works when the GUI is running, thus not in your vimrc or
- gvimrc file. Use the |GUIEnter| autocommand to use this
- function just after the GUI has started.
-
-getfperm({fname}) *getfperm()*
- The result is a String, which is the read, write, and execute
- permissions of the given file {fname}.
- If {fname} does not exist or its directory cannot be read, an
- empty string is returned.
- The result is of the form "rwxrwxrwx", where each group of
- "rwx" flags represent, in turn, the permissions of the owner
- of the file, the group the file belongs to, and other users.
- If a user does not have a given permission the flag for this
- is replaced with the string "-". Examples: >
- :echo getfperm("/etc/passwd")
- :echo getfperm(expand("~/.config/nvim/init.vim"))
-< This will hopefully (from a security point of view) display
- the string "rw-r--r--" or even "rw-------".
-
- Can also be used as a |method|: >
- GetFilename()->getfperm()
-<
- For setting permissions use |setfperm()|.
-
-getfsize({fname}) *getfsize()*
- The result is a Number, which is the size in bytes of the
- given file {fname}.
- If {fname} is a directory, 0 is returned.
- If the file {fname} can't be found, -1 is returned.
- If the size of {fname} is too big to fit in a Number then -2
- is returned.
-
- Can also be used as a |method|: >
- GetFilename()->getfsize()
-
-getftime({fname}) *getftime()*
- The result is a Number, which is the last modification time of
- the given file {fname}. The value is measured as seconds
- since 1st Jan 1970, and may be passed to strftime(). See also
- |localtime()| and |strftime()|.
- If the file {fname} can't be found -1 is returned.
-
- Can also be used as a |method|: >
- GetFilename()->getftime()
-
-getftype({fname}) *getftype()*
- The result is a String, which is a description of the kind of
- file of the given file {fname}.
- If {fname} does not exist an empty string is returned.
- Here is a table over different kinds of files and their
- results:
- Normal file "file"
- Directory "dir"
- Symbolic link "link"
- Block device "bdev"
- Character device "cdev"
- Socket "socket"
- FIFO "fifo"
- All other "other"
- Example: >
- getftype("/home")
-< Note that a type such as "link" will only be returned on
- systems that support it. On some systems only "dir" and
- "file" are returned.
-
- Can also be used as a |method|: >
- GetFilename()->getftype()
-
-getjumplist([{winnr} [, {tabnr}]]) *getjumplist()*
- Returns the |jumplist| for the specified window.
-
- Without arguments use the current window.
- With {winnr} only use this window in the current tab page.
- {winnr} can also be a |window-ID|.
- With {winnr} and {tabnr} use the window in the specified tab
- page.
-
- The returned list contains two entries: a list with the jump
- locations and the last used jump position number in the list.
- Each entry in the jump location list is a dictionary with
- the following entries:
- bufnr buffer number
- col column number
- coladd column offset for 'virtualedit'
- filename filename if available
- lnum line number
-
- Can also be used as a |method|: >
- GetWinnr()->getjumplist()
-
-< *getline()*
-getline({lnum} [, {end}])
- Without {end} the result is a String, which is line {lnum}
- from the current buffer. Example: >
- getline(1)
-< When {lnum} is a String that doesn't start with a
- digit, |line()| is called to translate the String into a Number.
- To get the line under the cursor: >
- getline(".")
-< When {lnum} is a number smaller than 1 or bigger than the
- number of lines in the buffer, an empty string is returned.
-
- When {end} is given the result is a |List| where each item is
- a line from the current buffer in the range {lnum} to {end},
- including line {end}.
- {end} is used in the same way as {lnum}.
- Non-existing lines are silently omitted.
- When {end} is before {lnum} an empty |List| is returned.
- Example: >
- :let start = line('.')
- :let end = search("^$") - 1
- :let lines = getline(start, end)
-
-< Can also be used as a |method|: >
- ComputeLnum()->getline()
-
-< To get lines from another buffer see |getbufline()|
-
-getloclist({nr},[, {what}]) *getloclist()*
- Returns a |List| with all the entries in the location list for
- window {nr}. {nr} can be the window number or the |window-ID|.
- When {nr} is zero the current window is used.
-
- For a location list window, the displayed location list is
- returned. For an invalid window number {nr}, an empty list is
- returned. Otherwise, same as |getqflist()|.
-
- If the optional {what} dictionary argument is supplied, then
- returns the items listed in {what} as a dictionary. Refer to
- |getqflist()| for the supported items in {what}.
- If {what} contains 'filewinid', then returns the id of the
- window used to display files from the location list. This
- field is applicable only when called from a location list
- window. See |location-list-file-window| for more details.
-
- Returns a |Dictionary| with default values if there is no
- location list for the window {nr}.
- Returns an empty Dictionary if window {nr} does not exist.
-
- Examples (See also |getqflist-examples|): >
- :echo getloclist(3, {'all': 0})
- :echo getloclist(5, {'filewinid': 0})
-
-
-getmarklist([{buf}]) *getmarklist()*
- Without the {buf} argument returns a |List| with information
- about all the global marks. |mark|
-
- If the optional {buf} argument is specified, returns the
- local marks defined in buffer {buf}. For the use of {buf},
- see |bufname()|.
-
- Each item in the returned List is a |Dict| with the following:
- mark name of the mark prefixed by "'"
- pos a |List| with the position of the mark:
- [bufnum, lnum, col, off]
- Refer to |getpos()| for more information.
- file file name
-
- Refer to |getpos()| for getting information about a specific
- mark.
-
- Can also be used as a |method|: >
- GetBufnr()->getmarklist()
-
-getmatches([{win}]) *getmatches()*
- Returns a |List| with all matches previously defined for the
- current window by |matchadd()| and the |:match| commands.
- |getmatches()| is useful in combination with |setmatches()|,
- as |setmatches()| can restore a list of matches saved by
- |getmatches()|.
- If {win} is specified, use the window with this number or
- window ID instead of the current window.
- Example: >
- :echo getmatches()
-< [{'group': 'MyGroup1', 'pattern': 'TODO',
- 'priority': 10, 'id': 1}, {'group': 'MyGroup2',
- 'pattern': 'FIXME', 'priority': 10, 'id': 2}] >
- :let m = getmatches()
- :call clearmatches()
- :echo getmatches()
-< [] >
- :call setmatches(m)
- :echo getmatches()
-< [{'group': 'MyGroup1', 'pattern': 'TODO',
- 'priority': 10, 'id': 1}, {'group': 'MyGroup2',
- 'pattern': 'FIXME', 'priority': 10, 'id': 2}] >
- :unlet m
-<
-getmousepos() *getmousepos()*
- Returns a Dictionary with the last known position of the
- mouse. This can be used in a mapping for a mouse click. The
- items are:
- screenrow screen row
- screencol screen column
- winid Window ID of the click
- winrow row inside "winid"
- wincol column inside "winid"
- line text line inside "winid"
- column text column inside "winid"
- All numbers are 1-based.
-
- If not over a window, e.g. when in the command line, then only
- "screenrow" and "screencol" are valid, the others are zero.
-
- When on the status line below a window or the vertical
- separater right of a window, the "line" and "column" values
- are zero.
-
- When the position is after the text then "column" is the
- length of the text in bytes plus one.
-
- If the mouse is over a focusable floating window then that
- window is used.
-
- When using |getchar()| the Vim variables |v:mouse_lnum|,
- |v:mouse_col| and |v:mouse_winid| also provide these values.
-
- *getpid()*
-getpid() Return a Number which is the process ID of the Vim process.
- This is a unique number, until Vim exits.
-
- *getpos()*
-getpos({expr}) Get the position for String {expr}. For possible values of
- {expr} see |line()|. For getting the cursor position see
- |getcurpos()|.
- The result is a |List| with four numbers:
- [bufnum, lnum, col, off]
- "bufnum" is zero, unless a mark like '0 or 'A is used, then it
- is the buffer number of the mark.
- "lnum" and "col" are the position in the buffer. The first
- column is 1.
- The "off" number is zero, unless 'virtualedit' is used. Then
- it is the offset in screen columns from the start of the
- character. E.g., a position within a <Tab> or after the last
- character.
- Note that for '< and '> Visual mode matters: when it is "V"
- (visual line mode) the column of '< is zero and the column of
- '> is a large number.
- The column number in the returned List is the byte position
- within the line.
- The column number can be very large, e.g. 2147483647, in which
- case it means "after the end of the line".
- This can be used to save and restore the position of a mark: >
- let save_a_mark = getpos("'a")
- ...
- call setpos("'a", save_a_mark)
-< Also see |getcurpos()| and |setpos()|.
-
- Can also be used as a |method|: >
- GetMark()->getpos()
-
-getqflist([{what}]) *getqflist()*
- Returns a |List| with all the current quickfix errors. Each
- list item is a dictionary with these entries:
- bufnr number of buffer that has the file name, use
- bufname() to get the name
- module module name
- lnum line number in the buffer (first line is 1)
- end_lnum
- end of line number if the item is multiline
- col column number (first column is 1)
- end_col end of column number if the item has range
- vcol |TRUE|: "col" is visual column
- |FALSE|: "col" is byte index
- nr error number
- pattern search pattern used to locate the error
- text description of the error
- type type of the error, 'E', '1', etc.
- valid |TRUE|: recognized error message
-
- When there is no error list or it's empty, an empty list is
- returned. Quickfix list entries with a non-existing buffer
- number are returned with "bufnr" set to zero (Note: some
- functions accept buffer number zero for the alternate buffer,
- you may need to explicitly check for zero).
-
- Useful application: Find pattern matches in multiple files and
- do something with them: >
- :vimgrep /theword/jg *.c
- :for d in getqflist()
- : echo bufname(d.bufnr) ':' d.lnum '=' d.text
- :endfor
-<
- If the optional {what} dictionary argument is supplied, then
- returns only the items listed in {what} as a dictionary. The
- following string items are supported in {what}:
- changedtick get the total number of changes made
- to the list |quickfix-changedtick|
- context get the |quickfix-context|
- efm errorformat to use when parsing "lines". If
- not present, then the 'errorformat' option
- value is used.
- id get information for the quickfix list with
- |quickfix-ID|; zero means the id for the
- current list or the list specified by "nr"
- idx get information for the quickfix entry at this
- index in the list specified by 'id' or 'nr'.
- If set to zero, then uses the current entry.
- See |quickfix-index|
- items quickfix list entries
- lines parse a list of lines using 'efm' and return
- the resulting entries. Only a |List| type is
- accepted. The current quickfix list is not
- modified. See |quickfix-parse|.
- nr get information for this quickfix list; zero
- means the current quickfix list and "$" means
- the last quickfix list
- size number of entries in the quickfix list
- title get the list title |quickfix-title|
- winid get the quickfix |window-ID|
- all all of the above quickfix properties
- Non-string items in {what} are ignored. To get the value of a
- particular item, set it to zero.
- If "nr" is not present then the current quickfix list is used.
- If both "nr" and a non-zero "id" are specified, then the list
- specified by "id" is used.
- To get the number of lists in the quickfix stack, set "nr" to
- "$" in {what}. The "nr" value in the returned dictionary
- contains the quickfix stack size.
- When "lines" is specified, all the other items except "efm"
- are ignored. The returned dictionary contains the entry
- "items" with the list of entries.
-
- The returned dictionary contains the following entries:
- changedtick total number of changes made to the
- list |quickfix-changedtick|
- context quickfix list context. See |quickfix-context|
- If not present, set to "".
- id quickfix list ID |quickfix-ID|. If not
- present, set to 0.
- idx index of the quickfix entry in the list. If not
- present, set to 0.
- items quickfix list entries. If not present, set to
- an empty list.
- nr quickfix list number. If not present, set to 0
- size number of entries in the quickfix list. If not
- present, set to 0.
- title quickfix list title text. If not present, set
- to "".
- winid quickfix |window-ID|. If not present, set to 0
-
- Examples (See also |getqflist-examples|): >
- :echo getqflist({'all': 1})
- :echo getqflist({'nr': 2, 'title': 1})
- :echo getqflist({'lines' : ["F1:10:L10"]})
-<
-getreg([{regname} [, 1 [, {list}]]]) *getreg()*
- The result is a String, which is the contents of register
- {regname}. Example: >
- :let cliptext = getreg('*')
-< When register {regname} was not set the result is an empty
- string.
- The {regname} argument must be a string.
-
- getreg('=') returns the last evaluated value of the expression
- register. (For use in maps.)
- getreg('=', 1) returns the expression itself, so that it can
- be restored with |setreg()|. For other registers the extra
- argument is ignored, thus you can always give it.
-
- If {list} is present and |TRUE|, the result type is changed
- to |List|. Each list item is one text line. Use it if you care
- about zero bytes possibly present inside register: without
- third argument both NLs and zero bytes are represented as NLs
- (see |NL-used-for-Nul|).
- When the register was not set an empty list is returned.
-
- If {regname} is not specified, |v:register| is used.
-
- Can also be used as a |method|: >
- GetRegname()->getreg()
-
-getreginfo([{regname}]) *getreginfo()*
- Returns detailed information about register {regname} as a
- Dictionary with the following entries:
- regcontents List of lines contained in register
- {regname}, like
- |getreg|({regname}, 1, 1).
- regtype the type of register {regname}, as in
- |getregtype()|.
- isunnamed Boolean flag, v:true if this register
- is currently pointed to by the unnamed
- register.
- points_to for the unnamed register, gives the
- single letter name of the register
- currently pointed to (see |quotequote|).
- For example, after deleting a line
- with `dd`, this field will be "1",
- which is the register that got the
- deleted text.
-
- The {regname} argument is a string. If {regname} is invalid
- or not set, an empty Dictionary will be returned.
- If {regname} is not specified, |v:register| is used.
- The returned Dictionary can be passed to |setreg()|.
-
- Can also be used as a |method|: >
- GetRegname()->getreginfo()
-
-getregtype([{regname}]) *getregtype()*
- The result is a String, which is type of register {regname}.
- The value will be one of:
- "v" for |charwise| text
- "V" for |linewise| text
- "<CTRL-V>{width}" for |blockwise-visual| text
- "" for an empty or unknown register
- <CTRL-V> is one character with value 0x16.
- The {regname} argument is a string. If {regname} is not
- specified, |v:register| is used.
-
- Can also be used as a |method|: >
- GetRegname()->getregtype()
-
-gettabinfo([{tabnr}]) *gettabinfo()*
- If {tabnr} is not specified, then information about all the
- tab pages is returned as a |List|. Each List item is a
- |Dictionary|. Otherwise, {tabnr} specifies the tab page
- number and information about that one is returned. If the tab
- page does not exist an empty List is returned.
-
- Each List item is a |Dictionary| with the following entries:
- tabnr tab page number.
- variables a reference to the dictionary with
- tabpage-local variables
- windows List of |window-ID|s in the tab page.
-
- Can also be used as a |method|: >
- GetTabnr()->gettabinfo()
-
-gettabvar({tabnr}, {varname} [, {def}]) *gettabvar()*
- Get the value of a tab-local variable {varname} in tab page
- {tabnr}. |t:var|
- Tabs are numbered starting with one.
- The {varname} argument is a string. When {varname} is empty a
- dictionary with all tab-local variables is returned.
- Note that the name without "t:" must be used.
- When the tab or variable doesn't exist {def} or an empty
- string is returned, there is no error message.
-
- Can also be used as a |method|: >
- GetTabnr()->gettabvar(varname)
-
-gettabwinvar({tabnr}, {winnr}, {varname} [, {def}]) *gettabwinvar()*
- Get the value of window-local variable {varname} in window
- {winnr} in tab page {tabnr}.
- The {varname} argument is a string. When {varname} is empty a
- dictionary with all window-local variables is returned.
- When {varname} is equal to "&" get the values of all
- window-local options in a |Dictionary|.
- Otherwise, when {varname} starts with "&" get the value of a
- window-local option.
- Note that {varname} must be the name without "w:".
- Tabs are numbered starting with one. For the current tabpage
- use |getwinvar()|.
- {winnr} can be the window number or the |window-ID|.
- When {winnr} is zero the current window is used.
- This also works for a global option, buffer-local option and
- window-local option, but it doesn't work for a global variable
- or buffer-local variable.
- When the tab, window or variable doesn't exist {def} or an
- empty string is returned, there is no error message.
- Examples: >
- :let list_is_on = gettabwinvar(1, 2, '&list')
- :echo "myvar = " . gettabwinvar(3, 1, 'myvar')
-<
- To obtain all window-local variables use: >
- gettabwinvar({tabnr}, {winnr}, '&')
-
-< Can also be used as a |method|: >
- GetTabnr()->gettabwinvar(winnr, varname)
-
-gettagstack([{winnr}]) *gettagstack()*
- The result is a Dict, which is the tag stack of window {winnr}.
- {winnr} can be the window number or the |window-ID|.
- When {winnr} is not specified, the current window is used.
- When window {winnr} doesn't exist, an empty Dict is returned.
-
- The returned dictionary contains the following entries:
- curidx Current index in the stack. When at
- top of the stack, set to (length + 1).
- Index of bottom of the stack is 1.
- items List of items in the stack. Each item
- is a dictionary containing the
- entries described below.
- length Number of entries in the stack.
-
- Each item in the stack is a dictionary with the following
- entries:
- bufnr buffer number of the current jump
- from cursor position before the tag jump.
- See |getpos()| for the format of the
- returned list.
- matchnr current matching tag number. Used when
- multiple matching tags are found for a
- name.
- tagname name of the tag
-
- See |tagstack| for more information about the tag stack.
-
- Can also be used as a |method|: >
- GetWinnr()->gettagstack()
-
-getwininfo([{winid}]) *getwininfo()*
- Returns information about windows as a |List| with Dictionaries.
-
- If {winid} is given Information about the window with that ID
- is returned, as a |List| with one item. If the window does not
- exist the result is an empty list.
-
- Without {winid} information about all the windows in all the
- tab pages is returned.
-
- Each List item is a |Dictionary| with the following entries:
- botline last complete displayed buffer line
- bufnr number of buffer in the window
- height window height (excluding winbar)
- loclist 1 if showing a location list
- quickfix 1 if quickfix or location list window
- terminal 1 if a terminal window
- tabnr tab page number
- topline first displayed buffer line
- variables a reference to the dictionary with
- window-local variables
- width window width
- winbar 1 if the window has a toolbar, 0
- otherwise
- wincol leftmost screen column of the window;
- "col" from |win_screenpos()|
- textoff number of columns occupied by any
- 'foldcolumn', 'signcolumn' and line
- number in front of the text
- winid |window-ID|
- winnr window number
- winrow topmost screen line of the window;
- "row" from |win_screenpos()|
-
- Can also be used as a |method|: >
- GetWinnr()->getwininfo()
-
-getwinpos([{timeout}]) *getwinpos()*
- The result is a |List| with two numbers, the result of
- |getwinposx()| and |getwinposy()| combined:
- [x-pos, y-pos]
- {timeout} can be used to specify how long to wait in msec for
- a response from the terminal. When omitted 100 msec is used.
-
- Use a longer time for a remote terminal.
- When using a value less than 10 and no response is received
- within that time, a previously reported position is returned,
- if available. This can be used to poll for the position and
- do some work in the meantime: >
- while 1
- let res = getwinpos(1)
- if res[0] >= 0
- break
- endif
- " Do some work here
- endwhile
-<
- Can also be used as a |method|: >
- GetTimeout()->getwinpos()
-<
- *getwinposx()*
-getwinposx() The result is a Number, which is the X coordinate in pixels of
- the left hand side of the GUI Vim window. The result will be
- -1 if the information is not available.
- The value can be used with `:winpos`.
-
- *getwinposy()*
-getwinposy() The result is a Number, which is the Y coordinate in pixels of
- the top of the GUI Vim window. The result will be -1 if the
- information is not available.
- The value can be used with `:winpos`.
-
-getwinvar({winnr}, {varname} [, {def}]) *getwinvar()*
- Like |gettabwinvar()| for the current tabpage.
- Examples: >
- :let list_is_on = getwinvar(2, '&list')
- :echo "myvar = " . getwinvar(1, 'myvar')
-
-< Can also be used as a |method|: >
- GetWinnr()->getwinvar(varname)
-<
-glob({expr} [, {nosuf} [, {list} [, {alllinks}]]]) *glob()*
- Expand the file wildcards in {expr}. See |wildcards| for the
- use of special characters.
-
- Unless the optional {nosuf} argument is given and is |TRUE|,
- the 'suffixes' and 'wildignore' options apply: Names matching
- one of the patterns in 'wildignore' will be skipped and
- 'suffixes' affect the ordering of matches.
- 'wildignorecase' always applies.
-
- When {list} is present and it is |TRUE| the result is a |List|
- with all matching files. The advantage of using a List is,
- you also get filenames containing newlines correctly.
- Otherwise the result is a String and when there are several
- matches, they are separated by <NL> characters.
-
- If the expansion fails, the result is an empty String or List.
-
- You can also use |readdir()| if you need to do complicated
- things, such as limiting the number of matches.
-
- A name for a non-existing file is not included. A symbolic
- link is only included if it points to an existing file.
- However, when the {alllinks} argument is present and it is
- |TRUE| then all symbolic links are included.
-
- For most systems backticks can be used to get files names from
- any external command. Example: >
- :let tagfiles = glob("`find . -name tags -print`")
- :let &tags = substitute(tagfiles, "\n", ",", "g")
-< The result of the program inside the backticks should be one
- item per line. Spaces inside an item are allowed.
-
- See |expand()| for expanding special Vim variables. See
- |system()| for getting the raw output of an external command.
-
- Can also be used as a |method|: >
- GetExpr()->glob()
-
-glob2regpat({string}) *glob2regpat()*
- Convert a file pattern, as used by glob(), into a search
- pattern. The result can be used to match with a string that
- is a file name. E.g. >
- if filename =~ glob2regpat('Make*.mak')
-< This is equivalent to: >
- if filename =~ '^Make.*\.mak$'
-< When {string} is an empty string the result is "^$", match an
- empty string.
- Note that the result depends on the system. On MS-Windows
- a backslash usually means a path separator.
-
- Can also be used as a |method|: >
- GetExpr()->glob2regpat()
-< *globpath()*
-globpath({path}, {expr} [, {nosuf} [, {list} [, {allinks}]]])
- Perform glob() for String {expr} on all directories in {path}
- and concatenate the results. Example: >
- :echo globpath(&rtp, "syntax/c.vim")
-<
- {path} is a comma-separated list of directory names. Each
- directory name is prepended to {expr} and expanded like with
- |glob()|. A path separator is inserted when needed.
- To add a comma inside a directory name escape it with a
- backslash. Note that on MS-Windows a directory may have a
- trailing backslash, remove it if you put a comma after it.
- If the expansion fails for one of the directories, there is no
- error message.
-
- Unless the optional {nosuf} argument is given and is |TRUE|,
- the 'suffixes' and 'wildignore' options apply: Names matching
- one of the patterns in 'wildignore' will be skipped and
- 'suffixes' affect the ordering of matches.
-
- When {list} is present and it is |TRUE| the result is a |List|
- with all matching files. The advantage of using a List is, you
- also get filenames containing newlines correctly. Otherwise
- the result is a String and when there are several matches,
- they are separated by <NL> characters. Example: >
- :echo globpath(&rtp, "syntax/c.vim", 0, 1)
-<
- {allinks} is used as with |glob()|.
-
- The "**" item can be used to search in a directory tree.
- For example, to find all "README.txt" files in the directories
- in 'runtimepath' and below: >
- :echo globpath(&rtp, "**/README.txt")
-< Upwards search and limiting the depth of "**" is not
- supported, thus using 'path' will not always work properly.
-
- Can also be used as a |method|, the base is passed as the
- second argument: >
- GetExpr()->globpath(&rtp)
-<
- *has()*
-has({feature}) Returns 1 if {feature} is supported, 0 otherwise. The
- {feature} argument is a feature name like "nvim-0.2.1" or
- "win32", see below. See also |exists()|.
-
- If the code has a syntax error, then Nvim may skip the rest
- of the line and miss |:endif|. >
- if has('feature') | let x = this->breaks->without->the->feature | endif
-<
- Put |:if| and |:endif| on separate lines to avoid the
- syntax error. >
- if has('feature')
- let x = this->breaks->without->the->feature
- endif
-<
- Vim's compile-time feature-names (prefixed with "+") are not
- recognized because Nvim is always compiled with all possible
- features. |feature-compile|
-
- Feature names can be:
- 1. Nvim version. For example the "nvim-0.2.1" feature means
- that Nvim is version 0.2.1 or later: >
- :if has("nvim-0.2.1")
-
-< 2. Runtime condition or other pseudo-feature. For example the
- "win32" feature checks if the current system is Windows: >
- :if has("win32")
-< *feature-list*
- List of supported pseudo-feature names:
- acl |ACL| support
- bsd BSD system (not macOS, use "mac" for that).
- iconv Can use |iconv()| for conversion.
- +shellslash Can use backslashes in filenames (Windows)
- clipboard |clipboard| provider is available.
- fname_case Case in file names matters (for Darwin and MS-Windows
- this is not present).
- mac MacOS system.
- nvim This is Nvim.
- python2 Legacy Vim |python2| interface. |has-python|
- python3 Legacy Vim |python3| interface. |has-python|
- pythonx Legacy Vim |python_x| interface. |has-pythonx|
- ttyin input is a terminal (tty)
- ttyout output is a terminal (tty)
- unix Unix system.
- *vim_starting* True during |startup|.
- win32 Windows system (32 or 64 bit).
- win64 Windows system (64 bit).
- wsl WSL (Windows Subsystem for Linux) system
-
- *has-patch*
- 3. Vim patch. For example the "patch123" feature means that
- Vim patch 123 at the current |v:version| was included: >
- :if v:version > 602 || v:version == 602 && has("patch148")
-
-< 4. Vim version. For example the "patch-7.4.237" feature means
- that Nvim is Vim-compatible to version 7.4.237 or later. >
- :if has("patch-7.4.237")
-
-
-has_key({dict}, {key}) *has_key()*
- The result is a Number, which is TRUE if |Dictionary| {dict}
- has an entry with key {key}. FALSE otherwise. The {key}
- argument is a string.
-
- Can also be used as a |method|: >
- mydict->has_key(key)
-
-haslocaldir([{winnr}[, {tabnr}]]) *haslocaldir()*
- The result is a Number, which is 1 when the window has set a
- local path via |:lcd| or when {winnr} is -1 and the tabpage
- has set a local path via |:tcd|, otherwise 0.
-
- Tabs and windows are identified by their respective numbers,
- 0 means current tab or window. Missing argument implies 0.
- Thus the following are equivalent: >
- haslocaldir()
- haslocaldir(0)
- haslocaldir(0, 0)
-< With {winnr} use that window in the current tabpage.
- With {winnr} and {tabnr} use the window in that tabpage.
- {winnr} can be the window number or the |window-ID|.
- If {winnr} is -1 it is ignored, only the tab is resolved.
- Throw error if the arguments are invalid. |E5000| |E5001| |E5002|
-
- Can also be used as a |method|: >
- GetWinnr()->haslocaldir()
-
-hasmapto({what} [, {mode} [, {abbr}]]) *hasmapto()*
- The result is a Number, which is TRUE if there is a mapping
- that contains {what} in somewhere in the rhs (what it is
- mapped to) and this mapping exists in one of the modes
- indicated by {mode}.
- The arguments {what} and {mode} are strings.
- When {abbr} is there and it is |TRUE| use abbreviations
- instead of mappings. Don't forget to specify Insert and/or
- Command-line mode.
- Both the global mappings and the mappings local to the current
- buffer are checked for a match.
- If no matching mapping is found FALSE is returned.
- The following characters are recognized in {mode}:
- n Normal mode
- v Visual and Select mode
- x Visual mode
- s Select mode
- o Operator-pending mode
- i Insert mode
- l Language-Argument ("r", "f", "t", etc.)
- c Command-line mode
- When {mode} is omitted, "nvo" is used.
-
- This function is useful to check if a mapping already exists
- to a function in a Vim script. Example: >
- :if !hasmapto('\ABCdoit')
- : map <Leader>d \ABCdoit
- :endif
-< This installs the mapping to "\ABCdoit" only if there isn't
- already a mapping to "\ABCdoit".
-
- Can also be used as a |method|: >
- GetRHS()->hasmapto()
-
-histadd({history}, {item}) *histadd()*
- Add the String {item} to the history {history} which can be
- one of: *hist-names*
- "cmd" or ":" command line history
- "search" or "/" search pattern history
- "expr" or "=" typed expression history
- "input" or "@" input line history
- "debug" or ">" debug command history
- empty the current or last used history
- The {history} string does not need to be the whole name, one
- character is sufficient.
- If {item} does already exist in the history, it will be
- shifted to become the newest entry.
- The result is a Number: TRUE if the operation was successful,
- otherwise FALSE is returned.
-
- Example: >
- :call histadd("input", strftime("%Y %b %d"))
- :let date=input("Enter date: ")
-< This function is not available in the |sandbox|.
-
- Can also be used as a |method|, the base is passed as the
- second argument: >
- GetHistory()->histadd('search')
-
-histdel({history} [, {item}]) *histdel()*
- Clear {history}, i.e. delete all its entries. See |hist-names|
- for the possible values of {history}.
-
- If the parameter {item} evaluates to a String, it is used as a
- regular expression. All entries matching that expression will
- be removed from the history (if there are any).
- Upper/lowercase must match, unless "\c" is used |/\c|.
- If {item} evaluates to a Number, it will be interpreted as
- an index, see |:history-indexing|. The respective entry will
- be removed if it exists.
-
- The result is TRUE for a successful operation, otherwise FALSE
- is returned.
-
- Examples:
- Clear expression register history: >
- :call histdel("expr")
-<
- Remove all entries starting with "*" from the search history: >
- :call histdel("/", '^\*')
-<
- The following three are equivalent: >
- :call histdel("search", histnr("search"))
- :call histdel("search", -1)
- :call histdel("search", '^'.histget("search", -1).'$')
-<
- To delete the last search pattern and use the last-but-one for
- the "n" command and 'hlsearch': >
- :call histdel("search", -1)
- :let @/ = histget("search", -1)
-<
- Can also be used as a |method|: >
- GetHistory()->histdel()
-
-histget({history} [, {index}]) *histget()*
- The result is a String, the entry with Number {index} from
- {history}. See |hist-names| for the possible values of
- {history}, and |:history-indexing| for {index}. If there is
- no such entry, an empty String is returned. When {index} is
- omitted, the most recent item from the history is used.
-
- Examples:
- Redo the second last search from history. >
- :execute '/' . histget("search", -2)
-
-< Define an Ex command ":H {num}" that supports re-execution of
- the {num}th entry from the output of |:history|. >
- :command -nargs=1 H execute histget("cmd", 0+<args>)
-<
- Can also be used as a |method|: >
- GetHistory()->histget()
-
-histnr({history}) *histnr()*
- The result is the Number of the current entry in {history}.
- See |hist-names| for the possible values of {history}.
- If an error occurred, -1 is returned.
-
- Example: >
- :let inp_index = histnr("expr")
-
-< Can also be used as a |method|: >
- GetHistory()->histnr()
-<
-hlexists({name}) *hlexists()*
- The result is a Number, which is TRUE if a highlight group
- called {name} exists. This is when the group has been
- defined in some way. Not necessarily when highlighting has
- been defined for it, it may also have been used for a syntax
- item.
-
- Can also be used as a |method|: >
- GetName()->hlexists()
-<
- *hlID()*
-hlID({name}) The result is a Number, which is the ID of the highlight group
- with name {name}. When the highlight group doesn't exist,
- zero is returned.
- This can be used to retrieve information about the highlight
- group. For example, to get the background color of the
- "Comment" group: >
- :echo synIDattr(synIDtrans(hlID("Comment")), "bg")
-<
- Can also be used as a |method|: >
- GetName()->hlID()
-
-hostname() *hostname()*
- The result is a String, which is the name of the machine on
- which Vim is currently running. Machine names greater than
- 256 characters long are truncated.
-
-iconv({string}, {from}, {to}) *iconv()*
- The result is a String, which is the text {string} converted
- from encoding {from} to encoding {to}.
- When the conversion completely fails an empty string is
- returned. When some characters could not be converted they
- are replaced with "?".
- The encoding names are whatever the iconv() library function
- can accept, see ":!man 3 iconv".
- Note that Vim uses UTF-8 for all Unicode encodings, conversion
- from/to UCS-2 is automatically changed to use UTF-8. You
- cannot use UCS-2 in a string anyway, because of the NUL bytes.
-
- Can also be used as a |method|: >
- GetText()->iconv('latin1', 'utf-8')
-<
- *indent()*
-indent({lnum}) The result is a Number, which is indent of line {lnum} in the
- current buffer. The indent is counted in spaces, the value
- of 'tabstop' is relevant. {lnum} is used just like in
- |getline()|.
- When {lnum} is invalid -1 is returned.
-
- Can also be used as a |method|: >
- GetLnum()->indent()
-
-index({object}, {expr} [, {start} [, {ic}]]) *index()*
- If {object} is a |List| return the lowest index where the item
- has a value equal to {expr}. There is no automatic
- conversion, so the String "4" is different from the Number 4.
- And the Number 4 is different from the Float 4.0. The value
- of 'ignorecase' is not used here, case always matters.
-
- If {object} is a |Blob| return the lowest index where the byte
- value is equal to {expr}.
-
- If {start} is given then start looking at the item with index
- {start} (may be negative for an item relative to the end).
- When {ic} is given and it is |TRUE|, ignore case. Otherwise
- case must match.
- -1 is returned when {expr} is not found in {object}.
- Example: >
- :let idx = index(words, "the")
- :if index(numbers, 123) >= 0
-
-< Can also be used as a |method|: >
- GetObject()->index(what)
-
-input({prompt} [, {text} [, {completion}]]) *input()*
-input({opts})
- The result is a String, which is whatever the user typed on
- the command-line. The {prompt} argument is either a prompt
- string, or a blank string (for no prompt). A '\n' can be used
- in the prompt to start a new line.
-
- In the second form it accepts a single dictionary with the
- following keys, any of which may be omitted:
-
- Key Default Description ~
- prompt "" Same as {prompt} in the first form.
- default "" Same as {text} in the first form.
- completion nothing Same as {completion} in the first form.
- cancelreturn "" The value returned when the dialog is
- cancelled.
- highlight nothing Highlight handler: |Funcref|.
-
- The highlighting set with |:echohl| is used for the prompt.
- The input is entered just like a command-line, with the same
- editing commands and mappings. There is a separate history
- for lines typed for input().
- Example: >
- :if input("Coffee or beer? ") == "beer"
- : echo "Cheers!"
- :endif
-<
- If the optional {text} argument is present and not empty, this
- is used for the default reply, as if the user typed this.
- Example: >
- :let color = input("Color? ", "white")
-
-< The optional {completion} argument specifies the type of
- completion supported for the input. Without it completion is
- not performed. The supported completion types are the same as
- that can be supplied to a user-defined command using the
- "-complete=" argument. Refer to |:command-completion| for
- more information. Example: >
- let fname = input("File: ", "", "file")
-
-< *input()-highlight* *E5400* *E5402*
- The optional `highlight` key allows specifying function which
- will be used for highlighting user input. This function
- receives user input as its only argument and must return
- a list of 3-tuples [hl_start_col, hl_end_col + 1, hl_group]
- where
- hl_start_col is the first highlighted column,
- hl_end_col is the last highlighted column (+ 1!),
- hl_group is |:hi| group used for highlighting.
- *E5403* *E5404* *E5405* *E5406*
- Both hl_start_col and hl_end_col + 1 must point to the start
- of the multibyte character (highlighting must not break
- multibyte characters), hl_end_col + 1 may be equal to the
- input length. Start column must be in range [0, len(input)),
- end column must be in range (hl_start_col, len(input)],
- sections must be ordered so that next hl_start_col is greater
- then or equal to previous hl_end_col.
-
- Example (try some input with parentheses): >
- highlight RBP1 guibg=Red ctermbg=red
- highlight RBP2 guibg=Yellow ctermbg=yellow
- highlight RBP3 guibg=Green ctermbg=green
- highlight RBP4 guibg=Blue ctermbg=blue
- let g:rainbow_levels = 4
- function! RainbowParens(cmdline)
- let ret = []
- let i = 0
- let lvl = 0
- while i < len(a:cmdline)
- if a:cmdline[i] is# '('
- call add(ret, [i, i + 1, 'RBP' . ((lvl % g:rainbow_levels) + 1)])
- let lvl += 1
- elseif a:cmdline[i] is# ')'
- let lvl -= 1
- call add(ret, [i, i + 1, 'RBP' . ((lvl % g:rainbow_levels) + 1)])
- endif
- let i += 1
- endwhile
- return ret
- endfunction
- call input({'prompt':'>','highlight':'RainbowParens'})
-<
- Highlight function is called at least once for each new
- displayed input string, before command-line is redrawn. It is
- expected that function is pure for the duration of one input()
- call, i.e. it produces the same output for the same input, so
- output may be memoized. Function is run like under |:silent|
- modifier. If the function causes any errors, it will be
- skipped for the duration of the current input() call.
-
- Highlighting is disabled if command-line contains arabic
- characters.
-
- NOTE: This function must not be used in a startup file, for
- the versions that only run in GUI mode (e.g., the Win32 GUI).
- Note: When input() is called from within a mapping it will
- consume remaining characters from that mapping, because a
- mapping is handled like the characters were typed.
- Use |inputsave()| before input() and |inputrestore()|
- after input() to avoid that. Another solution is to avoid
- that further characters follow in the mapping, e.g., by using
- |:execute| or |:normal|.
-
- Example with a mapping: >
- :nmap \x :call GetFoo()<CR>:exe "/" . Foo<CR>
- :function GetFoo()
- : call inputsave()
- : let g:Foo = input("enter search pattern: ")
- : call inputrestore()
- :endfunction
-
-< Can also be used as a |method|: >
- GetPrompt()->input()
-
-inputlist({textlist}) *inputlist()*
- {textlist} must be a |List| of strings. This |List| is
- displayed, one string per line. The user will be prompted to
- enter a number, which is returned.
- The user can also select an item by clicking on it with the
- mouse, if the mouse is enabled in the command line ('mouse' is
- "a" or includes "c"). For the first string 0 is returned.
- When clicking above the first item a negative number is
- returned. When clicking on the prompt one more than the
- length of {textlist} is returned.
- Make sure {textlist} has less than 'lines' entries, otherwise
- it won't work. It's a good idea to put the entry number at
- the start of the string. And put a prompt in the first item.
- Example: >
- let color = inputlist(['Select color:', '1. red',
- \ '2. green', '3. blue'])
-
-< Can also be used as a |method|: >
- GetChoices()->inputlist()
-
-inputrestore() *inputrestore()*
- Restore typeahead that was saved with a previous |inputsave()|.
- Should be called the same number of times inputsave() is
- called. Calling it more often is harmless though.
- Returns TRUE when there is nothing to restore, FALSE otherwise.
-
-inputsave() *inputsave()*
- Preserve typeahead (also from mappings) and clear it, so that
- a following prompt gets input from the user. Should be
- followed by a matching inputrestore() after the prompt. Can
- be used several times, in which case there must be just as
- many inputrestore() calls.
- Returns TRUE when out of memory, FALSE otherwise.
-
-inputsecret({prompt} [, {text}]) *inputsecret()*
- This function acts much like the |input()| function with but
- two exceptions:
- a) the user's response will be displayed as a sequence of
- asterisks ("*") thereby keeping the entry secret, and
- b) the user's response will not be recorded on the input
- |history| stack.
- The result is a String, which is whatever the user actually
- typed on the command-line in response to the issued prompt.
- NOTE: Command-line completion is not supported.
-
- Can also be used as a |method|: >
- GetPrompt()->inputsecret()
-
-insert({object}, {item} [, {idx}]) *insert()*
- When {object} is a |List| or a |Blob| insert {item} at the start
- of it.
-
- If {idx} is specified insert {item} before the item with index
- {idx}. If {idx} is zero it goes before the first item, just
- like omitting {idx}. A negative {idx} is also possible, see
- |list-index|. -1 inserts just before the last item.
-
- Returns the resulting |List| or |Blob|. Examples: >
- :let mylist = insert([2, 3, 5], 1)
- :call insert(mylist, 4, -1)
- :call insert(mylist, 6, len(mylist))
-< The last example can be done simpler with |add()|.
- Note that when {item} is a |List| it is inserted as a single
- item. Use |extend()| to concatenate |Lists|.
-
- Can also be used as a |method|: >
- mylist->insert(item)
-
-interrupt() *interrupt()*
- Interrupt script execution. It works more or less like the
- user typing CTRL-C, most commands won't execute and control
- returns to the user. This is useful to abort execution
- from lower down, e.g. in an autocommand. Example: >
- :function s:check_typoname(file)
- : if fnamemodify(a:file, ':t') == '['
- : echomsg 'Maybe typo'
- : call interrupt()
- : endif
- :endfunction
- :au BufWritePre * call s:check_typoname(expand('<amatch>'))
-
-invert({expr}) *invert()*
- Bitwise invert. The argument is converted to a number. A
- List, Dict or Float argument causes an error. Example: >
- :let bits = invert(bits)
-< Can also be used as a |method|: >
- :let bits = bits->invert()
-
-isdirectory({directory}) *isdirectory()*
- The result is a Number, which is |TRUE| when a directory
- with the name {directory} exists. If {directory} doesn't
- exist, or isn't a directory, the result is |FALSE|. {directory}
- is any expression, which is used as a String.
-
- Can also be used as a |method|: >
- GetName()->isdirectory()
-
-isinf({expr}) *isinf()*
- Return 1 if {expr} is a positive infinity, or -1 a negative
- infinity, otherwise 0. >
- :echo isinf(1.0 / 0.0)
-< 1 >
- :echo isinf(-1.0 / 0.0)
-< -1
-
- Can also be used as a |method|: >
- Compute()->isinf()
-
-islocked({expr}) *islocked()* *E786*
- The result is a Number, which is |TRUE| when {expr} is the
- name of a locked variable.
- The string argument {expr} must be the name of a variable,
- |List| item or |Dictionary| entry, not the variable itself!
- Example: >
- :let alist = [0, ['a', 'b'], 2, 3]
- :lockvar 1 alist
- :echo islocked('alist') " 1
- :echo islocked('alist[1]') " 0
-
-< When {expr} is a variable that does not exist you get an error
- message. Use |exists()| to check for existence.
-
- Can also be used as a |method|: >
- GetName()->islocked()
-
-id({expr}) *id()*
- Returns a |String| which is a unique identifier of the
- container type (|List|, |Dict|, |Blob| and |Partial|). It is
- guaranteed that for the mentioned types `id(v1) ==# id(v2)`
- returns true iff `type(v1) == type(v2) && v1 is v2`.
- Note that |v:_null_string|, |v:_null_list|, |v:_null_dict| and
- |v:_null_blob| have the same `id()` with different types
- because they are internally represented as NULL pointers.
- `id()` returns a hexadecimal representanion of the pointers to
- the containers (i.e. like `0x994a40`), same as `printf("%p",
- {expr})`, but it is advised against counting on the exact
- format of the return value.
-
- It is not guaranteed that `id(no_longer_existing_container)`
- will not be equal to some other `id()`: new containers may
- reuse identifiers of the garbage-collected ones.
-
-items({dict}) *items()*
- Return a |List| with all the key-value pairs of {dict}. Each
- |List| item is a list with two items: the key of a {dict}
- entry and the value of this entry. The |List| is in arbitrary
- order. Also see |keys()| and |values()|.
- Example: >
- for [key, value] in items(mydict)
- echo key . ': ' . value
- endfor
-
-< Can also be used as a |method|: >
- mydict->items()
-
-isnan({expr}) *isnan()*
- Return |TRUE| if {expr} is a float with value NaN. >
- echo isnan(0.0 / 0.0)
-< 1
-
- Can also be used as a |method|: >
- Compute()->isnan()
-
-jobpid({job}) *jobpid()*
- Return the PID (process id) of |job-id| {job}.
-
-jobresize({job}, {width}, {height}) *jobresize()*
- Resize the pseudo terminal window of |job-id| {job} to {width}
- columns and {height} rows.
- Fails if the job was not started with `"pty":v:true`.
-
-jobstart({cmd}[, {opts}]) *jobstart()*
- Spawns {cmd} as a job.
- If {cmd} is a List it runs directly (no 'shell').
- If {cmd} is a String it runs in the 'shell', like this: >
- :call jobstart(split(&shell) + split(&shellcmdflag) + ['{cmd}'])
-< (See |shell-unquoting| for details.)
-
- Example: >
- :call jobstart('nvim -h', {'on_stdout':{j,d,e->append(line('.'),d)}})
-<
- Returns |job-id| on success, 0 on invalid arguments (or job
- table is full), -1 if {cmd}[0] or 'shell' is not executable.
- The returned job-id is a valid |channel-id| representing the
- job's stdio streams. Use |chansend()| (or |rpcnotify()| and
- |rpcrequest()| if "rpc" was enabled) to send data to stdin and
- |chanclose()| to close the streams without stopping the job.
-
- See |job-control| and |RPC|.
-
- NOTE: on Windows if {cmd} is a List:
- - cmd[0] must be an executable (not a "built-in"). If it is
- in $PATH it can be called by name, without an extension: >
- :call jobstart(['ping', 'neovim.io'])
-< If it is a full or partial path, extension is required: >
- :call jobstart(['System32\ping.exe', 'neovim.io'])
-< - {cmd} is collapsed to a string of quoted args as expected
- by CommandLineToArgvW https://msdn.microsoft.com/bb776391
- unless cmd[0] is some form of "cmd.exe".
-
- *jobstart-options*
- {opts} is a dictionary with these keys:
- clear_env: (boolean) `env` defines the job environment
- exactly, instead of merging current environment.
- cwd: (string, default=|current-directory|) Working
- directory of the job.
- detach: (boolean) Detach the job process: it will not be
- killed when Nvim exits. If the process exits
- before Nvim, `on_exit` will be invoked.
- env: (dict) Map of environment variable name:value
- pairs extending (or replacing if |clear_env|)
- the current environment.
- height: (number) Height of the `pty` terminal.
- |on_exit|: (function) Callback invoked when the job exits.
- |on_stdout|: (function) Callback invoked when the job emits
- stdout data.
- |on_stderr|: (function) Callback invoked when the job emits
- stderr data.
- overlapped: (boolean) Set FILE_FLAG_OVERLAPPED for the
- standard input/output passed to the child process.
- Normally you do not need to set this.
- (Only available on MS-Windows, On other
- platforms, this option is silently ignored.)
- pty: (boolean) Connect the job to a new pseudo
- terminal, and its streams to the master file
- descriptor. Then `on_stderr` is ignored,
- `on_stdout` receives all output.
- rpc: (boolean) Use |msgpack-rpc| to communicate with
- the job over stdio. Then `on_stdout` is ignored,
- but `on_stderr` can still be used.
- stderr_buffered: (boolean) Collect data until EOF (stream closed)
- before invoking `on_stderr`. |channel-buffered|
- stdout_buffered: (boolean) Collect data until EOF (stream
- closed) before invoking `on_stdout`. |channel-buffered|
- stdin: (string) Either "pipe" (default) to connect the
- job's stdin to a channel or "null" to disconnect
- stdin.
- width: (number) Width of the `pty` terminal.
-
- {opts} is passed as |self| dictionary to the callback; the
- caller may set other keys to pass application-specific data.
-
- Returns:
- - |channel-id| on success
- - 0 on invalid arguments
- - -1 if {cmd}[0] is not executable.
- See also |job-control|, |channel|, |msgpack-rpc|.
-
-jobstop({id}) *jobstop()*
- Stop |job-id| {id} by sending SIGTERM to the job process. If
- the process does not terminate after a timeout then SIGKILL
- will be sent. When the job terminates its |on_exit| handler
- (if any) will be invoked.
- See |job-control|.
-
- Returns 1 for valid job id, 0 for invalid id, including jobs have
- exited or stopped.
-
-jobwait({jobs}[, {timeout}]) *jobwait()*
- Waits for jobs and their |on_exit| handlers to complete.
-
- {jobs} is a List of |job-id|s to wait for.
- {timeout} is the maximum waiting time in milliseconds. If
- omitted or -1, wait forever.
-
- Timeout of 0 can be used to check the status of a job: >
- let running = jobwait([{job-id}], 0)[0] == -1
-<
- During jobwait() callbacks for jobs not in the {jobs} list may
- be invoked. The screen will not redraw unless |:redraw| is
- invoked by a callback.
-
- Returns a list of len({jobs}) integers, where each integer is
- the status of the corresponding job:
- Exit-code, if the job exited
- -1 if the timeout was exceeded
- -2 if the job was interrupted (by |CTRL-C|)
- -3 if the job-id is invalid
-
-join({list} [, {sep}]) *join()*
- Join the items in {list} together into one String.
- When {sep} is specified it is put in between the items. If
- {sep} is omitted a single space is used.
- Note that {sep} is not added at the end. You might want to
- add it there too: >
- let lines = join(mylist, "\n") . "\n"
-< String items are used as-is. |Lists| and |Dictionaries| are
- converted into a string like with |string()|.
- The opposite function is |split()|.
-
- Can also be used as a |method|: >
- mylist->join()
-
-json_decode({expr}) *json_decode()*
- Convert {expr} from JSON object. Accepts |readfile()|-style
- list as the input, as well as regular string. May output any
- Vim value. In the following cases it will output
- |msgpack-special-dict|:
- 1. Dictionary contains duplicate key.
- 2. Dictionary contains empty key.
- 3. String contains NUL byte. Two special dictionaries: for
- dictionary and for string will be emitted in case string
- with NUL byte was a dictionary key.
-
- Note: function treats its input as UTF-8 always. The JSON
- standard allows only a few encodings, of which UTF-8 is
- recommended and the only one required to be supported.
- Non-UTF-8 characters are an error.
-
- Can also be used as a |method|: >
- ReadObject()->json_decode()
-
-json_encode({expr}) *json_encode()*
- Convert {expr} into a JSON string. Accepts
- |msgpack-special-dict| as the input. Will not convert
- |Funcref|s, mappings with non-string keys (can be created as
- |msgpack-special-dict|), values with self-referencing
- containers, strings which contain non-UTF-8 characters,
- pseudo-UTF-8 strings which contain codepoints reserved for
- surrogate pairs (such strings are not valid UTF-8 strings).
- Non-printable characters are converted into "\u1234" escapes
- or special escapes like "\t", other are dumped as-is.
- |Blob|s are converted to arrays of the individual bytes.
-
- Can also be used as a |method|: >
- GetObject()->json_encode()
-
-keys({dict}) *keys()*
- Return a |List| with all the keys of {dict}. The |List| is in
- arbitrary order. Also see |items()| and |values()|.
-
- Can also be used as a |method|: >
- mydict->keys()
-
-< *len()* *E701*
-len({expr}) The result is a Number, which is the length of the argument.
- When {expr} is a String or a Number the length in bytes is
- used, as with |strlen()|.
- When {expr} is a |List| the number of items in the |List| is
- returned.
- When {expr} is a |Blob| the number of bytes is returned.
- When {expr} is a |Dictionary| the number of entries in the
- |Dictionary| is returned.
- Otherwise an error is given.
-
- Can also be used as a |method|: >
- mylist->len()
-
-< *libcall()* *E364* *E368*
-libcall({libname}, {funcname}, {argument})
- Call function {funcname} in the run-time library {libname}
- with single argument {argument}.
- This is useful to call functions in a library that you
- especially made to be used with Vim. Since only one argument
- is possible, calling standard library functions is rather
- limited.
- The result is the String returned by the function. If the
- function returns NULL, this will appear as an empty string ""
- to Vim.
- If the function returns a number, use libcallnr()!
- If {argument} is a number, it is passed to the function as an
- int; if {argument} is a string, it is passed as a
- null-terminated string.
-
- libcall() allows you to write your own 'plug-in' extensions to
- Vim without having to recompile the program. It is NOT a
- means to call system functions! If you try to do so Vim will
- very probably crash.
-
- For Win32, the functions you write must be placed in a DLL
- and use the normal C calling convention (NOT Pascal which is
- used in Windows System DLLs). The function must take exactly
- one parameter, either a character pointer or a long integer,
- and must return a character pointer or NULL. The character
- pointer returned must point to memory that will remain valid
- after the function has returned (e.g. in static data in the
- DLL). If it points to allocated memory, that memory will
- leak away. Using a static buffer in the function should work,
- it's then freed when the DLL is unloaded.
-
- WARNING: If the function returns a non-valid pointer, Vim may
- crash! This also happens if the function returns a number,
- because Vim thinks it's a pointer.
- For Win32 systems, {libname} should be the filename of the DLL
- without the ".DLL" suffix. A full path is only required if
- the DLL is not in the usual places.
- For Unix: When compiling your own plugins, remember that the
- object code must be compiled as position-independent ('PIC').
- Examples: >
- :echo libcall("libc.so", "getenv", "HOME")
-
-< Can also be used as a |method|, the base is passed as the
- third argument: >
- GetValue()->libcall("libc.so", "getenv")
-<
- *libcallnr()*
-libcallnr({libname}, {funcname}, {argument})
- Just like |libcall()|, but used for a function that returns an
- int instead of a string.
- Examples: >
- :echo libcallnr("/usr/lib/libc.so", "getpid", "")
- :call libcallnr("libc.so", "printf", "Hello World!\n")
- :call libcallnr("libc.so", "sleep", 10)
-<
- Can also be used as a |method|, the base is passed as the
- third argument: >
- GetValue()->libcallnr("libc.so", "printf")
-<
-line({expr} [, {winid}]) *line()*
- The result is a Number, which is the line number of the file
- position given with {expr}. The {expr} argument is a string.
- The accepted positions are:
- . the cursor position
- $ the last line in the current buffer
- 'x position of mark x (if the mark is not set, 0 is
- returned)
- w0 first line visible in current window (one if the
- display isn't updated, e.g. in silent Ex mode)
- w$ last line visible in current window (this is one
- less than "w0" if no lines are visible)
- v In Visual mode: the start of the Visual area (the
- cursor is the end). When not in Visual mode
- returns the cursor position. Differs from |'<| in
- that it's updated right away.
- Note that a mark in another file can be used. The line number
- then applies to another buffer.
- To get the column number use |col()|. To get both use
- |getpos()|.
- With the optional {winid} argument the values are obtained for
- that window instead of the current window.
- Examples: >
- line(".") line number of the cursor
- line(".", winid) idem, in window "winid"
- line("'t") line number of mark t
- line("'" . marker) line number of mark marker
-<
- Can also be used as a |method|: >
- GetValue()->line()
-
-line2byte({lnum}) *line2byte()*
- Return the byte count from the start of the buffer for line
- {lnum}. This includes the end-of-line character, depending on
- the 'fileformat' option for the current buffer. The first
- line returns 1. UTF-8 encoding is used, 'fileencoding' is
- ignored. This can also be used to get the byte count for the
- line just below the last line: >
- line2byte(line("$") + 1)
-< This is the buffer size plus one. If 'fileencoding' is empty
- it is the file size plus one. {lnum} is used like with
- |getline()|. When {lnum} is invalid -1 is returned.
- Also see |byte2line()|, |go| and |:goto|.
-
- Can also be used as a |method|: >
- GetLnum()->line2byte()
-
-lispindent({lnum}) *lispindent()*
- Get the amount of indent for line {lnum} according the lisp
- indenting rules, as with 'lisp'.
- The indent is counted in spaces, the value of 'tabstop' is
- relevant. {lnum} is used just like in |getline()|.
- When {lnum} is invalid, -1 is returned.
-
- Can also be used as a |method|: >
- GetLnum()->lispindent()
-
-list2str({list} [, {utf8}]) *list2str()*
- Convert each number in {list} to a character string can
- concatenate them all. Examples: >
- list2str([32]) returns " "
- list2str([65, 66, 67]) returns "ABC"
-< The same can be done (slowly) with: >
- join(map(list, {nr, val -> nr2char(val)}), '')
-< |str2list()| does the opposite.
-
- UTF-8 encoding is always used, {utf8} option has no effect,
- and exists only for backwards-compatibility.
- With UTF-8 composing characters work as expected: >
- list2str([97, 769]) returns "á"
-<
- Can also be used as a |method|: >
- GetList()->list2str()
-
-localtime() *localtime()*
- Return the current time, measured as seconds since 1st Jan
- 1970. See also |strftime()|, |strptime()| and |getftime()|.
-
-
-log({expr}) *log()*
- Return the natural logarithm (base e) of {expr} as a |Float|.
- {expr} must evaluate to a |Float| or a |Number| in the range
- (0, inf].
- Examples: >
- :echo log(10)
-< 2.302585 >
- :echo log(exp(5))
-< 5.0
-
- Can also be used as a |method|: >
- Compute()->log()
-
-log10({expr}) *log10()*
- Return the logarithm of Float {expr} to base 10 as a |Float|.
- {expr} must evaluate to a |Float| or a |Number|.
- Examples: >
- :echo log10(1000)
-< 3.0 >
- :echo log10(0.01)
-< -2.0
-
- Can also be used as a |method|: >
- Compute()->log10()
-
-luaeval({expr}[, {expr}])
- Evaluate Lua expression {expr} and return its result converted
- to Vim data structures. See |lua-eval| for more details.
-
- Can also be used as a |method|: >
- GetExpr()->luaeval()
-
-map({expr1}, {expr2}) *map()*
- {expr1} must be a |List|, |Blob| or |Dictionary|.
- Replace each item in {expr1} with the result of evaluating
- {expr2}. For a |Blob| each byte is replaced.
-
- {expr2} must be a |string| or |Funcref|.
-
- If {expr2} is a |string|, inside {expr2} |v:val| has the value
- of the current item. For a |Dictionary| |v:key| has the key
- of the current item and for a |List| |v:key| has the index of
- the current item. For a |Blob| |v:key| has the index of the
- current byte.
- Example: >
- :call map(mylist, '"> " . v:val . " <"')
-< This puts "> " before and " <" after each item in "mylist".
-
- Note that {expr2} is the result of an expression and is then
- used as an expression again. Often it is good to use a
- |literal-string| to avoid having to double backslashes. You
- still have to double ' quotes
-
- If {expr2} is a |Funcref| it is called with two arguments:
- 1. The key or the index of the current item.
- 2. the value of the current item.
- The function must return the new value of the item. Example
- that changes each value by "key-value": >
- func KeyValue(key, val)
- return a:key . '-' . a:val
- endfunc
- call map(myDict, function('KeyValue'))
-< It is shorter when using a |lambda|: >
- call map(myDict, {key, val -> key . '-' . val})
-< If you do not use "val" you can leave it out: >
- call map(myDict, {key -> 'item: ' . key})
-< If you do not use "key" you can use a short name: >
- call map(myDict, {_, val -> 'item: ' . val})
-<
- The operation is done in-place. If you want a |List| or
- |Dictionary| to remain unmodified make a copy first: >
- :let tlist = map(copy(mylist), ' v:val . "\t"')
-
-< Returns {expr1}, the |List|, |Blob| or |Dictionary| that was
- filtered. When an error is encountered while evaluating
- {expr2} no further items in {expr1} are processed. When
- {expr2} is a Funcref errors inside a function are ignored,
- unless it was defined with the "abort" flag.
-
- Can also be used as a |method|: >
- mylist->map(expr2)
-
-maparg({name} [, {mode} [, {abbr} [, {dict}]]]) *maparg()*
- When {dict} is omitted or zero: Return the rhs of mapping
- {name} in mode {mode}. The returned String has special
- characters translated like in the output of the ":map" command
- listing.
-
- When there is no mapping for {name}, an empty String is
- returned. When the mapping for {name} is empty, then "<Nop>"
- is returned.
-
- The {name} can have special key names, like in the ":map"
- command.
-
- {mode} can be one of these strings:
- "n" Normal
- "v" Visual (including Select)
- "o" Operator-pending
- "i" Insert
- "c" Cmd-line
- "s" Select
- "x" Visual
- "l" langmap |language-mapping|
- "t" Terminal
- "" Normal, Visual and Operator-pending
- When {mode} is omitted, the modes for "" are used.
-
- When {abbr} is there and it is |TRUE| use abbreviations
- instead of mappings.
-
- When {dict} is there and it is |TRUE| return a dictionary
- containing all the information of the mapping with the
- following items:
- "lhs" The {lhs} of the mapping.
- "rhs" The {rhs} of the mapping as typed.
- "silent" 1 for a |:map-silent| mapping, else 0.
- "noremap" 1 if the {rhs} of the mapping is not remappable.
- "script" 1 if mapping was defined with <script>.
- "expr" 1 for an expression mapping (|:map-<expr>|).
- "buffer" 1 for a buffer local mapping (|:map-local|).
- "mode" Modes for which the mapping is defined. In
- addition to the modes mentioned above, these
- characters will be used:
- " " Normal, Visual and Operator-pending
- "!" Insert and Commandline mode
- (|mapmode-ic|)
- "sid" The script local ID, used for <sid> mappings
- (|<SID>|).
- "lnum" The line number in "sid", zero if unknown.
- "nowait" Do not wait for other, longer mappings.
- (|:map-<nowait>|).
-
- The mappings local to the current buffer are checked first,
- then the global mappings.
- This function can be used to map a key even when it's already
- mapped, and have it do the original mapping too. Sketch: >
- exe 'nnoremap <Tab> ==' . maparg('<Tab>', 'n')
-
-< Can also be used as a |method|: >
- GetKey()->maparg('n')
-
-mapcheck({name} [, {mode} [, {abbr}]]) *mapcheck()*
- Check if there is a mapping that matches with {name} in mode
- {mode}. See |maparg()| for {mode} and special names in
- {name}.
- When {abbr} is there and it is non-zero use abbreviations
- instead of mappings.
- A match happens with a mapping that starts with {name} and
- with a mapping which is equal to the start of {name}.
-
- matches mapping "a" "ab" "abc" ~
- mapcheck("a") yes yes yes
- mapcheck("abc") yes yes yes
- mapcheck("ax") yes no no
- mapcheck("b") no no no
-
- The difference with maparg() is that mapcheck() finds a
- mapping that matches with {name}, while maparg() only finds a
- mapping for {name} exactly.
- When there is no mapping that starts with {name}, an empty
- String is returned. If there is one, the RHS of that mapping
- is returned. If there are several mappings that start with
- {name}, the RHS of one of them is returned. This will be
- "<Nop>" if the RHS is empty.
- The mappings local to the current buffer are checked first,
- then the global mappings.
- This function can be used to check if a mapping can be added
- without being ambiguous. Example: >
- :if mapcheck("_vv") == ""
- : map _vv :set guifont=7x13<CR>
- :endif
-< This avoids adding the "_vv" mapping when there already is a
- mapping for "_v" or for "_vvv".
-
- Can also be used as a |method|: >
- GetKey()->mapcheck('n')
-
-match({expr}, {pat} [, {start} [, {count}]]) *match()*
- When {expr} is a |List| then this returns the index of the
- first item where {pat} matches. Each item is used as a
- String, |Lists| and |Dictionaries| are used as echoed.
-
- Otherwise, {expr} is used as a String. The result is a
- Number, which gives the index (byte offset) in {expr} where
- {pat} matches.
-
- A match at the first character or |List| item returns zero.
- If there is no match -1 is returned.
-
- For getting submatches see |matchlist()|.
- Example: >
- :echo match("testing", "ing") " results in 4
- :echo match([1, 'x'], '\a') " results in 1
-< See |string-match| for how {pat} is used.
- *strpbrk()*
- Vim doesn't have a strpbrk() function. But you can do: >
- :let sepidx = match(line, '[.,;: \t]')
-< *strcasestr()*
- Vim doesn't have a strcasestr() function. But you can add
- "\c" to the pattern to ignore case: >
- :let idx = match(haystack, '\cneedle')
-<
- If {start} is given, the search starts from byte index
- {start} in a String or item {start} in a |List|.
- The result, however, is still the index counted from the
- first character/item. Example: >
- :echo match("testing", "ing", 2)
-< result is again "4". >
- :echo match("testing", "ing", 4)
-< result is again "4". >
- :echo match("testing", "t", 2)
-< result is "3".
- For a String, if {start} > 0 then it is like the string starts
- {start} bytes later, thus "^" will match at {start}. Except
- when {count} is given, then it's like matches before the
- {start} byte are ignored (this is a bit complicated to keep it
- backwards compatible).
- For a String, if {start} < 0, it will be set to 0. For a list
- the index is counted from the end.
- If {start} is out of range ({start} > strlen({expr}) for a
- String or {start} > len({expr}) for a |List|) -1 is returned.
-
- When {count} is given use the {count}'th match. When a match
- is found in a String the search for the next one starts one
- character further. Thus this example results in 1: >
- echo match("testing", "..", 0, 2)
-< In a |List| the search continues in the next item.
- Note that when {count} is added the way {start} works changes,
- see above.
-
- See |pattern| for the patterns that are accepted.
- The 'ignorecase' option is used to set the ignore-caseness of
- the pattern. 'smartcase' is NOT used. The matching is always
- done like 'magic' is set and 'cpoptions' is empty.
- Note that a match at the start is preferred, thus when the
- pattern is using "*" (any number of matches) it tends to find
- zero matches at the start instead of a number of matches
- further down in the text.
-
- Can also be used as a |method|: >
- GetText()->match('word')
- GetList()->match('word')
-<
- *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
- identification number (ID), which can be used to delete the
- match using |matchdelete()|. The ID is bound to the window.
- Matching is case sensitive and magic, unless case sensitivity
- or magicness are explicitly overridden in {pattern}. The
- 'magic', 'smartcase' and 'ignorecase' options are not used.
- The "Conceal" value is special, it causes the match to be
- concealed.
-
- The optional {priority} argument assigns a priority to the
- match. A match with a high priority will have its
- highlighting overrule that of a match with a lower priority.
- A priority is specified as an integer (negative numbers are no
- exception). If the {priority} argument is not specified, the
- default priority is 10. The priority of 'hlsearch' is zero,
- hence all matches with a priority greater than zero will
- overrule it. Syntax highlighting (see 'syntax') is a separate
- mechanism, and regardless of the chosen priority a match will
- always overrule syntax highlighting.
-
- The optional {id} argument allows the request for a specific
- match ID. If a specified ID is already taken, an error
- message will appear and the match will not be added. An ID
- is specified as a positive integer (zero excluded). IDs 1, 2
- and 3 are reserved for |:match|, |:2match| and |:3match|,
- respectively. If the {id} argument is not specified or -1,
- |matchadd()| automatically chooses a free ID.
-
- The optional {dict} argument allows for further custom
- values. Currently this is used to specify a match specific
- conceal character that will be shown for |hl-Conceal|
- highlighted matches. The dict can have the following members:
-
- conceal Special character to show instead of the
- match (only for |hl-Conceal| highlighed
- matches, see |:syn-cchar|)
- window Instead of the current window use the
- window with this number or window ID.
-
- The number of matches is not limited, as it is the case with
- the |:match| commands.
-
- Example: >
- :highlight MyGroup ctermbg=green guibg=green
- :let m = matchadd("MyGroup", "TODO")
-< Deletion of the pattern: >
- :call matchdelete(m)
-
-< A list of matches defined by |matchadd()| and |:match| are
- available from |getmatches()|. All matches can be deleted in
- one operation by |clearmatches()|.
-
- Can also be used as a |method|: >
- GetGroup()->matchadd('TODO')
-<
- *matchaddpos()*
-matchaddpos({group}, {pos} [, {priority} [, {id} [, {dict}]]])
- Same as |matchadd()|, but requires a list of positions {pos}
- instead of a pattern. This command is faster than |matchadd()|
- because it does not require to handle regular expressions and
- sets buffer line boundaries to redraw screen. It is supposed
- to be used when fast match additions and deletions are
- required, for example to highlight matching parentheses.
- *E5030* *E5031*
- {pos} is a list of positions. Each position can be one of
- these:
- - A number. This whole line will be highlighted. The first
- line has number 1.
- - A list with one number, e.g., [23]. The whole line with this
- number will be highlighted.
- - A list with two numbers, e.g., [23, 11]. The first number is
- the line number, the second one is the column number (first
- column is 1, the value must correspond to the byte index as
- |col()| would return). The character at this position will
- be highlighted.
- - A list with three numbers, e.g., [23, 11, 3]. As above, but
- the third number gives the length of the highlight in bytes.
-
- Entries with zero and negative line numbers are silently
- ignored, as well as entries with negative column numbers and
- lengths.
-
- The maximum number of positions in {pos} is 8.
-
- Example: >
- :highlight MyGroup ctermbg=green guibg=green
- :let m = matchaddpos("MyGroup", [[23, 24], 34])
-< Deletion of the pattern: >
- :call matchdelete(m)
-
-< Matches added by |matchaddpos()| are returned by
- |getmatches()|.
-
- Can also be used as a |method|: >
- GetGroup()->matchaddpos([23, 11])
-
-matcharg({nr}) *matcharg()*
- Selects the {nr} match item, as set with a |:match|,
- |:2match| or |:3match| command.
- Return a |List| with two elements:
- The name of the highlight group used
- The pattern used.
- When {nr} is not 1, 2 or 3 returns an empty |List|.
- When there is no match item set returns ['', ''].
- This is useful to save and restore a |:match|.
- Highlighting matches using the |:match| commands are limited
- to three matches. |matchadd()| does not have this limitation.
-
- Can also be used as a |method|: >
- GetMatch()->matcharg()
-
-matchdelete({id} [, {win}]) *matchdelete()* *E802* *E803*
- Deletes a match with ID {id} previously defined by |matchadd()|
- or one of the |:match| commands. Returns 0 if successful,
- otherwise -1. See example for |matchadd()|. All matches can
- be deleted in one operation by |clearmatches()|.
- If {win} is specified, use the window with this number or
- window ID instead of the current window.
-
- Can also be used as a |method|: >
- GetMatch()->matchdelete()
-
-matchend({expr}, {pat} [, {start} [, {count}]]) *matchend()*
- Same as |match()|, but return the index of first character
- after the match. Example: >
- :echo matchend("testing", "ing")
-< results in "7".
- *strspn()* *strcspn()*
- Vim doesn't have a strspn() or strcspn() function, but you can
- do it with matchend(): >
- :let span = matchend(line, '[a-zA-Z]')
- :let span = matchend(line, '[^a-zA-Z]')
-< Except that -1 is returned when there are no matches.
-
- The {start}, if given, has the same meaning as for |match()|. >
- :echo matchend("testing", "ing", 2)
-< results in "7". >
- :echo matchend("testing", "ing", 5)
-< result is "-1".
- When {expr} is a |List| the result is equal to |match()|.
-
- Can also be used as a |method|: >
- GetText()->matchend('word')
-
-matchlist({expr}, {pat} [, {start} [, {count}]]) *matchlist()*
- Same as |match()|, but return a |List|. The first item in the
- list is the matched string, same as what matchstr() would
- return. Following items are submatches, like "\1", "\2", etc.
- in |:substitute|. When an optional submatch didn't match an
- empty string is used. Example: >
- echo matchlist('acd', '\(a\)\?\(b\)\?\(c\)\?\(.*\)')
-< Results in: ['acd', 'a', '', 'c', 'd', '', '', '', '', '']
- When there is no match an empty list is returned.
-
- You can pass in a List, but that is not very useful.
-
- Can also be used as a |method|: >
- GetText()->matchlist('word')
-
-matchstr({expr}, {pat} [, {start} [, {count}]]) *matchstr()*
- Same as |match()|, but return the matched string. Example: >
- :echo matchstr("testing", "ing")
-< results in "ing".
- When there is no match "" is returned.
- The {start}, if given, has the same meaning as for |match()|. >
- :echo matchstr("testing", "ing", 2)
-< results in "ing". >
- :echo matchstr("testing", "ing", 5)
-< result is "".
- When {expr} is a |List| then the matching item is returned.
- The type isn't changed, it's not necessarily a String.
-
- Can also be used as a |method|: >
- GetText()->matchstr('word')
-
-matchstrpos({expr}, {pat} [, {start} [, {count}]]) *matchstrpos()*
- Same as |matchstr()|, but return the matched string, the start
- position and the end position of the match. Example: >
- :echo matchstrpos("testing", "ing")
-< results in ["ing", 4, 7].
- When there is no match ["", -1, -1] is returned.
- The {start}, if given, has the same meaning as for |match()|. >
- :echo matchstrpos("testing", "ing", 2)
-< results in ["ing", 4, 7]. >
- :echo matchstrpos("testing", "ing", 5)
-< result is ["", -1, -1].
- When {expr} is a |List| then the matching item, the index
- of first item where {pat} matches, the start position and the
- end position of the match are returned. >
- :echo matchstrpos([1, '__x'], '\a')
-< result is ["x", 1, 2, 3].
- The type isn't changed, it's not necessarily a String.
-
- Can also be used as a |method|: >
- GetText()->matchstrpos('word')
-<
- *max()*
-max({expr}) Return the maximum value of all items in {expr}.
- {expr} can be a |List| or a |Dictionary|. For a Dictionary,
- it returns the maximum of all values in the Dictionary.
- If {expr} is neither a List nor a Dictionary, or one of the
- items in {expr} cannot be used as a Number this results in
- an error. An empty |List| or |Dictionary| results in zero.
-
- Can also be used as a |method|: >
- mylist->max()
-
-menu_get({path} [, {modes}]) *menu_get()*
- Returns a |List| of |Dictionaries| describing |menus| (defined
- by |:menu|, |:amenu|, …), including |hidden-menus|.
-
- {path} matches a menu by name, or all menus if {path} is an
- empty string. Example: >
- :echo menu_get('File','')
- :echo menu_get('')
-<
- {modes} is a string of zero or more modes (see |maparg()| or
- |creating-menus| for the list of modes). "a" means "all".
-
- Example: >
- nnoremenu &Test.Test inormal
- inoremenu Test.Test insert
- vnoremenu Test.Test x
- echo menu_get("")
-
-< returns something like this: >
-
- [ {
- "hidden": 0,
- "name": "Test",
- "priority": 500,
- "shortcut": 84,
- "submenus": [ {
- "hidden": 0,
- "mappings": {
- i": {
- "enabled": 1,
- "noremap": 1,
- "rhs": "insert",
- "sid": 1,
- "silent": 0
- },
- n": { ... },
- s": { ... },
- v": { ... }
- },
- "name": "Test",
- "priority": 500,
- "shortcut": 0
- } ]
- } ]
-<
-
- *min()*
-min({expr}) Return the minimum value of all items in {expr}.
- {expr} can be a |List| or a |Dictionary|. For a Dictionary,
- it returns the minimum of all values in the Dictionary.
- If {expr} is neither a List nor a Dictionary, or one of the
- items in {expr} cannot be used as a Number this results in
- an error. An empty |List| or |Dictionary| results in zero.
-
- Can also be used as a |method|: >
- mylist->min()
-
-< *mkdir()* *E739*
-mkdir({name} [, {path} [, {prot}]])
- Create directory {name}.
- If {path} is "p" then intermediate directories are created as
- necessary. Otherwise it must be "".
- If {prot} is given it is used to set the protection bits of
- the new directory. The default is 0o755 (rwxr-xr-x: r/w for
- the user, readable for others). Use 0o700 to make it
- unreadable for others.
-
- {prot} is applied for all parts of {name}. Thus if you create
- /tmp/foo/bar then /tmp/foo will be created with 0700. Example: >
- :call mkdir($HOME . "/tmp/foo/bar", "p", 0700)
-< This function is not available in the |sandbox|.
-
- If you try to create an existing directory with {path} set to
- "p" mkdir() will silently exit.
-
- The function result is a Number, which is TRUE if the call was
- successful or FALSE if the directory creation failed or partly
- failed.
-
- Can also be used as a |method|: >
- GetName()->mkdir()
-<
- *mode()*
-mode([expr]) Return a string that indicates the current mode.
- If [expr] is supplied and it evaluates to a non-zero Number or
- a non-empty String (|non-zero-arg|), then the full mode is
- returned, otherwise only the first letter is returned.
-
- n Normal
- no Operator-pending
- nov Operator-pending (forced charwise |o_v|)
- noV Operator-pending (forced linewise |o_V|)
- noCTRL-V Operator-pending (forced blockwise |o_CTRL-V|)
- CTRL-V is one character
- niI Normal using |i_CTRL-O| in |Insert-mode|
- niR Normal using |i_CTRL-O| in |Replace-mode|
- niV Normal using |i_CTRL-O| in |Virtual-Replace-mode|
- nt Normal in |terminal-emulator| (insert goes to
- Terminal mode)
- v Visual by character
- vs Visual by character using |v_CTRL-O| in Select mode
- V Visual by line
- Vs Visual by line using |v_CTRL-O| in Select mode
- CTRL-V Visual blockwise
- CTRL-Vs Visual blockwise using |v_CTRL-O| in Select mode
- s Select by character
- S Select by line
- CTRL-S Select blockwise
- i Insert
- ic Insert mode completion |compl-generic|
- ix Insert mode |i_CTRL-X| completion
- R Replace |R|
- Rc Replace mode completion |compl-generic|
- Rx Replace mode |i_CTRL-X| completion
- Rv Virtual Replace |gR|
- Rvc Virtual Replace mode completion |compl-generic|
- Rvx Virtual Replace mode |i_CTRL-X| completion
- c Command-line editing
- cv Vim Ex mode |gQ|
- r Hit-enter prompt
- rm The -- more -- prompt
- r? A |:confirm| query of some sort
- ! Shell or external command is executing
- t Terminal mode: keys go to the job
-
- This is useful in the 'statusline' option or when used
- with |remote_expr()| In most other places it always returns
- "c" or "n".
- Note that in the future more modes and more specific modes may
- be added. It's better not to compare the whole string but only
- the leading character(s).
- Also see |visualmode()|.
-
- Can also be used as a |method|: >
- DoFull()->mode()
-
-msgpackdump({list} [, {type}]) *msgpackdump()*
- Convert a list of VimL objects to msgpack. Returned value is a
- |readfile()|-style list. When {type} contains "B", a |Blob| is
- returned instead. Example: >
- call writefile(msgpackdump([{}]), 'fname.mpack', 'b')
-< or, using a |Blob|: >
- call writefile(msgpackdump([{}], 'B'), 'fname.mpack')
-<
- This will write the single 0x80 byte to a `fname.mpack` file
- (dictionary with zero items is represented by 0x80 byte in
- messagepack).
-
- Limitations: *E5004* *E5005*
- 1. |Funcref|s cannot be dumped.
- 2. Containers that reference themselves cannot be dumped.
- 3. Dictionary keys are always dumped as STR strings.
- 4. Other strings and |Blob|s are always dumped as BIN strings.
- 5. Points 3. and 4. do not apply to |msgpack-special-dict|s.
-
-msgpackparse({data}) *msgpackparse()*
- Convert a |readfile()|-style list or a |Blob| to a list of
- VimL objects.
- Example: >
- let fname = expand('~/.config/nvim/shada/main.shada')
- let mpack = readfile(fname, 'b')
- let shada_objects = msgpackparse(mpack)
-< This will read ~/.config/nvim/shada/main.shada file to
- `shada_objects` list.
-
- Limitations:
- 1. Mapping ordering is not preserved unless messagepack
- mapping is dumped using generic mapping
- (|msgpack-special-map|).
- 2. Since the parser aims to preserve all data untouched
- (except for 1.) some strings are parsed to
- |msgpack-special-dict| format which is not convenient to
- use.
- *msgpack-special-dict*
- Some messagepack strings may be parsed to special
- dictionaries. Special dictionaries are dictionaries which
-
- 1. Contain exactly two keys: `_TYPE` and `_VAL`.
- 2. `_TYPE` key is one of the types found in |v:msgpack_types|
- variable.
- 3. Value for `_VAL` has the following format (Key column
- contains name of the key from |v:msgpack_types|):
-
- Key Value ~
- nil Zero, ignored when dumping. Not returned by
- |msgpackparse()| since |v:null| was introduced.
- boolean One or zero. When dumping it is only checked that
- value is a |Number|. Not returned by |msgpackparse()|
- since |v:true| and |v:false| were introduced.
- integer |List| with four numbers: sign (-1 or 1), highest two
- bits, number with bits from 62nd to 31st, lowest 31
- bits. I.e. to get actual number one will need to use
- code like >
- _VAL[0] * ((_VAL[1] << 62)
- & (_VAL[2] << 31)
- & _VAL[3])
-< Special dictionary with this type will appear in
- |msgpackparse()| output under one of the following
- circumstances:
- 1. |Number| is 32-bit and value is either above
- INT32_MAX or below INT32_MIN.
- 2. |Number| is 64-bit and value is above INT64_MAX. It
- cannot possibly be below INT64_MIN because msgpack
- C parser does not support such values.
- float |Float|. This value cannot possibly appear in
- |msgpackparse()| output.
- string |readfile()|-style list of strings. This value will
- appear in |msgpackparse()| output if string contains
- zero byte or if string is a mapping key and mapping is
- being represented as special dictionary for other
- reasons.
- binary |String|, or |Blob| if binary string contains zero
- byte. This value cannot appear in |msgpackparse()|
- output since blobs were introduced.
- array |List|. This value cannot appear in |msgpackparse()|
- output.
- *msgpack-special-map*
- map |List| of |List|s with two items (key and value) each.
- This value will appear in |msgpackparse()| output if
- parsed mapping contains one of the following keys:
- 1. Any key that is not a string (including keys which
- are binary strings).
- 2. String with NUL byte inside.
- 3. Duplicate key.
- 4. Empty key.
- ext |List| with two values: first is a signed integer
- representing extension type. Second is
- |readfile()|-style list of strings.
-
-nextnonblank({lnum}) *nextnonblank()*
- Return the line number of the first line at or below {lnum}
- that is not blank. Example: >
- if getline(nextnonblank(1)) =~ "Java"
-< When {lnum} is invalid or there is no non-blank line at or
- below it, zero is returned.
- {lnum} is used like with |getline()|.
- See also |prevnonblank()|.
-
- Can also be used as a |method|: >
- GetLnum()->nextnonblank()
-
-nr2char({expr} [, {utf8}]) *nr2char()*
- Return a string with a single character, which has the number
- value {expr}. Examples: >
- nr2char(64) returns "@"
- nr2char(32) returns " "
-< Example for "utf-8": >
- nr2char(300) returns I with bow character
-< UTF-8 encoding is always used, {utf8} option has no effect,
- and exists only for backwards-compatibility.
- Note that a NUL character in the file is specified with
- nr2char(10), because NULs are represented with newline
- characters. nr2char(0) is a real NUL and terminates the
- string, thus results in an empty string.
-
- Can also be used as a |method|: >
- GetNumber()->nr2char()
-
-nvim_...({...}) *E5555* *nvim_...()* *eval-api*
- Call nvim |api| functions. The type checking of arguments will
- be stricter than for most other builtins. For instance,
- if Integer is expected, a |Number| must be passed in, a
- |String| will not be autoconverted.
- Buffer numbers, as returned by |bufnr()| could be used as
- first argument to nvim_buf_... functions. All functions
- expecting an object (buffer, window or tabpage) can
- also take the numerical value 0 to indicate the current
- (focused) object.
-
-or({expr}, {expr}) *or()*
- Bitwise OR on the two arguments. The arguments are converted
- to a number. A List, Dict or Float argument causes an error.
- Example: >
- :let bits = or(bits, 0x80)
-< Can also be used as a |method|: >
- :let bits = bits->or(0x80)
-
-pathshorten({path}) *pathshorten()*
- Shorten directory names in the path {path} and return the
- result. The tail, the file name, is kept as-is. The other
- components in the path are reduced to single letters. Leading
- '~' and '.' characters are kept. Example: >
- :echo pathshorten('~/.config/nvim/autoload/file1.vim')
-< ~/.c/n/a/file1.vim ~
- It doesn't matter if the path exists or not.
-
- Can also be used as a |method|: >
- GetDirectories()->pathshorten()
-
-perleval({expr}) *perleval()*
- Evaluate |perl| expression {expr} and return its result
- converted to Vim data structures.
- Numbers and strings are returned as they are (strings are
- copied though).
- Lists are represented as Vim |List| type.
- Dictionaries are represented as Vim |Dictionary| type,
- non-string keys result in error.
-
- Note: If you want an array or hash, {expr} must return a
- reference to it.
- Example: >
- :echo perleval('[1 .. 4]')
-< [1, 2, 3, 4]
-
- Can also be used as a |method|: >
- GetExpr()->perleval()
-
-pow({x}, {y}) *pow()*
- Return the power of {x} to the exponent {y} as a |Float|.
- {x} and {y} must evaluate to a |Float| or a |Number|.
- Examples: >
- :echo pow(3, 3)
-< 27.0 >
- :echo pow(2, 16)
-< 65536.0 >
- :echo pow(32, 0.20)
-< 2.0
-
- Can also be used as a |method|: >
- Compute()->pow(3)
-
-prevnonblank({lnum}) *prevnonblank()*
- Return the line number of the first line at or above {lnum}
- that is not blank. Example: >
- let ind = indent(prevnonblank(v:lnum - 1))
-< When {lnum} is invalid or there is no non-blank line at or
- above it, zero is returned.
- {lnum} is used like with |getline()|.
- Also see |nextnonblank()|.
-
- Can also be used as a |method|: >
- GetLnum()->prevnonblank()
-
-printf({fmt}, {expr1} ...) *printf()*
- Return a String with {fmt}, where "%" items are replaced by
- the formatted form of their respective arguments. Example: >
- printf("%4d: E%d %.30s", lnum, errno, msg)
-< May result in:
- " 99: E42 asdfasdfasdfasdfasdfasdfasdfas" ~
-
- When used as a |method| the base is passed as the second
- argument: >
- Compute()->printf("result: %d")
-
-< Often used items are:
- %s string
- %6S string right-aligned in 6 display cells
- %6s string right-aligned in 6 bytes
- %.9s string truncated to 9 bytes
- %c single byte
- %d decimal number
- %5d decimal number padded with spaces to 5 characters
- %b binary number
- %08b binary number padded with zeros to at least 8 characters
- %B binary number using upper case letters
- %x hex number
- %04x hex number padded with zeros to at least 4 characters
- %X hex number using upper case letters
- %o octal number
- %f floating point number as 12.23, inf, -inf or nan
- %F floating point number as 12.23, INF, -INF or NAN
- %e floating point number as 1.23e3, inf, -inf or nan
- %E floating point number as 1.23E3, INF, -INF or NAN
- %g floating point number, as %f or %e depending on value
- %G floating point number, as %F or %E depending on value
- %% the % character itself
- %p representation of the pointer to the container
-
- Conversion specifications start with '%' and end with the
- conversion type. All other characters are copied unchanged to
- the result.
-
- The "%" starts a conversion specification. The following
- arguments appear in sequence:
-
- % [flags] [field-width] [.precision] type
-
- flags
- Zero or more of the following flags:
-
- # The value should be converted to an "alternate
- form". For c, d, and s conversions, this option
- has no effect. For o conversions, the precision
- of the number is increased to force the first
- character of the output string to a zero (except
- if a zero value is printed with an explicit
- precision of zero).
- For x and X conversions, a non-zero result has
- the string "0x" (or "0X" for X conversions)
- prepended to it.
-
- 0 (zero) Zero padding. For all conversions the converted
- value is padded on the left with zeros rather
- than blanks. If a precision is given with a
- numeric conversion (d, o, x, and X), the 0 flag
- is ignored.
-
- - A negative field width flag; the converted value
- is to be left adjusted on the field boundary.
- The converted value is padded on the right with
- blanks, rather than on the left with blanks or
- zeros. A - overrides a 0 if both are given.
-
- ' ' (space) A blank should be left before a positive
- number produced by a signed conversion (d).
-
- + A sign must always be placed before a number
- produced by a signed conversion. A + overrides
- a space if both are used.
-
- field-width
- An optional decimal digit string specifying a minimum
- field width. If the converted value has fewer bytes
- than the field width, it will be padded with spaces on
- the left (or right, if the left-adjustment flag has
- been given) to fill out the field width. For the S
- conversion the count is in cells.
-
- .precision
- An optional precision, in the form of a period '.'
- followed by an optional digit string. If the digit
- string is omitted, the precision is taken as zero.
- This gives the minimum number of digits to appear for
- d, o, x, and X conversions, the maximum number of
- bytes to be printed from a string for s conversions,
- or the maximum number of cells to be printed from a
- string for S conversions.
- For floating point it is the number of digits after
- the decimal point.
-
- type
- A character that specifies the type of conversion to
- be applied, see below.
-
- A field width or precision, or both, may be indicated by an
- asterisk '*' instead of a digit string. In this case, a
- Number argument supplies the field width or precision. A
- negative field width is treated as a left adjustment flag
- followed by a positive field width; a negative precision is
- treated as though it were missing. Example: >
- :echo printf("%d: %.*s", nr, width, line)
-< This limits the length of the text used from "line" to
- "width" bytes.
-
- The conversion specifiers and their meanings are:
-
- *printf-d* *printf-b* *printf-B* *printf-o* *printf-x* *printf-X*
- dbBoxX The Number argument is converted to signed decimal (d),
- unsigned binary (b and B), unsigned octal (o), or
- unsigned hexadecimal (x and X) notation. The letters
- "abcdef" are used for x conversions; the letters
- "ABCDEF" are used for X conversions. The precision, if
- any, gives the minimum number of digits that must
- appear; if the converted value requires fewer digits, it
- is padded on the left with zeros. In no case does a
- non-existent or small field width cause truncation of a
- numeric field; if the result of a conversion is wider
- than the field width, the field is expanded to contain
- the conversion result.
- The 'h' modifier indicates the argument is 16 bits.
- The 'l' modifier indicates the argument is 32 bits.
- The 'L' modifier indicates the argument is 64 bits.
- Generally, these modifiers are not useful. They are
- ignored when type is known from the argument.
-
- i alias for d
- D alias for ld
- U alias for lu
- O alias for lo
-
- *printf-c*
- c The Number argument is converted to a byte, and the
- resulting character is written.
-
- *printf-s*
- s The text of the String argument is used. If a
- precision is specified, no more bytes than the number
- specified are used.
- If the argument is not a String type, it is
- automatically converted to text with the same format
- as ":echo".
- *printf-S*
- S The text of the String argument is used. If a
- precision is specified, no more display cells than the
- number specified are used.
-
- *printf-f* *E807*
- f F The Float argument is converted into a string of the
- form 123.456. The precision specifies the number of
- digits after the decimal point. When the precision is
- zero the decimal point is omitted. When the precision
- is not specified 6 is used. A really big number
- (out of range or dividing by zero) results in "inf"
- or "-inf" with %f (INF or -INF with %F).
- "0.0 / 0.0" results in "nan" with %f (NAN with %F).
- Example: >
- echo printf("%.2f", 12.115)
-< 12.12
- Note that roundoff depends on the system libraries.
- Use |round()| when in doubt.
-
- *printf-e* *printf-E*
- e E The Float argument is converted into a string of the
- form 1.234e+03 or 1.234E+03 when using 'E'. The
- precision specifies the number of digits after the
- decimal point, like with 'f'.
-
- *printf-g* *printf-G*
- g G The Float argument is converted like with 'f' if the
- value is between 0.001 (inclusive) and 10000000.0
- (exclusive). Otherwise 'e' is used for 'g' and 'E'
- for 'G'. When no precision is specified superfluous
- zeroes and '+' signs are removed, except for the zero
- immediately after the decimal point. Thus 10000000.0
- results in 1.0e7.
-
- *printf-%*
- % A '%' is written. No argument is converted. The
- complete conversion specification is "%%".
-
- When a Number argument is expected a String argument is also
- accepted and automatically converted.
- When a Float or String argument is expected a Number argument
- is also accepted and automatically converted.
- Any other argument type results in an error message.
-
- *E766* *E767*
- The number of {exprN} arguments must exactly match the number
- of "%" items. If there are not sufficient or too many
- arguments an error is given. Up to 18 arguments can be used.
-
-prompt_getprompt({buf}) *prompt_getprompt()*
- Returns the effective prompt text for buffer {buf}. {buf} can
- be a buffer name or number. See |prompt-buffer|.
-
- If the buffer doesn't exist or isn't a prompt buffer, an empty
- string is returned.
-
- Can also be used as a |method|: >
- GetBuffer()->prompt_getprompt()
-
-prompt_setcallback({buf}, {expr}) *prompt_setcallback()*
- Set prompt callback for buffer {buf} to {expr}. When {expr}
- is an empty string the callback is removed. This has only
- effect if {buf} has 'buftype' set to "prompt".
-
- The callback is invoked when pressing Enter. The current
- buffer will always be the prompt buffer. A new line for a
- prompt is added before invoking the callback, thus the prompt
- for which the callback was invoked will be in the last but one
- line.
- If the callback wants to add text to the buffer, it must
- insert it above the last line, since that is where the current
- prompt is. This can also be done asynchronously.
- The callback is invoked with one argument, which is the text
- that was entered at the prompt. This can be an empty string
- if the user only typed Enter.
- Example: >
- call prompt_setcallback(bufnr(''), function('s:TextEntered'))
- func s:TextEntered(text)
- if a:text == 'exit' || a:text == 'quit'
- stopinsert
- close
- else
- call append(line('$') - 1, 'Entered: "' . a:text . '"')
- " Reset 'modified' to allow the buffer to be closed.
- set nomodified
- endif
- endfunc
-
-< Can also be used as a |method|: >
- GetBuffer()->prompt_setcallback(callback)
-
-prompt_setinterrupt({buf}, {expr}) *prompt_setinterrupt()*
- Set a callback for buffer {buf} to {expr}. When {expr} is an
- empty string the callback is removed. This has only effect if
- {buf} has 'buftype' set to "prompt".
-
- This callback will be invoked when pressing CTRL-C in Insert
- mode. Without setting a callback Vim will exit Insert mode,
- as in any buffer.
-
- Can also be used as a |method|: >
- GetBuffer()->prompt_setinterrupt(callback)
-
-prompt_setprompt({buf}, {text}) *prompt_setprompt()*
- Set prompt for buffer {buf} to {text}. You most likely want
- {text} to end in a space.
- The result is only visible if {buf} has 'buftype' set to
- "prompt". Example: >
- call prompt_setprompt(bufnr(''), 'command: ')
-<
- Can also be used as a |method|: >
- GetBuffer()->prompt_setprompt('command: ')
-
-pum_getpos() *pum_getpos()*
- If the popup menu (see |ins-completion-menu|) is not visible,
- returns an empty |Dictionary|, otherwise, returns a
- |Dictionary| with the following keys:
- height nr of items visible
- width screen cells
- row top screen row (0 first row)
- col leftmost screen column (0 first col)
- size total nr of items
- scrollbar |TRUE| if scrollbar is visible
-
- The values are the same as in |v:event| during |CompleteChanged|.
-
-pumvisible() *pumvisible()*
- Returns non-zero when the popup menu is visible, zero
- otherwise. See |ins-completion-menu|.
- This can be used to avoid some things that would remove the
- popup menu.
-
-py3eval({expr}) *py3eval()*
- Evaluate Python expression {expr} and return its result
- converted to Vim data structures.
- Numbers and strings are returned as they are (strings are
- copied though, Unicode strings are additionally converted to
- UTF-8).
- Lists are represented as Vim |List| type.
- Dictionaries are represented as Vim |Dictionary| type with
- keys converted to strings.
-
- Can also be used as a |method|: >
- GetExpr()->py3eval()
-<
- *E858* *E859*
-pyeval({expr}) *pyeval()*
- Evaluate Python expression {expr} and return its result
- converted to Vim data structures.
- Numbers and strings are returned as they are (strings are
- copied though).
- Lists are represented as Vim |List| type.
- Dictionaries are represented as Vim |Dictionary| type,
- non-string keys result in error.
-
- Can also be used as a |method|: >
- GetExpr()->pyeval()
-
-pyxeval({expr}) *pyxeval()*
- Evaluate Python expression {expr} and return its result
- converted to Vim data structures.
- Uses Python 2 or 3, see |python_x| and 'pyxversion'.
- See also: |pyeval()|, |py3eval()|
-
- Can also be used as a |method|: >
- GetExpr()->pyxeval()
-<
- *E726* *E727*
-range({expr} [, {max} [, {stride}]]) *range()*
- Returns a |List| with Numbers:
- - If only {expr} is specified: [0, 1, ..., {expr} - 1]
- - If {max} is specified: [{expr}, {expr} + 1, ..., {max}]
- - If {stride} is specified: [{expr}, {expr} + {stride}, ...,
- {max}] (increasing {expr} with {stride} each time, not
- producing a value past {max}).
- When the maximum is one before the start the result is an
- empty list. When the maximum is more than one before the
- start this is an error.
- Examples: >
- range(4) " [0, 1, 2, 3]
- range(2, 4) " [2, 3, 4]
- range(2, 9, 3) " [2, 5, 8]
- range(2, -2, -1) " [2, 1, 0, -1, -2]
- range(0) " []
- range(2, 0) " error!
-<
- Can also be used as a |method|: >
- GetExpr()->range()
-<
- *readdir()*
-readdir({directory} [, {expr}])
- Return a list with file and directory names in {directory}.
-
- When {expr} is omitted all entries are included.
- When {expr} is given, it is evaluated to check what to do:
- If {expr} results in -1 then no further entries will
- be handled.
- If {expr} results in 0 then this entry will not be
- added to the list.
- If {expr} results in 1 then this entry will be added
- to the list.
- Each time {expr} is evaluated |v:val| is set to the entry name.
- When {expr} is a function the name is passed as the argument.
- For example, to get a list of files ending in ".txt": >
- readdir(dirname, {n -> n =~ '.txt$'})
-< To skip hidden and backup files: >
- readdir(dirname, {n -> n !~ '^\.\|\~$'})
-
-< If you want to get a directory tree: >
- function! s:tree(dir)
- return {a:dir : map(readdir(a:dir),
- \ {_, x -> isdirectory(x) ?
- \ {x : s:tree(a:dir . '/' . x)} : x})}
- endfunction
- echo s:tree(".")
-<
- Can also be used as a |method|: >
- GetDirName()->readdir()
-<
- *readfile()*
-readfile({fname} [, {type} [, {max}]])
- Read file {fname} and return a |List|, each line of the file
- as an item. Lines are broken at NL characters. Macintosh
- files separated with CR will result in a single long line
- (unless a NL appears somewhere).
- All NUL characters are replaced with a NL character.
- When {type} contains "b" binary mode is used:
- - When the last line ends in a NL an extra empty list item is
- added.
- - No CR characters are removed.
- When {type} contains "B" a |Blob| is returned with the binary
- data of the file unmodified.
- Otherwise:
- - CR characters that appear before a NL are removed.
- - Whether the last line ends in a NL or not does not matter.
- - Any UTF-8 byte order mark is removed from the text.
- When {max} is given this specifies the maximum number of lines
- to be read. Useful if you only want to check the first ten
- lines of a file: >
- :for line in readfile(fname, '', 10)
- : if line =~ 'Date' | echo line | endif
- :endfor
-< When {max} is negative -{max} lines from the end of the file
- are returned, or as many as there are.
- When {max} is zero the result is an empty list.
- Note that without {max} the whole file is read into memory.
- Also note that there is no recognition of encoding. Read a
- file into a buffer if you need to.
- When the file can't be opened an error message is given and
- the result is an empty list.
- Also see |writefile()|.
-
- Can also be used as a |method|: >
- GetFileName()->readfile()
-
-reg_executing() *reg_executing()*
- Returns the single letter name of the register being executed.
- Returns an empty string when no register is being executed.
- See |@|.
-
-reg_recorded() *reg_recorded()*
- Returns the single letter name of the last recorded register.
- Returns an empty string string when nothing was recorded yet.
- See |q| and |Q|.
-
-reg_recording() *reg_recording()*
- Returns the single letter name of the register being recorded.
- Returns an empty string string when not recording. See |q|.
-
-reltime([{start} [, {end}]]) *reltime()*
- Return an item that represents a time value. The item is a
- list with items that depend on the system.
- The item can be passed to |reltimestr()| to convert it to a
- string or |reltimefloat()| to convert to a Float.
-
- Without an argument it returns the current "relative time", an
- implementation-defined value meaningful only when used as an
- argument to |reltime()|, |reltimestr()| and |reltimefloat()|.
-
- With one argument it returns the time passed since the time
- specified in the argument.
- With two arguments it returns the time passed between {start}
- and {end}.
-
- The {start} and {end} arguments must be values returned by
- reltime().
-
- Can also be used as a |method|: >
- GetStart()->reltime()
-<
- Note: |localtime()| returns the current (non-relative) time.
-
-reltimefloat({time}) *reltimefloat()*
- Return a Float that represents the time value of {time}.
- Unit of time is seconds.
- Example:
- let start = reltime()
- call MyFunction()
- let seconds = reltimefloat(reltime(start))
- See the note of reltimestr() about overhead.
- Also see |profiling|.
- If there is an error an empty string is returned
-
- Can also be used as a |method|: >
- reltime(start)->reltimefloat()
-
-reltimestr({time}) *reltimestr()*
- Return a String that represents the time value of {time}.
- This is the number of seconds, a dot and the number of
- microseconds. Example: >
- let start = reltime()
- call MyFunction()
- echo reltimestr(reltime(start))
-< Note that overhead for the commands will be added to the time.
- Leading spaces are used to make the string align nicely. You
- can use split() to remove it. >
- echo split(reltimestr(reltime(start)))[0]
-< Also see |profiling|.
- If there is an error an empty string is returned
-
- Can also be used as a |method|: >
- reltime(start)->reltimestr()
-<
- *remote_expr()* *E449*
-remote_expr({server}, {string} [, {idvar} [, {timeout}]])
- Send the {string} to {server}. The string is sent as an
- expression and the result is returned after evaluation.
- The result must be a String or a |List|. A |List| is turned
- into a String by joining the items with a line break in
- between (not at the end), like with join(expr, "\n").
- If {idvar} is present and not empty, it is taken as the name
- of a variable and a {serverid} for later use with
- |remote_read()| is stored there.
- If {timeout} is given the read times out after this many
- seconds. Otherwise a timeout of 600 seconds is used.
- See also |clientserver| |RemoteReply|.
- This function is not available in the |sandbox|.
- Note: Any errors will cause a local error message to be issued
- and the result will be the empty string.
-
- Variables will be evaluated in the global namespace,
- independent of a function currently being active. Except
- when in debug mode, then local function variables and
- arguments can be evaluated.
-
- Examples: >
- :echo remote_expr("gvim", "2+2")
- :echo remote_expr("gvim1", "b:current_syntax")
-<
-
-remote_foreground({server}) *remote_foreground()*
- Move the Vim server with the name {server} to the foreground.
- The {server} argument is a string.
- This works like: >
- remote_expr({server}, "foreground()")
-< Except that on Win32 systems the client does the work, to work
- around the problem that the OS doesn't always allow the server
- to bring itself to the foreground.
- Note: This does not restore the window if it was minimized,
- like foreground() does.
- This function is not available in the |sandbox|.
- {only in the Win32 GUI and the Win32 console version}
-
-
-remote_peek({serverid} [, {retvar}]) *remote_peek()*
- Returns a positive number if there are available strings
- from {serverid}. Copies any reply string into the variable
- {retvar} if specified. {retvar} must be a string with the
- name of a variable.
- Returns zero if none are available.
- Returns -1 if something is wrong.
- See also |clientserver|.
- This function is not available in the |sandbox|.
- Examples: >
- :let repl = ""
- :echo "PEEK: ".remote_peek(id, "repl").": ".repl
-
-remote_read({serverid}, [{timeout}]) *remote_read()*
- Return the oldest available reply from {serverid} and consume
- it. Unless a {timeout} in seconds is given, it blocks until a
- reply is available.
- See also |clientserver|.
- This function is not available in the |sandbox|.
- Example: >
- :echo remote_read(id)
-<
- *remote_send()* *E241*
-remote_send({server}, {string} [, {idvar}])
- Send the {string} to {server}. The string is sent as input
- keys and the function returns immediately. At the Vim server
- the keys are not mapped |:map|.
- If {idvar} is present, it is taken as the name of a variable
- and a {serverid} for later use with remote_read() is stored
- there.
- See also |clientserver| |RemoteReply|.
- This function is not available in the |sandbox|.
-
- Note: Any errors will be reported in the server and may mess
- up the display.
- Examples: >
- :echo remote_send("gvim", ":DropAndReply ".file, "serverid").
- \ remote_read(serverid)
-
- :autocmd NONE RemoteReply *
- \ echo remote_read(expand("<amatch>"))
- :echo remote_send("gvim", ":sleep 10 | echo ".
- \ 'server2client(expand("<client>"), "HELLO")<CR>')
-<
- *remote_startserver()* *E941* *E942*
-remote_startserver({name})
- Become the server {name}. This fails if already running as a
- server, when |v:servername| is not empty.
-
-remove({list}, {idx} [, {end}]) *remove()*
- Without {end}: Remove the item at {idx} from |List| {list} and
- return the item.
- With {end}: Remove items from {idx} to {end} (inclusive) and
- return a |List| with these items. When {idx} points to the same
- item as {end} a list with one item is returned. When {end}
- points to an item before {idx} this is an error.
- See |list-index| for possible values of {idx} and {end}.
- Example: >
- :echo "last item: " . remove(mylist, -1)
- :call remove(mylist, 0, 9)
-
-< Can also be used as a |method|: >
- mylist->remove(idx)
-
-remove({blob}, {idx} [, {end}])
- Without {end}: Remove the byte at {idx} from |Blob| {blob} and
- return the byte.
- With {end}: Remove bytes from {idx} to {end} (inclusive) and
- return a |Blob| with these bytes. When {idx} points to the same
- byte as {end} a |Blob| with one byte is returned. When {end}
- points to a byte before {idx} this is an error.
- Example: >
- :echo "last byte: " . remove(myblob, -1)
- :call remove(mylist, 0, 9)
-
-remove({dict}, {key})
- Remove the entry from {dict} with key {key} and return it.
- Example: >
- :echo "removed " . remove(dict, "one")
-< If there is no {key} in {dict} this is an error.
-
- Use |delete()| to remove a file.
-
-rename({from}, {to}) *rename()*
- Rename the file by the name {from} to the name {to}. This
- should also work to move files across file systems. The
- result is a Number, which is 0 if the file was renamed
- successfully, and non-zero when the renaming failed.
- NOTE: If {to} exists it is overwritten without warning.
- This function is not available in the |sandbox|.
-
- Can also be used as a |method|: >
- GetOldName()->rename(newname)
-
-repeat({expr}, {count}) *repeat()*
- Repeat {expr} {count} times and return the concatenated
- result. Example: >
- :let separator = repeat('-', 80)
-< When {count} is zero or negative the result is empty.
- When {expr} is a |List| the result is {expr} concatenated
- {count} times. Example: >
- :let longlist = repeat(['a', 'b'], 3)
-< Results in ['a', 'b', 'a', 'b', 'a', 'b'].
-
- Can also be used as a |method|: >
- mylist->repeat(count)
-
-resolve({filename}) *resolve()* *E655*
- On MS-Windows, when {filename} is a shortcut (a .lnk file),
- returns the path the shortcut points to in a simplified form.
- On Unix, repeat resolving symbolic links in all path
- components of {filename} and return the simplified result.
- To cope with link cycles, resolving of symbolic links is
- stopped after 100 iterations.
- On other systems, return the simplified {filename}.
- The simplification step is done as by |simplify()|.
- resolve() keeps a leading path component specifying the
- current directory (provided the result is still a relative
- path name) and also keeps a trailing path separator.
-
- Can also be used as a |method|: >
- GetName()->resolve()
-<
- *reverse()*
-reverse({object})
- Reverse the order of items in {object} in-place.
- {object} can be a |List| or a |Blob|.
- Returns {object}.
- If you want an object to remain unmodified make a copy first: >
- :let revlist = reverse(copy(mylist))
-< Can also be used as a |method|: >
- mylist->reverse()
-
-round({expr}) *round()*
- Round off {expr} to the nearest integral value and return it
- as a |Float|. If {expr} lies halfway between two integral
- values, then use the larger one (away from zero).
- {expr} must evaluate to a |Float| or a |Number|.
- Examples: >
- echo round(0.456)
-< 0.0 >
- echo round(4.5)
-< 5.0 >
- echo round(-4.5)
-< -5.0
-
- Can also be used as a |method|: >
- Compute()->round()
-
-rpcnotify({channel}, {event}[, {args}...]) *rpcnotify()*
- Sends {event} to {channel} via |RPC| and returns immediately.
- If {channel} is 0, the event is broadcast to all channels.
- Example: >
- :au VimLeave call rpcnotify(0, "leaving")
-
-rpcrequest({channel}, {method}[, {args}...]) *rpcrequest()*
- Sends a request to {channel} to invoke {method} via
- |RPC| and blocks until a response is received.
- Example: >
- :let result = rpcrequest(rpc_chan, "func", 1, 2, 3)
-
-rpcstart({prog}[, {argv}]) *rpcstart()*
- Deprecated. Replace >
- :let id = rpcstart('prog', ['arg1', 'arg2'])
-< with >
- :let id = jobstart(['prog', 'arg1', 'arg2'], {'rpc': v:true})
-
-rubyeval({expr}) *rubyeval()*
- Evaluate Ruby expression {expr} and return its result
- converted to Vim data structures.
- Numbers, floats and strings are returned as they are (strings
- are copied though).
- Arrays are represented as Vim |List| type.
- Hashes are represented as Vim |Dictionary| type.
- Other objects are represented as strings resulted from their
- "Object#to_s" method.
-
- Can also be used as a |method|: >
- GetRubyExpr()->rubyeval()
-
-screenattr({row}, {col}) *screenattr()*
- Like |screenchar()|, but return the attribute. This is a rather
- arbitrary number that can only be used to compare to the
- attribute at other positions.
-
- Can also be used as a |method|: >
- GetRow()->screenattr(col)
-
-screenchar({row}, {col}) *screenchar()*
- The result is a Number, which is the character at position
- [row, col] on the screen. This works for every possible
- screen position, also status lines, window separators and the
- command line. The top left position is row one, column one
- The character excludes composing characters. For double-byte
- encodings it may only be the first byte.
- This is mainly to be used for testing.
- Returns -1 when row or col is out of range.
-
- Can also be used as a |method|: >
- GetRow()->screenchar(col)
-
-screenchars({row}, {col}) *screenchars()*
- The result is a List of Numbers. The first number is the same
- as what |screenchar()| returns. Further numbers are
- composing characters on top of the base character.
- This is mainly to be used for testing.
- Returns an empty List when row or col is out of range.
-
- Can also be used as a |method|: >
- GetRow()->screenchars(col)
-
-screencol() *screencol()*
- The result is a Number, which is the current screen column of
- the cursor. The leftmost column has number 1.
- This function is mainly used for testing.
-
- Note: Always returns the current screen column, thus if used
- in a command (e.g. ":echo screencol()") it will return the
- column inside the command line, which is 1 when the command is
- executed. To get the cursor position in the file use one of
- the following mappings: >
- nnoremap <expr> GG ":echom ".screencol()."\n"
- nnoremap <silent> GG :echom screencol()<CR>
- noremap GG <Cmd>echom screencol()<Cr>
-<
-screenpos({winid}, {lnum}, {col}) *screenpos()*
- The result is a Dict with the screen position of the text
- character in window {winid} at buffer line {lnum} and column
- {col}. {col} is a one-based byte index.
- The Dict has these members:
- row screen row
- col first screen column
- endcol last screen column
- curscol cursor screen column
- If the specified position is not visible, all values are zero.
- The "endcol" value differs from "col" when the character
- occupies more than one screen cell. E.g. for a Tab "col" can
- be 1 and "endcol" can be 8.
- The "curscol" value is where the cursor would be placed. For
- a Tab it would be the same as "endcol", while for a double
- width character it would be the same as "col".
- The |conceal| feature is ignored here, the column numbers are
- as if 'conceallevel' is zero. You can set the cursor to the
- right position and use |screencol()| to get the value with
- |conceal| taken into account.
-
- Can also be used as a |method|: >
- GetWinid()->screenpos(lnum, col)
-
-screenrow() *screenrow()*
- The result is a Number, which is the current screen row of the
- cursor. The top line has number one.
- This function is mainly used for testing.
- Alternatively you can use |winline()|.
-
- Note: Same restrictions as with |screencol()|.
-
-screenstring({row}, {col}) *screenstring()*
- The result is a String that contains the base character and
- any composing characters at position [row, col] on the screen.
- This is like |screenchars()| but returning a String with the
- characters.
- This is mainly to be used for testing.
- Returns an empty String when row or col is out of range.
-
- Can also be used as a |method|: >
- GetRow()->screenstring(col)
-
-search({pattern} [, {flags} [, {stopline} [, {timeout}]]]) *search()*
- Search for regexp pattern {pattern}. The search starts at the
- cursor position (you can use |cursor()| to set it).
-
- When a match has been found its line number is returned.
- If there is no match a 0 is returned and the cursor doesn't
- move. No error message is given.
-
- {flags} is a String, which can contain these character flags:
- 'b' search Backward instead of forward
- 'c' accept a match at the Cursor position
- 'e' move to the End of the match
- 'n' do Not move the cursor
- 'p' return number of matching sub-Pattern (see below)
- 's' Set the ' mark at the previous location of the cursor
- 'w' Wrap around the end of the file
- 'W' don't Wrap around the end of the file
- 'z' start searching at the cursor column instead of Zero
- If neither 'w' or 'W' is given, the 'wrapscan' option applies.
-
- If the 's' flag is supplied, the ' mark is set, only if the
- cursor is moved. The 's' flag cannot be combined with the 'n'
- flag.
-
- 'ignorecase', 'smartcase' and 'magic' are used.
-
- When the 'z' flag is not given, forward searching always
- starts in column zero and then matches before the cursor are
- skipped. When the 'c' flag is present in 'cpo' the next
- search starts after the match. Without the 'c' flag the next
- search starts one column further. This matters for
- overlapping matches.
- When searching backwards and the 'z' flag is given then the
- search starts in column zero, thus no match in the current
- line will be found (unless wrapping around the end of the
- file).
-
- When the {stopline} argument is given then the search stops
- after searching this line. This is useful to restrict the
- search to a range of lines. Examples: >
- let match = search('(', 'b', line("w0"))
- let end = search('END', '', line("w$"))
-< When {stopline} is used and it is not zero this also implies
- that the search does not wrap around the end of the file.
- A zero value is equal to not giving the argument.
-
- When the {timeout} argument is given the search stops when
- more than this many milliseconds have passed. Thus when
- {timeout} is 500 the search stops after half a second.
- The value must not be negative. A zero value is like not
- giving the argument.
-
- *search()-sub-match*
- With the 'p' flag the returned value is one more than the
- first sub-match in \(\). One if none of them matched but the
- whole pattern did match.
- To get the column number too use |searchpos()|.
-
- The cursor will be positioned at the match, unless the 'n'
- flag is used.
-
- Example (goes over all files in the argument list): >
- :let n = 1
- :while n <= argc() " loop over all files in arglist
- : exe "argument " . n
- : " start at the last char in the file and wrap for the
- : " first search to find match at start of file
- : normal G$
- : let flags = "w"
- : while search("foo", flags) > 0
- : s/foo/bar/g
- : let flags = "W"
- : endwhile
- : update " write the file if modified
- : let n = n + 1
- :endwhile
-<
- Example for using some flags: >
- :echo search('\<if\|\(else\)\|\(endif\)', 'ncpe')
-< This will search for the keywords "if", "else", and "endif"
- under or after the cursor. Because of the 'p' flag, it
- returns 1, 2, or 3 depending on which keyword is found, or 0
- if the search fails. With the cursor on the first word of the
- line:
- if (foo == 0) | let foo = foo + 1 | endif ~
- the function returns 1. Without the 'c' flag, the function
- finds the "endif" and returns 3. The same thing happens
- without the 'e' flag if the cursor is on the "f" of "if".
- The 'n' flag tells the function not to move the cursor.
-
- Can also be used as a |method|: >
- GetPattern()->search()
-
-searchcount([{options}]) *searchcount()*
- Get or update the last search count, like what is displayed
- without the "S" flag in 'shortmess'. This works even if
- 'shortmess' does contain the "S" flag.
-
- This returns a Dictionary. The dictionary is empty if the
- previous pattern was not set and "pattern" was not specified.
-
- key type meaning ~
- current |Number| current position of match;
- 0 if the cursor position is
- before the first match
- exact_match |Boolean| 1 if "current" is matched on
- "pos", otherwise 0
- total |Number| total count of matches found
- incomplete |Number| 0: search was fully completed
- 1: recomputing was timed out
- 2: max count exceeded
-
- For {options} see further down.
-
- To get the last search count when |n| or |N| was pressed, call
- this function with `recompute: 0` . This sometimes returns
- wrong information because |n| and |N|'s maximum count is 99.
- If it exceeded 99 the result must be max count + 1 (100). If
- you want to get correct information, specify `recompute: 1`: >
-
- " result == maxcount + 1 (100) when many matches
- let result = searchcount(#{recompute: 0})
-
- " Below returns correct result (recompute defaults
- " to 1)
- let result = searchcount()
-<
- The function is useful to add the count to |statusline|: >
- function! LastSearchCount() abort
- let result = searchcount(#{recompute: 0})
- if empty(result)
- return ''
- endif
- if result.incomplete ==# 1 " timed out
- return printf(' /%s [?/??]', @/)
- elseif result.incomplete ==# 2 " max count exceeded
- if result.total > result.maxcount &&
- \ result.current > result.maxcount
- return printf(' /%s [>%d/>%d]', @/,
- \ result.current, result.total)
- elseif result.total > result.maxcount
- return printf(' /%s [%d/>%d]', @/,
- \ result.current, result.total)
- endif
- endif
- return printf(' /%s [%d/%d]', @/,
- \ result.current, result.total)
- endfunction
- let &statusline .= '%{LastSearchCount()}'
-
- " Or if you want to show the count only when
- " 'hlsearch' was on
- " let &statusline .=
- " \ '%{v:hlsearch ? LastSearchCount() : ""}'
-<
- You can also update the search count, which can be useful in a
- |CursorMoved| or |CursorMovedI| autocommand: >
-
- autocmd CursorMoved,CursorMovedI *
- \ let s:searchcount_timer = timer_start(
- \ 200, function('s:update_searchcount'))
- function! s:update_searchcount(timer) abort
- if a:timer ==# s:searchcount_timer
- call searchcount(#{
- \ recompute: 1, maxcount: 0, timeout: 100})
- redrawstatus
- endif
- endfunction
-<
- This can also be used to count matched texts with specified
- pattern in the current buffer using "pattern": >
-
- " Count '\<foo\>' in this buffer
- " (Note that it also updates search count)
- let result = searchcount(#{pattern: '\<foo\>'})
-
- " To restore old search count by old pattern,
- " search again
- call searchcount()
-<
- {options} must be a Dictionary. It can contain:
- key type meaning ~
- recompute |Boolean| if |TRUE|, recompute the count
- like |n| or |N| was executed.
- otherwise returns the last
- result by |n|, |N|, or this
- function is returned.
- (default: |TRUE|)
- pattern |String| recompute if this was given
- and different with |@/|.
- this works as same as the
- below command is executed
- before calling this function >
- let @/ = pattern
-< (default: |@/|)
- timeout |Number| 0 or negative number is no
- timeout. timeout milliseconds
- for recomputing the result
- (default: 0)
- maxcount |Number| 0 or negative number is no
- limit. max count of matched
- text while recomputing the
- result. if search exceeded
- total count, "total" value
- becomes `maxcount + 1`
- (default: 0)
- pos |List| `[lnum, col, off]` value
- when recomputing the result.
- this changes "current" result
- value. see |cursor()|, |getpos()
- (default: cursor's position)
-
- Can also be used as a |method|: >
- GetSearchOpts()->searchcount()
-<
-searchdecl({name} [, {global} [, {thisblock}]]) *searchdecl()*
- Search for the declaration of {name}.
-
- With a non-zero {global} argument it works like |gD|, find
- first match in the file. Otherwise it works like |gd|, find
- first match in the function.
-
- With a non-zero {thisblock} argument matches in a {} block
- that ends before the cursor position are ignored. Avoids
- finding variable declarations only valid in another scope.
-
- Moves the cursor to the found match.
- Returns zero for success, non-zero for failure.
- Example: >
- if searchdecl('myvar') == 0
- echo getline('.')
- endif
-<
- Can also be used as a |method|: >
- GetName()->searchdecl()
-<
- *searchpair()*
-searchpair({start}, {middle}, {end} [, {flags} [, {skip}
- [, {stopline} [, {timeout}]]]])
- Search for the match of a nested start-end pair. This can be
- used to find the "endif" that matches an "if", while other
- if/endif pairs in between are ignored.
- The search starts at the cursor. The default is to search
- forward, include 'b' in {flags} to search backward.
- If a match is found, the cursor is positioned at it and the
- line number is returned. If no match is found 0 or -1 is
- returned and the cursor doesn't move. No error message is
- given.
-
- {start}, {middle} and {end} are patterns, see |pattern|. They
- must not contain \( \) pairs. Use of \%( \) is allowed. When
- {middle} is not empty, it is found when searching from either
- direction, but only when not in a nested start-end pair. A
- typical use is: >
- searchpair('\<if\>', '\<else\>', '\<endif\>')
-< By leaving {middle} empty the "else" is skipped.
-
- {flags} 'b', 'c', 'n', 's', 'w' and 'W' are used like with
- |search()|. Additionally:
- 'r' Repeat until no more matches found; will find the
- outer pair. Implies the 'W' flag.
- 'm' Return number of matches instead of line number with
- the match; will be > 1 when 'r' is used.
- Note: it's nearly always a good idea to use the 'W' flag, to
- avoid wrapping around the end of the file.
-
- When a match for {start}, {middle} or {end} is found, the
- {skip} expression is evaluated with the cursor positioned on
- the start of the match. It should return non-zero if this
- match is to be skipped. E.g., because it is inside a comment
- or a string.
- When {skip} is omitted or empty, every match is accepted.
- When evaluating {skip} causes an error the search is aborted
- and -1 returned.
- {skip} can be a string, a lambda, a funcref or a partial.
- Anything else makes the function fail.
-
- For {stopline} and {timeout} see |search()|.
-
- The value of 'ignorecase' is used. 'magic' is ignored, the
- patterns are used like it's on.
-
- The search starts exactly at the cursor. A match with
- {start}, {middle} or {end} at the next character, in the
- direction of searching, is the first one found. Example: >
- if 1
- if 2
- endif 2
- endif 1
-< When starting at the "if 2", with the cursor on the "i", and
- searching forwards, the "endif 2" is found. When starting on
- the character just before the "if 2", the "endif 1" will be
- found. That's because the "if 2" will be found first, and
- then this is considered to be a nested if/endif from "if 2" to
- "endif 2".
- When searching backwards and {end} is more than one character,
- it may be useful to put "\zs" at the end of the pattern, so
- that when the cursor is inside a match with the end it finds
- the matching start.
-
- Example, to find the "endif" command in a Vim script: >
-
- :echo searchpair('\<if\>', '\<el\%[seif]\>', '\<en\%[dif]\>', 'W',
- \ 'getline(".") =~ "^\\s*\""')
-
-< The cursor must be at or after the "if" for which a match is
- to be found. Note that single-quote strings are used to avoid
- having to double the backslashes. The skip expression only
- catches comments at the start of a line, not after a command.
- Also, a word "en" or "if" halfway through a line is considered
- a match.
- Another example, to search for the matching "{" of a "}": >
-
- :echo searchpair('{', '', '}', 'bW')
-
-< This works when the cursor is at or before the "}" for which a
- match is to be found. To reject matches that syntax
- highlighting recognized as strings: >
-
- :echo searchpair('{', '', '}', 'bW',
- \ 'synIDattr(synID(line("."), col("."), 0), "name") =~? "string"')
-<
- *searchpairpos()*
-searchpairpos({start}, {middle}, {end} [, {flags} [, {skip}
- [, {stopline} [, {timeout}]]]])
- Same as |searchpair()|, but returns a |List| with the line and
- column position of the match. The first element of the |List|
- is the line number and the second element is the byte index of
- the column position of the match. If no match is found,
- returns [0, 0]. >
-
- :let [lnum,col] = searchpairpos('{', '', '}', 'n')
-<
- See |match-parens| for a bigger and more useful example.
-
-searchpos({pattern} [, {flags} [, {stopline} [, {timeout}]]]) *searchpos()*
- Same as |search()|, but returns a |List| with the line and
- column position of the match. The first element of the |List|
- is the line number and the second element is the byte index of
- the column position of the match. If no match is found,
- returns [0, 0].
- Example: >
- :let [lnum, col] = searchpos('mypattern', 'n')
-
-< When the 'p' flag is given then there is an extra item with
- the sub-pattern match number |search()-sub-match|. Example: >
- :let [lnum, col, submatch] = searchpos('\(\l\)\|\(\u\)', 'np')
-< In this example "submatch" is 2 when a lowercase letter is
- found |/\l|, 3 when an uppercase letter is found |/\u|.
-
- Can also be used as a |method|: >
- GetPattern()->searchpos()
-
-server2client({clientid}, {string}) *server2client()*
- Send a reply string to {clientid}. The most recent {clientid}
- that sent a string can be retrieved with expand("<client>").
- Note:
- Returns zero for success, -1 for failure.
- This id has to be stored before the next command can be
- received. I.e. before returning from the received command and
- before calling any commands that waits for input.
- See also |clientserver|.
- Example: >
- :echo server2client(expand("<client>"), "HELLO")
-
-< Can also be used as a |method|: >
- GetClientId()->server2client(string)
-<
-serverlist() *serverlist()*
- Returns a list of server addresses, or empty if all servers
- were stopped. |serverstart()| |serverstop()|
- Example: >
- :echo serverlist()
-
-serverstart([{address}]) *serverstart()*
- Opens a socket or named pipe at {address} and listens for
- |RPC| messages. Clients can send |API| commands to the address
- to control Nvim. Returns the address string.
-
- If {address} does not contain a colon ":" it is interpreted as
- a named pipe or Unix domain socket path.
-
- Example: >
- if has('win32')
- call serverstart('\\.\pipe\nvim-pipe-1234')
- else
- call serverstart('nvim.sock')
- endif
-<
- If {address} contains a colon ":" it is interpreted as a TCP
- address where the last ":" separates the host and port.
- Assigns a random port if it is empty or 0. Supports IPv4/IPv6.
-
- Example: >
- :call serverstart('::1:12345')
-<
- If no address is given, it is equivalent to: >
- :call serverstart(tempname())
-
-< |$NVIM_LISTEN_ADDRESS| is set to {address} if not already set.
-
-serverstop({address}) *serverstop()*
- Closes the pipe or socket at {address}.
- Returns TRUE if {address} is valid, else FALSE.
- If |$NVIM_LISTEN_ADDRESS| is stopped it is unset.
- If |v:servername| is stopped it is set to the next available
- address returned by |serverlist()|.
-
-setbufline({buf}, {lnum}, {text}) *setbufline()*
- Set line {lnum} to {text} in buffer {buf}. This works like
- |setline()| for the specified buffer.
-
- This function works only for loaded buffers. First call
- |bufload()| if needed.
-
- To insert lines use |appendbufline()|.
- Any text properties in {lnum} are cleared.
-
- {text} can be a string to set one line, or a list of strings
- to set multiple lines. If the list extends below the last
- line then those lines are added.
-
- For the use of {buf}, see |bufname()| above.
-
- {lnum} is used like with |setline()|.
- Use "$" to refer to the last line in buffer {buf}.
- When {lnum} is just below the last line the {text} will be
- added below the last line.
- On success 0 is returned, on failure 1 is returned.
-
- If {buf} is not a valid buffer or {lnum} is not valid, an
- error message is given.
-
- Can also be used as a |method|, the base is passed as the
- third argument: >
- GetText()->setbufline(buf, lnum)
-
-setbufvar({buf}, {varname}, {val}) *setbufvar()*
- Set option or local variable {varname} in buffer {buf} to
- {val}.
- This also works for a global or local window option, but it
- doesn't work for a global or local window variable.
- For a local window option the global value is unchanged.
- For the use of {buf}, see |bufname()| above.
- The {varname} argument is a string.
- Note that the variable name without "b:" must be used.
- Examples: >
- :call setbufvar(1, "&mod", 1)
- :call setbufvar("todo", "myvar", "foobar")
-< This function is not available in the |sandbox|.
-
- Can also be used as a |method|, the base is passed as the
- third argument: >
- GetValue()->setbufvar(buf, varname)
-
-setcharsearch({dict}) *setcharsearch()*
- Set the current character search information to {dict},
- which contains one or more of the following entries:
-
- char character which will be used for a subsequent
- |,| or |;| command; an empty string clears the
- character search
- forward direction of character search; 1 for forward,
- 0 for backward
- until type of character search; 1 for a |t| or |T|
- character search, 0 for an |f| or |F|
- character search
-
- This can be useful to save/restore a user's character search
- from a script: >
- :let prevsearch = getcharsearch()
- :" Perform a command which clobbers user's search
- :call setcharsearch(prevsearch)
-< Also see |getcharsearch()|.
-
- Can also be used as a |method|: >
- SavedSearch()->setcharsearch()
-
-setcmdpos({pos}) *setcmdpos()*
- Set the cursor position in the command line to byte position
- {pos}. The first position is 1.
- Use |getcmdpos()| to obtain the current position.
- Only works while editing the command line, thus you must use
- |c_CTRL-\_e|, |c_CTRL-R_=| or |c_CTRL-R_CTRL-R| with '='. For
- |c_CTRL-\_e| and |c_CTRL-R_CTRL-R| with '=' the position is
- set after the command line is set to the expression. For
- |c_CTRL-R_=| it is set after evaluating the expression but
- before inserting the resulting text.
- When the number is too big the cursor is put at the end of the
- line. A number smaller than one has undefined results.
- Returns FALSE when successful, TRUE when not editing the
- command line.
-
- Can also be used as a |method|: >
- GetPos()->setcmdpos()
-
-setenv({name}, {val}) *setenv()*
- Set environment variable {name} to {val}. Example: >
- call setenv('HOME', '/home/myhome')
-
-< When {val} is |v:null| the environment variable is deleted.
- See also |expr-env|.
-
- Can also be used as a |method|, the base is passed as the
- second argument: >
- GetPath()->setenv('PATH')
-
-setfperm({fname}, {mode}) *setfperm()* *chmod*
- Set the file permissions for {fname} to {mode}.
- {mode} must be a string with 9 characters. It is of the form
- "rwxrwxrwx", where each group of "rwx" flags represent, in
- turn, the permissions of the owner of the file, the group the
- file belongs to, and other users. A '-' character means the
- permission is off, any other character means on. Multi-byte
- characters are not supported.
-
- For example "rw-r-----" means read-write for the user,
- readable by the group, not accessible by others. "xx-x-----"
- would do the same thing.
-
- Returns non-zero for success, zero for failure.
-
- Can also be used as a |method|: >
- GetFilename()->setfperm(mode)
-<
- To read permissions see |getfperm()|.
-
-setline({lnum}, {text}) *setline()*
- Set line {lnum} of the current buffer to {text}. To insert
- lines use |append()|. To set lines in another buffer use
- |setbufline()|.
-
- {lnum} is used like with |getline()|.
- When {lnum} is just below the last line the {text} will be
- added below the last line.
-
- If this succeeds, FALSE is returned. If this fails (most likely
- because {lnum} is invalid) TRUE is returned.
-
- Example: >
- :call setline(5, strftime("%c"))
-
-< When {text} is a |List| then line {lnum} and following lines
- will be set to the items in the list. Example: >
- :call setline(5, ['aaa', 'bbb', 'ccc'])
-< This is equivalent to: >
- :for [n, l] in [[5, 'aaa'], [6, 'bbb'], [7, 'ccc']]
- : call setline(n, l)
- :endfor
-
-< Note: The '[ and '] marks are not set.
-
- Can also be used as a |method|, the base is passed as the
- second argument: >
- GetText()->setline(lnum)
-
-setloclist({nr}, {list} [, {action} [, {what}]]) *setloclist()*
- Create or replace or add to the location list for window {nr}.
- {nr} can be the window number or the |window-ID|.
- When {nr} is zero the current window is used.
-
- For a location list window, the displayed location list is
- modified. For an invalid window number {nr}, -1 is returned.
- Otherwise, same as |setqflist()|.
- Also see |location-list|.
-
- For {action} see |setqflist-action|.
-
- If the optional {what} dictionary argument is supplied, then
- only the items listed in {what} are set. Refer to |setqflist()|
- for the list of supported keys in {what}.
-
- Can also be used as a |method|, the base is passed as the
- second argument: >
- GetLoclist()->setloclist(winnr)
-
-setmatches({list} [, {win}]) *setmatches()*
- Restores a list of matches saved by |getmatches() for the
- current window|. Returns 0 if successful, otherwise -1. All
- current matches are cleared before the list is restored. See
- example for |getmatches()|.
- If {win} is specified, use the window with this number or
- window ID instead of the current window.
-
- Can also be used as a |method|: >
- GetMatches()->setmatches()
-<
- *setpos()*
-setpos({expr}, {list})
- Set the position for String {expr}. Possible values:
- . the cursor
- 'x mark x
-
- {list} must be a |List| with four or five numbers:
- [bufnum, lnum, col, off]
- [bufnum, lnum, col, off, curswant]
-
- "bufnum" is the buffer number. Zero can be used for the
- current buffer. When setting an uppercase mark "bufnum" is
- used for the mark position. For other marks it specifies the
- buffer to set the mark in. You can use the |bufnr()| function
- to turn a file name into a buffer number.
- For setting the cursor and the ' mark "bufnum" is ignored,
- since these are associated with a window, not a buffer.
- Does not change the jumplist.
-
- "lnum" and "col" are the position in the buffer. The first
- column is 1. Use a zero "lnum" to delete a mark. If "col" is
- smaller than 1 then 1 is used.
-
- The "off" number is only used when 'virtualedit' is set. Then
- it is the offset in screen columns from the start of the
- character. E.g., a position within a <Tab> or after the last
- character.
-
- The "curswant" number is only used when setting the cursor
- position. It sets the preferred column for when moving the
- cursor vertically. When the "curswant" number is missing the
- preferred column is not set. When it is present and setting a
- mark position it is not used.
-
- Note that for '< and '> changing the line number may result in
- the marks to be effectively be swapped, so that '< is always
- before '>.
-
- Returns 0 when the position could be set, -1 otherwise.
- An error message is given if {expr} is invalid.
-
- Also see |getpos()| and |getcurpos()|.
-
- This does not restore the preferred column for moving
- vertically; if you set the cursor position with this, |j| and
- |k| motions will jump to previous columns! Use |cursor()| to
- also set the preferred column. Also see the "curswant" key in
- |winrestview()|.
-
- Can also be used as a |method|: >
- GetPosition()->setpos('.')
-
-setqflist({list} [, {action} [, {what}]]) *setqflist()*
- Create or replace or add to the quickfix list.
-
- If the optional {what} dictionary argument is supplied, then
- only the items listed in {what} are set. The first {list}
- argument is ignored. See below for the supported items in
- {what}.
- *setqflist-what*
- When {what} is not present, the items in {list} are used. Each
- item must be a dictionary. Non-dictionary items in {list} are
- ignored. Each dictionary item can contain the following
- entries:
-
- bufnr buffer number; must be the number of a valid
- buffer
- filename name of a file; only used when "bufnr" is not
- present or it is invalid.
- module name of a module; if given it will be used in
- quickfix error window instead of the filename
- lnum line number in the file
- pattern search pattern used to locate the error
- col column number
- vcol when non-zero: "col" is visual column
- when zero: "col" is byte index
- nr error number
- text description of the error
- type single-character error type, 'E', 'W', etc.
- valid recognized error message
-
- The "col", "vcol", "nr", "type" and "text" entries are
- optional. Either "lnum" or "pattern" entry can be used to
- locate a matching error line.
- If the "filename" and "bufnr" entries are not present or
- neither the "lnum" or "pattern" entries are present, then the
- item will not be handled as an error line.
- If both "pattern" and "lnum" are present then "pattern" will
- be used.
- If the "valid" entry is not supplied, then the valid flag is
- set when "bufnr" is a valid buffer or "filename" exists.
- If you supply an empty {list}, the quickfix list will be
- cleared.
- Note that the list is not exactly the same as what
- |getqflist()| returns.
-
- {action} values: *setqflist-action* *E927*
- 'a' The items from {list} are added to the existing
- quickfix list. If there is no existing list, then a
- new list is created.
-
- 'r' The items from the current quickfix list are replaced
- with the items from {list}. This can also be used to
- clear the list: >
- :call setqflist([], 'r')
-<
- 'f' All the quickfix lists in the quickfix stack are
- freed.
-
- If {action} is not present or is set to ' ', then a new list
- is created. The new quickfix list is added after the current
- quickfix list in the stack and all the following lists are
- freed. To add a new quickfix list at the end of the stack,
- set "nr" in {what} to "$".
-
- The following items can be specified in dictionary {what}:
- context quickfix list context. See |quickfix-context|
- efm errorformat to use when parsing text from
- "lines". If this is not present, then the
- 'errorformat' option value is used.
- See |quickfix-parse|
- id quickfix list identifier |quickfix-ID|
- idx index of the current entry in the quickfix
- list specified by 'id' or 'nr'. If set to '$',
- then the last entry in the list is set as the
- current entry. See |quickfix-index|
- items list of quickfix entries. Same as the {list}
- argument.
- lines use 'errorformat' to parse a list of lines and
- add the resulting entries to the quickfix list
- {nr} or {id}. Only a |List| value is supported.
- See |quickfix-parse|
- nr list number in the quickfix stack; zero
- means the current quickfix list and "$" means
- the last quickfix list.
- quickfixtextfunc
- function to get the text to display in the
- quickfix window. The value can be the name of
- a function or a funcref or a lambda. Refer to
- |quickfix-window-function| for an explanation
- of how to write the function and an example.
- title quickfix list title text. See |quickfix-title|
- Unsupported keys in {what} are ignored.
- If the "nr" item is not present, then the current quickfix list
- is modified. When creating a new quickfix list, "nr" can be
- set to a value one greater than the quickfix stack size.
- When modifying a quickfix list, to guarantee that the correct
- list is modified, "id" should be used instead of "nr" to
- specify the list.
-
- Examples (See also |setqflist-examples|): >
- :call setqflist([], 'r', {'title': 'My search'})
- :call setqflist([], 'r', {'nr': 2, 'title': 'Errors'})
- :call setqflist([], 'a', {'id':qfid, 'lines':["F1:10:L10"]})
-<
- Returns zero for success, -1 for failure.
-
- This function can be used to create a quickfix list
- independent of the 'errorformat' setting. Use a command like
- `:cc 1` to jump to the first position.
-
- Can also be used as a |method|, the base is passed as the
- second argument: >
- GetErrorlist()->setqflist()
-<
- *setreg()*
-setreg({regname}, {value} [, {options}])
- Set the register {regname} to {value}.
- The {regname} argument is a string.
-
- {value} may be any value returned by |getreg()| or
- |getreginfo()|, including a |List| or |Dict|.
- If {options} contains "a" or {regname} is upper case,
- then the value is appended.
-
- {options} can also contain a register type specification:
- "c" or "v" |charwise| mode
- "l" or "V" |linewise| mode
- "b" or "<CTRL-V>" |blockwise-visual| mode
- If a number immediately follows "b" or "<CTRL-V>" then this is
- used as the width of the selection - if it is not specified
- then the width of the block is set to the number of characters
- in the longest line (counting a <Tab> as 1 character).
- If {options} contains "u" or '"', then the unnamed register is
- set to point to register {regname}.
-
- If {options} contains no register settings, then the default
- is to use character mode unless {value} ends in a <NL> for
- string {value} and linewise mode for list {value}. Blockwise
- mode is never selected automatically.
- Returns zero for success, non-zero for failure.
-
- *E883*
- Note: you may not use |List| containing more than one item to
- set search and expression registers. Lists containing no
- items act like empty strings.
-
- Examples: >
- :call setreg(v:register, @*)
- :call setreg('*', @%, 'ac')
- :call setreg('a', "1\n2\n3", 'b5')
- :call setreg('"', { 'points_to': 'a'})
-
-< This example shows using the functions to save and restore a
- register: >
- :let var_a = getreginfo()
- :call setreg('a', var_a)
-< or: >
- :let var_a = getreg('a', 1, 1)
- :let var_amode = getregtype('a')
- ....
- :call setreg('a', var_a, var_amode)
-< Note: you may not reliably restore register value
- without using the third argument to |getreg()| as without it
- newlines are represented as newlines AND Nul bytes are
- represented as newlines as well, see |NL-used-for-Nul|.
-
- You can also change the type of a register by appending
- nothing: >
- :call setreg('a', '', 'al')
-
-< Can also be used as a |method|, the base is passed as the
- second argument: >
- GetText()->setreg('a')
-
-settabvar({tabnr}, {varname}, {val}) *settabvar()*
- Set tab-local variable {varname} to {val} in tab page {tabnr}.
- |t:var|
- The {varname} argument is a string.
- Note that the variable name without "t:" must be used.
- Tabs are numbered starting with one.
- This function is not available in the |sandbox|.
-
- Can also be used as a |method|, the base is passed as the
- third argument: >
- GetValue()->settabvar(tab, name)
-
-settabwinvar({tabnr}, {winnr}, {varname}, {val}) *settabwinvar()*
- Set option or local variable {varname} in window {winnr} to
- {val}.
- Tabs are numbered starting with one. For the current tabpage
- use |setwinvar()|.
- {winnr} can be the window number or the |window-ID|.
- When {winnr} is zero the current window is used.
- This also works for a global or local buffer option, but it
- doesn't work for a global or local buffer variable.
- For a local buffer option the global value is unchanged.
- Note that the variable name without "w:" must be used.
- Examples: >
- :call settabwinvar(1, 1, "&list", 0)
- :call settabwinvar(3, 2, "myvar", "foobar")
-< This function is not available in the |sandbox|.
-
- Can also be used as a |method|, the base is passed as the
- fourth argument: >
- GetValue()->settabwinvar(tab, winnr, name)
-
-settagstack({nr}, {dict} [, {action}]) *settagstack()*
- Modify the tag stack of the window {nr} using {dict}.
- {nr} can be the window number or the |window-ID|.
-
- For a list of supported items in {dict}, refer to
- |gettagstack()|. "curidx" takes effect before changing the tag
- stack.
- *E962*
- How the tag stack is modified depends on the {action}
- argument:
- - If {action} is not present or is set to 'r', then the tag
- stack is replaced.
- - If {action} is set to 'a', then new entries from {dict} are
- pushed (added) onto the tag stack.
- - If {action} is set to 't', then all the entries from the
- current entry in the tag stack or "curidx" in {dict} are
- removed and then new entries are pushed to the stack.
-
- The current index is set to one after the length of the tag
- stack after the modification.
-
- Returns zero for success, -1 for failure.
-
- Examples (for more examples see |tagstack-examples|):
- Empty the tag stack of window 3: >
- call settagstack(3, {'items' : []})
-
-< Save and restore the tag stack: >
- let stack = gettagstack(1003)
- " do something else
- call settagstack(1003, stack)
- unlet stack
-<
- Can also be used as a |method|, the base is passed as the
- second argument: >
- GetStack()->settagstack(winnr)
-
-setwinvar({nr}, {varname}, {val}) *setwinvar()*
- Like |settabwinvar()| for the current tab page.
- Examples: >
- :call setwinvar(1, "&list", 0)
- :call setwinvar(2, "myvar", "foobar")
-
-< Can also be used as a |method|, the base is passed as the
- third argument: >
- GetValue()->setwinvar(winnr, name)
-
-sha256({string}) *sha256()*
- Returns a String with 64 hex characters, which is the SHA256
- checksum of {string}.
-
- Can also be used as a |method|: >
- GetText()->sha256()
-
-shellescape({string} [, {special}]) *shellescape()*
- Escape {string} for use as a shell command argument.
-
- On Windows when 'shellslash' is not set, encloses {string} in
- double-quotes and doubles all double-quotes within {string}.
- Otherwise encloses {string} in single-quotes and replaces all
- "'" with "'\''".
-
- If {special} is a |non-zero-arg|:
- - Special items such as "!", "%", "#" and "<cword>" will be
- preceded by a backslash. The backslash will be removed again
- by the |:!| command.
- - The <NL> character is escaped.
-
- If 'shell' contains "csh" in the tail:
- - The "!" character will be escaped. This is because csh and
- tcsh use "!" for history replacement even in single-quotes.
- - The <NL> character is escaped (twice if {special} is
- a |non-zero-arg|).
-
- If 'shell' contains "fish" in the tail, the "\" character will
- be escaped because in fish it is used as an escape character
- inside single quotes.
-
- Example of use with a |:!| command: >
- :exe '!dir ' . shellescape(expand('<cfile>'), 1)
-< This results in a directory listing for the file under the
- cursor. Example of use with |system()|: >
- :call system("chmod +w -- " . shellescape(expand("%")))
-< See also |::S|.
-
- Can also be used as a |method|: >
- GetCommand()->shellescape()
-
-shiftwidth([{col}]) *shiftwidth()*
- Returns the effective value of 'shiftwidth'. This is the
- 'shiftwidth' value unless it is zero, in which case it is the
- 'tabstop' value. To be backwards compatible in indent
- plugins, use this: >
- if exists('*shiftwidth')
- func s:sw()
- return shiftwidth()
- endfunc
- else
- func s:sw()
- return &sw
- endfunc
- endif
-< And then use s:sw() instead of &sw.
-
- When there is one argument {col} this is used as column number
- for which to return the 'shiftwidth' value. This matters for the
- 'vartabstop' feature. If no {col} argument is given, column 1
- will be assumed.
-
- Can also be used as a |method|: >
- GetColumn()->shiftwidth()
-
-sign_ functions are documented here: |sign-functions-details|
-
-simplify({filename}) *simplify()*
- Simplify the file name as much as possible without changing
- the meaning. Shortcuts (on MS-Windows) or symbolic links (on
- Unix) are not resolved. If the first path component in
- {filename} designates the current directory, this will be
- valid for the result as well. A trailing path separator is
- not removed either. On Unix "//path" is unchanged, but
- "///path" is simplified to "/path" (this follows the Posix
- standard).
- Example: >
- simplify("./dir/.././/file/") == "./file/"
-< Note: The combination "dir/.." is only removed if "dir" is
- a searchable directory or does not exist. On Unix, it is also
- removed when "dir" is a symbolic link within the same
- directory. In order to resolve all the involved symbolic
- links before simplifying the path name, use |resolve()|.
-
- Can also be used as a |method|: >
- GetName()->simplify()
-
-sin({expr}) *sin()*
- Return the sine of {expr}, measured in radians, as a |Float|.
- {expr} must evaluate to a |Float| or a |Number|.
- Examples: >
- :echo sin(100)
-< -0.506366 >
- :echo sin(-4.01)
-< 0.763301
-
- Can also be used as a |method|: >
- Compute()->sin()
-
-sinh({expr}) *sinh()*
- Return the hyperbolic sine of {expr} as a |Float| in the range
- [-inf, inf].
- {expr} must evaluate to a |Float| or a |Number|.
- Examples: >
- :echo sinh(0.5)
-< 0.521095 >
- :echo sinh(-0.9)
-< -1.026517
-
- Can also be used as a |method|: >
- Compute()->sinh()
-
-sockconnect({mode}, {address} [, {opts}]) *sockconnect()*
- Connect a socket to an address. If {mode} is "pipe" then
- {address} should be the path of a named pipe. If {mode} is
- "tcp" then {address} should be of the form "host:port" where
- the host should be an ip adderess or host name, and port the
- port number.
-
- Returns a |channel| ID. Close the socket with |chanclose()|.
- Use |chansend()| to send data over a bytes socket, and
- |rpcrequest()| and |rpcnotify()| to communicate with a RPC
- socket.
-
- {opts} is an optional dictionary with these keys:
- |on_data| : callback invoked when data was read from socket
- data_buffered : read socket data in |channel-buffered| mode.
- rpc : If set, |msgpack-rpc| will be used to communicate
- over the socket.
- Returns:
- - The channel ID on success (greater than zero)
- - 0 on invalid arguments or connection failure.
-
-sort({list} [, {func} [, {dict}]]) *sort()* *E702*
- Sort the items in {list} in-place. Returns {list}.
-
- If you want a list to remain unmodified make a copy first: >
- :let sortedlist = sort(copy(mylist))
-
-< When {func} is omitted, is empty or zero, then sort() uses the
- string representation of each item to sort on. Numbers sort
- after Strings, |Lists| after Numbers. For sorting text in the
- current buffer use |:sort|.
-
- When {func} is given and it is '1' or 'i' then case is
- ignored.
-
- When {func} is given and it is 'l' then the current collation
- locale is used for ordering. Implementation details: strcoll()
- is used to compare strings. See |:language| check or set the
- collation locale. |v:collate| can also be used to check the
- current locale. Sorting using the locale typically ignores
- case. Example: >
- " ö is sorted similarly to o with English locale.
- :language collate en_US.UTF8
- :echo sort(['n', 'o', 'O', 'ö', 'p', 'z'], 'l')
-< ['n', 'o', 'O', 'ö', 'p', 'z'] ~
->
- " ö is sorted after z with Swedish locale.
- :language collate sv_SE.UTF8
- :echo sort(['n', 'o', 'O', 'ö', 'p', 'z'], 'l')
-< ['n', 'o', 'O', 'p', 'z', 'ö'] ~
- This does not work properly on Mac.
-
- When {func} is given and it is 'n' then all items will be
- sorted numerical (Implementation detail: this uses the
- strtod() function to parse numbers, Strings, Lists, Dicts and
- Funcrefs will be considered as being 0).
-
- When {func} is given and it is 'N' then all items will be
- sorted numerical. This is like 'n' but a string containing
- digits will be used as the number they represent.
-
- When {func} is given and it is 'f' then all items will be
- sorted numerical. All values must be a Number or a Float.
-
- When {func} is a |Funcref| or a function name, this function
- is called to compare items. The function is invoked with two
- items as argument and must return zero if they are equal, 1 or
- bigger if the first one sorts after the second one, -1 or
- smaller if the first one sorts before the second one.
-
- {dict} is for functions with the "dict" attribute. It will be
- used to set the local variable "self". |Dictionary-function|
-
- The sort is stable, items which compare equal (as number or as
- string) will keep their relative position. E.g., when sorting
- on numbers, text strings will sort next to each other, in the
- same order as they were originally.
-
- Can also be used as a |method|: >
- mylist->sort()
-
-< Also see |uniq()|.
-
- Example: >
- func MyCompare(i1, i2)
- return a:i1 == a:i2 ? 0 : a:i1 > a:i2 ? 1 : -1
- endfunc
- eval mylist->sort("MyCompare")
-< A shorter compare version for this specific simple case, which
- ignores overflow: >
- func MyCompare(i1, i2)
- return a:i1 - a:i2
- endfunc
-< For a simple expression you can use a lambda: >
- eval mylist->sort({i1, i2 -> i1 - i2})
-<
- *soundfold()*
-soundfold({word})
- Return the sound-folded equivalent of {word}. Uses the first
- language in 'spelllang' for the current window that supports
- soundfolding. 'spell' must be set. When no sound folding is
- possible the {word} is returned unmodified.
- This can be used for making spelling suggestions. Note that
- the method can be quite slow.
-
- Can also be used as a |method|: >
- GetWord()->soundfold()
-<
- *spellbadword()*
-spellbadword([{sentence}])
- Without argument: The result is the badly spelled word under
- or after the cursor. The cursor is moved to the start of the
- bad word. When no bad word is found in the cursor line the
- result is an empty string and the cursor doesn't move.
-
- With argument: The result is the first word in {sentence} that
- is badly spelled. If there are no spelling mistakes the
- result is an empty string.
-
- The return value is a list with two items:
- - The badly spelled word or an empty string.
- - The type of the spelling error:
- "bad" spelling mistake
- "rare" rare word
- "local" word only valid in another region
- "caps" word should start with Capital
- Example: >
- echo spellbadword("the quik brown fox")
-< ['quik', 'bad'] ~
-
- The spelling information for the current window and the value
- of 'spelllang' are used.
-
- Can also be used as a |method|: >
- GetText()->spellbadword()
-<
- *spellsuggest()*
-spellsuggest({word} [, {max} [, {capital}]])
- Return a |List| with spelling suggestions to replace {word}.
- When {max} is given up to this number of suggestions are
- returned. Otherwise up to 25 suggestions are returned.
-
- When the {capital} argument is given and it's non-zero only
- suggestions with a leading capital will be given. Use this
- after a match with 'spellcapcheck'.
-
- {word} can be a badly spelled word followed by other text.
- This allows for joining two words that were split. The
- suggestions also include the following text, thus you can
- replace a line.
-
- {word} may also be a good word. Similar words will then be
- returned. {word} itself is not included in the suggestions,
- although it may appear capitalized.
-
- The spelling information for the current window is used. The
- values of 'spelllang' and 'spellsuggest' are used.
-
- Can also be used as a |method|: >
- GetWord()->spellsuggest()
-
-split({string} [, {pattern} [, {keepempty}]]) *split()*
- Make a |List| out of {string}. When {pattern} is omitted or
- empty each white-separated sequence of characters becomes an
- item.
- Otherwise the string is split where {pattern} matches,
- removing the matched characters. 'ignorecase' is not used
- here, add \c to ignore case. |/\c|
- When the first or last item is empty it is omitted, unless the
- {keepempty} argument is given and it's non-zero.
- Other empty items are kept when {pattern} matches at least one
- character or when {keepempty} is non-zero.
- Example: >
- :let words = split(getline('.'), '\W\+')
-< To split a string in individual characters: >
- :for c in split(mystring, '\zs')
-< If you want to keep the separator you can also use '\zs' at
- the end of the pattern: >
- :echo split('abc:def:ghi', ':\zs')
-< ['abc:', 'def:', 'ghi'] ~
- Splitting a table where the first element can be empty: >
- :let items = split(line, ':', 1)
-< The opposite function is |join()|.
-
- Can also be used as a |method|: >
- GetString()->split()
-
-sqrt({expr}) *sqrt()*
- Return the non-negative square root of Float {expr} as a
- |Float|.
- {expr} must evaluate to a |Float| or a |Number|. When {expr}
- is negative the result is NaN (Not a Number).
- Examples: >
- :echo sqrt(100)
-< 10.0 >
- :echo sqrt(-4.01)
-< nan
- "nan" may be different, it depends on system libraries.
-
- Can also be used as a |method|: >
- Compute()->sqrt()
-
-stdioopen({opts}) *stdioopen()*
- With |--headless| this opens stdin and stdout as a |channel|.
- May be called only once. See |channel-stdio|. stderr is not
- handled by this function, see |v:stderr|.
-
- Close the stdio handles with |chanclose()|. Use |chansend()|
- to send data to stdout, and |rpcrequest()| and |rpcnotify()|
- to communicate over RPC.
-
- {opts} is a dictionary with these keys:
- |on_stdin| : callback invoked when stdin is written to.
- stdin_buffered : read stdin in |channel-buffered| mode.
- rpc : If set, |msgpack-rpc| will be used to communicate
- over stdio
- Returns:
- - |channel-id| on success (value is always 1)
- - 0 on invalid arguments
-
-
-stdpath({what}) *stdpath()* *E6100*
- Returns |standard-path| locations of various default files and
- directories.
-
- {what} Type Description ~
- cache String Cache directory. Arbitrary temporary
- storage for plugins, etc.
- config String User configuration directory. The
- |init.vim| is stored here.
- config_dirs List Additional configuration directories.
- data String User data directory. The |shada-file|
- is stored here.
- data_dirs List Additional data directories.
-
- Example: >
- :echo stdpath("config")
-
-
-str2float({string} [, {quoted}]) *str2float()*
- Convert String {string} to a Float. This mostly works the
- same as when using a floating point number in an expression,
- see |floating-point-format|. But it's a bit more permissive.
- E.g., "1e40" is accepted, while in an expression you need to
- write "1.0e40". The hexadecimal form "0x123" is also
- accepted, but not others, like binary or octal.
- When {quoted} is present and non-zero then embedded single
- quotes before the dot are ignored, thus "1'000.0" is a
- thousand.
- Text after the number is silently ignored.
- The decimal point is always '.', no matter what the locale is
- set to. A comma ends the number: "12,345.67" is converted to
- 12.0. You can strip out thousands separators with
- |substitute()|: >
- let f = str2float(substitute(text, ',', '', 'g'))
-<
- Can also be used as a |method|: >
- let f = text->substitute(',', '', 'g')->str2float()
-
-str2list({string} [, {utf8}]) *str2list()*
- Return a list containing the number values which represent
- each character in String {string}. Examples: >
- str2list(" ") returns [32]
- str2list("ABC") returns [65, 66, 67]
-< |list2str()| does the opposite.
-
- UTF-8 encoding is always used, {utf8} option has no effect,
- and exists only for backwards-compatibility.
- With UTF-8 composing characters are handled properly: >
- str2list("á") returns [97, 769]
-
-< Can also be used as a |method|: >
- GetString()->str2list()
-
-str2nr({string} [, {base}]) *str2nr()*
- Convert string {string} to a number.
- {base} is the conversion base, it can be 2, 8, 10 or 16.
- When {quoted} is present and non-zero then embedded single
- quotes are ignored, thus "1'000'000" is a million.
-
- When {base} is omitted base 10 is used. This also means that
- a leading zero doesn't cause octal conversion to be used, as
- with the default String to Number conversion. Example: >
- let nr = str2nr('0123')
-<
- When {base} is 16 a leading "0x" or "0X" is ignored. With a
- different base the result will be zero. Similarly, when
- {base} is 8 a leading "0", "0o" or "0O" is ignored, and when
- {base} is 2 a leading "0b" or "0B" is ignored.
- Text after the number is silently ignored.
-
- Can also be used as a |method|: >
- GetText()->str2nr()
-
-strcharpart({src}, {start} [, {len}]) *strcharpart()*
- Like |strpart()| but using character index and length instead
- of byte index and length. Composing characters are counted
- separately.
- When a character index is used where a character does not
- exist it is assumed to be one character. For example: >
- strcharpart('abc', -1, 2)
-< results in 'a'.
-
- Can also be used as a |method|: >
- GetText()->strcharpart(5)
-
-strchars({string} [, {skipcc}]) *strchars()*
- The result is a Number, which is the number of characters
- in String {string}.
- When {skipcc} is omitted or zero, composing characters are
- counted separately.
- When {skipcc} set to 1, Composing characters are ignored.
- Also see |strlen()|, |strdisplaywidth()| and |strwidth()|.
-
- {skipcc} is only available after 7.4.755. For backward
- compatibility, you can define a wrapper function: >
- if has("patch-7.4.755")
- function s:strchars(str, skipcc)
- return strchars(a:str, a:skipcc)
- endfunction
- else
- function s:strchars(str, skipcc)
- if a:skipcc
- return strlen(substitute(a:str, ".", "x", "g"))
- else
- return strchars(a:str)
- endif
- endfunction
- endif
-<
- Can also be used as a |method|: >
- GetText()->strchars()
-
-strdisplaywidth({string} [, {col}]) *strdisplaywidth()*
- The result is a Number, which is the number of display cells
- String {string} occupies on the screen when it starts at {col}
- (first column is zero). When {col} is omitted zero is used.
- Otherwise it is the screen column where to start. This
- matters for Tab characters.
- The option settings of the current window are used. This
- matters for anything that's displayed differently, such as
- 'tabstop' and 'display'.
- When {string} contains characters with East Asian Width Class
- Ambiguous, this function's return value depends on 'ambiwidth'.
- Also see |strlen()|, |strwidth()| and |strchars()|.
-
- Can also be used as a |method|: >
- GetText()->strdisplaywidth()
-
-strftime({format} [, {time}]) *strftime()*
- The result is a String, which is a formatted date and time, as
- specified by the {format} string. The given {time} is used,
- or the current time if no time is given. The accepted
- {format} depends on your system, thus this is not portable!
- See the manual page of the C function strftime() for the
- format. The maximum length of the result is 80 characters.
- See also |localtime()|, |getftime()| and |strptime()|.
- The language can be changed with the |:language| command.
- Examples: >
- :echo strftime("%c") Sun Apr 27 11:49:23 1997
- :echo strftime("%Y %b %d %X") 1997 Apr 27 11:53:25
- :echo strftime("%y%m%d %T") 970427 11:53:55
- :echo strftime("%H:%M") 11:55
- :echo strftime("%c", getftime("file.c"))
- Show mod time of file.c.
-
-< Can also be used as a |method|: >
- GetFormat()->strftime()
-
-strgetchar({str}, {index}) *strgetchar()*
- Get character {index} from {str}. This uses a character
- index, not a byte index. Composing characters are considered
- separate characters here.
- Also see |strcharpart()| and |strchars()|.
-
- Can also be used as a |method|: >
- GetText()->strgetchar(5)
-
-stridx({haystack}, {needle} [, {start}]) *stridx()*
- The result is a Number, which gives the byte index in
- {haystack} of the first occurrence of the String {needle}.
- If {start} is specified, the search starts at index {start}.
- This can be used to find a second match: >
- :let colon1 = stridx(line, ":")
- :let colon2 = stridx(line, ":", colon1 + 1)
-< The search is done case-sensitive.
- For pattern searches use |match()|.
- -1 is returned if the {needle} does not occur in {haystack}.
- See also |strridx()|.
- Examples: >
- :echo stridx("An Example", "Example") 3
- :echo stridx("Starting point", "Start") 0
- :echo stridx("Starting point", "start") -1
-< *strstr()* *strchr()*
- stridx() works similar to the C function strstr(). When used
- with a single character it works similar to strchr().
-
- Can also be used as a |method|: >
- GetHaystack()->stridx(needle)
-
- *string()*
-string({expr}) Return {expr} converted to a String. If {expr} is a Number,
- Float, String, Blob or a composition of them, then the result
- can be parsed back with |eval()|.
- {expr} type result ~
- String 'string'
- Number 123
- Float 123.123456 or 1.123456e8 or
- `str2float('inf')`
- Funcref `function('name')`
- Blob 0z00112233.44556677.8899
- List [item, item]
- Dictionary {key: value, key: value}
- Note that in String values the ' character is doubled.
- Also see |strtrans()|.
- Note 2: Output format is mostly compatible with YAML, except
- for infinite and NaN floating-point values representations
- which use |str2float()|. Strings are also dumped literally,
- only single quote is escaped, which does not allow using YAML
- for parsing back binary strings. |eval()| should always work for
- strings and floats though and this is the only official
- method, use |msgpackdump()| or |json_encode()| if you need to
- share data with other application.
-
- Can also be used as a |method|: >
- mylist->string()
-
-strlen({string}) *strlen()*
- The result is a Number, which is the length of the String
- {string} in bytes.
- If the argument is a Number it is first converted to a String.
- For other types an error is given.
- If you want to count the number of multibyte characters use
- |strchars()|.
- Also see |len()|, |strdisplaywidth()| and |strwidth()|.
-
- Can also be used as a |method|: >
- GetString()->strlen()
-
-strpart({src}, {start} [, {len} [, {chars}]]) *strpart()*
- The result is a String, which is part of {src}, starting from
- byte {start}, with the byte length {len}.
- When {chars} is present and TRUE then {len} is the number of
- characters positions (composing characters are not counted
- separately, thus "1" means one base character and any
- following composing characters).
- To count {start} as characters instead of bytes use
- |strcharpart()|.
-
- When bytes are selected which do not exist, this doesn't
- result in an error, the bytes are simply omitted.
- If {len} is missing, the copy continues from {start} till the
- end of the {src}. >
- strpart("abcdefg", 3, 2) == "de"
- strpart("abcdefg", -2, 4) == "ab"
- strpart("abcdefg", 5, 4) == "fg"
- strpart("abcdefg", 3) == "defg"
-
-< Note: To get the first character, {start} must be 0. For
- example, to get the character under the cursor: >
- strpart(getline("."), col(".") - 1, 1, v:true)
-<
- Can also be used as a |method|: >
- GetText()->strpart(5)
-
-strptime({format}, {timestring}) *strptime()*
- The result is a Number, which is a unix timestamp representing
- the date and time in {timestring}, which is expected to match
- the format specified in {format}.
-
- The accepted {format} depends on your system, thus this is not
- portable! See the manual page of the C function strptime()
- for the format. Especially avoid "%c". The value of $TZ also
- matters.
-
- If the {timestring} cannot be parsed with {format} zero is
- returned. If you do not know the format of {timestring} you
- can try different {format} values until you get a non-zero
- result.
-
- See also |strftime()|.
- Examples: >
- :echo strptime("%Y %b %d %X", "1997 Apr 27 11:49:23")
-< 862156163 >
- :echo strftime("%c", strptime("%y%m%d %T", "970427 11:53:55"))
-< Sun Apr 27 11:53:55 1997 >
- :echo strftime("%c", strptime("%Y%m%d%H%M%S", "19970427115355") + 3600)
-< Sun Apr 27 12:53:55 1997
-
- Can also be used as a |method|: >
- GetFormat()->strptime(timestring)
-<
-strridx({haystack}, {needle} [, {start}]) *strridx()*
- The result is a Number, which gives the byte index in
- {haystack} of the last occurrence of the String {needle}.
- When {start} is specified, matches beyond this index are
- ignored. This can be used to find a match before a previous
- match: >
- :let lastcomma = strridx(line, ",")
- :let comma2 = strridx(line, ",", lastcomma - 1)
-< The search is done case-sensitive.
- For pattern searches use |match()|.
- -1 is returned if the {needle} does not occur in {haystack}.
- If the {needle} is empty the length of {haystack} is returned.
- See also |stridx()|. Examples: >
- :echo strridx("an angry armadillo", "an") 3
-< *strrchr()*
- When used with a single character it works similar to the C
- function strrchr().
-
- Can also be used as a |method|: >
- GetHaystack()->strridx(needle)
-
-strtrans({string}) *strtrans()*
- The result is a String, which is {string} with all unprintable
- characters translated into printable characters |'isprint'|.
- Like they are shown in a window. Example: >
- echo strtrans(@a)
-< This displays a newline in register a as "^@" instead of
- starting a new line.
-
- Can also be used as a |method|: >
- GetString()->strtrans()
-
-strwidth({string}) *strwidth()*
- The result is a Number, which is the number of display cells
- String {string} occupies. A Tab character is counted as one
- cell, alternatively use |strdisplaywidth()|.
- When {string} contains characters with East Asian Width Class
- Ambiguous, this function's return value depends on 'ambiwidth'.
- Also see |strlen()|, |strdisplaywidth()| and |strchars()|.
-
- Can also be used as a |method|: >
- GetString()->strwidth()
-
-submatch({nr} [, {list}]) *submatch()* *E935*
- Only for an expression in a |:substitute| command or
- substitute() function.
- Returns the {nr}'th submatch of the matched text. When {nr}
- is 0 the whole matched text is returned.
- Note that a NL in the string can stand for a line break of a
- multi-line match or a NUL character in the text.
- Also see |sub-replace-expression|.
-
- If {list} is present and non-zero then submatch() returns
- a list of strings, similar to |getline()| with two arguments.
- NL characters in the text represent NUL characters in the
- text.
- Only returns more than one item for |:substitute|, inside
- |substitute()| this list will always contain one or zero
- items, since there are no real line breaks.
-
- When substitute() is used recursively only the submatches in
- the current (deepest) call can be obtained.
-
- Examples: >
- :s/\d\+/\=submatch(0) + 1/
- :echo substitute(text, '\d\+', '\=submatch(0) + 1', '')
-< This finds the first number in the line and adds one to it.
- A line break is included as a newline character.
-
- Can also be used as a |method|: >
- GetNr()->submatch()
-
-substitute({string}, {pat}, {sub}, {flags}) *substitute()*
- The result is a String, which is a copy of {string}, in which
- the first match of {pat} is replaced with {sub}.
- When {flags} is "g", all matches of {pat} in {string} are
- replaced. Otherwise {flags} should be "".
-
- This works like the ":substitute" command (without any flags).
- But the matching with {pat} is always done like the 'magic'
- option is set and 'cpoptions' is empty (to make scripts
- portable). 'ignorecase' is still relevant, use |/\c| or |/\C|
- if you want to ignore or match case and ignore 'ignorecase'.
- 'smartcase' is not used. See |string-match| for how {pat} is
- used.
-
- A "~" in {sub} is not replaced with the previous {sub}.
- Note that some codes in {sub} have a special meaning
- |sub-replace-special|. For example, to replace something with
- "\n" (two characters), use "\\\\n" or '\\n'.
-
- When {pat} does not match in {string}, {string} is returned
- unmodified.
-
- Example: >
- :let &path = substitute(&path, ",\\=[^,]*$", "", "")
-< This removes the last component of the 'path' option. >
- :echo substitute("testing", ".*", "\\U\\0", "")
-< results in "TESTING".
-
- When {sub} starts with "\=", the remainder is interpreted as
- an expression. See |sub-replace-expression|. Example: >
- :echo substitute(s, '%\(\x\x\)',
- \ '\=nr2char("0x" . submatch(1))', 'g')
-
-< When {sub} is a Funcref that function is called, with one
- optional argument. Example: >
- :echo substitute(s, '%\(\x\x\)', SubNr, 'g')
-< The optional argument is a list which contains the whole
- matched string and up to nine submatches, like what
- |submatch()| returns. Example: >
- :echo substitute(s, '%\(\x\x\)', {m -> '0x' . m[1]}, 'g')
-
-< Can also be used as a |method|: >
- GetString()->substitute(pat, sub, flags)
-
-swapinfo({fname}) *swapinfo()*
- The result is a dictionary, which holds information about the
- swapfile {fname}. The available fields are:
- version VIM version
- user user name
- host host name
- fname original file name
- pid PID of the VIM process that created the swap
- file
- mtime last modification time in seconds
- inode Optional: INODE number of the file
- dirty 1 if file was modified, 0 if not
- In case of failure an "error" item is added with the reason:
- Cannot open file: file not found or in accessible
- Cannot read file: cannot read first block
- Not a swap file: does not contain correct block ID
- Magic number mismatch: Info in first block is invalid
-
- Can also be used as a |method|: >
- GetFilename()->swapinfo()
-
-swapname({buf}) *swapname()*
- The result is the swap file path of the buffer {buf}.
- For the use of {buf}, see |bufname()| above.
- If buffer {buf} is the current buffer, the result is equal to
- |:swapname| (unless there is no swap file).
- If buffer {buf} has no swap file, returns an empty string.
-
- Can also be used as a |method|: >
- GetBufname()->swapname()
-
-synID({lnum}, {col}, {trans}) *synID()*
- The result is a Number, which is the syntax ID at the position
- {lnum} and {col} in the current window.
- The syntax ID can be used with |synIDattr()| and
- |synIDtrans()| to obtain syntax information about text.
-
- {col} is 1 for the leftmost column, {lnum} is 1 for the first
- line. 'synmaxcol' applies, in a longer line zero is returned.
- Note that when the position is after the last character,
- that's where the cursor can be in Insert mode, synID() returns
- zero. {lnum} is used like with |getline()|.
-
- When {trans} is |TRUE|, transparent items are reduced to the
- item that they reveal. This is useful when wanting to know
- the effective color. When {trans} is |FALSE|, the transparent
- item is returned. This is useful when wanting to know which
- syntax item is effective (e.g. inside parens).
- Warning: This function can be very slow. Best speed is
- obtained by going through the file in forward direction.
-
- Example (echoes the name of the syntax item under the cursor): >
- :echo synIDattr(synID(line("."), col("."), 1), "name")
-<
-
-synIDattr({synID}, {what} [, {mode}]) *synIDattr()*
- The result is a String, which is the {what} attribute of
- syntax ID {synID}. This can be used to obtain information
- about a syntax item.
- {mode} can be "gui", "cterm" or "term", to get the attributes
- for that mode. When {mode} is omitted, or an invalid value is
- used, the attributes for the currently active highlighting are
- used (GUI, cterm or term).
- Use synIDtrans() to follow linked highlight groups.
- {what} result
- "name" the name of the syntax item
- "fg" foreground color (GUI: color name used to set
- the color, cterm: color number as a string,
- term: empty string)
- "bg" background color (as with "fg")
- "font" font name (only available in the GUI)
- |highlight-font|
- "sp" special color (as with "fg") |highlight-guisp|
- "fg#" like "fg", but for the GUI and the GUI is
- running the name in "#RRGGBB" form
- "bg#" like "fg#" for "bg"
- "sp#" like "fg#" for "sp"
- "bold" "1" if bold
- "italic" "1" if italic
- "reverse" "1" if reverse
- "inverse" "1" if inverse (= reverse)
- "standout" "1" if standout
- "underline" "1" if underlined
- "undercurl" "1" if undercurled
- "strikethrough" "1" if struckthrough
-
- Example (echoes the color of the syntax item under the
- cursor): >
- :echo synIDattr(synIDtrans(synID(line("."), col("."), 1)), "fg")
-<
- Can also be used as a |method|: >
- :echo synID(line("."), col("."), 1)->synIDtrans()->synIDattr("fg")
-
-synIDtrans({synID}) *synIDtrans()*
- The result is a Number, which is the translated syntax ID of
- {synID}. This is the syntax group ID of what is being used to
- highlight the character. Highlight links given with
- ":highlight link" are followed.
-
- Can also be used as a |method|: >
- :echo synID(line("."), col("."), 1)->synIDtrans()->synIDattr("fg")
-
-synconcealed({lnum}, {col}) *synconcealed()*
- The result is a |List| with currently three items:
- 1. The first item in the list is 0 if the character at the
- position {lnum} and {col} is not part of a concealable
- region, 1 if it is. {lnum} is used like with |getline()|.
- 2. The second item in the list is a string. If the first item
- is 1, the second item contains the text which will be
- displayed in place of the concealed text, depending on the
- current setting of 'conceallevel' and 'listchars'.
- 3. The third and final item in the list is a number
- representing the specific syntax region matched in the
- line. When the character is not concealed the value is
- zero. This allows detection of the beginning of a new
- concealable region if there are two consecutive regions
- with the same replacement character. For an example, if
- the text is "123456" and both "23" and "45" are concealed
- and replaced by the character "X", then:
- call returns ~
- synconcealed(lnum, 1) [0, '', 0]
- synconcealed(lnum, 2) [1, 'X', 1]
- synconcealed(lnum, 3) [1, 'X', 1]
- synconcealed(lnum, 4) [1, 'X', 2]
- synconcealed(lnum, 5) [1, 'X', 2]
- synconcealed(lnum, 6) [0, '', 0]
-
-
-synstack({lnum}, {col}) *synstack()*
- Return a |List|, which is the stack of syntax items at the
- position {lnum} and {col} in the current window. {lnum} is
- used like with |getline()|. Each item in the List is an ID
- like what |synID()| returns.
- The first item in the List is the outer region, following are
- items contained in that one. The last one is what |synID()|
- returns, unless not the whole item is highlighted or it is a
- transparent item.
- This function is useful for debugging a syntax file.
- Example that shows the syntax stack under the cursor: >
- for id in synstack(line("."), col("."))
- echo synIDattr(id, "name")
- endfor
-< When the position specified with {lnum} and {col} is invalid
- nothing is returned. The position just after the last
- character in a line and the first column in an empty line are
- valid positions.
-
-system({cmd} [, {input}]) *system()* *E677*
- Gets the output of {cmd} as a |string| (|systemlist()| returns
- a |List|) and sets |v:shell_error| to the error code.
- {cmd} is treated as in |jobstart()|:
- If {cmd} is a List it runs directly (no 'shell').
- If {cmd} is a String it runs in the 'shell', like this: >
- :call jobstart(split(&shell) + split(&shellcmdflag) + ['{cmd}'])
-
-< Not to be used for interactive commands.
-
- Result is a String, filtered to avoid platform-specific quirks:
- - <CR><NL> is replaced with <NL>
- - NUL characters are replaced with SOH (0x01)
-
- Example: >
- :echo system(['ls', expand('%:h')])
-
-< If {input} is a string it is written to a pipe and passed as
- stdin to the command. The string is written as-is, line
- separators are not changed.
- If {input} is a |List| it is written to the pipe as
- |writefile()| does with {binary} set to "b" (i.e. with
- a newline between each list item, and newlines inside list
- items converted to NULs).
- When {input} is given and is a valid buffer id, the content of
- the buffer is written to the file line by line, each line
- terminated by NL (and NUL where the text has NL).
- *E5677*
- Note: system() cannot write to or read from backgrounded ("&")
- shell commands, e.g.: >
- :echo system("cat - &", "foo")
-< which is equivalent to: >
- $ echo foo | bash -c 'cat - &'
-< The pipes are disconnected (unless overridden by shell
- redirection syntax) before input can reach it. Use
- |jobstart()| instead.
-
- Note: Use |shellescape()| or |::S| with |expand()| or
- |fnamemodify()| to escape special characters in a command
- argument. 'shellquote' and 'shellxquote' must be properly
- configured. Example: >
- :echo system('ls '..shellescape(expand('%:h')))
- :echo system('ls '..expand('%:h:S'))
-
-< Unlike ":!cmd" there is no automatic check for changed files.
- Use |:checktime| to force a check.
-
- Can also be used as a |method|: >
- :echo GetCmd()->system()
-
-systemlist({cmd} [, {input} [, {keepempty}]]) *systemlist()*
- Same as |system()|, but returns a |List| with lines (parts of
- output separated by NL) with NULs transformed into NLs. Output
- is the same as |readfile()| will output with {binary} argument
- set to "b", except that a final newline is not preserved,
- unless {keepempty} is non-zero.
- Note that on MS-Windows you may get trailing CR characters.
-
- To see the difference between "echo hello" and "echo -n hello"
- use |system()| and |split()|: >
- echo split(system('echo hello'), '\n', 1)
-<
- Returns an empty string on error.
-
- Can also be used as a |method|: >
- :echo GetCmd()->systemlist()
-
-tabpagebuflist([{arg}]) *tabpagebuflist()*
- The result is a |List|, where each item is the number of the
- buffer associated with each window in the current tab page.
- {arg} specifies the number of the tab page to be used. When
- omitted the current tab page is used.
- When {arg} is invalid the number zero is returned.
- To get a list of all buffers in all tabs use this: >
- let buflist = []
- for i in range(tabpagenr('$'))
- call extend(buflist, tabpagebuflist(i + 1))
- endfor
-< Note that a buffer may appear in more than one window.
-
- Can also be used as a |method|: >
- GetTabpage()->tabpagebuflist()
-
-tabpagenr([{arg}]) *tabpagenr()*
- The result is a Number, which is the number of the current
- tab page. The first tab page has number 1.
- The optional argument {arg} supports the following values:
- $ the number of the last tab page (the tab page
- count).
- # the number of the last accessed tab page (where
- |g<Tab>| goes to). If there is no previous
- tab page, 0 is returned.
- The number can be used with the |:tab| command.
-
-
-tabpagewinnr({tabarg} [, {arg}]) *tabpagewinnr()*
- Like |winnr()| but for tab page {tabarg}.
- {tabarg} specifies the number of tab page to be used.
- {arg} is used like with |winnr()|:
- - When omitted the current window number is returned. This is
- the window which will be used when going to this tab page.
- - When "$" the number of windows is returned.
- - When "#" the previous window nr is returned.
- Useful examples: >
- tabpagewinnr(1) " current window of tab page 1
- tabpagewinnr(4, '$') " number of windows in tab page 4
-< When {tabarg} is invalid zero is returned.
-
- Can also be used as a |method|: >
- GetTabpage()->tabpagewinnr()
-<
- *tagfiles()*
-tagfiles() Returns a |List| with the file names used to search for tags
- for the current buffer. This is the 'tags' option expanded.
-
-
-taglist({expr} [, {filename}]) *taglist()*
- Returns a |List| of tags matching the regular expression {expr}.
-
- If {filename} is passed it is used to prioritize the results
- in the same way that |:tselect| does. See |tag-priority|.
- {filename} should be the full path of the file.
-
- Each list item is a dictionary with at least the following
- entries:
- name Name of the tag.
- filename Name of the file where the tag is
- defined. It is either relative to the
- current directory or a full path.
- cmd Ex command used to locate the tag in
- the file.
- kind Type of the tag. The value for this
- entry depends on the language specific
- kind values. Only available when
- using a tags file generated by
- Exuberant ctags or hdrtag.
- static A file specific tag. Refer to
- |static-tag| for more information.
- More entries may be present, depending on the content of the
- tags file: access, implementation, inherits and signature.
- Refer to the ctags documentation for information about these
- fields. For C code the fields "struct", "class" and "enum"
- may appear, they give the name of the entity the tag is
- contained in.
-
- The ex-command "cmd" can be either an ex search pattern, a
- line number or a line number followed by a byte number.
-
- If there are no matching tags, then an empty list is returned.
-
- To get an exact tag match, the anchors '^' and '$' should be
- used in {expr}. This also make the function work faster.
- Refer to |tag-regexp| for more information about the tag
- search regular expression pattern.
-
- Refer to |'tags'| for information about how the tags file is
- located by Vim. Refer to |tags-file-format| for the format of
- the tags file generated by the different ctags tools.
-
- Can also be used as a |method|: >
- GetTagpattern()->taglist()
-
-tempname() *tempname()* *temp-file-name*
- The result is a String, which is the name of a file that
- doesn't exist. It can be used for a temporary file. Example: >
- :let tmpfile = tempname()
- :exe "redir > " . tmpfile
-< For Unix, the file will be in a private directory |tempfile|.
- For MS-Windows forward slashes are used when the 'shellslash'
- option is set or when 'shellcmdflag' starts with '-'.
-
-termopen({cmd}[, {opts}]) *termopen()*
- Spawns {cmd} in a new pseudo-terminal session connected
- to the current buffer. {cmd} is the same as the one passed to
- |jobstart()|. This function fails if the current buffer is
- modified (all buffer contents are destroyed).
-
- The {opts} dict is similar to the one passed to |jobstart()|,
- but the `pty`, `width`, `height`, and `TERM` fields are
- ignored: `height`/`width` are taken from the current window
- and `$TERM` is set to "xterm-256color".
- Returns the same values as |jobstart()|.
-
- See |terminal| for more information.
-
-test_ functions are documented here: |test-functions-details|
-
-tan({expr}) *tan()*
- Return the tangent of {expr}, measured in radians, as a |Float|
- in the range [-inf, inf].
- {expr} must evaluate to a |Float| or a |Number|.
- Examples: >
- :echo tan(10)
-< 0.648361 >
- :echo tan(-4.01)
-< -1.181502
-
- Can also be used as a |method|: >
- Compute()->tan()
-
-tanh({expr}) *tanh()*
- Return the hyperbolic tangent of {expr} as a |Float| in the
- range [-1, 1].
- {expr} must evaluate to a |Float| or a |Number|.
- Examples: >
- :echo tanh(0.5)
-< 0.462117 >
- :echo tanh(-1)
-< -0.761594
-
- Can also be used as a |method|: >
- Compute()->tanh()
-<
- *timer_info()*
-timer_info([{id}])
- Return a list with information about timers.
- When {id} is given only information about this timer is
- returned. When timer {id} does not exist an empty list is
- returned.
- When {id} is omitted information about all timers is returned.
-
- For each timer the information is stored in a |Dictionary| with
- these items:
- "id" the timer ID
- "time" time the timer was started with
- "repeat" number of times the timer will still fire;
- -1 means forever
- "callback" the callback
-
- Can also be used as a |method|: >
- GetTimer()->timer_info()
-<
-timer_pause({timer}, {paused}) *timer_pause()*
- Pause or unpause a timer. A paused timer does not invoke its
- callback when its time expires. Unpausing a timer may cause
- the callback to be invoked almost immediately if enough time
- has passed.
-
- Pausing a timer is useful to avoid the callback to be called
- for a short time.
-
- If {paused} evaluates to a non-zero Number or a non-empty
- String, then the timer is paused, otherwise it is unpaused.
- See |non-zero-arg|.
-
- Can also be used as a |method|: >
- GetTimer()->timer_pause(1)
-<
- *timer_start()* *timer* *timers*
-timer_start({time}, {callback} [, {options}])
- Create a timer and return the timer ID.
-
- {time} is the waiting time in milliseconds. This is the
- minimum time before invoking the callback. When the system is
- busy or Vim is not waiting for input the time will be longer.
-
- {callback} is the function to call. It can be the name of a
- function or a |Funcref|. It is called with one argument, which
- is the timer ID. The callback is only invoked when Vim is
- waiting for input.
-
- {options} is a dictionary. Supported entries:
- "repeat" Number of times to repeat the callback.
- -1 means forever. Default is 1.
- If the timer causes an error three times in a
- row the repeat is cancelled.
-
- Example: >
- func MyHandler(timer)
- echo 'Handler called'
- endfunc
- let timer = timer_start(500, 'MyHandler',
- \ {'repeat': 3})
-< This invokes MyHandler() three times at 500 msec intervals.
-
- Can also be used as a |method|: >
- GetMsec()->timer_start(callback)
-
-< Not available in the |sandbox|.
-
-timer_stop({timer}) *timer_stop()*
- Stop a timer. The timer callback will no longer be invoked.
- {timer} is an ID returned by timer_start(), thus it must be a
- Number. If {timer} does not exist there is no error.
-
- Can also be used as a |method|: >
- GetTimer()->timer_stop()
-<
-timer_stopall() *timer_stopall()*
- Stop all timers. The timer callbacks will no longer be
- invoked. Useful if some timers is misbehaving. If there are
- no timers there is no error.
-
-tolower({expr}) *tolower()*
- The result is a copy of the String given, with all uppercase
- characters turned into lowercase (just like applying |gu| to
- the string).
-
- Can also be used as a |method|: >
- GetText()->tolower()
-
-toupper({expr}) *toupper()*
- The result is a copy of the String given, with all lowercase
- characters turned into uppercase (just like applying |gU| to
- the string).
-
- Can also be used as a |method|: >
- GetText()->toupper()
-
-tr({src}, {fromstr}, {tostr}) *tr()*
- The result is a copy of the {src} string with all characters
- which appear in {fromstr} replaced by the character in that
- position in the {tostr} string. Thus the first character in
- {fromstr} is translated into the first character in {tostr}
- and so on. Exactly like the unix "tr" command.
- This code also deals with multibyte characters properly.
-
- Examples: >
- echo tr("hello there", "ht", "HT")
-< returns "Hello THere" >
- echo tr("<blob>", "<>", "{}")
-< returns "{blob}"
-
- Can also be used as a |method|: >
- GetText()->tr(from, to)
-
-trim({text} [, {mask} [, {dir}]]) *trim()*
- Return {text} as a String where any character in {mask} is
- removed from the beginning and/or end of {text}.
- If {mask} is not given, {mask} is all characters up to 0x20,
- which includes Tab, space, NL and CR, plus the non-breaking
- space character 0xa0.
- The optional {dir} argument specifies where to remove the
- characters:
- 0 remove from the beginning and end of {text}
- 1 remove only at the beginning of {text}
- 2 remove only at the end of {text}
- When omitted both ends are trimmed.
- This function deals with multibyte characters properly.
- Examples: >
- echo trim(" some text ")
-< returns "some text" >
- echo trim(" \r\t\t\r RESERVE \t\n\x0B\xA0") . "_TAIL"
-< returns "RESERVE_TAIL" >
- echo trim("rm<Xrm<>X>rrm", "rm<>")
-< returns "Xrm<>X" (characters in the middle are not removed) >
- echo trim(" vim ", " ", 2)
-< returns " vim"
-
- Can also be used as a |method|: >
- GetText()->trim()
-
-trunc({expr}) *trunc()*
- Return the largest integral value with magnitude less than or
- equal to {expr} as a |Float| (truncate towards zero).
- {expr} must evaluate to a |Float| or a |Number|.
- Examples: >
- echo trunc(1.456)
-< 1.0 >
- echo trunc(-5.456)
-< -5.0 >
- echo trunc(4.0)
-< 4.0
-
- Can also be used as a |method|: >
- Compute()->trunc()
-
-type({expr}) *type()*
- The result is a Number representing the type of {expr}.
- Instead of using the number directly, it is better to use the
- v:t_ variable that has the value:
- Number: 0 (|v:t_number|)
- String: 1 (|v:t_string|)
- Funcref: 2 (|v:t_func|)
- List: 3 (|v:t_list|)
- Dictionary: 4 (|v:t_dict|)
- Float: 5 (|v:t_float|)
- Boolean: 6 (|v:true| and |v:false|)
- Null: 7 (|v:null|)
- Blob: 10 (|v:t_blob|)
- For backward compatibility, this method can be used: >
- :if type(myvar) == type(0)
- :if type(myvar) == type("")
- :if type(myvar) == type(function("tr"))
- :if type(myvar) == type([])
- :if type(myvar) == type({})
- :if type(myvar) == type(0.0)
- :if type(myvar) == type(v:true)
-< In place of checking for |v:null| type it is better to check
- for |v:null| directly as it is the only value of this type: >
- :if myvar is v:null
-< To check if the v:t_ variables exist use this: >
- :if exists('v:t_number')
-
-< Can also be used as a |method|: >
- mylist->type()
-
-undofile({name}) *undofile()*
- Return the name of the undo file that would be used for a file
- with name {name} when writing. This uses the 'undodir'
- option, finding directories that exist. It does not check if
- the undo file exists.
- {name} is always expanded to the full path, since that is what
- is used internally.
- If {name} is empty undofile() returns an empty string, since a
- buffer without a file name will not write an undo file.
- Useful in combination with |:wundo| and |:rundo|.
-
- Can also be used as a |method|: >
- GetFilename()->undofile()
-
-undotree() *undotree()*
- Return the current state of the undo tree in a dictionary with
- the following items:
- "seq_last" The highest undo sequence number used.
- "seq_cur" The sequence number of the current position in
- the undo tree. This differs from "seq_last"
- when some changes were undone.
- "time_cur" Time last used for |:earlier| and related
- commands. Use |strftime()| to convert to
- something readable.
- "save_last" Number of the last file write. Zero when no
- write yet.
- "save_cur" Number of the current position in the undo
- tree.
- "synced" Non-zero when the last undo block was synced.
- This happens when waiting from input from the
- user. See |undo-blocks|.
- "entries" A list of dictionaries with information about
- undo blocks.
-
- The first item in the "entries" list is the oldest undo item.
- Each List item is a |Dictionary| with these items:
- "seq" Undo sequence number. Same as what appears in
- |:undolist|.
- "time" Timestamp when the change happened. Use
- |strftime()| to convert to something readable.
- "newhead" Only appears in the item that is the last one
- that was added. This marks the last change
- and where further changes will be added.
- "curhead" Only appears in the item that is the last one
- that was undone. This marks the current
- position in the undo tree, the block that will
- be used by a redo command. When nothing was
- undone after the last change this item will
- not appear anywhere.
- "save" Only appears on the last block before a file
- write. The number is the write count. The
- first write has number 1, the last one the
- "save_last" mentioned above.
- "alt" Alternate entry. This is again a List of undo
- blocks. Each item may again have an "alt"
- item.
-
-uniq({list} [, {func} [, {dict}]]) *uniq()* *E882*
- Remove second and succeeding copies of repeated adjacent
- {list} items in-place. Returns {list}. If you want a list
- to remain unmodified make a copy first: >
- :let newlist = uniq(copy(mylist))
-< The default compare function uses the string representation of
- each item. For the use of {func} and {dict} see |sort()|.
-
- Can also be used as a |method|: >
- mylist->uniq()
-
-values({dict}) *values()*
- Return a |List| with all the values of {dict}. The |List| is
- in arbitrary order. Also see |items()| and |keys()|.
-
- Can also be used as a |method|: >
- mydict->values()
-
-virtcol({expr}) *virtcol()*
- The result is a Number, which is the screen column of the file
- position given with {expr}. That is, the last screen position
- occupied by the character at that position, when the screen
- would be of unlimited width. When there is a <Tab> at the
- position, the returned Number will be the column at the end of
- the <Tab>. For example, for a <Tab> in column 1, with 'ts'
- set to 8, it returns 8. |conceal| is ignored.
- For the byte position use |col()|.
- For the use of {expr} see |col()|.
- When 'virtualedit' is used {expr} can be [lnum, col, off], where
- "off" is the offset in screen columns from the start of the
- character. E.g., a position within a <Tab> or after the last
- character. When "off" is omitted zero is used.
- When Virtual editing is active in the current mode, a position
- beyond the end of the line can be returned. |'virtualedit'|
- The accepted positions are:
- . the cursor position
- $ the end of the cursor line (the result is the
- number of displayed characters in the cursor line
- plus one)
- 'x position of mark x (if the mark is not set, 0 is
- returned)
- v In Visual mode: the start of the Visual area (the
- cursor is the end). When not in Visual mode
- returns the cursor position. Differs from |'<| in
- that it's updated right away.
- Note that only marks in the current file can be used.
- Examples: >
- virtcol(".") with text "foo^Lbar", with cursor on the "^L", returns 5
- virtcol("$") with text "foo^Lbar", returns 9
- virtcol("'t") with text " there", with 't at 'h', returns 6
-< The first column is 1. 0 is returned for an error.
- A more advanced example that echoes the maximum length of
- all lines: >
- echo max(map(range(1, line('$')), "virtcol([v:val, '$'])"))
-
-< Can also be used as a |method|: >
- GetPos()->virtcol()
-
-visualmode([{expr}]) *visualmode()*
- The result is a String, which describes the last Visual mode
- used in the current buffer. Initially it returns an empty
- string, but once Visual mode has been used, it returns "v",
- "V", or "<CTRL-V>" (a single CTRL-V character) for
- character-wise, line-wise, or block-wise Visual mode
- respectively.
- Example: >
- :exe "normal " . visualmode()
-< This enters the same Visual mode as before. It is also useful
- in scripts if you wish to act differently depending on the
- Visual mode that was used.
- If Visual mode is active, use |mode()| to get the Visual mode
- (e.g., in a |:vmap|).
- If {expr} is supplied and it evaluates to a non-zero Number or
- a non-empty String, then the Visual mode will be cleared and
- the old value is returned. See |non-zero-arg|.
-
-wait({timeout}, {condition}[, {interval}]) *wait()*
- Waits until {condition} evaluates to |TRUE|, where {condition}
- is a |Funcref| or |string| containing an expression.
-
- {timeout} is the maximum waiting time in milliseconds, -1
- means forever.
-
- Condition is evaluated on user events, internal events, and
- every {interval} milliseconds (default: 200).
-
- Returns a status integer:
- 0 if the condition was satisfied before timeout
- -1 if the timeout was exceeded
- -2 if the function was interrupted (by |CTRL-C|)
- -3 if an error occurred
-
-wildmenumode() *wildmenumode()*
- Returns |TRUE| when the wildmenu is active and |FALSE|
- otherwise. See 'wildmenu' and 'wildmode'.
- This can be used in mappings to handle the 'wildcharm' option
- gracefully. (Makes only sense with |mapmode-c| mappings).
-
- For example to make <c-j> work like <down> in wildmode, use: >
- :cnoremap <expr> <C-j> wildmenumode() ? "\<Down>\<Tab>" : "\<c-j>"
-<
- (Note, this needs the 'wildcharm' option set appropriately).
-
-win_execute({id}, {command} [, {silent}]) *win_execute()*
- Like `execute()` but in the context of window {id}.
- The window will temporarily be made the current window,
- without triggering autocommands or changing directory. When
- executing {command} autocommands will be triggered, this may
- have unexpected side effects. Use |:noautocmd| if needed.
- Example: >
- call win_execute(winid, 'syntax enable')
-<
- Can also be used as a |method|, the base is passed as the
- second argument: >
- GetCommand()->win_execute(winid)
-
-win_findbuf({bufnr}) *win_findbuf()*
- Returns a |List| with |window-ID|s for windows that contain
- buffer {bufnr}. When there is none the list is empty.
-
- Can also be used as a |method|: >
- GetBufnr()->win_findbuf()
-
-win_getid([{win} [, {tab}]]) *win_getid()*
- Get the |window-ID| for the specified window.
- When {win} is missing use the current window.
- With {win} this is the window number. The top window has
- number 1.
- Without {tab} use the current tab, otherwise the tab with
- number {tab}. The first tab has number one.
- Return zero if the window cannot be found.
-
- Can also be used as a |method|: >
- GetWinnr()->win_getid()
-
-win_gettype([{nr}]) *win_gettype()*
- Return the type of the window:
- "autocmd" autocommand window. Temporary window
- used to execute autocommands.
- "command" command-line window |cmdwin|
- (empty) normal window
- "loclist" |location-list-window|
- "popup" popup window |popup|
- "preview" preview window |preview-window|
- "quickfix" |quickfix-window|
- "unknown" window {nr} not found
-
- When {nr} is omitted return the type of the current window.
- When {nr} is given return the type of this window by number or
- |window-ID|.
-
- Also see the 'buftype' option. When running a terminal in a
- popup window then 'buftype' is "terminal" and win_gettype()
- returns "popup".
-
- Can also be used as a |method|: >
- GetWinid()->win_gettype()
-<
-win_gotoid({expr}) *win_gotoid()*
- Go to window with ID {expr}. This may also change the current
- tabpage.
- Return TRUE if successful, FALSE if the window cannot be found.
-
- Can also be used as a |method|: >
- GetWinid()->win_gotoid()
-
-win_id2tabwin({expr}) *win_id2tabwin()*
- Return a list with the tab number and window number of window
- with ID {expr}: [tabnr, winnr].
- Return [0, 0] if the window cannot be found.
-
- Can also be used as a |method|: >
- GetWinid()->win_id2tabwin()
-
-win_id2win({expr}) *win_id2win()*
- Return the window number of window with ID {expr}.
- Return 0 if the window cannot be found in the current tabpage.
-
- Can also be used as a |method|: >
- GetWinid()->win_id2win()
-
-win_screenpos({nr}) *win_screenpos()*
- Return the screen position of window {nr} as a list with two
- numbers: [row, col]. The first window always has position
- [1, 1], unless there is a tabline, then it is [2, 1].
- {nr} can be the window number or the |window-ID|. Use zero
- for the current window.
- Returns [0, 0] if the window cannot be found in the current
- tabpage.
-
- Can also be used as a |method|: >
- GetWinid()->win_screenpos()
-<
-win_splitmove({nr}, {target} [, {options}]) *win_splitmove()*
- Move the window {nr} to a new split of the window {target}.
- This is similar to moving to {target}, creating a new window
- using |:split| but having the same contents as window {nr}, and
- then closing {nr}.
-
- Both {nr} and {target} can be window numbers or |window-ID|s.
- Both must be in the current tab page.
-
- Returns zero for success, non-zero for failure.
-
- {options} is a |Dictionary| with the following optional entries:
- "vertical" When TRUE, the split is created vertically,
- like with |:vsplit|.
- "rightbelow" When TRUE, the split is made below or to the
- right (if vertical). When FALSE, it is done
- above or to the left (if vertical). When not
- present, the values of 'splitbelow' and
- 'splitright' are used.
-
- Can also be used as a |method|: >
- GetWinid()->win_splitmove(target)
-<
- *winbufnr()*
-winbufnr({nr}) The result is a Number, which is the number of the buffer
- associated with window {nr}. {nr} can be the window number or
- the |window-ID|.
- When {nr} is zero, the number of the buffer in the current
- window is returned.
- When window {nr} doesn't exist, -1 is returned.
- Example: >
- :echo "The file in the current window is " . bufname(winbufnr(0))
-<
- Can also be used as a |method|: >
- FindWindow()->winbufnr()->bufname()
-<
- *wincol()*
-wincol() The result is a Number, which is the virtual column of the
- cursor in the window. This is counting screen cells from the
- left side of the window. The leftmost column is one.
-
- *windowsversion()*
-windowsversion()
- The result is a String. For MS-Windows it indicates the OS
- version. E.g, Windows 10 is "10.0", Windows 8 is "6.2",
- Windows XP is "5.1". For non-MS-Windows systems the result is
- an empty string.
-
-winheight({nr}) *winheight()*
- The result is a Number, which is the height of window {nr}.
- {nr} can be the window number or the |window-ID|.
- When {nr} is zero, the height of the current window is
- returned. When window {nr} doesn't exist, -1 is returned.
- An existing window always has a height of zero or more.
- This excludes any window toolbar line.
- Examples: >
- :echo "The current window has " . winheight(0) . " lines."
-
-< Can also be used as a |method|: >
- GetWinid()->winheight()
-<
-winlayout([{tabnr}]) *winlayout()*
- The result is a nested List containing the layout of windows
- in a tabpage.
-
- Without {tabnr} use the current tabpage, otherwise the tabpage
- with number {tabnr}. If the tabpage {tabnr} is not found,
- returns an empty list.
-
- For a leaf window, it returns:
- ['leaf', {winid}]
- For horizontally split windows, which form a column, it
- returns:
- ['col', [{nested list of windows}]]
- For vertically split windows, which form a row, it returns:
- ['row', [{nested list of windows}]]
-
- Example: >
- " Only one window in the tab page
- :echo winlayout()
- ['leaf', 1000]
- " Two horizontally split windows
- :echo winlayout()
- ['col', [['leaf', 1000], ['leaf', 1001]]]
- " The second tab page, with three horizontally split
- " windows, with two vertically split windows in the
- " middle window
- :echo winlayout(2)
- ['col', [['leaf', 1002], ['row', [['leaf', 1003],
- ['leaf', 1001]]], ['leaf', 1000]]]
-<
- Can also be used as a |method|: >
- GetTabnr()->winlayout()
-<
- *winline()*
-winline() The result is a Number, which is the screen line of the cursor
- in the window. This is counting screen lines from the top of
- the window. The first line is one.
- If the cursor was moved the view on the file will be updated
- first, this may cause a scroll.
-
- *winnr()*
-winnr([{arg}]) The result is a Number, which is the number of the current
- window. The top window has number 1.
- Returns zero for a popup window.
-
- The optional argument {arg} supports the following values:
- $ the number of the last window (the window
- count).
- # the number of the last accessed window (where
- |CTRL-W_p| goes to). If there is no previous
- window or it is in another tab page 0 is
- returned.
- {N}j the number of the Nth window below the
- current window (where |CTRL-W_j| goes to).
- {N}k the number of the Nth window above the current
- window (where |CTRL-W_k| goes to).
- {N}h the number of the Nth window left of the
- current window (where |CTRL-W_h| goes to).
- {N}l the number of the Nth window right of the
- current window (where |CTRL-W_l| goes to).
- The number can be used with |CTRL-W_w| and ":wincmd w"
- |:wincmd|.
- Also see |tabpagewinnr()| and |win_getid()|.
- Examples: >
- let window_count = winnr('$')
- let prev_window = winnr('#')
- let wnum = winnr('3k')
-
-< Can also be used as a |method|: >
- GetWinval()->winnr()
-<
- *winrestcmd()*
-winrestcmd() Returns a sequence of |:resize| commands that should restore
- the current window sizes. Only works properly when no windows
- are opened or closed and the current window and tab page is
- unchanged.
- Example: >
- :let cmd = winrestcmd()
- :call MessWithWindowSizes()
- :exe cmd
-<
- *winrestview()*
-winrestview({dict})
- Uses the |Dictionary| returned by |winsaveview()| to restore
- the view of the current window.
- Note: The {dict} does not have to contain all values, that are
- returned by |winsaveview()|. If values are missing, those
- settings won't be restored. So you can use: >
- :call winrestview({'curswant': 4})
-<
- This will only set the curswant value (the column the cursor
- wants to move on vertical movements) of the cursor to column 5
- (yes, that is 5), while all other settings will remain the
- same. This is useful, if you set the cursor position manually.
-
- If you have changed the values the result is unpredictable.
- If the window size changed the result won't be the same.
-
- Can also be used as a |method|: >
- GetView()->winrestview()
-<
- *winsaveview()*
-winsaveview() Returns a |Dictionary| that contains information to restore
- the view of the current window. Use |winrestview()| to
- restore the view.
- This is useful if you have a mapping that jumps around in the
- buffer and you want to go back to the original view.
- This does not save fold information. Use the 'foldenable'
- option to temporarily switch off folding, so that folds are
- not opened when moving around. This may have side effects.
- The return value includes:
- lnum cursor line number
- col cursor column (Note: the first column
- zero, as opposed to what getpos()
- returns)
- coladd cursor column offset for 'virtualedit'
- curswant column for vertical movement
- topline first line in the window
- topfill filler lines, only in diff mode
- leftcol first column displayed; only used when
- 'wrap' is off
- skipcol columns skipped
- Note that no option values are saved.
-
-
-winwidth({nr}) *winwidth()*
- The result is a Number, which is the width of window {nr}.
- {nr} can be the window number or the |window-ID|.
- When {nr} is zero, the width of the current window is
- returned. When window {nr} doesn't exist, -1 is returned.
- An existing window always has a width of zero or more.
- Examples: >
- :echo "The current window has " . winwidth(0) . " columns."
- :if winwidth(0) <= 50
- : 50 wincmd |
- :endif
-< For getting the terminal or screen size, see the 'columns'
- option.
-
- Can also be used as a |method|: >
- GetWinid()->winwidth()
-
-wordcount() *wordcount()*
- The result is a dictionary of byte/chars/word statistics for
- the current buffer. This is the same info as provided by
- |g_CTRL-G|
- The return value includes:
- bytes Number of bytes in the buffer
- chars Number of chars in the buffer
- words Number of words in the buffer
- cursor_bytes Number of bytes before cursor position
- (not in Visual mode)
- cursor_chars Number of chars before cursor position
- (not in Visual mode)
- cursor_words Number of words before cursor position
- (not in Visual mode)
- visual_bytes Number of bytes visually selected
- (only in Visual mode)
- visual_chars Number of chars visually selected
- (only in Visual mode)
- visual_words Number of words visually selected
- (only in Visual mode)
-
-
- *writefile()*
-writefile({object}, {fname} [, {flags}])
- When {object} is a |List| write it to file {fname}. Each list
- item is separated with a NL. Each list item must be a String
- or Number.
- When {flags} contains "b" then binary mode is used: There will
- not be a NL after the last list item. An empty item at the
- end does cause the last line in the file to end in a NL.
-
- When {object} is a |Blob| write the bytes to file {fname}
- unmodified.
-
- When {flags} contains "a" then append mode is used, lines are
- appended to the file: >
- :call writefile(["foo"], "event.log", "a")
- :call writefile(["bar"], "event.log", "a")
-<
- When {flags} contains "S" fsync() call is not used, with "s"
- it is used, 'fsync' option applies by default. No fsync()
- means that writefile() will finish faster, but writes may be
- left in OS buffers and not yet written to disk. Such changes
- will disappear if system crashes before OS does writing.
-
- All NL characters are replaced with a NUL character.
- Inserting CR characters needs to be done before passing {list}
- to writefile().
- An existing file is overwritten, if possible.
- When the write fails -1 is returned, otherwise 0. There is an
- error message if the file can't be created or when writing
- fails.
- Also see |readfile()|.
- To copy a file byte for byte: >
- :let fl = readfile("foo", "b")
- :call writefile(fl, "foocopy", "b")
-
-< Can also be used as a |method|: >
- GetText()->writefile("thefile")
-
-xor({expr}, {expr}) *xor()*
- Bitwise XOR on the two arguments. The arguments are converted
- to a number. A List, Dict or Float argument causes an error.
- Example: >
- :let bits = xor(bits, 0x80)
-<
- Can also be used as a |method|: >
- :let bits = bits->xor(0x80)
-<
-
- *string-match*
-Matching a pattern in a String
-
-A regexp pattern as explained at |pattern| is normally used to find a match in
-the buffer lines. When a pattern is used to find a match in a String, almost
-everything works in the same way. The difference is that a String is handled
-like it is one line. When it contains a "\n" character, this is not seen as a
-line break for the pattern. It can be matched with a "\n" in the pattern, or
-with ".". Example: >
- :let a = "aaaa\nxxxx"
- :echo matchstr(a, "..\n..")
- aa
- xx
- :echo matchstr(a, "a.x")
- a
- x
-
-Don't forget that "^" will only match at the first character of the String and
-"$" at the last character of the string. They don't match after or before a
-"\n".
+The alphabetic list of all builtin functions and details are in a separate
+help file: |builtin-functions|.
==============================================================================
5. Defining functions *user-function*
@@ -11163,9 +2548,9 @@ Example: >
: echohl Title
: echo a:title
: echohl None
- : echo a:0 . " items:"
+ : echo a:0 .. " items:"
: for s in a:000
- : echon ' ' . s
+ : echon ' ' .. s
: endfor
:endfunction
@@ -11204,7 +2589,7 @@ This function can then be called with: >
this works:
*function-range-example* >
:function Mynumber(arg)
- : echo line(".") . " " . a:arg
+ : echo line(".") .. " " .. a:arg
:endfunction
:1,5call Mynumber(getline("."))
<
@@ -11215,7 +2600,7 @@ This function can then be called with: >
Example of a function that handles the range itself: >
:function Cont() range
- : execute (a:firstline + 1) . "," . a:lastline . 's/^/\t\\ '
+ : execute (a:firstline + 1) .. "," .. a:lastline .. 's/^/\t\\ '
:endfunction
:4,8call Cont()
<
@@ -11377,7 +2762,7 @@ This does NOT work: >
This cannot be used to add an item to a |List|.
This cannot be used to set a byte in a String. You
can do that like this: >
- :let var = var[0:2] . 'X' . var[4:]
+ :let var = var[0:2] .. 'X' .. var[4:]
< When {var-name} is a |Blob| then {idx} can be the
length of the blob, in which case one byte is
appended.
@@ -11439,7 +2824,7 @@ This does NOT work: >
is just like using the |:set| command: both the local
value and the global value are changed.
Example: >
- :let &path = &path . ',/usr/local/include'
+ :let &path = &path .. ',/usr/local/include'
:let &{option-name} .= {expr1}
For a string option: Append {expr1} to the value.
@@ -11694,14 +3079,17 @@ text...
opposite of |:lockvar|.
:if {expr1} *:if* *:end* *:endif* *:en* *E171* *E579* *E580*
-:en[dif] Execute the commands until the next matching ":else"
- or ":endif" if {expr1} evaluates to non-zero.
+:en[dif] Execute the commands until the next matching `:else`
+ or `:endif` if {expr1} evaluates to non-zero.
+ Although the short forms work, it is recommended to
+ always use `:endif` to avoid confusion and to make
+ auto-indenting work properly.
From Vim version 4.5 until 5.0, every Ex command in
- between the ":if" and ":endif" is ignored. These two
+ between the `:if` and `:endif` is ignored. These two
commands were just to allow for future expansions in a
backward compatible way. Nesting was allowed. Note
- that any ":else" or ":elseif" was ignored, the "else"
+ that any `:else` or `:elseif` was ignored, the `else`
part was not executed either.
You can use this to remain compatible with older
@@ -11710,32 +3098,32 @@ text...
: version-5-specific-commands
:endif
< The commands still need to be parsed to find the
- "endif". Sometimes an older Vim has a problem with a
- new command. For example, ":silent" is recognized as
- a ":substitute" command. In that case ":execute" can
+ `endif`. Sometimes an older Vim has a problem with a
+ new command. For example, `:silent` is recognized as
+ a `:substitute` command. In that case `:execute` can
avoid problems: >
:if version >= 600
: execute "silent 1,$delete"
:endif
<
- NOTE: The ":append" and ":insert" commands don't work
- properly in between ":if" and ":endif".
+ NOTE: The `:append` and `:insert` commands don't work
+ properly in between `:if` and `:endif`.
*:else* *:el* *E581* *E583*
-:el[se] Execute the commands until the next matching ":else"
- or ":endif" if they previously were not being
+:el[se] Execute the commands until the next matching `:else`
+ or `:endif` if they previously were not being
executed.
*:elseif* *:elsei* *E582* *E584*
-:elsei[f] {expr1} Short for ":else" ":if", with the addition that there
- is no extra ":endif".
+:elsei[f] {expr1} Short for `:else` `:if`, with the addition that there
+ is no extra `:endif`.
:wh[ile] {expr1} *:while* *:endwhile* *:wh* *:endw*
*E170* *E585* *E588* *E733*
-:endw[hile] Repeat the commands between ":while" and ":endwhile",
+:endw[hile] Repeat the commands between `:while` and `:endwhile`,
as long as {expr1} evaluates to non-zero.
When an error is detected from a command inside the
- loop, execution continues after the "endwhile".
+ loop, execution continues after the `endwhile`.
Example: >
:let lnum = 1
:while lnum <= line("$")
@@ -11743,16 +3131,19 @@ text...
:let lnum = lnum + 1
:endwhile
<
- NOTE: The ":append" and ":insert" commands don't work
- properly inside a ":while" and ":for" loop.
+ NOTE: The `:append` and `:insert` commands don't work
+ properly inside a `:while` and `:for` loop.
:for {var} in {object} *:for* *E690* *E732*
:endfo[r] *:endfo* *:endfor*
- Repeat the commands between ":for" and ":endfor" for
- each item in {object}. {object} can be a |List| or
- a |Blob|. Variable {var} is set to the value of each
- item. When an error is detected for a command inside
- the loop, execution continues after the "endfor".
+ Repeat the commands between `:for` and `:endfor` for
+ each item in {object}. {object} can be a |List|,
+ a |Blob| or a |String|.
+
+ Variable {var} is set to the value of each item.
+
+ When an error is detected for a command inside the
+ loop, execution continues after the `endfor`.
Changing {object} inside the loop affects what items
are used. Make a copy if this is unwanted: >
:for item in copy(mylist)
@@ -11776,7 +3167,7 @@ text...
:for [{var1}, {var2}, ...] in {listlist}
:endfo[r]
- Like ":for" above, but each item in {listlist} must be
+ Like `:for` above, but each item in {listlist} must be
a list, of which each item is assigned to {var1},
{var2}, etc. Example: >
:for [lnum, col] in [[1, 3], [2, 5], [3, 8]]
@@ -11784,38 +3175,39 @@ text...
:endfor
<
*:continue* *:con* *E586*
-:con[tinue] When used inside a ":while" or ":for" loop, jumps back
+:con[tinue] When used inside a `:while` or `:for` loop, jumps back
to the start of the loop.
- If it is used after a |:try| inside the loop but
- before the matching |:finally| (if present), the
- commands following the ":finally" up to the matching
- |:endtry| are executed first. This process applies to
- all nested ":try"s inside the loop. The outermost
- ":endtry" then jumps back to the start of the loop.
+
+ If it is used after a `:try` inside the loop but
+ before the matching `:finally` (if present), the
+ commands following the `:finally` up to the matching
+ `:endtry` are executed first. This process applies to
+ all nested `:try`s inside the loop. The outermost
+ `:endtry` then jumps back to the start of the loop.
*:break* *:brea* *E587*
-:brea[k] When used inside a ":while" or ":for" loop, skips to
- the command after the matching ":endwhile" or
- ":endfor".
- If it is used after a |:try| inside the loop but
- before the matching |:finally| (if present), the
- commands following the ":finally" up to the matching
- |:endtry| are executed first. This process applies to
- all nested ":try"s inside the loop. The outermost
- ":endtry" then jumps to the command after the loop.
+:brea[k] When used inside a `:while` or `:for` loop, skips to
+ the command after the matching `:endwhile` or
+ `:endfor`.
+ If it is used after a `:try` inside the loop but
+ before the matching `:finally` (if present), the
+ commands following the `:finally` up to the matching
+ `:endtry` are executed first. This process applies to
+ all nested `:try`s inside the loop. The outermost
+ `:endtry` then jumps to the command after the loop.
:try *:try* *:endt* *:endtry* *E600* *E601* *E602*
:endt[ry] Change the error handling for the commands between
- ":try" and ":endtry" including everything being
- executed across ":source" commands, function calls,
+ `:try` and `:endtry` including everything being
+ executed across `:source` commands, function calls,
or autocommand invocations.
When an error or interrupt is detected and there is
- a |:finally| command following, execution continues
- after the ":finally". Otherwise, or when the
- ":endtry" is reached thereafter, the next
- (dynamically) surrounding ":try" is checked for
- a corresponding ":finally" etc. Then the script
+ a `:finally` command following, execution continues
+ after the `:finally`. Otherwise, or when the
+ `:endtry` is reached thereafter, the next
+ (dynamically) surrounding `:try` is checked for
+ a corresponding `:finally` etc. Then the script
processing is terminated. Whether a function
definition has an "abort" argument does not matter.
Example: >
@@ -11823,9 +3215,9 @@ text...
echomsg "not reached"
<
Moreover, an error or interrupt (dynamically) inside
- ":try" and ":endtry" is converted to an exception. It
- can be caught as if it were thrown by a |:throw|
- command (see |:catch|). In this case, the script
+ `:try` and `:endtry` is converted to an exception. It
+ can be caught as if it were thrown by a `:throw`
+ command (see `:catch`). In this case, the script
processing is not terminated.
The value "Vim:Interrupt" is used for an interrupt
@@ -11841,22 +3233,22 @@ text...
try | edit | catch /^Vim(edit):E\d\+/ | echo "error" | endtry
<
*:cat* *:catch* *E603* *E604* *E605*
-:cat[ch] /{pattern}/ The following commands until the next |:catch|,
- |:finally|, or |:endtry| that belongs to the same
- |:try| as the ":catch" are executed when an exception
+:cat[ch] /{pattern}/ The following commands until the next `:catch`,
+ `:finally`, or `:endtry` that belongs to the same
+ `:try` as the `:catch` are executed when an exception
matching {pattern} is being thrown and has not yet
- been caught by a previous ":catch". Otherwise, these
+ been caught by a previous `:catch`. Otherwise, these
commands are skipped.
When {pattern} is omitted all errors are caught.
Examples: >
- :catch /^Vim:Interrupt$/ " catch interrupts (CTRL-C)
- :catch /^Vim\%((\a\+)\)\=:E/ " catch all Vim errors
- :catch /^Vim\%((\a\+)\)\=:/ " catch errors and interrupts
- :catch /^Vim(write):/ " catch all errors in :write
- :catch /^Vim\%((\a\+)\)\=:E123/ " catch error E123
- :catch /my-exception/ " catch user exception
- :catch /.*/ " catch everything
- :catch " same as /.*/
+ :catch /^Vim:Interrupt$/ " catch interrupts (CTRL-C)
+ :catch /^Vim\%((\a\+)\)\=:E/ " catch all Vim errors
+ :catch /^Vim\%((\a\+)\)\=:/ " catch errors and interrupts
+ :catch /^Vim(write):/ " catch all errors in :write
+ :catch /^Vim\%((\a\+)\)\=:E123:/ " catch error E123
+ :catch /my-exception/ " catch user exception
+ :catch /.*/ " catch everything
+ :catch " same as /.*/
<
Another character can be used instead of / around the
{pattern}, so long as it does not have a special
@@ -11869,27 +3261,27 @@ text...
locales.
*:fina* *:finally* *E606* *E607*
-:fina[lly] The following commands until the matching |:endtry|
+:fina[lly] The following commands until the matching `:endtry`
are executed whenever the part between the matching
- |:try| and the ":finally" is left: either by falling
- through to the ":finally" or by a |:continue|,
- |:break|, |:finish|, or |:return|, or by an error or
- interrupt or exception (see |:throw|).
+ `:try` and the `:finally` is left: either by falling
+ through to the `:finally` or by a `:continue`,
+ `:break`, `:finish`, or `:return`, or by an error or
+ interrupt or exception (see `:throw`).
*:th* *:throw* *E608*
:th[row] {expr1} The {expr1} is evaluated and thrown as an exception.
- If the ":throw" is used after a |:try| but before the
- first corresponding |:catch|, commands are skipped
- until the first ":catch" matching {expr1} is reached.
- If there is no such ":catch" or if the ":throw" is
- used after a ":catch" but before the |:finally|, the
- commands following the ":finally" (if present) up to
- the matching |:endtry| are executed. If the ":throw"
- is after the ":finally", commands up to the ":endtry"
- are skipped. At the ":endtry", this process applies
- again for the next dynamically surrounding ":try"
+ If the `:throw` is used after a `:try` but before the
+ first corresponding `:catch`, commands are skipped
+ until the first `:catch` matching {expr1} is reached.
+ If there is no such `:catch` or if the `:throw` is
+ used after a `:catch` but before the `:finally`, the
+ commands following the `:finally` (if present) up to
+ the matching `:endtry` are executed. If the `:throw`
+ is after the `:finally`, commands up to the `:endtry`
+ are skipped. At the `:endtry`, this process applies
+ again for the next dynamically surrounding `:try`
(which may be found in a calling function or sourcing
- script), until a matching ":catch" has been found.
+ script), until a matching `:catch` has been found.
If the exception is not caught, the command processing
is terminated.
Example: >
@@ -11904,7 +3296,7 @@ text...
Also see |:comment|.
Use "\n" to start a new line. Use "\r" to move the
cursor to the first column.
- Uses the highlighting set by the |:echohl| command.
+ Uses the highlighting set by the `:echohl` command.
Cannot be followed by a comment.
Example: >
:echo "the value of 'shell' is" &shell
@@ -11913,9 +3305,9 @@ text...
And since Vim mostly postpones redrawing until it's
finished with a sequence of commands this happens
quite often. To avoid that a command from before the
- ":echo" causes a redraw afterwards (redraws are often
+ `:echo` causes a redraw afterwards (redraws are often
postponed until you type something), force a redraw
- with the |:redraw| command. Example: >
+ with the `:redraw` command. Example: >
:new | redraw | echo "there is a new window"
< *:echo-self-refer*
When printing nested containers echo prints second
@@ -11934,13 +3326,13 @@ text...
*:echon*
:echon {expr1} .. Echoes each {expr1}, without anything added. Also see
|:comment|.
- Uses the highlighting set by the |:echohl| command.
+ Uses the highlighting set by the `:echohl` command.
Cannot be followed by a comment.
Example: >
:echon "the value of 'shell' is " &shell
<
- Note the difference between using ":echo", which is a
- Vim command, and ":!echo", which is an external shell
+ Note the difference between using `:echo`, which is a
+ Vim command, and `:!echo`, which is an external shell
command: >
:!echo % --> filename
< The arguments of ":!" are expanded, see |:_%|. >
@@ -11956,8 +3348,8 @@ text...
*:echoh* *:echohl*
:echoh[l] {name} Use the highlight group {name} for the following
- |:echo|, |:echon| and |:echomsg| commands. Also used
- for the |input()| prompt. Example: >
+ `:echo`, `:echon` and `:echomsg` commands. Also used
+ for the `input()` prompt. Example: >
:echohl WarningMsg | echo "Don't panic!" | echohl None
< Don't forget to set the group back to "None",
otherwise all following echo's will be highlighted.
@@ -11966,14 +3358,14 @@ text...
:echom[sg] {expr1} .. Echo the expression(s) as a true message, saving the
message in the |message-history|.
Spaces are placed between the arguments as with the
- |:echo| command. But unprintable characters are
+ `:echo` command. But unprintable characters are
displayed, not interpreted.
- The parsing works slightly different from |:echo|,
- more like |:execute|. All the expressions are first
+ The parsing works slightly different from `:echo`,
+ more like `:execute`. All the expressions are first
evaluated and concatenated before echoing anything.
If expressions does not evaluate to a Number or
String, string() is used to turn it into a string.
- Uses the highlighting set by the |:echohl| command.
+ Uses the highlighting set by the `:echohl` command.
Example: >
:echomsg "It's a Zizzer Zazzer Zuzz, as you can plainly see."
< See |:echo-redraw| to avoid the message disappearing
@@ -11983,12 +3375,12 @@ text...
message in the |message-history|. When used in a
script or function the line number will be added.
Spaces are placed between the arguments as with the
- |:echomsg| command. When used inside a try conditional,
+ `:echomsg` command. When used inside a try conditional,
the message is raised as an error exception instead
(see |try-echoerr|).
Example: >
:echoerr "This script just failed!"
-< If you just want a highlighted message use |:echohl|.
+< If you just want a highlighted message use `:echohl`.
And to get a beep: >
:exe "normal \<Esc>"
<
@@ -12293,7 +3685,7 @@ exception most recently caught as long it is not finished.
:function! Caught()
: if v:exception != ""
- : echo 'Caught "' . v:exception . '" in ' . v:throwpoint
+ : echo 'Caught "' .. v:exception .. '" in ' .. v:throwpoint
: else
: echo 'Nothing caught'
: endif
@@ -12696,8 +4088,8 @@ a script in order to catch unexpected things.
:catch /^Vim:Interrupt$/
: echo "Script interrupted"
:catch /.*/
- : echo "Internal error (" . v:exception . ")"
- : echo " - occurred at " . v:throwpoint
+ : echo "Internal error (" .. v:exception .. ")"
+ : echo " - occurred at " .. v:throwpoint
:endtry
:" end of script
@@ -12893,7 +4285,7 @@ parentheses can be cut out from |v:exception| with the ":substitute" command.
:function! CheckRange(a, func)
: if a:a < 0
- : throw "EXCEPT:MATHERR:RANGE(" . a:func . ")"
+ : throw "EXCEPT:MATHERR:RANGE(" .. a:func .. ")"
: endif
:endfunction
:
@@ -12920,13 +4312,13 @@ parentheses can be cut out from |v:exception| with the ":substitute" command.
: try
: execute "write" fnameescape(a:file)
: catch /^Vim(write):/
- : throw "EXCEPT:IO(" . getcwd() . ", " . a:file . "):WRITEERR"
+ : throw "EXCEPT:IO(" .. getcwd() .. ", " .. a:file .. "):WRITEERR"
: endtry
:endfunction
:
:try
:
- : " something with arithmetics and I/O
+ : " something with arithmetic and I/O
:
:catch /^EXCEPT:MATHERR:RANGE/
: let function = substitute(v:exception, '.*(\(\a\+\)).*', '\1', "")
@@ -12939,9 +4331,9 @@ parentheses can be cut out from |v:exception| with the ":substitute" command.
: let dir = substitute(v:exception, '.*(\(.\+\),\s*.\+).*', '\1', "")
: let file = substitute(v:exception, '.*(.\+,\s*\(.\+\)).*', '\1', "")
: if file !~ '^/'
- : let file = dir . "/" . file
+ : let file = dir .. "/" .. file
: endif
- : echo 'I/O error for "' . file . '"'
+ : echo 'I/O error for "' .. file .. '"'
:
:catch /^EXCEPT/
: echo "Unspecified error"
@@ -13009,7 +4401,7 @@ clauses, however, is executed.
: echo "inner finally"
: endtry
:catch
- : echo 'outer catch-all caught "' . v:exception . '"'
+ : echo 'outer catch-all caught "' .. v:exception .. '"'
: finally
: echo "outer finally"
:endtry
@@ -13071,7 +4463,7 @@ Printing in Binary ~
: let n = a:nr
: let r = ""
: while n
- : let r = '01'[n % 2] . r
+ : let r = '01'[n % 2] .. r
: let n = n / 2
: endwhile
: return r
@@ -13082,7 +4474,7 @@ Printing in Binary ~
:func String2Bin(str)
: let out = ''
: for ix in range(strlen(a:str))
- : let out = out . '-' . Nr2Bin(char2nr(a:str[ix]))
+ : let out = out .. '-' .. Nr2Bin(char2nr(a:str[ix]))
: endfor
: return out[1:]
:endfunc
diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt
index fdd9c8c12e..7fff74a963 100644
--- a/runtime/doc/filetype.txt
+++ b/runtime/doc/filetype.txt
@@ -131,26 +131,42 @@ 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|
- *.fs g:filetype_fs |ft-forth-syntax|
+ *.bas g:filetype_bas |ft-basic-syntax|
+ *.cfg g:filetype_cfg
+ *.cls g:filetype_cls
+ *.csh g:filetype_csh |ft-csh-syntax|
+ *.dat g:filetype_dat
+ *.frm g:filetype_frm |ft-form-syntax|
+ *.fs g:filetype_fs |ft-forth-syntax|
*.i g:filetype_i |ft-progress-syntax|
*.inc g:filetype_inc
*.m g:filetype_m |ft-mathematica-syntax|
+ *.mod g:filetype_mod
*.p g:filetype_p |ft-pascal-syntax|
*.pl g:filetype_pl
*.pp g:filetype_pp |ft-pascal-syntax|
*.prg g:filetype_prg
+ *.r g:filetype_r
+ *.sig g:filetype_sig
+ *.sql g:filetype_sql |ft-sql-syntax|
+ *.src g:filetype_src
+ *.sys g:filetype_sys
*.sh g:bash_is_sh |ft-sh-syntax|
*.tex g:tex_flavor |ft-tex-plugin|
*.w g:filetype_w |ft-cweb-syntax|
+For a few filetypes the global variable is used only when the filetype could
+not be detected:
+ *.r g:filetype_r |ft-rexx-syntax|
+
*filetype-ignore*
To avoid that certain files are being inspected, the g:ft_ignore_pat variable
is used. The default value is set like this: >
@@ -161,7 +177,9 @@ This means that the contents of compressed files are not inspected.
If a file type that you want to use is not detected yet, there are a few ways
to add it. In any way, it's better not to modify the $VIMRUNTIME/filetype.lua
or $VIMRUNTIME/filetype.vim files. They will be overwritten when installing a
-new version of Nvim.
+new version of Nvim. The following explains the legacy Vim mechanism (enabled
+if |do_legacy_filetype| is set). For Nvim's default mechanism, see
+|vim.filetype.add()|.
A. If you want to overrule all default file type checks.
This works by writing one file for each filetype. The disadvantage is that
@@ -220,36 +238,8 @@ C. If your file type can be detected by the file name or extension.
Write this file as "filetype.vim" in your user runtime directory. For
example, for Unix: >
:w ~/.config/nvim/filetype.vim
-<
- Alternatively, create a file called "filetype.lua" that adds new
- filetypes.
- Example: >
- vim.filetype.add({
- extension = {
- foo = "fooscript",
- },
- filename = {
- [".foorc"] = "foorc",
- },
- pattern = {
- [".*/etc/foo/.*%.conf"] = "foorc",
- },
- })
-<
- See |vim.filetype.add()|.
- *g:do_filetype_lua*
- For now, Lua filetype detection is opt-in. You can enable it by adding
- the following to your |init.vim|: >
- let g:do_filetype_lua = 1
-< *g:did_load_filetypes*
- In either case, the builtin filetype detection provided by Nvim can be
- disabled by setting the did_load_filetypes global variable. If this
- variable exists, $VIMRUNTIME/filetype.vim will not run.
- Example: >
- " Disable filetype.vim
- let g:did_load_filetypes = 1
-< 3. To use the new filetype detection you must restart Vim.
+< 3. To use the new filetype detection you must restart Vim.
Your filetype.vim will be sourced before the default FileType autocommands
have been installed. Your autocommands will match first, and the
@@ -296,6 +286,16 @@ the 'runtimepath' for a directory to use. If there isn't one, set
'runtimepath' in the |system-vimrc|. Be careful to keep the default
directories!
+ *g:do_legacy_filetype*
+To disable Nvim's default filetype detection and revert to Vim's legacy
+filetype detection, add the following to your |init.vim|: >
+ let g:do_legacy_filetype = 1
+< *g:did_load_filetypes*
+The builtin filetype detection provided by Nvim can be disabled by setting
+the `did_load_filetypes` global variable. If this variable exists, neither
+the default `$VIMRUNTIME/filetype.lua` nor the legacy `$VIMRUNTIME/filetype.vim`
+will run.
+
*plugin-details*
The "plugin" directory can be in any of the directories in the 'runtimepath'
option. All of these directories will be searched for plugins and they are
@@ -534,12 +534,31 @@ Options:
For further discussion of fortran_have_tabs and the method used for the
detection of source format see |ft-fortran-syntax|.
+
+FREEBASIC *ft-freebasic-plugin*
+
+This plugin aims to treat the four FreeBASIC dialects, "fb", "qb", "fblite"
+and "deprecated", as distinct languages.
+
+The dialect will be set to the first name found in g:freebasic_forcelang, any
+#lang directive or $lang metacommand in the file being edited, or finally
+g:freebasic_lang. These global variables conceptually map to the fbc options
+-forcelang and -lang. If no dialect is explicitly specified "fb" will be
+used.
+
+For example, to set the dialect to a default of "fblite" but still allow for
+any #lang directive overrides, use the following command: >
+
+ let g:freebasic_lang = "fblite"
+
+
GIT COMMIT *ft-gitcommit-plugin*
One command, :DiffGitCached, is provided to show a diff of the current commit
in the preview window. It is equivalent to calling "git diff --cached" plus
any arguments given to the command.
+
GPROF *ft-gprof-plugin*
The gprof filetype plugin defines a mapping <C-]> to jump from a function
@@ -549,6 +568,7 @@ to the details of that function in the call graph.
The mapping can be disabled with: >
let g:no_gprof_maps = 1
+
MAIL *ft-mail-plugin*
Options:
diff --git a/runtime/doc/fold.txt b/runtime/doc/fold.txt
index 80c934d13b..9e3d78faff 100644
--- a/runtime/doc/fold.txt
+++ b/runtime/doc/fold.txt
@@ -497,11 +497,13 @@ Note the use of backslashes to avoid some characters to be interpreted by the
:function MyFoldText()
: let line = getline(v:foldstart)
: let sub = substitute(line, '/\*\|\*/\|{{{\d\=', '', 'g')
- : return v:folddashes . sub
+ : return v:folddashes .. sub
: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/ft_ada.txt b/runtime/doc/ft_ada.txt
index 771ccc3302..f6dfa708fb 100644
--- a/runtime/doc/ft_ada.txt
+++ b/runtime/doc/ft_ada.txt
@@ -89,9 +89,9 @@ file is opened and adds Ada related entries to the main and pop-up menu.
*ft-ada-omni*
The Ada omni-completions (|i_CTRL-X_CTRL-O|) uses tags database created either
-by "gnat xref -v" or the "exuberant Ctags (http://ctags.sourceforge.net). The
-complete function will automatically detect which tool was used to create the
-tags file.
+by "gnat xref -v" or the "Universal Ctags" (https://ctags.io). The complete
+function will automatically detect which tool was used to create the tags
+file.
------------------------------------------------------------------------------
3.1 Omni Completion with "gnat xref" ~
@@ -125,18 +125,18 @@ NOTE: "gnat xref -v" is very tricky to use as it has almost no diagnostic
3.2 Omni Completion with "ctags"~
*ada-ctags*
-Exuberant Ctags uses its own multi-language code parser. The parser is quite
-fast, produces a lot of extra information (hence the name "Exuberant Ctags")
-and can run on files which currently do not compile.
+Universal/Exuberant Ctags use their own multi-language code parser. The
+parser is quite fast, produces a lot of extra information and can run on files
+which currently do not compile.
-There are also lots of other Vim-tools which use exuberant Ctags.
+There are also lots of other Vim-tools which use Universal/Exuberant Ctags.
+Universal Ctags is preferred, Exuberant Ctags is no longer being developed.
-You will need to install a version of the Exuberant Ctags which has Ada
-support patched in. Such a version is available from the GNU Ada Project
-(http://gnuada.sourceforge.net).
+You will need to install Universal Ctags which is available from
+https://ctags.io
-The Ada parser for Exuberant Ctags is fairly new - don't expect complete
-support yet.
+The Ada parser for Universal/Exuberant Ctags is fairly new - don't expect
+complete support yet.
==============================================================================
4. Compiler Support ~
diff --git a/runtime/doc/ft_raku.txt b/runtime/doc/ft_raku.txt
index 00b140ee9c..3d1179ed4e 100644
--- a/runtime/doc/ft_raku.txt
+++ b/runtime/doc/ft_raku.txt
@@ -47,20 +47,20 @@ Numbers, subscripts and superscripts are available with 's' and 'S':
But some don't come defined by default. Those are digraph definitions you can
add in your ~/.vimrc file. >
- exec 'digraph \\ '.char2nr('∖')
- exec 'digraph \< '.char2nr('≼')
- exec 'digraph \> '.char2nr('≽')
- exec 'digraph (L '.char2nr('⊈')
- exec 'digraph )L '.char2nr('⊉')
- exec 'digraph (/ '.char2nr('⊄')
- exec 'digraph )/ '.char2nr('⊅')
- exec 'digraph )/ '.char2nr('⊅')
- exec 'digraph U+ '.char2nr('⊎')
- exec 'digraph 0- '.char2nr('⊖')
+ exec 'digraph \\ ' .. char2nr('∖')
+ exec 'digraph \< ' .. char2nr('≼')
+ exec 'digraph \> ' .. char2nr('≽')
+ exec 'digraph (L ' .. char2nr('⊈')
+ exec 'digraph )L ' .. char2nr('⊉')
+ exec 'digraph (/ ' .. char2nr('⊄')
+ exec 'digraph )/ ' .. char2nr('⊅')
+ exec 'digraph )/ ' .. char2nr('⊅')
+ exec 'digraph U+ ' .. char2nr('⊎')
+ exec 'digraph 0- ' .. char2nr('⊖')
" Euler's constant
- exec 'digraph ne '.char2nr('𝑒')
+ exec 'digraph ne ' .. char2nr('𝑒')
" Raku's atomic operations marker
- exec 'digraph @@ '.char2nr('⚛')
+ exec 'digraph @@ ' .. char2nr('⚛')
Alternatively, you can write Insert mode abbreviations that convert ASCII-
based operators into their single-character Unicode equivalent. >
diff --git a/runtime/doc/ft_rust.txt b/runtime/doc/ft_rust.txt
index ff2e0ca56f..5c8782ec7a 100644
--- a/runtime/doc/ft_rust.txt
+++ b/runtime/doc/ft_rust.txt
@@ -26,7 +26,7 @@ behavior of the plugin.
g:rustc_path~
Set this option to the path to rustc for use in the |:RustRun| and
|:RustExpand| commands. If unset, "rustc" will be located in $PATH: >
- let g:rustc_path = $HOME."/bin/rustc"
+ let g:rustc_path = $HOME .. "/bin/rustc"
<
*g:rustc_makeprg_no_percent*
@@ -87,7 +87,7 @@ g:rust_bang_comment_leader~
g:ftplugin_rust_source_path~
Set this option to a path that should be prepended to 'path' for Rust
source files: >
- let g:ftplugin_rust_source_path = $HOME.'/dev/rust'
+ let g:ftplugin_rust_source_path = $HOME .. '/dev/rust'
<
*g:rustfmt_command*
diff --git a/runtime/doc/ft_sql.txt b/runtime/doc/ft_sql.txt
index 53a99a9e1d..6972fe0768 100644
--- a/runtime/doc/ft_sql.txt
+++ b/runtime/doc/ft_sql.txt
@@ -109,8 +109,8 @@ must be configurable. The filetype plugin attempts to define many of the
standard objects, plus many additional ones. In order to make this as
flexible as possible, you can override the list of objects from within your
|vimrc| with the following: >
- let g:ftplugin_sql_objects = 'function,procedure,event,table,trigger' .
- \ ',schema,service,publication,database,datatype,domain' .
+ let g:ftplugin_sql_objects = 'function,procedure,event,table,trigger' ..
+ \ ',schema,service,publication,database,datatype,domain' ..
\ ',index,subscription,synchronization,view,variable'
The following |Normal| mode and |Visual| mode maps have been created which use
@@ -131,10 +131,10 @@ Repeatedly pressing ]} will cycle through each of these create statements: >
create index i1 on t1 (c1);
The default setting for g:ftplugin_sql_objects is: >
- let g:ftplugin_sql_objects = 'function,procedure,event,' .
- \ '\\(existing\\\\|global\\s\\+temporary\\s\\+\\)\\\{,1}' .
- \ 'table,trigger' .
- \ ',schema,service,publication,database,datatype,domain' .
+ let g:ftplugin_sql_objects = 'function,procedure,event,' ..
+ \ '\\(existing\\\\|global\\s\\+temporary\\s\\+\\)\\\{,1}' ..
+ \ 'table,trigger' ..
+ \ ',schema,service,publication,database,datatype,domain' ..
\ ',index,subscription,synchronization,view,variable'
The above will also handle these cases: >
@@ -555,7 +555,7 @@ the SQL completion plugin. >
< 1. After typing SELECT press <C-C>t to display a list of tables.
2. Highlight the table you need the column list for.
3. Press <Enter> to choose the table from the list.
- 4. Press <C-C>l to request a comma separated list of all columns
+ 4. Press <C-C>l to request a comma-separated list of all columns
for this table.
5. Based on the table name chosen in step 3, the plugin attempts to
decide on a reasonable table alias. You are then prompted to
@@ -609,7 +609,7 @@ your |init.vim|: >
>
omni_sql_use_tbl_alias
< - Default: a
- - This setting is only used when generating a comma separated
+ - This setting is only used when generating a comma-separated
column list. By default the map is <C-C>l. When generating
a column list, an alias can be prepended to the beginning of each
column, for example: e.emp_id, e.emp_name. This option has three
@@ -693,9 +693,9 @@ plugin. >
<C-C>c
< - Displays a list of columns for a specific table. >
<C-C>l
-< - Displays a comma separated list of columns for a specific table. >
+< - Displays a comma-separated list of columns for a specific table. >
<C-C>L
-< - Displays a comma separated list of columns for a specific table.
+< - Displays a comma-separated list of columns for a specific table.
This should only be used when the completion window is active. >
<Right>
< - Displays a list of columns for the table currently highlighted in
diff --git a/runtime/doc/gui.txt b/runtime/doc/gui.txt
index 812259741f..776ff228d6 100644
--- a/runtime/doc/gui.txt
+++ b/runtime/doc/gui.txt
@@ -195,6 +195,10 @@ the mouse button down on this will pop up a menu containing the item
"Big Changes", which is a sub-menu containing the item "Delete All Spaces",
which when selected, performs the operation.
+To create a menu for terminal mode, use |:tlmenu| instead of |:tmenu| unlike
+key mapping (|:tmap|). This is because |:tmenu| is already used for defining
+tooltips for menus. See |terminal-input|.
+
Special characters in a menu name:
& The next character is the shortcut key. Make sure each
@@ -214,9 +218,9 @@ this menu can be used. The second part is shown as "Open :e". The ":e"
is right aligned, and the "O" is underlined, to indicate it is the shortcut.
*:am* *:amenu* *:an* *:anoremenu*
-The ":amenu" command can be used to define menu entries for all modes at once.
-To make the command work correctly, a character is automatically inserted for
-some modes:
+The ":amenu" command can be used to define menu entries for all modes at once,
+expect for Terminal mode. To make the command work correctly, a character is
+automatically inserted for some modes:
mode inserted appended ~
Normal nothing nothing
Visual <C-C> <C-\><C-G>
@@ -224,9 +228,6 @@ some modes:
Cmdline <C-C> <C-\><C-G>
Op-pending <C-C> <C-\><C-G>
-Appending CTRL-\ CTRL-G is for going back to insert mode when 'insertmode' is
-set. |CTRL-\_CTRL-G|
-
Example: >
:amenu File.Next :next^M
@@ -440,6 +441,19 @@ You can define the special menu "PopUp". This is the menu that is displayed
when the right mouse button is pressed, if 'mousemodel' is set to popup or
popup_setpos.
+The default "PopUp" menu is: >
+ aunmenu PopUp
+ vnoremenu PopUp.Cut "+x
+ vnoremenu PopUp.Copy "+y
+ anoremenu PopUp.Paste "+gP
+ vnoremenu PopUp.Paste "+P
+ vnoremenu PopUp.Delete "_x
+ nnoremenu PopUp.Select\ All> ggVG
+ vnoremenu PopUp.Select\ All> gg0oG$
+ inoremenu PopUp.Select\ All <C-Home><C-O>VG
+ anoremenu PopUp.-1- <Nop>
+ anoremenu PopUp.How-to\ disable\ mouse <Cmd>help disable-mouse<CR>
+<
Showing What Menus Are Mapped To *showing-menus*
@@ -472,6 +486,16 @@ Executing Menus *execute-menus*
insert-mode menu Eg: >
:emenu File.Exit
+:[range]em[enu] {mode} {menu} Like above, but execute the menu for {mode}:
+ 'n': |:nmenu| Normal mode
+ 'v': |:vmenu| Visual mode
+ 's': |:smenu| Select mode
+ 'o': |:omenu| Operator-pending mode
+ 't': |:tlmenu| Terminal mode
+ 'i': |:imenu| Insert mode
+ 'c': |:cmenu| Cmdline mode
+
+
You can use :emenu to access useful menu items you may have got used to from
GUI mode. See 'wildmenu' for an option that works well with this. See
|console-menus| for an example.
@@ -497,7 +521,9 @@ may be used to complete the name of the menu item for the appropriate mode.
To remove all menus use: *:unmenu-all* >
:unmenu * " remove all menus in Normal and visual mode
:unmenu! * " remove all menus in Insert and Command-line mode
- :aunmenu * " remove all menus in all modes
+ :aunmenu * " remove all menus in all modes, except for Terminal
+ " mode
+ :tlunmenu * " remove all menus in Terminal mode
If you want to get rid of the menu bar: >
:set guioptions-=m
@@ -550,6 +576,8 @@ See section |42.4| in the user manual.
:tu[nmenu] {menupath} Remove a tip for a menu or tool.
{only in X11 and Win32 GUI}
+Note: To create menus for terminal mode, use |:tlmenu| instead.
+
When a tip is defined for a menu item, it appears in the command-line area
when the mouse is over that item, much like a standard Windows menu hint in
the status bar. (Except when Vim is in Command-line mode, when of course
@@ -580,8 +608,8 @@ a menu item - you don't need to do a :tunmenu as well.
5.9 Popup Menus
-In the Win32 GUI, you can cause a menu to popup at the cursor. This behaves
-similarly to the PopUp menus except that any menu tree can be popped up.
+You can cause a menu to popup at the cursor. This behaves similarly to the
+PopUp menus except that any menu tree can be popped up.
This command is for backwards compatibility, using it is discouraged, because
it behaves in a strange way.
@@ -590,7 +618,6 @@ it behaves in a strange way.
:popu[p] {name} Popup the menu {name}. The menu named must
have at least one subentry, but need not
appear on the menu-bar (see |hidden-menus|).
- {only available for Win32 GUI}
:popu[p]! {name} Like above, but use the position of the mouse
pointer instead of the cursor.
diff --git a/runtime/doc/help.txt b/runtime/doc/help.txt
index 173d3c0cdf..b97c9a2e3f 100644
--- a/runtime/doc/help.txt
+++ b/runtime/doc/help.txt
@@ -129,6 +129,7 @@ Advanced editing ~
|diff.txt| working with two to eight versions of the same file
|autocmd.txt| automatically executing commands on an event
|eval.txt| expression evaluation, conditional commands
+|builtin.txt| builtin functions
|fold.txt| hide (fold) ranges of lines
|lua.txt| Lua API
|api.txt| Nvim API via RPC, Lua and VimL
diff --git a/runtime/doc/helphelp.txt b/runtime/doc/helphelp.txt
index 03ec8966d4..569995d319 100644
--- a/runtime/doc/helphelp.txt
+++ b/runtime/doc/helphelp.txt
@@ -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_pyth.txt b/runtime/doc/if_pyth.txt
index fea47de220..9b434e61d7 100644
--- a/runtime/doc/if_pyth.txt
+++ b/runtime/doc/if_pyth.txt
@@ -1,10 +1,10 @@
*if_pyth.txt* Nvim
- VIM REFERENCE MANUAL by Paul Moore
+ NVIM REFERENCE MANUAL
-The Python Interface to Vim *if_pyth* *python* *Python*
+The Python Interface to NVim *if_pyth* *python* *Python*
See |provider-python| for more information.
@@ -134,7 +134,7 @@ Instead, put the Python command in a function and call that function:
Note that "EOF" must be at the start of the line.
==============================================================================
-The vim module *python-vim* *python2*
+The vim module *python-vim*
Python code gets all of its access to vim (with one exception - see
|python-output| below) via the "vim" module. The vim module implements two
@@ -322,14 +322,13 @@ Output from Python *python-output*
supported, and may cause the program to crash. This should probably be
fixed.
- *python2-directory* *python3-directory* *pythonx-directory*
+ *python3-directory* *pythonx-directory*
Python 'runtimepath' handling *python-special-path*
In python vim.VIM_SPECIAL_PATH special directory is used as a replacement for
the list of paths found in 'runtimepath': with this directory in sys.path and
vim.path_hooks in sys.path_hooks python will try to load module from
-{rtp}/python2 (or python3) and {rtp}/pythonx (for both python versions) for
-each {rtp} found in 'runtimepath'.
+{rtp}/python3 and {rtp}/pythonx for each {rtp} found in 'runtimepath'.
Implementation is similar to the following, but written in C: >
@@ -401,8 +400,8 @@ vim._get_paths *python-_get_paths*
hook. You should not rely on this method being present in future
versions, but can use it for debugging.
- It returns a list of {rtp}/python2 (or {rtp}/python3) and
- {rtp}/pythonx directories for each {rtp} in 'runtimepath'.
+ It returns a list of {rtp}/python3 and {rtp}/pythonx
+ directories for each {rtp} in 'runtimepath'.
==============================================================================
Buffer objects *python-buffer*
@@ -590,6 +589,11 @@ functions to evaluate Python expressions and pass their values to Vim script.
==============================================================================
Python 3 *python3*
+As Python 3 is the only supported version in Nvim, "python" is synonymous
+with "python3" in the current version. However, code that aims to support older
+versions of Neovim, as well as Vim, should prefer to use "python3"
+variants explicitly if Python 3 is required.
+
*:py3* *:python3*
:[range]py3 {stmt}
:[range]py3 << [endmarker]
@@ -619,31 +623,26 @@ Raising SystemExit exception in python isn't endorsed way to quit vim, use: >
:py vim.command("qall!")
<
*has-python*
-You can test what Python version is available with: >
- if has('python')
- echo 'there is Python 2.x'
+You can test if Python is available with: >
+ if has('pythonx')
+ echo 'there is Python'
endif
if has('python3')
echo 'there is Python 3.x'
endif
+Python 2 is no longer supported. Thus `has('python')` always returns
+zero for backwards compatibility reasons.
+
==============================================================================
Python X *python_x* *pythonx*
-Because most python code can be written so that it works with Python 2.6+ and
-Python 3, the pyx* functions and commands have been written. They work the
-same as the Python 2 and 3 variants, but select the Python version using the
-'pyxversion' setting.
-
-Set 'pyxversion' in your |vimrc| to prefer Python 2 or Python 3 for Python
-commands. Changing this setting at runtime risks losing the state of plugins
-(such as initialization).
-
-If you want to use a module, you can put it in the {rtp}/pythonx directory.
-See |pythonx-directory|.
+The "pythonx" and "pyx" prefixes were introduced for python code which
+works with Python 2.6+ and Python 3. As Nvim only supports Python 3,
+all these commands are now synonymous to their "python3" equivalents.
*:pyx* *:pythonx*
-`:pyx` and `:pythonx` work similar to `:python`. To check if `:pyx` works: >
+`:pyx` and `:pythonx` work the same as `:python3`. To check if `:pyx` works: >
:pyx print("Hello")
To see what version of Python is being used: >
@@ -651,33 +650,15 @@ To see what version of Python is being used: >
:pyx print(sys.version)
<
*:pyxfile* *python_x-special-comments*
-`:pyxfile` works similar to `:pyfile`. But you can add a "shebang" comment to
-force Vim to use `:pyfile` or `:py3file`: >
- #!/any string/python2 " Shebang. Must be the first line of the file.
- #!/any string/python3 " Shebang. Must be the first line of the file.
- # requires python 2.x " Maximum lines depend on 'modelines'.
- # requires python 3.x " Maximum lines depend on 'modelines'.
-Unlike normal modelines, the bottom of the file is not checked.
-If none of them are found, the 'pyxversion' option is used.
- *W20* *W21*
-If Vim does not support the selected Python version a silent message will be
-printed. Use `:messages` to read them.
+`:pyxfile` works the same as `:py3file`.
*:pyxdo*
-`:pyxdo` works similar to `:pydo`.
+`:pyxdo` works the same as `:py3do`.
*has-pythonx*
-To check if pyx* functions and commands are available: >
+To check if `pyx*` functions and commands are available: >
if has('pythonx')
- echo 'pyx* commands are available. (Python ' . &pyx . ')'
- endif
-
-If you prefer Python 2 and want to fallback to Python 3, set 'pyxversion'
-explicitly in your |.vimrc|. Example: >
- if has('python')
- set pyx=2
- elseif has('python3')
- set pyx=3
+ echo 'pyx* commands are available. (Python ' .. &pyx .. ')'
endif
==============================================================================
diff --git a/runtime/doc/indent.txt b/runtime/doc/indent.txt
index a76f8636f8..1a1d8e30b0 100644
--- a/runtime/doc/indent.txt
+++ b/runtime/doc/indent.txt
@@ -38,11 +38,12 @@ is not a C compiler: it does not recognize all syntax. One requirement is
that toplevel functions have a '{' in the first column. Otherwise they are
easily confused with declarations.
-These four options control C program indenting:
+These five options control C program indenting:
'cindent' Enables Vim to perform C program indenting automatically.
'cinkeys' Specifies which keys trigger reindenting in insert mode.
'cinoptions' Sets your preferred indent style.
'cinwords' Defines keywords that start an extra indent in the next line.
+'cinscopedecls' Defines strings that are recognized as a C++ scope declaration.
If 'lisp' is not on and 'equalprg' is empty, the "=" operator indents using
Vim's built-in algorithm rather than calling an external program.
@@ -289,8 +290,9 @@ The examples below assume a 'shiftwidth' of 4.
<
*cino-g*
gN Place C++ scope declarations N characters from the indent of the
- block they are in. (default 'shiftwidth'). A scope declaration
- can be "public:", "protected:" or "private:".
+ block they are in. (default 'shiftwidth'). By default, a scope
+ declaration is "public:", "protected:" or "private:". This can
+ be adjusted with the 'cinscopedecls' option.
cino= cino=g0 >
{ {
@@ -771,6 +773,15 @@ You can set the indent for the first line after <script> and <style>
"auto" auto indent (same indent as the blocktag)
"inc" auto indent + one indent step
+You can set the indent for attributes after an open <tag line: >
+
+ :let g:html_indent_attribute = 1
+<
+ VALUE MEANING ~
+ 1 auto indent, one indent step more than <tag
+ 2 auto indent, two indent steps (default)
+ > 2 auto indent, more indent steps
+
Many tags increase the indent for what follows per default (see "Add Indent
Tags" in the script). You can add further tags with: >
diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt
index d8689e2c65..25b98ae4ab 100644
--- a/runtime/doc/index.txt
+++ b/runtime/doc/index.txt
@@ -26,7 +26,7 @@ tag char action in Insert mode ~
insert
|i_CTRL-A| CTRL-A insert previously inserted text
|i_CTRL-C| CTRL-C quit insert mode, without checking for
- abbreviation, unless 'insertmode' set.
+ abbreviation
|i_CTRL-D| CTRL-D delete one shiftwidth of indent in the current
line
|i_CTRL-E| CTRL-E insert the character which is below the cursor
@@ -50,7 +50,6 @@ tag char action in Insert mode ~
|i_CTRL-J| CTRL-J same as <CR>
|i_CTRL-K| CTRL-K {char1} {char2}
enter digraph
-|i_CTRL-L| CTRL-L when 'insertmode' set: Leave Insert mode
|i_<CR>| <CR> begin new line
|i_CTRL-M| CTRL-M same as <CR>
|i_CTRL-N| CTRL-N find next match for keyword in front of the
@@ -86,11 +85,10 @@ tag char action in Insert mode ~
|i_CTRL-W| CTRL-W delete word before the cursor
|i_CTRL-X| CTRL-X {mode} enter CTRL-X sub mode, see |i_CTRL-X_index|
|i_CTRL-Y| CTRL-Y insert the character which is above the cursor
-|i_CTRL-Z| CTRL-Z when 'insertmode' set: suspend Vim
-|i_<Esc>| <Esc> end insert mode (unless 'insertmode' set)
+|i_<Esc>| <Esc> end insert mode
|i_CTRL-[| CTRL-[ same as <Esc>
|i_CTRL-\_CTRL-N| CTRL-\ CTRL-N go to Normal mode
-|i_CTRL-\_CTRL-G| CTRL-\ CTRL-G go to mode specified with 'insertmode'
+|i_CTRL-\_CTRL-G| CTRL-\ CTRL-G go to Normal mode
CTRL-\ a - z reserved for extensions
CTRL-\ others not used
|i_CTRL-]| CTRL-] trigger abbreviation
@@ -221,7 +219,7 @@ tag char note action in Normal mode ~
|CTRL-Z| CTRL-Z suspend program (or start new shell)
CTRL-[ <Esc> not used
|CTRL-\_CTRL-N| CTRL-\ CTRL-N go to Normal mode (no-op)
-|CTRL-\_CTRL-G| CTRL-\ CTRL-G go to mode specified with 'insertmode'
+|CTRL-\_CTRL-G| CTRL-\ CTRL-G go to Normal mode (no-op)
CTRL-\ a - z reserved for extensions
CTRL-\ others not used
|CTRL-]| CTRL-] :ta to ident under cursor
@@ -284,7 +282,7 @@ tag char note action in Normal mode ~
|/| /{pattern}<CR> 1 search forward for the Nth occurrence of
{pattern}
|/<CR>| /<CR> 1 search forward for {pattern} of last search
-|count| 0 1 cursor to the first char of the line
+|0| 0 1 cursor to the first char of the line
|count| 1 prepend to command to give a count
|count| 2 "
|count| 3 "
@@ -353,6 +351,7 @@ tag char note action in Normal mode ~
register x]
|Y| ["x]Y yank N lines [into register x]; synonym for
"yy"
+ Note: Mapped to "y$" by default. |default-mappings|
|ZZ| ZZ write if buffer changed and close window
|ZQ| ZQ close window without writing
|[| [{char} square bracket command (see |[| below)
@@ -430,6 +429,7 @@ tag char note action in Normal mode ~
|<C-LeftMouse>| <C-LeftMouse> ":ta" to the keyword at the mouse click
|<C-Right>| <C-Right> 1 same as "w"
|<C-RightMouse>| <C-RightMouse> same as "CTRL-T"
+|<C-Tab>| <C-Tab> same as "g<Tab>"
|<Del>| ["x]<Del> 2 same as "x"
|N<Del>| {count}<Del> remove the last digit from {count}
|<Down>| <Down> 1 same as "j"
@@ -576,7 +576,7 @@ tag command action in Normal mode ~
following the file name.
|CTRL-W_gt| CTRL-W g t same as `gt`: go to next tab page
|CTRL-W_gT| CTRL-W g T same as `gT`: go to previous tab page
-|CTRL-W_g<Tab>| CTRL-W g <Tab> same as `g<Tab>` : go to last accessed tab
+|CTRL-W_g<Tab>| CTRL-W g <Tab> same as |g<Tab>|: go to last accessed tab
page
|CTRL-W_h| CTRL-W h go to Nth left window (stop at first window)
|CTRL-W_i| CTRL-W i split window and jump to declaration of
@@ -890,7 +890,7 @@ here are those that are different.
tag command note action in Visual mode ~
------------------------------------------------------------------------------
|v_CTRL-\_CTRL-N| CTRL-\ CTRL-N stop Visual mode
-|v_CTRL-\_CTRL-G| CTRL-\ CTRL-G go to mode specified with 'insertmode'
+|v_CTRL-\_CTRL-G| CTRL-\ CTRL-G go to Normal mode
|v_CTRL-A| CTRL-A 2 add N to number in highlighted text
|v_CTRL-C| CTRL-C stop Visual mode
|v_CTRL-G| CTRL-G toggle between Visual mode and Select mode
@@ -923,7 +923,9 @@ tag command note action in Visual mode ~
before the highlighted area
|v_J| J 2 join the highlighted lines
|v_K| K run 'keywordprg' on the highlighted area
-|v_O| O Move horizontally to other corner of area.
+|v_O| O move horizontally to other corner of area
+|v_P| P replace highlighted area with register
+ contents; registers are unchanged
Q does not start Ex mode
|v_R| R 2 delete the highlighted lines and start
insert
@@ -986,6 +988,8 @@ tag command note action in Visual mode ~
|v_i{| i{ same as iB
|v_i}| i} same as iB
|v_o| o move cursor to other corner of area
+|v_p| p replace highlighted area with register
+ contents; deleted text in unnamed register
|v_r| r 2 replace highlighted area with a character
|v_s| s 2 delete highlighted area and start insert
|v_u| u 2 make highlighted area lowercase
@@ -1062,8 +1066,7 @@ tag command action in Command-line editing mode ~
|c_<Esc>| <Esc> abandon command-line without executing it
|c_CTRL-[| CTRL-[ same as <Esc>
|c_CTRL-\_CTRL-N| CTRL-\ CTRL-N go to Normal mode, abandon command-line
-|c_CTRL-\_CTRL-G| CTRL-\ CTRL-G go to mode specified with 'insertmode',
- abandon command-line
+|c_CTRL-\_CTRL-G| CTRL-\ CTRL-G go to Normal mode, abandon command-line
CTRL-\ a - d reserved for extensions
|c_CTRL-\_e| CTRL-\ e {expr} replace the command line with the result of
{expr}
@@ -1495,8 +1498,9 @@ tag command action ~
|:recover| :rec[over] recover a file from a swap file
|:redo| :red[o] redo one undone change
|:redir| :redi[r] redirect messages to a file or register
-|:redraw| :redr[aw] force a redraw of the display
-|:redrawstatus| :redraws[tatus] force a redraw of the status line(s)
+|:redraw| :redr[aw] force a redraw of the display
+|:redrawstatus| :redraws[tatus] force a redraw of the status line(s) and
+ window bar(s)
|:redrawtabline| :redrawt[abline] force a redraw of the tabline
|:registers| :reg[isters] display the contents of registers
|:resize| :res[ize] change current window height
@@ -1618,17 +1622,20 @@ tag command action ~
|:tjump| :tj[ump] like ":tselect", but jump directly when there
is only one match
|:tlast| :tl[ast] jump to last matching tag
-|:tmapclear| :tmapc[lear] remove all mappings for Terminal-Job mode
-|:tmap| :tma[p] like ":map" but for Terminal-Job mode
+|:tlmenu| :tlm[enu] add menu for |Terminal-mode|
+|:tlnoremenu| :tln[oremenu] like ":noremenu" but for |Terminal-mode|
+|:tlunmenu| :tlu[nmenu] remove menu for |Terminal-mode|
+|:tmapclear| :tmapc[lear] remove all mappings for |Terminal-mode|
+|:tmap| :tma[p] like ":map" but for |Terminal-mode|
|:tmenu| :tm[enu] define menu tooltip
|:tnext| :tn[ext] jump to next matching tag
-|:tnoremap| :tno[remap] like ":noremap" but for Terminal-Job mode
+|:tnoremap| :tno[remap] like ":noremap" but for |Terminal-mode|
|:topleft| :to[pleft] make split window appear at top or far left
|:tprevious| :tp[revious] jump to previous matching tag
|:trewind| :tr[ewind] jump to first matching tag
|:try| :try execute commands, abort on error or exception
|:tselect| :ts[elect] list matching tags and select one
-|:tunmap| :tunma[p] like ":unmap" but for Terminal-Job mode
+|:tunmap| :tunma[p] like ":unmap" but for |Terminal-mode|
|:tunmenu| :tu[nmenu] remove menu tooltip
|:undo| :u[ndo] undo last change(s)
|:undojoin| :undoj[oin] join next change with previous undo block
diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt
index ae2b9c4418..a16d88b4e9 100644
--- a/runtime/doc/insert.txt
+++ b/runtime/doc/insert.txt
@@ -32,9 +32,6 @@ If you are working in a special language mode when inserting text, see the
'langmap' option, |'langmap'|, on how to avoid switching this mode on and off
all the time.
-If you have 'insertmode' set, <Esc> and a few other keys get another meaning.
-See |'insertmode'|.
-
char action ~
-----------------------------------------------------------------------
*i_CTRL-[* *i_<Esc>*
@@ -192,12 +189,14 @@ CTRL-D Delete one shiftwidth of indent at the start of the current
label.
*i_CTRL-V*
-CTRL-V Insert next non-digit literally. For special keys, the
- terminal code is inserted. It's also possible to enter the
- decimal, octal or hexadecimal value of a character
+CTRL-V Insert next non-digit literally. It's also possible to enter
+ the decimal, octal or hexadecimal value of a character
|i_CTRL-V_digit|.
The characters typed right after CTRL-V are not considered for
mapping.
+ For special keys, the CTRL modifier may be included into the
+ key to produce a control character. If there is no control
+ character for the key then its |key-notation| is inserted.
Note: When CTRL-V is mapped (e.g., to paste text) you can
often use CTRL-Q instead |i_CTRL-Q|.
@@ -206,6 +205,10 @@ CTRL-Q Same as CTRL-V.
Note: Some terminal connections may eat CTRL-Q, it doesn't
work then. It does work in the GUI.
+CTRL-SHIFT-V *i_CTRL-SHIFT-V* *i_CTRL-SHIFT-Q*
+CTRL-SHIFT-Q Works just like CTRL-V, but do not try to include the CTRL
+ modifier into the key.
+
CTRL-X Enter CTRL-X mode. This is a sub-mode where commands can
be given to complete words or scroll the window. See
|i_CTRL-X| and |ins-completion|.
@@ -258,7 +261,7 @@ CTRL-] Trigger abbreviation, without inserting a character.
*i_backspacing*
The effect of the <BS>, CTRL-W, and CTRL-U depend on the 'backspace' option
-(unless 'revins' is set). This is a comma separated list of items:
+(unless 'revins' is set). This is a comma-separated list of items:
item action ~
indent allow backspacing over autoindent
@@ -269,8 +272,8 @@ start allow backspacing over the start position of insert; CTRL-W and
When 'backspace' is empty, Vi compatible backspacing is used. You cannot
backspace over autoindent, before column 1 or before where insert started.
-For backwards compatibility the values "0", "1" and "2" are also allowed, see
-|'backspace'|.
+For backwards compatibility the values "0", "1", "2" and "3" are also allowed,
+see |'backspace'|.
If the 'backspace' option does contain "eol" and the cursor is in column 1
when one of the three keys is used, the current line is joined with the
@@ -329,9 +332,8 @@ that key is interpreted as in Insert mode.
The following keys are special. They stop the current insert, do something,
and then restart insertion. This means you can do something without getting
out of Insert mode. This is very handy if you prefer to use the Insert mode
-all the time, just like editors that don't have a separate Normal mode. You
-may also want to set the 'insertmode' option. You can use CTRL-O if you want
-to map a function key to a command.
+all the time, just like editors that don't have a separate Normal mode. You
+can use CTRL-O if you want to map a function key to a command.
The changes (inserted or deleted characters) before and after these keys can
be undone separately. Only the last change can be redone and always behaves
@@ -372,7 +374,6 @@ CTRL-G CTRL-J cursor one line down, insert start column *i_CTRL-G_CTRL-J*
<S-ScrollWheelRight> move window one page right *i_<S-ScrollWheelRight>*
CTRL-O execute one command, return to Insert mode *i_CTRL-O*
CTRL-\ CTRL-O like CTRL-O but don't move the cursor *i_CTRL-\_CTRL-O*
-CTRL-L when 'insertmode' is set: go to Normal mode *i_CTRL-L*
CTRL-G u break undo sequence, start new change *i_CTRL-G_u*
CTRL-G U don't break undo with next left/right cursor *i_CTRL-G_U*
movement, if the cursor stays within the
@@ -653,8 +654,10 @@ Note: The keys that are valid in CTRL-X mode are not mapped. This allows for
ends CTRL-X mode (any key that is not a valid CTRL-X mode command) is mapped.
Also, when doing completion with 'complete' mappings apply as usual.
-Note: While completion is active Insert mode can't be used recursively.
-Mappings that somehow invoke ":normal i.." will generate an E523 error.
+ *E565*
+Note: While completion is active Insert mode can't be used recursively and
+buffer text cannot be changed. Mappings that somehow invoke ":normal i.."
+will generate an E565 error.
The following mappings are suggested to make typing the completion commands
a bit easier (although they will hide other commands): >
@@ -780,7 +783,7 @@ If the previous expansion was split, because it got longer than 'textwidth',
then just the text in the current line will be used.
If the match found is at the end of a line, then the first word in the next
-line will be inserted and the message "word from next line" displayed, if
+line will be inserted and the message "Word from other line" displayed, if
this word is accepted the next CTRL-X CTRL-P or CTRL-X CTRL-N will search
for those lines starting with this word.
@@ -798,6 +801,7 @@ CTRL-X CTRL-K Search the files given with the 'dictionary' option
the 'dictionary' option is empty.
For suggestions where to find a list of words, see the
'dictionary' option.
+ 'ignorecase', 'smartcase' and 'infercase' apply.
CTRL-K or
CTRL-N Search forward for next matching keyword. This
@@ -864,7 +868,7 @@ Groß): >
else
let res = []
let h = ''
- for l in split(system('aiksaurus '.shellescape(a:base)), '\n')
+ for l in split(system('aiksaurus ' .. shellescape(a:base)), '\n')
if l[:3] == '=== '
let h = substitute(l[4:], ' =*$', '', '')
elseif l[0] =~ '\a'
@@ -1199,7 +1203,7 @@ An example that completes the names of the months: >
" find months matching with "a:base"
let res = []
for m in split("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec")
- if m =~ '^' . a:base
+ if m =~ '^' .. a:base
call add(res, m)
endif
endfor
@@ -1221,7 +1225,7 @@ The same, but now pretending searching for matches is slow: >
else
" find months matching with "a:base"
for m in split("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec")
- if m =~ '^' . a:base
+ if m =~ '^' .. a:base
call complete_add(m)
endif
sleep 300m " simulate searching for next match
@@ -1341,11 +1345,16 @@ in 'runtimepath'. Thus for "java" it is autoload/javacomplete.vim.
C *ft-c-omni*
-Completion of C code requires a tags file. You should use Exuberant ctags,
-because it adds extra information that is needed for completion. You can find
-it here: http://ctags.sourceforge.net/ Version 5.6 or later is recommended.
+Completion of C code requires a tags file. You should use Universal/
+Exuberant ctags, because it adds extra information that is needed for
+completion. You can find it here:
+ Universal Ctags: https://ctags.io
+ Exuberant Ctags: http://ctags.sourceforge.net
-For version 5.5.4 you should add a patch that adds the "typename:" field:
+Universal Ctags is preferred, Exuberant Ctags is no longer being developed.
+
+For Exuberant ctags, version 5.6 or later is recommended. For version 5.5.4
+you should add a patch that adds the "typename:" field:
ftp://ftp.vim.org/pub/vim/unstable/patches/ctags-5.5.4.patch
A compiled .exe for MS-Windows can be found at:
http://ctags.sourceforge.net/
@@ -1466,8 +1475,11 @@ will be suggested. All other elements are not placed in suggestion list.
PHP *ft-php-omni*
Completion of PHP code requires a tags file for completion of data from
-external files and for class aware completion. You should use Exuberant ctags
-version 5.5.4 or newer. You can find it here: http://ctags.sourceforge.net/
+external files and for class aware completion. You should use Universal/
+Exuberant ctags version 5.5.4 or newer. You can find it here:
+
+ Universal Ctags: https://ctags.io
+ Exuberant Ctags: http://ctags.sourceforge.net
Script completes:
@@ -1863,14 +1875,10 @@ gi Insert text in the same position as where Insert mode
*o*
o Begin a new line below the cursor and insert text,
repeat [count] times.
- When the '#' flag is in 'cpoptions' the count is
- ignored.
*O*
O Begin a new line above the cursor and insert text,
repeat [count] times.
- When the '#' flag is in 'cpoptions' the count is
- ignored.
These commands are used to start inserting text. You can end insert mode with
<Esc>. See |mode-ins-repl| for the other special characters in Insert mode.
diff --git a/runtime/doc/intro.txt b/runtime/doc/intro.txt
index 0e0156ac6b..51e823b75f 100644
--- a/runtime/doc/intro.txt
+++ b/runtime/doc/intro.txt
@@ -120,7 +120,7 @@ Vim would never have become what it is now, without the help of these people!
Daniel Elstner GTK+ 2 port
Eric Fischer Mac port, 'cindent', and other improvements
Benji Fisher Answering lots of user questions
- Bill Foster Athena GUI port
+ Bill Foster Athena GUI port (later removed)
Google Lets me work on Vim one day a week
Loic Grenie xvim (ideas for multi windows version)
Sven Guckes Vim promoter and previous WWW page maintainer
@@ -286,10 +286,12 @@ and <> are part of what you type, the context should make this clear.
*CTRL-{char}*
CTRL-{char} {char} typed as a control character; that is, typing {char}
- while holding the CTRL key down. The case of {char} does not
- matter; thus CTRL-A and CTRL-a are equivalent. But on some
- terminals, using the SHIFT key will produce another code,
- don't use it then.
+ while holding the CTRL key down. The case of {char} is
+ ignored; thus CTRL-A and CTRL-a are equivalent. But in
+ some terminals and environments, using the SHIFT key will
+ produce a distinct code (e.g. CTRL-SHIFT-a); in these
+ environments using the SHIFT key will not trigger commands
+ such as CTRL-A.
*'option'*
'option' An option, or parameter, that can be set to a value, is
@@ -322,7 +324,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>*
@@ -384,6 +385,8 @@ Note:
combinations actually work depends on the the UI or host terminal.
- When a key is pressed using a meta or alt modifier and no mapping exists
for that keypress, Nvim behaves as though <Esc> was pressed before the key.
+- It is possible to notate combined modifiers (e.g. <C-A-T> for CTRL-ALT-T),
+ but your terminal must encode the input for that to work. |tui-input|
*<>*
Examples are often given in the <> notation. Sometimes this is just to make
@@ -421,8 +424,7 @@ Vim has seven BASIC modes:
*Normal* *Normal-mode* *command-mode*
Normal mode In Normal mode you can enter all the normal editor
commands. If you start the editor you are in this
- mode (unless you have set the 'insertmode' option,
- see below). This is also known as command mode.
+ mode. This is also known as command mode.
Visual mode This is like Normal mode, but the movement commands
extend a highlighted area. When a non-movement
@@ -548,8 +550,6 @@ Ex :vi -- -- -- -- --
*6 Go from Select mode to Insert mode by typing a printable character. The
selection is deleted and the character is inserted.
-If the 'insertmode' option is on, editing a file will start in Insert mode.
-
*CTRL-\_CTRL-N* *i_CTRL-\_CTRL-N* *c_CTRL-\_CTRL-N* *v_CTRL-\_CTRL-N*
Additionally the command CTRL-\ CTRL-N or <C-\><C-N> can be used to go to
Normal mode from any other mode. This can be used to make sure Vim is in
@@ -558,17 +558,14 @@ work in Ex mode. When used after a command that takes an argument, such as
|f| or |m|, the timeout set with 'ttimeoutlen' applies.
*CTRL-\_CTRL-G* *i_CTRL-\_CTRL-G* *c_CTRL-\_CTRL-G* *v_CTRL-\_CTRL-G*
-The command CTRL-\ CTRL-G or <C-\><C-G> can be used to go to Insert mode when
-'insertmode' is set. Otherwise it goes to Normal mode. This can be used to
-make sure Vim is in the mode indicated by 'insertmode', without knowing in
-what mode Vim currently is.
+CTRL-\ CTRL-G works the same as |CTRL-\_CTRL-N| for backward compatibility.
*gQ* *mode-Ex* *Ex-mode* *Ex* *EX* *E501*
gQ Switch to Ex mode. This is like typing ":" commands
one after another, except:
- You don't have to keep pressing ":".
- The screen doesn't get updated after each command.
- Use the ":vi" command |:visual| to exit this mode.
+ Use the `:vi` command (|:visual|) to exit this mode.
==============================================================================
Window contents *window-contents*
diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt
index bb42a87034..78100d5277 100644
--- a/runtime/doc/lsp.txt
+++ b/runtime/doc/lsp.txt
@@ -24,88 +24,81 @@ QUICKSTART *lsp-quickstart*
Nvim provides an LSP client, but the servers are provided by third parties.
Follow these steps to get LSP features:
- 1. Install the nvim-lspconfig plugin. It provides common configuration for
- various servers so you can get started quickly.
- https://github.com/neovim/nvim-lspconfig
- 2. Install a language server. A list of language servers can be found here:
+ 1. Install language servers using your package manager or by
+ following the upstream installation instruction.
+
+ A list of language servers is available at:
+
https://microsoft.github.io/language-server-protocol/implementors/servers/
- See individual server documentation for installation instructions.
- 3. Add `lua require('lspconfig').xx.setup{…}` to your init.vim, where "xx" is
- the name of the relevant config. See the nvim-lspconfig README for details.
- NOTE: Make sure to restart nvim after installing and configuring.
- 4. Check that an LSP client has attached to the current buffer: >
- :lua print(vim.inspect(vim.lsp.buf_get_clients()))
+ 2. Configure the LSP client per language server.
+ A minimal example:
+>
+ vim.lsp.start({
+ name = 'my-server-name',
+ cmd = {'name-of-language-server-executable'},
+ root_dir = vim.fs.dirname(vim.fs.find({'setup.py', 'pyproject.toml'}, { upward = true })[1]),
+ })
+<
+ See |vim.lsp.start| for details.
+
+ 3. Configure keymaps and autocmds to utilize LSP features.
+ See |lsp-config|.
<
*lsp-config*
-Inline diagnostics are enabled automatically, e.g. syntax errors will be
-annotated in the buffer. But you probably also want to use other features
-like go-to-definition, hover, etc.
-
-While Nvim does not provide an "auto-completion" framework by default, it is
-still possible to get completions from the LSP server. To incorporate these
-completions, it is recommended to use |vim.lsp.omnifunc|, which is an 'omnifunc'
-handler. When 'omnifunc' is set to `v:lua.vim.lsp.omnifunc`, |i_CTRL-X_CTRL-O|
-will provide completions from the language server.
-
-Example config (in init.vim): >
-
- lua << EOF
- local custom_lsp_attach = function(client)
- -- See `:help nvim_buf_set_keymap()` for more information
- vim.api.nvim_buf_set_keymap(0, 'n', 'K', '<cmd>lua vim.lsp.buf.hover()<CR>', {noremap = true})
- vim.api.nvim_buf_set_keymap(0, 'n', '<c-]>', '<cmd>lua vim.lsp.buf.definition()<CR>', {noremap = true})
- -- ... and other keymappings for LSP
-
- -- Use LSP as the handler for omnifunc.
- -- See `:help omnifunc` and `:help ins-completion` for more information.
- vim.api.nvim_buf_set_option(0, 'omnifunc', 'v:lua.vim.lsp.omnifunc')
-
- -- Use LSP as the handler for formatexpr.
- -- See `:help formatexpr` for more information.
- vim.api.nvim_buf_set_option(0, 'formatexpr', 'v:lua.vim.lsp.formatexpr()')
-
- -- For plugins with an `on_attach` callback, call them here. For example:
- -- require('completion').on_attach()
- end
-
- -- An example of configuring for `sumneko_lua`,
- -- a language server for Lua.
-
- -- set the path to the sumneko installation
- local system_name = "Linux" -- (Linux, macOS, or Windows)
- local sumneko_root_path = '/path/to/lua-language-server'
- local sumneko_binary = sumneko_root_path.."/bin/"..system_name.."/lua-language-server"
-
- require('lspconfig').sumneko_lua.setup({
- cmd = {sumneko_binary, "-E", sumneko_root_path .. "/main.lua"};
- -- An example of settings for an LSP server.
- -- For more options, see nvim-lspconfig
- settings = {
- Lua = {
- runtime = {
- -- Tell the language server which version of Lua you're using (most likely LuaJIT in the case of Neovim)
- version = 'LuaJIT',
- -- Setup your lua path
- path = vim.split(package.path, ';'),
- },
- diagnostics = {
- -- Get the language server to recognize the `vim` global
- globals = {'vim'},
- },
- workspace = {
- -- Make the server aware of Neovim runtime files
- library = {
- [vim.fn.expand('$VIMRUNTIME/lua')] = true,
- [vim.fn.expand('$VIMRUNTIME/lua/vim/lsp')] = true,
- },
- },
- }
- },
-
- on_attach = custom_lsp_attach
+
+Starting a LSP client will automatically report diagnostics via
+|vim.diagnostic|. Read |vim.diagnostic.config| to learn how to customize the
+display.
+
+It also sets some buffer options if the options are otherwise empty and if the
+language server supports the functionality.
+
+- |omnifunc| is set to |vim.lsp.omnifunc|. This allows to trigger completion
+ using |i_CTRL-X_CTRL-o|
+- |tagfunc| is set to |vim.lsp.tagfunc|. This enables features like
+ go-to-definition, |:tjump|, and keymaps like |CTRL-]|, |CTRL-W_]|,
+ |CTRL-W_}| to utilize the language server.
+
+To use other LSP features like hover, rename, etc. you can setup some
+additional keymaps. It's recommended to setup them in a |LspAttach| autocmd to
+ensure they're only active if there is a LSP client running. An example:
+>
+ vim.api.nvim_create_autocmd('LspAttach', {
+ callback = function(args)
+ vim.keymap.set('n', 'K', vim.lsp.buf.hover, { buffer = args.buf })
+ end,
})
- EOF
+
+<
+The most used functions are:
+
+- |vim.lsp.buf.hover()|
+- |vim.lsp.buf.format()|
+- |vim.lsp.buf.references()|
+- |vim.lsp.buf.implementation()|
+- |vim.lsp.buf.code_action()|
+
+
+Not all language servers provide the same capabilities. To ensure you only set
+keymaps if the language server supports a feature, you can guard the keymap
+calls behind capability checks:
+>
+ vim.api.nvim_create_autocmd('LspAttach', {
+ callback = function(args)
+ local client = vim.lsp.get_client_by_id(args.data.client_id)
+ if client.server_capabilities.hoverProvider then
+ vim.keymap.set('n', 'K', vim.lsp.buf.hover, { buffer = args.buf })
+ end
+ end,
+ })
+<
+
+To learn what capabilities are available you can run the following command in
+a buffer with a started LSP client:
+
+>
+ :lua =vim.lsp.get_active_clients()[1].server_capabilities
<
Full list of features provided by default can be found in |lsp-buf|.
@@ -355,8 +348,8 @@ To configure the behavior of a builtin |lsp-handler|, the convenient method
*lsp-handler-resolution*
Handlers can be set by:
-- Setting a field in |vim.lsp.handlers|. *vim.lsp.handlers*
- |vim.lsp.handlers| is a global table that contains the default mapping of
+- Setting a field in vim.lsp.handlers. *vim.lsp.handlers*
+ vim.lsp.handlers is a global table that contains the default mapping of
|lsp-method| names to |lsp-handlers|.
To override the handler for the `"textDocument/definition"` method: >
@@ -461,6 +454,39 @@ LspSignatureActiveParameter
==============================================================================
EVENTS *lsp-events*
+ *LspAttach*
+After an LSP client attaches to a buffer. The |autocmd-pattern| is the
+name of the buffer. When used from Lua, the client ID is passed to the
+callback in the "data" table. Example: >
+
+ vim.api.nvim_create_autocmd("LspAttach", {
+ callback = function(args)
+ local bufnr = args.buf
+ local client = vim.lsp.get_client_by_id(args.data.client_id)
+ if client.server_capabilities.completionProvider then
+ vim.bo[bufnr].omnifunc = "v:lua.vim.lsp.omnifunc"
+ end
+ if client.server_capabilities.definitionProvider then
+ vim.bo[bufnr].tagfunc = "v:lua.vim.lsp.tagfunc"
+ end
+ end,
+ })
+<
+ *LspDetach*
+Just before an LSP client detaches from a buffer. The |autocmd-pattern| is the
+name of the buffer. When used from Lua, the client ID is passed to the
+callback in the "data" table. Example: >
+
+ vim.api.nvim_create_autocmd("LspDetach", {
+ callback = function(args)
+ local client = vim.lsp.get_client_by_id(args.data.client_id)
+ -- Do something with the client
+ vim.cmd("setlocal tagfunc< omnifunc<")
+ end,
+ })
+<
+In addition, the following |User| |autocommands| are provided:
+
LspProgressUpdate *LspProgressUpdate*
Upon receipt of a progress notification from the server. See
|vim.lsp.util.get_progress_messages()|.
@@ -495,16 +521,8 @@ buf_detach_client({bufnr}, {client_id}) *vim.lsp.buf_detach_client()*
notification.
Parameters: ~
- {bufnr} number Buffer handle, or 0 for current
- {client_id} number Client id
-
-buf_get_clients({bufnr}) *vim.lsp.buf_get_clients()*
- Gets a map of client_id:client pairs for the given buffer,
- where each value is a |vim.lsp.client| object.
-
- Parameters: ~
- {bufnr} (optional, number): Buffer handle, or 0 for
- current
+ {bufnr} (number) Buffer handle, or 0 for current
+ {client_id} (number) Client id
buf_is_attached({bufnr}, {client_id}) *vim.lsp.buf_is_attached()*
Checks if a buffer is attached for a particular client.
@@ -563,7 +581,7 @@ buf_request_all({bufnr}, {method}, {params}, {callback})
Return: ~
(function) A function that will cancel all requests which
- is the same as the one returned from `buf_request` .
+ is the same as the one returned from `buf_request`.
*vim.lsp.buf_request_sync()*
buf_request_sync({bufnr}, {method}, {params}, {timeout_ms})
@@ -600,9 +618,9 @@ client() *vim.lsp.client*
{handler} is not specified, If one is not found there,
then an error will occur. Returns: {status},
{[client_id]}. {status} is a boolean indicating if the
- notification was successful. If it is `false` , then it
+ notification was successful. If it is `false`, then it
will always be `false` (the client has shutdown). If
- {status} is `true` , the function returns {request_id} as
+ {status} is `true`, the function returns {request_id} as
the second result. You can use this with
`client.cancel_request(request_id)` to cancel the request.
• request_sync(method, params, timeout_ms, bufnr) Sends a
@@ -612,13 +630,13 @@ client() *vim.lsp.client*
`err` and `result` come from the |lsp-handler|. On
timeout, cancel or error, returns `(nil, err)` where `err`
is a string describing the failure reason. If the request
- was unsuccessful returns `nil` .
+ was unsuccessful returns `nil`.
• notify(method, params) Sends a notification to an LSP
server. Returns: a boolean to indicate if the notification
was successful. If it is false, then it will always be
false (the client has shutdown).
• cancel_request(id) Cancels a request with a given request
- id. Returns: same as `notify()` .
+ id. Returns: same as `notify()`.
• stop([force]) Stops a client, optionally with force. By
default, it will just ask the server to shutdown without
force. If you request to stop a client which has
@@ -639,23 +657,20 @@ client() *vim.lsp.client*
interaction with the client. See |vim.lsp.rpc.start()|.
• {offset_encoding} (string): The encoding used for
communicating with the server. You can modify this in the
- `config` 's `on_init` method before text is sent to the
+ `config`'s `on_init` method before text is sent to the
server.
• {handlers} (table): The handlers used by the client as
described in |lsp-handler|.
• {requests} (table): The current pending requests in flight
to the server. Entries are key-value pairs with the key
being the request ID while the value is a table with
- `type` , `bufnr` , and `method` key-value pairs. `type` is
+ `type`, `bufnr`, and `method` key-value pairs. `type` is
either "pending" for an active request, or "cancel" for a
cancel request.
• {config} (table): copy of the table that was passed by the
user to |vim.lsp.start_client()|.
• {server_capabilities} (table): Response from the server
sent on `initialize` describing the server's capabilities.
- • {resolved_capabilities} (table): Normalized table of
- capabilities that we have detected based on the initialize
- response from the server in `server_capabilities` .
client_is_stopped({client_id}) *vim.lsp.client_is_stopped()*
Checks whether a client is stopped.
@@ -671,11 +686,11 @@ for_each_buffer_client({bufnr}, {fn})
Invokes a function for each LSP client attached to a buffer.
Parameters: ~
- {bufnr} number Buffer number
- {fn} function Function to run on each client attached
- to buffer {bufnr}. The function takes the client,
- client ID, and buffer number as arguments.
- Example: >
+ {bufnr} (number) Buffer number
+ {fn} (function) Function to run on each client
+ attached to buffer {bufnr}. The function takes
+ the client, client ID, and buffer number as
+ arguments. Example: >
vim.lsp.for_each_buffer_client(0, function(client, client_id, bufnr)
print(vim.inspect(client))
@@ -686,27 +701,42 @@ formatexpr({opts}) *vim.lsp.formatexpr()*
Provides an interface between the built-in client and a
`formatexpr` function.
- Currently only supports a single client. This can be set via `setlocal formatexpr=v:lua.vim.lsp.formatexpr()` but will typically or in `on_attach` via vim.api.nvim_buf_set_option(bufnr, 'formatexpr , 'v:lua.vim.lsp.formatexpr(#{timeout_ms:250})')`.
+ Currently only supports a single client. This can be set via
+ `setlocal formatexpr=v:lua.vim.lsp.formatexpr()` but will
+ typically or in `on_attach` via
+ `vim.api.nvim_buf_set_option(bufnr, 'formatexpr',
+ 'v:lua.vim.lsp.formatexpr(#{timeout_ms:250})')`.
Parameters: ~
- {opts} table options for customizing the formatting
+ {opts} (table) options for customizing the formatting
expression which takes the following optional
keys:
• timeout_ms (default 500ms). The timeout period
for the formatting request.
-get_active_clients() *vim.lsp.get_active_clients()*
- Gets all active clients.
+get_active_clients({filter}) *vim.lsp.get_active_clients()*
+ Get active clients.
+
+ Parameters: ~
+ {filter} (table|nil) A table with key-value pairs used to
+ filter the returned clients. The available keys
+ are:
+ • id (number): Only return clients with the
+ given id
+ • bufnr (number): Only return clients attached
+ to this buffer
+ • name (string): Only return clients with the
+ given name
Return: ~
- Table of |vim.lsp.client| objects
+ (table) List of |vim.lsp.client| objects
*vim.lsp.get_buffers_by_client_id()*
get_buffers_by_client_id({client_id})
Returns list of buffers attached to client_id.
Parameters: ~
- {client_id} number client id
+ {client_id} (number) client id
Return: ~
list of buffer ids
@@ -716,7 +746,7 @@ get_client_by_id({client_id}) *vim.lsp.get_client_by_id()*
client may not yet be fully initialized.
Parameters: ~
- {client_id} number client id
+ {client_id} (number) client id
Return: ~
|vim.lsp.client| object, or nil
@@ -749,8 +779,10 @@ 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",
+ "OFF"
+
+ Level numbers begin with "TRACE" at 0
Use `lsp.log_levels` for reverse lookup.
@@ -761,6 +793,66 @@ set_log_level({level}) *vim.lsp.set_log_level()*
See also: ~
|vim.lsp.log_levels|
+start({config}, {opts}) *vim.lsp.start()*
+ Create a new LSP client and start a language server or reuses
+ an already running client if one is found matching `name` and
+ `root_dir`. Attaches the current buffer to the client.
+
+ Example:
+>
+
+ vim.lsp.start({
+ name = 'my-server-name',
+ cmd = {'name-of-language-server-executable'},
+ root_dir = vim.fs.dirname(vim.fs.find({'pyproject.toml', 'setup.py'}, { upward = true })[1]),
+ })
+<
+
+ See |lsp.start_client| for all available options. The most
+ important are:
+
+ `name` is an arbitrary name for the LSP client. It should be
+ unique per language server.
+
+ `cmd` the command as list - used to start the language server. The
+ command must be present in the `$PATH` environment variable or an absolute path to the executable.
+ Shell constructs like `~` are NOT expanded.
+
+ `root_dir` path to the project root. By default this is used
+ to decide if an existing client should be re-used. The example
+ above uses |vim.fs.find| and |vim.fs.dirname| to detect the
+ root by traversing the file system upwards starting from the
+ current directory until either a `pyproject.toml` or
+ `setup.py` file is found.
+
+ `workspace_folders` a list of { uri:string, name: string }
+ tables. The project root folders used by the language server.
+ If `nil` the property is derived from the `root_dir` for
+ convenience.
+
+ Language servers use this information to discover metadata
+ like the dependencies of your project and they tend to index
+ the contents within the project folder.
+
+ To ensure a language server is only started for languages it
+ can handle, make sure to call |vim.lsp.start| within a
+ |FileType| autocmd. Either use |:au|, |nvim_create_autocmd()|
+ or put the call in a `ftplugin/<filetype_name>.lua` (See
+ |ftplugin-name|)
+
+ Parameters: ~
+ {config} (table) Same configuration as documented in
+ |lsp.start_client()|
+ {opts} nil|table Optional keyword arguments:
+ • reuse_client (fun(client: client, config:
+ table): boolean) Predicate used to decide if a
+ client should be re-used. Used on all running
+ clients. The default implementation re-uses a
+ client if name and root_dir matches.
+
+ Return: ~
+ (number) client_id
+
start_client({config}) *vim.lsp.start_client()*
Starts and initializes a client with the given configuration.
@@ -775,7 +867,7 @@ start_client({config}) *vim.lsp.start_client()*
initiates the LSP client.
{cmd_cwd} (string, default=|getcwd()|)
Directory to launch the `cmd`
- process. Not related to `root_dir` .
+ process. Not related to `root_dir`.
{cmd_env} (table) Environment flags to pass to
the LSP on spawn. Can be specified
using keys like a map or as a list
@@ -784,6 +876,13 @@ start_client({config}) *vim.lsp.start_client()*
{ "PRODUCTION=true"; "TEST=123"; PORT = 8080; HOST = "0.0.0.0"; }
<
+ {detached} (boolean, default true) Daemonize the
+ server process so that it runs in a
+ separate process group from Nvim.
+ Nvim will shutdown the process on
+ exit, but if Nvim fails to exit
+ cleanly this could leave behind
+ orphaned server processes.
{workspace_folders} (table) List of workspace folders
passed to the language server. For
backwards compatibility rootUri and
@@ -800,17 +899,17 @@ start_client({config}) *vim.lsp.start_client()*
its result.
• Note: To send an empty dictionary
use
- `{[vim.type_idx]=vim.types.dictionary}`
- , else it will be encoded as an
+ `{[vim.type_idx]=vim.types.dictionary}`,
+ else it will be encoded as an
array.
{handlers} Map of language server method names
to |lsp-handler|
{settings} Map with language server specific
settings. These are returned to the
language server if requested via
- `workspace/configuration` . Keys are
+ `workspace/configuration`. Keys are
case-sensitive.
- {commands} table Table that maps string of
+ {commands} (table) Table that maps string of
clientside commands to user-defined
functions. Commands passed to
start_client take precedence over the
@@ -821,7 +920,7 @@ start_client({config}) *vim.lsp.start_client()*
action, code lenses, ...) triggers
the command.
{init_options} Values to pass in the initialization
- request as `initializationOptions` .
+ request as `initializationOptions`.
See `initialize` in the LSP spec.
{name} (string, default=client-id) Name in
log messages.
@@ -890,7 +989,7 @@ start_client({config}) *vim.lsp.start_client()*
default 150): 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
@@ -898,10 +997,10 @@ start_client({config}) *vim.lsp.start_client()*
kill -15. If set to false, nvim
exits immediately after sending the
'shutdown' request to the server.
- {root_dir} string Directory where the LSP server
- will base its workspaceFolders,
- rootUri, and rootPath on
- initialization.
+ {root_dir} (string) Directory where the LSP
+ server will base its
+ workspaceFolders, rootUri, and
+ rootPath on initialization.
Return: ~
Client id. |vim.lsp.get_client_by_id()| Note: client may
@@ -925,7 +1024,7 @@ stop_client({client_id}, {force}) *vim.lsp.stop_client()*
Parameters: ~
{client_id} client id or |vim.lsp.client| object, or list
thereof
- {force} boolean (optional) shutdown forcefully
+ {force} (boolean) (optional) shutdown forcefully
tagfunc({...}) *vim.lsp.tagfunc()*
Provides an interface between the built-in client and
@@ -965,18 +1064,28 @@ add_workspace_folder({workspace_folder})
clear_references() *vim.lsp.buf.clear_references()*
Removes document highlights from current buffer.
-code_action({context}) *vim.lsp.buf.code_action()*
+code_action({options}) *vim.lsp.buf.code_action()*
Selects a code action available at the current cursor
position.
Parameters: ~
- {context} table|nil `CodeActionContext` of the LSP specification:
- • diagnostics: (table|nil) LSP`Diagnostic[]` . Inferred from the current position if not
- provided.
- • only: (string|nil) LSP `CodeActionKind` used
- to filter the code actions. Most language
- servers support values like `refactor` or
- `quickfix` .
+ {options} (table|nil) Optional table which holds the
+ following optional fields:
+ • context (table|nil): Corresponds to `CodeActionContext` of the LSP specification:
+ • diagnostics (table|nil): LSP`Diagnostic[]` . Inferred from the current position if not
+ provided.
+ • only (table|nil): List of LSP
+ `CodeActionKind`s used to filter the code
+ actions. Most language servers support
+ values like `refactor` or `quickfix`.
+
+ • filter (function|nil): Predicate function
+ taking an `CodeAction` and returning a
+ boolean.
+ • apply (boolean|nil): When set to `true`, and
+ there is just one remaining action (after
+ filtering), the action is applied without
+ user query.
See also: ~
https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction
@@ -995,20 +1104,30 @@ completion({context}) *vim.lsp.buf.completion()*
See also: ~
|vim.lsp.protocol.constants.CompletionTriggerKind|
-declaration() *vim.lsp.buf.declaration()*
+declaration({options}) *vim.lsp.buf.declaration()*
Jumps to the declaration of the symbol under the cursor.
Note:
Many servers do not implement this method. Generally, see
|vim.lsp.buf.definition()| instead.
-definition() *vim.lsp.buf.definition()*
+ Parameters: ~
+ {options} (table|nil) additional options
+ • reuse_win: (boolean) Jump to existing window
+ if buffer is already open.
+
+definition({options}) *vim.lsp.buf.definition()*
Jumps to the definition of the symbol under the cursor.
+ Parameters: ~
+ {options} (table|nil) additional options
+ • reuse_win: (boolean) Jump to existing window
+ if buffer is already open.
+
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:
+ triggered by a key mapping or by events such as `CursorHold`,
+ e.g.:
>
autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight()
autocmd CursorHoldI <buffer> lua vim.lsp.buf.document_highlight()
@@ -1024,20 +1143,58 @@ 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
+format({options}) *vim.lsp.buf.format()*
+ Formats a buffer using the attached (and optionally filtered)
+ language server clients.
+
+ Parameters: ~
+ {options} table|nil Optional table which holds the
+ following optional fields:
+ • formatting_options (table|nil): Can be used
+ to specify FormattingOptions. Some
+ unspecified options will be automatically
+ derived from the current Neovim options.
+
+ See also: ~
+ https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting
+ • timeout_ms (integer|nil, default 1000): Time in
+ milliseconds to block for formatting requests. No effect
+ if async=true
+ • bufnr (number|nil): Restrict formatting to the clients
+ attached to the given buffer, defaults to the current
+ buffer (0).
+ • filter (function|nil): Predicate used to filter clients.
+ Receives a client as argument and must return a boolean.
+ Clients matching the predicate are included. Example: • >
+
+ -- Never request typescript-language-server for formatting
+ vim.lsp.buf.format {
+ filter = function(client) return client.name ~= "tsserver" end
+ }
+<
+ • async boolean|nil If true the method won't block.
+ Defaults to false. Editing the buffer while formatting
+ asynchronous can lead to unexpected changes.
+ • id (number|nil): Restrict formatting to the client with
+ ID (client.id) matching this field.
+ • name (string|nil): Restrict formatting to the client
+ with name (client.name) matching this field.
+
formatting({options}) *vim.lsp.buf.formatting()*
Formats the current buffer.
Parameters: ~
- {options} (optional, table) Can be used to specify
+ {options} (table|nil) Can be used to specify
FormattingOptions. Some unspecified options
will be automatically derived from the current
Neovim options.
@@ -1061,15 +1218,13 @@ formatting_seq_sync({options}, {timeout_ms}, {order})
<
Parameters: ~
- {options} (optional, table) `FormattingOptions`
- entries
- {timeout_ms} (optional, number) Request timeout
- {order} (optional, table) List of client names.
- Formatting is requested from clients in the
- following order: first all clients that are
- not in the `order` list, then the remaining
- clients in the order as they occur in the
- `order` list.
+ {options} (table|nil) `FormattingOptions` entries
+ {timeout_ms} (number|nil) Request timeout
+ {order} (table|nil) List of client names. Formatting
+ is requested from clients in the following
+ order: first all clients that are not in the
+ `order` list, then the remaining clients in
+ the order as they occur in the `order` list.
*vim.lsp.buf.formatting_sync()*
formatting_sync({options}, {timeout_ms})
@@ -1084,7 +1239,8 @@ formatting_sync({options}, {timeout_ms})
<
Parameters: ~
- {options} Table with valid `FormattingOptions` entries
+ {options} (table|nil) with valid `FormattingOptions`
+ entries
{timeout_ms} (number) Request timeout
See also: ~
@@ -1117,13 +1273,13 @@ range_code_action({context}, {start_pos}, {end_pos})
Performs |vim.lsp.buf.code_action()| for a given range.
Parameters: ~
- {context} table|nil `CodeActionContext` of the LSP specification:
+ {context} (table|nil) `CodeActionContext` of the LSP specification:
• diagnostics: (table|nil) LSP`Diagnostic[]` . Inferred from the current position if not
provided.
- • only: (string|nil) LSP `CodeActionKind`
- used to filter the code actions. Most
- language servers support values like
- `refactor` or `quickfix` .
+ • only: (table|nil) List of LSP
+ `CodeActionKind`s used to filter the code
+ actions. Most language servers support
+ values like `refactor` or `quickfix`.
{start_pos} ({number, number}, optional) mark-indexed
position. Defaults to the start of the last
visual selection.
@@ -1160,13 +1316,21 @@ remove_workspace_folder({workspace_folder})
{path} is not provided, the user will be prompted for a path
using |input()|.
-rename({new_name}) *vim.lsp.buf.rename()*
+rename({new_name}, {options}) *vim.lsp.buf.rename()*
Renames all references to the symbol under the cursor.
Parameters: ~
- {new_name} (string) If not provided, the user will be
+ {new_name} (string|nil) If not provided, the user will be
prompted for a new name using
|vim.ui.input()|.
+ {options} (table|nil) additional options
+ • filter (function|nil): Predicate used to
+ filter clients. Receives a client as
+ argument and must return a boolean. Clients
+ matching the predicate are included.
+ • name (string|nil): Restrict clients used for
+ rename to ones where client.name matches
+ this field.
server_ready() *vim.lsp.buf.server_ready()*
Checks whether the language servers attached to the current
@@ -1179,10 +1343,15 @@ signature_help() *vim.lsp.buf.signature_help()*
Displays signature information about the symbol under the
cursor in a floating window.
-type_definition() *vim.lsp.buf.type_definition()*
+type_definition({options}) *vim.lsp.buf.type_definition()*
Jumps to the definition of the type of the symbol under the
cursor.
+ Parameters: ~
+ {options} (table|nil) additional options
+ • reuse_win: (boolean) Jump to existing window
+ if buffer is already open.
+
workspace_symbol({query}) *vim.lsp.buf.workspace_symbol()*
Lists all symbols in the current workspace in the quickfix
window.
@@ -1204,7 +1373,7 @@ get_namespace({client_id}) *vim.lsp.diagnostic.get_namespace()*
|vim.diagnostic|.
Parameters: ~
- {client_id} number The id of the LSP client
+ {client_id} (number) The id of the LSP client
*vim.lsp.diagnostic.on_publish_diagnostics()*
on_publish_diagnostics({_}, {result}, {ctx}, {config})
@@ -1224,8 +1393,8 @@ on_publish_diagnostics({_}, {result}, {ctx}, {config})
},
-- 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,
@@ -1234,7 +1403,7 @@ on_publish_diagnostics({_}, {result}, {ctx}, {config})
<
Parameters: ~
- {config} table Configuration table (see
+ {config} (table) Configuration table (see
|vim.diagnostic.config()|).
@@ -1245,20 +1414,20 @@ display({lenses}, {bufnr}, {client_id}) *vim.lsp.codelens.display()*
Display the lenses using virtual text
Parameters: ~
- {lenses} table of lenses to display ( `CodeLens[] |
- null` )
- {bufnr} number
- {client_id} number
+ {lenses} (table) of lenses to display (`CodeLens[] |
+ null`)
+ {bufnr} (number)
+ {client_id} (number)
get({bufnr}) *vim.lsp.codelens.get()*
Return all lenses for the given buffer
Parameters: ~
- {bufnr} number Buffer number. 0 can be used for the
+ {bufnr} (number) Buffer number. 0 can be used for the
current buffer.
Return: ~
- table ( `CodeLens[]` )
+ (table) (`CodeLens[]`)
*vim.lsp.codelens.on_codelens()*
on_codelens({err}, {result}, {ctx}, {_})
@@ -1280,10 +1449,10 @@ save({lenses}, {bufnr}, {client_id}) *vim.lsp.codelens.save()*
Store lenses for a specific buffer and client
Parameters: ~
- {lenses} table of lenses to store ( `CodeLens[] |
- null` )
- {bufnr} number
- {client_id} number
+ {lenses} (table) of lenses to store (`CodeLens[] |
+ null`)
+ {bufnr} (number)
+ {client_id} (number)
==============================================================================
@@ -1301,7 +1470,7 @@ hover({_}, {result}, {ctx}, {config}) *vim.lsp.handlers.hover()*
<
Parameters: ~
- {config} table Configuration table.
+ {config} (table) Configuration table.
• border: (default=nil)
• Add borders to the floating window
• See |nvim_open_win()|
@@ -1321,7 +1490,7 @@ signature_help({_}, {result}, {ctx}, {config})
<
Parameters: ~
- {config} table Configuration table.
+ {config} (table) Configuration table.
• border: (default=nil)
• Add borders to the floating window
• See |vim.api.nvim_open_win()|
@@ -1331,8 +1500,8 @@ 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})
- Applies a `TextDocumentEdit` , which is a list of changes to a
+apply_text_document_edit({text_document_edit}, {index}, {offset_encoding})
+ Applies a `TextDocumentEdit`, which is a list of changes to a
single document.
Parameters: ~
@@ -1349,39 +1518,37 @@ apply_text_edits({text_edits}, {bufnr}, {offset_encoding})
Applies a list of text edits to a buffer.
Parameters: ~
- {text_edits} table list of `TextEdit` objects
- {bufnr} number Buffer id
- {offset_encoding} string utf-8|utf-16|utf-32|nil defaults
- to encoding of first client of `bufnr`
+ {text_edits} (table) list of `TextEdit` objects
+ {bufnr} (number) Buffer id
+ {offset_encoding} (string) utf-8|utf-16|utf-32
See also: ~
https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textEdit
*vim.lsp.util.apply_workspace_edit()*
-apply_workspace_edit({workspace_edit})
- Applies a `WorkspaceEdit` .
+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.
Parameters: ~
- {bufnr} number Buffer id
+ {bufnr} (number) Buffer id
*vim.lsp.util.buf_highlight_references()*
buf_highlight_references({bufnr}, {references}, {offset_encoding})
Shows a list of document highlights for a certain buffer.
Parameters: ~
- {bufnr} number Buffer id
- {references} table List of `DocumentHighlight`
+ {bufnr} (number) Buffer id
+ {references} (table) List of `DocumentHighlight`
objects to highlight
- {offset_encoding} string One of "utf-8", "utf-16",
- "utf-32", or nil. Defaults to
- `offset_encoding` of first client of
- `bufnr`
+ {offset_encoding} (string) One of "utf-8", "utf-16",
+ "utf-32".
See also: ~
https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#documentHighlight
@@ -1395,9 +1562,9 @@ character_offset({buf}, {row}, {col}, {offset_encoding})
{buf} buffer id (0 for current)
{row} 0-indexed line
{col} 0-indexed byte offset in line
- {offset_encoding} string utf-8|utf-16|utf-32|nil defaults
- to `offset_encoding` of first client of
- `buf`
+ {offset_encoding} (string) utf-8|utf-16|utf-32|nil
+ defaults to `offset_encoding` of first
+ client of `buf`
Return: ~
(number, number) `offset_encoding` index of the character
@@ -1408,13 +1575,13 @@ convert_input_to_markdown_lines({input}, {contents})
Converts any of `MarkedString` | `MarkedString[]` |
`MarkupContent` into a list of lines containing valid
markdown. Useful to populate the hover window for
- `textDocument/hover` , for parsing the result of
- `textDocument/signatureHelp` , and potentially others.
+ `textDocument/hover`, for parsing the result of
+ `textDocument/signatureHelp`, and potentially others.
Parameters: ~
- {input} ( `MarkedString` | `MarkedString[]` |
- `MarkupContent` )
- {contents} (table, optional, default `{}` ) List of
+ {input} (`MarkedString` | `MarkedString[]` |
+ `MarkupContent`)
+ {contents} (table, optional, default `{}`) List of
strings to extend with converted lines
Return: ~
@@ -1458,28 +1625,32 @@ extract_completion_items({result})
https://microsoft.github.io/language-server-protocol/specification#textDocument_completion
get_effective_tabstop({bufnr}) *vim.lsp.util.get_effective_tabstop()*
- Returns visual width of tabstop.
+ Returns indentation size.
Parameters: ~
- {bufnr} (optional, number): Buffer handle, defaults to
- current
+ {bufnr} (number|nil): Buffer handle, defaults to current
Return: ~
- (number) tabstop visual width
+ (number) indentation size
See also: ~
- |softtabstop|
+ |shiftwidth|
-jump_to_location({location}) *vim.lsp.util.jump_to_location()*
+ *vim.lsp.util.jump_to_location()*
+jump_to_location({location}, {offset_encoding}, {reuse_win})
Jumps to a location.
Parameters: ~
- {location} ( `Location` | `LocationLink` )
+ {location} (table) (`Location`|`LocationLink`)
+ {offset_encoding} (string) utf-8|utf-16|utf-32 (required)
+ {reuse_win} (boolean) Jump to existing window if
+ buffer is already opened.
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 +1659,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
@@ -1521,7 +1694,7 @@ make_floating_popup_options({width}, {height}, {opts})
• border (string or table) override `border`
• focusable (string or table) override
`focusable`
- • zindex (string or table) override `zindex` ,
+ • zindex (string or table) override `zindex`,
defaults to 50
Return: ~
@@ -1533,7 +1706,8 @@ make_formatting_params({options})
buffer and cursor position.
Parameters: ~
- {options} Table with valid `FormattingOptions` entries
+ {options} (table|nil) with valid `FormattingOptions`
+ entries
Return: ~
`DocumentFormattingParams` object
@@ -1555,13 +1729,13 @@ make_given_range_params({start_pos}, {end_pos}, {bufnr}, {offset_encoding})
end of the last visual selection.
{bufnr} (optional, number): buffer handle or 0
for current, defaults to current
- {offset_encoding} string utf-8|utf-16|utf-32|nil defaults
- to `offset_encoding` of first client of
- `bufnr`
+ {offset_encoding} (string) utf-8|utf-16|utf-32|nil
+ defaults to `offset_encoding` of first
+ client of `bufnr`
Return: ~
{ textDocument = { uri = `current_file_uri` }, range = {
- start = `start_position` , end = `end_position` } }
+ start = `start_position`, end = `end_position` } }
*vim.lsp.util.make_position_params()*
make_position_params({window}, {offset_encoding})
@@ -1571,9 +1745,9 @@ make_position_params({window}, {offset_encoding})
Parameters: ~
{window} (optional, number): window handle or 0
for current, defaults to current
- {offset_encoding} string utf-8|utf-16|utf-32|nil defaults
- to `offset_encoding` of first client of
- buffer of `window`
+ {offset_encoding} (string) utf-8|utf-16|utf-32|nil
+ defaults to `offset_encoding` of first
+ client of buffer of `window`
Return: ~
`TextDocumentPositionParams` object
@@ -1585,20 +1759,20 @@ make_position_params({window}, {offset_encoding})
make_range_params({window}, {offset_encoding})
Using the current position in the current buffer, creates an
object that can be used as a building block for several LSP
- requests, such as `textDocument/codeAction` ,
- `textDocument/colorPresentation` ,
- `textDocument/rangeFormatting` .
+ requests, such as `textDocument/codeAction`,
+ `textDocument/colorPresentation`,
+ `textDocument/rangeFormatting`.
Parameters: ~
{window} (optional, number): window handle or 0
for current, defaults to current
- {offset_encoding} string utf-8|utf-16|utf-32|nil defaults
- to `offset_encoding` of first client of
- buffer of `window`
+ {offset_encoding} (string) utf-8|utf-16|utf-32|nil
+ defaults to `offset_encoding` of first
+ client of buffer of `window`
Return: ~
{ textDocument = { uri = `current_file_uri` }, range = {
- start = `current_position` , end = `current_position` } }
+ start = `current_position`, end = `current_position` } }
*vim.lsp.util.make_text_document_params()*
make_text_document_params({bufnr})
@@ -1628,15 +1802,15 @@ open_floating_preview({contents}, {syntax}, {opts})
Shows contents in a floating window.
Parameters: ~
- {contents} table of lines to show in window
- {syntax} string of syntax to set for opened buffer
- {opts} table with optional fields (additional keys
+ {contents} (table) of lines to show in window
+ {syntax} (string) of syntax to set for opened buffer
+ {opts} (table) with optional fields (additional keys
are passed on to |vim.api.nvim_open_win()|)
• height: (number) height of floating window
• width: (number) width of floating window
• wrap: (boolean, default true) wrap long
lines
- • wrap_at: (string) character to wrap at for
+ • wrap_at: (number) character to wrap at for
computing height when wrap is enabled
• max_width: (number) maximal width of
floating window
@@ -1652,8 +1826,8 @@ open_floating_preview({contents}, {syntax}, {opts})
closes the floating window
• focusable: (boolean, default true) Make
float focusable
- • focus: (boolean, default true) If `true` ,
- and if {focusable} is also `true` , focus an
+ • focus: (boolean, default true) If `true`,
+ and if {focusable} is also `true`, focus an
existing floating window with the same
{focus_id}
@@ -1665,10 +1839,10 @@ parse_snippet({input}) *vim.lsp.util.parse_snippet()*
Parses snippets in a completion entry.
Parameters: ~
- {input} string unparsed snippet
+ {input} (string) unparsed snippet
Return: ~
- string parsed snippet
+ (string) parsed snippet
preview_location({location}, {opts}) *vim.lsp.util.preview_location()*
Previews a location in a floating window
@@ -1721,7 +1895,7 @@ stylize_markdown({bufnr}, {contents}, {opts})
`open_floating_preview` instead
Parameters: ~
- {contents} table of lines to show in window
+ {contents} (table) of lines to show in window
{opts} dictionary with optional fields
• height of floating window
• width of floating window
@@ -1752,7 +1926,7 @@ text_document_completion_list_to_complete_items({result}, {prefix})
Parameters: ~
{result} The result of a `textDocument/completion` call,
e.g. from |vim.lsp.buf.completion()|, which may
- be one of `CompletionItem[]` , `CompletionList`
+ be one of `CompletionItem[]`, `CompletionList`
or `null`
{prefix} (string) the prefix to filter the completion
items
@@ -1799,14 +1973,15 @@ get_level() *vim.lsp.log.get_level()*
Gets the current log level.
Return: ~
- string current log level
+ (string) current log level
set_format_func({handle}) *vim.lsp.log.set_format_func()*
Sets formatting function used to format logs
Parameters: ~
- {handle} function function to apply to logging arguments,
- pass vim.inspect for multi-line formatting
+ {handle} (function) function to apply to logging
+ arguments, pass vim.inspect for multi-line
+ formatting
set_level({level}) *vim.lsp.log.set_level()*
Sets the current log level.
@@ -1818,7 +1993,7 @@ should_log({level}) *vim.lsp.log.should_log()*
Checks whether the level is sufficient for logging.
Parameters: ~
- {level} number log level
+ {level} (number) log level
Return: ~
(bool) true if would log, false if not
@@ -1857,8 +2032,8 @@ request({method}, {params}, {callback}, {notify_reply_callback})
{params} (table) Parameters for the
invoked LSP method
{callback} (function) Callback to invoke
- {notify_reply_callback} (function) Callback to invoke as
- soon as a request is no longer
+ {notify_reply_callback} (function|nil) Callback to invoke
+ as soon as a request is no longer
pending
Return: ~
@@ -1918,25 +2093,24 @@ start({cmd}, {cmd_args}, {dispatchers}, {extra_spawn_params})
Lua module: vim.lsp.sync *lsp-sync*
*vim.lsp.sync.compute_diff()*
-compute_diff({prev_lines}, {curr_lines}, {firstline}, {lastline},
- {new_lastline}, {offset_encoding}, {line_ending})
+compute_diff({___MissingCloseParenHere___})
Returns the range table for the difference between prev and
curr lines
Parameters: ~
- {prev_lines} table list of lines
- {curr_lines} table list of lines
- {firstline} number line to begin search for first
+ {prev_lines} (table) list of lines
+ {curr_lines} (table) list of lines
+ {firstline} (number) line to begin search for first
difference
- {lastline} number line to begin search in
+ {lastline} (number) line to begin search in
old_lines for last difference
- {new_lastline} number line to begin search in
+ {new_lastline} (number) line to begin search in
new_lines for last difference
- {offset_encoding} string encoding requested by language
+ {offset_encoding} (string) encoding requested by language
server
Return: ~
- table TextDocumentContentChangeEvent see https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#textDocumentContentChangeEvent
+ (table) TextDocumentContentChangeEvent see https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#textDocumentContentChangeEvent
==============================================================================
@@ -1953,10 +2127,10 @@ resolve_capabilities({server_capabilities})
capabilities.
Parameters: ~
- {server_capabilities} table Table of capabilities
+ {server_capabilities} (table) Table of capabilities
supported by the server
Return: ~
- table Normalized table of capabilities
+ (table) Normalized table of capabilities
vim:tw=78:ts=8:ft=help:norl:
diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt
index 80c1f58323..089cf0ce9d 100644
--- a/runtime/doc/lua.txt
+++ b/runtime/doc/lua.txt
@@ -4,65 +4,89 @@
NVIM REFERENCE MANUAL
-Lua engine *lua* *Lua*
+Lua engine *lua* *Lua*
- Type |gO| to see the table of contents.
+ Type |gO| to see the table of contents.
==============================================================================
-INTRODUCTION *lua-intro*
+INTRODUCTION *lua-intro*
-The Lua 5.1 language is builtin and always available. Try this command to get
+The Lua 5.1 language is builtin and always available. Try this command to get
an idea of what lurks beneath: >
:lua print(vim.inspect(package.loaded))
+<
+Nvim includes a "standard library" |lua-stdlib| for Lua. It complements the
+"editor stdlib" (|builtin-functions| and Ex commands) and the |API|, all of
+which can be used from Lua code. A good overview of using Lua in neovim is
+given by https://github.com/nanotee/nvim-lua-guide.
-Nvim includes a "standard library" |lua-stdlib| for Lua. It complements the
-"editor stdlib" (|functions| and Ex commands) and the |API|, all of which can
-be used from Lua code. A good overview of using Lua in neovim is given by
-https://github.com/nanotee/nvim-lua-guide.
-
-Module conflicts are resolved by "last wins". For example if both of these
-are on 'runtimepath':
- runtime/lua/foo.lua
- ~/.config/nvim/lua/foo.lua
-then `require('foo')` loads "~/.config/nvim/lua/foo.lua", and
-"runtime/lua/foo.lua" is not used. See |lua-require| to understand how Nvim
-finds and loads Lua modules. The conventions are similar to those of
-Vimscript |plugin|s, with some extra features. See |lua-require-example| for
-a walkthrough.
+The |:source| and |:runtime| commands can run Lua scripts as well as Vim
+scripts. Lua modules can be loaded with `require('name')`, which
+conventionally returns a table but can return any value.
-==============================================================================
-IMPORTING LUA MODULES *lua-require*
+See |lua-require| for details on how Nvim finds and loads Lua modules.
+See |lua-require-example| for an example of how to write and use a module.
- *lua-package-path*
-Nvim automatically adjusts `package.path` and `package.cpath` according to
-effective 'runtimepath' value. Adjustment happens whenever 'runtimepath' is
-changed. `package.path` is adjusted by simply appending `/lua/?.lua` and
+==============================================================================
+IMPORTING LUA MODULES *lua-require*
+
+Modules are searched for under the directories specified in 'runtimepath', in
+the order they appear. Any `.` in the module name is treated as a directory
+separator when searching. For a module `foo.bar`, each directory is searched
+for `lua/foo/bar.lua`, then `lua/foo/bar/init.lua`. If no files are found,
+the directories are searched again for a shared library with a name matching
+`lua/foo/bar.?`, where `?` is a list of suffixes (such as `so` or `dll`) derived from
+the initial value of `package.cpath`. If still no files are found, Nvim falls
+back to Lua's default search mechanism. The first script found is run and
+`require()` returns the value returned by the script if any, else `true`.
+
+The return value is cached after the first call to `require()` for each module,
+with subsequent calls returning the cached value without searching for, or
+executing any script. For further details on `require()`, see the Lua
+documentation at https://www.lua.org/manual/5.1/manual.html#pdf-require.
+
+For example, if 'runtimepath' is `foo,bar` and `package.cpath` was
+`./?.so;./?.dll` at startup, `require('mod')` searches these paths in order
+and loads the first module found:
+
+ foo/lua/mod.lua
+ foo/lua/mod/init.lua
+ bar/lua/mod.lua
+ bar/lua/mod/init.lua
+ foo/lua/mod.so
+ foo/lua/mod.dll
+ bar/lua/mod.so
+ bar/lua/mod.dll
+
+Nvim automatically adjusts `package.path` and `package.cpath` according to the
+effective 'runtimepath' value. Adjustment happens whenever 'runtimepath' is
+changed. `package.path` is adjusted by simply appending `/lua/?.lua` and
`/lua/?/init.lua` to each directory from 'runtimepath' (`/` is actually the
first character of `package.config`).
Similarly to `package.path`, modified directories from 'runtimepath' are also
-added to `package.cpath`. In this case, instead of appending `/lua/?.lua` and
+added to `package.cpath`. In this case, instead of appending `/lua/?.lua` and
`/lua/?/init.lua` to each runtimepath, all unique `?`-containing suffixes of
-the existing `package.cpath` are used. Example:
+the existing `package.cpath` are used. Example:
1. Given that
- 'runtimepath' contains `/foo/bar,/xxx;yyy/baz,/abc`;
- - initial (defined at compile-time or derived from
- `$LUA_CPATH`/`$LUA_INIT`) `package.cpath` contains
+ - initial (defined at compile-time or derived from
+ `$LUA_CPATH`/`$LUA_INIT`) `package.cpath` contains
`./?.so;/def/ghi/a?d/j/g.elf;/def/?.so`.
-2. It finds `?`-containing suffixes `/?.so`, `/a?d/j/g.elf` and `/?.so`, in
- order: parts of the path starting from the first path component containing
+2. It finds `?`-containing suffixes `/?.so`, `/a?d/j/g.elf` and `/?.so`, in
+ order: parts of the path starting from the first path component containing
question mark and preceding path separator.
-3. The suffix of `/def/?.so`, namely `/?.so` is not unique, as it’s the same
- as the suffix of the first path from `package.path` (i.e. `./?.so`). Which
+3. The suffix of `/def/?.so`, namely `/?.so` is not unique, as it’s the same
+ as the suffix of the first path from `package.path` (i.e. `./?.so`). Which
leaves `/?.so` and `/a?d/j/g.elf`, in this order.
-4. 'runtimepath' has three paths: `/foo/bar`, `/xxx;yyy/baz` and `/abc`. The
- second one contains semicolon which is a paths separator so it is out,
+4. 'runtimepath' has three paths: `/foo/bar`, `/xxx;yyy/baz` and `/abc`. The
+ second one contains a semicolon which is a paths separator so it is out,
leaving only `/foo/bar` and `/abc`, in order.
-5. The cartesian product of paths from 4. and suffixes from 3. is taken,
- giving four variants. In each variant `/lua` path segment is inserted
- between path and suffix, leaving
+5. The cartesian product of paths from 4. and suffixes from 3. is taken,
+ giving four variants. In each variant, a `/lua` path segment is inserted
+ between path and suffix, leaving:
- `/foo/bar/lua/?.so`
- `/foo/bar/lua/a?d/j/g.elf`
@@ -82,28 +106,28 @@ Note:
- To track 'runtimepath' updates, paths added at previous update are
remembered and removed at the next update, while all paths derived from the
- new 'runtimepath' are prepended as described above. This allows removing
+ new 'runtimepath' are prepended as described above. This allows removing
paths when path is removed from 'runtimepath', adding paths when they are
added and reordering `package.path`/`package.cpath` content if 'runtimepath'
was reordered.
- Although adjustments happen automatically, Nvim does not track current
- values of `package.path` or `package.cpath`. If you happen to delete some
+ values of `package.path` or `package.cpath`. If you happen to delete some
paths from there you can set 'runtimepath' to trigger an update: >
let &runtimepath = &runtimepath
- Skipping paths from 'runtimepath' which contain semicolons applies both to
- `package.path` and `package.cpath`. Given that there are some badly written
- plugins using shell which will not work with paths containing semicolons it
- is better to not have them in 'runtimepath' at all.
+ `package.path` and `package.cpath`. Given that there are some badly written
+ plugins using shell, which will not work with paths containing semicolons,
+ it is better to not have them in 'runtimepath' at all.
==============================================================================
-Lua Syntax Information *lua-syntax-help*
+Lua Syntax Information *lua-syntax-help*
While Lua has a simple syntax, there are a few things to understand,
particularly when looking at the documentation above.
- *lua-syntax-call-function*
+ *lua-syntax-call-function*
Lua functions can be called in multiple ways. Consider the function: >
@@ -111,31 +135,30 @@ Lua functions can be called in multiple ways. Consider the function: >
print("A is: ", a)
print("B is: ", b)
end
+<
+The first way to call this function is: >
-
-The first way to call a function is: >
-
example_func(1, 2)
-- ==== Result ====
-- A is: 1
-- B is: 2
<
- This way of calling a function is familiar to most scripting languages.
- In Lua, it's important to understand that any function arguments that are
- not supplied are automatically set to `nil`. For example: >
+This way of calling a function is familiar from most scripting languages.
+In Lua, it's important to understand that any function arguments that are
+not supplied are automatically set to `nil`. For example: >
example_func(1)
-- ==== Result ====
-- A is: 1
-- B is: nil
<
+Additionally, if any extra parameters are passed, they are discarded
+completely.
- Additionally, if any extra parameters are passed, they are discarded
- completely.
-
-In Lua, it is also possible (when only one argument is passed) to call the
-function without any parentheses. This is most often used to approximate
-"keyword"-style arguments with a single dictionary. For example: >
+In Lua, it is also possible to omit the parentheses (only) if the function
+takes a single string or table literal (`"foo"` or "`{1,2,3}`", respectively).
+The latter is most often used to approximate "keyword-style" arguments with a
+single dictionary, for example: >
local func_with_opts = function(opts)
local will_do_foo = opts.foo
@@ -146,25 +169,46 @@ function without any parentheses. This is most often used to approximate
func_with_opts { foo = true, filename = "hello.world" }
<
-
- In this style, each "parameter" is passed via keyword. It is still valid
- to call the function in this style: >
+In this style, each "parameter" is passed via keyword. It is still valid
+to call the function in the standard style: >
func_with_opts({ foo = true, filename = "hello.world" })
<
+But often in the documentation, you will see the former rather than the
+latter style due to its brevity.
+
+==============================================================================
+Lua Patterns *lua-patterns*
+
+For performance reasons, Lua does not support regular expressions natively.
+Instead, the Lua `string` standard library allows manipulations using a
+restricted set of "patterns", see https://www.lua.org/manual/5.1/manual.html#5.4.1
+
+Examples (`string.match` extracts the first match): >
+
+ print(string.match("foo123bar123", "%d+"))
+ -- -> 123
- But often in the documentation, you will see the former rather than the
- latter style, due to its brevity (this is vim after all!).
+ print(string.match("foo123bar123", "[^%d]+"))
+ -- -> foo
+ print(string.match("foo123bar123", "[abc]+"))
+ -- -> ba
+
+ print(string.match("foo.bar", "%.bar"))
+ -- -> .bar
+
+For more complex matching, Vim regular expressions can be used from Lua
+through |vim.regex()|.
------------------------------------------------------------------------------
-LUA PLUGIN EXAMPLE *lua-require-example*
+LUA PLUGIN EXAMPLE *lua-require-example*
-The following example plugin adds a command `:MakeCharBlob` which transforms
-current buffer into a long `unsigned char` array. Lua contains transformation
-function in a module `lua/charblob.lua` which is imported in
-`autoload/charblob.vim` (`require("charblob")`). Example plugin is supposed
-to be put into any directory from 'runtimepath', e.g. `~/.config/nvim` (in
+The following example plugin adds a command `:MakeCharBlob` which transforms
+current buffer into a long `unsigned char` array. Lua contains transformation
+function in a module `lua/charblob.lua` which is imported in
+`autoload/charblob.vim` (`require("charblob")`). Example plugin is supposed
+to be put into any directory from 'runtimepath', e.g. `~/.config/nvim` (in
this case `lua/charblob.lua` means `~/.config/nvim/lua/charblob.lua`).
autoload/charblob.vim: >
@@ -174,7 +218,7 @@ autoload/charblob.vim: >
\ 'require("charblob").encode(unpack(_A))',
\ [getline(1, '$'), &textwidth, ' ']))
endfunction
-
+<
plugin/charblob.vim: >
if exists('g:charblob_loaded')
@@ -183,7 +227,7 @@ plugin/charblob.vim: >
let g:charblob_loaded = 1
command MakeCharBlob :call charblob#encode_buffer()
-
+<
lua/charblob.lua: >
local function charblob_bytes_iter(lines)
@@ -233,9 +277,9 @@ lua/charblob.lua: >
bytes_iter = charblob_bytes_iter,
encode = charblob_encode,
}
-
+<
==============================================================================
-COMMANDS *lua-commands*
+COMMANDS *lua-commands*
These commands execute a Lua chunk from either the command line (:lua, :luado)
or a file (:luafile) on the given line [range]. As always in Lua, each chunk
@@ -246,10 +290,10 @@ command calls. The |lua-stdlib| modules, user modules, and anything else on
The Lua print() function redirects its output to the Nvim message area, with
arguments separated by " " (space) instead of "\t" (tab).
- *:lua*
-:[range]lua {chunk}
+ *:lua*
+:lua {chunk}
Executes Lua chunk {chunk}.
- if {chunk} starts with "=" the rest of the chunk is
+ If {chunk} starts with "=" the rest of the chunk is
evaluated as an expression and printed. `:lua =expr`
is equivalent to `:lua print(vim.inspect(expr))`
Examples: >
@@ -259,8 +303,8 @@ arguments separated by " " (space) instead of "\t" (tab).
< To see the LuaJIT version: >
:lua =jit.version
<
- *:lua-heredoc*
-:[range]lua << [endmarker]
+ *:lua-heredoc*
+:lua << [endmarker]
{script}
{endmarker}
Executes Lua script {script} from within Vimscript.
@@ -279,11 +323,11 @@ arguments separated by " " (space) instead of "\t" (tab).
linenr, #curline))
EOF
endfunction
-
-< Note that the `local` variables will disappear when
+<
+ Note that the `local` variables will disappear when
the block finishes. But not globals.
- *:luado*
+ *:luado*
:[range]luado {body} Executes Lua chunk "function(line, linenr) {body} end"
for each buffer line in [range], where `line` is the
current line text (without <EOL>), and `linenr` is the
@@ -300,9 +344,8 @@ arguments separated by " " (space) instead of "\t" (tab).
:lua bp = lpeg.P{ "(" * ((1 - lpeg.S"()") + lpeg.V(1))^0 * ")" }
:luado if bp:match(line) then return "-->\t" .. line end
<
-
- *:luafile*
-:[range]luafile {file}
+ *:luafile*
+:luafile {file}
Execute Lua script in {file}.
The whole argument is used as the filename (like
|:edit|), spaces do not need to be escaped.
@@ -314,19 +357,19 @@ arguments separated by " " (space) instead of "\t" (tab).
<
==============================================================================
-luaeval() *lua-eval* *luaeval()*
+luaeval() *lua-eval* *luaeval()*
The (dual) equivalent of "vim.eval" for passing Lua values to Nvim is
-"luaeval". "luaeval" takes an expression string and an optional argument used
-for _A inside expression and returns the result of the expression. It is
-semantically equivalent in Lua to:
->
+"luaeval". "luaeval" takes an expression string and an optional argument used
+for _A inside expression and returns the result of the expression. It is
+semantically equivalent in Lua to: >
+
local chunkheader = "local _A = select(1, ...) return "
function luaeval (expstr, arg)
local chunk = assert(loadstring(chunkheader .. expstr, "luaeval"))
return chunk(arg) -- return typval
end
-
+<
Lua nils, numbers, strings, tables and booleans are converted to their
respective Vimscript types. If a Lua string contains a NUL byte, it will be
converted to a |Blob|. Conversion of other Lua types is an error.
@@ -338,22 +381,22 @@ Example: >
42
:echo luaeval('string.match(_A, "[a-z]+")', 'XYXfoo123')
foo
-
+<
Lua tables are used as both dictionaries and lists, so it is impossible to
determine whether empty table is meant to be empty list or empty dictionary.
Additionally Lua does not have integer numbers. To distinguish between these
cases there is the following agreement:
0. Empty table is empty list.
-1. Table with N incrementally growing integral numbers, starting from 1 and
+1. Table with N incrementally growing integral numbers, starting from 1 and
ending with N is considered to be a list.
-2. Table with string keys, none of which contains NUL byte, is considered to
+2. Table with string keys, none of which contains NUL byte, is considered to
be a dictionary.
-3. Table with string keys, at least one of which contains NUL byte, is also
- considered to be a dictionary, but this time it is converted to
+3. Table with string keys, at least one of which contains NUL byte, is also
+ considered to be a dictionary, but this time it is converted to
a |msgpack-special-map|.
- *lua-special-tbl*
-4. Table with `vim.type_idx` key may be a dictionary, a list or floating-point
+ *lua-special-tbl*
+4. Table with `vim.type_idx` key may be a dictionary, a list or floating-point
value:
- `{[vim.type_idx]=vim.types.float, [vim.val_idx]=1}` is converted to
a floating-point 1.0. Note that by default integral Lua numbers are
@@ -376,14 +419,14 @@ Examples: >
: return luaeval('(_A.y-_A.x)*math.random()+_A.x', {'x':a:x,'y':a:y})
: endfunction
:echo Rand(1,10)
-
-Note: second argument to `luaeval` is converted ("marshalled") from Vimscript
+<
+Note: Second argument to `luaeval` is converted ("marshalled") from Vimscript
to Lua, so changes to Lua containers do not affect values in Vimscript. Return
value is also always converted. When converting, |msgpack-special-dict|s are
treated specially.
==============================================================================
-Vimscript v:lua interface *v:lua-call*
+Vimscript v:lua interface *v:lua-call*
From Vimscript the special `v:lua` prefix can be used to call Lua functions
which are global or accessible from global tables. The expression >
@@ -398,7 +441,7 @@ is equivalent to the Lua chunk >
In addition, functions of packages can be accessed like >
v:lua.require'mypack'.func(arg1, arg2)
v:lua.require'mypack.submod'.func(arg1, arg2)
-Note: only single quote form without parens is allowed. Using
+Note: Only single quote form without parens is allowed. Using
`require"mypack"` or `require('mypack')` as prefixes do NOT work (the latter
is still valid as a function call of itself, in case require returns a useful
value).
@@ -406,7 +449,7 @@ value).
The `v:lua` prefix may be used to call Lua functions as |method|s. For
example: >
arg1->v:lua.somemod.func(arg2)
-
+<
You can use `v:lua` in "func" options like 'tagfunc', 'omnifunc', etc.
For example consider the following Lua omnifunc handler: >
@@ -419,7 +462,7 @@ For example consider the following Lua omnifunc handler: >
end
vim.api.nvim_buf_set_option(0, 'omnifunc', 'v:lua.mymod.omnifunc')
-Note: the module ("mymod" in the above example) must either be a Lua global,
+Note: The module ("mymod" in the above example) must either be a Lua global,
or use the require syntax as specified above to access it from a package.
Note: `v:lua` without a call is not allowed in a Vimscript expression:
@@ -429,13 +472,12 @@ Note: `v:lua` without a call is not allowed in a Vimscript expression:
call SomeFunc(v:lua.mycallback) " Error
let g:foo = v:lua " Error
let g:foo = v:['lua'] " Error
-
-
+<
==============================================================================
-Lua standard modules *lua-stdlib*
+Lua standard modules *lua-stdlib*
The Nvim Lua "standard library" (stdlib) is the `vim` module, which exposes
-various functions and sub-modules. It is always loaded, thus require("vim")
+various functions and sub-modules. It is always loaded, thus `require("vim")`
is unnecessary.
You can peek at the module properties: >
@@ -466,34 +508,35 @@ Note that underscore-prefixed functions (e.g. "_os_proc_children") are
internal/private and must not be used by plugins.
------------------------------------------------------------------------------
-VIM.LOOP *lua-loop* *vim.loop*
+VIM.LOOP *lua-loop* *vim.loop*
-`vim.loop` exposes all features of the Nvim event-loop. This is a low-level
+`vim.loop` exposes all features of the Nvim event-loop. This is a low-level
API that provides functionality for networking, filesystem, and process
-management. Try this command to see available functions: >
+management. Try this command to see available functions: >
:lua print(vim.inspect(vim.loop))
-
+<
Reference: https://github.com/luvit/luv/blob/master/docs.md
Examples: https://github.com/luvit/luv/tree/master/examples
- *E5560* *lua-loop-callbacks*
+ *E5560* *lua-loop-callbacks*
It is an error to directly invoke `vim.api` functions (except |api-fast|) in
-`vim.loop` callbacks. For example, this is an error: >
+`vim.loop` callbacks. For example, this is an error: >
local timer = vim.loop.new_timer()
timer:start(1000, 0, function()
vim.api.nvim_command('echomsg "test"')
end)
-
+<
To avoid the error use |vim.schedule_wrap()| to defer the callback: >
local timer = vim.loop.new_timer()
timer:start(1000, 0, vim.schedule_wrap(function()
vim.api.nvim_command('echomsg "test"')
end))
-
-(For one-shot timers, see |vim.defer_fn()|, which automatically adds the wrapping.)
+<
+(For one-shot timers, see |vim.defer_fn()|, which automatically adds the
+wrapping.)
Example: repeating timer
1. Save this code to a file.
@@ -511,9 +554,8 @@ Example: repeating timer
i = i + 1
end)
print('sleeping');
-
-
-Example: File-change detection *watch-file*
+<
+Example: File-change detection *watch-file*
1. Save this code to a file.
2. Execute it with ":luafile %".
3. Use ":Watch %" to watch any file.
@@ -537,9 +579,8 @@ Example: File-change detection *watch-file*
end
vim.api.nvim_command(
"command! -nargs=1 Watch call luaeval('watch_file(_A)', expand('<args>'))")
-
-
-Example: TCP echo-server *tcp-server*
+<
+Example: TCP echo-server *tcp-server*
1. Save this code to a file.
2. Execute it with ":luafile %".
3. Note the port number.
@@ -567,9 +608,28 @@ Example: TCP echo-server *tcp-server*
end)
end)
print('TCP echo-server listening on port: '..server:getsockname().port)
+<
+Multithreading *lua-loop-threading*
+
+Plugins can perform work in separate (os-level) threads using the threading
+APIs in luv, for instance `vim.loop.new_thread`. Note that every thread
+gets its own separate lua interpreter state, with no access to lua globals
+in the main thread. Neither can the state of the editor (buffers, windows,
+etc) be directly accessed from threads.
+
+A subset of the `vim.*` API is available in threads. This includes:
+
+- `vim.loop` with a separate event loop per thread.
+- `vim.mpack` and `vim.json` (useful for serializing messages between threads)
+- `require` in threads can use lua packages from the global |lua-package-path|
+- `print()` and `vim.inspect`
+- `vim.diff`
+- most utility functions in `vim.*` for working with pure lua values
+ like `vim.split`, `vim.tbl_*`, `vim.list_*`, and so on.
+- `vim.is_thread()` returns true from a non-main thread.
------------------------------------------------------------------------------
-VIM.HIGHLIGHT *lua-highlight*
+VIM.HIGHLIGHT *lua-highlight*
Nvim includes a function for highlighting a selection on yank (see for example
https://github.com/machakann/vim-highlightedyank). To enable it, add
@@ -585,8 +645,7 @@ If you want to exclude visual selections from highlighting on yank, use
>
au TextYankPost * silent! lua vim.highlight.on_yank {on_visual=false}
<
-
-vim.highlight.on_yank({opts}) *vim.highlight.on_yank()*
+vim.highlight.on_yank({opts}) *vim.highlight.on_yank()*
Highlights the yanked text. The fields of the optional dict {opts}
control the highlight:
- {higroup} highlight group for yanked region (default |hl-IncSearch|)
@@ -595,28 +654,49 @@ vim.highlight.on_yank({opts}) *vim.highlight.on_yank()*
- {on_visual} highlight when yanking visual selection (default `true`)
- {event} event structure (default |v:event|)
-vim.highlight.range({bufnr}, {ns}, {higroup}, {start}, {finish}, {rtype}, {inclusive})
+vim.highlight.range({bufnr}, {ns}, {hlgroup}, {start}, {finish}, {opts})
*vim.highlight.range()*
- Highlights the range between {start} and {finish} (tuples of {line,col})
- in buffer {bufnr} with the highlight group {higroup} using the namespace
- {ns}. Optional arguments are the type of range (characterwise, linewise,
- or blockwise, see |setreg|; default to characterwise) and whether the
- range is inclusive (default false).
+
+ Apply highlight group to range of text.
+
+ Parameters: ~
+ {bufnr} buffer number
+ {ns} namespace for highlights
+ {hlgroup} highlight group name
+ {start} starting position (tuple {line,col})
+ {finish} finish position (tuple {line,col})
+ {opts} optional parameters:
+ • `regtype`: type of range (characterwise, linewise,
+ or blockwise, see |setreg|), default `'v'`
+ • `inclusive`: range includes end position,
+ default `false`
+ • `priority`: priority of highlight, default
+ `vim.highlight.user` (see below)
+
+vim.highlight.priorities *vim.highlight.priorities*
+
+ Table with default priorities used for highlighting:
+ • `syntax`: `50`, used for standard syntax highlighting
+ • `treesitter`: `100`, used for tree-sitter-based highlighting
+ • `diagnostics`: `150`, used for code analysis such as diagnostics
+ • `user`: `200`, used for user-triggered highlights such as LSP
+ document symbols or `on_yank` autocommands
------------------------------------------------------------------------------
-VIM.REGEX *lua-regex*
+VIM.REGEX *lua-regex*
Vim regexes can be used directly from lua. Currently they only allow
matching within a single line.
-vim.regex({re}) *vim.regex()*
+vim.regex({re}) *vim.regex()*
Parse the Vim regex {re} and return a regex object. Regexes are
- "magic" and case-insensitive by default, regardless of 'magic' and
- 'ignorecase'. The can be controlled with flags, see |/magic|.
+ "magic" and case-sensitive by default, regardless of 'magic' and
+ 'ignorecase'. They can be controlled with flags, see |/magic| and
+ |/ignorecase|.
Methods on the regex object:
-regex:match_str({str}) *regex:match_str()*
+regex:match_str({str}) *regex:match_str()*
Match the string against the regex. If the string should match the
regex precisely, surround the regex with `^` and `$`.
If the was a match, the byte indices for the beginning and end of
@@ -624,7 +704,7 @@ regex:match_str({str}) *regex:match_str()*
As any integer is truth-y, `regex:match()` can be directly used
as a condition in an if-statement.
-regex:match_line({bufnr}, {line_idx}[, {start}, {end}]) *regex:match_line()*
+regex:match_line({bufnr}, {line_idx}[, {start}, {end}]) *regex:match_line()*
Match line {line_idx} (zero-based) in buffer {bufnr}. If {start} and
{end} are supplied, match only this byte index range. Otherwise see
|regex:match_str()|. If {start} is used, then the returned byte
@@ -639,18 +719,19 @@ vim.diff({a}, {b}, {opts}) *vim.diff()*
1-based.
Examples: >
- vim.diff('a\n', 'b\nc\n')
- -->
- @@ -1 +1,2 @@
- -a
- +b
- +c
-
- vim.diff('a\n', 'b\nc\n', {result_type = 'indices'})
- -->
- {
- {1, 1, 1, 2}
- }
+
+ vim.diff('a\n', 'b\nc\n')
+ -->
+ @@ -1 +1,2 @@
+ -a
+ +b
+ +c
+
+ vim.diff('a\n', 'b\nc\n', {result_type = 'indices'})
+ -->
+ {
+ {1, 1, 1, 2}
+ }
<
Parameters: ~
{a} First string to compare
@@ -668,7 +749,7 @@ vim.diff({a}, {b}, {opts}) *vim.diff()*
• `result_type` (string): Form of the returned diff:
• "unified": (default) String in unified format.
• "indices": Array of hunk locations.
- Note this option is ignored if `on_hunk` is
+ Note: This option is ignored if `on_hunk` is
used.
• `algorithm` (string):
Diff algorithm to use. Values:
@@ -703,31 +784,31 @@ VIM.MPACK *lua-mpack*
The *vim.mpack* module provides encoding and decoding of Lua objects to and
from msgpack-encoded strings. Supports |vim.NIL| and |vim.empty_dict()|.
-vim.mpack.encode({obj}) *vim.mpack.encode*
+vim.mpack.encode({obj}) *vim.mpack.encode*
Encodes (or "packs") Lua object {obj} as msgpack in a Lua string.
-vim.mpack.decode({str}) *vim.mpack.decode*
+vim.mpack.decode({str}) *vim.mpack.decode*
Decodes (or "unpacks") the msgpack-encoded {str} to a Lua object.
------------------------------------------------------------------------------
-VIM.SPELL *lua-spell*
+VIM.SPELL *lua-spell*
-vim.spell.check({str}) *vim.spell.check()*
+vim.spell.check({str}) *vim.spell.check()*
Check {str} for spelling errors. Similar to the Vimscript function
|spellbadword()|.
Note: The behaviour of this function is dependent on: 'spelllang',
- 'spellfile', 'spellcapcheck' and 'spelloptions' which can all be local
- to the buffer. Consider calling this with |nvim_buf_call()|.
+ 'spellfile', 'spellcapcheck' and 'spelloptions' which can all be
+ local to the buffer. Consider calling this with |nvim_buf_call()|.
Example: >
+
vim.spell.check("the quik brown fox")
-->
{
{'quik', 'bad', 4}
}
<
-
Parameters: ~
{str} String to spell check.
@@ -742,24 +823,24 @@ vim.spell.check({str}) *vim.spell.check()*
- The position in {str} where the word begins.
------------------------------------------------------------------------------
-VIM *lua-builtin*
+VIM *lua-builtin*
-vim.api.{func}({...}) *vim.api*
+vim.api.{func}({...}) *vim.api*
Invokes Nvim |API| function {func} with arguments {...}.
Example: call the "nvim_get_current_line()" API function: >
print(tostring(vim.api.nvim_get_current_line()))
-vim.version() *vim.version*
+vim.version() *vim.version*
Gets the version of the current Nvim build.
-vim.in_fast_event() *vim.in_fast_event()*
+vim.in_fast_event() *vim.in_fast_event()*
Returns true if the code is executing as part of a "fast" event
handler, where most of the API is disabled. These are low-level events
(e.g. |lua-loop-callbacks|) which can be invoked whenever Nvim polls
- for input. When this is `false` most API functions are callable (but
+ for input. When this is `false` most API functions are callable (but
may be subject to other restrictions such as |textlock|).
-vim.NIL *vim.NIL*
+vim.NIL *vim.NIL*
Special value representing NIL in |RPC| and |v:null| in Vimscript
conversion, and similar cases. Lua `nil` cannot be used as part of
a Lua table representing a Dictionary or Array, because it is
@@ -771,10 +852,10 @@ vim.empty_dict() *vim.empty_dict()*
Vimscript or API types. Nvim by default converts an empty table `{}`
without this metatable to an list/array.
- Note: if numeric keys are present in the table, Nvim ignores the
+ Note: If numeric keys are present in the table, Nvim ignores the
metatable marker and converts the dict to a list/array anyway.
-vim.rpcnotify({channel}, {method}[, {args}...]) *vim.rpcnotify()*
+vim.rpcnotify({channel}, {method}[, {args}...]) *vim.rpcnotify()*
Sends {event} to {channel} via |RPC| and returns immediately. If
{channel} is 0, the event is broadcast to all channels.
@@ -787,12 +868,12 @@ vim.rpcrequest({channel}, {method}[, {args}...]) *vim.rpcrequest()*
Note: NIL values as part of the return value is represented as
|vim.NIL| special value
-vim.stricmp({a}, {b}) *vim.stricmp()*
- Compares strings case-insensitively. Returns 0, 1 or -1 if strings
+vim.stricmp({a}, {b}) *vim.stricmp()*
+ Compares strings case-insensitively. Returns 0, 1 or -1 if strings
are equal, {a} is greater than {b} or {a} is lesser than {b},
respectively.
-vim.str_utfindex({str}[, {index}]) *vim.str_utfindex()*
+vim.str_utfindex({str}[, {index}]) *vim.str_utfindex()*
Convert byte index to UTF-32 and UTF-16 indices. If {index} is not
supplied, the length of the string is used. All indices are zero-based.
Returns two values: the UTF-32 and UTF-16 indices respectively.
@@ -802,21 +883,21 @@ vim.str_utfindex({str}[, {index}]) *vim.str_utfindex()*
point each. An {index} in the middle of a UTF-8 sequence is rounded
upwards to the end of that sequence.
-vim.str_byteindex({str}, {index}[, {use_utf16}]) *vim.str_byteindex()*
+vim.str_byteindex({str}, {index}[, {use_utf16}]) *vim.str_byteindex()*
Convert UTF-32 or UTF-16 {index} to byte index. If {use_utf16} is not
supplied, it defaults to false (use UTF-32). Returns the byte index.
- Invalid UTF-8 and NUL is treated like by |vim.str_byteindex()|. An {index}
- in the middle of a UTF-16 sequence is rounded upwards to the end of that
- sequence.
+ Invalid UTF-8 and NUL is treated like by |vim.str_byteindex()|.
+ An {index} in the middle of a UTF-16 sequence is rounded upwards to
+ the end of that sequence.
-vim.schedule({callback}) *vim.schedule()*
+vim.schedule({callback}) *vim.schedule()*
Schedules {callback} to be invoked soon by the main event-loop. Useful
to avoid |textlock| or other temporary restrictions.
-vim.defer_fn({fn}, {timeout}) *vim.defer_fn*
- Defers calling {fn} until {timeout} ms passes. Use to do a one-shot timer
+vim.defer_fn({fn}, {timeout}) *vim.defer_fn*
+ Defers calling {fn} until {timeout} ms passes. Use to do a one-shot timer
that calls {fn}.
Note: The {fn} is |schedule_wrap|ped automatically, so API functions are
@@ -880,15 +961,15 @@ vim.wait({time} [, {callback}, {interval}, {fast_only}]) *vim.wait()*
end
<
-vim.type_idx *vim.type_idx*
- Type index for use in |lua-special-tbl|. Specifying one of the values
+vim.type_idx *vim.type_idx*
+ Type index for use in |lua-special-tbl|. Specifying one of the values
from |vim.types| allows typing the empty table (it is unclear whether
empty Lua table represents empty list or empty array) and forcing
- integral numbers to be |Float|. See |lua-special-tbl| for more
+ integral numbers to be |Float|. See |lua-special-tbl| for more
details.
-vim.val_idx *vim.val_idx*
- Value index for tables representing |Float|s. A table representing
+vim.val_idx *vim.val_idx*
+ Value index for tables representing |Float|s. A table representing
floating-point value 1.0 looks like this: >
{
[vim.type_idx] = vim.types.float,
@@ -896,17 +977,17 @@ vim.val_idx *vim.val_idx*
}
< See also |vim.type_idx| and |lua-special-tbl|.
-vim.types *vim.types*
- Table with possible values for |vim.type_idx|. Contains two sets of
+vim.types *vim.types*
+ Table with possible values for |vim.type_idx|. Contains two sets of
key-value pairs: first maps possible values for |vim.type_idx| to
human-readable strings, second maps human-readable type names to
- values for |vim.type_idx|. Currently contains pairs for `float`,
+ values for |vim.type_idx|. Currently contains pairs for `float`,
`array` and `dictionary` types.
- Note: one must expect that values corresponding to `vim.types.float`,
+ Note: One must expect that values corresponding to `vim.types.float`,
`vim.types.array` and `vim.types.dictionary` fall under only two
following assumptions:
- 1. Value may serve both as a key and as a value in a table. Given the
+ 1. Value may serve both as a key and as a value in a table. Given the
properties of Lua tables this basically means “value is not `nil`”.
2. For each value in `vim.types` table `vim.types[vim.types[value]]`
is the same as `value`.
@@ -923,35 +1004,25 @@ Log levels are one of the values defined in `vim.log.levels`:
vim.log.levels.INFO
vim.log.levels.TRACE
vim.log.levels.WARN
+ vim.log.levels.OFF
------------------------------------------------------------------------------
-LUA-VIMSCRIPT BRIDGE *lua-vimscript*
+LUA-VIMSCRIPT BRIDGE *lua-vimscript*
Nvim Lua provides an interface to Vimscript variables and functions, and
editor commands and options.
See also https://github.com/nanotee/nvim-lua-guide.
-vim.call({func}, {...}) *vim.call()*
+vim.call({func}, {...}) *vim.call()*
Invokes |vim-function| or |user-function| {func} with arguments {...}.
See also |vim.fn|.
Equivalent to: >
vim.fn[func]({...})
-vim.cmd({cmd}) *vim.cmd()*
- Executes multiple lines of Vimscript at once. It is an alias to
- |nvim_exec()|, where `output` is set to false. Thus it works identical
- to |:source|.
- See also |ex-cmd-index|.
- Example: >
- vim.cmd('echo 42')
- vim.cmd([[
- augroup My_group
- autocmd!
- autocmd FileType c setlocal cindent
- augroup END
- ]])
-
-vim.fn.{func}({...}) *vim.fn*
+vim.cmd({command})
+ See |vim.cmd()|.
+
+vim.fn.{func}({...}) *vim.fn*
Invokes |vim-function| or |user-function| {func} with arguments {...}.
To call autoload functions, use the syntax: >
vim.fn['some#function']({...})
@@ -967,8 +1038,10 @@ vim.fn.{func}({...}) *vim.fn*
Note: vim.fn keys are generated lazily, thus `pairs(vim.fn)` only
enumerates functions that were called at least once.
+ Note: The majority of functions cannot run in |api-fast| callbacks with some
+ undocumented exceptions which are allowed.
- *lua-vim-variables*
+ *lua-vim-variables*
The Vim editor global dictionaries |g:| |w:| |b:| |t:| |v:| can be accessed
from Lua conveniently and idiomatically by referencing the `vim.*` Lua tables
described below. In this way you can easily read and modify global Vimscript
@@ -980,31 +1053,31 @@ Example: >
print(vim.g.foo) -- Get and print the g:foo Vimscript variable.
vim.g.foo = nil -- Delete (:unlet) the Vimscript variable.
vim.b[2].foo = 6 -- Set b:foo for buffer 2
-
-vim.g *vim.g*
+<
+vim.g *vim.g*
Global (|g:|) editor variables.
Key with no value returns `nil`.
-vim.b *vim.b*
+vim.b *vim.b*
Buffer-scoped (|b:|) variables for the current buffer.
Invalid or unset key returns `nil`. Can be indexed with
an integer to access variables for a specific buffer.
-vim.w *vim.w*
+vim.w *vim.w*
Window-scoped (|w:|) variables for the current window.
Invalid or unset key returns `nil`. Can be indexed with
an integer to access variables for a specific window.
-vim.t *vim.t*
+vim.t *vim.t*
Tabpage-scoped (|t:|) variables for the current tabpage.
Invalid or unset key returns `nil`. Can be indexed with
an integer to access variables for a specific tabpage.
-vim.v *vim.v*
+vim.v *vim.v*
|v:| variables.
Invalid or unset key returns `nil`.
-vim.env *vim.env*
+vim.env *vim.env*
Environment variables defined in the editor session.
See |expand-env| and |:let-environment| for the Vimscript behavior.
Invalid or unset key returns `nil`.
@@ -1019,7 +1092,7 @@ vim.env *vim.env*
*lua-vim-optlocal*
*lua-vim-setlocal*
-In Vimscript, there is an way to set options |set-option|. In Lua, the
+In Vimscript, there is a way to set options |set-option|. In Lua, the
corresponding method is `vim.opt`.
`vim.opt` provides several conveniences for setting and controlling options
@@ -1043,7 +1116,7 @@ from within Lua.
`vim.opt.wildignore = '*.o,*.a,__pycache__'`
However, vim.opt also supports a more elegent way of setting
- list-style options, but using lua tables:
+ list-style options by using lua tables:
`vim.opt.wildignore = { '*.o', '*.a', '__pycache__' }`
To replicate the behavior of |:set+=|, use: >
@@ -1054,7 +1127,6 @@ from within Lua.
-- or using the `:append(...)` method
vim.opt.wildignore:append { "*.pyc", "node_modules" }
<
-
To replicate the behavior of |:set^=|, use: >
-- vim.opt supports prepending options via the "^" operator
@@ -1086,7 +1158,7 @@ In any of the above examples, to replicate the behavior |setlocal|, use
|vim.opt| returns an Option object.
-For example: `local listchar_object = vim.opt.listchar`
+For example: `local listchar_object = vim.opt.listchars`
An `Option` has the following methods:
@@ -1175,14 +1247,13 @@ vim.o :set set set
vim.bo/vim.wo :setlocal - set
vim.go :setglobal set -
-vim.o *vim.o*
+vim.o *vim.o*
Get or set editor options, like |:set|. Invalid key is an error.
Example: >
vim.o.cmdheight = 4
print(vim.o.columns)
-
-
-vim.go *vim.go*
+<
+vim.go *vim.go*
Get or set an |option|. Invalid key is an error.
This is a wrapper around |nvim_set_option()| and |nvim_get_option()|.
@@ -1194,8 +1265,7 @@ vim.go *vim.go*
Example: >
vim.go.cmdheight = 4
<
-
-vim.bo *vim.bo*
+vim.bo *vim.bo*
Get or set buffer-scoped |local-options|. Invalid key is an error.
This is a wrapper around |nvim_buf_set_option()| and
@@ -1204,8 +1274,8 @@ vim.bo *vim.bo*
Example: >
vim.bo.buflisted = true
print(vim.bo.comments)
-
-vim.wo *vim.wo*
+<
+vim.wo *vim.wo*
Get or set window-scoped |local-options|. Invalid key is an error.
This is a wrapper around |nvim_win_set_option()| and
@@ -1214,11 +1284,42 @@ vim.wo *vim.wo*
Example: >
vim.wo.cursorcolumn = true
print(vim.wo.foldmarker)
-
-
+<
==============================================================================
Lua module: vim *lua-vim*
+cmd({command}) *vim.cmd()*
+ Execute Vim script commands.
+
+ Example: >
+
+ vim.cmd('echo 42')
+ vim.cmd([[
+ augroup My_group
+ autocmd!
+ autocmd FileType c setlocal cindent
+ augroup END
+ ]])
+ vim.cmd({ cmd = 'echo', args = { '"foo"' } })
+<
+
+ Parameters: ~
+ {command} string|table Command(s) to execute. If a
+ string, executes multiple lines of Vim script
+ at once. In this case, it is an alias to
+ |nvim_exec()|, where `output` is set to false.
+ Thus it works identical to |:source|. If a
+ table, executes a single command. In this case,
+ it is an alias to |nvim_cmd()| where `opts` is
+ empty.
+
+ See also: ~
+ |ex-cmd-index|
+
+ *vim.connection_failure_errmsg()*
+connection_failure_errmsg({consequence})
+ TODO: Documentation
+
defer_fn({fn}, {timeout}) *vim.defer_fn()*
Defers calling `fn` until `timeout` ms passes.
@@ -1233,6 +1334,21 @@ defer_fn({fn}, {timeout}) *vim.defer_fn()*
Return: ~
timer luv timer object
+ *vim.deprecate()*
+deprecate({name}, {alternative}, {version}, {plugin}, {backtrace})
+ Display a deprecation notification to the user.
+
+ Parameters: ~
+ {name} string Deprecated function.
+ {alternative} (string|nil) Preferred alternative
+ function.
+ {version} string Version in which the deprecated
+ function will be removed.
+ {plugin} string|nil Plugin name that the function
+ will be removed from. Defaults to "Nvim".
+ {backtrace} boolean|nil Prints backtrace. Defaults to
+ true.
+
inspect({object}, {options}) *vim.inspect()*
Return a human-readable representation of the given object.
@@ -1248,11 +1364,12 @@ notify({msg}, {level}, {opts}) *vim.notify()*
notification provider). By default, writes to |:messages|.
Parameters: ~
- {msg} string Content of the notification to show to the
- user.
- {level} number|nil One of the values from
+ {msg} (string) Content of the notification to show to
+ the user.
+ {level} (number|nil) One of the values from
|vim.log.levels|.
- {opts} table|nil Optional parameters. Unused by default.
+ {opts} (table|nil) Optional parameters. Unused by
+ default.
notify_once({msg}, {level}, {opts}) *vim.notify_once()*
Display a notification only one time.
@@ -1261,11 +1378,15 @@ notify_once({msg}, {level}, {opts}) *vim.notify_once()*
message will not display a notification.
Parameters: ~
- {msg} string Content of the notification to show to the
- user.
- {level} number|nil One of the values from
+ {msg} (string) Content of the notification to show to
+ the user.
+ {level} (number|nil) One of the values from
|vim.log.levels|.
- {opts} table|nil Optional parameters. Unused by default.
+ {opts} (table|nil) Optional parameters. Unused by
+ default.
+
+ Return: ~
+ (boolean) true if message was displayed, else false
on_key({fn}, {ns_id}) *vim.on_key()*
Adds Lua function {fn} with namespace id {ns_id} as a listener
@@ -1291,8 +1412,8 @@ on_key({fn}, {ns_id}) *vim.on_key()*
returns a new |nvim_create_namespace()| id.
Return: ~
- number Namespace id associated with {fn}. Or count of all
- callbacks if on_key() is called without arguments.
+ (number) Namespace id associated with {fn}. Or count of
+ all callbacks if on_key() is called without arguments.
Note:
{fn} will be removed if an error occurs while calling.
@@ -1346,12 +1467,12 @@ region({bufnr}, {pos1}, {pos2}, {regtype}, {inclusive}) *vim.region()*
marked by two points
Parameters: ~
- {bufnr} number of buffer
+ {bufnr} (number) of buffer
{pos1} (line, column) tuple marking beginning of
region
{pos2} (line, column) tuple marking end of region
{regtype} type of selection (:help setreg)
- {inclusive} boolean indicating whether the selection is
+ {inclusive} (boolean) indicating whether the selection is
end-inclusive
Return: ~
@@ -1371,14 +1492,14 @@ schedule_wrap({cb}) *vim.schedule_wrap()*
deep_equal({a}, {b}) *vim.deep_equal()*
Deep compare values for equality
- Tables are compared recursively unless they both provide the `eq` methamethod. All other types are compared using the equality `==` operator.
+ Tables are compared recursively unless they both provide the `eq` metamethod. All other types are compared using the equality `==` operator.
Parameters: ~
- {a} first value
- {b} second value
+ {a} any First value
+ {b} any Second value
Return: ~
- `true` if values are equals, else `false` .
+ (boolean) `true` if values are equals, else `false`
deepcopy({orig}) *vim.deepcopy()*
Returns a deep copy of the given object. Non-table objects are
@@ -1389,32 +1510,32 @@ deepcopy({orig}) *vim.deepcopy()*
and will throw an error.
Parameters: ~
- {orig} table Table to copy
+ {orig} (table) Table to copy
Return: ~
- New table of copied keys and (nested) values.
+ (table) Table of copied keys and (nested) values.
endswith({s}, {suffix}) *vim.endswith()*
- Tests if `s` ends with `suffix` .
+ Tests if `s` ends with `suffix`.
Parameters: ~
- {s} (string) a string
- {suffix} (string) a suffix
+ {s} (string) String
+ {suffix} (string) Suffix to match
Return: ~
- (boolean) true if `suffix` is a suffix of s
+ (boolean) `true` if `suffix` is a suffix of `s`
gsplit({s}, {sep}, {plain}) *vim.gsplit()*
Splits a string at each instance of a separator.
Parameters: ~
- {s} String to split
- {sep} Separator string or pattern
- {plain} If `true` use `sep` literally (passed to
- String.find)
+ {s} (string) String to split
+ {sep} (string) Separator or pattern
+ {plain} (boolean) If `true` use `sep` literally (passed
+ to string.find)
Return: ~
- Iterator over the split components
+ (function) Iterator over the split components
See also: ~
|vim.split()|
@@ -1425,10 +1546,10 @@ is_callable({f}) *vim.is_callable()*
Returns true if object `f` can be called as a function.
Parameters: ~
- {f} Any object
+ {f} any Any object
Return: ~
- true if `f` is callable, else false
+ (boolean) `true` if `f` is callable, else `false`
list_extend({dst}, {src}, {start}, {finish}) *vim.list_extend()*
Extends a list-like table with the values of another list-like
@@ -1437,13 +1558,14 @@ list_extend({dst}, {src}, {start}, {finish}) *vim.list_extend()*
NOTE: This mutates dst!
Parameters: ~
- {dst} list which will be modified and appended to.
- {src} list from which values will be inserted.
- {start} Start index on src. defaults to 1
- {finish} Final index on src. defaults to #src
+ {dst} (table) List which will be modified and appended
+ to
+ {src} (table) List from which values will be inserted
+ {start} (number) Start index on src. Defaults to 1
+ {finish} (number) Final index on src. Defaults to `#src`
Return: ~
- dst
+ (table) dst
See also: ~
|vim.tbl_extend()|
@@ -1453,21 +1575,22 @@ list_slice({list}, {start}, {finish}) *vim.list_slice()*
to end (inclusive)
Parameters: ~
- {list} table table
- {start} integer Start range of slice
- {finish} integer End range of slice
+ {list} (table) Table
+ {start} (number) Start range of slice
+ {finish} (number) End range of slice
Return: ~
- Copy of table sliced from start to finish (inclusive)
+ (table) Copy of table sliced from start to finish
+ (inclusive)
pesc({s}) *vim.pesc()*
Escapes magic chars in a Lua pattern.
Parameters: ~
- {s} String to escape
+ {s} (string) String to escape
Return: ~
- %-escaped pattern string
+ (string) %-escaped pattern string
See also: ~
https://github.com/rxi/lume
@@ -1484,49 +1607,55 @@ split({s}, {sep}, {kwargs}) *vim.split()*
<
Parameters: ~
- {s} String to split
- {sep} Separator string or pattern
- {kwargs} Keyword arguments:
+ {s} (string) String to split
+ {sep} (string) Separator or pattern
+ {kwargs} (table) Keyword arguments:
• plain: (boolean) If `true` use `sep` literally
(passed to string.find)
• trimempty: (boolean) If `true` remove empty
items from the front and back of the list
Return: ~
- List-like table of the split components.
+ (table) List of split components
See also: ~
|vim.gsplit()|
startswith({s}, {prefix}) *vim.startswith()*
- Tests if `s` starts with `prefix` .
+ Tests if `s` starts with `prefix`.
Parameters: ~
- {s} (string) a string
- {prefix} (string) a prefix
+ {s} (string) String
+ {prefix} (string) Prefix to match
Return: ~
- (boolean) true if `prefix` is a prefix of s
+ (boolean) `true` if `prefix` is a prefix of `s`
tbl_add_reverse_lookup({o}) *vim.tbl_add_reverse_lookup()*
Add the reverse lookup values to an existing table. For
- example: tbl_add_reverse_lookup { A = 1 } == { [1] = 'A , A = 1 }`
+ example: `tbl_add_reverse_lookup { A = 1 } == { [1] = 'A', A =
+ 1 }`
+
+ Note that this modifies the input.
Parameters: ~
- {o} table The table to add the reverse to.
+ {o} (table) Table to add the reverse to
+
+ Return: ~
+ (table) o
tbl_contains({t}, {value}) *vim.tbl_contains()*
- Checks if a list-like (vector) table contains `value` .
+ Checks if a list-like (vector) table contains `value`.
Parameters: ~
- {t} Table to check
- {value} Value to compare
+ {t} (table) Table to check
+ {value} any Value to compare
Return: ~
- true if `t` contains `value`
+ (boolean) `true` if `t` contains `value`
tbl_count({t}) *vim.tbl_count()*
- Counts the number of non-nil values in table `t` .
+ Counts the number of non-nil values in table `t`.
>
vim.tbl_count({ a=1, b=2 }) => 2
@@ -1534,10 +1663,10 @@ tbl_count({t}) *vim.tbl_count()*
<
Parameters: ~
- {t} Table
+ {t} (table) Table
Return: ~
- Number that is the number of the value in table
+ (number) Number of non-nil values in table
See also: ~
https://github.com/Tieske/Penlight/blob/master/lua/pl/tablex.lua
@@ -1546,12 +1675,15 @@ tbl_deep_extend({behavior}, {...}) *vim.tbl_deep_extend()*
Merges recursively two or more map-like tables.
Parameters: ~
- {behavior} Decides what to do if a key is found in more
- than one map:
+ {behavior} (string) Decides what to do if a key is found
+ in more than one map:
• "error": raise an error
• "keep": use value from the leftmost map
• "force": use value from the rightmost map
- {...} Two or more map-like tables.
+ {...} (table) Two or more map-like tables
+
+ Return: ~
+ (table) Merged table
See also: ~
|tbl_extend()|
@@ -1560,12 +1692,15 @@ tbl_extend({behavior}, {...}) *vim.tbl_extend()*
Merges two or more map-like tables.
Parameters: ~
- {behavior} Decides what to do if a key is found in more
- than one map:
+ {behavior} (string) Decides what to do if a key is found
+ in more than one map:
• "error": raise an error
• "keep": use value from the leftmost map
• "force": use value from the rightmost map
- {...} Two or more map-like tables.
+ {...} (table) Two or more map-like tables
+
+ Return: ~
+ (table) Merged table
See also: ~
|extend()|
@@ -1574,27 +1709,51 @@ tbl_filter({func}, {t}) *vim.tbl_filter()*
Filter a table using a predicate function
Parameters: ~
- {func} function or callable table
- {t} table
+ {func} function|table Function or callable table
+ {t} (table) Table
+
+ Return: ~
+ (table) Table of filtered values
tbl_flatten({t}) *vim.tbl_flatten()*
Creates a copy of a list-like table such that any nested
tables are "unrolled" and appended to the result.
Parameters: ~
- {t} List-like table
+ {t} (table) List-like table
Return: ~
- Flattened copy of the given list-like table.
+ (table) Flattened copy of the given list-like table
See also: ~
From https://github.com/premake/premake-core/blob/master/src/base/table.lua
+tbl_get({o}, {...}) *vim.tbl_get()*
+ Index into a table (first argument) via string keys passed as
+ subsequent arguments. Return `nil` if the key does not exist.
+
+ Examples: >
+
+ vim.tbl_get({ key = { nested_key = true }}, 'key', 'nested_key') == true
+ vim.tbl_get({ key = {}}, 'key', 'nested_key') == nil
+<
+
+ Parameters: ~
+ {o} (table) Table to index
+ {...} (string) Optional strings (0 or more, variadic) via
+ which to index the table
+
+ Return: ~
+ any Nested value indexed by key (if it exists), else nil
+
tbl_isempty({t}) *vim.tbl_isempty()*
Checks if a table is empty.
Parameters: ~
- {t} Table to check
+ {t} (table) Table to check
+
+ Return: ~
+ (boolean) `true` if `t` is empty
See also: ~
https://github.com/premake/premake-core/blob/master/src/base/table.lua
@@ -1608,20 +1767,20 @@ tbl_islist({t}) *vim.tbl_islist()*
|vim.fn|.
Parameters: ~
- {t} Table
+ {t} (table) Table
Return: ~
- `true` if array-like table, else `false` .
+ (boolean) `true` if array-like table, else `false`
tbl_keys({t}) *vim.tbl_keys()*
Return a list of all keys used in a table. However, the order
of the return table of keys is not guaranteed.
Parameters: ~
- {t} Table
+ {t} (table) Table
Return: ~
- list of keys
+ (table) List of keys
See also: ~
From https://github.com/premake/premake-core/blob/master/src/base/table.lua
@@ -1630,28 +1789,32 @@ tbl_map({func}, {t}) *vim.tbl_map()*
Apply a function to all values of a table.
Parameters: ~
- {func} function or callable table
- {t} table
+ {func} function|table Function or callable table
+ {t} (table) Table
+
+ Return: ~
+ (table) Table of transformed values
tbl_values({t}) *vim.tbl_values()*
Return a list of all values used in a table. However, the
order of the return table of values is not guaranteed.
Parameters: ~
- {t} Table
+ {t} (table) Table
Return: ~
- list of values
+ (table) List of values
trim({s}) *vim.trim()*
Trim whitespace (Lua pattern "%s") from both sides of a
string.
Parameters: ~
- {s} String to trim
+ {s} (string) String to trim
Return: ~
- String with whitespace removed from its beginning and end
+ (string) String with whitespace removed from its beginning
+ and end
See also: ~
https://www.lua.org/pil/20.2.html
@@ -1693,7 +1856,7 @@ validate({opt}) *vim.validate()*
<
Parameters: ~
- {opt} table of parameter names to validations. Each key
+ {opt} (table) Names of parameters to validate. Each key
is a parameter name; each value is a tuple in one
of these forms:
1. (arg_value, type_name, optional)
@@ -1723,38 +1886,38 @@ uri_from_bufnr({bufnr}) *vim.uri_from_bufnr()*
Get a URI from a bufnr
Parameters: ~
- {bufnr} number
+ {bufnr} (number)
Return: ~
- string URI
+ (string) URI
uri_from_fname({path}) *vim.uri_from_fname()*
Get a URI from a file path.
Parameters: ~
- {path} string Path to file
+ {path} (string) Path to file
Return: ~
- string URI
+ (string) URI
uri_to_bufnr({uri}) *vim.uri_to_bufnr()*
Get the buffer for a uri. Creates a new unloaded buffer if no
buffer for the uri already exists.
Parameters: ~
- {uri} string
+ {uri} (string)
Return: ~
- number bufnr
+ (number) bufnr
uri_to_fname({uri}) *vim.uri_to_fname()*
Get a filename from a URI
Parameters: ~
- {uri} string
+ {uri} (string)
Return: ~
- string filename or unchanged URI for non-file URIs
+ (string) filename or unchanged URI for non-file URIs
==============================================================================
@@ -1763,10 +1926,16 @@ Lua module: ui *lua-ui*
input({opts}, {on_confirm}) *vim.ui.input()*
Prompts the user for input
+ Example: >
+
+ vim.ui.input({ prompt = 'Enter value for shiftwidth: ' }, function(input)
+ vim.o.shiftwidth = tonumber(input)
+ end)
+<
+
Parameters: ~
- {opts} table Additional options. See |input()|
- • prompt (string|nil) Text of the prompt.
- Defaults to `Input:` .
+ {opts} (table) Additional options. See |input()|
+ • prompt (string|nil) Text of the prompt
• default (string|nil) Default reply to the
input
• completion (string|nil) Specifies type of
@@ -1777,32 +1946,48 @@ input({opts}, {on_confirm}) *vim.ui.input()*
|:command-completion|
• highlight (function) Function that will be
used for highlighting user inputs.
- {on_confirm} function ((input|nil) -> ()) Called once the
- user confirms or abort the input. `input` is
- what the user typed. `nil` if the user
- aborted the dialog.
+ {on_confirm} (function) ((input|nil) -> ()) Called once
+ the user confirms or abort the input.
+ `input` is what the user typed. `nil` if the
+ user aborted the dialog.
select({items}, {opts}, {on_choice}) *vim.ui.select()*
Prompts the user to pick a single item from a collection of
entries
+ Example: >
+
+ vim.ui.select({ 'tabs', 'spaces' }, {
+ prompt = 'Select tabs or spaces:',
+ format_item = function(item)
+ return "I'd like to choose " .. item
+ end,
+ }, function(choice)
+ if choice == 'spaces' then
+ vim.o.expandtab = true
+ else
+ vim.o.expandtab = false
+ end
+ end)
+<
+
Parameters: ~
- {items} table Arbitrary items
- {opts} table Additional options
+ {items} (table) Arbitrary items
+ {opts} (table) Additional options
• prompt (string|nil) Text of the prompt.
Defaults to `Select one of:`
• format_item (function item -> text)
Function to format an individual item from
- `items` . Defaults to `tostring` .
+ `items`. Defaults to `tostring`.
• kind (string|nil) Arbitrary hint string
indicating the item shape. Plugins
reimplementing `vim.ui.select` may wish to
use this to infer the structure or
- semantics of `items` , or the context in
+ semantics of `items`, or the context in
which select() was called.
- {on_choice} function ((item|nil, idx|nil) -> ()) Called
+ {on_choice} (function) ((item|nil, idx|nil) -> ()) Called
once the user made a choice. `idx` is the
- 1-based index of `item` within `item` . `nil`
+ 1-based index of `item` within `items`. `nil`
if the user aborted the dialog.
@@ -1816,16 +2001,19 @@ add({filetypes}) *vim.filetype.add()*
filename (either the "tail" or the full file path). The full
file path is checked first, followed by the file name. If a
match is not found using the filename, then the filename is
- matched against the list of patterns (sorted by priority)
- until a match is found. Lastly, if pattern matching does not
- find a filetype, then the file extension is used.
+ matched against the list of |lua-patterns| (sorted by
+ priority) until a match is found. Lastly, if pattern matching
+ does not find a filetype, then the file extension is used.
The filetype can be either a string (in which case it is used
as the filetype directly) or a function. If a function, it
takes the full path and buffer number of the file as arguments
(along with captures from the matched pattern, if any) and
should return a string that will be used as the buffer's
- filetype.
+ filetype. Optionally, the function can return a second
+ function value which, when called, modifies the state of the
+ buffer. This can be used to, for example, set
+ filetype-specific buffer variables.
Filename patterns can specify an optional priority to resolve
cases when a file path matches multiple patterns. Higher
@@ -1834,8 +2022,8 @@ add({filetypes}) *vim.filetype.add()*
See $VIMRUNTIME/lua/vim/filetype.lua for more examples.
- Note that Lua filetype detection is only enabled when
- |g:do_filetype_lua| is set to 1.
+ Note that Lua filetype detection is disabled when
+ |g:do_legacy_filetype| is set.
Example: >
@@ -1844,7 +2032,10 @@ add({filetypes}) *vim.filetype.add()*
foo = "fooscript",
bar = function(path, bufnr)
if some_condition() then
- return "barscript"
+ return "barscript", function(bufnr)
+ -- Set a buffer variable
+ vim.b[bufnr].barscript_version = 2
+ end
end
return "bar"
end,
@@ -1868,10 +2059,87 @@ add({filetypes}) *vim.filetype.add()*
})
<
+ To add a fallback match on contents (see
+ |new-filetype-scripts|), use >
+
+ vim.filetype.add {
+ pattern = {
+ ['.*'] = {
+ priority = -math.huge,
+ function(path, bufnr)
+ local content = vim.filetype.getlines(bufnr, 1)
+ if vim.filetype.matchregex(content, { [[^#!.*\<mine\>]] }) then
+ return 'mine'
+ elseif vim.filetype.matchregex(content, { [[\<drawing\>]] }) then
+ return 'drawing'
+ end
+ end,
+ },
+ },
+ }
+<
+
Parameters: ~
- {filetypes} table A table containing new filetype maps
+ {filetypes} (table) A table containing new filetype maps
(see example).
+match({args}) *vim.filetype.match()*
+ Perform filetype detection.
+
+ The filetype can be detected using one of three methods:
+ 1. Using an existing buffer
+ 2. Using only a file name
+ 3. Using only file contents
+
+ Of these, option 1 provides the most accurate result as it
+ uses both the buffer's filename and (optionally) the buffer
+ contents. Options 2 and 3 can be used without an existing
+ buffer, but may not always provide a match in cases where the
+ filename (or contents) cannot unambiguously determine the
+ filetype.
+
+ Each of the three options is specified using a key to the
+ single argument of this function. Example:
+>
+
+ -- Using a buffer number
+ vim.filetype.match({ buf = 42 })
+
+ -- Override the filename of the given buffer
+ vim.filetype.match({ buf = 42, filename = 'foo.c' })
+
+ -- Using a filename without a buffer
+ vim.filetype.match({ filename = 'main.lua' })
+
+ -- Using file contents
+ vim.filetype.match({ contents = {'#!/usr/bin/env bash'} })
+<
+
+ Parameters: ~
+ {args} (table) Table specifying which matching strategy
+ to use. Accepted keys are:
+ • buf (number): Buffer number to use for matching.
+ Mutually exclusive with {contents}
+ • filename (string): Filename to use for matching.
+ When {buf} is given, defaults to the filename of
+ the given buffer number. The file need not
+ actually exist in the filesystem. When used
+ without {buf} only the name of the file is used
+ for filetype matching. This may result in
+ failure to detect the filetype in cases where
+ the filename alone is not enough to disambiguate
+ the filetype.
+ • contents (table): An array of lines representing
+ file contents to use for matching. Can be used
+ with {filename}. Mutually exclusive with {buf}.
+
+ Return: ~
+ (string|nil) If a match was found, the matched filetype.
+ (function|nil) A function that modifies buffer state when
+ called (for example, to set some filetype specific buffer
+ variables). The function accepts a buffer number as its
+ only argument.
+
==============================================================================
Lua module: keymap *lua-keymap*
@@ -1885,7 +2153,7 @@ del({modes}, {lhs}, {opts}) *vim.keymap.del()*
<
Parameters: ~
- {opts} table A table of optional arguments:
+ {opts} (table) A table of optional arguments:
• buffer: (number or boolean) Remove a mapping
from the given buffer. When "true" or 0, use the
current buffer.
@@ -1918,9 +2186,9 @@ set({mode}, {lhs}, {rhs}, {opts}) *vim.keymap.set()*
vim.keymap.set('n', 'asdf', require('jkl').my_fun)
<
- the require('jkl') gets evaluated during this call in order to
- access the function. If you want to avoid this cost at startup
- you can wrap it in a function, for example: >
+ the `require('jkl')` gets evaluated during this call in order to access the
+ function. If you want to avoid this cost at startup you can
+ wrap it in a function, for example: >
vim.keymap.set('n', 'asdf', function() return require('jkl').my_fun() end)
<
@@ -1929,24 +2197,152 @@ set({mode}, {lhs}, {rhs}, {opts}) *vim.keymap.set()*
{mode} string|table Same mode short names as
|nvim_set_keymap()|. Can also be list of modes to
create mapping on multiple modes.
- {lhs} string Left-hand side |{lhs}| of the mapping.
+ {lhs} (string) Left-hand side |{lhs}| of the mapping.
{rhs} string|function Right-hand side |{rhs}| of the
- mapping. Can also be a Lua function.
- {opts} table A table of |:map-arguments| such as
+ mapping. Can also be a Lua function. If a Lua
+ function and `opts.expr == true`, returning `nil`
+ is equivalent to an empty string.
+ {opts} (table) A table of |:map-arguments| such as
"silent". In addition to the options listed in
|nvim_set_keymap()|, this table also accepts the
following keys:
+ • buffer: (number or boolean) Add a mapping to the
+ given buffer. When "true" or 0, use the current
+ buffer.
• replace_keycodes: (boolean, default true) When
both this and expr is "true",
|nvim_replace_termcodes()| is applied to the
result of Lua expr maps.
• remap: (boolean) Make the mapping recursive.
This is the inverse of the "noremap" option from
- |nvim_set_keymap()|. Default `true` if `lhs` is
- a string starting with `<plug>`
- (case-insensitive), `false` otherwise.
+ |nvim_set_keymap()|. Default `false`.
See also: ~
|nvim_set_keymap()|
+
+==============================================================================
+Lua module: fs *lua-fs*
+
+basename({file}) *vim.fs.basename()*
+ Return the basename of the given file or directory
+
+ Parameters: ~
+ {file} (string) File or directory
+
+ Return: ~
+ (string) Basename of {file}
+
+dir({path}) *vim.fs.dir()*
+ Return an iterator over the files and directories located in
+ {path}
+
+ Parameters: ~
+ {path} (string) An absolute or relative path to the
+ directory to iterate over. The path is first
+ normalized |vim.fs.normalize()|.
+
+ Return: ~
+ Iterator over files and directories in {path}. Each
+ iteration yields two values: name and type. Each "name" is
+ the basename of the file or directory relative to {path}.
+ Type is one of "file" or "directory".
+
+dirname({file}) *vim.fs.dirname()*
+ Return the parent directory of the given file or directory
+
+ Parameters: ~
+ {file} (string) File or directory
+
+ Return: ~
+ (string) Parent directory of {file}
+
+find({names}, {opts}) *vim.fs.find()*
+ Find files or directories in the given path.
+
+ Finds any files or directories given in {names} starting from
+ {path}. If {upward} is "true" then the search traverses upward
+ through parent directories; otherwise, the search traverses
+ downward. Note that downward searches are recursive and may
+ search through many directories! If {stop} is non-nil, then
+ the search stops when the directory given in {stop} is
+ reached. The search terminates when {limit} (default 1)
+ matches are found. The search can be narrowed to find only
+ files or or only directories by specifying {type} to be "file"
+ or "directory", respectively.
+
+ Parameters: ~
+ {names} (string|table) Names of the files and directories
+ to find. Must be base names, paths and globs are
+ not supported.
+ {opts} (table) Optional keyword arguments:
+ • path (string): Path to begin searching from. If
+ omitted, the current working directory is used.
+ • upward (boolean, default false): If true,
+ search upward through parent directories.
+ Otherwise, search through child directories
+ (recursively).
+ • stop (string): Stop searching when this
+ directory is reached. The directory itself is
+ not searched.
+ • type (string): Find only files ("file") or
+ directories ("directory"). If omitted, both
+ files and directories that match {name} are
+ included.
+ • limit (number, default 1): Stop the search
+ after finding this many matches. Use
+ `math.huge` to place no limit on the number of
+ matches.
+
+ Return: ~
+ (table) The paths of all matching files or directories
+
+normalize({path}) *vim.fs.normalize()*
+ Normalize a path to a standard format. A tilde (~) character
+ at the beginning of the path is expanded to the user's home
+ directory and any backslash (\) characters are converted to
+ forward slashes (/). Environment variables are also expanded.
+
+ Example: >
+
+ vim.fs.normalize('C:\Users\jdoe')
+ => 'C:/Users/jdoe'
+
+ vim.fs.normalize('~/src/neovim')
+ => '/home/jdoe/src/neovim'
+
+ vim.fs.normalize('$XDG_CONFIG_HOME/nvim/init.vim')
+ => '/Users/jdoe/.config/nvim/init.vim'
+<
+
+ Parameters: ~
+ {path} (string) Path to normalize
+
+ Return: ~
+ (string) Normalized path
+
+parents({start}) *vim.fs.parents()*
+ Iterate over all the parents of the given file or directory.
+
+ Example: >
+
+ local root_dir
+ for dir in vim.fs.parents(vim.api.nvim_buf_get_name(0)) do
+ if vim.fn.isdirectory(dir .. "/.git") == 1 then
+ root_dir = dir
+ break
+ end
+ end
+
+ if root_dir then
+ print("Found git repository at", root_dir)
+ end
+<
+
+ Parameters: ~
+ {start} (string) Initial file or directory.
+
+ Return: ~
+ (function) Iterator
+
vim:tw=78:ts=8:ft=help:norl:
diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt
index e2c274e040..7e94167e07 100644
--- a/runtime/doc/map.txt
+++ b/runtime/doc/map.txt
@@ -48,6 +48,7 @@ modes.
allows for nested and recursive use of mappings.
Note: Trailing spaces are included in the {rhs},
because space is a valid Normal mode command.
+ See |map-trailing-white|.
*:nore* *:norem*
:no[remap] {lhs} {rhs} |mapmode-nvo| *:no* *:noremap* *:nor*
@@ -65,6 +66,8 @@ modes.
where the map command applies. Disallow mapping of
{rhs}, to avoid nested and recursive mappings. Often
used to redefine a command.
+ Note: When <Plug> appears in the {rhs} this part is
+ always applied even if remapping is disallowed.
:unm[ap] {lhs} |mapmode-nvo| *:unm* *:unmap*
@@ -83,10 +86,8 @@ modes.
for other modes where it applies.
It also works when {lhs} matches the {rhs} of a
mapping. This is for when an abbreviation applied.
- Note: Trailing spaces are included in the {lhs}. This
- unmap does NOT work: >
- :map @@ foo
- :unmap @@ | print
+ Note: Trailing spaces are included in the {lhs}.
+ See |map-trailing-white|.
:mapc[lear] |mapmode-nvo| *:mapc* *:mapclear*
:nmapc[lear] |mapmode-n| *:nmapc* *:nmapclear*
@@ -103,7 +104,7 @@ modes.
command applies.
Use the <buffer> argument to remove buffer-local
mappings |:map-<buffer>|
- Warning: This also removes the default mappings.
+ Warning: This also removes the |default-mappings|.
:map |mapmode-nvo|
:nm[ap] |mapmode-n|
@@ -150,6 +151,24 @@ that mapping won't get expanded yet, Vim is waiting for another character.
If you type a space, then "foo" will get inserted, plus the space. If you
type "a", then "bar" will get inserted.
+Trailing white space ~
+ *map-trailing-white*
+This unmap command does NOT work: >
+ :map @@ foo
+ :unmap @@ | print
+
+Because it tries to unmap "@@ ", including the white space before the command
+separator "|". Other examples with trailing white space: >
+ unmap @@
+ unmap @@ " comment
+
+An error will be issued, which is very hard to identify, because the ending
+whitespace character in `unmap @@ ` is not visible.
+
+A generic solution is to put the command separator "|" right after the mapped
+keys. After that white space and a comment may follow: >
+ unmap @@| " comment
+
1.2 SPECIAL ARGUMENTS *:map-arguments*
@@ -157,7 +176,8 @@ type "a", then "bar" will get inserted.
"<unique>" can be used in any order. They must appear right after the
command, before any other arguments.
- *:map-local* *:map-<buffer>* *:map-buffer* *E224* *E225*
+ *:map-local* *:map-<buffer>* *:map-buffer*
+ *E224* *E225*
If the first argument to one of these commands is "<buffer>" the mapping will
be effective in the current buffer only. Example: >
:map <buffer> ,w /[.,;]<CR>
@@ -244,7 +264,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('.')
@@ -285,7 +305,7 @@ Here is an example that inserts a list number that increases: >
func ListItem()
let g:counter += 1
- return g:counter . '. '
+ return g:counter .. '. '
endfunc
func ListReset()
@@ -326,8 +346,6 @@ Note:
mapping is recursive.
- In Visual mode you can use `line('v')` and `col('v')` to get one end of the
Visual area, the cursor is at the other end.
-- In select-mode, |:map| and |:vmap| command mappings are executed in
- visual-mode. Use |:smap| to handle select-mode.
*E5520*
<Cmd> commands must terminate, that is, they must be followed by <CR> in the
@@ -502,7 +520,9 @@ Note: When using mappings for Visual mode, you can use the "'<" mark, which
is the start of the last selected Visual area in the current buffer |'<|.
The |:filter| command can be used to select what mappings to list. The
-pattern is matched against the {lhs} and {rhs} in the raw form.
+pattern is matched against the {lhs} and {rhs} in the raw form. If a
+description was added using |nvim_set_keymap()| or |nvim_buf_set_keymap()|
+then the pattern is also matched against it.
*:map-verbose*
When 'verbose' is non-zero, listing a key map will also display where it was
@@ -792,9 +812,8 @@ command. For example: >
:noremap j k
This will exchange the cursor up and down commands.
-With the normal :map command, when the 'remap' option is on, mapping takes
-place until the text is found not to be a part of a {lhs}. For example, if
-you use: >
+With the normal :map command mapping takes place until the text is found not
+to be a part of a {lhs}. For example, if you use: >
:map x y
:map y x
Vim will replace x with y, and then y with x, etc. When this has happened
@@ -1217,7 +1236,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 ~
@@ -1241,6 +1260,10 @@ See |:verbose-cmd| for more information.
:delc[ommand] {cmd} *:delc* *:delcommand* *E184*
Delete the user-defined command {cmd}.
+:delc[ommand] -buffer {cmd} *E1237*
+ Delete the user-defined command {cmd} that was defined
+ for the current buffer.
+
:comc[lear] *:comc* *:comclear*
Delete all user-defined commands.
@@ -1332,6 +1355,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.
@@ -1411,7 +1436,7 @@ which by default correspond to the current line, last line and the whole
buffer, relate to arguments, (loaded) buffers, windows or tab pages.
Possible values are (second column is the short name used in listing):
- -addr=lines Range of lines (this is the default)
+ -addr=lines line Range of lines (this is the default)
-addr=arguments arg Range for arguments
-addr=buffers buf Range for buffers (also not loaded buffers)
-addr=loaded_buffers load Range for loaded buffers
@@ -1421,6 +1446,112 @@ Possible values are (second column is the short name used in listing):
-addr=other ? other kind of range
+Incremental preview ~
+ *:command-preview* {nvim-api}
+Commands can show an 'inccommand' (as-you-type) preview by defining a preview
+handler (only from Lua, see |nvim_create_user_command()|).
+
+The preview callback must be a Lua function with this signature: >
+
+ function cmdpreview(opts, ns, buf)
+<
+where "opts" has the same form as that given to |nvim_create_user_command()|
+callbacks, "ns" is the preview namespace id for highlights, and "buf" is the
+buffer that your preview routine will directly modify to show the previewed
+results (for "inccommand=split", or nil for "inccommand=nosplit").
+
+Your command preview routine must implement this protocol:
+
+1. Modify the current buffer as required for the preview (see
+ |nvim_buf_set_text()| and |nvim_buf_set_lines()|).
+2. If preview buffer is provided, add necessary text to the preview buffer.
+3. Add required highlights to the current buffer. If preview buffer is
+ provided, add required highlights to the preview buffer as well. All
+ highlights must be added to the preview namespace which is provided as an
+ argument to the preview callback (see |nvim_buf_add_highlight()| and
+ |nvim_buf_set_extmark()| for help on how to add highlights to a namespace).
+4. Return an integer (0, 1, 2) which controls how Nvim behaves as follows:
+ 0: No preview is shown.
+ 1: Preview is shown without preview window (even with "inccommand=split").
+ 2: Preview is shown and preview window is opened (if "inccommand=split").
+ For "inccommand=nosplit" this is the same as 1.
+
+After preview ends, Nvim discards all changes to the buffer and all highlights
+in the preview namespace.
+
+Here's an example of a command to trim trailing whitespace from lines that
+supports incremental command preview:
+>
+ -- Trims trailing whitespace in the current buffer.
+ -- Also performs 'inccommand' preview if invoked as a preview callback
+ -- (preview_ns is non-nil).
+ local function trim_space(opts, preview_ns, preview_buf)
+ local line1 = opts.line1
+ local line2 = opts.line2
+ local buf = vim.api.nvim_get_current_buf()
+ local lines = vim.api.nvim_buf_get_lines(buf, line1 - 1, line2, 0)
+ local new_lines = {}
+ local preview_buf_line = 0
+
+ for i, line in ipairs(lines) do
+ local startidx, endidx = string.find(line, '%s+$')
+
+ if startidx ~= nil then
+ -- Highlight the match if in command preview mode
+ if preview_ns ~= nil then
+ vim.api.nvim_buf_add_highlight(
+ buf, preview_ns, 'Substitute', line1 + i - 2, startidx - 1,
+ endidx
+ )
+
+ -- Add lines and highlight to the preview buffer
+ -- if inccommand=split
+ if preview_buf ~= nil then
+ local prefix = string.format('|%d| ', line1 + i - 1)
+
+ vim.api.nvim_buf_set_lines(
+ preview_buf, preview_buf_line, preview_buf_line, 0,
+ { prefix .. line }
+ )
+ vim.api.nvim_buf_add_highlight(
+ preview_buf, preview_ns, 'Substitute', preview_buf_line,
+ #prefix + startidx - 1, #prefix + endidx
+ )
+
+ preview_buf_line = preview_buf_line + 1
+ end
+ end
+ end
+
+ if not preview_ns then
+ new_lines[#new_lines+1] = string.gsub(line, '%s+$', '')
+ end
+ end
+
+ -- Don't make any changes to the buffer if previewing
+ if not preview_ns then
+ vim.api.nvim_buf_set_lines(buf, line1 - 1, line2, 0, new_lines)
+ end
+
+ -- When called as a preview callback, return the value of the
+ -- preview type
+ if preview_ns ~= nil then
+ return 2
+ end
+ end
+
+ -- Create the user command
+ vim.api.nvim_create_user_command(
+ 'TrimTrailingWhitespace',
+ trim_space,
+ { nargs = '?', range = '%', addr = 'lines', preview = trim_space }
+ )
+<
+Note that in the above example, the same function is used as both the command
+callback and the preview callback, but you could instead use separate
+functions.
+
+
Special cases ~
*:command-bang* *:command-bar*
*:command-register* *:command-buffer*
@@ -1434,6 +1565,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
@@ -1471,19 +1605,19 @@ The valid escape sequences are
nothing. Supported modifiers are |:aboveleft|, |:belowright|,
|:botright|, |:browse|, |:confirm|, |:hide|, |:keepalt|,
|:keepjumps|, |:keepmarks|, |:keeppatterns|, |:leftabove|,
- |:lockmarks|, |:noswapfile| |:rightbelow|, |:silent|, |:tab|,
- |:topleft|, |:verbose|, and |:vertical|.
- Note that these are not yet supported: |:noautocmd|,
- |:sandbox| and |:unsilent|.
+ |:lockmarks|, |:noautocmd|, |:noswapfile| |:rightbelow|,
+ |:sandbox|, |:silent|, |:tab|, |:topleft|, |:unsilent|,
+ |:verbose|, and |:vertical|.
+ Note that |:filter| is not supported.
Examples: >
command! -nargs=+ -complete=file MyEdit
\ for f in expand(<q-args>, 0, 1) |
- \ exe '<mods> split ' . f |
+ \ exe '<mods> split ' .. f |
\ endfor
function! SpecialEdit(files, mods)
for f in expand(a:files, 0, 1)
- exe a:mods . ' split ' . f
+ exe a:mods .. ' split ' .. f
endfor
endfunction
command! -nargs=+ -complete=file Sedit
@@ -1559,7 +1693,7 @@ This will invoke: >
: let i = 0
: while i < argc()
: if filereadable(argv(i))
- : execute "e " . argv(i)
+ : execute "e " .. argv(i)
: execute a:command
: endif
: let i = i + 1
diff --git a/runtime/doc/message.txt b/runtime/doc/message.txt
index 950028d9cc..dac4df5ee9 100644
--- a/runtime/doc/message.txt
+++ b/runtime/doc/message.txt
@@ -27,8 +27,7 @@ depends on the 'shortmess' option.
Clear messages, keeping only the {count} most
recent ones.
-The number of remembered messages is fixed at 20 for the tiny version and 200
-for other versions.
+The number of remembered messages is fixed at 200.
*g<*
The "g<" command can be used to see the last page of previous command output.
@@ -62,7 +61,7 @@ If you are lazy, it also works without the shift key: >
When an error message is displayed, but it is removed before you could read
it, you can see it again with: >
- :echo errmsg
+ :echo v:errmsg
Or view a list of recent messages with: >
:messages
See `:messages` above.
diff --git a/runtime/doc/motion.txt b/runtime/doc/motion.txt
index c473244827..9655d07a84 100644
--- a/runtime/doc/motion.txt
+++ b/runtime/doc/motion.txt
@@ -735,6 +735,11 @@ Jumping to a mark can be done in two ways:
2. With ' (single quote): The cursor is positioned on the first non-blank
character in the line of the specified location and
the motion is linewise.
+ *mark-view*
+3. Apart from the above if 'jumpoptions' contains "view", they will also try to
+restore the mark view. This is the number of lines between the cursor position
+and the window topline (first buffer line displayed in the window) when it was
+set.
*m* *mark* *Mark*
m{a-zA-Z} Set mark {a-zA-Z} at cursor position (does not move
@@ -993,7 +998,7 @@ These commands are not marks themselves, but jump to a mark:
:let lnum = line(".")
:keepjumps normal gg
:call SetLastChange()
- :keepjumps exe "normal " . lnum . "G"
+ :keepjumps exe "normal " .. lnum .. "G"
<
Note that ":keepjumps" must be used for every command.
When invoking a function the commands in that function
@@ -1044,6 +1049,9 @@ The "file/text" column shows the file name, or the text at the jump if it is
in the current file (an indent is removed and a long line is truncated to fit
in the window).
+The marker ">" indicates the current position in the jumplist. It may not be
+shown when filtering the |:jumps| command using |:filter|
+
You are currently in line 1167. If you then use the CTRL-O command, the
cursor is put in line 1154. This results in:
diff --git a/runtime/doc/nvim_terminal_emulator.txt b/runtime/doc/nvim_terminal_emulator.txt
index bfacbe19f5..a7be9ff98f 100644
--- a/runtime/doc/nvim_terminal_emulator.txt
+++ b/runtime/doc/nvim_terminal_emulator.txt
@@ -25,31 +25,30 @@ Start *terminal-start*
There are several ways to create a terminal buffer:
-- Invoke the |:terminal| command.
-- Call the |termopen()| function.
-- Edit a file with a name matching `term://(.{-}//(\d+:)?)?\zs.*`.
- For example:
->
+- Run the |:terminal| command.
+- Call the |nvim_open_term()| or |termopen()| function.
+- Edit a "term://" buffer. Examples: >
:edit term://bash
:vsplit term://top
-<
- Note: The "term://" pattern is handled by a BufReadCmd handler, so the
- |autocmd-nested| modifier is required to use it in an autocmd. >
+
+< Note: To open a "term://" buffer from an autocmd, the |autocmd-nested|
+ modifier is required. >
autocmd VimEnter * ++nested split term://sh
-< This is only mentioned for reference; use |:terminal| instead.
+< (This is only mentioned for reference; use |:terminal| instead.)
When the terminal starts, the buffer contents are updated and the buffer is
named in the form of `term://{cwd}//{pid}:{cmd}`. This naming scheme is used
by |:mksession| to restore a terminal buffer (by restarting the {cmd}).
+The terminal environment is initialized as in |jobstart-env|.
+
==============================================================================
Input *terminal-input*
-To send input, enter |Terminal-mode| using any command that would enter "insert
-mode" in a normal buffer, such as |i| or |:startinsert|. In this mode all keys
-except <C-\> are sent to the underlying program. If <C-\> is pressed, the
-next key is sent unless it is <C-N>. Use <C-\><C-N> to return to normal-mode.
-|CTRL-\_CTRL-N|
+To send input, enter |Terminal-mode| with |i|, |I|, |a|, |A| or
+|:startinsert|. In this mode all keys except <C-\> are sent to the underlying
+program. If <C-\> is pressed, the next key is sent unless it is <C-N>. Use
+<C-\><C-N> to return to normal-mode. |CTRL-\_CTRL-N|
Terminal-mode forces these local options:
@@ -81,6 +80,9 @@ To use `ALT+{h,j,k,l}` to navigate windows from any mode: >
:nnoremap <A-k> <C-w>k
:nnoremap <A-l> <C-w>l
+You can also create menus similar to terminal mode mappings, but you have to
+use |:tlmenu| instead of |:tmenu|.
+
Mouse input has the following behavior:
- If the program has enabled mouse events, the corresponding events will be
@@ -162,12 +164,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
@@ -267,6 +268,8 @@ Put focus on the gdb window to type commands there. Some common ones are:
- next execute the current line and stop at the next line
- step execute the current line and stop at the next statement,
entering functions
+- until execute until past the current cursor line or past a specified
+ position or the current stack frame returns
- finish execute until leaving the current function
- where show the stack
- frame N go to the Nth stack frame
@@ -283,6 +286,7 @@ gdb:
*:Step* execute the gdb "step" command
*:Over* execute the gdb "next" command (`:Next` is a Vim command)
+ *:Until* execute the gdb "until" command
*:Finish* execute the gdb "finish" command
*:Continue* execute the gdb "continue" command
*:Stop* interrupt the program
@@ -303,7 +307,7 @@ breakpoint, or use the "Clear breakpoint" right-click menu entry.
Inspecting variables ~
*termdebug-variables* *:Evaluate*
`:Evaluate` evaluate the expression under the cursor
- `K` same
+ `K` same (see |termdebug_map_K| to disable)
`:Evaluate` {expr} evaluate {expr}
`:'<,'>Evaluate` evaluate the Visually selected text
@@ -332,7 +336,7 @@ Four autocommands can be used: >
*TermdebugStartPre*
TermdebugStartPre Before starting debugging.
Not triggered if the debugger is already
- running or |g:termdebugger| cannot be
+ running or the debugger command cannot be
executed.
*TermdebugStartPost*
TermdebugStartPost After debugging has initialized.
@@ -361,14 +365,24 @@ This works slightly differently:
*termdebug_use_prompt*
Prompt mode can be used with: >
+ let g:termdebug_config['use_prompt'] = 1
+Or if there is no g:termdebug_config: >
let g:termdebug_use_prompt = 1
-
+<
+ *termdebug_map_K*
+The K key is normally mapped to :Evaluate. If you do not want this use: >
+ let g:termdebug_config['map_K'] = 0
+Or if there is no g:termdebug_config: >
+ let g:termdebug_map_K = 0
<
*termdebug_disasm_window*
-If you want the Asm window shown by default, set this to 1. Setting to
-any value greater than 1 will set the Asm window height to that value: >
+If you want the Asm window shown by default, set the flag to 1.
+the "disasm_window_height" entry can be used to set the window height: >
+ let g:termdebug_config['disasm_window'] = 1
+ let g:termdebug_config['disasm_window_height'] = 15
+or, if there is no g:termdebug_config: >
let g:termdebug_disasm_window = 15
-<
+Any value greater than 1 will set the Asm window height to that value.
Communication ~
*termdebug-communication*
@@ -385,13 +399,24 @@ communication channel.
Customizing ~
+ *termdebug-customizing* *g:termdebug_config*
+In the past several global variables were used for configuration. These are
+deprecated, using the g:termdebug_config dictionary is preferred. When
+g:termdebug_config exists the other global variables will not be used.
+
-GDB command *termdebug-customizing*
+GDB command ~
+ *g:termdebugger*
+To change the name of the gdb command, set "debugger" entry in
+g:termdebug_config or the "g:termdebugger" variable before invoking
+`:Termdebug`: >
+ let g:termdebug_config['command'] = "mygdb"
+Or if there is no g:termdebug_config: >
+ let g:termdebugger = "mygdb"
-To change the name of the gdb command, set the "termdebugger" variable before
-invoking `:Termdebug`: >
- let termdebugger = "mygdb"
If the command needs an argument use a List: >
+ let g:termdebug_config['command'] = ['rr', 'replay', '--']
+Or if there is no g:termdebug_config: >
let g:termdebugger = ['rr', 'replay', '--']
To not use neovim floating windows for previewing variable evaluation, set the
@@ -405,7 +430,17 @@ cursor: >
or set/unset a breakpoint: >
nnoremap <RightMouse> :Break<CR>
-< *gdb-version*
+
+Several arguments will be added to make gdb work well for the debugger.
+If you want to modify them, add a function to filter the argument list: >
+ let g:termdebug_config['command_filter'] = MyDebugFilter
+
+If you do not want the arguments to be added, but you do need to set the
+"pty", use a function to add the necessary arguments: >
+ let g:termdebug_config['command_add_args'] = MyAddArguments
+The function will be called with the list of arguments so far, and a second
+argument that is the name of the pty.
+ *gdb-version*
Only debuggers fully compatible with gdb will work. Vim uses the GDB/MI
interface. The "new-ui" command requires gdb version 7.12 or later. if you
get this error:
@@ -413,8 +448,8 @@ get this error:
Then your gdb is too old.
-Colors *hl-debugPC* *hl-debugBreakpoint*
-
+Colors ~
+ *hl-debugPC* *hl-debugBreakpoint*
The color of the signs can be adjusted with these highlight groups:
- debugPC the current position
- debugBreakpoint a breakpoint
@@ -428,7 +463,8 @@ When 'background' is "dark":
hi debugBreakpoint term=reverse ctermbg=red guibg=red
-Shortcuts *termdebug_shortcuts*
+Shortcuts ~
+ *termdebug_shortcuts*
You can define your own shortcuts (mappings) to control gdb, that can work in
any window, using the TermDebugSendCommand() function. Example: >
@@ -436,18 +472,37 @@ any window, using the TermDebugSendCommand() function. Example: >
The argument is the gdb command.
-Vim window width *termdebug_wide*
+Popup menu ~
+ *termdebug_popup*
+
+By default the Termdebug plugin sets 'mousemodel' to "popup_setpos" and adds
+these entries to the popup menu:
+ Set breakpoint `:Break`
+ Clear breakpoint `:Clear`
+ Evaluate `:Evaluate`
+If you don't want this then disable it with: >
+ let g:termdebug_config['popup'] = 0
+or if there is no g:termdebug_config: >
+ let g:termdebug_popup = 0
+
+
+Vim window width ~
+ *termdebug_wide*
+
+To change the width of the Vim window when debugging starts and use a vertical
+split: >
+ let g:termdebug_config['wide'] = 163
+Or if there is no g:termdebug_config: >
+ let g:termdebug_wide = 163
+
+This will set 'columns' to 163 when `:Termdebug` is used. The value is
+restored when quitting the debugger.
-To change the width of the Vim window when debugging starts, and use a
-vertical split: >
- let g:termdebug_wide = 163
-This will set &columns to 163 when `:Termdebug` is used. The value is restored
-when quitting the debugger.
-If g:termdebug_wide is set and &columns is already larger than
-g:termdebug_wide then a vertical split will be used without changing &columns.
-Set it to 1 to get a vertical split without every changing &columns (useful
-for when the terminal can't be resized by Vim).
+If the wide value is set and 'columns' is already a greater value, then a
+vertical split will be used without modifying 'columns'.
+Set the wide value to 1 to use a vertical split without ever changing
+'columns'. This is useful when the terminal can't be resized by Vim.
vim:tw=78:ts=8:noet:ft=help:norl:
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index 7dea475801..eda596bf71 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}.
@@ -65,7 +69,7 @@ achieve special effects. These options come in three forms:
:se[t] {option}+={value} *:set+=*
Add the {value} to a number option, or append the
{value} to a string option. When the option is a
- comma separated list, a comma is added, unless the
+ comma-separated list, a comma is added, unless the
value was empty.
If the option is a list of flags, superfluous flags
are removed. When adding a flag that was already
@@ -75,7 +79,7 @@ achieve special effects. These options come in three forms:
:se[t] {option}^={value} *:set^=*
Multiply the {value} to a number option, or prepend
the {value} to a string option. When the option is a
- comma separated list, a comma is added, unless the
+ comma-separated list, a comma is added, unless the
value was empty.
Also see |:set-args| above.
@@ -83,7 +87,7 @@ achieve special effects. These options come in three forms:
Subtract the {value} from a number option, or remove
the {value} from a string option, if it is there.
If the {value} is not found in a string option, there
- is no error or warning. When the option is a comma
+ is no error or warning. When the option is a comma-
separated list, a comma is deleted, unless the option
becomes empty.
When the option is a list of flags, {value} must be
@@ -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
@@ -747,7 +751,8 @@ A jump table for the options with a short description can be found at |Q_op|.
nostop like start, except CTRL-W and CTRL-U do not stop at the start of
insert.
- When the value is empty, Vi compatible backspacing is used.
+ When the value is empty, Vi compatible backspacing is used, none of
+ the ways mentioned for the items above are possible.
For backwards compatibility with version 5.4 and earlier:
value effect ~
@@ -771,10 +776,10 @@ A jump table for the options with a short description can be found at |Q_op|.
oldest version of a file.
*'backupcopy'* *'bkc'*
-'backupcopy' 'bkc' string (Vi default for Unix: "yes", otherwise: "auto")
+'backupcopy' 'bkc' string (default: "auto")
global or local to buffer |global-local|
When writing a file and a backup is made, this option tells how it's
- done. This is a comma separated list of words.
+ done. This is a comma-separated list of words.
The main values are:
"yes" make a copy of the file and overwrite the original one
@@ -798,10 +803,10 @@ A jump table for the options with a short description can be found at |Q_op|.
file.
- When the file is a link the new file will not be a link.
- The "auto" value is the middle way: When Vim sees that renaming file
- is possible without side effects (the attributes can be passed on and
- the file is not a link) that is used. When problems are expected, a
- copy will be made.
+ The "auto" value is the middle way: When Vim sees that renaming the
+ file is possible without side effects (the attributes can be passed on
+ and the file is not a link) that is used. When problems are expected,
+ a copy will be made.
The "breaksymlink" and "breakhardlink" values can be used in
combination with any of "yes", "no" and "auto". When included, they
@@ -820,13 +825,13 @@ A jump table for the options with a short description can be found at |Q_op|.
When a copy is made, the original file is truncated and then filled
with the new text. This means that protection bits, owner and
- symbolic links of the original file are unmodified. The backup file
+ symbolic links of the original file are unmodified. The backup file,
however, is a new file, owned by the user who edited the file. The
group of the backup is set to the group of the original file. If this
fails, the protection bits for the group are made the same as for
others.
- When the file is renamed this is the other way around: The backup has
+ When the file is renamed, this is the other way around: The backup has
the same attributes of the original file, and the newly written file
is owned by the current user. When the file was a (hard/symbolic)
link, the new file will not! That's why the "auto" value doesn't
@@ -836,7 +841,7 @@ A jump table for the options with a short description can be found at |Q_op|.
again not rename the file.
*'backupdir'* *'bdir'*
-'backupdir' 'bdir' string (default ".,$XDG_DATA_HOME/nvim/backup//")
+'backupdir' 'bdir' string (default ".,$XDG_STATE_HOME/nvim/backup//")
global
List of directories for the backup file, separated with commas.
- The backup file will be created in the first directory in the list
@@ -888,12 +893,12 @@ A jump table for the options with a short description can be found at |Q_op|.
accidentally overwriting existing files with a backup file. You might
prefer using ".bak", but make sure that you don't have files with
".bak" that you want to keep.
- Only normal file name characters can be used, "/\*?[|<>" are illegal.
+ Only normal file name characters can be used; "/\*?[|<>" are illegal.
If you like to keep a lot of backups, you could use a BufWritePre
autocommand to change 'backupext' just before writing the file to
include a timestamp. >
- :au BufWritePre * let &bex = '-' . strftime("%Y%b%d%X") . '~'
+ :au BufWritePre * let &bex = '-' .. strftime("%Y%b%d%X") .. '~'
< Use 'backupdir' to put the backup in a different directory.
*'backupskip'* *'bsk'*
@@ -916,25 +921,16 @@ A jump table for the options with a short description can be found at |Q_op|.
Note that environment variables are not expanded. If you want to use
$HOME you must expand it explicitly, e.g.: >
- :let backupskip = escape(expand('$HOME'), '\') . '/tmp/*'
+ :let &backupskip = escape(expand('$HOME'), '\') .. '/tmp/*'
< Note that the default also makes sure that "crontab -e" works (when a
backup would be made by renaming the original file crontab won't see
the newly created file). Also see 'backupcopy' and |crontab|.
- *'balloondelay'* *'bdlay'*
-'balloondelay' 'bdlay' Removed.
-
- *'ballooneval'* *'beval'* *'noballooneval'* *'nobeval'*
-'ballooneval' 'beval' Removed.
-
- *'balloonexpr'* *'bexpr'*
-'balloonexpr' 'bexpr' Removed.
-
*'belloff'* *'bo'*
'belloff' 'bo' string (default "all")
global
- Specifies for which events the bell will not be rung. It is a comma
+ Specifies for which events the bell will not be rung. It is a comma-
separated list of items. For each item that is present, the bell
will be silenced. This is most useful to specify specific events in
insert mode to be silenced.
@@ -954,7 +950,6 @@ A jump table for the options with a short description can be found at |Q_op|.
(mostly used in |Normal-mode| or |Cmdline-mode|).
esc hitting <Esc> in |Normal-mode|.
hangul Ignored.
- insertmode Pressing <Esc> in 'insertmode'.
lang Calling the beep module for Lua/Mzscheme/TCL.
mess No output available for |g<|.
showmatch Error occurred for 'showmatch' function.
@@ -1025,7 +1020,7 @@ A jump table for the options with a short description can be found at |Q_op|.
This option lets you choose which characters might cause a line
break if 'linebreak' is on. Only works for ASCII characters.
- *'breakindent'* *'bri'*
+ *'breakindent'* *'bri'* *'nobreakindent'* *'nobri'*
'breakindent' 'bri' boolean (default off)
local to window
Every wrapped line will continue visually indented (same amount of
@@ -1072,16 +1067,16 @@ A jump table for the options with a short description can be found at |Q_op|.
This option specifies what happens when a buffer is no longer
displayed in a window:
<empty> follow the global 'hidden' option
- hide hide the buffer (don't unload it), also when 'hidden'
- is not set
- unload unload the buffer, also when 'hidden' is set or using
- |:hide|
- delete delete the buffer from the buffer list, also when
- 'hidden' is set or using |:hide|, like using
- |:bdelete|
- wipe wipe out the buffer from the buffer list, also when
- 'hidden' is set or using |:hide|, like using
- |:bwipeout|
+ hide hide the buffer (don't unload it), even if 'hidden' is
+ not set
+ unload unload the buffer, even if 'hidden' is set; the
+ |:hide| command will also unload the buffer
+ delete delete the buffer from the buffer list, even if
+ 'hidden' is set; the |:hide| command will also delete
+ the buffer, making it behave like |:bdelete|
+ wipe wipe the buffer from the buffer list, even if
+ 'hidden' is set; the |:hide| command will also wipe
+ out the buffer, making it behave like |:bwipeout|
CAREFUL: when "unload", "delete" or "wipe" is used changes in a buffer
are lost without a warning. Also, these values may break autocommands
@@ -1181,13 +1176,13 @@ A jump table for the options with a short description can be found at |Q_op|.
If the default value taken from $CDPATH is not what you want, include
a modified version of the following command in your vimrc file to
override it: >
- :let &cdpath = ',' . substitute(substitute($CDPATH, '[, ]', '\\\0', 'g'), ':', ',', 'g')
+ :let &cdpath = ',' .. substitute(substitute($CDPATH, '[, ]', '\\\0', 'g'), ':', ',', 'g')
< This option cannot be set from a |modeline| or in the |sandbox|, for
security reasons.
(parts of 'cdpath' can be passed to the shell to expand file names).
*'cedit'*
-'cedit' string (Vim default: CTRL-F, Vi default: "")
+'cedit' string (default: CTRL-F)
global
The key used in Command-line Mode to open the command-line window.
Only non-printable keys are allowed.
@@ -1216,8 +1211,8 @@ A jump table for the options with a short description can be found at |Q_op|.
preferred, because it is much faster.
'charconvert' is not used when reading stdin |--|, because there is no
file to convert from. You will have to save the text in a file first.
- The expression must return zero or an empty string for success,
- non-zero for failure.
+ The expression must return zero, false or an empty string for success,
+ non-zero or true for failure.
See |encoding-names| for possible encoding names.
Additionally, names given in 'fileencodings' and 'fileencoding' are
used.
@@ -1228,8 +1223,8 @@ A jump table for the options with a short description can be found at |Q_op|.
set charconvert=CharConvert()
fun CharConvert()
system("recode "
- \ . v:charconvert_from . ".." . v:charconvert_to
- \ . " <" . v:fname_in . " >" v:fname_out)
+ \ .. v:charconvert_from .. ".." .. v:charconvert_to
+ \ .. " <" .. v:fname_in .. " >" .. v:fname_out)
return v:shell_error
endfun
< The related Vim variables are:
@@ -1283,10 +1278,18 @@ A jump table for the options with a short description can be found at |Q_op|.
matter, include the keyword both the uppercase and lowercase:
"if,If,IF".
- *'clipboard'* *'cb'*
+ *'cinscopedecls'* *'cinsd'*
+'cinscopedecls' 'cinsd' string (default "public,protected,private")
+ local to buffer
+ Keywords that are interpreted as a C++ scope declaration by |cino-g|.
+ Useful e.g. for working with the Qt framework that defines additional
+ scope declarations "signals", "public slots" and "private slots": >
+ set cinscopedecls+=signals,public\ slots,private\ slots
+
+< *'clipboard'* *'cb'*
'clipboard' 'cb' string (default "")
global
- This option is a list of comma separated names.
+ This option is a list of comma-separated names.
These names are recognized:
*clipboard-unnamed*
@@ -1317,6 +1320,9 @@ A jump table for the options with a short description can be found at |Q_op|.
The value of this option is stored with the tab page, so that each tab
page can have a different value.
+ When 'cmdheight' is zero, it disables echo area and all outputs need
+ |hit-enter| prompt.
+
*'cmdwinheight'* *'cwh'*
'cmdwinheight' 'cwh' number (default 7)
global
@@ -1325,10 +1331,11 @@ A jump table for the options with a short description can be found at |Q_op|.
*'colorcolumn'* *'cc'*
'colorcolumn' 'cc' string (default "")
local to window
- 'colorcolumn' is a comma separated list of screen columns that are
+ 'colorcolumn' is a comma-separated list of screen columns that are
highlighted with ColorColumn |hl-ColorColumn| and drawn using the
colocol option from 'fillchars'. Useful to align text. Will make
screen redrawing slower.
+
The screen column can be an absolute number, or a number preceded with
'+' or '-', which is added to or subtracted from 'textwidth'. >
@@ -1359,7 +1366,7 @@ A jump table for the options with a short description can be found at |Q_op|.
'comments' 'com' string (default
"s1:/*,mb:*,ex:*/,://,b:#,:%,:XCOMM,n:>,fb:-")
local to buffer
- A comma separated list of strings that can start a comment line. See
+ A comma-separated list of strings that can start a comment line. See
|format-comments|. See |option-backslash| about using backslashes to
insert a space.
@@ -1376,7 +1383,7 @@ A jump table for the options with a short description can be found at |Q_op|.
This option specifies how keyword completion |ins-completion| works
when CTRL-P or CTRL-N are used. It is also used for whole-line
completion |i_CTRL-X_CTRL-L|. It indicates the type of completion
- and the places to scan. It is a comma separated list of flags:
+ and the places to scan. It is a comma-separated list of flags:
. scan the current buffer ('wrapscan' is ignored)
w scan buffers from other windows
b scan other loaded buffers that are in the buffer list
@@ -1433,7 +1440,7 @@ A jump table for the options with a short description can be found at |Q_op|.
*'completeopt'* *'cot'*
'completeopt' 'cot' string (default: "menu,preview")
global
- A comma separated list of options for Insert mode completion
+ A comma-separated list of options for Insert mode completion
|ins-completion|. The supported values are:
menu Use a popup menu to show the possible completions. The
@@ -1531,8 +1538,7 @@ A jump table for the options with a short description can be found at |Q_op|.
See 'preserveindent'.
*'cpoptions'* *'cpo'* *cpo*
-'cpoptions' 'cpo' string (Vim default: "aABceFs_",
- Vi default: all flags)
+'cpoptions' 'cpo' string (default: "aABceFs_")
global
A sequence of single character flags. When a character is present
this indicates Vi-compatible behavior. This is used for things where
@@ -1838,7 +1844,7 @@ A jump table for the options with a short description can be found at |Q_op|.
*'cursorlineopt'* *'culopt'*
'cursorlineopt' 'culopt' string (default: "number,line")
local to window
- Comma separated list of settings for how 'cursorline' is displayed.
+ Comma-separated list of settings for how 'cursorline' is displayed.
Valid values:
"line" Highlight the text line of the cursor with
CursorLine |hl-CursorLine|.
@@ -2049,7 +2055,7 @@ A jump table for the options with a short description can be found at |Q_op|.
{char2}. See |digraphs|.
*'directory'* *'dir'*
-'directory' 'dir' string (default "$XDG_DATA_HOME/nvim/swap//")
+'directory' 'dir' string (default "$XDG_STATE_HOME/nvim/swap//")
global
List of directory names for the swap file, separated with commas.
@@ -2101,9 +2107,9 @@ A jump table for the options with a short description can be found at |Q_op|.
security reasons.
*'display'* *'dy'*
-'display' 'dy' string (default "lastline,msgsep", Vi default: "")
+'display' 'dy' string (default "lastline,msgsep")
global
- Change the way text is displayed. This is comma separated list of
+ Change the way text is displayed. This is comma-separated list of
flags:
lastline When included, as much as possible of the last line
in a window will be displayed. "@@@" is put in the
@@ -2129,7 +2135,7 @@ A jump table for the options with a short description can be found at |Q_op|.
hor horizontally, height of windows is not affected
both width and height of windows is affected
- *'emoji'* *'emo'*
+ *'emoji'* *'emo'* *'noemoji'* *'noemo'*
'emoji' 'emo' boolean (default: on)
global
When on all Unicode emoji characters are considered to be full width.
@@ -2221,7 +2227,7 @@ A jump table for the options with a short description can be found at |Q_op|.
A list of autocommand event names, which are to be ignored.
When set to "all" or when "all" is one of the items, all autocommand
events are ignored, autocommands will not be executed.
- Otherwise this is a comma separated list of event names. Example: >
+ Otherwise this is a comma-separated list of event names. Example: >
:set ei=WinEnter,WinLeave
<
*'expandtab'* *'et'* *'noexpandtab'* *'noet'*
@@ -2347,9 +2353,8 @@ A jump table for the options with a short description can be found at |Q_op|.
*'fileformats'* *'ffs'*
'fileformats' 'ffs' string (default:
- Vim+Vi Win32: "dos,unix",
- Vim Unix: "unix,dos",
- Vi others: "")
+ Win32: "dos,unix",
+ Unix: "unix,dos")
global
This gives the end-of-line (<EOL>) formats that will be tried when
starting to edit a new buffer and when reading a file into an existing
@@ -2433,12 +2438,20 @@ A jump table for the options with a short description can be found at |Q_op|.
'fillchars' 'fcs' string (default "")
global or local to window |global-local|
Characters to fill the statuslines and vertical separators.
- It is a comma separated list of items:
+ It is a comma-separated list of items:
item default Used for ~
stl:c ' ' or '^' statusline of the current window
stlnc:c ' ' or '=' statusline of the non-current windows
+ wbr:c ' ' window bar
+ horiz:c '─' or '-' horizontal separators |:split|
+ horizup:c '┴' or '-' upwards facing horizontal separator
+ horizdown:c '┬' or '-' downwards facing horizontal separator
vert:c '│' or '|' vertical separators |:vsplit|
+ vertleft:c '┤' or '|' left facing vertical separator
+ vertright:c '├' or '|' right facing vertical separator
+ verthoriz:c '┼' or '+' overlapping vertical and horizontal
+ separator
fold:c '·' or '-' filling 'foldtext'
foldopen:c '-' mark the beginning of a fold
foldclose:c '+' show a closed fold
@@ -2452,21 +2465,35 @@ A jump table for the options with a short description can be found at |Q_op|.
"stlnc" the space will be used when there is highlighting, '^' or '='
otherwise.
- If 'ambiwidth' is "double" then "vert", "foldsep" and "fold" default to
- single-byte alternatives.
+ Note that "horiz", "horizup", "horizdown", "vertleft", "vertright" and
+ "verthoriz" are only used when 'laststatus' is 3, since only vertical
+ window separators are used otherwise.
+
+ If 'ambiwidth' is "double" then "horiz", "horizup", "horizdown",
+ "vert", "vertleft", "vertright", "verthoriz", "foldsep" and "fold"
+ default to single-byte alternatives.
Example: >
:set fillchars=stl:^,stlnc:=,vert:│,fold:·,diff:-
< This is similar to the default, except that these characters will also
be used when there is highlighting.
- for "stl" and "stlnc" only single-byte values are supported.
+ For the "stl", "stlnc", "foldopen", "foldclose" and "foldsep" items
+ single-byte and multibyte characters are supported. But double-width
+ characters are not supported.
The highlighting used for these items:
item highlight group ~
stl:c StatusLine |hl-StatusLine|
stlnc:c StatusLineNC |hl-StatusLineNC|
- vert:c VertSplit |hl-VertSplit|
+ wbr:c WinBar |hl-WinBar| or |hl-WinBarNC|
+ horiz:c WinSeparator |hl-WinSeparator|
+ horizup:c WinSeparator |hl-WinSeparator|
+ horizdown:c WinSeparator |hl-WinSeparator|
+ vert:c WinSeparator |hl-WinSeparator|
+ vertleft:c WinSeparator |hl-WinSeparator|
+ vertright:c WinSeparator |hl-WinSeparator|
+ verthoriz:c WinSeparator |hl-WinSeparator|
fold:c Folded |hl-Folded|
diff:c DiffDelete |hl-DiffDelete|
eob:c EndOfBuffer |hl-EndOfBuffer|
@@ -2597,7 +2624,7 @@ A jump table for the options with a short description can be found at |Q_op|.
search,tag,undo")
global
Specifies for which type of commands folds will be opened, if the
- command moves the cursor into a closed fold. It is a comma separated
+ command moves the cursor into a closed fold. It is a comma-separated
list of items.
NOTE: When the command is part of a mapping this option is not used.
Add the |zv| command to the mapping to get the same effect.
@@ -2688,7 +2715,7 @@ A jump table for the options with a short description can be found at |Q_op|.
character and white space.
*'formatoptions'* *'fo'*
-'formatoptions' 'fo' string (default: "tcqj", Vi default: "vt")
+'formatoptions' 'fo' string (default: "tcqj")
local to buffer
This is a sequence of letters which describes how automatic
formatting is to be done. See |fo-table|. When the 'paste' option is
@@ -2712,7 +2739,7 @@ A jump table for the options with a short description can be found at |Q_op|.
This option cannot be set from a |modeline| or in the |sandbox|, for
security reasons.
- *'fsync'* *'fs'*
+ *'fsync'* *'fs'* *'nofsync'* *'nofs'*
'fsync' 'fs' boolean (default off)
global
When on, the OS function fsync() will be called after saving a file
@@ -2788,7 +2815,7 @@ A jump table for the options with a short description can be found at |Q_op|.
\,a:blinkwait700-blinkoff400-blinkon250-Cursor/lCursor
\,sm:block-blinkwait175-blinkoff150-blinkon175
-< The option is a comma separated list of parts. Each part consists of a
+< The option is a comma-separated list of parts. Each part consists of a
mode-list and an argument-list:
mode-list:argument-list,mode-list:argument-list,..
The mode-list is a dash separated list of these modes:
@@ -3038,7 +3065,7 @@ A jump table for the options with a short description can be found at |Q_op|.
*'guitablabel'* *'gtl'*
'guitablabel' 'gtl' string (default empty)
global
- When nonempty describes the text to use in a label of the GUI tab
+ When non-empty describes the text to use in a label of the GUI tab
pages line. When empty and when the result is empty Vim will use a
default label. See |setting-guitablabel| for more info.
@@ -3055,7 +3082,7 @@ A jump table for the options with a short description can be found at |Q_op|.
*'guitabtooltip'* *'gtt'*
'guitabtooltip' 'gtt' string (default empty)
global
- When nonempty describes the text to use in a tooltip for the GUI tab
+ When non-empty describes the text to use in a tooltip for the GUI tab
pages line. When empty Vim will use a default tooltip.
This option is otherwise just like 'guitablabel' above.
You can include a line break. Simplest method is to use |:let|: >
@@ -3088,7 +3115,7 @@ A jump table for the options with a short description can be found at |Q_op|.
*'helplang'* *'hlg'*
'helplang' 'hlg' string (default: messages language or empty)
global
- Comma separated list of languages. Vim will use the first language
+ Comma-separated list of languages. Vim will use the first language
for which the desired help can be found. The English help will always
be used as a last resort. You can add "en" to prefer English over
another language, but that will only find tags that exist in that
@@ -3108,15 +3135,19 @@ A jump table for the options with a short description can be found at |Q_op|.
when it is |abandon|ed. When on a buffer becomes hidden when it is
|abandon|ed. A buffer displayed in another window does not become
hidden, of course.
+
Commands that move through the buffer list sometimes hide a buffer
- although the 'hidden' option is off: when the buffer is modified,
- 'autowrite' is off or writing is not possible, and the '!' flag was
- used. See also |windows|.
+ although the 'hidden' option is off when these three are true:
+ - the buffer is modified
+ - 'autowrite' is off or writing is not possible
+ - the '!' flag was used
+ Also see |windows|.
+
To hide a specific buffer use the 'bufhidden' option.
'hidden' is set for one command with ":hide {command}" |:hide|.
*'history'* *'hi'*
-'history' 'hi' number (Vim default: 10000, Vi default: 0)
+'history' 'hi' number (default: 10000)
global
A history of ":" commands, and a history of previous search patterns
is remembered. This option decides how many entries may be stored in
@@ -3141,8 +3172,12 @@ A jump table for the options with a short description can be found at |Q_op|.
'hlsearch' 'hls' boolean (default on)
global
When there is a previous search pattern, highlight all its matches.
- The |hl-Search| highlight group determines the highlighting. Note that
- only the matching text is highlighted, any offsets are not applied.
+ The |hl-Search| highlight group determines the highlighting for all
+ matches not under the cursor while the |hl-CurSearch| highlight group
+ (if defined) determines the highlighting for the match under the
+ cursor. If |hl-CurSearch| is not defined, then |hl-Search| is used for
+ both. Note that only the matching text is highlighted, any offsets
+ are not applied.
See also: 'incsearch' and |:match|.
When you get bored looking at the highlighted matches, you can turn it
off with |:nohlsearch|. This does not change the option value, as
@@ -3239,8 +3274,9 @@ A jump table for the options with a short description can be found at |Q_op|.
'inccommand' 'icm' string (default "nosplit")
global
- When nonempty, shows the effects of |:substitute|, |:smagic|, and
- |:snomagic| as you type.
+ When nonempty, shows the effects of |:substitute|, |:smagic|,
+ |:snomagic| and user commands with the |:command-preview| flag as you
+ type.
Possible values:
nosplit Shows the effects of a command incrementally in the
@@ -3248,8 +3284,9 @@ A jump table for the options with a short description can be found at |Q_op|.
split Like "nosplit", but also shows partial off-screen
results in a preview window.
- If the preview is too slow (exceeds 'redrawtime') then 'inccommand' is
- automatically disabled until |Command-line-mode| is done.
+ If the preview for built-in commands is too slow (exceeds
+ 'redrawtime') then 'inccommand' is automatically disabled until
+ |Command-line-mode| is done.
*'include'* *'inc'*
'include' 'inc' string (default "^\s*#\s*include")
@@ -3375,31 +3412,6 @@ A jump table for the options with a short description can be found at |Q_op|.
and there is a letter before it, the completed part is made uppercase.
With 'noinfercase' the match is used as-is.
- *'insertmode'* *'im'* *'noinsertmode'* *'noim'*
-'insertmode' 'im' boolean (default off)
- global
- Makes Vim work in a way that Insert mode is the default mode. Useful
- if you want to use Vim as a modeless editor.
- These Insert mode commands will be useful:
- - Use the cursor keys to move around.
- - Use CTRL-O to execute one Normal mode command |i_CTRL-O|. When
- this is a mapping, it is executed as if 'insertmode' was off.
- Normal mode remains active until the mapping is finished.
- - Use CTRL-L to execute a number of Normal mode commands, then use
- <Esc> to get back to Insert mode. Note that CTRL-L moves the cursor
- left, like <Esc> does when 'insertmode' isn't set. |i_CTRL-L|
-
- These items change when 'insertmode' is set:
- - when starting to edit of a file, Vim goes to Insert mode.
- - <Esc> in Insert mode is a no-op and beeps.
- - <Esc> in Normal mode makes Vim go to Insert mode.
- - CTRL-L in Insert mode is a command, it is not inserted.
- - CTRL-Z in Insert mode suspends Vim, see |CTRL-Z|. *i_CTRL-Z*
- However, when <Esc> is used inside a mapping, it behaves like
- 'insertmode' was not set. This was done to be able to use the same
- mappings with 'insertmode' set or not set.
- When executing commands with |:normal| 'insertmode' is not used.
-
*'isfname'* *'isf'*
'isfname' 'isf' string (default for Windows:
"@,48-57,/,\,.,-,_,+,,,#,$,%,{,},[,],:,@-@,!,~,="
@@ -3463,12 +3475,11 @@ A jump table for the options with a short description can be found at |Q_op|.
option. For '@' only characters up to 255 are used.
Careful: If you change this option, it might break expanding
environment variables. E.g., when '/' is included and Vim tries to
- expand "$HOME/.local/share/nvim/shada/main.shada". Maybe you should
+ expand "$HOME/.local/state/nvim/shada/main.shada". Maybe you should
change 'iskeyword' instead.
*'iskeyword'* *'isk'*
-'iskeyword' 'isk' string (default: @,48-57,_,192-255
- Vi default: @,48-57,_)
+'iskeyword' 'isk' string (default: @,48-57,_,192-255)
local to buffer
Keywords are used in searching and recognizing with many commands:
"w", "*", "[i", etc. It is also used for "\k" in a |pattern|. See
@@ -3524,6 +3535,10 @@ A jump table for the options with a short description can be found at |Q_op|.
jumplist and then jumping to a location.
|jumplist-stack|
+ view When moving through the jumplist, |changelist|,
+ |alternate-file| or using |mark-motions| try to
+ restore the |mark-view| in which the action occurred.
+
*'joinspaces'* *'js'* *'nojoinspaces'* *'nojs'*
'joinspaces' 'js' boolean (default off)
global
@@ -3542,7 +3557,7 @@ A jump table for the options with a short description can be found at |Q_op|.
*'keymodel'* *'km'*
'keymodel' 'km' string (default "")
global
- List of comma separated words, which enable special things that keys
+ List of comma-separated words, which enable special things that keys
can do. These values can be used:
startsel Using a shifted special key starts selection (either
Select mode or Visual mode, depending on "key" being
@@ -3618,7 +3633,7 @@ A jump table for the options with a short description can be found at |Q_op|.
global
Language to use for menu translation. Tells which file is loaded
from the "lang" directory in 'runtimepath': >
- "lang/menu_" . &langmenu . ".vim"
+ "lang/menu_" .. &langmenu .. ".vim"
< (without the spaces). For example, to always use the Dutch menus, no
matter what $LANG is set to: >
:set langmenu=nl_NL.ISO_8859-1
@@ -3650,6 +3665,7 @@ A jump table for the options with a short description can be found at |Q_op|.
0: never
1: only if there are at least two windows
2: always
+ 3: always and ONLY the last window
The screen looks nicer with a status line if you have several
windows, but it takes another screen line. |status-line|
@@ -3660,6 +3676,9 @@ A jump table for the options with a short description can be found at |Q_op|.
executing macros, registers and other commands that have not been
typed. Also, updating the window title is postponed. To force an
update use |:redraw|.
+ This may occasionally cause display errors. It is only meant to be set
+ temporarily when performing an operation where redrawing may cause
+ flickering or cause a slow down.
*'linebreak'* *'lbr'* *'nolinebreak'* *'nolbr'*
'linebreak' 'lbr' boolean (default off)
@@ -3716,7 +3735,7 @@ A jump table for the options with a short description can be found at |Q_op|.
*'lispwords'* *'lw'*
'lispwords' 'lw' string (default is very long)
global or local to buffer |global-local|
- Comma separated list of words that influence the Lisp indenting.
+ Comma-separated list of words that influence the Lisp indenting.
|'lisp'|
*'list'* *'nolist'*
@@ -3737,11 +3756,10 @@ A jump table for the options with a short description can be found at |Q_op|.
changing the way tabs are displayed.
*'listchars'* *'lcs'*
-'listchars' 'lcs' string (default: "tab:> ,trail:-,nbsp:+"
- Vi default: "eol:$")
+'listchars' 'lcs' string (default: "tab:> ,trail:-,nbsp:+")
global or local to window |global-local|
Strings to use in 'list' mode and for the |:list| command. It is a
- comma separated list of string settings.
+ comma-separated list of string settings.
*lcs-eol*
eol:c Character to show at the end of each line. When
@@ -3779,14 +3797,24 @@ A jump table for the options with a short description can be found at |Q_op|.
"space" setting is used. For example,
`:set listchars=multispace:---+` shows ten consecutive
spaces as:
- ---+---+--
+ ---+---+-- ~
*lcs-lead*
lead:c Character to show for leading spaces. When omitted,
leading spaces are blank. Overrides the "space" and
"multispace" settings for leading spaces. You can
combine it with "tab:", for example: >
:set listchars+=tab:>-,lead:.
-< *lcs-trail*
+< *lcs-leadmultispace*
+ leadmultispace:c...
+ Like the |lcs-multispace| value, but for leading
+ spaces only. Also overrides |lcs-lead| for leading
+ multiple spaces.
+ `:set listchars=leadmultispace:---+` shows ten
+ consecutive leading spaces as:
+ ---+---+--XXX ~
+ Where "XXX" denotes the first non-blank characters in
+ the line.
+ *lcs-trail*
trail:c Character to show for trailing spaces. When omitted,
trailing spaces are blank. Overrides the "space" and
"multispace" settings for trailing spaces.
@@ -3918,12 +3946,6 @@ A jump table for the options with a short description can be found at |Q_op|.
set. Note that this is not in milliseconds, like other options that
set a time. This is to be compatible with Nvi.
- *'maxcombine'* *'mco'*
-'maxcombine' 'mco' Removed. |vim-differences|
- Nvim always displays up to 6 combining characters. You can still edit
- text with more than 6 combining characters, you just can't see them.
- Use |g8| or |ga|. See |mbyte-combining|.
-
*'maxfuncdepth'* *'mfd'*
'maxfuncdepth' 'mfd' number (default 100)
global
@@ -4006,8 +4028,7 @@ A jump table for the options with a short description can be found at |Q_op|.
This option cannot be set from a |modeline| or in the |sandbox|.
*'modeline'* *'ml'* *'nomodeline'* *'noml'*
-'modeline' 'ml' boolean (Vim default: on (off for root),
- Vi default: off)
+'modeline' 'ml' boolean (default: on (off for root))
local to buffer
If 'modeline' is on 'modelines' gives the number of lines that is
checked for set commands. If 'modeline' is off or 'modelines' is zero
@@ -4062,14 +4083,14 @@ A jump table for the options with a short description can be found at |Q_op|.
when using "rA" on an "A".
*'more'* *'nomore'*
-'more' boolean (Vim default: on, Vi default: off)
+'more' boolean (default: on)
global
When on, listings pause when the whole screen is filled. You will get
the |more-prompt|. When this option is off there are no pauses, the
listing continues until finished.
*'mouse'*
-'mouse' string (default "")
+'mouse' string (default "nvi")
global
Enables mouse support. For example, to enable the mouse in Normal mode
@@ -4141,7 +4162,7 @@ A jump table for the options with a short description can be found at |Q_op|.
The mouse pointer is restored when the mouse is moved.
*'mousemodel'* *'mousem'*
-'mousemodel' 'mousem' string (default "extend")
+'mousemodel' 'mousem' string (default "popup_setpos")
global
Sets the model to use for the mouse. The name mostly specifies what
the right mouse button is used for:
@@ -4167,7 +4188,7 @@ A jump table for the options with a short description can be found at |Q_op|.
middle click paste paste
In the "popup" model the right mouse button produces a pop-up menu.
- You need to define this first, see |popup-menu|.
+ Nvim creates a default |popup-menu| but you can redefine it.
Note that you can further refine the meaning of buttons with mappings.
See |mouse-overview|. But mappings are NOT used for modeless selection.
@@ -4193,12 +4214,32 @@ A jump table for the options with a short description can be found at |Q_op|.
The 'mousemodel' option is set by the |:behave| command.
+ *mousescroll*
+'mousescroll' string (default "ver:3,hor:6")
+ global
+ This option controls the number of lines / columns to scroll by when
+ scrolling with a mouse. The option is a comma separated list of parts.
+ Each part consists of a direction and a count as follows:
+ direction:count,direction:count
+ Direction is one of either "hor" or "ver", "hor" controls horizontal
+ scrolling and "ver" controls vertical scrolling. Count sets the amount
+ to scroll by for the given direction, it should be a non negative
+ integer. Each direction should be set at most once. If a direction
+ is omitted, a default value is used (6 for horizontal scrolling and 3
+ for vertical scrolling). You can disable mouse scrolling by using
+ a count of 0.
+
+ Example: >
+ :set mousescroll=ver:5,hor:2
+< Will make Nvim scroll 5 lines at a time when scrolling vertically, and
+ scroll 2 columns at a time when scrolling horizontally.
+
*'mouseshape'* *'mouses'* *E547*
'mouseshape' 'mouses' string (default "i:beam,r:beam,s:updown,sd:cross,
m:no,ml:up-arrow,v:rightup-arrow")
global
This option tells Vim what the mouse pointer should look like in
- different modes. The option is a comma separated list of parts, much
+ different modes. The option is a comma-separated list of parts, much
like used for 'guicursor'. Each part consist of a mode/location-list
and an argument-list:
mode-list:shape,mode-list:shape,..
@@ -4317,7 +4358,7 @@ A jump table for the options with a short description can be found at |Q_op|.
|there | 4 there | 1 there | 1 there
*'numberwidth'* *'nuw'*
-'numberwidth' 'nuw' number (Vim default: 4 Vi default: 8)
+'numberwidth' 'nuw' number (default: 4)
local to window
Minimal number of columns to use for the line number. Only relevant
when the 'number' or 'relativenumber' option is set or printing lines
@@ -4443,8 +4484,7 @@ A jump table for the options with a short description can be found at |Q_op|.
Note that typing <F10> in paste mode inserts "<F10>", since in paste
mode everything is inserted literally, except the 'pastetoggle' key
sequence.
- No timeout is used, this means that a multi-key 'pastetoggle' can not
- be triggered manually.
+ When the value has several bytes 'ttimeoutlen' applies.
*'pex'* *'patchexpr'*
'patchexpr' 'pex' string (default "")
@@ -4520,7 +4560,7 @@ A jump table for the options with a short description can be found at |Q_op|.
< To use an environment variable, you probably need to replace the
separator. Here is an example to append $INCL, in which directory
names are separated with a semi-colon: >
- :let &path = &path . "," . substitute($INCL, ';', ',', 'g')
+ :let &path = &path .. "," .. substitute($INCL, ';', ',', 'g')
< Replace the ';' with a ':' or whatever separator is used. Note that
this doesn't work when $INCL contains a comma or white space.
@@ -4636,26 +4676,11 @@ A jump table for the options with a short description can be found at |Q_op|.
nudged to fit on the screen.
*'pyxversion'* *'pyx'*
-'pyxversion' 'pyx' number (default depends on the build)
+'pyxversion' 'pyx' number (default 3)
global
Specifies the python version used for pyx* functions and commands
- |python_x|. The default value is as follows:
-
- |provider| installed Default ~
- |+python| and |+python3| 0
- only |+python| 2
- only |+python3| 3
-
- Available values are 0, 2 and 3.
- If 'pyxversion' is 0, it is set to 2 or 3 after the first execution of
- any python2/3 commands or functions. E.g. `:py` sets to 2, and `:py3`
- sets to 3. `:pyx` sets it to 3 if Python 3 is available, otherwise sets
- to 2 if Python 2 is available.
- See also: |has-pythonx|
-
- If only |+python| or |+python3| are available,
- 'pyxversion' has no effect. The pyx* functions and commands are
- always the same as the installed version.
+ |python_x|. As only Python 3 is supported, this always has the value
+ `3`. Setting any other value is an error.
This option cannot be set from a |modeline| or in the |sandbox|, for
security reasons.
@@ -4774,15 +4799,6 @@ A jump table for the options with a short description can be found at |Q_op|.
'number', see |number_relativenumber| for all combinations of the two
options.
- *'remap'* *'noremap'*
-'remap' boolean (default on)
- global
- Allows for mappings to work recursively. If you do not want this for
- a single entry, use the :noremap[!] command.
- NOTE: To avoid portability problems with Vim scripts, always keep
- this option at the default "on". Only switch it off when working with
- old Vi scripts.
-
*'report'*
'report' number (default 2)
global
@@ -4837,9 +4853,11 @@ A jump table for the options with a short description can be found at |Q_op|.
45% relative position in the file
If 'rulerformat' is set, it will determine the contents of the ruler.
Each window has its own ruler. If a window has a status line, the
- ruler is shown there. Otherwise it is shown in the last line of the
- screen. If the statusline is given by 'statusline' (i.e. not empty),
- this option takes precedence over 'ruler' and 'rulerformat'
+ ruler is shown there. If a window doesn't have a status line and
+ 'cmdheight' is 0, the ruler is not shown. Otherwise it is shown in
+ the last line of the screen. If the statusline is given by
+ 'statusline' (i.e. not empty), this option takes precedence over
+ 'ruler' and 'rulerformat'.
If the number of characters displayed is different from the number of
bytes in the text (e.g., for a TAB or a multibyte character), both
the text column (byte number) and the screen column are shown,
@@ -4918,9 +4936,12 @@ A jump table for the options with a short description can be found at |Q_op|.
but are not part of the Nvim distribution. XDG_DATA_DIRS defaults
to /usr/local/share/:/usr/share/, so system administrators are
expected to install site plugins to /usr/share/nvim/site.
- 5. $VIMRUNTIME, for files distributed with Neovim.
+ 5. Applications state home directory, for files that contain your
+ session state (eg. backupdir, viewdir, undodir, etc).
+ Given by `stdpath("state")`. |$XDG_STATE_HOME|
+ 6. $VIMRUNTIME, for files distributed with Neovim.
*after-directory*
- 6, 7, 8, 9. In after/ subdirectories of 1, 2, 3 and 4, with reverse
+ 7, 8, 9, 10. In after/ subdirectories of 1, 2, 3 and 4, with reverse
ordering. This is for preferences to overrule or add to the
distributed defaults or system-wide settings (rarely needed).
@@ -5084,7 +5105,7 @@ A jump table for the options with a short description can be found at |Q_op|.
*'selectmode'* *'slm'*
'selectmode' 'slm' string (default "")
global
- This is a comma separated list of words, which specifies when to start
+ This is a comma-separated list of words, which specifies when to start
Select mode instead of Visual mode, when a selection is started.
Possible values:
mouse when using the mouse
@@ -5095,11 +5116,9 @@ A jump table for the options with a short description can be found at |Q_op|.
*'sessionoptions'* *'ssop'*
'sessionoptions' 'ssop' string (default: "blank,buffers,curdir,folds,
- help,tabpages,winsize"
- Vi default: "blank,buffers,curdir,folds,
- help,options,tabpages,winsize")
+ help,tabpages,winsize")
global
- Changes the effect of the |:mksession| command. It is a comma
+ Changes the effect of the |:mksession| command. It is a comma-
separated list of words. Each word enables saving and restoring
something:
word save and restore ~
@@ -5137,13 +5156,12 @@ A jump table for the options with a short description can be found at |Q_op|.
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
+'shada' 'sd' string (default for
Win32: !,'100,<50,s10,h,rA:,rB:
- others: !,'100,<50,s10,h
- Vi default: "")
+ others: !,'100,<50,s10,h)
global
When non-empty, the shada file is read upon startup and written
- when exiting Vim (see |shada-file|). The string should be a comma
+ when exiting Vim (see |shada-file|). The string should be a comma-
separated list of parameters, each consisting of a single character
identifying the particular parameter, followed by a number or string
which specifies the value of that parameter. If a particular
@@ -5303,9 +5321,9 @@ A jump table for the options with a short description can be found at |Q_op|.
unescaping, so to keep yourself sane use |:let-&| like shown above.
*shell-powershell*
To use PowerShell: >
- let &shell = has('win32') ? 'powershell' : 'pwsh'
+ let &shell = executable('pwsh') ? 'pwsh' : 'powershell'
let &shellcmdflag = '-NoLogo -NoProfile -ExecutionPolicy RemoteSigned -Command [Console]::InputEncoding=[Console]::OutputEncoding=[System.Text.Encoding]::UTF8;'
- let &shellredir = '2>&1 | Out-File -Encoding UTF8 %s; exit $LastExitCode'
+ let &shellredir = '-RedirectStandardOutput %s -NoNewWindow -Wait'
let &shellpipe = '2>&1 | Out-File -Encoding UTF8 %s; exit $LastExitCode'
set shellquote= shellxquote=
@@ -5418,7 +5436,7 @@ A jump table for the options with a short description can be found at |Q_op|.
< Also see 'completeslash'.
*'shelltemp'* *'stmp'* *'noshelltemp'* *'nostmp'*
-'shelltemp' 'stmp' boolean (Vim default on, Vi default off)
+'shelltemp' 'stmp' boolean (default on)
global
When on, use temp files for shell commands. When off use a pipe.
When using a pipe is not possible temp files are used anyway.
@@ -5467,7 +5485,7 @@ A jump table for the options with a short description can be found at |Q_op|.
function to get the effective shiftwidth value.
*'shortmess'* *'shm'*
-'shortmess' 'shm' string (Vim default "filnxtToOF", Vi default: "S")
+'shortmess' 'shm' string (default "filnxtToOF")
global
This option helps to avoid all the |hit-enter| prompts caused by file
messages, for example with CTRL-G, and to avoid some other messages.
@@ -5475,7 +5493,7 @@ A jump table for the options with a short description can be found at |Q_op|.
flag meaning when present ~
f use "(3 of 5)" instead of "(file 3 of 5)"
i use "[noeol]" instead of "[Incomplete last line]"
- l use "999L, 888C" instead of "999 lines, 888 characters"
+ l use "999L, 888B" instead of "999 lines, 888 bytes"
m use "[+]" instead of "[Modified]"
n use "[New]" instead of "[New File]"
r use "[RO]" instead of "[readonly]"
@@ -5507,8 +5525,7 @@ A jump table for the options with a short description can be found at |Q_op|.
"Pattern not found", "Back at original", etc.
q use "recording" instead of "recording @a"
F don't give the file info when editing a file, like `:silent`
- was used for the command; note that this also affects messages
- from autocommands
+ was used for the command
S do not show search count message when searching, e.g.
"[1/5]"
@@ -5542,10 +5559,11 @@ A jump table for the options with a short description can be found at |Q_op|.
:setlocal showbreak=NONE
<
*'showcmd'* *'sc'* *'noshowcmd'* *'nosc'*
-'showcmd' 'sc' boolean (Vim default: on, Vi default: off)
+'showcmd' 'sc' boolean (default: on)
global
Show (partial) command in the last line of the screen. Set this
option off if your terminal is slow.
+ The option is disabled if 'cmdheight' is 0.
In Visual mode the size of the selected area is shown:
- When selecting characters within a line, the number of characters.
If the number of bytes is different it is also displayed: "2-6"
@@ -5588,10 +5606,11 @@ A jump table for the options with a short description can be found at |Q_op|.
Note: Use of the short form is rated PG.
*'showmode'* *'smd'* *'noshowmode'* *'nosmd'*
-'showmode' 'smd' boolean (Vim default: on, Vi default: off)
+'showmode' 'smd' boolean (default: on)
global
If in Insert, Replace or Visual mode put a message on the last line.
The |hl-ModeMsg| highlight group determines the highlighting.
+ The option is disabled if 'cmdheight' is 0.
*'showtabline'* *'stal'*
'showtabline' 'stal' number (default 1)
@@ -5660,7 +5679,7 @@ A jump table for the options with a short description can be found at |Q_op|.
Note regarding 'orphaned signs': with signcolumn numbers higher than
1, deleting lines will also remove the associated signs automatically,
in contrast to the default Vim behavior of keeping and grouping them.
- This is done in order for the signcolumn appearence not appear weird
+ This is done in order for the signcolumn appearance not appear weird
during line deletion.
@@ -5762,7 +5781,7 @@ A jump table for the options with a short description can be found at |Q_op|.
commands. It must end in ".{encoding}.add". You need to include the
path, otherwise the file is placed in the current directory.
*E765*
- It may also be a comma separated list of names. A count before the
+ It may also be a comma-separated list of names. A count before the
|zg| and |zw| commands can be used to access each. This allows using
a personal word list file and a project word list file.
When a word is added while this option is empty Vim will set it for
@@ -5782,7 +5801,7 @@ A jump table for the options with a short description can be found at |Q_op|.
*'spelllang'* *'spl'*
'spelllang' 'spl' string (default "en")
local to buffer
- A comma separated list of word list names. When the 'spell' option is
+ A comma-separated list of word list names. When the 'spell' option is
on spellchecking will be done for these languages. Example: >
set spelllang=en_us,nl,medical
< This means US English, Dutch and medical words are recognized. Words
@@ -5822,7 +5841,7 @@ A jump table for the options with a short description can be found at |Q_op|.
*'spelloptions'* *'spo'*
'spelloptions' 'spo' string (default "")
local to buffer
- A comma separated list of options for spell checking:
+ A comma-separated list of options for spell checking:
camel When a word is CamelCased, assume "Cased" is a
separate word: every upper-case character in a word
that comes after a lower case character indicates the
@@ -5919,7 +5938,7 @@ A jump table for the options with a short description can be found at |Q_op|.
*'statusline'* *'stl'* *E540* *E542*
'statusline' 'stl' string (default empty)
global or local to window |global-local|
- When nonempty, this option determines the content of the status line.
+ When non-empty, this option determines the content of the status line.
Also see |status-line|.
The option consists of printf style '%' items interspersed with
@@ -5942,7 +5961,7 @@ A jump table for the options with a short description can be found at |Q_op|.
empty to avoid further errors. Otherwise screen updating would loop.
Note that the only effect of 'ruler' when this option is set (and
- 'laststatus' is 2) is controlling the output of |CTRL-G|.
+ 'laststatus' is 2 or 3) is controlling the output of |CTRL-G|.
field meaning ~
- Left justify the item. The default is right justified
@@ -6026,7 +6045,7 @@ A jump table for the options with a short description can be found at |Q_op|.
the label, e.g.: %3Xclose%X. Use %999X for a "close current
tab" label. Clicking this label with left mouse button closes
specified tab page.
- @ N For 'tabline': start of execute function label. Use %X or %T to
+ @ N Start of execute function label. Use %X or %T to
end the label, e.g.: %10@SwitchBuffer@foo.c%X. Clicking this
label runs specified function: in the example when clicking once
using left mouse button on "foo.c" "SwitchBuffer(10, 1, 'l',
@@ -6050,8 +6069,6 @@ A jump table for the options with a short description can be found at |Q_op|.
is a bug that denotes that new mouse button recognition was
added without modifying code that reacts on mouse clicks on
this label.
- Note: to test whether your version of Neovim contains this
- feature use `has('tablineat')`.
< - Where to truncate line if too long. Default is at the start.
No width fields allowed.
= - Separation point between alignment sections. Each section will
@@ -6095,10 +6112,9 @@ A jump table for the options with a short description can be found at |Q_op|.
If the statusline is not updated when you want it (e.g., after setting
a variable that's used in an expression), you can force an update by
- setting an option without changing its value. Example: >
- :let &ro = &ro
+ using `:redrawstatus`.
-< A result of all digits is regarded a number for display purposes.
+ A result of all digits is regarded a number for display purposes.
Otherwise the result is taken as flag text and applied to the rules
described above.
@@ -6142,7 +6158,7 @@ A jump table for the options with a short description can be found at |Q_op|.
*'suffixesadd'* *'sua'*
'suffixesadd' 'sua' string (default "")
local to buffer
- Comma separated list of suffixes, which are used when searching for a
+ Comma-separated list of suffixes, which are used when searching for a
file for the "gf", "[I", etc. commands. Example: >
:set suffixesadd=.java
<
@@ -6174,7 +6190,7 @@ A jump table for the options with a short description can be found at |Q_op|.
This option controls the behavior when switching between buffers.
Mostly for |quickfix| commands some values are also used for other
commands, as mentioned below.
- Possible values (comma separated list):
+ Possible values (comma-separated list):
useopen If included, jump to the first open window that
contains the specified buffer (if there is one).
Otherwise: Do not examine other windows.
@@ -6235,7 +6251,7 @@ A jump table for the options with a short description can be found at |Q_op|.
*'tabline'* *'tal'*
'tabline' 'tal' string (default empty)
global
- When nonempty, this option determines the content of the tab pages
+ When non-empty, this option determines the content of the tab pages
line at the top of the Vim window. When empty Vim will use a default
tab pages line. See |setting-tabline| for more info.
@@ -6268,10 +6284,11 @@ A jump table for the options with a short description can be found at |Q_op|.
'tabstop' 'ts' number (default 8)
local to buffer
Number of spaces that a <Tab> in the file counts for. Also see
- |:retab| command, and 'softtabstop' option.
+ the |:retab| command, and the 'softtabstop' option.
Note: Setting 'tabstop' to any other value than 8 can make your file
- appear wrong in many places (e.g., when printing it).
+ appear wrong in many places, e.g., when printing it.
+ The value must be more than 0 and less than 10000.
There are four main ways to use tabs in Vim:
1. Always keep 'tabstop' at 8, set 'softtabstop' and 'shiftwidth' to 4
@@ -6325,9 +6342,10 @@ A jump table for the options with a short description can be found at |Q_op|.
linear search can be avoided when case is ignored. Use a value of '2'
in the "!_TAG_FILE_SORTED" line for this. A tag file can be case-fold
sorted with the -f switch to "sort" in most unices, as in the command:
- "sort -f -o tags tags". For "Exuberant ctags" version 5.x or higher
- (at least 5.5) the --sort=foldcase switch can be used for this as
- well. Note that case must be folded to uppercase for this to work.
+ "sort -f -o tags tags". For Universal ctags and Exuberant ctags
+ version 5.x or higher (at least 5.5) the --sort=foldcase switch can be
+ used for this as well. Note that case must be folded to uppercase for
+ this to work.
By default, tag searches are case-sensitive. Case is ignored when
'ignorecase' is set and 'tagcase' is "followic", or when 'tagcase' is
@@ -6370,7 +6388,7 @@ A jump table for the options with a short description can be found at |Q_op|.
If non-zero, tags are significant up to this number of characters.
*'tagrelative'* *'tr'* *'notagrelative'* *'notr'*
-'tagrelative' 'tr' boolean (Vim default: on, Vi default: off)
+'tagrelative' 'tr' boolean (default: on)
global
If on and using a tags file in another directory, file names in that
tags file are relative to the directory where the tags file is.
@@ -6420,7 +6438,7 @@ A jump table for the options with a short description can be found at |Q_op|.
'arabicshape' is ignored, but 'rightleft' isn't changed automatically.
For further details see |arabic.txt|.
- *'termguicolors'* *'tgc'*
+ *'termguicolors'* *'tgc'* *'notermguicolors'* *'notgc'*
'termguicolors' 'tgc' boolean (default off)
global
Enables 24-bit RGB color in the |TUI|. Uses "gui" |:highlight|
@@ -6430,7 +6448,7 @@ A jump table for the options with a short description can be found at |Q_op|.
*'termpastefilter'* *'tpf'*
'termpastefilter' 'tpf' string (default: "BS,HT,ESC,DEL")
global
- A comma separated list of options for specifying control characters
+ A comma-separated list of options for specifying control characters
to be removed from the text pasted into the terminal window. The
supported values are:
@@ -6450,13 +6468,6 @@ A jump table for the options with a short description can be found at |Q_op|.
C1 Control characters 0x80...0x9F
- *'terse'* *'noterse'*
-'terse' boolean (default off)
- global
- When set: Add 's' flag to 'shortmess' option (this makes the message
- for a search that hits the start or end of the file not being
- displayed). When reset: Remove 's' flag from 'shortmess' option.
-
*'textwidth'* *'tw'*
'textwidth' 'tw' number (default 0)
local to buffer
@@ -6582,7 +6593,7 @@ A jump table for the options with a short description can be found at |Q_op|.
This option cannot be set in a modeline when 'modelineexpr' is off.
Example: >
- :auto BufEnter * let &titlestring = hostname() . "/" . expand("%:p")
+ :auto BufEnter * let &titlestring = hostname() .. "/" .. expand("%:p")
:set title titlestring=%<%F%=%l/%L-%P titlelen=70
< The value of 'titlelen' is used to align items in the middle or right
of the available space.
@@ -6594,11 +6605,8 @@ A jump table for the options with a short description can be found at |Q_op|.
NOTE: Use of special characters in 'titlestring' may cause the display
to be garbled (e.g., when it contains a CR or NL character).
- *'ttyfast'* *'tf'* *'nottyfast'* *'notf'*
-'ttyfast' 'tf' Removed. |vim-differences|
-
*'undodir'* *'udir'* *E5003*
-'undodir' 'udir' string (default "$XDG_DATA_HOME/nvim/undo//")
+'undodir' 'udir' string (default "$XDG_STATE_HOME/nvim/undo//")
global
List of directory names for undo files, separated with commas.
See 'backupdir' for details of the format.
@@ -6766,8 +6774,8 @@ A jump table for the options with a short description can be found at |Q_op|.
global
When bigger than zero, Vim will give messages about what it is doing.
Currently, these messages are given:
- >= 1 When the shada file is read or written.
- >= 2 When a file is ":source"'ed.
+ >= 1 Lua assignments to options, mappings, etc.
+ >= 2 When a file is ":source"'ed and when the shada file is read or written..
>= 3 UI info, terminal capabilities
>= 4 Shell commands.
>= 5 Every searched tags file and include file.
@@ -6799,7 +6807,7 @@ A jump table for the options with a short description can be found at |Q_op|.
displayed when 'verbosefile' is set.
*'viewdir'* *'vdir'*
-'viewdir' 'vdir' string (default: "$XDG_DATA_HOME/nvim/view//")
+'viewdir' 'vdir' string (default: "$XDG_STATE_HOME/nvim/view//")
global
Name of the directory where to store files for |:mkview|.
This option cannot be set from a |modeline| or in the |sandbox|, for
@@ -6808,7 +6816,7 @@ A jump table for the options with a short description can be found at |Q_op|.
*'viewoptions'* *'vop'*
'viewoptions' 'vop' string (default: "folds,cursor,curdir")
global
- Changes the effect of the |:mkview| command. It is a comma separated
+ Changes the effect of the |:mkview| command. It is a comma-separated
list of words. Each word enables saving and restoring something:
word save and restore ~
cursor cursor position in file and in window
@@ -6823,12 +6831,16 @@ A jump table for the options with a short description can be found at |Q_op|.
*'virtualedit'* *'ve'*
'virtualedit' 've' string (default "")
- global
- A comma separated list of these words:
+ 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
@@ -6859,7 +6871,7 @@ A jump table for the options with a short description can be found at |Q_op|.
has been changed.
*'whichwrap'* *'ww'*
-'whichwrap' 'ww' string (Vim default: "b,s", Vi default: "")
+'whichwrap' 'ww' string (default: "b,s")
global
Allow specified keys that move the cursor left/right to move to the
previous/next line when the cursor is on the first/last character in
@@ -6889,7 +6901,7 @@ A jump table for the options with a short description can be found at |Q_op|.
makes "dl", "cl", "yl" etc. work normally.
*'wildchar'* *'wc'*
-'wildchar' 'wc' number (Vim default: <Tab>, Vi default: CTRL-E)
+'wildchar' 'wc' number (default: <Tab>)
global
Character you have to type to start wildcard expansion in the
command-line, as specified with 'wildmode'.
@@ -6977,7 +6989,7 @@ A jump table for the options with a short description can be found at |Q_op|.
'wildmode' 'wim' string (default: "full")
global
Completion mode that is used for the character specified with
- 'wildchar'. It is a comma separated list of up to four parts. Each
+ 'wildchar'. It is a comma-separated list of up to four parts. Each
part specifies what to do for each consecutive use of 'wildchar'. The
first part specifies the behavior for the first use of 'wildchar',
The second part for the second use, etc.
@@ -7024,7 +7036,7 @@ A jump table for the options with a short description can be found at |Q_op|.
'wildoptions' 'wop' string (default "pum,tagfile")
global
List of words that change how |cmdline-completion| is done.
- pum Display the completion matches using the popupmenu
+ pum Display the completion matches using the popup menu
in the same style as the |ins-completion-menu|.
tagfile When using CTRL-D to list matching tags, the kind of
tag and the file of the tag is listed. Only one match
@@ -7051,6 +7063,23 @@ A jump table for the options with a short description can be found at |Q_op|.
key is never used for the menu.
This option is not used for <F10>; on Win32.
+ *'winbar'* *'wbr'*
+'winbar' 'wbr' string (default empty)
+ global or local to window |global-local|
+ When non-empty, this option enables the window bar and determines its
+ contents. The window bar is a bar that's shown at the top of every
+ window with it enabled. The value of 'winbar' is evaluated like with
+ 'statusline'.
+
+ When changing something that is used in 'winbar' that does not trigger
+ it to be updated, use |:redrawstatus|.
+
+ Floating windows do not use the global value of 'winbar'. The
+ window-local value of 'winbar' must be set for a floating window to
+ have a window bar.
+
+ This option cannot be set in a modeline when 'modelineexpr' is off.
+
*'winblend'* *'winbl'*
'winblend' 'winbl' number (default 0)
local to window
@@ -7192,7 +7221,7 @@ A jump table for the options with a short description can be found at |Q_op|.
starts. When typing text beyond this limit, an <EOL> will be inserted
and inserting continues on the next line.
Options that add a margin, such as 'number' and 'foldcolumn', cause
- the text width to be further reduced. This is Vi compatible.
+ the text width to be further reduced.
When 'textwidth' is non-zero, this option is not used.
See also 'formatoptions' and |ins-textwidth|.
diff --git a/runtime/doc/pattern.txt b/runtime/doc/pattern.txt
index 634145da3e..371a210847 100644
--- a/runtime/doc/pattern.txt
+++ b/runtime/doc/pattern.txt
@@ -71,12 +71,18 @@ N Repeat the latest "/" or "?" [count] times in
Only whole keywords are searched for, like with the
command "/\<keyword\>". |exclusive|
'ignorecase' is used, 'smartcase' is not.
+ *v_star-default*
+ In Visual mode, search forward for the current selection.
+ |default-mappings|
*#*
# Same as "*", but search backward. The pound sign
(character 163) also works. If the "#" key works as
backspace, try using "stty erase <BS>" before starting
Vim (<BS> is CTRL-H or a real backspace).
+ *v_#-default*
+ In Visual mode, search backward for the current selection.
+ |default-mappings|
*gstar*
g* Like "*", but don't put "\<" and "\>" around the word.
@@ -153,9 +159,10 @@ index, on which the cursor is. This can look like this: >
Note: the count does not take offset into account.
When no match is found you get the error: *E486* Pattern not found
-Note that for the |:global| command this behaves like a normal message, for Vi
-compatibility. For the |:s| command the "e" flag can be used to avoid the
-error message |:s_flags|.
+Note that for the `:global` command, you get a normal message "Pattern not
+found", for Vi compatibility.
+For the |:s| command the "e" flag can be used to avoid the error message
+|:s_flags|.
*search-offset* *{offset}*
These commands search for the specified pattern. With "/" and "?" an
@@ -913,17 +920,24 @@ $ At end of pattern or in front of "\|", "\)" or "\n" ('magic' on):
becomes invalid. Vim doesn't automatically update the matches.
Similar to moving the cursor for "\%#" |/\%#|.
- */\%l* */\%>l* */\%<l* *E951*
+ */\%l* */\%>l* */\%<l* *E951* *E1204*
\%23l Matches in a specific line.
\%<23l Matches above a specific line (lower line number).
\%>23l Matches below a specific line (higher line number).
- These three can be used to match specific lines in a buffer. The "23"
+\%.l Matches at the cursor line.
+\%<.l Matches above the cursor line.
+\%>.l Matches below the cursor line.
+ These six can be used to match specific lines in a buffer. The "23"
can be any line number. The first line is 1.
WARNING: When inserting or deleting lines Vim does not automatically
update the matches. This means Syntax highlighting quickly becomes
- wrong.
+ wrong. Also when referring to the cursor position (".") and
+ the cursor moves the display isn't updated for this change. An update
+ is done when using the |CTRL-L| command (the whole screen is updated).
Example, to highlight the line where the cursor currently is: >
- :exe '/\%' . line(".") . 'l.*'
+ :exe '/\%' .. line(".") .. 'l'
+< Alternatively use: >
+ /\%.l
< When 'hlsearch' is set and you move the cursor around and make changes
this will clearly show when the match is updated or not.
@@ -931,15 +945,22 @@ $ At end of pattern or in front of "\|", "\)" or "\n" ('magic' on):
\%23c Matches in a specific column.
\%<23c Matches before a specific column.
\%>23c Matches after a specific column.
- These three can be used to match specific columns in a buffer or
- string. The "23" can be any column number. The first column is 1.
- Actually, the column is the byte number (thus it's not exactly right
- for multibyte characters).
+\%.c Matches at the cursor column.
+\%<.c Matches before the cursor column.
+\%>.c Matches after the cursor column.
+ These six can be used to match specific columns in a buffer or string.
+ The "23" can be any column number. The first column is 1. Actually,
+ the column is the byte number (thus it's not exactly right for
+ multibyte characters).
WARNING: When inserting or deleting text Vim does not automatically
update the matches. This means Syntax highlighting quickly becomes
- wrong.
+ wrong. Also when referring to the cursor position (".") and
+ the cursor moves the display isn't updated for this change. An update
+ is done when using the |CTRL-L| command (the whole screen is updated).
Example, to highlight the column where the cursor currently is: >
- :exe '/\%' . col(".") . 'c'
+ :exe '/\%' .. col(".") .. 'c'
+< Alternatively use: >
+ /\%.c
< When 'hlsearch' is set and you move the cursor around and make changes
this will clearly show when the match is updated or not.
Example for matching a single byte in column 44: >
@@ -950,8 +971,11 @@ $ At end of pattern or in front of "\|", "\)" or "\n" ('magic' on):
\%23v Matches in a specific virtual column.
\%<23v Matches before a specific virtual column.
\%>23v Matches after a specific virtual column.
- These three can be used to match specific virtual columns in a buffer
- or string. When not matching with a buffer in a window, the option
+\%.v Matches at the current virtual column.
+\%<.v Matches before the current virtual column.
+\%>.v Matches after the current virtual column.
+ These six can be used to match specific virtual columns in a buffer or
+ string. When not matching with a buffer in a window, the option
values of the current window are used (e.g., 'tabstop').
The "23" can be any column number. The first column is 1.
Note that some virtual column positions will never match, because they
@@ -959,13 +983,18 @@ $ At end of pattern or in front of "\|", "\)" or "\n" ('magic' on):
one screen character.
WARNING: When inserting or deleting text Vim does not automatically
update highlighted matches. This means Syntax highlighting quickly
- becomes wrong.
+ becomes wrong. Also when referring to the cursor position (".") and
+ the cursor moves the display isn't updated for this change. An update
+ is done when using the |CTRL-L| command (the whole screen is updated).
Example, to highlight all the characters after virtual column 72: >
/\%>72v.*
< When 'hlsearch' is set and you move the cursor around and make changes
this will clearly show when the match is updated or not.
To match the text up to column 17: >
/^.*\%17v
+< To match all characters after the current virtual column (where the
+ cursor is): >
+ /\%>.v.*
< Column 17 is not included, because this is a |/zero-width| match. To
include the column use: >
/^.*\%17v.
@@ -1421,5 +1450,38 @@ Finally, these constructs are unique to Perl:
are suggested to use ":match" for manual matching and
":2match" for another plugin.
+==============================================================================
+11. Fuzzy matching *fuzzy-matching*
+
+Fuzzy matching refers to matching strings using a non-exact search string.
+Fuzzy matching will match a string, if all the characters in the search string
+are present anywhere in the string in the same order. Case is ignored. In a
+matched string, other characters can be present between two consecutive
+characters in the search string. If the search string has multiple words, then
+each word is matched separately. So the words in the search string can be
+present in any order in a string.
+
+Fuzzy matching assigns a score for each matched string based on the following
+criteria:
+ - The number of sequentially matching characters.
+ - The number of characters (distance) between two consecutive matching
+ characters.
+ - Matches at the beginning of a word
+ - Matches at a camel case character (e.g. Case in CamelCase)
+ - Matches after a path separator or a hyphen.
+ - The number of unmatched characters in a string.
+The matching string with the highest score is returned first.
+
+For example, when you search for the "get pat" string using fuzzy matching, it
+will match the strings "GetPattern", "PatternGet", "getPattern", "patGetter",
+"getSomePattern", "MatchpatternGet" etc.
+
+The functions |matchfuzzy()| and |matchfuzzypos()| can be used to fuzzy search
+a string in a List of strings. The matchfuzzy() function returns a List of
+matching strings. The matchfuzzypos() functions returns the List of matches,
+the matching positions and the fuzzy match scores.
+
+The "f" flag of `:vimgrep` enables fuzzy matching.
+
vim:tw=78:ts=8:noet:ft=help:norl:
diff --git a/runtime/doc/pi_health.txt b/runtime/doc/pi_health.txt
index 179c1066cd..04e04b5165 100644
--- a/runtime/doc/pi_health.txt
+++ b/runtime/doc/pi_health.txt
@@ -48,27 +48,26 @@ Commands *health-commands*
:checkhealth vim*
<
==============================================================================
-Lua Functions *health-functions-lua* *health-lua*
+Lua Functions *health-functions-lua* *health-lua* *vim.health*
The Lua "health" module can be used to create new healthchecks (see also
-|health-functions-vim|). To get started, simply use: >
- local health = require('health')
-<
-health.report_start({name}) *health.report_start()*
+|health-functions-vim|). To get started, simply use:
+
+vim.health.report_start({name}) *vim.health.report_start()*
Starts a new report. Most plugins should call this only once, but if
you want different sections to appear in your report, call this once
per section.
-health.report_info({msg}) *health.report_info()*
+vim.health.report_info({msg}) *vim.health.report_info()*
Reports an informational message.
-health.report_ok({msg}) *health.report_ok()*
+vim.health.report_ok({msg}) *vim.health.report_ok()*
Reports a "success" message.
-health.report_warn({msg} [, {advice}]) *health.report_warn()*
+vim.health.report_warn({msg} [, {advice}]) *vim.health.report_warn()*
Reports a warning. {advice} is an optional List of suggestions.
-health.report_error({msg} [, {advice}]) *health.report_error()*
+vim.health.report_error({msg} [, {advice}]) *vim.health.report_error()*
Reports an error. {advice} is an optional List of suggestions.
==============================================================================
@@ -98,15 +97,14 @@ Copy this sample code into `lua/foo/health/init.lua` or `lua/foo/health.lua`,
replacing "foo" in the path with your plugin name: >
local M = {}
- local health = require("health")
M.check = function()
- health.report_start("my_plugin report")
+ vim.health.report_start("my_plugin report")
-- make sure setup function parameters are ok
if check_setup() then
- health.report_ok("Setup function is correct")
+ vim.health.report_ok("Setup function is correct")
else
- health.report_error("Setup function is incorrect")
+ vim.health.report_error("Setup function is incorrect")
end
-- do some more checking
-- ...
diff --git a/runtime/doc/pi_msgpack.txt b/runtime/doc/pi_msgpack.txt
index 1dbd268038..801c56e49f 100644
--- a/runtime/doc/pi_msgpack.txt
+++ b/runtime/doc/pi_msgpack.txt
@@ -88,7 +88,7 @@ msgpack#type({msgpack-value}) *msgpack#type()*
Returns name of the key in |v:msgpack_types| that represents
{msgpack-value} type. Never returns zero: this function returns
msgpack type which will be dumped by |msgpackdump()| should it receive
- a list with singe {msgpack-value} as input.
+ a list with single {msgpack-value} as input.
msgpack#deepcopy({msgpack-value}) *msgpack#deepcopy()*
Like |deepcopy()|, but works correctly with |msgpack-special-dict|
diff --git a/runtime/doc/pi_netrw.txt b/runtime/doc/pi_netrw.txt
index 3ac61be6f2..6f7b53722c 100644
--- a/runtime/doc/pi_netrw.txt
+++ b/runtime/doc/pi_netrw.txt
@@ -440,11 +440,11 @@ settings are described below, in |netrw-browser-options|, and in
*g:netrw_use_errorwindow* =2: messages from netrw will use a popup window
Move the mouse and pause to remove the popup window.
- (default value if popup windows are availble)
+ (default value if popup windows are available)
=1 : messages from netrw will use a separate one
line window. This window provides reliable
delivery of messages.
- (default value if popup windows are not availble)
+ (default value if popup windows are not available)
=0 : messages from netrw will use echoerr ;
messages don't always seem to show up this
way, but one doesn't have to quit the window.
@@ -968,7 +968,7 @@ itself:
fun! NetReadFixup(method, line1, line2)
if method == 3 " ftp (no <.netrc>)
let fourblanklines= line2 - 3
- silent fourblanklines.",".line2."g/^\s*/d"
+ silent fourblanklines .. "," .. line2 .. "g/^\s*/d"
endif
endfunction
endif
@@ -1198,7 +1198,7 @@ If there are marked files and/or directories, mb will add them to the bookmark
list.
*netrw-:NetrwMB*
-Addtionally, one may use :NetrwMB to bookmark files or directories. >
+Additionally, one may use :NetrwMB to bookmark files or directories. >
:NetrwMB[!] [files/directories]
@@ -1975,7 +1975,7 @@ To use this function, simply assign its output to |g:netrw_list_hide| option. >
Example: let g:netrw_list_hide= netrw_gitignore#Hide('my_gitignore_file')
Function can take additional files with git-ignore patterns.
- Example: g:netrw_list_hide= netrw_gitignore#Hide() . '.*\.swp$'
+ Example: let g:netrw_list_hide= netrw_gitignore#Hide() .. '.*\.swp$'
Combining 'netrw_gitignore#Hide' with custom patterns.
<
@@ -2814,7 +2814,7 @@ your browsing preferences. (see also: |netrw-settings|)
= 2: wide listing (multiple files in columns)
= 3: tree style listing
- *g:netrw_list_hide* comma separated pattern list for hiding files
+ *g:netrw_list_hide* comma-separated pattern list for hiding files
Patterns are regular expressions (see |regexp|)
There's some special support for git-ignore
files: you may add the output from the helper
@@ -2824,7 +2824,7 @@ your browsing preferences. (see also: |netrw-settings|)
Examples:
let g:netrw_list_hide= '.*\.swp$'
- let g:netrw_list_hide= netrw_gitignore#Hide().'.*\.swp$'
+ let g:netrw_list_hide= netrw_gitignore#Hide() .. '.*\.swp$'
default: ""
*g:netrw_localcopycmd* ="cp" Linux/Unix/MacOS/Cygwin
diff --git a/runtime/doc/print.txt b/runtime/doc/print.txt
index f54d0429a6..924fab175e 100644
--- a/runtime/doc/print.txt
+++ b/runtime/doc/print.txt
@@ -127,21 +127,21 @@ file: >
system(['lpr']
+ (empty(&printdevice)?[]:['-P', &printdevice])
+ [v:fname_in])
- . delete(v:fname_in)
+ .. delete(v:fname_in)
+ v:shell_error
On MS-Dos and MS-Windows machines the default is to copy the file to the
currently specified printdevice: >
system(['copy', v:fname_in, empty(&printdevice)?'LPT1':&printdevice])
- . delete(v:fname_in)
+ .. delete(v:fname_in)
If you change this option, using a function is an easy way to avoid having to
escape all the spaces. Example: >
:set printexpr=PrintFile(v:fname_in)
:function PrintFile(fname)
- : call system("ghostview " . a:fname)
+ : call system("ghostview " .. a:fname)
: call delete(a:fname)
: return v:shell_error
:endfunc
diff --git a/runtime/doc/provider.txt b/runtime/doc/provider.txt
index b785010699..9fd35f19c5 100644
--- a/runtime/doc/provider.txt
+++ b/runtime/doc/provider.txt
@@ -20,11 +20,12 @@ Run the |:checkhealth| command, and review the sections below.
==============================================================================
Python integration *provider-python*
-Nvim supports Python |remote-plugin|s and the Vim legacy |python2| and
-|python3| interfaces (which are implemented as remote-plugins).
+Nvim supports Python |remote-plugin|s and the Vim legacy |python3| and
+|pythonx| interfaces (which are implemented as remote-plugins).
Note: Only the Vim 7.3 legacy interface is supported, not later features such
-as |python-bindeval| (Vim 7.4); use the Nvim API instead.
+as |python-bindeval| (Vim 7.4); use the Nvim API instead. Python 2 is not
+supported.
PYTHON QUICKSTART ~
@@ -38,11 +39,6 @@ For Python 3 plugins:
2. Install the module (try "python" if "python3" is missing): >
python3 -m pip install --user --upgrade pynvim
-For Python 2 plugins:
-1. Make sure Python 2.7 is available in your $PATH.
-2. Install the module (try "python" if "python2" is missing): >
- python2 -m pip install --user --upgrade pynvim
-
The pip `--upgrade` flag ensures that you get the latest version even if
a previous version was already installed.
@@ -56,22 +52,12 @@ If you run into problems, uninstall _both_ then install "pynvim" again: >
PYTHON PROVIDER CONFIGURATION ~
- *g:python_host_prog*
-Command to start Python 2 (executable, not directory). Setting this makes
-startup faster. Useful for working with virtualenvs. Must be set before any
-check for has("python2"). >
- let g:python_host_prog = '/path/to/python'
-<
*g:python3_host_prog*
Command to start Python 3 (executable, not directory). Setting this makes
startup faster. Useful for working with virtualenvs. Must be set before any
check for has("python3"). >
let g:python3_host_prog = '/path/to/python3'
<
- *g:loaded_python_provider*
-To disable Python 2 support: >
- let g:loaded_python_provider = 0
-<
*g:loaded_python3_provider*
To disable Python 3 support: >
let g:loaded_python3_provider = 0
@@ -81,8 +67,8 @@ PYTHON VIRTUALENVS ~
*python-virtualenv*
If you plan to use per-project virtualenvs often, you should assign one
virtualenv for Neovim and hard-code the interpreter path via
-|g:python3_host_prog| (or |g:python_host_prog|) so that the "pynvim" package
-is not required for each virtualenv.
+|g:python3_host_prog| so that the "pynvim" package is not required
+for each virtualenv.
Example using pyenv: >
pyenv install 3.4.4
diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt
index 873fbd8971..dab3bfa280 100644
--- a/runtime/doc/quickfix.txt
+++ b/runtime/doc/quickfix.txt
@@ -341,7 +341,7 @@ processing a quickfix or location list command, it will be aborted.
cursor position will not be changed. See |:cexpr| for
more information.
Example: >
- :g/mypattern/caddexpr expand("%") . ":" . line(".") . ":" . getline(".")
+ :g/mypattern/caddexpr expand("%") .. ":" .. line(".") .. ":" .. getline(".")
<
*:lad* *:addd* *:laddexpr*
:lad[dexpr] {expr} Same as ":caddexpr", except the location list for the
@@ -497,7 +497,6 @@ EXECUTE A COMMAND IN ALL THE BUFFERS IN QUICKFIX OR LOCATION LIST:
autocommand event is disabled by adding it to
'eventignore'. This considerably speeds up editing
each buffer.
- {not in Vi}
Also see |:bufdo|, |:tabdo|, |:argdo|, |:windo|,
|:ldo|, |:cfdo| and |:lfdo|.
@@ -510,7 +509,6 @@ EXECUTE A COMMAND IN ALL THE BUFFERS IN QUICKFIX OR LOCATION LIST:
:{cmd}
etc.
< Otherwise it works the same as `:cdo`.
- {not in Vi}
*:ldo*
:ld[o][!] {cmd} Execute {cmd} in each valid entry in the location list
@@ -523,7 +521,6 @@ EXECUTE A COMMAND IN ALL THE BUFFERS IN QUICKFIX OR LOCATION LIST:
etc.
< Only valid entries in the location list are used.
Otherwise it works the same as `:cdo`.
- {not in Vi}
*:lfdo*
:lfdo[!] {cmd} Execute {cmd} in each file in the location list for
@@ -535,7 +532,6 @@ EXECUTE A COMMAND IN ALL THE BUFFERS IN QUICKFIX OR LOCATION LIST:
:{cmd}
etc.
< Otherwise it works the same as `:ldo`.
- {not in Vi}
FILTERING A QUICKFIX OR LOCATION LIST:
*cfilter-plugin* *:Cfilter* *:Lfilter*
@@ -560,6 +556,9 @@ pattern is used.
The |:Lfilter| command does the same as |:Cfilter| but operates on the current
location list.
+The current quickfix/location list is not modified by these commands, so you
+can go back to the unfiltered list using the |:colder|/|:lolder| command.
+
=============================================================================
2. The error window *quickfix-window*
@@ -575,6 +574,7 @@ location list.
second quickfix window. If [height] is given the
existing window will be resized to it.
+ *quickfix-buffer*
The window will contain a special buffer, with
'buftype' equal to "quickfix". Don't change this!
The window will have the w:quickfix_title variable set
@@ -583,7 +583,11 @@ location list.
status line if the value of 'statusline' is adjusted
properly. Whenever this buffer is modified by a
quickfix command or function, the |b:changedtick|
- variable is incremented.
+ variable is incremented. You can get the number of
+ this buffer using the getqflist() and getloclist()
+ functions by passing the 'qfbufnr' item. For a
+ location list, this buffer is wiped out when the
+ location list is removed.
*:lop* *:lopen*
:lop[en] [height] Open a window to show the location list for the
@@ -641,6 +645,27 @@ quickfix window. If there already is a window for that file, it is used
instead. If the buffer in the used window has changed, and the error is in
another file, jumping to the error will fail. You will first have to make
sure the window contains a buffer which can be abandoned.
+
+When you select a file from the quickfix window, the following steps are used
+to find a window to edit the file:
+
+1. If a window displaying the selected file is present in the current tabpage
+ (starting with the window before the quickfix window), then that window is
+ used.
+2. If the above step fails and if 'switchbuf' contains "usetab" and a window
+ displaying the selected file is present in any one of the tabpages
+ (starting with the first tabpage) then that window is used.
+3. If the above step fails then a window in the current tabpage displaying a
+ buffer with 'buftype' not set (starting with the window before the quickfix
+ window) is used.
+4. If the above step fails and if 'switchbuf' contains "uselast", then the
+ previously accessed window is used.
+5. If the above step fails then the window before the quickfix window is used.
+ If there is no previous window, then the window after the quickfix window
+ is used.
+6. If the above step fails, then a new horizontally split window above the
+ quickfix window is used.
+
*CTRL-W_<Enter>* *CTRL-W_<CR>*
You can use CTRL-W <Enter> to open a new window and jump to the error there.
@@ -650,7 +675,7 @@ FileType event (also see |qf.vim|). Then the BufReadPost event is triggered,
using "quickfix" for the buffer name. This can be used to perform some action
on the listed errors. Example: >
au BufReadPost quickfix setlocal modifiable
- \ | silent exe 'g/^/s//\=line(".")." "/'
+ \ | silent exe 'g/^/s//\=line(".") .. " "/'
\ | setlocal nomodifiable
This prepends the line number to each line. Note the use of "\=" in the
substitute string of the ":s" command, which is used to evaluate an
@@ -679,13 +704,15 @@ this window, the displayed location list is used.
When you select a file from the location list window, the following steps are
used to find a window to edit the file:
-1. If a window with the location list displayed in the location list window is
- present, then the file is opened in that window.
-2. If the above step fails and if the file is already opened in another
- window, then that window is used.
-3. If the above step fails then an existing window showing a buffer with
- 'buftype' not set is used.
-4. If the above step fails, then the file is edited in a new window.
+1. If a non-quickfix window associated with the location list is present in
+ the current tabpage, then that window is used.
+2. If the above step fails and if the file is already opened in another window
+ in the current tabpage, then that window is used.
+3. If the above step fails and 'switchbuf' contains "usetab" and if the file
+ is opened in a window in any one of the tabpages, then that window is used.
+4. If the above step fails then a window in the current tabpage showing a
+ buffer with 'buftype' not set is used.
+5. If the above step fails, then the file is edited in a new window.
In all of the above cases, if the location list for the selected window is not
yet set, then it is set to the location list displayed in the location list
@@ -749,12 +776,18 @@ using these functions are below:
" get the quickfix list window id
:echo getqflist({'winid' : 0}).winid
+ " get the quickfix list window buffer number
+ :echo getqflist({'qfbufnr' : 0}).qfbufnr
+
" get the context of the current location list
:echo getloclist(0, {'context' : 0}).context
" get the location list window id of the third window
:echo getloclist(3, {'winid' : 0}).winid
+ " get the location list window buffer number of the third window
+ :echo getloclist(3, {'qfbufnr' : 0}).qfbufnr
+
" get the file window id of a location list window (winnr: 4)
:echo getloclist(4, {'filewinid' : 0}).filewinid
<
@@ -989,7 +1022,7 @@ commands can be combined to create a NewGrep command: >
5.1 using Vim's internal grep
*:vim* *:vimgrep* *E682* *E683*
-:vim[grep][!] /{pattern}/[g][j] {file} ...
+:vim[grep][!] /{pattern}/[g][j][f] {file} ...
Search for {pattern} in the files {file} ... and set
the error list to the matches. Files matching
'wildignore' are ignored; files in 'suffixes' are
@@ -1014,6 +1047,13 @@ commands can be combined to create a NewGrep command: >
updated. With the [!] any changes in the current
buffer are abandoned.
+ 'f' When the 'f' flag is specified, fuzzy string
+ matching is used to find matching lines. In this
+ case, {pattern} is treated as a literal string
+ instead of a regular expression. See
+ |fuzzy-matching| for more information about fuzzy
+ matching strings.
+
|QuickFixCmdPre| and |QuickFixCmdPost| are triggered.
A file that is opened for matching may use a buffer
number, but it is reused if possible to avoid
@@ -1042,20 +1082,20 @@ commands can be combined to create a NewGrep command: >
:vimgrep Error *.c
<
*:lv* *:lvimgrep*
-:lv[imgrep][!] /{pattern}/[g][j] {file} ...
+:lv[imgrep][!] /{pattern}/[g][j][f] {file} ...
:lv[imgrep][!] {pattern} {file} ...
Same as ":vimgrep", except the location list for the
current window is used instead of the quickfix list.
*:vimgrepa* *:vimgrepadd*
-:vimgrepa[dd][!] /{pattern}/[g][j] {file} ...
+:vimgrepa[dd][!] /{pattern}/[g][j][f] {file} ...
:vimgrepa[dd][!] {pattern} {file} ...
Just like ":vimgrep", but instead of making a new list
of errors the matches are appended to the current
list.
*:lvimgrepa* *:lvimgrepadd*
-:lvimgrepa[dd][!] /{pattern}/[g][j] {file} ...
+:lvimgrepa[dd][!] /{pattern}/[g][j][f] {file} ...
:lvimgrepa[dd][!] {pattern} {file} ...
Same as ":vimgrepadd", except the location list for
the current window is used instead of the quickfix
@@ -1332,12 +1372,17 @@ Basic items
%f file name (finds a string)
%o module name (finds a string)
%l line number (finds a number)
+ %e end line number (finds a number)
%c column number (finds a number representing character
column of the error, byte index, a <tab> is 1
character column)
%v virtual column number (finds a number representing
screen column of the error (1 <tab> == 8 screen
columns))
+ %k end column number (finds a number representing
+ the character column of the error, byte index, or a
+ number representing screen end column of the error if
+ it's used with %v)
%t error type (finds a single character):
e - error message
w - warning message
@@ -1446,7 +1491,7 @@ error message (line numbers are not part of the actual output):
4 Traceback (most recent call last):
5 File "unittests/dbfacadeTest.py", line 89, in testFoo
6 self.assertEquals(34, dtid)
- 7 File "/usr/lib/python2.2/unittest.py", line 286, in
+ 7 File "/usr/lib/python3.8/unittest.py", line 286, in
8 failUnlessEqual
9 raise self.failureException, \
10 AssertionError: 34 != 33
diff --git a/runtime/doc/quickref.txt b/runtime/doc/quickref.txt
index af8301f1a0..6f16db5cc2 100644
--- a/runtime/doc/quickref.txt
+++ b/runtime/doc/quickref.txt
@@ -356,6 +356,7 @@ In Insert or Command-line mode:
|v_y| {visual}y yank the highlighted text into a register
|yy| N yy yank N lines into a register
|Y| N Y yank N lines into a register
+ Note: Mapped to "y$" by default. |default-mappings|
|p| N p put a register after the cursor position (N times)
|P| N P put a register before the cursor position (N times)
|]p| N ]p like p, but adjust indent to current line
@@ -637,6 +638,7 @@ Short explanation of each option: *option-list*
'cinkeys' 'cink' keys that trigger indent when 'cindent' is set
'cinoptions' 'cino' how to do indenting when 'cindent' is set
'cinwords' 'cinw' words where 'si' and 'cin' add an indent
+'cinscopedecls' 'cinsd' words that are recognized by 'cino-g'
'clipboard' 'cb' use the clipboard as the unnamed register
'cmdheight' 'ch' number of lines to use for the command-line
'cmdwinheight' 'cwh' height of the command-line window
@@ -739,7 +741,6 @@ Short explanation of each option: *option-list*
'indentexpr' 'inde' expression used to obtain the indent of a line
'indentkeys' 'indk' keys that trigger indenting with 'indentexpr'
'infercase' 'inf' adjust case of match for keyword completion
-'insertmode' 'im' start the edit of a file in Insert mode
'isfname' 'isf' characters included in file names and pathnames
'isident' 'isi' characters included in identifiers
'iskeyword' 'isk' characters included in keywords
@@ -784,6 +785,7 @@ Short explanation of each option: *option-list*
'mousefocus' 'mousef' keyboard focus follows the mouse
'mousehide' 'mh' hide mouse pointer while typing
'mousemodel' 'mousem' changes meaning of mouse buttons
+'mousescroll' amount to scroll by when scrolling with a mouse
'mouseshape' 'mouses' shape of the mouse pointer in different modes
'mousetime' 'mouset' max time between mouse double-click
'nrformats' 'nf' number formats recognized for CTRL-A command
@@ -822,7 +824,6 @@ Short explanation of each option: *option-list*
'redrawtime' 'rdt' timeout for 'hlsearch' and |:match| highlighting
'regexpengine' 're' default regexp engine to use
'relativenumber' 'rnu' show relative line number in front of each line
-'remap' allow mappings to work recursively
'report' threshold for reporting nr. of lines changed
'revins' 'ri' inserting characters will work backwards
'rightleft' 'rl' window is right-to-left oriented
@@ -894,7 +895,6 @@ Short explanation of each option: *option-list*
'tagstack' 'tgst' push tags onto the tag stack
'term' name of the terminal
'termbidi' 'tbidi' terminal takes care of bi-directionality
-'terse' shorten some messages
'textwidth' 'tw' maximum width of text that is being inserted
'thesaurus' 'tsr' list of thesaurus files for keyword completion
'thesaurusfunc' 'tsrfu' function to be used for thesaurus completion
@@ -1267,7 +1267,7 @@ Context-sensitive completion on the command-line:
|CTRL-W_^| CTRL-W ^ split window and edit alternate file
|CTRL-W_n| CTRL-W n or :new create new empty window
|CTRL-W_q| CTRL-W q or :q[uit] quit editing and close window
-|CTRL-W_c| CTRL-W c or :cl[ose] make buffer hidden and close window
+|CTRL-W_c| CTRL-W c or :clo[se] make buffer hidden and close window
|CTRL-W_o| CTRL-W o or :on[ly] make current window only one on the
screen
diff --git a/runtime/doc/recover.txt b/runtime/doc/recover.txt
index 9ef5a37452..d9aaa757ad 100644
--- a/runtime/doc/recover.txt
+++ b/runtime/doc/recover.txt
@@ -108,7 +108,7 @@ command:
*:pre* *:preserve* *E313* *E314*
:pre[serve] Write all text for the current buffer into its swap
file. The original file is no longer needed for
- recovery. This sets a flag in the current buffer.
+ recovery.
A Vim swap file can be recognized by the first six characters: "b0VIM ".
After that comes the version number, e.g., "3.0".
diff --git a/runtime/doc/remote.txt b/runtime/doc/remote.txt
new file mode 100644
index 0000000000..0c1e3438de
--- /dev/null
+++ b/runtime/doc/remote.txt
@@ -0,0 +1,131 @@
+*remote.txt* Nvim
+
+
+ VIM REFERENCE MANUAL by Bram Moolenaar
+
+
+Vim client-server communication *client-server*
+
+ Type |gO| to see the table of contents.
+
+==============================================================================
+1. Common functionality *clientserver*
+
+Nvim's |RPC| functionality allows clients to programmatically control Nvim. Nvim
+itself takes command-line arguments that cause it to become a client to another
+Nvim running as a server. These arguments match those provided by Vim's
+clientserver option.
+
+The following command line arguments are available:
+
+ argument meaning ~
+
+ --remote [+{cmd}] {file} ... *--remote*
+ Open the file list in a remote Vim. When
+ there is no Vim server, execute locally.
+ Vim allows one init command: +{cmd}.
+ This must be an Ex command that can be
+ followed by "|". It's not yet supported by
+ Nvim.
+ The rest of the command line is taken as the
+ file list. Thus any non-file arguments must
+ come before this.
+ You cannot edit stdin this way |--|.
+ The remote Vim is raised. If you don't want
+ this use >
+ nvim --remote-send "<C-\><C-N>:n filename<CR>"
+<
+ --remote-silent [+{cmd}] {file} ... *--remote-silent*
+ As above, but don't complain if there is no
+ server and the file is edited locally.
+ *--remote-tab*
+ --remote-tab Like --remote but open each file in a new
+ tabpage.
+ *--remote-tab-silent*
+ --remote-tab-silent Like --remote-silent but open each file in a
+ new tabpage.
+ *--remote-send*
+ --remote-send {keys} Send {keys} to server and exit. The {keys}
+ are not mapped. Special key names are
+ recognized, e.g., "<CR>" results in a CR
+ character.
+ *--remote-expr*
+ --remote-expr {expr} Evaluate {expr} in server and print the result
+ on stdout.
+ *--server*
+ --server {addr} Connect to the named pipe or socket at the
+ given address for executing remote commands.
+ See |--listen| for specifying an address when
+ starting a server.
+
+Examples ~
+
+Start an Nvim server listening on a named pipe at '~/.cache/nvim/server.pipe': >
+ nvim --listen ~/.cache/nvim/server.pipe
+
+Edit "file.txt" in an Nvim server listening at '~/.cache/nvim/server.pipe': >
+ nvim --server ~/.cache/nvim/server.pipe --remote file.txt
+
+This doesn't work, all arguments after --remote will be used as file names: >
+ nvim --remote --server ~/.cache/nvim/server.pipe file.txt
+
+Tell the remote server to write all files and exit: >
+ nvim --server ~/.cache/nvim/server.pipe --remote-send '<C-\><C-N>:wqa<CR>'
+
+
+REMOTE EDITING
+
+The --remote argument will cause a |:drop| command to be constructed from the
+rest of the command line and sent as described above.
+Note that the --remote and --remote-wait arguments will consume the rest of
+the command line. I.e. all remaining arguments will be regarded as filenames.
+You can not put options there!
+
+
+==============================================================================
+2. Missing functionality *E5600* *clientserver-missing*
+
+Vim supports additional functionality in clientserver that's not yet
+implemented in Nvim. In particular, none of the "wait" variants are supported
+yet. The following command line arguments are not yet available:
+
+ argument meaning ~
+
+ --remote-wait [+{cmd}] {file} ... *--remote-wait*
+ Not yet supported by Nvim.
+ As --remote, but wait for files to complete
+ (unload) in remote Vim.
+ --remote-wait-silent [+{cmd}] {file} ... *--remote-wait-silent*
+ Not yet supported by Nvim.
+ As --remote-wait, but don't complain if there
+ is no server.
+ *--remote-tab-wait*
+ --remote-tab-wait Not yet supported by Nvim.
+ Like --remote-wait but open each file in a new
+ tabpage.
+ *--remote-tab-wait-silent*
+ --remote-tab-wait-silent Not yet supported by Nvim.
+ Like --remote-wait-silent but open each file
+ in a new tabpage.
+ *--servername*
+ --servername {name} Not yet supported by Nvim.
+ Become the server {name}. When used together
+ with one of the --remote commands: connect to
+ server {name} instead of the default (see
+ below). The name used will be uppercase.
+
+ *--serverlist*
+ --serverlist Not yet supported by Nvim.
+ Output a list of server names.
+
+
+
+
+SERVER NAME *client-server-name*
+
+By default Vim will try to register the name under which it was invoked (gvim,
+egvim ...). This can be overridden with the --servername argument. Nvim
+either listens on a named pipe or a socket and does not yet support this
+--servername functionality.
+
+ vim:tw=78:sw=4:ts=8:noet:ft=help:norl:
diff --git a/runtime/doc/repeat.txt b/runtime/doc/repeat.txt
index c7481ad290..508565dea4 100644
--- a/runtime/doc/repeat.txt
+++ b/runtime/doc/repeat.txt
@@ -161,6 +161,11 @@ Q Repeat the last recorded register [count] times.
result of evaluating the expression is executed as an
Ex command.
Mappings are not recognized in these commands.
+ When the |line-continuation| character (\) is present
+ at the beginning of a line in a linewise register,
+ then it is combined with the previous line. This is
+ useful for yanking and executing parts of a Vim
+ script.
*:@:*
:[addr]@: Repeat last command-line. First set cursor at line
@@ -177,8 +182,7 @@ For writing a Vim script, see chapter 41 of the user manual |usr_41.txt|.
*:so* *:source* *load-vim-script*
:[range]so[urce] [file] Runs |Ex| commands or Lua code (".lua" files) from
- [file], or from the current buffer if no [file] is
- given.
+ [file], or current buffer if no [file].
Triggers the |SourcePre| autocommand.
*:source!*
:[range]so[urce]! {file}
@@ -253,21 +257,22 @@ For writing a Vim script, see chapter 41 of the user manual |usr_41.txt|.
below "plugin", just like with plugins in
'runtimepath'.
- If the filetype detection was not enabled yet (this
+ If the filetype detection was already enabled (this
is usually done with a "syntax enable" or "filetype
- on" command in your .vimrc file), this will also look
+ on" command in your |init.vim|, or automatically during
+ |initialization|), and the package was found in
+ "pack/*/opt/{name}", this command will also look
for "{name}/ftdetect/*.vim" files.
When the optional ! is added no plugin files or
ftdetect scripts are loaded, only the matching
directories are added to 'runtimepath'. This is
- useful in your .vimrc. The plugins will then be
- loaded during initialization, see |load-plugins| (note
+ useful in your |init.vim|. The plugins will then be
+ loaded during |initialization|, see |load-plugins| (note
that the loading order will be reversed, because each
- directory is inserted before others).
- Note that for ftdetect scripts to be loaded
- you will need to write `filetype plugin indent on`
- AFTER all `packadd!` commands.
+ directory is inserted before others). In this case, the
+ ftdetect scripts will be loaded during |initialization|,
+ before the |load-plugins| step.
Also see |pack-add|.
@@ -465,8 +470,8 @@ flag when defining the function, it is not relevant when executing it. >
:set cpo-=C
<
*line-continuation-comment*
-To add a comment in between the lines start with '\" '. Notice the space
-after the double quote. Example: >
+To add a comment in between the lines start with '"\ '. Notice the space
+after the backslash. Example: >
let array = [
"\ first entry comment
\ 'first',
diff --git a/runtime/doc/scroll.txt b/runtime/doc/scroll.txt
index 7d1da3b409..170c87a1a4 100644
--- a/runtime/doc/scroll.txt
+++ b/runtime/doc/scroll.txt
@@ -239,12 +239,16 @@ the "h" flag in 'guioptions' is set, the cursor moves to the longest visible
line if the cursor line is about to be scrolled off the screen (similarly to
how the horizontal scrollbar works).
-You can modify the default behavior by mapping the keys. For example, to make
-the scroll wheel move one line or half a page in Normal mode: >
- :map <ScrollWheelUp> <C-Y>
- :map <S-ScrollWheelUp> <C-U>
- :map <ScrollWheelDown> <C-E>
- :map <S-ScrollWheelDown> <C-D>
-You can also use Alt and Ctrl modifiers.
+You can control the number of lines / columns to scroll by using the
+'mousescroll' option. You can also modify the default behavior by mapping
+the keys. For example, to scroll a page at a time in normal mode: >
+ :map <ScrollWheelUp> <C-B>
+ :map <ScrollWheelDown> <C-F>
+Scroll keys can also be combined with modifiers such as Shift, Ctrl, and Alt.
+
+When scrolling with a mouse, the window currently under the cursor is
+scrolled. This allows you to scroll inactive windows. Note that when scroll
+keys are remapped to keyboard keys, the active window is affected regardless
+of the current cursor position.
vim:tw=78:ts=8:noet:ft=help:norl:
diff --git a/runtime/doc/sign.txt b/runtime/doc/sign.txt
index 5cfa06c33c..a2a5645baa 100644
--- a/runtime/doc/sign.txt
+++ b/runtime/doc/sign.txt
@@ -87,7 +87,7 @@ the delete is undone the sign does not move back.
Here is an example that places a sign "piet", displayed with the text ">>", in
line 23 of the current file: >
:sign define piet text=>> texthl=Search
- :exe ":sign place 2 line=23 name=piet file=" . expand("%:p")
+ :exe ":sign place 2 line=23 name=piet file=" .. expand("%:p")
And here is the command to delete it again: >
:sign unplace 2
diff --git a/runtime/doc/spell.txt b/runtime/doc/spell.txt
index 03c00c8495..bc45b0e511 100644
--- a/runtime/doc/spell.txt
+++ b/runtime/doc/spell.txt
@@ -120,8 +120,8 @@ zuG Undo |zW| and |zG|, remove the word from the internal
rare as this is a fairly uncommon command and all
intuitive commands for this are already taken. If you
want you can add mappings with e.g.: >
- nnoremap z? :exe ':spellrare ' . expand('<cWORD>')<CR>
- nnoremap z/ :exe ':spellrare! ' . expand('<cWORD>')<CR>
+ nnoremap z? :exe ':spellrare ' .. expand('<cWORD>')<CR>
+ nnoremap z/ :exe ':spellrare! ' .. expand('<cWORD>')<CR>
< |:spellundo|, |zuw|, or |zuW| can be used to undo this.
:spellr[rare]! {word} Add {word} as a rare word to the internal word
diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt
index bb775ec884..d57a85423c 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.
@@ -367,7 +367,7 @@ argument.
*--headless*
--headless Start without UI, and do not wait for `nvim_ui_attach`. The
builtin TUI is not used, so stdio works as an arbitrary
- communication channel. |channel-stdio|
+ communication channel. |channel-stdio|
Also useful for scripting (tests) to see messages that would
not be printed by |-es|.
@@ -407,9 +407,14 @@ accordingly, proceeding as follows:
Nvim started with |--embed| waits for the UI to connect before
proceeding to load user configuration.
-4. Setup |default-mappings| and |default-autocmds|.
+4. Setup |default-mappings| and |default-autocmds|. Create |popup-menu|.
-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,10 +524,9 @@ 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.
The starting flag is reset, has("vim_starting") will now return zero.
The |v:vim_did_enter| variable is set to 1.
The |VimEnter| autocommands are executed.
@@ -581,7 +583,7 @@ setting can affect the entire editor in ways that are not initially obvious.
To find the cause of a problem in your config, you must "bisect" it:
1. Remove or disable half of your |config|.
2. Restart Nvim.
-3. If the problem still occurs, goto 1.
+3. If the problem still occurs, goto 1.
4. If the problem is gone, restore half of the removed lines.
5. Continue narrowing your config in this way, until you find the setting or
plugin causing the issue.
@@ -698,7 +700,7 @@ vimrc file.
These commands will write ":map" and ":set" commands to a file, in such a way
that when these commands are executed, the current key mappings and options
will be set to the same values. The options 'columns', 'endofline',
-'fileformat', 'lines', 'modified', and 'scroll' are not included, because
+'fileformat', 'lines', 'modified', and 'scroll' are not included, because
these are terminal or file dependent.
Note that the options 'binary', 'paste' and 'readonly' are included, this
might not always be what you want.
@@ -715,7 +717,7 @@ with ":map" and ":set" commands and write the modified file. First read the
default vimrc in with a command like ":source ~piet/.vimrc.Cprogs", change
the settings and then save them in the current directory with ":mkvimrc!". If
you want to make this file your default |config|, move it to
-$XDG_CONFIG_HOME/nvim. You could also use autocommands |autocommand| and/or
+$XDG_CONFIG_HOME/nvim. You could also use autocommands |autocommand| and/or
modelines |modeline|.
*vimrc-option-example*
@@ -797,7 +799,7 @@ resulting file, when executed with a ":source" command:
After restoring the Session, the full filename of your current Session is
available in the internal variable |v:this_session|.
An example mapping: >
- :nmap <F2> :wa<Bar>exe "mksession! " . v:this_session<CR>:so ~/sessions/
+ :nmap <F2> :wa<Bar>exe "mksession! " .. v:this_session<CR>:so ~/sessions/
This saves the current Session, and starts off the command to load another.
A session includes all tab pages, unless "tabpages" was removed from
@@ -883,7 +885,7 @@ Shada ("shared data") file *shada* *shada-file*
If you exit Vim and later start it again, you would normally lose a lot of
information. The ShaDa file can be used to remember that information, which
-enables you to continue where you left off. Its name is the abbreviation of
+enables you to continue where you left off. Its name is the abbreviation of
SHAred DAta because it is used for sharing data between Neovim sessions.
This is introduced in section |21.3| of the user manual.
@@ -914,9 +916,9 @@ The |v:oldfiles| variable is filled. The marks are not read in at startup
option upon startup.
*shada-write*
-When Vim exits and 'shada' is non-empty, the info is stored in the ShaDa file
-(it's actually merged with the existing one, if one exists |shada-merging|).
-The 'shada' option is a string containing information about what info should
+When Vim exits and 'shada' is non-empty, the info is stored in the ShaDa file
+(it's actually merged with the existing one, if one exists |shada-merging|).
+The 'shada' option is a string containing information about what info should
be stored, and contains limits on how much should be stored (see 'shada').
Notes for Unix:
@@ -974,75 +976,75 @@ remembered.
MERGING *shada-merging*
-When writing ShaDa files with |:wshada| without bang or at regular exit
-information in the existing ShaDa file is merged with information from current
-Neovim instance. For this purpose ShaDa files store timestamps associated
+When writing ShaDa files with |:wshada| without bang or at regular exit
+information in the existing ShaDa file is merged with information from current
+Neovim instance. For this purpose ShaDa files store timestamps associated
with ShaDa entries. Specifically the following is being done:
-1. History lines are merged, ordered by timestamp. Maximum amount of items in
- ShaDa file is defined by 'shada' option (|shada-/|, |shada-:|, |shada-@|,
- etc: one suboption for each character that represents history name
+1. History lines are merged, ordered by timestamp. Maximum amount of items in
+ ShaDa file is defined by 'shada' option (|shada-/|, |shada-:|, |shada-@|,
+ etc: one suboption for each character that represents history name
(|:history|)).
-2. Local marks and changes for files that were not opened by Neovim are copied
- to new ShaDa file. Marks for files that were opened by Neovim are merged,
+2. Local marks and changes for files that were not opened by Neovim are copied
+ to new ShaDa file. Marks for files that were opened by Neovim are merged,
changes to files opened by Neovim are ignored. |shada-'|
-3. Jump list is merged: jumps are ordered by timestamp, identical jumps
+3. Jump list is merged: jumps are ordered by timestamp, identical jumps
(identical position AND timestamp) are squashed.
-4. Search patterns and substitute strings are not merged: search pattern or
- substitute string which has greatest timestamp will be the only one copied
+4. Search patterns and substitute strings are not merged: search pattern or
+ substitute string which has greatest timestamp will be the only one copied
to ShaDa file.
-5. For each register entity with greatest timestamp is the only saved.
+5. For each register entity with greatest timestamp is the only saved.
|shada-<|
-6. All saved variables are saved from current Neovim instance. Additionally
- existing variable values are copied, meaning that the only way to remove
- variable from a ShaDa file is either removing it by hand or disabling
+6. All saved variables are saved from current Neovim instance. Additionally
+ existing variable values are copied, meaning that the only way to remove
+ variable from a ShaDa file is either removing it by hand or disabling
writing variables completely. |shada-!|
7. For each global mark entity with greatest timestamp is the only saved.
-8. Buffer list and header are the only entries which are not merged in any
- fashion: the only header and buffer list present are the ones from the
+8. Buffer list and header are the only entries which are not merged in any
+ fashion: the only header and buffer list present are the ones from the
Neovim instance which was last writing the file. |shada-%|
COMPATIBILITY *shada-compatibility*
ShaDa files are forward and backward compatible. This means that
-1. Entries which have unknown type (i.e. that hold unidentified data) are
+1. Entries which have unknown type (i.e. that hold unidentified data) are
ignored when reading and blindly copied when writing.
-2. Register entries with unknown register name are ignored when reading and
- blindly copied when writing. Limitation: only registers that use name with
+2. Register entries with unknown register name are ignored when reading and
+ blindly copied when writing. Limitation: only registers that use name with
code in interval [1, 255] are supported. |registers|
-3. Register entries with unknown register type are ignored when reading and
+3. Register entries with unknown register type are ignored when reading and
merged as usual when writing. |getregtype()|
-4. Local and global mark entries with unknown mark names are ignored when
- reading. When writing global mark entries are blindly copied and local mark
- entries are also blindly copied, but only if file they are attached to fits
- in the |shada-'| limit. Unknown local mark entry's timestamp is also taken
- into account when calculating which files exactly should fit into this
- limit. Limitation: only marks that use name with code in interval [1, 255]
+4. Local and global mark entries with unknown mark names are ignored when
+ reading. When writing global mark entries are blindly copied and local mark
+ entries are also blindly copied, but only if file they are attached to fits
+ in the |shada-'| limit. Unknown local mark entry's timestamp is also taken
+ into account when calculating which files exactly should fit into this
+ limit. Limitation: only marks that use name with code in interval [1, 255]
are supported. |mark-motions|
-5. History entries with unknown history type are ignored when reading and
- blindly copied when writing. Limitation: there can be only up to 256
+5. History entries with unknown history type are ignored when reading and
+ blindly copied when writing. Limitation: there can be only up to 256
history types. |history|
-6. Unknown keys found in register, local mark, global mark, change, jump and
- search pattern entries are saved internally and dumped when writing.
+6. Unknown keys found in register, local mark, global mark, change, jump and
+ search pattern entries are saved internally and dumped when writing.
Entries created during Neovim session never have such additions.
-7. Additional elements found in replacement string and history entries are
- saved internally and dumped. Entries created during Neovim session never
+7. Additional elements found in replacement string and history entries are
+ saved internally and dumped. Entries created during Neovim session never
have such additions.
-8. Additional elements found in variable entries are simply ignored when
- reading. When writing new variables they will be preserved during merging,
- but that's all. Variable values dumped from current Neovim session never
- have additional elements, even if variables themselves were obtained by
+8. Additional elements found in variable entries are simply ignored when
+ reading. When writing new variables they will be preserved during merging,
+ but that's all. Variable values dumped from current Neovim session never
+ have additional elements, even if variables themselves were obtained by
reading ShaDa files.
-"Blindly" here means that there will be no attempts to somehow merge them,
+"Blindly" here means that there will be no attempts to somehow merge them,
even if other entries (with known name/type/etc) are merged. |shada-merging|
SHADA FILE NAME *shada-file-name*
- Default name of the |shada| file is:
- Unix: "$XDG_DATA_HOME/nvim/shada/main.shada"
- Windows: "$XDG_DATA_HOME/nvim-data/shada/main.shada"
+ Unix: "$XDG_STATE_HOME/nvim/shada/main.shada"
+ Windows: "$XDG_STATE_HOME/nvim-data/shada/main.shada"
See also |base-directories|.
- To choose a different file name you can use:
- The "n" flag in the 'shada' option.
@@ -1064,55 +1066,55 @@ however that this means everything will be overwritten with information from
the first Vim, including the command line history, etc.
The ShaDa file itself can be edited by hand too, although we suggest you
-start with an existing one to get the format right. You need to understand
-MessagePack (or, more likely, find software that is able to use it) format to
-do this. This can be useful in order to create a second file, say
-"~/.my.shada" which could contain certain settings that you always want when
-you first start Neovim. For example, you can preload registers with
-particular data, or put certain commands in the command line history. A line
+start with an existing one to get the format right. You need to understand
+MessagePack (or, more likely, find software that is able to use it) format to
+do this. This can be useful in order to create a second file, say
+"~/.my.shada" which could contain certain settings that you always want when
+you first start Neovim. For example, you can preload registers with
+particular data, or put certain commands in the command line history. A line
in your |config| file like >
:rshada! ~/.my.shada
-can be used to load this information. You could even have different ShaDa
-files for different types of files (e.g., C code) and load them based on the
-file name, using the ":autocmd" command (see |:autocmd|). More information on
+can be used to load this information. You could even have different ShaDa
+files for different types of files (e.g., C code) and load them based on the
+file name, using the ":autocmd" command (see |:autocmd|). More information on
ShaDa file format is contained in |shada-format| section.
*E136* *E929* *shada-error-handling*
-Some errors make Neovim leave temporary file named `{basename}.tmp.X` (X is
-any free letter from `a` to `z`) while normally it will create this file,
-write to it and then rename `{basename}.tmp.X` to `{basename}`. Such errors
+Some errors make Neovim leave temporary file named `{basename}.tmp.X` (X is
+any free letter from `a` to `z`) while normally it will create this file,
+write to it and then rename `{basename}.tmp.X` to `{basename}`. Such errors
include:
-- Errors which make Neovim think that read file is not a ShaDa file at all:
- non-ShaDa files are not overwritten for safety reasons to avoid accidentally
- destroying an unrelated file. This could happen e.g. when typing "nvim -i
- file" in place of "nvim -R file" (yes, somebody did that at least with Vim).
+- Errors which make Neovim think that read file is not a ShaDa file at all:
+ non-ShaDa files are not overwritten for safety reasons to avoid accidentally
+ destroying an unrelated file. This could happen e.g. when typing "nvim -i
+ file" in place of "nvim -R file" (yes, somebody did that at least with Vim).
Such errors are listed at |shada-critical-contents-errors|.
-- If writing to the temporary file failed: e.g. because of the insufficient
+- If writing to the temporary file failed: e.g. because of the insufficient
space left.
- If renaming file failed: e.g. because of insufficient permissions.
-- If target ShaDa file has different from the Neovim instance's owners (user
- and group) and changing them failed. Unix-specific, applies only when
+- If target ShaDa file has different from the Neovim instance's owners (user
+ and group) and changing them failed. Unix-specific, applies only when
Neovim was launched from root.
-Do not forget to remove the temporary file or replace the target file with
-temporary one after getting one of the above errors or all attempts to create
-a ShaDa file may fail with |E929|. If you got one of them when using
-|:wshada| (and not when exiting Neovim: i.e. when you have Neovim session
+Do not forget to remove the temporary file or replace the target file with
+temporary one after getting one of the above errors or all attempts to create
+a ShaDa file may fail with |E929|. If you got one of them when using
+|:wshada| (and not when exiting Neovim: i.e. when you have Neovim session
running) you have additional options:
-- First thing which you should consider if you got any error, except failure
- to write to the temporary file: remove existing file and replace it with the
+- First thing which you should consider if you got any error, except failure
+ to write to the temporary file: remove existing file and replace it with the
temporary file. Do it even if you have running Neovim instance.
-- Fix the permissions and/or file ownership, free some space and attempt to
+- Fix the permissions and/or file ownership, free some space and attempt to
write again. Do not remove the existing file.
-- Use |:wshada| with bang. Does not help in case of permission error. If
- target file was actually the ShaDa file some information may be lost in this
- case. To make the matters slightly better use |:rshada| prior to writing,
- but this still will loose buffer-local marks and change list entries for any
+- Use |:wshada| with bang. Does not help in case of permission error. If
+ target file was actually the ShaDa file some information may be lost in this
+ case. To make the matters slightly better use |:rshada| prior to writing,
+ but this still will loose buffer-local marks and change list entries for any
file which is not opened in the current Neovim instance.
-- Remove the target file from shell and use |:wshada|. Consequences are not
- different from using |:wshada| with bang, but "rm -f" works in some cases
+- Remove the target file from shell and use |:wshada|. Consequences are not
+ different from using |:wshada| with bang, but "rm -f" works in some cases
when you don't have write permissions.
*:rsh* *:rshada* *E886*
@@ -1126,13 +1128,13 @@ running) you have additional options:
The information in the file is first read in to make
a merge between old and new info. When [!] is used,
the old information is not read first, only the
- internal info is written (also disables safety checks
- described in |shada-error-handling|). If 'shada' is
+ internal info is written (also disables safety checks
+ described in |shada-error-handling|). If 'shada' is
empty, marks for up to 100 files will be written.
- When you get error "E929: All .tmp.X files exist,
- cannot write ShaDa file!" check that no old temp files
- were left behind (e.g.
- ~/.local/share/nvim/shada/main.shada.tmp*).
+ When you get error "E929: All .tmp.X files exist,
+ cannot write ShaDa file!" check that no old temp files
+ were left behind (e.g.
+ ~/.local/state/nvim/shada/main.shada.tmp*).
Note: Executing :wshada will reset all |'quote| marks.
@@ -1155,82 +1157,82 @@ running) you have additional options:
SHADA FILE FORMAT *shada-format*
-ShaDa files are concats of MessagePack entries. Each entry is a concat of
+ShaDa files are concats of MessagePack entries. Each entry is a concat of
exactly four MessagePack objects:
-1. First goes type of the entry. Object type must be an unsigned integer.
+1. First goes type of the entry. Object type must be an unsigned integer.
Object type must not be equal to zero.
2. Second goes entry timestamp. It must also be an unsigned integer.
-3. Third goes the length of the fourth entry. Unsigned integer as well, used
+3. Third goes the length of the fourth entry. Unsigned integer as well, used
for fast skipping without parsing.
-4. Fourth is actual entry data. All currently used ShaDa entries use
- containers to hold data: either map or array. All string values in those
- containers are either binary (applies to filenames) or UTF-8, yet parser
+4. Fourth is actual entry data. All currently used ShaDa entries use
+ containers to hold data: either map or array. All string values in those
+ containers are either binary (applies to filenames) or UTF-8, yet parser
needs to expect that invalid bytes may be present in a UTF-8 string.
Exact format depends on the entry type:
Entry type (name) Entry data ~
- 1 (Header) Map containing data that describes the generator
- instance that wrote this ShaDa file. It is ignored
+ 1 (Header) Map containing data that describes the generator
+ instance that wrote this ShaDa file. It is ignored
when reading ShaDa files. Contains the following data:
Key Data ~
- generator Binary, software used to generate ShaDa
- file. Is equal to "nvim" when ShaDa file was
+ generator Binary, software used to generate ShaDa
+ file. Is equal to "nvim" when ShaDa file was
written by Neovim.
version Binary, generator version.
encoding Binary, effective 'encoding' value.
max_kbyte Integer, effective |shada-s| limit value.
pid Integer, instance process ID.
- * It is allowed to have any number of
+ * It is allowed to have any number of
additional keys with any data.
- 2 (SearchPattern) Map containing data describing last used search or
- substitute pattern. Normally ShaDa file contains two
- such entries: one with "ss" key set to true (describes
- substitute pattern, see |:substitute|), and one set to
- false (describes search pattern, see
- |search-commands|). "su" key should be true on one of
- the entries. If key value is equal to default then it
+ 2 (SearchPattern) Map containing data describing last used search or
+ substitute pattern. Normally ShaDa file contains two
+ such entries: one with "ss" key set to true (describes
+ substitute pattern, see |:substitute|), and one set to
+ false (describes search pattern, see
+ |search-commands|). "su" key should be true on one of
+ the entries. If key value is equal to default then it
is normally not present. Keys:
Key Type Default Description ~
sm Boolean true Effective 'magic' value.
sc Boolean false Effective 'smartcase' value.
- sl Boolean true True if search pattern comes
- with a line offset. See
+ sl Boolean true True if search pattern comes
+ with a line offset. See
|search-offset|.
- se Boolean false True if |search-offset|
- requested to place cursor at
- (relative to) the end of the
+ se Boolean false True if |search-offset|
+ requested to place cursor at
+ (relative to) the end of the
pattern.
so Integer 0 Offset value. |search-offset|
- su Boolean false True if current entry was the
+ su Boolean false True if current entry was the
last used search pattern.
- ss Boolean false True if current entry describes
+ ss Boolean false True if current entry describes
|:substitute| pattern.
sh Boolean false True if |v:hlsearch| is on.
- With |shada-h| or 'nohlsearch'
+ With |shada-h| or 'nohlsearch'
this key is always false.
sp Binary N/A Actual pattern. Required.
- sb Boolean false True if search direction is
+ sb Boolean false True if search direction is
backward.
- * any none Other keys are allowed for
- compatibility reasons, see
+ * any none Other keys are allowed for
+ compatibility reasons, see
|shada-compatibility|.
- 3 (SubString) Array containing last |:substitute| replacement string.
- Contains single entry: binary, replacement string used.
- More entries are allowed for compatibility reasons, see
+ 3 (SubString) Array containing last |:substitute| replacement string.
+ Contains single entry: binary, replacement string used.
+ More entries are allowed for compatibility reasons, see
|shada-compatibility|.
- 4 (HistoryEntry) Array containing one entry from history. Should have
- two or three entries. First one is history type
- (unsigned integer), second is history line (binary),
- third is the separator character (unsigned integer,
- must be in interval [0, 255]). Third item is only
- valid for search history. Possible history types are
- listed in |hist-names|, here are the corresponding
- numbers: 0 - cmd, 1 - search, 2 - expr, 3 - input,
+ 4 (HistoryEntry) Array containing one entry from history. Should have
+ two or three entries. First one is history type
+ (unsigned integer), second is history line (binary),
+ third is the separator character (unsigned integer,
+ must be in interval [0, 255]). Third item is only
+ valid for search history. Possible history types are
+ listed in |hist-names|, here are the corresponding
+ numbers: 0 - cmd, 1 - search, 2 - expr, 3 - input,
4 - debug.
- 5 (Register) Map describing one register (|registers|). If key
- value is equal to default then it is normally not
+ 5 (Register) Map describing one register (|registers|). If key
+ value is equal to default then it is normally not
present. Keys:
Key Type Def Description ~
rt UInteger 0 Register type:
@@ -1258,12 +1260,12 @@ exactly four MessagePack objects:
* any none Other keys are allowed
for compatibility reasons,
see |shada-compatibility|.
- 6 (Variable) Array containing two items: variable name (binary) and
- variable value (any object). Values are converted
- using the same code |msgpackparse()| uses when reading,
- |msgpackdump()| when writing, so there may appear
- |msgpack-special-dict|s. If there are more then two
- entries then the rest are ignored
+ 6 (Variable) Array containing two items: variable name (binary) and
+ variable value (any object). Values are converted
+ using the same code |msgpackparse()| uses when reading,
+ |msgpackdump()| when writing, so there may appear
+ |msgpack-special-dict|s. If there are more then two
+ entries then the rest are ignored
(|shada-compatibility|).
7 (GlobalMark)
8 (Jump)
@@ -1277,57 +1279,58 @@ exactly four MessagePack objects:
Data contained in the map:
Key Type Default Description ~
- l UInteger 1 Position line number. Must be
+ l UInteger 1 Position line number. Must be
greater then zero.
c UInteger 0 Position column number.
- n UInteger 34 ('"') Mark name. Only valid for
- GlobalMark and LocalMark
+ n UInteger 34 ('"') Mark name. Only valid for
+ GlobalMark and LocalMark
entries.
f Binary N/A File name. Required.
- * any none Other keys are allowed for
- compatibility reasons, see
+ * any none Other keys are allowed for
+ compatibility reasons, see
|shada-compatibility|.
- 9 (BufferList) Array containing maps. Each map in the array
+ 9 (BufferList) Array containing maps. Each map in the array
represents one buffer. Possible keys:
Key Type Default Description ~
- l UInteger 1 Position line number. Must be
+ l UInteger 1 Position line number. Must be
greater then zero.
c UInteger 0 Position column number.
f Binary N/A File name. Required.
- * any none Other keys are allowed for
- compatibility reasons, see
+ * any none Other keys are allowed for
+ compatibility reasons, see
|shada-compatibility|.
- * (Unknown) Any other entry type is allowed for compatibility
+ * (Unknown) Any other entry type is allowed for compatibility
reasons, see |shada-compatibility|.
*E575* *E576*
-Errors in ShaDa file may have two types: E575 used for all “logical” errors
-and E576 used for all “critical” errors. Critical errors trigger behaviour
-described in |shada-error-handling| when writing and skipping the rest of the
+Errors in ShaDa file may have two types: E575 used for all “logical” errors
+and E576 used for all “critical” errors. Critical errors trigger behaviour
+described in |shada-error-handling| when writing and skipping the rest of the
file when reading and include:
*shada-critical-contents-errors*
- Any of first three MessagePack objects being not an unsigned integer.
-- Third object requesting amount of bytes greater then bytes left in the ShaDa
+- Third object requesting amount of bytes greater then bytes left in the ShaDa
file.
- Entry with zero type. I.e. first object being equal to zero.
- MessagePack parser failing to parse the entry data.
-- MessagePack parser consuming less or requesting greater bytes then described
- in the third object for parsing fourth object. I.e. when fourth object
- either contains more then one MessagePack object or it does not contain
+- MessagePack parser consuming less or requesting greater bytes then described
+ in the third object for parsing fourth object. I.e. when fourth object
+ either contains more then one MessagePack object or it does not contain
complete MessagePack object.
==============================================================================
Standard Paths *standard-path*
-Nvim stores configuration, data, and logs in standard locations. Plugins are
-strongly encouraged to follow this pattern also. Use |stdpath()| to get the
+Nvim stores configuration, data, and logs in standard locations. Plugins are
+strongly encouraged to follow this pattern also. Use |stdpath()| to get the
paths.
*base-directories* *xdg*
The "base" (root) directories conform to the XDG Base Directory Specification.
https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
-The $XDG_CONFIG_HOME and $XDG_DATA_HOME environment variables are used if they
-exist, otherwise default values (listed below) are used.
+The $XDG_CONFIG_HOME, $XDG_DATA_HOME, $XDG_RUNTIME_DIR, and $XDG_STATE_HOME
+environment variables are used if defined, else default values (listed below)
+are used.
CONFIG DIRECTORY (DEFAULT) ~
*$XDG_CONFIG_HOME* Nvim: stdpath("config")
@@ -1339,14 +1342,24 @@ DATA DIRECTORY (DEFAULT) ~
Unix: ~/.local/share ~/.local/share/nvim
Windows: ~/AppData/Local ~/AppData/Local/nvim-data
+RUN DIRECTORY (DEFAULT) ~
+ *$XDG_RUNTIME_DIR* Nvim: stdpath("run")
+ Unix: /tmp/nvim.user/xxx /tmp/nvim.user/xxx
+ Windows: $TMP/nvim.user/xxx $TMP/nvim.user/xxx
+
+STATE DIRECTORY (DEFAULT) ~
+ *$XDG_STATE_HOME* Nvim: stdpath("state")
+ Unix: ~/.local/state ~/.local/state/nvim
+ Windows: ~/AppData/Local ~/AppData/Local/nvim-data
+
Note: Throughout the user manual these defaults are used as placeholders, e.g.
"~/.config" is understood to mean "$XDG_CONFIG_HOME or ~/.config".
-LOG FILE *$NVIM_LOG_FILE*
+LOG FILE *$NVIM_LOG_FILE* *E5430*
Besides 'debug' and 'verbose', Nvim keeps a general log file for internal
debugging, plugins and RPC clients. >
:echo $NVIM_LOG_FILE
-By default, the file is located at stdpath('cache')/log unless that path
+By default, the file is located at stdpath('log')/log unless that path
is inaccessible or if $NVIM_LOG_FILE was set before |startup|.
diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt
index 2c5531411d..9ed3c37b8c 100644
--- a/runtime/doc/syntax.txt
+++ b/runtime/doc/syntax.txt
@@ -26,7 +26,8 @@ In the User Manual:
1. Quick start *:syn-qstart*
*:syn-enable* *:syntax-enable* *:syn-on* *:syntax-on*
-This command switches on syntax highlighting: >
+Syntax highlighting is enabled by default. If you need to enable it again
+after it was disabled (see below), use: >
:syntax enable
@@ -188,7 +189,8 @@ A syntax group name doesn't specify any color or attributes itself.
The name for a highlight or syntax group must consist of ASCII letters, digits
and the underscore. As a regexp: "[a-zA-Z0-9_]*". However, Vim does not give
-an error when using other characters.
+an error when using other characters. The maximum length of a group name is
+about 200 bytes. *E1249*
To be able to allow each user to pick their favorite set of colors, there must
be preferred names for highlight groups that are common for many languages.
@@ -451,7 +453,7 @@ your own highlight colors for the progress bar. Example: >
hi TOhtmlProgress guifg=#c0ffee ctermbg=7
<
*g:html_number_lines*
-Default: current 'number' setting.
+Default: Current 'number' setting.
When 0, buffer text is displayed in the generated HTML without line numbering.
When 1, a column of line numbers is added to the generated HTML with the same
highlighting as the line number column in Vim (|hl-LineNr|).
@@ -540,7 +542,7 @@ folded text will open the fold as if |g:html_hover_unfold| were set.
:let g:html_no_foldcolumn = 1
<
*TOhtml-uncopyable-text* *g:html_prevent_copy*
-Default: empty string.
+Default: Empty string.
This option prevents certain regions of the generated HTML from being copied,
when you select all text in document rendered in a browser and copy it. Useful
for allowing users to copy-paste only the source text even if a fold column or
@@ -566,7 +568,7 @@ uncopyable regions. In some browsers, especially older browsers, after
selecting an entire page and copying the selection, the <input> tags are not
pasted with the page text. If |g:html_no_invalid| is 0, the <input> tags have
invalid type; this works in more browsers, but the page will not validate.
-Note: this method does NOT work in recent versions of Chrome and equivalent
+Note: This method does NOT work in recent versions of Chrome and equivalent
browsers; the <input> tags get pasted with the text.
When "fallback" (default value), the same <input> elements are generated for
@@ -616,18 +618,18 @@ evaluate to get a unique string to append to each ID used in a given document,
so that the full IDs will be unique even when combined with other content in a
larger HTML document. Example, to append _ and the buffer number to each ID: >
- :let g:html_id_expr = '"_".bufnr("%")'
+ :let g:html_id_expr = '"_" .. bufnr("%")'
<
To append a string "_mystring" to the end of each ID: >
:let g:html_id_expr = '"_mystring"'
<
-Note, when converting a diff view to HTML, the expression will only be
+Note: When converting a diff view to HTML, the expression will only be
evaluated for the first window in the diff, and the result used for all the
windows.
*TOhtml-wrap-text* *g:html_pre_wrap*
-Default: current 'wrap' setting.
+Default: Current 'wrap' setting.
When 0, if |g:html_no_pre| is 0 or unset, the text in the generated HTML does
not wrap at the edge of the browser window.
When 1, if |g:html_use_css| is 1, the CSS 2.0 "white-space:pre-wrap" value is
@@ -687,7 +689,7 @@ wide browser support. However, you can override this to support specific
encodings that may not be automatically detected by default (see options
below). See http://www.iana.org/assignments/character-sets for the IANA names.
-Note, by default all Unicode encodings are converted to UTF-8 with no BOM in
+Note: By default all Unicode encodings are converted to UTF-8 with no BOM in
the generated HTML, as recommended by W3C:
http://www.w3.org/International/questions/qa-choosing-encodings
@@ -920,12 +922,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*
@@ -1287,8 +1293,8 @@ the global or buffer-local variable load_doxygen_syntax. This is done by
adding the following to your vimrc. >
:let g:load_doxygen_syntax=1
-There are a couple of variables that have an effect on syntax highlighting, and
-are to do with non-standard highlighting options.
+There are a couple of variables that have an effect on syntax highlighting,
+and are to do with non-standard highlighting options.
Variable Default Effect ~
g:doxygen_enhanced_color
@@ -1430,8 +1436,8 @@ To enable highlighting some special atoms, put this in your vimrc: >
ELIXIR *elixir.vim* *ft-elixir-syntax*
-Elixir is a dynamic, functional language for building scalable and maintainable
-applications.
+Elixir is a dynamic, functional language for building scalable and
+maintainable applications.
The following file extensions are auto-detected as Elixir file types:
@@ -1447,7 +1453,7 @@ Elixir.
FLEXWIKI *flexwiki.vim* *ft-flexwiki-syntax*
FlexWiki is an ASP.NET-based wiki package available at http://www.flexwiki.com
-NOTE: this site currently doesn't work, on Wikipedia is mentioned that
+NOTE: This site currently doesn't work, on Wikipedia is mentioned that
development stopped in 2009.
Syntax highlighting is available for the most common elements of FlexWiki
@@ -1470,7 +1476,7 @@ modes Conditional, Number, Statement, Comment, PreProc, Type, and String,
following the language specifications in 'Symbolic Manipulation with FORM' by
J.A.M. Vermaseren, CAN, Netherlands, 1991.
-If you want include your own changes to the default colors, you have to
+If you want to include your own changes to the default colors, you have to
redefine the following syntax groups:
- formConditional
@@ -1496,6 +1502,14 @@ The enhanced mode also takes advantage of additional color features for a dark
gvim display. Here, statements are colored LightYellow instead of Yellow, and
conditionals are LightBlue for better distinction.
+Both Visual Basic and FORM use the extension ".frm". 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 found, filetype will be "vb", otherwise "form".
+
+If the automatic detection doesn't work for you or you only edit, for
+example, FORM files, use this in your startup vimrc: >
+ :let filetype_frm = "form"
+
FORTH *forth.vim* *ft-forth-syntax*
@@ -1519,8 +1533,8 @@ syntax highlighting will not be correct if the form is incorrectly set.
When you create a new fortran file, the syntax script assumes fixed source
form. If you always use free source form, then >
:let fortran_free_source=1
-in your vimrc prior to the :syntax on command. If you always use fixed source
-form, then >
+in your vimrc prior to the :syntax on command. If you always use fixed
+source form, then >
:let fortran_fixed_source=1
in your vimrc prior to the :syntax on command.
@@ -1645,6 +1659,21 @@ because Fortran90 has no reserved words.
For further information related to fortran, see |ft-fortran-indent| and
|ft-fortran-plugin|.
+FREEBASIC *freebasic.vim* *ft-freebasic-syntax*
+
+FreeBASIC files will be highlighted differently for each of the four available
+dialects, "fb", "qb", "fblite" and "deprecated". See |ft-freebasic-plugin|
+for how to select the correct dialect.
+
+Highlighting is further configurable via the following variables.
+
+Variable Highlight ~
+*freebasic_no_comment_fold* disable multiline comment folding
+*freebasic_operators* non-alpha operators
+*freebasic_space_errors* trailing white space and spaces before a <Tab>
+*freebasic_type_suffixes* QuickBASIC style type suffixes
+
+
FVWM CONFIGURATION FILES *fvwm.vim* *ft-fvwm-syntax*
@@ -1793,8 +1822,8 @@ ends with -->) you can define >
JavaScript and Visual Basic embedded inside HTML documents are highlighted as
'Special' with statements, comments, strings and so on colored as in standard
-programming languages. Note that only JavaScript and Visual Basic are currently
-supported, no other scripting language has been added yet.
+programming languages. Note that only JavaScript and Visual Basic are
+currently supported, no other scripting language has been added yet.
Embedded and inlined cascading style sheets (CSS) are highlighted too.
@@ -1898,8 +1927,8 @@ The java.vim syntax highlighting file offers several options:
In Java 1.0.2 it was never possible to have braces inside parens, so this was
flagged as an error. Since Java 1.1 this is possible (with anonymous
-classes), and therefore is no longer marked as an error. If you prefer the old
-way, put the following line into your vim startup file: >
+classes), and therefore is no longer marked as an error. If you prefer the
+old way, put the following line into your vim startup file: >
:let java_mark_braces_in_parens_as_errors=1
All identifiers in java.lang.* are always visible in all classes. To
@@ -2017,10 +2046,10 @@ LISP *lisp.vim* *ft-lisp-syntax*
The lisp syntax highlighting provides two options: >
- g:lisp_instring : if it exists, then "(...)" strings are highlighted
+ g:lisp_instring : If it exists, then "(...)" strings are highlighted
as if the contents of the string were lisp.
Useful for AutoLisp.
- g:lisp_rainbow : if it exists and is nonzero, then differing levels
+ g:lisp_rainbow : If it exists and is nonzero, then differing levels
of parenthesization will receive different
highlighting.
<
@@ -2150,6 +2179,15 @@ $VIMRUNTIME/syntax/syntax.vim).
mv_finance mv_logic mv_powseries
+MARKDOWN *ft-markdown-syntax*
+
+If you have long regions there might be wrong highlighting. At the cost of
+slowing down displaying, you can have the engine look further back to sync on
+the start of a region, for example 500 lines: >
+
+ :let g:markdown_minlines = 500
+
+
MATHEMATICA *mma.vim* *ft-mma-syntax* *ft-mathematica-syntax*
Empty *.m files will automatically be presumed to be Matlab files unless you
@@ -2417,8 +2455,8 @@ If you do not want complex things like '@{${"foo"}}' to be parsed: >
(In Vim 6.x it was the other way around: "perl_extended_vars" enabled it.)
-The coloring strings can be changed. By default strings and qq friends will be
-highlighted like the first line. If you set the variable
+The coloring strings can be changed. By default strings and qq friends will
+be highlighted like the first line. If you set the variable
perl_string_as_statement, it will be highlighted as in the second line.
"hello world!"; qq|hello world|;
@@ -2430,8 +2468,8 @@ perl_string_as_statement, it will be highlighted as in the second line.
The syncing has 3 options. The first two switch off some triggering of
synchronization and should only be needed in case it fails to work properly.
If while scrolling all of a sudden the whole screen changes color completely
-then you should try and switch off one of those. Let me know if you can figure
-out the line that causes the mistake.
+then you should try and switch off one of those. Let me know if you can
+figure out the line that causes the mistake.
One triggers on "^\s*sub\s*" and the other on "^[$@%]" more or less. >
@@ -2468,7 +2506,7 @@ behavior, set 'perl_nofold_packages': >
PHP3 and PHP4 *php.vim* *php3.vim* *ft-php-syntax* *ft-php3-syntax*
-[note: previously this was called "php3", but since it now also supports php4
+[Note: Previously this was called "php3", but since it now also supports php4
it has been renamed to "php"]
There are the following options for the php syntax highlighting.
@@ -2538,15 +2576,15 @@ PPWizard is a preprocessor for HTML and OS/2 INF files
This syntax file has the options:
-- ppwiz_highlight_defs : determines highlighting mode for PPWizard's
+- ppwiz_highlight_defs : Determines highlighting mode for PPWizard's
definitions. Possible values are
ppwiz_highlight_defs = 1 : PPWizard #define statements retain the
- colors of their contents (e.g. PPWizard macros and variables)
+ colors of their contents (e.g. PPWizard macros and variables).
- ppwiz_highlight_defs = 2 : preprocessor #define and #evaluate
+ ppwiz_highlight_defs = 2 : Preprocessor #define and #evaluate
statements are shown in a single color with the exception of line
- continuation symbols
+ continuation symbols.
The default setting for ppwiz_highlight_defs is 1.
@@ -2585,7 +2623,7 @@ highlighted by defining the postscr_level variable as follows: >
If this variable is not defined it defaults to 2 (level 2) since this is
the most prevalent version currently.
-Note, not all PS interpreters will support all language features for a
+Note: Not all PS interpreters will support all language features for a
particular language level. In particular the %!PS-Adobe-3.0 at the start of
PS files does NOT mean the PostScript present is level 3 PostScript!
@@ -2687,7 +2725,7 @@ If you want all possible Python highlighting (the same as setting the
preceding last option and unsetting all other ones): >
:let python_highlight_all = 1
-Note: only existence of these options matter, not their value. You can replace
+Note: Only existence of these options matter, not their value. You can replace
1 above with anything.
QUAKE *quake.vim* *ft-quake-syntax*
@@ -3127,16 +3165,16 @@ The Speedup syntax file has some options:
the DECLARE section; if you defined own types, you have to include
them in the syntax file.
-- oneline_comments : this value ranges from 1 to 3 and determines the
+- oneline_comments : This value ranges from 1 to 3 and determines the
highlighting of # style comments.
- oneline_comments = 1 : allow normal Speedup code after an even
+ oneline_comments = 1 : Allow normal Speedup code after an even
number of #s.
- oneline_comments = 2 : show code starting with the second # as
+ oneline_comments = 2 : Show code starting with the second # as
error. This is the default setting.
- oneline_comments = 3 : show the whole line as error if it contains
+ oneline_comments = 3 : Show the whole line as error if it contains
more than one #.
Since especially OPERATION sections tend to become very large due to
@@ -3175,8 +3213,8 @@ This covers the shell named "tcsh". It is a superset of csh. See |csh.vim|
for how the filetype is detected.
Tcsh does not allow \" in strings unless the "backslash_quote" shell variable
-is set. If you want VIM to assume that no backslash quote constructs exist add
-this line to your vimrc: >
+is set. If you want VIM to assume that no backslash quote constructs exist
+add this line to your vimrc: >
:let tcsh_backslash_quote = 0
@@ -3529,7 +3567,7 @@ start and end tags. This can be turned on by >
:let g:xml_syntax_folding = 1
:set foldmethod=syntax
-Note: syntax folding might slow down syntax highlighting significantly,
+Note: Syntax folding might slow down syntax highlighting significantly,
especially for large files.
@@ -3546,8 +3584,8 @@ Do you want to draw with the mouse? Try the following: >
:function! GetPixel()
: let c = getline(".")[col(".") - 1]
: echo c
- : exe "noremap <LeftMouse> <LeftMouse>r".c
- : exe "noremap <LeftDrag> <LeftMouse>r".c
+ : exe "noremap <LeftMouse> <LeftMouse>r" .. c
+ : exe "noremap <LeftDrag> <LeftMouse>r" .. c
:endfunction
:noremap <RightMouse> <LeftMouse>:call GetPixel()<CR>
:set guicursor=n:hor20 " to see the color beneath the cursor
@@ -4384,7 +4422,7 @@ Leading context *:syn-lc* *:syn-leading* *:syn-context*
Note: This is an obsolete feature, only included for backwards compatibility
with previous Vim versions. It's now recommended to use the |/\@<=| construct
-in the pattern.
+in the pattern. You can also often use |/\zs|.
The "lc" offset specifies leading context -- a part of the pattern that must
be present, but is not considered part of the match. An offset of "lc=n" will
@@ -4817,6 +4855,7 @@ in their own color.
:hi[ghlight] {group-name}
List one highlight group.
+ *highlight-clear* *:hi-clear*
:hi[ghlight] clear Reset all highlighting to the defaults. Removes all
highlighting for groups added by the user!
Uses the current value of 'background' to decide which
@@ -4872,14 +4911,18 @@ the same syntax file on all UIs.
1. TUI highlight arguments
*bold* *underline* *undercurl*
- *inverse* *italic* *standout*
- *nocombine* *strikethrough*
+ *underdouble* *underdotted*
+ *underdashed* *inverse* *italic*
+ *standout* *nocombine* *strikethrough*
cterm={attr-list} *attr-list* *highlight-cterm* *E418*
- attr-list is a comma separated list (without spaces) of the
+ attr-list is a comma-separated list (without spaces) of the
following items (in any order):
bold
underline
undercurl curly underline
+ underdouble double underline
+ underdotted dotted underline
+ underdashed dashed underline
strikethrough
reverse
inverse same as reverse
@@ -4890,8 +4933,9 @@ cterm={attr-list} *attr-list* *highlight-cterm* *E418*
Note that "bold" can be used here and by using a bold font. They
have the same effect.
- "undercurl" falls back to "underline" in a terminal that does not
- support it. The color is set using |highlight-guisp|.
+ "undercurl", "underdouble", "underdotted", and "underdashed" fall back
+ to "underline" in a terminal that does not support them. The color is
+ set using |highlight-guisp|.
start={term-list} *highlight-start* *E422*
stop={term-list} *term-list* *highlight-stop*
@@ -4953,8 +4997,8 @@ ctermbg={color-nr} *highlight-ctermbg*
a number instead of a color name.
Note that for 16 color ansi style terminals (including xterms), the
- numbers in the NR-8 column is used. Here '*' means 'add 8' so that Blue
- is 12, DarkGray is 8 etc.
+ numbers in the NR-8 column is used. Here '*' means 'add 8' so that
+ Blue is 12, DarkGray is 8 etc.
Note that for some color terminals these names may result in the wrong
colors!
@@ -5024,8 +5068,8 @@ guifg={color-name} *highlight-guifg*
guibg={color-name} *highlight-guibg*
guisp={color-name} *highlight-guisp*
These give the foreground (guifg), background (guibg) and special
- (guisp) color to use in the GUI. "guisp" is used for undercurl
- and underline.
+ (guisp) color to use in the GUI. "guisp" is used for various
+ underlines.
There are a few special names:
NONE no color (transparent)
bg use normal background color
@@ -5067,53 +5111,56 @@ These are the builtin highlighting groups. Note that the highlighting depends
on the value of 'background'. You can see the current settings with the
":highlight" command.
*hl-ColorColumn*
-ColorColumn used for the columns set with 'colorcolumn'
+ColorColumn Used for the columns set with 'colorcolumn'.
*hl-Conceal*
-Conceal placeholder characters substituted for concealed
- text (see 'conceallevel')
+Conceal Placeholder characters substituted for concealed
+ text (see 'conceallevel').
+ *hl-CurSearch*
+CurSearch Used for highlighting a search pattern under the cursor
+ (see 'hlsearch').
*hl-Cursor*
-Cursor character under the cursor
-lCursor the character under the cursor when |language-mapping|
- is used (see 'guicursor')
+Cursor Character under the cursor.
+lCursor Character under the cursor when |language-mapping|
+ is used (see 'guicursor').
*hl-CursorIM*
-CursorIM like Cursor, but used when in IME mode |CursorIM|
+CursorIM Like Cursor, but used when in IME mode. |CursorIM|
*hl-CursorColumn*
CursorColumn Screen-column at the cursor, when 'cursorcolumn' is set.
*hl-CursorLine*
CursorLine Screen-line at the cursor, when 'cursorline' is set.
Low-priority if foreground (ctermfg OR guifg) is not set.
*hl-Directory*
-Directory directory names (and other special names in listings)
+Directory Directory names (and other special names in listings).
*hl-DiffAdd*
-DiffAdd diff mode: Added line |diff.txt|
+DiffAdd Diff mode: Added line. |diff.txt|
*hl-DiffChange*
-DiffChange diff mode: Changed line |diff.txt|
+DiffChange Diff mode: Changed line. |diff.txt|
*hl-DiffDelete*
-DiffDelete diff mode: Deleted line |diff.txt|
+DiffDelete Diff mode: Deleted line. |diff.txt|
*hl-DiffText*
-DiffText diff mode: Changed text within a changed line |diff.txt|
+DiffText Diff mode: Changed text within a changed line. |diff.txt|
*hl-EndOfBuffer*
-EndOfBuffer filler lines (~) after the end of the buffer.
+EndOfBuffer Filler lines (~) after the end of the buffer.
By default, this is highlighted like |hl-NonText|.
*hl-TermCursor*
-TermCursor cursor in a focused terminal
+TermCursor Cursor in a focused terminal.
*hl-TermCursorNC*
-TermCursorNC cursor in an unfocused terminal
+TermCursorNC Cursor in an unfocused terminal.
*hl-ErrorMsg*
-ErrorMsg error messages on the command line
- *hl-VertSplit*
-VertSplit the column separating vertically split windows
+ErrorMsg Error messages on the command line.
+ *hl-WinSeparator*
+WinSeparator Separators between window splits.
*hl-Folded*
-Folded line used for closed folds
+Folded Line used for closed folds.
*hl-FoldColumn*
FoldColumn 'foldcolumn'
*hl-SignColumn*
-SignColumn column where |signs| are displayed
+SignColumn Column where |signs| are displayed.
*hl-IncSearch*
IncSearch 'incsearch' highlighting; also used for the text replaced with
- ":s///c"
+ ":s///c".
*hl-Substitute*
-Substitute |:substitute| replacement text highlighting
+Substitute |:substitute| replacement text highlighting.
*hl-LineNr*
LineNr Line number for ":number" and ":#" commands, and when 'number'
@@ -5132,15 +5179,15 @@ CursorLineSign Like SignColumn when 'cursorline' is set for the cursor line.
*hl-CursorLineFold*
CursorLineFold Like FoldColumn when 'cursorline' is set for the cursor line.
*hl-MatchParen*
-MatchParen The character under the cursor or just before it, if it
+MatchParen Character under the cursor or just before it, if it
is a paired bracket, and its match. |pi_paren.txt|
*hl-ModeMsg*
-ModeMsg 'showmode' message (e.g., "-- INSERT --")
+ModeMsg 'showmode' message (e.g., "-- INSERT --").
*hl-MsgArea*
-MsgArea Area for messages and cmdline
+MsgArea Area for messages and cmdline.
*hl-MsgSeparator*
-MsgSeparator Separator for scrolled messages, `msgsep` flag of 'display'
+MsgSeparator Separator for scrolled messages, `msgsep` flag of 'display'.
*hl-MoreMsg*
MoreMsg |more-prompt|
*hl-NonText*
@@ -5149,21 +5196,21 @@ NonText '@' at the end of the window, characters from 'showbreak'
(e.g., ">" displayed when a double-wide character doesn't
fit at the end of the line). See also |hl-EndOfBuffer|.
*hl-Normal*
-Normal normal text
+Normal Normal text.
*hl-NormalFloat*
NormalFloat Normal text in floating windows.
*hl-NormalNC*
-NormalNC normal text in non-current windows
+NormalNC Normal text in non-current windows.
*hl-Pmenu*
-Pmenu Popup menu: normal item.
+Pmenu Popup menu: Normal item.
*hl-PmenuSel*
-PmenuSel Popup menu: selected item.
+PmenuSel Popup menu: Selected item.
*hl-PmenuSbar*
-PmenuSbar Popup menu: scrollbar.
+PmenuSbar Popup menu: Scrollbar.
*hl-PmenuThumb*
PmenuThumb Popup menu: Thumb of the scrollbar.
*hl-Question*
-Question |hit-enter| prompt and yes/no questions
+Question |hit-enter| prompt and yes/no questions.
*hl-QuickFixLine*
QuickFixLine Current |quickfix| item in the quickfix window. Combined with
|hl-CursorLine| when the cursor is there.
@@ -5171,7 +5218,7 @@ QuickFixLine Current |quickfix| item in the quickfix window. Combined with
Search Last search pattern highlighting (see 'hlsearch').
Also used for similar items that need to stand out.
*hl-SpecialKey*
-SpecialKey Unprintable characters: text displayed differently from what
+SpecialKey Unprintable characters: Text displayed differently from what
it really is. But not 'listchars' whitespace. |hl-Whitespace|
*hl-SpellBad*
SpellBad Word that is not recognized by the spellchecker. |spell|
@@ -5188,29 +5235,34 @@ SpellRare Word that is recognized by the spellchecker as one that is
hardly ever used. |spell|
Combined with the highlighting used otherwise.
*hl-StatusLine*
-StatusLine status line of current window
+StatusLine Status line of current window.
*hl-StatusLineNC*
-StatusLineNC status lines of not-current windows
- Note: if this is equal to "StatusLine" Vim will use "^^^" in
+StatusLineNC Status lines of not-current windows.
+ Note: If this is equal to "StatusLine", Vim will use "^^^" in
the status line of the current window.
*hl-TabLine*
-TabLine tab pages line, not active tab page label
+TabLine Tab pages line, not active tab page label.
*hl-TabLineFill*
-TabLineFill tab pages line, where there are no labels
+TabLineFill Tab pages line, where there are no labels.
*hl-TabLineSel*
-TabLineSel tab pages line, active tab page label
+TabLineSel Tab pages line, active tab page label.
*hl-Title*
-Title titles for output from ":set all", ":autocmd" etc.
+Title Titles for output from ":set all", ":autocmd" etc.
*hl-Visual*
-Visual Visual mode selection
+Visual Visual mode selection.
*hl-VisualNOS*
VisualNOS Visual mode selection when vim is "Not Owning the Selection".
*hl-WarningMsg*
-WarningMsg warning messages
+WarningMsg Warning messages.
*hl-Whitespace*
-Whitespace "nbsp", "space", "tab" and "trail" in 'listchars'
+Whitespace "nbsp", "space", "tab", "multispace", "lead" and "trail"
+ in 'listchars'.
*hl-WildMenu*
-WildMenu current match in 'wildmenu' completion
+WildMenu Current match in 'wildmenu' completion.
+ *hl-WinBar*
+WinBar Window bar of current window.
+ *hl-WinBarNC*
+WinBarNC Window bar of not-current windows.
*hl-User1* *hl-User1..9* *hl-User9*
The 'statusline' syntax allows the use of 9 different highlights in the
@@ -5345,11 +5397,12 @@ WARNING: The longer the tags file, the slower this will be, and the more
memory Vim will consume.
Only highlighting typedefs, unions and structs can be done too. For this you
-must use Exuberant ctags (found at http://ctags.sf.net).
+must use Universal Ctags (found at https://ctags.io) or Exuberant ctags (found
+at http://ctags.sf.net).
Put these lines in your Makefile:
-# Make a highlight file for types. Requires Exuberant ctags and awk
+# Make a highlight file for types. Requires Universal/Exuberant ctags and awk
types: types.vim
types.vim: *.[ch]
ctags --c-kinds=gstu -o- *.[ch] |\
@@ -5359,9 +5412,9 @@ types.vim: *.[ch]
And put these lines in your vimrc: >
" load the types.vim highlighting file, if it exists
- autocmd BufRead,BufNewFile *.[ch] let fname = expand('<afile>:p:h') . '/types.vim'
+ autocmd BufRead,BufNewFile *.[ch] let fname = expand('<afile>:p:h') .. '/types.vim'
autocmd BufRead,BufNewFile *.[ch] if filereadable(fname)
- autocmd BufRead,BufNewFile *.[ch] exe 'so ' . fname
+ autocmd BufRead,BufNewFile *.[ch] exe 'so ' .. fname
autocmd BufRead,BufNewFile *.[ch] endif
==============================================================================
@@ -5402,7 +5455,7 @@ To test your color setup, a file has been included in the Vim distribution.
To use it, execute this command: >
:runtime syntax/colortest.vim
-Nvim uses 256-color and |true-color| terminal capabilities whereever possible.
+Nvim uses 256-color and |true-color| terminal capabilities wherever possible.
==============================================================================
18. When syntax is slow *:syntime*
diff --git a/runtime/doc/tabpage.txt b/runtime/doc/tabpage.txt
index 7f91fda9f4..9197710819 100644
--- a/runtime/doc/tabpage.txt
+++ b/runtime/doc/tabpage.txt
@@ -129,11 +129,14 @@ something else.
:+tabclose " close the next tab page
:1tabclose " close the first tab page
:$tabclose " close the last tab page
- :tabclose -2 " close the 2nd previous tab page
+ :tabclose -2 " close the 2nd previous tab page
:tabclose + " close the next tab page
:tabclose 3 " close the third tab page
:tabclose $ " close the last tab page
-<
+ :tabclose # " close the last accessed tab page
+
+When a tab is closed the next tab page will become the current one.
+
*:tabo* *:tabonly*
:tabo[nly][!] Close all other tab pages.
When the 'hidden' option is set, all buffers in closed windows
@@ -159,6 +162,8 @@ something else.
" one
:tabonly 1 " close all tab pages except the first one
:tabonly $ " close all tab pages except the last one
+ :tabonly # " close all tab pages except the last
+ " accessed one
SWITCHING TO ANOTHER TAB PAGE:
@@ -181,6 +186,7 @@ gt *i_CTRL-<PageDown>* *i_<C-PageDown>*
:+2tabnext " go to the two next tab page
:1tabnext " go to the first tab page
:$tabnext " go to the last tab page
+ :tabnext # " go to the last accessed tab page
:tabnext $ " as above
:tabnext - " go to the previous tab page
:tabnext -1 " as above
@@ -190,10 +196,6 @@ gt *i_CTRL-<PageDown>* *i_<C-PageDown>*
{count}<C-PageDown>
{count}gt Go to tab page {count}. The first tab page has number one.
-CTRL-<Tab> *CTRL-<Tab>*
-CTRL-W g<Tab> *g<Tab>* *CTRL-W_g<Tab>*
-g<Tab> Go to previous (last accessed) tab page.
-
:tabp[revious] *:tabp* *:tabprevious* *gT* *:tabN*
:tabN[ext] *:tabNext* *CTRL-<PageUp>*
<C-PageUp> *<C-PageUp>* *i_CTRL-<PageUp>* *i_<C-PageUp>*
@@ -213,6 +215,9 @@ gT Go to the previous tab page. Wraps around from the first one
*:tabl* *:tablast*
:tabl[ast] Go to the last tab page.
+<C-Tab> *CTRL-<Tab>* *<C-Tab>*
+CTRL-W g<Tab> *g<Tab>* *CTRL-W_g<Tab>*
+g<Tab> Go to the last accessed tab page.
Other commands:
*:tabs*
@@ -245,6 +250,8 @@ REORDERING TAB PAGES:
:tabmove " move the tab page to the last
:$tabmove " as above
:tabmove $ " as above
+ :tabmove # " move the tab page after the last accessed
+ " tab page
:tabm[ove] +[N]
:tabm[ove] -[N]
@@ -366,24 +373,24 @@ pages and define labels for them. Then get the label for each tab page. >
for i in range(tabpagenr('$'))
" select the highlighting
if i + 1 == tabpagenr()
- let s .= '%#TabLineSel#'
+ let s ..= '%#TabLineSel#'
else
- let s .= '%#TabLine#'
+ let s ..= '%#TabLine#'
endif
" set the tab page number (for mouse clicks)
- let s .= '%' . (i + 1) . 'T'
+ let s ..= '%' .. (i + 1) .. 'T'
" the label is made by MyTabLabel()
- let s .= ' %{MyTabLabel(' . (i + 1) . ')} '
+ let s ..= ' %{MyTabLabel(' .. (i + 1) .. ')} '
endfor
" after the last tab fill with TabLineFill and reset tab page nr
- let s .= '%#TabLineFill#%T'
+ let s ..= '%#TabLineFill#%T'
" right-align the label to close the current tab page
if tabpagenr('$') > 1
- let s .= '%=%#TabLine#%999Xclose'
+ let s ..= '%=%#TabLine#%999Xclose'
endif
return s
@@ -446,14 +453,14 @@ windows in the tab page and a '+' if there is a modified buffer: >
" Append the number of windows in the tab page if more than one
let wincount = tabpagewinnr(v:lnum, '$')
if wincount > 1
- let label .= wincount
+ let label ..= wincount
endif
if label != ''
- let label .= ' '
+ let label ..= ' '
endif
" Append the buffer name
- return label . bufname(bufnrlist[tabpagewinnr(v:lnum) - 1])
+ return label .. bufname(bufnrlist[tabpagewinnr(v:lnum) - 1])
endfunction
set guitablabel=%{GuiTabLabel()}
diff --git a/runtime/doc/tagsrch.txt b/runtime/doc/tagsrch.txt
index 4d938c4a23..2485290667 100644
--- a/runtime/doc/tagsrch.txt
+++ b/runtime/doc/tagsrch.txt
@@ -544,7 +544,8 @@ also works. The <CR> and <NL> characters can never appear inside a line.
The second format is new. It includes additional information in optional
fields at the end of each line. It is backwards compatible with Vi. It is
-only supported by new versions of ctags (such as Exuberant ctags).
+only supported by new versions of ctags (such as Universal ctags or Exuberant
+ctags).
{tagname} The identifier. Normally the name of a function, but it can
be any identifier. It cannot contain a <Tab>.
@@ -705,7 +706,7 @@ matches the pattern "^# *define" it is not considered to be a comment.
If you want to list matches, and then select one to jump to, you could use a
mapping to do that for you. Here is an example: >
- :map <F4> [I:let nr = input("Which one: ")<Bar>exe "normal " . nr ."[\t"<CR>
+ :map <F4> [I:let nr = input("Which one: ")<Bar>exe "normal " .. nr .. "[\t"<CR>
<
*[i*
[i Display the first line that contains the keyword
diff --git a/runtime/doc/term.txt b/runtime/doc/term.txt
index 62e13285f5..cd6798a5de 100644
--- a/runtime/doc/term.txt
+++ b/runtime/doc/term.txt
@@ -108,6 +108,58 @@ and right scroll margins as well. If Nvim detects that the terminal is Xterm,
it will make use of this ability to speed up scrolling that is not the full
width of the terminal.
+ *tui-input*
+Nvim uses libtermkey to convert terminal escape sequences to key codes.
+|terminfo| is used first, and CSI sequences not in |terminfo| (including
+extended keys a.k.a. modifyOtherKeys or "CSI u") can also be parsed.
+For example, when running Nvim in tmux, this makes Nvim leave Insert mode and
+go to the window below: >
+ tmux send-keys 'Escape' [ 2 7 u 'C-W' j
+Where `'Escape' [ 2 7 u` is an unambiguous "CSI u" sequence for the <Esc> key.
+
+The kitty keyboard protocol https://sw.kovidgoyal.net/kitty/keyboard-protocol/
+is partially supported, including keypad keys in Unicode Private Use Area.
+For example, this sequence is recognized by Nvim as <C-kEnter>: >
+ CSI 57414 ; 5 u
+and can be used differently from <C-CR> in mappings.
+
+ *tui-modifyOtherKeys* *tui-csiu*
+Historically, terminal emulators could not distinguish between certain control
+key modifiers and other keys. For example, <C-I> and <Tab> are represented the
+same way, as are <Esc> and <C-[>, <CR> and <C-M>, and <NL> and <C-J>. This
+meant that Nvim also could not map these keys separately.
+
+Modern terminal emulators are able to distinguish between these pairs of keys
+by encoding control modifiers differently. There are two common but distinct
+ways of doing this, known as "modifyOtherKeys" and "CSI u". Nvim supports both
+encoding methods and at startup will tell the terminal emulator that it
+understands these key encodings. If your terminal emulator supports it then
+this will allow you to map the key pairs listed above separately.
+
+At startup Nvim will query your terminal to see if it supports the CSI u
+encoding by writing the sequence >
+
+ CSI ? u CSI c
+
+If your terminal emulator responds with >
+
+ CSI ? <flags> u
+
+this means your terminal supports the CSI u encoding and Nvim will tell your
+terminal to enable it by writing the sequence >
+
+ CSI > 1 u
+
+If your terminal does not support CSI u then Nvim will instead enable the
+"modifyOtherKeys" encoding by writing the sequence >
+
+ CSI > 4 ; 2 m
+
+When Nvim exits cleanly it will send the corresponding sequence to disable the
+special key encoding. If Nvim does not exit cleanly then your terminal
+emulator could be in a bad state. If this happens, simply run "reset".
+
+
*tui-colors*
Nvim uses 256 colours by default, ignoring |terminfo| for most terminal types,
including "linux" (whose virtual terminals have had 256-colour support since
@@ -321,7 +373,7 @@ an #if/#else/#endif block, the selection becomes linewise.
For MS-Windows and xterm the time for double clicking can be set with the
'mousetime' option. For the other systems this time is defined outside of Vim.
An example, for using a double click to jump to the tag under the cursor: >
- :map <2-LeftMouse> :exe "tag ". expand("<cword>")<CR>
+ :map <2-LeftMouse> :exe "tag " .. expand("<cword>")<CR>
Dragging the mouse with a double click (button-down, button-up, button-down
and then drag) will result in whole words to be selected. This continues
diff --git a/runtime/doc/testing.txt b/runtime/doc/testing.txt
index 8ec66d26a4..4e4a908d0f 100644
--- a/runtime/doc/testing.txt
+++ b/runtime/doc/testing.txt
@@ -12,7 +12,7 @@ and for testing plugins.
1. Testing Vim |testing|
2. Test functions |test-functions-details|
-3. Assert funtions |assert-functions-details|
+3. Assert functions |assert-functions-details|
==============================================================================
1. Testing Vim *testing*
diff --git a/runtime/doc/tips.txt b/runtime/doc/tips.txt
index b77c7d9a6d..d913b53c6b 100644
--- a/runtime/doc/tips.txt
+++ b/runtime/doc/tips.txt
@@ -84,14 +84,14 @@ What you need:
create it with the shell command "mkid file1 file2 ..".
Put this in your |init.vim|: >
- map _u :call ID_search()<Bar>execute "/\\<" . g:word . "\\>"<CR>
- map _n :n<Bar>execute "/\\<" . g:word . "\\>"<CR>
+ map _u :call ID_search()<Bar>execute "/\\<" .. g:word .. "\\>"<CR>
+ map _n :n<Bar>execute "/\\<" .. g:word .. "\\>"<CR>
function! ID_search()
let g:word = expand("<cword>")
- let x = system("lid --key=none ". g:word)
+ let x = system("lid --key=none " .. g:word)
let x = substitute(x, "\n", " ", "g")
- execute "next " . x
+ execute "next " .. x
endfun
To use it, place the cursor on a word, type "_u" and vim will load the file
@@ -285,13 +285,13 @@ This mapping will format any bullet list. It requires that there is an empty
line above and below each list entry. The expression commands are used to
be able to give comments to the parts of the mapping. >
- :let m = ":map _f :set ai<CR>" " need 'autoindent' set
- :let m = m . "{O<Esc>" " add empty line above item
- :let m = m . "}{)^W" " move to text after bullet
- :let m = m . "i <CR> <Esc>" " add space for indent
- :let m = m . "gq}" " format text after the bullet
- :let m = m . "{dd" " remove the empty line
- :let m = m . "5lDJ" " put text after bullet
+ :let m = ":map _f :set ai<CR>" " need 'autoindent' set
+ :let m ..= "{O<Esc>" " add empty line above item
+ :let m ..= "}{)^W" " move to text after bullet
+ :let m ..= "i <CR> <Esc>" " add space for indent
+ :let m ..= "gq}" " format text after the bullet
+ :let m ..= "{dd" " remove the empty line
+ :let m ..= "5lDJ" " put text after bullet
:execute m |" define the mapping
(<> notation |<>|. Note that this is all typed literally. ^W is "^" "W", not
@@ -429,15 +429,15 @@ A slightly more advanced version is used in the |matchparen| plugin.
let c = '\['
let c2 = '\]'
endif
- let s_skip ='synIDattr(synID(line("."), col("."), 0), "name") ' .
+ let s_skip ='synIDattr(synID(line("."), col("."), 0), "name") ' ..
\ '=~? "string\\|comment"'
execute 'if' s_skip '| let s_skip = 0 | endif'
let [m_lnum, m_col] = searchpairpos(c, '', c2, s_flags, s_skip)
if m_lnum > 0 && m_lnum >= line('w0') && m_lnum <= line('w$')
- exe 'match Search /\(\%' . c_lnum . 'l\%' . c_col .
- \ 'c\)\|\(\%' . m_lnum . 'l\%' . m_col . 'c\)/'
+ exe 'match Search /\(\%' .. c_lnum .. 'l\%' .. c_col ..
+ \ 'c\)\|\(\%' .. m_lnum .. 'l\%' .. m_col .. 'c\)/'
let s:paren_hl_on = 1
endif
endfunction
diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt
index 7de6a0f890..eb19bf5934 100644
--- a/runtime/doc/treesitter.txt
+++ b/runtime/doc/treesitter.txt
@@ -14,10 +14,12 @@ VIM.TREESITTER *lua-treesitter*
Nvim integrates the tree-sitter library for incremental parsing of buffers.
*vim.treesitter.language_version*
-To check which language version is compiled with neovim, the number is stored
-within `vim.treesitter.language_version`. This number is not too helpful
-unless you are wondering about compatibility between different versions of
-compiled grammars.
+The latest parser ABI version that is supported by the bundled tree-sitter
+library.
+
+ *vim.treesitter.minimum_language_version*
+The earliest parser ABI version that is supported by the bundled tree-sitter
+library.
Parser files *treesitter-parsers*
@@ -49,10 +51,10 @@ Whenever you need to access the current syntax tree, parse the buffer: >
tstree = parser:parse()
-<This will return a table of immutable trees that represent the current state of the
-buffer. When the plugin wants to access the state after a (possible) edit
-it should call `parse()` again. If the buffer wasn't edited, the same tree will
-be returned again without extra work. If the buffer was parsed before,
+<This will return a table of immutable trees that represent the current state
+of the buffer. When the plugin wants to access the state after a (possible)
+edit it should call `parse()` again. If the buffer wasn't edited, the same tree
+will be returned again without extra work. If the buffer was parsed before,
incremental parsing will be done of the changed parts.
Note: to use the parser directly inside a |nvim_buf_attach| Lua callback, you
@@ -61,9 +63,10 @@ parsing shouldn't be done directly in the change callback anyway as they will
be very frequent. Rather a plugin that does any kind of analysis on a tree
should use a timer to throttle too frequent updates.
-tsparser:set_included_regions({region_list}) *tsparser:set_included_regions()*
+tsparser:set_included_regions({region_list}) *tsparser:set_included_regions()*
Changes the regions the parser should consider. This is used for
- language injection. {region_list} should be of the form (all zero-based): >
+ language injection. {region_list} should be of the form
+ (all zero-based): >
{
{node1, node2},
...
@@ -92,13 +95,13 @@ tsnode:next_sibling() *tsnode:next_sibling()*
tsnode:prev_sibling() *tsnode:prev_sibling()*
Get the node's previous sibling.
-tsnode:next_named_sibling() *tsnode:next_named_sibling()*
+tsnode:next_named_sibling() *tsnode:next_named_sibling()*
Get the node's next named sibling.
-tsnode:prev_named_sibling() *tsnode:prev_named_sibling()*
+tsnode:prev_named_sibling() *tsnode:prev_named_sibling()*
Get the node's previous named sibling.
-tsnode:iter_children() *tsnode:iter_children()*
+tsnode:iter_children() *tsnode:iter_children()*
Iterates over all the direct children of {tsnode}, regardless of
whether they are named or not.
Returns the child node plus the eventual field name corresponding to
@@ -114,10 +117,10 @@ tsnode:child({index}) *tsnode:child()*
Get the node's child at the given {index}, where zero represents the
first child.
-tsnode:named_child_count() *tsnode:named_child_count()*
+tsnode:named_child_count() *tsnode:named_child_count()*
Get the node's number of named children.
-tsnode:named_child({index}) *tsnode:named_child()*
+tsnode:named_child({index}) *tsnode:named_child()*
Get the node's named child at the given {index}, where zero represents
the first named child.
@@ -157,20 +160,20 @@ tsnode:sexpr() *tsnode:sexpr()*
tsnode:id() *tsnode:id()*
Get an unique identifier for the node inside its own tree.
- No guarantees are made about this identifier's internal representation,
- except for being a primitive lua type with value equality (so not a table).
- Presently it is a (non-printable) string.
+ No guarantees are made about this identifier's internal
+ representation, except for being a primitive lua type with value
+ equality (so not a table). Presently it is a (non-printable) string.
Note: the id is not guaranteed to be unique for nodes from different
trees.
+ *tsnode:descendant_for_range()*
tsnode:descendant_for_range({start_row}, {start_col}, {end_row}, {end_col})
- *tsnode:descendant_for_range()*
Get the smallest node within this node that spans the given range of
(row, column) positions
+ *tsnode:named_descendant_for_range()*
tsnode:named_descendant_for_range({start_row}, {start_col}, {end_row}, {end_col})
- *tsnode:named_descendant_for_range()*
Get the smallest named node within this node that spans the given
range of (row, column) positions
@@ -192,11 +195,11 @@ and predicates. A `capture` allows you to associate names with a specific
node in a pattern. A `predicate` adds arbitrary metadata and conditional data
to a match.
-Treesitter Query Predicates *lua-treesitter-predicates*
+Treesitter Query Predicates *lua-treesitter-predicates*
When writing queries for treesitter, one might use `predicates`, that is,
-special scheme nodes that are evaluated to verify things on a captured node for
-example, the |eq?| predicate : >
+special scheme nodes that are evaluated to verify things on a captured node
+for example, the |eq?| predicate : >
((identifier) @foo (#eq? @foo "foo"))
This will only match identifier corresponding to the `"foo"` text.
@@ -204,100 +207,102 @@ Here is a list of built-in predicates :
`eq?` *ts-predicate-eq?*
This predicate will check text correspondence between nodes or
- strings : >
+ strings: >
((identifier) @foo (#eq? @foo "foo"))
((node1) @left (node2) @right (#eq? @left @right))
<
`match?` *ts-predicate-match?*
- `vim-match?` *ts-predicate-vim-match?*
+ `vim-match?` *ts-predicate-vim-match?*
This will match if the provided vim regex matches the text
- corresponding to a node : >
+ corresponding to a node: >
((identifier) @constant (#match? @constant "^[A-Z_]+$"))
< Note: the `^` and `$` anchors will respectively match the
start and end of the node's text.
- `lua-match?` *ts-predicate-lua-match?*
+ `lua-match?` *ts-predicate-lua-match?*
This will match the same way than |match?| but using lua
regexes.
- `contains?` *ts-predicate-contains?*
+ `contains?` *ts-predicate-contains?*
Will check if any of the following arguments appears in the
- text corresponding to the node : >
+ text corresponding to the node: >
((identifier) @foo (#contains? @foo "foo"))
((identifier) @foo-bar (#contains @foo-bar "foo" "bar"))
<
- `any-of?` *ts-predicate-any-of?*
- Will check if the text is the same as any of the following.
+ `any-of?` *ts-predicate-any-of?*
+ Will check if the text is the same as any of the following
+ arguments: >
+ ((identifier) @foo (#any-of? @foo "foo" "bar"))
+<
This is the recommended way to check if the node matches one
of many keywords for example, as it has been optimized for
this.
- arguments : >
- ((identifier) @foo (#any-of? @foo "foo" "bar"))
<
- *lua-treesitter-not-predicate*
+ *lua-treesitter-not-predicate*
Each predicate has a `not-` prefixed predicate that is just the negation of
the predicate.
- *vim.treesitter.query.add_predicate()*
+ *vim.treesitter.query.add_predicate()*
vim.treesitter.query.add_predicate({name}, {handler})
This adds a predicate with the name {name} to be used in queries.
{handler} should be a function whose signature will be : >
handler(match, pattern, bufnr, predicate)
<
- *vim.treesitter.query.list_predicates()*
+ *vim.treesitter.query.list_predicates()*
vim.treesitter.query.list_predicates()
This lists the currently available predicates to use in queries.
-Treesitter Query Directive *lua-treesitter-directives*
+Treesitter Query Directive *lua-treesitter-directives*
-Treesitter queries can also contain `directives`. Directives store metadata for a node
-or match and perform side effects. For example, the |set!| predicate sets metadata on
-the match or node : >
+Treesitter queries can also contain `directives`. Directives store metadata
+for a node or match and perform side effects. For example, the |set!|
+predicate sets metadata on the match or node : >
((identifier) @foo (#set! "type" "parameter"))
-Here is a list of built-in directives:
+Built-in directives:
+
+ `set!` *ts-directive-set!*
+ Sets key/value metadata for a specific match or capture.
+ Value is accessible as either `metadata[key]` (match
+ specific) or `metadata[capture_id][key]` (capture specific).
- `set!` *ts-directive-set!*
- Sets key/value metadata for a specific node or match : >
- ((identifier) @foo (#set! @foo "kind" "parameter"))
- ((node1) @left (node2) @right (#set! "type" "pair"))
+ Parameters: ~
+ {capture_id} (optional)
+ {key}
+ {value}
+
+ Examples: >
+ ((identifier) @foo (#set! @foo "kind" "parameter"))
+ ((node1) @left (node2) @right (#set! "type" "pair"))
<
- `offset!` *ts-predicate-offset!*
- Takes the range of the captured node and applies the offsets
- to it's range : >
- ((identifier) @constant (#offset! @constant 0 1 0 -1))
-< This will generate a range object for the captured node with the
- offsets applied. The arguments are
- `({capture_id}, {start_row}, {start_col}, {end_row}, {end_col}, {key?})`
- The default key is "offset".
-
- *vim.treesitter.query.add_directive()*
-vim.treesitter.query.add_directive({name}, {handler})
-
-This adds a directive with the name {name} to be used in queries.
-{handler} should be a function whose signature will be : >
- handler(match, pattern, bufnr, predicate, metadata)
-Handlers can set match level data by setting directly on the metadata object `metadata.key = value`
-Handlers can set node level data by using the capture id on the metadata table
-`metadata[capture_id].key = value`
+ `offset!` *ts-directive-offset!*
+ Takes the range of the captured node and applies an offset.
+ This will generate a new range object for the captured node
+ as `metadata[capture_id].range`.
- *vim.treesitter.query.list_directives()*
-vim.treesitter.query.list_directives()
+ Parameters: ~
+ {capture_id}
+ {start_row}
+ {start_col}
+ {end_row}
+ {end_col}
-This lists the currently available directives to use in queries.
+ Example: >
+ ((identifier) @constant (#offset! @constant 0 1 0 -1))
+<
-Treesitter syntax highlighting (WIP) *lua-treesitter-highlight*
+Treesitter syntax highlighting (WIP) *lua-treesitter-highlight*
NOTE: This is a partially implemented feature, and not usable as a default
solution yet. What is documented here is a temporary interface intended
for those who want to experiment with this feature and contribute to
its development.
-Highlights are defined in the same query format as in the tree-sitter highlight
-crate, with some limitations and additions. Set a highlight query for a
-buffer with this code: >
+Highlights are defined in the same query format as in the tree-sitter
+highlight crate, with some limitations and additions. Set a highlight query
+for a buffer with this code: >
local query = [[
"for" @keyword
@@ -338,7 +343,8 @@ Treesitter Highlighting Priority *lua-treesitter-highlight-priority*
Tree-sitter uses |nvim_buf_set_extmark()| to set highlights with a default
priority of 100. This enables plugins to set a highlighting priority lower or
higher than tree-sitter. It is also possible to change the priority of an
-individual query pattern manually by setting its `"priority"` metadata attribute: >
+individual query pattern manually by setting its `"priority"` metadata
+attribute: >
(
(super_important_node) @ImportantHighlight
@@ -404,10 +410,15 @@ Lua module: vim.treesitter.query *treesitter-query*
add_directive({name}, {handler}, {force}) *add_directive()*
Adds a new directive to be used in queries
+ Handlers can set match level data by setting directly on the
+ metadata object `metadata.key = value`, additionally, handlers
+ can set node level data by using the capture id on the
+ metadata table `metadata[capture_id].key = value`
+
Parameters: ~
{name} the name of the directive, without leading #
{handler} the handler function to be used signature will
- be (match, pattern, bufnr, predicate)
+ be (match, pattern, bufnr, predicate, metadata)
add_predicate({name}, {handler}, {force}) *add_predicate()*
Adds a new predicate to be used in queries
@@ -446,6 +457,8 @@ get_query_files({lang}, {query_name}, {is_included})
as `nil`
list_directives() *list_directives()*
+ Lists the currently available directives to use in queries.
+
Return: ~
The list of supported directives.
@@ -461,14 +474,15 @@ parse_query({lang}, {query}) *parse_query()*
can be used to search nodes in the syntax tree for the
patterns defined in {query} using `iter_*` methods below.
- Exposes `info` and `captures` with additional information about the {query}.
+ Exposes `info` and `captures` with additional context about {query}.
• `captures` contains the list of unique capture names defined
- in {query}. - `info.captures` also points to `captures` .
+ in {query}. -`info.captures` also points to `captures`.
• `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
@@ -522,8 +536,8 @@ Query:iter_matches({self}, {node}, {source}, {start}, {stop})
a table mapping capture indices to nodes, and metadata from
any directives processing the match. If the query has more
than one pattern the capture table might be sparse, and e.g.
- `pairs()` method should be used over `ipairs` . Here an
- example iterating over all captures in every match:
+ `pairs()` method should be used over `ipairs`. Here an example
+ iterating over all captures in every match:
>
for pattern, match, metadata in cquery:iter_matches(tree:root(), bufnr, first, last) do
@@ -608,10 +622,7 @@ LanguageTree:children({self}) *LanguageTree:children()*
{self}
LanguageTree:contains({self}, {range}) *LanguageTree:contains()*
- Determines whether This goes down the tree to recursively check children.
-
- Parameters: ~
- {range} is contained in this language tree
+ Determines whether {range} is contained in this language tree
Parameters: ~
{range} A range, that is a `{ start_line, start_col,
@@ -621,8 +632,9 @@ LanguageTree:contains({self}, {range}) *LanguageTree:contains()*
LanguageTree:destroy({self}) *LanguageTree:destroy()*
Destroys this language tree and all its children.
- Any cleanup logic should be performed here. Note, this DOES
- NOT remove this tree from a parent. `remove_child` must be called on the parent to remove it.
+ Any cleanup logic should be performed here.
+
+ Note: This DOES NOT remove this tree from a parent. Instead, `remove_child` must be called on the parent to remove it.
Parameters: ~
{self}
@@ -665,7 +677,8 @@ 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,
+ call `parse()` . This will return the updated tree.
Parameters: ~
{self}
@@ -678,7 +691,7 @@ LanguageTree:lang({self}) *LanguageTree:lang()*
*LanguageTree:language_for_range()*
LanguageTree:language_for_range({self}, {range})
- Gets the appropriate language that contains
+ Gets the appropriate language that contains {range}
Parameters: ~
{range} A text range, see |LanguageTree:contains|
@@ -694,13 +707,22 @@ LanguageTree:parse({self}) *LanguageTree:parse()*
{self}
LanguageTree:register_cbs({self}, {cbs}) *LanguageTree:register_cbs()*
- Registers callbacks for the parser
-
- Parameters: ~
- {cbs} An `nvim_buf_attach` -like table argument with the following keys : `on_bytes` : see `nvim_buf_attach` , but this will be called after the parsers callback. `on_changedtree` : a callback that will be called every time the
- tree has syntactical changes. it will only be
- passed one argument, that is a table of the ranges
- (as node ranges) that changed. `on_child_added` : emitted when a child is added to the tree. `on_child_removed` : emitted when a child is removed from the tree.
+ Registers callbacks for the parser.
+
+ Parameters: ~
+ {cbs} (table) An |nvim_buf_attach()|-like table argument
+ with the following keys :
+ • `on_bytes` : see |nvim_buf_attach()|, but this will be
+ called after the parsers callback.
+ • `on_changedtree` : a callback that will be
+ called every time the tree has syntactical
+ changes. It will only be passed one argument,
+ which is a table of the ranges (as node ranges)
+ that changed.
+ • `on_child_added` : emitted when a child is added
+ to the tree.
+ • `on_child_removed` : emitted when a child is
+ removed from the tree.
{self}
LanguageTree:remove_child({self}, {lang}) *LanguageTree:remove_child()*
@@ -732,8 +754,8 @@ LanguageTree:set_included_regions({self}, {regions})
parsed again.
Parameters: ~
- {regions} A list of regions this tree should manage and
- parse.
+ {regions} (table) list of regions this tree should manage
+ and parse.
{self}
LanguageTree:source({self}) *LanguageTree:source()*
diff --git a/runtime/doc/uganda.txt b/runtime/doc/uganda.txt
index 79519da51e..23dfa082a0 100644
--- a/runtime/doc/uganda.txt
+++ b/runtime/doc/uganda.txt
@@ -129,11 +129,12 @@ Kibaale Children's Centre *kcc* *Kibaale* *charity*
Kibaale Children's Centre (KCC) is located in Kibaale, a small town in the
south of Uganda, near Tanzania, in East Africa. The area is known as Rakai
District. The population is mostly farmers. Although people are poor, there
-is enough food. But this district is suffering from AIDS more than any other
-part of the world. Some say that it started there. Estimations are that 10
-to 30% of the Ugandans are infected with HIV. Because parents die, there are
-many orphans. In this district about 60,000 children have lost one or both
-parents, out of a population of 350,000. And this is still continuing.
+usually is enough food. But this district is suffering from AIDS more than
+any other part of the world. Some say that it started there. Estimations are
+that in the past 10 to 30% of the Ugandans are infected with HIV. Because
+parents die, there are many orphans. In this district about 60,000 children
+have lost one or both parents, out of a population of 350,000. Although AIDS
+is now mostly under control, the problems are still continuing.
The children need a lot of help. The KCC is working hard to provide the needy
with food, medical care and education. Food and medical care to keep them
diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt
index e7be14e732..3fb9ed1125 100644
--- a/runtime/doc/ui.txt
+++ b/runtime/doc/ui.txt
@@ -49,6 +49,14 @@ with these (optional) keys:
'wildmenu'. |ui-popupmenu|
`ext_tabline` Externalize the tabline. |ui-tabline|
`ext_termcolors` Use external default colors.
+ `term_name` Sets the name of the terminal 'term'.
+ `term_colors` Sets the number of supported colors 't_Co'.
+ `term_background` Sets the default value of 'background'.
+ `stdin_fd` Read buffer from `fd` as if it was a stdin pipe
+ This option can only used by |--embed| ui,
+ see |ui-startup-stdin|.
+
+
Specifying an unknown option is an error; UIs can check the |api-metadata|
`ui_options` key for supported options.
@@ -137,6 +145,19 @@ procedure:
Inside this request handler, the UI can safely do any initialization before
entering normal mode, for example reading variables set by init.vim.
+ *ui-startup-stdin*
+An UI can support the native read from stdin feature as invoked with
+`command | nvim -` for the builtin TUI. |--|
+The embedding process can detect that its stdin is open to a file which
+not is a terminal, just like nvim does. It then needs to forward this fd
+to Nvim. As fd=0 is already is used to send rpc data from the embedder to
+Nvim, it needs to use some other file descriptor, like fd=3 or higher.
+
+Then, `stdin_fd` option should be passed to `nvim_ui_attach` and nvim will
+implicitly read it as a buffer. This option can only be used when Nvim is
+launched with `--embed` option, as described above.
+
+
==============================================================================
Global Events *ui-global*
@@ -283,19 +304,23 @@ numerical highlight ids to the actual attributes.
attributes specified by the `rgb_attr` and `cterm_attr` dicts, with the
following (all optional) keys.
- `foreground`: foreground color.
- `background`: background color.
- `special`: color to use for underline and undercurl, when present.
- `reverse`: reverse video. Foreground and background colors are
- switched.
- `italic`: italic text.
- `bold`: bold text.
- `strikethrough`: struckthrough text.
- `underline`: underlined text. The line has `special` color.
- `undercurl`: undercurled text. The curl has `special` color.
- `blend`: Blend level (0-100). Could be used by UIs to support
- blending floating windows to the background or to
- signal a transparent cursor.
+ `foreground`: foreground color.
+ `background`: background color.
+ `special`: color to use for various underlines, when
+ present.
+ `reverse`: reverse video. Foreground and background colors
+ are switched.
+ `italic`: italic text.
+ `bold`: bold text.
+ `strikethrough`: struckthrough text.
+ `underline`: underlined text. The line has `special` color.
+ `undercurl`: undercurled text. The curl has `special` color.
+ `underdouble`: double underlined text. The lines have `special` color.
+ `underdotted`: underdotted text. The dots have `special` color.
+ `underdashed`: underdashed text. The dashes have `special` color.
+ `blend`: Blend level (0-100). Could be used by UIs to
+ support blending floating windows to the
+ background or to signal a transparent cursor.
For absent color keys the default color should be used. Don't store
the default value in the table, rather a sentinel value, so that
@@ -444,7 +469,7 @@ is not active. New UIs should implement |ui-linegrid| instead.
`foreground`: foreground color.
`background`: background color.
- `special`: color to use for underline and undercurl, when present.
+ `special`: color to use for various underlines, when present.
`reverse`: reverse video. Foreground and background colors are
switched.
`italic`: italic text.
@@ -452,6 +477,9 @@ is not active. New UIs should implement |ui-linegrid| instead.
`strikethrough`: struckthrough text.
`underline`: underlined text. The line has `special` color.
`undercurl`: undercurled text. The curl has `special` color.
+ `underdouble`: double underlined text. The lines have `special` color.
+ `underdotted`: underdotted text. The dots have `special` color.
+ `underdashed`: underdashed text. The dashes have `special` color.
["put", text]
The (utf-8 encoded) string `text` is put at the cursor position
@@ -598,6 +626,10 @@ tabs.
`botline` is set to one more than the line count of the buffer, if
there are filler lines past the end.
+["win_extmark", grid, win, ns_id, mark_id, row, col]
+ Updates the position of an extmark which is currently visible in a
+ window. Only emitted if the mark has the `ui_watched` attribute.
+
==============================================================================
Popupmenu Events *ui-popupmenu*
diff --git a/runtime/doc/undo.txt b/runtime/doc/undo.txt
index b11d7581ed..67f24103cd 100644
--- a/runtime/doc/undo.txt
+++ b/runtime/doc/undo.txt
@@ -22,6 +22,14 @@ u Undo [count] changes.
:u[ndo] {N} Jump to after change number {N}. See |undo-branches|
for the meaning of {N}.
+:u[ndo]! Undo one change and remove it from undo history.
+ *E5767*
+:u[ndo]! {N} Like ":u[ndo] {N}", but forget all changes in the
+ current undo branch up until {N}. You may only use
+ ":undo! {N}" to move backwards in the same undo
+ branch, not to redo or switch to a different undo
+ branch.
+
*CTRL-R*
CTRL-R Redo [count] changes which were undone.
@@ -100,12 +108,14 @@ change again. But you can do something like this: >
After this a "u" command will undo the delete command and the previous
change.
-
+ *undo-break*
To do the opposite, break a change into two undo blocks, in Insert mode use
CTRL-G u. This is useful if you want an insert command to be undoable in
parts. E.g., for each sentence. |i_CTRL-G_u|
+
Setting the value of 'undolevels' also breaks undo. Even when the new value
-is equal to the old value.
+is equal to the old value: >
+ let &undolevels = &undolevels
==============================================================================
4. Undo branches *undo-branches* *undo-tree*
@@ -272,12 +282,12 @@ history file. E.g.: >
au BufReadPost * call ReadUndo()
au BufWritePost * call WriteUndo()
func ReadUndo()
- if filereadable(expand('%:h'). '/UNDO/' . expand('%:t'))
+ if filereadable(expand('%:h') .. '/UNDO/' .. expand('%:t'))
rundo %:h/UNDO/%:t
endif
endfunc
func WriteUndo()
- let dirname = expand('%:h') . '/UNDO'
+ let dirname = expand('%:h') .. '/UNDO'
if !isdirectory(dirname)
call mkdir(dirname)
endif
diff --git a/runtime/doc/usr_04.txt b/runtime/doc/usr_04.txt
index b2dd617542..c7c900274b 100644
--- a/runtime/doc/usr_04.txt
+++ b/runtime/doc/usr_04.txt
@@ -349,15 +349,17 @@ Notice that "yw" includes the white space after a word. If you don't want
this, use "ye".
The "yy" command yanks a whole line, just like "dd" deletes a whole line.
-Unexpectedly, while "D" deletes from the cursor to the end of the line, "Y"
-works like "yy", it yanks the whole line. Watch out for this inconsistency!
-Use "y$" to yank to the end of the line.
a text line yy a text line a text line
line 2 line 2 p line 2
last line last line a text line
last line
+"Y" was originally equivalent to "yank the entire line", as opposed to "D"
+which is "delete to end of the line". "Y" has thus been remapped to mean
+"yank to end of the line" to make it consistent with the behavior of "D".
+Mappings will be covered in later chapters.
+
==============================================================================
*04.7* Using the clipboard
diff --git a/runtime/doc/usr_05.txt b/runtime/doc/usr_05.txt
index f93a221e43..0e94d9a1b1 100644
--- a/runtime/doc/usr_05.txt
+++ b/runtime/doc/usr_05.txt
@@ -45,7 +45,7 @@ This file is always used and is recommended:
The vimrc file can contain all the commands that you type after a colon. The
simplest ones are for setting options. For example, if you want Vim to always
-start with the 'incsearch' option on, add this line your vimrc file: >
+start with the 'ignorecase' option on, add this line your vimrc file: >
set ignorecase
@@ -131,7 +131,7 @@ it worked before Vim 5.0. Otherwise the "Q" command starts Ex mode, but you
will not need it.
>
- vnoremap _g y:exe "grep /" . escape(@", '\\/') . "/ *.c *.h"<CR>
+ vnoremap _g y:exe "grep /" .. escape(@", '\\/') .. "/ *.c *.h"<CR>
This mapping yanks the visually selected text and searches for it in C files.
This is a complicated mapping. You can see that mappings can be used to do
@@ -139,13 +139,11 @@ quite complicated things. Still, it is just a sequence of commands that are
executed like you typed them.
>
- syntax on
set hlsearch
-This switches on syntax highlighting. And the 'hlsearch' option tells Vim to
-highlight matches with the last used search pattern. The "if" command is very
-useful to set options only when some condition is met. More about that in
-|usr_41.txt|.
+This option tells Vim to highlight matches with the last used search pattern.
+The "if" command is very useful to set options only when some condition is
+met. More about that in |usr_41.txt|.
*vimrc-filetype* >
filetype plugin indent on
diff --git a/runtime/doc/usr_06.txt b/runtime/doc/usr_06.txt
index 360f72ec63..b99e0fb482 100644
--- a/runtime/doc/usr_06.txt
+++ b/runtime/doc/usr_06.txt
@@ -24,28 +24,8 @@ Table of contents: |usr_toc.txt|
==============================================================================
*06.1* Switching it on
-It all starts with one simple command: >
-
- :syntax enable
-
-That should work in most situations to get color in your files. Vim will
-automagically detect the type of file and load the right syntax highlighting.
-Suddenly comments are blue, keywords brown and strings red. This makes it
-easy to overview the file. After a while you will find that black&white text
-slows you down!
-
-If you always want to use syntax highlighting, put the ":syntax enable"
-command in your |init.vim| file.
-
-If you want syntax highlighting only when the terminal supports colors, you
-can put this in your |init.vim| file: >
-
- if &t_Co > 1
- syntax enable
- endif
-
-If you want syntax highlighting only in the GUI version, put the ":syntax
-enable" command in your |ginit.vim| file.
+Syntax highlighting is enabled by default. Nvim will automagically detect the
+type of file and load the right syntax highlighting.
==============================================================================
*06.2* No or wrong colors?
diff --git a/runtime/doc/usr_07.txt b/runtime/doc/usr_07.txt
index 649be8d7ce..ebf5c3d7b8 100644
--- a/runtime/doc/usr_07.txt
+++ b/runtime/doc/usr_07.txt
@@ -336,7 +336,7 @@ there. >
Of course you can use many other commands to yank the text. For example, to
select whole lines start Visual mode with "V". Or use CTRL-V to select a
-rectangular block. Or use "Y" to yank a single line, "yaw" to yank-a-word,
+rectangular block. Or use "yy" to yank a single line, "yaw" to yank-a-word,
etc.
The "p" command puts the text after the cursor. Use "P" to put the text
before the cursor. Notice that Vim remembers if you yanked a whole line or a
@@ -359,7 +359,7 @@ the text should be placed in the f register. This must come just before the
yank command.
Now yank three whole lines to the l register (l for line): >
- "l3Y
+ "l3yy
The count could be before the "l just as well. To yank a block of text to the
b (for block) register: >
diff --git a/runtime/doc/usr_08.txt b/runtime/doc/usr_08.txt
index 8ccaa73006..1d20913a14 100644
--- a/runtime/doc/usr_08.txt
+++ b/runtime/doc/usr_08.txt
@@ -482,6 +482,8 @@ statusline:
0 never
1 only when there are split windows (the default)
2 always
+ 3 have a global statusline at the bottom instead of one for each
+ window
Many commands that edit another file have a variant that splits the window.
For Command-line commands this is done by prepending an "s". For example:
diff --git a/runtime/doc/usr_10.txt b/runtime/doc/usr_10.txt
index 5365f90314..8844671e01 100644
--- a/runtime/doc/usr_10.txt
+++ b/runtime/doc/usr_10.txt
@@ -132,11 +132,11 @@ This works both with recording and with yank and delete commands. For
example, you want to collect a sequence of lines into the a register. Yank
the first line with: >
- "aY
+ "ayy
Now move to the second line, and type: >
- "AY
+ "Ayy
Repeat this command for all lines. The a register now contains all those
lines, in the order you yanked them.
diff --git a/runtime/doc/usr_21.txt b/runtime/doc/usr_21.txt
index 44d653a1a7..add5d48073 100644
--- a/runtime/doc/usr_21.txt
+++ b/runtime/doc/usr_21.txt
@@ -352,12 +352,12 @@ another session.
this yourself then. Example: >
:mksession! ~/.config/nvim/secret.vim
- :wshada! ~/.local/share/nvim/shada/secret.shada
+ :wshada! ~/.local/state/nvim/shada/secret.shada
And to restore this again: >
:source ~/.config/nvim/secret.vim
- :rshada! ~/.local/share/nvim/shada/secret.shada
+ :rshada! ~/.local/state/nvim/shada/secret.shada
==============================================================================
*21.5* Views
diff --git a/runtime/doc/usr_29.txt b/runtime/doc/usr_29.txt
index 3381d1870c..d8c556c281 100644
--- a/runtime/doc/usr_29.txt
+++ b/runtime/doc/usr_29.txt
@@ -33,10 +33,12 @@ following command: >
ctags *.c
"ctags" is a separate program. Most Unix systems already have it installed.
-If you do not have it yet, you can find Exuberant ctags here:
-
+If you do not have it yet, you can find Universal/Exuberant ctags at:
+ http://ctags.io ~
http://ctags.sf.net ~
+Universal ctags is preferred, Exuberant ctags is no longer being developed.
+
Now when you are in Vim and you want to go to a function definition, you can
jump to it by using the following command: >
@@ -142,15 +144,15 @@ ONE TAGS FILE
When Vim has to search many places for tags files, you can hear the disk
rattling. It may get a bit slow. In that case it's better to spend this
time while generating one big tags file. You might do this overnight.
- This requires the Exuberant ctags program, mentioned above. It offers an
-argument to search a whole directory tree: >
+ This requires the Universal or Exuberant ctags program, mentioned above.
+It offers an argument to search a whole directory tree: >
cd ~/proj
ctags -R .
-The nice thing about this is that Exuberant ctags recognizes various file
-types. Thus this doesn't work just for C and C++ programs, also for Eiffel
-and even Vim scripts. See the ctags documentation to tune this.
+The nice thing about this is that Universal/Exuberant ctags recognizes various
+file types. Thus this doesn't work just for C and C++ programs, also for
+Eiffel and even Vim scripts. See the ctags documentation to tune this.
Now you only need to tell Vim where your big tags file is: >
:set tags=~/proj/tags
@@ -232,7 +234,8 @@ A TAGS BROWSER
Since CTRL-] takes you to the definition of the identifier under the cursor,
you can use a list of identifier names as a table of contents. Here is an
example.
- First create a list of identifiers (this requires Exuberant ctags): >
+ First create a list of identifiers (this requires Universal or Exuberant
+ctags): >
ctags --c-types=f -f functions *.c
diff --git a/runtime/doc/usr_40.txt b/runtime/doc/usr_40.txt
index 5b1254e2ae..f47c933124 100644
--- a/runtime/doc/usr_40.txt
+++ b/runtime/doc/usr_40.txt
@@ -463,6 +463,17 @@ separated) that trigger the command.
The optional [++nested] flag allows for nesting of autocommands (see below),
and finally, {command} is the command to be executed.
+When adding an autocommand the already existing ones remain. To avoid adding
+the autocommand several times you should use this form: >
+
+ :augroup updateDate
+ : autocmd!
+ : autocmd BufWritePre * call DateInsert()
+ :augroup END
+
+This will delete any previously defined autocommand with `:autocmd!` before
+defining the new one. Groups are explained later.
+
EVENTS
diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt
index 7e611a47f3..5acef2f352 100644
--- a/runtime/doc/usr_41.txt
+++ b/runtime/doc/usr_41.txt
@@ -157,7 +157,7 @@ cannot start with a digit. Valid variable names are:
FuncLength
LENGTH
-Invalid names are "foo+bar" and "6var".
+Invalid names are "foo.bar" and "6var".
These variables are global. To see a list of currently defined variables
use this command: >
@@ -549,7 +549,7 @@ A "&" character is prepended to "path", thus the argument to eval() is
Vim defines many functions and provides a large amount of functionality that
way. A few examples will be given in this section. You can find the whole
-list here: |functions|.
+list below: |function-list|.
A function is called with the ":call" command. The parameters are passed in
between parentheses separated by commas. Example: >
@@ -581,15 +581,15 @@ statements is equal to: >
:substitute/\a/*/g
-Using the functions becomes more interesting when you do more work before and
+Using the functions becomes interesting when you do more work before and
after the substitute() call.
FUNCTIONS *function-list*
There are many functions. We will mention them here, grouped by what they are
-used for. You can find an alphabetical list here: |functions|. Use CTRL-] on
-the function name to jump to detailed help on it.
+used for. You can find an alphabetical list here: |builtin-function-list|.
+Use CTRL-] on the function name to jump to detailed help on it.
String manipulation: *string-functions*
nr2char() get a character by its number value
@@ -608,6 +608,8 @@ String manipulation: *string-functions*
toupper() turn a string to uppercase
match() position where a pattern matches in a string
matchend() position where a pattern match ends in a string
+ matchfuzzy() fuzzy matches a string in a list of strings
+ matchfuzzypos() fuzzy matches a string in a list of strings
matchstr() match of a pattern in a string
matchstrpos() match and positions of a pattern in a string
matchlist() like matchstr() and also return submatches
@@ -746,6 +748,11 @@ Cursor and mark position: *cursor-functions* *mark-functions*
screenchar() get character code at a screen line/row
screenchars() get character codes at a screen line/row
screenstring() get string of characters at a screen line/row
+ charcol() character number of the cursor or a mark
+ getcharpos() get character position of cursor, mark, etc.
+ setcharpos() set character position of cursor, mark, etc.
+ getcursorcharpos() get character position of the cursor
+ setcursorcharpos() set character position of the cursor
Working with text in the current buffer: *text-functions*
getline() get a line or list of lines from the buffer
@@ -837,6 +844,8 @@ Buffers, windows and the argument list:
win_gotoid() go to window with ID
win_id2tabwin() get tab and window nr from window ID
win_id2win() get window nr from window ID
+ win_move_separator() move window vertical separator
+ win_move_statusline() move window status line
getbufinfo() get a list with buffer information
gettabinfo() get a list with tab page information
getwininfo() get a list with window information
@@ -846,8 +855,12 @@ Buffers, windows and the argument list:
swapname() get the swap file path of a buffer
Command line: *command-line-functions*
+ getcmdcompltype() get the type of the current command line
+ completion
getcmdline() get the current command line
getcmdpos() get position of the cursor in the command line
+ getcmdscreenpos() get screen position of the cursor in the
+ command line
setcmdpos() set position of the cursor in the command line
getcmdtype() return the current command-line type
getcmdwintype() return the current command-line window type
@@ -951,6 +964,10 @@ Window size and position: *window-size-functions*
winrestview() restore saved view of current window
Mappings: *mapping-functions*
+ digraph_get() get |digraph|
+ digraph_getlist() get all |digraph|s
+ digraph_set() register |digraph|
+ digraph_setlist() register multiple |digraph|s
hasmapto() check if a mapping exists
mapcheck() check if a matching mapping exists
maparg() get rhs of a mapping
@@ -1579,7 +1596,7 @@ the same (the text may change, e.g., it may be translated).
When the ":read" command causes another error, the pattern "E484:" will not
match in it. Thus this exception will not be caught and result in the usual
-error message.
+error message and execution is aborted.
You might be tempted to do this: >
@@ -1619,27 +1636,29 @@ manual: |exception-handling|.
Here is a summary of items that apply to Vim scripts. They are also mentioned
elsewhere, but form a nice checklist.
-The end-of-line character depends on the system. For Unix a single <NL>
-character is used. For Windows <CR><NL> is used. This is important when
-using mappings that end in a <CR>. See |:source_crnl|.
+The end-of-line character depends on the system. For Vim scripts it is
+recommended to always use the Unix fileformat. Lines are then separated with
+the Newline character. This also works on any other system. That way you can
+copy your Vim scripts from MS-Windows to Unix and they still work. See
+|:source_crnl|. To be sure it is set right, do this before writing the file:
+ >
+ :setlocal fileformat=unix
+When using "dos" fileformat, lines are separated with CR-NL, two characters.
+The CR character causes various problems, better avoid this.
-WHITE SPACE
-
-Blank lines are allowed and ignored.
-Leading whitespace characters (blanks and TABs) are always ignored. The
-whitespaces between parameters (e.g. between the "set" and the "cpoptions" in
-the example below) are reduced to one blank character and plays the role of a
-separator, the whitespaces after the last (visible) character may or may not
-be ignored depending on the situation, see below.
+WHITE SPACE
-For a ":set" command involving the "=" (equal) sign, such as in: >
+Blank lines are allowed in a script and ignored.
- :set cpoptions =aABceFst
+Leading whitespace characters (blanks and TABs) are ignored, except when using
+|:let-heredoc| without "trim".
-the whitespace immediately before the "=" sign is ignored. But there can be
-no whitespace after the "=" sign!
+Trailing whitespace is often ignored, but not always. One command that
+includes it is `map`. You have to watch out for that, it can cause hard to
+understand mistakes. A generic solution is to never use trailing white space,
+unless you really need it.
To include a whitespace character in the value of an option, it must be
escaped by a "\" (backslash) as in the following example: >
diff --git a/runtime/doc/usr_42.txt b/runtime/doc/usr_42.txt
index ff3ae7057a..470f4e0fe5 100644
--- a/runtime/doc/usr_42.txt
+++ b/runtime/doc/usr_42.txt
@@ -150,7 +150,8 @@ like the variations on the ":map" command:
:menu! Insert and Command-line mode
:imenu Insert mode
:cmenu Command-line mode
- :amenu All modes
+ :tlmenu Terminal mode
+ :amenu All modes (except for Terminal mode)
To avoid that the commands of a menu item are being mapped, use the command
":noremenu", ":nnoremenu", ":anoremenu", etc.
diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt
index 05bffc3038..9eb6470962 100644
--- a/runtime/doc/various.txt
+++ b/runtime/doc/various.txt
@@ -30,10 +30,10 @@ CTRL-L Clears and redraws the screen. The redraw may happen
function (or a mapping if 'lazyredraw' set).
*:redraws* *:redrawstatus*
-:redraws[tatus][!] Redraws the status line of the current window, or all
- status lines if "!" is included.
- Useful if 'statusline' includes an item that doesn't
- cause automatic updating.
+:redraws[tatus][!] Redraws the status line and window bar of the current
+ window, or all status lines and window bars if "!" is
+ included. Useful if 'statusline' or 'winbar' includes
+ an item that doesn't cause automatic updating.
*:redrawt* *:redrawtabline*
:redrawt[abline] Redraw the tabline. Useful to update the tabline when
@@ -208,8 +208,6 @@ g8 Print the hex values of the bytes used in the
{commands} cannot start with a space. Put a count of
1 (one) before it, "1 " is one space.
- The 'insertmode' option is ignored for {commands}.
-
This command cannot be followed by another command,
since any '|' is considered part of the command.
@@ -275,7 +273,7 @@ g8 Print the hex values of the bytes used in the
Special characters are not escaped, use quotes or
|shellescape()|: >
:!ls "%"
- :exe "!ls " . shellescape(expand("%"))
+ :exe "!ls " .. shellescape(expand("%"))
<
Newline character ends {cmd} unless a backslash
precedes the newline. What follows is interpreted as
@@ -348,7 +346,11 @@ g8 Print the hex values of the bytes used in the
Only string variables can be used. After the
redirection starts, if the variable is removed or
locked or the variable type is changed, then further
- command output messages will cause errors.
+ command output messages will cause errors. When using
+ a local variable (l:var in a function or s:var in a
+ script) and another `:redir` causes the current one to
+ end, the scope might be different and the assignment
+ fails.
To get the output of one command the |execute()|
function can be used instead of redirection.
@@ -358,19 +360,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
@@ -388,6 +390,8 @@ g8 Print the hex values of the bytes used in the
|:marks| - filter by text in the current file,
or file name for other files
|:oldfiles| - filter by file name
+ |:registers| - filter by register contents
+ (does not work multi-line)
|:set| - filter by option name
Only normal messages are filtered, error messages are
@@ -430,7 +434,7 @@ g8 Print the hex values of the bytes used in the
used. In this example |:silent| is used to avoid the
message about reading the file and |:unsilent| to be
able to list the first line of each file. >
- :silent argdo unsilent echo expand('%') . ": " . getline(1)
+ :silent argdo unsilent echo expand('%') .. ": " .. getline(1)
<
*:verb* *:verbose*
@@ -459,10 +463,11 @@ g8 Print the hex values of the bytes used in the
*:verbose-cmd*
When 'verbose' is non-zero, listing the value of a Vim option or a key map or
an abbreviation or a user-defined function or a command or a highlight group
-or an autocommand will also display where it was last defined. If it was
-defined manually then there will be no "Last set" message. When it was
-defined while executing a function, user command or autocommand, the script in
-which it was defined is reported.
+or an autocommand will also display where it was last defined. If they were
+defined in Lua they will only be located if 'verbose' is set. So Start
+nvim with -V1 arg to see them. If it was defined manually then there
+will be no "Last set" message. When it was defined while executing a function,
+user command or autocommand, the script in which it was defined is reported.
*K*
[count]K Runs the program given by 'keywordprg' to lookup the
diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt
index 4fcaf15717..a74149d050 100644
--- a/runtime/doc/vim_diff.txt
+++ b/runtime/doc/vim_diff.txt
@@ -17,25 +17,27 @@ centralized reference of the differences.
- Use `$XDG_CONFIG_HOME/nvim/init.vim` instead of `.vimrc` for your |config|.
- Use `$XDG_CONFIG_HOME/nvim` instead of `.vim` to store configuration files.
-- Use `$XDG_DATA_HOME/nvim/shada/main.shada` instead of `.viminfo` for persistent
+- Use `$XDG_STATE_HOME/nvim/shada/main.shada` instead of `.viminfo` for persistent
session information. |shada|
==============================================================================
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
- 'background' defaults to "dark" (unless set automatically by the terminal/UI)
- 'backspace' defaults to "indent,eol,start"
-- 'backupdir' defaults to .,~/.local/share/nvim/backup// (|xdg|), auto-created
+- 'backupdir' defaults to .,~/.local/state/nvim/backup// (|xdg|), auto-created
- 'belloff' defaults to "all"
- 'compatible' is always disabled
- 'complete' excludes "i"
- 'cscopeverbose' is enabled
-- 'directory' defaults to ~/.local/share/nvim/swap// (|xdg|), auto-created
+- 'directory' defaults to ~/.local/state/nvim/swap// (|xdg|), auto-created
- 'display' defaults to "lastline,msgsep"
- 'encoding' is UTF-8 (cf. 'fileencoding' for file-content encoding)
- 'fillchars' defaults (in effect) to "vert:│,fold:·,sep:│"
@@ -50,6 +52,8 @@ centralized reference of the differences.
- 'langremap' is disabled
- 'laststatus' defaults to 2 (statusline is always shown)
- 'listchars' defaults to "tab:> ,trail:-,nbsp:+"
+- 'mouse' defaults to "nvi"
+- 'mousemodel' defaults to "popup_setpos"
- 'nrformats' defaults to "bin,hex"
- 'ruler' is enabled
- 'sessionoptions' includes "unix,slash", excludes "options"
@@ -63,7 +67,7 @@ centralized reference of the differences.
- 'tags' defaults to "./tags;,tags"
- 'ttimeoutlen' defaults to 50
- 'ttyfast' is always set
-- 'undodir' defaults to ~/.local/share/nvim/undo// (|xdg|), auto-created
+- 'undodir' defaults to ~/.local/state/nvim/undo// (|xdg|), auto-created
- 'viewoptions' includes "unix,slash", excludes "options"
- 'viminfo' includes "!"
- 'wildmenu' is enabled
@@ -76,15 +80,42 @@ centralized reference of the differences.
- |g:vimsyn_embed| defaults to "l" to enable Lua highlighting
+Default Mouse ~
+ *default-mouse* *disable-mouse*
+By default the mouse is enabled. The right button click opens |popup-menu|
+with standard actions, such as "Cut", "Copy" and "Paste".
+
+If you don't like this you can add to your |config| any of the following:
+
+- ignore mouse completely >
+ set mouse=
+<
+- no |popup-menu| but the right button extends selection >
+ set mousemodel=extend
+>
+- pressing ALT+LeftMouse releases mouse until main cursor moves >
+ nnoremap <M-LeftMouse> <Cmd>
+ \ set mouse=<Bar>
+ \ echo 'mouse OFF until next cursor-move'<Bar>
+ \ autocmd CursorMoved * ++once set mouse&<Bar>
+ \ echo 'mouse ON'<CR>
+<
+Also, mouse is not in use in |command-mode| or at |more-prompt|. So if you
+need to copy/paste with your terminal then just pressing ':' makes Nvim to
+release the mouse cursor temporarily.
+
Default Mappings ~
*default-mappings*
Nvim creates the following default mappings at |startup|. You can disable any
of these in your config by simply removing the mapping, e.g. ":unmap Y".
>
nnoremap Y y$
- nnoremap <C-L> <Cmd>nohlsearch<Bar>diffupdate<CR><C-L>
+ nnoremap <C-L> <Cmd>nohlsearch<Bar>diffupdate<Bar>normal! <C-L><CR>
inoremap <C-U> <C-G>u<C-U>
inoremap <C-W> <C-G>u<C-W>
+ xnoremap * y/\V<C-R>"<CR>
+ xnoremap # y?\V<C-R>"<CR>
+ nnoremap & :&&<CR>
<
Default Autocommands ~
*default-autocmds*
@@ -178,6 +209,8 @@ Commands:
|:Man| is available by default, with many improvements such as completion
|:sign-define| accepts a `numhl` argument, to highlight the line number
|:match| can be invoked before highlight group is defined
+ |:source| works with Lua and anonymous (no file) scripts
+ User commands can support |:command-preview| to show results as you type
Events:
|RecordingEnter|
@@ -209,6 +242,7 @@ Highlight groups:
|hl-Substitute|
|hl-TermCursor|
|hl-TermCursorNC|
+ |hl-WinSeparator| highlights window separators
|hl-Whitespace| highlights 'listchars' whitespace
Input/Mappings:
@@ -225,9 +259,13 @@ Options:
'cpoptions' flags: |cpo-_|
'display' flags: "msgsep" minimizes scrolling when showing messages
'guicursor' works in the terminal
- 'fillchars' flags: "msgsep" (see 'display')
+ 'fillchars' flags: "msgsep" (see 'display'), "horiz", "horizup",
+ "horizdown", "vertleft", "vertright", "verthoriz"
'foldcolumn' supports up to 9 dynamic/fixed columns
'inccommand' shows interactive results for |:substitute|-like commands
+ and |:command-preview| commands
+ 'laststatus' global statusline support
+ 'mousescroll' amount to scroll by when scrolling with a mouse
'pumblend' pseudo-transparent popupmenu
'scrollback'
'signcolumn' supports up to 9 dynamic/fixed columns
@@ -320,6 +358,8 @@ coerced to strings. See |id()| for more details, currently it uses
|c_CTRL-R| pasting a non-special register into |cmdline| omits the last <CR>.
+|CursorMoved| always triggers when moving between windows.
+
Lua interface (|lua.txt|):
- `:lua print("a\0b")` will print `a^@b`, like with `:echomsg "a\nb"` . In Vim
@@ -337,13 +377,17 @@ Commands:
Functions:
|input()| and |inputdialog()| support for each other’s features (return on
cancel and completion respectively) via dictionary argument (replaces all
- other arguments if used).
+ other arguments if used), and "cancelreturn" can have any type if passed in
+ a dictionary.
|input()| and |inputdialog()| support user-defined cmdline highlighting.
Highlight groups:
|hl-ColorColumn|, |hl-CursorColumn| are lower priority than most other
groups
+ |hl-CurSearch| highlights match under cursor instead of last match found
+ using |n| or |N|
|hl-CursorLine| is low-priority unless foreground color is set
+ |hl-VertSplit| superseded by |hl-WinSeparator|
Macro/|recording| behavior
Replay of a macro recorded during :lmap produces the same actions as when it
@@ -354,6 +398,10 @@ Macro/|recording| behavior
macros and 'keymap' at the same time. This also means you can use |:imap| on
the results of keys from 'keymap'.
+Mappings:
+ Creating a mapping for a simplifiable key (e.g. <C-I>) doesn't replace an
+ existing mapping for its simplified form (e.g. <Tab>).
+
Motion:
The |jumplist| avoids useless/phantom jumps.
@@ -364,6 +412,9 @@ Normal commands:
Options:
'ttimeout', 'ttimeoutlen' behavior was simplified
|jumpoptions| "stack" behavior
+ |jumpoptions| "view" tries to restore the |mark-view| when moving through
+ the |jumplist|, |changelist|, |alternate-file| or using |mark-motions|.
+ 'shortmess' the "F" flag does not affect output from autocommands
Shell:
Shell output (|:!|, |:make|, …) is always routed through the UI, so it
@@ -429,7 +480,8 @@ Vimscript compatibility:
`this_session` does not alias to |v:this_session|
Working directory (Vim implemented some of these later than Nvim):
-- |DirChanged| can be triggered when switching to another window.
+- |DirChanged| and |DirChangedPre| can be triggered when switching to another
+ window or tab.
- |getcwd()| and |haslocaldir()| may throw errors if the tab page or window
cannot be found. *E5000* *E5001* *E5002*
- |haslocaldir()| checks for tab-local directory if and only if -1 is passed as
@@ -481,7 +533,6 @@ Commands:
:tearoff
Compile-time features:
- EBCDIC
Emacs tags support
X11 integration (see |x11-selection|)
@@ -490,6 +541,9 @@ Eval:
*js_encode()*
*js_decode()*
*v:none* (used by Vim to represent JavaScript "undefined"); use |v:null| instead.
+ *v:sizeofint*
+ *v:sizeoflong*
+ *v:sizeofpointer*
Events:
*SigUSR1* Use |Signal| to detect `SIGUSR1` signal instead.
@@ -507,6 +561,9 @@ Highlight groups:
Options:
'antialias'
+ *'balloondelay'* *'bdlay'*
+ *'ballooneval'* *'beval'* *'noballooneval'* *'nobeval'*
+ *'balloonexpr'* *'bexpr'*
'bioskey' (MS-DOS)
'conskey' (MS-DOS)
*'cp'* *'nocompatible'* *'nocp'* *'compatible'* (Nvim is always "nocompatible".)
@@ -522,25 +579,58 @@ Options:
*'imactivatefunc'* *'imaf'*
*'imactivatekey'* *'imak'*
*'imstatusfunc'* *'imsf'*
+ *'insertmode'* *'im'* Use the following script to emulate 'insertmode':
+>
+ autocmd BufWinEnter * startinsert
+ inoremap <Esc> <C-X><C-Z><C-]>
+ inoremap <C-C> <C-X><C-Z>
+ inoremap <C-L> <C-X><C-Z><C-]><Esc>
+ inoremap <C-Z> <C-X><C-Z><Cmd>suspend<CR>
+ noremap <C-C> <Esc>
+ snoremap <C-C> <Esc>
+ noremap <C-\><C-G> <C-\><C-N><Cmd>startinsert<CR>
+ cnoremap <C-\><C-G> <C-\><C-N><Cmd>startinsert<CR>
+ inoremap <C-\><C-G> <C-X><C-Z>
+ autocmd CmdWinEnter * noremap <buffer> <C-C> <C-C>
+ autocmd CmdWinEnter * inoremap <buffer> <C-C> <C-C>
+
+ lua << EOF
+ vim.on_key(function(c)
+ if c == '\27' then
+ local mode = vim.api.nvim_get_mode().mode
+ if mode:find('^[nvV\22sS\19]') and vim.fn.getcmdtype() == '' then
+ vim.schedule(function()
+ vim.cmd('startinsert')
+ end)
+ end
+ end
+ end)
+ EOF
+<
*'macatsui'*
+ *'maxcombine'* *'mco'*
+ Nvim always displays up to 6 combining characters. You can still edit
+ text with more than 6 combining characters, you just can't see them.
+ Use |g8| or |ga|. See |mbyte-combining|.
'maxmem' Nvim delegates memory-management to the OS.
'maxmemtot' Nvim delegates memory-management to the OS.
- 'maxcombine' (6 is always used)
*'prompt'* *'noprompt'*
+ *'remap'* *'noremap'*
*'restorescreen'* *'rs'* *'norestorescreen'* *'nors'*
'shelltype'
*'shortname'* *'sn'* *'noshortname'* *'nosn'*
*'swapsync'* *'sws'*
*'termencoding'* *'tenc'* (Vim 7.4.852 also removed this for Windows)
+ *'terse'* *'noterse'* (Add "s" to 'shortmess' instead)
'textauto'
'textmode'
*'toolbar'* *'tb'*
*'toolbariconsize'* *'tbis'*
*'ttybuiltin'* *'tbi'* *'nottybuiltin'* *'notbi'*
+ *'ttyfast'* *'tf'* *'nottyfast'* *'notf'*
*'ttymouse'* *'ttym'*
*'ttyscroll'* *'tsl'*
*'ttytype'* *'tty'*
- 'ttyfast'
'weirdinvert'
Startup:
@@ -572,6 +662,7 @@ Test functions:
test_scrollbar()
test_setmouse()
test_settime()
+ test_srand_seed()
TUI:
*t_xx* *termcap-options* *t_AB* *t_Sb* *t_vb* *t_SI*
diff --git a/runtime/doc/visual.txt b/runtime/doc/visual.txt
index 4a69fc989b..905ae49887 100644
--- a/runtime/doc/visual.txt
+++ b/runtime/doc/visual.txt
@@ -255,6 +255,7 @@ Additionally the following commands can be used:
X delete (2) |v_X|
Y yank (2) |v_Y|
p put |v_p|
+ P put without overwriting registers |v_P|
J join (1) |v_J|
U make uppercase |v_U|
u make lowercase |v_u|
@@ -478,6 +479,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.
@@ -494,6 +499,13 @@ work both in Visual mode and in Select mode. When these are used in Select
mode Vim automatically switches to Visual mode, so that the same behavior as
in Visual mode is effective. If you don't want this use |:xmap| or |:smap|.
+One particular edge case: >
+ :vnoremap <C-K> <Esc>
+This ends Visual mode when in Visual mode, but in Select mode it does not
+work, because Select mode is restored after executing the mapped keys. You
+need to use: >
+ :snoremap <C-K> <Esc>
+<
Users will expect printable characters to replace the selected area.
Therefore avoid mapping printable characters in Select mode. Or use
|:sunmap| after |:map| and |:vmap| to remove it for Select mode.
diff --git a/runtime/doc/windows.txt b/runtime/doc/windows.txt
index 5b91321c40..8062b9e28f 100644
--- a/runtime/doc/windows.txt
+++ b/runtime/doc/windows.txt
@@ -104,6 +104,8 @@ when the last window also has a status line:
'laststatus' = 0 never a status line
'laststatus' = 1 status line if there is more than one window
'laststatus' = 2 always a status line
+ 'laststatus' = 3 have a global statusline at the bottom instead
+ of one for each window
You can change the contents of the status line with the 'statusline' option.
This option can be local to the window, so that you can have a different
@@ -447,7 +449,7 @@ These commands can also be executed with ":wincmd":
the |CursorHold| autocommand event). Or when a Normal mode
command is inconvenient.
The count can also be a window number. Example: >
- :exe nr . "wincmd w"
+ :exe nr .. "wincmd w"
< This goes to window "nr".
==============================================================================
@@ -909,12 +911,12 @@ CTRL-W g } *CTRL-W_g}*
cursor. This is less clever than using |:ptag|, but you don't
need a tags file and it will also find matches in system
include files. Example: >
- :au! CursorHold *.[ch] ++nested exe "silent! psearch " . expand("<cword>")
+ :au! CursorHold *.[ch] ++nested exe "silent! psearch " .. expand("<cword>")
< Warning: This can be slow.
Example *CursorHold-example* >
- :au! CursorHold *.[ch] ++nested exe "silent! ptag " . expand("<cword>")
+ :au! CursorHold *.[ch] ++nested exe "silent! ptag " .. expand("<cword>")
This will cause a ":ptag" to be executed for the keyword under the cursor,
when the cursor hasn't moved for the time set with 'updatetime'. "++nested"
@@ -937,14 +939,14 @@ is no word under the cursor, and a few other things: >
:
: " Delete any existing highlight before showing another tag
: silent! wincmd P " jump to preview window
- : if &previewwindow " if we really get there...
+ : if &previewwindow " if we really get there...
: match none " delete existing highlight
: wincmd p " back to old window
: endif
:
: " Try displaying a matching tag for the word under the cursor
: try
- : exe "ptag " . w
+ : exe "ptag " .. w
: catch
: return
: endtry
@@ -956,10 +958,10 @@ is no word under the cursor, and a few other things: >
: endif
: call search("$", "b") " to end of previous line
: let w = substitute(w, '\\', '\\\\', "")
- : call search('\<\V' . w . '\>') " position cursor on match
+ : call search('\<\V' .. w .. '\>') " position cursor on match
: " Add a match highlight to the word at this position
: hi previewWord term=bold ctermbg=green guibg=green
- : exe 'match previewWord "\%' . line(".") . 'l\%' . col(".") . 'c\k*"'
+ : exe 'match previewWord "\%' .. line(".") .. 'l\%' .. col(".") .. 'c\k*"'
: wincmd p " back to old window
: endif
: endif
@@ -981,7 +983,7 @@ modified, but is forced (with '!') to be removed from a window, and
'autowrite' is off or the buffer can't be written.
You can make a hidden buffer not hidden by starting to edit it with any
-command. Or by deleting it with the ":bdelete" command.
+command, or by deleting it with the ":bdelete" command.
The 'hidden' is global, it is used for all buffers. The 'bufhidden' option
can be used to make an exception for a specific buffer. It can take these
diff --git a/runtime/filetype.lua b/runtime/filetype.lua
index fcfc5701f0..9f5b5fd0dc 100644
--- a/runtime/filetype.lua
+++ b/runtime/filetype.lua
@@ -1,26 +1,51 @@
-if vim.g.did_load_filetypes and vim.g.did_load_filetypes ~= 0 then
+-- Skip if legacy filetype is enabled or filetype detection is disabled
+if vim.g.do_legacy_filetype or vim.g.did_load_filetypes then
return
end
+vim.g.did_load_filetypes = 1
--- For now, make this opt-in with a global variable
-if vim.g.do_filetype_lua ~= 1 then
- return
-end
+vim.api.nvim_create_augroup('filetypedetect', { clear = false })
-vim.cmd [[
-augroup filetypedetect
-au BufRead,BufNewFile * call v:lua.vim.filetype.match(expand('<afile>'))
+vim.api.nvim_create_autocmd({ 'BufRead', 'BufNewFile' }, {
+ group = 'filetypedetect',
+ callback = function(args)
+ local ft, on_detect = vim.filetype.match({ filename = args.match, buf = args.buf })
+ if not ft then
+ -- Generic configuration file used as fallback
+ ft = require('vim.filetype.detect').conf(args.file, args.buf)
+ if ft then
+ vim.api.nvim_cmd({ cmd = 'setf', args = { 'FALLBACK', ft } }, {})
+ end
+ else
+ vim.api.nvim_buf_set_option(args.buf, 'filetype', ft)
+ if on_detect then
+ on_detect(args.buf)
+ end
+ end
+ end,
+})
-" These *must* be sourced after the autocommand above is created
-runtime! ftdetect/*.vim
-runtime! ftdetect/*.lua
+-- These *must* be sourced after the autocommand above is created
+if not vim.g.did_load_ftdetect then
+ vim.cmd([[
+ augroup filetypedetect
+ runtime! ftdetect/*.vim
+ runtime! ftdetect/*.lua
+ augroup END
+ ]])
+end
-" Set a marker so that the ftdetect scripts are not sourced a second time by filetype.vim
-let g:did_load_ftdetect = 1
+-- Set up the autocmd for user scripts.vim
+vim.api.nvim_create_autocmd({ 'BufRead', 'BufNewFile' }, {
+ group = 'filetypedetect',
+ command = "if !did_filetype() && expand('<amatch>') !~ g:ft_ignore_pat | runtime! scripts.vim | endif",
+})
-augroup END
-]]
+vim.api.nvim_create_autocmd('StdinReadPost', {
+ group = 'filetypedetect',
+ command = 'if !did_filetype() | runtime! scripts.vim | endif',
+})
if not vim.g.ft_ignore_pat then
- vim.g.ft_ignore_pat = "\\.\\(Z\\|gz\\|bz2\\|zip\\|tgz\\)$"
+ vim.g.ft_ignore_pat = '\\.\\(Z\\|gz\\|bz2\\|zip\\|tgz\\)$'
end
diff --git a/runtime/filetype.vim b/runtime/filetype.vim
index aa12a5f8dd..f28118e272 100644
--- a/runtime/filetype.vim
+++ b/runtime/filetype.vim
@@ -1,7 +1,12 @@
" Vim support file to detect file types
"
" Maintainer: Bram Moolenaar <Bram@vim.org>
-" Last Change: 2022 Jan 05
+" Last Change: 2022 Jul 5
+
+" Only run this if enabled
+if !exists("do_legacy_filetype")
+ finish
+endif
" Listen very carefully, I will say this only once
if exists("did_load_filetypes")
@@ -44,7 +49,7 @@ endif
" file name matches ft_ignore_pat.
" When using this, the entry should probably be further down below with the
" other StarSetf() calls.
-func! s:StarSetf(ft)
+func s:StarSetf(ft)
if expand("<amatch>") !~ g:ft_ignore_pat
exe 'setf ' . a:ft
endif
@@ -151,14 +156,21 @@ au BufNewFile,BufRead *.asp
\ setf aspvbs |
\ endif
-" Grub (must be before catch *.lst)
+" Grub (must be before pattern *.lst)
au BufNewFile,BufRead */boot/grub/menu.lst,*/boot/grub/grub.conf,*/etc/grub.conf setf grub
+" Maxima, see:
+" https://maxima.sourceforge.io/docs/manual/maxima_71.html#file_005ftype_005fmaxima
+" Must be before the pattern *.mac.
+" *.dem omitted - also used by gnuplot demos
+" *.mc omitted - used by dist#ft#McSetf()
+au BufNewFile,BufRead *.demo,*.dm{1,2,3,t},*.wxm,maxima-init.mac setf maxima
+
" Assembly (all kinds)
" *.lst is not pure assembly, it has two extra columns (address, byte codes)
au BufNewFile,BufRead *.asm,*.[sS],*.[aA],*.mac,*.lst call dist#ft#FTasm()
-" Macro (VAX)
+" Assembly - Macro (VAX)
au BufNewFile,BufRead *.mar setf vmasm
" Atlas
@@ -189,7 +201,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,13 +211,15 @@ 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
+" Batch file for MSDOS. See dist#ft#FTsys for *.sys
+au BufNewFile,BufRead *.bat setf dosbatch
" *.cmd is close to a Batch file, but on OS/2 Rexx files also use *.cmd.
au BufNewFile,BufRead *.cmd
\ if getline(1) =~ '^/\*' | setf rexx | else | setf dosbatch | endif
+" ABB RAPID or Batch file for MSDOS.
+au BufNewFile,BufRead *.sys\c call dist#ft#FTsys()
" Batch file for 4DOS
au BufNewFile,BufRead *.btm call dist#ft#FTbtm()
@@ -224,6 +239,9 @@ au BufNewFile,BufRead *.bib setf bib
" BibTeX Bibliography Style
au BufNewFile,BufRead *.bst setf bst
+" Bicep
+au BufNewFile,BufRead *.bicep setf bicep
+
" BIND configuration
" sudoedit uses namedXXXX.conf
au BufNewFile,BufRead named*.conf,rndc*.conf,rndc*.key setf named
@@ -235,6 +253,9 @@ au BufNewFile,BufRead *.db call dist#ft#BindzoneCheck('')
" Blank
au BufNewFile,BufRead *.bl setf blank
+" Bitbake
+au BufNewFile,BufRead *.bb,*.bbappend,*.bbclass,*/build/conf/*.conf,*/meta{-*,}/conf/*.conf setf bitbake
+
" Blkid cache file
au BufNewFile,BufRead */etc/blkid.tab,*/etc/blkid.tab.old setf xml
@@ -352,13 +373,8 @@ au BufNewFile,BufRead *.eni setf cl
" Clever or dtd
au BufNewFile,BufRead *.ent call dist#ft#FTent()
-" Clipper (or FoxPro; could also be eviews)
-au BufNewFile,BufRead *.prg
- \ if exists("g:filetype_prg") |
- \ exe "setf " . g:filetype_prg |
- \ else |
- \ setf clipper |
- \ endif
+" Clipper, FoxPro, ABB RAPID or eviews
+au BufNewFile,BufRead *.prg\c call dist#ft#FTprg()
" Clojure
au BufNewFile,BufRead *.clj,*.cljs,*.cljx,*.cljc setf clojure
@@ -389,12 +405,18 @@ au BufNewFile,BufRead *.cfm,*.cfi,*.cfc setf cf
" Configure scripts
au BufNewFile,BufRead configure.in,configure.ac setf config
+" Cooklang
+au BufNewFile,BufRead *.cook setf cook
+
+" CSV Files
+au BufNewFile,BufRead *.csv setf csv
+
" CUDA Compute Unified Device Architecture
au BufNewFile,BufRead *.cu,*.cuh setf cuda
" Dockerfile; Podman uses the same syntax with name Containerfile
" Also see Dockerfile.* below.
-au BufNewFile,BufRead Containerfile,Dockerfile,*.Dockerfile setf dockerfile
+au BufNewFile,BufRead Containerfile,Dockerfile,dockerfile,*.[dD]ockerfile setf dockerfile
" WildPackets EtherPeek Decoder
au BufNewFile,BufRead *.dcd setf dcd
@@ -412,6 +434,9 @@ au BufNewFile,BufRead *.ex call dist#ft#ExCheck()
au BufRead,BufNewFile mix.lock,*.exs setf elixir
au BufRead,BufNewFile *.eex,*.leex setf eelixir
+" Elvish
+au BufRead,BufNewFile *.elv setf elvish
+
" Euphoria 3 or 4
au BufNewFile,BufRead *.eu,*.ew,*.exu,*.exw call dist#ft#EuphoriaCheck()
if has("fname_case")
@@ -433,7 +458,7 @@ au BufNewFile,BufRead *quake[1-3]/*.cfg setf quake
au BufNewFile,BufRead *.qc setf c
" Configure files
-au BufNewFile,BufRead *.cfg setf cfg
+au BufNewFile,BufRead *.cfg\c call dist#ft#FTcfg()
" Cucumber
au BufNewFile,BufRead *.feature setf cucumber
@@ -476,6 +501,7 @@ au BufNewFile,BufRead */etc/dnsmasq.conf setf dnsmasq
au BufNewFile,BufRead *.desc setf desc
" the D language or dtrace
+au BufNewFile,BufRead */dtrace/*.d setf dtrace
au BufNewFile,BufRead *.d call dist#ft#DtraceCheck()
" Desktop files
@@ -649,6 +675,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()
@@ -656,11 +685,17 @@ au BufNewFile,BufRead *.fs call dist#ft#FTfs()
au BufNewFile,BufRead *.fsi,*.fsx setf fsharp
" GDB command files
-au BufNewFile,BufRead .gdbinit,gdbinit setf gdb
+au BufNewFile,BufRead .gdbinit,gdbinit,.gdbearlyinit,gdbearlyinit,*.gdb 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
@@ -673,8 +708,10 @@ autocmd BufRead,BufNewFile *.gift setf gift
" Git
au BufNewFile,BufRead COMMIT_EDITMSG,MERGE_MSG,TAG_EDITMSG setf gitcommit
au BufNewFile,BufRead NOTES_EDITMSG,EDIT_DESCRIPTION setf gitcommit
-au BufNewFile,BufRead *.git/config,.gitconfig,/etc/gitconfig setf gitconfig
+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
@@ -689,6 +726,12 @@ au BufNewFile,BufRead *.git/*
" Gkrellmrc
au BufNewFile,BufRead gkrellmrc,gkrellmrc_? setf gkrellmrc
+" Gleam
+au BufNewFile,BufRead *.gleam setf gleam
+
+" GLSL
+au BufNewFile,BufRead *.glsl setf glsl
+
" GP scripts (2.0 and onward)
au BufNewFile,BufRead *.gp,.gprc setf gp
@@ -708,16 +751,24 @@ au BufNewFile,BufRead gnashrc,.gnashrc,gnashpluginrc,.gnashpluginrc setf gnash
au BufNewFile,BufRead gitolite.conf setf gitolite
au BufNewFile,BufRead {,.}gitolite.rc,example.gitolite.rc setf perl
+" Glimmer-flavored TypeScript and JavaScript
+au BufNewFile,BufRead *.gts setf typescript.glimmer
+au BufNewFile,BufRead *.gjs setf javascript.glimmer
+
" 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
@@ -733,12 +784,21 @@ 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
+
+" Hare
+au BufNewFile,BufRead *.ha setf hare
+
" Haskell
au BufNewFile,BufRead *.hs,*.hsc,*.hs-boot,*.hsig setf haskell
au BufNewFile,BufRead *.lhs setf lhaskell
@@ -751,20 +811,33 @@ 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
+" Hoon
+au BufRead,BufNewFile *.hoon setf hoon
+
" Tilde (must be before HTML)
au BufNewFile,BufRead *.t.html setf tilde
" HTML (.shtml and .stm for server side)
au BufNewFile,BufRead *.html,*.htm,*.shtml,*.stm call dist#ft#FThtml()
+au BufNewFile,BufRead *.cshtml setf html
" HTML with Ruby - eRuby
au BufNewFile,BufRead *.erb,*.rhtml setf eruby
@@ -881,6 +954,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
@@ -899,6 +975,11 @@ au BufNewFile,BufRead *.jl setf julia
" Kixtart
au BufNewFile,BufRead *.kix setf kix
+" Kuka Robot Language
+au BufNewFile,BufRead *.src\c call dist#ft#FTsrc()
+au BufNewFile,BufRead *.dat\c call dist#ft#FTdat()
+au BufNewFile,BufRead *.sub\c setf krl
+
" Kimwitu[++]
au BufNewFile,BufRead *.k setf kwt
@@ -923,8 +1004,8 @@ au BufNewFile,BufRead *.latte,*.lte setf latte
" Limits
au BufNewFile,BufRead */etc/limits,*/etc/*limits.conf,*/etc/*limits.d/*.conf setf limits
-" LambdaProlog (*.mod too, see Modsim)
-au BufNewFile,BufRead *.sig setf lprolog
+" LambdaProlog or SML (see dist#ft#FTmod for *.mod)
+au BufNewFile,BufRead *.sig call dist#ft#FTsig()
" LDAP LDIF
au BufNewFile,BufRead *.ldif setf ldif
@@ -932,6 +1013,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
@@ -953,6 +1037,9 @@ au BufNewFile,BufRead *.ll setf lifelines
" Lilo: Linux loader
au BufNewFile,BufRead lilo.conf setf lilo
+" Lilypond
+au BufNewFile,BufRead *.ly,*.ily setf lilypond
+
" Lisp (*.el = ELisp, *.cl = Common Lisp)
" *.jl was removed, it's also used for Julia, better skip than guess wrong.
if has("fname_case")
@@ -1083,18 +1170,11 @@ au BufNewFile,BufRead *.mms call dist#ft#FTmms()
" Symbian meta-makefile definition (MMP)
au BufNewFile,BufRead *.mmp setf mmp
-" Modsim III (or LambdaProlog)
-au BufNewFile,BufRead *.mod
- \ if expand("<afile>") =~ '\<go.mod$' |
- \ setf gomod |
- \ elseif getline(1) =~ '\<module\>' |
- \ setf lprolog |
- \ else |
- \ setf modsim3 |
- \ endif
+" ABB Rapid, Modula-2, Modsim III or LambdaProlog
+au BufNewFile,BufRead *.mod\c call dist#ft#FTmod()
-" Modula-2 (.md removed in favor of Markdown)
-au BufNewFile,BufRead *.m2,*.DEF,*.MOD,*.mi setf modula2
+" Modula-2 (.md removed in favor of Markdown, see dist#ft#FTmod for *.MOD)
+au BufNewFile,BufRead *.m2,*.DEF,*.mi setf modula2
" Modula-3 (.m3, .i3, .mg, .ig)
au BufNewFile,BufRead *.[mi][3g] setf modula3
@@ -1105,6 +1185,9 @@ au BufNewFile,BufRead *.isc,*.monk,*.ssc,*.tsc setf monk
" MOO
au BufNewFile,BufRead *.moo setf moo
+" Moonscript
+au BufNewFile,BufRead *.moon setf moonscript
+
" Modconf
au BufNewFile,BufRead */etc/modules.conf,*/etc/modules,*/etc/conf.modules setf modconf
@@ -1169,6 +1252,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
@@ -1214,24 +1300,30 @@ au BufNewFile,BufRead opam,*.opam,*.opam.template setf opam
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
+au BufNewFile,BufRead *.or setf openroad
" OPL
-au BufNewFile,BufRead *.[Oo][Pp][Ll] setf opl
+au BufNewFile,BufRead *.[Oo][Pp][Ll] setf opl
+
+" OpenSCAD
+au BufNewFile,BufRead *.scad setf openscad
" Oracle config file
-au BufNewFile,BufRead *.ora setf ora
+au BufNewFile,BufRead *.ora setf ora
+
+" Org
+au BufNewFile,BufRead *.org,*.org_archive setf org
" Packet filter conf
-au BufNewFile,BufRead pf.conf setf pf
+au BufNewFile,BufRead pf.conf setf pf
-" Pacman Config (close enough to dosini)
-au BufNewFile,BufRead */etc/pacman.conf setf dosini
+" ini style config files, using # comments
+au BufNewFile,BufRead */etc/pacman.conf,mpv.conf setf confini
" Pacman hooks
au BufNewFile,BufRead *.hook
\ if getline(1) == '[Trigger]' |
- \ setf dosini |
+ \ setf conf |
\ endif
" Pam conf
@@ -1241,36 +1333,36 @@ au BufNewFile,BufRead */etc/pam.conf setf pamconf
au BufNewFile,BufRead pam_env.conf,.pam_environment setf pamenv
" PApp
-au BufNewFile,BufRead *.papp,*.pxml,*.pxsl setf papp
+au BufNewFile,BufRead *.papp,*.pxml,*.pxsl setf papp
" Password file
au BufNewFile,BufRead */etc/passwd,*/etc/passwd-,*/etc/passwd.edit,*/etc/shadow,*/etc/shadow-,*/etc/shadow.edit,*/var/backups/passwd.bak,*/var/backups/shadow.bak setf passwd
" Pascal (also *.p, *.pp, *.inc)
-au BufNewFile,BufRead *.pas setf pascal
+au BufNewFile,BufRead *.pas setf pascal
" Pascal or Puppet manifest
-au BufNewFile,BufRead *.pp call dist#ft#FTpp()
+au BufNewFile,BufRead *.pp call dist#ft#FTpp()
" Delphi or Lazarus program file
-au BufNewFile,BufRead *.dpr,*.lpr setf pascal
+au BufNewFile,BufRead *.dpr,*.lpr setf pascal
" Free Pascal makefile definition file
-au BufNewFile,BufRead *.fpc setf fpcmake
+au BufNewFile,BufRead *.fpc setf fpcmake
" PDF
-au BufNewFile,BufRead *.pdf setf pdf
+au BufNewFile,BufRead *.pdf setf pdf
" PCMK - HAE - crm configure edit
-au BufNewFile,BufRead *.pcmk setf pcmk
+au BufNewFile,BufRead *.pcmk setf pcmk
" Perl
if has("fname_case")
- au BufNewFile,BufRead *.pl,*.PL call dist#ft#FTpl()
+ au BufNewFile,BufRead *.pl,*.PL call dist#ft#FTpl()
else
- au BufNewFile,BufRead *.pl call dist#ft#FTpl()
+ au BufNewFile,BufRead *.pl call dist#ft#FTpl()
endif
-au BufNewFile,BufRead *.plx,*.al,*.psgi setf perl
+au BufNewFile,BufRead *.plx,*.al,*.psgi setf perl
" Perl, XPM or XPM2
au BufNewFile,BufRead *.pm
@@ -1286,9 +1378,10 @@ au BufNewFile,BufRead *.pm
au BufNewFile,BufRead *.pod setf pod
" Php, php3, php4, etc.
-" Also Phtml (was used for PHP 2 in the past)
-" Also .ctp for Cake template file
-au BufNewFile,BufRead *.php,*.php\d,*.phtml,*.ctp setf php
+" Also Phtml (was used for PHP 2 in the past).
+" Also .ctp for Cake template file.
+" Also .phpt for php tests.
+au BufNewFile,BufRead *.php,*.php\d,*.phtml,*.ctp,*.phpt setf php
" PHP config
au BufNewFile,BufRead php.ini-* setf dosini
@@ -1354,6 +1447,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 +1457,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 +1525,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
@@ -1491,6 +1593,9 @@ au BufNewFile,BufRead *.r,*.R call dist#ft#FTr()
" Remind
au BufNewFile,BufRead .reminders,*.remind,*.rem setf remind
+" ReScript
+au BufNewFile,BufRead *.res,*.resi setf rescript
+
" Resolv.conf
au BufNewFile,BufRead resolv.conf setf resolv
@@ -1503,6 +1608,9 @@ au BufNewFile,BufRead *.rng setf rng
" RPL/2
au BufNewFile,BufRead *.rpl setf rpl
+" Robot Framework
+au BufNewFile,BufRead *.robot,*.resource setf robot
+
" Robots.txt
au BufNewFile,BufRead robots.txt setf robots
@@ -1562,16 +1670,22 @@ au BufNewFile,BufRead *.sass setf sass
au BufNewFile,BufRead *.sa setf sather
" Scala
-au BufNewFile,BufRead *.scala,*.sc setf scala
+au BufNewFile,BufRead *.scala setf scala
" SBT - Scala Build Tool
au BufNewFile,BufRead *.sbt setf sbt
+" SuperCollider
+au BufNewFile,BufRead *.sc call dist#ft#FTsc()
+
+au BufNewFile,BufRead *.quark setf supercollider
+
+" scdoc
+au BufNewFile,BufRead *.scd call dist#ft#FTscd()
+
" Scilab
au BufNewFile,BufRead *.sci,*.sce setf scilab
-" scdoc
-au BufNewFile,BufRead *.scd setf scdoc
" SCSS
au BufNewFile,BufRead *.scss setf scss
@@ -1594,7 +1708,8 @@ au BufNewFile,BufRead *.siv,*.sieve setf sieve
" Sendmail
au BufNewFile,BufRead sendmail.cf setf sm
-" Sendmail .mc files are actually m4. Could also be MS Message text file.
+" Sendmail .mc files are actually m4. Could also be MS Message text file or
+" Maxima.
au BufNewFile,BufRead *.mc call dist#ft#McSetf()
" Services
@@ -1665,7 +1780,7 @@ au BufNewFile,BufRead .zshrc,.zshenv,.zlogin,.zlogout,.zcompdump setf zsh
au BufNewFile,BufRead *.zsh setf zsh
" Scheme
-au BufNewFile,BufRead *.scm,*.ss,*.sld,*.rkt,*.rktd,*.rktl setf scheme
+au BufNewFile,BufRead *.scm,*.ss,*.sld,*.rkt,*.rktd,*.rktl setf scheme
" Screen RC
au BufNewFile,BufRead .screenrc,screenrc setf screen
@@ -1690,16 +1805,11 @@ au BufNewFile,BufRead *.il,*.ils,*.cdf setf skill
au BufNewFile,BufRead .slrnrc setf slrnrc
au BufNewFile,BufRead *.score setf slrnsc
-" Smalltalk (and TeX)
+" Smalltalk
au BufNewFile,BufRead *.st setf st
-au BufNewFile,BufRead *.cls
- \ if getline(1) =~ '^%' |
- \ setf tex |
- \ elseif getline(1)[0] == '#' && getline(1) =~ 'rexx' |
- \ setf rexx |
- \ else |
- \ setf st |
- \ endif
+
+" Smalltalk (and Rexx, TeX, and Visual Basic)
+au BufNewFile,BufRead *.cls call dist#ft#FTcls()
" Smarty templates
au BufNewFile,BufRead *.tpl setf smarty
@@ -1733,6 +1843,9 @@ au BufNewFile,BufRead *.mib,*.my setf mib
au BufNewFile,BufRead *.hog,snort.conf,vision.conf setf hog
au BufNewFile,BufRead *.rules call dist#ft#FTRules()
+" Solidity
+au BufRead,BufNewFile *.sol setf solidity
+
" SPARQL queries
au BufNewFile,BufRead *.rq,*.sparql setf sparql
@@ -1830,6 +1943,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 +1963,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 +1983,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 +2003,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 +2018,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
@@ -1907,6 +2035,9 @@ au BufNewFile,BufReadPost *.tssop setf tssop
" TSS - Command Line (temporary)
au BufNewFile,BufReadPost *.tsscl setf tsscl
+" TSV Files
+au BufNewFile,BufRead *.tsv setf tsv
+
" Tutor mode
au BufNewFile,BufReadPost *.tutor setf tutor
@@ -1950,9 +2081,15 @@ au BufNewFile,BufRead */.init/*.conf,*/.init/*.override setf upstart
au BufNewFile,BufRead */.config/upstart/*.conf setf upstart
au BufNewFile,BufRead */.config/upstart/*.override setf upstart
+" Vala
+au BufNewFile,BufRead *.vala setf vala
+
" Vera
au BufNewFile,BufRead *.vr,*.vri,*.vrh setf vera
+" Vagrant (uses Ruby syntax)
+au BufNewFile,BufRead Vagrantfile setf ruby
+
" Verilog HDL
au BufNewFile,BufRead *.v setf verilog
@@ -1980,7 +2117,7 @@ au BufRead,BufNewFile *.hw,*.module,*.pkg
\ endif
" Visual Basic (also uses *.bas) or FORM
-au BufNewFile,BufRead *.frm call dist#ft#FTVB("form")
+au BufNewFile,BufRead *.frm call dist#ft#FTfrm()
" SaxBasic is close to Visual Basic
au BufNewFile,BufRead *.sba setf vb
@@ -2006,6 +2143,9 @@ au BufNewFile,BufRead *.wm setf webmacro
" Wget config
au BufNewFile,BufRead .wgetrc,wgetrc setf wget
+" Wget2 config
+au BufNewFile,BufRead .wget2rc,wget2rc setf wget2
+
" Website MetaLanguage
au BufNewFile,BufRead *.wml setf wml
@@ -2144,6 +2284,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
@@ -2259,7 +2402,7 @@ au BufNewFile,BufRead *fvwm2rc*
au BufNewFile,BufRead */tmp/lltmp* call s:StarSetf('gedcom')
" Git
-au BufNewFile,BufRead */.gitconfig.d/*,/etc/gitconfig.d/* call s:StarSetf('gitconfig')
+au BufNewFile,BufRead */.gitconfig.d/*,*/etc/gitconfig.d/* call s:StarSetf('gitconfig')
" Gitolite
au BufNewFile,BufRead */gitolite-admin/conf/* call s:StarSetf('gitolite')
@@ -2285,6 +2428,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')
@@ -2321,7 +2467,7 @@ au BufNewFile,BufRead neomuttrc*,Neomuttrc* call s:StarSetf('neomuttrc')
au BufNewFile,BufRead tmac.* call s:StarSetf('nroff')
" OpenBSD hostname.if
-au BufNewFile,BufRead /etc/hostname.* call s:StarSetf('config')
+au BufNewFile,BufRead */etc/hostname.* call s:StarSetf('config')
" Pam conf
au BufNewFile,BufRead */etc/pam.d/* call s:StarSetf('pamconf')
@@ -2438,7 +2584,7 @@ endif
" Function called for testing all functions defined here. These are
" script-local, thus need to be executed here.
" Returns a string with error messages (hopefully empty).
-func! TestFiletypeFuncs(testlist)
+func TestFiletypeFuncs(testlist)
let output = ''
for f in a:testlist
try
diff --git a/runtime/ftplugin/abaqus.vim b/runtime/ftplugin/abaqus.vim
index b263d0c318..5ce565ef3f 100644
--- a/runtime/ftplugin/abaqus.vim
+++ b/runtime/ftplugin/abaqus.vim
@@ -1,7 +1,7 @@
" Vim filetype plugin file
" Language: Abaqus finite element input file (www.abaqus.com)
" Maintainer: Carl Osterwisch <osterwischc@asme.org>
-" Last Change: 2012 Apr 30
+" Last Change: 2022 May 09
" Only do this when not done yet for this buffer
if exists("b:did_ftplugin") | finish | endif
@@ -86,11 +86,11 @@ let b:undo_ftplugin .= "|unmap <buffer> [[|unmap <buffer> ]]"
\ . "|unmap <buffer> <LocalLeader><LocalLeader>"
" Undo must be done in nocompatible mode for <LocalLeader>.
-let b:undo_ftplugin = "let s:cpo_save = &cpoptions|"
+let b:undo_ftplugin = "let b:cpo_save = &cpoptions|"
\ . "set cpoptions&vim|"
\ . b:undo_ftplugin
- \ . "|let &cpoptions = s:cpo_save"
- \ . "|unlet s:cpo_save"
+ \ . "|let &cpoptions = b:cpo_save"
+ \ . "|unlet b:cpo_save"
" Restore saved compatibility options
let &cpoptions = s:cpo_save
diff --git a/runtime/ftplugin/ant.vim b/runtime/ftplugin/ant.vim
index 5905858896..aee07ca4b9 100644
--- a/runtime/ftplugin/ant.vim
+++ b/runtime/ftplugin/ant.vim
@@ -1,8 +1,10 @@
" Vim filetype plugin file
" Language: ant
-" Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net>
+"
+" This runtime file is looking for a new maintainer.
+"
+" Former maintainer: Dan Sharp
" Last Changed: 20 Jan 2009
-" URL: http://dwsharp.users.sourceforge.net/vim/ftplugin
if exists("b:did_ftplugin") | finish | endif
diff --git a/runtime/ftplugin/aspvbs.vim b/runtime/ftplugin/aspvbs.vim
index 660dab4685..70a130d287 100644
--- a/runtime/ftplugin/aspvbs.vim
+++ b/runtime/ftplugin/aspvbs.vim
@@ -1,8 +1,10 @@
" Vim filetype plugin file
" Language: aspvbs
-" Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net>
+"
+" This runtime file is looking for a new maintainer.
+"
+" Former maintainer: Dan Sharp
" Last Changed: 20 Jan 2009
-" URL: http://dwsharp.users.sourceforge.net/vim/ftplugin
if exists("b:did_ftplugin") | finish | endif
diff --git a/runtime/ftplugin/basic.vim b/runtime/ftplugin/basic.vim
index c6ec254dfc..4399fbf3ad 100644
--- a/runtime/ftplugin/basic.vim
+++ b/runtime/ftplugin/basic.vim
@@ -1,7 +1,7 @@
" Vim filetype plugin file
-" Language: BASIC
+" Language: BASIC (QuickBASIC 4.5)
" Maintainer: Doug Kearns <dougkearns@gmail.com>
-" Last Change: 2015 Jan 10
+" Last Change: 2022 Jun 22
if exists("b:did_ftplugin")
finish
@@ -11,17 +11,47 @@ let b:did_ftplugin = 1
let s:cpo_save = &cpo
set cpo&vim
-setlocal comments=:REM,:'
+setlocal comments=:REM\ ,:Rem\ ,:rem\ ,:'
setlocal commentstring='\ %s
setlocal formatoptions-=t formatoptions+=croql
-if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter")
- let b:browsefilter = "BASIC Source Files (*.bas)\t*.bas\n" .
- \ "All Files (*.*)\t*.*\n"
+let b:undo_ftplugin = "setl fo< com< cms<"
+
+" TODO: support exit ... as middle matches?
+if exists("loaded_matchit") && !exists("b:match_words")
+ let s:line_start = '\%(^\s*\)\@<='
+ let s:not_end = '\%(end\s\+\)\@<!'
+ let s:not_end_or_exit = '\%(\%(end\|exit\)\s\+\)\@<!'
+
+ let b:match_ignorecase = 1
+ let b:match_words =
+ \ s:not_end_or_exit .. '\<def\s\+fn:\<end\s\+def\>,' ..
+ \ s:not_end_or_exit .. '\<function\>:\<end\s\+function\>,' ..
+ \ s:not_end_or_exit .. '\<sub\>:\<end\s\+sub\>,' ..
+ \ s:not_end .. '\<type\>:\<end\s\+type\>,' ..
+ \ s:not_end .. '\<select\>:\%(select\s\+\)\@<!\<case\%(\s\+\%(else\|is\)\)\=\>:\<end\s\+select\>,' ..
+ \ '\<do\>:\<loop\>,' ..
+ \ '\<for\>\%(\s\+\%(input\|output\|random\|append\|binary\)\)\@!:\<next\>,' ..
+ \ '\<while\>:\<wend\>,' ..
+ \ s:line_start .. 'if\%(.*\<then\s*\%($\|''\)\)\@=:\<\%(' .. s:line_start .. 'else\|elseif\)\>:\<end\s\+if\>,' ..
+ \ '\<lock\>:\<unlock\>'
+ let b:match_skip = 'synIDattr(synID(line("."),col("."),1),"name") =~? "comment\\|string" || ' ..
+ \ 'strpart(getline("."), 0, col(".") ) =~? "\\<exit\\s\\+"'
+
+ let b:undo_ftplugin ..= " | unlet! b:match_ignorecase b:match_skip b:match_words"
+
+ unlet s:line_start s:not_end s:not_end_or_exit
endif
-let b:undo_ftplugin = "setl fo< com< cms< sua<" .
- \ " | unlet! b:browsefilter"
+if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter")
+ let b:browsefilter = "BASIC Source Files (*.bas)\t*.bas\n" ..
+ \ "BASIC Include Files (*.bi, *.bm)\t*.bi;*.bm\n" ..
+ \ "All Files (*.*)\t*.*\n"
+ let b:basic_set_browsefilter = 1
+ let b:undo_ftplugin ..= " | unlet! b:browsefilter b:basic_set_browsefilter"
+endif
let &cpo = s:cpo_save
unlet s:cpo_save
+
+" vim: nowrap sw=2 sts=2 ts=8 noet fdm=marker:
diff --git a/runtime/ftplugin/c.vim b/runtime/ftplugin/c.vim
index d4564a4aec..cfaf26f66c 100644
--- a/runtime/ftplugin/c.vim
+++ b/runtime/ftplugin/c.vim
@@ -1,7 +1,7 @@
" Vim filetype plugin file
" Language: C
" Maintainer: Bram Moolenaar <Bram@vim.org>
-" Last Change: 2021 Sep 21
+" Last Change: 2022 Apr 08
" Only do this when not done yet for this buffer
if exists("b:did_ftplugin")
@@ -31,7 +31,8 @@ if exists('&ofu')
endif
" Set 'comments' to format dashed lists in comments.
-setlocal comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,://
+" Also include ///, used for Doxygen.
+ setlocal comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,:///,://
" When the matchit plugin is loaded, this makes the % command skip parens and
" braces in comments properly.
diff --git a/runtime/ftplugin/clojure.vim b/runtime/ftplugin/clojure.vim
index 81d53b1227..c922d75699 100644
--- a/runtime/ftplugin/clojure.vim
+++ b/runtime/ftplugin/clojure.vim
@@ -5,7 +5,7 @@
" Meikel Brandmeyer <mb@kotka.de>
" URL: https://github.com/clojure-vim/clojure.vim
" License: Vim (see :h license)
-" Last Change: 2021-10-26
+" Last Change: 2022-03-24
if exists("b:did_ftplugin")
finish
@@ -43,7 +43,7 @@ setlocal commentstring=;\ %s
" specially and hence are not indented specially.
"
" -*- LISPWORDS -*-
-" Generated from https://github.com/clojure-vim/clojure.vim/blob/62b215f079ce0f3834fd295c7a7f6bd8cc54bcc3/clj/src/vim_clojure_static/generate.clj
+" Generated from https://github.com/clojure-vim/clojure.vim/blob/fd280e33e84c88e97860930557dba3ff80b1a82d/clj/src/vim_clojure_static/generate.clj
setlocal lispwords=as->,binding,bound-fn,case,catch,cond->,cond->>,condp,def,definline,definterface,defmacro,defmethod,defmulti,defn,defn-,defonce,defprotocol,defrecord,defstruct,deftest,deftest-,deftype,doseq,dotimes,doto,extend,extend-protocol,extend-type,fn,for,if,if-let,if-not,if-some,let,letfn,locking,loop,ns,proxy,reify,set-test,testing,when,when-first,when-let,when-not,when-some,while,with-bindings,with-in-str,with-local-vars,with-open,with-precision,with-redefs,with-redefs-fn,with-test
" Provide insert mode completions for special forms and clojure.core. As
@@ -66,10 +66,10 @@ endif
" Filter files in the browse dialog
if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter")
- let b:browsefilter = "Clojure Source Files (*.clj)\t*.clj\n" .
- \ "ClojureScript Source Files (*.cljs)\t*.cljs\n" .
- \ "Java Source Files (*.java)\t*.java\n" .
- \ "All Files (*.*)\t*.*\n"
+ let b:browsefilter = "All Files\t*\n" .
+ \ "Clojure Files\t*.clj;*.cljc;*.cljs;*.cljx\n" .
+ \ "EDN Files\t*.edn\n" .
+ \ "Java Files\t*.java\n"
let b:undo_ftplugin .= ' | unlet! b:browsefilter'
endif
diff --git a/runtime/ftplugin/config.vim b/runtime/ftplugin/config.vim
index 7fde42ebf5..73136cbc66 100644
--- a/runtime/ftplugin/config.vim
+++ b/runtime/ftplugin/config.vim
@@ -1,8 +1,10 @@
" Vim filetype plugin file
" Language: config
-" Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net>
+"
+" This runtime file is looking for a new maintainer.
+"
+" Former maintainer: Dan Sharp
" Last Changed: 20 Jan 2009
-" URL: http://dwsharp.users.sourceforge.net/vim/ftplugin
if exists("b:did_ftplugin") | finish | endif
diff --git a/runtime/ftplugin/confini.vim b/runtime/ftplugin/confini.vim
new file mode 100644
index 0000000000..310e046353
--- /dev/null
+++ b/runtime/ftplugin/confini.vim
@@ -0,0 +1,10 @@
+" Vim filetype plugin file
+" Language: confini
+
+" Quit if a ftplugin file was already loaded
+if exists("b:did_ftplugin")
+ finish
+endif
+
+" Use the cfg plugin, it's similar enough.
+runtime! ftplugin/cfg.vim
diff --git a/runtime/ftplugin/cpp.vim b/runtime/ftplugin/cpp.vim
index f9d31cbec3..58c4e4b24a 100644
--- a/runtime/ftplugin/cpp.vim
+++ b/runtime/ftplugin/cpp.vim
@@ -10,6 +10,7 @@ endif
" Behaves mostly just like C
runtime! ftplugin/c.vim ftplugin/c_*.vim ftplugin/c/*.vim
+runtime! ftplugin/c.lua ftplugin/c_*.lua ftplugin/c/*.lua
" C++ uses templates with <things>
" Disabled, because it gives an error for typing an unmatched ">".
diff --git a/runtime/ftplugin/cs.vim b/runtime/ftplugin/cs.vim
index 7c2cbda32f..f53ffcbc8e 100644
--- a/runtime/ftplugin/cs.vim
+++ b/runtime/ftplugin/cs.vim
@@ -1,29 +1,43 @@
" Vim filetype plugin file
-" Language: C#
-" Maintainer: Johannes Zellner <johannes@zellner.org>
-" Last Change: Tue, 09 Mar 2004 14:09:33 CET
+" Language: C#
+" Maintainer: Nick Jensen <nickspoon@gmail.com>
+" Former Maintainer: Johannes Zellner <johannes@zellner.org>
+" Last Change: 2021-12-07
+" License: Vim (see :h license)
+" Repository: https://github.com/nickspoons/vim-cs
-" Only do this when not done yet for this buffer
-if exists("b:did_ftplugin")
+if exists('b:did_ftplugin')
finish
endif
-
-" Don't load another plugin for this buffer
let b:did_ftplugin = 1
-let s:keepcpo= &cpo
-set cpo&vim
+
+let s:save_cpo = &cpoptions
+set cpoptions&vim
" Set 'formatoptions' to break comment lines but not other lines,
" and insert the comment leader when hitting <CR> or using "o".
-setlocal fo-=t fo+=croql
+setlocal formatoptions-=t formatoptions+=croql
" Set 'comments' to format dashed lists in comments.
setlocal comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,:///,://
-if has("gui_win32") && !exists("b:browsefilter")
- let b:browsefilter = "C# Source Files (*.cs)\t*.cs\n" .
- \ "All Files (*.*)\t*.*\n"
+let b:undo_ftplugin = 'setl com< fo<'
+
+if exists('loaded_matchit') && !exists('b:match_words')
+ " #if/#endif support included by default
+ let b:match_words = '\%(^\s*\)\@<=#\s*region\>:\%(^\s*\)\@<=#\s*endregion\>,'
+ let b:undo_ftplugin .= ' | unlet! b:match_words'
endif
-let &cpo = s:keepcpo
-unlet s:keepcpo
+if (has('gui_win32') || has('gui_gtk')) && !exists('b:browsefilter')
+ let b:browsefilter = "C# Source Files (*.cs *.csx)\t*.cs;*.csx\n" .
+ \ "C# Project Files (*.csproj)\t*.csproj\n" .
+ \ "Visual Studio Solution Files (*.sln)\t*.sln\n" .
+ \ "All Files (*.*)\t*.*\n"
+ let b:undo_ftplugin .= ' | unlet! b:browsefilter'
+endif
+
+let &cpoptions = s:save_cpo
+unlet s:save_cpo
+
+" vim:et:sw=2:sts=2
diff --git a/runtime/ftplugin/csc.vim b/runtime/ftplugin/csc.vim
index 3a09c3bf8b..7b4126a503 100644
--- a/runtime/ftplugin/csc.vim
+++ b/runtime/ftplugin/csc.vim
@@ -1,8 +1,10 @@
" Vim filetype plugin file
" Language: csc
-" Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net>
+"
+" This runtime file is looking for a new maintainer.
+"
+" Former maintainer: Dan Sharp
" Last Changed: 20 Jan 2009
-" URL: http://dwsharp.users.sourceforge.net/vim/ftplugin
if exists("b:did_ftplugin") | finish | endif
let b:did_ftplugin = 1
diff --git a/runtime/ftplugin/csh.vim b/runtime/ftplugin/csh.vim
index 929823219c..ca5da5a8b9 100644
--- a/runtime/ftplugin/csh.vim
+++ b/runtime/ftplugin/csh.vim
@@ -1,7 +1,7 @@
" Vim filetype plugin file
" Language: csh
" Maintainer: Doug Kearns <dougkearns@gmail.com>
-" Previous Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net>
+" Previous Maintainer: Dan Sharp
" Contributor: Johannes Zellner <johannes@zellner.org>
" Last Change: 2021 Oct 15
diff --git a/runtime/ftplugin/dtd.vim b/runtime/ftplugin/dtd.vim
index 6c08f6691d..a046118c70 100644
--- a/runtime/ftplugin/dtd.vim
+++ b/runtime/ftplugin/dtd.vim
@@ -1,8 +1,10 @@
" Vim filetype plugin file
" Language: dtd
-" Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net>
+"
+" This runtime file is looking for a new maintainer.
+"
+" Former maintainer: Dan Sharp
" Last Changed: 20 Jan 2009
-" URL: http://dwsharp.users.sourceforge.net/vim/ftplugin
if exists("b:did_ftplugin") | finish | endif
let b:did_ftplugin = 1
diff --git a/runtime/ftplugin/elixir.vim b/runtime/ftplugin/elixir.vim
new file mode 100644
index 0000000000..3b38051d08
--- /dev/null
+++ b/runtime/ftplugin/elixir.vim
@@ -0,0 +1,11 @@
+" Elixir filetype plugin
+" Language: Elixir
+" Maintainer: Mitchell Hanberg <vimNOSPAM@mitchellhanberg.com>
+" Last Change: 2022 Apr 20
+
+if exists("b:did_ftplugin")
+ finish
+endif
+let b:did_ftplugin = 1
+
+setlocal commentstring=#\ %s
diff --git a/runtime/ftplugin/elm.vim b/runtime/ftplugin/elm.vim
index 1e10346186..8c2de190b0 100644
--- a/runtime/ftplugin/elm.vim
+++ b/runtime/ftplugin/elm.vim
@@ -1,7 +1,7 @@
" Elm filetype plugin file
" Language: Elm
" Maintainer: Andreas Scharf <as@99n.de>
-" Latest Revision: 2020-05-29
+" Latest Revision: 2022-04-25
if exists("b:did_ftplugin")
finish
@@ -14,5 +14,7 @@ set cpo&vim
setlocal comments=s1fl:{-,mb:\ ,ex:-},:--
setlocal commentstring=--\ %s
+let b:undo_ftplugin = "setl com< cms<"
+
let &cpo = s:cpo_save
unlet s:cpo_save
diff --git a/runtime/ftplugin/fennel.vim b/runtime/ftplugin/fennel.vim
new file mode 100644
index 0000000000..2e502699c5
--- /dev/null
+++ b/runtime/ftplugin/fennel.vim
@@ -0,0 +1,18 @@
+" Vim filetype plugin file
+" Language: Fennel
+" Maintainer: Gregory Anders <greg[NOSPAM]@gpanders.com>
+" Last Update: 2022 Apr 20
+
+if exists('b:did_ftplugin')
+ finish
+endif
+let b:did_ftplugin = 1
+
+setlocal commentstring=;%s
+setlocal comments=:;;,:;
+setlocal formatoptions-=t
+setlocal suffixesadd=.fnl
+setlocal lisp
+setlocal lispwords=accumulate,collect,do,doto,each,eval-compiler,fn,for,icollect,lambda,let,macro,macros,match,match-try,when,while,with-open
+
+let b:undo_ftplugin = 'setlocal commentstring< comments< formatoptions< suffixesadd< lisp< lispwords<'
diff --git a/runtime/ftplugin/fetchmail.vim b/runtime/ftplugin/fetchmail.vim
index 33bb417be4..032f6610f9 100644
--- a/runtime/ftplugin/fetchmail.vim
+++ b/runtime/ftplugin/fetchmail.vim
@@ -1,19 +1,16 @@
" Vim filetype plugin file
-" Language: fetchmail(1) RC File
-" Previous Maintainer: Nikolai Weibull <now@bitwi.se>
-" Latest Revision: 2008-07-09
+" Language: fetchmail(1) RC File
+" Maintainer: Doug Kearns <dougkearns@gmail.com>
+" Previous Maintainer: Nikolai Weibull <now@bitwi.se>
+" Latest Revision: 2022 Jun 30
if exists("b:did_ftplugin")
finish
endif
let b:did_ftplugin = 1
-let s:cpo_save = &cpo
-set cpo&vim
+setlocal comments=:# commentstring=#\ %s
+setlocal formatoptions-=t formatoptions+=croql
let b:undo_ftplugin = "setl com< cms< fo<"
-setlocal comments=:# commentstring=#\ %s formatoptions-=t formatoptions+=croql
-
-let &cpo = s:cpo_save
-unlet s:cpo_save
diff --git a/runtime/ftplugin/fortran.vim b/runtime/ftplugin/fortran.vim
index 26dc90a184..8f5b243b82 100644
--- a/runtime/ftplugin/fortran.vim
+++ b/runtime/ftplugin/fortran.vim
@@ -1,6 +1,6 @@
" Vim settings file
" Language: Fortran 2008 (and older: Fortran 2003, 95, 90, 77, 66)
-" Version: (v53) 2021 April 06
+" Version: (v53) 2021 April 06 (updated 2022 May 22)
" Maintainer: Ajit J. Thakkar <ajit@unb.ca>; <http://www2.unb.ca/~ajit/>
" Usage: For instructions, do :help fortran-plugin from Vim
" Credits:
@@ -105,12 +105,13 @@ if !exists("b:match_words")
let s:notselect = '\%(\<select\s\+\)\@<!'
let s:notelse = '\%(\<end\s\+\|\<else\s\+\)\@<!'
let s:notprocedure = '\%(\s\+procedure\>\)\@!'
+ let s:nothash = '\%(^\s*#\s*\)\@<!'
let b:match_ignorecase = 1
let b:match_words =
\ '(:),' .
\ '\<select\s*case\>:' . s:notselect. '\<case\>:\<end\s*select\>,' .
\ s:notelse . '\<if\s*(.\+)\s*then\>:' .
- \ '\<else\s*\%(if\s*(.\+)\s*then\)\=\>:\<end\s*if\>,'.
+ \ s:nothash . '\<else\s*\%(if\s*(.\+)\s*then\)\=\>:' . s:nothash . '\<end\s*if\>,'.
\ 'do\s\+\(\d\+\):\%(^\s*\)\@<=\1\s,'.
\ s:notend . '\<do\>:\<end\s*do\>,'.
\ s:notelse . '\<where\>:\<elsewhere\>:\<end\s*where\>,'.
@@ -122,7 +123,8 @@ if !exists("b:match_words")
\ s:notend . '\<subroutine\>:\<end\s*subroutine\>,'.
\ s:notend . '\<function\>:\<end\s*function\>,'.
\ s:notend . '\<module\>' . s:notprocedure . ':\<end\s*module\>,'.
- \ s:notend . '\<program\>:\<end\s*program\>'
+ \ s:notend . '\<program\>:\<end\s*program\>,'.
+ \ '\%(^\s*\)\@<=#\s*if\%(def\|ndef\)\=\>:\%(^\s*\)\@<=#\s*\%(elif\|else\)\>:\%(^\s*\)\@<=#\s*endif\>'
endif
" File filters for :browse e
diff --git a/runtime/ftplugin/freebasic.vim b/runtime/ftplugin/freebasic.vim
index a2bb459f20..1680e84c9c 100644
--- a/runtime/ftplugin/freebasic.vim
+++ b/runtime/ftplugin/freebasic.vim
@@ -1,13 +1,81 @@
" Vim filetype plugin file
-" Language: FreeBasic
+" Language: FreeBASIC
" Maintainer: Doug Kearns <dougkearns@gmail.com>
-" Last Change: 2015 Jan 10
+" Last Change: 2022 Jun 24
+" Setup {{{1
if exists("b:did_ftplugin")
finish
endif
-let b:did_ftplugin = 1
+
+let s:cpo_save = &cpo
+set cpo&vim
runtime! ftplugin/basic.vim
-" vim: ts=8
+let s:dialect = freebasic#GetDialect()
+
+" Comments {{{1
+" add ''comments before 'comments
+let &l:comments = "sO:*\ -,mO:*\ \ ,exO:*/,s1:/',mb:',ex:'/,:''," .. &l:comments
+
+" Match words {{{1
+if exists("loaded_matchit")
+ let s:line_start = '\%(^\s*\)\@<='
+ let s:not_end = '\%(end\s\+\)\@<!'
+
+ let b:match_words ..= ','
+
+ if s:dialect == 'fb'
+ let b:match_words ..= s:not_end .. '\<constructor\>:\<end\s\+constructor\>,' ..
+ \ s:not_end .. '\<destructor\>:\<end\s\+destructor\>,' ..
+ \ s:not_end .. '\<property\>:\<end\s\+property\>,' ..
+ \ s:not_end .. '\<operator\>:\<end\s\+operator\>,' ..
+ \ s:not_end .. '\<extern\%(\s\+"\)\@=:\<end\s\+extern\>,'
+ endif
+
+ if s:dialect == 'fb' || s:dialect == 'deprecated'
+ let b:match_words ..= s:not_end .. '\<scope\>:\<end\s\+scope\>,'
+ endif
+
+ if s:dialect == 'qb'
+ let b:match_words ..= s:not_end .. '\<__asm\>:\<end\s\+__asm\>,' ..
+ \ s:not_end .. '\<__union\>:\<end\s\+__union\>,' ..
+ \ s:not_end .. '\<__with\>:\<end\s\+__with\>,'
+ else
+ let b:match_words ..= s:not_end .. '\<asm\>:\<end\s\+asm\>,' ..
+ \ s:not_end .. '\<namespace\>:\<end\s\+namespace\>,' ..
+ \ s:not_end .. '\<union\>:\<end\s\+union\>,' ..
+ \ s:not_end .. '\<with\>:\<end\s\+with\>,'
+ endif
+
+ let b:match_words ..= s:not_end .. '\<enum\>:\<end\s\+enum\>,' ..
+ \ s:line_start .. '#\s*\%(if\|ifdef\|ifndef\)\>:' ..
+ \ s:line_start .. '#\s*\%(else\|elseif\)\>:' ..
+ \ s:line_start .. '#\s*endif\>,' ..
+ \ s:line_start .. '#\s*macro\>:' .. s:line_start .. '#\s*endmacro\>,' ..
+ \ "/':'/"
+
+ " skip "function = <retval>" and "continue { do | for | while }"
+ if s:dialect == "qb"
+ let s:continue = "__continue"
+ else
+ let s:continue = "continue"
+ endif
+ let b:match_skip ..= ' || strpart(getline("."), col(".") - 1) =~? "^\\<function\\s\\+="' ..
+ \ ' || strpart(getline("."), 0, col(".") ) =~? "\\<' .. s:continue .. '\\s\\+"'
+
+ unlet s:not_end s:line_start
+endif
+
+if (has("gui_win32") || has("gui_gtk")) && exists("b:basic_set_browsefilter")
+ let b:browsefilter = "FreeBASIC Source Files (*.bas)\t*.bas\n" ..
+ \ "FreeBASIC Header Files (*.bi)\t*.bi\n" ..
+ \ "All Files (*.*)\t*.*\n"
+endif
+
+" Cleanup {{{1
+let &cpo = s:cpo_save
+unlet s:cpo_save s:dialect
+
+" vim: nowrap sw=2 sts=2 ts=8 noet fdm=marker:
diff --git a/runtime/ftplugin/html.vim b/runtime/ftplugin/html.vim
index 7579080ea5..3179aa2e88 100644
--- a/runtime/ftplugin/html.vim
+++ b/runtime/ftplugin/html.vim
@@ -1,8 +1,10 @@
" Vim filetype plugin file
" Language: html
-" Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net>
+"
+" This runtime file is looking for a new maintainer.
+"
+" Former maintainer: Dan Sharp
" Last Changed: 20 Jan 2009
-" URL: http://dwsharp.users.sourceforge.net/vim/ftplugin
if exists("b:did_ftplugin") | finish | endif
let b:did_ftplugin = 1
diff --git a/runtime/ftplugin/icon.vim b/runtime/ftplugin/icon.vim
new file mode 100644
index 0000000000..33a86dfb9e
--- /dev/null
+++ b/runtime/ftplugin/icon.vim
@@ -0,0 +1,36 @@
+" Vim filetype plugin file
+" Language: Icon
+" Maintainer: Doug Kearns <dougkearns@gmail.com>
+" Last Change: 2022 Jun 16
+
+if exists("b:did_ftplugin")
+ finish
+endif
+let b:did_ftplugin = 1
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+setlocal comments=:#
+setlocal commentstring=#\ %s
+setlocal formatoptions-=t formatoptions+=croql
+
+let b:undo_ftplugin = "setl fo< com< cms<"
+
+if exists("loaded_matchit")
+ " let b:match_ignorecase = 0
+ let b:match_words = '^\s*$\s*if\(def\|ndef\)\=\>:^\s*$\s*elif\>:^\s*$\s*else\>:^\s*$\s*endif\>,' .
+ \ '\<procedure\>:\<\%(initial\|return\|suspend\|fail\)\>:\<end\>'
+ let b:undo_ftplugin ..= " | unlet! b:match_words"
+endif
+
+if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter")
+ let b:browsefilter = "Icon Source Files (*.icn)\t*.icn\n" .
+ \ "All Files (*.*)\t*.*\n"
+ let b:undo_ftplugin ..= " | unlet! b:browsefilter"
+endif
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
+
+" vim: nowrap sw=2 sts=2 ts=8
diff --git a/runtime/ftplugin/java.vim b/runtime/ftplugin/java.vim
index 292cb6b166..74c8e8d1c1 100644
--- a/runtime/ftplugin/java.vim
+++ b/runtime/ftplugin/java.vim
@@ -1,8 +1,10 @@
" Vim filetype plugin file
" Language: Java
-" Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net>
+"
+" This runtime file is looking for a new maintainer.
+"
+" Former maintainer: Dan Sharp
" Last Change: 2012 Mar 11
-" URL: http://dwsharp.users.sourceforge.net/vim/ftplugin
if exists("b:did_ftplugin") | finish | endif
let b:did_ftplugin = 1
diff --git a/runtime/ftplugin/jsp.vim b/runtime/ftplugin/jsp.vim
index fbba863b32..18136ccc24 100644
--- a/runtime/ftplugin/jsp.vim
+++ b/runtime/ftplugin/jsp.vim
@@ -1,8 +1,10 @@
" Vim filetype plugin file
" Language: jsp
-" Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net>
+"
+" This runtime file is looking for a new maintainer.
+"
+" Former maintainer: Dan Sharp
" Last Changed: 20 Jan 2009
-" URL: http://dwsharp.users.sourceforge.net/vim/ftplugin
if exists("b:did_ftplugin") | finish | endif
diff --git a/runtime/ftplugin/liquid.vim b/runtime/ftplugin/liquid.vim
index b211a884c6..f24ec4cbb2 100644
--- a/runtime/ftplugin/liquid.vim
+++ b/runtime/ftplugin/liquid.vim
@@ -1,7 +1,7 @@
" Vim filetype plugin
" Language: Liquid
" Maintainer: Tim Pope <vimNOSPAM@tpope.org>
-" Last Change: 2010 May 21
+" Last Change: 2022 Mar 15
if exists('b:did_ftplugin')
finish
@@ -53,7 +53,7 @@ if has('gui_win32')
endif
if exists('loaded_matchit')
- let b:match_words .= '\<\%(if\w*\|unless\|case\)\>:\<\%(elsif\|else\|when\)\>:\<end\%(if\w*\|unless\|case\)\>,\<\%(for\|tablerow\)\>:\%({%\s*\)\@<=empty\>:\<end\%(for\|tablerow\)\>,<\(capture\|comment\|highlight\)\>:\<end\1\>'
+ let b:match_words .= '\<\%(if\w*\|unless\|case\)\>:\<\%(elsif\|else\|when\)\>:\<end\%(if\w*\|unless\|case\)\>,\<\%(for\|tablerow\)\>:\%({%\s*\)\@<=empty\>:\<end\%(for\|tablerow\)\>,\<\(capture\|comment\|highlight\)\>:\<end\1\>'
endif
setlocal commentstring={%\ comment\ %}%s{%\ endcomment\ %}
diff --git a/runtime/ftplugin/m3build.vim b/runtime/ftplugin/m3build.vim
index 9e1e169d38..c910eaecd0 100644
--- a/runtime/ftplugin/m3build.vim
+++ b/runtime/ftplugin/m3build.vim
@@ -1,21 +1,20 @@
" Vim filetype plugin file
" Language: Modula-3 Makefile
" Maintainer: Doug Kearns <dougkearns@gmail.com>
-" Last Change: 2021 April 15
+" Last Change: 2022 June 12
if exists("b:did_ftplugin")
finish
endif
-let b:did_ftplugin = 1
let s:cpo_save = &cpo
set cpo&vim
runtime! ftplugin/m3quake.vim
-if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter")
- let b:browsefilter = "Modula-3 Makefile (m3makefile m3overrides)\tm3makefile;m3overrides\n" ..
- \ "All Files (*.*)\t*.*\n"
+if (has("gui_win32") || has("gui_gtk")) && exists("b:m3quake_set_browsefilter")
+ let b:browsefilter = "Modula-3 Makefile (m3makefile m3overrides)\tm3makefile;m3overrides\n" ..
+ \ "All Files (*.*)\t*.*\n"
endif
let &cpo = s:cpo_save
diff --git a/runtime/ftplugin/m3quake.vim b/runtime/ftplugin/m3quake.vim
index 627aabdc3e..7762875db9 100644
--- a/runtime/ftplugin/m3quake.vim
+++ b/runtime/ftplugin/m3quake.vim
@@ -1,7 +1,7 @@
" Vim filetype plugin file
" Language: Modula-3 Quake
" Maintainer: Doug Kearns <dougkearns@gmail.com>
-" Last Change: 2021 April 15
+" Last Change: 2022 June 12
if exists("b:did_ftplugin")
finish
@@ -15,18 +15,20 @@ setlocal comments=s1:/*,mb:*,ex:*/,:%
setlocal commentstring=%\ %s
setlocal formatoptions-=t formatoptions+=croql
+let b:undo_ftplugin = "setl fo< com< cms<"
+
if exists("loaded_matchit") && !exists("b:match_words")
let b:match_words = '\<\%(proc\|if\|foreach\)\>:\<else\>:\<end\>'
+ let b:undo_ftplugin ..= " | unlet! b:match_words"
endif
if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter")
let b:browsefilter = "Modula-3 Quake Source Files (*.quake)\t*.quake\n" ..
\ "All Files (*.*)\t*.*\n"
+ let b:m3quake_set_browsefilter = 1
+ let b:undo_ftplugin ..= " | unlet! b:browsefilter b:m3quake_set_browsefilter"
endif
-let b:undo_ftplugin = "setl fo< com< cms< " ..
- \ "| unlet! b:browsefilter b:match_words"
-
let &cpo = s:cpo_save
unlet s:cpo_save
diff --git a/runtime/ftplugin/man.vim b/runtime/ftplugin/man.vim
index fce12012b5..d7a08a9941 100644
--- a/runtime/ftplugin/man.vim
+++ b/runtime/ftplugin/man.vim
@@ -24,7 +24,11 @@ if !exists('g:no_plugin_maps') && !exists('g:no_man_maps')
nnoremap <silent> <buffer> k gk
nnoremap <silent> <buffer> gO :call man#show_toc()<CR>
nnoremap <silent> <buffer> <2-LeftMouse> :Man<CR>
- nnoremap <silent> <buffer> <nowait> q :lclose<CR><C-W>c
+ if get(b:, 'pager')
+ nnoremap <silent> <buffer> <nowait> q :lclose<CR><C-W>q
+ else
+ nnoremap <silent> <buffer> <nowait> q :lclose<CR><C-W>c
+ endif
endif
if get(g:, 'ft_man_folding_enable', 0)
diff --git a/runtime/ftplugin/masm.vim b/runtime/ftplugin/masm.vim
index 235469ac32..ab196eb820 100644
--- a/runtime/ftplugin/masm.vim
+++ b/runtime/ftplugin/masm.vim
@@ -1,7 +1,7 @@
" Vim filetype plugin file
" Language: Microsoft Macro Assembler (80x86)
" Maintainer: Wu Yongwei <wuyongwei@gmail.com>
-" Last Change: 2020-05-09 23:02:05 +0800
+" Last Change: 2022-04-24 21:24:52 +0800
if exists("b:did_ftplugin")
finish
@@ -15,5 +15,18 @@ let b:undo_ftplugin = "setl iskeyword<"
setlocal iskeyword=@,48-57,_,36,60,62,63,@-@
+" Matchit support
+if !exists('b:match_words')
+ let b:match_words = '^\s*\.IF\>:^\s*\.ELSEIF\>:^\s*\.ELSE\>:^\s*\.ENDIF\>,'
+ \ .. '^\s*\.REPEAT\>:^\s*\.UNTIL\(CXZ\)\?\>,'
+ \ .. '^\s*\.WHILE\>:^\s*\.ENDW\>,'
+ \ .. '^\s*IF\(1\|2\|E\|DEF\|NDEF\|B\|NB\|IDNI\?\|DIFI\?\)\?\>:^\s*ELSEIF\(1\|2\|E\|DEF\|NDEF\|B\|NB\|IDNI\?\|DIFI\?\)\?\>:^\s*ELSE\>:^\s*ENDIF\>,'
+ \ .. '\(\<MACRO\>\|^\s*%\?\s*FORC\?\>\|^\s*REPEAT\>\|^\s*WHILE\):^\s*ENDM\>,'
+ \ .. '\<PROC\>:\<ENDP\>,'
+ \ .. '\<SEGMENT\>:\<ENDS\>'
+ let b:match_ignorecase = 1
+ let b:undo_ftplugin ..= " | unlet! b:match_ignorecase b:match_words"
+endif
+
let &cpo = s:cpo_save
unlet s:cpo_save
diff --git a/runtime/ftplugin/modula3.vim b/runtime/ftplugin/modula3.vim
index fc9eb57949..1f8ab359d5 100644
--- a/runtime/ftplugin/modula3.vim
+++ b/runtime/ftplugin/modula3.vim
@@ -1,7 +1,7 @@
" Vim filetype plugin file
" Language: Modula-3
" Maintainer: Doug Kearns <dougkearns@gmail.com>
-" Last Change: 2021 Apr 08
+" Last Change: 2022 June 12
if exists("b:did_ftplugin")
finish
@@ -14,24 +14,28 @@ set cpo&vim
setlocal comments=s0:(*,mb:\ ,ex:*)
setlocal commentstring=(*%s*)
setlocal formatoptions-=t formatoptions+=croql
+setlocal suffixesadd+=.m3
+setlocal formatprg=m3pp
+
+let b:undo_ftplugin = "setlocal com< cms< fo< fp< sua<"
if exists("loaded_matchit") && !exists("b:match_words")
let b:match_words = '\<REPEAT\>:\<UNTIL\>,' ..
- \ '\<\%(BEGIN\|CASE\|FOR\|IF\|LOCK\|LOOP\|TRY\|TYPECASE\|WHILE\|WITH\)\>' ..
+ \ '\<\%(BEGIN\|CASE\|FOR\|IF\|LOCK\|LOOP\|TRY\|TYPECASE\|WHILE\|WITH\|RECORD\|OBJECT\)\>' ..
\ ':' ..
- \ '\<\%(ELSIF\|ELSE\|EXCEPT\|FINALLY\)\>\|\%(^\s*\)\@<=\S.*=>' ..
+ \ '\<\%(ELSIF\|ELSE\|EXCEPT\|FINALLY\|METHODS\|OVERRIDES\)\>\|\%(^\s*\)\@<=\S.*=>' ..
\ ':' ..
- \ '\<END\>'
+ \ '\<END\>,' ..
+ \ '(\*:\*),<\*:\*>'
+ let b:undo_ftplugin ..= " | unlet! b:match_words"
endif
if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter")
let b:browsefilter = "Modula-3 Source Files (*.m3)\t*.m3\n" ..
\ "All Files (*.*)\t*.*\n"
+ let b:undo_ftplugin ..= " | unlet! b:browsefilter"
endif
-let b:undo_ftplugin = "setl com< cms< fo< " ..
- \ "| unlet! b:browsefilter b:match_words"
-
let &cpo = s:cpo_save
unlet s:cpo_save
diff --git a/runtime/ftplugin/pascal.vim b/runtime/ftplugin/pascal.vim
index 2de92563ae..aba1e54f27 100644
--- a/runtime/ftplugin/pascal.vim
+++ b/runtime/ftplugin/pascal.vim
@@ -1,7 +1,7 @@
" Vim filetype plugin file
" Language: Pascal
" Maintainer: Doug Kearns <dougkearns@gmail.com>
-" Previous Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net>
+" Previous Maintainer: Dan Sharp
" Last Change: 2021 Apr 23
if exists("b:did_ftplugin") | finish | endif
diff --git a/runtime/ftplugin/perl.vim b/runtime/ftplugin/perl.vim
index 603ba49502..d0bdbc0cfb 100644
--- a/runtime/ftplugin/perl.vim
+++ b/runtime/ftplugin/perl.vim
@@ -3,7 +3,8 @@
" Maintainer: vim-perl <vim-perl@googlegroups.com>
" Homepage: https://github.com/vim-perl/vim-perl
" Bugs/requests: https://github.com/vim-perl/vim-perl/issues
-" Last Change: 2020 Apr 15
+" License: Vim License (see :help license)
+" Last Change: 2021 Nov 10
if exists("b:did_ftplugin") | finish | endif
let b:did_ftplugin = 1
@@ -20,27 +21,36 @@ setlocal keywordprg=perldoc\ -f
setlocal comments=:#
setlocal commentstring=#%s
-" Change the browse dialog on Win32 to show mainly Perl-related files
-if has("gui_win32")
- let b:browsefilter = "Perl Source Files (*.pl)\t*.pl\n" .
- \ "Perl Modules (*.pm)\t*.pm\n" .
- \ "Perl Documentation Files (*.pod)\t*.pod\n" .
- \ "All Files (*.*)\t*.*\n"
-endif
-
" Provided by Ned Konz <ned at bike-nomad dot com>
"---------------------------------------------
setlocal include=\\<\\(use\\\|require\\)\\>
-setlocal includeexpr=substitute(substitute(substitute(v:fname,'::','/','g'),'->\*','',''),'$','.pm','')
+" '+' is removed to support plugins in Catalyst or DBIx::Class
+" where the leading plus indicates a fully-qualified module name.
+setlocal includeexpr=substitute(substitute(substitute(substitute(v:fname,'+','',''),'::','/','g'),'->\*','',''),'$','.pm','')
setlocal define=[^A-Za-z_]
setlocal iskeyword+=:
" The following line changes a global variable but is necessary to make
" gf and similar commands work. Thanks to Andrew Pimlott for pointing
-" out the problem. If this causes a problem for you, add an
-" after/ftplugin/perl.vim file that contains
-" set isfname-=:
+" out the problem.
+let s:old_isfname = &isfname
set isfname+=:
+let s:new_isfname = &isfname
+
+augroup perl_global_options
+ au!
+ exe "au BufEnter * if &filetype == 'perl' | let &isfname = '" . s:new_isfname . "' | endif"
+ exe "au BufLeave * if &filetype == 'perl' | let &isfname = '" . s:old_isfname . "' | endif"
+augroup END
+
+" Undo the stuff we changed.
+let b:undo_ftplugin = "setlocal fo< kp< com< cms< inc< inex< def< isk<" .
+ \ " | let &isfname = '" . s:old_isfname . "'"
+
+if get(g:, 'perl_fold', 0)
+ setlocal foldmethod=syntax
+ let b:undo_ftplugin .= " | setlocal fdm<"
+endif
" Set this once, globally.
if !exists("perlpath")
@@ -74,16 +84,26 @@ if &l:path == ""
else
let &l:path=&l:path.",".perlpath
endif
+
+let b:undo_ftplugin .= " | setlocal pa<"
"---------------------------------------------
-" Undo the stuff we changed.
-let b:undo_ftplugin = "setlocal fo< com< cms< inc< inex< def< isk< isf< kp< path<" .
- \ " | unlet! b:browsefilter"
+" Change the browse dialog to show mainly Perl-related files
+if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter")
+ let b:browsefilter = "Perl Source Files (*.pl)\t*.pl\n" .
+ \ "Perl Modules (*.pm)\t*.pm\n" .
+ \ "Perl Documentation Files (*.pod)\t*.pod\n" .
+ \ "All Files (*.*)\t*.*\n"
+ let b:undo_ftplugin .= " | unlet! b:browsefilter"
+endif
-" proper matching for matchit plugin
-let b:match_skip = 's:comment\|string\|perlQQ\|perlShellCommand\|perlHereDoc\|perlSubstitution\|perlTranslation\|perlMatch\|perlFormatField'
-let b:match_words = '\<if\>:\<elsif\>:\<else\>'
+" Proper matching for matchit plugin
+if exists("loaded_matchit") && !exists("b:match_words")
+ let b:match_skip = 's:comment\|string\|perlQQ\|perlShellCommand\|perlHereDoc\|perlSubstitution\|perlTranslation\|perlMatch\|perlFormatField'
+ let b:match_words = '\<if\>:\<elsif\>:\<else\>'
+ let b:undo_ftplugin .= " | unlet! b:match_words b:match_skip"
+endif
" Restore the saved compatibility options.
let &cpo = s:save_cpo
-unlet s:save_cpo
+unlet s:save_cpo s:old_isfname s:new_isfname
diff --git a/runtime/ftplugin/php.vim b/runtime/ftplugin/php.vim
index a2f8b4d8d3..2824a5853b 100644
--- a/runtime/ftplugin/php.vim
+++ b/runtime/ftplugin/php.vim
@@ -1,8 +1,10 @@
" Vim filetype plugin file
" Language: php
-" Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net>
+"
+" This runtime file is looking for a new maintainer.
+"
+" Former maintainer: Dan Sharp
" Last Changed: 20 Jan 2009
-" URL: http://dwsharp.users.sourceforge.net/vim/ftplugin
if exists("b:did_ftplugin") | finish | endif
@@ -71,10 +73,11 @@ exe 'nno <buffer> <silent> ]] /' . escape(s:section, '|') . '/<CR>:nohls<CR>'
exe 'ono <buffer> <silent> [[ ?' . escape(s:section, '|') . '?<CR>:nohls<CR>'
exe 'ono <buffer> <silent> ]] /' . escape(s:section, '|') . '/<CR>:nohls<CR>'
+setlocal suffixesadd=.php
setlocal commentstring=/*%s*/
" Undo the stuff we changed.
-let b:undo_ftplugin = "setlocal commentstring< include< omnifunc<" .
+let b:undo_ftplugin = "setlocal suffixesadd< commentstring< include< omnifunc<" .
\ " | unlet! b:browsefilter b:match_words | " .
\ s:undo_ftplugin
diff --git a/runtime/ftplugin/pod.vim b/runtime/ftplugin/pod.vim
new file mode 100644
index 0000000000..2a905ab354
--- /dev/null
+++ b/runtime/ftplugin/pod.vim
@@ -0,0 +1,71 @@
+" Vim filetype plugin file
+" Language: Perl POD format
+" Maintainer: vim-perl <vim-perl@googlegroups.com>
+" Author: Doug Kearns <dougkearns@gmail.com>
+" Homepage: https://github.com/vim-perl/vim-perl
+" Bugs/requests: https://github.com/vim-perl/vim-perl/issues
+" License: Vim License (see :help license)
+" Last Change: 2021 Oct 19
+
+if exists("b:did_ftplugin")
+ finish
+endif
+
+let s:save_cpo = &cpo
+set cpo-=C
+
+setlocal comments=fb:=for\ comment
+setlocal commentstring==for\ comment\ %s
+
+let b:undo_ftplugin = "setl com< cms<"
+
+if exists("loaded_matchit") && !exists("b:match_words")
+ let b:match_words =
+ \ '^=pod\>:^=cut\>,' .
+ \ '^=begin\s\+\(\S\+\):^=end\s\+\1,' .
+ \ '^=over\>:^=item\>:^=back\>,' .
+ \ '[IBCLEFSXZ]<<\%(\s\+\|$\)\@=:\%(\s\+\|^\)\@<=>>,' .
+ \ '[IBCLEFSXZ]<:>'
+ let b:undo_ftplugin .= " | unlet! b:match_words"
+endif
+
+if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter")
+ let b:browsefilter = "POD Source Files (*.pod)\t*.pod\n" .
+ \ "Perl Source Files (*.pl)\t*.pl\n" .
+ \ "Perl Modules (*.pm)\t*.pm\n" .
+ \ "All Files (*.*)\t*.*\n"
+ let b:undo_ftplugin .= " | unlet! b:browsefilter"
+endif
+
+function! s:jumpToSection(backwards)
+ let flags = a:backwards ? 'bsWz' : 'sWz'
+ if has('syntax_items')
+ let skip = "synIDattr(synID(line('.'), col('.'), 1), 'name') !~# '\\<podCommand\\>'"
+ else
+ let skip = ''
+ endif
+ for i in range(v:count1)
+ call search('^=\a', flags, 0, 0, skip)
+ endfor
+endfunction
+
+if !exists("no_plugin_maps") && !exists("no_pod_maps")
+ nnoremap <silent> <buffer> ]] <Cmd>call <SID>jumpToSection()<CR>
+ vnoremap <silent> <buffer> ]] <Cmd>call <SID>jumpToSection()<CR>
+ nnoremap <silent> <buffer> ][ <Cmd>call <SID>jumpToSection()<CR>
+ vnoremap <silent> <buffer> ][ <Cmd>call <SID>jumpToSection()<CR>
+ nnoremap <silent> <buffer> [[ <Cmd>call <SID>jumpToSection(1)<CR>
+ vnoremap <silent> <buffer> [[ <Cmd>call <SID>jumpToSection(1)<CR>
+ nnoremap <silent> <buffer> [] <Cmd>call <SID>jumpToSection(1)<CR>
+ vnoremap <silent> <buffer> [] <Cmd>call <SID>jumpToSection(1)<CR>
+ let b:undo_ftplugin .=
+ \ " | silent! exe 'nunmap <buffer> ]]' | silent! exe 'vunmap <buffer> ]]'" .
+ \ " | silent! exe 'nunmap <buffer> ][' | silent! exe 'vunmap <buffer> ]['" .
+ \ " | silent! exe 'nunmap <buffer> ]]' | silent! exe 'vunmap <buffer> ]]'" .
+ \ " | silent! exe 'nunmap <buffer> []' | silent! exe 'vunmap <buffer> []'"
+endif
+
+let &cpo = s:save_cpo
+unlet s:save_cpo
+
+" vim: set expandtab:
diff --git a/runtime/ftplugin/qb64.vim b/runtime/ftplugin/qb64.vim
new file mode 100644
index 0000000000..0fa36fc3d2
--- /dev/null
+++ b/runtime/ftplugin/qb64.vim
@@ -0,0 +1,26 @@
+" Vim filetype plugin file
+" Language: QB64
+" Maintainer: Doug Kearns <dougkearns@gmail.com>
+
+if exists("b:did_ftplugin")
+ finish
+endif
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+runtime! ftplugin/basic.vim
+
+let s:not_end = '\%(end\s\+\)\@<!'
+
+let b:match_words ..= ',' ..
+ \ s:not_end .. '\<declare\>:\<end\s\+declare\>,' ..
+ \ '\<select\s\+everycase\>:\%(select\s\+\)\@<!\<case\%(\s\+\%(else\|is\)\)\=\>:\<end\s\+select\>,' ..
+ \ '$IF\>:$\%(ELSEIF\|ELSE\)\>:$END\s*IF\>'
+
+unlet s:not_end
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
+
+" vim: nowrap sw=2 sts=2 ts=8 noet fdm=marker:
diff --git a/runtime/ftplugin/query.lua b/runtime/ftplugin/query.lua
new file mode 100644
index 0000000000..3b99d67247
--- /dev/null
+++ b/runtime/ftplugin/query.lua
@@ -0,0 +1,6 @@
+-- Neovim filetype plugin file
+-- Language: Tree-sitter query
+-- Last Change: 2022 Mar 29
+
+-- it's a lisp!
+vim.cmd([[ runtime! ftplugin/lisp.vim ]])
diff --git a/runtime/ftplugin/reva.vim b/runtime/ftplugin/reva.vim
index 05c8bc3250..29ecab0d0a 100644
--- a/runtime/ftplugin/reva.vim
+++ b/runtime/ftplugin/reva.vim
@@ -1,7 +1,7 @@
" Vim ftplugin file
" Language: Reva Forth
" Version: 7.1
-" Last Change: 2008/01/11
+" Last Change: 2022 Apr 27
" Maintainer: Ron Aaron <ron@ronware.org>
" URL: http://ronware.org/reva/
" Filetypes: *.rf *.frt
@@ -23,3 +23,5 @@ setlocal com=s1:/*,mb:*,ex:*/,:\|,:\\
setlocal fo=tcrqol
setlocal matchpairs+=\::;
setlocal iskeyword=!,@,33-35,%,$,38-64,A-Z,91-96,a-z,123-126,128-255
+
+let b:undo_ftplugin = "setlocal sts< sw< com< fo< mps< isk<"
diff --git a/runtime/ftplugin/ruby.vim b/runtime/ftplugin/ruby.vim
index 4a476fd8cf..8c1f47731c 100644
--- a/runtime/ftplugin/ruby.vim
+++ b/runtime/ftplugin/ruby.vim
@@ -3,7 +3,7 @@
" Maintainer: Tim Pope <vimNOSPAM@tpope.org>
" URL: https://github.com/vim-ruby/vim-ruby
" Release Coordinator: Doug Kearns <dougkearns@gmail.com>
-" Last Change: 2020 Feb 13
+" Last Change: 2022 Mar 21
if (exists("b:did_ftplugin"))
finish
@@ -53,7 +53,7 @@ endif
" TODO:
"setlocal define=^\\s*def
-setlocal comments=:#
+setlocal comments=b:#
setlocal commentstring=#\ %s
if !exists('g:ruby_version_paths')
@@ -87,8 +87,14 @@ endfunction
function! s:build_path(path) abort
let path = join(map(copy(a:path), 'v:val ==# "." ? "" : v:val'), ',')
- if &g:path !~# '\v^%(\.,)=%(/%(usr|emx)/include,)=,$'
- let path = substitute(&g:path,',,$',',','') . ',' . path
+ if &g:path =~# '\v^%(\.,)=%(/%(usr|emx)/include,)=,$'
+ let path = path . ',.,,'
+ elseif &g:path =~# ',\.,,$'
+ let path = &g:path[0:-4] . path . ',.,,'
+ elseif &g:path =~# ',,$'
+ let path = &g:path[0:-2] . path . ',,'
+ else
+ let path = substitute(&g:path, '[^,]\zs$', ',', '') . path
endif
return path
endfunction
@@ -164,6 +170,8 @@ let b:undo_ftplugin .= "| sil! cunmap <buffer> <Plug><ctag>| sil! cunmap <buffer
if !exists("g:no_plugin_maps") && !exists("g:no_ruby_maps")
nmap <buffer><script> <SID>: :<C-U>
nmap <buffer><script> <SID>c: :<C-U><C-R>=v:count ? v:count : ''<CR>
+ cmap <buffer> <SID><cfile> <Plug><cfile>
+ cmap <buffer> <SID><ctag> <Plug><ctag>
nnoremap <silent> <buffer> [m :<C-U>call <SID>searchsyn('\<def\>',['rubyDefine'],'b','n')<CR>
nnoremap <silent> <buffer> ]m :<C-U>call <SID>searchsyn('\<def\>',['rubyDefine'],'','n')<CR>
@@ -210,20 +218,20 @@ if !exists("g:no_plugin_maps") && !exists("g:no_ruby_maps")
call s:map('c', '', '<C-R><C-F> <Plug><cfile>')
cmap <buffer><script><expr> <SID>tagzv &foldopen =~# 'tag' ? '<Bar>norm! zv' : ''
- call s:map('n', '<silent>', '<C-]> <SID>:exe v:count1."tag <Plug><ctag>"<SID>tagzv<CR>')
- call s:map('n', '<silent>', 'g<C-]> <SID>:exe "tjump <Plug><ctag>"<SID>tagzv<CR>')
- call s:map('n', '<silent>', 'g] <SID>:exe "tselect <Plug><ctag>"<SID>tagzv<CR>')
- call s:map('n', '<silent>', '<C-W>] <SID>:exe v:count1."stag <Plug><ctag>"<SID>tagzv<CR>')
- call s:map('n', '<silent>', '<C-W><C-]> <SID>:exe v:count1."stag <Plug><ctag>"<SID>tagzv<CR>')
- call s:map('n', '<silent>', '<C-W>g<C-]> <SID>:exe "stjump <Plug><ctag>"<SID>tagzv<CR>')
- call s:map('n', '<silent>', '<C-W>g] <SID>:exe "stselect <Plug><ctag>"<SID>tagzv<CR>')
- call s:map('n', '<silent>', '<C-W>} <SID>:exe v:count1."ptag <Plug><ctag>"<CR>')
- call s:map('n', '<silent>', '<C-W>g} <SID>:exe "ptjump <Plug><ctag>"<CR>')
-
- call s:map('n', '<silent>', 'gf <SID>c:find <Plug><cfile><CR>')
- call s:map('n', '<silent>', '<C-W>f <SID>c:sfind <Plug><cfile><CR>')
- call s:map('n', '<silent>', '<C-W><C-F> <SID>c:sfind <Plug><cfile><CR>')
- call s:map('n', '<silent>', '<C-W>gf <SID>c:tabfind <Plug><cfile><CR>')
+ call s:map('n', '<script><silent>', '<C-]> <SID>:exe v:count1."tag <SID><ctag>"<SID>tagzv<CR>')
+ call s:map('n', '<script><silent>', 'g<C-]> <SID>:exe "tjump <SID><ctag>"<SID>tagzv<CR>')
+ call s:map('n', '<script><silent>', 'g] <SID>:exe "tselect <SID><ctag>"<SID>tagzv<CR>')
+ call s:map('n', '<script><silent>', '<C-W>] <SID>:exe v:count1."stag <SID><ctag>"<SID>tagzv<CR>')
+ call s:map('n', '<script><silent>', '<C-W><C-]> <SID>:exe v:count1."stag <SID><ctag>"<SID>tagzv<CR>')
+ call s:map('n', '<script><silent>', '<C-W>g<C-]> <SID>:exe "stjump <SID><ctag>"<SID>tagzv<CR>')
+ call s:map('n', '<script><silent>', '<C-W>g] <SID>:exe "stselect <SID><ctag>"<SID>tagzv<CR>')
+ call s:map('n', '<script><silent>', '<C-W>} <SID>:exe v:count1."ptag <SID><ctag>"<CR>')
+ call s:map('n', '<script><silent>', '<C-W>g} <SID>:exe "ptjump <SID><ctag>"<CR>')
+
+ call s:map('n', '<script><silent>', 'gf <SID>c:find <SID><cfile><CR>')
+ call s:map('n', '<script><silent>', '<C-W>f <SID>c:sfind <SID><cfile><CR>')
+ call s:map('n', '<script><silent>', '<C-W><C-F> <SID>c:sfind <SID><cfile><CR>')
+ call s:map('n', '<script><silent>', '<C-W>gf <SID>c:tabfind <SID><cfile><CR>')
endif
let &cpo = s:cpo_save
diff --git a/runtime/ftplugin/scdoc.vim b/runtime/ftplugin/scdoc.vim
index 2e98e647f4..552c865baa 100644
--- a/runtime/ftplugin/scdoc.vim
+++ b/runtime/ftplugin/scdoc.vim
@@ -1,6 +1,7 @@
" scdoc filetype plugin
-" Maintainer: Gregory Anders <greg@gpanders.com>
-" Last Updated: 2021-08-04
+" Maintainer: Gregory Anders <contact@gpanders.com>
+" Last Updated: 2022-05-09
+" Upstream: https://github.com/gpanders/vim-scdoc
" Only do this when not done yet for this buffer
if exists('b:did_ftplugin')
@@ -19,8 +20,3 @@ setlocal softtabstop=0
setlocal textwidth=80
let b:undo_ftplugin = 'setl com< cms< fo< et< sw< sts< tw<'
-
-if has('conceal')
- setlocal conceallevel=2
- let b:undo_ftplugin .= ' cole<'
-endif
diff --git a/runtime/ftplugin/sgml.vim b/runtime/ftplugin/sgml.vim
index bf63efbf1f..ef52125c68 100644
--- a/runtime/ftplugin/sgml.vim
+++ b/runtime/ftplugin/sgml.vim
@@ -1,8 +1,10 @@
" Vim filetype plugin file
" Language: sgml
-" Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net>
+"
+" This runtime file is looking for a new maintainer.
+"
+" Former maintainer: Dan Sharp
" Last Changed: 20 Jan 2009
-" URL: http://dwsharp.users.sourceforge.net/vim/ftplugin
if exists("b:did_ftplugin") | finish | endif
diff --git a/runtime/ftplugin/sh.vim b/runtime/ftplugin/sh.vim
index 593fcec927..93a46f63e2 100644
--- a/runtime/ftplugin/sh.vim
+++ b/runtime/ftplugin/sh.vim
@@ -1,8 +1,10 @@
" Vim filetype plugin file
" Language: sh
-" Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net>
+"
+" This runtime file is looking for a new maintainer.
+"
+" Former maintainer: Dan Sharp
" Last Changed: 20 Jan 2009
-" URL: http://dwsharp.users.sourceforge.net/vim/ftplugin
if exists("b:did_ftplugin") | finish | endif
let b:did_ftplugin = 1
diff --git a/runtime/ftplugin/spec.vim b/runtime/ftplugin/spec.vim
index ce00021a69..9040e19ce1 100644
--- a/runtime/ftplugin/spec.vim
+++ b/runtime/ftplugin/spec.vim
@@ -3,6 +3,7 @@
" Maintainer: Igor Gnatenko i.gnatenko.brain@gmail.com
" Former Maintainer: Gustavo Niemeyer <niemeyer@conectiva.com> (until March 2014)
" Last Change: Mon Jun 01 21:15 MSK 2015 Igor Gnatenko
+" Update by Zdenek Dohnal, 2022 May 17
if exists("b:did_ftplugin")
finish
@@ -41,8 +42,8 @@ else:
headers = spec.sourceHeader
version = headers["Version"]
release = headers["Release"]
- vim.command("let ver = " + version)
- vim.command("let rel = " + release)
+ vim.command("let ver = '" + version + "'")
+ vim.command("let rel = '" + release + "'")
PYEND
endif
endfunction
diff --git a/runtime/ftplugin/svg.vim b/runtime/ftplugin/svg.vim
index 8fff6ea32c..6f16b1a0f4 100644
--- a/runtime/ftplugin/svg.vim
+++ b/runtime/ftplugin/svg.vim
@@ -1,8 +1,10 @@
" Vim filetype plugin file
" Language: svg
-" Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net>
+"
+" This runtime file is looking for a new maintainer.
+"
+" Former maintainer: Dan Sharp
" Last Changed: 20 Jan 2009
-" URL: http://dwsharp.users.sourceforge.net/vim/ftplugin
if exists("b:did_ftplugin") | finish | endif
diff --git a/runtime/ftplugin/tap.vim b/runtime/ftplugin/tap.vim
new file mode 100644
index 0000000000..c165c0ce68
--- /dev/null
+++ b/runtime/ftplugin/tap.vim
@@ -0,0 +1,27 @@
+" Vim filetype plugin file
+" Language: Verbose TAP Output
+" Maintainer: vim-perl <vim-perl@googlegroups.com>
+" Homepage: https://github.com/vim-perl/vim-perl
+" Bugs/requests: https://github.com/vim-perl/vim-perl/issues
+" License: Vim License (see :help license)
+" Last Change: 2021 Oct 22
+
+" Only do this when not done yet for this buffer
+if exists('b:did_ftplugin')
+ finish
+endif
+let b:did_ftplugin = 1
+
+setlocal foldtext=TAPTestLine_foldtext()
+function! TAPTestLine_foldtext()
+ let line = getline(v:foldstart)
+ let sub = substitute(line, '/\*\|\*/\|{{{\d\=', '', 'g')
+ return sub
+endfunction
+
+setlocal foldminlines=5
+setlocal foldcolumn=2
+setlocal foldenable
+setlocal foldmethod=syntax
+
+let b:undo_ftplugin = 'setlocal foldtext< foldminlines< foldcolumn< foldenable< foldmethod<'
diff --git a/runtime/ftplugin/tcsh.vim b/runtime/ftplugin/tcsh.vim
index 33f1aabf68..85d3873b33 100644
--- a/runtime/ftplugin/tcsh.vim
+++ b/runtime/ftplugin/tcsh.vim
@@ -1,7 +1,7 @@
" Vim filetype plugin file
" Language: tcsh
" Maintainer: Doug Kearns <dougkearns@gmail.com>
-" Previous Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net>
+" Previous Maintainer: Dan Sharp
" Last Change: 2021 Oct 15
if exists("b:did_ftplugin") | finish | endif
diff --git a/runtime/ftplugin/tt2html.vim b/runtime/ftplugin/tt2html.vim
index 10520d8064..0407dbba4e 100644
--- a/runtime/ftplugin/tt2html.vim
+++ b/runtime/ftplugin/tt2html.vim
@@ -1,8 +1,10 @@
+" Vim filetype plugin file
" Language: TT2 embedded with HTML
" Maintainer: vim-perl <vim-perl@googlegroups.com>
-" Homepage: http://github.com/vim-perl/vim-perl
-" Bugs/requests: http://github.com/vim-perl/vim-perl/issues
-" Last Change: 2013-07-21
+" Homepage: https://github.com/vim-perl/vim-perl
+" Bugs/requests: https://github.com/vim-perl/vim-perl/issues
+" License: Vim License (see :help license)
+" Last Change: 2018 Mar 28
" Only do this when not done yet for this buffer
if exists("b:did_ftplugin")
diff --git a/runtime/ftplugin/vue.vim b/runtime/ftplugin/vue.vim
new file mode 100644
index 0000000000..e8833742ad
--- /dev/null
+++ b/runtime/ftplugin/vue.vim
@@ -0,0 +1,24 @@
+" Vim filetype plugin file
+" Language: vue
+
+if exists("b:did_ftplugin") | finish | endif
+let b:did_ftplugin = 1
+
+" Make sure the continuation lines below do not cause problems in
+" compatibility mode.
+let s:save_cpo = &cpo
+set cpo-=C
+
+" Copied from ftplugin/html.vim
+" Original thanks to Johannes Zellner and Benji Fisher.
+if exists("loaded_matchit")
+ let b:match_ignorecase = 1
+ let b:match_words = '<:>,'
+ \ .. '<\@<=[ou]l\>[^>]*\%(>\|$\):<\@<=li\>:<\@<=/[ou]l>,'
+ \ .. '<\@<=dl\>[^>]*\%(>\|$\):<\@<=d[td]\>:<\@<=/dl>,'
+ \ .. '<\@<=\([^/][^ \t>]*\)[^>]*\%(>\|$\):<\@<=/\1>'
+endif
+
+" Restore the saved compatibility options.
+let &cpo = s:save_cpo
+unlet s:save_cpo
diff --git a/runtime/ftplugin/wget.vim b/runtime/ftplugin/wget.vim
new file mode 100644
index 0000000000..7a10221824
--- /dev/null
+++ b/runtime/ftplugin/wget.vim
@@ -0,0 +1,29 @@
+" Vim filetype plugin file
+" Language: Wget configuration file (/etc/wgetrc ~/.wgetrc)
+" Maintainer: Doug Kearns <dougkearns@gmail.com>
+" Last Change: 2022 Apr 28
+
+if exists("b:did_ftplugin")
+ finish
+endif
+let b:did_ftplugin = 1
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+setlocal comments=:#,://
+setlocal commentstring=#\ %s
+setlocal formatoptions-=t formatoptions+=croql
+
+let b:undo_ftplugin = "setl fo< com< cms<"
+
+if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter")
+ let b:browsefilter = "Wget Configuration File (wgetrc, .wgetrc)\twgetrc;.wgetrc\n" .
+ \ "All Files (*.*)\t*.*\n"
+ let b:undo_ftplugin ..= " | unlet! b:browsefilter"
+endif
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
+
+" vim: nowrap sw=2 sts=2 ts=8
diff --git a/runtime/ftplugin/wget2.vim b/runtime/ftplugin/wget2.vim
new file mode 100644
index 0000000000..a6845f6df5
--- /dev/null
+++ b/runtime/ftplugin/wget2.vim
@@ -0,0 +1,29 @@
+" Vim filetype plugin file
+" Language: Wget2 configuration file (/etc/wget2rc ~/.wget2rc)
+" Maintainer: Doug Kearns <dougkearns@gmail.com>
+" Last Change: 2022 Apr 28
+
+if exists("b:did_ftplugin")
+ finish
+endif
+let b:did_ftplugin = 1
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+setlocal comments=:#,://
+setlocal commentstring=#\ %s
+setlocal formatoptions-=t formatoptions+=croql
+
+let b:undo_ftplugin = "setl fo< com< cms<"
+
+if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter")
+ let b:browsefilter = "Wget2 Configuration File (wget2rc, .wget2rc)\twget2rc;.wget2rc\n" .
+ \ "All Files (*.*)\t*.*\n"
+ let b:undo_ftplugin ..= " | unlet! b:browsefilter"
+endif
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
+
+" vim: nowrap sw=2 sts=2 ts=8
diff --git a/runtime/ftplugin/xhtml.vim b/runtime/ftplugin/xhtml.vim
index 21ed3e1100..d2a1c0b566 100644
--- a/runtime/ftplugin/xhtml.vim
+++ b/runtime/ftplugin/xhtml.vim
@@ -1,8 +1,10 @@
" Vim filetype plugin file
" Language: xhtml
-" Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net>
+"
+" This runtime file is looking for a new maintainer.
+"
+" Former maintainer: Dan Sharp
" Last Changed: 20 Jan 2009
-" URL: http://dwsharp.users.sourceforge.net/vim/ftplugin
if exists("b:did_ftplugin") | finish | endif
diff --git a/runtime/ftplugin/xml.vim b/runtime/ftplugin/xml.vim
index 1d43521155..9aa188cecc 100644
--- a/runtime/ftplugin/xml.vim
+++ b/runtime/ftplugin/xml.vim
@@ -3,7 +3,7 @@
" Maintainer: Christian Brabandt <cb@256bit.org>
" Last Changed: Dec 07th, 2018
" Repository: https://github.com/chrisbra/vim-xml-ftplugin
-" Previous Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net>
+" Previous Maintainer: Dan Sharp
" URL: http://dwsharp.users.sourceforge.net/vim/ftplugin
if exists("b:did_ftplugin") | finish | endif
diff --git a/runtime/ftplugin/xs.vim b/runtime/ftplugin/xs.vim
index a28da6aa1e..206fbc2713 100644
--- a/runtime/ftplugin/xs.vim
+++ b/runtime/ftplugin/xs.vim
@@ -1,9 +1,10 @@
" Vim filetype plugin file
" Language: XS (Perl extension interface language)
" Maintainer: vim-perl <vim-perl@googlegroups.com>
-" Homepage: http://github.com/vim-perl/vim-perl
-" Bugs/requests: http://github.com/vim-perl/vim-perl/issues
-" Last Change: 2013-07-21
+" Homepage: https://github.com/vim-perl/vim-perl
+" Bugs/requests: https://github.com/vim-perl/vim-perl/issues
+" License: Vim License (see :help license)
+" Last Change: 2018 Mar 28
" Only do this when not done yet for this buffer
if exists("b:did_ftplugin")
diff --git a/runtime/ftplugin/xsd.vim b/runtime/ftplugin/xsd.vim
index 6a4a193656..7d3efbb390 100644
--- a/runtime/ftplugin/xsd.vim
+++ b/runtime/ftplugin/xsd.vim
@@ -1,8 +1,10 @@
" Vim filetype plugin file
" Language: xsd
-" Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net>
+"
+" This runtime file is looking for a new maintainer.
+"
+" Former maintainer: Dan Sharp
" Last Changed: 20 Jan 2009
-" URL: http://dwsharp.users.sourceforge.net/vim/ftplugin
if exists("b:did_ftplugin") | finish | endif
diff --git a/runtime/ftplugin/xslt.vim b/runtime/ftplugin/xslt.vim
index 1a5ee62865..47e78eb81d 100644
--- a/runtime/ftplugin/xslt.vim
+++ b/runtime/ftplugin/xslt.vim
@@ -1,16 +1,18 @@
" Vim filetype plugin file
-" Language: xslt
-" Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net>
-" Last Changed: 20 Jan 2009
-" URL: http://dwsharp.users.sourceforge.net/vim/ftplugin
+" Language: XSLT
+" Maintainer: Doug Kearns <dougkearns@gmail.com>
+" Previous Maintainer: Dan Sharp
+" Last Change: 2022 Apr 25
-if exists("b:did_ftplugin") | finish | endif
+if exists("b:did_ftplugin")
+ finish
+endif
runtime! ftplugin/xml.vim ftplugin/xml_*.vim ftplugin/xml/*.vim
let b:did_ftplugin = 1
" Change the :browse e filter to primarily show xsd-related files.
-if has("gui_win32") && exists("b:browsefilter")
- let b:browsefilter="XSLT Files (*.xsl,*.xslt)\t*.xsl;*.xslt\n" . b:browsefilter
+if (has("gui_win32") || has("gui_gtk")) && exists("b:browsefilter")
+ let b:browsefilter = "XSLT Files (*.xsl,*.xslt)\t*.xsl;*.xslt\n" . b:browsefilter
endif
diff --git a/runtime/indent/README.txt b/runtime/indent/README.txt
index 8b114365c6..649870636e 100644
--- a/runtime/indent/README.txt
+++ b/runtime/indent/README.txt
@@ -31,7 +31,7 @@ Always use ":setlocal" to set 'indentexpr'. This avoids it being carried over
to other buffers.
To trigger the indenting after typing a word like "endif", add the word to the
-'cinkeys' option with "+=".
+'indentkeys' option with "+=".
You normally set 'indentexpr' to evaluate a function and then define that
function. That function only needs to be defined once for as long as Vim is
diff --git a/runtime/indent/ada.vim b/runtime/indent/ada.vim
index 6c8ab05267..582d033b23 100644
--- a/runtime/indent/ada.vim
+++ b/runtime/indent/ada.vim
@@ -16,6 +16,7 @@
" 15.10.2006 MK Bram's suggestion for runtime integration
" 05.11.2006 MK Bram suggested to save on spaces
" 19.09.2007 NO g: missing before ada#Comment
+" 2022 April: b:undo_indent added by Doug Kearns
" Help Page: ft-vim-indent
"------------------------------------------------------------------------------
" ToDo:
@@ -35,6 +36,8 @@ setlocal indentexpr=GetAdaIndent()
setlocal indentkeys-=0{,0}
setlocal indentkeys+=0=~then,0=~end,0=~elsif,0=~when,0=~exception,0=~begin,0=~is,0=~record
+let b:undo_indent = "setl inde< indk<"
+
" Only define the functions once.
if exists("*GetAdaIndent")
finish
diff --git a/runtime/indent/awk.vim b/runtime/indent/awk.vim
index e65331977c..cf8132241c 100644
--- a/runtime/indent/awk.vim
+++ b/runtime/indent/awk.vim
@@ -24,6 +24,7 @@
" 29-04-2002 Fixed problems in function headers and max line width
" Added support for two-line if's without curly braces
" Fixed hang: 2011 Aug 31
+" 2022 April: b:undo_indent added by Doug Kearns
" Only load this indent file when no other was loaded.
if exists("b:did_indent")
@@ -36,6 +37,8 @@ setlocal indentexpr=GetAwkIndent()
" Mmm, copied from the tcl indent program. Is this okay?
setlocal indentkeys-=:,0#
+let b:undo_indent = "setl inde< indk<"
+
" Only define the function once.
if exists("*GetAwkIndent")
finish
diff --git a/runtime/indent/basic.vim b/runtime/indent/basic.vim
new file mode 100644
index 0000000000..7228772251
--- /dev/null
+++ b/runtime/indent/basic.vim
@@ -0,0 +1,11 @@
+" Vim indent file
+" Language: BASIC (QuickBASIC 4.5)
+" Maintainer: Doug Kearns <dougkearns@gmail.com>
+" Last Change: 2022 Jan 24
+
+" Only load this indent file when no other was loaded.
+if exists("b:did_indent")
+ finish
+endif
+
+runtime! indent/vb.vim
diff --git a/runtime/indent/bst.vim b/runtime/indent/bst.vim
index 47e3058810..3dd8d711a8 100644
--- a/runtime/indent/bst.vim
+++ b/runtime/indent/bst.vim
@@ -1,20 +1,18 @@
" Vim indent file
" Language: bst
" Author: Tim Pope <vimNOSPAM@tpope.info>
-" $Id: bst.vim,v 1.1 2007/05/05 18:11:12 vimboss Exp $
+" Last Change: 2022 Mar 15
if exists("b:did_indent")
finish
endif
let b:did_indent = 1
-setlocal expandtab
setlocal indentexpr=GetBstIndent(v:lnum)
-"setlocal smartindent
setlocal cinkeys&
setlocal cinkeys-=0#
setlocal indentkeys&
-"setlocal indentkeys+=0%
+let b:undo_indent = 'setlocal indentexpr< cinkeys< indentkeys<'
" Only define the function once.
if exists("*GetBstIndent")
diff --git a/runtime/indent/cdl.vim b/runtime/indent/cdl.vim
index 0e3c6152b0..2c0fc7988e 100644
--- a/runtime/indent/cdl.vim
+++ b/runtime/indent/cdl.vim
@@ -1,7 +1,7 @@
" Description: Comshare Dimension Definition Language (CDL)
" Maintainer: Raul Segura Acevedo <raulseguraaceved@netscape.net> (Invalid email address)
" Doug Kearns <dougkearns@gmail.com>
-" Last Change: Fri Nov 30 13:35:48 2001 CST
+" Last Change: 2022 Apr 06
if exists("b:did_indent")
"finish
@@ -12,6 +12,8 @@ setlocal indentexpr=CdlGetIndent(v:lnum)
setlocal indentkeys&
setlocal indentkeys+==~else,=~endif,=~then,;,),=
+let b:undo_indent = "setl inde< indk<"
+
" Only define the function once.
if exists("*CdlGetIndent")
"finish
diff --git a/runtime/indent/chaiscript.vim b/runtime/indent/chaiscript.vim
index 445281cc46..b7a3fe5896 100644
--- a/runtime/indent/chaiscript.vim
+++ b/runtime/indent/chaiscript.vim
@@ -1,6 +1,7 @@
" Vim indent file
" Language: ChaiScript
" Maintainer: Jason Turner <lefticus 'at' gmail com>
+" Last Change: 2022 Apr 06
" Only load this indent file when no other was loaded.
if exists("b:did_indent")
@@ -11,6 +12,8 @@ let b:did_indent = 1
setlocal indentexpr=GetChaiScriptIndent()
setlocal autoindent
+let b:undo_indent = "setl ai< inde<"
+
" Only define the function once.
if exists("*GetChaiScriptIndent")
finish
diff --git a/runtime/indent/clojure.vim b/runtime/indent/clojure.vim
index fadcaf4b4a..5bfbfbb197 100644
--- a/runtime/indent/clojure.vim
+++ b/runtime/indent/clojure.vim
@@ -5,7 +5,7 @@
" Meikel Brandmeyer <mb@kotka.de>
" URL: https://github.com/clojure-vim/clojure.vim
" License: Vim (see :h license)
-" Last Change: 2021-10-26
+" Last Change: 2022-03-24
if exists("b:did_indent")
finish
diff --git a/runtime/indent/cmake.vim b/runtime/indent/cmake.vim
index 845bdd7655..af27c0d49b 100644
--- a/runtime/indent/cmake.vim
+++ b/runtime/indent/cmake.vim
@@ -3,7 +3,7 @@
" Author: Andy Cedilnik <andy.cedilnik@kitware.com>
" Maintainer: Dimitri Merejkowsky <d.merej@gmail.com>
" Former Maintainer: Karthik Krishnan <karthik.krishnan@kitware.com>
-" Last Change: 2017 Sep 24
+" Last Change: 2022 Apr 06
"
" Licence: The CMake license applies to this file. See
" https://cmake.org/licensing
@@ -17,6 +17,8 @@ let b:did_indent = 1
setlocal indentexpr=CMakeGetIndent(v:lnum)
setlocal indentkeys+==ENDIF(,ENDFOREACH(,ENDMACRO(,ELSE(,ELSEIF(,ENDWHILE(
+let b:undo_indent = "setl inde< indk<"
+
" Only define the function once.
if exists("*CMakeGetIndent")
finish
diff --git a/runtime/indent/cs.vim b/runtime/indent/cs.vim
index 76c12efecf..acc3ba75d5 100644
--- a/runtime/indent/cs.vim
+++ b/runtime/indent/cs.vim
@@ -3,13 +3,10 @@
" Maintainer: Nick Jensen <nickspoon@gmail.com>
" Former Maintainers: Aquila Deus
" Johannes Zellner <johannes@zellner.org>
-" Last Change: 2018-11-21
-" Filenames: *.cs
+" Last Change: 2020-03-26
" License: Vim (see :h license)
" Repository: https://github.com/nickspoons/vim-cs
-"
-" Only load this indent file when no other was loaded.
if exists('b:did_indent')
finish
endif
@@ -22,19 +19,23 @@ set cpoptions&vim
setlocal indentexpr=GetCSIndent(v:lnum)
function! s:IsCompilerDirective(line)
- return a:line =~? '^\s*#'
+ " Exclude #region and #endregion - these should be indented normally
+ return a:line =~# '^\s*#' && !s:IsRegionDirective(a:line)
+endf
+
+function! s:IsRegionDirective(line)
+ return a:line =~# '^\s*#\s*region' || a:line =~# '^\s*#\s*endregion'
endf
function! s:IsAttributeLine(line)
- return a:line =~? '^\s*\[[A-Za-z]' && a:line =~? '\]$'
+ return a:line =~# '^\s*\[[A-Za-z]' && a:line =~# '\]$'
endf
function! s:FindPreviousNonCompilerDirectiveLine(start_lnum)
for delta in range(0, a:start_lnum)
let lnum = a:start_lnum - delta
let line = getline(lnum)
- let is_directive = s:IsCompilerDirective(line)
- if !is_directive
+ if !s:IsCompilerDirective(line) && !s:IsRegionDirective(line)
return lnum
endif
endfor
@@ -58,8 +59,9 @@ function! GetCSIndent(lnum) abort
let lnum = s:FindPreviousNonCompilerDirectiveLine(a:lnum - 1)
let previous_code_line = getline(lnum)
if s:IsAttributeLine(previous_code_line)
- let ind = indent(lnum)
- return ind
+ return indent(lnum)
+ elseif s:IsRegionDirective(this_line)
+ return cindent(lnum)
else
return cindent(a:lnum)
endif
diff --git a/runtime/indent/d.vim b/runtime/indent/d.vim
index 57f9125890..80c9a2f559 100644
--- a/runtime/indent/d.vim
+++ b/runtime/indent/d.vim
@@ -2,7 +2,7 @@
" Language: D
" Maintainer: Jason Mills <jmills@cs.mun.ca> (Invalid email address)
" Doug Kearns <dougkearns@gmail.com>
-" Last Change: 2005 Nov 22
+" Last Change: 2022 Apr 06
" Version: 0.1
"
" Please email me with bugs, comments, and suggestion. Put vim in the subject
@@ -19,4 +19,6 @@ let b:did_indent = 1
" D indenting is a lot like the built-in C indenting.
setlocal cindent
+let b:undo_indent = "setl cin<"
+
" vim: ts=8 noet
diff --git a/runtime/indent/dictconf.vim b/runtime/indent/dictconf.vim
index 2e15c76146..fa40585a92 100644
--- a/runtime/indent/dictconf.vim
+++ b/runtime/indent/dictconf.vim
@@ -1,7 +1,7 @@
" Vim indent file
" Language: dict(1) configuration file
" Previous Maintainer: Nikolai Weibull <now@bitwi.se>
-" Latest Revision: 2006-12-20
+" Last Change: 2022 Apr 06
if exists("b:did_indent")
finish
@@ -11,3 +11,5 @@ let b:did_indent = 1
setlocal indentkeys=0{,0},!^F,o,O cinwords= autoindent smartindent
setlocal nosmartindent
inoremap <buffer> # X#
+
+let b:undo_indent = "setl ai< cinw< indk< si< | silent! iunmap <buffer> #"
diff --git a/runtime/indent/dictdconf.vim b/runtime/indent/dictdconf.vim
index 5c4fbdafb5..5c0e7c566c 100644
--- a/runtime/indent/dictdconf.vim
+++ b/runtime/indent/dictdconf.vim
@@ -11,3 +11,5 @@ let b:did_indent = 1
setlocal indentkeys=0{,0},!^F,o,O cinwords= autoindent smartindent
setlocal nosmartindent
inoremap <buffer> # X#
+
+let b:undo_indent = "setl ai< cinw< indk< si< | silent! iunmap <buffer> #"
diff --git a/runtime/indent/dtd.vim b/runtime/indent/dtd.vim
index c2b3c0c58e..9fca296c0b 100644
--- a/runtime/indent/dtd.vim
+++ b/runtime/indent/dtd.vim
@@ -4,6 +4,12 @@
" Previous Maintainer: Nikolai Weibull <now@bitwi.se>
" Last Change: 24 Sep 2021
+" Only load this indent file when no other was loaded.
+if exists("b:did_indent")
+ finish
+endif
+let b:did_indent = 1
+
setlocal indentexpr=GetDTDIndent()
setlocal indentkeys=!^F,o,O,>
setlocal nosmartindent
diff --git a/runtime/indent/dylan.vim b/runtime/indent/dylan.vim
index 55255ddfa9..e2a6d1039c 100644
--- a/runtime/indent/dylan.vim
+++ b/runtime/indent/dylan.vim
@@ -3,7 +3,7 @@
" Maintainer: Brent A. Fulgham <bfulgham@debian.org> (Invalid email address)
" Doug Kearns <dougkearns@gmail.com>
" Version: 0.01
-" Last Change: 2017 Jun 13
+" Last Change: 2022 Apr 06
" Only load this indent file when no other was loaded.
if exists("b:did_indent")
@@ -15,6 +15,9 @@ setlocal indentkeys+==~begin,=~block,=~case,=~cleanup,=~define,=~end,=~else,=~el
" Define the appropriate indent function but only once
setlocal indentexpr=DylanGetIndent()
+
+let b:undo_indent = "setl inde< indk<"
+
if exists("*DylanGetIndent")
finish
endif
diff --git a/runtime/indent/falcon.vim b/runtime/indent/falcon.vim
index 664ad61aa5..a58ccad870 100644
--- a/runtime/indent/falcon.vim
+++ b/runtime/indent/falcon.vim
@@ -3,6 +3,7 @@
" Maintainer: Steven Oliver <oliver.steven@gmail.com>
" Website: https://steveno@github.com/steveno/falconpl-vim.git
" Credits: This is, to a great extent, a copy n' paste of ruby.vim.
+" 2022 April: b:undo_indent added by Doug Kearns
" 1. Setup {{{1
" ============
@@ -20,6 +21,8 @@ setlocal indentexpr=FalconGetIndent(v:lnum)
setlocal indentkeys=0{,0},0),0],!^F,o,O,e
setlocal indentkeys+==~case,=~catch,=~default,=~elif,=~else,=~end,=~\"
+let b:undo_indent = "setl inde< indk< si<"
+
" Define the appropriate indent function but only once
if exists("*FalconGetIndent")
finish
diff --git a/runtime/indent/fennel.vim b/runtime/indent/fennel.vim
new file mode 100644
index 0000000000..e12aa18a8a
--- /dev/null
+++ b/runtime/indent/fennel.vim
@@ -0,0 +1,12 @@
+" Vim indent file
+" Language: Fennel
+" Maintainer: Gregory Anders <greg[NOSPAM]@gpanders.com>
+" Last Change: 2022 Apr 20
+
+" Only load this indent file when no other was loaded.
+if exists("b:did_indent")
+ finish
+endif
+
+" Use the Lisp indenting
+runtime! indent/lisp.vim
diff --git a/runtime/indent/fortran.vim b/runtime/indent/fortran.vim
index 26ed33a54d..9623014818 100644
--- a/runtime/indent/fortran.vim
+++ b/runtime/indent/fortran.vim
@@ -1,13 +1,13 @@
" Vim indent file
" Language: Fortran 2008 (and older: Fortran 2003, 95, 90, and 77)
-" Version: (v48) 2020 October 07
-" Maintainer: Ajit J. Thakkar <ajit@unb.ca>; <http://www2.unb.ca/~ajit/>
+" Version: (v49) 2022 May 14
+" Maintainer: Ajit J. Thakkar <thakkar.ajit@gmail.com>; <http://www2.unb.ca/~ajit/>
" Usage: For instructions, do :help fortran-indent from Vim
" Credits:
" Version 0.1 was created in September 2000 by Ajit Thakkar.
" Since then, useful suggestions and contributions have been made, in order, by:
" Albert Oliver Serra, Takuya Fujiwara, Philipp Edelmann, Eisuke Kawashima,
-" and Louis Cochen.
+" Louis Cochen, and Doug Kearns.
" Only load this indent file when no other was loaded.
if exists("b:did_indent")
@@ -17,6 +17,7 @@ let b:did_indent = 1
let s:cposet=&cpoptions
set cpoptions&vim
+let b:undo_indent = "setl inde< indk<"
setlocal indentkeys+==~end,=~case,=~if,=~else,=~do,=~where,=~elsewhere,=~select
setlocal indentkeys+==~endif,=~enddo,=~endwhere,=~endselect,=~elseif
diff --git a/runtime/indent/freebasic.vim b/runtime/indent/freebasic.vim
new file mode 100644
index 0000000000..248b928635
--- /dev/null
+++ b/runtime/indent/freebasic.vim
@@ -0,0 +1,11 @@
+" Vim indent file
+" Language: FreeBASIC
+" Maintainer: Doug Kearns <dougkearns@gmail.com>
+" Last Change: 2022 Jan 24
+
+" Only load this indent file when no other was loaded.
+if exists("b:did_indent")
+ finish
+endif
+
+runtime! indent/vb.vim
diff --git a/runtime/indent/gitolite.vim b/runtime/indent/gitolite.vim
index b36f30a494..22be6872cb 100644
--- a/runtime/indent/gitolite.vim
+++ b/runtime/indent/gitolite.vim
@@ -4,7 +4,7 @@
" (https://raw.githubusercontent.com/sitaramc/gitolite/master/contrib/vim/indent/gitolite.vim)
" Maintainer: Sitaram Chamarty <sitaramc@gmail.com>
" (former Maintainer: Teemu Matilainen <teemu.matilainen@iki.fi>)
-" Last Change: 2017 Oct 05
+" Last Change: 2022 Apr 06
if exists("b:did_indent")
finish
@@ -15,6 +15,8 @@ setlocal autoindent
setlocal indentexpr=GetGitoliteIndent()
setlocal indentkeys=o,O,*<Return>,!^F,=repo,\",=
+let b:undo_indent = "setl ai< inde< indk<"
+
" Only define the function once.
if exists("*GetGitoliteIndent")
finish
diff --git a/runtime/indent/haml.vim b/runtime/indent/haml.vim
index baca1d49d9..acd99d9c7d 100644
--- a/runtime/indent/haml.vim
+++ b/runtime/indent/haml.vim
@@ -1,7 +1,7 @@
" Vim indent file
" Language: Haml
" Maintainer: Tim Pope <vimNOSPAM@tpope.org>
-" Last Change: 2019 Dec 05
+" Last Change: 2022 Mar 15
if exists("b:did_indent")
finish
@@ -14,6 +14,8 @@ setlocal autoindent
setlocal indentexpr=GetHamlIndent()
setlocal indentkeys=o,O,*<Return>,},],0),!^F,=end,=else,=elsif,=rescue,=ensure,=when
+let b:undo_indent = "setl ai< inde< indk<"
+
" Only define the function once.
if exists("*GetHamlIndent")
finish
diff --git a/runtime/indent/html.vim b/runtime/indent/html.vim
index d4b91f6421..a3c32d6342 100644
--- a/runtime/indent/html.vim
+++ b/runtime/indent/html.vim
@@ -1,7 +1,7 @@
" Vim indent script for HTML
" Maintainer: Bram Moolenaar
" Original Author: Andy Wokula <anwoku@yahoo.de>
-" Last Change: 2021 Jun 13
+" Last Change: 2022 Jan 31
" Version: 1.0 "{{{
" Description: HTML indent script with cached state for faster indenting on a
" range of lines.
@@ -149,6 +149,15 @@ func HtmlIndent_CheckUserSettings()
let b:html_indent_line_limit = 200
endif
endif
+
+ if exists('b:html_indent_attribute')
+ let b:hi_attr_indent = b:html_indent_attribute
+ elseif exists('g:html_indent_attribute')
+ let b:hi_attr_indent = g:html_indent_attribute
+ else
+ let b:hi_attr_indent = 2
+ endif
+
endfunc "}}}
" Init Script Vars
@@ -946,11 +955,11 @@ func s:InsideTag(foundHtmlString)
let idx = match(text, '<' . s:tagname . '\s\+\zs\w')
endif
if idx == -1
- " after just "<tag" indent two levels more
+ " after just "<tag" indent two levels more by default
let idx = match(text, '<' . s:tagname . '$')
if idx >= 0
call cursor(lnum, idx + 1)
- return virtcol('.') - 1 + shiftwidth() * 2
+ return virtcol('.') - 1 + shiftwidth() * b:hi_attr_indent
endif
endif
if idx > 0
diff --git a/runtime/indent/idlang.vim b/runtime/indent/idlang.vim
index e6a1d73775..1519865ab5 100644
--- a/runtime/indent/idlang.vim
+++ b/runtime/indent/idlang.vim
@@ -2,7 +2,7 @@
" Language: IDL (ft=idlang)
" Maintainer: Aleksandar Jelenak <ajelenak AT yahoo.com> (Invalid email address)
" Doug Kearns <dougkearns@gmail.com>
-" Last change: 2017 Jun 13
+" Last change: 2022 Apr 06
" Only load this indent file when no other was loaded.
if exists("b:did_indent")
@@ -14,6 +14,8 @@ setlocal indentkeys=o,O,0=endif,0=ENDIF,0=endelse,0=ENDELSE,0=endwhile,0=ENDWHIL
setlocal indentexpr=GetIdlangIndent(v:lnum)
+let b:undo_indent = "setl inde< indk<"
+
" Only define the function once.
if exists("*GetIdlangIndent")
finish
diff --git a/runtime/indent/julia.vim b/runtime/indent/julia.vim
index a90cff49e4..8e4c60322e 100644
--- a/runtime/indent/julia.vim
+++ b/runtime/indent/julia.vim
@@ -2,9 +2,15 @@
" Language: Julia
" Maintainer: Carlo Baldassi <carlobaldassi@gmail.com>
" Homepage: https://github.com/JuliaEditorSupport/julia-vim
-" Last Change: 2016 jun 16
+" Last Change: 2022 Jun 14
" Notes: originally based on Bram Molenaar's indent file for vim
+" Only load this indent file when no other was loaded.
+if exists("b:did_indent")
+ finish
+endif
+let b:did_indent = 1
+
setlocal autoindent
setlocal indentexpr=GetJuliaIndent()
diff --git a/runtime/indent/krl.vim b/runtime/indent/krl.vim
new file mode 100644
index 0000000000..cc3cbd1abb
--- /dev/null
+++ b/runtime/indent/krl.vim
@@ -0,0 +1,130 @@
+" Vim indent file
+" Language: Kuka Robot Language
+" Maintainer: Patrick Meiser-Knosowski <knosowski@graeffrobotics.de>
+" Version: 3.0.0
+" Last Change: 15. Apr 2022
+" Credits: Based on indent/vim.vim
+
+" Only load this indent file when no other was loaded.
+if exists("b:did_indent")
+ finish
+endif
+let b:did_indent = 1
+
+setlocal nolisp
+setlocal nocindent
+setlocal nosmartindent
+setlocal autoindent
+setlocal indentexpr=GetKrlIndent()
+setlocal indentkeys=!^F,o,O,=~end,0=~else,0=~case,0=~default,0=~until,0=~continue,=~part
+let b:undo_indent = "setlocal lisp< cindent< smartindent< autoindent< indentexpr< indentkeys<"
+
+if get(g:,'krlSpaceIndent',1)
+ " Use spaces, not tabs, for indention, 2 is enough.
+ " More or even tabs would waste valuable space on the teach pendant.
+ setlocal softtabstop=2
+ setlocal shiftwidth=2
+ setlocal expandtab
+ setlocal shiftround
+ let b:undo_indent = b:undo_indent." softtabstop< shiftwidth< expandtab< shiftround<"
+endif
+
+" Only define the function once.
+if exists("*GetKrlIndent")
+ finish
+endif
+let s:keepcpo = &cpo
+set cpo&vim
+
+function GetKrlIndent() abort
+
+ let currentLine = getline(v:lnum)
+ if currentLine =~? '\v^;(\s*(end)?fold>)@!' && !get(g:, 'krlCommentIndent', 0)
+ " If current line has a ; in column 1 and is no fold, keep zero indent.
+ " This may be usefull if code is commented out at the first column.
+ return 0
+ endif
+
+ " Find a non-blank line above the current line.
+ let preNoneBlankLineNum = s:KrlPreNoneBlank(v:lnum - 1)
+ if preNoneBlankLineNum == 0
+ " At the start of the file use zero indent.
+ return 0
+ endif
+
+ let preNoneBlankLine = getline(preNoneBlankLineNum)
+ let ind = indent(preNoneBlankLineNum)
+
+ " Define add 'shiftwidth' pattern
+ let addShiftwidthPattern = '\v^\s*('
+ if get(g:, 'krlIndentBetweenDef', 1)
+ let addShiftwidthPattern ..= '(global\s+)?def(fct|dat)?\s+\$?\w'
+ let addShiftwidthPattern ..= '|'
+ endif
+ let addShiftwidthPattern ..= 'if>|while>|for>|loop>'
+ let addShiftwidthPattern ..= '|else>'
+ let addShiftwidthPattern ..= '|case>|default>'
+ let addShiftwidthPattern ..= '|repeat>'
+ let addShiftwidthPattern ..= '|skip>|(ptp_)?spline>'
+ let addShiftwidthPattern ..= '|time_block\s+(start|part)>'
+ let addShiftwidthPattern ..= '|const_vel\s+start>'
+ let addShiftwidthPattern ..= ')'
+
+ " Define Subtract 'shiftwidth' pattern
+ let subtractShiftwidthPattern = '\v^\s*('
+ if get(g:, 'krlIndentBetweenDef', 1)
+ let subtractShiftwidthPattern ..= 'end(fct|dat)?>'
+ let subtractShiftwidthPattern ..= '|'
+ endif
+ let subtractShiftwidthPattern ..= 'end(if|while|for|loop)>'
+ let subtractShiftwidthPattern ..= '|else>'
+ let subtractShiftwidthPattern ..= '|case>|default>|endswitch>'
+ let subtractShiftwidthPattern ..= '|until>'
+ let subtractShiftwidthPattern ..= '|end(skip|spline)>'
+ let subtractShiftwidthPattern ..= '|time_block\s+(part|end)>'
+ let subtractShiftwidthPattern ..= '|const_vel\s+end>'
+ let subtractShiftwidthPattern ..= ')'
+
+ " Add shiftwidth
+ if preNoneBlankLine =~? addShiftwidthPattern
+ let ind += &sw
+ endif
+
+ " Subtract shiftwidth
+ if currentLine =~? subtractShiftwidthPattern
+ let ind = ind - &sw
+ endif
+
+ " First case after a switch gets the indent of the switch.
+ if currentLine =~? '\v^\s*case>'
+ \&& preNoneBlankLine =~? '\v^\s*switch>'
+ let ind = ind + &sw
+ endif
+
+ " align continue with the following instruction
+ if currentLine =~? '\v^\s*continue>'
+ \&& getline(v:lnum + 1) =~? subtractShiftwidthPattern
+ let ind = ind - &sw
+ endif
+
+ return ind
+endfunction
+
+" This function works almost like prevnonblank() but handles &-headers,
+" comments and continue instructions like blank lines
+function s:KrlPreNoneBlank(lnum) abort
+
+ let nPreNoneBlank = prevnonblank(a:lnum)
+
+ while nPreNoneBlank > 0 && getline(nPreNoneBlank) =~? '\v^\s*(\&\w\+|;|continue>)'
+ " Previouse none blank line irrelevant. Look further aback.
+ let nPreNoneBlank = prevnonblank(nPreNoneBlank - 1)
+ endwhile
+
+ return nPreNoneBlank
+endfunction
+
+let &cpo = s:keepcpo
+unlet s:keepcpo
+
+" vim:sw=2 sts=2 et
diff --git a/runtime/indent/liquid.vim b/runtime/indent/liquid.vim
index 7beb0388d1..6fc933797e 100644
--- a/runtime/indent/liquid.vim
+++ b/runtime/indent/liquid.vim
@@ -1,7 +1,7 @@
" Vim indent file
" Language: Liquid
" Maintainer: Tim Pope <vimNOSPAM@tpope.org>
-" Last Change: 2017 Jun 13
+" Last Change: 2022 Mar 15
if exists('b:did_indent')
finish
@@ -29,17 +29,19 @@ let b:did_indent = 1
setlocal indentexpr=GetLiquidIndent()
setlocal indentkeys=o,O,*<Return>,<>>,{,},0),0],o,O,!^F,=end,=endif,=endunless,=endifchanged,=endcase,=endfor,=endtablerow,=endcapture,=else,=elsif,=when,=empty
+let b:undo_indent = "setl inde< indk<"
+
" Only define the function once.
if exists('*GetLiquidIndent')
finish
endif
-function! s:count(string,pattern)
+function! s:count(string, pattern) abort
let string = substitute(a:string,'\C'.a:pattern,"\n",'g')
return strlen(substitute(string,"[^\n]",'','g'))
endfunction
-function! GetLiquidIndent(...)
+function! GetLiquidIndent(...) abort
if a:0 && a:1 == '.'
let v:lnum = line('.')
elseif a:0 && a:1 =~ '^\d'
@@ -51,13 +53,14 @@ function! GetLiquidIndent(...)
let lnum = prevnonblank(v:lnum-1)
let line = getline(lnum)
let cline = getline(v:lnum)
- let line = substitute(line,'\C^\%(\s*{%\s*end\w*\s*%}\)\+','','')
- let line .= matchstr(cline,'\C^\%(\s*{%\s*end\w*\s*%}\)\+')
- let cline = substitute(cline,'\C^\%(\s*{%\s*end\w*\s*%}\)\+','','')
+ let line = substitute(line,'\C^\%(\s*{%-\=\s*end\w*\s*-\=%}\)\+','','')
+ let line = substitute(line,'\C\%(\s*{%-\=\s*if.\+-\=%}.\+{%-\=\s*endif\s*-\=%}\)\+','','g')
+ let line .= matchstr(cline,'\C^\%(\s*{%-\=\s*end\w*\s*-\=%}\)\+')
+ let cline = substitute(cline,'\C^\%(\s*{%-\=\s*end\w*\s*-\=%}\)\+','','')
let sw = shiftwidth()
- let ind += sw * s:count(line,'{%\s*\%(if\|elsif\|else\|unless\|ifchanged\|case\|when\|for\|empty\|tablerow\|capture\)\>')
- let ind -= sw * s:count(line,'{%\s*end\%(if\|unless\|ifchanged\|case\|for\|tablerow\|capture\)\>')
- let ind -= sw * s:count(cline,'{%\s*\%(elsif\|else\|when\|empty\)\>')
- let ind -= sw * s:count(cline,'{%\s*end\w*$')
+ let ind += sw * s:count(line,'{%-\=\s*\%(if\|elsif\|else\|unless\|ifchanged\|case\|when\|for\|empty\|tablerow\|capture\)\>')
+ let ind -= sw * s:count(line,'{%-\=\s*end\%(if\|unless\|ifchanged\|case\|for\|tablerow\|capture\)\>')
+ let ind -= sw * s:count(cline,'{%-\=\s*\%(elsif\|else\|when\|empty\)\>')
+ let ind -= sw * s:count(cline,'{%-\=\s*end\w*$')
return ind
endfunction
diff --git a/runtime/indent/make.vim b/runtime/indent/make.vim
index 76c8f83399..4d1838b3aa 100644
--- a/runtime/indent/make.vim
+++ b/runtime/indent/make.vim
@@ -2,7 +2,7 @@
" Language: Makefile
" Maintainer: Doug Kearns <dougkearns@gmail.com>
" Previous Maintainer: Nikolai Weibull <now@bitwi.se>
-" Last Change: 24 Sep 2021
+" Last Change: 2022 Apr 06
if exists("b:did_indent")
finish
@@ -13,7 +13,7 @@ setlocal indentexpr=GetMakeIndent()
setlocal indentkeys=!^F,o,O,<:>,=else,=endif
setlocal nosmartindent
-let b:undo_indent = "setl ai< inde< indk<"
+let b:undo_indent = "setl inde< indk< si<"
if exists("*GetMakeIndent")
finish
diff --git a/runtime/indent/mma.vim b/runtime/indent/mma.vim
index ebf98b9a38..9dbfd74d66 100644
--- a/runtime/indent/mma.vim
+++ b/runtime/indent/mma.vim
@@ -3,6 +3,7 @@
" Maintainer: Steve Layland <layland@wolfram.com> (Invalid email address)
" Doug Kearns <dougkearns@gmail.com>
" Last Change: Sat May 10 18:56:22 CDT 2005
+" 2022 April: b:undo_indent added by Doug Kearns
" Source: http://vim.sourceforge.net/scripts/script.php?script_id=1274
" http://members.wolfram.com/layland/vim/indent/mma.vim
"
@@ -26,6 +27,8 @@ setlocal indentexpr=GetMmaIndent()
setlocal indentkeys+=0[,0],0(,0)
setlocal nosi "turn off smart indent so we don't over analyze } blocks
+let b:undo_indent = "setl inde< indk< si<"
+
if exists("*GetMmaIndent")
finish
endif
diff --git a/runtime/indent/nginx.vim b/runtime/indent/nginx.vim
index d4afec1c11..8cef7662e0 100644
--- a/runtime/indent/nginx.vim
+++ b/runtime/indent/nginx.vim
@@ -1,7 +1,7 @@
" Vim indent file
" Language: nginx.conf
" Maintainer: Chris Aumann <me@chr4.org>
-" Last Change: Apr 15, 2017
+" Last Change: 2022 Apr 06
if exists("b:did_indent")
finish
@@ -15,3 +15,5 @@ setlocal cindent
" Just make sure that the comments are not reset as defs would be.
setlocal cinkeys-=0#
+
+let b:undo_indent = "setl inde< cin< cink<"
diff --git a/runtime/indent/objc.vim b/runtime/indent/objc.vim
index a5451a5a11..1d107050dd 100644
--- a/runtime/indent/objc.vim
+++ b/runtime/indent/objc.vim
@@ -1,9 +1,7 @@
" Vim indent file
" Language: Objective-C
" Maintainer: Kazunobu Kuriyama <kazunobu.kuriyama@nifty.com>
-" Last Change: 2004 May 16
-"
-
+" Last Change: 2022 Apr 06
" Only load this indent file when no other was loaded.
if exists("b:did_indent")
@@ -19,6 +17,8 @@ setlocal indentexpr=GetObjCIndent()
setlocal indentkeys-=:
setlocal indentkeys+=<:>
+let b:undo_indent = "setl cin< inde< indk<"
+
" Only define the function once.
if exists("*GetObjCIndent")
finish
diff --git a/runtime/indent/occam.vim b/runtime/indent/occam.vim
index 2979ac16ac..673940a7ec 100644
--- a/runtime/indent/occam.vim
+++ b/runtime/indent/occam.vim
@@ -2,7 +2,7 @@
" Language: occam
" Maintainer: Mario Schweigler <ms44@kent.ac.uk> (Invalid email address)
" Doug Kearns <dougkearns@gmail.com>
-" Last Change: 23 April 2003
+" Last Change: 2022 Apr 06
" Only load this indent file when no other was loaded.
if exists("b:did_indent")
@@ -17,6 +17,8 @@ setlocal indentexpr=GetOccamIndent()
setlocal indentkeys=o,O,0=:
"}}}
+let b:undo_indent = "setl inde< indk<"
+
" Only define the function once
if exists("*GetOccamIndent")
finish
diff --git a/runtime/indent/perl.vim b/runtime/indent/perl.vim
index 5fc8b7008a..4c91fa1b33 100644
--- a/runtime/indent/perl.vim
+++ b/runtime/indent/perl.vim
@@ -1,9 +1,10 @@
" Vim indent file
-" Language: Perl 5
+" Language: Perl
" Maintainer: vim-perl <vim-perl@googlegroups.com>
" Homepage: https://github.com/vim-perl/vim-perl
" Bugs/requests: https://github.com/vim-perl/vim-perl/issues
-" Last Change: 2020 Apr 15
+" License: Vim License (see :help license)
+" Last Change: 2021 Sep 24
" Suggestions and improvements by :
" Aaron J. Sherman (use syntax for hints)
@@ -35,6 +36,8 @@ if !b:indent_use_syntax
setlocal indentkeys+=0=EO
endif
+let b:undo_indent = "setl inde< indk<"
+
let s:cpo_save = &cpo
set cpo-=C
diff --git a/runtime/indent/postscr.vim b/runtime/indent/postscr.vim
index 0691cd237c..8430ccf8b8 100644
--- a/runtime/indent/postscr.vim
+++ b/runtime/indent/postscr.vim
@@ -1,9 +1,7 @@
" PostScript indent file
" Language: PostScript
-" Maintainer: Mike Williams <mrw@netcomuk.co.uk> (Invalid email address)
-" Doug Kearns <dougkearns@gmail.com>
-" Last Change: 2nd July 2001
-"
+" Maintainer: Mike Williams <mrw@eandem.co.uk>
+" Last Change: 2022 Apr 06
" Only load this indent file when no other was loaded.
if exists("b:did_indent")
@@ -14,6 +12,8 @@ let b:did_indent = 1
setlocal indentexpr=PostscrIndentGet(v:lnum)
setlocal indentkeys+=0],0=>>,0=%%,0=end,0=restore,0=grestore indentkeys-=:,0#,e
+let b:undo_indent = "setl inde< indk<"
+
" Catch multiple instantiations
if exists("*PostscrIndentGet")
finish
diff --git a/runtime/indent/pov.vim b/runtime/indent/pov.vim
index f74c96b7f7..60077ffffe 100644
--- a/runtime/indent/pov.vim
+++ b/runtime/indent/pov.vim
@@ -2,6 +2,7 @@
" Language: PoV-Ray Scene Description Language
" Maintainer: David Necas (Yeti) <yeti@physics.muni.cz>
" Last Change: 2017 Jun 13
+" 2022 April: b:undo_indent added by Doug Kearns
" URI: http://trific.ath.cx/Ftp/vim/indent/pov.vim
" Only load this indent file when no other was loaded.
@@ -16,6 +17,8 @@ setlocal nolisp " Make sure lisp indenting doesn't supersede us.
setlocal indentexpr=GetPoVRayIndent()
setlocal indentkeys+==else,=end,0]
+let b:undo_indent = "setl inde< indk< lisp<"
+
" Only define the function once.
if exists("*GetPoVRayIndent")
finish
diff --git a/runtime/indent/prolog.vim b/runtime/indent/prolog.vim
index ac03c28064..0c4fd541f9 100644
--- a/runtime/indent/prolog.vim
+++ b/runtime/indent/prolog.vim
@@ -4,6 +4,7 @@
" Doug Kearns <dougkearns@gmail.com>
" Revised on: 2002.02.18. 23:34:05
" Last change by: Takuya Fujiwara, 2018 Sep 23
+" 2022 April: b:undo_indent added by Doug Kearns
" TODO:
" checking with respect to syntax highlighting
@@ -21,6 +22,8 @@ setlocal indentexpr=GetPrologIndent()
setlocal indentkeys-=:,0#
setlocal indentkeys+=0%,-,0;,>,0)
+let b:undo_indent = "setl inde< indk<"
+
" Only define the function once.
"if exists("*GetPrologIndent")
" finish
diff --git a/runtime/indent/systemd.vim b/runtime/indent/qb64.vim
index a05a87bb1c..09f815c43d 100644
--- a/runtime/indent/systemd.vim
+++ b/runtime/indent/qb64.vim
@@ -1,10 +1,11 @@
" Vim indent file
-" Language: systemd.unit(5)
+" Language: QB64
+" Maintainer: Doug Kearns <dougkearns@gmail.com>
+" Last Change: 2022 Jan 24
" Only load this indent file when no other was loaded.
if exists("b:did_indent")
finish
endif
-" Looks a lot like dosini files.
-runtime! indent/dosini.vim
+runtime! indent/vb.vim
diff --git a/runtime/indent/query.lua b/runtime/indent/query.lua
new file mode 100644
index 0000000000..c86948e95e
--- /dev/null
+++ b/runtime/indent/query.lua
@@ -0,0 +1,6 @@
+-- Neovim indent file
+-- Language: Tree-sitter query
+-- Last Change: 2022 Mar 29
+
+-- it's a lisp!
+vim.cmd([[ runtime! indent/lisp.vim ]])
diff --git a/runtime/indent/ruby.vim b/runtime/indent/ruby.vim
index 559d8652a6..6ce8529fd1 100644
--- a/runtime/indent/ruby.vim
+++ b/runtime/indent/ruby.vim
@@ -4,7 +4,7 @@
" Previous Maintainer: Nikolai Weibull <now at bitwi.se>
" URL: https://github.com/vim-ruby/vim-ruby
" Release Coordinator: Doug Kearns <dougkearns@gmail.com>
-" Last Change: 2021 Feb 03
+" Last Change: 2022 Mar 22
" 0. Initialization {{{1
" =================
@@ -40,9 +40,11 @@ setlocal nosmartindent
" Now, set up our indentation expression and keys that trigger it.
setlocal indentexpr=GetRubyIndent(v:lnum)
setlocal indentkeys=0{,0},0),0],!^F,o,O,e,:,.
-setlocal indentkeys+==end,=else,=elsif,=when,=ensure,=rescue,==begin,==end
+setlocal indentkeys+==end,=else,=elsif,=when,=in\ ,=ensure,=rescue,==begin,==end
setlocal indentkeys+==private,=protected,=public
+let b:undo_indent = "setlocal indentexpr< indentkeys< smartindent<"
+
" Only define the function once.
if exists("*GetRubyIndent")
finish
@@ -85,14 +87,17 @@ let s:skip_expr =
" Regex used for words that, at the start of a line, add a level of indent.
let s:ruby_indent_keywords =
\ '^\s*\zs\<\%(module\|class\|if\|for' .
- \ '\|while\|until\|else\|elsif\|case\|when\|unless\|begin\|ensure\|rescue' .
+ \ '\|while\|until\|else\|elsif\|case\|when\|in\|unless\|begin\|ensure\|rescue' .
\ '\|\%(\K\k*[!?]\?\s\+\)\=def\):\@!\>' .
\ '\|\%([=,*/%+-]\|<<\|>>\|:\s\)\s*\zs' .
\ '\<\%(if\|for\|while\|until\|case\|unless\|begin\):\@!\>'
+" Def without an end clause: def method_call(...) = <expression>
+let s:ruby_endless_def = '\<def\s\+\k\+[!?]\=\%((.*)\|\s\)\s*='
+
" Regex used for words that, at the start of a line, remove a level of indent.
let s:ruby_deindent_keywords =
- \ '^\s*\zs\<\%(ensure\|else\|rescue\|elsif\|when\|end\):\@!\>'
+ \ '^\s*\zs\<\%(ensure\|else\|rescue\|elsif\|when\|in\|end\):\@!\>'
" Regex that defines the start-match for the 'end' keyword.
"let s:end_start_regex = '\%(^\|[^.]\)\<\%(module\|class\|def\|if\|for\|while\|until\|case\|unless\|begin\|do\)\>'
@@ -104,15 +109,31 @@ let s:end_start_regex =
\ '\|\%(^\|[^.:@$]\)\@<=\<do:\@!\>'
" Regex that defines the middle-match for the 'end' keyword.
-let s:end_middle_regex = '\<\%(ensure\|else\|\%(\%(^\|;\)\s*\)\@<=\<rescue:\@!\>\|when\|elsif\):\@!\>'
+let s:end_middle_regex = '\<\%(ensure\|else\|\%(\%(^\|;\)\s*\)\@<=\<rescue:\@!\>\|when\|\%(\%(^\|;\)\s*\)\@<=\<in\|elsif\):\@!\>'
" Regex that defines the end-match for the 'end' keyword.
let s:end_end_regex = '\%(^\|[^.:@$]\)\@<=\<end:\@!\>'
-" Expression used for searchpair() call for finding match for 'end' keyword.
-let s:end_skip_expr = s:skip_expr .
- \ ' || (expand("<cword>") == "do"' .
- \ ' && getline(".") =~ "^\\s*\\<\\(while\\|until\\|for\\):\\@!\\>")'
+" Expression used for searchpair() call for finding a match for an 'end' keyword.
+function! s:EndSkipExpr()
+ if eval(s:skip_expr)
+ return 1
+ elseif expand('<cword>') == 'do'
+ \ && getline(".") =~ '^\s*\<\(while\|until\|for\):\@!\>'
+ return 1
+ elseif getline('.') =~ s:ruby_endless_def
+ return 1
+ elseif getline('.') =~ '\<def\s\+\k\+[!?]\=([^)]*$'
+ " Then it's a `def method(` with a possible `) =` later
+ call search('\<def\s\+\k\+\zs(', 'W', line('.'))
+ normal! %
+ return getline('.') =~ ')\s*='
+ else
+ return 0
+ endif
+endfunction
+
+let s:end_skip_expr = function('s:EndSkipExpr')
" Regex that defines continuation lines, not including (, {, or [.
let s:non_bracket_continuation_regex =
@@ -572,6 +593,11 @@ function! s:AfterUnbalancedBracket(pline_info) abort
call cursor(info.plnum, closing.pos + 1)
normal! %
+ if strpart(info.pline, closing.pos) =~ '^)\s*='
+ " special case: the closing `) =` of an endless def
+ return indent(s:GetMSL(line('.')))
+ endif
+
if s:Match(line('.'), s:ruby_indent_keywords)
return indent('.') + info.sw
else
@@ -610,7 +636,7 @@ function! s:AfterIndentKeyword(pline_info) abort
let info = a:pline_info
let col = s:Match(info.plnum, s:ruby_indent_keywords)
- if col > 0
+ if col > 0 && s:Match(info.plnum, s:ruby_endless_def) <= 0
call cursor(info.plnum, col)
let ind = virtcol('.') - 1 + info.sw
" TODO: make this better (we need to count them) (or, if a searchpair
@@ -657,7 +683,7 @@ function! s:IndentingKeywordInMSL(msl_info) abort
" TODO: this does not take into account contrived things such as
" module Foo; class Bar; end
let col = s:Match(info.plnum_msl, s:ruby_indent_keywords)
- if col > 0
+ if col > 0 && s:Match(info.plnum_msl, s:ruby_endless_def) <= 0
let ind = indent(info.plnum_msl) + info.sw
if s:Match(info.plnum_msl, s:end_end_regex)
let ind = ind - info.sw
diff --git a/runtime/indent/sas.vim b/runtime/indent/sas.vim
index 9cc9e025c4..bbbbbf02eb 100644
--- a/runtime/indent/sas.vim
+++ b/runtime/indent/sas.vim
@@ -2,7 +2,7 @@
" Language: SAS
" Maintainer: Zhen-Huan Hu <wildkeny@gmail.com>
" Version: 3.0.3
-" Last Change: Jun 26, 2018
+" Last Change: 2022 Apr 06
if exists("b:did_indent")
finish
@@ -12,6 +12,8 @@ let b:did_indent = 1
setlocal indentexpr=GetSASIndent()
setlocal indentkeys+=;,=~data,=~proc,=~macro
+let b:undo_indent = "setl inde< indk<"
+
if exists("*GetSASIndent")
finish
endif
diff --git a/runtime/indent/sass.vim b/runtime/indent/sass.vim
index d6dbf3a8bb..8c0ecd0746 100644
--- a/runtime/indent/sass.vim
+++ b/runtime/indent/sass.vim
@@ -1,7 +1,7 @@
" Vim indent file
" Language: Sass
" Maintainer: Tim Pope <vimNOSPAM@tpope.org>
-" Last Change: 2017 Jun 13
+" Last Change: 2022 Mar 15
if exists("b:did_indent")
finish
@@ -12,6 +12,8 @@ setlocal autoindent sw=2 et
setlocal indentexpr=GetSassIndent()
setlocal indentkeys=o,O,*<Return>,<:>,!^F
+let b:undo_indent = "setl ai< inde< indk<"
+
" Only define the function once.
if exists("*GetSassIndent")
finish
diff --git a/runtime/indent/sml.vim b/runtime/indent/sml.vim
index e760a8e350..a0b0c3e911 100644
--- a/runtime/indent/sml.vim
+++ b/runtime/indent/sml.vim
@@ -7,10 +7,11 @@
" Mike Leary <leary@nwlink.com>
" Markus Mottl <markus@oefai.at>
" OCaml URL: http://www.oefai.at/~markus/vim/indent/ocaml.vim
-" Last Change: 2003 Jan 04 - Adapted to SML
+" Last Change: 2022 Apr 06
" 2002 Nov 06 - Some fixes (JY)
" 2002 Oct 28 - Fixed bug with indentation of ']' (MM)
" 2002 Oct 22 - Major rewrite (JY)
+" 2022 April: b:undo_indent added by Doug Kearns
" Only load this indent file when no other was loaded.
if exists("b:did_indent")
@@ -26,6 +27,8 @@ setlocal nosmartindent
setlocal textwidth=80
setlocal shiftwidth=2
+let b:undo_indent = "setl et< inde< indk< lisp< si< sw< tw<"
+
" Comment formatting
if (has("comments"))
set comments=sr:(*,mb:*,ex:*)
diff --git a/runtime/indent/systemverilog.vim b/runtime/indent/systemverilog.vim
index 16fb4515c5..f6114dc1fd 100644
--- a/runtime/indent/systemverilog.vim
+++ b/runtime/indent/systemverilog.vim
@@ -2,6 +2,7 @@
" Language: SystemVerilog
" Maintainer: kocha <kocha.lsifrontend@gmail.com>
" Last Change: 05-Feb-2017 by Bilal Wasim
+" 2022 April: b:undo_indent added by Doug Kearns
" Only load this indent file when no other was loaded.
if exists("b:did_indent")
@@ -16,6 +17,8 @@ setlocal indentkeys+==endclass,=endpackage,=endsequence,=endclocking
setlocal indentkeys+==endinterface,=endgroup,=endprogram,=endproperty,=endchecker
setlocal indentkeys+==`else,=`endif
+let b:undo_indent = "setl inde< indk<"
+
" Only define the function once.
if exists("*SystemVerilogIndent")
finish
diff --git a/runtime/indent/testdir/html.in b/runtime/indent/testdir/html.in
index 1acf8c0402..b62c67ddb2 100644
--- a/runtime/indent/testdir/html.in
+++ b/runtime/indent/testdir/html.in
@@ -1,4 +1,4 @@
-" vim: set ft=html sw=4 :
+" vim: set ft=html sw=4 ts=8 :
" START_INDENT
@@ -41,6 +41,11 @@ dd text
dt text
</dt>
</dl>
+<div
+class="test"
+style="color: yellow">
+text
+</div>
</body>
</html>
@@ -50,6 +55,7 @@ dt text
% START_INDENT
% INDENT_EXE let g:html_indent_style1 = "inc"
% INDENT_EXE let g:html_indent_script1 = "zero"
+% INDENT_EXE let g:html_indent_attribute = 1
% INDENT_EXE call HtmlIndent_CheckUserSettings()
<html>
<body>
@@ -61,6 +67,11 @@ div#d2 { color: green; }
var v1 = "v1";
var v2 = "v2";
</script>
+<div
+class="test"
+style="color: yellow">
+text
+</div>
</body>
</html>
% END_INDENT
diff --git a/runtime/indent/testdir/html.ok b/runtime/indent/testdir/html.ok
index c0dfc9dc72..938e965d8c 100644
--- a/runtime/indent/testdir/html.ok
+++ b/runtime/indent/testdir/html.ok
@@ -1,4 +1,4 @@
-" vim: set ft=html sw=4 :
+" vim: set ft=html sw=4 ts=8 :
" START_INDENT
@@ -41,6 +41,11 @@ div#d2 { color: green; }
dt text
</dt>
</dl>
+ <div
+ class="test"
+ style="color: yellow">
+ text
+ </div>
</body>
</html>
@@ -50,6 +55,7 @@ div#d2 { color: green; }
% START_INDENT
% INDENT_EXE let g:html_indent_style1 = "inc"
% INDENT_EXE let g:html_indent_script1 = "zero"
+% INDENT_EXE let g:html_indent_attribute = 1
% INDENT_EXE call HtmlIndent_CheckUserSettings()
<html>
<body>
@@ -61,6 +67,11 @@ div#d2 { color: green; }
var v1 = "v1";
var v2 = "v2";
</script>
+ <div
+ class="test"
+ style="color: yellow">
+ text
+ </div>
</body>
</html>
% END_INDENT
diff --git a/runtime/indent/testdir/krl.in b/runtime/indent/testdir/krl.in
new file mode 100644
index 0000000000..ec90feb08b
--- /dev/null
+++ b/runtime/indent/testdir/krl.in
@@ -0,0 +1,148 @@
+; vim: set ft=krl :
+
+; START_INDENT
+
+def One()
+int i
+If i==1 then
+While i>=1
+For i=1 to 5 step 2
+Loop
+i = i+1
+EndLoop
+EndFor
+EndWhile
+Else
+Repeat
+Switch i
+Case 1
+Skip 123
+i = i+1
+EndSkip 123
+Spline with $acc=100, $vel.cp=3
+slin {x 100}
+scirc {x 110, y 110}, {x 120, y 90}
+slin {x 200} c_dis
+Time_Block Start
+slin {x 300} c_dis
+Time_Block Part = 22.2
+slin {y 400} c_dis
+Time_Block Part = 33.3
+Time_Block End = 10
+slin {y 200} c_dis
+Const_Vel Start +100 OnStart
+slin {y 300} c_dis
+slin {x 100}
+Const_Vel End -5.5
+slin {y 200} c_dis
+EndSpline
+Case 2,3
+PTP_Spline with $acc=100, $vel.ptp=100
+sptp {a1 0} c_ptp
+sptp {a1 90}
+EndSpline c_spl
+Default
+i = i+1
+EndSwitch
+Continue
+Until False
+EndIf
+end
+
+DEF Two()
+int i
+END
+
+global def Three()
+int i
+end
+
+GLOBAL DEF Four()
+int i
+END
+
+Global Def Five()
+int i
+End
+
+deffct bool fOne()
+int i
+endfct
+
+DEFFCT bool fTwo()
+int i
+ENDFCT
+
+global deffct bool fThree()
+int i
+endfct
+
+GLOBAL DEFFCT bool fFour()
+int i
+ENDFCT
+
+Global DefFct bool fFive()
+int i
+EndFct
+
+DefDat datfile()
+global int i=1
+; don't indent column 1 comments unless g:krlCommentIndent is set
+; global int o=2
+EndDat
+
+; END_INDENT
+
+; START_INDENT
+; INDENT_EXE let g:krlSpaceIndent = 0
+; INDENT_EXE set shiftwidth=4
+
+def bla()
+int i
+end
+
+; END_INDENT
+
+; START_INDENT
+; INDENT_EXE let g:krlCommentIndent = 1
+def bla()
+; indent this first column comment because of g:krlCommentIndent=1
+end
+; END_INDENT
+
+; START_INDENT
+; INDENT_EXE let g:krlIndentBetweenDef = 0
+def bla()
+int i ; don't indent this line because of g:krlIndentBetweenDef=0
+end
+; END_INDENT
+
+; START_INDENT
+; INDENT_AT this-line
+def Some()
+int f
+if true then
+f = 1 ; this-line
+endif
+end
+; END_INDENT
+
+; START_INDENT
+; INDENT_NEXT next-line
+def Some()
+ int i
+ ; next-line
+i = 1 ; should get indent of line 'int i' above
+end
+; END_INDENT
+
+; START_INDENT
+; INDENT_PREV prev-line
+def Some()
+int f
+if true then
+f = 1
+; prev-line
+endif
+end
+; END_INDENT
diff --git a/runtime/indent/testdir/krl.ok b/runtime/indent/testdir/krl.ok
new file mode 100644
index 0000000000..34dc0f57b1
--- /dev/null
+++ b/runtime/indent/testdir/krl.ok
@@ -0,0 +1,148 @@
+; vim: set ft=krl :
+
+; START_INDENT
+
+def One()
+ int i
+ If i==1 then
+ While i>=1
+ For i=1 to 5 step 2
+ Loop
+ i = i+1
+ EndLoop
+ EndFor
+ EndWhile
+ Else
+ Repeat
+ Switch i
+ Case 1
+ Skip 123
+ i = i+1
+ EndSkip 123
+ Spline with $acc=100, $vel.cp=3
+ slin {x 100}
+ scirc {x 110, y 110}, {x 120, y 90}
+ slin {x 200} c_dis
+ Time_Block Start
+ slin {x 300} c_dis
+ Time_Block Part = 22.2
+ slin {y 400} c_dis
+ Time_Block Part = 33.3
+ Time_Block End = 10
+ slin {y 200} c_dis
+ Const_Vel Start +100 OnStart
+ slin {y 300} c_dis
+ slin {x 100}
+ Const_Vel End -5.5
+ slin {y 200} c_dis
+ EndSpline
+ Case 2,3
+ PTP_Spline with $acc=100, $vel.ptp=100
+ sptp {a1 0} c_ptp
+ sptp {a1 90}
+ EndSpline c_spl
+ Default
+ i = i+1
+ EndSwitch
+ Continue
+ Until False
+ EndIf
+end
+
+DEF Two()
+ int i
+END
+
+global def Three()
+ int i
+end
+
+GLOBAL DEF Four()
+ int i
+END
+
+Global Def Five()
+ int i
+End
+
+deffct bool fOne()
+ int i
+endfct
+
+DEFFCT bool fTwo()
+ int i
+ENDFCT
+
+global deffct bool fThree()
+ int i
+endfct
+
+GLOBAL DEFFCT bool fFour()
+ int i
+ENDFCT
+
+Global DefFct bool fFive()
+ int i
+EndFct
+
+DefDat datfile()
+ global int i=1
+; don't indent column 1 comments unless g:krlCommentIndent is set
+; global int o=2
+EndDat
+
+; END_INDENT
+
+; START_INDENT
+; INDENT_EXE let g:krlSpaceIndent = 0
+; INDENT_EXE set shiftwidth=4
+
+def bla()
+ int i
+end
+
+; END_INDENT
+
+; START_INDENT
+; INDENT_EXE let g:krlCommentIndent = 1
+def bla()
+ ; indent this first column comment because of g:krlCommentIndent=1
+end
+; END_INDENT
+
+; START_INDENT
+; INDENT_EXE let g:krlIndentBetweenDef = 0
+def bla()
+int i ; don't indent this line because of g:krlIndentBetweenDef=0
+end
+; END_INDENT
+
+; START_INDENT
+; INDENT_AT this-line
+def Some()
+int f
+if true then
+ f = 1 ; this-line
+endif
+end
+; END_INDENT
+
+; START_INDENT
+; INDENT_NEXT next-line
+def Some()
+ int i
+ ; next-line
+ i = 1 ; should get indent of line 'int i' above
+end
+; END_INDENT
+
+; START_INDENT
+; INDENT_PREV prev-line
+def Some()
+int f
+if true then
+ f = 1
+; prev-line
+endif
+end
+; END_INDENT
diff --git a/runtime/indent/testdir/vim.in b/runtime/indent/testdir/vim.in
index 699e4c243d..873045bc2c 100644
--- a/runtime/indent/testdir/vim.in
+++ b/runtime/indent/testdir/vim.in
@@ -30,6 +30,12 @@ for x in [
eval 0
endfor
+let t = [
+\ {
+\ 'k': 'val',
+\ },
+\ ]
+
" END_INDENT
" START_INDENT
diff --git a/runtime/indent/testdir/vim.ok b/runtime/indent/testdir/vim.ok
index f597d97e80..8e70abe619 100644
--- a/runtime/indent/testdir/vim.ok
+++ b/runtime/indent/testdir/vim.ok
@@ -30,6 +30,12 @@ for x in [
eval 0
endfor
+let t = [
+ \ {
+ \ 'k': 'val',
+ \ },
+ \ ]
+
" END_INDENT
" START_INDENT
diff --git a/runtime/indent/testdir/yaml.in b/runtime/indent/testdir/yaml.in
index 8515e1752c..bf99668da2 100644
--- a/runtime/indent/testdir/yaml.in
+++ b/runtime/indent/testdir/yaml.in
@@ -14,6 +14,7 @@ map: val
# END_INDENT
# START_INDENT
-map: multiline
-value
+map: |
+line1
+line2
# END_INDENT
diff --git a/runtime/indent/testdir/yaml.ok b/runtime/indent/testdir/yaml.ok
index 5ca2871fc9..8b38633e71 100644
--- a/runtime/indent/testdir/yaml.ok
+++ b/runtime/indent/testdir/yaml.ok
@@ -14,6 +14,7 @@ map: val
# END_INDENT
# START_INDENT
-map: multiline
- value
+map: |
+ line1
+ line2
# END_INDENT
diff --git a/runtime/indent/tf.vim b/runtime/indent/tf.vim
index a980538506..bb40bf6f01 100644
--- a/runtime/indent/tf.vim
+++ b/runtime/indent/tf.vim
@@ -2,7 +2,7 @@
" Language: tf (TinyFugue)
" Maintainer: Christian J. Robinson <heptite@gmail.com>
" URL: http://www.vim.org/scripts/script.php?script_id=174
-" Last Change: 2017 Feb 25
+" Last Change: 2022 Apr 25
" Only load this indent file when no other was loaded.
if exists("b:did_indent")
@@ -14,6 +14,8 @@ setlocal indentexpr=GetTFIndent()
setlocal indentkeys-=0{,0} indentkeys-=0# indentkeys-=:
setlocal indentkeys+==/endif,=/then,=/else,=/done,0;
+let b:undo_indent = "setlocal indentexpr< indentkeys<"
+
" Only define the function once:
if exists("*GetTFIndent")
finish
diff --git a/runtime/indent/tilde.vim b/runtime/indent/tilde.vim
index e722a0994c..8658035f72 100644
--- a/runtime/indent/tilde.vim
+++ b/runtime/indent/tilde.vim
@@ -1,7 +1,8 @@
"Description: Indent scheme for the tilde weblanguage
-"Author: Tobias Rundström <tobi@tobi.nu>
+"Author: Tobias Rundström <tobi@tobi.nu> (Invalid email address)
"URL: http://tilde.tildesoftware.net
"Last Change: May 8 09:15:09 CEST 2002
+" 2022 April: b:undo_indent added by Doug Kearns
if exists ("b:did_indent")
finish
@@ -13,6 +14,8 @@ setlocal autoindent
setlocal indentexpr=GetTildeIndent(v:lnum)
setlocal indentkeys=o,O,)
+let b:undo_indent = "setl ai< inde< indk<"
+
if exists("*GetTildeIndent")
finish
endif
diff --git a/runtime/indent/treetop.vim b/runtime/indent/treetop.vim
index 42ec1c8ad9..6c39c69687 100644
--- a/runtime/indent/treetop.vim
+++ b/runtime/indent/treetop.vim
@@ -1,7 +1,8 @@
" Vim indent file
-" Language: Treetop
-" Previous Maintainer: Nikolai Weibull <now@bitwi.se>
-" Latest Revision: 2011-03-14
+" Language: Treetop
+" Maintainer: Doug Kearns <dougkearns@gmail.com>
+" Previous Maintainer: Nikolai Weibull <now@bitwi.se>
+" Last Change: 2022 April 25
if exists("b:did_indent")
finish
@@ -12,6 +13,8 @@ setlocal indentexpr=GetTreetopIndent()
setlocal indentkeys=0{,0},!^F,o,O,=end
setlocal nosmartindent
+let b:undo_indent = "setl inde< indk< si<"
+
if exists("*GetTreetopIndent")
finish
endif
diff --git a/runtime/indent/vim.vim b/runtime/indent/vim.vim
index 7c03ff2873..8076b2df07 100644
--- a/runtime/indent/vim.vim
+++ b/runtime/indent/vim.vim
@@ -1,7 +1,7 @@
" Vim indent file
" Language: Vim script
" Maintainer: Bram Moolenaar <Bram@vim.org>
-" Last Change: 2021 Nov 27
+" Last Change: 2022 Jun 24
" Only load this indent file when no other was loaded.
if exists("b:did_indent")
@@ -10,7 +10,7 @@ endif
let b:did_indent = 1
setlocal indentexpr=GetVimIndent()
-setlocal indentkeys+==end,=},=else,=cat,=finall,=END,0\\,0=\"\\\
+setlocal indentkeys+==endif,=enddef,=endfu,=endfor,=endwh,=endtry,=},=else,=cat,=finall,=END,0\\,0=\"\\\
setlocal indentkeys-=0#
setlocal indentkeys-=:
@@ -36,6 +36,14 @@ endfunc
let s:lineContPat = '^\s*\(\\\|"\\ \)'
function GetVimIndentIntern()
+ " If the current line has line continuation and the previous one too, use
+ " the same indent. This does not skip empty lines.
+ let cur_text = getline(v:lnum)
+ let cur_has_linecont = cur_text =~ s:lineContPat
+ if cur_has_linecont && v:lnum > 1 && getline(v:lnum - 1) =~ s:lineContPat
+ return indent(v:lnum - 1)
+ endif
+
" Find a non-blank line above the current line.
let lnum = prevnonblank(v:lnum - 1)
@@ -44,8 +52,7 @@ function GetVimIndentIntern()
" If the current line doesn't start with '\' or '"\ ' and below a line that
" starts with '\' or '"\ ', use the indent of the line above it.
- let cur_text = getline(v:lnum)
- if cur_text !~ s:lineContPat
+ if !cur_has_linecont
while lnum > 0 && getline(lnum) =~ s:lineContPat
let lnum = lnum - 1
endwhile
@@ -103,10 +110,11 @@ function GetVimIndentIntern()
" A line starting with :au does not increment/decrement indent.
" A { may start a block or a dict. Assume that when a } follows it's a
" terminated dict.
+ " ":function" starts a block but "function(" doesn't.
if prev_text !~ '^\s*au\%[tocmd]' && prev_text !~ '^\s*{.*}'
- let i = match(prev_text, '\(^\||\)\s*\(export\s\+\)\?\({\|\(if\|wh\%[ile]\|for\|try\|cat\%[ch]\|fina\|finall\%[y]\|fu\%[nction]\|def\|el\%[seif]\)\>\)')
+ let i = match(prev_text, '\(^\||\)\s*\(export\s\+\)\?\({\|\(if\|wh\%[ile]\|for\|try\|cat\%[ch]\|fina\|finall\%[y]\|def\|el\%[seif]\)\>\|fu\%[nction][! ]\)')
if i >= 0
- let ind += shiftwidth()
+ let ind += shiftwidth()
if strpart(prev_text, i, 1) == '|' && has('syntax_items')
\ && synIDattr(synID(lnum, i, 1), "name") =~ '\(Comment\|String\|PatSep\)$'
let ind -= shiftwidth()
@@ -170,10 +178,15 @@ function GetVimIndentIntern()
let ind = ind + shiftwidth()
endif
- " Subtract a 'shiftwidth' on a :endif, :endwhile, :catch, :finally, :endtry,
- " :endfun, :else and :augroup END.
- if cur_text =~ '^\s*\(ene\@!\|cat\|finall\|el\|aug\%[roup]\s\+[eE][nN][dD]\)'
+ " Subtract a 'shiftwidth' on a :endif, :endwhile, :endfor, :catch, :finally,
+ " :endtry, :endfun, :enddef, :else and :augroup END.
+ " Although ":en" would be enough only match short command names as in
+ " 'indentkeys'.
+ if cur_text =~ '^\s*\(endif\|endwh\|endfor\|endtry\|endfu\|enddef\|cat\|finall\|else\|aug\%[roup]\s\+[eE][nN][dD]\)'
let ind = ind - shiftwidth()
+ if ind < 0
+ let ind = 0
+ endif
endif
return ind
diff --git a/runtime/indent/xf86conf.vim b/runtime/indent/xf86conf.vim
index 5a8bc0fec1..834eb00b25 100644
--- a/runtime/indent/xf86conf.vim
+++ b/runtime/indent/xf86conf.vim
@@ -1,7 +1,8 @@
" Vim indent file
-" Language: XFree86 Configuration File
-" Previous Maintainer: Nikolai Weibull <now@bitwi.se>
-" Latest Revision: 2006-12-20
+" Language: XFree86 Configuration File
+" Maintainer: Doug Kearns <dougkearns@gmail.com>
+" Previous Maintainer: Nikolai Weibull <now@bitwi.se>
+" Last Change: 2022 April 25
if exists("b:did_indent")
finish
@@ -12,6 +13,8 @@ setlocal indentexpr=GetXF86ConfIndent()
setlocal indentkeys=!^F,o,O,=End
setlocal nosmartindent
+let b:undo_indent = "setl inde< indk< si<"
+
if exists("*GetXF86ConfIndent")
finish
endif
diff --git a/runtime/indent/xinetd.vim b/runtime/indent/xinetd.vim
index 977ee3d33b..21000b7bc4 100644
--- a/runtime/indent/xinetd.vim
+++ b/runtime/indent/xinetd.vim
@@ -1,7 +1,8 @@
" Vim indent file
-" Language: xinetd.conf(5) configuration file
-" Previous Maintainer: Nikolai Weibull <now@bitwi.se>
-" Latest Revision: 2006-12-20
+" Language: xinetd.conf(5) configuration file
+" Maintainer: Doug Kearns <dougkearns@gmail.com>
+" Previous Maintainer: Nikolai Weibull <now@bitwi.se>
+" Last Change: 2022 April 25
if exists("b:did_indent")
finish
@@ -12,6 +13,8 @@ setlocal indentexpr=GetXinetdIndent()
setlocal indentkeys=0{,0},!^F,o,O
setlocal nosmartindent
+let b:undo_indent = "setl inde< indk< si<"
+
if exists("*GetXinetdIndent")
finish
endif
diff --git a/runtime/indent/yacc.vim b/runtime/indent/yacc.vim
index 01ad4fc56d..253ccc5371 100644
--- a/runtime/indent/yacc.vim
+++ b/runtime/indent/yacc.vim
@@ -1,7 +1,8 @@
" Vim indent file
-" Language: YACC input file
-" Previous Maintainer: Nikolai Weibull <now@bitwi.se>
-" Latest Revision: 2006-12-20
+" Language: YACC input file
+" Maintainer: Doug Kearns <dougkearns@gmail.com>
+" Previous Maintainer: Nikolai Weibull <now@bitwi.se>
+" Last Change: 2022 April 25
" Only load this indent file when no other was loaded.
if exists("b:did_indent")
@@ -14,6 +15,8 @@ setlocal indentexpr=GetYaccIndent()
setlocal indentkeys=!^F,o,O
setlocal nosmartindent
+let b:undo_indent = "setl inde< indk< si<"
+
" Only define the function once.
if exists("*GetYaccIndent")
finish
diff --git a/runtime/indent/yaml.vim b/runtime/indent/yaml.vim
index ed57e68d8b..d732c37c05 100644
--- a/runtime/indent/yaml.vim
+++ b/runtime/indent/yaml.vim
@@ -2,7 +2,7 @@
" Language: YAML
" Maintainer: Nikolai Pavlov <zyx.vim@gmail.com>
" Last Update: Lukas Reineke
-" Last Change: 2021 Aug 13
+" Last Change: 2022 Jun 17
" Only load this indent file when no other was loaded.
if exists('b:did_indent')
@@ -44,30 +44,30 @@ function s:FindPrevLEIndentedLineMatchingRegex(lnum, regex)
return plilnum
endfunction
-let s:mapkeyregex='\v^\s*\#@!\S@=%(\''%([^'']|\''\'')*\'''.
- \ '|\"%([^"\\]|\\.)*\"'.
+let s:mapkeyregex = '\v^\s*\#@!\S@=%(\''%([^'']|\''\'')*\''' ..
+ \ '|\"%([^"\\]|\\.)*\"' ..
\ '|%(%(\:\ )@!.)*)\:%(\ |$)'
-let s:liststartregex='\v^\s*%(\-%(\ |$))'
+let s:liststartregex = '\v^\s*%(\-%(\ |$))'
let s:c_ns_anchor_char = '\v%([\n\r\uFEFF \t,[\]{}]@!\p)'
-let s:c_ns_anchor_name = s:c_ns_anchor_char.'+'
-let s:c_ns_anchor_property = '\v\&'.s:c_ns_anchor_name
+let s:c_ns_anchor_name = s:c_ns_anchor_char .. '+'
+let s:c_ns_anchor_property = '\v\&' .. s:c_ns_anchor_name
let s:ns_word_char = '\v[[:alnum:]_\-]'
-let s:ns_tag_char = '\v%('.s:ns_word_char.'|[#/;?:@&=+$.~*''()])'
-let s:c_named_tag_handle = '\v\!'.s:ns_word_char.'+\!'
+let s:ns_tag_char = '\v%(%\x\x|' .. s:ns_word_char .. '|[#/;?:@&=+$.~*''()])'
+let s:c_named_tag_handle = '\v\!' .. s:ns_word_char .. '+\!'
let s:c_secondary_tag_handle = '\v\!\!'
let s:c_primary_tag_handle = '\v\!'
-let s:c_tag_handle = '\v%('.s:c_named_tag_handle.
- \ '|'.s:c_secondary_tag_handle.
- \ '|'.s:c_primary_tag_handle.')'
-let s:c_ns_shorthand_tag = '\v'.s:c_tag_handle . s:ns_tag_char.'+'
+let s:c_tag_handle = '\v%(' .. s:c_named_tag_handle.
+ \ '|' .. s:c_secondary_tag_handle.
+ \ '|' .. s:c_primary_tag_handle .. ')'
+let s:c_ns_shorthand_tag = '\v' .. s:c_tag_handle .. s:ns_tag_char .. '+'
let s:c_non_specific_tag = '\v\!'
-let s:ns_uri_char = '\v%('.s:ns_word_char.'\v|[#/;?:@&=+$,.!~*''()[\]])'
-let s:c_verbatim_tag = '\v\!\<'.s:ns_uri_char.'+\>'
-let s:c_ns_tag_property = '\v'.s:c_verbatim_tag.
- \ '\v|'.s:c_ns_shorthand_tag.
- \ '\v|'.s:c_non_specific_tag
+let s:ns_uri_char = '\v%(%\x\x|' .. s:ns_word_char .. '\v|[#/;?:@&=+$,.!~*''()[\]])'
+let s:c_verbatim_tag = '\v\!\<' .. s:ns_uri_char.. '+\>'
+let s:c_ns_tag_property = '\v' .. s:c_verbatim_tag.
+ \ '\v|' .. s:c_ns_shorthand_tag.
+ \ '\v|' .. s:c_non_specific_tag
let s:block_scalar_header = '\v[|>]%([+-]?[1-9]|[1-9]?[+-])?'
@@ -142,9 +142,9 @@ function GetYAMLIndent(lnum)
" - List with
" multiline scalar
return previndent+2
- elseif prevline =~# s:mapkeyregex . '\v\s*%(%('.s:c_ns_tag_property.
- \ '\v|'.s:c_ns_anchor_property.
- \ '\v|'.s:block_scalar_header.
+ elseif prevline =~# s:mapkeyregex .. '\v\s*%(%(' .. s:c_ns_tag_property ..
+ \ '\v|' .. s:c_ns_anchor_property ..
+ \ '\v|' .. s:block_scalar_header ..
\ '\v)%(\s+|\s*%(\#.*)?$))*'
" Mapping with: value
" that is multiline scalar
diff --git a/runtime/lua/health.lua b/runtime/lua/health.lua
index 142a353bf2..40e2b3c3e7 100644
--- a/runtime/lua/health.lua
+++ b/runtime/lua/health.lua
@@ -1,23 +1,6 @@
-local M = {}
-
-function M.report_start(msg)
- vim.fn['health#report_start'](msg)
-end
-
-function M.report_info(msg)
- vim.fn['health#report_info'](msg)
-end
-
-function M.report_ok(msg)
- vim.fn['health#report_ok'](msg)
-end
-
-function M.report_warn(msg, ...)
- vim.fn['health#report_warn'](msg, ...)
-end
-
-function M.report_error(msg, ...)
- vim.fn['health#report_error'](msg, ...)
-end
-
-return M
+return setmetatable({}, {
+ __index = function(_, k)
+ vim.deprecate("require('health')", 'vim.health', '0.9', false)
+ return vim.health[k]
+ end,
+})
diff --git a/runtime/lua/man.lua b/runtime/lua/man.lua
index ba6b9d09c9..5da3d2a92f 100644
--- a/runtime/lua/man.lua
+++ b/runtime/lua/man.lua
@@ -8,7 +8,7 @@ local function highlight_line(line, linenr)
local overstrike, escape = false, false
local hls = {} -- Store highlight groups as { attr, start, final }
local NONE, BOLD, UNDERLINE, ITALIC = 0, 1, 2, 3
- local hl_groups = {[BOLD]="manBold", [UNDERLINE]="manUnderline", [ITALIC]="manItalic"}
+ local hl_groups = { [BOLD] = 'manBold', [UNDERLINE] = 'manUnderline', [ITALIC] = 'manItalic' }
local attr = NONE
local byte = 0 -- byte offset
@@ -47,7 +47,7 @@ local function highlight_line(line, linenr)
end
if continue_hl then
- hls[#hls + 1] = {attr=attr, start=byte, final=-1}
+ hls[#hls + 1] = { attr = attr, start = byte, final = -1 }
else
if attr == NONE then
for a, _ in pairs(hl_groups) do
@@ -63,7 +63,7 @@ local function highlight_line(line, linenr)
-- can be represented in one byte. Any code point above that is represented by
-- a leading byte (0xc0 and above) and continuation bytes (0x80 to 0xbf, or
-- decimal 128 to 191).
- for char in line:gmatch("[^\128-\191][\128-\191]*") do
+ for char in line:gmatch('[^\128-\191][\128-\191]*') do
if overstrike then
local last_hl = hls[#hls]
if char == prev_char then
@@ -93,7 +93,7 @@ local function highlight_line(line, linenr)
if last_hl and last_hl.attr == attr and last_hl.final == byte then
last_hl.final = byte + #char
else
- hls[#hls + 1] = {attr=attr, start=byte, final=byte + #char}
+ hls[#hls + 1] = { attr = attr, start = byte, final = byte + #char }
end
overstrike = false
@@ -106,25 +106,25 @@ local function highlight_line(line, linenr)
-- We only want to match against SGR sequences, which consist of ESC
-- followed by '[', then a series of parameter and intermediate bytes in
-- the range 0x20 - 0x3f, then 'm'. (See ECMA-48, sections 5.4 & 8.3.117)
- local sgr = prev_char:match("^%[([\032-\063]*)m$")
+ local sgr = prev_char:match('^%[([\032-\063]*)m$')
-- Ignore escape sequences with : characters, as specified by ITU's T.416
-- Open Document Architecture and interchange format.
- if sgr and not string.find(sgr, ":") then
+ if sgr and not string.find(sgr, ':') then
local match
while sgr and #sgr > 0 do
-- Match against SGR parameters, which may be separated by ';'
- match, sgr = sgr:match("^(%d*);?(.*)")
+ match, sgr = sgr:match('^(%d*);?(.*)')
add_attr_hl(match + 0) -- coerce to number
end
escape = false
- elseif not prev_char:match("^%[[\032-\063]*$") then
+ elseif not prev_char:match('^%[[\032-\063]*$') then
-- Stop looking if this isn't a partial CSI sequence
escape = false
end
- elseif char == "\027" then
+ elseif char == '\027' then
escape = true
prev_char = ''
- elseif char == "\b" then
+ elseif char == '\b' then
overstrike = true
prev_char = chars[#chars]
byte = byte - #prev_char
@@ -143,7 +143,7 @@ local function highlight_line(line, linenr)
hl_groups[hl.attr],
linenr - 1,
hl.start,
- hl.final
+ hl.final,
}
end
end
@@ -152,8 +152,8 @@ local function highlight_line(line, linenr)
end
local function highlight_man_page()
- local mod = vim.api.nvim_buf_get_option(0, "modifiable")
- vim.api.nvim_buf_set_option(0, "modifiable", true)
+ local mod = vim.api.nvim_buf_get_option(0, 'modifiable')
+ vim.api.nvim_buf_set_option(0, 'modifiable', true)
local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false)
for i, line in ipairs(lines) do
@@ -166,7 +166,7 @@ local function highlight_man_page()
end
buf_hls = {}
- vim.api.nvim_buf_set_option(0, "modifiable", mod)
+ vim.api.nvim_buf_set_option(0, 'modifiable', mod)
end
return { highlight_man_page = highlight_man_page }
diff --git a/runtime/lua/vim/F.lua b/runtime/lua/vim/F.lua
index 9327c652db..bca5ddf68b 100644
--- a/runtime/lua/vim/F.lua
+++ b/runtime/lua/vim/F.lua
@@ -5,13 +5,17 @@ local F = {}
---@param a
---@param b
function F.if_nil(a, b)
- if a == nil then return b end
+ if a == nil then
+ return b
+ end
return a
end
-- Use in combination with pcall
function F.ok_or_nil(status, ...)
- if not status then return end
+ if not status then
+ return
+ end
return ...
end
@@ -29,7 +33,7 @@ end
--- like {...} except preserve the length explicitly
function F.pack_len(...)
- return {n=select('#', ...), ...}
+ return { n = select('#', ...), ... }
end
--- like unpack() but use the length set by F.pack_len if present
diff --git a/src/nvim/lua/vim.lua b/runtime/lua/vim/_editor.lua
index 731e7d8d36..442d7b07d8 100644
--- a/src/nvim/lua/vim.lua
+++ b/runtime/lua/vim/_editor.lua
@@ -3,9 +3,11 @@
-- Lua code lives in one of three places:
-- 1. runtime/lua/vim/ (the runtime): For "nice to have" features, e.g. the
-- `inspect` and `lpeg` modules.
--- 2. runtime/lua/vim/shared.lua: Code shared between Nvim and tests.
--- (This will go away if we migrate to nvim as the test-runner.)
--- 3. src/nvim/lua/: Compiled-into Nvim itself.
+-- 2. runtime/lua/vim/shared.lua: pure lua functions which always
+-- are available. Used in the test runner, as well as worker threads
+-- and processes launched from Nvim.
+-- 3. runtime/lua/vim/_editor.lua: Code which directly interacts with
+-- the Nvim editor state. Only available in the main thread.
--
-- Guideline: "If in doubt, put it in the runtime".
--
@@ -34,102 +36,34 @@
-- - https://github.com/bakpakin/Fennel (pretty print, repl)
-- - https://github.com/howl-editor/howl/tree/master/lib/howl/util
-local vim = vim
-assert(vim)
-
-vim.inspect = package.loaded['vim.inspect']
-assert(vim.inspect)
-
-vim.filetype = package.loaded['vim.filetype']
-assert(vim.filetype)
-
-local pathtrails = {}
-vim._so_trails = {}
-for s in (package.cpath..';'):gmatch('[^;]*;') do
- s = s:sub(1, -2) -- Strip trailing semicolon
- -- Find out path patterns. pathtrail should contain something like
- -- /?.so, \?.dll. This allows not to bother determining what correct
- -- suffixes are.
- local pathtrail = s:match('[/\\][^/\\]*%?.*$')
- if pathtrail and not pathtrails[pathtrail] then
- pathtrails[pathtrail] = true
- table.insert(vim._so_trails, pathtrail)
- end
-end
-
-function vim._load_package(name)
- local basename = name:gsub('%.', '/')
- local paths = {"lua/"..basename..".lua", "lua/"..basename.."/init.lua"}
- local found = vim.api.nvim__get_runtime(paths, false, {is_lua=true})
- if #found > 0 then
- local f, err = loadfile(found[1])
- return f or error(err)
- end
-
- local so_paths = {}
- for _,trail in ipairs(vim._so_trails) do
- local path = "lua"..trail:gsub('?', basename) -- so_trails contains a leading slash
- table.insert(so_paths, path)
- end
-
- found = vim.api.nvim__get_runtime(so_paths, false, {is_lua=true})
- if #found > 0 then
- -- Making function name in Lua 5.1 (see src/loadlib.c:mkfuncname) is
- -- a) strip prefix up to and including the first dash, if any
- -- b) replace all dots by underscores
- -- c) prepend "luaopen_"
- -- So "foo-bar.baz" should result in "luaopen_bar_baz"
- local dash = name:find("-", 1, true)
- local modname = dash and name:sub(dash + 1) or name
- local f, err = package.loadlib(found[1], "luaopen_"..modname:gsub("%.", "_"))
- return f or error(err)
- end
- return nil
-end
-
-table.insert(package.loaders, 1, vim._load_package)
+local vim = assert(vim)
-- These are for loading runtime modules lazily since they aren't available in
-- the nvim binary as specified in executor.c
-setmetatable(vim, {
- __index = function(t, key)
- if key == 'treesitter' then
- t.treesitter = require('vim.treesitter')
- return t.treesitter
- elseif key == 'F' then
- t.F = require('vim.F')
- return t.F
- elseif require('vim.uri')[key] ~= nil then
- -- Expose all `vim.uri` functions on the `vim` module.
- t[key] = require('vim.uri')[key]
- return t[key]
- elseif key == 'lsp' then
- t.lsp = require('vim.lsp')
- return t.lsp
- elseif key == 'highlight' then
- t.highlight = require('vim.highlight')
- return t.highlight
- elseif key == 'diagnostic' then
- t.diagnostic = require('vim.diagnostic')
- return t.diagnostic
- elseif key == 'ui' then
- t.ui = require('vim.ui')
- return t.ui
- elseif key == 'keymap' then
- t.keymap = require('vim.keymap')
- return t.keymap
- end
- end
-})
+for k, v in pairs({
+ treesitter = true,
+ filetype = true,
+ F = true,
+ lsp = true,
+ highlight = true,
+ diagnostic = true,
+ keymap = true,
+ ui = true,
+ health = true,
+ fs = true,
+}) do
+ vim._submodules[k] = v
+end
vim.log = {
levels = {
- TRACE = 0;
- DEBUG = 1;
- INFO = 2;
- WARN = 3;
- ERROR = 4;
- }
+ TRACE = 0,
+ DEBUG = 1,
+ INFO = 2,
+ WARN = 3,
+ ERROR = 4,
+ OFF = 5,
+ },
}
-- Internal-only until comments in #8107 are addressed.
@@ -147,14 +81,14 @@ function vim._os_proc_info(pid)
if pid == nil or pid <= 0 or type(pid) ~= 'number' then
error('invalid pid')
end
- local cmd = { 'ps', '-p', pid, '-o', 'comm=', }
+ local cmd = { 'ps', '-p', pid, '-o', 'comm=' }
local err, name = vim._system(cmd)
if 1 == err and vim.trim(name) == '' then
- return {} -- Process not found.
+ return {} -- Process not found.
elseif 0 ~= err then
- error('command failed: '..vim.fn.string(cmd))
+ error('command failed: ' .. vim.fn.string(cmd))
end
- local _, ppid = vim._system({ 'ps', '-p', pid, '-o', 'ppid=', })
+ local _, ppid = vim._system({ 'ps', '-p', pid, '-o', 'ppid=' })
-- Remove trailing whitespace.
name = vim.trim(name):gsub('^.*/', '')
ppid = tonumber(ppid) or -1
@@ -171,12 +105,12 @@ function vim._os_proc_children(ppid)
if ppid == nil or ppid <= 0 or type(ppid) ~= 'number' then
error('invalid ppid')
end
- local cmd = { 'pgrep', '-P', ppid, }
+ local cmd = { 'pgrep', '-P', ppid }
local err, rv = vim._system(cmd)
if 1 == err and vim.trim(rv) == '' then
- return {} -- Process not found.
+ return {} -- Process not found.
elseif 0 ~= err then
- error('command failed: '..vim.fn.string(cmd))
+ error('command failed: ' .. vim.fn.string(cmd))
end
local children = {}
for s in rv:gmatch('%S+') do
@@ -194,12 +128,12 @@ end
---
---@see https://github.com/kikito/inspect.lua
---@see https://github.com/mpeterv/vinspect
-local function inspect(object, options) -- luacheck: no unused
- error(object, options) -- Stub for gen_vimdoc.py
+local function inspect(object, options) -- luacheck: no unused
+ error(object, options) -- Stub for gen_vimdoc.py
end
do
- local tdots, tick, got_line1 = 0, 0, false
+ local tdots, tick, got_line1, undo_started, trailing_nl = 0, 0, false, false, false
--- Paste handler, invoked by |nvim_paste()| when a conforming UI
--- (such as the |TUI|) pastes text into the editor.
@@ -227,44 +161,80 @@ do
--- - 3: ends the paste (exactly once)
---@returns false if client should cancel the paste.
function vim.paste(lines, phase)
- local call = vim.api.nvim_call_function
local now = vim.loop.now()
- local mode = call('mode', {}):sub(1,1)
- if phase < 2 then -- Reset flags.
- tdots, tick, got_line1 = now, 0, false
- elseif mode ~= 'c' then
+ local is_first_chunk = phase < 2
+ local is_last_chunk = phase == -1 or phase == 3
+ if is_first_chunk then -- Reset flags.
+ tdots, tick, got_line1, undo_started, trailing_nl = now, 0, false, false, false
+ end
+ if #lines == 0 then
+ lines = { '' }
+ end
+ if #lines == 1 and lines[1] == '' and not is_last_chunk then
+ -- An empty chunk can cause some edge cases in streamed pasting,
+ -- so don't do anything unless it is the last chunk.
+ return true
+ end
+ -- Note: mode doesn't always start with "c" in cmdline mode, so use getcmdtype() instead.
+ if vim.fn.getcmdtype() ~= '' then -- cmdline-mode: paste only 1 line.
+ if not got_line1 then
+ got_line1 = (#lines > 1)
+ -- Escape control characters
+ local line1 = lines[1]:gsub('(%c)', '\022%1')
+ -- nvim_input() is affected by mappings,
+ -- so use nvim_feedkeys() with "n" flag to ignore mappings.
+ vim.api.nvim_feedkeys(line1, 'n', true)
+ end
+ return true
+ end
+ local mode = vim.api.nvim_get_mode().mode
+ if undo_started then
vim.api.nvim_command('undojoin')
end
- if mode == 'c' and not got_line1 then -- cmdline-mode: paste only 1 line.
- got_line1 = (#lines > 1)
- vim.api.nvim_set_option('paste', true) -- For nvim_input().
- local line1 = lines[1]:gsub('<', '<lt>'):gsub('[\r\n\012\027]', ' ') -- Scrub.
- vim.api.nvim_input(line1)
- vim.api.nvim_set_option('paste', false)
- elseif mode ~= 'c' then
- if phase < 2 and mode:find('^[vV\22sS\19]') then
- vim.api.nvim_command([[exe "normal! \<Del>"]])
- vim.api.nvim_put(lines, 'c', false, true)
- elseif phase < 2 and not mode:find('^[iRt]') then
- vim.api.nvim_put(lines, 'c', true, true)
- -- XXX: Normal-mode: workaround bad cursor-placement after first chunk.
- vim.api.nvim_command('normal! a')
- elseif phase < 2 and mode == 'R' then
- local nchars = 0
- for _, line in ipairs(lines) do
- nchars = nchars + line:len()
+ if mode:find('^i') or mode:find('^n?t') then -- Insert mode or Terminal buffer
+ vim.api.nvim_put(lines, 'c', false, true)
+ elseif phase < 2 and mode:find('^R') and not mode:find('^Rv') then -- Replace mode
+ -- TODO: implement Replace mode streamed pasting
+ -- TODO: support Virtual Replace mode
+ local nchars = 0
+ for _, line in ipairs(lines) do
+ nchars = nchars + line:len()
+ end
+ local row, col = unpack(vim.api.nvim_win_get_cursor(0))
+ local bufline = vim.api.nvim_buf_get_lines(0, row - 1, row, true)[1]
+ local firstline = lines[1]
+ firstline = bufline:sub(1, col) .. firstline
+ lines[1] = firstline
+ lines[#lines] = lines[#lines] .. bufline:sub(col + nchars + 1, bufline:len())
+ vim.api.nvim_buf_set_lines(0, row - 1, row, false, lines)
+ elseif mode:find('^[nvV\22sS\19]') then -- Normal or Visual or Select mode
+ if mode:find('^n') then -- Normal mode
+ -- When there was a trailing new line in the previous chunk,
+ -- the cursor is on the first character of the next line,
+ -- so paste before the cursor instead of after it.
+ vim.api.nvim_put(lines, 'c', not trailing_nl, false)
+ else -- Visual or Select mode
+ vim.api.nvim_command([[exe "silent normal! \<Del>"]])
+ local del_start = vim.fn.getpos("'[")
+ local cursor_pos = vim.fn.getpos('.')
+ if mode:find('^[VS]') then -- linewise
+ if cursor_pos[2] < del_start[2] then -- replacing lines at eof
+ -- create a new line
+ vim.api.nvim_put({ '' }, 'l', true, true)
+ end
+ vim.api.nvim_put(lines, 'c', false, false)
+ else
+ -- paste after cursor when replacing text at eol, otherwise paste before cursor
+ vim.api.nvim_put(lines, 'c', cursor_pos[3] < del_start[3], false)
end
- local row, col = unpack(vim.api.nvim_win_get_cursor(0))
- local bufline = vim.api.nvim_buf_get_lines(0, row-1, row, true)[1]
- local firstline = lines[1]
- firstline = bufline:sub(1, col)..firstline
- lines[1] = firstline
- lines[#lines] = lines[#lines]..bufline:sub(col + nchars + 1, bufline:len())
- vim.api.nvim_buf_set_lines(0, row-1, row, false, lines)
- else
- vim.api.nvim_put(lines, 'c', false, true)
end
+ -- put cursor at the end of the text instead of one character after it
+ vim.fn.setpos('.', vim.fn.getpos("']"))
+ trailing_nl = lines[#lines] == ''
+ else -- Don't know what to do in other modes
+ return false
end
+ undo_started = true
if phase ~= -1 and (now - tdots >= 100) then
local dots = ('.'):rep(tick % 4)
tdots = now
@@ -273,10 +243,10 @@ do
-- message when there are zero dots.
vim.api.nvim_command(('echo "%s"'):format(dots))
end
- if phase == -1 or phase == 3 then
- vim.api.nvim_command('redraw'..(tick > 1 and '|echo ""' or ''))
+ if is_last_chunk then
+ vim.api.nvim_command('redraw' .. (tick > 1 and '|echo ""' or ''))
end
- return true -- Paste will not continue if not returning `true`.
+ return true -- Paste will not continue if not returning `true`.
end
end
@@ -286,16 +256,12 @@ end
---@see |vim.schedule()|
---@see |vim.in_fast_event()|
function vim.schedule_wrap(cb)
- return (function (...)
+ return function(...)
local args = vim.F.pack_len(...)
- vim.schedule(function() cb(vim.F.unpack_len(args)) end)
- end)
-end
-
---- <Docs described in |vim.empty_dict()| >
----@private
-function vim.empty_dict()
- return setmetatable({}, vim._empty_dict_mt)
+ vim.schedule(function()
+ cb(vim.F.unpack_len(args))
+ end)
+ end
end
-- vim.fn.{func}(...)
@@ -304,7 +270,7 @@ vim.fn = setmetatable({}, {
local _fn
if vim.api[key] ~= nil then
_fn = function()
- error(string.format("Tried to call API function with vim.fn: use vim.api.%s instead", key))
+ error(string.format('Tried to call API function with vim.fn: use vim.api.%s instead', key))
end
else
_fn = function(...)
@@ -313,16 +279,40 @@ vim.fn = setmetatable({}, {
end
t[key] = _fn
return _fn
- end
+ end,
})
vim.funcref = function(viml_func_name)
return vim.fn[viml_func_name]
end
--- An easier alias for commands.
-vim.cmd = function(command)
- return vim.api.nvim_exec(command, false)
+--- Execute Vim script commands.
+---
+--- Example:
+--- <pre>
+--- vim.cmd('echo 42')
+--- vim.cmd([[
+--- augroup My_group
+--- autocmd!
+--- autocmd FileType c setlocal cindent
+--- augroup END
+--- ]])
+--- vim.cmd({ cmd = 'echo', args = { '"foo"' } })
+--- </pre>
+---
+---@param command string|table Command(s) to execute.
+--- If a string, executes multiple lines of Vim script at once. In this
+--- case, it is an alias to |nvim_exec()|, where `output` is set to
+--- false. Thus it works identical to |:source|.
+--- If a table, executes a single command. In this case, it is an alias
+--- to |nvim_cmd()| where `opts` is empty.
+---@see |ex-cmd-index|
+function vim.cmd(command)
+ if type(command) == 'table' then
+ return vim.api.nvim_cmd(command, {})
+ else
+ return vim.api.nvim_exec(command, false)
+ end
end
-- These are the vim.env/v/g/o/bo/wo variable magic accessors.
@@ -331,9 +321,9 @@ do
--@private
local function make_dict_accessor(scope, handle)
- validate {
- scope = {scope, 's'};
- }
+ validate({
+ scope = { scope, 's' },
+ })
local mt = {}
function mt:__newindex(k, v)
return vim._setvar(scope, handle or 0, k, v)
@@ -383,7 +373,7 @@ function vim.region(bufnr, pos1, pos2, regtype, inclusive)
local region = {}
for l = pos1[1], pos2[1] do
local c1, c2
- if regtype:byte() == 22 then -- block selection: take width from regtype
+ if regtype:byte() == 22 then -- block selection: take width from regtype
c1 = pos1[2]
c2 = c1 + regtype:sub(2)
-- and adjust for non-ASCII characters
@@ -395,10 +385,10 @@ function vim.region(bufnr, pos1, pos2, regtype, inclusive)
c2 = vim.str_byteindex(bufline, c2)
end
else
- c1 = (l == pos1[1]) and (pos1[2]) or 0
+ c1 = (l == pos1[1]) and pos1[2] or 0
c2 = (l == pos2[1]) and (pos2[2] + (inclusive and 1 or 0)) or -1
end
- table.insert(region, l, {c1, c2})
+ table.insert(region, l, { c1, c2 })
end
return region
end
@@ -412,19 +402,23 @@ end
---@param timeout Number of milliseconds to wait before calling `fn`
---@return timer luv timer object
function vim.defer_fn(fn, timeout)
- vim.validate { fn = { fn, 'c', true}; }
+ vim.validate({ fn = { fn, 'c', true } })
local timer = vim.loop.new_timer()
- timer:start(timeout, 0, vim.schedule_wrap(function()
- timer:stop()
- timer:close()
+ timer:start(
+ timeout,
+ 0,
+ vim.schedule_wrap(function()
+ if not timer:is_closing() then
+ timer:close()
+ end
- fn()
- end))
+ fn()
+ end)
+ )
return timer
end
-
--- Display a notification to the user.
---
--- This function can be overridden by plugins to display notifications using a
@@ -438,9 +432,9 @@ function vim.notify(msg, level, opts) -- luacheck: no unused args
if level == vim.log.levels.ERROR then
vim.api.nvim_err_writeln(msg)
elseif level == vim.log.levels.WARN then
- vim.api.nvim_echo({{msg, 'WarningMsg'}}, true, {})
+ vim.api.nvim_echo({ { msg, 'WarningMsg' } }, true, {})
else
- vim.api.nvim_echo({{msg}}, true, {})
+ vim.api.nvim_echo({ { msg } }, true, {})
end
end
@@ -455,11 +449,14 @@ do
---@param msg string Content of the notification to show to the user.
---@param level number|nil One of the values from |vim.log.levels|.
---@param opts table|nil Optional parameters. Unused by default.
- function vim.notify_once(msg, level, opts) -- luacheck: no unused args
+ ---@return boolean true if message was displayed, else false
+ function vim.notify_once(msg, level, opts)
if not notified[msg] then
vim.notify(msg, level, opts)
notified[msg] = true
+ return true
end
+ return false
end
end
@@ -493,10 +490,10 @@ function vim.on_key(fn, ns_id)
return #on_key_cbs
end
- vim.validate {
- fn = { fn, 'c', true},
- ns_id = { ns_id, 'n', true }
- }
+ vim.validate({
+ fn = { fn, 'c', true },
+ ns_id = { ns_id, 'n', true },
+ })
if ns_id == nil or ns_id == 0 then
ns_id = vim.api.nvim_create_namespace('')
@@ -521,10 +518,13 @@ function vim._on_key(char)
end
if failed_ns_ids[1] then
- error(string.format(
- "Error executing 'on_key' with ns_ids '%s'\n Messages: %s",
- table.concat(failed_ns_ids, ", "),
- table.concat(failed_messages, "\n")))
+ error(
+ string.format(
+ "Error executing 'on_key' with ns_ids '%s'\n Messages: %s",
+ table.concat(failed_ns_ids, ', '),
+ table.concat(failed_messages, '\n')
+ )
+ )
end
end
@@ -551,8 +551,10 @@ function vim._expand_pat(pat, env)
-- Probably just need to do a smarter match than just `:match`
-- Get the last part of the pattern
- local last_part = pat:match("[%w.:_%[%]'\"]+$")
- if not last_part then return {}, 0 end
+ local last_part = pat:match('[%w.:_%[%]\'"]+$')
+ if not last_part then
+ return {}, 0
+ end
local parts, search_index = vim._expand_pat_get_parts(last_part)
@@ -569,7 +571,7 @@ function vim._expand_pat(pat, env)
-- Normally, we just have a string
-- Just attempt to get the string directly from the environment
- if type(part) == "string" then
+ if type(part) == 'string' then
key = part
else
-- However, sometimes you want to use a variable, and complete on it
@@ -594,8 +596,10 @@ function vim._expand_pat(pat, env)
local field = rawget(final_env, key)
if field == nil then
local mt = getmetatable(final_env)
- if mt and type(mt.__index) == "table" then
+ if mt and type(mt.__index) == 'table' then
field = rawget(mt.__index, key)
+ elseif final_env == vim and vim._submodules[key] then
+ field = vim[key]
end
end
final_env = field
@@ -608,21 +612,25 @@ function vim._expand_pat(pat, env)
local keys = {}
---@private
local function insert_keys(obj)
- for k,_ in pairs(obj) do
- if type(k) == "string" and string.sub(k,1,string.len(match_part)) == match_part then
- table.insert(keys,k)
+ for k, _ in pairs(obj) do
+ if type(k) == 'string' and string.sub(k, 1, string.len(match_part)) == match_part then
+ keys[k] = true
end
end
end
- if type(final_env) == "table" then
+ if type(final_env) == 'table' then
insert_keys(final_env)
end
local mt = getmetatable(final_env)
- if mt and type(mt.__index) == "table" then
+ if mt and type(mt.__index) == 'table' then
insert_keys(mt.__index)
end
+ if final_env == vim then
+ insert_keys(vim._submodules)
+ end
+ keys = vim.tbl_keys(keys)
table.sort(keys)
return keys, #prefix_match_pat
@@ -637,12 +645,12 @@ vim._expand_pat_get_parts = function(lua_string)
for idx = 1, #lua_string do
local s = lua_string:sub(idx, idx)
- if not in_brackets and (s == "." or s == ":") then
+ if not in_brackets and (s == '.' or s == ':') then
table.insert(parts, accumulator)
accumulator = ''
search_index = idx + 1
- elseif s == "[" then
+ elseif s == '[' then
in_brackets = true
table.insert(parts, accumulator)
@@ -654,7 +662,7 @@ vim._expand_pat_get_parts = function(lua_string)
in_brackets = false
search_index = idx + 1
- if string_char == "VAR" then
+ if string_char == 'VAR' then
table.insert(parts, { accumulator })
accumulator = ''
@@ -666,7 +674,7 @@ vim._expand_pat_get_parts = function(lua_string)
if s == '"' or s == "'" then
string_char = s
elseif s ~= ' ' then
- string_char = "VAR"
+ string_char = 'VAR'
accumulator = s
end
elseif string_char then
@@ -684,7 +692,9 @@ vim._expand_pat_get_parts = function(lua_string)
end
end
- parts = vim.tbl_filter(function(val) return #val > 0 end, parts)
+ parts = vim.tbl_filter(function(val)
+ return #val > 0
+ end, parts)
return parts, search_index
end
@@ -708,4 +718,137 @@ function vim.pretty_print(...)
return ...
end
-return module
+function vim._cs_remote(rcid, server_addr, connect_error, args)
+ local function connection_failure_errmsg(consequence)
+ local explanation
+ if server_addr == '' then
+ explanation = 'No server specified with --server'
+ else
+ explanation = "Failed to connect to '" .. server_addr .. "'"
+ if connect_error ~= '' then
+ explanation = explanation .. ': ' .. connect_error
+ end
+ end
+ return 'E247: ' .. explanation .. '. ' .. consequence
+ end
+
+ local f_silent = false
+ local f_tab = false
+
+ local subcmd = string.sub(args[1], 10)
+ if subcmd == 'tab' then
+ f_tab = true
+ elseif subcmd == 'silent' then
+ f_silent = true
+ elseif
+ subcmd == 'wait'
+ or subcmd == 'wait-silent'
+ or subcmd == 'tab-wait'
+ or subcmd == 'tab-wait-silent'
+ then
+ return { errmsg = 'E5600: Wait commands not yet implemented in nvim' }
+ elseif subcmd == 'tab-silent' then
+ f_tab = true
+ f_silent = true
+ elseif subcmd == 'send' then
+ if rcid == 0 then
+ return { errmsg = connection_failure_errmsg('Send failed.') }
+ end
+ vim.fn.rpcrequest(rcid, 'nvim_input', args[2])
+ return { should_exit = true, tabbed = false }
+ elseif subcmd == 'expr' then
+ if rcid == 0 then
+ return { errmsg = connection_failure_errmsg('Send expression failed.') }
+ end
+ print(vim.fn.rpcrequest(rcid, 'nvim_eval', args[2]))
+ return { should_exit = true, tabbed = false }
+ elseif subcmd ~= '' then
+ return { errmsg = 'Unknown option argument: ' .. args[1] }
+ end
+
+ if rcid == 0 then
+ if not f_silent then
+ vim.notify(connection_failure_errmsg('Editing locally'), vim.log.levels.WARN)
+ end
+ else
+ local command = {}
+ if f_tab then
+ table.insert(command, 'tab')
+ end
+ table.insert(command, 'drop')
+ for i = 2, #args do
+ table.insert(command, vim.fn.fnameescape(args[i]))
+ end
+ vim.fn.rpcrequest(rcid, 'nvim_command', table.concat(command, ' '))
+ end
+
+ return {
+ should_exit = rcid ~= 0,
+ tabbed = f_tab,
+ }
+end
+
+--- Display a deprecation notification to the user.
+---
+---@param name string Deprecated function.
+---@param alternative string|nil Preferred alternative function.
+---@param version string Version in which the deprecated function will
+--- be removed.
+---@param plugin string|nil Plugin name that the function will be removed
+--- from. Defaults to "Nvim".
+---@param backtrace boolean|nil Prints backtrace. Defaults to true.
+function vim.deprecate(name, alternative, version, plugin, backtrace)
+ local message = name .. ' is deprecated'
+ plugin = plugin or 'Nvim'
+ message = alternative and (message .. ', use ' .. alternative .. ' instead.') or message
+ message = message
+ .. ' See :h deprecated\nThis function will be removed in '
+ .. plugin
+ .. ' version '
+ .. version
+ if vim.notify_once(message, vim.log.levels.WARN) and backtrace ~= false then
+ vim.notify(debug.traceback('', 2):sub(2), vim.log.levels.WARN)
+ end
+end
+
+--- Create builtin mappings (incl. menus).
+--- Called once on startup.
+function vim._init_default_mappings()
+ -- mappings
+
+ --@private
+ local function map(mode, lhs, rhs)
+ vim.api.nvim_set_keymap(mode, lhs, rhs, { noremap = true, desc = 'Nvim builtin' })
+ end
+
+ map('n', 'Y', 'y$')
+ -- Use normal! <C-L> to prevent inserting raw <C-L> when using i_<C-O>. #17473
+ map('n', '<C-L>', '<Cmd>nohlsearch<Bar>diffupdate<Bar>normal! <C-L><CR>')
+ map('i', '<C-U>', '<C-G>u<C-U>')
+ map('i', '<C-W>', '<C-G>u<C-W>')
+ map('x', '*', 'y/\\V<C-R>"<CR>')
+ map('x', '#', 'y?\\V<C-R>"<CR>')
+ -- Use : instead of <Cmd> so that ranges are supported. #19365
+ map('n', '&', ':&&<CR>')
+
+ -- menus
+
+ -- TODO VimScript, no l10n
+ vim.cmd([[
+ aunmenu *
+ vnoremenu PopUp.Cut "+x
+ vnoremenu PopUp.Copy "+y
+ anoremenu PopUp.Paste "+gP
+ vnoremenu PopUp.Paste "+P
+ vnoremenu PopUp.Delete "_x
+ nnoremenu PopUp.Select\ All ggVG
+ vnoremenu PopUp.Select\ All gg0oG$
+ inoremenu PopUp.Select\ All <C-Home><C-O>VG
+ anoremenu PopUp.-1- <Nop>
+ anoremenu PopUp.How-to\ disable\ mouse <Cmd>help disable-mouse<CR>
+ ]])
+end
+
+require('vim._meta')
+
+return vim
diff --git a/runtime/lua/vim/_init_packages.lua b/runtime/lua/vim/_init_packages.lua
new file mode 100644
index 0000000000..7e3c73667e
--- /dev/null
+++ b/runtime/lua/vim/_init_packages.lua
@@ -0,0 +1,83 @@
+-- prevents luacheck from making lints for setting things on vim
+local vim = assert(vim)
+
+local pathtrails = {}
+vim._so_trails = {}
+for s in (package.cpath .. ';'):gmatch('[^;]*;') do
+ s = s:sub(1, -2) -- Strip trailing semicolon
+ -- Find out path patterns. pathtrail should contain something like
+ -- /?.so, \?.dll. This allows not to bother determining what correct
+ -- suffixes are.
+ local pathtrail = s:match('[/\\][^/\\]*%?.*$')
+ if pathtrail and not pathtrails[pathtrail] then
+ pathtrails[pathtrail] = true
+ table.insert(vim._so_trails, pathtrail)
+ end
+end
+
+function vim._load_package(name)
+ local basename = name:gsub('%.', '/')
+ local paths = { 'lua/' .. basename .. '.lua', 'lua/' .. basename .. '/init.lua' }
+ local found = vim.api.nvim__get_runtime(paths, false, { is_lua = true })
+ if #found > 0 then
+ local f, err = loadfile(found[1])
+ return f or error(err)
+ end
+
+ local so_paths = {}
+ for _, trail in ipairs(vim._so_trails) do
+ local path = 'lua' .. trail:gsub('?', basename) -- so_trails contains a leading slash
+ table.insert(so_paths, path)
+ end
+
+ found = vim.api.nvim__get_runtime(so_paths, false, { is_lua = true })
+ if #found > 0 then
+ -- Making function name in Lua 5.1 (see src/loadlib.c:mkfuncname) is
+ -- a) strip prefix up to and including the first dash, if any
+ -- b) replace all dots by underscores
+ -- c) prepend "luaopen_"
+ -- So "foo-bar.baz" should result in "luaopen_bar_baz"
+ local dash = name:find('-', 1, true)
+ local modname = dash and name:sub(dash + 1) or name
+ local f, err = package.loadlib(found[1], 'luaopen_' .. modname:gsub('%.', '_'))
+ return f or error(err)
+ end
+ return nil
+end
+
+-- Insert vim._load_package after the preloader at position 2
+table.insert(package.loaders, 2, vim._load_package)
+
+-- builtin functions which always should be available
+require('vim.shared')
+
+vim._submodules = { inspect = true }
+
+-- These are for loading runtime modules in the vim namespace lazily.
+setmetatable(vim, {
+ __index = function(t, key)
+ if vim._submodules[key] then
+ t[key] = require('vim.' .. key)
+ return t[key]
+ elseif vim.startswith(key, 'uri_') then
+ local val = require('vim.uri')[key]
+ if val ~= nil then
+ -- Expose all `vim.uri` functions on the `vim` module.
+ t[key] = val
+ return t[key]
+ end
+ end
+ end,
+})
+
+--- <Docs described in |vim.empty_dict()| >
+---@private
+--- TODO: should be in vim.shared when vim.shared always uses nvim-lua
+function vim.empty_dict()
+ return setmetatable({}, vim._empty_dict_mt)
+end
+
+-- only on main thread: functions for interacting with editor state
+if not vim.is_thread() then
+ require('vim._editor')
+end
diff --git a/runtime/lua/vim/_meta.lua b/runtime/lua/vim/_meta.lua
index 522e26caa7..f1652718ee 100644
--- a/runtime/lua/vim/_meta.lua
+++ b/runtime/lua/vim/_meta.lua
@@ -5,41 +5,55 @@ local a = vim.api
local validate = vim.validate
local SET_TYPES = setmetatable({
- SET = 0,
- LOCAL = 1,
+ SET = 0,
+ LOCAL = 1,
GLOBAL = 2,
}, { __index = error })
-local options_info = {}
-for _, v in pairs(a.nvim_get_all_options_info()) do
- options_info[v.name] = v
- if v.shortname ~= "" then options_info[v.shortname] = v end
-end
+local options_info = nil
+local buf_options = nil
+local glb_options = nil
+local win_options = nil
-local get_scoped_options = function(scope)
- local result = {}
- for name, option_info in pairs(options_info) do
- if option_info.scope == scope then
- result[name] = true
+local function _setup()
+ if options_info ~= nil then
+ return
+ end
+ options_info = {}
+ for _, v in pairs(a.nvim_get_all_options_info()) do
+ options_info[v.name] = v
+ if v.shortname ~= '' then
+ options_info[v.shortname] = v
end
end
- return result
-end
+ local function get_scoped_options(scope)
+ local result = {}
+ for name, option_info in pairs(options_info) do
+ if option_info.scope == scope then
+ result[name] = true
+ end
+ end
+
+ return result
+ end
-local buf_options = get_scoped_options("buf")
-local glb_options = get_scoped_options("global")
-local win_options = get_scoped_options("win")
+ buf_options = get_scoped_options('buf')
+ glb_options = get_scoped_options('global')
+ win_options = get_scoped_options('win')
+end
local function make_meta_accessor(get, set, del, validator)
- validator = validator or function() return true end
+ validator = validator or function()
+ return true
+ end
- validate {
- get = {get, 'f'};
- set = {set, 'f'};
- del = {del, 'f', true};
- validator = {validator, 'f'};
- }
+ validate({
+ get = { get, 'f' },
+ set = { set, 'f' },
+ del = { del, 'f', true },
+ validator = { validator, 'f' },
+ })
local mt = {}
function mt:__newindex(k, v)
@@ -73,23 +87,28 @@ end, vim.fn.setenv)
do -- buffer option accessor
local function new_buf_opt_accessor(bufnr)
local function get(k)
- if bufnr == nil and type(k) == "number" then
+ if bufnr == nil and type(k) == 'number' then
return new_buf_opt_accessor(k)
end
- return a.nvim_buf_get_option(bufnr or 0, k)
+ return a.nvim_get_option_value(k, { buf = bufnr or 0 })
end
local function set(k, v)
- return a.nvim_buf_set_option(bufnr or 0, k, v)
+ return a.nvim_set_option_value(k, v, { buf = bufnr or 0 })
end
return make_meta_accessor(get, set, nil, function(k)
if type(k) == 'string' then
+ _setup()
if win_options[k] then
- error(string.format([['%s' is a window option, not a buffer option. See ":help %s"]], k, k))
+ error(
+ string.format([['%s' is a window option, not a buffer option. See ":help %s"]], k, k)
+ )
elseif glb_options[k] then
- error(string.format([['%s' is a global option, not a buffer option. See ":help %s"]], k, k))
+ error(
+ string.format([['%s' is a global option, not a buffer option. See ":help %s"]], k, k)
+ )
end
end
@@ -103,22 +122,27 @@ end
do -- window option accessor
local function new_win_opt_accessor(winnr)
local function get(k)
- if winnr == nil and type(k) == "number" then
+ if winnr == nil and type(k) == 'number' then
return new_win_opt_accessor(k)
end
- return a.nvim_win_get_option(winnr or 0, k)
+ return a.nvim_get_option_value(k, { win = winnr or 0 })
end
local function set(k, v)
- return a.nvim_win_set_option(winnr or 0, k, v)
+ return a.nvim_set_option_value(k, v, { win = winnr or 0 })
end
return make_meta_accessor(get, set, nil, function(k)
if type(k) == 'string' then
+ _setup()
if buf_options[k] then
- error(string.format([['%s' is a buffer option, not a window option. See ":help %s"]], k, k))
+ error(
+ string.format([['%s' is a buffer option, not a window option. See ":help %s"]], k, k)
+ )
elseif glb_options[k] then
- error(string.format([['%s' is a global option, not a window option. See ":help %s"]], k, k))
+ error(
+ string.format([['%s' is a global option, not a window option. See ":help %s"]], k, k)
+ )
end
end
@@ -131,17 +155,19 @@ end
-- vim global option
-- this ONLY sets the global option. like `setglobal`
-vim.go = make_meta_accessor(
- function(k) return a.nvim_get_option_value(k, {scope = "global"}) end,
- function(k, v) return a.nvim_set_option_value(k, v, {scope = "global"}) end
-)
+vim.go = make_meta_accessor(function(k)
+ return a.nvim_get_option_value(k, { scope = 'global' })
+end, function(k, v)
+ return a.nvim_set_option_value(k, v, { scope = 'global' })
+end)
-- vim `set` style options.
-- it has no additional metamethod magic.
-vim.o = make_meta_accessor(
- function(k) return a.nvim_get_option_value(k, {}) end,
- function(k, v) return a.nvim_set_option_value(k, v, {}) end
-)
+vim.o = make_meta_accessor(function(k)
+ return a.nvim_get_option_value(k, {})
+end, function(k, v)
+ return a.nvim_set_option_value(k, v, {})
+end)
---@brief [[
--- vim.opt, vim.opt_local and vim.opt_global implementation
@@ -154,7 +180,9 @@ vim.o = make_meta_accessor(
--- Preserves the order and does not mutate the original list
local remove_duplicate_values = function(t)
local result, seen = {}, {}
- if type(t) == "function" then error(debug.traceback("asdf")) end
+ if type(t) == 'function' then
+ error(debug.traceback('asdf'))
+ end
for _, v in ipairs(t) do
if not seen[v] then
table.insert(result, v)
@@ -171,37 +199,41 @@ end
local key_value_options = {
fillchars = true,
listchars = true,
- winhl = true,
+ winhl = true,
}
---@class OptionTypes
--- Option Type Enum
local OptionTypes = setmetatable({
BOOLEAN = 0,
- NUMBER = 1,
- STRING = 2,
- ARRAY = 3,
- MAP = 4,
- SET = 5,
+ NUMBER = 1,
+ STRING = 2,
+ ARRAY = 3,
+ MAP = 4,
+ SET = 5,
}, {
- __index = function(_, k) error("Not a valid OptionType: " .. k) end,
- __newindex = function(_, k) error("Cannot set a new OptionType: " .. k) end,
+ __index = function(_, k)
+ error('Not a valid OptionType: ' .. k)
+ end,
+ __newindex = function(_, k)
+ error('Cannot set a new OptionType: ' .. k)
+ end,
})
--- Convert a vimoption_T style dictionary to the correct OptionType associated with it.
---@return OptionType
local get_option_type = function(name, info)
- if info.type == "boolean" then
+ if info.type == 'boolean' then
return OptionTypes.BOOLEAN
- elseif info.type == "number" then
+ elseif info.type == 'number' then
return OptionTypes.NUMBER
- elseif info.type == "string" then
+ elseif info.type == 'string' then
if not info.commalist and not info.flaglist then
return OptionTypes.STRING
end
if key_value_options[name] then
- assert(info.commalist, "Must be a comma list to use key:value style")
+ assert(info.commalist, 'Must be a comma list to use key:value style')
return OptionTypes.MAP
end
@@ -211,13 +243,12 @@ local get_option_type = function(name, info)
return OptionTypes.ARRAY
end
- error("Fallthrough in OptionTypes")
+ error('Fallthrough in OptionTypes')
else
- error("Not a known info.type:" .. info.type)
+ error('Not a known info.type:' .. info.type)
end
end
-
-- Check whether the OptionTypes is allowed for vim.opt
-- If it does not match, throw an error which indicates which option causes the error.
local function assert_valid_value(name, value, types)
@@ -228,16 +259,23 @@ local function assert_valid_value(name, value, types)
end
end
- error(string.format("Invalid option type '%s' for '%s', should be %s", type_of_value, name, table.concat(types, " or ")))
+ error(
+ string.format(
+ "Invalid option type '%s' for '%s', should be %s",
+ type_of_value,
+ name,
+ table.concat(types, ' or ')
+ )
+ )
end
local valid_types = {
- [OptionTypes.BOOLEAN] = { "boolean" },
- [OptionTypes.NUMBER] = { "number" },
- [OptionTypes.STRING] = { "string" },
- [OptionTypes.SET] = { "string", "table" },
- [OptionTypes.ARRAY] = { "string", "table" },
- [OptionTypes.MAP] = { "string", "table" },
+ [OptionTypes.BOOLEAN] = { 'boolean' },
+ [OptionTypes.NUMBER] = { 'number' },
+ [OptionTypes.STRING] = { 'string' },
+ [OptionTypes.SET] = { 'string', 'table' },
+ [OptionTypes.ARRAY] = { 'string', 'table' },
+ [OptionTypes.MAP] = { 'string', 'table' },
}
--- Convert a lua value to a vimoption_T value
@@ -245,12 +283,20 @@ local convert_value_to_vim = (function()
-- Map of functions to take a Lua style value and convert to vimoption_T style value.
-- Each function takes (info, lua_value) -> vim_value
local to_vim_value = {
- [OptionTypes.BOOLEAN] = function(_, value) return value end,
- [OptionTypes.NUMBER] = function(_, value) return value end,
- [OptionTypes.STRING] = function(_, value) return value end,
+ [OptionTypes.BOOLEAN] = function(_, value)
+ return value
+ end,
+ [OptionTypes.NUMBER] = function(_, value)
+ return value
+ end,
+ [OptionTypes.STRING] = function(_, value)
+ return value
+ end,
[OptionTypes.SET] = function(info, value)
- if type(value) == "string" then return value end
+ if type(value) == 'string' then
+ return value
+ end
if info.flaglist and info.commalist then
local keys = {}
@@ -261,7 +307,7 @@ local convert_value_to_vim = (function()
end
table.sort(keys)
- return table.concat(keys, ",")
+ return table.concat(keys, ',')
else
local result = ''
for k, v in pairs(value) do
@@ -275,23 +321,27 @@ local convert_value_to_vim = (function()
end,
[OptionTypes.ARRAY] = function(info, value)
- if type(value) == "string" then return value end
+ if type(value) == 'string' then
+ return value
+ end
if not info.allows_duplicates then
value = remove_duplicate_values(value)
end
- return table.concat(value, ",")
+ return table.concat(value, ',')
end,
[OptionTypes.MAP] = function(_, value)
- if type(value) == "string" then return value end
+ if type(value) == 'string' then
+ return value
+ end
local result = {}
for opt_key, opt_value in pairs(value) do
- table.insert(result, string.format("%s:%s", opt_key, opt_value))
+ table.insert(result, string.format('%s:%s', opt_key, opt_value))
end
table.sort(result)
- return table.concat(result, ",")
+ return table.concat(result, ',')
end,
}
@@ -312,12 +362,18 @@ local convert_value_to_lua = (function()
-- Map of OptionType to functions that take vimoption_T values and convert to lua values.
-- Each function takes (info, vim_value) -> lua_value
local to_lua_value = {
- [OptionTypes.BOOLEAN] = function(_, value) return value end,
- [OptionTypes.NUMBER] = function(_, value) return value end,
- [OptionTypes.STRING] = function(_, value) return value end,
+ [OptionTypes.BOOLEAN] = function(_, value)
+ return value
+ end,
+ [OptionTypes.NUMBER] = function(_, value)
+ return value
+ end,
+ [OptionTypes.STRING] = function(_, value)
+ return value
+ end,
[OptionTypes.ARRAY] = function(info, value)
- if type(value) == "table" then
+ if type(value) == 'table' then
if not info.allows_duplicates then
value = remove_duplicate_values(value)
end
@@ -332,41 +388,43 @@ local convert_value_to_lua = (function()
end
-- Handles unescaped commas in a list.
- if string.find(value, ",,,") then
- local comma_split = vim.split(value, ",,,")
+ if string.find(value, ',,,') then
+ local comma_split = vim.split(value, ',,,')
local left = comma_split[1]
local right = comma_split[2]
local result = {}
- vim.list_extend(result, vim.split(left, ","))
- table.insert(result, ",")
- vim.list_extend(result, vim.split(right, ","))
+ vim.list_extend(result, vim.split(left, ','))
+ table.insert(result, ',')
+ vim.list_extend(result, vim.split(right, ','))
table.sort(result)
return result
end
- if string.find(value, ",^,,", 1, true) then
- local comma_split = vim.split(value, ",^,,", true)
+ if string.find(value, ',^,,', 1, true) then
+ local comma_split = vim.split(value, ',^,,', true)
local left = comma_split[1]
local right = comma_split[2]
local result = {}
- vim.list_extend(result, vim.split(left, ","))
- table.insert(result, "^,")
- vim.list_extend(result, vim.split(right, ","))
+ vim.list_extend(result, vim.split(left, ','))
+ table.insert(result, '^,')
+ vim.list_extend(result, vim.split(right, ','))
table.sort(result)
return result
end
- return vim.split(value, ",")
+ return vim.split(value, ',')
end,
[OptionTypes.SET] = function(info, value)
- if type(value) == "table" then return value end
+ if type(value) == 'table' then
+ return value
+ end
-- Empty strings mean that there is nothing there,
-- so empty table should be returned.
@@ -374,10 +432,10 @@ local convert_value_to_lua = (function()
return {}
end
- assert(info.flaglist, "That is the only one I know how to handle")
+ assert(info.flaglist, 'That is the only one I know how to handle')
if info.flaglist and info.commalist then
- local split_value = vim.split(value, ",")
+ local split_value = vim.split(value, ',')
local result = {}
for _, v in ipairs(split_value) do
result[v] = true
@@ -395,15 +453,17 @@ local convert_value_to_lua = (function()
end,
[OptionTypes.MAP] = function(info, raw_value)
- if type(raw_value) == "table" then return raw_value end
+ if type(raw_value) == 'table' then
+ return raw_value
+ end
- assert(info.commalist, "Only commas are supported currently")
+ assert(info.commalist, 'Only commas are supported currently')
local result = {}
- local comma_split = vim.split(raw_value, ",")
+ local comma_split = vim.split(raw_value, ',')
for _, key_value_str in ipairs(comma_split) do
- local key, value = unpack(vim.split(key_value_str, ":"))
+ local key, value = unpack(vim.split(key_value_str, ':'))
key = vim.trim(key)
result[key] = value
@@ -443,17 +503,21 @@ local prepend_value = (function()
end,
[OptionTypes.MAP] = function(left, right)
- return vim.tbl_extend("force", left, right)
+ return vim.tbl_extend('force', left, right)
end,
[OptionTypes.SET] = function(left, right)
- return vim.tbl_extend("force", left, right)
+ return vim.tbl_extend('force', left, right)
end,
}
return function(name, info, current, new)
return value_mutator(
- name, info, convert_value_to_lua(name, info, current), convert_value_to_lua(name, info, new), methods
+ name,
+ info,
+ convert_value_to_lua(name, info, current),
+ convert_value_to_lua(name, info, new),
+ methods
)
end
end)()
@@ -478,17 +542,21 @@ local add_value = (function()
end,
[OptionTypes.MAP] = function(left, right)
- return vim.tbl_extend("force", left, right)
+ return vim.tbl_extend('force', left, right)
end,
[OptionTypes.SET] = function(left, right)
- return vim.tbl_extend("force", left, right)
+ return vim.tbl_extend('force', left, right)
end,
}
return function(name, info, current, new)
return value_mutator(
- name, info, convert_value_to_lua(name, info, current), convert_value_to_lua(name, info, new), methods
+ name,
+ info,
+ convert_value_to_lua(name, info, current),
+ convert_value_to_lua(name, info, new),
+ methods
)
end
end)()
@@ -518,11 +586,11 @@ local remove_value = (function()
end,
[OptionTypes.STRING] = function()
- error("Subtraction not supported for strings.")
+ error('Subtraction not supported for strings.')
end,
[OptionTypes.ARRAY] = function(left, right)
- if type(right) == "string" then
+ if type(right) == 'string' then
remove_one_item(left, right)
else
for _, v in ipairs(right) do
@@ -534,7 +602,7 @@ local remove_value = (function()
end,
[OptionTypes.MAP] = function(left, right)
- if type(right) == "string" then
+ if type(right) == 'string' then
left[right] = nil
else
for _, v in ipairs(right) do
@@ -546,7 +614,7 @@ local remove_value = (function()
end,
[OptionTypes.SET] = function(left, right)
- if type(right) == "string" then
+ if type(right) == 'string' then
left[right] = nil
else
for _, v in ipairs(right) do
@@ -567,9 +635,10 @@ local create_option_metatable = function(set_type)
local set_mt, option_mt
local make_option = function(name, value)
- local info = assert(options_info[name], "Not a valid option name: " .. name)
+ _setup()
+ local info = assert(options_info[name], 'Not a valid option name: ' .. name)
- if type(value) == "table" and getmetatable(value) == option_mt then
+ if type(value) == 'table' and getmetatable(value) == option_mt then
assert(name == value._name, "must be the same value, otherwise that's weird.")
value = value._value
@@ -584,9 +653,9 @@ local create_option_metatable = function(set_type)
local scope
if set_type == SET_TYPES.GLOBAL then
- scope = "global"
+ scope = 'global'
elseif set_type == SET_TYPES.LOCAL then
- scope = "local"
+ scope = 'local'
end
option_mt = {
@@ -594,7 +663,7 @@ local create_option_metatable = function(set_type)
-- opt[my_option] = value
_set = function(self)
local value = convert_value_to_vim(self._name, self._info, self._value)
- a.nvim_set_option_value(self._name, value, {scope = scope})
+ a.nvim_set_option_value(self._name, value, { scope = scope })
return self
end,
@@ -625,13 +694,13 @@ local create_option_metatable = function(set_type)
__sub = function(self, right)
return make_option(self._name, remove_value(self._name, self._info, self._value, right))
- end
+ end,
}
option_mt.__index = option_mt
set_mt = {
__index = function(_, k)
- return make_option(k, a.nvim_get_option_value(k, {scope = scope}))
+ return make_option(k, a.nvim_get_option_value(k, { scope = scope }))
end,
__newindex = function(_, k, v)
diff --git a/runtime/lua/vim/compat.lua b/runtime/lua/vim/compat.lua
index 168979bb95..2c9786d491 100644
--- a/runtime/lua/vim/compat.lua
+++ b/runtime/lua/vim/compat.lua
@@ -7,6 +7,6 @@
local lua_version = _VERSION:sub(-3)
-if lua_version >= "5.2" then
- unpack = table.unpack -- luacheck: ignore 121 143
+if lua_version >= '5.2' then
+ unpack = table.unpack -- luacheck: ignore 121 143
end
diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua
index 417b661155..ae20b5c517 100644
--- a/runtime/lua/vim/diagnostic.lua
+++ b/runtime/lua/vim/diagnostic.lua
@@ -28,9 +28,9 @@ local global_diagnostic_options = {
M.handlers = setmetatable({}, {
__newindex = function(t, name, handler)
- vim.validate { handler = {handler, "t" } }
+ 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,
@@ -39,7 +39,7 @@ M.handlers = setmetatable({}, {
-- Metatable that automatically creates an empty table when assigning to a missing key
local bufnr_and_namespace_cacher_mt = {
__index = function(t, bufnr)
- assert(bufnr > 0, "Invalid buffer number")
+ assert(bufnr > 0, 'Invalid buffer number')
t[bufnr] = {}
return t[bufnr]
end,
@@ -47,11 +47,11 @@ local bufnr_and_namespace_cacher_mt = {
local diagnostic_cache = setmetatable({}, {
__index = function(t, bufnr)
- assert(bufnr > 0, "Invalid buffer number")
+ assert(bufnr > 0, 'Invalid buffer number')
vim.api.nvim_buf_attach(bufnr, false, {
on_detach = function()
rawset(t, bufnr, nil) -- clear cache
- end
+ end,
})
t[bufnr] = {}
return t[bufnr]
@@ -68,7 +68,10 @@ local all_namespaces = {}
---@private
local function to_severity(severity)
if type(severity) == 'string' then
- return assert(M.severity[string.upper(severity)], string.format("Invalid severity: %s", severity))
+ return assert(
+ M.severity[string.upper(severity)],
+ string.format('Invalid severity: %s', severity)
+ )
end
return severity
end
@@ -79,15 +82,19 @@ local function filter_by_severity(severity, diagnostics)
return diagnostics
end
- if type(severity) ~= "table" then
+ if type(severity) ~= 'table' then
severity = to_severity(severity)
- return vim.tbl_filter(function(t) return t.severity == severity end, diagnostics)
+ return vim.tbl_filter(function(t)
+ return t.severity == severity
+ end, diagnostics)
end
local min_severity = to_severity(severity.min) or M.severity.HINT
local max_severity = to_severity(severity.max) or M.severity.ERROR
- return vim.tbl_filter(function(t) return t.severity <= min_severity and t.severity >= max_severity end, diagnostics)
+ return vim.tbl_filter(function(t)
+ return t.severity <= min_severity and t.severity >= max_severity
+ end, diagnostics)
end
---@private
@@ -113,17 +120,17 @@ local function prefix_source(diagnostics)
end
local t = vim.deepcopy(d)
- t.message = string.format("%s: %s", d.source, d.message)
+ t.message = string.format('%s: %s', d.source, d.message)
return t
end, diagnostics)
end
---@private
local function reformat_diagnostics(format, diagnostics)
- vim.validate {
- format = {format, 'f'},
- diagnostics = {diagnostics, 't'},
- }
+ vim.validate({
+ format = { format, 'f' },
+ diagnostics = { diagnostics, 't' },
+ })
local formatted = vim.deepcopy(diagnostics)
for _, diagnostic in ipairs(formatted) do
@@ -135,11 +142,11 @@ end
---@private
local function enabled_value(option, namespace)
local ns = namespace and M.get_namespace(namespace) or {}
- if ns.opts and type(ns.opts[option]) == "table" then
+ if ns.opts and type(ns.opts[option]) == 'table' then
return ns.opts[option]
end
- if type(global_diagnostic_options[option]) == "table" then
+ if type(global_diagnostic_options[option]) == 'table' then
return global_diagnostic_options[option]
end
@@ -162,7 +169,7 @@ local function resolve_optional_value(option, value, namespace, bufnr)
elseif type(value) == 'table' then
return value
else
- error("Unexpected option type: " .. vim.inspect(value))
+ error('Unexpected option type: ' .. vim.inspect(value))
end
end
@@ -181,10 +188,10 @@ end
-- Default diagnostic highlights
local diagnostic_severities = {
- [M.severity.ERROR] = { ctermfg = 1, guifg = "Red" };
- [M.severity.WARN] = { ctermfg = 3, guifg = "Orange" };
- [M.severity.INFO] = { ctermfg = 4, guifg = "LightBlue" };
- [M.severity.HINT] = { ctermfg = 7, guifg = "LightGrey" };
+ [M.severity.ERROR] = { ctermfg = 1, guifg = 'Red' },
+ [M.severity.WARN] = { ctermfg = 3, guifg = 'Orange' },
+ [M.severity.INFO] = { ctermfg = 4, guifg = 'LightBlue' },
+ [M.severity.HINT] = { ctermfg = 7, guifg = 'LightGrey' },
}
-- Make a map from DiagnosticSeverity -> Highlight Name
@@ -194,16 +201,16 @@ local function make_highlight_map(base_name)
for k in pairs(diagnostic_severities) do
local name = M.severity[k]
name = name:sub(1, 1) .. name:sub(2):lower()
- result[k] = "Diagnostic" .. base_name .. name
+ result[k] = 'Diagnostic' .. base_name .. name
end
return result
end
-local virtual_text_highlight_map = make_highlight_map("VirtualText")
-local underline_highlight_map = make_highlight_map("Underline")
-local floating_highlight_map = make_highlight_map("Floating")
-local sign_highlight_map = make_highlight_map("Sign")
+local virtual_text_highlight_map = make_highlight_map('VirtualText')
+local underline_highlight_map = make_highlight_map('Underline')
+local floating_highlight_map = make_highlight_map('Floating')
+local sign_highlight_map = make_highlight_map('Sign')
---@private
local define_default_signs = (function()
@@ -244,7 +251,7 @@ local function is_disabled(namespace, bufnr)
return true
end
- if type(diagnostic_disabled[bufnr]) == "table" then
+ if type(diagnostic_disabled[bufnr]) == 'table' then
return diagnostic_disabled[bufnr][namespace]
end
return diagnostic_disabled[bufnr]
@@ -271,9 +278,10 @@ end
---@private
local function set_diagnostic_cache(namespace, bufnr, diagnostics)
for _, diagnostic in ipairs(diagnostics) do
- assert(diagnostic.lnum, "Diagnostic line number is required")
- assert(diagnostic.col, "Diagnostic column is required")
- diagnostic.severity = diagnostic.severity and to_severity(diagnostic.severity) or M.severity.ERROR
+ assert(diagnostic.lnum, 'Diagnostic line number is required')
+ assert(diagnostic.col, 'Diagnostic column is required')
+ diagnostic.severity = diagnostic.severity and to_severity(diagnostic.severity)
+ or M.severity.ERROR
diagnostic.end_lnum = diagnostic.end_lnum or diagnostic.lnum
diagnostic.end_col = diagnostic.end_col or diagnostic.col
diagnostic.namespace = namespace
@@ -285,7 +293,7 @@ end
---@private
local function restore_extmarks(bufnr, last)
for ns, extmarks in pairs(diagnostic_cache_extmarks[bufnr]) do
- local extmarks_current = vim.api.nvim_buf_get_extmarks(bufnr, ns, 0, -1, {details = true})
+ local extmarks_current = vim.api.nvim_buf_get_extmarks(bufnr, ns, 0, -1, { details = true })
local found = {}
for _, extmark in ipairs(extmarks_current) do
-- nvim_buf_set_lines will move any extmark to the line after the last
@@ -314,10 +322,12 @@ local function save_extmarks(namespace, bufnr)
end,
on_detach = function()
diagnostic_cache_extmarks[bufnr] = nil
- end})
+ end,
+ })
diagnostic_attached_buffers[bufnr] = true
end
- diagnostic_cache_extmarks[bufnr][namespace] = vim.api.nvim_buf_get_extmarks(bufnr, namespace, 0, -1, {details = true})
+ diagnostic_cache_extmarks[bufnr][namespace] =
+ vim.api.nvim_buf_get_extmarks(bufnr, namespace, 0, -1, { details = true })
end
local registered_autocmds = {}
@@ -325,11 +335,37 @@ local registered_autocmds = {}
---@private
local function make_augroup_key(namespace, bufnr)
local ns = M.get_namespace(namespace)
- return string.format("DiagnosticInsertLeave:%s:%s", bufnr, ns.name)
+ return string.format('DiagnosticInsertLeave:%s:%s', bufnr, ns.name)
+end
+
+---@private
+local function execute_scheduled_display(namespace, bufnr)
+ local args = bufs_waiting_to_update[bufnr][namespace]
+ if not args then
+ return
+ end
+
+ -- Clear the args so we don't display unnecessarily.
+ bufs_waiting_to_update[bufnr][namespace] = nil
+
+ M.show(namespace, bufnr, nil, args)
+end
+
+--- @deprecated
+--- Callback scheduled when leaving Insert mode.
+---
+--- called from the Vimscript autocommand.
+---
+--- See @ref schedule_display()
+---
+---@private
+function M._execute_scheduled_display(namespace, bufnr)
+ vim.deprecate('vim.diagnostic._execute_scheduled_display', nil, '0.9')
+ execute_scheduled_display(namespace, bufnr)
end
--- Table of autocmd events to fire the update for displaying new diagnostic information
-local insert_leave_auto_cmds = { "InsertLeave", "CursorHoldI" }
+local insert_leave_auto_cmds = { 'InsertLeave', 'CursorHoldI' }
---@private
local function schedule_display(namespace, bufnr, args)
@@ -337,15 +373,15 @@ local function schedule_display(namespace, bufnr, args)
local key = make_augroup_key(namespace, bufnr)
if not registered_autocmds[key] then
- vim.cmd(string.format([[augroup %s
- au!
- autocmd %s <buffer=%s> lua vim.diagnostic._execute_scheduled_display(%s, %s)
- augroup END]],
- key,
- table.concat(insert_leave_auto_cmds, ","),
- bufnr,
- namespace,
- bufnr))
+ local group = vim.api.nvim_create_augroup(key, { clear = true })
+ vim.api.nvim_create_autocmd(insert_leave_auto_cmds, {
+ group = group,
+ buffer = bufnr,
+ callback = function()
+ execute_scheduled_display(namespace, bufnr)
+ end,
+ desc = 'vim.diagnostic: display diagnostics',
+ })
registered_autocmds[key] = true
end
end
@@ -355,9 +391,7 @@ local function clear_scheduled_display(namespace, bufnr)
local key = make_augroup_key(namespace, bufnr)
if registered_autocmds[key] then
- vim.cmd(string.format([[augroup %s
- au!
- augroup END]], key))
+ vim.api.nvim_del_augroup_by_name(key)
registered_autocmds[key] = nil
end
end
@@ -382,10 +416,19 @@ local function get_diagnostics(bufnr, opts, clamp)
if not opts.lnum or d.lnum == opts.lnum then
if clamp and vim.api.nvim_buf_is_loaded(b) then
local line_count = buf_line_count[b] - 1
- if (d.lnum > line_count or d.end_lnum > line_count or d.lnum < 0 or d.end_lnum < 0) then
+ if
+ d.lnum > line_count
+ or d.end_lnum > line_count
+ or d.lnum < 0
+ or d.end_lnum < 0
+ or d.col < 0
+ or d.end_col < 0
+ then
d = vim.deepcopy(d)
d.lnum = math.max(math.min(d.lnum, line_count), 0)
d.end_lnum = math.max(math.min(d.end_lnum, line_count), 0)
+ d.col = math.max(d.col, 0)
+ d.end_col = math.max(d.end_col, 0)
end
end
table.insert(diagnostics, d)
@@ -431,7 +474,7 @@ end
local function set_list(loclist, opts)
opts = opts or {}
local open = vim.F.if_nil(opts.open, true)
- local title = opts.title or "Diagnostics"
+ local title = opts.title or 'Diagnostics'
local winnr = opts.winnr or 0
local bufnr
if loclist then
@@ -447,7 +490,7 @@ local function set_list(loclist, opts)
vim.fn.setqflist({}, ' ', { title = title, items = items })
end
if open then
- vim.api.nvim_command(loclist and "lopen" or "copen")
+ vim.api.nvim_command(loclist and 'lopen' or 'botright copen')
end
end
@@ -457,7 +500,8 @@ local function next_diagnostic(position, search_forward, bufnr, opts, namespace)
bufnr = get_bufnr(bufnr)
local wrap = vim.F.if_nil(opts.wrap, true)
local line_count = vim.api.nvim_buf_line_count(bufnr)
- local diagnostics = get_diagnostics(bufnr, vim.tbl_extend("keep", opts, {namespace = namespace}), true)
+ local diagnostics =
+ get_diagnostics(bufnr, vim.tbl_extend('keep', opts, { namespace = namespace }), true)
local line_diagnostics = diagnostic_lines(diagnostics)
for i = 0, line_count do
local offset = i * (search_forward and 1 or -1)
@@ -469,14 +513,22 @@ local function next_diagnostic(position, search_forward, bufnr, opts, namespace)
lnum = (lnum + line_count) % line_count
end
if line_diagnostics[lnum] and not vim.tbl_isempty(line_diagnostics[lnum]) then
- local line_length = #vim.api.nvim_buf_get_lines(bufnr, lnum, lnum + 1, true)[1]
+ local line_length = #vim.api.nvim_buf_get_lines(bufnr, lnum, lnum + 1, true)[1]
local sort_diagnostics, is_next
if search_forward then
- sort_diagnostics = function(a, b) return a.col < b.col end
- is_next = function(d) return math.min(d.col, line_length - 1) > position[2] end
+ sort_diagnostics = function(a, b)
+ return a.col < b.col
+ end
+ is_next = function(d)
+ return math.min(d.col, line_length - 1) > position[2]
+ end
else
- sort_diagnostics = function(a, b) return a.col > b.col end
- is_next = function(d) return math.min(d.col, line_length - 1) < position[2] end
+ sort_diagnostics = function(a, b)
+ return a.col > b.col
+ end
+ is_next = function(d)
+ return math.min(d.col, line_length - 1) < position[2]
+ end
end
table.sort(line_diagnostics[lnum], sort_diagnostics)
if i == 0 then
@@ -500,28 +552,26 @@ local function diagnostic_move_pos(opts, pos)
local win_id = opts.win_id or vim.api.nvim_get_current_win()
if not pos then
- vim.api.nvim_echo({{"No more valid diagnostics to move to", "WarningMsg"}}, true, {})
+ vim.api.nvim_echo({ { 'No more valid diagnostics to move to', 'WarningMsg' } }, true, {})
return
end
vim.api.nvim_win_call(win_id, function()
-- Save position in the window's jumplist
vim.cmd("normal! m'")
- vim.api.nvim_win_set_cursor(win_id, {pos[1] + 1, pos[2]})
+ vim.api.nvim_win_set_cursor(win_id, { pos[1] + 1, pos[2] })
-- Open folds under the cursor
- vim.cmd("normal! zv")
+ vim.cmd('normal! zv')
end)
if float then
- local float_opts = type(float) == "table" and float or {}
+ local float_opts = type(float) == 'table' and float or {}
vim.schedule(function()
- M.open_float(
- vim.tbl_extend("keep", float_opts, {
- bufnr = vim.api.nvim_win_get_buf(win_id),
- scope = "cursor",
- focus = false,
- })
- )
+ M.open_float(vim.tbl_extend('keep', float_opts, {
+ bufnr = vim.api.nvim_win_get_buf(win_id),
+ scope = 'cursor',
+ focus = false,
+ }))
end)
end
end
@@ -552,7 +602,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|
@@ -598,10 +649,10 @@ end
---@param namespace number|nil Update the options for the given namespace. When omitted, update the
--- global diagnostic options.
function M.config(opts, namespace)
- vim.validate {
- opts = { opts, 't' },
+ vim.validate({
+ opts = { opts, 't', true },
namespace = { namespace, 'n', true },
- }
+ })
local t
if namespace then
@@ -611,10 +662,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
@@ -641,16 +695,16 @@ end
---@param diagnostics table A list of diagnostic items |diagnostic-structure|
---@param opts table|nil Display options to pass to |vim.diagnostic.show()|
function M.set(namespace, bufnr, diagnostics, opts)
- vim.validate {
- namespace = {namespace, 'n'},
- bufnr = {bufnr, 'n'},
+ vim.validate({
+ namespace = { namespace, 'n' },
+ bufnr = { bufnr, 'n' },
diagnostics = {
diagnostics,
vim.tbl_islist,
- "a list of diagnostics",
+ 'a list of diagnostics',
},
- opts = {opts, 't', true},
- }
+ opts = { opts, 't', true },
+ })
bufnr = get_bufnr(bufnr)
@@ -664,14 +718,10 @@ function M.set(namespace, bufnr, diagnostics, opts)
M.show(namespace, bufnr, nil, opts)
end
- vim.api.nvim_buf_call(bufnr, function()
- vim.api.nvim_command(
- string.format(
- "doautocmd <nomodeline> DiagnosticChanged %s",
- vim.fn.fnameescape(vim.api.nvim_buf_get_name(bufnr))
- )
- )
- end)
+ vim.api.nvim_exec_autocmds('DiagnosticChanged', {
+ modeline = false,
+ buffer = bufnr,
+ })
end
--- Get namespace metadata.
@@ -679,7 +729,7 @@ end
---@param namespace number Diagnostic namespace
---@return table Namespace metadata
function M.get_namespace(namespace)
- vim.validate { namespace = { namespace, 'n' } }
+ vim.validate({ namespace = { namespace, 'n' } })
if not all_namespaces[namespace] then
local name
for k, v in pairs(vim.api.nvim_get_namespaces()) do
@@ -689,7 +739,7 @@ function M.get_namespace(namespace)
end
end
- assert(name, "namespace does not exist or is anonymous")
+ assert(name, 'namespace does not exist or is anonymous')
all_namespaces[namespace] = {
name = name,
@@ -717,10 +767,10 @@ end
--- - severity: See |diagnostic-severity|.
---@return table A list of diagnostic items |diagnostic-structure|.
function M.get(bufnr, opts)
- vim.validate {
+ vim.validate({
bufnr = { bufnr, 'n', true },
opts = { opts, 't', true },
- }
+ })
return get_diagnostics(bufnr, opts, false)
end
@@ -749,16 +799,13 @@ function M.get_prev_pos(opts)
return false
end
- return {prev.lnum, prev.col}
+ return { prev.lnum, prev.col }
end
--- Move to the previous diagnostic in the current buffer.
---@param opts table See |vim.diagnostic.goto_next()|
function M.goto_prev(opts)
- return diagnostic_move_pos(
- opts,
- M.get_prev_pos(opts)
- )
+ return diagnostic_move_pos(opts, M.get_prev_pos(opts))
end
--- Get the next diagnostic closest to the cursor position.
@@ -785,7 +832,7 @@ function M.get_next_pos(opts)
return false
end
- return {next.lnum, next.col}
+ return { next.lnum, next.col }
end
--- Move to the next diagnostic.
@@ -803,26 +850,24 @@ end
--- the "scope" option).
--- - win_id: (number, default 0) Window ID
function M.goto_next(opts)
- return diagnostic_move_pos(
- opts,
- M.get_next_pos(opts)
- )
+ return diagnostic_move_pos(opts, M.get_next_pos(opts))
end
M.handlers.signs = {
show = function(namespace, bufnr, diagnostics, opts)
- vim.validate {
- namespace = {namespace, 'n'},
- bufnr = {bufnr, 'n'},
+ vim.validate({
+ namespace = { namespace, 'n' },
+ bufnr = { bufnr, 'n' },
diagnostics = {
diagnostics,
vim.tbl_islist,
- "a list of diagnostics",
+ 'a list of diagnostics',
},
- opts = {opts, 't', true},
- }
+ opts = { opts, 't', true },
+ })
bufnr = get_bufnr(bufnr)
+ opts = opts or {}
if opts.signs and opts.signs.severity then
diagnostics = filter_by_severity(opts.signs.severity, diagnostics)
@@ -834,7 +879,7 @@ M.handlers.signs = {
local priority = opts.signs and opts.signs.priority or 10
local get_priority
if opts.severity_sort then
- if type(opts.severity_sort) == "table" and opts.severity_sort.reverse then
+ if type(opts.severity_sort) == 'table' and opts.severity_sort.reverse then
get_priority = function(severity)
return priority + (severity - vim.diagnostic.severity.ERROR)
end
@@ -851,45 +896,40 @@ M.handlers.signs = {
local ns = M.get_namespace(namespace)
if not ns.user_data.sign_group then
- ns.user_data.sign_group = string.format("vim.diagnostic.%s", ns.name)
+ ns.user_data.sign_group = string.format('vim.diagnostic.%s', ns.name)
end
local sign_group = ns.user_data.sign_group
for _, diagnostic in ipairs(diagnostics) do
- vim.fn.sign_place(
- 0,
- sign_group,
- sign_highlight_map[diagnostic.severity],
- bufnr,
- {
- priority = get_priority(diagnostic.severity),
- lnum = diagnostic.lnum + 1
- }
- )
+ vim.fn.sign_place(0, sign_group, sign_highlight_map[diagnostic.severity], bufnr, {
+ priority = get_priority(diagnostic.severity),
+ lnum = diagnostic.lnum + 1,
+ })
end
end,
hide = function(namespace, bufnr)
local ns = M.get_namespace(namespace)
if ns.user_data.sign_group then
- vim.fn.sign_unplace(ns.user_data.sign_group, {buffer=bufnr})
+ vim.fn.sign_unplace(ns.user_data.sign_group, { buffer = bufnr })
end
end,
}
M.handlers.underline = {
show = function(namespace, bufnr, diagnostics, opts)
- vim.validate {
- namespace = {namespace, 'n'},
- bufnr = {bufnr, 'n'},
+ vim.validate({
+ namespace = { namespace, 'n' },
+ bufnr = { bufnr, 'n' },
diagnostics = {
diagnostics,
vim.tbl_islist,
- "a list of diagnostics",
+ 'a list of diagnostics',
},
- opts = {opts, 't', true},
- }
+ opts = { opts, 't', true },
+ })
bufnr = get_bufnr(bufnr)
+ opts = opts or {}
if opts.underline and opts.underline.severity then
diagnostics = filter_by_severity(opts.underline.severity, diagnostics)
@@ -897,7 +937,7 @@ M.handlers.underline = {
local ns = M.get_namespace(namespace)
if not ns.user_data.underline_ns then
- ns.user_data.underline_ns = vim.api.nvim_create_namespace("")
+ ns.user_data.underline_ns = vim.api.nvim_create_namespace('')
end
local underline_ns = ns.user_data.underline_ns
@@ -914,7 +954,8 @@ M.handlers.underline = {
underline_ns,
higroup,
{ diagnostic.lnum, diagnostic.col },
- { diagnostic.end_lnum, diagnostic.end_col }
+ { diagnostic.end_lnum, diagnostic.end_col },
+ { priority = vim.highlight.priorities.diagnostics }
)
end
save_extmarks(underline_ns, bufnr)
@@ -925,23 +966,24 @@ M.handlers.underline = {
diagnostic_cache_extmarks[bufnr][ns.user_data.underline_ns] = {}
vim.api.nvim_buf_clear_namespace(bufnr, ns.user_data.underline_ns, 0, -1)
end
- end
+ end,
}
M.handlers.virtual_text = {
show = function(namespace, bufnr, diagnostics, opts)
- vim.validate {
- namespace = {namespace, 'n'},
- bufnr = {bufnr, 'n'},
+ vim.validate({
+ namespace = { namespace, 'n' },
+ bufnr = { bufnr, 'n' },
diagnostics = {
diagnostics,
vim.tbl_islist,
- "a list of diagnostics",
+ 'a list of diagnostics',
},
- opts = {opts, 't', true},
- }
+ opts = { opts, 't', true },
+ })
bufnr = get_bufnr(bufnr)
+ opts = opts or {}
local severity
if opts.virtual_text then
@@ -950,7 +992,7 @@ M.handlers.virtual_text = {
end
if
opts.virtual_text.source
- and (opts.virtual_text.source ~= "if_many" or count_sources(bufnr) > 1)
+ and (opts.virtual_text.source ~= 'if_many' or count_sources(bufnr) > 1)
then
diagnostics = prefix_source(diagnostics)
end
@@ -961,7 +1003,7 @@ M.handlers.virtual_text = {
local ns = M.get_namespace(namespace)
if not ns.user_data.virt_text_ns then
- ns.user_data.virt_text_ns = vim.api.nvim_create_namespace("")
+ ns.user_data.virt_text_ns = vim.api.nvim_create_namespace('')
end
local virt_text_ns = ns.user_data.virt_text_ns
@@ -974,7 +1016,7 @@ M.handlers.virtual_text = {
if virt_texts then
vim.api.nvim_buf_set_extmark(bufnr, virt_text_ns, line, 0, {
- hl_mode = "combine",
+ hl_mode = 'combine',
virt_text = virt_texts,
})
end
@@ -1002,52 +1044,29 @@ function M._get_virt_text_chunks(line_diags, opts)
end
opts = opts or {}
- local prefix = opts.prefix or "■"
+ local prefix = opts.prefix or '■'
local spacing = opts.spacing or 4
-- Create a little more space between virtual text and contents
- local virt_texts = {{string.rep(" ", spacing)}}
+ local virt_texts = { { string.rep(' ', spacing) } }
for i = 1, #line_diags - 1 do
- table.insert(virt_texts, {prefix, virtual_text_highlight_map[line_diags[i].severity]})
+ table.insert(virt_texts, { prefix, virtual_text_highlight_map[line_diags[i].severity] })
end
local last = line_diags[#line_diags]
-- TODO(tjdevries): Allow different servers to be shown first somehow?
-- TODO(tjdevries): Display server name associated with these?
if last.message then
- table.insert(
- virt_texts,
- {
- string.format("%s %s", prefix, last.message:gsub("\r", ""):gsub("\n", " ")),
- virtual_text_highlight_map[last.severity]
- }
- )
+ table.insert(virt_texts, {
+ string.format('%s %s', prefix, last.message:gsub('\r', ''):gsub('\n', ' ')),
+ virtual_text_highlight_map[last.severity],
+ })
return virt_texts
end
end
---- Callback scheduled when leaving Insert mode.
----
---- This function must be exported publicly so that it is available to be
---- called from the Vimscript autocommand.
----
---- See @ref schedule_display()
----
----@private
-function M._execute_scheduled_display(namespace, bufnr)
- local args = bufs_waiting_to_update[bufnr][namespace]
- if not args then
- return
- end
-
- -- Clear the args so we don't display unnecessarily.
- bufs_waiting_to_update[bufnr][namespace] = nil
-
- M.show(namespace, bufnr, nil, args)
-end
-
--- Hide currently displayed diagnostics.
---
--- This only clears the decorations displayed in the buffer. Diagnostics can
@@ -1062,14 +1081,14 @@ end
---@param bufnr number|nil Buffer number, or 0 for current buffer. When
--- omitted, hide diagnostics in all buffers.
function M.hide(namespace, bufnr)
- vim.validate {
+ vim.validate({
namespace = { namespace, 'n', true },
bufnr = { bufnr, 'n', true },
- }
+ })
- local buffers = bufnr and {get_bufnr(bufnr)} or vim.tbl_keys(diagnostic_cache)
+ local buffers = bufnr and { get_bufnr(bufnr) } or vim.tbl_keys(diagnostic_cache)
for _, iter_bufnr in ipairs(buffers) do
- local namespaces = namespace and {namespace} or vim.tbl_keys(diagnostic_cache[iter_bufnr])
+ local namespaces = namespace and { namespace } or vim.tbl_keys(diagnostic_cache[iter_bufnr])
for _, iter_namespace in ipairs(namespaces) do
for _, handler in pairs(M.handlers) do
if handler.hide then
@@ -1094,19 +1113,21 @@ end
--- or {bufnr} is nil.
---@param opts table|nil Display options. See |vim.diagnostic.config()|.
function M.show(namespace, bufnr, diagnostics, opts)
- vim.validate {
+ vim.validate({
namespace = { namespace, 'n', true },
bufnr = { bufnr, 'n', true },
diagnostics = {
diagnostics,
- function(v) return v == nil or vim.tbl_islist(v) end,
- "a list of diagnostics",
+ function(v)
+ return v == nil or vim.tbl_islist(v)
+ end,
+ 'a list of diagnostics',
},
opts = { opts, 't', true },
- }
+ })
if not bufnr or not namespace then
- assert(not diagnostics, "Cannot show diagnostics without a buffer and namespace")
+ assert(not diagnostics, 'Cannot show diagnostics without a buffer and namespace')
if not bufnr then
for iter_bufnr in pairs(diagnostic_cache) do
M.show(namespace, iter_bufnr, nil, opts)
@@ -1127,7 +1148,7 @@ function M.show(namespace, bufnr, diagnostics, opts)
M.hide(namespace, bufnr)
- diagnostics = diagnostics or get_diagnostics(bufnr, {namespace=namespace}, true)
+ diagnostics = diagnostics or get_diagnostics(bufnr, { namespace = namespace }, true)
if not diagnostics or vim.tbl_isempty(diagnostics) then
return
@@ -1146,10 +1167,14 @@ function M.show(namespace, bufnr, diagnostics, opts)
end
if vim.F.if_nil(opts.severity_sort, false) then
- if type(opts.severity_sort) == "table" and opts.severity_sort.reverse then
- table.sort(diagnostics, function(a, b) return a.severity < b.severity end)
+ if type(opts.severity_sort) == 'table' and opts.severity_sort.reverse then
+ table.sort(diagnostics, function(a, b)
+ return a.severity < b.severity
+ end)
else
- table.sort(diagnostics, function(a, b) return a.severity > b.severity end)
+ table.sort(diagnostics, function(a, b)
+ return a.severity > b.severity
+ end)
end
end
@@ -1205,53 +1230,54 @@ end
function M.open_float(opts, ...)
-- Support old (bufnr, opts) signature
local bufnr
- if opts == nil or type(opts) == "number" then
+ if opts == nil or type(opts) == 'number' then
bufnr = opts
opts = ...
else
- vim.validate {
+ vim.validate({
opts = { opts, 't', true },
- }
+ })
end
opts = opts or {}
bufnr = get_bufnr(bufnr or opts.bufnr)
- local scope = ({l = "line", c = "cursor", b = "buffer"})[opts.scope] or opts.scope or "line"
+
+ do
+ -- Resolve options with user settings from vim.diagnostic.config
+ -- Unlike the other decoration functions (e.g. set_virtual_text, set_signs, etc.) `open_float`
+ -- does not have a dedicated table for configuration options; instead, the options are mixed in
+ -- with its `opts` table which also includes "keyword" parameters. So we create a dedicated
+ -- options table that inherits missing keys from the global configuration before resolving.
+ local t = global_diagnostic_options.float
+ local float_opts = vim.tbl_extend('keep', opts, type(t) == 'table' and t or {})
+ opts = get_resolved_options({ float = float_opts }, nil, bufnr).float
+ end
+
+ local scope = ({ l = 'line', c = 'cursor', b = 'buffer' })[opts.scope] or opts.scope or 'line'
local lnum, col
- if scope == "line" or scope == "cursor" then
+ if scope == 'line' or scope == 'cursor' then
if not opts.pos then
local pos = vim.api.nvim_win_get_cursor(0)
lnum = pos[1] - 1
col = pos[2]
- elseif type(opts.pos) == "number" then
+ elseif type(opts.pos) == 'number' then
lnum = opts.pos
- elseif type(opts.pos) == "table" then
+ elseif type(opts.pos) == 'table' then
lnum, col = unpack(opts.pos)
else
error("Invalid value for option 'pos'")
end
- elseif scope ~= "buffer" then
+ elseif scope ~= 'buffer' then
error("Invalid value for option 'scope'")
end
- do
- -- Resolve options with user settings from vim.diagnostic.config
- -- Unlike the other decoration functions (e.g. set_virtual_text, set_signs, etc.) `open_float`
- -- does not have a dedicated table for configuration options; instead, the options are mixed in
- -- with its `opts` table which also includes "keyword" parameters. So we create a dedicated
- -- options table that inherits missing keys from the global configuration before resolving.
- local t = global_diagnostic_options.float
- local float_opts = vim.tbl_extend("keep", opts, type(t) == "table" and t or {})
- opts = get_resolved_options({ float = float_opts }, nil, bufnr).float
- end
-
local diagnostics = get_diagnostics(bufnr, opts, true)
- if scope == "line" then
+ if scope == 'line' then
diagnostics = vim.tbl_filter(function(d)
return d.lnum == lnum
end, diagnostics)
- elseif scope == "cursor" then
+ elseif scope == 'cursor' then
-- LSP servers can send diagnostics with `end_col` past the length of the line
local line_length = #vim.api.nvim_buf_get_lines(bufnr, lnum, lnum + 1, true)[1]
diagnostics = vim.tbl_filter(function(d)
@@ -1267,29 +1293,39 @@ function M.open_float(opts, ...)
local severity_sort = vim.F.if_nil(opts.severity_sort, global_diagnostic_options.severity_sort)
if severity_sort then
- if type(severity_sort) == "table" and severity_sort.reverse then
- table.sort(diagnostics, function(a, b) return a.severity > b.severity end)
+ if type(severity_sort) == 'table' and severity_sort.reverse then
+ table.sort(diagnostics, function(a, b)
+ return a.severity > b.severity
+ end)
else
- table.sort(diagnostics, function(a, b) return a.severity < b.severity end)
+ table.sort(diagnostics, function(a, b)
+ return a.severity < b.severity
+ end)
end
end
local lines = {}
local highlights = {}
- local header = if_nil(opts.header, "Diagnostics:")
+ local header = if_nil(opts.header, 'Diagnostics:')
if header then
- vim.validate { header = { header, function(v)
- return type(v) == "string" or type(v) == "table"
- end, "'string' or 'table'" } }
- if type(header) == "table" then
+ vim.validate({
+ header = {
+ header,
+ function(v)
+ return type(v) == 'string' or type(v) == 'table'
+ end,
+ "'string' or 'table'",
+ },
+ })
+ if type(header) == 'table' then
-- Don't insert any lines for an empty string
- if string.len(if_nil(header[1], "")) > 0 then
+ if string.len(if_nil(header[1], '')) > 0 then
table.insert(lines, header[1])
- table.insert(highlights, {0, header[2] or "Bold"})
+ table.insert(highlights, { 0, header[2] or 'Bold' })
end
elseif #header > 0 then
table.insert(lines, header)
- table.insert(highlights, {0, "Bold"})
+ table.insert(highlights, { 0, 'Bold' })
end
end
@@ -1297,38 +1333,45 @@ function M.open_float(opts, ...)
diagnostics = reformat_diagnostics(opts.format, diagnostics)
end
- if opts.source and (opts.source ~= "if_many" or count_sources(bufnr) > 1) then
+ if opts.source and (opts.source ~= 'if_many' or count_sources(bufnr) > 1) then
diagnostics = prefix_source(diagnostics)
end
- local prefix_opt = if_nil(opts.prefix, (scope == "cursor" and #diagnostics <= 1) and "" or function(_, i)
- return string.format("%d. ", i)
- end)
+ local prefix_opt =
+ if_nil(opts.prefix, (scope == 'cursor' and #diagnostics <= 1) and '' or function(_, i)
+ return string.format('%d. ', i)
+ end)
local prefix, prefix_hl_group
if prefix_opt then
- vim.validate { prefix = { prefix_opt, function(v)
- return type(v) == "string" or type(v) == "table" or type(v) == "function"
- end, "'string' or 'table' or 'function'" } }
- if type(prefix_opt) == "string" then
- prefix, prefix_hl_group = prefix_opt, "NormalFloat"
- elseif type(prefix_opt) == "table" then
- prefix, prefix_hl_group = prefix_opt[1] or "", prefix_opt[2] or "NormalFloat"
+ vim.validate({
+ prefix = {
+ prefix_opt,
+ function(v)
+ return type(v) == 'string' or type(v) == 'table' or type(v) == 'function'
+ end,
+ "'string' or 'table' or 'function'",
+ },
+ })
+ if type(prefix_opt) == 'string' then
+ prefix, prefix_hl_group = prefix_opt, 'NormalFloat'
+ elseif type(prefix_opt) == 'table' then
+ prefix, prefix_hl_group = prefix_opt[1] or '', prefix_opt[2] or 'NormalFloat'
end
end
for i, diagnostic in ipairs(diagnostics) do
- if prefix_opt and type(prefix_opt) == "function" then
+ if prefix_opt and type(prefix_opt) == 'function' then
prefix, prefix_hl_group = prefix_opt(diagnostic, i, #diagnostics)
- prefix, prefix_hl_group = prefix or "", prefix_hl_group or "NormalFloat"
+ prefix, prefix_hl_group = prefix or '', prefix_hl_group or 'NormalFloat'
end
local hiname = floating_highlight_map[diagnostic.severity]
local message_lines = vim.split(diagnostic.message, '\n')
- table.insert(lines, prefix..message_lines[1])
- table.insert(highlights, {#prefix, hiname, prefix_hl_group})
+ table.insert(lines, prefix .. message_lines[1])
+ table.insert(highlights, { #prefix, hiname, prefix_hl_group })
for j = 2, #message_lines do
table.insert(lines, string.rep(' ', #prefix) .. message_lines[j])
- table.insert(highlights, {0, hiname})
+ table.insert(highlights, { 0, hiname })
end
end
@@ -1340,9 +1383,9 @@ function M.open_float(opts, ...)
for i, hi in ipairs(highlights) do
local prefixlen, hiname, prefix_hiname = unpack(hi)
if prefix_hiname then
- vim.api.nvim_buf_add_highlight(float_bufnr, -1, prefix_hiname, i-1, 0, prefixlen)
+ vim.api.nvim_buf_add_highlight(float_bufnr, -1, prefix_hiname, i - 1, 0, prefixlen)
end
- vim.api.nvim_buf_add_highlight(float_bufnr, -1, hiname, i-1, prefixlen, -1)
+ vim.api.nvim_buf_add_highlight(float_bufnr, -1, hiname, i - 1, prefixlen, -1)
end
return float_bufnr, winnr
@@ -1360,27 +1403,23 @@ end
---@param bufnr number|nil Remove diagnostics for the given buffer. When omitted,
--- diagnostics are removed for all buffers.
function M.reset(namespace, bufnr)
- vim.validate {
- namespace = {namespace, 'n', true},
- bufnr = {bufnr, 'n', true},
- }
+ vim.validate({
+ namespace = { namespace, 'n', true },
+ bufnr = { bufnr, 'n', true },
+ })
- local buffers = bufnr and {get_bufnr(bufnr)} or vim.tbl_keys(diagnostic_cache)
+ local buffers = bufnr and { get_bufnr(bufnr) } or vim.tbl_keys(diagnostic_cache)
for _, iter_bufnr in ipairs(buffers) do
- local namespaces = namespace and {namespace} or vim.tbl_keys(diagnostic_cache[iter_bufnr])
+ local namespaces = namespace and { namespace } or vim.tbl_keys(diagnostic_cache[iter_bufnr])
for _, iter_namespace in ipairs(namespaces) do
diagnostic_cache[iter_bufnr][iter_namespace] = nil
M.hide(iter_namespace, iter_bufnr)
end
- vim.api.nvim_buf_call(iter_bufnr, function()
- vim.api.nvim_command(
- string.format(
- "doautocmd <nomodeline> DiagnosticChanged %s",
- vim.fn.fnameescape(vim.api.nvim_buf_get_name(iter_bufnr))
- )
- )
- end)
+ vim.api.nvim_exec_autocmds('DiagnosticChanged', {
+ modeline = false,
+ buffer = iter_bufnr,
+ })
end
end
@@ -1413,7 +1452,7 @@ end
--- omitted, disable diagnostics in all buffers.
---@param namespace number|nil Only disable diagnostics for the given namespace.
function M.disable(bufnr, namespace)
- vim.validate { bufnr = {bufnr, 'n', true}, namespace = {namespace, 'n', true} }
+ vim.validate({ bufnr = { bufnr, 'n', true }, namespace = { namespace, 'n', true } })
if bufnr == nil then
if namespace == nil then
-- Disable everything (including as yet non-existing buffers and
@@ -1421,7 +1460,9 @@ function M.disable(bufnr, namespace)
-- its metatable to always return true. This metatable is removed
-- in enable()
diagnostic_disabled = setmetatable({}, {
- __index = function() return true end,
+ __index = function()
+ return true
+ end,
})
else
local ns = M.get_namespace(namespace)
@@ -1432,7 +1473,7 @@ function M.disable(bufnr, namespace)
if namespace == nil then
diagnostic_disabled[bufnr] = true
else
- if type(diagnostic_disabled[bufnr]) ~= "table" then
+ if type(diagnostic_disabled[bufnr]) ~= 'table' then
diagnostic_disabled[bufnr] = {}
end
diagnostic_disabled[bufnr][namespace] = true
@@ -1448,7 +1489,7 @@ end
--- omitted, enable diagnostics in all buffers.
---@param namespace number|nil Only enable diagnostics for the given namespace.
function M.enable(bufnr, namespace)
- vim.validate { bufnr = {bufnr, 'n', true}, namespace = {namespace, 'n', true} }
+ vim.validate({ bufnr = { bufnr, 'n', true }, namespace = { namespace, 'n', true } })
if bufnr == nil then
if namespace == nil then
-- Enable everything by setting diagnostic_disabled to an empty table
@@ -1462,7 +1503,7 @@ function M.enable(bufnr, namespace)
if namespace == nil then
diagnostic_disabled[bufnr] = nil
else
- if type(diagnostic_disabled[bufnr]) ~= "table" then
+ if type(diagnostic_disabled[bufnr]) ~= 'table' then
return
end
diagnostic_disabled[bufnr][namespace] = nil
@@ -1499,33 +1540,33 @@ end
--- ERROR.
---@return diagnostic |diagnostic-structure| or `nil` if {pat} fails to match {str}.
function M.match(str, pat, groups, severity_map, defaults)
- vim.validate {
+ vim.validate({
str = { str, 's' },
pat = { pat, 's' },
groups = { groups, 't' },
severity_map = { severity_map, 't', true },
defaults = { defaults, 't', true },
- }
+ })
severity_map = severity_map or M.severity
local diagnostic = {}
- local matches = {string.match(str, pat)}
+ local matches = { string.match(str, pat) }
if vim.tbl_isempty(matches) then
return
end
for i, match in ipairs(matches) do
local field = groups[i]
- if field == "severity" then
+ if field == 'severity' then
match = severity_map[match]
- elseif field == "lnum" or field == "end_lnum" or field == "col" or field == "end_col" then
+ elseif field == 'lnum' or field == 'end_lnum' or field == 'col' or field == 'end_col' then
match = assert(tonumber(match)) - 1
end
diagnostic[field] = match
end
- diagnostic = vim.tbl_extend("keep", diagnostic, defaults or {})
+ diagnostic = vim.tbl_extend('keep', diagnostic, defaults or {})
diagnostic.severity = diagnostic.severity or M.severity.ERROR
diagnostic.col = diagnostic.col or 0
diagnostic.end_lnum = diagnostic.end_lnum or diagnostic.lnum
@@ -1546,13 +1587,13 @@ local errlist_type_map = {
---@param diagnostics table List of diagnostics |diagnostic-structure|.
---@return array of quickfix list items |setqflist-what|
function M.toqflist(diagnostics)
- vim.validate {
+ vim.validate({
diagnostics = {
diagnostics,
vim.tbl_islist,
- "a list of diagnostics",
+ 'a list of diagnostics',
},
- }
+ })
local list = {}
for _, v in ipairs(diagnostics) do
@@ -1583,13 +1624,13 @@ end
--- |getloclist()|.
---@return array of diagnostics |diagnostic-structure|
function M.fromqflist(list)
- vim.validate {
+ vim.validate({
list = {
list,
vim.tbl_islist,
- "a list of quickfix items",
+ 'a list of quickfix items',
},
- }
+ })
local diagnostics = {}
for _, item in ipairs(list) do
@@ -1598,7 +1639,7 @@ function M.fromqflist(list)
local col = math.max(0, item.col - 1)
local end_lnum = item.end_lnum > 0 and (item.end_lnum - 1) or lnum
local end_col = item.end_col > 0 and (item.end_col - 1) or col
- local severity = item.type ~= "" and M.severity[item.type] or M.severity.ERROR
+ local severity = item.type ~= '' and M.severity[item.type] or M.severity.ERROR
table.insert(diagnostics, {
bufnr = item.bufnr,
lnum = lnum,
diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index 3d91abc406..70c8cd15eb 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -3,1361 +3,2187 @@ local api = vim.api
local M = {}
---@private
-local function starsetf(ft)
- return {function(path)
- if not vim.g.fg_ignore_pat then
- return ft
- end
+local function starsetf(ft, opts)
+ return {
+ function(path, bufnr)
+ local f = type(ft) == 'function' and ft(path, bufnr) or ft
+ if not vim.g.ft_ignore_pat then
+ return f
+ end
- local re = vim.regex(vim.g.fg_ignore_pat)
- if re:match_str(path) then
- return ft
+ local re = vim.regex(vim.g.ft_ignore_pat)
+ if not re:match_str(path) then
+ return f
+ end
+ end,
+ {
+ -- Starset matches should have lowest priority by default
+ priority = (opts and opts.priority) or -math.huge,
+ },
+ }
+end
+
+---@private
+--- Get a single line or line range from the buffer.
+--- If only start_lnum is specified, return a single line as a string.
+--- If both start_lnum and end_lnum are omitted, return all lines from the buffer.
+---
+---@param bufnr number|nil The buffer to get the lines from
+---@param start_lnum number|nil The line number of the first line (inclusive, 1-based)
+---@param end_lnum number|nil The line number of the last line (inclusive, 1-based)
+---@return table<string>|string Array of lines, or string when end_lnum is omitted
+function M.getlines(bufnr, start_lnum, end_lnum)
+ if end_lnum then
+ -- Return a line range
+ return api.nvim_buf_get_lines(bufnr, start_lnum - 1, end_lnum, false)
+ end
+ if start_lnum then
+ -- Return a single line
+ return api.nvim_buf_get_lines(bufnr, start_lnum - 1, start_lnum, false)[1] or ''
+ else
+ -- Return all lines
+ return api.nvim_buf_get_lines(bufnr, 0, -1, false)
+ end
+end
+
+---@private
+--- Check whether a string matches any of the given Lua patterns.
+---
+---@param s string The string to check
+---@param patterns table<string> A list of Lua patterns
+---@return boolean `true` if s matched a pattern, else `false`
+function M.findany(s, patterns)
+ if s == nil then
+ return false
+ end
+ for _, v in ipairs(patterns) do
+ if s:find(v) then
+ return true
end
- end, {
- -- Starset matches should always have lowest priority
- priority = -math.huge,
- }}
+ end
+ return false
end
---@private
-local function getline(bufnr, lnum)
- return api.nvim_buf_get_lines(bufnr, lnum-1, lnum, false)[1]
+--- Get the next non-whitespace line in the buffer.
+---
+---@param bufnr number The buffer to get the line from
+---@param start_lnum number The line number of the first line to start from (inclusive, 1-based)
+---@return string|nil The first non-blank line if found or `nil` otherwise
+function M.nextnonblank(bufnr, start_lnum)
+ for _, line in ipairs(M.getlines(bufnr, start_lnum, -1)) do
+ if not line:find('^%s*$') then
+ return line
+ end
+ end
+ return nil
end
--- Filetypes based on file extension
+---@private
+--- Check whether the given string matches the Vim regex pattern.
+M.matchregex = (function()
+ local cache = {}
+ return function(s, pattern)
+ if s == nil then
+ return nil
+ end
+ if not cache[pattern] then
+ cache[pattern] = vim.regex(pattern)
+ end
+ return cache[pattern]:match_str(s)
+ end
+end)()
+
-- luacheck: push no unused args
+-- luacheck: push ignore 122
+
+-- Filetypes based on file extension
+---@diagnostic disable: unused-local
local extension = {
-- BEGIN EXTENSION
- ["8th"] = "8th",
- ["a65"] = "a65",
- aap = "aap",
- abap = "abap",
- abc = "abc",
- abl = "abel",
- wrm = "acedb",
- ads = "ada",
- ada = "ada",
- gpr = "ada",
- adb = "ada",
- tdf = "ahdl",
- aidl = "aidl",
- aml = "aml",
- run = "ampl",
- scpt = "applescript",
- ino = "arduino",
- pde = "arduino",
- art = "art",
- asciidoc = "asciidoc",
- adoc = "asciidoc",
- ["asn1"] = "asn",
- asn = "asn",
- atl = "atlas",
- as = "atlas",
- ahk = "autohotkey",
- ["au3"] = "autoit",
- ave = "ave",
- gawk = "awk",
- awk = "awk",
- ref = "b",
- imp = "b",
- mch = "b",
- bc = "bc",
- bdf = "bdf",
- beancount = "beancount",
- bib = "bib",
- bl = "blank",
- bsdl = "bsdl",
- bst = "bst",
- bzl = "bzl",
- bazel = "bzl",
- BUILD = "bzl",
- qc = "c",
- cabal = "cabal",
- cdl = "cdl",
- toc = "cdrtoc",
- cfc = "cf",
- cfm = "cf",
- cfi = "cf",
- cfg = "cfg",
- hgrc = "cfg",
- chf = "ch",
- chai = "chaiscript",
- chs = "chaskell",
- chopro = "chordpro",
- crd = "chordpro",
- crdpro = "chordpro",
- cho = "chordpro",
- chordpro = "chordpro",
- eni = "cl",
- dcl = "clean",
- icl = "clean",
- cljx = "clojure",
- clj = "clojure",
- cljc = "clojure",
- cljs = "clojure",
- cmake = "cmake",
- cmod = "cmod",
- lib = "cobol",
- cob = "cobol",
- cbl = "cobol",
- atg = "coco",
- recipe = "conaryrecipe",
- mklx = "context",
- mkiv = "context",
- mkii = "context",
- mkxl = "context",
- mkvi = "context",
- moc = "cpp",
- hh = "cpp",
- tlh = "cpp",
- inl = "cpp",
- ipp = "cpp",
- ["c++"] = "cpp",
- C = "cpp",
- cxx = "cpp",
- H = "cpp",
- tcc = "cpp",
- hxx = "cpp",
- hpp = "cpp",
- cpp = function()
- if vim.g.cynlib_syntax_for_cc then
- return "cynlib"
+ ['8th'] = '8th',
+ ['a65'] = 'a65',
+ aap = 'aap',
+ abap = 'abap',
+ abc = 'abc',
+ abl = 'abel',
+ wrm = 'acedb',
+ ads = 'ada',
+ ada = 'ada',
+ gpr = 'ada',
+ adb = 'ada',
+ tdf = 'ahdl',
+ aidl = 'aidl',
+ aml = 'aml',
+ run = 'ampl',
+ scpt = 'applescript',
+ ino = 'arduino',
+ pde = 'arduino',
+ art = 'art',
+ asciidoc = 'asciidoc',
+ adoc = 'asciidoc',
+ asa = function(path, bufnr)
+ if vim.g.filetype_asa then
+ return vim.g.filetype_asa
end
- return "cpp"
- end,
- crm = "crm",
- csx = "cs",
- cs = "cs",
- csc = "csc",
- csdl = "csdl",
- fdr = "csp",
- csp = "csp",
- css = "css",
- con = "cterm",
- feature = "cucumber",
- cuh = "cuda",
- cu = "cuda",
- pld = "cupl",
- si = "cuplsim",
- cyn = "cynpp",
- dart = "dart",
- drt = "dart",
- ds = "datascript",
- dcd = "dcd",
- def = "def",
- desc = "desc",
- directory = "desktop",
- desktop = "desktop",
- diff = "diff",
- rej = "diff",
- Dockerfile = "dockerfile",
- sys = "dosbatch",
- bat = "dosbatch",
- wrap = "dosini",
- ini = "dosini",
- dot = "dot",
- gv = "dot",
- drac = "dracula",
- drc = "dracula",
- dtd = "dtd",
- dts = "dts",
- dtsi = "dts",
- dylan = "dylan",
- intr = "dylanintr",
- lid = "dylanlid",
- ecd = "ecd",
- eex = "eelixir",
- leex = "eelixir",
- exs = "elixir",
- elm = "elm",
- epp = "epuppet",
- erl = "erlang",
- hrl = "erlang",
- yaws = "erlang",
- erb = "eruby",
- rhtml = "eruby",
- ec = "esqlc",
- EC = "esqlc",
- strl = "esterel",
- exp = "expect",
- factor = "factor",
- fal = "falcon",
- fan = "fan",
- fwt = "fan",
- fnl = "fennel",
- ["m4gl"] = "fgl",
- ["4gl"] = "fgl",
- ["4gh"] = "fgl",
- fish = "fish",
- focexec = "focexec",
- fex = "focexec",
- fth = "forth",
- ft = "forth",
- FOR = "fortran",
- ["f77"] = "fortran",
- ["f03"] = "fortran",
- fortran = "fortran",
- ["F95"] = "fortran",
- ["f90"] = "fortran",
- ["F03"] = "fortran",
- fpp = "fortran",
- FTN = "fortran",
- ftn = "fortran",
- ["for"] = "fortran",
- ["F90"] = "fortran",
- ["F77"] = "fortran",
- ["f95"] = "fortran",
- FPP = "fortran",
- f = "fortran",
- F = "fortran",
- ["F08"] = "fortran",
- ["f08"] = "fortran",
- fpc = "fpcmake",
- fsl = "framescript",
- bi = "freebasic",
- fb = "freebasic",
- fsi = "fsharp",
- fsx = "fsharp",
- gdmo = "gdmo",
- mo = "gdmo",
- ged = "gedcom",
- gmi = "gemtext",
- gemini = "gemtext",
- gift = "gift",
- gpi = "gnuplot",
- go = "go",
- gp = "gp",
- gs = "grads",
- gretl = "gretl",
- gradle = "groovy",
- groovy = "groovy",
- gsp = "gsp",
- haml = "haml",
- hsm = "hamster",
- ["hs-boot"] = "haskell",
- hsig = "haskell",
- hsc = "haskell",
- hs = "haskell",
- ht = "haste",
- htpp = "hastepreproc",
- hb = "hb",
- sum = "hercules",
- errsum = "hercules",
- ev = "hercules",
- vc = "hercules",
- hex = "hex",
- ["h32"] = "hex",
- hog = "hog",
- hws = "hollywood",
- htt = "httest",
- htb = "httest",
- iba = "ibasic",
- ibi = "ibasic",
- icn = "icon",
- inf = "inform",
- INF = "inform",
- ii = "initng",
- iss = "iss",
- mst = "ist",
- ist = "ist",
- ijs = "j",
- JAL = "jal",
- jal = "jal",
- jpr = "jam",
- jpl = "jam",
- jav = "java",
- java = "java",
- jj = "javacc",
- jjt = "javacc",
- es = "javascript",
- mjs = "javascript",
- javascript = "javascript",
- js = "javascript",
- cjs = "javascript",
- jsx = "javascriptreact",
- clp = "jess",
- jgr = "jgraph",
- ["j73"] = "jovial",
- jov = "jovial",
- jovial = "jovial",
- properties = "jproperties",
- slnf = "json",
- json = "json",
- jsonp = "json",
- webmanifest = "json",
- ipynb = "json",
- ["json-patch"] = "json",
- jsonc = "jsonc",
- jsp = "jsp",
- jl = "julia",
- kv = "kivy",
- kix = "kix",
- kts = "kotlin",
- kt = "kotlin",
- ktm = "kotlin",
- ks = "kscript",
- k = "kwt",
- ACE = "lace",
- ace = "lace",
- latte = "latte",
- lte = "latte",
- ld = "ld",
- ldif = "ldif",
- less = "less",
- lex = "lex",
- lxx = "lex",
- ["l++"] = "lex",
- l = "lex",
- lhs = "lhaskell",
- ll = "lifelines",
- liquid = "liquid",
- cl = "lisp",
- L = "lisp",
- lisp = "lisp",
- el = "lisp",
- lsp = "lisp",
- asd = "lisp",
- lt = "lite",
- lite = "lite",
- lgt = "logtalk",
- lotos = "lotos",
- lot = "lotos",
- lout = "lout",
- lou = "lout",
- ulpc = "lpc",
- lpc = "lpc",
- sig = "lprolog",
- lsl = "lsl",
- lss = "lss",
- nse = "lua",
- rockspec = "lua",
- lua = "lua",
- quake = "m3quake",
- at = "m4",
- eml = "mail",
- mk = "make",
- mak = "make",
- dsp = "make",
- page = "mallard",
- map = "map",
- mws = "maple",
- mpl = "maple",
- mv = "maple",
- mkdn = "markdown",
- md = "markdown",
- mdwn = "markdown",
- mkd = "markdown",
- markdown = "markdown",
- mdown = "markdown",
- mhtml = "mason",
- comp = "mason",
- mason = "mason",
- master = "master",
- mas = "master",
- mel = "mel",
- mf = "mf",
- mgl = "mgl",
- mgp = "mgp",
- my = "mib",
- mib = "mib",
- mix = "mix",
- mixal = "mix",
- nb = "mma",
- mmp = "mmp",
- DEF = "modula2",
- ["m2"] = "modula2",
- MOD = "modula2",
- mi = "modula2",
- ssc = "monk",
- monk = "monk",
- tsc = "monk",
- isc = "monk",
- moo = "moo",
- mp = "mp",
- mof = "msidl",
- odl = "msidl",
- msql = "msql",
- mu = "mupad",
- mush = "mush",
- mysql = "mysql",
- ["n1ql"] = "n1ql",
- nql = "n1ql",
- nanorc = "nanorc",
- ncf = "ncf",
- nginx = "nginx",
- ninja = "ninja",
- nqc = "nqc",
- roff = "nroff",
- tmac = "nroff",
- man = "nroff",
- mom = "nroff",
- nr = "nroff",
- tr = "nroff",
- nsi = "nsis",
- nsh = "nsis",
- obj = "obj",
- mlt = "ocaml",
- mly = "ocaml",
- mll = "ocaml",
- mlp = "ocaml",
- mlip = "ocaml",
- mli = "ocaml",
- ml = "ocaml",
- occ = "occam",
- xom = "omnimark",
- xin = "omnimark",
- opam = "opam",
- ["or"] = "openroad",
- ora = "ora",
- pxsl = "papp",
- papp = "papp",
- pxml = "papp",
- pas = "pascal",
- lpr = "pascal",
- dpr = "pascal",
- pbtxt = "pbtxt",
- g = "pccts",
- pcmk = "pcmk",
- pdf = "pdf",
- plx = "perl",
- psgi = "perl",
- al = "perl",
- ctp = "php",
- php = "php",
- phtml = "php",
- pike = "pike",
- pmod = "pike",
- rcp = "pilrc",
- pli = "pli",
- ["pl1"] = "pli",
- ["p36"] = "plm",
- plm = "plm",
- pac = "plm",
- plp = "plp",
- pls = "plsql",
- plsql = "plsql",
- po = "po",
- pot = "po",
- pod = "pod",
- pk = "poke",
- ps = "postscr",
- epsi = "postscr",
- afm = "postscr",
- epsf = "postscr",
- eps = "postscr",
- pfa = "postscr",
- ai = "postscr",
- pov = "pov",
- ppd = "ppd",
- it = "ppwiz",
- ih = "ppwiz",
- action = "privoxy",
- pc = "proc",
- pdb = "prolog",
- pml = "promela",
- proto = "proto",
- ["psd1"] = "ps1",
- ["psm1"] = "ps1",
- ["ps1"] = "ps1",
- pssc = "ps1",
- ["ps1xml"] = "ps1xml",
- psf = "psf",
- psl = "psl",
- arr = "pyret",
- pxd = "pyrex",
- pyx = "pyrex",
- pyw = "python",
- py = "python",
- pyi = "python",
- ptl = "python",
- rad = "radiance",
- mat = "radiance",
- ["pod6"] = "raku",
- rakudoc = "raku",
- rakutest = "raku",
- rakumod = "raku",
- ["pm6"] = "raku",
- raku = "raku",
- ["t6"] = "raku",
- ["p6"] = "raku",
- raml = "raml",
- rbs = "rbs",
- rego = "rego",
- rem = "remind",
- remind = "remind",
- frt = "reva",
- testUnit = "rexx",
- rex = "rexx",
- orx = "rexx",
- rexx = "rexx",
- jrexx = "rexx",
- rxj = "rexx",
- rexxj = "rexx",
- testGroup = "rexx",
- rxo = "rexx",
- Rd = "rhelp",
- rd = "rhelp",
- rib = "rib",
- Rmd = "rmd",
- rmd = "rmd",
- smd = "rmd",
- Smd = "rmd",
- rnc = "rnc",
- rng = "rng",
- rnw = "rnoweb",
- snw = "rnoweb",
- Rnw = "rnoweb",
- Snw = "rnoweb",
- rsc = "routeros",
- x = "rpcgen",
- rpl = "rpl",
- Srst = "rrst",
- srst = "rrst",
- Rrst = "rrst",
- rrst = "rrst",
- rst = "rst",
- rtf = "rtf",
- rjs = "ruby",
- rxml = "ruby",
- rb = "ruby",
- rant = "ruby",
- ru = "ruby",
- rbw = "ruby",
- gemspec = "ruby",
- builder = "ruby",
- rake = "ruby",
- rs = "rust",
- sas = "sas",
- sass = "sass",
- sa = "sather",
- sbt = "sbt",
- scala = "scala",
- sc = "scala",
- scd = "scdoc",
- ss = "scheme",
- scm = "scheme",
- sld = "scheme",
- rkt = "scheme",
- rktd = "scheme",
- rktl = "scheme",
- sce = "scilab",
- sci = "scilab",
- scss = "scss",
- sd = "sd",
- sdc = "sdc",
- pr = "sdl",
- sdl = "sdl",
- sed = "sed",
- sexp = "sexplib",
- sieve = "sieve",
- siv = "sieve",
- sil = "sil",
- sim = "simula",
- ["s85"] = "sinda",
- sin = "sinda",
- ssm = "sisu",
- sst = "sisu",
- ssi = "sisu",
- ["_sst"] = "sisu",
- ["-sst"] = "sisu",
- il = "skill",
- ils = "skill",
- cdf = "skill",
- sl = "slang",
- ice = "slice",
- score = "slrnsc",
- tpl = "smarty",
- ihlp = "smcl",
- smcl = "smcl",
- hlp = "smcl",
- smith = "smith",
- smt = "smith",
- sml = "sml",
- spt = "snobol4",
- sno = "snobol4",
- sln = "solution",
- sparql = "sparql",
- rq = "sparql",
- spec = "spec",
- spice = "spice",
- sp = "spice",
- spd = "spup",
- spdata = "spup",
- speedup = "spup",
- spi = "spyce",
- spy = "spyce",
- tyc = "sql",
- typ = "sql",
- pkb = "sql",
- tyb = "sql",
- pks = "sql",
- sqlj = "sqlj",
- sqi = "sqr",
- sqr = "sqr",
- nut = "squirrel",
- ["s28"] = "srec",
- ["s37"] = "srec",
- srec = "srec",
- mot = "srec",
- ["s19"] = "srec",
- st = "st",
- imata = "stata",
- ["do"] = "stata",
- mata = "stata",
- ado = "stata",
- stp = "stp",
- svelte = "svelte",
- svg = "svg",
- swift = "swift",
- svh = "systemverilog",
- sv = "systemverilog",
- tak = "tak",
- task = "taskedit",
- tm = "tcl",
- tcl = "tcl",
- itk = "tcl",
- itcl = "tcl",
- tk = "tcl",
- jacl = "tcl",
- tmpl = "template",
- ti = "terminfo",
- dtx = "tex",
- ltx = "tex",
- bbl = "tex",
- latex = "tex",
- sty = "tex",
- texi = "texinfo",
- txi = "texinfo",
- texinfo = "texinfo",
- text = "text",
- tf = "tf",
- tli = "tli",
- toml = "toml",
- tpp = "tpp",
- treetop = "treetop",
- slt = "tsalt",
- tsscl = "tsscl",
- tssgm = "tssgm",
- tssop = "tssop",
- tutor = "tutor",
- twig = "twig",
- ts = function(path, bufnr)
- if getline(bufnr, 1):find("<%?xml") then
- return "xml"
- else
- return "typescript"
+ return 'aspvbs'
+ end,
+ asm = function(path, bufnr)
+ return require('vim.filetype.detect').asm(bufnr)
+ end,
+ lst = function(path, bufnr)
+ return require('vim.filetype.detect').asm(bufnr)
+ end,
+ mac = function(path, bufnr)
+ return require('vim.filetype.detect').asm(bufnr)
+ end,
+ ['asn1'] = 'asn',
+ asn = 'asn',
+ asp = function(path, bufnr)
+ return require('vim.filetype.detect').asp(bufnr)
+ end,
+ atl = 'atlas',
+ as = 'atlas',
+ ahk = 'autohotkey',
+ ['au3'] = 'autoit',
+ ave = 'ave',
+ gawk = 'awk',
+ awk = 'awk',
+ ref = 'b',
+ imp = 'b',
+ mch = 'b',
+ bas = function(path, bufnr)
+ return require('vim.filetype.detect').bas(bufnr)
+ end,
+ bi = function(path, bufnr)
+ return require('vim.filetype.detect').bas(bufnr)
+ end,
+ bm = function(path, bufnr)
+ return require('vim.filetype.detect').bas(bufnr)
+ end,
+ bc = 'bc',
+ bdf = 'bdf',
+ beancount = 'beancount',
+ bib = 'bib',
+ com = function(path, bufnr)
+ return require('vim.filetype.detect').bindzone(bufnr, 'dcl')
+ end,
+ db = function(path, bufnr)
+ return require('vim.filetype.detect').bindzone(bufnr, '')
+ end,
+ bicep = 'bicep',
+ bb = 'bitbake',
+ bbappend = 'bitbake',
+ bbclass = 'bitbake',
+ bl = 'blank',
+ bsdl = 'bsdl',
+ bst = 'bst',
+ btm = function(path, bufnr)
+ return (vim.g.dosbatch_syntax_for_btm and vim.g.dosbatch_syntax_for_btm ~= 0) and 'dosbatch'
+ or 'btm'
+ end,
+ bzl = 'bzl',
+ bazel = 'bzl',
+ BUILD = 'bzl',
+ qc = 'c',
+ cabal = 'cabal',
+ cdl = 'cdl',
+ toc = 'cdrtoc',
+ cfc = 'cf',
+ cfm = 'cf',
+ cfi = 'cf',
+ hgrc = 'cfg',
+ chf = 'ch',
+ chai = 'chaiscript',
+ ch = function(path, bufnr)
+ return require('vim.filetype.detect').change(bufnr)
+ end,
+ chs = 'chaskell',
+ chopro = 'chordpro',
+ crd = 'chordpro',
+ crdpro = 'chordpro',
+ cho = 'chordpro',
+ chordpro = 'chordpro',
+ eni = 'cl',
+ icl = 'clean',
+ cljx = 'clojure',
+ clj = 'clojure',
+ cljc = 'clojure',
+ cljs = 'clojure',
+ cook = 'cook',
+ cmake = 'cmake',
+ cmod = 'cmod',
+ lib = 'cobol',
+ cob = 'cobol',
+ cbl = 'cobol',
+ atg = 'coco',
+ recipe = 'conaryrecipe',
+ hook = function(path, bufnr)
+ return M.getlines(bufnr, 1) == '[Trigger]' and 'conf'
+ end,
+ mklx = 'context',
+ mkiv = 'context',
+ mkii = 'context',
+ mkxl = 'context',
+ mkvi = 'context',
+ control = function(path, bufnr)
+ return require('vim.filetype.detect').control(bufnr)
+ end,
+ copyright = function(path, bufnr)
+ return require('vim.filetype.detect').copyright(bufnr)
+ end,
+ csh = function(path, bufnr)
+ return require('vim.filetype.detect').csh(path, bufnr)
+ end,
+ moc = 'cpp',
+ hh = 'cpp',
+ tlh = 'cpp',
+ inl = 'cpp',
+ ipp = 'cpp',
+ ['c++'] = 'cpp',
+ C = 'cpp',
+ cxx = 'cpp',
+ H = 'cpp',
+ tcc = 'cpp',
+ hxx = 'cpp',
+ hpp = 'cpp',
+ cpp = function(path, bufnr)
+ return vim.g.cynlib_syntax_for_cpp and 'cynlib' or 'cpp'
+ end,
+ cc = function(path, bufnr)
+ return vim.g.cynlib_syntax_for_cc and 'cynlib' or 'cpp'
+ end,
+ crm = 'crm',
+ csx = 'cs',
+ cs = 'cs',
+ csc = 'csc',
+ csdl = 'csdl',
+ cshtml = 'html',
+ fdr = 'csp',
+ csp = 'csp',
+ css = 'css',
+ csv = 'csv',
+ con = 'cterm',
+ feature = 'cucumber',
+ cuh = 'cuda',
+ cu = 'cuda',
+ pld = 'cupl',
+ si = 'cuplsim',
+ cyn = 'cynpp',
+ dart = 'dart',
+ drt = 'dart',
+ ds = 'datascript',
+ dcd = 'dcd',
+ decl = function(path, bufnr)
+ return require('vim.filetype.detect').decl(bufnr)
+ end,
+ dec = function(path, bufnr)
+ return require('vim.filetype.detect').decl(bufnr)
+ end,
+ dcl = function(path, bufnr)
+ return require('vim.filetype.detect').decl(bufnr) or 'clean'
+ end,
+ def = 'def',
+ desc = 'desc',
+ directory = 'desktop',
+ desktop = 'desktop',
+ diff = 'diff',
+ rej = 'diff',
+ Dockerfile = 'dockerfile',
+ dockerfile = 'dockerfile',
+ bat = 'dosbatch',
+ wrap = 'dosini',
+ ini = 'dosini',
+ dot = 'dot',
+ gv = 'dot',
+ drac = 'dracula',
+ drc = 'dracula',
+ dtd = 'dtd',
+ d = function(path, bufnr)
+ return require('vim.filetype.detect').dtrace(bufnr)
+ end,
+ dts = 'dts',
+ dtsi = 'dts',
+ dylan = 'dylan',
+ intr = 'dylanintr',
+ lid = 'dylanlid',
+ e = function(path, bufnr)
+ return require('vim.filetype.detect').e(bufnr)
+ end,
+ E = function(path, bufnr)
+ return require('vim.filetype.detect').e(bufnr)
+ end,
+ ecd = 'ecd',
+ edf = 'edif',
+ edif = 'edif',
+ edo = 'edif',
+ edn = function(path, bufnr)
+ return require('vim.filetype.detect').edn(bufnr)
+ end,
+ eex = 'eelixir',
+ leex = 'eelixir',
+ am = function(path, bufnr)
+ if not path:lower():find('makefile%.am$') then
+ return 'elf'
end
end,
- tsx = "typescriptreact",
- uc = "uc",
- uit = "uil",
- uil = "uil",
- sba = "vb",
- vb = "vb",
- dsm = "vb",
- ctl = "vb",
- vbs = "vb",
- vr = "vera",
- vri = "vera",
- vrh = "vera",
- v = "verilog",
- va = "verilogams",
- vams = "verilogams",
- vhdl = "vhdl",
- vst = "vhdl",
- vhd = "vhdl",
- hdl = "vhdl",
- vho = "vhdl",
- vbe = "vhdl",
- vim = "vim",
- vba = "vim",
- mar = "vmasm",
- cm = "voscm",
- wrl = "vrml",
- vroom = "vroom",
- vue = "vue",
- wat = "wast",
- wast = "wast",
- wm = "webmacro",
- wbt = "winbatch",
- wml = "wml",
- wsml = "wsml",
- ad = "xdefaults",
- xhtml = "xhtml",
- xht = "xhtml",
- msc = "xmath",
- msf = "xmath",
- ["psc1"] = "xml",
- tpm = "xml",
- xliff = "xml",
- atom = "xml",
- xul = "xml",
- cdxml = "xml",
- mpd = "xml",
- rss = "xml",
- fsproj = "xml",
- ui = "xml",
- vbproj = "xml",
- xlf = "xml",
- wsdl = "xml",
- csproj = "xml",
- wpl = "xml",
- xmi = "xml",
- ["xpm2"] = "xpm2",
- xqy = "xquery",
- xqm = "xquery",
- xquery = "xquery",
- xq = "xquery",
- xql = "xquery",
- xs = "xs",
- xsd = "xsd",
- xsl = "xslt",
- xslt = "xslt",
- yy = "yacc",
- ["y++"] = "yacc",
- yxx = "yacc",
- yml = "yaml",
- yaml = "yaml",
- ["z8a"] = "z8a",
- zig = "zig",
- zu = "zimbu",
- zut = "zimbutempl",
- zsh = "zsh",
- E = function() vim.fn["dist#ft#FTe"]() end,
- EU = function() vim.fn["dist#ft#EuphoriaCheck"]() end,
- EW = function() vim.fn["dist#ft#EuphoriaCheck"]() end,
- EX = function() vim.fn["dist#ft#EuphoriaCheck"]() end,
- EXU = function() vim.fn["dist#ft#EuphoriaCheck"]() end,
- EXW = function() vim.fn["dist#ft#EuphoriaCheck"]() end,
- 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,
- 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,
- ch = function() vim.fn["dist#ft#FTchange"]() end,
- com = function() vim.fn["dist#ft#BindzoneCheck"]('dcl') end,
- cpt = function() vim.fn["dist#ft#FThtml"]() end,
- csh = function() vim.fn["dist#ft#CSH"]() end,
- d = function() vim.fn["dist#ft#DtraceCheck"]() end,
- db = function() vim.fn["dist#ft#BindzoneCheck"]('') end,
- dtml = function() vim.fn["dist#ft#FThtml"]() end,
- e = function() vim.fn["dist#ft#FTe"]() end,
- ebuild = function() vim.fn["dist#ft#SetFileTypeSH"]("bash") end,
- eclass = function() vim.fn["dist#ft#SetFileTypeSH"]("bash") end,
- ent = function() vim.fn["dist#ft#FTent"]() end,
- env = function() vim.fn["dist#ft#SetFileTypeSH"](vim.fn.getline(1)) end,
- eu = function() vim.fn["dist#ft#EuphoriaCheck"]() end,
- ew = function() vim.fn["dist#ft#EuphoriaCheck"]() end,
- ex = function() vim.fn["dist#ft#ExCheck"]() end,
- exu = function() vim.fn["dist#ft#EuphoriaCheck"]() end,
- exw = function() vim.fn["dist#ft#EuphoriaCheck"]() end,
- frm = function() vim.fn["dist#ft#FTVB"]("form") end,
- fs = function() vim.fn["dist#ft#FTfs"]() end,
- h = function() vim.fn["dist#ft#FTheader"]() end,
- htm = function() vim.fn["dist#ft#FThtml"]() end,
- html = function() vim.fn["dist#ft#FThtml"]() end,
- i = function() vim.fn["dist#ft#FTprogress_asm"]() end,
- idl = function() vim.fn["dist#ft#FTidl"]() end,
- inc = function() vim.fn["dist#ft#FTinc"]() end,
- inp = function() vim.fn["dist#ft#Check_inp"]() end,
- ksh = function() vim.fn["dist#ft#SetFileTypeSH"]("ksh") end,
- lst = function() vim.fn["dist#ft#FTasm"]() end,
- m = function() vim.fn["dist#ft#FTm"]() end,
- mac = function() vim.fn["dist#ft#FTasm"]() end,
- mc = function() vim.fn["dist#ft#McSetf"]() end,
- 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,
- 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,
- pt = function() vim.fn["dist#ft#FThtml"]() end,
- r = function() vim.fn["dist#ft#FTr"]() end,
- rdf = function() vim.fn["dist#ft#Redif"]() end,
- rules = function() vim.fn["dist#ft#FTRules"]() end,
- sh = function() vim.fn["dist#ft#SetFileTypeSH"](vim.fn.getline(1)) end,
- shtml = function() vim.fn["dist#ft#FThtml"]() end,
- sql = function() vim.fn["dist#ft#SQL"]() end,
- 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,
- 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,
- zsql = function() vim.fn["dist#ft#SQL"]() end,
+ exs = 'elixir',
+ elm = 'elm',
+ elv = 'elvish',
+ ent = function(path, bufnr)
+ return require('vim.filetype.detect').ent(bufnr)
+ end,
+ epp = 'epuppet',
+ erl = 'erlang',
+ hrl = 'erlang',
+ yaws = 'erlang',
+ erb = 'eruby',
+ rhtml = 'eruby',
+ ec = 'esqlc',
+ EC = 'esqlc',
+ strl = 'esterel',
+ eu = function(path, bufnr)
+ return vim.g.filetype_euphoria or 'euphoria3'
+ end,
+ EU = function(path, bufnr)
+ return vim.g.filetype_euphoria or 'euphoria3'
+ end,
+ ew = function(path, bufnr)
+ return vim.g.filetype_euphoria or 'euphoria3'
+ end,
+ EW = function(path, bufnr)
+ return vim.g.filetype_euphoria or 'euphoria3'
+ end,
+ EX = function(path, bufnr)
+ return vim.g.filetype_euphoria or 'euphoria3'
+ end,
+ exu = function(path, bufnr)
+ return vim.g.filetype_euphoria or 'euphoria3'
+ end,
+ EXU = function(path, bufnr)
+ return vim.g.filetype_euphoria or 'euphoria3'
+ end,
+ exw = function(path, bufnr)
+ return vim.g.filetype_euphoria or 'euphoria3'
+ end,
+ EXW = function(path, bufnr)
+ return vim.g.filetype_euphoria or 'euphoria3'
+ end,
+ ex = function(path, bufnr)
+ return require('vim.filetype.detect').ex(bufnr)
+ end,
+ exp = 'expect',
+ factor = 'factor',
+ fal = 'falcon',
+ fan = 'fan',
+ fwt = 'fan',
+ fnl = 'fennel',
+ ['m4gl'] = 'fgl',
+ ['4gl'] = 'fgl',
+ ['4gh'] = 'fgl',
+ fish = 'fish',
+ focexec = 'focexec',
+ fex = 'focexec',
+ fth = 'forth',
+ ft = 'forth',
+ FOR = 'fortran',
+ ['f77'] = 'fortran',
+ ['f03'] = 'fortran',
+ fortran = 'fortran',
+ ['F95'] = 'fortran',
+ ['f90'] = 'fortran',
+ ['F03'] = 'fortran',
+ fpp = 'fortran',
+ FTN = 'fortran',
+ ftn = 'fortran',
+ ['for'] = 'fortran',
+ ['F90'] = 'fortran',
+ ['F77'] = 'fortran',
+ ['f95'] = 'fortran',
+ FPP = 'fortran',
+ f = 'fortran',
+ F = 'fortran',
+ ['F08'] = 'fortran',
+ ['f08'] = 'fortran',
+ fpc = 'fpcmake',
+ fsl = 'framescript',
+ frm = function(path, bufnr)
+ return require('vim.filetype.detect').frm(bufnr)
+ end,
+ fb = 'freebasic',
+ fs = function(path, bufnr)
+ return require('vim.filetype.detect').fs(bufnr)
+ end,
+ fsi = 'fsharp',
+ fsx = 'fsharp',
+ fusion = 'fusion',
+ gdb = 'gdb',
+ gdmo = 'gdmo',
+ mo = 'gdmo',
+ tres = 'gdresource',
+ tscn = 'gdresource',
+ gd = 'gdscript',
+ ged = 'gedcom',
+ gmi = 'gemtext',
+ gemini = 'gemtext',
+ gift = 'gift',
+ gleam = 'gleam',
+ glsl = 'glsl',
+ gpi = 'gnuplot',
+ go = 'go',
+ gp = 'gp',
+ gs = 'grads',
+ gql = 'graphql',
+ graphql = 'graphql',
+ graphqls = 'graphql',
+ gretl = 'gretl',
+ gradle = 'groovy',
+ groovy = 'groovy',
+ gsp = 'gsp',
+ gjs = 'javascript.glimmer',
+ gts = 'typescript.glimmer',
+ hack = 'hack',
+ hackpartial = 'hack',
+ haml = 'haml',
+ hsm = 'hamster',
+ hbs = 'handlebars',
+ ha = 'hare',
+ ['hs-boot'] = 'haskell',
+ hsig = 'haskell',
+ hsc = 'haskell',
+ hs = 'haskell',
+ ht = 'haste',
+ htpp = 'hastepreproc',
+ hb = 'hb',
+ h = function(path, bufnr)
+ return require('vim.filetype.detect').header(bufnr)
+ end,
+ sum = 'hercules',
+ errsum = 'hercules',
+ ev = 'hercules',
+ vc = 'hercules',
+ hcl = 'hcl',
+ heex = 'heex',
+ hex = 'hex',
+ ['h32'] = 'hex',
+ hjson = 'hjson',
+ hog = 'hog',
+ hws = 'hollywood',
+ hoon = 'hoon',
+ cpt = function(path, bufnr)
+ return require('vim.filetype.detect').html(bufnr)
+ end,
+ dtml = function(path, bufnr)
+ return require('vim.filetype.detect').html(bufnr)
+ end,
+ htm = function(path, bufnr)
+ return require('vim.filetype.detect').html(bufnr)
+ end,
+ html = function(path, bufnr)
+ return require('vim.filetype.detect').html(bufnr)
+ end,
+ pt = function(path, bufnr)
+ return require('vim.filetype.detect').html(bufnr)
+ end,
+ shtml = function(path, bufnr)
+ return require('vim.filetype.detect').html(bufnr)
+ end,
+ stm = function(path, bufnr)
+ return require('vim.filetype.detect').html(bufnr)
+ end,
+ htt = 'httest',
+ htb = 'httest',
+ hw = function(path, bufnr)
+ return require('vim.filetype.detect').hw(bufnr)
+ end,
+ module = function(path, bufnr)
+ return require('vim.filetype.detect').hw(bufnr)
+ end,
+ pkg = function(path, bufnr)
+ return require('vim.filetype.detect').hw(bufnr)
+ end,
+ iba = 'ibasic',
+ ibi = 'ibasic',
+ icn = 'icon',
+ idl = function(path, bufnr)
+ return require('vim.filetype.detect').idl(bufnr)
+ end,
+ inc = function(path, bufnr)
+ return require('vim.filetype.detect').inc(bufnr)
+ end,
+ inf = 'inform',
+ INF = 'inform',
+ ii = 'initng',
+ inp = function(path, bufnr)
+ return require('vim.filetype.detect').inp(bufnr)
+ end,
+ ms = function(path, bufnr)
+ return require('vim.filetype.detect').nroff(bufnr) or 'xmath'
+ end,
+ iss = 'iss',
+ mst = 'ist',
+ ist = 'ist',
+ ijs = 'j',
+ JAL = 'jal',
+ jal = 'jal',
+ jpr = 'jam',
+ jpl = 'jam',
+ jav = 'java',
+ java = 'java',
+ jj = 'javacc',
+ jjt = 'javacc',
+ es = 'javascript',
+ mjs = 'javascript',
+ javascript = 'javascript',
+ js = 'javascript',
+ cjs = 'javascript',
+ jsx = 'javascriptreact',
+ clp = 'jess',
+ jgr = 'jgraph',
+ ['j73'] = 'jovial',
+ jov = 'jovial',
+ jovial = 'jovial',
+ properties = 'jproperties',
+ slnf = 'json',
+ json = 'json',
+ jsonp = 'json',
+ webmanifest = 'json',
+ ipynb = 'json',
+ ['json-patch'] = 'json',
+ json5 = 'json5',
+ jsonc = 'jsonc',
+ jsp = 'jsp',
+ jl = 'julia',
+ kv = 'kivy',
+ kix = 'kix',
+ kts = 'kotlin',
+ kt = 'kotlin',
+ ktm = 'kotlin',
+ ks = 'kscript',
+ k = 'kwt',
+ ACE = 'lace',
+ ace = 'lace',
+ latte = 'latte',
+ lte = 'latte',
+ ld = 'ld',
+ ldif = 'ldif',
+ journal = 'ledger',
+ ldg = 'ledger',
+ ledger = 'ledger',
+ less = 'less',
+ lex = 'lex',
+ lxx = 'lex',
+ ['l++'] = 'lex',
+ l = 'lex',
+ lhs = 'lhaskell',
+ ll = 'lifelines',
+ ly = 'lilypond',
+ ily = 'lilypond',
+ liquid = 'liquid',
+ cl = 'lisp',
+ L = 'lisp',
+ lisp = 'lisp',
+ el = 'lisp',
+ lsp = 'lisp',
+ asd = 'lisp',
+ lt = 'lite',
+ lite = 'lite',
+ lgt = 'logtalk',
+ lotos = 'lotos',
+ lot = 'lotos',
+ lout = 'lout',
+ lou = 'lout',
+ ulpc = 'lpc',
+ lpc = 'lpc',
+ c = function(path, bufnr)
+ return require('vim.filetype.detect').lpc(bufnr)
+ end,
+ lsl = 'lsl',
+ lss = 'lss',
+ nse = 'lua',
+ rockspec = 'lua',
+ lua = 'lua',
+ m = function(path, bufnr)
+ return require('vim.filetype.detect').m(bufnr)
+ end,
+ at = 'm4',
+ mc = function(path, bufnr)
+ return require('vim.filetype.detect').mc(bufnr)
+ end,
+ quake = 'm3quake',
+ ['m4'] = function(path, bufnr)
+ path = path:lower()
+ return not (path:find('html%.m4$') or path:find('fvwm2rc')) and 'm4'
+ end,
+ eml = 'mail',
+ mk = 'make',
+ mak = 'make',
+ dsp = 'make',
+ page = 'mallard',
+ map = 'map',
+ mws = 'maple',
+ mpl = 'maple',
+ mv = 'maple',
+ mkdn = 'markdown',
+ md = 'markdown',
+ mdwn = 'markdown',
+ mkd = 'markdown',
+ markdown = 'markdown',
+ mdown = 'markdown',
+ mhtml = 'mason',
+ comp = 'mason',
+ mason = 'mason',
+ master = 'master',
+ mas = 'master',
+ demo = 'maxima',
+ dm1 = 'maxima',
+ dm2 = 'maxima',
+ dm3 = 'maxima',
+ dmt = 'maxima',
+ wxm = 'maxima',
+ mel = 'mel',
+ mf = 'mf',
+ mgl = 'mgl',
+ mgp = 'mgp',
+ my = 'mib',
+ mib = 'mib',
+ mix = 'mix',
+ mixal = 'mix',
+ mm = function(path, bufnr)
+ return require('vim.filetype.detect').mm(bufnr)
+ end,
+ nb = 'mma',
+ mmp = 'mmp',
+ mms = function(path, bufnr)
+ return require('vim.filetype.detect').mms(bufnr)
+ end,
+ DEF = 'modula2',
+ ['m2'] = 'modula2',
+ mi = 'modula2',
+ ssc = 'monk',
+ monk = 'monk',
+ tsc = 'monk',
+ isc = 'monk',
+ moo = 'moo',
+ moon = 'moonscript',
+ mp = 'mp',
+ mof = 'msidl',
+ odl = 'msidl',
+ msql = 'msql',
+ mu = 'mupad',
+ mush = 'mush',
+ mysql = 'mysql',
+ ['n1ql'] = 'n1ql',
+ nql = 'n1ql',
+ nanorc = 'nanorc',
+ ncf = 'ncf',
+ nginx = 'nginx',
+ ninja = 'ninja',
+ nix = 'nix',
+ nqc = 'nqc',
+ roff = 'nroff',
+ tmac = 'nroff',
+ man = 'nroff',
+ mom = 'nroff',
+ nr = 'nroff',
+ tr = 'nroff',
+ nsi = 'nsis',
+ nsh = 'nsis',
+ obj = 'obj',
+ mlt = 'ocaml',
+ mly = 'ocaml',
+ mll = 'ocaml',
+ mlp = 'ocaml',
+ mlip = 'ocaml',
+ mli = 'ocaml',
+ ml = 'ocaml',
+ occ = 'occam',
+ xom = 'omnimark',
+ xin = 'omnimark',
+ opam = 'opam',
+ ['or'] = 'openroad',
+ scad = 'openscad',
+ ora = 'ora',
+ org = 'org',
+ org_archive = 'org',
+ pxsl = 'papp',
+ papp = 'papp',
+ pxml = 'papp',
+ pas = 'pascal',
+ lpr = 'pascal',
+ dpr = 'pascal',
+ pbtxt = 'pbtxt',
+ g = 'pccts',
+ pcmk = 'pcmk',
+ pdf = 'pdf',
+ plx = 'perl',
+ prisma = 'prisma',
+ psgi = 'perl',
+ al = 'perl',
+ ctp = 'php',
+ php = 'php',
+ phpt = 'php',
+ phtml = 'php',
+ pike = 'pike',
+ pmod = 'pike',
+ rcp = 'pilrc',
+ PL = function(path, bufnr)
+ return require('vim.filetype.detect').pl(bufnr)
+ end,
+ pli = 'pli',
+ ['pl1'] = 'pli',
+ ['p36'] = 'plm',
+ plm = 'plm',
+ pac = 'plm',
+ plp = 'plp',
+ pls = 'plsql',
+ plsql = 'plsql',
+ po = 'po',
+ pot = 'po',
+ pod = 'pod',
+ pk = 'poke',
+ ps = 'postscr',
+ epsi = 'postscr',
+ afm = 'postscr',
+ epsf = 'postscr',
+ eps = 'postscr',
+ pfa = 'postscr',
+ ai = 'postscr',
+ pov = 'pov',
+ ppd = 'ppd',
+ it = 'ppwiz',
+ ih = 'ppwiz',
+ action = 'privoxy',
+ pc = 'proc',
+ pdb = 'prolog',
+ pml = 'promela',
+ proto = 'proto',
+ ['psd1'] = 'ps1',
+ ['psm1'] = 'ps1',
+ ['ps1'] = 'ps1',
+ pssc = 'ps1',
+ ['ps1xml'] = 'ps1xml',
+ psf = 'psf',
+ psl = 'psl',
+ pug = 'pug',
+ arr = 'pyret',
+ pxd = 'pyrex',
+ pyx = 'pyrex',
+ pyw = 'python',
+ py = 'python',
+ pyi = 'python',
+ ptl = 'python',
+ ql = 'ql',
+ qll = 'ql',
+ R = function(path, bufnr)
+ return require('vim.filetype.detect').r(bufnr)
+ end,
+ rad = 'radiance',
+ mat = 'radiance',
+ ['pod6'] = 'raku',
+ rakudoc = 'raku',
+ rakutest = 'raku',
+ rakumod = 'raku',
+ ['pm6'] = 'raku',
+ raku = 'raku',
+ ['t6'] = 'raku',
+ ['p6'] = 'raku',
+ raml = 'raml',
+ rbs = 'rbs',
+ rego = 'rego',
+ rem = 'remind',
+ remind = 'remind',
+ res = 'rescript',
+ resi = 'rescript',
+ frt = 'reva',
+ testUnit = 'rexx',
+ rex = 'rexx',
+ orx = 'rexx',
+ rexx = 'rexx',
+ jrexx = 'rexx',
+ rxj = 'rexx',
+ rexxj = 'rexx',
+ testGroup = 'rexx',
+ rxo = 'rexx',
+ Rd = 'rhelp',
+ rd = 'rhelp',
+ rib = 'rib',
+ Rmd = 'rmd',
+ rmd = 'rmd',
+ smd = 'rmd',
+ Smd = 'rmd',
+ rnc = 'rnc',
+ rng = 'rng',
+ rnw = 'rnoweb',
+ snw = 'rnoweb',
+ Rnw = 'rnoweb',
+ Snw = 'rnoweb',
+ robot = 'robot',
+ resource = 'robot',
+ rsc = 'routeros',
+ x = 'rpcgen',
+ rpl = 'rpl',
+ Srst = 'rrst',
+ srst = 'rrst',
+ Rrst = 'rrst',
+ rrst = 'rrst',
+ rst = 'rst',
+ rtf = 'rtf',
+ rjs = 'ruby',
+ rxml = 'ruby',
+ rb = 'ruby',
+ rant = 'ruby',
+ ru = 'ruby',
+ rbw = 'ruby',
+ gemspec = 'ruby',
+ builder = 'ruby',
+ rake = 'ruby',
+ rs = 'rust',
+ sas = 'sas',
+ sass = 'sass',
+ sa = 'sather',
+ sbt = 'sbt',
+ scala = 'scala',
+ ss = 'scheme',
+ scm = 'scheme',
+ sld = 'scheme',
+ rkt = 'scheme',
+ rktd = 'scheme',
+ rktl = 'scheme',
+ sce = 'scilab',
+ sci = 'scilab',
+ scss = 'scss',
+ sd = 'sd',
+ sdc = 'sdc',
+ pr = 'sdl',
+ sdl = 'sdl',
+ sed = 'sed',
+ sexp = 'sexplib',
+ bash = function(path, bufnr)
+ return require('vim.filetype.detect').sh(path, M.getlines(bufnr), 'bash')
+ end,
+ ebuild = function(path, bufnr)
+ return require('vim.filetype.detect').sh(path, M.getlines(bufnr), 'bash')
+ end,
+ eclass = function(path, bufnr)
+ return require('vim.filetype.detect').sh(path, M.getlines(bufnr), 'bash')
+ end,
+ env = function(path, bufnr)
+ return require('vim.filetype.detect').sh(path, M.getlines(bufnr))
+ end,
+ ksh = function(path, bufnr)
+ return require('vim.filetype.detect').sh(path, M.getlines(bufnr), 'ksh')
+ end,
+ sh = function(path, bufnr)
+ return require('vim.filetype.detect').sh(path, M.getlines(bufnr))
+ end,
+ sieve = 'sieve',
+ siv = 'sieve',
+ sig = function(path, bufnr)
+ return require('vim.filetype.detect').sig(bufnr)
+ end,
+ sil = 'sil',
+ sim = 'simula',
+ ['s85'] = 'sinda',
+ sin = 'sinda',
+ ssm = 'sisu',
+ sst = 'sisu',
+ ssi = 'sisu',
+ ['_sst'] = 'sisu',
+ ['-sst'] = 'sisu',
+ il = 'skill',
+ ils = 'skill',
+ cdf = 'skill',
+ sl = 'slang',
+ ice = 'slice',
+ score = 'slrnsc',
+ sol = 'solidity',
+ tpl = 'smarty',
+ ihlp = 'smcl',
+ smcl = 'smcl',
+ hlp = 'smcl',
+ smith = 'smith',
+ smt = 'smith',
+ sml = 'sml',
+ spt = 'snobol4',
+ sno = 'snobol4',
+ sln = 'solution',
+ sparql = 'sparql',
+ rq = 'sparql',
+ spec = 'spec',
+ spice = 'spice',
+ sp = 'spice',
+ spd = 'spup',
+ spdata = 'spup',
+ speedup = 'spup',
+ spi = 'spyce',
+ spy = 'spyce',
+ tyc = 'sql',
+ typ = 'sql',
+ pkb = 'sql',
+ tyb = 'sql',
+ pks = 'sql',
+ sqlj = 'sqlj',
+ sqi = 'sqr',
+ sqr = 'sqr',
+ nut = 'squirrel',
+ ['s28'] = 'srec',
+ ['s37'] = 'srec',
+ srec = 'srec',
+ mot = 'srec',
+ ['s19'] = 'srec',
+ st = 'st',
+ imata = 'stata',
+ ['do'] = 'stata',
+ mata = 'stata',
+ ado = 'stata',
+ stp = 'stp',
+ quark = 'supercollider',
+ sface = 'surface',
+ svelte = 'svelte',
+ svg = 'svg',
+ swift = 'swift',
+ svh = 'systemverilog',
+ sv = 'systemverilog',
+ tak = 'tak',
+ task = 'taskedit',
+ tm = 'tcl',
+ tcl = 'tcl',
+ itk = 'tcl',
+ itcl = 'tcl',
+ tk = 'tcl',
+ jacl = 'tcl',
+ tl = 'teal',
+ tmpl = 'template',
+ ti = 'terminfo',
+ dtx = 'tex',
+ ltx = 'tex',
+ bbl = 'tex',
+ latex = 'tex',
+ sty = 'tex',
+ cls = function(path, bufnr)
+ return require('vim.filetype.detect').cls(bufnr)
+ end,
+ texi = 'texinfo',
+ txi = 'texinfo',
+ texinfo = 'texinfo',
+ text = 'text',
+ tfvars = 'terraform',
+ tla = 'tla',
+ tli = 'tli',
+ toml = 'toml',
+ tpp = 'tpp',
+ treetop = 'treetop',
+ slt = 'tsalt',
+ tsscl = 'tsscl',
+ tssgm = 'tssgm',
+ tssop = 'tssop',
+ tsv = 'tsv',
+ tutor = 'tutor',
+ twig = 'twig',
+ ts = function(path, bufnr)
+ return M.getlines(bufnr, 1):find('<%?xml') and 'xml' or 'typescript'
+ end,
+ tsx = 'typescriptreact',
+ uc = 'uc',
+ uit = 'uil',
+ uil = 'uil',
+ sba = 'vb',
+ vb = 'vb',
+ dsm = 'vb',
+ ctl = 'vb',
+ vbs = 'vb',
+ vr = 'vera',
+ vri = 'vera',
+ vrh = 'vera',
+ v = 'verilog',
+ va = 'verilogams',
+ vams = 'verilogams',
+ vhdl = 'vhdl',
+ vst = 'vhdl',
+ vhd = 'vhdl',
+ hdl = 'vhdl',
+ vho = 'vhdl',
+ vbe = 'vhdl',
+ vim = 'vim',
+ vba = 'vim',
+ mar = 'vmasm',
+ cm = 'voscm',
+ wrl = 'vrml',
+ vroom = 'vroom',
+ vue = 'vue',
+ wat = 'wast',
+ wast = 'wast',
+ wm = 'webmacro',
+ wbt = 'winbatch',
+ wml = 'wml',
+ wsml = 'wsml',
+ ad = 'xdefaults',
+ xhtml = 'xhtml',
+ xht = 'xhtml',
+ msc = 'xmath',
+ msf = 'xmath',
+ ['psc1'] = 'xml',
+ tpm = 'xml',
+ xliff = 'xml',
+ atom = 'xml',
+ xul = 'xml',
+ cdxml = 'xml',
+ mpd = 'xml',
+ rss = 'xml',
+ fsproj = 'xml',
+ ui = 'xml',
+ vbproj = 'xml',
+ xlf = 'xml',
+ wsdl = 'xml',
+ csproj = 'xml',
+ wpl = 'xml',
+ xmi = 'xml',
+ xpm = function(path, bufnr)
+ return M.getlines(bufnr, 1):find('XPM2') and 'xpm2' or 'xpm'
+ end,
+ ['xpm2'] = 'xpm2',
+ xqy = 'xquery',
+ xqm = 'xquery',
+ xquery = 'xquery',
+ xq = 'xquery',
+ xql = 'xquery',
+ xs = 'xs',
+ xsd = 'xsd',
+ xsl = 'xslt',
+ xslt = 'xslt',
+ yy = 'yacc',
+ ['y++'] = 'yacc',
+ yxx = 'yacc',
+ yml = 'yaml',
+ yaml = 'yaml',
+ yang = 'yang',
+ ['z8a'] = 'z8a',
+ zig = 'zig',
+ zu = 'zimbu',
+ zut = 'zimbutempl',
+ zsh = 'zsh',
+ vala = 'vala',
+ web = function(path, bufnr)
+ return require('vim.filetype.detect').web(bufnr)
+ end,
+ pl = function(path, bufnr)
+ return require('vim.filetype.detect').pl(bufnr)
+ end,
+ pp = function(path, bufnr)
+ return require('vim.filetype.detect').pp(bufnr)
+ end,
+ i = function(path, bufnr)
+ return require('vim.filetype.detect').progress_asm(bufnr)
+ end,
+ w = function(path, bufnr)
+ return require('vim.filetype.detect').progress_cweb(bufnr)
+ end,
+ p = function(path, bufnr)
+ return require('vim.filetype.detect').progress_pascal(bufnr)
+ end,
+ pro = function(path, bufnr)
+ return require('vim.filetype.detect').proto(bufnr, 'idlang')
+ end,
+ patch = function(path, bufnr)
+ return require('vim.filetype.detect').patch(bufnr)
+ end,
+ r = function(path, bufnr)
+ return require('vim.filetype.detect').r(bufnr)
+ end,
+ rdf = function(path, bufnr)
+ return require('vim.filetype.detect').redif(bufnr)
+ end,
+ rules = function(path, bufnr)
+ return require('vim.filetype.detect').rules(path)
+ end,
+ sc = function(path, bufnr)
+ return require('vim.filetype.detect').sc(bufnr)
+ end,
+ scd = function(path, bufnr)
+ return require('vim.filetype.detect').scd(bufnr)
+ end,
+ tcsh = function(path, bufnr)
+ return require('vim.filetype.detect').shell(path, M.getlines(bufnr), 'tcsh')
+ end,
+ sql = function(path, bufnr)
+ return vim.g.filetype_sql and vim.g.filetype_sql or 'sql'
+ end,
+ zsql = function(path, bufnr)
+ return vim.g.filetype_sql and vim.g.filetype_sql or 'sql'
+ end,
+ tex = function(path, bufnr)
+ return require('vim.filetype.detect').tex(path, bufnr)
+ end,
+ tf = function(path, bufnr)
+ return require('vim.filetype.detect').tf(bufnr)
+ end,
txt = function(path, bufnr)
- --helpfiles match *.txt, but should have a modeline as last line
- if not getline(bufnr, -1):match("vim:.*ft=help") then
- return "text"
+ return require('vim.filetype.detect').txt(bufnr)
+ end,
+ xml = function(path, bufnr)
+ return require('vim.filetype.detect').xml(bufnr)
+ end,
+ y = function(path, bufnr)
+ return require('vim.filetype.detect').y(bufnr)
+ end,
+ cmd = function(path, bufnr)
+ return M.getlines(bufnr, 1):find('^/%*') and 'rexx' or 'dosbatch'
+ end,
+ rul = function(path, bufnr)
+ return require('vim.filetype.detect').rul(bufnr)
+ end,
+ cpy = function(path, bufnr)
+ return M.getlines(bufnr, 1):find('^##') and 'python' or 'cobol'
+ end,
+ dsl = function(path, bufnr)
+ return M.getlines(bufnr, 1):find('^%s*<!') and 'dsl' or 'structurizr'
+ end,
+ smil = function(path, bufnr)
+ return M.getlines(bufnr, 1):find('<%?%s*xml.*%?>') and 'xml' or 'smil'
+ end,
+ smi = function(path, bufnr)
+ return require('vim.filetype.detect').smi(bufnr)
+ end,
+ install = function(path, bufnr)
+ return require('vim.filetype.detect').install(path, bufnr)
+ end,
+ pm = function(path, bufnr)
+ return require('vim.filetype.detect').pm(bufnr)
+ end,
+ me = function(path, bufnr)
+ return require('vim.filetype.detect').me(path)
+ end,
+ reg = function(path, bufnr)
+ return require('vim.filetype.detect').reg(bufnr)
+ end,
+ ttl = function(path, bufnr)
+ return require('vim.filetype.detect').ttl(bufnr)
+ end,
+ rc = function(path, bufnr)
+ if not path:find('/etc/Muttrc%.d/') then
+ return 'rc'
+ end
+ end,
+ rch = function(path, bufnr)
+ if not path:find('/etc/Muttrc%.d/') then
+ return 'rc'
end
end,
+ class = function(path, bufnr)
+ require('vim.filetype.detect').class(bufnr)
+ end,
+ sgml = function(path, bufnr)
+ return require('vim.filetype.detect').sgml(bufnr)
+ end,
+ sgm = function(path, bufnr)
+ return require('vim.filetype.detect').sgml(bufnr)
+ end,
+ t = function(path, bufnr)
+ local nroff = require('vim.filetype.detect').nroff(bufnr)
+ return nroff or require('vim.filetype.detect').perl(path, bufnr) or 'tads'
+ end,
+ -- Ignored extensions
+ bak = function(path, bufnr)
+ local root = vim.fn.fnamemodify(path, ':r')
+ return M.match({ buf = bufnr, filename = root })
+ end,
+ ['dpkg-bak'] = function(path, bufnr)
+ local root = vim.fn.fnamemodify(path, ':r')
+ return M.match({ buf = bufnr, filename = root })
+ end,
+ ['dpkg-dist'] = function(path, bufnr)
+ local root = vim.fn.fnamemodify(path, ':r')
+ return M.match({ buf = bufnr, filename = root })
+ end,
+ ['dpkg-old'] = function(path, bufnr)
+ local root = vim.fn.fnamemodify(path, ':r')
+ return M.match({ buf = bufnr, filename = root })
+ end,
+ ['dpkg-new'] = function(path, bufnr)
+ local root = vim.fn.fnamemodify(path, ':r')
+ return M.match({ buf = bufnr, filename = root })
+ end,
+ ['in'] = function(path, bufnr)
+ if vim.fs.basename(path) ~= 'configure.in' then
+ local root = vim.fn.fnamemodify(path, ':r')
+ return M.match({ buf = bufnr, filename = root })
+ end
+ end,
+ new = function(path, bufnr)
+ local root = vim.fn.fnamemodify(path, ':r')
+ return M.match({ buf = bufnr, filename = root })
+ end,
+ old = function(path, bufnr)
+ local root = vim.fn.fnamemodify(path, ':r')
+ return M.match({ buf = bufnr, filename = root })
+ end,
+ orig = function(path, bufnr)
+ local root = vim.fn.fnamemodify(path, ':r')
+ return M.match({ buf = bufnr, filename = root })
+ end,
+ pacsave = function(path, bufnr)
+ local root = vim.fn.fnamemodify(path, ':r')
+ return M.match({ buf = bufnr, filename = root })
+ end,
+ pacnew = function(path, bufnr)
+ local root = vim.fn.fnamemodify(path, ':r')
+ return M.match({ buf = bufnr, filename = root })
+ end,
+ rpmsave = function(path, bufnr)
+ local root = vim.fn.fnamemodify(path, ':r')
+ return M.match({ buf = bufnr, filename = root })
+ end,
+ rmpnew = function(path, bufnr)
+ local root = vim.fn.fnamemodify(path, ':r')
+ return M.match({ buf = bufnr, filename = root })
+ end,
-- END EXTENSION
}
local filename = {
-- BEGIN FILENAME
- ["a2psrc"] = "a2ps",
- ["/etc/a2ps.cfg"] = "a2ps",
- [".a2psrc"] = "a2ps",
- [".asoundrc"] = "alsaconf",
- ["/usr/share/alsa/alsa.conf"] = "alsaconf",
- ["/etc/asound.conf"] = "alsaconf",
- ["build.xml"] = "ant",
- [".htaccess"] = "apache",
- ["apt.conf"] = "aptconf",
- ["/.aptitude/config"] = "aptconf",
- ["=tagging-method"] = "arch",
- [".arch-inventory"] = "arch",
- ["GNUmakefile.am"] = "automake",
- ["named.root"] = "bindzone",
- WORKSPACE = "bzl",
- BUILD = "bzl",
- ["cabal.config"] = "cabalconfig",
- ["cabal.project"] = "cabalproject",
- calendar = "calendar",
- catalog = "catalog",
- ["/etc/cdrdao.conf"] = "cdrdaoconf",
- [".cdrdao"] = "cdrdaoconf",
- ["/etc/default/cdrdao"] = "cdrdaoconf",
- ["/etc/defaults/cdrdao"] = "cdrdaoconf",
- ["cfengine.conf"] = "cfengine",
- ["CMakeLists.txt"] = "cmake",
- ["auto.master"] = "conf",
- ["configure.in"] = "config",
- ["configure.ac"] = "config",
- [".cvsrc"] = "cvsrc",
- ["/debian/changelog"] = "debchangelog",
- ["changelog.dch"] = "debchangelog",
- ["changelog.Debian"] = "debchangelog",
- ["NEWS.dch"] = "debchangelog",
- ["NEWS.Debian"] = "debchangelog",
- ["/debian/control"] = "debcontrol",
- ["/debian/copyright"] = "debcopyright",
- ["/etc/apt/sources.list"] = "debsources",
- ["denyhosts.conf"] = "denyhosts",
- ["dict.conf"] = "dictconf",
- [".dictrc"] = "dictconf",
- ["/etc/DIR_COLORS"] = "dircolors",
- [".dir_colors"] = "dircolors",
- [".dircolors"] = "dircolors",
- ["/etc/dnsmasq.conf"] = "dnsmasq",
- Containerfile = "dockerfile",
- Dockerfile = "dockerfile",
- npmrc = "dosini",
- ["/etc/yum.conf"] = "dosini",
- ["/etc/pacman.conf"] = "dosini",
- [".npmrc"] = "dosini",
- [".editorconfig"] = "dosini",
- dune = "dune",
- jbuild = "dune",
- ["dune-workspace"] = "dune",
- ["dune-project"] = "dune",
- ["elinks.conf"] = "elinks",
- ["mix.lock"] = "elixir",
- ["filter-rules"] = "elmfilt",
- ["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",
- gdbinit = "gdb",
- ["lltxxxxx.txt"] = "gedcom",
- ["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",
- [".gnashrc"] = "gnash",
- [".gnashpluginrc"] = "gnash",
- gnashpluginrc = "gnash",
- gnashrc = "gnash",
- [".gprc"] = "gp",
- ["/.gnupg/gpg.conf"] = "gpg",
- ["/.gnupg/options"] = "gpg",
- ["/var/backups/gshadow.bak"] = "group",
- ["/etc/gshadow"] = "group",
- ["/etc/group-"] = "group",
- ["/etc/gshadow.edit"] = "group",
- ["/etc/gshadow-"] = "group",
- ["/etc/group"] = "group",
- ["/var/backups/group.bak"] = "group",
- ["/etc/group.edit"] = "group",
- ["/boot/grub/menu.lst"] = "grub",
- ["/etc/grub.conf"] = "grub",
- ["/boot/grub/grub.conf"] = "grub",
- [".gtkrc"] = "gtkrc",
- gtkrc = "gtkrc",
- ["snort.conf"] = "hog",
- ["vision.conf"] = "hog",
- ["/etc/host.conf"] = "hostconf",
- ["/etc/hosts.allow"] = "hostsaccess",
- ["/etc/hosts.deny"] = "hostsaccess",
- ["/i3/config"] = "i3config",
- ["/sway/config"] = "i3config",
- ["/.sway/config"] = "i3config",
- ["/.i3/config"] = "i3config",
- ["/.icewm/menu"] = "icemenu",
- [".indent.pro"] = "indent",
- indentrc = "indent",
- inittab = "inittab",
- ["ipf.conf"] = "ipfilter",
- ["ipf6.conf"] = "ipfilter",
- ["ipf.rules"] = "ipfilter",
- [".eslintrc"] = "json",
- [".babelrc"] = "json",
- ["Pipfile.lock"] = "json",
- [".firebaserc"] = "json",
- [".prettierrc"] = "json",
- Kconfig = "kconfig",
- ["Kconfig.debug"] = "kconfig",
- ["lftp.conf"] = "lftp",
- [".lftprc"] = "lftp",
- ["/.libao"] = "libao",
- ["/etc/libao.conf"] = "libao",
- ["lilo.conf"] = "lilo",
- ["/etc/limits"] = "limits",
- [".emacs"] = "lisp",
- sbclrc = "lisp",
- [".sbclrc"] = "lisp",
- [".sawfishrc"] = "lisp",
- ["/etc/login.access"] = "loginaccess",
- ["/etc/login.defs"] = "logindefs",
- ["lynx.cfg"] = "lynx",
- ["m3overrides"] = "m3build",
- ["m3makefile"] = "m3build",
- ["cm3.cfg"] = "m3quake",
- [".followup"] = "mail",
- [".article"] = "mail",
- [".letter"] = "mail",
- ["/etc/aliases"] = "mailaliases",
- ["/etc/mail/aliases"] = "mailaliases",
- mailcap = "mailcap",
- [".mailcap"] = "mailcap",
- ["/etc/man.conf"] = "manconf",
- ["man.config"] = "manconf",
- ["meson.build"] = "meson",
- ["meson_options.txt"] = "meson",
- ["/etc/conf.modules"] = "modconf",
- ["/etc/modules"] = "modconf",
- ["/etc/modules.conf"] = "modconf",
- ["/.mplayer/config"] = "mplayerconf",
- ["mplayer.conf"] = "mplayerconf",
- mrxvtrc = "mrxvtrc",
- [".mrxvtrc"] = "mrxvtrc",
- ["/etc/nanorc"] = "nanorc",
- Neomuttrc = "neomuttrc",
- [".netrc"] = "netrc",
- [".ocamlinit"] = "ocaml",
- [".octaverc"] = "octave",
- octaverc = "octave",
- ["octave.conf"] = "octave",
- opam = "opam",
- ["/etc/pam.conf"] = "pamconf",
- ["pam_env.conf"] = "pamenv",
- [".pam_environment"] = "pamenv",
- ["/var/backups/passwd.bak"] = "passwd",
- ["/var/backups/shadow.bak"] = "passwd",
- ["/etc/passwd"] = "passwd",
- ["/etc/passwd-"] = "passwd",
- ["/etc/shadow.edit"] = "passwd",
- ["/etc/shadow-"] = "passwd",
- ["/etc/shadow"] = "passwd",
- ["/etc/passwd.edit"] = "passwd",
- ["pf.conf"] = "pf",
- ["main.cf"] = "pfmain",
- pinerc = "pine",
- [".pinercex"] = "pine",
- [".pinerc"] = "pine",
- pinercex = "pine",
- ["/etc/pinforc"] = "pinfo",
- ["/.pinforc"] = "pinfo",
- [".povrayrc"] = "povini",
- [".procmailrc"] = "procmail",
- [".procmail"] = "procmail",
- ["/etc/protocols"] = "protocols",
- [".pythonstartup"] = "python",
- [".pythonrc"] = "python",
- SConstruct = "python",
- ratpoisonrc = "ratpoison",
- [".ratpoisonrc"] = "ratpoison",
- v = "rcs",
- inputrc = "readline",
- [".inputrc"] = "readline",
- [".reminders"] = "remind",
- ["resolv.conf"] = "resolv",
- ["robots.txt"] = "robots",
- Gemfile = "ruby",
- Puppetfile = "ruby",
- [".irbrc"] = "ruby",
- irbrc = "ruby",
- ["smb.conf"] = "samba",
- screenrc = "screen",
- [".screenrc"] = "screen",
- ["/etc/sensors3.conf"] = "sensors",
- ["/etc/sensors.conf"] = "sensors",
- ["/etc/services"] = "services",
- ["/etc/serial.conf"] = "setserial",
- ["/etc/udev/cdsymlinks.conf"] = "sh",
- ["/etc/slp.conf"] = "slpconf",
- ["/etc/slp.reg"] = "slpreg",
- ["/etc/slp.spi"] = "slpspi",
- [".slrnrc"] = "slrnrc",
- ["sendmail.cf"] = "sm",
- ["squid.conf"] = "squid",
- ["/.ssh/config"] = "sshconfig",
- ["ssh_config"] = "sshconfig",
- ["sshd_config"] = "sshdconfig",
- ["/etc/sudoers"] = "sudoers",
- ["sudoers.tmp"] = "sudoers",
- ["/etc/sysctl.conf"] = "sysctl",
- tags = "tags",
- [".tclshrc"] = "tcl",
- [".wishrc"] = "tcl",
- ["tclsh.rc"] = "tcl",
- ["texmf.cnf"] = "texmf",
- COPYING = "text",
- README = "text",
- LICENSE = "text",
- AUTHORS = "text",
- tfrc = "tf",
- [".tfrc"] = "tf",
- ["tidy.conf"] = "tidy",
- tidyrc = "tidy",
- [".tidyrc"] = "tidy",
- ["/.cargo/config"] = "toml",
- Pipfile = "toml",
- ["Gopkg.lock"] = "toml",
- ["/.cargo/credentials"] = "toml",
- ["Cargo.lock"] = "toml",
- ["trustees.conf"] = "trustees",
- ["/etc/udev/udev.conf"] = "udevconf",
- ["/etc/updatedb.conf"] = "updatedb",
- ["fdrupstream.log"] = "upstreamlog",
- vgrindefs = "vgrindefs",
- [".exrc"] = "vim",
- ["_exrc"] = "vim",
- ["_viminfo"] = "viminfo",
- [".viminfo"] = "viminfo",
- [".wgetrc"] = "wget",
- wgetrc = "wget",
- [".wvdialrc"] = "wvdial",
- ["wvdial.conf"] = "wvdial",
- [".Xresources"] = "xdefaults",
- [".Xpdefaults"] = "xdefaults",
- ["xdm-config"] = "xdefaults",
- [".Xdefaults"] = "xdefaults",
- ["/etc/xinetd.conf"] = "xinetd",
- fglrxrc = "xml",
- ["/etc/blkid.tab"] = "xml",
- ["/etc/blkid.tab.old"] = "xml",
- ["/etc/zprofile"] = "zsh",
- [".zlogin"] = "zsh",
- [".zlogout"] = "zsh",
- [".zshrc"] = "zsh",
- [".zprofile"] = "zsh",
- [".zcompdump"] = "zsh",
- [".zshenv"] = "zsh",
- [".zfbfmarks"] = "zsh",
- [".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,
- [".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,
- [".tcshrc"] = function() vim.fn["dist#ft#SetFileTypeShell"]("tcsh") end,
- ["/etc/profile"] = function() vim.fn["dist#ft#SetFileTypeSH"](vim.fn.getline(1)) end,
- APKBUILD = function() vim.fn["dist#ft#SetFileTypeSH"]("bash") end,
- PKGBUILD = function() vim.fn["dist#ft#SetFileTypeSH"]("bash") end,
- ["bash.bashrc"] = function() vim.fn["dist#ft#SetFileTypeSH"]("bash") end,
- bashrc = function() vim.fn["dist#ft#SetFileTypeSH"]("bash") end,
- crontab = starsetf('crontab'),
- ["csh.cshrc"] = function() vim.fn["dist#ft#CSH"]() end,
- ["csh.login"] = function() vim.fn["dist#ft#CSH"]() end,
- ["csh.logout"] = function() vim.fn["dist#ft#CSH"]() end,
- ["indent.pro"] = function() vim.fn["dist#ft#ProtoCheck"]('indent') end,
- ["tcsh.login"] = function() vim.fn["dist#ft#SetFileTypeShell"]("tcsh") end,
- ["tcsh.tcshrc"] = function() vim.fn["dist#ft#SetFileTypeShell"]("tcsh") end,
+ ['a2psrc'] = 'a2ps',
+ ['/etc/a2ps.cfg'] = 'a2ps',
+ ['.a2psrc'] = 'a2ps',
+ ['.asoundrc'] = 'alsaconf',
+ ['/usr/share/alsa/alsa.conf'] = 'alsaconf',
+ ['/etc/asound.conf'] = 'alsaconf',
+ ['build.xml'] = 'ant',
+ ['.htaccess'] = 'apache',
+ ['apt.conf'] = 'aptconf',
+ ['/.aptitude/config'] = 'aptconf',
+ ['=tagging-method'] = 'arch',
+ ['.arch-inventory'] = 'arch',
+ ['GNUmakefile.am'] = 'automake',
+ ['named.root'] = 'bindzone',
+ WORKSPACE = 'bzl',
+ BUILD = 'bzl',
+ ['cabal.project'] = 'cabalproject',
+ [vim.env.HOME .. '/cabal.config'] = 'cabalconfig',
+ ['cabal.config'] = 'cabalconfig',
+ calendar = 'calendar',
+ catalog = 'catalog',
+ ['/etc/cdrdao.conf'] = 'cdrdaoconf',
+ ['.cdrdao'] = 'cdrdaoconf',
+ ['/etc/default/cdrdao'] = 'cdrdaoconf',
+ ['/etc/defaults/cdrdao'] = 'cdrdaoconf',
+ ['cfengine.conf'] = 'cfengine',
+ ['CMakeLists.txt'] = 'cmake',
+ ['.alias'] = function(path, bufnr)
+ return require('vim.filetype.detect').csh(path, bufnr)
+ end,
+ ['.cshrc'] = function(path, bufnr)
+ return require('vim.filetype.detect').csh(path, bufnr)
+ end,
+ ['.login'] = function(path, bufnr)
+ return require('vim.filetype.detect').csh(path, bufnr)
+ end,
+ ['csh.cshrc'] = function(path, bufnr)
+ return require('vim.filetype.detect').csh(path, bufnr)
+ end,
+ ['csh.login'] = function(path, bufnr)
+ return require('vim.filetype.detect').csh(path, bufnr)
+ end,
+ ['csh.logout'] = function(path, bufnr)
+ return require('vim.filetype.detect').csh(path, bufnr)
+ end,
+ ['auto.master'] = 'conf',
+ ['configure.in'] = 'config',
+ ['configure.ac'] = 'config',
+ crontab = 'crontab',
+ ['.cvsrc'] = 'cvsrc',
+ ['/debian/changelog'] = 'debchangelog',
+ ['changelog.dch'] = 'debchangelog',
+ ['changelog.Debian'] = 'debchangelog',
+ ['NEWS.dch'] = 'debchangelog',
+ ['NEWS.Debian'] = 'debchangelog',
+ ['/debian/control'] = 'debcontrol',
+ ['/debian/copyright'] = 'debcopyright',
+ ['/etc/apt/sources.list'] = 'debsources',
+ ['denyhosts.conf'] = 'denyhosts',
+ ['dict.conf'] = 'dictconf',
+ ['.dictrc'] = 'dictconf',
+ ['/etc/DIR_COLORS'] = 'dircolors',
+ ['.dir_colors'] = 'dircolors',
+ ['.dircolors'] = 'dircolors',
+ ['/etc/dnsmasq.conf'] = 'dnsmasq',
+ Containerfile = 'dockerfile',
+ dockerfile = 'dockerfile',
+ Dockerfile = 'dockerfile',
+ npmrc = 'dosini',
+ ['/etc/yum.conf'] = 'dosini',
+ ['.npmrc'] = 'dosini',
+ ['.editorconfig'] = 'dosini',
+ ['/etc/pacman.conf'] = 'confini',
+ ['mpv.conf'] = 'confini',
+ dune = 'dune',
+ jbuild = 'dune',
+ ['dune-workspace'] = 'dune',
+ ['dune-project'] = 'dune',
+ ['elinks.conf'] = 'elinks',
+ ['mix.lock'] = 'elixir',
+ ['filter-rules'] = 'elmfilt',
+ ['exim.conf'] = 'exim',
+ exports = 'exports',
+ ['.fetchmailrc'] = 'fetchmail',
+ fvSchemes = function(path, bufnr)
+ return require('vim.filetype.detect').foam(bufnr)
+ end,
+ fvSolution = function(path, bufnr)
+ return require('vim.filetype.detect').foam(bufnr)
+ end,
+ fvConstraints = function(path, bufnr)
+ return require('vim.filetype.detect').foam(bufnr)
+ end,
+ fvModels = function(path, bufnr)
+ return require('vim.filetype.detect').foam(bufnr)
+ end,
+ fstab = 'fstab',
+ mtab = 'fstab',
+ ['.gdbinit'] = 'gdb',
+ gdbinit = 'gdb',
+ ['.gdbearlyinit'] = 'gdb',
+ gdbearlyinit = 'gdb',
+ ['lltxxxxx.txt'] = 'gedcom',
+ ['TAG_EDITMSG'] = 'gitcommit',
+ ['MERGE_MSG'] = 'gitcommit',
+ ['COMMIT_EDITMSG'] = 'gitcommit',
+ ['NOTES_EDITMSG'] = 'gitcommit',
+ ['EDIT_DESCRIPTION'] = 'gitcommit',
+ ['.gitconfig'] = 'gitconfig',
+ ['.gitmodules'] = 'gitconfig',
+ ['gitolite.conf'] = 'gitolite',
+ ['git-rebase-todo'] = 'gitrebase',
+ gkrellmrc = 'gkrellmrc',
+ ['.gnashrc'] = 'gnash',
+ ['.gnashpluginrc'] = 'gnash',
+ gnashpluginrc = 'gnash',
+ gnashrc = 'gnash',
+ ['.gnuplot'] = 'gnuplot',
+ ['go.work'] = 'gowork',
+ ['.gprc'] = 'gp',
+ ['/.gnupg/gpg.conf'] = 'gpg',
+ ['/.gnupg/options'] = 'gpg',
+ ['/var/backups/gshadow.bak'] = 'group',
+ ['/etc/gshadow'] = 'group',
+ ['/etc/group-'] = 'group',
+ ['/etc/gshadow.edit'] = 'group',
+ ['/etc/gshadow-'] = 'group',
+ ['/etc/group'] = 'group',
+ ['/var/backups/group.bak'] = 'group',
+ ['/etc/group.edit'] = 'group',
+ ['/boot/grub/menu.lst'] = 'grub',
+ ['/etc/grub.conf'] = 'grub',
+ ['/boot/grub/grub.conf'] = 'grub',
+ ['.gtkrc'] = 'gtkrc',
+ gtkrc = 'gtkrc',
+ ['snort.conf'] = 'hog',
+ ['vision.conf'] = 'hog',
+ ['/etc/host.conf'] = 'hostconf',
+ ['/etc/hosts.allow'] = 'hostsaccess',
+ ['/etc/hosts.deny'] = 'hostsaccess',
+ ['/i3/config'] = 'i3config',
+ ['/sway/config'] = 'i3config',
+ ['/.sway/config'] = 'i3config',
+ ['/.i3/config'] = 'i3config',
+ ['/.icewm/menu'] = 'icemenu',
+ ['.indent.pro'] = 'indent',
+ indentrc = 'indent',
+ inittab = 'inittab',
+ ['ipf.conf'] = 'ipfilter',
+ ['ipf6.conf'] = 'ipfilter',
+ ['ipf.rules'] = 'ipfilter',
+ ['.eslintrc'] = 'json',
+ ['.babelrc'] = 'json',
+ ['Pipfile.lock'] = 'json',
+ ['.firebaserc'] = 'json',
+ ['.prettierrc'] = 'json',
+ Kconfig = 'kconfig',
+ ['Kconfig.debug'] = 'kconfig',
+ ['lftp.conf'] = 'lftp',
+ ['.lftprc'] = 'lftp',
+ ['/.libao'] = 'libao',
+ ['/etc/libao.conf'] = 'libao',
+ ['lilo.conf'] = 'lilo',
+ ['/etc/limits'] = 'limits',
+ ['.emacs'] = 'lisp',
+ sbclrc = 'lisp',
+ ['.sbclrc'] = 'lisp',
+ ['.sawfishrc'] = 'lisp',
+ ['/etc/login.access'] = 'loginaccess',
+ ['/etc/login.defs'] = 'logindefs',
+ ['lynx.cfg'] = 'lynx',
+ ['m3overrides'] = 'm3build',
+ ['m3makefile'] = 'm3build',
+ ['cm3.cfg'] = 'm3quake',
+ ['.followup'] = 'mail',
+ ['.article'] = 'mail',
+ ['.letter'] = 'mail',
+ ['/etc/aliases'] = 'mailaliases',
+ ['/etc/mail/aliases'] = 'mailaliases',
+ mailcap = 'mailcap',
+ ['.mailcap'] = 'mailcap',
+ ['/etc/man.conf'] = 'manconf',
+ ['man.config'] = 'manconf',
+ ['maxima-init.mac'] = 'maxima',
+ ['meson.build'] = 'meson',
+ ['meson_options.txt'] = 'meson',
+ ['/etc/conf.modules'] = 'modconf',
+ ['/etc/modules'] = 'modconf',
+ ['/etc/modules.conf'] = 'modconf',
+ ['/.mplayer/config'] = 'mplayerconf',
+ ['mplayer.conf'] = 'mplayerconf',
+ mrxvtrc = 'mrxvtrc',
+ ['.mrxvtrc'] = 'mrxvtrc',
+ ['/etc/nanorc'] = 'nanorc',
+ Neomuttrc = 'neomuttrc',
+ ['.netrc'] = 'netrc',
+ NEWS = function(path, bufnr)
+ return require('vim.filetype.detect').news(bufnr)
+ end,
+ ['.ocamlinit'] = 'ocaml',
+ ['.octaverc'] = 'octave',
+ octaverc = 'octave',
+ ['octave.conf'] = 'octave',
+ opam = 'opam',
+ ['/etc/pam.conf'] = 'pamconf',
+ ['pam_env.conf'] = 'pamenv',
+ ['.pam_environment'] = 'pamenv',
+ ['/var/backups/passwd.bak'] = 'passwd',
+ ['/var/backups/shadow.bak'] = 'passwd',
+ ['/etc/passwd'] = 'passwd',
+ ['/etc/passwd-'] = 'passwd',
+ ['/etc/shadow.edit'] = 'passwd',
+ ['/etc/shadow-'] = 'passwd',
+ ['/etc/shadow'] = 'passwd',
+ ['/etc/passwd.edit'] = 'passwd',
+ ['pf.conf'] = 'pf',
+ ['main.cf'] = 'pfmain',
+ pinerc = 'pine',
+ ['.pinercex'] = 'pine',
+ ['.pinerc'] = 'pine',
+ pinercex = 'pine',
+ ['/etc/pinforc'] = 'pinfo',
+ ['/.pinforc'] = 'pinfo',
+ ['.povrayrc'] = 'povini',
+ ['printcap'] = function(path, bufnr)
+ return 'ptcap', function(b)
+ vim.b[b].ptcap_type = 'print'
+ end
+ end,
+ ['termcap'] = function(path, bufnr)
+ return 'ptcap', function(b)
+ vim.b[b].ptcap_type = 'term'
+ end
+ end,
+ ['.procmailrc'] = 'procmail',
+ ['.procmail'] = 'procmail',
+ ['indent.pro'] = function(path, bufnr)
+ return require('vim.filetype.detect').proto(bufnr, 'indent')
+ end,
+ ['/etc/protocols'] = 'protocols',
+ ['INDEX'] = function(path, bufnr)
+ return require('vim.filetype.detect').psf(bufnr)
+ end,
+ ['INFO'] = function(path, bufnr)
+ return require('vim.filetype.detect').psf(bufnr)
+ end,
+ ['.pythonstartup'] = 'python',
+ ['.pythonrc'] = 'python',
+ SConstruct = 'python',
+ ratpoisonrc = 'ratpoison',
+ ['.ratpoisonrc'] = 'ratpoison',
+ inputrc = 'readline',
+ ['.inputrc'] = 'readline',
+ ['.reminders'] = 'remind',
+ ['resolv.conf'] = 'resolv',
+ ['robots.txt'] = 'robots',
+ Gemfile = 'ruby',
+ Puppetfile = 'ruby',
+ ['.irbrc'] = 'ruby',
+ irbrc = 'ruby',
+ Vagrantfile = 'ruby',
+ ['smb.conf'] = 'samba',
+ screenrc = 'screen',
+ ['.screenrc'] = 'screen',
+ ['/etc/sensors3.conf'] = 'sensors',
+ ['/etc/sensors.conf'] = 'sensors',
+ ['/etc/services'] = 'services',
+ ['/etc/serial.conf'] = 'setserial',
+ ['/etc/udev/cdsymlinks.conf'] = 'sh',
+ ['bash.bashrc'] = function(path, bufnr)
+ return require('vim.filetype.detect').sh(path, M.getlines(bufnr), 'bash')
+ end,
+ bashrc = function(path, bufnr)
+ return require('vim.filetype.detect').sh(path, M.getlines(bufnr), 'bash')
+ end,
+ ['.bashrc'] = function(path, bufnr)
+ return require('vim.filetype.detect').sh(path, M.getlines(bufnr), 'bash')
+ end,
+ ['.env'] = function(path, bufnr)
+ return require('vim.filetype.detect').sh(path, M.getlines(bufnr))
+ end,
+ ['.kshrc'] = function(path, bufnr)
+ return require('vim.filetype.detect').sh(path, M.getlines(bufnr), 'ksh')
+ end,
+ ['.profile'] = function(path, bufnr)
+ return require('vim.filetype.detect').sh(path, M.getlines(bufnr))
+ end,
+ ['/etc/profile'] = function(path, bufnr)
+ return require('vim.filetype.detect').sh(path, M.getlines(bufnr))
+ end,
+ APKBUILD = function(path, bufnr)
+ return require('vim.filetype.detect').sh(path, M.getlines(bufnr), 'bash')
+ end,
+ PKGBUILD = function(path, bufnr)
+ return require('vim.filetype.detect').sh(path, M.getlines(bufnr), 'bash')
+ end,
+ ['.tcshrc'] = function(path, bufnr)
+ return require('vim.filetype.detect').shell(path, M.getlines(bufnr), 'tcsh')
+ end,
+ ['tcsh.login'] = function(path, bufnr)
+ return require('vim.filetype.detect').shell(path, M.getlines(bufnr), 'tcsh')
+ end,
+ ['tcsh.tcshrc'] = function(path, bufnr)
+ return require('vim.filetype.detect').shell(path, M.getlines(bufnr), 'tcsh')
+ end,
+ ['/etc/slp.conf'] = 'slpconf',
+ ['/etc/slp.reg'] = 'slpreg',
+ ['/etc/slp.spi'] = 'slpspi',
+ ['.slrnrc'] = 'slrnrc',
+ ['sendmail.cf'] = 'sm',
+ ['squid.conf'] = 'squid',
+ ['ssh_config'] = 'sshconfig',
+ ['sshd_config'] = 'sshdconfig',
+ ['/etc/sudoers'] = 'sudoers',
+ ['sudoers.tmp'] = 'sudoers',
+ ['/etc/sysctl.conf'] = 'sysctl',
+ tags = 'tags',
+ ['pending.data'] = 'taskdata',
+ ['completed.data'] = 'taskdata',
+ ['undo.data'] = 'taskdata',
+ ['.tclshrc'] = 'tcl',
+ ['.wishrc'] = 'tcl',
+ ['tclsh.rc'] = 'tcl',
+ ['texmf.cnf'] = 'texmf',
+ COPYING = 'text',
+ README = 'text',
+ LICENSE = 'text',
+ AUTHORS = 'text',
+ tfrc = 'tf',
+ ['.tfrc'] = 'tf',
+ ['tidy.conf'] = 'tidy',
+ tidyrc = 'tidy',
+ ['.tidyrc'] = 'tidy',
+ ['.tmux.conf'] = 'tmux',
+ ['/.cargo/config'] = 'toml',
+ Pipfile = 'toml',
+ ['Gopkg.lock'] = 'toml',
+ ['/.cargo/credentials'] = 'toml',
+ ['Cargo.lock'] = 'toml',
+ ['trustees.conf'] = 'trustees',
+ ['/etc/udev/udev.conf'] = 'udevconf',
+ ['/etc/updatedb.conf'] = 'updatedb',
+ ['fdrupstream.log'] = 'upstreamlog',
+ vgrindefs = 'vgrindefs',
+ ['.exrc'] = 'vim',
+ ['_exrc'] = 'vim',
+ ['_viminfo'] = 'viminfo',
+ ['.viminfo'] = 'viminfo',
+ ['.wgetrc'] = 'wget',
+ ['.wget2rc'] = 'wget2',
+ wgetrc = 'wget',
+ wget2rc = 'wget2',
+ ['.wvdialrc'] = 'wvdial',
+ ['wvdial.conf'] = 'wvdial',
+ ['.Xresources'] = 'xdefaults',
+ ['.Xpdefaults'] = 'xdefaults',
+ ['xdm-config'] = 'xdefaults',
+ ['.Xdefaults'] = 'xdefaults',
+ ['xorg.conf'] = function(path, bufnr)
+ return 'xf86conf', function(b)
+ vim.b[b].xf86conf_xfree86_version = 4
+ end
+ end,
+ ['xorg.conf-4'] = function(path, bufnr)
+ return 'xf86conf', function(b)
+ vim.b[b].xf86conf_xfree86_version = 4
+ end
+ end,
+ ['XF86Config'] = function(path, bufnr)
+ return require('vim.filetype.detect').xfree86()
+ end,
+ ['/etc/xinetd.conf'] = 'xinetd',
+ fglrxrc = 'xml',
+ ['/etc/blkid.tab'] = 'xml',
+ ['/etc/blkid.tab.old'] = 'xml',
+ ['/etc/zprofile'] = 'zsh',
+ ['.zlogin'] = 'zsh',
+ ['.zlogout'] = 'zsh',
+ ['.zshrc'] = 'zsh',
+ ['.zprofile'] = 'zsh',
+ ['.zcompdump'] = 'zsh',
+ ['.zshenv'] = 'zsh',
+ ['.zfbfmarks'] = 'zsh',
-- END FILENAME
}
local pattern = {
-- BEGIN PATTERN
- [".*/etc/a2ps/.*%.cfg"] = "a2ps",
- [".*/etc/a2ps%.cfg"] = "a2ps",
- [".*/usr/share/alsa/alsa%.conf"] = "alsaconf",
- [".*/etc/asound%.conf"] = "alsaconf",
- [".*/etc/apache2/sites%-.*/.*%.com"] = "apache",
- [".*/etc/httpd/.*%.conf"] = "apache",
- [".*/%.aptitude/config"] = "aptconf",
- ["[mM]akefile%.am"] = "automake",
- [".*bsd"] = "bsdl",
- ["bzr_log%..*"] = "bzr",
- [".*enlightenment/.*%.cfg"] = "c",
- [".*/etc/defaults/cdrdao"] = "cdrdaoconf",
- [".*/etc/cdrdao%.conf"] = "cdrdaoconf",
- [".*/etc/default/cdrdao"] = "cdrdaoconf",
- [".*hgrc"] = "cfg",
- [".*%.%.ch"] = "chill",
- [".*%.cmake%.in"] = "cmake",
- [".*/debian/changelog"] = "debchangelog",
- [".*/debian/control"] = "debcontrol",
- [".*/debian/copyright"] = "debcopyright",
- [".*/etc/apt/sources%.list%.d/.*%.list"] = "debsources",
- [".*/etc/apt/sources%.list"] = "debsources",
- ["dictd.*%.conf"] = "dictdconf",
- [".*/etc/DIR_COLORS"] = "dircolors",
- [".*/etc/dnsmasq%.conf"] = "dnsmasq",
- ["php%.ini%-.*"] = "dosini",
- [".*/etc/pacman%.conf"] = "dosini",
- [".*/etc/yum%.conf"] = "dosini",
- [".*lvs"] = "dracula",
- [".*lpe"] = "dracula",
- [".*esmtprc"] = "esmtprc",
- [".*Eterm/.*%.cfg"] = "eterm",
- [".*%.git/modules/.*/config"] = "gitconfig",
- [".*%.git/config"] = "gitconfig",
- [".*/%.config/git/config"] = "gitconfig",
- ["%.gitsendemail%.msg%......."] = "gitsendemail",
- ["gkrellmrc_."] = "gkrellmrc",
- [".*/usr/.*/gnupg/options%.skel"] = "gpg",
- [".*/%.gnupg/options"] = "gpg",
- [".*/%.gnupg/gpg%.conf"] = "gpg",
- [".*/etc/group"] = "group",
- [".*/etc/gshadow"] = "group",
- [".*/etc/group%.edit"] = "group",
- [".*/var/backups/gshadow%.bak"] = "group",
- [".*/etc/group-"] = "group",
- [".*/etc/gshadow-"] = "group",
- [".*/var/backups/group%.bak"] = "group",
- [".*/etc/gshadow%.edit"] = "group",
- [".*/boot/grub/grub%.conf"] = "grub",
- [".*/boot/grub/menu%.lst"] = "grub",
- [".*/etc/grub%.conf"] = "grub",
- ["hg%-editor%-.*%.txt"] = "hgcommit",
- [".*/etc/host%.conf"] = "hostconf",
- [".*/etc/hosts%.deny"] = "hostsaccess",
- [".*/etc/hosts%.allow"] = "hostsaccess",
- [".*%.html%.m4"] = "htmlm4",
- [".*/%.i3/config"] = "i3config",
- [".*/sway/config"] = "i3config",
- [".*/i3/config"] = "i3config",
- [".*/%.sway/config"] = "i3config",
- [".*/%.icewm/menu"] = "icemenu",
- [".*/etc/initng/.*/.*%.i"] = "initng",
- [".*%.properties_.."] = "jproperties",
- [".*%.properties_.._.."] = "jproperties",
- [".*lftp/rc"] = "lftp",
- [".*/%.libao"] = "libao",
- [".*/etc/libao%.conf"] = "libao",
- [".*/etc/.*limits%.conf"] = "limits",
- [".*/etc/limits"] = "limits",
- [".*/etc/.*limits%.d/.*%.conf"] = "limits",
- [".*/LiteStep/.*/.*%.rc"] = "litestep",
- [".*/etc/login%.access"] = "loginaccess",
- [".*/etc/login%.defs"] = "logindefs",
- [".*/etc/mail/aliases"] = "mailaliases",
- [".*/etc/aliases"] = "mailaliases",
- [".*[mM]akefile"] = "make",
- [".*/etc/man%.conf"] = "manconf",
- [".*/etc/modules%.conf"] = "modconf",
- [".*/etc/conf%.modules"] = "modconf",
- [".*/etc/modules"] = "modconf",
- [".*%.[mi][3g]"] = "modula3",
- [".*/%.mplayer/config"] = "mplayerconf",
- ["rndc.*%.conf"] = "named",
- ["rndc.*%.key"] = "named",
- ["named.*%.conf"] = "named",
- [".*/etc/nanorc"] = "nanorc",
- [".*%.NS[ACGLMNPS]"] = "natural",
- ["nginx.*%.conf"] = "nginx",
- [".*/etc/nginx/.*"] = "nginx",
- [".*nginx%.conf"] = "nginx",
- [".*/nginx/.*%.conf"] = "nginx",
- [".*/usr/local/nginx/conf/.*"] = "nginx",
- [".*%.ml%.cppo"] = "ocaml",
- [".*%.mli%.cppo"] = "ocaml",
- [".*%.opam%.template"] = "opam",
- [".*%.[Oo][Pp][Ll]"] = "opl",
- [".*/etc/pam%.conf"] = "pamconf",
- [".*/etc/passwd-"] = "passwd",
- [".*/etc/shadow"] = "passwd",
- [".*/etc/shadow%.edit"] = "passwd",
- [".*/var/backups/shadow%.bak"] = "passwd",
- [".*/var/backups/passwd%.bak"] = "passwd",
- [".*/etc/passwd"] = "passwd",
- [".*/etc/passwd%.edit"] = "passwd",
- [".*/etc/shadow-"] = "passwd",
- [".*/%.pinforc"] = "pinfo",
- [".*/etc/pinforc"] = "pinfo",
- [".*/etc/protocols"] = "protocols",
- [".*baseq[2-3]/.*%.cfg"] = "quake",
- [".*quake[1-3]/.*%.cfg"] = "quake",
- [".*id1/.*%.cfg"] = "quake",
- ["[rR]antfile"] = "ruby",
- ["[rR]akefile"] = "ruby",
- [".*/etc/sensors%.conf"] = "sensors",
- [".*/etc/sensors3%.conf"] = "sensors",
- [".*/etc/services"] = "services",
- [".*/etc/serial%.conf"] = "setserial",
- [".*/etc/udev/cdsymlinks%.conf"] = "sh",
- [".*%._sst%.meta"] = "sisu",
- [".*%.%-sst%.meta"] = "sisu",
- [".*%.sst%.meta"] = "sisu",
- [".*/etc/slp%.conf"] = "slpconf",
- [".*/etc/slp%.reg"] = "slpreg",
- [".*/etc/slp%.spi"] = "slpspi",
- [".*/etc/ssh/ssh_config%.d/.*%.conf"] = "sshconfig",
- [".*/%.ssh/config"] = "sshconfig",
- [".*/etc/ssh/sshd_config%.d/.*%.conf"] = "sshdconfig",
- [".*/etc/sudoers"] = "sudoers",
- ["svn%-commit.*%.tmp"] = "svn",
- [".*%.swift%.gyb"] = "swiftgyb",
- [".*/etc/sysctl%.conf"] = "sysctl",
- [".*/etc/sysctl%.d/.*%.conf"] = "sysctl",
- [".*/etc/systemd/.*%.conf%.d/.*%.conf"] = "systemd",
- [".*/%.config/systemd/user/.*%.d/.*%.conf"] = "systemd",
- [".*/etc/systemd/system/.*%.d/.*%.conf"] = "systemd",
- [".*%.t%.html"] = "tilde",
- [".*/%.cargo/config"] = "toml",
- [".*/%.cargo/credentials"] = "toml",
- [".*/etc/udev/udev%.conf"] = "udevconf",
- [".*/etc/udev/permissions%.d/.*%.permissions"] = "udevperm",
- [".*/etc/updatedb%.conf"] = "updatedb",
- [".*/%.init/.*%.override"] = "upstart",
- [".*/usr/share/upstart/.*%.conf"] = "upstart",
- [".*/%.config/upstart/.*%.override"] = "upstart",
- [".*/etc/init/.*%.conf"] = "upstart",
- [".*/etc/init/.*%.override"] = "upstart",
- [".*/%.config/upstart/.*%.conf"] = "upstart",
- [".*/%.init/.*%.conf"] = "upstart",
- [".*/usr/share/upstart/.*%.override"] = "upstart",
- [".*%.ws[fc]"] = "wsh",
- [".*/etc/xinetd%.conf"] = "xinetd",
- [".*/etc/blkid%.tab"] = "xml",
- [".*/etc/blkid%.tab%.old"] = "xml",
- [".*%.vbproj%.user"] = "xml",
- [".*%.fsproj%.user"] = "xml",
- [".*%.csproj%.user"] = "xml",
- [".*/etc/xdg/menus/.*%.menu"] = "xml",
- [".*Xmodmap"] = "xmodmap",
- [".*/etc/zprofile"] = "zsh",
- ["%.bash[_-]aliases"] = function() vim.fn["dist#ft#SetFileTypeSH"]("bash") end,
- ["%.bash[_-]logout"] = function() vim.fn["dist#ft#SetFileTypeSH"]("bash") end,
- ["%.bash[_-]profile"] = function() vim.fn["dist#ft#SetFileTypeSH"]("bash") end,
- ["%.cshrc.*"] = function() vim.fn["dist#ft#CSH"]() end,
- ["%.gtkrc.*"] = starsetf('gtkrc'),
- ["%.kshrc.*"] = function() vim.fn["dist#ft#SetFileTypeSH"]("ksh") end,
- ["%.login.*"] = function() vim.fn["dist#ft#CSH"]() end,
- ["%.neomuttrc.*"] = starsetf('neomuttrc'),
- ["%.profile.*"] = function() vim.fn["dist#ft#SetFileTypeSH"](vim.fn.getline(1)) end,
- ["%.reminders.*"] = starsetf('remind'),
- ["%.tcshrc.*"] = function() vim.fn["dist#ft#SetFileTypeShell"]("tcsh") end,
- ["%.zcompdump.*"] = starsetf('zsh'),
- ["%.zlog.*"] = starsetf('zsh'),
- ["%.zsh.*"] = starsetf('zsh'),
- [".*%.[1-9]"] = function() vim.fn["dist#ft#FTnroff"]() end,
- [".*%.[aA]"] = function() vim.fn["dist#ft#FTasm"]() end,
- [".*%.[sS]"] = function() vim.fn["dist#ft#FTasm"]() end,
- [".*%.properties_.._.._.*"] = starsetf('jproperties'),
- [".*%.vhdl_[0-9].*"] = starsetf('vhdl'),
- [".*/%.fvwm/.*"] = starsetf('fvwm'),
- [".*/%.gitconfig%.d/.*"] = starsetf('gitconfig'),
- [".*/%.neomutt/neomuttrc.*"] = starsetf('neomuttrc'),
- [".*/Xresources/.*"] = starsetf('xdefaults'),
- [".*/app%-defaults/.*"] = starsetf('xdefaults'),
- [".*/bind/db%..*"] = starsetf('bindzone'),
- [".*/debian/patches/.*"] = function() vim.fn["dist#ft#Dep3patch"]() end,
- [".*/etc/Muttrc%.d/.*"] = starsetf('muttrc'),
- [".*/etc/apache2/.*%.conf.*"] = starsetf('apache'),
- [".*/etc/apache2/conf%..*/.*"] = starsetf('apache'),
- [".*/etc/apache2/mods%-.*/.*"] = starsetf('apache'),
- [".*/etc/apache2/sites%-.*/.*"] = starsetf('apache'),
- [".*/etc/cron%.d/.*"] = starsetf('crontab'),
- [".*/etc/dnsmasq%.d/.*"] = starsetf('dnsmasq'),
- [".*/etc/httpd/conf%..*/.*"] = starsetf('apache'),
- [".*/etc/httpd/conf%.d/.*%.conf.*"] = starsetf('apache'),
- [".*/etc/httpd/mods%-.*/.*"] = starsetf('apache'),
- [".*/etc/httpd/sites%-.*/.*"] = starsetf('apache'),
- [".*/etc/logcheck/.*%.d.*/.*"] = starsetf('logcheck'),
- [".*/etc/modprobe%..*"] = starsetf('modconf'),
- [".*/etc/pam%.d/.*"] = starsetf('pamconf'),
- [".*/etc/profile"] = function() vim.fn["dist#ft#SetFileTypeSH"](vim.fn.getline(1)) end,
- [".*/etc/proftpd/.*%.conf.*"] = starsetf('apachestyle'),
- [".*/etc/proftpd/conf%..*/.*"] = starsetf('apachestyle'),
- [".*/etc/sudoers%.d/.*"] = starsetf('sudoers'),
- [".*/etc/xinetd%.d/.*"] = starsetf('xinetd'),
- [".*/etc/yum%.repos%.d/.*"] = starsetf('dosini'),
- [".*/gitolite%-admin/conf/.*"] = starsetf('gitolite'),
- [".*/named/db%..*"] = starsetf('bindzone'),
- [".*/tmp/lltmp.*"] = starsetf('gedcom'),
- [".*asterisk.*/.*voicemail%.conf.*"] = starsetf('asteriskvm'),
- [".*asterisk/.*%.conf.*"] = starsetf('asterisk'),
- [".*vimrc.*"] = starsetf('vim'),
- [".*xmodmap.*"] = starsetf('xmodmap'),
- ["/etc/gitconfig%.d/.*"] = starsetf('gitconfig'),
- ["/etc/hostname%..*"] = starsetf('config'),
- ["Containerfile%..*"] = starsetf('dockerfile'),
- ["Dockerfile%..*"] = starsetf('dockerfile'),
- ["JAM.*%..*"] = starsetf('jam'),
- ["Kconfig%..*"] = starsetf('kconfig'),
- ["Neomuttrc.*"] = starsetf('neomuttrc'),
- ["Prl.*%..*"] = starsetf('jam'),
- ["Xresources.*"] = starsetf('xdefaults'),
- ["[mM]akefile.*"] = starsetf('make'),
- ["[rR]akefile.*"] = starsetf('ruby'),
- ["access%.conf.*"] = starsetf('apache'),
- ["apache%.conf.*"] = starsetf('apache'),
- ["apache2%.conf.*"] = starsetf('apache'),
- ["bash%-fc[-%.]"] = function() vim.fn["dist#ft#SetFileTypeSH"]("bash") end,
- ["cabal%.project%..*"] = starsetf('cabalproject'),
- ["crontab%..*"] = starsetf('crontab'),
- ["drac%..*"] = starsetf('dracula'),
- ["gtkrc.*"] = starsetf('gtkrc'),
- ["httpd%.conf.*"] = starsetf('apache'),
- ["lilo%.conf.*"] = starsetf('lilo'),
- ["neomuttrc.*"] = starsetf('neomuttrc'),
- ["proftpd%.conf.*"] = starsetf('apachestyle'),
- ["reportbug%-.*"] = starsetf('mail'),
- ["sgml%.catalog.*"] = starsetf('catalog'),
- ["srm%.conf.*"] = starsetf('apache'),
- ["tmac%..*"] = starsetf('nroff'),
- ["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/a2ps/.*%.cfg'] = 'a2ps',
+ ['.*/etc/a2ps%.cfg'] = 'a2ps',
+ ['.*/usr/share/alsa/alsa%.conf'] = 'alsaconf',
+ ['.*/etc/asound%.conf'] = 'alsaconf',
+ ['.*/etc/apache2/sites%-.*/.*%.com'] = 'apache',
+ ['.*/etc/httpd/.*%.conf'] = 'apache',
+ ['.*/etc/apache2/.*%.conf.*'] = starsetf('apache'),
+ ['.*/etc/apache2/conf%..*/.*'] = starsetf('apache'),
+ ['.*/etc/apache2/mods%-.*/.*'] = starsetf('apache'),
+ ['.*/etc/apache2/sites%-.*/.*'] = starsetf('apache'),
+ ['access%.conf.*'] = starsetf('apache'),
+ ['apache%.conf.*'] = starsetf('apache'),
+ ['apache2%.conf.*'] = starsetf('apache'),
+ ['httpd%.conf.*'] = starsetf('apache'),
+ ['srm%.conf.*'] = starsetf('apache'),
+ ['.*/etc/httpd/conf%..*/.*'] = starsetf('apache'),
+ ['.*/etc/httpd/conf%.d/.*%.conf.*'] = starsetf('apache'),
+ ['.*/etc/httpd/mods%-.*/.*'] = starsetf('apache'),
+ ['.*/etc/httpd/sites%-.*/.*'] = starsetf('apache'),
+ ['.*/etc/proftpd/.*%.conf.*'] = starsetf('apachestyle'),
+ ['.*/etc/proftpd/conf%..*/.*'] = starsetf('apachestyle'),
+ ['proftpd%.conf.*'] = starsetf('apachestyle'),
+ ['.*asterisk/.*%.conf.*'] = starsetf('asterisk'),
+ ['.*asterisk.*/.*voicemail%.conf.*'] = starsetf('asteriskvm'),
+ ['.*/%.aptitude/config'] = 'aptconf',
+ ['.*%.[aA]'] = function(path, bufnr)
+ return require('vim.filetype.detect').asm(bufnr)
+ end,
+ ['.*%.[sS]'] = function(path, bufnr)
+ return require('vim.filetype.detect').asm(bufnr)
+ end,
+ ['[mM]akefile%.am'] = 'automake',
+ ['.*/bind/db%..*'] = starsetf('bindzone'),
+ ['.*/named/db%..*'] = starsetf('bindzone'),
+ ['.*/build/conf/.*%.conf'] = 'bitbake',
+ ['.*/meta/conf/.*%.conf'] = 'bitbake',
+ ['.*/meta%-.*/conf/.*%.conf'] = 'bitbake',
+ ['.*bsd'] = 'bsdl',
+ ['bzr_log%..*'] = 'bzr',
+ ['.*enlightenment/.*%.cfg'] = 'c',
+ ['cabal%.project%..*'] = starsetf('cabalproject'),
+ ['.*/%.calendar/.*'] = starsetf('calendar'),
+ ['.*/share/calendar/.*/calendar%..*'] = starsetf('calendar'),
+ ['.*/share/calendar/calendar%..*'] = starsetf('calendar'),
+ ['sgml%.catalog.*'] = starsetf('catalog'),
+ ['.*/etc/defaults/cdrdao'] = 'cdrdaoconf',
+ ['.*/etc/cdrdao%.conf'] = 'cdrdaoconf',
+ ['.*/etc/default/cdrdao'] = 'cdrdaoconf',
+ ['.*hgrc'] = 'cfg',
+ ['.*%.[Cc][Ff][Gg]'] = {
+ function(path, bufnr)
+ return require('vim.filetype.detect').cfg(bufnr)
+ end,
+ -- Decrease priority to avoid conflicts with more specific patterns
+ -- such as '.*/etc/a2ps/.*%.cfg', '.*enlightenment/.*%.cfg', etc.
+ { priority = -1 },
+ },
+ ['[cC]hange[lL]og.*'] = starsetf(function(path, bufnr)
+ require('vim.filetype.detect').changelog(bufnr)
+ end),
+ ['.*%.%.ch'] = 'chill',
+ ['.*%.cmake%.in'] = 'cmake',
+ -- */cmus/rc and */.cmus/rc
+ ['.*/%.?cmus/rc'] = 'cmusrc',
+ -- */cmus/*.theme and */.cmus/*.theme
+ ['.*/%.?cmus/.*%.theme'] = 'cmusrc',
+ ['.*/%.cmus/autosave'] = 'cmusrc',
+ ['.*/%.cmus/command%-history'] = 'cmusrc',
+ ['.*/etc/hostname%..*'] = starsetf('config'),
+ ['crontab%..*'] = starsetf('crontab'),
+ ['.*/etc/cron%.d/.*'] = starsetf('crontab'),
+ ['%.cshrc.*'] = function(path, bufnr)
+ return require('vim.filetype.detect').csh(path, bufnr)
+ end,
+ ['%.login.*'] = function(path, bufnr)
+ return require('vim.filetype.detect').csh(path, bufnr)
+ end,
+ ['cvs%d+'] = 'cvs',
+ ['.*%.[Dd][Aa][Tt]'] = function(path, bufnr)
+ return require('vim.filetype.detect').dat(path, bufnr)
+ end,
+ ['.*/debian/patches/.*'] = function(path, bufnr)
+ return require('vim.filetype.detect').dep3patch(path, bufnr)
+ end,
+ ['.*/etc/dnsmasq%.d/.*'] = starsetf('dnsmasq'),
+ ['Containerfile%..*'] = starsetf('dockerfile'),
+ ['Dockerfile%..*'] = starsetf('dockerfile'),
+ ['.*/etc/yum%.repos%.d/.*'] = starsetf('dosini'),
+ ['drac%..*'] = starsetf('dracula'),
+ ['.*/debian/changelog'] = 'debchangelog',
+ ['.*/debian/control'] = 'debcontrol',
+ ['.*/debian/copyright'] = 'debcopyright',
+ ['.*/etc/apt/sources%.list%.d/.*%.list'] = 'debsources',
+ ['.*/etc/apt/sources%.list'] = 'debsources',
+ ['.*%.directory'] = 'desktop',
+ ['.*%.desktop'] = 'desktop',
+ ['dictd.*%.conf'] = 'dictdconf',
+ ['.*/etc/DIR_COLORS'] = 'dircolors',
+ ['.*/etc/dnsmasq%.conf'] = 'dnsmasq',
+ ['php%.ini%-.*'] = 'dosini',
+ ['.*/etc/pacman%.conf'] = 'confini',
+ ['.*/etc/yum%.conf'] = 'dosini',
+ ['.*lvs'] = 'dracula',
+ ['.*lpe'] = 'dracula',
+ ['.*/dtrace/.*%.d'] = 'dtrace',
+ ['.*esmtprc'] = 'esmtprc',
+ ['.*Eterm/.*%.cfg'] = 'eterm',
+ ['[a-zA-Z0-9].*Dict'] = function(path, bufnr)
+ return require('vim.filetype.detect').foam(bufnr)
+ end,
+ ['[a-zA-Z0-9].*Dict%..*'] = function(path, bufnr)
+ return require('vim.filetype.detect').foam(bufnr)
+ end,
+ ['[a-zA-Z].*Properties'] = function(path, bufnr)
+ return require('vim.filetype.detect').foam(bufnr)
+ end,
+ ['[a-zA-Z].*Properties%..*'] = function(path, bufnr)
+ return require('vim.filetype.detect').foam(bufnr)
+ end,
+ ['.*Transport%..*'] = function(path, bufnr)
+ return require('vim.filetype.detect').foam(bufnr)
+ end,
+ ['.*/constant/g'] = function(path, bufnr)
+ return require('vim.filetype.detect').foam(bufnr)
+ end,
+ ['.*/0/.*'] = function(path, bufnr)
+ return require('vim.filetype.detect').foam(bufnr)
+ end,
+ ['.*/0%.orig/.*'] = function(path, bufnr)
+ return require('vim.filetype.detect').foam(bufnr)
+ end,
+ ['.*/%.fvwm/.*'] = starsetf('fvwm'),
+ ['.*fvwmrc.*'] = starsetf(function(path, bufnr)
+ return 'fvwm', function(b)
+ vim.b[b].fvwm_version = 1
+ end
+ end),
+ ['.*fvwm95.*%.hook'] = starsetf(function(path, bufnr)
+ return 'fvwm', function(b)
+ vim.b[b].fvwm_version = 1
+ end
+ end),
+ ['.*fvwm2rc.*'] = starsetf(function(path, bufnr)
+ return require('vim.filetype.detect').fvwm(path)
+ end),
+ ['.*/tmp/lltmp.*'] = starsetf('gedcom'),
+ ['.*/etc/gitconfig%.d/.*'] = starsetf('gitconfig'),
+ ['.*/gitolite%-admin/conf/.*'] = starsetf('gitolite'),
+ ['tmac%..*'] = starsetf('nroff'),
+ ['.*/%.gitconfig%.d/.*'] = starsetf('gitconfig'),
+ ['.*%.git/.*'] = {
+ function(path, bufnr)
+ return require('vim.filetype.detect').git(bufnr)
+ end,
+ -- Decrease priority to run after simple pattern checks
+ { priority = -1 },
+ },
+ ['.*%.git/modules/.*/config'] = 'gitconfig',
+ ['.*%.git/modules/config'] = 'gitconfig',
+ ['.*%.git/config'] = 'gitconfig',
+ ['.*/etc/gitconfig'] = 'gitconfig',
+ ['.*/%.config/git/config'] = 'gitconfig',
+ ['.*%.git/config%.worktree'] = 'gitconfig',
+ ['.*%.git/worktrees/.*/config%.worktree'] = 'gitconfig',
+ ['.*/git/config'] = function(path, bufnr)
+ if vim.env.XDG_CONFIG_HOME and path:find(vim.env.XDG_CONFIG_HOME .. '/git/config') then
+ return 'gitconfig'
+ end
+ end,
+ ['%.gitsendemail%.msg%.......'] = 'gitsendemail',
+ ['gkrellmrc_.'] = 'gkrellmrc',
+ ['.*/usr/.*/gnupg/options%.skel'] = 'gpg',
+ ['.*/%.gnupg/options'] = 'gpg',
+ ['.*/%.gnupg/gpg%.conf'] = 'gpg',
+ ['.*/options'] = function(path, bufnr)
+ if vim.env.GNUPGHOME and path:find(vim.env.GNUPGHOME .. '/options') then
+ return 'gpg'
+ end
+ end,
+ ['.*/gpg%.conf'] = function(path, bufnr)
+ if vim.env.GNUPGHOME and path:find(vim.env.GNUPGHOME .. '/gpg%.conf') then
+ return 'gpg'
+ end
+ end,
+ ['.*/etc/group'] = 'group',
+ ['.*/etc/gshadow'] = 'group',
+ ['.*/etc/group%.edit'] = 'group',
+ ['.*/var/backups/gshadow%.bak'] = 'group',
+ ['.*/etc/group%-'] = 'group',
+ ['.*/etc/gshadow%-'] = 'group',
+ ['.*/var/backups/group%.bak'] = 'group',
+ ['.*/etc/gshadow%.edit'] = 'group',
+ ['.*/boot/grub/grub%.conf'] = 'grub',
+ ['.*/boot/grub/menu%.lst'] = 'grub',
+ ['.*/etc/grub%.conf'] = 'grub',
+ -- gtkrc* and .gtkrc*
+ ['%.?gtkrc.*'] = starsetf('gtkrc'),
+ [vim.env.VIMRUNTIME .. '/doc/.*%.txt'] = 'help',
+ ['hg%-editor%-.*%.txt'] = 'hgcommit',
+ ['.*/etc/host%.conf'] = 'hostconf',
+ ['.*/etc/hosts%.deny'] = 'hostsaccess',
+ ['.*/etc/hosts%.allow'] = 'hostsaccess',
+ ['.*%.html%.m4'] = 'htmlm4',
+ ['.*/%.i3/config'] = 'i3config',
+ ['.*/sway/config'] = 'i3config',
+ ['.*/i3/config'] = 'i3config',
+ ['.*/%.sway/config'] = 'i3config',
+ ['.*/%.icewm/menu'] = 'icemenu',
+ ['.*/etc/initng/.*/.*%.i'] = 'initng',
+ ['JAM.*%..*'] = starsetf('jam'),
+ ['Prl.*%..*'] = starsetf('jam'),
+ ['.*%.properties_..'] = 'jproperties',
+ ['.*%.properties_.._..'] = 'jproperties',
+ ['.*%.properties_.._.._.*'] = starsetf('jproperties'),
+ ['Kconfig%..*'] = starsetf('kconfig'),
+ ['.*%.[Ss][Uu][Bb]'] = 'krl',
+ ['lilo%.conf.*'] = starsetf('lilo'),
+ ['.*/etc/logcheck/.*%.d.*/.*'] = starsetf('logcheck'),
+ ['.*lftp/rc'] = 'lftp',
+ ['.*/%.libao'] = 'libao',
+ ['.*/etc/libao%.conf'] = 'libao',
+ ['.*/etc/.*limits%.conf'] = 'limits',
+ ['.*/etc/limits'] = 'limits',
+ ['.*/etc/.*limits%.d/.*%.conf'] = 'limits',
+ ['.*/LiteStep/.*/.*%.rc'] = 'litestep',
+ ['.*/etc/login%.access'] = 'loginaccess',
+ ['.*/etc/login%.defs'] = 'logindefs',
+ ['%.letter%.%d+'] = 'mail',
+ ['%.article%.%d+'] = 'mail',
+ ['/tmp/SLRN[0-9A-Z.]+'] = 'mail',
+ ['ae%d+%.txt'] = 'mail',
+ ['pico%.%d+'] = 'mail',
+ ['mutt%-.*%-%w+'] = 'mail',
+ ['muttng%-.*%-%w+'] = 'mail',
+ ['neomutt%-.*%-%w+'] = 'mail',
+ ['mutt' .. string.rep('[%w_-]', 6)] = 'mail',
+ ['neomutt' .. string.rep('[%w_-]', 6)] = 'mail',
+ ['snd%.%d+'] = 'mail',
+ ['reportbug%-.*'] = starsetf('mail'),
+ ['.*/etc/mail/aliases'] = 'mailaliases',
+ ['.*/etc/aliases'] = 'mailaliases',
+ ['.*[mM]akefile'] = 'make',
+ ['[mM]akefile.*'] = starsetf('make'),
+ ['.*/etc/man%.conf'] = 'manconf',
+ ['.*/log/auth'] = 'messages',
+ ['.*/log/cron'] = 'messages',
+ ['.*/log/daemon'] = 'messages',
+ ['.*/log/debug'] = 'messages',
+ ['.*/log/kern'] = 'messages',
+ ['.*/log/lpr'] = 'messages',
+ ['.*/log/mail'] = 'messages',
+ ['.*/log/messages'] = 'messages',
+ ['.*/log/news/news'] = 'messages',
+ ['.*/log/syslog'] = 'messages',
+ ['.*/log/user'] = 'messages',
+ ['.*/log/auth%.log'] = 'messages',
+ ['.*/log/cron%.log'] = 'messages',
+ ['.*/log/daemon%.log'] = 'messages',
+ ['.*/log/debug%.log'] = 'messages',
+ ['.*/log/kern%.log'] = 'messages',
+ ['.*/log/lpr%.log'] = 'messages',
+ ['.*/log/mail%.log'] = 'messages',
+ ['.*/log/messages%.log'] = 'messages',
+ ['.*/log/news/news%.log'] = 'messages',
+ ['.*/log/syslog%.log'] = 'messages',
+ ['.*/log/user%.log'] = 'messages',
+ ['.*/log/auth%.err'] = 'messages',
+ ['.*/log/cron%.err'] = 'messages',
+ ['.*/log/daemon%.err'] = 'messages',
+ ['.*/log/debug%.err'] = 'messages',
+ ['.*/log/kern%.err'] = 'messages',
+ ['.*/log/lpr%.err'] = 'messages',
+ ['.*/log/mail%.err'] = 'messages',
+ ['.*/log/messages%.err'] = 'messages',
+ ['.*/log/news/news%.err'] = 'messages',
+ ['.*/log/syslog%.err'] = 'messages',
+ ['.*/log/user%.err'] = 'messages',
+ ['.*/log/auth%.info'] = 'messages',
+ ['.*/log/cron%.info'] = 'messages',
+ ['.*/log/daemon%.info'] = 'messages',
+ ['.*/log/debug%.info'] = 'messages',
+ ['.*/log/kern%.info'] = 'messages',
+ ['.*/log/lpr%.info'] = 'messages',
+ ['.*/log/mail%.info'] = 'messages',
+ ['.*/log/messages%.info'] = 'messages',
+ ['.*/log/news/news%.info'] = 'messages',
+ ['.*/log/syslog%.info'] = 'messages',
+ ['.*/log/user%.info'] = 'messages',
+ ['.*/log/auth%.warn'] = 'messages',
+ ['.*/log/cron%.warn'] = 'messages',
+ ['.*/log/daemon%.warn'] = 'messages',
+ ['.*/log/debug%.warn'] = 'messages',
+ ['.*/log/kern%.warn'] = 'messages',
+ ['.*/log/lpr%.warn'] = 'messages',
+ ['.*/log/mail%.warn'] = 'messages',
+ ['.*/log/messages%.warn'] = 'messages',
+ ['.*/log/news/news%.warn'] = 'messages',
+ ['.*/log/syslog%.warn'] = 'messages',
+ ['.*/log/user%.warn'] = 'messages',
+ ['.*/log/auth%.crit'] = 'messages',
+ ['.*/log/cron%.crit'] = 'messages',
+ ['.*/log/daemon%.crit'] = 'messages',
+ ['.*/log/debug%.crit'] = 'messages',
+ ['.*/log/kern%.crit'] = 'messages',
+ ['.*/log/lpr%.crit'] = 'messages',
+ ['.*/log/mail%.crit'] = 'messages',
+ ['.*/log/messages%.crit'] = 'messages',
+ ['.*/log/news/news%.crit'] = 'messages',
+ ['.*/log/syslog%.crit'] = 'messages',
+ ['.*/log/user%.crit'] = 'messages',
+ ['.*/log/auth%.notice'] = 'messages',
+ ['.*/log/cron%.notice'] = 'messages',
+ ['.*/log/daemon%.notice'] = 'messages',
+ ['.*/log/debug%.notice'] = 'messages',
+ ['.*/log/kern%.notice'] = 'messages',
+ ['.*/log/lpr%.notice'] = 'messages',
+ ['.*/log/mail%.notice'] = 'messages',
+ ['.*/log/messages%.notice'] = 'messages',
+ ['.*/log/news/news%.notice'] = 'messages',
+ ['.*/log/syslog%.notice'] = 'messages',
+ ['.*/log/user%.notice'] = 'messages',
+ ['.*%.[Mm][Oo][Dd]'] = function(path, bufnr)
+ return require('vim.filetype.detect').mod(path, bufnr)
+ end,
+ ['.*/etc/modules%.conf'] = 'modconf',
+ ['.*/etc/conf%.modules'] = 'modconf',
+ ['.*/etc/modules'] = 'modconf',
+ ['.*/etc/modprobe%..*'] = starsetf('modconf'),
+ ['.*/etc/modutils/.*'] = starsetf(function(path, bufnr)
+ if vim.fn.executable(vim.fn.expand(path)) ~= 1 then
+ return 'modconf'
+ end
+ end),
+ ['.*%.[mi][3g]'] = 'modula3',
+ ['Muttrc'] = 'muttrc',
+ ['Muttngrc'] = 'muttrc',
+ ['.*/etc/Muttrc%.d/.*'] = starsetf('muttrc'),
+ ['.*/%.mplayer/config'] = 'mplayerconf',
+ ['Muttrc.*'] = starsetf('muttrc'),
+ ['Muttngrc.*'] = starsetf('muttrc'),
+ -- muttrc* and .muttrc*
+ ['%.?muttrc.*'] = starsetf('muttrc'),
+ -- muttngrc* and .muttngrc*
+ ['%.?muttngrc.*'] = starsetf('muttrc'),
+ ['.*/%.mutt/muttrc.*'] = starsetf('muttrc'),
+ ['.*/%.muttng/muttrc.*'] = starsetf('muttrc'),
+ ['.*/%.muttng/muttngrc.*'] = starsetf('muttrc'),
+ ['rndc.*%.conf'] = 'named',
+ ['rndc.*%.key'] = 'named',
+ ['named.*%.conf'] = 'named',
+ ['.*/etc/nanorc'] = 'nanorc',
+ ['.*%.NS[ACGLMNPS]'] = 'natural',
+ ['Neomuttrc.*'] = starsetf('neomuttrc'),
+ -- neomuttrc* and .neomuttrc*
+ ['%.?neomuttrc.*'] = starsetf('neomuttrc'),
+ ['.*/%.neomutt/neomuttrc.*'] = starsetf('neomuttrc'),
+ ['nginx.*%.conf'] = 'nginx',
+ ['.*/etc/nginx/.*'] = 'nginx',
+ ['.*nginx%.conf'] = 'nginx',
+ ['.*/nginx/.*%.conf'] = 'nginx',
+ ['.*/usr/local/nginx/conf/.*'] = 'nginx',
+ ['.*%.[1-9]'] = function(path, bufnr)
+ return require('vim.filetype.detect').nroff(bufnr)
+ end,
+ ['.*%.ml%.cppo'] = 'ocaml',
+ ['.*%.mli%.cppo'] = 'ocaml',
+ ['.*%.opam%.template'] = 'opam',
+ ['.*%.[Oo][Pp][Ll]'] = 'opl',
+ ['.*/etc/pam%.conf'] = 'pamconf',
+ ['.*/etc/pam%.d/.*'] = starsetf('pamconf'),
+ ['.*/etc/passwd%-'] = 'passwd',
+ ['.*/etc/shadow'] = 'passwd',
+ ['.*/etc/shadow%.edit'] = 'passwd',
+ ['.*/var/backups/shadow%.bak'] = 'passwd',
+ ['.*/var/backups/passwd%.bak'] = 'passwd',
+ ['.*/etc/passwd'] = 'passwd',
+ ['.*/etc/passwd%.edit'] = 'passwd',
+ ['.*/etc/shadow%-'] = 'passwd',
+ ['%.?gitolite%.rc'] = 'perl',
+ ['example%.gitolite%.rc'] = 'perl',
+ ['.*%.php%d'] = 'php',
+ ['.*/%.pinforc'] = 'pinfo',
+ ['.*/etc/pinforc'] = 'pinfo',
+ ['.*%.[Pp][Rr][Gg]'] = function(path, bufnr)
+ return require('vim.filetype.detect').prg(bufnr)
+ end,
+ ['.*/etc/protocols'] = 'protocols',
+ ['.*printcap.*'] = starsetf(function(path, bufnr)
+ return require('vim.filetype.detect').printcap('print')
+ end),
+ ['.*baseq[2-3]/.*%.cfg'] = 'quake',
+ ['.*quake[1-3]/.*%.cfg'] = 'quake',
+ ['.*id1/.*%.cfg'] = 'quake',
+ ['.*/queries/.*%.scm'] = 'query', -- tree-sitter queries (Neovim only)
+ ['.*,v'] = 'rcs',
+ ['%.reminders.*'] = starsetf('remind'),
+ ['[rR]akefile.*'] = starsetf('ruby'),
+ ['[rR]antfile'] = 'ruby',
+ ['[rR]akefile'] = 'ruby',
+ ['.*/etc/sensors%.d/[^.].*'] = starsetf('sensors'),
+ ['.*/etc/sensors%.conf'] = 'sensors',
+ ['.*/etc/sensors3%.conf'] = 'sensors',
+ ['.*/etc/services'] = 'services',
+ ['.*/etc/serial%.conf'] = 'setserial',
+ ['.*/etc/udev/cdsymlinks%.conf'] = 'sh',
+ ['%.bash[_%-]aliases'] = function(path, bufnr)
+ return require('vim.filetype.detect').sh(path, M.getlines(bufnr), 'bash')
+ end,
+ ['%.bash[_%-]logout'] = function(path, bufnr)
+ return require('vim.filetype.detect').sh(path, M.getlines(bufnr), 'bash')
+ end,
+ ['%.bash[_%-]profile'] = function(path, bufnr)
+ return require('vim.filetype.detect').sh(path, M.getlines(bufnr), 'bash')
+ end,
+ ['%.kshrc.*'] = function(path, bufnr)
+ return require('vim.filetype.detect').sh(path, M.getlines(bufnr), 'ksh')
+ end,
+ ['%.profile.*'] = function(path, bufnr)
+ return require('vim.filetype.detect').sh(path, M.getlines(bufnr))
+ end,
+ ['.*/etc/profile'] = function(path, bufnr)
+ return require('vim.filetype.detect').sh(path, M.getlines(bufnr))
+ end,
+ ['bash%-fc[%-%.]'] = function(path, bufnr)
+ return require('vim.filetype.detect').sh(path, M.getlines(bufnr), 'bash')
+ end,
+ ['%.tcshrc.*'] = function(path, bufnr)
+ return require('vim.filetype.detect').sh(path, M.getlines(bufnr), 'tcsh')
+ end,
+ ['.*/etc/sudoers%.d/.*'] = starsetf('sudoers'),
+ ['.*%._sst%.meta'] = 'sisu',
+ ['.*%.%-sst%.meta'] = 'sisu',
+ ['.*%.sst%.meta'] = 'sisu',
+ ['.*/etc/slp%.conf'] = 'slpconf',
+ ['.*/etc/slp%.reg'] = 'slpreg',
+ ['.*/etc/slp%.spi'] = 'slpspi',
+ ['.*/etc/ssh/ssh_config%.d/.*%.conf'] = 'sshconfig',
+ ['.*/%.ssh/config'] = 'sshconfig',
+ ['.*/%.ssh/.*%.conf'] = 'sshconfig',
+ ['.*/etc/ssh/sshd_config%.d/.*%.conf'] = 'sshdconfig',
+ ['.*%.[Ss][Rr][Cc]'] = function(path, bufnr)
+ return require('vim.filetype.detect').src(bufnr)
+ end,
+ ['.*/etc/sudoers'] = 'sudoers',
+ ['svn%-commit.*%.tmp'] = 'svn',
+ ['.*%.swift%.gyb'] = 'swiftgyb',
+ ['.*%.[Ss][Yy][Ss]'] = function(path, bufnr)
+ return require('vim.filetype.detect').sys(bufnr)
+ end,
+ ['.*/etc/sysctl%.conf'] = 'sysctl',
+ ['.*/etc/sysctl%.d/.*%.conf'] = 'sysctl',
+ ['.*/systemd/.*%.automount'] = 'systemd',
+ ['.*/systemd/.*%.dnssd'] = 'systemd',
+ ['.*/systemd/.*%.link'] = 'systemd',
+ ['.*/systemd/.*%.mount'] = 'systemd',
+ ['.*/systemd/.*%.netdev'] = 'systemd',
+ ['.*/systemd/.*%.network'] = 'systemd',
+ ['.*/systemd/.*%.nspawn'] = 'systemd',
+ ['.*/systemd/.*%.path'] = 'systemd',
+ ['.*/systemd/.*%.service'] = 'systemd',
+ ['.*/systemd/.*%.slice'] = 'systemd',
+ ['.*/systemd/.*%.socket'] = 'systemd',
+ ['.*/systemd/.*%.swap'] = 'systemd',
+ ['.*/systemd/.*%.target'] = 'systemd',
+ ['.*/systemd/.*%.timer'] = 'systemd',
+ ['.*/etc/systemd/.*%.conf%.d/.*%.conf'] = 'systemd',
+ ['.*/%.config/systemd/user/.*%.d/.*%.conf'] = 'systemd',
+ ['.*/etc/systemd/system/.*%.d/.*%.conf'] = 'systemd',
+ ['.*/etc/systemd/system/.*%.d/%.#.*'] = 'systemd',
+ ['.*/etc/systemd/system/%.#.*'] = 'systemd',
+ ['.*/%.config/systemd/user/.*%.d/%.#.*'] = 'systemd',
+ ['.*/%.config/systemd/user/%.#.*'] = 'systemd',
+ ['.*termcap.*'] = starsetf(function(path, bufnr)
+ return require('vim.filetype.detect').printcap('term')
+ end),
+ ['.*%.t%.html'] = 'tilde',
+ ['%.?tmux.*%.conf'] = 'tmux',
+ ['%.?tmux.*%.conf.*'] = { 'tmux', { priority = -1 } },
+ ['.*/%.cargo/config'] = 'toml',
+ ['.*/%.cargo/credentials'] = 'toml',
+ ['.*/etc/udev/udev%.conf'] = 'udevconf',
+ ['.*/etc/udev/permissions%.d/.*%.permissions'] = 'udevperm',
+ ['.*/etc/updatedb%.conf'] = 'updatedb',
+ ['.*/%.init/.*%.override'] = 'upstart',
+ ['.*/usr/share/upstart/.*%.conf'] = 'upstart',
+ ['.*/%.config/upstart/.*%.override'] = 'upstart',
+ ['.*/etc/init/.*%.conf'] = 'upstart',
+ ['.*/etc/init/.*%.override'] = 'upstart',
+ ['.*/%.config/upstart/.*%.conf'] = 'upstart',
+ ['.*/%.init/.*%.conf'] = 'upstart',
+ ['.*/usr/share/upstart/.*%.override'] = 'upstart',
+ ['.*%.[Ll][Oo][Gg]'] = function(path, bufnr)
+ return require('vim.filetype.detect').log(path)
+ end,
+ ['.*%.vhdl_[0-9].*'] = starsetf('vhdl'),
+ ['.*%.ws[fc]'] = 'wsh',
+ ['.*/Xresources/.*'] = starsetf('xdefaults'),
+ ['.*/app%-defaults/.*'] = starsetf('xdefaults'),
+ ['.*/etc/xinetd%.conf'] = 'xinetd',
+ ['.*/etc/blkid%.tab'] = 'xml',
+ ['.*/etc/blkid%.tab%.old'] = 'xml',
+ ['.*%.vbproj%.user'] = 'xml',
+ ['.*%.fsproj%.user'] = 'xml',
+ ['.*%.csproj%.user'] = 'xml',
+ ['.*/etc/xdg/menus/.*%.menu'] = 'xml',
+ ['.*Xmodmap'] = 'xmodmap',
+ ['.*/etc/zprofile'] = 'zsh',
+ ['.*vimrc.*'] = starsetf('vim'),
+ ['Xresources.*'] = starsetf('xdefaults'),
+ ['.*/etc/xinetd%.d/.*'] = starsetf('xinetd'),
+ ['.*xmodmap.*'] = starsetf('xmodmap'),
+ ['.*/xorg%.conf%.d/.*%.conf'] = function(path, bufnr)
+ return 'xf86config', function(b)
+ vim.b[b].xf86conf_xfree86_version = 4
+ end
+ end,
+ -- Increase priority to run before the pattern below
+ ['XF86Config%-4.*'] = starsetf(function(path, bufnr)
+ return 'xf86conf', function(b)
+ vim.b[b].xf86conf_xfree86_version = 4
+ end
+ end, { priority = -math.huge + 1 }),
+ ['XF86Config.*'] = starsetf(function(path, bufnr)
+ return require('vim.filetype.detect').xfree86()
+ end),
+ ['%.zcompdump.*'] = starsetf('zsh'),
+ -- .zlog* and zlog*
+ ['%.?zlog.*'] = starsetf('zsh'),
+ -- .zsh* and zsh*
+ ['%.?zsh.*'] = starsetf('zsh'),
+ -- Ignored extension
+ ['.*~'] = function(path, bufnr)
+ local short = path:gsub('~$', '', 1)
+ if path ~= short and short ~= '' then
+ return M.match({ buf = bufnr, filename = vim.fn.fnameescape(short) })
+ end
+ end,
-- END PATTERN
}
-- luacheck: pop
+-- luacheck: pop
---@private
local function sort_by_priority(t)
local sorted = {}
for k, v in pairs(t) do
- local ft = type(v) == "table" and v[1] or v
- assert(type(ft) == "string" or type(ft) == "function", "Expected string or function for filetype")
+ local ft = type(v) == 'table' and v[1] or v
+ assert(
+ type(ft) == 'string' or type(ft) == 'function',
+ 'Expected string or function for filetype'
+ )
- local opts = (type(v) == "table" and type(v[2]) == "table") and v[2] or {}
+ local opts = (type(v) == 'table' and type(v[2]) == 'table') and v[2] or {}
if not opts.priority then
opts.priority = 0
end
@@ -1372,8 +2198,18 @@ end
local pattern_sorted = sort_by_priority(pattern)
---@private
-local function normalize_path(path)
- return (path:gsub("\\", "/"))
+local function normalize_path(path, as_pattern)
+ local normal = path:gsub('\\', '/')
+ if normal:find('^~') then
+ if as_pattern then
+ -- Escape Lua's metacharacters when $HOME is used in a pattern.
+ -- The rest of path should already be properly escaped.
+ normal = vim.pesc(vim.env.HOME) .. normal:sub(2)
+ else
+ normal = vim.env.HOME .. normal:sub(2)
+ end
+ end
+ return normal
end
--- Add new filetype mappings.
@@ -1381,7 +2217,7 @@ end
--- Filetype mappings can be added either by extension or by filename (either
--- the "tail" or the full file path). The full file path is checked first,
--- followed by the file name. If a match is not found using the filename, then
---- the filename is matched against the list of patterns (sorted by priority)
+--- the filename is matched against the list of |lua-patterns| (sorted by priority)
--- until a match is found. Lastly, if pattern matching does not find a
--- filetype, then the file extension is used.
---
@@ -1389,7 +2225,9 @@ end
--- filetype directly) or a function. If a function, it takes the full path and
--- buffer number of the file as arguments (along with captures from the matched
--- pattern, if any) and should return a string that will be used as the
---- buffer's filetype.
+--- buffer's filetype. Optionally, the function can return a second function
+--- value which, when called, modifies the state of the buffer. This can be used
+--- to, for example, set filetype-specific buffer variables.
---
--- Filename patterns can specify an optional priority to resolve cases when a
--- file path matches multiple patterns. Higher priorities are matched first.
@@ -1397,8 +2235,7 @@ end
---
--- See $VIMRUNTIME/lua/vim/filetype.lua for more examples.
---
---- Note that Lua filetype detection is only enabled when |g:do_filetype_lua| is
---- set to 1.
+--- Note that Lua filetype detection is disabled when |g:do_legacy_filetype| is set.
---
--- Example:
--- <pre>
@@ -1407,7 +2244,10 @@ end
--- foo = "fooscript",
--- bar = function(path, bufnr)
--- if some_condition() then
---- return "barscript"
+--- return "barscript", function(bufnr)
+--- -- Set a buffer variable
+--- vim.b[bufnr].barscript_version = 2
+--- end
--- end
--- return "bar"
--- end,
@@ -1431,6 +2271,25 @@ end
--- })
--- </pre>
---
+--- To add a fallback match on contents (see |new-filetype-scripts|), use
+--- <pre>
+--- vim.filetype.add {
+--- pattern = {
+--- ['.*'] = {
+--- priority = -math.huge,
+--- function(path, bufnr)
+--- local content = vim.filetype.getlines(bufnr, 1)
+--- if vim.filetype.matchregex(content, { [[^#!.*\\<mine\\>]] }) then
+--- return 'mine'
+--- elseif vim.filetype.matchregex(content, { [[\\<drawing\\>]] }) then
+--- return 'drawing'
+--- end
+--- end,
+--- },
+--- },
+--- }
+--- </pre>
+---
---@param filetypes table A table containing new filetype maps (see example).
function M.add(filetypes)
for k, v in pairs(filetypes.extension or {}) do
@@ -1442,7 +2301,7 @@ function M.add(filetypes)
end
for k, v in pairs(filetypes.pattern or {}) do
- pattern[normalize_path(k)] = v
+ pattern[normalize_path(k, true)] = v
end
if filetypes.pattern then
@@ -1452,27 +2311,34 @@ end
---@private
local function dispatch(ft, path, bufnr, ...)
- if type(ft) == "function" then
- ft = ft(path, bufnr, ...)
+ local on_detect
+ if type(ft) == 'function' then
+ if bufnr then
+ ft, on_detect = ft(path, bufnr, ...)
+ else
+ -- If bufnr is nil (meaning we are matching only against the filename), set it to an invalid
+ -- value (-1) and catch any errors from the filetype detection function. If the function tries
+ -- to use the buffer then it will fail, but this enables functions which do not need a buffer
+ -- to still work.
+ local ok
+ ok, ft, on_detect = pcall(ft, path, -1, ...)
+ if not ok then
+ return
+ end
+ end
end
- if type(ft) == "string" then
- api.nvim_buf_set_option(bufnr, "filetype", ft)
- return true
+ if type(ft) == 'string' then
+ return ft, on_detect
end
-
- -- Any non-falsey value (that is, anything other than 'nil' or 'false') will
- -- end filetype matching. This is useful for e.g. the dist#ft functions that
- -- return 0, but set the buffer's filetype themselves
- return ft
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 fullpat = '^' .. pat .. '$'
local matches
- if pat:find("/") then
+ 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)
@@ -1483,28 +2349,87 @@ local function match_pattern(name, path, tail, pat)
return matches
end
---- Set the filetype for the given buffer from a file name.
+--- Perform filetype detection.
+---
+--- The filetype can be detected using one of three methods:
+--- 1. Using an existing buffer
+--- 2. Using only a file name
+--- 3. Using only file contents
+---
+--- Of these, option 1 provides the most accurate result as it uses both the buffer's filename and
+--- (optionally) the buffer contents. Options 2 and 3 can be used without an existing buffer, but
+--- may not always provide a match in cases where the filename (or contents) cannot unambiguously
+--- determine the filetype.
+---
+--- Each of the three options is specified using a key to the single argument of this function.
+--- Example:
+---
+--- <pre>
+--- -- Using a buffer number
+--- vim.filetype.match({ buf = 42 })
+---
+--- -- Override the filename of the given buffer
+--- vim.filetype.match({ buf = 42, filename = 'foo.c' })
---
----@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
- -- wish to perform filetype detection on buffers other than the current one.
- bufnr = bufnr or api.nvim_get_current_buf()
+--- -- Using a filename without a buffer
+--- vim.filetype.match({ filename = 'main.lua' })
+---
+--- -- Using file contents
+--- vim.filetype.match({ contents = {'#!/usr/bin/env bash'} })
+--- </pre>
+---
+---@param args table Table specifying which matching strategy to use. Accepted keys are:
+--- * buf (number): Buffer number to use for matching. Mutually exclusive with
+--- {contents}
+--- * filename (string): Filename to use for matching. When {buf} is given,
+--- defaults to the filename of the given buffer number. The
+--- file need not actually exist in the filesystem. When used
+--- without {buf} only the name of the file is used for
+--- filetype matching. This may result in failure to detect
+--- the filetype in cases where the filename alone is not
+--- enough to disambiguate the filetype.
+--- * contents (table): An array of lines representing file contents to use for
+--- matching. Can be used with {filename}. Mutually exclusive
+--- with {buf}.
+---@return string|nil If a match was found, the matched filetype.
+---@return function|nil A function that modifies buffer state when called (for example, to set some
+--- filetype specific buffer variables). The function accepts a buffer number as
+--- its only argument.
+function M.match(args)
+ vim.validate({
+ arg = { args, 't' },
+ })
+
+ if not (args.buf or args.filename or args.contents) then
+ error('At least one of "buf", "filename", or "contents" must be given')
+ end
+
+ local bufnr = args.buf
+ local name = args.filename
+ local contents = args.contents
+
+ if bufnr and not name then
+ name = api.nvim_buf_get_name(bufnr)
+ end
- name = normalize_path(name)
+ if name then
+ name = normalize_path(name)
+ end
+
+ local ft, on_detect
-- First check for the simple case where the full path exists as a key
- local path = vim.fn.resolve(vim.fn.fnamemodify(name, ":p"))
- if dispatch(filename[path], path, bufnr) then
- return
+ local path = vim.fn.fnamemodify(name, ':p')
+ ft, on_detect = dispatch(filename[path], path, bufnr)
+ if ft then
+ return ft, on_detect
end
-- Next check against just the file name
- local tail = vim.fn.fnamemodify(name, ":t")
- if dispatch(filename[tail], path, bufnr) then
- return
+ local tail = vim.fn.fnamemodify(name, ':t')
+ ft, on_detect = dispatch(filename[tail], path, bufnr)
+ if ft then
+ return ft, on_detect
end
-- Next, check the file path against available patterns with non-negative priority
@@ -1517,34 +2442,50 @@ function M.match(name, bufnr)
break
end
- local ft = v[k][1]
+ local filetype = v[k][1]
local matches = match_pattern(name, path, tail, k)
if matches then
- if dispatch(ft, path, bufnr, matches) then
- return
+ ft, on_detect = dispatch(filetype, path, bufnr, matches)
+ if ft then
+ return ft, on_detect
end
end
end
-- Next, check file extension
- local ext = vim.fn.fnamemodify(name, ":e")
- if dispatch(extension[ext], path, bufnr) then
- return
+ local ext = vim.fn.fnamemodify(name, ':e')
+ ft, on_detect = dispatch(extension[ext], path, bufnr)
+ if ft then
+ return ft, on_detect
end
- -- Finally, check patterns with negative priority
+ -- Next, 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 filetype = v[k][1]
local matches = match_pattern(name, path, tail, k)
if matches then
- if dispatch(ft, path, bufnr, matches) then
- return
+ ft, on_detect = dispatch(filetype, path, bufnr, matches)
+ if ft then
+ return ft, on_detect
end
end
end
+
+ -- Finally, check file contents
+ if contents or bufnr then
+ contents = contents or M.getlines(bufnr)
+ -- If name is nil, catch any errors from the contents filetype detection function.
+ -- If the function tries to use the filename that is nil then it will fail,
+ -- but this enables checks which do not need a filename to still work.
+ local ok
+ ok, ft = pcall(require('vim.filetype.detect').match_contents, contents, name)
+ if ok and ft then
+ return ft
+ end
+ end
end
return M
diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua
new file mode 100644
index 0000000000..8c10517687
--- /dev/null
+++ b/runtime/lua/vim/filetype/detect.lua
@@ -0,0 +1,1630 @@
+-- Contains filetype detection functions for use in filetype.lua that are either:
+-- * used more than once or
+-- * complex (e.g. check more than one line or use conditionals).
+-- Simple one-line checks, such as a check for a string in the first line are better inlined in filetype.lua.
+
+-- A few guidelines to follow when porting a new function:
+-- * Sort the function alphabetically and omit 'ft' or 'check' from the new function name.
+-- * Use ':find' instead of ':match' / ':sub' if possible.
+-- * When '=~' is used to match a pattern, there are two possibilities:
+-- - If the pattern only contains lowercase characters, treat the comparison as case-insensitive.
+-- - Otherwise, treat it as case-sensitive.
+-- (Basically, we apply 'smartcase': if upper case characters are used in the original pattern, then
+-- it's likely that case does matter).
+-- * When '\k', '\<' or '\>' is used in a pattern, use the 'matchregex' function.
+-- Note that vim.regex is case-sensitive by default, so add the '\c' flag if only lowercase letters
+-- are present in the pattern:
+-- Example:
+-- `if line =~ '^\s*unwind_protect\>'` => `if matchregex(line, [[\c^\s*unwind_protect\>]])`
+
+local M = {}
+
+local getlines = vim.filetype.getlines
+local findany = vim.filetype.findany
+local nextnonblank = vim.filetype.nextnonblank
+local matchregex = vim.filetype.matchregex
+
+-- luacheck: push no unused args
+-- luacheck: push ignore 122
+
+-- This function checks for the kind of assembly that is wanted by the user, or
+-- can be detected from the first five lines of the file.
+function M.asm(bufnr)
+ local syntax = vim.b[bufnr].asmsyntax
+ if not syntax or syntax == '' then
+ syntax = M.asm_syntax(bufnr)
+ end
+
+ -- If b:asmsyntax still isn't set, default to asmsyntax or GNU
+ if not syntax or syntax == '' then
+ if vim.g.asmsyntax and vim.g.asmsyntax ~= 0 then
+ syntax = vim.g.asmsyntax
+ else
+ syntax = 'asm'
+ end
+ end
+ return syntax, function(b)
+ vim.b[b].asmsyntax = syntax
+ end
+end
+
+-- Active Server Pages (with Perl or Visual Basic Script)
+function M.asp(bufnr)
+ if vim.g.filetype_asp then
+ return vim.g.filetype_asp
+ elseif table.concat(getlines(bufnr, 1, 3)):lower():find('perlscript') then
+ return 'aspperl'
+ else
+ return 'aspvbs'
+ end
+end
+
+-- Checks the first 5 lines for a asmsyntax=foo override.
+-- Only whitespace characters can be present immediately before or after this statement.
+function M.asm_syntax(bufnr)
+ local lines = ' ' .. table.concat(getlines(bufnr, 1, 5), ' '):lower() .. ' '
+ local match = lines:match('%sasmsyntax=([a-zA-Z0-9]+)%s')
+ if match then
+ return match
+ elseif findany(lines, { '%.title', '%.ident', '%.macro', '%.subtitle', '%.library' }) then
+ return 'vmasm'
+ end
+end
+
+local visual_basic_content =
+ { 'vb_name', 'begin vb%.form', 'begin vb%.mdiform', 'begin vb%.usercontrol' }
+
+-- See frm() for Visual Basic form file detection
+function M.bas(bufnr)
+ if vim.g.filetype_bas then
+ return vim.g.filetype_bas
+ end
+
+ -- Most frequent FreeBASIC-specific keywords in distro files
+ local fb_keywords =
+ [[\c^\s*\%(extern\|var\|enum\|private\|scope\|union\|byref\|operator\|constructor\|delete\|namespace\|public\|property\|with\|destructor\|using\)\>\%(\s*[:=(]\)\@!]]
+ local fb_preproc =
+ [[\c^\s*\%(#\s*\a\+\|option\s\+\%(byval\|dynamic\|escape\|\%(no\)\=gosub\|nokeyword\|private\|static\)\>\|\%(''\|rem\)\s*\$lang\>\|def\%(byte\|longint\|short\|ubyte\|uint\|ulongint\|ushort\)\>\)]]
+
+ local fb_comment = "^%s*/'"
+ -- OPTION EXPLICIT, without the leading underscore, is common to many dialects
+ local qb64_preproc = [[\c^\s*\%($\a\+\|option\s\+\%(_explicit\|_\=explicitarray\)\>\)]]
+
+ for _, line in ipairs(getlines(bufnr, 1, 100)) do
+ if findany(line:lower(), visual_basic_content) then
+ return 'vb'
+ elseif
+ line:find(fb_comment)
+ or matchregex(line, fb_preproc)
+ or matchregex(line, fb_keywords)
+ then
+ return 'freebasic'
+ elseif matchregex(line, qb64_preproc) then
+ return 'qb64'
+ end
+ end
+ return 'basic'
+end
+
+function M.bindzone(bufnr, default)
+ local lines = table.concat(getlines(bufnr, 1, 4))
+ if findany(lines, { '^; <<>> DiG [0-9%.]+.* <<>>', '%$ORIGIN', '%$TTL', 'IN%s+SOA' }) then
+ return 'bindzone'
+ end
+ return default
+end
+
+-- Returns true if file content looks like RAPID
+local function is_rapid(bufnr, extension)
+ if extension == 'cfg' then
+ local line = getlines(bufnr, 1):lower()
+ return findany(line, { 'eio:cfg', 'mmc:cfg', 'moc:cfg', 'proc:cfg', 'sio:cfg', 'sys:cfg' })
+ end
+ local line = nextnonblank(bufnr, 1)
+ if line then
+ -- Called from mod, prg or sys functions
+ return matchregex(line:lower(), [[\c\v^\s*%(\%{3}|module\s+\k+\s*%(\(|$))]])
+ end
+ return false
+end
+
+function M.cfg(bufnr)
+ if vim.g.filetype_cfg then
+ return vim.g.filetype_cfg
+ elseif is_rapid(bufnr, 'cfg') then
+ return 'rapid'
+ else
+ return 'cfg'
+ end
+end
+
+-- This function checks if one of the first ten lines start with a '@'. In
+-- that case it is probably a change file.
+-- If the first line starts with # or ! it's probably a ch file.
+-- If a line has "main", "include", "//" or "/*" it's probably ch.
+-- Otherwise CHILL is assumed.
+function M.change(bufnr)
+ local first_line = getlines(bufnr, 1)
+ if findany(first_line, { '^#', '^!' }) then
+ return 'ch'
+ end
+ for _, line in ipairs(getlines(bufnr, 1, 10)) do
+ if line:find('^@') then
+ return 'change'
+ end
+ if line:find('MODULE') then
+ return 'chill'
+ elseif findany(line:lower(), { 'main%s*%(', '#%s*include', '//' }) then
+ return 'ch'
+ end
+ end
+ return 'chill'
+end
+
+function M.changelog(bufnr)
+ local line = getlines(bufnr, 1):lower()
+ if line:find('; urgency=') then
+ return 'debchangelog'
+ end
+ return 'changelog'
+end
+
+function M.class(bufnr)
+ -- Check if not a Java class (starts with '\xca\xfe\xba\xbe')
+ if not getlines(bufnr, 1):find('^\202\254\186\190') then
+ return 'stata'
+ end
+end
+
+function M.cls(bufnr)
+ if vim.g.filetype_cls then
+ return vim.g.filetype_cls
+ end
+ local line = getlines(bufnr, 1)
+ if line:find('^%%') then
+ return 'tex'
+ elseif line:find('^#') and line:lower():find('rexx') then
+ return 'rexx'
+ elseif line == 'VERSION 1.0 CLASS' then
+ return 'vb'
+ else
+ return 'st'
+ end
+end
+
+function M.conf(path, bufnr)
+ if vim.fn.did_filetype() ~= 0 or path:find(vim.g.ft_ignore_pat) then
+ return
+ end
+ for _, line in ipairs(getlines(bufnr, 1, 5)) do
+ if line:find('^#') then
+ return 'conf'
+ end
+ end
+end
+
+-- Debian Control
+function M.control(bufnr)
+ if getlines(bufnr, 1):find('^Source:') then
+ return 'debcontrol'
+ end
+end
+
+-- Debian Copyright
+function M.copyright(bufnr)
+ if getlines(bufnr, 1):find('^Format:') then
+ return 'debcopyright'
+ end
+end
+
+function M.csh(path, bufnr)
+ if vim.fn.did_filetype() ~= 0 then
+ -- Filetype was already detected
+ return
+ end
+ local contents = getlines(bufnr)
+ if vim.g.filetype_csh then
+ return M.shell(path, contents, vim.g.filetype_csh)
+ elseif string.find(vim.o.shell, 'tcsh') then
+ return M.shell(path, contents, 'tcsh')
+ else
+ return M.shell(path, contents, 'csh')
+ end
+end
+
+local function cvs_diff(path, contents)
+ for _, line in ipairs(contents) do
+ if not line:find('^%? ') then
+ if matchregex(line, [[^Index:\s\+\f\+$]]) then
+ -- CVS diff
+ return 'diff'
+ elseif
+ -- Locale input files: Formal Definitions of Cultural Conventions
+ -- Filename must be like en_US, fr_FR@euro or en_US.UTF-8
+ findany(path, {
+ '%a%a_%a%a$',
+ '%a%a_%a%a[%.@]',
+ '%a%a_%a%ai18n$',
+ '%a%a_%a%aPOSIX$',
+ '%a%a_%a%atranslit_',
+ })
+ then
+ -- Only look at the first 100 lines
+ for line_nr = 1, 100 do
+ if not contents[line_nr] then
+ break
+ elseif
+ findany(contents[line_nr], {
+ '^LC_IDENTIFICATION$',
+ '^LC_CTYPE$',
+ '^LC_COLLATE$',
+ '^LC_MONETARY$',
+ '^LC_NUMERIC$',
+ '^LC_TIME$',
+ '^LC_MESSAGES$',
+ '^LC_PAPER$',
+ '^LC_TELEPHONE$',
+ '^LC_MEASUREMENT$',
+ '^LC_NAME$',
+ '^LC_ADDRESS$',
+ })
+ then
+ return 'fdcc'
+ end
+ end
+ end
+ end
+ end
+end
+
+function M.dat(path, bufnr)
+ local file_name = vim.fn.fnamemodify(path, ':t'):lower()
+ -- Innovation data processing
+ if findany(file_name, { '^upstream%.dat$', '^upstream%..*%.dat$', '^.*%.upstream%.dat$' }) then
+ return 'upstreamdat'
+ end
+ if vim.g.filetype_dat then
+ return vim.g.filetype_dat
+ end
+ -- Determine if a *.dat file is Kuka Robot Language
+ local line = nextnonblank(bufnr, 1)
+ if matchregex(line, [[\c\v^\s*%(\&\w+|defdat>)]]) then
+ return 'krl'
+ end
+end
+
+function M.decl(bufnr)
+ for _, line in ipairs(getlines(bufnr, 1, 3)) do
+ if line:lower():find('^<!sgml') then
+ return 'sgmldecl'
+ end
+ end
+end
+
+-- This function is called for all files under */debian/patches/*, make sure not
+-- to non-dep3patch files, such as README and other text files.
+function M.dep3patch(path, bufnr)
+ local file_name = vim.fn.fnamemodify(path, ':t')
+ if file_name == 'series' then
+ return
+ end
+
+ for _, line in ipairs(getlines(bufnr, 1, 100)) do
+ if
+ findany(line, {
+ '^Description:',
+ '^Subject:',
+ '^Origin:',
+ '^Bug:',
+ '^Forwarded:',
+ '^Author:',
+ '^From:',
+ '^Reviewed%-by:',
+ '^Acked%-by:',
+ '^Last%-Updated:',
+ '^Applied%-Upstream:',
+ })
+ then
+ return 'dep3patch'
+ elseif line:find('^%-%-%-') then
+ -- End of headers found. stop processing
+ return
+ end
+ end
+end
+
+local function diff(contents)
+ if
+ contents[1]:find('^%-%-%- ') and contents[2]:find('^%+%+%+ ')
+ or contents[1]:find('^%* looking for ') and contents[2]:find('^%* comparing to ')
+ or contents[1]:find('^%*%*%* ') and contents[2]:find('^%-%-%- ')
+ or contents[1]:find('^=== ') and ((contents[2]:find('^' .. string.rep('=', 66)) and contents[3]:find(
+ '^%-%-% '
+ ) and contents[4]:find('^%+%+%+')) or (contents[2]:find('^%-%-%- ') and contents[3]:find(
+ '^%+%+%+ '
+ )))
+ or findany(contents[1], { '^=== removed', '^=== added', '^=== renamed', '^=== modified' })
+ then
+ return 'diff'
+ end
+end
+
+function M.dns_zone(contents)
+ if
+ findany(
+ contents[1] .. contents[2] .. contents[3] .. contents[4],
+ { '^; <<>> DiG [0-9%.]+.* <<>>', '%$ORIGIN', '%$TTL', 'IN%s+SOA' }
+ )
+ then
+ return 'bindzone'
+ end
+ -- BAAN
+ if -- Check for 1 to 80 '*' characters
+ contents[1]:find('|%*' .. string.rep('%*?', 79)) and contents[2]:find('VRC ')
+ or contents[2]:find('|%*' .. string.rep('%*?', 79)) and contents[3]:find('VRC ')
+ then
+ return 'baan'
+ end
+end
+
+function M.dtrace(bufnr)
+ if vim.fn.did_filetype() ~= 0 then
+ -- Filetype was already detected
+ return
+ end
+ for _, line in ipairs(getlines(bufnr, 1, 100)) do
+ if matchregex(line, [[\c^module\>\|^import\>]]) then
+ -- D files often start with a module and/or import statement.
+ return 'd'
+ elseif findany(line, { '^#!%S+dtrace', '#pragma%s+D%s+option', ':%S-:%S-:' }) then
+ return 'dtrace'
+ end
+ end
+ return 'd'
+end
+
+function M.e(bufnr)
+ if vim.g.filetype_euphoria then
+ return vim.g.filetype_euphoria
+ end
+ for _, line in ipairs(getlines(bufnr, 1, 100)) do
+ if findany(line, { "^%s*<'%s*$", "^%s*'>%s*$" }) then
+ return 'specman'
+ end
+ end
+ return 'eiffel'
+end
+
+function M.edn(bufnr)
+ local line = getlines(bufnr, 1)
+ if matchregex(line, [[\c^\s*(\s*edif\>]]) then
+ return 'edif'
+ else
+ return 'clojure'
+ end
+end
+
+-- This function checks for valid cl syntax in the first five lines.
+-- Look for either an opening comment, '#', or a block start, '{'.
+-- If not found, assume SGML.
+function M.ent(bufnr)
+ for _, line in ipairs(getlines(bufnr, 1, 5)) do
+ if line:find('^%s*[#{]') then
+ return 'cl'
+ elseif not line:find('^%s*$') then
+ -- Not a blank line, not a comment, and not a block start,
+ -- so doesn't look like valid cl code.
+ break
+ end
+ end
+ return 'dtd'
+end
+
+function M.ex(bufnr)
+ if vim.g.filetype_euphoria then
+ return vim.g.filetype_euphoria
+ else
+ for _, line in ipairs(getlines(bufnr, 1, 100)) do
+ if matchregex(line, [[\c^--\|^ifdef\>\|^include\>]]) then
+ return 'euphoria3'
+ end
+ end
+ return 'elixir'
+ end
+end
+
+-- 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
+function M.foam(bufnr)
+ local foam_file = false
+ for _, line in ipairs(getlines(bufnr, 1, 15)) do
+ if line:find('^FoamFile') then
+ foam_file = true
+ elseif foam_file and line:find('^%s*object') then
+ return 'foam'
+ end
+ end
+end
+
+function M.frm(bufnr)
+ if vim.g.filetype_frm then
+ return vim.g.filetype_frm
+ end
+ local lines = table.concat(getlines(bufnr, 1, 5)):lower()
+ if findany(lines, visual_basic_content) then
+ return 'vb'
+ else
+ return 'form'
+ end
+end
+
+function M.fvwm(path)
+ if vim.fn.fnamemodify(path, ':e') == 'm4' then
+ return 'fvwm2m4'
+ end
+ return 'fvwm', function(bufnr)
+ vim.b[bufnr].fvwm_version = 2
+ end
+end
+
+-- Distinguish between Forth and F#.
+function M.fs(bufnr)
+ if vim.g.filetype_fs then
+ return vim.g.filetype_fs
+ end
+ local line = nextnonblank(bufnr, 1)
+ if findany(line, { '^%s*%.?%( ', '^%s*\\G? ', '^\\$', '^%s*: %S' }) then
+ return 'forth'
+ else
+ return 'fsharp'
+ end
+end
+
+function M.git(bufnr)
+ local line = getlines(bufnr, 1)
+ if matchregex(line, [[^\x\{40,\}\>\|^ref: ]]) then
+ return 'git'
+ end
+end
+
+function M.header(bufnr)
+ for _, line in ipairs(getlines(bufnr, 1, 200)) do
+ if findany(line:lower(), { '^@interface', '^@end', '^@class' }) then
+ if vim.g.c_syntax_for_h then
+ return 'objc'
+ else
+ return 'objcpp'
+ end
+ end
+ end
+ if vim.g.c_syntax_for_h then
+ return 'c'
+ elseif vim.g.ch_syntax_for_h then
+ return 'ch'
+ else
+ return 'cpp'
+ end
+end
+
+function M.html(bufnr)
+ for _, line in ipairs(getlines(bufnr, 1, 10)) do
+ if matchregex(line, [[\<DTD\s\+XHTML\s]]) then
+ return 'xhtml'
+ elseif matchregex(line, [[\c{%\s*\(extends\|block\|load\)\>\|{#\s\+]]) then
+ return 'htmldjango'
+ end
+ end
+ return 'html'
+end
+
+-- Virata Config Script File or Drupal module
+function M.hw(bufnr)
+ if getlines(bufnr, 1):lower():find('<%?php') then
+ return 'php'
+ end
+ return 'virata'
+end
+
+function M.idl(bufnr)
+ for _, line in ipairs(getlines(bufnr, 1, 50)) do
+ if findany(line:lower(), { '^%s*import%s+"unknwn"%.idl', '^%s*import%s+"objidl"%.idl' }) then
+ return 'msidl'
+ end
+ end
+ return 'idl'
+end
+
+local pascal_comments = { '^%s*{', '^%s*%(%*', '^%s*//' }
+local pascal_keywords =
+ [[\c^\s*\%(program\|unit\|library\|uses\|begin\|procedure\|function\|const\|type\|var\)\>]]
+
+function M.inc(bufnr)
+ if vim.g.filetype_inc then
+ return vim.g.filetype_inc
+ end
+ local lines = table.concat(getlines(bufnr, 1, 3))
+ if lines:lower():find('perlscript') then
+ return 'aspperl'
+ elseif lines:find('<%%') then
+ return 'aspvbs'
+ elseif lines:find('<%?') then
+ return 'php'
+ -- Pascal supports // comments but they're vary rarely used for file
+ -- headers so assume POV-Ray
+ elseif findany(lines, { '^%s{', '^%s%(%*' }) or matchregex(lines, pascal_keywords) then
+ return 'pascal'
+ elseif findany(lines, { '^%s*inherit ', '^%s*require ', '^%s*%w+%s+= ' }) then
+ return 'bitbake'
+ else
+ local syntax = M.asm_syntax(bufnr)
+ if not syntax or syntax == '' then
+ return 'pov'
+ end
+ return syntax, function(b)
+ vim.b[b].asmsyntax = syntax
+ end
+ end
+end
+
+function M.inp(bufnr)
+ if getlines(bufnr, 1):find('^%*') then
+ return 'abaqus'
+ else
+ for _, line in ipairs(getlines(bufnr, 1, 500)) do
+ if line:lower():find('^header surface data') then
+ return 'trasys'
+ end
+ end
+ end
+end
+
+function M.install(path, bufnr)
+ if getlines(bufnr, 1):lower():find('<%?php') then
+ return 'php'
+ end
+ return M.sh(path, getlines(bufnr), 'bash')
+end
+
+-- Innovation Data Processing
+-- (refactor of filetype.vim since the patterns are case-insensitive)
+function M.log(path)
+ path = path:lower()
+ if
+ findany(
+ path,
+ { 'upstream%.log', 'upstream%..*%.log', '.*%.upstream%.log', 'upstream%-.*%.log' }
+ )
+ then
+ return 'upstreamlog'
+ elseif
+ findany(
+ path,
+ { 'upstreaminstall%.log', 'upstreaminstall%..*%.log', '.*%.upstreaminstall%.log' }
+ )
+ then
+ return 'upstreaminstalllog'
+ elseif findany(path, { 'usserver%.log', 'usserver%..*%.log', '.*%.usserver%.log' }) then
+ return 'usserverlog'
+ elseif findany(path, { 'usw2kagt%.log', 'usw2kagt%..*%.log', '.*%.usw2kagt%.log' }) then
+ return 'usw2kagtlog'
+ end
+end
+
+function M.lpc(bufnr)
+ if vim.g.lpc_syntax_for_c then
+ for _, line in ipairs(getlines(bufnr, 1, 12)) do
+ if
+ findany(line, {
+ '^//',
+ '^inherit',
+ '^private',
+ '^protected',
+ '^nosave',
+ '^string',
+ '^object',
+ '^mapping',
+ '^mixed',
+ })
+ then
+ return 'lpc'
+ end
+ end
+ end
+ return 'c'
+end
+
+function M.m(bufnr)
+ if vim.g.filetype_m then
+ return vim.g.filetype_m
+ end
+
+ -- Excluding end(for|function|if|switch|while) common to Murphi
+ local octave_block_terminators =
+ [[\<end\%(_try_catch\|classdef\|enumeration\|events\|methods\|parfor\|properties\)\>]]
+ local objc_preprocessor =
+ [[\c^\s*#\s*\%(import\|include\|define\|if\|ifn\=def\|undef\|line\|error\|pragma\)\>]]
+
+ -- Whether we've seen a multiline comment leader
+ local saw_comment = false
+ for _, line in ipairs(getlines(bufnr, 1, 100)) do
+ if line:find('^%s*/%*') then
+ -- /* ... */ is a comment in Objective C and Murphi, so we can't conclude
+ -- it's either of them yet, but track this as a hint in case we don't see
+ -- anything more definitive.
+ saw_comment = true
+ end
+ if
+ line:find('^%s*//')
+ or matchregex(line, [[\c^\s*@import\>]])
+ or matchregex(line, objc_preprocessor)
+ then
+ return 'objc'
+ end
+ if
+ findany(line, { '^%s*#', '^%s*%%!' })
+ or matchregex(line, [[\c^\s*unwind_protect\>]])
+ or matchregex(line, [[\c\%(^\|;\)\s*]] .. octave_block_terminators)
+ then
+ return 'octave'
+ elseif line:find('^%s*%%') then
+ return 'matlab'
+ elseif line:find('^%s*%(%*') then
+ return 'mma'
+ elseif matchregex(line, [[\c^\s*\(\(type\|var\)\>\|--\)]]) then
+ return 'murphi'
+ end
+ end
+
+ if saw_comment then
+ -- We didn't see anything definitive, but this looks like either Objective C
+ -- or Murphi based on the comment leader. Assume the former as it is more
+ -- common.
+ return 'objc'
+ else
+ -- Default is Matlab
+ return 'matlab'
+ end
+end
+
+local function m4(contents)
+ for _, line in ipairs(contents) do
+ if matchregex(line, [[^\s*dnl\>]]) then
+ return 'm4'
+ end
+ end
+ if vim.env.TERM == 'amiga' and findany(contents[1]:lower(), { '^;', '^%.bra' }) then
+ -- AmigaDos scripts
+ return 'amiga'
+ end
+end
+
+-- Rely on the file to start with a comment.
+-- MS message text files use ';', Sendmail files use '#' or 'dnl'
+function M.mc(bufnr)
+ for _, line in ipairs(getlines(bufnr, 1, 20)) do
+ if findany(line:lower(), { '^%s*#', '^%s*dnl' }) then
+ -- Sendmail .mc file
+ return 'm4'
+ elseif line:find('^%s*;') then
+ return 'msmessages'
+ end
+ end
+ -- Default: Sendmail .mc file
+ return 'm4'
+end
+
+function M.me(path)
+ local filename = vim.fn.fnamemodify(path, ':t'):lower()
+ if filename ~= 'read.me' and filename ~= 'click.me' then
+ return 'nroff'
+ end
+end
+
+function M.mm(bufnr)
+ for _, line in ipairs(getlines(bufnr, 1, 20)) do
+ if matchregex(line, [[\c^\s*\(#\s*\(include\|import\)\>\|@import\>\|/\*\)]]) then
+ return 'objcpp'
+ end
+ end
+ return 'nroff'
+end
+
+function M.mms(bufnr)
+ for _, line in ipairs(getlines(bufnr, 1, 20)) do
+ if findany(line, { '^%s*%%', '^%s*//', '^%*' }) then
+ return 'mmix'
+ elseif line:find('^%s*#') then
+ return 'make'
+ end
+ end
+ return 'mmix'
+end
+
+-- Returns true if file content looks like LambdaProlog
+local function is_lprolog(bufnr)
+ -- Skip apparent comments and blank lines, what looks like
+ -- LambdaProlog comment may be RAPID header
+ for _, line in ipairs(getlines(bufnr)) do
+ -- The second pattern matches a LambdaProlog comment
+ if not findany(line, { '^%s*$', '^%s*%%' }) then
+ -- The pattern must not catch a go.mod file
+ return matchregex(line, [[\c\<module\s\+\w\+\s*\.\s*\(%\|$\)]]) ~= nil
+ end
+ end
+end
+
+-- Determine if *.mod is ABB RAPID, LambdaProlog, Modula-2, Modsim III or go.mod
+function M.mod(path, bufnr)
+ if vim.g.filetype_mod then
+ return vim.g.filetype_mod
+ elseif is_lprolog(bufnr) then
+ return 'lprolog'
+ elseif matchregex(nextnonblank(bufnr, 1), [[\%(\<MODULE\s\+\w\+\s*;\|^\s*(\*\)]]) then
+ return 'modula2'
+ elseif is_rapid(bufnr) then
+ return 'rapid'
+ elseif matchregex(path, [[\c\<go\.mod$]]) then
+ return 'gomod'
+ else
+ -- Nothing recognized, assume modsim3
+ return 'modsim3'
+ end
+end
+
+function M.news(bufnr)
+ if getlines(bufnr, 1):lower():find('; urgency=') then
+ return 'debchangelog'
+ end
+end
+
+-- This function checks if one of the first five lines start with a dot. In
+-- that case it is probably an nroff file.
+function M.nroff(bufnr)
+ for _, line in ipairs(getlines(bufnr, 1, 5)) do
+ if line:find('^%.') then
+ return 'nroff'
+ end
+ end
+end
+
+function M.patch(bufnr)
+ local firstline = getlines(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
+
+-- If the file has an extension of 't' and is in a directory 't' or 'xt' then
+-- it is almost certainly a Perl test file.
+-- If the first line starts with '#' and contains 'perl' it's probably a Perl file.
+-- (Slow test) If a file contains a 'use' statement then it is almost certainly a Perl file.
+function M.perl(path, bufnr)
+ local dir_name = vim.fs.dirname(path)
+ if vim.fn.expand(path, '%:e') == 't' and (dir_name == 't' or dir_name == 'xt') then
+ return 'perl'
+ end
+ local first_line = getlines(bufnr, 1)
+ if first_line:find('^#') and first_line:lower():find('perl') then
+ return 'perl'
+ end
+ for _, line in ipairs(getlines(bufnr, 1, 30)) do
+ if matchregex(line, [[\c^use\s\s*\k]]) then
+ return 'perl'
+ end
+ end
+end
+
+function M.pl(bufnr)
+ if vim.g.filetype_pl then
+ return vim.g.filetype_pl
+ end
+ -- Recognize Prolog by specific text in the first non-empty line;
+ -- require a blank after the '%' because Perl uses "%list" and "%translate"
+ local line = nextnonblank(bufnr, 1)
+ if
+ line and line:find(':%-')
+ or matchregex(line, [[\c\<prolog\>]])
+ or findany(line, { '^%s*%%+%s', '^%s*%%+$', '^%s*/%*' })
+ then
+ return 'prolog'
+ else
+ return 'perl'
+ end
+end
+
+function M.pm(bufnr)
+ local line = getlines(bufnr, 1)
+ if line:find('XPM2') then
+ return 'xpm2'
+ elseif line:find('XPM') then
+ return 'xpm'
+ else
+ return 'perl'
+ end
+end
+
+function M.pp(bufnr)
+ if vim.g.filetype_pp then
+ return vim.g.filetype_pp
+ end
+ local line = nextnonblank(bufnr, 1)
+ if findany(line, pascal_comments) or matchregex(line, pascal_keywords) then
+ return 'pascal'
+ else
+ return 'puppet'
+ end
+end
+
+function M.prg(bufnr)
+ if vim.g.filetype_prg then
+ return vim.g.filetype_prg
+ elseif is_rapid(bufnr) then
+ return 'rapid'
+ else
+ -- Nothing recognized, assume Clipper
+ return 'clipper'
+ end
+end
+
+function M.printcap(ptcap_type)
+ if vim.fn.did_filetype() == 0 then
+ return 'ptcap', function(bufnr)
+ vim.b[bufnr].ptcap_type = ptcap_type
+ end
+ end
+end
+
+-- This function checks for an assembly comment in the first ten lines.
+-- If not found, assume Progress.
+function M.progress_asm(bufnr)
+ if vim.g.filetype_i then
+ return vim.g.filetype_i
+ end
+
+ for _, line in ipairs(getlines(bufnr, 1, 10)) do
+ if line:find('^%s*;') or line:find('^/%*') then
+ return M.asm(bufnr)
+ elseif not line:find('^%s*$') or line:find('^/%*') then
+ -- Not an empty line: doesn't look like valid assembly code
+ -- or it looks like a Progress /* comment.
+ break
+ end
+ end
+ return 'progress'
+end
+
+function M.progress_cweb(bufnr)
+ if vim.g.filetype_w then
+ return vim.g.filetype_w
+ else
+ if
+ getlines(bufnr, 1):lower():find('^&analyze')
+ or getlines(bufnr, 3):lower():find('^&global%-define')
+ then
+ return 'progress'
+ else
+ return 'cweb'
+ end
+ end
+end
+
+-- This function checks for valid Pascal syntax in the first 10 lines.
+-- Look for either an opening comment or a program start.
+-- If not found, assume Progress.
+function M.progress_pascal(bufnr)
+ if vim.g.filetype_p then
+ return vim.g.filetype_p
+ end
+ for _, line in ipairs(getlines(bufnr, 1, 10)) do
+ if findany(line, pascal_comments) or matchregex(line, pascal_keywords) then
+ return 'pascal'
+ elseif not line:find('^%s*$') or line:find('^/%*') then
+ -- Not an empty line: Doesn't look like valid Pascal code.
+ -- Or it looks like a Progress /* comment
+ break
+ end
+ end
+ return 'progress'
+end
+
+-- Distinguish between "default" and Cproto prototype file.
+function M.proto(bufnr, default)
+ -- Cproto files have a comment in the first line and a function prototype in
+ -- the second line, it always ends in ";". Indent files may also have
+ -- comments, thus we can't match comments to see the difference.
+ -- IDL files can have a single ';' in the second line, require at least one
+ -- character before the ';'.
+ if getlines(bufnr, 2):find('.;$') then
+ return 'cpp'
+ else
+ return default
+ end
+end
+
+-- Software Distributor Product Specification File (POSIX 1387.2-1995)
+function M.psf(bufnr)
+ local line = getlines(bufnr, 1):lower()
+ if
+ findany(line, {
+ '^%s*distribution%s*$',
+ '^%s*installed_software%s*$',
+ '^%s*root%s*$',
+ '^%s*bundle%s*$',
+ '^%s*product%s*$',
+ })
+ then
+ return 'psf'
+ end
+end
+
+function M.r(bufnr)
+ local lines = getlines(bufnr, 1, 50)
+ -- Rebol is easy to recognize, check for that first
+ if matchregex(table.concat(lines), [[\c\<rebol\>]]) then
+ return 'rebol'
+ end
+
+ for _, line in ipairs(lines) do
+ -- R has # comments
+ if line:find('^%s*#') then
+ return 'r'
+ end
+ -- Rexx has /* comments */
+ if line:find('^%s*/%*') then
+ return 'rexx'
+ end
+ end
+
+ -- Nothing recognized, use user default or assume R
+ if vim.g.filetype_r then
+ return vim.g.filetype_r
+ else
+ -- Rexx used to be the default, but R appears to be much more popular.
+ return 'r'
+ end
+end
+
+function M.redif(bufnr)
+ for _, line in ipairs(getlines(bufnr, 1, 5)) do
+ if line:lower():find('^template%-type:') then
+ return 'redif'
+ end
+ end
+end
+
+function M.reg(bufnr)
+ local line = getlines(bufnr, 1):lower()
+ if
+ line:find('^regedit[0-9]*%s*$') or line:find('^windows registry editor version %d*%.%d*%s*$')
+ then
+ return 'registry'
+ end
+end
+
+-- Diva (with Skill) or InstallShield
+function M.rul(bufnr)
+ if table.concat(getlines(bufnr, 1, 6)):lower():find('installshield') then
+ return 'ishd'
+ end
+ return 'diva'
+end
+
+local udev_rules_pattern = '^%s*udev_rules%s*=%s*"([%^"]+)/*".*'
+function M.rules(path)
+ path = path:lower()
+ if
+ findany(path, {
+ '/etc/udev/.*%.rules$',
+ '/etc/udev/rules%.d/.*$.rules$',
+ '/usr/lib/udev/.*%.rules$',
+ '/usr/lib/udev/rules%.d/.*%.rules$',
+ '/lib/udev/.*%.rules$',
+ '/lib/udev/rules%.d/.*%.rules$',
+ })
+ then
+ return 'udevrules'
+ elseif path:find('^/etc/ufw/') then
+ -- Better than hog
+ return 'conf'
+ elseif findany(path, { '^/etc/polkit%-1/rules%.d', '/usr/share/polkit%-1/rules%.d' }) then
+ return 'javascript'
+ else
+ local ok, config_lines = pcall(vim.fn.readfile, '/etc/udev/udev.conf')
+ if not ok then
+ return 'hog'
+ end
+ local dir = vim.fn.expand(path, ':h')
+ for _, line in ipairs(config_lines) do
+ local match = line:match(udev_rules_pattern)
+ if match then
+ local udev_rules = line:gsub(udev_rules_pattern, match, 1)
+ if dir == udev_rules then
+ return 'udevrules'
+ end
+ end
+ end
+ return 'hog'
+ end
+end
+
+-- LambdaProlog and Standard ML signature files
+function M.sig(bufnr)
+ if vim.g.filetype_sig then
+ return vim.g.filetype_sig
+ end
+
+ local line = nextnonblank(bufnr, 1)
+
+ -- LambdaProlog comment or keyword
+ if findany(line, { '^%s*/%*', '^%s*%%', '^%s*sig%s+%a' }) then
+ return 'lprolog'
+ -- SML comment or keyword
+ elseif findany(line, { '^%s*%(%*', '^%s*signature%s+%a', '^%s*structure%s+%a' }) then
+ return 'sml'
+ end
+end
+
+-- This function checks the first 25 lines of file extension "sc" to resolve
+-- detection between scala and SuperCollider
+function M.sc(bufnr)
+ for _, line in ipairs(getlines(bufnr, 1, 25)) do
+ if
+ findany(line, {
+ '[A-Za-z0-9]*%s:%s[A-Za-z0-9]',
+ 'var%s<',
+ 'classvar%s<',
+ '%^this.*',
+ '|%w*|',
+ '%+%s%w*%s{',
+ '%*ar%s',
+ })
+ then
+ return 'supercollider'
+ end
+ end
+ return 'scala'
+end
+
+-- This function checks the first line of file extension "scd" to resolve
+-- detection between scdoc and SuperCollider
+function M.scd(bufnr)
+ local first = '^%S+%(%d[0-9A-Za-z]*%)'
+ local opt = [[%s+"[^"]*"]]
+ local line = getlines(bufnr, 1)
+ if findany(line, { first .. '$', first .. opt .. '$', first .. opt .. opt .. '$' }) then
+ return 'scdoc'
+ else
+ return 'supercollider'
+ end
+end
+
+function M.sgml(bufnr)
+ local lines = table.concat(getlines(bufnr, 1, 5))
+ if lines:find('linuxdoc') then
+ return 'smgllnx'
+ elseif lines:find('<!DOCTYPE.*DocBook') then
+ return 'docbk',
+ function(b)
+ vim.b[b].docbk_type = 'sgml'
+ vim.b[b].docbk_ver = 4
+ end
+ else
+ return 'sgml'
+ end
+end
+
+function M.sh(path, contents, name)
+ -- Path may be nil, do not fail in that case
+ if vim.fn.did_filetype() ~= 0 or (path or ''):find(vim.g.ft_ignore_pat) then
+ -- Filetype was already detected or detection should be skipped
+ return
+ end
+
+ local on_detect
+
+ -- Get the name from the first line if not specified
+ name = name or contents[1]
+ if matchregex(name, [[\<csh\>]]) then
+ -- Some .sh scripts contain #!/bin/csh.
+ return M.shell(path, contents, 'csh')
+ -- Some .sh scripts contain #!/bin/tcsh.
+ elseif matchregex(name, [[\<tcsh\>]]) then
+ return M.shell(path, contents, 'tcsh')
+ -- Some .sh scripts contain #!/bin/zsh.
+ elseif matchregex(name, [[\<zsh\>]]) then
+ return M.shell(path, contents, 'zsh')
+ elseif matchregex(name, [[\<ksh\>]]) then
+ on_detect = function(b)
+ vim.b[b].is_kornshell = 1
+ vim.b[b].is_bash = nil
+ vim.b[b].is_sh = nil
+ end
+ elseif vim.g.bash_is_sh or matchregex(name, [[\<bash\>]]) or matchregex(name, [[\<bash2\>]]) then
+ on_detect = function(b)
+ vim.b[b].is_bash = 1
+ vim.b[b].is_kornshell = nil
+ vim.b[b].is_sh = nil
+ end
+ elseif matchregex(name, [[\<sh\>]]) then
+ on_detect = function(b)
+ vim.b[b].is_sh = 1
+ vim.b[b].is_kornshell = nil
+ vim.b[b].is_bash = nil
+ end
+ end
+ return M.shell(path, contents, 'sh'), on_detect
+end
+
+-- For shell-like file types, check for an "exec" command hidden in a comment, as used for Tcl.
+function M.shell(path, contents, name)
+ if vim.fn.did_filetype() ~= 0 or matchregex(path, vim.g.ft_ignore_pat) then
+ -- Filetype was already detected or detection should be skipped
+ return
+ end
+
+ local prev_line = ''
+ for line_nr, line in ipairs(contents) do
+ -- Skip the first line
+ if line_nr ~= 1 then
+ line = line:lower()
+ if line:find('%s*exec%s') and not prev_line:find('^%s*#.*\\$') then
+ -- Found an "exec" line after a comment with continuation
+ local n = line:gsub('%s*exec%s+([^ ]*/)?', '', 1)
+ if matchregex(n, [[\c\<tclsh\|\<wish]]) then
+ return 'tcl'
+ end
+ end
+ prev_line = line
+ end
+ end
+ return name
+end
+
+-- SMIL or SNMP MIB file
+function M.smi(bufnr)
+ local line = getlines(bufnr, 1)
+ if matchregex(line, [[\c\<smil\>]]) then
+ return 'smil'
+ else
+ return 'mib'
+ end
+end
+
+-- Determine if a *.src file is Kuka Robot Language
+function M.src(bufnr)
+ if vim.g.filetype_src then
+ return vim.g.filetype_src
+ end
+ local line = nextnonblank(bufnr, 1)
+ if matchregex(line, [[\c\v^\s*%(\&\w+|%(global\s+)?def%(fct)?>)]]) then
+ return 'krl'
+ end
+end
+
+function M.sys(bufnr)
+ if vim.g.filetype_sys then
+ return vim.g.filetype_sys
+ elseif is_rapid(bufnr) then
+ return 'rapid'
+ else
+ return 'bat'
+ end
+end
+
+-- Choose context, plaintex, or tex (LaTeX) based on these rules:
+-- 1. Check the first line of the file for "%&<format>".
+-- 2. Check the first 1000 non-comment lines for LaTeX or ConTeXt keywords.
+-- 3. Default to "plain" or to g:tex_flavor, can be set in user's vimrc.
+function M.tex(path, bufnr)
+ local format = getlines(bufnr, 1):find('^%%&%s*(%a+)')
+ if format then
+ format = format:lower():gsub('pdf', '', 1)
+ if format == 'tex' then
+ return 'tex'
+ elseif format == 'plaintex' then
+ return 'plaintex'
+ end
+ elseif path:lower():find('tex/context/.*/.*%.tex') then
+ return 'context'
+ else
+ local lpat = [[documentclass\>\|usepackage\>\|begin{\|newcommand\>\|renewcommand\>]]
+ local cpat =
+ [[start\a\+\|setup\a\+\|usemodule\|enablemode\|enableregime\|setvariables\|useencoding\|usesymbols\|stelle\a\+\|verwende\a\+\|stel\a\+\|gebruik\a\+\|usa\a\+\|imposta\a\+\|regle\a\+\|utilisemodule\>]]
+
+ for i, l in ipairs(getlines(bufnr, 1, 1000)) do
+ -- Find first non-comment line
+ if not l:find('^%s*%%%S') then
+ -- Check the next thousand lines for a LaTeX or ConTeXt keyword.
+ for _, line in ipairs(getlines(bufnr, i + 1, i + 1000)) do
+ local lpat_match, cpat_match =
+ matchregex(line, [[\c^\s*\\\%(]] .. lpat .. [[\)\|^\s*\\\(]] .. cpat .. [[\)]])
+ if lpat_match then
+ return 'tex'
+ elseif cpat_match then
+ return 'context'
+ end
+ end
+ end
+ end
+ -- TODO: add AMSTeX, RevTex, others?
+ if not vim.g.tex_flavor or vim.g.tex_flavor == 'plain' then
+ return 'plaintex'
+ elseif vim.g.tex_flavor == 'context' then
+ return 'context'
+ else
+ -- Probably LaTeX
+ return 'tex'
+ end
+ end
+end
+
+-- Determine if a *.tf file is TF mud client or terraform
+function M.tf(bufnr)
+ for _, line in ipairs(getlines(bufnr)) do
+ -- Assume terraform file on a non-empty line (not whitespace-only)
+ -- and when the first non-whitespace character is not a ; or /
+ if not line:find('^%s*$') and not line:find('^%s*[;/]') then
+ return 'terraform'
+ end
+ end
+ return 'tf'
+end
+
+function M.ttl(bufnr)
+ local line = getlines(bufnr, 1):lower()
+ if line:find('^@?prefix') or line:find('^@?base') then
+ return 'turtle'
+ end
+ return 'teraterm'
+end
+
+function M.txt(bufnr)
+ -- helpfiles match *.txt, but should have a modeline as last line
+ if not getlines(bufnr, -1):find('vim:.*ft=help') then
+ return 'text'
+ end
+end
+
+-- WEB (*.web is also used for Winbatch: Guess, based on expecting "%" comment
+-- lines in a WEB file).
+function M.web(bufnr)
+ for _, line in ipairs(getlines(bufnr, 1, 5)) do
+ if line:find('^%%') then
+ return 'web'
+ end
+ end
+ return 'winbatch'
+end
+
+-- XFree86 config
+function M.xfree86()
+ return 'xf86conf',
+ function(bufnr)
+ local line = getlines(bufnr, 1)
+ if matchregex(line, [[\<XConfigurator\>]]) then
+ vim.b[bufnr].xf86conf_xfree86_version = 3
+ end
+ end
+end
+
+function M.xml(bufnr)
+ for _, line in ipairs(getlines(bufnr, 1, 100)) do
+ local is_docbook4 = line:find('<!DOCTYPE.*DocBook')
+ line = line:lower()
+ local is_docbook5 = line:find([[ xmlns="http://docbook.org/ns/docbook"]])
+ if is_docbook4 or is_docbook5 then
+ return 'docbk',
+ function(b)
+ vim.b[b].docbk_type = 'xml'
+ vim.b[b].docbk_ver = is_docbook4 and 4 or 5
+ end
+ end
+ if line:find([[xmlns:xbl="http://www.mozilla.org/xbl"]]) then
+ return 'xbl'
+ end
+ end
+ return 'xml'
+end
+
+function M.y(bufnr)
+ for _, line in ipairs(getlines(bufnr, 1, 100)) do
+ if line:find('^%s*%%') then
+ return 'yacc'
+ end
+ if matchregex(line, [[\c^\s*\(#\|class\>\)]]) and not line:lower():find('^%s*#%s*include') then
+ return 'racc'
+ end
+ end
+ return 'yacc'
+end
+
+-- luacheck: pop
+-- luacheck: pop
+
+local patterns_hashbang = {
+ ['^zsh\\>'] = { 'zsh', { vim_regex = true } },
+ ['^\\(tclsh\\|wish\\|expectk\\|itclsh\\|itkwish\\)\\>'] = { 'tcl', { vim_regex = true } },
+ ['^expect\\>'] = { 'expect', { vim_regex = true } },
+ ['^gnuplot\\>'] = { 'gnuplot', { vim_regex = true } },
+ ['make\\>'] = { 'make', { vim_regex = true } },
+ ['^pike\\%(\\>\\|[0-9]\\)'] = { 'pike', { vim_regex = true } },
+ lua = 'lua',
+ perl = 'perl',
+ php = 'php',
+ python = 'python',
+ ['^groovy\\>'] = { 'groovy', { vim_regex = true } },
+ raku = 'raku',
+ ruby = 'ruby',
+ ['node\\(js\\)\\=\\>\\|js\\>'] = { 'javascript', { vim_regex = true } },
+ ['rhino\\>'] = { 'javascript', { vim_regex = true } },
+ -- BC calculator
+ ['^bc\\>'] = { 'bc', { vim_regex = true } },
+ ['sed\\>'] = { 'sed', { vim_regex = true } },
+ ocaml = 'ocaml',
+ -- Awk scripts; also finds "gawk"
+ ['awk\\>'] = { 'awk', { vim_regex = true } },
+ wml = 'wml',
+ scheme = 'scheme',
+ cfengine = 'cfengine',
+ escript = 'erlang',
+ haskell = 'haskell',
+ clojure = 'clojure',
+ ['scala\\>'] = { 'scala', { vim_regex = true } },
+ -- Free Pascal
+ ['instantfpc\\>'] = { 'pascal', { vim_regex = true } },
+ ['fennel\\>'] = { 'fennel', { vim_regex = true } },
+ -- MikroTik RouterOS script
+ ['rsc\\>'] = { 'routeros', { vim_regex = true } },
+ ['fish\\>'] = { 'fish', { vim_regex = true } },
+ ['gforth\\>'] = { 'forth', { vim_regex = true } },
+ ['icon\\>'] = { 'icon', { vim_regex = true } },
+ guile = 'scheme',
+}
+
+---@private
+-- File starts with "#!".
+local function match_from_hashbang(contents, path)
+ local first_line = contents[1]
+ -- Check for a line like "#!/usr/bin/env {options} bash". Turn it into
+ -- "#!/usr/bin/bash" to make matching easier.
+ -- Recognize only a few {options} that are commonly used.
+ if matchregex(first_line, [[^#!\s*\S*\<env\s]]) then
+ first_line = first_line:gsub('%S+=%S+', '')
+ first_line = first_line
+ :gsub('%-%-ignore%-environment', '', 1)
+ :gsub('%-%-split%-string', '', 1)
+ :gsub('%-[iS]', '', 1)
+ first_line = vim.fn.substitute(first_line, [[\<env\s\+]], '', '')
+ end
+
+ -- Get the program name.
+ -- Only accept spaces in PC style paths: "#!c:/program files/perl [args]".
+ -- If the word env is used, use the first word after the space:
+ -- "#!/usr/bin/env perl [path/args]"
+ -- If there is no path use the first word: "#!perl [path/args]".
+ -- Otherwise get the last word after a slash: "#!/usr/bin/perl [path/args]".
+ local name
+ if first_line:find('^#!%s*%a:[/\\]') then
+ name = vim.fn.substitute(first_line, [[^#!.*[/\\]\(\i\+\).*]], '\\1', '')
+ elseif matchregex(first_line, [[^#!.*\<env\>]]) then
+ name = vim.fn.substitute(first_line, [[^#!.*\<env\>\s\+\(\i\+\).*]], '\\1', '')
+ elseif matchregex(first_line, [[^#!\s*[^/\\ ]*\>\([^/\\]\|$\)]]) then
+ name = vim.fn.substitute(first_line, [[^#!\s*\([^/\\ ]*\>\).*]], '\\1', '')
+ else
+ name = vim.fn.substitute(first_line, [[^#!\s*\S*[/\\]\(\i\+\).*]], '\\1', '')
+ end
+
+ -- tcl scripts may have #!/bin/sh in the first line and "exec wish" in the
+ -- third line. Suggested by Steven Atkinson.
+ if contents[3] and contents[3]:find('^exec wish') then
+ name = 'wish'
+ end
+
+ if matchregex(name, [[^\(bash\d*\|\|ksh\d*\|sh\)\>]]) then
+ -- Bourne-like shell scripts: bash bash2 ksh ksh93 sh
+ return require('vim.filetype.detect').sh(path, contents, first_line)
+ elseif matchregex(name, [[^csh\>]]) then
+ return require('vim.filetype.detect').shell(path, contents, vim.g.filetype_csh or 'csh')
+ elseif matchregex(name, [[^tcsh\>]]) then
+ return require('vim.filetype.detect').shell(path, contents, 'tcsh')
+ end
+
+ for k, v in pairs(patterns_hashbang) do
+ local ft = type(v) == 'table' and v[1] or v
+ local opts = type(v) == 'table' and v[2] or {}
+ if opts.vim_regex and matchregex(name, k) or name:find(k) then
+ return ft
+ end
+ end
+end
+
+local patterns_text = {
+ ['^#compdef\\>'] = { 'zsh', { vim_regex = true } },
+ ['^#autoload\\>'] = { 'zsh', { vim_regex = true } },
+ -- ELM Mail files
+ ['^From [a-zA-Z][a-zA-Z_0-9%.=%-]*(@[^ ]*)? .* 19%d%d$'] = 'mail',
+ ['^From [a-zA-Z][a-zA-Z_0-9%.=%-]*(@[^ ]*)? .* 20%d%d$'] = 'mail',
+ ['^From %- .* 19%d%d$'] = 'mail',
+ ['^From %- .* 20%d%d$'] = 'mail',
+ -- Mason
+ ['^<[%%&].*>'] = 'mason',
+ -- Vim scripts (must have '" vim' as the first line to trigger this)
+ ['^" *[vV]im$['] = 'vim',
+ -- libcxx and libstdc++ standard library headers like ["iostream["] do not have
+ -- an extension, recognize the Emacs file mode.
+ ['%-%*%-.*[cC]%+%+.*%-%*%-'] = 'cpp',
+ ['^\\*\\* LambdaMOO Database, Format Version \\%([1-3]\\>\\)\\@!\\d\\+ \\*\\*$'] = {
+ 'moo',
+ { vim_regex = true },
+ },
+ -- Diff file:
+ -- - "diff" in first line (context diff)
+ -- - "Only in " in first line
+ -- - "--- " in first line and "+++ " in second line (unified diff).
+ -- - "*** " in first line and "--- " in second line (context diff).
+ -- - "# It was generated by makepatch " in the second line (makepatch diff).
+ -- - "Index: <filename>" in the first line (CVS file)
+ -- - "=== ", line of "=", "---", "+++ " (SVK diff)
+ -- - "=== ", "--- ", "+++ " (bzr diff, common case)
+ -- - "=== (removed|added|renamed|modified)" (bzr diff, alternative)
+ -- - "# HG changeset patch" in first line (Mercurial export format)
+ ['^\\(diff\\>\\|Only in \\|\\d\\+\\(,\\d\\+\\)\\=[cda]\\d\\+\\>\\|# It was generated by makepatch \\|Index:\\s\\+\\f\\+\\r\\=$\\|===== \\f\\+ \\d\\+\\.\\d\\+ vs edited\\|==== //\\f\\+#\\d\\+\\|# HG changeset patch\\)'] = {
+ 'diff',
+ { vim_regex = true },
+ },
+ function(contents)
+ return diff(contents)
+ end,
+ -- PostScript Files (must have %!PS as the first line, like a2ps output)
+ ['^%%![ \t]*PS'] = 'postscr',
+ function(contents)
+ return m4(contents)
+ end,
+ -- SiCAD scripts (must have procn or procd as the first line to trigger this)
+ ['^ *proc[nd] *$'] = { 'sicad', { ignore_case = true } },
+ ['^%*%*%*%* Purify'] = 'purifylog',
+ -- XML
+ ['<%?%s*xml.*%?>'] = 'xml',
+ -- XHTML (e.g.: PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN")
+ ['\\<DTD\\s\\+XHTML\\s'] = 'xhtml',
+ -- HTML (e.g.: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN")
+ -- Avoid "doctype html", used by slim.
+ ['\\c<!DOCTYPE\\s\\+html\\>'] = { 'html', { vim_regex = true } },
+ -- PDF
+ ['^%%PDF%-'] = 'pdf',
+ -- XXD output
+ ['^%x%x%x%x%x%x%x: %x%x ?%x%x ?%x%x ?%x%x '] = 'xxd',
+ -- RCS/CVS log output
+ ['^RCS file:'] = { 'rcslog', { start_lnum = 1, end_lnum = 2 } },
+ -- CVS commit
+ ['^CVS:'] = { 'cvs', { start_lnum = 2 } },
+ ['^CVS: '] = { 'cvs', { start_lnum = -1 } },
+ -- Prescribe
+ ['^!R!'] = 'prescribe',
+ -- Send-pr
+ ['^SEND%-PR:'] = 'sendpr',
+ -- SNNS files
+ ['^SNNS network definition file'] = 'snnsnet',
+ ['^SNNS pattern definition file'] = 'snnspat',
+ ['^SNNS result file'] = 'snnsres',
+ ['^%%.-[Vv]irata'] = { 'virata', { start_lnum = 1, end_lnum = 5 } },
+ ['[0-9:%.]* *execve%('] = 'strace',
+ ['^__libc_start_main'] = 'strace',
+ -- VSE JCL
+ ['^\\* $$ JOB\\>'] = { 'vsejcl', { vim_regex = true } },
+ ['^// *JOB\\>'] = { 'vsejcl', { vim_regex = true } },
+ -- TAK and SINDA
+ ['K & K Associates'] = { 'takout', { start_lnum = 4 } },
+ ['TAK 2000'] = { 'takout', { start_lnum = 2 } },
+ ['S Y S T E M S I M P R O V E D '] = { 'syndaout', { start_lnum = 3 } },
+ ['Run Date: '] = { 'takcmp', { start_lnum = 6 } },
+ ['Node File 1'] = { 'sindacmp', { start_lnum = 9 } },
+ function(contents)
+ require('vim.filetype.detect').dns_zone(contents)
+ end,
+ -- Valgrind
+ ['^==%d+== valgrind'] = 'valgrind',
+ ['^==%d+== Using valgrind'] = { 'valgrind', { start_lnum = 3 } },
+ -- Go docs
+ ['PACKAGE DOCUMENTATION$'] = 'godoc',
+ -- Renderman Interface Bytestream
+ ['^##RenderMan'] = 'rib',
+ -- Scheme scripts
+ ['exec%s%+%S*scheme'] = { 'scheme', { start_lnum = 1, end_lnum = 2 } },
+ -- Git output
+ ['^\\(commit\\|tree\\|object\\) \\x\\{40,\\}\\>\\|^tag \\S\\+$'] = {
+ 'git',
+ { vim_regex = true },
+ },
+ function(lines)
+ -- Gprof (gnu profiler)
+ if
+ lines[1] == 'Flat profile:'
+ and lines[2] == ''
+ and lines[3]:find('^Each sample counts as .* seconds%.$')
+ then
+ return 'gprof'
+ end
+ end,
+ -- Erlang terms
+ -- (See also: http://www.gnu.org/software/emacs/manual/html_node/emacs/Choosing-Modes.html#Choosing-Modes)
+ ['%-%*%-.*erlang.*%-%*%-'] = { 'erlang', { ignore_case = true } },
+ -- YAML
+ ['^%%YAML'] = 'yaml',
+ -- MikroTik RouterOS script
+ ['^#.*by RouterOS'] = 'routeros',
+ -- Sed scripts
+ -- #ncomment is allowed but most likely a false positive so require a space before any trailing comment text
+ ['^#n%s'] = 'sed',
+ ['^#n$'] = 'sed',
+}
+
+---@private
+-- File does not start with "#!".
+local function match_from_text(contents, path)
+ if contents[1]:find('^:$') then
+ -- Bourne-like shell scripts: sh ksh bash bash2
+ return M.sh(path, contents)
+ elseif
+ matchregex(
+ '\n' .. table.concat(contents, '\n'),
+ [[\n\s*emulate\s\+\%(-[LR]\s\+\)\=[ckz]\=sh\>]]
+ )
+ then
+ -- Z shell scripts
+ return 'zsh'
+ end
+
+ for k, v in pairs(patterns_text) do
+ if type(v) == 'string' then
+ -- Check the first line only
+ if contents[1]:find(k) then
+ return v
+ end
+ elseif type(v) == 'function' then
+ -- If filetype detection fails, continue with the next pattern
+ local ok, ft = pcall(v, contents)
+ if ok and ft then
+ return ft
+ end
+ else
+ local opts = type(v) == 'table' and v[2] or {}
+ if opts.start_lnum and opts.end_lnum then
+ assert(
+ not opts.ignore_case,
+ 'ignore_case=true is ignored when start_lnum is also present, needs refactor'
+ )
+ for i = opts.start_lnum, opts.end_lnum do
+ if not contents[i] then
+ break
+ elseif contents[i]:find(k) then
+ return v[1]
+ end
+ end
+ else
+ local line_nr = opts.start_lnum == -1 and #contents or opts.start_lnum or 1
+ if contents[line_nr] then
+ local line = opts.ignore_case and contents[line_nr]:lower() or contents[line_nr]
+ if opts.vim_regex and matchregex(line, k) or line:find(k) then
+ return v[1]
+ end
+ end
+ end
+ end
+ end
+ return cvs_diff(path, contents)
+end
+
+M.match_contents = function(contents, path)
+ local first_line = contents[1]
+ if first_line:find('^#!') then
+ return match_from_hashbang(contents, path)
+ else
+ return match_from_text(contents, path)
+ end
+end
+
+return M
diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua
new file mode 100644
index 0000000000..ce845eda15
--- /dev/null
+++ b/runtime/lua/vim/fs.lua
@@ -0,0 +1,209 @@
+local M = {}
+
+--- Iterate over all the parents of the given file or directory.
+---
+--- Example:
+--- <pre>
+--- local root_dir
+--- for dir in vim.fs.parents(vim.api.nvim_buf_get_name(0)) do
+--- if vim.fn.isdirectory(dir .. "/.git") == 1 then
+--- root_dir = dir
+--- break
+--- end
+--- end
+---
+--- if root_dir then
+--- print("Found git repository at", root_dir)
+--- end
+--- </pre>
+---
+---@param start (string) Initial file or directory.
+---@return (function) Iterator
+function M.parents(start)
+ return function(_, dir)
+ local parent = M.dirname(dir)
+ if parent == dir then
+ return nil
+ end
+
+ return parent
+ end,
+ nil,
+ start
+end
+
+--- Return the parent directory of the given file or directory
+---
+---@param file (string) File or directory
+---@return (string) Parent directory of {file}
+function M.dirname(file)
+ if file == nil then
+ return nil
+ end
+ return vim.fn.fnamemodify(file, ':h')
+end
+
+--- Return the basename of the given file or directory
+---
+---@param file (string) File or directory
+---@return (string) Basename of {file}
+function M.basename(file)
+ return vim.fn.fnamemodify(file, ':t')
+end
+
+--- Return an iterator over the files and directories located in {path}
+---
+---@param path (string) An absolute or relative path to the directory to iterate
+--- over. The path is first normalized |vim.fs.normalize()|.
+---@return Iterator over files and directories in {path}. Each iteration yields
+--- two values: name and type. Each "name" is the basename of the file or
+--- directory relative to {path}. Type is one of "file" or "directory".
+function M.dir(path)
+ return function(fs)
+ return vim.loop.fs_scandir_next(fs)
+ end,
+ vim.loop.fs_scandir(M.normalize(path))
+end
+
+--- Find files or directories in the given path.
+---
+--- Finds any files or directories given in {names} starting from {path}. If
+--- {upward} is "true" then the search traverses upward through parent
+--- directories; otherwise, the search traverses downward. Note that downward
+--- searches are recursive and may search through many directories! If {stop}
+--- is non-nil, then the search stops when the directory given in {stop} is
+--- reached. The search terminates when {limit} (default 1) matches are found.
+--- The search can be narrowed to find only files or or only directories by
+--- specifying {type} to be "file" or "directory", respectively.
+---
+---@param names (string|table) Names of the files and directories to find. Must
+--- be base names, paths and globs are not supported.
+---@param opts (table) Optional keyword arguments:
+--- - path (string): Path to begin searching from. If
+--- omitted, the current working directory is used.
+--- - upward (boolean, default false): If true, search
+--- upward through parent directories. Otherwise,
+--- search through child directories
+--- (recursively).
+--- - stop (string): Stop searching when this directory is
+--- reached. The directory itself is not searched.
+--- - type (string): Find only files ("file") or
+--- directories ("directory"). If omitted, both
+--- files and directories that match {name} are
+--- included.
+--- - limit (number, default 1): Stop the search after
+--- finding this many matches. Use `math.huge` to
+--- place no limit on the number of matches.
+---@return (table) The paths of all matching files or directories
+function M.find(names, opts)
+ opts = opts or {}
+ vim.validate({
+ names = { names, { 's', 't' } },
+ path = { opts.path, 's', true },
+ upward = { opts.upward, 'b', true },
+ stop = { opts.stop, 's', true },
+ type = { opts.type, 's', true },
+ limit = { opts.limit, 'n', true },
+ })
+
+ names = type(names) == 'string' and { names } or names
+
+ local path = opts.path or vim.loop.cwd()
+ local stop = opts.stop
+ local limit = opts.limit or 1
+
+ local matches = {}
+
+ ---@private
+ local function add(match)
+ matches[#matches + 1] = match
+ if #matches == limit then
+ return true
+ end
+ end
+
+ if opts.upward then
+ ---@private
+ local function test(p)
+ local t = {}
+ for _, name in ipairs(names) do
+ local f = p .. '/' .. name
+ local stat = vim.loop.fs_stat(f)
+ if stat and (not opts.type or opts.type == stat.type) then
+ t[#t + 1] = f
+ end
+ end
+
+ return t
+ end
+
+ for _, match in ipairs(test(path)) do
+ if add(match) then
+ return matches
+ end
+ end
+
+ for parent in M.parents(path) do
+ if stop and parent == stop then
+ break
+ end
+
+ for _, match in ipairs(test(parent)) do
+ if add(match) then
+ return matches
+ end
+ end
+ end
+ else
+ local dirs = { path }
+ while #dirs > 0 do
+ local dir = table.remove(dirs, 1)
+ if stop and dir == stop then
+ break
+ end
+
+ for other, type in M.dir(dir) do
+ local f = dir .. '/' .. other
+ for _, name in ipairs(names) do
+ if name == other and (not opts.type or opts.type == type) then
+ if add(f) then
+ return matches
+ end
+ end
+ end
+
+ if type == 'directory' then
+ dirs[#dirs + 1] = f
+ end
+ end
+ end
+ end
+
+ return matches
+end
+
+--- Normalize a path to a standard format. A tilde (~) character at the
+--- beginning of the path is expanded to the user's home directory and any
+--- backslash (\\) characters are converted to forward slashes (/). Environment
+--- variables are also expanded.
+---
+--- Example:
+--- <pre>
+--- vim.fs.normalize('C:\\Users\\jdoe')
+--- => 'C:/Users/jdoe'
+---
+--- vim.fs.normalize('~/src/neovim')
+--- => '/home/jdoe/src/neovim'
+---
+--- vim.fs.normalize('$XDG_CONFIG_HOME/nvim/init.vim')
+--- => '/Users/jdoe/.config/nvim/init.vim'
+--- </pre>
+---
+---@param path (string) Path to normalize
+---@return (string) Normalized path
+function M.normalize(path)
+ vim.validate({ path = { path, 's' } })
+ return (path:gsub('^~/', vim.env.HOME .. '/'):gsub('%$([%w_]+)', vim.env):gsub('\\', '/'))
+end
+
+return M
diff --git a/runtime/lua/vim/health.lua b/runtime/lua/vim/health.lua
new file mode 100644
index 0000000000..b875da0abc
--- /dev/null
+++ b/runtime/lua/vim/health.lua
@@ -0,0 +1,49 @@
+local M = {}
+
+function M.report_start(msg)
+ vim.fn['health#report_start'](msg)
+end
+
+function M.report_info(msg)
+ vim.fn['health#report_info'](msg)
+end
+
+function M.report_ok(msg)
+ vim.fn['health#report_ok'](msg)
+end
+
+function M.report_warn(msg, ...)
+ vim.fn['health#report_warn'](msg, ...)
+end
+
+function M.report_error(msg, ...)
+ vim.fn['health#report_error'](msg, ...)
+end
+
+local path2name = function(path)
+ if path:match('%.lua$') then
+ -- Lua: transform "../lua/vim/lsp/health.lua" into "vim.lsp"
+ return path:gsub('.-lua[%\\%/]', '', 1):gsub('[%\\%/]', '.'):gsub('%.health.-$', '')
+ else
+ -- Vim: transform "../autoload/health/provider.vim" into "provider"
+ return vim.fn.fnamemodify(path, ':t:r')
+ end
+end
+
+local PATTERNS = { '/autoload/health/*.vim', '/lua/**/**/health.lua', '/lua/**/**/health/init.lua' }
+-- :checkhealth completion function used by ex_getln.c get_healthcheck_names()
+M._complete = function()
+ local names = vim.tbl_flatten(vim.tbl_map(function(pattern)
+ return vim.tbl_map(path2name, vim.api.nvim_get_runtime_file(pattern, true))
+ end, PATTERNS))
+ -- Remove duplicates
+ local unique = {}
+ vim.tbl_map(function(f)
+ unique[f] = true
+ end, names)
+ -- vim.health is this file, which is not a healthcheck
+ unique['vim'] = nil
+ return vim.tbl_keys(unique)
+end
+
+return M
diff --git a/runtime/lua/vim/highlight.lua b/runtime/lua/vim/highlight.lua
index 236f3165f2..ddd504a0e0 100644
--- a/runtime/lua/vim/highlight.lua
+++ b/runtime/lua/vim/highlight.lua
@@ -1,45 +1,79 @@
local api = vim.api
-local highlight = {}
+local M = {}
+
+M.priorities = {
+ syntax = 50,
+ treesitter = 100,
+ diagnostics = 150,
+ user = 200,
+}
---@private
-function highlight.create(higroup, hi_info, default)
+function M.create(higroup, hi_info, default)
+ vim.deprecate('vim.highlight.create', 'vim.api.nvim_set_hl', '0.9')
local options = {}
-- TODO: Add validation
for k, v in pairs(hi_info) do
- table.insert(options, string.format("%s=%s", k, v))
+ table.insert(options, string.format('%s=%s', k, v))
end
- vim.cmd(string.format([[highlight %s %s %s]], default and "default" or "", higroup, table.concat(options, " ")))
+ vim.cmd(
+ string.format(
+ [[highlight %s %s %s]],
+ default and 'default' or '',
+ higroup,
+ table.concat(options, ' ')
+ )
+ )
end
---@private
-function highlight.link(higroup, link_to, force)
- vim.cmd(string.format([[highlight%s link %s %s]], force and "!" or " default", higroup, link_to))
+function M.link(higroup, link_to, force)
+ vim.deprecate('vim.highlight.link', 'vim.api.nvim_set_hl', '0.9')
+ vim.cmd(string.format([[highlight%s link %s %s]], force and '!' or ' default', higroup, link_to))
end
-
--- Highlight range between two positions
---
---@param bufnr number of buffer to apply highlighting to
---@param ns namespace to add highlight to
---@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)
- rtype = rtype or 'v'
- inclusive = inclusive or false
+---@param start first position (tuple {line,col})
+---@param finish second position (tuple {line,col})
+---@param opts table with options:
+-- - regtype type of range (:help setreg, default charwise)
+-- - inclusive boolean indicating whether the range is end-inclusive (default false)
+-- - priority number indicating priority of highlight (default priorities.user)
+function M.range(bufnr, ns, higroup, start, finish, opts)
+ opts = opts or {}
+ local regtype = opts.regtype or 'v'
+ local inclusive = opts.inclusive or false
+ local priority = opts.priority or M.priorities.user
-- sanity check
- if start[2] < 0 or finish[1] < start[1] then return end
+ if start[2] < 0 or finish[1] < start[1] then
+ return
+ end
- local region = vim.region(bufnr, start, finish, rtype, inclusive)
+ local region = vim.region(bufnr, start, finish, regtype, 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
local yank_ns = api.nvim_create_namespace('hlyank')
+local yank_timer
--- Highlight the yanked region
---
--- use from init.vim via
@@ -49,49 +83,71 @@ local yank_ns = api.nvim_create_namespace('hlyank')
--- customize conditions (here: do not highlight a visual selection) via
--- au TextYankPost * lua vim.highlight.on_yank {on_visual=false}
---
--- @param opts dictionary with options controlling the highlight:
+-- @param opts table with options controlling the highlight:
-- - higroup highlight group for yanked region (default "IncSearch")
-- - timeout time in ms before highlight is cleared (default 150)
-- - on_macro highlight when executing macro (default false)
-- - on_visual highlight when yanking visual selection (default true)
-- - event event structure (default vim.v.event)
-function highlight.on_yank(opts)
- vim.validate {
- opts = { opts,
- function(t) if t == nil then return true else return type(t) == 'table' end end,
- 'a table or nil to configure options (see `:h highlight.on_yank`)',
- }}
+function M.on_yank(opts)
+ vim.validate({
+ opts = {
+ opts,
+ function(t)
+ if t == nil then
+ return true
+ else
+ return type(t) == 'table'
+ end
+ end,
+ 'a table or nil to configure options (see `:h highlight.on_yank`)',
+ },
+ })
opts = opts or {}
local event = opts.event or vim.v.event
local on_macro = opts.on_macro or false
local on_visual = (opts.on_visual ~= false)
- if (not on_macro) and vim.fn.reg_executing() ~= '' then return end
- if event.operator ~= 'y' or event.regtype == '' then return end
- if (not on_visual) and event.visual then return end
+ if not on_macro and vim.fn.reg_executing() ~= '' then
+ return
+ end
+ if event.operator ~= 'y' or event.regtype == '' then
+ return
+ end
+ if not on_visual and event.visual then
+ return
+ end
- local higroup = opts.higroup or "IncSearch"
+ local higroup = opts.higroup or 'IncSearch'
local timeout = opts.timeout or 150
local bufnr = api.nvim_get_current_buf()
api.nvim_buf_clear_namespace(bufnr, yank_ns, 0, -1)
+ if yank_timer then
+ yank_timer:close()
+ end
local pos1 = vim.fn.getpos("'[")
local pos2 = vim.fn.getpos("']")
- 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)
+ pos1 = { pos1[2] - 1, pos1[3] - 1 + pos1[4] }
+ pos2 = { pos2[2] - 1, pos2[3] - 1 + pos2[4] }
- vim.defer_fn(
- function()
- if api.nvim_buf_is_valid(bufnr) then
- api.nvim_buf_clear_namespace(bufnr, yank_ns, 0, -1)
- end
- end,
- timeout
+ M.range(
+ bufnr,
+ yank_ns,
+ higroup,
+ pos1,
+ pos2,
+ { regtype = event.regtype, inclusive = event.inclusive, priority = M.priorities.user }
)
+
+ yank_timer = vim.defer_fn(function()
+ yank_timer = nil
+ if api.nvim_buf_is_valid(bufnr) then
+ api.nvim_buf_clear_namespace(bufnr, yank_ns, 0, -1)
+ end
+ end, timeout)
end
-return highlight
+return M
diff --git a/runtime/lua/vim/inspect.lua b/runtime/lua/vim/inspect.lua
index 0448ea487f..0a53fb203b 100644
--- a/runtime/lua/vim/inspect.lua
+++ b/runtime/lua/vim/inspect.lua
@@ -1,6 +1,6 @@
-local inspect ={
+local inspect = {
_VERSION = 'inspect.lua 3.1.0',
- _URL = 'http://github.com/kikito/inspect.lua',
+ _URL = 'http://github.com/kikito/inspect.lua',
_DESCRIPTION = 'human-readable representations of tables',
_LICENSE = [[
MIT LICENSE
@@ -25,13 +25,26 @@ local inspect ={
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- ]]
+ ]],
}
-local tostring = tostring
+inspect.KEY = setmetatable({}, {
+ __tostring = function()
+ return 'inspect.KEY'
+ end,
+})
+inspect.METATABLE = setmetatable({}, {
+ __tostring = function()
+ return 'inspect.METATABLE'
+ end,
+})
-inspect.KEY = setmetatable({}, {__tostring = function() return 'inspect.KEY' end})
-inspect.METATABLE = setmetatable({}, {__tostring = function() return 'inspect.METATABLE' end})
+local tostring = tostring
+local rep = string.rep
+local match = string.match
+local char = string.char
+local gsub = string.gsub
+local fmt = string.format
local function rawpairs(t)
return next, t, nil
@@ -40,127 +53,130 @@ end
-- Apostrophizes the string if it has quotes, but not aphostrophes
-- Otherwise, it returns a regular quoted string
local function smartQuote(str)
- if str:match('"') and not str:match("'") then
+ if match(str, '"') and not match(str, "'") then
return "'" .. str .. "'"
end
- return '"' .. str:gsub('"', '\\"') .. '"'
+ return '"' .. gsub(str, '"', '\\"') .. '"'
end
-- \a => '\\a', \0 => '\\0', 31 => '\31'
local shortControlCharEscapes = {
- ["\a"] = "\\a", ["\b"] = "\\b", ["\f"] = "\\f", ["\n"] = "\\n",
- ["\r"] = "\\r", ["\t"] = "\\t", ["\v"] = "\\v"
+ ['\a'] = '\\a',
+ ['\b'] = '\\b',
+ ['\f'] = '\\f',
+ ['\n'] = '\\n',
+ ['\r'] = '\\r',
+ ['\t'] = '\\t',
+ ['\v'] = '\\v',
+ ['\127'] = '\\127',
}
-local longControlCharEscapes = {} -- \a => nil, \0 => \000, 31 => \031
-for i=0, 31 do
- local ch = string.char(i)
+local longControlCharEscapes = { ['\127'] = '\127' }
+for i = 0, 31 do
+ local ch = char(i)
if not shortControlCharEscapes[ch] then
- shortControlCharEscapes[ch] = "\\"..i
- longControlCharEscapes[ch] = string.format("\\%03d", i)
+ shortControlCharEscapes[ch] = '\\' .. i
+ longControlCharEscapes[ch] = fmt('\\%03d', i)
end
end
local function escape(str)
- return (str:gsub("\\", "\\\\")
- :gsub("(%c)%f[0-9]", longControlCharEscapes)
- :gsub("%c", shortControlCharEscapes))
+ return (
+ gsub(
+ gsub(gsub(str, '\\', '\\\\'), '(%c)%f[0-9]', longControlCharEscapes),
+ '%c',
+ shortControlCharEscapes
+ )
+ )
end
local function isIdentifier(str)
- return type(str) == 'string' and str:match( "^[_%a][_%a%d]*$" )
+ return type(str) == 'string' and not not str:match('^[_%a][_%a%d]*$')
end
+local flr = math.floor
local function isSequenceKey(k, sequenceLength)
- return type(k) == 'number'
- and 1 <= k
- and k <= sequenceLength
- and math.floor(k) == k
+ return type(k) == 'number' and flr(k) == k and 1 <= k and k <= sequenceLength
end
local defaultTypeOrders = {
- ['number'] = 1, ['boolean'] = 2, ['string'] = 3, ['table'] = 4,
- ['function'] = 5, ['userdata'] = 6, ['thread'] = 7
+ ['number'] = 1,
+ ['boolean'] = 2,
+ ['string'] = 3,
+ ['table'] = 4,
+ ['function'] = 5,
+ ['userdata'] = 6,
+ ['thread'] = 7,
}
local function sortKeys(a, b)
local ta, tb = type(a), type(b)
-- strings and numbers are sorted numerically/alphabetically
- if ta == tb and (ta == 'string' or ta == 'number') then return a < b end
+ if ta == tb and (ta == 'string' or ta == 'number') then
+ return a < b
+ end
- local dta, dtb = defaultTypeOrders[ta], defaultTypeOrders[tb]
+ local dta = defaultTypeOrders[ta] or 100
+ local dtb = defaultTypeOrders[tb] or 100
-- Two default types are compared according to the defaultTypeOrders table
- if dta and dtb then return defaultTypeOrders[ta] < defaultTypeOrders[tb]
- elseif dta then return true -- default types before custom ones
- elseif dtb then return false -- custom types after default ones
- end
-- custom types are sorted out alphabetically
- return ta < tb
+ return dta == dtb and ta < tb or dta < dtb
end
--- For implementation reasons, the behavior of rawlen & # is "undefined" when
--- tables aren't pure sequences. So we implement our own # operator.
-local function getSequenceLength(t)
- local len = 1
- local v = rawget(t,len)
- while v ~= nil do
- len = len + 1
- v = rawget(t,len)
+local function getKeys(t)
+ local seqLen = 1
+ while rawget(t, seqLen) ~= nil do
+ seqLen = seqLen + 1
end
- return len - 1
-end
+ seqLen = seqLen - 1
-local function getNonSequentialKeys(t)
- local keys, keysLength = {}, 0
- local sequenceLength = getSequenceLength(t)
- for k,_ in rawpairs(t) do
- if not isSequenceKey(k, sequenceLength) then
- keysLength = keysLength + 1
- keys[keysLength] = k
+ local keys, keysLen = {}, 0
+ for k in rawpairs(t) do
+ if not isSequenceKey(k, seqLen) then
+ keysLen = keysLen + 1
+ keys[keysLen] = k
end
end
table.sort(keys, sortKeys)
- return keys, keysLength, sequenceLength
+ return keys, keysLen, seqLen
end
-local function countTableAppearances(t, tableAppearances)
- tableAppearances = tableAppearances or {}
-
- if type(t) == 'table' then
- if not tableAppearances[t] then
- tableAppearances[t] = 1
- for k,v in rawpairs(t) do
- countTableAppearances(k, tableAppearances)
- countTableAppearances(v, tableAppearances)
- end
- countTableAppearances(getmetatable(t), tableAppearances)
+local function countCycles(x, cycles)
+ if type(x) == 'table' then
+ if cycles[x] then
+ cycles[x] = cycles[x] + 1
else
- tableAppearances[t] = tableAppearances[t] + 1
+ cycles[x] = 1
+ for k, v in rawpairs(x) do
+ countCycles(k, cycles)
+ countCycles(v, cycles)
+ end
+ countCycles(getmetatable(x), cycles)
end
end
-
- return tableAppearances
-end
-
-local copySequence = function(s)
- local copy, len = {}, #s
- for i=1, len do copy[i] = s[i] end
- return copy, len
end
-local function makePath(path, ...)
- local keys = {...}
- local newPath, len = copySequence(path)
- for i=1, #keys do
- newPath[len + i] = keys[i]
+local function makePath(path, a, b)
+ local newPath = {}
+ local len = #path
+ for i = 1, len do
+ newPath[i] = path[i]
end
+
+ newPath[len + 1] = a
+ newPath[len + 2] = b
+
return newPath
end
local function processRecursive(process, item, path, visited)
- if item == nil then return nil end
- if visited[item] then return visited[item] end
+ if item == nil then
+ return nil
+ end
+ if visited[item] then
+ return visited[item]
+ end
local processed = process(item, path)
if type(processed) == 'table' then
@@ -168,171 +184,166 @@ local function processRecursive(process, item, path, visited)
visited[item] = processedCopy
local processedKey
- for k,v in rawpairs(processed) do
+ for k, v in rawpairs(processed) do
processedKey = processRecursive(process, k, makePath(path, k, inspect.KEY), visited)
if processedKey ~= nil then
- processedCopy[processedKey] = processRecursive(process, v, makePath(path, processedKey), visited)
+ processedCopy[processedKey] =
+ processRecursive(process, v, makePath(path, processedKey), visited)
end
end
- local mt = processRecursive(process, getmetatable(processed), makePath(path, inspect.METATABLE), visited)
- if type(mt) ~= 'table' then mt = nil end -- ignore not nil/table __metatable field
+ local mt =
+ processRecursive(process, getmetatable(processed), makePath(path, inspect.METATABLE), visited)
+ if type(mt) ~= 'table' then
+ mt = nil
+ end
setmetatable(processedCopy, mt)
processed = processedCopy
end
return processed
end
-
-
--------------------------------------------------------------------
-
-local Inspector = {}
-local Inspector_mt = {__index = Inspector}
-
-function Inspector:puts(...)
- local args = {...}
- local buffer = self.buffer
- local len = #buffer
- for i=1, #args do
- len = len + 1
- buffer[len] = args[i]
- end
+local function puts(buf, str)
+ buf.n = buf.n + 1
+ buf[buf.n] = str
end
-function Inspector:down(f)
- self.level = self.level + 1
- f()
- self.level = self.level - 1
-end
+local Inspector = {}
-function Inspector:tabify()
- self:puts(self.newline, string.rep(self.indent, self.level))
-end
+local Inspector_mt = { __index = Inspector }
-function Inspector:alreadyVisited(v)
- return self.ids[v] ~= nil
+local function tabify(inspector)
+ puts(inspector.buf, inspector.newline .. rep(inspector.indent, inspector.level))
end
function Inspector:getId(v)
local id = self.ids[v]
+ local ids = self.ids
if not id then
local tv = type(v)
- id = (self.maxIds[tv] or 0) + 1
- self.maxIds[tv] = id
- self.ids[v] = id
+ id = (ids[tv] or 0) + 1
+ ids[v], ids[tv] = id, id
end
return tostring(id)
end
-function Inspector:putKey(k)
- if isIdentifier(k) then return self:puts(k) end
- self:puts("[")
- self:putValue(k)
- self:puts("]")
-end
+function Inspector:putValue(v)
+ local buf = self.buf
+ local tv = type(v)
+ if tv == 'string' then
+ puts(buf, smartQuote(escape(v)))
+ elseif
+ tv == 'number'
+ or tv == 'boolean'
+ or tv == 'nil'
+ or tv == 'cdata'
+ or tv == 'ctype'
+ or (vim and v == vim.NIL)
+ then
+ puts(buf, tostring(v))
+ elseif tv == 'table' and not self.ids[v] then
+ local t = v
+
+ if t == inspect.KEY or t == inspect.METATABLE then
+ puts(buf, tostring(t))
+ elseif self.level >= self.depth then
+ puts(buf, '{...}')
+ else
+ if self.cycles[t] > 1 then
+ puts(buf, fmt('<%d>', self:getId(t)))
+ end
-function Inspector:putTable(t)
- if t == inspect.KEY or t == inspect.METATABLE then
- self:puts(tostring(t))
- elseif self:alreadyVisited(t) then
- self:puts('<table ', self:getId(t), '>')
- elseif self.level >= self.depth then
- self:puts('{...}')
- else
- if self.tableAppearances[t] > 1 then self:puts('<', self:getId(t), '>') end
-
- local nonSequentialKeys, nonSequentialKeysLength, sequenceLength = getNonSequentialKeys(t)
- local mt = getmetatable(t)
- if (vim and sequenceLength == 0 and nonSequentialKeysLength == 0
- and mt == vim._empty_dict_mt) then
- self:puts(tostring(t))
- return
- end
+ local keys, keysLen, seqLen = getKeys(t)
+ local mt = getmetatable(t)
- self:puts('{')
- self:down(function()
- local count = 0
- for i=1, sequenceLength do
- if count > 0 then self:puts(',') end
- self:puts(' ')
- self:putValue(t[i])
- count = count + 1
+ if vim and seqLen == 0 and keysLen == 0 and mt == vim._empty_dict_mt then
+ puts(buf, tostring(t))
+ return
end
- for i=1, nonSequentialKeysLength do
- local k = nonSequentialKeys[i]
- if count > 0 then self:puts(',') end
- self:tabify()
- self:putKey(k)
- self:puts(' = ')
- self:putValue(t[k])
- count = count + 1
+ puts(buf, '{')
+ self.level = self.level + 1
+
+ for i = 1, seqLen + keysLen do
+ if i > 1 then
+ puts(buf, ',')
+ end
+ if i <= seqLen then
+ puts(buf, ' ')
+ self:putValue(t[i])
+ else
+ local k = keys[i - seqLen]
+ tabify(self)
+ if isIdentifier(k) then
+ puts(buf, k)
+ else
+ puts(buf, '[')
+ self:putValue(k)
+ puts(buf, ']')
+ end
+ puts(buf, ' = ')
+ self:putValue(t[k])
+ end
end
if type(mt) == 'table' then
- if count > 0 then self:puts(',') end
- self:tabify()
- self:puts('<metatable> = ')
+ if seqLen + keysLen > 0 then
+ puts(buf, ',')
+ end
+ tabify(self)
+ puts(buf, '<metatable> = ')
self:putValue(mt)
end
- end)
- if nonSequentialKeysLength > 0 or type(mt) == 'table' then -- result is multi-lined. Justify closing }
- self:tabify()
- elseif sequenceLength > 0 then -- array tables have one extra space before closing }
- self:puts(' ')
- end
+ self.level = self.level - 1
- self:puts('}')
- end
-end
-
-function Inspector:putValue(v)
- local tv = type(v)
+ if keysLen > 0 or type(mt) == 'table' then
+ tabify(self)
+ elseif seqLen > 0 then
+ puts(buf, ' ')
+ end
- if tv == 'string' then
- self:puts(smartQuote(escape(v)))
- elseif tv == 'number' or tv == 'boolean' or tv == 'nil' or
- tv == 'cdata' or tv == 'ctype' or (vim and v == vim.NIL) then
- self:puts(tostring(v))
- elseif tv == 'table' then
- self:putTable(v)
+ puts(buf, '}')
+ end
else
- self:puts('<', tv, ' ', self:getId(v), '>')
+ puts(buf, fmt('<%s %d>', tv, self:getId(v)))
end
end
--------------------------------------------------------------------
-
function inspect.inspect(root, options)
- options = options or {}
+ options = options or {}
- local depth = options.depth or math.huge
+ local depth = options.depth or math.huge
local newline = options.newline or '\n'
- local indent = options.indent or ' '
+ local indent = options.indent or ' '
local process = options.process
if process then
root = processRecursive(process, root, {}, {})
end
+ local cycles = {}
+ countCycles(root, cycles)
+
local inspector = setmetatable({
- depth = depth,
- level = 0,
- buffer = {},
- ids = {},
- maxIds = {},
- newline = newline,
- indent = indent,
- tableAppearances = countTableAppearances(root)
+ buf = { n = 0 },
+ ids = {},
+ cycles = cycles,
+ depth = depth,
+ level = 0,
+ newline = newline,
+ indent = indent,
}, Inspector_mt)
inspector:putValue(root)
- return table.concat(inspector.buffer)
+ return table.concat(inspector.buf)
end
-setmetatable(inspect, { __call = function(_, ...) return inspect.inspect(...) end })
+setmetatable(inspect, {
+ __call = function(_, root, options)
+ return inspect.inspect(root, options)
+ end,
+})
return inspect
diff --git a/runtime/lua/vim/keymap.lua b/runtime/lua/vim/keymap.lua
index d53b790746..f4c2b507a9 100644
--- a/runtime/lua/vim/keymap.lua
+++ b/runtime/lua/vim/keymap.lua
@@ -25,8 +25,8 @@ local keymap = {}
--- vim.keymap.set('n', 'asdf', require('jkl').my_fun)
--- </pre>
---
---- the require('jkl') gets evaluated during this call in order to access the function. If you want to
---- avoid this cost at startup you can wrap it in a function, for example:
+--- the ``require('jkl')`` gets evaluated during this call in order to access the function.
+--- If you want to avoid this cost at startup you can wrap it in a function, for example:
--- <pre>
--- vim.keymap.set('n', 'asdf', function() return require('jkl').my_fun() end)
--- </pre>
@@ -35,39 +35,51 @@ local keymap = {}
--- Can also be list of modes to create mapping on multiple modes.
---@param lhs string Left-hand side |{lhs}| of the mapping.
---@param rhs string|function Right-hand side |{rhs}| of the mapping. Can also be a Lua function.
+--- If a Lua function and `opts.expr == true`, returning `nil` is
+--- equivalent to an empty string.
--
---@param opts table A table of |:map-arguments| such as "silent". In addition to the options
--- listed in |nvim_set_keymap()|, this table also accepts the following keys:
+--- - buffer: (number or boolean) Add a mapping to the given buffer. When "true"
+--- or 0, use the current buffer.
--- - replace_keycodes: (boolean, default true) When both this and expr is "true",
--- |nvim_replace_termcodes()| is applied to the result of Lua expr maps.
--- - remap: (boolean) Make the mapping recursive. This is the
--- inverse of the "noremap" option from |nvim_set_keymap()|.
---- Default `true` if `lhs` is a string starting with `<plug>` (case-insensitive), `false` otherwise.
+--- Default `false`.
---@see |nvim_set_keymap()|
function keymap.set(mode, lhs, rhs, opts)
- vim.validate {
- mode = {mode, {'s', 't'}},
- lhs = {lhs, 's'},
- rhs = {rhs, {'s', 'f'}},
- opts = {opts, 't', true}
- }
+ vim.validate({
+ mode = { mode, { 's', 't' } },
+ lhs = { lhs, 's' },
+ rhs = { rhs, { 's', 'f' } },
+ opts = { opts, 't', true },
+ })
opts = vim.deepcopy(opts) or {}
- local is_rhs_luaref = type(rhs) == "function"
- mode = type(mode) == 'string' and {mode} or mode
+ local is_rhs_luaref = type(rhs) == 'function'
+ mode = type(mode) == 'string' and { mode } or mode
- if is_rhs_luaref and opts.expr and opts.replace_keycodes ~= false then
+ if is_rhs_luaref and opts.expr then
local user_rhs = rhs
- rhs = function ()
- return vim.api.nvim_replace_termcodes(user_rhs(), true, true, true)
+ rhs = function()
+ local res = user_rhs()
+ if res == nil then
+ -- TODO(lewis6991): Handle this in C?
+ return ''
+ elseif opts.replace_keycodes ~= false then
+ return vim.api.nvim_replace_termcodes(res, true, true, true)
+ else
+ return res
+ end
end
end
-- clear replace_keycodes from opts table
opts.replace_keycodes = nil
if opts.remap == nil then
- -- remap by default on <plug> mappings and don't otherwise.
- opts.noremap = is_rhs_luaref or rhs:lower():match("^<plug>") == nil
+ -- default remap value is false
+ opts.noremap = true
else
-- remaps behavior is opposite of noremap option.
opts.noremap = not opts.remap
@@ -106,19 +118,18 @@ end
---@see |vim.keymap.set()|
---
function keymap.del(modes, lhs, opts)
- vim.validate {
- mode = {modes, {'s', 't'}},
- lhs = {lhs, 's'},
- opts = {opts, 't', true}
- }
+ vim.validate({
+ mode = { modes, { 's', 't' } },
+ lhs = { lhs, 's' },
+ opts = { opts, 't', true },
+ })
opts = opts or {}
- modes = type(modes) == 'string' and {modes} or modes
+ modes = type(modes) == 'string' and { modes } or modes
local buffer = false
if opts.buffer ~= nil then
buffer = opts.buffer == true and 0 or opts.buffer
- opts.buffer = nil
end
if buffer == false then
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 8b7eb4ac90..61586ca44f 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -1,58 +1,62 @@
-local if_nil = vim.F.if_nil
-
-local default_handlers = require 'vim.lsp.handlers'
-local log = require 'vim.lsp.log'
-local lsp_rpc = require 'vim.lsp.rpc'
-local protocol = require 'vim.lsp.protocol'
-local util = require 'vim.lsp.util'
-local sync = require 'vim.lsp.sync'
+local default_handlers = require('vim.lsp.handlers')
+local log = require('vim.lsp.log')
+local lsp_rpc = require('vim.lsp.rpc')
+local protocol = require('vim.lsp.protocol')
+local util = require('vim.lsp.util')
+local sync = require('vim.lsp.sync')
local vim = vim
-local nvim_err_writeln, nvim_buf_get_lines, nvim_command, nvim_buf_get_option
- = vim.api.nvim_err_writeln, vim.api.nvim_buf_get_lines, vim.api.nvim_command, vim.api.nvim_buf_get_option
+local api = vim.api
+local nvim_err_writeln, nvim_buf_get_lines, nvim_command, nvim_buf_get_option, nvim_exec_autocmds =
+ api.nvim_err_writeln,
+ api.nvim_buf_get_lines,
+ api.nvim_command,
+ api.nvim_buf_get_option,
+ api.nvim_exec_autocmds
local uv = vim.loop
local tbl_isempty, tbl_extend = vim.tbl_isempty, vim.tbl_extend
local validate = vim.validate
+local if_nil = vim.F.if_nil
local lsp = {
- protocol = protocol;
+ protocol = protocol,
- handlers = default_handlers;
+ handlers = default_handlers,
- buf = require'vim.lsp.buf';
- diagnostic = require'vim.lsp.diagnostic';
- codelens = require'vim.lsp.codelens';
- util = util;
+ buf = require('vim.lsp.buf'),
+ diagnostic = require('vim.lsp.diagnostic'),
+ codelens = require('vim.lsp.codelens'),
+ util = util,
-- Allow raw RPC access.
- rpc = lsp_rpc;
+ rpc = lsp_rpc,
-- Export these directly from rpc.
- rpc_response_error = lsp_rpc.rpc_response_error;
+ rpc_response_error = lsp_rpc.rpc_response_error,
}
--- maps request name to the required resolved_capability in the client.
+-- maps request name to the required server_capability in the client.
lsp._request_name_to_capability = {
- ['textDocument/hover'] = 'hover';
- ['textDocument/signatureHelp'] = 'signature_help';
- ['textDocument/definition'] = 'goto_definition';
- ['textDocument/implementation'] = 'implementation';
- ['textDocument/declaration'] = 'declaration';
- ['textDocument/typeDefinition'] = 'type_definition';
- ['textDocument/documentSymbol'] = 'document_symbol';
- ['textDocument/prepareCallHierarchy'] = 'call_hierarchy';
- ['textDocument/rename'] = 'rename';
- ['textDocument/prepareRename'] = 'rename';
- ['textDocument/codeAction'] = 'code_action';
- ['textDocument/codeLens'] = 'code_lens';
- ['codeLens/resolve'] = 'code_lens_resolve';
- ['workspace/executeCommand'] = 'execute_command';
- ['workspace/symbol'] = 'workspace_symbol';
- ['textDocument/references'] = 'find_references';
- ['textDocument/rangeFormatting'] = 'document_range_formatting';
- ['textDocument/formatting'] = 'document_formatting';
- ['textDocument/completion'] = 'completion';
- ['textDocument/documentHighlight'] = 'document_highlight';
+ ['textDocument/hover'] = { 'hoverProvider' },
+ ['textDocument/signatureHelp'] = { 'signatureHelpProvider' },
+ ['textDocument/definition'] = { 'definitionProvider' },
+ ['textDocument/implementation'] = { 'implementationProvider' },
+ ['textDocument/declaration'] = { 'declarationProvider' },
+ ['textDocument/typeDefinition'] = { 'typeDefinitionProvider' },
+ ['textDocument/documentSymbol'] = { 'documentSymbolProvider' },
+ ['textDocument/prepareCallHierarchy'] = { 'callHierarchyProvider' },
+ ['textDocument/rename'] = { 'renameProvider' },
+ ['textDocument/prepareRename'] = { 'renameProvider', 'prepareProvider' },
+ ['textDocument/codeAction'] = { 'codeActionProvider' },
+ ['textDocument/codeLens'] = { 'codeLensProvider' },
+ ['codeLens/resolve'] = { 'codeLensProvider', 'resolveProvider' },
+ ['workspace/executeCommand'] = { 'executeCommandProvider' },
+ ['workspace/symbol'] = { 'workspaceSymbolProvider' },
+ ['textDocument/references'] = { 'referencesProvider' },
+ ['textDocument/rangeFormatting'] = { 'documentRangeFormattingProvider' },
+ ['textDocument/formatting'] = { 'documentFormattingProvider' },
+ ['textDocument/completion'] = { 'completionProvider' },
+ ['textDocument/documentHighlight'] = { 'documentHighlightProvider' },
}
-- TODO improve handling of scratch buffers with LSP attached.
@@ -62,8 +66,8 @@ lsp._request_name_to_capability = {
---
---@param {...} (List of strings) List to write to the buffer
local function err_message(...)
- nvim_err_writeln(table.concat(vim.tbl_flatten{...}))
- nvim_command("redraw")
+ nvim_err_writeln(table.concat(vim.tbl_flatten({ ... })))
+ nvim_command('redraw')
end
---@private
@@ -73,9 +77,9 @@ end
---buffer if not given.
---@returns bufnr (number) Number of requested buffer
local function resolve_bufnr(bufnr)
- validate { bufnr = { bufnr, 'n', true } }
+ validate({ bufnr = { bufnr, 'n', true } })
if bufnr == nil or bufnr == 0 then
- return vim.api.nvim_get_current_buf()
+ return api.nvim_get_current_buf()
end
return bufnr
end
@@ -85,7 +89,10 @@ end
--- supported in any of the servers registered for the current buffer.
---@param method (string) name of the method
function lsp._unsupported_method(method)
- local msg = string.format("method %s is not supported by any of the servers registered for the current buffer", method)
+ local msg = string.format(
+ 'method %s is not supported by any of the servers registered for the current buffer',
+ method
+ )
log.warn(msg)
return msg
end
@@ -96,23 +103,29 @@ end
---@param filename (string) path to check
---@returns true if {filename} exists and is a directory, false otherwise
local function is_dir(filename)
- validate{filename={filename,'s'}}
+ validate({ filename = { filename, 's' } })
local stat = uv.fs_stat(filename)
return stat and stat.type == 'directory' or false
end
-local wait_result_reason = { [-1] = "timeout"; [-2] = "interrupted"; [-3] = "error" }
+local wait_result_reason = { [-1] = 'timeout', [-2] = 'interrupted', [-3] = 'error' }
local valid_encodings = {
- ["utf-8"] = 'utf-8'; ["utf-16"] = 'utf-16'; ["utf-32"] = 'utf-32';
- ["utf8"] = 'utf-8'; ["utf16"] = 'utf-16'; ["utf32"] = 'utf-32';
- UTF8 = 'utf-8'; UTF16 = 'utf-16'; UTF32 = 'utf-32';
+ ['utf-8'] = 'utf-8',
+ ['utf-16'] = 'utf-16',
+ ['utf-32'] = 'utf-32',
+ ['utf8'] = 'utf-8',
+ ['utf16'] = 'utf-16',
+ ['utf32'] = 'utf-32',
+ UTF8 = 'utf-8',
+ UTF16 = 'utf-16',
+ UTF32 = 'utf-32',
}
local format_line_ending = {
- ["unix"] = '\n',
- ["dos"] = '\r\n',
- ["mac"] = '\r',
+ ['unix'] = '\n',
+ ['dos'] = '\r\n',
+ ['mac'] = '\r',
}
---@private
@@ -138,10 +151,10 @@ local uninitialized_clients = {}
---@private
local function for_each_buffer_client(bufnr, fn, restrict_client_ids)
- validate {
- fn = { fn, 'f' };
- restrict_client_ids = { restrict_client_ids, 't' , true};
- }
+ validate({
+ fn = { fn, 'f' },
+ restrict_client_ids = { restrict_client_ids, 't', true },
+ })
bufnr = resolve_bufnr(bufnr)
local client_ids = all_buffer_active_clients[bufnr]
if not client_ids or tbl_isempty(client_ids) then
@@ -169,9 +182,13 @@ end
-- Error codes to be used with `on_error` from |vim.lsp.start_client|.
-- Can be used to look up the string from a the number or the number
-- from the string.
-lsp.client_errors = tbl_extend("error", lsp_rpc.client_errors, vim.tbl_add_reverse_lookup {
- ON_INIT_CALLBACK_ERROR = table.maxn(lsp_rpc.client_errors) + 1;
-})
+lsp.client_errors = tbl_extend(
+ 'error',
+ lsp_rpc.client_errors,
+ vim.tbl_add_reverse_lookup({
+ ON_INIT_CALLBACK_ERROR = table.maxn(lsp_rpc.client_errors) + 1,
+ })
+)
---@private
--- Normalizes {encoding} to valid LSP encoding names.
@@ -179,11 +196,16 @@ lsp.client_errors = tbl_extend("error", lsp_rpc.client_errors, vim.tbl_add_rever
---@param encoding (string) Encoding to normalize
---@returns (string) normalized encoding name
local function validate_encoding(encoding)
- validate {
- encoding = { encoding, 's' };
- }
+ validate({
+ encoding = { encoding, 's' },
+ })
return valid_encodings[encoding:lower()]
- or error(string.format("Invalid offset encoding %q. Must be one of: 'utf-8', 'utf-16', 'utf-32'", encoding))
+ or error(
+ string.format(
+ "Invalid offset encoding %q. Must be one of: 'utf-8', 'utf-16', 'utf-32'",
+ encoding
+ )
+ )
end
---@internal
@@ -194,16 +216,21 @@ end
---@returns (string) the command
---@returns (list of strings) its arguments
function lsp._cmd_parts(input)
- vim.validate{cmd={
- input,
- function() return vim.tbl_islist(input) end,
- "list"}}
+ validate({
+ cmd = {
+ input,
+ function()
+ return vim.tbl_islist(input)
+ end,
+ 'list',
+ },
+ })
local cmd = input[1]
local cmd_args = {}
-- Don't mutate our input.
for i, v in ipairs(input) do
- vim.validate{["cmd argument"]={v, "s"}}
+ validate({ ['cmd argument'] = { v, 's' } })
if i > 1 then
table.insert(cmd_args, v)
end
@@ -233,30 +260,33 @@ end
---
---@see |vim.lsp.start_client()|
local function validate_client_config(config)
- validate {
- config = { config, 't' };
- }
- validate {
- handlers = { config.handlers, "t", true };
- capabilities = { config.capabilities, "t", true };
- cmd_cwd = { config.cmd_cwd, optional_validator(is_dir), "directory" };
- cmd_env = { config.cmd_env, "t", true };
- name = { config.name, 's', true };
- on_error = { config.on_error, "f", true };
- on_exit = { config.on_exit, "f", true };
- on_init = { config.on_init, "f", true };
- settings = { config.settings, "t", true };
- commands = { config.commands, 't', true };
- before_init = { config.before_init, "f", true };
- offset_encoding = { config.offset_encoding, "s", true };
- flags = { config.flags, "t", true };
- get_language_id = { config.get_language_id, "f", true };
- }
+ validate({
+ config = { config, 't' },
+ })
+ validate({
+ handlers = { config.handlers, 't', true },
+ capabilities = { config.capabilities, 't', true },
+ cmd_cwd = { config.cmd_cwd, optional_validator(is_dir), 'directory' },
+ cmd_env = { config.cmd_env, 't', true },
+ detached = { config.detached, 'b', true },
+ name = { config.name, 's', true },
+ on_error = { config.on_error, 'f', true },
+ on_exit = { config.on_exit, 'f', true },
+ on_init = { config.on_init, 'f', true },
+ settings = { config.settings, 't', true },
+ commands = { config.commands, 't', true },
+ before_init = { config.before_init, 'f', true },
+ offset_encoding = { config.offset_encoding, 's', true },
+ flags = { config.flags, 't', true },
+ get_language_id = { config.get_language_id, 'f', true },
+ })
assert(
- (not config.flags
+ (
+ not config.flags
or not config.flags.debounce_text_changes
- or type(config.flags.debounce_text_changes) == 'number'),
- "flags.debounce_text_changes must be a number with the debounce time in milliseconds"
+ or type(config.flags.debounce_text_changes) == 'number'
+ ),
+ 'flags.debounce_text_changes must be a number with the debounce time in milliseconds'
)
local cmd, cmd_args = lsp._cmd_parts(config.cmd)
@@ -266,9 +296,9 @@ local function validate_client_config(config)
end
return {
- cmd = cmd;
- cmd_args = cmd_args;
- offset_encoding = offset_encoding;
+ cmd = cmd,
+ cmd_args = cmd_args,
+ offset_encoding = offset_encoding,
}
end
@@ -328,14 +358,15 @@ do
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
+ and vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'change')
+ == protocol.TextDocumentSyncKind.Incremental
)
local state = state_by_client[client.id]
if not state then
state = {
- buffers = {};
+ buffers = {},
debounce = client.config.flags.debounce_text_changes or 150,
- use_incremental_sync = use_incremental_sync;
+ use_incremental_sync = use_incremental_sync,
}
state_by_client[client.id] = state
end
@@ -344,6 +375,7 @@ do
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
@@ -403,21 +435,59 @@ do
---@private
function changetracking.prepare(bufnr, firstline, lastline, new_lastline)
local incremental_changes = function(client, buf_state)
- local curr_lines = nvim_buf_get_lines(bufnr, 0, -1, true)
+ 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(
- buf_state.lines, curr_lines, firstline, lastline, new_lastline, client.offset_encoding or 'utf-16', line_ending)
+ 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()
return {
- text = buf_get_full_text(bufnr);
- };
+ text = buf_get_full_text(bufnr),
+ }
end)
local uri = vim.uri_from_bufnr(bufnr)
return function(client)
- if client.resolved_capabilities.text_document_did_change == protocol.TextDocumentSyncKind.None then
+ if
+ vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'change')
+ == protocol.TextDocumentSyncKind.None
+ then
return
end
local state = state_by_client[client.id]
@@ -430,13 +500,17 @@ do
table.insert(buf_state.pending_changes, incremental_changes(client, buf_state))
end
buf_state.pending_change = function()
+ if buf_state.pending_change == nil then
+ return
+ end
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
+ if client.is_stopped() or not api.nvim_buf_is_valid(bufnr) then
return
end
- local changes = state.use_incremental_sync and buf_state.pending_changes or { full_changes() }
- client.notify("textDocument/didChange", {
+ 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],
@@ -448,7 +522,7 @@ do
if debounce == 0 then
buf_state.pending_change()
else
- local timer = vim.loop.new_timer()
+ local timer = uv.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))
@@ -488,29 +562,28 @@ do
end
end
-
---@private
--- Default handler for the 'textDocument/didOpen' LSP notification.
---
----@param bufnr (Number) Number of the buffer, or 0 for current
+---@param bufnr number Number of the buffer, or 0 for current
---@param client Client object
local function text_document_did_open_handler(bufnr, client)
changetracking.init(client, bufnr)
- if not client.resolved_capabilities.text_document_open_close then
+ if not vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'openClose') then
return
end
- if not vim.api.nvim_buf_is_loaded(bufnr) then
+ if not api.nvim_buf_is_loaded(bufnr) then
return
end
local filetype = nvim_buf_get_option(bufnr, 'filetype')
local params = {
textDocument = {
- version = 0;
- uri = vim.uri_from_bufnr(bufnr);
- languageId = client.config.get_language_id(bufnr, filetype);
- text = buf_get_full_text(bufnr);
- }
+ version = 0,
+ uri = vim.uri_from_bufnr(bufnr),
+ languageId = client.config.get_language_id(bufnr, filetype),
+ text = buf_get_full_text(bufnr),
+ },
}
client.notify('textDocument/didOpen', params)
util.buf_versions[bufnr] = params.textDocument.version
@@ -519,7 +592,7 @@ local function text_document_did_open_handler(bufnr, client)
vim.schedule(function()
-- Protect against a race where the buffer disappears
-- between `did_open_handler` and the scheduled function firing.
- if vim.api.nvim_buf_is_valid(bufnr) then
+ if api.nvim_buf_is_valid(bufnr) then
local namespace = vim.lsp.diagnostic.get_namespace(client.id)
vim.diagnostic.show(namespace, bufnr)
end
@@ -602,14 +675,86 @@ end
---
--- - {server_capabilities} (table): Response from the server sent on
--- `initialize` describing the server's capabilities.
----
---- - {resolved_capabilities} (table): Normalized table of
---- capabilities that we have detected based on the initialize
---- response from the server in `server_capabilities`.
function lsp.client()
error()
end
+--- Create a new LSP client and start a language server or reuses an already
+--- running client if one is found matching `name` and `root_dir`.
+--- Attaches the current buffer to the client.
+---
+--- Example:
+---
+--- <pre>
+--- vim.lsp.start({
+--- name = 'my-server-name',
+--- cmd = {'name-of-language-server-executable'},
+--- root_dir = vim.fs.dirname(vim.fs.find({'pyproject.toml', 'setup.py'}, { upward = true })[1]),
+--- })
+--- </pre>
+---
+--- See |lsp.start_client| for all available options. The most important are:
+---
+--- `name` is an arbitrary name for the LSP client. It should be unique per
+--- language server.
+---
+--- `cmd` the command as list - used to start the language server.
+--- The command must be present in the `$PATH` environment variable or an
+--- absolute path to the executable. Shell constructs like `~` are *NOT* expanded.
+---
+--- `root_dir` path to the project root.
+--- By default this is used to decide if an existing client should be re-used.
+--- The example above uses |vim.fs.find| and |vim.fs.dirname| to detect the
+--- root by traversing the file system upwards starting
+--- from the current directory until either a `pyproject.toml` or `setup.py`
+--- file is found.
+---
+--- `workspace_folders` a list of { uri:string, name: string } tables.
+--- The project root folders used by the language server.
+--- If `nil` the property is derived from the `root_dir` for convenience.
+---
+--- Language servers use this information to discover metadata like the
+--- dependencies of your project and they tend to index the contents within the
+--- project folder.
+---
+---
+--- To ensure a language server is only started for languages it can handle,
+--- make sure to call |vim.lsp.start| within a |FileType| autocmd.
+--- Either use |:au|, |nvim_create_autocmd()| or put the call in a
+--- `ftplugin/<filetype_name>.lua` (See |ftplugin-name|)
+---
+---@param config table Same configuration as documented in |lsp.start_client()|
+---@param opts nil|table Optional keyword arguments:
+--- - reuse_client (fun(client: client, config: table): boolean)
+--- Predicate used to decide if a client should be re-used.
+--- Used on all running clients.
+--- The default implementation re-uses a client if name
+--- and root_dir matches.
+---@return number client_id
+function lsp.start(config, opts)
+ opts = opts or {}
+ local reuse_client = opts.reuse_client
+ or function(client, conf)
+ return client.config.root_dir == conf.root_dir and client.name == conf.name
+ end
+ config.name = config.name or (config.cmd[1] and vim.fs.basename(config.cmd[1])) or nil
+ local bufnr = api.nvim_get_current_buf()
+ for _, clients in ipairs({ uninitialized_clients, lsp.get_active_clients() }) do
+ for _, client in pairs(clients) do
+ if reuse_client(client, config) then
+ lsp.buf_attach_client(bufnr, client.id)
+ return client.id
+ end
+ end
+ end
+ local client_id = lsp.start_client(config)
+ if client_id == nil then
+ return nil -- lsp.start_client will have printed an error
+ end
+ lsp.buf_attach_client(bufnr, client_id)
+ return client_id
+end
+
-- FIXME: DOC: Currently all methods on the `vim.lsp.client` object are
-- documented twice: Here, and on the methods themselves (e.g.
-- `client.request()`). This is a workaround for the vimdoc generator script
@@ -637,6 +782,10 @@ end
--- { "PRODUCTION=true"; "TEST=123"; PORT = 8080; HOST = "0.0.0.0"; }
--- </pre>
---
+---@param detached: (boolean, default true) Daemonize the server process so that it runs in a
+--- separate process group from Nvim. Nvim will shutdown the process on exit, but if Nvim fails to
+--- exit cleanly this could leave behind orphaned server processes.
+---
---@param workspace_folders (table) List of workspace folders passed to the
--- language server. For backwards compatibility rootUri and rootPath will be
--- derived from the first workspace folder in this list. See `workspaceFolders` in
@@ -708,7 +857,7 @@ end
--- server in the initialize request. Invalid/empty values will default to "off"
---@param flags: A table with flags for the client. The current (experimental) flags are:
--- - allow_incremental_sync (bool, default true): Allow using incremental sync for buffer edits
---- - debounce_text_changes (number, default nil): Debounce didChange
+--- - debounce_text_changes (number, default 150): Debounce didChange
--- notifications to the server by the given number in milliseconds. No debounce
--- occurs if nil
--- - exit_timeout (number, default 500): Milliseconds to wait for server to
@@ -724,7 +873,8 @@ end
--- the client has been initialized.
function lsp.start_client(config)
local cleaned_config = validate_client_config(config)
- local cmd, cmd_args, offset_encoding = cleaned_config.cmd, cleaned_config.cmd_args, cleaned_config.offset_encoding
+ local cmd, cmd_args, offset_encoding =
+ cleaned_config.cmd, cleaned_config.cmd_args, cleaned_config.offset_encoding
config.flags = config.flags or {}
config.settings = config.settings or {}
@@ -732,13 +882,15 @@ function lsp.start_client(config)
-- By default, get_language_id just returns the exact filetype it is passed.
-- It is possible to pass in something that will calculate a different filetype,
-- to be sent by the client.
- config.get_language_id = config.get_language_id or function(_, filetype) return filetype end
+ config.get_language_id = config.get_language_id or function(_, filetype)
+ return filetype
+ end
local client_id = next_client_id()
local handlers = config.handlers or {}
local name = config.name or tostring(client_id)
- local log_prefix = string.format("LSP[%s]", name)
+ local log_prefix = string.format('LSP[%s]', name)
local dispatch = {}
@@ -763,7 +915,7 @@ function lsp.start_client(config)
local handler = resolve_handler(method)
if handler then
-- Method name is provided here for convenience.
- handler(nil, params, {method=method, client_id=client_id})
+ handler(nil, params, { method = method, client_id = client_id })
end
end
@@ -776,10 +928,10 @@ function lsp.start_client(config)
local _ = log.trace() and log.trace('server_request', method, params)
local handler = resolve_handler(method)
if handler then
- local _ = log.trace() and log.trace("server_request: found handler for", method)
- return handler(nil, params, {method=method, client_id=client_id})
+ local _ = log.trace() and log.trace('server_request: found handler for', method)
+ return handler(nil, params, { method = method, client_id = client_id })
end
- local _ = log.warn() and log.warn("server_request: no handler found for", method)
+ local _ = log.warn() and log.warn('server_request: no handler found for', method)
return nil, lsp.rpc_response_error(protocol.ErrorCodes.MethodNotFound)
end
@@ -791,18 +943,41 @@ function lsp.start_client(config)
---@see |vim.lsp.rpc.client_errors| for possible errors. Use
---`vim.lsp.rpc.client_errors[code]` to get a human-friendly name.
function dispatch.on_error(code, err)
- local _ = log.error() and log.error(log_prefix, "on_error", { code = lsp.client_errors[code], err = err })
+ local _ = log.error()
+ and log.error(log_prefix, 'on_error', { code = lsp.client_errors[code], err = err })
err_message(log_prefix, ': Error ', lsp.client_errors[code], ': ', vim.inspect(err))
if config.on_error then
local status, usererr = pcall(config.on_error, code, err)
if not status then
- local _ = log.error() and log.error(log_prefix, "user on_error failed", { err = usererr })
+ local _ = log.error() and log.error(log_prefix, 'user on_error failed', { err = usererr })
err_message(log_prefix, ' user on_error failed: ', tostring(usererr))
end
end
end
---@private
+ local function set_defaults(client, bufnr)
+ if client.server_capabilities.definitionProvider and vim.bo[bufnr].tagfunc == '' then
+ vim.bo[bufnr].tagfunc = 'v:lua.vim.lsp.tagfunc'
+ end
+ if client.server_capabilities.completionProvider and vim.bo[bufnr].omnifunc == '' then
+ vim.bo[bufnr].omnifunc = 'v:lua.vim.lsp.omnifunc'
+ end
+ end
+
+ ---@private
+ --- Reset defaults set by `set_defaults`.
+ --- Must only be called if the last client attached to a buffer exits.
+ local function unset_defaults(bufnr)
+ if vim.bo[bufnr].tagfunc == 'v:lua.vim.lsp.tagfunc' then
+ vim.bo[bufnr].tagfunc = nil
+ end
+ if vim.bo[bufnr].omnifunc == 'v:lua.vim.lsp.omnifunc' then
+ vim.bo[bufnr].omnifunc = nil
+ end
+ end
+
+ ---@private
--- Invoked on client exit.
---
---@param code (number) exit code of the process
@@ -812,17 +987,35 @@ function lsp.start_client(config)
pcall(config.on_exit, code, signal, client_id)
end
+ for bufnr, client_ids in pairs(all_buffer_active_clients) do
+ if client_ids[client_id] then
+ vim.schedule(function()
+ nvim_exec_autocmds('LspDetach', {
+ buffer = bufnr,
+ modeline = false,
+ data = { client_id = client_id },
+ })
+
+ local namespace = vim.lsp.diagnostic.get_namespace(client_id)
+ vim.diagnostic.reset(namespace, bufnr)
+ end)
+
+ client_ids[client_id] = nil
+ end
+ if vim.tbl_isempty(client_ids) then
+ vim.schedule(function()
+ unset_defaults(bufnr)
+ end)
+ end
+ end
+
active_clients[client_id] = nil
uninitialized_clients[client_id] = nil
- lsp.diagnostic.reset(client_id, all_buffer_active_clients)
changetracking.reset(client_id)
- for _, client_ids in pairs(all_buffer_active_clients) do
- client_ids[client_id] = nil
- end
-
if code ~= 0 or (signal ~= 0 and signal ~= 15) then
- local msg = string.format("Client %s quit with exit code %s and signal %s", client_id, code, signal)
+ local msg =
+ string.format('Client %s quit with exit code %s and signal %s', client_id, code, signal)
vim.schedule(function()
vim.notify(msg, vim.log.levels.WARN)
end)
@@ -831,36 +1024,41 @@ function lsp.start_client(config)
-- Start the RPC client.
local rpc = lsp_rpc.start(cmd, cmd_args, dispatch, {
- cwd = config.cmd_cwd;
- env = config.cmd_env;
+ cwd = config.cmd_cwd,
+ env = config.cmd_env,
+ detached = config.detached,
})
-- Return nil if client fails to start
- if not rpc then return end
+ if not rpc then
+ return
+ end
local client = {
- id = client_id;
- name = name;
- rpc = rpc;
- offset_encoding = offset_encoding;
- config = config;
- attached_buffers = {};
+ id = client_id,
+ name = name,
+ rpc = rpc,
+ offset_encoding = offset_encoding,
+ config = config,
+ attached_buffers = {},
- handlers = handlers;
- commands = config.commands or {};
+ handlers = handlers,
+ commands = config.commands or {},
- requests = {};
+ requests = {},
-- for $/progress report
- messages = { name = name, messages = {}, progress = {}, status = {} };
+ messages = { name = name, messages = {}, progress = {}, status = {} },
}
-- Store the uninitialized_clients for cleanup in case we exit before initialize finishes.
- uninitialized_clients[client_id] = client;
+ uninitialized_clients[client_id] = client
---@private
local function initialize()
local valid_traces = {
- off = 'off'; messages = 'messages'; verbose = 'verbose';
+ off = 'off',
+ messages = 'messages',
+ verbose = 'verbose',
}
local version = vim.version()
@@ -869,10 +1067,12 @@ function lsp.start_client(config)
local root_path
if config.workspace_folders or config.root_dir then
if config.root_dir and not config.workspace_folders then
- workspace_folders = {{
- uri = vim.uri_from_fname(config.root_dir);
- name = string.format("%s", config.root_dir);
- }};
+ workspace_folders = {
+ {
+ uri = vim.uri_from_fname(config.root_dir),
+ name = string.format('%s', config.root_dir),
+ },
+ }
else
workspace_folders = config.workspace_folders
end
@@ -889,68 +1089,102 @@ function lsp.start_client(config)
-- the process has not been started by another process. If the parent
-- process is not alive then the server should exit (see exit notification)
-- its process.
- processId = uv.getpid();
+ processId = uv.getpid(),
-- Information about the client
-- since 3.15.0
clientInfo = {
- name = "Neovim",
- version = string.format("%s.%s.%s", version.major, version.minor, version.patch)
- };
+ name = 'Neovim',
+ version = string.format('%s.%s.%s', version.major, version.minor, version.patch),
+ },
-- The rootPath of the workspace. Is null if no folder is open.
--
-- @deprecated in favour of rootUri.
- rootPath = root_path or vim.NIL;
+ rootPath = root_path or vim.NIL,
-- The rootUri of the workspace. Is null if no folder is open. If both
-- `rootPath` and `rootUri` are set `rootUri` wins.
- rootUri = root_uri or vim.NIL;
+ rootUri = root_uri or vim.NIL,
-- The workspace folders configured in the client when the server starts.
-- This property is only available if the client supports workspace folders.
-- It can be `null` if the client supports workspace folders but none are
-- configured.
- workspaceFolders = workspace_folders or vim.NIL;
+ workspaceFolders = workspace_folders or vim.NIL,
-- User provided initialization options.
- initializationOptions = config.init_options;
+ initializationOptions = config.init_options,
-- The capabilities provided by the client (editor or tool)
- capabilities = config.capabilities or protocol.make_client_capabilities();
+ capabilities = config.capabilities or protocol.make_client_capabilities(),
-- The initial trace setting. If omitted trace is disabled ("off").
-- trace = "off" | "messages" | "verbose";
- trace = valid_traces[config.trace] or 'off';
+ trace = valid_traces[config.trace] or 'off',
}
if config.before_init then
-- TODO(ashkan) handle errors here.
pcall(config.before_init, initialize_params, config)
end
- local _ = log.trace() and log.trace(log_prefix, "initialize_params", initialize_params)
+ local _ = log.trace() and log.trace(log_prefix, 'initialize_params', initialize_params)
rpc.request('initialize', initialize_params, function(init_err, result)
assert(not init_err, tostring(init_err))
- assert(result, "server sent empty result")
+ assert(result, 'server sent empty result')
rpc.notify('initialized', vim.empty_dict())
client.initialized = true
uninitialized_clients[client_id] = nil
client.workspace_folders = workspace_folders
-- TODO(mjlbach): Backwards compatibility, to be removed in 0.7
client.workspaceFolders = client.workspace_folders
- client.server_capabilities = assert(result.capabilities, "initialize result doesn't contain capabilities")
+
-- These are the cleaned up capabilities we use for dynamically deciding
-- when to send certain events to clients.
- client.resolved_capabilities = protocol.resolve_capabilities(client.server_capabilities)
+ client.server_capabilities =
+ assert(result.capabilities, "initialize result doesn't contain capabilities")
+ client.server_capabilities = protocol.resolve_capabilities(client.server_capabilities)
+
+ -- Deprecation wrapper: this will be removed in 0.8
+ local mt = {}
+ mt.__index = function(table, key)
+ if key == 'resolved_capabilities' then
+ vim.notify_once(
+ '[LSP] Accessing client.resolved_capabilities is deprecated, '
+ .. 'update your plugins or configuration to access client.server_capabilities instead.'
+ .. 'The new key/value pairs in server_capabilities directly match those '
+ .. 'defined in the language server protocol',
+ vim.log.levels.WARN
+ )
+ rawset(table, key, protocol._resolve_capabilities_compat(client.server_capabilities))
+ return rawget(table, key)
+ else
+ return rawget(table, key)
+ end
+ end
+ setmetatable(client, mt)
+
client.supports_method = function(method)
local required_capability = lsp._request_name_to_capability[method]
-- if we don't know about the method, assume that the client supports it.
if not required_capability then
return true
end
+ if vim.tbl_get(client.server_capabilities, unpack(required_capability)) then
+ return true
+ else
+ return false
+ end
+ end
- return client.resolved_capabilities[required_capability]
+ if next(config.settings) then
+ client.notify('workspace/didChangeConfiguration', { settings = config.settings })
end
+
if config.on_init then
local status, err = pcall(config.on_init, client, result)
if not status then
pcall(handlers.on_error, lsp.client_errors.ON_INIT_CALLBACK_ERROR, err)
end
end
- local _ = log.debug() and log.debug(log_prefix, "server_capabilities", client.server_capabilities)
- local _ = log.info() and log.info(log_prefix, "initialized", { resolved_capabilities = client.resolved_capabilities })
+ local _ = log.info()
+ and log.info(
+ log_prefix,
+ 'server_capabilities',
+ { server_capabilities = client.server_capabilities }
+ )
-- Only assign after initialized.
active_clients[client_id] = client
@@ -985,22 +1219,27 @@ function lsp.start_client(config)
function client.request(method, params, handler, bufnr)
if not handler then
handler = resolve_handler(method)
- or error(string.format("not found: %q request handler for client %q.", method, client.name))
+ 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, bufnr)
bufnr = resolve_bufnr(bufnr)
- local _ = log.debug() and log.debug(log_prefix, "client.request", client_id, method, params, handler, bufnr)
+ local _ = log.debug()
+ and log.debug(log_prefix, 'client.request', client_id, method, params, handler, bufnr)
local success, request_id = rpc.request(method, params, function(err, result)
- handler(err, result, {method=method, client_id=client_id, bufnr=bufnr, params=params})
+ handler(
+ err,
+ result,
+ { method = method, client_id = client_id, bufnr = bufnr, params = params }
+ )
end, function(request_id)
client.requests[request_id] = nil
- nvim_command("doautocmd <nomodeline> User LspRequest")
+ nvim_exec_autocmds('User', { pattern = 'LspRequest', modeline = false })
end)
if success then
- client.requests[request_id] = { type='pending', bufnr=bufnr, method=method }
- nvim_command("doautocmd <nomodeline> User LspRequest")
+ client.requests[request_id] = { type = 'pending', bufnr = bufnr, method = method }
+ nvim_exec_autocmds('User', { pattern = 'LspRequest', modeline = false })
end
return success, request_id
@@ -1027,9 +1266,10 @@ function lsp.start_client(config)
request_result = { err = err, result = result }
end
- local success, request_id = client.request(method, params, _sync_handler,
- bufnr)
- if not success then return nil end
+ local success, request_id = client.request(method, params, _sync_handler, bufnr)
+ if not success then
+ return nil
+ end
local wait_result, reason = vim.wait(timeout_ms or 1000, function()
return request_result ~= nil
@@ -1064,13 +1304,13 @@ function lsp.start_client(config)
---@returns true if any client returns true; false otherwise
---@see |vim.lsp.client.notify()|
function client.cancel_request(id)
- validate{id = {id, 'n'}}
+ validate({ id = { id, 'n' } })
local request = client.requests[id]
if request and request.type == 'pending' then
request.type = 'cancel'
- nvim_command("doautocmd <nomodeline> User LspRequest")
+ nvim_exec_autocmds('User', { pattern = 'LspRequest', modeline = false })
end
- return rpc.notify("$/cancelRequest", { id = id })
+ return rpc.notify('$/cancelRequest', { id = id })
end
-- Track this so that we can escalate automatically if we've already tried a
@@ -1085,18 +1325,11 @@ function lsp.start_client(config)
---
---@param force (bool, optional)
function client.stop(force)
-
- lsp.diagnostic.reset(client_id, all_buffer_active_clients)
- changetracking.reset(client_id)
- for _, client_ids in pairs(all_buffer_active_clients) do
- client_ids[client_id] = nil
- end
-
local handle = rpc.handle
if handle:is_closing() then
return
end
- if force or (not client.initialized) or graceful_shutdown_failed then
+ if force or not client.initialized or graceful_shutdown_failed then
handle:kill(15)
return
end
@@ -1126,6 +1359,15 @@ function lsp.start_client(config)
---@param bufnr (number) Buffer number
function client._on_attach(bufnr)
text_document_did_open_handler(bufnr, client)
+
+ set_defaults(client, bufnr)
+
+ nvim_exec_autocmds('LspAttach', {
+ buffer = bufnr,
+ modeline = false,
+ data = { client_id = client.id },
+ })
+
if config.on_attach then
-- TODO(ashkan) handle errors.
pcall(config.on_attach, client, bufnr)
@@ -1143,34 +1385,37 @@ end
--- Notify all attached clients that a buffer has changed.
local text_document_did_change_handler
do
- text_document_did_change_handler = function(_, bufnr, changedtick, firstline, lastline, new_lastline)
-
- -- Detach (nvim_buf_attach) via returning True to on_lines if no clients are attached
- if tbl_isempty(all_buffer_active_clients[bufnr] or {}) then
- return true
+ text_document_did_change_handler =
+ function(_, bufnr, changedtick, firstline, lastline, new_lastline)
+ -- Detach (nvim_buf_attach) via returning True to on_lines if no clients are attached
+ if tbl_isempty(all_buffer_active_clients[bufnr] or {}) then
+ return true
+ end
+ util.buf_versions[bufnr] = changedtick
+ local compute_change_and_notify =
+ changetracking.prepare(bufnr, firstline, lastline, new_lastline)
+ for_each_buffer_client(bufnr, compute_change_and_notify)
end
- util.buf_versions[bufnr] = changedtick
- local compute_change_and_notify = changetracking.prepare(bufnr, firstline, lastline, new_lastline)
- for_each_buffer_client(bufnr, compute_change_and_notify)
- end
end
--- Buffer lifecycle handler for textDocument/didSave
-function lsp._text_document_did_save_handler(bufnr)
+---@private
+---Buffer lifecycle handler for textDocument/didSave
+local function text_document_did_save_handler(bufnr)
bufnr = resolve_bufnr(bufnr)
local uri = vim.uri_from_bufnr(bufnr)
local text = once(buf_get_full_text)
- for_each_buffer_client(bufnr, function(client, _client_id)
- if client.resolved_capabilities.text_document_save then
+ for_each_buffer_client(bufnr, function(client)
+ local save_capability = vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'save')
+ if save_capability then
local included_text
- if client.resolved_capabilities.text_document_save_include_text then
+ if type(save_capability) == 'table' and save_capability.includeText then
included_text = text(bufnr)
end
client.notify('textDocument/didSave', {
textDocument = {
- uri = uri;
- };
- text = included_text;
+ uri = uri,
+ },
+ text = included_text,
})
end
end)
@@ -1184,15 +1429,14 @@ end
---@param bufnr (number) Buffer handle, or 0 for current
---@param client_id (number) Client id
function lsp.buf_attach_client(bufnr, client_id)
- validate {
- bufnr = {bufnr, 'n', true};
- client_id = {client_id, 'n'};
- }
+ validate({
+ bufnr = { bufnr, 'n', true },
+ client_id = { client_id, 'n' },
+ })
bufnr = resolve_bufnr(bufnr)
- if not vim.api.nvim_buf_is_loaded(bufnr) then
- local _ = log.warn() and log.warn(
- string.format("buf_attach_client called on unloaded buffer (id: %d): ", bufnr)
- )
+ if not api.nvim_buf_is_loaded(bufnr) then
+ local _ = log.warn()
+ and log.warn(string.format('buf_attach_client called on unloaded buffer (id: %d): ', bufnr))
return false
end
local buffer_client_ids = all_buffer_active_clients[bufnr]
@@ -1202,45 +1446,49 @@ function lsp.buf_attach_client(bufnr, client_id)
all_buffer_active_clients[bufnr] = buffer_client_ids
local uri = vim.uri_from_bufnr(bufnr)
- local buf_did_save_autocommand = [=[
- augroup lsp_c_%d_b_%d_did_save
- au!
- au BufWritePost <buffer=%d> lua vim.lsp._text_document_did_save_handler(0)
- augroup END
- ]=]
- vim.api.nvim_exec(string.format(buf_did_save_autocommand, client_id, bufnr, bufnr), false)
+ local augroup = ('lsp_c_%d_b_%d_did_save'):format(client_id, bufnr)
+ api.nvim_create_autocmd('BufWritePost', {
+ group = api.nvim_create_augroup(augroup, { clear = true }),
+ buffer = bufnr,
+ desc = 'vim.lsp: textDocument/didSave handler',
+ callback = function(ctx)
+ text_document_did_save_handler(ctx.buf)
+ end,
+ })
-- First time, so attach and set up stuff.
- vim.api.nvim_buf_attach(bufnr, false, {
- on_lines = text_document_did_change_handler;
+ api.nvim_buf_attach(bufnr, false, {
+ on_lines = text_document_did_change_handler,
on_reload = function()
- local params = { textDocument = { uri = uri; } }
+ local params = { textDocument = { uri = uri } }
for_each_buffer_client(bufnr, function(client, _)
changetracking.reset_buf(client, bufnr)
- if client.resolved_capabilities.text_document_open_close then
+ if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'openClose') then
client.notify('textDocument/didClose', params)
end
text_document_did_open_handler(bufnr, client)
end)
- end;
+ end,
on_detach = function()
- local params = { textDocument = { uri = uri; } }
+ local params = { textDocument = { uri = uri } }
for_each_buffer_client(bufnr, function(client, _)
changetracking.reset_buf(client, bufnr)
- if client.resolved_capabilities.text_document_open_close then
+ if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'openClose') then
client.notify('textDocument/didClose', params)
end
end)
util.buf_versions[bufnr] = nil
all_buffer_active_clients[bufnr] = nil
- end;
+ end,
-- TODO if we know all of the potential clients ahead of time, then we
-- could conditionally set this.
-- utf_sizes = size_index > 1;
- utf_sizes = true;
+ utf_sizes = true,
})
end
- if buffer_client_ids[client_id] then return end
+ if buffer_client_ids[client_id] then
+ return
+ end
-- This is our first time attaching this client to this buffer.
buffer_client_ids[client_id] = true
@@ -1260,25 +1508,35 @@ end
---@param bufnr number Buffer handle, or 0 for current
---@param client_id number Client id
function lsp.buf_detach_client(bufnr, client_id)
- validate {
- bufnr = {bufnr, 'n', true};
- client_id = {client_id, 'n'};
- }
+ validate({
+ bufnr = { bufnr, 'n', true },
+ client_id = { client_id, 'n' },
+ })
bufnr = resolve_bufnr(bufnr)
local client = lsp.get_client_by_id(client_id)
if not client or not client.attached_buffers[bufnr] then
vim.notify(
- string.format('Buffer (id: %d) is not attached to client (id: %d). Cannot detach.', client_id, bufnr)
+ string.format(
+ 'Buffer (id: %d) is not attached to client (id: %d). Cannot detach.',
+ client_id,
+ bufnr
+ )
)
return
end
+ nvim_exec_autocmds('LspDetach', {
+ buffer = bufnr,
+ modeline = false,
+ data = { client_id = client_id },
+ })
+
changetracking.reset_buf(client, bufnr)
- if client.resolved_capabilities.text_document_open_close then
+ if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'openClose') then
local uri = vim.uri_from_bufnr(bufnr)
- local params = { textDocument = { uri = uri; } }
+ local params = { textDocument = { uri = uri } }
client.notify('textDocument/didClose', params)
end
@@ -1294,7 +1552,6 @@ function lsp.buf_detach_client(bufnr, client_id)
vim.diagnostic.reset(namespace, bufnr)
vim.notify(string.format('Detached buffer (id: %d) from client (id: %d)', bufnr, client_id))
-
end
--- Checks if a buffer is attached for a particular client.
@@ -1339,7 +1596,7 @@ end
---@param client_id client id or |vim.lsp.client| object, or list thereof
---@param force boolean (optional) shutdown forcefully
function lsp.stop_client(client_id, force)
- local ids = type(client_id) == 'table' and client_id or {client_id}
+ local ids = type(client_id) == 'table' and client_id or { client_id }
for _, id in ipairs(ids) do
if type(id) == 'table' and id.stop ~= nil then
id.stop(force)
@@ -1351,68 +1608,90 @@ function lsp.stop_client(client_id, force)
end
end
---- Gets all active clients.
+--- Get active clients.
---
----@returns Table of |vim.lsp.client| objects
-function lsp.get_active_clients()
- return vim.tbl_values(active_clients)
-end
+---@param filter (table|nil) A table with key-value pairs used to filter the
+--- returned clients. The available keys are:
+--- - id (number): Only return clients with the given id
+--- - bufnr (number): Only return clients attached to this buffer
+--- - name (string): Only return clients with the given name
+---@returns (table) List of |vim.lsp.client| objects
+function lsp.get_active_clients(filter)
+ validate({ filter = { filter, 't', true } })
-function lsp._vim_exit_handler()
- log.info("exit_handler", active_clients)
- for _, client in pairs(uninitialized_clients) do
- client.stop(true)
- end
- -- TODO handle v:dying differently?
- if tbl_isempty(active_clients) then
- return
- end
- for _, client in pairs(active_clients) do
- client.stop()
- end
+ filter = filter or {}
- local timeouts = {}
- local max_timeout = 0
- local send_kill = false
+ local clients = {}
- for client_id, client in pairs(active_clients) do
- local timeout = if_nil(client.config.flags.exit_timeout, 500)
- if timeout then
- send_kill = true
- timeouts[client_id] = timeout
- max_timeout = math.max(timeout, max_timeout)
+ local t = filter.bufnr and (all_buffer_active_clients[resolve_bufnr(filter.bufnr)] or {})
+ or active_clients
+ for client_id in pairs(t) do
+ local client = active_clients[client_id]
+ if
+ (filter.id == nil or client.id == filter.id)
+ and (filter.name == nil or client.name == filter.name)
+ then
+ clients[#clients + 1] = client
end
end
+ return clients
+end
- local poll_time = 50
-
- ---@private
- local function check_clients_closed()
- for client_id, timeout in pairs(timeouts) do
- timeouts[client_id] = timeout - poll_time
+api.nvim_create_autocmd('VimLeavePre', {
+ desc = 'vim.lsp: exit handler',
+ callback = function()
+ log.info('exit_handler', active_clients)
+ for _, client in pairs(uninitialized_clients) do
+ client.stop(true)
+ end
+ -- TODO handle v:dying differently?
+ if tbl_isempty(active_clients) then
+ return
end
+ for _, client in pairs(active_clients) do
+ client.stop()
+ end
+
+ local timeouts = {}
+ local max_timeout = 0
+ local send_kill = false
- for client_id, _ in pairs(active_clients) do
- if timeouts[client_id] ~= nil and timeouts[client_id] > 0 then
- return false
+ for client_id, client in pairs(active_clients) do
+ local timeout = if_nil(client.config.flags.exit_timeout, 500)
+ if timeout then
+ send_kill = true
+ timeouts[client_id] = timeout
+ max_timeout = math.max(timeout, max_timeout)
end
end
- return true
- end
- if send_kill then
- if not vim.wait(max_timeout, check_clients_closed, poll_time) then
- for client_id, client in pairs(active_clients) do
- if timeouts[client_id] ~= nil then
- client.stop(true)
+ local poll_time = 50
+
+ ---@private
+ local function check_clients_closed()
+ for client_id, timeout in pairs(timeouts) do
+ timeouts[client_id] = timeout - poll_time
+ end
+
+ for client_id, _ in pairs(active_clients) do
+ if timeouts[client_id] ~= nil and timeouts[client_id] > 0 then
+ return false
end
end
+ return true
end
- end
-end
-
-nvim_command("autocmd VimLeavePre * lua vim.lsp._vim_exit_handler()")
+ if send_kill then
+ if not vim.wait(max_timeout, check_clients_closed, poll_time) then
+ for client_id, client in pairs(active_clients) do
+ if timeouts[client_id] ~= nil then
+ client.stop(true)
+ end
+ end
+ end
+ end
+ end,
+})
--- Sends an async request for all active clients attached to the
--- buffer.
@@ -1428,11 +1707,11 @@ nvim_command("autocmd VimLeavePre * lua vim.lsp._vim_exit_handler()")
--- - Function which can be used to cancel all the requests. You could instead
--- iterate all clients and call their `cancel_request()` methods.
function lsp.buf_request(bufnr, method, params, handler)
- validate {
- bufnr = { bufnr, 'n', true };
- method = { method, 's' };
- handler = { handler, 'f', true };
- }
+ validate({
+ bufnr = { bufnr, 'n', true },
+ method = { method, 's' },
+ handler = { handler, 'f', true },
+ })
local supported_clients = {}
local method_supported = false
@@ -1444,20 +1723,22 @@ function lsp.buf_request(bufnr, method, params, handler)
end)
-- if has client but no clients support the given method, notify the user
- if not tbl_isempty(all_buffer_active_clients[resolve_bufnr(bufnr)] or {}) and not method_supported then
+ if
+ not tbl_isempty(all_buffer_active_clients[resolve_bufnr(bufnr)] or {}) and not method_supported
+ then
vim.notify(lsp._unsupported_method(method), vim.log.levels.ERROR)
- vim.api.nvim_command("redraw")
+ nvim_command('redraw')
return {}, function() end
end
local client_request_ids = {}
for_each_buffer_client(bufnr, function(client, client_id, resolved_bufnr)
- local request_success, request_id = client.request(method, params, handler, resolved_bufnr)
- -- This could only fail if the client shut down in the time since we looked
- -- it up and we did the request, which should be rare.
- if request_success then
- client_request_ids[client_id] = request_id
- end
+ local request_success, request_id = client.request(method, params, handler, resolved_bufnr)
+ -- This could only fail if the client shut down in the time since we looked
+ -- it up and we did the request, which should be rare.
+ if request_success then
+ client_request_ids[client_id] = request_id
+ end
end, supported_clients)
local function _cancel_all_requests()
@@ -1488,7 +1769,7 @@ function lsp.buf_request_all(bufnr, method, params, callback)
local result_count = 0
local expected_result_count = 0
- local set_expected_result_count = once(function ()
+ local set_expected_result_count = once(function()
for_each_buffer_client(bufnr, function(client)
if client.supports_method(method) then
expected_result_count = expected_result_count + 1
@@ -1552,23 +1833,24 @@ end
---
---@returns true if any client returns true; false otherwise
function lsp.buf_notify(bufnr, method, params)
- validate {
- bufnr = { bufnr, 'n', true };
- method = { method, 's' };
- }
+ validate({
+ bufnr = { bufnr, 'n', true },
+ method = { method, 's' },
+ })
local resp = false
for_each_buffer_client(bufnr, function(client, _client_id, _resolved_bufnr)
- if client.rpc.notify(method, params) then resp = true end
+ if client.rpc.notify(method, params) then
+ resp = true
+ end
end)
return resp
end
-
---@private
local function adjust_start_col(lnum, line, items, encoding)
local min_start_char = nil
for _, item in pairs(items) do
- if item.textEdit and item.textEdit.range.start.line == lnum - 1 then
+ if item.filterText == nil and item.textEdit and item.textEdit.range.start.line == lnum - 1 then
if min_start_char and min_start_char ~= item.textEdit.range.start.character then
return nil
end
@@ -1595,7 +1877,7 @@ end
--- - findstart=0: column where the completion starts, or -2 or -3
--- - findstart=1: list of matches (actually just calls |complete()|)
function lsp.omnifunc(findstart, base)
- local _ = log.debug() and log.debug("omnifunc.findstart", { findstart = findstart, base = base })
+ local _ = log.debug() and log.debug('omnifunc.findstart', { findstart = findstart, base = base })
local bufnr = resolve_bufnr()
local has_buffer_clients = not tbl_isempty(all_buffer_active_clients[bufnr] or {})
@@ -1608,12 +1890,12 @@ function lsp.omnifunc(findstart, base)
end
-- Then, perform standard completion request
- local _ = log.info() and log.info("base ", base)
+ local _ = log.info() and log.info('base ', base)
- local pos = vim.api.nvim_win_get_cursor(0)
- local line = vim.api.nvim_get_current_line()
+ local pos = api.nvim_win_get_cursor(0)
+ local line = api.nvim_get_current_line()
local line_to_cursor = line:sub(1, pos[2])
- local _ = log.trace() and log.trace("omnifunc.line", pos, line)
+ local _ = log.trace() and log.trace('omnifunc.line', pos, line)
-- Get the start position of the current keyword
local textMatch = vim.fn.match(line_to_cursor, '\\k*$')
@@ -1622,7 +1904,9 @@ function lsp.omnifunc(findstart, base)
local items = {}
lsp.buf_request(bufnr, 'textDocument/completion', params, function(err, result, ctx)
- if err or not result or vim.fn.mode() ~= "i" then return end
+ if err or not result or vim.fn.mode() ~= 'i' then
+ return
+ end
-- Completion response items may be relative to a position different than `textMatch`.
-- Concrete example, with sumneko/lua-language-server:
@@ -1659,7 +1943,7 @@ end
---
--- Currently only supports a single client. This can be set via
--- `setlocal formatexpr=v:lua.vim.lsp.formatexpr()` but will typically or in `on_attach`
---- via `vim.api.nvim_buf_set_option(bufnr, 'formatexpr', 'v:lua.vim.lsp.formatexpr(#{timeout_ms:250})')`.
+--- via ``vim.api.nvim_buf_set_option(bufnr, 'formatexpr', 'v:lua.vim.lsp.formatexpr(#{timeout_ms:250})')``.
---
---@param opts table options for customizing the formatting expression which takes the
--- following optional keys:
@@ -1668,7 +1952,7 @@ function lsp.formatexpr(opts)
opts = opts or {}
local timeout_ms = opts.timeout_ms or 500
- if vim.tbl_contains({'i', 'R', 'ic', 'ix'}, vim.fn.mode()) then
+ if vim.tbl_contains({ 'i', 'R', 'ic', 'ix' }, vim.fn.mode()) then
-- `formatexpr` is also called when exceeding `textwidth` in insert mode
-- fall back to internal formatting
return 1
@@ -1679,19 +1963,24 @@ function lsp.formatexpr(opts)
if start_line > 0 and end_line > 0 then
local params = {
- textDocument = util.make_text_document_params();
+ textDocument = util.make_text_document_params(),
range = {
- start = { line = start_line - 1; character = 0; };
- ["end"] = { line = end_line - 1; character = 0; };
- };
- };
+ start = { line = start_line - 1, character = 0 },
+ ['end'] = { line = end_line - 1, character = 0 },
+ },
+ }
params.options = util.make_formatting_params().options
- local client_results = vim.lsp.buf_request_sync(0, "textDocument/rangeFormatting", params, timeout_ms)
+ local client_results =
+ vim.lsp.buf_request_sync(0, 'textDocument/rangeFormatting', params, timeout_ms)
-- Apply the text edits from one and only one of the clients.
- for _, response in pairs(client_results) do
+ for client_id, response in pairs(client_results) do
if response.result then
- vim.lsp.util.apply_text_edits(response.result, 0)
+ vim.lsp.util.apply_text_edits(
+ response.result,
+ 0,
+ vim.lsp.get_client_by_id(client_id).offset_encoding
+ )
return 0
end
end
@@ -1728,26 +2017,28 @@ end
--- is a |vim.lsp.client| object.
---
---@param bufnr (optional, number): Buffer handle, or 0 for current
+---@returns (table) Table of (client_id, client) pairs
+---@deprecated Use |vim.lsp.get_active_clients()| instead.
function lsp.buf_get_clients(bufnr)
- bufnr = resolve_bufnr(bufnr)
- local result = {}
- for_each_buffer_client(bufnr, function(client, client_id)
- result[client_id] = client
- end)
- return result
+ local result = {}
+ for _, client in ipairs(lsp.get_active_clients({ bufnr = resolve_bufnr(bufnr) })) do
+ result[client.id] = client
+ end
+ return result
end
-- Log level dictionary with reverse lookup as well.
--
-- Can be used to lookup the number from the name or the
-- name from the number.
--- Levels by name: "TRACE", "DEBUG", "INFO", "WARN", "ERROR"
+-- Levels by name: "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "OFF"
-- Level numbers begin with "TRACE" at 0
lsp.log_levels = log.levels
--- Sets the global log level for LSP logging.
---
---- Levels by name: "TRACE", "DEBUG", "INFO", "WARN", "ERROR"
+--- Levels by name: "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "OFF"
+---
--- Level numbers begin with "TRACE" at 0
---
--- Use `lsp.log_levels` for reverse lookup.
@@ -1759,7 +2050,7 @@ function lsp.set_log_level(level)
if type(level) == 'string' or type(level) == 'number' then
log.set_level(level)
else
- error(string.format("Invalid log level: %q", level))
+ error(string.format('Invalid log level: %q', level))
end
end
@@ -1789,7 +2080,7 @@ end
---@param override_config (table) Table containing the keys to override behavior of the {handler}
function lsp.with(handler, override_config)
return function(err, result, ctx, config)
- return handler(err, result, ctx, vim.tbl_deep_extend("force", config or {}, override_config))
+ return handler(err, result, ctx, vim.tbl_deep_extend('force', config or {}, override_config))
end
end
@@ -1804,12 +2095,16 @@ function lsp._with_extend(name, options, user_config)
local resulting_config = {}
for k, v in pairs(user_config) do
if options[k] == nil then
- error(debug.traceback(string.format(
- "Invalid option for `%s`: %s. Valid options are:\n%s",
- name,
- k,
- vim.inspect(vim.tbl_keys(options))
- )))
+ error(
+ debug.traceback(
+ string.format(
+ 'Invalid option for `%s`: %s. Valid options are:\n%s',
+ name,
+ k,
+ vim.inspect(vim.tbl_keys(options))
+ )
+ )
+ )
end
resulting_config[k] = v
@@ -1824,7 +2119,6 @@ function lsp._with_extend(name, options, user_config)
return resulting_config
end
-
--- Registry for client side commands.
--- This is an extension point for plugins to handle custom commands which are
--- not part of the core language server protocol specification.
@@ -1846,12 +2140,11 @@ end
--- The second argument is the `ctx` of |lsp-handler|
lsp.commands = setmetatable({}, {
__newindex = function(tbl, key, value)
- assert(type(key) == 'string', "The key for commands in `vim.lsp.commands` must be a string")
- assert(type(value) == 'function', "Command added to `vim.lsp.commands` must be a function")
+ assert(type(key) == 'string', 'The key for commands in `vim.lsp.commands` must be a string')
+ assert(type(value) == 'function', 'Command added to `vim.lsp.commands` must be a function')
rawset(tbl, key, value)
- end;
+ end,
})
-
return lsp
-- vim:sw=2 ts=2 et
diff --git a/runtime/lua/vim/lsp/_snippet.lua b/runtime/lua/vim/lsp/_snippet.lua
index 0140b0aee3..3488639fb4 100644
--- a/runtime/lua/vim/lsp/_snippet.lua
+++ b/runtime/lua/vim/lsp/_snippet.lua
@@ -41,7 +41,7 @@ P.take_until = function(targets, specials)
parsed = true,
value = {
raw = table.concat(raw, ''),
- esc = table.concat(esc, '')
+ esc = table.concat(esc, ''),
},
pos = new_pos,
}
@@ -156,10 +156,10 @@ P.seq = function(...)
return function(input, pos)
local values = {}
local new_pos = pos
- for _, parser in ipairs(parsers) do
+ for i, parser in ipairs(parsers) do
local result = parser(input, new_pos)
if result.parsed then
- table.insert(values, result.value)
+ values[i] = result.value
new_pos = result.pos
else
return P.unmatch(pos)
@@ -248,49 +248,122 @@ S.format = P.any(
capture_index = values[3],
}, Node)
end),
- P.map(P.seq(S.dollar, S.open, S.int, S.colon, S.slash, P.any(
- P.token('upcase'),
- P.token('downcase'),
- P.token('capitalize'),
- P.token('camelcase'),
- P.token('pascalcase')
- ), S.close), function(values)
- return setmetatable({
- type = Node.Type.FORMAT,
- capture_index = values[3],
- modifier = values[6],
- }, Node)
- end),
- P.map(P.seq(S.dollar, S.open, S.int, S.colon, P.any(
- P.seq(S.question, P.take_until({ ':' }, { '\\' }), S.colon, P.take_until({ '}' }, { '\\' })),
- P.seq(S.plus, P.take_until({ '}' }, { '\\' })),
- P.seq(S.minus, P.take_until({ '}' }, { '\\' }))
- ), S.close), function(values)
+ P.map(
+ P.seq(
+ S.dollar,
+ S.open,
+ S.int,
+ S.colon,
+ S.slash,
+ P.any(
+ P.token('upcase'),
+ P.token('downcase'),
+ P.token('capitalize'),
+ P.token('camelcase'),
+ P.token('pascalcase')
+ ),
+ S.close
+ ),
+ function(values)
+ return setmetatable({
+ type = Node.Type.FORMAT,
+ capture_index = values[3],
+ modifier = values[6],
+ }, Node)
+ end
+ ),
+ P.map(
+ P.seq(
+ S.dollar,
+ S.open,
+ S.int,
+ S.colon,
+ P.seq(
+ S.question,
+ P.opt(P.take_until({ ':' }, { '\\' })),
+ S.colon,
+ P.opt(P.take_until({ '}' }, { '\\' }))
+ ),
+ S.close
+ ),
+ function(values)
+ return setmetatable({
+ type = Node.Type.FORMAT,
+ capture_index = values[3],
+ if_text = values[5][2] and values[5][2].esc or '',
+ else_text = values[5][4] and values[5][4].esc or '',
+ }, Node)
+ end
+ ),
+ P.map(
+ P.seq(
+ S.dollar,
+ S.open,
+ S.int,
+ S.colon,
+ P.seq(S.plus, P.opt(P.take_until({ '}' }, { '\\' }))),
+ S.close
+ ),
+ function(values)
+ return setmetatable({
+ type = Node.Type.FORMAT,
+ capture_index = values[3],
+ if_text = values[5][2] and values[5][2].esc or '',
+ else_text = '',
+ }, Node)
+ end
+ ),
+ P.map(
+ P.seq(
+ S.dollar,
+ S.open,
+ S.int,
+ S.colon,
+ S.minus,
+ P.opt(P.take_until({ '}' }, { '\\' })),
+ S.close
+ ),
+ function(values)
+ return setmetatable({
+ type = Node.Type.FORMAT,
+ capture_index = values[3],
+ if_text = '',
+ else_text = values[6] and values[6].esc or '',
+ }, Node)
+ end
+ ),
+ P.map(
+ P.seq(S.dollar, S.open, S.int, S.colon, P.opt(P.take_until({ '}' }, { '\\' })), S.close),
+ function(values)
+ return setmetatable({
+ type = Node.Type.FORMAT,
+ capture_index = values[3],
+ if_text = '',
+ else_text = values[5] and values[5].esc or '',
+ }, Node)
+ end
+ )
+)
+
+S.transform = P.map(
+ P.seq(
+ S.slash,
+ P.take_until({ '/' }, { '\\' }),
+ S.slash,
+ P.many(P.any(S.format, S.text({ '$', '/' }, { '\\' }))),
+ S.slash,
+ P.opt(P.pattern('[ig]+'))
+ ),
+ function(values)
return setmetatable({
- type = Node.Type.FORMAT,
- capture_index = values[3],
- if_text = values[5][2].esc,
- else_text = (values[5][4] or {}).esc,
+ type = Node.Type.TRANSFORM,
+ pattern = values[2].raw,
+ format = values[4],
+ option = values[6],
}, Node)
- end)
+ end
)
-S.transform = P.map(P.seq(
- S.slash,
- P.take_until({ '/' }, { '\\' }),
- S.slash,
- P.many(P.any(S.format, S.text({ '$', '/' }, { '\\' }))),
- S.slash,
- P.opt(P.pattern('[ig]+'))
-), function(values)
- return setmetatable({
- type = Node.Type.TRANSFORM,
- pattern = values[2].raw,
- format = values[4],
- option = values[6],
- }, Node)
-end)
-
S.tabstop = P.any(
P.map(P.seq(S.dollar, S.int), function(values)
return setmetatable({
@@ -314,34 +387,52 @@ S.tabstop = P.any(
)
S.placeholder = P.any(
- P.map(P.seq(S.dollar, S.open, S.int, S.colon, P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\' }))), S.close), function(values)
- return setmetatable({
- type = Node.Type.PLACEHOLDER,
- tabstop = values[3],
- children = values[5],
- }, Node)
- end)
+ P.map(
+ P.seq(
+ S.dollar,
+ S.open,
+ S.int,
+ S.colon,
+ P.opt(P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\' })))),
+ S.close
+ ),
+ function(values)
+ return setmetatable({
+ type = Node.Type.PLACEHOLDER,
+ tabstop = values[3],
+ -- insert empty text if opt did not match.
+ children = values[5] or {
+ setmetatable({
+ type = Node.Type.TEXT,
+ raw = '',
+ esc = '',
+ }, Node),
+ },
+ }, Node)
+ end
+ )
)
-S.choice = P.map(P.seq(
- S.dollar,
- S.open,
- S.int,
- S.pipe,
- P.many(
- P.map(P.seq(S.text({ ',', '|' }), P.opt(S.comma)), function(values)
+S.choice = P.map(
+ P.seq(
+ S.dollar,
+ S.open,
+ S.int,
+ S.pipe,
+ P.many(P.map(P.seq(S.text({ ',', '|' }), P.opt(S.comma)), function(values)
return values[1].esc
- end)
+ end)),
+ S.pipe,
+ S.close
),
- S.pipe,
- S.close
-), function(values)
- return setmetatable({
- type = Node.Type.CHOICE,
- tabstop = values[3],
- items = values[5],
- }, Node)
-end)
+ function(values)
+ return setmetatable({
+ type = Node.Type.CHOICE,
+ tabstop = values[3],
+ items = values[5],
+ }, Node)
+ end
+)
S.variable = P.any(
P.map(P.seq(S.dollar, S.var), function(values)
@@ -363,13 +454,23 @@ S.variable = P.any(
transform = values[4],
}, Node)
end),
- P.map(P.seq(S.dollar, S.open, S.var, S.colon, P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\' }))), S.close), function(values)
- return setmetatable({
- type = Node.Type.VARIABLE,
- name = values[3],
- children = values[5],
- }, Node)
- end)
+ P.map(
+ P.seq(
+ S.dollar,
+ S.open,
+ S.var,
+ S.colon,
+ P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\' }))),
+ S.close
+ ),
+ function(values)
+ return setmetatable({
+ type = Node.Type.VARIABLE,
+ name = values[3],
+ children = values[5],
+ }, Node)
+ end
+ )
)
S.snippet = P.map(P.many(P.any(S.toplevel, S.text({ '$' }, { '}', '\\' }))), function(values)
diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua
index c1d777ae6c..50a51e897c 100644
--- a/runtime/lua/vim/lsp/buf.lua
+++ b/runtime/lua/vim/lsp/buf.lua
@@ -1,30 +1,12 @@
local vim = vim
+local api = vim.api
local validate = vim.validate
-local vfn = vim.fn
-local util = require 'vim.lsp.util'
+local util = require('vim.lsp.util')
+local npcall = vim.F.npcall
local M = {}
---@private
---- Returns nil if {status} is false or nil, otherwise returns the rest of the
---- arguments.
-local function ok_or_nil(status, ...)
- if not status then return end
- return ...
-end
-
----@private
---- Swallows errors.
----
----@param fn Function to run
----@param ... Function arguments
----@returns Result of `fn(...)` if there are no errors, otherwise nil.
---- Returns nil if errors occur during {fn}, otherwise returns
-local function npcall(fn, ...)
- return ok_or_nil(pcall(fn, ...))
-end
-
----@private
--- Sends an async request to all active clients attached to the current
--- buffer.
---
@@ -39,10 +21,10 @@ end
---
---@see |vim.lsp.buf_request()|
local function request(method, params, handler)
- validate {
- method = {method, 's'};
- handler = {handler, 'f', true};
- }
+ validate({
+ method = { method, 's' },
+ handler = { handler, 'f', true },
+ })
return vim.lsp.buf_request(0, method, params, handler)
end
@@ -51,7 +33,7 @@ end
---
---@returns `true` if server responds.
function M.server_ready()
- return not not vim.lsp.buf_notify(0, "window/progress", {})
+ return not not vim.lsp.buf_notify(0, 'window/progress', {})
end
--- Displays hover information about the symbol under the cursor in a floating
@@ -61,26 +43,45 @@ function M.hover()
request('textDocument/hover', params)
end
+---@private
+local function request_with_options(name, params, options)
+ local req_handler
+ if options then
+ req_handler = function(err, result, ctx, config)
+ local client = vim.lsp.get_client_by_id(ctx.client_id)
+ local handler = client.handlers[name] or vim.lsp.handlers[name]
+ handler(err, result, ctx, vim.tbl_extend('force', config or {}, options))
+ end
+ end
+ request(name, params, req_handler)
+end
+
--- Jumps to the declaration of the symbol under the cursor.
---@note Many servers do not implement this method. Generally, see |vim.lsp.buf.definition()| instead.
---
-function M.declaration()
+---@param options table|nil additional options
+--- - reuse_win: (boolean) Jump to existing window if buffer is already open.
+function M.declaration(options)
local params = util.make_position_params()
- request('textDocument/declaration', params)
+ request_with_options('textDocument/declaration', params, options)
end
--- Jumps to the definition of the symbol under the cursor.
---
-function M.definition()
+---@param options table|nil additional options
+--- - reuse_win: (boolean) Jump to existing window if buffer is already open.
+function M.definition(options)
local params = util.make_position_params()
- request('textDocument/definition', params)
+ request_with_options('textDocument/definition', params, options)
end
--- Jumps to the definition of the type of the symbol under the cursor.
---
-function M.type_definition()
+---@param options table|nil additional options
+--- - reuse_win: (boolean) Jump to existing window if buffer is already open.
+function M.type_definition(options)
local params = util.make_position_params()
- request('textDocument/typeDefinition', params)
+ request_with_options('textDocument/typeDefinition', params, options)
end
--- Lists all the implementations for the symbol under the cursor in the
@@ -117,9 +118,9 @@ end
--
---@returns The client that the user selected or nil
local function select_client(method, on_choice)
- validate {
+ validate({
on_choice = { on_choice, 'function', false },
- }
+ })
local clients = vim.tbl_values(vim.lsp.buf_get_clients())
clients = vim.tbl_filter(function(client)
return client.supports_method(method)
@@ -143,16 +144,105 @@ local function select_client(method, on_choice)
end
end
+--- Formats a buffer using the attached (and optionally filtered) language
+--- server clients.
+---
+--- @param options table|nil Optional table which holds the following optional fields:
+--- - formatting_options (table|nil):
+--- Can be used to specify FormattingOptions. Some unspecified options will be
+--- automatically derived from the current Neovim options.
+--- @see https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting
+--- - timeout_ms (integer|nil, default 1000):
+--- Time in milliseconds to block for formatting requests. No effect if async=true
+--- - bufnr (number|nil):
+--- Restrict formatting to the clients attached to the given buffer, defaults to the current
+--- buffer (0).
+---
+--- - filter (function|nil):
+--- Predicate used to filter clients. Receives a client as argument and must return a
+--- boolean. Clients matching the predicate are included. Example:
+---
+--- <pre>
+--- -- Never request typescript-language-server for formatting
+--- vim.lsp.buf.format {
+--- filter = function(client) return client.name ~= "tsserver" end
+--- }
+--- </pre>
+---
+--- - async boolean|nil
+--- If true the method won't block. Defaults to false.
+--- Editing the buffer while formatting asynchronous can lead to unexpected
+--- changes.
+---
+--- - id (number|nil):
+--- Restrict formatting to the client with ID (client.id) matching this field.
+--- - name (string|nil):
+--- Restrict formatting to the client with name (client.name) matching this field.
+
+function M.format(options)
+ options = options or {}
+ local bufnr = options.bufnr or api.nvim_get_current_buf()
+ local clients = vim.lsp.get_active_clients({
+ id = options.id,
+ bufnr = bufnr,
+ name = options.name,
+ })
+
+ if options.filter then
+ clients = vim.tbl_filter(options.filter, clients)
+ end
+
+ clients = vim.tbl_filter(function(client)
+ return client.supports_method('textDocument/formatting')
+ end, clients)
+
+ if #clients == 0 then
+ vim.notify('[LSP] Format request failed, no matching language servers.')
+ end
+
+ if options.async then
+ local do_format
+ do_format = function(idx, client)
+ if not client then
+ return
+ end
+ local params = util.make_formatting_params(options.formatting_options)
+ client.request('textDocument/formatting', params, function(...)
+ local handler = client.handlers['textDocument/formatting']
+ or vim.lsp.handlers['textDocument/formatting']
+ handler(...)
+ do_format(next(clients, idx))
+ end, bufnr)
+ end
+ do_format(next(clients))
+ else
+ local timeout_ms = options.timeout_ms or 1000
+ for _, client in pairs(clients) do
+ local params = util.make_formatting_params(options.formatting_options)
+ 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, client.offset_encoding)
+ elseif err then
+ vim.notify(string.format('[LSP][%s] %s', client.name, err), vim.log.levels.WARN)
+ end
+ end
+ end
+end
+
--- Formats the current buffer.
---
----@param options (optional, table) Can be used to specify FormattingOptions.
+---@param options (table|nil) Can be used to specify FormattingOptions.
--- Some unspecified options will be automatically derived from the current
--- Neovim options.
--
---@see https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting
function M.formatting(options)
+ vim.notify_once(
+ 'vim.lsp.buf.formatting is deprecated. Use vim.lsp.buf.format { async = true } instead',
+ vim.log.levels.WARN
+ )
local params = util.make_formatting_params(options)
- local bufnr = vim.api.nvim_get_current_buf()
+ local bufnr = api.nvim_get_current_buf()
select_client('textDocument/formatting', function(client)
if client == nil then
return
@@ -171,12 +261,16 @@ end
--- autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting_sync()
--- </pre>
---
----@param options Table with valid `FormattingOptions` entries
+---@param options table|nil with valid `FormattingOptions` entries
---@param timeout_ms (number) Request timeout
---@see |vim.lsp.buf.formatting_seq_sync|
function M.formatting_sync(options, timeout_ms)
+ vim.notify_once(
+ 'vim.lsp.buf.formatting_sync is deprecated. Use vim.lsp.buf.format instead',
+ vim.log.levels.WARN
+ )
local params = util.make_formatting_params(options)
- local bufnr = vim.api.nvim_get_current_buf()
+ local bufnr = api.nvim_get_current_buf()
select_client('textDocument/formatting', function(client)
if client == nil then
return
@@ -184,7 +278,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
@@ -202,14 +296,18 @@ end
--- vim.api.nvim_command[[autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting_seq_sync()]]
--- </pre>
---
----@param options (optional, table) `FormattingOptions` entries
----@param timeout_ms (optional, number) Request timeout
----@param order (optional, table) List of client names. Formatting is requested from clients
+---@param options (table|nil) `FormattingOptions` entries
+---@param timeout_ms (number|nil) Request timeout
+---@param order (table|nil) List of client names. Formatting is requested from clients
---in the following order: first all clients that are not in the `order` list, then
---the remaining clients in the order as they occur in the `order` list.
function M.formatting_seq_sync(options, timeout_ms, order)
- local clients = vim.tbl_values(vim.lsp.buf_get_clients());
- local bufnr = vim.api.nvim_get_current_buf()
+ vim.notify_once(
+ 'vim.lsp.buf.formatting_seq_sync is deprecated. Use vim.lsp.buf.format instead',
+ vim.log.levels.WARN
+ )
+ local clients = vim.tbl_values(vim.lsp.buf_get_clients())
+ local bufnr = api.nvim_get_current_buf()
-- sort the clients according to `order`
for _, client_name in pairs(order or {}) do
@@ -224,13 +322,21 @@ function M.formatting_seq_sync(options, timeout_ms, order)
-- loop through the clients and make synchronous formatting requests
for _, client in pairs(clients) do
- if client.resolved_capabilities.document_formatting then
+ if vim.tbl_get(client.server_capabilities, 'documentFormattingProvider') then
local params = util.make_formatting_params(options)
- local result, err = client.request_sync("textDocument/formatting", params, timeout_ms, vim.api.nvim_get_current_buf())
+ local result, err = client.request_sync(
+ 'textDocument/formatting',
+ params,
+ timeout_ms,
+ 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)
+ vim.notify(
+ string.format('vim.lsp.buf.formatting_seq_sync: (%s) %s', client.name, err),
+ vim.log.levels.WARN
+ )
end
end
end
@@ -257,50 +363,133 @@ end
--- Renames all references to the symbol under the cursor.
---
----@param new_name (string) If not provided, the user will be prompted for a new
----name using |vim.ui.input()|.
-function M.rename(new_name)
- local opts = {
- prompt = "New Name: "
- }
+---@param new_name string|nil If not provided, the user will be prompted for a new
+--- name using |vim.ui.input()|.
+---@param options table|nil additional options
+--- - filter (function|nil):
+--- Predicate used to filter clients. Receives a client as argument and
+--- must return a boolean. Clients matching the predicate are included.
+--- - name (string|nil):
+--- Restrict clients used for rename to ones where client.name matches
+--- this field.
+function M.rename(new_name, options)
+ options = options or {}
+ local bufnr = options.bufnr or api.nvim_get_current_buf()
+ local clients = vim.lsp.get_active_clients({
+ bufnr = bufnr,
+ name = options.name,
+ })
+ if options.filter then
+ clients = vim.tbl_filter(options.filter, clients)
+ end
- ---@private
- local function on_confirm(input)
- if not (input and #input > 0) then return end
- local params = util.make_position_params()
- params.newName = input
- request('textDocument/rename', params)
+ -- Clients must at least support rename, prepareRename is optional
+ clients = vim.tbl_filter(function(client)
+ return client.supports_method('textDocument/rename')
+ end, clients)
+
+ if #clients == 0 then
+ vim.notify('[LSP] Rename, no matching language servers with rename capability.')
end
+ local win = api.nvim_get_current_win()
+
+ -- Compute early to account for cursor movements after going async
+ local cword = vim.fn.expand('<cword>')
+
---@private
- local function prepare_rename(err, result)
- if err == nil and result == nil then
- vim.notify('nothing to rename', vim.log.levels.INFO)
+ local function get_text_at_range(range, offset_encoding)
+ return api.nvim_buf_get_text(
+ bufnr,
+ range.start.line,
+ util._get_line_byte_from_position(bufnr, range.start, offset_encoding),
+ range['end'].line,
+ util._get_line_byte_from_position(bufnr, range['end'], offset_encoding),
+ {}
+ )[1]
+ end
+
+ local try_use_client
+ try_use_client = function(idx, client)
+ if not client then
return
end
- if result and result.placeholder then
- opts.default = result.placeholder
- if not new_name then npcall(vim.ui.input, opts, on_confirm) end
- elseif result and result.start and result['end'] and
- result.start.line == result['end'].line then
- local line = vfn.getline(result.start.line+1)
- local start_char = result.start.character+1
- local end_char = result['end'].character
- opts.default = string.sub(line, start_char, end_char)
- if not new_name then npcall(vim.ui.input, opts, on_confirm) end
+
+ ---@private
+ local function rename(name)
+ local params = util.make_position_params(win, client.offset_encoding)
+ params.newName = name
+ local handler = client.handlers['textDocument/rename']
+ or vim.lsp.handlers['textDocument/rename']
+ client.request('textDocument/rename', params, function(...)
+ handler(...)
+ try_use_client(next(clients, idx))
+ end, bufnr)
+ end
+
+ if client.supports_method('textDocument/prepareRename') then
+ local params = util.make_position_params(win, client.offset_encoding)
+ client.request('textDocument/prepareRename', params, function(err, result)
+ if err or result == nil then
+ if next(clients, idx) then
+ try_use_client(next(clients, idx))
+ else
+ local msg = err and ('Error on prepareRename: ' .. (err.message or ''))
+ or 'Nothing to rename'
+ vim.notify(msg, vim.log.levels.INFO)
+ end
+ return
+ end
+
+ if new_name then
+ rename(new_name)
+ return
+ end
+
+ local prompt_opts = {
+ prompt = 'New Name: ',
+ }
+ -- result: Range | { range: Range, placeholder: string }
+ if result.placeholder then
+ prompt_opts.default = result.placeholder
+ elseif result.start then
+ prompt_opts.default = get_text_at_range(result, client.offset_encoding)
+ elseif result.range then
+ prompt_opts.default = get_text_at_range(result.range, client.offset_encoding)
+ else
+ prompt_opts.default = cword
+ end
+ vim.ui.input(prompt_opts, function(input)
+ if not input or #input == 0 then
+ return
+ end
+ rename(input)
+ end)
+ end, bufnr)
else
- -- fallback to guessing symbol using <cword>
- --
- -- this can happen if the language server does not support prepareRename,
- -- returns an unexpected response, or requests for "default behavior"
- --
- -- see https://microsoft.github.io/language-server-protocol/specification#textDocument_prepareRename
- opts.default = vfn.expand('<cword>')
- if not new_name then npcall(vim.ui.input, opts, on_confirm) end
+ assert(
+ client.supports_method('textDocument/rename'),
+ 'Client must support textDocument/rename'
+ )
+ if new_name then
+ rename(new_name)
+ return
+ end
+
+ local prompt_opts = {
+ prompt = 'New Name: ',
+ default = cword,
+ }
+ vim.ui.input(prompt_opts, function(input)
+ if not input or #input == 0 then
+ return
+ end
+ rename(input)
+ end)
end
- if new_name then on_confirm(new_name) end
end
- request('textDocument/prepareRename', util.make_position_params(), prepare_rename)
+
+ try_use_client(next(clients))
end
--- Lists all the references to the symbol under the cursor in the quickfix window.
@@ -308,10 +497,10 @@ end
---@param context (table) Context for the request
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references
function M.references(context)
- validate { context = { context, 't', true } }
+ validate({ context = { context, 't', true } })
local params = util.make_position_params()
params.context = context or {
- includeDeclaration = true;
+ includeDeclaration = true,
}
request('textDocument/references', params)
end
@@ -325,14 +514,16 @@ end
---@private
local function pick_call_hierarchy_item(call_hierarchy_items)
- if not call_hierarchy_items then return end
+ if not call_hierarchy_items then
+ return
+ end
if #call_hierarchy_items == 1 then
return call_hierarchy_items[1]
end
local items = {}
for i, item in pairs(call_hierarchy_items) do
local entry = item.detail or item.name
- table.insert(items, string.format("%d. %s", i, entry))
+ table.insert(items, string.format('%d. %s', i, entry))
end
local choice = vim.fn.inputlist(items)
if choice < 1 or choice > #items then
@@ -354,8 +545,8 @@ local function call_hierarchy(method)
if client then
client.request(method, { item = call_hierarchy_item }, nil, ctx.bufnr)
else
- vim.notify(string.format(
- 'Client with id=%d disappeared during call hierarchy request', ctx.client_id),
+ vim.notify(
+ string.format('Client with id=%d disappeared during call hierarchy request', ctx.client_id),
vim.log.levels.WARN
)
end
@@ -391,20 +582,26 @@ end
--- Add the folder at path to the workspace folders. If {path} is
--- not provided, the user will be prompted for a path using |input()|.
function M.add_workspace_folder(workspace_folder)
- workspace_folder = workspace_folder or npcall(vfn.input, "Workspace Folder: ", vfn.expand('%:p:h'), 'dir')
- vim.api.nvim_command("redraw")
- if not (workspace_folder and #workspace_folder > 0) then return end
+ workspace_folder = workspace_folder
+ or npcall(vim.fn.input, 'Workspace Folder: ', vim.fn.expand('%:p:h'), 'dir')
+ api.nvim_command('redraw')
+ if not (workspace_folder and #workspace_folder > 0) then
+ return
+ end
if vim.fn.isdirectory(workspace_folder) == 0 then
- print(workspace_folder, " is not a valid directory")
+ print(workspace_folder, ' is not a valid directory')
return
end
- local params = util.make_workspace_params({{uri = vim.uri_from_fname(workspace_folder); name = workspace_folder}}, {{}})
+ local params = util.make_workspace_params(
+ { { uri = vim.uri_from_fname(workspace_folder), name = workspace_folder } },
+ { {} }
+ )
for _, client in pairs(vim.lsp.buf_get_clients()) do
local found = false
for _, folder in pairs(client.workspace_folders or {}) do
if folder.name == workspace_folder then
found = true
- print(workspace_folder, "is already part of this workspace")
+ print(workspace_folder, 'is already part of this workspace')
break
end
end
@@ -422,10 +619,16 @@ end
--- {path} is not provided, the user will be prompted for
--- a path using |input()|.
function M.remove_workspace_folder(workspace_folder)
- workspace_folder = workspace_folder or npcall(vfn.input, "Workspace Folder: ", vfn.expand('%:p:h'))
- vim.api.nvim_command("redraw")
- if not (workspace_folder and #workspace_folder > 0) then return end
- local params = util.make_workspace_params({{}}, {{uri = vim.uri_from_fname(workspace_folder); name = workspace_folder}})
+ workspace_folder = workspace_folder
+ or npcall(vim.fn.input, 'Workspace Folder: ', vim.fn.expand('%:p:h'))
+ api.nvim_command('redraw')
+ if not (workspace_folder and #workspace_folder > 0) then
+ return
+ end
+ local params = util.make_workspace_params(
+ { {} },
+ { { uri = vim.uri_from_fname(workspace_folder), name = workspace_folder } }
+ )
for _, client in pairs(vim.lsp.buf_get_clients()) do
for idx, folder in pairs(client.workspace_folders) do
if folder.name == workspace_folder then
@@ -435,7 +638,7 @@ function M.remove_workspace_folder(workspace_folder)
end
end
end
- print(workspace_folder, "is not currently part of the workspace")
+ print(workspace_folder, 'is not currently part of the workspace')
end
--- Lists all symbols in the current workspace in the quickfix window.
@@ -446,8 +649,11 @@ end
---
---@param query (string, optional)
function M.workspace_symbol(query)
- query = query or npcall(vfn.input, "Query: ")
- local params = {query = query}
+ query = query or npcall(vim.fn.input, 'Query: ')
+ if query == nil then
+ return
+ end
+ local params = { query = query }
request('workspace/symbol', params)
end
@@ -477,7 +683,6 @@ function M.clear_references()
util.buf_clear_references()
end
-
---@private
--
--- This is not public because the main extension point is
@@ -488,11 +693,42 @@ end
--- from multiple clients to have 1 single UI prompt for the user, yet we still
--- need to be able to link a `CodeAction|Command` to the right client for
--- `codeAction/resolve`
-local function on_code_action_results(results, ctx)
+local function on_code_action_results(results, ctx, options)
local action_tuples = {}
+
+ ---@private
+ local function action_filter(a)
+ -- filter by specified action kind
+ if options and options.context and options.context.only then
+ if not a.kind then
+ return false
+ end
+ local found = false
+ for _, o in ipairs(options.context.only) do
+ -- action kinds are hierarchical with . as a separator: when requesting only
+ -- 'quickfix' this filter allows both 'quickfix' and 'quickfix.foo', for example
+ if a.kind:find('^' .. o .. '$') or a.kind:find('^' .. o .. '%.') then
+ found = true
+ break
+ end
+ end
+ if not found then
+ return false
+ end
+ end
+ -- filter by user function
+ if options and options.filter and not options.filter(a) then
+ return false
+ end
+ -- no filter removed this action
+ return true
+ end
+
for client_id, result in pairs(results) do
for _, action in pairs(result.result or {}) do
- table.insert(action_tuples, { client_id, action })
+ if action_filter(action) then
+ table.insert(action_tuples, { client_id, action })
+ end
end
end
if #action_tuples == 0 then
@@ -503,7 +739,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
@@ -513,7 +749,14 @@ local function on_code_action_results(results, ctx)
enriched_ctx.client_id = client.id
fn(command, enriched_ctx)
else
- M.execute_command(command)
+ -- Not using command directly to exclude extra properties,
+ -- see https://github.com/python-lsp/python-lsp-server/issues/146
+ local params = {
+ command = command.command,
+ arguments = command.arguments,
+ workDoneToken = command.workDoneToken,
+ }
+ client.request('workspace/executeCommand', params, nil, ctx.bufnr)
end
end
end
@@ -537,11 +780,11 @@ local function on_code_action_results(results, ctx)
--
local client = vim.lsp.get_client_by_id(action_tuple[1])
local action = action_tuple[2]
- if not action.edit
- and client
- and type(client.resolved_capabilities.code_action) == 'table'
- and client.resolved_capabilities.code_action.resolveProvider then
-
+ if
+ not action.edit
+ and client
+ and vim.tbl_get(client.server_capabilities, 'codeActionProvider', 'resolveProvider')
+ then
client.request('codeAction/resolve', action, function(err, resolved_action)
if err then
vim.notify(err.code .. ': ' .. err.message, vim.log.levels.ERROR)
@@ -554,6 +797,13 @@ local function on_code_action_results(results, ctx)
end
end
+ -- If options.apply is given, and there are just one remaining code action,
+ -- apply it directly without querying the user.
+ if options and options.apply and #action_tuples == 1 then
+ on_user_choice(action_tuples[1])
+ return
+ end
+
vim.ui.select(action_tuples, {
prompt = 'Code actions:',
kind = 'codeaction',
@@ -564,39 +814,53 @@ local function on_code_action_results(results, ctx)
}, on_user_choice)
end
-
--- Requests code actions from all clients and calls the handler exactly once
--- with all aggregated results
---@private
-local function code_action_request(params)
- local bufnr = vim.api.nvim_get_current_buf()
+local function code_action_request(params, options)
+ local bufnr = api.nvim_get_current_buf()
local method = 'textDocument/codeAction'
vim.lsp.buf_request_all(bufnr, method, params, function(results)
- on_code_action_results(results, { bufnr = bufnr, method = method, params = params })
+ local ctx = { bufnr = bufnr, method = method, params = params }
+ on_code_action_results(results, ctx, options)
end)
end
--- Selects a code action available at the current
--- cursor position.
---
----@param context table|nil `CodeActionContext` of the LSP specification:
---- - diagnostics: (table|nil)
---- LSP `Diagnostic[]`. Inferred from the current
---- position if not provided.
---- - only: (string|nil)
---- LSP `CodeActionKind` used to filter the code actions.
---- Most language servers support values like `refactor`
---- or `quickfix`.
+---@param options table|nil Optional table which holds the following optional fields:
+--- - context (table|nil):
+--- Corresponds to `CodeActionContext` of the LSP specification:
+--- - diagnostics (table|nil):
+--- LSP `Diagnostic[]`. Inferred from the current
+--- position if not provided.
+--- - only (table|nil):
+--- List of LSP `CodeActionKind`s used to filter the code actions.
+--- Most language servers support values like `refactor`
+--- or `quickfix`.
+--- - filter (function|nil):
+--- Predicate function taking an `CodeAction` and returning a boolean.
+--- - apply (boolean|nil):
+--- When set to `true`, and there is just one remaining action
+--- (after filtering), the action is applied without user query.
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction
-function M.code_action(context)
- validate { context = { context, 't', true } }
- context = context or {}
+function M.code_action(options)
+ validate({ options = { options, 't', true } })
+ options = options or {}
+ -- Detect old API call code_action(context) which should now be
+ -- code_action({ context = context} )
+ if options.diagnostics or options.only then
+ options = { options = options }
+ end
+ local context = options.context or {}
if not context.diagnostics then
- context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics()
+ local bufnr = api.nvim_get_current_buf()
+ context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics(bufnr)
end
local params = util.make_range_params()
params.context = context
- code_action_request(params)
+ code_action_request(params, options)
end
--- Performs |vim.lsp.buf.code_action()| for a given range.
@@ -606,8 +870,8 @@ end
--- - diagnostics: (table|nil)
--- LSP `Diagnostic[]`. Inferred from the current
--- position if not provided.
---- - only: (string|nil)
---- LSP `CodeActionKind` used to filter the code actions.
+--- - only: (table|nil)
+--- List of LSP `CodeActionKind`s used to filter the code actions.
--- Most language servers support values like `refactor`
--- or `quickfix`.
---@param start_pos ({number, number}, optional) mark-indexed position.
@@ -615,10 +879,11 @@ end
---@param end_pos ({number, number}, optional) mark-indexed position.
---Defaults to the end of the last visual selection.
function M.range_code_action(context, start_pos, end_pos)
- validate { context = { context, 't', true } }
+ validate({ context = { context, 't', true } })
context = context or {}
if not context.diagnostics then
- context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics()
+ local bufnr = api.nvim_get_current_buf()
+ context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics(bufnr)
end
local params = util.make_given_range_params(start_pos, end_pos)
params.context = context
@@ -630,16 +895,16 @@ end
---@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_params)
- validate {
+ validate({
command = { command_params.command, 's' },
- arguments = { command_params.arguments, 't', true }
- }
+ arguments = { command_params.arguments, 't', true },
+ })
command_params = {
- command=command_params.command,
- arguments=command_params.arguments,
- workDoneToken=command_params.workDoneToken,
+ command = command_params.command,
+ arguments = command_params.arguments,
+ workDoneToken = command_params.workDoneToken,
}
- request('workspace/executeCommand', command_params )
+ request('workspace/executeCommand', command_params)
end
return M
diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua
index 9eb64c9a2e..4fa02c8db2 100644
--- a/runtime/lua/vim/lsp/codelens.lua
+++ b/runtime/lua/vim/lsp/codelens.lua
@@ -1,4 +1,5 @@
local util = require('vim.lsp.util')
+local log = require('vim.lsp.log')
local api = vim.api
local M = {}
@@ -11,7 +12,7 @@ local lens_cache_by_buf = setmetatable({}, {
__index = function(t, b)
local key = b > 0 and b or api.nvim_get_current_buf()
return rawget(t, key)
- end
+ end,
})
local namespaces = setmetatable({}, {
@@ -19,13 +20,12 @@ local namespaces = setmetatable({}, {
local value = api.nvim_create_namespace('vim_lsp_codelens:' .. key)
rawset(t, key, value)
return value
- end;
+ end,
})
---@private
M.__namespaces = namespaces
-
---@private
local function execute_lens(lens, bufnr, client_id)
local line = lens.range.start.line
@@ -43,10 +43,14 @@ local function execute_lens(lens, bufnr, client_id)
local command_provider = client.server_capabilities.executeCommandProvider
local commands = type(command_provider) == 'table' and command_provider.commands or {}
if not vim.tbl_contains(commands, command.command) then
- vim.notify(string.format(
- "Language server does not support command `%s`. This command may require a client extension.", command.command),
- vim.log.levels.WARN)
- return
+ vim.notify(
+ string.format(
+ 'Language server does not support command `%s`. This command may require a client extension.',
+ command.command
+ ),
+ vim.log.levels.WARN
+ )
+ return
end
client.request('workspace/executeCommand', command, function(...)
local result = vim.lsp.handlers['workspace/executeCommand'](...)
@@ -55,14 +59,15 @@ local function execute_lens(lens, bufnr, client_id)
end, bufnr)
end
-
--- Return all lenses for the given buffer
---
---@param bufnr number Buffer number. 0 can be used for the current buffer.
---@return table (`CodeLens[]`)
function M.get(bufnr)
local lenses_by_client = lens_cache_by_buf[bufnr or 0]
- if not lenses_by_client then return {} end
+ if not lenses_by_client then
+ return {}
+ end
local lenses = {}
for _, client_lenses in pairs(lenses_by_client) do
vim.list_extend(lenses, client_lenses)
@@ -70,7 +75,6 @@ function M.get(bufnr)
return lenses
end
-
--- Run the code lens in the current line
---
function M.run()
@@ -81,7 +85,7 @@ function M.run()
for client, lenses in pairs(lenses_by_client) do
for _, lens in pairs(lenses) do
if lens.range.start.line == (line - 1) then
- table.insert(options, {client=client, lens=lens})
+ table.insert(options, { client = client, lens = lens })
end
end
end
@@ -104,7 +108,6 @@ function M.run()
end
end
-
--- Display the lenses using virtual text
---
---@param lenses table of lenses to display (`CodeLens[] | null`)
@@ -130,21 +133,25 @@ function M.display(lenses, bufnr, client_id)
api.nvim_buf_clear_namespace(bufnr, ns, i, i + 1)
local chunks = {}
local num_line_lenses = #line_lenses
+ table.sort(line_lenses, function(a, b)
+ return a.range.start.character < b.range.start.character
+ end)
for j, lens in ipairs(line_lenses) do
local text = lens.command and lens.command.title or 'Unresolved lens ...'
- table.insert(chunks, {text, 'LspCodeLens' })
+ table.insert(chunks, { text, 'LspCodeLens' })
if j < num_line_lenses then
- table.insert(chunks, {' | ', 'LspCodeLensSeparator' })
+ table.insert(chunks, { ' | ', 'LspCodeLensSeparator' })
end
end
if #chunks > 0 then
- api.nvim_buf_set_extmark(bufnr, ns, i, 0, { virt_text = chunks,
- hl_mode="combine" })
+ api.nvim_buf_set_extmark(bufnr, ns, i, 0, {
+ virt_text = chunks,
+ hl_mode = 'combine',
+ })
end
end
end
-
--- Store lenses for a specific buffer and client
---
---@param lenses table of lenses to store (`CodeLens[] | null`)
@@ -157,16 +164,17 @@ function M.save(lenses, bufnr, client_id)
lens_cache_by_buf[bufnr] = lenses_by_client
local ns = namespaces[client_id]
api.nvim_buf_attach(bufnr, false, {
- on_detach = function(b) lens_cache_by_buf[b] = nil end,
+ on_detach = function(b)
+ lens_cache_by_buf[b] = nil
+ end,
on_lines = function(_, b, _, first_lnum, last_lnum)
api.nvim_buf_clear_namespace(b, ns, first_lnum, last_lnum)
- end
+ end,
})
end
lenses_by_client[client_id] = lenses
end
-
---@private
local function resolve_lenses(lenses, bufnr, client_id, callback)
lenses = lenses or {}
@@ -200,8 +208,7 @@ local function resolve_lenses(lenses, bufnr, client_id, callback)
ns,
lens.range.start.line,
0,
- { virt_text = {{ lens.command.title, 'LspCodeLens' }},
- hl_mode="combine" }
+ { virt_text = { { lens.command.title, 'LspCodeLens' } }, hl_mode = 'combine' }
)
end
countdown()
@@ -210,11 +217,14 @@ local function resolve_lenses(lenses, bufnr, client_id, callback)
end
end
-
--- |lsp-handler| for the method `textDocument/codeLens`
---
function M.on_codelens(err, result, ctx, _)
- assert(not err, vim.inspect(err))
+ if err then
+ active_refreshes[ctx.bufnr] = nil
+ local _ = log.error() and log.error('codelens', err)
+ return
+ end
M.save(result, ctx.bufnr, ctx.client_id)
@@ -222,12 +232,11 @@ function M.on_codelens(err, result, ctx, _)
-- once resolved.
M.display(result, ctx.bufnr, ctx.client_id)
resolve_lenses(result, ctx.bufnr, ctx.client_id, function()
- M.display(result, ctx.bufnr, ctx.client_id)
active_refreshes[ctx.bufnr] = nil
+ M.display(result, ctx.bufnr, ctx.client_id)
end)
end
-
--- Refresh the codelens for the current buffer
---
--- It is recommended to trigger this using an autocmd or via keymap.
@@ -238,15 +247,14 @@ end
---
function M.refresh()
local params = {
- textDocument = util.make_text_document_params()
+ textDocument = util.make_text_document_params(),
}
local bufnr = api.nvim_get_current_buf()
if active_refreshes[bufnr] then
return
end
active_refreshes[bufnr] = true
- vim.lsp.buf_request(0, 'textDocument/codeLens', params)
+ vim.lsp.buf_request(0, 'textDocument/codeLens', params, M.on_codelens)
end
-
return M
diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua
index f38b469f3c..1f9d084e2b 100644
--- a/runtime/lua/vim/lsp/diagnostic.lua
+++ b/runtime/lua/vim/lsp/diagnostic.lua
@@ -22,17 +22,6 @@ local function get_client_id(client_id)
end
---@private
-local function get_bufnr(bufnr)
- if not bufnr then
- return vim.api.nvim_get_current_buf()
- elseif bufnr == 0 then
- return vim.api.nvim_get_current_buf()
- end
-
- return bufnr
-end
-
----@private
local function severity_lsp_to_vim(severity)
if type(severity) == 'string' then
severity = vim.lsp.protocol.DiagnosticSeverity[severity]
@@ -50,12 +39,12 @@ end
---@private
local function line_byte_from_position(lines, lnum, col, offset_encoding)
- if not lines or offset_encoding == "utf-8" then
+ if not lines or offset_encoding == 'utf-8' then
return col
end
local line = lines[lnum + 1]
- local ok, result = pcall(vim.str_byteindex, line, col, offset_encoding == "utf-16")
+ local ok, result = pcall(vim.str_byteindex, line, col, offset_encoding == 'utf-16')
if ok then
return result
end
@@ -75,7 +64,7 @@ local function get_buf_lines(bufnr)
return
end
- local content = f:read("*a")
+ local content = f:read('*a')
if not content then
-- Some LSP servers report diagnostics at a directory level, in which case
-- io.read() returns nil
@@ -83,7 +72,7 @@ local function get_buf_lines(bufnr)
return
end
- local lines = vim.split(content, "\n")
+ local lines = vim.split(content, '\n')
f:close()
return lines
end
@@ -92,10 +81,10 @@ end
local function diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)
local buf_lines = get_buf_lines(bufnr)
local client = vim.lsp.get_client_by_id(client_id)
- local offset_encoding = client and client.offset_encoding or "utf-16"
+ local offset_encoding = client and client.offset_encoding or 'utf-16'
return vim.tbl_map(function(diagnostic)
local start = diagnostic.range.start
- local _end = diagnostic.range["end"]
+ local _end = diagnostic.range['end']
return {
lnum = start.line,
col = line_byte_from_position(buf_lines, start.line, start.character, offset_encoding),
@@ -104,8 +93,10 @@ local function diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)
severity = severity_lsp_to_vim(diagnostic.severity),
message = diagnostic.message,
source = diagnostic.source,
+ code = diagnostic.code,
user_data = {
lsp = {
+ -- usage of user_data.lsp.code is deprecated in favor of the top-level code field
code = diagnostic.code,
codeDescription = diagnostic.codeDescription,
tags = diagnostic.tags,
@@ -120,13 +111,14 @@ end
---@private
local function diagnostic_vim_to_lsp(diagnostics)
return vim.tbl_map(function(diagnostic)
- return vim.tbl_extend("error", {
+ return vim.tbl_extend('keep', {
+ -- "keep" the below fields over any duplicate fields in diagnostic.user_data.lsp
range = {
start = {
line = diagnostic.lnum,
character = diagnostic.col,
},
- ["end"] = {
+ ['end'] = {
line = diagnostic.end_lnum,
character = diagnostic.end_col,
},
@@ -134,6 +126,7 @@ local function diagnostic_vim_to_lsp(diagnostics)
severity = severity_vim_to_lsp(diagnostic.severity),
message = diagnostic.message,
source = diagnostic.source,
+ code = diagnostic.code,
}, diagnostic.user_data and (diagnostic.user_data.lsp or {}) or {})
end, diagnostics)
end
@@ -144,10 +137,10 @@ local _client_namespaces = {}
---
---@param client_id number The id of the LSP client
function M.get_namespace(client_id)
- vim.validate { client_id = { client_id, 'n' } }
+ vim.validate({ client_id = { client_id, 'n' } })
if not _client_namespaces[client_id] then
local client = vim.lsp.get_client_by_id(client_id)
- local name = string.format("vim.lsp.%s.%d", client and client.name or "unknown", client_id)
+ local name = string.format('vim.lsp.%s.%d', client and client.name or 'unknown', client_id)
_client_namespaces[client_id] = vim.api.nvim_create_namespace(name)
end
return _client_namespaces[client_id]
@@ -168,8 +161,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,
@@ -181,7 +174,12 @@ end
function M.on_publish_diagnostics(_, result, ctx, config)
local client_id = ctx.client_id
local uri = result.uri
- local bufnr = vim.uri_to_bufnr(uri)
+ local fname = vim.uri_to_fname(uri)
+ local diagnostics = result.diagnostics
+ if #diagnostics == 0 and vim.fn.bufexists(fname) == 0 then
+ return
+ end
+ local bufnr = vim.fn.bufadd(fname)
if not bufnr then
return
@@ -189,13 +187,12 @@ function M.on_publish_diagnostics(_, result, ctx, config)
client_id = get_client_id(client_id)
local namespace = M.get_namespace(client_id)
- local diagnostics = result.diagnostics
if config then
for _, opt in pairs(config) do
if type(opt) == 'table' then
if not opt.severity and opt.severity_limit then
- opt.severity = {min=severity_lsp_to_vim(opt.severity_limit)}
+ opt.severity = { min = severity_lsp_to_vim(opt.severity_limit) }
end
end
end
@@ -230,72 +227,6 @@ function M.reset(client_id, buffer_client_map)
end)
end
--- Deprecated Functions {{{
-
-
---- Save diagnostics to the current buffer.
----
----@deprecated Prefer |vim.diagnostic.set()|
----
---- Handles saving diagnostics from multiple clients in the same buffer.
----@param diagnostics Diagnostic[]
----@param bufnr number
----@param client_id number
----@private
-function M.save(diagnostics, bufnr, client_id)
- vim.notify_once('vim.lsp.diagnostic.save is deprecated. See :h deprecated', vim.log.levels.WARN)
- local namespace = M.get_namespace(client_id)
- vim.diagnostic.set(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id))
-end
--- }}}
-
---- Get all diagnostics for clients
----
----@deprecated Prefer |vim.diagnostic.get()|
----
----@param client_id number Restrict included diagnostics to the client
---- If nil, diagnostics of all clients are included.
----@return table with diagnostics grouped by bufnr (bufnr: Diagnostic[])
-function M.get_all(client_id)
- vim.notify_once('vim.lsp.diagnostic.get_all is deprecated. See :h deprecated', vim.log.levels.WARN)
- local result = {}
- local namespace
- if client_id then
- namespace = M.get_namespace(client_id)
- end
- for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do
- local diagnostics = diagnostic_vim_to_lsp(vim.diagnostic.get(bufnr, {namespace = namespace}))
- result[bufnr] = diagnostics
- end
- return result
-end
-
---- Return associated diagnostics for bufnr
----
----@deprecated Prefer |vim.diagnostic.get()|
----
----@param bufnr number
----@param client_id number|nil If nil, then return all of the diagnostics.
---- Else, return just the diagnostics associated with the client_id.
----@param predicate function|nil Optional function for filtering diagnostics
-function M.get(bufnr, client_id, predicate)
- vim.notify_once('vim.lsp.diagnostic.get is deprecated. See :h deprecated', vim.log.levels.WARN)
- predicate = predicate or function() return true end
- if client_id == nil then
- local all_diagnostics = {}
- vim.lsp.for_each_buffer_client(bufnr, function(_, iter_client_id, _)
- local iter_diagnostics = vim.tbl_filter(predicate, M.get(bufnr, iter_client_id))
- for _, diagnostic in ipairs(iter_diagnostics) do
- table.insert(all_diagnostics, diagnostic)
- end
- end)
- return all_diagnostics
- end
-
- local namespace = M.get_namespace(client_id)
- return diagnostic_vim_to_lsp(vim.tbl_filter(predicate, vim.diagnostic.get(bufnr, {namespace=namespace})))
-end
-
--- Get the diagnostics by line
---
--- Marked private as this is used internally by the LSP subsystem, but
@@ -317,7 +248,7 @@ function M.get_line_diagnostics(bufnr, line_nr, opts, client_id)
if opts.severity then
opts.severity = severity_lsp_to_vim(opts.severity)
elseif opts.severity_limit then
- opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)}
+ opts.severity = { min = severity_lsp_to_vim(opts.severity_limit) }
end
if client_id then
@@ -333,390 +264,4 @@ function M.get_line_diagnostics(bufnr, line_nr, opts, client_id)
return diagnostic_vim_to_lsp(vim.diagnostic.get(bufnr, opts))
end
---- Get the counts for a particular severity
----
----@deprecated Prefer |vim.diagnostic.get_count()|
----
----@param bufnr number The buffer number
----@param severity DiagnosticSeverity
----@param client_id number the client id
-function M.get_count(bufnr, severity, client_id)
- vim.notify_once('vim.lsp.diagnostic.get_count is deprecated. See :h deprecated', vim.log.levels.WARN)
- severity = severity_lsp_to_vim(severity)
- local opts = { severity = severity }
- if client_id ~= nil then
- opts.namespace = M.get_namespace(client_id)
- end
-
- return #vim.diagnostic.get(bufnr, opts)
-end
-
---- Get the previous diagnostic closest to the cursor_position
----
----@deprecated Prefer |vim.diagnostic.get_prev()|
----
----@param opts table See |vim.lsp.diagnostic.goto_next()|
----@return table Previous diagnostic
-function M.get_prev(opts)
- vim.notify_once('vim.lsp.diagnostic.get_prev is deprecated. See :h deprecated', vim.log.levels.WARN)
- if opts then
- if opts.severity then
- opts.severity = severity_lsp_to_vim(opts.severity)
- elseif opts.severity_limit then
- opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)}
- end
- end
- return diagnostic_vim_to_lsp({vim.diagnostic.get_prev(opts)})[1]
-end
-
---- Return the pos, {row, col}, for the prev diagnostic in the current buffer.
----
----@deprecated Prefer |vim.diagnostic.get_prev_pos()|
----
----@param opts table See |vim.lsp.diagnostic.goto_next()|
----@return table Previous diagnostic position
-function M.get_prev_pos(opts)
- vim.notify_once('vim.lsp.diagnostic.get_prev_pos is deprecated. See :h deprecated', vim.log.levels.WARN)
- if opts then
- if opts.severity then
- opts.severity = severity_lsp_to_vim(opts.severity)
- elseif opts.severity_limit then
- opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)}
- end
- end
- return vim.diagnostic.get_prev_pos(opts)
-end
-
---- Move to the previous diagnostic
----
----@deprecated Prefer |vim.diagnostic.goto_prev()|
----
----@param opts table See |vim.lsp.diagnostic.goto_next()|
-function M.goto_prev(opts)
- vim.notify_once('vim.lsp.diagnostic.goto_prev is deprecated. See :h deprecated', vim.log.levels.WARN)
- if opts then
- if opts.severity then
- opts.severity = severity_lsp_to_vim(opts.severity)
- elseif opts.severity_limit then
- opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)}
- end
- end
- return vim.diagnostic.goto_prev(opts)
-end
-
---- Get the next diagnostic closest to the cursor_position
----
----@deprecated Prefer |vim.diagnostic.get_next()|
----
----@param opts table See |vim.lsp.diagnostic.goto_next()|
----@return table Next diagnostic
-function M.get_next(opts)
- vim.notify_once('vim.lsp.diagnostic.get_next is deprecated. See :h deprecated', vim.log.levels.WARN)
- if opts then
- if opts.severity then
- opts.severity = severity_lsp_to_vim(opts.severity)
- elseif opts.severity_limit then
- opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)}
- end
- end
- return diagnostic_vim_to_lsp({vim.diagnostic.get_next(opts)})[1]
-end
-
---- Return the pos, {row, col}, for the next diagnostic in the current buffer.
----
----@deprecated Prefer |vim.diagnostic.get_next_pos()|
----
----@param opts table See |vim.lsp.diagnostic.goto_next()|
----@return table Next diagnostic position
-function M.get_next_pos(opts)
- vim.notify_once('vim.lsp.diagnostic.get_next_pos is deprecated. See :h deprecated', vim.log.levels.WARN)
- if opts then
- if opts.severity then
- opts.severity = severity_lsp_to_vim(opts.severity)
- elseif opts.severity_limit then
- opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)}
- end
- end
- return vim.diagnostic.get_next_pos(opts)
-end
-
---- Move to the next diagnostic
----
----@deprecated Prefer |vim.diagnostic.goto_next()|
-function M.goto_next(opts)
- vim.notify_once('vim.lsp.diagnostic.goto_next is deprecated. See :h deprecated', vim.log.levels.WARN)
- if opts then
- if opts.severity then
- opts.severity = severity_lsp_to_vim(opts.severity)
- elseif opts.severity_limit then
- opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)}
- end
- end
- return vim.diagnostic.goto_next(opts)
-end
-
---- Set signs for given diagnostics
----
----@deprecated Prefer |vim.diagnostic._set_signs()|
----
----@param diagnostics Diagnostic[]
----@param bufnr number The buffer number
----@param client_id number the client id
----@param sign_ns number|nil
----@param opts table Configuration for signs. Keys:
---- - priority: Set the priority of the signs.
---- - severity_limit (DiagnosticSeverity):
---- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid.
-function M.set_signs(diagnostics, bufnr, client_id, _, opts)
- vim.notify_once('vim.lsp.diagnostic.set_signs is deprecated. See :h deprecated', vim.log.levels.WARN)
- local namespace = M.get_namespace(client_id)
- if opts and not opts.severity and opts.severity_limit then
- opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)}
- end
-
- vim.diagnostic._set_signs(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id), opts)
-end
-
---- Set underline for given diagnostics
----
----@deprecated Prefer |vim.diagnostic._set_underline()|
----
----@param diagnostics Diagnostic[]
----@param bufnr number: The buffer number
----@param client_id number: The client id
----@param diagnostic_ns number|nil: The namespace
----@param opts table: Configuration table:
---- - severity_limit (DiagnosticSeverity):
---- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid.
-function M.set_underline(diagnostics, bufnr, client_id, _, opts)
- vim.notify_once('vim.lsp.diagnostic.set_underline is deprecated. See :h deprecated', vim.log.levels.WARN)
- local namespace = M.get_namespace(client_id)
- if opts and not opts.severity and opts.severity_limit then
- opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)}
- end
- return vim.diagnostic._set_underline(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id), opts)
-end
-
---- Set virtual text given diagnostics
----
----@deprecated Prefer |vim.diagnostic._set_virtual_text()|
----
----@param diagnostics Diagnostic[]
----@param bufnr number
----@param client_id number
----@param diagnostic_ns number
----@param opts table Options on how to display virtual text. Keys:
---- - prefix (string): Prefix to display before virtual text on line
---- - spacing (number): Number of spaces to insert before virtual text
---- - severity_limit (DiagnosticSeverity):
---- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid.
-function M.set_virtual_text(diagnostics, bufnr, client_id, _, opts)
- vim.notify_once('vim.lsp.diagnostic.set_virtual_text is deprecated. See :h deprecated', vim.log.levels.WARN)
- local namespace = M.get_namespace(client_id)
- if opts and not opts.severity and opts.severity_limit then
- opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)}
- end
- return vim.diagnostic._set_virtual_text(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id), opts)
-end
-
---- Default function to get text chunks to display using |nvim_buf_set_extmark()|.
----
----@deprecated Prefer |vim.diagnostic.get_virt_text_chunks()|
----
----@param bufnr number The buffer to display the virtual text in
----@param line number The line number to display the virtual text on
----@param line_diags Diagnostic[] The diagnostics associated with the line
----@param opts table See {opts} from |vim.lsp.diagnostic.set_virtual_text()|
----@return an array of [text, hl_group] arrays. This can be passed directly to
---- the {virt_text} option of |nvim_buf_set_extmark()|.
-function M.get_virtual_text_chunks_for_line(bufnr, _, line_diags, opts)
- vim.notify_once('vim.lsp.diagnostic.get_virtual_text_chunks_for_line is deprecated. See :h deprecated', vim.log.levels.WARN)
- return vim.diagnostic._get_virt_text_chunks(diagnostic_lsp_to_vim(line_diags, bufnr), opts)
-end
-
---- Open a floating window with the diagnostics from {position}
----
----@deprecated Prefer |vim.diagnostic.show_position_diagnostics()|
----
----@param opts table|nil Configuration keys
---- - severity: (DiagnosticSeverity, default nil)
---- - Only return diagnostics with this severity. Overrides severity_limit
---- - severity_limit: (DiagnosticSeverity, default nil)
---- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid.
---- - all opts for |show_diagnostics()| can be used here
----@param buf_nr number|nil The buffer number
----@param position table|nil The (0,0)-indexed position
----@return table {popup_bufnr, win_id}
-function M.show_position_diagnostics(opts, buf_nr, position)
- vim.notify_once('vim.lsp.diagnostic.show_position_diagnostics is deprecated. See :h deprecated', vim.log.levels.WARN)
- opts = opts or {}
- opts.scope = "cursor"
- opts.pos = position
- if opts.severity then
- opts.severity = severity_lsp_to_vim(opts.severity)
- elseif opts.severity_limit then
- opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)}
- end
- return vim.diagnostic.open_float(buf_nr, opts)
-end
-
---- Open a floating window with the diagnostics from {line_nr}
----
----@deprecated Prefer |vim.diagnostic.open_float()|
----
----@param opts table Configuration table
---- - all opts for |vim.lsp.diagnostic.get_line_diagnostics()| and
---- |show_diagnostics()| can be used here
----@param buf_nr number|nil The buffer number
----@param line_nr number|nil The line number
----@param client_id number|nil the client id
----@return table {popup_bufnr, win_id}
-function M.show_line_diagnostics(opts, buf_nr, line_nr, client_id)
- vim.notify_once('vim.lsp.diagnostic.show_line_diagnostics is deprecated. See :h deprecated', vim.log.levels.WARN)
- opts = opts or {}
- opts.scope = "line"
- opts.pos = line_nr
- if client_id then
- opts.namespace = M.get_namespace(client_id)
- end
- return vim.diagnostic.open_float(buf_nr, opts)
-end
-
---- Redraw diagnostics for the given buffer and client
----
----@deprecated Prefer |vim.diagnostic.show()|
----
---- This calls the "textDocument/publishDiagnostics" handler manually using
---- the cached diagnostics already received from the server. This can be useful
---- for redrawing diagnostics after making changes in diagnostics
---- configuration. |lsp-handler-configuration|
----
----@param bufnr (optional, number): Buffer handle, defaults to current
----@param client_id (optional, number): Redraw diagnostics for the given
---- client. The default is to redraw diagnostics for all attached
---- clients.
-function M.redraw(bufnr, client_id)
- vim.notify_once('vim.lsp.diagnostic.redraw is deprecated. See :h deprecated', vim.log.levels.WARN)
- bufnr = get_bufnr(bufnr)
- if not client_id then
- return vim.lsp.for_each_buffer_client(bufnr, function(client)
- M.redraw(bufnr, client.id)
- end)
- end
-
- local namespace = M.get_namespace(client_id)
- return vim.diagnostic.show(namespace, bufnr)
-end
-
---- Sets the quickfix list
----
----@deprecated Prefer |vim.diagnostic.setqflist()|
----
----@param opts table|nil Configuration table. Keys:
---- - {open}: (boolean, default true)
---- - Open quickfix list after set
---- - {client_id}: (number)
---- - If nil, will consider all clients attached to buffer.
---- - {severity}: (DiagnosticSeverity)
---- - Exclusive severity to consider. Overrides {severity_limit}
---- - {severity_limit}: (DiagnosticSeverity)
---- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid.
---- - {workspace}: (boolean, default true)
---- - Set the list with workspace diagnostics
-function M.set_qflist(opts)
- vim.notify_once('vim.lsp.diagnostic.set_qflist is deprecated. See :h deprecated', vim.log.levels.WARN)
- opts = opts or {}
- if opts.severity then
- opts.severity = severity_lsp_to_vim(opts.severity)
- elseif opts.severity_limit then
- opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)}
- end
- if opts.client_id then
- opts.client_id = nil
- opts.namespace = M.get_namespace(opts.client_id)
- end
- local workspace = vim.F.if_nil(opts.workspace, true)
- opts.bufnr = not workspace and 0
- return vim.diagnostic.setqflist(opts)
-end
-
---- Sets the location list
----
----@deprecated Prefer |vim.diagnostic.setloclist()|
----
----@param opts table|nil Configuration table. Keys:
---- - {open}: (boolean, default true)
---- - Open loclist after set
---- - {client_id}: (number)
---- - If nil, will consider all clients attached to buffer.
---- - {severity}: (DiagnosticSeverity)
---- - Exclusive severity to consider. Overrides {severity_limit}
---- - {severity_limit}: (DiagnosticSeverity)
---- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid.
---- - {workspace}: (boolean, default false)
---- - Set the list with workspace diagnostics
-function M.set_loclist(opts)
- vim.notify_once('vim.lsp.diagnostic.set_loclist is deprecated. See :h deprecated', vim.log.levels.WARN)
- opts = opts or {}
- if opts.severity then
- opts.severity = severity_lsp_to_vim(opts.severity)
- elseif opts.severity_limit then
- opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)}
- end
- if opts.client_id then
- opts.client_id = nil
- opts.namespace = M.get_namespace(opts.client_id)
- end
- local workspace = vim.F.if_nil(opts.workspace, false)
- opts.bufnr = not workspace and 0
- return vim.diagnostic.setloclist(opts)
-end
-
---- Disable diagnostics for the given buffer and client
----
----@deprecated Prefer |vim.diagnostic.disable()|
----
----@param bufnr (optional, number): Buffer handle, defaults to current
----@param client_id (optional, number): Disable diagnostics for the given
---- client. The default is to disable diagnostics for all attached
---- clients.
--- Note that when diagnostics are disabled for a buffer, the server will still
--- send diagnostic information and the client will still process it. The
--- diagnostics are simply not displayed to the user.
-function M.disable(bufnr, client_id)
- vim.notify_once('vim.lsp.diagnostic.disable is deprecated. See :h deprecated', vim.log.levels.WARN)
- if not client_id then
- return vim.lsp.for_each_buffer_client(bufnr, function(client)
- M.disable(bufnr, client.id)
- end)
- end
-
- bufnr = get_bufnr(bufnr)
- local namespace = M.get_namespace(client_id)
- return vim.diagnostic.disable(bufnr, namespace)
-end
-
---- Enable diagnostics for the given buffer and client
----
----@deprecated Prefer |vim.diagnostic.enable()|
----
----@param bufnr (optional, number): Buffer handle, defaults to current
----@param client_id (optional, number): Enable diagnostics for the given
---- client. The default is to enable diagnostics for all attached
---- clients.
-function M.enable(bufnr, client_id)
- vim.notify_once('vim.lsp.diagnostic.enable is deprecated. See :h deprecated', vim.log.levels.WARN)
- if not client_id then
- return vim.lsp.for_each_buffer_client(bufnr, function(client)
- M.enable(bufnr, client.id)
- end)
- end
-
- bufnr = get_bufnr(bufnr)
- local namespace = M.get_namespace(client_id)
- return vim.diagnostic.enable(bufnr, namespace)
-end
-
--- }}}
-
return M
diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua
index a48302cc4b..3b869d8f5c 100644
--- a/runtime/lua/vim/lsp/handlers.lua
+++ b/runtime/lua/vim/lsp/handlers.lua
@@ -1,6 +1,6 @@
-local log = require 'vim.lsp.log'
-local protocol = require 'vim.lsp.protocol'
-local util = require 'vim.lsp.util'
+local log = require('vim.lsp.log')
+local protocol = require('vim.lsp.protocol')
+local util = require('vim.lsp.util')
local vim = vim
local api = vim.api
@@ -12,8 +12,8 @@ local M = {}
--- Writes to error buffer.
---@param ... (table of strings) Will be concatenated before being written
local function err_message(...)
- vim.notify(table.concat(vim.tbl_flatten{...}), vim.log.levels.ERROR)
- api.nvim_command("redraw")
+ vim.notify(table.concat(vim.tbl_flatten({ ... })), vim.log.levels.ERROR)
+ api.nvim_command('redraw')
end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand
@@ -25,51 +25,56 @@ end
local function progress_handler(_, result, ctx, _)
local client_id = ctx.client_id
local client = vim.lsp.get_client_by_id(client_id)
- local client_name = client and client.name or string.format("id=%d", client_id)
+ local client_name = client and client.name or string.format('id=%d', client_id)
if not client then
- err_message("LSP[", client_name, "] client has shut down after sending the message")
+ err_message('LSP[', client_name, '] client has shut down during progress update')
return vim.NIL
end
- local val = result.value -- unspecified yet
- local token = result.token -- string or number
-
+ local val = result.value -- unspecified yet
+ local token = result.token -- string or number
+ if type(val) ~= 'table' then
+ val = { content = val }
+ end
if val.kind then
if val.kind == 'begin' then
client.messages.progress[token] = {
title = val.title,
+ cancellable = val.cancellable,
message = val.message,
percentage = val.percentage,
}
elseif val.kind == 'report' then
- client.messages.progress[token].message = val.message;
- client.messages.progress[token].percentage = val.percentage;
+ client.messages.progress[token].cancellable = val.cancellable
+ client.messages.progress[token].message = val.message
+ client.messages.progress[token].percentage = val.percentage
elseif val.kind == 'end' then
if client.messages.progress[token] == nil then
- err_message("LSP[", client_name, "] received `end` message with no corresponding `begin`")
+ err_message('LSP[', client_name, '] received `end` message with no corresponding `begin`')
else
client.messages.progress[token].message = val.message
client.messages.progress[token].done = true
end
end
else
- table.insert(client.messages, {content = val, show_once = true, shown = 0})
+ client.messages.progress[token] = val
+ client.messages.progress[token].done = true
end
- vim.api.nvim_command("doautocmd <nomodeline> User LspProgressUpdate")
+ api.nvim_exec_autocmds('User', { pattern = 'LspProgressUpdate', modeline = false })
end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#progress
M['$/progress'] = progress_handler
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_workDoneProgress_create
-M['window/workDoneProgress/create'] = function(_, result, ctx)
+M['window/workDoneProgress/create'] = function(_, result, ctx)
local client_id = ctx.client_id
local client = vim.lsp.get_client_by_id(client_id)
- local token = result.token -- string or number
- local client_name = client and client.name or string.format("id=%d", client_id)
+ local token = result.token -- string or number
+ local client_name = client and client.name or string.format('id=%d', client_id)
if not client then
- err_message("LSP[", client_name, "] client has shut down after sending the message")
+ err_message('LSP[', client_name, '] client has shut down while creating progress report')
return vim.NIL
end
client.messages.progress[token] = {}
@@ -78,20 +83,19 @@ end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_showMessageRequest
M['window/showMessageRequest'] = function(_, result)
-
local actions = result.actions
print(result.message)
- local option_strings = {result.message, "\nRequest Actions:"}
+ local option_strings = { result.message, '\nRequest Actions:' }
for i, action in ipairs(actions) do
local title = action.title:gsub('\r\n', '\\r\\n')
title = title:gsub('\n', '\\n')
- table.insert(option_strings, string.format("%d. %s", i, title))
+ table.insert(option_strings, string.format('%d. %s', i, title))
end
-- window/showMessageRequest can return either MessageActionItem[] or null.
local choice = vim.fn.inputlist(option_strings)
if choice < 1 or choice > #actions then
- return vim.NIL
+ return vim.NIL
else
return actions[choice]
end
@@ -100,27 +104,32 @@ end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#client_registerCapability
M['client/registerCapability'] = function(_, _, ctx)
local client_id = ctx.client_id
- local warning_tpl = "The language server %s triggers a registerCapability "..
- "handler despite dynamicRegistration set to false. "..
- "Report upstream, this warning is harmless"
+ local warning_tpl = 'The language server %s triggers a registerCapability '
+ .. 'handler despite dynamicRegistration set to false. '
+ .. 'Report upstream, this warning is harmless'
local client = vim.lsp.get_client_by_id(client_id)
- local client_name = client and client.name or string.format("id=%d", client_id)
+ local client_name = client and client.name or string.format('id=%d', client_id)
local warning = string.format(warning_tpl, client_name)
log.warn(warning)
return vim.NIL
end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit
-M['workspace/applyEdit'] = function(_, workspace_edit)
- if not workspace_edit then return end
+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)
+ 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;
+ applied = status,
+ failureReason = result,
}
end
@@ -129,7 +138,11 @@ M['workspace/configuration'] = function(_, result, ctx)
local client_id = ctx.client_id
local client = vim.lsp.get_client_by_id(client_id)
if not client then
- err_message("LSP[id=", client_id, "] client has shut down after sending the message")
+ err_message(
+ 'LSP[',
+ client_id,
+ '] client has shut down after sending a workspace/configuration request'
+ )
return
end
if not result.items then
@@ -139,7 +152,7 @@ M['workspace/configuration'] = function(_, result, ctx)
local response = {}
for _, item in ipairs(result.items) do
if item.section then
- local value = util.lookup_section(client.config.settings, item.section) or vim.NIL
+ local value = util.lookup_section(client.config.settings, item.section)
-- For empty sections with no explicit '' key, return settings as is
if value == vim.NIL and item.section == '' then
value = client.config.settings or vim.NIL
@@ -150,6 +163,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
@@ -158,7 +182,30 @@ M['textDocument/codeLens'] = function(...)
return require('vim.lsp.codelens').on_codelens(...)
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,69 +216,88 @@ 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';
- items = map_result(result, ctx.bufnr);
+ title = title_fn(ctx),
+ items = map_result(result, ctx.bufnr),
+ context = ctx,
})
- api.nvim_command("lopen")
+ api.nvim_command('lopen')
else
vim.fn.setqflist({}, ' ', {
- title = 'Language Server';
- items = map_result(result, ctx.bufnr);
+ title = title_fn(ctx),
+ items = map_result(result, ctx.bufnr),
+ context = ctx,
})
- api.nvim_command("botright copen")
+ api.nvim_command('botright copen')
end
end
end
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, _)
- if not result then return end
- util.apply_workspace_edit(result)
+M['textDocument/rename'] = function(_, result, ctx, _)
+ if not result then
+ return
+ end
+ 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)
+ if not result then
+ return
+ end
+ 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)
+ if not result then
+ return
+ end
+ 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
M['textDocument/completion'] = function(_, result, _, _)
- if vim.tbl_isempty(result or {}) then return end
+ if vim.tbl_isempty(result or {}) then
+ return
+ end
local row, col = unpack(api.nvim_win_get_cursor(0))
- local line = assert(api.nvim_buf_get_lines(0, row-1, row, false)[1])
- local line_to_cursor = line:sub(col+1)
+ local line = assert(api.nvim_buf_get_lines(0, row - 1, row, false)[1])
+ local line_to_cursor = line:sub(col + 1)
local textMatch = vim.fn.match(line_to_cursor, '\\k*$')
- local prefix = line_to_cursor:sub(textMatch+1)
+ local prefix = line_to_cursor:sub(textMatch + 1)
local matches = util.text_document_completion_list_to_complete_items(result, prefix)
- vim.fn.complete(textMatch+1, matches)
+ vim.fn.complete(textMatch + 1, matches)
end
--- |lsp-handler| for the method "textDocument/hover"
@@ -251,16 +317,16 @@ function M.hover(_, result, ctx, config)
config = config or {}
config.focus_id = ctx.method
if not (result and result.contents) then
- -- return { 'No information available' }
+ vim.notify('No information available')
return
end
local markdown_lines = util.convert_input_to_markdown_lines(result.contents)
markdown_lines = util.trim_empty_lines(markdown_lines)
if vim.tbl_isempty(markdown_lines) then
- -- return { 'No information available' }
+ vim.notify('No information available')
return
end
- return util.open_floating_preview(markdown_lines, "markdown", config)
+ return util.open_floating_preview(markdown_lines, 'markdown', config)
end
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover
@@ -272,24 +338,30 @@ M['textDocument/hover'] = M.hover
---@param result (table) result of LSP method; a location or a list of locations.
---@param ctx (table) table containing the context of the request, including the method
---(`textDocument/definition` can return `Location` or `Location[]`
-local function location_handler(_, result, ctx, _)
+local function location_handler(_, result, ctx, config)
if result == nil or vim.tbl_isempty(result) then
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)
+
+ config = config or {}
-- 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, config.reuse_win)
if #result > 1 then
- vim.fn.setqflist({}, ' ', {title = 'LSP locations', items = util.locations_to_items(result)})
- api.nvim_command("copen")
+ vim.fn.setqflist({}, ' ', {
+ title = 'LSP locations',
+ items = util.locations_to_items(result, client.offset_encoding),
+ })
+ api.nvim_command('botright copen')
end
else
- util.jump_to_location(result)
+ util.jump_to_location(result, client.offset_encoding, config.reuse_win)
end
end
@@ -328,7 +400,8 @@ function M.signature_help(_, result, ctx, config)
return
end
local client = vim.lsp.get_client_by_id(ctx.client_id)
- local triggers = client.resolved_capabilities.signature_help_trigger_characters
+ local triggers =
+ vim.tbl_get(client.server_capabilities, 'signatureHelpProvider', 'triggerCharacters')
local ft = api.nvim_buf_get_option(ctx.bufnr, 'filetype')
local lines, hl = util.convert_signature_help_to_markdown_lines(result, ft, triggers)
lines = util.trim_empty_lines(lines)
@@ -338,9 +411,9 @@ function M.signature_help(_, result, ctx, config)
end
return
end
- local fbuf, fwin = util.open_floating_preview(lines, "markdown", config)
+ local fbuf, fwin = util.open_floating_preview(lines, 'markdown', config)
if hl then
- api.nvim_buf_add_highlight(fbuf, -1, "LspSignatureActiveParameter", 0, unpack(hl))
+ api.nvim_buf_add_highlight(fbuf, -1, 'LspSignatureActiveParameter', 0, unpack(hl))
end
return fbuf, fwin
end
@@ -350,10 +423,14 @@ M['textDocument/signatureHelp'] = M.signature_help
--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentHighlight
M['textDocument/documentHighlight'] = function(_, result, ctx, _)
- if not result then return end
+ if not result then
+ return
+ end
local client_id = ctx.client_id
local client = vim.lsp.get_client_by_id(client_id)
- if not client then return end
+ if not client then
+ return
+ end
util.buf_highlight_references(ctx.bufnr, result, client.offset_encoding)
end
@@ -366,7 +443,9 @@ end
---@returns `CallHierarchyOutgoingCall[]` if {direction} is `"to"`,
local make_call_hierarchy_handler = function(direction)
return function(_, result)
- if not result then return end
+ if not result then
+ return
+ end
local items = {}
for _, call_hierarchy_call in pairs(result) do
local call_hierarchy_item = call_hierarchy_call[direction]
@@ -379,8 +458,8 @@ local make_call_hierarchy_handler = function(direction)
})
end
end
- vim.fn.setqflist({}, ' ', {title = 'LSP call hierarchy', items = items})
- api.nvim_command("copen")
+ vim.fn.setqflist({}, ' ', { title = 'LSP call hierarchy', items = items })
+ api.nvim_command('botright copen')
end
end
@@ -396,15 +475,15 @@ M['window/logMessage'] = function(_, result, ctx, _)
local message = result.message
local client_id = ctx.client_id
local client = vim.lsp.get_client_by_id(client_id)
- local client_name = client and client.name or string.format("id=%d", client_id)
+ local client_name = client and client.name or string.format('id=%d', client_id)
if not client then
- err_message("LSP[", client_name, "] client has shut down after sending the message")
+ err_message('LSP[', client_name, '] client has shut down after sending ', message)
end
if message_type == protocol.MessageType.Error then
log.error(message)
elseif message_type == protocol.MessageType.Warning then
log.warn(message)
- elseif message_type == protocol.MessageType.Info or message_type == protocol.MessageType.Log then
+ elseif message_type == protocol.MessageType.Info or message_type == protocol.MessageType.Log then
log.info(message)
else
log.debug(message)
@@ -418,15 +497,15 @@ M['window/showMessage'] = function(_, result, ctx, _)
local message = result.message
local client_id = ctx.client_id
local client = vim.lsp.get_client_by_id(client_id)
- local client_name = client and client.name or string.format("id=%d", client_id)
+ local client_name = client and client.name or string.format('id=%d', client_id)
if not client then
- err_message("LSP[", client_name, "] client has shut down after sending the message")
+ err_message('LSP[', client_name, '] client has shut down after sending ', message)
end
if message_type == protocol.MessageType.Error then
- err_message("LSP[", client_name, "] ", message)
+ err_message('LSP[', client_name, '] ', message)
else
local message_type_name = protocol.MessageType[message_type]
- api.nvim_out_write(string.format("LSP[%s][%s] %s\n", client_name, message_type_name, message))
+ api.nvim_out_write(string.format('LSP[%s][%s] %s\n', client_name, message_type_name, message))
end
return result
end
@@ -434,9 +513,13 @@ end
-- Add boilerplate error validation and logging for all of these.
for k, fn in pairs(M) do
M[k] = function(err, result, ctx, config)
- local _ = log.trace() and log.trace('default_handler', ctx.method, {
- err = err, result = result, ctx=vim.inspect(ctx), config = config
- })
+ local _ = log.trace()
+ and log.trace('default_handler', ctx.method, {
+ err = err,
+ result = result,
+ ctx = vim.inspect(ctx),
+ config = config,
+ })
if err then
-- LSP spec:
@@ -448,7 +531,7 @@ for k, fn in pairs(M) do
-- Per LSP, don't show ContentModified error to the user.
if err.code ~= protocol.ErrorCodes.ContentModified then
local client = vim.lsp.get_client_by_id(ctx.client_id)
- local client_name = client and client.name or string.format("client_id=%d", ctx.client_id)
+ local client_name = client and client.name or string.format('client_id=%d', ctx.client_id)
err_message(client_name .. ': ' .. tostring(err.code) .. ': ' .. err.message)
end
diff --git a/runtime/lua/vim/lsp/health.lua b/runtime/lua/vim/lsp/health.lua
index ed3eea59df..ba730e3d6d 100644
--- a/runtime/lua/vim/lsp/health.lua
+++ b/runtime/lua/vim/lsp/health.lua
@@ -8,20 +8,25 @@ function M.check()
local log = require('vim.lsp.log')
local current_log_level = log.get_level()
local log_level_string = log.levels[current_log_level]
- report_info(string.format("LSP log level : %s", log_level_string))
+ report_info(string.format('LSP log level : %s', log_level_string))
if current_log_level < log.levels.WARN then
- report_warn(string.format("Log level %s will cause degraded performance and high disk usage", log_level_string))
+ report_warn(
+ string.format(
+ 'Log level %s will cause degraded performance and high disk usage',
+ log_level_string
+ )
+ )
end
local log_path = vim.lsp.get_log_path()
- report_info(string.format("Log path: %s", log_path))
+ report_info(string.format('Log path: %s', log_path))
- local log_size = vim.loop.fs_stat(log_path).size
+ local log_file = vim.loop.fs_stat(log_path)
+ local log_size = log_file and log_file.size or 0
local report_fn = (log_size / 1000000 > 100 and report_warn or report_info)
- report_fn(string.format("Log size: %d KB", log_size / 1000 ))
+ report_fn(string.format('Log size: %d KB', log_size / 1000))
end
return M
-
diff --git a/runtime/lua/vim/lsp/log.lua b/runtime/lua/vim/lsp/log.lua
index e0b5653587..6c6ba0f206 100644
--- a/runtime/lua/vim/lsp/log.lua
+++ b/runtime/lua/vim/lsp/log.lua
@@ -8,22 +8,29 @@ local log = {}
-- Log level dictionary with reverse lookup as well.
--
-- Can be used to lookup the number from the name or the name from the number.
--- Levels by name: "TRACE", "DEBUG", "INFO", "WARN", "ERROR"
+-- Levels by name: "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "OFF"
-- Level numbers begin with "TRACE" at 0
log.levels = vim.deepcopy(vim.log.levels)
-- Default log level is warn.
local current_log_level = log.levels.WARN
-local log_date_format = "%F %H:%M:%S"
-local format_func = function(arg) return vim.inspect(arg, {newline=''}) end
+local log_date_format = '%F %H:%M:%S'
+local format_func = function(arg)
+ return vim.inspect(arg, { newline = '' })
+end
do
- local path_sep = vim.loop.os_uname().version:match("Windows") and "\\" or "/"
+ local path_sep = vim.loop.os_uname().version:match('Windows') and '\\' or '/'
---@private
local function path_join(...)
- return table.concat(vim.tbl_flatten{...}, path_sep)
+ return table.concat(vim.tbl_flatten({ ... }), path_sep)
end
- local logfilename = path_join(vim.fn.stdpath('cache'), 'lsp.log')
+ local logfilename = path_join(vim.fn.stdpath('log'), 'lsp.log')
+
+ -- TODO: Ideally the directory should be created in open_logfile(), right
+ -- before opening the log file, but open_logfile() can be called from libuv
+ -- callbacks, where using fn.mkdir() is not allowed.
+ vim.fn.mkdir(vim.fn.stdpath('log'), 'p')
--- Returns the log filename.
---@returns (string) log filename
@@ -31,21 +38,40 @@ do
return logfilename
end
- vim.fn.mkdir(vim.fn.stdpath('cache'), "p")
- local logfile = assert(io.open(logfilename, "a+"))
-
- local log_info = vim.loop.fs_stat(logfilename)
- if log_info and log_info.size > 1e9 then
- local warn_msg = string.format(
- "LSP client log is large (%d MB): %s",
- log_info.size / (1000 * 1000),
- logfilename
- )
- vim.notify(warn_msg)
+ local logfile, openerr
+ ---@private
+ --- Opens log file. Returns true if file is open, false on error
+ local function open_logfile()
+ -- Try to open file only once
+ if logfile then
+ return true
+ end
+ if openerr then
+ return false
+ end
+
+ logfile, openerr = io.open(logfilename, 'a+')
+ if not logfile then
+ local err_msg = string.format('Failed to open LSP client log file: %s', openerr)
+ vim.notify(err_msg, vim.log.levels.ERROR)
+ return false
+ end
+
+ local log_info = vim.loop.fs_stat(logfilename)
+ if log_info and log_info.size > 1e9 then
+ local warn_msg = string.format(
+ 'LSP client log is large (%d MB): %s',
+ log_info.size / (1000 * 1000),
+ logfilename
+ )
+ vim.notify(warn_msg)
+ end
+
+ -- Start message for logging
+ logfile:write(string.format('[START][%s] LSP logging initiated\n', os.date(log_date_format)))
+ return true
end
- -- Start message for logging
- logfile:write(string.format("[START][%s] LSP logging initiated\n", os.date(log_date_format)))
for level, levelnr in pairs(log.levels) do
-- Also export the log level on the root object.
log[level] = levelnr
@@ -63,23 +89,38 @@ do
-- ```
--
-- This way you can avoid string allocations if the log level isn't high enough.
- log[level:lower()] = function(...)
- local argc = select("#", ...)
- if levelnr < current_log_level then return false end
- if argc == 0 then return true end
- local info = debug.getinfo(2, "Sl")
- local header = string.format("[%s][%s] ...%s:%s", level, os.date(log_date_format), string.sub(info.short_src, #info.short_src - 15), info.currentline)
- local parts = { header }
- for i = 1, argc do
- local arg = select(i, ...)
- if arg == nil then
- table.insert(parts, "nil")
- else
- table.insert(parts, format_func(arg))
+ if level ~= 'OFF' then
+ log[level:lower()] = function(...)
+ local argc = select('#', ...)
+ if levelnr < current_log_level then
+ return false
+ end
+ if argc == 0 then
+ return true
+ end
+ if not open_logfile() then
+ return false
+ end
+ local info = debug.getinfo(2, 'Sl')
+ local header = string.format(
+ '[%s][%s] ...%s:%s',
+ level,
+ os.date(log_date_format),
+ string.sub(info.short_src, #info.short_src - 15),
+ info.currentline
+ )
+ local parts = { header }
+ for i = 1, argc do
+ local arg = select(i, ...)
+ if arg == nil then
+ table.insert(parts, 'nil')
+ else
+ table.insert(parts, format_func(arg))
+ end
end
+ logfile:write(table.concat(parts, '\t'), '\n')
+ logfile:flush()
end
- logfile:write(table.concat(parts, '\t'), "\n")
- logfile:flush()
end
end
end
@@ -92,10 +133,11 @@ vim.tbl_add_reverse_lookup(log.levels)
---@param level (string or number) One of `vim.lsp.log.levels`
function log.set_level(level)
if type(level) == 'string' then
- current_log_level = assert(log.levels[level:upper()], string.format("Invalid log level: %q", level))
+ current_log_level =
+ assert(log.levels[level:upper()], string.format('Invalid log level: %q', level))
else
- assert(type(level) == 'number', "level must be a number or string")
- assert(log.levels[level], string.format("Invalid log level: %d", level))
+ assert(type(level) == 'number', 'level must be a number or string')
+ assert(log.levels[level], string.format('Invalid log level: %d', level))
current_log_level = level
end
end
@@ -109,7 +151,7 @@ end
--- Sets formatting function used to format logs
---@param handle function function to apply to logging arguments, pass vim.inspect for multi-line formatting
function log.set_format_func(handle)
- assert(handle == vim.inspect or type(handle) == 'function', "handle must be a function")
+ assert(handle == vim.inspect or type(handle) == 'function', 'handle must be a function')
format_func = handle
end
diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua
index 86c9e2fd58..6ecb9959d5 100644
--- a/runtime/lua/vim/lsp/protocol.lua
+++ b/runtime/lua/vim/lsp/protocol.lua
@@ -1,7 +1,5 @@
-- Protocol for the Microsoft Language Server Protocol (mslsp)
-local if_nil = vim.F.if_nil
-
local protocol = {}
--[=[
@@ -25,150 +23,150 @@ end
local constants = {
DiagnosticSeverity = {
-- Reports an error.
- Error = 1;
+ Error = 1,
-- Reports a warning.
- Warning = 2;
+ Warning = 2,
-- Reports an information.
- Information = 3;
+ Information = 3,
-- Reports a hint.
- Hint = 4;
- };
+ Hint = 4,
+ },
DiagnosticTag = {
-- Unused or unnecessary code
- Unnecessary = 1;
+ Unnecessary = 1,
-- Deprecated or obsolete code
- Deprecated = 2;
- };
+ Deprecated = 2,
+ },
MessageType = {
-- An error message.
- Error = 1;
+ Error = 1,
-- A warning message.
- Warning = 2;
+ Warning = 2,
-- An information message.
- Info = 3;
+ Info = 3,
-- A log message.
- Log = 4;
- };
+ Log = 4,
+ },
-- The file event type.
FileChangeType = {
-- The file got created.
- Created = 1;
+ Created = 1,
-- The file got changed.
- Changed = 2;
+ Changed = 2,
-- The file got deleted.
- Deleted = 3;
- };
+ Deleted = 3,
+ },
-- The kind of a completion entry.
CompletionItemKind = {
- Text = 1;
- Method = 2;
- Function = 3;
- Constructor = 4;
- Field = 5;
- Variable = 6;
- Class = 7;
- Interface = 8;
- Module = 9;
- Property = 10;
- Unit = 11;
- Value = 12;
- Enum = 13;
- Keyword = 14;
- Snippet = 15;
- Color = 16;
- File = 17;
- Reference = 18;
- Folder = 19;
- EnumMember = 20;
- Constant = 21;
- Struct = 22;
- Event = 23;
- Operator = 24;
- TypeParameter = 25;
- };
+ Text = 1,
+ Method = 2,
+ Function = 3,
+ Constructor = 4,
+ Field = 5,
+ Variable = 6,
+ Class = 7,
+ Interface = 8,
+ Module = 9,
+ Property = 10,
+ Unit = 11,
+ Value = 12,
+ Enum = 13,
+ Keyword = 14,
+ Snippet = 15,
+ Color = 16,
+ File = 17,
+ Reference = 18,
+ Folder = 19,
+ EnumMember = 20,
+ Constant = 21,
+ Struct = 22,
+ Event = 23,
+ Operator = 24,
+ TypeParameter = 25,
+ },
-- How a completion was triggered
CompletionTriggerKind = {
-- Completion was triggered by typing an identifier (24x7 code
-- complete), manual invocation (e.g Ctrl+Space) or via API.
- Invoked = 1;
+ Invoked = 1,
-- Completion was triggered by a trigger character specified by
-- the `triggerCharacters` properties of the `CompletionRegistrationOptions`.
- TriggerCharacter = 2;
+ TriggerCharacter = 2,
-- Completion was re-triggered as the current completion list is incomplete.
- TriggerForIncompleteCompletions = 3;
- };
+ TriggerForIncompleteCompletions = 3,
+ },
-- A document highlight kind.
DocumentHighlightKind = {
-- A textual occurrence.
- Text = 1;
+ Text = 1,
-- Read-access of a symbol, like reading a variable.
- Read = 2;
+ Read = 2,
-- Write-access of a symbol, like writing to a variable.
- Write = 3;
- };
+ Write = 3,
+ },
-- A symbol kind.
SymbolKind = {
- File = 1;
- Module = 2;
- Namespace = 3;
- Package = 4;
- Class = 5;
- Method = 6;
- Property = 7;
- Field = 8;
- Constructor = 9;
- Enum = 10;
- Interface = 11;
- Function = 12;
- Variable = 13;
- Constant = 14;
- String = 15;
- Number = 16;
- Boolean = 17;
- Array = 18;
- Object = 19;
- Key = 20;
- Null = 21;
- EnumMember = 22;
- Struct = 23;
- Event = 24;
- Operator = 25;
- TypeParameter = 26;
- };
+ File = 1,
+ Module = 2,
+ Namespace = 3,
+ Package = 4,
+ Class = 5,
+ Method = 6,
+ Property = 7,
+ Field = 8,
+ Constructor = 9,
+ Enum = 10,
+ Interface = 11,
+ Function = 12,
+ Variable = 13,
+ Constant = 14,
+ String = 15,
+ Number = 16,
+ Boolean = 17,
+ Array = 18,
+ Object = 19,
+ Key = 20,
+ Null = 21,
+ EnumMember = 22,
+ Struct = 23,
+ Event = 24,
+ Operator = 25,
+ TypeParameter = 26,
+ },
-- Represents reasons why a text document is saved.
TextDocumentSaveReason = {
-- Manually triggered, e.g. by the user pressing save, by starting debugging,
-- or by an API call.
- Manual = 1;
+ Manual = 1,
-- Automatic after a delay.
- AfterDelay = 2;
+ AfterDelay = 2,
-- When the editor lost focus.
- FocusOut = 3;
- };
+ FocusOut = 3,
+ },
ErrorCodes = {
-- Defined by JSON RPC
- ParseError = -32700;
- InvalidRequest = -32600;
- MethodNotFound = -32601;
- InvalidParams = -32602;
- InternalError = -32603;
- serverErrorStart = -32099;
- serverErrorEnd = -32000;
- ServerNotInitialized = -32002;
- UnknownErrorCode = -32001;
+ ParseError = -32700,
+ InvalidRequest = -32600,
+ MethodNotFound = -32601,
+ InvalidParams = -32602,
+ InternalError = -32603,
+ serverErrorStart = -32099,
+ serverErrorEnd = -32000,
+ ServerNotInitialized = -32002,
+ UnknownErrorCode = -32001,
-- Defined by the protocol.
- RequestCancelled = -32800;
- ContentModified = -32801;
- };
+ RequestCancelled = -32800,
+ ContentModified = -32801,
+ },
-- Describes the content type that a client supports in various
-- result literals like `Hover`, `ParameterInfo` or `CompletionItem`.
@@ -177,88 +175,88 @@ local constants = {
-- are reserved for internal usage.
MarkupKind = {
-- Plain text is supported as a content format
- PlainText = 'plaintext';
+ PlainText = 'plaintext',
-- Markdown is supported as a content format
- Markdown = 'markdown';
- };
+ Markdown = 'markdown',
+ },
ResourceOperationKind = {
-- Supports creating new files and folders.
- Create = 'create';
+ Create = 'create',
-- Supports renaming existing files and folders.
- Rename = 'rename';
+ Rename = 'rename',
-- Supports deleting existing files and folders.
- Delete = 'delete';
- };
+ Delete = 'delete',
+ },
FailureHandlingKind = {
-- Applying the workspace change is simply aborted if one of the changes provided
-- fails. All operations executed before the failing operation stay executed.
- Abort = 'abort';
+ Abort = 'abort',
-- All operations are executed transactionally. That means they either all
-- succeed or no changes at all are applied to the workspace.
- Transactional = 'transactional';
+ Transactional = 'transactional',
-- If the workspace edit contains only textual file changes they are executed transactionally.
-- If resource changes (create, rename or delete file) are part of the change the failure
-- handling strategy is abort.
- TextOnlyTransactional = 'textOnlyTransactional';
+ TextOnlyTransactional = 'textOnlyTransactional',
-- The client tries to undo the operations already executed. But there is no
-- guarantee that this succeeds.
- Undo = 'undo';
- };
+ Undo = 'undo',
+ },
-- Known error codes for an `InitializeError`;
InitializeError = {
-- If the protocol version provided by the client can't be handled by the server.
-- @deprecated This initialize error got replaced by client capabilities. There is
-- no version handshake in version 3.0x
- unknownProtocolVersion = 1;
- };
+ unknownProtocolVersion = 1,
+ },
-- Defines how the host (editor) should sync document changes to the language server.
TextDocumentSyncKind = {
-- Documents should not be synced at all.
- None = 0;
+ None = 0,
-- Documents are synced by always sending the full content
-- of the document.
- Full = 1;
+ Full = 1,
-- Documents are synced by sending the full content on open.
-- After that only incremental updates to the document are
-- send.
- Incremental = 2;
- };
+ Incremental = 2,
+ },
WatchKind = {
-- Interested in create events.
- Create = 1;
+ Create = 1,
-- Interested in change events
- Change = 2;
+ Change = 2,
-- Interested in delete events
- Delete = 4;
- };
+ Delete = 4,
+ },
-- Defines whether the insert text in a completion item should be interpreted as
-- plain text or a snippet.
InsertTextFormat = {
-- The primary text to be inserted is treated as a plain string.
- PlainText = 1;
+ PlainText = 1,
-- The primary text to be inserted is treated as a snippet.
--
-- A snippet can define tab stops and placeholders with `$1`, `$2`
-- and `${3:foo};`. `$0` defines the final tab stop, it defaults to
-- the end of the snippet. Placeholders with equal identifiers are linked,
-- that is typing in one will update others too.
- Snippet = 2;
- };
+ Snippet = 2,
+ },
-- A set of predefined code action kinds
CodeActionKind = {
-- Empty kind.
- Empty = '';
+ Empty = '',
-- Base kind for quickfix actions
- QuickFix = 'quickfix';
+ QuickFix = 'quickfix',
-- Base kind for refactoring actions
- Refactor = 'refactor';
+ Refactor = 'refactor',
-- Base kind for refactoring extraction actions
--
-- Example extract actions:
@@ -268,7 +266,7 @@ local constants = {
-- - Extract variable
-- - Extract interface from class
-- - ...
- RefactorExtract = 'refactor.extract';
+ RefactorExtract = 'refactor.extract',
-- Base kind for refactoring inline actions
--
-- Example inline actions:
@@ -277,7 +275,7 @@ local constants = {
-- - Inline variable
-- - Inline constant
-- - ...
- RefactorInline = 'refactor.inline';
+ RefactorInline = 'refactor.inline',
-- Base kind for refactoring rewrite actions
--
-- Example rewrite actions:
@@ -288,14 +286,14 @@ local constants = {
-- - Make method static
-- - Move method to base class
-- - ...
- RefactorRewrite = 'refactor.rewrite';
+ RefactorRewrite = 'refactor.rewrite',
-- Base kind for source actions
--
-- Source code actions apply to the entire file.
- Source = 'source';
+ Source = 'source',
-- Base kind for an organize imports source action
- SourceOrganizeImports = 'source.organizeImports';
- };
+ SourceOrganizeImports = 'source.organizeImports',
+ },
}
for k, v in pairs(constants) do
@@ -622,19 +620,19 @@ function protocol.make_client_capabilities()
return {
textDocument = {
synchronization = {
- dynamicRegistration = false;
+ dynamicRegistration = false,
-- TODO(ashkan) Send textDocument/willSave before saving (BufWritePre)
- willSave = false;
+ willSave = false,
-- TODO(ashkan) Implement textDocument/willSaveWaitUntil
- willSaveWaitUntil = false;
+ willSaveWaitUntil = false,
-- Send textDocument/didSave after saving (BufWritePost)
- didSave = true;
- };
+ didSave = true,
+ },
codeAction = {
- dynamicRegistration = false;
+ dynamicRegistration = false,
codeActionLiteralSupport = {
codeActionKind = {
@@ -642,144 +640,193 @@ function protocol.make_client_capabilities()
local res = vim.tbl_values(protocol.CodeActionKind)
table.sort(res)
return res
- end)();
- };
- };
- dataSupport = true;
+ end)(),
+ },
+ },
+ isPreferredSupport = true,
+ dataSupport = true,
resolveSupport = {
- properties = { 'edit', }
- };
- };
+ properties = { 'edit' },
+ },
+ },
completion = {
- dynamicRegistration = false;
+ dynamicRegistration = false,
completionItem = {
-- Until we can actually expand snippet, move cursor and allow for true snippet experience,
-- this should be disabled out of the box.
-- However, users can turn this back on if they have a snippet plugin.
- snippetSupport = false;
+ snippetSupport = false,
- commitCharactersSupport = false;
- preselectSupport = false;
- deprecatedSupport = false;
- documentationFormat = { protocol.MarkupKind.Markdown; protocol.MarkupKind.PlainText };
- };
+ commitCharactersSupport = false,
+ preselectSupport = false,
+ deprecatedSupport = false,
+ documentationFormat = { protocol.MarkupKind.Markdown, protocol.MarkupKind.PlainText },
+ },
completionItemKind = {
valueSet = (function()
local res = {}
for k in ipairs(protocol.CompletionItemKind) do
- if type(k) == 'number' then table.insert(res, k) end
+ if type(k) == 'number' then
+ table.insert(res, k)
+ end
end
return res
- end)();
- };
+ end)(),
+ },
-- TODO(tjdevries): Implement this
- contextSupport = false;
- };
+ contextSupport = false,
+ },
declaration = {
- linkSupport = true;
- };
+ linkSupport = true,
+ },
definition = {
- linkSupport = true;
- };
+ linkSupport = true,
+ },
implementation = {
- linkSupport = true;
- };
+ linkSupport = true,
+ },
typeDefinition = {
- linkSupport = true;
- };
+ linkSupport = true,
+ },
hover = {
- dynamicRegistration = false;
- contentFormat = { protocol.MarkupKind.Markdown; protocol.MarkupKind.PlainText };
- };
+ dynamicRegistration = false,
+ contentFormat = { protocol.MarkupKind.Markdown, protocol.MarkupKind.PlainText },
+ },
signatureHelp = {
- dynamicRegistration = false;
+ dynamicRegistration = false,
signatureInformation = {
- activeParameterSupport = true;
- documentationFormat = { protocol.MarkupKind.Markdown; protocol.MarkupKind.PlainText };
+ activeParameterSupport = true,
+ documentationFormat = { protocol.MarkupKind.Markdown, protocol.MarkupKind.PlainText },
parameterInformation = {
- labelOffsetSupport = true;
- };
- };
- };
+ labelOffsetSupport = true,
+ },
+ },
+ },
references = {
- dynamicRegistration = false;
- };
+ dynamicRegistration = false,
+ },
documentHighlight = {
- dynamicRegistration = false
- };
+ dynamicRegistration = false,
+ },
documentSymbol = {
- dynamicRegistration = false;
+ dynamicRegistration = false,
symbolKind = {
valueSet = (function()
local res = {}
for k in ipairs(protocol.SymbolKind) do
- if type(k) == 'number' then table.insert(res, k) end
+ if type(k) == 'number' then
+ table.insert(res, k)
+ end
end
return res
- end)();
- };
- hierarchicalDocumentSymbolSupport = true;
- };
+ end)(),
+ },
+ hierarchicalDocumentSymbolSupport = true,
+ },
rename = {
- dynamicRegistration = false;
- prepareSupport = true;
- };
+ dynamicRegistration = false,
+ prepareSupport = true,
+ },
publishDiagnostics = {
- relatedInformation = true;
+ relatedInformation = true,
tagSupport = {
valueSet = (function()
local res = {}
for k in ipairs(protocol.DiagnosticTag) do
- if type(k) == 'number' then table.insert(res, k) end
+ if type(k) == 'number' then
+ table.insert(res, k)
+ end
end
return res
- end)();
- };
- };
- };
+ end)(),
+ },
+ },
+ },
workspace = {
symbol = {
- dynamicRegistration = false;
+ dynamicRegistration = false,
symbolKind = {
valueSet = (function()
local res = {}
for k in ipairs(protocol.SymbolKind) do
- if type(k) == 'number' then table.insert(res, k) end
+ if type(k) == 'number' then
+ table.insert(res, k)
+ end
end
return res
- end)();
- };
- hierarchicalWorkspaceSymbolSupport = true;
- };
- workspaceFolders = true;
- applyEdit = true;
+ end)(),
+ },
+ hierarchicalWorkspaceSymbolSupport = true,
+ },
+ workspaceFolders = true,
+ applyEdit = true,
workspaceEdit = {
- resourceOperations = {'rename', 'create', 'delete',},
- };
- };
+ resourceOperations = { 'rename', 'create', 'delete' },
+ },
+ },
callHierarchy = {
- dynamicRegistration = false;
- };
- experimental = nil;
+ dynamicRegistration = false,
+ },
+ experimental = nil,
window = {
- workDoneProgress = true;
+ workDoneProgress = true,
showMessage = {
messageActionItem = {
- additionalPropertiesSupport = false;
- };
- };
+ additionalPropertiesSupport = false,
+ },
+ },
showDocument = {
- support = false;
- };
- };
+ support = false,
+ },
+ },
}
end
+local if_nil = vim.F.if_nil
--- Creates a normalized object describing LSP server capabilities.
---@param server_capabilities table Table of capabilities supported by the server
---@return table Normalized table of capabilities
function protocol.resolve_capabilities(server_capabilities)
+ local TextDocumentSyncKind = protocol.TextDocumentSyncKind
+ local textDocumentSync = server_capabilities.textDocumentSync
+ if textDocumentSync == nil then
+ -- Defaults if omitted.
+ server_capabilities.textDocumentSync = {
+ openClose = false,
+ change = TextDocumentSyncKind.None,
+ willSave = false,
+ willSaveWaitUntil = false,
+ save = {
+ includeText = false,
+ },
+ }
+ elseif type(textDocumentSync) == 'number' then
+ -- Backwards compatibility
+ if not TextDocumentSyncKind[textDocumentSync] then
+ return nil, 'Invalid server TextDocumentSyncKind for textDocumentSync'
+ end
+ server_capabilities.textDocumentSync = {
+ openClose = true,
+ change = textDocumentSync,
+ willSave = false,
+ willSaveWaitUntil = false,
+ save = {
+ includeText = false,
+ },
+ }
+ elseif type(textDocumentSync) ~= 'table' then
+ return nil, string.format('Invalid type for textDocumentSync: %q', type(textDocumentSync))
+ end
+ return server_capabilities
+end
+
+---@private
+--- Creates a normalized object describing LSP server capabilities.
+-- @deprecated access resolved_capabilities instead
+---@param server_capabilities table Table of capabilities supported by the server
+---@return table Normalized table of capabilities
+function protocol._resolve_capabilities_compat(server_capabilities)
local general_properties = {}
local text_document_sync_properties
do
@@ -788,39 +835,41 @@ function protocol.resolve_capabilities(server_capabilities)
if textDocumentSync == nil then
-- Defaults if omitted.
text_document_sync_properties = {
- text_document_open_close = false;
- text_document_did_change = TextDocumentSyncKind.None;
--- text_document_did_change = false;
- text_document_will_save = false;
- text_document_will_save_wait_until = false;
- text_document_save = false;
- text_document_save_include_text = false;
+ text_document_open_close = false,
+ text_document_did_change = TextDocumentSyncKind.None,
+ -- text_document_did_change = false;
+ text_document_will_save = false,
+ text_document_will_save_wait_until = false,
+ text_document_save = false,
+ text_document_save_include_text = false,
}
elseif type(textDocumentSync) == 'number' then
-- Backwards compatibility
if not TextDocumentSyncKind[textDocumentSync] then
- return nil, "Invalid server TextDocumentSyncKind for textDocumentSync"
+ return nil, 'Invalid server TextDocumentSyncKind for textDocumentSync'
end
text_document_sync_properties = {
- text_document_open_close = true;
- text_document_did_change = textDocumentSync;
- text_document_will_save = false;
- text_document_will_save_wait_until = false;
- text_document_save = true;
- text_document_save_include_text = false;
+ text_document_open_close = true,
+ text_document_did_change = textDocumentSync,
+ text_document_will_save = false,
+ text_document_will_save_wait_until = false,
+ text_document_save = true,
+ text_document_save_include_text = false,
}
elseif type(textDocumentSync) == 'table' then
text_document_sync_properties = {
- text_document_open_close = if_nil(textDocumentSync.openClose, false);
- text_document_did_change = if_nil(textDocumentSync.change, TextDocumentSyncKind.None);
- text_document_will_save = if_nil(textDocumentSync.willSave, false);
- text_document_will_save_wait_until = if_nil(textDocumentSync.willSaveWaitUntil, false);
- text_document_save = if_nil(textDocumentSync.save, false);
- text_document_save_include_text = if_nil(type(textDocumentSync.save) == 'table'
- and textDocumentSync.save.includeText, false);
+ text_document_open_close = if_nil(textDocumentSync.openClose, false),
+ text_document_did_change = if_nil(textDocumentSync.change, TextDocumentSyncKind.None),
+ text_document_will_save = if_nil(textDocumentSync.willSave, false),
+ text_document_will_save_wait_until = if_nil(textDocumentSync.willSaveWaitUntil, false),
+ text_document_save = if_nil(textDocumentSync.save, false),
+ text_document_save_include_text = if_nil(
+ type(textDocumentSync.save) == 'table' and textDocumentSync.save.includeText,
+ false
+ ),
}
else
- return nil, string.format("Invalid type for textDocumentSync: %q", type(textDocumentSync))
+ return nil, string.format('Invalid type for textDocumentSync: %q', type(textDocumentSync))
end
end
general_properties.completion = server_capabilities.completionProvider ~= nil
@@ -831,7 +880,8 @@ function protocol.resolve_capabilities(server_capabilities)
general_properties.document_symbol = server_capabilities.documentSymbolProvider or false
general_properties.workspace_symbol = server_capabilities.workspaceSymbolProvider or false
general_properties.document_formatting = server_capabilities.documentFormattingProvider or false
- general_properties.document_range_formatting = server_capabilities.documentRangeFormattingProvider or false
+ general_properties.document_range_formatting = server_capabilities.documentRangeFormattingProvider
+ or false
general_properties.call_hierarchy = server_capabilities.callHierarchyProvider or false
general_properties.execute_command = server_capabilities.executeCommandProvider ~= nil
@@ -848,18 +898,21 @@ function protocol.resolve_capabilities(server_capabilities)
general_properties.code_lens_resolve = false
elseif type(server_capabilities.codeLensProvider) == 'table' then
general_properties.code_lens = true
- general_properties.code_lens_resolve = server_capabilities.codeLensProvider.resolveProvider or false
+ general_properties.code_lens_resolve = server_capabilities.codeLensProvider.resolveProvider
+ or false
else
- error("The server sent invalid codeLensProvider")
+ error('The server sent invalid codeLensProvider')
end
if server_capabilities.codeActionProvider == nil then
general_properties.code_action = false
- elseif type(server_capabilities.codeActionProvider) == 'boolean'
- or type(server_capabilities.codeActionProvider) == 'table' then
+ elseif
+ type(server_capabilities.codeActionProvider) == 'boolean'
+ or type(server_capabilities.codeActionProvider) == 'table'
+ then
general_properties.code_action = server_capabilities.codeActionProvider
else
- error("The server sent invalid codeActionProvider")
+ error('The server sent invalid codeActionProvider')
end
if server_capabilities.declarationProvider == nil then
@@ -869,7 +922,7 @@ function protocol.resolve_capabilities(server_capabilities)
elseif type(server_capabilities.declarationProvider) == 'table' then
general_properties.declaration = server_capabilities.declarationProvider
else
- error("The server sent invalid declarationProvider")
+ error('The server sent invalid declarationProvider')
end
if server_capabilities.typeDefinitionProvider == nil then
@@ -879,7 +932,7 @@ function protocol.resolve_capabilities(server_capabilities)
elseif type(server_capabilities.typeDefinitionProvider) == 'table' then
general_properties.type_definition = server_capabilities.typeDefinitionProvider
else
- error("The server sent invalid typeDefinitionProvider")
+ error('The server sent invalid typeDefinitionProvider')
end
if server_capabilities.implementationProvider == nil then
@@ -889,7 +942,7 @@ function protocol.resolve_capabilities(server_capabilities)
elseif type(server_capabilities.implementationProvider) == 'table' then
general_properties.implementation = server_capabilities.implementationProvider
else
- error("The server sent invalid implementationProvider")
+ error('The server sent invalid implementationProvider')
end
local workspace = server_capabilities.workspace
@@ -897,45 +950,48 @@ function protocol.resolve_capabilities(server_capabilities)
if workspace == nil or workspace.workspaceFolders == nil then
-- Defaults if omitted.
workspace_properties = {
- workspace_folder_properties = {
- supported = false;
- changeNotifications=false;
- }
+ workspace_folder_properties = {
+ supported = false,
+ changeNotifications = false,
+ },
}
elseif type(workspace.workspaceFolders) == 'table' then
workspace_properties = {
workspace_folder_properties = {
- supported = if_nil(workspace.workspaceFolders.supported, false);
- changeNotifications = if_nil(workspace.workspaceFolders.changeNotifications, false);
-
- }
+ supported = if_nil(workspace.workspaceFolders.supported, false),
+ changeNotifications = if_nil(workspace.workspaceFolders.changeNotifications, false),
+ },
}
else
- error("The server sent invalid workspace")
+ error('The server sent invalid workspace')
end
local signature_help_properties
if server_capabilities.signatureHelpProvider == nil then
signature_help_properties = {
- signature_help = false;
- signature_help_trigger_characters = {};
+ signature_help = false,
+ signature_help_trigger_characters = {},
}
elseif type(server_capabilities.signatureHelpProvider) == 'table' then
signature_help_properties = {
- signature_help = true;
+ signature_help = true,
-- The characters that trigger signature help automatically.
- signature_help_trigger_characters = server_capabilities.signatureHelpProvider.triggerCharacters or {};
+ signature_help_trigger_characters = server_capabilities.signatureHelpProvider.triggerCharacters
+ or {},
}
else
- error("The server sent invalid signatureHelpProvider")
+ error('The server sent invalid signatureHelpProvider')
end
- return vim.tbl_extend("error"
- , text_document_sync_properties
- , signature_help_properties
- , workspace_properties
- , general_properties
- )
+ local capabilities = vim.tbl_extend(
+ 'error',
+ text_document_sync_properties,
+ signature_help_properties,
+ workspace_properties,
+ general_properties
+ )
+
+ return capabilities
end
return protocol
diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua
index 1ecac50df4..913eee19a2 100644
--- a/runtime/lua/vim/lsp/rpc.lua
+++ b/runtime/lua/vim/lsp/rpc.lua
@@ -4,12 +4,14 @@ local log = require('vim.lsp.log')
local protocol = require('vim.lsp.protocol')
local validate, schedule, schedule_wrap = vim.validate, vim.schedule, vim.schedule_wrap
+local is_win = uv.os_uname().version:find('Windows')
+
---@private
--- Checks whether a given path exists and is a directory.
---@param filename (string) path to check
---@returns (bool)
local function is_dir(filename)
- local stat = vim.loop.fs_stat(filename)
+ local stat = uv.fs_stat(filename)
return stat and stat.type == 'directory' or false
end
@@ -32,9 +34,9 @@ local function env_merge(env)
-- Merge.
env = vim.tbl_extend('force', vim.fn.environ(), env)
local final_env = {}
- for k,v in pairs(env) do
+ for k, v in pairs(env) do
assert(type(k) == 'string', 'env must be a dict')
- table.insert(final_env, k..'='..tostring(v))
+ table.insert(final_env, k .. '=' .. tostring(v))
end
return final_env
end
@@ -45,10 +47,12 @@ end
---@param encoded_message (string)
---@returns (table) table containing encoded message and `Content-Length` attribute
local function format_message_with_content_length(encoded_message)
- return table.concat {
- 'Content-Length: '; tostring(#encoded_message); '\r\n\r\n';
- encoded_message;
- }
+ return table.concat({
+ 'Content-Length: ',
+ tostring(#encoded_message),
+ '\r\n\r\n',
+ encoded_message,
+ })
end
---@private
@@ -65,23 +69,25 @@ local function parse_headers(header)
if line == '' then
break
end
- local key, value = line:match("^%s*(%S+)%s*:%s*(.+)%s*$")
+ local key, value = line:match('^%s*(%S+)%s*:%s*(.+)%s*$')
if key then
key = key:lower():gsub('%-', '_')
headers[key] = value
else
- local _ = log.error() and log.error("invalid header line %q", line)
- error(string.format("invalid header line %q", line))
+ local _ = log.error() and log.error('invalid header line %q', line)
+ error(string.format('invalid header line %q', line))
end
end
headers.content_length = tonumber(headers.content_length)
- or error(string.format("Content-Length not found in headers. %q", header))
+ or error(string.format('Content-Length not found in headers. %q', header))
return headers
end
-- This is the start of any possible header patterns. The gsub converts it to a
-- case insensitive pattern.
-local header_start_pattern = ("content"):gsub("%w", function(c) return "["..c..c:upper().."]" end)
+local header_start_pattern = ('content'):gsub('%w', function(c)
+ return '[' .. c .. c:upper() .. ']'
+end)
---@private
--- The actual workhorse.
@@ -100,17 +106,17 @@ local function request_parser_loop()
-- be searching for.
-- TODO(ashkan) I'd like to remove this, but it seems permanent :(
local buffer_start = buffer:find(header_start_pattern)
- local headers = parse_headers(buffer:sub(buffer_start, start-1))
+ local headers = parse_headers(buffer:sub(buffer_start, start - 1))
local content_length = headers.content_length
-- Use table instead of just string to buffer the message. It prevents
-- a ton of strings allocating.
-- ref. http://www.lua.org/pil/11.6.html
- local body_chunks = {buffer:sub(finish+1)}
+ local body_chunks = { buffer:sub(finish + 1) }
local body_length = #body_chunks[1]
-- Keep waiting for data until we have enough.
while body_length < content_length do
local chunk = coroutine.yield()
- or error("Expected more data for the body. The server may have died.") -- TODO hmm.
+ or error('Expected more data for the body. The server may have died.') -- TODO hmm.
table.insert(body_chunks, chunk)
body_length = body_length + #chunk
end
@@ -123,25 +129,30 @@ local function request_parser_loop()
end
local body = table.concat(body_chunks)
-- Yield our data.
- buffer = rest..(coroutine.yield(headers, body)
- or error("Expected more data for the body. The server may have died.")) -- TODO hmm.
+ buffer = rest
+ .. (
+ coroutine.yield(headers, body)
+ or error('Expected more data for the body. The server may have died.')
+ ) -- TODO hmm.
else
-- Get more data since we don't have enough.
- buffer = buffer..(coroutine.yield()
- or error("Expected more data for the header. The server may have died.")) -- TODO hmm.
+ buffer = buffer
+ .. (
+ coroutine.yield() or error('Expected more data for the header. The server may have died.')
+ ) -- TODO hmm.
end
end
end
--- Mapping of error codes used by the client
local client_errors = {
- INVALID_SERVER_MESSAGE = 1;
- INVALID_SERVER_JSON = 2;
- NO_RESULT_CALLBACK_FOUND = 3;
- READ_ERROR = 4;
- NOTIFICATION_HANDLER_ERROR = 5;
- SERVER_REQUEST_HANDLER_ERROR = 6;
- SERVER_RESULT_CALLBACK_ERROR = 7;
+ INVALID_SERVER_MESSAGE = 1,
+ INVALID_SERVER_JSON = 2,
+ NO_RESULT_CALLBACK_FOUND = 3,
+ READ_ERROR = 4,
+ NOTIFICATION_HANDLER_ERROR = 5,
+ SERVER_REQUEST_HANDLER_ERROR = 6,
+ SERVER_RESULT_CALLBACK_ERROR = 7,
}
client_errors = vim.tbl_add_reverse_lookup(client_errors)
@@ -151,26 +162,26 @@ client_errors = vim.tbl_add_reverse_lookup(client_errors)
---@param err (table) The error object
---@returns (string) The formatted error message
local function format_rpc_error(err)
- validate {
- err = { err, 't' };
- }
+ validate({
+ err = { err, 't' },
+ })
-- There is ErrorCodes in the LSP specification,
-- but in ResponseError.code it is not used and the actual type is number.
local code
if protocol.ErrorCodes[err.code] then
- code = string.format("code_name = %s,", protocol.ErrorCodes[err.code])
+ code = string.format('code_name = %s,', protocol.ErrorCodes[err.code])
else
- code = string.format("code_name = unknown, code = %s,", err.code)
+ code = string.format('code_name = unknown, code = %s,', err.code)
end
- local message_parts = {"RPC[Error]", code}
+ local message_parts = { 'RPC[Error]', code }
if err.message then
- table.insert(message_parts, "message =")
- table.insert(message_parts, string.format("%q", err.message))
+ table.insert(message_parts, 'message =')
+ table.insert(message_parts, string.format('%q', err.message))
end
if err.data then
- table.insert(message_parts, "data =")
+ table.insert(message_parts, 'data =')
table.insert(message_parts, vim.inspect(err.data))
end
return table.concat(message_parts, ' ')
@@ -185,11 +196,11 @@ local function rpc_response_error(code, message, data)
-- TODO should this error or just pick a sane error (like InternalError)?
local code_name = assert(protocol.ErrorCodes[code], 'Invalid RPC error code')
return setmetatable({
- code = code;
- message = message or code_name;
- data = data;
+ code = code,
+ message = message or code_name,
+ data = data,
}, {
- __tostring = format_rpc_error;
+ __tostring = format_rpc_error,
})
end
@@ -220,7 +231,7 @@ end
---@param signal (number): Number describing the signal used to terminate (if
---any)
function default_dispatchers.on_exit(code, signal)
- local _ = log.info() and log.info("client_exit", { code = code, signal = signal })
+ local _ = log.info() and log.info('client_exit', { code = code, signal = signal })
end
---@private
--- Default dispatcher for client errors.
@@ -258,15 +269,16 @@ end
--- - {handle} A handle for low-level interaction with the LSP server process
--- |vim.loop|.
local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
- local _ = log.info() and log.info("Starting RPC client", {cmd = cmd, args = cmd_args, extra = extra_spawn_params})
- validate {
- cmd = { cmd, 's' };
- cmd_args = { cmd_args, 't' };
- dispatchers = { dispatchers, 't', true };
- }
+ local _ = log.info()
+ and log.info('Starting RPC client', { cmd = cmd, args = cmd_args, extra = extra_spawn_params })
+ validate({
+ cmd = { cmd, 's' },
+ cmd_args = { cmd_args, 't' },
+ dispatchers = { dispatchers, 't', true },
+ })
if extra_spawn_params and extra_spawn_params.cwd then
- assert(is_dir(extra_spawn_params.cwd), "cwd must be a directory")
+ assert(is_dir(extra_spawn_params.cwd), 'cwd must be a directory')
end
if dispatchers then
local user_dispatchers = dispatchers
@@ -275,11 +287,11 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
local user_dispatcher = user_dispatchers[dispatch_name]
if user_dispatcher then
if type(user_dispatcher) ~= 'function' then
- error(string.format("dispatcher.%s must be a function", dispatch_name))
+ error(string.format('dispatcher.%s must be a function', dispatch_name))
end
-- server_request is wrapped elsewhere.
- if not (dispatch_name == 'server_request'
- or dispatch_name == 'on_exit') -- TODO this blocks the loop exiting for some reason.
+ if
+ not (dispatch_name == 'server_request' or dispatch_name == 'on_exit') -- TODO this blocks the loop exiting for some reason.
then
user_dispatcher = schedule_wrap(user_dispatcher)
end
@@ -317,20 +329,25 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
dispatchers.on_exit(code, signal)
end
local spawn_params = {
- args = cmd_args;
- stdio = {stdin, stdout, stderr};
+ args = cmd_args,
+ stdio = { stdin, stdout, stderr },
+ detached = not is_win,
}
if extra_spawn_params then
spawn_params.cwd = extra_spawn_params.cwd
spawn_params.env = env_merge(extra_spawn_params.env)
+ if extra_spawn_params.detached ~= nil then
+ spawn_params.detached = extra_spawn_params.detached
+ end
end
handle, pid = uv.spawn(cmd, spawn_params, onexit)
if handle == nil then
- local msg = string.format("Spawning language server with cmd: `%s` failed", cmd)
- if string.match(pid, "ENOENT") then
- msg = msg .. ". The language server is either not installed, missing from PATH, or not executable."
+ local msg = string.format('Spawning language server with cmd: `%s` failed', cmd)
+ if string.match(pid, 'ENOENT') then
+ msg = msg
+ .. '. The language server is either not installed, missing from PATH, or not executable.'
else
- msg = msg .. string.format(" with error message: %s", pid)
+ msg = msg .. string.format(' with error message: %s', pid)
end
vim.notify(msg, vim.log.levels.WARN)
return
@@ -344,8 +361,10 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
---@param payload table
---@returns true if the payload could be scheduled, false if the main event-loop is in the process of closing.
local function encode_and_send(payload)
- local _ = log.debug() and log.debug("rpc.send", payload)
- if handle == nil or handle:is_closing() then return false end
+ local _ = log.debug() and log.debug('rpc.send', payload)
+ if handle == nil or handle:is_closing() then
+ return false
+ end
local encoded = vim.json.encode(payload)
stdin:write(format_message_with_content_length(encoded))
return true
@@ -359,22 +378,22 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
---@param params (table): Parameters for the invoked LSP method
---@returns (bool) `true` if notification could be sent, `false` if not
local function notify(method, params)
- return encode_and_send {
- jsonrpc = "2.0";
- method = method;
- params = params;
- }
+ return encode_and_send({
+ jsonrpc = '2.0',
+ method = method,
+ params = params,
+ })
end
---@private
--- sends an error object to the remote LSP process.
local function send_response(request_id, err, result)
- return encode_and_send {
- id = request_id;
- jsonrpc = "2.0";
- error = err;
- result = result;
- }
+ return encode_and_send({
+ id = request_id,
+ jsonrpc = '2.0',
+ error = err,
+ result = result,
+ })
end
-- FIXME: DOC: Should be placed on the RPC client object returned by
@@ -385,21 +404,21 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
---@param method (string) The invoked LSP method
---@param params (table) Parameters for the invoked LSP method
---@param callback (function) Callback to invoke
- ---@param notify_reply_callback (function) Callback to invoke as soon as a request is no longer pending
+ ---@param notify_reply_callback (function|nil) Callback to invoke as soon as a request is no longer pending
---@returns (bool, number) `(true, message_id)` if request could be sent, `false` if not
local function request(method, params, callback, notify_reply_callback)
- validate {
- callback = { callback, 'f' };
- notify_reply_callback = { notify_reply_callback, 'f', true };
- }
+ validate({
+ callback = { callback, 'f' },
+ notify_reply_callback = { notify_reply_callback, 'f', true },
+ })
message_index = message_index + 1
local message_id = message_index
- local result = encode_and_send {
- id = message_id;
- jsonrpc = "2.0";
- method = method;
- params = params;
- }
+ local result = encode_and_send({
+ id = message_id,
+ jsonrpc = '2.0',
+ method = method,
+ params = params,
+ })
if result then
if message_callbacks then
message_callbacks[message_id] = schedule_wrap(callback)
@@ -417,7 +436,7 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
stderr:read_start(function(_err, chunk)
if chunk then
- local _ = log.error() and log.error("rpc", cmd, "stderr", chunk)
+ local _ = log.error() and log.error('rpc', cmd, 'stderr', chunk)
end
end)
@@ -451,7 +470,7 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
on_error(client_errors.INVALID_SERVER_JSON, decoded)
return
end
- local _ = log.debug() and log.debug("rpc.receive", decoded)
+ local _ = log.debug() and log.debug('rpc.receive', decoded)
if type(decoded.method) == 'string' and decoded.id then
local err
@@ -459,17 +478,36 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
-- we can still use the result.
schedule(function()
local status, result
- status, result, err = try_call(client_errors.SERVER_REQUEST_HANDLER_ERROR,
- dispatchers.server_request, decoded.method, decoded.params)
- local _ = log.debug() and log.debug("server_request: callback result", { status = status, result = result, err = err })
+ status, result, err = try_call(
+ client_errors.SERVER_REQUEST_HANDLER_ERROR,
+ dispatchers.server_request,
+ decoded.method,
+ decoded.params
+ )
+ local _ = log.debug()
+ and log.debug(
+ 'server_request: callback result',
+ { status = status, result = result, err = err }
+ )
if status then
if not (result or err) then
-- TODO this can be a problem if `null` is sent for result. needs vim.NIL
- error(string.format("method %q: either a result or an error must be sent to the server in response", decoded.method))
+ error(
+ string.format(
+ 'method %q: either a result or an error must be sent to the server in response',
+ decoded.method
+ )
+ )
end
if err then
- assert(type(err) == 'table', "err must be a table. Use rpc_response_error to help format errors.")
- local code_name = assert(protocol.ErrorCodes[err.code], "Errors must use protocol.ErrorCodes. Use rpc_response_error to help format errors.")
+ assert(
+ type(err) == 'table',
+ 'err must be a table. Use rpc_response_error to help format errors.'
+ )
+ local code_name = assert(
+ protocol.ErrorCodes[err.code],
+ 'Errors must use protocol.ErrorCodes. Use rpc_response_error to help format errors.'
+ )
err.message = err.message or code_name
end
else
@@ -479,18 +517,17 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
end
send_response(decoded.id, err, result)
end)
- -- This works because we are expecting vim.NIL here
+ -- This works because we are expecting vim.NIL here
elseif decoded.id and (decoded.result ~= vim.NIL or decoded.error ~= vim.NIL) then
-
-- We sent a number, so we expect a number.
local result_id = tonumber(decoded.id)
-- Notify the user that a response was received for the request
local notify_reply_callback = notify_reply_callbacks and notify_reply_callbacks[result_id]
if notify_reply_callback then
- validate {
- notify_reply_callback = { notify_reply_callback, 'f' };
- }
+ validate({
+ notify_reply_callback = { notify_reply_callback, 'f' },
+ })
notify_reply_callback(result_id)
notify_reply_callbacks[result_id] = nil
end
@@ -499,7 +536,7 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
if decoded.error then
local mute_error = false
if decoded.error.code == protocol.ErrorCodes.RequestCancelled then
- local _ = log.debug() and log.debug("Received cancellation ack", decoded)
+ local _ = log.debug() and log.debug('Received cancellation ack', decoded)
mute_error = true
end
@@ -519,24 +556,33 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
local callback = message_callbacks and message_callbacks[result_id]
if callback then
message_callbacks[result_id] = nil
- validate {
- callback = { callback, 'f' };
- }
+ validate({
+ callback = { callback, 'f' },
+ })
if decoded.error then
decoded.error = setmetatable(decoded.error, {
- __tostring = format_rpc_error;
+ __tostring = format_rpc_error,
})
end
- try_call(client_errors.SERVER_RESULT_CALLBACK_ERROR,
- callback, decoded.error, decoded.result)
+ try_call(
+ client_errors.SERVER_RESULT_CALLBACK_ERROR,
+ callback,
+ decoded.error,
+ decoded.result
+ )
else
on_error(client_errors.NO_RESULT_CALLBACK_FOUND, decoded)
- local _ = log.error() and log.error("No callback found for server response id "..result_id)
+ local _ = log.error()
+ and log.error('No callback found for server response id ' .. result_id)
end
elseif type(decoded.method) == 'string' then
-- Notification
- try_call(client_errors.NOTIFICATION_HANDLER_ERROR,
- dispatchers.notification, decoded.method, decoded.params)
+ try_call(
+ client_errors.NOTIFICATION_HANDLER_ERROR,
+ dispatchers.notification,
+ decoded.method,
+ decoded.params
+ )
else
-- Invalid server message
on_error(client_errors.INVALID_SERVER_MESSAGE, decoded)
@@ -552,7 +598,9 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
return
end
-- This should signal that we are done reading from the client.
- if not chunk then return end
+ if not chunk then
+ return
+ end
-- Flush anything in the parser by looping until we don't get a result
-- anymore.
while true do
@@ -570,17 +618,17 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
end)
return {
- pid = pid;
- handle = handle;
- request = request;
- notify = notify
+ pid = pid,
+ handle = handle,
+ request = request,
+ notify = notify,
}
end
return {
- start = start;
- rpc_response_error = rpc_response_error;
- format_rpc_error = format_rpc_error;
- client_errors = client_errors;
+ start = start,
+ rpc_response_error = rpc_response_error,
+ format_rpc_error = format_rpc_error,
+ client_errors = client_errors,
}
-- vim:sw=2 ts=2 et
diff --git a/runtime/lua/vim/lsp/sync.lua b/runtime/lua/vim/lsp/sync.lua
index 0f4e5b572b..0d65e86b55 100644
--- a/runtime/lua/vim/lsp/sync.lua
+++ b/runtime/lua/vim/lsp/sync.lua
@@ -79,7 +79,7 @@ local function compute_line_length(line, offset_encoding)
local length
local _
if offset_encoding == 'utf-16' then
- _, length = str_utfindex(line)
+ _, length = str_utfindex(line)
elseif offset_encoding == 'utf-32' then
length, _ = str_utfindex(line)
else
@@ -100,7 +100,7 @@ local function align_end_position(line, byte, offset_encoding)
-- If on the first byte, or an empty string: the trivial case
if byte == 1 or #line == 0 then
char = byte
- -- Called in the case of extending an empty line "" -> "a"
+ -- Called in the case of extending an empty line "" -> "a"
elseif byte == #line + 1 then
char = compute_line_length(line, offset_encoding) + 1
else
@@ -130,14 +130,38 @@ end
---@param new_lastline integer new_lastline from on_lines, adjusted to 1-index
---@param offset_encoding string utf-8|utf-16|utf-32|nil (fallback to utf-8)
---@returns table<int, int> line_idx, byte_idx, and char_idx of first change position
-local function compute_start_range(prev_lines, curr_lines, firstline, lastline, new_lastline, offset_encoding)
+local function compute_start_range(
+ prev_lines,
+ curr_lines,
+ firstline,
+ lastline,
+ new_lastline,
+ offset_encoding
+)
+ local char_idx
+ local byte_idx
-- If firstline == lastline, no existing text is changed. All edit operations
-- occur on a new line pointed to by lastline. This occurs during insertion of
-- new lines(O), the new newline is inserted at the line indicated by
-- new_lastline.
+ if firstline == lastline then
+ local line_idx
+ local line = prev_lines[firstline - 1]
+ if line then
+ line_idx = firstline - 1
+ byte_idx = #line + 1
+ char_idx = compute_line_length(line, offset_encoding) + 1
+ else
+ line_idx = firstline
+ byte_idx = 1
+ char_idx = 1
+ end
+ return { line_idx = line_idx, byte_idx = byte_idx, char_idx = char_idx }
+ end
+
-- If firstline == new_lastline, the first change occurred on a line that was deleted.
-- In this case, the first byte change is also at the first byte of firstline
- if firstline == new_lastline or firstline == lastline then
+ if firstline == new_lastline then
return { line_idx = firstline, byte_idx = 1, char_idx = 1 }
end
@@ -158,14 +182,12 @@ local function compute_start_range(prev_lines, curr_lines, firstline, lastline,
end
-- Convert byte to codepoint if applicable
- local char_idx
- local byte_idx
- if start_byte_idx == 1 or (#prev_line == 0 and start_byte_idx == 1)then
+ if start_byte_idx == 1 or (#prev_line == 0 and start_byte_idx == 1) then
byte_idx = start_byte_idx
char_idx = 1
elseif start_byte_idx == #prev_line + 1 then
byte_idx = start_byte_idx
- char_idx = compute_line_length(prev_line, offset_encoding) + 1
+ char_idx = compute_line_length(prev_line, offset_encoding) + 1
else
byte_idx = start_byte_idx + str_utf_start(prev_line, start_byte_idx)
char_idx = byte_to_utf(prev_line, byte_idx, offset_encoding)
@@ -188,14 +210,30 @@ end
---@param new_lastline integer
---@param offset_encoding string
---@returns (int, int) end_line_idx and end_col_idx of range
-local function compute_end_range(prev_lines, curr_lines, start_range, firstline, lastline, new_lastline, offset_encoding)
+local function compute_end_range(
+ prev_lines,
+ curr_lines,
+ start_range,
+ firstline,
+ lastline,
+ new_lastline,
+ offset_encoding
+)
-- If firstline == new_lastline, the first change occurred on a line that was deleted.
-- In this case, the last_byte...
if firstline == new_lastline then
- return { line_idx = (lastline - new_lastline + firstline), byte_idx = 1, char_idx = 1 }, { line_idx = firstline, byte_idx = 1, char_idx = 1 }
+ return { line_idx = (lastline - new_lastline + firstline), byte_idx = 1, char_idx = 1 }, {
+ line_idx = firstline,
+ byte_idx = 1,
+ char_idx = 1,
+ }
end
if firstline == lastline then
- return { line_idx = firstline, byte_idx = 1, char_idx = 1 }, { line_idx = new_lastline - lastline + firstline, byte_idx = 1, char_idx = 1 }
+ return { line_idx = firstline, byte_idx = 1, char_idx = 1 }, {
+ line_idx = new_lastline - lastline + firstline,
+ byte_idx = 1,
+ char_idx = 1,
+ }
end
-- Compare on last line, at minimum will be the start range
local start_line_idx = start_range.line_idx
@@ -218,14 +256,18 @@ local function compute_end_range(prev_lines, curr_lines, start_range, firstline,
local max_length
if start_line_idx == prev_line_idx then
-- Search until beginning of difference
- max_length = min(prev_line_length - start_range.byte_idx, curr_line_length - start_range.byte_idx) + 1
+ max_length = min(
+ prev_line_length - start_range.byte_idx,
+ curr_line_length - start_range.byte_idx
+ ) + 1
else
max_length = min(prev_line_length, curr_line_length) + 1
end
for idx = 0, max_length do
byte_offset = idx
if
- str_byte(prev_line, prev_line_length - byte_offset) ~= str_byte(curr_line, curr_line_length - byte_offset)
+ str_byte(prev_line, prev_line_length - byte_offset)
+ ~= str_byte(curr_line, curr_line_length - byte_offset)
then
break
end
@@ -239,8 +281,10 @@ local function compute_end_range(prev_lines, curr_lines, start_range, firstline,
if prev_end_byte_idx == 0 then
prev_end_byte_idx = 1
end
- local prev_byte_idx, prev_char_idx = align_end_position(prev_line, prev_end_byte_idx, offset_encoding)
- local prev_end_range = { line_idx = prev_line_idx, byte_idx = prev_byte_idx, char_idx = prev_char_idx }
+ local prev_byte_idx, prev_char_idx =
+ align_end_position(prev_line, prev_end_byte_idx, offset_encoding)
+ local prev_end_range =
+ { line_idx = prev_line_idx, byte_idx = prev_byte_idx, char_idx = prev_char_idx }
local curr_end_range
-- Deletion event, new_range cannot be before start
@@ -252,8 +296,10 @@ local function compute_end_range(prev_lines, curr_lines, start_range, firstline,
if curr_end_byte_idx == 0 then
curr_end_byte_idx = 1
end
- local curr_byte_idx, curr_char_idx = align_end_position(curr_line, curr_end_byte_idx, offset_encoding)
- curr_end_range = { line_idx = curr_line_idx, byte_idx = curr_byte_idx, char_idx = curr_char_idx }
+ local curr_byte_idx, curr_char_idx =
+ align_end_position(curr_line, curr_end_byte_idx, offset_encoding)
+ curr_end_range =
+ { line_idx = curr_line_idx, byte_idx = curr_byte_idx, char_idx = curr_char_idx }
end
return prev_end_range, curr_end_range
@@ -266,14 +312,13 @@ end
---@param end_range table new_end_range returned by last_difference
---@returns string text extracted from defined region
local function extract_text(lines, start_range, end_range, line_ending)
- if not lines[start_range.line_idx] then
- return ""
- end
+ if not lines[start_range.line_idx] then
+ return ''
+ end
-- Trivial case: start and end range are the same line, directly grab changed text
if start_range.line_idx == end_range.line_idx then
-- string.sub is inclusive, end_range is not
return string.sub(lines[start_range.line_idx], start_range.byte_idx, end_range.byte_idx - 1)
-
else
-- Handle deletion case
-- Collect the changed portion of the first changed line
@@ -288,7 +333,7 @@ local function extract_text(lines, start_range, end_range, line_ending)
-- Collect the changed portion of the last changed line.
table.insert(result, string.sub(lines[end_range.line_idx], 1, end_range.byte_idx - 1))
else
- table.insert(result, "")
+ table.insert(result, '')
end
-- Add line ending between all lines
@@ -313,7 +358,10 @@ local function compute_range_length(lines, start_range, end_range, offset_encodi
local start_line = lines[start_range.line_idx]
local range_length
if start_line and #start_line > 0 then
- range_length = compute_line_length(start_line, offset_encoding) - start_range.char_idx + 1 + line_ending_length
+ range_length = compute_line_length(start_line, offset_encoding)
+ - start_range.char_idx
+ + 1
+ + line_ending_length
else
-- Length of newline character
range_length = line_ending_length
@@ -345,7 +393,15 @@ end
---@param new_lastline number line to begin search in new_lines for last difference
---@param offset_encoding string encoding requested by language server
---@returns table TextDocumentContentChangeEvent see https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#textDocumentContentChangeEvent
-function M.compute_diff(prev_lines, curr_lines, firstline, lastline, new_lastline, offset_encoding, line_ending)
+function M.compute_diff(
+ prev_lines,
+ curr_lines,
+ firstline,
+ lastline,
+ new_lastline,
+ offset_encoding,
+ line_ending
+)
-- Find the start of changes between the previous and current buffer. Common between both.
-- Sent to the server as the start of the changed range.
-- Used to grab the changed text from the latest buffer.
@@ -375,7 +431,8 @@ function M.compute_diff(prev_lines, curr_lines, firstline, lastline, new_lastlin
local text = extract_text(curr_lines, start_range, curr_end_range, line_ending)
-- Compute the range of the replaced text. Deprecated but still required for certain language servers
- local range_length = compute_range_length(prev_lines, start_range, prev_end_range, offset_encoding, line_ending)
+ local range_length =
+ compute_range_length(prev_lines, start_range, prev_end_range, offset_encoding, line_ending)
-- convert to 0 based indexing
local result = {
diff --git a/runtime/lua/vim/lsp/tagfunc.lua b/runtime/lua/vim/lsp/tagfunc.lua
index 5c55e8559f..49029f8599 100644
--- a/runtime/lua/vim/lsp/tagfunc.lua
+++ b/runtime/lua/vim/lsp/tagfunc.lua
@@ -1,5 +1,5 @@
local lsp = vim.lsp
-local util = vim.lsp.util
+local util = lsp.util
---@private
local function mk_tag_item(name, range, uri, offset_encoding)
@@ -15,7 +15,7 @@ end
---@private
local function query_definition(pattern)
- local params = lsp.util.make_position_params()
+ local params = util.make_position_params()
local results_by_client, err = lsp.buf_request_sync(0, 'textDocument/definition', params, 1000)
if err then
return {}
@@ -44,7 +44,8 @@ end
---@private
local function query_workspace_symbols(pattern)
- local results_by_client, err = lsp.buf_request_sync(0, 'workspace/symbol', { query = pattern }, 1000)
+ local results_by_client, err =
+ lsp.buf_request_sync(0, 'workspace/symbol', { query = pattern }, 1000)
if err then
return {}
end
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index 89c5ebe8f7..70f5010256 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -1,10 +1,10 @@
-local protocol = require 'vim.lsp.protocol'
-local snippet = require 'vim.lsp._snippet'
+local protocol = require('vim.lsp.protocol')
+local snippet = require('vim.lsp._snippet')
local vim = vim
local validate = vim.validate
local api = vim.api
local list_extend = vim.list_extend
-local highlight = require 'vim.highlight'
+local highlight = require('vim.highlight')
local uv = vim.loop
local npcall = vim.F.npcall
@@ -13,14 +13,14 @@ local split = vim.split
local M = {}
local default_border = {
- {"", "NormalFloat"},
- {"", "NormalFloat"},
- {"", "NormalFloat"},
- {" ", "NormalFloat"},
- {"", "NormalFloat"},
- {"", "NormalFloat"},
- {"", "NormalFloat"},
- {" ", "NormalFloat"},
+ { '', 'NormalFloat' },
+ { '', 'NormalFloat' },
+ { '', 'NormalFloat' },
+ { ' ', 'NormalFloat' },
+ { '', 'NormalFloat' },
+ { '', 'NormalFloat' },
+ { '', 'NormalFloat' },
+ { ' ', 'NormalFloat' },
}
---@private
@@ -35,43 +35,70 @@ local function get_border_size(opts)
local width = 0
if type(border) == 'string' then
- local border_size = {none = {0, 0}, single = {2, 2}, double = {2, 2}, rounded = {2, 2}, solid = {2, 2}, shadow = {1, 1}}
+ local border_size = {
+ none = { 0, 0 },
+ single = { 2, 2 },
+ double = { 2, 2 },
+ rounded = { 2, 2 },
+ solid = { 2, 2 },
+ shadow = { 1, 1 },
+ }
if border_size[border] == nil then
- error(string.format("invalid floating preview border: %s. :help vim.api.nvim_open_win()", vim.inspect(border)))
+ error(
+ string.format(
+ 'invalid floating preview border: %s. :help vim.api.nvim_open_win()',
+ vim.inspect(border)
+ )
+ )
end
height, width = unpack(border_size[border])
else
if 8 % #border ~= 0 then
- error(string.format("invalid floating preview border: %s. :help vim.api.nvim_open_win()", vim.inspect(border)))
+ error(
+ string.format(
+ 'invalid floating preview border: %s. :help vim.api.nvim_open_win()',
+ vim.inspect(border)
+ )
+ )
end
---@private
local function border_width(id)
id = (id - 1) % #border + 1
- if type(border[id]) == "table" then
+ if type(border[id]) == 'table' then
-- border specified as a table of <character, highlight group>
return vim.fn.strdisplaywidth(border[id][1])
- elseif type(border[id]) == "string" then
+ elseif type(border[id]) == 'string' then
-- border specified as a list of border characters
return vim.fn.strdisplaywidth(border[id])
end
- error(string.format("invalid floating preview border: %s. :help vim.api.nvim_open_win()", vim.inspect(border)))
+ error(
+ string.format(
+ 'invalid floating preview border: %s. :help vim.api.nvim_open_win()',
+ vim.inspect(border)
+ )
+ )
end
---@private
local function border_height(id)
id = (id - 1) % #border + 1
- if type(border[id]) == "table" then
+ if type(border[id]) == 'table' then
-- border specified as a table of <character, highlight group>
return #border[id][1] > 0 and 1 or 0
- elseif type(border[id]) == "string" then
+ elseif type(border[id]) == 'string' then
-- border specified as a list of border characters
return #border[id] > 0 and 1 or 0
end
- error(string.format("invalid floating preview border: %s. :help vim.api.nvim_open_win()", vim.inspect(border)))
+ error(
+ string.format(
+ 'invalid floating preview border: %s. :help vim.api.nvim_open_win()',
+ vim.inspect(border)
+ )
+ )
end
- height = height + border_height(2) -- top
- height = height + border_height(6) -- bottom
- width = width + border_width(4) -- right
- width = width + border_width(8) -- left
+ height = height + border_height(2) -- top
+ height = height + border_height(6) -- bottom
+ width = width + border_width(4) -- right
+ width = width + border_width(8) -- left
end
return { height = height, width = width }
@@ -89,9 +116,15 @@ end
---@param encoding string utf-8|utf-16|utf-32|nil defaults to utf-16
---@return number `encoding` index of `index` in `line`
function M._str_utfindex_enc(line, index, encoding)
- if not encoding then encoding = 'utf-16' end
+ if not encoding then
+ encoding = 'utf-16'
+ end
if encoding == 'utf-8' then
- if index then return index else return #line end
+ if index then
+ return index
+ else
+ return #line
+ end
elseif encoding == 'utf-16' then
local _, col16 = vim.str_utfindex(line, index)
return col16
@@ -99,7 +132,7 @@ function M._str_utfindex_enc(line, index, encoding)
local col32, _ = vim.str_utfindex(line, index)
return col32
else
- error("Invalid encoding: " .. vim.inspect(encoding))
+ error('Invalid encoding: ' .. vim.inspect(encoding))
end
end
@@ -111,15 +144,21 @@ end
---@param encoding string utf-8|utf-16|utf-32|nil defaults to utf-16
---@return number byte (utf-8) index of `encoding` index `index` in `line`
function M._str_byteindex_enc(line, index, encoding)
- if not encoding then encoding = 'utf-16' end
+ if not encoding then
+ encoding = 'utf-16'
+ end
if encoding == 'utf-8' then
- if index then return index else return #line end
+ if index then
+ return index
+ else
+ return #line
+ end
elseif encoding == 'utf-16' then
return vim.str_byteindex(line, index, true)
elseif encoding == 'utf-32' then
return vim.str_byteindex(line, index)
else
- error("Invalid encoding: " .. vim.inspect(encoding))
+ error('Invalid encoding: ' .. vim.inspect(encoding))
end
end
@@ -142,34 +181,38 @@ function M.set_lines(lines, A, B, new_lines)
-- specifying a line number after what we would call the last line.
local i_n = math.min(B[1] + 1, #lines)
if not (i_0 >= 1 and i_0 <= #lines + 1 and i_n >= 1 and i_n <= #lines) then
- error("Invalid range: "..vim.inspect{A = A; B = B; #lines, new_lines})
+ error('Invalid range: ' .. vim.inspect({ A = A, B = B, #lines, new_lines }))
end
- local prefix = ""
- local suffix = lines[i_n]:sub(B[2]+1)
+ local prefix = ''
+ local suffix = lines[i_n]:sub(B[2] + 1)
if A[2] > 0 then
prefix = lines[i_0]:sub(1, A[2])
end
local n = i_n - i_0 + 1
if n ~= #new_lines then
- for _ = 1, n - #new_lines do table.remove(lines, i_0) end
- for _ = 1, #new_lines - n do table.insert(lines, i_0, '') end
+ for _ = 1, n - #new_lines do
+ table.remove(lines, i_0)
+ end
+ for _ = 1, #new_lines - n do
+ table.insert(lines, i_0, '')
+ end
end
for i = 1, #new_lines do
lines[i - 1 + i_0] = new_lines[i]
end
if #suffix > 0 then
local i = i_0 + #new_lines - 1
- lines[i] = lines[i]..suffix
+ lines[i] = lines[i] .. suffix
end
if #prefix > 0 then
- lines[i_0] = prefix..lines[i_0]
+ lines[i_0] = prefix .. lines[i_0]
end
return lines
end
---@private
local function sort_by_key(fn)
- return function(a,b)
+ return function(a, b)
local ka, kb = fn(a), fn(b)
assert(#ka == #kb)
for i = 1, #ka do
@@ -191,18 +234,18 @@ end
---@param rows number[] zero-indexed line numbers
---@return table<number string> a table mapping rows to lines
local function get_lines(bufnr, rows)
- rows = type(rows) == "table" and rows or { rows }
+ 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()
+ bufnr = api.nvim_get_current_buf()
end
---@private
local function buf_lines()
local lines = {}
for _, row in pairs(rows) do
- lines[row] = (vim.api.nvim_buf_get_lines(bufnr, row, row + 1, false) or { "" })[1]
+ lines[row] = (api.nvim_buf_get_lines(bufnr, row, row + 1, false) or { '' })[1]
end
return lines
end
@@ -211,7 +254,7 @@ local function get_lines(bufnr, rows)
-- load the buffer if this is not a file uri
-- Custom language server protocol extensions can result in servers sending URIs with custom schemes. Plugins are able to load these via `BufReadCmd` autocmds.
- if uri:sub(1, 4) ~= "file" then
+ if uri:sub(1, 4) ~= 'file' then
vim.fn.bufload(bufnr)
return buf_lines()
end
@@ -224,8 +267,10 @@ local function get_lines(bufnr, rows)
local filename = api.nvim_buf_get_name(bufnr)
-- get the data from the file
- local fd = uv.fs_open(filename, "r", 438)
- if not fd then return "" end
+ local fd = uv.fs_open(filename, 'r', 438)
+ if not fd then
+ return ''
+ end
local stat = uv.fs_fstat(fd)
local data = uv.fs_read(fd, stat.size, 0)
uv.fs_close(fd)
@@ -242,11 +287,13 @@ local function get_lines(bufnr, rows)
local found = 0
local lnum = 0
- for line in string.gmatch(data, "([^\n]*)\n?") do
+ for line in string.gmatch(data, '([^\n]*)\n?') do
if lines[lnum] == true then
lines[lnum] = line
found = found + 1
- if found == need then break end
+ if found == need then
+ break
+ end
end
lnum = lnum + 1
end
@@ -254,13 +301,12 @@ local function get_lines(bufnr, rows)
-- change any lines we didn't find to the empty string
for i, line in pairs(lines) do
if line == true then
- lines[i] = ""
+ lines[i] = ''
end
end
return lines
end
-
---@private
--- Gets the zero-indexed line from the given buffer.
--- Works on unloaded buffers by reading the file using libuv to bypass buf reading events.
@@ -273,11 +319,10 @@ local function get_line(bufnr, row)
return get_lines(bufnr, { row })[row]
end
-
---@private
--- Position is a https://microsoft.github.io/language-server-protocol/specifications/specification-current/#position
--- Returns a zero-indexed column, since set_lines() does the conversion to
----@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to utf-16
+---@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
@@ -286,7 +331,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
@@ -300,54 +345,27 @@ end
--- Process and return progress reports from lsp server
---@private
function M.get_progress_messages()
-
local new_messages = {}
- local msg_remove = {}
local progress_remove = {}
for _, client in ipairs(vim.lsp.get_active_clients()) do
- local messages = client.messages
- local data = messages
- for token, ctx in pairs(data.progress) do
-
- local new_report = {
- name = data.name,
- title = ctx.title or "empty title",
- message = ctx.message,
- percentage = ctx.percentage,
- done = ctx.done,
- progress = true,
- }
- table.insert(new_messages, new_report)
-
- if ctx.done then
- table.insert(progress_remove, {client = client, token = token})
- end
- end
-
- for i, msg in ipairs(data.messages) do
- if msg.show_once then
- msg.shown = msg.shown + 1
- if msg.shown > 1 then
- table.insert(msg_remove, {client = client, idx = i})
- end
- end
-
- table.insert(new_messages, {name = data.name, content = msg.content})
- end
+ local messages = client.messages
+ local data = messages
+ for token, ctx in pairs(data.progress) do
+ local new_report = {
+ name = data.name,
+ title = ctx.title or 'empty title',
+ message = ctx.message,
+ percentage = ctx.percentage,
+ done = ctx.done,
+ progress = true,
+ }
+ table.insert(new_messages, new_report)
- if next(data.status) ~= nil then
- table.insert(new_messages, {
- name = data.name,
- content = data.status.content,
- uri = data.status.uri,
- status = true
- })
+ if ctx.done then
+ table.insert(progress_remove, { client = client, token = token })
end
- for _, item in ipairs(msg_remove) do
- table.remove(client.messages, item.idx)
end
-
end
for _, item in ipairs(progress_remove) do
@@ -360,16 +378,17 @@ 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
---@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 or M._get_offset_encoding(bufnr)
- if not next(text_edits) then return end
+ validate({
+ text_edits = { text_edits, 't', false },
+ bufnr = { bufnr, 'number', false },
+ offset_encoding = { offset_encoding, 'string', false },
+ })
+ if not next(text_edits) then
+ return
+ end
if not api.nvim_buf_is_loaded(bufnr) then
vim.fn.bufload(bufnr)
end
@@ -381,7 +400,11 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding)
index = index + 1
text_edit._index = index
- if text_edit.range.start.line > text_edit.range['end'].line or text_edit.range.start.line == text_edit.range['end'].line and text_edit.range.start.character > text_edit.range['end'].character then
+ if
+ text_edit.range.start.line > text_edit.range['end'].line
+ or text_edit.range.start.line == text_edit.range['end'].line
+ and text_edit.range.start.character > text_edit.range['end'].character
+ then
local start = text_edit.range.start
text_edit.range.start = text_edit.range['end']
text_edit.range['end'] = start
@@ -402,28 +425,9 @@ 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
+ local is_current_buf = api.nvim_get_current_buf() == bufnr
local cursor = (function()
if not is_current_buf then
return {
@@ -431,7 +435,7 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding)
col = -1,
}
end
- local cursor = vim.api.nvim_win_get_cursor(0)
+ local cursor = api.nvim_win_get_cursor(0)
return {
row = cursor[1] - 1,
col = cursor[2],
@@ -440,16 +444,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']),
- text = vim.split(text_edit.newText, '\n', true),
+ end_col = get_line_byte_from_position(bufnr, text_edit.range['end'], offset_encoding),
+ text = split(text_edit.newText, '\n', true),
}
- vim.api.nvim_buf_set_text(bufnr, e.start_row, e.start_col, e.end_row, e.end_col, e.text)
+ -- 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 = 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
+ 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)
@@ -464,21 +490,28 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding)
end
end
+ local max = 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 })
+ api.nvim_win_set_cursor(0, { cursor.row + 1, cursor.col })
end
end
-- 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 (
+ api.nvim_buf_get_option(bufnr, 'eol')
+ or (api.nvim_buf_get_option(bufnr, 'fixeol') and not api.nvim_buf_get_option(bufnr, 'binary'))
+ )
+ fix_eol = fix_eol and get_line(bufnr, max - 1) == ''
if fix_eol then
- vim.api.nvim_buf_set_lines(bufnr, -2, -1, false, {})
+ api.nvim_buf_set_lines(bufnr, -2, -1, false, {})
end
end
@@ -514,9 +547,15 @@ 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.
@@ -527,15 +566,20 @@ function M.apply_text_document_edit(text_document_edit, index)
-- `VersionedTextDocumentIdentifier`s version may be null
-- https://microsoft.github.io/language-server-protocol/specification#versionedTextDocumentIdentifier
- if should_check_version and (text_document.version
+ if
+ should_check_version
+ and (
+ text_document.version
and text_document.version > 0
and M.buf_versions[bufnr]
- and M.buf_versions[bufnr] > text_document.version) then
- print("Buffer ", text_document.uri, " newer than edits.")
+ and M.buf_versions[bufnr] > text_document.version
+ )
+ then
+ print('Buffer ', text_document.uri, ' newer than edits.')
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.
@@ -567,16 +611,16 @@ end
--- precedence is as follows: textEdit.newText > insertText > label
--see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion
local function get_completion_word(item)
- if item.textEdit ~= nil and item.textEdit.newText ~= nil and item.textEdit.newText ~= "" then
+ if item.textEdit ~= nil and item.textEdit.newText ~= nil and item.textEdit.newText ~= '' then
local insert_text_format = protocol.InsertTextFormat[item.insertTextFormat]
- if insert_text_format == "PlainText" or insert_text_format == nil then
+ if insert_text_format == 'PlainText' or insert_text_format == nil then
return item.textEdit.newText
else
return M.parse_snippet(item.textEdit.newText)
end
- elseif item.insertText ~= nil and item.insertText ~= "" then
+ elseif item.insertText ~= nil and item.insertText ~= '' then
local insert_text_format = protocol.InsertTextFormat[item.insertTextFormat]
- if insert_text_format == "PlainText" or insert_text_format == nil then
+ if insert_text_format == 'PlainText' or insert_text_format == nil then
return item.insertText
else
return M.parse_snippet(item.insertText)
@@ -604,7 +648,7 @@ end
---@returns (`vim.lsp.protocol.completionItemKind`)
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion
function M._get_completion_item_kind_name(completion_item_kind)
- return protocol.CompletionItemKind[completion_item_kind] or "Unknown"
+ return protocol.CompletionItemKind[completion_item_kind] or 'Unknown'
end
--- Turns the result of a `textDocument/completion` request into vim-compatible
@@ -635,7 +679,7 @@ function M.text_document_completion_list_to_complete_items(result, prefix)
info = documentation
elseif type(documentation) == 'table' and type(documentation.value) == 'string' then
info = documentation.value
- -- else
+ -- else
-- TODO(ashkan) Validation handling here?
end
end
@@ -653,9 +697,9 @@ function M.text_document_completion_list_to_complete_items(result, prefix)
user_data = {
nvim = {
lsp = {
- completion_item = completion_item
- }
- }
+ completion_item = completion_item,
+ },
+ },
},
})
end
@@ -663,6 +707,15 @@ function M.text_document_completion_list_to_complete_items(result, prefix)
return matches
end
+---@private
+--- Like vim.fn.bufwinid except it works across tabpages.
+local function bufwinid(bufnr)
+ for _, win in ipairs(api.nvim_list_wins()) do
+ if api.nvim_win_get_buf(win) == bufnr then
+ return win
+ end
+ end
+end
--- Rename old_fname to new_fname
---
@@ -671,7 +724,7 @@ end
-- ignoreIfExists? bool
function M.rename(old_fname, new_fname, opts)
opts = opts or {}
- local target_exists = vim.loop.fs_stat(new_fname) ~= nil
+ local target_exists = uv.fs_stat(new_fname) ~= nil
if target_exists and not opts.overwrite or opts.ignoreIfExists then
vim.notify('Rename target already exists. Skipping rename.')
return
@@ -688,10 +741,9 @@ function M.rename(old_fname, new_fname, opts)
assert(ok, err)
local newbuf = vim.fn.bufadd(new_fname)
- for _, win in pairs(api.nvim_list_wins()) do
- if api.nvim_win_get_buf(win) == oldbuf then
- api.nvim_win_set_buf(win, newbuf)
- end
+ local win = bufwinid(oldbuf)
+ if win then
+ api.nvim_win_set_buf(win, newbuf)
end
api.nvim_buf_delete(oldbuf, { force = true })
end
@@ -712,11 +764,11 @@ end
local function delete_file(change)
local opts = change.options or {}
local fname = vim.uri_to_fname(change.uri)
- local stat = vim.loop.fs_stat(fname)
+ local stat = uv.fs_stat(fname)
if opts.ignoreIfNotExists and not stat then
return
end
- assert(stat, "Cannot delete not existing file or folder " .. fname)
+ assert(stat, 'Cannot delete not existing file or folder ' .. fname)
local flags
if stat and stat.type == 'directory' then
flags = opts.recursive and 'rf' or 'd'
@@ -729,28 +781,30 @@ local function delete_file(change)
api.nvim_buf_delete(bufnr, { force = true })
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
- M.rename(
- vim.uri_to_fname(change.oldUri),
- vim.uri_to_fname(change.newUri),
- change.options
- )
+ if change.kind == 'rename' then
+ M.rename(vim.uri_to_fname(change.oldUri), vim.uri_to_fname(change.newUri), change.options)
elseif change.kind == 'create' then
create_file(change)
elseif change.kind == 'delete' then
delete_file(change)
elseif change.kind then
- error(string.format("Unsupported change: %q", vim.inspect(change)))
+ 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
@@ -763,7 +817,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
@@ -782,7 +836,7 @@ function M.convert_input_to_markdown_lines(input, contents)
if type(input) == 'string' then
list_extend(contents, split_lines(input))
else
- assert(type(input) == 'table', "Expected a table for Hover.contents")
+ assert(type(input) == 'table', 'Expected a table for Hover.contents')
-- MarkupContent
if input.kind then
-- The kind can be either plaintext or markdown.
@@ -791,22 +845,22 @@ function M.convert_input_to_markdown_lines(input, contents)
-- Some servers send input.value as empty, so let's ignore this :(
local value = input.value or ''
- if input.kind == "plaintext" then
+ if input.kind == 'plaintext' then
-- wrap this in a <text></text> block so that stylize_markdown
-- can properly process it as plaintext
- value = string.format("<text>\n%s\n</text>", value)
+ value = string.format('<text>\n%s\n</text>', value)
end
-- assert(type(value) == 'string')
list_extend(contents, split_lines(value))
- -- MarkupString variation 2
+ -- MarkupString variation 2
elseif input.language then
-- Some servers send input.value as empty, so let's ignore this :(
-- assert(type(input.value) == 'string')
- table.insert(contents, "```"..input.language)
+ table.insert(contents, '```' .. input.language)
list_extend(contents, split_lines(input.value or ''))
- table.insert(contents, "```")
- -- By deduction, this must be MarkedString[]
+ table.insert(contents, '```')
+ -- By deduction, this must be MarkedString[]
else
-- Use our existing logic to handle MarkedString
for _, marked_string in ipairs(input) do
@@ -839,7 +893,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]
@@ -849,16 +904,16 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers
local label = signature.label
if ft then
-- wrap inside a code block so stylize_markdown can render it properly
- label = ("```%s\n%s\n```"):format(ft, label)
+ label = ('```%s\n%s\n```'):format(ft, label)
end
- vim.list_extend(contents, vim.split(label, '\n', true))
+ list_extend(contents, split(label, '\n', true))
if signature.documentation then
M.convert_input_to_markdown_lines(signature.documentation, contents)
end
if signature.parameters and #signature.parameters > 0 then
local active_parameter = (signature.activeParameter or signature_help.activeParameter or 0)
- if active_parameter < 0
- then active_parameter = 0
+ if active_parameter < 0 then
+ active_parameter = 0
end
-- If the activeParameter is > #parameters, then set it to the last
@@ -888,7 +943,7 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers
}
--]=]
if parameter.label then
- if type(parameter.label) == "table" then
+ if type(parameter.label) == 'table' then
active_hl = parameter.label
else
local offset = 1
@@ -901,9 +956,11 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers
end
for p, param in pairs(signature.parameters) do
offset = signature.label:find(param.label, offset, true)
- if not offset then break end
+ if not offset then
+ break
+ end
if p == active_parameter + 1 then
- active_hl = {offset - 1, offset + #parameter.label - 1}
+ active_hl = { offset - 1, offset + #parameter.label - 1 }
break
end
offset = offset + #param.label + 1
@@ -931,14 +988,14 @@ end
--- - zindex (string or table) override `zindex`, defaults to 50
---@returns (table) Options
function M.make_floating_popup_options(width, height, opts)
- validate {
- opts = { opts, 't', true };
- }
+ validate({
+ opts = { opts, 't', true },
+ })
opts = opts or {}
- validate {
- ["opts.offset_x"] = { opts.offset_x, 'n', true };
- ["opts.offset_y"] = { opts.offset_y, 'n', true };
- }
+ validate({
+ ['opts.offset_x'] = { opts.offset_x, 'n', true },
+ ['opts.offset_y'] = { opts.offset_y, 'n', true },
+ })
local anchor = ''
local row, col
@@ -947,20 +1004,20 @@ function M.make_floating_popup_options(width, height, opts)
local lines_below = vim.fn.winheight(0) - lines_above
if lines_above < lines_below then
- anchor = anchor..'N'
+ anchor = anchor .. 'N'
height = math.min(lines_below, height)
row = 1
else
- anchor = anchor..'S'
+ anchor = anchor .. 'S'
height = math.min(lines_above, height)
row = 0
end
if vim.fn.wincol() + width + (opts.offset_x or 0) <= api.nvim_get_option('columns') then
- anchor = anchor..'W'
+ anchor = anchor .. 'W'
col = 0
else
- anchor = anchor..'E'
+ anchor = anchor .. 'E'
col = 1
end
@@ -980,30 +1037,45 @@ 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)
+---@param reuse_win boolean Jump to existing window if buffer is already opened.
---@returns `true` if the jump succeeded
-function M.jump_to_location(location)
+function M.jump_to_location(location, offset_encoding, reuse_win)
-- location may be Location or LocationLink
local uri = location.uri or location.targetUri
- if uri == nil then return end
+ 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'"
+ vim.cmd("normal! m'")
-- Push a new item into tagstack
- local from = {vim.fn.bufnr('%'), vim.fn.line('.'), vim.fn.col('.'), 0}
- local items = {{tagname=vim.fn.expand('<cword>'), from=from}}
- vim.fn.settagstack(vim.fn.win_getid(), {items=items}, 't')
+ local from = { vim.fn.bufnr('%'), vim.fn.line('.'), vim.fn.col('.'), 0 }
+ local items = { { tagname = vim.fn.expand('<cword>'), from = from } }
+ vim.fn.settagstack(vim.fn.win_getid(), { items = items }, 't')
--- Jump to new location (adjusting for UTF-16 encoding of characters)
- api.nvim_set_current_buf(bufnr)
- api.nvim_buf_set_option(bufnr, 'buflisted', true)
+ local win = reuse_win and bufwinid(bufnr)
+ if win then
+ api.nvim_set_current_win(win)
+ else
+ api.nvim_buf_set_option(bufnr, 'buflisted', true)
+ api.nvim_set_current_buf(bufnr)
+ end
local range = location.range or location.targetSelectionRange
local row = range.start.line
- local col = get_line_byte_from_position(bufnr, range.start)
- api.nvim_win_set_cursor(0, {row + 1, col})
+ 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")
+ vim.cmd('normal! zv')
return true
end
@@ -1018,22 +1090,24 @@ end
function M.preview_location(location, opts)
-- location may be LocationLink or Location (more useful for the former)
local uri = location.targetUri or location.uri
- if uri == nil then return end
+ if uri == nil then
+ return
+ end
local bufnr = vim.uri_to_bufnr(uri)
if not api.nvim_buf_is_loaded(bufnr) then
vim.fn.bufload(bufnr)
end
local range = location.targetRange or location.range
- local contents = api.nvim_buf_get_lines(bufnr, range.start.line, range["end"].line+1, false)
+ local contents = api.nvim_buf_get_lines(bufnr, range.start.line, range['end'].line + 1, false)
local syntax = api.nvim_buf_get_option(bufnr, 'syntax')
- if syntax == "" then
+ if syntax == '' then
-- When no syntax is set, we use filetype as fallback. This might not result
-- in a valid syntax definition. See also ft detection in stylize_markdown.
-- An empty syntax is more common now with TreeSitter, since TS disables syntax.
syntax = api.nvim_buf_get_option(bufnr, 'filetype')
end
opts = opts or {}
- opts.focus_id = "location"
+ opts.focus_id = 'location'
return M.open_floating_preview(contents, syntax, opts)
end
@@ -1054,20 +1128,20 @@ end
--- - pad_bottom number of lines to pad contents at bottom (default 0)
---@return contents table of trimmed and padded lines
function M._trim(contents, opts)
- validate {
- contents = { contents, 't' };
- opts = { opts, 't', true };
- }
+ validate({
+ contents = { contents, 't' },
+ opts = { opts, 't', true },
+ })
opts = opts or {}
contents = M.trim_empty_lines(contents)
if opts.pad_top then
for _ = 1, opts.pad_top do
- table.insert(contents, 1, "")
+ table.insert(contents, 1, '')
end
end
if opts.pad_bottom then
for _ = 1, opts.pad_bottom do
- table.insert(contents, "")
+ table.insert(contents, '')
end
end
return contents
@@ -1080,7 +1154,7 @@ end
local function get_markdown_fences()
local fences = {}
for _, fence in pairs(vim.g.markdown_fenced_languages or {}) do
- local lang, syntax = fence:match("^(.*)=(.*)$")
+ local lang, syntax = fence:match('^(.*)=(.*)$')
if lang then
fences[lang] = syntax
end
@@ -1109,28 +1183,28 @@ end
--- - separator insert separator after code block
---@returns width,height size of float
function M.stylize_markdown(bufnr, contents, opts)
- validate {
- contents = { contents, 't' };
- opts = { opts, 't', true };
- }
+ validate({
+ contents = { contents, 't' },
+ opts = { opts, 't', true },
+ })
opts = opts or {}
-- table of fence types to {ft, begin, end}
-- when ft is nil, we get the ft from the regex match
local matchers = {
- block = {nil, "```+([a-zA-Z0-9_]*)", "```+"},
- pre = {"", "<pre>", "</pre>"},
- code = {"", "<code>", "</code>"},
- text = {"plaintex", "<text>", "</text>"},
+ block = { nil, '```+([a-zA-Z0-9_]*)', '```+' },
+ pre = { '', '<pre>', '</pre>' },
+ code = { '', '<code>', '</code>' },
+ text = { 'text', '<text>', '</text>' },
}
local match_begin = function(line)
for type, pattern in pairs(matchers) do
- local ret = line:match(string.format("^%%s*%s%%s*$", pattern[2]))
+ local ret = line:match(string.format('^%%s*%s%%s*$', pattern[2]))
if ret then
return {
type = type,
- ft = pattern[1] or ret
+ ft = pattern[1] or ret,
}
end
end
@@ -1138,7 +1212,7 @@ function M.stylize_markdown(bufnr, contents, opts)
local match_end = function(line, match)
local pattern = matchers[match.type]
- return line:match(string.format("^%%s*%s%%s*$", pattern[3]))
+ return line:match(string.format('^%%s*%s%%s*$', pattern[3]))
end
-- Clean up
@@ -1168,25 +1242,34 @@ function M.stylize_markdown(bufnr, contents, opts)
i = i + 1
end
table.insert(highlights, {
- ft = match.ft;
- start = start + 1;
- finish = #stripped;
+ ft = match.ft,
+ start = start + 1,
+ finish = #stripped,
})
-- add a separator, but not on the last line
if add_sep and i < #contents then
- table.insert(stripped, "---")
+ table.insert(stripped, '---')
markdown_lines[#stripped] = true
end
else
-- strip any empty lines or separators prior to this separator in actual markdown
- if line:match("^---+$") then
- while markdown_lines[#stripped] and (stripped[#stripped]:match("^%s*$") or stripped[#stripped]:match("^---+$")) do
+ if line:match('^---+$') then
+ while
+ markdown_lines[#stripped]
+ and (stripped[#stripped]:match('^%s*$') or stripped[#stripped]:match('^---+$'))
+ do
markdown_lines[#stripped] = false
table.remove(stripped, #stripped)
end
end
-- add the line if its not an empty line following a separator
- if not (line:match("^%s*$") and markdown_lines[#stripped] and stripped[#stripped]:match("^---+$")) then
+ if
+ not (
+ line:match('^%s*$')
+ and markdown_lines[#stripped]
+ and stripped[#stripped]:match('^---+$')
+ )
+ then
table.insert(stripped, line)
markdown_lines[#stripped] = true
end
@@ -1196,18 +1279,18 @@ function M.stylize_markdown(bufnr, contents, opts)
end
-- Compute size of float needed to show (wrapped) lines
- opts.wrap_at = opts.wrap_at or (vim.wo["wrap"] and api.nvim_win_get_width(0))
+ opts.wrap_at = opts.wrap_at or (vim.wo['wrap'] and api.nvim_win_get_width(0))
local width = M._make_floating_popup_size(stripped, opts)
- local sep_line = string.rep("─", math.min(width, opts.wrap_at or width))
+ local sep_line = string.rep('─', math.min(width, opts.wrap_at or width))
for l in pairs(markdown_lines) do
- if stripped[l]:match("^---+$") then
+ if stripped[l]:match('^---+$') then
stripped[l] = sep_line
end
end
- vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, stripped)
+ api.nvim_buf_set_lines(bufnr, 0, -1, false, stripped)
local idx = 1
---@private
@@ -1216,24 +1299,38 @@ function M.stylize_markdown(bufnr, contents, opts)
local langs = {}
local fences = get_markdown_fences()
local function apply_syntax_to_region(ft, start, finish)
- if ft == "" then
- vim.cmd(string.format("syntax region markdownCode start=+\\%%%dl+ end=+\\%%%dl+ keepend extend", start, finish + 1))
+ if ft == '' then
+ vim.cmd(
+ string.format(
+ 'syntax region markdownCode start=+\\%%%dl+ end=+\\%%%dl+ keepend extend',
+ start,
+ finish + 1
+ )
+ )
return
end
ft = fences[ft] or ft
- local name = ft..idx
+ local name = ft .. idx
idx = idx + 1
- local lang = "@"..ft:upper()
+ local lang = '@' .. ft:upper()
if not langs[lang] then
-- HACK: reset current_syntax, since some syntax files like markdown won't load if it is already set
- pcall(vim.api.nvim_buf_del_var, bufnr, "current_syntax")
+ pcall(api.nvim_buf_del_var, bufnr, 'current_syntax')
-- TODO(ashkan): better validation before this.
- if not pcall(vim.cmd, string.format("syntax include %s syntax/%s.vim", lang, ft)) then
+ if not pcall(vim.cmd, string.format('syntax include %s syntax/%s.vim', lang, ft)) then
return
end
langs[lang] = true
end
- vim.cmd(string.format("syntax region %s start=+\\%%%dl+ end=+\\%%%dl+ contains=%s keepend", name, start, finish + 1, lang))
+ vim.cmd(
+ string.format(
+ 'syntax region %s start=+\\%%%dl+ end=+\\%%%dl+ contains=%s keepend',
+ name,
+ start,
+ finish + 1,
+ lang
+ )
+ )
end
-- needs to run in the buffer for the regions to work
@@ -1244,13 +1341,13 @@ function M.stylize_markdown(bufnr, contents, opts)
local last = 1
for _, h in ipairs(highlights) do
if last < h.start then
- apply_syntax_to_region("lsp_markdown", last, h.start - 1)
+ apply_syntax_to_region('lsp_markdown', last, h.start - 1)
end
apply_syntax_to_region(h.ft, h.start, h.finish)
last = h.finish + 1
end
if last <= #stripped then
- apply_syntax_to_region("lsp_markdown", last, #stripped)
+ apply_syntax_to_region('lsp_markdown', last, #stripped)
end
end)
@@ -1258,6 +1355,24 @@ function M.stylize_markdown(bufnr, contents, opts)
end
---@private
+--- Closes the preview window
+---
+---@param winnr number window id of preview window
+---@param bufnrs table|nil optional list of ignored buffers
+local function close_preview_window(winnr, bufnrs)
+ vim.schedule(function()
+ -- exit if we are in one of ignored buffers
+ if bufnrs and vim.tbl_contains(bufnrs, api.nvim_get_current_buf()) then
+ return
+ end
+
+ local augroup = 'preview_window_' .. winnr
+ pcall(api.nvim_del_augroup_by_name, augroup)
+ pcall(api.nvim_win_close, winnr, true)
+ end)
+end
+
+---@private
--- Creates autocommands to close a preview window when events happen.
---
---@param events table list of events
@@ -1265,49 +1380,30 @@ end
---@param bufnrs table list of buffers where the preview window will remain visible
---@see |autocmd-events|
local function close_preview_autocmd(events, winnr, bufnrs)
- local augroup = 'preview_window_'..winnr
+ local augroup = api.nvim_create_augroup('preview_window_' .. winnr, {
+ clear = true,
+ })
-- close the preview window when entered a buffer that is not
-- the floating window buffer or the buffer that spawned it
- vim.cmd(string.format([[
- augroup %s
- autocmd!
- autocmd BufEnter * lua vim.lsp.util._close_preview_window(%d, {%s})
- augroup end
- ]], augroup, winnr, table.concat(bufnrs, ',')))
+ api.nvim_create_autocmd('BufEnter', {
+ group = augroup,
+ callback = function()
+ close_preview_window(winnr, bufnrs)
+ end,
+ })
if #events > 0 then
- vim.cmd(string.format([[
- augroup %s
- autocmd %s <buffer> lua vim.lsp.util._close_preview_window(%d)
- augroup end
- ]], augroup, table.concat(events, ','), winnr))
+ api.nvim_create_autocmd(events, {
+ group = augroup,
+ buffer = bufnrs[2],
+ callback = function()
+ close_preview_window(winnr)
+ end,
+ })
end
end
----@private
---- Closes the preview window
----
----@param winnr number window id of preview window
----@param bufnrs table|nil optional list of ignored buffers
-function M._close_preview_window(winnr, bufnrs)
- vim.schedule(function()
- -- exit if we are in one of ignored buffers
- if bufnrs and vim.tbl_contains(bufnrs, api.nvim_get_current_buf()) then
- return
- end
-
- local augroup = 'preview_window_'..winnr
- vim.cmd(string.format([[
- augroup %s
- autocmd!
- augroup end
- augroup! %s
- ]], augroup, augroup))
- pcall(vim.api.nvim_win_close, winnr, true)
- end)
-end
-
---@internal
--- Computes size of float needed to show contents (with optional wrapping)
---
@@ -1320,10 +1416,10 @@ end
--- - max_height maximal height of floating window
---@returns width,height size of float
function M._make_floating_popup_size(contents, opts)
- validate {
- contents = { contents, 't' };
- opts = { opts, 't', true };
- }
+ validate({
+ contents = { contents, 't' },
+ opts = { opts, 't', true },
+ })
opts = opts or {}
local width = opts.width
@@ -1367,11 +1463,11 @@ function M._make_floating_popup_size(contents, opts)
if vim.tbl_isempty(line_widths) then
for _, line in ipairs(contents) do
local line_width = vim.fn.strdisplaywidth(line)
- height = height + math.ceil(line_width/wrap_at)
+ height = height + math.ceil(line_width / wrap_at)
end
else
for i = 1, #contents do
- height = height + math.max(1, math.ceil(line_widths[i]/wrap_at))
+ height = height + math.max(1, math.ceil(line_widths[i] / wrap_at))
end
end
end
@@ -1391,7 +1487,7 @@ end
--- - height: (number) height of floating window
--- - width: (number) width of floating window
--- - wrap: (boolean, default true) wrap long lines
---- - wrap_at: (string) character to wrap at for computing height when wrap is enabled
+--- - wrap_at: (number) character to wrap at for computing height when wrap is enabled
--- - max_width: (number) maximal width of floating window
--- - max_height: (number) maximal height of floating window
--- - pad_top: (number) number of lines to pad contents at top
@@ -1405,16 +1501,16 @@ end
---@returns bufnr,winnr buffer and window number of the newly created floating
---preview window
function M.open_floating_preview(contents, syntax, opts)
- validate {
- contents = { contents, 't' };
- syntax = { syntax, 's', true };
- opts = { opts, 't', true };
- }
+ validate({
+ contents = { contents, 't' },
+ syntax = { syntax, 's', true },
+ opts = { opts, 't', true },
+ })
opts = opts or {}
opts.wrap = opts.wrap ~= false -- wrapping by default
- opts.stylize_markdown = opts.stylize_markdown ~= false
+ opts.stylize_markdown = opts.stylize_markdown ~= false and vim.g.syntax_on ~= nil
opts.focus = opts.focus ~= false
- opts.close_events = opts.close_events or {"CursorMoved", "CursorMovedI", "InsertCharPre"}
+ opts.close_events = opts.close_events or { 'CursorMoved', 'CursorMovedI', 'InsertCharPre' }
local bufnr = api.nvim_get_current_buf()
@@ -1423,7 +1519,7 @@ function M.open_floating_preview(contents, syntax, opts)
-- Go back to previous window if we are in a focusable one
local current_winnr = api.nvim_get_current_win()
if npcall(api.nvim_win_get_var, current_winnr, opts.focus_id) then
- api.nvim_command("wincmd p")
+ api.nvim_command('wincmd p')
return bufnr, current_winnr
end
do
@@ -1431,7 +1527,7 @@ function M.open_floating_preview(contents, syntax, opts)
if win and api.nvim_win_is_valid(win) and vim.fn.pumvisible() == 0 then
-- focus and return the existing buf, win
api.nvim_set_current_win(win)
- api.nvim_command("stopinsert")
+ api.nvim_command('stopinsert')
return api.nvim_win_get_buf(win), win
end
end
@@ -1439,14 +1535,13 @@ function M.open_floating_preview(contents, syntax, opts)
-- check if another floating preview already exists for this buffer
-- and close it if needed
- local existing_float = npcall(api.nvim_buf_get_var, bufnr, "lsp_floating_preview")
+ local existing_float = npcall(api.nvim_buf_get_var, bufnr, 'lsp_floating_preview')
if existing_float and api.nvim_win_is_valid(existing_float) then
api.nvim_win_close(existing_float, true)
end
local floating_bufnr = api.nvim_create_buf(false, true)
- local do_stylize = syntax == "markdown" and opts.stylize_markdown
-
+ local do_stylize = syntax == 'markdown' and opts.stylize_markdown
-- Clean up input: trim empty lines from the end, pad
contents = M._trim(contents, opts)
@@ -1482,26 +1577,32 @@ function M.open_floating_preview(contents, syntax, opts)
api.nvim_buf_set_option(floating_bufnr, 'modifiable', false)
api.nvim_buf_set_option(floating_bufnr, 'bufhidden', 'wipe')
- api.nvim_buf_set_keymap(floating_bufnr, "n", "q", "<cmd>bdelete<cr>", {silent = true, noremap = true, nowait = true})
- close_preview_autocmd(opts.close_events, floating_winnr, {floating_bufnr, bufnr})
+ api.nvim_buf_set_keymap(
+ floating_bufnr,
+ 'n',
+ 'q',
+ '<cmd>bdelete<cr>',
+ { silent = true, noremap = true, nowait = true }
+ )
+ close_preview_autocmd(opts.close_events, floating_winnr, { floating_bufnr, bufnr })
-- save focus_id
if opts.focus_id then
api.nvim_win_set_var(floating_winnr, opts.focus_id, bufnr)
end
- api.nvim_buf_set_var(bufnr, "lsp_floating_preview", floating_winnr)
+ api.nvim_buf_set_var(bufnr, 'lsp_floating_preview', floating_winnr)
return floating_bufnr, floating_winnr
end
do --[[ References ]]
- local reference_ns = api.nvim_create_namespace("vim_lsp_references")
+ local reference_ns = api.nvim_create_namespace('vim_lsp_references')
--- Removes document highlights from a buffer.
---
---@param bufnr number Buffer id
function M.buf_clear_references(bufnr)
- validate { bufnr = {bufnr, 'n', true} }
+ validate({ bufnr = { bufnr, 'n', true } })
api.nvim_buf_clear_namespace(bufnr or 0, reference_ns, 0, -1)
end
@@ -1509,35 +1610,50 @@ 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"]
-
- local start_idx = get_line_byte_from_position(bufnr, { line = start_line, character = start_char }, offset_encoding)
- local end_idx = get_line_byte_from_position(bufnr, { line = start_line, character = end_char }, offset_encoding)
+ local start_line, start_char =
+ reference['range']['start']['line'], reference['range']['start']['character']
+ local end_line, end_char =
+ reference['range']['end']['line'], reference['range']['end']['character']
+
+ local start_idx = get_line_byte_from_position(
+ bufnr,
+ { line = start_line, character = start_char },
+ offset_encoding
+ )
+ local end_idx = get_line_byte_from_position(
+ bufnr,
+ { line = start_line, character = end_char },
+ offset_encoding
+ )
local document_highlight_kind = {
- [protocol.DocumentHighlightKind.Text] = "LspReferenceText";
- [protocol.DocumentHighlightKind.Read] = "LspReferenceRead";
- [protocol.DocumentHighlightKind.Write] = "LspReferenceWrite";
+ [protocol.DocumentHighlightKind.Text] = 'LspReferenceText',
+ [protocol.DocumentHighlightKind.Read] = 'LspReferenceRead',
+ [protocol.DocumentHighlightKind.Write] = 'LspReferenceWrite',
}
- local kind = reference["kind"] or protocol.DocumentHighlightKind.Text
- highlight.range(bufnr,
- reference_ns,
- document_highlight_kind[kind],
- { start_line, start_idx },
- { end_line, end_idx })
+ local kind = reference['kind'] or protocol.DocumentHighlightKind.Text
+ highlight.range(
+ bufnr,
+ reference_ns,
+ document_highlight_kind[kind],
+ { start_line, start_idx },
+ { end_line, end_idx },
+ { priority = vim.highlight.priorities.user }
+ )
end
end
end
local position_sort = sort_by_key(function(v)
- return {v.start.line, v.start.character}
+ return { v.start.line, v.start.character }
end)
--- Returns the items with the byte position calculated correctly and in sorted
@@ -1546,25 +1662,32 @@ 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)
local v = {}
rawset(t, k, v)
return v
- end;
+ end,
})
for _, d in ipairs(locations) do
-- locations may be Location or LocationLink
local uri = d.uri or d.targetUri
local range = d.range or d.targetSelectionRange
- table.insert(grouped[uri], {start = range.start})
+ table.insert(grouped[uri], { start = range.start })
end
-
local keys = vim.tbl_keys(grouped)
table.sort(keys)
-- TODO(ashkan) I wish we could do this lazily.
@@ -1587,53 +1710,24 @@ function M.locations_to_items(locations)
for _, temp in ipairs(rows) do
local pos = temp.start
local row = pos.line
- local line = lines[row] or ""
- local col = pos.character
+ local line = lines[row] or ''
+ local col = M._str_byteindex_enc(line, pos.character, offset_encoding)
table.insert(items, {
filename = filename,
lnum = row + 1,
- col = col + 1;
- text = line;
+ col = col + 1,
+ text = line,
})
end
end
return items
end
---- Fills target window's location list with given list of items.
---- Can be obtained with e.g. |vim.lsp.util.locations_to_items()|.
---- Defaults to current window.
----
----@deprecated Use |setloclist()|
----
----@param items (table) list of items
-function M.set_loclist(items, win_id)
- vim.api.nvim_echo({{'vim.lsp.util.set_loclist is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
- vim.fn.setloclist(win_id or 0, {}, ' ', {
- title = 'Language Server';
- items = items;
- })
-end
-
---- Fills quickfix list with given list of items.
---- Can be obtained with e.g. |vim.lsp.util.locations_to_items()|.
----
----@deprecated Use |setqflist()|
----
----@param items (table) list of items
-function M.set_qflist(items)
- vim.api.nvim_echo({{'vim.lsp.util.set_qflist is deprecated. See :h deprecated', 'WarningMsg'}}, true, {})
- vim.fn.setqflist({}, ' ', {
- title = 'Language Server';
- items = items;
- })
-end
-
-- According to LSP spec, if the client set "symbolKind.valueSet",
-- the client must handle it properly even if it receives a value outside the specification.
-- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol
function M._get_symbol_kind_name(symbol_kind)
- return protocol.SymbolKind[symbol_kind] or "Unknown"
+ return protocol.SymbolKind[symbol_kind] or 'Unknown'
end
--- Converts symbols to quickfix list items.
@@ -1651,17 +1745,17 @@ function M.symbols_to_items(symbols, bufnr)
lnum = range.start.line + 1,
col = range.start.character + 1,
kind = kind,
- text = '['..kind..'] '..symbol.name,
+ text = '[' .. kind .. '] ' .. symbol.name,
})
elseif symbol.selectionRange then -- DocumentSymbole type
local kind = M._get_symbol_kind_name(symbol.kind)
table.insert(_items, {
-- bufnr = _bufnr,
- filename = vim.api.nvim_buf_get_name(_bufnr),
+ filename = api.nvim_buf_get_name(_bufnr),
lnum = symbol.selectionRange.start.line + 1,
col = symbol.selectionRange.start.character + 1,
kind = kind,
- text = '['..kind..'] '..symbol.name
+ text = '[' .. kind .. '] ' .. symbol.name,
})
if symbol.children then
for _, v in ipairs(_symbols_to_items(symbol.children, _items, _bufnr)) do
@@ -1695,7 +1789,7 @@ function M.trim_empty_lines(lines)
break
end
end
- return vim.list_extend({}, lines, start, finish)
+ return list_extend({}, lines, start, finish)
end
--- Accepts markdown lines and tries to reduce them to a filetype if they
@@ -1706,12 +1800,12 @@ end
---@param lines (table) list of lines
---@returns (string) filetype or 'markdown' if it was unchanged.
function M.try_trim_markdown_code_blocks(lines)
- local language_id = lines[1]:match("^```(.*)")
+ local language_id = lines[1]:match('^```(.*)')
if language_id then
local has_inner_code_fence = false
for i = 2, (#lines - 1) do
local line = lines[i]
- if line:sub(1,3) == '```' then
+ if line:sub(1, 3) == '```' then
has_inner_code_fence = true
break
end
@@ -1731,18 +1825,18 @@ end
---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of buffer of `window`
local function make_position_param(window, offset_encoding)
window = window or 0
- local buf = vim.api.nvim_win_get_buf(window)
+ local buf = api.nvim_win_get_buf(window)
local row, col = unpack(api.nvim_win_get_cursor(window))
offset_encoding = offset_encoding or M._get_offset_encoding(buf)
row = row - 1
- local line = api.nvim_buf_get_lines(buf, row, row+1, true)[1]
+ local line = api.nvim_buf_get_lines(buf, row, row + 1, true)[1]
if not line then
- return { line = 0; character = 0; }
+ return { line = 0, character = 0 }
end
col = _str_utfindex_enc(line, col, offset_encoding)
- return { line = row; character = col; }
+ return { line = row, character = col }
end
--- Creates a `TextDocumentPositionParams` object for the current buffer and cursor position.
@@ -1753,11 +1847,11 @@ end
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentPositionParams
function M.make_position_params(window, offset_encoding)
window = window or 0
- local buf = vim.api.nvim_win_get_buf(window)
+ local buf = api.nvim_win_get_buf(window)
offset_encoding = offset_encoding or M._get_offset_encoding(buf)
return {
- textDocument = M.make_text_document_params(buf);
- position = make_position_param(window, offset_encoding)
+ textDocument = M.make_text_document_params(buf),
+ position = make_position_param(window, offset_encoding),
}
end
@@ -1765,18 +1859,30 @@ end
---@param bufnr (number) buffer handle or 0 for current, defaults to current
---@returns (string) encoding first client if there is one, nil otherwise
function M._get_offset_encoding(bufnr)
- validate {
- bufnr = {bufnr, 'n', true};
- }
+ validate({
+ bufnr = { bufnr, 'n', true },
+ })
local offset_encoding
for _, client in pairs(vim.lsp.buf_get_clients(bufnr)) do
- local this_offset_encoding = client.offset_encoding or "utf-16"
+ if 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
- vim.notify("warning: multiple different client offset_encodings detected for buffer, this is not supported yet", vim.log.levels.WARN)
+ vim.notify(
+ 'warning: multiple different client offset_encodings detected for buffer, this is not supported yet',
+ vim.log.levels.WARN
+ )
end
end
@@ -1793,12 +1899,12 @@ end
---@returns { textDocument = { uri = `current_file_uri` }, range = { start =
---`current_position`, end = `current_position` } }
function M.make_range_params(window, offset_encoding)
- local buf = vim.api.nvim_win_get_buf(window or 0)
+ local buf = api.nvim_win_get_buf(window or 0)
offset_encoding = offset_encoding or M._get_offset_encoding(buf)
local position = make_position_param(window, offset_encoding)
return {
textDocument = M.make_text_document_params(buf),
- range = { start = position; ["end"] = position; }
+ range = { start = position, ['end'] = position },
}
end
@@ -1814,12 +1920,12 @@ end
---@returns { textDocument = { uri = `current_file_uri` }, range = { start =
---`start_position`, end = `end_position` } }
function M.make_given_range_params(start_pos, end_pos, bufnr, offset_encoding)
- validate {
- start_pos = {start_pos, 't', true};
- end_pos = {end_pos, 't', true};
- offset_encoding = {offset_encoding, 's', true};
- }
- bufnr = bufnr or vim.api.nvim_get_current_buf()
+ validate({
+ start_pos = { start_pos, 't', true },
+ end_pos = { end_pos, 't', true },
+ offset_encoding = { offset_encoding, 's', true },
+ })
+ bufnr = bufnr or 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, '>'))
@@ -1828,10 +1934,10 @@ function M.make_given_range_params(start_pos, end_pos, bufnr, offset_encoding)
B[1] = B[1] - 1
-- account for offset_encoding.
if A[2] > 0 then
- A = {A[1], M.character_offset(bufnr, A[1], A[2], offset_encoding)}
+ A = { A[1], M.character_offset(bufnr, A[1], A[2], offset_encoding) }
end
if B[2] > 0 then
- B = {B[1], M.character_offset(bufnr, B[1], B[2], offset_encoding)}
+ B = { B[1], M.character_offset(bufnr, B[1], B[2], offset_encoding) }
end
-- we need to offset the end character position otherwise we loose the last
-- character of the selection, as LSP end position is exclusive
@@ -1842,9 +1948,9 @@ function M.make_given_range_params(start_pos, end_pos, bufnr, offset_encoding)
return {
textDocument = M.make_text_document_params(bufnr),
range = {
- start = {line = A[1], character = A[2]},
- ['end'] = {line = B[1], character = B[2]}
- }
+ start = { line = A[1], character = A[2] },
+ ['end'] = { line = B[1], character = B[2] },
+ },
}
end
@@ -1861,34 +1967,34 @@ end
---@param added
---@param removed
function M.make_workspace_params(added, removed)
- return { event = { added = added; removed = removed; } }
+ return { event = { added = added, removed = removed } }
end
---- Returns visual width of tabstop.
+--- Returns indentation size.
---
----@see |softtabstop|
----@param bufnr (optional, number): Buffer handle, defaults to current
----@returns (number) tabstop visual width
+---@see |shiftwidth|
+---@param bufnr (number|nil): Buffer handle, defaults to current
+---@returns (number) indentation size
function M.get_effective_tabstop(bufnr)
- validate { bufnr = {bufnr, 'n', true} }
+ validate({ bufnr = { bufnr, 'n', true } })
local bo = bufnr and vim.bo[bufnr] or vim.bo
- local sts = bo.softtabstop
- return (sts > 0 and sts) or (sts < 0 and bo.shiftwidth) or bo.tabstop
+ local sw = bo.shiftwidth
+ return (sw == 0 and bo.tabstop) or sw
end
--- Creates a `DocumentFormattingParams` object for the current buffer and cursor position.
---
----@param options Table with valid `FormattingOptions` entries
+---@param options table|nil with valid `FormattingOptions` entries
---@returns `DocumentFormattingParams` object
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting
function M.make_formatting_params(options)
- validate { options = {options, 't', true} }
+ validate({ options = { options, 't', true } })
options = vim.tbl_extend('keep', options or {}, {
- tabSize = M.get_effective_tabstop();
- insertSpaces = vim.bo.expandtab;
+ tabSize = M.get_effective_tabstop(),
+ insertSpaces = vim.bo.expandtab,
})
return {
- textDocument = { uri = vim.uri_from_bufnr(0) };
- options = options;
+ textDocument = { uri = vim.uri_from_bufnr(0) },
+ options = options,
}
end
@@ -1901,7 +2007,12 @@ 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)
@@ -1917,8 +2028,8 @@ end
function M.lookup_section(settings, section)
for part in vim.gsplit(section, '.', true) do
settings = settings[part]
- if not settings then
- return
+ if settings == nil then
+ return vim.NIL
end
end
return settings
diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua
index e170befa4c..d6c3e25b3b 100644
--- a/runtime/lua/vim/shared.lua
+++ b/runtime/lua/vim/shared.lua
@@ -1,8 +1,10 @@
-- Functions shared by Nvim and its test-suite.
--
--- The singular purpose of this module is to share code with the Nvim
--- test-suite. If, in the future, Nvim itself is used to run the test-suite
--- instead of "vanilla Lua", these functions could move to src/nvim/lua/vim.lua
+-- These are "pure" lua functions not depending of the state of the editor.
+-- Thus they should always be available whenever nvim-related lua code is run,
+-- regardless if it is code in the editor itself, or in worker threads/processes,
+-- or the test suite. (Eventually the test suite will be run in a worker process,
+-- so this wouldn't be a separate case to consider)
local vim = vim or {}
@@ -13,8 +15,8 @@ local vim = vim or {}
--- copied and will throw an error.
---
---@param orig table Table to copy
----@returns New table of copied keys and (nested) values.
-function vim.deepcopy(orig) end -- luacheck: no unused
+---@return table Table of copied keys and (nested) values.
+function vim.deepcopy(orig) end -- luacheck: no unused
vim.deepcopy = (function()
local function _id(v)
return v
@@ -22,7 +24,9 @@ vim.deepcopy = (function()
local deepcopy_funcs = {
table = function(orig, cache)
- if cache[orig] then return cache[orig] end
+ if cache[orig] then
+ return cache[orig]
+ end
local copy = {}
cache[orig] = copy
@@ -44,7 +48,7 @@ vim.deepcopy = (function()
if f then
return f(orig, cache or {})
else
- error("Cannot deepcopy object of type "..type(orig))
+ error('Cannot deepcopy object of type ' .. type(orig))
end
end
end)()
@@ -55,19 +59,19 @@ end)()
---@see https://www.lua.org/pil/20.2.html
---@see http://lua-users.org/wiki/StringLibraryTutorial
---
----@param s String to split
----@param sep Separator string or pattern
----@param plain If `true` use `sep` literally (passed to String.find)
----@returns Iterator over the split components
+---@param s string String to split
+---@param sep string Separator or pattern
+---@param plain boolean If `true` use `sep` literally (passed to string.find)
+---@return function Iterator over the split components
function vim.gsplit(s, sep, plain)
- vim.validate{s={s,'s'},sep={sep,'s'},plain={plain,'b',true}}
+ vim.validate({ s = { s, 's' }, sep = { sep, 's' }, plain = { plain, 'b', true } })
local start = 1
local done = false
local function _pass(i, j, ...)
if i then
- assert(j+1 > start, "Infinite loop detected")
+ assert(j + 1 > start, 'Infinite loop detected')
local seg = s:sub(start, i - 1)
start = j + 1
return seg, ...
@@ -85,7 +89,7 @@ function vim.gsplit(s, sep, plain)
if start == #s then
done = true
end
- return _pass(start+1, start)
+ return _pass(start + 1, start)
end
return _pass(s:find(sep, start, plain))
end
@@ -103,13 +107,13 @@ end
---
---@see |vim.gsplit()|
---
----@param s String to split
----@param sep Separator string or pattern
----@param kwargs Keyword arguments:
+---@param s string String to split
+---@param sep string Separator or pattern
+---@param kwargs table Keyword arguments:
--- - plain: (boolean) If `true` use `sep` literally (passed to string.find)
--- - trimempty: (boolean) If `true` remove empty items from the front
--- and back of the list
----@returns List-like table of the split components.
+---@return table List of split components
function vim.split(s, sep, kwargs)
local plain
local trimempty = false
@@ -117,7 +121,7 @@ function vim.split(s, sep, kwargs)
-- Support old signature for backward compatibility
plain = kwargs
else
- vim.validate { kwargs = {kwargs, 't', true} }
+ vim.validate({ kwargs = { kwargs, 't', true } })
kwargs = kwargs or {}
plain = kwargs.plain
trimempty = kwargs.trimempty
@@ -126,7 +130,7 @@ function vim.split(s, sep, kwargs)
local t = {}
local skip = trimempty
for c in vim.gsplit(s, sep, plain) do
- if c ~= "" then
+ if c ~= '' then
skip = false
end
@@ -137,7 +141,7 @@ function vim.split(s, sep, kwargs)
if trimempty then
for i = #t, 1, -1 do
- if t[i] ~= "" then
+ if t[i] ~= '' then
break
end
table.remove(t, i)
@@ -152,10 +156,10 @@ end
---
---@see From https://github.com/premake/premake-core/blob/master/src/base/table.lua
---
----@param t Table
----@returns list of keys
+---@param t table Table
+---@return table List of keys
function vim.tbl_keys(t)
- assert(type(t) == 'table', string.format("Expected table, got %s", type(t)))
+ assert(type(t) == 'table', string.format('Expected table, got %s', type(t)))
local keys = {}
for k, _ in pairs(t) do
@@ -167,10 +171,10 @@ end
--- Return a list of all values used in a table.
--- However, the order of the return table of values is not guaranteed.
---
----@param t Table
----@returns list of values
+---@param t table Table
+---@return table List of values
function vim.tbl_values(t)
- assert(type(t) == 'table', string.format("Expected table, got %s", type(t)))
+ assert(type(t) == 'table', string.format('Expected table, got %s', type(t)))
local values = {}
for _, v in pairs(t) do
@@ -181,10 +185,11 @@ end
--- Apply a function to all values of a table.
---
----@param func function or callable table
----@param t table
+---@param func function|table Function or callable table
+---@param t table Table
+---@return table Table of transformed values
function vim.tbl_map(func, t)
- vim.validate{func={func,'c'},t={t,'t'}}
+ vim.validate({ func = { func, 'c' }, t = { t, 't' } })
local rettab = {}
for k, v in pairs(t) do
@@ -195,10 +200,11 @@ end
--- Filter a table using a predicate function
---
----@param func function or callable table
----@param t table
+---@param func function|table Function or callable table
+---@param t table Table
+---@return table Table of filtered values
function vim.tbl_filter(func, t)
- vim.validate{func={func,'c'},t={t,'t'}}
+ vim.validate({ func = { func, 'c' }, t = { t, 't' } })
local rettab = {}
for _, entry in pairs(t) do
@@ -211,13 +217,13 @@ end
--- Checks if a list-like (vector) table contains `value`.
---
----@param t Table to check
----@param value Value to compare
----@returns true if `t` contains `value`
+---@param t table Table to check
+---@param value any Value to compare
+---@return boolean `true` if `t` contains `value`
function vim.tbl_contains(t, value)
- vim.validate{t={t,'t'}}
+ vim.validate({ t = { t, 't' } })
- for _,v in ipairs(t) do
+ for _, v in ipairs(t) do
if v == value then
return true
end
@@ -229,25 +235,30 @@ end
---
---@see https://github.com/premake/premake-core/blob/master/src/base/table.lua
---
----@param t Table to check
+---@param t table Table to check
+---@return boolean `true` if `t` is empty
function vim.tbl_isempty(t)
- assert(type(t) == 'table', string.format("Expected table, got %s", type(t)))
+ assert(type(t) == 'table', string.format('Expected table, got %s', type(t)))
return next(t) == nil
end
---- we only merge empty tables or tables that are not a list
+--- We only merge empty tables or tables that are not a list
---@private
local function can_merge(v)
- return type(v) == "table" and (vim.tbl_isempty(v) or not vim.tbl_islist(v))
+ return type(v) == 'table' and (vim.tbl_isempty(v) or not vim.tbl_islist(v))
end
local function tbl_extend(behavior, deep_extend, ...)
- if (behavior ~= 'error' and behavior ~= 'keep' and behavior ~= 'force') then
- error('invalid "behavior": '..tostring(behavior))
+ if behavior ~= 'error' and behavior ~= 'keep' and behavior ~= 'force' then
+ error('invalid "behavior": ' .. tostring(behavior))
end
if select('#', ...) < 2 then
- error('wrong number of arguments (given '..tostring(1 + select('#', ...))..', expected at least 3)')
+ error(
+ 'wrong number of arguments (given '
+ .. tostring(1 + select('#', ...))
+ .. ', expected at least 3)'
+ )
end
local ret = {}
@@ -257,15 +268,15 @@ local function tbl_extend(behavior, deep_extend, ...)
for i = 1, select('#', ...) do
local tbl = select(i, ...)
- vim.validate{["after the second argument"] = {tbl,'t'}}
+ vim.validate({ ['after the second argument'] = { tbl, 't' } })
if tbl then
for k, v in pairs(tbl) do
if deep_extend and can_merge(v) and can_merge(ret[k]) then
ret[k] = tbl_extend(behavior, true, ret[k], v)
elseif behavior ~= 'force' and ret[k] ~= nil then
if behavior == 'error' then
- error('key found in more than one map: '..k)
- end -- Else behavior is "keep".
+ error('key found in more than one map: ' .. k)
+ end -- Else behavior is "keep".
else
ret[k] = v
end
@@ -279,11 +290,12 @@ end
---
---@see |extend()|
---
----@param behavior Decides what to do if a key is found in more than one map:
+---@param behavior string Decides what to do if a key is found in more than one map:
--- - "error": raise an error
--- - "keep": use value from the leftmost map
--- - "force": use value from the rightmost map
----@param ... Two or more map-like tables.
+---@param ... table Two or more map-like tables
+---@return table Merged table
function vim.tbl_extend(behavior, ...)
return tbl_extend(behavior, false, ...)
end
@@ -292,25 +304,30 @@ end
---
---@see |tbl_extend()|
---
----@param behavior Decides what to do if a key is found in more than one map:
+---@param behavior string Decides what to do if a key is found in more than one map:
--- - "error": raise an error
--- - "keep": use value from the leftmost map
--- - "force": use value from the rightmost map
----@param ... Two or more map-like tables.
+---@param ... table Two or more map-like tables
+---@return table Merged table
function vim.tbl_deep_extend(behavior, ...)
return tbl_extend(behavior, true, ...)
end
--- Deep compare values for equality
---
---- Tables are compared recursively unless they both provide the `eq` methamethod.
+--- Tables are compared recursively unless they both provide the `eq` metamethod.
--- All other types are compared using the equality `==` operator.
----@param a first value
----@param b second value
----@returns `true` if values are equals, else `false`.
+---@param a any First value
+---@param b any Second value
+---@return boolean `true` if values are equals, else `false`
function vim.deep_equal(a, b)
- if a == b then return true end
- if type(a) ~= type(b) then return false end
+ if a == b then
+ return true
+ end
+ if type(a) ~= type(b) then
+ return false
+ end
if type(a) == 'table' then
for k, v in pairs(a) do
if not vim.deep_equal(v, b[k]) then
@@ -329,40 +346,77 @@ end
--- Add the reverse lookup values to an existing table.
--- For example:
---- `tbl_add_reverse_lookup { A = 1 } == { [1] = 'A', A = 1 }`
---
---Do note that it *modifies* the input.
----@param o table The table to add the reverse to.
+--- ``tbl_add_reverse_lookup { A = 1 } == { [1] = 'A', A = 1 }``
+---
+--- Note that this *modifies* the input.
+---@param o table Table to add the reverse to
+---@return table o
function vim.tbl_add_reverse_lookup(o)
local keys = vim.tbl_keys(o)
for _, k in ipairs(keys) do
local v = o[k]
if o[v] then
- error(string.format("The reverse lookup found an existing value for %q while processing key %q", tostring(v), tostring(k)))
+ error(
+ string.format(
+ 'The reverse lookup found an existing value for %q while processing key %q',
+ tostring(v),
+ tostring(k)
+ )
+ )
end
o[v] = k
end
return o
end
+--- Index into a table (first argument) via string keys passed as subsequent arguments.
+--- Return `nil` if the key does not exist.
+---
+--- Examples:
+--- <pre>
+--- vim.tbl_get({ key = { nested_key = true }}, 'key', 'nested_key') == true
+--- vim.tbl_get({ key = {}}, 'key', 'nested_key') == nil
+--- </pre>
+---
+---@param o table Table to index
+---@param ... string Optional strings (0 or more, variadic) via which to index the table
+---
+---@return any Nested value indexed by key (if it exists), else nil
+function vim.tbl_get(o, ...)
+ local keys = { ... }
+ if #keys == 0 then
+ return
+ end
+ for i, k in ipairs(keys) do
+ if type(o[k]) ~= 'table' and next(keys, i) then
+ return nil
+ end
+ o = o[k]
+ if o == nil then
+ return
+ end
+ end
+ return o
+end
+
--- Extends a list-like table with the values of another list-like table.
---
--- NOTE: This mutates dst!
---
---@see |vim.tbl_extend()|
---
----@param dst list which will be modified and appended to.
----@param src list from which values will be inserted.
----@param start Start index on src. defaults to 1
----@param finish Final index on src. defaults to #src
----@returns dst
+---@param dst table List which will be modified and appended to
+---@param src table List from which values will be inserted
+---@param start number Start index on src. Defaults to 1
+---@param finish number Final index on src. Defaults to `#src`
+---@return table dst
function vim.list_extend(dst, src, start, finish)
- vim.validate {
- dst = {dst, 't'};
- src = {src, 't'};
- start = {start, 'n', true};
- finish = {finish, 'n', true};
- }
+ vim.validate({
+ dst = { dst, 't' },
+ src = { src, 't' },
+ start = { start, 'n', true },
+ finish = { finish, 'n', true },
+ })
for i = start or 1, finish or #src do
table.insert(dst, src[i])
end
@@ -374,15 +428,15 @@ end
---
---@see From https://github.com/premake/premake-core/blob/master/src/base/table.lua
---
----@param t List-like table
----@returns Flattened copy of the given list-like table.
+---@param t table List-like table
+---@return table Flattened copy of the given list-like table
function vim.tbl_flatten(t)
local result = {}
local function _tbl_flatten(_t)
local n = #_t
for i = 1, n do
local v = _t[i]
- if type(v) == "table" then
+ if type(v) == 'table' then
_tbl_flatten(v)
elseif v then
table.insert(result, v)
@@ -399,8 +453,8 @@ end
--- |vim.empty_dict()| or returned as a dict-like |API| or Vimscript result,
--- for example from |rpcrequest()| or |vim.fn|.
---
----@param t Table
----@returns `true` if array-like table, else `false`.
+---@param t table Table
+---@return boolean `true` if array-like table, else `false`
function vim.tbl_islist(t)
if type(t) ~= 'table' then
return false
@@ -409,7 +463,7 @@ function vim.tbl_islist(t)
local count = 0
for k, _ in pairs(t) do
- if type(k) == "number" then
+ if type(k) == 'number' then
count = count + 1
else
return false
@@ -436,26 +490,28 @@ end
--- </pre>
---
---@see https://github.com/Tieske/Penlight/blob/master/lua/pl/tablex.lua
----@param t Table
----@returns Number that is the number of the value in table
+---@param t table Table
+---@return number Number of non-nil values in table
function vim.tbl_count(t)
- vim.validate{t={t,'t'}}
+ vim.validate({ t = { t, 't' } })
local count = 0
- for _ in pairs(t) do count = count + 1 end
+ for _ in pairs(t) do
+ count = count + 1
+ end
return count
end
--- Creates a copy of a table containing only elements from start to end (inclusive)
---
----@param list table table
----@param start integer Start range of slice
----@param finish integer End range of slice
----@returns Copy of table sliced from start to finish (inclusive)
+---@param list table Table
+---@param start number Start range of slice
+---@param finish number End range of slice
+---@return table Copy of table sliced from start to finish (inclusive)
function vim.list_slice(list, start, finish)
local new_list = {}
for i = start or 1, finish or #list do
- new_list[#new_list+1] = list[i]
+ new_list[#new_list + 1] = list[i]
end
return new_list
end
@@ -463,40 +519,40 @@ end
--- Trim whitespace (Lua pattern "%s") from both sides of a string.
---
---@see https://www.lua.org/pil/20.2.html
----@param s String to trim
----@returns String with whitespace removed from its beginning and end
+---@param s string String to trim
+---@return string String with whitespace removed from its beginning and end
function vim.trim(s)
- vim.validate{s={s,'s'}}
+ vim.validate({ s = { s, 's' } })
return s:match('^%s*(.*%S)') or ''
end
--- Escapes magic chars in a Lua pattern.
---
---@see https://github.com/rxi/lume
----@param s String to escape
----@returns %-escaped pattern string
+---@param s string String to escape
+---@return string %-escaped pattern string
function vim.pesc(s)
- vim.validate{s={s,'s'}}
+ vim.validate({ s = { s, 's' } })
return s:gsub('[%(%)%.%%%+%-%*%?%[%]%^%$]', '%%%1')
end
--- Tests if `s` starts with `prefix`.
---
----@param s (string) a string
----@param prefix (string) a prefix
----@return (boolean) true if `prefix` is a prefix of s
+---@param s string String
+---@param prefix string Prefix to match
+---@return boolean `true` if `prefix` is a prefix of `s`
function vim.startswith(s, prefix)
- vim.validate { s = {s, 's'}; prefix = {prefix, 's'}; }
+ vim.validate({ s = { s, 's' }, prefix = { prefix, 's' } })
return s:sub(1, #prefix) == prefix
end
--- Tests if `s` ends with `suffix`.
---
----@param s (string) a string
----@param suffix (string) a suffix
----@return (boolean) true if `suffix` is a suffix of s
+---@param s string String
+---@param suffix string Suffix to match
+---@return boolean `true` if `suffix` is a suffix of `s`
function vim.endswith(s, suffix)
- vim.validate { s = {s, 's'}; suffix = {suffix, 's'}; }
+ vim.validate({ s = { s, 's' }, suffix = { suffix, 's' } })
return #suffix == 0 or s:sub(-#suffix) == suffix
end
@@ -536,7 +592,7 @@ end
---
--- </pre>
---
----@param opt table of parameter names to validations. Each key is a parameter
+---@param opt table Names of parameters to validate. Each key is a parameter
--- name; each value is a tuple in one of these forms:
--- 1. (arg_value, type_name, optional)
--- - arg_value: argument value
@@ -550,18 +606,24 @@ end
--- only if the argument is valid. Can optionally return an additional
--- informative error message as the second returned value.
--- - msg: (optional) error string if validation fails
-function vim.validate(opt) end -- luacheck: no unused
+function vim.validate(opt) end -- luacheck: no unused
do
local type_names = {
- ['table'] = 'table', t = 'table',
- ['string'] = 'string', s = 'string',
- ['number'] = 'number', n = 'number',
- ['boolean'] = 'boolean', b = 'boolean',
- ['function'] = 'function', f = 'function',
- ['callable'] = 'callable', c = 'callable',
- ['nil'] = 'nil',
- ['thread'] = 'thread',
+ ['table'] = 'table',
+ t = 'table',
+ ['string'] = 'string',
+ s = 'string',
+ ['number'] = 'number',
+ n = 'number',
+ ['boolean'] = 'boolean',
+ b = 'boolean',
+ ['function'] = 'function',
+ f = 'function',
+ ['callable'] = 'callable',
+ c = 'callable',
+ ['nil'] = 'nil',
+ ['thread'] = 'thread',
['userdata'] = 'userdata',
}
@@ -580,21 +642,22 @@ do
return false, string.format('opt[%s]: expected table, got %s', param_name, type(spec))
end
- local val = spec[1] -- Argument value.
- local types = spec[2] -- Type name, or callable.
+ local val = spec[1] -- Argument value
+ local types = spec[2] -- Type name, or callable
local optional = (true == spec[3])
if type(types) == 'string' then
- types = {types}
+ types = { types }
end
if vim.is_callable(types) then
- -- Check user-provided validation function.
+ -- Check user-provided validation function
local valid, optional_message = types(val)
if not valid then
- local error_message = string.format("%s: expected %s, got %s", param_name, (spec[3] or '?'), tostring(val))
+ local error_message =
+ string.format('%s: expected %s, got %s', param_name, (spec[3] or '?'), tostring(val))
if optional_message ~= nil then
- error_message = error_message .. string.format(". Info: %s", optional_message)
+ error_message = error_message .. string.format('. Info: %s', optional_message)
end
return false, error_message
@@ -614,10 +677,16 @@ do
end
end
if not success then
- return false, string.format("%s: expected %s, got %s", param_name, table.concat(types, '|'), type(val))
+ return false,
+ string.format(
+ '%s: expected %s, got %s',
+ param_name,
+ table.concat(types, '|'),
+ type(val)
+ )
end
else
- return false, string.format("invalid type name: %s", tostring(types))
+ return false, string.format('invalid type name: %s', tostring(types))
end
end
@@ -633,12 +702,16 @@ do
end
--- Returns true if object `f` can be called as a function.
---
----@param f Any object
----@return true if `f` is callable, else false
+---@param f any Any object
+---@return boolean `true` if `f` is callable, else `false`
function vim.is_callable(f)
- if type(f) == 'function' then return true end
+ if type(f) == 'function' then
+ return true
+ end
local m = getmetatable(f)
- if m == nil then return false end
+ if m == nil then
+ return false
+ end
return type(m.__call) == 'function'
end
diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua
index 07f6418c0c..70f2c425ed 100644
--- a/runtime/lua/vim/treesitter.lua
+++ b/runtime/lua/vim/treesitter.lua
@@ -1,31 +1,32 @@
local a = vim.api
-local query = require'vim.treesitter.query'
-local language = require'vim.treesitter.language'
-local LanguageTree = require'vim.treesitter.languagetree'
+local query = require('vim.treesitter.query')
+local language = require('vim.treesitter.language')
+local LanguageTree = require('vim.treesitter.languagetree')
-- TODO(bfredl): currently we retain parsers for the lifetime of the buffer.
-- Consider use weak references to release parser if all plugins are done with
-- it.
local parsers = {}
-local M = vim.tbl_extend("error", query, language)
+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)
- if k == "highlighter" then
- t[k] = require'vim.treesitter.highlighter'
- return t[k]
- elseif k == "language" then
- t[k] = require"vim.treesitter.language"
- return t[k]
- elseif k == "query" then
- t[k] = require"vim.treesitter.query"
- return t[k]
- end
- end
- })
+ __index = function(t, k)
+ if k == 'highlighter' then
+ t[k] = require('vim.treesitter.highlighter')
+ return t[k]
+ elseif k == 'language' then
+ t[k] = require('vim.treesitter.language')
+ return t[k]
+ elseif k == 'query' then
+ t[k] = require('vim.treesitter.query')
+ return t[k]
+ end
+ end,
+})
--- Creates a new parser.
---
@@ -62,7 +63,11 @@ function M._create_parser(bufnr, lang, opts)
self:_on_reload(...)
end
- a.nvim_buf_attach(self:source(), false, {on_bytes=bytes_cb, on_detach=detach_cb, on_reload=reload_cb, preview=true})
+ a.nvim_buf_attach(
+ self:source(),
+ false,
+ { on_bytes = bytes_cb, on_detach = detach_cb, on_reload = reload_cb, preview = true }
+ )
self:parse()
@@ -86,10 +91,10 @@ function M.get_parser(bufnr, lang, opts)
bufnr = a.nvim_get_current_buf()
end
if lang == nil then
- lang = a.nvim_buf_get_option(bufnr, "filetype")
+ lang = a.nvim_buf_get_option(bufnr, 'filetype')
end
- if parsers[bufnr] == nil then
+ if parsers[bufnr] == nil or parsers[bufnr]:lang() ~= lang then
parsers[bufnr] = M._create_parser(bufnr, lang, opts)
end
@@ -104,10 +109,10 @@ end
---@param lang The language of this string
---@param opts Options to pass to the created language tree
function M.get_string_parser(str, lang, opts)
- vim.validate {
+ vim.validate({
str = { str, 'string' },
- lang = { lang, 'string' }
- }
+ lang = { lang, 'string' },
+ })
language.require_language(lang)
return LanguageTree.new(str, lang, opts)
diff --git a/runtime/lua/vim/treesitter/health.lua b/runtime/lua/vim/treesitter/health.lua
index 53ccc6e88d..3bd59ca282 100644
--- a/runtime/lua/vim/treesitter/health.lua
+++ b/runtime/lua/vim/treesitter/health.lua
@@ -15,24 +15,24 @@ function M.check()
local report_error = vim.fn['health#report_error']
local parsers = M.list_parsers()
- report_info(string.format("Runtime ABI version : %d", ts.language_version))
+ report_info(string.format('Runtime ABI version : %d', ts.language_version))
for _, parser in pairs(parsers) do
- local parsername = vim.fn.fnamemodify(parser, ":t:r")
+ local parsername = vim.fn.fnamemodify(parser, ':t:r')
local is_loadable, ret = pcall(ts.language.require_language, parsername)
if not is_loadable then
- report_error(string.format("Impossible to load parser for %s: %s", parsername, ret))
+ report_error(string.format('Impossible to load parser for %s: %s', parsername, ret))
elseif ret then
local lang = ts.language.inspect_language(parsername)
- report_ok(string.format("Loaded parser for %s: ABI version %d",
- parsername, lang._abi_version))
+ report_ok(
+ string.format('Loaded parser for %s: ABI version %d', parsername, lang._abi_version)
+ )
else
- report_error(string.format("Unable to load parser for %s", parsername))
+ report_error(string.format('Unable to load parser for %s', parsername))
end
end
end
return M
-
diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua
index 22b528838c..e27a5fa9c3 100644
--- a/runtime/lua/vim/treesitter/highlighter.lua
+++ b/runtime/lua/vim/treesitter/highlighter.lua
@@ -1,5 +1,5 @@
local a = vim.api
-local query = require"vim.treesitter.query"
+local query = require('vim.treesitter.query')
-- support reload for quick experimentation
local TSHighlighter = rawget(vim.treesitter, 'TSHighlighter') or {}
@@ -10,64 +10,88 @@ TSHighlighter.active = TSHighlighter.active or {}
local TSHighlighterQuery = {}
TSHighlighterQuery.__index = TSHighlighterQuery
-local ns = a.nvim_create_namespace("treesitter/highlighter")
+local ns = a.nvim_create_namespace('treesitter/highlighter')
local _default_highlights = {}
local _link_default_highlight_once = function(from, to)
if not _default_highlights[from] then
_default_highlights[from] = true
- vim.cmd(string.format("highlight default link %s %s", from, to))
+ a.nvim_set_hl(0, from, { link = to, default = true })
end
return from
end
-TSHighlighter.hl_map = {
- ["error"] = "Error",
-
--- Miscs
- ["comment"] = "Comment",
- ["punctuation.delimiter"] = "Delimiter",
- ["punctuation.bracket"] = "Delimiter",
- ["punctuation.special"] = "Delimiter",
-
--- Constants
- ["constant"] = "Constant",
- ["constant.builtin"] = "Special",
- ["constant.macro"] = "Define",
- ["string"] = "String",
- ["string.regex"] = "String",
- ["string.escape"] = "SpecialChar",
- ["character"] = "Character",
- ["number"] = "Number",
- ["boolean"] = "Boolean",
- ["float"] = "Float",
-
--- Functions
- ["function"] = "Function",
- ["function.special"] = "Function",
- ["function.builtin"] = "Special",
- ["function.macro"] = "Macro",
- ["parameter"] = "Identifier",
- ["method"] = "Function",
- ["field"] = "Identifier",
- ["property"] = "Identifier",
- ["constructor"] = "Special",
-
--- Keywords
- ["conditional"] = "Conditional",
- ["repeat"] = "Repeat",
- ["label"] = "Label",
- ["operator"] = "Operator",
- ["keyword"] = "Keyword",
- ["exception"] = "Exception",
-
- ["type"] = "Type",
- ["type.builtin"] = "Type",
- ["structure"] = "Structure",
- ["include"] = "Include",
+-- If @definition.special does not exist use @definition instead
+local subcapture_fallback = {
+ __index = function(self, capture)
+ local rtn
+ local shortened = capture
+ while not rtn and shortened do
+ shortened = shortened:match('(.*)%.')
+ rtn = shortened and rawget(self, shortened)
+ end
+ rawset(self, capture, rtn or '__notfound')
+ return rtn
+ end,
}
+TSHighlighter.hl_map = setmetatable({
+ ['error'] = 'Error',
+ ['text.underline'] = 'Underlined',
+ ['todo'] = 'Todo',
+ ['debug'] = 'Debug',
+
+ -- Miscs
+ ['comment'] = 'Comment',
+ ['punctuation.delimiter'] = 'Delimiter',
+ ['punctuation.bracket'] = 'Delimiter',
+ ['punctuation.special'] = 'Delimiter',
+
+ -- Constants
+ ['constant'] = 'Constant',
+ ['constant.builtin'] = 'Special',
+ ['constant.macro'] = 'Define',
+ ['define'] = 'Define',
+ ['macro'] = 'Macro',
+ ['string'] = 'String',
+ ['string.regex'] = 'String',
+ ['string.escape'] = 'SpecialChar',
+ ['character'] = 'Character',
+ ['character.special'] = 'SpecialChar',
+ ['number'] = 'Number',
+ ['boolean'] = 'Boolean',
+ ['float'] = 'Float',
+
+ -- Functions
+ ['function'] = 'Function',
+ ['function.special'] = 'Function',
+ ['function.builtin'] = 'Special',
+ ['function.macro'] = 'Macro',
+ ['parameter'] = 'Identifier',
+ ['method'] = 'Function',
+ ['field'] = 'Identifier',
+ ['property'] = 'Identifier',
+ ['constructor'] = 'Special',
+
+ -- Keywords
+ ['conditional'] = 'Conditional',
+ ['repeat'] = 'Repeat',
+ ['label'] = 'Label',
+ ['operator'] = 'Operator',
+ ['keyword'] = 'Keyword',
+ ['exception'] = 'Exception',
+
+ ['type'] = 'Type',
+ ['type.builtin'] = 'Type',
+ ['type.qualifier'] = 'Type',
+ ['type.definition'] = 'Typedef',
+ ['storageclass'] = 'StorageClass',
+ ['structure'] = 'Structure',
+ ['include'] = 'Include',
+ ['preproc'] = 'PreProc',
+}, subcapture_fallback)
+
---@private
local function is_highlight_name(capture_name)
local firstc = string.sub(capture_name, 1, 1)
@@ -89,13 +113,13 @@ function TSHighlighterQuery.new(lang, query_string)
rawset(table, capture, id)
return id
- end
+ end,
})
if query_string then
self._query = query.parse_query(lang, query_string)
else
- self._query = query.get_query(lang, "highlights")
+ self._query = query.get_query(lang, 'highlights')
end
return self
@@ -128,17 +152,23 @@ end
function TSHighlighter.new(tree, opts)
local self = setmetatable({}, TSHighlighter)
- if type(tree:source()) ~= "number" then
- error("TSHighlighter can not be used with a string parser source.")
+ if type(tree:source()) ~= 'number' then
+ error('TSHighlighter can not be used with a string parser source.')
end
opts = opts or {}
self.tree = tree
- tree:register_cbs {
- on_changedtree = function(...) self:on_changedtree(...) end;
- on_bytes = function(...) self:on_bytes(...) end;
- on_detach = function(...) self:on_detach(...) end;
- }
+ tree:register_cbs({
+ on_changedtree = function(...)
+ self:on_changedtree(...)
+ end,
+ on_bytes = function(...)
+ self:on_bytes(...)
+ end,
+ on_detach = function(...)
+ self:on_detach(...)
+ end,
+ })
self.bufnr = tree:source()
self.edit_count = 0
@@ -157,7 +187,7 @@ function TSHighlighter.new(tree, opts)
end
end
- a.nvim_buf_set_option(self.bufnr, "syntax", "")
+ a.nvim_buf_set_option(self.bufnr, 'syntax', '')
TSHighlighter.active[self.bufnr] = self
@@ -166,7 +196,7 @@ function TSHighlighter.new(tree, opts)
-- syntax FileType autocmds. Later on we should integrate with the
-- `:syntax` and `set syntax=...` machinery properly.
if vim.g.syntax_on ~= 1 then
- vim.api.nvim_command("runtime! syntax/synload.vim")
+ vim.api.nvim_command('runtime! syntax/synload.vim')
end
self.tree:parse()
@@ -186,7 +216,7 @@ function TSHighlighter:get_highlight_state(tstree)
if not self._highlight_states[tstree] then
self._highlight_states[tstree] = {
next_row = 0,
- iter = nil
+ iter = nil,
}
end
@@ -211,7 +241,7 @@ end
---@private
function TSHighlighter:on_changedtree(changes)
for _, ch in ipairs(changes or {}) do
- a.nvim__buf_redraw_range(self.bufnr, ch[1], ch[3]+1)
+ a.nvim__buf_redraw_range(self.bufnr, ch[1], ch[3] + 1)
end
end
@@ -229,39 +259,50 @@ end
---@private
local function on_line_impl(self, buf, line)
self.tree:for_each_tree(function(tstree, tree)
- if not tstree then return end
+ if not tstree then
+ return
+ end
local root_node = tstree:root()
local root_start_row, _, root_end_row, _ = root_node:range()
-- Only worry about trees within the line range
- if root_start_row > line or root_end_row < line then return end
+ if root_start_row > line or root_end_row < line then
+ return
+ end
local state = self:get_highlight_state(tstree)
local highlighter_query = self:get_query(tree:lang())
-- Some injected languages may not have highlight queries.
- if not highlighter_query:query() then return end
+ if not highlighter_query:query() then
+ return
+ end
- if state.iter == nil then
- state.iter = highlighter_query:query():iter_captures(root_node, self.bufnr, line, root_end_row + 1)
+ if state.iter == nil or state.next_row < line then
+ state.iter =
+ highlighter_query:query():iter_captures(root_node, self.bufnr, line, root_end_row + 1)
end
while line >= state.next_row do
local capture, node, metadata = state.iter()
- if capture == nil then break end
+ if capture == nil then
+ break
+ end
local start_row, start_col, end_row, end_col = node:range()
local hl = highlighter_query.hl_cache[capture]
if hl and end_row >= line then
- a.nvim_buf_set_extmark(buf, ns, start_row, start_col,
- { end_line = end_row, end_col = end_col,
- hl_group = hl,
- ephemeral = true,
- priority = tonumber(metadata.priority) or 100 -- Low but leaves room below
- })
+ a.nvim_buf_set_extmark(buf, ns, start_row, start_col, {
+ end_line = end_row,
+ end_col = end_col,
+ hl_group = hl,
+ ephemeral = true,
+ priority = tonumber(metadata.priority) or 100, -- Low but leaves room below
+ conceal = metadata.conceal,
+ })
end
if start_row > line then
state.next_row = start_row
@@ -273,7 +314,9 @@ end
---@private
function TSHighlighter._on_line(_, _win, buf, line, _)
local self = TSHighlighter.active[buf]
- if not self then return end
+ if not self then
+ return
+ end
on_line_impl(self, buf, line)
end
@@ -299,9 +342,9 @@ function TSHighlighter._on_win(_, _win, buf, _topline)
end
a.nvim_set_decoration_provider(ns, {
- on_buf = TSHighlighter._on_buf;
- on_win = TSHighlighter._on_win;
- on_line = TSHighlighter._on_line;
+ on_buf = TSHighlighter._on_buf,
+ on_win = TSHighlighter._on_win,
+ on_line = TSHighlighter._on_line,
})
return TSHighlighter
diff --git a/runtime/lua/vim/treesitter/language.lua b/runtime/lua/vim/treesitter/language.lua
index 6f347ff25f..dfb6f5be84 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
@@ -22,13 +22,15 @@ function M.require_language(lang, path, silent)
end
-- TODO(bfredl): help tag?
- error("no parser for '"..lang.."' language, see :help treesitter-parsers")
+ error("no parser for '" .. lang .. "' language, see :help treesitter-parsers")
end
path = paths[1]
end
if silent then
- return pcall(function() vim._ts_add_language(path, lang) end)
+ return pcall(function()
+ vim._ts_add_language(path, lang)
+ end)
else
vim._ts_add_language(path, lang)
end
diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua
index 85fd5cd8e0..4d3b0631a2 100644
--- a/runtime/lua/vim/treesitter/languagetree.lua
+++ b/runtime/lua/vim/treesitter/languagetree.lua
@@ -1,6 +1,6 @@
local a = vim.api
-local query = require'vim.treesitter.query'
-local language = require'vim.treesitter.language'
+local query = require('vim.treesitter.query')
+local language = require('vim.treesitter.language')
local LanguageTree = {}
LanguageTree.__index = LanguageTree
@@ -32,9 +32,8 @@ function LanguageTree.new(source, lang, opts)
_regions = {},
_trees = {},
_opts = opts,
- _injection_query = injections[lang]
- and query.parse_query(lang, injections[lang])
- or query.get_query(lang, "injections"),
+ _injection_query = injections[lang] and query.parse_query(lang, injections[lang])
+ or query.get_query(lang, 'injections'),
_valid = false,
_parser = vim._create_ts_parser(lang),
_callbacks = {
@@ -42,11 +41,10 @@ function LanguageTree.new(source, lang, opts)
bytes = {},
detach = {},
child_added = {},
- child_removed = {}
+ child_removed = {},
},
}, LanguageTree)
-
return self
end
@@ -76,8 +74,8 @@ function LanguageTree:lang()
end
--- Determines whether this tree is valid.
---- If the tree is invalid, `parse()` must be called
---- to get the updated tree.
+--- If the tree is invalid, call `parse()`.
+--- This will return the updated tree.
function LanguageTree:is_valid()
return self._valid
end
@@ -234,7 +232,9 @@ end
--- Destroys this language tree and all its children.
---
--- Any cleanup logic should be performed here.
---- Note, this DOES NOT remove this tree from a parent.
+---
+--- Note:
+--- This DOES NOT remove this tree from a parent. Instead,
--- `remove_child` must be called on the parent to remove it.
function LanguageTree:destroy()
-- Cleanup here
@@ -259,22 +259,27 @@ end
---
--- Note, this call invalidates the tree and requires it to be parsed again.
---
----@param regions A list of regions this tree should manage and parse.
+---@param regions (table) list of regions this tree should manage and parse.
function LanguageTree:set_included_regions(regions)
- -- TODO(vigoux): I don't think string parsers are useful for now
- if type(self._source) == "number" then
- -- Transform the tables from 4 element long to 6 element long (with byte offset)
- for _, region in ipairs(regions) do
- for i, range in ipairs(region) do
- if type(range) == "table" and #range == 4 then
- local start_row, start_col, end_row, end_col = unpack(range)
+ -- Transform the tables from 4 element long to 6 element long (with byte offset)
+ for _, region in ipairs(regions) do
+ for i, range in ipairs(region) do
+ if type(range) == 'table' and #range == 4 then
+ local start_row, start_col, end_row, end_col = unpack(range)
+ local start_byte = 0
+ local end_byte = 0
+ -- TODO(vigoux): proper byte computation here, and account for EOL ?
+ if type(self._source) == 'number' then
-- Easy case, this is a buffer parser
- -- TODO(vigoux): proper byte computation here, and account for EOL ?
- local start_byte = a.nvim_buf_get_offset(self._source, start_row) + start_col
- local end_byte = a.nvim_buf_get_offset(self._source, end_row) + end_col
-
- region[i] = { start_row, start_col, start_byte, end_row, end_col, end_byte }
+ start_byte = a.nvim_buf_get_offset(self._source, start_row) + start_col
+ end_byte = a.nvim_buf_get_offset(self._source, end_row) + end_col
+ elseif type(self._source) == 'string' then
+ -- string parser, single `\n` delimited string
+ start_byte = vim.fn.byteidx(self._source, start_col)
+ end_byte = vim.fn.byteidx(self._source, end_col)
end
+
+ region[i] = { start_row, start_col, start_byte, end_row, end_col, end_byte }
end
end
end
@@ -293,6 +298,14 @@ function LanguageTree:included_regions()
return self._regions
end
+---@private
+local function get_node_range(node, id, metadata)
+ if metadata[id] and metadata[id].range then
+ return metadata[id].range
+ end
+ return { node:range() }
+end
+
--- Gets language injection points by language.
---
--- This is where most of the injection processing occurs.
@@ -301,7 +314,9 @@ end
--- instead of using the entire nodes range.
---@private
function LanguageTree:_get_injections()
- if not self._injection_query then return {} end
+ if not self._injection_query then
+ return {}
+ end
local injections = {}
@@ -309,7 +324,9 @@ function LanguageTree:_get_injections()
local root_node = tree:root()
local start_line, _, end_line, _ = root_node:range()
- for pattern, match, metadata in self._injection_query:iter_matches(root_node, self._source, start_line, end_line+1) do
+ for pattern, match, metadata in
+ self._injection_query:iter_matches(root_node, self._source, start_line, end_line + 1)
+ do
local lang = nil
local ranges = {}
local combined = metadata.combined
@@ -320,11 +337,11 @@ function LanguageTree:_get_injections()
local content = metadata.content
-- Allow for captured nodes to be used
- if type(content) == "number" then
- content = {match[content]}
+ if type(content) == 'number' then
+ content = { match[content]:range() }
end
- if content then
+ if type(content) == 'table' and #content >= 4 then
vim.list_extend(ranges, content)
end
end
@@ -340,21 +357,21 @@ function LanguageTree:_get_injections()
local name = self._injection_query.captures[id]
-- Lang should override any other language tag
- if name == "language" and not lang then
+ if name == 'language' and not lang then
lang = query.get_node_text(node, self._source)
- elseif name == "combined" then
+ elseif name == 'combined' then
combined = true
- elseif name == "content" and #ranges == 0 then
- table.insert(ranges, node)
- -- Ignore any tags that start with "_"
- -- Allows for other tags to be used in matches
- elseif string.sub(name, 1, 1) ~= "_" then
+ elseif name == 'content' and #ranges == 0 then
+ table.insert(ranges, get_node_range(node, id, metadata))
+ -- Ignore any tags that start with "_"
+ -- Allows for other tags to be used in matches
+ elseif string.sub(name, 1, 1) ~= '_' then
if not lang then
lang = name
end
if #ranges == 0 then
- table.insert(ranges, node)
+ table.insert(ranges, get_node_range(node, id, metadata))
end
end
end
@@ -391,7 +408,10 @@ function LanguageTree:_get_injections()
for _, entry in pairs(patterns) do
if entry.combined then
- table.insert(result[lang], vim.tbl_flatten(entry.regions))
+ local regions = vim.tbl_map(function(e)
+ return vim.tbl_flatten(e)
+ end, entry.regions)
+ table.insert(result[lang], regions)
else
for _, ranges in ipairs(entry.regions) do
table.insert(result[lang], ranges)
@@ -412,10 +432,19 @@ function LanguageTree:_do_callback(cb_name, ...)
end
---@private
-function LanguageTree:_on_bytes(bufnr, changed_tick,
- start_row, start_col, start_byte,
- old_row, old_col, old_byte,
- new_row, new_col, new_byte)
+function LanguageTree:_on_bytes(
+ bufnr,
+ changed_tick,
+ start_row,
+ start_col,
+ start_byte,
+ old_row,
+ old_col,
+ old_byte,
+ new_row,
+ new_col,
+ new_byte
+)
self:invalidate()
local old_end_col = old_col + ((old_row == 0) and start_col or 0)
@@ -424,16 +453,33 @@ function LanguageTree:_on_bytes(bufnr, changed_tick,
-- Edit all trees recursively, together BEFORE emitting a bytes callback.
-- In most cases this callback should only be called from the root tree.
self:for_each_tree(function(tree)
- tree:edit(start_byte,start_byte+old_byte,start_byte+new_byte,
- start_row, start_col,
- start_row+old_row, old_end_col,
- start_row+new_row, new_end_col)
+ tree:edit(
+ start_byte,
+ start_byte + old_byte,
+ start_byte + new_byte,
+ start_row,
+ start_col,
+ start_row + old_row,
+ old_end_col,
+ start_row + new_row,
+ new_end_col
+ )
end)
- self:_do_callback('bytes', bufnr, changed_tick,
- start_row, start_col, start_byte,
- old_row, old_col, old_byte,
- new_row, new_col, new_byte)
+ self:_do_callback(
+ 'bytes',
+ bufnr,
+ changed_tick,
+ start_row,
+ start_col,
+ start_byte,
+ old_row,
+ old_col,
+ old_byte,
+ new_row,
+ new_col,
+ new_byte
+ )
end
---@private
@@ -441,23 +487,24 @@ function LanguageTree:_on_reload()
self:invalidate(true)
end
-
---@private
function LanguageTree:_on_detach(...)
self:invalidate(true)
self:_do_callback('detach', ...)
end
---- Registers callbacks for the parser
----@param cbs An `nvim_buf_attach`-like table argument with the following keys :
---- `on_bytes` : see `nvim_buf_attach`, but this will be called _after_ the parsers callback.
---- `on_changedtree` : a callback that will be called every time the tree has syntactical changes.
---- it will only be passed one argument, that is a table of the ranges (as node ranges) that
---- changed.
---- `on_child_added` : emitted when a child is added to the tree.
---- `on_child_removed` : emitted when a child is removed from the tree.
+--- Registers callbacks for the parser.
+---@param cbs table An |nvim_buf_attach()|-like table argument with the following keys :
+--- - `on_bytes` : see |nvim_buf_attach()|, but this will be called _after_ the parsers callback.
+--- - `on_changedtree` : a callback that will be called every time the tree has syntactical changes.
+--- It will only be passed one argument, which is a table of the ranges (as node ranges) that
+--- changed.
+--- - `on_child_added` : emitted when a child is added to the tree.
+--- - `on_child_removed` : emitted when a child is removed from the tree.
function LanguageTree:register_cbs(cbs)
- if not cbs then return end
+ if not cbs then
+ return
+ end
if cbs.on_changedtree then
table.insert(self._callbacks.changedtree, cbs.on_changedtree)
@@ -486,16 +533,10 @@ local function tree_contains(tree, range)
local start_fits = start_row < range[1] or (start_row == range[1] and start_col <= range[2])
local end_fits = end_row > range[3] or (end_row == range[3] and end_col >= range[4])
- if start_fits and end_fits then
- return true
- end
-
- return false
+ return start_fits and end_fits
end
---- Determines whether @param range is contained in this language tree
----
---- This goes down the tree to recursively check children.
+--- Determines whether {range} is contained in this language tree
---
---@param range A range, that is a `{ start_line, start_col, end_line, end_col }` table.
function LanguageTree:contains(range)
@@ -508,7 +549,7 @@ function LanguageTree:contains(range)
return false
end
---- Gets the appropriate language that contains @param range
+--- Gets the appropriate language that contains {range}
---
---@param range A text range, see |LanguageTree:contains|
function LanguageTree:language_for_range(range)
diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua
index ebed502c92..103e85abfd 100644
--- a/runtime/lua/vim/treesitter/query.lua
+++ b/runtime/lua/vim/treesitter/query.lua
@@ -1,5 +1,5 @@
local a = vim.api
-local language = require'vim.treesitter.language'
+local language = require('vim.treesitter.language')
-- query: pattern matching on trees
-- predicate matching is implemented in lua
@@ -43,7 +43,9 @@ function M.get_query_files(lang, query_name, is_included)
local query_path = string.format('queries/%s/%s.scm', lang, query_name)
local lang_files = dedupe_files(a.nvim_get_runtime_file(query_path, true))
- if #lang_files == 0 then return {} end
+ if #lang_files == 0 then
+ return {}
+ end
local base_langs = {}
@@ -52,7 +54,7 @@ function M.get_query_files(lang, query_name, is_included)
-- ;+ inherits: ({language},)*{language}
--
-- {language} ::= {lang} | ({lang})
- local MODELINE_FORMAT = "^;+%s*inherits%s*:?%s*([a-z_,()]+)%s*$"
+ local MODELINE_FORMAT = '^;+%s*inherits%s*:?%s*([a-z_,()]+)%s*$'
for _, file in ipairs(lang_files) do
local modeline = safe_read(file, '*l')
@@ -62,7 +64,7 @@ function M.get_query_files(lang, query_name, is_included)
if langlist then
for _, incllang in ipairs(vim.split(langlist, ',', true)) do
- local is_optional = incllang:match("%(.*%)")
+ local is_optional = incllang:match('%(.*%)')
if is_optional then
if not is_included then
@@ -90,7 +92,7 @@ end
local function read_query_files(filenames)
local contents = {}
- for _,filename in ipairs(filenames) do
+ for _, filename in ipairs(filenames) do
table.insert(contents, safe_read(filename, '*a'))
end
@@ -138,30 +140,43 @@ 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).
+--- should read the contents into a string before calling).
---
--- Returns a `Query` (see |lua-treesitter-query|) object which can be used to
--- search nodes in the syntax tree for the patterns defined in {query}
--- using `iter_*` methods below.
---
---- Exposes `info` and `captures` with additional information about the {query}.
+--- Exposes `info` and `captures` with additional context about {query}.
--- - `captures` contains the list of unique capture names defined in
--- {query}.
--- -` 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
@@ -172,7 +187,7 @@ function M.get_node_text(node, source)
local start_row, start_col, start_byte = node:start()
local end_row, end_col, end_byte = node:end_()
- if type(source) == "number" then
+ if type(source) == 'number' then
local lines
local eof_row = a.nvim_buf_line_count(source)
if start_row >= eof_row then
@@ -186,56 +201,64 @@ function M.get_node_text(node, source)
lines = a.nvim_buf_get_lines(source, start_row, end_row + 1, true)
end
- if #lines == 1 then
- lines[1] = string.sub(lines[1], start_col+1, end_col)
- else
- lines[1] = string.sub(lines[1], start_col+1)
- lines[#lines] = string.sub(lines[#lines], 1, end_col)
+ if #lines > 0 then
+ if #lines == 1 then
+ lines[1] = string.sub(lines[1], start_col + 1, end_col)
+ else
+ lines[1] = string.sub(lines[1], start_col + 1)
+ lines[#lines] = string.sub(lines[#lines], 1, end_col)
+ end
end
- return table.concat(lines, "\n")
- elseif type(source) == "string" then
- return source:sub(start_byte+1, end_byte)
+ return table.concat(lines, '\n')
+ elseif type(source) == 'string' then
+ return source:sub(start_byte + 1, end_byte)
end
end
-- Predicate handler receive the following arguments
-- (match, pattern, bufnr, predicate)
local predicate_handlers = {
- ["eq?"] = function(match, _, source, predicate)
- local node = match[predicate[2]]
- local node_text = M.get_node_text(node, source)
-
- local str
- if type(predicate[3]) == "string" then
- -- (#eq? @aa "foo")
- str = predicate[3]
- else
- -- (#eq? @aa @bb)
- str = M.get_node_text(match[predicate[3]], source)
- end
+ ['eq?'] = function(match, _, source, predicate)
+ local node = match[predicate[2]]
+ if not node then
+ return true
+ end
+ local node_text = M.get_node_text(node, source)
- if node_text ~= str or str == nil then
- return false
- end
+ local str
+ if type(predicate[3]) == 'string' then
+ -- (#eq? @aa "foo")
+ str = predicate[3]
+ else
+ -- (#eq? @aa @bb)
+ str = M.get_node_text(match[predicate[3]], source)
+ end
- return true
+ if node_text ~= str or str == nil then
+ return false
+ end
+
+ return true
end,
- ["lua-match?"] = function(match, _, source, predicate)
- local node = match[predicate[2]]
- local regex = predicate[3]
- return string.find(M.get_node_text(node, source), regex)
+ ['lua-match?'] = function(match, _, source, predicate)
+ local node = match[predicate[2]]
+ if not node then
+ return true
+ end
+ local regex = predicate[3]
+ return string.find(M.get_node_text(node, source), regex)
end,
- ["match?"] = (function()
- local magic_prefixes = {['\\v']=true, ['\\m']=true, ['\\M']=true, ['\\V']=true}
+ ['match?'] = (function()
+ local magic_prefixes = { ['\\v'] = true, ['\\m'] = true, ['\\M'] = true, ['\\V'] = true }
---@private
local function check_magic(str)
- if string.len(str) < 2 or magic_prefixes[string.sub(str,1,2)] then
+ if string.len(str) < 2 or magic_prefixes[string.sub(str, 1, 2)] then
return str
end
- return '\\v'..str
+ return '\\v' .. str
end
local compiled_vim_regexes = setmetatable({}, {
@@ -243,21 +266,27 @@ local predicate_handlers = {
local res = vim.regex(check_magic(pattern))
rawset(t, pattern, res)
return res
- end
+ end,
})
return function(match, _, source, pred)
local node = match[pred[2]]
+ if not node then
+ return true
+ end
local regex = compiled_vim_regexes[pred[3]]
return regex:match_str(M.get_node_text(node, source))
end
end)(),
- ["contains?"] = function(match, _, source, predicate)
+ ['contains?'] = function(match, _, source, predicate)
local node = match[predicate[2]]
+ if not node then
+ return true
+ end
local node_text = M.get_node_text(node, source)
- for i=3,#predicate do
+ for i = 3, #predicate do
if string.find(node_text, predicate[i], 1, true) then
return true
end
@@ -266,19 +295,22 @@ local predicate_handlers = {
return false
end,
- ["any-of?"] = function(match, _, source, predicate)
+ ['any-of?'] = function(match, _, source, predicate)
local node = match[predicate[2]]
+ if not node then
+ return true
+ end
local node_text = M.get_node_text(node, source)
-- Since 'predicate' will not be used by callers of this function, use it
-- to store a string set built from the list of words to check against.
- local string_set = predicate["string_set"]
+ local string_set = predicate['string_set']
if not string_set then
string_set = {}
- for i=3,#predicate do
+ for i = 3, #predicate do
string_set[predicate[i]] = true
end
- predicate["string_set"] = string_set
+ predicate['string_set'] = string_set
end
return string_set[node_text]
@@ -286,32 +318,33 @@ local predicate_handlers = {
}
-- As we provide lua-match? also expose vim-match?
-predicate_handlers["vim-match?"] = predicate_handlers["match?"]
-
+predicate_handlers['vim-match?'] = predicate_handlers['match?']
-- Directives store metadata or perform side effects against a match.
-- Directives should always end with a `!`.
-- Directive handler receive the following arguments
-- (match, pattern, bufnr, predicate, metadata)
local directive_handlers = {
- ["set!"] = function(_, _, _, pred, metadata)
+ ['set!'] = function(_, _, _, pred, metadata)
if #pred == 4 then
-- (#set! @capture "key" "value")
- local capture = pred[2]
- if not metadata[capture] then
- metadata[capture] = {}
+ local _, capture_id, key, value = unpack(pred)
+ if not metadata[capture_id] then
+ metadata[capture_id] = {}
end
- metadata[capture][pred[3]] = pred[4]
+ metadata[capture_id][key] = value
else
+ local _, key, value = unpack(pred)
-- (#set! "key" "value")
- metadata[pred[2]] = pred[3]
+ metadata[key] = value
end
end,
-- Shifts the range of a node.
-- Example: (#offset! @_node 0 1 0 -1)
- ["offset!"] = function(match, _, _, pred, metadata)
- local offset_node = match[pred[2]]
- local range = {offset_node:range()}
+ ['offset!'] = function(match, _, _, pred, metadata)
+ local capture_id = pred[2]
+ local offset_node = match[capture_id]
+ local range = { offset_node:range() }
local start_row_offset = pred[3] or 0
local start_col_offset = pred[4] or 0
local end_row_offset = pred[5] or 0
@@ -324,9 +357,12 @@ local directive_handlers = {
-- If this produces an invalid range, we just skip it.
if range[1] < range[3] or (range[1] == range[3] and range[2] <= range[4]) then
- metadata.content = {range}
+ if not metadata[capture_id] then
+ metadata[capture_id] = {}
+ end
+ metadata[capture_id].range = range
end
- end
+ end,
}
--- Adds a new predicate to be used in queries
@@ -336,7 +372,7 @@ local directive_handlers = {
--- signature will be (match, pattern, bufnr, predicate)
function M.add_predicate(name, handler, force)
if predicate_handlers[name] and not force then
- error(string.format("Overriding %s", name))
+ error(string.format('Overriding %s', name))
end
predicate_handlers[name] = handler
@@ -344,17 +380,23 @@ end
--- Adds a new directive to be used in queries
---
+--- Handlers can set match level data by setting directly on the
+--- metadata object `metadata.key = value`, additionally, handlers
+--- can set node level data by using the capture id on the
+--- metadata table `metadata[capture_id].key = value`
+---
---@param name the name of the directive, without leading #
---@param handler the handler function to be used
---- signature will be (match, pattern, bufnr, predicate)
+--- signature will be (match, pattern, bufnr, predicate, metadata)
function M.add_directive(name, handler, force)
if directive_handlers[name] and not force then
- error(string.format("Overriding %s", name))
+ error(string.format('Overriding %s', name))
end
directive_handlers[name] = handler
end
+--- Lists the currently available directives to use in queries.
---@return The list of supported directives.
function M.list_directives()
return vim.tbl_keys(directive_handlers)
@@ -372,7 +414,7 @@ end
---@private
local function is_directive(name)
- return string.sub(name, -1) == "!"
+ return string.sub(name, -1) == '!'
end
---@private
@@ -389,7 +431,7 @@ function Query:match_preds(match, pattern, source)
-- Skip over directives... they will get processed after all the predicates.
if not is_directive(pred[1]) then
- if string.sub(pred[1], 1, 4) == "not-" then
+ if string.sub(pred[1], 1, 4) == 'not-' then
pred_name = string.sub(pred[1], 5)
is_not = true
else
@@ -400,7 +442,7 @@ function Query:match_preds(match, pattern, source)
local handler = predicate_handlers[pred_name]
if not handler then
- error(string.format("No handler for %s", pred[1]))
+ error(string.format('No handler for %s', pred[1]))
return false
end
@@ -423,7 +465,7 @@ function Query:apply_directives(match, pattern, source, metadata)
local handler = directive_handlers[pred[1]]
if not handler then
- error(string.format("No handler for %s", pred[1]))
+ error(string.format('No handler for %s', pred[1]))
return
end
@@ -432,7 +474,6 @@ function Query:apply_directives(match, pattern, source, metadata)
end
end
-
--- Returns the start and stop value if set else the node's range.
-- When the node's range is used, the stop is incremented by 1
-- to make the search inclusive.
@@ -477,7 +518,7 @@ end
---@returns The matching capture id
---@returns The captured node
function Query:iter_captures(node, source, start, stop)
- if type(source) == "number" and source == 0 then
+ if type(source) == 'number' and source == 0 then
source = vim.api.nvim_get_current_buf()
end
@@ -534,7 +575,7 @@ end
---@returns The matching pattern id
---@returns The matching match
function Query:iter_matches(node, source, start, stop)
- if type(source) == "number" and source == 0 then
+ if type(source) == 'number' and source == 0 then
source = vim.api.nvim_get_current_buf()
end
diff --git a/runtime/lua/vim/ui.lua b/runtime/lua/vim/ui.lua
index 9568b60fd0..6f1ce3089d 100644
--- a/runtime/lua/vim/ui.lua
+++ b/runtime/lua/vim/ui.lua
@@ -16,15 +16,33 @@ local M = {}
--- `items`, or the context in which select() was called.
---@param on_choice function ((item|nil, idx|nil) -> ())
--- Called once the user made a choice.
---- `idx` is the 1-based index of `item` within `item`.
+--- `idx` is the 1-based index of `item` within `items`.
--- `nil` if the user aborted the dialog.
+---
+---
+--- Example:
+--- <pre>
+--- vim.ui.select({ 'tabs', 'spaces' }, {
+--- prompt = 'Select tabs or spaces:',
+--- format_item = function(item)
+--- return "I'd like to choose " .. item
+--- end,
+--- }, function(choice)
+--- if choice == 'spaces' then
+--- vim.o.expandtab = true
+--- else
+--- vim.o.expandtab = false
+--- end
+--- end)
+--- </pre>
+
function M.select(items, opts, on_choice)
- vim.validate {
+ vim.validate({
items = { items, 'table', false },
on_choice = { on_choice, 'function', false },
- }
+ })
opts = opts or {}
- local choices = {opts.prompt or 'Select one of:'}
+ local choices = { opts.prompt or 'Select one of:' }
local format_item = opts.format_item or tostring
for i, item in pairs(items) do
table.insert(choices, string.format('%d: %s', i, format_item(item)))
@@ -41,7 +59,7 @@ end
---
---@param opts table Additional options. See |input()|
--- - prompt (string|nil)
---- Text of the prompt. Defaults to `Input: `.
+--- Text of the prompt
--- - default (string|nil)
--- Default reply to the input
--- - completion (string|nil)
@@ -57,12 +75,19 @@ end
--- Called once the user confirms or abort the input.
--- `input` is what the user typed.
--- `nil` if the user aborted the dialog.
+---
+--- Example:
+--- <pre>
+--- vim.ui.input({ prompt = 'Enter value for shiftwidth: ' }, function(input)
+--- vim.o.shiftwidth = tonumber(input)
+--- end)
+--- </pre>
function M.input(opts, on_confirm)
- vim.validate {
+ vim.validate({
on_confirm = { on_confirm, 'function', false },
- }
+ })
- opts = opts or {}
+ opts = (opts and not vim.tbl_isempty(opts)) and opts or vim.empty_dict()
local input = vim.fn.input(opts)
if #input > 0 then
on_confirm(input)
diff --git a/runtime/lua/vim/uri.lua b/runtime/lua/vim/uri.lua
index 11b661cd1a..d6b0b7410e 100644
--- a/runtime/lua/vim/uri.lua
+++ b/runtime/lua/vim/uri.lua
@@ -3,7 +3,6 @@
-- https://tools.ietf.org/html/rfc2732
-- https://tools.ietf.org/html/rfc2396
-
local uri_decode
do
local schar = string.char
@@ -14,7 +13,7 @@ do
return schar(tonumber(hex, 16))
end
uri_decode = function(str)
- return str:gsub("%%([a-fA-F0-9][a-fA-F0-9])", hex_to_char)
+ return str:gsub('%%([a-fA-F0-9][a-fA-F0-9])', hex_to_char)
end
end
@@ -23,33 +22,36 @@ do
local PATTERNS = {
--- RFC 2396
-- https://tools.ietf.org/html/rfc2396#section-2.2
- rfc2396 = "^A-Za-z0-9%-_.!~*'()";
+ rfc2396 = "^A-Za-z0-9%-_.!~*'()",
--- RFC 2732
-- https://tools.ietf.org/html/rfc2732
- rfc2732 = "^A-Za-z0-9%-_.!~*'()[]";
+ rfc2732 = "^A-Za-z0-9%-_.!~*'()[]",
--- RFC 3986
-- https://tools.ietf.org/html/rfc3986#section-2.2
- rfc3986 = "^A-Za-z0-9%-._~!$&'()*+,;=:@/";
+ rfc3986 = "^A-Za-z0-9%-._~!$&'()*+,;=:@/",
}
local sbyte, tohex = string.byte
if jit then
- tohex = require'bit'.tohex
+ tohex = require('bit').tohex
else
- tohex = function(b) return string.format("%02x", b) end
+ tohex = function(b)
+ return string.format('%02x', b)
+ end
end
---@private
local function percent_encode_char(char)
- return "%"..tohex(sbyte(char), 2)
+ return '%' .. tohex(sbyte(char), 2)
end
uri_encode = function(text, rfc)
- if not text then return end
+ if not text then
+ return
+ end
local pattern = PATTERNS[rfc] or PATTERNS.rfc3986
- return text:gsub("(["..pattern.."])", percent_encode_char)
+ return text:gsub('([' .. pattern .. '])', percent_encode_char)
end
end
-
---@private
local function is_windows_file_uri(uri)
return uri:match('^file:/+[a-zA-Z]:') ~= nil
@@ -59,16 +61,16 @@ end
---@param path string Path to file
---@return string URI
local function uri_from_fname(path)
- local volume_path, fname = path:match("^([a-zA-Z]:)(.*)")
+ local volume_path, fname = path:match('^([a-zA-Z]:)(.*)')
local is_windows = volume_path ~= nil
if is_windows then
- path = volume_path..uri_encode(fname:gsub("\\", "/"))
+ path = volume_path .. uri_encode(fname:gsub('\\', '/'))
else
path = uri_encode(path)
end
- local uri_parts = {"file://"}
+ local uri_parts = { 'file://' }
if is_windows then
- table.insert(uri_parts, "/")
+ table.insert(uri_parts, '/')
end
table.insert(uri_parts, path)
return table.concat(uri_parts)
@@ -82,11 +84,11 @@ local WINDOWS_URI_SCHEME_PATTERN = '^([a-zA-Z]+[a-zA-Z0-9.+-]*):[a-zA-Z]:.*'
---@return string URI
local function uri_from_bufnr(bufnr)
local fname = vim.api.nvim_buf_get_name(bufnr)
- local volume_path = fname:match("^([a-zA-Z]:).*")
+ local volume_path = fname:match('^([a-zA-Z]:).*')
local is_windows = volume_path ~= nil
local scheme
if is_windows then
- fname = fname:gsub("\\", "/")
+ fname = fname:gsub('\\', '/')
scheme = fname:match(WINDOWS_URI_SCHEME_PATTERN)
else
scheme = fname:match(URI_SCHEME_PATTERN)
diff --git a/runtime/menu.vim b/runtime/menu.vim
index 701dd33cdd..e20720dbd2 100644
--- a/runtime/menu.vim
+++ b/runtime/menu.vim
@@ -164,6 +164,9 @@ if exists(':tlmenu')
endif
nnoremenu 20.360 &Edit.&Paste<Tab>"+gP "+gP
cnoremenu &Edit.&Paste<Tab>"+gP <C-R>+
+if exists(':tlmenu')
+ tlnoremenu &Edit.&Paste<Tab>"+gP <C-W>"+
+endif
exe 'vnoremenu <script> &Edit.&Paste<Tab>"+gP ' . paste#paste_cmd['v']
exe 'inoremenu <script> &Edit.&Paste<Tab>"+gP ' . paste#paste_cmd['i']
nnoremenu 20.370 &Edit.Put\ &Before<Tab>[p [p
@@ -569,9 +572,9 @@ func! s:XxdConv()
%!mc vim:xxd
else
call s:XxdFind()
- exe '%!' . g:xxdprogram
+ exe ':%!' . g:xxdprogram
endif
- if getline(1) =~ "^0000000:" " only if it worked
+ if getline(1) =~ "^00000000:" " only if it worked
set ft=xxd
endif
let &mod = mod
@@ -583,7 +586,7 @@ func! s:XxdBack()
%!mc vim:xxd -r
else
call s:XxdFind()
- exe '%!' . g:xxdprogram . ' -r'
+ exe ':%!' . g:xxdprogram . ' -r'
endif
set ft=
doautocmd filetypedetect BufReadPost
diff --git a/runtime/nvim.appdata.xml b/runtime/nvim.appdata.xml
index 4ad656f1a3..1464c27694 100644
--- a/runtime/nvim.appdata.xml
+++ b/runtime/nvim.appdata.xml
@@ -26,6 +26,7 @@
</screenshots>
<releases>
+ <release date="2022-04-15" version="0.7.0"/>
<release date="2021-12-31" version="0.6.1"/>
<release date="2021-11-30" version="0.6.0"/>
<release date="2021-09-26" version="0.5.1"/>
diff --git a/runtime/nvim.desktop b/runtime/nvim.desktop
index 052904dd76..224353c6ad 100644
--- a/runtime/nvim.desktop
+++ b/runtime/nvim.desktop
@@ -1,6 +1,7 @@
[Desktop Entry]
Name=Neovim
GenericName=Text Editor
+GenericName[ckb]=دەستکاریکەری دەق
GenericName[de]=Texteditor
GenericName[fr]=Éditeur de texte
GenericName[ru]=Текстовый редактор
@@ -16,6 +17,7 @@ Comment[bg]=Редактиране на текстови файлове
Comment[bn]=টেক্স্ট ফাইল এডিট করুন
Comment[bs]=Izmijeni tekstualne datoteke
Comment[ca]=Edita fitxers de text
+Comment[ckb]=دەستکاریی فایلی دەق بکە
Comment[cs]=Úprava textových souborů
Comment[cy]=Golygu ffeiliau testun
Comment[da]=Redigér tekstfiler
@@ -79,6 +81,7 @@ Exec=nvim %F
Terminal=true
Type=Application
Keywords=Text;editor;
+Keywords[ckb]=دەق;دەستکاریکەر;
Keywords[fr]=Texte;éditeur;
Keywords[ru]=текст;текстовый редактор;
Keywords[sr]=Текст;едитор;
diff --git a/runtime/optwin.vim b/runtime/optwin.vim
index c873252909..228336e5a4 100644
--- a/runtime/optwin.vim
+++ b/runtime/optwin.vim
@@ -1,7 +1,7 @@
" These commands create the option window.
"
" Maintainer: Bram Moolenaar <Bram@vim.org>
-" Last Change: 2021 Dec 12
+" Last Change: 2022 Apr 07
" If there already is an option window, jump to that one.
let buf = bufnr('option-window')
@@ -229,8 +229,6 @@ call append("$", "compatible\tbehave very Vi compatible (not advisable)")
call <SID>BinOptionG("cp", &cp)
call append("$", "cpoptions\tlist of flags to specify Vi compatibility")
call <SID>OptionG("cpo", &cpo)
-call append("$", "insertmode\tuse Insert mode as the default mode")
-call <SID>BinOptionG("im", &im)
call append("$", "paste\tpaste mode, insert typed text literally")
call <SID>BinOptionG("paste", &paste)
call append("$", "pastetoggle\tkey sequence to toggle paste mode")
@@ -471,7 +469,7 @@ endif
call <SID>Header("multiple windows")
-call append("$", "laststatus\t0, 1 or 2; when to use a status line for the last window")
+call append("$", "laststatus\t0, 1, 2 or 3; when to use a status line for the last window")
call append("$", " \tset ls=" . &ls)
if has("statusline")
call append("$", "statusline\talternate format to be used for a status line")
@@ -870,6 +868,9 @@ if has("cindent")
call append("$", "cinwords\tlist of words that cause more C-indent")
call append("$", "\t(local to buffer)")
call <SID>OptionL("cinw")
+ call append("$", "cinscopedecls\tlist of scope declaration names used by cino-g")
+ call append("$", "\t(local to buffer)")
+ call <SID>OptionL("cinsd")
call append("$", "indentexpr\texpression used to obtain the indent of a line")
call append("$", "\t(local to buffer)")
call <SID>OptionL("inde")
@@ -953,7 +954,6 @@ call <SID>Header("mapping")
call append("$", "maxmapdepth\tmaximum depth of mapping")
call append("$", " \tset mmd=" . &mmd)
call append("$", "remap\trecognize mappings in mapped keys")
-call <SID>BinOptionG("remap", &remap)
call append("$", "timeout\tallow timing out halfway into a mapping")
call <SID>BinOptionG("to", &to)
call append("$", "ttimeout\tallow timing out halfway into a key code")
diff --git a/runtime/pack/dist/opt/matchit/autoload/matchit.vim b/runtime/pack/dist/opt/matchit/autoload/matchit.vim
index e8689980ae..eafb7c0551 100644
--- a/runtime/pack/dist/opt/matchit/autoload/matchit.vim
+++ b/runtime/pack/dist/opt/matchit/autoload/matchit.vim
@@ -763,9 +763,9 @@ fun! s:ParseSkip(str)
let skip = "synIDattr(synID(line('.'),col('.'),1),'name') !~? '" ..
\ strpart(skip,2) .. "'"
elseif skip[0] == "r"
- let skip = "strpart(getline('.'),0,col('.'))=~'" . strpart(skip,2). "'"
+ let skip = "strpart(getline('.'),0,col('.'))=~'" .. strpart(skip,2) .. "'"
elseif skip[0] == "R"
- let skip = "strpart(getline('.'),0,col('.'))!~'" . strpart(skip,2). "'"
+ let skip = "strpart(getline('.'),0,col('.'))!~'" .. strpart(skip,2) .. "'"
endif
endif
return skip
diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
index c881133b72..802ebd42b5 100644
--- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
+++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
@@ -2,12 +2,13 @@
"
" Author: Bram Moolenaar
" Copyright: Vim license applies, see ":help license"
-" Last Change: 2021 Dec 16
+" Last Change: 2022 Jun 24
"
-" WORK IN PROGRESS - Only the basics work
-" Note: On MS-Windows you need a recent version of gdb. The one included with
-" MingW is too old (7.6.1).
-" I used version 7.12 from http://www.equation.com/servlet/equation.cmd?fa=gdb
+" WORK IN PROGRESS - The basics works stable, more to come
+" Note: In general you need at least GDB 7.12 because this provides the
+" frame= response in MI thread-selected events we need to sync stack to file.
+" The one included with "old" MingW is too old (7.6.1), you may upgrade it or
+" use a newer version from http://www.equation.com/servlet/equation.cmd?fa=gdb
"
" There are two ways to run gdb:
" - In a terminal window; used if possible, does not work on MS-Windows
@@ -36,7 +37,7 @@
"
" For neovim compatibility, the vim specific calls were replaced with neovim
" specific calls:
-" term_start -> term_open
+" term_start -> termopen
" term_sendkeys -> chansend
" term_getline -> getbufline
" job_info && term_getjob -> using linux command ps to get the tty
@@ -70,11 +71,6 @@ set cpo&vim
command -nargs=* -complete=file -bang Termdebug call s:StartDebug(<bang>0, <f-args>)
command -nargs=+ -complete=file -bang TermdebugCommand call s:StartDebugCommand(<bang>0, <f-args>)
-" Name of the gdb command, defaults to "gdb".
-if !exists('g:termdebugger')
- let g:termdebugger = 'gdb'
-endif
-
let s:pc_id = 12
let s:asm_id = 13
let s:break_id = 14 " breakpoint number is added to this
@@ -104,8 +100,17 @@ call s:Highlight(1, '', &background)
hi default debugBreakpoint term=reverse ctermbg=red guibg=red
hi default debugBreakpointDisabled term=reverse ctermbg=gray guibg=gray
+" Get the command to execute the debugger as a list, defaults to ["gdb"].
func s:GetCommand()
- return type(g:termdebugger) == v:t_list ? copy(g:termdebugger) : [g:termdebugger]
+ if exists('g:termdebug_config')
+ let cmd = get(g:termdebug_config, 'command', 'gdb')
+ elseif exists('g:termdebugger')
+ let cmd = g:termdebugger
+ else
+ let cmd = 'gdb'
+ endif
+
+ return type(cmd) == v:t_list ? copy(cmd) : [cmd]
endfunc
func s:StartDebug(bang, ...)
@@ -176,12 +181,10 @@ func s:StartDebug_internal(dict)
call s:StartDebug_term(a:dict)
endif
- if exists('g:termdebug_disasm_window')
- if g:termdebug_disasm_window
- let curwinid = win_getid(winnr())
- call s:GotoAsmwinOrCreateIt()
- call win_gotoid(curwinid)
- endif
+ if s:GetDisasmWindow()
+ let curwinid = win_getid(winnr())
+ call s:GotoAsmwinOrCreateIt()
+ call win_gotoid(curwinid)
endif
if exists('#User#TermdebugStartPost')
@@ -196,7 +199,7 @@ func s:CloseBuffers()
endfunc
func s:CheckGdbRunning()
- if nvim_get_chan_info(s:gdb_job_id) == {}
+ if !s:running
echoerr string(s:GetCommand()[0]) . ' exited unexpectedly'
call s:CloseBuffers()
return ''
@@ -204,8 +207,8 @@ func s:CheckGdbRunning()
return 'ok'
endfunc
+" Open a terminal window without a job, to run the debugged program in.
func s:StartDebug_term(dict)
- " Open a terminal window without a job, to run the debugged program in.
execute s:vertical ? 'vnew' : 'new'
let s:pty_job_id = termopen('tail -f /dev/null;#gdb program')
if s:pty_job_id == 0
@@ -251,18 +254,28 @@ func s:StartDebug_term(dict)
let proc_args = get(a:dict, 'proc_args', [])
let gdb_cmd = s:GetCommand()
- " Add -quiet to avoid the intro message causing a hit-enter prompt.
- let gdb_cmd += ['-quiet']
- " Disable pagination, it causes everything to stop at the gdb
- let gdb_cmd += ['-iex', 'set pagination off']
- " Interpret commands while the target is running. This should usually only
- " be exec-interrupt, since many commands don't work properly while the
- " target is running (so execute during startup).
- let gdb_cmd += ['-iex', 'set mi-async on']
- " Open a terminal window to run the debugger.
- let gdb_cmd += ['-tty', pty]
- " Command executed _after_ startup is done, provides us with the necessary feedback
- let gdb_cmd += ['-ex', 'echo startupdone\n']
+
+ if exists('g:termdebug_config') && has_key(g:termdebug_config, 'command_add_args')
+ let gdb_cmd = g:termdebug_config.command_add_args(gdb_cmd, pty)
+ else
+ " Add -quiet to avoid the intro message causing a hit-enter prompt.
+ let gdb_cmd += ['-quiet']
+ " Disable pagination, it causes everything to stop at the gdb
+ let gdb_cmd += ['-iex', 'set pagination off']
+ " Interpret commands while the target is running. This should usually only
+ " be exec-interrupt, since many commands don't work properly while the
+ " target is running (so execute during startup).
+ let gdb_cmd += ['-iex', 'set mi-async on']
+ " Open a terminal window to run the debugger.
+ let gdb_cmd += ['-tty', pty]
+ " Command executed _after_ startup is done, provides us with the necessary
+ " feedback
+ let gdb_cmd += ['-ex', 'echo startupdone\n']
+ endif
+
+ if exists('g:termdebug_config') && has_key(g:termdebug_config, 'command_filter')
+ let gdb_cmd = g:termdebug_config.command_filter(gdb_cmd)
+ endif
" Adding arguments requested by the user
let gdb_cmd += gdb_args
@@ -279,6 +292,8 @@ func s:StartDebug_term(dict)
call s:CloseBuffers()
return
endif
+ let s:running = v:true
+ let s:starting = v:true
let gdb_job_info = nvim_get_chan_info(s:gdb_job_id)
let s:gdbbuf = gdb_job_info['buffer']
let s:gdbwin = win_getid(winnr())
@@ -354,14 +369,16 @@ func s:StartDebug_term(dict)
sleep 10m
endwhile
+ let s:starting = v:false
+
" Set the filetype, this can be used to add mappings.
set filetype=termdebug
call s:StartDebugCommon(a:dict)
endfunc
+" Open a window with a prompt buffer to run gdb in.
func s:StartDebug_prompt(dict)
- " Open a window with a prompt buffer to run gdb in.
if s:vertical
vertical new
else
@@ -461,7 +478,7 @@ endfunc
func s:StartDebugCommon(dict)
" Sign used to highlight the line where the program has stopped.
" There can be only one.
- sign define debugPC linehl=debugPC
+ call sign_define('debugPC', #{linehl: 'debugPC'})
" Install debugger commands in the text window.
call win_gotoid(s:sourcewin)
@@ -522,6 +539,7 @@ func TermDebugSendCommand(cmd)
endif
sleep 10m
endif
+ " TODO: should we prepend CTRL-U to clear the command?
call chansend(s:gdb_job_id, a:cmd . "\r")
if do_continue
Continue
@@ -662,6 +680,11 @@ func s:GetAsmAddr(msg)
endfunc
function s:EndTermDebug(job_id, exit_code, event)
+ let s:running = v:false
+ if s:starting
+ return
+ endif
+
if exists('#User#TermdebugStopPre')
doauto <nomodeline> User TermdebugStopPre
endif
@@ -690,7 +713,9 @@ func s:EndDebugCommon()
endif
endif
endfor
- exe was_buf .. "buf"
+ if bufexists(was_buf)
+ exe was_buf .. "buf"
+ endif
call s:DeleteCommands()
@@ -749,8 +774,8 @@ func s:HandleDisasmMsg(msg)
let lnum = search('^' . s:asm_addr)
if lnum != 0
- exe 'sign unplace ' . s:asm_id
- exe 'sign place ' . s:asm_id . ' line=' . lnum . ' name=debugPC'
+ call sign_unplace('TermDebug', #{id: s:asm_id})
+ call sign_place(s:asm_id, 'TermDebug', 'debugPC', '%', #{lnum: lnum})
endif
call win_gotoid(curwinid)
@@ -836,6 +861,7 @@ func s:InstallCommands()
command Clear call s:ClearBreakpoint()
command Step call s:SendResumingCommand('-exec-step')
command Over call s:SendResumingCommand('-exec-next')
+ command -nargs=? Until call s:Until(<q-args>)
command Finish call s:SendResumingCommand('-exec-finish')
command -nargs=* Run call s:Run(<q-args>)
command -nargs=* Arguments call s:SendResumingCommand('-exec-arguments ' . <q-args>)
@@ -858,7 +884,13 @@ func s:InstallCommands()
command Asm call s:GotoAsmwinOrCreateIt()
command Winbar call s:InstallWinbar()
- if !exists('g:termdebug_map_K') || g:termdebug_map_K
+ let map = 1
+ if exists('g:termdebug_config')
+ let map = get(g:termdebug_config, 'map_K', 1)
+ elseif exists('g:termdebug_map_K')
+ let map = g:termdebug_map_K
+ endif
+ if map
" let s:k_map_saved = maparg('K', 'n', 0, 1)
let s:k_map_saved = {}
for map in nvim_get_keymap('n')
@@ -870,6 +902,26 @@ func s:InstallCommands()
nnoremap K :Evaluate<CR>
endif
+ if has('menu') && &mouse != ''
+ call s:InstallWinbar()
+
+ let popup = 1
+ if exists('g:termdebug_config')
+ let popup = get(g:termdebug_config, 'popup', 1)
+ elseif exists('g:termdebug_popup')
+ let popup = g:termdebug_popup
+ endif
+ if popup
+ let s:saved_mousemodel = &mousemodel
+ let &mousemodel = 'popup_setpos'
+ an 1.200 PopUp.-SEP3- <Nop>
+ an 1.210 PopUp.Set\ breakpoint :Break<CR>
+ an 1.220 PopUp.Clear\ breakpoint :Clear<CR>
+ an 1.230 PopUp.Run\ until :Until<CR>
+ an 1.240 PopUp.Evaluate :Evaluate<CR>
+ endif
+ endif
+
let &cpo = save_cpo
endfunc
@@ -894,6 +946,7 @@ func s:DeleteCommands()
delcommand Clear
delcommand Step
delcommand Over
+ delcommand Until
delcommand Finish
delcommand Run
delcommand Arguments
@@ -910,7 +963,7 @@ func s:DeleteCommands()
if empty(s:k_map_saved)
nunmap K
else
- " call mapset('n', 0, s:k_map_saved)
+ " call mapset(s:k_map_saved)
let mode = s:k_map_saved.mode !=# ' ' ? s:k_map_saved.mode : ''
call nvim_set_keymap(mode, 'K', s:k_map_saved.rhs, {
\ 'expr': s:k_map_saved.expr ? v:true : v:false,
@@ -923,22 +976,57 @@ func s:DeleteCommands()
unlet s:k_map_saved
endif
- exe 'sign unplace ' . s:pc_id
- for [id, entries] in items(s:breakpoints)
- for subid in keys(entries)
- exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid)
- endfor
- endfor
+ if has('menu')
+ " Remove the WinBar entries from all windows where it was added.
+ " let curwinid = win_getid(winnr())
+ " for winid in s:winbar_winids
+ " if win_gotoid(winid)
+ " aunmenu WinBar.Step
+ " aunmenu WinBar.Next
+ " aunmenu WinBar.Finish
+ " aunmenu WinBar.Cont
+ " aunmenu WinBar.Stop
+ " aunmenu WinBar.Eval
+ " endif
+ " endfor
+ " call win_gotoid(curwinid)
+ " let s:winbar_winids = []
+
+ if exists('s:saved_mousemodel')
+ let &mousemodel = s:saved_mousemodel
+ unlet s:saved_mousemodel
+ aunmenu PopUp.-SEP3-
+ aunmenu PopUp.Set\ breakpoint
+ aunmenu PopUp.Clear\ breakpoint
+ aunmenu PopUp.Run\ until
+ aunmenu PopUp.Evaluate
+ endif
+ endif
+
+ call sign_unplace('TermDebug')
unlet s:breakpoints
unlet s:breakpoint_locations
- sign undefine debugPC
- for val in s:BreakpointSigns
- exe "sign undefine debugBreakpoint" . val
- endfor
+ call sign_undefine('debugPC')
+ call sign_undefine(s:BreakpointSigns->map("'debugBreakpoint' .. v:val"))
let s:BreakpointSigns = []
endfunc
+" :Until - Execute until past a specified position or current line
+func s:Until(at)
+ if s:stopped
+ " reset s:stopped here, it may take a bit of time before we get a response
+ let s:stopped = 0
+ " call ch_log('assume that program is running after this command')
+ " Use the fname:lnum format
+ let at = empty(a:at) ?
+ \ fnameescape(expand('%:p')) . ':' . line('.') : a:at
+ call s:SendCommand('-exec-until ' . at)
+ " else
+ " call ch_log('dropping command, program is running: exec-until')
+ endif
+endfunc
+
" :Break - Set a breakpoint at the cursor position.
func s:SetBreakpoint(at)
" Setting a breakpoint may not work while the program is running.
@@ -972,7 +1060,8 @@ func s:ClearBreakpoint()
" Assume this always works, the reply is simply "^done".
call s:SendCommand('-break-delete ' . id)
for subid in keys(s:breakpoints[id])
- exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid)
+ call sign_unplace('TermDebug',
+ \ #{id: s:Breakpoint2SignNumber(id, subid)})
endfor
unlet s:breakpoints[id]
unlet s:breakpoint_locations[bploc][idx]
@@ -1026,7 +1115,7 @@ func s:Evaluate(range, arg)
call s:SendEval(expr)
endfunc
-" get what is specified / under the cursor
+" get what is specified / under the cursor
func s:GetEvaluationExpression(range, arg)
if a:arg != ''
" user supplied evaluation
@@ -1257,6 +1346,26 @@ func s:GotoSourcewinOrCreateIt()
endif
endfunc
+func s:GetDisasmWindow()
+ if exists('g:termdebug_config')
+ return get(g:termdebug_config, 'disasm_window', 0)
+ endif
+ if exists('g:termdebug_disasm_window')
+ return g:termdebug_disasm_window
+ endif
+ return 0
+endfunc
+
+func s:GetDisasmWindowHeight()
+ if exists('g:termdebug_config')
+ return get(g:termdebug_config, 'disasm_window_height', 0)
+ endif
+ if exists('g:termdebug_disasm_window') && g:termdebug_disasm_window > 1
+ return g:termdebug_disasm_window
+ endif
+ return 0
+endfunc
+
func s:GotoAsmwinOrCreateIt()
if !win_gotoid(s:asmwin)
if win_gotoid(s:sourcewin)
@@ -1280,10 +1389,8 @@ func s:GotoAsmwinOrCreateIt()
exe 'file Termdebug-asm-listing'
endif
- if exists('g:termdebug_disasm_window')
- if g:termdebug_disasm_window > 1
- exe 'resize ' . g:termdebug_disasm_window
- endif
+ if s:GetDisasmWindowHeight() > 0
+ exe 'resize ' .. s:GetDisasmWindowHeight()
endif
endif
@@ -1294,8 +1401,8 @@ func s:GotoAsmwinOrCreateIt()
call s:SendCommand('disassemble $pc')
endif
else
- exe 'sign unplace ' . s:asm_id
- exe 'sign place ' . s:asm_id . ' line=' . lnum . ' name=debugPC'
+ call sign_unplace('TermDebug', #{id: s:asm_id})
+ call sign_place(s:asm_id, 'TermDebug', 'debugPC', '%', #{lnum: lnum})
endif
endif
endfunc
@@ -1330,8 +1437,8 @@ func s:HandleCursor(msg)
if lnum == 0
call s:SendCommand('disassemble $pc')
else
- exe 'sign unplace ' . s:asm_id
- exe 'sign place ' . s:asm_id . ' line=' . lnum . ' name=debugPC'
+ call sign_unplace('TermDebug', #{id: s:asm_id})
+ call sign_place(s:asm_id, 'TermDebug', 'debugPC', '%', #{lnum: lnum})
endif
call win_gotoid(curwinid)
@@ -1344,6 +1451,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 it is unlikely we 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,11 +1469,15 @@ func s:HandleCursor(msg)
else
exe 'edit ' . fnameescape(fname)
endif
+ augroup Termdebug
+ au! SwapExists
+ augroup END
endif
exe lnum
normal! zv
- exe 'sign unplace ' . s:pc_id
- exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname
+ call sign_unplace('TermDebug', #{id: s:pc_id})
+ call sign_place(s:pc_id, 'TermDebug', 'debugPC', fname,
+ \ #{lnum: lnum, priority: 110})
if !exists('b:save_signcolumn')
let b:save_signcolumn = &signcolumn
call add(s:signcolumn_buflist, bufnr())
@@ -1364,7 +1485,7 @@ func s:HandleCursor(msg)
setlocal signcolumn=yes
endif
elseif !s:stopped || fname != ''
- exe 'sign unplace ' . s:pc_id
+ call sign_unplace('TermDebug', #{id: s:pc_id})
endif
call win_gotoid(wid)
@@ -1381,7 +1502,9 @@ func s:CreateBreakpoint(id, subid, enabled)
else
let hiName = "debugBreakpoint"
endif
- exe "sign define debugBreakpoint" . nr . " text=" . substitute(nr, '\..*', '', '') . " texthl=" . hiName
+ call sign_define('debugBreakpoint' .. nr,
+ \ #{text: substitute(nr, '\..*', '', ''),
+ \ texthl: hiName})
endif
endfunc
@@ -1459,7 +1582,9 @@ endfunc
func s:PlaceSign(id, subid, entry)
let nr = printf('%d.%d', a:id, a:subid)
- exe 'sign place ' . s:Breakpoint2SignNumber(a:id, a:subid) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . nr . ' priority=110 file=' . a:entry['fname']
+ call sign_place(s:Breakpoint2SignNumber(a:id, a:subid), 'TermDebug',
+ \ 'debugBreakpoint' .. nr, a:entry['fname'],
+ \ #{lnum: a:entry['lnum'], priority: 110})
let a:entry['placed'] = 1
endfunc
@@ -1473,7 +1598,8 @@ func s:HandleBreakpointDelete(msg)
if has_key(s:breakpoints, id)
for [subid, entry] in items(s:breakpoints[id])
if has_key(entry, 'placed')
- exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid)
+ call sign_unplace('TermDebug',
+ \ #{id: s:Breakpoint2SignNumber(id, subid)})
unlet entry['placed']
endif
endfor
diff --git a/runtime/plugin/rplugin.vim b/runtime/plugin/rplugin.vim
index 122d8d47f8..52f4bd05bc 100644
--- a/runtime/plugin/rplugin.vim
+++ b/runtime/plugin/rplugin.vim
@@ -62,4 +62,6 @@ endfunction
command! -bar UpdateRemotePlugins call remote#host#UpdateRemotePlugins()
-call s:LoadRemotePlugins()
+if index(v:argv, "--clean") < 0
+ call s:LoadRemotePlugins()
+endif
diff --git a/runtime/scripts.vim b/runtime/scripts.vim
index e41405a6c5..2d8bfdcb05 100644
--- a/runtime/scripts.vim
+++ b/runtime/scripts.vim
@@ -11,6 +11,10 @@
" 'ignorecase' option making a difference. Where case is to be ignored use
" =~? instead. Do not use =~ anywhere.
+" Only run when using legacy filetype
+if !exists('g:do_legacy_filetype')
+ finish
+endif
" Only do the rest when the FileType autocommand has not been triggered yet.
if did_filetype()
@@ -202,6 +206,14 @@ if s:line1 =~# "^#!"
elseif s:name =~# 'gforth\>'
set ft=forth
+ " Icon
+ elseif s:name =~# 'icon\>'
+ set ft=icon
+
+ " Guile
+ elseif s:name =~# 'guile'
+ set ft=scheme
+
endif
unlet s:name
@@ -406,6 +418,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/spell/cleanadd.vim b/runtime/spell/cleanadd.vim
new file mode 100644
index 0000000000..6dc0692186
--- /dev/null
+++ b/runtime/spell/cleanadd.vim
@@ -0,0 +1,32 @@
+" Vim script to clean the ll.xxxxx.add files of commented out entries
+" Author: Antonio Colombo, Bram Moolenaar
+" Last Update: 2008 Jun 3
+
+" Time in seconds after last time an ll.xxxxx.add file was updated
+" Default is one second.
+" If you invoke this script often set it to something bigger, e.g. 60 * 60
+" (one hour)
+if !exists("g:spell_clean_limit")
+ let g:spell_clean_limit = 1
+endif
+
+" Loop over all the runtime/spell/*.add files.
+" Delete all comment lines, except the ones starting with ##.
+for s:fname in split(globpath(&rtp, "spell/*.add"), "\n")
+ if filewritable(s:fname) && localtime() - getftime(s:fname) > g:spell_clean_limit
+ if exists('*fnameescape')
+ let s:f = fnameescape(s:fname)
+ else
+ let s:f = escape(s:fname, ' \|<')
+ endif
+ silent exe "tab split " . s:f
+ echo "Processing" s:f
+ silent! g/^#[^#]/d
+ silent update
+ close
+ unlet s:f
+ endif
+endfor
+unlet s:fname
+
+echo "Done"
diff --git a/runtime/syntax/apache.vim b/runtime/syntax/apache.vim
index dd18aa6570..e73045e4c8 100644
--- a/runtime/syntax/apache.vim
+++ b/runtime/syntax/apache.vim
@@ -3,7 +3,7 @@
" Maintainer: David Necas (Yeti) <yeti@physics.muni.cz>
" License: This file can be redistribued and/or modified under the same terms
" as Vim itself.
-" Last Change: 2020 Oct 07
+" Last Change: 2022 Apr 25
" Notes: Last synced with apache-2.2.3, version 1.x is no longer supported
" TODO: see particular FIXME's scattered through the file
" make it really linewise?
@@ -46,6 +46,7 @@ syn keyword apacheMethodOption GET POST PUT DELETE CONNECT OPTIONS TRACE PATCH P
syn keyword apacheMethodOption BASELINE-CONTROL CHECKIN CHECKOUT LABEL MERGE MKACTIVITY MKWORKSPACE REPORT UNCHECKOUT UPDATE VERSION-CONTROL contained
syn case ignore
syn match apacheSection "<\/\=\(Directory\|DirectoryMatch\|Files\|FilesMatch\|IfModule\|IfDefine\|Location\|LocationMatch\|VirtualHost\)[^>]*>" contains=apacheAnything
+syn match apacheSection "<\/\=\(RequireAll\|RequireAny\|RequireNone\)>" contains=apacheAnything
syn match apacheLimitSection "<\/\=\(Limit\|LimitExcept\)[^>]*>" contains=apacheLimitSectionKeyword,apacheMethodOption,apacheError
syn keyword apacheLimitSectionKeyword Limit LimitExcept contained
syn match apacheAuthType "AuthType\s.*$" contains=apacheAuthTypeValue
diff --git a/runtime/syntax/basic.vim b/runtime/syntax/basic.vim
index ad9450b3b8..b6bf23a3ed 100644
--- a/runtime/syntax/basic.vim
+++ b/runtime/syntax/basic.vim
@@ -1,14 +1,15 @@
" Vim syntax file
-" Language: BASIC
+" Language: BASIC (QuickBASIC 4.5)
" Maintainer: Doug Kearns <dougkearns@gmail.com>
" Previous Maintainer: Allan Kelly <allan@fruitloaf.co.uk>
" Contributors: Thilo Six
-" Last Change: 2015 Jan 10
+" Last Change: 2022 Jun 22
" First version based on Micro$soft QBASIC circa 1989, as documented in
" 'Learn BASIC Now' by Halvorson&Rygmyr. Microsoft Press 1989.
-" This syntax file not a complete implementation yet. Send suggestions to the
-" maintainer.
+"
+" Second version attempts to match Microsoft QuickBASIC 4.5 while keeping FreeBASIC
+" (-lang qb) and QB64 (excluding extensions) in mind. -- DJK
" Prelude {{{1
if exists("b:current_syntax")
@@ -18,154 +19,357 @@ endif
let s:cpo_save = &cpo
set cpo&vim
+syn iskeyword @,48-57,.,!,#,%,&,$
+syn case ignore
+
+" Whitespace Errors {{{1
+if exists("basic_space_errors")
+ if !exists("basic_no_trail_space_error")
+ syn match basicSpaceError display excludenl "\s\+$"
+ endif
+ if !exists("basic_no_tab_space_error")
+ syn match basicSpaceError display " \+\t"me=e-1
+ endif
+endif
+
+" Comment Errors {{{1
+if !exists("basic_no_comment_errors")
+ syn match basicCommentError "\<REM\>.*"
+endif
+
+" Not Top Cluster {{{1
+syn cluster basicNotTop contains=@basicLineIdentifier,basicDataString,basicDataSeparator,basicTodo
+
+" Statements {{{1
+
+syn cluster basicStatements contains=basicStatement,basicDataStatement,basicMetaRemStatement,basicPutStatement,basicRemStatement
+
+let s:statements =<< trim EOL " {{{2
+ beep
+ bload
+ bsave
+ call
+ calls
+ case
+ chain
+ chdir
+ circle
+ clear
+ close
+ cls
+ color
+ com
+ common
+ const
+ declare
+ def
+ def\s\+seg
+ defdbl
+ defint
+ deflng
+ defsng
+ defstr
+ dim
+ do
+ draw
+ elseif
+ end
+ end\s\+\%(def\|function\|if\|select\|sub\|type\)
+ environ
+ erase
+ error
+ exit\s\+\%(def\|do\|for\|function\|sub\)
+ field
+ files
+ for
+ function
+ get
+ gosub
+ goto
+ if
+ input
+ ioctl
+ key
+ kill
+ let
+ line
+ line\s\+input
+ locate
+ lock
+ loop
+ lprint
+ lset
+ mkdir
+ name
+ next
+ on
+ on\s\+error
+ on\s\+uevent
+ open
+ open\s\+com
+ option
+ out
+ paint
+ palette
+ palette\s\+using
+ pcopy
+ pen
+ pmap
+ poke
+ preset
+ print
+ pset
+ randomize
+ read
+ redim
+ reset
+ restore
+ resume
+ return
+ rmdir
+ rset
+ run
+ select\s\+case
+ shared
+ shell
+ sleep
+ sound
+ static
+ stop
+ strig
+ sub
+ swap
+ system
+ troff
+ tron
+ type
+ uevent
+ unlock
+ using
+ view
+ view\s\+print
+ wait
+ wend
+ while
+ width
+ window
+ write
+EOL
+" }}}
+
+for s in s:statements
+ exe 'syn match basicStatement "\<' .. s .. '\>" contained'
+endfor
+
+syn match basicStatement "\<\%(then\|else\)\>" nextgroup=@basicStatements skipwhite
+
+" DATA Statement
+syn match basicDataSeparator "," contained
+syn region basicDataStatement matchgroup=basicStatement start="\<data\>" matchgroup=basicStatementSeparator end=":\|$" contained contains=basicDataSeparator,basicDataString,basicNumber,basicFloat,basicString
+
+if !exists("basic_no_data_fold")
+ syn region basicMultilineData start="^\s*\<data\>.*\n\%(^\s*\<data\>\)\@=" end="^\s*\<data\>.*\n\%(^\s*\<data\>\)\@!" contains=basicDataStatement transparent fold keepend
+endif
+
+" PUT File I/O and Graphics statements - needs special handling for graphics
+" action verbs
+syn match basicPutAction "\<\%(pset\|preset\|and\|or\|xor\)\>" contained
+syn region basicPutStatement matchgroup=basicStatement start="\<put\>" matchgroup=basicStatementSeparator end=":\|$" contained contains=basicKeyword,basicPutAction,basicFilenumber
+
" Keywords {{{1
-syn keyword basicStatement BEEP beep Beep BLOAD bload Bload BSAVE bsave Bsave
-syn keyword basicStatement CALL call Call ABSOLUTE absolute Absolute
-syn keyword basicStatement CHAIN chain Chain CHDIR chdir Chdir
-syn keyword basicStatement CIRCLE circle Circle CLEAR clear Clear
-syn keyword basicStatement CLOSE close Close CLS cls Cls COLOR color Color
-syn keyword basicStatement COM com Com COMMON common Common
-syn keyword basicStatement CONST const Const DATA data Data
-syn keyword basicStatement DECLARE declare Declare DEF def Def
-syn keyword basicStatement DEFDBL defdbl Defdbl DEFINT defint Defint
-syn keyword basicStatement DEFLNG deflng Deflng DEFSNG defsng Defsng
-syn keyword basicStatement DEFSTR defstr Defstr DIM dim Dim
-syn keyword basicStatement DO do Do LOOP loop Loop
-syn keyword basicStatement DRAW draw Draw END end End
-syn keyword basicStatement ENVIRON environ Environ ERASE erase Erase
-syn keyword basicStatement ERROR error Error EXIT exit Exit
-syn keyword basicStatement FIELD field Field FILES files Files
-syn keyword basicStatement FOR for For NEXT next Next
-syn keyword basicStatement FUNCTION function Function GET get Get
-syn keyword basicStatement GOSUB gosub Gosub GOTO goto Goto
-syn keyword basicStatement IF if If THEN then Then ELSE else Else
-syn keyword basicStatement INPUT input Input INPUT# input# Input#
-syn keyword basicStatement IOCTL ioctl Ioctl KEY key Key
-syn keyword basicStatement KILL kill Kill LET let Let
-syn keyword basicStatement LINE line Line LOCATE locate Locate
-syn keyword basicStatement LOCK lock Lock UNLOCK unlock Unlock
-syn keyword basicStatement LPRINT lprint Lprint USING using Using
-syn keyword basicStatement LSET lset Lset MKDIR mkdir Mkdir
-syn keyword basicStatement NAME name Name ON on On
-syn keyword basicStatement ERROR error Error OPEN open Open
-syn keyword basicStatement OPTION option Option BASE base Base
-syn keyword basicStatement OUT out Out PAINT paint Paint
-syn keyword basicStatement PALETTE palette Palette PCOPY pcopy Pcopy
-syn keyword basicStatement PEN pen Pen PLAY play Play
-syn keyword basicStatement PMAP pmap Pmap POKE poke Poke
-syn keyword basicStatement PRESET preset Preset PRINT print Print
-syn keyword basicStatement PRINT# print# Print# USING using Using
-syn keyword basicStatement PSET pset Pset PUT put Put
-syn keyword basicStatement RANDOMIZE randomize Randomize READ read Read
-syn keyword basicStatement REDIM redim Redim RESET reset Reset
-syn keyword basicStatement RESTORE restore Restore RESUME resume Resume
-syn keyword basicStatement RETURN return Return RMDIR rmdir Rmdir
-syn keyword basicStatement RSET rset Rset RUN run Run
-syn keyword basicStatement SEEK seek Seek SELECT select Select
-syn keyword basicStatement CASE case Case SHARED shared Shared
-syn keyword basicStatement SHELL shell Shell SLEEP sleep Sleep
-syn keyword basicStatement SOUND sound Sound STATIC static Static
-syn keyword basicStatement STOP stop Stop STRIG strig Strig
-syn keyword basicStatement SUB sub Sub SWAP swap Swap
-syn keyword basicStatement SYSTEM system System TIMER timer Timer
-syn keyword basicStatement TROFF troff Troff TRON tron Tron
-syn keyword basicStatement TYPE type Type UNLOCK unlock Unlock
-syn keyword basicStatement VIEW view View WAIT wait Wait
-syn keyword basicStatement WHILE while While WEND wend Wend
-syn keyword basicStatement WIDTH width Width WINDOW window Window
-syn keyword basicStatement WRITE write Write DATE$ date$ Date$
-syn keyword basicStatement MID$ mid$ Mid$ TIME$ time$ Time$
-
-syn keyword basicFunction ABS abs Abs ASC asc Asc
-syn keyword basicFunction ATN atn Atn CDBL cdbl Cdbl
-syn keyword basicFunction CINT cint Cint CLNG clng Clng
-syn keyword basicFunction COS cos Cos CSNG csng Csng
-syn keyword basicFunction CSRLIN csrlin Csrlin CVD cvd Cvd
-syn keyword basicFunction CVDMBF cvdmbf Cvdmbf CVI cvi Cvi
-syn keyword basicFunction CVL cvl Cvl CVS cvs Cvs
-syn keyword basicFunction CVSMBF cvsmbf Cvsmbf EOF eof Eof
-syn keyword basicFunction ERDEV erdev Erdev ERL erl Erl
-syn keyword basicFunction ERR err Err EXP exp Exp
-syn keyword basicFunction FILEATTR fileattr Fileattr FIX fix Fix
-syn keyword basicFunction FRE fre Fre FREEFILE freefile Freefile
-syn keyword basicFunction INP inp Inp INSTR instr Instr
-syn keyword basicFunction INT int Int LBOUND lbound Lbound
-syn keyword basicFunction LEN len Len LOC loc Loc
-syn keyword basicFunction LOF lof Lof LOG log Log
-syn keyword basicFunction LPOS lpos Lpos PEEK peek Peek
-syn keyword basicFunction PEN pen Pen POINT point Point
-syn keyword basicFunction POS pos Pos RND rnd Rnd
-syn keyword basicFunction SADD sadd Sadd SCREEN screen Screen
-syn keyword basicFunction SEEK seek Seek SETMEM setmem Setmem
-syn keyword basicFunction SGN sgn Sgn SIN sin Sin
-syn keyword basicFunction SPC spc Spc SQR sqr Sqr
-syn keyword basicFunction STICK stick Stick STRIG strig Strig
-syn keyword basicFunction TAB tab Tab TAN tan Tan
-syn keyword basicFunction UBOUND ubound Ubound VAL val Val
-syn keyword basicFunction VALPTR valptr Valptr VALSEG valseg Valseg
-syn keyword basicFunction VARPTR varptr Varptr VARSEG varseg Varseg
-syn keyword basicFunction CHR$ Chr$ chr$ COMMAND$ command$ Command$
-syn keyword basicFunction DATE$ date$ Date$ ENVIRON$ environ$ Environ$
-syn keyword basicFunction ERDEV$ erdev$ Erdev$ HEX$ hex$ Hex$
-syn keyword basicFunction INKEY$ inkey$ Inkey$ INPUT$ input$ Input$
-syn keyword basicFunction IOCTL$ ioctl$ Ioctl$ LCASES$ lcases$ Lcases$
-syn keyword basicFunction LAFT$ laft$ Laft$ LTRIM$ ltrim$ Ltrim$
-syn keyword basicFunction MID$ mid$ Mid$ MKDMBF$ mkdmbf$ Mkdmbf$
-syn keyword basicFunction MKD$ mkd$ Mkd$ MKI$ mki$ Mki$
-syn keyword basicFunction MKL$ mkl$ Mkl$ MKSMBF$ mksmbf$ Mksmbf$
-syn keyword basicFunction MKS$ mks$ Mks$ OCT$ oct$ Oct$
-syn keyword basicFunction RIGHT$ right$ Right$ RTRIM$ rtrim$ Rtrim$
-syn keyword basicFunction SPACE$ space$ Space$ STR$ str$ Str$
-syn keyword basicFunction STRING$ string$ String$ TIME$ time$ Time$
-syn keyword basicFunction UCASE$ ucase$ Ucase$ VARPTR$ varptr$ Varptr$
+let s:keywords =<< trim EOL " {{{2
+ absolute
+ access
+ alias
+ append
+ as
+ base
+ binary
+ byval
+ cdecl
+ com
+ def
+ do
+ for
+ function
+ gosub
+ goto
+ input
+ int86old
+ int86xold
+ interrupt
+ interruptx
+ is
+ key
+ len
+ list
+ local
+ lock
+ lprint
+ next
+ off
+ on
+ output
+ pen
+ play
+ random
+ read
+ resume
+ screen
+ seg
+ shared
+ signal
+ static
+ step
+ stop
+ strig
+ sub
+ timer
+ to
+ until
+ using
+ while
+ write
+EOL
+" }}}
+
+for k in s:keywords
+ exe 'syn match basicKeyword "\<' .. k .. '\>"'
+endfor
+
+" Functions {{{1
+syn keyword basicFunction abs asc atn cdbl chr$ cint clng command$ cos csng
+syn keyword basicFunction csrlin cvd cvdmbf cvi cvl cvs cvsmbf environ$ eof
+syn keyword basicFunction erdev erdev$ erl err exp fileattr fix fre freefile
+syn keyword basicFunction hex$ inkey$ inp input$ instr int ioctl$ left$ lbound
+syn keyword basicFunction lcase$ len loc lof log lpos ltrim$ mkd$ mkdmbf$ mki$
+syn keyword basicFunction mkl$ mks$ mksmbf$ oct$ peek pen point pos right$ rnd
+syn keyword basicFunction rtrim$ sadd setmem sgn sin space$ spc sqr stick str$
+syn keyword basicFunction strig string$ tab tan ubound ucase$ val valptr
+syn keyword basicFunction valseg varptr varptr$ varseg
+
+" Functions and statements (same name) {{{1
+syn match basicStatement "\<\%(date\$\|mid\$\|play\|screen\|seek\|time\$\|timer\)\>" contained
+syn match basicFunction "\<\%(date\$\|mid\$\|play\|screen\|seek\|time\$\|timer\)\>"
+
+" Types {{{1
+syn keyword basicType integer long single double string any
+
+" Strings {{{1
+
+" Unquoted DATA strings - anything except [:,] and leading or trailing whitespace
+" Needs lower priority than numbers
+syn match basicDataString "[^[:space:],:]\+\%(\s\+[^[:space:],:]\+\)*" contained
+
+syn region basicString start=+"+ end=+"+ oneline
+
+" Booleans {{{1
+if exists("basic_booleans")
+ syn keyword basicBoolean true false
+endif
" Numbers {{{1
-" Integer number, or floating point number without a dot.
-syn match basicNumber "\<\d\+\>"
-" Floating point number, with dot
-syn match basicNumber "\<\d\+\.\d*\>"
-" Floating point number, starting with a dot
-syn match basicNumber "\.\d\+\>"
-" String and Character constants {{{1
-syn match basicSpecial "\\\d\d\d\|\\." contained
-syn region basicString start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=basicSpecial
+" Integers
+syn match basicNumber "-\=&o\=\o\+[%&]\=\>"
+syn match basicNumber "-\=&h\x\+[%&]\=\>"
+syn match basicNumber "-\=\<\d\+[%&]\=\>"
+
+" Floats
+syn match basicFloat "-\=\<\d\+\.\=\d*\%(\%([ed][+-]\=\d*\)\|[!#]\)\=\>"
+syn match basicFloat "-\=\<\.\d\+\%(\%([ed][+-]\=\d*\)\|[!#]\)\=\>"
-" Line numbers {{{1
-syn region basicLineNumber start="^\d" end="\s"
+" Statement anchors {{{1
+syn match basicLineStart "^" nextgroup=@basicStatements,@basicLineIdentifier skipwhite
+syn match basicStatementSeparator ":" nextgroup=@basicStatements skipwhite
-" Data-type suffixes {{{1
-syn match basicTypeSpecifier "[a-zA-Z0-9][$%&!#]"ms=s+1
-" Used with OPEN statement
-syn match basicFilenumber "#\d\+"
+" Line numbers and labels {{{1
+
+" QuickBASIC limits these to 65,529 and 40 chars respectively
+syn match basicLineNumber "\d\+" nextgroup=@basicStatements skipwhite contained
+syn match basicLineLabel "\a[[:alnum:]]*\ze\s*:" nextgroup=@basicStatements skipwhite contained
+
+syn cluster basicLineIdentifier contains=basicLineNumber,basicLineLabel
+
+" Line Continuation {{{1
+syn match basicLineContinuation "\s*\zs_\ze\s*$"
+
+" Type suffixes {{{1
+if exists("basic_type_suffixes")
+ syn match basicTypeSuffix "\a[[:alnum:].]*\zs[$%&!#]"
+endif
-" Mathematical operators {{{1
-" syn match basicMathsOperator "[<>+\*^/\\=-]"
-syn match basicMathsOperator "-\|=\|[:<>+\*^/\\]\|AND\|OR"
+" File numbers {{{1
+syn match basicFilenumber "#\d\+"
+syn match basicFilenumber "#\a[[:alnum:].]*[%&!#]\="
+
+" Operators {{{1
+if exists("basic_operators")
+ syn match basicArithmeticOperator "[-+*/\\^]"
+ syn match basicRelationalOperator "<>\|<=\|>=\|[><=]"
+endif
+syn match basicLogicalOperator "\<\%(not\|and\|or\|xor\|eqv\|imp\)\>"
+syn match basicArithmeticOperator "\<mod\>"
+
+" Metacommands {{{1
+" Note: No trailing word boundaries. Text may be freely mixed however there
+" must be only leading whitespace prior to the first metacommand
+syn match basicMetacommand "$INCLUDE\s*:\s*'[^']\+'" contained containedin=@basicMetaComments
+syn match basicMetacommand "$\%(DYNAMIC\|STATIC\)" contained containedin=@basicMetaComments
" Comments {{{1
-syn keyword basicTodo TODO FIXME XXX NOTE contained
-syn region basicComment start="^\s*\zsREM\>" start="\%(:\s*\)\@<=REM\>" end="$" contains=basicTodo
-syn region basicComment start="'" end="$" contains=basicTodo
+syn keyword basicTodo TODO FIXME XXX NOTE contained
+
+syn region basicRemStatement matchgroup=basicStatement start="REM\>" end="$" contains=basicTodo,@Spell contained
+syn region basicComment start="'" end="$" contains=basicTodo,@Spell
+
+if !exists("basic_no_comment_fold")
+ syn region basicMultilineComment start="^\s*'.*\n\%(\s*'\)\@=" end="^\s*'.*\n\%(\s*'\)\@!" contains=@basicComments transparent fold keepend
+endif
+
+" Metacommands
+syn region basicMetaRemStatement matchgroup=basicStatement start="REM\>\s*\$\@=" end="$" contains=basicTodo contained
+syn region basicMetaComment start="'\s*\$\@=" end="$" contains=basicTodo
+
+syn cluster basicMetaComments contains=basicMetaComment,basicMetaRemStatement
+syn cluster basicComments contains=basicComment,basicMetaComment
"syn sync ccomment basicComment
" Default Highlighting {{{1
-hi def link basicLabel Label
-hi def link basicConditional Conditional
-hi def link basicRepeat Repeat
-hi def link basicLineNumber Comment
-hi def link basicNumber Number
-hi def link basicError Error
-hi def link basicStatement Statement
-hi def link basicString String
-hi def link basicComment Comment
-hi def link basicSpecial Special
-hi def link basicTodo Todo
-hi def link basicFunction Identifier
-hi def link basicTypeSpecifier Type
-hi def link basicFilenumber basicTypeSpecifier
-"hi basicMathsOperator term=bold cterm=bold gui=bold
+hi def link basicArithmeticOperator basicOperator
+hi def link basicBoolean Boolean
+hi def link basicComment Comment
+hi def link basicCommentError Error
+hi def link basicDataString basicString
+hi def link basicFilenumber basicTypeSuffix " TODO: better group
+hi def link basicFloat Float
+hi def link basicFunction Function
+hi def link basicKeyword Keyword
+hi def link basicLineIdentifier LineNr
+hi def link basicLineContinuation Special
+hi def link basicLineLabel basicLineIdentifier
+hi def link basicLineNumber basicLineIdentifier
+hi def link basicLogicalOperator basicOperator
+hi def link basicMetacommand SpecialComment
+hi def link basicMetaComment Comment
+hi def link basicMetaRemStatement Comment
+hi def link basicNumber Number
+hi def link basicOperator Operator
+hi def link basicPutAction Keyword
+hi def link basicRelationalOperator basicOperator
+hi def link basicRemStatement Comment
+hi def link basicSpaceError Error
+hi def link basicStatementSeparator Special
+hi def link basicStatement Statement
+hi def link basicString String
+hi def link basicTodo Todo
+hi def link basicType Type
+hi def link basicTypeSuffix Special
+if exists("basic_legacy_syntax_groups")
+ hi def link basicTypeSpecifier Type
+ hi def link basicTypeSuffix basicTypeSpecifier
+endif
" Postscript {{{1
let b:current_syntax = "basic"
diff --git a/runtime/syntax/c.vim b/runtime/syntax/c.vim
index e86e1b8669..890e9ae1a7 100644
--- a/runtime/syntax/c.vim
+++ b/runtime/syntax/c.vim
@@ -1,7 +1,7 @@
" Vim syntax file
" Language: C
" Maintainer: Bram Moolenaar <Bram@vim.org>
-" Last Change: 2021 Dec 07
+" Last Change: 2022 Apr 24
" Quit when a (custom) syntax file was already loaded
if exists("b:current_syntax")
@@ -11,7 +11,7 @@ endif
let s:cpo_save = &cpo
set cpo&vim
-let s:ft = matchstr(&ft, '^\([^.]\)\+')
+let s:ft = matchstr(&ft, '^\%([^.]\)\+')
" check if this was included from cpp.vim
let s:in_cpp_family = exists("b:filetype_in_cpp_family")
@@ -42,17 +42,17 @@ syn cluster cCommentGroup contains=cTodo,cBadContinuation
" String and Character constants
" Highlight special characters (those which have a backslash) differently
-syn match cSpecial display contained "\\\(x\x\+\|\o\{1,3}\|.\|$\)"
+syn match cSpecial display contained "\\\%(x\x\+\|\o\{1,3}\|.\|$\)"
if !exists("c_no_utf")
- syn match cSpecial display contained "\\\(u\x\{4}\|U\x\{8}\)"
+ syn match cSpecial display contained "\\\%(u\x\{4}\|U\x\{8}\)"
endif
if !exists("c_no_cformat")
" Highlight % items in strings.
if !exists("c_no_c99") " ISO C99
- syn match cFormat display "%\(\d\+\$\)\=[-+' #0*]*\(\d*\|\*\|\*\d\+\$\)\(\.\(\d*\|\*\|\*\d\+\$\)\)\=\([hlLjzt]\|ll\|hh\)\=\([aAbdiuoxXDOUfFeEgGcCsSpn]\|\[\^\=.[^]]*\]\)" contained
+ syn match cFormat display "%\%(\d\+\$\)\=[-+' #0*]*\%(\d*\|\*\|\*\d\+\$\)\%(\.\%(\d*\|\*\|\*\d\+\$\)\)\=\%([hlLjzt]\|ll\|hh\)\=\%([aAbdiuoxXDOUfFeEgGcCsSpn]\|\[\^\=.[^]]*\]\)" contained
else
- syn match cFormat display "%\(\d\+\$\)\=[-+' #0*]*\(\d*\|\*\|\*\d\+\$\)\(\.\(\d*\|\*\|\*\d\+\$\)\)\=\([hlL]\|ll\)\=\([bdiuoxXDOUfeEgGcCsSpn]\|\[\^\=.[^]]*\]\)" contained
+ syn match cFormat display "%\%(\d\+\$\)\=[-+' #0*]*\%(\d*\|\*\|\*\d\+\$\)\%(\.\%(\d*\|\*\|\*\d\+\$\)\)\=\%([hlL]\|ll\)\=\%([bdiuoxXDOUfeEgGcCsSpn]\|\[\^\=.[^]]*\]\)" contained
endif
syn match cFormat display "%%" contained
endif
@@ -60,8 +60,8 @@ endif
" cCppString: same as cString, but ends at end of line
if s:in_cpp_family && !exists("cpp_no_cpp11") && !exists("c_no_cformat")
" ISO C++11
- syn region cString start=+\(L\|u\|u8\|U\|R\|LR\|u8R\|uR\|UR\)\="+ skip=+\\\\\|\\"+ end=+"+ contains=cSpecial,cFormat,@Spell extend
- syn region cCppString start=+\(L\|u\|u8\|U\|R\|LR\|u8R\|uR\|UR\)\="+ skip=+\\\\\|\\"\|\\$+ excludenl end=+"+ end='$' contains=cSpecial,cFormat,@Spell
+ syn region cString start=+\%(L\|u\|u8\|U\|R\|LR\|u8R\|uR\|UR\)\="+ skip=+\\\\\|\\"+ end=+"+ contains=cSpecial,cFormat,@Spell extend
+ syn region cCppString start=+\%(L\|u\|u8\|U\|R\|LR\|u8R\|uR\|UR\)\="+ skip=+\\\\\|\\"\|\\$+ excludenl end=+"+ end='$' contains=cSpecial,cFormat,@Spell
elseif s:ft ==# "c" && !exists("c_no_c11") && !exists("c_no_cformat")
" ISO C99
syn region cString start=+\%(L\|U\|u8\)\="+ skip=+\\\\\|\\"+ end=+"+ contains=cSpecial,cFormat,@Spell extend
@@ -73,7 +73,7 @@ else
syn region cCppString start=+L\="+ skip=+\\\\\|\\"\|\\$+ excludenl end=+"+ end='$' contains=cSpecial,cFormat,@Spell
endif
-syn region cCppSkip contained start="^\s*\(%:\|#\)\s*\(if\>\|ifdef\>\|ifndef\>\)" skip="\\$" end="^\s*\(%:\|#\)\s*endif\>" contains=cSpaceError,cCppSkip
+syn region cCppSkip contained start="^\s*\%(%:\|#\)\s*\%(if\>\|ifdef\>\|ifndef\>\)" skip="\\$" end="^\s*\%(%:\|#\)\s*endif\>" contains=cSpaceError,cCppSkip
syn cluster cStringGroup contains=cCppString,cCppSkip
@@ -190,16 +190,16 @@ syn case ignore
syn match cNumbers display transparent "\<\d\|\.\d" contains=cNumber,cFloat,cOctalError,cOctal
" Same, but without octal error (for comments)
syn match cNumbersCom display contained transparent "\<\d\|\.\d" contains=cNumber,cFloat,cOctal
-syn match cNumber display contained "\d\+\(u\=l\{0,2}\|ll\=u\)\>"
+syn match cNumber display contained "\d\+\%(u\=l\{0,2}\|ll\=u\)\>"
"hex number
-syn match cNumber display contained "0x\x\+\(u\=l\{0,2}\|ll\=u\)\>"
+syn match cNumber display contained "0x\x\+\%(u\=l\{0,2}\|ll\=u\)\>"
" Flag the first zero of an octal number as something special
-syn match cOctal display contained "0\o\+\(u\=l\{0,2}\|ll\=u\)\>" contains=cOctalZero
+syn match cOctal display contained "0\o\+\%(u\=l\{0,2}\|ll\=u\)\>" contains=cOctalZero
syn match cOctalZero display contained "\<0"
"floating point number, with dot, optional exponent
-syn match cFloat display contained "\d\+\.\d*\(e[-+]\=\d\+\)\=[fl]\="
+syn match cFloat display contained "\d\+\.\d*\%(e[-+]\=\d\+\)\=[fl]\="
"floating point number, starting with a dot, optional exponent
-syn match cFloat display contained "\.\d\+\(e[-+]\=\d\+\)\=[fl]\=\>"
+syn match cFloat display contained "\.\d\+\%(e[-+]\=\d\+\)\=[fl]\=\>"
"floating point number, without dot, with exponent
syn match cFloat display contained "\d\+e[-+]\=\d\+[fl]\=\>"
if !exists("c_no_c99")
@@ -219,7 +219,7 @@ if exists("c_comment_strings")
" need to use a special type of cString: cCommentString, which also ends on
" "*/", and sees a "*" at the start of the line as comment again.
" Unfortunately this doesn't very well work for // type of comments :-(
- syn match cCommentSkip contained "^\s*\*\($\|\s\+\)"
+ syn match cCommentSkip contained "^\s*\*\%($\|\s\+\)"
syn region cCommentString contained start=+L\=\\\@<!"+ skip=+\\\\\|\\"+ end=+"+ end=+\*/+me=s-1 contains=cSpecial,cCommentSkip
syn region cComment2String contained start=+L\=\\\@<!"+ skip=+\\\\\|\\"+ end=+"+ end="$" contains=cSpecial
syn region cCommentL start="//" skip="\\$" end="$" keepend contains=@cCommentGroup,cComment2String,cCharacter,cNumbersCom,cSpaceError,cWrongComTail,@Spell
@@ -245,8 +245,14 @@ syn match cWrongComTail display "\*/"
syn keyword cOperator sizeof
if exists("c_gnu")
+ syn keyword cType __label__ __complex__
syn keyword cStatement __asm__
- syn keyword cOperator typeof __real__ __imag__
+ syn keyword cOperator __alignof__
+ syn keyword cOperator typeof __typeof__
+ syn keyword cOperator __real__ __imag__
+ syn keyword cStorageClass __attribute__ __const__ __extension__
+ syn keyword cStorageClass inline __inline__
+ syn keyword cStorageClass __restrict__ __volatile__ __noreturn__
endif
syn keyword cType int long short char void
syn keyword cType signed unsigned float double
@@ -270,16 +276,10 @@ if !exists("c_no_c99") " ISO C99
syn keyword cType intptr_t uintptr_t
syn keyword cType intmax_t uintmax_t
endif
-if exists("c_gnu")
- syn keyword cType __label__ __complex__ __volatile__
-endif
syn keyword cTypedef typedef
syn keyword cStructure struct union enum
syn keyword cStorageClass static register auto volatile extern const
-if exists("c_gnu")
- syn keyword cStorageClass inline __attribute__
-endif
if !exists("c_no_c99") && !s:in_cpp_family
syn keyword cStorageClass inline restrict
endif
@@ -292,6 +292,7 @@ if !exists("c_no_c11")
syn keyword cOperator _Static_assert static_assert
syn keyword cStorageClass _Thread_local thread_local
syn keyword cType char16_t char32_t
+ syn keyword cType max_align_t
" C11 atomics (take down the shield wall!)
syn keyword cType atomic_bool atomic_char atomic_schar atomic_uchar
syn keyword Ctype atomic_short atomic_ushort atomic_int atomic_uint
@@ -375,36 +376,36 @@ if !exists("c_no_c99") " ISO C99
endif
" Accept %: for # (C99)
-syn region cPreCondit start="^\s*\zs\(%:\|#\)\s*\(if\|ifdef\|ifndef\|elif\)\>" skip="\\$" end="$" keepend contains=cComment,cCommentL,cCppString,cCharacter,cCppParen,cParenError,cNumbers,cCommentError,cSpaceError
-syn match cPreConditMatch display "^\s*\zs\(%:\|#\)\s*\(else\|endif\)\>"
+syn region cPreCondit start="^\s*\zs\%(%:\|#\)\s*\%(if\|ifdef\|ifndef\|elif\)\>" skip="\\$" end="$" keepend contains=cComment,cCommentL,cCppString,cCharacter,cCppParen,cParenError,cNumbers,cCommentError,cSpaceError
+syn match cPreConditMatch display "^\s*\zs\%(%:\|#\)\s*\%(else\|endif\)\>"
if !exists("c_no_if0")
syn cluster cCppOutInGroup contains=cCppInIf,cCppInElse,cCppInElse2,cCppOutIf,cCppOutIf2,cCppOutElse,cCppInSkip,cCppOutSkip
- syn region cCppOutWrapper start="^\s*\zs\(%:\|#\)\s*if\s\+0\+\s*\($\|//\|/\*\|&\)" end=".\@=\|$" contains=cCppOutIf,cCppOutElse,@NoSpell fold
- syn region cCppOutIf contained start="0\+" matchgroup=cCppOutWrapper end="^\s*\(%:\|#\)\s*endif\>" contains=cCppOutIf2,cCppOutElse
+ syn region cCppOutWrapper start="^\s*\zs\%(%:\|#\)\s*if\s\+0\+\s*\%($\|//\|/\*\|&\)" end=".\@=\|$" contains=cCppOutIf,cCppOutElse,@NoSpell fold
+ syn region cCppOutIf contained start="0\+" matchgroup=cCppOutWrapper end="^\s*\%(%:\|#\)\s*endif\>" contains=cCppOutIf2,cCppOutElse
if !exists("c_no_if0_fold")
- syn region cCppOutIf2 contained matchgroup=cCppOutWrapper start="0\+" end="^\s*\(%:\|#\)\s*\(else\>\|elif\s\+\(0\+\s*\($\|//\|/\*\|&\)\)\@!\|endif\>\)"me=s-1 contains=cSpaceError,cCppOutSkip,@Spell fold
+ syn region cCppOutIf2 contained matchgroup=cCppOutWrapper start="0\+" end="^\s*\%(%:\|#\)\s*\%(else\>\|elif\s\+\%(0\+\s*\%($\|//\|/\*\|&\)\)\@!\|endif\>\)"me=s-1 contains=cSpaceError,cCppOutSkip,@Spell fold
else
- syn region cCppOutIf2 contained matchgroup=cCppOutWrapper start="0\+" end="^\s*\(%:\|#\)\s*\(else\>\|elif\s\+\(0\+\s*\($\|//\|/\*\|&\)\)\@!\|endif\>\)"me=s-1 contains=cSpaceError,cCppOutSkip,@Spell
+ syn region cCppOutIf2 contained matchgroup=cCppOutWrapper start="0\+" end="^\s*\%(%:\|#\)\s*\%(else\>\|elif\s\+\%(0\+\s*\%($\|//\|/\*\|&\)\)\@!\|endif\>\)"me=s-1 contains=cSpaceError,cCppOutSkip,@Spell
endif
- syn region cCppOutElse contained matchgroup=cCppOutWrapper start="^\s*\(%:\|#\)\s*\(else\|elif\)" end="^\s*\(%:\|#\)\s*endif\>"me=s-1 contains=TOP,cPreCondit
- syn region cCppInWrapper start="^\s*\zs\(%:\|#\)\s*if\s\+0*[1-9]\d*\s*\($\|//\|/\*\||\)" end=".\@=\|$" contains=cCppInIf,cCppInElse fold
- syn region cCppInIf contained matchgroup=cCppInWrapper start="\d\+" end="^\s*\(%:\|#\)\s*endif\>" contains=TOP,cPreCondit
+ syn region cCppOutElse contained matchgroup=cCppOutWrapper start="^\s*\%(%:\|#\)\s*\%(else\|elif\)" end="^\s*\%(%:\|#\)\s*endif\>"me=s-1 contains=TOP,cPreCondit
+ syn region cCppInWrapper start="^\s*\zs\%(%:\|#\)\s*if\s\+0*[1-9]\d*\s*\%($\|//\|/\*\||\)" end=".\@=\|$" contains=cCppInIf,cCppInElse fold
+ syn region cCppInIf contained matchgroup=cCppInWrapper start="\d\+" end="^\s*\%(%:\|#\)\s*endif\>" contains=TOP,cPreCondit
if !exists("c_no_if0_fold")
- syn region cCppInElse contained start="^\s*\(%:\|#\)\s*\(else\>\|elif\s\+\(0*[1-9]\d*\s*\($\|//\|/\*\||\)\)\@!\)" end=".\@=\|$" containedin=cCppInIf contains=cCppInElse2 fold
+ syn region cCppInElse contained start="^\s*\%(%:\|#\)\s*\%(else\>\|elif\s\+\%(0*[1-9]\d*\s*\%($\|//\|/\*\||\)\)\@!\)" end=".\@=\|$" containedin=cCppInIf contains=cCppInElse2 fold
else
- syn region cCppInElse contained start="^\s*\(%:\|#\)\s*\(else\>\|elif\s\+\(0*[1-9]\d*\s*\($\|//\|/\*\||\)\)\@!\)" end=".\@=\|$" containedin=cCppInIf contains=cCppInElse2
+ syn region cCppInElse contained start="^\s*\%(%:\|#\)\s*\%(else\>\|elif\s\+\%(0*[1-9]\d*\s*\%($\|//\|/\*\||\)\)\@!\)" end=".\@=\|$" containedin=cCppInIf contains=cCppInElse2
endif
- syn region cCppInElse2 contained matchgroup=cCppInWrapper start="^\s*\(%:\|#\)\s*\(else\|elif\)\([^/]\|/[^/*]\)*" end="^\s*\(%:\|#\)\s*endif\>"me=s-1 contains=cSpaceError,cCppOutSkip,@Spell
- syn region cCppOutSkip contained start="^\s*\(%:\|#\)\s*\(if\>\|ifdef\>\|ifndef\>\)" skip="\\$" end="^\s*\(%:\|#\)\s*endif\>" contains=cSpaceError,cCppOutSkip
- syn region cCppInSkip contained matchgroup=cCppInWrapper start="^\s*\(%:\|#\)\s*\(if\s\+\(\d\+\s*\($\|//\|/\*\||\|&\)\)\@!\|ifdef\>\|ifndef\>\)" skip="\\$" end="^\s*\(%:\|#\)\s*endif\>" containedin=cCppOutElse,cCppInIf,cCppInSkip contains=TOP,cPreProc
+ syn region cCppInElse2 contained matchgroup=cCppInWrapper start="^\s*\%(%:\|#\)\s*\%(else\|elif\)\%([^/]\|/[^/*]\)*" end="^\s*\%(%:\|#\)\s*endif\>"me=s-1 contains=cSpaceError,cCppOutSkip,@Spell
+ syn region cCppOutSkip contained start="^\s*\%(%:\|#\)\s*\%(if\>\|ifdef\>\|ifndef\>\)" skip="\\$" end="^\s*\%(%:\|#\)\s*endif\>" contains=cSpaceError,cCppOutSkip
+ syn region cCppInSkip contained matchgroup=cCppInWrapper start="^\s*\%(%:\|#\)\s*\%(if\s\+\%(\d\+\s*\%($\|//\|/\*\||\|&\)\)\@!\|ifdef\>\|ifndef\>\)" skip="\\$" end="^\s*\%(%:\|#\)\s*endif\>" containedin=cCppOutElse,cCppInIf,cCppInSkip contains=TOP,cPreProc
endif
syn region cIncluded display contained start=+"+ skip=+\\\\\|\\"+ end=+"+
syn match cIncluded display contained "<[^>]*>"
-syn match cInclude display "^\s*\zs\(%:\|#\)\s*include\>\s*["<]" contains=cIncluded
+syn match cInclude display "^\s*\zs\%(%:\|#\)\s*include\>\s*["<]" contains=cIncluded
"syn match cLineSkip "\\$"
syn cluster cPreProcGroup contains=cPreCondit,cIncluded,cInclude,cDefine,cErrInParen,cErrInBracket,cUserLabel,cSpecial,cOctalZero,cCppOutWrapper,cCppInWrapper,@cCppOutInGroup,cFormat,cNumber,cFloat,cOctal,cOctalError,cNumbersCom,cString,cCommentSkip,cCommentString,cComment2String,@cCommentGroup,cCommentStartError,cParen,cBracket,cMulti,cBadBlock
-syn region cDefine start="^\s*\zs\(%:\|#\)\s*\(define\|undef\)\>" skip="\\$" end="$" keepend contains=ALLBUT,@cPreProcGroup,@Spell
-syn region cPreProc start="^\s*\zs\(%:\|#\)\s*\(pragma\>\|line\>\|warning\>\|warn\>\|error\>\)" skip="\\$" end="$" keepend contains=ALLBUT,@cPreProcGroup,@Spell
+syn region cDefine start="^\s*\zs\%(%:\|#\)\s*\%(define\|undef\)\>" skip="\\$" end="$" keepend contains=ALLBUT,@cPreProcGroup,@Spell
+syn region cPreProc start="^\s*\zs\%(%:\|#\)\s*\%(pragma\>\|line\>\|warning\>\|warn\>\|error\>\)" skip="\\$" end="$" keepend contains=ALLBUT,@cPreProcGroup,@Spell
" Optional embedded Autodoc parsing
if exists("c_autodoc")
diff --git a/runtime/syntax/chordpro.vim b/runtime/syntax/chordpro.vim
index 5ccec26508..41a0a1e9d1 100644
--- a/runtime/syntax/chordpro.vim
+++ b/runtime/syntax/chordpro.vim
@@ -1,8 +1,7 @@
" Vim syntax file
-" Language: ChordPro (v. 3.6.2)
+" Language: ChordPro 6 (https://www.chordpro.org)
" Maintainer: Niels Bo Andersen <niels@niboan.dk>
-" Last Change: 2006 Apr 30
-" Remark: Requires VIM version 6.00 or greater
+" Last Change: 2022-04-15
" Quit when a syntax file was already loaded
if exists("b:current_syntax")
@@ -12,54 +11,161 @@ endif
let s:cpo_save = &cpo
set cpo&vim
-setlocal iskeyword+=-
-
syn case ignore
-syn keyword chordproDirective contained
- \ start_of_chorus soc end_of_chorus eoc new_song ns no_grid ng grid g
- \ new_page np new_physical_page npp start_of_tab sot end_of_tab eot
- \ column_break colb
-
-syn keyword chordproDirWithOpt contained
- \ comment c comment_italic ci comment_box cb title t subtitle st define
- \ textfont textsize chordfont chordsize columns col
-
-syn keyword chordproDefineKeyword contained base-fret frets
+" Include embedded abc syntax
+syn include @Abc syntax/abc.vim
+
+" Lilypond and Pango syntaxes could be embedded as well, but they are not
+" available in the distribution.
+
+" Directives without arguments
+syn keyword chordproDirective contained nextgroup=chordproConditional
+ \ new_song ns
+ \ start_of_chorus soc
+ \ chorus
+ \ start_of_verse sov
+ \ start_of_bridge sob
+ \ start_of_tab sot
+ \ start_of_grid sog
+ \ start_of_abc
+ \ start_of_ly
+ \ end_of_chorus eoc
+ \ end_of_verse eov
+ \ end_of_bridge eob
+ \ end_of_tab eot
+ \ end_of_grid eog
+ \ end_of_abc
+ \ end_of_ly
+ \ new_page np
+ \ new_physical_page npp
+ \ column_break cb
+ \ grid g
+ \ no_grid ng
+ \ transpose
+ \ chordfont cf chordsize cs chordcolour
+ \ footerfont footersize footercolour
+ \ gridfont gridsize gridcolour
+ \ tabfont tabsize tabcolour
+ \ tocfont tocsize toccolour
+ \ textfont tf textsize ts textcolour
+ \ titlefont titlesize titlecolour
+
+" Directives with arguments. Some directives are in both groups, as they can
+" be used both with and without arguments
+syn keyword chordproDirWithArg contained nextgroup=chordproConditional
+ \ title t
+ \ subtitle st
+ \ sorttitle
+ \ artist
+ \ composer
+ \ lyricist
+ \ arranger
+ \ copyright
+ \ album
+ \ year
+ \ key
+ \ time
+ \ tempo
+ \ duration
+ \ capo
+ \ comment c
+ \ highlight
+ \ comment_italic ci
+ \ comment_box cb
+ \ image
+ \ start_of_chorus soc
+ \ chorus
+ \ start_of_verse sov
+ \ start_of_bridge sob
+ \ start_of_tab sot
+ \ start_of_grid sog
+ \ start_of_abc
+ \ start_of_ly
+ \ define
+ \ chord
+ \ transpose
+ \ chordfont cf chordsize cs chordcolour
+ \ footerfont footersize footercolour
+ \ gridfont gridsize gridcolour
+ \ tabfont tabsize tabcolour
+ \ tocfont tocsize toccolour
+ \ textfont tf textsize ts textcolour
+ \ titlefont titlesize titlecolour
+ \ pagetype
+ \ titles
+ \ columns col
+
+syn keyword chordproMetaKeyword contained meta
+syn keyword chordproMetadata contained title sorttitle subtitle artist composer lyricist arranger copyright album year key time tempo duration capo
+syn keyword chordproStandardMetadata contained songindex page pages pagerange today tuning instrument user
+syn match chordproStandardMetadata /instrument\.type/ contained
+syn match chordproStandardMetadata /instrument\.description/ contained
+syn match chordproStandardMetadata /user\.name/ contained
+syn match chordproStandardMetadata /user\.fullname/ contained
+
+syn keyword chordproDefineKeyword contained frets fingers
+syn match chordproDefineKeyword /base-fret/ contained
+
+syn match chordproArgumentsNumber /\d\+/ contained
+
+syn match chordproCustom /x_\w\+/ contained
+
+syn match chordproDirMatch /{\w\+\(-\w\+\)\?}/ contains=chordproDirective contained transparent
+syn match chordproDirArgMatch /{\w\+\(-\w\+\)\?[: ]/ contains=chordproDirWithArg contained transparent
+syn match chordproMetaMatch /{meta\(-\w\+\)\?[: ]\+\w\+/ contains=chordproMetaKeyword,chordproMetadata contained transparent
+syn match chordproCustomMatch /{x_\w\+\(-\w\+\)\?[: ]/ contains=chordproCustom contained transparent
+
+syn match chordproConditional /-\w\+/ contained
+
+syn match chordproMetaDataOperator /[=|]/ contained
+syn match chordproMetaDataValue /%{\w*/ contains=chordproMetaData,chordproStandardMetadata contained transparent
+" Handles nested metadata tags, but the end of the containing chordproTag is
+" not highlighted correctly, if there are more than two levels of nesting
+syn region chordproMetaDataTag start=/%{\w*/ skip=/%{[^}]*}/ end=/}/ contains=chordproMetaDataValue,chordproMetaDataOperator,chordproMetadataTag contained
+
+syn region chordproArguments start=/{\w\+\(-\w\+\)\?[: ]/hs=e+1 skip=/%{[^}]*}/ end=/}/he=s-1 contains=chordproDirArgMatch,chordproArgumentsNumber,chordproMetaDataTag contained
+syn region chordproArguments start=/{\(define\|chord\)\(-\w\+\)\?[: ]/hs=e+1 end=/}/he=s-1 contains=chordproDirArgMatch,chordproDefineKeyword,chordproArgumentsNumber contained
+syn region chordproArguments start=/{meta\(-\w\+\)\?[: ]/hs=e+1 skip=/%{[^}]*}/ end=/}/he=s-1 contains=chordproMetaMatch,chordproMetaDataTag contained
+syn region chordproArguments start=/{x_\w\+\(-\w\+\)\?[: ]/hs=e+1 end=/}/he=s-1 contains=chordproCustomMatch contained
+
+syn region chordproTag start=/{/ skip=/%{[^}]*}/ end=/}/ contains=chordproDirMatch,chordproArguments oneline
-syn match chordproDirMatch /{\w*}/ contains=chordproDirective contained transparent
-syn match chordproDirOptMatch /{\w*:/ contains=chordproDirWithOpt contained transparent
+syn region chordproChord matchgroup=chordproBracket start=/\[/ end=/]/ oneline
-" Workaround for a bug in VIM 6, which causes incorrect coloring of the first {
-if version < 700
- syn region chordproOptions start=/{\w*:/ end=/}/ contains=chordproDirOptMatch contained transparent
- syn region chordproOptions start=/{define:/ end=/}/ contains=chordproDirOptMatch, chordproDefineKeyword contained transparent
-else
- syn region chordproOptions start=/{\w*:/hs=e+1 end=/}/he=s-1 contains=chordproDirOptMatch contained
- syn region chordproOptions start=/{define:/hs=e+1 end=/}/he=s-1 contains=chordproDirOptMatch, chordproDefineKeyword contained
-endif
+syn region chordproAnnotation matchgroup=chordproBracket start=/\[\*/ end=/]/ oneline
-syn region chordproTag start=/{/ end=/}/ contains=chordproDirMatch,chordproOptions oneline
+syn region chordproTab start=/{start_of_tab\(-\w\+\)\?\([: ].\+\)\?}\|{sot\(-\w\+\)\?\([: ].\+\)\?}/hs=e+1 end=/{end_of_tab}\|{eot}/me=s-1 contains=chordproTag,chordproComment keepend
-syn region chordproChord matchgroup=chordproBracket start=/\[/ end=/]/ oneline
+syn region chordproChorus start=/{start_of_chorus\(-\w\+\)\?\([: ].\+\)\?}\|{soc\(-\w\+\)\?\([: ].\+\)\?}/hs=e+1 end=/{end_of_chorus}\|{eoc}/me=s-1 contains=chordproTag,chordproChord,chordproAnnotation,chordproComment keepend
-syn region chordproTab start=/{start_of_tab}\|{sot}/hs=e+1 end=/{end_of_tab}\|{eot}/he=s-1 contains=chordproTag,chordproComment keepend
+syn region chordproBridge start=/{start_of_bridge\(-\w\+\)\?\([: ].\+\)\?}\|{sob\(-\w\+\)\?\([: ].\+\)\?}/hs=e+1 end=/{end_of_bridge}\|{eob}/me=s-1 contains=chordproTag,chordproChord,chordproAnnotation,chordproComment keepend
-syn region chordproChorus start=/{start_of_chorus}\|{soc}/hs=e+1 end=/{end_of_chorus}\|{eoc}/he=s-1 contains=chordproTag,chordproChord,chordproComment keepend
+syn region chordproAbc start=/{start_of_abc\(-\w\+\)\?\([: ].\+\)\?}/hs=e+1 end=/{end_of_abc}/me=s-1 contains=chordproTag,@Abc keepend
syn match chordproComment /^#.*/
" Define the default highlighting.
hi def link chordproDirective Statement
-hi def link chordproDirWithOpt Statement
-hi def link chordproOptions Special
+hi def link chordproDirWithArg Statement
+hi def link chordproConditional Statement
+hi def link chordproCustom Statement
+hi def link chordproMetaKeyword Statement
+hi def link chordproMetaDataOperator Operator
+hi def link chordproMetaDataTag Function
+hi def link chordproArguments Special
+hi def link chordproArgumentsNumber Number
hi def link chordproChord Type
+hi def link chordproAnnotation Identifier
hi def link chordproTag Constant
hi def link chordproTab PreProc
hi def link chordproComment Comment
hi def link chordproBracket Constant
-hi def link chordproDefineKeyword Type
+hi def link chordproDefineKeyword Identifier
+hi def link chordproMetadata Identifier
+hi def link chordproStandardMetadata Identifier
hi def chordproChorus term=bold cterm=bold gui=bold
+hi def chordproBridge term=italic cterm=italic gui=italic
let b:current_syntax = "chordpro"
diff --git a/runtime/syntax/clojure.vim b/runtime/syntax/clojure.vim
index 9782dc41ad..0d63728250 100644
--- a/runtime/syntax/clojure.vim
+++ b/runtime/syntax/clojure.vim
@@ -7,7 +7,7 @@
" Contributors: Joel Holdbrooks <cjholdbrooks@gmail.com> (Regexp support, bug fixes)
" URL: https://github.com/clojure-vim/clojure.vim
" License: Vim (see :h license)
-" Last Change: 2021-10-26
+" Last Change: 2022-03-24
if exists("b:current_syntax")
finish
@@ -21,20 +21,20 @@ if has("folding") && exists("g:clojure_fold") && g:clojure_fold > 0
endif
" -*- KEYWORDS -*-
-" Generated from https://github.com/clojure-vim/clojure.vim/blob/62b215f079ce0f3834fd295c7a7f6bd8cc54bcc3/clj/src/vim_clojure_static/generate.clj
-" Clojure version 1.10.3
+" Generated from https://github.com/clojure-vim/clojure.vim/blob/fd280e33e84c88e97860930557dba3ff80b1a82d/clj/src/vim_clojure_static/generate.clj
+" Clojure version 1.11.0
let s:clojure_syntax_keywords = {
- \ 'clojureBoolean': ["false","true"]
- \ , 'clojureCond': ["case","clojure.core/case","clojure.core/cond","clojure.core/cond->","clojure.core/cond->>","clojure.core/condp","clojure.core/if-let","clojure.core/if-not","clojure.core/if-some","clojure.core/when","clojure.core/when-first","clojure.core/when-let","clojure.core/when-not","clojure.core/when-some","cond","cond->","cond->>","condp","if-let","if-not","if-some","when","when-first","when-let","when-not","when-some"]
- \ , 'clojureConstant': ["nil"]
- \ , 'clojureDefine': ["clojure.core/definline","clojure.core/definterface","clojure.core/defmacro","clojure.core/defmethod","clojure.core/defmulti","clojure.core/defn","clojure.core/defn-","clojure.core/defonce","clojure.core/defprotocol","clojure.core/defrecord","clojure.core/defstruct","clojure.core/deftype","definline","definterface","defmacro","defmethod","defmulti","defn","defn-","defonce","defprotocol","defrecord","defstruct","deftype"]
- \ , 'clojureException': ["catch","finally","throw","try"]
- \ , 'clojureFunc': ["*","*'","+","+'","-","-'","->ArrayChunk","->Eduction","->Vec","->VecNode","->VecSeq","-cache-protocol-fn","-reset-methods","/","<","<=","=","==",">",">=","PrintWriter-on","StackTraceElement->vec","Throwable->map","accessor","aclone","add-classpath","add-tap","add-watch","agent","agent-error","agent-errors","aget","alength","alias","all-ns","alter","alter-meta!","alter-var-root","ancestors","any?","apply","array-map","aset","aset-boolean","aset-byte","aset-char","aset-double","aset-float","aset-int","aset-long","aset-short","assoc","assoc!","assoc-in","associative?","atom","await","await-for","await1","bases","bean","bigdec","bigint","biginteger","bit-and","bit-and-not","bit-clear","bit-flip","bit-not","bit-or","bit-set","bit-shift-left","bit-shift-right","bit-test","bit-xor","boolean","boolean-array","boolean?","booleans","bound-fn*","bound?","bounded-count","butlast","byte","byte-array","bytes","bytes?","cast","cat","char","char-array","char?","chars","chunk","chunk-append","chunk-buffer","chunk-cons","chunk-first","chunk-next","chunk-rest","chunked-seq?","class","class?","clear-agent-errors","clojure-version","clojure.core/*","clojure.core/*'","clojure.core/+","clojure.core/+'","clojure.core/-","clojure.core/-'","clojure.core/->ArrayChunk","clojure.core/->Eduction","clojure.core/->Vec","clojure.core/->VecNode","clojure.core/->VecSeq","clojure.core/-cache-protocol-fn","clojure.core/-reset-methods","clojure.core//","clojure.core/<","clojure.core/<=","clojure.core/=","clojure.core/==","clojure.core/>","clojure.core/>=","clojure.core/PrintWriter-on","clojure.core/StackTraceElement->vec","clojure.core/Throwable->map","clojure.core/accessor","clojure.core/aclone","clojure.core/add-classpath","clojure.core/add-tap","clojure.core/add-watch","clojure.core/agent","clojure.core/agent-error","clojure.core/agent-errors","clojure.core/aget","clojure.core/alength","clojure.core/alias","clojure.core/all-ns","clojure.core/alter","clojure.core/alter-meta!","clojure.core/alter-var-root","clojure.core/ancestors","clojure.core/any?","clojure.core/apply","clojure.core/array-map","clojure.core/aset","clojure.core/aset-boolean","clojure.core/aset-byte","clojure.core/aset-char","clojure.core/aset-double","clojure.core/aset-float","clojure.core/aset-int","clojure.core/aset-long","clojure.core/aset-short","clojure.core/assoc","clojure.core/assoc!","clojure.core/assoc-in","clojure.core/associative?","clojure.core/atom","clojure.core/await","clojure.core/await-for","clojure.core/await1","clojure.core/bases","clojure.core/bean","clojure.core/bigdec","clojure.core/bigint","clojure.core/biginteger","clojure.core/bit-and","clojure.core/bit-and-not","clojure.core/bit-clear","clojure.core/bit-flip","clojure.core/bit-not","clojure.core/bit-or","clojure.core/bit-set","clojure.core/bit-shift-left","clojure.core/bit-shift-right","clojure.core/bit-test","clojure.core/bit-xor","clojure.core/boolean","clojure.core/boolean-array","clojure.core/boolean?","clojure.core/booleans","clojure.core/bound-fn*","clojure.core/bound?","clojure.core/bounded-count","clojure.core/butlast","clojure.core/byte","clojure.core/byte-array","clojure.core/bytes","clojure.core/bytes?","clojure.core/cast","clojure.core/cat","clojure.core/char","clojure.core/char-array","clojure.core/char?","clojure.core/chars","clojure.core/chunk","clojure.core/chunk-append","clojure.core/chunk-buffer","clojure.core/chunk-cons","clojure.core/chunk-first","clojure.core/chunk-next","clojure.core/chunk-rest","clojure.core/chunked-seq?","clojure.core/class","clojure.core/class?","clojure.core/clear-agent-errors","clojure.core/clojure-version","clojure.core/coll?","clojure.core/commute","clojure.core/comp","clojure.core/comparator","clojure.core/compare","clojure.core/compare-and-set!","clojure.core/compile","clojure.core/complement","clojure.core/completing","clojure.core/concat","clojure.core/conj","clojure.core/conj!","clojure.core/cons","clojure.core/constantly","clojure.core/construct-proxy","clojure.core/contains?","clojure.core/count","clojure.core/counted?","clojure.core/create-ns","clojure.core/create-struct","clojure.core/cycle","clojure.core/dec","clojure.core/dec'","clojure.core/decimal?","clojure.core/dedupe","clojure.core/delay?","clojure.core/deliver","clojure.core/denominator","clojure.core/deref","clojure.core/derive","clojure.core/descendants","clojure.core/destructure","clojure.core/disj","clojure.core/disj!","clojure.core/dissoc","clojure.core/dissoc!","clojure.core/distinct","clojure.core/distinct?","clojure.core/doall","clojure.core/dorun","clojure.core/double","clojure.core/double-array","clojure.core/double?","clojure.core/doubles","clojure.core/drop","clojure.core/drop-last","clojure.core/drop-while","clojure.core/eduction","clojure.core/empty","clojure.core/empty?","clojure.core/ensure","clojure.core/ensure-reduced","clojure.core/enumeration-seq","clojure.core/error-handler","clojure.core/error-mode","clojure.core/eval","clojure.core/even?","clojure.core/every-pred","clojure.core/every?","clojure.core/ex-cause","clojure.core/ex-data","clojure.core/ex-info","clojure.core/ex-message","clojure.core/extend","clojure.core/extenders","clojure.core/extends?","clojure.core/false?","clojure.core/ffirst","clojure.core/file-seq","clojure.core/filter","clojure.core/filterv","clojure.core/find","clojure.core/find-keyword","clojure.core/find-ns","clojure.core/find-protocol-impl","clojure.core/find-protocol-method","clojure.core/find-var","clojure.core/first","clojure.core/flatten","clojure.core/float","clojure.core/float-array","clojure.core/float?","clojure.core/floats","clojure.core/flush","clojure.core/fn?","clojure.core/fnext","clojure.core/fnil","clojure.core/force","clojure.core/format","clojure.core/frequencies","clojure.core/future-call","clojure.core/future-cancel","clojure.core/future-cancelled?","clojure.core/future-done?","clojure.core/future?","clojure.core/gensym","clojure.core/get","clojure.core/get-in","clojure.core/get-method","clojure.core/get-proxy-class","clojure.core/get-thread-bindings","clojure.core/get-validator","clojure.core/group-by","clojure.core/halt-when","clojure.core/hash","clojure.core/hash-combine","clojure.core/hash-map","clojure.core/hash-ordered-coll","clojure.core/hash-set","clojure.core/hash-unordered-coll","clojure.core/ident?","clojure.core/identical?","clojure.core/identity","clojure.core/ifn?","clojure.core/in-ns","clojure.core/inc","clojure.core/inc'","clojure.core/indexed?","clojure.core/init-proxy","clojure.core/inst-ms","clojure.core/inst-ms*","clojure.core/inst?","clojure.core/instance?","clojure.core/int","clojure.core/int-array","clojure.core/int?","clojure.core/integer?","clojure.core/interleave","clojure.core/intern","clojure.core/interpose","clojure.core/into","clojure.core/into-array","clojure.core/ints","clojure.core/isa?","clojure.core/iterate","clojure.core/iterator-seq","clojure.core/juxt","clojure.core/keep","clojure.core/keep-indexed","clojure.core/key","clojure.core/keys","clojure.core/keyword","clojure.core/keyword?","clojure.core/last","clojure.core/line-seq","clojure.core/list","clojure.core/list*","clojure.core/list?","clojure.core/load","clojure.core/load-file","clojure.core/load-reader","clojure.core/load-string","clojure.core/loaded-libs","clojure.core/long","clojure.core/long-array","clojure.core/longs","clojure.core/macroexpand","clojure.core/macroexpand-1","clojure.core/make-array","clojure.core/make-hierarchy","clojure.core/map","clojure.core/map-entry?","clojure.core/map-indexed","clojure.core/map?","clojure.core/mapcat","clojure.core/mapv","clojure.core/max","clojure.core/max-key","clojure.core/memoize","clojure.core/merge","clojure.core/merge-with","clojure.core/meta","clojure.core/method-sig","clojure.core/methods","clojure.core/min","clojure.core/min-key","clojure.core/mix-collection-hash","clojure.core/mod","clojure.core/munge","clojure.core/name","clojure.core/namespace","clojure.core/namespace-munge","clojure.core/nat-int?","clojure.core/neg-int?","clojure.core/neg?","clojure.core/newline","clojure.core/next","clojure.core/nfirst","clojure.core/nil?","clojure.core/nnext","clojure.core/not","clojure.core/not-any?","clojure.core/not-empty","clojure.core/not-every?","clojure.core/not=","clojure.core/ns-aliases","clojure.core/ns-imports","clojure.core/ns-interns","clojure.core/ns-map","clojure.core/ns-name","clojure.core/ns-publics","clojure.core/ns-refers","clojure.core/ns-resolve","clojure.core/ns-unalias","clojure.core/ns-unmap","clojure.core/nth","clojure.core/nthnext","clojure.core/nthrest","clojure.core/num","clojure.core/number?","clojure.core/numerator","clojure.core/object-array","clojure.core/odd?","clojure.core/parents","clojure.core/partial","clojure.core/partition","clojure.core/partition-all","clojure.core/partition-by","clojure.core/pcalls","clojure.core/peek","clojure.core/persistent!","clojure.core/pmap","clojure.core/pop","clojure.core/pop!","clojure.core/pop-thread-bindings","clojure.core/pos-int?","clojure.core/pos?","clojure.core/pr","clojure.core/pr-str","clojure.core/prefer-method","clojure.core/prefers","clojure.core/print","clojure.core/print-ctor","clojure.core/print-dup","clojure.core/print-method","clojure.core/print-simple","clojure.core/print-str","clojure.core/printf","clojure.core/println","clojure.core/println-str","clojure.core/prn","clojure.core/prn-str","clojure.core/promise","clojure.core/proxy-call-with-super","clojure.core/proxy-mappings","clojure.core/proxy-name","clojure.core/push-thread-bindings","clojure.core/qualified-ident?","clojure.core/qualified-keyword?","clojure.core/qualified-symbol?","clojure.core/quot","clojure.core/rand","clojure.core/rand-int","clojure.core/rand-nth","clojure.core/random-sample","clojure.core/range","clojure.core/ratio?","clojure.core/rational?","clojure.core/rationalize","clojure.core/re-find","clojure.core/re-groups","clojure.core/re-matcher","clojure.core/re-matches","clojure.core/re-pattern","clojure.core/re-seq","clojure.core/read","clojure.core/read+string","clojure.core/read-line","clojure.core/read-string","clojure.core/reader-conditional","clojure.core/reader-conditional?","clojure.core/realized?","clojure.core/record?","clojure.core/reduce","clojure.core/reduce-kv","clojure.core/reduced","clojure.core/reduced?","clojure.core/reductions","clojure.core/ref","clojure.core/ref-history-count","clojure.core/ref-max-history","clojure.core/ref-min-history","clojure.core/ref-set","clojure.core/refer","clojure.core/release-pending-sends","clojure.core/rem","clojure.core/remove","clojure.core/remove-all-methods","clojure.core/remove-method","clojure.core/remove-ns","clojure.core/remove-tap","clojure.core/remove-watch","clojure.core/repeat","clojure.core/repeatedly","clojure.core/replace","clojure.core/replicate","clojure.core/require","clojure.core/requiring-resolve","clojure.core/reset!","clojure.core/reset-meta!","clojure.core/reset-vals!","clojure.core/resolve","clojure.core/rest","clojure.core/restart-agent","clojure.core/resultset-seq","clojure.core/reverse","clojure.core/reversible?","clojure.core/rseq","clojure.core/rsubseq","clojure.core/run!","clojure.core/satisfies?","clojure.core/second","clojure.core/select-keys","clojure.core/send","clojure.core/send-off","clojure.core/send-via","clojure.core/seq","clojure.core/seq?","clojure.core/seqable?","clojure.core/seque","clojure.core/sequence","clojure.core/sequential?","clojure.core/set","clojure.core/set-agent-send-executor!","clojure.core/set-agent-send-off-executor!","clojure.core/set-error-handler!","clojure.core/set-error-mode!","clojure.core/set-validator!","clojure.core/set?","clojure.core/short","clojure.core/short-array","clojure.core/shorts","clojure.core/shuffle","clojure.core/shutdown-agents","clojure.core/simple-ident?","clojure.core/simple-keyword?","clojure.core/simple-symbol?","clojure.core/slurp","clojure.core/some","clojure.core/some-fn","clojure.core/some?","clojure.core/sort","clojure.core/sort-by","clojure.core/sorted-map","clojure.core/sorted-map-by","clojure.core/sorted-set","clojure.core/sorted-set-by","clojure.core/sorted?","clojure.core/special-symbol?","clojure.core/spit","clojure.core/split-at","clojure.core/split-with","clojure.core/str","clojure.core/string?","clojure.core/struct","clojure.core/struct-map","clojure.core/subs","clojure.core/subseq","clojure.core/subvec","clojure.core/supers","clojure.core/swap!","clojure.core/swap-vals!","clojure.core/symbol","clojure.core/symbol?","clojure.core/tagged-literal","clojure.core/tagged-literal?","clojure.core/take","clojure.core/take-last","clojure.core/take-nth","clojure.core/take-while","clojure.core/tap>","clojure.core/test","clojure.core/the-ns","clojure.core/thread-bound?","clojure.core/to-array","clojure.core/to-array-2d","clojure.core/trampoline","clojure.core/transduce","clojure.core/transient","clojure.core/tree-seq","clojure.core/true?","clojure.core/type","clojure.core/unchecked-add","clojure.core/unchecked-add-int","clojure.core/unchecked-byte","clojure.core/unchecked-char","clojure.core/unchecked-dec","clojure.core/unchecked-dec-int","clojure.core/unchecked-divide-int","clojure.core/unchecked-double","clojure.core/unchecked-float","clojure.core/unchecked-inc","clojure.core/unchecked-inc-int","clojure.core/unchecked-int","clojure.core/unchecked-long","clojure.core/unchecked-multiply","clojure.core/unchecked-multiply-int","clojure.core/unchecked-negate","clojure.core/unchecked-negate-int","clojure.core/unchecked-remainder-int","clojure.core/unchecked-short","clojure.core/unchecked-subtract","clojure.core/unchecked-subtract-int","clojure.core/underive","clojure.core/unreduced","clojure.core/unsigned-bit-shift-right","clojure.core/update","clojure.core/update-in","clojure.core/update-proxy","clojure.core/uri?","clojure.core/use","clojure.core/uuid?","clojure.core/val","clojure.core/vals","clojure.core/var-get","clojure.core/var-set","clojure.core/var?","clojure.core/vary-meta","clojure.core/vec","clojure.core/vector","clojure.core/vector-of","clojure.core/vector?","clojure.core/volatile!","clojure.core/volatile?","clojure.core/vreset!","clojure.core/with-bindings*","clojure.core/with-meta","clojure.core/with-redefs-fn","clojure.core/xml-seq","clojure.core/zero?","clojure.core/zipmap","coll?","commute","comp","comparator","compare","compare-and-set!","compile","complement","completing","concat","conj","conj!","cons","constantly","construct-proxy","contains?","count","counted?","create-ns","create-struct","cycle","dec","dec'","decimal?","dedupe","delay?","deliver","denominator","deref","derive","descendants","destructure","disj","disj!","dissoc","dissoc!","distinct","distinct?","doall","dorun","double","double-array","double?","doubles","drop","drop-last","drop-while","eduction","empty","empty?","ensure","ensure-reduced","enumeration-seq","error-handler","error-mode","eval","even?","every-pred","every?","ex-cause","ex-data","ex-info","ex-message","extend","extenders","extends?","false?","ffirst","file-seq","filter","filterv","find","find-keyword","find-ns","find-protocol-impl","find-protocol-method","find-var","first","flatten","float","float-array","float?","floats","flush","fn?","fnext","fnil","force","format","frequencies","future-call","future-cancel","future-cancelled?","future-done?","future?","gensym","get","get-in","get-method","get-proxy-class","get-thread-bindings","get-validator","group-by","halt-when","hash","hash-combine","hash-map","hash-ordered-coll","hash-set","hash-unordered-coll","ident?","identical?","identity","ifn?","in-ns","inc","inc'","indexed?","init-proxy","inst-ms","inst-ms*","inst?","instance?","int","int-array","int?","integer?","interleave","intern","interpose","into","into-array","ints","isa?","iterate","iterator-seq","juxt","keep","keep-indexed","key","keys","keyword","keyword?","last","line-seq","list","list*","list?","load","load-file","load-reader","load-string","loaded-libs","long","long-array","longs","macroexpand","macroexpand-1","make-array","make-hierarchy","map","map-entry?","map-indexed","map?","mapcat","mapv","max","max-key","memoize","merge","merge-with","meta","method-sig","methods","min","min-key","mix-collection-hash","mod","munge","name","namespace","namespace-munge","nat-int?","neg-int?","neg?","newline","next","nfirst","nil?","nnext","not","not-any?","not-empty","not-every?","not=","ns-aliases","ns-imports","ns-interns","ns-map","ns-name","ns-publics","ns-refers","ns-resolve","ns-unalias","ns-unmap","nth","nthnext","nthrest","num","number?","numerator","object-array","odd?","parents","partial","partition","partition-all","partition-by","pcalls","peek","persistent!","pmap","pop","pop!","pop-thread-bindings","pos-int?","pos?","pr","pr-str","prefer-method","prefers","print","print-ctor","print-dup","print-method","print-simple","print-str","printf","println","println-str","prn","prn-str","promise","proxy-call-with-super","proxy-mappings","proxy-name","push-thread-bindings","qualified-ident?","qualified-keyword?","qualified-symbol?","quot","rand","rand-int","rand-nth","random-sample","range","ratio?","rational?","rationalize","re-find","re-groups","re-matcher","re-matches","re-pattern","re-seq","read","read+string","read-line","read-string","reader-conditional","reader-conditional?","realized?","record?","reduce","reduce-kv","reduced","reduced?","reductions","ref","ref-history-count","ref-max-history","ref-min-history","ref-set","refer","release-pending-sends","rem","remove","remove-all-methods","remove-method","remove-ns","remove-tap","remove-watch","repeat","repeatedly","replace","replicate","require","requiring-resolve","reset!","reset-meta!","reset-vals!","resolve","rest","restart-agent","resultset-seq","reverse","reversible?","rseq","rsubseq","run!","satisfies?","second","select-keys","send","send-off","send-via","seq","seq?","seqable?","seque","sequence","sequential?","set","set-agent-send-executor!","set-agent-send-off-executor!","set-error-handler!","set-error-mode!","set-validator!","set?","short","short-array","shorts","shuffle","shutdown-agents","simple-ident?","simple-keyword?","simple-symbol?","slurp","some","some-fn","some?","sort","sort-by","sorted-map","sorted-map-by","sorted-set","sorted-set-by","sorted?","special-symbol?","spit","split-at","split-with","str","string?","struct","struct-map","subs","subseq","subvec","supers","swap!","swap-vals!","symbol","symbol?","tagged-literal","tagged-literal?","take","take-last","take-nth","take-while","tap>","test","the-ns","thread-bound?","to-array","to-array-2d","trampoline","transduce","transient","tree-seq","true?","type","unchecked-add","unchecked-add-int","unchecked-byte","unchecked-char","unchecked-dec","unchecked-dec-int","unchecked-divide-int","unchecked-double","unchecked-float","unchecked-inc","unchecked-inc-int","unchecked-int","unchecked-long","unchecked-multiply","unchecked-multiply-int","unchecked-negate","unchecked-negate-int","unchecked-remainder-int","unchecked-short","unchecked-subtract","unchecked-subtract-int","underive","unreduced","unsigned-bit-shift-right","update","update-in","update-proxy","uri?","use","uuid?","val","vals","var-get","var-set","var?","vary-meta","vec","vector","vector-of","vector?","volatile!","volatile?","vreset!","with-bindings*","with-meta","with-redefs-fn","xml-seq","zero?","zipmap"]
- \ , 'clojureMacro': ["->","->>","..","amap","and","areduce","as->","assert","binding","bound-fn","clojure.core/->","clojure.core/->>","clojure.core/..","clojure.core/amap","clojure.core/and","clojure.core/areduce","clojure.core/as->","clojure.core/assert","clojure.core/binding","clojure.core/bound-fn","clojure.core/comment","clojure.core/declare","clojure.core/delay","clojure.core/dosync","clojure.core/doto","clojure.core/extend-protocol","clojure.core/extend-type","clojure.core/for","clojure.core/future","clojure.core/gen-class","clojure.core/gen-interface","clojure.core/import","clojure.core/io!","clojure.core/lazy-cat","clojure.core/lazy-seq","clojure.core/letfn","clojure.core/locking","clojure.core/memfn","clojure.core/ns","clojure.core/or","clojure.core/proxy","clojure.core/proxy-super","clojure.core/pvalues","clojure.core/refer-clojure","clojure.core/reify","clojure.core/some->","clojure.core/some->>","clojure.core/sync","clojure.core/time","clojure.core/vswap!","clojure.core/with-bindings","clojure.core/with-in-str","clojure.core/with-loading-context","clojure.core/with-local-vars","clojure.core/with-open","clojure.core/with-out-str","clojure.core/with-precision","clojure.core/with-redefs","comment","declare","delay","dosync","doto","extend-protocol","extend-type","for","future","gen-class","gen-interface","import","io!","lazy-cat","lazy-seq","letfn","locking","memfn","ns","or","proxy","proxy-super","pvalues","refer-clojure","reify","some->","some->>","sync","time","vswap!","with-bindings","with-in-str","with-loading-context","with-local-vars","with-open","with-out-str","with-precision","with-redefs"]
- \ , 'clojureRepeat': ["clojure.core/doseq","clojure.core/dotimes","clojure.core/while","doseq","dotimes","while"]
- \ , 'clojureSpecial': [".","clojure.core/fn","clojure.core/let","clojure.core/loop","def","do","fn","if","let","loop","monitor-enter","monitor-exit","new","quote","recur","set!","var"]
- \ , 'clojureVariable': ["*1","*2","*3","*agent*","*allow-unresolved-vars*","*assert*","*clojure-version*","*command-line-args*","*compile-files*","*compile-path*","*compiler-options*","*data-readers*","*default-data-reader-fn*","*e","*err*","*file*","*flush-on-newline*","*fn-loader*","*in*","*math-context*","*ns*","*out*","*print-dup*","*print-length*","*print-level*","*print-meta*","*print-namespace-maps*","*print-readably*","*read-eval*","*reader-resolver*","*source-path*","*suppress-read*","*unchecked-math*","*use-context-classloader*","*verbose-defrecords*","*warn-on-reflection*","EMPTY-NODE","Inst","char-escape-string","char-name-string","clojure.core/*1","clojure.core/*2","clojure.core/*3","clojure.core/*agent*","clojure.core/*allow-unresolved-vars*","clojure.core/*assert*","clojure.core/*clojure-version*","clojure.core/*command-line-args*","clojure.core/*compile-files*","clojure.core/*compile-path*","clojure.core/*compiler-options*","clojure.core/*data-readers*","clojure.core/*default-data-reader-fn*","clojure.core/*e","clojure.core/*err*","clojure.core/*file*","clojure.core/*flush-on-newline*","clojure.core/*fn-loader*","clojure.core/*in*","clojure.core/*math-context*","clojure.core/*ns*","clojure.core/*out*","clojure.core/*print-dup*","clojure.core/*print-length*","clojure.core/*print-level*","clojure.core/*print-meta*","clojure.core/*print-namespace-maps*","clojure.core/*print-readably*","clojure.core/*read-eval*","clojure.core/*reader-resolver*","clojure.core/*source-path*","clojure.core/*suppress-read*","clojure.core/*unchecked-math*","clojure.core/*use-context-classloader*","clojure.core/*verbose-defrecords*","clojure.core/*warn-on-reflection*","clojure.core/EMPTY-NODE","clojure.core/Inst","clojure.core/char-escape-string","clojure.core/char-name-string","clojure.core/default-data-readers","clojure.core/primitives-classnames","clojure.core/unquote","clojure.core/unquote-splicing","default-data-readers","primitives-classnames","unquote","unquote-splicing"]
- \ }
+ \ 'clojureBoolean': ["false","true"],
+ \ 'clojureCond': ["case","case*","clojure.core/case","clojure.core/cond","clojure.core/cond->","clojure.core/cond->>","clojure.core/condp","clojure.core/if-let","clojure.core/if-not","clojure.core/if-some","clojure.core/when","clojure.core/when-first","clojure.core/when-let","clojure.core/when-not","clojure.core/when-some","cond","cond->","cond->>","condp","if","if-let","if-not","if-some","when","when-first","when-let","when-not","when-some"],
+ \ 'clojureConstant': ["nil"],
+ \ 'clojureDefine': ["clojure.core/definline","clojure.core/definterface","clojure.core/defmacro","clojure.core/defmethod","clojure.core/defmulti","clojure.core/defn","clojure.core/defn-","clojure.core/defonce","clojure.core/defprotocol","clojure.core/defrecord","clojure.core/defstruct","clojure.core/deftype","def","definline","definterface","defmacro","defmethod","defmulti","defn","defn-","defonce","defprotocol","defrecord","defstruct","deftype","deftype*"],
+ \ 'clojureException': ["catch","finally","throw","try"],
+ \ 'clojureFunc': ["*","*'","+","+'","-","-'","->ArrayChunk","->Eduction","->Vec","->VecNode","->VecSeq","-cache-protocol-fn","-reset-methods","/","<","<=","=","==",">",">=","NaN?","PrintWriter-on","StackTraceElement->vec","Throwable->map","abs","accessor","aclone","add-classpath","add-tap","add-watch","agent","agent-error","agent-errors","aget","alength","alias","all-ns","alter","alter-meta!","alter-var-root","ancestors","any?","apply","array-map","aset","aset-boolean","aset-byte","aset-char","aset-double","aset-float","aset-int","aset-long","aset-short","assoc","assoc!","assoc-in","associative?","atom","await","await-for","await1","bases","bean","bigdec","bigint","biginteger","bit-and","bit-and-not","bit-clear","bit-flip","bit-not","bit-or","bit-set","bit-shift-left","bit-shift-right","bit-test","bit-xor","boolean","boolean-array","boolean?","booleans","bound-fn*","bound?","bounded-count","butlast","byte","byte-array","bytes","bytes?","cast","cat","char","char-array","char?","chars","chunk","chunk-append","chunk-buffer","chunk-cons","chunk-first","chunk-next","chunk-rest","chunked-seq?","class","class?","clear-agent-errors","clojure-version","clojure.core/*","clojure.core/*'","clojure.core/+","clojure.core/+'","clojure.core/-","clojure.core/-'","clojure.core/->ArrayChunk","clojure.core/->Eduction","clojure.core/->Vec","clojure.core/->VecNode","clojure.core/->VecSeq","clojure.core/-cache-protocol-fn","clojure.core/-reset-methods","clojure.core//","clojure.core/<","clojure.core/<=","clojure.core/=","clojure.core/==","clojure.core/>","clojure.core/>=","clojure.core/NaN?","clojure.core/PrintWriter-on","clojure.core/StackTraceElement->vec","clojure.core/Throwable->map","clojure.core/abs","clojure.core/accessor","clojure.core/aclone","clojure.core/add-classpath","clojure.core/add-tap","clojure.core/add-watch","clojure.core/agent","clojure.core/agent-error","clojure.core/agent-errors","clojure.core/aget","clojure.core/alength","clojure.core/alias","clojure.core/all-ns","clojure.core/alter","clojure.core/alter-meta!","clojure.core/alter-var-root","clojure.core/ancestors","clojure.core/any?","clojure.core/apply","clojure.core/array-map","clojure.core/aset","clojure.core/aset-boolean","clojure.core/aset-byte","clojure.core/aset-char","clojure.core/aset-double","clojure.core/aset-float","clojure.core/aset-int","clojure.core/aset-long","clojure.core/aset-short","clojure.core/assoc","clojure.core/assoc!","clojure.core/assoc-in","clojure.core/associative?","clojure.core/atom","clojure.core/await","clojure.core/await-for","clojure.core/await1","clojure.core/bases","clojure.core/bean","clojure.core/bigdec","clojure.core/bigint","clojure.core/biginteger","clojure.core/bit-and","clojure.core/bit-and-not","clojure.core/bit-clear","clojure.core/bit-flip","clojure.core/bit-not","clojure.core/bit-or","clojure.core/bit-set","clojure.core/bit-shift-left","clojure.core/bit-shift-right","clojure.core/bit-test","clojure.core/bit-xor","clojure.core/boolean","clojure.core/boolean-array","clojure.core/boolean?","clojure.core/booleans","clojure.core/bound-fn*","clojure.core/bound?","clojure.core/bounded-count","clojure.core/butlast","clojure.core/byte","clojure.core/byte-array","clojure.core/bytes","clojure.core/bytes?","clojure.core/cast","clojure.core/cat","clojure.core/char","clojure.core/char-array","clojure.core/char?","clojure.core/chars","clojure.core/chunk","clojure.core/chunk-append","clojure.core/chunk-buffer","clojure.core/chunk-cons","clojure.core/chunk-first","clojure.core/chunk-next","clojure.core/chunk-rest","clojure.core/chunked-seq?","clojure.core/class","clojure.core/class?","clojure.core/clear-agent-errors","clojure.core/clojure-version","clojure.core/coll?","clojure.core/commute","clojure.core/comp","clojure.core/comparator","clojure.core/compare","clojure.core/compare-and-set!","clojure.core/compile","clojure.core/complement","clojure.core/completing","clojure.core/concat","clojure.core/conj","clojure.core/conj!","clojure.core/cons","clojure.core/constantly","clojure.core/construct-proxy","clojure.core/contains?","clojure.core/count","clojure.core/counted?","clojure.core/create-ns","clojure.core/create-struct","clojure.core/cycle","clojure.core/dec","clojure.core/dec'","clojure.core/decimal?","clojure.core/dedupe","clojure.core/delay?","clojure.core/deliver","clojure.core/denominator","clojure.core/deref","clojure.core/derive","clojure.core/descendants","clojure.core/destructure","clojure.core/disj","clojure.core/disj!","clojure.core/dissoc","clojure.core/dissoc!","clojure.core/distinct","clojure.core/distinct?","clojure.core/doall","clojure.core/dorun","clojure.core/double","clojure.core/double-array","clojure.core/double?","clojure.core/doubles","clojure.core/drop","clojure.core/drop-last","clojure.core/drop-while","clojure.core/eduction","clojure.core/empty","clojure.core/empty?","clojure.core/ensure","clojure.core/ensure-reduced","clojure.core/enumeration-seq","clojure.core/error-handler","clojure.core/error-mode","clojure.core/eval","clojure.core/even?","clojure.core/every-pred","clojure.core/every?","clojure.core/ex-cause","clojure.core/ex-data","clojure.core/ex-info","clojure.core/ex-message","clojure.core/extend","clojure.core/extenders","clojure.core/extends?","clojure.core/false?","clojure.core/ffirst","clojure.core/file-seq","clojure.core/filter","clojure.core/filterv","clojure.core/find","clojure.core/find-keyword","clojure.core/find-ns","clojure.core/find-protocol-impl","clojure.core/find-protocol-method","clojure.core/find-var","clojure.core/first","clojure.core/flatten","clojure.core/float","clojure.core/float-array","clojure.core/float?","clojure.core/floats","clojure.core/flush","clojure.core/fn?","clojure.core/fnext","clojure.core/fnil","clojure.core/force","clojure.core/format","clojure.core/frequencies","clojure.core/future-call","clojure.core/future-cancel","clojure.core/future-cancelled?","clojure.core/future-done?","clojure.core/future?","clojure.core/gensym","clojure.core/get","clojure.core/get-in","clojure.core/get-method","clojure.core/get-proxy-class","clojure.core/get-thread-bindings","clojure.core/get-validator","clojure.core/group-by","clojure.core/halt-when","clojure.core/hash","clojure.core/hash-combine","clojure.core/hash-map","clojure.core/hash-ordered-coll","clojure.core/hash-set","clojure.core/hash-unordered-coll","clojure.core/ident?","clojure.core/identical?","clojure.core/identity","clojure.core/ifn?","clojure.core/in-ns","clojure.core/inc","clojure.core/inc'","clojure.core/indexed?","clojure.core/infinite?","clojure.core/init-proxy","clojure.core/inst-ms","clojure.core/inst-ms*","clojure.core/inst?","clojure.core/instance?","clojure.core/int","clojure.core/int-array","clojure.core/int?","clojure.core/integer?","clojure.core/interleave","clojure.core/intern","clojure.core/interpose","clojure.core/into","clojure.core/into-array","clojure.core/ints","clojure.core/isa?","clojure.core/iterate","clojure.core/iteration","clojure.core/iterator-seq","clojure.core/juxt","clojure.core/keep","clojure.core/keep-indexed","clojure.core/key","clojure.core/keys","clojure.core/keyword","clojure.core/keyword?","clojure.core/last","clojure.core/line-seq","clojure.core/list","clojure.core/list*","clojure.core/list?","clojure.core/load","clojure.core/load-file","clojure.core/load-reader","clojure.core/load-string","clojure.core/loaded-libs","clojure.core/long","clojure.core/long-array","clojure.core/longs","clojure.core/macroexpand","clojure.core/macroexpand-1","clojure.core/make-array","clojure.core/make-hierarchy","clojure.core/map","clojure.core/map-entry?","clojure.core/map-indexed","clojure.core/map?","clojure.core/mapcat","clojure.core/mapv","clojure.core/max","clojure.core/max-key","clojure.core/memoize","clojure.core/merge","clojure.core/merge-with","clojure.core/meta","clojure.core/method-sig","clojure.core/methods","clojure.core/min","clojure.core/min-key","clojure.core/mix-collection-hash","clojure.core/mod","clojure.core/munge","clojure.core/name","clojure.core/namespace","clojure.core/namespace-munge","clojure.core/nat-int?","clojure.core/neg-int?","clojure.core/neg?","clojure.core/newline","clojure.core/next","clojure.core/nfirst","clojure.core/nil?","clojure.core/nnext","clojure.core/not","clojure.core/not-any?","clojure.core/not-empty","clojure.core/not-every?","clojure.core/not=","clojure.core/ns-aliases","clojure.core/ns-imports","clojure.core/ns-interns","clojure.core/ns-map","clojure.core/ns-name","clojure.core/ns-publics","clojure.core/ns-refers","clojure.core/ns-resolve","clojure.core/ns-unalias","clojure.core/ns-unmap","clojure.core/nth","clojure.core/nthnext","clojure.core/nthrest","clojure.core/num","clojure.core/number?","clojure.core/numerator","clojure.core/object-array","clojure.core/odd?","clojure.core/parents","clojure.core/parse-boolean","clojure.core/parse-double","clojure.core/parse-long","clojure.core/parse-uuid","clojure.core/partial","clojure.core/partition","clojure.core/partition-all","clojure.core/partition-by","clojure.core/pcalls","clojure.core/peek","clojure.core/persistent!","clojure.core/pmap","clojure.core/pop","clojure.core/pop!","clojure.core/pop-thread-bindings","clojure.core/pos-int?","clojure.core/pos?","clojure.core/pr","clojure.core/pr-str","clojure.core/prefer-method","clojure.core/prefers","clojure.core/print","clojure.core/print-ctor","clojure.core/print-dup","clojure.core/print-method","clojure.core/print-simple","clojure.core/print-str","clojure.core/printf","clojure.core/println","clojure.core/println-str","clojure.core/prn","clojure.core/prn-str","clojure.core/promise","clojure.core/proxy-call-with-super","clojure.core/proxy-mappings","clojure.core/proxy-name","clojure.core/push-thread-bindings","clojure.core/qualified-ident?","clojure.core/qualified-keyword?","clojure.core/qualified-symbol?","clojure.core/quot","clojure.core/rand","clojure.core/rand-int","clojure.core/rand-nth","clojure.core/random-sample","clojure.core/random-uuid","clojure.core/range","clojure.core/ratio?","clojure.core/rational?","clojure.core/rationalize","clojure.core/re-find","clojure.core/re-groups","clojure.core/re-matcher","clojure.core/re-matches","clojure.core/re-pattern","clojure.core/re-seq","clojure.core/read","clojure.core/read+string","clojure.core/read-line","clojure.core/read-string","clojure.core/reader-conditional","clojure.core/reader-conditional?","clojure.core/realized?","clojure.core/record?","clojure.core/reduce","clojure.core/reduce-kv","clojure.core/reduced","clojure.core/reduced?","clojure.core/reductions","clojure.core/ref","clojure.core/ref-history-count","clojure.core/ref-max-history","clojure.core/ref-min-history","clojure.core/ref-set","clojure.core/refer","clojure.core/release-pending-sends","clojure.core/rem","clojure.core/remove","clojure.core/remove-all-methods","clojure.core/remove-method","clojure.core/remove-ns","clojure.core/remove-tap","clojure.core/remove-watch","clojure.core/repeat","clojure.core/repeatedly","clojure.core/replace","clojure.core/replicate","clojure.core/require","clojure.core/requiring-resolve","clojure.core/reset!","clojure.core/reset-meta!","clojure.core/reset-vals!","clojure.core/resolve","clojure.core/rest","clojure.core/restart-agent","clojure.core/resultset-seq","clojure.core/reverse","clojure.core/reversible?","clojure.core/rseq","clojure.core/rsubseq","clojure.core/run!","clojure.core/satisfies?","clojure.core/second","clojure.core/select-keys","clojure.core/send","clojure.core/send-off","clojure.core/send-via","clojure.core/seq","clojure.core/seq-to-map-for-destructuring","clojure.core/seq?","clojure.core/seqable?","clojure.core/seque","clojure.core/sequence","clojure.core/sequential?","clojure.core/set","clojure.core/set-agent-send-executor!","clojure.core/set-agent-send-off-executor!","clojure.core/set-error-handler!","clojure.core/set-error-mode!","clojure.core/set-validator!","clojure.core/set?","clojure.core/short","clojure.core/short-array","clojure.core/shorts","clojure.core/shuffle","clojure.core/shutdown-agents","clojure.core/simple-ident?","clojure.core/simple-keyword?","clojure.core/simple-symbol?","clojure.core/slurp","clojure.core/some","clojure.core/some-fn","clojure.core/some?","clojure.core/sort","clojure.core/sort-by","clojure.core/sorted-map","clojure.core/sorted-map-by","clojure.core/sorted-set","clojure.core/sorted-set-by","clojure.core/sorted?","clojure.core/special-symbol?","clojure.core/spit","clojure.core/split-at","clojure.core/split-with","clojure.core/str","clojure.core/string?","clojure.core/struct","clojure.core/struct-map","clojure.core/subs","clojure.core/subseq","clojure.core/subvec","clojure.core/supers","clojure.core/swap!","clojure.core/swap-vals!","clojure.core/symbol","clojure.core/symbol?","clojure.core/tagged-literal","clojure.core/tagged-literal?","clojure.core/take","clojure.core/take-last","clojure.core/take-nth","clojure.core/take-while","clojure.core/tap>","clojure.core/test","clojure.core/the-ns","clojure.core/thread-bound?","clojure.core/to-array","clojure.core/to-array-2d","clojure.core/trampoline","clojure.core/transduce","clojure.core/transient","clojure.core/tree-seq","clojure.core/true?","clojure.core/type","clojure.core/unchecked-add","clojure.core/unchecked-add-int","clojure.core/unchecked-byte","clojure.core/unchecked-char","clojure.core/unchecked-dec","clojure.core/unchecked-dec-int","clojure.core/unchecked-divide-int","clojure.core/unchecked-double","clojure.core/unchecked-float","clojure.core/unchecked-inc","clojure.core/unchecked-inc-int","clojure.core/unchecked-int","clojure.core/unchecked-long","clojure.core/unchecked-multiply","clojure.core/unchecked-multiply-int","clojure.core/unchecked-negate","clojure.core/unchecked-negate-int","clojure.core/unchecked-remainder-int","clojure.core/unchecked-short","clojure.core/unchecked-subtract","clojure.core/unchecked-subtract-int","clojure.core/underive","clojure.core/unreduced","clojure.core/unsigned-bit-shift-right","clojure.core/update","clojure.core/update-in","clojure.core/update-keys","clojure.core/update-proxy","clojure.core/update-vals","clojure.core/uri?","clojure.core/use","clojure.core/uuid?","clojure.core/val","clojure.core/vals","clojure.core/var-get","clojure.core/var-set","clojure.core/var?","clojure.core/vary-meta","clojure.core/vec","clojure.core/vector","clojure.core/vector-of","clojure.core/vector?","clojure.core/volatile!","clojure.core/volatile?","clojure.core/vreset!","clojure.core/with-bindings*","clojure.core/with-meta","clojure.core/with-redefs-fn","clojure.core/xml-seq","clojure.core/zero?","clojure.core/zipmap","coll?","commute","comp","comparator","compare","compare-and-set!","compile","complement","completing","concat","conj","conj!","cons","constantly","construct-proxy","contains?","count","counted?","create-ns","create-struct","cycle","dec","dec'","decimal?","dedupe","delay?","deliver","denominator","deref","derive","descendants","destructure","disj","disj!","dissoc","dissoc!","distinct","distinct?","doall","dorun","double","double-array","double?","doubles","drop","drop-last","drop-while","eduction","empty","empty?","ensure","ensure-reduced","enumeration-seq","error-handler","error-mode","eval","even?","every-pred","every?","ex-cause","ex-data","ex-info","ex-message","extend","extenders","extends?","false?","ffirst","file-seq","filter","filterv","find","find-keyword","find-ns","find-protocol-impl","find-protocol-method","find-var","first","flatten","float","float-array","float?","floats","flush","fn?","fnext","fnil","force","format","frequencies","future-call","future-cancel","future-cancelled?","future-done?","future?","gensym","get","get-in","get-method","get-proxy-class","get-thread-bindings","get-validator","group-by","halt-when","hash","hash-combine","hash-map","hash-ordered-coll","hash-set","hash-unordered-coll","ident?","identical?","identity","ifn?","in-ns","inc","inc'","indexed?","infinite?","init-proxy","inst-ms","inst-ms*","inst?","instance?","int","int-array","int?","integer?","interleave","intern","interpose","into","into-array","ints","isa?","iterate","iteration","iterator-seq","juxt","keep","keep-indexed","key","keys","keyword","keyword?","last","line-seq","list","list*","list?","load","load-file","load-reader","load-string","loaded-libs","long","long-array","longs","macroexpand","macroexpand-1","make-array","make-hierarchy","map","map-entry?","map-indexed","map?","mapcat","mapv","max","max-key","memoize","merge","merge-with","meta","method-sig","methods","min","min-key","mix-collection-hash","mod","munge","name","namespace","namespace-munge","nat-int?","neg-int?","neg?","newline","next","nfirst","nil?","nnext","not","not-any?","not-empty","not-every?","not=","ns-aliases","ns-imports","ns-interns","ns-map","ns-name","ns-publics","ns-refers","ns-resolve","ns-unalias","ns-unmap","nth","nthnext","nthrest","num","number?","numerator","object-array","odd?","parents","parse-boolean","parse-double","parse-long","parse-uuid","partial","partition","partition-all","partition-by","pcalls","peek","persistent!","pmap","pop","pop!","pop-thread-bindings","pos-int?","pos?","pr","pr-str","prefer-method","prefers","print","print-ctor","print-dup","print-method","print-simple","print-str","printf","println","println-str","prn","prn-str","promise","proxy-call-with-super","proxy-mappings","proxy-name","push-thread-bindings","qualified-ident?","qualified-keyword?","qualified-symbol?","quot","rand","rand-int","rand-nth","random-sample","random-uuid","range","ratio?","rational?","rationalize","re-find","re-groups","re-matcher","re-matches","re-pattern","re-seq","read","read+string","read-line","read-string","reader-conditional","reader-conditional?","realized?","record?","reduce","reduce-kv","reduced","reduced?","reductions","ref","ref-history-count","ref-max-history","ref-min-history","ref-set","refer","release-pending-sends","rem","remove","remove-all-methods","remove-method","remove-ns","remove-tap","remove-watch","repeat","repeatedly","replace","replicate","require","requiring-resolve","reset!","reset-meta!","reset-vals!","resolve","rest","restart-agent","resultset-seq","reverse","reversible?","rseq","rsubseq","run!","satisfies?","second","select-keys","send","send-off","send-via","seq","seq-to-map-for-destructuring","seq?","seqable?","seque","sequence","sequential?","set","set-agent-send-executor!","set-agent-send-off-executor!","set-error-handler!","set-error-mode!","set-validator!","set?","short","short-array","shorts","shuffle","shutdown-agents","simple-ident?","simple-keyword?","simple-symbol?","slurp","some","some-fn","some?","sort","sort-by","sorted-map","sorted-map-by","sorted-set","sorted-set-by","sorted?","special-symbol?","spit","split-at","split-with","str","string?","struct","struct-map","subs","subseq","subvec","supers","swap!","swap-vals!","symbol","symbol?","tagged-literal","tagged-literal?","take","take-last","take-nth","take-while","tap>","test","the-ns","thread-bound?","to-array","to-array-2d","trampoline","transduce","transient","tree-seq","true?","type","unchecked-add","unchecked-add-int","unchecked-byte","unchecked-char","unchecked-dec","unchecked-dec-int","unchecked-divide-int","unchecked-double","unchecked-float","unchecked-inc","unchecked-inc-int","unchecked-int","unchecked-long","unchecked-multiply","unchecked-multiply-int","unchecked-negate","unchecked-negate-int","unchecked-remainder-int","unchecked-short","unchecked-subtract","unchecked-subtract-int","underive","unreduced","unsigned-bit-shift-right","update","update-in","update-keys","update-proxy","update-vals","uri?","use","uuid?","val","vals","var-get","var-set","var?","vary-meta","vec","vector","vector-of","vector?","volatile!","volatile?","vreset!","with-bindings*","with-meta","with-redefs-fn","xml-seq","zero?","zipmap"],
+ \ 'clojureMacro': ["->","->>","..","amap","and","areduce","as->","assert","binding","bound-fn","clojure.core/->","clojure.core/->>","clojure.core/..","clojure.core/amap","clojure.core/and","clojure.core/areduce","clojure.core/as->","clojure.core/assert","clojure.core/binding","clojure.core/bound-fn","clojure.core/comment","clojure.core/declare","clojure.core/delay","clojure.core/dosync","clojure.core/doto","clojure.core/extend-protocol","clojure.core/extend-type","clojure.core/for","clojure.core/future","clojure.core/gen-class","clojure.core/gen-interface","clojure.core/import","clojure.core/io!","clojure.core/lazy-cat","clojure.core/lazy-seq","clojure.core/locking","clojure.core/memfn","clojure.core/ns","clojure.core/or","clojure.core/proxy","clojure.core/proxy-super","clojure.core/pvalues","clojure.core/refer-clojure","clojure.core/reify","clojure.core/some->","clojure.core/some->>","clojure.core/sync","clojure.core/time","clojure.core/vswap!","clojure.core/with-bindings","clojure.core/with-in-str","clojure.core/with-loading-context","clojure.core/with-local-vars","clojure.core/with-open","clojure.core/with-out-str","clojure.core/with-precision","clojure.core/with-redefs","comment","declare","delay","dosync","doto","extend-protocol","extend-type","for","future","gen-class","gen-interface","import","io!","lazy-cat","lazy-seq","locking","memfn","ns","or","proxy","proxy-super","pvalues","refer-clojure","reify","some->","some->>","sync","time","vswap!","with-bindings","with-in-str","with-loading-context","with-local-vars","with-open","with-out-str","with-precision","with-redefs"],
+ \ 'clojureRepeat': ["clojure.core/doseq","clojure.core/dotimes","clojure.core/loop","clojure.core/while","doseq","dotimes","loop","loop*","recur","while"],
+ \ 'clojureSpecial': ["&",".","clojure.core/fn","clojure.core/import*","clojure.core/let","clojure.core/letfn","do","fn","fn*","let","let*","letfn","letfn*","monitor-enter","monitor-exit","new","quote","reify*","set!","var"],
+ \ 'clojureVariable': ["*1","*2","*3","*agent*","*allow-unresolved-vars*","*assert*","*clojure-version*","*command-line-args*","*compile-files*","*compile-path*","*compiler-options*","*data-readers*","*default-data-reader-fn*","*e","*err*","*file*","*flush-on-newline*","*fn-loader*","*in*","*math-context*","*ns*","*out*","*print-dup*","*print-length*","*print-level*","*print-meta*","*print-namespace-maps*","*print-readably*","*read-eval*","*reader-resolver*","*source-path*","*suppress-read*","*unchecked-math*","*use-context-classloader*","*verbose-defrecords*","*warn-on-reflection*","EMPTY-NODE","Inst","char-escape-string","char-name-string","clojure.core/*1","clojure.core/*2","clojure.core/*3","clojure.core/*agent*","clojure.core/*allow-unresolved-vars*","clojure.core/*assert*","clojure.core/*clojure-version*","clojure.core/*command-line-args*","clojure.core/*compile-files*","clojure.core/*compile-path*","clojure.core/*compiler-options*","clojure.core/*data-readers*","clojure.core/*default-data-reader-fn*","clojure.core/*e","clojure.core/*err*","clojure.core/*file*","clojure.core/*flush-on-newline*","clojure.core/*fn-loader*","clojure.core/*in*","clojure.core/*math-context*","clojure.core/*ns*","clojure.core/*out*","clojure.core/*print-dup*","clojure.core/*print-length*","clojure.core/*print-level*","clojure.core/*print-meta*","clojure.core/*print-namespace-maps*","clojure.core/*print-readably*","clojure.core/*read-eval*","clojure.core/*reader-resolver*","clojure.core/*source-path*","clojure.core/*suppress-read*","clojure.core/*unchecked-math*","clojure.core/*use-context-classloader*","clojure.core/*verbose-defrecords*","clojure.core/*warn-on-reflection*","clojure.core/EMPTY-NODE","clojure.core/Inst","clojure.core/char-escape-string","clojure.core/char-name-string","clojure.core/default-data-readers","clojure.core/primitives-classnames","clojure.core/print-dup","clojure.core/print-method","clojure.core/unquote","clojure.core/unquote-splicing","default-data-readers","primitives-classnames","print-dup","print-method","unquote","unquote-splicing"]
+ \ }
function! s:syntax_keyword(dict)
for key in keys(a:dict)
@@ -81,8 +81,6 @@ syntax match clojureSymbol "\v%([a-zA-Z!$&*_+=|<.>?-]|[^\x00-\x7F])+%(:?%([a-zA-
" NB. Correct matching of radix literals was removed for better performance.
syntax match clojureNumber "\v<[-+]?%(%([2-9]|[12]\d|3[0-6])[rR][[:alnum:]]+|%(0\o*|0x\x+|[1-9]\d*)N?|%(0|[1-9]\d*|%(0|[1-9]\d*)\.\d*)%(M|[eE][-+]?\d+)?|%(0|[1-9]\d*)/%(0|[1-9]\d*))>"
-syntax match clojureVarArg "&"
-
syntax match clojureQuote "\v['`]"
syntax match clojureUnquote "\v\~\@?"
syntax match clojureMeta "\^"
@@ -97,8 +95,8 @@ syntax region clojureRegexpQuoted start=/\\Q/ms=e+1 skip=/\\\\\|\\"/ end=/\\E/me
syntax region clojureRegexpQuote start=/\\Q/ skip=/\\\\\|\\"/ end=/\\E/ end=/"/me=s-1 contains=clojureRegexpQuoted keepend contained
" -*- CHARACTER PROPERTY CLASSES -*-
-" Generated from https://github.com/clojure-vim/clojure.vim/blob/62b215f079ce0f3834fd295c7a7f6bd8cc54bcc3/clj/src/vim_clojure_static/generate.clj
-" Java version 17
+" Generated from https://github.com/clojure-vim/clojure.vim/blob/fd280e33e84c88e97860930557dba3ff80b1a82d/clj/src/vim_clojure_static/generate.clj
+" Java version 17.0.2
syntax match clojureRegexpPosixCharClass "\v\\[pP]\{%(Cntrl|A%(l%(pha|num)|SCII)|Space|Graph|Upper|P%(rint|unct)|Blank|XDigit|Digit|Lower)\}" contained display
syntax match clojureRegexpJavaCharClass "\v\\[pP]\{java%(Whitespace|JavaIdentifier%(Part|Start)|SpaceChar|Mirrored|TitleCase|I%(SOControl|de%(ographic|ntifierIgnorable))|D%(efined|igit)|U%(pperCase|nicodeIdentifier%(Part|Start))|L%(etter%(OrDigit)?|owerCase)|Alphabetic)\}" contained display
syntax match clojureRegexpUnicodeCharClass "\v\\[pP]\{\cIs%(l%(owercase|etter)|hex%(digit|_digit)|w%(hite%(_space|space)|ord)|noncharacter%(_code_point|codepoint)|p%(rint|unctuation)|ideographic|graph|a%(l%(num|phabetic)|ssigned)|uppercase|join%(control|_control)|titlecase|blank|digit|control)\}" contained display
@@ -127,7 +125,7 @@ syntax match clojureRegexpMod "\v\(@<=\?%(\<?[=!]|\>)" contained display
syntax match clojureRegexpMod "\v\(@<=\?\<[[:alpha:]]+\>" contained display
syntax region clojureRegexpGroup start="(" skip=/\\\\\|\\)/ end=")" matchgroup=clojureRegexpGroup contained contains=clojureRegexpMod,clojureRegexpQuantifier,clojureRegexpBoundary,clojureRegexpEscape,@clojureRegexpCharClasses
-syntax region clojureRegexp start=/\#"/ skip=/\\\\\|\\"/ end=/"/ contains=@clojureRegexpCharClasses,clojureRegexpEscape,clojureRegexpQuote,clojureRegexpBoundary,clojureRegexpQuantifier,clojureRegexpOr,clojureRegexpBackRef,clojureRegexpGroup keepend
+syntax region clojureRegexp matchgroup=clojureRegexpDelimiter start=/\#"/ skip=/\\\\\|\\"/ end=/"/ contains=@clojureRegexpCharClasses,clojureRegexpEscape,clojureRegexpQuote,clojureRegexpBoundary,clojureRegexpQuantifier,clojureRegexpOr,clojureRegexpBackRef,clojureRegexpGroup keepend
syntax keyword clojureCommentTodo contained FIXME XXX TODO BUG NOTE HACK FIXME: XXX: TODO: BUG: NOTE: HACK:
@@ -149,8 +147,8 @@ if exists('g:clojure_discard_macro') && g:clojure_discard_macro
endif
" -*- TOP CLUSTER -*-
-" Generated from https://github.com/clojure-vim/clojure.vim/blob/62b215f079ce0f3834fd295c7a7f6bd8cc54bcc3/clj/src/vim_clojure_static/generate.clj
-syntax cluster clojureTop contains=@Spell,clojureAnonArg,clojureBoolean,clojureCharacter,clojureComment,clojureCond,clojureConstant,clojureDefine,clojureDeref,clojureDiscard,clojureDispatch,clojureError,clojureException,clojureFunc,clojureKeyword,clojureMacro,clojureMap,clojureMeta,clojureNumber,clojureQuote,clojureRegexp,clojureRepeat,clojureSexp,clojureSpecial,clojureString,clojureSymbol,clojureUnquote,clojureVarArg,clojureVariable,clojureVector
+" Generated from https://github.com/clojure-vim/clojure.vim/blob/fd280e33e84c88e97860930557dba3ff80b1a82d/clj/src/vim_clojure_static/generate.clj
+syntax cluster clojureTop contains=@Spell,clojureAnonArg,clojureBoolean,clojureCharacter,clojureComment,clojureCond,clojureConstant,clojureDefine,clojureDeref,clojureDiscard,clojureDispatch,clojureError,clojureException,clojureFunc,clojureKeyword,clojureMacro,clojureMap,clojureMeta,clojureNumber,clojureQuote,clojureRegexp,clojureRepeat,clojureSexp,clojureSpecial,clojureString,clojureSymbol,clojureUnquote,clojureVariable,clojureVector
syntax region clojureSexp matchgroup=clojureParen start="(" end=")" contains=@clojureTop fold
syntax region clojureVector matchgroup=clojureParen start="\[" end="]" contains=@clojureTop fold
@@ -171,6 +169,7 @@ highlight default link clojureStringDelimiter String
highlight default link clojureStringEscape Character
highlight default link clojureRegexp Constant
+highlight default link clojureRegexpDelimiter Constant
highlight default link clojureRegexpEscape Character
highlight default link clojureRegexpCharClass SpecialChar
highlight default link clojureRegexpPosixCharClass clojureRegexpCharClass
@@ -195,7 +194,6 @@ highlight default link clojureMacro Macro
highlight default link clojureRepeat Repeat
highlight default link clojureSpecial Special
-highlight default link clojureVarArg Special
highlight default link clojureQuote SpecialChar
highlight default link clojureUnquote SpecialChar
highlight default link clojureMeta SpecialChar
diff --git a/runtime/syntax/coco.vim b/runtime/syntax/coco.vim
index ae32348bcb..dc21314e35 100644
--- a/runtime/syntax/coco.vim
+++ b/runtime/syntax/coco.vim
@@ -1,7 +1,7 @@
" Vim syntax file
" Language: Coco/R
" Maintainer: Ashish Shukla <wahjava@gmail.com>
-" Last Change: 2007 Aug 10
+" Last Change: 2022 Jun 14
" Remark: Coco/R syntax partially implemented.
" License: Vim license
@@ -30,3 +30,4 @@ hi def link cocoOperator Operator
hi def link cocoProductionCode Statement
hi def link cocoPragma Special
+let b:current_syntax = 'coco'
diff --git a/runtime/syntax/confini.vim b/runtime/syntax/confini.vim
new file mode 100644
index 0000000000..823d417a81
--- /dev/null
+++ b/runtime/syntax/confini.vim
@@ -0,0 +1,12 @@
+" Vim syntax file
+" Language: confini
+
+" Quit if a syntax file was already loaded
+if exists("b:current_syntax")
+ finish
+endif
+
+" Use the cfg syntax for now, it's similar.
+runtime! syntax/cfg.vim
+
+let b:current_syntax = 'confini'
diff --git a/runtime/syntax/cs.vim b/runtime/syntax/cs.vim
index b4cb6489e6..722ddbedf6 100644
--- a/runtime/syntax/cs.vim
+++ b/runtime/syntax/cs.vim
@@ -3,13 +3,16 @@
" Maintainer: Nick Jensen <nickspoon@gmail.com>
" Former Maintainers: Anduin Withers <awithers@anduin.com>
" Johannes Zellner <johannes@zellner.org>
-" Last Change: 2020-11-23
+" Last Change: 2022-03-01
" Filenames: *.cs
" License: Vim (see :h license)
" Repository: https://github.com/nickspoons/vim-cs
"
-" REFERENCES:
-" [1] ECMA TC39: C# Language Specification (WD13Oct01.doc)
+" References:
+" - ECMA-334 5th Edition: C# Language Specification
+" https://www.ecma-international.org/publications-and-standards/standards/ecma-334/
+" - C# Language Design: Draft 6th Edition and later proposals
+" https://github.com/dotnet/csharplang
if exists('b:current_syntax')
finish
@@ -19,37 +22,72 @@ let s:save_cpo = &cpoptions
set cpoptions&vim
syn keyword csType bool byte char decimal double float int long object sbyte short string T uint ulong ushort var void dynamic
-syn keyword csStorage delegate enum interface namespace struct
+syn keyword csType nint nuint " contextual
+
+syn keyword csStorage enum interface namespace struct
+syn match csStorage "\<delegate\>"
syn keyword csRepeat break continue do for foreach goto return while
syn keyword csConditional else if switch
syn keyword csLabel case default
-syn match csOperatorError display +::+
-syn match csGlobal display +global::+
-" user labels (see [1] 8.6 Statements)
+
+syn match csNamespaceAlias "@\=\h\w*\ze\_s*::" display
+syn match csGlobalNamespaceAlias "global\ze\_s*::" display
+syn cluster csNamespaceAlias contains=csGlobalNamespaceAlias,csNamespaceAlias,csNamespaceAliasQualifier
+
+" user labels
syn match csLabel display +^\s*\I\i*\s*:\%([^:]\)\@=+
-syn keyword csModifier abstract const extern internal override private protected public readonly sealed static virtual volatile
-syn keyword csConstant false null true
+
+" Function pointers
+syn match csType "\<delegate\s*\*" contains=csOpSymbols nextgroup=csManagedModifier skipwhite skipempty
+syn keyword csManagedModifier managed unmanaged contained
+
+" Modifiers
+syn match csUsingModifier "\<global\ze\_s\+using\>"
+syn keyword csAccessModifier internal private protected public
+" TODO: in new out
+syn keyword csModifier abstract const event override readonly sealed static virtual volatile
+syn match csModifier "\<\%(extern\|fixed\|unsafe\)\>"
+syn match csModifier "\<partial\ze\_s\+\%(class\|struct\|interface\|record\|void\)\>"
+
syn keyword csException try catch finally throw when
-syn keyword csLinq ascending by descending equals from group in into join let on orderby select where
-syn keyword csAsync async await
+syn keyword csLinq ascending by descending equals from group in into join let on orderby select
+syn match csLinq "\<where\>"
-syn keyword csUnspecifiedStatement as base checked event fixed in is lock nameof operator out params ref sizeof stackalloc this unchecked unsafe using
-syn keyword csUnsupportedStatement add remove value
-syn keyword csUnspecifiedKeyword explicit implicit
+" Type parameter constraint clause
+syn match csStorage "\<where\>\ze\_s\+@\=\h\w*\_s*:"
-" Contextual Keywords
-syn match csContextualStatement /\<yield[[:space:]\n]\+\%(return\|break\)/me=s+5
-syn match csContextualStatement /\<partial[[:space:]\n]\+\%(class\|struct\|interface\)/me=s+7
-syn match csContextualStatement /\<\%(get\|set\)\%(;\|[[:space:]\n]*{\)/me=s+3
-syn match csContextualStatement /\<\%(get\|set\)\s*=>/me=s+3
-syn match csContextualStatement /\<where\>[^:]\+:/me=s+5
+" Async
+syn keyword csAsyncModifier async
+syn keyword csAsyncOperator await
+
+syn match csStorage "\<extern\ze\s\+alias\>"
+syn match csStorage "\%(\<extern\s\+\)\@16<=alias\>"
+
+syn match csStatement "\<\%(checked\|unchecked\|unsafe\)\ze\_s*{"
+syn match csStatement "\<fixed\ze\_s*("
+syn keyword csStatement lock
+syn match csStatement "\<yield\ze\_s\+\%(return\|break\)\>"
+
+syn match csAccessor "\<\%(get\|set\|init\|add\|remove\)\ze\_s*\%([;{]\|=>\)"
+
+syn keyword csAccess base
+syn match csAccess "\<this\>"
+
+" Extension method parameter modifier
+syn match csModifier "\<this\ze\_s\+@\=\h"
+
+syn keyword csUnspecifiedStatement as in is nameof operator out params ref sizeof stackalloc using
+syn keyword csUnsupportedStatement value
+syn keyword csUnspecifiedKeyword explicit implicit
" Operators
-syn keyword csTypeOf typeof contained
-syn region csTypeOfStatement start="typeof(" end=")" contains=csType, csTypeOf
+syn keyword csTypeOf typeof nextgroup=csTypeOfOperand,csTypeOfError skipwhite skipempty
+syn region csTypeOfOperand matchgroup=csParens start="(" end=")" contained contains=csType
+syn match csTypeOfError "[^([:space:]]" contained
+syn match csKeywordOperator "\<\%(checked\|unchecked\)\ze\_s*("
" Punctuation
-syn match csBraces "[{}\[\]]" display
+syn match csBraces "[{}[\]]" display
syn match csParens "[()]" display
syn match csOpSymbols "+\{1,2}" display
syn match csOpSymbols "-\{1,2}" display
@@ -64,128 +102,173 @@ syn match csLogicSymbols "&&" display
syn match csLogicSymbols "||" display
syn match csLogicSymbols "?" display
syn match csLogicSymbols ":" display
+syn match csNamespaceAliasQualifier "::" display
" Generics
-syn region csGeneric matchgroup=csGenericBraces start="<" end=">" oneline contains=csType,csGeneric,csUserType,csUserIdentifier,csUserInterface,csUserMethod
+syn region csGeneric matchgroup=csGenericBraces start="<" end=">" oneline contains=csType,csGeneric,@csNamespaceAlias,csUserType,csUserIdentifier,csUserInterface,csUserMethod
" Comments
"
" PROVIDES: @csCommentHook
syn keyword csTodo contained TODO FIXME XXX NOTE HACK TBD
-syn region csComment start="/\*" end="\*/" contains=@csCommentHook,csTodo,@Spell
-syn match csComment "//.*$" contains=@csCommentHook,csTodo,@Spell
-
-" xml markup inside '///' comments
-syn cluster xmlRegionHook add=csXmlCommentLeader
-syn cluster xmlCdataHook add=csXmlCommentLeader
-syn cluster xmlStartTagHook add=csXmlCommentLeader
-syn keyword csXmlTag contained Libraries Packages Types Excluded ExcludedTypeName ExcludedLibraryName
-syn keyword csXmlTag contained ExcludedBucketName TypeExcluded Type TypeKind TypeSignature AssemblyInfo
-syn keyword csXmlTag contained AssemblyName AssemblyPublicKey AssemblyVersion AssemblyCulture Base
-syn keyword csXmlTag contained BaseTypeName Interfaces Interface InterfaceName Attributes Attribute
-syn keyword csXmlTag contained AttributeName Members Member MemberSignature MemberType MemberValue
-syn keyword csXmlTag contained ReturnValue ReturnType Parameters Parameter MemberOfPackage
-syn keyword csXmlTag contained ThreadingSafetyStatement Docs devdoc example overload remarks returns summary
-syn keyword csXmlTag contained threadsafe value internalonly nodoc exception param permission platnote
-syn keyword csXmlTag contained seealso b c i pre sub sup block code note paramref see subscript superscript
-syn keyword csXmlTag contained list listheader item term description altcompliant altmember
-
-syn cluster xmlTagHook add=csXmlTag
-
-syn match csXmlCommentLeader "///" contained
-syn match csXmlComment "///.*$" contains=csXmlCommentLeader,@csXml,@Spell keepend
+syn region csBlockComment start="/\*" end="\*/" contains=@csCommentHook,csTodo,@Spell
+syn match csLineComment "//.*$" contains=@csCommentHook,csTodo,@Spell
+syn cluster csComment contains=csLineComment,csBlockComment
+
+syn region csSummary start="^\s*/// <summary" end="^\%\(\s*///\)\@!" transparent fold keepend
+
+" xml markup inside '///' and /**...*/ comments
+syn cluster xmlRegionHook add=csXmlLineCommentLeader,csXmlBlockCommentMiddle
+syn cluster xmlCdataHook add=csXmlLineCommentLeader,csXmlBlockCommentMiddle
+syn cluster xmlStartTagHook add=csXmlLineCommentLeader,csXmlBlockCommentMiddle
+syn cluster xmlTagHook add=csXmlTag
+syn cluster xmlAttribHook add=csXmlAttrib
+
+" https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/xmldoc/recommended-tags
+syn keyword csXmlTag contained summary remarks
+syn keyword csXmlTag contained returns param paramref exception value
+syn keyword csXmlTag contained para list c code example
+syn keyword csXmlTag contained inheritdoc include
+syn keyword csXmlTag contained see seealso
+syn keyword csXmlTag contained typeparam typeparamref
+syn keyword csXmlTag contained b i u br a
+syn keyword csXmlAttrib contained cref href
+
+syn match csXmlLineCommentLeader "///" contained
+syn match csXmlLineComment "///.*$" contains=csXmlLineCommentLeader,@csXml,@Spell keepend
+syn match csXmlBlockCommentMiddle "^\s*\zs\*" contained
+syn region csXmlBlockComment start="/\*\*" end="\*/" contains=@csXml,@Spell,csXmlBlockCommentMiddle keepend
syn include @csXml syntax/xml.vim
hi def link xmlRegion Comment
" Since syntax/xml.vim contains `syn spell toplevel`, we need to set it back to `default` here.
syn spell default
-" [1] 9.5 Pre-processing directives
-syn region csPreCondit start="^\s*#\s*\%(define\|undef\|if\|elif\|else\|endif\|line\|error\|warning\|pragma\)\>" skip="\\$" end="$" contains=csComment keepend
-syn region csRegion matchgroup=csPreCondit start="^\s*#\s*region.*$" end="^\s*#\s*endregion" transparent fold contains=TOP
-syn region csSummary start="^\s*/// <summary" end="^\%\(\s*///\)\@!" transparent fold keepend
+" Pre-processing directives
+syn region csPreProcDeclaration start="^\s*\zs#\s*\%(define\|undef\)\>" end="$" contains=csLineComment keepend
+syn region csPreProcConditional start="^\s*\zs#\s*\%(if\|elif\)\>" end="$" contains=csLineComment keepend
+syn region csPreProcConditional start="^\s*\zs#\s*\%(else\|endif\)\>" end="$" contains=csLineComment keepend
+syn region csPreProcLine start="^\s*\zs#\s*line\>" end="$" contains=csLineComment keepend
+syn region csPreProcDiagnostic start="^\s*\zs#\s*\%(error\|warning\)\>" end="$"
+syn region csPreProcConditionalSection matchgroup=csPreProcRegion start="^\s*#\s*region\>.*" end="^\s*#\s*endregion\>.*" transparent fold contains=TOP
+syn region csPreProcPragma start="^\s*\zs#\s*pragma\>" end="$" contains=csLineComment keepend
+syn region csPreProcNullable start="^\s*\zs#\s*nullable\>" end="$" contains=csLineComment keepend
+
+if expand('%:e') == 'csx' || getline('1') =~ '^#!.*\<dotnet-script\>'
+ syn region csPreProcInclude start="^\s*\zs#\s*\%(load\|r\)\>" end="$" contains=csLineComment keepend
+ syn match csShebang "\%^#!.*" display
+endif
+syn cluster csPreProcessor contains=csPreProc.*
-syn region csClassType start="@\@1<!\<class\>"hs=s+6 end="[:\n{]"me=e-1 contains=csClass
+syn region csClassType start="\<class\>"hs=s+6 end=">" end="[:{]"me=e-1 contains=csClass
" csUserType may be defined by user scripts/plugins - it should be contained in csNewType
-syn region csNewType start="@\@1<!\<new\>"hs=s+4 end="[;\n{(<\[]"me=e-1 contains=csNew,csUserType
+syn region csNewType start="\<new\>"hs=s+4 end="[;\n{(<\[]"me=e-1 contains=csNew,@csNamespaceAlias,csUserType
syn region csIsType start=" is "hs=s+4 end="[A-Za-z0-9]\+" oneline contains=csIsAs
syn region csIsType start=" as "hs=s+4 end="[A-Za-z0-9]\+" oneline contains=csIsAs
syn keyword csNew new contained
syn keyword csClass class contained
syn keyword csIsAs is as
+syn keyword csBoolean false true
+syn keyword csNull null
+
" Strings and constants
syn match csSpecialError "\\." contained
syn match csSpecialCharError "[^']" contained
-" [1] 9.4.4.4 Character literals
-syn match csSpecialChar +\\["\\'0abfnrtvx]+ contained display
-syn match csUnicodeNumber +\\x\x\{2,4}+ contained contains=csUnicodeSpecifier display
+" Character literals
+syn match csSpecialChar +\\["\\'0abfnrtv]+ contained display
+syn match csUnicodeNumber +\\x\x\{1,4}+ contained contains=csUnicodeSpecifier display
syn match csUnicodeNumber +\\u\x\{4}+ contained contains=csUnicodeSpecifier display
-syn match csUnicodeNumber +\\U\x\{8}+ contained contains=csUnicodeSpecifier display
-syn match csUnicodeSpecifier +\\[uU]+ contained display
+syn match csUnicodeNumber +\\U00\x\{6}+ contained contains=csUnicodeSpecifier display
+syn match csUnicodeSpecifier +\\[uUx]+ contained display
syn region csString matchgroup=csQuote start=+"+ end=+"+ end=+$+ extend contains=csSpecialChar,csSpecialError,csUnicodeNumber,@Spell
syn match csCharacter "'[^']*'" contains=csSpecialChar,csSpecialCharError,csUnicodeNumber display
syn match csCharacter "'\\''" contains=csSpecialChar display
syn match csCharacter "'[^\\]'" display
-syn match csNumber "\<0[0-7]*[lL]\=\>" display
-syn match csNumber "\<0[xX][[:xdigit:]_]\+[lL]\=\>" display
-syn match csNumber "\<0[bB][01_]\+[lL]\=\>" display
-syn match csNumber "\<[[:digit:]_]\+[lL]\=\>" display
-syn match csNumber "\<[[:digit:]_]\+\.[[:digit:]_]*\%\([eE][-+]\=[[:digit:]_]\+\)\=[fFdDmM]\=" display
-syn match csNumber "\.[[:digit:]_]\+\%\([eE][-+]\=[[:digit:]_]\+\)\=[fFdDmM]\=" display
-syn match csNumber "\<[[:digit:]_]\+[eE][-+]\=[[:digit:]_]\+[fFdDmM]\=\>" display
-syn match csNumber "\<[[:digit:]_]\+\%\([eE][-+]\=[[:digit:]_]\+\)\=[fFdDmM]\>" display
+
+" Numbers
+syn case ignore
+syn match csInteger "\<0b[01_]*[01]\%([lu]\|lu\|ul\)\=\>" display
+syn match csInteger "\<\d\+\%(_\+\d\+\)*\%([lu]\|lu\|ul\)\=\>" display
+syn match csInteger "\<0x[[:xdigit:]_]*\x\%([lu]\|lu\|ul\)\=\>" display
+syn match csReal "\<\d\+\%(_\+\d\+\)*\.\d\+\%(_\+\d\+\)*\%\(e[-+]\=\d\+\%(_\+\d\+\)*\)\=[fdm]\=" display
+syn match csReal "\.\d\+\%(_\+\d\+\)*\%(e[-+]\=\d\+\%(_\+\d\+\)*\)\=[fdm]\=\>" display
+syn match csReal "\<\d\+\%(_\+\d\+\)*e[-+]\=\d\+\%(_\+\d\+\)*[fdm]\=\>" display
+syn match csReal "\<\d\+\%(_\+\d\+\)*[fdm]\>" display
+syn case match
+syn cluster csNumber contains=csInteger,csReal
syn region csInterpolatedString matchgroup=csQuote start=+\$"+ end=+"+ extend contains=csInterpolation,csEscapedInterpolation,csSpecialChar,csSpecialError,csUnicodeNumber,@Spell
syn region csInterpolation matchgroup=csInterpolationDelimiter start=+{+ end=+}+ keepend contained contains=@csAll,csBraced,csBracketed,csInterpolationAlign,csInterpolationFormat
syn match csEscapedInterpolation "{{" transparent contains=NONE display
syn match csEscapedInterpolation "}}" transparent contains=NONE display
-syn region csInterpolationAlign matchgroup=csInterpolationAlignDel start=+,+ end=+}+ end=+:+me=e-1 contained contains=csNumber,csConstant,csCharacter,csParens,csOpSymbols,csString,csBracketed display
+syn region csInterpolationAlign matchgroup=csInterpolationAlignDel start=+,+ end=+}+ end=+:+me=e-1 contained contains=@csNumber,csBoolean,csConstant,csCharacter,csParens,csOpSymbols,csString,csBracketed display
syn match csInterpolationFormat +:[^}]\+}+ contained contains=csInterpolationFormatDel display
syn match csInterpolationAlignDel +,+ contained display
syn match csInterpolationFormatDel +:+ contained display
syn region csVerbatimString matchgroup=csQuote start=+@"+ end=+"+ skip=+""+ extend contains=csVerbatimQuote,@Spell
syn match csVerbatimQuote +""+ contained
-syn match csQuoteError +@$"+he=s+2,me=s+2
-syn region csInterVerbString matchgroup=csQuote start=+\$@"+ end=+"+ skip=+""+ extend contains=csInterpolation,csEscapedInterpolation,csSpecialChar,csSpecialError,csUnicodeNumber,csVerbatimQuote,@Spell
+syn region csInterVerbString matchgroup=csQuote start=+$@"+ start=+@$"+ end=+"+ skip=+""+ extend contains=csInterpolation,csEscapedInterpolation,csSpecialChar,csSpecialError,csUnicodeNumber,csVerbatimQuote,@Spell
+
+syn cluster csString contains=csString,csInterpolatedString,csVerbatimString,csInterVerbString
+
+syn cluster csLiteral contains=csBoolean,@csNumber,csCharacter,@csString,csNull
syn region csBracketed matchgroup=csParens start=+(+ end=+)+ extend contained transparent contains=@csAll,csBraced,csBracketed
syn region csBraced matchgroup=csParens start=+{+ end=+}+ extend contained transparent contains=@csAll,csBraced,csBracketed
-syn cluster csAll contains=csCharacter,csClassType,csComment,csContextualStatement,csEndColon,csIsType,csLabel,csLogicSymbols,csNewType,csConstant,csNumber,csOpSymbols,csOperatorError,csParens,csPreCondit,csRegion,csString,csSummary,csType,csUnicodeNumber,csUnicodeSpecifier,csInterpolatedString,csVerbatimString,csInterVerbString,csUserType,csUserIdentifier,csUserInterface,csUserMethod
+syn cluster csAll contains=@csLiteral,csClassType,@csComment,csEndColon,csIsType,csLabel,csLogicSymbols,csNewType,csOpSymbols,csParens,@csPreProcessor,csSummary,@csNamespaceAlias,csType,csUnicodeNumber,csUserType,csUserIdentifier,csUserInterface,csUserMethod
+
+" Keyword identifiers
+syn match csIdentifier "@\h\w*"
" The default highlighting.
+hi def link csUnspecifiedStatement Statement
+hi def link csUnsupportedStatement Statement
+hi def link csUnspecifiedKeyword Keyword
+
+hi def link csGlobalNamespaceAlias Include
+
hi def link csType Type
hi def link csClassType Type
hi def link csIsType Type
+
hi def link csStorage Structure
hi def link csClass Structure
+hi def link csNew Statement
+hi def link csIsAs Keyword
+hi def link csAccessor Keyword
+hi def link csAccess Keyword
+
+hi def link csLinq Statement
+
+hi def link csStatement Statement
hi def link csRepeat Repeat
hi def link csConditional Conditional
hi def link csLabel Label
-hi def link csModifier StorageClass
-hi def link csConstant Constant
hi def link csException Exception
-hi def link csTypeOf Keyword
-hi def link csTypeOfStatement Typedef
-hi def link csUnspecifiedStatement Statement
-hi def link csUnsupportedStatement Statement
-hi def link csUnspecifiedKeyword Keyword
-hi def link csNew Statement
-hi def link csLinq Statement
-hi def link csIsAs Keyword
-hi def link csAsync Keyword
-hi def link csContextualStatement Statement
-hi def link csOperatorError Error
+
+hi def link csModifier StorageClass
+hi def link csAccessModifier csModifier
+hi def link csAsyncModifier csModifier
+hi def link csManagedModifier csModifier
+hi def link csUsingModifier csModifier
hi def link csTodo Todo
hi def link csComment Comment
-
+hi def link csLineComment csComment
+hi def link csBlockComment csComment
+
+hi def link csKeywordOperator Keyword
+hi def link csAsyncOperator csKeywordOperator
+hi def link csTypeOf csKeywordOperator
+hi def link csTypeOfOperand Typedef
+hi def link csTypeOfError Error
hi def link csOpSymbols Operator
hi def link csLogicSymbols Operator
@@ -193,15 +276,29 @@ hi def link csSpecialError Error
hi def link csSpecialCharError Error
hi def link csString String
hi def link csQuote String
-hi def link csQuoteError Error
hi def link csInterpolatedString String
hi def link csVerbatimString String
hi def link csInterVerbString String
hi def link csVerbatimQuote SpecialChar
-hi def link csPreCondit PreCondit
+
+hi def link csPreProc PreProc
+hi def link csPreProcDeclaration Define
+hi def link csPreProcConditional PreCondit
+hi def link csPreProcLine csPreProc
+hi def link csPreProcDiagnostic csPreProc
+hi def link csPreProcRegion csPreProc
+hi def link csPreProcPragma csPreProc
+hi def link csPreProcNullable csPreProc
+hi def link csPreProcInclude csPreProc
+hi def link csShebang csPreProc
+
+hi def link csConstant Constant
+hi def link csNull Constant
+hi def link csBoolean Boolean
hi def link csCharacter Character
hi def link csSpecialChar SpecialChar
-hi def link csNumber Number
+hi def link csInteger Number
+hi def link csReal Float
hi def link csUnicodeNumber SpecialChar
hi def link csUnicodeSpecifier SpecialChar
hi def link csInterpolationDelimiter Delimiter
@@ -212,9 +309,12 @@ hi def link csInterpolationFormatDel csInterpolationDelimiter
hi def link csGenericBraces csBraces
" xml markup
-hi def link csXmlCommentLeader Comment
-hi def link csXmlComment Comment
+hi def link csXmlLineCommentLeader Comment
+hi def link csXmlLineComment Comment
+hi def link csXmlBlockComment Comment
+hi def link csXmlBlockCommentMiddle csXmlBlockComment
hi def link csXmlTag Statement
+hi def link csXmlAttrib Statement
let b:current_syntax = 'cs'
diff --git a/runtime/syntax/debchangelog.vim b/runtime/syntax/debchangelog.vim
index 79352c0827..2efd919772 100644
--- a/runtime/syntax/debchangelog.vim
+++ b/runtime/syntax/debchangelog.vim
@@ -3,7 +3,7 @@
" Maintainer: Debian Vim Maintainers
" Former Maintainers: Gerfried Fuchs <alfie@ist.org>
" Wichert Akkerman <wakkerma@debian.org>
-" Last Change: 2021 Oct 19
+" Last Change: 2022 May 01
" URL: https://salsa.debian.org/vim-team/vim-debian/blob/master/syntax/debchangelog.vim
" Standard syntax initialization
@@ -24,7 +24,7 @@ let s:supported = [
\ 'jessie', 'stretch', 'buster', 'bullseye', 'bookworm',
\ 'trixie', 'sid', 'rc-buggy',
\
- \ 'trusty', 'xenial', 'bionic', 'focal', 'hirsute', 'impish', 'jammy',
+ \ 'trusty', 'xenial', 'bionic', 'focal', 'impish', 'jammy', 'kinetic',
\ 'devel'
\ ]
let s:unsupported = [
@@ -35,7 +35,7 @@ let s:unsupported = [
\ 'gutsy', 'hardy', 'intrepid', 'jaunty', 'karmic', 'lucid',
\ 'maverick', 'natty', 'oneiric', 'precise', 'quantal', 'raring', 'saucy',
\ 'utopic', 'vivid', 'wily', 'yakkety', 'zesty', 'artful', 'cosmic',
- \ 'disco', 'eoan', 'groovy'
+ \ 'disco', 'eoan', 'hirsute', 'groovy'
\ ]
let &cpo=s:cpo
diff --git a/runtime/syntax/debcontrol.vim b/runtime/syntax/debcontrol.vim
index 8b65ece4ca..4c7fb5dd63 100644
--- a/runtime/syntax/debcontrol.vim
+++ b/runtime/syntax/debcontrol.vim
@@ -3,7 +3,7 @@
" Maintainer: Debian Vim Maintainers
" Former Maintainers: Gerfried Fuchs <alfie@ist.org>
" Wichert Akkerman <wakkerma@debian.org>
-" Last Change: 2021 Nov 26
+" Last Change: 2022 May 11
" URL: https://salsa.debian.org/vim-team/vim-debian/blob/master/syntax/debcontrol.vim
" Standard syntax initialization
@@ -17,7 +17,7 @@ set cpo&vim
" Should match case except for the keys of each field
syn case match
-syn iskeyword @,48-57,-,/
+syn iskeyword @,48-57,-
" Everything that is not explicitly matched by the rules below
syn match debcontrolElse "^.*$"
diff --git a/runtime/syntax/debsources.vim b/runtime/syntax/debsources.vim
index 4b4c497f18..e3ec6e6598 100644
--- a/runtime/syntax/debsources.vim
+++ b/runtime/syntax/debsources.vim
@@ -2,7 +2,7 @@
" Language: Debian sources.list
" Maintainer: Debian Vim Maintainers
" Former Maintainer: Matthijs Mohlmann <matthijs@cacholong.nl>
-" Last Change: 2021 Oct 19
+" Last Change: 2022 May 01
" URL: https://salsa.debian.org/vim-team/vim-debian/blob/master/syntax/debsources.vim
" Standard syntax initialization
@@ -26,7 +26,7 @@ let s:supported = [
\ 'jessie', 'stretch', 'buster', 'bullseye', 'bookworm',
\ 'trixie', 'sid', 'rc-buggy',
\
- \ 'trusty', 'xenial', 'bionic', 'focal', 'hirsute', 'impish', 'jammy',
+ \ 'trusty', 'xenial', 'bionic', 'focal', 'impish', 'jammy', 'kinetic',
\ 'devel'
\ ]
let s:unsupported = [
@@ -37,7 +37,7 @@ let s:unsupported = [
\ 'gutsy', 'hardy', 'intrepid', 'jaunty', 'karmic', 'lucid',
\ 'maverick', 'natty', 'oneiric', 'precise', 'quantal', 'raring', 'saucy',
\ 'utopic', 'vivid', 'wily', 'yakkety', 'zesty', 'artful', 'cosmic',
- \ 'disco', 'eoan', 'groovy'
+ \ 'disco', 'eoan', 'hirsute', 'groovy'
\ ]
let &cpo=s:cpo
diff --git a/runtime/syntax/dep3patch.vim b/runtime/syntax/dep3patch.vim
index 8b2cee629c..cb0eda8931 100644
--- a/runtime/syntax/dep3patch.vim
+++ b/runtime/syntax/dep3patch.vim
@@ -1,7 +1,7 @@
" Vim syntax file
" Language: Debian DEP3 Patch headers
" Maintainer: Gabriel Filion <gabster@lelutin.ca>
-" Last Change: 2021-01-09
+" Last Change: 2022 Apr 06
" URL: https://salsa.debian.org/vim-team/vim-debian/blob/master/syntax/dep3patch.vim
"
" Specification of the DEP3 patch header format is available at:
@@ -28,7 +28,7 @@ syn region dep3patchMultiField matchgroup=dep3patchKey start="^Bug\%(-[[:graph:]
syn region dep3patchMultiField matchgroup=dep3patchKey start="^Forwarded\ze: *" end="$" contained contains=dep3patchHTTPUrl,dep3patchForwardedShort oneline keepend
syn region dep3patchMultiField matchgroup=dep3patchKey start="^\%(Author\|From\)\ze: *" end="$" contained contains=dep3patchEmail oneline keepend
syn region dep3patchMultiField matchgroup=dep3patchKey start="^\%(Reviewed-by\|Acked-by\)\ze: *" end="$" contained contains=dep3patchEmail oneline keepend
-syn region dep3patchMultiField matchgroup=dep3patchKey start="^Last-Updated\ze: *" end="$" contained contains=dep3patchISODate oneline keepend
+syn region dep3patchMultiField matchgroup=dep3patchKey start="^Last-Update\ze: *" end="$" contained contains=dep3patchISODate oneline keepend
syn region dep3patchMultiField matchgroup=dep3patchKey start="^Applied-Upstream\ze: *" end="$" contained contains=dep3patchHTTPUrl,dep3patchCommitID oneline keepend
syn match dep3patchHTTPUrl contained "\vhttps?://[[:alnum:]][-[:alnum:]]*[[:alnum:]]?(\.[[:alnum:]][-[:alnum:]]*[[:alnum:]]?)*\.[[:alpha:]][-[:alnum:]]*[[:alpha:]]?(:\d+)?(/[^[:space:]]*)?$"
diff --git a/runtime/syntax/dirpager.vim b/runtime/syntax/dirpager.vim
index 40137e4936..e93420f3ab 100644
--- a/runtime/syntax/dirpager.vim
+++ b/runtime/syntax/dirpager.vim
@@ -6,7 +6,7 @@
" Description: display directory content inside Vim with syntax
" highlighting
" File: runtime/syntax/dirpager.vim
-" Last Change: 2012 May 19
+" Last Change: 2022 Jun 14
" Modeline: vim: ts=8:sw=2:sts=2:
"
" Credits: dirpager.vim is derived from Nikolai Weibulls dircolors.vim
diff --git a/runtime/syntax/doxygen.vim b/runtime/syntax/doxygen.vim
index 167b17cd0f..357c4302cf 100644
--- a/runtime/syntax/doxygen.vim
+++ b/runtime/syntax/doxygen.vim
@@ -120,7 +120,7 @@ try
" end=+\(\n\s*\*\=\s*\([@\\]\([npcbea]\>\|em\>\|ref\>\|link\>\|f\$\|[$\\&<>#]\)\@!\)\|\s*$\)\@=+
"syn region doxygenBriefLine contained start=+\<\k+ skip=+^\s*\(\*/\@!\s*\)\=\(\<\|[@\\]\<\([npcbea]\>\|em\>\|ref\|link\>\>\|f\$\|[$\\&<>#]\)\|[^ \t\\@*]\)+ end=+^+ contains=doxygenContinueCommentWhite,doxygenSmallSpecial,@doxygenHtmlGroup,doxygenTODO,doxygenHyperLink,doxygenHashLink,@Spell skipwhite keepend matchgroup=xxx
-syn region doxygenBriefLine contained start=+\<\k+ skip=+^\s*\(\*/\@!\s*\)\=\(\<\|[@\\]\<\([npcbea]\>\|em\>\|ref\|link\>\>\|f\$\|[$\\&<>#]\)\|[^ \t\\@*]\)+ end=+^+ skipwhite keepend matchgroup=xxx
+syn region doxygenBriefLine contained start=+\<\k+ skip=+^\s*\(\*/\@!\s*\)\=\(\<\|[@\\]\<\([npcbea]\>\|em\>\|ref\|link\>\>\|f\$\|[$\\&<>#]\)\|[^ \t\\@*]\)+ end=+^+ skipwhite keepend matchgroup=xxx contains=@Spell
" syn region doxygenBriefLine matchgroup=xxxy contained start=+\<\k.\++ skip=+^\s*\k+ end=+end+ skipwhite keepend
"doxygenFindBriefSpecial,
"" syn region doxygenSpecialMultilineDesc start=+.\++ contained contains=doxygenSpecialContinueCommentWhite,doxygenSmallSpecial,doxygenHyperLink,doxygenHashLink,@doxygenHtmlGroup,@Spell skipwhite keepend
@@ -498,12 +498,16 @@ endif
syn match doxygenLeadingWhite +\(^\s*\*\)\@<=\s*+ contained
- " This is still a proposal, but won't do any harm.
- aug doxygengroup
- au!
- au Syntax UserColor_reset nested call s:Doxygen_Hilights_Base()
- au Syntax UserColor_{on,reset,enable} nested call s:Doxygen_Hilights()
- aug END
+ " This is still a proposal, but it is probably fine. However, it doesn't
+ " work when 'syntax' is set in a modeline, catch the security error.
+ try
+ aug doxygengroup
+ au!
+ au Syntax UserColor_reset nested call s:Doxygen_Hilights_Base()
+ au Syntax UserColor_{on,reset,enable} nested call s:Doxygen_Hilights()
+ aug END
+ catch /E12:/
+ endtry
SynLink doxygenBody Comment
diff --git a/runtime/syntax/dts.vim b/runtime/syntax/dts.vim
index d75b9c178a..be51ab5b10 100644
--- a/runtime/syntax/dts.vim
+++ b/runtime/syntax/dts.vim
@@ -1,11 +1,12 @@
" Vim syntax file
" Language: dts/dtsi (device tree files)
" Maintainer: Daniel Mack <vim@zonque.org>
-" Last Change: 2021 May 15
+" Last Change: 2022 Jun 14
if exists("b:current_syntax")
finish
endif
+let b:current_syntax = 'dts'
syntax region dtsComment start="/\*" end="\*/"
syntax match dtsReference "&[[:alpha:][:digit:]_]\+"
diff --git a/runtime/syntax/eruby.vim b/runtime/syntax/eruby.vim
index 6bb24fe562..3d1bf715db 100644
--- a/runtime/syntax/eruby.vim
+++ b/runtime/syntax/eruby.vim
@@ -3,9 +3,9 @@
" Maintainer: Tim Pope <vimNOSPAM@tpope.org>
" URL: https://github.com/vim-ruby/vim-ruby
" Release Coordinator: Doug Kearns <dougkearns@gmail.com>
-" Last Change: 2018 Jul 04
+" Last Change: 2022 Mar 18
-if &syntax !~# '\<eruby\>' || get(b:, 'current_syntax') =~# '\<eruby\>'
+if exists("b:current_syntax")
finish
endif
@@ -19,8 +19,6 @@ endif
if &filetype =~ '^eruby\.'
let b:eruby_subtype = matchstr(&filetype,'^eruby\.\zs\w\+')
-elseif &filetype =~ '^.*\.eruby\>'
- let b:eruby_subtype = matchstr(&filetype,'^.\{-\}\ze\.eruby\>')
elseif !exists("b:eruby_subtype") && main_syntax == 'eruby'
let s:lines = getline(1)."\n".getline(2)."\n".getline(3)."\n".getline(4)."\n".getline(5)."\n".getline("$")
let b:eruby_subtype = matchstr(s:lines,'eruby_subtype=\zs\w\+')
@@ -54,10 +52,10 @@ if !b:eruby_nest_level
let b:eruby_nest_level = 1
endif
-if get(b:, 'eruby_subtype', '') !~# '^\%(eruby\)\=$' && &syntax =~# '^eruby\>'
+if exists("b:eruby_subtype") && b:eruby_subtype != '' && b:eruby_subtype !=? 'eruby'
exe "runtime! syntax/".b:eruby_subtype.".vim"
+ unlet! b:current_syntax
endif
-unlet! b:current_syntax
syn include @rubyTop syntax/ruby.vim
syn cluster erubyRegions contains=erubyOneLiner,erubyBlock,erubyExpression,erubyComment
@@ -72,7 +70,7 @@ exe 'syn region erubyComment matchgroup=erubyDelimiter start="<%\{1,'.b:erub
hi def link erubyDelimiter PreProc
hi def link erubyComment Comment
-let b:current_syntax = matchstr(&syntax, '^.*\<eruby\>')
+let b:current_syntax = 'eruby'
if main_syntax == 'eruby'
unlet main_syntax
diff --git a/runtime/syntax/fetchmail.vim b/runtime/syntax/fetchmail.vim
index 7a421050d0..50d6b82562 100644
--- a/runtime/syntax/fetchmail.vim
+++ b/runtime/syntax/fetchmail.vim
@@ -1,7 +1,10 @@
" Vim syntax file
-" Language: fetchmail(1) RC File
-" Previous Maintainer: Nikolai Weibull <now@bitwi.se>
-" Latest Revision: 2006-04-19
+" Language: fetchmail(1) RC File
+" Maintainer: Doug Kearns <dougkearns@gmail.com>
+" Previous Maintainer: Nikolai Weibull <now@bitwi.se>
+" Latest Revision: 2022 Jul 02
+
+" Version 6.4.3
if exists("b:current_syntax")
finish
@@ -10,64 +13,69 @@ endif
let s:cpo_save = &cpo
set cpo&vim
-syn keyword fetchmailTodo contained FIXME TODO XXX NOTE
+syn keyword fetchmailTodo contained FIXME TODO XXX NOTE
-syn region fetchmailComment start='#' end='$' contains=fetchmailTodo,@Spell
+syn region fetchmailComment start='#' end='$' contains=fetchmailTodo,@Spell
-syn match fetchmailNumber display '\<\d\+\>'
+syn match fetchmailNumber display '\<\d\+\>'
-syn region fetchmailString start=+"+ skip=+\\\\\|\\"+ end=+"+
- \ contains=fetchmailStringEsc
-syn region fetchmailString start=+'+ skip=+\\\\\|\\'+ end=+'+
- \ contains=fetchmailStringEsc
+syn region fetchmailString start=+"+ skip=+\\\\\|\\"+ end=+"+
+ \ contains=fetchmailStringEsc
+syn region fetchmailString start=+'+ skip=+\\\\\|\\'+ end=+'+
+ \ contains=fetchmailStringEsc
-syn match fetchmailStringEsc contained '\\\([ntb]\|0\d*\|x\x\+\)'
+syn match fetchmailStringEsc contained '\\\([ntb]\|0\d*\|x\x\+\)'
-syn region fetchmailKeyword transparent matchgroup=fetchmailKeyword
- \ start='\<poll\|skip\|defaults\>'
- \ end='\<poll\|skip\|defaults\>'
- \ contains=ALLBUT,fetchmailOptions,fetchmailSet
+syn region fetchmailKeyword transparent matchgroup=fetchmailKeyword
+ \ start='\<poll\|skip\|defaults\>'
+ \ end='\<poll\|skip\|defaults\>'
+ \ contains=ALLBUT,fetchmailOptions,fetchmailSet
syn keyword fetchmailServerOpts contained via proto[col] local[domains] port
- \ auth[enticate] timeout envelope qvirtual aka
- \ interface monitor plugin plugout dns
- \ checkalias uidl interval netsec principal
- \ esmtpname esmtppassword
- \ sslcertck sslcertpath sslfingerprint
+ \ service auth[enticate] timeout envelope
+ \ qvirtual aka interface monitor plugin plugout
+ \ dns checkalias uidl interval tracepolls
+ \ principal esmtpname esmtppassword
+" removed in 6.3.0
+syn keyword fetchmailServerOpts contained netsec
+syn match fetchmailServerOpts contained '\<bad-header\>'
syn match fetchmailServerOpts contained '\<no\_s\+\(envelope\|dns\|checkalias\|uidl\)'
-syn keyword fetchmailUserOpts contained user[name] is to pass[word] ssl
- \ sslcert sslkey sslproto folder smtphost
- \ fetchdomains smtpaddress smtpname antispam
- \ mda bsmtp preconnect postconnect keep flush
- \ fetchall rewrite stripcr forcecr pass8bits
- \ dropstatus dropdelivered mimedecode idle
- \ limit warnings batchlimit fetchlimit expunge
- \ tracepolls properties
-syn match fetchmailUserOpts contained '\<no\_s\+\(keep\|flush\|fetchall\|rewrite\|stripcr\|forcecr\|pass8bits\|dropstatus\|dropdelivered\|mimedecode\|noidle\)'
-
-syn keyword fetchmailSpecial contained here there
-
-syn keyword fetchmailNoise and with has wants options
-syn match fetchmailNoise display '[:;,]'
-
-syn keyword fetchmailSet nextgroup=fetchmailOptions skipwhite skipnl set
-
-syn keyword fetchmailOptions daemon postmaster bouncemail spambounce logfile
- \ idfile syslog nosyslog properties
-syn match fetchmailOptions '\<no\_s\+\(bouncemail\|spambounce\)'
-
-hi def link fetchmailComment Comment
-hi def link fetchmailTodo Todo
-hi def link fetchmailNumber Number
-hi def link fetchmailString String
-hi def link fetchmailStringEsc SpecialChar
-hi def link fetchmailKeyword Keyword
+syn keyword fetchmailUserOpts contained user[name] is to pass[word] ssl
+ \ sslcert sslcertck sslcertfile sslcertpath
+ \ sslfingerprint sslkey sslproto folder
+ \ smtphost fetchdomains smtpaddress smtpname
+ \ antispam mda bsmtp preconnect postconnect
+ \ keep flush limitflush fetchall rewrite
+ \ stripcr forcecr pass8bits dropstatus
+ \ dropdelivered mimedecode idle limit warnings
+ \ batchlimit fetchlimit fetchsizelimit
+ \ fastuidl expunge properties
+ \ sslcommonname
+syn match fetchmailUserOpts contained '\<no\_s\+\(sslcertck\|keep\|flush\|fetchall\|rewrite\|stripcr\|forcecr\|pass8bits\|dropstatus\|dropdelivered\|mimedecode\|idle\)'
+
+syn keyword fetchmailSpecial contained here there
+
+syn keyword fetchmailNoise and with has wants options
+syn match fetchmailNoise display '[:;,]'
+
+syn keyword fetchmailSet nextgroup=fetchmailOptions skipwhite skipnl set
+
+syn keyword fetchmailOptions daemon postmaster bouncemail spambounce
+ \ softbounce logfile pidfile idfile syslog properties
+syn match fetchmailOptions '\<no\_s\+\(bouncemail\|spambounce\|softbounce\|syslog\)'
+
+hi def link fetchmailComment Comment
+hi def link fetchmailTodo Todo
+hi def link fetchmailNumber Number
+hi def link fetchmailString String
+hi def link fetchmailStringEsc SpecialChar
+hi def link fetchmailKeyword Keyword
hi def link fetchmailServerOpts Identifier
-hi def link fetchmailUserOpts Identifier
-hi def link fetchmailSpecial Special
-hi def link fetchmailSet Keyword
-hi def link fetchmailOptions Identifier
+hi def link fetchmailUserOpts Identifier
+hi def link fetchmailSpecial Special
+hi def link fetchmailSet Keyword
+hi def link fetchmailOptions Identifier
let b:current_syntax = "fetchmail"
diff --git a/runtime/syntax/freebasic.vim b/runtime/syntax/freebasic.vim
index 68e838f393..7549d02555 100644
--- a/runtime/syntax/freebasic.vim
+++ b/runtime/syntax/freebasic.vim
@@ -1,14 +1,14 @@
" Vim syntax file
-" Language: FreeBasic
-" Maintainer: Mark Manning <markem@sim1.us>
-" Updated: 10/9/2019
-" Version: 7.0b
+" Language: FreeBASIC
+" Maintainer: Doug Kearns <dougkearns@gmail.com>
+" Previous Maintainer: Mark Manning <markem@sim1.us>
+" Last Change: 2022 Jun 26
"
" Description:
"
" Based originally on the work done by Allan Kelly <Allan.Kelly@ed.ac.uk>
" Updated by Mark Manning <markem@sim1.us>
-" Applied FreeBasic support to the already excellent support
+" Applied FreeBASIC support to the already excellent support
" for standard basic syntax (like QB).
"
" First version based on Micro$soft QBASIC circa
@@ -17,215 +17,595 @@
" not a complete implementation yet. Send suggestions to
" the maintainer.
"
+" TODO: split this into separate dialect-based files, possibly with a common base
+"
" Quit when a (custom) syntax file was already loaded (Taken from c.vim)
"
if exists("b:current_syntax")
finish
endif
"
+" Dialect detection
+"
+let s:lang = freebasic#GetDialect()
+"
+" Whitespace Errors
+"
+if exists("freebasic_space_errors")
+ if !exists("freebasic_no_trail_space_error")
+ syn match freebasicSpaceError display excludenl "\s\+$"
+ endif
+ if !exists("freebasic_no_tab_space_error")
+ syn match freebasicSpaceError display " \+\t"me=e-1
+ endif
+endif
+"
" Be sure to turn on the "case ignore" since current versions
" of freebasic support both upper as well as lowercase
" letters. - MEM 10/1/2006
"
syn case ignore
"
-" This list of keywords is taken directly from the FreeBasic
-" user's guide as presented by the FreeBasic online site.
+" Do the Basic variables names first. This is because it
+" is the most inclusive of the tests. Later on we change
+" this so the identifiers are split up into the various
+" types of identifiers like functions, basic commands and
+" such. MEM 9/9/2006
+"
+if s:lang =~# '\<\%(qb\|fblite\)\>'
+ syn iskeyword @,48-57,_,192-255,.
+ syn match freebasicIdentifier "\<\h\%(\w\|\.\)*\>"
+ syn match freebasicGenericFunction "\<\h\%(\w\|\.\)*\>\ze\s*("
+else
+ syn iskeyword @,48-57,_,192-255
+ syn match freebasicIdentifier "\<\h\w*\>"
+ syn match freebasicGenericFunction "\<\h\w*\>\ze\s*("
+endif
+"
+" This list of keywords is taken directly from the FreeBASIC
+" user's guide as presented by the FreeBASIC online site.
"
-syn keyword freebasicArrays ERASE LBOUND REDIM PRESERVE UBOUND
+syn keyword freebasicArrays ERASE LBOUND PRESERVE REDIM UBOUND
-syn keyword freebasicBitManipulation BIT BITRESET BITSET HIBYTE HIWORD LOBYTE LOWORD SHL SHR
+" array.bi
+syn keyword freebasicArrays ARRAYLEN ARRAYSIZE
+if s:lang == "fb"
+ syn keyword freebasicArrays ArrayConstDescriptorPtr ArrayDescriptorPtr FBARRAY
+endif
-syn keyword freebasicCompilerSwitches DEFBYTE DEFDBL DEFINT DEFLNG DEFLNGINT DEFSHORT DEFSNG DEFSTR
-syn keyword freebasicCompilerSwitches DEFUBYTE DEFUINT DEFULNGINT DEFUSHORT
-syn match freebasicCompilerSwitches "\<option\s+\(BASE\|BYVAL\|DYNAMIC\|ESCAPE\|EXPLICIT\|NOKEYWORD\)\>"
-syn match freebasicCompilerSwitches "\<option\s+\(PRIVATE\|STATIC\)\>"
+if s:lang == "qb"
+ syn keyword freebasicAsm __ASM
+ syn match freebasicAsm "\<end\s\+__asm\>"
+else
+ syn keyword freebasicAsm ASM
+ syn match freebasicAsm "\<end\s\+asm\>"
+endif
-syn region freebasicConditional start="\son\s+" skip=".*" end="gosub"
-syn region freebasicConditional start="\son\s+" skip=".*" end="goto"
-syn match freebasicConditional "\<select\s+case\>"
-syn keyword freebasicConditional if iif then case else elseif with
+if s:lang == "qb"
+ syn keyword freebasicBitManipulation __BIT __BITRESET __BITSET __HIBYTE __HIWORD __LOBYTE __LOWORD
+else
+ syn keyword freebasicBitManipulation BIT BITRESET BITSET HIBYTE HIWORD LOBYTE LOWORD
+endif
-syn match freebasicConsole "\<open\s+\(CONS\|ERR\|PIPE\|SCRN\)\>"
-syn keyword freebasicConsole BEEP CLS CSRLIN LOCATE PRINT POS SPC TAB VIEW WIDTH
+if s:lang != "fb"
+ syn keyword freebasicCompilerSwitches DEFDBL DEFINT DEFLNG DEFSNG DEFSTR
+endif
+if s:lang == "qb"
+ syn keyword freebasicCompilerSwitches __DEFBYTE __DEFLONGINT __DEFSHORT __DEFUBYTE __DEFUINT __DEFULONGINT __DEFUSHORT
+elseif s:lang == "fblite" || s:lang == "deprecated"
+ syn keyword freebasicCompilerSwitches DEFBYTE DEFLONGINT DEFSHORT DEFUBYTE DEFUINT DEFUILONGINT DEFUSHORT
+endif
-syn keyword freebasicDataTypes BYTE AS DIM CONST DOUBLE ENUM INTEGER LONG LONGINT SHARED SHORT STRING
-syn keyword freebasicDataTypes SINGLE TYPE UBYTE UINTEGER ULONGINT UNION UNSIGNED USHORT WSTRING ZSTRING
+syn match freebasicCompilerSwitches "\<option\s\+\%(BASE\|BYVAL\|DYNAMIC\|ESCAPE\|EXPLICIT\|GOSUB\|NOGOSUB\)\>"
+syn match freebasicCompilerSwitches "\<option\s\+\%(NOKEYWORD\|PRIVATE\|STATIC\)\>"
-syn keyword freebasicDateTime DATE DATEADD DATEDIFF DATEPART DATESERIAL DATEVALUE DAY HOUR MINUTE
-syn keyword freebasicDateTime MONTH MONTHNAME NOW SECOND SETDATE SETTIME TIME TIMESERIAL TIMEVALUE
-syn keyword freebasicDateTime TIMER YEAR WEEKDAY WEEKDAYNAME
+syn keyword freebasicData DATA READ RESTORE
-syn keyword freebasicDebug ASSERT STOP
+syn keyword freebasicProgramFlow EXIT GOTO RETURN SLEEP
+syn match freebasicProgramFlow "\<end\>"
+if s:lang == "qb"
+ syn keyword freebasicProgramFlow __SLEEP
+endif
+if s:lang == "fblite" || s:lang == "qb"
+ syn keyword freebasicProgramFlow GOSUB
+endif
+if s:lang == "fb" || s:lang == "deprecated"
+ syn keyword freebasicProgramFlow SCOPE
+ syn match freebasicProgramFlow "\<end\s\+scope\>"
+endif
-syn keyword freebasicErrorHandling ERR ERL ERROR LOCAL RESUME
-syn match freebasicErrorHandling "\<resume\s+next\>"
-syn match freebasicErrorHandling "\<on\s+error\>"
+if s:lang == "fblite" || s:lang == "qb"
+ syn region freebasicConditional matchgroup=freebasicConditional start="\<on\>" end="\<gosub\>" transparent
+ syn region freebasicConditional matchgroup=freebasicConditional start="\<on\>" end="\<goto\>" transparent
+endif
+syn keyword freebasicConditional IF THEN ELSE ELSEIF
+if s:lang == "qb"
+ syn keyword freebasicConditional __IIF __WITH
+ syn match freebasicConditional "\<end\s\+__with\>"
+else
+ syn keyword freebasicConditional IIF WITH
+ syn match freebasicConditional "\<end\s\+with\>"
+endif
+syn match freebasicConditional "\<end\s\+if\>"
+syn match freebasicConditional "\<select\s\+case\>"
+syn match freebasicConditional "\<case\>"
+syn match freebasicConditional "\<case\s\+is\>"
+syn match freebasicConditional "\<end\s\+select\>"
-syn match freebasicFiles "\<get\s+#\>"
-syn match freebasicFiles "\<input\s+#\>"
-syn match freebasicFiles "\<line\s+input\s+#\>"
-syn match freebasicFiles "\<put\s+#\>"
-syn keyword freebasicFiles ACCESS APPEND BINARY BLOAD BSAVE CLOSE EOF FREEFILE INPUT LOC
-syn keyword freebasicFiles LOCK LOF OPEN OUTPUT RANDOM RESET SEEK UNLOCK WRITE
+syn keyword freebasicConsole BEEP CLS CSRLIN LOCATE PRINT POS SPC TAB USING VIEW WIDTH
+syn match freebasicConsole "?"
-syn keyword freebasicFunctions ALIAS ANY BYREF BYVAL CALL CDECL CONSTRUCTOR DESTRUCTOR
-syn keyword freebasicFunctions DECLARE FUNCTION LIB OVERLOAD PASCAL STATIC SUB STDCALL
-syn keyword freebasicFunctions VA_ARG VA_FIRST VA_NEXT
+syn keyword freebasicDataTypes SINGLE DOUBLE INTEGER LONG
+syn match freebasicDataTypes "\<string\>"
+syn keyword freebasicDataTypes AS DIM CONST ENUM SHARED TYPE
+syn match freebasicDataTypes "\<end\s\+enum\>"
+syn match freebasicDataTypes "\<end\s\+type\>"
+if s:lang == "qb"
+ syn keyword freebasicDataTypes __BOOLEAN __BYTE __LONGINT __SHORT __UBYTE __UINTEGER __ULONG __ULONGINT __UNSIGNED __USHORT __ZSTRING
+ syn match freebasicDataTypes "\<__WSTRING\>"
+ syn keyword freebasicDataTypes __EXPLICIT __EXTENDS __IMPLEMENTS __OBJECT __POINTER __PTR __SIZEOF __TYPEOF
+ syn keyword freebasicDataTypes __UNION
+ syn match freebasicDataTypes "\<end\s\+__union\>"
+else
+ syn keyword freebasicDataTypes BOOLEAN BYTE LONGINT SHORT UBYTE UINTEGER ULONG ULONGINT UNSIGNED USHORT ZSTRING
+ syn match freebasicDataTypes "\<WSTRING\>"
+ syn keyword freebasicDataTypes EXPLICIT EXTENDS IMPLEMENTS OBJECT POINTER PTR SIZEOF TYPEOF
+ syn keyword freebasicDataTypes UNION
+ syn match freebasicDataTypes "\<end\s\+union\>"
+endif
+if s:lang == "fb"
+ syn keyword freebasicDataTypes BASE CLASS THIS VAR
+endif
-syn match freebasicGraphics "\<palette\s+get\>"
-syn keyword freebasicGraphics ALPHA CIRCLE CLS COLOR CUSTOM DRAW FLIP GET
-syn keyword freebasicGraphics IMAGECREATE IMAGEDESTROY LINE PAINT PALETTE PCOPY PMAP POINT
-syn keyword freebasicGraphics PRESET PSET PUT RGB RGBA SCREEN SCREENCOPY SCREENINFO SCREENLIST
-syn keyword freebasicGraphics SCREENLOCK SCREENPTR SCREENRES SCREENSET SCREENSYNC SCREENUNLOCK
-syn keyword freebasicGraphics TRANS USING VIEW WINDOW
+if s:lang == "qb"
+ syn match freebasicDateTime "\<\%(date\|time\)\$"
+elseif s:lang == "fblite" || s:lang == "deprecated"
+ syn match freebasicDateTime "\<\%(date\|time\)\>\$\="
+else " fb
+ syn keyword freebasicDateTime DATE TIME
+endif
+syn keyword freebasicDateTime SETDATE SETTIME
-syn match freebasicHardware "\<open\s+com\>"
-syn keyword freebasicHardware INP OUT WAIT LPT LPOS LPRINT
+" datetime.bi
+syn keyword freebasicDateTime DATEADD DATEDIFF DATEPART DATESERIAL DATEVALUE DAY HOUR ISDATE MINUTE
+syn keyword freebasicDateTime MONTH MONTHNAME NOW SECOND TIMESERIAL TIMEVALUE
+syn keyword freebasicDateTime TIMER YEAR WEEKDAY WEEKDAYNAME
-syn keyword freebasicLogical AND EQV IMP OR NOT XOR
+syn keyword freebasicDebug STOP
+if s:lang == "qb"
+ syn keyword freebasicDebug __ASSERT __ASSERTWARN
+else
+ syn keyword freebasicDebug ASSERT ASSERTWARN
+endif
-syn keyword freebasicMath ABS ACOS ASIN ATAN2 ATN COS EXP FIX INT LOG MOD RANDOMIZE
-syn keyword freebasicMath RND SGN SIN SQR TAN
+syn keyword freebasicErrorHandling ERR ERL ERROR
+if s:lang == "qb"
+ syn keyword freebasicErrorHandling __ERFN __ERMN
+ syn match freebasicErrorHandling "\<on\s\+error\>"
+else
+ syn keyword freebasicErrorHandling ERFN ERMN
+ syn match freebasicErrorHandling "\<on\s\+\%(local\s\+\)\=error\>"
+endif
+if s:lang != "fb"
+ syn match freebasicErrorHandling "\<resume\%(\s\+next\)\=\>"
+endif
-syn keyword freebasicMemory ALLOCATE CALLOCATE CLEAR DEALLOCATE FIELD FRE PEEK POKE REALLOCATE
+syn match freebasicFiles "\<get\s\+#\>"
+syn match freebasicFiles "\<input\s\+#\>"
+syn match freebasicFiles "\<line\s\+input\s\+#\>"
+syn match freebasicFiles "\<put\s\+#\>"
+syn keyword freebasicFiles ACCESS APPEND BINARY CLOSE EOF FREEFILE INPUT LOC
+syn keyword freebasicFiles LOCK LOF OUTPUT RANDOM RESET SEEK UNLOCK WRITE
+syn match freebasicFiles "\<open\>"
+if s:lang == "qb"
+ syn keyword freebasicFiles __ENCODING
+else
+ syn keyword freebasicFiles ENCODING WINPUT
+ syn match freebasicFiles "\<open\s\+\%(cons\|err\|pipe\|scrn\)\>"
+endif
-syn keyword freebasicMisc ASM DATA LET TO READ RESTORE SIZEOF SWAP OFFSETOF
+" file.bi
+syn keyword freebasicFiles FILEATTR FILECOPY FILEDATETIME FILEEXISTS FILEFLUSH FILELEN FILESETEOF
-syn keyword freebasicModularizing CHAIN COMMON EXPORT EXTERN DYLIBFREE DYLIBLOAD DYLIBSYMBOL
-syn keyword freebasicModularizing PRIVATE PUBLIC
+syn keyword freebasicFunctions ALIAS BYREF BYVAL CDECL DECLARE LIB NAKED PASCAL STATIC STDCALL
+syn match freebasicFunctions "\<option\ze\s*("
-syn keyword freebasicMultithreading MUTEXCREATE MUTEXDESTROY MUTEXLOCK MUTEXUNLOCK THREADCREATE THREADWAIT
+if s:lang == "qb"
+ syn keyword freebasicFunctions __CVA_ARG __CVA_COPY __CVA_END __CVA_LIST __CVA_START
+ syn keyword freebasicFunctions __VA_ARG __VA_FIRST __VA_NEXT
+else
+ syn keyword freebasicFunctions CVA_ARG CVA_COPY CVA_END CVA_LIST CVA_START
+ syn keyword freebasicFunctions VA_ARG VA_FIRST VA_NEXT
+ syn keyword freebasicFunctions ANY OVERLOAD
+endif
-syn keyword freebasicShell CHDIR DIR COMMAND ENVIRON EXEC EXEPATH KILL NAME MKDIR RMDIR RUN
+syn keyword freebasicFunctions FUNCTION SUB
+syn match freebasicFunctions "\<end\s\+function\>"
+syn match freebasicFunctions "\<end\s\+sub\>"
-syn keyword freebasicEnviron SHELL SYSTEM WINDOWTITLE POINTERS
+if s:lang == "fb"
+ syn keyword freebasicFunctions ABSTRACT OVERRIDE VIRTUAL __THISCALL
+ syn keyword freebasicFunctions CONSTRUCTOR DESTRUCTOR OPERATOR PROPERTY
+ syn match freebasicFunctions "\<end\s\+constructor\>"
+ syn match freebasicFunctions "\<end\s\+destructor\>"
+ syn match freebasicFunctions "\<end\s\+operator\>"
+ syn match freebasicFunctions "\<end\s\+property\>"
+else
+ syn keyword freebasicFunctions CALL
+endif
-syn keyword freebasicLoops FOR LOOP WHILE WEND DO CONTINUE STEP UNTIL next
+syn match freebasicGraphics "\<palette\s\+get\>"
+syn keyword freebasicGraphics ADD ALPHA BLOAD BSAVE CIRCLE CLS COLOR DRAW GET
+syn keyword freebasicGraphics LINE PAINT PALETTE PCOPY PMAP POINT
+syn keyword freebasicGraphics PRESET PSET PUT SCREEN
+syn keyword freebasicGraphics TRANS WINDOW
+if s:lang == "qb"
+ syn keyword freebasicGraphics __FLIP __IMAGECONVERTROW __IMAGECREATE __IMAGEDESTROY __IMAGEINFO __POINTCOORD
+ syn keyword freebasicGraphics __RGB __RGBA __SCREENCOPY __SCREENCONTROL __SCREENEVENT __SCREENGLPROC __SCREENINFO
+ syn keyword freebasicGraphics __SCREENLIST __SCREENLOCK __SCREENPTR __SCREENRES __SCREENSET __SCREENSYNC
+ syn keyword freebasicGraphics __SCREENUNLOCK __WINDOWTITLE
+else
+ syn keyword freebasicGraphics CUSTOM
+ syn keyword freebasicGraphics FLIP IMAGECONVERTROW IMAGECREATE IMAGEDESTROY IMAGEINFO POINTCOORD
+ syn keyword freebasicGraphics RGB RGBA SCREENCOPY SCREENCONTROL SCREENEVENT SCREENGLPROC SCREENINFO
+ syn keyword freebasicGraphics SCREENLIST SCREENLOCK SCREENPTR SCREENRES SCREENSET SCREENSYNC
+ syn keyword freebasicGraphics SCREENUNLOCK WINDOWTITLE
+endif
-syn match freebasicInclude "\<#\s*\(inclib\|include\)\>"
-syn match freebasicInclude "\<\$\s*include\>"
+if s:lang != "qb"
+ syn match freebasicHardware "\<open\s\+\%(com\|lpt\)\>"
+endif
+syn keyword freebasicHardware INP OUT WAIT LPOS LPRINT
-syn keyword freebasicPointer PROCPTR PTR SADD STRPTR VARPTR
+syn keyword freebasicMath ABS ATN COS EXP FIX FRAC INT LOG MOD RANDOMIZE RND SGN SIN SQR TAN
-syn keyword freebasicPredefined __DATE__ __FB_DOS__ __FB_LINUX__ __FB_MAIN__ __FB_MIN_VERSION__
-syn keyword freebasicPredefined __FB_SIGNATURE__ __FB_VERSION__ __FB_WIN32__ __FB_VER_MAJOR__
-syn keyword freebasicPredefined __FB_VER_MINOR__ __FB_VER_PATCH__ __FILE__ __FUNCTION__
-syn keyword freebasicPredefined __LINE__ __TIME__
+if s:lang == "qb"
+ syn keyword freebasicMath __ACOS __ASIN __ATAN2
+else
+ syn keyword freebasicMath ACOS ASIN ATAN2
+endif
-syn match freebasicPreProcessor "\<^#\s*\(define\|undef\)\>"
-syn match freebasicPreProcessor "\<^#\s*\(ifdef\|ifndef\|else\|elseif\|endif\|if\)\>"
-syn match freebasicPreProcessor "\<#\s*error\>"
-syn match freebasicPreProcessor "\<#\s*\(print\|dynamic\|static\)\>"
-syn keyword freebasicPreProcessor DEFINED ONCE
+if s:lang == "qb"
+ syn keyword freebasicMemory __ALLOCATE __CALLOCATE __DEALLOCATE __REALLOCATE
+else
+ syn keyword freebasicMemory ALLOCATE CALLOCATE DEALLOCATE REALLOCATE
+ syn keyword freebasicMemory PEEK POKE CLEAR FB_MEMCOPY FB_MEMCOPYCLEAR FB_MEMMOVE SWAP SADD
+ syn keyword freebasicMemory FIELD FRE
+endif
-syn keyword freebasicProgramFlow END EXIT GOSUB GOTO
-syn keyword freebasicProgramFlow IS RETURN SCOPE SLEEP
+syn keyword freebasicMisc LET TO
+if s:lang == "qb"
+ syn keyword freebasicMisc __OFFSETOF
+else
+ syn keyword freebasicMisc OFFSETOF
+endif
+
+syn keyword freebasicModularizing CHAIN COMMON
+if s:lang == "fb"
+ syn keyword freebasicModularizing EXTERN
+ syn match freebasicModularizing "\<end\s\+extern\>"
+ syn keyword freebasicModularizing PROTECTED
+endif
+if s:lang == "qb"
+ syn keyword freebasicModularizing __EXPORT __IMPORT __DYLIBFREE __DYLIBLOAD __DYLIBSYMBOL
+else
+ syn keyword freebasicModularizing EXPORT IMPORT DYLIBFREE DYLIBLOAD DYLIBSYMBOL
+ syn keyword freebasicModularizing PRIVATE PUBLIC
+ syn keyword freebasicModularizing NAMESPACE
+ syn match freebasicModularizing "\<end\s\+namespace\>"
+endif
+
+if s:lang != "qb"
+ syn keyword freebasicMultithreading MUTEXCREATE MUTEXDESTROY MUTEXLOCK MUTEXUNLOCK THREADCREATE THREADWAIT
+ syn keyword freebasicMultithreading CONDBROADCAST CONDCREATE CONDDESTROY CONDSIGNAL CONDWAIT
+ syn keyword freebasicMultithreading THREADCALL THREADDETACH THREADSELF
+endif
-syn keyword freebasicString INSTR LCASE LEFT LEN LSET LTRIM MID RIGHT RSET RTRIM
-syn keyword freebasicString SPACE STRING TRIM UCASE ASC BIN CHR CVD CVI CVL CVLONGINT
-syn keyword freebasicString CVS CVSHORT FORMAT HEX MKD MKI MKL MKLONGINT MKS MKSHORT
-syn keyword freebasicString OCT STR VAL VALLNG VALINT VALUINT VALULNG
+syn keyword freebasicShell CHDIR KILL NAME MKDIR RMDIR RUN SETENVIRON
+if s:lang == "qb"
+ syn keyword freebasicShell __CURDIR __DIR __EXEC __EXEPATH
+ syn match freebasicString "\<\%(command\|environ\)\$"
+else
+ " fbio.bi
+ syn keyword freebasicShell ISREDIRECTED
+ syn keyword freebasicShell CURDIR DIR EXEC EXEPATH
+ syn match freebasicString "\<\%(command\|environ\)\>\$\="
+endif
-syn keyword freebasicTypeCasting CAST CBYTE CDBL CINT CLNG CLNGINT CPTR CSHORT CSIGN CSNG
-syn keyword freebasicTypeCasting CUBYTE CUINT CULNGINT CUNSG CURDIR CUSHORT
+syn keyword freebasicEnviron SHELL SYSTEM
-syn match freebasicUserInput "\<line\s+input\>"
-syn keyword freebasicUserInput GETJOYSTICK GETKEY GETMOUSE INKEY INPUT MULTIKEY SETMOUSE
+syn keyword freebasicLoops FOR LOOP WHILE WEND DO STEP UNTIL NEXT
+if s:lang == "qb"
+ syn keyword freebasicLoops __CONTINUE
+else
+ syn keyword freebasicLoops CONTINUE
+endif
"
-" Do the Basic variables names first. This is because it
-" is the most inclusive of the tests. Later on we change
-" this so the identifiers are split up into the various
-" types of identifiers like functions, basic commands and
-" such. MEM 9/9/2006
+" File numbers
"
-syn match freebasicIdentifier "\<[a-zA-Z_][a-zA-Z0-9_]*\>"
-syn match freebasicGenericFunction "\<[a-zA-Z_][a-zA-Z0-9_]*\>\s*("me=e-1,he=e-1
+syn match freebasicFilenumber "#\d\+"
+syn match freebasicFilenumber "#\a[[:alpha:].]*[%&!#]\="
+
+syn match freebasicMetacommand "$\s*\%(dynamic\|static\)"
+syn match freebasicMetacommand "$\s*include\s*\%(once\)\=\s*:\s*'[^']\+'"
+syn match freebasicMetacommand '$\s*include\s*\%(once\)\=\s*:\s*"[^"]\+"'
+syn match freebasicMetacommand '$\s*lang\s*:\s*"[^"]\+"'
+"
+" Intrinsic defines
+"
+syn keyword freebasicPredefined __DATE__ __DATE_ISO__
+syn keyword freebasicPredefined __FB_64BIT__ __FB_ARGC__ __FB_ARG_COUNT__ __FB_ARG_EXTRACT__ __FB_ARG_LEFTOF__
+syn keyword freebasicPredefined __FB_ARG_RIGHTOF__ __FB_ARGV__ __FB_ARM__ __FB_ASM__ __FB_BACKEND__
+syn keyword freebasicPredefined __FB_BIGENDIAN__ __FB_BUILD_DATE__ __FB_BUILD_DATE_ISO__ __FB_BUILD_SHA1__
+syn keyword freebasicPredefined __FB_CYGWIN__ __FB_DARWIN__ __FB_DEBUG__ __FB_DOS__ __FB_ERR__ __FB_EVAL__
+syn keyword freebasicPredefined __FB_FPMODE__ __FB_FPU__ __FB_FREEBSD__ __FB_GCC__ __FB_GUI__ __FB_JOIN__
+syn keyword freebasicPredefined __FB_LANG__ __FB_LINUX__ __FB_MAIN__ __FB_MIN_VERSION__ __FB_MT__ __FB_NETBSD__
+syn keyword freebasicPredefined __FB_OPENBSD__ __FB_OPTIMIZE__ __FB_OPTION_BYVAL__ __FB_OPTION_DYNAMIC__
+syn keyword freebasicPredefined __FB_OPTION_ESCAPE__ __FB_OPTION_EXPLICIT__ __FB_OPTION_GOSUB__
+syn keyword freebasicPredefined __FB_OPTION_PRIVATE__ __FB_OUT_DLL__ __FB_OUT_EXE__ __FB_OUT_LIB__ __FB_OUT_OBJ__
+syn keyword freebasicPredefined __FB_PCOS__ __FB_PPC__ __FB_QUOTE__ __FB_SIGNATURE__ __FB_SSE__ __FB_UNIQUEID__
+syn keyword freebasicPredefined __FB_UNIQUEID_POP__ __FB_UNIQUEID_PUSH__ __FB_UNIX__ __FB_UNQUOTE__
+syn keyword freebasicPredefined __FB_VECTORIZE__ __FB_VER_MAJOR__ __FB_VER_MINOR__ __FB_VER_PATCH__ __FB_VERSION__
+syn keyword freebasicPredefined __FB_WIN32__ __FB_X86__ __FB_XBOX__
+syn keyword freebasicPredefined __FILE__ __FILE_NQ__ __FUNCTION__ __FUNCTION_NQ__
+syn keyword freebasicPredefined __LINE__ __PATH__ __TIME__
+"
+" Preprocessor directives
+"
+syn match freebasicInclude "#\s*\%(inclib\|include\%(\s\+once\)\=\|libpath\)\>"
+
+syn match freebasicPreProcessor "#\s*assert\>"
+syn match freebasicPreProcessor "#\s*cmdline\>"
+syn match freebasicPreProcessor "#\s*\%(define\|undef\)\>"
+syn match freebasicPreProcessor "#\s*\%(if\|ifdef\|ifndef\|else\|elseif\|endif\)\>"
+syn match freebasicPreProcessor "#\s*\%(macro\|endmacro\)\>"
+syn match freebasicPreProcessor "#\s*error\>"
+syn match freebasicPreProcessor "#\s*lang\>"
+syn match freebasicPreProcessor "#\s*line\>"
+syn match freebasicPreProcessor "#\s*pragma\%(\s\+reserve\)\=\>"
+syn match freebasicPreProcessor "#\s*\%(print\|dynamic\|static\)\>"
+syn keyword freebasicPreProcessor DEFINED
+
+syn keyword freebasicString LEN
+syn keyword freebasicString ASC
+" string.bi
+syn keyword freebasicString FORMAT
+syn keyword freebasicString VAL
+syn keyword freebasicString CVD CVI CVL CVS
+syn keyword freebasicString INSTR
+syn keyword freebasicString LSET RSET
+
+if s:lang == "qb"
+ syn match freebasicString "\<string\$\ze\s*("
+ syn match freebasicString "\<__wstring\ze\s*("
+ syn match freebasicString "\<space\$"
+ syn keyword freebasicString __WSPACE
+ syn match freebasicString "\<chr\$"
+ syn keyword freebasicString __WCHR
+ syn keyword freebasicString __WBIN __WHEX __WOCT __WSTR
+ syn match freebasicString "\<\%(bin\|hex\|oct\|str\)\$"
+ syn keyword freebasicString __VALLNG __VALINT __VALUINT __VALULNG
+ syn match freebasicString "\<\%(mkd\|mki\|mkl\|mks\)\$"
+ syn keyword freebasicString __MKLONGINT __MKSHORT
+ syn keyword freebasicString __CVLONGINT __CVSHORT
+ syn match freebasicString "\<\%(left\|mid\|right\|lcase\|ucase\|ltrim\|rtrim\)\$"
+ syn keyword freebasicString __TRIM
+ syn keyword freebasicString __INSTRREV
+else
+ syn match freebasicString "\<string\$\=\ze\s*("
+ syn match freebasicString "\<wstring\ze\s*("
+ syn match freebasicString "\<space\>\$\="
+ syn keyword freebasicString WSPACE
+ syn match freebasicString "\<chr\>\$\="
+ syn keyword freebasicString WCHR
+ syn keyword freebasicString WBIN WHEX WOCT WSTR
+ syn match freebasicString "\<\%(bin\|hex\|oct\|str\)\>\$\="
+ syn keyword freebasicString VALLNG VALINT VALUINT VALULNG
+ syn match freebasicString "\<\%(mkd\|mki\|mkl\|mks\)\>\$\="
+ syn match freebasicString "\<\%(mklongint\|mkshort\)\>\$\="
+ syn keyword freebasicString CVLONGINT CVSHORT
+ syn match freebasicString "\<\%(left\|mid\|right\|lcase\|ucase\|ltrim\|rtrim\)\>\$\="
+ syn match freebasicString "\<trim\>\$\="
+ syn keyword freebasicString INSTRREV
+endif
+
+syn keyword freebasicTypeCasting CDBL CINT CLNG CSNG
+if s:lang == "qb"
+ syn keyword freebasicTypeCasting __CAST __CBOOL __CBYTE __CLNGINT __CPTR __CSHORT __CSIGN __CYBTE __CUINT __CULNG
+ syn keyword freebasicTypeCasting __CULNGINT __CUNSG __CUSHORT
+else
+ syn keyword freebasicTypeCasting CAST CBOOL CBYTE CLNGINT CPTR CSHORT CSIGN CUBYTE CUINT CULNG CULNGINT CUNSG CUSHORT
+endif
+
+syn match freebasicUserInput "\<line\s\+input\>"
+syn keyword freebasicUserInput INKEY INPUT
+if s:lang == "qb"
+ syn keyword freebasicUserInput __GETJOYSTICK __GETKEY __GETMOUSE __MULTIKEY __SETMOUSE STICK STRIG
+else
+ syn keyword freebasicUserInput GETJOYSTICK GETKEY GETMOUSE MULTIKEY SETMOUSE
+endif
"
-" Function list
+" Operators
"
-syn keyword freebasicTodo contained TODO
+" TODO: make these context sensitive to remove the overlap of common operators
+" : alpha operators should probably always be highlighted
+" -- DJK 20/11/19
+if s:lang == "qb"
+ syn match freebasicArithmeticOperator "\<\%(MOD\|__SHL\|__SHR\)\>"
+else
+ syn match freebasicArithmeticOperator "\<\%(MOD\|SHL\|SHR\)\>"
+endif
+syn match freebasicBitwiseOperator "\<\%(AND\|EQV\|IMP\|NOT\|OR\|XOR\)\>" " freebaseLogical?
+if s:lang == "qb"
+ syn match freebasicAssignmentOperator "\<\%(MOD\|AND\|EQV\|IMP\|OR\|XOR\|__SHL\|__SHR\)=\@=" " exclude trailing '='
+else
+ syn match freebasicAssignmentOperator "\<\%(MOD\|AND\|EQV\|IMP\|OR\|XOR\|SHL\|SHR\)=\@="
+endif
+syn match freebasicShortcircuitOperator "\<\%(ANDALSO\|ORELSE\)\>"
+if s:lang == "fb"
+ syn match freebasicMemoryOperator '\<\%(new\|delete\)\>'
+endif
+syn keyword freebasicPointerOperator STRPTR VARPTR
+if s:lang == "qb"
+ syn keyword freebasicPointerOperator __PROCPTR
+else
+ syn keyword freebasicPointerOperator PROCPTR
+endif
+syn match freebasicTypeOperator '\<is\>'
+syn match freebasicTypeOperator '\.' nextgroup=freebasicIdentifier skipwhite
+if s:lang == "fb"
+ syn match freebasicTypeOperator '->' nextgroup=freebasicIdentifier skipwhite
+endif
+
+if exists("freebasic_operators")
+ syn match freebasicAssignmentOperator "=>\=\|[-+&/\\*^]="
+ if s:lang == "qb"
+ syn match freebasicAssignmentOperator "\<\%(MOD\|AND\|EQV\|IMP\|OR\|XOR\|__SHL\|__SHR\)=" " include trailing '='
+ else
+ syn match freebasicAssignmentOperator "\<\%(MOD\|AND\|EQV\|IMP\|OR\|XOR\|SHL\|SHR\)="
+ endif
+ syn match freebasicArithmeticOperator "[-+&/\\*^]"
+ " syn match freebasicIndexingOperator "[[\]()]" " FIXME
+ syn match freebasicRelationalOperator "=\|<>\|<=\|<\|>=\|>"
+ syn match freebasicPreprocessorOperator '\%(^\s*\)\@<!\%(##\|#\)\|[$!]"\@='
+ syn match freebasicPointerOperator '[@*]'
+ syn match freebasicTypeOperator '\.' nextgroup=freebasicIdentifier skipwhite
+ if s:lang == "fb"
+ syn match freebasicTypeOperator '->' nextgroup=freebasicIdentifier skipwhite
+ endif
+endif
+
+syn cluster freebasicOperator contains=freebasic.*Operator
"
" Catch errors caused by wrong parenthesis
"
-syn region freebasicParen transparent start='(' end=')' contains=ALLBUT,@freebasicParenGroup
-syn match freebasicParenError ")"
-syn match freebasicInParen contained "[{}]"
-syn cluster freebasicParenGroup contains=freebasicParenError,freebasicSpecial,freebasicTodo,freebasicUserCont,freebasicUserLabel,freebasicBitField
+" syn region freebasicParen transparent start='(' end=')' contains=ALLBUT,@freebasicParenGroup
+" syn match freebasicParenError ")"
+" syn match freebasicInParen contained "[{}]"
+" syn cluster freebasicParenGroup contains=freebasicParenError,freebasicSpecial,freebasicTodo,freebasicUserCont,freebasicUserLabel,freebasicBitField
"
-" Integer number, or floating point number without a dot and with "f".
+" Integer number
"
-syn region freebasicHex start="&h" end="\W"
-syn region freebasicHexError start="&h\x*[g-zG-Z]" end="\W"
-syn region freebasicOctal start="&o" end="\W"
-syn region freebasicOctalError start="&o[0-7]*[89a-zA-Z]" end="\W"
-syn region freebasicBinary start="&b" end="\W"
-syn region freebasicBinaryError start="&b[01]*[2-9a-zA-Z]" end="\W"
-syn match freebasicInteger "\<\d\+\(u\=l\=\|lu\|f\)\>"
+syn match freebasicHexError "&h\w*\>"
+syn match freebasicOctalError "&o\w*\>"
+syn match freebasicBinaryError "&b\w*\>"
+syn match freebasicHex "&h\x\+\%([%L&U]\|UL\|LL\|ULL\)\=\>"
+syn match freebasicOctal "&o\o\+\%([%L&U]\|UL\|LL\|ULL\)\=\>"
+syn match freebasicBinary "&b[10]\+\%([%L&U]\|UL\|LL\|ULL\)\=\>"
+syn match freebasicInteger "\<\d\+\%([%L&U]\|UL\|LL\|ULL\)\=\>"
"
-" Floating point number, with dot, optional exponent
+" Floating point
+" See: https://www.freebasic.net/forum/viewtopic.php?t=20323
"
-syn match freebasicFloat "\<\d\+\.\d*\(e[-+]\=\d\+\)\=[fl]\=\>"
+" Floating point number, with dot, optional exponent, optional suffix
"
-" Floating point number, starting with a dot, optional exponent
+syn match freebasicFloat "\<\d\+\.\d*\%([de][-+]\=\d*\)\=[f!#]\="
"
-syn match freebasicFloat "\.\d\+\(e[-+]\=\d\+\)\=[fl]\=\>"
+" Floating point number, starting with a dot, optional exponent, optional suffix
"
-" Floating point number, without dot, with exponent
+syn match freebasicFloat "\.\d\+\%([de][-+]\=\d*\)\=[f!#]\="
"
-syn match freebasicFloat "\<\d\+e[-+]\=\d\+[fl]\=\>"
+" Floating point number, without dot, with optional exponent, optional suffix
"
-" Octal number
+syn match freebasicFloat "\<\d\+\%([de][-+]\=\d*\)[f!#]\="
"
-syn case match
-syn match freebasicOctal2 "\<0\o*\>"
-syn match freebasicOctal2Error "\<0\o*[89a-zA-Z]"
+" Floating point number, without dot, without exponent, with suffix
"
-" String and Character contstants
+syn match freebasicFloat "\<\d\+[f!#]"
"
-syn region freebasicString start='"' end='"' contains=freebasicSpecial,freebasicTodo
-syn region freebasicString start="'" end="'" contains=freebasicSpecial,freebasicTodo
+" Create the clusters
"
-" Comments
+syn cluster freebasicNumber contains=freebasicHex,freebasicOctal,freebasicBinary,freebasicInteger,freebasicFloat
+syn cluster freebasicNumberError contains=freebasicHexError,freebasicOctalError,freebasicBinaryError
"
-syn match freebasicSpecial contained "\\\\."
-syn region freebasicComment start="^rem" end="$" contains=freebasicSpecial,freebasicTodo
-syn region freebasicComment start=":\s*rem" end="$" contains=freebasicSpecial,freebasicTodo
-syn region freebasicComment start="\s*'" end="$" contains=freebasicSpecial,freebasicTodo
-syn region freebasicComment start="^'" end="$" contains=freebasicSpecial,freebasicTodo
+" Booleans
"
-" Now do the comments and labels
+if s:lang != "qb"
+ syn keyword freebasicBoolean TRUE FALSE
+endif
"
-syn match freebasicLabel "^\d"
-syn match freebasicLabel "\<^\w+:\>"
-syn region freebasicLineNumber start="^\d" end="\s"
"
-" Create the clusters
+" String and escape sequences
"
-syn cluster freebasicNumber contains=freebasicHex,freebasicOctal,freebasicOctal2,freebasicBinary,freebasicInteger,freebasicFloat
-syn cluster freebasicError contains=freebasicHexError,freebasicOctalError,freebasicOctal2,freebasicBinary
+syn match freebasicSpecial contained "\\."
+syn match freebasicSpecial contained "\\\d\{1,3}"
+syn match freebasicSpecial contained "\\&h\x\{1,2}"
+syn match freebasicSpecial contained "\\&o\o\{1,3}"
+syn match freebasicSpecial contained "\\&b[01]\{1,8}"
+syn match freebasicSpecial contained "\\u\x\{1,4}"
+syn region freebasicString start='"' end='"' " TODO: Toggle contains on Option Escape in fblite and qb? -- DJK 20/11/19
+syn region freebasicString start='!\zs"' end='"' contains=freebasicSpecial
+syn region freebasicString start='$\zs"' end='"'
"
-" Used with OPEN statement
+" Line labels
"
-syn match freebasicFilenumber "#\d\+"
-syn match freebasicMathOperator "[\+\-\=\|\*\/\>\<\%\()[\]]" contains=freebasicParen
+if s:lang =~# '\<\%(qb\|fblite\)\>'
+ syn match freebasicLineLabel "^\s*\zs\h\%(\w\|\.\)*\ze\s*:"
+else
+ syn match freebasicLineLabel "^\s*\zs\h\w*\ze\s*:"
+endif
+syn match freebasicLineNumber "^\s*\zs\d\+"
+"
+" Line continuations
+"
+" syn match freebasicLineContinuation "\<_\>" nextgroup=freebasicComment,freebasicPostLineContinuation skipwhite
+syn keyword freebasicLineContinuation _ nextgroup=freebasicComment,freebasicPostLineContinuation skipwhite
+syn match freebasicPostLineContinuation ".*" contained
+"
+"
+" Type suffixes
+if exists("freebasic_type_suffixes") && s:lang =~# '\<\%(qb\|fblite\)\>'
+ syn match freebasicTypeSuffix "\h\%(\w\|.\)*\zs[$%&!#]"
+endif
+"
+" Comments
+"
+syn keyword freebasicTodo TODO FIXME XXX NOTE contained
+syn region freebasicComment start="\<rem\>" end="$" contains=freebasicTodo,@Spell,freebasicMetacommand
+syn region freebasicComment start="'" end="$" contains=freebasicTodo,@Spell,freebasicMetacommand
+syn region freebasicDoubleComment start="''" end="$" contains=freebasicTodo,@Spell
+
+if !exists("freebasic_no_comment_fold")
+ syn region freebasicMultilineComment start="/'" end="'/" contains=freebasicTodo,@Spell,freeBasicMultilineComment fold keepend extend
+ syn region freebasicMultilineComment2 start="^\s*'.*\n\%(\s*'\)\@=" end="^\s*'.*\n\%(\s*'\)\@!" contains=freebasicComment,freebasicDoubleComment keepend fold
+else
+ syn region freebasicMultilineComment start="/'" end="'/" contains=freebasicTodo,@Spell,freeBasicMultilineComment
+endif
+
+syn case match
+
+syn sync linebreaks=1
+
"
" The default methods for highlighting. Can be overridden later
"
hi def link freebasicArrays StorageClass
+hi def link freebasicAsm Special
hi def link freebasicBitManipulation Operator
-hi def link freebasicCompilerSwitches PreCondit
+hi def link freebasicBoolean Boolean
+if s:lang == "fb"
+ hi def link freebasicCompilerSwitches freebasicUnsupportedError
+else
+ hi def link freebasicCompilerSwitches PreCondit
+endif
hi def link freebasicConsole Special
+hi def link freebasicData Special
hi def link freebasicDataTypes Type
hi def link freebasicDateTime Type
hi def link freebasicDebug Special
hi def link freebasicErrorHandling Special
+hi def link freebasicFilenumber Special
hi def link freebasicFiles Special
hi def link freebasicFunctions Function
hi def link freebasicGraphics Function
hi def link freebasicHardware Special
-hi def link freebasicLogical Conditional
+hi def link freebasicLoops Repeat
hi def link freebasicMath Function
+if s:lang == "fb"
+ hi def link freebasicMetacommand freebasicUnsupportedError
+else
+ hi def link freebasicMetacommand SpecialComment
+endif
hi def link freebasicMemory Function
hi def link freebasicMisc Special
hi def link freebasicModularizing Special
@@ -240,18 +620,55 @@ hi def link freebasicString String
hi def link freebasicTypeCasting Type
hi def link freebasicUserInput Statement
hi def link freebasicComment Comment
+hi def link freebasicDoubleComment Comment
+hi def link freebasicMultilineComment Comment
hi def link freebasicConditional Conditional
hi def link freebasicError Error
hi def link freebasicIdentifier Identifier
hi def link freebasicInclude Include
hi def link freebasicGenericFunction Function
-hi def link freebasicLabel Label
-hi def link freebasicLineNumber Label
+hi def link freebasicLineContinuation Special
+hi def link freebasicLineLabel LineNr
+if s:lang == "fb"
+ hi def link freebasicLineNumber freebasicUnsupportedError
+else
+ hi def link freebasicLineNumber LineNr
+endif
hi def link freebasicMathOperator Operator
-hi def link freebasicNumber Number
+
+hi def link freebasicHex Number
+hi def link freebasicOctal Number
+hi def link freebasicBinary Number
+hi def link freebasicInteger Number
+hi def link freebasicFloat Float
+
+hi def link freebasicHexError Error
+hi def link freebasicOctalError Error
+hi def link freebasicBinaryError Error
+
+hi def link freebasicAssignmentOperator Operator
+hi def link freebasicArithmeticOperator Operator
+hi def link freebasicIndexingOperator Operator
+hi def link freebasicRelationalOperator Operator
+hi def link freebasicBitwiseOperator Operator
+hi def link freebasicShortcircuitOperator Operator
+hi def link freebasicPreprocessorOperator Operator
+hi def link freebasicPointerOperator Operator
+if exists("freebasic_operators")
+ hi def link freebasicTypeOperator Operator
+endif
+hi def link freebasicMemoryOperator Operator
+
+hi def link freebasicSpaceError Error
+
hi def link freebasicSpecial Special
hi def link freebasicTodo Todo
+hi def link freebasicUnsupported freebasicUnsupportedError
+hi def link freebasicUnsupportedError Error
+
+unlet s:lang
+
let b:current_syntax = "freebasic"
-" vim: ts=8
+" vim: ts=8 tw=132 fdm=marker
diff --git a/runtime/syntax/i3config.vim b/runtime/syntax/i3config.vim
index f9e15d57e5..0018081da7 100644
--- a/runtime/syntax/i3config.vim
+++ b/runtime/syntax/i3config.vim
@@ -3,7 +3,7 @@
" Original Author: Mohamed Boughaba <mohamed dot bgb at gmail dot com>
" Maintainer: Quentin Hibon (github user hiqua)
" Version: 0.4
-" Last Change: 2022 Jan 04
+" Last Change: 2022 Jun 05
" References:
" http://i3wm.org/docs/userguide.html#configuring
@@ -17,9 +17,6 @@ endif
scriptencoding utf-8
-" Error
-syn match i3ConfigError /.*/
-
" Todo
syn keyword i3ConfigTodo TODO FIXME XXX contained
@@ -50,6 +47,10 @@ syn match i3ConfigVariable /\$\w\+\(\(-\w\+\)\+\)\?\(\s\|+\)\?/ contains=i3Confi
syn keyword i3ConfigInitializeKeyword set contained
syn match i3ConfigInitialize /^\s*set\s\+.*$/ contains=i3ConfigVariable,i3ConfigInitializeKeyword,i3ConfigColor,i3ConfigString
+" Include
+syn keyword i3ConfigIncludeKeyword include contained
+syn match i3ConfigInclude /^\s*include\s\+.*$/ contains=i3ConfigIncludeKeyword,i3ConfigString,i3ConfigVariable
+
" Gaps
syn keyword i3ConfigGapStyleKeyword inner outer horizontal vertical top right bottom left current all set plus minus toggle up down contained
syn match i3ConfigGapStyle /^\s*\(gaps\)\s\+\(inner\|outer\|horizontal\|vertical\|left\|top\|right\|bottom\)\(\s\+\(current\|all\)\)\?\(\s\+\(set\|plus\|minus\|toggle\)\)\?\(\s\+\(-\?\d\+\|\$.*\)\)$/ contains=i3ConfigGapStyleKeyword,i3ConfigNumber,i3ConfigVariable
@@ -175,14 +176,13 @@ syn keyword i3ConfigDrawingMarksKeyword show_marks contained
syn match i3ConfigDrawingMarks /^\s*show_marks\s\+\(yes\|no\)\s\?$/ contains=i3ConfigFocusWrappingType,i3ConfigDrawingMarksKeyword
" Group mode/bar
-syn keyword i3ConfigBlockKeyword mode bar colors i3bar_command status_command position exec mode hidden_state modifier id position output background statusline tray_output tray_padding separator separator_symbol workspace_buttons strip_workspace_numbers binding_mode_indicator focused_workspace active_workspace inactive_workspace urgent_workspace binding_mode contained
-syn region i3ConfigBlock start=+.*s\?{$+ end=+^}$+ contains=i3ConfigBlockKeyword,i3ConfigString,i3ConfigBind,i3ConfigComment,i3ConfigFont,i3ConfigFocusWrappingType,i3ConfigColor,i3ConfigVariable transparent keepend extend
+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*[^#]*s\?{$+ end=+^\s*[^#]*}$+ contains=i3ConfigBlockKeyword,i3ConfigString,i3ConfigBind,i3ConfigComment,i3ConfigFont,i3ConfigFocusWrappingType,i3ConfigColor,i3ConfigVariable transparent keepend extend
" Line continuation
syn region i3ConfigLineCont start=/^.*\\$/ end=/^.*$/ contains=i3ConfigBlockKeyword,i3ConfigString,i3ConfigBind,i3ConfigComment,i3ConfigFont,i3ConfigFocusWrappingType,i3ConfigColor,i3ConfigVariable transparent keepend extend
" Define the highlighting.
-hi def link i3ConfigError Error
hi def link i3ConfigTodo Todo
hi def link i3ConfigComment Comment
hi def link i3ConfigFontContent Type
@@ -221,6 +221,7 @@ hi def link i3ConfigAssignSpecial Special
hi def link i3ConfigFontNamespace PreProc
hi def link i3ConfigBindArgument PreProc
hi def link i3ConfigNoStartupId PreProc
+hi def link i3ConfigIncludeKeyword Identifier
hi def link i3ConfigFontKeyword Identifier
hi def link i3ConfigBindKeyword Identifier
hi def link i3ConfigOrientation Identifier
diff --git a/runtime/syntax/icon.vim b/runtime/syntax/icon.vim
index 179d75cd28..276bb58497 100644
--- a/runtime/syntax/icon.vim
+++ b/runtime/syntax/icon.vim
@@ -1,199 +1,211 @@
" Vim syntax file
-" Language: Icon
-" Maintainer: Wendell Turner <wendell@adsi-m4.com>
-" URL: ftp://ftp.halcyon.com/pub/users/wturner/icon.vim
-" Last Change: 2003 May 11
+" Language: Icon
+" Maintainer: Doug Kearns <dougkearns@gmail.com>
+" Previous Maintainer: Wendell Turner <wendell@adsi-m4.com> (invalid last known address)
+" Last Change: 2022 Jun 16
+" Contributor: eschen@alumni.princeton.edu 2002.09.18
-" quit when a syntax file was already loaded
+" Prelude {{{1
if exists("b:current_syntax")
finish
endif
-syn keyword iconFunction abs acos any args asin atan bal
-syn keyword iconFunction callout center char chdir close collect copy
-syn keyword iconFunction cos cset delay delete detab display dtor
-syn keyword iconFunction entab errorclear exit exp find flush function
-syn keyword iconFunction get getch getche getenv iand icom image
-syn keyword iconFunction insert integer ior ishift ixor kbhit key
-syn keyword iconFunction left list loadfunc log many map match
-syn keyword iconFunction member move name numeric open ord pop
-syn keyword iconFunction pos proc pull push put read reads
-syn keyword iconFunction real remove rename repl reverse right rtod
-syn keyword iconFunction runerr save seek seq set sin sort
-syn keyword iconFunction sortf sqrt stop string system tab table
-syn keyword iconFunction tan trim type upto variable where write writes
-
-" Keywords
-syn match iconKeyword "&allocated"
-syn match iconKeyword "&ascii"
-syn match iconKeyword "&clock"
-syn match iconKeyword "&collections"
-syn match iconKeyword "&cset"
-syn match iconKeyword "&current"
-syn match iconKeyword "&date"
-syn match iconKeyword "&dateline"
-syn match iconKeyword "&digits"
-syn match iconKeyword "&dump"
-syn match iconKeyword "&e"
-syn match iconKeyword "&error"
-syn match iconKeyword "&errornumber"
-syn match iconKeyword "&errortext"
-syn match iconKeyword "&errorvalue"
-syn match iconKeyword "&errout"
-syn match iconKeyword "&fail"
-syn match iconKeyword "&features"
-syn match iconKeyword "&file"
-syn match iconKeyword "&host"
-syn match iconKeyword "&input"
-syn match iconKeyword "&lcase"
-syn match iconKeyword "&letters"
-syn match iconKeyword "&level"
-syn match iconKeyword "&line"
-syn match iconKeyword "&main"
-syn match iconKeyword "&null"
-syn match iconKeyword "&output"
-syn match iconKeyword "&phi"
-syn match iconKeyword "&pi"
-syn match iconKeyword "&pos"
-syn match iconKeyword "&progname"
-syn match iconKeyword "&random"
-syn match iconKeyword "&regions"
-syn match iconKeyword "&source"
-syn match iconKeyword "&storage"
-syn match iconKeyword "&subject"
-syn match iconKeyword "&time"
-syn match iconKeyword "&trace"
-syn match iconKeyword "&ucase"
-syn match iconKeyword "&version"
-
-" Reserved words
-syn keyword iconReserved break by case create default do
-syn keyword iconReserved else end every fail if
-syn keyword iconReserved initial link next not of
-syn keyword iconReserved procedure repeat return suspend
-syn keyword iconReserved then to until while
-
-" Storage class reserved words
-syn keyword iconStorageClass global static local record
-
-syn keyword iconTodo contained TODO FIXME XXX BUG
-
-" String and Character constants
-" Highlight special characters (those which have a backslash) differently
-syn match iconSpecial contained "\\x\x\{2}\|\\\o\{3\}\|\\[bdeflnrtv\"\'\\]\|\\^c[a-zA-Z0-9]\|\\$"
-syn region iconString start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=iconSpecial
-syn region iconCset start=+'+ skip=+\\\\\|\\'+ end=+'+ contains=iconSpecial
-syn match iconCharacter "'[^\\]'"
-
-" not sure about these
-"syn match iconSpecialCharacter "'\\[bdeflnrtv]'"
-"syn match iconSpecialCharacter "'\\\o\{3\}'"
-"syn match iconSpecialCharacter "'\\x\x\{2}'"
-"syn match iconSpecialCharacter "'\\^c\[a-zA-Z0-9]'"
-
-"when wanted, highlight trailing white space
+syn iskeyword @,48-57,_,192-255,&
+
+" Not Top {{{1
+syn cluster iconNotTop contains=iconDocField,iconIncluded,iconStringSpecial,iconTodo,@Spell
+
+" Whitespace errors {{{1
if exists("icon_space_errors")
- syn match iconSpaceError "\s*$"
- syn match iconSpaceError " \+\t"me=e-1
+ if !exists("icon_no_trail_space_error")
+ syn match iconSpaceError "\s\+$" display excludenl
+ endif
+ if !exists("icon_no_tab_space_error")
+ syn match iconSpaceError " \+\t"me=e-1 display
+ endif
endif
-"catch errors caused by wrong parenthesis
-syn cluster iconParenGroup contains=iconParenError,iconIncluded,iconSpecial,iconTodo,iconUserCont,iconUserLabel,iconBitField
+" Reserved words {{{1
+syn keyword iconReserved break by case create default do else every fail if
+syn keyword iconReserved initial next not of repeat return suspend then to
+syn keyword iconReserved until while
-syn region iconParen transparent start='(' end=')' contains=ALLBUT,@iconParenGroup
-syn match iconParenError ")"
-syn match iconInParen contained "[{}]"
+syn keyword iconStorageClass global static local record invocable
+syn keyword iconLink link
-syn case ignore
+" Procedure definitions {{{1
+if exists("icon_no_procedure_fold")
+ syn region iconProcedure matchgroup=iconReserved start="\<procedure\>" end="\<end\>" contains=ALLBUT,@iconNotTop
+else
+ syn region iconProcedure matchgroup=iconReserved start="\<procedure\>" end="\<end\>" contains=ALLBUT,@iconNotTop fold
+endif
-"integer number, or floating point number without a dot
-syn match iconNumber "\<\d\+\>"
+" Keywords {{{1
+syn keyword iconKeyword &allocated &ascii &clock &collections &cset &current
+syn keyword iconKeyword &date &dateline &digits &dump &e &error &errornumber
+syn keyword iconKeyword &errortext &errorvalue &errout &fail &features &file
+syn keyword iconKeyword &host &input &lcase &letters &level &line &main &null
+syn keyword iconKeyword &output &phi &pi &pos &progname &random &regions
+syn keyword iconKeyword &source &storage &subject &time &trace &ucase &version
+
+" Graphics keywords
+syn keyword iconKeyword &col &control &interval &ldrag &lpress &lrelease
+syn keyword iconKeyword &mdrag &meta &mpress &mrelease &rdrag &resize &row
+syn keyword iconKeyword &rpress &rrelease &shift &window &x &y
+
+" Functions {{{1
+syn keyword iconFunction abs acos any args asin atan bal callout center char
+syn keyword iconFunction chdir close collect copy cos cset delay delete detab
+syn keyword iconFunction display dtor entab errorclear exit exp find flush
+syn keyword iconFunction function get getch getche getenv iand icom image
+syn keyword iconFunction insert integer ior ishift ixor kbhit key left list
+syn keyword iconFunction loadfunc log many map match member move name numeric
+syn keyword iconFunction open ord pop pos proc pull push put read reads real
+syn keyword iconFunction remove rename repl reverse right rtod runerr save
+syn keyword iconFunction seek self seq serial set sin sort sortf sqrt stop
+syn keyword iconFunction string system tab table tan trim type upto variable
+syn keyword iconFunction where write writes
+
+" Graphics functions
+syn keyword iconFunction Active Alert Bg CenterString Clip Clone Color
+syn keyword iconFunction ColorDialog ColorValue CopyArea Couple DrawArc
+syn keyword iconFunction DrawCircle DrawCurve DrawImage DrawLine DrawPoint
+syn keyword iconFunction DrawPolygon DrawRectangle DrawSegment DrawString
+syn keyword iconFunction Enqueue EraseArea Event Fg FillArc FillCircle
+syn keyword iconFunction FillPolygon FillRectangle Font FreeColor GotoRC
+syn keyword iconFunction GotoXY LeftString Lower NewColor Notice OpenDialog
+syn keyword iconFunction PaletteChars PaletteColor PaletteGrays PaletteKey
+syn keyword iconFunction Pattern Pending Pixel Raise ReadImage RightString
+syn keyword iconFunction SaveDialog SelectDialog Shade TextDialog TextWidth
+syn keyword iconFunction ToggleDialog Uncouple WAttrib WClose WDefault WDelay
+syn keyword iconFunction WDone WFlush WOpen WQuit WRead WReads WriteImage
+syn keyword iconFunction WSync WWrite WWrites
+
+" String and character constants {{{1
+syn match iconStringSpecial "\\x\x\{2}\|\\\o\{3\}\|\\[bdeflnrtv\"\'\\]\|\\^[a-zA-Z0-9]" contained
+syn match iconStringSpecial "\\$" contained
+syn match iconStringSpecial "_\ze\s*$" contained
+
+syn region iconString start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=iconStringSpecial
+syn region iconCset start=+'+ skip=+\\\\\|\\'+ end=+'+ contains=iconStringSpecial
+
+" Bracket errors {{{1
+
+if !exists("icon_no_bracket_errors")
+ " catch errors caused by wrong brackets (ACE 2002.09.18)
+ syn cluster iconBracketGroup contains=iconBracketError,iconIncluded
+ syn region iconBracket start='\[' end='\]' contains=ALLBUT,@iconBracketGroup,@iconNotTop transparent
+ syn match iconBracketError "]"
+
+ "catch errors caused by wrong braces (ACE 2002.09.18)
+ syn cluster iconBraceGroup contains=iconBraceError,iconIncluded
+ syn region iconBrace start='{' end='}' contains=ALLBUT,@iconBraceGroup,@iconNotTop transparent
+ syn match iconBraceError "}"
+
+ "catch errors caused by wrong parenthesis
+ syn cluster iconParenGroup contains=iconParenError,iconIncluded
+ syn region iconParen start='(' end=')' contains=ALLBUT,@iconParenGroup,@iconNotTop transparent
+ syn match iconParenError ")"
+end
+
+" Numbers {{{1
+syn case ignore
-"floating point number, with dot, optional exponent
-syn match iconFloat "\<\d\+\.\d*\(e[-+]\=\d\+\)\=\>"
+" integer
+syn match iconInteger "\<\d\+\>"
+syn match iconInteger "\<\d\{1,2}[rR][a-zA-Z0-9]\+\>"
-"floating point number, starting with a dot, optional exponent
-syn match iconFloat "\.\d\+\(e[-+]\=\d\+\)\=\>"
+" real with trailing dot
+syn match iconReal "\<\d\+\."
-"floating point number, without dot, with exponent
-syn match iconFloat "\<\d\+e[-+]\=\d\+\>"
+" real, with dot, optional exponent
+syn match iconReal "\<\d\+\.\d*\%(e[-+]\=\d\+\)\=\>"
-"radix number
-syn match iconRadix "\<\d\{1,2}[rR][a-zA-Z0-9]\+\>"
+" real, with leading dot, optional exponent
+syn match iconReal "\.\d\+\%(e[-+]\=\d\+\)\=\>"
+" real, without dot, with exponent
+syn match iconReal "\<\d\+e[-+]\=\d\+\>"
-" syn match iconIdentifier "\<[a-z_][a-z0-9_]*\>"
+syn cluster iconNumber contains=iconInteger,iconReal
syn case match
-" Comment
-syn match iconComment "#.*" contains=iconTodo,iconSpaceError
-
-syn region iconPreCondit start="^\s*$\s*\(if\>\|ifdef\>\|ifndef\>\|elif\>\|else\>\|endif\>\)" skip="\\$" end="$" contains=iconComment,iconString,iconCharacter,iconNumber,iconCommentError,iconSpaceError
-
-syn region iconIncluded contained start=+"+ skip=+\\\\\|\\"+ end=+"+
-syn match iconIncluded contained "<[^>]*>"
-syn match iconInclude "^\s*$\s*include\>\s*["<]" contains=iconIncluded
-"syn match iconLineSkip "\\$"
-
-syn cluster iconPreProcGroup contains=iconPreCondit,iconIncluded,iconInclude,iconDefine,iconInParen,iconUserLabel
-
-syn region iconDefine start="^\s*$\s*\(define\>\|undef\>\)" skip="\\$" end="$" contains=ALLBUT,@iconPreProcGroup
+" Comments {{{1
+syn keyword iconTodo TODO FIXME XXX BUG contained
+syn match iconComment "#.*" contains=iconTodo,iconSpaceError,@Spell
+syn match iconDocField "^#\s\+\zs\%(File\|Subject\|Authors\=\|Date\|Version\|Links\|Requires\|See also\):" contained
-"wt:syn region iconPreProc "start="^\s*#\s*\(pragma\>\|line\>\|warning\>\|warn\>\|error\>\)" skip="\\$" "end="$" contains=ALLBUT,@iconPreProcGroup
-
-" Highlight User Labels
-
-" syn cluster iconMultiGroup contains=iconIncluded,iconSpecial,iconTodo,iconUserCont,iconUserLabel,iconBitField
+if exists("icon_no_comment_fold")
+ syn region iconDocumentation start="\%^#\{2,}\%(\n#\+\%(\s\+.*\)\=\)\+" end="^#\+\n\s*$" contains=iconDocField keepend
+else
+ syn region iconMultilineComment start="^\s*#.*\n\%(^\s*#\)\@=" end="^\s*#.*\n\%(^\s*#\)\@!" contains=iconComment keepend fold transparent
+ syn region iconDocumentation start="\%^#\{2,}\%(\n#\)\+" end="^#\+\n\%([^#]\|$\)" contains=iconDocField keepend fold
+endif
+" Preprocessor {{{1
+syn match iconPreInclude '^\s*\zs$\s*include\>\ze\s*"' nextgroup=iconIncluded skipwhite
+syn match iconIncluded '"[^"]\+"' contained
+
+syn region iconPreDefine start="^\s*\zs$\s*\%(define\|undef\)\>" end="$" oneline contains=ALLBUT,@iconPreGroup
+syn region iconPreProc start="^\s*\zs$\s*\%(error\|line\)\>" end="$" oneline contains=ALLBUT,@iconPreGroup
+syn region iconPreConditional start="^\s*\zs$\s*\%(if\|ifdef\|ifndef\|elif\|else\|endif\)\>" end="$" oneline contains=iconComment,iconString,iconCset,iconNumber,iconSpaceError
+
+syn cluster iconPreGroup contains=iconPreCondit,iconPreInclude,iconIncluded,iconPreDefine
+
+syn match iconPreSymbol "_V\d\+"
+syn keyword iconPreSymbol _ACORN _AMIGA _ARM_FUNCTIONS _ASCII _CALLING
+syn keyword iconPreSymbol _CO_EXPRESSIONS _COMPILED _DIRECT_EXECUTION
+syn keyword iconPreSymbol _DOS_FUNCTIONS _EBCDIC _EVENT_MONITOR
+syn keyword iconPreSymbol _EXECUTABLE_IMAGES _EXTERNAL_FUNCTIONS
+syn keyword iconPreSymbol _EXTERNAL_VALUES _INTERPRETED _KEYBOARD_FUNCTIONS
+syn keyword iconPreSymbol _LARGE_INTEGERS _MACINTOSH _MEMORY_MONITOR _MSDOS
+syn keyword iconPreSymbol _MSDOS_386 _MULTIREGION _MULTITASKING _OS2 _PIPES
+syn keyword iconPreSymbol _PORT _PRESENTATION_MGR _RECORD_IO _STRING_INVOKE
+syn keyword iconPreSymbol _SYSTEM_FUNCTION _UNIX _VISUALIZATION _VMS
+syn keyword iconPreSymbol _WINDOW_FUNCTIONS _X_WINDOW_SYSTEM
+
+" Syncing {{{1
if !exists("icon_minlines")
- let icon_minlines = 15
+ let icon_minlines = 250
endif
exec "syn sync ccomment iconComment minlines=" . icon_minlines
-" Define the default highlighting.
-
-" Only when an item doesn't have highlighting
-
-" The default methods for highlighting. Can be overridden later
+" Default Highlighting {{{1
-" hi def link iconSpecialCharacter iconSpecial
+hi def link iconParenError iconError
+hi def link iconBracketError iconError
+hi def link iconBraceError iconError
+hi def link iconSpaceError iconError
+hi def link iconError Error
-hi def link iconOctalError iconError
-hi def link iconParenError iconError
-hi def link iconInParen iconError
-hi def link iconCommentError iconError
-hi def link iconSpaceError iconError
-hi def link iconCommentError iconError
-hi def link iconIncluded iconString
-hi def link iconCommentString iconString
-hi def link iconComment2String iconString
-hi def link iconCommentSkip iconComment
+hi def link iconInteger Number
+hi def link iconReal Float
+hi def link iconString String
+hi def link iconCset String
+hi def link iconStringSpecial SpecialChar
-hi def link iconUserLabel Label
-hi def link iconCharacter Character
-hi def link iconNumber Number
-hi def link iconRadix Number
-hi def link iconFloat Float
-hi def link iconInclude Include
hi def link iconPreProc PreProc
-hi def link iconDefine Macro
-hi def link iconError Error
-hi def link iconStatement Statement
-hi def link iconPreCondit PreCondit
-hi def link iconString String
-hi def link iconCset String
-hi def link iconComment Comment
-hi def link iconSpecial SpecialChar
-hi def link iconTodo Todo
-hi def link iconStorageClass StorageClass
-hi def link iconFunction Statement
-hi def link iconReserved Label
-hi def link iconKeyword Operator
+hi def link iconIncluded iconString
+hi def link iconPreInclude Include
+hi def link iconPreSymbol iconPreProc
+hi def link iconPreDefine Define
+hi def link iconPreConditional PreCondit
-"hi def link iconIdentifier Identifier
+hi def link iconStatement Statement
+hi def link iconStorageClass StorageClass
+hi def link iconFunction Function
+hi def link iconReserved Label
+hi def link iconLink Include
+hi def link iconKeyword Keyword
+hi def link iconComment Comment
+hi def link iconTodo Todo
+hi def link iconDocField SpecialComment
+hi def link iconDocumentation Comment
+" Postscript {{{1
let b:current_syntax = "icon"
+" vim: nowrap sw=2 sts=2 ts=8 noet fdm=marker:
diff --git a/runtime/syntax/initng.vim b/runtime/syntax/initng.vim
index 5d7685cde1..959c79a176 100644
--- a/runtime/syntax/initng.vim
+++ b/runtime/syntax/initng.vim
@@ -20,7 +20,6 @@ endif
syn case match
let is_bash = 1
-unlet! b:current_syntax
syn include @shTop syntax/sh.vim
syn region initngService matchgroup=initngServiceHeader start="^\s*\(service\|virtual\|daemon\|class\|cron\)\s\+\(\(\w\|[-/*]\)\+\(\s\+:\s\+\(\w\|[-/*]\)\+\)\?\)\s\+{" end="}" contains=@initngServiceCluster
diff --git a/runtime/syntax/ipfilter.vim b/runtime/syntax/ipfilter.vim
index a7b7df2bac..c00762623f 100644
--- a/runtime/syntax/ipfilter.vim
+++ b/runtime/syntax/ipfilter.vim
@@ -1,7 +1,7 @@
" ipfilter syntax file
" Language: ipfilter configuration file
" Maintainer: Hendrik Scholz <hendrik@scholz.net>
-" Last Change: 2005 Jan 27
+" Last Change: 2022 Jun 14
"
" http://www.wormulon.net/files/misc/ipfilter.vim
"
@@ -52,3 +52,4 @@ hi def link IPFNetmask String
hi def link IPFAny Statement
hi def link IPFProto Identifier
+let b:current_syntax = 'ipfilter'
diff --git a/runtime/syntax/java.vim b/runtime/syntax/java.vim
index c9bb5dc2d4..00d6071944 100644
--- a/runtime/syntax/java.vim
+++ b/runtime/syntax/java.vim
@@ -2,7 +2,7 @@
" Language: Java
" Maintainer: Claudio Fleiner <claudio@fleiner.com>
" URL: https://github.com/fleiner/vim/blob/master/runtime/syntax/java.vim
-" Last Change: 2018 July 26
+" Last Change: 2022 Jun 08
" Please check :help java.vim for comments on some of the options available.
@@ -23,8 +23,6 @@ set cpo&vim
syn match javaError "[\\@`]"
syn match javaError "<<<\|\.\.\|=>\|||=\|&&=\|\*\/"
-syn match javaOK "\.\.\."
-
" use separate name so that it can be deleted in javacc.vim
syn match javaError2 "#\|=<"
hi def link javaError2 javaError
@@ -59,8 +57,12 @@ syn match javaUserLabelRef "\k\+" contained
syn match javaVarArg "\.\.\."
syn keyword javaScopeDecl public protected private abstract
+function s:isModuleInfoDeclarationCurrentBuffer() abort
+ return fnamemodify(bufname("%"), ":t") =~ '^module-info\%(\.class\>\)\@!'
+endfunction
+
" Java Modules(Since Java 9, for "module-info.java" file)
-if fnamemodify(bufname("%"), ":t") == "module-info.java"
+if s:isModuleInfoDeclarationCurrentBuffer()
syn keyword javaModuleStorageClass module transitive
syn keyword javaModuleStmt open requires exports opens uses provides
syn keyword javaModuleExternal to with
@@ -72,20 +74,42 @@ if exists("java_highlight_java_lang_ids")
endif
if exists("java_highlight_all") || exists("java_highlight_java") || exists("java_highlight_java_lang")
" java.lang.*
- syn match javaLangClass "\<System\>"
- syn keyword javaR_JavaLang NegativeArraySizeException ArrayStoreException IllegalStateException RuntimeException IndexOutOfBoundsException UnsupportedOperationException ArrayIndexOutOfBoundsException ArithmeticException ClassCastException EnumConstantNotPresentException StringIndexOutOfBoundsException IllegalArgumentException IllegalMonitorStateException IllegalThreadStateException NumberFormatException NullPointerException TypeNotPresentException SecurityException
+ "
+ " The keywords of javaR_JavaLang, javaC_JavaLang, javaE_JavaLang,
+ " and javaX_JavaLang are sub-grouped according to the Java version
+ " of their introduction, and sub-group keywords (that is, class
+ " names) are arranged in alphabetical order, so that future newer
+ " keywords can be pre-sorted and appended without disturbing
+ " the current keyword placement. The below _match_es follow suit.
+
+ syn keyword javaR_JavaLang ArithmeticException ArrayIndexOutOfBoundsException ArrayStoreException ClassCastException IllegalArgumentException IllegalMonitorStateException IllegalThreadStateException IndexOutOfBoundsException NegativeArraySizeException NullPointerException NumberFormatException RuntimeException SecurityException StringIndexOutOfBoundsException IllegalStateException UnsupportedOperationException EnumConstantNotPresentException TypeNotPresentException IllegalCallerException LayerInstantiationException
syn cluster javaTop add=javaR_JavaLang
syn cluster javaClasses add=javaR_JavaLang
hi def link javaR_JavaLang javaR_Java
- syn keyword javaC_JavaLang Process RuntimePermission StringKeySet CharacterData01 Class ThreadLocal ThreadLocalMap CharacterData0E Package Character StringCoding Long ProcessImpl ProcessEnvironment Short AssertionStatusDirectives 1PackageInfoProxy UnicodeBlock InheritableThreadLocal AbstractStringBuilder StringEnvironment ClassLoader ConditionalSpecialCasing CharacterDataPrivateUse StringBuffer StringDecoder Entry StringEntry WrappedHook StringBuilder StrictMath State ThreadGroup Runtime CharacterData02 MethodArray Object CharacterDataUndefined Integer Gate Boolean Enum Variable Subset StringEncoder Void Terminator CharsetSD IntegerCache CharacterCache Byte CharsetSE Thread SystemClassLoaderAction CharacterDataLatin1 StringValues StackTraceElement Shutdown ShortCache String ConverterSD ByteCache Lock EnclosingMethodInfo Math Float Value Double SecurityManager LongCache ProcessBuilder StringEntrySet Compiler Number UNIXProcess ConverterSE ExternalData CaseInsensitiveComparator CharacterData00 NativeLibrary
+ " Member enumerations:
+ syn match javaC_JavaLang "\%(\<Thread\.\)\@<=\<State\>"
+ syn match javaC_JavaLang "\%(\<Character\.\)\@<=\<UnicodeScript\>"
+ syn match javaC_JavaLang "\%(\<ProcessBuilder\.Redirect\.\)\@<=\<Type\>"
+ syn match javaC_JavaLang "\%(\<StackWalker\.\)\@<=\<Option\>"
+ syn match javaC_JavaLang "\%(\<System\.Logger\.\)\@<=\<Level\>"
+ " Member classes:
+ syn match javaC_JavaLang "\%(\<Character\.\)\@<=\<Subset\>"
+ syn match javaC_JavaLang "\%(\<Character\.\)\@<=\<UnicodeBlock\>"
+ syn match javaC_JavaLang "\%(\<ProcessBuilder\.\)\@<=\<Redirect\>"
+ syn match javaC_JavaLang "\%(\<ModuleLayer\.\)\@<=\<Controller\>"
+ syn match javaC_JavaLang "\%(\<Runtime\.\)\@<=\<Version\>"
+ syn match javaC_JavaLang "\%(\<System\.\)\@<=\<LoggerFinder\>"
+ syn match javaC_JavaLang "\%(\<Enum\.\)\@<=\<EnumDesc\>"
+ syn keyword javaC_JavaLang Boolean Character Class ClassLoader Compiler Double Float Integer Long Math Number Object Process Runtime SecurityManager String StringBuffer Thread ThreadGroup Byte Short Void InheritableThreadLocal Package RuntimePermission ThreadLocal StrictMath StackTraceElement Enum ProcessBuilder StringBuilder ClassValue Module ModuleLayer StackWalker Record
+ syn match javaC_JavaLang "\<System\>" " See javaDebug.
syn cluster javaTop add=javaC_JavaLang
syn cluster javaClasses add=javaC_JavaLang
hi def link javaC_JavaLang javaC_Java
- syn keyword javaE_JavaLang IncompatibleClassChangeError InternalError UnknownError ClassCircularityError AssertionError ThreadDeath IllegalAccessError NoClassDefFoundError ClassFormatError UnsupportedClassVersionError NoSuchFieldError VerifyError ExceptionInInitializerError InstantiationError LinkageError NoSuchMethodError Error UnsatisfiedLinkError StackOverflowError AbstractMethodError VirtualMachineError OutOfMemoryError
+ syn keyword javaE_JavaLang AbstractMethodError ClassCircularityError ClassFormatError Error IllegalAccessError IncompatibleClassChangeError InstantiationError InternalError LinkageError NoClassDefFoundError NoSuchFieldError NoSuchMethodError OutOfMemoryError StackOverflowError ThreadDeath UnknownError UnsatisfiedLinkError VerifyError VirtualMachineError ExceptionInInitializerError UnsupportedClassVersionError AssertionError BootstrapMethodError
syn cluster javaTop add=javaE_JavaLang
syn cluster javaClasses add=javaE_JavaLang
hi def link javaE_JavaLang javaE_Java
- syn keyword javaX_JavaLang CloneNotSupportedException Exception NoSuchMethodException IllegalAccessException NoSuchFieldException Throwable InterruptedException ClassNotFoundException InstantiationException
+ syn keyword javaX_JavaLang ClassNotFoundException CloneNotSupportedException Exception IllegalAccessException InstantiationException InterruptedException NoSuchMethodException Throwable NoSuchFieldException ReflectiveOperationException
syn cluster javaTop add=javaX_JavaLang
syn cluster javaClasses add=javaX_JavaLang
hi def link javaX_JavaLang javaX_Java
@@ -118,7 +142,7 @@ if exists("java_space_errors")
endif
endif
-syn region javaLabelRegion transparent matchgroup=javaLabel start="\<case\>" matchgroup=NONE end=":" contains=javaNumber,javaCharacter,javaString
+syn region javaLabelRegion transparent matchgroup=javaLabel start="\<case\>" end="->" matchgroup=NONE end=":" contains=javaNumber,javaCharacter,javaString
syn match javaUserLabel "^\s*[_$a-zA-Z][_$a-zA-Z0-9_]*\s*:"he=e-1 contains=javaLabel
syn keyword javaLabel default
@@ -126,7 +150,7 @@ syn keyword javaLabel default
" annoying. Was: if !exists("java_allow_cpp_keywords")
" The following cluster contains all java groups except the contained ones
-syn cluster javaTop add=javaExternal,javaError,javaError,javaBranch,javaLabelRegion,javaLabel,javaConditional,javaRepeat,javaBoolean,javaConstant,javaTypedef,javaOperator,javaType,javaType,javaStatement,javaStorageClass,javaAssert,javaExceptions,javaMethodDecl,javaClassDecl,javaClassDecl,javaClassDecl,javaScopeDecl,javaError,javaError2,javaUserLabel,javaLangObject,javaAnnotation,javaVarArg
+syn cluster javaTop add=javaExternal,javaError,javaBranch,javaLabelRegion,javaLabel,javaConditional,javaRepeat,javaBoolean,javaConstant,javaTypedef,javaOperator,javaType,javaStatement,javaStorageClass,javaAssert,javaExceptions,javaMethodDecl,javaClassDecl,javaScopeDecl,javaError2,javaUserLabel,javaLangObject,javaAnnotation,javaVarArg
" Comments
@@ -153,7 +177,7 @@ syn cluster javaTop add=javaComment,javaLineComment
if !exists("java_ignore_javadoc") && main_syntax != 'jsp'
syntax case ignore
" syntax coloring for javadoc comments (HTML)
- syntax include @javaHtml <sfile>:p:h/html.vim
+ syntax include @javaHtml syntax/html.vim
unlet b:current_syntax
" HTML enables spell checking for all text that is not in a syntax item. This
" is wrong for Java (all identifiers would be spell-checked), so it's undone
@@ -336,7 +360,7 @@ hi def link htmlComment Special
hi def link htmlCommentPart Special
hi def link javaSpaceError Error
-if fnamemodify(bufname("%"), ":t") == "module-info.java"
+if s:isModuleInfoDeclarationCurrentBuffer()
hi def link javaModuleStorageClass StorageClass
hi def link javaModuleStmt Statement
hi def link javaModuleExternal Include
@@ -348,6 +372,7 @@ if main_syntax == 'java'
unlet main_syntax
endif
+delfunction! s:isModuleInfoDeclarationCurrentBuffer
let b:spell_options="contained"
let &cpo = s:cpo_save
unlet s:cpo_save
diff --git a/runtime/syntax/javascript.vim b/runtime/syntax/javascript.vim
index 1ab1c2db11..e513137984 100644
--- a/runtime/syntax/javascript.vim
+++ b/runtime/syntax/javascript.vim
@@ -7,7 +7,7 @@
" (ss) repaired several quoting and grouping glitches
" (ss) fixed regex parsing issue with multiple qualifiers [gi]
" (ss) additional factoring of keywords, globals, and members
-" Last Change: 2021 Mar 30
+" Last Change: 2022 Jun 09
" 2013 Jun 12: adjusted javaScriptRegexpString (Kevin Locke)
" 2018 Apr 14: adjusted javaScriptRegexpString (LongJohnCoder)
@@ -39,9 +39,16 @@ syn region javaScriptStringT start=+`+ skip=+\\\\\|\\`+ end=+`+ contai
syn region javaScriptEmbed start=+${+ end=+}+ contains=@javaScriptEmbededExpr
+" number handling by Christopher Leonard chris.j.leonard@gmx.com
syn match javaScriptSpecialCharacter "'\\.'"
-syn match javaScriptNumber "-\=\<\d\+L\=\>\|0[xX][0-9a-fA-F]\+\>"
-syn match javaScriptNumber "-\=\<\d\+\%(_\d\+\)*\>"
+syn match javaScriptNumber "\<0[bB][0-1]\+\(_[0-1]\+\)*\>"
+syn match javaScriptNumber "\<0[oO][0-7]\+\(_[0-7]\+\)*\>"
+syn match javaScriptNumber "\<0\([0-7]\+\(_[0-7]\+\)*\)\?\>"
+syn match javaScriptNumber "\<0[xX][0-9a-fA-F]\+\(_[0-9a-fA-F]\+\)*\>"
+syn match javaScriptNumber "\<\d\+\(_\d\+\)*[eE][+-]\?\d\+\>"
+syn match javaScriptNumber "\<[1-9]\d*\(_\d\+\)*\(\.\(\d\+\(_\d\+\)*\([eE][+-]\?\d\+\)\?\)\?\)\?\>"
+syn match javaScriptNumber "\<\(\d\+\(_\d\+\)*\)\?\.\d\+\(_\d\+\)*\([eE][+-]\?\d\+\)\?\>"
+syn match javaScriptNumber "\<\d\+\(_\d\+\)*\.\(\d\+\(_\d\+\)*\([eE][+-]\?\d\+\)\?\)\?\>"
syn region javaScriptRegexpString start=+[,(=+]\s*/[^/*]+ms=e-1,me=e-1 skip=+\\\\\|\\/+ end=+/[gimuys]\{0,2\}\s*$+ end=+/[gimuys]\{0,2\}\s*[+;.,)\]}]+me=e-1 end=+/[gimuys]\{0,2\}\s\+\/+me=e-1 contains=@htmlPreproc,javaScriptComment oneline
syn keyword javaScriptConditional if else switch
diff --git a/runtime/syntax/krl.vim b/runtime/syntax/krl.vim
new file mode 100644
index 0000000000..a50790841e
--- /dev/null
+++ b/runtime/syntax/krl.vim
@@ -0,0 +1,458 @@
+" Vim syntax file
+" Language: Kuka Robot Language
+" Maintainer: Patrick Meiser-Knosowski <knosowski@graeffrobotics.de>
+" Version: 3.0.0
+" Last Change: 18. Apr 2022
+" Credits: Thanks for contributions to this to Michael Jagusch
+" Thanks for beta testing to Thomas Baginski
+"
+" Note to self:
+" for testing perfomance
+" open a 1000 lines file.
+" :syntime on
+" G
+" hold down CTRL-U until reaching top
+" :syntime report
+
+" Init {{{
+if exists("b:current_syntax")
+ finish
+endif
+
+let s:keepcpo = &cpo
+set cpo&vim
+
+" if colorscheme is tortus(less)? krlGroupName defaults to 1
+if get(g:, 'colors_name', " ") =~ '\<tortus'
+ \&& !exists("g:krlGroupName")
+ let g:krlGroupName=1
+endif
+" krlGroupName defaults to 0 if it's not initialized yet or 0
+if !get(g:, "krlGroupName", 0)
+ let g:krlGroupName = 0
+endif
+
+" krl does ignore case
+syn case ignore
+" take #, $ and & into keyword (syntax only)
+syn iskeyword @,48-57,_,192-255,#,$,&
+" spell checking
+syn spell notoplevel
+" }}} init
+
+" Comment and Folding {{{
+
+" Special Comment
+
+" TODO Comment
+syn keyword krlTodo contained TODO FIXME XXX
+highlight default link krlTodo Todo
+
+" Debug Comment
+syn keyword krlDebug contained DEBUG
+highlight default link krlDebug Debug
+
+" Comment
+" none move fold comment until second ;
+syn match krlFoldComment /\c\v^\s*;\s*%(end)?fold>[^;]*/ containedin=krlFold contains=krlSingleQuoteString,krlInteger,krlFloat,krlMovement,krlDelimiter,krlBoolean
+highlight default link krlFoldComment Comment
+
+" move fold comment until second ;
+syn match krlMoveFoldComment /\c\v^\s*;\s*fold>[^;]*<s?%(ptp|lin|circ|spl)(_rel)?>[^;]*/ containedin=krlFold contains=krlInteger,krlFloat,krlMovement,krlDelimiter
+highlight default link krlMoveFoldComment Comment
+
+" things to highlight in a fold line
+syn keyword krlFoldHighlights CONT IN SYN OUT containedin=krlFoldComment
+syn match krlFoldHighlights /\c\v<(M|F|E|A|t|i|bin|binin|UP|SPSMAKRO)\d+>/ containedin=krlFoldComment
+if g:krlGroupName
+ highlight default link krlFoldHighlights Sysvars
+else
+ " default color for Fold Highlights
+endif
+syn keyword krlVkrcFoldConstants EIN AUS containedin=krlFoldComment
+highlight default link krlVkrcFoldConstants Boolean
+
+" Comment without Fold, also includes endfold lines and fold line part after second ;
+syn match krlComment /\c\v;\s*%(<%(end)?fold>)@!.*$/ containedin=krlFold contains=krlTodo,krlDebug,@Spell
+" Commented out Fold line: "; ;FOLD PTP..."
+syn match krlComment /\c\v^\s*;\s*;.*$/ contains=krlTodo,krlDebug
+highlight default link krlComment Comment
+
+if has("conceal") && get(g:, 'krlConcealFoldTail', 1)
+ syn match krlConcealFoldTail /\c\v(^\s*;\s*fold[^;]*)@250<=;%(--|\s*<fold>|\s*<endfold>)@!.*$/ transparent containedin=krlComment conceal cchar=*
+endif
+" }}} Comment and Folding
+
+" Header {{{
+syn match krlHeader /&\a\w*/
+highlight default link krlHeader PreProc
+" }}} Header
+
+" Operator {{{
+" Boolean operator
+syn keyword krlBoolOperator and or exor not b_and b_or b_exor b_not
+highlight default link krlBoolOperator Operator
+" Arithmetic operator
+syn match krlArithOperator /[+-]/ containedin=krlFloat
+syn match krlArithOperator /[*/]/
+highlight default link krlArithOperator Operator
+" Compare operator
+syn match krlCompOperator /[<>=]/
+highlight default link krlCompOperator Operator
+" Geometric operator
+" Do not move the : operator
+" Must be present befor krlParamdef
+syn match krlGeomOperator /[:]/
+" syn match krlGeomOperator /[:]/ containedin=krlLabel,krlParamdef
+highlight default link krlGeomOperator Operator
+" }}} Operator
+
+" Type, StorageClass and Typedef {{{
+" Simple data types
+syn keyword krlType bool char real int containedin=krlAnyType
+" External program and function
+syn keyword krlType ext extfct extfctp extp containedin=krlAnyType
+" Communication
+syn keyword krlType signal channel containedin=krlAnyType
+highlight default link krlType Type
+" StorageClass
+syn keyword krlStorageClass decl global const struc enum
+highlight default link krlStorageClass StorageClass
+" .dat file public
+syn keyword krlDatStorageClass public
+highlight default link krlDatStorageClass StorageClass
+" Parameter StorageClass
+" Do not move the :in/:out
+" Must be present after krlGeomOperator
+syn match krlParamdef /[:]\s*in\>/
+syn match krlParamdef /[:]\s*out\>/
+highlight default link krlParamdef StorageClass
+" Not a typedef but I like to have those highlighted
+" different then types, structures or strorage classes
+syn keyword krlTypedef DEF DEFFCT ENDFCT DEFDAT ENDDAT
+syn match krlTypedef /^\s*END\>/
+highlight default link krlTypedef Typedef
+" }}} Type, StorageClass and Typedef
+
+" Delimiter {{{
+syn match krlDelimiter /[\[\](),\\]/
+highlight default link krlDelimiter Delimiter
+" }}} Delimiter
+
+" Constant values {{{
+" Boolean
+syn keyword krlBoolean true false containedin=krlStructVal
+highlight default link krlBoolean Boolean
+" Binary integer
+syn match krlBinaryInt /'b[01]\+'/ containedin=krlStructVal
+highlight default link krlBinaryInt Number
+" Hexadecimal integer
+syn match krlHexInt /'h[0-9a-fA-F]\+'/ containedin=krlStructVal
+highlight default link krlHexInt Number
+" Integer
+syn match krlInteger /\W\@1<=[+-]\?\d\+/ containedin=krlStructVal,krlFloat contains=krlArithOperator
+highlight default link krlInteger Number
+" Float
+syn match krlFloat /\v\W@1<=[+-]?\d+\.?\d*%(\s*[eE][+-]?\d+)?/ containedin=krlStructVal
+highlight default link krlFloat Float
+" String
+syn region krlString start=/"/ end=/"/ oneline containedin=krlStructVal contains=@Spell
+highlight default link krlString String
+syn match krlSpecialChar /[|]/ containedin=krlString
+highlight default link krlSpecialChar SpecialChar
+" String within a fold line
+syn region krlSingleQuoteString start=/'/ end=/'/ oneline contained contains=@Spell
+highlight default link krlSingleQuoteString String
+" Enum
+syn match krlEnumVal /#\s*\a\w*/ containedin=krlStructVal
+highlight default link krlEnumVal Constant
+" }}} Constant values
+
+" Predefined Structure and Enum {{{
+" Predefined structures and enums found in
+" /r1/mada/$*.dat, /r1/steu/$*.dat and
+" /r1/system/$config.dat as well as
+" basisTech, gripperTech and spotTech
+"
+" Predefined data types found in krc1
+syn keyword krlStructure servopara keymove powermodul trace techangle tech techfct techcps techfctctrl axis_inc axis_cal date display_var pro_ip con bus
+syn keyword krlEnum ident_state sig_state move_state async_state emt_mode boxmode msg_prm_typ msg_typ cmd_stat asys trace_state trace_mode direction techsys techgeoref techclass techmode hpu_key_val pro_state eax transsys mode_move cosys device rotsys emstop cause_t
+"
+" Predefined data types found in kss functions
+syn keyword krlEnum ediagstate rdc_fs_state ret_c_psync_e var_type cancel_psync_e sys_vars
+syn keyword krlStructure siginf rw_rdc_file rw_mam_file diagpar_t error_t stopmess case_sense_t msgbuf_t e3pos e3axis diagopt_t
+"
+" Predefined structures for movement
+syn keyword krlStructure frame e6pos pos e6axis axis
+syn keyword krlStructure fdat ldat pdat
+syn keyword krlStructure load inertia
+"
+" Predefined structures for shapes
+syn keyword krlStructure axbox cylinder box
+"
+" Predefined structures and enums found in /r1/mada/$machine.dat
+syn keyword krlStructure cp fra acc_car jerk_struc dhart spin trpspin ex_kin et_ax maxtool
+syn keyword krlEnum individual_mames supply_voltage kinclass main_axis wrist_axis sw_onoff
+"
+" Predefined structures and enums found in /r1/mada/$robcor.dat
+" syn keyword krlStructure
+syn keyword krlEnum adap_acc model_type control_parameter eko_mode
+"
+" Predefined structures and enums found in /steu/mada/$custom.dat
+syn keyword krlStructure pro_io_t ser ext_mod_t coop_krc ws_config bin_type coop_update_t ldc_reaction
+syn keyword krlEnum axis_of_coordinates spline_para_variant target_status cp_vel_type cp_statmon
+"
+" Predefined structures and enums found in /steu/mada/$machine.dat
+syn keyword krlStructure emstop_path boxstatesafein boxstatesafeout
+syn keyword krlEnum digincode
+"
+" Predefined structures and enums found in /steu/mada/$option.dat
+syn keyword krlStructure msg_t
+" syn keyword krlEnum
+"
+" Predefined structures and enums found in /r1/system/$config.dat
+" BasisTech
+syn keyword krlStructure dig_out_type ctrl_in_t ctrl_out_t fct_out_t fct_in_t odat basis_sugg_t out_sugg_t md_state machine_def_t machine_tool_t machine_frame_t trigger_para constvel_para condstop_para adat tm_sugg_t tqm_tqdat_t sps_prog_type
+syn keyword krlEnum bas_command out_modetype ipo_m_t apo_mode_t funct_type p00_command timer_actiontype
+"
+" GripperTech
+syn keyword krlStructure grp_typ grp_types grp_sugg_t
+syn keyword krlEnum on_off_typ apo_typ
+"
+" SpotTech
+syn keyword krlStructure spot_type spot_sugg_t
+syn keyword krlEnum s_command s_pair_slct command_retr
+"
+" VW
+syn keyword krlStructure vw_mpara_typ zangentyp zangenbedingung ibszangentyp last_ibs_typ verr_typ verrcheck_t t_fb_state kollisionsdaten state_t modus_t
+syn keyword krlEnum synctype dir_typ subtype ari_typ bool_typ vw_command ibgn_command vw_user_cmd move_types adv_t_type bas_type ibs_mode_typ vw_user_cmd pro_mode mode_op
+"
+" ProgCoop
+syn keyword krlStructure ydat
+" syn keyword krlEnum
+"
+" bas.src
+syn keyword krlStructure cont
+syn keyword krlEnum esys ipo_mode circ_mode circ_type ori_type var_state
+"
+" MsgLib.src
+syn keyword krlStructure KrlMsg_T KrlMsgParType_T KrlMsgPar_T KrlMsgOpt_T KrlMsgDlgSK_T
+syn keyword krlEnum EKrlMsgType
+"
+highlight default link krlStructure Structure
+highlight default link krlEnum Structure
+" }}} Predefined Structure and Enum
+
+" System variable {{{
+syn match krlSysvars /\<\$\a[a-zA-Z0-9_.]*/
+if g:krlGroupName
+ highlight default link krlSysvars Sysvars
+else
+ " default color for Sysvars
+endif
+" }}} System variable
+
+" Statements, keywords et al {{{
+" continue
+syn keyword krlContinue continue
+if g:krlGroupName
+ highlight default link krlContinue Continue
+else
+ highlight default link krlContinue Statement
+endif
+" interrupt
+syn match krlStatement /\v\c%(<global>\s+)?<INTERRUPT>%(\s+<decl>)?/ contains=krlStorageClass
+" keywords
+syn keyword krlStatement wait on off enable disable stop trigger with when distance onstart delay do prio import is minimum maximum confirm on_error_proceed
+syn match krlStatement /\v\c%(<wait\s+)@7<=<sec>/
+syn match krlStatement /\v\c%(<when\s+)@7<=<path>/
+highlight default link krlStatement Statement
+" Conditional
+syn keyword krlConditional if then else endif switch case default endswitch skip endskip
+highlight default link krlConditional Conditional
+" Repeat
+syn keyword krlRepeat for to step endfor while endwhile repeat until loop endloop exit
+highlight default link krlRepeat Repeat
+" Label
+syn keyword krlLabel goto
+syn match krlLabel /^\s*\w\+:\ze\s*\%(;.*\)\?$/
+highlight default link krlLabel Label
+" Keyword
+syn keyword krlKeyword anin anout digin
+highlight default link krlKeyword Keyword
+" Exception
+syn keyword krlException return resume halt
+highlight default link krlException Exception
+" }}} Statements, keywords et al
+
+" special keywords for movement commands {{{
+syn keyword krlMovement PTP PTP_REL LIN LIN_REL CIRC CIRC_REL SPL SPL_REL SPTP SPTP_REL SLIN SLIN_REL SCIRC SCIRC_REL
+syn keyword krlMovement ASYPTP ASYCONT ASYSTOP ASYCANCEL MOVE_EMI
+syn match krlMovement /\v\c^\s*<BRAKE(\s+F)?>/
+if g:krlGroupName
+ highlight default link krlMovement Movement
+else
+ highlight default link krlMovement Special
+endif
+" movement modifiers
+syn match krlMoveBlockInst /\c\v^\s*TIME_BLOCK\s+(START|PART|END)/
+syn match krlMoveBlockInst /\c\v^\s*CONST_VEL\s+(START|END)/
+syn keyword krlMoveBlockInst ptp_spline spline endspline
+highlight default link krlMoveBlockInst Statement
+syn keyword krlMoveMod ca c_ptp c_dis c_vel c_ori c_spl
+if g:krlGroupName
+ highlight default link krlMoveMod Movement
+else
+ highlight default link krlMoveMod Special
+endif
+" }}} special keywords for movement commands
+
+" Structure value {{{
+" avoid coloring structure component names
+syn match krlNames /\.[a-zA-Z_][.a-zA-Z0-9_$]*/
+syn match krlNames contained /[a-zA-Z_][.a-zA-Z0-9_$]*/
+" highlight default link krlNames None
+" Structure value
+syn region krlStructVal start=/{/ end=/}/ oneline containedin=krlStructVal contains=krlNames
+highlight default link krlStructVal Delimiter
+" }}} Structure value
+
+" BuildInFunction {{{
+syn keyword krlBuildInFunction contained Pulse
+syn keyword krlBuildInFunction contained m_comment
+syn keyword krlBuildInFunction contained is_key_pressed
+syn keyword krlBuildInFunction contained set_opt_filter
+syn keyword krlBuildInFunction contained timer_limit
+syn keyword krlBuildInFunction contained tool_adj
+syn keyword krlBuildInFunction contained FRand
+syn keyword krlBuildInFunction contained ExecFunc eb_test EB EK EO LK mbx_rec
+" safe robot
+syn keyword krlbuildinfunction contained get_AxesMask get_BrakeTest_Time
+" math
+syn keyword krlBuildInFunction contained Abs Sin Cos Acos Tan Atan Atan2 Sqrt
+syn keyword krlBuildInFunction contained Forward Inverse inv_pos
+" cFoo sFoo
+syn keyword krlBuildInFunction contained cClose cOpen cRead cWrite sRead sWrite
+" string
+syn keyword krlBuildInFunction contained StrToBool StrToInt StrToReal StrToString StrToFrame StrToPos StrToE3Pos StrToE6Pos StrToAxis StrToE3Axis StrToE6Axis
+syn keyword krlBuildInFunction contained StrAdd StrClear StrCopy StrComp StrFind StrLen StrDeclLen StrToBool StrToInt StrToReal StrToString
+" diag
+syn keyword krlBuildInFunction contained diag_start diag_stop get_DiagState
+" rdc mam pid
+syn keyword krlBuildInFunction contained CheckPidOnRdc check_mam_on_rdc get_rdc_fs_state
+syn keyword krlBuildInFunction contained set_mam_on_hd copy_mam_hd_to_rdc copy_mam_rdc_to_hd
+syn keyword krlBuildInFunction contained PidToHd PidToRdc
+syn keyword krlBuildInFunction contained cal_to_rdc rdc_file_to_hd
+syn keyword krlBuildInFunction contained delete_pid_on_rdc delete_rdc_content
+syn keyword krlBuildInFunction contained create_rdc_archive restore_rdc_archive
+" ioctl
+syn keyword krlBuildInFunction contained IOCtl cIOCtl
+syn keyword krlBuildInFunction contained WSpaceGive WSpaceTake
+" sync
+syn keyword krlBuildInFunction contained Sync SyncCmd CancelProgSync
+" remote
+syn keyword krlBuildInFunction contained RemoteCmd RemoteRead
+" msg/dlg
+syn keyword krlBuildInFunction contained IsMessageSet clear_KrlMsg get_MsgBuffer exists_KrlDlg exists_KrlMsg set_KrlDlg set_KrlDlgAnswer set_KrlMsg
+" robvers
+syn keyword krlBuildInFunction contained maximize_UsedxRobvers set_UsedxRobvers
+" md_foo
+syn keyword krlBuildInFunction contained md_Cmd md_GetState md_SetState md_Asgn
+" emi
+syn keyword krlBuildInFunction contained emi_ActPos emi_EndPos emi_StartPos emi_RecState emi_RecName
+" var
+syn keyword krlBuildInFunction contained cast_from cast_to
+syn keyword krlBuildInFunction contained GetVarsize GetCycDef get_sig_inf get_decl_place VarType VarState
+" sys
+syn keyword krlBuildInFunction contained GetSysState get_system_data set_system_data set_system_data_delayed
+" err
+syn keyword krlBuildInFunction contained err_clear err_raise
+" motion
+syn keyword krlBuildInFunction contained delete_backward_buffer rob_stop rob_stop_release set_brake_delay suppress_repositioning VectorMoveOn VectorMoveOff
+" torque
+syn keyword krlBuildInFunction contained set_torque_limits reset_torque_limits
+" krc1
+syn keyword krlBuildInFunction contained cLcopy cCurpos cNew cClear cRelease cKey
+if g:krlGroupName
+ highlight default link krlBuildInFunction BuildInFunction
+else
+ highlight default link krlBuildInFunction Function
+endif
+" }}} BuildInFunction
+
+" Function {{{
+syn match krlFunction /[a-zA-Z_]\w* *(/me=e-1 contains=krlBuildInFunction
+highlight default link krlFunction Function
+" }}} Function
+
+" Error {{{
+if get(g:, 'krlShowError', 1)
+ " some more or less common typos
+ "
+ " vars or funcs >24 chars are not possible in krl. a234567890123456789012345
+ syn match krlError0 /\w\{25,}/ containedin=krlFunction,krlNames,krlLabel,krlAnyType,krlEnumVal,krlSysvars
+ "
+ " should be interrupt (on|off) \w+
+ syn match krlError1 /\vinterrupt[ \t(]+[_$a-zA-Z0-9]+[_$a-zA-Z0-9.\[\]()+\-*/]*[ \t)]+o%(n|ff)>/
+ "
+ " for bla==5 to 7...
+ " ||
+ syn match krlError3 /\v%(^\s*for%(\(|\s)+[_$a-zA-Z]+[_$a-zA-Z0-9.\[\]()+\-*/ ]*\s*)@<=[:=]\=/
+ "
+ " TODO optimize performance
+ " wait for a=b
+ " |
+ syn match krlError4 /\v%(^\s*%(return|wait\s+for|if|while|until|%(global\s+)?interrupt\s+decl)>[^;]+[^;<>=])@<=\=[^=]/
+ "
+ " wait for a><b
+ " ||
+ syn match krlError5 /\v%(^\s*%(return|wait\s+for|if|while|until|%(global\s+)?interrupt\s+decl)>[^;]+)@<=\>\s*\</
+ "
+ " if (a==5) (b==6) ...
+ " |||
+ syn match krlError6 /\v%(^\s*%(return|wait\s+for|if|while|until|%(global\s+)?interrupt\s+decl)>[^;]+[^;])@<=\)\s*\(/
+ "
+ " TODO optimize performance
+ " a == b + 1
+ " a := b + 1
+ " ||
+ syn match krlError7 /\v%(^\s*%(return|wait\s+for|if|while|until|%(global\s+)?interrupt\s+decl)>[^;]+[^;])@1<!%(^\s*[_$a-zA-Z]+[_$a-zA-Z0-9.\[\],+\-*/]*\s*)@<=[:=]\=/
+ syn match krlError7 /\v\c%(^\s*%(decl\s+)%(global\s+)?%(const\s+)?\w+\s+\w+\s*)@<=[:=]\=/
+ syn match krlError7 /\v\c%(^\s*%(decl\s+)?%(global\s+)?%(const\s+)?%(bool\s+|int\s+|real\s+|char\s+)\w+\s*)@<=[:=]\=/
+ "
+ " this one is tricky. Make sure this does not match trigger instructions; OK, next try, now search for false positives
+ " TODO optimize performance
+ " a = b and c or (int1=int2)
+ " |
+ syn match krlError8 /\v(^\s*[_$a-zA-Z]+[_$a-zA-Z0-9.\[\]()+\-*/]*\s*\=[^;]*[^;<>=])@<=\=\ze[^=]/
+ "
+ " <(distance|delay|prio)> :=
+ " <(distance|delay|prio)> ==
+ " ||
+ syn match krlError9 /\v(^[^;]*<(distance|delay|prio|minimum|maximum)\s*)@<=[:=]\=/
+ "
+ " 'for', 'while' or 'repeat' followed by 'do'
+ syn match krlError10 /\c\v^\s*(until|while|for)>[^;]*<do>/
+ "
+ highlight default link krlError0 Error
+ highlight default link krlError1 Error
+ highlight default link krlError2 Error
+ highlight default link krlError3 Error
+ highlight default link krlError4 Error
+ highlight default link krlError5 Error
+ highlight default link krlError6 Error
+ highlight default link krlError7 Error
+ highlight default link krlError8 Error
+ highlight default link krlError9 Error
+ highlight default link krlError10 Error
+endif
+" }}} Error
+
+" Finish {{{
+let &cpo = s:keepcpo
+unlet s:keepcpo
+
+let b:current_syntax = "krl"
+" }}} Finish
+
+" vim:sw=2 sts=2 et fdm=marker
diff --git a/runtime/syntax/liquid.vim b/runtime/syntax/liquid.vim
index 295a91775e..966b60f6f8 100644
--- a/runtime/syntax/liquid.vim
+++ b/runtime/syntax/liquid.vim
@@ -2,7 +2,7 @@
" Language: Liquid
" Maintainer: Tim Pope <vimNOSPAM@tpope.org>
" Filenames: *.liquid
-" Last Change: 2013 May 30
+" Last Change: 2022 Mar 15
if exists('b:current_syntax')
finish
@@ -68,10 +68,10 @@ if !exists('s:subtype')
unlet s:subtype
endif
-syn region liquidStatement matchgroup=liquidDelimiter start="{%" end="%}" contains=@liquidStatement containedin=ALLBUT,@liquidExempt keepend
-syn region liquidExpression matchgroup=liquidDelimiter start="{{" end="}}" contains=@liquidExpression containedin=ALLBUT,@liquidExempt keepend
-syn region liquidComment matchgroup=liquidDelimiter start="{%\s*comment\s*%}" end="{%\s*endcomment\s*%}" contains=liquidTodo,@Spell containedin=ALLBUT,@liquidExempt keepend
-syn region liquidRaw matchgroup=liquidDelimiter start="{%\s*raw\s*%}" end="{%\s*endraw\s*%}" contains=TOP,@liquidExempt containedin=ALLBUT,@liquidExempt keepend
+syn region liquidStatement matchgroup=liquidDelimiter start="{%-\=" end="-\=%}" contains=@liquidStatement containedin=ALLBUT,@liquidExempt keepend
+syn region liquidExpression matchgroup=liquidDelimiter start="{{-\=" end="-\=}}" contains=@liquidExpression containedin=ALLBUT,@liquidExempt keepend
+syn region liquidComment matchgroup=liquidDelimiter start="{%-\=\s*comment\s*-\=%}" end="{%-\=\s*endcomment\s*-\=%}" contains=liquidTodo,@Spell containedin=ALLBUT,@liquidExempt keepend
+syn region liquidRaw matchgroup=liquidDelimiter start="{%-\=\s*raw\s*-\=%}" end="{%-\=\s*endraw\s*-\=%}" contains=TOP,@liquidExempt containedin=ALLBUT,@liquidExempt keepend
syn cluster liquidExempt contains=liquidStatement,liquidExpression,liquidComment,liquidRaw,@liquidStatement,liquidYamlHead
syn cluster liquidStatement contains=liquidConditional,liquidRepeat,liquidKeyword,@liquidExpression
@@ -79,11 +79,11 @@ syn cluster liquidExpression contains=liquidOperator,liquidString,liquidNumber,l
syn keyword liquidKeyword highlight nextgroup=liquidTypeHighlight skipwhite contained
syn keyword liquidKeyword endhighlight contained
-syn region liquidHighlight start="{%\s*highlight\s\+\w\+\s*%}" end="{% endhighlight %}" keepend
+syn region liquidHighlight start="{%-\=\s*highlight\s\+\w\+\s*-\=%}" end="{%-\= endhighlight -\=%}" keepend
for s:type in g:liquid_highlight_types
exe 'syn match liquidTypeHighlight "\<'.matchstr(s:type,'[^=]*').'\>" contained'
- exe 'syn region liquidHighlight'.substitute(matchstr(s:type,'[^=]*$'),'\..*','','').' start="{%\s*highlight\s\+'.matchstr(s:type,'[^=]*').'\s*%}" end="{% endhighlight %}" keepend contains=@liquidHighlight'.substitute(matchstr(s:type,'[^=]*$'),'\.','','g')
+ exe 'syn region liquidHighlight'.substitute(matchstr(s:type,'[^=]*$'),'\..*','','').' start="{%-\=\s*highlight\s\+'.matchstr(s:type,'[^=]*').'\s*-\=%}" end="{%-\= endhighlight -\=%}" keepend contains=@liquidHighlight'.substitute(matchstr(s:type,'[^=]*$'),'\.','','g')
endfor
unlet! s:type
@@ -92,18 +92,18 @@ syn region liquidString matchgroup=liquidQuote start=+'+ end=+'+ contained
syn match liquidNumber "-\=\<\d\+\>" contained
syn match liquidFloat "-\=\<\d\+\>\.\.\@!\%(\d\+\>\)\=" contained
syn keyword liquidBoolean true false contained
-syn keyword liquidNull null nil contained
+syn keyword liquidNull null nil blank contained
syn match liquidEmpty "\<empty\>" contained
syn keyword liquidOperator and or not contained
syn match liquidPipe '|' contained skipwhite nextgroup=liquidFilter
-syn keyword liquidFilter date capitalize downcase upcase first last join sort size strip_html strip_newlines newline_to_br replace replace_first remove remove_first truncate truncatewords prepend append minus plus times divided_by contained
+syn keyword liquidFilter date capitalize downcase upcase escape escape_once first last join sort size where uniq strip_html strip_newlines newline_to_br replace replace_first remove remove_first slice split strip truncate truncatewords prepend append url_encode url_decode abs at_most at_least ceil divided_by floor minus plus round times modulo contained
syn keyword liquidConditional if elsif else endif unless endunless case when endcase ifchanged endifchanged contained
-syn keyword liquidRepeat for endfor tablerow endtablerow in contained
-syn match liquidRepeat "\%({%\s*\)\@<=empty\>" contained
-syn keyword liquidKeyword assign cycle include with contained
+syn keyword liquidRepeat for endfor tablerow endtablerow in break continue limit offset reversed contained
+syn match liquidRepeat "\%({%-\=\s*\)\@<=empty\>" contained
+syn keyword liquidKeyword assign capture endcapture increasement decreasement cycle include with render contained
syn keyword liquidForloop forloop nextgroup=liquidForloopDot contained
syn match liquidForloopDot "\." nextgroup=liquidForloopAttribute contained
diff --git a/runtime/syntax/lsp_markdown.vim b/runtime/syntax/lsp_markdown.vim
index 90d3185673..4be7595807 100644
--- a/runtime/syntax/lsp_markdown.vim
+++ b/runtime/syntax/lsp_markdown.vim
@@ -1,23 +1,34 @@
" Vim syntax file
-" Language: lsp_markdown
-" Maintainer: Michael Lingelbach <m.j.lbach@gmail.com
-" URL: http://neovim.io
-" Remark: Uses markdown syntax file
+" Language: Markdown-like LSP docstrings
+" Maintainer: https://github.com/neovim/neovim
+" URL: http://neovim.io
+" Remark: Uses markdown syntax file
-" always source the system included markdown instead of any other installed
-" markdown.vim syntax files
+" Source the default Nvim markdown syntax, not other random ones.
execute 'source' expand('<sfile>:p:h') .. '/markdown.vim'
syn cluster mkdNonListItem add=mkdEscape,mkdNbsp
+" Don't highlight invalid markdown syntax in LSP docstrings.
+syn clear markdownError
+
syn clear markdownEscape
syntax region markdownEscape matchgroup=markdownEscape start=/\\\ze[\\\x60*{}\[\]()#+\-,.!_>~|"$%&'\/:;<=?@^ ]/ end=/./ containedin=ALL keepend oneline concealends
-" conceal html entities
+" Conceal backticks (which delimit code fragments).
+" We ignore g:markdown_syntax_conceal here.
+syn region markdownCode matchgroup=markdownCodeDelimiter start="`" end="`" keepend contains=markdownLineStart concealends
+syn region markdownCode matchgroup=markdownCodeDelimiter start="`` \=" end=" \=``" keepend contains=markdownLineStart concealends
+syn region markdownCode matchgroup=markdownCodeDelimiter start="^\s*````*.*$" end="^\s*````*\ze\s*$" keepend concealends
+
+" Highlight code fragments.
+hi def link markdownCode Special
+
+" Conceal HTML entities.
syntax match mkdNbsp /&nbsp;/ conceal cchar=
syntax match mkdLt /&lt;/ conceal cchar=<
syntax match mkdGt /&gt;/ conceal cchar=>
syntax match mkdAmp /&amp;/ conceal cchar=&
syntax match mkdQuot /&quot;/ conceal cchar="
-hi def link mkdEscape special
+hi def link mkdEscape Special
diff --git a/runtime/syntax/lua.vim b/runtime/syntax/lua.vim
index f313c14e7a..b398e2e5c6 100644
--- a/runtime/syntax/lua.vim
+++ b/runtime/syntax/lua.vim
@@ -2,7 +2,7 @@
" Language: Lua 4.0, Lua 5.0, Lua 5.1 and Lua 5.2
" Maintainer: Marcus Aurelius Farias <masserahguard-lua 'at' yahoo com>
" First Author: Carlos Augusto Teixeira Mendes <cmendes 'at' inf puc-rio br>
-" Last Change: 2012 Aug 12
+" Last Change: 2022 Mar 31
" Options: lua_version = 4 or 5
" lua_subversion = 0 (4.0, 5.0) or 1 (5.1) or 2 (5.2)
" default 5.2
@@ -319,6 +319,15 @@ elseif lua_version == 5
syn match luaFunc /\<debug\.upvalueid\>/
syn match luaFunc /\<debug\.upvaluejoin\>/
endif
+ if lua_subversion >= 3
+ "https://www.lua.org/manual/5.3/manual.html#6.5
+ syn match luaFunc /\<utf8\.char\>/
+ syn match luaFunc /\<utf8\.charpattern\>/
+ syn match luaFunc /\<utf8\.codes\>/
+ syn match luaFunc /\<utf8\.codepoint\>/
+ syn match luaFunc /\<utf8\.len\>/
+ syn match luaFunc /\<utf8\.offset\>/
+ endif
endif
" Define the default highlighting.
diff --git a/runtime/syntax/m4.vim b/runtime/syntax/m4.vim
index 6f229ea1ab..f90197aeb0 100644
--- a/runtime/syntax/m4.vim
+++ b/runtime/syntax/m4.vim
@@ -2,7 +2,8 @@
" Language: M4
" Maintainer: Claudio Fleiner (claudio@fleiner.com)
" URL: http://www.fleiner.com/vim/syntax/m4.vim
-" Last Change: 2005 Jan 15
+" (outdated)
+" Last Change: 2022 Jun 12
" This file will highlight user function calls if they use only
" capital letters and have at least one argument (i.e. the '('
@@ -21,6 +22,7 @@ endif
syn match m4Variable contained "\$\d\+"
syn match m4Special contained "$[@*#]"
syn match m4Comment "\<\(m4_\)\=dnl\>.*" contains=SpellErrors
+syn match m4Comment "#.*" contains=SpellErrors
syn match m4Constants "\<\(m4_\)\=__file__"
syn match m4Constants "\<\(m4_\)\=__line__"
syn keyword m4Constants divnum sysval m4_divnum m4_sysval
@@ -32,7 +34,7 @@ syn region m4Command matchgroup=m4builtin start="\<\(m4_\)\=\(len\|index\|regex
syn keyword m4Statement divert undivert
syn region m4Command matchgroup=m4Type start="\<\(m4_\)\=\(undefine\|popdef\)("he=e-1 end=")" contains=@m4Top
syn region m4Function matchgroup=m4Type start="\<[_A-Z][_A-Z0-9]*("he=e-1 end=")" contains=@m4Top
-syn region m4String start="`" end="'" contained contains=@m4Top,@m4StringContents,SpellErrors
+syn region m4String start="`" end="'" contains=SpellErrors
syn cluster m4Top contains=m4Comment,m4Constants,m4Special,m4Variable,m4String,m4Paren,m4Command,m4Statement,m4Function
" Define the default highlighting.
diff --git a/runtime/syntax/man.vim b/runtime/syntax/man.vim
index 55b0e16de9..89162f1e20 100644
--- a/runtime/syntax/man.vim
+++ b/runtime/syntax/man.vim
@@ -10,7 +10,7 @@ syntax match manReference display '[^()[:space:]]\+(\%([0-9][a-z]*\|[nlpox]
syntax match manSectionHeading display '^\S.*$'
syntax match manHeader display '^\%1l.*$'
syntax match manSubHeading display '^ \{3\}\S.*$'
-syntax match manOptionDesc display '^\s\+\%(+\|-\)\S\+'
+syntax match manOptionDesc display '^\s\+\(\%(+\|-\)\S\+,\s\+\)*\%(+\|-\)\S\+'
highlight default link manHeader Title
highlight default link manSectionHeading Statement
diff --git a/runtime/syntax/masm.vim b/runtime/syntax/masm.vim
index c3b2a9bd63..3be0fd45d1 100644
--- a/runtime/syntax/masm.vim
+++ b/runtime/syntax/masm.vim
@@ -2,7 +2,7 @@
" Language: Microsoft Macro Assembler (80x86)
" Orig Author: Rob Brady <robb@datatone.com>
" Maintainer: Wu Yongwei <wuyongwei@gmail.com>
-" Last Change: 2020-05-09 22:55:38 +0800
+" Last Change: 2022-04-24 20:07:04 +0800
" Quit when a syntax file was already loaded
if exists("b:current_syntax")
@@ -68,6 +68,7 @@ syn keyword masmType STDCALL SYSCALL C BASIC FORTRAN PASCAL
syn keyword masmType PTR NEAR FAR NEAR16 FAR16 NEAR32 FAR32
syn keyword masmType REAL4 REAL8 REAL10 BYTE SBYTE TBYTE
syn keyword masmType WORD DWORD QWORD FWORD SWORD SDWORD
+syn keyword masmType SQWORD OWORD MMWORD XMMWORD YMMWORD
syn keyword masmOperator AND NOT OR SHL SHR XOR MOD DUP
syn keyword masmOperator EQ GE GT LE LT NE
syn keyword masmOperator LROFFSET SEG LENGTH LENGTHOF SIZE SIZEOF
diff --git a/runtime/syntax/mason.vim b/runtime/syntax/mason.vim
index f087def794..e1638ece75 100644
--- a/runtime/syntax/mason.vim
+++ b/runtime/syntax/mason.vim
@@ -1,11 +1,12 @@
" Vim syntax file
-" Language: Mason (Perl embedded in HTML)
-" Maintainer: vim-perl <vim-perl@googlegroups.com>
-" Homepage: http://github.com/vim-perl/vim-perl/tree/master
-" Bugs/requests: http://github.com/vim-perl/vim-perl/issues
-" Last Change: 2017-09-12
-" Contributors: Hinrik Örn Sigurðsson <hinrik.sig@gmail.com>
-" Andrew Smith <andrewdsmith@yahoo.com>
+" Language: Mason (Perl embedded in HTML)
+" Maintainer: vim-perl <vim-perl@googlegroups.com>
+" Homepage: https://github.com/vim-perl/vim-perl
+" Bugs/requests: https://github.com/vim-perl/vim-perl/issues
+" License: Vim License (see :help license)
+" Last Change: 2021 Oct 10
+" Contributors: Hinrik Örn Sigurðsson <hinrik.sig@gmail.com>
+" Andrew Smith <andrewdsmith@yahoo.com>
"
" TODO:
" - Fix <%text> blocks to show HTML tags but ignore Mason tags.
@@ -74,6 +75,7 @@ syn cluster masonTop contains=masonLine,masonExpr,masonPerl,masonComp,masonArgs,
" syntax files.
hi def link masonDoc Comment
hi def link masonPod Comment
+hi def link podOrdinary masonPod
hi def link masonPerlComment perlComment
let b:current_syntax = "mason"
diff --git a/runtime/syntax/mupad.vim b/runtime/syntax/mupad.vim
index 55ea9c4e43..df87ad14fe 100644
--- a/runtime/syntax/mupad.vim
+++ b/runtime/syntax/mupad.vim
@@ -279,5 +279,6 @@ hi def link mupadType Type
hi def link mupadDefine Define
hi def link mupadIdentifier Identifier
+let b:current_syntax = 'mupad'
" TODO More comprehensive listing.
diff --git a/runtime/syntax/neomuttrc.vim b/runtime/syntax/neomuttrc.vim
index bd73de49ea..421b11ffa3 100644
--- a/runtime/syntax/neomuttrc.vim
+++ b/runtime/syntax/neomuttrc.vim
@@ -2,10 +2,10 @@
" Language: NeoMutt setup files
" Maintainer: Richard Russon <rich@flatcap.org>
" Previous Maintainer: Guillaume Brogi <gui-gui@netcourrier.com>
-" Last Change: 2020-06-21
+" Last Change: 2022-04-08
" Original version based on syntax/muttrc.vim
-" This file covers NeoMutt 2020-06-19
+" This file covers NeoMutt 2022-04-08
" quit when a syntax file was already loaded
if exists("b:current_syntax")
@@ -115,6 +115,8 @@ syntax region muttrcIndexFormatStr contained skipwhite keepend start=+"+ sk
syntax region muttrcIndexFormatStr contained skipwhite keepend start=+'+ skip=+\\'+ end=+'+ contains=muttrcIndexFormatEscapes,muttrcIndexFormatConditionals,muttrcFormatErrors,muttrcTimeEscapes nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr
syntax region muttrcMixFormatStr contained skipwhite keepend start=+"+ skip=+\\"+ end=+"+ contains=muttrcMixFormatEscapes,muttrcMixFormatConditionals,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr
syntax region muttrcMixFormatStr contained skipwhite keepend start=+'+ skip=+\\'+ end=+'+ contains=muttrcMixFormatEscapes,muttrcMixFormatConditionals,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr
+syntax region muttrcPatternFormatStr contained skipwhite keepend start=+"+ skip=+\\"+ end=+"+ contains=muttrcPatternFormatEscapes,muttrcPatternFormatConditionals,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr
+syntax region muttrcPatternFormatStr contained skipwhite keepend start=+'+ skip=+\\'+ end=+'+ contains=muttrcPatternFormatEscapes,muttrcPatternFormatConditionals,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr
syntax region muttrcPGPCmdFormatStr contained skipwhite keepend start=+"+ skip=+\\"+ end=+"+ contains=muttrcPGPCmdFormatEscapes,muttrcPGPCmdFormatConditionals,muttrcVariable,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr
syntax region muttrcPGPCmdFormatStr contained skipwhite keepend start=+'+ skip=+\\'+ end=+'+ contains=muttrcPGPCmdFormatEscapes,muttrcPGPCmdFormatConditionals,muttrcVariable,muttrcFormatErrors nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr
syntax region muttrcPGPFormatStr contained skipwhite keepend start=+"+ skip=+\\"+ end=+"+ contains=muttrcPGPFormatEscapes,muttrcPGPFormatConditionals,muttrcFormatErrors,muttrcPGPTimeEscapes nextgroup=muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr
@@ -144,35 +146,37 @@ function! s:escapesConditionals(baseName, sequence, padding, conditional)
endif
endfunction
-" CHECKED 2020-06-21
-" Ref: alias_format_str() in alias/dlgalias.c
+" CHECKED 2022-04-08
+" Ref: alias_format_str() in alias/dlg_alias.c
call s:escapesConditionals('AliasFormat', '[acfnrt]', 1, 0)
-" Ref: attach_format_str() in recvattach.c
+" Ref: attach_format_str() in attach/dlg_attach.c
call s:escapesConditionals('AttachFormat', '[CcDdeFfIMmnQsTtuX]', 1, 1)
-" Ref: compose_format_str() in compose.c
+" Ref: compose_format_str() in compose/cbar.c
call s:escapesConditionals('ComposeFormat', '[ahlv]', 1, 1)
-" Ref: folder_format_str() in browser.c
+" Ref: folder_format_str() in browser/browser.c
call s:escapesConditionals('FolderFormat', '[CDdFfgilmNnstu]', 1, 0)
-" Ref: group_index_format_str() in browser.c
+" Ref: group_index_format_str() in nntp/browse.c
call s:escapesConditionals('GroupIndexFormat', '[CdfMNns]', 1, 1)
" Ref: index_format_str() in hdrline.c
call s:escapesConditionals('IndexFormat', '[AaBbCDdEefgHIiJKLlMmNnOPqRrSsTtuvWXxYyZ(<[{]\|@\i\+@\|G[a-zA-Z]\+\|Fp\=\|z[cst]\|cr\=', 1, 1)
" Ref: mix_format_str() in remailer.c
call s:escapesConditionals('MixFormat', '[acns]', 1, 0)
+" Ref: pattern_format_str() in pattern/dlg_pattern.c
+call s:escapesConditionals('PatternFormat', '[den]', 1, 0)
" Ref: pgp_command_format_str() in ncrypt/pgpinvoke.c
call s:escapesConditionals('PGPCmdFormat', '[afprs]', 0, 1)
-" Ref: crypt_format_str() in ncrypt/crypt_gpgme.c
-" Ref: pgp_entry_format_str() in ncrypt/pgpkey.c
+" Ref: crypt_format_str() in ncrypt/dlg_gpgme.c
+" Ref: pgp_entry_format_str() in ncrypt/dlg_pgp.c
" Note: crypt_format_str() supports 'p', but pgp_entry_fmt() does not
call s:escapesConditionals('PGPFormat', '[AaCcFfKkLlnptu[]', 0, 0)
-" Ref: query_format_str() in alias/dlgquery.c
+" Ref: query_format_str() in alias/dlg_query.c
call s:escapesConditionals('QueryFormat', '[acent]', 1, 1)
-" Ref: sidebar_format_str() in sidebar.c
+" Ref: sidebar_format_str() in sidebar/window.c
call s:escapesConditionals('SidebarFormat', '[!BDdFLNnorStZ]', 1, 1)
" Ref: smime_command_format_str() in ncrypt/smime.c
call s:escapesConditionals('SmimeFormat', '[aCcdfiks]', 0, 1)
" Ref: status_format_str() in status.c
-call s:escapesConditionals('StatusFormat', '[bDdFfhLlMmnoPpRrSstuVv]', 1, 1)
+call s:escapesConditionals('StatusFormat', '[bDdFfhLlMmnoPpRrSsTtuVv]', 1, 1)
syntax region muttrcPGPTimeEscapes contained start=+%\[+ end=+\]+ contains=muttrcStrftimeEscapes
syntax region muttrcTimeEscapes contained start=+%(+ end=+)+ contains=muttrcStrftimeEscapes
@@ -187,6 +191,7 @@ syntax match muttrcVarEqualsFolderFmt contained skipwhite "=" nextgroup=mutt
syntax match muttrcVarEqualsGrpIdxFmt contained skipwhite "=" nextgroup=muttrcGroupIndexFormatStr
syntax match muttrcVarEqualsIdxFmt contained skipwhite "=" nextgroup=muttrcIndexFormatStr
syntax match muttrcVarEqualsMixFmt contained skipwhite "=" nextgroup=muttrcMixFormatStr
+syntax match muttrcVarEqualsPatternFmt contained skipwhite "=" nextgroup=muttrcPatternFormatStr
syntax match muttrcVarEqualsPGPCmdFmt contained skipwhite "=" nextgroup=muttrcPGPCmdFormatStr
syntax match muttrcVarEqualsPGPFmt contained skipwhite "=" nextgroup=muttrcPGPFormatStr
syntax match muttrcVarEqualsQueryFmt contained skipwhite "=" nextgroup=muttrcQueryFormatStr
@@ -197,9 +202,9 @@ syntax match muttrcVarEqualsStrftimeFmt contained skipwhite "=" nextgroup=mutt
syntax match muttrcVPrefix contained /[?&]/ nextgroup=muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr
-" CHECKED 2020-06-21
-" List of the different screens in mutt (see Menus in keymap.c)
-syntax keyword muttrcMenu contained alias attach browser compose editor generic index key_select_pgp key_select_smime mix pager pgp postpone query smime
+" CHECKED 2022-04-08
+" List of the different screens in NeoMutt (see MenuNames in menu/type.c)
+syntax keyword muttrcMenu contained alias attach autocrypt browser compose editor generic index key_select_pgp key_select_smime mix pager pgp postpone query smime
syntax match muttrcMenuList "\S\+" contained contains=muttrcMenu
syntax match muttrcMenuCommas /,/ contained
@@ -234,12 +239,12 @@ syntax match muttrcEscapedVariable contained "\\\$[a-zA-Z_-]\+"
syntax match muttrcBadAction contained "[^<>]\+" contains=muttrcEmail
syntax match muttrcAction contained "<[^>]\{-}>" contains=muttrcBadAction,muttrcFunction,muttrcKeyName
-" CHECKED 2020-06-21
-" First, functions that take regular expressions:
+" CHECKED 2022-04-08
+" First, hooks that take regular expressions:
syntax match muttrcRXHookNot contained /!\s*/ skipwhite nextgroup=muttrcRXHookString,muttrcRXHookStringNL
syntax match muttrcRXHooks /\<\%(account\|append\|close\|crypt\|folder\|mbox\|open\|pgp\)-hook\>/ skipwhite nextgroup=muttrcRXHookNot,muttrcRXHookString,muttrcRXHookStringNL
-" Now, functions that take patterns
+" Now, hooks that take patterns
syntax match muttrcPatHookNot contained /!\s*/ skipwhite nextgroup=muttrcPattern
syntax match muttrcPatHooks /\<\%(charset\|iconv\|index-format\)-hook\>/ skipwhite nextgroup=muttrcPatHookNot,muttrcPattern
syntax match muttrcPatHooks /\<\%(message\|reply\|send\|send2\|save\|fcc\|fcc-save\)-hook\>/ skipwhite nextgroup=muttrcPatHookNot,muttrcOptPattern
@@ -295,10 +300,10 @@ syntax match muttrcAliasNL contained /\s*\\$/ skipwhite skipnl nextgroup=muttrc
syntax match muttrcUnAliasKey contained "\s*\w\+\s*" skipwhite nextgroup=muttrcUnAliasKey,muttrcUnAliasNL
syntax match muttrcUnAliasNL contained /\s*\\$/ skipwhite skipnl nextgroup=muttrcUnAliasKey,muttrcUnAliasNL
-" CHECKED 2020-06-21
-" List of letters in Flags in pattern.c
+" CHECKED 2022-04-08
+" List of letters in Flags in pattern/flags.c
" Parameter: none
-syntax match muttrcSimplePat contained "!\?\^\?[~][ADEFGgklNOPpQRSTuUvV#$=]"
+syntax match muttrcSimplePat contained "!\?\^\?[~][ADEFGgklNOPpQRSTUuVv#$=]"
" Parameter: range
syntax match muttrcSimplePat contained "!\?\^\?[~][mnXz]\s*\%([<>-][0-9]\+[kM]\?\|[0-9]\+[kM]\?[-]\%([0-9]\+[kM]\?\)\?\)"
" Parameter: date
@@ -306,7 +311,7 @@ syntax match muttrcSimplePat contained "!\?\^\?[~][dr]\s*\%(\%(-\?[0-9]\{1,2}\%(
" Parameter: regex
syntax match muttrcSimplePat contained "!\?\^\?[~][BbCcefHhIiLMstwxYy]\s*" nextgroup=muttrcSimplePatRXContainer
" Parameter: pattern
-syntax match muttrcSimplePat contained "!\?\^\?[%][bBcCefhHiLstxy]\s*" nextgroup=muttrcSimplePatString
+syntax match muttrcSimplePat contained "!\?\^\?[%][BbCcefHhiLstxy]\s*" nextgroup=muttrcSimplePatString
" Parameter: pattern
syntax match muttrcSimplePat contained "!\?\^\?[=][bcCefhHiLstxy]\s*" nextgroup=muttrcSimplePatString
syntax region muttrcSimplePat contained keepend start=+!\?\^\?[~](+ end=+)+ contains=muttrcSimplePat
@@ -369,8 +374,8 @@ syntax keyword muttrcMonoAttrib contained bold none normal reverse standout unde
syntax keyword muttrcMono contained mono skipwhite nextgroup=muttrcColorField,muttrcColorCompose
syntax match muttrcMonoLine "^\s*mono\s\+\S\+" skipwhite nextgroup=muttrcMonoAttrib contains=muttrcMono
-" CHECKED 2020-06-21
-" List of fields in Fields in color.c
+" CHECKED 2022-04-08
+" List of fields in ColorFields in color/commmand.c
syntax keyword muttrcColorField skipwhite contained
\ attachment attach_headers body bold error hdrdefault header index index_author
\ index_collapsed index_date index_flags index_label index_number index_size index_subject
@@ -383,8 +388,8 @@ syntax match muttrcColorField contained "\<quoted\d\=\>"
syntax match muttrcColorCompose skipwhite contained /\s*compose\s*/ nextgroup=muttrcColorComposeField
-" CHECKED 2020-06-21
-" List of fields in ComposeFields in color.c
+" CHECKED 2022-04-08
+" List of fields in ComposeColorFields in color/command.c
syntax keyword muttrcColorComposeField skipwhite contained
\ header security_both security_encrypt security_none security_sign
\ nextgroup=muttrcColorFG,muttrcColorFGNL
@@ -411,20 +416,21 @@ function! s:boolQuadGen(type, vars, deprecated)
endfunction
-" CHECKED 2020-06-21
+" CHECKED 2022-04-08
" List of DT_BOOL in MuttVars in mutt_config.c
call s:boolQuadGen('Bool', [
- \ 'abort_backspace', 'allow_8bit', 'allow_ansi', 'arrow_cursor', 'ascii_chars', 'askbcc',
- \ 'askcc', 'ask_follow_up', 'ask_x_comment_to', 'attach_save_without_prompting',
- \ 'attach_split', 'autocrypt', 'autocrypt_reply', 'autoedit', 'auto_subscribe', 'auto_tag',
+ \ 'abort_backspace', 'allow_8bit', 'allow_ansi', 'arrow_cursor', 'ascii_chars', 'ask_bcc',
+ \ 'ask_cc', 'ask_follow_up', 'ask_x_comment_to', 'attach_save_without_prompting',
+ \ 'attach_split', 'autocrypt', 'autocrypt_reply', 'auto_edit', 'auto_subscribe', 'auto_tag',
\ 'beep', 'beep_new', 'bounce_delivered', 'braille_friendly',
\ 'browser_abbreviate_mailboxes', 'change_folder_next', 'check_mbox_size', 'check_new',
- \ 'collapse_all', 'collapse_flagged', 'collapse_unread', 'confirmappend', 'confirmcreate',
- \ 'crypt_autoencrypt', 'crypt_autopgp', 'crypt_autosign', 'crypt_autosmime',
- \ 'crypt_confirmhook', 'crypt_opportunistic_encrypt',
+ \ 'collapse_all', 'collapse_flagged', 'collapse_unread', 'compose_show_user_headers',
+ \ 'confirm_append', 'confirm_create', 'copy_decode_weed', 'count_alternatives',
+ \ 'crypt_auto_encrypt', 'crypt_auto_pgp', 'crypt_auto_sign', 'crypt_auto_smime',
+ \ 'crypt_confirm_hook', 'crypt_opportunistic_encrypt',
\ 'crypt_opportunistic_encrypt_strong_keys', 'crypt_protected_headers_read',
- \ 'crypt_protected_headers_save', 'crypt_protected_headers_write', 'crypt_replyencrypt',
- \ 'crypt_replysign', 'crypt_replysignencrypted', 'crypt_timestamp', 'crypt_use_gpgme',
+ \ 'crypt_protected_headers_save', 'crypt_protected_headers_write', 'crypt_reply_encrypt',
+ \ 'crypt_reply_sign', 'crypt_reply_sign_encrypted', 'crypt_timestamp', 'crypt_use_gpgme',
\ 'crypt_use_pka', 'delete_untag', 'digest_collapse', 'duplicate_threads', 'edit_headers',
\ 'encode_from', 'fast_reply', 'fcc_before_send', 'fcc_clear', 'flag_safe', 'followup_to',
\ 'force_name', 'forward_decode', 'forward_decrypt', 'forward_quote', 'forward_references',
@@ -433,45 +439,52 @@ call s:boolQuadGen('Bool', [
\ 'history_remove_dups', 'honor_disposition', 'idn_decode', 'idn_encode',
\ 'ignore_list_reply_to', 'imap_check_subscribed', 'imap_condstore', 'imap_deflate',
\ 'imap_idle', 'imap_list_subscribed', 'imap_passive', 'imap_peek', 'imap_qresync',
- \ 'imap_rfc5161', 'imap_servernoise', 'implicit_autoview', 'include_encrypted',
- \ 'include_onlyfirst', 'keep_flagged', 'mailcap_sanitize', 'maildir_check_cur',
- \ 'maildir_header_cache_verify', 'maildir_trash', 'mail_check_recent', 'mail_check_stats',
- \ 'markers', 'mark_old', 'menu_move_off', 'menu_scroll', 'message_cache_clean', 'meta_key',
- \ 'metoo', 'mh_purge', 'mime_forward_decode', 'mime_subject', 'mime_type_query_first',
- \ 'narrow_tree', 'nm_record', 'nntp_listgroup', 'nntp_load_description', 'pager_stop',
- \ 'pgp_autoinline', 'pgp_auto_decode', 'pgp_check_exit', 'pgp_check_gpg_decrypt_status_fd',
- \ 'pgp_ignore_subkeys', 'pgp_long_ids', 'pgp_replyinline', 'pgp_retainable_sigs',
+ \ 'imap_rfc5161', 'imap_server_noise', 'implicit_autoview', 'include_encrypted',
+ \ 'include_only_first', 'keep_flagged', 'local_date_header', 'mailcap_sanitize',
+ \ 'maildir_check_cur', 'maildir_header_cache_verify', 'maildir_trash', 'mail_check_recent',
+ \ 'mail_check_stats', 'markers', 'mark_old', 'menu_move_off', 'menu_scroll',
+ \ 'message_cache_clean', 'meta_key', 'me_too', 'mh_purge', 'mime_forward_decode',
+ \ 'mime_type_query_first', 'narrow_tree', 'nm_query_window_enable', 'nm_record',
+ \ 'nntp_listgroup', 'nntp_load_description', 'pager_stop', 'pgp_auto_decode',
+ \ 'pgp_auto_inline', 'pgp_check_exit', 'pgp_check_gpg_decrypt_status_fd',
+ \ 'pgp_ignore_subkeys', 'pgp_long_ids', 'pgp_reply_inline', 'pgp_retainable_sigs',
\ 'pgp_self_encrypt', 'pgp_show_unusable', 'pgp_strict_enc', 'pgp_use_gpg_agent',
- \ 'pipe_decode', 'pipe_split', 'pop_auth_try_all', 'pop_last', 'postpone_encrypt',
- \ 'print_decode', 'print_split', 'prompt_after', 'read_only', 'reflow_space_quotes',
- \ 'reflow_text', 'reply_self', 'reply_with_xorig', 'resolve', 'resume_draft_files',
- \ 'resume_edited_draft_files', 'reverse_alias', 'reverse_name', 'reverse_realname',
- \ 'rfc2047_parameters', 'save_address', 'save_empty', 'save_name', 'save_unsubscribed',
- \ 'score', 'show_new_news', 'show_only_unread', 'sidebar_folder_indent',
- \ 'sidebar_new_mail_only', 'sidebar_next_new_wrap', 'sidebar_non_empty_mailbox_only',
- \ 'sidebar_on_right', 'sidebar_short_path', 'sidebar_visible', 'sig_dashes', 'sig_on_top',
- \ 'size_show_bytes', 'size_show_fractions', 'size_show_mb', 'size_units_on_left',
- \ 'smart_wrap', 'smime_ask_cert_label', 'smime_decrypt_use_default_key', 'smime_is_default',
- \ 'smime_self_encrypt', 'sort_re', 'ssl_force_tls', 'ssl_usesystemcerts', 'ssl_use_sslv2',
- \ 'ssl_use_sslv3', 'ssl_use_tlsv1', 'ssl_use_tlsv1_1', 'ssl_use_tlsv1_2', 'ssl_use_tlsv1_3',
+ \ 'pipe_decode', 'pipe_decode_weed', 'pipe_split', 'pop_auth_try_all', 'pop_last',
+ \ 'postpone_encrypt', 'print_decode', 'print_decode_weed', 'print_split', 'prompt_after',
+ \ 'read_only', 'reflow_space_quotes', 'reflow_text', 'reply_self', 'reply_with_xorig',
+ \ 'resolve', 'resume_draft_files', 'resume_edited_draft_files', 'reverse_alias',
+ \ 'reverse_name', 'reverse_real_name', 'rfc2047_parameters', 'save_address', 'save_empty',
+ \ 'save_name', 'save_unsubscribed', 'score', 'show_new_news', 'show_only_unread',
+ \ 'sidebar_folder_indent', 'sidebar_new_mail_only', 'sidebar_next_new_wrap',
+ \ 'sidebar_non_empty_mailbox_only', 'sidebar_on_right', 'sidebar_short_path',
+ \ 'sidebar_visible', 'sig_dashes', 'sig_on_top', 'size_show_bytes', 'size_show_fractions',
+ \ 'size_show_mb', 'size_units_on_left', 'smart_wrap', 'smime_ask_cert_label',
+ \ 'smime_decrypt_use_default_key', 'smime_is_default', 'smime_self_encrypt', 'sort_re',
+ \ 'ssl_force_tls', 'ssl_use_sslv2', 'ssl_use_sslv3', 'ssl_use_system_certs',
+ \ 'ssl_use_tlsv1', 'ssl_use_tlsv1_1', 'ssl_use_tlsv1_2', 'ssl_use_tlsv1_3',
\ 'ssl_verify_dates', 'ssl_verify_host', 'ssl_verify_partial_chains', 'status_on_top',
\ 'strict_threads', 'suspend', 'text_flowed', 'thorough_search', 'thread_received', 'tilde',
- \ 'ts_enabled', 'uncollapse_jump', 'uncollapse_new', 'user_agent', 'use_8bitmime',
- \ 'use_domain', 'use_envelope_from', 'use_from', 'use_ipv6', 'virtual_spoolfile',
- \ 'wait_key', 'weed', 'wrap_search', 'write_bcc', 'x_comment_to'
+ \ 'ts_enabled', 'tunnel_is_secure', 'uncollapse_jump', 'uncollapse_new', 'user_agent',
+ \ 'use_8bit_mime', 'use_domain', 'use_envelope_from', 'use_from', 'use_ipv6',
+ \ 'virtual_spool_file', 'wait_key', 'weed', 'wrap_search', 'write_bcc', 'x_comment_to'
\ ], 0)
-" CHECKED 2020-06-21
+" CHECKED 2022-04-08
" Deprecated Bools
" List of DT_SYNONYM or DT_DEPRECATED Bools in MuttVars in mutt_config.c
call s:boolQuadGen('Bool', [
- \ 'edit_hdrs', 'envelope_from', 'forw_decode', 'forw_decrypt', 'forw_quote',
- \ 'header_cache_compress', 'ignore_linear_white_space', 'pgp_autoencrypt', 'pgp_autosign',
- \ 'pgp_auto_traditional', 'pgp_create_traditional', 'pgp_replyencrypt', 'pgp_replysign',
- \ 'pgp_replysignencrypted', 'xterm_set_titles'
+ \ 'askbcc', 'askcc', 'autoedit', 'confirmappend', 'confirmcreate', 'crypt_autoencrypt',
+ \ 'crypt_autopgp', 'crypt_autosign', 'crypt_autosmime', 'crypt_confirmhook',
+ \ 'crypt_replyencrypt', 'crypt_replysign', 'crypt_replysignencrypted', 'edit_hdrs',
+ \ 'envelope_from', 'forw_decode', 'forw_decrypt', 'forw_quote', 'header_cache_compress',
+ \ 'ignore_linear_white_space', 'imap_servernoise', 'include_onlyfirst', 'metoo',
+ \ 'mime_subject', 'pgp_autoencrypt', 'pgp_autoinline', 'pgp_autosign',
+ \ 'pgp_auto_traditional', 'pgp_create_traditional', 'pgp_replyencrypt', 'pgp_replyinline',
+ \ 'pgp_replysign', 'pgp_replysignencrypted', 'reverse_realname', 'ssl_usesystemcerts',
+ \ 'use_8bitmime', 'virtual_spoolfile', 'xterm_set_titles'
\ ], 1)
-" CHECKED 2020-06-21
+" CHECKED 2022-04-08
" List of DT_QUAD in MuttVars in mutt_config.c
call s:boolQuadGen('Quad', [
\ 'abort_noattach', 'abort_nosubject', 'abort_unmodified', 'bounce', 'catchup_newsgroup',
@@ -481,31 +494,32 @@ call s:boolQuadGen('Quad', [
\ 'post_moderated', 'print', 'quit', 'recall', 'reply_to', 'ssl_starttls',
\ ], 0)
-" CHECKED 2020-06-21
+" CHECKED 2022-04-08
" Deprecated Quads
" List of DT_SYNONYM or DT_DEPRECATED Quads in MuttVars in mutt_config.c
call s:boolQuadGen('Quad', [
\ 'mime_fwd', 'pgp_encrypt_self', 'pgp_verify_sig', 'smime_encrypt_self'
\ ], 1)
-" CHECKED 2020-06-21
+" CHECKED 2022-04-08
" List of DT_NUMBER or DT_LONG in MuttVars in mutt_config.c
syntax keyword muttrcVarNum skipwhite contained
- \ connect_timeout debug_level header_cache_compress_level history
- \ imap_fetch_chunk_size imap_keepalive imap_pipeline_depth imap_poll_timeout mail_check
- \ mail_check_stats_interval menu_context net_inc nm_db_limit nm_open_timeout
- \ nm_query_window_current_position nm_query_window_duration nntp_context nntp_poll
- \ pager_context pager_index_lines pgp_timeout pop_checkinterval read_inc reflow_wrap
- \ save_history score_threshold_delete score_threshold_flag score_threshold_read
- \ search_context sendmail_wait sidebar_component_depth sidebar_width skip_quoted_offset
- \ sleep_time smime_timeout ssl_min_dh_prime_bits timeout time_inc toggle_quoted_show_levels
- \ wrap wrap_headers write_inc
+ \ connect_timeout debug_level header_cache_compress_level history imap_fetch_chunk_size
+ \ imap_keepalive imap_pipeline_depth imap_poll_timeout mail_check mail_check_stats_interval
+ \ menu_context net_inc nm_db_limit nm_open_timeout nm_query_window_current_position
+ \ nm_query_window_duration nntp_context nntp_poll pager_context pager_index_lines
+ \ pager_read_delay pager_skip_quoted_context pgp_timeout pop_check_interval read_inc
+ \ reflow_wrap save_history score_threshold_delete score_threshold_flag score_threshold_read
+ \ search_context sendmail_wait sidebar_component_depth sidebar_width sleep_time
+ \ smime_timeout ssl_min_dh_prime_bits timeout time_inc toggle_quoted_show_levels wrap
+ \ wrap_headers write_inc
\ nextgroup=muttrcSetNumAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr
+" CHECKED 2022-04-08
+" Deprecated Numbers
syntax keyword muttrcVarDeprecatedNum contained skipwhite
- \ header_cache_pagesize wrapmargin
- \ nextgroup=muttrcSetNumAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr
+ \ header_cache_pagesize pop_checkinterval skip_quoted_offset
-" CHECKED 2020-06-21
+" CHECKED 2022-04-08
" List of DT_STRING in MuttVars in mutt_config.c
" Special cases first, and all the rest at the end
" Formats themselves must be updated in their respective groups
@@ -517,19 +531,19 @@ syntax keyword muttrcVarStr contained skipwhite compose_format nextgroup=muttrcV
syntax keyword muttrcVarStr contained skipwhite folder_format vfolder_format nextgroup=muttrcVarEqualsFolderFmt
syntax keyword muttrcVarStr contained skipwhite attribution forward_format index_format message_format pager_format nextgroup=muttrcVarEqualsIdxFmt
syntax keyword muttrcVarStr contained skipwhite mix_entry_format nextgroup=muttrcVarEqualsMixFmt
+syntax keyword muttrcVarStr contained skipwhite pattern_format nextgroup=muttrcVarEqualsPatternFmt
syntax keyword muttrcVarStr contained skipwhite
- \ pgp_clearsign_command pgp_decode_command pgp_decrypt_command
- \ pgp_encrypt_only_command pgp_encrypt_sign_command pgp_export_command pgp_getkeys_command
- \ pgp_import_command pgp_list_pubring_command pgp_list_secring_command
- \ pgp_sign_command pgp_verify_command pgp_verify_key_command
+ \ pgp_clear_sign_command pgp_decode_command pgp_decrypt_command pgp_encrypt_only_command
+ \ pgp_encrypt_sign_command pgp_export_command pgp_get_keys_command pgp_import_command
+ \ pgp_list_pubring_command pgp_list_secring_command pgp_sign_command pgp_verify_command
+ \ pgp_verify_key_command
\ nextgroup=muttrcVarEqualsPGPCmdFmt
syntax keyword muttrcVarStr contained skipwhite pgp_entry_format nextgroup=muttrcVarEqualsPGPFmt
syntax keyword muttrcVarStr contained skipwhite query_format nextgroup=muttrcVarEqualsQueryFmt
syntax keyword muttrcVarStr contained skipwhite
\ smime_decrypt_command smime_encrypt_command smime_get_cert_command
- \ smime_get_cert_email_command smime_get_signer_cert_command
- \ smime_import_cert_command smime_pk7out_command smime_sign_command
- \ smime_verify_command smime_verify_opaque_command
+ \ smime_get_cert_email_command smime_get_signer_cert_command smime_import_cert_command
+ \ smime_pk7out_command smime_sign_command smime_verify_command smime_verify_opaque_command
\ nextgroup=muttrcVarEqualsSmimeFmt
syntax keyword muttrcVarStr contained skipwhite status_format ts_icon_format ts_status_format nextgroup=muttrcVarEqualsStatusFmt
syntax keyword muttrcVarStr contained skipwhite date_format nextgroup=muttrcVarEqualsStrftimeFmt
@@ -538,64 +552,66 @@ syntax keyword muttrcVarStr contained skipwhite sidebar_format nextgroup=muttrcV
syntax keyword muttrcVarStr contained skipwhite
\ abort_key arrow_string assumed_charset attach_charset attach_sep attribution_locale
\ autocrypt_acct_format charset config_charset content_type crypt_protected_headers_subject
- \ default_hook dsn_notify dsn_return empty_subject escape forward_attribution_intro
- \ forward_attribution_trailer header_cache_backend header_cache_compress_method hidden_tags
- \ hostname imap_authenticators imap_delim_chars imap_headers imap_login imap_pass imap_user
- \ indent_string mailcap_path mark_macro_prefix mh_seq_flagged mh_seq_replied mh_seq_unseen
- \ newsgroups_charset news_server nm_default_url nm_exclude_tags nm_flagged_tag nm_query_type
- \ nm_query_window_current_search nm_query_window_timebase nm_record_tags nm_replied_tag
- \ nm_unread_tag nntp_authenticators nntp_pass nntp_user pgp_default_key pgp_sign_as pipe_sep
- \ pop_authenticators pop_host pop_pass pop_user postpone_encrypt_as post_indent_string
- \ preconnect preferred_languages realname send_charset show_multipart_alternative
- \ sidebar_delim_chars sidebar_divider_char sidebar_indent_string simple_search
- \ smime_default_key smime_encrypt_with smime_sign_as smime_sign_digest_alg
- \ smtp_authenticators smtp_pass smtp_url smtp_user spam_separator ssl_ciphers
+ \ default_hook dsn_notify dsn_return empty_subject forward_attribution_intro
+ \ forward_attribution_trailer greeting header_cache_backend header_cache_compress_method
+ \ hidden_tags hostname imap_authenticators imap_delim_chars imap_headers imap_login
+ \ imap_pass imap_user indent_string mailcap_path mark_macro_prefix mh_seq_flagged
+ \ mh_seq_replied mh_seq_unseen newsgroups_charset news_server nm_default_url nm_exclude_tags
+ \ nm_flagged_tag nm_query_type nm_query_window_current_search nm_query_window_or_terms
+ \ nm_query_window_timebase nm_record_tags nm_replied_tag nm_unread_tag nntp_authenticators
+ \ nntp_pass nntp_user pgp_default_key pgp_sign_as pipe_sep pop_authenticators pop_host
+ \ pop_pass pop_user postpone_encrypt_as post_indent_string preconnect preferred_languages
+ \ real_name send_charset show_multipart_alternative sidebar_delim_chars sidebar_divider_char
+ \ sidebar_indent_string simple_search smime_default_key smime_encrypt_with smime_sign_as
+ \ smime_sign_digest_alg smtp_authenticators smtp_pass smtp_url smtp_user spam_separator
+ \ ssl_ciphers
\ nextgroup=muttrcSetStrAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr
" Deprecated strings
syntax keyword muttrcVarDeprecatedStr
- \ abort_noattach_regexp attach_keyword forw_format hdr_format indent_str msg_format
- \ nm_default_uri pgp_self_encrypt_as post_indent_str print_cmd quote_regexp reply_regexp
- \ smime_self_encrypt_as xterm_icon xterm_title
+ \ abort_noattach_regexp attach_keyword escape forw_format hdr_format indent_str msg_format
+ \ nm_default_uri pgp_clearsign_command pgp_getkeys_command pgp_self_encrypt_as
+ \ post_indent_str print_cmd quote_regexp realname reply_regexp smime_self_encrypt_as
+ \ spoolfile visual xterm_icon xterm_title
-" CHECKED 2020-06-21
+" CHECKED 2022-04-08
" List of DT_ADDRESS
syntax keyword muttrcVarStr contained skipwhite envelope_from_address from nextgroup=muttrcSetStrAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr
" List of DT_ENUM
-syntax keyword muttrcVarStr contained skipwhite mbox_type nextgroup=muttrcSetStrAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr
+syntax keyword muttrcVarStr contained skipwhite mbox_type use_threads nextgroup=muttrcSetStrAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr
" List of DT_MBTABLE
syntax keyword muttrcVarStr contained skipwhite crypt_chars flag_chars from_chars status_chars to_chars nextgroup=muttrcSetStrAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr
-" CHECKED 2020-06-21
-" List of DT_PATH
+" CHECKED 2022-04-08
+" List of DT_PATH or DT_MAILBOX
syntax keyword muttrcVarStr contained skipwhite
\ alias_file attach_save_dir autocrypt_dir certificate_file debug_file
\ entropy_file folder header_cache history_file mbox message_cachedir newsrc
\ news_cache_dir postponed record signature smime_ca_location
- \ smime_certificates smime_keys spoolfile ssl_ca_certificates_file
- \ ssl_client_cert tmpdir trash
+ \ smime_certificates smime_keys spool_file ssl_ca_certificates_file ssl_client_cert
+ \ tmpdir trash
\ nextgroup=muttrcSetStrAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr
" List of DT_COMMAND (excluding pgp_*_command and smime_*_command)
syntax keyword muttrcVarStr contained skipwhite
\ display_filter editor inews ispell mixmaster new_mail_command pager
- \ print_command query_command sendmail shell visual external_search_command
+ \ print_command query_command sendmail shell external_search_command
\ imap_oauth_refresh_command pop_oauth_refresh_command
\ mime_type_query_command smtp_oauth_refresh_command tunnel
\ nextgroup=muttrcSetStrAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr
-" CHECKED 2020-06-21
+" CHECKED 2022-04-08
" List of DT_REGEX
syntax keyword muttrcVarStr contained skipwhite
- \ abort_noattach_regex gecos_mask mask pgp_decryption_okay pgp_good_sign
- \ quote_regex reply_regex smileys
+ \ abort_noattach_regex gecos_mask mask pgp_decryption_okay pgp_good_sign quote_regex
+ \ reply_regex smileys
\ nextgroup=muttrcSetStrAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr
" List of DT_SORT
syntax keyword muttrcVarStr contained skipwhite
\ pgp_sort_keys sidebar_sort_method sort sort_alias sort_aux sort_browser
\ nextgroup=muttrcSetStrAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr
-" CHECKED 2020-06-21
-" List of commands in Commands in mutt_config.c
+" CHECKED 2022-04-08
+" List of commands in mutt_commands in mutt_commands.c
" Remember to remove hooks, they have already been dealt with
syntax keyword muttrcCommand skipwhite alias nextgroup=muttrcAliasGroupDef,muttrcAliasKey,muttrcAliasNL
syntax keyword muttrcCommand skipwhite bind nextgroup=muttrcBindMenuList,muttrcBindMenuListNL
@@ -607,14 +623,12 @@ syntax keyword muttrcCommand skipwhite spam nextgroup=muttrcSpamPattern
syntax keyword muttrcCommand skipwhite unalias nextgroup=muttrcUnAliasKey,muttrcUnAliasNL
syntax keyword muttrcCommand skipwhite unhook nextgroup=muttrcHooks
syntax keyword muttrcCommand skipwhite
- \ alternative_order attachments auto_view finish hdr_order ifdef ifndef
- \ ignore lua lua-source mailboxes mailto_allow mime_lookup my_hdr push score
- \ setenv sidebar_whitelist source subjectrx subscribe-to tag-formats
- \ tag-transforms unalternative_order unattachments unauto_view uncolor
- \ unhdr_order unignore unmailboxes unmailto_allow unmime_lookup unmono
- \ unmy_hdr unscore unsetenv unsidebar_whitelist unsubjectrx unsubscribe-from
- \ unvirtual-mailboxes virtual-mailboxes named-mailboxes
- \ echo unbind unmacro
+ \ alternative_order attachments auto_view cd echo finish hdr_order ifdef ifndef ignore lua
+ \ lua-source mailboxes mailto_allow mime_lookup my_hdr named-mailboxes push score setenv
+ \ sidebar_whitelist source subjectrx subscribe-to tag-formats tag-transforms
+ \ unalternative_order unattachments unauto_view unbind uncolor unhdr_order unignore unmacro
+ \ unmailboxes unmailto_allow unmime_lookup unmono unmy_hdr unscore unsetenv
+ \ unsidebar_whitelist unsubjectrx unsubscribe-from unvirtual-mailboxes virtual-mailboxes
function! s:genFunctions(functions)
for f in a:functions
@@ -622,66 +636,68 @@ function! s:genFunctions(functions)
endfor
endfunction
-" CHECKED 2020-06-21
+" CHECKED 2022-04-08
" List of functions in functions.c
" Note: 'noop' is included but is elsewhere in the source
call s:genFunctions(['noop',
- \ 'accept', 'append', 'attach-file', 'attach-key', 'attach-message', 'attach-news-message',
- \ 'autocrypt-acct-menu', 'autocrypt-menu', 'backspace', 'backward-char', 'backward-word',
- \ 'bol', 'bottom-page', 'bottom', 'bounce-message', 'break-thread', 'buffy-cycle',
- \ 'buffy-list', 'capitalize-word', 'catchup', 'chain-next', 'chain-prev', 'change-dir',
- \ 'change-folder-readonly', 'change-folder', 'change-newsgroup-readonly',
- \ 'change-newsgroup', 'change-vfolder', 'check-new', 'check-stats',
+ \ 'accept', 'alias-dialog', 'append', 'attach-file', 'attach-key', 'attach-message',
+ \ 'attach-news-message', 'autocrypt-acct-menu', 'autocrypt-menu', 'backspace',
+ \ 'backward-char', 'backward-word', 'bol', 'bottom', 'bottom-page', 'bounce-message',
+ \ 'break-thread', 'buffy-cycle', 'buffy-list', 'capitalize-word', 'catchup', 'chain-next',
+ \ 'chain-prev', 'change-dir', 'change-folder', 'change-folder-readonly', 'change-newsgroup',
+ \ 'change-newsgroup-readonly', 'change-vfolder', 'check-new', 'check-stats',
\ 'check-traditional-pgp', 'clear-flag', 'collapse-all', 'collapse-parts',
- \ 'collapse-thread', 'complete-query', 'complete', 'compose-to-sender', 'copy-file',
+ \ 'collapse-thread', 'complete', 'complete-query', 'compose-to-sender', 'copy-file',
\ 'copy-message', 'create-account', 'create-alias', 'create-mailbox', 'current-bottom',
\ 'current-middle', 'current-top', 'decode-copy', 'decode-save', 'decrypt-copy',
- \ 'decrypt-save', 'delete-account', 'delete-char', 'delete-entry', 'delete-mailbox',
- \ 'delete-message', 'delete-pattern', 'delete-subthread', 'delete-thread', 'delete',
+ \ 'decrypt-save', 'delete', 'delete-account', 'delete-char', 'delete-entry',
+ \ 'delete-mailbox', 'delete-message', 'delete-pattern', 'delete-subthread', 'delete-thread',
\ 'descend-directory', 'detach-file', 'display-address', 'display-filename',
- \ 'display-message', 'display-toggle-weed', 'downcase-word', 'edit-bcc', 'edit-cc',
- \ 'edit-description', 'edit-encoding', 'edit-fcc', 'edit-file', 'edit-followup-to',
- \ 'edit-from', 'edit-headers', 'edit-label', 'edit-language', 'edit-message', 'edit-mime',
- \ 'edit-newsgroups', 'edit-or-view-raw-message', 'edit-raw-message', 'edit-reply-to',
- \ 'edit-subject', 'edit-to', 'edit-type', 'edit-x-comment-to', 'edit', 'end-cond',
- \ 'enter-command', 'enter-mask', 'entire-thread', 'eol', 'exit', 'extract-keys',
- \ 'fetch-mail', 'filter-entry', 'first-entry', 'flag-message', 'followup-message',
- \ 'forget-passphrase', 'forward-char', 'forward-message', 'forward-to-group',
- \ 'forward-word', 'get-attachment', 'get-children', 'get-message', 'get-parent',
- \ 'goto-folder', 'goto-parent', 'group-alternatives', 'group-chat-reply',
- \ 'group-multilingual', 'group-reply', 'half-down', 'half-up', 'help', 'history-down',
- \ 'history-search', 'history-up', 'imap-fetch-mail', 'imap-logout-all', 'insert', 'ispell',
- \ 'jump', 'kill-eol', 'kill-eow', 'kill-line', 'kill-word', 'last-entry',
- \ 'limit-current-thread', 'limit', 'link-threads', 'list-reply', 'mail-key',
- \ 'mailbox-cycle', 'mailbox-list', 'mail', 'mark-as-new', 'mark-message', 'middle-page',
- \ 'mix', 'modify-labels-then-hide', 'modify-labels', 'modify-tags-then-hide',
- \ 'modify-tags', 'move-down', 'move-up', 'new-mime', 'next-entry', 'next-line',
- \ 'next-new-then-unread', 'next-new', 'next-page', 'next-subthread', 'next-thread',
- \ 'next-undeleted', 'next-unread-mailbox', 'next-unread', 'parent-message', 'pgp-menu',
- \ 'pipe-entry', 'pipe-message', 'post-message', 'postpone-message', 'previous-entry',
- \ 'previous-line', 'previous-new-then-unread', 'previous-new', 'previous-page',
- \ 'previous-subthread', 'previous-thread', 'previous-undeleted', 'previous-unread',
- \ 'print-entry', 'print-message', 'purge-message', 'purge-thread', 'quasi-delete',
- \ 'query-append', 'query', 'quit', 'quote-char', 'read-subthread', 'read-thread',
- \ 'recall-message', 'reconstruct-thread', 'redraw-screen', 'refresh', 'reload-active',
- \ 'rename-attachment', 'rename-file', 'rename-mailbox', 'reply', 'resend-message',
- \ 'root-message', 'save-entry', 'save-message', 'search-next', 'search-opposite',
- \ 'search-reverse', 'search-toggle', 'search', 'select-entry', 'select-new',
+ \ 'display-message', 'display-toggle-weed', 'downcase-word', 'edit', 'edit-bcc', 'edit-cc',
+ \ 'edit-content-id', 'edit-description', 'edit-encoding', 'edit-fcc', 'edit-file',
+ \ 'edit-followup-to', 'edit-from', 'edit-headers', 'edit-label', 'edit-language',
+ \ 'edit-message', 'edit-mime', 'edit-newsgroups', 'edit-or-view-raw-message',
+ \ 'edit-raw-message', 'edit-reply-to', 'edit-subject', 'edit-to', 'edit-type',
+ \ 'edit-x-comment-to', 'end-cond', 'enter-command', 'enter-mask', 'entire-thread', 'eol',
+ \ 'error-history', 'exit', 'extract-keys', 'fetch-mail', 'filter-entry', 'first-entry',
+ \ 'flag-message', 'followup-message', 'forget-passphrase', 'forward-char',
+ \ 'forward-message', 'forward-to-group', 'forward-word', 'get-attachment', 'get-children',
+ \ 'get-message', 'get-parent', 'goto-folder', 'goto-parent', 'group-alternatives',
+ \ 'group-chat-reply', 'group-multilingual', 'group-related', 'group-reply', 'half-down',
+ \ 'half-up', 'help', 'history-down', 'history-search', 'history-up', 'imap-fetch-mail',
+ \ 'imap-logout-all', 'insert', 'ispell', 'jump', 'kill-eol', 'kill-eow', 'kill-line',
+ \ 'kill-word', 'last-entry', 'limit', 'limit-current-thread', 'link-threads', 'list-reply',
+ \ 'list-subscribe', 'list-unsubscribe', 'mail', 'mail-key', 'mailbox-cycle', 'mailbox-list',
+ \ 'mark-as-new', 'mark-message', 'middle-page', 'mix', 'modify-labels',
+ \ 'modify-labels-then-hide', 'modify-tags', 'modify-tags-then-hide', 'move-down', 'move-up',
+ \ 'new-mime', 'next-entry', 'next-line', 'next-new', 'next-new-then-unread', 'next-page',
+ \ 'next-subthread', 'next-thread', 'next-undeleted', 'next-unread', 'next-unread-mailbox',
+ \ 'parent-message', 'pgp-menu', 'pipe-entry', 'pipe-message', 'post-message',
+ \ 'postpone-message', 'previous-entry', 'previous-line', 'previous-new',
+ \ 'previous-new-then-unread', 'previous-page', 'previous-subthread', 'previous-thread',
+ \ 'previous-undeleted', 'previous-unread', 'print-entry', 'print-message', 'purge-message',
+ \ 'purge-thread', 'quasi-delete', 'query', 'query-append', 'quit', 'quote-char',
+ \ 'read-subthread', 'read-thread', 'recall-message', 'reconstruct-thread', 'redraw-screen',
+ \ 'refresh', 'reload-active', 'rename-attachment', 'rename-file', 'rename-mailbox', 'reply',
+ \ 'resend-message', 'root-message', 'save-entry', 'save-message', 'search', 'search-next',
+ \ 'search-opposite', 'search-reverse', 'search-toggle', 'select-entry', 'select-new',
\ 'send-message', 'set-flag', 'shell-escape', 'show-limit', 'show-log-messages',
- \ 'show-version', 'sidebar-next-new', 'sidebar-first', 'sidebar-last', 'sidebar-next',
- \ 'sidebar-open', 'sidebar-page-down', 'sidebar-page-up', 'sidebar-prev-new',
- \ 'sidebar-prev', 'sidebar-toggle-virtual', 'sidebar-toggle-visible', 'skip-quoted',
- \ 'smime-menu', 'sort-mailbox', 'sort-reverse', 'sort', 'subscribe-pattern',
- \ 'sync-mailbox', 'tag-entry', 'tag-message', 'tag-pattern', 'tag-prefix-cond',
- \ 'tag-prefix', 'tag-subthread', 'tag-thread', 'toggle-active', 'toggle-disposition',
- \ 'toggle-mailboxes', 'toggle-new', 'toggle-prefer-encrypt', 'toggle-quoted',
- \ 'toggle-read', 'toggle-recode', 'toggle-subscribed', 'toggle-unlink', 'toggle-write',
- \ 'top-page', 'top', 'transpose-chars', 'uncatchup', 'undelete-entry', 'undelete-message',
- \ 'undelete-pattern', 'undelete-subthread', 'undelete-thread', 'unsubscribe-pattern',
- \ 'untag-pattern', 'upcase-word', 'update-encoding', 'verify-key',
- \ 'vfolder-from-query-readonly', 'vfolder-from-query', 'vfolder-window-backward',
- \ 'vfolder-window-forward', 'view-attachments', 'view-attach', 'view-file', 'view-mailcap',
- \ 'view-name', 'view-raw-message', 'view-text', 'what-key', 'write-fcc'
+ \ 'show-version', 'sidebar-first', 'sidebar-last', 'sidebar-next', 'sidebar-next-new',
+ \ 'sidebar-open', 'sidebar-page-down', 'sidebar-page-up', 'sidebar-prev',
+ \ 'sidebar-prev-new', 'sidebar-toggle-virtual', 'sidebar-toggle-visible', 'skip-headers',
+ \ 'skip-quoted', 'smime-menu', 'sort', 'sort-alias', 'sort-alias-reverse', 'sort-mailbox',
+ \ 'sort-reverse', 'subscribe', 'subscribe-pattern', 'sync-mailbox', 'tag-entry',
+ \ 'tag-message', 'tag-pattern', 'tag-prefix', 'tag-prefix-cond', 'tag-subthread',
+ \ 'tag-thread', 'toggle-active', 'toggle-disposition', 'toggle-mailboxes', 'toggle-new',
+ \ 'toggle-prefer-encrypt', 'toggle-quoted', 'toggle-read', 'toggle-recode',
+ \ 'toggle-subscribed', 'toggle-unlink', 'toggle-write', 'top', 'top-page',
+ \ 'transpose-chars', 'uncatchup', 'undelete-entry', 'undelete-message', 'undelete-pattern',
+ \ 'undelete-subthread', 'undelete-thread', 'ungroup-attachment', 'unsubscribe',
+ \ 'unsubscribe-pattern', 'untag-pattern', 'upcase-word', 'update-encoding', 'verify-key',
+ \ 'vfolder-from-query', 'vfolder-from-query-readonly', 'vfolder-window-backward',
+ \ 'vfolder-window-forward', 'vfolder-window-reset', 'view-attach', 'view-attachments',
+ \ 'view-file', 'view-mailcap', 'view-name', 'view-pager', 'view-raw-message', 'view-text',
+ \ 'what-key', 'write-fcc'
\ ])
" Define the default highlighting.
@@ -758,6 +774,7 @@ highlight def link muttrcFolderFormatEscapes muttrcEscape
highlight def link muttrcGroupIndexFormatEscapes muttrcEscape
highlight def link muttrcIndexFormatEscapes muttrcEscape
highlight def link muttrcMixFormatEscapes muttrcEscape
+highlight def link muttrcPatternFormatEscapes muttrcEscape
highlight def link muttrcPGPCmdFormatEscapes muttrcEscape
highlight def link muttrcPGPFormatEscapes muttrcEscape
highlight def link muttrcPGPTimeEscapes muttrcEscape
@@ -774,6 +791,7 @@ highlight def link muttrcComposeFormatConditionals muttrcFormatConditionals2
highlight def link muttrcFolderFormatConditionals muttrcFormatConditionals2
highlight def link muttrcIndexFormatConditionals muttrcFormatConditionals2
highlight def link muttrcMixFormatConditionals muttrcFormatConditionals2
+highlight def link muttrcPatternFormatConditionals muttrcFormatConditionals2
highlight def link muttrcPGPCmdFormatConditionals muttrcFormatConditionals2
highlight def link muttrcPGPFormatConditionals muttrcFormatConditionals2
highlight def link muttrcSmimeFormatConditionals muttrcFormatConditionals2
@@ -789,6 +807,7 @@ highlight def link muttrcFolderFormatStr muttrcString
highlight def link muttrcGroupIndexFormatStr muttrcString
highlight def link muttrcIndexFormatStr muttrcString
highlight def link muttrcMixFormatStr muttrcString
+highlight def link muttrcPatternFormatStr muttrcString
highlight def link muttrcPGPCmdFormatStr muttrcString
highlight def link muttrcPGPFormatStr muttrcString
highlight def link muttrcQueryFormatStr muttrcString
diff --git a/runtime/syntax/openscad.vim b/runtime/syntax/openscad.vim
new file mode 100644
index 0000000000..1e20c743c4
--- /dev/null
+++ b/runtime/syntax/openscad.vim
@@ -0,0 +1,129 @@
+" Vim syntax file
+" Language: OpenSCAD
+" Maintainer: Niklas Adam <adam@oddodd.org>
+" Last change: 2022-04-15
+"
+"
+" From salkin-mada/openscad.nvim
+" Building on the work of Sirtaj Singh Kang and others for vim-openscad
+"
+
+" quit when a syntax file was already loaded
+if exists("b:current_syntax")
+ finish
+endif
+
+syntax case ignore
+
+setlocal iskeyword=a-z,A-Z,48-57,_
+
+syn match openscadAoperator "{"
+syn match openscadAoperator "}"
+syn match openscadLi "\["
+syn match openscadLi "\]"
+syn match openscadPar "("
+syn match openscadPar ")"
+
+syn match openscadSpecialVariable "\$[a-zA-Z_]\+\>" display
+syn match openscadModifier "^\s*[\*\!\#\%]" display
+
+syn match openscadBinaryoperator "+"
+syn match openscadBinaryoperator "-"
+syn match openscadBinaryoperator "*"
+syn match openscadBinaryoperator "/"
+syn match openscadBinaryoperator "%"
+syn match openscadBinaryoperator "\*\*"
+syn match openscadBinaryoperator "<"
+syn match openscadBinaryoperator "<="
+syn match openscadBinaryoperator ">"
+syn match openscadBinaryoperator ">="
+syn match openscadBinaryoperator "="
+syn match openscadBinaryoperator "=="
+syn match openscadBinaryoperator "==="
+syn match openscadBinaryoperator "!="
+syn match openscadBinaryoperator "!=="
+syn match openscadBinaryoperator "&"
+syn match openscadBinaryoperator "|"
+syn match openscadBinaryoperator "<!"
+syn match openscadBinaryoperator "?"
+syn match openscadBinaryoperator "??"
+syn match openscadBinaryoperator "!?"
+syn match openscadBinaryoperator "!"
+syn match openscadBinaryoperator "#"
+syn match openscadBinaryoperator "_"
+syn match openscadBinaryoperator "\.\."
+syn match openscadBinaryoperator "\.\.\."
+syn match openscadBinaryoperator "`"
+syn match openscadBinaryoperator ":"
+
+syn keyword openscadFunctionDef function nextgroup=openscadFunction skipwhite skipempty
+syn match openscadFunction /\<\h\w*\>/ contained display
+
+syn keyword openscadModuleDef module nextgroup=openscadModule skipwhite skipempty
+syn match openscadModule /\<\h\w*\>/ contained display
+
+syn keyword openscadStatement echo assign let assert
+syn keyword openscadConditional if else
+syn keyword openscadRepeat for intersection_for
+syn keyword openscadInclude include use
+syn keyword openscadCsgKeyword union difference intersection render intersection_for
+syn keyword openscadTransform scale rotate translate resize mirror multmatrix color minkowski hull projection linear_extrude rotate_extrude offset
+syn keyword openscadPrimitiveSolid cube sphere cylinder polyhedron surface
+syn keyword openscadPrimitive2D square circle polygon import_dxf text
+syn keyword openscadPrimitiveImport import child children
+
+syn match openscadNumbers "\<\d\|\.\d" contains=openscadNumber display transparent
+syn match openscadNumber "\d\+" display contained
+syn match openscadNumber "\.\d\+" display contained
+
+syn region openscadString start=/"/ skip=/\\"/ end=/"/
+
+syn keyword openscadBoolean true false
+
+syn keyword openscadCommentTodo TODO FIXME XXX NOTE contained display
+syn match openscadInlineComment ://.*$: contains=openscadCommentTodo
+syn region openscadBlockComment start=:/\*: end=:\*/: fold contains=openscadCommentTodo
+
+syn region openscadBlock start="{" end="}" transparent fold
+syn region openscadVector start="\[" end="\]" transparent fold
+
+syn keyword openscadBuiltin abs acos asin atan atan2 ceil cos exp floor ln log
+syn keyword openscadBuiltin lookup max min pow rands round sign sin sqrt tan
+syn keyword openscadBuiltin str len search version version_num concat chr ord cross norm
+syn keyword openscadBuiltin parent_module
+syn keyword openscadBuiltin dxf_cross dxf_dim
+syn keyword openscadBuiltinSpecial PI undef
+
+"""""""""""""""""""""""""""""""""""""""""
+" linkage
+"""""""""""""""""""""""""""""""""""""""""
+hi def link openscadFunctionDef Structure
+hi def link openscadAoperator Function
+hi def link openscadLi Function
+" hi def link openscadPar Structure
+hi def link openscadBuiltinSpecial Special
+hi def link openscadBinaryoperator Special
+hi def link openscadFunction Function
+hi def link openscadModuleDef Structure
+hi def link openscadModule Function
+hi def link openscadBlockComment Comment
+hi def link openscadBoolean Boolean
+hi def link openscadBuiltin Function
+hi def link openscadConditional Conditional
+hi def link openscadCsgKeyword Structure
+hi def link openscadInclude Include
+hi def link openscadInlineComment Comment
+hi def link openscadModifier Special
+hi def link openscadStatement Statement
+hi def link openscadNumbers Number
+hi def link openscadNumber Number
+hi def link openscadPrimitiveSolid Keyword
+hi def link openscadPrimitive2D Keyword
+hi def link openscadPrimitiveImport Keyword
+hi def link openscadRepeat Repeat
+hi def link openscadSpecialVariable Special
+hi def link openscadString String
+hi def link openscadTransform Statement
+hi def link openscadCommentTodo Todo
+
+let b:current_syntax = 'openscad'
diff --git a/runtime/syntax/perl.vim b/runtime/syntax/perl.vim
index f4f9520de5..73f967a183 100644
--- a/runtime/syntax/perl.vim
+++ b/runtime/syntax/perl.vim
@@ -1,15 +1,17 @@
" Vim syntax file
-" Language: Perl 5
+" Language: Perl
" Maintainer: vim-perl <vim-perl@googlegroups.com>
-" Homepage: https://github.com/vim-perl/vim-perl/tree/master
+" Homepage: https://github.com/vim-perl/vim-perl
" Bugs/requests: https://github.com/vim-perl/vim-perl/issues
-" Last Change: 2020 Apr 15
+" License: Vim License (see :help license)
+" Last Change: 2022 Jun 13
" Contributors: Andy Lester <andy@petdance.com>
" Hinrik Örn Sigurðsson <hinrik.sig@gmail.com>
" Lukas Mai <l.mai.web.de>
" Nick Hibma <nick@van-laarhoven.org>
" Sonia Heimann <niania@netsurf.org>
" Rob Hoelz <rob@hoelz.ro>
+" Doug Kearns <dougkearns@gmail.com>
" and many others.
"
" Please download the most recent version first, before mailing
@@ -41,11 +43,11 @@ set cpo&vim
" POD starts with ^=<word> and ends with ^=cut
-if !exists("perl_include_pod") || perl_include_pod == 1
+if get(g:, 'perl_include_pod', 1)
" Include a while extra syntax file
syn include @Pod syntax/pod.vim
unlet b:current_syntax
- if exists("perl_fold")
+ if get(g:, 'perl_fold', 1)
syn region perlPOD start="^=[a-z]" end="^=cut" contains=@Pod,@Spell,perlTodo keepend fold extend
syn region perlPOD start="^=cut" end="^=cut" contains=perlTodo keepend fold extend
else
@@ -54,7 +56,7 @@ if !exists("perl_include_pod") || perl_include_pod == 1
endif
else
" Use only the bare minimum of rules
- if exists("perl_fold")
+ if get(g:, 'perl_fold', 1)
syn region perlPOD start="^=[a-z]" end="^=cut" fold
else
syn region perlPOD start="^=[a-z]" end="^=cut"
@@ -92,7 +94,7 @@ syn match perlStatementFiles "\<\%(ch\%(dir\|mod\|own\|root\)\|glob\|link\|mkdi
syn match perlStatementFiles "-[rwxoRWXOezsfdlpSbctugkTBMAC]\>"
syn match perlStatementFlow "\<\%(caller\|die\|dump\|eval\|exit\|wantarray\|evalbytes\)\>"
syn match perlStatementInclude "\<\%(require\|import\|unimport\)\>"
-syn match perlStatementInclude "\<\%(use\|no\)\s\+\%(\%(attributes\|attrs\|autodie\|autouse\|parent\|base\|big\%(int\|num\|rat\)\|blib\|bytes\|charnames\|constant\|diagnostics\|encoding\%(::warnings\)\=\|feature\|fields\|filetest\|if\|integer\|less\|lib\|locale\|mro\|open\|ops\|overload\|overloading\|re\|sigtrap\|sort\|strict\|subs\|threads\%(::shared\)\=\|utf8\|vars\|version\|vmsish\|warnings\%(::register\)\=\)\>\)\="
+syn match perlStatementInclude "\<\%(use\|no\)\s\+\%(\%(attributes\|attrs\|autodie\%(::\%(exception\%(::system\)\=\|hints\|skip\)\)\=\|autouse\|parent\|base\|big\%(int\|num\|rat\)\|blib\|bytes\|charnames\|constant\|deprecate\|diagnostics\|encoding\%(::warnings\)\=\|experimental\|feature\|fields\|filetest\|if\|integer\|less\|lib\|locale\|mro\|ok\|open\|ops\|overload\|overloading\|re\|sigtrap\|sort\|strict\|subs\|threads\%(::shared\)\=\|utf8\|vars\|version\|vmsish\|warnings\%(::register\)\=\)\>\)\="
syn match perlStatementProc "\<\%(alarm\|exec\|fork\|get\%(pgrp\|ppid\|priority\)\|kill\|pipe\|set\%(pgrp\|priority\)\|sleep\|system\|times\|wait\%(pid\)\=\)\>"
syn match perlStatementSocket "\<\%(accept\|bind\|connect\|get\%(peername\|sock\%(name\|opt\)\)\|listen\|recv\|send\|setsockopt\|shutdown\|socket\%(pair\)\=\)\>"
syn match perlStatementIPC "\<\%(msg\%(ctl\|get\|rcv\|snd\)\|sem\%(ctl\|get\|op\)\|shm\%(ctl\|get\|read\|write\)\)\>"
@@ -138,38 +140,36 @@ syn match perlPackageRef "[$@#%*&]\%(\%(::\|'\)\=\I\i*\%(\%(::\|'\)\I\i*\)*\)\
" If you don't want complex things like @{${"foo"}} to be processed,
" just set the variable "perl_no_extended_vars"...
-if !exists("perl_no_scope_in_variables")
- syn match perlVarPlain "\%([@$]\|\$#\)\$*\%(\I\i*\)\=\%(\%(::\|'\)\I\i*\)*\%(::\|\i\@<=\)" contains=perlPackageRef nextgroup=perlVarMember,perlVarSimpleMember,perlMethod,perlPostDeref
- syn match perlVarPlain2 "%\$*\%(\I\i*\)\=\%(\%(::\|'\)\I\i*\)*\%(::\|\i\@<=\)" contains=perlPackageRef nextgroup=perlVarMember,perlVarSimpleMember,perlMethod,perlPostDeref
- syn match perlFunctionName "&\$*\%(\I\i*\)\=\%(\%(::\|'\)\I\i*\)*\%(::\|\i\@<=\)" contains=perlPackageRef nextgroup=perlVarMember,perlVarSimpleMember,perlMethod,perlPostDeref
+if !get(g:, 'perl_no_scope_in_variables', 0)
+ syn match perlVarPlain "\%([@$]\|\$#\)\$*\%(\I\i*\)\=\%(\%(::\|'\)\I\i*\)*\%(::\|\i\@<=\)" contains=perlPackageRef nextgroup=perlVarMember,perlVarSimpleMember,perlPostDeref
+ syn match perlVarPlain2 "%\$*\%(\I\i*\)\=\%(\%(::\|'\)\I\i*\)*\%(::\|\i\@<=\)" contains=perlPackageRef nextgroup=perlVarMember,perlVarSimpleMember,perlPostDeref
+ syn match perlFunctionName "&\$*\%(\I\i*\)\=\%(\%(::\|'\)\I\i*\)*\%(::\|\i\@<=\)" contains=perlPackageRef nextgroup=perlVarMember,perlVarSimpleMember,perlPostDeref
else
- syn match perlVarPlain "\%([@$]\|\$#\)\$*\%(\I\i*\)\=\%(\%(::\|'\)\I\i*\)*\%(::\|\i\@<=\)" nextgroup=perlVarMember,perlVarSimpleMember,perlMethod,perlPostDeref
- syn match perlVarPlain2 "%\$*\%(\I\i*\)\=\%(\%(::\|'\)\I\i*\)*\%(::\|\i\@<=\)" nextgroup=perlVarMember,perlVarSimpleMember,perlMethod,perlPostDeref
- syn match perlFunctionName "&\$*\%(\I\i*\)\=\%(\%(::\|'\)\I\i*\)*\%(::\|\i\@<=\)" nextgroup=perlVarMember,perlVarSimpleMember,perlMethod,perlPostDeref
+ syn match perlVarPlain "\%([@$]\|\$#\)\$*\%(\I\i*\)\=\%(\%(::\|'\)\I\i*\)*\%(::\|\i\@<=\)" nextgroup=perlVarMember,perlVarSimpleMember,perlPostDeref
+ syn match perlVarPlain2 "%\$*\%(\I\i*\)\=\%(\%(::\|'\)\I\i*\)*\%(::\|\i\@<=\)" nextgroup=perlVarMember,perlVarSimpleMember,perlPostDeref
+ syn match perlFunctionName "&\$*\%(\I\i*\)\=\%(\%(::\|'\)\I\i*\)*\%(::\|\i\@<=\)" nextgroup=perlVarMember,perlVarSimpleMember,perlPostDeref
endif
syn match perlVarPlain2 "%[-+]"
-if !exists("perl_no_extended_vars")
- syn cluster perlExpr contains=perlStatementIndirObjWrap,perlStatementScalar,perlStatementRegexp,perlStatementNumeric,perlStatementList,perlStatementHash,perlStatementFiles,perlStatementTime,perlStatementMisc,perlVarPlain,perlVarPlain2,perlVarNotInMatches,perlVarSlash,perlVarBlock,perlVarBlock2,perlShellCommand,perlFloat,perlNumber,perlStringUnexpanded,perlString,perlQQ,perlArrow,perlBraces
- syn region perlArrow matchgroup=perlArrow start="->\s*(" end=")" contains=@perlExpr nextgroup=perlVarMember,perlVarSimpleMember,perlMethod,perlPostDeref contained
- syn region perlArrow matchgroup=perlArrow start="->\s*\[" end="\]" contains=@perlExpr nextgroup=perlVarMember,perlVarSimpleMember,perlMethod,perlPostDeref contained
- syn region perlArrow matchgroup=perlArrow start="->\s*{" end="}" contains=@perlExpr nextgroup=perlVarMember,perlVarSimpleMember,perlMethod,perlPostDeref contained
- syn match perlArrow "->\s*{\s*\I\i*\s*}" contains=perlVarSimpleMemberName nextgroup=perlVarMember,perlVarSimpleMember,perlMethod,perlPostDeref contained
- syn region perlArrow matchgroup=perlArrow start="->\s*\$*\I\i*\s*(" end=")" contains=@perlExpr nextgroup=perlVarMember,perlVarSimpleMember,perlMethod,perlPostDeref contained
- syn region perlVarBlock matchgroup=perlVarPlain start="\%($#\|[$@]\)\$*{" skip="\\}" end=+}\|\%(\%(<<\%('\|"\)\?\)\@=\)+ contains=@perlExpr nextgroup=perlVarMember,perlVarSimpleMember,perlMethod,perlPostDeref extend
- syn region perlVarBlock2 matchgroup=perlVarPlain start="[%&*]\$*{" skip="\\}" end=+}\|\%(\%(<<\%('\|"\)\?\)\@=\)+ contains=@perlExpr nextgroup=perlVarMember,perlVarSimpleMember,perlMethod,perlPostDeref extend
- syn match perlVarPlain2 "[%&*]\$*{\I\i*}" nextgroup=perlVarMember,perlVarSimpleMember,perlMethod,perlPostDeref extend
- syn match perlVarPlain "\%(\$#\|[@$]\)\$*{\I\i*}" nextgroup=perlVarMember,perlVarSimpleMember,perlMethod,perlPostDeref extend
- syn region perlVarMember matchgroup=perlVarPlain start="\%(->\)\={" skip="\\}" end="}" contained contains=@perlExpr nextgroup=perlVarMember,perlVarSimpleMember,perlMethod,perlPostDeref extend
- syn match perlVarSimpleMember "\%(->\)\={\s*\I\i*\s*}" nextgroup=perlVarMember,perlVarSimpleMember,perlMethod,perlPostDeref contains=perlVarSimpleMemberName contained extend
+if !get(g:, 'perl_no_extended_vars', 0)
+ syn cluster perlExpr contains=perlStatementIndirObjWrap,perlStatementScalar,perlStatementRegexp,perlStatementNumeric,perlStatementList,perlStatementHash,perlStatementFiles,perlStatementTime,perlStatementMisc,perlVarPlain,perlVarPlain2,perlVarNotInMatches,perlVarSlash,perlVarBlock,perlVarBlock2,perlShellCommand,perlFloat,perlNumber,perlStringUnexpanded,perlString,perlQ,perlQQ,perlQW,perlQR,perlArrow,perlBraces
+ syn region perlArrow matchgroup=perlArrow start="->\s*(" end=")" contains=@perlExpr nextgroup=perlVarMember,perlVarSimpleMember,perlPostDeref contained
+ syn region perlArrow matchgroup=perlArrow start="->\s*\[" end="\]" contains=@perlExpr nextgroup=perlVarMember,perlVarSimpleMember,perlPostDeref contained
+ syn region perlArrow matchgroup=perlArrow start="->\s*{" end="}" contains=@perlExpr nextgroup=perlVarMember,perlVarSimpleMember,perlPostDeref contained
+ syn match perlArrow "->\s*{\s*\I\i*\s*}" contains=perlVarSimpleMemberName nextgroup=perlVarMember,perlVarSimpleMember,perlPostDeref contained
+ syn region perlVarBlock matchgroup=perlVarPlain start="\%($#\|[$@]\)\$*{" skip="\\}" end=+}\|\%(\%(<<\%('\|"\)\?\)\@=\)+ contains=@perlExpr nextgroup=perlVarMember,perlVarSimpleMember,perlPostDeref extend
+ syn region perlVarBlock2 matchgroup=perlVarPlain start="[%&*]\$*{" skip="\\}" end=+}\|\%(\%(<<\%('\|"\)\?\)\@=\)+ contains=@perlExpr nextgroup=perlVarMember,perlVarSimpleMember,perlPostDeref extend
+ syn match perlVarPlain2 "[%&*]\$*{\I\i*}" nextgroup=perlVarMember,perlVarSimpleMember,perlPostDeref extend
+ syn match perlVarPlain "\%(\$#\|[@$]\)\$*{\I\i*}" nextgroup=perlVarMember,perlVarSimpleMember,perlPostDeref extend
+ syn region perlVarMember matchgroup=perlVarPlain start="\%(->\)\={" skip="\\}" end="}" contained contains=@perlExpr nextgroup=perlVarMember,perlVarSimpleMember,perlPostDeref extend
+ syn match perlVarSimpleMember "\%(->\)\={\s*\I\i*\s*}" nextgroup=perlVarMember,perlVarSimpleMember,perlPostDeref contains=perlVarSimpleMemberName contained extend
syn match perlVarSimpleMemberName "\I\i*" contained
- syn region perlVarMember matchgroup=perlVarPlain start="\%(->\)\=\[" skip="\\]" end="]" contained contains=@perlExpr nextgroup=perlVarMember,perlVarSimpleMember,perlMethod,perlPostDeref extend
- syn match perlPackageConst "__PACKAGE__" nextgroup=perlMethod,perlPostDeref
- syn match perlMethod "->\$*\I\i*" contained nextgroup=perlVarSimpleMember,perlVarMember,perlMethod,perlPostDeref
- syn match perlPostDeref "->\%($#\|[$@%&*]\)\*" contained nextgroup=perlVarSimpleMember,perlVarMember,perlMethod,perlPostDeref
- syn region perlPostDeref start="->\%($#\|[$@%&*]\)\[" skip="\\]" end="]" contained contains=@perlExpr nextgroup=perlVarSimpleMember,perlVarMember,perlMethod,perlPostDeref
- syn region perlPostDeref matchgroup=perlPostDeref start="->\%($#\|[$@%&*]\){" skip="\\}" end="}" contained contains=@perlExpr nextgroup=perlVarSimpleMember,perlVarMember,perlMethod,perlPostDeref
+ syn region perlVarMember matchgroup=perlVarPlain start="\%(->\)\=\[" skip="\\]" end="]" contained contains=@perlExpr nextgroup=perlVarMember,perlVarSimpleMember,perlPostDeref extend
+ syn match perlPackageConst "__PACKAGE__" nextgroup=perlPostDeref
+ syn match perlPostDeref "->\%($#\|[$@%&*]\)\*" contained nextgroup=perlVarSimpleMember,perlVarMember,perlPostDeref
+ syn region perlPostDeref start="->\%($#\|[$@%&*]\)\[" skip="\\]" end="]" contained contains=@perlExpr nextgroup=perlVarSimpleMember,perlVarMember,perlPostDeref
+ syn region perlPostDeref matchgroup=perlPostDeref start="->\%($#\|[$@%&*]\){" skip="\\}" end="}" keepend extend contained contains=@perlExpr nextgroup=perlVarSimpleMember,perlVarMember,perlPostDeref
endif
" File Descriptors
@@ -221,10 +221,16 @@ syn region perlShellCommand matchgroup=perlMatchStartEnd start="`" end="`" cont
" Constants
"
" Numbers
-syn match perlNumber "\<\%(0\%(x\x[[:xdigit:]_]*\|b[01][01_]*\|\o[0-7_]*\|\)\|[1-9][[:digit:]_]*\)\>"
-syn match perlFloat "\<\d[[:digit:]_]*[eE][\-+]\=\d\+"
-syn match perlFloat "\<\d[[:digit:]_]*\.[[:digit:]_]*\%([eE][\-+]\=\d\+\)\="
-syn match perlFloat "\.[[:digit:]][[:digit:]_]*\%([eE][\-+]\=\d\+\)\="
+syn case ignore
+syn match perlNumber "\<\%(0\|[1-9]\%(_\=\d\)*\)\>"
+syn match perlNumber "\<0\%(x\x\%(_\=\x\)*\|b[01]\%(_\=[01]\)*\|o\=\%(_\=\o\)*\)\>"
+syn match perlFloat "\<\d\%(_\=\d\)*e[-+]\=\d\%(_\=\d\)*"
+syn match perlFloat "\<\d\%(_\=\d\)*\.\%(\d\%(_\=\d\)*\)\=\%(e[-+]\=\d\%(_\=\d\)*\)\="
+syn match perlFloat "\.\d\%(_\=\d\)*\%(e[-+]\=\d\%(_\=\d\)*\)\="
+syn match perlFloat "\<0x\x\%(_\=\x\)*p[-+]\=\d\%(_\=\d\)*"
+syn match perlFloat "\<0x\x\%(_\=\x\)*\.\%(\x\%(_\=\x\)*\)\=\%(p[-+]\=\d\%(_\=\d\)*\)\="
+syn match perlFloat "\<0x\.\x\%(_\=\x\)*\%(p[-+]\=\d\%(_\=\d\)*\)\="
+syn case match
syn match perlString "\<\%(v\d\+\%(\.\d\+\)*\|\d\+\%(\.\d\+\)\{2,}\)\>" contains=perlVStringV
syn match perlVStringV "\<v" contained
@@ -242,22 +248,24 @@ syn region perlAnglesDQ start=+<+ end=+>+ extend contained contains=perlAnglesD
" Simple version of searches and matches
-syn region perlMatch matchgroup=perlMatchStartEnd start=+\<\%(::\|'\|->\)\@<!m\>\s*\z([^[:space:]'([{<#]\)+ end=+\z1[msixpodualgcn]*+ contains=@perlInterpMatch keepend extend
-syn region perlMatch matchgroup=perlMatchStartEnd start=+\<\%(::\|'\|->\)\@<!m#+ end=+#[msixpodualgcn]*+ contains=@perlInterpMatch keepend extend
-syn region perlMatch matchgroup=perlMatchStartEnd start=+\<\%(::\|'\|->\)\@<!m\s*'+ end=+'[msixpodualgcn]*+ contains=@perlInterpSQ keepend extend
-syn region perlMatch matchgroup=perlMatchStartEnd start=+\<\%(::\|'\|->\)\@<!m\s*/+ end=+/[msixpodualgcn]*+ contains=@perlInterpSlash keepend extend
-syn region perlMatch matchgroup=perlMatchStartEnd start=+\<\%(::\|'\|->\)\@<!m\s*(+ end=+)[msixpodualgcn]*+ contains=@perlInterpMatch,perlParensDQ keepend extend
-syn region perlMatch matchgroup=perlMatchStartEnd start=+\<\%(::\|'\|->\)\@<!m\s*{+ end=+}[msixpodualgcn]*+ contains=@perlInterpMatch,perlBracesDQ extend
-syn region perlMatch matchgroup=perlMatchStartEnd start=+\<\%(::\|'\|->\)\@<!m\s*<+ end=+>[msixpodualgcn]*+ contains=@perlInterpMatch,perlAnglesDQ keepend extend
-syn region perlMatch matchgroup=perlMatchStartEnd start=+\<\%(::\|'\|->\)\@<!m\s*\[+ end=+\][msixpodualgcn]*+ contains=@perlInterpMatch,perlBracketsDQ keepend extend
+syn match perlMatchModifiers "[msixpadluncgo]\+" contained
+syn region perlMatch matchgroup=perlMatchStartEnd start=+\<\%(::\|'\|->\)\@<!m\>\s*\z([^[:space:]'([{<#]\)+ end=+\z1+ contains=@perlInterpMatch keepend extend nextgroup=perlMatchModifiers
+syn region perlMatch matchgroup=perlMatchStartEnd start=+\<\%(::\|'\|->\)\@<!m#+ end=+#+ contains=@perlInterpMatch keepend extend nextgroup=perlMatchModifiers
+syn region perlMatch matchgroup=perlMatchStartEnd start=+\<\%(::\|'\|->\)\@<!m\s*'+ end=+'+ contains=@perlInterpSQ keepend extend nextgroup=perlMatchModifiers
+syn region perlMatch matchgroup=perlMatchStartEnd start=+\<\%(::\|'\|->\)\@<!m\s*/+ end=+/+ contains=@perlInterpSlash keepend extend nextgroup=perlMatchModifiers
+syn region perlMatch matchgroup=perlMatchStartEnd start=+\<\%(::\|'\|->\)\@<!m\s*(+ end=+)+ contains=@perlInterpMatch,perlParensDQ keepend extend nextgroup=perlMatchModifiers
+syn region perlMatch matchgroup=perlMatchStartEnd start=+\<\%(::\|'\|->\)\@<!m\s*{+ end=+}+ contains=@perlInterpMatch,perlBracesDQ extend nextgroup=perlMatchModifiers
+syn region perlMatch matchgroup=perlMatchStartEnd start=+\<\%(::\|'\|->\)\@<!m\s*<+ end=+>+ contains=@perlInterpMatch,perlAnglesDQ keepend extend nextgroup=perlMatchModifiers
+syn region perlMatch matchgroup=perlMatchStartEnd start=+\<\%(::\|'\|->\)\@<!m\s*\[+ end=+\]+ contains=@perlInterpMatch,perlBracketsDQ keepend extend nextgroup=perlMatchModifiers
" Below some hacks to recognise the // variant. This is virtually impossible to catch in all
" cases as the / is used in so many other ways, but these should be the most obvious ones.
-syn region perlMatch matchgroup=perlMatchStartEnd start="\%([$@%&*]\@<!\%(\<split\|\<while\|\<if\|\<unless\|\.\.\|[-+*!~(\[{=]\)\s*\)\@<=/\%(/=\)\@!" start=+^/\%(/=\)\@!+ start=+\s\@<=/\%(/=\)\@![^[:space:][:digit:]$@%=]\@=\%(/\_s*\%([([{$@%&*[:digit:]"'`]\|\_s\w\|[[:upper:]_abd-fhjklnqrt-wyz]\)\)\@!+ skip=+\\/+ end=+/[msixpodualgcn]*+ contains=@perlInterpSlash extend
+syn region perlMatch matchgroup=perlMatchStartEnd start="\%([$@%&*]\@<!\%(\<split\|\<while\|\<if\|\<unless\|\.\.\|[-+*!~(\[{=]\)\s*\)\@<=/\%(/=\)\@!" start=+^/\%(/=\)\@!+ start=+\s\@<=/\%(/=\)\@![^[:space:][:digit:]$@%=]\@=\%(/\_s*\%([([{$@%&*[:digit:]"'`]\|\_s\w\|[[:upper:]_abd-fhjklnqrt-wyz]\)\)\@!+ skip=+\\/+ end=+/+ contains=@perlInterpSlash extend nextgroup=perlMatchModifiers
" Substitutions
" perlMatch is the first part, perlSubstitution* is the substitution part
+syn match perlSubstitutionModifiers "[msixpadluncgero]\+" contained
syn region perlMatch matchgroup=perlMatchStartEnd start=+\<\%(::\|'\|->\)\@<!s\>\s*\z([^[:space:]'([{<#]\)+ end=+\z1+me=e-1 contains=@perlInterpMatch nextgroup=perlSubstitutionGQQ keepend extend
syn region perlMatch matchgroup=perlMatchStartEnd start=+\<\%(::\|'\|->\)\@<!s\s*'+ end=+'+me=e-1 contains=@perlInterpSQ nextgroup=perlSubstitutionSQ keepend extend
syn region perlMatch matchgroup=perlMatchStartEnd start=+\<\%(::\|'\|->\)\@<!s\s*/+ end=+/+me=e-1 contains=@perlInterpSlash nextgroup=perlSubstitutionGQQ keepend extend
@@ -266,38 +274,39 @@ syn region perlMatch matchgroup=perlMatchStartEnd start=+\<\%(::\|'\|->\)\@<!s\s
syn region perlMatch matchgroup=perlMatchStartEnd start=+\<\%(::\|'\|->\)\@<!s\s*<+ end=+>+ contains=@perlInterpMatch,perlAnglesDQ nextgroup=perlSubstitutionGQQ skipwhite skipempty skipnl keepend extend
syn region perlMatch matchgroup=perlMatchStartEnd start=+\<\%(::\|'\|->\)\@<!s\s*\[+ end=+\]+ contains=@perlInterpMatch,perlBracketsDQ nextgroup=perlSubstitutionGQQ skipwhite skipempty skipnl keepend extend
syn region perlMatch matchgroup=perlMatchStartEnd start=+\<\%(::\|'\|->\)\@<!s\s*{+ end=+}+ contains=@perlInterpMatch,perlBracesDQ nextgroup=perlSubstitutionGQQ skipwhite skipempty skipnl keepend extend
-syn region perlSubstitutionGQQ matchgroup=perlMatchStartEnd start=+\z([^[:space:]'([{<]\)+ end=+\z1[msixpodualgcern]*+ keepend contained contains=@perlInterpDQ extend
-syn region perlSubstitutionGQQ matchgroup=perlMatchStartEnd start=+(+ end=+)[msixpodualgcern]*+ contained contains=@perlInterpDQ,perlParensDQ keepend extend
-syn region perlSubstitutionGQQ matchgroup=perlMatchStartEnd start=+\[+ end=+\][msixpodualgcern]*+ contained contains=@perlInterpDQ,perlBracketsDQ keepend extend
-syn region perlSubstitutionGQQ matchgroup=perlMatchStartEnd start=+{+ end=+}[msixpodualgcern]*+ contained contains=@perlInterpDQ,perlBracesDQ keepend extend extend
-syn region perlSubstitutionGQQ matchgroup=perlMatchStartEnd start=+<+ end=+>[msixpodualgcern]*+ contained contains=@perlInterpDQ,perlAnglesDQ keepend extend
-syn region perlSubstitutionSQ matchgroup=perlMatchStartEnd start=+'+ end=+'[msixpodualgcern]*+ contained contains=@perlInterpSQ keepend extend
+syn region perlSubstitutionGQQ matchgroup=perlMatchStartEnd start=+\z([^[:space:]'([{<]\)+ end=+\z1+ keepend contained contains=@perlInterpDQ extend nextgroup=perlSubstitutionModifiers
+syn region perlSubstitutionGQQ matchgroup=perlMatchStartEnd start=+(+ end=+)+ contained contains=@perlInterpDQ,perlParensDQ keepend extend nextgroup=perlSubstitutionModifiers
+syn region perlSubstitutionGQQ matchgroup=perlMatchStartEnd start=+\[+ end=+\]+ contained contains=@perlInterpDQ,perlBracketsDQ keepend extend nextgroup=perlSubstitutionModifiers
+syn region perlSubstitutionGQQ matchgroup=perlMatchStartEnd start=+{+ end=+}+ contained contains=@perlInterpDQ,perlBracesDQ keepend extend extend nextgroup=perlSubstitutionModifiers
+syn region perlSubstitutionGQQ matchgroup=perlMatchStartEnd start=+<+ end=+>+ contained contains=@perlInterpDQ,perlAnglesDQ keepend extend nextgroup=perlSubstitutionModifiers
+syn region perlSubstitutionSQ matchgroup=perlMatchStartEnd start=+'+ end=+'+ contained contains=@perlInterpSQ keepend extend nextgroup=perlSubstitutionModifiers
" Translations
" perlMatch is the first part, perlTranslation* is the second, translator part.
+syn match perlTranslationModifiers "[cdsr]\+" contained
syn region perlMatch matchgroup=perlMatchStartEnd start=+\<\%(::\|'\|->\)\@<!\%(tr\|y\)\>\s*\z([^[:space:]([{<#]\)+ end=+\z1+me=e-1 contains=@perlInterpSQ nextgroup=perlTranslationGQ
syn region perlMatch matchgroup=perlMatchStartEnd start=+\<\%(::\|'\|->\)\@<!\%(tr\|y\)#+ end=+#+me=e-1 contains=@perlInterpSQ nextgroup=perlTranslationGQ
syn region perlMatch matchgroup=perlMatchStartEnd start=+\<\%(::\|'\|->\)\@<!\%(tr\|y\)\s*\[+ end=+\]+ contains=@perlInterpSQ,perlBracketsSQ nextgroup=perlTranslationGQ skipwhite skipempty skipnl
syn region perlMatch matchgroup=perlMatchStartEnd start=+\<\%(::\|'\|->\)\@<!\%(tr\|y\)\s*(+ end=+)+ contains=@perlInterpSQ,perlParensSQ nextgroup=perlTranslationGQ skipwhite skipempty skipnl
syn region perlMatch matchgroup=perlMatchStartEnd start=+\<\%(::\|'\|->\)\@<!\%(tr\|y\)\s*<+ end=+>+ contains=@perlInterpSQ,perlAnglesSQ nextgroup=perlTranslationGQ skipwhite skipempty skipnl
syn region perlMatch matchgroup=perlMatchStartEnd start=+\<\%(::\|'\|->\)\@<!\%(tr\|y\)\s*{+ end=+}+ contains=@perlInterpSQ,perlBracesSQ nextgroup=perlTranslationGQ skipwhite skipempty skipnl
-syn region perlTranslationGQ matchgroup=perlMatchStartEnd start=+\z([^[:space:]([{<]\)+ end=+\z1[cdsr]*+ contained
-syn region perlTranslationGQ matchgroup=perlMatchStartEnd start=+(+ end=+)[cdsr]*+ contains=perlParensSQ contained
-syn region perlTranslationGQ matchgroup=perlMatchStartEnd start=+\[+ end=+\][cdsr]*+ contains=perlBracketsSQ contained
-syn region perlTranslationGQ matchgroup=perlMatchStartEnd start=+{+ end=+}[cdsr]*+ contains=perlBracesSQ contained
-syn region perlTranslationGQ matchgroup=perlMatchStartEnd start=+<+ end=+>[cdsr]*+ contains=perlAnglesSQ contained
+syn region perlTranslationGQ matchgroup=perlMatchStartEnd start=+\z([^[:space:]([{<]\)+ end=+\z1+ contained nextgroup=perlTranslationModifiers
+syn region perlTranslationGQ matchgroup=perlMatchStartEnd start=+(+ end=+)+ contains=perlParensSQ contained nextgroup=perlTranslationModifiers
+syn region perlTranslationGQ matchgroup=perlMatchStartEnd start=+\[+ end=+\]+ contains=perlBracketsSQ contained nextgroup=perlTranslationModifiers
+syn region perlTranslationGQ matchgroup=perlMatchStartEnd start=+{+ end=+}+ contains=perlBracesSQ contained nextgroup=perlTranslationModifiers
+syn region perlTranslationGQ matchgroup=perlMatchStartEnd start=+<+ end=+>+ contains=perlAnglesSQ contained nextgroup=perlTranslationModifiers
" Strings and q, qq, qw and qr expressions
syn region perlStringUnexpanded matchgroup=perlStringStartEnd start="'" end="'" contains=@perlInterpSQ keepend extend
syn region perlString matchgroup=perlStringStartEnd start=+"+ end=+"+ contains=@perlInterpDQ keepend extend
-syn region perlQQ matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!q\>\s*\z([^[:space:]#([{<]\)+ end=+\z1+ contains=@perlInterpSQ keepend extend
-syn region perlQQ matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!q#+ end=+#+ contains=@perlInterpSQ keepend extend
-syn region perlQQ matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!q\s*(+ end=+)+ contains=@perlInterpSQ,perlParensSQ keepend extend
-syn region perlQQ matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!q\s*\[+ end=+\]+ contains=@perlInterpSQ,perlBracketsSQ keepend extend
-syn region perlQQ matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!q\s*{+ end=+}+ contains=@perlInterpSQ,perlBracesSQ keepend extend
-syn region perlQQ matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!q\s*<+ end=+>+ contains=@perlInterpSQ,perlAnglesSQ keepend extend
+syn region perlQ matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!q\>\s*\z([^[:space:]#([{<]\)+ end=+\z1+ contains=@perlInterpSQ keepend extend
+syn region perlQ matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!q#+ end=+#+ contains=@perlInterpSQ keepend extend
+syn region perlQ matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!q\s*(+ end=+)+ contains=@perlInterpSQ,perlParensSQ keepend extend
+syn region perlQ matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!q\s*\[+ end=+\]+ contains=@perlInterpSQ,perlBracketsSQ keepend extend
+syn region perlQ matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!q\s*{+ end=+}+ contains=@perlInterpSQ,perlBracesSQ keepend extend
+syn region perlQ matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!q\s*<+ end=+>+ contains=@perlInterpSQ,perlAnglesSQ keepend extend
syn region perlQQ matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!q[qx]\>\s*\z([^[:space:]#([{<]\)+ end=+\z1+ contains=@perlInterpDQ keepend extend
syn region perlQQ matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!q[qx]#+ end=+#+ contains=@perlInterpDQ keepend extend
@@ -306,48 +315,83 @@ syn region perlQQ matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!q[qx
syn region perlQQ matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!q[qx]\s*{+ end=+}+ contains=@perlInterpDQ,perlBracesDQ keepend extend
syn region perlQQ matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!q[qx]\s*<+ end=+>+ contains=@perlInterpDQ,perlAnglesDQ keepend extend
-syn region perlQQ matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!qw\s*\z([^[:space:]#([{<]\)+ end=+\z1+ contains=@perlInterpSQ keepend extend
-syn region perlQQ matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!qw#+ end=+#+ contains=@perlInterpSQ keepend extend
-syn region perlQQ matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!qw\s*(+ end=+)+ contains=@perlInterpSQ,perlParensSQ keepend extend
-syn region perlQQ matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!qw\s*\[+ end=+\]+ contains=@perlInterpSQ,perlBracketsSQ keepend extend
-syn region perlQQ matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!qw\s*{+ end=+}+ contains=@perlInterpSQ,perlBracesSQ keepend extend
-syn region perlQQ matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!qw\s*<+ end=+>+ contains=@perlInterpSQ,perlAnglesSQ keepend extend
+syn region perlQW matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!qw\s*\z([^[:space:]#([{<]\)+ end=+\z1+ contains=@perlInterpSQ keepend extend
+syn region perlQW matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!qw#+ end=+#+ contains=@perlInterpSQ keepend extend
+syn region perlQW matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!qw\s*(+ end=+)+ contains=@perlInterpSQ,perlParensSQ keepend extend
+syn region perlQW matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!qw\s*\[+ end=+\]+ contains=@perlInterpSQ,perlBracketsSQ keepend extend
+syn region perlQW matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!qw\s*{+ end=+}+ contains=@perlInterpSQ,perlBracesSQ keepend extend
+syn region perlQW matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!qw\s*<+ end=+>+ contains=@perlInterpSQ,perlAnglesSQ keepend extend
-syn region perlQQ matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!qr\>\s*\z([^[:space:]#([{<'/]\)+ end=+\z1[imosxdual]*+ contains=@perlInterpMatch keepend extend
-syn region perlQQ matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!qr\s*/+ end=+/[imosxdual]*+ contains=@perlInterpSlash keepend extend
-syn region perlQQ matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!qr#+ end=+#[imosxdual]*+ contains=@perlInterpMatch keepend extend
-syn region perlQQ matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!qr\s*'+ end=+'[imosxdual]*+ contains=@perlInterpSQ keepend extend
-syn region perlQQ matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!qr\s*(+ end=+)[imosxdual]*+ contains=@perlInterpMatch,perlParensDQ keepend extend
+syn match perlQRModifiers "[msixpadluno]\+" contained
+syn region perlQR matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!qr\>\s*\z([^[:space:]#([{<'/]\)+ end=+\z1+ contains=@perlInterpMatch keepend extend nextgroup=perlQRModifiers
+syn region perlQR matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!qr\s*/+ end=+/+ contains=@perlInterpSlash keepend extend nextgroup=perlQRModifiers
+syn region perlQR matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!qr#+ end=+#+ contains=@perlInterpMatch keepend extend nextgroup=perlQRModifiers
+syn region perlQR matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!qr\s*'+ end=+'+ contains=@perlInterpSQ keepend extend nextgroup=perlQRModifiers
+syn region perlQR matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!qr\s*(+ end=+)+ contains=@perlInterpMatch,perlParensDQ keepend extend nextgroup=perlQRModifiers
" A special case for qr{}, qr<> and qr[] which allows for comments and extra whitespace in the pattern
-syn region perlQQ matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!qr\s*{+ end=+}[imosxdual]*+ contains=@perlInterpMatch,perlBracesDQ,perlComment keepend extend
-syn region perlQQ matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!qr\s*<+ end=+>[imosxdual]*+ contains=@perlInterpMatch,perlAnglesDQ,perlComment keepend extend
-syn region perlQQ matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!qr\s*\[+ end=+\][imosxdual]*+ contains=@perlInterpMatch,perlBracketsDQ,perlComment keepend extend
+syn region perlQR matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!qr\s*{+ end=+}+ contains=@perlInterpMatch,perlBracesDQ,perlComment keepend extend nextgroup=perlQRModifiers
+syn region perlQR matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!qr\s*<+ end=+>+ contains=@perlInterpMatch,perlAnglesDQ,perlComment keepend extend nextgroup=perlQRModifiers
+syn region perlQR matchgroup=perlStringStartEnd start=+\<\%(::\|'\|->\)\@<!qr\s*\[+ end=+\]+ contains=@perlInterpMatch,perlBracketsDQ,perlComment keepend extend nextgroup=perlQRModifiers
" Constructs such as print <<EOF [...] EOF, 'here' documents
"
" XXX Any statements after the identifier are in perlString colour (i.e.
" 'if $a' in 'print <<EOF if $a'). This is almost impossible to get right it
" seems due to the 'auto-extending nature' of regions.
-syn region perlHereDocStart matchgroup=perlStringStartEnd start=+<<\z(\I\i*\)+ end=+$+ contains=@perlTop oneline
-syn region perlHereDocStart matchgroup=perlStringStartEnd start=+<<\s*"\z([^\\"]*\%(\\.[^\\"]*\)*\)"+ end=+$+ contains=@perlTop oneline
-syn region perlHereDocStart matchgroup=perlStringStartEnd start=+<<\s*'\z([^\\']*\%(\\.[^\\']*\)*\)'+ end=+$+ contains=@perlTop oneline
-syn region perlHereDocStart matchgroup=perlStringStartEnd start=+<<\s*""+ end=+$+ contains=@perlTop oneline
-syn region perlHereDocStart matchgroup=perlStringStartEnd start=+<<\s*''+ end=+$+ contains=@perlTop oneline
-if exists("perl_fold")
- syn region perlHereDoc start=+<<\z(\I\i*\)+ matchgroup=perlStringStartEnd end=+^\z1$+ contains=perlHereDocStart,@perlInterpDQ fold extend
+" XXX Indented heredocs are not perfect - they sometimes seem to take a moment
+" to update if switched from double to single quotes and vice versa.
+"
+" Note: bare delimiters such as << to mean <<"" are not supported. These are a
+" fatal error since 5.28 and, apparently, a rarely used feature.
+
+syn region perlHereDocStart matchgroup=perlStringStartEnd start=+<<\~\=\I\i*+ end=+$+ contains=@perlTop oneline
+syn region perlHereDocStart matchgroup=perlStringStartEnd start=+<<\~\=\\\I\i*+ end=+$+ contains=@perlTop oneline
+syn region perlHereDocStart matchgroup=perlStringStartEnd start=+<<\~\=\s*"[^\\"]*\%(\\.[^\\"]*\)*"+ end=+$+ contains=@perlTop oneline
+syn region perlHereDocStart matchgroup=perlStringStartEnd start=+<<\~\=\s*'[^\\']*\%(\\.[^\\']*\)*'+ end=+$+ contains=@perlTop oneline
+syn region perlHereDocStart matchgroup=perlStringStartEnd start=+<<\~\=\s*`[^\\`]*\%(\\.[^\\`]*\)*`+ end=+$+ contains=@perlTop oneline
+syn region perlHereDocStart matchgroup=perlStringStartEnd start=+<<\~\=\s*""+ end=+$+ contains=@perlTop oneline
+syn region perlHereDocStart matchgroup=perlStringStartEnd start=+<<\~\=\s*''+ end=+$+ contains=@perlTop oneline
+syn region perlHereDocStart matchgroup=perlStringStartEnd start=+<<\~\=\s*``+ end=+$+ contains=@perlTop oneline
+
+if get(g:, 'perl_fold', 0)
+ syn region perlHereDoc start=+<<\z(\I\i*\)+ matchgroup=perlStringStartEnd end=+^\z1$+ contains=perlHereDocStart,@perlInterpDQ fold extend
+ syn region perlHereDoc start=+<<\\\z(\I\i*\)+ matchgroup=perlStringStartEnd end=+^\z1$+ contains=perlHereDocStart,@perlInterpSQ fold extend
syn region perlHereDoc start=+<<\s*"\z([^\\"]*\%(\\.[^\\"]*\)*\)"+ matchgroup=perlStringStartEnd end=+^\z1$+ contains=perlHereDocStart,@perlInterpDQ fold extend
syn region perlHereDoc start=+<<\s*'\z([^\\']*\%(\\.[^\\']*\)*\)'+ matchgroup=perlStringStartEnd end=+^\z1$+ contains=perlHereDocStart,@perlInterpSQ fold extend
- syn region perlHereDoc start=+<<\s*""+ matchgroup=perlStringStartEnd end=+^$+ contains=perlHereDocStart,@perlInterpDQ,perlNotEmptyLine fold extend
- syn region perlHereDoc start=+<<\s*''+ matchgroup=perlStringStartEnd end=+^$+ contains=perlHereDocStart,@perlInterpSQ,perlNotEmptyLine fold extend
- syn region perlAutoload matchgroup=perlStringStartEnd start=+<<\s*\(['"]\=\)\z(END_\%(SUB\|OF_FUNC\|OF_AUTOLOAD\)\)\1+ end=+^\z1$+ contains=ALL fold extend
+ syn region perlHereDoc start=+<<\s*`\z([^\\`]*\%(\\.[^\\`]*\)*\)`+ matchgroup=perlStringStartEnd end=+^\z1$+ contains=perlHereDocStart,@perlInterpDQ fold extend
+ syn region perlHereDoc start=+<<\s*""+ matchgroup=perlStringStartEnd end=+^$+ contains=perlHereDocStart,@perlInterpDQ,perlNotEmptyLine fold extend
+ syn region perlHereDoc start=+<<\s*''+ matchgroup=perlStringStartEnd end=+^$+ contains=perlHereDocStart,@perlInterpSQ,perlNotEmptyLine fold extend
+ syn region perlHereDoc start=+<<\s*``+ matchgroup=perlStringStartEnd end=+^$+ contains=perlHereDocStart,@perlInterpDQ,perlNotEmptyLine fold extend
else
- syn region perlHereDoc start=+<<\z(\I\i*\)+ matchgroup=perlStringStartEnd end=+^\z1$+ contains=perlHereDocStart,@perlInterpDQ
+ syn region perlHereDoc start=+<<\z(\I\i*\)+ matchgroup=perlStringStartEnd end=+^\z1$+ contains=perlHereDocStart,@perlInterpDQ
+ syn region perlHereDoc start=+<<\\\z(\I\i*\)+ matchgroup=perlStringStartEnd end=+^\z1$+ contains=perlHereDocStart,@perlInterpSQ
syn region perlHereDoc start=+<<\s*"\z([^\\"]*\%(\\.[^\\"]*\)*\)"+ matchgroup=perlStringStartEnd end=+^\z1$+ contains=perlHereDocStart,@perlInterpDQ
syn region perlHereDoc start=+<<\s*'\z([^\\']*\%(\\.[^\\']*\)*\)'+ matchgroup=perlStringStartEnd end=+^\z1$+ contains=perlHereDocStart,@perlInterpSQ
- syn region perlHereDoc start=+<<\s*""+ matchgroup=perlStringStartEnd end=+^$+ contains=perlHereDocStart,@perlInterpDQ,perlNotEmptyLine
- syn region perlHereDoc start=+<<\s*''+ matchgroup=perlStringStartEnd end=+^$+ contains=perlHereDocStart,@perlInterpSQ,perlNotEmptyLine
- syn region perlAutoload matchgroup=perlStringStartEnd start=+<<\s*\(['"]\=\)\z(END_\%(SUB\|OF_FUNC\|OF_AUTOLOAD\)\)\1+ end=+^\z1$+ contains=ALL
+ syn region perlHereDoc start=+<<\s*`\z([^\\`]*\%(\\.[^\\`]*\)*\)`+ matchgroup=perlStringStartEnd end=+^\z1$+ contains=perlHereDocStart,@perlInterpDQ
+ syn region perlHereDoc start=+<<\s*""+ matchgroup=perlStringStartEnd end=+^$+ contains=perlHereDocStart,@perlInterpDQ,perlNotEmptyLine
+ syn region perlHereDoc start=+<<\s*''+ matchgroup=perlStringStartEnd end=+^$+ contains=perlHereDocStart,@perlInterpSQ,perlNotEmptyLine
+ syn region perlHereDoc start=+<<\s*``+ matchgroup=perlStringStartEnd end=+^$+ contains=perlHereDocStart,@perlInterpDQ,perlNotEmptyLine
+endif
+
+if get(g:, 'perl_fold', 0)
+ syn region perlIndentedHereDoc start=+<<\~\z(\I\i*\)+ matchgroup=perlStringStartEnd end=+^\s*\z1$+ contains=perlIndentedHereDocStart,@perlInterpDQ fold extend
+ syn region perlIndentedHereDoc start=+<<\~\\\z(\I\i*\)+ matchgroup=perlStringStartEnd end=+^\s*\z1$+ contains=perlIndentedHereDocStart,@perlInterpSQ fold extend
+ syn region perlIndentedHereDoc start=+<<\~\s*"\z([^\\"]*\%(\\.[^\\"]*\)*\)"+ matchgroup=perlStringStartEnd end=+^\s*\z1$+ contains=perlIndentedHereDocStart,@perlInterpDQ fold extend
+ syn region perlIndentedHereDoc start=+<<\~\s*'\z([^\\']*\%(\\.[^\\']*\)*\)'+ matchgroup=perlStringStartEnd end=+^\s*\z1$+ contains=perlIndentedHereDocStart,@perlInterpSQ fold extend
+ syn region perlIndentedHereDoc start=+<<\~\s*`\z([^\\`]*\%(\\.[^\\`]*\)*\)`+ matchgroup=perlStringStartEnd end=+^\s*\z1$+ contains=perlIndentedHereDocStart,@perlInterpDQ fold extend
+ syn region perlIndentedHereDoc start=+<<\~\s*""+ matchgroup=perlStringStartEnd end=+^$+ contains=perlIndentedHereDocStart,@perlInterpDQ,perlNotEmptyLine fold extend
+ syn region perlIndentedHereDoc start=+<<\~\s*''+ matchgroup=perlStringStartEnd end=+^$+ contains=perlIndentedHereDocStart,@perlInterpSQ,perlNotEmptyLine fold extend
+ syn region perlIndentedHereDoc start=+<<\~\s*``+ matchgroup=perlStringStartEnd end=+^$+ contains=perlIndentedHereDocStart,@perlInterpDQ,perlNotEmptyLine fold extend
+else
+ syn region perlIndentedHereDoc start=+<<\~\z(\I\i*\)+ matchgroup=perlStringStartEnd end=+^\s*\z1$+ contains=perlHereDocStart,@perlInterpDQ
+ syn region perlIndentedHereDoc start=+<<\~\\\z(\I\i*\)+ matchgroup=perlStringStartEnd end=+^\s*\z1$+ contains=perlHereDocStart,@perlInterpSQ
+ syn region perlIndentedHereDoc start=+<<\~\s*"\z([^\\"]*\%(\\.[^\\"]*\)*\)"+ matchgroup=perlStringStartEnd end=+^\s*\z1$+ contains=perlHereDocStart,@perlInterpDQ
+ syn region perlIndentedHereDoc start=+<<\~\s*'\z([^\\']*\%(\\.[^\\']*\)*\)'+ matchgroup=perlStringStartEnd end=+^\s*\z1$+ contains=perlHereDocStart,@perlInterpSQ
+ syn region perlIndentedHereDoc start=+<<\~\s*`\z([^\\`]*\%(\\.[^\\`]*\)*\)`+ matchgroup=perlStringStartEnd end=+^\s*\z1$+ contains=perlHereDocStart,@perlInterpDQ
+ syn region perlIndentedHereDoc start=+<<\~\s*""+ matchgroup=perlStringStartEnd end=+^$+ contains=perlHereDocStart,@perlInterpDQ,perlNotEmptyLine
+ syn region perlIndentedHereDoc start=+<<\~\s*''+ matchgroup=perlStringStartEnd end=+^$+ contains=perlHereDocStart,@perlInterpSQ,perlNotEmptyLine
+ syn region perlIndentedHereDoc start=+<<\~\s*``+ matchgroup=perlStringStartEnd end=+^$+ contains=perlHereDocStart,@perlInterpDQ,perlNotEmptyLine
endif
@@ -357,24 +401,15 @@ syn match perlPackageDecl "\<package\s\+\%(\h\|::\)\%(\w\|::\)*" contains=per
syn keyword perlStatementPackage package contained
" Functions
-" sub [name] [(prototype)] {
-"
-syn match perlSubError "[^[:space:];{#]" contained
-syn match perlSubAttributesCont "\h\w*\_s*\%(:\_s*\)\=" nextgroup=@perlSubAttrMaybe contained
-syn region perlSubAttributesCont matchgroup=perlSubAttributesCont start="\h\w*(" end=")\_s*\%(:\_s*\)\=" nextgroup=@perlSubAttrMaybe contained contains=@perlInterpSQ,perlParensSQ
-syn cluster perlSubAttrMaybe contains=perlSubAttributesCont,perlSubError,perlFakeGroup
-syn match perlSubAttributes "" contained nextgroup=perlSubError
-syn match perlSubAttributes ":\_s*" contained nextgroup=@perlSubAttrMaybe
if get(g:, "perl_sub_signatures", 0)
- syn match perlSignature +(\_[^)]*)\_s*+ nextgroup=perlSubAttributes,perlComment contained
+ syn match perlSubSignature "\s*([^)]*)" contained extend
else
- syn match perlSubPrototypeError "(\%(\_s*\%(\%(\\\%([$@%&*]\|\[[$@%&*]\+\]\)\|[$&*]\|[@%]\%(\_s*)\)\@=\|;\%(\_s*[)$@%&*\\]\)\@=\|_\%(\_s*[);]\)\@=\)\_s*\)*\)\@>\zs\_[^)]\+" contained
- syn match perlSubPrototype +(\_[^)]*)\_s*+ nextgroup=perlSubAttributes,perlComment contained contains=perlSubPrototypeError
+ syn match perlSubPrototype "\s*([\\$@%&*\[\];]*)" contained extend
endif
-
-syn match perlSubName +\%(\h\|::\|'\w\)\%(\w\|::\|'\w\)*\_s*\|+ contained nextgroup=perlSubPrototype,perlSignature,perlSubAttributes,perlComment
-
-syn match perlFunction +\<sub\>\_s*+ nextgroup=perlSubName
+syn match perlSubAttribute "\s*:\s*\h\w*\%(([^)]*)\|\)" contained extend
+syn match perlSubName "\%(\h\|::\|'\w\)\%(\w\|::\|'\w\)*\s*" contained extend
+syn region perlSubDeclaration start="" end="[;{]" contains=perlSubName,perlSubPrototype,perlSubAttribute,perlSubSignature,perlComment contained transparent
+syn match perlFunction "\<sub\>\_s*" nextgroup=perlSubDeclaration
" The => operator forces a bareword to the left of it to be interpreted as
" a string
@@ -394,39 +429,75 @@ syn match perlFormatField "@[^A-Za-z_|<>~#*]"me=e-1 contained
syn match perlFormatField "@$" contained
" __END__ and __DATA__ clauses
-if exists("perl_fold")
- syntax region perlDATA start="^__DATA__$" skip="." end="." contains=@perlDATA fold
- syntax region perlDATA start="^__END__$" skip="." end="." contains=perlPOD,@perlDATA fold
+
+" Vim excludes empty syn-region end lines from the fold region. This is
+" probably a bug and means a DATA section ending with an empty line does not
+" have that final line included in the fold.
+"
+" As a workaround we exploit an unterminated syntax region here with an end
+" pattern that will (probably) never match. This forces all lines to be
+" included in the fold region. Of course, if it does match then there's
+" nothing to work around as it is a non-empty line.
+"
+" This problem also exists with empty string delimited heredocs but there's no
+" known workaround for that case.
+if get(g:, 'perl_fold', 0)
+ syntax region perlDATA matchgroup=perlDATAStart start="^__DATA__$" end="VIM_PERL_EOF\%$" contains=@perlDATA fold
+ syntax region perlEND matchgroup=perlENDStart start="^__END__$" end="VIM_PERL_EOF\%$" contains=@perlDATA fold
else
- syntax region perlDATA start="^__DATA__$" skip="." end="." contains=@perlDATA
- syntax region perlDATA start="^__END__$" skip="." end="." contains=perlPOD,@perlDATA
+ syntax region perlDATA matchgroup=perlDATAStart start="^__DATA__$" end="\%$" contains=@perlDATA
+ syntax region perlEND matchgroup=perlENDStart start="^__END__$" end="\%$" contains=@perlDATA
+endif
+
+" TODO: generalise this to allow other filetypes
+if get(g:, 'perl_highlight_data', 0)
+ syn cluster perlDATA add=perlPOD
+else
+ syn cluster perlDATA remove=perlPOD
endif
"
" Folding
-
-if exists("perl_fold")
+if get(g:, 'perl_fold', 0)
" Note: this bit must come before the actual highlighting of the "package"
" keyword, otherwise this will screw up Pod lines that match /^package/
- if !exists("perl_nofold_packages")
+ if !get(g:, 'perl_nofold_packages', 0)
syn region perlPackageFold start="^package \S\+;\s*\%(#.*\)\=$" end="^1;\=\s*\%(#.*\)\=$" end="\n\+package"me=s-1 transparent fold keepend
+ syn region perlPackageFold start="^\z(\s*\)package\s*\S\+\s*{" end="^\z1}" transparent fold keepend
endif
- if !exists("perl_nofold_subs")
+ if !get(g:, 'perl_nofold_subs', 0)
if get(g:, "perl_fold_anonymous_subs", 0)
- syn region perlSubFold start="\<sub\>[^{]*{" end="}" transparent fold keepend extend
- syn region perlSubFold start="\<\%(BEGIN\|END\|CHECK\|INIT\)\>\s*{" end="}" transparent fold keepend
+ " EXPLANATION:
+ " \<sub\> - "sub" keyword
+ " \_[^;{]* - any characters, including new line, but not ";" or "{", zero or more times
+ " \%(([\\$@%&*\[\];]*)\)\= - prototype definition, \$@%&*[]; characters between (), zero or 1 times
+ " \_[^;]* - any characters, including new line, but not ";" or "{", zero or more times
+ " { - start subroutine block
+ syn region perlSubFold start="\<sub\>\_[^;{]*\%(([\\$@%&*\[\];]*)\)\=\_[^;{]*{" end="}" transparent fold keepend extend
else
- syn region perlSubFold start="^\z(\s*\)\<sub\>.*[^};]$" end="^\z1}\s*\%(#.*\)\=$" transparent fold keepend
- syn region perlSubFold start="^\z(\s*\)\<\%(BEGIN\|END\|CHECK\|INIT\|UNITCHECK\)\>.*[^};]$" end="^\z1}\s*$" transparent fold keepend
+ " EXPLANATION:
+ " same, as above, but first non-space character after "sub" keyword must
+ " be [A-Za-z_]
+ syn region perlSubFold start="\<sub\>\s*\h\_[^;{]*\%(([\\$@%&*\[\];]*)\)\=\_[^;]*{" end="}" transparent fold keepend extend
endif
+
+ syn region perlSubFold start="\<\%(BEGIN\|END\|CHECK\|INIT\|UNITCHECK\)\>\_s*{" end="}" transparent fold keepend
endif
- if exists("perl_fold_blocks")
+ if get(g:, 'perl_fold_blocks', 0)
syn region perlBlockFold start="^\z(\s*\)\%(if\|elsif\|unless\|for\|while\|until\|given\)\s*(.*)\%(\s*{\)\=\s*\%(#.*\)\=$" start="^\z(\s*\)for\%(each\)\=\s*\%(\%(my\|our\)\=\s*\S\+\s*\)\=(.*)\%(\s*{\)\=\s*\%(#.*\)\=$" end="^\z1}\s*;\=\%(#.*\)\=$" transparent fold keepend
+
+ " TODO this is works incorrectly
syn region perlBlockFold start="^\z(\s*\)\%(do\|else\)\%(\s*{\)\=\s*\%(#.*\)\=$" end="^\z1}\s*while" end="^\z1}\s*;\=\%(#.*\)\=$" transparent fold keepend
+ else
+ if get(g:, 'perl_fold_do_blocks', 0)
+ syn region perlDoBlockDeclaration start="" end="{" contains=perlComment contained transparent
+ syn match perlOperator "\<do\>\_s*" nextgroup=perlDoBlockDeclaration
+
+ syn region perlDoBlockFold start="\<do\>\_[^{]*{" end="}" transparent fold keepend extend
+ endif
endif
- setlocal foldmethod=syntax
syn sync fromstart
else
" fromstart above seems to set minlines even if perl_fold is not set.
@@ -438,29 +509,28 @@ endif
" The default highlighting.
hi def link perlSharpBang PreProc
-hi def link perlControl PreProc
-hi def link perlInclude Include
-hi def link perlSpecial Special
-hi def link perlString String
+hi def link perlControl PreProc
+hi def link perlInclude Include
+hi def link perlSpecial Special
+hi def link perlString String
hi def link perlCharacter Character
-hi def link perlNumber Number
-hi def link perlFloat Float
+hi def link perlNumber Number
+hi def link perlFloat Float
hi def link perlType Type
hi def link perlIdentifier Identifier
-hi def link perlLabel Label
+hi def link perlLabel Label
hi def link perlStatement Statement
hi def link perlConditional Conditional
-hi def link perlRepeat Repeat
+hi def link perlRepeat Repeat
hi def link perlOperator Operator
hi def link perlFunction Keyword
-hi def link perlSubName Function
+hi def link perlSubName Function
hi def link perlSubPrototype Type
-hi def link perlSignature Type
-hi def link perlSubAttributes PreProc
-hi def link perlSubAttributesCont perlSubAttributes
-hi def link perlComment Comment
+hi def link perlSubSignature Type
+hi def link perlSubAttribute PreProc
+hi def link perlComment Comment
hi def link perlTodo Todo
-if exists("perl_string_as_statement")
+if get(g:, 'perl_string_as_statement', 0)
hi def link perlStringStartEnd perlStatement
else
hi def link perlStringStartEnd perlString
@@ -470,21 +540,29 @@ hi def link perlList perlStatement
hi def link perlMisc perlStatement
hi def link perlVarPlain perlIdentifier
hi def link perlVarPlain2 perlIdentifier
-hi def link perlArrow perlIdentifier
+hi def link perlArrow perlIdentifier
hi def link perlFiledescRead perlIdentifier
hi def link perlFiledescStatement perlIdentifier
-hi def link perlVarSimpleMember perlIdentifier
-hi def link perlVarSimpleMemberName perlString
-hi def link perlVarNotInMatches perlIdentifier
+hi def link perlVarSimpleMember perlIdentifier
+hi def link perlVarSimpleMemberName perlString
+hi def link perlVarNotInMatches perlIdentifier
hi def link perlVarSlash perlIdentifier
+hi def link perlQ perlString
hi def link perlQQ perlString
-hi def link perlHereDoc perlString
+hi def link perlQW perlString
+hi def link perlQR perlString
+hi def link perlMatchModifiers perlMatchStartEnd
+hi def link perlSubstitutionModifiers perlMatchStartEnd
+hi def link perlTranslationModifiers perlMatchStartEnd
+hi def link perlQRModifiers perlStringStartEnd
+hi def link perlHereDoc perlString
+hi def link perlIndentedHereDoc perlString
hi def link perlStringUnexpanded perlString
-hi def link perlSubstitutionSQ perlString
-hi def link perlSubstitutionGQQ perlString
-hi def link perlTranslationGQ perlString
-hi def link perlMatch perlString
-hi def link perlMatchStartEnd perlStatement
+hi def link perlSubstitutionSQ perlString
+hi def link perlSubstitutionGQQ perlString
+hi def link perlTranslationGQ perlString
+hi def link perlMatch perlString
+hi def link perlMatchStartEnd perlStatement
hi def link perlFormatName perlIdentifier
hi def link perlFormatField perlString
hi def link perlPackageDecl perlType
@@ -493,44 +571,48 @@ hi def link perlPackageRef perlType
hi def link perlStatementPackage perlStatement
hi def link perlStatementStorage perlStatement
hi def link perlStatementControl perlStatement
-hi def link perlStatementScalar perlStatement
-hi def link perlStatementRegexp perlStatement
+hi def link perlStatementScalar perlStatement
+hi def link perlStatementRegexp perlStatement
hi def link perlStatementNumeric perlStatement
-hi def link perlStatementList perlStatement
-hi def link perlStatementHash perlStatement
-hi def link perlStatementIOfunc perlStatement
+hi def link perlStatementList perlStatement
+hi def link perlStatementHash perlStatement
+hi def link perlStatementIOfunc perlStatement
hi def link perlStatementFiledesc perlStatement
-hi def link perlStatementVector perlStatement
-hi def link perlStatementFiles perlStatement
-hi def link perlStatementFlow perlStatement
+hi def link perlStatementVector perlStatement
+hi def link perlStatementFiles perlStatement
+hi def link perlStatementFlow perlStatement
hi def link perlStatementInclude perlStatement
-hi def link perlStatementProc perlStatement
-hi def link perlStatementSocket perlStatement
+hi def link perlStatementProc perlStatement
+hi def link perlStatementSocket perlStatement
hi def link perlStatementIPC perlStatement
hi def link perlStatementNetwork perlStatement
-hi def link perlStatementPword perlStatement
-hi def link perlStatementTime perlStatement
-hi def link perlStatementMisc perlStatement
+hi def link perlStatementPword perlStatement
+hi def link perlStatementTime perlStatement
+hi def link perlStatementMisc perlStatement
hi def link perlStatementIndirObj perlStatement
hi def link perlFunctionName perlIdentifier
-hi def link perlMethod perlIdentifier
+hi def link perlMethod perlIdentifier
hi def link perlPostDeref perlIdentifier
hi def link perlFunctionPRef perlType
+
if !get(g:, 'perl_include_pod', 1)
hi def link perlPOD perlComment
endif
hi def link perlShellCommand perlString
hi def link perlSpecialAscii perlSpecial
-hi def link perlSpecialDollar perlSpecial
-hi def link perlSpecialString perlSpecial
-hi def link perlSpecialStringU perlSpecial
+hi def link perlSpecialDollar perlSpecial
+hi def link perlSpecialString perlSpecial
+hi def link perlSpecialStringU perlSpecial
hi def link perlSpecialMatch perlSpecial
+hi def link perlEND perlComment
+hi def link perlENDStart perlEND
hi def link perlDATA perlComment
+hi def link perlDATAStart perlDATA
" NOTE: Due to a bug in Vim (or more likely, a misunderstanding on my part),
-" I had to remove the transparent property from the following regions
-" in order to get them to highlight correctly. Feel free to remove
-" these and reinstate the transparent property if you know how.
+" I had to remove the transparent property from the following regions
+" in order to get them to highlight correctly. Feel free to remove
+" these and reinstate the transparent property if you know how.
hi def link perlParensSQ perlString
hi def link perlBracketsSQ perlString
hi def link perlBracesSQ perlString
@@ -546,23 +628,21 @@ hi def link perlSpecialStringU2 perlString
" Possible errors
hi def link perlNotEmptyLine Error
hi def link perlElseIfError Error
-hi def link perlSubPrototypeError Error
-hi def link perlSubError Error
" Syncing to speed up processing
"
-if !exists("perl_no_sync_on_sub")
+if !get(g:, 'perl_no_sync_on_sub', 0)
syn sync match perlSync grouphere NONE "^\s*\<package\s"
syn sync match perlSync grouphere NONE "^\s*\<sub\>"
syn sync match perlSync grouphere NONE "^}"
endif
-if !exists("perl_no_sync_on_global_var")
+if !get(g:, 'perl_no_sync_on_global_var', 0)
syn sync match perlSync grouphere NONE "^$\I[[:alnum:]_:]+\s*=\s*{"
syn sync match perlSync grouphere NONE "^[@%]\I[[:alnum:]_:]+\s*=\s*("
endif
-if exists("perl_sync_dist")
+if get(g:, 'perl_sync_dist', 0)
execute "syn sync maxlines=" . perl_sync_dist
else
syn sync maxlines=100
diff --git a/runtime/syntax/plsql.vim b/runtime/syntax/plsql.vim
index 1868c9cb16..7b36c0a180 100644
--- a/runtime/syntax/plsql.vim
+++ b/runtime/syntax/plsql.vim
@@ -1,19 +1,43 @@
" Vim syntax file
-" Language: Oracle Procedureal SQL (PL/SQL)
-" Maintainer: Jeff Lanzarotta (jefflanzarotta at yahoo dot com)
-" Original Maintainer: C. Laurence Gonsalves (clgonsal@kami.com)
-" URL: http://lanzarotta.tripod.com/vim/syntax/plsql.vim.zip
-" Last Change: September 18, 2002
-" History: Geoff Evans & Bill Pribyl (bill at plnet dot org)
-" Added 9i keywords.
-" Austin Ziegler (austin at halostatue dot ca)
-" Added 8i+ features.
+" Language: Oracle Procedural SQL (PL/SQL)
+" Maintainer: Lee Lindley (lee dot lindley at gmail dot com)
+" Previous Maintainer: Jeff Lanzarotta (jefflanzarotta at yahoo dot com)
+" Previous Maintainer: C. Laurence Gonsalves (clgonsal@kami.com)
+" URL: https://github.com/lee-lindley/vim_plsql_syntax
+" Last Change: April 28, 2022
+" History Lee Lindley (lee dot lindley at gmail dot com)
+" updated to 19c keywords. refined quoting.
+" separated reserved, non-reserved keywords and functions
+" revised folding, giving up on procedure folding due to issue
+" with multiple ways to enter <begin>.
+" Eugene Lysyonok (lysyonok at inbox ru)
+" Added folding.
+" Geoff Evans & Bill Pribyl (bill at plnet dot org)
+" Added 9i keywords.
+" Austin Ziegler (austin at halostatue dot ca)
+" Added 8i+ features.
"
-" quit when a syntax file was already loaded
+" To put SQL keywords as Function group in legacy style
+" let plsql_legacy_sql_keywords = 1
+"
+" To enable folding (It does setlocal foldmethod=syntax)
+" let plsql_fold = 1
+"
+" From my vimrc file -- turn syntax and syntax folding on,
+" associate file suffixes as plsql, open all the folds on file open
+" let plsql_fold = 1
+" au BufNewFile,BufRead *.sql,*.pls,*.tps,*.tpb,*.pks,*.pkb,*.pkg,*.trg set filetype=plsql
+" au BufNewFile,BufRead *.sql,*.pls,*.tps,*.tpb,*.pks,*.pkb,*.pkg,*.trg syntax on
+" au Syntax plsql normal zR
+
if exists("b:current_syntax")
finish
endif
+" this file uses line continuations
+let s:cpo_sav = &cpo
+set cpo&vim
+
" Todo.
syn keyword plsqlTodo TODO FIXME XXX DEBUG NOTE
syn cluster plsqlCommentGroup contains=plsqlTodo
@@ -25,110 +49,512 @@ syn match plsqlIdentifier "[a-z][a-z0-9$_#]*"
syn match plsqlHostIdentifier ":[a-z][a-z0-9$_#]*"
" When wanted, highlight the trailing whitespace.
-if exists("c_space_errors")
- if !exists("c_no_trail_space_error")
+if exists("plsql_space_errors")
+ if !exists("plsql_no_trail_space_error")
syn match plsqlSpaceError "\s\+$"
endif
- if !exists("c_no_tab_space_error")
+ if !exists("plsql_no_tab_space_error")
syn match plsqlSpaceError " \+\t"me=e-1
endif
endif
" Symbols.
-syn match plsqlSymbol "\(;\|,\|\.\)"
-
-" Operators.
-syn match plsqlOperator "\(+\|-\|\*\|/\|=\|<\|>\|@\|\*\*\|!=\|\~=\)"
-syn match plsqlOperator "\(^=\|<=\|>=\|:=\|=>\|\.\.\|||\|<<\|>>\|\"\)"
-
-" Some of Oracle's SQL keywords.
-syn keyword plsqlSQLKeyword ABORT ACCESS ACCESSED ADD AFTER ALL ALTER AND ANY
-syn keyword plsqlSQLKeyword AS ASC ATTRIBUTE AUDIT AUTHORIZATION AVG BASE_TABLE
-syn keyword plsqlSQLKeyword BEFORE BETWEEN BY CASCADE CAST CHECK CLUSTER
-syn keyword plsqlSQLKeyword CLUSTERS COLAUTH COLUMN COMMENT COMPRESS CONNECT
-syn keyword plsqlSQLKeyword CONSTRAINT CRASH CREATE CURRENT DATA DATABASE
-syn keyword plsqlSQLKeyword DATA_BASE DBA DEFAULT DELAY DELETE DESC DISTINCT
-syn keyword plsqlSQLKeyword DROP DUAL ELSE EXCLUSIVE EXISTS EXTENDS EXTRACT
-syn keyword plsqlSQLKeyword FILE FORCE FOREIGN FROM GRANT GROUP HAVING HEAP
-syn keyword plsqlSQLKeyword IDENTIFIED IDENTIFIER IMMEDIATE IN INCLUDING
-syn keyword plsqlSQLKeyword INCREMENT INDEX INDEXES INITIAL INSERT INSTEAD
-syn keyword plsqlSQLKeyword INTERSECT INTO INVALIDATE IS ISOLATION KEY LIBRARY
-syn keyword plsqlSQLKeyword LIKE LOCK MAXEXTENTS MINUS MODE MODIFY MULTISET
-syn keyword plsqlSQLKeyword NESTED NOAUDIT NOCOMPRESS NOT NOWAIT OF OFF OFFLINE
-syn keyword plsqlSQLKeyword ON ONLINE OPERATOR OPTION OR ORDER ORGANIZATION
-syn keyword plsqlSQLKeyword PCTFREE PRIMARY PRIOR PRIVATE PRIVILEGES PUBLIC
-syn keyword plsqlSQLKeyword QUOTA RELEASE RENAME REPLACE RESOURCE REVOKE ROLLBACK
-syn keyword plsqlSQLKeyword ROW ROWLABEL ROWS SCHEMA SELECT SEPARATE SESSION SET
-syn keyword plsqlSQLKeyword SHARE SIZE SPACE START STORE SUCCESSFUL SYNONYM
-syn keyword plsqlSQLKeyword SYSDATE TABLE TABLES TABLESPACE TEMPORARY TO TREAT
-syn keyword plsqlSQLKeyword TRIGGER TRUNCATE UID UNION UNIQUE UNLIMITED UPDATE
-syn keyword plsqlSQLKeyword USE USER VALIDATE VALUES VIEW WHENEVER WHERE WITH
-
-" PL/SQL's own keywords.
-syn keyword plsqlKeyword AGENT AND ANY ARRAY ASSIGN AS AT AUTHID BEGIN BODY BY
-syn keyword plsqlKeyword BULK C CASE CHAR_BASE CHARSETFORM CHARSETID CLOSE
-syn keyword plsqlKeyword COLLECT CONSTANT CONSTRUCTOR CONTEXT CURRVAL DECLARE
-syn keyword plsqlKeyword DVOID EXCEPTION EXCEPTION_INIT EXECUTE EXIT FETCH
-syn keyword plsqlKeyword FINAL FUNCTION GOTO HASH IMMEDIATE IN INDICATOR
-syn keyword plsqlKeyword INSTANTIABLE IS JAVA LANGUAGE LIBRARY MAP MAXLEN
-syn keyword plsqlKeyword MEMBER NAME NEW NOCOPY NUMBER_BASE OBJECT OCICOLL
-syn keyword plsqlKeyword OCIDATE OCIDATETIME OCILOBLOCATOR OCINUMBER OCIRAW
-syn keyword plsqlKeyword OCISTRING OF OPAQUE OPEN OR ORDER OTHERS OUT
-syn keyword plsqlKeyword OVERRIDING PACKAGE PARALLEL_ENABLE PARAMETERS
-syn keyword plsqlKeyword PARTITION PIPELINED PRAGMA PROCEDURE RAISE RANGE REF
-syn keyword plsqlKeyword RESULT RETURN REVERSE ROWTYPE SB1 SELF SHORT SIZE_T
-syn keyword plsqlKeyword SQL SQLCODE SQLERRM STATIC STRUCT SUBTYPE TDO THEN
-syn keyword plsqlKeyword TABLE TIMEZONE_ABBR TIMEZONE_HOUR TIMEZONE_MINUTE
-syn keyword plsqlKeyword TIMEZONE_REGION TYPE UNDER UNSIGNED USING VARIANCE
-syn keyword plsqlKeyword VARRAY VARYING WHEN WRITE
-syn match plsqlKeyword "\<END\>"
-syn match plsqlKeyword "\.COUNT\>"hs=s+1
-syn match plsqlKeyword "\.EXISTS\>"hs=s+1
-syn match plsqlKeyword "\.FIRST\>"hs=s+1
-syn match plsqlKeyword "\.LAST\>"hs=s+1
-syn match plsqlKeyword "\.DELETE\>"hs=s+1
-syn match plsqlKeyword "\.PREV\>"hs=s+1
-syn match plsqlKeyword "\.NEXT\>"hs=s+1
-
-" PL/SQL functions.
-syn keyword plsqlFunction ABS ACOS ADD_MONTHS ASCII ASCIISTR ASIN ATAN ATAN2
-syn keyword plsqlFunction BFILENAME BITAND CEIL CHARTOROWID CHR COALESCE
-syn keyword plsqlFunction COMMIT COMMIT_CM COMPOSE CONCAT CONVERT COS COSH
-syn keyword plsqlFunction COUNT CUBE CURRENT_DATE CURRENT_TIME CURRENT_TIMESTAMP
-syn keyword plsqlFunction DBTIMEZONE DECODE DECOMPOSE DEREF DUMP EMPTY_BLOB
-syn keyword plsqlFunction EMPTY_CLOB EXISTS EXP FLOOR FROM_TZ GETBND GLB
-syn keyword plsqlFunction GREATEST GREATEST_LB GROUPING HEXTORAW INITCAP
-syn keyword plsqlFunction INSTR INSTR2 INSTR4 INSTRB INSTRC ISNCHAR LAST_DAY
-syn keyword plsqlFunction LEAST LEAST_UB LENGTH LENGTH2 LENGTH4 LENGTHB LENGTHC
-syn keyword plsqlFunction LN LOCALTIME LOCALTIMESTAMP LOG LOWER LPAD
-syn keyword plsqlFunction LTRIM LUB MAKE_REF MAX MIN MOD MONTHS_BETWEEN
-syn keyword plsqlFunction NCHARTOROWID NCHR NEW_TIME NEXT_DAY NHEXTORAW
-syn keyword plsqlFunction NLS_CHARSET_DECL_LEN NLS_CHARSET_ID NLS_CHARSET_NAME
-syn keyword plsqlFunction NLS_INITCAP NLS_LOWER NLSSORT NLS_UPPER NULLFN NULLIF
-syn keyword plsqlFunction NUMTODSINTERVAL NUMTOYMINTERVAL NVL POWER
-syn keyword plsqlFunction RAISE_APPLICATION_ERROR RAWTOHEX RAWTONHEX REF
-syn keyword plsqlFunction REFTOHEX REPLACE ROLLBACK_NR ROLLBACK_SV ROLLUP ROUND
-syn keyword plsqlFunction ROWIDTOCHAR ROWIDTONCHAR ROWLABEL RPAD RTRIM
-syn keyword plsqlFunction SAVEPOINT SESSIONTIMEZONE SETBND SET_TRANSACTION_USE
-syn keyword plsqlFunction SIGN SIN SINH SOUNDEX SQLCODE SQLERRM SQRT STDDEV
-syn keyword plsqlFunction SUBSTR SUBSTR2 SUBSTR4 SUBSTRB SUBSTRC SUM
-syn keyword plsqlFunction SYS_AT_TIME_ZONE SYS_CONTEXT SYSDATE SYS_EXTRACT_UTC
-syn keyword plsqlFunction SYS_GUID SYS_LITERALTODATE SYS_LITERALTODSINTERVAL
-syn keyword plsqlFunction SYS_LITERALTOTIME SYS_LITERALTOTIMESTAMP
-syn keyword plsqlFunction SYS_LITERALTOTZTIME SYS_LITERALTOTZTIMESTAMP
-syn keyword plsqlFunction SYS_LITERALTOYMINTERVAL SYS_OVER__DD SYS_OVER__DI
-syn keyword plsqlFunction SYS_OVER__ID SYS_OVER_IID SYS_OVER_IIT
-syn keyword plsqlFunction SYS_OVER__IT SYS_OVER__TI SYS_OVER__TT
-syn keyword plsqlFunction SYSTIMESTAMP TAN TANH TO_ANYLOB TO_BLOB TO_CHAR
-syn keyword plsqlFunction TO_CLOB TO_DATE TO_DSINTERVAL TO_LABEL TO_MULTI_BYTE
-syn keyword plsqlFunction TO_NCHAR TO_NCLOB TO_NUMBER TO_RAW TO_SINGLE_BYTE
-syn keyword plsqlFunction TO_TIME TO_TIMESTAMP TO_TIMESTAMP_TZ TO_TIME_TZ
-syn keyword plsqlFunction TO_YMINTERVAL TRANSLATE TREAT TRIM TRUNC TZ_OFFSET UID
-syn keyword plsqlFunction UNISTR UPPER UROWID USER USERENV VALUE VARIANCE
-syn keyword plsqlFunction VSIZE WORK XOR
+syn match plsqlSymbol "[;,.()]"
+
+" Operators. and words that would be something else if not in operator mode
+syn match plsqlOperator "[-+*/=<>@"]"
+syn match plsqlOperator "\(\^=\|<=\|>=\|:=\|=>\|\.\.\|||\|<<\|>>\|\*\*\|!=\|\~=\)"
+syn match plsqlOperator "\<\(NOT\|AND\|OR\|LIKE\|BETWEEN\|IN\)\>"
+syn match plsqlBooleanLiteral "\<NULL\>"
+syn match plsqlOperator "\<IS\\_s\+\(NOT\_s\+\)\?NULL\>"
+"
+" conditional compilation Preprocessor directives and sqlplus define sigil
+syn match plsqlPseudo "$[$a-z][a-z0-9$_#]*"
+syn match plsqlPseudo "&"
+
+syn match plsqlReserved "\<\(CREATE\|THEN\|UPDATE\|INSERT\|SET\)\>"
+syn match plsqlKeyword "\<\(REPLACE\|PACKAGE\|FUNCTION\|PROCEDURE\|TYPE|BODY\|WHEN\|MATCHED\)\>"
+syn region plsqlUpdate
+ \ matchgroup=plsqlReserved
+ \ start="\<UPDATE\>"
+ \ end="\<SET\>"
+ \ contains=@plsqlIdentifiers
+syn match plsqlReserved "\<WHEN\_s\+\(NOT\_s\+\)\?MATCHED\_s\+THEN\_s\+\(UPDATE\|INSERT\)\(\_s\+SET\)\?"
+
+"
+" Oracle's non-reserved keywords
+syn keyword plsqlKeyword ABORT ABS ABSENT ACCESS ACCESSED ACCOUNT ACL ACOS ACROSS ACTION ACTIONS
+syn keyword plsqlKeyword ACTIVATE ACTIVE ACTIVE_COMPONENT ACTIVE_DATA ACTIVE_FUNCTION ACTIVE_TAG ACTIVITY
+syn keyword plsqlKeyword ADAPTIVE_PLAN ADD ADD_COLUMN ADD_GROUP ADD_MONTHS ADG_REDIRECT_DML ADG_REDIRECT_PLSQL
+syn keyword plsqlKeyword ADJ_DATE ADMIN ADMINISTER ADMINISTRATOR ADVANCED ADVISE ADVISOR AFD_DISKSTRING
+syn keyword plsqlKeyword AFFINITY AFTER AGGREGATE AGGREGATES ALGORITHM ALIAS ALLOCATE ALLOW ALL_ROWS
+syn keyword plsqlKeyword ALTERNATE ALWAYS ANALYTIC ANALYTIC_VIEW_SQL ANALYZE ANCESTOR ANCILLARY AND_EQUAL
+syn keyword plsqlKeyword ANOMALY ANSI_REARCH ANSWER_QUERY_USING_STATS ANTIJOIN ANYSCHEMA ANY_VALUE
+syn keyword plsqlKeyword APPEND APPENDCHILDXML APPEND_VALUES APPLICATION APPLY APPROX_COUNT APPROX_COUNT_DISTINCT
+syn keyword plsqlKeyword APPROX_COUNT_DISTINCT_AGG APPROX_COUNT_DISTINCT_DETAIL APPROX_MEDIAN APPROX_PERCENTILE
+syn keyword plsqlKeyword APPROX_PERCENTILE_AGG APPROX_PERCENTILE_DETAIL APPROX_RANK APPROX_SUM ARCHIVAL
+syn keyword plsqlKeyword ARCHIVE ARCHIVED ARCHIVELOG ARRAY ARRAYS ASCII ASCIISTR ASIN ASIS ASSEMBLY
+syn keyword plsqlKeyword ASSIGN ASSOCIATE ASYNC ASYNCHRONOUS AS_JSON AT ATAN ATAN2 ATTRIBUTE ATTRIBUTES
+syn keyword plsqlKeyword AUDIT AUTHENTICATED AUTHENTICATION AUTHID AUTHORIZATION AUTO AUTOALLOCATE
+syn keyword plsqlKeyword AUTOEXTEND AUTOMATIC AUTO_LOGIN AUTO_REOPTIMIZE AVAILABILITY AVCACHE_OP AVERAGE_RANK
+syn keyword plsqlKeyword AVG AVMDX_OP AVRO AV_AGGREGATE AV_CACHE AW BACKGROUND BACKINGFILE BACKUP BAND_JOIN
+syn keyword plsqlKeyword BASIC BASICFILE BATCH BATCHSIZE BATCH_TABLE_ACCESS_BY_ROWID BECOME BEFORE
+syn keyword plsqlKeyword BEGINNING BEGIN_OUTLINE_DATA BEHALF BEQUEATH BFILENAME BIGFILE BINARY
+syn keyword plsqlKeyword BINARY_DOUBLE_INFINITY BINARY_DOUBLE_NAN BINARY_FLOAT_INFINITY BINARY_FLOAT_NAN
+syn keyword plsqlKeyword BINDING BIND_AWARE BIN_TO_NUM BITAND BITMAP BITMAPS BITMAP_AND BITMAP_BIT_POSITION
+syn keyword plsqlKeyword BITMAP_BUCKET_NUMBER BITMAP_CONSTRUCT_AGG BITMAP_COUNT BITMAP_OR_AGG BITMAP_TREE
+syn keyword plsqlKeyword BITOR BITS BITXOR BIT_AND_AGG BIT_OR_AGG BIT_XOR_AGG BLOCK BLOCKCHAIN BLOCKING
+syn keyword plsqlKeyword BLOCKS BLOCKSIZE BLOCK_RANGE BOOL BOOTSTRAP BOTH BOUND BRANCH BREADTH
+syn keyword plsqlKeyword BROADCAST BSON BUFFER BUFFER_CACHE BUFFER_POOL BUILD BULK BUSHY_JOIN BYPASS_RECURSIVE_CHECK
+syn keyword plsqlKeyword BYPASS_UJVC CACHE CACHE_CB CACHE_INSTANCES CACHE_TEMP_TABLE CACHING CALCULATED
+syn keyword plsqlKeyword CALL CALLBACK CANCEL CAPACITY CAPTION CAPTURE CARDINALITY CASCADE CAST
+syn keyword plsqlKeyword CATALOG_DBLINK CATEGORY CDB$DEFAULT CDB_HTTP_HANDLER CEIL CELLMEMORY CELL_FLASH_CACHE
+syn keyword plsqlKeyword CERTIFICATE CFILE CHAINED CHANGE CHANGE_DUPKEY_ERROR_INDEX CHARTOROWID CHAR_CS
+syn keyword plsqlKeyword CHECKPOINT CHECKSUM CHECK_ACL_REWRITE CHILD CHOOSE CHR CHUNK CLASS CLASSIFICATION
+syn keyword plsqlKeyword CLASSIFIER CLAUSE CLEAN CLEANUP CLEAR CLIENT CLONE CLOSE CLOSEST CLOSE_CACHED_OPEN_CURSORS
+syn keyword plsqlKeyword CLOUD_IDENTITY CLUSTERING CLUSTERING_FACTOR CLUSTERS CLUSTER_BY_ROWID CLUSTER_DETAILS
+syn keyword plsqlKeyword CLUSTER_DISTANCE CLUSTER_ID CLUSTER_PROBABILITY CLUSTER_SET COALESCE COALESCE_SQ
+syn keyword plsqlKeyword COARSE COLAUTH COLD COLLATE COLLATION COLLECT COLUMN COLUMNAR COLUMNS COLUMN_AUTHORIZATION_INDICATOR
+syn keyword plsqlKeyword COLUMN_AUTH_INDICATOR COLUMN_STATS COLUMN_VALUE COMMENT COMMIT COMMITTED COMMON
+syn keyword plsqlKeyword COMMON_DATA_MAP COMPACT COMPATIBILITY COMPILE COMPLETE COMPLIANCE COMPONENT
+syn keyword plsqlKeyword COMPONENTS COMPOSE COMPOSITE COMPOSITE_LIMIT COMPOUND COMPUTATION COMPUTE
+syn keyword plsqlKeyword CONCAT CONDITION CONDITIONAL CONFIRM CONFORMING CONNECT_BY_CB_WHR_ONLY CONNECT_BY_COMBINE_SW
+syn keyword plsqlKeyword CONNECT_BY_COST_BASED CONNECT_BY_ELIM_DUPS CONNECT_BY_FILTERING CONNECT_BY_ISCYCLE
+syn keyword plsqlKeyword CONNECT_BY_ISLEAF CONNECT_BY_ROOT CONNECT_TIME CONSENSUS CONSIDER CONSISTENT
+syn keyword plsqlKeyword CONST CONSTANT CONSTRAINT CONSTRAINTS CONTAINER CONTAINERS CONTAINERS_DEFAULT
+syn keyword plsqlKeyword CONTAINER_DATA CONTAINER_DATA_ADMIT_NULL CONTAINER_MAP CONTAINER_MAP_OBJECT
+syn keyword plsqlKeyword CONTENT CONTENTS CONTEXT CONTINUE CONTROLFILE CONVERSION CONVERT CON_DBID_TO_ID
+syn keyword plsqlKeyword CON_GUID_TO_ID CON_ID CON_ID_FILTER CON_ID_TO_CON_NAME CON_ID_TO_DBID CON_ID_TO_GUID
+syn keyword plsqlKeyword CON_ID_TO_UID CON_NAME_TO_ID CON_UID_TO_ID COOKIE COPY CORR CORRUPTION CORRUPT_XID
+syn keyword plsqlKeyword CORRUPT_XID_ALL CORR_K CORR_S COS COSH COST COST_XML_QUERY_REWRITE COVAR_POP
+syn match plsqlKeyword "\<COUNT\>"
+syn keyword plsqlKeyword COVAR_SAMP CO_AUTH_IND CPU_COSTING CPU_COUNT CPU_PER_CALL CPU_PER_SESSION
+syn keyword plsqlKeyword CPU_TIME CRASH CREATE_FILE_DEST CREATE_STORED_OUTLINES CREATION CREDENTIAL
+syn keyword plsqlKeyword CREDENTIALS CRITICAL CROSS CROSSEDITION CSCONVERT CUBE CUBE_AJ CUBE_GB CUBE_SJ
+syn keyword plsqlKeyword CUME_DIST CUME_DISTM CURRENT CURRENTV CURRENT_DATE CURRENT_INSTANCE CURRENT_PARTSET_KEY
+syn keyword plsqlKeyword CURRENT_SCHEMA CURRENT_SHARD_KEY CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER
+syn keyword plsqlKeyword CURSOR CURSOR_SHARING_EXACT CURSOR_SPECIFIC_SEGMENT CV CYCLE DAGG_OPTIM_GSETS
+syn keyword plsqlKeyword DANGLING DATA DATABASE DATABASES DATAFILE DATAFILES DATAMOVEMENT DATAOBJNO
+syn keyword plsqlKeyword DATAOBJ_TO_MAT_PARTITION DATAOBJ_TO_PARTITION DATAPUMP DATASTORE DATA_LINK_DML
+syn keyword plsqlKeyword DATA_SECURITY_REWRITE_LIMIT DATA_VALIDATE DATE_MODE DAYS DBA DBA_RECYCLEBIN
+syn keyword plsqlKeyword DBMS_STATS DBSTR2UTF8 DBTIMEZONE DB_ROLE_CHANGE DB_UNIQUE_NAME DB_VERSION
+syn keyword plsqlKeyword DDL DEALLOCATE DEBUG DEBUGGER DECLARE DECODE DECOMPOSE DECOMPRESS DECORRELATE
+syn keyword plsqlKeyword DECR DECREMENT DECRYPT DEDUPLICATE DEFAULTS DEFAULT_COLLATION DEFAULT_PDB_HINT
+syn keyword plsqlKeyword DEFERRABLE DEFERRED DEFINE DEFINED DEFINER DEFINITION DEGREE DELAY DELEGATE
+syn keyword plsqlKeyword DELETEXML DELETE_ALL DEMAND DENORM_AV DENSE_RANK DENSE_RANKM DEPENDENT DEPTH
+syn keyword plsqlKeyword DEQUEUE DEREF DEREF_NO_REWRITE DESCENDANT DESCRIPTION DESTROY DETACHED DETERMINED
+syn keyword plsqlKeyword DETERMINES DETERMINISTIC DG_GATHER_STATS DIAGNOSTICS DICTIONARY DIGEST DIMENSION
+syn keyword plsqlKeyword DIMENSIONS DIRECT DIRECTORY DIRECT_LOAD DIRECT_PATH DISABLE DISABLE_ALL DISABLE_CONTAINERS_DEFAULT
+syn keyword plsqlKeyword DISABLE_CONTAINER_MAP DISABLE_PARALLEL_DML DISABLE_PRESET DISABLE_RPKE DISALLOW
+syn keyword plsqlKeyword DISASSOCIATE DISCARD DISCONNECT DISK DISKGROUP DISKS DISMOUNT DISTINGUISHED
+syn keyword plsqlKeyword DISTRIBUTE DISTRIBUTED DIST_AGG_PROLLUP_PUSHDOWN DML DML_UPDATE DOCFIDELITY
+syn keyword plsqlKeyword DOCUMENT DOMAIN_INDEX_FILTER DOMAIN_INDEX_NO_SORT DOMAIN_INDEX_SORT DOWNGRADE
+syn keyword plsqlKeyword DRAIN_TIMEOUT DRIVING_SITE DROP_COLUMN DROP_GROUP DST_UPGRADE_INSERT_CONV
+syn keyword plsqlKeyword DUMP DUPLICATE DUPLICATED DV DYNAMIC DYNAMIC_SAMPLING DYNAMIC_SAMPLING_EST_CDN
+syn keyword plsqlKeyword EACH EDITION EDITIONABLE EDITIONING EDITIONS ELAPSED_TIME ELEMENT ELIMINATE_JOIN
+syn keyword plsqlKeyword ELIMINATE_OBY ELIMINATE_OUTER_JOIN ELIMINATE_SQ ELIM_GROUPBY EM EMPTY EMPTY_BLOB
+syn keyword plsqlKeyword EMPTY_CLOB ENABLE ENABLE_ALL ENABLE_PARALLEL_DML ENABLE_PRESET ENCODE ENCODING
+syn keyword plsqlKeyword ENCRYPT ENCRYPTION
+syn keyword plsqlKeyword END_OUTLINE_DATA ENFORCE ENFORCED ENQUEUE ENTERPRISE
+syn keyword plsqlKeyword ENTITYESCAPING ENTRY EQUIPART EQUIVALENT ERROR ERRORS ERROR_ARGUMENT ERROR_ON_OVERLAP_TIME
+syn keyword plsqlKeyword ESCAPE ESTIMATE EVAL EVALNAME EVALUATE EVALUATION EVEN EVENTS EVERY EXCEPTION
+syn keyword plsqlKeyword EXCEPTIONS EXCHANGE EXCLUDE EXCLUDING EXECUTE EXEMPT EXISTING EXISTSNODE EXP
+syn keyword plsqlKeyword EXPAND EXPAND_GSET_TO_UNION EXPAND_TABLE EXPIRE EXPLAIN EXPLOSION EXPORT EXPRESS
+syn keyword plsqlKeyword EXPR_CORR_CHECK EXTEND EXTENDED EXTENDS EXTENT EXTENTS EXTERNAL EXTERNALLY
+syn keyword plsqlKeyword EXTRA EXTRACT EXTRACTCLOBXML EXTRACTVALUE FACILITY FACT FACTOR FACTORIZE_JOIN
+syn keyword plsqlKeyword FAILED FAILED_LOGIN_ATTEMPTS FAILGROUP FAILOVER FAILURE FAMILY FAR FAST
+syn keyword plsqlKeyword FBTSCAN FEATURE FEATURE_COMPARE FEATURE_DETAILS FEATURE_ID FEATURE_SET FEATURE_VALUE
+syn keyword plsqlKeyword FEDERATION FETCH FILE FILEGROUP FILESTORE FILESYSTEM_LIKE_LOGGING FILE_NAME_CONVERT
+syn keyword plsqlKeyword FILTER FINAL FINE FINISH FIRSTM FIRST_ROWS FIRST_VALUE FIXED_VIEW_DATA
+syn match plsqlKeyword "\<FIRST\>"
+syn keyword plsqlKeyword FLAGGER FLASHBACK FLASH_CACHE FLEX FLOB FLOOR FLUSH FOLDER FOLLOWING FOLLOWS
+syn keyword plsqlKeyword FORCE FORCE_JSON_TABLE_TRANSFORM FORCE_SPATIAL FORCE_XML_QUERY_REWRITE FOREIGN
+syn keyword plsqlKeyword FOREVER FORMAT FORWARD FRAGMENT_NUMBER FREE FREELIST FREELISTS FREEPOOLS FRESH
+syn keyword plsqlKeyword FRESH_MV FROM_TZ FTP FULL FULL_OUTER_JOIN_TO_OUTER FUNCTIONS GATHER_OPTIMIZER_STATISTICS
+syn keyword plsqlKeyword GATHER_PLAN_STATISTICS GBY_CONC_ROLLUP GBY_PUSHDOWN GENERATED GET GLOBAL GLOBALLY
+syn keyword plsqlKeyword GLOBAL_NAME GLOBAL_TOPIC_ENABLED GOLDENGATE GOTO GRANTED GRANULAR GREATEST
+syn keyword plsqlKeyword GROUPING GROUPING_ID GROUPS GROUP_BY GROUP_ID GUARANTEE GUARANTEED GUARD H
+syn keyword plsqlKeyword HALF_YEARS HASH HASHING HASHKEYS HASHSET_BUILD HASH_AJ HASH_SJ HEADER HEAP
+syn keyword plsqlKeyword HELP HEXTORAW HEXTOREF HIDDEN HIDE HIERARCHICAL HIERARCHIES HIERARCHY HIER_ANCESTOR
+syn keyword plsqlKeyword HIER_CAPTION HIER_CHILDREN HIER_CHILD_COUNT HIER_COLUMN HIER_CONDITION HIER_DEPTH
+syn keyword plsqlKeyword HIER_DESCRIPTION HIER_HAS_CHILDREN HIER_LAG HIER_LEAD HIER_LEVEL HIER_MEMBER_NAME
+syn keyword plsqlKeyword HIER_MEMBER_UNIQUE_NAME HIER_ORDER HIER_PARENT HIER_WINDOW HIGH HINTSET_BEGIN
+syn keyword plsqlKeyword HINTSET_END HOST HOT HOUR HOURS HTTP HWM_BROKERED HYBRID ID IDENTIFIER IDENTITY
+syn keyword plsqlKeyword IDGENERATORS IDLE IDLE_TIME IGNORE IGNORE_OPTIM_EMBEDDED_HINTS IGNORE_ROW_ON_DUPKEY_INDEX
+syn keyword plsqlKeyword IGNORE_WHERE_CLAUSE ILM IMMEDIATE IMMUTABLE IMPACT IMPORT INACTIVE INACTIVE_ACCOUNT_TIME
+syn keyword plsqlKeyword INCLUDE INCLUDES INCLUDE_VERSION INCLUDING INCOMING INCR INCREMENT INCREMENTAL
+syn keyword plsqlKeyword INDENT INDEXED INDEXES INDEXING INDEXTYPE INDEXTYPES INDEX_ASC INDEX_COMBINE
+syn keyword plsqlKeyword INDEX_DESC INDEX_FFS INDEX_FILTER INDEX_JOIN INDEX_ROWS INDEX_RRS INDEX_RS
+syn keyword plsqlKeyword INDEX_RS_ASC INDEX_RS_DESC INDEX_SCAN INDEX_SKIP_SCAN INDEX_SS INDEX_SS_ASC
+syn keyword plsqlKeyword INDEX_SS_DESC INDEX_STATS INDICATOR INFINITE INFORMATIONAL INHERIT INITCAP
+syn keyword plsqlKeyword INITIAL INITIALIZED INITIALLY INITRANS INLINE INLINE_XMLTYPE_NT INLINE_XT
+syn keyword plsqlKeyword INMEMORY INMEMORY_PRUNING INNER INPLACE INSENSITIVE INSERTCHILDXML INSERTCHILDXMLAFTER
+syn keyword plsqlKeyword INSERTCHILDXMLBEFORE INSERTXMLAFTER INSERTXMLBEFORE INSTALL INSTANCE INSTANCES
+syn keyword plsqlKeyword INSTANTIABLE INSTANTLY INSTEAD INSTR INSTR2 INSTR4 INSTRB INSTRC INTERLEAVED
+syn keyword plsqlKeyword INTERMEDIATE INTERNAL_CONVERT INTERNAL_USE INTERPRETED INTRA_CDB INVALIDATE
+syn keyword plsqlKeyword INVALIDATION INVISIBLE IN_MEMORY_METADATA IN_XQUERY IOSEEKTIM IOTFRSPEED IO_LOGICAL
+syn keyword plsqlKeyword IO_MEGABYTES IO_REQUESTS ISOLATE ISOLATION ISOLATION_LEVEL ITERATE ITERATION_NUMBER
+syn keyword plsqlKeyword JAVA JOB JOIN JSON JSONGET JSONPARSE JSONTOXML JSON_ARRAY JSON_ARRAYAGG JSON_EQUAL
+syn keyword plsqlKeyword JSON_EQUAL2 JSON_EXISTS JSON_EXISTS2 JSON_HASH JSON_LENGTH JSON_MERGEPATCH
+syn keyword plsqlKeyword JSON_MKMVI JSON_OBJECT JSON_OBJECTAGG JSON_PATCH JSON_QUERY JSON_SCALAR JSON_SERIALIZE
+syn keyword plsqlKeyword JSON_TABLE JSON_TEXTCONTAINS JSON_TEXTCONTAINS2 JSON_TRANSFORM JSON_VALUE
+syn keyword plsqlKeyword KEEP KEEP_DUPLICATES KERBEROS KEY KEYS KEYSIZE KEYSTORE KEY_LENGTH KILL
+syn keyword plsqlKeyword KURTOSIS_POP KURTOSIS_SAMP LABEL LAG LAG_DIFF LAG_DIFF_PERCENT LANGUAGE
+syn match plsqlKeyword "\<LAST\>"
+syn keyword plsqlKeyword LAST_DAY LAST_VALUE LATERAL LAX LAYER LDAP_REGISTRATION LDAP_REGISTRATION_ENABLED
+syn keyword plsqlKeyword LDAP_REG_SYNC_INTERVAL LEAD LEADING LEAD_CDB LEAD_CDB_URI LEAD_DIFF LEAD_DIFF_PERCENT
+syn keyword plsqlKeyword LEAF LEAST LEAVES LEDGER LEFT LENGTH LENGTH2 LENGTH4 LENGTHB LENGTHC LESS
+syn keyword plsqlKeyword LEVEL LEVELS LIBRARY LIFE LIFECYCLE LIFETIME LIKE2 LIKE4 LIKEC LIMIT LINEAR
+syn keyword plsqlKeyword LINK LIST LISTAGG LN LNNVL LOAD LOB LOBNVL LOBS LOB_VALUE LOCALTIME LOCALTIMESTAMP
+syn keyword plsqlKeyword LOCAL_INDEXES LOCATION LOCATOR LOCKDOWN LOCKED LOCKING LOG LOGFILE LOGFILES
+syn keyword plsqlKeyword LOGGING LOGICAL LOGICAL_READS_PER_CALL LOGICAL_READS_PER_SESSION LOGMINING
+syn keyword plsqlKeyword LOGOFF LOGON LOG_READ_ONLY_VIOLATIONS LOST LOW LOWER LPAD LTRIM MAIN MAKE_REF
+syn keyword plsqlKeyword MANAGE MANAGED MANAGEMENT MANAGER MANDATORY MANUAL MAP MAPPER MAPPING MASTER
+syn keyword plsqlKeyword MATCH MATCHES MATCH_NUMBER MATCH_RECOGNIZE MATERIALIZE MATERIALIZED
+syn keyword plsqlKeyword MATRIX MAX MAXARCHLOGS MAXDATAFILES MAXEXTENTS MAXIMIZE MAXINSTANCES MAXLOGFILES
+syn keyword plsqlKeyword MAXLOGHISTORY MAXLOGMEMBERS MAXSIZE MAXTRANS MAXVALUE MAX_AUDIT_SIZE MAX_DIAG_SIZE
+syn keyword plsqlKeyword MAX_PDB_SNAPSHOTS MAX_SHARED_TEMP_SIZE MBRC MEASURE MEASURES MEDIAN MEDIUM
+syn keyword plsqlKeyword MEMBER MEMCOMPRESS MEMOPTIMIZE MEMOPTIMIZE_WRITE MEMORY MERGE MERGE$ACTIONS
+syn keyword plsqlKeyword MERGE_AJ MERGE_CONST_ON MERGE_SJ METADATA METADATA_SOURCE_PDB METHOD MIGRATE
+syn keyword plsqlKeyword MIGRATE_CROSS_CON MIGRATION MIN MINEXTENTS MINIMIZE MINIMUM MINING MINUS_NULL
+syn keyword plsqlKeyword MINUTE MINUTES MINVALUE MIRROR MIRRORCOLD MIRRORHOT MISMATCH MISSING MLE MLSLABEL
+syn keyword plsqlKeyword MOD MODEL MODEL_COMPILE_SUBQUERY MODEL_DONTVERIFY_UNIQUENESS MODEL_DYNAMIC_SUBQUERY
+syn keyword plsqlKeyword MODEL_MIN_ANALYSIS MODEL_NB MODEL_NO_ANALYSIS MODEL_PBY MODEL_PUSH_REF MODEL_SV
+syn keyword plsqlKeyword MODIFICATION MODIFY MODIFY_COLUMN_TYPE MODULE MONITOR MONITORING MONTHS MONTHS_BETWEEN
+syn keyword plsqlKeyword MOUNT MOUNTPATH MOUNTPOINT MOVE MOVEMENT MULTIDIMENSIONAL MULTISET MULTIVALUE
+syn keyword plsqlKeyword MV_MERGE NAME NAMED NAMES NAMESPACE NAN NANVL NATIVE NATIVE_FULL_OUTER_JOIN
+syn keyword plsqlKeyword NATURAL NAV NCHAR_CS NCHR NEEDED NEG NESTED NESTED_ROLLUP_TOP NESTED_TABLE_FAST_INSERT
+syn keyword plsqlKeyword NESTED_TABLE_GET_REFS NESTED_TABLE_ID NESTED_TABLE_SET_REFS NESTED_TABLE_SET_SETID
+syn keyword plsqlKeyword NETWORK NEVER NEW NEW_TIME NEXT_DAY NLJ_BATCHING NLJ_INDEX_FILTER NLJ_INDEX_SCAN
+syn match plsqlKeyword "\<NEXT\>"
+syn keyword plsqlKeyword NLJ_PREFETCH NLSSORT NLS_CALENDAR NLS_CHARACTERSET NLS_CHARSET_DECL_LEN NLS_CHARSET_ID
+syn keyword plsqlKeyword NLS_CHARSET_NAME NLS_COLLATION_ID NLS_COLLATION_NAME NLS_COMP NLS_CURRENCY
+syn keyword plsqlKeyword NLS_DATE_FORMAT NLS_DATE_LANGUAGE NLS_INITCAP NLS_ISO_CURRENCY NLS_LANG NLS_LANGUAGE
+syn keyword plsqlKeyword NLS_LENGTH_SEMANTICS NLS_LOWER NLS_NCHAR_CONV_EXCP NLS_NUMERIC_CHARACTERS
+syn keyword plsqlKeyword NLS_SORT NLS_SPECIAL_CHARS NLS_TERRITORY NLS_UPPER NL_AJ NL_SJ NO NOAPPEND
+syn keyword plsqlKeyword NOARCHIVELOG NOAUDIT NOCACHE NOCOPY NOCPU_COSTING NOCYCLE NODELAY NOENTITYESCAPING
+syn keyword plsqlKeyword NOEXTEND NOFORCE NOGUARANTEE NOKEEP NOLOCAL NOLOGGING NOMAPPING NOMAXVALUE
+syn keyword plsqlKeyword NOMINIMIZE NOMINVALUE NOMONITORING NONBLOCKING NONE NONEDITIONABLE NONPARTITIONED
+syn keyword plsqlKeyword NONSCHEMA NOORDER NOOVERRIDE NOPARALLEL NOPARALLEL_INDEX NORELOCATE NORELY
+syn keyword plsqlKeyword NOREPAIR NOREPLAY NORESETLOGS NOREVERSE NOREWRITE NORMAL NOROWDEPENDENCIES
+syn keyword plsqlKeyword NOSCALE NOSCHEMACHECK NOSEGMENT NOSHARD NOSORT NOSTRICT NOSWITCH NOTHING NOTIFICATION
+syn keyword plsqlKeyword NOVALIDATE NOW NO_ACCESS NO_ADAPTIVE_PLAN NO_ANSI_REARCH NO_ANSWER_QUERY_USING_STATS
+syn keyword plsqlKeyword NO_AUTO_REOPTIMIZE NO_BAND_JOIN NO_BASETABLE_MULTIMV_REWRITE NO_BATCH_TABLE_ACCESS_BY_ROWID
+syn keyword plsqlKeyword NO_BIND_AWARE NO_BUFFER NO_BUSHY_JOIN NO_CARTESIAN NO_CHECK_ACL_REWRITE NO_CLUSTERING
+syn keyword plsqlKeyword NO_CLUSTER_BY_ROWID NO_COALESCE_SQ NO_COMMON_DATA NO_CONNECT_BY_CB_WHR_ONLY
+syn keyword plsqlKeyword NO_CONNECT_BY_COMBINE_SW NO_CONNECT_BY_COST_BASED NO_CONNECT_BY_ELIM_DUPS
+syn keyword plsqlKeyword NO_CONNECT_BY_FILTERING NO_CONTAINERS NO_COST_XML_QUERY_REWRITE NO_CPU_COSTING
+syn keyword plsqlKeyword NO_CROSS_CONTAINER NO_DAGG_OPTIM_GSETS NO_DATA_SECURITY_REWRITE NO_DECORRELATE
+syn keyword plsqlKeyword NO_DIST_AGG_PROLLUP_PUSHDOWN NO_DOMAIN_INDEX_FILTER NO_DST_UPGRADE_INSERT_CONV
+syn keyword plsqlKeyword NO_ELIMINATE_JOIN NO_ELIMINATE_OBY NO_ELIMINATE_OUTER_JOIN NO_ELIMINATE_SQ
+syn keyword plsqlKeyword NO_ELIM_GROUPBY NO_EXPAND NO_EXPAND_GSET_TO_UNION NO_EXPAND_TABLE NO_FACT
+syn keyword plsqlKeyword NO_FACTORIZE_JOIN NO_FILTERING NO_FULL_OUTER_JOIN_TO_OUTER NO_GATHER_OPTIMIZER_STATISTICS
+syn keyword plsqlKeyword NO_GBY_PUSHDOWN NO_INDEX NO_INDEX_FFS NO_INDEX_SS NO_INMEMORY NO_INMEMORY_PRUNING
+syn keyword plsqlKeyword NO_JSON_TABLE_TRANSFORM NO_LOAD NO_MERGE NO_MODEL_PUSH_REF NO_MONITOR NO_MONITORING
+syn keyword plsqlKeyword NO_MULTIMV_REWRITE NO_NATIVE_FULL_OUTER_JOIN NO_NLJ_BATCHING NO_NLJ_PREFETCH
+syn keyword plsqlKeyword NO_OBJECT_LINK NO_OBY_GBYPD_SEPARATE NO_ORDER_ROLLUPS NO_OR_EXPAND NO_OUTER_JOIN_TO_ANTI
+syn keyword plsqlKeyword NO_OUTER_JOIN_TO_INNER NO_PARALLEL NO_PARALLEL_INDEX NO_PARTIAL_COMMIT NO_PARTIAL_JOIN
+syn keyword plsqlKeyword NO_PARTIAL_OSON_UPDATE NO_PARTIAL_ROLLUP_PUSHDOWN NO_PLACE_DISTINCT NO_PLACE_GROUP_BY
+syn keyword plsqlKeyword NO_PQ_CONCURRENT_UNION NO_PQ_EXPAND_TABLE NO_PQ_MAP NO_PQ_NONLEAF_SKEW NO_PQ_REPLICATE
+syn keyword plsqlKeyword NO_PQ_SKEW NO_PRUNE_GSETS NO_PULL_PRED NO_PUSH_HAVING_TO_GBY NO_PUSH_PRED
+syn keyword plsqlKeyword NO_PUSH_SUBQ NO_PX_FAULT_TOLERANCE NO_PX_JOIN_FILTER NO_QKN_BUFF NO_QUERY_TRANSFORMATION
+syn keyword plsqlKeyword NO_REF_CASCADE NO_REORDER_WIF NO_RESULT_CACHE NO_REWRITE NO_ROOT_SW_FOR_LOCAL
+syn keyword plsqlKeyword NO_SEMIJOIN NO_SEMI_TO_INNER NO_SET_GBY_PUSHDOWN NO_SET_TO_JOIN NO_SQL_TRANSLATION
+syn keyword plsqlKeyword NO_SQL_TUNE NO_STAR_TRANSFORMATION NO_STATEMENT_QUEUING NO_STATS_GSETS NO_SUBQUERY_PRUNING
+syn keyword plsqlKeyword NO_SUBSTRB_PAD NO_SWAP_JOIN_INPUTS NO_TABLE_LOOKUP_BY_NL NO_TEMP_TABLE NO_TRANSFORM_DISTINCT_AGG
+syn keyword plsqlKeyword NO_UNNEST NO_USE_CUBE NO_USE_DAGG_UNION_ALL_GSETS NO_USE_HASH NO_USE_HASH_AGGREGATION
+syn keyword plsqlKeyword NO_USE_HASH_GBY_FOR_DAGGPSHD NO_USE_HASH_GBY_FOR_PUSHDOWN NO_USE_INVISIBLE_INDEXES
+syn keyword plsqlKeyword NO_USE_MERGE NO_USE_NL NO_USE_PARTITION_WISE_DISTINCT NO_USE_PARTITION_WISE_GBY
+syn keyword plsqlKeyword NO_USE_PARTITION_WISE_WIF NO_USE_SCALABLE_GBY_INVDIST NO_USE_VECTOR_AGGREGATION
+syn keyword plsqlKeyword NO_VECTOR_TRANSFORM NO_VECTOR_TRANSFORM_DIMS NO_VECTOR_TRANSFORM_FACT NO_XDB_FASTPATH_INSERT
+syn keyword plsqlKeyword NO_XMLINDEX_REWRITE NO_XMLINDEX_REWRITE_IN_SELECT NO_XML_DML_REWRITE NO_XML_QUERY_REWRITE
+syn keyword plsqlKeyword NO_ZONEMAP NTH_VALUE NTILE NULLIF NULLS NUMTODSINTERVAL NUMTOYMINTERVAL NUM_INDEX_KEYS
+syn keyword plsqlKeyword NVL NVL2 OBJECT OBJECT2XML OBJNO OBJNO_REUSE OBJ_ID OBY_GBYPD_SEPARATE OCCURENCES
+syn keyword plsqlKeyword OCCURRENCES ODD OFF OFFLINE OFFSET OID OIDINDEX OLAP OLD OLD_PUSH_PRED OLS
+syn keyword plsqlKeyword OLTP OMIT ONE ONLINE ONLY OPAQUE OPAQUE_TRANSFORM OPAQUE_XCANONICAL OPCODE
+syn keyword plsqlKeyword OPEN OPERATIONS OPERATOR OPTIMAL OPTIMIZE OPTIMIZER_FEATURES_ENABLE OPTIMIZER_GOAL
+syn keyword plsqlKeyword OPT_ESTIMATE OPT_PARAM ORADEBUG ORA_BRANCH ORA_CHECK_ACL ORA_CHECK_PRIVILEGE
+syn keyword plsqlKeyword ORA_CHECK_SYS_PRIVILEGE ORA_CLUSTERING ORA_CONCAT_RWKEY ORA_DM_PARTITION_NAME
+syn keyword plsqlKeyword ORA_DST_AFFECTED ORA_DST_CONVERT ORA_DST_ERROR ORA_GET_ACLIDS ORA_GET_PRIVILEGES
+syn keyword plsqlKeyword ORA_HASH ORA_INVOKING_USER ORA_INVOKING_USERID ORA_INVOKING_XS_USER ORA_INVOKING_XS_USER_GUID
+syn keyword plsqlKeyword ORA_NORMALIZE ORA_PARTITION_VALIDATION ORA_RAWCOMPARE ORA_RAWCONCAT ORA_ROWSCN
+syn keyword plsqlKeyword ORA_ROWSCN_RAW ORA_ROWVERSION ORA_SEARCH_RWKEY ORA_SHARDSPACE_NAME ORA_SHARD_ID
+syn keyword plsqlKeyword ORA_TABVERSION ORA_WRITE_TIME ORDERED ORDERED_PREDICATES ORDER_KEY_VECTOR_USE
+syn keyword plsqlKeyword ORDER_SUBQ ORDINALITY ORGANIZATION OR_EXPAND OR_PREDICATES OSON OSON_DIAG
+syn keyword plsqlKeyword OSON_GET_CONTENT OTHER OTHERS OUTER OUTER_JOIN_TO_ANTI OUTER_JOIN_TO_INNER
+syn keyword plsqlKeyword OUTLINE OUTLINE_LEAF OUT_OF_LINE OVER OVERFLOW OVERFLOW_NOMOVE OVERLAPS OWN
+syn keyword plsqlKeyword OWNER OWNERSHIP PACKAGES PARALLEL PARALLEL_INDEX PARAM PARAMETERS
+syn keyword plsqlKeyword PARENT PARITY PART$NUM$INST PARTIAL PARTIALLY PARTIAL_JOIN PARTIAL_ROLLUP_PUSHDOWN
+syn keyword plsqlKeyword PARTITION PARTITIONING PARTITIONS PARTITIONSET PARTITION_CONTAINED PARTITION_HASH
+syn keyword plsqlKeyword PARTITION_LIST PARTITION_RANGE PASSING PASSIVE PASSWORD PASSWORDFILE_METADATA_CACHE
+syn keyword plsqlKeyword PASSWORD_GRACE_TIME PASSWORD_LIFE_TIME PASSWORD_LOCK_TIME PASSWORD_REUSE_MAX
+syn keyword plsqlKeyword PASSWORD_REUSE_TIME PASSWORD_ROLLOVER_TIME PASSWORD_VERIFY_FUNCTION PAST PATCH
+syn keyword plsqlKeyword PATH PATHS PATH_PREFIX PATTERN PBL_HS_BEGIN PBL_HS_END PCTINCREASE PCTTHRESHOLD
+syn keyword plsqlKeyword PCTUSED PCTVERSION PDB_LOCAL_ONLY PEER PEERS PENDING PER PERCENT PERCENTAGE
+syn keyword plsqlKeyword PERCENTILE_CONT PERCENTILE_DISC PERCENT_RANK PERCENT_RANKM PERFORMANCE PERIOD
+syn keyword plsqlKeyword PERMANENT PERMISSION PERMUTE PERSISTABLE PFILE PHV PHYSICAL PIKEY PIVOT PIV_GB
+syn keyword plsqlKeyword PIV_SSF PLACE_DISTINCT PLACE_GROUP_BY PLAN PLSCOPE_SETTINGS PLSQL_CCFLAGS
+syn keyword plsqlKeyword PLSQL_CODE_TYPE PLSQL_DEBUG PLSQL_OPTIMIZE_LEVEL PLSQL_WARNINGS PLUGGABLE
+syn keyword plsqlKeyword PMEM POINT POLICY POOL_16K POOL_2K POOL_32K POOL_4K POOL_8K PORT POSITION
+syn keyword plsqlKeyword POST_TRANSACTION POWER POWERMULTISET POWERMULTISET_BY_CARDINALITY PQ_CONCURRENT_UNION
+syn keyword plsqlKeyword PQ_DISTRIBUTE PQ_DISTRIBUTE_WINDOW PQ_EXPAND_TABLE PQ_FILTER PQ_MAP PQ_NOMAP
+syn keyword plsqlKeyword PQ_NONLEAF_SKEW PQ_REPLICATE PQ_SKEW PREBUILT PRECEDES PRECEDING PRECOMPUTE_SUBQUERY
+syn keyword plsqlKeyword PREDICATE_REORDERS PREDICTION PREDICTION_BOUNDS PREDICTION_COST PREDICTION_DETAILS
+syn keyword plsqlKeyword PREDICTION_PROBABILITY PREDICTION_SET PRELOAD PREPARE PRESENT PRESENTNNV PRESENTV
+syn keyword plsqlKeyword PRESERVE PRESERVE_OID PRETTY PREVIOUS PRIMARY PRINTBLOBTOCLOB PRIORITY
+syn match plsqlKeyword "\<PREV\>"
+syn keyword plsqlKeyword PRIVATE PRIVATE_SGA PRIVILEGE PRIVILEGED PRIVILEGES PROCEDURAL PROCESS
+syn keyword plsqlKeyword PROFILE PROGRAM PROJECT PROPAGATE PROPAGATION PROPERTY PROTECTED PROTECTION
+syn keyword plsqlKeyword PROTOCOL PROXY PRUNING PULL_PRED PURGE PUSH_HAVING_TO_GBY PUSH_PRED PUSH_SUBQ
+syn keyword plsqlKeyword PX_FAULT_TOLERANCE PX_GRANULE PX_JOIN_FILTER QB_NAME QUALIFY QUARANTINE QUARTERS
+syn keyword plsqlKeyword QUERY QUERY_BLOCK QUEUE QUEUE_CURR QUEUE_ROWP QUIESCE QUORUM QUOTA QUOTAGROUP
+syn keyword plsqlKeyword QUOTES RANDOM RANDOM_LOCAL RANGE RANK RANKM RAPIDLY RATIO_TO_REPORT RAWTOHEX
+syn keyword plsqlKeyword RAWTONHEX RAWTOREF RBA RBO_OUTLINE RDBA READ READS READ_OR_WRITE REALM REBALANCE
+syn keyword plsqlKeyword REBUILD RECONNECT RECORDS_PER_BLOCK RECOVER RECOVERABLE RECOVERY RECYCLE RECYCLEBIN
+syn keyword plsqlKeyword REDACTION REDEFINE REDO REDUCED REDUNDANCY REFERENCE REFERENCED REFERENCES
+syn keyword plsqlKeyword REFERENCING REFRESH REFTOHEX REFTORAW REF_CASCADE_CURSOR REGEXP_COUNT REGEXP_INSTR
+syn keyword plsqlKeyword REGEXP_LIKE REGEXP_REPLACE REGEXP_SUBSTR REGISTER REGR_AVGX REGR_AVGY REGR_COUNT
+syn keyword plsqlKeyword REGR_INTERCEPT REGR_R2 REGR_SLOPE REGR_SXX REGR_SXY REGR_SYY REGULAR REJECT
+syn keyword plsqlKeyword REKEY RELATIONAL RELOCATE RELY REMAINDER REMOTE REMOTE_MAPPED REMOVE REORDER_WIF
+syn keyword plsqlKeyword REPAIR REPEAT REPLICATION REQUIRED RESERVOIR_SAMPLING RESET RESETLOGS
+syn keyword plsqlKeyword RESIZE RESOLVE RESOLVER RESPECT RESTART RESTORE RESTORE_AS_INTERVALS RESTRICT
+syn keyword plsqlKeyword RESTRICTED RESTRICT_ALL_REF_CONS RESULT_CACHE RESUMABLE RESUME RETENTION RETRY_ON_ROW_CHANGE
+syn keyword plsqlKeyword RETURN RETURNING REUSE REVERSE REWRITE REWRITE_OR_ERROR RIGHT RLS_FORCE ROLE
+syn keyword plsqlKeyword ROLES ROLESET ROLLBACK ROLLING ROLLOVER ROLLUP ROOT ROUND ROUND_TIES_TO_EVEN
+syn keyword plsqlKeyword ROW ROWDEPENDENCIES ROWIDTOCHAR ROWIDTONCHAR ROWID_MAPPING_TABLE ROWNUM ROWS
+syn keyword plsqlKeyword ROW_LENGTH ROW_NUMBER RPAD RTRIM RULE RULES RUNNING SALT SAMPLE SAVE SAVEPOINT
+syn keyword plsqlKeyword SAVE_AS_INTERVALS SB4 SCALAR SCALARS SCALE SCALE_ROWS SCAN SCAN_INSTANCES
+syn keyword plsqlKeyword SCHEDULER SCHEMA SCHEMACHECK SCN SCN_ASCENDING SCOPE SCRUB SDO_GEOM_KEY SDO_GEOM_MAX_X
+syn keyword plsqlKeyword SDO_GEOM_MAX_Y SDO_GEOM_MAX_Z SDO_GEOM_MBB SDO_GEOM_MBR SDO_GEOM_MIN_X SDO_GEOM_MIN_Y
+syn keyword plsqlKeyword SDO_GEOM_MIN_Z SDO_TOLERANCE SD_ALL SD_INHIBIT SD_SHOW SEARCH SECONDS SECRET
+syn keyword plsqlKeyword SECUREFILE SECUREFILE_DBA SECURITY SEED SEGMENT SEG_BLOCK SEG_FILE SELECTIVITY
+syn keyword plsqlKeyword SELF SEMIJOIN SEMIJOIN_DRIVER SEMI_TO_INNER SENSITIVE SEQUENCE SEQUENCED SEQUENTIAL
+syn keyword plsqlKeyword SERIAL SERIALIZABLE SERVERERROR SERVICE SERVICES SERVICE_NAME_CONVERT SESSION
+syn keyword plsqlKeyword SESSIONS_PER_USER SESSIONTIMEZONE SESSIONTZNAME SESSION_CACHED_CURSORS SETS
+syn keyword plsqlKeyword SETTINGS SET_GBY_PUSHDOWN SET_TO_JOIN SEVERE SHARD SHARDED SHARDS SHARDSPACE
+syn keyword plsqlKeyword SHARD_CHUNK_ID SHARED SHARED_POOL SHARE_OF SHARING SHD$COL$MAP SHELFLIFE SHOW
+syn keyword plsqlKeyword SHRINK SHUTDOWN SIBLING SIBLINGS SID SIGN SIGNAL_COMPONENT SIGNAL_FUNCTION
+syn keyword plsqlKeyword SIGNATURE SIMPLE SIN SINGLE SINGLETASK SINH SITE SKEWNESS_POP SKEWNESS_SAMP
+syn keyword plsqlKeyword SKIP SKIP_EXT_OPTIMIZER SKIP_PROXY SKIP_UNQ_UNUSABLE_IDX SKIP_UNUSABLE_INDEXES
+syn keyword plsqlKeyword SMALLFILE SNAPSHOT SOME SORT SOUNDEX SOURCE SOURCE_FILE_DIRECTORY SOURCE_FILE_NAME_CONVERT
+syn keyword plsqlKeyword SPACE SPATIAL SPECIFICATION SPFILE SPLIT SPREADSHEET SQL SQLLDR SQL_SCOPE
+syn keyword plsqlKeyword SQL_TRACE SQL_TRANSLATION_PROFILE SQRT STALE STANDALONE STANDARD_HASH STANDBY
+syn keyword plsqlKeyword STANDBYS STANDBY_MAX_DATA_DELAY STAR STARTUP STAR_TRANSFORMATION STATE STATEMENT
+syn keyword plsqlKeyword STATEMENTS STATEMENT_ID STATEMENT_QUEUING STATIC STATISTICS STATS_BINOMIAL_TEST
+syn keyword plsqlKeyword STATS_CROSSTAB STATS_F_TEST STATS_KS_TEST STATS_MODE STATS_MW_TEST STATS_ONE_WAY_ANOVA
+syn keyword plsqlKeyword STATS_T_TEST_INDEP STATS_T_TEST_INDEPU STATS_T_TEST_ONE STATS_T_TEST_PAIRED
+syn keyword plsqlKeyword STATS_WSR_TEST STDDEV STDDEV_POP STDDEV_SAMP STOP STORAGE STORAGE_INDEX STORE
+syn keyword plsqlKeyword STREAM STREAMS STRICT STRING STRINGS STRIP STRIPE_COLUMNS STRIPE_WIDTH STRUCTURE
+syn keyword plsqlKeyword SUBMULTISET SUBPARTITION SUBPARTITIONING SUBPARTITIONS SUBPARTITION_REL SUBQUERIES
+syn keyword plsqlKeyword SUBQUERY_PRUNING SUBSCRIBE SUBSET SUBSTITUTABLE SUBSTR SUBSTR2 SUBSTR4 SUBSTRB
+syn keyword plsqlKeyword SUBSTRC SUBTYPE SUCCESS SUCCESSFUL SUM SUMMARY SUPPLEMENTAL SUPPRESS_LOAD
+syn keyword plsqlKeyword SUSPEND SWAP_JOIN_INPUTS SWITCH SWITCHOVER SYNC SYNCHRONOUS SYSASM SYSAUX
+syn keyword plsqlKeyword SYSBACKUP SYSDATE SYSDBA SYSDG SYSGUID SYSKM SYSOBJ SYSOPER SYSRAC SYSTEM
+syn keyword plsqlKeyword SYSTEM_DEFINED SYSTEM_STATS SYSTIMESTAMP SYS_AUDIT SYS_CHECKACL SYS_CHECK_PRIVILEGE
+syn keyword plsqlKeyword SYS_CONNECT_BY_PATH SYS_CONS_ANY_SCALAR SYS_CONTEXT SYS_CTXINFOPK SYS_CTX_CONTAINS2
+syn keyword plsqlKeyword SYS_CTX_MKIVIDX SYS_DBURIGEN SYS_DL_CURSOR SYS_DM_RXFORM_CHR SYS_DM_RXFORM_LAB
+syn keyword plsqlKeyword SYS_DM_RXFORM_NUM SYS_DOM_COMPARE SYS_DST_PRIM2SEC SYS_DST_SEC2PRIM SYS_ET_BFILE_TO_RAW
+syn keyword plsqlKeyword SYS_ET_BLOB_TO_IMAGE SYS_ET_IMAGE_TO_BLOB SYS_ET_RAW_TO_BFILE SYS_EXTPDTXT
+syn keyword plsqlKeyword SYS_EXTRACT_UTC SYS_FBT_INSDEL SYS_FILTER_ACLS SYS_FNMATCHES SYS_FNREPLACE
+syn keyword plsqlKeyword SYS_GETTOKENID SYS_GETXTIVAL SYS_GET_ACLIDS SYS_GET_ANY_SCALAR SYS_GET_COL_ACLIDS
+syn keyword plsqlKeyword SYS_GET_PRIVILEGES SYS_GUID SYS_MAKEXML SYS_MAKE_XMLNODEID SYS_MKXMLATTR SYS_MKXTI
+syn keyword plsqlKeyword SYS_OPTLOBPRBSC SYS_OPTXICMP SYS_OPTXQCASTASNQ SYS_OP_ADT2BIN SYS_OP_ADTCONS
+syn keyword plsqlKeyword SYS_OP_ALSCRVAL SYS_OP_ATG SYS_OP_BIN2ADT SYS_OP_BITVEC SYS_OP_BL2R SYS_OP_BLOOM_FILTER
+syn keyword plsqlKeyword SYS_OP_BLOOM_FILTER_LIST SYS_OP_C2C SYS_OP_CAST SYS_OP_CEG SYS_OP_CL2C SYS_OP_COMBINED_HASH
+syn keyword plsqlKeyword SYS_OP_COMP SYS_OP_CONVERT SYS_OP_COUNTCHG SYS_OP_CSCONV SYS_OP_CSCONVTEST
+syn keyword plsqlKeyword SYS_OP_CSR SYS_OP_CSX_PATCH SYS_OP_CYCLED_SEQ SYS_OP_DECOMP SYS_OP_DESCEND
+syn keyword plsqlKeyword SYS_OP_DISTINCT SYS_OP_DRA SYS_OP_DSB_DESERIALIZE SYS_OP_DSB_SERIALIZE SYS_OP_DUMP
+syn keyword plsqlKeyword SYS_OP_DV_CHECK SYS_OP_ENFORCE_NOT_NULL$ SYS_OP_EXTRACT SYS_OP_GROUPING SYS_OP_GUID
+syn keyword plsqlKeyword SYS_OP_HASH SYS_OP_HCS_TABLE SYS_OP_IIX SYS_OP_INTERVAL_HIGH_BOUND SYS_OP_ITR
+syn keyword plsqlKeyword SYS_OP_KEY_VECTOR_CREATE SYS_OP_KEY_VECTOR_FILTER SYS_OP_KEY_VECTOR_FILTER_LIST
+syn keyword plsqlKeyword SYS_OP_KEY_VECTOR_PAYLOAD SYS_OP_KEY_VECTOR_SUCCEEDED SYS_OP_KEY_VECTOR_USE
+syn keyword plsqlKeyword SYS_OP_LBID SYS_OP_LOBLOC2BLOB SYS_OP_LOBLOC2CLOB SYS_OP_LOBLOC2ID SYS_OP_LOBLOC2NCLOB
+syn keyword plsqlKeyword SYS_OP_LOBLOC2TYP SYS_OP_LSVI SYS_OP_LVL SYS_OP_MAKEOID SYS_OP_MAP_NONNULL
+syn keyword plsqlKeyword SYS_OP_MSR SYS_OP_NICOMBINE SYS_OP_NIEXTRACT SYS_OP_NII SYS_OP_NIX SYS_OP_NOEXPAND
+syn keyword plsqlKeyword SYS_OP_NTCIMG$ SYS_OP_NUMTORAW SYS_OP_OBJ_UPD_IN_TXN SYS_OP_OIDVALUE SYS_OP_OPNSIZE
+syn keyword plsqlKeyword SYS_OP_PAR SYS_OP_PARGID SYS_OP_PARGID_1 SYS_OP_PART_ID SYS_OP_PAR_1 SYS_OP_PIVOT
+syn keyword plsqlKeyword SYS_OP_R2O SYS_OP_RAWTONUM SYS_OP_RDTM SYS_OP_REF SYS_OP_RMTD SYS_OP_ROWIDTOOBJ
+syn keyword plsqlKeyword SYS_OP_RPB SYS_OP_TOSETID SYS_OP_TPR SYS_OP_TRTB SYS_OP_UNDESCEND SYS_OP_VECAND
+syn keyword plsqlKeyword SYS_OP_VECBIT SYS_OP_VECOR SYS_OP_VECTOR_GROUP_BY SYS_OP_VECXOR SYS_OP_VERSION
+syn keyword plsqlKeyword SYS_OP_VREF SYS_OP_VVD SYS_OP_XMLCONS_FOR_CSX SYS_OP_XPTHATG SYS_OP_XPTHIDX
+syn keyword plsqlKeyword SYS_OP_XPTHOP SYS_OP_XTNN SYS_OP_XTXT2SQLT SYS_OP_ZONE_ID SYS_ORDERKEY_DEPTH
+syn keyword plsqlKeyword SYS_ORDERKEY_MAXCHILD SYS_ORDERKEY_PARENT SYS_PARALLEL_TXN SYS_PATHID_IS_ATTR
+syn keyword plsqlKeyword SYS_PATHID_IS_NMSPC SYS_PATHID_LASTNAME SYS_PATHID_LASTNMSPC SYS_PATH_REVERSE
+syn keyword plsqlKeyword SYS_PLSQL_COUNT SYS_PLSQL_CPU SYS_PLSQL_IO SYS_PXQEXTRACT SYS_RAW_TO_XSID
+syn keyword plsqlKeyword SYS_REMAP_XMLTYPE SYS_RID_ORDER SYS_ROW_DELTA SYS_SC_2_XMLT SYS_SYNRCIREDO
+syn keyword plsqlKeyword SYS_TYPEID SYS_UMAKEXML SYS_XMLANALYZE SYS_XMLCONTAINS SYS_XMLCONV SYS_XMLEXNSURI
+syn keyword plsqlKeyword SYS_XMLGEN SYS_XMLINSTR SYS_XMLI_LOC_ISNODE SYS_XMLI_LOC_ISTEXT SYS_XMLLOCATOR_GETSVAL
+syn keyword plsqlKeyword SYS_XMLNODEID SYS_XMLNODEID_GETCID SYS_XMLNODEID_GETLOCATOR SYS_XMLNODEID_GETOKEY
+syn keyword plsqlKeyword SYS_XMLNODEID_GETPATHID SYS_XMLNODEID_GETPTRID SYS_XMLNODEID_GETRID SYS_XMLNODEID_GETSVAL
+syn keyword plsqlKeyword SYS_XMLNODEID_GETTID SYS_XMLTRANSLATE SYS_XMLTYPE2SQL SYS_XMLT_2_SC SYS_XQBASEURI
+syn keyword plsqlKeyword SYS_XQCASTABLEERRH SYS_XQCODEP2STR SYS_XQCODEPEQ SYS_XQCON2SEQ SYS_XQCONCAT
+syn keyword plsqlKeyword SYS_XQDELETE SYS_XQDFLTCOLATION SYS_XQDOC SYS_XQDOCURI SYS_XQDURDIV SYS_XQED4URI
+syn keyword plsqlKeyword SYS_XQENDSWITH SYS_XQERR SYS_XQERRH SYS_XQESHTMLURI SYS_XQEXLOBVAL SYS_XQEXSTWRP
+syn keyword plsqlKeyword SYS_XQEXTRACT SYS_XQEXTRREF SYS_XQEXVAL SYS_XQFB2STR SYS_XQFNBOOL SYS_XQFNCMP
+syn keyword plsqlKeyword SYS_XQFNDATIM SYS_XQFNLNAME SYS_XQFNNM SYS_XQFNNSURI SYS_XQFNPREDTRUTH SYS_XQFNQNM
+syn keyword plsqlKeyword SYS_XQFNROOT SYS_XQFORMATNUM SYS_XQFTCONTAIN SYS_XQFUNCR SYS_XQGETCONTENT
+syn keyword plsqlKeyword SYS_XQINDXOF SYS_XQINSERT SYS_XQINSPFX SYS_XQIRI2URI SYS_XQLANG SYS_XQLLNMFRMQNM
+syn keyword plsqlKeyword SYS_XQMKNODEREF SYS_XQNILLED SYS_XQNODENAME SYS_XQNORMSPACE SYS_XQNORMUCODE
+syn keyword plsqlKeyword SYS_XQNSP4PFX SYS_XQNSPFRMQNM SYS_XQPFXFRMQNM SYS_XQPOLYABS SYS_XQPOLYADD
+syn keyword plsqlKeyword SYS_XQPOLYCEL SYS_XQPOLYCST SYS_XQPOLYCSTBL SYS_XQPOLYDIV SYS_XQPOLYFLR SYS_XQPOLYMOD
+syn keyword plsqlKeyword SYS_XQPOLYMUL SYS_XQPOLYRND SYS_XQPOLYSQRT SYS_XQPOLYSUB SYS_XQPOLYUMUS SYS_XQPOLYUPLS
+syn keyword plsqlKeyword SYS_XQPOLYVEQ SYS_XQPOLYVGE SYS_XQPOLYVGT SYS_XQPOLYVLE SYS_XQPOLYVLT SYS_XQPOLYVNE
+syn keyword plsqlKeyword SYS_XQREF2VAL SYS_XQRENAME SYS_XQREPLACE SYS_XQRESVURI SYS_XQRNDHALF2EVN SYS_XQRSLVQNM
+syn keyword plsqlKeyword SYS_XQRYENVPGET SYS_XQRYVARGET SYS_XQRYWRP SYS_XQSEQ2CON SYS_XQSEQ2CON4XC
+syn keyword plsqlKeyword SYS_XQSEQDEEPEQ SYS_XQSEQINSB SYS_XQSEQRM SYS_XQSEQRVS SYS_XQSEQSUB SYS_XQSEQTYPMATCH
+syn keyword plsqlKeyword SYS_XQSTARTSWITH SYS_XQSTATBURI SYS_XQSTR2CODEP SYS_XQSTRJOIN SYS_XQSUBSTRAFT
+syn keyword plsqlKeyword SYS_XQSUBSTRBEF SYS_XQTOKENIZE SYS_XQTREATAS SYS_XQXFORM SYS_XQ_ASQLCNV SYS_XQ_ATOMCNVCHK
+syn keyword plsqlKeyword SYS_XQ_NRNG SYS_XQ_PKSQL2XML SYS_XQ_UPKXML2SQL SYS_XSID_TO_RAW SYS_ZMAP_FILTER
+syn keyword plsqlKeyword SYS_ZMAP_REFRESH TABAUTH TABLES TABLESPACE TABLESPACE_NO TABLE_LOOKUP_BY_NL
+syn keyword plsqlKeyword TABLE_STATS TABNO TAG TAN TANH TARGET TBL$OR$IDX$PART$NUM TEMP TEMPFILE TEMPLATE
+syn keyword plsqlKeyword TEMPORARY TEMP_TABLE TENANT_ID TEST TEXT THAN THE THREAD THROUGH TIER TIES
+syn keyword plsqlKeyword TIMEOUT TIMES TIMESTAMP_TO_NUMBER TIMEZONE_ABBR TIMEZONE_HOUR TIMEZONE_MINUTE
+syn keyword plsqlKeyword TIMEZONE_OFFSET TIMEZONE_REGION TIME_ZONE TIV_GB TIV_SSF TOKEN TOPLEVEL TO_ACLID
+syn keyword plsqlKeyword TO_APPROX_COUNT_DISTINCT TO_APPROX_PERCENTILE TO_BINARY_DOUBLE TO_BINARY_FLOAT
+syn keyword plsqlKeyword TO_BLOB TO_CHAR TO_CLOB TO_DATE TO_DSINTERVAL TO_ISO_STRING TO_LOB TO_MULTI_BYTE
+syn keyword plsqlKeyword TO_NCHAR TO_NCLOB TO_NUMBER TO_SINGLE_BYTE TO_TIME TO_TIMESTAMP TO_TIMESTAMP_TZ
+syn keyword plsqlKeyword TO_TIME_TZ TO_UTC_TIMESTAMP_TZ TO_YMINTERVAL TRACE TRACING TRACKING TRAILING
+syn keyword plsqlKeyword TRANSACTION TRANSFORM TRANSFORM_DISTINCT_AGG TRANSITION TRANSITIONAL TRANSLATE
+syn keyword plsqlKeyword TRANSLATION TRANSPORTABLE TREAT TRIGGERS TRIM TRUNC TRUNCATE TRUST TRUSTED
+syn keyword plsqlKeyword TUNING TX TYPENAME TYPES TZ_OFFSET UB2 UBA UCS2 UID UNARCHIVED UNBOUND
+syn keyword plsqlKeyword UNBOUNDED UNCONDITIONAL UNDER UNDO UNDROP UNIFORM UNINSTALL UNION_ALL UNISTR
+syn keyword plsqlKeyword UNITE UNIXTIME UNLIMITED UNLOAD UNLOCK UNMATCHED UNNEST UNNEST_INNERJ_DISTINCT_VIEW
+syn keyword plsqlKeyword UNNEST_NOSEMIJ_NODISTINCTVIEW UNNEST_SEMIJ_VIEW UNPACKED UNPIVOT UNPLUG UNPROTECTED
+syn keyword plsqlKeyword UNQUIESCE UNRECOVERABLE UNRESTRICTED UNSUBSCRIBE UNTIL UNUSABLE UNUSED UPDATABLE
+syn keyword plsqlKeyword UPDATED UPDATEXML UPD_INDEXES UPD_JOININDEX UPGRADE UPPER UPSERT USABLE USAGE
+syn keyword plsqlKeyword USE USER USERENV USERGROUP USERS USER_DATA USER_DEFINED USER_RECYCLEBIN USER_TABLESPACES
+syn keyword plsqlKeyword USE_ANTI USE_CONCAT USE_CUBE USE_DAGG_UNION_ALL_GSETS USE_HASH USE_HASH_AGGREGATION
+syn keyword plsqlKeyword USE_HASH_GBY_FOR_DAGGPSHD USE_HASH_GBY_FOR_PUSHDOWN USE_HIDDEN_PARTITIONS
+syn keyword plsqlKeyword USE_INVISIBLE_INDEXES USE_MERGE USE_MERGE_CARTESIAN USE_NL USE_NL_WITH_INDEX
+syn keyword plsqlKeyword USE_PARTITION_WISE_DISTINCT USE_PARTITION_WISE_GBY USE_PARTITION_WISE_WIF
+syn keyword plsqlKeyword USE_PRIVATE_OUTLINES USE_SCALABLE_GBY_INVDIST USE_SEMI USE_STORED_OUTLINES
+syn keyword plsqlKeyword USE_TTT_FOR_GSETS USE_VECTOR_AGGREGATION USE_WEAK_NAME_RESL USING USING_NO_EXPAND
+syn keyword plsqlKeyword UTF16BE UTF16LE UTF32 UTF8 V1 V2 VALIDATE VALIDATE_CONVERSION VALIDATION VALID_TIME_END
+syn keyword plsqlKeyword VALUE VARIANCE VARRAY VARRAYS VAR_POP VAR_SAMP VECTOR VECTOR_ENCODE VECTOR_READ
+syn keyword plsqlKeyword VECTOR_READ_TRACE VECTOR_TRANSFORM VECTOR_TRANSFORM_DIMS VECTOR_TRANSFORM_FACT
+syn keyword plsqlKeyword VERIFIER VERIFY VERSION VERSIONING VERSIONS VERSIONS_ENDSCN VERSIONS_ENDTIME
+syn keyword plsqlKeyword VERSIONS_OPERATION VERSIONS_STARTSCN VERSIONS_STARTTIME VERSIONS_XID VIEWS
+syn keyword plsqlKeyword VIOLATION VIRTUAL VISIBILITY VISIBLE VOLUME VSIZE WAIT WALLET WEEK WEEKS WELLFORMED
+syn keyword plsqlKeyword WHENEVER WHITESPACE WIDTH_BUCKET WINDOW WITHIN WITHOUT WITH_EXPRESSION
+syn keyword plsqlKeyword WITH_PLSQL WORK WRAPPED WRAPPER WRITE XDB_FASTPATH_INSERT XID XML XML2OBJECT
+syn keyword plsqlKeyword XMLATTRIBUTES XMLCAST XMLCDATA XMLCOLATTVAL XMLCOMMENT XMLCONCAT XMLDIFF XMLELEMENT
+syn keyword plsqlKeyword XMLEXISTS XMLEXISTS2 XMLFOREST XMLINDEX_REWRITE XMLINDEX_REWRITE_IN_SELECT
+syn keyword plsqlKeyword XMLINDEX_SEL_IDX_TBL XMLISNODE XMLISVALID XMLNAMESPACES XMLPARSE XMLPATCH
+syn keyword plsqlKeyword XMLPI XMLQUERY XMLQUERYVAL XMLROOT XMLSCHEMA XMLSERIALIZE XMLTABLE XMLTOJSON
+syn keyword plsqlKeyword XMLTOKENSET XMLTRANSFORM XMLTRANSFORMBLOB XMLTSET_DML_ENABLE XML_DIAG XML_DML_RWT_STMT
+syn keyword plsqlKeyword XPATHTABLE XS XS_SYS_CONTEXT X_DYN_PRUNE YEARS YES ZONEMAP
+
+" Some of Oracle's Reserved keywords.
+syn keyword plsqlReserved ACCESSIBLE AGENT ALL ALTER ANY ASC BFILE_BASE BLOB_BASE BY
+"syn match plsqlReserved "\<AS\>"
+syn keyword plsqlReserved C CALLING CHARSET CHARSETFORM CHARSETID CHAR_BASE CHECK CLOB_BASE CLUSTER
+syn keyword plsqlReserved COLLATE COMPILED COMPRESS CONNECT CONNECT_BY_ROOT CONSTRUCTOR CUSTOMDATUM
+syn keyword plsqlReserved DATE_BASE DEFAULT DESC DISTINCT DROP DURATION EXCEPT EXCLUSIVE
+syn match plsqlReserved "\<DELETE\>"
+syn keyword plsqlReserved EXIT FIXED FROM GENERAL GRANT GROUP HAVING IDENTIFIED INDEX
+syn match plsqlReserved "\<EXISTS\>"
+syn keyword plsqlReserved INDICES INTERFACE INTERSECT INTO LARGE LIMITED LOCK MAXLEN
+syn keyword plsqlReserved MINUS MODE NOCOMPRESS NOWAIT NUMBER_BASE OCICOLL OCIDATE OCIDATETIME
+syn keyword plsqlReserved OCIDURATION OCIINTERVAL OCILOBLOCATOR OCINUMBER OCIRAW OCIREF OCIREFCURSOR
+syn keyword plsqlReserved OCIROWID OCISTRING OCITYPE OF ON OPTION ORACLE ORADATA ORDER ORLANY ORLVARY
+syn keyword plsqlReserved OUT OVERRIDING PARALLEL_ENABLE PARAMETER PASCAL PCTFREE PIPE PIPELINED POLYMORPHIC
+syn keyword plsqlReserved PRAGMA PRIOR PUBLIC RAISE RECORD RELIES_ON REM RENAME RESOURCE RESULT REVOKE ROWID
+syn keyword plsqlReserved SB1 SB2
+syn match plsqlReserved "\<SELECT\>"
+syn keyword plsqlReserved SEPARATE SHARE SHORT SIZE SIZE_T SPARSE SQLCODE SQLDATA
+syn keyword plsqlReserved SQLNAME SQLSTATE STANDARD START STORED STRUCT STYLE SYNONYM TABLE TDO
+syn keyword plsqlReserved TRANSACTIONAL TRIGGER UB1 UB4 UNION UNIQUE UNSIGNED UNTRUSTED VALIST
+syn keyword plsqlReserved VALUES VARIABLE VIEW VOID WHERE WITH
+
+" PL/SQL and SQL functions.
+syn keyword plsqlFunction ABS ACOS ADD_MONTHS APPROX_COUNT APPROX_COUNT_DISTINCT APPROX_COUNT_DISTINCT_AGG
+syn keyword plsqlFunction APPROX_COUNT_DISTINCT_DETAIL APPROX_MEDIAN APPROX_PERCENTILE APPROX_PERCENTILE_AGG
+syn keyword plsqlFunction APPROX_PERCENTILE_DETAIL APPROX_RANK APPROX_SUM ASCII ASCIISTR ASIN ATAN ATAN2
+syn keyword plsqlFunction AVG BFILENAME BIN_TO_NUM BITAND CARDINALITY CAST CEIL CHARTOROWID CHR CLUSTER_DETAILS
+syn keyword plsqlFunction CLUSTER_DISTANCE CLUSTER_ID CLUSTER_PROBABILITY CLUSTER_SET COALESCE COLLATION
+syn keyword plsqlFunction COLLECT COMPOSE CONCAT CONVERT CON_DBID_TO_ID CON_GUID_TO_ID CON_NAME_TO_ID
+syn keyword plsqlFunction CON_UID_TO_ID CORR COS COSH COVAR_POP COVAR_SAMP CUME_DIST CURRENT_DATE
+syn keyword plsqlFunction CURRENT_TIMESTAMP CV DATAOBJ_TO_MAT_PARTITION DATAOBJ_TO_PARTITION DBTIMEZONE
+syn keyword plsqlFunction DECODE DECOMPOSE DENSE_RANK DEPTH DEREF DUMP EMPTY_BLOB EMPTY_CLOB EXISTSNODE
+syn keyword plsqlFunction EXP EXTRACT EXTRACTVALUE FEATURE_COMPARE FEATURE_DETAILS FEATURE_ID FEATURE_SET
+syn keyword plsqlFunction FEATURE_VALUE FIRST_VALUE FLOOR FROM_TZ GREATEST GROUPING GROUPING_ID
+syn keyword plsqlFunction GROUP_ID HEXTORAW INITCAP INSTR ITERATION_NUMBER JSON_ARRAY JSON_ARRAYAGG
+syn keyword plsqlFunction JSON_OBJECT JSON_OBJECTAGG JSON_QUERY JSON_TABLE JSON_VALUE LAG LAST_DAY
+syn keyword plsqlFunction LAST_VALUE LEAD LEAST LENGTH LISTAGG LN LNNVL LOCALTIMESTAMP LOG LOWER LPAD
+syn keyword plsqlFunction LTRIM MAKE_REF MAX MEDIAN MIN MOD MONTHS_BETWEEN NANVL NCHR NEW_TIME NEXT_DAY
+syn keyword plsqlFunction NLSSORT NLS_CHARSET_DECL_LEN NLS_CHARSET_ID NLS_CHARSET_NAME NLS_COLLATION_ID
+syn keyword plsqlFunction NLS_COLLATION_NAME NLS_INITCAP NLS_LOWER NLS_UPPER NTH_VALUE NTILE NULLIF
+syn keyword plsqlFunction NUMTODSINTERVAL NUMTOYMINTERVAL NVL NVL2 ORA_DM_PARTITION_NAME ORA_DST_AFFECTED
+syn keyword plsqlFunction ORA_DST_CONVERT ORA_DST_ERROR ORA_HASH ORA_INVOKING_USER ORA_INVOKING_USERID
+syn keyword plsqlFunction PATH PERCENTILE_CONT PERCENTILE_DISC PERCENT_RANK POWER POWERMULTISET POWERMULTISET_BY_CARDINALITY
+syn keyword plsqlFunction PREDICTION PREDICTION_BOUNDS PREDICTION_COST PREDICTION_DETAILS PREDICTION_PROBABILITY
+syn keyword plsqlFunction PREDICTION_SET PRESENTNNV PRESENTV PREVIOUS RANK RATIO_TO_REPORT RAWTOHEX
+syn keyword plsqlFunction RAWTONHEX REFTOHEX REGEXP_COUNT REGEXP_INSTR REGEXP_REPLACE REGEXP_SUBSTR
+syn keyword plsqlFunction REMAINDER ROUND ROUND ROWIDTOCHAR ROWIDTONCHAR ROW_NUMBER RPAD RTRIM
+syn keyword plsqlFunction SCN_TO_TIMESTAMP SESSIONTIMEZONE SIGN SIN SINH SOUNDEX SQRT STANDARD_HASH
+syn keyword plsqlFunction STATS_BINOMIAL_TEST STATS_CROSSTAB STATS_F_TEST STATS_KS_TEST STATS_MODE STATS_MW_TEST
+syn keyword plsqlFunction STATS_ONE_WAY_ANOVA STATS_WSR_TEST STDDEV STDDEV_POP STDDEV_SAMP SUBSTR SUM
+syn keyword plsqlFunction SYSDATE SYSTIMESTAMP SYS_CONNECT_BY_PATH SYS_CONTEXT SYS_DBURIGEN SYS_EXTRACT_UTC
+syn keyword plsqlFunction SYS_GUID SYS_OP_ZONE_ID SYS_TYPEID SYS_XMLAGG SYS_XMLGEN TAN TANH TIMESTAMP_TO_SCN
+syn keyword plsqlFunction TO_APPROX_COUNT_DISTINCT TO_APPROX_PERCENTILE TO_BINARY_DOUBLE TO_BINARY_FLOAT
+syn keyword plsqlFunction TO_BLOB TO_CHAR TO_CLOB TO_DATE TO_DSINTERVAL TO_LOB TO_MULTI_BYTE TO_NCHAR
+syn keyword plsqlFunction TO_NCLOB TO_NUMBER TO_SINGLE_BYTE TO_TIMESTAMP TO_TIMESTAMP_TZ TO_YMINTERVAL
+syn keyword plsqlFunction TRANSLATE TREAT TRIM TRUNC TZ_OFFSET UID UNISTR UPPER USER USERENV VALIDATE_CONVERSION
+syn keyword plsqlFunction VALUE VARIANCE VAR_POP VAR_SAMP VSIZE WIDTH_BUCKET XMLAGG XMLCAST XMLCDATA
+syn keyword plsqlFunction XMLCOLATTVAL XMLCOMMENT XMLCONCAT XMLDIFF XMLELEMENT XMLEXISTS XMLFOREST XMLISVALID
+syn keyword plsqlFunction XMLPARSE XMLPATCH XMLPI XMLQUERY XMLROOT XMLSEQUENCE XMLSERIALIZE XMLTABLE
+syn keyword plsqlFunction XMLTRANSFORM
+syn keyword plsqlFunction CURRVAL NEXTVAL
syn match plsqlFunction "\<SYS\$LOB_REPLICATION\>"
+syn match plsqlFunction "\.COUNT\>"hs=s+1
+syn match plsqlFunction "\.EXISTS\>"hs=s+1
+syn match plsqlFunction "\.FIRST\>"hs=s+1
+syn match plsqlFunction "\.LAST\>"hs=s+1
+syn match plsqlFunction "\.DELETE\>"hs=s+1
+syn match plsqlFunction "\.PREV\>"hs=s+1
+syn match plsqlFunction "\.NEXT\>"hs=s+1
+
+if exists("plsql_legacy_sql_keywords")
+ " Some of Oracle's SQL keywords.
+ syn keyword plsqlSQLKeyword ABORT ACCESS ACCESSED ADD AFTER ALL ALTER AND ANY
+ syn keyword plsqlSQLKeyword ASC ATTRIBUTE AUDIT AUTHORIZATION AVG BASE_TABLE
+ syn keyword plsqlSQLKeyword BEFORE BETWEEN BY CASCADE CAST CHECK CLUSTER
+ syn keyword plsqlSQLKeyword CLUSTERS COLAUTH COLUMN COMMENT COMPRESS CONNECT
+ syn keyword plsqlSQLKeyword CONSTRAINT CRASH CURRENT DATA DATABASE
+ syn keyword plsqlSQLKeyword DATA_BASE DBA DEFAULT DELAY DELETE DESC DISTINCT
+ syn keyword plsqlSQLKeyword DROP DUAL EXCLUSIVE EXISTS EXTENDS EXTRACT
+ syn keyword plsqlSQLKeyword FILE FORCE FOREIGN FROM GRANT GROUP HAVING HEAP
+ syn keyword plsqlSQLKeyword IDENTIFIED IDENTIFIER IMMEDIATE IN INCLUDING
+ syn keyword plsqlSQLKeyword INCREMENT INDEX INDEXES INITIAL INSERT INSTEAD
+ syn keyword plsqlSQLKeyword INTERSECT INTO INVALIDATE ISOLATION KEY LIBRARY
+ syn keyword plsqlSQLKeyword LIKE LOCK MAXEXTENTS MINUS MODE MODIFY MULTISET
+ syn keyword plsqlSQLKeyword NESTED NOAUDIT NOCOMPRESS NOT NOWAIT OF OFF OFFLINE
+ syn keyword plsqlSQLKeyword ON ONLINE OPERATOR OPTION ORDER ORGANIZATION
+ syn keyword plsqlSQLKeyword PCTFREE PRIMARY PRIOR PRIVATE PRIVILEGES PUBLIC
+ syn keyword plsqlSQLKeyword QUOTA RELEASE RENAME REPLACE RESOURCE REVOKE ROLLBACK
+ syn keyword plsqlSQLKeyword ROW ROWLABEL ROWS SCHEMA
+ syn match plsqlSQLKeyword "\<SELECT\>"
+ syn keyword plsqlSQLKeyword SEPARATE SESSION SET
+ syn keyword plsqlSQLKeyword SHARE SIZE SPACE START STORE SUCCESSFUL SYNONYM
+ syn keyword plsqlSQLKeyword SYSDATE TABLE TABLES TABLESPACE TEMPORARY TO TREAT
+ syn keyword plsqlSQLKeyword TRIGGER TRUNCATE UID UNION UNIQUE UNLIMITED UPDATE
+ syn keyword plsqlSQLKeyword USE USER VALIDATE VALUES VIEW WHENEVER WHERE WITH
+endif
+
+" The built-in types.
+syn keyword plsqlStorage CHAR BYTE VARCHAR2 NCHAR NVARCHAR2
+syn keyword plsqlStorage NUMBER FLOAT BINARY_FLOAT BINARY_DOUBLE LONG RAW
+syn keyword plsqlStorage DATE TIMESTAMP INTERVAL LOCAL TIME ZONE TO MONTH SECOND YEAR DAY
+syn keyword plsqlStorage BLOB CLOB NCLOB BFILE UROWID
+syn keyword plsqlStorage CHARACTER VARYING VARCHAR NATIONAL CHARACTER
+syn keyword plsqlStorage NUMERIC DECIMAL DEC INTEGER INT SMALLINT
+syn keyword plsqlStorage FLOAT DOUBLE PRECISION REAL
+syn keyword plsqlStorage SDO_GEOMETRY SDO_TOPO_GEOMETRY SDO_GEORASTER
+syn keyword plsqlStorage REF ANYTYPE ANYDATA ANYDATASET XMLTYPE HTTPURITYPE XDBURITYPE DUBRITYPE
+syn keyword plsqlStorage BOOLEAN PLS_INTEGER BINARY_INTEGER SIMPLE_FLOAT SIMPLE_INTEGER SIMPLE_DOUBLE SYS_REFCURSOR
+"
" PL/SQL Exceptions
syn keyword plsqlException ACCESS_INTO_NULL CASE_NOT_FOUND COLLECTION_IS_NULL
syn keyword plsqlException CURSOR_ALREADY_OPEN DUP_VAL_ON_INDEX INVALID_CURSOR
@@ -139,32 +565,30 @@ syn keyword plsqlException SUBSCRIPT_OUTSIDE_LIMIT SYS_INVALID_ROWID
syn keyword plsqlException TIMEOUT_ON_RESOURCE TOO_MANY_ROWS VALUE_ERROR
syn keyword plsqlException ZERO_DIVIDE
-" Oracle Pseudo Colums.
-syn keyword plsqlPseudo CURRVAL LEVEL NEXTVAL ROWID ROWNUM
-
if exists("plsql_highlight_triggers")
- syn keyword plsqlTrigger INSERTING UPDATING DELETING
+ syn keyword plsqlTrigger INSERTING UPDATING DELETING
endif
-" Conditionals.
-syn keyword plsqlConditional ELSIF ELSE IF
-syn match plsqlConditional "\<END\s\+IF\>"
-
-" Loops.
-syn keyword plsqlRepeat FOR LOOP WHILE FORALL
-syn match plsqlRepeat "\<END\s\+LOOP\>"
+" so can not contain it for folding. May no longer be necessary and can change them to plsqlKeyword
+syn match plsqlBEGIN "\<BEGIN\>"
+syn match plsqlEND "\<END\>"
+syn match plsqlISAS "\<\(IS\|AS\)\>"
" Various types of comments.
-if exists("c_comment_strings")
- syntax match plsqlCommentSkip contained "^\s*\*\($\|\s\+\)"
- syntax region plsqlCommentString contained start=+L\="+ skip=+\\\\\|\\"+ end=+"+ end=+\*/+me=s-1 contains=plsqlCommentSkip
- syntax region plsqlComment2String contained start=+L\="+ skip=+\\\\\|\\"+ end=+"+ end="$"
- syntax region plsqlCommentL start="--" skip="\\$" end="$" keepend contains=@plsqlCommentGroup,plsqlComment2String,plsqlCharLiteral,plsqlBooleanLiteral,plsqlNumbersCom,plsqlSpaceError
- syntax region plsqlComment start="/\*" end="\*/" contains=@plsqlCommentGroup,plsqlComment2String,plsqlCharLiteral,plsqlBooleanLiteral,plsqlNumbersCom,plsqlSpaceError
+syntax region plsqlCommentL start="--" skip="\\$" end="$" keepend extend contains=@plsqlCommentGroup,plsqlSpaceError
+if exists("plsql_fold")
+ syntax region plsqlComment
+ \ start="/\*" end="\*/"
+ \ extend
+ \ contains=@plsqlCommentGroup,plsqlSpaceError
+ \ fold
else
- syntax region plsqlCommentL start="--" skip="\\$" end="$" keepend contains=@plsqlCommentGroup,plsqlSpaceError
- syntax region plsqlComment start="/\*" end="\*/" contains=@plsqlCommentGroup,plsqlSpaceError
+ syntax region plsqlComment
+ \ start="/\*" end="\*/"
+ \ extend
+ \ contains=@plsqlCommentGroup,plsqlSpaceError
endif
+syn cluster plsqlCommentAll contains=plsqlCommentL,plsqlComment
syn sync ccomment plsqlComment
syn sync ccomment plsqlCommentL
@@ -173,28 +597,38 @@ syn sync ccomment plsqlCommentL
syn match plsqlStringError "'.*$"
" Various types of literals.
-syn match plsqlNumbers transparent "\<[+-]\=\d\|[+-]\=\.\d" contains=plsqlIntLiteral,plsqlFloatLiteral
-syn match plsqlNumbersCom contained transparent "\<[+-]\=\d\|[+-]\=\.\d" contains=plsqlIntLiteral,plsqlFloatLiteral
-syn match plsqlIntLiteral contained "[+-]\=\d\+"
-syn match plsqlFloatLiteral contained "[+-]\=\d\+\.\d*"
-syn match plsqlFloatLiteral contained "[+-]\=\d*\.\d*"
-syn match plsqlCharLiteral "'[^']'"
-syn match plsqlStringLiteral "'\([^']\|''\)*'"
-syn keyword plsqlBooleanLiteral TRUE FALSE NULL
+" the + and - get sucked up as operators. Not sure how to take precedence here. Something to do with word boundaries.
+" most other syntax files do not try to includ +/- in the number token, so leave them as unary operators
+" even though the oracle documentation counts the sign as part of the numeric literal
+syn match plsqlNumbers transparent "\<\d\|\.\d" contains=plsqlIntLiteral,plsqlFloatLiteral
+syn match plsqlNumbersCom contained transparent "\<\d\|\.\d" contains=plsqlIntLiteral,plsqlFloatLiteral
+syn match plsqlIntLiteral contained "\d\+"
+syn match plsqlFloatLiteral contained "\d\+\.\(\d\+\([eE][+-]\?\d\+\)\?\)\?[fd]\?"
+syn match plsqlFloatLiteral contained "\.\(\d\+\([eE][+-]\?\d\+\)\?\)[fd]\?"
-" The built-in types.
-syn keyword plsqlStorage ANYDATA ANYTYPE BFILE BINARY_INTEGER BLOB BOOLEAN
-syn keyword plsqlStorage BYTE CHAR CHARACTER CLOB CURSOR DATE DAY DEC DECIMAL
-syn keyword plsqlStorage DOUBLE DSINTERVAL_UNCONSTRAINED FLOAT HOUR
-syn keyword plsqlStorage INT INTEGER INTERVAL LOB LONG MINUTE
-syn keyword plsqlStorage MLSLABEL MONTH NATURAL NATURALN NCHAR NCHAR_CS NCLOB
-syn keyword plsqlStorage NUMBER NUMERIC NVARCHAR PLS_INT PLS_INTEGER
-syn keyword plsqlStorage POSITIVE POSITIVEN PRECISION RAW REAL RECORD
-syn keyword plsqlStorage SECOND SIGNTYPE SMALLINT STRING SYS_REFCURSOR TABLE TIME
-syn keyword plsqlStorage TIMESTAMP TIMESTAMP_UNCONSTRAINED
-syn keyword plsqlStorage TIMESTAMP_TZ_UNCONSTRAINED
-syn keyword plsqlStorage TIMESTAMP_LTZ_UNCONSTRAINED UROWID VARCHAR
-syn keyword plsqlStorage VARCHAR2 YEAR YMINTERVAL_UNCONSTRAINED ZONE
+" double quoted strings in SQL are database object names. Should be a subgroup of Normal.
+" We will use Character group as a proxy for that so color can be chosen close to Normal
+syn region plsqlQuotedIdentifier matchgroup=plsqlOperator start=+n\?"+ end=+"+ keepend extend
+syn cluster plsqlIdentifiers contains=plsqlIdentifier,plsqlQuotedIdentifier
+
+" quoted string literals
+if exists("plsql_fold")
+ syn region plsqlStringLiteral matchgroup=plsqlOperator start=+n\?'+ skip=+''+ end=+'+ fold keepend extend
+ syn region plsqlStringLiteral matchgroup=plsqlOperator start=+n\?q'\z([^[(<{]\)+ end=+\z1'+ fold keepend extend
+ syn region plsqlStringLiteral matchgroup=plsqlOperator start=+n\?q'<+ end=+>'+ fold keepend extend
+ syn region plsqlStringLiteral matchgroup=plsqlOperator start=+n\?q'{+ end=+}'+ fold keepend extend
+ syn region plsqlStringLiteral matchgroup=plsqlOperator start=+n\?q'(+ end=+)'+ fold keepend extend
+ syn region plsqlStringLiteral matchgroup=plsqlOperator start=+n\?q'\[+ end=+]'+ fold keepend extend
+else
+ syn region plsqlStringLiteral matchgroup=plsqlOperator start=+n\?'+ skip=+''+ end=+'+
+ syn region plsqlStringLiteral matchgroup=plsqlOperator start=+n\?q'\z([^[(<{]\)+ end=+\z1'+
+ syn region plsqlStringLiteral matchgroup=plsqlOperator start=+n\?q'<+ end=+>'+
+ syn region plsqlStringLiteral matchgroup=plsqlOperator start=+n\?q'{+ end=+}'+
+ syn region plsqlStringLiteral matchgroup=plsqlOperator start=+n\?q'(+ end=+)'+
+ syn region plsqlStringLiteral matchgroup=plsqlOperator start=+n\?q'\[+ end=+]'+
+endif
+
+syn keyword plsqlBooleanLiteral TRUE FALSE
" A type-attribute is really a type.
syn match plsqlTypeAttribute "%\(TYPE\|ROWTYPE\)\>"
@@ -204,61 +638,183 @@ syn match plsqlAttribute "%\(BULK_EXCEPTIONS\|BULK_ROWCOUNT\|ISOPEN\|FOUND\|NOTF
" This'll catch mis-matched close-parens.
syn cluster plsqlParenGroup contains=plsqlParenError,@plsqlCommentGroup,plsqlCommentSkip,plsqlIntLiteral,plsqlFloatLiteral,plsqlNumbersCom
-if exists("c_no_bracket_error")
- syn region plsqlParen transparent start='(' end=')' contains=ALLBUT,@plsqlParenGroup
- syn match plsqlParenError ")"
- syn match plsqlErrInParen contained "[{}]"
+
+if exists("plsql_bracket_error")
+ " I suspect this code was copied from c.vim and never properly considered. Do
+ " we even use braces or brackets in sql or pl/sql?
+ if exists("plsql_fold")
+ syn region plsqlParen start='(' end=')' contains=ALLBUT,@plsqlParenGroup,plsqlErrInBracket fold keepend extend transparent
+ else
+ syn region plsqlParen transparent start='(' end=')' contains=ALLBUT,@plsqlParenGroup,plsqlErrInBracket
+ endif
+ syn match plsqlParenError "[\])]"
+ syn match plsqlErrInParen contained "[{}]"
+ syn region plsqlBracket transparent start='\[' end=']' contains=ALLBUT,@plsqlParenGroup,plsqlErrInParen
+ syn match plsqlErrInBracket contained "[);{}]"
else
- syn region plsqlParen transparent start='(' end=')' contains=ALLBUT,@plsqlParenGroup,plsqlErrInBracket
- syn match plsqlParenError "[\])]"
- syn match plsqlErrInParen contained "[{}]"
- syn region plsqlBracket transparent start='\[' end=']' contains=ALLBUT,@plsqlParenGroup,plsqlErrInParen
- syn match plsqlErrInBracket contained "[);{}]"
+ if exists("plsql_fold")
+ syn region plsqlParen start='(' end=')' contains=ALLBUT,@plsqlParenGroup,plsqlErrInParen fold keepend extend transparent
+ else
+ syn region plsqlParen transparent start='(' end=')' contains=ALLBUT,@plsqlParenGroup,plsqlErrInParen
+ endif
+ syn match plsqlParenError ")"
+ " should this not be parens isntead of brackets? I never quite followed what this was doing
+ syn match plsqlErrInParen contained "[{}]"
endif
-" Syntax Synchronizing
-syn sync minlines=10 maxlines=100
+syn match plsqlReserved "\<BODY\>"
+syn match plsqlReserved "\<CREATE\_s\+\(OR\_s\+REPLACE\_s\+\)\?"
+" Loops
+syn match plsqlRepeat "\<\(FOR\|WHILE\|LOOP\|FORALL\)\>"
+syn match plsqlRepeat "\<END\_s\+LOOP\>"
+" conditionals
+syn match plsqlConditional "\<\(ELSIF\|IF\|ELSE\)\>"
+syn match plsqlConditional "\<END\>\_s\+\<IF\>"
+syn match plsqlCase "\<END\>\_s\+\<CASE\>"
+syn match plsqlCase "\<CASE\>"
+
+if exists("plsql_fold")
+ setlocal foldmethod=syntax
+ syn sync fromstart
+
+ syn cluster plsqlProcedureGroup contains=plsqlProcedure
+ syn cluster plsqlOnlyGroup contains=@plsqlProcedure,plsqlConditionalBlock,plsqlLoopBlock,plsqlBlock
+
+ syntax region plsqlUpdateSet
+ \ start="\(\<update\>\_s\+\(\<set\>\)\@![a-z][a-z0-9$_#]*\_s\+\(\(\<set\>\)\@![a-z][a-z0-9$_#]*\_s\+\)\?\)\|\(\<when\>\_s\+\<matched\>\_s\+\<then\>\_s\+\<update\>\_s\+\)\<set\>"
+ \ end="\(\_s*\(;\|\<from\>\|\<where\>\|\<when\>\)\)\@="
+ \ fold
+ \ keepend
+ \ extend
+ \ transparent
+ \ contains=ALLBUT,@plsqlOnlyGroup,plsqlSelect
+
+ syntax region plsqlSelect
+ \ start="\<select\>"
+ \ end="\(\_s*\<from\>\)\@="
+ \ fold
+ \ keepend
+ \ extend
+ \ transparent
+ \ contains=ALLBUT,@plsqlOnlyGroup,plsqlUpdateSet
+
+ " this is brute force and requires you have the procedure/function name in the END
+ " statement. ALthough Oracle makes it optional, we cannot. If you do not
+ " have it, then you can fold the BEGIN/END block of the procedure but not
+ " the specification of it (other than a paren group). You also cannot fold
+ " BEGIN/END blocks in the procedure body. Local procedures will fold as
+ " long as the END statement includes the procedure/function name.
+ " As for why we cannot make it work any other way, I don't know. It is
+ " something to do with both plsqlBlock and plsqlProcedure both consuming BEGIN and END,
+ " even if we use a lookahead for one of them.
+ syntax region plsqlProcedure
+ "\ start="\(create\(\_s\+or\_s\+replace\)\?\_s\+\)\?\<\(procedure\|function\)\>\_s\+\z([a-z][a-z0-9$_#]*\)"
+ \ start="\<\(procedure\|function\)\>\_s\+\(\z([a-z][a-z0-9$_#]*\)\)\([^;]\|\n\)\{-}\<\(is\|as\)\>\_.\{-}\(\<end\>\_s\+\2\_s*;\)\@="
+ \ end="\<end\>\_s\+\z1\_s*;"
+ \ fold
+ \ keepend
+ \ extend
+ \ transparent
+ \ contains=ALLBUT,plsqlBlock
+ syntax region plsqlBlock
+ \ start="\<begin\>"
+ "\ end="\<end\>\_s*\;"
+ \ end="\<end\>\(\_s\+\(if\|loop\|case\)\@![a-z][a-z0-9$_#]*\)\?\_s*;"
+ \ fold
+ \ transparent
+ \ contains=ALLBUT,@plsqlProcedureGroup,plsqlPackage,plsqlErrInBracket,PlsqlProcedureJava
+ \ keepend
+ \ extend
+
+ syn region plsqlCaseBlock
+ \ transparent
+ \ start="\<case\>\(\_s*;\)\@!"
+ \ end="\<end\>\(\_s\+case\_s*;\)\?"
+ \ fold
+ \ contains=ALLBUT,@plsqlProcedureGroup,plsqlPackage,plsqlErrInBracket,PlsqlProcedureJava
+ \ keepend
+ \ extend
+ "\ contained
+
+ syntax region plsqlLoopBlock
+ \ transparent
+ \ start="\<loop\>\(\_s*;\)\@!"
+ \ end="\<end\>\(\_s\+\<loop\>\)\?\_s*;"
+ \ fold
+ \ keepend extend
+ \ contained
+ \ contains=ALLBUT,@plsqlProcedureGroup,plsqlPackage,plsqlErrInBracket,PlsqlProcedureJava
+
+ syn region plsqlConditionalBlock
+ \ transparent
+ \ start="\<if\>\(\_s*;\)\@!"
+ \ end="\<end\>\_s\+\<if\>\_s*;"
+ \ fold
+ \ keepend extend
+ \ contained
+ \ contains=ALLBUT,@plsqlProcedureGroup,plsqlPackage,plsqlErrInBracket,PlsqlProcedureJava
+
+else
+ " Syntax Synchronizing
+ syn sync minlines=1000 maxlines=2000
+endif
+"
" Define the default highlighting.
" Only when an item doesn't have highlighting yet.
-hi def link plsqlAttribute Macro
-hi def link plsqlBlockError Error
-hi def link plsqlBooleanLiteral Boolean
-hi def link plsqlCharLiteral Character
-hi def link plsqlComment Comment
-hi def link plsqlCommentL Comment
-hi def link plsqlConditional Conditional
-hi def link plsqlError Error
-hi def link plsqlErrInBracket Error
-hi def link plsqlErrInBlock Error
-hi def link plsqlErrInParen Error
-hi def link plsqlException Function
-hi def link plsqlFloatLiteral Float
-hi def link plsqlFunction Function
-hi def link plsqlGarbage Error
-hi def link plsqlHostIdentifier Label
-hi def link plsqlIdentifier Normal
-hi def link plsqlIntLiteral Number
-hi def link plsqlOperator Operator
-hi def link plsqlParen Normal
-hi def link plsqlParenError Error
-hi def link plsqlSpaceError Error
-hi def link plsqlPseudo PreProc
-hi def link plsqlKeyword Keyword
-hi def link plsqlRepeat Repeat
-hi def link plsqlStorage StorageClass
-hi def link plsqlSQLKeyword Function
-hi def link plsqlStringError Error
-hi def link plsqlStringLiteral String
-hi def link plsqlCommentString String
-hi def link plsqlComment2String String
-hi def link plsqlSymbol Normal
-hi def link plsqlTrigger Function
-hi def link plsqlTypeAttribute StorageClass
-hi def link plsqlTodo Todo
-
+hi def link plsqlAttribute Macro
+hi def link plsqlBlockError Error
+hi def link plsqlBooleanLiteral Boolean
+hi def link plsqlQuotedIdentifier Character
+hi def link plsqlComment Comment
+hi def link plsqlCommentL Comment
+hi def link plsqlConditional Conditional
+hi def link plsqlCase Conditional
+hi def link plsqlError Error
+hi def link plsqlErrInBracket Error
+hi def link plsqlErrInBlock Error
+hi def link plsqlErrInParen Error
+hi def link plsqlException Function
+hi def link plsqlFloatLiteral Float
+hi def link plsqlFunction Function
+hi def link plsqlGarbage Error
+hi def link plsqlHostIdentifier Label
+hi def link plsqlIdentifier Normal
+hi def link plsqlIntLiteral Number
+hi def link plsqlOperator Operator
+hi def link plsqlParenError Error
+hi def link plsqlSpaceError Error
+hi def link plsqlPseudo PreProc
+hi def link plsqlKeyword Keyword
+hi def link plsqlEND Keyword
+hi def link plsqlBEGIN Keyword
+hi def link plsqlISAS Statement
+hi def link plsqlReserved Statement
+hi def link plsqlRepeat Repeat
+hi def link plsqlStorage StorageClass
+hi def link plsqlFunction Function
+hi def link plsqlStringError Error
+hi def link plsqlStringLiteral String
+hi def link plsqlCommentString String
+hi def link plsqlComment2String String
+hi def link plsqlTrigger Function
+hi def link plsqlTypeAttribute StorageClass
+hi def link plsqlTodo Todo
+" to be able to change them after loading, need override whether defined or not
+if exists("plsql_legacy_sql_keywords")
+ hi link plsqlSQLKeyword Function
+ hi link plsqlSymbol Normal
+ hi link plsqlParen Normal
+else
+ hi link plsqlSymbol Special
+ hi link plsqlParen Special
+endif
let b:current_syntax = "plsql"
-" vim: ts=8 sw=2
+" restore setting from when we entered this file
+let &cpo = s:cpo_sav
+unlet! s:cpo_sav
+
+" vim: ts=4 sw=4
diff --git a/runtime/syntax/pod.vim b/runtime/syntax/pod.vim
index 604713260b..90c9b99a3e 100644
--- a/runtime/syntax/pod.vim
+++ b/runtime/syntax/pod.vim
@@ -2,9 +2,10 @@
" Language: Perl POD format
" Maintainer: vim-perl <vim-perl@googlegroups.com>
" Previously: Scott Bigham <dsb@killerbunnies.org>
-" Homepage: http://github.com/vim-perl/vim-perl
-" Bugs/requests: http://github.com/vim-perl/vim-perl/issues
-" Last Change: 2017-09-12
+" Homepage: https://github.com/vim-perl/vim-perl
+" Bugs/requests: https://github.com/vim-perl/vim-perl/issues
+" License: Vim License (see :help license)
+" Last Change: 2022 Jun 13
" To add embedded POD documentation highlighting to your syntax file, add
" the commands:
@@ -27,71 +28,117 @@ endif
let s:cpo_save = &cpo
set cpo&vim
-" POD commands
-syn match podCommand "^=encoding" nextgroup=podCmdText contains=@NoSpell
-syn match podCommand "^=head[1234]" nextgroup=podCmdText contains=@NoSpell
-syn match podCommand "^=item" nextgroup=podCmdText contains=@NoSpell
-syn match podCommand "^=over" nextgroup=podOverIndent skipwhite contains=@NoSpell
-syn match podCommand "^=back" contains=@NoSpell
-syn match podCommand "^=cut" contains=@NoSpell
-syn match podCommand "^=pod" contains=@NoSpell
-syn match podCommand "^=for" nextgroup=podForKeywd skipwhite contains=@NoSpell
-syn match podCommand "^=begin" nextgroup=podForKeywd skipwhite contains=@NoSpell
-syn match podCommand "^=end" nextgroup=podForKeywd skipwhite contains=@NoSpell
+" TODO: add supported encodings when we can utilize better performing Vim 8 features
+syn match podEncoding "[0-9A-Za-z_-]\+" contained contains=@NoSpell
" Text of a =head1, =head2 or =item command
-syn match podCmdText ".*$" contained contains=podFormat,@NoSpell
+syn region podCmdText start="\S.*$" end="^\ze\s*$" end="^\ze=cut\>" contained contains=podFormat,@NoSpell
" Indent amount of =over command
-syn match podOverIndent "\d\+" contained contains=@NoSpell
+syn match podOverIndent "\d*\.\=\d\+\>" contained contains=@NoSpell
" Formatter identifier keyword for =for, =begin and =end commands
syn match podForKeywd "\S\+" contained contains=@NoSpell
" An indented line, to be displayed verbatim
-syn match podVerbatimLine "^\s.*$" contains=@NoSpell
+syn region podVerbatim start="^\s\+\S.*$" end="^\ze\s*$" end="^\ze=cut\>" contains=@NoSpell
+
+syn region podOrdinary start="^\S.*$" end="^\ze\s*$" end="^\ze=cut\>" contains=podFormat,podSpecial,@Spell
" Inline textual items handled specially by POD
syn match podSpecial "\(\<\|&\)\I\i*\(::\I\i*\)*([^)]*)" contains=@NoSpell
syn match podSpecial "[$@%]\I\i*\(::\I\i*\)*\>" contains=@NoSpell
" Special formatting sequences
-syn region podFormat start="[IBSCLFX]<[^<]"me=e-1 end=">" oneline contains=podFormat,@NoSpell
-syn region podFormat start="[IBSCLFX]<<\s" end="\s>>" oneline contains=podFormat,@NoSpell
+
+syn cluster podFormat contains=podFormat,podFormatError
+
+syn match podFormatError "[ADGHJKM-RT-WY]<"
+
+syn region podFormat matchgroup=podFormatDelimiter start="[IBSCLFX]<" end=">" contains=@podFormat,@NoSpell
+syn region podFormat matchgroup=podFormatDelimiter start="[IBSCLFX]<<\%(\s\+\|$\)" end="\%(\s\+\|^\)>>" contains=@podFormat,@NoSpell
+
syn match podFormat "Z<>"
-syn match podFormat "E<\(\d\+\|\I\i*\)>" contains=podEscape,podEscape2,@NoSpell
-syn match podEscape "\I\i*>"me=e-1 contained contains=@NoSpell
-syn match podEscape2 "\d\+>"me=e-1 contained contains=@NoSpell
+
+syn region podFormat matchgroup=podFormatDelimiter start="E<" end=">" oneline contains=podEscape,podEscape2,@NoSpell
+
+" HTML entities {{{1
+" Source: Pod/Escapes.pm
+syn keyword podEscape contained lt gt quot amp apos sol verbar lchevron rchevron nbsp iexcl cent pound curren yen brvbar sect uml copy ordf laquo not shy reg macr deg plusmn sup2 sup3 acute micro para middot cedil sup1 ordm raquo frac14 frac12 frac34 iquest Agrave Aacute Acirc Atilde Auml Aring AElig Ccedil Egrave Eacute Ecirc Euml Igrave Iacute Icirc Iuml ETH Ntilde Ograve Oacute Ocirc Otilde Ouml times Oslash Ugrave Uacute Ucirc Uuml Yacute THORN szlig agrave aacute acirc atilde auml aring aelig ccedil egrave eacute ecirc euml igrave iacute icirc iuml eth ntilde ograve oacute ocirc otilde ouml divide oslash ugrave uacute ucirc uuml yacute thorn yuml fnof Alpha Beta Gamma Delta Epsilon Zeta Eta Theta Iota Kappa Lambda Mu Nu Xi Omicron Pi Rho Sigma Tau Upsilon Phi Chi Psi Omega alpha beta gamma delta epsilon zeta eta theta iota kappa lambda mu nu xi omicron pi rho sigmaf sigma tau upsilon phi chi psi omega thetasym upsih piv bull hellip prime Prime oline frasl weierp image real trade alefsym larr uarr rarr darr harr crarr lArr uArr rArr dArr hArr forall part exist empty nabla isin notin ni prod sum minus lowast radic prop infin ang and or cap cup int there4 sim cong asymp ne equiv le ge sub sup nsub sube supe oplus otimes perp sdot lceil rceil lfloor rfloor lang rang loz spades clubs hearts diams OElig oelig Scaron scaron Yuml circ tilde ensp emsp thinsp zwnj zwj lrm rlm ndash mdash lsquo rsquo sbquo ldquo rdquo bdquo dagger Dagger permil lsaquo rsaquo
+" }}}
+
+syn match podEscape2 "\d\+" contained contains=@NoSpell
+syn match podEscape2 "0\=x\x\+" contained contains=@NoSpell
+syn match podEscape2 "0\o\+" contained contains=@NoSpell
+
+
+" POD commands
+syn match podCommand "^=encoding\>" nextgroup=podEncoding skipwhite contains=@NoSpell
+syn match podCommand "^=head[1234]\>" nextgroup=podCmdText skipwhite skipnl contains=@NoSpell
+syn match podCommand "^=item\>" nextgroup=podCmdText skipwhite skipnl contains=@NoSpell
+syn match podCommand "^=over\>" nextgroup=podOverIndent skipwhite contains=@NoSpell
+syn match podCommand "^=back" contains=@NoSpell
+syn match podCommand "^=cut" contains=@NoSpell
+syn match podCommand "^=pod" contains=@NoSpell
+syn match podCommand "^=for" nextgroup=podForKeywd skipwhite contains=@NoSpell
+syn match podCommand "^=begin" nextgroup=podForKeywd skipwhite contains=@NoSpell
+syn match podCommand "^=end" nextgroup=podForKeywd skipwhite contains=@NoSpell
+
+" Comments
+
+syn keyword podForKeywd comment contained nextgroup=podForComment skipwhite skipnl
+
+if exists("perl_pod_no_comment_fold")
+ syn region podBeginComment start="^=begin\s\+comment\s*$" end="^=end\s\+comment\ze\s*$" keepend extend contains=podCommand
+ syn region podForComment start="\S.*$" end="^\ze\s*$" end="^\ze=cut\>" contained contains=@Spell,podTodo
+else
+ syn region podBeginComment start="^=begin\s\+comment\s*$" end="^=end\s\+comment\ze\s*$" keepend extend contains=podCommand,podTodo fold
+ syn region podForComment start="\S.*$" end="^\ze\s*$" end="^\ze=cut\>" contained contains=@Spell,podTodo fold
+endif
+
+syn keyword podTodo contained TODO FIXME XXX
+
+" Plain Pod files
+syn region podNonPod start="\%^\%(=\w\+\>\)\@!" end="^\ze=\a\w*\>"
+syn region podNonPod matchgroup=podCommand start="^=cut\>" end="\%$"
+syn region podNonPod matchgroup=podCommand start="^=cut\>" end="^\ze=\a\w*\>"
" Define the default highlighting.
" Only when an item doesn't have highlighting yet
hi def link podCommand Statement
+hi def link podBeginComment Comment
+hi def link podForComment Comment
+hi def link podNonPod Comment
+hi def link podTodo Todo
hi def link podCmdText String
+hi def link podEncoding Constant
hi def link podOverIndent Number
hi def link podForKeywd Identifier
+hi def link podVerbatim PreProc
hi def link podFormat Identifier
-hi def link podVerbatimLine PreProc
+hi def link podFormatDelimiter podFormat
+hi def link podFormatError Error
hi def link podSpecial Identifier
-hi def link podEscape String
+hi def link podEscape Constant
hi def link podEscape2 Number
if exists("perl_pod_spellcheck_headings")
" Spell-check headings
syn clear podCmdText
- syn match podCmdText ".*$" contained contains=podFormat
+ syn region podCmdText start="\S.*$" end="^\s*$" end="^\ze=cut\>" contained contains=podFormat
endif
if exists("perl_pod_formatting")
" By default, escapes like C<> are not checked for spelling. Remove B<>
" and I<> from the list of escapes.
syn clear podFormat
- syn region podFormat start="[CLF]<[^<]"me=e-1 end=">" oneline contains=podFormat,@NoSpell
- syn region podFormat start="[CLF]<<\s" end="\s>>" oneline contains=podFormat,@NoSpell
+ syn region podFormat start="[CLF]<[^<]"me=e-1 end=">" contains=@podFormat,@NoSpell
+ syn region podFormat start="[CLF]<<\%(\s\+\|$\)" end="\%(\s\+\|^\)>>" contains=@podFormat,@NoSpell
" Don't spell-check inside E<>, but ensure that the E< itself isn't
" marked as a spelling mistake.
- syn match podFormat "E<\(\d\+\|\I\i*\)>" contains=podEscape,podEscape2,@NoSpell
+ syn region podFormat start="E<" end=">" oneline contains=podEscape,podEscape2,@NoSpell
" Z<> is a mock formatting code. Ensure Z<> on its own isn't marked as a
" spelling mistake.
@@ -105,41 +152,41 @@ if exists("perl_pod_formatting")
syn match podIndexOpen "X<" contains=@NoSpell
" Same as above but for the << >> syntax.
- syn match podBoldAlternativeDelimOpen "B<< " contains=@NoSpell
- syn match podItalicAlternativeDelimOpen "I<< " contains=@NoSpell
- syn match podNoSpaceAlternativeDelimOpen "S<< " contains=@NoSpell
- syn match podIndexAlternativeDelimOpen "X<< " contains=@NoSpell
+ syn match podBoldAlternativeDelimOpen "B<<\%(\s\+\|$\)" contains=@NoSpell
+ syn match podItalicAlternativeDelimOpen "I<<\%(\s\+\|$\)" contains=@NoSpell
+ syn match podNoSpaceAlternativeDelimOpen "S<<\%(\s\+\|$\)" contains=@NoSpell
+ syn match podIndexAlternativeDelimOpen "X<<\%(\s\+\|$\)" contains=@NoSpell
" Add support for spell checking text inside B<>, I<>, S<> and X<>.
- syn region podBold start="B<[^<]"me=e end=">" oneline contains=podBoldItalic,podBoldOpen
- syn region podBoldAlternativeDelim start="B<<\s" end="\s>>" oneline contains=podBoldAlternativeDelimOpen
+ syn region podBold start="B<[^<]"me=e end=">" contains=podBoldItalic,podBoldOpen
+ syn region podBoldAlternativeDelim start="B<<\%(\s\+\|$\)" end="\%(\s\+\|^\)>>" contains=podBoldAlternativeDelimOpen
- syn region podItalic start="I<[^<]"me=e end=">" oneline contains=podItalicBold,podItalicOpen
- syn region podItalicAlternativeDelim start="I<<\s" end="\s>>" oneline contains=podItalicAlternativeDelimOpen
+ syn region podItalic start="I<[^<]"me=e end=">" contains=podItalicBold,podItalicOpen
+ syn region podItalicAlternativeDelim start="I<<\%(\s\+\|$\)" end="\%(\s\+\|^\)>>" contains=podItalicAlternativeDelimOpen
" Nested bold/italic and vice-versa
- syn region podBoldItalic contained start="I<[^<]"me=e end=">" oneline
- syn region podItalicBold contained start="B<[^<]"me=e end=">" oneline
+ syn region podBoldItalic contained start="I<[^<]"me=e end=">"
+ syn region podItalicBold contained start="B<[^<]"me=e end=">"
- syn region podNoSpace start="S<[^<]"ms=s-2 end=">"me=e oneline contains=podNoSpaceOpen
- syn region podNoSpaceAlternativeDelim start="S<<\s"ms=s-2 end="\s>>"me=e oneline contains=podNoSpaceAlternativeDelimOpen
+ syn region podNoSpace start="S<[^<]"ms=s-2 end=">"me=e contains=podNoSpaceOpen
+ syn region podNoSpaceAlternativeDelim start="S<<\%(\s\+\|$\)"ms=s-2 end="\%(\s\+\|^\)>>"me=e contains=podNoSpaceAlternativeDelimOpen
- syn region podIndex start="X<[^<]"ms=s-2 end=">"me=e oneline contains=podIndexOpen
- syn region podIndexAlternativeDelim start="X<<\s"ms=s-2 end="\s>>"me=e oneline contains=podIndexAlternativeDelimOpen
+ syn region podIndex start="X<[^<]"ms=s-2 end=">"me=e contains=podIndexOpen
+ syn region podIndexAlternativeDelim start="X<<\%(\s\+\|$\)"ms=s-2 end="\%(\s\+\|^\)>>"me=e contains=podIndexAlternativeDelimOpen
" Restore this (otherwise B<> is shown as bold inside verbatim)
- syn match podVerbatimLine "^\s.*$" contains=@NoSpell
+ syn region podVerbatim start="^\s\+\S.*$" end="^\ze\s*$" end="^\ze=cut\>" contains=@NoSpell
" Ensure formatted text can be displayed in headings and items
syn clear podCmdText
if exists("perl_pod_spellcheck_headings")
- syn match podCmdText ".*$" contained contains=podFormat,podBold,
+ syn match podCmdText ".*$" contained contains=@podFormat,podBold,
\podBoldAlternativeDelim,podItalic,podItalicAlternativeDelim,
\podBoldOpen,podItalicOpen,podBoldAlternativeDelimOpen,
\podItalicAlternativeDelimOpen,podNoSpaceOpen
else
- syn match podCmdText ".*$" contained contains=podFormat,podBold,
+ syn match podCmdText ".*$" contained contains=@podFormat,podBold,
\podBoldAlternativeDelim,podItalic,podItalicAlternativeDelim,
\@NoSpell
endif
@@ -172,4 +219,4 @@ let b:current_syntax = "pod"
let &cpo = s:cpo_save
unlet s:cpo_save
-" vim: ts=8
+" vim: ts=8 fdm=marker:
diff --git a/runtime/syntax/qb64.vim b/runtime/syntax/qb64.vim
new file mode 100644
index 0000000000..a777e14481
--- /dev/null
+++ b/runtime/syntax/qb64.vim
@@ -0,0 +1,409 @@
+" Vim syntax file
+" Language: QB64
+" Maintainer: Doug Kearns <dougkearns@gmail.com>
+" Last Change: 2022 Jan 21
+
+" Prelude {{{1
+if exists("b:current_syntax")
+ finish
+endif
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+" syn iskeyword set after sourcing of basic.vim
+
+syn case ignore
+
+let s:prefix = search('\c^\s*$NOPREFIX\>', 'n') ? '_\=' : '_'
+
+" Statements {{{1
+
+let s:statements =<< trim EOL " {{{2
+ acceptfiledrop
+ allowfullscreen
+ assert
+ console
+ consolecursor
+ consolefont
+ consoletitle
+ continue
+ copypalette
+ define
+ delay
+ depthbuffer
+ displayorder
+ dontblend
+ echo
+ exit\s\+\%(select\|case\)
+ finishdrop
+ freefont
+ freeimage
+ icon
+ keyclear
+ limit
+ maptriangle
+ memcopy
+ memfill
+ memfree
+ memput
+ mousehide
+ mousemove
+ mouseshow
+ printimage
+ printstring
+ putimage
+ screenclick
+ screenhide
+ screenmove
+ screenprint
+ screenshow
+ setalpha
+ sndbal
+ sndclose
+ sndlimit
+ sndloop
+ sndpause
+ sndplay
+ sndplaycopy
+ sndplayfile
+ sndraw
+ sndrawdone
+ sndsetpos
+ sndstop
+ sndvol
+ title
+EOL
+" }}}
+
+for s in s:statements
+ exe 'syn match qb64Statement "\<' .. s:prefix .. s .. '\>" contained contains=qb64Underscore'
+endfor
+
+" Functions {{{1
+
+let s:functions =<< trim EOL " {{{2
+ acos
+ acosh
+ alpha
+ alpha32
+ arccot
+ arccsc
+ arcsec
+ asin
+ asinh
+ atan2
+ atanh
+ axis
+ backgroundcolor
+ blue
+ blue32
+ button
+ buttonchange
+ ceil
+ cinp
+ commandcount
+ connected
+ connectionaddress
+ connectionaddress$
+ consoleinput
+ copyimage
+ cot
+ coth
+ cosh
+ csc
+ csch
+ cv
+ cwd$
+ d2g
+ d2r
+ defaultcolor
+ deflate$
+ desktopheight
+ desktopwidth
+ device$
+ deviceinput
+ devices
+ dir$
+ direxists
+ droppedfile
+ droppedfile$
+ errorline
+ errormessage$
+ exit
+ fileexists
+ fontheight
+ fontwidth
+ freetimer
+ g2d
+ g2r
+ green
+ green32
+ height
+ hypot
+ inclerrorfile$
+ inclerrorline
+ inflate$
+ instrrev
+ keyhit
+ keydown
+ lastaxis
+ lastbutton
+ lastwheel
+ loadfont
+ loadimage
+ mem
+ memelement
+ memexists
+ memimage
+ memnew
+ memsound
+ mk$
+ mousebutton
+ mouseinput
+ mousemovementx
+ mousemovementy
+ mousepipeopen
+ mousewheel
+ mousex
+ mousey
+ newimage
+ offset
+ openclient
+ os$
+ pi
+ pixelsize
+ printwidth
+ r2d
+ r2g
+ red
+ red32
+ readbit
+ resetbit
+ resizeheight
+ resizewidth
+ rgb
+ rgb32
+ rgba
+ rgba32
+ round
+ sec
+ sech
+ screenexists
+ screenimage
+ screenx
+ screeny
+ setbit
+ shellhide
+ shl
+ shr
+ sinh
+ sndcopy
+ sndgetpos
+ sndlen
+ sndopen
+ sndopenraw
+ sndpaused
+ sndplaying
+ sndrate
+ sndrawlen
+ startdir$
+ strcmp
+ stricmp
+ tanh
+ title$
+ togglebit
+ totaldroppedfiles
+ trim$
+ wheel
+ width
+ windowhandle
+ windowhasfocus
+EOL
+" }}}
+
+for f in s:functions
+ exe 'syn match qb64Function "\<' .. s:prefix .. f .. '\>" contains=qb64Underscore'
+endfor
+
+" Functions and statements (same name) {{{1
+
+let s:common =<< trim EOL " {{{2
+ autodisplay
+ blend
+ blink
+ capslock
+ clearcolor
+ clipboard$
+ clipboardimage
+ controlchr
+ dest
+ display
+ font
+ fullscreen
+ mapunicode
+ memget
+ numlock
+ palettecolor
+ printmode
+ resize
+ screenicon
+ scrolllock
+ source
+EOL
+" }}}
+
+for c in s:common
+ exe 'syn match qb64Statement "\<' .. s:prefix .. c .. '\>" contains=qb64Underscore contained'
+ exe 'syn match qb64Function "\<' .. s:prefix .. c .. '\>" contains=qb64Underscore'
+endfor
+
+" Keywords {{{1
+
+" Non-prefixed keywords {{{2
+" TIMER FREE
+" _DEPTH_BUFFER LOCK
+syn keyword qb64Keyword free lock
+
+let s:keywords =<< trim EOL " {{{2
+ all
+ anticlockwise
+ behind
+ clear
+ clip
+ console
+ dontwait
+ explicit
+ explicitarray
+ fillbackground
+ hardware
+ hardware1
+ hide
+ keepbackground
+ middle
+ none
+ off
+ only
+ onlybackground
+ ontop
+ openconnection
+ openhost
+ preserve
+ seamless
+ smooth
+ smoothshrunk
+ smoothstretched
+ software
+ squarepixels
+ stretch
+ toggle
+EOL
+" }}}
+
+for k in s:keywords
+ exe 'syn match qb64Keyword "\<' .. s:prefix .. k .. '\>" contains=qb64Underscore'
+endfor
+
+syn match qb64Underscore "\<_" contained conceal transparent
+
+" Source QuickBASIC syntax {{{1
+runtime! syntax/basic.vim
+
+" add after the BASIC syntax file is sourced so cluster already exists
+syn cluster basicStatements add=qb64Statement,qb64Metacommand,qb64IfMetacommand
+syn cluster basicLineIdentifier add=qb64LineLabel
+syn cluster qb64NotTop contains=@basicNotTop,qb64Metavariable
+
+syn iskeyword @,48-57,.,_,!,#,$,%,&,`
+
+" Unsupported QuickBASIC features {{{1
+" TODO: add linux only missing features
+syn keyword qb64Unsupported alias any byval calls cdecl erdev erdev$ fileattr
+syn keyword qb64Unsupported fre ioctl ioctl$ pen play setmem signal uevent
+syn keyword qb64Unsupported tron troff
+syn match qb64Unsupported "\<declare\%(\s\+\%(sub\|function\)\>\)\@="
+syn match qb64Unsupported "\<\%(date\|time\)$\ze\s*=" " statements only
+syn match qb64Unsupported "\<def\zs\s\+FN"
+syn match qb64Unsupported "\<\%(exit\|end\)\s\+def\>"
+syn match qb64Unsupported "\<width\s\+lprint\>"
+
+" Types {{{1
+syn keyword qb64Type _BIT _BYTE _FLOAT _INTEGER64 _MEM _OFFSET _UNSIGNED
+
+" Type suffixes {{{1
+if exists("basic_type_suffixes")
+ " TODO: handle leading word boundary and __+ prefix
+ syn match qb64TypeSuffix "\%(\a[[:alnum:]._]*\)\@<=\~\=`\%(\d\+\)\="
+ syn match qb64TypeSuffix "\%(\a[[:alnum:]._]*\)\@<=\~\=\%(%\|%%\|&\|&&\|%&\)"
+ syn match qb64TypeSuffix "\%(\a[[:alnum:]._]*\)\@<=\%(!\|##\|#\)"
+ syn match qb64TypeSuffix "\%(\a[[:alnum:]._]*\)\@<=$\%(\d\+\)\="
+endif
+
+" Numbers {{{1
+
+" Integers
+syn match qb64Number "-\=&b[01]\+&\>\="
+
+syn match qb64Number "-\=\<[01]\~\=`\>"
+syn match qb64Number "-\=\<\d\+`\d\+\>"
+
+syn match qb64Number "-\=\<\d\+\%(%%\|&&\|%&\)\>"
+syn match qb64Number "\<\d\+\~\%(%%\|&&\|%&\)\>"
+
+syn match qb64Number "-\=\<&b[01]\+\%(%%\|&&\|%&\)\>"
+syn match qb64Number "\<&b[01]\+\~\%(%%\|&&\|%&\)\>"
+
+syn match qb64Number "-\=\<&o\=\o\+\%(%%\|&&\|%&\)\>"
+syn match qb64Number "\<&o\=\o\+\~\%(%%\|&&\|%&\)\>"
+
+syn match qb64Number "-\=\<&h\x\+\%(%%\|&&\|%&\)\>"
+syn match qb64Number "\<&h\x\+\~\%(%%\|&&\|%&\)\>"
+
+" Floats
+syn match qb64Float "-\=\<\d\+\.\=\d*##\>"
+syn match qb64Float "-\=\<\.\d\+##\>"
+
+" Line numbers and labels {{{1
+syn match qb64LineLabel "\%(_\{2,}\)\=\a[[:alnum:]._]*[[:alnum:]]\ze\s*:" nextgroup=@basicStatements skipwhite contained
+
+" Metacommands {{{1
+syn match qb64Metacommand contained "$NOPREFIX\>"
+syn match qb64Metacommand contained "$ASSERTS\%(:CONSOLE\)\=\>"
+syn match qb64Metacommand contained "$CHECKING:\%(ON\|OFF\)\>"
+syn match qb64Metacommand contained "$COLOR:\%(0\|32\)\>"
+syn match qb64Metacommand contained "$CONSOLE\%(:ONLY\)\=\>"
+syn match qb64Metacommand contained "$EXEICON\s*:\s*'[^']\+'"
+syn match qb64Metacommand contained "$ERROR\>"
+syn match qb64Metacommand contained "$LET\>"
+syn match qb64Metacommand contained "$RESIZE:\%(ON\|OFF\|STRETCH\|SMOOTH\)\>"
+syn match qb64Metacommand contained "$SCREEN\%(HIDE\|SHOW\)\>"
+syn match qb64Metacommand contained "$VERSIONINFO\s*:.*"
+syn match qb64Metacommand contained "$VIRTUALKEYBOARD:\%(ON\|OFF\)\>"
+
+syn region qb64IfMetacommand contained matchgroup=qb64Metacommand start="$\%(IF\|ELSEIF\)\>" end="\<THEN\>" oneline transparent contains=qb64Metavariable
+syn match qb64Metacommand contained "$\%(ELSE\|END\s*IF\)\>"
+
+syn keyword qb64Metavariable contained defined undefined
+syn keyword qb64Metavariable contained windows win linux mac maxosx
+syn keyword qb64Metavariable contained 32bit 64bit version
+
+" Default Highlighting {{{1
+hi def link qb64Float basicFloat
+hi def link qb64Function Function
+hi def link qb64Keyword Keyword
+hi def link qb64LineLabel basicLineLabel
+hi def link qb64Metacommand PreProc
+hi def link qb64Metavariable Identifier
+hi def link qb64Number basicNumber
+hi def link qb64Statement Statement
+hi def link qb64TypeSuffix basicTypeSuffix
+hi def link qb64Type Type
+hi def link qb64Unsupported Error
+
+" Postscript {{{1
+let b:current_syntax = "qb64"
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
+
+" vim: nowrap sw=2 sts=2 ts=8 noet fdm=marker:
diff --git a/runtime/syntax/query.lua b/runtime/syntax/query.lua
new file mode 100644
index 0000000000..8f40b1cb8a
--- /dev/null
+++ b/runtime/syntax/query.lua
@@ -0,0 +1,6 @@
+-- Neovim syntax file
+-- Language: Tree-sitter query
+-- Last Change: 2022 Apr 13
+
+-- it's a lisp!
+vim.cmd([[ runtime! syntax/lisp.vim ]])
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/ruby.vim b/runtime/syntax/ruby.vim
index 13d6d9efd8..c951fcfe1d 100644
--- a/runtime/syntax/ruby.vim
+++ b/runtime/syntax/ruby.vim
@@ -3,7 +3,7 @@
" Maintainer: Doug Kearns <dougkearns@gmail.com>
" URL: https://github.com/vim-ruby/vim-ruby
" Release Coordinator: Doug Kearns <dougkearns@gmail.com>
-" Last Change: 2021 Jun 06
+" Last Change: 2021 Nov 03
" ----------------------------------------------------------------------------
"
" Previous Maintainer: Mirko Nasato
@@ -66,7 +66,7 @@ endfunction
com! -nargs=* SynFold call s:run_syntax_fold(<q-args>)
" Not-Top Cluster {{{1
-syn cluster rubyNotTop contains=@rubyCommentNotTop,@rubyStringNotTop,@rubyRegexpSpecial,@rubyDeclaration,@rubyExceptionHandler,@rubyClassOperator,rubyConditional,rubyModuleName,rubyClassName,rubySymbolDelimiter,rubyParentheses,@Spell
+syn cluster rubyNotTop contains=@rubyCommentNotTop,@rubyStringNotTop,@rubyRegexpSpecial,@rubyDeclaration,@rubyExceptionHandler,@rubyClassOperator,rubyConditional,rubyModuleName,rubyClassName,rubySymbolDelimiter,rubyDoubleQuoteSymbolDelimiter,rubySingleQuoteSymbolDelimiter,rubyParentheses,@Spell
" Whitespace Errors {{{1
if exists("ruby_space_errors")
@@ -364,6 +364,9 @@ if !exists("b:ruby_no_expensive") && !exists("ruby_no_expensive")
SynFold 'class' syn region rubyClassBlock start="\<class\>" matchgroup=rubyClass skip="\<end:" end="\<end\>" contains=ALLBUT,@rubyNotTop
SynFold 'module' syn region rubyModuleBlock start="\<module\>" matchgroup=rubyModule skip="\<end:" end="\<end\>" contains=ALLBUT,@rubyNotTop
+ " endless def
+ syn match rubyDefine "\<def\s\+\ze[^[:space:];#(]\+\%(\s\+\|\s*(.*)\s*\)=" nextgroup=rubyMethodDeclaration skipwhite
+
" modifiers
syn match rubyLineContinuation "\\$" nextgroup=@rubyModifier skipwhite skipnl
syn match rubyConditionalModifier "\<\%(if\|unless\)\>"
@@ -430,9 +433,10 @@ endif
" Comments and Documentation {{{1
syn match rubySharpBang "\%^#!.*" display
syn keyword rubyTodo FIXME NOTE TODO OPTIMIZE HACK REVIEW XXX todo contained
-syn match rubyEncoding "[[:alnum:]-]\+" contained display
+syn match rubyEncoding "[[:alnum:]-_]\+" contained display
syn match rubyMagicComment "\c\%<3l#\s*\zs\%(coding\|encoding\):" contained nextgroup=rubyEncoding skipwhite
syn match rubyMagicComment "\c\%<10l#\s*\zs\%(frozen_string_literal\|warn_indent\|warn_past_scope\):" contained nextgroup=rubyBoolean skipwhite
+syn match rubyMagicComment "\c\%<10l#\s*\zs\%(shareable_constant_value\):" contained nextgroup=rubyEncoding skipwhite
syn match rubyComment "#.*" contains=@rubyCommentSpecial,rubySpaceError,@Spell
syn cluster rubyCommentSpecial contains=rubySharpBang,rubyTodo,rubyMagicComment
@@ -465,6 +469,10 @@ syn match rubyDefinedOperator "\%#=1\<defined?" display
syn match rubySymbol "\%(\w\|[^\x00-\x7F]\)\%(\w\|[^\x00-\x7F]\)*[?!]\=::\@!"he=e-1 contained containedin=rubyBlockParameterList,rubyCurlyBlock
syn match rubySymbol "[]})\"':]\@1<!\<\%(\h\|[^\x00-\x7F]\)\%(\w\|[^\x00-\x7F]\)*[!?]\=:[[:space:],;]\@="he=e-1
syn match rubySymbol "[[:space:],{(]\%(\h\|[^\x00-\x7F]\)\%(\w\|[^\x00-\x7F]\)*[!?]\=:[[:space:],;]\@="hs=s+1,he=e-1
+syn match rubySingleQuoteSymbolDelimiter "'" contained
+syn match rubySymbol "'\%(\\.\|[^']\)*'::\@!"he=e-1 contains=rubyQuoteEscape,rubyBackslashEscape,rubySingleQuoteSymbolDelimiter
+syn match rubyDoubleQuoteSymbolDelimiter "\"" contained
+syn match rubySymbol "\"\%(\\.\|[^\"]\)*\"::\@!"he=e-1 contains=@rubyStringSpecial,rubyDoubleQuoteSymbolDelimiter
" __END__ Directive {{{1
SynFold '__END__' syn region rubyData matchgroup=rubyDataDirective start="^__END__$" end="\%$"
@@ -565,6 +573,8 @@ hi def link rubyHeredocDelimiter rubyStringDelimiter
hi def link rubyPercentRegexpDelimiter rubyRegexpDelimiter
hi def link rubyPercentStringDelimiter rubyStringDelimiter
hi def link rubyPercentSymbolDelimiter rubySymbolDelimiter
+hi def link rubyDoubleQuoteSymbolDelimiter rubySymbolDelimiter
+hi def link rubySingleQuoteSymbolDelimiter rubySymbolDelimiter
hi def link rubyRegexpDelimiter rubyStringDelimiter
hi def link rubySymbolDelimiter rubySymbol
hi def link rubyString String
diff --git a/runtime/syntax/sass.vim b/runtime/syntax/sass.vim
index b51a0ae26b..8f41aba4f7 100644
--- a/runtime/syntax/sass.vim
+++ b/runtime/syntax/sass.vim
@@ -2,7 +2,7 @@
" Language: Sass
" Maintainer: Tim Pope <vimNOSPAM@tpope.org>
" Filenames: *.sass
-" Last Change: 2019 Dec 05
+" Last Change: 2022 Mar 15
if exists("b:current_syntax")
finish
@@ -58,6 +58,7 @@ syn match sassAmpersand "&"
" TODO: Arithmetic (including strings and concatenation)
syn region sassMediaQuery matchgroup=sassMedia start="@media" end="[{};]\@=\|$" contains=sassMediaOperators
+syn region sassKeyframe matchgroup=cssAtKeyword start=/@\(-[a-z]\+-\)\=keyframes\>/ end=";\|$" contains=cssVendor,cssComment nextgroup=cssDefinition
syn keyword sassMediaOperators and not only contained
syn region sassCharset start="@charset" end=";\|$" contains=scssComment,cssStringQ,cssStringQQ,cssURL,cssUnicodeEscape,cssMediaType
syn region sassInclude start="@import" end=";\|$" contains=scssComment,cssStringQ,cssStringQQ,cssURL,cssUnicodeEscape,cssMediaType
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/runtime/syntax/scdoc.vim b/runtime/syntax/scdoc.vim
index 25c9c5433b..0180f35027 100644
--- a/runtime/syntax/scdoc.vim
+++ b/runtime/syntax/scdoc.vim
@@ -1,6 +1,7 @@
" Syntax file for scdoc files
-" Maintainer: Gregory Anders <greg@gpanders.com>
-" Last Updated: 2021-08-04
+" Maintainer: Gregory Anders <contact@gpanders.com>
+" Last Updated: 2022-05-09
+" Upstream: https://github.com/gpanders/vim-scdoc
if exists('b:current_syntax')
finish
@@ -20,33 +21,43 @@ syntax match scdocIndentError "^[ ]\+"
syntax match scdocLineBreak "++$"
-syntax match scdocOrderedListMarker "^\s*\.\%(\s\+\S\)\@="
-syntax match scdocListMarker "^\s*-\%(\s\+\S\)\@="
+syntax region scdocOrderedListItem matchgroup=scdocOrderedListMarker start="^\z(\s*\)\." skip="^\z1 .*$" end="^" contains=scdocBold,scdocUnderline
+syntax region scdocListItem matchgroup=scdocListMarker start="^\z(\s*\)-" skip="^\z1 .*$" end="^" contains=scdocBold,scdocUnderline
-syntax match scdocTableStartMarker "^[\[|\]][\[\-\]]"
-syntax match scdocTableMarker "^[|:][\[\-\] ]"
+" Tables cannot start with a column
+syntax match scdocTableError "^:"
+
+syntax region scdocTable matchgroup=scdocTableEntry start="^[\[|\]][\[\-\]<=>]" end="^$" contains=scdocTableEntry,scdocTableError,scdocTableContinuation,scdocBold,scdocUnderline,scdocPre
+syntax match scdocTableError "^.*$" contained
+syntax match scdocTableContinuation "^ \+\S\+" contained
+syntax match scdocTableEntry "^[|:][\[\-\]<=> ]" contained
+syntax match scdocTableError "^[|:][\[\-\]<=> ]\S.*$" contained
syntax region scdocBold concealends matchgroup=scdocBoldDelimiter start="\\\@<!\*" end="\\\@<!\*"
syntax region scdocUnderline concealends matchgroup=scdocUnderlineDelimiter start="\<\\\@<!_" end="\\\@<!_\>"
syntax region scdocPre matchgroup=scdocPreDelimiter start="^\t*```" end="^\t*```"
-hi link scdocFirstLineValid Comment
-hi link scdocComment Comment
-hi link scdocHeader Title
-hi link scdocOrderedListMarker Statement
-hi link scdocListMarker scdocOrderedListMarker
-hi link scdocLineBreak Special
-hi link scdocTableMarker Statement
-hi link scdocTableStartMarker scdocTableMarker
-
-hi link scdocFirstLineError Error
-hi link scdocCommentError Error
-hi link scdocHeaderError Error
-hi link scdocIndentError Error
-
-hi link scdocPreDelimiter Delimiter
-
-hi scdocBold term=bold cterm=bold gui=bold
-hi scdocUnderline term=underline cterm=underline gui=underline
-hi link scdocBoldDelimiter scdocBold
-hi link scdocUnderlineDelimiter scdocUnderline
+syntax sync minlines=50
+
+hi default link scdocFirstLineValid Comment
+hi default link scdocComment Comment
+hi default link scdocHeader Title
+hi default link scdocOrderedListMarker Statement
+hi default link scdocListMarker scdocOrderedListMarker
+hi default link scdocLineBreak Special
+hi default link scdocTableSpecifier Statement
+hi default link scdocTableEntry Statement
+
+hi default link scdocFirstLineError Error
+hi default link scdocCommentError Error
+hi default link scdocHeaderError Error
+hi default link scdocIndentError Error
+hi default link scdocTableError Error
+hi default link scdocTableError Error
+
+hi default link scdocPreDelimiter Delimiter
+
+hi default scdocBold term=bold cterm=bold gui=bold
+hi default scdocUnderline term=underline cterm=underline gui=underline
+hi default link scdocBoldDelimiter scdocBold
+hi default link scdocUnderlineDelimiter scdocUnderline
diff --git a/runtime/syntax/sendpr.vim b/runtime/syntax/sendpr.vim
index 2e07802047..2541b95c67 100644
--- a/runtime/syntax/sendpr.vim
+++ b/runtime/syntax/sendpr.vim
@@ -1,7 +1,7 @@
" Vim syntax file
" Language: FreeBSD send-pr file
" Maintainer: Hendrik Scholz <hendrik@scholz.net>
-" Last Change: 2012 Feb 03
+" Last Change: 2022 Jun 14
"
" http://raisdorf.net/files/misc/send-pr.vim
@@ -31,5 +31,7 @@ hi def link sendprType Type
hi def link sendprString String
hi def link sendprLabel Label
+let b:current_syntax = 'sendpr'
+
let &cpo = s:cpo_save
unlet s:cpo_save
diff --git a/runtime/syntax/sh.vim b/runtime/syntax/sh.vim
index 731a514cb2..e44699faa5 100644
--- a/runtime/syntax/sh.vim
+++ b/runtime/syntax/sh.vim
@@ -2,8 +2,8 @@
" Language: shell (sh) Korn shell (ksh) bash (sh)
" Maintainer: Charles E. Campbell <NcampObell@SdrPchip.AorgM-NOSPAM>
" Previous Maintainer: Lennart Schultz <Lennart.Schultz@ecmwf.int>
-" Last Change: Oct 26, 2021
-" Version: 199
+" Last Change: Jun 29, 2022
+" Version: 202
" URL: http://www.drchip.org/astronaut/vim/index.html#SYNTAX_SH
" For options and settings, please use: :help ft-sh-syntax
" This file includes many ideas from Eric Brunet (eric.brunet@ens.fr) and heredoc fixes from Felipe Contreras
@@ -138,7 +138,7 @@ syn cluster shErrorList contains=shDoError,shIfError,shInError,shCaseError,shEsa
if exists("b:is_kornshell") || exists("b:is_bash")
syn cluster ErrorList add=shDTestError
endif
-syn cluster shArithParenList contains=shArithmetic,shCaseEsac,shComment,shDeref,shDo,shDerefSimple,shEcho,shEscape,shNumber,shOperator,shPosnParm,shExSingleQuote,shExDoubleQuote,shHereString,shRedir,shSingleQuote,shDoubleQuote,shStatement,shVariable,shAlias,shTest,shCtrlSeq,shSpecial,shParen,bashSpecialVariables,bashStatement,shIf,shFor,shFunctionKey,shFunctionOne,shFunctionTwo
+syn cluster shArithParenList contains=shArithmetic,shArithParen,shCaseEsac,shComment,shDeref,shDo,shDerefSimple,shEcho,shEscape,shNumber,shOperator,shPosnParm,shExSingleQuote,shExDoubleQuote,shHereString,shRedir,shSingleQuote,shDoubleQuote,shStatement,shVariable,shAlias,shTest,shCtrlSeq,shSpecial,shParen,bashSpecialVariables,bashStatement,shIf,shFor,shFunctionKey,shFunctionOne,shFunctionTwo
syn cluster shArithList contains=@shArithParenList,shParenError
syn cluster shCaseEsacList contains=shCaseStart,shCaseLabel,shCase,shCaseBar,shCaseIn,shComment,shDeref,shDerefSimple,shCaseCommandSub,shCaseExSingleQuote,shCaseSingleQuote,shCaseDoubleQuote,shCtrlSeq,@shErrorList,shStringSpecial,shCaseRange
syn cluster shCaseList contains=@shCommandSubList,shCaseEsac,shColon,shCommandSub,shCommandSubBQ,shComment,shDo,shEcho,shExpr,shFor,shHereDoc,shIf,shHereString,shRedir,shSetList,shSource,shStatement,shVariable,shCtrlSeq
@@ -147,8 +147,8 @@ if exists("b:is_kornshell") || exists("b:is_bash")
endif
syn cluster shCommandSubList contains=shAlias,shArithmetic,shCmdParenRegion,shCommandSub,shComment,shCtrlSeq,shDeref,shDerefSimple,shDoubleQuote,shEcho,shEscape,shExDoubleQuote,shExpr,shExSingleQuote,shHereDoc,shNumber,shOperator,shOption,shPosnParm,shHereString,shRedir,shSingleQuote,shSpecial,shStatement,shSubSh,shTest,shVariable
syn cluster shCurlyList contains=shNumber,shComma,shDeref,shDerefSimple,shDerefSpecial
-" COMBAK: removing shEscape from shDblQuoteList fails ksh04:43
-syn cluster shDblQuoteList contains=shArithmetic,shCommandSub,shCommandSubBQ,shDeref,shDerefSimple,shPosnParm,shCtrlSeq,shSpecial,shSpecialDQ
+" COMBAK: removing shEscape from shDblQuoteList fails ksh04:43 -- Jun 09, 2022: I don't see the problem with ksh04, so am reinstating shEscape
+syn cluster shDblQuoteList contains=shArithmetic,shCommandSub,shCommandSubBQ,shDeref,shDerefSimple,shEscape,shPosnParm,shCtrlSeq,shSpecial,shSpecialDQ
syn cluster shDerefList contains=shDeref,shDerefSimple,shDerefVar,shDerefSpecial,shDerefWordError,shDerefPSR,shDerefPPS
syn cluster shDerefVarList contains=shDerefOffset,shDerefOp,shDerefVarArray,shDerefOpError
syn cluster shEchoList contains=shArithmetic,shCommandSub,shCommandSubBQ,shDeref,shDerefSimple,shEscape,shExSingleQuote,shExDoubleQuote,shSingleQuote,shDoubleQuote,shCtrlSeq,shEchoQuote
@@ -157,11 +157,12 @@ syn cluster shExprList2 contains=@shExprList1,@shCaseList,shTest
syn cluster shFunctionList contains=@shCommandSubList,shCaseEsac,shColon,shComment,shDo,shEcho,shExpr,shFor,shHereDoc,shIf,shOption,shHereString,shRedir,shSetList,shSource,shStatement,shVariable,shOperator,shCtrlSeq
if exists("b:is_kornshell") || exists("b:is_bash")
syn cluster shFunctionList add=shRepeat,shDblBrace,shDblParen,shForPP
+ syn cluster shDerefList add=shCommandSubList,shEchoDeref
endif
syn cluster shHereBeginList contains=@shCommandSubList
syn cluster shHereList contains=shBeginHere,shHerePayload
syn cluster shHereListDQ contains=shBeginHere,@shDblQuoteList,shHerePayload
-syn cluster shIdList contains=shCommandSub,shCommandSubBQ,shWrapLineOperator,shSetOption,shComment,shDeref,shDerefSimple,shHereString,shNumber,shOperator,shRedir,shExSingleQuote,shExDoubleQuote,shSingleQuote,shDoubleQuote,shExpr,shCtrlSeq,shStringSpecial,shAtExpr
+syn cluster shIdList contains=shArithmetic,shCommandSub,shCommandSubBQ,shWrapLineOperator,shSetOption,shComment,shDeref,shDerefSimple,shHereString,shNumber,shOperator,shRedir,shExSingleQuote,shExDoubleQuote,shSingleQuote,shDoubleQuote,shExpr,shCtrlSeq,shStringSpecial,shAtExpr
syn cluster shIfList contains=@shLoopList,shDblBrace,shDblParen,shFunctionKey,shFunctionOne,shFunctionTwo
syn cluster shLoopList contains=@shCaseList,@shErrorList,shCaseEsac,shConditional,shDblBrace,shExpr,shFor,shIf,shOption,shSet,shTest,shTestOpr,shTouch
if exists("b:is_kornshell") || exists("b:is_bash")
@@ -179,6 +180,10 @@ syn cluster shForList contains=shTestOpr,shNumber,shDerefSimple,shDeref,shComman
" This one is needed INSIDE a CommandSub, so that `echo bla` be correct
syn region shEcho matchgroup=shStatement start="\<echo\>" skip="\\$" matchgroup=shEchoDelim end="$" matchgroup=NONE end="[<>;&|()`]"me=e-1 end="\d[<>]"me=e-2 end="#"me=e-1 contains=@shEchoList skipwhite nextgroup=shQuickComment
syn region shEcho matchgroup=shStatement start="\<print\>" skip="\\$" matchgroup=shEchoDelim end="$" matchgroup=NONE end="[<>;&|()`]"me=e-1 end="\d[<>]"me=e-2 end="#"me=e-1 contains=@shEchoList skipwhite nextgroup=shQuickComment
+if exists("b:is_kornshell") || exists("b:is_bash") || exists("b:is_posix")
+ syn region shEchoDeref contained matchgroup=shStatement start="\<echo\>" skip="\\$" matchgroup=shEchoDelim end="$" end="[<>;&|()`}]"me=e-1 end="\d[<>]"me=e-2 end="#"me=e-1 contains=@shEchoList skipwhite nextgroup=shQuickComment
+ syn region shEchoDeref contained matchgroup=shStatement start="\<print\>" skip="\\$" matchgroup=shEchoDelim end="$" end="[<>;&|()`}]"me=e-1 end="\d[<>]"me=e-2 end="#"me=e-1 contains=@shEchoList skipwhite nextgroup=shQuickComment
+endif
syn match shEchoQuote contained '\%(\\\\\)*\\["`'()]'
" This must be after the strings, so that ... \" will be correct
@@ -291,13 +296,15 @@ syn match shCaseStart contained skipwhite skipnl "(" nextgroup=shCase,shCaseBa
syn match shCaseLabel contained skipwhite "\%(\\.\|[-a-zA-Z0-9_*.]\)\+" contains=shCharClass
if exists("b:is_bash")
ShFoldIfDoFor syn region shCase contained skipwhite skipnl matchgroup=shSnglCase start="\%(\\.\|[^#$()'" \t]\)\{-}\zs)" end=";;" end=";&" end=";;&" end="esac"me=s-1 contains=@shCaseList nextgroup=shCaseStart,shCase,shComment
+elseif exists("b:is_kornshell")
+ ShFoldIfDoFor syn region shCase contained skipwhite skipnl matchgroup=shSnglCase start="\%(\\.\|[^#$()'" \t]\)\{-}\zs)" end=";;" end=";&" end="esac"me=s-1 contains=@shCaseList nextgroup=shCaseStart,shCase,shComment
else
ShFoldIfDoFor syn region shCase contained skipwhite skipnl matchgroup=shSnglCase start="\%(\\.\|[^#$()'" \t]\)\{-}\zs)" end=";;" end="esac"me=s-1 contains=@shCaseList nextgroup=shCaseStart,shCase,shComment
endif
ShFoldIfDoFor syn region shCaseEsac matchgroup=shConditional start="\<case\>" end="\<esac\>" contains=@shCaseEsacList
syn keyword shCaseIn contained skipwhite skipnl in nextgroup=shCase,shCaseStart,shCaseBar,shComment,shCaseExSingleQuote,shCaseSingleQuote,shCaseDoubleQuote
-if exists("b:is_bash")
+if exists("b:is_bash") || exists("b:is_kornshell")
syn region shCaseExSingleQuote matchgroup=shQuote start=+\$'+ skip=+\\\\\|\\.+ end=+'+ contains=shStringSpecial,shSpecial skipwhite skipnl nextgroup=shCaseBar contained
elseif !exists("g:sh_no_error")
syn region shCaseExSingleQuote matchgroup=Error start=+\$'+ skip=+\\\\\|\\.+ end=+'+ contains=shStringSpecial skipwhite skipnl nextgroup=shCaseBar contained
@@ -329,10 +336,11 @@ syn match shEscape contained '\%(^\)\@!\%(\\\\\)*\\.' nextgroup=shComment
" systems too, however, so the following syntax will flag $(..) as
" an Error under /bin/sh. By consensus of vimdev'ers!
if exists("b:is_kornshell") || exists("b:is_bash") || exists("b:is_posix")
- syn region shCommandSub matchgroup=shCmdSubRegion start="\$(" skip='\\\\\|\\.' end=")" contains=@shCommandSubList
+ syn region shCommandSub matchgroup=shCmdSubRegion start="\$(\ze[^(]" skip='\\\\\|\\.' end=")" contains=@shCommandSubList
syn region shArithmetic matchgroup=shArithRegion start="\$((" skip='\\\\\|\\.' end="))" contains=@shArithList
syn region shArithmetic matchgroup=shArithRegion start="\$\[" skip='\\\\\|\\.' end="\]" contains=@shArithList
syn match shSkipInitWS contained "^\s\+"
+ syn region shArithParen matchgroup=shArithRegion contained start="(" end=")" contains=@shArithParenList
elseif !exists("g:sh_no_error")
syn region shCommandSub matchgroup=Error start="\$(" end=")" contains=@shCommandSubList
endif
@@ -342,7 +350,7 @@ if exists("b:is_bash")
syn cluster shCommandSubList add=bashSpecialVariables,bashStatement
syn cluster shCaseList add=bashAdminStatement,bashStatement
syn keyword bashSpecialVariables contained auto_resume BASH BASH_ALIASES BASH_ALIASES BASH_ARGC BASH_ARGC BASH_ARGV BASH_ARGV BASH_CMDS BASH_CMDS BASH_COMMAND BASH_COMMAND BASH_ENV BASH_EXECUTION_STRING BASH_EXECUTION_STRING BASH_LINENO BASH_LINENO BASHOPTS BASHOPTS BASHPID BASHPID BASH_REMATCH BASH_REMATCH BASH_SOURCE BASH_SOURCE BASH_SUBSHELL BASH_SUBSHELL BASH_VERSINFO BASH_VERSION BASH_XTRACEFD BASH_XTRACEFD CDPATH COLUMNS COLUMNS COMP_CWORD COMP_CWORD COMP_KEY COMP_KEY COMP_LINE COMP_LINE COMP_POINT COMP_POINT COMPREPLY COMPREPLY COMP_TYPE COMP_TYPE COMP_WORDBREAKS COMP_WORDBREAKS COMP_WORDS COMP_WORDS COPROC COPROC DIRSTACK EMACS EMACS ENV ENV EUID FCEDIT FIGNORE FUNCNAME FUNCNAME FUNCNEST FUNCNEST GLOBIGNORE GROUPS histchars HISTCMD HISTCONTROL HISTFILE HISTFILESIZE HISTIGNORE HISTSIZE HISTTIMEFORMAT HISTTIMEFORMAT HOME HOSTFILE HOSTNAME HOSTTYPE IFS IGNOREEOF INPUTRC LANG LC_ALL LC_COLLATE LC_CTYPE LC_CTYPE LC_MESSAGES LC_NUMERIC LC_NUMERIC LINENO LINES LINES MACHTYPE MAIL MAILCHECK MAILPATH MAPFILE MAPFILE OLDPWD OPTARG OPTERR OPTIND OSTYPE PATH PIPESTATUS POSIXLY_CORRECT POSIXLY_CORRECT PPID PROMPT_COMMAND PS1 PS2 PS3 PS4 PWD RANDOM READLINE_LINE READLINE_LINE READLINE_POINT READLINE_POINT REPLY SECONDS SHELL SHELL SHELLOPTS SHLVL TIMEFORMAT TIMEOUT TMPDIR TMPDIR UID
- syn keyword bashStatement chmod clear complete du egrep expr fgrep find gnufind gnugrep grep less ls mkdir mv rm rmdir rpm sed sleep sort strip tail
+ syn keyword bashStatement chmod clear complete du egrep expr fgrep find gnufind gnugrep grep head less ls mkdir mv rm rmdir rpm sed sleep sort strip tail
syn keyword bashAdminStatement daemon killall killproc nice reload restart start status stop
syn keyword bashStatement command compgen
endif
@@ -351,7 +359,7 @@ if exists("b:is_kornshell") || exists("b:is_posix")
syn cluster shCommandSubList add=kshSpecialVariables,kshStatement
syn cluster shCaseList add=kshStatement
syn keyword kshSpecialVariables contained CDPATH COLUMNS EDITOR ENV ERRNO FCEDIT FPATH HISTFILE HISTSIZE HOME IFS LINENO LINES MAIL MAILCHECK MAILPATH OLDPWD OPTARG OPTIND PATH PPID PS1 PS2 PS3 PS4 PWD RANDOM REPLY SECONDS SHELL TMOUT VISUAL
- syn keyword kshStatement cat chmod clear cp du egrep expr fgrep find grep killall less ls mkdir mv nice printenv rm rmdir sed sort strip stty tail tput
+ syn keyword kshStatement cat chmod clear cp du egrep expr fgrep find grep head killall less ls mkdir mv nice printenv rm rmdir sed sort strip stty tail tput
syn keyword kshStatement command setgroups setsenv
endif
@@ -368,7 +376,7 @@ endif
syn match shNumber "\<\d\+\>#\="
syn match shNumber "\<-\=\.\=\d\+\>#\="
syn match shCtrlSeq "\\\d\d\d\|\\[abcfnrtv0]" contained
-if exists("b:is_bash")
+if exists("b:is_bash") || exists("b:is_kornshell")
syn match shSpecial "[^\\]\(\\\\\)*\zs\\\o\o\o\|\\x\x\x\|\\c[^"]\|\\[abefnrtv]" contained
syn match shSpecial "^\(\\\\\)*\zs\\\o\o\o\|\\x\x\x\|\\c[^"]\|\\[abefnrtv]" contained
syn region shExSingleQuote matchgroup=shQuote start=+\$'+ skip=+\\\\\|\\.+ end=+'+ contains=shStringSpecial,shSpecial nextgroup=shSpecialNxt
@@ -441,19 +449,24 @@ syn match shVar contained "\h\w*"
syn region shAtExpr contained start="@(" end=")" contains=@shIdList
if exists("b:is_bash")
syn match shSet "^\s*set\ze\s\+$"
- syn region shSetList oneline matchgroup=shSet start="\<\%(declare\|local\|export\)\>\ze[/a-zA-Z_]\@!" end="$" matchgroup=shSetListDelim end="\ze[}|);&]" matchgroup=NONE end="\ze\s\+#\|=" contains=@shIdList
+ syn region shSetList oneline matchgroup=shSet start="\<\%(declare\|local\|export\)\>\ze[/a-zA-Z_]\@!" end="$" matchgroup=shSetListDelim end="\ze[}|);&]" matchgroup=NONE end="\ze\s\+#\|=" contains=@shIdList
syn region shSetList oneline matchgroup=shSet start="\<\%(set\|unset\)\>[/a-zA-Z_]\@!" end="\ze[;|#)]\|$" matchgroup=shSetListDelim end="\ze[}|);&]" matchgroup=NONE end="\ze\s\+=" contains=@shIdList nextgroup=shComment
elseif exists("b:is_kornshell") || exists("b:is_posix")
syn match shSet "^\s*set\ze\s\+$"
if exists("b:is_dash")
- syn region shSetList oneline matchgroup=shSet start="\<\%(local\)\>\ze[/]\@!" end="$" matchgroup=shSetListDelim end="\ze[}|);&]" matchgroup=NONE end="\ze\s\+[#=]" contains=@shIdList
+ syn region shSetList oneline matchgroup=shSet start="\<\%(local\)\>\ze[/]\@!" end="$" matchgroup=shSetListDelim end="\ze[}|);&]" matchgroup=NONE end="\ze\s\+[#=]" contains=@shIdList
endif
- syn region shSetList oneline matchgroup=shSet start="\<\(export\)\>\ze[/]\@!" end="$" matchgroup=shSetListDelim end="\ze[}|);&]" matchgroup=NONE end="\ze\s\+[#=]" contains=@shIdList
+ syn region shSetList oneline matchgroup=shSet start="\<\(export\)\>\ze[/]\@!" end="$" matchgroup=shSetListDelim end="\ze[}|);&]" matchgroup=NONE end="\ze\s\+[#=]" contains=@shIdList
syn region shSetList oneline matchgroup=shSet start="\<\%(set\|unset\>\)\ze[/a-zA-Z_]\@!" end="\ze[;|#)]\|$" matchgroup=shSetListDelim end="\ze[}|);&]" matchgroup=NONE end="\ze\s\+[#=]" contains=@shIdList nextgroup=shComment
else
syn region shSetList oneline matchgroup=shSet start="\<\(set\|export\|unset\)\>\ze[/a-zA-Z_]\@!" end="\ze[;|#)]\|$" matchgroup=shSetListDelim end="\ze[}|);&]" matchgroup=NONE end="\ze\s\+[#=]" contains=@shIdList
endif
+" KornShell namespace: {{{1
+if exists("b:is_kornshell")
+ syn keyword shFunctionKey namespace skipwhite skipnl nextgroup=shFunctionTwo
+endif
+
" Functions: {{{1
if !exists("b:is_posix")
syn keyword shFunctionKey function skipwhite skipnl nextgroup=shFunctionTwo
@@ -473,7 +486,7 @@ endif
" Parameter Dereferencing: {{{1
" ========================
-if !exists("g:sh_no_error")
+if !exists("g:sh_no_error") && !(exists("b:is_bash") || exists("b:is_kornshell") || exists("b:is_posix"))
syn match shDerefWordError "[^}$[~]" contained
endif
syn match shDerefSimple "\$\%(\h\w*\|\d\)" nextgroup=@shNoZSList
@@ -486,6 +499,13 @@ if exists("b:is_bash") || exists("b:is_kornshell") || exists("b:is_posix")
syn region shDeref matchgroup=PreProc start="\${\$\$" end="}" contains=@shDerefList nextgroup=@shSpecialNoZS,shSpecialStart
endif
+" ksh: ${.sh.*} variables: {{{1
+" ========================================
+if exists("b:is_kornshell")
+" syn match shDerefVar contained "[.]*" nextgroup=@shDerefVarList
+ syn match shDerefVar contained "\.\+" nextgroup=@shDerefVarList
+endif
+
" ksh: ${!var[*]} array index list syntax: {{{1
" ========================================
if exists("b:is_kornshell") || exists("b:is_posix")
diff --git a/runtime/syntax/sml.vim b/runtime/syntax/sml.vim
index 53ff12a859..8f1af3f9bd 100644
--- a/runtime/syntax/sml.vim
+++ b/runtime/syntax/sml.vim
@@ -1,9 +1,10 @@
" Vim syntax file
" Language: SML
" Filenames: *.sml *.sig
-" Maintainers: Markus Mottl <markus.mottl@gmail.com>
-" Fabrizio Zeno Cornelli <zeno@filibusta.crema.unimi.it>
-" Last Change: 2021 Oct 04
+" Maintainer: Markus Mottl <markus.mottl@gmail.com>
+" Previous Maintainer: Fabrizio Zeno Cornelli
+" <zeno@filibusta.crema.unimi.it> (invalid)
+" Last Change: 2022 Apr 01
" 2015 Aug 31 - Fixed opening of modules (Ramana Kumar)
" 2006 Oct 23 - Fixed character highlighting bug (MM)
diff --git a/runtime/syntax/strace.vim b/runtime/syntax/strace.vim
index 206c58919e..20516a1853 100644
--- a/runtime/syntax/strace.vim
+++ b/runtime/syntax/strace.vim
@@ -1,8 +1,7 @@
" Vim syntax file
-" This is a GENERATED FILE. Please always refer to source file at the URI below.
" Language: strace output
" Maintainer: David Necas (Yeti) <yeti@physics.muni.cz>
-" Last Change: 2015-01-16
+" Last Change: 2022 Jan 29
" Setup
" quit when a syntax file was already loaded
diff --git a/runtime/syntax/structurizr.vim b/runtime/syntax/structurizr.vim
index 73629b1495..ab9e4ee609 100644
--- a/runtime/syntax/structurizr.vim
+++ b/runtime/syntax/structurizr.vim
@@ -1,7 +1,7 @@
" Vim syntax file
" Language: Structurizr DSL
" Maintainer: Bastian Venthur <venthur@debian.org>
-" Last Change: 2021-08-16
+" Last Change: 2022-02-15
" Remark: For a language reference, see
" https://github.com/structurizr/dsl
@@ -30,6 +30,7 @@ syn keyword skeyword deployment
syn keyword skeyword deploymentenvironment
syn keyword skeyword deploymentgroup
syn keyword skeyword deploymentnode
+syn keyword skeyword description
syn keyword skeyword dynamic
syn keyword skeyword element
syn keyword skeyword enterprise
@@ -37,7 +38,6 @@ syn keyword skeyword exclude
syn keyword skeyword filtered
syn keyword skeyword group
syn keyword skeyword healthcheck
-syn keyword skeyword impliedrelationships
syn keyword skeyword include
syn keyword skeyword infrastructurenode
syn keyword skeyword model
@@ -51,6 +51,7 @@ syn keyword skeyword styles
syn keyword skeyword systemcontext
syn keyword skeyword systemlandscape
syn keyword skeyword tags
+syn keyword skeyword technology
syn keyword skeyword terminology
syn keyword skeyword theme
syn keyword skeyword title
@@ -63,7 +64,11 @@ syn match skeyword "\!adrs\s\+"
syn match skeyword "\!constant\s\+"
syn match skeyword "\!docs\s\+"
syn match skeyword "\!identifiers\s\+"
+syn match skeyword "\!impliedrelationships\s\+"
syn match skeyword "\!include\s\+"
+syn match skeyword "\!plugin\s\+"
+syn match skeyword "\!ref\s\+"
+syn match skeyword "\!script\s\+"
syn region sstring oneline start='"' end='"'
diff --git a/runtime/syntax/synload.vim b/runtime/syntax/synload.vim
index bfcd3b06da..b88cd95103 100644
--- a/runtime/syntax/synload.vim
+++ b/runtime/syntax/synload.vim
@@ -1,6 +1,6 @@
" Vim syntax support file
" Maintainer: Bram Moolenaar <Bram@vim.org>
-" Last Change: 2020 Apr 13
+" Last Change: 2022 Apr 12
" This file sets up for syntax highlighting.
" It is loaded from "syntax.vim" and "manual.vim".
diff --git a/runtime/syntax/syntax.vim b/runtime/syntax/syntax.vim
index f274d93f4f..ac7dc314b9 100644
--- a/runtime/syntax/syntax.vim
+++ b/runtime/syntax/syntax.vim
@@ -1,6 +1,6 @@
" Vim syntax support file
" Maintainer: Bram Moolenaar <Bram@vim.org>
-" Last Change: 2001 Sep 04
+" Last Change: 2022 Apr 12
" This file is used for ":syntax on".
" It installs the autocommands and starts highlighting for all buffers.
diff --git a/runtime/syntax/tap.vim b/runtime/syntax/tap.vim
index db37bb8980..6b00b1d588 100644
--- a/runtime/syntax/tap.vim
+++ b/runtime/syntax/tap.vim
@@ -2,9 +2,9 @@
" Language: Verbose TAP Output
" Maintainer: Rufus Cable <rufus@threebytesfull.com>
" Remark: Simple syntax highlighting for TAP output
-" License:
+" License: Vim License (see :help license)
" Copyright: (c) 2008-2013 Rufus Cable
-" Last Change: 2014-12-13
+" Last Change: 2020 Mar 15
if exists("b:current_syntax")
finish
@@ -29,12 +29,12 @@ syn match tapTestStatusOK /ok/ contained
syn match tapTestStatusNotOK /not ok/ contained
" highlight todo tests
-syn match tapTestTodo /\(# TODO\|Failed (TODO)\) .*$/ contained contains=tapTestTodoRev
-syn match tapTestTodoRev /\<TODO\>/ contained
+syn match tapTestTodo /\c\(# TODO\|Failed (TODO)\) .*$/ contained contains=tapTestTodoRev
+syn match tapTestTodoRev /\c\<TODO\>/ contained
" highlight skipped tests
-syn match tapTestSkip /# skip .*$/ contained contains=tapTestSkipTag
-syn match tapTestSkipTag /\(# \)\@<=skip\>/ contained
+syn match tapTestSkip /\c# skip .*$/ contained contains=tapTestSkipTag
+syn match tapTestSkipTag /\c\(# \)\@<=skip\>/ contained
" look behind so "ok 123" and "not ok 124" match test number
syn match tapTestNumber /\(ok \)\@<=\d\d*/ contained
@@ -51,17 +51,6 @@ syn region tapTestResultsSummaryNotOK start=/TODO passed:/ end=/$/ contained
syn region tapTestInstructionsRegion start=/\%1l/ end=/^$/
-set foldtext=TAPTestLine_foldtext()
-function! TAPTestLine_foldtext()
- let line = getline(v:foldstart)
- let sub = substitute(line, '/\*\|\*/\|{{{\d\=', '', 'g')
- return sub
-endfunction
-
-set foldminlines=5
-set foldcolumn=2
-set foldenable
-set foldmethod=syntax
syn sync fromstart
if !exists("did_tapverboseoutput_syntax_inits")
diff --git a/runtime/syntax/tar.vim b/runtime/syntax/tar.vim
index 497683a186..815c2219cb 100644
--- a/runtime/syntax/tar.vim
+++ b/runtime/syntax/tar.vim
@@ -14,4 +14,6 @@ hi def link tarComment Comment
hi def link tarFilename Constant
hi def link tarDirectory Type
+let b:current_syntax = 'tar'
+
" vim: ts=8
diff --git a/runtime/syntax/tex.vim b/runtime/syntax/tex.vim
index ec0f7b99bd..5544fb63fc 100644
--- a/runtime/syntax/tex.vim
+++ b/runtime/syntax/tex.vim
@@ -1,8 +1,8 @@
" Vim syntax file
" Language: TeX
" Maintainer: Charles E. Campbell <NcampObell@SdrPchip.AorgM-NOSPAM>
-" Last Change: Jun 29, 2020
-" Version: 119
+" Last Change: Apr 22, 2022
+" Version: 121
" URL: http://www.drchip.org/astronaut/vim/index.html#SYNTAX_TEX
"
" Notes: {{{1
@@ -173,13 +173,25 @@ if !s:tex_excludematcher
syn cluster texItalGroup add=texMatcher
endif
if !s:tex_nospell
- syn cluster texMatchGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texLength,texLigature,texMatcher,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texBoldStyle,texBoldItalStyle,texItalStyle,texItalBoldStyle,texZone,texInputFile,texOption,@Spell
- syn cluster texMatchNMGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texLength,texLigature,texMatcherNM,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texBoldStyle,texBoldItalStyle,texItalStyle,texItalBoldStyle,texZone,texInputFile,texOption,@Spell
- syn cluster texStyleGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texLength,texLigature,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texBoldStyle,texBoldItalStyle,texItalStyle,texItalBoldStyle,texZone,texInputFile,texOption,texStyleStatement,texStyleMatcher,@Spell
+ if !s:tex_no_error
+ syn cluster texMatchGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texError,texInput,texLength,texLigature,texMatcher,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texBoldStyle,texBoldItalStyle,texItalStyle,texItalBoldStyle,texZone,texInputFile,texOption,@Spell
+ syn cluster texMatchNMGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texError,texInput,texLength,texLigature,texMatcherNM,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texBoldStyle,texBoldItalStyle,texItalStyle,texItalBoldStyle,texZone,texInputFile,texOption,@Spell
+ syn cluster texStyleGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texError,texInput,texLength,texLigature,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texBoldStyle,texBoldItalStyle,texItalStyle,texItalBoldStyle,texZone,texInputFile,texOption,texStyleStatement,texStyleMatcher,@Spell
+ else
+ syn cluster texMatchGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texLength,texLigature,texMatcher,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texBoldStyle,texBoldItalStyle,texItalStyle,texItalBoldStyle,texZone,texInputFile,texOption,@Spell
+ syn cluster texMatchNMGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texLength,texLigature,texMatcherNM,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texBoldStyle,texBoldItalStyle,texItalStyle,texItalBoldStyle,texZone,texInputFile,texOption,@Spell
+ syn cluster texStyleGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texLength,texLigature,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texBoldStyle,texBoldItalStyle,texItalStyle,texItalBoldStyle,texZone,texInputFile,texOption,texStyleStatement,texStyleMatcher,@Spell
+ endif
else
- syn cluster texMatchGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texLength,texLigature,texMatcher,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texZone,texInputFile,texOption
- syn cluster texMatchNMGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texLength,texLigature,texMatcherNM,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texZone,texInputFile,texOption
- syn cluster texStyleGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texLength,texLigature,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texZone,texInputFile,texOption,texStyleStatement,texStyleMatcher
+ if !s:tex_no_error
+ syn cluster texMatchGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texError,texInput,texLength,texLigature,texMatcher,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texZone,texInputFile,texOption
+ syn cluster texMatchNMGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texError,texInput,texLength,texLigature,texMatcherNM,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texZone,texInputFile,texOption
+ syn cluster texStyleGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texError,texInput,texLength,texLigature,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texZone,texInputFile,texOption,texStyleStatement,texStyleMatcher
+ else
+ syn cluster texMatchGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texLength,texLigature,texMatcher,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texZone,texInputFile,texOption
+ syn cluster texMatchNMGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texLength,texLigature,texMatcherNM,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texZone,texInputFile,texOption
+ syn cluster texStyleGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texLength,texLigature,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texZone,texInputFile,texOption,texStyleStatement,texStyleMatcher
+ endif
endif
syn cluster texPreambleMatchGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texLength,texLigature,texMatcherNM,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTitle,texTypeSize,texTypeStyle,texZone,texInputFile,texOption,texMathZoneZ
syn cluster texRefGroup contains=texMatcher,texComment,texDelimiter
@@ -189,7 +201,7 @@ if !exists("g:tex_no_math")
syn cluster texMatchGroup add=@texMathZones
syn cluster texMathDelimGroup contains=texMathDelimBad,texMathDelimKey,texMathDelimSet1,texMathDelimSet2
syn cluster texMathMatchGroup contains=@texMathZones,texComment,texDefCmd,texDelimiter,texDocType,texInput,texLength,texLigature,texMathDelim,texMathMatcher,texMathOper,texNewCmd,texNewEnv,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texZone
- syn cluster texMathZoneGroup contains=texComment,texDelimiter,texLength,texMathDelim,texMathMatcher,texMathOper,texMathSymbol,texMathText,texRefZone,texSpecialChar,texStatement,texTypeSize,texTypeStyle
+ syn cluster texMathZoneGroup contains=texBadPar,texComment,texDelimiter,texLength,texMathDelim,texMathMatcher,texMathOper,texMathSymbol,texMathText,texRefZone,texSpecialChar,texStatement,texTypeSize,texTypeStyle
if !s:tex_no_error
syn cluster texMathMatchGroup add=texMathError
syn cluster texMathZoneGroup add=texMathError
@@ -214,21 +226,21 @@ endif
if s:tex_fast =~# 'm'
if !s:tex_no_error
if s:tex_matchcheck =~ '{'
- syn region texMatcher matchgroup=Delimiter start="{" skip="\\\\\|\\[{}]" end="}" transparent contains=@texMatchGroup,texError
- syn region texMatcherNM matchgroup=Delimiter start="{" skip="\\\\\|\\[{}]" end="}" transparent contains=@texMatchNMGroup,texError
+ syn region texMatcher matchgroup=texDelimiter start="{" skip="\\\\\|\\[{}]" end="}" transparent contains=@texMatchGroup,texError
+ syn region texMatcherNM matchgroup=texDelimiter start="{" skip="\\\\\|\\[{}]" end="}" transparent contains=@texMatchNMGroup,texError
endif
if s:tex_matchcheck =~ '\['
- syn region texMatcher matchgroup=Delimiter start="\[" end="]" transparent contains=@texMatchGroup,texError,@NoSpell
- syn region texMatcherNM matchgroup=Delimiter start="\[" end="]" transparent contains=@texMatchNMGroup,texError,@NoSpell
+ syn region texMatcher matchgroup=texDelimiter start="\[" end="]" transparent contains=@texMatchGroup,texError,@NoSpell
+ syn region texMatcherNM matchgroup=texDelimiter start="\[" end="]" transparent contains=@texMatchNMGroup,texError,@NoSpell
endif
else
if s:tex_matchcheck =~ '{'
- syn region texMatcher matchgroup=Delimiter start="{" skip="\\\\\|\\[{}]" end="}" transparent contains=@texMatchGroup
- syn region texMatcherNM matchgroup=Delimiter start="{" skip="\\\\\|\\[{}]" end="}" transparent contains=@texMatchNMGroup
+ syn region texMatcher matchgroup=texDelimiter start="{" skip="\\\\\|\\[{}]" end="}" transparent contains=@texMatchGroup
+ syn region texMatcherNM matchgroup=texDelimiter start="{" skip="\\\\\|\\[{}]" end="}" transparent contains=@texMatchNMGroup
endif
if s:tex_matchcheck =~ '\['
- syn region texMatcher matchgroup=Delimiter start="\[" end="]" transparent contains=@texMatchGroup
- syn region texMatcherNM matchgroup=Delimiter start="\[" end="]" transparent contains=@texMatchNMGroup
+ syn region texMatcher matchgroup=texDelimiter start="\[" end="]" transparent contains=@texMatchGroup
+ syn region texMatcherNM matchgroup=texDelimiter start="\[" end="]" transparent contains=@texMatchNMGroup
endif
endif
if s:tex_matchcheck =~ '('
@@ -251,7 +263,7 @@ if s:tex_fast =~# 'M'
if !s:tex_no_error
syn match texMathError "}" contained
endif
- syn region texMathMatcher matchgroup=Delimiter start="{" skip="\%(\\\\\)*\\}" end="}" end="%stopzone\>" contained contains=@texMathMatchGroup
+ syn region texMathMatcher matchgroup=texDelimiter start="{" skip="\%(\\\\\)*\\}" end="}" end="%stopzone\>" contained contains=@texMathMatchGroup
endif
endif
@@ -291,14 +303,14 @@ syn match texLigature "\\\([ijolL]\|ae\|oe\|ss\|AA\|AE\|OE\)$"
" \begin{}/\end{} section markers: {{{1
syn match texBeginEnd "\\begin\>\|\\end\>" nextgroup=texBeginEndName
if s:tex_fast =~# 'm'
- syn region texBeginEndName matchgroup=Delimiter start="{" end="}" contained nextgroup=texBeginEndModifier contains=texComment
- syn region texBeginEndModifier matchgroup=Delimiter start="\[" end="]" contained contains=texComment,@texMathZones,@NoSpell
+ syn region texBeginEndName matchgroup=texDelimiter start="{" end="}" contained nextgroup=texBeginEndModifier contains=texComment
+ syn region texBeginEndModifier matchgroup=texDelimiter start="\[" end="]" contained contains=texComment,@texMathZones,@NoSpell
endif
" \documentclass, \documentstyle, \usepackage: {{{1
syn match texDocType "\\documentclass\>\|\\documentstyle\>\|\\usepackage\>" nextgroup=texBeginEndName,texDocTypeArgs
if s:tex_fast =~# 'm'
- syn region texDocTypeArgs matchgroup=Delimiter start="\[" end="]" contained nextgroup=texBeginEndName contains=texComment,@NoSpell
+ syn region texDocTypeArgs matchgroup=texDelimiter start="\[" end="]" contained nextgroup=texBeginEndName contains=texComment,@NoSpell
endif
" Preamble syntax-based folding support: {{{1
@@ -312,7 +324,7 @@ syn match texInputFile "\\include\(graphics\|list\)\=\(\[.\{-}\]\)\=\s*{.\{-}}"
syn match texInputFile "\\\(epsfig\|input\|usepackage\)\s*\(\[.*\]\)\={.\{-}}" contains=texStatement,texInputCurlies,texInputFileOpt
syn match texInputCurlies "[{}]" contained
if s:tex_fast =~# 'm'
- syn region texInputFileOpt matchgroup=Delimiter start="\[" end="\]" contained contains=texComment
+ syn region texInputFileOpt matchgroup=texDelimiter start="\[" end="\]" contained contains=texComment
endif
" Type Styles (LaTeX 2.09): {{{1
@@ -334,10 +346,8 @@ endif
syn match texTypeStyle "\\textmd\>"
syn match texTypeStyle "\\textrm\>"
-syn match texTypeStyle "\\mathbb\>"
syn match texTypeStyle "\\mathbf\>"
syn match texTypeStyle "\\mathcal\>"
-syn match texTypeStyle "\\mathfrak\>"
syn match texTypeStyle "\\mathit\>"
syn match texTypeStyle "\\mathnormal\>"
syn match texTypeStyle "\\mathrm\>"
@@ -430,6 +440,7 @@ if !exists("g:tex_no_math") && !s:tex_no_error
syn match texBadMath "\\end\s*{\s*\(array\|[bBpvV]matrix\|split\|smallmatrix\)\s*}"
syn match texBadMath "\\end\s*{\s*\(displaymath\|equation\|eqnarray\|math\)\*\=\s*}"
syn match texBadMath "\\[\])]"
+ syn match texBadPar contained "\%(\\par\>\|^\s*\n.\)"
endif
" Math Zones: {{{1
@@ -475,15 +486,15 @@ if !exists("g:tex_no_math")
" Inline Math Zones: {{{2
if s:tex_fast =~# 'M'
if has("conceal") && &enc == 'utf-8' && s:tex_conceal =~# 'd'
- syn region texMathZoneV matchgroup=Delimiter start="\\(" matchgroup=Delimiter end="\\)\|%stopzone\>" keepend concealends contains=@texMathZoneGroup
- syn region texMathZoneW matchgroup=Delimiter start="\\\[" matchgroup=Delimiter end="\\]\|%stopzone\>" keepend concealends contains=@texMathZoneGroup
- syn region texMathZoneX matchgroup=Delimiter start="\$" skip="\\\\\|\\\$" matchgroup=Delimiter end="\$" end="%stopzone\>" concealends contains=@texMathZoneGroup
- syn region texMathZoneY matchgroup=Delimiter start="\$\$" matchgroup=Delimiter end="\$\$" end="%stopzone\>" keepend concealends contains=@texMathZoneGroup
+ syn region texMathZoneV matchgroup=texDelimiter start="\\(" matchgroup=texDelimiter end="\\)\|%stopzone\>" keepend concealends contains=@texMathZoneGroup
+ syn region texMathZoneW matchgroup=texDelimiter start="\\\[" matchgroup=texDelimiter end="\\]\|%stopzone\>" keepend concealends contains=@texMathZoneGroup
+ syn region texMathZoneX matchgroup=texDelimiter start="\$" skip="\\\\\|\\\$" matchgroup=texDelimiter end="\$" end="%stopzone\>" concealends contains=@texMathZoneGroup
+ syn region texMathZoneY matchgroup=texDelimiter start="\$\$" matchgroup=texDelimiter end="\$\$" end="%stopzone\>" keepend concealends contains=@texMathZoneGroup
else
- syn region texMathZoneV matchgroup=Delimiter start="\\(" matchgroup=Delimiter end="\\)\|%stopzone\>" keepend contains=@texMathZoneGroup
- syn region texMathZoneW matchgroup=Delimiter start="\\\[" matchgroup=Delimiter end="\\]\|%stopzone\>" keepend contains=@texMathZoneGroup
- syn region texMathZoneX matchgroup=Delimiter start="\$" skip="\%(\\\\\)*\\\$" matchgroup=Delimiter end="\$" end="%stopzone\>" contains=@texMathZoneGroup
- syn region texMathZoneY matchgroup=Delimiter start="\$\$" matchgroup=Delimiter end="\$\$" end="%stopzone\>" keepend contains=@texMathZoneGroup
+ syn region texMathZoneV matchgroup=texDelimiter start="\\(" matchgroup=texDelimiter end="\\)\|%stopzone\>" keepend contains=@texMathZoneGroup
+ syn region texMathZoneW matchgroup=texDelimiter start="\\\[" matchgroup=texDelimiter end="\\]\|%stopzone\>" keepend contains=@texMathZoneGroup
+ syn region texMathZoneX matchgroup=texDelimiter start="\$" skip="\%(\\\\\)*\\\$" matchgroup=texDelimiter end="\$" end="%stopzone\>" contains=@texMathZoneGroup
+ syn region texMathZoneY matchgroup=texDelimiter start="\$\$" matchgroup=texDelimiter end="\$\$" end="%stopzone\>" keepend contains=@texMathZoneGroup
endif
syn region texMathZoneZ matchgroup=texStatement start="\\ensuremath\s*{" matchgroup=texStatement end="}" end="%stopzone\>" contains=@texMathZoneGroup
endif
@@ -650,23 +661,23 @@ if s:tex_fast =~# 'r'
syn region texRefZone matchgroup=texStatement start="\\label{" end="}\|%stopzone\>" contains=@texRefGroup
syn region texRefZone matchgroup=texStatement start="\\\(page\|eq\)ref{" end="}\|%stopzone\>" contains=@texRefGroup
syn region texRefZone matchgroup=texStatement start="\\v\=ref{" end="}\|%stopzone\>" contains=@texRefGroup
- syn region texRefOption contained matchgroup=Delimiter start='\[' end=']' contains=@texRefGroup,texRefZone nextgroup=texRefOption,texCite
- syn region texCite contained matchgroup=Delimiter start='{' end='}' contains=@texRefGroup,texRefZone,texCite
+ syn region texRefOption contained matchgroup=texDelimiter start='\[' end=']' contains=@texRefGroup,texRefZone nextgroup=texRefOption,texCite
+ syn region texCite contained matchgroup=texDelimiter start='{' end='}' contains=@texRefGroup,texRefZone,texCite
endif
syn match texRefZone '\\cite\%([tp]\*\=\)\=\>' nextgroup=texRefOption,texCite
-" Handle newcommand, newenvironment : {{{1
-syn match texNewCmd "\\newcommand\>" nextgroup=texCmdName skipwhite skipnl
+" Handle (re)newcommand, (re)newenvironment : {{{1
+syn match texNewCmd "\\\%(re\)\=newcommand\>" nextgroup=texCmdName skipwhite skipnl
if s:tex_fast =~# 'V'
- syn region texCmdName contained matchgroup=Delimiter start="{"rs=s+1 end="}" nextgroup=texCmdArgs,texCmdBody skipwhite skipnl
- syn region texCmdArgs contained matchgroup=Delimiter start="\["rs=s+1 end="]" nextgroup=texCmdBody skipwhite skipnl
- syn region texCmdBody contained matchgroup=Delimiter start="{"rs=s+1 skip="\\\\\|\\[{}]" matchgroup=Delimiter end="}" contains=@texCmdGroup
+ syn region texCmdName contained matchgroup=texDelimiter start="{"rs=s+1 end="}" nextgroup=texCmdArgs,texCmdBody skipwhite skipnl
+ syn region texCmdArgs contained matchgroup=texDelimiter start="\["rs=s+1 end="]" nextgroup=texCmdBody skipwhite skipnl
+ syn region texCmdBody contained matchgroup=texDelimiter start="{"rs=s+1 skip="\\\\\|\\[{}]" matchgroup=texDelimiter end="}" contains=@texCmdGroup
endif
-syn match texNewEnv "\\newenvironment\>" nextgroup=texEnvName skipwhite skipnl
+syn match texNewEnv "\\\%(re\)\=newenvironment\>" nextgroup=texEnvName skipwhite skipnl
if s:tex_fast =~# 'V'
- syn region texEnvName contained matchgroup=Delimiter start="{"rs=s+1 end="}" nextgroup=texEnvBgn skipwhite skipnl
- syn region texEnvBgn contained matchgroup=Delimiter start="{"rs=s+1 end="}" nextgroup=texEnvEnd skipwhite skipnl contains=@texEnvGroup
- syn region texEnvEnd contained matchgroup=Delimiter start="{"rs=s+1 end="}" skipwhite skipnl contains=@texEnvGroup
+ syn region texEnvName contained matchgroup=texDelimiter start="{"rs=s+1 end="}" nextgroup=texEnvBgn skipwhite skipnl
+ syn region texEnvBgn contained matchgroup=texDelimiter start="{"rs=s+1 end="}" nextgroup=texEnvEnd skipwhite skipnl contains=@texEnvGroup
+ syn region texEnvEnd contained matchgroup=texDelimiter start="{"rs=s+1 end="}" skipwhite skipnl contains=@texEnvGroup
endif
" Definitions/Commands: {{{1
@@ -694,8 +705,8 @@ if !s:tex_no_error
endif
syn match texStyleStatement "\\[a-zA-Z@]\+" contained
if s:tex_fast =~# 'S'
- syn region texStyleMatcher matchgroup=Delimiter start="{" skip="\\\\\|\\[{}]" end="}" contains=@texStyleGroup,texError contained
- syn region texStyleMatcher matchgroup=Delimiter start="\[" end="]" contains=@texStyleGroup,texError contained
+ syn region texStyleMatcher matchgroup=texDelimiter start="{" skip="\\\\\|\\[{}]" end="}" contains=@texStyleGroup,texError contained
+ syn region texStyleMatcher matchgroup=texDelimiter start="\[" end="]" contains=@texStyleGroup,texError contained
endif
endif
@@ -1017,8 +1028,8 @@ if has("conceal") && &enc == 'utf-8'
" Superscripts/Subscripts {{{2
if s:tex_conceal =~# 's'
if s:tex_fast =~# 's'
- syn region texSuperscript matchgroup=Delimiter start='\^{' skip="\\\\\|\\[{}]" end='}' contained concealends contains=texSpecialChar,texSuperscripts,texStatement,texSubscript,texSuperscript,texMathMatcher
- syn region texSubscript matchgroup=Delimiter start='_{' skip="\\\\\|\\[{}]" end='}' contained concealends contains=texSpecialChar,texSubscripts,texStatement,texSubscript,texSuperscript,texMathMatcher
+ syn region texSuperscript matchgroup=texDelimiter start='\^{' skip="\\\\\|\\[{}]" end='}' contained concealends contains=texSpecialChar,texSuperscripts,texStatement,texSubscript,texSuperscript,texMathMatcher
+ syn region texSubscript matchgroup=texDelimiter start='_{' skip="\\\\\|\\[{}]" end='}' contained concealends contains=texSpecialChar,texSubscripts,texStatement,texSubscript,texSuperscript,texMathMatcher
endif
" s:SuperSub:
fun! s:SuperSub(group,leader,pat,cchar)
@@ -1255,6 +1266,7 @@ if !exists("skip_tex_syntax_inits")
if !exists("g:tex_no_error")
if !exists("g:tex_no_math")
hi def link texBadMath texError
+ hi def link texBadPar texBadMath
hi def link texMathDelimBad texError
hi def link texMathError texError
if !b:tex_stylish
diff --git a/runtime/syntax/tmux.vim b/runtime/syntax/tmux.vim
index 867c033cb5..042b96e872 100644
--- a/runtime/syntax/tmux.vim
+++ b/runtime/syntax/tmux.vim
@@ -1,5 +1,5 @@
" Language: tmux(1) configuration file
-" Version: 3.2a (git-44ada9cd)
+" Version: 3.3-rc (git-964deae4)
" URL: https://github.com/ericpruitt/tmux.vim/
" Maintainer: Eric Pruitt <eric.pruitt@gmail.com>
" License: 2-Clause BSD (http://opensource.org/licenses/BSD-2-Clause)
@@ -18,40 +18,49 @@ syntax iskeyword @,48-57,_,192-255,-
syntax case match
syn keyword tmuxAction none any current other
-syn keyword tmuxBoolean off on
+syn keyword tmuxBoolean off on yes no
syn keyword tmuxTodo FIXME NOTE TODO XXX contained
-syn match tmuxColour /\<colour[0-9]\+/ display
+syn match tmuxColour /\<colou\?r[0-9]\+\>/ display
syn match tmuxKey /\(C-\|M-\|\^\)\+\S\+/ display
syn match tmuxNumber /\<\d\+\>/ display
syn match tmuxFlags /\s-\a\+/ display
-syn match tmuxVariable /\w\+=/ display
-syn match tmuxVariableExpansion /\${\=\w\+}\=/ display
-syn match tmuxControl /%\(if\|elif\|else\|endif\)/
+syn match tmuxVariableExpansion /\$\({[A-Za-z_]\w*}\|[A-Za-z_]\w*\)/ display
+syn match tmuxControl /^\s*%\(if\|elif\|else\|endif\)\>/
+syn match tmuxEscape /\\\(u\x\{4\}\|U\x\{8\}\|\o\{3\}\|[\\ernt$]\)/ display
syn region tmuxComment start=/#/ skip=/\\\@<!\\$/ end=/$/ contains=tmuxTodo,@Spell
-syn region tmuxString start=+"+ skip=+\\\\\|\\"\|\\$+ excludenl end=+"+ end='$' contains=tmuxFormatString,@Spell
-syn region tmuxString start=+'+ skip=+\\\\\|\\'\|\\$+ excludenl end=+'+ end='$' contains=tmuxFormatString,@Spell
+syn region tmuxString start=+"+ skip=+\\\\\|\\"\|\\$+ excludenl end=+"+ end='$' contains=tmuxFormatString,tmuxEscape,tmuxVariableExpansion,@Spell
+syn region tmuxUninterpolatedString start=+'+ skip=+\\$+ excludenl end=+'+ end='$' contains=tmuxFormatString,@Spell
" TODO: Figure out how escaping works inside of #(...) and #{...} blocks.
syn region tmuxFormatString start=/#[#DFhHIPSTW]/ end=// contained keepend
syn region tmuxFormatString start=/#{/ skip=/#{.\{-}}/ end=/}/ keepend
syn region tmuxFormatString start=/#(/ skip=/#(.\{-})/ end=/)/ contained keepend
+" At the time of this writing, the latest tmux release will parse a line
+" reading "abc=xyz set-option ..." as an assignment followed by a command
+" hence the presence of "\s" in the "end" argument.
+syn region tmuxAssignment matchgroup=tmuxVariable start=/^\s*[A-Za-z_]\w*=\@=/ skip=/\\$\|\\\s/ end=/\s\|$/ contains=tmuxString,tmuxUninterpolatedString,tmuxVariableExpansion,tmuxControl,tmuxEscape
+
hi def link tmuxFormatString Identifier
hi def link tmuxAction Boolean
hi def link tmuxBoolean Boolean
hi def link tmuxCommands Keyword
-hi def link tmuxControl Keyword
+hi def link tmuxControl PreCondit
hi def link tmuxComment Comment
+hi def link tmuxEscape Special
+hi def link tmuxEscapeUnquoted Special
hi def link tmuxKey Special
hi def link tmuxNumber Number
hi def link tmuxFlags Identifier
hi def link tmuxOptions Function
hi def link tmuxString String
hi def link tmuxTodo Todo
+hi def link tmuxUninterpolatedString
+\ String
hi def link tmuxVariable Identifier
hi def link tmuxVariableExpansion Identifier
@@ -61,63 +70,85 @@ hi def link tmuxVariableExpansion Identifier
if get(g:, "tmux_syntax_colors", 1)
for s:i in range(0, 255)
let s:bg = (!s:i || s:i == 16 || (s:i > 231 && s:i < 235)) ? 15 : "none"
- exec "syn match tmuxColour" . s:i . " /\\<colour" . s:i . "\\>/ display"
+ exec "syn match tmuxColour" . s:i . " /\\<colou\\?r" . s:i . "\\>/ display"
\ " | highlight tmuxColour" . s:i . " ctermfg=" . s:i . " ctermbg=" . s:bg
endfor
endif
syn keyword tmuxOptions
-\ backspace buffer-limit command-alias copy-command default-terminal editor
-\ escape-time exit-empty activity-action assume-paste-time base-index
-\ bell-action default-command default-shell default-size destroy-unattached
+\ activity-action after-bind-key after-capture-pane after-copy-mode
+\ after-display-message after-display-panes after-kill-pane
+\ after-list-buffers after-list-clients after-list-keys after-list-panes
+\ after-list-sessions after-list-windows after-load-buffer after-lock-server
+\ after-new-session after-new-window after-paste-buffer after-pipe-pane
+\ after-queue after-refresh-client after-rename-session after-rename-window
+\ after-resize-pane after-resize-window after-save-buffer
+\ after-select-layout after-select-pane after-select-window after-send-keys
+\ after-set-buffer after-set-environment after-set-hook after-set-option
+\ after-show-environment after-show-messages after-show-options
+\ after-split-window after-unbind-key aggressive-resize alert-activity
+\ alert-bell alert-silence allow-passthrough allow-rename alternate-screen
+\ assume-paste-time automatic-rename automatic-rename-format backspace
+\ base-index bell-action buffer-limit client-active client-attached
+\ client-detached client-focus-in client-focus-out client-resized
+\ client-session-changed clock-mode-colour clock-mode-style command-alias
+\ copy-command copy-mode-current-match-style copy-mode-mark-style
+\ copy-mode-match-style cursor-colour cursor-style default-command
+\ default-shell default-size default-terminal destroy-unattached
\ detach-on-destroy display-panes-active-colour display-panes-colour
-\ display-panes-time display-time exit-unattached extended-keys focus-events
-\ history-file history-limit key-table lock-after-time lock-command
-\ message-command-style message-limit message-style aggressive-resize
-\ allow-rename alternate-screen automatic-rename automatic-rename-format
-\ clock-mode-colour clock-mode-style copy-mode-current-match-style
-\ copy-mode-mark-style copy-mode-match-style main-pane-height
-\ main-pane-width mode-keys mode-style monitor-activity monitor-bell
-\ monitor-silence mouse other-pane-height other-pane-width
-\ pane-active-border-style pane-base-index pane-border-format
-\ pane-border-lines pane-border-status pane-border-style pane-colours prefix
-\ prefix2 prompt-history-limit remain-on-exit renumber-windows repeat-time
-\ set-clipboard set-titles set-titles-string silence-action status status-bg
-\ status-fg status-format status-interval status-justify status-keys
-\ status-left status-left-length status-left-style status-position
-\ status-right status-right-length status-right-style status-style
-\ synchronize-panes terminal-features terminal-overrides update-environment
-\ user-keys visual-activity visual-bell visual-silence window-active-style
+\ display-panes-time display-time editor escape-time exit-empty
+\ exit-unattached extended-keys fill-character focus-events history-file
+\ history-limit key-table lock-after-time lock-command main-pane-height
+\ main-pane-width message-command-style message-limit message-style
+\ mode-keys mode-style monitor-activity monitor-bell monitor-silence mouse
+\ other-pane-height other-pane-width pane-active-border-style
+\ pane-base-index pane-border-format pane-border-indicators
+\ pane-border-lines pane-border-status pane-border-style pane-colours
+\ pane-died pane-exited pane-focus-in pane-focus-out pane-mode-changed
+\ pane-set-clipboard pane-title-changed popup-border-lines
+\ popup-border-style popup-style prefix prefix2 prompt-history-limit
+\ remain-on-exit remain-on-exit-format renumber-windows repeat-time
+\ scroll-on-clear session-closed session-created session-renamed
+\ session-window-changed set-clipboard set-titles set-titles-string
+\ silence-action status status-bg status-fg status-format status-interval
+\ status-justify status-keys status-left status-left-length
+\ status-left-style status-position status-right status-right-length
+\ status-right-style status-style synchronize-panes terminal-features
+\ terminal-overrides update-environment user-keys visual-activity
+\ visual-bell visual-silence window-active-style window-layout-changed
+\ window-linked window-pane-changed window-renamed window-resized
\ window-size window-status-activity-style window-status-bell-style
\ window-status-current-format window-status-current-style
\ window-status-format window-status-last-style window-status-separator
-\ window-status-style window-style word-separators wrap-search
+\ window-status-style window-style window-unlinked word-separators
+\ wrap-search xterm-keys
syn keyword tmuxCommands
\ attach attach-session bind bind-key break-pane breakp capture-pane
-\ capturep choose-buffer choose-client choose-tree clear-history clearhist
+\ capturep choose-buffer choose-client choose-session choose-tree
+\ choose-window clear-history clear-prompt-history clearhist clearphist
\ clock-mode command-prompt confirm confirm-before copy-mode customize-mode
-\ detach detach-client display display-menu display-message display-panes
-\ display-popup displayp find-window findw if if-shell join-pane joinp
-\ kill-pane kill-server kill-session kill-window killp has has-session killw
+\ delete-buffer deleteb detach detach-client display display-menu
+\ display-message display-panes display-popup displayp find-window findw has
+\ has-session if if-shell info join-pane joinp kill-pane kill-server
+\ kill-session kill-window killp killw last last-pane last-window lastp
\ link-window linkw list-buffers list-clients list-commands list-keys
\ list-panes list-sessions list-windows load-buffer loadb lock lock-client
-\ lock-server lock-session lockc last-pane lastp locks ls last last-window
-\ lsb delete-buffer deleteb lsc lscm lsk lsp lsw menu move-pane move-window
-\ clear-prompt-history clearphist movep movew new new-session new-window
-\ neww next next-layout next-window nextl paste-buffer pasteb pipe-pane
-\ pipep popup prev previous-layout previous-window prevl refresh
-\ refresh-client rename rename-session rename-window renamew resize-pane
-\ resize-window resizep resizew respawn-pane respawn-window respawnp
-\ respawnw rotate-window rotatew run run-shell save-buffer saveb
-\ select-layout select-pane select-window selectl selectp selectw send
-\ send-keys send-prefix set set-buffer set-environment set-hook set-option
+\ lock-server lock-session lockc locks ls lsb lsc lscm lsk lsp lsw menu
+\ move-pane move-window movep movew new new-session new-window neww next
+\ next-layout next-window nextl paste-buffer pasteb pipe-pane pipep popup
+\ prev previous-layout previous-window prevl refresh refresh-client rename
+\ rename-session rename-window renamew resize-pane resize-window resizep
+\ resizew respawn-pane respawn-window respawnp respawnw rotate-window
+\ rotatew run run-shell save-buffer saveb select-layout select-pane
+\ select-window selectl selectp selectw send send-keys send-prefix
+\ server-info set set-buffer set-environment set-hook set-option
\ set-window-option setb setenv setw show show-buffer show-environment
\ show-hooks show-messages show-options show-prompt-history
\ show-window-options showb showenv showmsgs showphist showw source
-\ source-file split-window splitw start start-server suspend-client suspendc
-\ swap-pane swap-window swapp swapw switch-client switchc unbind unbind-key
-\ unlink-window unlinkw wait wait-for
+\ source-file split-pane split-window splitp splitw start start-server
+\ suspend-client suspendc swap-pane swap-window swapp swapw switch-client
+\ switchc unbind unbind-key unlink-window unlinkw wait wait-for
let &cpo = s:original_cpo
unlet! s:original_cpo s:bg s:i
diff --git a/runtime/syntax/trustees.vim b/runtime/syntax/trustees.vim
index 6c58d3983d..3a7d26e896 100644
--- a/runtime/syntax/trustees.vim
+++ b/runtime/syntax/trustees.vim
@@ -1,7 +1,7 @@
" Vim syntax file
" Language: trustees
" Maintainer: Nima Talebi <nima@it.net.au>
-" Last Change: 2005-10-12
+" Last Change: 2022 Jun 14
" quit when a syntax file was already loaded
if exists("b:current_syntax")
@@ -40,3 +40,5 @@ syntax match tfsRuleWho ~\(\*\|[+]\{0,1\}[a-zA-Z0-9/]\+\)~ contained contains=tf
highlight link tfsRuleWho Identifier
syntax match tfsRuleWhat ~[RWEBXODCU!]\+~ contained contains=tfsSpecialChar
highlight link tfsRuleWhat Structure
+
+let b:current_syntax = 'trustees'
diff --git a/runtime/syntax/tt2.vim b/runtime/syntax/tt2.vim
index 6432b85a05..6826fb1581 100644
--- a/runtime/syntax/tt2.vim
+++ b/runtime/syntax/tt2.vim
@@ -1,9 +1,11 @@
+" Vim syntax file
" Language: TT2 (Perl Template Toolkit)
" Maintainer: vim-perl <vim-perl@googlegroups.com>
" Author: Moriki, Atsushi <4woods+vim@gmail.com>
-" Homepage: http://github.com/vim-perl/vim-perl
-" Bugs/requests: http://github.com/vim-perl/vim-perl/issues
-" Last Change: 2015-04-25
+" Homepage: https://github.com/vim-perl/vim-perl
+" Bugs/requests: https://github.com/vim-perl/vim-perl/issues
+" License: Vim License (see :help license)
+" Last Change: 2018 Mar 28
"
" Installation:
" put tt2.vim and tt2html.vim in to your syntax directory.
@@ -42,9 +44,6 @@
" Release
" 0.1.0
" Internal
-"
-" License: follow Vim :help uganda
-"
if !exists("b:tt2_syn_tags")
let b:tt2_syn_tags = '\[% %]'
@@ -95,7 +94,7 @@ if exists("b:tt2_syn_tags")
"Include Perl syntax when 'PERL' 'RAWPERL' block
if b:tt2_syn_inc_perl
- syn include @Perl $VIMRUNTIME/syntax/perl.vim
+ syn include @Perl syntax/perl.vim
exec 'syn region tt2_perlcode '.
\ 'start=+\(\(RAW\)\=PERL\s*[-]\=' . s:ed . '\(\n\)\=\)\@<=+ ' .
\ 'end=+' . s:st . '[-]\=\s*END+me=s-1 contains=@Perl keepend'
@@ -122,7 +121,7 @@ else
"Include Perl syntax when 'PERL' 'RAWPERL' block
if b:tt2_syn_inc_perl
- syn include @Perl $VIMRUNTIME/syntax/perl.vim
+ syn include @Perl syntax/perl.vim
syn region tt2_perlcode
\ start=+\(\(RAW\)\=PERL\s*[-]\=%]\(\n\)\=\)\@<=+
\ end=+\[%[-]\=\s*END+me=s-1
@@ -155,7 +154,7 @@ syn match tt2_operator "[!=<>]=\=\|&&\|||" contained
syn match tt2_operator "\(\s\)\@<=_\(\s\)\@=" contained
syn match tt2_operator "=>\|," contained
syn match tt2_deref "\([[:alnum:]_)\]}]\s*\)\@<=\." contained
-syn match tt2_comment +#.*$+ contained extend
+syn match tt2_comment +#.*$+ contained
syn match tt2_func +\<\I\w*\(\s*(\)\@=+ contained nextgroup=tt2_bracket_r skipempty skipwhite
"
syn region tt2_bracket_r start=+(+ end=+)+ contained contains=@tt2_statement_cluster keepend extend
diff --git a/runtime/syntax/tt2html.vim b/runtime/syntax/tt2html.vim
index ac5744f39d..8c223be707 100644
--- a/runtime/syntax/tt2html.vim
+++ b/runtime/syntax/tt2html.vim
@@ -1,9 +1,11 @@
+" Vim syntax file
" Language: TT2 embedded with HTML
" Maintainer: vim-perl <vim-perl@googlegroups.com>
" Author: Moriki, Atsushi <4woods+vim@gmail.com>
-" Homepage: http://github.com/vim-perl/vim-perl
-" Bugs/requests: http://github.com/vim-perl/vim-perl/issues
-" Last Change: 2013-07-21
+" Homepage: https://github.com/vim-perl/vim-perl
+" Bugs/requests: https://github.com/vim-perl/vim-perl/issues
+" License: Vim License (see :help license)
+" Last Change: 2018 Mar 28
if exists("b:current_syntax")
finish
diff --git a/runtime/syntax/tt2js.vim b/runtime/syntax/tt2js.vim
index 350df57d4c..52e5a3c54a 100644
--- a/runtime/syntax/tt2js.vim
+++ b/runtime/syntax/tt2js.vim
@@ -1,9 +1,11 @@
+" Vim syntax file
" Language: TT2 embedded with Javascript
" Maintainer: Andy Lester <andy@petdance.com>
" Author: Yates, Peter <pd.yates@gmail.com>
-" Homepage: http://github.com/vim-perl/vim-perl
-" Bugs/requests: http://github.com/vim-perl/vim-perl/issues
-" Last Change: 2013-07-21
+" Homepage: https://github.com/vim-perl/vim-perl
+" Bugs/requests: https://github.com/vim-perl/vim-perl/issues
+" License: Vim License (see :help license)
+" Last Change: 2018 Mar 28
if exists("b:current_syntax")
finish
diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim
index 41993b65b0..2b1c58c449 100644
--- a/runtime/syntax/vim.vim
+++ b/runtime/syntax/vim.vim
@@ -23,7 +23,7 @@ syn cluster vimCommentGroup contains=vimTodo,@Spell
" Special and plugin vim commands {{{2
syn match vimCommand contained "\<z[-+^.=]\=\>"
syn keyword vimOnlyCommand contained fix[del] op[en] sh[ell] P[rint]
-syn keyword vimStdPlugin contained DiffOrig Man N[ext] S TOhtml XMLent XMLns
+syn keyword vimStdPlugin contained Arguments Asm Break Cfilter Clear Continue DiffOrig Evaluate Finish Gdb Lfilter Man N[ext] Over P[rint] Program Run S Source Step Stop Termdebug TermdebugCommand TOhtml Until Winbar XMLent XMLns
" Vim-specific options {{{2
syn keyword vimOnlyOption contained biosk bioskey cp compatible consk conskey cm cryptmethod edcompatible guipty key macatsui mzq mzquantum osfiletype oft renderoptions rop st shelltype sn shortname tenc termencoding ta textauto tx textmode tf ttyfast ttym ttymouse tbi ttybuiltin wiv weirdinvert
@@ -34,8 +34,8 @@ syn keyword vimOnlyOption contained nobiosk nobioskey noconsk noconskey nocp noc
" Invertible setting variants
syn keyword vimOnlyOption contained invbiosk invbioskey invconsk invconskey invcp invcompatible invguipty invmacatsui invsn invshortname invta invtextauto invtx invtextmode invtf invttyfast invtbi invttybuiltin invwiv invweirdinvert
" termcap codes (which can also be set) {{{2
-syn keyword vimTermOption contained t_8b t_AB t_al t_bc t_ce t_cl t_Co t_Cs t_CV t_db t_DL t_F1 t_F2 t_F3 t_F4 t_F5 t_F6 t_F7 t_F8 t_F9 t_fs t_IE t_IS t_k1 t_K1 t_k2 t_k3 t_K3 t_k4 t_K4 t_k5 t_K5 t_k6 t_K6 t_k7 t_K7 t_k8 t_K8 t_k9 t_K9 t_KA t_kb t_kB t_KB t_KC t_kd t_kD t_KD t_ke t_KE t_KF t_KG t_kh t_KH t_kI t_KI t_KJ t_KK t_kl t_KL t_kN t_kP t_kr t_ks t_ku t_le t_mb t_md t_me t_mr t_ms t_nd t_op t_RB t_RI t_RV t_Sb t_se t_Sf t_SI t_so t_sr t_SR t_te t_ti t_ts t_u7 t_ue t_us t_ut t_vb t_ve t_vi t_vs t_WP t_WS t_xn t_xs t_ZH t_ZR
-syn keyword vimTermOption contained t_8f t_AF t_AL t_cd t_Ce t_cm t_cs t_CS t_da t_dl t_EI
+syn keyword vimTermOption contained t_8b t_AB t_al t_bc t_ce t_cl t_Co t_Cs t_CV t_db t_DL t_Ds t_F1 t_F2 t_F3 t_F4 t_F5 t_F6 t_F7 t_F8 t_F9 t_fs t_IE t_IS t_k1 t_K1 t_k2 t_k3 t_K3 t_k4 t_K4 t_k5 t_K5 t_k6 t_K6 t_k7 t_K7 t_k8 t_K8 t_k9 t_K9 t_KA t_kb t_kB t_KB t_KC t_kd t_kD t_KD t_ke t_KE t_KF t_KG t_KH t_KI t_KJ t_KK t_kl t_KL t_kN t_kP t_kr t_ks t_ku t_le t_mb t_md t_me t_mr t_ms t_nd t_op t_RB t_RI t_RV t_Sb t_se t_Sf t_SI t_so t_sr t_SR t_te t_ti t_ts t_u7 t_ue t_us t_Us t_ut t_vb t_ve t_vi t_vs t_WP t_WS t_xn t_xs t_ZH t_ZR
+syn keyword vimTermOption contained t_8f t_AF t_AL t_cd t_Ce t_cm t_cs t_CS t_da t_dl t_ds t_EI t_kh t_kI
syn match vimTermOption contained "t_%1"
syn match vimTermOption contained "t_#2"
syn match vimTermOption contained "t_#4"
@@ -46,14 +46,14 @@ syn match vimTermOption contained "t_%i"
syn match vimTermOption contained "t_k;"
" unsupported settings: these are supported by vi but don't do anything in vim {{{2
-syn keyword vimErrSetting contained hardtabs ht w1200 w300 w9600
+syn keyword vimErrSetting contained hardtabs ht w1200 w300 w9600
"}}}2
syn case ignore
" Highlight commonly used Groupnames {{{2
-syn keyword vimGroup contained Comment Constant String Character Number Boolean Float Identifier Function Statement Conditional Repeat Label Operator Keyword Exception PreProc Include Define Macro PreCondit Type StorageClass Structure Typedef Special SpecialChar Tag Delimiter SpecialComment Debug Underlined Ignore Error Todo
+syn keyword vimGroup contained Comment Constant String Character Number Boolean Float Identifier Function Statement Conditional Repeat Label Operator Keyword Exception PreProc Include Define Macro PreCondit Type StorageClass Structure Typedef Special SpecialChar Tag Delimiter SpecialComment Debug Underlined Ignore Error Todo
" Default highlighting groups {{{2
-syn keyword vimHLGroup contained ColorColumn Cursor CursorColumn CursorIM CursorLine CursorLineFold CursorLineNr CursorLineSign DiffAdd DiffChange DiffDelete DiffText Directory EndOfBuffer ErrorMsg FoldColumn Folded IncSearch LineNr MatchParen Menu ModeMsg MoreMsg NonText Normal Pmenu PmenuSbar PmenuSel PmenuThumb Question QuickFixLine Scrollbar Search SignColumn SpecialKey SpellBad SpellCap SpellLocal SpellRare StatusLine StatusLineNC TabLine TabLineFill TabLineSel Title Tooltip VertSplit Visual WarningMsg WildMenu
+syn keyword vimHLGroup contained ColorColumn Cursor CursorColumn CursorIM CursorLine CursorLineFold CursorLineNr CursorLineSign DiffAdd DiffChange DiffDelete DiffText Directory EndOfBuffer ErrorMsg FoldColumn Folded IncSearch LineNr MatchParen Menu ModeMsg MoreMsg NonText Normal Pmenu PmenuSbar PmenuSel PmenuThumb Question QuickFixLine Scrollbar Search SignColumn SpecialKey SpellBad SpellCap SpellLocal SpellRare StatusLine StatusLineNC TabLine TabLineFill TabLineSel Title Tooltip VertSplit Visual WarningMsg WildMenu
syn match vimHLGroup contained "Conceal"
syn keyword vimOnlyHLGroup contained LineNrAbove LineNrBelow StatusLineTerm Terminal VisualNOS
syn keyword nvimHLGroup contained Substitute TermCursor TermCursorNC
@@ -88,10 +88,10 @@ if exists("g:vimsyn_folding") && g:vimsyn_folding =~# '[afhlmpPrt]'
else
com! -nargs=* VimFoldm <args>
endif
- if g:vimsyn_folding =~# 'p'
- com! -nargs=* VimFoldp <args> fold
- else
- com! -nargs=* VimFoldp <args>
+ if g:vimsyn_folding =~# 'p'
+ com! -nargs=* VimFoldp <args> fold
+ else
+ com! -nargs=* VimFoldp <args>
endif
if g:vimsyn_folding =~# 'P'
com! -nargs=* VimFoldP <args> fold
@@ -120,9 +120,6 @@ else
com! -nargs=* VimFoldt <args>
endif
-" commands not picked up by the generator (due to non-standard format) {{{2
-syn keyword vimCommand contained py3
-
" Deprecated variable options {{{2
if exists("g:vim_minlines")
let g:vimsyn_minlines= g:vim_minlines
@@ -148,8 +145,8 @@ syn match vimNumber '-\d\+\%(\.\d\+\%([eE][+-]\=\d\+\)\=\)\=' skipwhite nextgro
syn match vimNumber '\<0[xX]\x\+' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment
syn match vimNumber '\%(^\|\A\)\zs#\x\{6}' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment
syn match vimNumber '\<0[zZ][a-zA-Z0-9.]\+' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment
-syn match vimNumber '0[0-7]\+' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment
-syn match vimNumber '0[bB][01]\+' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment
+syn match vimNumber '0[0-7]\+' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment
+syn match vimNumber '0[bB][01]\+' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment
" All vimCommands are contained by vimIsCommand. {{{2
syn match vimCmdSep "[:|]\+" skipwhite nextgroup=vimAddress,vimAutoCmd,vimEcho,vimIsCommand,vimExtCmd,vimFilter,vimLet,vimMap,vimMark,vimSet,vimSyntax,vimUserCmd
@@ -202,7 +199,7 @@ syn keyword vimAugroupKey contained aug[roup]
" Operators: {{{2
" =========
-syn cluster vimOperGroup contains=vimEnvvar,vimFunc,vimFuncVar,vimOper,vimOperParen,vimNumber,vimString,vimType,vimRegister,vimContinue,vim9Comment
+syn cluster vimOperGroup contains=vimEnvvar,vimFunc,vimFuncVar,vimOper,vimOperParen,vimNumber,vimString,vimType,vimRegister,vimContinue,vim9Comment,vimVar
syn match vimOper "||\|&&\|[-+.!]" skipwhite nextgroup=vimString,vimSpecFile
syn match vimOper "\%#=1\(==\|!=\|>=\|<=\|=\~\|!\~\|>\|<\|=\|!\~#\)[?#]\{0,2}" skipwhite nextgroup=vimString,vimSpecFile
syn match vimOper "\(\<is\|\<isnot\)[?#]\{0,2}\>" skipwhite nextgroup=vimString,vimSpecFile
@@ -299,7 +296,7 @@ syn match vimPatSep contained "\\|"
syn region vimPatSepZone oneline contained matchgroup=vimPatSepZ start="\\%\=\ze(" skip="\\\\" end="\\)\|[^\\]['"]" contains=@vimStringGroup
syn region vimPatRegion contained transparent matchgroup=vimPatSepR start="\\[z%]\=(" end="\\)" contains=@vimSubstList oneline
syn match vimNotPatSep contained "\\\\"
-syn cluster vimStringGroup contains=vimEscapeBrace,vimPatSep,vimNotPatSep,vimPatSepErr,vimPatSepZone,@Spell
+syn cluster vimStringGroup contains=vimEscape,vimEscapeBrace,vimPatSep,vimNotPatSep,vimPatSepErr,vimPatSepZone,@Spell
syn region vimString oneline keepend start=+[^a-zA-Z>!\\@]"+lc=1 skip=+\\\\\|\\"+ matchgroup=vimStringEnd end=+"+ contains=@vimStringGroup
syn region vimString oneline keepend start=+[^a-zA-Z>!\\@]'+lc=1 end=+'+
syn region vimString oneline start=+=!+lc=1 skip=+\\\\\|\\!+ end=+!+ contains=@vimStringGroup
@@ -307,6 +304,7 @@ syn region vimString oneline start="=+"lc=1 skip="\\\\\|\\+" end="+" contains=@v
"syn region vimString oneline start="\s/\s*\A"lc=1 skip="\\\\\|\\+" end="/" contains=@vimStringGroup " see tst45.vim
syn match vimString contained +"[^"]*\\$+ skipnl nextgroup=vimStringCont
syn match vimStringCont contained +\(\\\\\|.\)\{-}[^\\]"+
+syn match vimEscape contained "\\."
" Substitutions: {{{2
" =============
@@ -370,7 +368,7 @@ syn match vimSetMod contained "&vim\=\|[!&?<]\|all&"
" Let: {{{2
" ===
syn keyword vimLet let unl[et] skipwhite nextgroup=vimVar,vimFuncVar,vimLetHereDoc
-VimFoldh syn region vimLetHereDoc matchgroup=vimLetHereDocStart start='=<<\s\+\%(trim\>\)\=\s*\z(\L\S*\)' matchgroup=vimLetHereDocStop end='^\s*\z1\s*$'
+VimFoldh syn region vimLetHereDoc matchgroup=vimLetHereDocStart start='=<<\s\+\%(trim\|eval\>\)\=\s*\z(\L\S*\)' matchgroup=vimLetHereDocStop end='^\s*\z1\s*$'
" Abbreviations: {{{2
" =============
@@ -413,7 +411,7 @@ syn case match
" Menus: {{{2
" =====
syn cluster vimMenuList contains=vimMenuBang,vimMenuPriority,vimMenuName,vimMenuMod
-syn keyword vimCommand am[enu] an[oremenu] aun[menu] cme[nu] cnoreme[nu] cunme[nu] ime[nu] inoreme[nu] iunme[nu] me[nu] nme[nu] nnoreme[nu] noreme[nu] nunme[nu] ome[nu] onoreme[nu] ounme[nu] unme[nu] vme[nu] vnoreme[nu] vunme[nu] skipwhite nextgroup=@vimMenuList
+syn keyword vimCommand am[enu] an[oremenu] aun[menu] cme[nu] cnoreme[nu] cunme[nu] ime[nu] inoreme[nu] iunme[nu] me[nu] nme[nu] nnoreme[nu] noreme[nu] nunme[nu] ome[nu] onoreme[nu] ounme[nu] tlm[enu] tln[oremenu] tlu[nmenu] unme[nu] vme[nu] vnoreme[nu] vunme[nu] skipwhite nextgroup=@vimMenuList
syn match vimMenuName "[^ \t\\<]\+" contained nextgroup=vimMenuNameMore,vimMenuMap
syn match vimMenuPriority "\d\+\(\.\d\+\)*" contained skipwhite nextgroup=vimMenuName
syn match vimMenuNameMore "\c\\\s\|<tab>\|\\\." contained nextgroup=vimMenuName,vimMenuNameMore contains=vimNotation
@@ -437,11 +435,11 @@ syn case match
" User Function Highlighting: {{{2
" (following Gautam Iyer's suggestion)
" ==========================
-syn match vimFunc "\%(\%([sSgGbBwWtTlL]:\|<[sS][iI][dD]>\)\=\%(\w\+\.\)*\I[a-zA-Z0-9_.]*\)\ze\s*(" contains=vimFuncName,vimUserFunc,vimExecute
+syn match vimFunc "\%(\%([sSgGbBwWtTlL]:\|<[sS][iI][dD]>\)\=\%(\w\+\.\)*\I[a-zA-Z0-9_.]*\)\ze\s*(" contains=vimFuncEcho,vimFuncName,vimUserFunc,vimExecute
syn match vimUserFunc contained "\%(\%([sSgGbBwWtTlL]:\|<[sS][iI][dD]>\)\=\%(\w\+\.\)*\I[a-zA-Z0-9_.]*\)\|\<\u[a-zA-Z0-9.]*\>\|\<if\>" contains=vimNotation
+syn keyword vimFuncEcho contained ec ech echo
" User Command Highlighting: {{{2
-"syn match vimUsrCmd '^\s*\zs\u\w*.*$'
syn match vimUsrCmd '^\s*\zs\u\%(\w*\)\@>\%([(#[]\|\s\+\%([-+*/%]\=\|\.\.\)=\)\@!'
" Errors And Warnings: {{{2
@@ -574,7 +572,7 @@ syn match vimHiBang contained "!" skipwhite nextgroup=@vimHighlightCluster
syn match vimHiGroup contained "\i\+"
syn case ignore
-syn keyword vimHiAttrib contained none bold inverse italic nocombine reverse standout strikethrough underline undercurl
+syn keyword vimHiAttrib contained none bold inverse italic nocombine reverse standout strikethrough underline undercurl underdouble underdotted underdashed
syn keyword vimFgBgAttrib contained none bg background fg foreground
syn case match
syn match vimHiAttribList contained "\i\+" contains=vimHiAttrib
@@ -873,8 +871,10 @@ if !exists("skip_vim_syntax_inits")
hi def link vimElseif vimCondHL
hi def link vimEnvvar PreProc
hi def link vimError Error
+ hi def link vimEscape Special
hi def link vimFBVar vimVar
hi def link vimFgBgAttrib vimHiAttrib
+ hi def link vimFuncEcho vimCommand
hi def link vimHiCtermul vimHiTerm
hi def link vimFold Folded
hi def link vimFTCmd vimCommand
diff --git a/runtime/syntax/webmacro.vim b/runtime/syntax/webmacro.vim
index fb1fff6d3d..95975251af 100644
--- a/runtime/syntax/webmacro.vim
+++ b/runtime/syntax/webmacro.vim
@@ -34,9 +34,9 @@ syn region webmacroList contained matchgroup=Structure start="\[" matchgroup=Str
syn region webmacroIf start="#if" start="#else" end="{"me=e-1 contains=webmacroVariable,webmacroNumber,webmacroString,webmacroBoolean,webmacroList nextgroup=webmacroBraces
syn region webmacroForeach start="#foreach" end="{"me=e-1 contains=webmacroVariable,webmacroNumber,webmacroString,webmacroBoolean,webmacroList nextgroup=webmacroBraces
-syn match webmacroSet "#set .*$" contains=webmacroVariable,webmacroNumber,webmacroNumber,webmacroBoolean,webmacroString,webmacroList
-syn match webmacroInclude "#include .*$" contains=webmacroVariable,webmacroNumber,webmacroNumber,webmacroBoolean,webmacroString,webmacroList
-syn match webmacroParse "#parse .*$" contains=webmacroVariable,webmacroNumber,webmacroNumber,webmacroBoolean,webmacroString,webmacroList
+syn match webmacroSet "#set .*$" contains=webmacroVariable,webmacroNumber,webmacroBoolean,webmacroString,webmacroList
+syn match webmacroInclude "#include .*$" contains=webmacroVariable,webmacroNumber,webmacroBoolean,webmacroString,webmacroList
+syn match webmacroParse "#parse .*$" contains=webmacroVariable,webmacroNumber,webmacroBoolean,webmacroString,webmacroList
syn region webmacroUse matchgroup=PreProc start="#use .*" matchgroup=PreProc end="^-.*" contains=webmacroHash,@HtmlTop
syn region webmacroBraces matchgroup=Structure start="{" matchgroup=Structure end="}" contained transparent
syn match webmacroBracesError "[{}]"
diff --git a/runtime/syntax/wget.vim b/runtime/syntax/wget.vim
index 2587022f2e..8178d02bad 100644
--- a/runtime/syntax/wget.vim
+++ b/runtime/syntax/wget.vim
@@ -1,7 +1,9 @@
" Vim syntax file
" Language: Wget configuration file (/etc/wgetrc ~/.wgetrc)
" Maintainer: Doug Kearns <dougkearns@gmail.com>
-" Last Change: 2013 Jun 1
+" Last Change: 2022 Apr 28
+
+" GNU Wget 1.21 built on linux-gnu.
if exists("b:current_syntax")
finish
@@ -18,155 +20,206 @@ syn region wgetString start=+"+ skip=+\\\\\|\\"+ end=+"+ contained oneline
syn region wgetString start=+'+ skip=+\\\\\|\\'+ end=+'+ contained oneline
syn case ignore
-syn keyword wgetBoolean on off contained
-syn keyword wgetNumber inf contained
-syn case match
-syn match wgetNumber "\<\%(\d\+\|inf\)\>" contained
-syn match wgetQuota "\<\d\+[kKmM]\>" contained
-syn match wgetTime "\<\d\+[smhdw]\>" contained
+syn keyword wgetBoolean on off yes no contained
+syn keyword wgetNumber inf contained
+
+syn match wgetNumber "\<\d\+>" contained
+syn match wgetQuota "\<\d\+[kmgt]\>" contained
+syn match wgetTime "\<\d\+[smhdw]\>" contained
"{{{ Commands
-let s:commands = map([
- \ "accept",
- \ "add_hostdir",
- \ "adjust_extension",
- \ "always_rest",
- \ "ask_password",
- \ "auth_no_challenge",
- \ "background",
- \ "backup_converted",
- \ "backups",
- \ "base",
- \ "bind_address",
- \ "ca_certificate",
- \ "ca_directory",
- \ "cache",
- \ "certificate",
- \ "certificate_type",
- \ "check_certificate",
- \ "connect_timeout",
- \ "content_disposition",
- \ "continue",
- \ "convert_links",
- \ "cookies",
- \ "cut_dirs",
- \ "debug",
- \ "default_page",
- \ "delete_after",
- \ "dns_cache",
- \ "dns_timeout",
- \ "dir_prefix",
- \ "dir_struct",
- \ "domains",
- \ "dot_bytes",
- \ "dots_in_line",
- \ "dot_spacing",
- \ "dot_style",
- \ "egd_file",
- \ "exclude_directories",
- \ "exclude_domains",
- \ "follow_ftp",
- \ "follow_tags",
- \ "force_html",
- \ "ftp_passwd",
- \ "ftp_password",
- \ "ftp_user",
- \ "ftp_proxy",
- \ "glob",
- \ "header",
- \ "html_extension",
- \ "htmlify",
- \ "http_keep_alive",
- \ "http_passwd",
- \ "http_password",
- \ "http_proxy",
- \ "https_proxy",
- \ "http_user",
- \ "ignore_case",
- \ "ignore_length",
- \ "ignore_tags",
- \ "include_directories",
- \ "inet4_only",
- \ "inet6_only",
- \ "input",
- \ "iri",
- \ "keep_session_cookies",
- \ "kill_longer",
- \ "limit_rate",
- \ "load_cookies",
- \ "locale",
- \ "local_encoding",
- \ "logfile",
- \ "login",
- \ "max_redirect",
- \ "mirror",
- \ "netrc",
- \ "no_clobber",
- \ "no_parent",
- \ "no_proxy",
- \ "numtries",
- \ "output_document",
- \ "page_requisites",
- \ "passive_ftp",
- \ "passwd",
- \ "password",
- \ "post_data",
- \ "post_file",
- \ "prefer_family",
- \ "preserve_permissions",
- \ "private_key",
- \ "private_key_type",
- \ "progress",
- \ "protocol_directories",
- \ "proxy_passwd",
- \ "proxy_password",
- \ "proxy_user",
- \ "quiet",
- \ "quota",
- \ "random_file",
- \ "random_wait",
- \ "read_timeout",
- \ "reclevel",
- \ "recursive",
- \ "referer",
- \ "reject",
- \ "relative_only",
- \ "remote_encoding",
- \ "remove_listing",
- \ "restrict_file_names",
- \ "retr_symlinks",
- \ "retry_connrefused",
- \ "robots",
- \ "save_cookies",
- \ "save_headers",
- \ "secure_protocol",
- \ "server_response",
- \ "show_all_dns_entries",
- \ "simple_host_check",
- \ "span_hosts",
- \ "spider",
- \ "strict_comments",
- \ "sslcertfile",
- \ "sslcertkey",
- \ "timeout",
- \ "time_stamping",
- \ "use_server_timestamps",
- \ "tries",
- \ "trust_server_names",
- \ "user",
- \ "use_proxy",
- \ "user_agent",
- \ "verbose",
- \ "wait",
- \ "wait_retry"],
- \ "substitute(v:val, '_', '[-_]\\\\=', 'g')")
+let s:commands =<< trim EOL
+ accept
+ accept_regex
+ add_host_dir
+ adjust_extension
+ always_rest
+ ask_password
+ auth_no_challenge
+ background
+ backup_converted
+ backups
+ base
+ bind_address
+ bind_dns_address
+ body_data
+ body_file
+ ca_certificate
+ ca_directory
+ cache
+ certificate
+ certificate_type
+ check_certificate
+ choose_config
+ ciphers
+ compression
+ connect_timeout
+ content_disposition
+ content_on_error
+ continue
+ convert_file_only
+ convert_links
+ cookies
+ crl_file
+ cut_dirs
+ debug
+ default_page
+ delete_after
+ dns_cache
+ dns_servers
+ dns_timeout
+ dir_prefix
+ dir_struct
+ domains
+ dot_bytes
+ dots_in_line
+ dot_spacing
+ dot_style
+ egd_file
+ exclude_directories
+ exclude_domains
+ follow_ftp
+ follow_tags
+ force_html
+ ftp_passwd
+ ftp_password
+ ftp_user
+ ftp_proxy
+ ftps_clear_data_connection
+ ftps_fallback_to_ftp
+ ftps_implicit
+ ftps_resume_ssl
+ hsts
+ hsts_file
+ ftp_stmlf
+ glob
+ header
+ html_extension
+ htmlify
+ http_keep_alive
+ http_passwd
+ http_password
+ http_proxy
+ https_proxy
+ https_only
+ http_user
+ if_modified_since
+ ignore_case
+ ignore_length
+ ignore_tags
+ include_directories
+ inet4_only
+ inet6_only
+ input
+ input_meta_link
+ iri
+ keep_bad_hash
+ keep_session_cookies
+ kill_longer
+ limit_rate
+ load_cookies
+ locale
+ local_encoding
+ logfile
+ login
+ max_redirect
+ metalink_index
+ metalink_over_http
+ method
+ mirror
+ netrc
+ no_clobber
+ no_config
+ no_parent
+ no_proxy
+ numtries
+ output_document
+ page_requisites
+ passive_ftp
+ passwd
+ password
+ pinned_pubkey
+ post_data
+ post_file
+ prefer_family
+ preferred_location
+ preserve_permissions
+ private_key
+ private_key_type
+ progress
+ protocol_directories
+ proxy_passwd
+ proxy_password
+ proxy_user
+ quiet
+ quota
+ random_file
+ random_wait
+ read_timeout
+ rec_level
+ recursive
+ referer
+ regex_type
+ reject
+ rejected_log
+ reject_regex
+ relative_only
+ remote_encoding
+ remove_listing
+ report_speed
+ restrict_file_names
+ retr_symlinks
+ retry_connrefused
+ retry_on_host_error
+ retry_on_http_error
+ robots
+ save_cookies
+ save_headers
+ secure_protocol
+ server_response
+ show_all_dns_entries
+ show_progress
+ simple_host_check
+ span_hosts
+ spider
+ start_pos
+ strict_comments
+ sslcertfile
+ sslcertkey
+ timeout
+ timestamping
+ use_server_timestamps
+ tries
+ trust_server_names
+ unlink
+ use_askpass
+ user
+ use_proxy
+ user_agent
+ verbose
+ wait
+ wait_retry
+ warc_cdx
+ warc_cdx_dedup
+ warc_compression
+ warc_digests
+ warc_file
+ warc_header
+ warc_keep_log
+ warc_max_size
+ warc_temp_dir
+ wdebug
+ xattr
+EOL
"}}}
-syn case ignore
+call map(s:commands, "substitute(v:val, '_', '[-_]\\\\=', 'g')")
+
for cmd in s:commands
- exe 'syn match wgetCommand "' . cmd . '" nextgroup=wgetAssignmentOperator skipwhite contained'
+ exe 'syn match wgetCommand "\<' . cmd . '\>" nextgroup=wgetAssignmentOperator skipwhite contained'
endfor
+
syn case match
syn match wgetStart "^" nextgroup=wgetCommand,wgetComment skipwhite
@@ -179,6 +232,7 @@ hi def link wgetComment Comment
hi def link wgetNumber Number
hi def link wgetQuota Number
hi def link wgetString String
+hi def link wgetTime Number
hi def link wgetTodo Todo
let b:current_syntax = "wget"
diff --git a/runtime/syntax/wget2.vim b/runtime/syntax/wget2.vim
new file mode 100644
index 0000000000..a63c336f06
--- /dev/null
+++ b/runtime/syntax/wget2.vim
@@ -0,0 +1,250 @@
+" Vim syntax file
+" Language: Wget2 configuration file (/etc/wget2rc ~/.wget2rc)
+" Maintainer: Doug Kearns <dougkearns@gmail.com>
+" Last Change: 2022 Apr 28
+
+" GNU Wget2 2.0.0 - multithreaded metalink/file/website downloader
+
+if exists("b:current_syntax")
+ finish
+endif
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+syn match wgetComment "#.*$" contains=wgetTodo contained
+
+syn keyword wgetTodo TODO NOTE FIXME XXX contained
+
+syn region wgetString start=+"+ skip=+\\\\\|\\"+ end=+"+ contained oneline
+syn region wgetString start=+'+ skip=+\\\\\|\\'+ end=+'+ contained oneline
+
+syn case ignore
+
+syn keyword wgetBoolean on off yes no y n contained
+syn keyword wgetNumber infinity inf contained
+
+syn match wgetNumber "\<\d\+>" contained
+syn match wgetQuota "\<\d\+[kmgt]\>" contained
+syn match wgetTime "\<\d\+[smhd]\>" contained
+
+"{{{ Commands
+let s:commands =<< trim EOL
+ accept
+ accept-regex
+ adjust-extension
+ append-output
+ ask-password
+ auth-no-challenge
+ background
+ backup-converted
+ backups
+ base
+ bind-address
+ bind-interface
+ body-data
+ body-file
+ ca-certificate
+ ca-directory
+ cache
+ certificate
+ certificate-type
+ check-certificate
+ check-hostname
+ chunk-size
+ clobber
+ compression
+ config
+ connect-timeout
+ content-disposition
+ content-on-error
+ continue
+ convert-file-only
+ convert-links
+ cookie-suffixes
+ cookies
+ crl-file
+ cut-dirs
+ cut-file-get-vars
+ cut-url-get-vars
+ debug
+ default-http-port
+ default-https-port
+ default-page
+ delete-after
+ directories
+ directory-prefix
+ dns-cache
+ dns-cache-preload
+ dns-timeout
+ domains
+ download-attr
+ egd-file
+ exclude-directories
+ exclude-domains
+ execute
+ filter-mime-type
+ filter-urls
+ follow-tags
+ force-atom
+ force-css
+ force-directories
+ force-html
+ force-metalink
+ force-progress
+ force-rss
+ force-sitemap
+ fsync-policy
+ gnupg-homedir
+ header
+ help
+ host-directories
+ hpkp
+ hpkp-file
+ hsts
+ hsts-file
+ hsts-preload
+ hsts-preload-file
+ html-extension
+ http-keep-alive
+ http-password
+ http-proxy
+ http-proxy-password
+ http-proxy-user
+ http-user
+ http2
+ http2-only
+ http2-request-window
+ https-enforce
+ https-only
+ https-proxy
+ hyperlink
+ if-modified-since
+ ignore-case
+ ignore-length
+ ignore-tags
+ include-directories
+ inet4-only
+ inet6-only
+ input-encoding
+ input-file
+ keep-extension
+ keep-session-cookies
+ level
+ limit-rate
+ list-plugins
+ load-cookies
+ local-db
+ local-encoding
+ local-plugin
+ max-redirect
+ max-threads
+ metalink
+ method
+ mirror
+ netrc
+ netrc-file
+ ocsp
+ ocsp-date
+ ocsp-file
+ ocsp-nonce
+ ocsp-server
+ ocsp-stapling
+ output-document
+ output-file
+ page-requisites
+ parent
+ password
+ plugin
+ plugin-dirs
+ plugin-help
+ plugin-opt
+ post-data
+ post-file
+ prefer-family
+ private-key
+ private-key-type
+ progress
+ protocol-directories
+ proxy
+ quiet
+ quota
+ random-file
+ random-wait
+ read-timeout
+ recursive
+ referer
+ regex-type
+ reject
+ reject-regex
+ remote-encoding
+ report-speed
+ restrict-file-names
+ retry-connrefused
+ retry-on-http-error
+ robots
+ save-content-on
+ save-cookies
+ save-headers
+ secure-protocol
+ server-response
+ signature-extensions
+ span-hosts
+ spider
+ start-pos
+ stats-dns
+ stats-ocsp
+ stats-server
+ stats-site
+ stats-tls
+ strict-comments
+ tcp-fastopen
+ timeout
+ timestamping
+ tls-false-start
+ tls-resume
+ tls-session-file
+ tries
+ trust-server-names
+ unlink
+ use-askpass
+ use-server-timestamps
+ user
+ user-agent
+ verbose
+ verify-save-failed
+ verify-sig
+ version
+ wait
+ waitretry
+ xattr
+EOL
+"}}}
+
+call map(s:commands, "substitute(v:val, '_', '[-_]\\\\=', 'g')")
+
+for cmd in s:commands
+ exe 'syn match wgetCommand "\<' . cmd . '\>" nextgroup=wgetAssignmentOperator skipwhite contained'
+endfor
+
+syn case match
+
+syn match wgetStart "^" nextgroup=wgetCommand,wgetComment skipwhite
+syn match wgetAssignmentOperator "=" nextgroup=wgetString,wgetBoolean,wgetNumber,wgetQuota,wgetTime skipwhite contained
+
+hi def link wgetAssignmentOperator Special
+hi def link wgetBoolean Boolean
+hi def link wgetCommand Identifier
+hi def link wgetComment Comment
+hi def link wgetNumber Number
+hi def link wgetQuota Number
+hi def link wgetString String
+hi def link wgetTime Number
+hi def link wgetTodo Todo
+
+let b:current_syntax = "wget"
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
+
+" vim: ts=8 fdm=marker:
diff --git a/runtime/syntax/xs.vim b/runtime/syntax/xs.vim
index 3e658b4a83..2145ede2dd 100644
--- a/runtime/syntax/xs.vim
+++ b/runtime/syntax/xs.vim
@@ -1,9 +1,12 @@
" Vim syntax file
-" Language: XS (Perl extension interface language)
-" Author: Autogenerated from perl headers, on an original basis of Michael W. Dodge <sarge@pobox.com>
-" Maintainer: vim-perl <vim-perl@googlegroups.com>
-" Previous: Vincent Pit <perl@profvince.com>
-" Last Change: 2017-09-12
+" Language: XS (Perl extension interface language)
+" Author: Autogenerated from perl headers, on an original basis of Michael W. Dodge <sarge@pobox.com>
+" Maintainer: vim-perl <vim-perl@googlegroups.com>
+" Previous: Vincent Pit <perl@profvince.com>
+" Homepage: https://github.com/vim-perl/vim-perl
+" Bugs/requests: https://github.com/vim-perl/vim-perl/issues
+" License: Vim License (see :help license)
+" Last Change: 2018 Mar 28
if exists("b:current_syntax")
finish
diff --git a/runtime/tutor/en/vim-01-beginner.tutor b/runtime/tutor/en/vim-01-beginner.tutor
index 7c0c357e80..e256711e70 100644
--- a/runtime/tutor/en/vim-01-beginner.tutor
+++ b/runtime/tutor/en/vim-01-beginner.tutor
@@ -1,15 +1,14 @@
-# Welcome to the VIM Tutor
+# Welcome to the Neovim Tutorial
-Vim is a very powerful editor that has many commands, too many to explain in
-a tutor such as this. This tutor is designed to describe enough of the
-commands that you will be able to easily use Vim as an all-purpose editor.
-It is IMPORTANT to remember that this tutor is set up to teach by use. That
+Neovim is a very powerful editor that has many commands, too many to explain in
+a tutorial such as this. This tutorial is designed to describe enough of the
+commands that you will be able to easily use Neovim as an all-purpose editor.
+It is IMPORTANT to remember that this tutorial is set up to teach by use. That
means that you need to do the exercises to learn them properly. If you only
read the text, you will soon forget what is most important!
-For now, make sure that your Shift-Lock key is NOT depressed and press the
-`j`{normal} key enough times to move the cursor so that Lesson 0 completely
-fills the screen.
+For now, make sure that your Caps-Lock is off and press the `j`{normal} key enough
+times to move the cursor so that Lesson 0 completely fills the screen.
# Lesson 0
@@ -20,12 +19,13 @@ pressing [<Esc>](<Esc>) and then [u](u) will undo the latest change.
This tutorial is interactive, and there are a few things you should know.
- Type [<Enter>](<Enter>) on links [like this](holy-grail ) to open the linked help section.
- Or simply type [K](K) on any word to find its documentation!
+- You can close this help window with `:q`{vim}
- Sometimes you will be required to modify text like
this here
Once you have done the changes correctly, the ✗ sign at the left will change
-to ✓. I imagine you can already see how neat Vim can be. ;)
+to ✓. I imagine you can already see how neat Neovim can be.
Other times, you'll be prompted to run a command (I'll explain this later):
~~~ cmd
:help <Enter>
@@ -43,9 +43,9 @@ Now, move to the next lesson (use the `j`{normal} key to scroll down).
** To move the cursor, press the `h`, `j`, `k`, `l` keys as indicated. **
- ↑
- k Hint: The `h`{normal} key is at the left and moves left.
- ← h l → The `l`{normal} key is at the right and moves right.
+ ↑
+ k Hint: The `h`{normal} key is at the left and moves left.
+ ← h l → The `l`{normal} key is at the right and moves right.
j The `j`{normal} key looks like a down arrow.
@@ -60,11 +60,11 @@ NOTE: If you are ever unsure about something you typed, press <Esc> to place
you in Normal mode. Then retype the command you wanted.
NOTE: The cursor keys should also work. But using hjkl you will be able to
- move around much faster, once you get used to it. Really!
+ move around much faster, once you get used to it.
-# Lesson 1.2: EXITING VIM
+# Lesson 1.2: EXITING NEOVIM
-!! NOTE: Before executing any of the steps below, read this entire lesson !!
+!! NOTE: Before executing any of the steps below, read the entire lesson !!
1. Press the <Esc> key (to make sure you are in Normal mode).
@@ -72,10 +72,10 @@ NOTE: The cursor keys should also work. But using hjkl you will be able to
`:q!`{vim} `<Enter>`{normal}.
- This exits the editor, DISCARDING any changes you have made.
+ This quits the editor, DISCARDING any changes you have made.
- 3. Open vim and get back here by executing the command that got you into
- this tutor. That might be:
+ 3. Open Neovim and get back here by executing the command that got you into
+ this tutorial. That might be:
:Tutor <Enter>
@@ -104,9 +104,9 @@ The ccow jumpedd ovverr thhe mooon.
5. Now that the line is correct, go on to Lesson 1.4.
-NOTE: As you go through this tutor, do not try to memorize everything,
- your Vim vocabulary will expand with usage. Consider returning to
- this tutor periodically for a refresher.
+NOTE: As you go through this tutorial, do not try to memorize everything,
+ your Neovim vocabulary will expand with usage. Consider returning to
+ this tutorial periodically for a refresher.
# Lesson 1.4: TEXT EDITING: INSERTION
@@ -151,11 +151,11 @@ There is also some text missing here.
# Lesson 1.6: EDITING A FILE
-** Use `:wq`{vim} to save a file and exit. **
+** Use `:wq`{vim} to write a file and quit. **
-!! NOTE: Before executing any of the steps below, read this entire lesson !!
+!! NOTE: Before executing any of the steps below, read the entire lesson !!
- 1. Exit this tutor as you did in Lesson 1.2: `:q!`{vim}
+ 1. Exit this tutorial as you did in Lesson 1.2: `:q!`{vim}
Or, if you have access to another terminal, do the following there.
2. At the shell prompt type this command:
@@ -167,28 +167,28 @@ There is also some text missing here.
3. Insert and delete text as you learned in the previous lessons.
- 4. Save the file with changes and exit Vim with:
+ 4. Save the file with changes and exit Neovim with:
~~~ cmd
:wq
~~~
Note you'll need to press `<Enter>` to execute the command.
- 5. If you have quit vimtutor in step 1 restart the vimtutor and move down
+ 5. If you have quit this tutorial in step 1, restart and move down
to the following summary.
- 6. After reading the above steps and understanding them: do it.
+ 6. After reading and understanding the above steps: do them.
# Lesson 1 SUMMARY
1. The cursor is moved using either the arrow keys or the hjkl keys.
h (left) j (down) k (up) l (right)
- 2. To start Vim from the shell prompt type:
+ 2. To start Neovim from the shell prompt type:
~~~ sh
$ nvim FILENAME
~~~
- 3. To exit Vim type: `<Esc>`{normal} `:q!`{vim} `<Enter>`{normal} to trash all changes.
- OR type: `<Esc>`{normal} `:wq`{vim} `<Enter>`{normal} to save the changes.
+ 3. To exit Neovim type: `<Esc>`{normal} `:q!`{vim} `<Enter>`{normal} to trash all changes.
+ OR type: `<Esc>`{normal} `:wq`{vim} `<Enter>`{normal} to save the changes.
4. To delete the character at the cursor type: `x`{normal}
@@ -297,8 +297,11 @@ Due to the frequency of whole line deletion, the designers of Vi decided
it would be easier to simply type two d's to delete a line.
1. Move the cursor to the second line in the phrase below.
+
2. Type [dd](dd) to delete the line.
+
3. Now move to the fourth line.
+
4. Type `2dd`{normal} to delete two lines.
1) Roses are red,
@@ -314,11 +317,17 @@ it would be easier to simply type two d's to delete a line.
** Press `u`{normal} to undo the last commands, `U`{normal} to fix a whole line. **
1. Move the cursor to the line below marked ✗ and place it on the first error.
+
2. Type `x`{normal} to delete the first unwanted character.
+
3. Now type `u`{normal} to undo the last command executed.
+
4. This time fix all the errors on the line using the `x`{normal} command.
+
5. Now type a capital `U`{normal} to return the line to its original state.
+
6. Now type `u`{normal} a few times to undo the `U`{normal} and preceding commands.
+
7. Now type `<C-r>`{normal} (Control + R) a few times to redo the commands.
Fiix the errors oon thhis line and reeplace them witth undo.
@@ -328,8 +337,11 @@ Fiix the errors oon thhis line and reeplace them witth undo.
# Lesson 2 SUMMARY
1. To delete from the cursor up to the next word type: `dw`{normal}
+
2. To delete from the cursor to the end of a line type: `d$`{normal}
+
3. To delete a whole line type: `dd`{normal}
+
4. To repeat a motion prepend it with a number: `2w`{normal}
5. The format for a change command is:
@@ -356,7 +368,7 @@ Fiix the errors oon thhis line and reeplace them witth undo.
1. Move the cursor to the first ✓ line below.
- 2. Type `dd`{normal} to delete the line and store it in a Vim register.
+ 2. Type `dd`{normal} to delete the line and store it in a Neovim register.
3. Move the cursor to the c) line, ABOVE where the deleted line should go.
@@ -369,6 +381,8 @@ b) Violets are blue,
c) Intelligence is learned,
a) Roses are red,
+NOTE: You can also put the text before the cursor with `P`{normal} (capital P)
+
# Lesson 3.2: THE REPLACE COMMAND
** Type `rx`{normal} to replace the character at the cursor with x. **
@@ -386,7 +400,7 @@ When this line was typed in, someone pressed some wrong keys!
5. Now move on to Lesson 3.3.
-NOTE: Remember that you should be learning by doing, not memorization.
+NOTE: Remember that you should be learning by doing, not memorizing.
# Lesson 3.3: THE CHANGE OPERATOR
@@ -439,7 +453,7 @@ NOTE: You can use the Backspace key to correct mistakes while typing.
3. The [change operator](c) allows you to change from the cursor to where
the motion takes you. Type `ce`{normal} to change from the cursor to the
- end of the word, `c$`{normal} to change to the end of a line.
+ end of the word, `c$`{normal} to change to the end of a line, etc.
4. The format for change is:
@@ -452,7 +466,7 @@ Now go on to the next lesson.
** Type `<C-g>`{normal} to show your location in a file and the file status.
Type `G`{normal} to move to a line in the file. **
-NOTE: Read this entire lesson before executing any of the steps!!
+NOTE: Read the entire lesson before executing any of these steps!!
1. Hold down the `<Ctrl>`{normal} key and press `g`{normal}. We call this `<C-g>`{normal}.
A message will appear at the bottom of the page with the filename and
@@ -535,8 +549,8 @@ Usually thee best time to see thee flowers is in thee spring.
~~~ cmd
:#,#s/old/new/g
~~~
- where #,# are the line numbers of the range of lines where the
- substitution is to be done.
+ where # are the line numbers of the range of lines where the
+ substitution is to be done (i.e., `1,3` means from line 1 to line 3, inclusive).
Type
~~~ cmd
@@ -551,6 +565,9 @@ Usually thee best time to see thee flowers is in thee spring.
to find every occurrence in the whole file, with a prompt whether to
substitute or not.
+NOTE: You can also select the lines you want to substitute first using visual-mode.
+ This will be explained more in a future lesson.
+
# Lesson 4 SUMMARY
1. `<C-g>`{normal} displays your location and the file status.
@@ -603,11 +620,10 @@ Usually thee best time to see thee flowers is in thee spring.
This will show you a listing of your directory, just as if you were
at the shell prompt.
-NOTE: It is possible to execute any external command this way, also with
- arguments.
+NOTE: It is possible to execute any external command this way, and you
+ can include arguments.
-NOTE: All `:`{vim} commands must be finished by hitting `<Enter>`{normal}.
- From here on we will not always mention it.
+NOTE: All `:`{vim} commands are executed when you press `<Enter>`{normal}.
# Lesson 5.2: MORE ON WRITING FILES
@@ -624,11 +640,11 @@ NOTE: All `:`{vim} commands must be finished by hitting `<Enter>`{normal}.
~~~
(where TEST is the filename you chose.)
- 4. This saves the whole file (the Vim Tutor) under the name TEST.
+ 4. This saves the current file under the name TEST.
To verify this, type `:!ls`{vim} again to see your directory.
-NOTE: If you were to exit Vim and start it again with `nvim TEST`, the file
- would be an exact copy of the tutor when you saved it.
+NOTE: If you were to exit Neovim and start it again with `nvim TEST`, the file
+ would be an exact copy of the tutorial when you saved it.
5. Now remove the file by typing:
~~~ cmd
@@ -659,7 +675,7 @@ NOTE: If you were to exit Vim and start it again with `nvim TEST`, the file
before you press `<Enter>`{normal}.
- 5. Vim will write the selected lines to the file TEST. Use `:!ls`{vim} to see it.
+ 5. Neovim will write the selected lines to the file TEST. Use `:!ls`{vim} to see it.
Do not remove it yet! We will use it in the next lesson.
NOTE: Pressing [v](v) starts [Visual selection](visual-mode). You can move the cursor around to
@@ -668,7 +684,7 @@ NOTE: Pressing [v](v) starts [Visual selection](visual-mode). You can move the c
# Lesson 5.4: RETRIEVING AND MERGING FILES
-** To insert the contents of a file, type `:r FILENAME`{vim}. **
+** To retrieve the contents of a file, type `:r FILENAME`{vim}. **
1. Place the cursor just above this line.
@@ -683,7 +699,7 @@ NOTE: After executing Step 2 you will see text from Lesson 5.3. Then move
The file you retrieve is placed below the cursor line.
3. To verify that a file was retrieved, cursor back and notice that there
- are now two copies of Lesson 5.3, the original and the file version.
+ are now two copies of Lesson 5.3, the original and the retrieved version.
NOTE: You can also read the output of an external command. For example,
@@ -699,7 +715,7 @@ NOTE: You can also read the output of an external command. For example,
`:!ls`{vim} - shows a directory listing
`:!rm FILENAME`{vim} - removes file FILENAME
- 2. [:w](:w) FILENAME writes the current Vim file to disk with
+ 2. [:w](:w) FILENAME writes the current Neovim file to disk with
name FILENAME.
3. [v](v) motion :w FILENAME saves the Visually selected lines in file
@@ -768,11 +784,11 @@ Adding 123 to xxx gives you xxx.
Adding 123 to 456 gives you 579.
NOTE: Replace mode is like Insert mode, but every typed character
- deletes an existing character.
+ replaces an existing character.
# Lesson 6.4: COPY AND PASTE TEXT
-** Use the `y`{normal} operator to copy text and `p`{normal} to paste it. **
+** Use the `y`{normal} operator to copy text and `p`{normal} to put it. **
1. Go to the line marked with ✓ below and place the cursor after "a)".
@@ -796,9 +812,13 @@ b)
NOTE: you can use `y`{normal} as an operator: `yw`{normal} yanks one word.
+NOTE: you can use `P`{normal} to put before the cursor, rather than after.
+
# Lesson 6.5: SET OPTION
-** Set an option so a search or substitute ignores case. **
+** Set an option so search and substitute commands ignore case. **
+
+There are many settings in Neovim that you can configure to suit your needs.
1. Search for 'ignore' by entering: `/ignore`
Repeat several times by pressing `n`{normal}.
@@ -820,7 +840,7 @@ NOTE: you can use `y`{normal} as an operator: `yw`{normal} yanks one word.
~~~ cmd
:set noic
~~~
- 7. To toggle the value of a setting, prepend it with "inv":
+ 7. To invert the value of a setting, prepend it with "inv":
~~~ cmd
:set invic
~~~
@@ -858,19 +878,18 @@ NOTE: If you want to ignore case for just one search command, use [\c](/\c)
~~~ cmd
:set noic
~~~
- 8. Prepend "inv" to toggle an option:
+ 8. Prepend "inv" to invert an option:
~~~ cmd
:set invic
~~~
# Lesson 7.1: GETTING HELP
-** Use the on-line help system. **
+** Use the online help system. **
-Vim has a comprehensive on-line help system.
+Neovim has a comprehensive online help system.
To get started, try one of these three:
- - press the `<HELP>`{normal} key (if you have one)
- press the `<F1>`{normal} key (if you have one)
- type `:help`{vim}
@@ -879,7 +898,7 @@ Type `<C-w><C-w>`{normal} to jump from one window to another.
Type `:q`{vim} to close the help window.
You can find help on just about any subject, by giving an argument to the
-":help" command. Try these (don't forget pressing <Enter>):
+":help" command. Try these (don't forget to press <Enter>):
~~~ cmd
:help w
:help c_CTRL-D
@@ -888,12 +907,12 @@ You can find help on just about any subject, by giving an argument to the
~~~
# Lesson 7.2: CREATE A STARTUP SCRIPT
-** Enable Vim features. **
+** Enable Neovim features. **
-Vim has many more features than Vi, but most of them are disabled by
-default. To start using more features you have to create a "vimrc" file.
+Neovim is a very configurable editor. You can customise it any way you like.
+To start using more features create an "init.vim" file.
- 1. Start editing the "vimrc" file.
+ 1. Start editing the "init.vim" file.
`:call mkdir(stdpath('config'),'p')`{vim}
`:exe 'edit' stdpath('config').'/init.vim'`{vim}
@@ -902,24 +921,24 @@ default. To start using more features you have to create a "vimrc" file.
`:w`{vim}
- You can add all your preferred settings to this "vimrc" file.
- For more information type `:help vimrc-intro`{vim}.
+ You can add all your preferred settings to this "init.vim" file.
+ For more information type `:help init.vim`{vim}.
# Lesson 7.3: COMPLETION
** Command line completion with `<C-d>`{normal} and `<Tab>`{normal}. **
- 1. Look what files exist in the directory: `:!ls`{vim}
+ 1. List the contents of the current directory: `:!ls`{vim}
2. Type the start of a command: `:e`{vim}
- 3. Press `<C-d>`{normal} and Vim will show a list of commands beginning with "e".
+ 3. Press `<C-d>`{normal} and Neovim will show a list of commands beginning with "e".
- 4. Press `<Tab>`{normal} and Vim will complete the command name to ":edit".
+ 4. Press `<Tab>`{normal} and Neovim will complete the command name to ":edit".
5. Now add a space and the start of an existing file name: `:edit FIL`{vim}
- 6. Press `<Tab>`{normal}. Vim will complete the name ("FIL" -> "FILE", if it is unique).
+ 6. Press `<Tab>`{normal}. Neovim will complete the name ("FIL" -> "FILE", if it is unique).
NOTE: Completion works for many commands. It is especially useful for `:help`{vim}.
@@ -934,19 +953,18 @@ NOTE: Completion works for many commands. It is especially useful for `:help`{vi
4. Type `:q`{vim} to close the help window
- 5. Create a vimrc startup script to keep your preferred settings.
+ 5. Create an init.vim startup script to keep your preferred settings.
6. While in command mode, press `<C-d>`{normal} to see possible completions.
Press `<Tab>`{normal} to use one completion.
# CONCLUSION
-This was intended to give a brief overview of the Vim editor, just enough to
-allow you to use the editor fairly easily. It is far from complete as Vim has
+This was intended to give a brief overview of the Neovim editor, just enough to
+allow you to use it fairly easily. It is far from complete as Neovim has
many many more commands. Consult the help often.
-
-There are many resources online to learn more about vim. Here's a bunch of
-them:
+There are also countless great tutorials and videos to be found online.
+Here's a bunch of them:
- *Learn Vim Progressively*:
http://yannesposito.com/Scratch/en/blog/Learn-Vim-Progressively/
@@ -964,7 +982,7 @@ them:
https://github.com/mhinz/vim-galore
If you prefer a book, *Practical Vim* by Drew Neil is recommended often
-(the sequel, *Modern Vim*, includes material specific to nvim).
+(the sequel, *Modern Vim*, includes material specific to Neovim).
This tutorial was written by Michael C. Pierce and Robert K. Ware, Colorado
School of Mines using ideas supplied by Charles Smith, Colorado State
@@ -972,5 +990,6 @@ University. E-mail: bware@mines.colorado.edu.
Modified for Vim by Bram Moolenaar.
Modified for vim-tutor-mode by Felipe Morales.
+Modified for Neovim by Rory Nesbitt.
// vim: nowrap
diff --git a/runtime/tutor/en/vim-01-beginner.tutor.json b/runtime/tutor/en/vim-01-beginner.tutor.json
index e71ead976d..5dccb824e0 100644
--- a/runtime/tutor/en/vim-01-beginner.tutor.json
+++ b/runtime/tutor/en/vim-01-beginner.tutor.json
@@ -12,34 +12,34 @@
"232": "Somebody typed the end of this line twice.",
"271": -1,
"290": "This line of words is cleaned up.",
- "304": -1,
- "305": -1,
- "306": -1,
"307": -1,
"308": -1,
"309": -1,
"310": -1,
- "324": "Fix the errors on this line and replace them with undo.",
- "367": -1,
- "368": -1,
- "369": -1,
- "370": -1,
- "384": "When this line was typed in, someone pressed some wrong keys!",
- "385": "When this line was typed in, someone pressed some wrong keys!",
- "405": "This line has a few words that need changing using the change operator.",
- "406": "This line has a few words that need changing using the change operator.",
- "426": "The end of this line needs to be corrected using the `c$` command.",
- "427": "The end of this line needs to be corrected using the `c$` command.",
- "490": -1,
- "509": -1,
- "532": "Usually the best time to see the flowers is in the spring.",
- "725": -1,
- "730": -1,
- "746": "This line will allow you to practice appending text to a line.",
- "747": "This line will allow you to practice appending text to a line.",
- "767": "Adding 123 to 456 gives you 579.",
- "768": "Adding 123 to 456 gives you 579.",
- "794": "a) This is the first item.",
- "795": "b) This is the second item."
+ "311": -1,
+ "312": -1,
+ "313": -1,
+ "333": "Fix the errors on this line and replace them with undo.",
+ "379": -1,
+ "380": -1,
+ "381": -1,
+ "382": -1,
+ "398": "When this line was typed in, someone pressed some wrong keys!",
+ "399": "When this line was typed in, someone pressed some wrong keys!",
+ "419": "This line has a few words that need changing using the change operator.",
+ "420": "This line has a few words that need changing using the change operator.",
+ "440": "The end of this line needs to be corrected using the `c$` command.",
+ "441": "The end of this line needs to be corrected using the `c$` command.",
+ "504": -1,
+ "523": -1,
+ "546": "Usually the best time to see the flowers is in the spring.",
+ "741": -1,
+ "746": -1,
+ "762": "This line will allow you to practice appending text to a line.",
+ "763": "This line will allow you to practice appending text to a line.",
+ "783": "Adding 123 to 456 gives you 579.",
+ "784": "Adding 123 to 456 gives you 579.",
+ "810": "a) This is the first item.",
+ "811": "b) This is the second item."
}
}
diff --git a/scripts/bump-deps.sh b/scripts/bump-deps.sh
new file mode 100755
index 0000000000..85c7f72700
--- /dev/null
+++ b/scripts/bump-deps.sh
@@ -0,0 +1,104 @@
+#!/usr/bin/env bash
+set -e
+set -u
+# Use privileged mode, which e.g. skips using CDPATH.
+set -p
+
+# Ensure that the user has a bash that supports -A
+if [[ "${BASH_VERSINFO[0]}" -lt 4 ]]; then
+ echo >&2 "error: script requires bash 4+ (you have ${BASH_VERSION})."
+ exit 1
+fi
+
+readonly NVIM_SOURCE_DIR="${NVIM_SOURCE_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
+readonly VIM_SOURCE_DIR_DEFAULT="${NVIM_SOURCE_DIR}/.vim-src"
+readonly VIM_SOURCE_DIR="${VIM_SOURCE_DIR:-${VIM_SOURCE_DIR_DEFAULT}}"
+BASENAME="$(basename "${0}")"
+readonly BASENAME
+
+usage() {
+ echo "Bump Neovim dependencies"
+ echo
+ echo "Usage: ${BASENAME} [ -h | --pr | --branch=<dep> | --dep=<dependency> ]"
+ echo
+ echo "Options:"
+ echo " -h show this message and exit."
+ echo " --pr submit pr for bumping deps."
+ echo " --branch=<dep> create a branch bump-<dep> from current branch."
+ echo " --dep=<dependency> bump to a specific release or tag."
+ echo
+ echo "Dependency Options:"
+ echo " --version=<tag> bump to a specific release or tag."
+ echo " --commit=<hash> bump to a specific commit."
+ echo " --HEAD bump to a current head."
+ echo
+ echo " <dependency> is one of:"
+ echo " \"LuaJIT\", \"libuv\", \"Luv\", \"tree-sitter\""
+}
+
+# Checks if a program is in the user's PATH, and is executable.
+check_executable() {
+ test -x "$(command -v "${1}")"
+}
+
+require_executable() {
+ if ! check_executable "${1}"; then
+ echo >&2 "${BASENAME}: '${1}' not found in PATH or not executable."
+ exit 1
+ fi
+}
+
+require_executable "nvim"
+
+if [ $# -eq 0 ]; then
+ usage
+ exit 1
+fi
+
+PARSED_ARGS=$(getopt -a -n "$BASENAME" -o h --long pr,branch:,dep:,version:,commit:,HEAD -- "$@")
+
+DEPENDENCY=""
+eval set -- "$PARSED_ARGS"
+while :; do
+ case "$1" in
+ -h)
+ usage
+ exit 0
+ ;;
+ --pr)
+ nvim -es +"lua require('scripts.bump_deps').submit_pr()"
+ exit 0
+ ;;
+ --branch)
+ DEP=$2
+ nvim -es +"lua require('scripts.bump_deps').create_branch('$DEP')"
+ exit 0
+ ;;
+ --dep)
+ DEPENDENCY=$2
+ shift 2
+ ;;
+ --version)
+ VERSION=$2
+ nvim -es +"lua require('scripts.bump_deps').version('$DEPENDENCY', '$VERSION')"
+ exit 0
+ ;;
+ --commit)
+ COMMIT=$2
+ nvim -es +"lua require('scripts.bump_deps').commit('$DEPENDENCY', '$COMMIT')"
+ exit 0
+ ;;
+ --HEAD)
+ nvim -es +"lua require('scripts.bump_deps').head('$DEPENDENCY')"
+ exit 0
+ ;;
+ *)
+ break
+ ;;
+ esac
+done
+
+usage
+exit 1
+
+# vim: et sw=2
diff --git a/scripts/bump_deps.lua b/scripts/bump_deps.lua
new file mode 100644
index 0000000000..17e3fd35d6
--- /dev/null
+++ b/scripts/bump_deps.lua
@@ -0,0 +1,343 @@
+-- Usage:
+-- # bump to version
+-- nvim -es +"lua require('scripts.bump_deps').version(dependency, version_tag)"
+--
+-- # bump to commit
+-- nvim -es +"lua require('scripts.bump_deps').commit(dependency, commit_hash)"
+--
+-- # bump to HEAD
+-- nvim -es +"lua require('scripts.bump_deps').head(dependency)"
+--
+-- # submit PR
+-- nvim -es +"lua require('scripts.bump_deps').submit_pr()"
+--
+-- # create branch
+-- nvim -es +"lua require('scripts.bump_deps').create_branch()"
+
+local M = {}
+
+local _trace = false
+local required_branch_prefix = "bump-"
+local commit_prefix = "build(deps): "
+
+-- Print message
+local function p(s)
+ vim.cmd("set verbose=1")
+ vim.api.nvim_echo({ { s, "" } }, false, {})
+ vim.cmd("set verbose=0")
+end
+
+local function die()
+ p("")
+ vim.cmd("cquit 1")
+end
+
+-- Executes and returns the output of `cmd`, or nil on failure.
+-- if die_on_fail is true, process dies with die_msg on failure
+--
+-- Prints `cmd` if `trace` is enabled.
+local function _run(cmd, die_on_fail, die_msg)
+ if _trace then
+ p("run: " .. vim.inspect(cmd))
+ end
+ local rv = vim.trim(vim.fn.system(cmd)) or ""
+ if vim.v.shell_error ~= 0 then
+ if die_on_fail then
+ if _trace then
+ p(rv)
+ end
+ p(die_msg)
+ die()
+ end
+ return nil
+ end
+ return rv
+end
+
+-- Run a command, return nil on failure
+local function run(cmd)
+ return _run(cmd, false, "")
+end
+
+-- Run a command, die on failure with err_msg
+local function run_die(cmd, err_msg)
+ return _run(cmd, true, err_msg)
+end
+
+local function require_executable(cmd)
+ local cmd_path = run_die({ "command", "-v", cmd }, cmd .. " not found!")
+ run_die({ "test", "-x", cmd_path }, cmd .. " is not executable")
+end
+
+local function rm_file_if_present(path_to_file)
+ run({ "rm", "-f", path_to_file })
+end
+
+local nvim_src_dir = vim.fn.getcwd()
+local temp_dir = nvim_src_dir .. "/tmp"
+run({ "mkdir", "-p", temp_dir })
+
+local function get_dependency(dependency_name)
+ local dependency_table = {
+ ["LuaJIT"] = {
+ repo = "LuaJIT/LuaJIT",
+ symbol = "LUAJIT",
+ },
+ ["libuv"] = {
+ repo = "libuv/libuv",
+ symbol = "LIBUV",
+ },
+ ["Luv"] = {
+ repo = "luvit/luv",
+ symbol = "LUV",
+ },
+ ["tree-sitter"] = {
+ repo = "tree-sitter/tree-sitter",
+ symbol = "TREESITTER",
+ },
+ }
+ local dependency = dependency_table[dependency_name]
+ if dependency == nil then
+ p("Not a dependency: " .. dependency_name)
+ die()
+ end
+ dependency.name = dependency_name
+ return dependency
+end
+
+local function get_gh_commit_sha(repo, ref)
+ require_executable("gh")
+
+ local sha = run_die(
+ { "gh", "api", "repos/" .. repo .. "/commits/" .. ref, "--jq", ".sha" },
+ "Failed to get commit hash from GitHub. Not a valid ref?"
+ )
+ return sha
+end
+
+local function get_archive_info(repo, ref)
+ require_executable("curl")
+
+ local archive_name = ref .. ".tar.gz"
+ local archive_path = temp_dir .. "/" .. archive_name
+ local archive_url = "https://github.com/" .. repo .. "/archive/" .. archive_name
+
+ rm_file_if_present(archive_path)
+ run_die({ "curl", "-sL", archive_url, "-o", archive_path }, "Failed to download archive from GitHub")
+
+ local archive_sha = run({ "sha256sum", archive_path }):gmatch("%w+")()
+ return { url = archive_url, sha = archive_sha }
+end
+
+local function write_cmakelists_line(symbol, kind, value)
+ require_executable("sed")
+
+ local cmakelists_path = nvim_src_dir .. "/" .. "cmake.deps/CMakeLists.txt"
+ run_die({
+ "sed",
+ "-i",
+ "-e",
+ "s/set(" .. symbol .. "_" .. kind .. ".*$" .. "/set(" .. symbol .. "_" .. kind .. " " .. value .. ")" .. "/",
+ cmakelists_path,
+ }, "Failed to write " .. cmakelists_path)
+end
+
+local function explicit_create_branch(dep)
+ require_executable("git")
+
+ local checked_out_branch = run({ "git", "rev-parse", "--abbrev-ref", "HEAD" })
+ if checked_out_branch ~= "master" then
+ p("Not on master!")
+ die()
+ end
+ run_die({ "git", "checkout", "-b", "bump-" .. dep }, "git failed to create branch")
+end
+
+local function verify_branch(new_branch_suffix)
+ require_executable("git")
+
+ local checked_out_branch = run({ "git", "rev-parse", "--abbrev-ref", "HEAD" })
+ if not checked_out_branch:match("^" .. required_branch_prefix) then
+ p("Current branch '" .. checked_out_branch .. "' doesn't seem to start with " .. required_branch_prefix)
+ p("Checking out to bump-" .. new_branch_suffix)
+ explicit_create_branch(new_branch_suffix)
+ end
+end
+
+local function update_cmakelists(dependency, archive, comment)
+ require_executable("git")
+
+ verify_branch(dependency.name)
+
+ local changed_file = nvim_src_dir .. "/" .. "cmake.deps/CMakeLists.txt"
+
+ p("Updating " .. dependency.name .. " to " .. archive.url .. "\n")
+ write_cmakelists_line(dependency.symbol, "URL", archive.url:gsub("/", "\\/"))
+ write_cmakelists_line(dependency.symbol, "SHA256", archive.sha)
+ run_die(
+ { "git", "commit", changed_file, "-m", commit_prefix .. "bump " .. dependency.name .. " to " .. comment },
+ "git failed to commit"
+ )
+end
+
+local function verify_cmakelists_committed()
+ require_executable("git")
+
+ local cmakelists_path = nvim_src_dir .. "/" .. "cmake.deps/CMakeLists.txt"
+ run_die({ "git", "diff", "--quiet", "HEAD", "--", cmakelists_path }, cmakelists_path .. " has uncommitted changes")
+end
+
+local function warn_luv_symbol()
+ p("warning: " .. get_dependency("Luv").symbol .. "_VERSION will not be updated")
+end
+
+-- return first 9 chars of commit
+local function short_commit(commit)
+ return string.sub(commit, 1, 9)
+end
+
+-- TODO: remove hardcoded fork
+local function gh_pr(pr_title, pr_body)
+ require_executable("gh")
+
+ local pr_url = run_die({
+ "gh",
+ "pr",
+ "create",
+ "--title",
+ pr_title,
+ "--body",
+ pr_body,
+ }, "Failed to create PR")
+ return pr_url
+end
+
+local function find_git_remote(fork)
+ require_executable("git")
+
+ local remotes = run({ "git", "remote", "-v" })
+ local git_remote = ""
+ for remote in remotes:gmatch("[^\r\n]+") do
+ local words = {}
+ for word in remote:gmatch("%w+") do
+ table.insert(words, word)
+ end
+ local match = words[1]:match("/github.com[:/]neovim/neovim/")
+ if fork == "fork" then
+ match = not match
+ end
+ if match and words[3] == "(fetch)" then
+ git_remote = words[0]
+ break
+ end
+ end
+ if git_remote == "" then
+ git_remote = "origin"
+ end
+ return git_remote
+end
+
+local function create_pr(pr_title, pr_body)
+ require_executable("git")
+
+ local push_first = true
+
+ local checked_out_branch = run({ "git", "rev-parse", "--abbrev-ref", "HEAD" })
+ if push_first then
+ local push_remote = run({ "git", "config", "--get", "branch." .. checked_out_branch .. ".pushRemote" })
+ if push_remote == nil then
+ push_remote = run({ "git", "config", "--get", "remote.pushDefault" })
+ if push_remote == nil then
+ push_remote = run({ "git", "config", "--get", "branch." .. checked_out_branch .. ".remote" })
+ if push_remote == nil or push_remote == find_git_remote(nil) then
+ push_remote = find_git_remote("fork")
+ end
+ end
+ end
+
+ p("Pushing to " .. push_remote .. "/" .. checked_out_branch)
+ run_die({ "git", "push", push_remote, checked_out_branch }, "Git failed to push")
+ end
+
+ local pr_url = gh_pr(pr_title, pr_body)
+ p("\nCreated PR: " .. pr_url .. "\n")
+end
+
+function M.commit(dependency_name, commit)
+ local dependency = get_dependency(dependency_name)
+ verify_cmakelists_committed()
+ local commit_sha = get_gh_commit_sha(dependency.repo, commit)
+ if commit_sha ~= commit then
+ p("Not a commit: " .. commit .. ". Did you mean version?")
+ die()
+ end
+ local archive = get_archive_info(dependency.repo, commit)
+ if dependency_name == "Luv" then
+ warn_luv_symbol()
+ end
+ update_cmakelists(dependency, archive, short_commit(commit))
+end
+
+function M.version(dependency_name, version)
+ local dependency = get_dependency(dependency_name)
+ verify_cmakelists_committed()
+ local commit_sha = get_gh_commit_sha(dependency.repo, version)
+ if commit_sha == version then
+ p("Not a version: " .. version .. ". Did you mean commit?")
+ die()
+ end
+ local archive = get_archive_info(dependency.repo, version)
+ if dependency_name == "Luv" then
+ write_cmakelists_line(dependency.symbol, "VERSION", version)
+ end
+ update_cmakelists(dependency, archive, version)
+end
+
+function M.head(dependency_name)
+ local dependency = get_dependency(dependency_name)
+ verify_cmakelists_committed()
+ local commit_sha = get_gh_commit_sha(dependency.repo, "HEAD")
+ local archive = get_archive_info(dependency.repo, commit_sha)
+ if dependency_name == "Luv" then
+ warn_luv_symbol()
+ end
+ update_cmakelists(dependency, archive, "HEAD - " .. short_commit(commit_sha))
+end
+
+function M.create_branch(dep)
+ explicit_create_branch(dep)
+end
+
+function M.submit_pr()
+ require_executable("git")
+
+ verify_branch("deps")
+
+ local nvim_remote = find_git_remote(nil)
+ local relevant_commit = run_die({
+ "git",
+ "log",
+ "--grep=" .. commit_prefix,
+ "--reverse",
+ "--format='%s'",
+ nvim_remote .. "/master..HEAD",
+ "-1",
+ }, "Failed to fetch commits")
+
+ local pr_title
+ local pr_body
+
+ if relevant_commit == "" then
+ pr_title = commit_prefix .. "bump some dependencies"
+ pr_body = "bump some dependencies"
+ else
+ relevant_commit = relevant_commit:gsub("'", "")
+ pr_title = relevant_commit
+ pr_body = relevant_commit:gsub(commit_prefix:gsub("%(", "%%("):gsub("%)", "%%)"), "")
+ end
+ pr_body = pr_body .. "\n\n(add explanations if needed)"
+ p(pr_title .. "\n" .. pr_body .. "\n")
+ create_pr(pr_title, pr_body)
+end
+
+return M
diff --git a/scripts/download-unicode-files.sh b/scripts/download-unicode-files.sh
index 12474d3c1e..4482cefa34 100755
--- a/scripts/download-unicode-files.sh
+++ b/scripts/download-unicode-files.sh
@@ -4,7 +4,7 @@ set -e
data_files="UnicodeData.txt CaseFolding.txt EastAsianWidth.txt"
emoji_files="emoji-data.txt"
-UNIDIR_DEFAULT=unicode
+UNIDIR_DEFAULT=src/unicode
DOWNLOAD_URL_BASE_DEFAULT='http://unicode.org/Public'
if test x$1 = 'x--help' ; then
@@ -39,5 +39,5 @@ done
(
cd "$UNIDIR"
- git commit -m "Update unicode files" -- $files
+ git commit -m "feat: update unicode tables" -- $files
)
diff --git a/scripts/gen_vimdoc.py b/scripts/gen_vimdoc.py
index 3e9fb21039..22fd155d32 100755
--- a/scripts/gen_vimdoc.py
+++ b/scripts/gen_vimdoc.py
@@ -52,12 +52,21 @@ import logging
from xml.dom import minidom
-MIN_PYTHON_VERSION = (3, 5)
+MIN_PYTHON_VERSION = (3, 6)
+MIN_DOXYGEN_VERSION = (1, 9, 0)
if sys.version_info < MIN_PYTHON_VERSION:
print("requires Python {}.{}+".format(*MIN_PYTHON_VERSION))
sys.exit(1)
+doxygen_version = tuple((int(i) for i in subprocess.check_output(["doxygen", "-v"],
+ universal_newlines=True).split()[0].split('.')))
+
+if doxygen_version < MIN_DOXYGEN_VERSION:
+ print("\nRequires doxygen {}.{}.{}+".format(*MIN_DOXYGEN_VERSION))
+ print("Your doxygen version is {}.{}.{}\n".format(*doxygen_version))
+ sys.exit(1)
+
# DEBUG = ('DEBUG' in os.environ)
INCLUDE_C_DECL = ('INCLUDE_C_DECL' in os.environ)
INCLUDE_DEPRECATED = ('INCLUDE_DEPRECATED' in os.environ)
@@ -70,13 +79,11 @@ LOG_LEVELS = {
]
}
-fmt_vimhelp = False # HACK
text_width = 78
script_path = os.path.abspath(__file__)
base_dir = os.path.dirname(os.path.dirname(script_path))
out_dir = os.path.join(base_dir, 'tmp-{target}-doc')
filter_cmd = '%s %s' % (sys.executable, script_path)
-seen_funcs = set()
msgs = [] # Messages to show on exit.
lua2dox_filter = os.path.join(base_dir, 'scripts', 'lua2dox_filter')
@@ -84,22 +91,22 @@ CONFIG = {
'api': {
'mode': 'c',
'filename': 'api.txt',
- # String used to find the start of the generated part of the doc.
- 'section_start_token': '*api-global*',
# Section ordering.
'section_order': [
'vim.c',
'vimscript.c',
+ 'command.c',
+ 'options.c',
'buffer.c',
'extmark.c',
'window.c',
'win_config.c',
'tabpage.c',
+ 'autocmd.c',
'ui.c',
- 'extmark.c',
],
- # List of files/directories for doxygen to read, separated by blanks
- 'files': os.path.join(base_dir, 'src/nvim/api'),
+ # List of files/directories for doxygen to read, relative to `base_dir`
+ 'files': ['src/nvim/api'],
# file patterns used by doxygen
'file_patterns': '*.h *.c',
# Only function with this prefix are considered
@@ -122,31 +129,41 @@ CONFIG = {
'lua': {
'mode': 'lua',
'filename': 'lua.txt',
- 'section_start_token': '*lua-vim*',
'section_order': [
- 'vim.lua',
+ '_editor.lua',
'shared.lua',
'uri.lua',
'ui.lua',
'filetype.lua',
'keymap.lua',
+ 'fs.lua',
+ ],
+ 'files': [
+ 'runtime/lua/vim/_editor.lua',
+ 'runtime/lua/vim/shared.lua',
+ 'runtime/lua/vim/uri.lua',
+ 'runtime/lua/vim/ui.lua',
+ 'runtime/lua/vim/filetype.lua',
+ 'runtime/lua/vim/keymap.lua',
+ 'runtime/lua/vim/fs.lua',
],
- 'files': ' '.join([
- os.path.join(base_dir, 'src/nvim/lua/vim.lua'),
- os.path.join(base_dir, 'runtime/lua/vim/shared.lua'),
- os.path.join(base_dir, 'runtime/lua/vim/uri.lua'),
- os.path.join(base_dir, 'runtime/lua/vim/ui.lua'),
- os.path.join(base_dir, 'runtime/lua/vim/filetype.lua'),
- os.path.join(base_dir, 'runtime/lua/vim/keymap.lua'),
- ]),
'file_patterns': '*.lua',
'fn_name_prefix': '',
'section_name': {
'lsp.lua': 'core',
},
- 'section_fmt': lambda name: f'Lua module: {name.lower()}',
- 'helptag_fmt': lambda name: f'*lua-{name.lower()}*',
- 'fn_helptag_fmt': lambda fstem, name: f'*{fstem}.{name}()*',
+ 'section_fmt': lambda name: (
+ 'Lua module: vim'
+ if name.lower() == '_editor'
+ else f'Lua module: {name.lower()}'),
+ 'helptag_fmt': lambda name: (
+ '*lua-vim*'
+ if name.lower() == '_editor'
+ else f'*lua-{name.lower()}*'),
+ 'fn_helptag_fmt': lambda fstem, name: (
+ f'*vim.{name}()*'
+ if fstem.lower() == '_editor'
+ else f'*{fstem}.{name}()*'),
'module_override': {
# `shared` functions are exposed on the `vim` module.
'shared': 'vim',
@@ -154,6 +171,7 @@ CONFIG = {
'ui': 'vim.ui',
'filetype': 'vim.filetype',
'keymap': 'vim.keymap',
+ 'fs': 'vim.fs',
},
'append_only': [
'shared.lua',
@@ -162,7 +180,6 @@ CONFIG = {
'lsp': {
'mode': 'lua',
'filename': 'lsp.txt',
- 'section_start_token': '*lsp-core*',
'section_order': [
'lsp.lua',
'buf.lua',
@@ -176,10 +193,10 @@ CONFIG = {
'sync.lua',
'protocol.lua',
],
- 'files': ' '.join([
- os.path.join(base_dir, 'runtime/lua/vim/lsp'),
- os.path.join(base_dir, 'runtime/lua/vim/lsp.lua'),
- ]),
+ 'files': [
+ 'runtime/lua/vim/lsp',
+ 'runtime/lua/vim/lsp.lua',
+ ],
'file_patterns': '*.lua',
'fn_name_prefix': '',
'section_name': {'lsp.lua': 'lsp'},
@@ -205,11 +222,10 @@ CONFIG = {
'diagnostic': {
'mode': 'lua',
'filename': 'diagnostic.txt',
- 'section_start_token': '*diagnostic-api*',
'section_order': [
'diagnostic.lua',
],
- 'files': os.path.join(base_dir, 'runtime/lua/vim/diagnostic.lua'),
+ 'files': ['runtime/lua/vim/diagnostic.lua'],
'file_patterns': '*.lua',
'fn_name_prefix': '',
'section_name': {'diagnostic.lua': 'diagnostic'},
@@ -222,7 +238,6 @@ CONFIG = {
'treesitter': {
'mode': 'lua',
'filename': 'treesitter.txt',
- 'section_start_token': '*lua-treesitter-core*',
'section_order': [
'treesitter.lua',
'language.lua',
@@ -230,10 +245,10 @@ CONFIG = {
'highlighter.lua',
'languagetree.lua',
],
- 'files': ' '.join([
- os.path.join(base_dir, 'runtime/lua/vim/treesitter.lua'),
- os.path.join(base_dir, 'runtime/lua/vim/treesitter/'),
- ]),
+ 'files': [
+ 'runtime/lua/vim/treesitter.lua',
+ 'runtime/lua/vim/treesitter/',
+ ],
'file_patterns': '*.lua',
'fn_name_prefix': '',
'section_name': {},
@@ -268,16 +283,13 @@ param_exclude = (
# Annotations are displayed as line items after API function descriptions.
annotation_map = {
- 'FUNC_API_FAST': '{fast}',
+ 'FUNC_API_FAST': '|api-fast|',
'FUNC_API_CHECK_TEXTLOCK': 'not allowed when |textlock| is active',
+ 'FUNC_API_REMOTE_ONLY': '|RPC| only',
+ 'FUNC_API_LUA_ONLY': '|vim.api| only',
}
-# Tracks `xrefsect` titles. As of this writing, used only for separating
-# deprecated functions.
-xrefs = set()
-
-
# Raises an error with details about `o`, if `cond` is in object `o`,
# or if `cond()` is callable and returns True.
def debug_this(o, cond=True):
@@ -340,14 +352,6 @@ def self_or_child(n):
return n.childNodes[0]
-def clean_text(text):
- """Cleans text.
-
- Only cleans superfluous whitespace at the moment.
- """
- return ' '.join(text.split()).strip()
-
-
def clean_lines(text):
"""Removes superfluous lines.
@@ -368,12 +372,12 @@ def get_text(n, preformatted=False):
if n.nodeName == 'computeroutput':
for node in n.childNodes:
text += get_text(node)
- return '`{}` '.format(text)
+ return '`{}`'.format(text)
for node in n.childNodes:
if node.nodeType == node.TEXT_NODE:
- text += node.data if preformatted else clean_text(node.data)
+ text += node.data
elif node.nodeType == node.ELEMENT_NODE:
- text += ' ' + get_text(node, preformatted)
+ text += get_text(node, preformatted)
return text
@@ -479,10 +483,8 @@ def update_params_map(parent, ret_map, width=62):
return ret_map
-def render_node(n, text, prefix='', indent='', width=62):
+def render_node(n, text, prefix='', indent='', width=62, fmt_vimhelp=False):
"""Renders a node as Vim help text, recursively traversing all descendants."""
- global fmt_vimhelp
- global has_seen_preformatted
def ind(s):
return s if fmt_vimhelp else ''
@@ -560,7 +562,7 @@ def render_node(n, text, prefix='', indent='', width=62):
return text
-def para_as_map(parent, indent='', width=62):
+def para_as_map(parent, indent='', width=62, fmt_vimhelp=False):
"""Extracts a Doxygen XML <para> node to a map.
Keys:
@@ -593,7 +595,8 @@ def para_as_map(parent, indent='', width=62):
last = ''
if is_inline(parent):
# Flatten inline text from a tree of non-block nodes.
- text = doc_wrap(render_node(parent, ""), indent=indent, width=width)
+ text = doc_wrap(render_node(parent, "", fmt_vimhelp=fmt_vimhelp),
+ indent=indent, width=width)
else:
prev = None # Previous node
for child in parent.childNodes:
@@ -609,7 +612,8 @@ def para_as_map(parent, indent='', width=62):
elif kind == 'see':
groups['seealso'].append(child)
elif kind in ('note', 'warning'):
- text += render_node(child, text, indent=indent, width=width)
+ text += render_node(child, text, indent=indent,
+ width=width, fmt_vimhelp=fmt_vimhelp)
else:
raise RuntimeError('unhandled simplesect: {}\n{}'.format(
child.nodeName, child.toprettyxml(indent=' ', newl='\n')))
@@ -622,7 +626,8 @@ def para_as_map(parent, indent='', width=62):
and ' ' != text[-1]):
text += ' '
- text += render_node(child, text, indent=indent, width=width)
+ text += render_node(child, text, indent=indent, width=width,
+ fmt_vimhelp=fmt_vimhelp)
prev = child
chunks['text'] += text
@@ -633,10 +638,12 @@ def para_as_map(parent, indent='', width=62):
update_params_map(child, ret_map=chunks['params'], width=width)
for child in groups['return']:
chunks['return'].append(render_node(
- child, '', indent=indent, width=width))
+ child, '', indent=indent, width=width, fmt_vimhelp=fmt_vimhelp))
for child in groups['seealso']:
chunks['seealso'].append(render_node(
- child, '', indent=indent, width=width))
+ child, '', indent=indent, width=width, fmt_vimhelp=fmt_vimhelp))
+
+ xrefs = set()
for child in groups['xrefs']:
# XXX: Add a space (or any char) to `title` here, otherwise xrefs
# ("Deprecated" section) acts very weird...
@@ -646,10 +653,10 @@ def para_as_map(parent, indent='', width=62):
chunks['xrefs'].append(doc_wrap(xrefdesc, prefix='{}: '.format(title),
width=width) + '\n')
- return chunks
+ return chunks, xrefs
-def fmt_node_as_vimhelp(parent, width=62, indent=''):
+def fmt_node_as_vimhelp(parent, width=62, indent='', fmt_vimhelp=False):
"""Renders (nested) Doxygen <para> nodes as Vim :help text.
NB: Blank lines in a docstring manifest as <para> tags.
@@ -672,7 +679,7 @@ def fmt_node_as_vimhelp(parent, width=62, indent=''):
return True
for child in parent.childNodes:
- para = para_as_map(child, indent, width)
+ para, _ = para_as_map(child, indent, width, fmt_vimhelp)
# Generate text from the gathered items.
chunks = [para['text']]
@@ -696,19 +703,16 @@ def fmt_node_as_vimhelp(parent, width=62, indent=''):
return clean_lines('\n'.join(rendered_blocks).strip())
-def extract_from_xml(filename, target, width):
+def extract_from_xml(filename, target, width, fmt_vimhelp):
"""Extracts Doxygen info as maps without formatting the text.
Returns two maps:
1. Functions
2. Deprecated functions
- The `fmt_vimhelp` global controls some special cases for use by
+ The `fmt_vimhelp` variable controls some special cases for use by
fmt_doxygen_xml_as_vimhelp(). (TODO: ugly :)
"""
- global xrefs
- global fmt_vimhelp
- xrefs.clear()
fns = {} # Map of func_name:docstring.
deprecated_fns = {} # Map of func_name:docstring.
@@ -815,16 +819,22 @@ def extract_from_xml(filename, target, width):
signature = prefix + suffix
signature += vimtag.rjust(width - len(signature))
+ # Tracks `xrefsect` titles. As of this writing, used only for separating
+ # deprecated functions.
+ xrefs_all = set()
paras = []
brief_desc = find_first(member, 'briefdescription')
if brief_desc:
for child in brief_desc.childNodes:
- paras.append(para_as_map(child))
+ para, xrefs = para_as_map(child)
+ xrefs_all.update(xrefs)
desc = find_first(member, 'detaileddescription')
if desc:
for child in desc.childNodes:
- paras.append(para_as_map(child))
+ para, xrefs = para_as_map(child)
+ paras.append(para)
+ xrefs_all.update(xrefs)
log.debug(
textwrap.indent(
re.sub(r'\n\s*\n+', '\n',
@@ -840,7 +850,8 @@ def extract_from_xml(filename, target, width):
'seealso': [],
}
if fmt_vimhelp:
- fn['desc_node'] = desc # HACK :(
+ fn['desc_node'] = desc
+ fn['brief_desc_node'] = brief_desc
for m in paras:
if 'text' in m:
@@ -857,18 +868,16 @@ def extract_from_xml(filename, target, width):
if INCLUDE_C_DECL:
fn['c_decl'] = c_decl
- if 'Deprecated' in str(xrefs):
+ if 'Deprecated' in str(xrefs_all):
deprecated_fns[name] = fn
elif name.startswith(CONFIG[target]['fn_name_prefix']):
fns[name] = fn
- xrefs.clear()
-
fns = collections.OrderedDict(sorted(
fns.items(),
key=lambda key_item_tuple: key_item_tuple[0].lower()))
deprecated_fns = collections.OrderedDict(sorted(deprecated_fns.items()))
- return (fns, deprecated_fns)
+ return fns, deprecated_fns
def fmt_doxygen_xml_as_vimhelp(filename, target):
@@ -878,16 +887,16 @@ def fmt_doxygen_xml_as_vimhelp(filename, target):
1. Vim help text for functions found in `filename`.
2. Vim help text for deprecated functions.
"""
- global fmt_vimhelp
- fmt_vimhelp = True
fns_txt = {} # Map of func_name:vim-help-text.
deprecated_fns_txt = {} # Map of func_name:vim-help-text.
- fns, _ = extract_from_xml(filename, target, width=text_width)
+ fns, _ = extract_from_xml(filename, target, text_width, True)
for name, fn in fns.items():
# Generate Vim :help for parameters.
if fn['desc_node']:
- doc = fmt_node_as_vimhelp(fn['desc_node'])
+ doc = fmt_node_as_vimhelp(fn['desc_node'], fmt_vimhelp=True)
+ if not doc and fn['brief_desc_node']:
+ doc = fmt_node_as_vimhelp(fn['brief_desc_node'])
if not doc:
doc = 'TODO: Documentation'
@@ -938,14 +947,9 @@ def fmt_doxygen_xml_as_vimhelp(filename, target):
func_doc = "\n".join(split_lines)
- if 'Deprecated' in xrefs:
- deprecated_fns_txt[name] = func_doc
- elif name.startswith(CONFIG[target]['fn_name_prefix']):
+ if name.startswith(CONFIG[target]['fn_name_prefix']):
fns_txt[name] = func_doc
- xrefs.clear()
-
- fmt_vimhelp = False
return ('\n\n'.join(list(fns_txt.values())),
'\n\n'.join(list(deprecated_fns_txt.values())))
@@ -997,7 +1001,8 @@ def main(config, args):
stderr=(subprocess.STDOUT if debug else subprocess.DEVNULL))
p.communicate(
config.format(
- input=CONFIG[target]['files'],
+ input=' '.join(
+ [f'"{file}"' for file in CONFIG[target]['files']]),
output=output_dir,
filter=filter_cmd,
file_patterns=CONFIG[target]['file_patterns'])
@@ -1048,7 +1053,7 @@ def main(config, args):
xmlfile = os.path.join(base,
'{}.xml'.format(compound.getAttribute('refid')))
# Extract unformatted (*.mpack).
- fn_map, _ = extract_from_xml(xmlfile, target, width=9999)
+ fn_map, _ = extract_from_xml(xmlfile, target, 9999, False)
# Extract formatted (:help).
functions_text, deprecated_text = fmt_doxygen_xml_as_vimhelp(
os.path.join(base, '{}.xml'.format(
@@ -1087,10 +1092,10 @@ def main(config, args):
raise RuntimeError(
'found new modules "{}"; update the "section_order" map'.format(
set(sections).difference(CONFIG[target]['section_order'])))
+ first_section_tag = sections[CONFIG[target]['section_order'][0]][1]
docs = ''
- i = 0
for filename in CONFIG[target]['section_order']:
try:
title, helptag, section_doc = sections.pop(filename)
@@ -1098,7 +1103,6 @@ def main(config, args):
msg(f'warning: empty docs, skipping (target={target}): {filename}')
msg(f' existing docs: {sections.keys()}')
continue
- i += 1
if filename not in CONFIG[target]['append_only']:
docs += sep
docs += '\n%s%s' % (title,
@@ -1112,7 +1116,8 @@ def main(config, args):
doc_file = os.path.join(base_dir, 'runtime', 'doc',
CONFIG[target]['filename'])
- delete_lines_below(doc_file, CONFIG[target]['section_start_token'])
+ if os.path.exists(doc_file):
+ delete_lines_below(doc_file, first_section_tag)
with open(doc_file, 'ab') as fp:
fp.write(docs.encode('utf8'))
diff --git a/scripts/genvimvim.lua b/scripts/genvimvim.lua
index ff60b6cce7..868084a583 100644
--- a/scripts/genvimvim.lua
+++ b/scripts/genvimvim.lua
@@ -44,12 +44,14 @@ local function cmd_kw(prev_cmd, cmd)
end
-- Exclude these from the vimCommand keyword list, they are handled specially
--- in syntax/vim.vim (vimAugroupKey, vimAutoCmd). #9327
-local function is_autocmd_cmd(cmd)
+-- in syntax/vim.vim (vimAugroupKey, vimAutoCmd, vimGlobal, vimSubst). #9327
+local function is_special_cased_cmd(cmd)
return (cmd == 'augroup'
or cmd == 'autocmd'
or cmd == 'doautocmd'
- or cmd == 'doautoall')
+ or cmd == 'doautoall'
+ or cmd == 'global'
+ or cmd == 'substitute')
end
local vimcmd_start = 'syn keyword vimCommand contained '
@@ -60,7 +62,7 @@ for _, cmd_desc in ipairs(ex_cmds.cmds) do
w('\n' .. vimcmd_start)
end
local cmd = cmd_desc.command
- if cmd:match('%w') and cmd ~= 'z' and not is_autocmd_cmd(cmd) then
+ if cmd:match('%w') and cmd ~= 'z' and not is_special_cased_cmd(cmd) then
w(' ' .. cmd_kw(prev_cmd, cmd))
end
prev_cmd = cmd
diff --git a/scripts/lintcommit.lua b/scripts/lintcommit.lua
index 0a7da4d4ef..16326cfe66 100644
--- a/scripts/lintcommit.lua
+++ b/scripts/lintcommit.lua
@@ -45,8 +45,13 @@ end
-- Returns nil if the given commit message is valid, or returns a string
-- message explaining why it is invalid.
local function validate_commit(commit_message)
- local commit_split = vim.split(commit_message, ":")
+ -- Return nil if the commit message starts with "fixup" as it signifies it's
+ -- a work in progress and shouldn't be linted yet.
+ if vim.startswith(commit_message, "fixup") then
+ return nil
+ end
+ local commit_split = vim.split(commit_message, ":")
-- Return nil if the type is vim-patch since most of the normal rules don't
-- apply.
if commit_split[1] == "vim-patch" then
@@ -73,7 +78,7 @@ local function validate_commit(commit_message)
-- Check if type is correct
local type = vim.split(before_colon, "%(")[1]
- local allowed_types = {'build', 'ci', 'docs', 'feat', 'fix', 'perf', 'refactor', 'revert', 'test', 'chore', 'vim-patch'}
+ local allowed_types = {'build', 'ci', 'docs', 'feat', 'fix', 'perf', 'refactor', 'revert', 'test', 'dist', 'vim-patch'}
if not vim.tbl_contains(allowed_types, type) then
return string.format(
'Invalid commit type "%s". Allowed types are:\n %s',
@@ -94,10 +99,24 @@ local function validate_commit(commit_message)
return [[Description ends with a period (".").]]
end
- -- Check that description has exactly one whitespace after colon, followed by
- -- a lowercase letter and then any number of letters.
- if not string.match(after_colon, '^ %l%a*') then
- return [[There should be one whitespace after the colon and the first letter should lowercase.]]
+ -- Check that description starts with a whitespace.
+ if after_colon:sub(1,1) ~= " " then
+ return [[There should be a whitespace after the colon.]]
+ end
+
+ -- Check that description doesn't start with multiple whitespaces.
+ if after_colon:sub(1,2) == " " then
+ return [[There should only be one whitespace after the colon.]]
+ end
+
+ -- Check that first character after space isn't uppercase.
+ if string.match(after_colon:sub(2,2), '%u') then
+ return [[First character should not be uppercase.]]
+ end
+
+ -- Check that description isn't just whitespaces
+ if vim.trim(after_colon) == "" then
+ return [[Description shouldn't be empty.]]
end
return nil
@@ -162,18 +181,23 @@ function M._test()
['refactor: normal message'] = true,
['revert: normal message'] = true,
['test: normal message'] = true,
- ['chore: normal message'] = true,
+ ['dist: normal message'] = true,
['ci(window): message with scope'] = true,
['ci!: message with breaking change'] = true,
['ci(tui)!: message with scope and breaking change'] = true,
['vim-patch:8.2.3374: Pyret files are not recognized (#15642)'] = true,
['vim-patch:8.1.1195,8.2.{3417,3419}'] = true,
+ ['revert: "ci: use continue-on-error instead of "|| true""'] = true,
+ ['fixup'] = true,
+ ['fixup: commit message'] = true,
+ ['fixup! commit message'] = true,
[':no type before colon 1'] = false,
[' :no type before colon 2'] = false,
[' :no type before colon 3'] = false,
['ci(empty description):'] = false,
- ['ci(whitespace as description): '] = false,
+ ['ci(only whitespace as description): '] = false,
['docs(multiple whitespaces as description): '] = false,
+ ['revert(multiple whitespaces and then characters as description): description'] = false,
['ci no colon after type'] = false,
['test: extra space after colon'] = false,
['ci: tab after colon'] = false,
@@ -181,10 +205,10 @@ function M._test()
['ci :extra space before colon'] = false,
['refactor(): empty scope'] = false,
['ci( ): whitespace as scope'] = false,
- ['chore: period at end of sentence.'] = false,
+ ['ci: period at end of sentence.'] = false,
['ci: Starting sentence capitalized'] = false,
['unknown: using unknown type'] = false,
- ['chore: you\'re saying this commit message just goes on and on and on and on and on and on for way too long?'] = false,
+ ['ci: you\'re saying this commit message just goes on and on and on and on and on and on for way too long?'] = false,
}
local failed = 0
diff --git a/scripts/lua2dox.lua b/scripts/lua2dox.lua
index d110e34c6a..6a206066b8 100644
--- a/scripts/lua2dox.lua
+++ b/scripts/lua2dox.lua
@@ -295,7 +295,7 @@ function TStream_Write.writelnTail(this,Line)
table.insert(this.tailLine,Line)
end
---! \brief outout tail lines
+--! \brief output tail lines
function TStream_Write.write_tailLines(this)
for _,line in ipairs(this.tailLine) do
TCore_IO_writeln(line)
@@ -403,6 +403,29 @@ function TLua2DoX_filter.readfile(this,AppStamp,Filename)
if string.sub(line, 3, 3) == '@' or string.sub(line, 1, 4) == '---@' then -- it's a magic comment
state = 'in_magic_comment'
local magic = string.sub(line, 4 + offset)
+
+ local magic_split = string_split(magic, ' ')
+
+ local type_index = 2
+ if magic_split[1] == 'param' then
+ type_index = type_index + 1
+ end
+
+ if magic_split[type_index] == 'number' or
+ magic_split[type_index] == 'number|nil' or
+ magic_split[type_index] == 'string' or
+ magic_split[type_index] == 'string|nil' or
+ magic_split[type_index] == 'table' or
+ magic_split[type_index] == 'table|nil' or
+ magic_split[type_index] == 'boolean' or
+ magic_split[type_index] == 'boolean|nil' or
+ magic_split[type_index] == 'function' or
+ magic_split[type_index] == 'function|nil'
+ then
+ magic_split[type_index] = '(' .. magic_split[type_index] .. ')'
+ end
+ magic = table.concat(magic_split, ' ')
+
outStream:writeln('/// @' .. magic)
fn_magic = checkComment4fn(fn_magic,magic)
elseif string.sub(line,3,3)=='-' then -- it's a nonmagic doc comment
diff --git a/scripts/pvscheck.sh b/scripts/pvscheck.sh
index 195a76763f..a931231fbd 100755
--- a/scripts/pvscheck.sh
+++ b/scripts/pvscheck.sh
@@ -380,7 +380,7 @@ run_analysis() {(
--sourcetree-root . || true
rm -rf PVS-studio.{xml,err,tsk,html.d}
- local plog_args="PVS-studio.log --srcRoot . --excludedCodes V011,V1042,V1051,V1074"
+ local plog_args="PVS-studio.log --srcRoot . --excludedCodes V011,V1042,V1051,V1074,V002"
plog-converter $plog_args --renderTypes xml --output PVS-studio.xml
plog-converter $plog_args --renderTypes errorfile --output PVS-studio.err
plog-converter $plog_args --renderTypes tasklist --output PVS-studio.tsk
diff --git a/scripts/stripdecls.py b/scripts/stripdecls.py
deleted file mode 100755
index b4ac34dcfe..0000000000
--- a/scripts/stripdecls.py
+++ /dev/null
@@ -1,140 +0,0 @@
-#!/usr/bin/python
-# vim: set fileencoding=utf-8:
-
-from __future__ import print_function, unicode_literals, division
-
-from clang.cindex import Index, CursorKind
-from collections import namedtuple, OrderedDict, defaultdict
-import sys
-import os
-
-
-DECL_KINDS = {
- CursorKind.FUNCTION_DECL,
-}
-
-
-Strip = namedtuple('Strip', 'start_line start_column end_line end_column')
-
-
-def main(progname, cfname, only_static, move_all):
- cfname = os.path.abspath(os.path.normpath(cfname))
-
- hfname1 = os.path.splitext(cfname)[0] + os.extsep + 'h'
- hfname2 = os.path.splitext(cfname)[0] + '_defs' + os.extsep + 'h'
-
- files_to_modify = (cfname, hfname1, hfname2)
-
- index = Index.create()
- src_dirname = os.path.join(os.path.dirname(__file__), '..', 'src')
- src_dirname = os.path.abspath(os.path.normpath(src_dirname))
- relname = os.path.join(src_dirname, 'nvim')
- unit = index.parse(cfname, args=('-I' + src_dirname,
- '-DUNIX',
- '-DEXITFREE',
- '-DFEAT_USR_CMDS',
- '-DFEAT_CMDL_COMPL',
- '-DFEAT_COMPL_FUNC',
- '-DPROTO',
- '-DUSE_MCH_ERRMSG'))
- cursor = unit.cursor
-
- tostrip = defaultdict(OrderedDict)
- definitions = set()
-
- for child in cursor.get_children():
- if not (child.location and child.location.file):
- continue
- fname = os.path.abspath(os.path.normpath(child.location.file.name))
- if fname not in files_to_modify:
- continue
- if child.kind not in DECL_KINDS:
- continue
- if only_static and next(child.get_tokens()).spelling == 'static':
- continue
-
- if child.is_definition() and fname == cfname:
- definitions.add(child.spelling)
- else:
- stripdict = tostrip[fname]
- assert(child.spelling not in stripdict)
- stripdict[child.spelling] = Strip(
- child.extent.start.line,
- child.extent.start.column,
- child.extent.end.line,
- child.extent.end.column,
- )
-
- for (fname, stripdict) in tostrip.items():
- if not move_all:
- for name in set(stripdict) - definitions:
- stripdict.pop(name)
-
- if not stripdict:
- continue
-
- if fname.endswith('.h'):
- is_h_file = True
- include_line = next(reversed(stripdict.values())).start_line + 1
- else:
- is_h_file = False
- include_line = next(iter(stripdict.values())).start_line
-
- lines = None
- generated_existed = os.path.exists(fname + '.generated.h')
- with open(fname, 'rb') as F:
- lines = list(F)
-
- stripped = []
-
- for name, position in reversed(stripdict.items()):
- sl = slice(position.start_line - 1, position.end_line)
- if is_h_file:
- include_line -= sl.stop - sl.start
- stripped += lines[sl]
- lines[sl] = ()
-
- if not generated_existed:
- lines[include_line:include_line] = [
- '#ifdef INCLUDE_GENERATED_DECLARATIONS\n',
- '# include "{0}.generated.h"\n'.format(
- os.path.relpath(fname, relname)),
- '#endif\n',
- ]
-
- with open(fname, 'wb') as F:
- F.writelines(lines)
-
-
-if __name__ == '__main__':
- progname = sys.argv[0]
- args = sys.argv[1:]
- if not args or '--help' in args:
- print('Usage:')
- print('')
- print(' {0} [--static [--all]] file.c...'.format(progname))
- print('')
- print('Stripts all declarations from file.c, file.h and file_defs.h.')
- print('If --static argument is given then only static declarations are')
- print('stripped. Declarations are stripped only if corresponding')
- print('definition is found unless --all argument was given.')
- print('')
- print('Note: it is assumed that static declarations starts with "static"')
- print(' keyword.')
- sys.exit(0 if args else 1)
-
- if args[0] == '--static':
- only_static = True
- args = args[1:]
- else:
- only_static = False
-
- if args[0] == '--all':
- move_all = True
- args = args[1:]
- else:
- move_all = False
-
- for cfname in args:
- print('Processing {0}'.format(cfname))
- main(progname, cfname, only_static, move_all)
diff --git a/scripts/uncrustify.sh b/scripts/uncrustify.sh
new file mode 100755
index 0000000000..ac5d542c29
--- /dev/null
+++ b/scripts/uncrustify.sh
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+
+set -e
+
+# Check that you have uncrustify
+hash uncrustify
+
+COMMITISH="${1:-master}"
+for file in $(git diff --diff-filter=d --name-only $COMMITISH | grep '\.[ch]$'); do
+ uncrustify -c src/uncrustify.cfg -l C --replace --no-backup "$file"
+done
diff --git a/scripts/update_terminfo.sh b/scripts/update_terminfo.sh
index 0cfc230ca6..8a0937cc8c 100755
--- a/scripts/update_terminfo.sh
+++ b/scripts/update_terminfo.sh
@@ -64,6 +64,8 @@ cat > "$target" <<EOF
// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+// uncrustify:off
+
//
// Generated by scripts/update_terminfo.sh and $(tic -V)
//
@@ -84,8 +86,8 @@ for term in $sorted_terms; do
infocmp -L -1 -A "$db" "$term" | sed -e '1d' -e 's#^#// #' | tr '\t' ' '
printf 'static const int8_t %s[] = {\n' "${entries[$term]}"
printf ' '
- od -v -t d1 < "$path" | cut -c9- | xargs | tr ' ' ',' | tr -d '\n'
- printf ' // NOLINT\n};\n'
+ od -v -t d1 < "$path" | cut -c9- | xargs | tr ' ' ','
+ printf '};\n'
done >> "$target"
cat >> "$target" <<EOF
diff --git a/scripts/update_version_stamp.lua b/scripts/update_version_stamp.lua
deleted file mode 100755
index 0342e08f31..0000000000
--- a/scripts/update_version_stamp.lua
+++ /dev/null
@@ -1,54 +0,0 @@
-#!/usr/bin/env lua
---
--- Script to update the Git version stamp during build.
--- This is called via the custom update_version_stamp target in
--- src/nvim/CMakeLists.txt.
---
--- arg[1]: file in which to update the version string
--- arg[2]: prefix to use always ("vX.Y.Z")
-
-local function die(msg)
- io.stderr:write(string.format('%s: %s\n', arg[0], msg))
- -- No error, fall back to using generated "-dev" version.
- os.exit(0)
-end
-
-local function iswin()
- return package.config:sub(1,1) == '\\'
-end
-
-if #arg ~= 2 then
- die(string.format("Expected two args, got %d", #arg))
-end
-
-local versiondeffile = arg[1]
-local prefix = arg[2]
-
-local dev_null = iswin() and 'NUL' or '/dev/null'
-local described = io.popen('git describe --first-parent --dirty 2>'..dev_null):read('*l')
-if not described then
- described = io.popen('git describe --first-parent --tags --always --dirty'):read('*l')
-end
-if not described then
- io.open(versiondeffile, 'w'):write('\n')
- die('git-describe failed, using empty include file.')
-end
-
--- `git describe` annotates the most recent tagged release; for pre-release
--- builds we append that to the dev version
-local with_prefix = prefix
-if prefix:match('-dev$') ~= nil then
- with_prefix = prefix .. '+' .. described:gsub('^v%d+%.%d+%.%d+-', '')
-end
-
--- Read existing include file.
-local current = io.open(versiondeffile, 'r')
-if current then
- current = current:read('*l')
-end
-
--- Write new include file, if different.
-local new = '#define NVIM_VERSION_MEDIUM "'..with_prefix..'"'
-if current ~= new then
- io.open(versiondeffile, 'w'):write(new .. '\n')
-end
diff --git a/scripts/vim-patch.sh b/scripts/vim-patch.sh
index 1c265f0f40..d5424f51ab 100755
--- a/scripts/vim-patch.sh
+++ b/scripts/vim-patch.sh
@@ -4,6 +4,8 @@ set -e
set -u
# Use privileged mode, which e.g. skips using CDPATH.
set -p
+# https://www.shellcheck.net/wiki/SC2031
+shopt -s lastpipe
# Ensure that the user has a bash that supports -A
if [[ "${BASH_VERSINFO[0]}" -lt 4 ]]; then
@@ -36,7 +38,7 @@ usage() {
echo " can be a Vim version (8.0.xxx) or a Git hash."
echo " -P {vim-revision} Download, generate and apply a Vim patch."
echo " -g {vim-revision} Download a Vim patch."
- echo " -s Create a vim-patch pull request."
+ echo " -s [pr args] Create a vim-patch pull request."
echo " -r {pr-number} Review a vim-patch pull request."
echo " -V Clone the Vim source code to \$VIM_SOURCE_DIR."
echo
@@ -190,8 +192,8 @@ preprocess_patch() {
2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/\<\%('"${na_files}"'\)\>@norm! d/\v(^diff)|%$ ' +w +q "$file"
# Remove *.proto, Make*, INSTALL*, gui_*, beval.*, some if_*, gvim, libvterm, tee, VisVim, xpm, xxd
- local na_src='auto\|configure.*\|GvimExt\|libvterm\|proto\|tee\|VisVim\|xpm\|xxd\|Make*\|INSTALL*\|beval.*\|gui_*\|if_lua\|if_mzsch\|if_olepp\|if_ole\|if_perl\|if_py\|if_ruby\|if_tcl\|if_xcmdsrv'
- 2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/src/\S*\<\%(testdir/\)\@<!\%('"${na_src}"'\)@norm! d/\v(^diff)|%$ ' +w +q "$file"
+ local na_src='auto\|configure.*\|GvimExt\|libvterm\|proto\|tee\|VisVim\|xpm\|xxd\|Make.*\|INSTALL.*\|beval.*\|gui.*\|if_lua\|if_mzsch\|if_olepp\|if_ole\|if_perl\|if_py\|if_ruby\|if_tcl\|if_xcmdsrv'
+ 2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/src/\S*\<\%(testdir/\)\@<!\%('"${na_src}"'\)\>@norm! d/\v(^diff)|%$ ' +w +q "$file"
# Remove unwanted Vim doc files.
local na_doc='channel\.txt\|netbeans\.txt\|os_\w\+\.txt\|term\.txt\|todo\.txt\|version\d\.txt\|vim9\.txt\|sponsor\.txt\|intro\.txt\|tags'
@@ -201,19 +203,18 @@ preprocess_patch() {
2>/dev/null $nvim --cmd 'set dir=/tmp' +'%s/^@@.*\n.*For Vim version.*Last change.*\n.*For Vim version.*Last change.*//' +w +q "$file"
# Remove gui, option, setup, screen dumps, testdir/Make_*.mak files
- local na_src_testdir='gen_opt_test.vim\|gui_.*\|Make_amiga.mak\|Make_dos.mak\|Make_ming.mak\|Make_vms.mms\|dumps/.*.dump\|setup_gui.vim'
+ local na_src_testdir='gen_opt_test\.vim\|gui_.*\|Make_amiga\.mak\|Make_dos\.mak\|Make_ming\.mak\|Make_vms\.mms\|dumps/.*\.dump\|setup_gui\.vim'
2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/src/testdir/\<\%('"${na_src_testdir}"'\)\>@norm! d/\v(^diff)|%$ ' +w +q "$file"
# Remove testdir/test_*.vim files
- local na_src_testdir='balloon.*\|channel.*\|crypt.vim\|gui.*\|job_fails.vim\|json.vim\|mzscheme.vim\|netbeans.*\|paste.vim\|popupwin.*\|restricted.vim\|shortpathname.vim\|tcl.vim\|terminal.*\|xxd.vim'
+ local na_src_testdir='balloon.*\|channel.*\|crypt\.vim\|gui.*\|job_fails\.vim\|json\.vim\|mzscheme\.vim\|netbeans.*\|paste\.vim\|popupwin.*\|restricted\.vim\|shortpathname\.vim\|tcl\.vim\|terminal.*\|xxd\.vim'
2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/src/testdir/\<test_\%('"${na_src_testdir}"'\)\>@norm! d/\v(^diff)|%$ ' +w +q "$file"
# Remove version.c #7555
- local na_po='version.c'
- 2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/src/\<\%('${na_po}'\)\>@norm! d/\v(^diff)|%$ ' +w +q "$file"
+ 2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/src/\<\%(version\.c\)\>@norm! d/\v(^diff)|%$ ' +w +q "$file"
# Remove some *.po files. #5622
- local na_po='sjiscorr.c\|ja.sjis.po\|ko.po\|pl.cp1250.po\|pl.po\|ru.cp1251.po\|uk.cp1251.po\|zh_CN.cp936.po\|zh_CN.po\|zh_TW.po'
+ local na_po='sjiscorr\.c\|ja\.sjis\.po\|ko\.po\|pl\.cp1250\.po\|pl\.po\|ru\.cp1251\.po\|uk\.cp1251\.po\|zh_CN\.cp936\.po\|zh_CN\.po\|zh_TW\.po'
2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/src/po/\<\%('${na_po}'\)\>@norm! d/\v(^diff)|%$ ' +w +q "$file"
# Remove vimrc_example.vim
@@ -232,16 +233,32 @@ preprocess_patch() {
LC_ALL=C sed -e 's/\( [ab]\/src\/nvim\)\/userfunc\.c/\1\/eval\/userfunc\.c/g' \
"$file" > "$file".tmp && mv "$file".tmp "$file"
+ # Rename map.c to mapping.c
+ LC_ALL=C sed -e 's/\( [ab]\/src\/nvim\)\/map\(\.[ch]\)/\1\/mapping\2/g' \
+ "$file" > "$file".tmp && mv "$file".tmp "$file"
+
# Rename session.c to ex_session.c
LC_ALL=C sed -e 's/\( [ab]\/src\/nvim\)\/session\(\.[ch]\)/\1\/ex_session\2/g' \
"$file" > "$file".tmp && mv "$file".tmp "$file"
+ # Rename highlight.c to highlight_group.c
+ LC_ALL=C sed -e 's/\( [ab]\/src\/nvim\)\/highlight\(\.[ch]\)/\1\/highlight_group\2/g' \
+ "$file" > "$file".tmp && mv "$file".tmp "$file"
+
+ # Rename keymap.h to keycodes.h
+ LC_ALL=C sed -e 's/\( [ab]\/src\/nvim\)\/keymap\.h/\1\/keycodes.h/g' \
+ "$file" > "$file".tmp && mv "$file".tmp "$file"
+
+ # Rename terminal.txt to nvim_terminal_emulator.txt
+ LC_ALL=C sed -e 's/\( [ab]\/runtime\/doc\)\/terminal\.txt/\1\/nvim_terminal_emulator.txt/g' \
+ "$file" > "$file".tmp && mv "$file".tmp "$file"
+
# Rename test_urls.vim to check_urls.vim
- LC_ALL=C sed -e 's@\( [ab]\)/runtime/doc/test\(_urls.vim\)@\1/scripts/check\2@g' \
+ LC_ALL=C sed -e 's@\( [ab]\)/runtime/doc/test\(_urls\.vim\)@\1/scripts/check\2@g' \
"$file" > "$file".tmp && mv "$file".tmp "$file"
# Rename path to check_colors.vim
- LC_ALL=C sed -e 's@\( [ab]/runtime\)/colors/\(tools/check_colors.vim\)@\1/\2@g' \
+ LC_ALL=C sed -e 's@\( [ab]/runtime\)/colors/\(tools/check_colors\.vim\)@\1/\2@g' \
"$file" > "$file".tmp && mv "$file".tmp "$file"
}
@@ -329,22 +346,29 @@ stage_patch() {
* Do this only for _related_ patches (otherwise it increases the
size of the pull request, making it harder to review)
- When you are done, try "%s -s" to create the pull request.
+ When you are done, try "%s -s" to create the pull request,
+ or "%s -s --draft" to create a draft pull request.
See the wiki for more information:
* https://github.com/neovim/neovim/wiki/Merging-patches-from-upstream-vim
-' "${vim_version}" "${BASENAME}" "${BASENAME}"
+' "${vim_version}" "${BASENAME}" "${BASENAME}" "${BASENAME}"
return $ret
}
gh_pr() {
- gh pr create --title "$1" --body "$2"
+ local pr_title
+ local pr_body
+ pr_title="$1"
+ pr_body="$2"
+ shift 2
+ gh pr create --title "${pr_title}" --body "${pr_body}" "$@"
}
git_hub_pr() {
local pr_message
pr_message="$(printf '%s\n\n%s\n' "$1" "$2")"
- git hub pull new -m "${pr_message}"
+ shift 2
+ git hub pull new -m "${pr_message}" "$@"
}
submit_pr() {
@@ -408,7 +432,7 @@ submit_pr() {
fi
echo "Creating pull request."
- if output="$($submit_fn "$pr_title" "$pr_body" 2>&1)"; then
+ if output="$($submit_fn "$pr_title" "$pr_body" "$@" 2>&1)"; then
msg_ok "$output"
else
msg_err "$output"
@@ -576,13 +600,13 @@ show_vimpatches() {
runtime_commits[$commit]=1
done
- while read -r vim_commit; do
+ list_missing_vimpatches 1 "$@" | while read -r vim_commit; do
if [[ "${runtime_commits[$vim_commit]-}" ]]; then
printf ' • %s (+runtime)\n' "${vim_commit}"
else
printf ' • %s\n' "${vim_commit}"
fi
- done <<< "$(list_missing_vimpatches 1 "$@")"
+ done
cat << EOF
@@ -799,7 +823,8 @@ while getopts "hlLmMVp:P:g:r:s" opt; do
exit 0
;;
s)
- submit_pr
+ shift # remove opt
+ submit_pr "$@"
exit 0
;;
V)
diff --git a/scripts/windows.ti b/scripts/windows.ti
index 4f4832b786..c3a367e6d4 100644
--- a/scripts/windows.ti
+++ b/scripts/windows.ti
@@ -34,7 +34,7 @@ conemu|ANIS X3.64 and Xterm 256 colors for ConEmu with libuv,
smcup=\E[?1049h, rmir@, rmkx@, rmm@, rs1@, rs2@,
setab=\E[48;5;%p1%dm, setaf=\E[38;5;%p1%dm,
sgr=\E[0%?%p1%p3%|%t;7%;%?%p2%t;4%;%?%p6%t;1%;m,
- sgr0=\E[0m, smam@, smir@, smkx@, smm@, tbc@, u6@, u7@, u8@, u9@,
+ sgr0=\E[0m, smam@, smglr@, smir@, smkx@, smm@, tbc@, u6@, u7@, u8@, u9@,
Cr@, Cs@, Ms@, XM@, kDC3@, kDC4@, kDC5@, kDC6@, kDC7@,
kDN@, kDN3@, kDN4@, kDN5@, kDN6@, kDN7@,
kEND3@, kEND4@, kEND5@, kEND6@, kEND7@,
@@ -57,7 +57,7 @@ vtpcon|ANIS emulation for console virtual terminal sequence with libuv,
mc0@, mc4@, mc5@, meml@, memu@, oc@, rmam@, rmcup=\E[?1049l,
smcup=\E[?1049h, rmir@, rmkx@, rmm@, rs1@, rs2@,
sgr=\E[0%?%p1%p3%|%t;7%;%?%p2%t;4%;%?%p6%t;1%;m,
- sgr0=\E[0m, smam@, smir@, smkx@, smm@, tbc@, u6@, u7@, u8@, u9@,
+ sgr0=\E[0m, smam@, smglr@, smir@, smkx@, smm@, tbc@, u6@, u7@, u8@, u9@,
Cr@, Cs@, Ms@, XM@, kDC3@, kDC4@, kDC5@, kDC6@, kDC7@,
kDN@, kDN3@, kDN4@, kDN5@, kDN6@, kDN7@,
kEND3@, kEND4@, kEND5@, kEND6@, kEND7@,
diff --git a/src/Doxyfile b/src/Doxyfile
index 461fafe99d..e085e4e198 100644
--- a/src/Doxyfile
+++ b/src/Doxyfile
@@ -1,112 +1,137 @@
-# Doxyfile 1.8.4
+# Doxyfile 1.9.0
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
#
-# All text after a double hash (##) is considered a comment and is placed
-# in front of the TAG it is preceding .
-# All text after a hash (#) is considered a comment and will be ignored.
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
# The format is:
-# TAG = value [value, ...]
-# For lists items can also be appended using:
-# TAG += value [value, ...]
-# Values that contain spaces should be placed between quotes (" ").
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
-# This tag specifies the encoding used for all characters in the config file
-# that follow. The default is UTF-8 which is also the encoding used for all
+# This tag specifies the encoding used for all characters in the configuration
+# file that follow. The default is UTF-8 which is also the encoding used for all
# text before the first occurrence of this tag. Doxygen uses libiconv (or the
# iconv built into libc) for the transcoding. See
-# http://www.gnu.org/software/libiconv for the list of possible encodings.
+# https://www.gnu.org/software/libiconv/ for the list of possible encodings.
+# The default value is: UTF-8.
DOXYFILE_ENCODING = UTF-8
-# The PROJECT_NAME tag is a single word (or sequence of words) that should
-# identify the project. Note that if you do not use Doxywizard you need
-# to put quotes around the project name if it contains spaces.
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
-PROJECT_NAME = "Neovim"
+PROJECT_NAME = Neovim
-# The PROJECT_NUMBER tag can be used to enter a project or revision number.
-# This could be handy for archiving the generated documentation or
-# if some version control system is used.
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
PROJECT_NUMBER =
# Using the PROJECT_BRIEF tag one can provide an optional one line description
-# for a project that appears at the top of each page and should give viewer
-# a quick idea about the purpose of the project. Keep the description short.
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
PROJECT_BRIEF =
-# With the PROJECT_LOGO tag one can specify a logo or icon that is
-# included in the documentation. The maximum height of the logo should not
-# exceed 55 pixels and the maximum width should not exceed 200 pixels.
-# Doxygen will copy the logo to the output directory.
+# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
+# in the documentation. The maximum height of the logo should not exceed 55
+# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
+# the logo to the output directory.
PROJECT_LOGO = contrib/doxygen/logo-devdoc.png
-# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
-# base path where the generated documentation will be put.
-# If a relative path is entered, it will be relative to the location
-# where doxygen was started. If left blank the current directory will be used.
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
OUTPUT_DIRECTORY = build/doxygen
-# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
-# 4096 sub-directories (in 2 levels) under the output directory of each output
-# format and will distribute the generated files over these directories.
-# Enabling this option can be useful when feeding doxygen a huge amount of
-# source files, where putting all generated files in the same directory would
-# otherwise cause performance problems for the file system.
+# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
CREATE_SUBDIRS = NO
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES = NO
+
# The OUTPUT_LANGUAGE tag is used to specify the language in which all
# documentation generated by doxygen is written. Doxygen will use this
# information to generate all constant output in the proper language.
-# The default language is English, other supported languages are:
-# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
-# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
-# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
-# messages), Korean, Korean-en, Latvian, Lithuanian, Norwegian, Macedonian,
-# Persian, Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic,
-# Slovak, Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
OUTPUT_LANGUAGE = English
-# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
-# include brief member descriptions after the members that are listed in
-# the file and class documentation (similar to JavaDoc).
-# Set to NO to disable this.
+# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all generated output in the proper direction.
+# Possible values are: None, LTR, RTL and Context.
+# The default value is: None.
+
+OUTPUT_TEXT_DIRECTION = None
+
+# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
BRIEF_MEMBER_DESC = YES
-# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
-# the brief description of a member or function before the detailed description.
-# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
# brief descriptions will be completely suppressed.
+# The default value is: YES.
REPEAT_BRIEF = YES
-# This tag implements a quasi-intelligent brief description abbreviator
-# that is used to form the text in various listings. Each string
-# in this list, if found as the leading text of the brief description, will be
-# stripped from the text and the result after processing the whole list, is
-# used as the annotated text. Otherwise, the brief description is used as-is.
-# If left blank, the following values are used ("$name" is automatically
-# replaced with the name of the entity): "The $name class" "The $name widget"
-# "The $name file" "is" "provides" "specifies" "contains"
-# "represents" "a" "an" "the"
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
ABBREVIATE_BRIEF =
# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
-# Doxygen will generate a detailed section even if there is only a brief
+# doxygen will generate a detailed section even if there is only a brief
# description.
+# The default value is: NO.
ALWAYS_DETAILED_SEC = NO
@@ -114,575 +139,764 @@ ALWAYS_DETAILED_SEC = NO
# inherited members of a class in the documentation of that class as if those
# members were ordinary class members. Constructors, destructors and assignment
# operators of the base classes will not be shown.
+# The default value is: NO.
INLINE_INHERITED_MEMB = NO
-# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
-# path before files name in the file list and in the header files. If set
-# to NO the shortest path that makes the file name unique will be used.
+# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
FULL_PATH_NAMES = YES
-# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
-# can be used to strip a user-defined part of the path. Stripping is
-# only done if one of the specified strings matches the left-hand part of
-# the path. The tag can be used to show relative paths in the file list.
-# If left blank the directory from which doxygen is run is used as the
-# path to strip. Note that you specify absolute paths here, but also
-# relative paths, which will be relative from the directory where doxygen is
-# started.
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
STRIP_FROM_PATH =
-# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
-# the path mentioned in the documentation of a class, which tells
-# the reader which header file to include in order to use a class.
-# If left blank only the name of the header file containing the class
-# definition is used. Otherwise one should specify the include paths that
-# are normally passed to the compiler using the -I flag.
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
STRIP_FROM_INC_PATH =
-# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
-# (but less readable) file names. This can be useful if your file system
-# doesn't support long names like on DOS, Mac, or CD-ROM.
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
SHORT_NAMES = NO
-# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
-# will interpret the first line (until the first dot) of a JavaDoc-style
-# comment as the brief description. If set to NO, the JavaDoc
-# comments will behave just like regular Qt-style comments
-# (thus requiring an explicit @brief command for a brief description.)
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
JAVADOC_AUTOBRIEF = NO
-# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
-# interpret the first line (until the first dot) of a Qt-style
-# comment as the brief description. If set to NO, the comments
-# will behave just like regular Qt-style comments (thus requiring
-# an explicit \brief command for a brief description.)
+# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line
+# such as
+# /***************
+# as being the beginning of a Javadoc-style comment "banner". If set to NO, the
+# Javadoc-style will behave just like regular comments and it will not be
+# interpreted by doxygen.
+# The default value is: NO.
+
+JAVADOC_BANNER = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
QT_AUTOBRIEF = NO
-# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
-# treat a multi-line C++ special comment block (i.e. a block of //! or ///
-# comments) as a brief description. This used to be the default behaviour.
-# The new default is to treat a multi-line C++ comment block as a detailed
-# description. Set this tag to YES if you prefer the old behaviour instead.
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
MULTILINE_CPP_IS_BRIEF = NO
-# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
-# member inherits the documentation from any documented member that it
-# re-implements.
+# By default Python docstrings are displayed as preformatted text and doxygen's
+# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the
+# doxygen's special commands can be used and the contents of the docstring
+# documentation blocks is shown as doxygen documentation.
+# The default value is: YES.
+
+PYTHON_DOCSTRING = YES
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
INHERIT_DOCS = YES
-# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
-# a new page for each member. If set to NO, the documentation of a member will
-# be part of the file/class/namespace that contains it.
+# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
+# page for each member. If set to NO, the documentation of a member will be part
+# of the file/class/namespace that contains it.
+# The default value is: NO.
SEPARATE_MEMBER_PAGES = NO
-# The TAB_SIZE tag can be used to set the number of spaces in a tab.
-# Doxygen uses this value to replace tabs by spaces in code fragments.
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
TAB_SIZE = 4
-# This tag can be used to specify a number of aliases that acts
-# as commands in the documentation. An alias has the form "name=value".
-# For example adding "sideeffect=\par Side Effects:\n" will allow you to
-# put the command \sideeffect (or @sideeffect) in the documentation, which
-# will result in a user-defined paragraph with heading "Side Effects:".
-# You can put \n's in the value part of an alias to insert newlines.
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines (in the resulting output). You can put ^^ in the value part of an
+# alias to insert a newline as if a physical newline was in the original file.
+# When you need a literal { or } or , in the value part of an alias you have to
+# escape them by means of a backslash (\), this can lead to conflicts with the
+# commands \{ and \} for these it is advised to use the version @{ and @} or use
+# a double escape (\\{ and \\})
ALIASES =
-# This tag can be used to specify a number of word-keyword mappings (TCL only).
-# A mapping has the form "name=value". For example adding
-# "class=itcl::class" will allow you to use the command class in the
-# itcl::class meaning.
-
-TCL_SUBST =
-
-# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
-# sources only. Doxygen will then generate output that is more tailored for C.
-# For instance, some of the names that are used will be different. The list
-# of all members will be omitted, etc.
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
OPTIMIZE_OUTPUT_FOR_C = YES
-# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
-# sources only. Doxygen will then generate output that is more tailored for
-# Java. For instance, namespaces will be presented as packages, qualified
-# scopes will look different, etc.
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
OPTIMIZE_OUTPUT_JAVA = NO
# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
-# sources only. Doxygen will then generate output that is more tailored for
-# Fortran.
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
OPTIMIZE_FOR_FORTRAN = NO
# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
-# sources. Doxygen will then generate output that is tailored for
-# VHDL.
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
OPTIMIZE_OUTPUT_VHDL = NO
+# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice
+# sources only. Doxygen will then generate output that is more tailored for that
+# language. For instance, namespaces will be presented as modules, types will be
+# separated into more groups, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_SLICE = NO
+
# Doxygen selects the parser to use depending on the extension of the files it
# parses. With this tag you can assign which parser to use for a given
# extension. Doxygen has a built-in mapping, but you can override or extend it
-# using this tag. The format is ext=language, where ext is a file extension,
-# and language is one of the parsers supported by doxygen: IDL, Java,
-# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C,
-# C++. For instance to make doxygen treat .inc files as Fortran files (default
-# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note
-# that for custom extensions you also need to set FILE_PATTERNS otherwise the
-# files are not read by doxygen.
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,
+# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL,
+# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
+# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
+# tries to guess whether the code is fixed or free formatted code, this is the
+# default for Fortran type files). For instance to make doxygen treat .inc files
+# as Fortran files (default is PHP), and .f files as C (default is Fortran),
+# use: inc=Fortran f=C.
+#
+# Note: For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen. When specifying no_extension you should add
+# * to the FILE_PATTERNS.
+#
+# Note see also the list of default file extension mappings.
EXTENSION_MAPPING = lua=C
-# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all
-# comments according to the Markdown format, which allows for more readable
-# documentation. See http://daringfireball.net/projects/markdown/ for details.
-# The output of markdown processing is further processed by doxygen, so you
-# can mix doxygen, HTML, and XML commands with Markdown formatting.
-# Disable only in case of backward compatibilities issues.
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See https://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
MARKDOWN_SUPPORT = YES
+# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
+# to that level are automatically included in the table of contents, even if
+# they do not have an id attribute.
+# Note: This feature currently applies only to Markdown headings.
+# Minimum value: 0, maximum value: 99, default value: 5.
+# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
+
+TOC_INCLUDE_HEADINGS = 5
+
# When enabled doxygen tries to link words that correspond to documented
# classes, or namespaces to their corresponding documentation. Such a link can
-# be prevented in individual cases by by putting a % sign in front of the word
-# or globally by setting AUTOLINK_SUPPORT to NO.
+# be prevented in individual cases by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
AUTOLINK_SUPPORT = YES
# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
-# to include (a tag file for) the STL sources as input, then you should
-# set this tag to YES in order to let doxygen match functions declarations and
-# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
-# func(std::string) {}). This also makes the inheritance and collaboration
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
BUILTIN_STL_SUPPORT = NO
# If you use Microsoft's C++/CLI language, you should set this option to YES to
# enable parsing support.
+# The default value is: NO.
CPP_CLI_SUPPORT = NO
-# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
-# Doxygen will parse them like normal C++ but will assume all classes use public
-# instead of private inheritance when no explicit protection keyword is present.
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
SIP_SUPPORT = NO
# For Microsoft's IDL there are propget and propput attributes to indicate
-# getter and setter methods for a property. Setting this option to YES (the
-# default) will make doxygen replace the get and set methods by a property in
-# the documentation. This will only work if the methods are indeed getting or
-# setting a simple type. If this is not the case, or you want to show the
-# methods anyway, you should set this option to NO.
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
IDL_PROPERTY_SUPPORT = YES
# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
-# tag is set to YES, then doxygen will reuse the documentation of the first
+# tag is set to YES then doxygen will reuse the documentation of the first
# member in the group (if any) for the other members of the group. By default
# all members of a group must be documented explicitly.
+# The default value is: NO.
DISTRIBUTE_GROUP_DOC = NO
-# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
-# the same type (for instance a group of public functions) to be put as a
-# subgroup of that type (e.g. under the Public Functions section). Set it to
-# NO to prevent subgrouping. Alternatively, this can be done per class using
-# the \nosubgrouping command.
+# If one adds a struct or class to a group and this option is enabled, then also
+# any nested class or struct is added to the same group. By default this option
+# is disabled and one has to add nested compounds explicitly via \ingroup.
+# The default value is: NO.
+
+GROUP_NESTED_COMPOUNDS = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
SUBGROUPING = YES
-# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
-# unions are shown inside the group in which they are included (e.g. using
-# @ingroup) instead of on a separate page (for HTML and Man pages) or
-# section (for LaTeX and RTF).
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
INLINE_GROUPED_CLASSES = NO
-# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and
-# unions with only public data fields or simple typedef fields will be shown
-# inline in the documentation of the scope in which they are defined (i.e. file,
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
# namespace, or group documentation), provided this scope is documented. If set
-# to NO (the default), structs, classes, and unions are shown on a separate
-# page (for HTML and Man pages) or section (for LaTeX and RTF).
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
INLINE_SIMPLE_STRUCTS = NO
-# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
-# is documented as struct, union, or enum with the name of the typedef. So
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
# with name TypeT. When disabled the typedef will appear as a member of a file,
-# namespace, or class. And the struct will be named TypeS. This can typically
-# be useful for C code in case the coding convention dictates that all compound
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
TYPEDEF_HIDES_STRUCT = NO
# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
-# cache is used to resolve symbols given their name and scope. Since this can
-# be an expensive process and often the same symbol appear multiple times in
-# the code, doxygen keeps a cache of pre-resolved symbols. If the cache is too
-# small doxygen will become slower. If the cache is too large, memory is wasted.
-# The cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid
-# range is 0..9, the default is 0, corresponding to a cache size of 2^16 = 65536
-# symbols.
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
LOOKUP_CACHE_SIZE = 0
+# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use
+# during processing. When set to 0 doxygen will based this on the number of
+# cores available in the system. You can set it explicitly to a value larger
+# than 0 to get more control over the balance between CPU load and processing
+# speed. At this moment only the input processing can be done using multiple
+# threads. Since this is still an experimental feature the default is set to 1,
+# which efficively disables parallel processing. Please report any issues you
+# encounter. Generating dot graphs in parallel is controlled by the
+# DOT_NUM_THREADS setting.
+# Minimum value: 0, maximum value: 32, default value: 1.
+
+NUM_PROC_THREADS = 1
+
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
-# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
-# documentation are documented, even if no documentation was available.
-# Private class members and static file members will be hidden unless
-# the EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES
+# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
EXTRACT_ALL = YES
-# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
-# will be included in the documentation.
+# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
EXTRACT_PRIVATE = NO
-# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
+# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual
+# methods of a class will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIV_VIRTUAL = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
# scope will be included in the documentation.
+# The default value is: NO.
EXTRACT_PACKAGE = NO
-# If the EXTRACT_STATIC tag is set to YES all static members of a file
-# will be included in the documentation.
+# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
EXTRACT_STATIC = NO
-# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
-# defined locally in source files will be included in the documentation.
-# If set to NO only classes defined in header files are included.
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO,
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
EXTRACT_LOCAL_CLASSES = YES
-# This flag is only useful for Objective-C code. When set to YES local
-# methods, which are defined in the implementation section but not in
-# the interface are included in the documentation.
-# If set to NO (the default) only methods in the interface are included.
+# This flag is only useful for Objective-C code. If set to YES, local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO, only methods in the interface are
+# included.
+# The default value is: NO.
EXTRACT_LOCAL_METHODS = NO
# If this flag is set to YES, the members of anonymous namespaces will be
# extracted and appear in the documentation as a namespace called
-# 'anonymous_namespace{file}', where file will be replaced with the base
-# name of the file that contains the anonymous namespace. By default
-# anonymous namespaces are hidden.
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
EXTRACT_ANON_NSPACES = NO
-# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
-# undocumented members of documented classes, files or namespaces.
-# If set to NO (the default) these members will be included in the
-# various overviews, but no documentation section is generated.
-# This option has no effect if EXTRACT_ALL is enabled.
+# If this flag is set to YES, the name of an unnamed parameter in a declaration
+# will be determined by the corresponding definition. By default unnamed
+# parameters remain unnamed in the output.
+# The default value is: YES.
+
+RESOLVE_UNNAMED_PARAMS = YES
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
HIDE_UNDOC_MEMBERS = NO
-# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
-# undocumented classes that are normally visible in the class hierarchy.
-# If set to NO (the default) these classes will be included in the various
-# overviews. This option has no effect if EXTRACT_ALL is enabled.
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO, these classes will be included in the various overviews. This option
+# has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
HIDE_UNDOC_CLASSES = NO
-# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
-# friend (class|struct|union) declarations.
-# If set to NO (the default) these declarations will be included in the
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# declarations. If set to NO, these declarations will be included in the
# documentation.
+# The default value is: NO.
HIDE_FRIEND_COMPOUNDS = NO
-# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
-# documentation blocks found inside the body of a function.
-# If set to NO (the default) these blocks will be appended to the
-# function's detailed documentation block.
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO, these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
HIDE_IN_BODY_DOCS = NO
-# The INTERNAL_DOCS tag determines if documentation
-# that is typed after a \internal command is included. If the tag is set
-# to NO (the default) then the documentation will be excluded.
-# Set it to YES to include the internal documentation.
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
INTERNAL_DOCS = NO
-# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
-# file names in lower-case letters. If set to YES upper-case letters are also
-# allowed. This is useful if you have classes or files whose names only differ
-# in case and if your file system supports case sensitive file names. Windows
-# and Mac users are advised to set this option to NO.
+# With the correct setting of option CASE_SENSE_NAMES doxygen will better be
+# able to match the capabilities of the underlying filesystem. In case the
+# filesystem is case sensitive (i.e. it supports files in the same directory
+# whose names only differ in casing), the option must be set to YES to properly
+# deal with such files in case they appear in the input. For filesystems that
+# are not case sensitive the option should be be set to NO to properly deal with
+# output files written for symbols that only differ in casing, such as for two
+# classes, one named CLASS and the other named Class, and to also support
+# references to files without having to specify the exact matching casing. On
+# Windows (including Cygwin) and MacOS, users should typically set this option
+# to NO, whereas on Linux or other Unix flavors it should typically be set to
+# YES.
+# The default value is: system dependent.
CASE_SENSE_NAMES = YES
-# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
-# will show members with their full class and namespace scopes in the
-# documentation. If set to YES the scope will be hidden.
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES, the
+# scope will be hidden.
+# The default value is: NO.
HIDE_SCOPE_NAMES = NO
-# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
-# will put a list of the files that are included by a file in the documentation
-# of that file.
+# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
+# append additional text to a page's title, such as Class Reference. If set to
+# YES the compound reference will be hidden.
+# The default value is: NO.
+
+HIDE_COMPOUND_REFERENCE= NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
SHOW_INCLUDE_FILES = YES
-# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
-# will list include files with double quotes in the documentation
-# rather than with sharp brackets.
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
FORCE_LOCAL_INCLUDES = NO
-# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
-# is inserted in the documentation for inline members.
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
INLINE_INFO = YES
-# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
-# will sort the (detailed) documentation of file and class members
-# alphabetically by member name. If set to NO the members will appear in
-# declaration order.
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order.
+# The default value is: YES.
SORT_MEMBER_DOCS = YES
-# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
-# brief documentation of file, namespace and class members alphabetically
-# by member name. If set to NO (the default) the members will appear in
-# declaration order.
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
SORT_BRIEF_DOCS = NO
-# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
-# will sort the (brief and detailed) documentation of class members so that
-# constructors and destructors are listed first. If set to NO (the default)
-# the constructors will appear in the respective orders defined by
-# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
-# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
-# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
SORT_MEMBERS_CTORS_1ST = NO
-# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
-# hierarchy of group names into alphabetical order. If set to NO (the default)
-# the group names will appear in their defined order.
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
SORT_GROUP_NAMES = NO
-# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
-# sorted by fully-qualified names, including namespaces. If set to
-# NO (the default), the class list will be sorted only by class name,
-# not including the namespace part.
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
-# Note: This option applies only to the class list, not to the
-# alphabetical list.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
SORT_BY_SCOPE_NAME = NO
-# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
-# do proper type resolution of all parameters of a function it will reject a
-# match between the prototype and the implementation of a member function even
-# if there is only one candidate or it is obvious which candidate to choose
-# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
-# will still accept a match between prototype and implementation in such cases.
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
STRICT_PROTO_MATCHING = NO
-# The GENERATE_TODOLIST tag can be used to enable (YES) or
-# disable (NO) the todo list. This list is created by putting \todo
-# commands in the documentation.
+# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
+# list. This list is created by putting \todo commands in the documentation.
+# The default value is: YES.
GENERATE_TODOLIST = YES
-# The GENERATE_TESTLIST tag can be used to enable (YES) or
-# disable (NO) the test list. This list is created by putting \test
-# commands in the documentation.
+# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
+# list. This list is created by putting \test commands in the documentation.
+# The default value is: YES.
GENERATE_TESTLIST = YES
-# The GENERATE_BUGLIST tag can be used to enable (YES) or
-# disable (NO) the bug list. This list is created by putting \bug
-# commands in the documentation.
+# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
GENERATE_BUGLIST = YES
-# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
-# disable (NO) the deprecated list. This list is created by putting
-# \deprecated commands in the documentation.
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
GENERATE_DEPRECATEDLIST= YES
-# The ENABLED_SECTIONS tag can be used to enable conditional
-# documentation sections, marked by \if section-label ... \endif
-# and \cond section-label ... \endcond blocks.
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
ENABLED_SECTIONS =
-# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
-# the initial value of a variable or macro consists of for it to appear in
-# the documentation. If the initializer consists of more lines than specified
-# here it will be hidden. Use a value of 0 to hide initializers completely.
-# The appearance of the initializer of individual variables and macros in the
-# documentation can be controlled using \showinitializer or \hideinitializer
-# command in the documentation regardless of this setting.
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
MAX_INITIALIZER_LINES = 30
-# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
-# at the bottom of the documentation of classes and structs. If set to YES the
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES, the
# list will mention the files that were used to generate the documentation.
+# The default value is: YES.
SHOW_USED_FILES = YES
-# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
-# This will remove the Files entry from the Quick Index and from the
-# Folder Tree View (if specified). The default is YES.
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
SHOW_FILES = YES
-# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
-# Namespaces page.
-# This will remove the Namespaces entry from the Quick Index
-# and from the Folder Tree View (if specified). The default is YES.
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
SHOW_NAMESPACES = YES
# The FILE_VERSION_FILTER tag can be used to specify a program or script that
# doxygen should invoke to get the current version for each file (typically from
# the version control system). Doxygen will invoke the program by executing (via
-# popen()) the command <command> <input-file>, where <command> is the value of
-# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
-# provided by doxygen. Whatever the program writes to standard output
-# is used as the file version. See the manual for examples.
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
FILE_VERSION_FILTER =
# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
# by doxygen. The layout file controls the global structure of the generated
# output files in an output format independent way. To create the layout file
-# that represents doxygen's defaults, run doxygen with the -l option.
-# You can optionally specify a file name after the option, if omitted
-# DoxygenLayout.xml will be used as the name of the layout file.
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
LAYOUT_FILE =
-# The CITE_BIB_FILES tag can be used to specify one or more bib files
-# containing the references data. This must be a list of .bib files. The
-# .bib extension is automatically appended if omitted. Using this command
-# requires the bibtex tool to be installed. See also
-# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style
-# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this
-# feature you need bibtex and perl available in the search path. Do not use
-# file names with spaces, bibtex cannot handle them.
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
CITE_BIB_FILES =
#---------------------------------------------------------------------------
-# configuration options related to warning and progress messages
+# Configuration options related to warning and progress messages
#---------------------------------------------------------------------------
-# The QUIET tag can be used to turn on/off the messages that are generated
-# by doxygen. Possible values are YES and NO. If left blank NO is used.
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
QUIET = NO
# The WARNINGS tag can be used to turn on/off the warning messages that are
-# generated by doxygen. Possible values are YES and NO. If left blank
-# NO is used.
+# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
WARNINGS = YES
-# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
-# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
-# automatically be disabled.
+# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
WARN_IF_UNDOCUMENTED = YES
-# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
-# potential errors in the documentation, such as not documenting some
-# parameters in a documented function, or documenting parameters that
-# don't exist or using markup commands wrongly.
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
WARN_IF_DOC_ERROR = YES
-# The WARN_NO_PARAMDOC option can be enabled to get warnings for
-# functions that are documented, but have no documentation for their parameters
-# or return value. If set to NO (the default) doxygen will only warn about
-# wrong or incomplete parameter documentation, but not about the absence of
-# documentation.
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO, doxygen will only warn about wrong or incomplete
+# parameter documentation, but not about the absence of documentation. If
+# EXTRACT_ALL is set to YES then this flag will automatically be disabled.
+# The default value is: NO.
WARN_NO_PARAMDOC = NO
-# The WARN_FORMAT tag determines the format of the warning messages that
-# doxygen can produce. The string should contain the $file, $line, and $text
-# tags, which will be replaced by the file and line number from which the
-# warning originated and the warning text. Optionally the format may contain
-# $version, which will be replaced by the version of the file (if it could
-# be obtained via FILE_VERSION_FILTER)
+# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
+# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
+# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
+# at the end of the doxygen process doxygen will return with a non-zero status.
+# Possible values are: NO, YES and FAIL_ON_WARNINGS.
+# The default value is: NO.
+
+WARN_AS_ERROR = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
WARN_FORMAT = "$file:$line: $text"
-# The WARN_LOGFILE tag can be used to specify a file to which warning
-# and error messages should be written. If left blank the output is written
-# to stderr.
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
WARN_LOGFILE =
#---------------------------------------------------------------------------
-# configuration options related to the input files
+# Configuration options related to the input files
#---------------------------------------------------------------------------
-# The INPUT tag can be used to specify the files and/or directories that contain
-# documented source files. You may enter file names like "myfile.cpp" or
-# directories like "/usr/src/myproject". Separate the files or directories
-# with spaces.
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
+# Note: If this tag is empty the current directory is searched.
INPUT = src/
# This tag can be used to specify the character encoding of the source files
-# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
-# also the default input encoding. Doxygen uses libiconv (or the iconv built
-# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
-# the list of possible encodings.
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see:
+# https://www.gnu.org/software/libiconv/) for the list of possible encodings.
+# The default value is: UTF-8.
INPUT_ENCODING = UTF-8
# If the value of the INPUT tag contains directories, you can use the
-# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank the following patterns are tested:
-# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
-# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
-# *.f90 *.f *.for *.vhd *.vhdl
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# read by doxygen.
+#
+# Note the list of default checked file patterns might differ from the list of
+# default file extension mappings.
+#
+# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
+# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
+# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
+# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment),
+# *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl,
+# *.ucf, *.qsf and *.ice.
-FILE_PATTERNS = *.h *.c *.lua
+FILE_PATTERNS = *.h \
+ *.c \
+ *.lua
-# The RECURSIVE tag can be used to turn specify whether or not subdirectories
-# should be searched for input files as well. Possible values are YES and NO.
-# If left blank NO is used.
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
RECURSIVE = YES
# The EXCLUDE tag can be used to specify files and/or directories that should be
# excluded from the INPUT source files. This way you can easily exclude a
# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
# Note that relative paths are relative to the directory from which doxygen is
# run.
@@ -691,14 +905,16 @@ EXCLUDE =
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
# directories that are symbolic links (a Unix file system feature) are excluded
# from the input.
+# The default value is: NO.
EXCLUDE_SYMLINKS = NO
# If the value of the INPUT tag contains directories, you can use the
# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
-# certain files from those directories. Note that the wildcards are matched
-# against the file with absolute path, so to exclude all test directories
-# for example use the pattern */test/*
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
EXCLUDE_PATTERNS =
@@ -707,215 +923,275 @@ EXCLUDE_PATTERNS =
# output. The symbol name can be a fully qualified name, a word, or if the
# wildcard * is used, a substring. Examples: ANamespace, AClass,
# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
EXCLUDE_SYMBOLS =
-# The EXAMPLE_PATH tag can be used to specify one or more files or
-# directories that contain example code fragments that are included (see
-# the \include command).
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
EXAMPLE_PATH =
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
-# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank all files are included.
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
EXAMPLE_PATTERNS =
# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
-# searched for input files to be used with the \include or \dontinclude
-# commands irrespective of the value of the RECURSIVE tag.
-# Possible values are YES and NO. If left blank NO is used.
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
EXAMPLE_RECURSIVE = NO
-# The IMAGE_PATH tag can be used to specify one or more files or
-# directories that contain image that are included in the documentation (see
-# the \image command).
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
IMAGE_PATH =
# The INPUT_FILTER tag can be used to specify a program that doxygen should
# invoke to filter for each input file. Doxygen will invoke the filter program
-# by executing (via popen()) the command <filter> <input-file>, where <filter>
-# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
-# input file. Doxygen will then use the output that the filter program writes
-# to standard output.
-# If FILTER_PATTERNS is specified, this tag will be ignored.
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
# Note that the filter must not add or remove lines; it is applied before the
# code is scanned, but not when the output code is generated. If lines are added
# or removed, the anchors will not be placed correctly.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
INPUT_FILTER =
# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
-# basis.
-# Doxygen will compare the file name with each pattern and apply the
-# filter if there is a match.
-# The filters are a list of the form:
-# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
-# info on how filters are used. If FILTER_PATTERNS is empty or if
-# non of the patterns match the file name, INPUT_FILTER is applied.
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
FILTER_PATTERNS = *.lua=scripts/lua2dox_filter
# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
-# INPUT_FILTER) will be used to filter the input files when producing source
-# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# INPUT_FILTER) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
FILTER_SOURCE_FILES = NO
# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
-# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
-# and it is also possible to disable source filtering for a specific pattern
-# using *.ext= (so without naming a filter). This option only has effect when
-# FILTER_SOURCE_FILES is enabled.
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
FILTER_SOURCE_PATTERNS =
-# If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
# is part of the input, its contents will be placed on the main page
# (index.html). This can be useful if you have a project on for instance GitHub
-# and want reuse the introduction page also for the doxygen output.
+# and want to reuse the introduction page also for the doxygen output.
USE_MDFILE_AS_MAINPAGE =
#---------------------------------------------------------------------------
-# configuration options related to source browsing
+# Configuration options related to source browsing
#---------------------------------------------------------------------------
-# If the SOURCE_BROWSER tag is set to YES then a list of source files will
-# be generated. Documented entities will be cross-referenced with these sources.
-# Note: To get rid of all source code in the generated output, make sure also
-# VERBATIM_HEADERS is set to NO.
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
SOURCE_BROWSER = NO
-# Setting the INLINE_SOURCES tag to YES will include the body
-# of functions and classes directly in the documentation.
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
INLINE_SOURCES = NO
-# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
-# doxygen to hide any special comment blocks from generated source code
-# fragments. Normal C, C++ and Fortran comments will always remain visible.
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
STRIP_CODE_COMMENTS = YES
-# If the REFERENCED_BY_RELATION tag is set to YES
-# then for each documented function all documented
-# functions referencing it will be listed.
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# entity all documented functions referencing it will be listed.
+# The default value is: NO.
REFERENCED_BY_RELATION = NO
-# If the REFERENCES_RELATION tag is set to YES
-# then for each documented function all documented entities
-# called/used by that function will be listed.
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
REFERENCES_RELATION = NO
-# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
-# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
-# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
-# link to the source code.
-# Otherwise they will link to the documentation.
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
REFERENCES_LINK_SOURCE = YES
-# If the USE_HTAGS tag is set to YES then the references to source code
-# will point to the HTML generated by the htags(1) tool instead of doxygen
-# built-in source browser. The htags tool is part of GNU's global source
-# tagging system (see http://www.gnu.org/software/global/global.html). You
-# will need version 4.8.6 or higher.
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see https://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
USE_HTAGS = NO
-# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
-# will generate a verbatim copy of the header file for each class for
-# which an include is specified. Set to NO to disable this.
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
VERBATIM_HEADERS = YES
#---------------------------------------------------------------------------
-# configuration options related to the alphabetical class index
+# Configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
-# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
-# of all compounds will be generated. Enable this if the project
-# contains a lot of classes, structs, unions or interfaces.
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
ALPHABETICAL_INDEX = YES
-# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
-# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
-# in which this list will be split (can be a number in the range [1..20])
-
-COLS_IN_ALPHA_INDEX = 5
-
-# In case all classes in a project start with a common prefix, all
-# classes will be put under the same header in the alphabetical index.
-# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
-# should be ignored while generating the index headers.
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
IGNORE_PREFIX =
#---------------------------------------------------------------------------
-# configuration options related to the HTML output
+# Configuration options related to the HTML output
#---------------------------------------------------------------------------
-# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
-# generate HTML output.
+# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
+# The default value is: YES.
GENERATE_HTML = YES
-# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `html' will be used as the default path.
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_OUTPUT = html
-# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
-# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
-# doxygen will generate files with .html extension.
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_FILE_EXTENSION = .html
-# The HTML_HEADER tag can be used to specify a personal HTML header for
-# each generated HTML page. If it is left blank doxygen will generate a
-# standard header. Note that when using a custom header you are responsible
-# for the proper inclusion of any scripts and style sheets that doxygen
-# needs, which is dependent on the configuration options used.
-# It is advised to generate a default header using "doxygen -w html
-# header.html footer.html stylesheet.css YourConfigFile" and then modify
-# that header. Note that the header is subject to change so you typically
-# have to redo this when upgrading to a newer version of doxygen or when
-# changing the value of configuration settings such as GENERATE_TREEVIEW!
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_HEADER = contrib/doxygen/header.html
-# The HTML_FOOTER tag can be used to specify a personal HTML footer for
-# each generated HTML page. If it is left blank doxygen will generate a
-# standard footer.
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_FOOTER = contrib/doxygen/footer.html
-# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
-# style sheet that is used by each HTML page. It can be used to
-# fine-tune the look of the HTML output. If left blank doxygen will
-# generate a default style sheet. Note that it is recommended to use
-# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this
-# tag will in the future become obsolete.
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_STYLESHEET = contrib/doxygen/customdoxygen.css
-# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional
-# user-defined cascading style sheet that is included after the standard
-# style sheets created by doxygen. Using this option one can overrule
-# certain style aspects. This is preferred over using HTML_STYLESHEET
-# since it does not replace the standard style sheet and is therefor more
-# robust against future updates. Doxygen will copy the style sheet file to
-# the output directory.
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefore more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list). For an example see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_STYLESHEET = contrib/doxygen/extra.css
@@ -923,632 +1199,911 @@ HTML_EXTRA_STYLESHEET = contrib/doxygen/extra.css
# other source files which should be copied to the HTML output directory. Note
# that these files will be copied to the base HTML output directory. Use the
# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
-# files. In the HTML_STYLESHEET file, use the file name only. Also note that
-# the files will be copied as-is; there are no commands or markers available.
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_FILES =
-# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
-# Doxygen will adjust the colors in the style sheet and background images
-# according to this color. Hue is specified as an angle on a colorwheel,
-# see http://en.wikipedia.org/wiki/Hue for more information.
-# For instance the value 0 represents red, 60 is yellow, 120 is green,
-# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
-# The allowed range is 0 to 359.
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the style sheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# https://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE_HUE = 220
-# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
-# the colors in the HTML output. For a value of 0 the output will use
-# grayscales only. A value of 255 will produce the most vivid colors.
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE_SAT = 100
-# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
-# the luminance component of the colors in the HTML output. Values below
-# 100 gradually make the output lighter, whereas values above 100 make
-# the output darker. The value divided by 100 is the actual gamma applied,
-# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
-# and 100 does not change the gamma.
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE_GAMMA = 80
# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
-# page will contain the date and time when the page was generated. Setting
-# this to NO can help when comparing the output of multiple runs.
+# page will contain the date and time when the page was generated. Setting this
+# to YES can help to show when doxygen was last run and thus if the
+# documentation is up to date.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_TIMESTAMP = YES
+# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
+# documentation will contain a main index with vertical navigation menus that
+# are dynamically created via JavaScript. If disabled, the navigation index will
+# consists of multiple levels of tabs that are statically embedded in every HTML
+# page. Disable this option to support browsers that do not have JavaScript,
+# like the Qt help browser.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_MENUS = YES
+
# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
# documentation will contain sections that can be hidden and shown after the
# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_DYNAMIC_SECTIONS = NO
-# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of
-# entries shown in the various tree structured indices initially; the user
-# can expand and collapse entries dynamically later on. Doxygen will expand
-# the tree to such a level that at most the specified number of entries are
-# visible (unless a fully collapsed tree already exceeds this amount).
-# So setting the number of entries 1 will produce a full collapsed tree by
-# default. 0 is a special value representing an infinite number of entries
-# and will result in a full expanded tree by default.
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_INDEX_NUM_ENTRIES = 100
-# If the GENERATE_DOCSET tag is set to YES, additional index files
-# will be generated that can be used as input for Apple's Xcode 3
-# integrated development environment, introduced with OSX 10.5 (Leopard).
-# To create a documentation set, doxygen will generate a Makefile in the
-# HTML output directory. Running make will produce the docset in that
-# directory and running "make install" will install the docset in
-# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
-# it at startup.
-# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
-# for more information.
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see:
+# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To
+# create a documentation set, doxygen will generate a Makefile in the HTML
+# output directory. Running make will produce the docset in that directory and
+# running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
+# genXcode/_index.html for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_DOCSET = NO
-# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
-# feed. A documentation feed provides an umbrella under which multiple
-# documentation sets from a single provider (such as a company or product suite)
-# can be grouped.
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_FEEDNAME = "Doxygen generated docs"
-# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
-# should uniquely identify the documentation set bundle. This should be a
-# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
-# will append .docset to the name.
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_BUNDLE_ID = org.doxygen.Project
-# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely
-# identify the documentation publisher. This should be a reverse domain-name
-# style string, e.g. com.mycompany.MyDocSet.documentation.
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_PUBLISHER_ID = org.doxygen.Publisher
-# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_PUBLISHER_NAME = Publisher
-# If the GENERATE_HTMLHELP tag is set to YES, additional index files
-# will be generated that can be used as input for tools like the
-# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
-# of the generated HTML documentation.
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see:
+# https://www.microsoft.com/en-us/download/details.aspx?id=21138) on Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_HTMLHELP = NO
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
-# be used to specify the file name of the resulting .chm file. You
-# can add a path in front of the file if the result should not be
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
CHM_FILE =
-# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
-# be used to specify the location (absolute path including file name) of
-# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
-# the HTML help compiler on the generated index.hhp.
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler (hhc.exe). If non-empty,
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
HHC_LOCATION =
-# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
-# controls if a separate .chi index file is generated (YES) or that
-# it should be included in the master .chm file (NO).
+# The GENERATE_CHI flag controls if a separate .chi index file is generated
+# (YES) or that it should be included in the main .chm file (NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
GENERATE_CHI = NO
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
-# is used to encode HtmlHelp index (hhk), content (hhc) and project file
-# content.
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
CHM_INDEX_ENCODING =
-# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
-# controls whether a binary table of contents is generated (YES) or a
-# normal table of contents (NO) in the .chm file.
+# The BINARY_TOC flag controls whether a binary table of contents is generated
+# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
BINARY_TOC = NO
-# The TOC_EXPAND flag can be set to YES to add extra items for group members
-# to the contents of the HTML help documentation and to the tree view.
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
TOC_EXPAND = NO
# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
-# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
-# that can be used as input for Qt's qhelpgenerator to generate a
-# Qt Compressed Help (.qch) of the generated HTML documentation.
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_QHP = NO
-# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
-# be used to specify the file name of the resulting .qch file.
-# The path specified is relative to the HTML output folder.
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
QCH_FILE =
-# The QHP_NAMESPACE tag specifies the namespace to use when generating
-# Qt Help Project output. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#namespace
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_NAMESPACE = org.doxygen.Project
-# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
-# Qt Help Project output. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_VIRTUAL_FOLDER = doc
-# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
-# add. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#custom-filters
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_NAME =
-# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
-# custom filter to add. For more information please see
-# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
-# Qt Help Project / Custom Filters</a>.
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_ATTRS =
# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
-# project's
-# filter section matches.
-# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
-# Qt Help Project / Filter Attributes</a>.
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_SECT_FILTER_ATTRS =
-# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
-# be used to specify the location of Qt's qhelpgenerator.
-# If non-empty doxygen will try to run qhelpgenerator on the generated
-# .qhp file.
+# The QHG_LOCATION tag can be used to specify the location (absolute path
+# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to
+# run qhelpgenerator on the generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
QHG_LOCATION =
-# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
-# will be generated, which together with the HTML files, form an Eclipse help
-# plugin. To install this plugin and make it available under the help contents
-# menu in Eclipse, the contents of the directory containing the HTML and XML
-# files needs to be copied into the plugins directory of eclipse. The name of
-# the directory within the plugins directory should be the same as
-# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
-# the help appears.
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_ECLIPSEHELP = NO
-# A unique identifier for the eclipse help plugin. When installing the plugin
-# the directory name containing the HTML and XML files should also have
-# this name.
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
ECLIPSE_DOC_ID = org.doxygen.Project
-# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs)
-# at top of each HTML page. The value NO (the default) enables the index and
-# the value YES disables it. Since the tabs have the same information as the
-# navigation tree you can set this option to NO if you already set
-# GENERATE_TREEVIEW to YES.
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
DISABLE_INDEX = NO
# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
-# structure should be generated to display hierarchical information.
-# If the tag value is set to YES, a side panel will be generated
-# containing a tree-like index structure (just like the one that
-# is generated for HTML Help). For this to work a browser that supports
-# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
-# Windows users are probably better off using the HTML help feature.
-# Since the tree basically has the same information as the tab index you
-# could consider to set DISABLE_INDEX to NO when enabling this option.
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_TREEVIEW = NO
-# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
-# (range [0,1..20]) that doxygen will group on one line in the generated HTML
-# documentation. Note that a value of 0 will completely suppress the enum
-# values from appearing in the overview section.
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
ENUM_VALUES_PER_LINE = 4
-# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
-# used to set the initial width (in pixels) of the frame in which the tree
-# is shown.
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
TREEVIEW_WIDTH = 250
-# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
-# links to external symbols imported via tag files in a separate window.
+# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
EXT_LINKS_IN_WINDOW = NO
-# Use this tag to change the font size of Latex formulas included
-# as images in the HTML documentation. The default is 10. Note that
-# when you change the font size after a successful doxygen run you need
-# to manually remove any form_*.png images from the HTML output directory
-# to force them to be regenerated.
+# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg
+# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see
+# https://inkscape.org) to generate formulas as SVG images instead of PNGs for
+# the HTML output. These images will generally look nicer at scaled resolutions.
+# Possible values are: png (the default) and svg (looks nicer but requires the
+# pdf2svg or inkscape tool).
+# The default value is: png.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FORMULA_FORMAT = png
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
FORMULA_FONTSIZE = 10
-# Use the FORMULA_TRANPARENT tag to determine whether or not the images
-# generated for formulas are transparent PNGs. Transparent PNGs are
-# not supported properly for IE 6.0, but are supported on all modern browsers.
-# Note that when changing this option you need to delete any form_*.png files
-# in the HTML output before the changes have effect.
+# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
FORMULA_TRANSPARENT = YES
-# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
-# (see http://www.mathjax.org) which uses client side Javascript for the
-# rendering instead of using prerendered bitmaps. Use this if you do not
-# have LaTeX installed or if you want to formulas look prettier in the HTML
-# output. When enabled you may also need to install MathJax separately and
-# configure the path to it using the MATHJAX_RELPATH option.
+# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
+# to create new LaTeX commands to be used in formulas as building blocks. See
+# the section "Including formulas" for details.
+
+FORMULA_MACROFILE =
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# https://www.mathjax.org) which uses client side JavaScript for the rendering
+# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
USE_MATHJAX = NO
# When MathJax is enabled you can set the default output format to be used for
-# the MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and
-# SVG. The default value is HTML-CSS, which is slower, but has the best
-# compatibility.
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_FORMAT = HTML-CSS
-# When MathJax is enabled you need to specify the location relative to the
-# HTML output directory using the MATHJAX_RELPATH option. The destination
-# directory should contain the MathJax.js script. For instance, if the mathjax
-# directory is located at the same level as the HTML output directory, then
-# MATHJAX_RELPATH should be ../mathjax. The default value points to
-# the MathJax Content Delivery Network so you can quickly see the result without
-# installing MathJax.
-# However, it is strongly recommended to install a local
-# copy of MathJax from http://www.mathjax.org before deployment.
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from https://www.mathjax.org before deployment.
+# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2.
+# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
-# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension
-# names that should be enabled during MathJax rendering.
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_EXTENSIONS =
-# The MATHJAX_CODEFILE tag can be used to specify a file with javascript
-# pieces of code that will be used on startup of the MathJax code.
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see:
+# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_CODEFILE =
-# When the SEARCHENGINE tag is enabled doxygen will generate a search box
-# for the HTML output. The underlying search engine uses javascript
-# and DHTML and should work on any modern browser. Note that when using
-# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
-# (GENERATE_DOCSET) there is already a search function so this one should
-# typically be disabled. For large projects the javascript based search engine
-# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
SEARCHENGINE = YES
# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
-# implemented using a web server instead of a web client using Javascript.
-# There are two flavours of web server based search depending on the
-# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for
-# searching and an index file used by the script. When EXTERNAL_SEARCH is
-# enabled the indexing and searching needs to be provided by external tools.
-# See the manual for details.
+# implemented using a web server instead of a web client using JavaScript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
SERVER_BASED_SEARCH = NO
-# When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
# script for searching. Instead the search results are written to an XML file
# which needs to be processed by an external indexer. Doxygen will invoke an
-# external search engine pointed to by the SEARCHENGINE_URL option to obtain
-# the search results. Doxygen ships with an example indexer (doxyindexer) and
-# search engine (doxysearch.cgi) which are based on the open source search
-# engine library Xapian. See the manual for configuration details.
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see:
+# https://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
EXTERNAL_SEARCH = NO
# The SEARCHENGINE_URL should point to a search engine hosted by a web server
-# which will returned the search results when EXTERNAL_SEARCH is enabled.
-# Doxygen ships with an example search engine (doxysearch) which is based on
-# the open source search engine library Xapian. See the manual for configuration
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see:
+# https://xapian.org/). See the section "External Indexing and Searching" for
# details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
SEARCHENGINE_URL =
# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
# search data is written to a file for indexing by an external tool. With the
# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
SEARCHDATA_FILE = searchdata.xml
-# When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
EXTERNAL_SEARCH_ID =
# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
# projects other than the one defined by this configuration file, but that are
# all added to the same external search index. Each project needs to have a
-# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id
-# of to a relative location where the documentation can be found.
-# The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ...
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
EXTRA_SEARCH_MAPPINGS =
#---------------------------------------------------------------------------
-# configuration options related to the LaTeX output
+# Configuration options related to the LaTeX output
#---------------------------------------------------------------------------
-# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
-# generate Latex output.
+# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
+# The default value is: YES.
GENERATE_LATEX = YES
-# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `latex' will be used as the default path.
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_OUTPUT = latex
# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
-# invoked. If left blank `latex' will be used as the default command name.
-# Note that when enabling USE_PDFLATEX this option is only used for
-# generating bitmaps for formulas in the HTML output, but not in the
-# Makefile that is written to the output directory.
+# invoked.
+#
+# Note that when not enabling USE_PDFLATEX the default is latex when enabling
+# USE_PDFLATEX the default is pdflatex and when in the later case latex is
+# chosen this is overwritten by pdflatex. For specific output languages the
+# default can have been set differently, this depends on the implementation of
+# the output language.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_CMD_NAME = latex
-# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
-# generate index for LaTeX. If left blank `makeindex' will be used as the
-# default command name.
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# Note: This tag is used in the Makefile / make.bat.
+# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file
+# (.tex).
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
MAKEINDEX_CMD_NAME = makeindex
-# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
-# LaTeX documents. This may be useful for small projects and may help to
-# save some trees in general.
+# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to
+# generate index for LaTeX. In case there is no backslash (\) as first character
+# it will be automatically added in the LaTeX code.
+# Note: This tag is used in the generated output file (.tex).
+# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.
+# The default value is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_MAKEINDEX_CMD = makeindex
+
+# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
COMPACT_LATEX = NO
-# The PAPER_TYPE tag can be used to set the paper type that is used
-# by the printer. Possible values are: a4, letter, legal and
-# executive. If left blank a4 will be used.
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
PAPER_TYPE = a4
-# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
-# packages that should be included in the LaTeX output.
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. The package can be specified just
+# by its name or with the correct syntax as to be used with the LaTeX
+# \usepackage command. To get the times font for instance you can specify :
+# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
+# To use the option intlimits with the amsmath package you can specify:
+# EXTRA_PACKAGES=[intlimits]{amsmath}
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
EXTRA_PACKAGES =
-# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
-# the generated latex document. The header should contain everything until
-# the first chapter. If it is left blank doxygen will generate a
-# standard header. Notice: only use this tag if you know what you are doing!
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
+# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
+# string, for the replacement values of the other commands the user is referred
+# to HTML_HEADER.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_HEADER =
-# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
-# the generated latex document. The footer should contain everything after
-# the last chapter. If it is left blank doxygen will generate a
-# standard footer. Notice: only use this tag if you know what you are doing!
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_FOOTER =
-# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images
-# or other source files which should be copied to the LaTeX output directory.
-# Note that the files will be copied as-is; there are no commands or markers
-# available.
+# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# LaTeX style sheets that are included after the standard style sheets created
+# by doxygen. Using this option one can overrule certain style aspects. Doxygen
+# will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_STYLESHEET =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_EXTRA_FILES =
-# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
-# is prepared for conversion to pdf (using ps2pdf). The pdf file will
-# contain links (just like the HTML output) instead of page references
-# This makes the output suitable for online browsing using a pdf viewer.
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
PDF_HYPERLINKS = YES
-# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
-# plain latex in the generated Makefile. Set this option to YES to get a
-# higher quality PDF documentation.
+# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as
+# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX
+# files. Set this option to YES, to get a higher quality PDF documentation.
+#
+# See also section LATEX_CMD_NAME for selecting the engine.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
USE_PDFLATEX = YES
-# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
-# command to the generated LaTeX files. This will instruct LaTeX to keep
-# running if errors occur, instead of asking the user for help.
-# This option is also used when generating formulas in HTML.
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_BATCHMODE = NO
-# If LATEX_HIDE_INDICES is set to YES then doxygen will not
-# include the index chapters (such as File Index, Compound Index, etc.)
-# in the output.
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_HIDE_INDICES = NO
-# If LATEX_SOURCE_CODE is set to YES then doxygen will include
-# source code with syntax highlighting in the LaTeX output.
-# Note that which sources are shown also depends on other settings
-# such as SOURCE_BROWSER.
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_SOURCE_CODE = NO
# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
-# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See
-# http://en.wikipedia.org/wiki/BibTeX for more info.
+# bibliography, e.g. plainnat, or ieeetr. See
+# https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_BIB_STYLE = plain
+# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_TIMESTAMP = NO
+
+# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
+# path from which the emoji images will be read. If a relative path is entered,
+# it will be relative to the LATEX_OUTPUT directory. If left blank the
+# LATEX_OUTPUT directory will be used.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EMOJI_DIRECTORY =
+
#---------------------------------------------------------------------------
-# configuration options related to the RTF output
+# Configuration options related to the RTF output
#---------------------------------------------------------------------------
-# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
-# The RTF output is optimized for Word 97 and may not look very pretty with
-# other RTF readers or editors.
+# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
GENERATE_RTF = NO
-# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `rtf' will be used as the default path.
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_OUTPUT = rtf
-# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
-# RTF documents. This may be useful for small projects and may help to
-# save some trees in general.
+# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
COMPACT_RTF = NO
-# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
-# will contain hyperlink fields. The RTF file will
-# contain links (just like the HTML output) instead of page references.
-# This makes the output suitable for online browsing using WORD or other
-# programs which support those fields.
-# Note: wordpad (write) and others do not support links.
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_HYPERLINKS = NO
-# Load style sheet definitions from file. Syntax is similar to doxygen's
-# config file, i.e. a series of assignments. You only have to provide
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# configuration file, i.e. a series of assignments. You only have to provide
# replacements, missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_STYLESHEET_FILE =
-# Set optional variables used in the generation of an rtf document.
-# Syntax is similar to doxygen's config file.
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's configuration file. A template extensions file can be
+# generated using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_EXTENSIONS_FILE =
+# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
+# with syntax highlighting in the RTF output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_SOURCE_CODE = NO
+
#---------------------------------------------------------------------------
-# configuration options related to the man page output
+# Configuration options related to the man page output
#---------------------------------------------------------------------------
-# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
-# generate man pages
+# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
GENERATE_MAN = NO
-# The MAN_OUTPUT tag is used to specify where the man pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `man' will be used as the default path.
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
MAN_OUTPUT = man
-# The MAN_EXTENSION tag determines the extension that is added to
-# the generated man pages (default is the subroutine's section .3)
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
MAN_EXTENSION = .3
-# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
-# then it will generate one additional man file for each entity
-# documented in the real man page(s). These additional files
-# only source the real man page, but without them the man command
-# would be unable to find the correct page. The default is NO.
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR =
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
MAN_LINKS = NO
#---------------------------------------------------------------------------
-# configuration options related to the XML output
+# Configuration options related to the XML output
#---------------------------------------------------------------------------
-# If the GENERATE_XML tag is set to YES Doxygen will
-# generate an XML file that captures the structure of
-# the code including all documentation.
+# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
GENERATE_XML = NO
-# The XML_OUTPUT tag is used to specify where the XML pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `xml' will be used as the default path.
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
XML_OUTPUT = xml
-# The XML_SCHEMA tag can be used to specify an XML schema,
-# which can be used by a validating XML parser to check the
-# syntax of the XML files.
+# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
-XML_SCHEMA =
-
-# The XML_DTD tag can be used to specify an XML DTD,
-# which can be used by a validating XML parser to check the
-# syntax of the XML files.
-
-XML_DTD =
+XML_PROGRAMLISTING = YES
-# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
-# dump the program listings (including syntax highlighting
-# and cross-referencing information) to the XML output. Note that
-# enabling this will significantly increase the size of the XML output.
+# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include
+# namespace members in file scope as well, matching the HTML output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_XML is set to YES.
-XML_PROGRAMLISTING = YES
+XML_NS_MEMB_FILE_SCOPE = NO
#---------------------------------------------------------------------------
-# configuration options related to the DOCBOOK output
+# Configuration options related to the DOCBOOK output
#---------------------------------------------------------------------------
-# If the GENERATE_DOCBOOK tag is set to YES Doxygen will generate DOCBOOK files
+# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
# that can be used to generate PDF.
+# The default value is: NO.
GENERATE_DOCBOOK = NO
-# The DOCBOOK_OUTPUT tag is used to specify where the DOCBOOK pages will be put.
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
-# front of it. If left blank docbook will be used as the default path.
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
DOCBOOK_OUTPUT = docbook
+# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
+# program listings (including syntax highlighting and cross-referencing
+# information) to the DOCBOOK output. Note that enabling this will significantly
+# increase the size of the DOCBOOK output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_PROGRAMLISTING = NO
+
#---------------------------------------------------------------------------
-# configuration options for the AutoGen Definitions output
+# Configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
-# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
-# generate an AutoGen Definitions (see autogen.sf.net) file
-# that captures the structure of the code including all
-# documentation. Note that this feature is still experimental
-# and incomplete at the moment.
+# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
+# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
+# the structure of the code including all documentation. Note that this feature
+# is still experimental and incomplete at the moment.
+# The default value is: NO.
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
-# configuration options related to the Perl module output
+# Configuration options related to the Perl module output
#---------------------------------------------------------------------------
-# If the GENERATE_PERLMOD tag is set to YES Doxygen will
-# generate a Perl module file that captures the structure of
-# the code including all documentation. Note that this
-# feature is still experimental and incomplete at the
-# moment.
+# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
GENERATE_PERLMOD = NO
-# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
-# the necessary Makefile rules, Perl scripts and LaTeX code to be able
-# to generate PDF and DVI output from the Perl module output.
+# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
PERLMOD_LATEX = NO
-# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
-# nicely formatted so it can be parsed by a human reader.
-# This is useful
-# if you want to understand what is going on.
-# On the other hand, if this
-# tag is set to NO the size of the Perl module output will be much smaller
-# and Perl will parse it just the same.
+# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO, the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
PERLMOD_PRETTY = YES
-# The names of the make variables in the generated doxyrules.make file
-# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
-# This is useful so different doxyrules.make files included by the same
-# Makefile don't overwrite each other's variables.
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
PERLMOD_MAKEVAR_PREFIX =
@@ -1556,335 +2111,456 @@ PERLMOD_MAKEVAR_PREFIX =
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
-# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
-# evaluate all C-preprocessor directives found in the sources and include
-# files.
+# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
ENABLE_PREPROCESSING = YES
-# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
-# names in the source code. If set to NO (the default) only conditional
-# compilation will be performed. Macro expansion can be done in a controlled
-# way by setting EXPAND_ONLY_PREDEF to YES.
+# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
+# in the source code. If set to NO, only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
MACRO_EXPANSION = NO
-# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
-# then the macro expansion is limited to the macros specified with the
-# PREDEFINED and EXPAND_AS_DEFINED tags.
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
EXPAND_ONLY_PREDEF = NO
-# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
-# pointed to by INCLUDE_PATH will be searched when a #include is found.
+# If the SEARCH_INCLUDES tag is set to YES, the include files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
SEARCH_INCLUDES = YES
# The INCLUDE_PATH tag can be used to specify one or more directories that
-# contain include files that are not input files but should be processed by
-# the preprocessor.
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
INCLUDE_PATH =
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
# patterns (like *.h and *.hpp) to filter out the header-files in the
-# directories. If left blank, the patterns specified with FILE_PATTERNS will
-# be used.
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
INCLUDE_FILE_PATTERNS =
-# The PREDEFINED tag can be used to specify one or more macro names that
-# are defined before the preprocessor is started (similar to the -D option of
-# gcc). The argument of the tag is a list of macros of the form: name
-# or name=definition (no spaces). If the definition and the = are
-# omitted =1 is assumed. To prevent a macro definition from being
-# undefined via #undef or recursively expanded use the := operator
-# instead of the = operator.
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
PREDEFINED =
-# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
-# this tag can be used to specify a list of macro names that should be expanded.
-# The macro definition that is found in the sources will be used.
-# Use the PREDEFINED tag if you want to use a different macro definition that
-# overrules the definition found in the source code.
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
EXPAND_AS_DEFINED =
-# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
-# doxygen's preprocessor will remove all references to function-like macros
-# that are alone on a line, have an all uppercase name, and do not end with a
-# semicolon, because these will confuse the parser if not removed.
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have
+# an all uppercase name, and do not end with a semicolon. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
-# Configuration::additions related to external references
+# Configuration options related to external references
#---------------------------------------------------------------------------
-# The TAGFILES option can be used to specify one or more tagfiles. For each
-# tag file the location of the external documentation should be added. The
-# format of a tag file without this location is as follows:
-#
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
# TAGFILES = file1 file2 ...
# Adding location for the tag files is done as follows:
-#
# TAGFILES = file1=loc1 "file2 = loc2" ...
-# where "loc1" and "loc2" can be relative or absolute paths
-# or URLs. Note that each tag file must have a unique name (where the name does
-# NOT include the path). If a tag file is not located in the directory in which
-# doxygen is run, you must also specify the path to the tagfile here.
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have a unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
TAGFILES =
-# When a file name is specified after GENERATE_TAGFILE, doxygen will create
-# a tag file that is based on the input files it reads.
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
GENERATE_TAGFILE =
-# If the ALLEXTERNALS tag is set to YES all external classes will be listed
-# in the class index. If set to NO only the inherited external classes
-# will be listed.
+# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
+# the class index. If set to NO, only the inherited external classes will be
+# listed.
+# The default value is: NO.
ALLEXTERNALS = NO
-# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
-# in the modules index. If set to NO, only the current project's groups will
-# be listed.
+# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
EXTERNAL_GROUPS = YES
-# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed
-# in the related pages index. If set to NO, only the current project's
-# pages will be listed.
+# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
EXTERNAL_PAGES = YES
-# The PERL_PATH should be the absolute path and name of the perl script
-# interpreter (i.e. the result of `which perl').
-
-PERL_PATH = /usr/bin/perl
-
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
-# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
-# generate an inheritance diagram (in HTML, RTF and LaTeX) for classes with base
-# or super classes. Setting the tag to NO turns the diagrams off. Note that
-# this option also works with HAVE_DOT disabled, but it is recommended to
-# install and use dot, since it yields more powerful graphs.
+# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
CLASS_DIAGRAMS = YES
-# You can define message sequence charts within doxygen comments using the \msc
-# command. Doxygen will then run the mscgen tool (see
-# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
-# documentation. The MSCGEN_PATH tag allows you to specify the directory where
-# the mscgen tool resides. If left empty the tool is assumed to be found in the
-# default search path.
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
-MSCGEN_PATH =
+DIA_PATH =
-# If set to YES, the inheritance and collaboration graphs will hide
-# inheritance and usage relations if the target is undocumented
-# or is not a class.
+# If set to YES the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
HIDE_UNDOC_RELATIONS = YES
# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
-# available from the path. This tool is part of Graphviz, a graph visualization
-# toolkit from AT&T and Lucent Bell Labs. The other options in this section
-# have no effect if this option is set to NO (the default)
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
HAVE_DOT = NO
-# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
-# allowed to run in parallel. When set to 0 (the default) doxygen will
-# base this on the number of processors available in the system. You can set it
-# explicitly to a value larger than 0 to get control over the balance
-# between CPU load and processing speed.
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
DOT_NUM_THREADS = 0
-# By default doxygen will use the Helvetica font for all dot files that
-# doxygen generates. When you want a differently looking font you can specify
-# the font name using DOT_FONTNAME. You need to make sure dot is able to find
-# the font, which can be done by putting it in a standard location or by setting
-# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
-# directory containing the font.
+# When you want a differently looking font in the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTNAME = Helvetica
-# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
-# The default size is 10pt.
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTSIZE = 10
-# By default doxygen will tell dot to use the Helvetica font.
-# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to
-# set the path where dot can find it.
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTPATH =
-# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect inheritance relations. Setting this tag to YES will force the
-# CLASS_DIAGRAMS tag to NO.
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
CLASS_GRAPH = YES
-# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect implementation dependencies (inheritance, containment, and
-# class references variables) of the class with other documented classes.
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
COLLABORATION_GRAPH = YES
-# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for groups, showing the direct groups dependencies
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
GROUP_GRAPHS = YES
-# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
# collaboration diagrams in a style similar to the OMG's Unified Modeling
# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
UML_LOOK = NO
-# If the UML_LOOK tag is enabled, the fields and methods are shown inside
-# the class node. If there are many fields or methods and many nodes the
-# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS
-# threshold limits the number of items for each type to make the size more
-# manageable. Set this to 0 for no limit. Note that the threshold may be
-# exceeded by 50% before the limit is enforced.
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag UML_LOOK is set to YES.
UML_LIMIT_NUM_FIELDS = 10
-# If set to YES, the inheritance and collaboration graphs will show the
-# relations between templates and their instances.
+# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and
+# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS
+# tag is set to YES, doxygen will add type and arguments for attributes and
+# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen
+# will not generate fields with class member information in the UML graphs. The
+# class diagrams will look similar to the default class diagrams but using UML
+# notation for the relationships.
+# Possible values are: NO, YES and NONE.
+# The default value is: NO.
+# This tag requires that the tag UML_LOOK is set to YES.
+
+DOT_UML_DETAILS = NO
+
+# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters
+# to display on a single line. If the actual line length exceeds this threshold
+# significantly it will wrapped across multiple lines. Some heuristics are apply
+# to avoid ugly line breaks.
+# Minimum value: 0, maximum value: 1000, default value: 17.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_WRAP_THRESHOLD = 17
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
TEMPLATE_RELATIONS = NO
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
-# tags are set to YES then doxygen will generate a graph for each documented
-# file showing the direct and indirect include dependencies of the file with
-# other documented files.
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
INCLUDE_GRAPH = YES
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
-# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
-# documented header file showing the documented files that directly or
-# indirectly include this file.
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
INCLUDED_BY_GRAPH = YES
-# If the CALL_GRAPH and HAVE_DOT options are set to YES then
-# doxygen will generate a call dependency graph for every global function
-# or class method. Note that enabling this option will significantly increase
-# the time of a run. So in most cases it will be better to enable call graphs
-# for selected functions only using the \callgraph command.
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command. Disabling a call graph can be
+# accomplished by means of the command \hidecallgraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
CALL_GRAPH = NO
-# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
-# doxygen will generate a caller dependency graph for every global function
-# or class method. Note that enabling this option will significantly increase
-# the time of a run. So in most cases it will be better to enable caller
-# graphs for selected functions only using the \callergraph command.
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command. Disabling a caller graph can be
+# accomplished by means of the command \hidecallergraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
CALLER_GRAPH = NO
-# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
-# will generate a graphical hierarchy of all classes instead of a textual one.
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
GRAPHICAL_HIERARCHY = YES
-# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES
-# then doxygen will show the dependencies a directory has on other directories
-# in a graphical way. The dependency relations are determined by the #include
-# relations between the files in the directories.
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
DIRECTORY_GRAPH = YES
# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
-# generated by dot. Possible values are svg, png, jpg, or gif.
-# If left blank png will be used. If you choose svg you need to set
-# HTML_FILE_EXTENSION to xhtml in order to make the SVG files
-# visible in IE 9+ (other browsers do not have this requirement).
+# generated by dot. For an explanation of the image formats see the section
+# output formats in the documentation of the dot tool (Graphviz (see:
+# http://www.graphviz.org/)).
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
+# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
+# png:gdiplus:gdiplus.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
DOT_IMAGE_FORMAT = png
# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
# enable generation of interactive SVG images that allow zooming and panning.
-# Note that this requires a modern browser other than Internet Explorer.
-# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you
-# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files
-# visible. Older versions of IE do not have SVG support.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
INTERACTIVE_SVG = NO
-# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
DOT_PATH =
# The DOTFILE_DIRS tag can be used to specify one or more directories that
-# contain dot files that are included in the documentation (see the
-# \dotfile command).
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
DOTFILE_DIRS =
# The MSCFILE_DIRS tag can be used to specify one or more directories that
-# contain msc files that are included in the documentation (see the
-# \mscfile command).
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
MSCFILE_DIRS =
-# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
-# nodes that will be shown in the graph. If the number of nodes in a graph
-# becomes larger than this value, doxygen will truncate the graph, which is
-# visualized by representing a node as a red box. Note that doxygen if the
-# number of direct children of the root node in a graph is already larger than
-# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
-# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS =
+
+# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
+# path where java can find the plantuml.jar file. If left blank, it is assumed
+# PlantUML is not used or called during a preprocessing step. Doxygen will
+# generate a warning when it encounters a \startuml command in this case and
+# will not generate output for the diagram.
+
+PLANTUML_JAR_PATH =
+
+# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
+# configuration file for plantuml.
+
+PLANTUML_CFG_FILE =
+
+# When using plantuml, the specified paths are searched for files specified by
+# the !include statement in a plantuml block.
+
+PLANTUML_INCLUDE_PATH =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
DOT_GRAPH_MAX_NODES = 50
-# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
-# graphs generated by dot. A depth value of 3 means that only nodes reachable
-# from the root by following a path via at most 3 edges will be shown. Nodes
-# that lay further from the root node will be omitted. Note that setting this
-# option to 1 or 2 may greatly reduce the computation time needed for large
-# code bases. Also note that the size of a graph can be further restricted by
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
MAX_DOT_GRAPH_DEPTH = 0
# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
-# background. This is disabled by default, because dot on Windows does not
-# seem to support this out of the box. Warning: Depending on the platform used,
-# enabling this option may lead to badly anti-aliased labels on the edges of
-# a graph (i.e. they become hard to read).
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
DOT_TRANSPARENT = NO
-# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
# files in one run (i.e. multiple -o and -T options on the command line). This
-# makes dot run faster, but since only newer versions of dot (>1.8.10)
-# support this, this feature is disabled by default.
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
DOT_MULTI_TARGETS = YES
-# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
-# generate a legend page explaining the meaning of the various boxes and
-# arrows in the dot generated graphs.
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
GENERATE_LEGEND = YES
-# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
-# remove the intermediate dot files that are used to generate
-# the various graphs.
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate
+# files that are used to generate the various graphs.
+#
+# Note: This setting is not only used for dot files but also for msc and
+# plantuml temporary files.
+# The default value is: YES.
DOT_CLEANUP = YES
diff --git a/src/cjson/lua_cjson.c b/src/cjson/lua_cjson.c
index cf9e82c38e..c243f93c05 100644
--- a/src/cjson/lua_cjson.c
+++ b/src/cjson/lua_cjson.c
@@ -776,7 +776,7 @@ static void json_append_data(lua_State *l, json_config_t *cfg,
if (has_metatable) {
- nlua_pushref(l, nlua_empty_dict_ref);
+ nlua_pushref(l, nlua_get_empty_dict_ref(l));
if (lua_rawequal(l, -2, -1)) {
as_empty_dict = true;
} else {
@@ -822,7 +822,7 @@ static void json_append_data(lua_State *l, json_config_t *cfg,
}
break;
case LUA_TUSERDATA:
- nlua_pushref(l, nlua_nil_ref);
+ nlua_pushref(l, nlua_get_nil_ref(l));
bool is_nil = lua_rawequal(l, -2, -1);
lua_pop(l, 1);
if (is_nil) {
@@ -1110,7 +1110,7 @@ static int json_is_invalid_number(json_parse_t *json)
/* Reject numbers starting with 0x, or leading zeros */
if (*p == '0') {
- int ch2 = *(p + 1);
+ char ch2 = *(p + 1);
if ((ch2 | 0x20) == 'x' || /* Hex */
('0' <= ch2 && ch2 <= '9')) /* Leading zero */
@@ -1285,7 +1285,7 @@ static void json_parse_object_context(lua_State *l, json_parse_t *json)
/* Handle empty objects */
if (token.type == T_OBJ_END) {
- nlua_pushref(l, nlua_empty_dict_ref); \
+ nlua_pushref(l, nlua_get_empty_dict_ref(l)); \
lua_setmetatable(l, -2); \
json_decode_ascend(json);
return;
@@ -1392,7 +1392,7 @@ static void json_process_value(lua_State *l, json_parse_t *json,
if (use_luanil) {
lua_pushnil(l);
} else {
- nlua_pushref(l, nlua_nil_ref);
+ nlua_pushref(l, nlua_get_nil_ref(l));
}
break;;
default:
@@ -1549,7 +1549,15 @@ int lua_cjson_new(lua_State *l)
};
/* Initialise number conversions */
- fpconv_init();
+ lua_getfield(l, LUA_REGISTRYINDEX, "nvim.thread");
+ bool is_thread = lua_toboolean(l, -1);
+ lua_pop(l, 1);
+
+ // Since fpconv_init does not need to be called multiple times and is not
+ // thread safe, it should only be called in the main thread.
+ if (!is_thread) {
+ fpconv_init();
+ }
/* Test if array metatables are in registry */
lua_pushlightuserdata(l, json_lightudata_mask(&json_empty_array));
@@ -1582,7 +1590,7 @@ int lua_cjson_new(lua_State *l)
compat_luaL_setfuncs(l, reg, 1);
/* Set cjson.null */
- nlua_pushref(l, nlua_nil_ref);
+ nlua_pushref(l, nlua_get_nil_ref(l));
lua_setfield(l, -2, "null");
/* Set cjson.empty_array_mt */
diff --git a/src/clint.py b/src/clint.py
index 4b7bf002e6..28f6031a57 100755
--- a/src/clint.py
+++ b/src/clint.py
@@ -1,5 +1,4 @@
-#!/usr/bin/env python
-# vim: set fileencoding=utf-8
+#!/usr/bin/env python3
#
# Copyright (c) 2009 Google Inc. All rights reserved.
#
@@ -41,21 +40,15 @@ We do a small hack, which is to ignore //'s with "'s after them on the
same line, but it is far from perfect (in either direction).
"""
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-from __future__ import unicode_literals
import codecs
import copy
import getopt
-import math # for log
import os
import re
import sre_compile
import string
import sys
-import unicodedata
import json
import collections # for defaultdict
@@ -179,26 +172,19 @@ _ERROR_CATEGORIES = [
'build/deprecated',
'build/endif_comment',
'build/header_guard',
- 'build/include',
'build/include_alpha',
- 'build/include_order',
'build/printf_format',
'build/storage_class',
- 'build/useless_fattr',
- 'readability/alt_tokens',
'readability/bool',
'readability/braces',
- 'readability/fn_size',
'readability/multiline_comment',
'readability/multiline_string',
- 'readability/nolint',
'readability/nul',
'readability/todo',
'readability/utf8',
'readability/increment',
'runtime/arrays',
'runtime/int',
- 'runtime/invalid_increment',
'runtime/memset',
'runtime/printf',
'runtime/printf_format',
@@ -206,23 +192,13 @@ _ERROR_CATEGORIES = [
'runtime/deprecated',
'syntax/parenthesis',
'whitespace/alignment',
- 'whitespace/blank_line',
'whitespace/braces',
- 'whitespace/comma',
'whitespace/comments',
- 'whitespace/empty_conditional_body',
- 'whitespace/empty_loop_body',
- 'whitespace/end_of_line',
- 'whitespace/ending_newline',
'whitespace/indent',
- 'whitespace/line_length',
'whitespace/newline',
'whitespace/operators',
'whitespace/parens',
- 'whitespace/semicolon',
- 'whitespace/tab',
'whitespace/todo',
- 'whitespace/line_continuation',
'whitespace/cast',
]
@@ -232,11 +208,6 @@ _ERROR_CATEGORIES = [
# All entries here should start with a '-' or '+', as in the --filter= flag.
_DEFAULT_FILTERS = ['-build/include_alpha']
-# These constants define types of headers for use with
-# _IncludeState.CheckNextIncludeOrder().
-_C_SYS_HEADER = 1
-_OTHER_HEADER = 5
-
# These constants define the current inline assembly state
_NO_ASM = 0 # Outside of inline assembly block
_INSIDE_ASM = 1 # Inside inline assembly block
@@ -268,12 +239,12 @@ _line_length = 100
# The allowed extensions for file names
# This is set by --extensions flag.
-_valid_extensions = set(['c', 'h'])
+_valid_extensions = {'c', 'h'}
_RE_COMMENTLINE = re.compile(r'^\s*//')
-def ParseNolintSuppressions(filename, raw_line, linenum, error):
+def ParseNolintSuppressions(raw_line, linenum):
"""Updates the global list of error-suppressions.
Parses any NOLINT comments on the current line, updating the global
@@ -281,10 +252,8 @@ def ParseNolintSuppressions(filename, raw_line, linenum, error):
was malformed.
Args:
- filename: str, the name of the input file.
raw_line: str, the line of input text, with comments.
linenum: int, the number of the current line.
- error: function, an error handler.
"""
# FIXME(adonovan): "NOLINT(" is misparsed as NOLINT(*).
matched = _RE_SUPPRESSION.search(raw_line)
@@ -298,9 +267,6 @@ def ParseNolintSuppressions(filename, raw_line, linenum, error):
if category in _ERROR_CATEGORIES:
_error_suppressions.setdefault(
category, set()).add(linenum)
- else:
- error(filename, linenum, 'readability/nolint', 5,
- 'Unknown NOLINT error category: %s' % category)
def ParseKnownErrorSuppressions(filename, raw_lines, linenum):
@@ -373,97 +339,7 @@ def Search(pattern, s):
return _regexp_compile_cache[pattern].search(s)
-class _IncludeState(dict):
-
- """Tracks line numbers for includes, and the order in which includes appear.
-
- As a dict, an _IncludeState object serves as a mapping between include
- filename and line number on which that file was included.
-
- Call CheckNextIncludeOrder() once for each header in the file, passing
- in the type constants defined above.
-
- """
- # self._section will move monotonically through this set. If it ever
- # needs to move backwards, CheckNextIncludeOrder will raise an error.
- _INITIAL_SECTION = 0
- _C_SECTION = 2
- _OTHER_H_SECTION = 4
-
- _TYPE_NAMES = {
- _C_SYS_HEADER: 'C system header',
- _OTHER_HEADER: 'other header',
- }
- _SECTION_NAMES = {
- _INITIAL_SECTION: "... nothing. (This can't be an error.)",
- _C_SECTION: 'C system header',
- _OTHER_H_SECTION: 'other header',
- }
-
- def __init__(self):
- dict.__init__(self)
- self.ResetSection()
-
- def ResetSection(self):
- # The name of the current section.
- self._section = self._INITIAL_SECTION
- # The path of last found header.
- self._last_header = ''
-
- def SetLastHeader(self, header_path):
- self._last_header = header_path
-
- def CanonicalizeAlphabeticalOrder(self, header_path):
- """Returns a path canonicalized for alphabetical comparison.
-
- - replaces "-" with "_" so they both cmp the same.
- - lowercase everything, just in case.
-
- Args:
- header_path: Path to be canonicalized.
-
- Returns:
- Canonicalized path.
- """
- return header_path.replace('-', '_').lower()
-
- def CheckNextIncludeOrder(self, header_type):
- """Returns a non-empty error message if the next header is out of order.
-
- This function also updates the internal state to be ready to check
- the next include.
-
- Args:
- header_type: One of the _XXX_HEADER constants defined above.
-
- Returns:
- The empty string if the header is in the right order, or an
- error message describing what's wrong.
-
- """
- error_message = ('Found %s after %s' %
- (self._TYPE_NAMES[header_type],
- self._SECTION_NAMES[self._section]))
-
- last_section = self._section
-
- if header_type == _C_SYS_HEADER:
- if self._section <= self._C_SECTION:
- self._section = self._C_SECTION
- else:
- self._last_header = ''
- return error_message
- else:
- assert header_type == _OTHER_HEADER
- self._section = self._OTHER_H_SECTION
-
- if last_section != self._section:
- self._last_header = ''
-
- return ''
-
-
-class _CppLintState(object):
+class _CppLintState:
"""Maintains module-wide state.."""
@@ -474,6 +350,7 @@ class _CppLintState(object):
self.filters = _DEFAULT_FILTERS[:]
self.counting = 'total' # In what way are we counting errors?
self.errors_by_category = {} # string to int dict storing error counts
+ self.stdin_filename = ''
# output format:
# "emacs" - format that emacs can parse (default)
@@ -558,7 +435,7 @@ class _CppLintState(object):
fname, lines, category = json.loads(line)
lines = tuple(lines)
self.suppressed_errors[fname][lines].add(category)
- except IOError:
+ except OSError:
pass
def RecordErrorsTo(self, fname):
@@ -624,7 +501,7 @@ def _SetFilters(filters):
_cpplint_state.SetFilters(filters)
-class _FunctionState(object):
+class _FunctionState:
"""Tracks current function name and the number of lines in its body."""
@@ -651,32 +528,6 @@ class _FunctionState(object):
if self.in_a_function:
self.lines_in_function += 1
- def Check(self, error, filename, linenum):
- """Report if too many lines in function body.
-
- Args:
- error: The function to call with any errors found.
- filename: The name of the current file.
- linenum: The number of the line to check.
- """
- if Match(r'T(EST|est)', self.current_function):
- base_trigger = self._TEST_TRIGGER
- else:
- base_trigger = self._NORMAL_TRIGGER
- trigger = base_trigger * 2**_VerboseLevel()
-
- if self.lines_in_function > trigger:
- error_level = int(
- math.log(self.lines_in_function / base_trigger, 2))
- # 50 => 0, 100 => 1, 200 => 2, 400 => 3, 800 => 4, 1600 => 5, ...
- if error_level > 5:
- error_level = 5
- error(filename, linenum, 'readability/fn_size', error_level,
- 'Small and focused functions are preferred:'
- ' %s has %d non-comment lines'
- ' (error triggered by exceeding %d lines).' % (
- self.current_function, self.lines_in_function, trigger))
-
def End(self):
"""Stop analyzing function body."""
self.in_a_function = False
@@ -695,7 +546,8 @@ class FileInfo:
def FullName(self):
"""Make Windows paths like Unix."""
- return os.path.abspath(self._filename).replace('\\', '/')
+ abspath = str(os.path.abspath(self._filename))
+ return abspath.replace('\\', '/')
def RelativePath(self):
"""FullName with <prefix>/src/nvim/ chopped off."""
@@ -799,6 +651,9 @@ def Error(filename, linenum, category, confidence, message):
elif _cpplint_state.output_format == 'eclipse':
sys.stdout.write('%s:%s: warning: %s [%s] [%d]\n' % (
filename, linenum, message, category, confidence))
+ elif _cpplint_state.output_format == 'gh_action':
+ sys.stdout.write('::error file=%s,line=%s::%s [%s] [%d]\n' % (
+ filename, linenum, message, category, confidence))
else:
sys.stdout.write('%s:%s: %s [%s] [%d]\n' % (
filename, linenum, message, category, confidence))
@@ -903,7 +758,7 @@ def CleanseComments(line):
return _RE_PATTERN_CLEANSE_LINE_C_COMMENTS.sub('', line)
-class CleansedLines(object):
+class CleansedLines:
"""Holds 5 copies of all lines with different preprocessing applied to them.
@@ -973,11 +828,10 @@ BRACES = {
'(': ')',
'{': '}',
'[': ']',
- # '<': '>', C++-specific pair removed
}
-CLOSING_BRACES = dict(((v, k) for k, v in BRACES.items()))
+CLOSING_BRACES = {v: k for k, v in BRACES.items()}
def GetExprBracesPosition(clean_lines, linenum, pos):
@@ -1137,6 +991,7 @@ def ReverseCloseExpression(clean_lines, linenum, pos):
"""
line = clean_lines.elided[linenum]
endchar = line[pos]
+ startchar = None
if endchar not in ')}]>':
return (line, 0, -1)
if endchar == ')':
@@ -1200,9 +1055,9 @@ def CheckForHeaderGuard(filename, lines, error):
lines: An array of strings, each representing a line of the file.
error: The function to call with any errors found.
"""
- if filename.endswith('.c.h') or FileInfo(filename).RelativePath() in set((
+ if filename.endswith('.c.h') or FileInfo(filename).RelativePath() in {
'func_attr.h',
- )):
+ }:
return
cppvar = GetHeaderGuardCPPVariable(filename)
@@ -1246,8 +1101,7 @@ def CheckForHeaderGuard(filename, lines, error):
if ifndef != cppvar + '_':
error_level = 5
- ParseNolintSuppressions(filename, lines[ifndef_linenum], ifndef_linenum,
- error)
+ ParseNolintSuppressions(lines[ifndef_linenum], ifndef_linenum)
error(filename, ifndef_linenum, 'build/header_guard', error_level,
'#ifndef header guard has wrong style, please use: %s' % cppvar)
@@ -1262,8 +1116,7 @@ def CheckForHeaderGuard(filename, lines, error):
if endif != ('#endif // %s' % (cppvar + '_')):
error_level = 5
- ParseNolintSuppressions(filename, lines[endif_linenum], endif_linenum,
- error)
+ ParseNolintSuppressions(lines[endif_linenum], endif_linenum)
error(filename, endif_linenum, 'build/header_guard', error_level,
'#endif line should be "#endif // %s"' % cppvar)
@@ -1286,7 +1139,7 @@ def CheckForBadCharacters(filename, lines, error):
error: The function to call with any errors found.
"""
for linenum, line in enumerate(lines):
- if u'\ufffd' in line:
+ if '\ufffd' in line:
error(filename, linenum, 'readability/utf8', 5,
'Line contains invalid UTF-8'
' (or Unicode replacement character).')
@@ -1295,24 +1148,6 @@ def CheckForBadCharacters(filename, lines, error):
5, 'Line contains NUL byte.')
-def CheckForNewlineAtEOF(filename, lines, error):
- """Logs an error if there is no newline char at the end of the file.
-
- Args:
- filename: The name of the current file.
- lines: An array of strings, each representing a line of the file.
- error: The function to call with any errors found.
- """
-
- # The array lines() was created by adding two newlines to the
- # original file (go figure), then splitting on \n.
- # To verify that the file ends in \n, we just have to make sure the
- # last-but-two element of lines() exists and is empty.
- if len(lines) < 3 or lines[-2]:
- error(filename, len(lines) - 2, 'whitespace/ending_newline', 5,
- 'Could not find a newline character at the end of the file.')
-
-
def CheckForMultilineCommentsAndStrings(filename, clean_lines, linenum, error):
"""Logs an error if we see /* ... */ or "..." that extend past one line.
@@ -1489,13 +1324,7 @@ def CheckOSFunctions(filename, clean_lines, linenum, error):
'...) instead of ' + function + '...).')
-# Matches invalid increment: *count++, which moves pointer instead of
-# incrementing a value.
-_RE_PATTERN_INVALID_INCREMENT = re.compile(
- r'^\s*\*\w+(\+\+|--);')
-
-
-class _BlockInfo(object):
+class _BlockInfo:
"""Stores information about a generic block of code."""
@@ -1505,7 +1334,7 @@ class _BlockInfo(object):
self.inline_asm = _NO_ASM
-class _PreprocessorInfo(object):
+class _PreprocessorInfo:
"""Stores checkpoints of nesting stacks when #if/#else is seen."""
@@ -1520,7 +1349,7 @@ class _PreprocessorInfo(object):
self.seen_else = False
-class _NestingState(object):
+class _NestingState:
"""Holds states related to parsing braces."""
@@ -1601,14 +1430,12 @@ class _NestingState(object):
# TODO(unknown): unexpected #endif, issue warning?
pass
- def Update(self, filename, clean_lines, linenum, error):
+ def Update(self, clean_lines, linenum):
"""Update nesting state with current line.
Args:
- filename: The name of the current file.
clean_lines: A CleansedLines instance containing the file.
linenum: The number of the line to check.
- error: The function to call with any errors found.
"""
line = clean_lines.elided[linenum]
@@ -1674,8 +1501,7 @@ class _NestingState(object):
line = matched.group(2)
-def CheckForNonStandardConstructs(filename, clean_lines, linenum,
- nesting_state, error):
+def CheckForNonStandardConstructs(filename, clean_lines, linenum, error):
r"""Logs an error if we see certain non-ANSI constructs ignored by gcc-2.
Complain about several constructs which gcc-2 accepts, but which are
@@ -1697,8 +1523,6 @@ def CheckForNonStandardConstructs(filename, clean_lines, linenum,
filename: The name of the current file.
clean_lines: A CleansedLines instance containing the file.
linenum: The number of the line to check.
- nesting_state: A _NestingState instance which maintains information about
- the current stack of nested blocks being parsed.
error: A callable to which errors are reported, which takes 4 arguments:
filename, line number, error level, and message
"""
@@ -1896,7 +1720,6 @@ def CheckForFunctionLengths(filename, clean_lines, linenum,
error(filename, linenum, 'readability/fn_size', 5,
'Lint failed to find start of function body.')
elif Match(r'^\}\s*$', line): # function end
- function_state.Check(error, filename, linenum)
function_state.End()
elif not Match(r'^\s*$', line):
function_state.Count() # Count non-blank/non-comment lines.
@@ -2158,7 +1981,7 @@ def CheckExpressionAlignment(filename, clean_lines, linenum, error, startpos=0):
del level_starts[depth]
-def CheckSpacing(filename, clean_lines, linenum, nesting_state, error):
+def CheckSpacing(filename, clean_lines, linenum, error):
"""Checks for the correctness of various spacing issues in the code.
Things we check for: spaces around operators, spaces after
@@ -2172,8 +1995,6 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error):
filename: The name of the current file.
clean_lines: A CleansedLines instance containing the file.
linenum: The number of the line to check.
- nesting_state: A _NestingState instance which maintains information about
- the current stack of nested blocks being parsed.
error: The function to call with any errors found.
"""
@@ -2212,7 +2033,6 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error):
# for the case where the previous line is indented 6 spaces, which
# may happen when the initializers of a constructor do not fit into
# a 80 column line.
- exception = False
if Match(r' {6}\w', prev_line): # Initializer list?
# We are looking for the opening column of initializer list,
# which should be indented 4 spaces to cause 6 space indentation
@@ -2221,39 +2041,6 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error):
while (search_position >= 0
and Match(r' {6}\w', elided[search_position])):
search_position -= 1
- exception = (search_position >= 0
- and elided[search_position][:5] == ' :')
- else:
- # Search for the function arguments or an initializer list. We
- # use a simple heuristic here: If the line is indented 4 spaces;
- # and we have a closing paren, without the opening paren,
- # followed by an opening brace or colon (for initializer lists)
- # we assume that it is the last line of a function header. If
- # we have a colon indented 4 spaces, it is an initializer list.
- exception = (Match(r' {4}\w[^\(]*\)\s*(const\s*)?(\{\s*$|:)',
- prev_line)
- or Match(r' {4}:', prev_line))
-
- if not exception:
- error(filename, linenum, 'whitespace/blank_line', 2,
- 'Redundant blank line at the start of a code block '
- 'should be deleted.')
- # Ignore blank lines at the end of a block in a long if-else
- # chain, like this:
- # if (condition1) {
- # // Something followed by a blank line
- #
- # } else if (condition2) {
- # // Something else
- # }
- if linenum + 1 < clean_lines.NumLines():
- next_line = raw[linenum + 1]
- if (next_line
- and Match(r'\s*}', next_line)
- and next_line.find('} else ') == -1):
- error(filename, linenum, 'whitespace/blank_line', 3,
- 'Redundant blank line at the end of a code block '
- 'should be deleted.')
# Next, we complain if there's a comment too near the text
commentpos = line.find('//')
@@ -2390,12 +2177,6 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error):
error(filename, linenum, 'whitespace/operators', 4,
'Extra space for operator %s' % match.group(1))
- # A pet peeve of mine: no spaces after an if, while, switch, or for
- match = Search(r' (if\(|for\(|while\(|switch\()', line)
- if match:
- error(filename, linenum, 'whitespace/parens', 5,
- 'Missing space before ( in %s' % match.group(1))
-
# For if/for/while/switch, the left and right parens should be
# consistent about how many spaces are inside the parens, and
# there should either be zero or one spaces inside the parens.
@@ -2416,34 +2197,11 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error):
'Should have zero or one spaces inside ( and ) in %s' %
match.group(1))
- # You should always have a space after a comma (either as fn arg or
- # operator).
- #
- # This does not apply when the non-space character following the
- # comma is another comma, since the only time when that happens is
- # for empty macro arguments.
- #
- # We run this check in two passes: first pass on elided lines to
- # verify that lines contain missing whitespaces, second pass on raw
- # lines to confirm that those missing whitespaces are not due to
- # elided comments.
- if Search(r',[^,\s]', line) and Search(r',[^,\s]', raw[linenum]):
- error(filename, linenum, 'whitespace/comma', 3,
- 'Missing space after ,')
-
- # You should always have a space after a semicolon
- # except for few corner cases
- # TODO(unknown): clarify if 'if (1) { return 1;}' is requires one more
- # space after ;
- if Search(r';[^\s};\\)/]', line):
- error(filename, linenum, 'whitespace/semicolon', 3,
- 'Missing space after ;')
-
# Next we will look for issues with function calls.
CheckSpacingForFunctionCall(filename, line, linenum, error)
# Check whether everything inside expressions is aligned correctly
- if any((line.find(k) >= 0 for k in BRACES if k != '{')):
+ if any(line.find(k) >= 0 for k in BRACES if k != '{'):
CheckExpressionAlignment(filename, clean_lines, linenum, error)
# Except after an opening paren, or after another opening brace (in case of
@@ -2486,9 +2244,6 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error):
for offset in range(endlinenum + 1,
min(endlinenum + 3, clean_lines.NumLines() - 1)):
trailing_text += clean_lines.elided[offset]
- if not Match(r'^[\s}]*[{.;,)<\]]', trailing_text):
- error(filename, linenum, 'whitespace/braces', 5,
- 'Missing space before {')
# Make sure '} else {' has spaces.
if Search(r'}else', line):
@@ -2501,19 +2256,6 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error):
error(filename, linenum, 'whitespace/braces', 5,
'Extra space before [')
- # You shouldn't have a space before a semicolon at the end of the line.
- if Search(r':\s*;\s*$', line):
- error(filename, linenum, 'whitespace/semicolon', 5,
- 'Semicolon defining empty statement. Use {} instead.')
- elif Search(r'^\s*;\s*$', line):
- error(filename, linenum, 'whitespace/semicolon', 5,
- 'Line contains only semicolon. If this should be an empty'
- ' statement, use {} instead.')
- elif Search(r'\s+;\s*$', line):
- error(filename, linenum, 'whitespace/semicolon', 5,
- 'Extra space before last semicolon. If this should be an empty '
- 'statement, use {} instead.')
-
if Search(r'\{(?!\})\S', line):
error(filename, linenum, 'whitespace/braces', 5,
'Missing space after {')
@@ -2521,18 +2263,6 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error):
error(filename, linenum, 'whitespace/braces', 5,
'Missing space before }')
- if Search(r'\S {2,}\\$', line):
- error(filename, linenum, 'whitespace/line_continuation', 5,
- 'Too many spaces before \\, line continuation character must be '
- 'preceded by exactly one space. For “blank lines” '
- 'it is preferred to use the same amount of spaces as preceding '
- 'indent')
-
- if Match(r'^ +#', line):
- error(filename, linenum, 'whitespace/indent', 5,
- 'Must not indent preprocessor directives, use 1-space indent '
- 'after the hash')
-
cast_line = re.sub(r'^# *define +\w+\([^)]*\)', '', line)
match = Search(r'(?<!\bkvec_t)'
r'(?<!\bkvec_withinit_t)'
@@ -2660,7 +2390,7 @@ def CheckBraces(filename, clean_lines, linenum, error):
clean_lines, linenum, pos)
if endline[endpos:].find('{') == -1:
error(filename, linenum, 'readability/braces', 5,
- '{0} should always use braces'.format(blockstart))
+ '{} should always use braces'.format(blockstart))
# If braces come on one side of an else, they should be on both.
# However, we have to worry about "else if" that spans multiple lines!
@@ -2809,67 +2539,7 @@ def CheckBraces(filename, clean_lines, linenum, error):
"You don't need a ; after a }")
-def CheckEmptyBlockBody(filename, clean_lines, linenum, error):
- """Look for empty loop/conditional body with only a single semicolon.
-
- Args:
- filename: The name of the current file.
- clean_lines: A CleansedLines instance containing the file.
- linenum: The number of the line to check.
- error: The function to call with any errors found.
- """
-
- # Search for loop keywords at the beginning of the line. Because only
- # whitespaces are allowed before the keywords, this will also ignore most
- # do-while-loops, since those lines should start with closing brace.
- #
- # We also check "if" blocks here, since an empty conditional block
- # is likely an error.
- line = clean_lines.elided[linenum]
- matched = Match(r'\s*(for|while|if)\s*\(', line)
- if matched:
- # Find the end of the conditional expression
- (end_line, end_linenum, end_pos) = CloseExpression(
- clean_lines, linenum, line.find('('))
-
- # Output warning if what follows the condition expression is a
- # semicolon. No warning for all other cases, including whitespace or
- # newline, since we have a separate check for semicolons preceded by
- # whitespace.
- if end_pos >= 0 and Match(r';', end_line[end_pos:]):
- if matched.group(1) == 'if':
- error(filename, end_linenum,
- 'whitespace/empty_conditional_body', 5,
- 'Empty conditional bodies should use {}')
- else:
- error(filename, end_linenum, 'whitespace/empty_loop_body', 5,
- 'Empty loop bodies should use {} or continue')
-
-
-def GetLineWidth(line):
- """Determines the width of the line in column positions.
-
- Args:
- line: A string, which may be a Unicode string.
-
- Returns:
- The width of the line in column positions, accounting for Unicode
- combining characters and wide characters.
- """
- if isinstance(line, str):
- width = 0
- for uc in unicodedata.normalize('NFC', line):
- if unicodedata.east_asian_width(uc) in ('W', 'F'):
- width += 2
- elif not unicodedata.combining(uc):
- width += 1
- return width
- else:
- return len(line)
-
-
-def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state,
- error):
+def CheckStyle(filename, clean_lines, linenum, error):
"""Checks rules from the 'C++ style rules' section of cppguide.html.
Most of these rules are hard to test (naming, comment style), but we
@@ -2880,9 +2550,6 @@ def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state,
filename: The name of the current file.
clean_lines: A CleansedLines instance containing the file.
linenum: The number of the line to check.
- file_extension: The extension (without the dot) of the filename.
- nesting_state: A _NestingState instance which maintains information about
- the current stack of nested blocks being parsed.
error: The function to call with any errors found.
"""
@@ -2892,10 +2559,6 @@ def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state,
raw_lines = clean_lines.lines_without_raw_strings
line = raw_lines[linenum]
- if line.find('\t') != -1:
- error(filename, linenum, 'whitespace/tab', 1,
- 'Tab found; better to use spaces')
-
# One or three blank spaces at the beginning of the line is weird; it's
# hard to reconcile that with 2-space indents.
# NOTE: here are the conditions rob pike used for his tests. Mine aren't
@@ -2911,47 +2574,9 @@ def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state,
# if(prevodd && match(prevprev, " +for \\(")) complain = 0;
initial_spaces = 0
cleansed_line = clean_lines.elided[linenum]
+
while initial_spaces < len(line) and line[initial_spaces] == ' ':
initial_spaces += 1
- if line and line[-1].isspace():
- error(filename, linenum, 'whitespace/end_of_line', 4,
- 'Line ends in whitespace. Consider deleting these extra spaces.')
- # There are certain situations we allow one space, notably for section
- # labels
- elif ((initial_spaces == 1 or initial_spaces == 3) and
- not Match(r'\s*\w+\s*:\s*$', cleansed_line)):
- error(filename, linenum, 'whitespace/indent', 3,
- 'Weird number of spaces at line-start. '
- 'Are you using a 2-space indent?')
-
- # Check if the line is a header guard.
- is_header_guard = False
- if file_extension == 'h':
- cppvar = GetHeaderGuardCPPVariable(filename)
- if (line.startswith('#ifndef %s' % cppvar) or
- line.startswith('#define %s' % cppvar) or
- line.startswith('#endif // %s' % cppvar)):
- is_header_guard = True
- # #include lines and header guards can be long, since there's no clean way
- # to split them.
- #
- # URLs can be long too. It's possible to split these, but it makes them
- # harder to cut&paste.
- #
- # The "$Id:...$" comment may also get very long without it being the
- # developers fault.
- if (not line.startswith('#include') and not is_header_guard and
- not Match(r'^\s*//.*http(s?)://\S*$', line) and
- not Match(r'^// \$Id:.*#[0-9]+ \$$', line)):
- line_width = GetLineWidth(line)
- extended_length = int((_line_length * 1.25))
- if line_width > extended_length:
- error(filename, linenum, 'whitespace/line_length', 4,
- 'Lines should very rarely be longer than %i characters' %
- extended_length)
- elif line_width > _line_length:
- error(filename, linenum, 'whitespace/line_length', 2,
- 'Lines should be <= %i characters long' % _line_length)
if (cleansed_line.count(';') > 1 and
# for loops are allowed two ;'s (and may run over two lines).
@@ -2967,96 +2592,10 @@ def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state,
# Some more style checks
CheckBraces(filename, clean_lines, linenum, error)
- CheckEmptyBlockBody(filename, clean_lines, linenum, error)
- CheckSpacing(filename, clean_lines, linenum, nesting_state, error)
+ CheckSpacing(filename, clean_lines, linenum, error)
-_RE_PATTERN_INCLUDE_NEW_STYLE = re.compile(r'#include +"[^/]+\.h"')
_RE_PATTERN_INCLUDE = re.compile(r'^\s*#\s*include\s*([<"])([^>"]*)[>"].*$')
-# Matches the first component of a filename delimited by -s and _s. That is:
-# _RE_FIRST_COMPONENT.match('foo').group(0) == 'foo'
-# _RE_FIRST_COMPONENT.match('foo.cc').group(0) == 'foo'
-# _RE_FIRST_COMPONENT.match('foo-bar_baz.cc').group(0) == 'foo'
-# _RE_FIRST_COMPONENT.match('foo_bar-baz.cc').group(0) == 'foo'
-_RE_FIRST_COMPONENT = re.compile(r'^[^-_.]+')
-
-
-def _ClassifyInclude(fileinfo, include, is_system):
- """Figures out what kind of header 'include' is.
-
- Args:
- fileinfo: The current file cpplint is running over. A FileInfo instance.
- include: The path to a #included file.
- is_system: True if the #include used <> rather than "".
-
- Returns:
- One of the _XXX_HEADER constants.
- """
- if is_system:
- return _C_SYS_HEADER
- return _OTHER_HEADER
-
-
-def CheckIncludeLine(filename, clean_lines, linenum, include_state, error):
- """Check rules that are applicable to #include lines.
-
- Strings on #include lines are NOT removed from elided line, to make
- certain tasks easier. However, to prevent false positives, checks
- applicable to #include lines in CheckLanguage must be put here.
-
- Args:
- filename : The name of the current file.
- clean_lines : A CleansedLines instance containing the file.
- linenum : The number of the line to check.
- include_state : An _IncludeState instance in which the headers are
- inserted.
- error : The function to call with any errors found.
- """
- fileinfo = FileInfo(filename)
-
- line = clean_lines.lines[linenum]
-
- # "include" should use the new style "foo/bar.h" instead of just "bar.h"
- # XXX: neovim doesn't currently use this style
- # if _RE_PATTERN_INCLUDE_NEW_STYLE.search(line):
- # error(filename, linenum, 'build/include', 4,
- # 'Include the directory when naming .h files')
-
- # we shouldn't include a file more than once. actually, there are a
- # handful of instances where doing so is okay, but in general it's
- # not.
- match = _RE_PATTERN_INCLUDE.search(line)
- if match:
- include = match.group(2)
- is_system = (match.group(1) == '<')
- if include in include_state:
- if is_system or not include.endswith('.c.h'):
- error(filename, linenum, 'build/include', 4,
- '"%s" already included at %s:%s' %
- (include, filename, include_state[include]))
- else:
- include_state[include] = linenum
-
- # We want to ensure that headers appear in the right order:
- # 1) for foo.cc, foo.h (preferred location)
- # 2) c system files
- # 3) cpp system files
- # 4) for foo.cc, foo.h (deprecated location)
- # 5) other google headers
- #
- # We classify each include statement as one of those 5 types
- # using a number of techniques. The include_state object keeps
- # track of the highest type seen, and complains if we see a
- # lower type after that.
- error_message = include_state.CheckNextIncludeOrder(
- _ClassifyInclude(fileinfo, include, is_system))
- if error_message:
- error(filename, linenum, 'build/include_order', 4,
- '%s. Should be: c system, c++ system, other.'
- % error_message)
- canonical_include = include_state.CanonicalizeAlphabeticalOrder(
- include)
- include_state.SetLastHeader(canonical_include)
def _GetTextInside(text, start_pattern):
@@ -3116,8 +2655,7 @@ def _GetTextInside(text, start_pattern):
return text[start_position:position - 1]
-def CheckLanguage(filename, clean_lines, linenum, file_extension,
- include_state, nesting_state, error):
+def CheckLanguage(filename, clean_lines, linenum, error):
"""Checks rules from the 'C++ language rules' section of cppguide.html.
Some of these rules are hard to test (function overloading, using
@@ -3127,11 +2665,6 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension,
filename : The name of the current file.
clean_lines : A CleansedLines instance containing the file.
linenum : The number of the line to check.
- file_extension : The extension (without the dot) of the filename.
- include_state : An _IncludeState instance in which the headers are
- inserted.
- nesting_state : A _NestingState instance which maintains information
- about the current stack of nested blocks being parsed.
error : The function to call with any errors found.
"""
# If the line is empty or consists of entirely a comment, no need to
@@ -3140,16 +2673,6 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension,
if not line:
return
- match = _RE_PATTERN_INCLUDE.search(line)
- if match:
- CheckIncludeLine(filename, clean_lines, linenum, include_state, error)
- return
-
- # Reset include state across preprocessor directives. This is meant
- # to silence warnings for conditional includes.
- if Match(r'^\s*#\s*(?:ifdef|elif|else|endif)\b', line):
- include_state.ResetSection()
-
# TODO(unknown): figure out if they're using default arguments in fn proto.
# Check if people are using the verboten C basic types.
@@ -3191,8 +2714,8 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension,
r'|li_(?:next|prev|tv))\b', line)
if match:
error(filename, linenum, 'runtime/deprecated', 4,
- 'Accessing list_T internals directly is prohibited '
- '(hint: see commit d46e37cb4c71)')
+ 'Accessing list_T internals directly is prohibited; '
+ 'see https://github.com/neovim/neovim/wiki/List-management-in-Neovim')
# Check for suspicious usage of "if" like
# } if (a == b) {
@@ -3211,8 +2734,9 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension,
if printf_args:
match = Match(r'([\w.\->()]+)$', printf_args)
if match and match.group(1) != '__VA_ARGS__':
- function_name = re.search(r'\b((?:string)?printf)\s*\(',
- line, re.I).group(1)
+ function_name_groups = re.search(r'\b((?:string)?printf)\s*\(', line, re.I)
+ assert function_name_groups
+ function_name = function_name_groups.group(1)
error(filename, linenum, 'runtime/printf', 4,
'Potential format string bug. Do %s("%%s", %s) instead.'
% (function_name, match.group(1)))
@@ -3278,7 +2802,7 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension,
if match:
token = match.group(1)
error(filename, linenum, 'readability/bool', 4,
- 'Use %s instead of %s.' % (token.lower(), token))
+ 'Use {} instead of {}.'.format(token.lower(), token))
# Detect MAYBE
match = Search(r'\b(MAYBE)\b', line)
@@ -3305,19 +2829,16 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension,
'for(;; action)')
-def ProcessLine(filename, file_extension, clean_lines, line,
- include_state, function_state, nesting_state, error,
+def ProcessLine(filename, clean_lines, line,
+ function_state, nesting_state, error,
extra_check_functions=[]):
"""Processes a single line in the file.
Args:
filename : Filename of the file that is being processed.
- file_extension : The extension (dot not included) of the file.
clean_lines : An array of strings, each representing a line of
the file, with comments stripped.
line : Number of line being processed.
- include_state : An _IncludeState instance in which the headers are
- inserted.
function_state : A _FunctionState instance which counts function
lines, etc.
nesting_state : A _NestingState instance which maintains
@@ -3332,19 +2853,16 @@ def ProcessLine(filename, file_extension, clean_lines, line,
"""
raw_lines = clean_lines.raw_lines
init_lines = clean_lines.init_lines
- ParseNolintSuppressions(filename, raw_lines[line], line, error)
- nesting_state.Update(filename, clean_lines, line, error)
+ ParseNolintSuppressions(raw_lines[line], line)
+ nesting_state.Update(clean_lines, line)
if nesting_state.stack and nesting_state.stack[-1].inline_asm != _NO_ASM:
return
CheckForFunctionLengths(filename, clean_lines, line, function_state, error)
CheckForMultilineCommentsAndStrings(filename, clean_lines, line, error)
CheckForOldStyleComments(filename, init_lines[line], line, error)
- CheckStyle(
- filename, clean_lines, line, file_extension, nesting_state, error)
- CheckLanguage(filename, clean_lines, line, file_extension, include_state,
- nesting_state, error)
- CheckForNonStandardConstructs(filename, clean_lines, line,
- nesting_state, error)
+ CheckStyle(filename, clean_lines, line, error)
+ CheckLanguage(filename, clean_lines, line, error)
+ CheckForNonStandardConstructs(filename, clean_lines, line, error)
CheckPosixThreading(filename, clean_lines, line, error)
CheckMemoryFunctions(filename, clean_lines, line, error)
CheckOSFunctions(filename, clean_lines, line, error)
@@ -3370,7 +2888,6 @@ def ProcessFileData(filename, file_extension, lines, error,
lines = (['// marker so line numbers and indices both start at 1'] + lines +
['// marker so line numbers end in a known way'])
- include_state = _IncludeState()
function_state = _FunctionState()
nesting_state = _NestingState()
@@ -3387,6 +2904,7 @@ def ProcessFileData(filename, file_extension, lines, error,
if not IsErrorSuppressedByNolint(category, linenum):
key = init_lines[linenum - 1 if linenum else 0:linenum + 2]
err = [filename, key, category]
+ assert _cpplint_state.record_errors_file
json.dump(err, _cpplint_state.record_errors_file)
_cpplint_state.record_errors_file.write('\n')
Error(filename, linenum, category, confidence, message)
@@ -3399,16 +2917,14 @@ def ProcessFileData(filename, file_extension, lines, error,
RemoveMultiLineComments(filename, lines, error)
clean_lines = CleansedLines(lines, init_lines)
for line in range(clean_lines.NumLines()):
- ProcessLine(filename, file_extension, clean_lines, line,
- include_state, function_state, nesting_state, error,
+ ProcessLine(filename, clean_lines, line,
+ function_state, nesting_state, error,
extra_check_functions)
# We check here rather than inside ProcessLine so that we see raw
# lines rather than "cleaned" lines.
CheckForBadCharacters(filename, lines, error)
- CheckForNewlineAtEOF(filename, lines, error)
-
def ProcessFile(filename, vlevel, extra_check_functions=[]):
"""Does neovim-lint on a single file.
@@ -3439,8 +2955,6 @@ def ProcessFile(filename, vlevel, extra_check_functions=[]):
if filename == '-':
stdin = sys.stdin.read()
- if sys.version_info < (3, 0):
- stdin = stdin.decode('utf8')
lines = stdin.split('\n')
if _cpplint_state.stdin_filename is not None:
filename = _cpplint_state.stdin_filename
@@ -3455,7 +2969,7 @@ def ProcessFile(filename, vlevel, extra_check_functions=[]):
lines[linenum] = lines[linenum].rstrip('\r')
carriage_return_found = True
- except IOError:
+ except OSError:
sys.stderr.write(
"Skipping input '%s': Can't open for reading\n" % filename)
return
@@ -3536,13 +3050,13 @@ def ParseArguments(args):
counting_style = ''
record_errors_file = None
suppress_errors_file = None
- stdin_filename = None
+ stdin_filename = ''
for (opt, val) in opts:
if opt == '--help':
PrintUsage(None)
elif opt == '--output':
- if val not in ('emacs', 'vs7', 'eclipse'):
+ if val not in ('emacs', 'vs7', 'eclipse', 'gh_action'):
PrintUsage('The only allowed output formats are emacs,'
' vs7 and eclipse.')
output_format = val
diff --git a/src/coverity-model.c b/src/coverity-model.c
index 2fd55c332c..4338b75ea2 100644
--- a/src/coverity-model.c
+++ b/src/coverity-model.c
@@ -42,3 +42,72 @@ int tv_dict_add(dict_T *const d, dictitem_T *const item)
{
__coverity_escape__(item);
}
+
+void *malloc(size_t size)
+{
+ int has_mem;
+ if (has_mem)
+ return __coverity_alloc__(size);
+ else
+ return 0;
+}
+
+void *try_malloc(size_t size)
+{
+ size_t allocated_size = size ? size : 1;
+ return malloc(allocated_size);
+}
+
+void *xmalloc(size_t size)
+{
+ void *p = malloc(size);
+ if (!p)
+ __coverity_panic__();
+ return p;
+}
+
+void xfree(void * ptr)
+{
+ __coverity_free__(ptr);
+}
+
+void *xcalloc(size_t count, size_t size)
+{
+ size_t allocated_count = count && size ? count : 1;
+ size_t allocated_size = count && size ? size : 1;
+ void *p = try_malloc(allocated_count * allocated_size);
+ if (!p)
+ __coverity_panic__();
+ __coverity_writeall0__(p);
+ return p;
+}
+
+void *xrealloc(void *ptr, size_t size)
+{
+ __coverity_escape__(ptr);
+ void * p = xmalloc(size);
+ __coverity_writeall__(p);
+ return p;
+}
+
+void *xmallocz(size_t size)
+{
+ void * p = malloc(size + 1);
+ ((char*)p)[size] = 0;
+ return p;
+}
+
+void * xmemdupz(const void * data, size_t len)
+{
+ void * p = xmallocz(len);
+ __coverity_writeall__(p);
+ ((char*)p)[len] = 0;
+ return p;
+}
+
+void * xmemdup(const void *data, size_t len)
+{
+ void * p = xmalloc(len);
+ __coverity_writeall__(p);
+ return p;
+}
diff --git a/man/Makefile b/src/man/Makefile
index 3c0457e2ab..3c0457e2ab 100644
--- a/man/Makefile
+++ b/src/man/Makefile
diff --git a/man/nvim.1 b/src/man/nvim.1
index 43dfc21dc7..9f35014ee8 100644
--- a/man/nvim.1
+++ b/src/man/nvim.1
@@ -188,7 +188,7 @@ loading plugins is also skipped.
Use
.Ar shada
instead of the default
-.Pa ~/.local/share/nvim/shada/main.shada .
+.Pa ~/.local/state/nvim/shada/main.shada .
If
.Ar shada
is
@@ -326,7 +326,7 @@ Print version information and exit.
.Sh ENVIRONMENT
.Bl -tag -width Fl
.It Ev NVIM_LOG_FILE
-Low-level log file, usually found at ~/.cache/nvim/log.
+Low-level log file, usually found at ~/.local/state/nvim/log.
:help $NVIM_LOG_FILE
.It Ev VIM
Used to locate user files, such as init.vim.
@@ -340,12 +340,20 @@ Path to the user-local configuration directory, see
Defaults to
.Pa ~/.config .
:help xdg
-.It Ev XDG_DATA_HOME
+.It Ev XDG_STATE_HOME
Like
.Ev XDG_CONFIG_HOME ,
but used to store data not generally edited by the user,
namely swap, backup, and ShaDa files.
Defaults to
+.Pa ~/.local/state .
+:help xdg
+.It Ev XDG_DATA_HOME
+Like
+.Ev XDG_CONFIG_HOME ,
+but used to store data not generally edited by the user,
+things like runtime files.
+Defaults to
.Pa ~/.local/share .
:help xdg
.It Ev VIMINIT
diff --git a/src/mpack/lmpack.c b/src/mpack/lmpack.c
index 126f2f3824..53d7092a0c 100644
--- a/src/mpack/lmpack.c
+++ b/src/mpack/lmpack.c
@@ -246,7 +246,7 @@ static mpack_uint32_t lmpack_objlen(lua_State *L, int *is_array)
}
end:
- if ((size_t)-1 > (mpack_uint32_t)-1 && len > (mpack_uint32_t)-1)
+ if ((size_t)-1 > (mpack_uint32_t)-1 && len > (mpack_uint32_t)-1) // -V560
/* msgpack spec doesn't allow lengths > 32 bits */
len = (mpack_uint32_t)-1;
assert(top == lua_gettop(L));
diff --git a/src/mpack/mpack_core.c b/src/mpack/mpack_core.c
index f8ca63b7a3..4ee67a032a 100644
--- a/src/mpack/mpack_core.c
+++ b/src/mpack/mpack_core.c
@@ -12,8 +12,6 @@
# define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
#endif
-static int mpack_rtoken(const char **buf, size_t *buflen,
- mpack_token_t *tok);
static int mpack_rpending(const char **b, size_t *nl, mpack_tokbuf_t *tb);
static int mpack_rvalue(mpack_token_type_t t, mpack_uint32_t l,
const char **b, size_t *bl, mpack_token_t *tok);
@@ -52,7 +50,10 @@ MPACK_API int mpack_read(mpack_tokbuf_t *tokbuf, const char **buf,
int status;
size_t initial_ppos, ptrlen, advanced;
const char *ptr, *ptr_save;
- assert(*buf && *buflen);
+ assert(*buf);
+ if (*buflen == 0) {
+ return MPACK_EOF;
+ }
if (tokbuf->passthrough) {
/* pass data from str/bin/ext directly as a MPACK_TOKEN_CHUNK, adjusting
@@ -170,8 +171,7 @@ MPACK_API int mpack_write(mpack_tokbuf_t *tokbuf, char **buf, size_t *buflen,
return MPACK_OK;
}
-static int mpack_rtoken(const char **buf, size_t *buflen,
- mpack_token_t *tok)
+int mpack_rtoken(const char **buf, size_t *buflen, mpack_token_t *tok)
{
unsigned char t = ADVANCE(buf, buflen);
if (t < 0x80) {
diff --git a/src/mpack/mpack_core.h b/src/mpack/mpack_core.h
index 9edd13c41e..1d601bc82d 100644
--- a/src/mpack/mpack_core.h
+++ b/src/mpack/mpack_core.h
@@ -83,5 +83,7 @@ MPACK_API int mpack_read(mpack_tokbuf_t *tb, const char **b, size_t *bl,
mpack_token_t *tok) FUNUSED FNONULL;
MPACK_API int mpack_write(mpack_tokbuf_t *tb, char **b, size_t *bl,
const mpack_token_t *tok) FUNUSED FNONULL;
+int mpack_rtoken(const char **buf, size_t *buflen,
+ mpack_token_t *tok);
#endif /* MPACK_CORE_H */
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt
index 94572b57cd..360993de68 100644..100755
--- a/src/nvim/CMakeLists.txt
+++ b/src/nvim/CMakeLists.txt
@@ -13,14 +13,15 @@ if(USE_GCOV)
endif()
if(WIN32)
- # tell MinGW compiler to enable wmain
- set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -municode")
+ if(MINGW)
+ # Enable wmain
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -municode")
+ endif()
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
- set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework CoreFoundation")
- set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -framework CoreFoundation")
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework CoreServices")
+ set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -framework CoreServices")
endif()
-set(TOUCHES_DIR ${PROJECT_BINARY_DIR}/touches)
set(GENERATOR_DIR ${CMAKE_CURRENT_LIST_DIR}/generators)
set(GENERATED_DIR ${PROJECT_BINARY_DIR}/src/nvim/auto)
set(BINARY_LIB_DIR ${PROJECT_BINARY_DIR}/lib/nvim/)
@@ -39,6 +40,7 @@ set(GENERATED_UI_EVENTS ${GENERATED_DIR}/ui_events.generated.h)
set(GENERATED_UI_EVENTS_CALL ${GENERATED_DIR}/ui_events_call.generated.h)
set(GENERATED_UI_EVENTS_REMOTE ${GENERATED_DIR}/ui_events_remote.generated.h)
set(GENERATED_UI_EVENTS_BRIDGE ${GENERATED_DIR}/ui_events_bridge.generated.h)
+set(GENERATED_UI_EVENTS_CLIENT ${GENERATED_DIR}/ui_events_client.generated.h)
set(GENERATED_UI_EVENTS_METADATA ${GENERATED_DIR}/api/private/ui_events_metadata.generated.h)
set(GENERATED_EX_CMDS_ENUM ${GENERATED_INCLUDES_DIR}/ex_cmds_enum.generated.h)
set(GENERATED_EX_CMDS_DEFS ${GENERATED_DIR}/ex_cmds_defs.generated.h)
@@ -54,15 +56,17 @@ set(EVENTS_GENERATOR ${GENERATOR_DIR}/gen_events.lua)
set(KEYSETS_GENERATOR ${GENERATOR_DIR}/gen_keysets.lua)
set(OPTIONS_GENERATOR ${GENERATOR_DIR}/gen_options.lua)
set(UNICODE_TABLES_GENERATOR ${GENERATOR_DIR}/gen_unicode_tables.lua)
-set(UNICODE_DIR ${PROJECT_SOURCE_DIR}/unicode)
+set(UNICODE_DIR ${PROJECT_SOURCE_DIR}/src/unicode)
set(GENERATED_UNICODE_TABLES ${GENERATED_DIR}/unicode_tables.generated.h)
set(VIM_MODULE_FILE ${GENERATED_DIR}/lua/vim_module.generated.h)
-set(LUA_VIM_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/src/nvim/lua/vim.lua)
+set(LUA_EDITOR_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/_editor.lua)
set(LUA_SHARED_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/shared.lua)
set(LUA_INSPECT_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/inspect.lua)
set(LUA_F_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/F.lua)
set(LUA_META_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/_meta.lua)
set(LUA_FILETYPE_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/filetype.lua)
+set(LUA_INIT_PACKAGES_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/_init_packages.lua)
+set(LUA_KEYMAP_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/keymap.lua)
set(CHAR_BLOB_GENERATOR ${GENERATOR_DIR}/gen_char_blob.lua)
set(LINT_SUPPRESS_FILE ${PROJECT_BINARY_DIR}/errors.json)
set(LINT_SUPPRESS_URL_BASE "https://raw.githubusercontent.com/neovim/doc/gh-pages/reports/clint")
@@ -75,10 +79,10 @@ set(LINT_SUPPRESSES_ARCHIVE ${LINT_SUPPRESSES_ROOT}/errors.tar.gz)
set(LINT_SUPPRESSES_TOUCH_FILE "${TOUCHES_DIR}/unpacked-clint-errors-archive")
set(LINT_SUPPRESSES_INSTALL_SCRIPT "${PROJECT_SOURCE_DIR}/cmake/InstallClintErrors.cmake")
-file(GLOB UNICODE_FILES ${UNICODE_DIR}/*.txt)
-file(GLOB API_HEADERS api/*.h)
+glob_wrapper(UNICODE_FILES ${UNICODE_DIR}/*.txt)
+glob_wrapper(API_HEADERS api/*.h)
list(REMOVE_ITEM API_HEADERS ${CMAKE_CURRENT_LIST_DIR}/api/ui_events.in.h)
-file(GLOB MSGPACK_RPC_HEADERS msgpack_rpc/*.h)
+glob_wrapper(MSGPACK_RPC_HEADERS msgpack_rpc/*.h)
include_directories(${GENERATED_DIR})
include_directories(${CACHED_GENERATED_DIR})
@@ -90,10 +94,10 @@ file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR})
file(MAKE_DIRECTORY ${LINT_SUPPRESSES_ROOT})
file(MAKE_DIRECTORY ${LINT_SUPPRESSES_ROOT}/src)
-file(GLOB NVIM_SOURCES *.c)
-file(GLOB NVIM_HEADERS *.h)
-file(GLOB EXTERNAL_SOURCES ../xdiff/*.c ../mpack/*.c ../cjson/*.c)
-file(GLOB EXTERNAL_HEADERS ../xdiff/*.h ../mpack/*.h ../cjson/*.h)
+glob_wrapper(NVIM_SOURCES *.c)
+glob_wrapper(NVIM_HEADERS *.h)
+glob_wrapper(EXTERNAL_SOURCES ../xdiff/*.c ../mpack/*.c ../cjson/*.c)
+glob_wrapper(EXTERNAL_HEADERS ../xdiff/*.h ../mpack/*.h ../cjson/*.h)
foreach(subdir
os
@@ -113,13 +117,13 @@ foreach(subdir
file(MAKE_DIRECTORY ${GENERATED_DIR}/${subdir})
file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/${subdir})
- file(GLOB sources ${subdir}/*.c)
- file(GLOB headers ${subdir}/*.h)
+ glob_wrapper(sources ${subdir}/*.c)
+ glob_wrapper(headers ${subdir}/*.h)
list(APPEND NVIM_SOURCES ${sources})
list(APPEND NVIM_HEADERS ${headers})
endforeach()
-file(GLOB UNIT_TEST_FIXTURES ${PROJECT_SOURCE_DIR}/test/unit/fixtures/*.c)
+glob_wrapper(UNIT_TEST_FIXTURES ${PROJECT_SOURCE_DIR}/test/unit/fixtures/*.c)
# Sort file lists to ensure generated files are created in the same order from
# build to build.
@@ -133,6 +137,9 @@ foreach(sfile ${NVIM_SOURCES})
if(${f} MATCHES "^(regexp_nfa.c)$")
list(APPEND to_remove ${sfile})
endif()
+ if(${f} MATCHES "^(regexp_bt.c)$")
+ list(APPEND to_remove ${sfile})
+ endif()
if(WIN32 AND ${f} MATCHES "^(pty_process_unix.c)$")
list(APPEND to_remove ${sfile})
endif()
@@ -151,61 +158,48 @@ list(REMOVE_ITEM NVIM_SOURCES ${to_remove})
# Legacy files that do not yet pass -Wconversion.
set(CONV_SOURCES
- diff.c
- edit.c
- eval.c
- eval/funcs.c
- eval/userfunc.c
- ex_cmds.c
- ex_docmd.c
- fileio.c
lua/treesitter.c
mbyte.c
memline.c
- message.c
regexp.c
screen.c
search.c
spell.c
spellfile.c
syntax.c
- tag.c
window.c)
foreach(sfile ${CONV_SOURCES})
if(NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/${sfile}")
message(FATAL_ERROR "${sfile} doesn't exist (it was added to CONV_SOURCES)")
endif()
endforeach()
-# xdiff, mpack, lua-cjson: inlined external project, we don't maintain it. #9306
-list(APPEND CONV_SOURCES ${EXTERNAL_SOURCES})
if(NOT MSVC)
set_source_files_properties(
${CONV_SOURCES} PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -Wno-conversion")
- # gperf generates ANSI-C with incorrect linkage, ignore it.
- check_c_compiler_flag(-Wstatic-in-inline HAS_WSTATIC_IN_INLINE)
- if(HAS_WSTATIC_IN_INLINE)
- set_source_files_properties(
- eval/funcs.c PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -Wno-static-in-inline -Wno-conversion")
- else()
- set_source_files_properties(
- eval/funcs.c PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -Wno-conversion")
- endif()
+
+ # xdiff, mpack, lua-cjson: inlined external project, we don't maintain it. #9306
+ set_source_files_properties(
+ ${EXTERNAL_SOURCES} PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -Wno-conversion -Wno-missing-noreturn -Wno-missing-format-attribute -Wno-double-promotion")
+
+ set_source_files_properties(
+ eval/funcs.c PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -Wno-conversion")
endif()
if(NOT "${MIN_LOG_LEVEL}" MATCHES "^$")
add_definitions(-DMIN_LOG_LEVEL=${MIN_LOG_LEVEL})
endif()
+if(DEBUG OR CLANG_ASAN_UBSAN OR CLANG_MSAN OR CLANG_TSAN)
+ add_definitions(-DEXITFREE)
+endif()
+
get_directory_property(gen_cdefs COMPILE_DEFINITIONS)
foreach(gen_cdef ${gen_cdefs} DO_NOT_DEFINE_EMPTY_ATTRIBUTES)
if(NOT ${gen_cdef} MATCHES "INCLUDE_GENERATED_DECLARATIONS")
list(APPEND gen_cflags "-D${gen_cdef}")
endif()
endforeach()
-if(CLANG_ASAN_UBSAN OR CLANG_MSAN OR CLANG_TSAN)
- list(APPEND gen_cflags "-DEXITFREE")
-endif()
get_directory_property(gen_includes INCLUDE_DIRECTORIES)
foreach(gen_include ${gen_includes} ${LUA_PREFERRED_INCLUDE_DIRS})
@@ -228,43 +222,31 @@ function(get_preproc_output varname iname)
endif()
endfunction()
-# Handle generating version from Git.
-set(use_git_version 0)
-if(NVIM_VERSION_MEDIUM)
- message(STATUS "NVIM_VERSION_MEDIUM: ${NVIM_VERSION_MEDIUM}")
-elseif(EXISTS ${PROJECT_SOURCE_DIR}/.git)
- find_program(GIT_EXECUTABLE git)
- if(GIT_EXECUTABLE)
- message(STATUS "Using NVIM_VERSION_MEDIUM from Git")
- set(use_git_version 1)
- else()
- message(STATUS "Skipping version-string generation (cannot find git)")
- endif()
-endif()
-if(use_git_version)
- # Create a update_version_stamp target to update the version during build.
- file(RELATIVE_PATH relbuild "${PROJECT_SOURCE_DIR}" "${CMAKE_BINARY_DIR}")
- add_custom_target(update_version_stamp ALL
- COMMAND ${LUA_PRG} scripts/update_version_stamp.lua
- ${relbuild}/config/auto/versiondef_git.h
- "v${NVIM_VERSION_MAJOR}.${NVIM_VERSION_MINOR}.${NVIM_VERSION_PATCH}${NVIM_VERSION_PRERELEASE}"
- WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
- BYPRODUCTS ${CMAKE_BINARY_DIR}/config/auto/versiondef_git.h)
-else()
- file(WRITE ${CMAKE_BINARY_DIR}/config/auto/versiondef_git.h "")
-endif()
+set(NVIM_VERSION_GIT_H ${PROJECT_BINARY_DIR}/cmake.config/auto/versiondef_git.h)
+add_custom_target(update_version_stamp
+ COMMAND ${CMAKE_COMMAND}
+ -DNVIM_VERSION_MAJOR=${NVIM_VERSION_MAJOR}
+ -DNVIM_VERSION_MINOR=${NVIM_VERSION_MINOR}
+ -DNVIM_VERSION_PATCH=${NVIM_VERSION_PATCH}
+ -DNVIM_VERSION_PRERELEASE=${NVIM_VERSION_PRERELEASE}
+ -DOUTPUT=${NVIM_VERSION_GIT_H}
+ -P ${PROJECT_SOURCE_DIR}/cmake/GenerateVersion.cmake
+ WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
+ BYPRODUCTS ${NVIM_VERSION_GIT_H})
# NVIM_GENERATED_FOR_HEADERS: generated headers to be included in headers
# NVIM_GENERATED_FOR_SOURCES: generated headers to be included in sources
# NVIM_GENERATED_SOURCES: generated source files
# These lists must be mutually exclusive.
foreach(sfile ${NVIM_SOURCES}
+ "${CMAKE_CURRENT_LIST_DIR}/regexp_bt.c"
"${CMAKE_CURRENT_LIST_DIR}/regexp_nfa.c"
${GENERATED_API_DISPATCH}
"${GENERATED_UI_EVENTS_CALL}"
"${GENERATED_UI_EVENTS_REMOTE}"
"${GENERATED_UI_EVENTS_BRIDGE}"
"${GENERATED_KEYSETS}"
+ "${GENERATED_UI_EVENTS_CLIENT}"
)
get_filename_component(full_d ${sfile} PATH)
file(RELATIVE_PATH d "${CMAKE_CURRENT_LIST_DIR}" "${full_d}")
@@ -284,9 +266,9 @@ foreach(sfile ${NVIM_SOURCES}
get_preproc_output(PREPROC_OUTPUT ${gf_i})
set(depends "${HEADER_GENERATOR}" "${sfile}")
- if(use_git_version AND "${f}" STREQUAL "version.c")
+ if("${f}" STREQUAL "version.c")
# Ensure auto/versiondef_git.h exists after "make clean".
- list(APPEND depends update_version_stamp)
+ list(APPEND depends "${NVIM_VERSION_GIT_H}")
endif()
add_custom_command(
OUTPUT "${gf_c_h}" "${gf_h_h}"
@@ -330,20 +312,25 @@ add_custom_command(
COMMAND ${CMAKE_COMMAND} -E env
"LUAC_PRG=${LUAC_PRG}"
${LUA_PRG} ${CHAR_BLOB_GENERATOR} -c ${VIM_MODULE_FILE}
- ${LUA_VIM_MODULE_SOURCE} vim_module
- ${LUA_SHARED_MODULE_SOURCE} shared_module
- ${LUA_INSPECT_MODULE_SOURCE} inspect_module
- ${LUA_F_MODULE_SOURCE} lua_F_module
- ${LUA_META_MODULE_SOURCE} lua_meta_module
- ${LUA_FILETYPE_MODULE_SOURCE} lua_filetype_module
+ ${LUA_INIT_PACKAGES_MODULE_SOURCE} "vim._init_packages"
+ ${LUA_INSPECT_MODULE_SOURCE} "vim.inspect"
+ ${LUA_EDITOR_MODULE_SOURCE} "vim._editor"
+ ${LUA_SHARED_MODULE_SOURCE} "vim.shared"
+ ${LUA_F_MODULE_SOURCE} "vim.F"
+ ${LUA_META_MODULE_SOURCE} "vim._meta"
+ ${LUA_FILETYPE_MODULE_SOURCE} "vim.filetype"
+ ${LUA_KEYMAP_MODULE_SOURCE} "vim.keymap"
DEPENDS
${CHAR_BLOB_GENERATOR}
- ${LUA_VIM_MODULE_SOURCE}
+ ${LUA_INIT_PACKAGES_MODULE_SOURCE}
+ ${LUA_EDITOR_MODULE_SOURCE}
${LUA_SHARED_MODULE_SOURCE}
${LUA_INSPECT_MODULE_SOURCE}
${LUA_F_MODULE_SOURCE}
${LUA_META_MODULE_SOURCE}
${LUA_FILETYPE_MODULE_SOURCE}
+ ${LUA_LOAD_PACKAGE_MODULE_SOURCE}
+ ${LUA_KEYMAP_MODULE_SOURCE}
VERBATIM
)
@@ -357,6 +344,7 @@ add_custom_command(
${GENERATED_UI_EVENTS_REMOTE}
${GENERATED_UI_EVENTS_BRIDGE}
${GENERATED_UI_EVENTS_METADATA}
+ ${GENERATED_UI_EVENTS_CLIENT}
COMMAND ${LUA_PRG} ${API_UI_EVENTS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_LIST_DIR}/api/ui_events.in.h
${GENERATED_UI_EVENTS}
@@ -364,6 +352,7 @@ add_custom_command(
${GENERATED_UI_EVENTS_REMOTE}
${GENERATED_UI_EVENTS_BRIDGE}
${GENERATED_UI_EVENTS_METADATA}
+ ${GENERATED_UI_EVENTS_CLIENT}
DEPENDS
${API_UI_EVENTS_GENERATOR}
${GENERATOR_C_GRAMMAR}
@@ -387,7 +376,7 @@ list(APPEND NVIM_GENERATED_FOR_SOURCES
)
list(APPEND NVIM_GENERATED_SOURCES
- "${PROJECT_BINARY_DIR}/config/auto/pathdef.c"
+ "${PROJECT_BINARY_DIR}/cmake.config/auto/pathdef.c"
)
add_custom_command(OUTPUT ${GENERATED_EX_CMDS_ENUM} ${GENERATED_EX_CMDS_DEFS}
@@ -396,14 +385,9 @@ add_custom_command(OUTPUT ${GENERATED_EX_CMDS_ENUM} ${GENERATED_EX_CMDS_DEFS}
DEPENDS ${EX_CMDS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/ex_cmds.lua
)
-if(NOT GPERF_PRG)
- message(FATAL_ERROR "gperf was not found.")
-endif()
add_custom_command(OUTPUT ${GENERATED_FUNCS} ${FUNCS_DATA}
COMMAND ${LUA_PRG} ${FUNCS_GENERATOR}
- ${CMAKE_CURRENT_LIST_DIR} ${GENERATED_DIR} ${API_METADATA} ${FUNCS_DATA}
- COMMAND ${GPERF_PRG}
- ${GENERATED_DIR}/funcs.generated.h.gperf --output-file=${GENERATED_FUNCS}
+ ${CMAKE_CURRENT_LIST_DIR} ${LUA_SHARED_MODULE_SOURCE} ${GENERATED_DIR} ${API_METADATA} ${FUNCS_DATA}
DEPENDS ${FUNCS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/eval.lua ${API_METADATA}
)
list(APPEND NVIM_GENERATED_FOR_SOURCES
@@ -451,7 +435,6 @@ endif()
if(WIN32)
list(APPEND NVIM_LINK_LIBRARIES netapi32)
- list(APPEND NVIM_LINK_LIBRARIES ${WINPTY_LIBRARIES})
endif()
# Use "luv" as imported library, to work around CMake using "-lluv" for
@@ -493,6 +476,9 @@ add_executable(nvim ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS}
${EXTERNAL_SOURCES} ${EXTERNAL_HEADERS})
target_link_libraries(nvim ${NVIM_EXEC_LINK_LIBRARIES})
install_helper(TARGETS nvim)
+if(MSVC)
+ install(FILES $<TARGET_PDB_FILE:nvim> DESTINATION ${CMAKE_INSTALL_BINDIR} OPTIONAL)
+endif()
set_property(TARGET nvim APPEND PROPERTY
INCLUDE_DIRECTORIES ${LUA_PREFERRED_INCLUDE_DIRS})
@@ -535,8 +521,6 @@ if(WIN32)
diff.exe
tee.exe
win32yank.exe
- winpty-agent.exe
- winpty.dll
xxd.exe
# Dependencies for neovim-qt
@@ -689,17 +673,14 @@ if(CLANG_ASAN_UBSAN)
set(SANITIZE_RECOVER -fno-sanitize-recover) # Clang 3.5-
endif()
endif()
- set_property(TARGET nvim APPEND PROPERTY COMPILE_DEFINITIONS EXITFREE)
set_property(TARGET nvim APPEND PROPERTY COMPILE_OPTIONS ${SANITIZE_RECOVER} -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=address -fsanitize=undefined -fsanitize-blacklist=${PROJECT_SOURCE_DIR}/src/.asan-blacklist)
set_property(TARGET nvim APPEND_STRING PROPERTY LINK_FLAGS "-fsanitize=address -fsanitize=undefined ")
elseif(CLANG_MSAN)
message(STATUS "Enabling Clang memory sanitizer for nvim.")
- set_property(TARGET nvim APPEND PROPERTY COMPILE_DEFINITIONS EXITFREE)
set_property(TARGET nvim APPEND PROPERTY COMPILE_OPTIONS -fsanitize=memory -fsanitize-memory-track-origins -fno-omit-frame-pointer -fno-optimize-sibling-calls)
set_property(TARGET nvim APPEND_STRING PROPERTY LINK_FLAGS "-fsanitize=memory -fsanitize-memory-track-origins ")
elseif(CLANG_TSAN)
message(STATUS "Enabling Clang thread sanitizer for nvim.")
- set_property(TARGET nvim APPEND PROPERTY COMPILE_DEFINITIONS EXITFREE)
set_property(TARGET nvim APPEND PROPERTY COMPILE_OPTIONS -fsanitize=thread)
set_property(TARGET nvim APPEND PROPERTY COMPILE_OPTIONS -fPIE)
set_property(TARGET nvim APPEND_STRING PROPERTY LINK_FLAGS "-fsanitize=thread ")
@@ -783,6 +764,12 @@ add_custom_command(
add_download(${LINT_SUPPRESS_FILE} ${LINT_SUPPRESS_URL} off)
+if(CI_BUILD)
+ set(LINT_OUTPUT_FORMAT gh_action)
+else()
+ set(LINT_OUTPUT_FORMAT vs7)
+endif()
+
set(LINT_NVIM_REL_SOURCES)
foreach(sfile ${LINT_NVIM_SOURCES})
get_test_target("" "${sfile}" r suffix)
@@ -792,7 +779,7 @@ foreach(sfile ${LINT_NVIM_SOURCES})
set(touch_file "${TOUCHES_DIR}/ran-clint-${suffix}")
add_custom_command(
OUTPUT ${touch_file}
- COMMAND ${LINT_PRG} --suppress-errors=${suppress_file} ${rsfile}
+ COMMAND ${LINT_PRG} --suppress-errors=${suppress_file} --output=${LINT_OUTPUT_FORMAT} ${rsfile}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
COMMAND ${CMAKE_COMMAND} -E touch ${touch_file}
DEPENDS ${LINT_PRG} ${sfile} ${LINT_SUPPRESSES_TOUCH_FILE}
@@ -800,14 +787,21 @@ foreach(sfile ${LINT_NVIM_SOURCES})
list(APPEND LINT_TARGETS ${touch_file})
list(APPEND LINT_NVIM_REL_SOURCES ${rsfile})
endforeach()
-add_custom_target(clint DEPENDS ${LINT_TARGETS})
+add_custom_target(lintc DEPENDS ${LINT_TARGETS})
+
+add_glob_targets(
+ TARGET lintuncrustify
+ COMMAND ${UNCRUSTIFY_PRG}
+ FLAGS -c "${PROJECT_SOURCE_DIR}/src/uncrustify.cfg" -q --check
+ FILES ${LINT_NVIM_SOURCES}
+ )
add_custom_target(
- clint-full
+ lintcfull
COMMAND
- ${LINT_PRG} --suppress-errors=${LINT_SUPPRESS_FILE} ${LINT_NVIM_REL_SOURCES}
+ ${LINT_PRG} --suppress-errors=${LINT_SUPPRESS_FILE} --output=${LINT_OUTPUT_FORMAT} ${LINT_NVIM_REL_SOURCES}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
- DEPENDS ${LINT_PRG} ${LINT_NVIM_SOURCES} ${LINT_SUPPRESS_FILE}
+ DEPENDS ${LINT_PRG} ${LINT_NVIM_SOURCES} ${LINT_SUPPRESS_FILE} lintuncrustify
)
add_custom_target(generated-sources DEPENDS
diff --git a/src/nvim/README.md b/src/nvim/README.md
index 4efb42b896..91fb3ca2f6 100644
--- a/src/nvim/README.md
+++ b/src/nvim/README.md
@@ -23,7 +23,7 @@ Logs
Low-level log messages sink to `$NVIM_LOG_FILE`.
-UI events are logged at DEBUG level (`DEBUG_LOG_LEVEL`).
+UI events are logged at DEBUG level (`LOGLVL_DBG`).
rm -rf build/
make CMAKE_EXTRA_FLAGS="-DMIN_LOG_LEVEL=0"
@@ -38,7 +38,7 @@ alternate file (e.g. stderr) use `LOG_CALLSTACK_TO_FILE(FILE*)`. Requires
Many log messages have a shared prefix, such as "UI" or "RPC". Use the shell to
filter the log, e.g. at DEBUG level you might want to exclude UI messages:
- tail -F ~/.cache/nvim/log | cat -v | stdbuf -o0 grep -v UI | stdbuf -o0 tee -a log
+ tail -F ~/.local/state/nvim/log | cat -v | stdbuf -o0 grep -v UI | stdbuf -o0 tee -a log
Build with ASAN
---------------
@@ -68,7 +68,7 @@ Configure the sanitizer(s) via these environment variables:
export ASAN_OPTIONS="detect_leaks=0:log_path=$HOME/logs/asan"
# Show backtraces in the logs.
export UBSAN_OPTIONS=print_stacktrace=1
- export MSAN_OPTIONS="log_path=${HOME}/logs/tsan"
+ export MSAN_OPTIONS="log_path=${HOME}/logs/msan"
export TSAN_OPTIONS="log_path=${HOME}/logs/tsan"
Logs will be written to `${HOME}/logs/*san.PID` then.
@@ -204,9 +204,14 @@ Then you can compare `bar` with another session, to debug TUI behavior.
### TUI redraw
-Set the 'writedelay' option to see where and when the UI is painted.
+Set the 'writedelay' and 'redrawdebug' options to see where and when the UI is painted.
- :set writedelay=1
+ :set writedelay=50 rdb=compositor
+
+Note: neovim uses an internal screenbuffer to only send minimal updates even if a large
+region is repainted internally. To also highlight excess internal redraws, use
+
+ :set writedelay=50 rdb=compositor,nodelta
### Terminal reference
diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c
new file mode 100644
index 0000000000..bf6402f938
--- /dev/null
+++ b/src/nvim/api/autocmd.c
@@ -0,0 +1,1016 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+#include <stdbool.h>
+#include <stdio.h>
+
+#include "lauxlib.h"
+#include "nvim/api/autocmd.h"
+#include "nvim/api/private/defs.h"
+#include "nvim/api/private/helpers.h"
+#include "nvim/ascii.h"
+#include "nvim/buffer.h"
+#include "nvim/eval/typval.h"
+#include "nvim/fileio.h"
+#include "nvim/lua/executor.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "api/autocmd.c.generated.h"
+#endif
+
+#define AUCMD_MAX_PATTERNS 256
+
+// Copy string or array of strings into an empty array.
+// Get the event number, unless it is an error. Then goto `goto_name`.
+#define GET_ONE_EVENT(event_nr, event_str, goto_name) \
+ char *__next_ev; \
+ event_T event_nr = \
+ event_name2nr(event_str.data.string.data, &__next_ev); \
+ if (event_nr >= NUM_EVENTS) { \
+ api_set_error(err, kErrorTypeValidation, "unexpected event"); \
+ goto goto_name; \
+ }
+
+// ID for associating autocmds created via nvim_create_autocmd
+// Used to delete autocmds from nvim_del_autocmd
+static int64_t next_autocmd_id = 1;
+
+/// Get all autocommands that match the corresponding {opts}.
+///
+/// These examples will get autocommands matching ALL the given criteria:
+/// <pre>
+/// -- Matches all criteria
+/// autocommands = vim.api.nvim_get_autocmds({
+/// group = "MyGroup",
+/// event = {"BufEnter", "BufWinEnter"},
+/// pattern = {"*.c", "*.h"}
+/// })
+///
+/// -- All commands from one group
+/// autocommands = vim.api.nvim_get_autocmds({
+/// group = "MyGroup",
+/// })
+/// </pre>
+///
+/// NOTE: When multiple patterns or events are provided, it will find all the autocommands that
+/// match any combination of them.
+///
+/// @param opts Dictionary with at least one of the following:
+/// - group (string|integer): the autocommand group name or id to match against.
+/// - event (string|array): event or events to match against |autocmd-events|.
+/// - pattern (string|array): pattern or patterns to match against |autocmd-pattern|.
+/// @return Array of autocommands matching the criteria, with each item
+/// containing the following fields:
+/// - id (number): the autocommand id (only when defined with the API).
+/// - group (integer): the autocommand group id.
+/// - group_name (string): the autocommand group name.
+/// - desc (string): the autocommand description.
+/// - event (string): the autocommand event.
+/// - command (string): the autocommand command. Note: this will be empty if a callback is set.
+/// - callback (function|string|nil): Lua function or name of a Vim script function
+/// which is executed when this autocommand is triggered.
+/// - once (boolean): whether the autocommand is only run once.
+/// - pattern (string): the autocommand pattern.
+/// If the autocommand is buffer local |autocmd-buffer-local|:
+/// - buflocal (boolean): true if the autocommand is buffer local.
+/// - buffer (number): the buffer number.
+Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err)
+ FUNC_API_SINCE(9)
+{
+ // TODO(tjdevries): Would be cool to add nvim_get_autocmds({ id = ... })
+
+ Array autocmd_list = ARRAY_DICT_INIT;
+ char *pattern_filters[AUCMD_MAX_PATTERNS];
+ char pattern_buflocal[BUFLOCAL_PAT_LEN];
+
+ Array buffers = ARRAY_DICT_INIT;
+
+ bool event_set[NUM_EVENTS] = { false };
+ bool check_event = false;
+
+ int group = 0;
+
+ switch (opts->group.type) {
+ case kObjectTypeNil:
+ break;
+ case kObjectTypeString:
+ group = augroup_find(opts->group.data.string.data);
+ if (group < 0) {
+ api_set_error(err, kErrorTypeValidation, "invalid augroup passed.");
+ goto cleanup;
+ }
+ break;
+ case kObjectTypeInteger:
+ group = (int)opts->group.data.integer;
+ char *name = augroup_name(group);
+ if (!augroup_exists(name)) {
+ api_set_error(err, kErrorTypeValidation, "invalid augroup passed.");
+ goto cleanup;
+ }
+ break;
+ default:
+ api_set_error(err, kErrorTypeValidation, "group must be a string or an integer.");
+ goto cleanup;
+ }
+
+ if (opts->event.type != kObjectTypeNil) {
+ check_event = true;
+
+ Object v = opts->event;
+ if (v.type == kObjectTypeString) {
+ GET_ONE_EVENT(event_nr, v, cleanup);
+ event_set[event_nr] = true;
+ } else if (v.type == kObjectTypeArray) {
+ FOREACH_ITEM(v.data.array, event_v, {
+ if (event_v.type != kObjectTypeString) {
+ api_set_error(err,
+ kErrorTypeValidation,
+ "Every event must be a string in 'event'");
+ goto cleanup;
+ }
+
+ GET_ONE_EVENT(event_nr, event_v, cleanup);
+ event_set[event_nr] = true;
+ })
+ } else {
+ api_set_error(err,
+ kErrorTypeValidation,
+ "Not a valid 'event' value. Must be a string or an array");
+ goto cleanup;
+ }
+ }
+
+ if (opts->pattern.type != kObjectTypeNil && opts->buffer.type != kObjectTypeNil) {
+ api_set_error(err, kErrorTypeValidation,
+ "Cannot use both 'pattern' and 'buffer'");
+ goto cleanup;
+ }
+
+ int pattern_filter_count = 0;
+ if (opts->pattern.type != kObjectTypeNil) {
+ Object v = opts->pattern;
+ if (v.type == kObjectTypeString) {
+ pattern_filters[pattern_filter_count] = v.data.string.data;
+ pattern_filter_count += 1;
+ } else if (v.type == kObjectTypeArray) {
+ if (v.data.array.size > AUCMD_MAX_PATTERNS) {
+ api_set_error(err, kErrorTypeValidation,
+ "Too many patterns. Please limit yourself to %d or fewer",
+ AUCMD_MAX_PATTERNS);
+ goto cleanup;
+ }
+
+ FOREACH_ITEM(v.data.array, item, {
+ if (item.type != kObjectTypeString) {
+ api_set_error(err, kErrorTypeValidation, "Invalid value for 'pattern': must be a string");
+ goto cleanup;
+ }
+
+ pattern_filters[pattern_filter_count] = item.data.string.data;
+ pattern_filter_count += 1;
+ });
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "Not a valid 'pattern' value. Must be a string or an array");
+ goto cleanup;
+ }
+ }
+
+ if (opts->buffer.type == kObjectTypeInteger || opts->buffer.type == kObjectTypeBuffer) {
+ buf_T *buf = find_buffer_by_handle((Buffer)opts->buffer.data.integer, err);
+ if (ERROR_SET(err)) {
+ goto cleanup;
+ }
+
+ snprintf((char *)pattern_buflocal, BUFLOCAL_PAT_LEN, "<buffer=%d>", (int)buf->handle);
+ ADD(buffers, CSTR_TO_OBJ((char *)pattern_buflocal));
+ } else if (opts->buffer.type == kObjectTypeArray) {
+ if (opts->buffer.data.array.size > AUCMD_MAX_PATTERNS) {
+ api_set_error(err,
+ kErrorTypeValidation,
+ "Too many buffers. Please limit yourself to %d or fewer", AUCMD_MAX_PATTERNS);
+ goto cleanup;
+ }
+
+ FOREACH_ITEM(opts->buffer.data.array, bufnr, {
+ if (bufnr.type != kObjectTypeInteger && bufnr.type != kObjectTypeBuffer) {
+ api_set_error(err, kErrorTypeValidation, "Invalid value for 'buffer': must be an integer");
+ goto cleanup;
+ }
+
+ buf_T *buf = find_buffer_by_handle((Buffer)bufnr.data.integer, err);
+ if (ERROR_SET(err)) {
+ goto cleanup;
+ }
+
+ snprintf((char *)pattern_buflocal, BUFLOCAL_PAT_LEN, "<buffer=%d>", (int)buf->handle);
+ ADD(buffers, CSTR_TO_OBJ((char *)pattern_buflocal));
+ });
+ } else if (opts->buffer.type != kObjectTypeNil) {
+ api_set_error(err, kErrorTypeValidation,
+ "Invalid value for 'buffer': must be an integer or array of integers");
+ goto cleanup;
+ }
+
+ FOREACH_ITEM(buffers, bufnr, {
+ pattern_filters[pattern_filter_count] = bufnr.data.string.data;
+ pattern_filter_count += 1;
+ });
+
+ FOR_ALL_AUEVENTS(event) {
+ if (check_event && !event_set[event]) {
+ continue;
+ }
+
+ for (AutoPat *ap = au_get_autopat_for_event(event); ap != NULL; ap = ap->next) {
+ if (ap->cmds == NULL) {
+ continue;
+ }
+
+ // Skip autocmds from invalid groups if passed.
+ if (group != 0 && ap->group != group) {
+ continue;
+ }
+
+ // Skip 'pattern' from invalid patterns if passed.
+ if (pattern_filter_count > 0) {
+ bool passed = false;
+ for (int i = 0; i < pattern_filter_count; i++) {
+ assert(i < AUCMD_MAX_PATTERNS);
+ assert(pattern_filters[i]);
+
+ char *pat = pattern_filters[i];
+ int patlen = (int)STRLEN(pat);
+
+ if (aupat_is_buflocal(pat, patlen)) {
+ aupat_normalize_buflocal_pat(pattern_buflocal,
+ pat,
+ patlen,
+ aupat_get_buflocal_nr(pat, patlen));
+
+ pat = pattern_buflocal;
+ }
+
+ if (strequal(ap->pat, pat)) {
+ passed = true;
+ break;
+ }
+ }
+
+ if (!passed) {
+ continue;
+ }
+ }
+
+ for (AutoCmd *ac = ap->cmds; ac != NULL; ac = ac->next) {
+ if (aucmd_exec_is_deleted(ac->exec)) {
+ continue;
+ }
+
+ Dictionary autocmd_info = ARRAY_DICT_INIT;
+
+ if (ap->group != AUGROUP_DEFAULT) {
+ PUT(autocmd_info, "group", INTEGER_OBJ(ap->group));
+ PUT(autocmd_info, "group_name", CSTR_TO_OBJ(augroup_name(ap->group)));
+ }
+
+ if (ac->id > 0) {
+ PUT(autocmd_info, "id", INTEGER_OBJ(ac->id));
+ }
+
+ if (ac->desc != NULL) {
+ PUT(autocmd_info, "desc", CSTR_TO_OBJ(ac->desc));
+ }
+
+ if (ac->exec.type == CALLABLE_CB) {
+ PUT(autocmd_info, "command", STRING_OBJ(STRING_INIT));
+
+ Callback *cb = &ac->exec.callable.cb;
+ switch (cb->type) {
+ case kCallbackLua:
+ if (nlua_ref_is_function(cb->data.luaref)) {
+ PUT(autocmd_info, "callback", LUAREF_OBJ(api_new_luaref(cb->data.luaref)));
+ }
+ break;
+ case kCallbackFuncref:
+ case kCallbackPartial:
+ PUT(autocmd_info, "callback", STRING_OBJ(cstr_as_string(callback_to_string(cb))));
+ break;
+ default:
+ abort();
+ }
+ } else {
+ PUT(autocmd_info,
+ "command",
+ STRING_OBJ(cstr_as_string(xstrdup(ac->exec.callable.cmd))));
+ }
+
+ PUT(autocmd_info,
+ "pattern",
+ STRING_OBJ(cstr_to_string((char *)ap->pat)));
+
+ PUT(autocmd_info,
+ "event",
+ STRING_OBJ(cstr_to_string((char *)event_nr2name(event))));
+
+ PUT(autocmd_info, "once", BOOLEAN_OBJ(ac->once));
+
+ if (ap->buflocal_nr) {
+ PUT(autocmd_info, "buflocal", BOOLEAN_OBJ(true));
+ PUT(autocmd_info, "buffer", INTEGER_OBJ(ap->buflocal_nr));
+ } else {
+ PUT(autocmd_info, "buflocal", BOOLEAN_OBJ(false));
+ }
+
+ // TODO(sctx): It would be good to unify script_ctx to actually work with lua
+ // right now it's just super weird, and never really gives you the info that
+ // you would expect from this.
+ //
+ // I think we should be able to get the line number, filename, etc. from lua
+ // when we're executing something, and it should be easy to then save that
+ // info here.
+ //
+ // I think it's a big loss not getting line numbers of where options, autocmds,
+ // etc. are set (just getting "Sourced (lua)" or something is not that helpful.
+ //
+ // Once we do that, we can put these into the autocmd_info, but I don't think it's
+ // useful to do that at this time.
+ //
+ // PUT(autocmd_info, "sid", INTEGER_OBJ(ac->script_ctx.sc_sid));
+ // PUT(autocmd_info, "lnum", INTEGER_OBJ(ac->script_ctx.sc_lnum));
+
+ ADD(autocmd_list, DICTIONARY_OBJ(autocmd_info));
+ }
+ }
+ }
+
+cleanup:
+ api_free_array(buffers);
+ return autocmd_list;
+}
+
+/// Create an |autocommand|
+///
+/// The API allows for two (mutually exclusive) types of actions to be executed when the autocommand
+/// triggers: a callback function (Lua or Vimscript), or a command (like regular autocommands).
+///
+/// Example using callback:
+/// <pre>
+/// -- Lua function
+/// local myluafun = function() print("This buffer enters") end
+///
+/// -- Vimscript function name (as a string)
+/// local myvimfun = "g:MyVimFunction"
+///
+/// vim.api.nvim_create_autocmd({"BufEnter", "BufWinEnter"}, {
+/// pattern = {"*.c", "*.h"},
+/// callback = myluafun, -- Or myvimfun
+/// })
+/// </pre>
+///
+/// Lua functions receive a table with information about the autocmd event as an argument. To use
+/// a function which itself accepts another (optional) parameter, wrap the function
+/// in a lambda:
+///
+/// <pre>
+/// -- Lua function with an optional parameter.
+/// -- The autocmd callback would pass a table as argument but this
+/// -- function expects number|nil
+/// local myluafun = function(bufnr) bufnr = bufnr or vim.api.nvim_get_current_buf() end
+///
+/// vim.api.nvim_create_autocmd({"BufEnter", "BufWinEnter"}, {
+/// pattern = {"*.c", "*.h"},
+/// callback = function() myluafun() end,
+/// })
+/// </pre>
+///
+/// Example using command:
+/// <pre>
+/// vim.api.nvim_create_autocmd({"BufEnter", "BufWinEnter"}, {
+/// pattern = {"*.c", "*.h"},
+/// command = "echo 'Entering a C or C++ file'",
+/// })
+/// </pre>
+///
+/// Example values for pattern:
+/// <pre>
+/// pattern = "*.py"
+/// pattern = { "*.py", "*.pyi" }
+/// </pre>
+///
+/// Example values for event:
+/// <pre>
+/// "BufWritePre"
+/// {"CursorHold", "BufWritePre", "BufWritePost"}
+/// </pre>
+///
+/// @param event (string|array) The event or events to register this autocommand
+/// @param opts Dictionary of autocommand options:
+/// - group (string|integer) optional: the autocommand group name or
+/// id to match against.
+/// - pattern (string|array) optional: pattern or patterns to match
+/// against |autocmd-pattern|.
+/// - buffer (integer) optional: buffer number for buffer local autocommands
+/// |autocmd-buflocal|. Cannot be used with {pattern}.
+/// - desc (string) optional: description of the autocommand.
+/// - callback (function|string) optional: if a string, the name of a Vimscript function
+/// to call when this autocommand is triggered. Otherwise, a Lua function which is
+/// called when this autocommand is triggered. Cannot be used with {command}. Lua
+/// callbacks can return true to delete the autocommand; in addition, they accept a
+/// single table argument with the following keys:
+/// - id: (number) the autocommand id
+/// - event: (string) the name of the event that triggered the autocommand
+/// |autocmd-events|
+/// - group: (number|nil) the autocommand group id, if it exists
+/// - match: (string) the expanded value of |<amatch>|
+/// - buf: (number) the expanded value of |<abuf>|
+/// - file: (string) the expanded value of |<afile>|
+/// - data: (any) arbitrary data passed to |nvim_exec_autocmds()|
+/// - command (string) optional: Vim command to execute on event. Cannot be used with
+/// {callback}
+/// - once (boolean) optional: defaults to false. Run the autocommand
+/// only once |autocmd-once|.
+/// - nested (boolean) optional: defaults to false. Run nested
+/// autocommands |autocmd-nested|.
+///
+/// @return Integer id of the created autocommand.
+/// @see |autocommand|
+/// @see |nvim_del_autocmd()|
+Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autocmd) *opts,
+ Error *err)
+ FUNC_API_SINCE(9)
+{
+ int64_t autocmd_id = -1;
+ char *desc = NULL;
+
+ Array patterns = ARRAY_DICT_INIT;
+ Array event_array = ARRAY_DICT_INIT;
+
+ AucmdExecutable aucmd = AUCMD_EXECUTABLE_INIT;
+ Callback cb = CALLBACK_NONE;
+
+ if (!unpack_string_or_array(&event_array, &event, "event", true, err)) {
+ goto cleanup;
+ }
+
+ if (opts->callback.type != kObjectTypeNil && opts->command.type != kObjectTypeNil) {
+ api_set_error(err, kErrorTypeValidation,
+ "cannot pass both: 'callback' and 'command' for the same autocmd");
+ goto cleanup;
+ } else if (opts->callback.type != kObjectTypeNil) {
+ // TODO(tjdevries): It's possible we could accept callable tables,
+ // but we don't do that many other places, so for the moment let's
+ // not do that.
+
+ Object *callback = &opts->callback;
+ switch (callback->type) {
+ case kObjectTypeLuaRef:
+ if (callback->data.luaref == LUA_NOREF) {
+ api_set_error(err,
+ kErrorTypeValidation,
+ "must pass an actual value");
+ goto cleanup;
+ }
+
+ if (!nlua_ref_is_function(callback->data.luaref)) {
+ api_set_error(err,
+ kErrorTypeValidation,
+ "must pass a function for callback");
+ goto cleanup;
+ }
+
+ cb.type = kCallbackLua;
+ cb.data.luaref = api_new_luaref(callback->data.luaref);
+ break;
+ case kObjectTypeString:
+ cb.type = kCallbackFuncref;
+ cb.data.funcref = string_to_cstr(callback->data.string);
+ break;
+ default:
+ api_set_error(err,
+ kErrorTypeException,
+ "'callback' must be a lua function or name of vim function");
+ goto cleanup;
+ }
+
+ aucmd.type = CALLABLE_CB;
+ aucmd.callable.cb = cb;
+ } else if (opts->command.type != kObjectTypeNil) {
+ Object *command = &opts->command;
+ if (command->type == kObjectTypeString) {
+ aucmd.type = CALLABLE_EX;
+ aucmd.callable.cmd = string_to_cstr(command->data.string);
+ } else {
+ api_set_error(err,
+ kErrorTypeValidation,
+ "'command' must be a string");
+ goto cleanup;
+ }
+ } else {
+ api_set_error(err, kErrorTypeValidation, "must pass one of: 'command', 'callback'");
+ goto cleanup;
+ }
+
+ bool is_once = api_object_to_bool(opts->once, "once", false, err);
+ bool is_nested = api_object_to_bool(opts->nested, "nested", false, err);
+
+ int au_group = get_augroup_from_object(opts->group, err);
+ if (au_group == AUGROUP_ERROR) {
+ goto cleanup;
+ }
+
+ if (!get_patterns_from_pattern_or_buf(&patterns, opts->pattern, opts->buffer, err)) {
+ goto cleanup;
+ }
+
+ if (opts->desc.type != kObjectTypeNil) {
+ if (opts->desc.type == kObjectTypeString) {
+ desc = opts->desc.data.string.data;
+ } else {
+ api_set_error(err,
+ kErrorTypeValidation,
+ "'desc' must be a string");
+ goto cleanup;
+ }
+ }
+
+ if (patterns.size == 0) {
+ ADD(patterns, STRING_OBJ(STATIC_CSTR_TO_STRING("*")));
+ }
+
+ if (event_array.size == 0) {
+ api_set_error(err, kErrorTypeValidation, "'event' is a required key");
+ goto cleanup;
+ }
+
+ autocmd_id = next_autocmd_id++;
+ FOREACH_ITEM(event_array, event_str, {
+ GET_ONE_EVENT(event_nr, event_str, cleanup);
+
+ int retval;
+
+ FOREACH_ITEM(patterns, pat, {
+ // See: TODO(sctx)
+ WITH_SCRIPT_CONTEXT(channel_id, {
+ retval = autocmd_register(autocmd_id,
+ event_nr,
+ pat.data.string.data,
+ (int)pat.data.string.size,
+ au_group,
+ is_once,
+ is_nested,
+ desc,
+ aucmd);
+ });
+
+ if (retval == FAIL) {
+ api_set_error(err, kErrorTypeException, "Failed to set autocmd");
+ goto cleanup;
+ }
+ })
+ });
+
+cleanup:
+ aucmd_exec_free(&aucmd);
+ api_free_array(event_array);
+ api_free_array(patterns);
+
+ return autocmd_id;
+}
+
+/// Delete an autocommand by id.
+///
+/// NOTE: Only autocommands created via the API have an id.
+/// @param id Integer The id returned by nvim_create_autocmd
+/// @see |nvim_create_autocmd()|
+void nvim_del_autocmd(Integer id, Error *err)
+ FUNC_API_SINCE(9)
+{
+ if (id <= 0) {
+ api_set_error(err, kErrorTypeException, "Invalid autocmd id");
+ return;
+ }
+ if (!autocmd_delete_id(id)) {
+ api_set_error(err, kErrorTypeException, "Failed to delete autocmd");
+ }
+}
+
+/// Clear all autocommands that match the corresponding {opts}. To delete
+/// a particular autocmd, see |nvim_del_autocmd|.
+/// @param opts Parameters
+/// - event: (string|table)
+/// Examples:
+/// - event: "pat1"
+/// - event: { "pat1" }
+/// - event: { "pat1", "pat2", "pat3" }
+/// - pattern: (string|table)
+/// - pattern or patterns to match exactly.
+/// - For example, if you have `*.py` as that pattern for the autocmd,
+/// you must pass `*.py` exactly to clear it. `test.py` will not
+/// match the pattern.
+/// - defaults to clearing all patterns.
+/// - NOTE: Cannot be used with {buffer}
+/// - buffer: (bufnr)
+/// - clear only |autocmd-buflocal| autocommands.
+/// - NOTE: Cannot be used with {pattern}
+/// - group: (string|int) The augroup name or id.
+/// - NOTE: If not passed, will only delete autocmds *not* in any group.
+///
+void nvim_clear_autocmds(Dict(clear_autocmds) *opts, Error *err)
+ FUNC_API_SINCE(9)
+{
+ // TODO(tjdevries): Future improvements:
+ // - once: (boolean) - Only clear autocmds with once. See |autocmd-once|
+ // - nested: (boolean) - Only clear autocmds with nested. See |autocmd-nested|
+ // - group: Allow passing "*" or true or something like that to force doing all
+ // autocmds, regardless of their group.
+
+ Array patterns = ARRAY_DICT_INIT;
+ Array event_array = ARRAY_DICT_INIT;
+
+ if (!unpack_string_or_array(&event_array, &opts->event, "event", false, err)) {
+ goto cleanup;
+ }
+
+ if (opts->pattern.type != kObjectTypeNil && opts->buffer.type != kObjectTypeNil) {
+ api_set_error(err, kErrorTypeValidation,
+ "Cannot use both 'pattern' and 'buffer'");
+ goto cleanup;
+ }
+
+ int au_group = get_augroup_from_object(opts->group, err);
+ if (au_group == AUGROUP_ERROR) {
+ goto cleanup;
+ }
+
+ if (!get_patterns_from_pattern_or_buf(&patterns, opts->pattern, opts->buffer, err)) {
+ goto cleanup;
+ }
+
+ // When we create the autocmds, we want to say that they are all matched, so that's *
+ // but when we clear them, we want to say that we didn't pass a pattern, so that's NUL
+ if (patterns.size == 0) {
+ ADD(patterns, STRING_OBJ(STATIC_CSTR_TO_STRING("")));
+ }
+
+ // If we didn't pass any events, that means clear all events.
+ if (event_array.size == 0) {
+ FOR_ALL_AUEVENTS(event) {
+ FOREACH_ITEM(patterns, pat_object, {
+ char *pat = pat_object.data.string.data;
+ if (!clear_autocmd(event, (char *)pat, au_group, err)) {
+ goto cleanup;
+ }
+ });
+ }
+ } else {
+ FOREACH_ITEM(event_array, event_str, {
+ GET_ONE_EVENT(event_nr, event_str, cleanup);
+
+ FOREACH_ITEM(patterns, pat_object, {
+ char *pat = pat_object.data.string.data;
+ if (!clear_autocmd(event_nr, (char *)pat, au_group, err)) {
+ goto cleanup;
+ }
+ });
+ });
+ }
+
+cleanup:
+ api_free_array(event_array);
+ api_free_array(patterns);
+}
+
+/// Create or get an autocommand group |autocmd-groups|.
+///
+/// To get an existing group id, do:
+/// <pre>
+/// local id = vim.api.nvim_create_augroup("MyGroup", {
+/// clear = false
+/// })
+/// </pre>
+///
+/// @param name String: The name of the group
+/// @param opts Dictionary Parameters
+/// - clear (bool) optional: defaults to true. Clear existing
+/// commands if the group already exists |autocmd-groups|.
+/// @return Integer id of the created group.
+/// @see |autocmd-groups|
+Integer nvim_create_augroup(uint64_t channel_id, String name, Dict(create_augroup) *opts,
+ Error *err)
+ FUNC_API_SINCE(9)
+{
+ char *augroup_name = name.data;
+ bool clear_autocmds = api_object_to_bool(opts->clear, "clear", true, err);
+
+ int augroup = -1;
+ WITH_SCRIPT_CONTEXT(channel_id, {
+ augroup = augroup_add(augroup_name);
+ if (augroup == AUGROUP_ERROR) {
+ api_set_error(err, kErrorTypeException, "Failed to set augroup");
+ return -1;
+ }
+
+ if (clear_autocmds) {
+ FOR_ALL_AUEVENTS(event) {
+ aupat_del_for_event_and_group(event, augroup);
+ }
+ }
+ });
+
+ return augroup;
+}
+
+/// Delete an autocommand group by id.
+///
+/// To get a group id one can use |nvim_get_autocmds()|.
+///
+/// NOTE: behavior differs from |augroup-delete|. When deleting a group, autocommands contained in
+/// this group will also be deleted and cleared. This group will no longer exist.
+/// @param id Integer The id of the group.
+/// @see |nvim_del_augroup_by_name()|
+/// @see |nvim_create_augroup()|
+void nvim_del_augroup_by_id(Integer id, Error *err)
+ FUNC_API_SINCE(9)
+{
+ TRY_WRAP({
+ try_start();
+ char *name = augroup_name((int)id);
+ augroup_del(name, false);
+ try_end(err);
+ });
+}
+
+/// Delete an autocommand group by name.
+///
+/// NOTE: behavior differs from |augroup-delete|. When deleting a group, autocommands contained in
+/// this group will also be deleted and cleared. This group will no longer exist.
+/// @param name String The name of the group.
+/// @see |autocommand-groups|
+void nvim_del_augroup_by_name(String name, Error *err)
+ FUNC_API_SINCE(9)
+{
+ TRY_WRAP({
+ try_start();
+ augroup_del(name.data, false);
+ try_end(err);
+ });
+}
+
+/// Execute all autocommands for {event} that match the corresponding
+/// {opts} |autocmd-execute|.
+/// @param event (String|Array) The event or events to execute
+/// @param opts Dictionary of autocommand options:
+/// - group (string|integer) optional: the autocommand group name or
+/// id to match against. |autocmd-groups|.
+/// - pattern (string|array) optional: defaults to "*" |autocmd-pattern|. Cannot be used
+/// with {buffer}.
+/// - buffer (integer) optional: buffer number |autocmd-buflocal|. Cannot be used with
+/// {pattern}.
+/// - modeline (bool) optional: defaults to true. Process the
+/// modeline after the autocommands |<nomodeline>|.
+/// - data (any): arbitrary data to send to the autocommand callback. See
+/// |nvim_create_autocmd()| for details.
+/// @see |:doautocmd|
+void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err)
+ FUNC_API_SINCE(9)
+{
+ int au_group = AUGROUP_ALL;
+ bool modeline = true;
+
+ buf_T *buf = curbuf;
+
+ Array patterns = ARRAY_DICT_INIT;
+ Array event_array = ARRAY_DICT_INIT;
+
+ Object *data = NULL;
+
+ if (!unpack_string_or_array(&event_array, &event, "event", true, err)) {
+ goto cleanup;
+ }
+
+ switch (opts->group.type) {
+ case kObjectTypeNil:
+ break;
+ case kObjectTypeString:
+ au_group = augroup_find(opts->group.data.string.data);
+ if (au_group == AUGROUP_ERROR) {
+ api_set_error(err,
+ kErrorTypeValidation,
+ "invalid augroup: %s", opts->group.data.string.data);
+ goto cleanup;
+ }
+ break;
+ case kObjectTypeInteger:
+ au_group = (int)opts->group.data.integer;
+ char *name = augroup_name(au_group);
+ if (!augroup_exists(name)) {
+ api_set_error(err, kErrorTypeValidation, "invalid augroup: %d", au_group);
+ goto cleanup;
+ }
+ break;
+ default:
+ api_set_error(err, kErrorTypeValidation, "'group' must be a string or an integer.");
+ goto cleanup;
+ }
+
+ if (opts->buffer.type != kObjectTypeNil) {
+ Object buf_obj = opts->buffer;
+ if (buf_obj.type != kObjectTypeInteger && buf_obj.type != kObjectTypeBuffer) {
+ api_set_error(err, kErrorTypeException, "invalid buffer: %d", buf_obj.type);
+ goto cleanup;
+ }
+
+ buf = find_buffer_by_handle((Buffer)buf_obj.data.integer, err);
+
+ if (ERROR_SET(err)) {
+ goto cleanup;
+ }
+ }
+
+ if (!get_patterns_from_pattern_or_buf(&patterns, opts->pattern, opts->buffer, err)) {
+ goto cleanup;
+ }
+
+ if (patterns.size == 0) {
+ ADD(patterns, STRING_OBJ(STATIC_CSTR_TO_STRING("")));
+ }
+
+ if (opts->data.type != kObjectTypeNil) {
+ data = &opts->data;
+ }
+
+ modeline = api_object_to_bool(opts->modeline, "modeline", true, err);
+
+ bool did_aucmd = false;
+ FOREACH_ITEM(event_array, event_str, {
+ GET_ONE_EVENT(event_nr, event_str, cleanup)
+
+ FOREACH_ITEM(patterns, pat, {
+ char *fname = opts->buffer.type == kObjectTypeNil ? pat.data.string.data : NULL;
+ did_aucmd |=
+ apply_autocmds_group(event_nr, fname, NULL, true, au_group, buf, NULL, data);
+ })
+ })
+
+ if (did_aucmd && modeline) {
+ do_modelines(0);
+ }
+
+cleanup:
+ api_free_array(event_array);
+ api_free_array(patterns);
+}
+
+static bool check_autocmd_string_array(Array arr, char *k, Error *err)
+{
+ FOREACH_ITEM(arr, entry, {
+ if (entry.type != kObjectTypeString) {
+ api_set_error(err,
+ kErrorTypeValidation,
+ "All entries in '%s' must be strings",
+ k);
+ return false;
+ }
+
+ // Disallow newlines in the middle of the line.
+ const String l = entry.data.string;
+ if (memchr(l.data, NL, l.size)) {
+ api_set_error(err, kErrorTypeValidation,
+ "String cannot contain newlines");
+ return false;
+ }
+ })
+ return true;
+}
+
+static bool unpack_string_or_array(Array *array, Object *v, char *k, bool required, Error *err)
+{
+ if (v->type == kObjectTypeString) {
+ ADD(*array, copy_object(*v));
+ } else if (v->type == kObjectTypeArray) {
+ if (!check_autocmd_string_array(v->data.array, k, err)) {
+ return false;
+ }
+ *array = copy_array(v->data.array);
+ } else {
+ if (required) {
+ api_set_error(err,
+ kErrorTypeValidation,
+ "'%s' must be an array or a string.",
+ k);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// Returns AUGROUP_ERROR if there was a problem with {group}
+static int get_augroup_from_object(Object group, Error *err)
+{
+ int au_group = AUGROUP_ERROR;
+
+ switch (group.type) {
+ case kObjectTypeNil:
+ return AUGROUP_DEFAULT;
+ case kObjectTypeString:
+ au_group = augroup_find(group.data.string.data);
+ if (au_group == AUGROUP_ERROR) {
+ api_set_error(err,
+ kErrorTypeValidation,
+ "invalid augroup: %s", group.data.string.data);
+
+ return AUGROUP_ERROR;
+ }
+
+ return au_group;
+ case kObjectTypeInteger:
+ au_group = (int)group.data.integer;
+ char *name = augroup_name(au_group);
+ if (!augroup_exists(name)) {
+ api_set_error(err, kErrorTypeValidation, "invalid augroup: %d", au_group);
+ return AUGROUP_ERROR;
+ }
+
+ return au_group;
+ default:
+ api_set_error(err, kErrorTypeValidation, "'group' must be a string or an integer.");
+ return AUGROUP_ERROR;
+ }
+}
+
+static bool get_patterns_from_pattern_or_buf(Array *patterns, Object pattern, Object buffer,
+ Error *err)
+{
+ const char pattern_buflocal[BUFLOCAL_PAT_LEN];
+
+ if (pattern.type != kObjectTypeNil && buffer.type != kObjectTypeNil) {
+ api_set_error(err, kErrorTypeValidation,
+ "cannot pass both: 'pattern' and 'buffer' for the same autocmd");
+ return false;
+ } else if (pattern.type != kObjectTypeNil) {
+ Object *v = &pattern;
+
+ if (v->type == kObjectTypeString) {
+ char *pat = v->data.string.data;
+ size_t patlen = aucmd_pattern_length(pat);
+ while (patlen) {
+ ADD(*patterns, STRING_OBJ(cbuf_to_string((char *)pat, patlen)));
+
+ pat = aucmd_next_pattern(pat, patlen);
+ patlen = aucmd_pattern_length(pat);
+ }
+ } else if (v->type == kObjectTypeArray) {
+ if (!check_autocmd_string_array(*patterns, "pattern", err)) {
+ return false;
+ }
+
+ Array array = v->data.array;
+ FOREACH_ITEM(array, entry, {
+ char *pat = entry.data.string.data;
+ size_t patlen = aucmd_pattern_length(pat);
+ while (patlen) {
+ ADD(*patterns, STRING_OBJ(cbuf_to_string((char *)pat, patlen)));
+
+ pat = aucmd_next_pattern(pat, patlen);
+ patlen = aucmd_pattern_length(pat);
+ }
+ })
+ } else {
+ api_set_error(err,
+ kErrorTypeValidation,
+ "'pattern' must be a string or table");
+ return false;
+ }
+ } else if (buffer.type != kObjectTypeNil) {
+ if (buffer.type != kObjectTypeInteger && buffer.type != kObjectTypeBuffer) {
+ api_set_error(err,
+ kErrorTypeValidation,
+ "'buffer' must be an integer");
+ return false;
+ }
+
+ buf_T *buf = find_buffer_by_handle((Buffer)buffer.data.integer, err);
+ if (ERROR_SET(err)) {
+ return false;
+ }
+
+ snprintf((char *)pattern_buflocal, BUFLOCAL_PAT_LEN, "<buffer=%d>", (int)buf->handle);
+ ADD(*patterns, STRING_OBJ(cstr_to_string((char *)pattern_buflocal)));
+ }
+
+ return true;
+}
+
+static bool clear_autocmd(event_T event, char *pat, int au_group, Error *err)
+{
+ if (do_autocmd_event(event, pat, false, false, "", true, au_group) == FAIL) {
+ api_set_error(err, kErrorTypeException, "Failed to clear autocmd");
+ return false;
+ }
+
+ return true;
+}
+
+#undef GET_ONE_EVENT
diff --git a/src/nvim/api/autocmd.h b/src/nvim/api/autocmd.h
new file mode 100644
index 0000000000..f9432830d9
--- /dev/null
+++ b/src/nvim/api/autocmd.h
@@ -0,0 +1,11 @@
+#ifndef NVIM_API_AUTOCMD_H
+#define NVIM_API_AUTOCMD_H
+
+#include <stdint.h>
+
+#include "nvim/api/private/defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "api/autocmd.h.generated.h"
+#endif
+#endif // NVIM_API_AUTOCMD_H
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index 2d5403d4b8..806b649ce6 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -22,6 +22,7 @@
#include "nvim/ex_docmd.h"
#include "nvim/extmark.h"
#include "nvim/lua/executor.h"
+#include "nvim/mapping.h"
#include "nvim/mark.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
@@ -35,7 +36,6 @@
# include "api/buffer.c.generated.h"
#endif
-
/// \defgroup api-buffer
///
/// \brief For more information on buffers, see |buffers|
@@ -51,8 +51,7 @@
/// You can use |nvim_buf_is_loaded()| or |nvim_buf_line_count()| to check
/// whether a buffer is loaded.
-
-/// Gets the buffer line count
+/// Returns the number of lines in the given buffer.
///
/// @param buffer Buffer handle, or 0 for current buffer
/// @param[out] err Error details, if any
@@ -133,7 +132,7 @@ Integer nvim_buf_line_count(Buffer buffer, Error *err)
/// - buffer handle
/// - on_reload: Lua callback invoked on reload. The entire buffer
/// content should be considered changed. Args:
-/// - the string "detach"
+/// - the string "reload"
/// - buffer handle
/// - utf_sizes: include UTF-32 and UTF-16 size of the replaced
/// region, as args to `on_lines`.
@@ -247,7 +246,7 @@ void nvim__buf_redraw_range(Buffer buffer, Integer first, Integer last, Error *e
return;
}
- redraw_buf_range_later(buf, (linenr_T)first+1, (linenr_T)last);
+ redraw_buf_range_later(buf, (linenr_T)first + 1, (linenr_T)last);
}
/// Gets a line-range from the buffer.
@@ -262,7 +261,7 @@ void nvim__buf_redraw_range(Buffer buffer, Integer first, Integer last, Error *e
/// @param channel_id
/// @param buffer Buffer handle, or 0 for current buffer
/// @param start First line index
-/// @param end Last line index (exclusive)
+/// @param end Last line index, exclusive
/// @param strict_indexing Whether out-of-bounds should be an error.
/// @param[out] err Error details, if any
/// @return Array of lines, or empty array for unloaded buffer.
@@ -287,8 +286,8 @@ ArrayOf(String) nvim_buf_get_lines(uint64_t channel_id,
}
bool oob = false;
- start = normalize_index(buf, start, &oob);
- end = normalize_index(buf, end, &oob);
+ start = normalize_index(buf, start, true, &oob);
+ end = normalize_index(buf, end, true, &oob);
if (strict_indexing && oob) {
api_set_error(err, kErrorTypeValidation, "Index out of bounds");
@@ -358,7 +357,7 @@ static bool check_string_array(Array arr, bool disallow_nl, Error *err)
/// @param channel_id
/// @param buffer Buffer handle, or 0 for current buffer
/// @param start First line index
-/// @param end Last line index (exclusive)
+/// @param end Last line index, exclusive
/// @param strict_indexing Whether out-of-bounds should be an error.
/// @param replacement Array of lines to use as replacement
/// @param[out] err Error details, if any
@@ -374,15 +373,14 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ
}
bool oob = false;
- start = normalize_index(buf, start, &oob);
- end = normalize_index(buf, end, &oob);
+ start = normalize_index(buf, start, true, &oob);
+ end = normalize_index(buf, end, true, &oob);
if (strict_indexing && oob) {
api_set_error(err, kErrorTypeValidation, "Index out of bounds");
return;
}
-
if (start > end) {
api_set_error(err,
kErrorTypeValidation,
@@ -423,7 +421,7 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ
goto end;
}
- bcount_t deleted_bytes = get_region_bytecount(curbuf, start, end, 0, 0);
+ bcount_t deleted_bytes = get_region_bytecount(curbuf, (linenr_T)start, (linenr_T)end, 0, 0);
// If the size of the range is reducing (ie, new_len < old_len) we
// need to delete some old_len. We do this at the start, by
@@ -453,7 +451,7 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ
goto end;
}
- if (ml_replace((linenr_T)lnum, (char_u *)lines[i], false) == FAIL) {
+ if (ml_replace((linenr_T)lnum, lines[i], false) == FAIL) {
api_set_error(err, kErrorTypeException, "Failed to replace line");
goto end;
}
@@ -473,7 +471,7 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ
goto end;
}
- if (ml_append((linenr_T)lnum, (char_u *)lines[i], 0, false) == FAIL) {
+ if (ml_append((linenr_T)lnum, lines[i], 0, false) == FAIL) {
api_set_error(err, kErrorTypeException, "Failed to insert line");
goto end;
}
@@ -493,14 +491,14 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ
mark_adjust((linenr_T)start,
(linenr_T)(end - 1),
MAXLNUM,
- (long)extra,
+ (linenr_T)extra,
kExtmarkNOOP);
- extmark_splice(curbuf, (int)start-1, 0, (int)(end-start), 0,
+ extmark_splice(curbuf, (int)start - 1, 0, (int)(end - start), 0,
deleted_bytes, (int)new_len, 0, inserted_bytes,
kExtmarkUndo);
- changed_lines((linenr_T)start, 0, (linenr_T)end, (long)extra, true);
+ changed_lines((linenr_T)start, 0, (linenr_T)end, (linenr_T)extra, true);
fix_cursor((linenr_T)start, (linenr_T)end, (linenr_T)extra);
end:
@@ -515,24 +513,25 @@ end:
/// Sets (replaces) a range in the buffer
///
-/// This is recommended over nvim_buf_set_lines when only modifying parts of a
-/// line, as extmarks will be preserved on non-modified parts of the touched
+/// This is recommended over |nvim_buf_set_lines()| when only modifying parts of
+/// a line, as extmarks will be preserved on non-modified parts of the touched
/// lines.
///
-/// Indexing is zero-based and end-exclusive.
+/// Indexing is zero-based. Row indices are end-inclusive, and column indices
+/// are end-exclusive.
///
-/// To insert text at a given index, set `start` and `end` ranges to the same
-/// index. To delete a range, set `replacement` to an array containing
-/// an empty string, or simply an empty array.
+/// To insert text at a given `(row, column)` location, use `start_row = end_row
+/// = row` and `start_col = end_col = col`. To delete the text in a range, use
+/// `replacement = {}`.
///
-/// Prefer nvim_buf_set_lines when adding or deleting entire lines only.
+/// Prefer |nvim_buf_set_lines()| if you are only adding or deleting entire lines.
///
/// @param channel_id
/// @param buffer Buffer handle, or 0 for current buffer
/// @param start_row First line index
-/// @param start_column First column
-/// @param end_row Last line index
-/// @param end_column Last column
+/// @param start_col Starting column (byte offset) on first line
+/// @param end_row Last line index, inclusive
+/// @param end_col Ending column (byte offset) on last line, exclusive
/// @param replacement Array of lines to use as replacement
/// @param[out] err Error details, if any
void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, Integer start_col,
@@ -554,25 +553,25 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
// check range is ordered and everything!
// start_row, end_row within buffer len (except add text past the end?)
- start_row = normalize_index(buf, start_row, &oob);
- if (oob || start_row == buf->b_ml.ml_line_count + 1) {
+ start_row = normalize_index(buf, start_row, false, &oob);
+ if (oob) {
api_set_error(err, kErrorTypeValidation, "start_row out of bounds");
return;
}
- end_row = normalize_index(buf, end_row, &oob);
- if (oob || end_row == buf->b_ml.ml_line_count + 1) {
+ end_row = normalize_index(buf, end_row, false, &oob);
+ if (oob) {
api_set_error(err, kErrorTypeValidation, "end_row out of bounds");
return;
}
- char *str_at_start = (char *)ml_get_buf(buf, start_row, false);
+ char *str_at_start = (char *)ml_get_buf(buf, (linenr_T)start_row, false);
if (start_col < 0 || (size_t)start_col > strlen(str_at_start)) {
api_set_error(err, kErrorTypeValidation, "start_col out of bounds");
return;
}
- char *str_at_end = (char *)ml_get_buf(buf, end_row, false);
+ char *str_at_end = (char *)ml_get_buf(buf, (linenr_T)end_row, false);
size_t len_at_end = strlen(str_at_end);
if (end_col < 0 || (size_t)end_col > len_at_end) {
api_set_error(err, kErrorTypeValidation, "end_col out of bounds");
@@ -598,21 +597,20 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
if (start_row == end_row) {
old_byte = (bcount_t)end_col - start_col;
} else {
- const char *bufline;
old_byte += (bcount_t)strlen(str_at_start) - start_col;
for (int64_t i = 1; i < end_row - start_row; i++) {
int64_t lnum = start_row + i;
- bufline = (char *)ml_get_buf(buf, lnum, false);
- old_byte += (bcount_t)(strlen(bufline))+1;
+ const char *bufline = (char *)ml_get_buf(buf, (linenr_T)lnum, false);
+ old_byte += (bcount_t)(strlen(bufline)) + 1;
}
- old_byte += (bcount_t)end_col+1;
+ old_byte += (bcount_t)end_col + 1;
}
String first_item = replacement.items[0].data.string;
- String last_item = replacement.items[replacement.size-1].data.string;
+ String last_item = replacement.items[replacement.size - 1].data.string;
- size_t firstlen = (size_t)start_col+first_item.size;
+ size_t firstlen = (size_t)start_col + first_item.size;
size_t last_part_len = strlen(str_at_end) - (size_t)end_col;
if (replacement.size == 1) {
firstlen += last_part_len;
@@ -620,32 +618,32 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
char *first = xmallocz(firstlen);
char *last = NULL;
memcpy(first, str_at_start, (size_t)start_col);
- memcpy(first+start_col, first_item.data, first_item.size);
- memchrsub(first+start_col, NUL, NL, first_item.size);
+ memcpy(first + start_col, first_item.data, first_item.size);
+ memchrsub(first + start_col, NUL, NL, first_item.size);
if (replacement.size == 1) {
- memcpy(first+start_col+first_item.size, str_at_end+end_col, last_part_len);
+ memcpy(first + start_col + first_item.size, str_at_end + end_col, last_part_len);
} else {
- last = xmallocz(last_item.size+last_part_len);
+ last = xmallocz(last_item.size + last_part_len);
memcpy(last, last_item.data, last_item.size);
memchrsub(last, NUL, NL, last_item.size);
- memcpy(last+last_item.size, str_at_end+end_col, last_part_len);
+ memcpy(last + last_item.size, str_at_end + end_col, last_part_len);
}
char **lines = xcalloc(new_len, sizeof(char *));
lines[0] = first;
new_byte += (bcount_t)(first_item.size);
- for (size_t i = 1; i < new_len-1; i++) {
+ for (size_t i = 1; i < new_len - 1; i++) {
const String l = replacement.items[i].data.string;
// Fill lines[i] with l's contents. Convert NULs to newlines as required by
// NL-used-for-NUL.
lines[i] = xmemdupz(l.data, l.size);
memchrsub(lines[i], NUL, NL, l.size);
- new_byte += (bcount_t)(l.size)+1;
+ new_byte += (bcount_t)(l.size) + 1;
}
if (replacement.size > 1) {
- lines[replacement.size-1] = last;
- new_byte += (bcount_t)(last_item.size)+1;
+ lines[replacement.size - 1] = last;
+ new_byte += (bcount_t)(last_item.size) + 1;
}
try_start();
@@ -665,7 +663,7 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
}
ptrdiff_t extra = 0; // lines added to text, can be negative
- size_t old_len = (size_t)(end_row-start_row+1);
+ size_t old_len = (size_t)(end_row - start_row + 1);
// If the size of the range is reducing (ie, new_len < old_len) we
// need to delete some old_len. We do this at the start, by
@@ -694,7 +692,7 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
goto end;
}
- if (ml_replace((linenr_T)lnum, (char_u *)lines[i], false) == FAIL) {
+ if (ml_replace((linenr_T)lnum, lines[i], false) == FAIL) {
api_set_error(err, kErrorTypeException, "Failed to replace line");
goto end;
}
@@ -712,7 +710,7 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
goto end;
}
- if (ml_append((linenr_T)lnum, (char_u *)lines[i], 0, false) == FAIL) {
+ if (ml_append((linenr_T)lnum, lines[i], 0, false) == FAIL) {
api_set_error(err, kErrorTypeException, "Failed to insert line");
goto end;
}
@@ -728,19 +726,17 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
mark_adjust((linenr_T)start_row,
(linenr_T)end_row,
MAXLNUM,
- (long)extra,
+ (linenr_T)extra,
kExtmarkNOOP);
colnr_T col_extent = (colnr_T)(end_col
- ((end_row == start_row) ? start_col : 0));
- extmark_splice(buf, (int)start_row-1, (colnr_T)start_col,
- (int)(end_row-start_row), col_extent, old_byte,
- (int)new_len-1, (colnr_T)last_item.size, new_byte,
+ extmark_splice(buf, (int)start_row - 1, (colnr_T)start_col,
+ (int)(end_row - start_row), col_extent, old_byte,
+ (int)new_len - 1, (colnr_T)last_item.size, new_byte,
kExtmarkUndo);
-
- changed_lines((linenr_T)start_row, 0, (linenr_T)end_row + 1,
- (long)extra, true);
+ changed_lines((linenr_T)start_row, 0, (linenr_T)end_row + 1, (linenr_T)extra, true);
// adjust cursor like an extmark ( i e it was inside last_part_len)
if (curwin->w_cursor.lnum == end_row && curwin->w_cursor.col > end_col) {
@@ -757,6 +753,109 @@ end:
try_end(err);
}
+/// Gets a range from the buffer.
+///
+/// This differs from |nvim_buf_get_lines()| in that it allows retrieving only
+/// portions of a line.
+///
+/// Indexing is zero-based. Row indices are end-inclusive, and column indices
+/// are end-exclusive.
+///
+/// Prefer |nvim_buf_get_lines()| when retrieving entire lines.
+///
+/// @param channel_id
+/// @param buffer Buffer handle, or 0 for current buffer
+/// @param start_row First line index
+/// @param start_col Starting column (byte offset) on first line
+/// @param end_row Last line index, inclusive
+/// @param end_col Ending column (byte offset) on last line, exclusive
+/// @param opts Optional parameters. Currently unused.
+/// @param[out] err Error details, if any
+/// @return Array of lines, or empty array for unloaded buffer.
+ArrayOf(String) nvim_buf_get_text(uint64_t channel_id, Buffer buffer,
+ Integer start_row, Integer start_col,
+ Integer end_row, Integer end_col,
+ Dictionary opts, Error *err)
+ FUNC_API_SINCE(9)
+{
+ Array rv = ARRAY_DICT_INIT;
+
+ if (opts.size > 0) {
+ api_set_error(err, kErrorTypeValidation, "opts dict isn't empty");
+ return rv;
+ }
+
+ buf_T *buf = find_buffer_by_handle(buffer, err);
+
+ if (!buf) {
+ return rv;
+ }
+
+ // return sentinel value if the buffer isn't loaded
+ if (buf->b_ml.ml_mfp == NULL) {
+ return rv;
+ }
+
+ bool oob = false;
+ start_row = normalize_index(buf, start_row, false, &oob);
+ end_row = normalize_index(buf, end_row, false, &oob);
+
+ if (oob) {
+ api_set_error(err, kErrorTypeValidation, "Index out of bounds");
+ return rv;
+ }
+
+ // nvim_buf_get_lines doesn't care if the start row is greater than the end
+ // row (it will just return an empty array), but nvim_buf_get_text does in
+ // order to maintain symmetry with nvim_buf_set_text.
+ if (start_row > end_row) {
+ api_set_error(err, kErrorTypeValidation, "start is higher than end");
+ return rv;
+ }
+
+ bool replace_nl = (channel_id != VIML_INTERNAL_CALL);
+
+ if (start_row == end_row) {
+ String line = buf_get_text(buf, start_row, start_col, end_col, replace_nl, err);
+ if (ERROR_SET(err)) {
+ return rv;
+ }
+
+ ADD(rv, STRING_OBJ(line));
+ return rv;
+ }
+
+ rv.size = (size_t)(end_row - start_row) + 1;
+ rv.items = xcalloc(rv.size, sizeof(Object));
+
+ rv.items[0] = STRING_OBJ(buf_get_text(buf, start_row, start_col, MAXCOL - 1, replace_nl, err));
+ if (ERROR_SET(err)) {
+ goto end;
+ }
+
+ if (rv.size > 2) {
+ Array tmp = ARRAY_DICT_INIT;
+ tmp.items = &rv.items[1];
+ if (!buf_collect_lines(buf, rv.size - 2, start_row + 1, replace_nl, &tmp, err)) {
+ goto end;
+ }
+ }
+
+ rv.items[rv.size - 1] = STRING_OBJ(buf_get_text(buf, end_row, 0, end_col, replace_nl, err));
+ if (ERROR_SET(err)) {
+ goto end;
+ }
+
+end:
+ if (ERROR_SET(err)) {
+ api_free_array(rv);
+ rv.size = 0;
+ rv.items = NULL;
+ }
+
+ return rv;
+}
+
/// Returns the byte offset of a line (0-indexed). |api-indexing|
///
/// Line 1 (index=0) has offset 0. UTF-8 bytes are counted. EOL is one byte.
@@ -789,7 +888,7 @@ Integer nvim_buf_get_offset(Buffer buffer, Integer index, Error *err)
return 0;
}
- return ml_find_line_or_offset(buf, (int)index+1, NULL, true);
+ return ml_find_line_or_offset(buf, (int)index + 1, NULL, true);
}
/// Gets a buffer-scoped (b:) variable.
@@ -852,11 +951,11 @@ ArrayOf(Dictionary) nvim_buf_get_keymap(uint64_t channel_id, Buffer buffer, Stri
/// @see |nvim_set_keymap()|
///
/// @param buffer Buffer handle, or 0 for current buffer
-void nvim_buf_set_keymap(Buffer buffer, String mode, String lhs, String rhs, Dict(keymap) *opts,
- Error *err)
+void nvim_buf_set_keymap(uint64_t channel_id, Buffer buffer, String mode, String lhs, String rhs,
+ Dict(keymap) *opts, Error *err)
FUNC_API_SINCE(6)
{
- modify_keymap(buffer, false, mode, lhs, rhs, opts, err);
+ modify_keymap(channel_id, buffer, false, mode, lhs, rhs, opts, err);
}
/// Unmaps a buffer-local |mapping| for the given mode.
@@ -864,42 +963,11 @@ void nvim_buf_set_keymap(Buffer buffer, String mode, String lhs, String rhs, Dic
/// @see |nvim_del_keymap()|
///
/// @param buffer Buffer handle, or 0 for current buffer
-void nvim_buf_del_keymap(Buffer buffer, String mode, String lhs, Error *err)
+void nvim_buf_del_keymap(uint64_t channel_id, Buffer buffer, String mode, String lhs, Error *err)
FUNC_API_SINCE(6)
{
String rhs = { .data = "", .size = 0 };
- modify_keymap(buffer, true, mode, lhs, rhs, NULL, err);
-}
-
-/// Gets a map of buffer-local |user-commands|.
-///
-/// @param buffer Buffer handle, or 0 for current buffer
-/// @param opts Optional parameters. Currently not used.
-/// @param[out] err Error details, if any.
-///
-/// @returns Map of maps describing commands.
-Dictionary nvim_buf_get_commands(Buffer buffer, Dict(get_commands) *opts, Error *err)
- FUNC_API_SINCE(4)
-{
- bool global = (buffer == -1);
- bool builtin = api_object_to_bool(opts->builtin, "builtin", false, err);
- if (ERROR_SET(err)) {
- return (Dictionary)ARRAY_DICT_INIT;
- }
-
- if (global) {
- if (builtin) {
- api_set_error(err, kErrorTypeValidation, "builtin=true not implemented");
- return (Dictionary)ARRAY_DICT_INIT;
- }
- return commands_array(NULL);
- }
-
- buf_T *buf = find_buffer_by_handle(buffer, err);
- if (builtin || !buf) {
- return (Dictionary)ARRAY_DICT_INIT;
- }
- return commands_array(buf);
+ modify_keymap(channel_id, buffer, true, mode, lhs, rhs, NULL, err);
}
/// Sets a buffer-scoped (b:) variable
@@ -937,45 +1005,6 @@ void nvim_buf_del_var(Buffer buffer, String name, Error *err)
dict_set_var(buf->b_vars, name, NIL, true, false, err);
}
-
-/// Gets a buffer option value
-///
-/// @param buffer Buffer handle, or 0 for current buffer
-/// @param name Option name
-/// @param[out] err Error details, if any
-/// @return Option value
-Object nvim_buf_get_option(Buffer buffer, String name, Error *err)
- FUNC_API_SINCE(1)
-{
- buf_T *buf = find_buffer_by_handle(buffer, err);
-
- if (!buf) {
- return (Object)OBJECT_INIT;
- }
-
- return get_option_from(buf, SREQ_BUF, name, err);
-}
-
-/// Sets a buffer option value. Passing 'nil' as value deletes the option (only
-/// works if there's a global fallback)
-///
-/// @param channel_id
-/// @param buffer Buffer handle, or 0 for current buffer
-/// @param name Option name
-/// @param value Option value
-/// @param[out] err Error details, if any
-void nvim_buf_set_option(uint64_t channel_id, Buffer buffer, String name, Object value, Error *err)
- FUNC_API_SINCE(1)
-{
- buf_T *buf = find_buffer_by_handle(buffer, err);
-
- if (!buf) {
- return;
- }
-
- set_option_to(channel_id, buf, SREQ_BUF, name, value, err);
-}
-
/// Gets the full file name for the buffer
///
/// @param buffer Buffer handle, or 0 for current buffer
@@ -1013,7 +1042,7 @@ void nvim_buf_set_name(Buffer buffer, String name, Error *err)
// Using aucmd_*: autocommands will be executed by rename_buffer
aco_save_T aco;
aucmd_prepbuf(&aco, buf);
- int ren_ret = rename_buffer((char_u *)name.data);
+ int ren_ret = rename_buffer(name.data);
aucmd_restbuf(&aco);
if (try_end(err)) {
@@ -1127,17 +1156,17 @@ Boolean nvim_buf_del_mark(Buffer buffer, String name, Error *err)
return res;
}
- pos_T *pos = getmark_buf(buf, *name.data, false);
+ fmark_T *fm = mark_get(buf, curwin, NULL, kMarkAllNoResolve, *name.data);
- // pos point to NULL when there's no mark with name
- if (pos == NULL) {
+ // fm is NULL when there's no mark with the given name
+ if (fm == NULL) {
api_set_error(err, kErrorTypeValidation, "Invalid mark name: '%c'",
*name.data);
return res;
}
- // pos->lnum is 0 when the mark is not valid in the buffer, or is not set.
- if (pos->lnum != 0) {
+ // mark.lnum is 0 when the mark is not valid in the buffer, or is not set.
+ if (fm->mark.lnum != 0 && fm->fnum == buf->handle) {
// since the mark belongs to the buffer delete it.
res = set_mark(buf, name, 0, 0, err);
}
@@ -1210,31 +1239,29 @@ ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err)
return rv;
}
- pos_T *posp;
+ fmark_T *fm;
+ pos_T pos;
char mark = *name.data;
- try_start();
- bufref_T save_buf;
- switch_buffer(&save_buf, buf);
- posp = getmark(mark, false);
- restore_buffer(&save_buf);
-
- if (try_end(err)) {
- return rv;
- }
-
- if (posp == NULL) {
+ fm = mark_get(buf, curwin, NULL, kMarkAllNoResolve, mark);
+ if (fm == NULL) {
api_set_error(err, kErrorTypeValidation, "Invalid mark name");
return rv;
}
+ // (0, 0) uppercase/file mark set in another buffer.
+ if (fm->fnum != buf->handle) {
+ pos.lnum = 0;
+ pos.col = 0;
+ } else {
+ pos = fm->mark;
+ }
- ADD(rv, INTEGER_OBJ(posp->lnum));
- ADD(rv, INTEGER_OBJ(posp->col));
+ ADD(rv, INTEGER_OBJ(pos.lnum));
+ ADD(rv, INTEGER_OBJ(pos.col));
return rv;
}
-
/// call a function with buffer as temporary current buffer
///
/// This temporarily switches current buffer to "buffer".
@@ -1273,63 +1300,6 @@ Object nvim_buf_call(Buffer buffer, LuaRef fun, Error *err)
return res;
}
-/// Create a new user command |user-commands| in the given buffer.
-///
-/// @param buffer Buffer handle, or 0 for current buffer.
-/// @param[out] err Error details, if any.
-/// @see nvim_add_user_command
-void nvim_buf_add_user_command(Buffer buffer, String name, Object command,
- Dict(user_command) *opts, Error *err)
- FUNC_API_SINCE(9)
-{
- buf_T *target_buf = find_buffer_by_handle(buffer, err);
- if (ERROR_SET(err)) {
- return;
- }
-
- buf_T *save_curbuf = curbuf;
- curbuf = target_buf;
- add_user_command(name, command, opts, UC_BUFFER, err);
- curbuf = save_curbuf;
-}
-
-/// Delete a buffer-local user-defined command.
-///
-/// Only commands created with |:command-buffer| or
-/// |nvim_buf_add_user_command()| can be deleted with this function.
-///
-/// @param buffer Buffer handle, or 0 for current buffer.
-/// @param name Name of the command to delete.
-/// @param[out] err Error details, if any.
-void nvim_buf_del_user_command(Buffer buffer, String name, Error *err)
- FUNC_API_SINCE(9)
-{
- garray_T *gap;
- if (buffer == -1) {
- gap = &ucmds;
- } else {
- buf_T *buf = find_buffer_by_handle(buffer, err);
- gap = &buf->b_ucmds;
- }
-
- for (int i = 0; i < gap->ga_len; i++) {
- ucmd_T *cmd = USER_CMD_GA(gap, i);
- if (!STRCMP(name.data, cmd->uc_name)) {
- free_ucmd(cmd);
-
- gap->ga_len -= 1;
-
- if (i < gap->ga_len) {
- memmove(cmd, cmd + 1, (size_t)(gap->ga_len - i) * sizeof(ucmd_T));
- }
-
- return;
- }
- }
-
- api_set_error(err, kErrorTypeException, "No such user-defined command: %s", name.data);
-}
-
Dictionary nvim__buf_stats(Buffer buffer, Error *err)
{
Dictionary rv = ARRAY_DICT_INIT;
@@ -1386,16 +1356,17 @@ static void fix_cursor(linenr_T lo, linenr_T hi, linenr_T extra)
}
// Normalizes 0-based indexes to buffer line numbers
-static int64_t normalize_index(buf_T *buf, int64_t index, bool *oob)
+static int64_t normalize_index(buf_T *buf, int64_t index, bool end_exclusive, bool *oob)
{
- int64_t line_count = buf->b_ml.ml_line_count;
+ assert(buf->b_ml.ml_line_count > 0);
+ int64_t max_index = buf->b_ml.ml_line_count + (int)end_exclusive - 1;
// Fix if < 0
- index = index < 0 ? line_count + index +1 : index;
+ index = index < 0 ? max_index + index + 1 : index;
// Check for oob
- if (index > line_count) {
+ if (index > max_index) {
*oob = true;
- index = line_count;
+ index = max_index;
} else if (index < 0) {
*oob = true;
index = 0;
diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c
new file mode 100644
index 0000000000..4c2404a0d8
--- /dev/null
+++ b/src/nvim/api/command.c
@@ -0,0 +1,1189 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "nvim/api/command.h"
+#include "nvim/api/private/converter.h"
+#include "nvim/api/private/defs.h"
+#include "nvim/api/private/helpers.h"
+#include "nvim/autocmd.h"
+#include "nvim/ex_docmd.h"
+#include "nvim/lua/executor.h"
+#include "nvim/ops.h"
+#include "nvim/regexp.h"
+#include "nvim/window.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "api/command.c.generated.h"
+#endif
+
+/// Parse command line.
+///
+/// Doesn't check the validity of command arguments.
+///
+/// @param str Command line string to parse. Cannot contain "\n".
+/// @param opts Optional parameters. Reserved for future use.
+/// @param[out] err Error details, if any.
+/// @return Dictionary containing command information, with these keys:
+/// - cmd: (string) Command name.
+/// - range: (array) Command <range>. Can have 0-2 elements depending on how many items the
+/// range contains. Has no elements if command doesn't accept a range or if
+/// no range was specified, one element if only a single range item was
+/// specified and two elements if both range items were specified.
+/// - count: (number) Any |<count>| that was supplied to the command. -1 if command cannot
+/// take a count.
+/// - reg: (number) The optional command |<register>|, if specified. Empty string if not
+/// specified or if command cannot take a register.
+/// - bang: (boolean) Whether command contains a |<bang>| (!) modifier.
+/// - args: (array) Command arguments.
+/// - addr: (string) Value of |:command-addr|. Uses short name.
+/// - nargs: (string) Value of |:command-nargs|.
+/// - nextcmd: (string) Next command if there are multiple commands separated by a |:bar|.
+/// Empty if there isn't a next command.
+/// - magic: (dictionary) Which characters have special meaning in the command arguments.
+/// - file: (boolean) The command expands filenames. Which means characters such as "%",
+/// "#" and wildcards are expanded.
+/// - bar: (boolean) The "|" character is treated as a command separator and the double
+/// quote character (\") is treated as the start of a comment.
+/// - mods: (dictionary) |:command-modifiers|.
+/// - filter: (dictionary) |:filter|.
+/// - pattern: (string) Filter pattern. Empty string if there is no filter.
+/// - force: (boolean) Whether filter is inverted or not.
+/// - silent: (boolean) |:silent|.
+/// - emsg_silent: (boolean) |:silent!|.
+/// - unsilent: (boolean) |:unsilent|.
+/// - sandbox: (boolean) |:sandbox|.
+/// - noautocmd: (boolean) |:noautocmd|.
+/// - browse: (boolean) |:browse|.
+/// - confirm: (boolean) |:confirm|.
+/// - hide: (boolean) |:hide|.
+/// - keepalt: (boolean) |:keepalt|.
+/// - keepjumps: (boolean) |:keepjumps|.
+/// - keepmarks: (boolean) |:keepmarks|.
+/// - keeppatterns: (boolean) |:keeppatterns|.
+/// - lockmarks: (boolean) |:lockmarks|.
+/// - noswapfile: (boolean) |:noswapfile|.
+/// - tab: (integer) |:tab|.
+/// - verbose: (integer) |:verbose|. -1 when omitted.
+/// - vertical: (boolean) |:vertical|.
+/// - split: (string) Split modifier string, is an empty string when there's no split
+/// modifier. If there is a split modifier it can be one of:
+/// - "aboveleft": |:aboveleft|.
+/// - "belowright": |:belowright|.
+/// - "topleft": |:topleft|.
+/// - "botright": |:botright|.
+Dictionary nvim_parse_cmd(String str, Dictionary opts, Error *err)
+ FUNC_API_SINCE(10) FUNC_API_FAST
+{
+ Dictionary result = ARRAY_DICT_INIT;
+
+ if (opts.size > 0) {
+ api_set_error(err, kErrorTypeValidation, "opts dict isn't empty");
+ return result;
+ }
+
+ // Parse command line
+ exarg_T ea;
+ CmdParseInfo cmdinfo;
+ char *cmdline = string_to_cstr(str);
+ char *errormsg = NULL;
+
+ if (!parse_cmdline(cmdline, &ea, &cmdinfo, &errormsg)) {
+ if (errormsg != NULL) {
+ api_set_error(err, kErrorTypeException, "Error while parsing command line: %s", errormsg);
+ } else {
+ api_set_error(err, kErrorTypeException, "Error while parsing command line");
+ }
+ goto end;
+ }
+
+ // Parse arguments
+ Array args = ARRAY_DICT_INIT;
+ size_t length = STRLEN(ea.arg);
+
+ // For nargs = 1 or '?', pass the entire argument list as a single argument,
+ // otherwise split arguments by whitespace.
+ if (ea.argt & EX_NOSPC) {
+ if (*ea.arg != NUL) {
+ ADD(args, STRING_OBJ(cstrn_to_string((char *)ea.arg, length)));
+ }
+ } else {
+ size_t end = 0;
+ size_t len = 0;
+ char *buf = xcalloc(length, sizeof(char));
+ bool done = false;
+
+ while (!done) {
+ done = uc_split_args_iter(ea.arg, length, &end, buf, &len);
+ if (len > 0) {
+ ADD(args, STRING_OBJ(cstrn_to_string(buf, len)));
+ }
+ }
+
+ xfree(buf);
+ }
+
+ ucmd_T *cmd = NULL;
+ if (ea.cmdidx == CMD_USER) {
+ cmd = USER_CMD(ea.useridx);
+ } else if (ea.cmdidx == CMD_USER_BUF) {
+ cmd = USER_CMD_GA(&curbuf->b_ucmds, ea.useridx);
+ }
+
+ if (cmd != NULL) {
+ PUT(result, "cmd", CSTR_TO_OBJ((char *)cmd->uc_name));
+ } else {
+ PUT(result, "cmd", CSTR_TO_OBJ((char *)get_command_name(NULL, ea.cmdidx)));
+ }
+
+ if ((ea.argt & EX_RANGE) && ea.addr_count > 0) {
+ Array range = ARRAY_DICT_INIT;
+ if (ea.addr_count > 1) {
+ ADD(range, INTEGER_OBJ(ea.line1));
+ }
+ ADD(range, INTEGER_OBJ(ea.line2));
+ PUT(result, "range", ARRAY_OBJ(range));
+ } else {
+ PUT(result, "range", ARRAY_OBJ(ARRAY_DICT_INIT));
+ }
+
+ if (ea.argt & EX_COUNT) {
+ if (ea.addr_count > 0) {
+ PUT(result, "count", INTEGER_OBJ(ea.line2));
+ } else if (cmd != NULL) {
+ PUT(result, "count", INTEGER_OBJ(cmd->uc_def));
+ } else {
+ PUT(result, "count", INTEGER_OBJ(0));
+ }
+ } else {
+ PUT(result, "count", INTEGER_OBJ(-1));
+ }
+
+ char reg[2];
+ reg[0] = (char)ea.regname;
+ reg[1] = '\0';
+ PUT(result, "reg", CSTR_TO_OBJ(reg));
+
+ PUT(result, "bang", BOOLEAN_OBJ(ea.forceit));
+ PUT(result, "args", ARRAY_OBJ(args));
+
+ char nargs[2];
+ if (ea.argt & EX_EXTRA) {
+ if (ea.argt & EX_NOSPC) {
+ if (ea.argt & EX_NEEDARG) {
+ nargs[0] = '1';
+ } else {
+ nargs[0] = '?';
+ }
+ } else if (ea.argt & EX_NEEDARG) {
+ nargs[0] = '+';
+ } else {
+ nargs[0] = '*';
+ }
+ } else {
+ nargs[0] = '0';
+ }
+ nargs[1] = '\0';
+ PUT(result, "nargs", CSTR_TO_OBJ(nargs));
+
+ const char *addr;
+ switch (ea.addr_type) {
+ case ADDR_LINES:
+ addr = "line";
+ break;
+ case ADDR_ARGUMENTS:
+ addr = "arg";
+ break;
+ case ADDR_BUFFERS:
+ addr = "buf";
+ break;
+ case ADDR_LOADED_BUFFERS:
+ addr = "load";
+ break;
+ case ADDR_WINDOWS:
+ addr = "win";
+ break;
+ case ADDR_TABS:
+ addr = "tab";
+ break;
+ case ADDR_QUICKFIX:
+ addr = "qf";
+ break;
+ case ADDR_NONE:
+ addr = "none";
+ break;
+ default:
+ addr = "?";
+ break;
+ }
+ PUT(result, "addr", CSTR_TO_OBJ(addr));
+ PUT(result, "nextcmd", CSTR_TO_OBJ((char *)ea.nextcmd));
+
+ Dictionary mods = ARRAY_DICT_INIT;
+
+ Dictionary filter = ARRAY_DICT_INIT;
+ PUT(filter, "pattern", cmdinfo.cmdmod.cmod_filter_pat
+ ? CSTR_TO_OBJ(cmdinfo.cmdmod.cmod_filter_pat)
+ : STRING_OBJ(STATIC_CSTR_TO_STRING("")));
+ PUT(filter, "force", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_filter_force));
+ PUT(mods, "filter", DICTIONARY_OBJ(filter));
+
+ PUT(mods, "silent", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_SILENT));
+ PUT(mods, "emsg_silent", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_ERRSILENT));
+ PUT(mods, "unsilent", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_UNSILENT));
+ PUT(mods, "sandbox", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_SANDBOX));
+ PUT(mods, "noautocmd", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_NOAUTOCMD));
+ PUT(mods, "tab", INTEGER_OBJ(cmdinfo.cmdmod.cmod_tab));
+ PUT(mods, "verbose", INTEGER_OBJ(cmdinfo.cmdmod.cmod_verbose - 1));
+ PUT(mods, "browse", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_BROWSE));
+ PUT(mods, "confirm", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_CONFIRM));
+ PUT(mods, "hide", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_HIDE));
+ PUT(mods, "keepalt", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_KEEPALT));
+ PUT(mods, "keepjumps", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_KEEPJUMPS));
+ PUT(mods, "keepmarks", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_KEEPMARKS));
+ PUT(mods, "keeppatterns", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_KEEPPATTERNS));
+ PUT(mods, "lockmarks", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_LOCKMARKS));
+ PUT(mods, "noswapfile", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_NOSWAPFILE));
+ PUT(mods, "vertical", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_split & WSP_VERT));
+
+ const char *split;
+ if (cmdinfo.cmdmod.cmod_split & WSP_BOT) {
+ split = "botright";
+ } else if (cmdinfo.cmdmod.cmod_split & WSP_TOP) {
+ split = "topleft";
+ } else if (cmdinfo.cmdmod.cmod_split & WSP_BELOW) {
+ split = "belowright";
+ } else if (cmdinfo.cmdmod.cmod_split & WSP_ABOVE) {
+ split = "aboveleft";
+ } else {
+ split = "";
+ }
+ PUT(mods, "split", CSTR_TO_OBJ(split));
+
+ PUT(result, "mods", DICTIONARY_OBJ(mods));
+
+ Dictionary magic = ARRAY_DICT_INIT;
+ PUT(magic, "file", BOOLEAN_OBJ(cmdinfo.magic.file));
+ PUT(magic, "bar", BOOLEAN_OBJ(cmdinfo.magic.bar));
+ PUT(result, "magic", DICTIONARY_OBJ(magic));
+
+ undo_cmdmod(&cmdinfo.cmdmod);
+end:
+ xfree(cmdline);
+ return result;
+}
+
+/// Executes an Ex command.
+///
+/// Unlike |nvim_command()| this command takes a structured Dictionary instead of a String. This
+/// allows for easier construction and manipulation of an Ex command. This also allows for things
+/// such as having spaces inside a command argument, expanding filenames in a command that otherwise
+/// doesn't expand filenames, etc.
+///
+/// On execution error: fails with VimL error, updates v:errmsg.
+///
+/// @see |nvim_exec()|
+/// @see |nvim_command()|
+///
+/// @param cmd Command to execute. Must be a Dictionary that can contain the same values as
+/// the return value of |nvim_parse_cmd()| except "addr", "nargs" and "nextcmd"
+/// which are ignored if provided. All values except for "cmd" are optional.
+/// @param opts Optional parameters.
+/// - output: (boolean, default false) Whether to return command output.
+/// @param[out] err Error details, if any.
+/// @return Command output (non-error, non-shell |:!|) if `output` is true, else empty string.
+String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error *err)
+ FUNC_API_SINCE(10)
+{
+ exarg_T ea;
+ memset(&ea, 0, sizeof(ea));
+
+ CmdParseInfo cmdinfo;
+ memset(&cmdinfo, 0, sizeof(cmdinfo));
+
+ char *cmdline = NULL;
+ char *cmdname = NULL;
+ char **args = NULL;
+ size_t argc = 0;
+
+ String retv = (String)STRING_INIT;
+
+#define OBJ_TO_BOOL(var, value, default, varname) \
+ do { \
+ (var) = api_object_to_bool(value, varname, default, err); \
+ if (ERROR_SET(err)) { \
+ goto end; \
+ } \
+ } while (0)
+
+#define OBJ_TO_CMOD_FLAG(flag, value, default, varname) \
+ do { \
+ if (api_object_to_bool(value, varname, default, err)) { \
+ cmdinfo.cmdmod.cmod_flags |= (flag); \
+ } \
+ if (ERROR_SET(err)) { \
+ goto end; \
+ } \
+ } while (0)
+
+#define VALIDATION_ERROR(...) \
+ do { \
+ api_set_error(err, kErrorTypeValidation, __VA_ARGS__); \
+ goto end; \
+ } while (0)
+
+ bool output;
+ OBJ_TO_BOOL(output, opts->output, false, "'output'");
+
+ // First, parse the command name and check if it exists and is valid.
+ if (!HAS_KEY(cmd->cmd) || cmd->cmd.type != kObjectTypeString
+ || cmd->cmd.data.string.data[0] == NUL) {
+ VALIDATION_ERROR("'cmd' must be a non-empty String");
+ }
+
+ cmdname = string_to_cstr(cmd->cmd.data.string);
+ ea.cmd = cmdname;
+
+ char *p = find_ex_command(&ea, NULL);
+
+ // If this looks like an undefined user command and there are CmdUndefined
+ // autocommands defined, trigger the matching autocommands.
+ if (p != NULL && ea.cmdidx == CMD_SIZE && ASCII_ISUPPER(*ea.cmd)
+ && has_event(EVENT_CMDUNDEFINED)) {
+ p = xstrdup(cmdname);
+ int ret = apply_autocmds(EVENT_CMDUNDEFINED, p, p, true, NULL);
+ xfree(p);
+ // If the autocommands did something and didn't cause an error, try
+ // finding the command again.
+ p = (ret && !aborting()) ? find_ex_command(&ea, NULL) : ea.cmd;
+ }
+
+ if (p == NULL || ea.cmdidx == CMD_SIZE) {
+ VALIDATION_ERROR("Command not found: %s", cmdname);
+ }
+ if (is_cmd_ni(ea.cmdidx)) {
+ VALIDATION_ERROR("Command not implemented: %s", cmdname);
+ }
+
+ // Get the command flags so that we can know what type of arguments the command uses.
+ // Not required for a user command since `find_ex_command` already deals with it in that case.
+ if (!IS_USER_CMDIDX(ea.cmdidx)) {
+ ea.argt = get_cmd_argt(ea.cmdidx);
+ }
+
+ // Parse command arguments since it's needed to get the command address type.
+ if (HAS_KEY(cmd->args)) {
+ if (cmd->args.type != kObjectTypeArray) {
+ VALIDATION_ERROR("'args' must be an Array");
+ }
+ // Check if every argument is valid
+ for (size_t i = 0; i < cmd->args.data.array.size; i++) {
+ Object elem = cmd->args.data.array.items[i];
+ if (elem.type != kObjectTypeString) {
+ VALIDATION_ERROR("Command argument must be a String");
+ } else if (string_iswhite(elem.data.string)) {
+ VALIDATION_ERROR("Command argument must have non-whitespace characters");
+ }
+ }
+
+ argc = cmd->args.data.array.size;
+ bool argc_valid;
+
+ // Check if correct number of arguments is used.
+ switch (ea.argt & (EX_EXTRA | EX_NOSPC | EX_NEEDARG)) {
+ case EX_EXTRA | EX_NOSPC | EX_NEEDARG:
+ argc_valid = argc == 1;
+ break;
+ case EX_EXTRA | EX_NOSPC:
+ argc_valid = argc <= 1;
+ break;
+ case EX_EXTRA | EX_NEEDARG:
+ argc_valid = argc >= 1;
+ break;
+ case EX_EXTRA:
+ argc_valid = true;
+ break;
+ default:
+ argc_valid = argc == 0;
+ break;
+ }
+
+ if (!argc_valid) {
+ argc = 0; // Ensure that args array isn't erroneously freed at the end.
+ VALIDATION_ERROR("Incorrect number of arguments supplied");
+ }
+
+ if (argc != 0) {
+ args = xcalloc(argc, sizeof(char *));
+
+ for (size_t i = 0; i < argc; i++) {
+ args[i] = string_to_cstr(cmd->args.data.array.items[i].data.string);
+ }
+ }
+ }
+
+ // Simply pass the first argument (if it exists) as the arg pointer to `set_cmd_addr_type()`
+ // since it only ever checks the first argument.
+ set_cmd_addr_type(&ea, argc > 0 ? args[0] : NULL);
+
+ if (HAS_KEY(cmd->range)) {
+ if (!(ea.argt & EX_RANGE)) {
+ VALIDATION_ERROR("Command cannot accept a range");
+ } else if (cmd->range.type != kObjectTypeArray) {
+ VALIDATION_ERROR("'range' must be an Array");
+ } else if (cmd->range.data.array.size > 2) {
+ VALIDATION_ERROR("'range' cannot contain more than two elements");
+ }
+
+ Array range = cmd->range.data.array;
+ ea.addr_count = (int)range.size;
+
+ for (size_t i = 0; i < range.size; i++) {
+ Object elem = range.items[i];
+ if (elem.type != kObjectTypeInteger || elem.data.integer < 0) {
+ VALIDATION_ERROR("'range' element must be a non-negative Integer");
+ }
+ }
+
+ if (range.size > 0) {
+ ea.line1 = (linenr_T)range.items[0].data.integer;
+ ea.line2 = (linenr_T)range.items[range.size - 1].data.integer;
+ }
+
+ if (invalid_range(&ea) != NULL) {
+ VALIDATION_ERROR("Invalid range provided");
+ }
+ }
+ if (ea.addr_count == 0) {
+ if (ea.argt & EX_DFLALL) {
+ set_cmd_dflall_range(&ea); // Default range for range=%
+ } else {
+ ea.line1 = ea.line2 = get_cmd_default_range(&ea); // Default range.
+
+ if (ea.addr_type == ADDR_OTHER) {
+ // Default is 1, not cursor.
+ ea.line2 = 1;
+ }
+ }
+ }
+
+ if (HAS_KEY(cmd->count)) {
+ if (!(ea.argt & EX_COUNT)) {
+ VALIDATION_ERROR("Command cannot accept a count");
+ } else if (cmd->count.type != kObjectTypeInteger || cmd->count.data.integer < 0) {
+ VALIDATION_ERROR("'count' must be a non-negative Integer");
+ }
+ set_cmd_count(&ea, cmd->count.data.integer, true);
+ }
+
+ if (HAS_KEY(cmd->reg)) {
+ if (!(ea.argt & EX_REGSTR)) {
+ VALIDATION_ERROR("Command cannot accept a register");
+ } else if (cmd->reg.type != kObjectTypeString || cmd->reg.data.string.size != 1) {
+ VALIDATION_ERROR("'reg' must be a single character");
+ }
+ char regname = cmd->reg.data.string.data[0];
+ if (regname == '=') {
+ VALIDATION_ERROR("Cannot use register \"=");
+ } else if (!valid_yank_reg(regname, ea.cmdidx != CMD_put && !IS_USER_CMDIDX(ea.cmdidx))) {
+ VALIDATION_ERROR("Invalid register: \"%c", regname);
+ }
+ ea.regname = (uint8_t)regname;
+ }
+
+ OBJ_TO_BOOL(ea.forceit, cmd->bang, false, "'bang'");
+ if (ea.forceit && !(ea.argt & EX_BANG)) {
+ VALIDATION_ERROR("Command cannot accept a bang");
+ }
+
+ if (HAS_KEY(cmd->magic)) {
+ if (cmd->magic.type != kObjectTypeDictionary) {
+ VALIDATION_ERROR("'magic' must be a Dictionary");
+ }
+
+ Dict(cmd_magic) magic = { 0 };
+ if (!api_dict_to_keydict(&magic, KeyDict_cmd_magic_get_field,
+ cmd->magic.data.dictionary, err)) {
+ goto end;
+ }
+
+ OBJ_TO_BOOL(cmdinfo.magic.file, magic.file, ea.argt & EX_XFILE, "'magic.file'");
+ OBJ_TO_BOOL(cmdinfo.magic.bar, magic.bar, ea.argt & EX_TRLBAR, "'magic.bar'");
+ } else {
+ cmdinfo.magic.file = ea.argt & EX_XFILE;
+ cmdinfo.magic.bar = ea.argt & EX_TRLBAR;
+ }
+
+ if (HAS_KEY(cmd->mods)) {
+ if (cmd->mods.type != kObjectTypeDictionary) {
+ VALIDATION_ERROR("'mods' must be a Dictionary");
+ }
+
+ Dict(cmd_mods) mods = { 0 };
+ if (!api_dict_to_keydict(&mods, KeyDict_cmd_mods_get_field, cmd->mods.data.dictionary, err)) {
+ goto end;
+ }
+
+ if (HAS_KEY(mods.filter)) {
+ if (mods.filter.type != kObjectTypeDictionary) {
+ VALIDATION_ERROR("'mods.filter' must be a Dictionary");
+ }
+
+ Dict(cmd_mods_filter) filter = { 0 };
+
+ if (!api_dict_to_keydict(&filter, KeyDict_cmd_mods_filter_get_field,
+ mods.filter.data.dictionary, err)) {
+ goto end;
+ }
+
+ if (HAS_KEY(filter.pattern)) {
+ if (filter.pattern.type != kObjectTypeString) {
+ VALIDATION_ERROR("'mods.filter.pattern' must be a String");
+ }
+
+ OBJ_TO_BOOL(cmdinfo.cmdmod.cmod_filter_force, filter.force, false, "'mods.filter.force'");
+
+ // "filter! // is not no-op, so add a filter if either the pattern is non-empty or if filter
+ // is inverted.
+ if (*filter.pattern.data.string.data != NUL || cmdinfo.cmdmod.cmod_filter_force) {
+ cmdinfo.cmdmod.cmod_filter_pat = string_to_cstr(filter.pattern.data.string);
+ cmdinfo.cmdmod.cmod_filter_regmatch.regprog = vim_regcomp(cmdinfo.cmdmod.cmod_filter_pat,
+ RE_MAGIC);
+ }
+ }
+ }
+
+ if (HAS_KEY(mods.tab)) {
+ if (mods.tab.type != kObjectTypeInteger || mods.tab.data.integer < 0) {
+ VALIDATION_ERROR("'mods.tab' must be a non-negative Integer");
+ }
+ cmdinfo.cmdmod.cmod_tab = (int)mods.tab.data.integer + 1;
+ }
+
+ if (HAS_KEY(mods.verbose)) {
+ if (mods.verbose.type != kObjectTypeInteger) {
+ VALIDATION_ERROR("'mods.verbose' must be a Integer");
+ } else if ((int)mods.verbose.data.integer >= 0) {
+ // Silently ignore negative integers to allow mods.verbose to be set to -1.
+ cmdinfo.cmdmod.cmod_verbose = (int)mods.verbose.data.integer + 1;
+ }
+ }
+
+ bool vertical;
+ OBJ_TO_BOOL(vertical, mods.vertical, false, "'mods.vertical'");
+ cmdinfo.cmdmod.cmod_split |= (vertical ? WSP_VERT : 0);
+
+ if (HAS_KEY(mods.split)) {
+ if (mods.split.type != kObjectTypeString) {
+ VALIDATION_ERROR("'mods.split' must be a String");
+ }
+
+ if (*mods.split.data.string.data == NUL) {
+ // Empty string, do nothing.
+ } else if (STRCMP(mods.split.data.string.data, "aboveleft") == 0
+ || STRCMP(mods.split.data.string.data, "leftabove") == 0) {
+ cmdinfo.cmdmod.cmod_split |= WSP_ABOVE;
+ } else if (STRCMP(mods.split.data.string.data, "belowright") == 0
+ || STRCMP(mods.split.data.string.data, "rightbelow") == 0) {
+ cmdinfo.cmdmod.cmod_split |= WSP_BELOW;
+ } else if (STRCMP(mods.split.data.string.data, "topleft") == 0) {
+ cmdinfo.cmdmod.cmod_split |= WSP_TOP;
+ } else if (STRCMP(mods.split.data.string.data, "botright") == 0) {
+ cmdinfo.cmdmod.cmod_split |= WSP_BOT;
+ } else {
+ VALIDATION_ERROR("Invalid value for 'mods.split'");
+ }
+ }
+
+ OBJ_TO_CMOD_FLAG(CMOD_SILENT, mods.silent, false, "'mods.silent'");
+ OBJ_TO_CMOD_FLAG(CMOD_ERRSILENT, mods.emsg_silent, false, "'mods.emsg_silent'");
+ OBJ_TO_CMOD_FLAG(CMOD_UNSILENT, mods.silent, false, "'mods.unsilent'");
+ OBJ_TO_CMOD_FLAG(CMOD_SANDBOX, mods.sandbox, false, "'mods.sandbox'");
+ OBJ_TO_CMOD_FLAG(CMOD_NOAUTOCMD, mods.noautocmd, false, "'mods.noautocmd'");
+ OBJ_TO_CMOD_FLAG(CMOD_BROWSE, mods.browse, false, "'mods.browse'");
+ OBJ_TO_CMOD_FLAG(CMOD_CONFIRM, mods.confirm, false, "'mods.confirm'");
+ OBJ_TO_CMOD_FLAG(CMOD_HIDE, mods.hide, false, "'mods.hide'");
+ OBJ_TO_CMOD_FLAG(CMOD_KEEPALT, mods.keepalt, false, "'mods.keepalt'");
+ OBJ_TO_CMOD_FLAG(CMOD_KEEPJUMPS, mods.keepjumps, false, "'mods.keepjumps'");
+ OBJ_TO_CMOD_FLAG(CMOD_KEEPMARKS, mods.keepmarks, false, "'mods.keepmarks'");
+ OBJ_TO_CMOD_FLAG(CMOD_KEEPPATTERNS, mods.keeppatterns, false, "'mods.keeppatterns'");
+ OBJ_TO_CMOD_FLAG(CMOD_LOCKMARKS, mods.lockmarks, false, "'mods.lockmarks'");
+ OBJ_TO_CMOD_FLAG(CMOD_NOSWAPFILE, mods.noswapfile, false, "'mods.noswapfile'");
+
+ if ((cmdinfo.cmdmod.cmod_flags & CMOD_SANDBOX) && !(ea.argt & EX_SBOXOK)) {
+ VALIDATION_ERROR("Command cannot be run in sandbox");
+ }
+ }
+
+ // Finally, build the command line string that will be stored inside ea.cmdlinep.
+ // This also sets the values of ea.cmd, ea.arg, ea.args and ea.arglens.
+ build_cmdline_str(&cmdline, &ea, &cmdinfo, args, argc);
+ ea.cmdlinep = &cmdline;
+
+ garray_T capture_local;
+ const int save_msg_silent = msg_silent;
+ garray_T * const save_capture_ga = capture_ga;
+
+ if (output) {
+ ga_init(&capture_local, 1, 80);
+ capture_ga = &capture_local;
+ }
+
+ TRY_WRAP({
+ try_start();
+ if (output) {
+ msg_silent++;
+ }
+
+ WITH_SCRIPT_CONTEXT(channel_id, {
+ execute_cmd(&ea, &cmdinfo, false);
+ });
+
+ if (output) {
+ capture_ga = save_capture_ga;
+ msg_silent = save_msg_silent;
+ }
+
+ try_end(err);
+ });
+
+ if (ERROR_SET(err)) {
+ goto clear_ga;
+ }
+
+ if (output && capture_local.ga_len > 1) {
+ retv = (String){
+ .data = capture_local.ga_data,
+ .size = (size_t)capture_local.ga_len,
+ };
+ // redir usually (except :echon) prepends a newline.
+ if (retv.data[0] == '\n') {
+ memmove(retv.data, retv.data + 1, retv.size - 1);
+ retv.data[retv.size - 1] = '\0';
+ retv.size = retv.size - 1;
+ }
+ goto end;
+ }
+clear_ga:
+ if (output) {
+ ga_clear(&capture_local);
+ }
+end:
+ xfree(cmdline);
+ xfree(cmdname);
+ xfree(ea.args);
+ xfree(ea.arglens);
+ for (size_t i = 0; i < argc; i++) {
+ xfree(args[i]);
+ }
+ xfree(args);
+
+ return retv;
+
+#undef OBJ_TO_BOOL
+#undef OBJ_TO_CMOD_FLAG
+#undef VALIDATION_ERROR
+}
+
+/// Check if a string contains only whitespace characters.
+static bool string_iswhite(String str)
+{
+ for (size_t i = 0; i < str.size; i++) {
+ if (!ascii_iswhite(str.data[i])) {
+ // Found a non-whitespace character
+ return false;
+ } else if (str.data[i] == NUL) {
+ // Terminate at first occurrence of a NUL character
+ break;
+ }
+ }
+ return true;
+}
+
+/// Build cmdline string for command, used by `nvim_cmd()`.
+///
+/// @return OK or FAIL.
+static void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdinfo, char **args,
+ size_t argc)
+{
+ StringBuilder cmdline = KV_INITIAL_VALUE;
+
+ // Add command modifiers
+ if (cmdinfo->cmdmod.cmod_tab != 0) {
+ kv_printf(cmdline, "%dtab ", cmdinfo->cmdmod.cmod_tab - 1);
+ }
+ if (cmdinfo->cmdmod.cmod_verbose > 0) {
+ kv_printf(cmdline, "%dverbose ", cmdinfo->cmdmod.cmod_verbose - 1);
+ }
+
+ if (cmdinfo->cmdmod.cmod_flags & CMOD_ERRSILENT) {
+ kv_concat(cmdline, "silent! ");
+ } else if (cmdinfo->cmdmod.cmod_flags & CMOD_SILENT) {
+ kv_concat(cmdline, "silent ");
+ }
+
+ if (cmdinfo->cmdmod.cmod_flags & CMOD_UNSILENT) {
+ kv_concat(cmdline, "unsilent ");
+ }
+
+ switch (cmdinfo->cmdmod.cmod_split & (WSP_ABOVE | WSP_BELOW | WSP_TOP | WSP_BOT)) {
+ case WSP_ABOVE:
+ kv_concat(cmdline, "aboveleft ");
+ break;
+ case WSP_BELOW:
+ kv_concat(cmdline, "belowright ");
+ break;
+ case WSP_TOP:
+ kv_concat(cmdline, "topleft ");
+ break;
+ case WSP_BOT:
+ kv_concat(cmdline, "botright ");
+ break;
+ default:
+ break;
+ }
+
+#define CMDLINE_APPEND_IF(cond, str) \
+ do { \
+ if (cond) { \
+ kv_concat(cmdline, str); \
+ } \
+ } while (0)
+
+ CMDLINE_APPEND_IF(cmdinfo->cmdmod.cmod_split & WSP_VERT, "vertical ");
+ CMDLINE_APPEND_IF(cmdinfo->cmdmod.cmod_flags & CMOD_SANDBOX, "sandbox ");
+ CMDLINE_APPEND_IF(cmdinfo->cmdmod.cmod_flags & CMOD_NOAUTOCMD, "noautocmd ");
+ CMDLINE_APPEND_IF(cmdinfo->cmdmod.cmod_flags & CMOD_BROWSE, "browse ");
+ CMDLINE_APPEND_IF(cmdinfo->cmdmod.cmod_flags & CMOD_CONFIRM, "confirm ");
+ CMDLINE_APPEND_IF(cmdinfo->cmdmod.cmod_flags & CMOD_HIDE, "hide ");
+ CMDLINE_APPEND_IF(cmdinfo->cmdmod.cmod_flags & CMOD_KEEPALT, "keepalt ");
+ CMDLINE_APPEND_IF(cmdinfo->cmdmod.cmod_flags & CMOD_KEEPJUMPS, "keepjumps ");
+ CMDLINE_APPEND_IF(cmdinfo->cmdmod.cmod_flags & CMOD_KEEPMARKS, "keepmarks ");
+ CMDLINE_APPEND_IF(cmdinfo->cmdmod.cmod_flags & CMOD_KEEPPATTERNS, "keeppatterns ");
+ CMDLINE_APPEND_IF(cmdinfo->cmdmod.cmod_flags & CMOD_LOCKMARKS, "lockmarks ");
+ CMDLINE_APPEND_IF(cmdinfo->cmdmod.cmod_flags & CMOD_NOSWAPFILE, "noswapfile ");
+#undef CMDLINE_APPEND_IF
+
+ // Command range / count.
+ if (eap->argt & EX_RANGE) {
+ if (eap->addr_count == 1) {
+ kv_printf(cmdline, "%" PRIdLINENR, eap->line2);
+ } else if (eap->addr_count > 1) {
+ kv_printf(cmdline, "%" PRIdLINENR ",%" PRIdLINENR, eap->line1, eap->line2);
+ eap->addr_count = 2; // Make sure address count is not greater than 2
+ }
+ }
+
+ // Keep the index of the position where command name starts, so eap->cmd can point to it.
+ size_t cmdname_idx = cmdline.size;
+ kv_printf(cmdline, "%s", eap->cmd);
+
+ // Command bang.
+ if (eap->argt & EX_BANG && eap->forceit) {
+ kv_printf(cmdline, "!");
+ }
+
+ // Command register.
+ if (eap->argt & EX_REGSTR && eap->regname) {
+ kv_printf(cmdline, " %c", eap->regname);
+ }
+
+ // Iterate through each argument and store the starting index and length of each argument
+ size_t *argidx = xcalloc(argc, sizeof(size_t));
+ eap->argc = argc;
+ eap->arglens = xcalloc(argc, sizeof(size_t));
+ for (size_t i = 0; i < argc; i++) {
+ argidx[i] = cmdline.size + 1; // add 1 to account for the space.
+ eap->arglens[i] = STRLEN(args[i]);
+ kv_printf(cmdline, " %s", args[i]);
+ }
+
+ // Now that all the arguments are appended, use the command index and argument indices to set the
+ // values of eap->cmd, eap->arg and eap->args.
+ eap->cmd = cmdline.items + cmdname_idx;
+ eap->args = xcalloc(argc, sizeof(char *));
+ for (size_t i = 0; i < argc; i++) {
+ eap->args[i] = cmdline.items + argidx[i];
+ }
+ // If there isn't an argument, make eap->arg point to end of cmdline.
+ eap->arg = argc > 0 ? eap->args[0] : cmdline.items + cmdline.size;
+
+ // Finally, make cmdlinep point to the cmdline string.
+ *cmdlinep = cmdline.items;
+ xfree(argidx);
+
+ // Replace, :make and :grep with 'makeprg' and 'grepprg'.
+ char *p = replace_makeprg(eap, eap->arg, cmdlinep);
+ if (p != eap->arg) {
+ // If replace_makeprg modified the cmdline string, correct the argument pointers.
+ assert(argc == 1);
+ eap->arg = p;
+ eap->args[0] = p;
+ }
+}
+
+/// Create a new user command |user-commands|
+///
+/// {name} is the name of the new command. The name must begin with an uppercase letter.
+///
+/// {command} is the replacement text or Lua function to execute.
+///
+/// Example:
+/// <pre>
+/// :call nvim_create_user_command('SayHello', 'echo "Hello world!"', {})
+/// :SayHello
+/// Hello world!
+/// </pre>
+///
+/// @param name Name of the new user command. Must begin with an uppercase letter.
+/// @param command Replacement command to execute when this user command is executed. When called
+/// from Lua, the command can also be a Lua function. The function is called with a
+/// single table argument that contains the following keys:
+/// - args: (string) The args passed to the command, if any |<args>|
+/// - fargs: (table) The args split by unescaped whitespace (when more than one
+/// argument is allowed), if any |<f-args>|
+/// - bang: (boolean) "true" if the command was executed with a ! modifier |<bang>|
+/// - line1: (number) The starting line of the command range |<line1>|
+/// - line2: (number) The final line of the command range |<line2>|
+/// - range: (number) The number of items in the command range: 0, 1, or 2 |<range>|
+/// - count: (number) Any count supplied |<count>|
+/// - reg: (string) The optional register, if specified |<reg>|
+/// - mods: (string) Command modifiers, if any |<mods>|
+/// - smods: (table) Command modifiers in a structured format. Has the same
+/// structure as the "mods" key of |nvim_parse_cmd()|.
+/// @param opts Optional command attributes. See |command-attributes| for more details. To use
+/// boolean attributes (such as |:command-bang| or |:command-bar|) set the value to
+/// "true". In addition to the string options listed in |:command-complete|, the
+/// "complete" key also accepts a Lua function which works like the "customlist"
+/// completion mode |:command-completion-customlist|. Additional parameters:
+/// - desc: (string) Used for listing the command when a Lua function is used for
+/// {command}.
+/// - force: (boolean, default true) Override any previous definition.
+/// - preview: (function) Preview callback for 'inccommand' |:command-preview|
+/// @param[out] err Error details, if any.
+void nvim_create_user_command(String name, Object command, Dict(user_command) *opts, Error *err)
+ FUNC_API_SINCE(9)
+{
+ create_user_command(name, command, opts, 0, err);
+}
+
+/// Delete a user-defined command.
+///
+/// @param name Name of the command to delete.
+/// @param[out] err Error details, if any.
+void nvim_del_user_command(String name, Error *err)
+ FUNC_API_SINCE(9)
+{
+ nvim_buf_del_user_command(-1, name, err);
+}
+
+/// Create a new user command |user-commands| in the given buffer.
+///
+/// @param buffer Buffer handle, or 0 for current buffer.
+/// @param[out] err Error details, if any.
+/// @see nvim_create_user_command
+void nvim_buf_create_user_command(Buffer buffer, String name, Object command,
+ Dict(user_command) *opts, Error *err)
+ FUNC_API_SINCE(9)
+{
+ buf_T *target_buf = find_buffer_by_handle(buffer, err);
+ if (ERROR_SET(err)) {
+ return;
+ }
+
+ buf_T *save_curbuf = curbuf;
+ curbuf = target_buf;
+ create_user_command(name, command, opts, UC_BUFFER, err);
+ curbuf = save_curbuf;
+}
+
+/// Delete a buffer-local user-defined command.
+///
+/// Only commands created with |:command-buffer| or
+/// |nvim_buf_create_user_command()| can be deleted with this function.
+///
+/// @param buffer Buffer handle, or 0 for current buffer.
+/// @param name Name of the command to delete.
+/// @param[out] err Error details, if any.
+void nvim_buf_del_user_command(Buffer buffer, String name, Error *err)
+ FUNC_API_SINCE(9)
+{
+ garray_T *gap;
+ if (buffer == -1) {
+ gap = &ucmds;
+ } else {
+ buf_T *buf = find_buffer_by_handle(buffer, err);
+ gap = &buf->b_ucmds;
+ }
+
+ for (int i = 0; i < gap->ga_len; i++) {
+ ucmd_T *cmd = USER_CMD_GA(gap, i);
+ if (!STRCMP(name.data, cmd->uc_name)) {
+ free_ucmd(cmd);
+
+ gap->ga_len -= 1;
+
+ if (i < gap->ga_len) {
+ memmove(cmd, cmd + 1, (size_t)(gap->ga_len - i) * sizeof(ucmd_T));
+ }
+
+ return;
+ }
+ }
+
+ api_set_error(err, kErrorTypeException, "No such user-defined command: %s", name.data);
+}
+
+void create_user_command(String name, Object command, Dict(user_command) *opts, int flags,
+ Error *err)
+{
+ uint32_t argt = 0;
+ long def = -1;
+ cmd_addr_T addr_type_arg = ADDR_NONE;
+ int compl = EXPAND_NOTHING;
+ char *compl_arg = NULL;
+ char *rep = NULL;
+ LuaRef luaref = LUA_NOREF;
+ LuaRef compl_luaref = LUA_NOREF;
+ LuaRef preview_luaref = LUA_NOREF;
+
+ if (!uc_validate_name(name.data)) {
+ api_set_error(err, kErrorTypeValidation, "Invalid command name");
+ goto err;
+ }
+
+ 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;
+ }
+
+ if (opts->nargs.type == kObjectTypeInteger) {
+ switch (opts->nargs.data.integer) {
+ case 0:
+ // Default value, nothing to do
+ break;
+ case 1:
+ argt |= EX_EXTRA | EX_NOSPC | EX_NEEDARG;
+ break;
+ default:
+ api_set_error(err, kErrorTypeValidation, "Invalid value for 'nargs'");
+ goto err;
+ }
+ } else if (opts->nargs.type == kObjectTypeString) {
+ if (opts->nargs.data.string.size > 1) {
+ api_set_error(err, kErrorTypeValidation, "Invalid value for 'nargs'");
+ goto err;
+ }
+
+ switch (opts->nargs.data.string.data[0]) {
+ case '*':
+ argt |= EX_EXTRA;
+ break;
+ case '?':
+ argt |= EX_EXTRA | EX_NOSPC;
+ break;
+ case '+':
+ argt |= EX_EXTRA | EX_NEEDARG;
+ break;
+ default:
+ api_set_error(err, kErrorTypeValidation, "Invalid value for 'nargs'");
+ goto err;
+ }
+ } else if (HAS_KEY(opts->nargs)) {
+ api_set_error(err, kErrorTypeValidation, "Invalid value for 'nargs'");
+ goto err;
+ }
+
+ if (HAS_KEY(opts->complete) && !argt) {
+ api_set_error(err, kErrorTypeValidation, "'complete' used without 'nargs'");
+ goto err;
+ }
+
+ if (opts->range.type == kObjectTypeBoolean) {
+ if (opts->range.data.boolean) {
+ argt |= EX_RANGE;
+ addr_type_arg = ADDR_LINES;
+ }
+ } else if (opts->range.type == kObjectTypeString) {
+ if (opts->range.data.string.data[0] == '%' && opts->range.data.string.size == 1) {
+ argt |= EX_RANGE | EX_DFLALL;
+ addr_type_arg = ADDR_LINES;
+ } else {
+ api_set_error(err, kErrorTypeValidation, "Invalid value for 'range'");
+ goto err;
+ }
+ } else if (opts->range.type == kObjectTypeInteger) {
+ argt |= EX_RANGE | EX_ZEROR;
+ def = opts->range.data.integer;
+ addr_type_arg = ADDR_LINES;
+ } else if (HAS_KEY(opts->range)) {
+ api_set_error(err, kErrorTypeValidation, "Invalid value for 'range'");
+ goto err;
+ }
+
+ if (opts->count.type == kObjectTypeBoolean) {
+ if (opts->count.data.boolean) {
+ argt |= EX_COUNT | EX_ZEROR | EX_RANGE;
+ addr_type_arg = ADDR_OTHER;
+ def = 0;
+ }
+ } else if (opts->count.type == kObjectTypeInteger) {
+ argt |= EX_COUNT | EX_ZEROR | EX_RANGE;
+ addr_type_arg = ADDR_OTHER;
+ def = opts->count.data.integer;
+ } else if (HAS_KEY(opts->count)) {
+ api_set_error(err, kErrorTypeValidation, "Invalid value for 'count'");
+ goto err;
+ }
+
+ if (opts->addr.type == kObjectTypeString) {
+ if (parse_addr_type_arg(opts->addr.data.string.data, (int)opts->addr.data.string.size,
+ &addr_type_arg) != OK) {
+ api_set_error(err, kErrorTypeValidation, "Invalid value for 'addr'");
+ goto err;
+ }
+
+ if (addr_type_arg != ADDR_LINES) {
+ argt |= EX_ZEROR;
+ }
+ } else if (HAS_KEY(opts->addr)) {
+ api_set_error(err, kErrorTypeValidation, "Invalid value for 'addr'");
+ goto err;
+ }
+
+ if (api_object_to_bool(opts->bang, "bang", false, err)) {
+ argt |= EX_BANG;
+ } else if (ERROR_SET(err)) {
+ goto err;
+ }
+
+ if (api_object_to_bool(opts->bar, "bar", false, err)) {
+ argt |= EX_TRLBAR;
+ } else if (ERROR_SET(err)) {
+ goto err;
+ }
+
+ if (api_object_to_bool(opts->register_, "register", false, err)) {
+ argt |= EX_REGSTR;
+ } else if (ERROR_SET(err)) {
+ 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;
+ }
+
+ if (opts->complete.type == kObjectTypeLuaRef) {
+ compl = EXPAND_USER_LUA;
+ compl_luaref = api_new_luaref(opts->complete.data.luaref);
+ } else if (opts->complete.type == kObjectTypeString) {
+ if (parse_compl_arg(opts->complete.data.string.data,
+ (int)opts->complete.data.string.size, &compl, &argt,
+ &compl_arg) != OK) {
+ api_set_error(err, kErrorTypeValidation, "Invalid value for 'complete'");
+ goto err;
+ }
+ } else if (HAS_KEY(opts->complete)) {
+ api_set_error(err, kErrorTypeValidation, "Invalid value for 'complete'");
+ goto err;
+ }
+
+ if (opts->preview.type == kObjectTypeLuaRef) {
+ argt |= EX_PREVIEW;
+ preview_luaref = api_new_luaref(opts->preview.data.luaref);
+ } else if (HAS_KEY(opts->preview)) {
+ api_set_error(err, kErrorTypeValidation, "Invalid value for 'preview'");
+ goto err;
+ }
+
+ switch (command.type) {
+ case kObjectTypeLuaRef:
+ luaref = api_new_luaref(command.data.luaref);
+ if (opts->desc.type == kObjectTypeString) {
+ rep = opts->desc.data.string.data;
+ } else {
+ snprintf((char *)IObuff, IOSIZE, "<Lua function %d>", luaref);
+ rep = (char *)IObuff;
+ }
+ break;
+ case kObjectTypeString:
+ rep = command.data.string.data;
+ break;
+ default:
+ api_set_error(err, kErrorTypeValidation, "'command' must be a string or Lua function");
+ goto err;
+ }
+
+ if (uc_add_command(name.data, name.size, rep, argt, def, flags, compl, compl_arg, compl_luaref,
+ preview_luaref, addr_type_arg, luaref, force) != OK) {
+ api_set_error(err, kErrorTypeException, "Failed to create user command");
+ // Do not goto err, since uc_add_command now owns luaref, compl_luaref, and compl_arg
+ }
+
+ return;
+
+err:
+ NLUA_CLEAR_REF(luaref);
+ NLUA_CLEAR_REF(compl_luaref);
+ xfree(compl_arg);
+}
+/// Gets a map of global (non-buffer-local) Ex commands.
+///
+/// Currently only |user-commands| are supported, not builtin Ex commands.
+///
+/// @param opts Optional parameters. Currently only supports
+/// {"builtin":false}
+/// @param[out] err Error details, if any.
+///
+/// @returns Map of maps describing commands.
+Dictionary nvim_get_commands(Dict(get_commands) *opts, Error *err)
+ FUNC_API_SINCE(4)
+{
+ return nvim_buf_get_commands(-1, opts, err);
+}
+
+/// Gets a map of buffer-local |user-commands|.
+///
+/// @param buffer Buffer handle, or 0 for current buffer
+/// @param opts Optional parameters. Currently not used.
+/// @param[out] err Error details, if any.
+///
+/// @returns Map of maps describing commands.
+Dictionary nvim_buf_get_commands(Buffer buffer, Dict(get_commands) *opts, Error *err)
+ FUNC_API_SINCE(4)
+{
+ bool global = (buffer == -1);
+ bool builtin = api_object_to_bool(opts->builtin, "builtin", false, err);
+ if (ERROR_SET(err)) {
+ return (Dictionary)ARRAY_DICT_INIT;
+ }
+
+ if (global) {
+ if (builtin) {
+ api_set_error(err, kErrorTypeValidation, "builtin=true not implemented");
+ return (Dictionary)ARRAY_DICT_INIT;
+ }
+ return commands_array(NULL);
+ }
+
+ buf_T *buf = find_buffer_by_handle(buffer, err);
+ if (builtin || !buf) {
+ return (Dictionary)ARRAY_DICT_INIT;
+ }
+ return commands_array(buf);
+}
diff --git a/src/nvim/api/command.h b/src/nvim/api/command.h
new file mode 100644
index 0000000000..b1c9230551
--- /dev/null
+++ b/src/nvim/api/command.h
@@ -0,0 +1,11 @@
+#ifndef NVIM_API_COMMAND_H
+#define NVIM_API_COMMAND_H
+
+#include "nvim/api/private/defs.h"
+#include "nvim/decoration.h"
+#include "nvim/ex_cmds.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "api/command.h.generated.h"
+#endif
+#endif // NVIM_API_COMMAND_H
diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c
index 76b699800e..abaac07755 100644
--- a/src/nvim/api/deprecated.c
+++ b/src/nvim/api/deprecated.c
@@ -22,11 +22,11 @@
/// @deprecated
/// @see nvim_exec
-String nvim_command_output(String command, Error *err)
+String nvim_command_output(uint64_t channel_id, String command, Error *err)
FUNC_API_SINCE(1)
FUNC_API_DEPRECATED_SINCE(7)
{
- return nvim_exec(command, true, err);
+ return nvim_exec(channel_id, command, true, err);
}
/// @deprecated Use nvim_exec_lua() instead.
@@ -51,11 +51,10 @@ Integer nvim_buf_get_number(Buffer buffer, Error *err)
FUNC_API_SINCE(1)
FUNC_API_DEPRECATED_SINCE(2)
{
- Integer rv = 0;
buf_T *buf = find_buffer_by_handle(buffer, err);
if (!buf) {
- return rv;
+ return 0;
}
return buf->b_fnum;
@@ -79,7 +78,6 @@ void nvim_buf_clear_highlight(Buffer buffer, Integer ns_id, Integer line_start,
nvim_buf_clear_namespace(buffer, ns_id, line_start, line_end, err);
}
-
/// Set the virtual text (annotation) for a buffer line.
///
/// @deprecated use nvim_buf_set_extmark to use full virtual text
@@ -130,7 +128,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);
@@ -138,7 +136,6 @@ Integer nvim_buf_set_virtual_text(Buffer buffer, Integer src_id, Integer line, A
return 0;
}
-
Decoration *existing = decor_find_virttext(buf, (int)line, ns_id);
if (existing) {
@@ -148,11 +145,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;
}
@@ -192,7 +190,7 @@ String buffer_get_line(Buffer buffer, Integer index, Error *err)
String rv = { .size = 0 };
index = convert_index(index);
- Array slice = nvim_buf_get_lines(0, buffer, index, index+1, true, err);
+ Array slice = nvim_buf_get_lines(0, buffer, index, index + 1, true, err);
if (!ERROR_SET(err) && slice.size) {
rv = slice.items[0].data.string;
@@ -221,7 +219,7 @@ void buffer_set_line(Buffer buffer, Integer index, String line, Error *err)
Object l = STRING_OBJ(line);
Array array = { .items = &l, .size = 1 };
index = convert_index(index);
- nvim_buf_set_lines(0, buffer, index, index+1, true, array, err);
+ nvim_buf_set_lines(0, buffer, index, index + 1, true, array, err);
}
/// Deletes a buffer line
@@ -239,7 +237,7 @@ void buffer_del_line(Buffer buffer, Integer index, Error *err)
{
Array array = ARRAY_DICT_INIT;
index = convert_index(index);
- nvim_buf_set_lines(0, buffer, index, index+1, true, array, err);
+ nvim_buf_set_lines(0, buffer, index, index + 1, true, array, err);
}
/// Retrieves a line range from the buffer
@@ -292,7 +290,6 @@ void buffer_set_line_slice(Buffer buffer, Integer start, Integer end, Boolean in
nvim_buf_set_lines(0, buffer, start, end, false, replacement, err);
}
-
/// Sets a buffer-scoped (b:) variable
///
/// @deprecated
diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c
index 742b953c2a..da1b6beeda 100644
--- a/src/nvim/api/extmark.c
+++ b/src/nvim/api/extmark.c
@@ -8,11 +8,13 @@
#include "nvim/api/extmark.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
+#include "nvim/charset.h"
+#include "nvim/decoration_provider.h"
#include "nvim/extmark.h"
+#include "nvim/highlight_group.h"
#include "nvim/lua/executor.h"
#include "nvim/memline.h"
#include "nvim/screen.h"
-#include "nvim/syntax.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/extmark.c.generated.h"
@@ -85,43 +87,79 @@ 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;
}
-
-static Array extmark_to_array(ExtmarkInfo extmark, bool id, bool add_dict)
+static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict)
{
Array rv = ARRAY_DICT_INIT;
if (id) {
- ADD(rv, INTEGER_OBJ((Integer)extmark.mark_id));
+ ADD(rv, INTEGER_OBJ((Integer)extmark->mark_id));
}
- ADD(rv, INTEGER_OBJ(extmark.row));
- ADD(rv, INTEGER_OBJ(extmark.col));
+ ADD(rv, INTEGER_OBJ(extmark->row));
+ ADD(rv, INTEGER_OBJ(extmark->col));
if (add_dict) {
Dictionary dict = ARRAY_DICT_INIT;
- 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, "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));
+ }
+
+ const 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 (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));
+ 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 (decor->ui_watched) {
+ PUT(dict, "ui_watched", BOOLEAN_OBJ(true));
+ }
+
+ 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 +167,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) || decor->ui_watched) {
PUT(dict, "priority", INTEGER_OBJ(decor->priority));
}
@@ -166,7 +209,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;
}
@@ -190,12 +233,11 @@ 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;
}
- return extmark_to_array(extmark, false, details);
+ return extmark_to_array(&extmark, false, details);
}
/// Gets extmarks in "traversal order" from a |charwise| region defined by
@@ -220,9 +262,9 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id,
/// local pos = a.nvim_win_get_cursor(0)
/// local ns = a.nvim_create_namespace('my-plugin')
/// -- Create new extmark at line 1, column 1.
-/// local m1 = a.nvim_buf_set_extmark(0, ns, 0, 0, 0, {})
+/// local m1 = a.nvim_buf_set_extmark(0, ns, 0, 0, {})
/// -- Create new extmark at line 3, column 1.
-/// local m2 = a.nvim_buf_set_extmark(0, ns, 0, 2, 0, {})
+/// local m2 = a.nvim_buf_set_extmark(0, ns, 0, 2, {})
/// -- Get extmarks only from line 3.
/// local ms = a.nvim_buf_get_extmarks(0, ns, {2,0}, {2,0}, {})
/// -- Get all marks in this buffer + namespace.
@@ -252,7 +294,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;
}
@@ -290,7 +332,6 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
limit = INT64_MAX;
}
-
bool reverse = false;
int l_row;
@@ -309,12 +350,11 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
reverse = true;
}
-
- 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++) {
- ADD(rv, ARRAY_OBJ(extmark_to_array(kv_A(marks, i), true, (bool)details)));
+ ADD(rv, ARRAY_OBJ(extmark_to_array(&kv_A(marks, i), true, (bool)details)));
}
kv_destroy(marks);
@@ -323,12 +363,11 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
/// Creates or updates an extmark.
///
-/// To create a new extmark, pass id=0. The extmark id will be returned.
-/// To move an existing mark, pass its id.
-///
-/// It is also allowed to create a new mark by passing in a previously unused
-/// id, but the caller must then keep track of existing and unused ids itself.
-/// (Useful over RPC, to avoid waiting for the return value.)
+/// By default a new extmark is created when no id is passed in, but it is also
+/// possible to create a new mark by passing in a previously unused id or move
+/// an existing mark by passing in its id. The caller must then keep track of
+/// existing and unused ids itself. (Useful over RPC, to avoid waiting for the
+/// return value.)
///
/// Using the optional arguments, it is possible to use this to highlight
/// a range of text, and also to associate virtual text to the mark.
@@ -404,6 +443,40 @@ 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.
+/// - sign_text: string of length 1-2 used to display in the
+/// sign column.
+/// Note: ranges are unsupported and decorations are only
+/// applied to start_row
+/// - sign_hl_group: name of the highlight group used to
+/// highlight the sign column text.
+/// Note: ranges are unsupported and decorations are only
+/// applied to start_row
+/// - number_hl_group: name of the highlight group used to
+/// highlight the number column.
+/// Note: ranges are unsupported and decorations are only
+/// applied to start_row
+/// - line_hl_group: name of the highlight group used to
+/// highlight the whole line.
+/// Note: ranges are unsupported and decorations are only
+/// applied to start_row
+/// - cursorline_hl_group: name of the highlight group used to
+/// highlight the line when the cursor is on the same line
+/// as the mark and 'cursorline' is enabled.
+/// Note: ranges are unsupported and decorations are only
+/// applied to start_row
+/// - conceal: string which should be either empty or a single
+/// character. Enable concealing similar to |:syn-conceal|.
+/// When a character is supplied it is used as |:syn-cchar|.
+/// "hl_group" is used as highlight for the cchar if provided,
+/// otherwise it defaults to |hl-Conceal|.
+/// - ui_watched: boolean that indicates the mark should be drawn
+/// by a UI. When set, the UI will receive win_extmark events.
+/// Note: the mark is positioned by virt_text attributes. Can be
+/// used together with virt_text.
+///
/// @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,
@@ -411,20 +484,21 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
FUNC_API_SINCE(7)
{
Decoration decor = DECORATION_INIT;
+ bool has_decor = false;
buf_T *buf = find_buffer_by_handle(buffer, err);
if (!buf) {
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 +515,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 {
@@ -468,16 +551,49 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
goto error;
}
- if (HAS_KEY(opts->hl_group)) {
- decor.hl_id = object_to_hl_id(opts->hl_group, "hl_group", err);
- if (ERROR_SET(err)) {
- goto error;
+ // uncrustify:off
+
+ struct {
+ const char *name;
+ Object *opt;
+ int *dest;
+ } hls[] = {
+ { "hl_group" , &opts->hl_group , &decor.hl_id },
+ { "sign_hl_group" , &opts->sign_hl_group , &decor.sign_hl_id },
+ { "number_hl_group" , &opts->number_hl_group , &decor.number_hl_id },
+ { "line_hl_group" , &opts->line_hl_group , &decor.line_hl_id },
+ { "cursorline_hl_group", &opts->cursorline_hl_group, &decor.cursorline_hl_id },
+ { NULL, NULL, NULL },
+ };
+
+ // uncrustify:on
+
+ for (int j = 0; hls[j].name && hls[j].dest; j++) {
+ if (HAS_KEY(*hls[j].opt)) {
+ *hls[j].dest = object_to_hl_id(*hls[j].opt, hls[j].name, err);
+ if (ERROR_SET(err)) {
+ goto error;
+ }
+ has_decor = true;
}
}
+ if (opts->conceal.type == kObjectTypeString) {
+ String c = opts->conceal.data.string;
+ decor.conceal = true;
+ if (c.size) {
+ decor.conceal_char = utf_ptr2char(c.data);
+ }
+ has_decor = true;
+ } else if (HAS_KEY(opts->conceal)) {
+ api_set_error(err, kErrorTypeValidation, "conceal is not a String");
+ goto error;
+ }
+
if (opts->virt_text.type == kObjectTypeArray) {
decor.virt_text = parse_virt_text(opts->virt_text.data.array, err,
&decor.virt_text_width);
+ has_decor = true;
if (ERROR_SET(err)) {
goto error;
}
@@ -512,12 +628,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);
@@ -555,13 +665,13 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
if (ERROR_SET(err)) {
goto error;
}
+ has_decor = true;
}
} else if (HAS_KEY(opts->virt_lines)) {
api_set_error(err, kErrorTypeValidation, "virt_lines is not an Array");
goto error;
}
-
OPTION_TO_BOOL(decor.virt_lines_above, virt_lines_above, false);
if (opts->priority.type == kObjectTypeInteger) {
@@ -577,6 +687,18 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
goto error;
}
+ if (opts->sign_text.type == kObjectTypeString) {
+ if (!init_sign_text((char **)&decor.sign_text,
+ opts->sign_text.data.string.data)) {
+ api_set_error(err, kErrorTypeValidation, "sign_text is not a valid value");
+ goto error;
+ }
+ has_decor = true;
+ } else if (HAS_KEY(opts->sign_text)) {
+ api_set_error(err, kErrorTypeValidation, "sign_text is not a String");
+ goto error;
+ }
+
bool right_gravity = true;
OPTION_TO_BOOL(right_gravity, right_gravity, true);
@@ -596,16 +718,35 @@ 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) {
+ OPTION_TO_BOOL(decor.ui_watched, ui_watched, false);
+ if (decor.ui_watched) {
+ has_decor = true;
+ }
+
+ 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));
+ 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,49 +762,36 @@ 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) {
- decor_add_ephemeral((int)line, (int)col, line2, col2, &decor);
+ decor_add_ephemeral((int)line, (int)col, line2, col2, &decor, (uint64_t)ns_id, id);
} else {
if (ephemeral) {
api_set_error(err, kErrorTypeException, "not yet implemented");
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,
+ has_decor ? &decor : NULL, right_gravity, end_right_gravity,
+ kExtmarkNoUndo);
}
return (Integer)id;
error:
clear_virttext(&decor.virt_text);
+ xfree(decor.sign_text);
return 0;
}
@@ -682,23 +810,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 +881,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
@@ -762,7 +890,7 @@ Integer nvim_buf_add_highlight(Buffer buffer, Integer ns_id, String hl_group, In
int hl_id = 0;
if (hl_group.size > 0) {
- hl_id = syn_check_group(hl_group.data, (int)hl_group.size);
+ hl_id = syn_check_group(hl_group.data, hl_group.size);
} else {
return ns_id;
}
@@ -773,10 +901,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,9 +939,9 @@ 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);
+ (int)line_end - 1, MAXCOL);
}
/// Set or change decoration provider for a namespace
@@ -903,3 +1034,154 @@ void nvim_set_decoration_provider(Integer ns_id, DictionaryOf(LuaRef) opts, Erro
error:
decor_provider_clear(p);
}
+
+/// Gets the line and column of an extmark.
+///
+/// Extmarks may be queried by position, name or even special names
+/// in the future such as "cursor".
+///
+/// @param[out] lnum extmark line
+/// @param[out] colnr extmark column
+///
+/// @return true if the extmark was found, else false
+static bool extmark_get_index_from_obj(buf_T *buf, Integer ns_id, Object obj, int *row,
+ colnr_T *col, Error *err)
+{
+ // Check if it is mark id
+ if (obj.type == kObjectTypeInteger) {
+ Integer id = obj.data.integer;
+ if (id == 0) {
+ *row = 0;
+ *col = 0;
+ return true;
+ } else if (id == -1) {
+ *row = MAXLNUM;
+ *col = MAXCOL;
+ return true;
+ } else if (id < 0) {
+ api_set_error(err, kErrorTypeValidation, "Mark id must be positive");
+ return false;
+ }
+
+ ExtmarkInfo extmark = extmark_from_id(buf, (uint32_t)ns_id, (uint32_t)id);
+ if (extmark.row >= 0) {
+ *row = extmark.row;
+ *col = extmark.col;
+ return true;
+ } else {
+ api_set_error(err, kErrorTypeValidation, "No mark with requested id");
+ return false;
+ }
+
+ // Check if it is a position
+ } else if (obj.type == kObjectTypeArray) {
+ Array pos = obj.data.array;
+ if (pos.size != 2
+ || pos.items[0].type != kObjectTypeInteger
+ || pos.items[1].type != kObjectTypeInteger) {
+ api_set_error(err, kErrorTypeValidation,
+ "Position must have 2 integer elements");
+ return false;
+ }
+ Integer pos_row = pos.items[0].data.integer;
+ Integer pos_col = pos.items[1].data.integer;
+ *row = (int)(pos_row >= 0 ? pos_row : MAXLNUM);
+ *col = (colnr_T)(pos_col >= 0 ? pos_col : MAXCOL);
+ return true;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "Position must be a mark id Integer or position Array");
+ return false;
+ }
+}
+// adapted from sign.c:sign_define_init_text.
+// TODO(lewis6991): Consider merging
+static int init_sign_text(char **sign_text, char *text)
+{
+ char *s;
+
+ char *endp = text + (int)STRLEN(text);
+
+ // Count cells and check for non-printable chars
+ int cells = 0;
+ for (s = text; s < endp; s += utfc_ptr2len(s)) {
+ if (!vim_isprintc(utf_ptr2char(s))) {
+ break;
+ }
+ cells += utf_ptr2cells(s);
+ }
+ // Currently must be empty, one or two display cells
+ if (s != endp || cells > 2) {
+ return FAIL;
+ }
+ if (cells < 1) {
+ return OK;
+ }
+
+ // Allocate one byte more if we need to pad up
+ // with a space.
+ size_t len = (size_t)(endp - text + ((cells == 1) ? 1 : 0));
+ *sign_text = xstrnsave(text, len);
+
+ if (cells == 1) {
+ STRCPY(*sign_text + len - 1, " ");
+ }
+
+ return OK;
+}
+
+VirtText parse_virt_text(Array chunks, Error *err, int *width)
+{
+ VirtText virt_text = KV_INITIAL_VALUE;
+ int w = 0;
+ for (size_t i = 0; i < chunks.size; i++) {
+ if (chunks.items[i].type != kObjectTypeArray) {
+ api_set_error(err, kErrorTypeValidation, "Chunk is not an array");
+ goto free_exit;
+ }
+ Array chunk = chunks.items[i].data.array;
+ if (chunk.size == 0 || chunk.size > 2
+ || chunk.items[0].type != kObjectTypeString) {
+ api_set_error(err, kErrorTypeValidation,
+ "Chunk is not an array with one or two strings");
+ goto free_exit;
+ }
+
+ String str = chunk.items[0].data.string;
+
+ int hl_id = 0;
+ if (chunk.size == 2) {
+ Object hl = chunk.items[1];
+ if (hl.type == kObjectTypeArray) {
+ Array arr = hl.data.array;
+ for (size_t j = 0; j < arr.size; j++) {
+ hl_id = object_to_hl_id(arr.items[j], "virt_text highlight", err);
+ if (ERROR_SET(err)) {
+ goto free_exit;
+ }
+ if (j < arr.size - 1) {
+ kv_push(virt_text, ((VirtTextChunk){ .text = NULL,
+ .hl_id = hl_id }));
+ }
+ }
+ } else {
+ hl_id = object_to_hl_id(hl, "virt_text highlight", err);
+ if (ERROR_SET(err)) {
+ goto free_exit;
+ }
+ }
+ }
+
+ char *text = transstr(str.size > 0 ? str.data : "", false); // allocates
+ w += (int)mb_string2cells(text);
+
+ kv_push(virt_text, ((VirtTextChunk){ .text = text, .hl_id = hl_id }));
+ }
+
+ *width = w;
+ return virt_text;
+
+free_exit:
+ clear_virttext(&virt_text);
+ return virt_text;
+}
diff --git a/src/nvim/api/extmark.h b/src/nvim/api/extmark.h
index c5e463cd86..74802c6efb 100644
--- a/src/nvim/api/extmark.h
+++ b/src/nvim/api/extmark.h
@@ -2,6 +2,7 @@
#define NVIM_API_EXTMARK_H
#include "nvim/api/private/defs.h"
+#include "nvim/decoration.h"
#include "nvim/map.h"
EXTERN Map(String, handle_T) namespace_ids INIT(= MAP_INIT);
diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua
index df271c805f..1f1fa1e63a 100644
--- a/src/nvim/api/keysets.lua
+++ b/src/nvim/api/keysets.lua
@@ -21,6 +21,14 @@ return {
"virt_lines";
"virt_lines_above";
"virt_lines_leftcol";
+ "strict";
+ "sign_text";
+ "sign_hl_group";
+ "number_hl_group";
+ "line_hl_group";
+ "cursorline_hl_group";
+ "conceal";
+ "ui_watched";
};
keymap = {
"noremap";
@@ -43,7 +51,9 @@ return {
"count";
"desc";
"force";
+ "keepscript";
"nargs";
+ "preview";
"range";
"register";
};
@@ -67,16 +77,136 @@ return {
};
runtime = {
"is_lua";
+ "do_source";
};
eval_statusline = {
"winid";
"maxwidth";
"fillchar";
"highlights";
+ "use_winbar";
"use_tabline";
};
option = {
"scope";
+ "win";
+ "buf";
+ };
+ highlight = {
+ "bold";
+ "standout";
+ "strikethrough";
+ "underline";
+ "undercurl";
+ "underdouble";
+ "underdotted";
+ "underdashed";
+ "italic";
+ "reverse";
+ "nocombine";
+ "default";
+ "global";
+ "cterm";
+ "foreground"; "fg";
+ "background"; "bg";
+ "ctermfg";
+ "ctermbg";
+ "special"; "sp";
+ "link";
+ "fallback";
+ "blend";
+ "temp";
+ };
+ highlight_cterm = {
+ "bold";
+ "standout";
+ "strikethrough";
+ "underline";
+ "undercurl";
+ "underdouble";
+ "underdotted";
+ "underdashed";
+ "italic";
+ "reverse";
+ "nocombine";
+ };
+ -- Autocmds
+ clear_autocmds = {
+ "buffer";
+ "event";
+ "group";
+ "pattern";
+ };
+ create_autocmd = {
+ "buffer";
+ "callback";
+ "command";
+ "desc";
+ "group";
+ "nested";
+ "once";
+ "pattern";
+ };
+ exec_autocmds = {
+ "buffer";
+ "group";
+ "modeline";
+ "pattern";
+ "data";
+ };
+ get_autocmds = {
+ "event";
+ "group";
+ "pattern";
+ "buffer";
+ };
+ create_augroup = {
+ "clear";
+ };
+ cmd = {
+ "cmd";
+ "range";
+ "count";
+ "reg";
+ "bang";
+ "args";
+ "magic";
+ "mods";
+ "nargs";
+ "addr";
+ "nextcmd";
+ };
+ cmd_magic = {
+ "file";
+ "bar";
+ };
+ cmd_mods = {
+ "silent";
+ "emsg_silent";
+ "unsilent";
+ "filter";
+ "sandbox";
+ "noautocmd";
+ "browse";
+ "confirm";
+ "hide";
+ "keepalt";
+ "keepjumps";
+ "keepmarks";
+ "keeppatterns";
+ "lockmarks";
+ "noswapfile";
+ "tab";
+ "verbose";
+ "vertical";
+ "split";
+ };
+ cmd_mods_filter = {
+ "pattern";
+ "force";
+ };
+ cmd_opts = {
+ "output";
};
}
diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c
new file mode 100644
index 0000000000..4ed676e613
--- /dev/null
+++ b/src/nvim/api/options.c
@@ -0,0 +1,554 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "nvim/api/options.h"
+#include "nvim/api/private/converter.h"
+#include "nvim/api/private/helpers.h"
+#include "nvim/autocmd.h"
+#include "nvim/buffer.h"
+#include "nvim/option.h"
+#include "nvim/option_defs.h"
+#include "nvim/window.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "api/options.c.generated.h"
+#endif
+
+static int validate_option_value_args(Dict(option) *opts, int *scope, int *opt_type, void **from,
+ Error *err)
+{
+ if (opts->scope.type == kObjectTypeString) {
+ if (!strcmp(opts->scope.data.string.data, "local")) {
+ *scope = OPT_LOCAL;
+ } else if (!strcmp(opts->scope.data.string.data, "global")) {
+ *scope = OPT_GLOBAL;
+ } else {
+ api_set_error(err, kErrorTypeValidation, "invalid scope: must be 'local' or 'global'");
+ return FAIL;
+ }
+ } else if (HAS_KEY(opts->scope)) {
+ api_set_error(err, kErrorTypeValidation, "invalid value for key: scope");
+ return FAIL;
+ }
+
+ *opt_type = SREQ_GLOBAL;
+
+ if (opts->win.type == kObjectTypeInteger) {
+ *opt_type = SREQ_WIN;
+ *from = find_window_by_handle((int)opts->win.data.integer, err);
+ if (ERROR_SET(err)) {
+ return FAIL;
+ }
+ } else if (HAS_KEY(opts->win)) {
+ api_set_error(err, kErrorTypeValidation, "invalid value for key: win");
+ return FAIL;
+ }
+
+ if (opts->buf.type == kObjectTypeInteger) {
+ *scope = OPT_LOCAL;
+ *opt_type = SREQ_BUF;
+ *from = find_buffer_by_handle((int)opts->buf.data.integer, err);
+ if (ERROR_SET(err)) {
+ return FAIL;
+ }
+ } else if (HAS_KEY(opts->buf)) {
+ api_set_error(err, kErrorTypeValidation, "invalid value for key: buf");
+ return FAIL;
+ }
+
+ if (HAS_KEY(opts->scope) && HAS_KEY(opts->buf)) {
+ api_set_error(err, kErrorTypeValidation, "scope and buf cannot be used together");
+ return FAIL;
+ }
+
+ if (HAS_KEY(opts->win) && HAS_KEY(opts->buf)) {
+ api_set_error(err, kErrorTypeValidation, "buf and win cannot be used together");
+ return FAIL;
+ }
+
+ return OK;
+}
+
+/// Gets the value of an option. The behavior of this function matches that of
+/// |:set|: the local value of an option is returned if it exists; otherwise,
+/// the global value is returned. Local values always correspond to the current
+/// buffer or window, unless "buf" or "win" is set in {opts}.
+///
+/// @param name Option name
+/// @param opts Optional parameters
+/// - scope: One of "global" or "local". Analogous to
+/// |:setglobal| and |:setlocal|, respectively.
+/// - win: |window-ID|. Used for getting window local options.
+/// - buf: Buffer number. Used for getting buffer local options.
+/// Implies {scope} is "local".
+/// @param[out] err Error details, if any
+/// @return Option value
+Object nvim_get_option_value(String name, Dict(option) *opts, Error *err)
+ FUNC_API_SINCE(9)
+{
+ Object rv = OBJECT_INIT;
+
+ int scope = 0;
+ int opt_type = SREQ_GLOBAL;
+ void *from = NULL;
+ if (!validate_option_value_args(opts, &scope, &opt_type, &from, err)) {
+ return rv;
+ }
+
+ long numval = 0;
+ char *stringval = NULL;
+ int result = access_option_value_for(name.data, &numval, &stringval, scope, opt_type, from,
+ true, err);
+ if (ERROR_SET(err)) {
+ return rv;
+ }
+
+ switch (result) {
+ case 0:
+ rv = STRING_OBJ(cstr_as_string(stringval));
+ break;
+ case 1:
+ rv = INTEGER_OBJ(numval);
+ break;
+ case 2:
+ switch (numval) {
+ case 0:
+ case 1:
+ rv = BOOLEAN_OBJ(numval);
+ break;
+ default:
+ // Boolean options that return something other than 0 or 1 should return nil. Currently this
+ // only applies to 'autoread' which uses -1 as a local value to indicate "unset"
+ rv = NIL;
+ break;
+ }
+ break;
+ default:
+ api_set_error(err, kErrorTypeValidation, "unknown option '%s'", name.data);
+ return rv;
+ }
+
+ return rv;
+}
+
+/// Sets the value of an option. The behavior of this function matches that of
+/// |:set|: for global-local options, both the global and local value are set
+/// unless otherwise specified with {scope}.
+///
+/// Note the options {win} and {buf} cannot be used together.
+///
+/// @param name Option name
+/// @param value New option value
+/// @param opts Optional parameters
+/// - scope: One of 'global' or 'local'. Analogous to
+/// |:setglobal| and |:setlocal|, respectively.
+/// - win: |window-ID|. Used for setting window local option.
+/// - buf: Buffer number. Used for setting buffer local option.
+/// @param[out] err Error details, if any
+void nvim_set_option_value(String name, Object value, Dict(option) *opts, Error *err)
+ FUNC_API_SINCE(9)
+{
+ int scope = 0;
+ int opt_type = SREQ_GLOBAL;
+ void *to = NULL;
+ if (!validate_option_value_args(opts, &scope, &opt_type, &to, err)) {
+ return;
+ }
+
+ // If:
+ // - window id is provided
+ // - scope is not provided
+ // - option is global or local to window (global-local)
+ //
+ // Then force scope to local since we don't want to change the global option
+ if (opt_type == SREQ_WIN && scope == 0) {
+ int flags = get_option_value_strict(name.data, NULL, NULL, opt_type, to);
+ if (flags & SOPT_GLOBAL) {
+ scope = OPT_LOCAL;
+ }
+ }
+
+ long numval = 0;
+ char *stringval = NULL;
+
+ switch (value.type) {
+ case kObjectTypeInteger:
+ numval = value.data.integer;
+ break;
+ case kObjectTypeBoolean:
+ numval = value.data.boolean ? 1 : 0;
+ break;
+ case kObjectTypeString:
+ stringval = value.data.string.data;
+ break;
+ case kObjectTypeNil:
+ scope |= OPT_CLEAR;
+ break;
+ default:
+ api_set_error(err, kErrorTypeValidation, "invalid value for option");
+ return;
+ }
+
+ access_option_value_for(name.data, &numval, &stringval, scope, opt_type, to, false, err);
+}
+
+/// Gets the option information for all options.
+///
+/// The dictionary has the full option names as keys and option metadata
+/// dictionaries as detailed at |nvim_get_option_info|.
+///
+/// @return dictionary of all options
+Dictionary nvim_get_all_options_info(Error *err)
+ FUNC_API_SINCE(7)
+{
+ return get_all_vimoptions();
+}
+
+/// Gets the option information for one option
+///
+/// Resulting dictionary has keys:
+/// - name: Name of the option (like 'filetype')
+/// - shortname: Shortened name of the option (like 'ft')
+/// - type: type of option ("string", "number" or "boolean")
+/// - default: The default value for the option
+/// - was_set: Whether the option was set.
+///
+/// - last_set_sid: Last set script id (if any)
+/// - last_set_linenr: line number where option was set
+/// - last_set_chan: Channel where option was set (0 for local)
+///
+/// - scope: one of "global", "win", or "buf"
+/// - global_local: whether win or buf option has a global value
+///
+/// - commalist: List of comma separated values
+/// - flaglist: List of single char flags
+///
+///
+/// @param name Option name
+/// @param[out] err Error details, if any
+/// @return Option Information
+Dictionary nvim_get_option_info(String name, Error *err)
+ FUNC_API_SINCE(7)
+{
+ return get_vimoption(name, err);
+}
+/// Sets the global value of an option.
+///
+/// @param channel_id
+/// @param name Option name
+/// @param value New option value
+/// @param[out] err Error details, if any
+void nvim_set_option(uint64_t channel_id, String name, Object value, Error *err)
+ FUNC_API_SINCE(1)
+{
+ set_option_to(channel_id, NULL, SREQ_GLOBAL, name, value, err);
+}
+
+/// Gets the global value of an option.
+///
+/// @param name Option name
+/// @param[out] err Error details, if any
+/// @return Option value (global)
+Object nvim_get_option(String name, Error *err)
+ FUNC_API_SINCE(1)
+{
+ return get_option_from(NULL, SREQ_GLOBAL, name, err);
+}
+
+/// Gets a buffer option value
+///
+/// @param buffer Buffer handle, or 0 for current buffer
+/// @param name Option name
+/// @param[out] err Error details, if any
+/// @return Option value
+Object nvim_buf_get_option(Buffer buffer, String name, Error *err)
+ FUNC_API_SINCE(1)
+{
+ buf_T *buf = find_buffer_by_handle(buffer, err);
+
+ if (!buf) {
+ return (Object)OBJECT_INIT;
+ }
+
+ return get_option_from(buf, SREQ_BUF, name, err);
+}
+
+/// Sets a buffer option value. Passing 'nil' as value deletes the option (only
+/// works if there's a global fallback)
+///
+/// @param channel_id
+/// @param buffer Buffer handle, or 0 for current buffer
+/// @param name Option name
+/// @param value Option value
+/// @param[out] err Error details, if any
+void nvim_buf_set_option(uint64_t channel_id, Buffer buffer, String name, Object value, Error *err)
+ FUNC_API_SINCE(1)
+{
+ buf_T *buf = find_buffer_by_handle(buffer, err);
+
+ if (!buf) {
+ return;
+ }
+
+ set_option_to(channel_id, buf, SREQ_BUF, name, value, err);
+}
+
+/// Gets a window option value
+///
+/// @param window Window handle, or 0 for current window
+/// @param name Option name
+/// @param[out] err Error details, if any
+/// @return Option value
+Object nvim_win_get_option(Window window, String name, Error *err)
+ FUNC_API_SINCE(1)
+{
+ win_T *win = find_window_by_handle(window, err);
+
+ if (!win) {
+ return (Object)OBJECT_INIT;
+ }
+
+ return get_option_from(win, SREQ_WIN, name, err);
+}
+
+/// Sets a window option value. Passing 'nil' as value deletes the option(only
+/// works if there's a global fallback)
+///
+/// @param channel_id
+/// @param window Window handle, or 0 for current window
+/// @param name Option name
+/// @param value Option value
+/// @param[out] err Error details, if any
+void nvim_win_set_option(uint64_t channel_id, Window window, String name, Object value, Error *err)
+ FUNC_API_SINCE(1)
+{
+ win_T *win = find_window_by_handle(window, err);
+
+ if (!win) {
+ return;
+ }
+
+ set_option_to(channel_id, win, SREQ_WIN, name, value, err);
+}
+
+/// Gets the value of a global or local(buffer, window) option.
+///
+/// @param from If `type` is `SREQ_WIN` or `SREQ_BUF`, this must be a pointer
+/// to the window or buffer.
+/// @param type One of `SREQ_GLOBAL`, `SREQ_WIN` or `SREQ_BUF`
+/// @param name The option name
+/// @param[out] err Details of an error that may have occurred
+/// @return the option value
+Object get_option_from(void *from, int type, String name, Error *err)
+{
+ Object rv = OBJECT_INIT;
+
+ if (name.size == 0) {
+ api_set_error(err, kErrorTypeValidation, "Empty option name");
+ return rv;
+ }
+
+ // Return values
+ int64_t numval;
+ char *stringval = NULL;
+ int flags = get_option_value_strict(name.data, &numval, &stringval,
+ type, from);
+
+ if (!flags) {
+ api_set_error(err, kErrorTypeValidation, "Invalid option name: '%s'",
+ name.data);
+ return rv;
+ }
+
+ if (flags & SOPT_BOOL) {
+ rv.type = kObjectTypeBoolean;
+ rv.data.boolean = numval ? true : false;
+ } else if (flags & SOPT_NUM) {
+ rv.type = kObjectTypeInteger;
+ rv.data.integer = numval;
+ } else if (flags & SOPT_STRING) {
+ if (stringval) {
+ rv.type = kObjectTypeString;
+ rv.data.string.data = stringval;
+ rv.data.string.size = strlen(stringval);
+ } else {
+ api_set_error(err, kErrorTypeException,
+ "Failed to get value for option '%s'",
+ name.data);
+ }
+ } else {
+ api_set_error(err,
+ kErrorTypeException,
+ "Unknown type for option '%s'",
+ name.data);
+ }
+
+ return rv;
+}
+
+/// Sets the value of a global or local(buffer, window) option.
+///
+/// @param to If `type` is `SREQ_WIN` or `SREQ_BUF`, this must be a pointer
+/// to the window or buffer.
+/// @param type One of `SREQ_GLOBAL`, `SREQ_WIN` or `SREQ_BUF`
+/// @param name The option name
+/// @param[out] err Details of an error that may have occurred
+void set_option_to(uint64_t channel_id, void *to, int type, String name, Object value, Error *err)
+{
+ if (name.size == 0) {
+ api_set_error(err, kErrorTypeValidation, "Empty option name");
+ return;
+ }
+
+ int flags = get_option_value_strict(name.data, NULL, NULL, type, to);
+
+ if (flags == 0) {
+ api_set_error(err, kErrorTypeValidation, "Invalid option name '%s'",
+ name.data);
+ return;
+ }
+
+ if (value.type == kObjectTypeNil) {
+ if (type == SREQ_GLOBAL) {
+ api_set_error(err, kErrorTypeException, "Cannot unset option '%s'",
+ name.data);
+ return;
+ } else if (!(flags & SOPT_GLOBAL)) {
+ api_set_error(err,
+ kErrorTypeException,
+ "Cannot unset option '%s' "
+ "because it doesn't have a global value",
+ name.data);
+ return;
+ } else {
+ unset_global_local_option(name.data, to);
+ return;
+ }
+ }
+
+ long numval = 0;
+ char *stringval = NULL;
+
+ if (flags & SOPT_BOOL) {
+ if (value.type != kObjectTypeBoolean) {
+ api_set_error(err,
+ kErrorTypeValidation,
+ "Option '%s' requires a Boolean value",
+ name.data);
+ return;
+ }
+
+ numval = value.data.boolean;
+ } else if (flags & SOPT_NUM) {
+ if (value.type != kObjectTypeInteger) {
+ api_set_error(err, kErrorTypeValidation,
+ "Option '%s' requires an integer value",
+ name.data);
+ return;
+ }
+
+ if (value.data.integer > INT_MAX || value.data.integer < INT_MIN) {
+ api_set_error(err, kErrorTypeValidation,
+ "Value for option '%s' is out of range",
+ name.data);
+ return;
+ }
+
+ numval = (int)value.data.integer;
+ } else {
+ if (value.type != kObjectTypeString) {
+ api_set_error(err, kErrorTypeValidation,
+ "Option '%s' requires a string value",
+ name.data);
+ return;
+ }
+
+ stringval = value.data.string.data;
+ }
+
+ // For global-win-local options -> setlocal
+ // For win-local options -> setglobal and setlocal (opt_flags == 0)
+ const int opt_flags = (type == SREQ_WIN && !(flags & SOPT_GLOBAL)) ? 0 :
+ (type == SREQ_GLOBAL) ? OPT_GLOBAL : OPT_LOCAL;
+
+ WITH_SCRIPT_CONTEXT(channel_id, {
+ access_option_value_for(name.data, &numval, &stringval, opt_flags, type, to, false, err);
+ });
+}
+
+static int access_option_value(char *key, long *numval, char **stringval, int opt_flags, bool get,
+ Error *err)
+{
+ if (get) {
+ return get_option_value(key, numval, stringval, opt_flags);
+ } else {
+ char *errmsg;
+ if ((errmsg = set_option_value(key, *numval, *stringval, opt_flags))) {
+ if (try_end(err)) {
+ return 0;
+ }
+
+ api_set_error(err, kErrorTypeException, "%s", errmsg);
+ }
+ return 0;
+ }
+}
+
+static int access_option_value_for(char *key, long *numval, char **stringval, int opt_flags,
+ int opt_type, void *from, bool get, Error *err)
+{
+ bool need_switch = false;
+ switchwin_T switchwin;
+ aco_save_T aco;
+ int result = 0;
+
+ try_start();
+ switch (opt_type) {
+ case SREQ_WIN:
+ need_switch = (win_T *)from != curwin;
+ if (need_switch) {
+ if (switch_win_noblock(&switchwin, (win_T *)from, win_find_tabpage((win_T *)from), true)
+ == FAIL) {
+ restore_win_noblock(&switchwin, true);
+ if (try_end(err)) {
+ return result;
+ }
+ api_set_error(err, kErrorTypeException, "Problem while switching windows");
+ return result;
+ }
+ }
+ result = access_option_value(key, numval, stringval, opt_flags, get, err);
+ if (need_switch) {
+ restore_win_noblock(&switchwin, true);
+ }
+ break;
+ case SREQ_BUF:
+ need_switch = (buf_T *)from != curbuf;
+ if (need_switch) {
+ aucmd_prepbuf(&aco, (buf_T *)from);
+ }
+ result = access_option_value(key, numval, stringval, opt_flags, get, err);
+ if (need_switch) {
+ aucmd_restbuf(&aco);
+ }
+ break;
+ case SREQ_GLOBAL:
+ result = access_option_value(key, numval, stringval, opt_flags, get, err);
+ break;
+ }
+
+ if (ERROR_SET(err)) {
+ return result;
+ }
+
+ try_end(err);
+
+ return result;
+}
diff --git a/src/nvim/api/options.h b/src/nvim/api/options.h
new file mode 100644
index 0000000000..efbfec3a6c
--- /dev/null
+++ b/src/nvim/api/options.h
@@ -0,0 +1,9 @@
+#ifndef NVIM_API_OPTIONS_H
+#define NVIM_API_OPTIONS_H
+
+#include "nvim/api/private/defs.h"
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "api/options.h.generated.h"
+#endif
+
+#endif // NVIM_API_OPTIONS_H
diff --git a/src/nvim/api/private/converter.c b/src/nvim/api/private/converter.c
index 36da6c13a9..8724ef4432 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 {
@@ -54,14 +57,20 @@ typedef struct {
const size_t len_ = (size_t)(len); \
const blob_T *const blob_ = (blob); \
kvi_push(edata->stack, STRING_OBJ(((String) { \
- .data = len_ != 0 ? xmemdup(blob_->bv_ga.ga_data, len_) : NULL, \
+ .data = len_ != 0 ? xmemdupz(blob_->bv_ga.ga_data, len_) : xstrdup(""), \
.size = len_ \
}))); \
} while (0)
#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
do { \
- TYPVAL_ENCODE_CONV_NIL(tv); \
+ ufunc_T *fp = find_func(fun); \
+ if (fp != NULL && fp->uf_cb == nlua_CFunction_func_call) { \
+ LuaRef ref = api_new_luaref(((LuaCFunctionState *)fp->uf_cb_state)->lua_callable.func_ref); \
+ kvi_push(edata->stack, LUAREF_OBJ(ref)); \
+ } else { \
+ TYPVAL_ENCODE_CONV_NIL(tv); \
+ } \
goto typval_encode_stop_converting_one_item; \
} while (0)
@@ -340,6 +349,17 @@ 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 *name =
+ (char *)register_cfunc(&nlua_CFunction_func_call, &nlua_CFunction_func_free, state);
+ tv->v_type = VAR_FUNC;
+ tv->vval.v_string = xstrdup(name);
+ break;
+ }
+
default:
abort();
}
diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h
index 396fab721d..b1e0dd364c 100644
--- a/src/nvim/api/private/defs.h
+++ b/src/nvim/api/private/defs.h
@@ -6,9 +6,10 @@
#include <string.h>
#include "nvim/func_attr.h"
+#include "nvim/lib/kvec.h"
#include "nvim/types.h"
-#define ARRAY_DICT_INIT { .size = 0, .capacity = 0, .items = NULL }
+#define ARRAY_DICT_INIT KV_INITIAL_VALUE
#define STRING_INIT { .data = NULL, .size = 0 }
#define OBJECT_INIT { .type = kObjectTypeNil }
#define ERROR_INIT { .type = kErrorTypeNone, .msg = NULL }
@@ -84,18 +85,10 @@ REMOTE_TYPE(Window);
REMOTE_TYPE(Tabpage);
typedef struct object Object;
-
-typedef struct {
- Object *items;
- size_t size, capacity;
-} Array;
+typedef kvec_t(Object) Array;
typedef struct key_value_pair KeyValuePair;
-
-typedef struct {
- KeyValuePair *items;
- size_t size, capacity;
-} Dictionary;
+typedef kvec_t(KeyValuePair) Dictionary;
typedef enum {
kObjectTypeNil = 0,
diff --git a/src/nvim/api/private/dispatch.c b/src/nvim/api/private/dispatch.c
index 8ab7743e01..d6a6fc1219 100644
--- a/src/nvim/api/private/dispatch.c
+++ b/src/nvim/api/private/dispatch.c
@@ -6,47 +6,50 @@
#include <msgpack.h>
#include <stdbool.h>
-#include "nvim/api/buffer.h"
#include "nvim/api/deprecated.h"
-#include "nvim/api/extmark.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/dispatch.h"
#include "nvim/api/private/helpers.h"
+#include "nvim/log.h"
+#include "nvim/map.h"
+#include "nvim/msgpack_rpc/helpers.h"
+#include "nvim/vim.h"
+
+// ===========================================================================
+// NEW API FILES MUST GO HERE.
+//
+// When creating a new API file, you must include it here,
+// so that the dispatcher can find the C functions that you are creating!
+// ===========================================================================
+#include "nvim/api/autocmd.h"
+#include "nvim/api/buffer.h"
+#include "nvim/api/command.h"
+#include "nvim/api/extmark.h"
+#include "nvim/api/options.h"
#include "nvim/api/tabpage.h"
#include "nvim/api/ui.h"
#include "nvim/api/vim.h"
#include "nvim/api/vimscript.h"
#include "nvim/api/win_config.h"
#include "nvim/api/window.h"
-#include "nvim/log.h"
-#include "nvim/map.h"
-#include "nvim/msgpack_rpc/helpers.h"
-#include "nvim/vim.h"
-
-static Map(String, MsgpackRpcRequestHandler) methods = MAP_INIT;
+#include "nvim/ui_client.h"
-static void msgpack_rpc_add_method_handler(String method, MsgpackRpcRequestHandler handler)
-{
- map_put(String, MsgpackRpcRequestHandler)(&methods, method, handler);
-}
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "api/private/dispatch_wrappers.generated.h"
+#endif
/// @param name API method name
/// @param name_len name size (includes terminating NUL)
MsgpackRpcRequestHandler msgpack_rpc_get_handler_for(const char *name, size_t name_len,
Error *error)
{
- String m = { .data = (char *)name, .size = name_len };
- MsgpackRpcRequestHandler rv =
- map_get(String, MsgpackRpcRequestHandler)(&methods, m);
+ int hash = msgpack_rpc_get_handler_for_hash(name, name_len);
- if (!rv.fn) {
+ if (hash < 0) {
api_set_error(error, kErrorTypeException, "Invalid method: %.*s",
- m.size > 0 ? (int)m.size : (int)sizeof("<empty>"),
- m.size > 0 ? m.data : "<empty>");
+ name_len > 0 ? (int)name_len : (int)sizeof("<empty>"),
+ name_len > 0 ? name : "<empty>");
+ return (MsgpackRpcRequestHandler){ 0 };
}
- return rv;
+ return method_handlers[hash];
}
-
-#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "api/private/dispatch_wrappers.generated.h"
-#endif
diff --git a/src/nvim/api/private/dispatch.h b/src/nvim/api/private/dispatch.h
index bad5a13934..4b7c394944 100644
--- a/src/nvim/api/private/dispatch.h
+++ b/src/nvim/api/private/dispatch.h
@@ -10,6 +10,7 @@ typedef Object (*ApiDispatchWrapper)(uint64_t channel_id,
/// The rpc_method_handlers table, used in msgpack_rpc_dispatch(), stores
/// functions of this type.
typedef struct {
+ const char *name;
ApiDispatchWrapper fn;
bool fast; // Function is safe to be executed immediately while running the
// uv loop (the loop is run very frequently due to breakcheck).
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index f9603acbda..fad75d55be 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -16,12 +16,12 @@
#include "nvim/assert.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
-#include "nvim/decoration.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/extmark.h"
#include "nvim/fileio.h"
-#include "nvim/getchar.h"
+#include "nvim/highlight_group.h"
#include "nvim/lib/kvec.h"
#include "nvim/lua/executor.h"
#include "nvim/map.h"
@@ -30,9 +30,6 @@
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/msgpack_rpc/helpers.h"
-#include "nvim/option.h"
-#include "nvim/option_defs.h"
-#include "nvim/syntax.h"
#include "nvim/ui.h"
#include "nvim/version.h"
#include "nvim/vim.h"
@@ -111,7 +108,7 @@ bool try_leave(const TryState *const tstate, Error *const err)
/// try_enter()/try_leave() pair should be used instead.
void try_start(void)
{
- ++trylevel;
+ trylevel++;
}
/// End try block, set the error message if any and return true if an error
@@ -139,10 +136,10 @@ bool try_end(Error *err)
got_int = false;
} else if (msg_list != NULL && *msg_list != NULL) {
int should_free;
- char *msg = (char *)get_exception_string(*msg_list,
- ET_ERROR,
- NULL,
- &should_free);
+ char *msg = get_exception_string(*msg_list,
+ ET_ERROR,
+ NULL,
+ &should_free);
api_set_error(err, kErrorTypeException, "%s", msg);
free_global_msglist();
@@ -261,157 +258,6 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, bool retva
return rv;
}
-/// Gets the value of a global or local(buffer, window) option.
-///
-/// @param from If `type` is `SREQ_WIN` or `SREQ_BUF`, this must be a pointer
-/// to the window or buffer.
-/// @param type One of `SREQ_GLOBAL`, `SREQ_WIN` or `SREQ_BUF`
-/// @param name The option name
-/// @param[out] err Details of an error that may have occurred
-/// @return the option value
-Object get_option_from(void *from, int type, String name, Error *err)
-{
- Object rv = OBJECT_INIT;
-
- if (name.size == 0) {
- api_set_error(err, kErrorTypeValidation, "Empty option name");
- return rv;
- }
-
- // Return values
- int64_t numval;
- char *stringval = NULL;
- int flags = get_option_value_strict(name.data, &numval, &stringval,
- type, from);
-
- if (!flags) {
- api_set_error(err, kErrorTypeValidation, "Invalid option name: '%s'",
- name.data);
- return rv;
- }
-
- if (flags & SOPT_BOOL) {
- rv.type = kObjectTypeBoolean;
- rv.data.boolean = numval ? true : false;
- } else if (flags & SOPT_NUM) {
- rv.type = kObjectTypeInteger;
- rv.data.integer = numval;
- } else if (flags & SOPT_STRING) {
- if (stringval) {
- rv.type = kObjectTypeString;
- rv.data.string.data = stringval;
- rv.data.string.size = strlen(stringval);
- } else {
- api_set_error(err, kErrorTypeException,
- "Failed to get value for option '%s'",
- name.data);
- }
- } else {
- api_set_error(err,
- kErrorTypeException,
- "Unknown type for option '%s'",
- name.data);
- }
-
- return rv;
-}
-
-/// Sets the value of a global or local(buffer, window) option.
-///
-/// @param to If `type` is `SREQ_WIN` or `SREQ_BUF`, this must be a pointer
-/// to the window or buffer.
-/// @param type One of `SREQ_GLOBAL`, `SREQ_WIN` or `SREQ_BUF`
-/// @param name The option name
-/// @param[out] err Details of an error that may have occurred
-void set_option_to(uint64_t channel_id, void *to, int type, String name, Object value, Error *err)
-{
- if (name.size == 0) {
- api_set_error(err, kErrorTypeValidation, "Empty option name");
- return;
- }
-
- int flags = get_option_value_strict(name.data, NULL, NULL, type, to);
-
- if (flags == 0) {
- api_set_error(err, kErrorTypeValidation, "Invalid option name '%s'",
- name.data);
- return;
- }
-
- if (value.type == kObjectTypeNil) {
- if (type == SREQ_GLOBAL) {
- api_set_error(err, kErrorTypeException, "Cannot unset option '%s'",
- name.data);
- return;
- } else if (!(flags & SOPT_GLOBAL)) {
- api_set_error(err,
- kErrorTypeException,
- "Cannot unset option '%s' "
- "because it doesn't have a global value",
- name.data);
- return;
- } else {
- unset_global_local_option(name.data, to);
- return;
- }
- }
-
- int numval = 0;
- char *stringval = NULL;
-
- if (flags & SOPT_BOOL) {
- if (value.type != kObjectTypeBoolean) {
- api_set_error(err,
- kErrorTypeValidation,
- "Option '%s' requires a Boolean value",
- name.data);
- return;
- }
-
- numval = value.data.boolean;
- } else if (flags & SOPT_NUM) {
- if (value.type != kObjectTypeInteger) {
- api_set_error(err, kErrorTypeValidation,
- "Option '%s' requires an integer value",
- name.data);
- return;
- }
-
- if (value.data.integer > INT_MAX || value.data.integer < INT_MIN) {
- api_set_error(err, kErrorTypeValidation,
- "Value for option '%s' is out of range",
- name.data);
- return;
- }
-
- numval = (int)value.data.integer;
- } else {
- if (value.type != kObjectTypeString) {
- api_set_error(err, kErrorTypeValidation,
- "Option '%s' requires a string value",
- name.data);
- return;
- }
-
- stringval = value.data.string.data;
- }
-
- const sctx_T save_current_sctx = current_sctx;
- current_sctx.sc_sid =
- channel_id == LUA_INTERNAL_CALL ? SID_LUA : SID_API_CLIENT;
- current_sctx.sc_lnum = 0;
- current_channel_id = channel_id;
-
- const int opt_flags = (type == SREQ_WIN && !(flags & SOPT_GLOBAL))
- ? 0 : (type == SREQ_GLOBAL)
- ? OPT_GLOBAL : OPT_LOCAL;
- set_option_value_for(name.data, numval, stringval,
- opt_flags, type, to, err);
-
- current_sctx = save_current_sctx;
-}
-
-
buf_T *find_buffer_by_handle(Buffer buffer, Error *err)
{
if (buffer == 0) {
@@ -493,6 +339,16 @@ String cstr_to_string(const char *str)
};
}
+/// Copies a String to an allocated, NUL-terminated C string.
+///
+/// @param str the String to copy
+/// @return the resulting C string
+char *string_to_cstr(String str)
+ FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ return xstrndup(str.data, str.size);
+}
+
/// Copies buffer to an allocated String.
/// The resulting string is also NUL-terminated, to facilitate interoperating
/// with code using C strings.
@@ -584,145 +440,6 @@ Array string_to_array(const String input, bool crlf)
return ret;
}
-/// Set, tweak, or remove a mapping in a mode. Acts as the implementation for
-/// functions like @ref nvim_buf_set_keymap.
-///
-/// Arguments are handled like @ref nvim_set_keymap unless noted.
-/// @param buffer Buffer handle for a specific buffer, or 0 for the current
-/// buffer, or -1 to signify global behavior ("all buffers")
-/// @param is_unmap When true, removes the mapping that matches {lhs}.
-void modify_keymap(Buffer buffer, bool is_unmap, String mode, String lhs, String rhs,
- Dict(keymap) *opts, Error *err)
-{
- LuaRef lua_funcref = LUA_NOREF;
- bool global = (buffer == -1);
- if (global) {
- buffer = 0;
- }
- buf_T *target_buf = find_buffer_by_handle(buffer, err);
-
- if (!target_buf) {
- return;
- }
-
- if (opts != NULL && opts->callback.type == kObjectTypeLuaRef) {
- lua_funcref = opts->callback.data.luaref;
- opts->callback.data.luaref = LUA_NOREF;
- }
- MapArguments parsed_args = MAP_ARGUMENTS_INIT;
- if (opts) {
-#define KEY_TO_BOOL(name) \
- parsed_args.name = api_object_to_bool(opts->name, #name, false, err); \
- if (ERROR_SET(err)) { \
- goto fail_and_free; \
- }
-
- KEY_TO_BOOL(nowait);
- KEY_TO_BOOL(noremap);
- KEY_TO_BOOL(silent);
- KEY_TO_BOOL(script);
- KEY_TO_BOOL(expr);
- KEY_TO_BOOL(unique);
-#undef KEY_TO_BOOL
- }
- parsed_args.buffer = !global;
-
- set_maparg_lhs_rhs((char_u *)lhs.data, lhs.size,
- (char_u *)rhs.data, rhs.size, lua_funcref,
- CPO_TO_CPO_FLAGS, &parsed_args);
- if (opts != NULL && opts->desc.type == kObjectTypeString) {
- parsed_args.desc = xstrdup(opts->desc.data.string.data);
- } else {
- parsed_args.desc = NULL;
- }
- if (parsed_args.lhs_len > MAXMAPLEN) {
- api_set_error(err, kErrorTypeValidation, "LHS exceeds maximum map length: %s", lhs.data);
- goto fail_and_free;
- }
-
- if (mode.size > 1) {
- api_set_error(err, kErrorTypeValidation, "Shortname is too long: %s", mode.data);
- goto fail_and_free;
- }
- int mode_val; // integer value of the mapping mode, to be passed to do_map()
- char_u *p = (char_u *)((mode.size) ? mode.data : "m");
- if (STRNCMP(p, "!", 2) == 0) {
- mode_val = get_map_mode(&p, true); // mapmode-ic
- } else {
- mode_val = get_map_mode(&p, false);
- if ((mode_val == VISUAL + SELECTMODE + NORMAL + OP_PENDING)
- && mode.size > 0) {
- // get_map_mode() treats unrecognized mode shortnames as ":map".
- // This is an error unless the given shortname was empty string "".
- api_set_error(err, kErrorTypeValidation, "Invalid mode shortname: \"%s\"", (char *)p);
- goto fail_and_free;
- }
- }
-
- if (parsed_args.lhs_len == 0) {
- api_set_error(err, kErrorTypeValidation, "Invalid (empty) LHS");
- goto fail_and_free;
- }
-
- bool is_noremap = parsed_args.noremap;
- assert(!(is_unmap && is_noremap));
-
- if (!is_unmap && lua_funcref == LUA_NOREF
- && (parsed_args.rhs_len == 0 && !parsed_args.rhs_is_noop)) {
- if (rhs.size == 0) { // assume that the user wants RHS to be a <Nop>
- parsed_args.rhs_is_noop = true;
- } else {
- // the given RHS was nonempty and not a <Nop>, but was parsed as if it
- // were empty?
- assert(false && "Failed to parse nonempty RHS!");
- api_set_error(err, kErrorTypeValidation, "Parsing of nonempty RHS failed: %s", rhs.data);
- goto fail_and_free;
- }
- } else if (is_unmap && (parsed_args.rhs_len || parsed_args.rhs_lua != LUA_NOREF)) {
- if (parsed_args.rhs_len) {
- api_set_error(err, kErrorTypeValidation,
- "Gave nonempty RHS in unmap command: %s", parsed_args.rhs);
- } else {
- api_set_error(err, kErrorTypeValidation, "Gave nonempty RHS for unmap");
- }
- goto fail_and_free;
- }
-
- // buf_do_map() reads noremap/unmap as its own argument.
- int maptype_val = 0;
- if (is_unmap) {
- maptype_val = 1;
- } else if (is_noremap) {
- maptype_val = 2;
- }
-
- switch (buf_do_map(maptype_val, &parsed_args, mode_val, 0, target_buf)) {
- case 0:
- break;
- case 1:
- api_set_error(err, kErrorTypeException, (char *)e_invarg, 0);
- goto fail_and_free;
- case 2:
- api_set_error(err, kErrorTypeException, (char *)e_nomap, 0);
- goto fail_and_free;
- case 5:
- api_set_error(err, kErrorTypeException,
- "E227: mapping already exists for %s", parsed_args.lhs);
- goto fail_and_free;
- default:
- assert(false && "Unrecognized return code!");
- goto fail_and_free;
- } // switch
-
- parsed_args.rhs_lua = LUA_NOREF; // don't clear ref on success
-fail_and_free:
- NLUA_CLEAR_REF(parsed_args.rhs_lua);
- xfree(parsed_args.rhs);
- xfree(parsed_args.orig_rhs);
- XFREE_CLEAR(parsed_args.desc);
- return;
-}
-
/// Collects `n` buffer lines into array `l`, optionally replacing newlines
/// with NUL.
///
@@ -759,6 +476,52 @@ bool buf_collect_lines(buf_T *buf, size_t n, int64_t start, bool replace_nl, Arr
return true;
}
+/// Returns a substring of a buffer line
+///
+/// @param buf Buffer handle
+/// @param lnum Line number (1-based)
+/// @param start_col Starting byte offset into line (0-based)
+/// @param end_col Ending byte offset into line (0-based, exclusive)
+/// @param replace_nl Replace newlines ('\n') with null ('\0')
+/// @param err Error object
+/// @return The text between start_col and end_col on line lnum of buffer buf
+String buf_get_text(buf_T *buf, int64_t lnum, int64_t start_col, int64_t end_col, bool replace_nl,
+ Error *err)
+{
+ String rv = STRING_INIT;
+
+ if (lnum >= MAXLNUM) {
+ api_set_error(err, kErrorTypeValidation, "Line index is too high");
+ return rv;
+ }
+
+ const char *bufstr = (char *)ml_get_buf(buf, (linenr_T)lnum, false);
+ size_t line_length = strlen(bufstr);
+
+ start_col = start_col < 0 ? (int64_t)line_length + start_col + 1 : start_col;
+ end_col = end_col < 0 ? (int64_t)line_length + end_col + 1 : end_col;
+
+ if (start_col >= MAXCOL || end_col >= MAXCOL) {
+ api_set_error(err, kErrorTypeValidation, "Column index is too high");
+ return rv;
+ }
+
+ if (start_col > end_col) {
+ api_set_error(err, kErrorTypeValidation, "start_col must be less than end_col");
+ return rv;
+ }
+
+ if ((size_t)start_col >= line_length) {
+ return rv;
+ }
+
+ rv = cstrn_to_string(&bufstr[start_col], (size_t)(end_col - start_col));
+ if (replace_nl) {
+ strchrsub(rv.data, '\n', '\0');
+ }
+
+ return rv;
+}
void api_free_string(String value)
{
@@ -769,6 +532,29 @@ void api_free_string(String value)
xfree(value.data);
}
+Array arena_array(Arena *arena, size_t max_size)
+{
+ Array arr = ARRAY_DICT_INIT;
+ kv_fixsize_arena(arena, arr, max_size);
+ return arr;
+}
+
+Dictionary arena_dict(Arena *arena, size_t max_size)
+{
+ Dictionary dict = ARRAY_DICT_INIT;
+ kv_fixsize_arena(arena, dict, max_size);
+ return dict;
+}
+
+String arena_string(Arena *arena, String str)
+{
+ if (str.size) {
+ return cbuf_as_string(arena_memdupz(arena, str.data, str.size), str.size);
+ } else {
+ return (String)STRING_INIT;
+ }
+}
+
void api_free_object(Object value)
{
switch (value.type) {
@@ -987,63 +773,6 @@ Object copy_object(Object obj)
}
}
-static void set_option_value_for(char *key, int numval, char *stringval, int opt_flags,
- int opt_type, void *from, Error *err)
-{
- win_T *save_curwin = NULL;
- tabpage_T *save_curtab = NULL;
- aco_save_T aco;
-
- try_start();
- switch (opt_type)
- {
- case SREQ_WIN:
- if (switch_win_noblock(&save_curwin, &save_curtab, (win_T *)from,
- win_find_tabpage((win_T *)from), true)
- == FAIL) {
- restore_win_noblock(save_curwin, save_curtab, true);
- if (try_end(err)) {
- return;
- }
- api_set_error(err,
- kErrorTypeException,
- "Problem while switching windows");
- return;
- }
- set_option_value_err(key, numval, stringval, opt_flags, err);
- restore_win_noblock(save_curwin, save_curtab, true);
- break;
- case SREQ_BUF:
- aucmd_prepbuf(&aco, (buf_T *)from);
- set_option_value_err(key, numval, stringval, opt_flags, err);
- aucmd_restbuf(&aco);
- break;
- case SREQ_GLOBAL:
- set_option_value_err(key, numval, stringval, opt_flags, err);
- break;
- }
-
- if (ERROR_SET(err)) {
- return;
- }
-
- try_end(err);
-}
-
-
-static void set_option_value_err(char *key, int numval, char *stringval, int opt_flags, Error *err)
-{
- char *errmsg;
-
- if ((errmsg = set_option_value(key, numval, stringval, opt_flags))) {
- if (try_end(err)) {
- return;
- }
-
- api_set_error(err, kErrorTypeException, "%s", errmsg);
- }
-}
-
void api_set_error(Error *err, ErrorType errType, const char *format, ...)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PRINTF(3, 4)
{
@@ -1064,171 +793,6 @@ void api_set_error(Error *err, ErrorType errType, const char *format, ...)
err->type = errType;
}
-/// Get an array containing dictionaries describing mappings
-/// based on mode and buffer id
-///
-/// @param mode The abbreviation for the mode
-/// @param buf The buffer to get the mapping array. NULL for global
-/// @param from_lua Whether it is called from internal lua api.
-/// @returns Array of maparg()-like dictionaries describing mappings
-ArrayOf(Dictionary) keymap_array(String mode, buf_T *buf, bool from_lua)
-{
- Array mappings = ARRAY_DICT_INIT;
- dict_T *const dict = tv_dict_alloc();
-
- // Convert the string mode to the integer mode
- // that is stored within each mapblock
- char_u *p = (char_u *)mode.data;
- int int_mode = get_map_mode(&p, 0);
-
- // Determine the desired buffer value
- long buffer_value = (buf == NULL) ? 0 : buf->handle;
-
- for (int i = 0; i < MAX_MAPHASH; i++) {
- for (const mapblock_T *current_maphash = get_maphash(i, buf);
- current_maphash;
- current_maphash = current_maphash->m_next) {
- // Check for correct mode
- if (int_mode & current_maphash->m_mode) {
- mapblock_fill_dict(dict, current_maphash, buffer_value, false);
- Object api_dict = vim_to_object((typval_T[]) { { .v_type = VAR_DICT,
- .vval.v_dict = dict } });
- if (from_lua) {
- Dictionary d = api_dict.data.dictionary;
- for (size_t j = 0; j < d.size; j++) {
- if (strequal("callback", d.items[j].key.data)) {
- d.items[j].value.type = kObjectTypeLuaRef;
- d.items[j].value.data.luaref = api_new_luaref((LuaRef)d.items[j].value.data.integer);
- break;
- }
- }
- }
- ADD(mappings, api_dict);
- tv_dict_clear(dict);
- }
- }
- }
- tv_dict_free(dict);
-
- return mappings;
-}
-
-/// Gets the line and column of an extmark.
-///
-/// Extmarks may be queried by position, name or even special names
-/// in the future such as "cursor".
-///
-/// @param[out] lnum extmark line
-/// @param[out] colnr extmark column
-///
-/// @return true if the extmark was found, else false
-bool extmark_get_index_from_obj(buf_T *buf, Integer ns_id, Object obj, int
- *row, colnr_T *col, Error *err)
-{
- // Check if it is mark id
- if (obj.type == kObjectTypeInteger) {
- Integer id = obj.data.integer;
- if (id == 0) {
- *row = 0;
- *col = 0;
- return true;
- } else if (id == -1) {
- *row = MAXLNUM;
- *col = MAXCOL;
- return true;
- } else if (id < 0) {
- api_set_error(err, kErrorTypeValidation, "Mark id must be positive");
- return false;
- }
-
- ExtmarkInfo extmark = extmark_from_id(buf, (uint64_t)ns_id, (uint64_t)id);
- if (extmark.row >= 0) {
- *row = extmark.row;
- *col = extmark.col;
- return true;
- } else {
- api_set_error(err, kErrorTypeValidation, "No mark with requested id");
- return false;
- }
-
- // Check if it is a position
- } else if (obj.type == kObjectTypeArray) {
- Array pos = obj.data.array;
- if (pos.size != 2
- || pos.items[0].type != kObjectTypeInteger
- || pos.items[1].type != kObjectTypeInteger) {
- api_set_error(err, kErrorTypeValidation,
- "Position must have 2 integer elements");
- return false;
- }
- Integer pos_row = pos.items[0].data.integer;
- Integer pos_col = pos.items[1].data.integer;
- *row = (int)(pos_row >= 0 ? pos_row : MAXLNUM);
- *col = (colnr_T)(pos_col >= 0 ? pos_col : MAXCOL);
- return true;
- } else {
- api_set_error(err, kErrorTypeValidation,
- "Position must be a mark id Integer or position Array");
- return false;
- }
-}
-
-VirtText parse_virt_text(Array chunks, Error *err, int *width)
-{
- VirtText virt_text = KV_INITIAL_VALUE;
- int w = 0;
- for (size_t i = 0; i < chunks.size; i++) {
- if (chunks.items[i].type != kObjectTypeArray) {
- api_set_error(err, kErrorTypeValidation, "Chunk is not an array");
- goto free_exit;
- }
- Array chunk = chunks.items[i].data.array;
- if (chunk.size == 0 || chunk.size > 2
- || chunk.items[0].type != kObjectTypeString) {
- api_set_error(err, kErrorTypeValidation,
- "Chunk is not an array with one or two strings");
- goto free_exit;
- }
-
- String str = chunk.items[0].data.string;
-
- int hl_id = 0;
- if (chunk.size == 2) {
- Object hl = chunk.items[1];
- if (hl.type == kObjectTypeArray) {
- Array arr = hl.data.array;
- for (size_t j = 0; j < arr.size; j++) {
- hl_id = object_to_hl_id(arr.items[j], "virt_text highlight", err);
- if (ERROR_SET(err)) {
- goto free_exit;
- }
- if (j < arr.size-1) {
- kv_push(virt_text, ((VirtTextChunk){ .text = NULL,
- .hl_id = hl_id }));
- }
- }
- } else {
- hl_id = object_to_hl_id(hl, "virt_text highlight", err);
- if (ERROR_SET(err)) {
- goto free_exit;
- }
- }
- }
-
- char *text = transstr(str.size > 0 ? str.data : "", false); // allocates
- w += (int)mb_string2cells((char_u *)text);
-
- kv_push(virt_text, ((VirtTextChunk){ .text = text, .hl_id = hl_id }));
- }
-
- *width = w;
- return virt_text;
-
-free_exit:
- clear_virttext(&virt_text);
- return virt_text;
-}
-
/// Force obj to bool.
/// If it fails, returns false and sets err
/// @param obj The object to coerce to a boolean
@@ -1253,7 +817,7 @@ int object_to_hl_id(Object obj, const char *what, Error *err)
{
if (obj.type == kObjectTypeString) {
String str = obj.data.string;
- return str.size ? syn_check_group(str.data, (int)str.size) : 0;
+ return str.size ? syn_check_group(str.data, str.size) : 0;
} else if (obj.type == kObjectTypeInteger) {
return MAX((int)obj.data.integer, 0);
} else {
@@ -1287,7 +851,7 @@ HlMessage parse_hl_msg(Array chunks, Error *err)
String hl = chunk.items[1].data.string;
if (hl.size > 0) {
// TODO(bfredl): use object_to_hl_id and allow integer
- int hl_id = syn_check_group(hl.data, (int)hl.size);
+ int hl_id = syn_check_group(hl.data, hl.size);
attr = hl_id > 0 ? syn_id2attr(hl_id) : 0;
}
}
@@ -1297,8 +861,8 @@ HlMessage parse_hl_msg(Array chunks, Error *err)
return hl_msg;
free_exit:
- clear_hl_msg(&hl_msg);
- return hl_msg;
+ hl_msg_free(hl_msg);
+ return (HlMessage)KV_INITIAL_VALUE;
}
bool api_dict_to_keydict(void *rv, field_hash hashy, Dictionary dict, Error *err)
@@ -1350,8 +914,9 @@ bool set_mark(buf_T *buf, String name, Integer line, Integer col, Error *err)
return res;
}
}
- pos_T pos = { line, (int)col, (int)col };
- res = setmark_pos(*name.data, &pos, buf->handle);
+ assert(INT32_MIN <= line && line <= INT32_MAX);
+ pos_T pos = { (linenr_T)line, (int)col, (int)col };
+ res = setmark_pos(*name.data, &pos, buf->handle, NULL);
if (!res) {
if (deleting) {
api_set_error(err, kErrorTypeException,
@@ -1365,199 +930,43 @@ bool set_mark(buf_T *buf, String name, Integer line, Integer col, Error *err)
}
/// Get default statusline highlight for window
-const char *get_default_stl_hl(win_T *wp)
+const char *get_default_stl_hl(win_T *wp, bool use_winbar)
{
if (wp == NULL) {
return "TabLineFill";
- } else if (wp == curwin) {
- return "StatusLine";
+ } else if (use_winbar) {
+ return (wp == curwin) ? "WinBar" : "WinBarNC";
} else {
- return "StatusLineNC";
+ return (wp == curwin) ? "StatusLine" : "StatusLineNC";
}
}
-void add_user_command(String name, Object command, Dict(user_command) *opts, int flags, Error *err)
+int find_sid(uint64_t channel_id)
{
- uint32_t argt = 0;
- long def = -1;
- cmd_addr_T addr_type_arg = ADDR_NONE;
- int compl = EXPAND_NOTHING;
- char *compl_arg = NULL;
- char *rep = NULL;
- 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;
- }
-
- if (opts->nargs.type == kObjectTypeInteger) {
- switch (opts->nargs.data.integer) {
- case 0:
- // Default value, nothing to do
- break;
- case 1:
- argt |= EX_EXTRA | EX_NOSPC | EX_NEEDARG;
- break;
- default:
- api_set_error(err, kErrorTypeValidation, "Invalid value for 'nargs'");
- goto err;
- }
- } else if (opts->nargs.type == kObjectTypeString) {
- if (opts->nargs.data.string.size > 1) {
- api_set_error(err, kErrorTypeValidation, "Invalid value for 'nargs'");
- goto err;
- }
-
- switch (opts->nargs.data.string.data[0]) {
- case '*':
- argt |= EX_EXTRA;
- break;
- case '?':
- argt |= EX_EXTRA | EX_NOSPC;
- break;
- case '+':
- argt |= EX_EXTRA | EX_NEEDARG;
- break;
- default:
- api_set_error(err, kErrorTypeValidation, "Invalid value for 'nargs'");
- goto err;
- }
- } else if (HAS_KEY(opts->nargs)) {
- api_set_error(err, kErrorTypeValidation, "Invalid value for 'nargs'");
- goto err;
- }
-
- if (HAS_KEY(opts->complete) && !argt) {
- api_set_error(err, kErrorTypeValidation, "'complete' used without 'nargs'");
- goto err;
- }
-
- if (opts->range.type == kObjectTypeBoolean) {
- if (opts->range.data.boolean) {
- argt |= EX_RANGE;
- addr_type_arg = ADDR_LINES;
- }
- } else if (opts->range.type == kObjectTypeString) {
- if (opts->range.data.string.data[0] == '%' && opts->range.data.string.size == 1) {
- argt |= EX_RANGE | EX_DFLALL;
- addr_type_arg = ADDR_LINES;
- } else {
- api_set_error(err, kErrorTypeValidation, "Invalid value for 'range'");
- goto err;
- }
- } else if (opts->range.type == kObjectTypeInteger) {
- argt |= EX_RANGE | EX_ZEROR;
- def = opts->range.data.integer;
- addr_type_arg = ADDR_LINES;
- } else if (HAS_KEY(opts->range)) {
- api_set_error(err, kErrorTypeValidation, "Invalid value for 'range'");
- goto err;
- }
-
- if (opts->count.type == kObjectTypeBoolean) {
- if (opts->count.data.boolean) {
- argt |= EX_COUNT | EX_ZEROR | EX_RANGE;
- addr_type_arg = ADDR_OTHER;
- def = 0;
- }
- } else if (opts->count.type == kObjectTypeInteger) {
- argt |= EX_COUNT | EX_ZEROR | EX_RANGE;
- addr_type_arg = ADDR_OTHER;
- def = opts->count.data.integer;
- } else if (HAS_KEY(opts->count)) {
- api_set_error(err, kErrorTypeValidation, "Invalid value for 'count'");
- goto err;
- }
-
- if (opts->addr.type == kObjectTypeString) {
- if (parse_addr_type_arg((char_u *)opts->addr.data.string.data, (int)opts->addr.data.string.size,
- &addr_type_arg) != OK) {
- api_set_error(err, kErrorTypeValidation, "Invalid value for 'addr'");
- goto err;
- }
-
- if (addr_type_arg != ADDR_LINES) {
- argt |= EX_ZEROR;
- }
- } else if (HAS_KEY(opts->addr)) {
- api_set_error(err, kErrorTypeValidation, "Invalid value for 'addr'");
- goto err;
- }
-
- if (api_object_to_bool(opts->bang, "bang", false, err)) {
- argt |= EX_BANG;
- } else if (ERROR_SET(err)) {
- goto err;
- }
-
- if (api_object_to_bool(opts->bar, "bar", false, err)) {
- argt |= EX_TRLBAR;
- } else if (ERROR_SET(err)) {
- goto err;
- }
-
-
- if (api_object_to_bool(opts->register_, "register", false, err)) {
- argt |= EX_REGSTR;
- } else if (ERROR_SET(err)) {
- goto err;
- }
-
- bool force = api_object_to_bool(opts->force, "force", true, err);
- if (ERROR_SET(err)) {
- goto err;
- }
-
- if (opts->complete.type == kObjectTypeLuaRef) {
- compl = EXPAND_USER_LUA;
- compl_luaref = api_new_luaref(opts->complete.data.luaref);
- } else if (opts->complete.type == kObjectTypeString) {
- if (parse_compl_arg((char_u *)opts->complete.data.string.data,
- (int)opts->complete.data.string.size, &compl, &argt,
- (char_u **)&compl_arg) != OK) {
- api_set_error(err, kErrorTypeValidation, "Invalid value for 'complete'");
- goto err;
- }
- } else if (HAS_KEY(opts->complete)) {
- api_set_error(err, kErrorTypeValidation, "Invalid value for 'complete'");
- goto err;
- }
-
- switch (command.type) {
- case kObjectTypeLuaRef:
- luaref = api_new_luaref(command.data.luaref);
- if (opts->desc.type == kObjectTypeString) {
- rep = opts->desc.data.string.data;
- } else {
- snprintf((char *)IObuff, IOSIZE, "<Lua function %d>", luaref);
- rep = (char *)IObuff;
- }
- break;
- case kObjectTypeString:
- rep = command.data.string.data;
- break;
+ switch (channel_id) {
+ case VIML_INTERNAL_CALL:
+ // TODO(autocmd): Figure out what this should be
+ // return SID_API_CLIENT;
+ case LUA_INTERNAL_CALL:
+ return SID_LUA;
default:
- api_set_error(err, kErrorTypeValidation, "'command' must be a string or Lua function");
- goto err;
- }
-
- if (uc_add_command((char_u *)name.data, name.size, (char_u *)rep, argt, def, flags,
- compl, (char_u *)compl_arg, compl_luaref, addr_type_arg, luaref,
- force) != OK) {
- api_set_error(err, kErrorTypeException, "Failed to create user command");
- goto err;
+ return SID_API_CLIENT;
}
+}
- return;
-
-err:
- NLUA_CLEAR_REF(luaref);
- NLUA_CLEAR_REF(compl_luaref);
+/// Sets sctx for API calls.
+///
+/// @param channel_id api clients id. Used to determine if it's a internal
+/// call or a rpc call.
+/// @return returns previous value of current_sctx. To be used
+/// to be used for restoring sctx to previous state.
+sctx_T api_set_sctx(uint64_t channel_id)
+{
+ sctx_T old_current_sctx = current_sctx;
+ if (channel_id != VIML_INTERNAL_CALL) {
+ current_sctx.sc_sid =
+ channel_id == LUA_INTERNAL_CALL ? SID_LUA : SID_API_CLIENT;
+ current_sctx.sc_lnum = 0;
+ }
+ return old_current_sctx;
}
diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h
index 6d0aec9c90..a4348d8b44 100644
--- a/src/nvim/api/private/helpers.h
+++ b/src/nvim/api/private/helpers.h
@@ -63,17 +63,31 @@
#define PUT(dict, k, v) \
kv_push(dict, ((KeyValuePair) { .key = cstr_to_string(k), .value = v }))
+#define PUT_C(dict, k, v) \
+ kv_push_c(dict, ((KeyValuePair) { .key = cstr_as_string(k), .value = v }))
+
#define PUT_BOOL(dict, name, condition) PUT(dict, name, BOOLEAN_OBJ(condition));
#define ADD(array, item) \
kv_push(array, item)
+#define ADD_C(array, item) \
+ kv_push_c(array, item)
+
#define FIXED_TEMP_ARRAY(name, fixsize) \
Array name = ARRAY_DICT_INIT; \
Object name##__items[fixsize]; \
name.size = fixsize; \
name.items = name##__items; \
+#define MAXSIZE_TEMP_ARRAY(name, maxsize) \
+ Array name = ARRAY_DICT_INIT; \
+ Object name##__items[maxsize]; \
+ name.capacity = maxsize; \
+ name.items = name##__items; \
+
+#define cbuf_as_string(d, s) ((String) { .data = d, .size = s })
+
#define STATIC_CSTR_AS_STRING(s) ((String) { .data = s, .size = sizeof(s) - 1 })
/// Create a new String instance, putting data in allocated memory
@@ -138,10 +152,28 @@ typedef struct {
msg_list = saved_msg_list; /* Restore the exception context. */ \
} while (0)
+// Useful macro for executing some `code` for each item in an array.
+#define FOREACH_ITEM(a, __foreach_item, code) \
+ for (size_t (__foreach_item##_index) = 0; (__foreach_item##_index) < (a).size; \
+ (__foreach_item##_index)++) { \
+ Object __foreach_item = (a).items[__foreach_item##_index]; \
+ code; \
+ }
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/private/helpers.h.generated.h"
# include "keysets.h.generated.h"
#endif
+#define WITH_SCRIPT_CONTEXT(channel_id, code) \
+ do { \
+ const sctx_T save_current_sctx = current_sctx; \
+ current_sctx.sc_sid = \
+ (channel_id) == LUA_INTERNAL_CALL ? SID_LUA : SID_API_CLIENT; \
+ current_sctx.sc_lnum = 0; \
+ current_channel_id = channel_id; \
+ code; \
+ current_sctx = save_current_sctx; \
+ } while (0);
#endif // NVIM_API_PRIVATE_HELPERS_H
diff --git a/src/nvim/api/tabpage.c b/src/nvim/api/tabpage.c
index 14b6be8eeb..b81fc3b7d7 100644
--- a/src/nvim/api/tabpage.c
+++ b/src/nvim/api/tabpage.c
@@ -102,11 +102,10 @@ void nvim_tabpage_del_var(Tabpage tabpage, String name, Error *err)
Window nvim_tabpage_get_win(Tabpage tabpage, Error *err)
FUNC_API_SINCE(1)
{
- Window rv = 0;
tabpage_T *tab = find_tab_by_handle(tabpage, err);
if (!tab || !valid_tabpage(tab)) {
- return rv;
+ return 0;
}
if (tab == curtab) {
@@ -130,11 +129,10 @@ Window nvim_tabpage_get_win(Tabpage tabpage, Error *err)
Integer nvim_tabpage_get_number(Tabpage tabpage, Error *err)
FUNC_API_SINCE(1)
{
- Integer rv = 0;
tabpage_T *tab = find_tab_by_handle(tabpage, err);
if (!tab) {
- return rv;
+ return 0;
}
return tabpage_index(tab);
@@ -152,4 +150,3 @@ Boolean nvim_tabpage_is_valid(Tabpage tabpage)
api_clear_error(&stub);
return ret;
}
-
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c
index d86aecc318..54ce838b9b 100644
--- a/src/nvim/api/ui.c
+++ b/src/nvim/api/ui.c
@@ -9,25 +9,46 @@
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/ui.h"
+#include "nvim/channel.h"
#include "nvim/cursor_shape.h"
#include "nvim/highlight.h"
#include "nvim/map.h"
#include "nvim/memory.h"
#include "nvim/msgpack_rpc/channel.h"
+#include "nvim/msgpack_rpc/helpers.h"
+#include "nvim/option.h"
#include "nvim/popupmnu.h"
#include "nvim/screen.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
#include "nvim/window.h"
-#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "api/ui.c.generated.h"
-# include "ui_events_remote.generated.h"
-#endif
-
typedef struct {
uint64_t channel_id;
- Array buffer;
+
+#define UI_BUF_SIZE 4096 ///< total buffer size for pending msgpack data.
+ /// guranteed size available for each new event (so packing of simple events
+ /// and the header of grid_line will never fail)
+#define EVENT_BUF_SIZE 256
+ char buf[UI_BUF_SIZE]; ///< buffer of packed but not yet sent msgpack data
+ char *buf_wptr; ///< write head of buffer
+ const char *cur_event; ///< name of current event (might get multiple arglists)
+ Array call_buf; ///< buffer for constructing a single arg list (max 16 elements!)
+
+ // state for write_cb, while packing a single arglist to msgpack. This
+ // might fail due to buffer overflow.
+ size_t pack_totlen;
+ bool buf_overflow;
+ char *temp_buf;
+
+ // We start packing the two outermost msgpack arrays before knowing the total
+ // number of elements. Thus track the location where array size will need
+ // to be written in the msgpack buffer, once the specifc array is finished.
+ char *nevents_pos;
+ char *ncalls_pos;
+ uint32_t nevents; ///< number of distinct events (top-level args to "redraw"
+ uint32_t ncalls; ///< number of calls made to the current event (plus one for the name!)
+ bool flushed_events; ///< events where sent to client without "flush" event
int hl_id; // Current highlight for legacy put event.
Integer cursor_row, cursor_col; // Intended visible cursor position.
@@ -37,8 +58,76 @@ typedef struct {
bool wildmenu_active;
} UIData;
+#define BUF_POS(data) ((size_t)((data)->buf_wptr - (data)->buf))
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "api/ui.c.generated.h"
+# include "ui_events_remote.generated.h"
+#endif
+
static PMap(uint64_t) connected_uis = MAP_INIT;
+#define mpack_w(b, byte) *(*b)++ = (char)(byte);
+static void mpack_w2(char **b, uint32_t v)
+{
+ *(*b)++ = (char)((v >> 8) & 0xff);
+ *(*b)++ = (char)(v & 0xff);
+}
+
+static void mpack_w4(char **b, uint32_t v)
+{
+ *(*b)++ = (char)((v >> 24) & 0xff);
+ *(*b)++ = (char)((v >> 16) & 0xff);
+ *(*b)++ = (char)((v >> 8) & 0xff);
+ *(*b)++ = (char)(v & 0xff);
+}
+
+static void mpack_uint(char **buf, uint32_t val)
+{
+ if (val > 0xffff) {
+ mpack_w(buf, 0xce);
+ mpack_w4(buf, val);
+ } else if (val > 0xff) {
+ mpack_w(buf, 0xcd);
+ mpack_w2(buf, val);
+ } else if (val > 0x7f) {
+ mpack_w(buf, 0xcc);
+ mpack_w(buf, val);
+ } else {
+ mpack_w(buf, val);
+ }
+}
+
+static void mpack_array(char **buf, uint32_t len)
+{
+ if (len < 0x10) {
+ mpack_w(buf, 0x90 | len);
+ } else if (len < 0x10000) {
+ mpack_w(buf, 0xdc);
+ mpack_w2(buf, len);
+ } else {
+ mpack_w(buf, 0xdd);
+ mpack_w4(buf, len);
+ }
+}
+
+static char *mpack_array_dyn16(char **buf)
+{
+ mpack_w(buf, 0xdc);
+ char *pos = *buf;
+ mpack_w2(buf, 0xFFEF);
+ return pos;
+}
+
+static void mpack_str(char **buf, const char *str)
+{
+ assert(sizeof(schar_T) - 1 < 0x20);
+ size_t len = STRLEN(str);
+ mpack_w(buf, 0xa0 | len);
+ memcpy(*buf, str, len);
+ *buf += len;
+}
+
void remote_ui_disconnect(uint64_t channel_id)
{
UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id);
@@ -46,9 +135,9 @@ void remote_ui_disconnect(uint64_t channel_id)
return;
}
UIData *data = ui->data;
- api_free_array(data->buffer); // Destroy pending screen updates.
+ kv_destroy(data->call_buf);
pmap_del(uint64_t)(&connected_uis, channel_id);
- xfree(ui->data);
+ xfree(data);
ui->data = NULL; // Flag UI as "stopped".
ui_detach_impl(ui, channel_id);
xfree(ui);
@@ -158,10 +247,19 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dictiona
UIData *data = xmalloc(sizeof(UIData));
data->channel_id = channel_id;
- data->buffer = (Array)ARRAY_DICT_INIT;
+ data->cur_event = NULL;
data->hl_id = 0;
data->client_col = -1;
+ data->nevents_pos = NULL;
+ data->nevents = 0;
+ data->flushed_events = false;
+ data->ncalls_pos = NULL;
+ data->ncalls = 0;
+ data->buf_wptr = data->buf;
+ data->temp_buf = NULL;
data->wildmenu_active = false;
+ data->call_buf = (Array)ARRAY_DICT_INIT;
+ kv_ensure_space(data->call_buf, 16);
ui->data = data;
pmap_put(uint64_t)(&connected_uis, channel_id, ui);
@@ -195,7 +293,6 @@ void nvim_ui_detach(uint64_t channel_id, Error *err)
remote_ui_disconnect(channel_id);
}
-
void nvim_ui_try_resize(uint64_t channel_id, Integer width, Integer height, Error *err)
FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY
{
@@ -255,6 +352,49 @@ static void ui_set_option(UI *ui, bool init, String name, Object value, Error *e
return;
}
+ if (strequal(name.data, "term_name")) {
+ if (value.type != kObjectTypeString) {
+ api_set_error(error, kErrorTypeValidation, "term_name must be a String");
+ return;
+ }
+ set_tty_option("term", string_to_cstr(value.data.string));
+ return;
+ }
+
+ if (strequal(name.data, "term_colors")) {
+ if (value.type != kObjectTypeInteger) {
+ api_set_error(error, kErrorTypeValidation, "term_colors must be a Integer");
+ return;
+ }
+ t_colors = (int)value.data.integer;
+ return;
+ }
+
+ if (strequal(name.data, "term_background")) {
+ if (value.type != kObjectTypeString) {
+ api_set_error(error, kErrorTypeValidation, "term_background must be a String");
+ return;
+ }
+ set_tty_background(value.data.string.data);
+ return;
+ }
+
+ if (strequal(name.data, "stdin_fd")) {
+ if (value.type != kObjectTypeInteger || value.data.integer < 0) {
+ api_set_error(error, kErrorTypeValidation, "stdin_fd must be a non-negative Integer");
+ return;
+ }
+
+ if (starting != NO_SCREEN) {
+ api_set_error(error, kErrorTypeValidation,
+ "stdin_fd can only be used with first attached ui");
+ return;
+ }
+
+ stdin_fd = (int)value.data.integer;
+ return;
+ }
+
// LEGACY: Deprecated option, use `ext_cmdline` instead.
bool is_popupmenu = strequal(name.data, "popupmenu_external");
@@ -305,7 +445,11 @@ void nvim_ui_try_resize_grid(uint64_t channel_id, Integer grid, Integer width, I
return;
}
- ui_grid_resize((handle_T)grid, (int)width, (int)height, err);
+ if (grid == DEFAULT_GRID_HANDLE) {
+ nvim_ui_try_resize(channel_id, width, height, err);
+ } else {
+ ui_grid_resize((handle_T)grid, (int)width, (int)height, err);
+ }
}
/// Tells Nvim the number of elements displaying in the popumenu, to decide
@@ -386,34 +530,128 @@ void nvim_ui_pum_set_bounds(uint64_t channel_id, Float width, Float height, Floa
ui->pum_pos = true;
}
-/// Pushes data into UI.UIData, to be consumed later by remote_ui_flush().
-static void push_call(UI *ui, const char *name, Array args)
+static void flush_event(UIData *data)
+{
+ if (data->cur_event) {
+ mpack_w2(&data->ncalls_pos, data->ncalls);
+ data->cur_event = NULL;
+ }
+ if (!data->nevents_pos) {
+ assert(BUF_POS(data) == 0);
+ char **buf = &data->buf_wptr;
+ // [2, "redraw", [...]]
+ mpack_array(buf, 3);
+ mpack_uint(buf, 2);
+ mpack_str(buf, "redraw");
+ data->nevents_pos = mpack_array_dyn16(buf);
+ }
+}
+
+static inline int write_cb(void *vdata, const char *buf, size_t len)
+{
+ UIData *data = (UIData *)vdata;
+ if (!buf) {
+ return 0;
+ }
+
+ data->pack_totlen += len;
+ if (!data->temp_buf && UI_BUF_SIZE - BUF_POS(data) < len) {
+ data->buf_overflow = true;
+ return 0;
+ }
+
+ memcpy(data->buf_wptr, buf, len);
+ data->buf_wptr += len;
+
+ return 0;
+}
+
+static bool prepare_call(UI *ui, const char *name)
{
- Array call = ARRAY_DICT_INIT;
UIData *data = ui->data;
- // To optimize data transfer(especially for "put"), we bundle adjacent
+ if (BUF_POS(data) > UI_BUF_SIZE - EVENT_BUF_SIZE) {
+ remote_ui_flush_buf(ui);
+ }
+
+ // To optimize data transfer(especially for "grid_line"), we bundle adjacent
// calls to same method together, so only add a new call entry if the last
// method call is different from "name"
- if (kv_size(data->buffer)) {
- call = kv_A(data->buffer, kv_size(data->buffer) - 1).data.array;
- }
- if (!kv_size(call) || strcmp(kv_A(call, 0).data.string.data, name)) {
- call = (Array)ARRAY_DICT_INIT;
- ADD(data->buffer, ARRAY_OBJ(call));
- ADD(call, STRING_OBJ(cstr_to_string(name)));
+ if (!data->cur_event || !strequal(data->cur_event, name)) {
+ flush_event(data);
+ data->cur_event = name;
+ char **buf = &data->buf_wptr;
+ data->ncalls_pos = mpack_array_dyn16(buf);
+ mpack_str(buf, name);
+ data->nevents++;
+ data->ncalls = 1;
+ return true;
}
- ADD(call, ARRAY_OBJ(args));
- kv_A(data->buffer, kv_size(data->buffer) - 1).data.array = call;
+ return false;
+}
+
+/// Pushes data into UI.UIData, to be consumed later by remote_ui_flush().
+static void push_call(UI *ui, const char *name, Array args)
+{
+ UIData *data = ui->data;
+ bool pending = data->nevents_pos;
+ char *buf_pos_save = data->buf_wptr;
+
+ bool new_event = prepare_call(ui, name);
+
+ msgpack_packer pac;
+ data->pack_totlen = 0;
+ data->buf_overflow = false;
+ msgpack_packer_init(&pac, data, write_cb);
+ msgpack_rpc_from_array(args, &pac);
+ if (data->buf_overflow) {
+ data->buf_wptr = buf_pos_save;
+ if (new_event) {
+ data->cur_event = NULL;
+ data->nevents--;
+ }
+ if (pending) {
+ remote_ui_flush_buf(ui);
+ }
+
+ if (data->pack_totlen > UI_BUF_SIZE - strlen(name) - 20) {
+ // TODO(bfredl): manually testable by setting UI_BUF_SIZE to 1024 (mode_info_set)
+ data->temp_buf = xmalloc(20 + strlen(name) + data->pack_totlen);
+ data->buf_wptr = data->temp_buf;
+ char **buf = &data->buf_wptr;
+ mpack_array(buf, 3);
+ mpack_uint(buf, 2);
+ mpack_str(buf, "redraw");
+ mpack_array(buf, 1);
+ mpack_array(buf, 2);
+ mpack_str(buf, name);
+ } else {
+ prepare_call(ui, name);
+ }
+ data->pack_totlen = 0;
+ data->buf_overflow = false;
+ msgpack_rpc_from_array(args, &pac);
+
+ if (data->temp_buf) {
+ size_t size = (size_t)(data->buf_wptr - data->temp_buf);
+ WBuffer *buf = wstream_new_buffer(data->temp_buf, size, 1, xfree);
+ rpc_write_raw(data->channel_id, buf);
+ data->temp_buf = NULL;
+ data->buf_wptr = data->buf;
+ data->nevents_pos = NULL;
+ }
+ }
+ data->ncalls++;
}
static void remote_ui_grid_clear(UI *ui, Integer grid)
{
- Array args = ARRAY_DICT_INIT;
+ UIData *data = ui->data;
+ Array args = data->call_buf;
if (ui->ui_ext[kUILinegrid]) {
- ADD(args, INTEGER_OBJ(grid));
+ ADD_C(args, INTEGER_OBJ(grid));
}
const char *name = ui->ui_ext[kUILinegrid] ? "grid_clear" : "clear";
push_call(ui, name, args);
@@ -421,12 +659,13 @@ static void remote_ui_grid_clear(UI *ui, Integer grid)
static void remote_ui_grid_resize(UI *ui, Integer grid, Integer width, Integer height)
{
- Array args = ARRAY_DICT_INIT;
+ UIData *data = ui->data;
+ Array args = data->call_buf;
if (ui->ui_ext[kUILinegrid]) {
- ADD(args, INTEGER_OBJ(grid));
+ ADD_C(args, INTEGER_OBJ(grid));
}
- ADD(args, INTEGER_OBJ(width));
- ADD(args, INTEGER_OBJ(height));
+ ADD_C(args, INTEGER_OBJ(width));
+ ADD_C(args, INTEGER_OBJ(height));
const char *name = ui->ui_ext[kUILinegrid] ? "grid_resize" : "resize";
push_call(ui, name, args);
}
@@ -434,35 +673,36 @@ static void remote_ui_grid_resize(UI *ui, Integer grid, Integer width, Integer h
static void remote_ui_grid_scroll(UI *ui, Integer grid, Integer top, Integer bot, Integer left,
Integer right, Integer rows, Integer cols)
{
+ UIData *data = ui->data;
if (ui->ui_ext[kUILinegrid]) {
- Array args = ARRAY_DICT_INIT;
- ADD(args, INTEGER_OBJ(grid));
- ADD(args, INTEGER_OBJ(top));
- ADD(args, INTEGER_OBJ(bot));
- ADD(args, INTEGER_OBJ(left));
- ADD(args, INTEGER_OBJ(right));
- ADD(args, INTEGER_OBJ(rows));
- ADD(args, INTEGER_OBJ(cols));
+ Array args = data->call_buf;
+ ADD_C(args, INTEGER_OBJ(grid));
+ ADD_C(args, INTEGER_OBJ(top));
+ ADD_C(args, INTEGER_OBJ(bot));
+ ADD_C(args, INTEGER_OBJ(left));
+ ADD_C(args, INTEGER_OBJ(right));
+ ADD_C(args, INTEGER_OBJ(rows));
+ ADD_C(args, INTEGER_OBJ(cols));
push_call(ui, "grid_scroll", args);
} else {
- Array args = ARRAY_DICT_INIT;
- ADD(args, INTEGER_OBJ(top));
- ADD(args, INTEGER_OBJ(bot-1));
- ADD(args, INTEGER_OBJ(left));
- ADD(args, INTEGER_OBJ(right-1));
+ Array args = data->call_buf;
+ ADD_C(args, INTEGER_OBJ(top));
+ ADD_C(args, INTEGER_OBJ(bot - 1));
+ ADD_C(args, INTEGER_OBJ(left));
+ ADD_C(args, INTEGER_OBJ(right - 1));
push_call(ui, "set_scroll_region", args);
- args = (Array)ARRAY_DICT_INIT;
- ADD(args, INTEGER_OBJ(rows));
+ args = data->call_buf;
+ ADD_C(args, INTEGER_OBJ(rows));
push_call(ui, "scroll", args);
// some clients have "clear" being affected by scroll region,
// so reset it.
- args = (Array)ARRAY_DICT_INIT;
- ADD(args, INTEGER_OBJ(0));
- ADD(args, INTEGER_OBJ(ui->height-1));
- ADD(args, INTEGER_OBJ(0));
- ADD(args, INTEGER_OBJ(ui->width-1));
+ args = data->call_buf;
+ ADD_C(args, INTEGER_OBJ(0));
+ ADD_C(args, INTEGER_OBJ(ui->height - 1));
+ ADD_C(args, INTEGER_OBJ(0));
+ ADD_C(args, INTEGER_OBJ(ui->width - 1));
push_call(ui, "set_scroll_region", args);
}
}
@@ -473,26 +713,27 @@ static void remote_ui_default_colors_set(UI *ui, Integer rgb_fg, Integer rgb_bg,
if (!ui->ui_ext[kUITermColors]) {
HL_SET_DEFAULT_COLORS(rgb_fg, rgb_bg, rgb_sp);
}
- Array args = ARRAY_DICT_INIT;
- ADD(args, INTEGER_OBJ(rgb_fg));
- ADD(args, INTEGER_OBJ(rgb_bg));
- ADD(args, INTEGER_OBJ(rgb_sp));
- ADD(args, INTEGER_OBJ(cterm_fg));
- ADD(args, INTEGER_OBJ(cterm_bg));
+ UIData *data = ui->data;
+ Array args = data->call_buf;
+ ADD_C(args, INTEGER_OBJ(rgb_fg));
+ ADD_C(args, INTEGER_OBJ(rgb_bg));
+ ADD_C(args, INTEGER_OBJ(rgb_sp));
+ ADD_C(args, INTEGER_OBJ(cterm_fg));
+ ADD_C(args, INTEGER_OBJ(cterm_bg));
push_call(ui, "default_colors_set", args);
// Deprecated
if (!ui->ui_ext[kUILinegrid]) {
- args = (Array)ARRAY_DICT_INIT;
- ADD(args, INTEGER_OBJ(ui->rgb ? rgb_fg : cterm_fg - 1));
+ args = data->call_buf;
+ ADD_C(args, INTEGER_OBJ(ui->rgb ? rgb_fg : cterm_fg - 1));
push_call(ui, "update_fg", args);
- args = (Array)ARRAY_DICT_INIT;
- ADD(args, INTEGER_OBJ(ui->rgb ? rgb_bg : cterm_bg - 1));
+ args = data->call_buf;
+ ADD_C(args, INTEGER_OBJ(ui->rgb ? rgb_bg : cterm_bg - 1));
push_call(ui, "update_bg", args);
- args = (Array)ARRAY_DICT_INIT;
- ADD(args, INTEGER_OBJ(ui->rgb ? rgb_sp : -1));
+ args = data->call_buf;
+ ADD_C(args, INTEGER_OBJ(ui->rgb ? rgb_sp : -1));
push_call(ui, "update_sp", args);
}
}
@@ -503,26 +744,29 @@ static void remote_ui_hl_attr_define(UI *ui, Integer id, HlAttrs rgb_attrs, HlAt
if (!ui->ui_ext[kUILinegrid]) {
return;
}
- Array args = ARRAY_DICT_INIT;
- ADD(args, INTEGER_OBJ(id));
- ADD(args, DICTIONARY_OBJ(hlattrs2dict(rgb_attrs, true)));
- ADD(args, DICTIONARY_OBJ(hlattrs2dict(cterm_attrs, false)));
+ UIData *data = ui->data;
+ Array args = data->call_buf;
+ ADD_C(args, INTEGER_OBJ(id));
+ ADD_C(args, DICTIONARY_OBJ(hlattrs2dict(rgb_attrs, true)));
+ ADD_C(args, DICTIONARY_OBJ(hlattrs2dict(cterm_attrs, false)));
if (ui->ui_ext[kUIHlState]) {
- ADD(args, ARRAY_OBJ(copy_array(info)));
+ ADD_C(args, ARRAY_OBJ(info));
} else {
- ADD(args, ARRAY_OBJ((Array)ARRAY_DICT_INIT));
+ ADD_C(args, ARRAY_OBJ((Array)ARRAY_DICT_INIT));
}
push_call(ui, "hl_attr_define", args);
+ // TODO(bfredl): could be elided
+ api_free_dictionary(kv_A(args, 1).data.dictionary);
+ api_free_dictionary(kv_A(args, 2).data.dictionary);
}
static void remote_ui_highlight_set(UI *ui, int id)
{
- Array args = ARRAY_DICT_INIT;
UIData *data = ui->data;
-
+ Array args = data->call_buf;
if (data->hl_id == id) {
return;
@@ -530,18 +774,20 @@ static void remote_ui_highlight_set(UI *ui, int id)
data->hl_id = id;
Dictionary hl = hlattrs2dict(syn_attr2entry(id), ui->rgb);
- ADD(args, DICTIONARY_OBJ(hl));
+ ADD_C(args, DICTIONARY_OBJ(hl));
push_call(ui, "highlight_set", args);
+ api_free_dictionary(kv_A(args, 0).data.dictionary);
}
/// "true" cursor used only for input focus
static void remote_ui_grid_cursor_goto(UI *ui, Integer grid, Integer row, Integer col)
{
if (ui->ui_ext[kUILinegrid]) {
- Array args = ARRAY_DICT_INIT;
- ADD(args, INTEGER_OBJ(grid));
- ADD(args, INTEGER_OBJ(row));
- ADD(args, INTEGER_OBJ(col));
+ UIData *data = ui->data;
+ Array args = data->call_buf;
+ ADD_C(args, INTEGER_OBJ(grid));
+ ADD_C(args, INTEGER_OBJ(row));
+ ADD_C(args, INTEGER_OBJ(col));
push_call(ui, "grid_cursor_goto", args);
} else {
UIData *data = ui->data;
@@ -560,9 +806,9 @@ static void remote_ui_cursor_goto(UI *ui, Integer row, Integer col)
}
data->client_row = row;
data->client_col = col;
- Array args = ARRAY_DICT_INIT;
- ADD(args, INTEGER_OBJ(row));
- ADD(args, INTEGER_OBJ(col));
+ Array args = data->call_buf;
+ ADD_C(args, INTEGER_OBJ(row));
+ ADD_C(args, INTEGER_OBJ(col));
push_call(ui, "cursor_goto", args);
}
@@ -570,8 +816,8 @@ static void remote_ui_put(UI *ui, const char *cell)
{
UIData *data = ui->data;
data->client_col++;
- Array args = ARRAY_DICT_INIT;
- ADD(args, STRING_OBJ(cstr_to_string(cell)));
+ Array args = data->call_buf;
+ ADD_C(args, STRING_OBJ(cstr_as_string((char *)cell)));
push_call(ui, "put", args);
}
@@ -581,47 +827,70 @@ static void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startc
{
UIData *data = ui->data;
if (ui->ui_ext[kUILinegrid]) {
- Array args = ARRAY_DICT_INIT;
- ADD(args, INTEGER_OBJ(grid));
- ADD(args, INTEGER_OBJ(row));
- ADD(args, INTEGER_OBJ(startcol));
- Array cells = ARRAY_DICT_INIT;
- int repeat = 0;
- size_t ncells = (size_t)(endcol-startcol);
+ prepare_call(ui, "grid_line");
+ data->ncalls++;
+
+ char **buf = &data->buf_wptr;
+ mpack_array(buf, 4);
+ mpack_uint(buf, (uint32_t)grid);
+ mpack_uint(buf, (uint32_t)row);
+ mpack_uint(buf, (uint32_t)startcol);
+ char *lenpos = mpack_array_dyn16(buf);
+
+ uint32_t repeat = 0;
+ size_t ncells = (size_t)(endcol - startcol);
int last_hl = -1;
+ uint32_t nelem = 0;
for (size_t i = 0; i < ncells; i++) {
repeat++;
- if (i == ncells-1 || attrs[i] != attrs[i+1]
- || STRCMP(chunk[i], chunk[i+1])) {
- Array cell = ARRAY_DICT_INIT;
- ADD(cell, STRING_OBJ(cstr_to_string((const char *)chunk[i])));
- if (attrs[i] != last_hl || repeat > 1) {
- ADD(cell, INTEGER_OBJ(attrs[i]));
- last_hl = attrs[i];
+ if (i == ncells - 1 || attrs[i] != attrs[i + 1]
+ || STRCMP(chunk[i], chunk[i + 1])) {
+ if (UI_BUF_SIZE - BUF_POS(data) < 2 * (1 + 2 + sizeof(schar_T) + 5 + 5)) {
+ // close to overflowing the redraw buffer. finish this event,
+ // flush, and start a new "grid_line" event at the current position.
+ // For simplicity leave place for the final "clear" element
+ // as well, hence the factor of 2 in the check.
+ mpack_w2(&lenpos, nelem);
+ remote_ui_flush_buf(ui);
+
+ prepare_call(ui, "grid_line");
+ data->ncalls++;
+ mpack_array(buf, 4);
+ mpack_uint(buf, (uint32_t)grid);
+ mpack_uint(buf, (uint32_t)row);
+ mpack_uint(buf, (uint32_t)startcol + (uint32_t)i - repeat + 1);
+ lenpos = mpack_array_dyn16(buf);
+ nelem = 0;
+ last_hl = -1;
}
- if (repeat > 1) {
- ADD(cell, INTEGER_OBJ(repeat));
+ uint32_t csize = (repeat > 1) ? 3 : ((attrs[i] != last_hl) ? 2 : 1);
+ nelem++;
+ mpack_array(buf, csize);
+ mpack_str(buf, (const char *)chunk[i]);
+ if (csize >= 2) {
+ mpack_uint(buf, (uint32_t)attrs[i]);
+ if (csize >= 3) {
+ mpack_uint(buf, repeat);
+ }
}
- ADD(cells, ARRAY_OBJ(cell));
+ last_hl = attrs[i];
repeat = 0;
}
}
if (endcol < clearcol) {
- Array cell = ARRAY_DICT_INIT;
- ADD(cell, STRING_OBJ(cstr_to_string(" ")));
- ADD(cell, INTEGER_OBJ(clearattr));
- ADD(cell, INTEGER_OBJ(clearcol-endcol));
- ADD(cells, ARRAY_OBJ(cell));
+ nelem++;
+ mpack_array(buf, 3);
+ mpack_str(buf, " ");
+ mpack_uint(buf, (uint32_t)clearattr);
+ mpack_uint(buf, (uint32_t)(clearcol - endcol));
}
- ADD(args, ARRAY_OBJ(cells));
-
- push_call(ui, "grid_line", args);
+ mpack_w2(&lenpos, nelem);
} else {
- for (int i = 0; i < endcol-startcol; i++) {
- remote_ui_cursor_goto(ui, row, startcol+i);
+ for (int i = 0; i < endcol - startcol; i++) {
+ remote_ui_cursor_goto(ui, row, startcol + i);
remote_ui_highlight_set(ui, attrs[i]);
remote_ui_put(ui, (const char *)chunk[i]);
- if (utf_ambiguous_width(utf_ptr2char(chunk[i]))) {
+ if (utf_ambiguous_width(utf_ptr2char((char *)chunk[i]))) {
data->client_col = -1; // force cursor update
}
}
@@ -642,16 +911,47 @@ static void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startc
}
}
+/// Flush the internal packing buffer to the client.
+///
+/// This might happen multiple times before the actual ui_flush, if the
+/// total redraw size is large!
+static void remote_ui_flush_buf(UI *ui)
+{
+ UIData *data = ui->data;
+ if (!data->nevents_pos) {
+ return;
+ }
+ if (data->cur_event) {
+ flush_event(data);
+ }
+ mpack_w2(&data->nevents_pos, data->nevents);
+ data->nevents = 0;
+ data->nevents_pos = NULL;
+
+ // TODO(bfredl): elide copy by a length one free-list like the arena
+ size_t size = BUF_POS(data);
+ WBuffer *buf = wstream_new_buffer(xmemdup(data->buf, size), size, 1, xfree);
+ rpc_write_raw(data->channel_id, buf);
+ data->buf_wptr = data->buf;
+ // we have sent events to the client, but possibly not yet the final "flush"
+ // event.
+ data->flushed_events = true;
+}
+
+/// An intentional flush (vsync) when Nvim is finished redrawing the screen
+///
+/// Clients can know this happened by a final "flush" event at the end of the
+/// "redraw" batch.
static void remote_ui_flush(UI *ui)
{
UIData *data = ui->data;
- if (data->buffer.size > 0) {
+ if (data->nevents > 0 || data->flushed_events) {
if (!ui->ui_ext[kUILinegrid]) {
remote_ui_cursor_goto(ui, data->cursor_row, data->cursor_col);
}
push_call(ui, "flush", (Array)ARRAY_DICT_INIT);
- rpc_send_event(data->channel_id, "redraw", data->buffer);
- data->buffer = (Array)ARRAY_DICT_INIT;
+ remote_ui_flush_buf(ui);
+ data->flushed_events = false;
}
}
@@ -686,7 +986,7 @@ static Array translate_firstarg(UI *ui, Array args)
return new_args;
}
-static void remote_ui_event(UI *ui, char *name, Array args, bool *args_consumed)
+static void remote_ui_event(UI *ui, char *name, Array args)
{
UIData *data = ui->data;
if (!ui->ui_ext[kUILinegrid]) {
@@ -695,21 +995,24 @@ static void remote_ui_event(UI *ui, char *name, Array args, bool *args_consumed)
if (strequal(name, "cmdline_show")) {
Array new_args = translate_firstarg(ui, args);
push_call(ui, name, new_args);
+ api_free_array(new_args);
return;
} else if (strequal(name, "cmdline_block_show")) {
- Array new_args = ARRAY_DICT_INIT;
+ Array new_args = data->call_buf;
Array block = args.items[0].data.array;
Array new_block = ARRAY_DICT_INIT;
for (size_t i = 0; i < block.size; i++) {
ADD(new_block,
ARRAY_OBJ(translate_contents(ui, block.items[i].data.array)));
}
- ADD(new_args, ARRAY_OBJ(new_block));
+ ADD_C(new_args, ARRAY_OBJ(new_block));
push_call(ui, name, new_args);
+ api_free_array(new_block);
return;
} else if (strequal(name, "cmdline_block_append")) {
Array new_args = translate_firstarg(ui, args);
push_call(ui, name, new_args);
+ api_free_array(new_args);
return;
}
}
@@ -720,18 +1023,19 @@ static void remote_ui_event(UI *ui, char *name, Array args, bool *args_consumed)
data->wildmenu_active = (args.items[4].data.integer == -1)
|| !ui->ui_ext[kUIPopupmenu];
if (data->wildmenu_active) {
- Array new_args = ARRAY_DICT_INIT;
+ Array new_args = data->call_buf;
Array items = args.items[0].data.array;
Array new_items = ARRAY_DICT_INIT;
for (size_t i = 0; i < items.size; i++) {
ADD(new_items, copy_object(items.items[i].data.array.items[0]));
}
- ADD(new_args, ARRAY_OBJ(new_items));
+ ADD_C(new_args, ARRAY_OBJ(new_items));
push_call(ui, "wildmenu_show", new_args);
+ api_free_array(new_items);
if (args.items[1].data.integer != -1) {
- Array new_args2 = ARRAY_DICT_INIT;
- ADD(new_args2, args.items[1]);
- push_call(ui, "wildmenu_select", new_args);
+ Array new_args2 = data->call_buf;
+ ADD_C(new_args2, args.items[1]);
+ push_call(ui, "wildmenu_select", new_args2);
}
return;
}
@@ -746,19 +1050,7 @@ static void remote_ui_event(UI *ui, char *name, Array args, bool *args_consumed)
}
}
-
- Array my_args = ARRAY_DICT_INIT;
- // Objects are currently single-reference
- // make a copy, but only if necessary
- if (*args_consumed) {
- for (size_t i = 0; i < args.size; i++) {
- ADD(my_args, copy_object(args.items[i]));
- }
- } else {
- my_args = args;
- *args_consumed = true;
- }
- push_call(ui, name, my_args);
+ push_call(ui, name, args);
}
static void remote_ui_inspect(UI *ui, Dictionary *info)
diff --git a/src/nvim/api/ui.h b/src/nvim/api/ui.h
index b3af14f8a8..bc70406acb 100644
--- a/src/nvim/api/ui.h
+++ b/src/nvim/api/ui.h
@@ -4,6 +4,7 @@
#include <stdint.h>
#include "nvim/api/private/defs.h"
+#include "nvim/map.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/ui.h.generated.h"
diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h
index 03fe5c5058..8b7e01e1c3 100644
--- a/src/nvim/api/ui_events.in.h
+++ b/src/nvim/api/ui_events.in.h
@@ -78,13 +78,13 @@ void hl_attr_define(Integer id, HlAttrs rgb_attrs, HlAttrs cterm_attrs,
void hl_group_set(String name, Integer id)
FUNC_API_SINCE(6) FUNC_API_BRIDGE_IMPL;
void grid_resize(Integer grid, Integer width, Integer height)
- FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL;
+ FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL FUNC_API_CLIENT_IMPL;
void grid_clear(Integer grid)
FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL;
void grid_cursor_goto(Integer grid, Integer row, Integer col)
FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL;
void grid_line(Integer grid, Integer row, Integer col_start, Array data)
- FUNC_API_SINCE(5) FUNC_API_REMOTE_ONLY;
+ FUNC_API_SINCE(5) FUNC_API_REMOTE_ONLY FUNC_API_CLIENT_IMPL;
void grid_scroll(Integer grid, Integer top, Integer bot,
Integer left, Integer right, Integer rows, Integer cols)
FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL;
@@ -99,7 +99,7 @@ void raw_line(Integer grid, Integer row, Integer startcol,
LineFlags flags, const schar_T *chunk, const sattr_T *attrs)
FUNC_API_NOEXPORT FUNC_API_COMPOSITOR_IMPL;
-void event(char *name, Array args, bool *args_consumed)
+void event(char *name, Array args)
FUNC_API_NOEXPORT;
void win_pos(Integer grid, Window win, Integer startrow,
@@ -123,6 +123,10 @@ void win_viewport(Integer grid, Window win, Integer topline,
Integer line_count)
FUNC_API_SINCE(7) FUNC_API_REMOTE_ONLY;
+void win_extmark(Integer grid, Window win, Integer ns_id, Integer mark_id,
+ Integer row, Integer col)
+ FUNC_API_SINCE(10) FUNC_API_REMOTE_ONLY;
+
void popupmenu_show(Array items, Integer selected,
Integer row, Integer col, Integer grid)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
@@ -170,4 +174,6 @@ void msg_ruler(Array content)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
void msg_history_show(Array entries)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
+void msg_history_clear(void)
+ FUNC_API_SINCE(10) FUNC_API_REMOTE_ONLY;
#endif // NVIM_API_UI_EVENTS_IN_H
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 59db12f2c0..56516b2ac7 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -19,19 +19,26 @@
#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/buffer_defs.h"
+#include "nvim/charset.h"
#include "nvim/context.h"
#include "nvim/decoration.h"
+#include "nvim/decoration_provider.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
#include "nvim/eval/userfunc.h"
#include "nvim/ex_cmds2.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/file_search.h"
#include "nvim/fileio.h"
#include "nvim/getchar.h"
+#include "nvim/globals.h"
#include "nvim/highlight.h"
+#include "nvim/highlight_defs.h"
+#include "nvim/highlight_group.h"
#include "nvim/lua/executor.h"
+#include "nvim/mapping.h"
#include "nvim/mark.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
@@ -39,6 +46,7 @@
#include "nvim/move.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/msgpack_rpc/helpers.h"
+#include "nvim/msgpack_rpc/unpacker.h"
#include "nvim/ops.h"
#include "nvim/option.h"
#include "nvim/os/input.h"
@@ -46,7 +54,6 @@
#include "nvim/popupmnu.h"
#include "nvim/screen.h"
#include "nvim/state.h"
-#include "nvim/syntax.h"
#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
@@ -108,7 +115,7 @@ Dictionary nvim_get_hl_by_id(Integer hl_id, Boolean rgb, Error *err)
Integer nvim_get_hl_id_by_name(String name)
FUNC_API_SINCE(7)
{
- return syn_check_group(name.data, (int)name.size);
+ return syn_check_group(name.data, name.size);
}
Dictionary nvim__get_hl_defs(Integer ns_id, Error *err)
@@ -119,33 +126,56 @@ Dictionary nvim__get_hl_defs(Integer ns_id, Error *err)
abort();
}
-/// Set a highlight group.
-///
-/// @param ns_id number of namespace for this highlight
-/// @param name highlight group name, like ErrorMsg
-/// @param val highlight definition map, like |nvim_get_hl_by_name|.
-/// in addition the following keys are also recognized:
-/// `default`: don't override existing definition,
-/// like `hi default`
-/// `ctermfg`: sets foreground of cterm color
-/// `ctermbg`: sets background of cterm color
-/// `cterm` : cterm attribute map. sets attributed for
-/// cterm colors. similer to `hi cterm`
-/// Note: by default cterm attributes are
-/// same as attributes of gui color
+/// Sets a highlight group.
+///
+/// @note Unlike the `:highlight` command which can update a highlight group,
+/// this function completely replaces the definition. For example:
+/// ``nvim_set_hl(0, 'Visual', {})`` will clear the highlight group
+/// 'Visual'.
+///
+/// @note The fg and bg keys also accept the string values `"fg"` or `"bg"`
+/// which act as aliases to the corresponding foreground and background
+/// values of the Normal group. If the Normal group has not been defined,
+/// using these values results in an error.
+///
+/// @param ns_id Namespace id for this highlight |nvim_create_namespace()|.
+/// Use 0 to set a highlight group globally |:highlight|.
+/// @param name Highlight group name, e.g. "ErrorMsg"
+/// @param val Highlight definition map, accepts the following keys:
+/// - fg (or foreground): color name or "#RRGGBB", see note.
+/// - bg (or background): color name or "#RRGGBB", see note.
+/// - sp (or special): color name or "#RRGGBB"
+/// - blend: integer between 0 and 100
+/// - bold: boolean
+/// - standout: boolean
+/// - underline: boolean
+/// - undercurl: boolean
+/// - underdouble: boolean
+/// - underdotted: boolean
+/// - underdashed: boolean
+/// - strikethrough: boolean
+/// - italic: boolean
+/// - reverse: boolean
+/// - nocombine: boolean
+/// - link: name of another highlight group to link to, see |:hi-link|.
+/// - default: Don't override existing definition |:hi-default|
+/// - ctermfg: Sets foreground of cterm color |highlight-ctermfg|
+/// - ctermbg: Sets background of cterm color |highlight-ctermbg|
+/// - cterm: cterm attribute map, like |highlight-args|. If not set,
+/// cterm attributes will match those from the attribute map
+/// documented above.
/// @param[out] err Error details, if any
///
-/// TODO: ns_id = 0, should modify :highlight namespace
-/// TODO val should take update vs reset flag
-void nvim_set_hl(Integer ns_id, String name, Dictionary val, Error *err)
+// TODO(bfredl): val should take update vs reset flag
+void nvim_set_hl(Integer ns_id, String name, Dict(highlight) *val, Error *err)
FUNC_API_SINCE(7)
{
- int hl_id = syn_check_group(name.data, (int)name.size);
+ int hl_id = syn_check_group(name.data, name.size);
int link_id = -1;
HlAttrs attrs = dict2hlattrs(val, true, &link_id, err);
if (!ERROR_SET(err)) {
- ns_hl_def((NS)ns_id, hl_id, attrs, link_id);
+ ns_hl_def((NS)ns_id, hl_id, attrs, link_id, val);
}
}
@@ -180,28 +210,29 @@ static void on_redraw_event(void **argv)
redraw_all_later(NOT_VALID);
}
-
/// Sends input-keys to Nvim, subject to various quirks controlled by `mode`
/// flags. This is a blocking call, unlike |nvim_input()|.
///
/// 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;
@@ -210,7 +241,7 @@ void nvim_feedkeys(String keys, String mode, Boolean escape_csi)
bool execute = false;
bool dangerous = false;
- for (size_t i = 0; i < mode.size; ++i) {
+ for (size_t i = 0; i < mode.size; i++) {
switch (mode.data[i]) {
case 'n':
remap = false; break;
@@ -232,20 +263,20 @@ 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 = vim_strsave_escape_ks(keys.data);
} else {
keys_esc = keys.data;
}
- ins_typebuf((char_u *)keys_esc, (remap ? REMAP_YES : REMAP_NONE),
+ ins_typebuf(keys_esc, (remap ? REMAP_YES : REMAP_NONE),
insert ? 0 : typebuf.tb_len, !typed, false);
if (vgetc_busy) {
typebuf_was_filled = true;
}
- if (escape_csi) {
+ if (escape_ks) {
xfree(keys_esc);
}
@@ -394,13 +425,22 @@ String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt, Bool
return (String) { .data = NULL, .size = 0 };
}
+ int flags = 0;
+ if (from_part) {
+ flags |= REPTERM_FROM_PART;
+ }
+ if (do_lt) {
+ flags |= REPTERM_DO_LT;
+ }
+ if (!special) {
+ flags |= REPTERM_NO_SPECIAL;
+ }
+
char *ptr = NULL;
- replace_termcodes((char_u *)str.data, str.size, (char_u **)&ptr,
- from_part, do_lt, special, CPO_TO_CPO_FLAGS);
+ replace_termcodes(str.data, str.size, &ptr, flags, NULL, CPO_TO_CPO_FLAGS);
return cstr_as_string(ptr);
}
-
/// Execute Lua code. Parameters (if any) are available as `...` inside the
/// chunk. The chunk can return a value.
///
@@ -441,7 +481,7 @@ Object nvim_notify(String msg, Integer log_level, Dictionary opts, Error *err)
}
/// Calculates the number of display cells occupied by `text`.
-/// <Tab> counts as one cell.
+/// Control characters including <Tab> count as one cell.
///
/// @param text Some text
/// @param[out] err Error details, if any
@@ -454,7 +494,7 @@ Integer nvim_strwidth(String text, Error *err)
return 0;
}
- return (Integer)mb_string2cells((char_u *)text.data);
+ return (Integer)mb_string2cells(text.data);
}
/// Gets the paths contained in 'runtimepath'.
@@ -491,12 +531,15 @@ ArrayOf(String) nvim_get_runtime_file(String name, Boolean all, Error *err)
int flags = DIP_DIRFILE | (all ? DIP_ALL : 0);
- do_in_runtimepath((char_u *)(name.size ? name.data : ""),
- flags, find_runtime_cb, &rv);
+ TRY_WRAP({
+ try_start();
+ do_in_runtimepath((name.size ? name.data : ""), flags, find_runtime_cb, &rv);
+ try_end(err);
+ });
return rv;
}
-static void find_runtime_cb(char_u *fname, void *cookie)
+static void find_runtime_cb(char *fname, void *cookie)
{
Array *rv = (Array *)cookie;
if (fname != NULL) {
@@ -513,20 +556,33 @@ String nvim__get_lib_dir(void)
///
/// @param pat pattern of files to search for
/// @param all whether to return all matches or only the first
-/// @param options
-/// is_lua: only search lua subdirs
+/// @param opts is_lua: only search lua subdirs
/// @return list of absolute paths to the found files
ArrayOf(String) nvim__get_runtime(Array pat, Boolean all, Dict(runtime) *opts, Error *err)
FUNC_API_SINCE(8)
FUNC_API_FAST
{
bool is_lua = api_object_to_bool(opts->is_lua, "is_lua", false, err);
+ bool source = api_object_to_bool(opts->do_source, "do_source", false, err);
+ if (source && !nlua_is_deferred_safe()) {
+ api_set_error(err, kErrorTypeValidation, "'do_source' cannot be used in fast callback");
+ }
+
if (ERROR_SET(err)) {
return (Array)ARRAY_DICT_INIT;
}
- return runtime_get_named(is_lua, pat, all);
-}
+ ArrayOf(String) res = runtime_get_named(is_lua, pat, all);
+
+ if (source) {
+ for (size_t i = 0; i < res.size; i++) {
+ String name = res.items[i].data.string;
+ (void)do_source(name.data, false, DOSO_NONE);
+ }
+ }
+
+ return res;
+}
/// Changes the global working directory.
///
@@ -546,14 +602,13 @@ void nvim_set_current_dir(String dir, Error *err)
try_start();
- if (vim_chdir((char_u *)string)) {
+ if (!changedir_func(string, kCdScopeGlobal)) {
if (!try_end(err)) {
api_set_error(err, kErrorTypeException, "Failed to change directory");
}
return;
}
- post_chdir(kCdScopeGlobal, true);
try_end(err);
}
@@ -596,7 +651,19 @@ void nvim_del_current_line(Error *err)
Object nvim_get_var(String name, Error *err)
FUNC_API_SINCE(1)
{
- return dict_get_value(&globvardict, name, err);
+ dictitem_T *di = tv_dict_find(&globvardict, name.data, (ptrdiff_t)name.size);
+ if (di == NULL) { // try to autoload script
+ if (!script_autoload(name.data, name.size, false) || aborting()) {
+ api_set_error(err, kErrorTypeValidation, "Key not found: %s", name.data);
+ return (Object)OBJECT_INIT;
+ }
+ di = tv_dict_find(&globvardict, name.data, (ptrdiff_t)name.size);
+ }
+ if (di == NULL) {
+ api_set_error(err, kErrorTypeValidation, "Key not found: %s", name.data);
+ return (Object)OBJECT_INIT;
+ }
+ return vim_to_object(&di->di_tv);
}
/// Sets a global (g:) variable.
@@ -642,189 +709,6 @@ void nvim_set_vvar(String name, Object value, Error *err)
dict_set_var(&vimvardict, name, value, false, false, err);
}
-/// Gets the global value of an option.
-///
-/// @param name Option name
-/// @param[out] err Error details, if any
-/// @return Option value (global)
-Object nvim_get_option(String name, Error *err)
- FUNC_API_SINCE(1)
-{
- return get_option_from(NULL, SREQ_GLOBAL, name, err);
-}
-
-/// Gets the value of an option. The behavior of this function matches that of
-/// |:set|: the local value of an option is returned if it exists; otherwise,
-/// the global value is returned. Local values always correspond to the current
-/// buffer or window. To get a buffer-local or window-local option for a
-/// specific buffer or window, use |nvim_buf_get_option()| or
-/// |nvim_win_get_option()|.
-///
-/// @param name Option name
-/// @param opts Optional parameters
-/// - scope: One of 'global' or 'local'. Analogous to
-/// |:setglobal| and |:setlocal|, respectively.
-/// @param[out] err Error details, if any
-/// @return Option value
-Object nvim_get_option_value(String name, Dict(option) *opts, Error *err)
- FUNC_API_SINCE(9)
-{
- Object rv = OBJECT_INIT;
-
- int scope = 0;
- if (opts->scope.type == kObjectTypeString) {
- if (!strcmp(opts->scope.data.string.data, "local")) {
- scope = OPT_LOCAL;
- } else if (!strcmp(opts->scope.data.string.data, "global")) {
- scope = OPT_GLOBAL;
- } else {
- api_set_error(err, kErrorTypeValidation, "invalid scope: must be 'local' or 'global'");
- goto end;
- }
- } else if (HAS_KEY(opts->scope)) {
- api_set_error(err, kErrorTypeValidation, "invalid value for key: scope");
- goto end;
- }
-
- long numval = 0;
- char *stringval = NULL;
- switch (get_option_value(name.data, &numval, (char_u **)&stringval, scope)) {
- case 0:
- rv = STRING_OBJ(cstr_as_string(stringval));
- break;
- case 1:
- rv = INTEGER_OBJ(numval);
- break;
- case 2:
- switch (numval) {
- case 0:
- case 1:
- rv = BOOLEAN_OBJ(numval);
- break;
- default:
- // Boolean options that return something other than 0 or 1 should return nil. Currently this
- // only applies to 'autoread' which uses -1 as a local value to indicate "unset"
- rv = NIL;
- break;
- }
- break;
- default:
- api_set_error(err, kErrorTypeValidation, "unknown option '%s'", name.data);
- goto end;
- }
-
-end:
- return rv;
-}
-
-/// Sets the value of an option. The behavior of this function matches that of
-/// |:set|: for global-local options, both the global and local value are set
-/// unless otherwise specified with {scope}.
-///
-/// @param name Option name
-/// @param value New option value
-/// @param opts Optional parameters
-/// - scope: One of 'global' or 'local'. Analogous to
-/// |:setglobal| and |:setlocal|, respectively.
-/// @param[out] err Error details, if any
-void nvim_set_option_value(String name, Object value, Dict(option) *opts, Error *err)
- FUNC_API_SINCE(9)
-{
- int scope = 0;
- if (opts->scope.type == kObjectTypeString) {
- if (!strcmp(opts->scope.data.string.data, "local")) {
- scope = OPT_LOCAL;
- } else if (!strcmp(opts->scope.data.string.data, "global")) {
- scope = OPT_GLOBAL;
- } else {
- api_set_error(err, kErrorTypeValidation, "invalid scope: must be 'local' or 'global'");
- return;
- }
- } else if (HAS_KEY(opts->scope)) {
- api_set_error(err, kErrorTypeValidation, "invalid value for key: scope");
- return;
- }
-
- long numval = 0;
- char *stringval = NULL;
-
- switch (value.type) {
- case kObjectTypeInteger:
- numval = value.data.integer;
- break;
- case kObjectTypeBoolean:
- numval = value.data.boolean ? 1 : 0;
- break;
- case kObjectTypeString:
- stringval = value.data.string.data;
- break;
- case kObjectTypeNil:
- scope |= OPT_CLEAR;
- break;
- default:
- api_set_error(err, kErrorTypeValidation, "invalid value for option");
- return;
- }
-
- char *e = set_option_value(name.data, numval, stringval, scope);
- if (e) {
- api_set_error(err, kErrorTypeException, "%s", e);
- }
-}
-
-/// Gets the option information for all options.
-///
-/// The dictionary has the full option names as keys and option metadata
-/// dictionaries as detailed at |nvim_get_option_info|.
-///
-/// @return dictionary of all options
-Dictionary nvim_get_all_options_info(Error *err)
- FUNC_API_SINCE(7)
-{
- return get_all_vimoptions();
-}
-
-/// Gets the option information for one option
-///
-/// Resulting dictionary has keys:
-/// - name: Name of the option (like 'filetype')
-/// - shortname: Shortened name of the option (like 'ft')
-/// - type: type of option ("string", "number" or "boolean")
-/// - default: The default value for the option
-/// - was_set: Whether the option was set.
-///
-/// - last_set_sid: Last set script id (if any)
-/// - last_set_linenr: line number where option was set
-/// - last_set_chan: Channel where option was set (0 for local)
-///
-/// - scope: one of "global", "win", or "buf"
-/// - global_local: whether win or buf option has a global value
-///
-/// - commalist: List of comma separated values
-/// - flaglist: List of single char flags
-///
-///
-/// @param name Option name
-/// @param[out] err Error details, if any
-/// @return Option Information
-Dictionary nvim_get_option_info(String name, Error *err)
- FUNC_API_SINCE(7)
-{
- return get_vimoption(name, err);
-}
-
-/// Sets the global value of an option.
-///
-/// @param channel_id
-/// @param name Option name
-/// @param value New option value
-/// @param[out] err Error details, if any
-void nvim_set_option(uint64_t channel_id, String name, Object value, Error *err)
- FUNC_API_SINCE(1)
-{
- set_option_to(channel_id, NULL, SREQ_GLOBAL, name, value, err);
-}
-
/// Echo a message.
///
/// @param chunks A list of [text, hl_group] arrays, each representing a
@@ -845,26 +729,15 @@ void nvim_echo(Array chunks, Boolean history, Dictionary opts, Error *err)
goto error;
}
- no_wait_return++;
- msg_start();
- msg_clr_eos();
- bool need_clear = false;
- for (uint32_t i = 0; i < kv_size(hl_msg); i++) {
- HlMessageChunk chunk = kv_A(hl_msg, i);
- msg_multiline_attr((const char *)chunk.text.data, chunk.attr,
- true, &need_clear);
- }
+ msg_multiattr(hl_msg, history ? "echomsg" : "echo", history);
+
if (history) {
- msg_ext_set_kind("echomsg");
- add_hl_msg_hist(hl_msg);
- } else {
- msg_ext_set_kind("echo");
+ // history takes ownership
+ return;
}
- no_wait_return--;
- msg_end();
error:
- clear_hl_msg(&hl_msg);
+ hl_msg_free(hl_msg);
}
/// Writes a message to the Vim output buffer. Does not append "\n", the
@@ -1114,6 +987,7 @@ Integer nvim_open_term(Buffer buffer, DictionaryOf(LuaRef) opts, Error *err)
TerminalOptions topts;
Channel *chan = channel_alloc(kChannelStreamInternal);
chan->stream.internal.cb = cb;
+ chan->stream.internal.closed = false;
topts.data = chan;
// NB: overridden in terminal_check_size if a window is already
// displaying the buffer
@@ -1152,14 +1026,12 @@ static void term_resize(uint16_t width, uint16_t height, void *data)
static void term_close(void *data)
{
Channel *chan = data;
- terminal_destroy(chan->term);
- chan->term = NULL;
+ terminal_destroy(&chan->term);
api_free_luaref(chan->stream.internal.cb);
chan->stream.internal.cb = LUA_NOREF;
channel_decref(chan);
}
-
/// 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
@@ -1293,25 +1165,25 @@ Boolean nvim_paste(String data, Boolean crlf, Integer phase, Error *err)
draining = true;
goto theend;
}
- if (!(State & (CMDLINE | INSERT)) && (phase == -1 || phase == 1)) {
+ if (!(State & (MODE_CMDLINE | MODE_INSERT)) && (phase == -1 || phase == 1)) {
ResetRedobuff();
AppendCharToRedobuff('a'); // Dot-repeat.
}
// vim.paste() decides if client should cancel. Errors do NOT cancel: we
// want to drain remaining chunks (rather than divert them to main input).
cancel = (rv.type == kObjectTypeBoolean && !rv.data.boolean);
- if (!cancel && !(State & CMDLINE)) { // Dot-repeat.
+ if (!cancel && !(State & MODE_CMDLINE)) { // Dot-repeat.
for (size_t i = 0; i < lines.size; i++) {
String s = lines.items[i].data.string;
- assert(data.size <= INT_MAX);
- AppendToRedobuffLit((char_u *)s.data, (int)s.size);
+ assert(s.size <= INT_MAX);
+ AppendToRedobuffLit(s.data, (int)s.size);
// readfile()-style: "\n" is indicated by presence of N+1 item.
if (i + 1 < lines.size) {
AppendCharToRedobuff(NL);
}
}
}
- if (!(State & (CMDLINE | INSERT)) && (phase == -1 || phase == 3)) {
+ if (!(State & (MODE_CMDLINE | MODE_INSERT)) && (phase == -1 || phase == 3)) {
AppendCharToRedobuff(ESC); // Dot-repeat.
}
theend:
@@ -1357,7 +1229,7 @@ void nvim_put(ArrayOf(String) lines, String type, Boolean after, Boolean follow,
goto cleanup;
}
String line = lines.items[i].data.string;
- reg->y_array[i] = (char_u *)xmemdupz(line.data, line.size);
+ reg->y_array[i] = xmemdupz(line.data, line.size);
memchrsub(reg->y_array[i], NUL, NL, line.size);
}
@@ -1422,7 +1294,8 @@ void nvim_unsubscribe(uint64_t channel_id, String event)
Integer nvim_get_color_by_name(String name)
FUNC_API_SINCE(1)
{
- return name_to_color(name.data);
+ int dummy;
+ return name_to_color(name.data, &dummy);
}
/// Returns a map of color names and RGB values.
@@ -1524,10 +1397,11 @@ Dictionary nvim_get_mode(void)
FUNC_API_SINCE(2) FUNC_API_FAST
{
Dictionary rv = ARRAY_DICT_INIT;
- char *modestr = get_mode();
+ char modestr[MODE_MAX_LENGTH];
+ get_mode(modestr);
bool blocked = input_blocking();
- PUT(rv, "mode", STRING_OBJ(cstr_as_string(modestr)));
+ PUT(rv, "mode", STRING_OBJ(cstr_to_string(modestr)));
PUT(rv, "blocking", BOOLEAN_OBJ(blocked));
return rv;
@@ -1561,21 +1435,24 @@ ArrayOf(Dictionary) nvim_get_keymap(uint64_t channel_id, String mode)
/// nmap <nowait> <Space><NL> <Nop>
/// </pre>
///
+/// @param channel_id
/// @param mode Mode short-name (map command prefix: "n", "i", "v", "x", …)
/// or "!" for |:map!|, or empty string for |:map|.
/// @param lhs Left-hand-side |{lhs}| of the mapping.
/// @param rhs Right-hand-side |{rhs}| of the mapping.
-/// @param opts Optional parameters map. Accepts all |:map-arguments|
-/// as keys excluding |<buffer>| but including |noremap| and "desc".
-/// |desc| can be used to give a description to keymap.
-/// When called from Lua, also accepts a "callback" key that takes
-/// a Lua function to call when the mapping is executed.
-/// Values are Booleans. Unknown key is an error.
+/// @param opts Optional parameters map: keys are |:map-arguments|, values
+/// are booleans (default false). Accepts all |:map-arguments| as
+/// keys excluding |<buffer>| but including |noremap| and "desc".
+/// Unknown key is an error. "desc" can be used to give a
+/// description to the mapping. When called from Lua, also accepts a
+/// "callback" key that takes a Lua function to call when the
+/// mapping is executed.
/// @param[out] err Error details, if any.
-void nvim_set_keymap(String mode, String lhs, String rhs, Dict(keymap) *opts, Error *err)
+void nvim_set_keymap(uint64_t channel_id, String mode, String lhs, String rhs, Dict(keymap) *opts,
+ Error *err)
FUNC_API_SINCE(6)
{
- modify_keymap(-1, false, mode, lhs, rhs, opts, err);
+ modify_keymap(channel_id, -1, false, mode, lhs, rhs, opts, err);
}
/// Unmaps a global |mapping| for the given mode.
@@ -1583,25 +1460,10 @@ void nvim_set_keymap(String mode, String lhs, String rhs, Dict(keymap) *opts, Er
/// To unmap a buffer-local mapping, use |nvim_buf_del_keymap()|.
///
/// @see |nvim_set_keymap()|
-void nvim_del_keymap(String mode, String lhs, Error *err)
+void nvim_del_keymap(uint64_t channel_id, String mode, String lhs, Error *err)
FUNC_API_SINCE(6)
{
- nvim_buf_del_keymap(-1, mode, lhs, err);
-}
-
-/// Gets a map of global (non-buffer-local) Ex commands.
-///
-/// Currently only |user-commands| are supported, not builtin Ex commands.
-///
-/// @param opts Optional parameters. Currently only supports
-/// {"builtin":false}
-/// @param[out] err Error details, if any.
-///
-/// @returns Map of maps describing commands.
-Dictionary nvim_get_commands(Dict(get_commands) *opts, Error *err)
- FUNC_API_SINCE(4)
-{
- return nvim_buf_get_commands(-1, opts, err);
+ nvim_buf_del_keymap(channel_id, -1, mode, lhs, err);
}
/// Returns a 2-tuple (Array), where item 0 is the current channel id and item
@@ -1713,7 +1575,7 @@ void nvim_set_client_info(uint64_t channel_id, String name, Dictionary version,
/// - "pty" (optional) Name of pseudoterminal. On a POSIX system this
/// is a device path like "/dev/pts/1". If the name is unknown,
/// the key will still be present if a pty is used (e.g. for
-/// winpty on Windows).
+/// conpty on Windows).
/// - "buffer" (optional) Buffer with connected |terminal| instance.
/// - "client" (optional) Info about the peer (client on the other end of
/// the RPC channel), if provided by it via
@@ -1845,15 +1707,15 @@ static void write_msg(String message, bool to_err)
static char out_line_buf[LINE_BUFFER_SIZE], err_line_buf[LINE_BUFFER_SIZE];
#define PUSH_CHAR(i, pos, line_buf, msg) \
- if (message.data[i] == NL || pos == LINE_BUFFER_SIZE - 1) { \
- line_buf[pos] = NUL; \
+ if (message.data[i] == NL || (pos) == LINE_BUFFER_SIZE - 1) { \
+ (line_buf)[pos] = NUL; \
msg(line_buf); \
- pos = 0; \
+ (pos) = 0; \
continue; \
} \
- line_buf[pos++] = message.data[i];
+ (line_buf)[(pos)++] = message.data[i];
- ++no_wait_return;
+ no_wait_return++;
for (uint32_t i = 0; i < message.size; i++) {
if (got_int) {
break;
@@ -1864,7 +1726,7 @@ static void write_msg(String message, bool to_err)
PUSH_CHAR(i, out_pos, out_line_buf, msg);
}
}
- --no_wait_return;
+ no_wait_return--;
msg_end();
}
@@ -1929,8 +1791,9 @@ Dictionary nvim__stats(void)
{
Dictionary rv = ARRAY_DICT_INIT;
PUT(rv, "fsync", INTEGER_OBJ(g_stats.fsync));
+ PUT(rv, "log_skip", INTEGER_OBJ(g_stats.log_skip));
+ PUT(rv, "lua_refcount", INTEGER_OBJ(nlua_get_global_ref_count()));
PUT(rv, "redraw", INTEGER_OBJ(g_stats.redraw));
- PUT(rv, "lua_refcount", INTEGER_OBJ(nlua_refcount));
return rv;
}
@@ -1964,14 +1827,13 @@ Array nvim_get_proc_children(Integer pid, Error *err)
size_t proc_count;
int rv = os_proc_children((int)pid, &proc_list, &proc_count);
- if (rv != 0) {
+ if (rv == 2) {
// syscall failed (possibly because of kernel options), try shelling out.
DLOG("fallback to vim._os_proc_children()");
Array a = ARRAY_DICT_INIT;
ADD(a, INTEGER_OBJ(pid));
- String s = cstr_to_string("return vim._os_proc_children(select(1, ...))");
+ String s = STATIC_CSTR_AS_STRING("return vim._os_proc_children(...)");
Object o = nlua_exec(s, a, err);
- api_free_string(s);
api_free_array(a);
if (o.type == kObjectTypeArray) {
rvobj = o.data.array;
@@ -2082,8 +1944,8 @@ Array nvim__inspect_cell(Integer grid, Integer row, Integer col, Error *err)
}
}
- if (row < 0 || row >= g->Rows
- || col < 0 || col >= g->Columns) {
+ if (row < 0 || row >= g->rows
+ || col < 0 || col >= g->cols) {
return ret;
}
size_t off = g->line_offset[(size_t)row] + (size_t)col;
@@ -2103,6 +1965,11 @@ void nvim__screenshot(String path)
ui_call_screenshot(path);
}
+Object nvim__unpack(String str, Error *err)
+ FUNC_API_FAST
+{
+ return unpack(str.data, str.size, err);
+}
/// Deletes an uppercase/file named mark. See |mark-motions|.
///
@@ -2160,20 +2027,20 @@ Array nvim_get_mark(String name, Dictionary opts, Error *err)
return rv;
}
- xfmark_T mark = get_global_mark(*name.data);
- pos_T pos = mark.fmark.mark;
+ xfmark_T *mark = mark_get_global(false, *name.data); // false avoids loading the mark buffer
+ pos_T pos = mark->fmark.mark;
bool allocated = false;
int bufnr;
char *filename;
// Marks are from an open buffer it fnum is non zero
- if (mark.fmark.fnum != 0) {
- bufnr = mark.fmark.fnum;
+ if (mark->fmark.fnum != 0) {
+ bufnr = mark->fmark.fnum;
filename = (char *)buflist_nr2name(bufnr, true, true);
allocated = true;
// Marks comes from shada
} else {
- filename = (char *)mark.fname;
+ filename = mark->fname;
bufnr = 0;
}
@@ -2214,10 +2081,11 @@ Array nvim_get_mark(String name, Dictionary opts, Error *err)
/// - winid: (number) |window-ID| of the window to use as context for statusline.
/// - maxwidth: (number) Maximum width of statusline.
/// - fillchar: (string) Character to fill blank spaces in the statusline (see
-/// 'fillchars').
+/// 'fillchars'). Treated as single-width even if it isn't.
/// - highlights: (boolean) Return highlight information.
+/// - use_winbar: (boolean) Evaluate winbar instead of statusline.
/// - use_tabline: (boolean) Evaluate tabline instead of statusline. When |TRUE|, {winid}
-/// is ignored.
+/// is ignored. Mutually exclusive with {use_winbar}.
///
/// @param[out] err Error details, if any.
/// @return Dictionary containing statusline information, with these keys:
@@ -2234,11 +2102,20 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
Dictionary result = ARRAY_DICT_INIT;
int maxwidth;
- char fillchar = 0;
+ int fillchar = 0;
Window window = 0;
+ bool use_winbar = false;
bool use_tabline = false;
bool highlights = false;
+ if (str.size < 2 || memcmp(str.data, "%!", 2)) {
+ const char *const errmsg = check_stl_option(str.data);
+ if (errmsg) {
+ api_set_error(err, kErrorTypeValidation, "%s", errmsg);
+ return result;
+ }
+ }
+
if (HAS_KEY(opts->winid)) {
if (opts->winid.type != kObjectTypeInteger) {
api_set_error(err, kErrorTypeValidation, "winid must be an integer");
@@ -2247,16 +2124,15 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
window = (Window)opts->winid.data.integer;
}
-
if (HAS_KEY(opts->fillchar)) {
- if (opts->fillchar.type != kObjectTypeString || opts->fillchar.data.string.size > 1) {
- api_set_error(err, kErrorTypeValidation, "fillchar must be an ASCII character");
+ if (opts->fillchar.type != kObjectTypeString || opts->fillchar.data.string.size == 0
+ || ((size_t)utf_ptr2len(opts->fillchar.data.string.data)
+ != opts->fillchar.data.string.size)) {
+ api_set_error(err, kErrorTypeValidation, "fillchar must be a single character");
return result;
}
-
- fillchar = opts->fillchar.data.string.data[0];
+ fillchar = utf_ptr2char(opts->fillchar.data.string.data);
}
-
if (HAS_KEY(opts->highlights)) {
highlights = api_object_to_bool(opts->highlights, "highlights", false, err);
@@ -2264,7 +2140,13 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
return result;
}
}
+ if (HAS_KEY(opts->use_winbar)) {
+ use_winbar = api_object_to_bool(opts->use_winbar, "use_winbar", false, err);
+ if (ERROR_SET(err)) {
+ return result;
+ }
+ }
if (HAS_KEY(opts->use_tabline)) {
use_tabline = api_object_to_bool(opts->use_tabline, "use_tabline", false, err);
@@ -2272,6 +2154,10 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
return result;
}
}
+ if (use_winbar && use_tabline) {
+ api_set_error(err, kErrorTypeValidation, "use_winbar and use_tabline are mutually exclusive");
+ return result;
+ }
win_T *wp, *ewp;
@@ -2281,11 +2167,19 @@ 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) {
- int attr;
- fillchar = (char)fillchar_status(&attr, wp);
+ if (use_winbar) {
+ fillchar = wp->w_p_fcs_chars.wbr;
+ } else {
+ int attr;
+ fillchar = fillchar_status(&attr, wp);
+ }
}
}
@@ -2297,7 +2191,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
maxwidth = (int)opts->maxwidth.data.integer;
} else {
- maxwidth = use_tabline ? Columns : wp->w_width;
+ maxwidth = (use_tabline || (!use_winbar && global_stl_height() > 0)) ? Columns : wp->w_width;
}
char buf[MAXPATHL];
@@ -2309,11 +2203,11 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
ewp->w_p_crb = false;
int width = build_stl_str_hl(ewp,
- (char_u *)buf,
+ buf,
sizeof(buf),
- (char_u *)str.data,
+ str.data,
false,
- (char_u)fillchar,
+ fillchar,
maxwidth,
hltab_ptr,
NULL);
@@ -2332,7 +2226,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
// add the default highlight at the beginning of the highlight list
if (hltab->start == NULL || ((char *)hltab->start - buf) != 0) {
Dictionary hl_info = ARRAY_DICT_INIT;
- grpname = get_default_stl_hl(wp);
+ grpname = get_default_stl_hl(wp, use_winbar);
PUT(hl_info, "start", INTEGER_OBJ(0));
PUT(hl_info, "group", CSTR_TO_OBJ(grpname));
@@ -2346,73 +2240,19 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
PUT(hl_info, "start", INTEGER_OBJ((char *)sp->start - buf));
if (sp->userhl == 0) {
- grpname = get_default_stl_hl(wp);
+ grpname = get_default_stl_hl(wp, use_winbar);
} else if (sp->userhl < 0) {
grpname = (char *)syn_id2name(-sp->userhl);
} else {
snprintf(user_group, sizeof(user_group), "User%d", sp->userhl);
grpname = user_group;
}
-
PUT(hl_info, "group", CSTR_TO_OBJ(grpname));
-
ADD(hl_values, DICTIONARY_OBJ(hl_info));
}
-
PUT(result, "highlights", ARRAY_OBJ(hl_values));
}
-
PUT(result, "str", CSTR_TO_OBJ((char *)buf));
return result;
}
-
-/// Create a new user command |user-commands|
-///
-/// {name} is the name of the new command. The name must begin with an uppercase letter.
-///
-/// {command} is the replacement text or Lua function to execute.
-///
-/// Example:
-/// <pre>
-/// :call nvim_add_user_command('SayHello', 'echo "Hello world!"', {})
-/// :SayHello
-/// Hello world!
-/// </pre>
-///
-/// @param name Name of the new user command. Must begin with an uppercase letter.
-/// @param command Replacement command to execute when this user command is executed. When called
-/// from Lua, the command can also be a Lua function. The function is called with a
-/// single table argument that contains the following keys:
-/// - args: (string) The args passed to the command, if any |<args>|
-/// - bang: (boolean) "true" if the command was executed with a ! modifier |<bang>|
-/// - line1: (number) The starting line of the command range |<line1>|
-/// - line2: (number) The final line of the command range |<line2>|
-/// - range: (number) The number of items in the command range: 0, 1, or 2 |<range>|
-/// - count: (number) Any count supplied |<count>|
-/// - reg: (string) The optional register, if specified |<reg>|
-/// - mods: (string) Command modifiers, if any |<mods>|
-/// @param opts Optional command attributes. See |command-attributes| for more details. To use
-/// boolean attributes (such as |:command-bang| or |:command-bar|) set the value to
-/// "true". In addition to the string options listed in |:command-complete|, the
-/// "complete" key also accepts a Lua function which works like the "customlist"
-/// completion mode |:command-completion-customlist|. Additional parameters:
-/// - desc: (string) Used for listing the command when a Lua function is used for
-/// {command}.
-/// - force: (boolean, default true) Override any previous definition.
-/// @param[out] err Error details, if any.
-void nvim_add_user_command(String name, Object command, Dict(user_command) *opts, Error *err)
- FUNC_API_SINCE(9)
-{
- add_user_command(name, command, opts, 0, err);
-}
-
-/// Delete a user-defined command.
-///
-/// @param name Name of the command to delete.
-/// @param[out] err Error details, if any.
-void nvim_del_user_command(String name, Error *err)
- FUNC_API_SINCE(9)
-{
- nvim_buf_del_user_command(-1, name, err);
-}
diff --git a/src/nvim/api/vimscript.c b/src/nvim/api/vimscript.c
index 640144b234..478e146781 100644
--- a/src/nvim/api/vimscript.c
+++ b/src/nvim/api/vimscript.c
@@ -10,34 +10,40 @@
#include "nvim/api/private/helpers.h"
#include "nvim/api/vimscript.h"
#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
#include "nvim/eval/userfunc.h"
#include "nvim/ex_cmds2.h"
+#include "nvim/ops.h"
+#include "nvim/strings.h"
+#include "nvim/vim.h"
#include "nvim/viml/parser/expressions.h"
#include "nvim/viml/parser/parser.h"
+#include "nvim/window.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/vimscript.c.generated.h"
#endif
-/// Executes Vimscript (multiline block of Ex-commands), like anonymous
+/// Executes Vimscript (multiline block of Ex commands), like anonymous
/// |:source|.
///
/// Unlike |nvim_command()| this function supports heredocs, script-scope (s:),
/// etc.
///
-/// On execution error: fails with VimL error, does not update v:errmsg.
+/// On execution error: fails with VimL error, updates v:errmsg.
///
/// @see |execute()|
/// @see |nvim_command()|
+/// @see |nvim_cmd()|
///
/// @param src Vimscript code
/// @param output Capture and return all (non-error, non-shell |:!|) output
/// @param[out] err Error details (Vim error), if any
/// @return Output (non-error, non-shell |:!|) if `output` is true,
/// else empty string.
-String nvim_exec(String src, Boolean output, Error *err)
+String nvim_exec(uint64_t channel_id, String src, Boolean output, Error *err)
FUNC_API_SINCE(7)
{
const int save_msg_silent = msg_silent;
@@ -52,11 +58,16 @@ String nvim_exec(String src, Boolean output, Error *err)
if (output) {
msg_silent++;
}
+
+ const sctx_T save_current_sctx = api_set_sctx(channel_id);
+
do_source_str(src.data, "nvim_exec()");
if (output) {
capture_ga = save_capture_ga;
msg_silent = save_msg_silent;
}
+
+ current_sctx = save_current_sctx;
try_end(err);
if (ERROR_SET(err)) {
@@ -83,13 +94,16 @@ theend:
return (String)STRING_INIT;
}
-/// Executes an ex-command.
+/// Executes an Ex command.
///
-/// On execution error: fails with VimL error, does not update v:errmsg.
+/// On execution error: fails with VimL error, updates v:errmsg.
///
-/// @see |nvim_exec()|
+/// Prefer using |nvim_cmd()| or |nvim_exec()| over this. To evaluate multiple lines of Vim script
+/// or an Ex command directly, use |nvim_exec()|. To construct an Ex command using a structured
+/// format and then execute it, use |nvim_cmd()|. To modify an Ex command before evaluating it, use
+/// |nvim_parse_cmd()| in conjunction with |nvim_cmd()|.
///
-/// @param command Ex-command string
+/// @param command Ex command string
/// @param[out] err Error details (Vim error), if any
void nvim_command(String command, Error *err)
FUNC_API_SINCE(1)
@@ -102,7 +116,7 @@ void nvim_command(String command, Error *err)
/// Evaluates a VimL |expression|.
/// Dictionaries and Lists are recursively expanded.
///
-/// On execution error: fails with VimL error, does not update v:errmsg.
+/// On execution error: fails with VimL error, updates v:errmsg.
///
/// @param expr VimL expression string
/// @param[out] err Error details, if any
@@ -126,7 +140,7 @@ Object nvim_eval(String expr, Error *err)
try_start();
typval_T rettv;
- int ok = eval0((char_u *)expr.data, &rettv, NULL, true);
+ int ok = eval0(expr.data, &rettv, NULL, true);
if (!try_end(err)) {
if (ok == FAIL) {
@@ -191,7 +205,7 @@ static Object _call_function(String fn, Array args, dict_T *self, Error *err)
funcexe.selfdict = self;
// call_func() retval is deceptive, ignore it. Instead we set `msg_list`
// (see above) to capture abort-causing non-exception errors.
- (void)call_func((char_u *)fn.data, (int)fn.size, &rettv, (int)args.size,
+ (void)call_func(fn.data, (int)fn.size, &rettv, (int)args.size,
vim_args, &funcexe);
if (!try_end(err)) {
rv = vim_to_object(&rettv);
@@ -210,7 +224,7 @@ free_vim_args:
/// Calls a VimL function with the given arguments.
///
-/// On execution error: fails with VimL error, does not update v:errmsg.
+/// On execution error: fails with VimL error, updates v:errmsg.
///
/// @param fn Function to call
/// @param args Function arguments packed in an Array
@@ -224,7 +238,7 @@ Object nvim_call_function(String fn, Array args, Error *err)
/// Calls a VimL |Dictionary-function| with the given arguments.
///
-/// On execution error: fails with VimL error, does not update v:errmsg.
+/// On execution error: fails with VimL error, updates v:errmsg.
///
/// @param dict Dictionary, or String evaluating to a VimL |self| dict
/// @param fn Name of the function defined on the VimL dict
@@ -241,7 +255,7 @@ Object nvim_call_dict_function(Object dict, String fn, Array args, Error *err)
switch (dict.type) {
case kObjectTypeString:
try_start();
- if (eval0((char_u *)dict.data.string.data, &rettv, NULL, true) == FAIL) {
+ if (eval0(dict.data.string.data, &rettv, NULL, true) == FAIL) {
api_set_error(err, kErrorTypeException,
"Failed to evaluate dict expression");
}
@@ -284,7 +298,7 @@ Object nvim_call_dict_function(Object dict, String fn, Array args, Error *err)
goto end;
}
fn = (String) {
- .data = (char *)di->di_tv.vval.v_string,
+ .data = di->di_tv.vval.v_string,
.size = STRLEN(di->di_tv.vval.v_string),
};
}
diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c
index 255e1e55cc..d36c5bfb95 100644
--- a/src/nvim/api/win_config.c
+++ b/src/nvim/api/win_config.c
@@ -10,6 +10,7 @@
#include "nvim/api/private/helpers.h"
#include "nvim/api/win_config.h"
#include "nvim/ascii.h"
+#include "nvim/highlight_group.h"
#include "nvim/option.h"
#include "nvim/screen.h"
#include "nvim/strings.h"
@@ -21,7 +22,6 @@
# include "api/win_config.c.generated.h"
#endif
-
/// Open a new window.
///
/// Currently this is used to open floating and external windows.
@@ -125,13 +125,13 @@
/// [ "╔", "═" ,"╗", "║", "╝", "═", "╚", "║" ].
/// If the number of chars are less than eight, they will be repeated. Thus
/// an ASCII border could be specified as
-/// [ "/", "-", "\\", "|" ],
+/// [ "/", "-", \"\\\\\", "|" ],
/// or all chars the same as
/// [ "x" ].
/// An empty string can be used to turn off a specific border, for instance,
/// [ "", "", "", ">", "", "", "", "<" ]
/// will only make vertical borders but not horizontal ones.
-/// By default, `FloatBorder` highlight is used, which links to `VertSplit`
+/// By default, `FloatBorder` highlight is used, which links to `WinSeparator`
/// when not defined. It could also be specified by character:
/// [ {"+", "MyCorner"}, {"x", "MyBorder"} ].
/// - noautocmd: If true then no buffer-related autocommand events such as
@@ -149,7 +149,7 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dict(float_config) *config, E
if (!parse_float_config(config, &fconfig, false, true, err)) {
return 0;
}
- win_T *wp = win_new_float(NULL, fconfig, err);
+ win_T *wp = win_new_float(NULL, false, fconfig, err);
if (!wp) {
return 0;
}
@@ -199,7 +199,7 @@ void nvim_win_set_config(Window window, Dict(float_config) *config, Error *err)
return;
}
if (new_float) {
- if (!win_new_float(win, fconfig, err)) {
+ if (!win_new_float(win, false, fconfig, err)) {
return;
}
redraw_later(win, NOT_VALID);
@@ -263,7 +263,7 @@ Dictionary nvim_win_get_config(Window window, Error *err)
String s = cstrn_to_string((const char *)config->border_chars[i], sizeof(schar_T));
int hi_id = config->border_hl_ids[i];
- char_u *hi_name = syn_id2name(hi_id);
+ char *hi_name = (char *)syn_id2name(hi_id);
if (hi_name[0]) {
ADD(tuple, STRING_OBJ(s));
ADD(tuple, STRING_OBJ(cstr_to_string((const char *)hi_name)));
@@ -325,7 +325,7 @@ static bool parse_float_bufpos(Array bufpos, lpos_T *out)
|| bufpos.items[1].type != kObjectTypeInteger) {
return false;
}
- out->lnum = bufpos.items[0].data.integer;
+ out->lnum = (linenr_T)bufpos.items[0].data.integer;
out->col = (colnr_T)bufpos.items[1].data.integer;
return true;
}
@@ -353,7 +353,7 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err)
if (style.type == kObjectTypeArray) {
Array arr = style.data.array;
size_t size = arr.size;
- if (!size || size > 8 || (size & (size-1))) {
+ if (!size || size > 8 || (size & (size - 1))) {
api_set_error(err, kErrorTypeValidation,
"invalid number of border chars");
return;
@@ -386,12 +386,12 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err)
return;
}
if (string.size
- && mb_string2cells_len((char_u *)string.data, string.size) > 1) {
+ && mb_string2cells_len(string.data, string.size) > 1) {
api_set_error(err, kErrorTypeValidation,
"border chars must be one cell");
return;
}
- size_t len = MIN(string.size, sizeof(*chars)-1);
+ size_t len = MIN(string.size, sizeof(*chars) - 1);
if (len) {
memcpy(chars[i], string.data, len);
}
@@ -399,8 +399,8 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err)
hl_ids[i] = hl_id;
}
while (size < 8) {
- memcpy(chars+size, chars, sizeof(*chars) * size);
- memcpy(hl_ids+size, hl_ids, sizeof(*hl_ids) * size);
+ memcpy(chars + size, chars, sizeof(*chars) * size);
+ memcpy(hl_ids + size, hl_ids, sizeof(*hl_ids) * size);
size <<= 1;
}
if ((chars[7][0] && chars[1][0] && !chars[0][0])
@@ -440,18 +440,18 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err)
static bool parse_title(FloatConfig* out, String s)
{
// The raw title is going to be at most the length of the string.
- char_u* out_title_raw = xcalloc(sizeof(char_u), s.size + 1);
+ char* out_title_raw = xcalloc(sizeof(char), s.size + 1);
size_t out_cursor = 0;
- char_u* data = (char_u*) s.data;
+ char* data = s.data;
size_t out_hlrec_nalloc = 4;
stl_hlrec_t* out_hlrec = xcalloc(sizeof(stl_hlrec_t), out_hlrec_nalloc);
- out_hlrec[0].start = out_title_raw;
+ out_hlrec[0].start = (char*) out_title_raw;
out_hlrec[0].userhl = 0;
size_t out_hl_cur = 1;
- char_u hlbuf[128];
+ char hlbuf[128];
size_t hlbuf_cur = 0;
int hl;
@@ -471,7 +471,7 @@ static bool parse_title(FloatConfig* out, String s)
i ++;
}
hlbuf[hlbuf_cur++] = 0;
- hl = syn_check_group(hlbuf, (int) strlen((char*)hlbuf));
+ hl = syn_check_group(hlbuf, strlen(hlbuf));
hlbuf_cur = 0;
if (out_hl_cur >= out_hlrec_nalloc - 1) { // Leave room for last.
@@ -479,7 +479,7 @@ static bool parse_title(FloatConfig* out, String s)
xrealloc(out_hlrec, sizeof(stl_hlrec_t) * (out_hlrec_nalloc *= 2));
}
- out_hlrec[out_hl_cur].start = out_title_raw + out_cursor;
+ out_hlrec[out_hl_cur].start = (out_title_raw + out_cursor);
out_hlrec[out_hl_cur++].userhl = -hl;
} else {
out_title_raw[out_cursor++] = data[i];
@@ -653,7 +653,6 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig,
return false;
}
-
if (HAS_KEY(config->focusable)) {
fconfig->focusable = api_object_to_bool(config->focusable, "'focusable' key", false, err);
if (ERROR_SET(err)) {
diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c
index 907306da7b..5a4ff70257 100644
--- a/src/nvim/api/window.c
+++ b/src/nvim/api/window.c
@@ -71,6 +71,7 @@ ArrayOf(Integer, 2) nvim_win_get_cursor(Window window, Error *err)
}
/// Sets the (1,0)-indexed cursor position in the window. |api-indexing|
+/// This scrolls the window even if it is not the current one.
///
/// @param window Window handle, or 0 for current window
/// @param pos (row, col) tuple representing the new position
@@ -118,6 +119,7 @@ void nvim_win_set_cursor(Window window, ArrayOf(Integer, 2) pos, Error *err)
update_topline_win(win);
redraw_later(win, VALID);
+ win->w_redr_status = true;
}
/// Gets the window height
@@ -137,8 +139,7 @@ Integer nvim_win_get_height(Window window, Error *err)
return win->w_height;
}
-/// Sets the window height. This will only succeed if the screen is split
-/// horizontally.
+/// Sets the window height.
///
/// @param window Window handle, or 0 for current window
/// @param height Height as a count of rows
@@ -263,44 +264,6 @@ void nvim_win_del_var(Window window, String name, Error *err)
dict_set_var(win->w_vars, name, NIL, true, false, err);
}
-/// Gets a window option value
-///
-/// @param window Window handle, or 0 for current window
-/// @param name Option name
-/// @param[out] err Error details, if any
-/// @return Option value
-Object nvim_win_get_option(Window window, String name, Error *err)
- FUNC_API_SINCE(1)
-{
- win_T *win = find_window_by_handle(window, err);
-
- if (!win) {
- return (Object)OBJECT_INIT;
- }
-
- return get_option_from(win, SREQ_WIN, name, err);
-}
-
-/// Sets a window option value. Passing 'nil' as value deletes the option(only
-/// works if there's a global fallback)
-///
-/// @param channel_id
-/// @param window Window handle, or 0 for current window
-/// @param name Option name
-/// @param value Option value
-/// @param[out] err Error details, if any
-void nvim_win_set_option(uint64_t channel_id, Window window, String name, Object value, Error *err)
- FUNC_API_SINCE(1)
-{
- win_T *win = find_window_by_handle(window, err);
-
- if (!win) {
- return;
- }
-
- set_option_to(channel_id, win, SREQ_WIN, name, value, err);
-}
-
/// Gets the window position in display cells. First position is zero.
///
/// @param window Window handle, or 0 for current window
@@ -372,7 +335,6 @@ Boolean nvim_win_is_valid(Window window)
return ret;
}
-
/// Closes the window and hide the buffer it contains (like |:hide| with a
/// |window-ID|).
///
@@ -395,7 +357,7 @@ void nvim_win_hide(Window window, Error *err)
TryState tstate;
try_enter(&tstate);
if (tabpage == curtab) {
- win_close(win, false);
+ win_close(win, false, false);
} else {
win_close_othertab(win, false, tabpage);
}
@@ -455,17 +417,12 @@ Object nvim_win_call(Window window, LuaRef fun, Error *err)
}
tabpage_T *tabpage = win_find_tabpage(win);
- win_T *save_curwin;
- tabpage_T *save_curtab;
-
try_start();
Object res = OBJECT_INIT;
- if (switch_win_noblock(&save_curwin, &save_curtab, win, tabpage, true) ==
- OK) {
+ WIN_EXECUTE(win, tabpage, {
Array args = ARRAY_DICT_INIT;
res = nlua_call_ref(fun, NULL, args, true, err);
- }
- restore_win_noblock(save_curwin, save_curtab, true);
+ });
try_end(err);
return res;
}
diff --git a/src/nvim/arabic.c b/src/nvim/arabic.c
index 5dcc3d3d0d..06536e6e2b 100644
--- a/src/nvim/arabic.c
+++ b/src/nvim/arabic.c
@@ -5,6 +5,13 @@
///
/// Functions for Arabic language.
///
+/// Author: Nadim Shaikli & Isam Bayazidi
+/// Farsi support and restructuring to make adding new letters easier by Ali
+/// Gholami Rudi. Further work by Ameretat Reith.
+
+/// Sorted list of unicode Arabic characters. Each entry holds the
+/// presentation forms of a letter.
+///
/// Arabic characters are categorized into following types:
///
/// Isolated - iso-8859-6 form char denoted with a_*
@@ -19,12 +26,7 @@
#include "nvim/ascii.h"
#include "nvim/vim.h"
-// Arabic ISO-10646-1 character set definition
-
-// Arabic ISO-8859-6 (subset of 10646; 0600 - 06FF)
-#define a_COMMA 0x060C
-#define a_SEMICOLON 0x061B
-#define a_QUESTION 0x061F
+// Unicode values for Arabic characters.
#define a_HAMZA 0x0621
#define a_ALEF_MADDA 0x0622
#define a_ALEF_HAMZA_ABOVE 0x0623
@@ -62,7 +64,6 @@
#define a_WAW 0x0648
#define a_ALEF_MAKSURA 0x0649
#define a_YEH 0x064a
-
#define a_FATHATAN 0x064b
#define a_DAMMATAN 0x064c
#define a_KASRATAN 0x064d
@@ -71,168 +72,17 @@
#define a_KASRA 0x0650
#define a_SHADDA 0x0651
#define a_SUKUN 0x0652
-
#define a_MADDA_ABOVE 0x0653
#define a_HAMZA_ABOVE 0x0654
#define a_HAMZA_BELOW 0x0655
-#define a_ZERO 0x0660
-#define a_ONE 0x0661
-#define a_TWO 0x0662
-#define a_THREE 0x0663
-#define a_FOUR 0x0664
-#define a_FIVE 0x0665
-#define a_SIX 0x0666
-#define a_SEVEN 0x0667
-#define a_EIGHT 0x0668
-#define a_NINE 0x0669
-#define a_PERCENT 0x066a
-#define a_DECIMAL 0x066b
-#define a_THOUSANDS 0x066c
-#define a_STAR 0x066d
-#define a_MINI_ALEF 0x0670
-// Rest of 8859-6 does not relate to Arabic
+#define a_PEH 0x067e
+#define a_TCHEH 0x0686
+#define a_JEH 0x0698
+#define a_FKAF 0x06a9
+#define a_GAF 0x06af
+#define a_FYEH 0x06cc
-// Arabic Presentation Form-B (subset of 10646; FE70 - FEFF)
-//
-// s -> isolated
-// i -> initial
-// m -> medial
-// f -> final
-#define a_s_FATHATAN 0xfe70
-#define a_m_TATWEEL_FATHATAN 0xfe71
-#define a_s_DAMMATAN 0xfe72
-
-#define a_s_KASRATAN 0xfe74
-
-#define a_s_FATHA 0xfe76
-#define a_m_FATHA 0xfe77
-#define a_s_DAMMA 0xfe78
-#define a_m_DAMMA 0xfe79
-#define a_s_KASRA 0xfe7a
-#define a_m_KASRA 0xfe7b
-#define a_s_SHADDA 0xfe7c
-#define a_m_SHADDA 0xfe7d
-#define a_s_SUKUN 0xfe7e
-#define a_m_SUKUN 0xfe7f
-
-#define a_s_HAMZA 0xfe80
-#define a_s_ALEF_MADDA 0xfe81
-#define a_f_ALEF_MADDA 0xfe82
-#define a_s_ALEF_HAMZA_ABOVE 0xfe83
-#define a_f_ALEF_HAMZA_ABOVE 0xfe84
-#define a_s_WAW_HAMZA 0xfe85
-#define a_f_WAW_HAMZA 0xfe86
-#define a_s_ALEF_HAMZA_BELOW 0xfe87
-#define a_f_ALEF_HAMZA_BELOW 0xfe88
-#define a_s_YEH_HAMZA 0xfe89
-#define a_f_YEH_HAMZA 0xfe8a
-#define a_i_YEH_HAMZA 0xfe8b
-#define a_m_YEH_HAMZA 0xfe8c
-#define a_s_ALEF 0xfe8d
-#define a_f_ALEF 0xfe8e
-#define a_s_BEH 0xfe8f
-#define a_f_BEH 0xfe90
-#define a_i_BEH 0xfe91
-#define a_m_BEH 0xfe92
-#define a_s_TEH_MARBUTA 0xfe93
-#define a_f_TEH_MARBUTA 0xfe94
-#define a_s_TEH 0xfe95
-#define a_f_TEH 0xfe96
-#define a_i_TEH 0xfe97
-#define a_m_TEH 0xfe98
-#define a_s_THEH 0xfe99
-#define a_f_THEH 0xfe9a
-#define a_i_THEH 0xfe9b
-#define a_m_THEH 0xfe9c
-#define a_s_JEEM 0xfe9d
-#define a_f_JEEM 0xfe9e
-#define a_i_JEEM 0xfe9f
-#define a_m_JEEM 0xfea0
-#define a_s_HAH 0xfea1
-#define a_f_HAH 0xfea2
-#define a_i_HAH 0xfea3
-#define a_m_HAH 0xfea4
-#define a_s_KHAH 0xfea5
-#define a_f_KHAH 0xfea6
-#define a_i_KHAH 0xfea7
-#define a_m_KHAH 0xfea8
-#define a_s_DAL 0xfea9
-#define a_f_DAL 0xfeaa
-#define a_s_THAL 0xfeab
-#define a_f_THAL 0xfeac
-#define a_s_REH 0xfead
-#define a_f_REH 0xfeae
-#define a_s_ZAIN 0xfeaf
-#define a_f_ZAIN 0xfeb0
-#define a_s_SEEN 0xfeb1
-#define a_f_SEEN 0xfeb2
-#define a_i_SEEN 0xfeb3
-#define a_m_SEEN 0xfeb4
-#define a_s_SHEEN 0xfeb5
-#define a_f_SHEEN 0xfeb6
-#define a_i_SHEEN 0xfeb7
-#define a_m_SHEEN 0xfeb8
-#define a_s_SAD 0xfeb9
-#define a_f_SAD 0xfeba
-#define a_i_SAD 0xfebb
-#define a_m_SAD 0xfebc
-#define a_s_DAD 0xfebd
-#define a_f_DAD 0xfebe
-#define a_i_DAD 0xfebf
-#define a_m_DAD 0xfec0
-#define a_s_TAH 0xfec1
-#define a_f_TAH 0xfec2
-#define a_i_TAH 0xfec3
-#define a_m_TAH 0xfec4
-#define a_s_ZAH 0xfec5
-#define a_f_ZAH 0xfec6
-#define a_i_ZAH 0xfec7
-#define a_m_ZAH 0xfec8
-#define a_s_AIN 0xfec9
-#define a_f_AIN 0xfeca
-#define a_i_AIN 0xfecb
-#define a_m_AIN 0xfecc
-#define a_s_GHAIN 0xfecd
-#define a_f_GHAIN 0xfece
-#define a_i_GHAIN 0xfecf
-#define a_m_GHAIN 0xfed0
-#define a_s_FEH 0xfed1
-#define a_f_FEH 0xfed2
-#define a_i_FEH 0xfed3
-#define a_m_FEH 0xfed4
-#define a_s_QAF 0xfed5
-#define a_f_QAF 0xfed6
-#define a_i_QAF 0xfed7
-#define a_m_QAF 0xfed8
-#define a_s_KAF 0xfed9
-#define a_f_KAF 0xfeda
-#define a_i_KAF 0xfedb
-#define a_m_KAF 0xfedc
-#define a_s_LAM 0xfedd
-#define a_f_LAM 0xfede
-#define a_i_LAM 0xfedf
-#define a_m_LAM 0xfee0
-#define a_s_MEEM 0xfee1
-#define a_f_MEEM 0xfee2
-#define a_i_MEEM 0xfee3
-#define a_m_MEEM 0xfee4
-#define a_s_NOON 0xfee5
-#define a_f_NOON 0xfee6
-#define a_i_NOON 0xfee7
-#define a_m_NOON 0xfee8
-#define a_s_HEH 0xfee9
-#define a_f_HEH 0xfeea
-#define a_i_HEH 0xfeeb
-#define a_m_HEH 0xfeec
-#define a_s_WAW 0xfeed
-#define a_f_WAW 0xfeee
-#define a_s_ALEF_MAKSURA 0xfeef
-#define a_f_ALEF_MAKSURA 0xfef0
-#define a_s_YEH 0xfef1
-#define a_f_YEH 0xfef2
-#define a_i_YEH 0xfef3
-#define a_m_YEH 0xfef4
#define a_s_LAM_ALEF_MADDA_ABOVE 0xfef5
#define a_f_LAM_ALEF_MADDA_ABOVE 0xfef6
#define a_s_LAM_ALEF_HAMZA_ABOVE 0xfef7
@@ -242,665 +92,201 @@
#define a_s_LAM_ALEF 0xfefb
#define a_f_LAM_ALEF 0xfefc
-#define a_BYTE_ORDER_MARK 0xfeff
+static struct achar {
+ unsigned c;
+ unsigned isolated;
+ unsigned initial;
+ unsigned medial;
+ unsigned final;
+} achars[] = {
+ { a_HAMZA, 0xfe80, 0, 0, 0 },
+ { a_ALEF_MADDA, 0xfe81, 0, 0, 0xfe82 },
+ { a_ALEF_HAMZA_ABOVE, 0xfe83, 0, 0, 0xfe84 },
+ { a_WAW_HAMZA, 0xfe85, 0, 0, 0xfe86 },
+ { a_ALEF_HAMZA_BELOW, 0xfe87, 0, 0, 0xfe88 },
+ { a_YEH_HAMZA, 0xfe89, 0xfe8b, 0xfe8c, 0xfe8a },
+ { a_ALEF, 0xfe8d, 0, 0, 0xfe8e },
+ { a_BEH, 0xfe8f, 0xfe91, 0xfe92, 0xfe90 },
+ { a_TEH_MARBUTA, 0xfe93, 0, 0, 0xfe94 },
+ { a_TEH, 0xfe95, 0xfe97, 0xfe98, 0xfe96 },
+ { a_THEH, 0xfe99, 0xfe9b, 0xfe9c, 0xfe9a },
+ { a_JEEM, 0xfe9d, 0xfe9f, 0xfea0, 0xfe9e },
+ { a_HAH, 0xfea1, 0xfea3, 0xfea4, 0xfea2 },
+ { a_KHAH, 0xfea5, 0xfea7, 0xfea8, 0xfea6 },
+ { a_DAL, 0xfea9, 0, 0, 0xfeaa },
+ { a_THAL, 0xfeab, 0, 0, 0xfeac },
+ { a_REH, 0xfead, 0, 0, 0xfeae },
+ { a_ZAIN, 0xfeaf, 0, 0, 0xfeb0 },
+ { a_SEEN, 0xfeb1, 0xfeb3, 0xfeb4, 0xfeb2 },
+ { a_SHEEN, 0xfeb5, 0xfeb7, 0xfeb8, 0xfeb6 },
+ { a_SAD, 0xfeb9, 0xfebb, 0xfebc, 0xfeba },
+ { a_DAD, 0xfebd, 0xfebf, 0xfec0, 0xfebe },
+ { a_TAH, 0xfec1, 0xfec3, 0xfec4, 0xfec2 },
+ { a_ZAH, 0xfec5, 0xfec7, 0xfec8, 0xfec6 },
+ { a_AIN, 0xfec9, 0xfecb, 0xfecc, 0xfeca },
+ { a_GHAIN, 0xfecd, 0xfecf, 0xfed0, 0xfece },
+ { a_TATWEEL, 0, 0x0640, 0x0640, 0x0640 },
+ { a_FEH, 0xfed1, 0xfed3, 0xfed4, 0xfed2 },
+ { a_QAF, 0xfed5, 0xfed7, 0xfed8, 0xfed6 },
+ { a_KAF, 0xfed9, 0xfedb, 0xfedc, 0xfeda },
+ { a_LAM, 0xfedd, 0xfedf, 0xfee0, 0xfede },
+ { a_MEEM, 0xfee1, 0xfee3, 0xfee4, 0xfee2 },
+ { a_NOON, 0xfee5, 0xfee7, 0xfee8, 0xfee6 },
+ { a_HEH, 0xfee9, 0xfeeb, 0xfeec, 0xfeea },
+ { a_WAW, 0xfeed, 0, 0, 0xfeee },
+ { a_ALEF_MAKSURA, 0xfeef, 0, 0, 0xfef0 },
+ { a_YEH, 0xfef1, 0xfef3, 0xfef4, 0xfef2 },
+ { a_FATHATAN, 0xfe70, 0, 0, 0 },
+ { a_DAMMATAN, 0xfe72, 0, 0, 0 },
+ { a_KASRATAN, 0xfe74, 0, 0, 0 },
+ { a_FATHA, 0xfe76, 0, 0xfe77, 0 },
+ { a_DAMMA, 0xfe78, 0, 0xfe79, 0 },
+ { a_KASRA, 0xfe7a, 0, 0xfe7b, 0 },
+ { a_SHADDA, 0xfe7c, 0, 0xfe7c, 0 },
+ { a_SUKUN, 0xfe7e, 0, 0xfe7f, 0 },
+ { a_MADDA_ABOVE, 0, 0, 0, 0 },
+ { a_HAMZA_ABOVE, 0, 0, 0, 0 },
+ { a_HAMZA_BELOW, 0, 0, 0, 0 },
+ { a_PEH, 0xfb56, 0xfb58, 0xfb59, 0xfb57 },
+ { a_TCHEH, 0xfb7a, 0xfb7c, 0xfb7d, 0xfb7b },
+ { a_JEH, 0xfb8a, 0, 0, 0xfb8b },
+ { a_FKAF, 0xfb8e, 0xfb90, 0xfb91, 0xfb8f },
+ { a_GAF, 0xfb92, 0xfb94, 0xfb95, 0xfb93 },
+ { a_FYEH, 0xfbfc, 0xfbfe, 0xfbff, 0xfbfd },
+};
+#define a_BYTE_ORDER_MARK 0xfeff
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "arabic.c.generated.h"
#endif
-// Returns true if c is an ISO-8859-6 shaped ARABIC letter (user entered).
-static bool A_is_a(int cur_c)
+/// Find the struct achar pointer to the given Arabic char.
+/// Returns NULL if not found.
+static struct achar *find_achar(int c)
{
- switch (cur_c) {
- case a_HAMZA:
- case a_ALEF_MADDA:
- case a_ALEF_HAMZA_ABOVE:
- case a_WAW_HAMZA:
- case a_ALEF_HAMZA_BELOW:
- case a_YEH_HAMZA:
- case a_ALEF:
- case a_BEH:
- case a_TEH_MARBUTA:
- case a_TEH:
- case a_THEH:
- case a_JEEM:
- case a_HAH:
- case a_KHAH:
- case a_DAL:
- case a_THAL:
- case a_REH:
- case a_ZAIN:
- case a_SEEN:
- case a_SHEEN:
- case a_SAD:
- case a_DAD:
- case a_TAH:
- case a_ZAH:
- case a_AIN:
- case a_GHAIN:
- case a_TATWEEL:
- case a_FEH:
- case a_QAF:
- case a_KAF:
- case a_LAM:
- case a_MEEM:
- case a_NOON:
- case a_HEH:
- case a_WAW:
- case a_ALEF_MAKSURA:
- case a_YEH:
- return true;
- }
-
- return false;
-}
-
-// Returns true if c is an Isolated Form-B ARABIC letter
-static bool A_is_s(int cur_c)
-{
- switch (cur_c) {
- case a_s_HAMZA:
- case a_s_ALEF_MADDA:
- case a_s_ALEF_HAMZA_ABOVE:
- case a_s_WAW_HAMZA:
- case a_s_ALEF_HAMZA_BELOW:
- case a_s_YEH_HAMZA:
- case a_s_ALEF:
- case a_s_BEH:
- case a_s_TEH_MARBUTA:
- case a_s_TEH:
- case a_s_THEH:
- case a_s_JEEM:
- case a_s_HAH:
- case a_s_KHAH:
- case a_s_DAL:
- case a_s_THAL:
- case a_s_REH:
- case a_s_ZAIN:
- case a_s_SEEN:
- case a_s_SHEEN:
- case a_s_SAD:
- case a_s_DAD:
- case a_s_TAH:
- case a_s_ZAH:
- case a_s_AIN:
- case a_s_GHAIN:
- case a_s_FEH:
- case a_s_QAF:
- case a_s_KAF:
- case a_s_LAM:
- case a_s_MEEM:
- case a_s_NOON:
- case a_s_HEH:
- case a_s_WAW:
- case a_s_ALEF_MAKSURA:
- case a_s_YEH:
- return true;
+ // using binary search to find c
+ int h = ARRAY_SIZE(achars);
+ int l = 0;
+ while (l < h) {
+ int m = (h + l) / 2;
+ if (achars[m].c == (unsigned)c) {
+ return &achars[m];
+ }
+ if ((unsigned)c < achars[m].c) {
+ h = m;
+ } else {
+ l = m + 1;
+ }
}
-
- return false;
+ return NULL;
}
-// Returns true if c is a Final shape of an ARABIC letter
-static bool A_is_f(int cur_c)
+/// Change shape - from Combination (2 char) to an Isolated
+static int chg_c_laa2i(int hid_c)
{
- switch (cur_c) {
- case a_f_ALEF_MADDA:
- case a_f_ALEF_HAMZA_ABOVE:
- case a_f_WAW_HAMZA:
- case a_f_ALEF_HAMZA_BELOW:
- case a_f_YEH_HAMZA:
- case a_f_ALEF:
- case a_f_BEH:
- case a_f_TEH_MARBUTA:
- case a_f_TEH:
- case a_f_THEH:
- case a_f_JEEM:
- case a_f_HAH:
- case a_f_KHAH:
- case a_f_DAL:
- case a_f_THAL:
- case a_f_REH:
- case a_f_ZAIN:
- case a_f_SEEN:
- case a_f_SHEEN:
- case a_f_SAD:
- case a_f_DAD:
- case a_f_TAH:
- case a_f_ZAH:
- case a_f_AIN:
- case a_f_GHAIN:
- case a_f_FEH:
- case a_f_QAF:
- case a_f_KAF:
- case a_f_LAM:
- case a_f_MEEM:
- case a_f_NOON:
- case a_f_HEH:
- case a_f_WAW:
- case a_f_ALEF_MAKSURA:
- case a_f_YEH:
- case a_f_LAM_ALEF_MADDA_ABOVE:
- case a_f_LAM_ALEF_HAMZA_ABOVE:
- case a_f_LAM_ALEF_HAMZA_BELOW:
- case a_f_LAM_ALEF:
- return true;
- }
- return false;
-}
+ int tempc;
-// Change shape - from ISO-8859-6/Isolated to Form-B Isolated
-static int chg_c_a2s(int cur_c)
-{
- switch (cur_c) {
- case a_HAMZA:
- return a_s_HAMZA;
+ switch (hid_c) {
case a_ALEF_MADDA:
- return a_s_ALEF_MADDA;
+ tempc = a_s_LAM_ALEF_MADDA_ABOVE;
+ break;
case a_ALEF_HAMZA_ABOVE:
- return a_s_ALEF_HAMZA_ABOVE;
- case a_WAW_HAMZA:
- return a_s_WAW_HAMZA;
+ tempc = a_s_LAM_ALEF_HAMZA_ABOVE;
+ break;
case a_ALEF_HAMZA_BELOW:
- return a_s_ALEF_HAMZA_BELOW;
- case a_YEH_HAMZA:
- return a_s_YEH_HAMZA;
+ tempc = a_s_LAM_ALEF_HAMZA_BELOW;
+ break;
case a_ALEF:
- return a_s_ALEF;
- case a_TEH_MARBUTA:
- return a_s_TEH_MARBUTA;
- case a_DAL:
- return a_s_DAL;
- case a_THAL:
- return a_s_THAL;
- case a_REH:
- return a_s_REH;
- case a_ZAIN:
- return a_s_ZAIN;
- case a_TATWEEL:
- return cur_c; // exceptions
- case a_WAW:
- return a_s_WAW;
- case a_ALEF_MAKSURA:
- return a_s_ALEF_MAKSURA;
- case a_BEH:
- return a_s_BEH;
- case a_TEH:
- return a_s_TEH;
- case a_THEH:
- return a_s_THEH;
- case a_JEEM:
- return a_s_JEEM;
- case a_HAH:
- return a_s_HAH;
- case a_KHAH:
- return a_s_KHAH;
- case a_SEEN:
- return a_s_SEEN;
- case a_SHEEN:
- return a_s_SHEEN;
- case a_SAD:
- return a_s_SAD;
- case a_DAD:
- return a_s_DAD;
- case a_TAH:
- return a_s_TAH;
- case a_ZAH:
- return a_s_ZAH;
- case a_AIN:
- return a_s_AIN;
- case a_GHAIN:
- return a_s_GHAIN;
- case a_FEH:
- return a_s_FEH;
- case a_QAF:
- return a_s_QAF;
- case a_KAF:
- return a_s_KAF;
- case a_LAM:
- return a_s_LAM;
- case a_MEEM:
- return a_s_MEEM;
- case a_NOON:
- return a_s_NOON;
- case a_HEH:
- return a_s_HEH;
- case a_YEH:
- return a_s_YEH;
+ tempc = a_s_LAM_ALEF;
+ break;
+ default:
+ tempc = 0;
}
- return 0;
-}
-// Change shape - from ISO-8859-6/Isolated to Initial
-static int chg_c_a2i(int cur_c)
-{
- switch (cur_c) {
- case a_YEH_HAMZA:
- return a_i_YEH_HAMZA;
- case a_HAMZA:
- return a_s_HAMZA; // exceptions
- case a_ALEF_MADDA:
- return a_s_ALEF_MADDA; // exceptions
- case a_ALEF_HAMZA_ABOVE:
- return a_s_ALEF_HAMZA_ABOVE; // exceptions
- case a_WAW_HAMZA:
- return a_s_WAW_HAMZA; // exceptions
- case a_ALEF_HAMZA_BELOW:
- return a_s_ALEF_HAMZA_BELOW; // exceptions
- case a_ALEF:
- return a_s_ALEF; // exceptions
- case a_TEH_MARBUTA:
- return a_s_TEH_MARBUTA; // exceptions
- case a_DAL:
- return a_s_DAL; // exceptions
- case a_THAL:
- return a_s_THAL; // exceptions
- case a_REH:
- return a_s_REH; // exceptions
- case a_ZAIN:
- return a_s_ZAIN; // exceptions
- case a_TATWEEL:
- return cur_c; // exceptions
- case a_WAW:
- return a_s_WAW; // exceptions
- case a_ALEF_MAKSURA:
- return a_s_ALEF_MAKSURA; // exceptions
- case a_BEH:
- return a_i_BEH;
- case a_TEH:
- return a_i_TEH;
- case a_THEH:
- return a_i_THEH;
- case a_JEEM:
- return a_i_JEEM;
- case a_HAH:
- return a_i_HAH;
- case a_KHAH:
- return a_i_KHAH;
- case a_SEEN:
- return a_i_SEEN;
- case a_SHEEN:
- return a_i_SHEEN;
- case a_SAD:
- return a_i_SAD;
- case a_DAD:
- return a_i_DAD;
- case a_TAH:
- return a_i_TAH;
- case a_ZAH:
- return a_i_ZAH;
- case a_AIN:
- return a_i_AIN;
- case a_GHAIN:
- return a_i_GHAIN;
- case a_FEH:
- return a_i_FEH;
- case a_QAF:
- return a_i_QAF;
- case a_KAF:
- return a_i_KAF;
- case a_LAM:
- return a_i_LAM;
- case a_MEEM:
- return a_i_MEEM;
- case a_NOON:
- return a_i_NOON;
- case a_HEH:
- return a_i_HEH;
- case a_YEH:
- return a_i_YEH;
- }
- return 0;
+ return tempc;
}
-// Change shape - from ISO-8859-6/Isolated to Medial
-static int chg_c_a2m(int cur_c)
+/// Change shape - from Combination-Isolated to Final
+static int chg_c_laa2f(int hid_c)
{
- switch (cur_c) {
- case a_HAMZA:
- return a_s_HAMZA; // exception
+ int tempc;
+
+ switch (hid_c) {
case a_ALEF_MADDA:
- return a_f_ALEF_MADDA; // exception
+ tempc = a_f_LAM_ALEF_MADDA_ABOVE;
+ break;
case a_ALEF_HAMZA_ABOVE:
- return a_f_ALEF_HAMZA_ABOVE; // exception
- case a_WAW_HAMZA:
- return a_f_WAW_HAMZA; // exception
+ tempc = a_f_LAM_ALEF_HAMZA_ABOVE;
+ break;
case a_ALEF_HAMZA_BELOW:
- return a_f_ALEF_HAMZA_BELOW; // exception
- case a_YEH_HAMZA:
- return a_m_YEH_HAMZA;
+ tempc = a_f_LAM_ALEF_HAMZA_BELOW;
+ break;
case a_ALEF:
- return a_f_ALEF; // exception
- case a_BEH:
- return a_m_BEH;
- case a_TEH_MARBUTA:
- return a_f_TEH_MARBUTA; // exception
- case a_TEH:
- return a_m_TEH;
- case a_THEH:
- return a_m_THEH;
- case a_JEEM:
- return a_m_JEEM;
- case a_HAH:
- return a_m_HAH;
- case a_KHAH:
- return a_m_KHAH;
- case a_DAL:
- return a_f_DAL; // exception
- case a_THAL:
- return a_f_THAL; // exception
- case a_REH:
- return a_f_REH; // exception
- case a_ZAIN:
- return a_f_ZAIN; // exception
- case a_SEEN:
- return a_m_SEEN;
- case a_SHEEN:
- return a_m_SHEEN;
- case a_SAD:
- return a_m_SAD;
- case a_DAD:
- return a_m_DAD;
- case a_TAH:
- return a_m_TAH;
- case a_ZAH:
- return a_m_ZAH;
- case a_AIN:
- return a_m_AIN;
- case a_GHAIN:
- return a_m_GHAIN;
- case a_TATWEEL:
- return cur_c; // exception
- case a_FEH:
- return a_m_FEH;
- case a_QAF:
- return a_m_QAF;
- case a_KAF:
- return a_m_KAF;
- case a_LAM:
- return a_m_LAM;
- case a_MEEM:
- return a_m_MEEM;
- case a_NOON:
- return a_m_NOON;
- case a_HEH:
- return a_m_HEH;
- case a_WAW:
- return a_f_WAW; // exception
- case a_ALEF_MAKSURA:
- return a_f_ALEF_MAKSURA; // exception
- case a_YEH:
- return a_m_YEH;
+ tempc = a_f_LAM_ALEF;
+ break;
+ default:
+ tempc = 0;
}
- return 0;
+
+ return tempc;
}
-// Change shape - from ISO-8859-6/Isolated to final
-static int chg_c_a2f(int cur_c)
+/// Returns whether it is possible to join the given letters
+static int can_join(int c1, int c2)
{
- // NOTE: these encodings need to be accounted for
- //
- // a_f_ALEF_MADDA;
- // a_f_ALEF_HAMZA_ABOVE;
- // a_f_ALEF_HAMZA_BELOW;
- // a_f_LAM_ALEF_MADDA_ABOVE;
- // a_f_LAM_ALEF_HAMZA_ABOVE;
- // a_f_LAM_ALEF_HAMZA_BELOW;
+ struct achar *a1 = find_achar(c1);
+ struct achar *a2 = find_achar(c2);
- switch (cur_c) {
- case a_HAMZA:
- return a_s_HAMZA; // exception
- case a_ALEF_MADDA:
- return a_f_ALEF_MADDA;
- case a_ALEF_HAMZA_ABOVE:
- return a_f_ALEF_HAMZA_ABOVE;
- case a_WAW_HAMZA:
- return a_f_WAW_HAMZA;
- case a_ALEF_HAMZA_BELOW:
- return a_f_ALEF_HAMZA_BELOW;
- case a_YEH_HAMZA:
- return a_f_YEH_HAMZA;
- case a_ALEF:
- return a_f_ALEF;
- case a_BEH:
- return a_f_BEH;
- case a_TEH_MARBUTA:
- return a_f_TEH_MARBUTA;
- case a_TEH:
- return a_f_TEH;
- case a_THEH:
- return a_f_THEH;
- case a_JEEM:
- return a_f_JEEM;
- case a_HAH:
- return a_f_HAH;
- case a_KHAH:
- return a_f_KHAH;
- case a_DAL:
- return a_f_DAL;
- case a_THAL:
- return a_f_THAL;
- case a_REH:
- return a_f_REH;
- case a_ZAIN:
- return a_f_ZAIN;
- case a_SEEN:
- return a_f_SEEN;
- case a_SHEEN:
- return a_f_SHEEN;
- case a_SAD:
- return a_f_SAD;
- case a_DAD:
- return a_f_DAD;
- case a_TAH:
- return a_f_TAH;
- case a_ZAH:
- return a_f_ZAH;
- case a_AIN:
- return a_f_AIN;
- case a_GHAIN:
- return a_f_GHAIN;
- case a_TATWEEL:
- return cur_c; // exception
- case a_FEH:
- return a_f_FEH;
- case a_QAF:
- return a_f_QAF;
- case a_KAF:
- return a_f_KAF;
- case a_LAM:
- return a_f_LAM;
- case a_MEEM:
- return a_f_MEEM;
- case a_NOON:
- return a_f_NOON;
- case a_HEH:
- return a_f_HEH;
- case a_WAW:
- return a_f_WAW;
- case a_ALEF_MAKSURA:
- return a_f_ALEF_MAKSURA;
- case a_YEH:
- return a_f_YEH;
- }
- return 0;
+ return a1 && a2 && (a1->initial || a1->medial) && (a2->final || a2->medial);
}
-// Change shape - from Initial to Medial
-// This code is unreachable, because for the relevant characters ARABIC_CHAR()
-// is FALSE;
-#if 0
-static int chg_c_i2m(int cur_c)
+/// Check whether we are dealing with a character that could be regarded as an
+/// Arabic combining character, need to check the character before this.
+bool arabic_maycombine(int two)
+ FUNC_ATTR_PURE
{
- switch (cur_c) {
- case a_i_YEH_HAMZA:
- return a_m_YEH_HAMZA;
- case a_i_BEH:
- return a_m_BEH;
- case a_i_TEH:
- return a_m_TEH;
- case a_i_THEH:
- return a_m_THEH;
- case a_i_JEEM:
- return a_m_JEEM;
- case a_i_HAH:
- return a_m_HAH;
- case a_i_KHAH:
- return a_m_KHAH;
- case a_i_SEEN:
- return a_m_SEEN;
- case a_i_SHEEN:
- return a_m_SHEEN;
- case a_i_SAD:
- return a_m_SAD;
- case a_i_DAD:
- return a_m_DAD;
- case a_i_TAH:
- return a_m_TAH;
- case a_i_ZAH:
- return a_m_ZAH;
- case a_i_AIN:
- return a_m_AIN;
- case a_i_GHAIN:
- return a_m_GHAIN;
- case a_i_FEH:
- return a_m_FEH;
- case a_i_QAF:
- return a_m_QAF;
- case a_i_KAF:
- return a_m_KAF;
- case a_i_LAM:
- return a_m_LAM;
- case a_i_MEEM:
- return a_m_MEEM;
- case a_i_NOON:
- return a_m_NOON;
- case a_i_HEH:
- return a_m_HEH;
- case a_i_YEH:
- return a_m_YEH;
+ if (p_arshape && !p_tbidi) {
+ return two == a_ALEF_MADDA
+ || two == a_ALEF_HAMZA_ABOVE
+ || two == a_ALEF_HAMZA_BELOW
+ || two == a_ALEF;
}
- return 0;
+ return false;
}
-#endif
-// Change shape - from Final to Medial
-static int chg_c_f2m(int cur_c)
+/// Check whether we are dealing with Arabic combining characters.
+/// Note: these are NOT really composing characters!
+///
+/// @param one First character.
+/// @param two Character just after "one".
+bool arabic_combine(int one, int two)
+ FUNC_ATTR_PURE
{
- switch (cur_c) {
- // NOTE: these encodings are multi-positional, no ?
- // case a_f_ALEF_MADDA:
- // case a_f_ALEF_HAMZA_ABOVE:
- // case a_f_ALEF_HAMZA_BELOW:
- case a_f_YEH_HAMZA:
- return a_m_YEH_HAMZA;
- case a_f_WAW_HAMZA: // exceptions
- case a_f_ALEF:
- case a_f_TEH_MARBUTA:
- case a_f_DAL:
- case a_f_THAL:
- case a_f_REH:
- case a_f_ZAIN:
- case a_f_WAW:
- case a_f_ALEF_MAKSURA:
- return cur_c;
- case a_f_BEH:
- return a_m_BEH;
- case a_f_TEH:
- return a_m_TEH;
- case a_f_THEH:
- return a_m_THEH;
- case a_f_JEEM:
- return a_m_JEEM;
- case a_f_HAH:
- return a_m_HAH;
- case a_f_KHAH:
- return a_m_KHAH;
- case a_f_SEEN:
- return a_m_SEEN;
- case a_f_SHEEN:
- return a_m_SHEEN;
- case a_f_SAD:
- return a_m_SAD;
- case a_f_DAD:
- return a_m_DAD;
- case a_f_TAH:
- return a_m_TAH;
- case a_f_ZAH:
- return a_m_ZAH;
- case a_f_AIN:
- return a_m_AIN;
- case a_f_GHAIN:
- return a_m_GHAIN;
- case a_f_FEH:
- return a_m_FEH;
- case a_f_QAF:
- return a_m_QAF;
- case a_f_KAF:
- return a_m_KAF;
- case a_f_LAM:
- return a_m_LAM;
- case a_f_MEEM:
- return a_m_MEEM;
- case a_f_NOON:
- return a_m_NOON;
- case a_f_HEH:
- return a_m_HEH;
- case a_f_YEH:
- return a_m_YEH;
- // NOTE: these encodings are multi-positional, no ?
- // case a_f_LAM_ALEF_MADDA_ABOVE:
- // case a_f_LAM_ALEF_HAMZA_ABOVE:
- // case a_f_LAM_ALEF_HAMZA_BELOW:
- // case a_f_LAM_ALEF:
+ if (one == a_LAM) {
+ return arabic_maycombine(two);
}
- return 0;
+ return false;
}
-// Change shape - from Combination (2 char) to an Isolated.
-static int chg_c_laa2i(int hid_c)
+/// A_is_iso returns true if 'c' is an Arabic ISO-8859-6 character
+/// (alphabet/number/punctuation)
+static int A_is_iso(int c)
{
- switch (hid_c) {
- case a_ALEF_MADDA:
- return a_s_LAM_ALEF_MADDA_ABOVE;
- case a_ALEF_HAMZA_ABOVE:
- return a_s_LAM_ALEF_HAMZA_ABOVE;
- case a_ALEF_HAMZA_BELOW:
- return a_s_LAM_ALEF_HAMZA_BELOW;
- case a_ALEF:
- return a_s_LAM_ALEF;
- }
- return 0;
+ return find_achar(c) != NULL;
}
-// Change shape - from Combination-Isolated to Final.
-static int chg_c_laa2f(int hid_c)
+/// A_is_ok returns true if 'c' is an Arabic 10646 (8859-6 or Form-B)
+static int A_is_ok(int c)
{
- switch (hid_c) {
- case a_ALEF_MADDA:
- return a_f_LAM_ALEF_MADDA_ABOVE;
- case a_ALEF_HAMZA_ABOVE:
- return a_f_LAM_ALEF_HAMZA_ABOVE;
- case a_ALEF_HAMZA_BELOW:
- return a_f_LAM_ALEF_HAMZA_BELOW;
- case a_ALEF:
- return a_f_LAM_ALEF;
- }
- return 0;
+ return (A_is_iso(c) || c == a_BYTE_ORDER_MARK);
}
-// Do "half-shaping" on character "c". Return zero if no shaping.
-static int half_shape(int c)
+/// A_is_valid returns true if 'c' is an Arabic 10646 (8859-6 or Form-B)
+/// with some exceptions/exclusions
+static int A_is_valid(int c)
{
- if (A_is_a(c)) {
- return chg_c_a2i(c);
- }
-
- if (A_is_valid(c) && A_is_f(c)) {
- return chg_c_f2m(c);
- }
- return 0;
+ return (A_is_ok(c) && c != a_HAMZA);
}
// Do Arabic shaping on character "c". Returns the shaped character.
@@ -917,37 +303,35 @@ int arabic_shape(int c, int *ccp, int *c1p, int prev_c, int prev_c1, int next_c)
return c;
}
- // half-shape current and previous character
- int shape_c = half_shape(prev_c);
-
int curr_c;
- int curr_laa = A_firstc_laa(c, *c1p);
- int prev_laa = A_firstc_laa(prev_c, prev_c1);
+ int curr_laa = arabic_combine(c, *c1p);
+ int prev_laa = arabic_combine(prev_c, prev_c1);
if (curr_laa) {
- if (A_is_valid(prev_c) && !A_is_f(shape_c) && !A_is_s(shape_c)
- && !prev_laa) {
- curr_c = chg_c_laa2f(curr_laa);
+ if (A_is_valid(prev_c) && can_join(prev_c, a_LAM) && !prev_laa) {
+ curr_c = chg_c_laa2f(*c1p);
} else {
- curr_c = chg_c_laa2i(curr_laa);
+ curr_c = chg_c_laa2i(*c1p);
}
-
// Remove the composing character
*c1p = 0;
- } else if (!A_is_valid(prev_c) && A_is_valid(next_c)) {
- curr_c = chg_c_a2i(c);
- } else if (!shape_c || A_is_f(shape_c) || A_is_s(shape_c) || prev_laa) {
- curr_c = A_is_valid(next_c) ? chg_c_a2i(c) : chg_c_a2s(c);
- } else if (A_is_valid(next_c)) {
-#if 0
- curr_c = A_is_iso(c) ? chg_c_a2m(c) : chg_c_i2m(c);
-#else
- curr_c = A_is_iso(c) ? chg_c_a2m(c) : 0;
-#endif
- } else if (A_is_valid(prev_c)) {
- curr_c = chg_c_a2f(c);
} else {
- curr_c = chg_c_a2s(c);
+ struct achar *curr_a = find_achar(c);
+ int backward_combine = !prev_laa && can_join(prev_c, c);
+ int forward_combine = can_join(c, next_c);
+
+ if (backward_combine && forward_combine) {
+ curr_c = (int)curr_a->medial;
+ }
+ if (backward_combine && !forward_combine) {
+ curr_c = (int)curr_a->final;
+ }
+ if (!backward_combine && forward_combine) {
+ curr_c = (int)curr_a->initial;
+ }
+ if (!backward_combine && !forward_combine) {
+ curr_c = (int)curr_a->isolated;
+ }
}
// Sanity check -- curr_c should, in the future, never be 0.
@@ -957,96 +341,13 @@ int arabic_shape(int c, int *ccp, int *c1p, int prev_c, int prev_c1, int next_c)
}
if ((curr_c != c) && (ccp != NULL)) {
- char_u buf[MB_MAXBYTES + 1];
+ char buf[MB_MAXBYTES + 1];
// Update the first byte of the character
utf_char2bytes(curr_c, buf);
- *ccp = buf[0];
+ *ccp = (uint8_t)buf[0];
}
// Return the shaped character
return curr_c;
}
-
-/// Check whether we are dealing with Arabic combining characters.
-/// Note: these are NOT really composing characters!
-///
-/// @param one First character.
-/// @param two Character just after "one".
-bool arabic_combine(int one, int two)
-{
- if (one == a_LAM) {
- return arabic_maycombine(two);
- }
- return false;
-}
-
-/// Check whether we are dealing with a character that could be regarded as an
-/// Arabic combining character, need to check the character before this.
-bool arabic_maycombine(int two)
-{
- if (p_arshape && !p_tbidi) {
- return two == a_ALEF_MADDA
- || two == a_ALEF_HAMZA_ABOVE
- || two == a_ALEF_HAMZA_BELOW
- || two == a_ALEF;
- }
- return false;
-}
-
-// A_firstc_laa returns first character of LAA combination if it exists
-// in: "c" base character
-// in: "c1" first composing character
-static int A_firstc_laa(int c, int c1)
-{
- if ((c1 != NUL) && (c == a_LAM) && !A_is_harakat(c1)) {
- return c1;
- }
- return 0;
-}
-
-// A_is_harakat returns true if 'c' is an Arabic Harakat character.
-// (harakat/tanween)
-static bool A_is_harakat(int c)
-{
- return c >= a_FATHATAN && c <= a_SUKUN;
-}
-
-// A_is_iso returns true if 'c' is an Arabic ISO-8859-6 character.
-// (alphabet/number/punctuation)
-static bool A_is_iso(int c)
-{
- return ((c >= a_HAMZA && c <= a_GHAIN)
- || (c >= a_TATWEEL && c <= a_HAMZA_BELOW)
- || c == a_MINI_ALEF);
-}
-
-// A_is_formb returns true if 'c' is an Arabic 10646-1 FormB character.
-// (alphabet/number/punctuation)
-static bool A_is_formb(int c)
-{
- return ((c >= a_s_FATHATAN && c <= a_s_DAMMATAN)
- || c == a_s_KASRATAN
- || (c >= a_s_FATHA && c <= a_f_LAM_ALEF)
- || c == a_BYTE_ORDER_MARK);
-}
-
-// A_is_ok returns true if 'c' is an Arabic 10646 (8859-6 or Form-B).
-static bool A_is_ok(int c)
-{
- return A_is_iso(c) || A_is_formb(c);
-}
-
-// A_is_valid returns true if 'c' is an Arabic 10646 (8859-6 or Form-B),
-// with some exceptions/exclusions.
-static bool A_is_valid(int c)
-{
- return A_is_ok(c) && !A_is_special(c);
-}
-
-// A_is_special returns true if 'c' is not a special Arabic character.
-// Specials don't adhere to most of the rules.
-static bool A_is_special(int c)
-{
- return c == a_HAMZA || c == a_s_HAMZA;
-}
diff --git a/src/nvim/arabic.h b/src/nvim/arabic.h
index eaab463777..3c34de1449 100644
--- a/src/nvim/arabic.h
+++ b/src/nvim/arabic.h
@@ -3,12 +3,7 @@
#include <stdbool.h>
-/// Whether c belongs to the range of Arabic characters that might be shaped.
-static inline bool arabic_char(int c)
-{
- // return c >= a_HAMZA && c <= a_MINI_ALEF;
- return c >= 0x0621 && c <= 0x0670;
-}
+#define ARABIC_CHAR(ch) (((ch) & 0xFF00) == 0x0600)
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "arabic.h.generated.h"
diff --git a/src/nvim/ascii.h b/src/nvim/ascii.h
index 2cabaa43ef..b1241166bf 100644
--- a/src/nvim/ascii.h
+++ b/src/nvim/ascii.h
@@ -9,11 +9,11 @@
// Definitions of various common control characters.
-#define CharOrd(x) ((uint8_t)(x) < 'a' \
- ? (uint8_t)(x) - 'A' \
- : (uint8_t)(x) - 'a')
-#define CharOrdLow(x) ((uint8_t)(x) - 'a')
-#define CharOrdUp(x) ((uint8_t)(x) - 'A')
+#define CHAR_ORD(x) ((uint8_t)(x) < 'a' \
+ ? (uint8_t)(x) - 'A' \
+ : (uint8_t)(x) - 'a')
+#define CHAR_ORD_LOW(x) ((uint8_t)(x) - 'a')
+#define CHAR_ORD_UP(x) ((uint8_t)(x) - 'A')
#define ROT13(c, a) (((((c) - (a)) + 13) % 26) + (a))
#define NUL '\000'
@@ -35,8 +35,8 @@
#define POUND 0xA3
-#define Ctrl_chr(x) (TOUPPER_ASC(x) ^ 0x40) // '?' -> DEL, '@' -> ^@, etc.
-#define Meta(x) ((x) | 0x80)
+#define CTRL_CHR(x) (TOUPPER_ASC(x) ^ 0x40) // '?' -> DEL, '@' -> ^@, etc.
+#define META(x) ((x) | 0x80)
#define CTRL_F_STR "\006"
#define CTRL_H_STR "\010"
@@ -75,7 +75,6 @@
#define Ctrl_HAT 30 // ^
#define Ctrl__ 31
-
// Character that separates dir names in a path.
#ifdef BACKSLASH_IN_FILENAME
# define PATHSEP psepc
diff --git a/src/nvim/assert.h b/src/nvim/assert.h
index 65519a8004..ad92d9a2af 100644
--- a/src/nvim/assert.h
+++ b/src/nvim/assert.h
@@ -1,5 +1,3 @@
-// uncrustify:off
-
#ifndef NVIM_ASSERT_H
#define NVIM_ASSERT_H
diff --git a/src/nvim/aucmd.c b/src/nvim/aucmd.c
deleted file mode 100644
index d7f73fa4a1..0000000000
--- a/src/nvim/aucmd.c
+++ /dev/null
@@ -1,123 +0,0 @@
-// This is an open source non-commercial project. Dear PVS-Studio, please check
-// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
-
-#include "nvim/aucmd.h"
-#include "nvim/buffer.h"
-#include "nvim/eval.h"
-#include "nvim/ex_docmd.h"
-#include "nvim/ex_getln.h"
-#include "nvim/fileio.h"
-#include "nvim/main.h"
-#include "nvim/os/os.h"
-#include "nvim/ui.h"
-#include "nvim/vim.h"
-
-#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "aucmd.c.generated.h"
-#endif
-
-void do_autocmd_uienter(uint64_t chanid, bool attached)
-{
- static bool recursive = false;
-
- if (recursive) {
- return; // disallow recursion
- }
- recursive = true;
-
- save_v_event_T save_v_event;
- dict_T *dict = get_v_event(&save_v_event);
- assert(chanid < VARNUMBER_MAX);
- tv_dict_add_nr(dict, S_LEN("chan"), (varnumber_T)chanid);
- tv_dict_set_keys_readonly(dict);
- apply_autocmds(attached ? EVENT_UIENTER : EVENT_UILEAVE,
- NULL, NULL, false, curbuf);
- restore_v_event(dict, &save_v_event);
-
- recursive = false;
-}
-
-void init_default_autocmds(void)
-{
- // open terminals when opening files that start with term://
-#define PROTO "term://"
- do_cmdline_cmd("augroup nvim_terminal");
- do_cmdline_cmd("autocmd BufReadCmd " PROTO "* ++nested "
- "if !exists('b:term_title')|call termopen("
- // Capture the command string
- "matchstr(expand(\"<amatch>\"), "
- "'\\c\\m" PROTO "\\%(.\\{-}//\\%(\\d\\+:\\)\\?\\)\\?\\zs.*'), "
- // capture the working directory
- "{'cwd': expand(get(matchlist(expand(\"<amatch>\"), "
- "'\\c\\m" PROTO "\\(.\\{-}\\)//'), 1, ''))})"
- "|endif");
- do_cmdline_cmd("augroup END");
-#undef PROTO
-
- // limit syntax synchronization in the command window
- do_cmdline_cmd("augroup nvim_cmdwin");
- do_cmdline_cmd("autocmd! CmdwinEnter [:>] syntax sync minlines=1 maxlines=1");
- do_cmdline_cmd("augroup END");
-}
-
-static void focusgained_event(void **argv)
-{
- bool *gainedp = argv[0];
- do_autocmd_focusgained(*gainedp);
- xfree(gainedp);
-}
-void aucmd_schedule_focusgained(bool gained)
-{
- bool *gainedp = xmalloc(sizeof(*gainedp));
- *gainedp = gained;
- loop_schedule_deferred(&main_loop,
- event_create(focusgained_event, 1, gainedp));
-}
-
-static void do_autocmd_focusgained(bool gained)
-{
- static bool recursive = false;
- static Timestamp last_time = (time_t)0;
- bool need_redraw = false;
-
- if (recursive) {
- return; // disallow recursion
- }
- recursive = true;
- need_redraw |= apply_autocmds((gained ? EVENT_FOCUSGAINED : EVENT_FOCUSLOST),
- NULL, NULL, false, curbuf);
-
- // When activated: Check if any file was modified outside of Vim.
- // Only do this when not done within the last two seconds as:
- // 1. Some filesystems have modification time granularity in seconds. Fat32
- // has a granularity of 2 seconds.
- // 2. We could get multiple notifications in a row.
- if (gained && last_time + (Timestamp)2000 < os_now()) {
- need_redraw = check_timestamps(true);
- last_time = os_now();
- }
-
- if (need_redraw) {
- // Something was executed, make sure the cursor is put back where it
- // belongs.
- need_wait_return = false;
-
- if (State & CMDLINE) {
- redrawcmdline();
- } else if ((State & NORMAL) || (State & INSERT)) {
- if (must_redraw != 0) {
- update_screen(0);
- }
-
- setcursor();
- }
-
- ui_flush();
- }
-
- if (need_maketitle) {
- maketitle();
- }
-
- recursive = false;
-}
diff --git a/src/nvim/aucmd.h b/src/nvim/aucmd.h
deleted file mode 100644
index 9a4dd79a78..0000000000
--- a/src/nvim/aucmd.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef NVIM_AUCMD_H
-#define NVIM_AUCMD_H
-
-#include <stdint.h>
-
-#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "aucmd.h.generated.h"
-#endif
-
-#endif // NVIM_AUCMD_H
-
diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua
index 8fe623fc96..93a870fe04 100644
--- a/src/nvim/auevents.lua
+++ b/src/nvim/auevents.lua
@@ -40,6 +40,7 @@ return {
'DiagnosticChanged', -- diagnostics in a buffer were modified
'DiffUpdated', -- diffs have been updated
'DirChanged', -- directory changed
+ 'DirChangedPre', -- directory is going to change
'EncodingChanged', -- after changing the 'encoding' option
'ExitPre', -- before exiting
'FileAppendCmd', -- append to a file using command
@@ -69,6 +70,8 @@ return {
'InsertEnter', -- when entering Insert mode
'InsertLeave', -- just after leaving Insert mode
'InsertLeavePre', -- just before leaving Insert mode
+ 'LspAttach', -- after an LSP client attaches to a buffer
+ 'LspDetach', -- after an LSP client detaches from a buffer
'MenuPopup', -- just before popup menu is displayed
'ModeChanged', -- after changing the mode
'OptionSet', -- after setting any option
@@ -132,18 +135,15 @@ return {
nvim_specific = {
BufModifiedSet=true,
DiagnosticChanged=true,
- DirChanged=true,
+ LspAttach=true,
+ LspDetach=true,
RecordingEnter=true,
RecordingLeave=true,
Signal=true,
- TabClosed=true,
- TabNew=true,
TabNewEntered=true,
TermClose=true,
TermOpen=true,
UIEnter=true,
UILeave=true,
- WinClosed=true,
- WinScrolled=true,
},
}
diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c
index 463bd5e0e6..d51079b515 100644
--- a/src/nvim/autocmd.c
+++ b/src/nvim/autocmd.c
@@ -2,7 +2,9 @@
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
// autocmd.c: Autocommand related functions
+#include <signal.h>
+#include "lauxlib.h"
#include "nvim/api/private/helpers.h"
#include "nvim/ascii.h"
#include "nvim/autocmd.h"
@@ -13,8 +15,11 @@
#include "nvim/eval.h"
#include "nvim/eval/userfunc.h"
#include "nvim/ex_docmd.h"
+#include "nvim/ex_getln.h"
#include "nvim/fileio.h"
#include "nvim/getchar.h"
+#include "nvim/lua/executor.h"
+#include "nvim/map.h"
#include "nvim/option.h"
#include "nvim/os/input.h"
#include "nvim/regexp.h"
@@ -22,12 +27,21 @@
#include "nvim/state.h"
#include "nvim/ui_compositor.h"
#include "nvim/vim.h"
+#include "nvim/window.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "auevents_name_map.generated.h"
# include "autocmd.c.generated.h"
#endif
+// Naming Conventions:
+// - general autocmd behavior start with au_
+// - AutoCmd start with aucmd_
+// - Autocmd.exec stat with aucmd_exec
+// - AutoPat start with aupat_
+// - Groups start with augroup_
+// - Events start with event_
+
//
// The autocommands are stored in a list for each event.
// Autocommands for the same pattern, that are consecutive, are joined
@@ -67,27 +81,50 @@
// Code for automatic commands.
static AutoPatCmd *active_apc_list = NULL; // stack of active autocommands
-/// List of autocmd group names
-static garray_T augroups = { 0, 0, sizeof(char_u *), 10, NULL };
-#define AUGROUP_NAME(i) (((char **)augroups.ga_data)[i])
-#define BUFLOCAL_PAT_LEN 25
+// ID for associating autocmds created via nvim_create_autocmd
+// Used to delete autocmds from nvim_del_autocmd
+static int next_augroup_id = 1;
// use get_deleted_augroup() to get this
static const char *deleted_augroup = NULL;
-// The ID of the current group. Group 0 is the default one.
+// The ID of the current group.
static int current_augroup = AUGROUP_DEFAULT;
-static int au_need_clean = false; // need to delete marked patterns
+// Whether we need to delete marked patterns.
+// While deleting autocmds, they aren't actually remover, just marked.
+static int au_need_clean = false;
-static event_T last_event;
-static int last_group;
static int autocmd_blocked = 0; // block all autocmds
static bool autocmd_nested = false;
static bool autocmd_include_groups = false;
-static char_u *old_termresponse = NULL;
+static char *old_termresponse = NULL;
+
+/// Iterates over all the AutoPats for a particular event
+#define FOR_ALL_AUPATS_IN_EVENT(event, ap) \
+ for (AutoPat *ap = first_autopat[event]; ap != NULL; ap = ap->next) // NOLINT
+
+// Map of autocmd group names and ids.
+// name -> ID
+// ID -> name
+static Map(String, int) map_augroup_name_to_id = MAP_INIT;
+static Map(int, String) map_augroup_id_to_name = MAP_INIT;
+
+static void augroup_map_del(int id, char *name)
+{
+ if (name != NULL) {
+ String key = map_key(String, int)(&map_augroup_name_to_id, cstr_as_string(name));
+ map_del(String, int)(&map_augroup_name_to_id, key);
+ api_free_string(key);
+ }
+ if (id > 0) {
+ String mapped = map_get(int, String)(&map_augroup_id_to_name, id);
+ api_free_string(mapped);
+ map_del(int, String)(&map_augroup_id_to_name, id);
+ }
+}
static inline const char *get_deleted_augroup(void) FUNC_ATTR_ALWAYS_INLINE
{
@@ -98,48 +135,53 @@ static inline const char *get_deleted_augroup(void) FUNC_ATTR_ALWAYS_INLINE
}
// Show the autocommands for one AutoPat.
-static void show_autocmd(AutoPat *ap, event_T event)
+static void aupat_show(AutoPat *ap, event_T event, int previous_group)
{
- AutoCmd *ac;
-
// Check for "got_int" (here and at various places below), which is set
// when "q" has been hit for the "--more--" prompt
if (got_int) {
return;
}
+
// pattern has been removed
if (ap->pat == NULL) {
return;
}
+ char *name = augroup_name(ap->group);
+
msg_putchar('\n');
if (got_int) {
return;
}
- if (event != last_event || ap->group != last_group) {
+ // When switching groups, we need to show the new group information.
+ if (ap->group != previous_group) {
+ // show the group name, if it's not the default group
if (ap->group != AUGROUP_DEFAULT) {
- if (AUGROUP_NAME(ap->group) == NULL) {
+ if (name == NULL) {
msg_puts_attr(get_deleted_augroup(), HL_ATTR(HLF_E));
} else {
- msg_puts_attr(AUGROUP_NAME(ap->group), HL_ATTR(HLF_T));
+ msg_puts_attr(name, HL_ATTR(HLF_T));
}
msg_puts(" ");
}
+ // show the event name
msg_puts_attr(event_nr2name(event), HL_ATTR(HLF_T));
- last_event = event;
- last_group = ap->group;
msg_putchar('\n');
if (got_int) {
return;
}
}
+
msg_col = 4;
msg_outtrans(ap->pat);
- for (ac = ap->cmds; ac != NULL; ac = ac->next) {
- if (ac->cmd == NULL) { // skip removed commands
+ for (AutoCmd *ac = ap->cmds; ac != NULL; ac = ac->next) {
+ // skip removed commands
+ if (aucmd_exec_is_deleted(ac->exec)) {
continue;
}
+
if (msg_col >= 14) {
msg_putchar('\n');
}
@@ -147,7 +189,18 @@ static void show_autocmd(AutoPat *ap, event_T event)
if (got_int) {
return;
}
- msg_outtrans(ac->cmd);
+
+ char *exec_to_string = aucmd_exec_to_string(ac, ac->exec);
+ if (ac->desc != NULL) {
+ size_t msglen = 100;
+ char *msg = (char *)xmallocz(msglen);
+ snprintf(msg, msglen, "%s [%s]", exec_to_string, ac->desc);
+ msg_outtrans(msg);
+ XFREE_CLEAR(msg);
+ } else {
+ msg_outtrans(exec_to_string);
+ }
+ XFREE_CLEAR(exec_to_string);
if (p_verbose > 0) {
last_set_msg(ac->script_ctx);
}
@@ -163,27 +216,111 @@ static void show_autocmd(AutoPat *ap, event_T event)
}
}
+static void au_show_for_all_events(int group, char *pat)
+{
+ FOR_ALL_AUEVENTS(event) {
+ au_show_for_event(group, event, pat);
+ }
+}
+
+static void au_show_for_event(int group, event_T event, char *pat)
+{
+ // Return early if there are no autocmds for this event
+ if (au_event_is_empty(event)) {
+ return;
+ }
+
+ // always need to show group information before the first pattern for the event
+ int previous_group = AUGROUP_ERROR;
+
+ if (*pat == NUL) {
+ FOR_ALL_AUPATS_IN_EVENT(event, ap) {
+ if (group == AUGROUP_ALL || ap->group == group) {
+ aupat_show(ap, event, previous_group);
+ previous_group = ap->group;
+ }
+ }
+ return;
+ }
+
+ char buflocal_pat[BUFLOCAL_PAT_LEN]; // for "<buffer=X>"
+ // Loop through all the specified patterns.
+ int patlen = (int)aucmd_pattern_length(pat);
+ while (patlen) {
+ // detect special <buffer[=X]> buffer-local patterns
+ if (aupat_is_buflocal(pat, patlen)) {
+ // normalize pat into standard "<buffer>#N" form
+ aupat_normalize_buflocal_pat(buflocal_pat, pat, patlen, aupat_get_buflocal_nr(pat, patlen));
+ pat = (char *)buflocal_pat;
+ patlen = (int)STRLEN(buflocal_pat);
+ }
+
+ assert(*pat != NUL);
+
+ // Find AutoPat entries with this pattern.
+ // always goes at or after the last one, so start at the end.
+ FOR_ALL_AUPATS_IN_EVENT(event, ap) {
+ if (ap->pat != NULL) {
+ // Accept a pattern when:
+ // - a group was specified and it's that group
+ // - the length of the pattern matches
+ // - the pattern matches.
+ // For <buffer[=X]>, this condition works because we normalize
+ // all buffer-local patterns.
+ if ((group == AUGROUP_ALL || ap->group == group)
+ && ap->patlen == patlen
+ && STRNCMP(pat, ap->pat, patlen) == 0) {
+ // Show autocmd's for this autopat, or buflocals <buffer=X>
+ aupat_show(ap, event, previous_group);
+ previous_group = ap->group;
+ }
+ }
+ }
+
+ pat = aucmd_next_pattern(pat, (size_t)patlen);
+ patlen = (int)aucmd_pattern_length(pat);
+ }
+}
+
// Mark an autocommand handler for deletion.
-static void au_remove_pat(AutoPat *ap)
+static void aupat_del(AutoPat *ap)
{
XFREE_CLEAR(ap->pat);
ap->buflocal_nr = -1;
au_need_clean = true;
}
+void aupat_del_for_event_and_group(event_T event, int group)
+{
+ FOR_ALL_AUPATS_IN_EVENT(event, ap) {
+ if (ap->group == group) {
+ aupat_del(ap);
+ }
+ }
+
+ au_cleanup();
+}
+
// Mark all commands for a pattern for deletion.
-static void au_remove_cmds(AutoPat *ap)
+static void aupat_remove_cmds(AutoPat *ap)
{
for (AutoCmd *ac = ap->cmds; ac != NULL; ac = ac->next) {
- XFREE_CLEAR(ac->cmd);
+ aucmd_exec_free(&ac->exec);
+
+ if (ac->desc != NULL) {
+ XFREE_CLEAR(ac->desc);
+ }
}
au_need_clean = true;
}
// Delete one command from an autocmd pattern.
-static void au_del_cmd(AutoCmd *ac)
+static void aucmd_del(AutoCmd *ac)
{
- XFREE_CLEAR(ac->cmd);
+ aucmd_exec_free(&ac->exec);
+ if (ac->desc != NULL) {
+ XFREE_CLEAR(ac->desc);
+ }
au_need_clean = true;
}
@@ -191,19 +328,15 @@ static void au_del_cmd(AutoCmd *ac)
/// This is only done when not executing autocommands.
static void au_cleanup(void)
{
- AutoPat *ap, **prev_ap;
- event_T event;
-
if (autocmd_busy || !au_need_clean) {
return;
}
// Loop over all events.
- for (event = (event_T)0; (int)event < NUM_EVENTS;
- event = (event_T)((int)event + 1)) {
+ FOR_ALL_AUEVENTS(event) {
// Loop over all autocommand patterns.
- prev_ap = &(first_autopat[(int)event]);
- for (ap = *prev_ap; ap != NULL; ap = *prev_ap) {
+ AutoPat **prev_ap = &(first_autopat[(int)event]);
+ for (AutoPat *ap = *prev_ap; ap != NULL; ap = *prev_ap) {
bool has_cmd = false;
// Loop over all commands for this pattern.
@@ -211,9 +344,13 @@ static void au_cleanup(void)
for (AutoCmd *ac = *prev_ac; ac != NULL; ac = *prev_ac) {
// Remove the command if the pattern is to be deleted or when
// the command has been marked for deletion.
- if (ap->pat == NULL || ac->cmd == NULL) {
+ if (ap->pat == NULL || aucmd_exec_is_deleted(ac->exec)) {
*prev_ac = ac->next;
- xfree(ac->cmd);
+ aucmd_exec_free(&ac->exec);
+ if (ac->desc != NULL) {
+ XFREE_CLEAR(ac->desc);
+ }
+
xfree(ac);
} else {
has_cmd = true;
@@ -224,7 +361,7 @@ static void au_cleanup(void)
if (ap->pat != NULL && !has_cmd) {
// Pattern was not marked for deletion, but all of its commands were.
// So mark the pattern for deletion.
- au_remove_pat(ap);
+ aupat_del(ap);
}
// Remove the pattern if it has been marked for deletion.
@@ -250,28 +387,30 @@ static void au_cleanup(void)
au_need_clean = false;
}
+// Get the first AutoPat for a particular event.
+AutoPat *au_get_autopat_for_event(event_T event)
+ FUNC_ATTR_PURE
+{
+ return first_autopat[(int)event];
+}
+
// Called when buffer is freed, to remove/invalidate related buffer-local
// autocmds.
void aubuflocal_remove(buf_T *buf)
{
- AutoPat *ap;
- event_T event;
- AutoPatCmd *apc;
-
// invalidate currently executing autocommands
- for (apc = active_apc_list; apc; apc = apc->next) {
+ for (AutoPatCmd *apc = active_apc_list; apc; apc = apc->next) {
if (buf->b_fnum == apc->arg_bufnr) {
apc->arg_bufnr = 0;
}
}
// invalidate buflocals looping through events
- for (event = (event_T)0; (int)event < NUM_EVENTS;
- event = (event_T)((int)event + 1)) {
- // loop over all autocommand patterns
- for (ap = first_autopat[(int)event]; ap != NULL; ap = ap->next) {
+ FOR_ALL_AUEVENTS(event) {
+ FOR_ALL_AUPATS_IN_EVENT(event, ap) {
if (ap->buflocal_nr == buf->b_fnum) {
- au_remove_pat(ap);
+ aupat_del(ap);
+
if (p_verbose >= 6) {
verbose_enter();
smsg(_("auto-removing autocommand: %s <buffer=%d>"),
@@ -284,60 +423,74 @@ void aubuflocal_remove(buf_T *buf)
au_cleanup();
}
-// Add an autocmd group name.
-// Return its ID. Returns AUGROUP_ERROR (< 0) for error.
-static int au_new_group(char_u *name)
+// Add an autocmd group name or return existing group matching name.
+// Return its ID.
+int augroup_add(char *name)
{
- int i = au_find_group(name);
- if (i == AUGROUP_ERROR) { // the group doesn't exist yet, add it.
- // First try using a free entry.
- for (i = 0; i < augroups.ga_len; i++) {
- if (AUGROUP_NAME(i) == NULL) {
- break;
- }
- }
- if (i == augroups.ga_len) {
- ga_grow(&augroups, 1);
- }
+ assert(STRICMP(name, "end") != 0);
- AUGROUP_NAME(i) = xstrdup((char *)name);
- if (i == augroups.ga_len) {
- augroups.ga_len++;
- }
+ int existing_id = augroup_find(name);
+ if (existing_id > 0) {
+ assert(existing_id != AUGROUP_DELETED);
+ return existing_id;
+ }
+
+ if (existing_id == AUGROUP_DELETED) {
+ augroup_map_del(existing_id, name);
}
- return i;
+ int next_id = next_augroup_id++;
+ String name_key = cstr_to_string(name);
+ String name_val = cstr_to_string(name);
+ map_put(String, int)(&map_augroup_name_to_id, name_key, next_id);
+ map_put(int, String)(&map_augroup_id_to_name, next_id, name_val);
+
+ return next_id;
}
-static void au_del_group(char_u *name)
+/// Delete the augroup that matches name.
+/// @param stupid_legacy_mode bool: This parameter determines whether to run the augroup
+/// deletion in the same fashion as `:augroup! {name}` where if there are any remaining
+/// autocmds left in the augroup, it will change the name of the augroup to `--- DELETED ---`
+/// but leave the autocmds existing. These are _separate_ augroups, so if you do this for
+/// multiple augroups, you will have a bunch of `--- DELETED ---` augroups at the same time.
+/// There is no way, as far as I could tell, how to actually delete them at this point as a user
+///
+/// I did not consider this good behavior, so now when NOT in stupid_legacy_mode, we actually
+/// delete these groups and their commands, like you would expect (and don't leave hanging
+/// `--- DELETED ---` groups around)
+void augroup_del(char *name, bool stupid_legacy_mode)
{
- int i = au_find_group(name);
+ int i = augroup_find(name);
if (i == AUGROUP_ERROR) { // the group doesn't exist
semsg(_("E367: No such group: \"%s\""), name);
} else if (i == current_augroup) {
emsg(_("E936: Cannot delete the current group"));
} else {
- event_T event;
- AutoPat *ap;
- int in_use = false;
-
- for (event = (event_T)0; (int)event < NUM_EVENTS;
- event = (event_T)((int)event + 1)) {
- for (ap = first_autopat[(int)event]; ap != NULL; ap = ap->next) {
- if (ap->group == i && ap->pat != NULL) {
- give_warning((char_u *)_("W19: Deleting augroup that is still in use"), true);
- in_use = true;
- event = NUM_EVENTS;
- break;
+ if (stupid_legacy_mode) {
+ FOR_ALL_AUEVENTS(event) {
+ FOR_ALL_AUPATS_IN_EVENT(event, ap) {
+ if (ap->group == i && ap->pat != NULL) {
+ give_warning(_("W19: Deleting augroup that is still in use"), true);
+ map_put(String, int)(&map_augroup_name_to_id, cstr_as_string(name), AUGROUP_DELETED);
+ augroup_map_del(ap->group, NULL);
+ return;
+ }
}
}
- }
- xfree(AUGROUP_NAME(i));
- if (in_use) {
- AUGROUP_NAME(i) = (char *)get_deleted_augroup();
} else {
- AUGROUP_NAME(i) = NULL;
+ FOR_ALL_AUEVENTS(event) {
+ FOR_ALL_AUPATS_IN_EVENT(event, ap) {
+ if (ap->group == i) {
+ aupat_del(ap);
+ }
+ }
+ }
}
+
+ // Remove the group because it's not currently in use.
+ augroup_map_del(i, name);
+ au_cleanup();
}
}
@@ -346,51 +499,97 @@ static void au_del_group(char_u *name)
/// @param name augroup name
///
/// @return the ID or AUGROUP_ERROR (< 0) for error.
-static int au_find_group(const char_u *name)
+int augroup_find(const char *name)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- for (int i = 0; i < augroups.ga_len; i++) {
- if (AUGROUP_NAME(i) != NULL && AUGROUP_NAME(i) != get_deleted_augroup()
- && STRCMP(AUGROUP_NAME(i), name) == 0) {
- return i;
- }
+ int existing_id = map_get(String, int)(&map_augroup_name_to_id, cstr_as_string((char *)name));
+ if (existing_id == AUGROUP_DELETED) {
+ return existing_id;
+ }
+
+ if (existing_id > 0) {
+ return existing_id;
}
+
return AUGROUP_ERROR;
}
+/// Gets the name for a particular group.
+char *augroup_name(int group)
+{
+ assert(group != 0);
+
+ if (group == AUGROUP_DELETED) {
+ return (char *)get_deleted_augroup();
+ }
+
+ if (group == AUGROUP_ALL) {
+ group = current_augroup;
+ }
+
+ // next_augroup_id is the "source of truth" about what autocmds have existed
+ //
+ // The map_size is not the source of truth because groups can be removed from
+ // the map. When this happens, the map size is reduced. That's why this function
+ // relies on next_augroup_id instead.
+
+ // "END" is always considered the last augroup ID.
+ // Used for expand_get_event_name and expand_get_augroup_name
+ if (group == next_augroup_id) {
+ return "END";
+ }
+
+ // If it's larger than the largest group, then it doesn't have a name
+ if (group > next_augroup_id) {
+ return NULL;
+ }
+
+ String key = map_get(int, String)(&map_augroup_id_to_name, group);
+ if (key.data != NULL) {
+ return key.data;
+ }
+
+ // If it's not in the map anymore, then it must have been deleted.
+ return (char *)get_deleted_augroup();
+}
+
/// Return true if augroup "name" exists.
///
/// @param name augroup name
-bool au_has_group(const char_u *name)
+bool augroup_exists(const char *name)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- return au_find_group(name) != AUGROUP_ERROR;
+ return augroup_find(name) > 0;
}
/// ":augroup {name}".
-void do_augroup(char_u *arg, int del_group)
+void do_augroup(char *arg, int del_group)
{
if (del_group) {
if (*arg == NUL) {
emsg(_(e_argreq));
} else {
- au_del_group(arg);
+ augroup_del(arg, true);
}
} else if (STRICMP(arg, "end") == 0) { // ":aug end": back to group 0
current_augroup = AUGROUP_DEFAULT;
} else if (*arg) { // ":aug xxx": switch to group xxx
- int i = au_new_group(arg);
- if (i != AUGROUP_ERROR) {
- current_augroup = i;
- }
+ current_augroup = augroup_add(arg);
} else { // ":aug": list the group names
msg_start();
- for (int i = 0; i < augroups.ga_len; i++) {
- if (AUGROUP_NAME(i) != NULL) {
- msg_puts(AUGROUP_NAME(i));
- msg_puts(" ");
+
+ String name;
+ int value;
+ map_foreach(&map_augroup_name_to_id, name, value, {
+ if (value > 0) {
+ msg_puts(name.data);
+ } else {
+ msg_puts(augroup_name(value));
}
- }
+
+ msg_puts(" ");
+ });
+
msg_clr_eos();
msg_end();
}
@@ -399,35 +598,44 @@ void do_augroup(char_u *arg, int del_group)
#if defined(EXITFREE)
void free_all_autocmds(void)
{
- for (current_augroup = -1; current_augroup < augroups.ga_len;
- current_augroup++) {
- do_autocmd((char_u *)"", true);
- }
-
- for (int i = 0; i < augroups.ga_len; i++) {
- char *const s = ((char **)(augroups.ga_data))[i];
- if ((const char *)s != get_deleted_augroup()) {
- xfree(s);
+ FOR_ALL_AUEVENTS(event) {
+ FOR_ALL_AUPATS_IN_EVENT(event, ap) {
+ aupat_del(ap);
}
}
- ga_clear(&augroups);
+
+ au_need_clean = true;
+ au_cleanup();
+
+ // Delete the augroup_map, including free the data
+ String name;
+ int id;
+ map_foreach(&map_augroup_name_to_id, name, id, {
+ (void)id;
+ api_free_string(name);
+ })
+ map_destroy(String, int)(&map_augroup_name_to_id);
+
+ map_foreach(&map_augroup_id_to_name, id, name, {
+ (void)id;
+ api_free_string(name);
+ })
+ map_destroy(int, String)(&map_augroup_id_to_name);
}
#endif
// Return the event number for event name "start".
// Return NUM_EVENTS if the event name was not found.
// Return a pointer to the next event name in "end".
-static event_T event_name2nr(const char_u *start, char_u **end)
+event_T event_name2nr(const char *start, char **end)
{
- const char_u *p;
+ const char *p;
int i;
- int len;
// the event name ends with end of line, '|', a blank or a comma
- for (p = start; *p && !ascii_iswhite(*p) && *p != ',' && *p != '|'; p++) {
- }
+ for (p = start; *p && !ascii_iswhite(*p) && *p != ',' && *p != '|'; p++) {}
for (i = 0; event_names[i].name != NULL; i++) {
- len = (int)event_names[i].len;
+ int len = (int)event_names[i].len;
if (len == p - start && STRNICMP(event_names[i].name, start, len) == 0) {
break;
}
@@ -435,7 +643,7 @@ static event_T event_name2nr(const char_u *start, char_u **end)
if (*p == ',') {
p++;
}
- *end = (char_u *)p;
+ *end = (char *)p;
if (event_names[i].name == NULL) {
return NUM_EVENTS;
}
@@ -447,12 +655,10 @@ static event_T event_name2nr(const char_u *start, char_u **end)
/// @param[in] event Event to return name for.
///
/// @return Event name, static string. Returns "Unknown" for unknown events.
-static const char *event_nr2name(event_T event)
+const char *event_nr2name(event_T event)
FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_CONST
{
- int i;
-
- for (i = 0; event_names[i].name != NULL; i++) {
+ for (int i = 0; event_names[i].name != NULL; i++) {
if (event_names[i].event == event) {
return event_names[i].name;
}
@@ -460,41 +666,13 @@ static const char *event_nr2name(event_T event)
return "Unknown";
}
-/// Scan over the events. "*" stands for all events.
-/// true when group name was found
-static char_u *find_end_event(char_u *arg, int have_group)
-{
- char_u *pat;
- char_u *p;
-
- if (*arg == '*') {
- if (arg[1] && !ascii_iswhite(arg[1])) {
- semsg(_("E215: Illegal character after *: %s"), arg);
- return NULL;
- }
- pat = arg + 1;
- } else {
- for (pat = arg; *pat && *pat != '|' && !ascii_iswhite(*pat); pat = p) {
- if ((int)event_name2nr(pat, &p) >= NUM_EVENTS) {
- if (have_group) {
- semsg(_("E216: No such event: %s"), pat);
- } else {
- semsg(_("E216: No such group or event: %s"), pat);
- }
- return NULL;
- }
- }
- }
- return pat;
-}
-
/// Return true if "event" is included in 'eventignore'.
///
/// @param event event to check
static bool event_ignored(event_T event)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- char_u *p = p_ei;
+ char *p = (char *)p_ei;
while (*p != NUL) {
if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ',')) {
@@ -511,7 +689,7 @@ static bool event_ignored(event_T event)
// Return OK when the contents of p_ei is valid, FAIL otherwise.
int check_ei(void)
{
- char_u *p = p_ei;
+ char *p = (char *)p_ei;
while (*p) {
if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ',')) {
@@ -530,13 +708,10 @@ int check_ei(void)
// Add "what" to 'eventignore' to skip loading syntax highlighting for every
// buffer loaded into the window. "what" must start with a comma.
// Returns the old value of 'eventignore' in allocated memory.
-char_u *au_event_disable(char *what)
+char *au_event_disable(char *what)
{
- char_u *new_ei;
- char_u *save_ei;
-
- save_ei = vim_strsave(p_ei);
- new_ei = vim_strnsave(p_ei, STRLEN(p_ei) + STRLEN(what));
+ char *save_ei = (char *)vim_strsave(p_ei);
+ char *new_ei = (char *)vim_strnsave(p_ei, STRLEN(p_ei) + STRLEN(what));
if (*what == ',' && *p_ei == NUL) {
STRCPY(new_ei, what + 1);
} else {
@@ -548,7 +723,7 @@ char_u *au_event_disable(char *what)
return save_ei;
}
-void au_event_restore(char_u *old_ei)
+void au_event_restore(char *old_ei)
{
if (old_ei != NULL) {
set_string_option_direct("ei", -1, old_ei, OPT_FREE, SID_NONE);
@@ -588,36 +763,35 @@ void au_event_restore(char_u *old_ei)
// :autocmd * *.c show all autocommands for *.c files.
//
// Mostly a {group} argument can optionally appear before <event>.
-void do_autocmd(char_u *arg_in, int forceit)
+void do_autocmd(char *arg_in, int forceit)
{
- char_u *arg = arg_in;
- char_u *pat;
- char_u *envpat = NULL;
- char_u *cmd;
+ char *arg = arg_in;
+ char *envpat = NULL;
+ char *cmd;
int need_free = false;
- int nested = false;
+ bool nested = false;
bool once = false;
int group;
if (*arg == '|') {
- arg = (char_u *)"";
+ arg = "";
group = AUGROUP_ALL; // no argument, use all groups
} else {
// Check for a legal group name. If not, use AUGROUP_ALL.
- group = au_get_grouparg(&arg);
+ group = arg_augroup_get(&arg);
}
// Scan over the events.
// If we find an illegal name, return here, don't do anything.
- pat = find_end_event(arg, group != AUGROUP_ALL);
+ char *pat = arg_event_skip(arg, group != AUGROUP_ALL);
if (pat == NULL) {
return;
}
pat = skipwhite(pat);
if (*pat == '|') {
- pat = (char_u *)"";
- cmd = (char_u *)"";
+ pat = "";
+ cmd = "";
} else {
// Scan over the pattern. Put a NUL at the end.
cmd = pat;
@@ -646,37 +820,22 @@ void do_autocmd(char_u *arg_in, int forceit)
}
cmd = skipwhite(cmd);
+
+ bool invalid_flags = false;
for (size_t i = 0; i < 2; i++) {
if (*cmd != NUL) {
- // Check for "++once" flag.
- if (STRNCMP(cmd, "++once", 6) == 0 && ascii_iswhite(cmd[6])) {
- if (once) {
- semsg(_(e_duparg2), "++once");
- }
- once = true;
- cmd = skipwhite(cmd + 6);
- }
+ invalid_flags |= arg_autocmd_flag_get(&once, &cmd, "++once", 6);
+ invalid_flags |= arg_autocmd_flag_get(&nested, &cmd, "++nested", 8);
- // Check for "++nested" flag.
- if ((STRNCMP(cmd, "++nested", 8) == 0 && ascii_iswhite(cmd[8]))) {
- if (nested) {
- semsg(_(e_duparg2), "++nested");
- }
- nested = true;
- cmd = skipwhite(cmd + 8);
- }
-
- // Check for the old (deprecated) "nested" flag.
- if (STRNCMP(cmd, "nested", 6) == 0 && ascii_iswhite(cmd[6])) {
- if (nested) {
- semsg(_(e_duparg2), "nested");
- }
- nested = true;
- cmd = skipwhite(cmd + 6);
- }
+ // Check the deprecated "nested" flag.
+ invalid_flags |= arg_autocmd_flag_get(&nested, &cmd, "nested", 6);
}
}
+ if (invalid_flags) {
+ return;
+ }
+
// Find the start of the commands.
// Expand <sfile> in it.
if (*cmd != NUL) {
@@ -688,36 +847,37 @@ void do_autocmd(char_u *arg_in, int forceit)
}
}
+ bool is_showing = !forceit && *cmd == NUL;
+
// Print header when showing autocommands.
- if (!forceit && *cmd == NUL) {
+ if (is_showing) {
// Highlight title
msg_puts_title(_("\n--- Autocommands ---"));
- }
- // Loop over the events.
- last_event = (event_T)-1; // for listing the event name
- last_group = AUGROUP_ERROR; // for listing the group name
- if (*arg == '*' || *arg == NUL || *arg == '|') {
- if (!forceit && *cmd != NUL) {
- emsg(_(e_cannot_define_autocommands_for_all_events));
+ if (*arg == '*' || *arg == '|' || *arg == NUL) {
+ au_show_for_all_events(group, pat);
+ } else {
+ event_T event = event_name2nr(arg, &arg);
+ assert(event < NUM_EVENTS);
+ au_show_for_event(group, event, pat);
+ }
+ } else {
+ if (*arg == '*' || *arg == NUL || *arg == '|') {
+ if (!forceit && *cmd != NUL) {
+ emsg(_(e_cannot_define_autocommands_for_all_events));
+ } else {
+ do_all_autocmd_events(pat, once, nested, cmd, forceit, group);
+ }
} else {
- for (event_T event = (event_T)0; event < NUM_EVENTS;
- event = (event_T)(event + 1)) {
+ while (*arg && *arg != '|' && !ascii_iswhite(*arg)) {
+ event_T event = event_name2nr(arg, &arg);
+ assert(event < NUM_EVENTS);
if (do_autocmd_event(event, pat, once, nested, cmd, forceit, group)
== FAIL) {
break;
}
}
}
- } else {
- while (*arg && *arg != '|' && !ascii_iswhite(*arg)) {
- event_T event = event_name2nr(arg, &arg);
- assert(event < NUM_EVENTS);
- if (do_autocmd_event(event, pat, once, nested, cmd, forceit, group)
- == FAIL) {
- break;
- }
- }
}
if (need_free) {
@@ -726,30 +886,14 @@ void do_autocmd(char_u *arg_in, int forceit)
xfree(envpat);
}
-// Find the group ID in a ":autocmd" or ":doautocmd" argument.
-// The "argp" argument is advanced to the following argument.
-//
-// Returns the group ID or AUGROUP_ALL.
-static int au_get_grouparg(char_u **argp)
+void do_all_autocmd_events(char *pat, bool once, int nested, char *cmd, bool delete, int group)
{
- char_u *group_name;
- char_u *p;
- char_u *arg = *argp;
- int group = AUGROUP_ALL;
-
- for (p = arg; *p && !ascii_iswhite(*p) && *p != '|'; p++) {
- }
- if (p > arg) {
- group_name = vim_strnsave(arg, (size_t)(p - arg));
- group = au_find_group(group_name);
- if (group == AUGROUP_ERROR) {
- group = AUGROUP_ALL; // no match, use all groups
- } else {
- *argp = skipwhite(p); // match, skip over group name
+ FOR_ALL_AUEVENTS(event) {
+ if (do_autocmd_event(event, pat, once, nested, cmd, delete, group)
+ == FAIL) {
+ return;
}
- xfree(group_name);
}
- return group;
}
// do_autocmd() for one event.
@@ -759,232 +903,310 @@ static int au_get_grouparg(char_u **argp)
// If *cmd == NUL: show entries.
// If forceit == true: delete entries.
// If group is not AUGROUP_ALL: only use this group.
-static int do_autocmd_event(event_T event, char_u *pat, bool once, int nested, char_u *cmd,
- int forceit, int group)
+int do_autocmd_event(event_T event, char *pat, bool once, int nested, char *cmd, bool delete,
+ int group)
+ FUNC_ATTR_NONNULL_ALL
{
+ // Cannot be used to show all patterns. See au_show_for_event or au_show_for_all_events
+ assert(*pat != NUL || delete);
+
AutoPat *ap;
AutoPat **prev_ap;
- AutoCmd *ac;
- AutoCmd **prev_ac;
- int brace_level;
- char_u *endpat;
int findgroup;
- int allgroups;
- int patlen;
- int is_buflocal;
int buflocal_nr;
- char_u buflocal_pat[BUFLOCAL_PAT_LEN]; // for "<buffer=X>"
+ char buflocal_pat[BUFLOCAL_PAT_LEN]; // for "<buffer=X>"
+
+ bool is_adding_cmd = *cmd != NUL;
if (group == AUGROUP_ALL) {
findgroup = current_augroup;
} else {
findgroup = group;
}
- allgroups = (group == AUGROUP_ALL && !forceit && *cmd == NUL);
- // Show or delete all patterns for an event.
- if (*pat == NUL) {
- for (ap = first_autopat[event]; ap != NULL; ap = ap->next) {
- if (forceit) { // delete the AutoPat, if it's in the current group
- if (ap->group == findgroup) {
- au_remove_pat(ap);
- }
- } else if (group == AUGROUP_ALL || ap->group == group) {
- show_autocmd(ap, event);
- }
- }
+ // Delete all aupat for an event.
+ if (*pat == NUL && delete) {
+ aupat_del_for_event_and_group(event, findgroup);
+ return OK;
}
// Loop through all the specified patterns.
- for (; *pat; pat = (*endpat == ',' ? endpat + 1 : endpat)) {
- // Find end of the pattern.
- // Watch out for a comma in braces, like "*.\{obj,o\}".
- endpat = pat;
- // ignore single comma
- if (*endpat == ',') {
- continue;
- }
- brace_level = 0;
- for (; *endpat && (*endpat != ',' || brace_level || endpat[-1] == '\\');
- endpat++) {
- if (*endpat == '{') {
- brace_level++;
- } else if (*endpat == '}') {
- brace_level--;
- }
- }
- patlen = (int)(endpat - pat);
-
- // detect special <buflocal[=X]> buffer-local patterns
- is_buflocal = false;
- buflocal_nr = 0;
-
- if (patlen >= 8 && STRNCMP(pat, "<buffer", 7) == 0
- && pat[patlen - 1] == '>') {
- // "<buffer...>": Error will be printed only for addition.
- // printing and removing will proceed silently.
- is_buflocal = true;
- if (patlen == 8) {
- // "<buffer>"
- buflocal_nr = curbuf->b_fnum;
- } else if (patlen > 9 && pat[7] == '=') {
- if (patlen == 13 && STRNICMP(pat, "<buffer=abuf>", 13) == 0) {
- // "<buffer=abuf>"
- buflocal_nr = autocmd_bufnr;
- } else if (skipdigits(pat + 8) == pat + patlen - 1) {
- // "<buffer=123>"
- buflocal_nr = atoi((char *)pat + 8);
- }
- }
- }
+ int patlen = (int)aucmd_pattern_length(pat);
+ while (patlen) {
+ // detect special <buffer[=X]> buffer-local patterns
+ int is_buflocal = aupat_is_buflocal(pat, patlen);
if (is_buflocal) {
+ buflocal_nr = aupat_get_buflocal_nr(pat, patlen);
+
// normalize pat into standard "<buffer>#N" form
- snprintf((char *)buflocal_pat,
- BUFLOCAL_PAT_LEN,
- "<buffer=%d>",
- buflocal_nr);
+ aupat_normalize_buflocal_pat(buflocal_pat, pat, patlen, buflocal_nr);
- pat = buflocal_pat; // can modify pat and patlen
- patlen = (int)STRLEN(buflocal_pat); // but not endpat
+ pat = buflocal_pat;
+ patlen = (int)STRLEN(buflocal_pat);
}
- // Find AutoPat entries with this pattern. When adding a command it
- // always goes at or after the last one, so start at the end.
- if (!forceit && *cmd != NUL && last_autopat[(int)event] != NULL) {
- prev_ap = &last_autopat[(int)event];
- } else {
+ if (delete) {
+ assert(*pat != NUL);
+
+ // Find AutoPat entries with this pattern.
prev_ap = &first_autopat[(int)event];
- }
- while ((ap = *prev_ap) != NULL) {
- if (ap->pat != NULL) {
- // Accept a pattern when:
- // - a group was specified and it's that group, or a group was
- // not specified and it's the current group, or a group was
- // not specified and we are listing
- // - the length of the pattern matches
- // - the pattern matches.
- // For <buffer[=X]>, this condition works because we normalize
- // all buffer-local patterns.
- if ((allgroups || ap->group == findgroup) && ap->patlen == patlen
- && STRNCMP(pat, ap->pat, patlen) == 0) {
- // Remove existing autocommands.
- // If adding any new autocmd's for this AutoPat, don't
- // delete the pattern from the autopat list, append to
- // this list.
- if (forceit) {
- if (*cmd != NUL && ap->next == NULL) {
- au_remove_cmds(ap);
+ while ((ap = *prev_ap) != NULL) {
+ if (ap->pat != NULL) {
+ // Accept a pattern when:
+ // - a group was specified and it's that group
+ // - the length of the pattern matches
+ // - the pattern matches.
+ // For <buffer[=X]>, this condition works because we normalize
+ // all buffer-local patterns.
+ if (ap->group == findgroup
+ && ap->patlen == patlen
+ && STRNCMP(pat, ap->pat, patlen) == 0) {
+ // Remove existing autocommands.
+ // If adding any new autocmd's for this AutoPat, don't
+ // delete the pattern from the autopat list, append to
+ // this list.
+ if (is_adding_cmd && ap->next == NULL) {
+ aupat_remove_cmds(ap);
break;
}
- au_remove_pat(ap);
- } else if (*cmd == NUL) {
- // Show autocmd's for this autopat, or buflocals <buffer=X>
- show_autocmd(ap, event);
- } else if (ap->next == NULL) {
- // Add autocmd to this autopat, if it's the last one.
- break;
+ aupat_del(ap);
}
}
+ prev_ap = &ap->next;
}
- prev_ap = &ap->next;
}
- // Add a new command.
- if (*cmd != NUL) {
- // If the pattern we want to add a command to does appear at the
- // end of the list (or not is not in the list at all), add the
- // pattern at the end of the list.
- if (ap == NULL) {
- // refuse to add buffer-local ap if buffer number is invalid
- if (is_buflocal
- && (buflocal_nr == 0 || buflist_findnr(buflocal_nr) == NULL)) {
- semsg(_("E680: <buffer=%d>: invalid buffer number "), buflocal_nr);
- return FAIL;
- }
+ if (is_adding_cmd) {
+ AucmdExecutable exec = AUCMD_EXECUTABLE_INIT;
+ exec.type = CALLABLE_EX;
+ exec.callable.cmd = cmd;
+ autocmd_register(0, event, pat, patlen, group, once, nested, NULL, exec);
+ }
- ap = xmalloc(sizeof(AutoPat));
- ap->pat = vim_strnsave(pat, (size_t)patlen);
- ap->patlen = patlen;
+ pat = aucmd_next_pattern(pat, (size_t)patlen);
+ patlen = (int)aucmd_pattern_length(pat);
+ }
- if (is_buflocal) {
- ap->buflocal_nr = buflocal_nr;
- ap->reg_prog = NULL;
- } else {
- char_u *reg_pat;
+ au_cleanup(); // may really delete removed patterns/commands now
+ return OK;
+}
- ap->buflocal_nr = 0;
- reg_pat = file_pat_to_reg_pat(pat, endpat, &ap->allow_dirs, true);
- if (reg_pat != NULL) {
- ap->reg_prog = vim_regcomp(reg_pat, RE_MAGIC);
- }
- xfree(reg_pat);
- if (reg_pat == NULL || ap->reg_prog == NULL) {
- xfree(ap->pat);
- xfree(ap);
- return FAIL;
- }
- }
+int autocmd_register(int64_t id, event_T event, char *pat, int patlen, int group, bool once,
+ bool nested, char *desc, AucmdExecutable aucmd)
+{
+ // 0 is not a valid group.
+ assert(group != 0);
- // need to initialize last_mode for the first ModeChanged autocmd
- if (event == EVENT_MODECHANGED && !has_event(EVENT_MODECHANGED)) {
- xfree(last_mode);
- last_mode = get_mode();
- }
+ AutoPat *ap;
+ AutoPat **prev_ap;
+ AutoCmd *ac;
+ int findgroup;
+ char buflocal_pat[BUFLOCAL_PAT_LEN]; // for "<buffer=X>"
- // If the event is CursorMoved, update the last cursor position
- // position to avoid immediately triggering the autocommand
- if (event == EVENT_CURSORMOVED && !has_event(EVENT_CURSORMOVED)) {
- curwin->w_last_cursormoved = curwin->w_cursor;
- }
+ if (patlen > (int)STRLEN(pat)) {
+ return FAIL;
+ }
- ap->cmds = NULL;
- *prev_ap = ap;
- last_autopat[(int)event] = ap;
- ap->next = NULL;
- if (group == AUGROUP_ALL) {
- ap->group = current_augroup;
- } else {
- ap->group = group;
+ if (group == AUGROUP_ALL) {
+ findgroup = current_augroup;
+ } else {
+ findgroup = group;
+ }
+
+ // detect special <buffer[=X]> buffer-local patterns
+ int is_buflocal = aupat_is_buflocal(pat, patlen);
+ int buflocal_nr = 0;
+
+ if (is_buflocal) {
+ buflocal_nr = aupat_get_buflocal_nr(pat, patlen);
+
+ // normalize pat into standard "<buffer>#N" form
+ aupat_normalize_buflocal_pat(buflocal_pat, pat, patlen, buflocal_nr);
+
+ pat = buflocal_pat;
+ patlen = (int)STRLEN(buflocal_pat);
+ }
+
+ // always goes at or after the last one, so start at the end.
+ if (last_autopat[(int)event] != NULL) {
+ prev_ap = &last_autopat[(int)event];
+ } else {
+ prev_ap = &first_autopat[(int)event];
+ }
+
+ while ((ap = *prev_ap) != NULL) {
+ if (ap->pat != NULL) {
+ // Accept a pattern when:
+ // - a group was specified and it's that group
+ // - the length of the pattern matches
+ // - the pattern matches.
+ // For <buffer[=X]>, this condition works because we normalize
+ // all buffer-local patterns.
+ if (ap->group == findgroup
+ && ap->patlen == patlen
+ && STRNCMP(pat, ap->pat, patlen) == 0) {
+ if (ap->next == NULL) {
+ // Add autocmd to this autopat, if it's the last one.
+ break;
}
}
+ }
+ prev_ap = &ap->next;
+ }
+
+ // If the pattern we want to add a command to does appear at the
+ // end of the list (or not is not in the list at all), add the
+ // pattern at the end of the list.
+ if (ap == NULL) {
+ // refuse to add buffer-local ap if buffer number is invalid
+ if (is_buflocal
+ && (buflocal_nr == 0 || buflist_findnr(buflocal_nr) == NULL)) {
+ semsg(_("E680: <buffer=%d>: invalid buffer number "), buflocal_nr);
+ return FAIL;
+ }
+
+ ap = xmalloc(sizeof(AutoPat));
+ ap->pat = xstrnsave(pat, (size_t)patlen);
+ ap->patlen = patlen;
+
+ if (is_buflocal) {
+ ap->buflocal_nr = buflocal_nr;
+ ap->reg_prog = NULL;
+ } else {
+ char *reg_pat;
- // Add the autocmd at the end of the AutoCmd list.
- prev_ac = &(ap->cmds);
- while ((ac = *prev_ac) != NULL) {
- prev_ac = &ac->next;
+ ap->buflocal_nr = 0;
+ reg_pat = file_pat_to_reg_pat(pat, pat + patlen, &ap->allow_dirs, true);
+ if (reg_pat != NULL) {
+ ap->reg_prog = vim_regcomp(reg_pat, RE_MAGIC);
+ }
+ xfree(reg_pat);
+ if (reg_pat == NULL || ap->reg_prog == NULL) {
+ xfree(ap->pat);
+ xfree(ap);
+ return FAIL;
}
- ac = xmalloc(sizeof(AutoCmd));
- ac->cmd = vim_strsave(cmd);
- ac->script_ctx = current_sctx;
- ac->script_ctx.sc_lnum += sourcing_lnum;
- ac->next = NULL;
- *prev_ac = ac;
- ac->once = once;
- ac->nested = nested;
+ }
+
+ // need to initialize last_mode for the first ModeChanged autocmd
+ if (event == EVENT_MODECHANGED && !has_event(EVENT_MODECHANGED)) {
+ get_mode(last_mode);
+ }
+
+ // If the event is CursorMoved, update the last cursor position
+ // position to avoid immediately triggering the autocommand
+ if (event == EVENT_CURSORMOVED && !has_event(EVENT_CURSORMOVED)) {
+ curwin->w_last_cursormoved = curwin->w_cursor;
+ }
+
+ // Initialize the fields checked by the WinScrolled trigger to
+ // stop it from firing right after the first autocmd is defined.
+ if (event == EVENT_WINSCROLLED && !has_event(EVENT_WINSCROLLED)) {
+ curwin->w_last_topline = curwin->w_topline;
+ curwin->w_last_leftcol = curwin->w_leftcol;
+ curwin->w_last_width = curwin->w_width;
+ curwin->w_last_height = curwin->w_height;
+ }
+
+ ap->cmds = NULL;
+ *prev_ap = ap;
+ last_autopat[(int)event] = ap;
+ ap->next = NULL;
+ if (group == AUGROUP_ALL) {
+ ap->group = current_augroup;
+ } else {
+ ap->group = group;
}
}
- au_cleanup(); // may really delete removed patterns/commands now
+ // Add the autocmd at the end of the AutoCmd list.
+ AutoCmd **prev_ac = &(ap->cmds);
+ while ((ac = *prev_ac) != NULL) {
+ prev_ac = &ac->next;
+ }
+
+ ac = xmalloc(sizeof(AutoCmd));
+ *prev_ac = ac;
+
+ ac->id = id;
+ ac->exec = aucmd_exec_copy(aucmd);
+ ac->script_ctx = current_sctx;
+ ac->script_ctx.sc_lnum += sourcing_lnum;
+ nlua_set_sctx(&ac->script_ctx);
+ ac->next = NULL;
+ ac->once = once;
+ ac->nested = nested;
+ ac->desc = NULL;
+
+ // TODO(tjdevries): What to do about :autocmd and where/how to show lua stuffs there.
+ // perhaps: <lua>DESCRIPTION or similar
+ if (desc != NULL) {
+ ac->desc = xstrdup(desc);
+ }
+
return OK;
}
+size_t aucmd_pattern_length(char *pat)
+ FUNC_ATTR_PURE
+{
+ if (*pat == NUL) {
+ return 0;
+ }
+
+ char *endpat;
+
+ for (; *pat; pat = endpat + 1) {
+ // Find end of the pattern.
+ // Watch out for a comma in braces, like "*.\{obj,o\}".
+ endpat = pat;
+ // ignore single comma
+ if (*endpat == ',') {
+ continue;
+ }
+ int brace_level = 0;
+ for (; *endpat && (*endpat != ',' || brace_level || endpat[-1] == '\\');
+ endpat++) {
+ if (*endpat == '{') {
+ brace_level++;
+ } else if (*endpat == '}') {
+ brace_level--;
+ }
+ }
+
+ return (size_t)(endpat - pat);
+ }
+
+ return STRLEN(pat);
+}
+
+char *aucmd_next_pattern(char *pat, size_t patlen)
+ FUNC_ATTR_PURE
+{
+ pat = pat + patlen;
+ if (*pat == ',') {
+ pat = pat + 1;
+ }
+
+ return pat;
+}
+
/// Implementation of ":doautocmd [group] event [fname]".
/// Return OK for success, FAIL for failure;
///
/// @param do_msg give message for no matching autocmds?
-int do_doautocmd(char_u *arg, bool do_msg, bool *did_something)
+int do_doautocmd(char *arg_start, bool do_msg, bool *did_something)
{
- char_u *fname;
+ char *arg = arg_start;
int nothing_done = true;
- int group;
if (did_something != NULL) {
*did_something = false;
}
// Check for a legal group name. If not, use AUGROUP_ALL.
- group = au_get_grouparg(&arg);
+ int group = arg_augroup_get(&arg);
if (*arg == '*') {
emsg(_("E217: Can't execute autocommands for ALL events"));
@@ -993,7 +1215,7 @@ int do_doautocmd(char_u *arg, bool do_msg, bool *did_something)
// Scan over the events.
// If we find an illegal name, return here, don't do anything.
- fname = find_end_event(arg, group != AUGROUP_ALL);
+ char *fname = arg_event_skip(arg, group != AUGROUP_ALL);
if (fname == NULL) {
return FAIL;
}
@@ -1003,13 +1225,13 @@ int do_doautocmd(char_u *arg, bool do_msg, bool *did_something)
// Loop over the events.
while (*arg && !ends_excmd(*arg) && !ascii_iswhite(*arg)) {
if (apply_autocmds_group(event_name2nr(arg, &arg), fname, NULL, true, group,
- curbuf, NULL)) {
+ curbuf, NULL, NULL)) {
nothing_done = false;
}
}
- if (nothing_done && do_msg) {
- msg(_("No matching autocommands"));
+ if (nothing_done && do_msg && !aborting()) {
+ smsg(_("No matching autocommands: %s"), arg_start);
}
if (did_something != NULL) {
*did_something = !nothing_done;
@@ -1023,7 +1245,7 @@ void ex_doautoall(exarg_T *eap)
{
int retval = OK;
aco_save_T aco;
- char_u *arg = eap->arg;
+ char *arg = eap->arg;
int call_do_modelines = check_nomodeline(&arg);
bufref_T bufref;
bool did_aucmd;
@@ -1069,8 +1291,6 @@ void ex_doautoall(exarg_T *eap)
do_modelines(0);
}
}
-
- check_cursor(); // just in case lines got deleted
}
/// Check *argp for <nomodeline>. When it is present return false, otherwise
@@ -1078,7 +1298,7 @@ void ex_doautoall(exarg_T *eap)
/// called when true is returned.
///
/// @param[in,out] argp argument string
-bool check_nomodeline(char_u **argp)
+bool check_nomodeline(char **argp)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
if (STRNCMP(*argp, "<nomodeline>", 12) == 0) {
@@ -1160,7 +1380,10 @@ void aucmd_prepbuf(aco_save_T *aco, buf_T *buf)
// Prevent chdir() call in win_enter_ext(), through do_autochdir()
int save_acd = p_acd;
p_acd = false;
+ // no redrawing and don't set the window title
+ RedrawingDisabled++;
win_enter(aucmd_win, false);
+ RedrawingDisabled--;
p_acd = save_acd;
unblock_autocmds();
curwin = aucmd_win;
@@ -1168,6 +1391,10 @@ void aucmd_prepbuf(aco_save_T *aco, buf_T *buf)
curbuf = buf;
aco->new_curwin_handle = curwin->handle;
set_bufref(&aco->new_curbuf, curbuf);
+
+ // disable the Visual area, the position may be invalid in another buffer
+ aco->save_VIsual_active = VIsual_active;
+ VIsual_active = false;
}
/// Cleanup after executing autocommands for a (hidden) buffer.
@@ -1264,6 +1491,12 @@ win_found:
check_cursor();
}
}
+
+ check_cursor(); // just in case lines got deleted
+ VIsual_active = aco->save_VIsual_active;
+ if (VIsual_active) {
+ check_pos(curbuf, &VIsual);
+ }
}
/// Execute autocommands for "event" and file name "fname".
@@ -1275,10 +1508,9 @@ win_found:
/// @param buf Buffer for <abuf>
///
/// @return true if some commands were executed.
-bool apply_autocmds(event_T event, char_u *fname, char_u *fname_io, bool force, buf_T *buf)
+bool apply_autocmds(event_T event, char *fname, char *fname_io, bool force, buf_T *buf)
{
- return apply_autocmds_group(event, fname, fname_io, force, AUGROUP_ALL, buf,
- NULL);
+ return apply_autocmds_group(event, fname, fname_io, force, AUGROUP_ALL, buf, NULL, NULL);
}
/// Like apply_autocmds(), but with extra "eap" argument. This takes care of
@@ -1292,11 +1524,10 @@ bool apply_autocmds(event_T event, char_u *fname, char_u *fname_io, bool force,
/// @param exarg Ex command arguments
///
/// @return true if some commands were executed.
-bool apply_autocmds_exarg(event_T event, char_u *fname, char_u *fname_io, bool force, buf_T *buf,
+bool apply_autocmds_exarg(event_T event, char *fname, char *fname_io, bool force, buf_T *buf,
exarg_T *eap)
{
- return apply_autocmds_group(event, fname, fname_io, force, AUGROUP_ALL, buf,
- eap);
+ return apply_autocmds_group(event, fname, fname_io, force, AUGROUP_ALL, buf, eap, NULL);
}
/// Like apply_autocmds(), but handles the caller's retval. If the script
@@ -1312,7 +1543,7 @@ bool apply_autocmds_exarg(event_T event, char_u *fname, char_u *fname_io, bool f
/// @param[in,out] retval caller's retval
///
/// @return true if some autocommands were executed
-bool apply_autocmds_retval(event_T event, char_u *fname, char_u *fname_io, bool force, buf_T *buf,
+bool apply_autocmds_retval(event_T event, char *fname, char *fname_io, bool force, buf_T *buf,
int *retval)
{
if (should_abort(*retval)) {
@@ -1320,7 +1551,7 @@ bool apply_autocmds_retval(event_T event, char_u *fname, char_u *fname_io, bool
}
bool did_cmd = apply_autocmds_group(event, fname, fname_io, force,
- AUGROUP_ALL, buf, NULL);
+ AUGROUP_ALL, buf, NULL, NULL);
if (did_cmd && aborting()) {
*retval = FAIL;
}
@@ -1339,19 +1570,17 @@ bool has_event(event_T event) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
/// the current mode.
bool has_cursorhold(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- return has_event((get_real_state() == NORMAL_BUSY ? EVENT_CURSORHOLD : EVENT_CURSORHOLDI));
+ return has_event((get_real_state() == MODE_NORMAL_BUSY ? EVENT_CURSORHOLD : EVENT_CURSORHOLDI));
// return first_autopat[] != NULL;
}
/// Return true if the CursorHold/CursorHoldI event can be triggered.
bool trigger_cursorhold(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- int state;
-
if (!did_cursorhold && has_cursorhold() && reg_recording == 0
&& typebuf.tb_len == 0 && !ins_compl_active()) {
- state = get_real_state();
- if (state == NORMAL_BUSY || (state & INSERT) != 0) {
+ int state = get_real_state();
+ if (state == MODE_NORMAL_BUSY || (state & MODE_INSERT) != 0) {
return true;
}
}
@@ -1370,31 +1599,20 @@ bool trigger_cursorhold(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
/// @param eap Ex command arguments
///
/// @return true if some commands were executed.
-static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, bool force,
- int group, buf_T *buf, exarg_T *eap)
+bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force, int group,
+ buf_T *buf, exarg_T *eap, Object *data)
{
- char_u *sfname = NULL; // short file name
- char_u *tail;
- bool save_changed;
- buf_T *old_curbuf;
+ char *sfname = NULL; // short file name
bool retval = false;
- char_u *save_sourcing_name;
- linenr_T save_sourcing_lnum;
- char_u *save_autocmd_fname;
- int save_autocmd_bufnr;
- char_u *save_autocmd_match;
- int save_autocmd_busy;
- int save_autocmd_nested;
static int nesting = 0;
- AutoPatCmd patcmd;
AutoPat *ap;
- char_u *save_cmdarg;
+ char *save_cmdarg;
long save_cmdbang;
static int filechangeshell_busy = false;
proftime_T wait_time;
bool did_save_redobuff = false;
save_redo_T save_redo;
- const bool save_KeyTyped = KeyTyped;
+ const bool save_KeyTyped = KeyTyped; // NOLINT
// Quickly return if there are no autocommands for this event or
// autocommands are blocked.
@@ -1443,13 +1661,13 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,
}
// Save the autocmd_* variables and info about the current buffer.
- save_autocmd_fname = autocmd_fname;
- save_autocmd_bufnr = autocmd_bufnr;
- save_autocmd_match = autocmd_match;
- save_autocmd_busy = autocmd_busy;
- save_autocmd_nested = autocmd_nested;
- save_changed = curbuf->b_changed;
- old_curbuf = curbuf;
+ char *save_autocmd_fname = autocmd_fname;
+ int save_autocmd_bufnr = autocmd_bufnr;
+ char *save_autocmd_match = autocmd_match;
+ int save_autocmd_busy = autocmd_busy;
+ int save_autocmd_nested = autocmd_nested;
+ bool save_changed = curbuf->b_changed;
+ buf_T *old_curbuf = curbuf;
// Set the file name to be used for <afile>.
// Make a copy to avoid that changing a buffer name or directory makes it
@@ -1470,7 +1688,7 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,
}
if (autocmd_fname != NULL) {
// Allocate MAXPATHL for when eval_vars() resolves the fullpath.
- autocmd_fname = vim_strnsave(autocmd_fname, MAXPATHL);
+ autocmd_fname = xstrnsave(autocmd_fname, MAXPATHL);
}
// Set the buffer number to be used for <abuf>.
@@ -1488,37 +1706,38 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,
fname = NULL;
} else {
if (event == EVENT_SYNTAX) {
- fname = buf->b_p_syn;
+ fname = (char *)buf->b_p_syn;
} else if (event == EVENT_FILETYPE) {
- fname = buf->b_p_ft;
+ fname = (char *)buf->b_p_ft;
} else {
if (buf->b_sfname != NULL) {
- sfname = vim_strsave(buf->b_sfname);
+ sfname = xstrdup(buf->b_sfname);
}
fname = buf->b_ffname;
}
}
if (fname == NULL) {
- fname = (char_u *)"";
+ fname = "";
}
- fname = vim_strsave(fname); // make a copy, so we can change it
+ fname = xstrdup(fname); // make a copy, so we can change it
} else {
- sfname = vim_strsave(fname);
+ sfname = xstrdup(fname);
// Don't try expanding the following events.
if (event == EVENT_CMDLINECHANGED || event == EVENT_CMDLINEENTER
|| event == EVENT_CMDLINELEAVE || event == EVENT_CMDWINENTER
|| event == EVENT_CMDWINLEAVE || event == EVENT_CMDUNDEFINED
|| event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
- || event == EVENT_DIRCHANGED || event == EVENT_FILETYPE
- || event == EVENT_FUNCUNDEFINED || event == EVENT_MODECHANGED
- || event == EVENT_OPTIONSET || event == EVENT_QUICKFIXCMDPOST
- || event == EVENT_QUICKFIXCMDPRE || event == EVENT_REMOTEREPLY
- || event == EVENT_SPELLFILEMISSING || event == EVENT_SYNTAX
- || event == EVENT_SIGNAL || event == EVENT_TABCLOSED
- || event == EVENT_WINCLOSED) {
- fname = vim_strsave(fname);
+ || event == EVENT_DIRCHANGED || event == EVENT_DIRCHANGEDPRE
+ || event == EVENT_FILETYPE || event == EVENT_FUNCUNDEFINED
+ || event == EVENT_MODECHANGED || event == EVENT_OPTIONSET
+ || event == EVENT_QUICKFIXCMDPOST || event == EVENT_QUICKFIXCMDPRE
+ || event == EVENT_REMOTEREPLY || event == EVENT_SPELLFILEMISSING
+ || event == EVENT_SYNTAX || event == EVENT_SIGNAL
+ || event == EVENT_TABCLOSED || event == EVENT_USER
+ || event == EVENT_WINCLOSED || event == EVENT_WINSCROLLED) {
+ fname = xstrdup(fname);
} else {
- fname = (char_u *)FullName_save((char *)fname, false);
+ fname = FullName_save(fname, false);
}
}
if (fname == NULL) { // out of memory
@@ -1541,9 +1760,9 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,
// Don't redraw while doing autocommands.
RedrawingDisabled++;
- save_sourcing_name = sourcing_name;
+ char *save_sourcing_name = sourcing_name;
sourcing_name = NULL; // don't free this one
- save_sourcing_lnum = sourcing_lnum;
+ linenr_T save_sourcing_lnum = sourcing_lnum;
sourcing_lnum = 0; // no line number here
const sctx_T save_current_sctx = current_sctx;
@@ -1576,9 +1795,10 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,
did_filetype = true;
}
- tail = path_tail(fname);
+ char *tail = path_tail(fname);
// Find first autocommand that matches
+ AutoPatCmd patcmd;
patcmd.curpat = first_autopat[(int)event];
patcmd.nextcmd = NULL;
patcmd.group = group;
@@ -1596,6 +1816,9 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,
patcmd.next = active_apc_list;
active_apc_list = &patcmd;
+ // Attach data to command
+ patcmd.data = data;
+
// set v:cmdarg (only when there is a matching pattern)
save_cmdbang = (long)get_vim_var_nr(VV_CMDBANG);
if (eap != NULL) {
@@ -1626,7 +1849,6 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,
reset_lnums();
}
-
if (eap != NULL) {
(void)set_cmdarg(NULL, save_cmdarg);
set_vim_var_nr(VV_CMDBANG, save_cmdbang);
@@ -1773,8 +1995,7 @@ void auto_next_pat(AutoPatCmd *apc, int stop_at_last)
= (STRLEN(s) + strlen(name) + (size_t)ap->patlen + 1);
sourcing_name = xmalloc(sourcing_name_len);
- snprintf((char *)sourcing_name, sourcing_name_len, s, name,
- (char *)ap->pat);
+ snprintf(sourcing_name, sourcing_name_len, s, name, ap->pat);
if (p_verbose >= 8) {
verbose_enter();
smsg(_("Executing %s"), sourcing_name);
@@ -1800,14 +2021,66 @@ void auto_next_pat(AutoPatCmd *apc, int stop_at_last)
}
}
+static bool call_autocmd_callback(const AutoCmd *ac, const AutoPatCmd *apc)
+{
+ bool ret = false;
+ Callback callback = ac->exec.callable.cb;
+ if (callback.type == kCallbackLua) {
+ Dictionary data = ARRAY_DICT_INIT;
+ PUT(data, "id", INTEGER_OBJ(ac->id));
+ PUT(data, "event", CSTR_TO_OBJ(event_nr2name(apc->event)));
+ PUT(data, "match", CSTR_TO_OBJ((char *)autocmd_match));
+ PUT(data, "file", CSTR_TO_OBJ((char *)autocmd_fname));
+ PUT(data, "buf", INTEGER_OBJ(autocmd_bufnr));
+
+ if (apc->data) {
+ PUT(data, "data", copy_object(*apc->data));
+ }
+
+ int group = apc->curpat->group;
+ switch (group) {
+ case AUGROUP_ERROR:
+ abort(); // unreachable
+ case AUGROUP_DEFAULT:
+ case AUGROUP_ALL:
+ case AUGROUP_DELETED:
+ // omit group in these cases
+ break;
+ default:
+ PUT(data, "group", INTEGER_OBJ(group));
+ break;
+ }
+
+ FIXED_TEMP_ARRAY(args, 1);
+ args.items[0] = DICTIONARY_OBJ(data);
+
+ Object result = nlua_call_ref(callback.data.luaref, NULL, args, true, NULL);
+ if (result.type == kObjectTypeBoolean) {
+ ret = result.data.boolean;
+ }
+ api_free_dictionary(data);
+ api_free_object(result);
+ } else {
+ typval_T argsin = TV_INITIAL_VALUE;
+ typval_T rettv = TV_INITIAL_VALUE;
+ callback_call(&callback, 0, &argsin, &rettv);
+ }
+
+ return ret;
+}
+
/// Get next autocommand command.
/// Called by do_cmdline() to get the next line for ":if".
/// @return allocated string, or NULL for end of autocommands.
-char_u *getnextac(int c, void *cookie, int indent, bool do_concat)
+char *getnextac(int c, void *cookie, int indent, bool do_concat)
{
+ // These arguments are required for do_cmdline.
+ (void)c;
+ (void)indent;
+ (void)do_concat;
+
AutoPatCmd *acp = (AutoPatCmd *)cookie;
- char_u *retval;
- AutoCmd *ac;
+ char *retval;
// Can be called again after returning the last line.
if (acp->curpat == NULL) {
@@ -1817,7 +2090,8 @@ char_u *getnextac(int c, void *cookie, int indent, bool do_concat)
// repeat until we find an autocommand to execute
for (;;) {
// skip removed commands
- while (acp->nextcmd != NULL && acp->nextcmd->cmd == NULL) {
+ while (acp->nextcmd != NULL
+ && aucmd_exec_is_deleted(acp->nextcmd->exec)) {
if (acp->nextcmd->last) {
acp->nextcmd = NULL;
} else {
@@ -1843,21 +2117,48 @@ char_u *getnextac(int c, void *cookie, int indent, bool do_concat)
}
}
- ac = acp->nextcmd;
+ AutoCmd *ac = acp->nextcmd;
+ bool oneshot = ac->once;
if (p_verbose >= 9) {
verbose_enter_scroll();
- smsg(_("autocommand %s"), ac->cmd);
+ char *exec_to_string = aucmd_exec_to_string(ac, ac->exec);
+ smsg(_("autocommand %s"), exec_to_string);
msg_puts("\n"); // don't overwrite this either
+ XFREE_CLEAR(exec_to_string);
verbose_leave_scroll();
}
- retval = vim_strsave(ac->cmd);
- // Remove one-shot ("once") autocmd in anticipation of its execution.
- if (ac->once) {
- au_del_cmd(ac);
- }
+
+ // Make sure to set autocmd_nested before executing
+ // lua code, so that it works properly
autocmd_nested = ac->nested;
current_sctx = ac->script_ctx;
+
+ if (ac->exec.type == CALLABLE_CB) {
+ if (call_autocmd_callback(ac, acp)) {
+ // If an autocommand callback returns true, delete the autocommand
+ oneshot = true;
+ }
+
+ // TODO(tjdevries):
+ //
+ // Major Hack Alert:
+ // We just return "not-null" and continue going.
+ // This would be a good candidate for a refactor. You would need to refactor:
+ // 1. do_cmdline to accept something besides a string
+ // OR
+ // 2. make where we call do_cmdline for autocmds not have to return anything,
+ // and instead we loop over all the matches and just execute one-by-one.
+ // However, my expectation would be that could be expensive.
+ retval = xstrdup("");
+ } else {
+ retval = xstrdup(ac->exec.callable.cmd);
+ }
+
+ // Remove one-shot ("once") autocmd in anticipation of its execution.
+ if (oneshot) {
+ aucmd_del(ac);
+ }
if (ac->last) {
acp->nextcmd = NULL;
} else {
@@ -1874,14 +2175,13 @@ char_u *getnextac(int c, void *cookie, int indent, bool do_concat)
/// @param event event that occurred.
/// @param sfname filename the event occurred in.
/// @param buf buffer the file is open in
-bool has_autocmd(event_T event, char_u *sfname, buf_T *buf) FUNC_ATTR_WARN_UNUSED_RESULT
+bool has_autocmd(event_T event, char *sfname, buf_T *buf)
+ FUNC_ATTR_WARN_UNUSED_RESULT
{
- AutoPat *ap;
- char_u *fname;
- char_u *tail = path_tail(sfname);
+ char *tail = path_tail(sfname);
bool retval = false;
- fname = (char_u *)FullName_save((char *)sfname, false);
+ char *fname = FullName_save(sfname, false);
if (fname == NULL) {
return false;
}
@@ -1894,7 +2194,7 @@ bool has_autocmd(event_T event, char_u *sfname, buf_T *buf) FUNC_ATTR_WARN_UNUSE
forward_slash(fname);
#endif
- for (ap = first_autopat[(int)event]; ap != NULL; ap = ap->next) {
+ for (AutoPat *ap = first_autopat[(int)event]; ap != NULL; ap = ap->next) {
if (ap->pat != NULL && ap->cmds != NULL
&& (ap->buflocal_nr == 0
? match_file_pat(NULL,
@@ -1919,31 +2219,21 @@ bool has_autocmd(event_T event, char_u *sfname, buf_T *buf) FUNC_ATTR_WARN_UNUSE
// Function given to ExpandGeneric() to obtain the list of autocommand group
// names.
-char_u *get_augroup_name(expand_T *xp, int idx)
+char *expand_get_augroup_name(expand_T *xp, int idx)
{
- if (idx == augroups.ga_len) { // add "END" add the end
- return (char_u *)"END";
- }
- if (idx >= augroups.ga_len) { // end of list
- return NULL;
- }
- if (AUGROUP_NAME(idx) == NULL || AUGROUP_NAME(idx) == get_deleted_augroup()) {
- // skip deleted entries
- return (char_u *)"";
- }
- return (char_u *)AUGROUP_NAME(idx);
+ // Required for ExpandGeneric
+ (void)xp;
+
+ return augroup_name(idx + 1);
}
/// @param doautocmd true for :doauto*, false for :autocmd
-char_u *set_context_in_autocmd(expand_T *xp, char_u *arg, int doautocmd)
+char *set_context_in_autocmd(expand_T *xp, char *arg, int doautocmd)
{
- char_u *p;
- int group;
-
// check for a group name, skip it if present
autocmd_include_groups = false;
- p = arg;
- group = au_get_grouparg(&arg);
+ char *p = arg;
+ int group = arg_augroup_get(&arg);
// If there only is a group name that's what we expand.
if (*arg == NUL && group != AUGROUP_ALL && !ascii_iswhite(arg[-1])) {
@@ -1984,16 +2274,24 @@ char_u *set_context_in_autocmd(expand_T *xp, char_u *arg, int doautocmd)
}
// Function given to ExpandGeneric() to obtain the list of event names.
-char_u *get_event_name(expand_T *xp, int idx)
+char *expand_get_event_name(expand_T *xp, int idx)
{
- if (idx < augroups.ga_len) { // First list group names, if wanted
- if (!autocmd_include_groups || AUGROUP_NAME(idx) == NULL
- || AUGROUP_NAME(idx) == get_deleted_augroup()) {
- return (char_u *)""; // skip deleted entries
+ // xp is a required parameter to be used with ExpandGeneric
+ (void)xp;
+
+ // List group names
+ char *name = augroup_name(idx + 1);
+ if (name != NULL) {
+ // skip when not including groups or skip deleted entries
+ if (!autocmd_include_groups || name == get_deleted_augroup()) {
+ return "";
}
- return (char_u *)AUGROUP_NAME(idx);
+
+ return name;
}
- return (char_u *)event_names[idx - augroups.ga_len].name;
+
+ // List event names
+ return event_names[idx - next_augroup_id].name;
}
/// Check whether given autocommand is supported
@@ -2004,8 +2302,8 @@ char_u *get_event_name(expand_T *xp, int idx)
bool autocmd_supported(const char *const event)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- char_u *p;
- return event_name2nr((const char_u *)event, &p) != NUM_EVENTS;
+ char *p;
+ return event_name2nr(event, &p) != NUM_EVENTS;
}
/// Return true if an autocommand is defined for a group, event and
@@ -2022,10 +2320,7 @@ bool autocmd_supported(const char *const event)
/// @param arg autocommand string
bool au_exists(const char *const arg) FUNC_ATTR_WARN_UNUSED_RESULT
{
- event_T event;
- AutoPat *ap;
buf_T *buflocal_buf = NULL;
- int group;
bool retval = false;
// Make a copy so that we can change the '#' chars to a NUL.
@@ -2036,7 +2331,7 @@ bool au_exists(const char *const arg) FUNC_ATTR_WARN_UNUSED_RESULT
}
// First, look for an autocmd group name.
- group = au_find_group((char_u *)arg_save);
+ int group = augroup_find(arg_save);
char *event_name;
if (group == AUGROUP_ERROR) {
// Didn't match a group name, assume the first argument is an event.
@@ -2060,7 +2355,7 @@ bool au_exists(const char *const arg) FUNC_ATTR_WARN_UNUSED_RESULT
char *pattern = p; // "pattern" is NULL when there is no pattern.
// Find the index (enum) for the event name.
- event = event_name2nr((char_u *)event_name, (char_u **)&p);
+ event_T event = event_name2nr(event_name, &p);
// return false if the event name is not recognized
if (event == NUM_EVENTS) {
@@ -2070,13 +2365,13 @@ bool au_exists(const char *const arg) FUNC_ATTR_WARN_UNUSED_RESULT
// Find the first autocommand for this event.
// If there isn't any, return false;
// If there is one and no pattern given, return true;
- ap = first_autopat[(int)event];
+ AutoPat *ap = first_autopat[(int)event];
if (ap == NULL) {
goto theend;
}
// if pattern is "<buffer>", special handling is needed which uses curbuf
- // for pattern "<buffer=N>, fnamecmp() will work fine
+ // for pattern "<buffer=N>, FNAMECMP() will work fine
if (pattern != NULL && STRICMP(pattern, "<buffer>") == 0) {
buflocal_buf = curbuf;
}
@@ -2084,12 +2379,12 @@ bool au_exists(const char *const arg) FUNC_ATTR_WARN_UNUSED_RESULT
// Check if there is an autocommand with the given pattern.
for (; ap != NULL; ap = ap->next) {
// only use a pattern when it has not been removed and has commands.
- // For buffer-local autocommands, fnamecmp() works fine.
+ // For buffer-local autocommands, FNAMECMP() works fine.
if (ap->pat != NULL && ap->cmds != NULL
&& (group == AUGROUP_ALL || ap->group == group)
&& (pattern == NULL
|| (buflocal_buf == NULL
- ? fnamecmp(ap->pat, (char_u *)pattern) == 0
+ ? FNAMECMP(ap->pat, pattern) == 0
: ap->buflocal_nr == buflocal_buf->b_fnum))) {
retval = true;
break;
@@ -2100,3 +2395,343 @@ theend:
xfree(arg_save);
return retval;
}
+
+// Checks if a pattern is buflocal
+bool aupat_is_buflocal(char *pat, int patlen)
+ FUNC_ATTR_PURE
+{
+ return patlen >= 8
+ && STRNCMP(pat, "<buffer", 7) == 0
+ && (pat)[patlen - 1] == '>';
+}
+
+int aupat_get_buflocal_nr(char *pat, int patlen)
+{
+ assert(aupat_is_buflocal((char *)pat, patlen));
+
+ // "<buffer>"
+ if (patlen == 8) {
+ return curbuf->b_fnum;
+ }
+
+ if (patlen > 9 && (pat)[7] == '=') {
+ // "<buffer=abuf>"
+ if (patlen == 13 && STRNICMP(pat, "<buffer=abuf>", 13) == 0) {
+ return autocmd_bufnr;
+ }
+
+ // "<buffer=123>"
+ if (skipdigits(pat + 8) == pat + patlen - 1) {
+ return atoi(pat + 8);
+ }
+ }
+
+ return 0;
+}
+
+// normalize buffer pattern
+void aupat_normalize_buflocal_pat(char *dest, char *pat, int patlen, int buflocal_nr)
+{
+ assert(aupat_is_buflocal(pat, patlen));
+
+ if (buflocal_nr == 0) {
+ buflocal_nr = curbuf->handle;
+ }
+
+ // normalize pat into standard "<buffer>#N" form
+ snprintf(dest,
+ BUFLOCAL_PAT_LEN,
+ "<buffer=%d>",
+ buflocal_nr);
+}
+
+int autocmd_delete_event(int group, event_T event, char *pat)
+ FUNC_ATTR_NONNULL_ALL
+{
+ return do_autocmd_event(event, pat, false, false, "", true, group);
+}
+
+/// Deletes an autocmd by ID.
+/// Only autocmds created via the API have IDs associated with them. There
+/// is no way to delete a specific autocmd created via :autocmd
+bool autocmd_delete_id(int64_t id)
+{
+ assert(id > 0);
+ bool success = false;
+
+ // Note that since multiple AutoCmd objects can have the same ID, we need to do a full scan.
+ FOR_ALL_AUEVENTS(event) {
+ FOR_ALL_AUPATS_IN_EVENT(event, ap) { // -V756
+ for (AutoCmd *ac = ap->cmds; ac != NULL; ac = ac->next) {
+ if (ac->id == id) {
+ aucmd_del(ac);
+ success = true;
+ }
+ }
+ }
+ }
+ return success;
+}
+
+// ===========================================================================
+// AucmdExecutable Functions
+// ===========================================================================
+
+/// Generate a string description for the command/callback of an autocmd
+char *aucmd_exec_to_string(AutoCmd *ac, AucmdExecutable acc)
+ FUNC_ATTR_PURE
+{
+ switch (acc.type) {
+ case CALLABLE_EX:
+ return xstrdup(acc.callable.cmd);
+ case CALLABLE_CB:
+ return callback_to_string(&acc.callable.cb);
+ case CALLABLE_NONE:
+ return "This is not possible";
+ }
+
+ abort();
+}
+
+void aucmd_exec_free(AucmdExecutable *acc)
+{
+ switch (acc->type) {
+ case CALLABLE_EX:
+ XFREE_CLEAR(acc->callable.cmd);
+ break;
+ case CALLABLE_CB:
+ callback_free(&acc->callable.cb);
+ break;
+ case CALLABLE_NONE:
+ return;
+ }
+
+ acc->type = CALLABLE_NONE;
+}
+
+AucmdExecutable aucmd_exec_copy(AucmdExecutable src)
+{
+ AucmdExecutable dest = AUCMD_EXECUTABLE_INIT;
+
+ switch (src.type) {
+ case CALLABLE_EX:
+ dest.type = CALLABLE_EX;
+ dest.callable.cmd = xstrdup(src.callable.cmd);
+ return dest;
+ case CALLABLE_CB:
+ dest.type = CALLABLE_CB;
+ callback_copy(&dest.callable.cb, &src.callable.cb);
+ return dest;
+ case CALLABLE_NONE:
+ return dest;
+ }
+
+ abort();
+}
+
+bool aucmd_exec_is_deleted(AucmdExecutable acc)
+ FUNC_ATTR_PURE
+{
+ switch (acc.type) {
+ case CALLABLE_EX:
+ return acc.callable.cmd == NULL;
+ case CALLABLE_CB:
+ return acc.callable.cb.type == kCallbackNone;
+ case CALLABLE_NONE:
+ return true;
+ }
+
+ abort();
+}
+
+bool au_event_is_empty(event_T event)
+ FUNC_ATTR_PURE
+{
+ return first_autopat[event] == NULL;
+}
+
+// Arg Parsing Functions
+
+/// Scan over the events. "*" stands for all events.
+/// true when group name was found
+static char *arg_event_skip(char *arg, int have_group)
+{
+ char *pat;
+ char *p;
+
+ if (*arg == '*') {
+ if (arg[1] && !ascii_iswhite(arg[1])) {
+ semsg(_("E215: Illegal character after *: %s"), arg);
+ return NULL;
+ }
+ pat = arg + 1;
+ } else {
+ for (pat = arg; *pat && *pat != '|' && !ascii_iswhite(*pat); pat = p) {
+ if ((int)event_name2nr(pat, &p) >= NUM_EVENTS) {
+ if (have_group) {
+ semsg(_("E216: No such event: %s"), pat);
+ } else {
+ semsg(_("E216: No such group or event: %s"), pat);
+ }
+ return NULL;
+ }
+ }
+ }
+ return pat;
+}
+
+// Find the group ID in a ":autocmd" or ":doautocmd" argument.
+// The "argp" argument is advanced to the following argument.
+//
+// Returns the group ID or AUGROUP_ALL.
+static int arg_augroup_get(char **argp)
+{
+ char *p;
+ char *arg = *argp;
+ int group = AUGROUP_ALL;
+
+ for (p = arg; *p && !ascii_iswhite(*p) && *p != '|'; p++) {}
+ if (p > arg) {
+ char *group_name = xstrnsave(arg, (size_t)(p - arg));
+ group = augroup_find(group_name);
+ if (group == AUGROUP_ERROR) {
+ group = AUGROUP_ALL; // no match, use all groups
+ } else {
+ *argp = skipwhite(p); // match, skip over group name
+ }
+ xfree(group_name);
+ }
+ return group;
+}
+
+/// Handles grabbing arguments from `:autocmd` such as ++once and ++nested
+static bool arg_autocmd_flag_get(bool *flag, char **cmd_ptr, char *pattern, int len)
+{
+ if (STRNCMP(*cmd_ptr, pattern, len) == 0 && ascii_iswhite((*cmd_ptr)[len])) {
+ if (*flag) {
+ semsg(_(e_duparg2), pattern);
+ return true;
+ }
+
+ *flag = true;
+ *cmd_ptr = skipwhite(*cmd_ptr + len);
+ }
+
+ return false;
+}
+
+// UI Enter
+void do_autocmd_uienter(uint64_t chanid, bool attached)
+{
+ static bool recursive = false;
+
+ if (recursive) {
+ return; // disallow recursion
+ }
+ recursive = true;
+
+ save_v_event_T save_v_event;
+ dict_T *dict = get_v_event(&save_v_event);
+ assert(chanid < VARNUMBER_MAX);
+ tv_dict_add_nr(dict, S_LEN("chan"), (varnumber_T)chanid);
+ tv_dict_set_keys_readonly(dict);
+ apply_autocmds(attached ? EVENT_UIENTER : EVENT_UILEAVE,
+ NULL, NULL, false, curbuf);
+ restore_v_event(dict, &save_v_event);
+
+ recursive = false;
+}
+
+// FocusGained
+
+static void focusgained_event(void **argv)
+{
+ bool *gainedp = argv[0];
+ do_autocmd_focusgained(*gainedp);
+ xfree(gainedp);
+}
+
+void autocmd_schedule_focusgained(bool gained)
+{
+ bool *gainedp = xmalloc(sizeof(*gainedp));
+ *gainedp = gained;
+ loop_schedule_deferred(&main_loop,
+ event_create(focusgained_event, 1, gainedp));
+}
+
+static void do_autocmd_focusgained(bool gained)
+{
+ static bool recursive = false;
+ static Timestamp last_time = (time_t)0;
+ bool need_redraw = false;
+
+ if (recursive) {
+ return; // disallow recursion
+ }
+ recursive = true;
+ need_redraw |= apply_autocmds((gained ? EVENT_FOCUSGAINED : EVENT_FOCUSLOST),
+ NULL, NULL, false, curbuf);
+
+ // When activated: Check if any file was modified outside of Vim.
+ // Only do this when not done within the last two seconds as:
+ // 1. Some filesystems have modification time granularity in seconds. Fat32
+ // has a granularity of 2 seconds.
+ // 2. We could get multiple notifications in a row.
+ if (gained && last_time + (Timestamp)2000 < os_now()) {
+ need_redraw = check_timestamps(true);
+ last_time = os_now();
+ }
+
+ if (need_redraw) {
+ // Something was executed, make sure the cursor is put back where it
+ // belongs.
+ need_wait_return = false;
+
+ if (State & MODE_CMDLINE) {
+ redrawcmdline();
+ } else if ((State & MODE_NORMAL) || (State & MODE_INSERT)) {
+ if (must_redraw != 0) {
+ update_screen(0);
+ }
+
+ setcursor();
+ }
+
+ ui_flush();
+ }
+
+ if (need_maketitle) {
+ maketitle();
+ }
+
+ recursive = false;
+}
+
+static void define_autocmd(event_T event, char *pat, char *group, bool once, bool nested, char *cmd)
+{
+ AucmdExecutable exec = AUCMD_EXECUTABLE_INIT;
+ exec.type = CALLABLE_EX;
+ exec.callable.cmd = cmd; // autocmd_register() makes a copy
+ int group_id = augroup_add(group);
+ autocmd_register(0, event, pat, (int)strlen(pat), group_id, once, nested, NULL, exec);
+}
+
+/// initialization of default autocmds
+void init_default_autocmds(void)
+{
+ // open terminals when opening files that start with term://
+#define PROTO "term://"
+ define_autocmd(EVENT_BUFREADCMD, PROTO "*", "nvim_terminal", false, true,
+ "if !exists('b:term_title')|call termopen("
+ // Capture the command string
+ "matchstr(expand(\"<amatch>\"), "
+ "'\\c\\m" PROTO "\\%(.\\{-}//\\%(\\d\\+:\\)\\?\\)\\?\\zs.*'), "
+ // capture the working directory
+ "{'cwd': expand(get(matchlist(expand(\"<amatch>\"), "
+ "'\\c\\m" PROTO "\\(.\\{-}\\)//'), 1, ''))})"
+ "|endif");
+#undef PROTO
+ // limit syntax synchronization in the command window
+ define_autocmd(EVENT_CMDWINENTER, "[:>]", "nvim_cmdwin", false, false,
+ "syntax sync minlines=1 maxlines=1");
+}
diff --git a/src/nvim/autocmd.h b/src/nvim/autocmd.h
index ac12e2acf3..a085a03455 100644
--- a/src/nvim/autocmd.h
+++ b/src/nvim/autocmd.h
@@ -4,6 +4,11 @@
#include "nvim/buffer_defs.h"
#include "nvim/ex_cmds_defs.h"
+// event_T definition
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "auevents_enum.generated.h"
+#endif
+
// Struct to save values in before executing autocommands for a buffer that is
// not the current buffer.
typedef struct {
@@ -13,26 +18,28 @@ typedef struct {
handle_T new_curwin_handle; ///< ID of new curwin
handle_T save_prevwin_handle; ///< ID of saved prevwin
bufref_T new_curbuf; ///< new curbuf
- char_u *globaldir; ///< saved value of globaldir
+ char *globaldir; ///< saved value of globaldir
+ bool save_VIsual_active; ///< saved VIsual_active
} aco_save_T;
typedef struct AutoCmd {
- char_u *cmd; // Command to be executed (NULL when
- // command has been removed)
+ AucmdExecutable exec;
bool once; // "One shot": removed after execution
bool nested; // If autocommands nest here
bool last; // last command in list
+ int64_t id; // ID used for uniquely tracking an autocmd.
sctx_T script_ctx; // script context where defined
- struct AutoCmd *next; // Next AutoCmd in list
+ char *desc; // Description for the autocmd.
+ struct AutoCmd *next; // Next AutoCmd in list
} AutoCmd;
typedef struct AutoPat {
- struct AutoPat *next; // next AutoPat in AutoPat list; MUST
- // be the first entry
- char_u *pat; // pattern as typed (NULL when pattern
- // has been removed)
- regprog_T *reg_prog; // compiled regprog for pattern
- AutoCmd *cmds; // list of commands to do
+ struct AutoPat *next; // next AutoPat in AutoPat list; MUST
+ // be the first entry
+ char *pat; // pattern as typed (NULL when pattern
+ // has been removed)
+ regprog_T *reg_prog; // compiled regprog for pattern
+ AutoCmd *cmds; // list of commands to do
int group; // group ID
int patlen; // strlen() of pat
int buflocal_nr; // !=0 for buffer-local AutoPat
@@ -40,27 +47,20 @@ typedef struct AutoPat {
char last; // last pattern for apply_autocmds()
} AutoPat;
-#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "auevents_enum.generated.h"
-#endif
-
-///
/// Struct used to keep status while executing autocommands for an event.
-///
typedef struct AutoPatCmd {
AutoPat *curpat; // next AutoPat to examine
AutoCmd *nextcmd; // next AutoCmd to execute
- int group; // group being used
- char_u *fname; // fname to match with
- char_u *sfname; // sfname to match with
- char_u *tail; // tail of fname
- event_T event; // current event
- int arg_bufnr; // initially equal to <abuf>, set to zero when
- // buf is deleted
- struct AutoPatCmd *next; // chain of active apc-s for auto-invalidation
+ int group; // group being used
+ char *fname; // fname to match with
+ char *sfname; // sfname to match with
+ char *tail; // tail of fname
+ event_T event; // current event
+ int arg_bufnr; // initially equal to <abuf>, set to zero when buf is deleted
+ Object *data; // arbitrary data
+ struct AutoPatCmd *next; // chain of active apc-s for auto-invalidation
} AutoPatCmd;
-
// Set by the apply_autocmds_group function if the given event is equal to
// EVENT_FILETYPE. Used by the readfile function in order to determine if
// EVENT_BUFREADPOST triggered the EVENT_FILETYPE.
@@ -69,13 +69,20 @@ typedef struct AutoPatCmd {
// apply_autocmds_group.
EXTERN bool au_did_filetype INIT(= false);
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "autocmd.h.generated.h"
#endif
-#define AUGROUP_DEFAULT -1 // default autocmd group
-#define AUGROUP_ERROR -2 // erroneous autocmd group
-#define AUGROUP_ALL -3 // all autocmd groups
+#define AUGROUP_DEFAULT (-1) // default autocmd group
+#define AUGROUP_ERROR (-2) // erroneous autocmd group
+#define AUGROUP_ALL (-3) // all autocmd groups
+#define AUGROUP_DELETED (-4) // all autocmd groups
+// #define AUGROUP_NS -5 // TODO(tjdevries): Support namespaced based augroups
+
+#define BUFLOCAL_PAT_LEN 25
+
+/// Iterates over all the events for auto commands
+#define FOR_ALL_AUEVENTS(event) \
+ for (event_T event = (event_T)0; (int)event < (int)NUM_EVENTS; event = (event_T)((int)event + 1)) // NOLINT
#endif // NVIM_AUTOCMD_H
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index abd22fba26..f937450107 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -25,7 +25,6 @@
#include <string.h>
#include "nvim/api/private/helpers.h"
-#include "nvim/api/vim.h"
#include "nvim/ascii.h"
#include "nvim/assert.h"
#include "nvim/buffer.h"
@@ -34,6 +33,7 @@
#include "nvim/channel.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
+#include "nvim/decoration.h"
#include "nvim/diff.h"
#include "nvim/digraph.h"
#include "nvim/eval.h"
@@ -49,11 +49,13 @@
#include "nvim/garray.h"
#include "nvim/getchar.h"
#include "nvim/hashtab.h"
-#include "nvim/highlight.h"
+#include "nvim/highlight_group.h"
#include "nvim/indent.h"
#include "nvim/indent_c.h"
#include "nvim/main.h"
+#include "nvim/mapping.h"
#include "nvim/mark.h"
+#include "nvim/mark_defs.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/message.h"
@@ -62,13 +64,11 @@
#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/os/time.h"
-#include "nvim/os_unix.h"
#include "nvim/path.h"
#include "nvim/plines.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
#include "nvim/screen.h"
-#include "nvim/shada.h"
#include "nvim/sign.h"
#include "nvim/spell.h"
#include "nvim/strings.h"
@@ -89,15 +89,24 @@
static char *msg_loclist = N_("[Location List]");
static char *msg_qflist = N_("[Quickfix List]");
static char *e_auabort = N_("E855: Autocommands caused command to abort");
+static char *e_buflocked = N_("E937: Attempt to delete a buffer that is in use");
// Number of times free_buffer() was called.
static int buf_free_count = 0;
+static int top_file_num = 1; ///< highest file number
+
typedef enum {
kBffClearWinInfo = 1,
kBffInitChangedtick = 2,
} BufFreeFlags;
+/// @return the highest possible buffer number
+int get_highest_fnum(void)
+{
+ return top_file_num - 1;
+}
+
/// Read data from buffer for retrying.
///
/// @param read_stdin read file from stdin, otherwise fifo
@@ -107,6 +116,7 @@ static int read_buffer(int read_stdin, exarg_T *eap, int flags)
{
int retval = OK;
linenr_T line_count;
+ bool silent = shortmess(SHM_FILEINFO);
// Read from the buffer which the text is already filled in and append at
// the end. This makes it possible to retry when 'fileformat' or
@@ -115,7 +125,7 @@ static int read_buffer(int read_stdin, exarg_T *eap, int flags)
retval = readfile(read_stdin ? NULL : curbuf->b_ffname,
read_stdin ? NULL : curbuf->b_fname,
line_count, (linenr_T)0, (linenr_T)MAXLNUM, eap,
- flags | READ_BUFFER);
+ flags | READ_BUFFER, silent);
if (retval == OK) {
// Delete the binary lines.
while (--line_count >= 0) {
@@ -160,6 +170,7 @@ int open_buffer(int read_stdin, exarg_T *eap, int flags)
bufref_T old_curbuf;
long old_tw = curbuf->b_p_tw;
int read_fifo = false;
+ bool silent = shortmess(SHM_FILEINFO);
// The 'readonly' flag is only set when BF_NEVERLOADED is being reset.
// When re-entering the same buffer, it should not change, because the
@@ -172,7 +183,7 @@ int open_buffer(int read_stdin, exarg_T *eap, int flags)
if (ml_open(curbuf) == FAIL) {
// There MUST be a memfile, otherwise we can't do anything
// If we can't create one for the current buffer, take another buffer
- close_buffer(NULL, curbuf, 0, false);
+ close_buffer(NULL, curbuf, 0, false, false);
curbuf = NULL;
FOR_ALL_BUFFERS(buf) {
@@ -210,7 +221,6 @@ int open_buffer(int read_stdin, exarg_T *eap, int flags)
curwin->w_valid = 0;
if (curbuf->b_ffname != NULL) {
- int old_msg_silent = msg_silent;
#ifdef UNIX
int save_bin = curbuf->b_p_bin;
int perm;
@@ -222,20 +232,17 @@ int open_buffer(int read_stdin, exarg_T *eap, int flags)
|| (S_ISCHR(perm)
&& is_dev_fd_file(curbuf->b_ffname))
# endif
- )) {
+ )) { // NOLINT(whitespace/parens)
read_fifo = true;
}
if (read_fifo) {
curbuf->b_p_bin = true;
}
#endif
- if (shortmess(SHM_FILEINFO)) {
- msg_silent = 1;
- }
retval = readfile(curbuf->b_ffname, curbuf->b_fname,
(linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM, eap,
- flags | READ_NEW | (read_fifo ? READ_FIFO : 0));
+ flags | READ_NEW | (read_fifo ? READ_FIFO : 0), silent);
#ifdef UNIX
if (read_fifo) {
curbuf->b_p_bin = save_bin;
@@ -244,7 +251,6 @@ int open_buffer(int read_stdin, exarg_T *eap, int flags)
}
}
#endif
- msg_silent = old_msg_silent;
// Help buffer is filtered.
if (bt_help(curbuf)) {
@@ -260,7 +266,7 @@ int open_buffer(int read_stdin, exarg_T *eap, int flags)
curbuf->b_p_bin = true;
retval = readfile(NULL, NULL, (linenr_T)0,
(linenr_T)0, (linenr_T)MAXLNUM, NULL,
- flags | (READ_NEW + READ_STDIN));
+ flags | (READ_NEW + READ_STDIN), silent);
curbuf->b_p_bin = save_bin;
if (retval == OK) {
retval = read_buffer(true, eap, flags);
@@ -355,6 +361,7 @@ void set_bufref(bufref_T *bufref, buf_T *buf)
///
/// @param bufref Buffer reference to check for.
bool bufref_valid(bufref_T *bufref)
+ FUNC_ATTR_PURE
{
return bufref->br_buf_free_count == buf_free_count
? true
@@ -400,8 +407,10 @@ bool buf_valid(buf_T *buf)
/// there to be only one window with this buffer. e.g. when
/// ":quit" is supposed to close the window but autocommands
/// close all other windows.
-/// @returns true when we got to the end and b_nwindows was decremented.
-bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
+/// @param ignore_abort
+/// If true, don't abort even when aborting() returns true.
+/// @return true when we got to the end and b_nwindows was decremented.
+bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last, bool ignore_abort)
{
bool unload_buf = (action != 0);
bool del_buf = (action == DOBUF_DEL || action == DOBUF_WIPE);
@@ -438,10 +447,11 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
// Disallow deleting the buffer when it is locked (already being closed or
// halfway a command that relies on it). Unloading is allowed.
if (buf->b_locked > 0 && (del_buf || wipe_buf)) {
- emsg(_("E937: Attempt to delete a buffer that is in use"));
+ emsg(_(e_buflocked));
return false;
}
+ // check no autocommands closed the window
if (win != NULL // Avoid bogus clang warning.
&& win_valid_any_tab(win)) {
// Set b_last_cursor when closing the last window for the buffer.
@@ -462,6 +472,7 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
// When the buffer is no longer in a window, trigger BufWinLeave
if (buf->b_nwindows == 1) {
buf->b_locked++;
+ buf->b_locked_split++;
if (apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname, buf->b_fname, false,
buf) && !bufref_valid(&bufref)) {
// Autocommands deleted the buffer.
@@ -469,6 +480,7 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
return false;
}
buf->b_locked--;
+ buf->b_locked_split--;
if (abort_if_last && last_nonfloat(win)) {
// Autocommands made this the only window.
emsg(_(e_auabort));
@@ -479,6 +491,7 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
// BufHidden
if (!unload_buf) {
buf->b_locked++;
+ buf->b_locked_split++;
if (apply_autocmds(EVENT_BUFHIDDEN, buf->b_fname, buf->b_fname, false,
buf) && !bufref_valid(&bufref)) {
// Autocommands deleted the buffer.
@@ -486,13 +499,15 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
return false;
}
buf->b_locked--;
+ buf->b_locked_split--;
if (abort_if_last && last_nonfloat(win)) {
// Autocommands made this the only window.
emsg(_(e_auabort));
return false;
}
}
- if (aborting()) { // autocmds may abort script processing
+ // autocmds may abort script processing
+ if (!ignore_abort && aborting()) {
return false;
}
}
@@ -524,7 +539,9 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
}
if (buf->terminal) {
- terminal_close(buf->terminal, -1);
+ buf->b_locked++;
+ terminal_close(&buf->terminal, -1);
+ buf->b_locked--;
}
// Always remove the buffer when there is no file name.
@@ -550,14 +567,16 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
buf->b_nwindows = nwindows;
- buf_freeall(buf, (del_buf ? BFA_DEL : 0) + (wipe_buf ? BFA_WIPE : 0));
+ buf_freeall(buf, ((del_buf ? BFA_DEL : 0)
+ + (wipe_buf ? BFA_WIPE : 0)
+ + (ignore_abort ? BFA_IGNORE_ABORT : 0)));
if (!bufref_valid(&bufref)) {
// Autocommands may have deleted the buffer.
return false;
}
- if (aborting()) {
- // Autocmds may abort script processing.
+ // autocmds may abort script processing.
+ if (!ignore_abort && aborting()) {
return false;
}
@@ -570,6 +589,10 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
return false;
}
+ // Disable buffer-updates for the current buffer.
+ // No need to check `unload_buf`: in that case the function returned above.
+ buf_updates_unload(buf, false);
+
if (win != NULL // Avoid bogus clang warning.
&& win_valid_any_tab(win)
&& win->w_buffer == buf) {
@@ -582,14 +605,12 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
buf->b_nwindows--;
}
- // Disable buffer-updates for the current buffer.
- // No need to check `unload_buf`: in that case the function returned above.
- buf_updates_unload(buf, false);
-
- /*
- * Remove the buffer from the list.
- */
+ // Remove the buffer from the list.
if (wipe_buf) {
+ // Do not wipe out the buffer if it is used in a window.
+ if (buf->b_nwindows > 0) {
+ return false;
+ }
if (buf->b_sfname != buf->b_ffname) {
XFREE_CLEAR(buf->b_sfname);
} else {
@@ -656,9 +677,10 @@ void buf_clear(void)
/// buf_freeall() - free all things allocated for a buffer that are related to
/// the file. Careful: get here with "curwin" NULL when exiting.
///
-/// @param flags BFA_DEL buffer is going to be deleted
-/// BFA_WIPE buffer is going to be wiped out
-/// BFA_KEEP_UNDO do not free undo information
+/// @param flags BFA_DEL buffer is going to be deleted
+/// BFA_WIPE buffer is going to be wiped out
+/// BFA_KEEP_UNDO do not free undo information
+/// BFA_IGNORE_ABORT don't abort even when aborting() returns true
void buf_freeall(buf_T *buf, int flags)
{
bool is_curbuf = (buf == curbuf);
@@ -668,6 +690,7 @@ void buf_freeall(buf_T *buf, int flags)
// Make sure the buffer isn't closed by autocommands.
buf->b_locked++;
+ buf->b_locked_split++;
bufref_T bufref;
set_bufref(&bufref, buf);
@@ -693,6 +716,7 @@ void buf_freeall(buf_T *buf, int flags)
return;
}
buf->b_locked--;
+ buf->b_locked_split--;
// If the buffer was in curwin and the window has changed, go back to that
// window, if it still exists. This avoids that ":edit x" triggering a
@@ -702,7 +726,8 @@ void buf_freeall(buf_T *buf, int flags)
goto_tabpage_win(the_curtab, the_curwin);
unblock_autocmds();
}
- if (aborting()) { // autocmds may abort script processing
+ // autocmds may abort script processing
+ if ((flags & BFA_IGNORE_ABORT) == 0 && aborting()) {
return;
}
@@ -736,10 +761,8 @@ void buf_freeall(buf_T *buf, int flags)
buf->b_flags &= ~BF_READERR; // a read error is no longer relevant
}
-/*
- * Free a buffer structure and the things it contains related to the buffer
- * itself (not the file, that must have been done already).
- */
+/// Free a buffer structure and the things it contains related to the buffer
+/// itself (not the file, that must have been done already).
static void free_buffer(buf_T *buf)
{
pmap_del(handle_T)(&buffer_handles, buf->b_fnum);
@@ -792,8 +815,7 @@ static void free_buffer_stuff(buf_T *buf, int free_flags)
// Avoid losing b:changedtick when deleting buffer: clearing variables
// implies using clear_tv() on b:changedtick and that sets changedtick to
// zero.
- hashitem_T *const changedtick_hi = hash_find(&buf->b_vars->dv_hashtab,
- (const char_u *)"changedtick");
+ hashitem_T *const changedtick_hi = hash_find(&buf->b_vars->dv_hashtab, "changedtick");
assert(changedtick_hi != NULL);
hash_remove(&buf->b_vars->dv_hashtab, changedtick_hi);
}
@@ -803,18 +825,16 @@ static void free_buffer_stuff(buf_T *buf, int free_flags)
buf_init_changedtick(buf);
}
uc_clear(&buf->b_ucmds); // clear local user commands
- buf_delete_signs(buf, (char_u *)"*"); // delete any signs
+ buf_delete_signs(buf, "*"); // delete any signs
extmark_free_all(buf); // delete any extmarks
- map_clear_int(buf, MAP_ALL_MODES, true, false); // clear local mappings
- map_clear_int(buf, MAP_ALL_MODES, true, true); // clear local abbrevs
+ map_clear_mode(buf, MAP_ALL_MODES, true, false); // clear local mappings
+ map_clear_mode(buf, MAP_ALL_MODES, true, true); // clear local abbrevs
XFREE_CLEAR(buf->b_start_fenc);
buf_updates_unload(buf, false);
}
-/*
- * Free the b_wininfo list for buffer "buf".
- */
+/// Free the b_wininfo list for buffer "buf".
static void clear_wininfo(buf_T *buf)
{
wininfo_T *wip;
@@ -826,9 +846,7 @@ static void clear_wininfo(buf_T *buf)
}
}
-/*
- * Go to another buffer. Handles the result of the ATTENTION dialog.
- */
+/// Go to another buffer. Handles the result of the ATTENTION dialog.
void goto_buffer(exarg_T *eap, int start, int dir, int count)
{
bufref_T old_curbuf;
@@ -846,7 +864,7 @@ void goto_buffer(exarg_T *eap, int start, int dir, int count)
enter_cleanup(&cs);
// Quitting means closing the split window, nothing else.
- win_close(curwin, true);
+ win_close(curwin, true, false);
swap_exists_action = SEA_NONE;
swap_exists_did_quit = true;
@@ -879,7 +897,7 @@ void handle_swap_exists(bufref_T *old_curbuf)
// open a new, empty buffer.
swap_exists_action = SEA_NONE; // don't want it again
swap_exists_did_quit = true;
- close_buffer(curwin, curbuf, DOBUF_UNLOAD, false);
+ close_buffer(curwin, curbuf, DOBUF_UNLOAD, false, false);
if (old_curbuf == NULL
|| !bufref_valid(old_curbuf)
|| old_curbuf->br_buf == curbuf) {
@@ -891,14 +909,7 @@ void handle_swap_exists(bufref_T *old_curbuf)
buf = old_curbuf->br_buf;
}
if (buf != NULL) {
- int old_msg_silent = msg_silent;
-
- if (shortmess(SHM_FILEINFO)) {
- msg_silent = 1; // prevent fileinfo message
- }
enter_buffer(buf);
- // restore msg_silent, so that the command line will be shown
- msg_silent = old_msg_silent;
if (old_tw != curbuf->b_p_tw) {
check_colorcolumn(curwin);
@@ -943,13 +954,13 @@ void handle_swap_exists(bufref_T *old_curbuf)
/// @param end_bnr buffer nr or last buffer nr in a range
///
/// @return error message or NULL
-char *do_bufdel(int command, char_u *arg, int addr_count, int start_bnr, int end_bnr, int forceit)
+char *do_bufdel(int command, char *arg, int addr_count, int start_bnr, int end_bnr, int forceit)
{
int do_current = 0; // delete current buffer?
int deleted = 0; // number of buffers deleted
char *errormsg = NULL; // return value
int bnr; // buffer number
- char_u *p;
+ char *p;
if (addr_count == 0) {
(void)do_buffer(command, DOBUF_CURRENT, FORWARD, 0, forceit);
@@ -987,8 +998,7 @@ char *do_bufdel(int command, char_u *arg, int addr_count, int start_bnr, int end
}
if (!ascii_isdigit(*arg)) {
p = skiptowhite_esc(arg);
- bnr = buflist_findpat(arg, p, command == DOBUF_WIPE,
- false, false);
+ bnr = buflist_findpat(arg, p, command == DOBUF_WIPE, false, false);
if (bnr < 0) { // failed
break;
}
@@ -1027,15 +1037,11 @@ char *do_bufdel(int command, char_u *arg, int addr_count, int start_bnr, int end
}
}
-
return errormsg;
}
-
-/*
- * Make the current buffer empty.
- * Used when it is wiped out and it's the last buffer.
- */
+/// Make the current buffer empty.
+/// Used when it is wiped out and it's the last buffer.
static int empty_curbuf(int close_others, int forceit, int action)
{
int retval;
@@ -1050,8 +1056,24 @@ static int empty_curbuf(int close_others, int forceit, int action)
set_bufref(&bufref, buf);
if (close_others) {
- // Close any other windows on this buffer, then make it empty.
- close_windows(buf, true);
+ bool can_close_all_others = true;
+ if (curwin->w_floating) {
+ // Closing all other windows with this buffer may leave only floating windows.
+ can_close_all_others = false;
+ for (win_T *wp = firstwin; !wp->w_floating; wp = wp->w_next) {
+ if (wp->w_buffer != curbuf) {
+ // Found another non-floating window with a different (probably unlisted) buffer.
+ // Closing all other windows with this buffer is fine in this case.
+ can_close_all_others = true;
+ break;
+ }
+ }
+ }
+ // If it is fine to close all other windows with this buffer, keep the current window and
+ // close any other windows with this buffer, then make it empty.
+ // Otherwise close_windows() will refuse to close the last non-floating window, so allow it
+ // to close the current window instead.
+ close_windows(buf, can_close_all_others);
}
setpcmark();
@@ -1062,7 +1084,7 @@ static int empty_curbuf(int close_others, int forceit, int action)
// the old one. But do_ecmd() may have done that already, check
// if the buffer still exists.
if (buf != curbuf && bufref_valid(&bufref) && buf->b_nwindows == 0) {
- close_buffer(NULL, buf, action, false);
+ close_buffer(NULL, buf, action, false, false);
}
if (!close_others) {
@@ -1168,11 +1190,14 @@ int do_buffer(int action, int start, int dir, int count, int forceit)
return FAIL;
}
-
// delete buffer "buf" from memory and/or the list
if (unload) {
int forward;
bufref_T bufref;
+ if (buf->b_locked) {
+ emsg(_(e_buflocked));
+ return FAIL;
+ }
set_bufref(&bufref, buf);
// When unloading or deleting a buffer that's already unloaded and
@@ -1182,7 +1207,7 @@ int do_buffer(int action, int start, int dir, int count, int forceit)
}
if (!forceit && bufIsChanged(buf)) {
- if ((p_confirm || cmdmod.confirm) && p_write) {
+ if ((p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) && p_write) {
dialog_changed(buf, false);
if (!bufref_valid(&bufref)) {
// Autocommand deleted buffer, oops! It's not changed now.
@@ -1202,13 +1227,12 @@ int do_buffer(int action, int start, int dir, int count, int forceit)
}
if (!forceit && buf->terminal && terminal_running(buf->terminal)) {
- if (p_confirm || cmdmod.confirm) {
+ if (p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) {
if (!dialog_close_terminal(buf)) {
return FAIL;
}
} else {
- semsg(_("E89: %s will be killed (add ! to override)"),
- (char *)buf->b_fname);
+ semsg(_("E89: %s will be killed (add ! to override)"), buf->b_fname);
return FAIL;
}
}
@@ -1232,12 +1256,13 @@ int do_buffer(int action, int start, int dir, int count, int forceit)
}
// If the deleted buffer is the current one, close the current window
- // (unless it's the only window). Repeat this so long as we end up in
- // a window with this buffer.
+ // (unless it's the only non-floating window).
+ // When the autocommand window is involved win_close() may need to print an error message.
+ // Repeat this so long as we end up in a window with this buffer.
while (buf == curbuf
&& !(curwin->w_closing || curwin->w_buffer->b_locked > 0)
- && (!ONE_WINDOW || first_tabpage->tp_next != NULL)) {
- if (win_close(curwin, false) == FAIL) {
+ && (lastwin == aucmd_win || !last_window(curwin))) {
+ if (win_close(curwin, false, false) == FAIL) {
break;
}
}
@@ -1246,7 +1271,7 @@ int do_buffer(int action, int start, int dir, int count, int forceit)
if (buf != curbuf) {
close_windows(buf, false);
if (buf != curbuf && bufref_valid(&bufref) && buf->b_nwindows <= 0) {
- close_buffer(NULL, buf, action, false);
+ close_buffer(NULL, buf, action, false, false);
}
return OK;
}
@@ -1275,8 +1300,10 @@ int do_buffer(int action, int start, int dir, int count, int forceit)
while (jumpidx != curwin->w_jumplistidx) {
buf = buflist_findnr(curwin->w_jumplist[jumpidx].fmark.fnum);
if (buf != NULL) {
- if (buf == curbuf || !buf->b_p_bl) {
- buf = NULL; // skip current and unlisted bufs
+ // Skip current and unlisted bufs. Also skip a quickfix
+ // buffer, it might be deleted soon.
+ if (buf == curbuf || !buf->b_p_bl || bt_quickfix(buf)) {
+ buf = NULL;
} else if (buf->b_ml.ml_mfp == NULL) {
// skip unloaded buf, but may keep it for later
if (bp == NULL) {
@@ -1314,7 +1341,7 @@ int do_buffer(int action, int start, int dir, int count, int forceit)
continue;
}
// in non-help buffer, try to skip help buffers, and vv
- if (buf->b_help == curbuf->b_help && buf->b_p_bl) {
+ if (buf->b_help == curbuf->b_help && buf->b_p_bl && !bt_quickfix(buf)) {
if (buf->b_ml.ml_mfp != NULL) { // found loaded buffer
break;
}
@@ -1334,7 +1361,7 @@ int do_buffer(int action, int start, int dir, int count, int forceit)
}
if (buf == NULL) { // No loaded buffer, find listed one
FOR_ALL_BUFFERS(buf2) {
- if (buf2->b_p_bl && buf2 != curbuf) {
+ if (buf2->b_p_bl && buf2 != curbuf && !bt_quickfix(buf2)) {
buf = buf2;
break;
}
@@ -1346,6 +1373,9 @@ int do_buffer(int action, int start, int dir, int count, int forceit)
} else {
buf = curbuf->b_prev;
}
+ if (bt_quickfix(buf)) {
+ buf = NULL;
+ }
}
}
@@ -1379,7 +1409,7 @@ int do_buffer(int action, int start, int dir, int count, int forceit)
// Check if the current buffer may be abandoned.
if (action == DOBUF_GOTO && !can_abandon(curbuf, forceit)) {
- if ((p_confirm || cmdmod.confirm) && p_write) {
+ if ((p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) && p_write) {
bufref_T bufref;
set_bufref(&bufref, buf);
dialog_changed(curbuf, false);
@@ -1408,16 +1438,15 @@ int do_buffer(int action, int start, int dir, int count, int forceit)
return OK;
}
-
-/*
- * Set current buffer to "buf". Executes autocommands and closes current
- * buffer. "action" tells how to close the current buffer:
- * DOBUF_GOTO free or hide it
- * DOBUF_SPLIT nothing
- * DOBUF_UNLOAD unload it
- * DOBUF_DEL delete it
- * DOBUF_WIPE wipe it out
- */
+/// Set current buffer to "buf". Executes autocommands and closes current
+/// buffer.
+///
+/// @param action tells how to close the current buffer:
+/// DOBUF_GOTO free or hide it
+/// DOBUF_SPLIT nothing
+/// DOBUF_UNLOAD unload it
+/// DOBUF_DEL delete it
+/// DOBUF_WIPE wipe it out
void set_curbuf(buf_T *buf, int action)
{
buf_T *prevbuf;
@@ -1426,7 +1455,7 @@ void set_curbuf(buf_T *buf, int action)
long old_tw = curbuf->b_p_tw;
setpcmark();
- if (!cmdmod.keepalt) {
+ if ((cmdmod.cmod_flags & CMOD_KEEPALT) == 0) {
curwin->w_alt_fnum = curbuf->b_fnum; // remember alternate file
}
buflist_altfpos(curwin); // remember curpos
@@ -1441,8 +1470,8 @@ void set_curbuf(buf_T *buf, int action)
set_bufref(&prevbufref, prevbuf);
set_bufref(&newbufref, buf);
- // Autocommands may delete the curren buffer and/or the buffer we want to go
- // to. In those cases don't close the buffer.
+ // Autocommands may delete the current buffer and/or the buffer we want to
+ // go to. In those cases don't close the buffer.
if (!apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, false, curbuf)
|| (bufref_valid(&prevbufref) && bufref_valid(&newbufref)
&& !aborting())) {
@@ -1454,7 +1483,11 @@ 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 & MODE_INSERT) == 0 || curbuf->b_nwindows <= 1)) {
u_sync(false);
}
close_buffer(prevbuf == curwin->w_buffer ? curwin : NULL,
@@ -1463,7 +1496,7 @@ void set_curbuf(buf_T *buf, int action)
? action
: (action == DOBUF_GOTO && !buf_hide(prevbuf)
&& !bufIsChanged(prevbuf)) ? DOBUF_UNLOAD : 0,
- false);
+ false, false);
if (curwin != previouswin && win_valid(previouswin)) {
// autocommands changed curwin, Grr!
curwin = previouswin;
@@ -1473,10 +1506,15 @@ void set_curbuf(buf_T *buf, int action)
// An autocommand may have deleted "buf", already entered it (e.g., when
// it did ":bunload") or aborted the script processing!
// If curwin->w_buffer is null, enter_buffer() will make it valid again
- if ((buf_valid(buf) && buf != curbuf
- && !aborting()
- ) || curwin->w_buffer == NULL) {
- enter_buffer(buf);
+ bool valid = buf_valid(buf);
+ if ((valid && buf != curbuf && !aborting()) || curwin->w_buffer == NULL) {
+ // If the buffer is not valid but curwin->w_buffer is NULL we must
+ // enter some buffer. Using the last one is hopefully OK.
+ if (!valid) {
+ enter_buffer(lastbuf);
+ } else {
+ enter_buffer(buf);
+ }
if (old_tw != curbuf->b_p_tw) {
check_colorcolumn(curwin);
}
@@ -1487,13 +1525,16 @@ void set_curbuf(buf_T *buf, int action)
}
}
-/*
- * Enter a new current buffer.
- * Old curbuf must have been abandoned already! This also means "curbuf" may
- * be pointing to freed memory.
- */
+/// Enter a new current buffer.
+/// Old curbuf must have been abandoned already! This also means "curbuf" may
+/// be pointing to freed memory.
void enter_buffer(buf_T *buf)
{
+ // Get the buffer in the current window.
+ curwin->w_buffer = buf;
+ curbuf = buf;
+ curbuf->b_nwindows++;
+
// Copy buffer and window local option values. Not for a help buffer.
buf_copy_options(buf, BCO_ENTER | BCO_NOHELP);
if (!buf->b_help) {
@@ -1504,11 +1545,6 @@ void enter_buffer(buf_T *buf)
}
foldUpdateAll(curwin); // update folds (later).
- // Get the buffer in the current window.
- curwin->w_buffer = buf;
- curbuf = buf;
- curbuf->b_nwindows++;
-
if (curwin->w_p_diff) {
diff_buf_add(curbuf);
}
@@ -1561,7 +1597,6 @@ void enter_buffer(buf_T *buf)
scroll_cursor_halfway(false); // redisplay at correct position
}
-
// Change directories when the 'acd' option is set.
do_autochdir();
@@ -1578,8 +1613,8 @@ void enter_buffer(buf_T *buf)
redraw_later(curwin, NOT_VALID);
}
-// Change to the directory of the current buffer.
-// Don't do this while still starting up.
+/// Change to the directory of the current buffer.
+/// Don't do this while still starting up.
void do_autochdir(void)
{
if (p_acd) {
@@ -1617,8 +1652,6 @@ void no_write_message_nobang(const buf_T *const buf)
// functions for dealing with the buffer list
//
-static int top_file_num = 1; ///< highest file number
-
/// Initialize b:changedtick and changedtick_val attribute
///
/// @param[out] buf Buffer to initialize for.
@@ -1656,11 +1689,11 @@ static inline void buf_init_changedtick(buf_T *const buf)
/// @param flags BLN_ defines
/// @param bufnr
///
-/// @return pointer to the buffer
-buf_T *buflist_new(char_u *ffname_arg, char_u *sfname_arg, linenr_T lnum, int flags)
+/// @return pointer to the buffer
+buf_T *buflist_new(char *ffname_arg, char *sfname_arg, linenr_T lnum, int flags)
{
- char_u *ffname = ffname_arg;
- char_u *sfname = sfname_arg;
+ char *ffname = ffname_arg;
+ char *sfname = sfname_arg;
buf_T *buf;
fname_expand(curbuf, &ffname, &sfname); // will allocate ffname
@@ -1670,11 +1703,9 @@ buf_T *buflist_new(char_u *ffname_arg, char_u *sfname_arg, linenr_T lnum, int fl
// We can use inode numbers when the file exists. Works better
// for hard links.
FileID file_id;
- bool file_id_valid = (sfname != NULL
- && os_fileid((char *)sfname, &file_id));
+ bool file_id_valid = (sfname != NULL && os_fileid(sfname, &file_id));
if (ffname != NULL && !(flags & (BLN_DUMMY | BLN_NEW))
- && (buf = buflist_findname_file_id(ffname, &file_id,
- file_id_valid)) != NULL) {
+ && (buf = buflist_findname_file_id(ffname, &file_id, file_id_valid)) != NULL) {
xfree(ffname);
if (lnum != 0) {
buflist_setfpos(buf, (flags & BLN_NOCURWIN) ? NULL : curwin,
@@ -1698,75 +1729,45 @@ buf_T *buflist_new(char_u *ffname_arg, char_u *sfname_arg, linenr_T lnum, int fl
return buf;
}
- /*
- * If the current buffer has no name and no contents, use the current
- * buffer. Otherwise: Need to allocate a new buffer structure.
- *
- * This is the ONLY place where a new buffer structure is allocated!
- * (A spell file buffer is allocated in spell.c, but that's not a normal
- * buffer.)
- */
+ // If the current buffer has no name and no contents, use the current
+ // buffer. Otherwise: Need to allocate a new buffer structure.
+ //
+ // This is the ONLY place where a new buffer structure is allocated!
+ // (A spell file buffer is allocated in spell.c, but that's not a normal
+ // buffer.)
buf = NULL;
if ((flags & BLN_CURBUF) && curbuf_reusable()) {
assert(curbuf != NULL);
buf = curbuf;
// It's like this buffer is deleted. Watch out for autocommands that
// change curbuf! If that happens, allocate a new buffer anyway.
- if (curbuf->b_p_bl) {
- apply_autocmds(EVENT_BUFDELETE, NULL, NULL, false, curbuf);
- }
- if (buf == curbuf) {
- apply_autocmds(EVENT_BUFWIPEOUT, NULL, NULL, false, curbuf);
+ buf_freeall(buf, BFA_WIPE | BFA_DEL);
+ if (buf != curbuf) { // autocommands deleted the buffer!
+ return NULL;
}
if (aborting()) { // autocmds may abort script processing
xfree(ffname);
return NULL;
}
- if (buf == curbuf) {
- // Make sure 'bufhidden' and 'buftype' are empty
- clear_string_option(&buf->b_p_bh);
- clear_string_option(&buf->b_p_bt);
- }
}
if (buf != curbuf || curbuf == NULL) {
buf = xcalloc(1, sizeof(buf_T));
// init b: variables
buf->b_vars = tv_dict_alloc();
- buf->b_signcols_valid = false;
+ buf->b_signcols.valid = false;
init_var_dict(buf->b_vars, &buf->b_bufvar, VAR_SCOPE);
buf_init_changedtick(buf);
}
if (ffname != NULL) {
buf->b_ffname = ffname;
- buf->b_sfname = vim_strsave(sfname);
+ buf->b_sfname = xstrdup(sfname);
}
clear_wininfo(buf);
buf->b_wininfo = xcalloc(1, sizeof(wininfo_T));
- if (ffname != NULL && (buf->b_ffname == NULL || buf->b_sfname == NULL)) {
- if (buf->b_sfname != buf->b_ffname) {
- XFREE_CLEAR(buf->b_sfname);
- } else {
- buf->b_sfname = NULL;
- }
- XFREE_CLEAR(buf->b_ffname);
- if (buf != curbuf) {
- free_buffer(buf);
- }
- return NULL;
- }
-
if (buf == curbuf) {
- // free all things allocated for this buffer
- buf_freeall(buf, 0);
- if (buf != curbuf) { // autocommands deleted the buffer!
- return NULL;
- }
- if (aborting()) { // autocmds may abort script processing
- return NULL;
- }
free_buffer_stuff(buf, kBffInitChangedtick); // delete local vars et al.
// Init the options.
@@ -1776,9 +1777,7 @@ buf_T *buflist_new(char_u *ffname_arg, char_u *sfname_arg, linenr_T lnum, int fl
// need to reload lmaps and set b:keymap_name
curbuf->b_kmap_state |= KEYMAP_INIT;
} else {
- /*
- * put new buffer at the end of the buffer list
- */
+ // put new buffer at the end of the buffer list
buf->b_next = NULL;
if (firstbuf == NULL) { // buffer list is empty
buf->b_prev = NULL;
@@ -1804,7 +1803,8 @@ buf_T *buflist_new(char_u *ffname_arg, char_u *sfname_arg, linenr_T lnum, int fl
buf_copy_options(buf, BCO_ALWAYS);
}
- buf->b_wininfo->wi_fpos.lnum = lnum;
+ buf->b_wininfo->wi_mark = (fmark_T)INIT_FMARK;
+ buf->b_wininfo->wi_mark.mark.lnum = lnum;
buf->b_wininfo->wi_win = curwin;
hash_init(&buf->b_s.b_keywtab);
@@ -1870,11 +1870,9 @@ bool curbuf_reusable(void)
&& !curbufIsChanged());
}
-/*
- * Free the memory for the options of a buffer.
- * If "free_p_ff" is true also free 'fileformat', 'buftype' and
- * 'fileencoding'.
- */
+/// Free the memory for the options of a buffer.
+/// If "free_p_ff" is true also free 'fileformat', 'buftype' and
+/// 'fileencoding'.
void free_buf_options(buf_T *buf, int free_p_ff)
{
if (free_p_ff) {
@@ -1896,10 +1894,8 @@ void free_buf_options(buf_T *buf, int free_p_ff)
clear_string_option(&buf->b_p_flp);
clear_string_option(&buf->b_p_isk);
clear_string_option(&buf->b_p_vsts);
- xfree(buf->b_p_vsts_nopaste);
- buf->b_p_vsts_nopaste = NULL;
- xfree(buf->b_p_vsts_array);
- buf->b_p_vsts_array = NULL;
+ XFREE_CLEAR(buf->b_p_vsts_nopaste);
+ XFREE_CLEAR(buf->b_p_vsts_array);
clear_string_option(&buf->b_p_vts);
XFREE_CLEAR(buf->b_p_vts_array);
clear_string_option(&buf->b_p_keymap);
@@ -1921,6 +1917,7 @@ void free_buf_options(buf_T *buf, int free_p_ff)
clear_string_option(&buf->b_p_cink);
clear_string_option(&buf->b_p_cino);
clear_string_option(&buf->b_p_cinw);
+ clear_string_option(&buf->b_p_cinsd);
clear_string_option(&buf->b_p_cpt);
clear_string_option(&buf->b_p_cfu);
clear_string_option(&buf->b_p_ofu);
@@ -1943,7 +1940,6 @@ void free_buf_options(buf_T *buf, int free_p_ff)
clear_string_option(&buf->b_p_menc);
}
-
/// Get alternate file "n".
/// Set linenr to "lnum" or altfpos.lnum if "lnum" == 0.
/// Also set cursor column to altfpos.col if 'startofline' is not set.
@@ -1956,7 +1952,7 @@ int buflist_getfile(int n, linenr_T lnum, int options, int forceit)
{
buf_T *buf;
win_T *wp = NULL;
- pos_T *fpos;
+ fmark_T *fm = NULL;
colnr_T col;
buf = buflist_findnr(n);
@@ -1974,19 +1970,17 @@ int buflist_getfile(int n, linenr_T lnum, int options, int forceit)
return OK;
}
- if (text_locked()) {
- text_locked_msg();
- return FAIL;
- }
- if (curbuf_locked()) {
+ if (text_or_buf_locked()) {
return FAIL;
}
+ bool restore_view = false;
// altfpos may be changed by getfile(), get it now
if (lnum == 0) {
- fpos = buflist_findfpos(buf);
- lnum = fpos->lnum;
- col = fpos->col;
+ fm = buflist_findfmark(buf);
+ lnum = fm->mark.lnum;
+ col = fm->mark.col;
+ restore_view = true;
} else {
col = 0;
}
@@ -2030,18 +2024,21 @@ int buflist_getfile(int n, linenr_T lnum, int options, int forceit)
curwin->w_cursor.coladd = 0;
curwin->w_set_curswant = true;
}
+ if (jop_flags & JOP_VIEW && restore_view) {
+ mark_view_restore(fm);
+ }
return OK;
}
RedrawingDisabled--;
return FAIL;
}
-// Go to the last known line number for the current buffer.
+/// Go to the last known line number for the current buffer.
void buflist_getfpos(void)
{
pos_T *fpos;
- fpos = buflist_findfpos(curbuf);
+ fpos = &buflist_findfmark(curbuf)->mark;
curwin->w_cursor.lnum = fpos->lnum;
check_cursor_lnum();
@@ -2056,24 +2053,23 @@ void buflist_getfpos(void)
}
}
-/*
- * Find file in buffer list by name (it has to be for the current window).
- * Returns NULL if not found.
- */
-buf_T *buflist_findname_exp(char_u *fname)
+/// Find file in buffer list by name (it has to be for the current window).
+///
+/// @return buffer or NULL if not found
+buf_T *buflist_findname_exp(char *fname)
{
- char_u *ffname;
+ char *ffname;
buf_T *buf = NULL;
// First make the name into a full path name
- ffname = (char_u *)FullName_save((char *)fname,
+ ffname = FullName_save(fname,
#ifdef UNIX
- // force expansion, get rid of symbolic links
- true
+ // force expansion, get rid of symbolic links
+ true
#else
- false
+ false
#endif
- );
+ ); // NOLINT(whitespace/parens)
if (ffname != NULL) {
buf = buflist_findname(ffname);
xfree(ffname);
@@ -2081,25 +2077,24 @@ buf_T *buflist_findname_exp(char_u *fname)
return buf;
}
-/*
- * Find file in buffer list by name (it has to be for the current window).
- * "ffname" must have a full path.
- * Skips dummy buffers.
- * Returns NULL if not found.
- */
-buf_T *buflist_findname(char_u *ffname)
+/// Find file in buffer list by name (it has to be for the current window).
+/// "ffname" must have a full path.
+/// Skips dummy buffers.
+///
+/// @return buffer or NULL if not found
+buf_T *buflist_findname(char *ffname)
{
FileID file_id;
- bool file_id_valid = os_fileid((char *)ffname, &file_id);
+ bool file_id_valid = os_fileid(ffname, &file_id);
return buflist_findname_file_id(ffname, &file_id, file_id_valid);
}
-/*
- * Same as buflist_findname(), but pass the FileID structure to avoid
- * getting it twice for the same file.
- * Returns NULL if not found.
- */
-static buf_T *buflist_findname_file_id(char_u *ffname, FileID *file_id, bool file_id_valid)
+/// Same as buflist_findname(), but pass the FileID structure to avoid
+/// getting it twice for the same file.
+///
+/// @return buffer or NULL if not found
+static buf_T *buflist_findname_file_id(char *ffname, FileID *file_id, bool file_id_valid)
+ FUNC_ATTR_PURE
{
// Start at the last buffer, expect to find a match sooner.
FOR_ALL_BUFFERS_BACKWARDS(buf) {
@@ -2112,23 +2107,23 @@ static buf_T *buflist_findname_file_id(char_u *ffname, FileID *file_id, bool fil
}
/// Find file in buffer list by a regexp pattern.
-/// Return fnum of the found buffer.
-/// Return < 0 for error.
///
/// @param pattern_end pointer to first char after pattern
/// @param unlisted find unlisted buffers
/// @param diffmode find diff-mode buffers only
/// @param curtab_only find buffers in current tab only
-int buflist_findpat(const char_u *pattern, const char_u *pattern_end, bool unlisted, bool diffmode,
+///
+/// @return fnum of the found buffer or < 0 for error.
+int buflist_findpat(const char *pattern, const char *pattern_end, bool unlisted, bool diffmode,
bool curtab_only)
FUNC_ATTR_NONNULL_ARG(1)
{
int match = -1;
int find_listed;
- char_u *pat;
- char_u *patend;
+ char *pat;
+ char *patend;
int attempt;
- char_u *p;
+ char *p;
int toggledollar;
if (pattern_end == pattern + 1 && (*pattern == '%' || *pattern == '#')) {
@@ -2142,7 +2137,6 @@ int buflist_findpat(const char_u *pattern, const char_u *pattern_end, bool unlis
match = -1;
}
} else {
- //
// Try four ways of matching a listed buffer:
// attempt == 0: without '^' or '$' (at any position)
// attempt == 1: with '^' at start (only at position 0)
@@ -2150,9 +2144,8 @@ int buflist_findpat(const char_u *pattern, const char_u *pattern_end, bool unlis
// attempt == 3: with '^' at start and '$' at end (only full match)
// Repeat this for finding an unlisted buffer if there was no matching
// listed buffer.
- //
- pat = file_pat_to_reg_pat(pattern, pattern_end, NULL, false);
+ pat = file_pat_to_reg_pat((char *)pattern, (char *)pattern_end, NULL, false);
if (pat == NULL) {
return -1;
}
@@ -2233,7 +2226,7 @@ int buflist_findpat(const char_u *pattern, const char_u *pattern_end, bool unlis
typedef struct {
buf_T *buf;
- char_u *match;
+ char *match;
} bufmatch_T;
/// Compare functions for qsort() below, that compares b_last_used.
@@ -2248,18 +2241,17 @@ static int buf_time_compare(const void *s1, const void *s2)
return buf1->b_last_used > buf2->b_last_used ? -1 : 1;
}
-/*
- * Find all buffer names that match.
- * For command line expansion of ":buf" and ":sbuf".
- * Return OK if matches found, FAIL otherwise.
- */
-int ExpandBufnames(char_u *pat, int *num_file, char_u ***file, int options)
+/// Find all buffer names that match.
+/// For command line expansion of ":buf" and ":sbuf".
+///
+/// @return OK if matches found, FAIL otherwise.
+int ExpandBufnames(char *pat, int *num_file, char ***file, int options)
{
int count = 0;
int round;
- char_u *p;
+ char *p;
int attempt;
- char_u *patc;
+ char *patc;
bufmatch_T *matches = NULL;
*num_file = 0; // return values in case of FAIL
@@ -2317,7 +2309,7 @@ int ExpandBufnames(char_u *pat, int *num_file, char_u ***file, int options)
if (options & WILD_HOME_REPLACE) {
p = home_replace_save(buf, p);
} else {
- p = vim_strsave(p);
+ p = xstrdup(p);
}
if (matches != NULL) {
matches[count].buf = buf;
@@ -2358,9 +2350,9 @@ int ExpandBufnames(char_u *pat, int *num_file, char_u ***file, int options)
// if the current buffer is first in the list, place it at the end
if (matches[0].buf == curbuf) {
for (int i = 1; i < count; i++) {
- (*file)[i-1] = matches[i].match;
+ (*file)[i - 1] = matches[i].match;
}
- (*file)[count-1] = matches[0].match;
+ (*file)[count - 1] = matches[0].match;
} else {
for (int i = 0; i < count; i++) {
(*file)[i] = matches[i].match;
@@ -2373,15 +2365,14 @@ int ExpandBufnames(char_u *pat, int *num_file, char_u ***file, int options)
return count == 0 ? FAIL : OK;
}
-
/// Check for a match on the file name for buffer "buf" with regprog "prog".
///
-/// @param ignore_case When true, ignore case. Use 'fic' otherwise.
-static char_u *buflist_match(regmatch_T *rmp, buf_T *buf, bool ignore_case)
+/// @param ignore_case When true, ignore case. Use 'fic' otherwise.
+static char *buflist_match(regmatch_T *rmp, buf_T *buf, bool ignore_case)
{
// First try the short file name, then the long file name.
- char_u *match = fname_match(rmp, buf->b_sfname, ignore_case);
- if (match == NULL) {
+ char *match = fname_match(rmp, buf->b_sfname, ignore_case);
+ if (match == NULL && rmp->regprog != NULL) {
match = fname_match(rmp, buf->b_ffname, ignore_case);
}
return match;
@@ -2389,19 +2380,20 @@ static char_u *buflist_match(regmatch_T *rmp, buf_T *buf, bool ignore_case)
/// Try matching the regexp in "prog" with file name "name".
///
-/// @param ignore_case When true, ignore case. Use 'fileignorecase' otherwise.
-/// @return "name" when there is a match, NULL when not.
-static char_u *fname_match(regmatch_T *rmp, char_u *name, bool ignore_case)
+/// @param ignore_case When true, ignore case. Use 'fileignorecase' otherwise.
+///
+/// @return "name" when there is a match, NULL when not.
+static char *fname_match(regmatch_T *rmp, char *name, bool ignore_case)
{
- char_u *match = NULL;
- char_u *p;
+ char *match = NULL;
+ char *p;
if (name != NULL) {
// Ignore case when 'fileignorecase' or the argument is set.
rmp->rm_ic = p_fic || ignore_case;
if (vim_regexec(rmp, name, (colnr_T)0)) {
match = name;
- } else {
+ } else if (rmp->regprog != NULL) {
// Replace $(HOME) with '~' and try matching again.
p = home_replace_save(NULL, name);
if (vim_regexec(rmp, p, (colnr_T)0)) {
@@ -2431,11 +2423,9 @@ buf_T *buflist_findnr(int nr)
/// @param helptail for help buffers return tail only
///
/// @return a pointer to allocated memory, of NULL when failed.
-char_u *buflist_nr2name(int n, int fullname, int helptail)
+char *buflist_nr2name(int n, int fullname, int helptail)
{
- buf_T *buf;
-
- buf = buflist_findnr(n);
+ buf_T *buf = buflist_findnr(n);
if (buf == NULL) {
return NULL;
}
@@ -2486,8 +2476,11 @@ void buflist_setfpos(buf_T *const buf, win_T *const win, linenr_T lnum, colnr_T
}
}
if (lnum != 0) {
- wip->wi_fpos.lnum = lnum;
- wip->wi_fpos.col = col;
+ wip->wi_mark.mark.lnum = lnum;
+ wip->wi_mark.mark.col = col;
+ if (win != NULL) {
+ wip->wi_mark.view = mark_view_make(win->w_topline, wip->wi_mark.mark);
+ }
}
if (copy_options && win != NULL) {
// Save the window-specific option values.
@@ -2506,7 +2499,6 @@ void buflist_setfpos(buf_T *const buf, win_T *const win, linenr_T lnum, colnr_T
}
}
-
/// Check that "wip" has 'diff' set and the diff is only for another tab page.
/// That's because a diff is local to a tab page.
static bool wininfo_other_tab_diff(wininfo_T *wip)
@@ -2525,14 +2517,15 @@ static bool wininfo_other_tab_diff(wininfo_T *wip)
return false;
}
-// Find info for the current window in buffer "buf".
-// If not found, return the info for the most recently used window.
-// When "need_options" is true skip entries where wi_optset is false.
-// When "skip_diff_buffer" is true avoid windows with 'diff' set that is in
-// another tab page.
-// Returns NULL when there isn't any info.
+/// Find info for the current window in buffer "buf".
+/// If not found, return the info for the most recently used window.
+///
+/// @param need_options when true, skip entries where wi_optset is false.
+/// @param skip_diff_buffer when true, avoid windows with 'diff' set that is in another tab page.
+///
+/// @return NULL when there isn't any info.
static wininfo_T *find_wininfo(buf_T *buf, bool need_options, bool skip_diff_buffer)
- FUNC_ATTR_NONNULL_ALL
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE
{
wininfo_T *wip;
@@ -2567,12 +2560,10 @@ static wininfo_T *find_wininfo(buf_T *buf, bool need_options, bool skip_diff_buf
return wip;
}
-/*
- * Reset the local window options to the values last used in this window.
- * If the buffer wasn't used in this window before, use the values from
- * the most recently used window. If the values were never set, use the
- * global values for the window.
- */
+/// Reset the local window options to the values last used in this window.
+/// If the buffer wasn't used in this window before, use the values from
+/// the most recently used window. If the values were never set, use the
+/// global values for the window.
void get_winopts(buf_T *buf)
{
clear_winopt(&curwin->w_onebuf_opt);
@@ -2607,28 +2598,26 @@ void get_winopts(buf_T *buf)
didset_window_options(curwin);
}
-/*
- * Find the position (lnum and col) for the buffer 'buf' for the current
- * window.
- * Returns a pointer to no_position if no position is found.
- */
-pos_T *buflist_findfpos(buf_T *buf)
+/// Find the mark for the buffer 'buf' for the current window.
+///
+/// @return a pointer to no_position if no position is found.
+fmark_T *buflist_findfmark(buf_T *buf)
+ FUNC_ATTR_PURE
{
- static pos_T no_position = { 1, 0, 0 };
+ static fmark_T no_position = { { 1, 0, 0 }, 0, 0, { 0 }, NULL };
wininfo_T *const wip = find_wininfo(buf, false, false);
- return (wip == NULL) ? &no_position : &(wip->wi_fpos);
+ return (wip == NULL) ? &no_position : &(wip->wi_mark);
}
-/*
- * Find the lnum for the buffer 'buf' for the current window.
- */
+/// Find the lnum for the buffer 'buf' for the current window.
linenr_T buflist_findlnum(buf_T *buf)
+ FUNC_ATTR_PURE
{
- return buflist_findfpos(buf)->lnum;
+ return buflist_findfmark(buf)->mark.lnum;
}
-// List all known file names (for :files and :buffers command).
+/// List all known file names (for :files and :buffers command).
void buflist_list(exarg_T *eap)
{
buf_T *buf = firstbuf;
@@ -2656,8 +2645,7 @@ void buflist_list(exarg_T *eap)
for (;
buf != NULL && !got_int;
buf = buflist_data != NULL
- ? (++p < buflist_data + buflist.ga_len ? *p : NULL)
- : buf->b_next) {
+ ? (++p < buflist_data + buflist.ga_len ? *p : NULL) : buf->b_next) {
const bool is_terminal = buf->terminal;
const bool job_running = buf->terminal && terminal_running(buf->terminal);
@@ -2683,7 +2671,7 @@ void buflist_list(exarg_T *eap)
if (buf_spname(buf) != NULL) {
STRLCPY(NameBuff, buf_spname(buf), MAXPATHL);
} else {
- home_replace(buf, buf->b_fname, NameBuff, MAXPATHL, true);
+ home_replace(buf, buf->b_fname, (char *)NameBuff, MAXPATHL, true);
}
if (message_filtered(NameBuff)) {
@@ -2713,20 +2701,18 @@ void buflist_list(exarg_T *eap)
}
// put "line 999" in column 40 or after the file name
- i = 40 - vim_strsize(IObuff);
+ i = 40 - vim_strsize((char *)IObuff);
do {
IObuff[len++] = ' ';
} while (--i > 0 && len < IOSIZE - 18);
if (vim_strchr(eap->arg, 't') && buf->b_last_used) {
undo_fmt_time(IObuff + len, (size_t)(IOSIZE - len), buf->b_last_used);
} else {
- vim_snprintf((char *)IObuff + len, (size_t)(IOSIZE - len),
- _("line %" PRId64),
- buf == curbuf ? (int64_t)curwin->w_cursor.lnum
- : (int64_t)buflist_findlnum(buf));
+ vim_snprintf((char *)IObuff + len, (size_t)(IOSIZE - len), _("line %" PRId64),
+ buf == curbuf ? (int64_t)curwin->w_cursor.lnum : (int64_t)buflist_findlnum(buf));
}
- msg_outtrans(IObuff);
+ msg_outtrans((char *)IObuff);
line_breakcheck();
}
@@ -2735,17 +2721,14 @@ void buflist_list(exarg_T *eap)
}
}
-/*
- * Get file name and line number for file 'fnum'.
- * Used by DoOneCmd() for translating '%' and '#'.
- * Used by insert_reg() and cmdline_paste() for '#' register.
- * Return FAIL if not found, OK for success.
- */
-int buflist_name_nr(int fnum, char_u **fname, linenr_T *lnum)
+/// Get file name and line number for file 'fnum'.
+/// Used by DoOneCmd() for translating '%' and '#'.
+/// Used by insert_reg() and cmdline_paste() for '#' register.
+///
+/// @return FAIL if not found, OK for success.
+int buflist_name_nr(int fnum, char **fname, linenr_T *lnum)
{
- buf_T *buf;
-
- buf = buflist_findnr(fnum);
+ buf_T *buf = buflist_findnr(fnum);
if (buf == NULL || buf->b_fname == NULL) {
return FAIL;
}
@@ -2763,10 +2746,10 @@ int buflist_name_nr(int fnum, char_u **fname, linenr_T *lnum)
/// @param message give message when buffer already exists
///
/// @return FAIL for failure (file name already in use by other buffer) OK otherwise.
-int setfname(buf_T *buf, char_u *ffname_arg, char_u *sfname_arg, bool message)
+int setfname(buf_T *buf, char *ffname_arg, char *sfname_arg, bool message)
{
- char_u *ffname = ffname_arg;
- char_u *sfname = sfname_arg;
+ char *ffname = ffname_arg;
+ char *sfname = sfname_arg;
buf_T *obuf = NULL;
FileID file_id;
bool file_id_valid = false;
@@ -2788,7 +2771,7 @@ int setfname(buf_T *buf, char_u *ffname_arg, char_u *sfname_arg, bool message)
// If the file name is already used in another buffer:
// - if the buffer is loaded, fail
// - if the buffer is not loaded, delete it from the list
- file_id_valid = os_fileid((char *)ffname, &file_id);
+ file_id_valid = os_fileid(ffname, &file_id);
if (!(buf->b_flags & BF_DUMMY)) {
obuf = buflist_findname_file_id(ffname, &file_id, file_id_valid);
}
@@ -2801,9 +2784,9 @@ int setfname(buf_T *buf, char_u *ffname_arg, char_u *sfname_arg, bool message)
return FAIL;
}
// delete from the list
- close_buffer(NULL, obuf, DOBUF_WIPE, false);
+ close_buffer(NULL, obuf, DOBUF_WIPE, false, false);
}
- sfname = vim_strsave(sfname);
+ sfname = xstrdup(sfname);
#ifdef USE_FNAME_CASE
path_fix_case(sfname); // set correct case for short file name
#endif
@@ -2826,21 +2809,17 @@ int setfname(buf_T *buf, char_u *ffname_arg, char_u *sfname_arg, bool message)
return OK;
}
-/*
- * Crude way of changing the name of a buffer. Use with care!
- * The name should be relative to the current directory.
- */
-void buf_set_name(int fnum, char_u *name)
+/// Crude way of changing the name of a buffer. Use with care!
+/// The name should be relative to the current directory.
+void buf_set_name(int fnum, char *name)
{
- buf_T *buf;
-
- buf = buflist_findnr(fnum);
+ buf_T *buf = buflist_findnr(fnum);
if (buf != NULL) {
if (buf->b_sfname != buf->b_ffname) {
xfree(buf->b_sfname);
}
xfree(buf->b_ffname);
- buf->b_ffname = vim_strsave(name);
+ buf->b_ffname = xstrdup(name);
buf->b_sfname = NULL;
// Allocate ffname and expand into full path. Also resolves .lnk
// files on Win32.
@@ -2849,15 +2828,10 @@ void buf_set_name(int fnum, char_u *name)
}
}
-/*
- * Take care of what needs to be done when the name of buffer "buf" has
- * changed.
- */
+/// Take care of what needs to be done when the name of buffer "buf" has changed.
void buf_name_changed(buf_T *buf)
{
- /*
- * If the file name changed, also change the name of the swapfile
- */
+ // If the file name changed, also change the name of the swapfile
if (buf->b_ml.ml_mfp != NULL) {
ml_setname(buf);
}
@@ -2871,19 +2845,16 @@ void buf_name_changed(buf_T *buf)
ml_timestamp(buf); // reset timestamp
}
-/*
- * set alternate file name for current window
- *
- * Used by do_one_cmd(), do_write() and do_ecmd().
- * Return the buffer.
- */
-buf_T *setaltfname(char_u *ffname, char_u *sfname, linenr_T lnum)
+/// Set alternate file name for current window
+///
+/// Used by do_one_cmd(), do_write() and do_ecmd().
+///
+/// @return the buffer.
+buf_T *setaltfname(char *ffname, char *sfname, linenr_T lnum)
{
- buf_T *buf;
-
// Create a buffer. 'buflisted' is not set if it's a new buffer
- buf = buflist_new(ffname, sfname, lnum, 0);
- if (buf != NULL && !cmdmod.keepalt) {
+ buf_T *buf = buflist_new(ffname, sfname, lnum, 0);
+ if (buf != NULL && (cmdmod.cmod_flags & CMOD_KEEPALT) == 0) {
curwin->w_alt_fnum = buf->b_fnum;
}
return buf;
@@ -2893,9 +2864,9 @@ buf_T *setaltfname(char_u *ffname, char_u *sfname, linenr_T lnum)
/// Return NULL if there isn't any, and give error message if requested.
///
/// @param errmsg give error message
-char_u *getaltfname(bool errmsg)
+char *getaltfname(bool errmsg)
{
- char_u *fname;
+ char *fname;
linenr_T dummy;
if (buflist_name_nr(0, &fname, &dummy) == FAIL) {
@@ -2907,17 +2878,13 @@ char_u *getaltfname(bool errmsg)
return fname;
}
-/*
- * Add a file name to the buflist and return its number.
- * Uses same flags as buflist_new(), except BLN_DUMMY.
- *
- * used by qf_init(), main() and doarglist()
- */
-int buflist_add(char_u *fname, int flags)
+/// Add a file name to the buflist and return its number.
+/// Uses same flags as buflist_new(), except BLN_DUMMY.
+///
+/// Used by qf_init(), main() and doarglist()
+int buflist_add(char *fname, int flags)
{
- buf_T *buf;
-
- buf = buflist_new(fname, NULL, (linenr_T)0, flags);
+ buf_T *buf = buflist_new(fname, NULL, (linenr_T)0, flags);
if (buf != NULL) {
return buf->b_fnum;
}
@@ -2925,9 +2892,7 @@ int buflist_add(char_u *fname, int flags)
}
#if defined(BACKSLASH_IN_FILENAME)
-/*
- * Adjust slashes in file names. Called after 'shellslash' was set.
- */
+/// Adjust slashes in file names. Called after 'shellslash' was set.
void buflist_slash_adjust(void)
{
FOR_ALL_BUFFERS(bp) {
@@ -2942,10 +2907,8 @@ void buflist_slash_adjust(void)
#endif
-/*
- * Set alternate cursor position for the current buffer and window "win".
- * Also save the local window option values.
- */
+/// Set alternate cursor position for the current buffer and window "win".
+/// Also save the local window option values.
void buflist_altfpos(win_T *win)
{
buflist_setfpos(curbuf, win, win->w_cursor.lnum, win->w_cursor.col, true);
@@ -2955,7 +2918,7 @@ void buflist_altfpos(win_T *win)
/// Fname must have a full path (expanded by path_to_absolute()).
///
/// @param ffname full path name to check
-bool otherfile(char_u *ffname)
+bool otherfile(char *ffname)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
return otherfile_buf(curbuf, ffname, NULL, false);
@@ -2968,14 +2931,14 @@ bool otherfile(char_u *ffname)
/// @param ffname full path name to check
/// @param file_id_p information about the file at "ffname".
/// @param file_id_valid whether a valid "file_id_p" was passed in.
-static bool otherfile_buf(buf_T *buf, char_u *ffname, FileID *file_id_p, bool file_id_valid)
+static bool otherfile_buf(buf_T *buf, char *ffname, FileID *file_id_p, bool file_id_valid)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
// no name is different
if (ffname == NULL || *ffname == NUL || buf->b_ffname == NULL) {
return true;
}
- if (fnamecmp(ffname, buf->b_ffname) == 0) {
+ if (FNAMECMP(ffname, buf->b_ffname) == 0) {
return false;
}
{
@@ -2983,7 +2946,7 @@ static bool otherfile_buf(buf_T *buf, char_u *ffname, FileID *file_id_p, bool fi
// If no struct stat given, get it now
if (file_id_p == NULL) {
file_id_p = &file_id;
- file_id_valid = os_fileid((char *)ffname, file_id_p);
+ file_id_valid = os_fileid(ffname, file_id_p);
}
if (!file_id_valid) {
// file_id not valid, assume files are different.
@@ -3008,13 +2971,13 @@ static bool otherfile_buf(buf_T *buf, char_u *ffname, FileID *file_id_p, bool fi
return true;
}
-// Set file_id for a buffer.
-// Must always be called when b_fname is changed!
+/// Set file_id for a buffer.
+/// Must always be called when b_fname is changed!
void buf_set_file_id(buf_T *buf)
{
FileID file_id;
if (buf->b_fname != NULL
- && os_fileid((char *)buf->b_fname, &file_id)) {
+ && os_fileid(buf->b_fname, &file_id)) {
buf->file_id_valid = true;
buf->file_id = file_id;
} else {
@@ -3037,7 +3000,7 @@ static bool buf_same_file_id(buf_T *buf, FileID *file_id)
/// @param fullname when non-zero print full path
void fileinfo(int fullname, int shorthelp, int dont_truncate)
{
- char_u *name;
+ char *name;
int n;
char *p;
char *buffer;
@@ -3061,7 +3024,7 @@ void fileinfo(int fullname, int shorthelp, int dont_truncate)
} else {
name = curbuf->b_ffname;
}
- home_replace(shorthelp ? curbuf : NULL, name, (char_u *)p,
+ home_replace(shorthelp ? curbuf : NULL, name, p,
(size_t)(IOSIZE - (p - buffer)), true);
}
@@ -3107,11 +3070,11 @@ void fileinfo(int fullname, int shorthelp, int dont_truncate)
n);
validate_virtcol();
len = STRLEN(buffer);
- col_print((char_u *)buffer + len, IOSIZE - len,
+ col_print(buffer + len, IOSIZE - len,
(int)curwin->w_cursor.col + 1, (int)curwin->w_virtcol + 1);
}
- (void)append_arg_number(curwin, (char_u *)buffer, IOSIZE, !shortmess(SHM_FILE));
+ (void)append_arg_number(curwin, buffer, IOSIZE, !shortmess(SHM_FILE));
if (dont_truncate) {
// Temporarily set msg_scroll to avoid the message being truncated.
@@ -3136,24 +3099,23 @@ void fileinfo(int fullname, int shorthelp, int dont_truncate)
xfree(buffer);
}
-void col_print(char_u *buf, size_t buflen, int col, int vcol)
+void col_print(char *buf, size_t buflen, int col, int vcol)
{
if (col == vcol) {
- vim_snprintf((char *)buf, buflen, "%d", col);
+ vim_snprintf(buf, buflen, "%d", col);
} else {
- vim_snprintf((char *)buf, buflen, "%d-%d", col, vcol);
+ vim_snprintf(buf, buflen, "%d-%d", col, vcol);
}
}
-static char_u *lasttitle = NULL;
-static char_u *lasticon = NULL;
-
+static char *lasttitle = NULL;
+static char *lasticon = NULL;
-// Put the title name in the title bar and icon of the window.
+/// Put the title name in the title bar and icon of the window.
void maketitle(void)
{
- char_u *title_str = NULL;
- char_u *icon_str = NULL;
+ char *title_str = NULL;
+ char *icon_str = NULL;
int maxlen = 0;
int len;
int mustset;
@@ -3181,21 +3143,18 @@ void maketitle(void)
if (*p_titlestring != NUL) {
if (stl_syntax & STL_IN_TITLE) {
int use_sandbox = false;
- int save_called_emsg = called_emsg;
+ const int called_emsg_before = called_emsg;
use_sandbox = was_set_insecurely(curwin, "titlestring", 0);
- called_emsg = false;
- build_stl_str_hl(curwin, (char_u *)buf, sizeof(buf),
- p_titlestring, use_sandbox,
+ build_stl_str_hl(curwin, buf, sizeof(buf),
+ (char *)p_titlestring, use_sandbox,
0, maxlen, NULL, NULL);
- title_str = (char_u *)buf;
- if (called_emsg) {
- set_string_option_direct("titlestring", -1, (char_u *)"",
- OPT_FREE, SID_ERROR);
+ title_str = buf;
+ if (called_emsg > called_emsg_before) {
+ set_string_option_direct("titlestring", -1, "", OPT_FREE, SID_ERROR);
}
- called_emsg |= save_called_emsg;
} else {
- title_str = p_titlestring;
+ title_str = (char *)p_titlestring;
}
} else {
// Format: "fname + (path) (1 of 2) - VIM".
@@ -3238,7 +3197,7 @@ void maketitle(void)
// Get path of file, replace home dir with ~.
*buf_p++ = ' ';
*buf_p++ = '(';
- home_replace(curbuf, curbuf->b_ffname, (char_u *)buf_p,
+ home_replace(curbuf, curbuf->b_ffname, buf_p,
(SPACE_FOR_DIR - (size_t)(buf_p - buf)), true);
#ifdef BACKSLASH_IN_FILENAME
// Avoid "c:/name" to be reduced to "c".
@@ -3247,7 +3206,7 @@ void maketitle(void)
}
#endif
// Remove the file name.
- char *p = (char *)path_tail_with_sep((char_u *)buf_p);
+ char *p = path_tail_with_sep(buf_p);
if (p == buf_p) {
// Must be a help buffer.
xstrlcpy(buf_p, _("help"), SPACE_FOR_DIR - (size_t)(buf_p - buf));
@@ -3275,18 +3234,17 @@ void maketitle(void)
*buf_p = NUL;
}
- append_arg_number(curwin, (char_u *)buf_p,
- (int)(SPACE_FOR_ARGNR - (size_t)(buf_p - buf)), false);
+ append_arg_number(curwin, buf_p, (int)(SPACE_FOR_ARGNR - (size_t)(buf_p - buf)), false);
xstrlcat(buf_p, " - NVIM", (sizeof(buf) - (size_t)(buf_p - buf)));
if (maxlen > 0) {
// Make it shorter by removing a bit in the middle.
- if (vim_strsize((char_u *)buf) > maxlen) {
- trunc_string((char_u *)buf, (char_u *)buf, maxlen, sizeof(buf));
+ if (vim_strsize(buf) > maxlen) {
+ trunc_string(buf, buf, maxlen, sizeof(buf));
}
}
- title_str = (char_u *)buf;
+ title_str = buf;
#undef SPACE_FOR_FNAME
#undef SPACE_FOR_DIR
#undef SPACE_FOR_ARGNR
@@ -3295,27 +3253,24 @@ void maketitle(void)
mustset = value_change(title_str, &lasttitle);
if (p_icon) {
- icon_str = (char_u *)buf;
+ icon_str = buf;
if (*p_iconstring != NUL) {
if (stl_syntax & STL_IN_ICON) {
int use_sandbox = false;
- int save_called_emsg = called_emsg;
+ const int called_emsg_before = called_emsg;
use_sandbox = was_set_insecurely(curwin, "iconstring", 0);
- called_emsg = false;
build_stl_str_hl(curwin, icon_str, sizeof(buf),
- p_iconstring, use_sandbox,
+ (char *)p_iconstring, use_sandbox,
0, 0, NULL, NULL);
- if (called_emsg) {
- set_string_option_direct("iconstring", -1,
- (char_u *)"", OPT_FREE, SID_ERROR);
+ if (called_emsg > called_emsg_before) {
+ set_string_option_direct("iconstring", -1, "", OPT_FREE, SID_ERROR);
}
- called_emsg |= save_called_emsg;
} else {
- icon_str = p_iconstring;
+ icon_str = (char *)p_iconstring;
}
} else {
- char_u *buf_p;
+ char *buf_p;
if (buf_spname(curbuf) != NULL) {
buf_p = buf_spname(curbuf);
} else { // use file name only in icon
@@ -3326,7 +3281,7 @@ void maketitle(void)
len = (int)STRLEN(buf_p);
if (len > 100) {
len -= 100;
- len += (*mb_tail_off)(buf_p, buf_p + len) + 1;
+ len += mb_tail_off(buf_p, buf_p + len) + 1;
buf_p += len;
}
STRCPY(icon_str, buf_p);
@@ -3347,9 +3302,9 @@ void maketitle(void)
///
/// @param str desired title string
/// @param[in,out] last current title string
-//
-/// @return true if resettitle() is to be called.
-static bool value_change(char_u *str, char_u **last)
+///
+/// @return true if resettitle() is to be called.
+static bool value_change(char *str, char **last)
FUNC_ATTR_WARN_UNUSED_RESULT
{
if ((str == NULL) != (*last == NULL)
@@ -3359,19 +3314,18 @@ static bool value_change(char_u *str, char_u **last)
*last = NULL;
resettitle();
} else {
- *last = vim_strsave(str);
+ *last = xstrdup(str);
return true;
}
}
return false;
}
-
/// Set current window title
void resettitle(void)
{
- ui_call_set_icon(cstr_as_string((char *)lasticon));
- ui_call_set_title(cstr_as_string((char *)lasttitle));
+ ui_call_set_icon(cstr_as_string(lasticon));
+ ui_call_set_title(cstr_as_string(lasttitle));
ui_flush();
}
@@ -3391,7 +3345,6 @@ typedef enum {
kNumBaseHexadecimal = 16,
} NumberBase;
-
/// Build a string from the status line items in "fmt".
/// Return length of string in screen cells.
///
@@ -3405,20 +3358,20 @@ typedef enum {
/// If maxwidth is not zero, the string will be filled at any middle marker
/// or truncated if too long, fillchar is used for all whitespace.
///
-/// @param wp The window to build a statusline for
-/// @param out The output buffer to write the statusline to
-/// Note: This should not be NameBuff
-/// @param outlen The length of the output buffer
-/// @param fmt The statusline format string
-/// @param use_sandbox Use a sandboxed environment when evaluating fmt
-/// @param fillchar Character to use when filling empty space in the statusline
-/// @param maxwidth The maximum width to make the statusline
-/// @param hltab HL attributes (can be NULL)
-/// @param tabtab Tab clicks definition (can be NULL).
+/// @param wp The window to build a statusline for
+/// @param out The output buffer to write the statusline to
+/// Note: This should not be NameBuff
+/// @param outlen The length of the output buffer
+/// @param fmt The statusline format string
+/// @param use_sandbox Use a sandboxed environment when evaluating fmt
+/// @param fillchar Character to use when filling empty space in the statusline
+/// @param maxwidth The maximum width to make the statusline
+/// @param hltab HL attributes (can be NULL)
+/// @param tabtab Tab clicks definition (can be NULL).
///
-/// @return The final width of the statusline
-int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use_sandbox,
- char_u fillchar, int maxwidth, stl_hlrec_t **hltab, StlClickRecord **tabtab)
+/// @return The final width of the statusline
+int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_sandbox, int fillchar,
+ int maxwidth, stl_hlrec_t **hltab, StlClickRecord **tabtab)
{
static size_t stl_items_len = 20; // Initial value, grows as needed.
static stl_item_t *stl_items = NULL;
@@ -3429,16 +3382,20 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
#define TMPLEN 70
char buf_tmp[TMPLEN];
- char_u win_tmp[TMPLEN];
- char_u *usefmt = fmt;
+ char win_tmp[TMPLEN];
+ char *usefmt = fmt;
const int save_must_redraw = must_redraw;
const int save_redr_type = curwin->w_redr_type;
if (stl_items == NULL) {
stl_items = xmalloc(sizeof(stl_item_t) * stl_items_len);
stl_groupitems = xmalloc(sizeof(int) * stl_items_len);
- stl_hltab = xmalloc(sizeof(stl_hlrec_t) * stl_items_len);
- stl_tabtab = xmalloc(sizeof(StlClickRecord) * stl_items_len);
+
+ // Allocate one more, because the last element is used to indicate the
+ // end of the list.
+ stl_hltab = xmalloc(sizeof(stl_hlrec_t) * (stl_items_len + 1));
+ stl_tabtab = xmalloc(sizeof(StlClickRecord) * (stl_items_len + 1));
+
stl_separator_locations = xmalloc(sizeof(int) * stl_items_len);
}
@@ -3461,9 +3418,6 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
if (fillchar == 0) {
fillchar = ' ';
- } else if (utf_char2len(fillchar) > 1) {
- // Can't handle a multi-byte fill character yet.
- fillchar = '-';
}
// The cursor in windows other than the current one isn't always
@@ -3475,7 +3429,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
}
// Get line & check if empty (cursorpos will show "0-1").
- const char_u *line_ptr = ml_get_buf(wp->w_buffer, lnum, false);
+ const char *line_ptr = (char *)ml_get_buf(wp->w_buffer, lnum, false);
bool empty_line = (*line_ptr == NUL);
// Get the byte value now, in case we need it below. This is more
@@ -3500,24 +3454,23 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
bool prevchar_isitem = false;
// out_p is the current position in the output buffer
- char_u *out_p = out;
+ char *out_p = out;
// out_end_p is the last valid character in the output buffer
// Note: The null termination character must occur here or earlier,
// so any user-visible characters must occur before here.
- char_u *out_end_p = (out + outlen) - 1;
-
+ char *out_end_p = (out + outlen) - 1;
// Proceed character by character through the statusline format string
// fmt_p is the current position in the input buffer
- for (char_u *fmt_p = usefmt; *fmt_p;) {
+ for (char *fmt_p = usefmt; *fmt_p;) {
if (curitem == (int)stl_items_len) {
size_t new_len = stl_items_len * 3 / 2;
stl_items = xrealloc(stl_items, sizeof(stl_item_t) * new_len);
stl_groupitems = xrealloc(stl_groupitems, sizeof(int) * new_len);
- stl_hltab = xrealloc(stl_hltab, sizeof(stl_hlrec_t) * new_len);
- stl_tabtab = xrealloc(stl_tabtab, sizeof(StlClickRecord) * new_len);
+ stl_hltab = xrealloc(stl_hltab, sizeof(stl_hlrec_t) * (new_len + 1));
+ stl_tabtab = xrealloc(stl_tabtab, sizeof(StlClickRecord) * (new_len + 1));
stl_separator_locations =
xrealloc(stl_separator_locations, sizeof(int) * new_len);
@@ -3591,7 +3544,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
// Determine how long the group is.
// Note: We set the current output position to null
// so `vim_strsize` will work.
- char_u *t = stl_items[stl_groupitems[groupdepth]].start;
+ char *t = stl_items[stl_groupitems[groupdepth]].start;
*out_p = NUL;
long group_len = vim_strsize(t);
@@ -3661,14 +3614,15 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
out_p = out_p - n + 1;
// Fill up space left over by half a double-wide char.
while (++group_len < stl_items[stl_groupitems[groupdepth]].minwid) {
- *out_p++ = fillchar;
+ MB_CHAR2BYTES(fillchar, out_p);
}
// }
// correct the start of the items for the truncation
for (int idx = stl_groupitems[groupdepth] + 1; idx < curitem; idx++) {
// Shift everything back by the number of removed bytes
- stl_items[idx].start -= n;
+ // Minus one for the leading '<' added above.
+ stl_items[idx].start -= n - 1;
// If the item was partially or completely truncated, set its
// start to the start of the group
@@ -3677,21 +3631,20 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
}
}
// If the group is shorter than the minimum width, add padding characters.
- } else if (
- abs(stl_items[stl_groupitems[groupdepth]].minwid) > group_len) {
+ } else if (abs(stl_items[stl_groupitems[groupdepth]].minwid) > group_len) {
long min_group_width = stl_items[stl_groupitems[groupdepth]].minwid;
// If the group is left-aligned, add characters to the right.
if (min_group_width < 0) {
min_group_width = 0 - min_group_width;
while (group_len++ < min_group_width && out_p < out_end_p) {
- *out_p++ = fillchar;
+ MB_CHAR2BYTES(fillchar, out_p);
}
// If the group is right-aligned, shift everything to the right and
// prepend with filler characters.
} else {
// { Move the group to the right
- memmove(t + min_group_width - group_len, t, (size_t)(out_p - t));
- group_len = min_group_width - group_len;
+ group_len = (min_group_width - group_len) * utf_char2len(fillchar);
+ memmove(t + group_len, t, (size_t)(out_p - t));
if (out_p + group_len >= (out_end_p + 1)) {
group_len = (long)(out_end_p - out_p);
}
@@ -3705,7 +3658,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
// Prepend the fill characters
for (; group_len > 0; group_len--) {
- *t++ = fillchar;
+ MB_CHAR2BYTES(fillchar, t);
}
}
}
@@ -3792,7 +3745,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
if (*fmt_p == STL_CLICK_FUNC) {
fmt_p++;
- char *t = (char *)fmt_p;
+ char *t = fmt_p;
while (*fmt_p != STL_CLICK_FUNC && *fmt_p) {
fmt_p++;
}
@@ -3801,7 +3754,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
}
stl_items[curitem].type = ClickFunc;
stl_items[curitem].start = out_p;
- stl_items[curitem].cmd = xmemdupz(t, (size_t)(((char *)fmt_p - t)));
+ stl_items[curitem].cmd = xmemdupz(t, (size_t)(fmt_p - t));
stl_items[curitem].minwid = minwid;
fmt_p++;
curitem++;
@@ -3848,7 +3801,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
}
// The status line item type
- char_u opt = *fmt_p++;
+ char opt = *fmt_p++;
// OK - now for the real work
NumberBase base = kNumBaseDecimal;
@@ -3866,20 +3819,20 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
if (buf_spname(wp->w_buffer) != NULL) {
STRLCPY(NameBuff, buf_spname(wp->w_buffer), MAXPATHL);
} else {
- char_u *t = (opt == STL_FULLPATH) ? wp->w_buffer->b_ffname
+ char *t = (opt == STL_FULLPATH) ? wp->w_buffer->b_ffname
: wp->w_buffer->b_fname;
- home_replace(wp->w_buffer, t, NameBuff, MAXPATHL, true);
+ home_replace(wp->w_buffer, t, (char *)NameBuff, MAXPATHL, true);
}
- trans_characters(NameBuff, MAXPATHL);
+ trans_characters((char *)NameBuff, MAXPATHL);
if (opt != STL_FILENAME) {
str = (char *)NameBuff;
} else {
- str = (char *)path_tail(NameBuff);
+ str = path_tail((char *)NameBuff);
}
break;
case STL_VIM_EXPR: // '{'
{
- char_u *block_start = fmt_p - 1;
+ char *block_start = fmt_p - 1;
int reevaluate = (*fmt_p == '%');
itemisflag = true;
@@ -3889,7 +3842,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
// Attempt to copy the expression to evaluate into
// the output buffer as a null-terminated string.
- char_u *t = out_p;
+ char *t = out_p;
while ((*fmt_p != '}' || (reevaluate && fmt_p[-1] != '%'))
&& *fmt_p != NUL && out_p < out_end_p) {
*out_p++ = *fmt_p++;
@@ -3912,9 +3865,9 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
// Store the current buffer number as a string variable
vim_snprintf(buf_tmp, sizeof(buf_tmp), "%d", curbuf->b_fnum);
- set_internal_string_var("g:actual_curbuf", (char_u *)buf_tmp);
+ set_internal_string_var("g:actual_curbuf", buf_tmp);
vim_snprintf((char *)win_tmp, sizeof(win_tmp), "%d", curwin->handle);
- set_internal_string_var("g:actual_curwin", win_tmp);
+ set_internal_string_var("g:actual_curwin", (char *)win_tmp);
buf_T *const save_curbuf = curbuf;
win_T *const save_curwin = curwin;
@@ -3927,7 +3880,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
}
// Note: The result stored in `t` is unused.
- str = (char *)eval_to_string_safe(out_p, &t, use_sandbox);
+ str = eval_to_string_safe(out_p, &t, use_sandbox);
curwin = save_curwin;
curbuf = save_curbuf;
@@ -3942,14 +3895,13 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
// Check if the evaluated result is a number.
// If so, convert the number to an int and free the string.
if (str != NULL && *str != 0) {
- if (*skipdigits((char_u *)str) == NUL) {
+ if (*skipdigits(str) == NUL) {
num = atoi(str);
XFREE_CLEAR(str);
itemisflag = false;
}
}
-
// If the output of the expression needs to be evaluated
// replace the %{} block with the result of evaluation
if (reevaluate && str != NULL && *str != 0
@@ -3958,18 +3910,14 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
size_t parsed_usefmt = (size_t)(block_start - usefmt);
size_t str_length = STRLEN(str);
size_t fmt_length = STRLEN(fmt_p);
- size_t new_fmt_len = parsed_usefmt
- + str_length + fmt_length + 3;
- char_u *new_fmt = (char_u *)xmalloc(new_fmt_len * sizeof(char_u));
- char_u *new_fmt_p = new_fmt;
-
- new_fmt_p = (char_u *)memcpy(new_fmt_p, usefmt, parsed_usefmt)
- + parsed_usefmt;
- new_fmt_p = (char_u *)memcpy(new_fmt_p, str, str_length)
- + str_length;
- new_fmt_p = (char_u *)memcpy(new_fmt_p, "%}", 2) + 2;
- new_fmt_p = (char_u *)memcpy(new_fmt_p, fmt_p, fmt_length)
- + fmt_length;
+ size_t new_fmt_len = parsed_usefmt + str_length + fmt_length + 3;
+ char *new_fmt = xmalloc(new_fmt_len * sizeof(char));
+ char *new_fmt_p = new_fmt;
+
+ new_fmt_p = (char *)memcpy(new_fmt_p, usefmt, parsed_usefmt) + parsed_usefmt;
+ new_fmt_p = (char *)memcpy(new_fmt_p, str, str_length) + str_length;
+ new_fmt_p = (char *)memcpy(new_fmt_p, "%}", 2) + 2;
+ new_fmt_p = (char *)memcpy(new_fmt_p, fmt_p, fmt_length) + fmt_length;
*new_fmt_p = 0;
new_fmt_p = NULL;
@@ -3995,23 +3943,15 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
break;
case STL_COLUMN:
- num = !(State & INSERT) && empty_line
- ? 0 : (int)wp->w_cursor.col + 1;
+ num = (State & MODE_INSERT) == 0 && empty_line ? 0 : (int)wp->w_cursor.col + 1;
break;
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
+ && (virtcol == (colnr_T)((State & MODE_INSERT) == 0 && empty_line
? 0 : (int)wp->w_cursor.col + 1))) {
break;
}
@@ -4028,7 +3968,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
// Store the position percentage in our temporary buffer.
// Note: We cannot store the value in `num` because
// `get_rel_pos` can return a named position. Ex: "Top"
- get_rel_pos(wp, (char_u *)buf_tmp, TMPLEN);
+ get_rel_pos(wp, buf_tmp, TMPLEN);
str = buf_tmp;
break;
@@ -4043,14 +3983,14 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
// Note: The call will only return true if it actually
// appended data to the `buf_tmp` buffer.
- if (append_arg_number(wp, (char_u *)buf_tmp, (int)sizeof(buf_tmp), false)) {
+ if (append_arg_number(wp, buf_tmp, (int)sizeof(buf_tmp), false)) {
str = buf_tmp;
}
break;
case STL_KEYMAP:
fillable = false;
- if (get_keymap_str(wp, (char_u *)"<%s>", (char_u *)buf_tmp, TMPLEN)) {
+ if (get_keymap_str(wp, "<%s>", buf_tmp, TMPLEN)) {
str = buf_tmp;
}
break;
@@ -4069,7 +4009,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
long l = ml_find_line_or_offset(wp->w_buffer, wp->w_cursor.lnum, NULL,
false);
num = (wp->w_buffer->b_ml.ml_flags & ML_EMPTY) || l < 0 ?
- 0L : l + 1 + (!(State & INSERT) && empty_line ?
+ 0L : l + 1 + ((State & MODE_INSERT) == 0 && empty_line ?
0 : (int)wp->w_cursor.col);
break;
}
@@ -4120,11 +4060,10 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
// (including the comma and null terminating character)
if (*wp->w_buffer->b_p_ft != NUL
&& STRLEN(wp->w_buffer->b_p_ft) < TMPLEN - 2) {
- vim_snprintf(buf_tmp, sizeof(buf_tmp), ",%s",
- wp->w_buffer->b_p_ft);
+ vim_snprintf(buf_tmp, sizeof(buf_tmp), ",%s", wp->w_buffer->b_p_ft);
// Uppercase the file extension
- for (char_u *t = (char_u *)buf_tmp; *t != 0; t++) {
- *t = (char_u)TOUPPER_LOC(*t);
+ for (char *t = buf_tmp; *t != 0; t++) {
+ *t = (char)TOUPPER_LOC(*t);
}
str = buf_tmp;
}
@@ -4166,7 +4105,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
case STL_HIGHLIGHT: {
// { The name of the highlight is surrounded by `#`
- char_u *t = fmt_p;
+ char *t = fmt_p;
while (*fmt_p != '#' && *fmt_p != NUL) {
fmt_p++;
}
@@ -4194,7 +4133,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
if (str != NULL && *str) {
// { Skip the leading `,` or ` ` if the item is a flag
// and the proper conditions are met
- char_u *t = (char_u *)str;
+ char *t = str;
if (itemisflag) {
if ((t[0] && t[1])
&& ((!prevchar_isitem && *t == ',')
@@ -4237,7 +4176,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
if (l + 1 == minwid && fillchar == '-' && ascii_isdigit(*t)) {
*out_p++ = ' ';
} else {
- *out_p++ = fillchar;
+ MB_CHAR2BYTES(fillchar, out_p);
}
}
minwid = 0;
@@ -4248,20 +4187,21 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
}
// { Copy the string text into the output buffer
- while (*t && out_p < out_end_p) {
- *out_p++ = *t++;
+ for (; *t && out_p < out_end_p; t++) {
// Change a space by fillchar, unless fillchar is '-' and a
// digit follows.
- if (fillable && out_p[-1] == ' '
- && (!ascii_isdigit(*t) || fillchar != '-')) {
- out_p[-1] = fillchar;
+ if (fillable && *t == ' '
+ && (!ascii_isdigit(*(t + 1)) || fillchar != '-')) {
+ MB_CHAR2BYTES(fillchar, out_p);
+ } else {
+ *out_p++ = *t;
}
}
// }
// For left-aligned items, fill any remaining space with the fillchar
for (; l < minwid && out_p < out_end_p; l++) {
- *out_p++ = fillchar;
+ MB_CHAR2BYTES(fillchar, out_p);
}
// Otherwise if the item is a number, copy that to the output buffer.
@@ -4272,8 +4212,8 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
prevchar_isitem = true;
// { Build the formatting string
- char_u nstr[20];
- char_u *t = nstr;
+ char nstr[20];
+ char *t = nstr;
if (opt == STL_VIRTCOL_ALT) {
*t++ = '-';
minwid--;
@@ -4285,7 +4225,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
// Note: The `*` means we take the width as one of the arguments
*t++ = '*';
- *t++ = (char_u)(base == kNumBaseHexadecimal ? 'X' : 'd');
+ *t++ = base == kNumBaseHexadecimal ? 'X' : 'd';
*t = 0;
// }
@@ -4332,11 +4272,9 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
*++t = 0;
// }
- vim_snprintf((char *)out_p, remaining_buf_len, (char *)nstr,
- 0, num, n);
+ vim_snprintf(out_p, remaining_buf_len, nstr, 0, num, n);
} else {
- vim_snprintf((char *)out_p, remaining_buf_len, (char *)nstr,
- minwid, num);
+ vim_snprintf(out_p, remaining_buf_len, nstr, minwid, num);
}
// Advance the output buffer position to the end of the
@@ -4351,7 +4289,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)) {
@@ -4377,7 +4315,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
if (maxwidth > 0 && width > maxwidth) {
// Result is too long, must truncate somewhere.
int item_idx = 0;
- char_u *trunc_p;
+ char *trunc_p;
// If there are no items, truncate from beginning
if (itemcnt == 0) {
@@ -4441,7 +4379,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
// }
// { Truncate the string
- char_u *trunc_end_p = trunc_p + trunc_len;
+ char *trunc_end_p = trunc_p + trunc_len;
STRMOVE(trunc_p + 1, trunc_end_p);
// Put a `<` to mark where we truncated at
@@ -4454,7 +4392,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
// Fill up for half a double-wide character.
while (++width < maxwidth) {
- *trunc_p++ = fillchar;
+ MB_CHAR2BYTES(fillchar, trunc_p);
*trunc_p = NUL;
}
// }
@@ -4505,13 +4443,13 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
standard_spaces * (num_separators - 1);
for (int i = 0; i < num_separators; i++) {
- int dislocation = (i == (num_separators - 1))
- ? final_spaces : standard_spaces;
- char_u *start = stl_items[stl_separator_locations[i]].start;
- char_u *seploc = start + dislocation;
+ int dislocation = (i == (num_separators - 1)) ? final_spaces : standard_spaces;
+ dislocation *= utf_char2len(fillchar);
+ char *start = stl_items[stl_separator_locations[i]].start;
+ char *seploc = start + dislocation;
STRMOVE(seploc, start);
- for (char_u *s = start; s < seploc; s++) {
- *s = fillchar;
+ for (char *s = start; s < seploc;) {
+ MB_CHAR2BYTES(fillchar, s);
}
for (int item_idx = stl_separator_locations[i] + 1;
@@ -4546,7 +4484,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
StlClickRecord *cur_tab_rec = stl_tabtab;
for (long l = 0; l < itemcnt; l++) {
if (stl_items[l].type == TabPage) {
- cur_tab_rec->start = (char *)stl_items[l].start;
+ cur_tab_rec->start = stl_items[l].start;
if (stl_items[l].minwid == 0) {
cur_tab_rec->def.type = kStlClickDisabled;
cur_tab_rec->def.tabnr = 0;
@@ -4563,7 +4501,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
cur_tab_rec->def.func = NULL;
cur_tab_rec++;
} else if (stl_items[l].type == ClickFunc) {
- cur_tab_rec->start = (char *)stl_items[l].start;
+ cur_tab_rec->start = stl_items[l].start;
cur_tab_rec->def.type = kStlClickFuncRun;
cur_tab_rec->def.tabnr = stl_items[l].minwid;
cur_tab_rec->def.func = stl_items[l].cmd;
@@ -4586,11 +4524,9 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
return width;
}
-/*
- * Get relative cursor position in window into "buf[buflen]", in the form 99%,
- * using "Top", "Bot" or "All" when appropriate.
- */
-void get_rel_pos(win_T *wp, char_u *buf, int buflen)
+/// Get relative cursor position in window into "buf[buflen]", in the form 99%,
+/// using "Top", "Bot" or "All" when appropriate.
+void get_rel_pos(win_T *wp, char *buf, int buflen)
{
// Need at least 3 chars for writing.
if (buflen < 3) {
@@ -4613,7 +4549,7 @@ void get_rel_pos(win_T *wp, char_u *buf, int buflen)
} else if (above <= 0) {
STRLCPY(buf, _("Top"), buflen);
} else {
- vim_snprintf((char *)buf, (size_t)buflen, "%2d%%", above > 1000000L
+ vim_snprintf(buf, (size_t)buflen, "%2d%%", above > 1000000L
? (int)(above / ((above + below) / 100L))
: (int)(above * 100L / (above + below)));
}
@@ -4626,8 +4562,8 @@ void get_rel_pos(win_T *wp, char_u *buf, int buflen)
/// @param buflen length of the string buffer
/// @param add_file if true, add "file" before the arg number
///
-/// @return true if it was appended.
-static bool append_arg_number(win_T *wp, char_u *buf, int buflen, bool add_file)
+/// @return true if it was appended.
+static bool append_arg_number(win_T *wp, char *buf, int buflen, bool add_file)
FUNC_ATTR_NONNULL_ALL
{
// Nothing to do
@@ -4635,7 +4571,7 @@ static bool append_arg_number(win_T *wp, char_u *buf, int buflen, bool add_file)
return false;
}
- char_u *p = buf + STRLEN(buf); // go to the end of the buffer
+ char *p = buf + STRLEN(buf); // go to the end of the buffer
// Early out if the string is getting too long
if (p - buf + 35 >= buflen) {
@@ -4648,20 +4584,20 @@ static bool append_arg_number(win_T *wp, char_u *buf, int buflen, bool add_file)
STRCPY(p, "file ");
p += 5;
}
- vim_snprintf((char *)p, (size_t)(buflen - (p - buf)),
+ vim_snprintf(p, (size_t)(buflen - (p - buf)),
wp->w_arg_idx_invalid
? "(%d) of %d)"
: "%d of %d)", wp->w_arg_idx + 1, ARGCOUNT);
return true;
}
-// Make "*ffname" a full file name, set "*sfname" to "*ffname" if not NULL.
-// "*ffname" becomes a pointer to allocated memory (or NULL).
-// When resolving a link both "*sfname" and "*ffname" will point to the same
-// allocated memory.
-// The "*ffname" and "*sfname" pointer values on call will not be freed.
-// Note that the resulting "*ffname" pointer should be considered not allocated.
-void fname_expand(buf_T *buf, char_u **ffname, char_u **sfname)
+/// Make "*ffname" a full file name, set "*sfname" to "*ffname" if not NULL.
+/// "*ffname" becomes a pointer to allocated memory (or NULL).
+/// When resolving a link both "*sfname" and "*ffname" will point to the same
+/// allocated memory.
+/// The "*ffname" and "*sfname" pointer values on call will not be freed.
+/// Note that the resulting "*ffname" pointer should be considered not allocated.
+void fname_expand(buf_T *buf, char **ffname, char **sfname)
{
if (*ffname == NULL) { // no file name given, nothing to do
return;
@@ -4669,7 +4605,7 @@ void fname_expand(buf_T *buf, char_u **ffname, char_u **sfname)
if (*sfname == NULL) { // no short file name given, use ffname
*sfname = *ffname;
}
- *ffname = (char_u *)fix_fname((char *)(*ffname)); // expand to full path
+ *ffname = fix_fname((*ffname)); // expand to full path
#ifdef WIN32
if (!buf->b_p_bin) {
@@ -4677,24 +4613,22 @@ void fname_expand(buf_T *buf, char_u **ffname, char_u **sfname)
char *rfname = os_resolve_shortcut((const char *)(*ffname));
if (rfname != NULL) {
xfree(*ffname);
- *ffname = (char_u *)rfname;
- *sfname = (char_u *)rfname;
+ *ffname = rfname;
+ *sfname = rfname;
}
}
#endif
}
-/*
- * Get the file name for an argument list entry.
- */
-char_u *alist_name(aentry_T *aep)
+/// Get the file name for an argument list entry.
+char *alist_name(aentry_T *aep)
{
buf_T *bp;
// Use the name from the associated buffer if it exists.
bp = buflist_findnr(aep->ae_fnum);
if (bp == NULL || bp->b_fname == NULL) {
- return aep->ae_fname;
+ return (char *)aep->ae_fname;
}
return bp->b_fname;
}
@@ -4705,7 +4639,7 @@ char_u *alist_name(aentry_T *aep)
/// @param keep_tabs keep current tabs, for ":tab drop file"
void do_arg_all(int count, int forceit, int keep_tabs)
{
- char_u *opened; // Array of weight for which args are open:
+ uint8_t *opened; // Array of weight for which args are open:
// 0: not opened
// 1: opened in other tab
// 2: opened in curtab
@@ -4719,7 +4653,7 @@ void do_arg_all(int count, int forceit, int keep_tabs)
alist_T *alist; // argument list to be used
buf_T *buf;
tabpage_T *tpnext;
- int had_tab = cmdmod.tab;
+ int had_tab = cmdmod.cmod_tab;
win_T *old_curwin, *last_curwin;
tabpage_T *old_curtab, *last_curtab;
win_T *new_curwin = NULL;
@@ -4728,8 +4662,7 @@ void do_arg_all(int count, int forceit, int keep_tabs)
assert(firstwin != NULL); // satisfy coverity
if (ARGCOUNT <= 0) {
- /* Don't give an error message. We don't want it when the ":all"
- * command is in the .vimrc. */
+ // Don't give an error message. We don't want it when the ":all" command is in the .vimrc.
return;
}
setpcmark();
@@ -4737,23 +4670,20 @@ void do_arg_all(int count, int forceit, int keep_tabs)
opened_len = ARGCOUNT;
opened = xcalloc((size_t)opened_len, 1);
- /* Autocommands may do anything to the argument list. Make sure it's not
- * freed while we are working here by "locking" it. We still have to
- * watch out for its size to be changed. */
+ // Autocommands may do anything to the argument list. Make sure it's not
+ // freed while we are working here by "locking" it. We still have to
+ // watch out for its size to be changed.
alist = curwin->w_alist;
alist->al_refcount++;
old_curwin = curwin;
old_curtab = curtab;
-
- /*
- * Try closing all windows that are not in the argument list.
- * Also close windows that are not full width;
- * When 'hidden' or "forceit" set the buffer becomes hidden.
- * Windows that have a changed buffer and can't be hidden won't be closed.
- * When the ":tab" modifier was used do this for all tab pages.
- */
+ // Try closing all windows that are not in the argument list.
+ // Also close windows that are not full width;
+ // When 'hidden' or "forceit" set the buffer becomes hidden.
+ // Windows that have a changed buffer and can't be hidden won't be closed.
+ // When the ":tab" modifier was used do this for all tab pages.
if (had_tab > 0) {
goto_tabpage_tp(first_tabpage, true, true);
}
@@ -4785,7 +4715,7 @@ void do_arg_all(int count, int forceit, int keep_tabs)
}
if (weight > (int)opened[i]) {
- opened[i] = (char_u)weight;
+ opened[i] = (uint8_t)weight;
if (i == 0) {
if (new_curwin != NULL) {
new_curwin->w_arg_idx = opened_len;
@@ -4798,8 +4728,7 @@ void do_arg_all(int count, int forceit, int keep_tabs)
}
if (wp->w_alist != alist) {
- /* Use the current argument list for all windows
- * containing a file from it. */
+ // Use the current argument list for all windows containing a file from it.
alist_unlink(wp->w_alist);
wp->w_alist = alist;
wp->w_alist->al_refcount++;
@@ -4813,8 +4742,7 @@ void do_arg_all(int count, int forceit, int keep_tabs)
if (i == opened_len && !keep_tabs) { // close this window
if (buf_hide(buf) || forceit || buf->b_nwindows > 1
|| !bufIsChanged(buf)) {
- /* If the buffer was changed, and we would like to hide it,
- * try autowriting. */
+ // If the buffer was changed, and we would like to hide it, try autowriting.
if (!buf_hide(buf) && buf->b_nwindows <= 1 && bufIsChanged(buf)) {
bufref_T bufref;
set_bufref(&bufref, buf);
@@ -4830,7 +4758,7 @@ void do_arg_all(int count, int forceit, int keep_tabs)
&& (first_tabpage->tp_next == NULL || !had_tab)) {
use_firstwin = true;
} else {
- win_close(wp, !buf_hide(buf) && !bufIsChanged(buf));
+ win_close(wp, !buf_hide(buf) && !bufIsChanged(buf), false);
// check if autocommands removed the next window
if (!win_valid(wpnext)) {
// start all over...
@@ -4853,10 +4781,8 @@ void do_arg_all(int count, int forceit, int keep_tabs)
goto_tabpage_tp(tpnext, true, true);
}
- /*
- * Open a window for files in the argument list that don't have one.
- * ARGCOUNT may change while doing this, because of autocommands.
- */
+ // Open a window for files in the argument list that don't have one.
+ // ARGCOUNT may change while doing this, because of autocommands.
if (count > opened_len || count <= 0) {
count = opened_len;
}
@@ -4887,6 +4813,10 @@ void do_arg_all(int count, int forceit, int keep_tabs)
if (keep_tabs) {
new_curwin = wp;
new_curtab = curtab;
+ } else if (wp->w_frame->fr_parent != curwin->w_frame->fr_parent) {
+ emsg(_("E249: window layout changed unexpectedly"));
+ i = count;
+ break;
} else {
win_move_after(wp, curwin);
}
@@ -4911,9 +4841,7 @@ void do_arg_all(int count, int forceit, int keep_tabs)
autocmd_no_leave--;
}
- /*
- * edit file "i"
- */
+ // edit file "i"
curwin->w_arg_idx = i;
if (i == 0) {
new_curwin = curwin;
@@ -4936,7 +4864,7 @@ void do_arg_all(int count, int forceit, int keep_tabs)
// When ":tab" was used open a new tab for a new window repeatedly.
if (had_tab > 0 && tabpage_index(NULL) <= p_tpm) {
- cmdmod.tab = 9999;
+ cmdmod.cmod_tab = 9999;
}
}
@@ -4965,15 +4893,14 @@ void do_arg_all(int count, int forceit, int keep_tabs)
xfree(opened);
}
-/// @return true if "buf" is a prompt buffer.
+/// @return true if "buf" is a prompt buffer.
bool bt_prompt(buf_T *buf)
+ FUNC_ATTR_PURE
{
return buf != NULL && buf->b_p_bt[0] == 'p';
}
-/*
- * Open a window for a number of buffers.
- */
+/// Open a window for a number of buffers.
void ex_buffer_all(exarg_T *eap)
{
buf_T *buf;
@@ -4984,7 +4911,7 @@ void ex_buffer_all(exarg_T *eap)
int r;
long count; // Maximum number of windows to open.
int all; // When true also load inactive buffers.
- int had_tab = cmdmod.tab;
+ int had_tab = cmdmod.cmod_tab;
tabpage_T *tpnext;
if (eap->addr_count == 0) { // make as many windows as possible
@@ -5000,11 +4927,8 @@ void ex_buffer_all(exarg_T *eap)
setpcmark();
-
- /*
- * Close superfluous windows (two windows for the same buffer).
- * Also close windows that are not full-width.
- */
+ // Close superfluous windows (two windows for the same buffer).
+ // Also close windows that are not full-width.
if (had_tab > 0) {
goto_tabpage_tp(first_tabpage, true, true);
}
@@ -5013,15 +4937,15 @@ void ex_buffer_all(exarg_T *eap)
for (wp = firstwin; wp != NULL; wp = wpnext) {
wpnext = wp->w_next;
if ((wp->w_buffer->b_nwindows > 1
- || ((cmdmod.split & WSP_VERT)
- ? wp->w_height + wp->w_status_height < Rows - p_ch
- - tabline_height()
+ || ((cmdmod.cmod_split & WSP_VERT)
+ ? wp->w_height + wp->w_hsep_height + wp->w_status_height < Rows - p_ch
+ - tabline_height() - global_stl_height()
: wp->w_width != Columns)
|| (had_tab > 0 && wp != firstwin))
&& !ONE_WINDOW
&& !(wp->w_closing
|| wp->w_buffer->b_locked > 0)) {
- win_close(wp, false);
+ win_close(wp, false, false);
wpnext = firstwin; // just in case an autocommand does
// something strange with windows
tpnext = first_tabpage; // start all over...
@@ -5038,7 +4962,6 @@ void ex_buffer_all(exarg_T *eap)
goto_tabpage_tp(tpnext, true, true);
}
- //
// Go through the buffer list. When a buffer doesn't have a window yet,
// open one. Otherwise move the window to the right position.
// Watch out for autocommands that delete buffers or windows!
@@ -5102,7 +5025,7 @@ void ex_buffer_all(exarg_T *eap)
enter_cleanup(&cs);
// User selected Quit at ATTENTION prompt; close this window.
- win_close(curwin, true);
+ win_close(curwin, true, false);
open_wins--;
swap_exists_action = SEA_NONE;
swap_exists_did_quit = true;
@@ -5127,16 +5050,14 @@ void ex_buffer_all(exarg_T *eap)
}
// When ":tab" was used open a new tab for a new window repeatedly.
if (had_tab > 0 && tabpage_index(NULL) <= p_tpm) {
- cmdmod.tab = 9999;
+ cmdmod.cmod_tab = 9999;
}
}
autocmd_no_enter--;
win_enter(firstwin, false); // back to first window
autocmd_no_leave--;
- /*
- * Close superfluous windows.
- */
+ // Close superfluous windows.
for (wp = lastwin; open_wins > count;) {
r = (buf_hide(wp->w_buffer) || !bufIsChanged(wp->w_buffer)
|| autowrite(wp->w_buffer, false) == OK);
@@ -5144,7 +5065,7 @@ void ex_buffer_all(exarg_T *eap)
// BufWrite Autocommands made the window invalid, start over
wp = lastwin;
} else if (r) {
- win_close(wp, !buf_hide(wp->w_buffer));
+ win_close(wp, !buf_hide(wp->w_buffer), false);
open_wins--;
wp = lastwin;
} else {
@@ -5156,16 +5077,13 @@ void ex_buffer_all(exarg_T *eap)
}
}
-
-/*
- * do_modelines() - process mode lines for the current file
- *
- * "flags" can be:
- * OPT_WINONLY only set options local to window
- * OPT_NOWIN don't set options local to window
- *
- * Returns immediately if the "ml" option isn't set.
- */
+/// do_modelines() - process mode lines for the current file
+///
+/// @param flags
+/// OPT_WINONLY only set options local to window
+/// OPT_NOWIN don't set options local to window
+///
+/// Returns immediately if the "ml" option isn't set.
void do_modelines(int flags)
{
linenr_T lnum;
@@ -5206,18 +5124,18 @@ void do_modelines(int flags)
/// @param flags Same as for do_modelines().
static int chk_modeline(linenr_T lnum, int flags)
{
- char_u *s;
- char_u *e;
- char_u *linecopy; // local copy of any modeline found
+ char *s;
+ char *e;
+ char *linecopy; // local copy of any modeline found
int prev;
intmax_t vers;
int end;
int retval = OK;
- char_u *save_sourcing_name;
+ char *save_sourcing_name;
linenr_T save_sourcing_lnum;
prev = -1;
- for (s = ml_get(lnum); *s != NUL; s++) {
+ for (s = (char *)ml_get(lnum); *s != NUL; s++) {
if (prev == -1 || ascii_isspace(prev)) {
if ((prev != -1 && STRNCMP(s, "ex:", (size_t)3) == 0)
|| STRNCMP(s, "vi:", (size_t)3) == 0) {
@@ -5236,7 +5154,7 @@ static int chk_modeline(linenr_T lnum, int flags)
if (*e == ':'
&& (s[0] != 'V'
- || STRNCMP(skipwhite(e + 1), "set", 3) == 0)
+ || STRNCMP(skipwhite((char *)e + 1), "set", 3) == 0)
&& (s[3] == ':'
|| (VIM_VERSION_100 >= vers && isdigit(s[3]))
|| (VIM_VERSION_100 < vers && s[3] == '<')
@@ -5246,7 +5164,7 @@ static int chk_modeline(linenr_T lnum, int flags)
}
}
}
- prev = *s;
+ prev = (uint8_t)(*s);
}
if (!*s) {
@@ -5257,12 +5175,12 @@ static int chk_modeline(linenr_T lnum, int flags)
s++;
} while (s[-1] != ':');
- s = linecopy = vim_strsave(s); // copy the line, it will change
+ s = linecopy = xstrdup(s); // copy the line, it will change
save_sourcing_lnum = sourcing_lnum;
save_sourcing_name = sourcing_name;
sourcing_lnum = lnum; // prepare for emsg()
- sourcing_name = (char_u *)"modelines";
+ sourcing_name = "modelines";
end = false;
while (end == false) {
@@ -5271,10 +5189,8 @@ static int chk_modeline(linenr_T lnum, int flags)
break;
}
- /*
- * Find end of set command: ':' or end of line.
- * Skip over "\:", replacing it with ":".
- */
+ // Find end of set command: ':' or end of line.
+ // Skip over "\:", replacing it with ":".
for (e = s; *e != ':' && *e != NUL; e++) {
if (e[0] == '\\' && e[1] == ':') {
STRMOVE(e, e + 1);
@@ -5284,13 +5200,11 @@ static int chk_modeline(linenr_T lnum, int flags)
end = true;
}
- /*
- * If there is a "set" command, require a terminating ':' and
- * ignore the stuff after the ':'.
- * "vi:set opt opt opt: foo" -- foo not interpreted
- * "vi:opt opt opt: foo" -- foo interpreted
- * Accept "se" for compatibility with Elvis.
- */
+ // If there is a "set" command, require a terminating ':' and
+ // ignore the stuff after the ':'.
+ // "vi:set opt opt opt: foo" -- foo not interpreted
+ // "vi:opt opt opt: foo" -- foo interpreted
+ // Accept "se" for compatibility with Elvis.
if (STRNCMP(s, "set ", (size_t)4) == 0
|| STRNCMP(s, "se ", (size_t)3) == 0) {
if (*e != ':') { // no terminating ':'?
@@ -5329,37 +5243,37 @@ static int chk_modeline(linenr_T lnum, int flags)
return retval;
}
-// Return true if "buf" is a help buffer.
+/// @return true if "buf" is a help buffer.
bool bt_help(const buf_T *const buf)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
return buf != NULL && buf->b_help;
}
-// Return true if "buf" is a normal buffer, 'buftype' is empty.
+/// @return true if "buf" is a normal buffer, 'buftype' is empty.
bool bt_normal(const buf_T *const buf)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
return buf != NULL && buf->b_p_bt[0] == NUL;
}
-// Return true if "buf" is the quickfix buffer.
+/// @return true if "buf" is the quickfix buffer.
bool bt_quickfix(const buf_T *const buf)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
return buf != NULL && buf->b_p_bt[0] == 'q';
}
-// Return true if "buf" is a terminal buffer.
+/// @return true if "buf" is a terminal buffer.
bool bt_terminal(const buf_T *const buf)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
return buf != NULL && buf->b_p_bt[0] == 't';
}
-// Return true if "buf" is a "nofile", "acwrite", "terminal" or "prompt"
-// buffer. This means the buffer name is not a file name.
-bool bt_nofile(const buf_T *const buf)
+/// @return true if "buf" is a "nofile", "acwrite", "terminal" or "prompt"
+/// buffer. This means the buffer name is not a file name.
+bool bt_nofilename(const buf_T *const buf)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
return buf != NULL && ((buf->b_p_bt[0] == 'n' && buf->b_p_bt[2] == 'f')
@@ -5368,8 +5282,15 @@ bool bt_nofile(const buf_T *const buf)
|| buf->b_p_bt[0] == 'p');
}
-// Return true if "buf" is a "nowrite", "nofile", "terminal" or "prompt"
-// buffer.
+/// @return true if "buf" has 'buftype' set to "nofile".
+bool bt_nofile(const buf_T *const buf)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ return buf != NULL && buf->b_p_bt[0] == 'n' && buf->b_p_bt[2] == 'f';
+}
+
+/// @return true if "buf" is a "nowrite", "nofile", "terminal" or "prompt"
+/// buffer.
bool bt_dontwrite(const buf_T *const buf)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
@@ -5388,9 +5309,10 @@ bool bt_dontwrite_msg(const buf_T *const buf)
return false;
}
-// Return true if the buffer should be hidden, according to 'hidden', ":hide"
-// and 'bufhidden'.
+/// @return true if the buffer should be hidden, according to 'hidden', ":hide"
+/// and 'bufhidden'.
bool buf_hide(const buf_T *const buf)
+ FUNC_ATTR_PURE
{
// 'bufhidden' overrules 'hidden' and ":hide", check it first
switch (buf->b_p_bh[0]) {
@@ -5401,37 +5323,31 @@ bool buf_hide(const buf_T *const buf)
case 'h':
return true; // "hide"
}
- return p_hid || cmdmod.hide;
+ return p_hid || (cmdmod.cmod_flags & CMOD_HIDE);
}
-/*
- * Return special buffer name.
- * Returns NULL when the buffer has a normal file name.
- */
-char_u *buf_spname(buf_T *buf)
+/// @return special buffer name or
+/// NULL when the buffer has a normal file name.
+char *buf_spname(buf_T *buf)
{
if (bt_quickfix(buf)) {
- win_T *win;
- tabpage_T *tp;
-
- // For location list window, w_llist_ref points to the location list.
- // For quickfix window, w_llist_ref is NULL.
- if (find_win_for_buf(buf, &win, &tp) && win->w_llist_ref != NULL) {
- return (char_u *)_(msg_loclist);
- } else {
- return (char_u *)_(msg_qflist);
+ // Differentiate between the quickfix and location list buffers using
+ // the buffer number stored in the global quickfix stack.
+ if (buf->b_fnum == qf_stack_get_bufnr()) {
+ return _(msg_qflist);
}
+ return _(msg_loclist);
}
// There is no _file_ when 'buftype' is "nofile", b_sfname
// contains the name as specified by the user.
- if (bt_nofile(buf)) {
+ if (bt_nofilename(buf)) {
if (buf->b_fname != NULL) {
return buf->b_fname;
}
if (bt_prompt(buf)) {
- return (char_u *)_("[Prompt]");
+ return _("[Prompt]");
}
- return (char_u *)_("[Scratch]");
+ return _("[Scratch]");
}
if (buf->b_fname == NULL) {
return buf_get_fname(buf);
@@ -5448,7 +5364,7 @@ char_u *buf_spname(buf_T *buf)
/// @param[out] wp stores the found window
/// @param[out] tp stores the found tabpage
///
-/// @return true if a window was found for the buffer.
+/// @return true if a window was found for the buffer.
bool find_win_for_buf(buf_T *buf, win_T **wp, tabpage_T **tp)
{
*wp = NULL;
@@ -5463,55 +5379,167 @@ bool find_win_for_buf(buf_T *buf, win_T **wp, tabpage_T **tp)
return false;
}
-int buf_signcols(buf_T *buf)
+static int buf_signcols_inner(buf_T *buf, int maximum)
{
- if (!buf->b_signcols_valid) {
- sign_entry_T *sign; // a sign in the sign list
- int signcols = 0;
- int linesum = 0;
- linenr_T curline = 0;
-
- FOR_ALL_SIGNS_IN_BUF(buf, sign) {
- if (sign->se_lnum > curline) {
- if (linesum > signcols) {
- signcols = linesum;
+ sign_entry_T *sign; // a sign in the sign list
+ int signcols = 0;
+ int linesum = 0;
+ linenr_T curline = 0;
+
+ buf->b_signcols.sentinel = 0;
+
+ FOR_ALL_SIGNS_IN_BUF(buf, sign) {
+ if (sign->se_lnum > curline) {
+ // Counted all signs, now add extmark signs
+ if (curline > 0) {
+ linesum += decor_signcols(buf, &decor_state, (int)curline - 1, (int)curline - 1,
+ maximum - linesum);
+ }
+ curline = sign->se_lnum;
+ if (linesum > signcols) {
+ signcols = linesum;
+ buf->b_signcols.sentinel = curline;
+ if (signcols >= maximum) {
+ return maximum;
}
- curline = sign->se_lnum;
- linesum = 0;
- }
- if (sign->se_has_text_or_icon) {
- linesum++;
}
+ linesum = 0;
}
- if (linesum > signcols) {
- signcols = linesum;
+ if (sign->se_has_text_or_icon) {
+ linesum++;
}
+ }
+
+ if (curline > 0) {
+ linesum += decor_signcols(buf, &decor_state, (int)curline - 1, (int)curline - 1,
+ maximum - linesum);
+ }
+ if (linesum > signcols) {
+ signcols = linesum;
+ if (signcols >= maximum) {
+ return maximum;
+ }
+ }
+
+ // Check extmarks between signs
+ linesum = decor_signcols(buf, &decor_state, 0, (int)buf->b_ml.ml_line_count - 1, maximum);
+
+ if (linesum > signcols) {
+ signcols = linesum;
+ buf->b_signcols.sentinel = curline;
+ if (signcols >= maximum) {
+ return maximum;
+ }
+ }
+
+ return signcols;
+}
+
+/// Invalidate the signcolumn if needed after deleting
+/// signs between line1 and line2 (inclusive).
+///
+/// @param buf buffer to check
+/// @param line1 start of region being deleted
+/// @param line2 end of region being deleted
+void buf_signcols_del_check(buf_T *buf, linenr_T line1, linenr_T line2)
+{
+ if (!buf->b_signcols.valid) {
+ return;
+ }
+
+ if (!buf->b_signcols.sentinel) {
+ buf->b_signcols.valid = false;
+ return;
+ }
+
+ linenr_T sent = buf->b_signcols.sentinel;
+
+ if (sent >= line1 && sent <= line2) {
+ // Only invalidate when removing signs at the sentinel line.
+ buf->b_signcols.valid = false;
+ }
+}
+/// Re-calculate the signcolumn after adding a sign.
+///
+/// @param buf buffer to check
+/// @param added sign being added
+void buf_signcols_add_check(buf_T *buf, sign_entry_T *added)
+{
+ if (!buf->b_signcols.valid) {
+ return;
+ }
+
+ if (!added || !buf->b_signcols.sentinel) {
+ buf->b_signcols.valid = false;
+ return;
+ }
+
+ if (added->se_lnum == buf->b_signcols.sentinel) {
+ if (buf->b_signcols.size == buf->b_signcols.max) {
+ buf->b_signcols.max++;
+ }
+ buf->b_signcols.size++;
+ redraw_buf_later(buf, NOT_VALID);
+ return;
+ }
+
+ sign_entry_T *s;
+
+ // Get first sign for added lnum
+ for (s = added; s->se_prev && s->se_lnum == s->se_prev->se_lnum; s = s->se_prev) {}
+
+ // Count signs for lnum
+ int linesum = 1;
+ for (; s->se_next && s->se_lnum == s->se_next->se_lnum; s = s->se_next) {
+ linesum++;
+ }
+ linesum += decor_signcols(buf, &decor_state, (int)s->se_lnum - 1, (int)s->se_lnum - 1,
+ SIGN_SHOW_MAX - linesum);
+
+ if (linesum > buf->b_signcols.size) {
+ buf->b_signcols.size = linesum;
+ buf->b_signcols.max = linesum;
+ buf->b_signcols.sentinel = added->se_lnum;
+ redraw_buf_later(buf, NOT_VALID);
+ }
+}
+
+int buf_signcols(buf_T *buf, int maximum)
+{
+ // The maximum can be determined from 'signcolumn' which is window scoped so
+ // need to invalidate signcols if the maximum is greater than the previous
+ // maximum.
+ if (maximum > buf->b_signcols.max) {
+ buf->b_signcols.valid = false;
+ }
+
+ if (!buf->b_signcols.valid) {
+ int signcols = buf_signcols_inner(buf, maximum);
// Check if we need to redraw
- if (signcols != buf->b_signcols) {
- buf->b_signcols = signcols;
+ if (signcols != buf->b_signcols.size) {
+ buf->b_signcols.size = signcols;
+ buf->b_signcols.max = maximum;
redraw_buf_later(buf, NOT_VALID);
}
- buf->b_signcols_valid = true;
+ buf->b_signcols.valid = true;
}
- return buf->b_signcols;
+ return buf->b_signcols.size;
}
-// Get "buf->b_fname", use "[No Name]" if it is NULL.
-char_u *buf_get_fname(const buf_T *buf)
+/// Get "buf->b_fname", use "[No Name]" if it is NULL.
+char *buf_get_fname(const buf_T *buf)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
if (buf->b_fname == NULL) {
- return (char_u *)_("[No Name]");
+ return _("[No Name]");
}
return buf->b_fname;
}
-/*
- * Set 'buflisted' for curbuf to "on" and trigger autocommands if it changed.
- */
+/// Set 'buflisted' for curbuf to "on" and trigger autocommands if it changed.
void set_buflisted(int on)
{
if (on != curbuf->b_p_bl) {
@@ -5529,7 +5557,7 @@ void set_buflisted(int on)
///
/// @param buf buffer to check
///
-/// @return true if the buffer's contents have changed
+/// @return true if the buffer's contents have changed
bool buf_contents_changed(buf_T *buf)
FUNC_ATTR_NONNULL_ALL
{
@@ -5552,7 +5580,7 @@ bool buf_contents_changed(buf_T *buf)
if (ml_open(curbuf) == OK
&& readfile(buf->b_ffname, buf->b_fname,
(linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM,
- &ea, READ_NEW | READ_DUMMY) == OK) {
+ &ea, READ_NEW | READ_DUMMY, false) == OK) {
// compare the two files line by line
if (buf->b_ml.ml_line_count == curbuf->b_ml.ml_line_count) {
differ = false;
@@ -5587,7 +5615,7 @@ void wipe_buffer(buf_T *buf, bool aucmd)
// Don't trigger BufDelete autocommands here.
block_autocmds();
}
- close_buffer(NULL, buf, DOBUF_WIPE, false);
+ close_buffer(NULL, buf, DOBUF_WIPE, false, true);
if (!aucmd) {
unblock_autocmds();
}
@@ -5604,10 +5632,9 @@ void wipe_buffer(buf_T *buf, bool aucmd)
void buf_open_scratch(handle_T bufnr, char *bufname)
{
(void)do_ecmd((int)bufnr, NULL, NULL, NULL, ECMD_ONE, ECMD_HIDE, NULL);
- (void)setfname(curbuf, (char_u *)bufname, NULL, true);
+ (void)setfname(curbuf, bufname, NULL, true);
set_option_value("bh", 0L, "hide", OPT_LOCAL);
set_option_value("bt", 0L, "nofile", OPT_LOCAL);
set_option_value("swf", 0L, NULL, OPT_LOCAL);
RESET_BINDING(curwin);
}
-
diff --git a/src/nvim/buffer.h b/src/nvim/buffer.h
index 9e2ca999e4..b452eb227e 100644
--- a/src/nvim/buffer.h
+++ b/src/nvim/buffer.h
@@ -2,14 +2,11 @@
#define NVIM_BUFFER_H
#include "nvim/eval.h"
-#include "nvim/ex_cmds_defs.h" // for exarg_T
#include "nvim/func_attr.h"
#include "nvim/macros.h"
#include "nvim/memline.h"
#include "nvim/pos.h" // for linenr_T
#include "nvim/screen.h" // for StlClickRecord
-#include "nvim/vim.h"
-#include "nvim/window.h"
// Values for buflist_getfile()
enum getf_values {
@@ -23,7 +20,7 @@ enum getf_retvalues {
GETFILE_ERROR = 1, // normal error
GETFILE_NOT_WRITTEN = 2, // "not written" error
GETFILE_SAME_FILE = 0, // success, same file
- GETFILE_OPEN_OTHER = -1, // success, opened another file
+ GETFILE_OPEN_OTHER = (-1), // success, opened another file
GETFILE_UNUSED = 8,
};
@@ -58,9 +55,10 @@ enum dobuf_start_values {
// flags for buf_freeall()
enum bfa_values {
- BFA_DEL = 1, // buffer is going to be deleted
- BFA_WIPE = 2, // buffer is going to be wiped out
- BFA_KEEP_UNDO = 4, // do not free undo information
+ BFA_DEL = 1, // buffer is going to be deleted
+ BFA_WIPE = 2, // buffer is going to be wiped out
+ BFA_KEEP_UNDO = 4, // do not free undo information
+ BFA_IGNORE_ABORT = 8, // do not abort for aborting()
};
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 0a7bd57565..4e890f7d10 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -6,6 +6,8 @@
// for FILE
#include <stdio.h>
+#include "grid_defs.h"
+
typedef struct file_buffer buf_T; // Forward declaration
// Reference to a buffer that stores the value of buf_free_count.
@@ -90,7 +92,6 @@ typedef struct {
#define BF_NEW_W 0x20 // Warned for BF_NEW and file created
#define BF_READERR 0x40 // got errors while reading the file
#define BF_DUMMY 0x80 // dummy buffer, only used internally
-#define BF_PRESERVED 0x100 // ":preserve" was used
#define BF_SYN_SET 0x200 // 'syntax' option was set
// Mask to check for flags that prevent normal writing
@@ -103,8 +104,6 @@ typedef uint64_t disptick_T; // display tick type
// for struct memline (it needs memfile_T)
#include "nvim/memline_defs.h"
-// for struct memfile, bhdr_T, blocknr_T... (it needs buf_T)
-#include "nvim/memfile_defs.h"
// for regprog_T. Needs win_T and buf_T.
#include "nvim/regexp_defs.h"
@@ -179,7 +178,7 @@ typedef struct {
#define w_p_fdi w_onebuf_opt.wo_fdi // 'foldignore'
long wo_fdl;
#define w_p_fdl w_onebuf_opt.wo_fdl // 'foldlevel'
- int wo_fdl_save;
+ long wo_fdl_save;
// 'foldlevel' state saved for diff mode
#define w_p_fdl_save w_onebuf_opt.wo_fdl_save
char_u *wo_fdm;
@@ -204,6 +203,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;
@@ -232,6 +235,8 @@ typedef struct {
#define w_p_sbr w_onebuf_opt.wo_sbr // 'showbreak'
char_u *wo_stl;
#define w_p_stl w_onebuf_opt.wo_stl // 'statusline'
+ char *wo_wbr;
+#define w_p_wbr w_onebuf_opt.wo_wbr // 'winbar'
int wo_scb;
#define w_p_scb w_onebuf_opt.wo_scb // 'scrollbind'
int wo_diff_saved; // options were saved for starting diff mode
@@ -278,8 +283,8 @@ typedef struct {
struct wininfo_S {
wininfo_T *wi_next; // next entry or NULL for last entry
wininfo_T *wi_prev; // previous entry or NULL for first entry
- win_T *wi_win; // pointer to window that did set wi_fpos
- pos_T wi_fpos; // last cursor position in the file
+ win_T *wi_win; // pointer to window that did set wi_mark
+ fmark_T wi_mark; // last cursor mark in the file
bool wi_optset; // true when wi_opt has useful values
winopt_T wi_opt; // local window options
bool wi_fold_manual; // copy of w_fold_manual
@@ -355,6 +360,8 @@ struct mapblock {
LuaRef m_luaref; // lua function reference as rhs
int m_keylen; // strlen(m_keys)
int m_mode; // valid mode
+ int m_simplified; // m_keys was simplified, do no use this map
+ // if keys are typed
int m_noremap; // if non-zero no re-mapping for m_str
char m_silent; // <silent> used, don't echo commands
char m_nowait; // <nowait> used
@@ -366,7 +373,7 @@ struct mapblock {
/// Used for highlighting in the status line.
typedef struct stl_hlrec stl_hlrec_t;
struct stl_hlrec {
- char_u *start;
+ char *start;
int userhl; // 0: no HL, 1-9: User HL, < 0 for syn ID
};
@@ -374,7 +381,7 @@ struct stl_hlrec {
typedef struct stl_item stl_item_t;
struct stl_item {
// Where the item starts in the status line output buffer
- char_u *start;
+ char *start;
// Function to run for ClickFunc items.
char *cmd;
// The minimum width of the item
@@ -529,19 +536,19 @@ struct file_buffer {
int b_flags; // various BF_ flags
int b_locked; // Buffer is being closed or referenced, don't
// let autocommands wipe it out.
+ int b_locked_split; // Buffer is being closed, don't allow opening
+ // a new window with it.
int b_ro_locked; // Non-zero when the buffer can't be changed.
// Used for FileChangedRO
- //
// b_ffname has the full path of the file (NULL for no name).
// b_sfname is the name as the user typed it (or NULL).
// b_fname is the same as b_sfname, unless ":cd" has been done,
// then it is the same as b_ffname (NULL for no name).
- //
- char_u *b_ffname; // full path file name, allocated
- char_u *b_sfname; // short file name, allocated, may be equal to
+ char *b_ffname; // full path file name, allocated
+ char *b_sfname; // short file name, allocated, may be equal to
// b_ffname
- char_u *b_fname; // current file name, points to b_ffname or
+ char *b_fname; // current file name, points to b_ffname or
// b_sfname
bool file_id_valid;
@@ -583,7 +590,9 @@ struct file_buffer {
// where invoked
long b_mtime; // last change time of original file
+ long b_mtime_ns; // nanoseconds of last change time
long b_mtime_read; // last change time when reading
+ long b_mtime_read_ns; // nanoseconds of last read time
uint64_t b_orig_size; // size of original file in bytes
int b_orig_mode; // mode of original file
time_t b_last_used; // time when the buffer was last used; used
@@ -656,7 +665,7 @@ struct file_buffer {
// flags for use of ":lmap" and IM control
long b_p_iminsert; // input mode for insert
long b_p_imsearch; // input mode for search
-#define B_IMODE_USE_INSERT -1 // Use b_p_iminsert value for search
+#define B_IMODE_USE_INSERT (-1) // Use b_p_iminsert value for search
#define B_IMODE_NONE 0 // Input via none
#define B_IMODE_LMAP 1 // Input via langmap
#define B_IMODE_LAST 1
@@ -691,6 +700,7 @@ struct file_buffer {
char_u *b_p_cino; ///< 'cinoptions'
char_u *b_p_cink; ///< 'cinkeys'
char_u *b_p_cinw; ///< 'cinwords'
+ char_u *b_p_cinsd; ///< 'cinscopedecls'
char_u *b_p_com; ///< 'comments'
char_u *b_p_cms; ///< 'commentstring'
char_u *b_p_cpt; ///< 'complete'
@@ -820,7 +830,7 @@ struct file_buffer {
int b_start_eol; // last line had eol when it was read
int b_start_ffc; // first char of 'ff' when edit started
- char_u *b_start_fenc; // 'fileencoding' when edit started or NULL
+ char *b_start_fenc; // 'fileencoding' when edit started or NULL
int b_bad_char; // "++bad=" argument when edit started or 0
int b_start_bomb; // 'bomb' when it was read
@@ -846,7 +856,7 @@ struct file_buffer {
// are not used! Use the B_SPELL macro to
// access b_spell without #ifdef.
- char_u *b_prompt_text; // set by prompt_setprompt()
+ char *b_prompt_text; // set by prompt_setprompt()
Callback b_prompt_callback; // set by prompt_setcallback()
Callback b_prompt_interrupt; // set by prompt_setinterrupt()
int b_prompt_insert; // value for restart_edit when entering
@@ -857,8 +867,12 @@ struct file_buffer {
// may use a different synblock_T.
sign_entry_T *b_signlist; // list of placed signs
- int b_signcols; // last calculated number of sign columns
- bool b_signcols_valid; // calculated sign columns is valid
+ struct {
+ int size; // last calculated number of sign columns
+ bool valid; // calculated sign columns is valid
+ linenr_T sentinel; // a line number which is holding up the signcolumn
+ int max; // Maximum value size is valid for.
+ } b_signcols;
Terminal *terminal; // Terminal instance associated with the buffer
@@ -867,9 +881,9 @@ 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
+ size_t b_signs; // number of sign extmarks
// array of channel_id:s which have asked to receive updates for this
// buffer.
@@ -950,8 +964,8 @@ struct tabpage_S {
frame_T *(tp_snapshot[SNAP_COUNT]); ///< window layout snapshots
ScopeDictDictItem tp_winvar; ///< Variable for "t:" Dictionary.
dict_T *tp_vars; ///< Internal variables, local to tab page.
- char_u *tp_localdir; ///< Absolute path of local cwd or NULL.
- char_u *tp_prevdir; ///< Previous directory.
+ char *tp_localdir; ///< Absolute path of local cwd or NULL.
+ char *tp_prevdir; ///< Previous directory.
};
/*
@@ -1015,6 +1029,7 @@ typedef struct {
colnr_T startcol; // in win_line() points to char where HL starts
colnr_T endcol; // in win_line() points to char where HL ends
bool is_addpos; // position specified directly by matchaddpos()
+ bool has_cursor; // true if the cursor is inside the match, used for CurSearch
proftime_T tm; // for a time limit
} match_T;
@@ -1047,7 +1062,7 @@ struct matchitem {
matchitem_T *next;
int id; ///< match ID
int priority; ///< match priority
- char_u *pattern; ///< pattern to highlight
+ char *pattern; ///< pattern to highlight
regmmatch_T match; ///< regexp program for pattern
posmatch_T pos; ///< position matches
match_T hl; ///< struct for doing the actual highlighting
@@ -1107,7 +1122,7 @@ typedef struct {
bool noautocmd;
stl_hlrec_t* title_hl;
- char_u* title;
+ char* title;
size_t n_title;
TitlePosition title_pos;
} FloatConfig;
@@ -1145,8 +1160,9 @@ enum {
MENU_INDEX_OP_PENDING = 3,
MENU_INDEX_INSERT = 4,
MENU_INDEX_CMDLINE = 5,
- MENU_INDEX_TIP = 6,
- MENU_MODES = 7,
+ MENU_INDEX_TERMINAL = 6,
+ MENU_INDEX_TIP = 7,
+ MENU_MODES = 8,
};
typedef struct VimMenu vimmenu_T;
@@ -1154,15 +1170,15 @@ typedef struct VimMenu vimmenu_T;
struct VimMenu {
int modes; ///< Which modes is this menu visible for
int enabled; ///< for which modes the menu is enabled
- char_u *name; ///< Name of menu, possibly translated
- char_u *dname; ///< Displayed Name ("name" without '&')
- char_u *en_name; ///< "name" untranslated, NULL when
- ///< was not translated
- char_u *en_dname; ///< NULL when "dname" untranslated
+ char *name; ///< Name of menu, possibly translated
+ char *dname; ///< Displayed Name ("name" without '&')
+ char *en_name; ///< "name" untranslated, NULL when
+ ///< was not translated
+ char *en_dname; ///< NULL when "dname" untranslated
int mnemonic; ///< mnemonic key (after '&')
- char_u *actext; ///< accelerator text (after TAB)
+ char *actext; ///< accelerator text (after TAB)
long priority; ///< Menu order priority
- char_u *strings[MENU_MODES]; ///< Mapped string for each mode
+ char *strings[MENU_MODES]; ///< Mapped string for each mode
int noremap[MENU_MODES]; ///< A \ref REMAP_VALUES flag for each mode
bool silent[MENU_MODES]; ///< A silent flag for each mode
vimmenu_T *children; ///< Children of sub-menu
@@ -1218,6 +1234,8 @@ struct window_S {
colnr_T w_old_visual_col; ///< last known start of visual part
colnr_T w_old_curswant; ///< last known value of Curswant
+ linenr_T w_last_cursor_lnum_rnu; ///< cursor lnum when 'rnu' was last redrawn
+
// 'listchars' characters. Defaults set in set_chars_option().
struct {
int eol;
@@ -1231,6 +1249,7 @@ struct window_S {
int lead;
int trail;
int *multispace;
+ int *leadmultispace;
int conceal;
} w_p_lcs_chars;
@@ -1238,7 +1257,14 @@ struct window_S {
struct {
int stl;
int stlnc;
+ int wbr;
+ int horiz;
+ int horizup;
+ int horizdown;
int vert;
+ int vertleft;
+ int vertright;
+ int verthoriz;
int fold;
int foldopen; ///< when fold is open
int foldclosed; ///< when fold is closed
@@ -1268,12 +1294,11 @@ struct window_S {
colnr_T w_skipcol; // starting column when a single line
// doesn't fit in the window
- // "w_last_topline" and "w_last_leftcol" are used to determine if
- // a Scroll autocommand should be emitted.
- linenr_T w_last_topline; ///< last known value for topline
- colnr_T w_last_leftcol; ///< last known value for leftcol
- int w_last_width; ///< last known value for width
- int w_last_height; ///< last known value for height
+ // four fields that are only used when there is a WinScrolled autocommand
+ linenr_T w_last_topline; ///< last known value for w_topline
+ colnr_T w_last_leftcol; ///< last known value for w_leftcol
+ int w_last_width; ///< last known value for w_width
+ int w_last_height; ///< last known value for w_height
//
// Layout of the window in the screen.
@@ -1281,13 +1306,20 @@ struct window_S {
//
int w_winrow; // first row of window in screen
int w_height; // number of rows in window, excluding
- // status/command/winbar line(s)
+ // status/command line(s)
int w_status_height; // number of status lines (0 or 1)
+ int w_winbar_height; // number of window bars (0 or 1)
int w_wincol; // Leftmost column of window in screen.
int w_width; // Width of window, excluding separation.
- int w_vsep_width; // Number of separator columns (0 or 1).
+ int w_hsep_height; // Number of horizontal separator rows (0 or 1)
+ int w_vsep_width; // Number of vertical separator columns (0 or 1).
pos_save_T w_save_cursor; // backup of cursor pos and topline
+ int w_winrow_off; ///< offset from winrow to the inner window area
+ int w_wincol_off; ///< offset from wincol to the inner window area
+ ///< this includes float border but excludes special columns
+ ///< implemented in win_line() (i.e. signs, folds, numbers)
+
// inner size of window, which can be overridden by external UI
int w_height_inner;
int w_width_inner;
@@ -1365,6 +1397,7 @@ struct window_S {
// recomputed
int w_nrwidth; // width of 'number' and 'relativenumber'
// column being used
+ int w_scwidth; // width of 'signcolumn'
/*
* === end of cached values ===
@@ -1375,7 +1408,7 @@ struct window_S {
// w_redr_type is REDRAW_TOP
linenr_T w_redraw_top; // when != 0: first line needing redraw
linenr_T w_redraw_bot; // when != 0: last line needing redraw
- bool w_redr_status; // if true status line must be redrawn
+ bool w_redr_status; // if true statusline/winbar must be redrawn
bool w_redr_border; // if true border must be redrawn
// remember what is shown in the ruler for this window (if 'ruler' set)
@@ -1393,8 +1426,8 @@ struct window_S {
// out of range!)
int w_arg_idx_invalid; // editing another file than w_arg_idx
- char_u *w_localdir; // absolute path of local directory or NULL
- char_u *w_prevdir; // previous directory
+ char *w_localdir; // absolute path of local directory or NULL
+ char *w_prevdir; // previous directory
// Options local to a window.
// They are local because they influence the layout of the window or
// depend on the window layout.
@@ -1405,12 +1438,13 @@ struct window_S {
// A few options have local flags for P_INSECURE.
uint32_t w_p_stl_flags; // flags for 'statusline'
+ uint32_t w_p_wbr_flags; // flags for 'winbar'
uint32_t w_p_fde_flags; // flags for 'foldexpr'
uint32_t w_p_fdt_flags; // flags for 'foldtext'
- int *w_p_cc_cols; // array of columns to highlight or NULL
- char_u w_p_culopt_flags; // flags for cursorline highlighting
- long w_p_siso; // 'sidescrolloff' local value
- long w_p_so; // 'scrolloff' local value
+ int *w_p_cc_cols; // array of columns to highlight or NULL
+ uint8_t w_p_culopt_flags; // flags for cursorline highlighting
+ long w_p_siso; // 'sidescrolloff' local value
+ long w_p_so; // 'scrolloff' local value
int w_briopt_min; // minimum width for breakindent
int w_briopt_shift; // additional shift for breakindent
@@ -1418,7 +1452,7 @@ struct window_S {
int w_briopt_list; // additional indent for lists
// transform a pointer to a "onebuf" option into a "allbuf" option
-#define GLOBAL_WO(p) ((char *)p + sizeof(winopt_T))
+#define GLOBAL_WO(p) ((char *)(p) + sizeof(winopt_T))
long w_scbind_pos;
@@ -1478,6 +1512,16 @@ struct window_S {
// Location list reference used in the location list window.
// In a non-location list window, w_llist_ref is NULL.
qf_info_T *w_llist_ref;
+
+ // Status line click definitions
+ StlClickDefinition *w_status_click_defs;
+ // Size of the w_status_click_defs array
+ size_t w_status_click_defs_size;
+
+ // Window bar click definitions
+ StlClickDefinition *w_winbar_click_defs;
+ // Size of the w_winbar_click_defs array
+ size_t w_winbar_click_defs_size;
};
static inline int win_hl_attr(win_T *wp, int hlf)
diff --git a/src/nvim/buffer_updates.c b/src/nvim/buffer_updates.c
index ee1b7ebc95..47b88945c7 100644
--- a/src/nvim/buffer_updates.c
+++ b/src/nvim/buffer_updates.c
@@ -84,6 +84,7 @@ bool buf_updates_register(buf_T *buf, uint64_t channel_id, BufUpdateCallbacks cb
}
bool buf_updates_active(buf_T *buf)
+ FUNC_ATTR_PURE
{
return kv_size(buf->update_channels) || kv_size(buf->update_callbacks);
}
@@ -185,9 +186,8 @@ void buf_updates_unload(buf_T *buf, bool can_reload)
}
}
-
void buf_updates_send_changes(buf_T *buf, linenr_T firstline, int64_t num_added,
- int64_t num_removed, bool send_tick)
+ int64_t num_removed)
{
size_t deleted_codepoints, deleted_codeunits;
size_t deleted_bytes = ml_flush_deleted_bytes(buf, &deleted_codepoints,
@@ -197,6 +197,9 @@ void buf_updates_send_changes(buf_T *buf, linenr_T firstline, int64_t num_added,
return;
}
+ // Don't send b:changedtick during 'inccommand' preview if "buf" is the current buffer.
+ bool send_tick = !(cmdpreview && buf == curbuf);
+
// if one the channels doesn't work, put its ID here so we can remove it later
uint64_t badchannelid = 0;
@@ -253,7 +256,7 @@ void buf_updates_send_changes(buf_T *buf, linenr_T firstline, int64_t num_added,
for (size_t i = 0; i < kv_size(buf->update_callbacks); i++) {
BufUpdateCallbacks cb = kv_A(buf->update_callbacks, i);
bool keep = true;
- if (cb.on_lines != LUA_NOREF && (cb.preview || !(State & CMDPREVIEW))) {
+ if (cb.on_lines != LUA_NOREF && (cb.preview || !cmdpreview)) {
Array args = ARRAY_DICT_INIT;
Object items[8];
args.size = 6; // may be increased to 8 below
@@ -312,7 +315,7 @@ void buf_updates_send_splice(buf_T *buf, int start_row, colnr_T start_col, bcoun
for (size_t i = 0; i < kv_size(buf->update_callbacks); i++) {
BufUpdateCallbacks cb = kv_A(buf->update_callbacks, i);
bool keep = true;
- if (cb.on_bytes != LUA_NOREF && (cb.preview || !(State & CMDPREVIEW))) {
+ if (cb.on_bytes != LUA_NOREF && (cb.preview || !cmdpreview)) {
FIXED_TEMP_ARRAY(args, 11);
// the first argument is always the buffer handle
diff --git a/src/nvim/buffer_updates.h b/src/nvim/buffer_updates.h
index 961fec879b..3c2635be71 100644
--- a/src/nvim/buffer_updates.h
+++ b/src/nvim/buffer_updates.h
@@ -1,8 +1,8 @@
#ifndef NVIM_BUFFER_UPDATES_H
#define NVIM_BUFFER_UPDATES_H
-#include "nvim/buffer_defs.h"
-#include "nvim/extmark.h"
+#include "nvim/buffer_defs.h" // for buf_T
+#include "nvim/extmark.h" // for bcount_t
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "buffer_updates.h.generated.h"
diff --git a/src/nvim/change.c b/src/nvim/change.c
index 1dbbfff024..4568b71fd9 100644
--- a/src/nvim/change.c
+++ b/src/nvim/change.c
@@ -42,7 +42,7 @@
/// Careful: may trigger autocommands that reload the buffer.
void change_warning(buf_T *buf, int col)
{
- static char *w_readonly = N_("W10: Warning: Changing a readonly file");
+ static const char *w_readonly = N_("W10: Warning: Changing a readonly file");
if (buf->b_did_warn == false
&& curbufIsChanged() == 0
@@ -138,12 +138,8 @@ void changed_internal(void)
/// Common code for when a change was made.
/// See changed_lines() for the arguments.
/// Careful: may trigger autocommands that reload the buffer.
-static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, long xtra)
+static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, linenr_T xtra)
{
- int i;
- pos_T *p;
- int add;
-
// mark the buffer as modified
changed();
@@ -152,19 +148,26 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, long xtra
}
// set the '. mark
- if (!cmdmod.keepjumps) {
- RESET_FMARK(&curbuf->b_last_change, ((pos_T) { lnum, col, 0 }), 0);
+ if ((cmdmod.cmod_flags & CMOD_KEEPJUMPS) == 0) {
+ fmarkv_T view = INIT_FMARKV;
+ // Set the markview only if lnum is visible, as changes might be done
+ // outside of the current window view.
+ if (lnum >= curwin->w_topline && lnum <= curwin->w_botline) {
+ view = mark_view_make(curwin->w_topline, curwin->w_cursor);
+ }
+ RESET_FMARK(&curbuf->b_last_change, ((pos_T) { lnum, col, 0 }), curbuf->handle, view);
// Create a new entry if a new undo-able change was started or we
// don't have an entry yet.
if (curbuf->b_new_change || curbuf->b_changelistlen == 0) {
+ int add;
if (curbuf->b_changelistlen == 0) {
add = true;
} else {
// Don't create a new entry when the line number is the same
// as the last one and the column is not too far away. Avoids
// creating many entries for typing "xxxxx".
- p = &curbuf->b_changelist[curbuf->b_changelistlen - 1].mark;
+ pos_T *p = &curbuf->b_changelist[curbuf->b_changelistlen - 1].mark;
if (p->lnum != lnum) {
add = true;
} else {
@@ -212,6 +215,10 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, long xtra
curwin->w_changelistidx = curbuf->b_changelistlen;
}
+ if (VIsual_active) {
+ check_visual_pos();
+ }
+
FOR_ALL_TAB_WINDOWS(tp, wp) {
if (wp->w_buffer == curbuf) {
// Mark this window to be redrawn later.
@@ -223,26 +230,27 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, long xtra
// values for the cursor.
// Update the folds for this window. Can't postpone this, because
// a following operator might work on the whole fold: ">>dd".
- foldUpdate(wp, lnum, lnume + xtra - 1);
+ linenr_T last = lnume + xtra - 1; // last line after the change
+ foldUpdate(wp, lnum, last);
// The change may cause lines above or below the change to become
// included in a fold. Set lnum/lnume to the first/last line that
// might be displayed differently.
// Set w_cline_folded here as an efficient way to update it when
- // inserting lines just above a closed fold. */
+ // inserting lines just above a closed fold.
bool folded = hasFoldingWin(wp, lnum, &lnum, NULL, false, NULL);
if (wp->w_cursor.lnum == lnum) {
wp->w_cline_folded = folded;
}
- folded = hasFoldingWin(wp, lnume, NULL, &lnume, false, NULL);
- if (wp->w_cursor.lnum == lnume) {
+ folded = hasFoldingWin(wp, last, NULL, &last, false, NULL);
+ if (wp->w_cursor.lnum == last) {
wp->w_cline_folded = folded;
}
// If the changed line is in a range of previously folded lines,
// compare with the first line in that range.
if (wp->w_cursor.lnum <= lnum) {
- i = find_wl_entry(wp, lnum);
+ int i = find_wl_entry(wp, lnum);
if (i >= 0 && wp->w_cursor.lnum > wp->w_lines[i].wl_lnum) {
changed_line_abv_curs_win(wp);
}
@@ -263,7 +271,7 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, long xtra
// For entries below the change: Correct the lnums for
// inserted/deleted lines. Makes it possible to stop displaying
// after the change.
- for (i = 0; i < wp->w_lines_valid; i++) {
+ for (int i = 0; i < wp->w_lines_valid; i++) {
if (wp->w_lines[i].wl_valid) {
if (wp->w_lines[i].wl_lnum >= lnum) {
if (wp->w_lines[i].wl_lnum < lnume) {
@@ -288,9 +296,11 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, long xtra
set_topline(wp, wp->w_topline);
}
- // Relative numbering may require updating more.
- if (wp->w_p_rnu) {
- redraw_later(wp, SOME_VALID);
+ // If lines have been added or removed, relative numbering always
+ // requires a redraw.
+ if (wp->w_p_rnu && xtra != 0) {
+ wp->w_last_cursor_lnum_rnu = 0;
+ redraw_later(wp, VALID);
}
// Cursor line highlighting probably need to be updated with
@@ -345,18 +355,16 @@ static void changedOneline(buf_T *buf, linenr_T lnum)
void changed_bytes(linenr_T lnum, colnr_T col)
{
changedOneline(curbuf, lnum);
- changed_common(lnum, col, lnum + 1, 0L);
+ changed_common(lnum, col, lnum + 1, 0);
// notify any channels that are watching
- buf_updates_send_changes(curbuf, lnum, 1, 1, true);
+ buf_updates_send_changes(curbuf, lnum, 1, 1);
// Diff highlighting in other diff windows may need to be updated too.
if (curwin->w_p_diff) {
- linenr_T wlnum;
-
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_p_diff && wp != curwin) {
redraw_later(wp, VALID);
- wlnum = diff_lnum_win(lnum, wp);
+ linenr_T wlnum = diff_lnum_win(lnum, wp);
if (wlnum > 0) {
changedOneline(wp->w_buffer, wlnum);
}
@@ -371,7 +379,7 @@ void changed_bytes(linenr_T lnum, colnr_T col)
void inserted_bytes(linenr_T lnum, colnr_T col, int old, int new)
{
if (curbuf_splice_pending == 0) {
- extmark_splice_cols(curbuf, (int)lnum-1, col, old, new, kExtmarkUndo);
+ extmark_splice_cols(curbuf, (int)lnum - 1, col, old, new, kExtmarkUndo);
}
changed_bytes(lnum, col);
@@ -380,7 +388,7 @@ void inserted_bytes(linenr_T lnum, colnr_T col, int old, int new)
/// Appended "count" lines below line "lnum" in the current buffer.
/// Must be called AFTER the change and after mark_adjust().
/// Takes care of marking the buffer to be redrawn and sets the changed flag.
-void appended_lines(linenr_T lnum, long count)
+void appended_lines(linenr_T lnum, linenr_T count)
{
changed_lines(lnum + 1, 0, lnum + 1, count, true);
}
@@ -391,18 +399,18 @@ void appended_lines_mark(linenr_T lnum, long count)
// Skip mark_adjust when adding a line after the last one, there can't
// be marks there. But it's still needed in diff mode.
if (lnum + count < curbuf->b_ml.ml_line_count || curwin->w_p_diff) {
- mark_adjust(lnum + 1, (linenr_T)MAXLNUM, count, 0L, kExtmarkUndo);
+ mark_adjust(lnum + 1, (linenr_T)MAXLNUM, (linenr_T)count, 0L, kExtmarkUndo);
} else {
- extmark_adjust(curbuf, lnum + 1, (linenr_T)MAXLNUM, count, 0L,
+ extmark_adjust(curbuf, lnum + 1, (linenr_T)MAXLNUM, (linenr_T)count, 0L,
kExtmarkUndo);
}
- changed_lines(lnum + 1, 0, lnum + 1, count, true);
+ changed_lines(lnum + 1, 0, lnum + 1, (linenr_T)count, true);
}
/// Deleted "count" lines at line "lnum" in the current buffer.
/// Must be called AFTER the change and after mark_adjust().
/// Takes care of marking the buffer to be redrawn and sets the changed flag.
-void deleted_lines(linenr_T lnum, long count)
+void deleted_lines(linenr_T lnum, linenr_T count)
{
changed_lines(lnum, 0, lnum + count, -count, true);
}
@@ -412,13 +420,13 @@ void deleted_lines(linenr_T lnum, long count)
/// be triggered to display the cursor.
void deleted_lines_mark(linenr_T lnum, long count)
{
- // if we deleted the entire buffer, we need to implicity add a new empty line
+ // if we deleted the entire buffer, we need to implicitly add a new empty line
bool made_empty = (count > 0) && curbuf->b_ml.ml_flags & ML_EMPTY;
mark_adjust(lnum, (linenr_T)(lnum + count - 1), (long)MAXLNUM,
- -count + (made_empty?1:0),
+ -(linenr_T)count + (made_empty?1:0),
kExtmarkUndo);
- changed_lines(lnum, 0, lnum + count, -count, true);
+ changed_lines(lnum, 0, lnum + (linenr_T)count, (linenr_T)(-count), true);
}
/// Marks the area to be redrawn after a change.
@@ -427,7 +435,7 @@ void deleted_lines_mark(linenr_T lnum, long count)
/// @param lnum first line with change
/// @param lnume line below last changed line
/// @param xtra number of extra lines (negative when deleting)
-void changed_lines_buf(buf_T *buf, linenr_T lnum, linenr_T lnume, long xtra)
+void changed_lines_buf(buf_T *buf, linenr_T lnum, linenr_T lnume, linenr_T xtra)
{
if (buf->b_mod_set) {
// find the maximum area that must be redisplayed
@@ -472,7 +480,7 @@ void changed_lines_buf(buf_T *buf, linenr_T lnum, linenr_T lnume, long xtra)
/// @param do_buf_event some callers like undo/redo call changed_lines() and
/// then increment changedtick *again*. This flag allows these callers to send
/// the nvim_buf_lines_event events after they're done modifying changedtick.
-void changed_lines(linenr_T lnum, colnr_T col, linenr_T lnume, long xtra, bool do_buf_event)
+void changed_lines(linenr_T lnum, colnr_T col, linenr_T lnume, linenr_T xtra, bool do_buf_event)
{
changed_lines_buf(curbuf, lnum, lnume, xtra);
@@ -499,7 +507,7 @@ void changed_lines(linenr_T lnum, colnr_T col, linenr_T lnume, long xtra, bool d
if (do_buf_event) {
int64_t num_added = (int64_t)(lnume + xtra - lnum);
int64_t num_removed = lnume - lnum;
- buf_updates_send_changes(curbuf, lnum, num_added, num_removed, true);
+ buf_updates_send_changes(curbuf, lnum, num_added, num_removed);
}
}
@@ -545,21 +553,21 @@ void ins_bytes_len(char_u *p, size_t len)
}
/// Insert or replace a single character at the cursor position.
-/// When in REPLACE or VREPLACE mode, replace any existing character.
+/// When in MODE_REPLACE or MODE_VREPLACE state, replace any existing character.
/// Caller must have prepared for undo.
/// For multi-byte characters we get the whole character, the caller must
/// convert bytes to a character.
void ins_char(int c)
{
char_u buf[MB_MAXBYTES + 1];
- size_t n = (size_t)utf_char2bytes(c, buf);
+ size_t n = (size_t)utf_char2bytes(c, (char *)buf);
// When "c" is 0x100, 0x200, etc. we don't want to insert a NUL byte.
// Happens for CTRL-Vu9900.
if (buf[0] == 0) {
buf[0] = '\n';
}
- ins_char_bytes(buf, n);
+ ins_char_bytes((char_u *)buf, n);
}
void ins_char_bytes(char_u *buf, size_t charlen)
@@ -601,7 +609,7 @@ void ins_char_bytes(char_u *buf, size_t charlen)
if (vcol > new_vcol && oldp[col + oldlen] == TAB) {
break;
}
- oldlen += (size_t)utfc_ptr2len(oldp + col + oldlen);
+ oldlen += (size_t)utfc_ptr2len((char *)oldp + col + oldlen);
// Deleted a bit too much, insert spaces.
if (vcol > new_vcol) {
newlen += (size_t)(vcol - new_vcol);
@@ -610,10 +618,9 @@ void ins_char_bytes(char_u *buf, size_t charlen)
curwin->w_p_list = old_list;
} else if (oldp[col] != NUL) {
// normal replace
- oldlen = (size_t)utfc_ptr2len(oldp + col);
+ oldlen = (size_t)utfc_ptr2len((char *)oldp + col);
}
-
// Push the replaced bytes onto the replace stack, so that they can be
// put back when BS is used. The bytes of a multi-byte character are
// done the other way around, so that the first byte is popped off
@@ -647,17 +654,17 @@ void ins_char_bytes(char_u *buf, size_t charlen)
}
// Replace the line in the buffer.
- ml_replace(lnum, newp, false);
+ ml_replace(lnum, (char *)newp, false);
// mark the buffer as changed and prepare for displaying
inserted_bytes(lnum, (colnr_T)col, (int)oldlen, (int)newlen);
// If we're in Insert or Replace mode and 'showmatch' is set, then briefly
// show the match for right parens and braces.
- if (p_sm && (State & INSERT)
+ if (p_sm && (State & MODE_INSERT)
&& msg_silent == 0
&& !ins_compl_active()) {
- showmatch(utf_ptr2char(buf));
+ showmatch(utf_ptr2char((char *)buf));
}
if (!p_ri || (State & REPLACE_FLAG)) {
@@ -672,21 +679,18 @@ void ins_char_bytes(char_u *buf, size_t charlen)
/// Caller must have prepared for undo.
void ins_str(char_u *s)
{
- char_u *oldp, *newp;
int newlen = (int)STRLEN(s);
- int oldlen;
- colnr_T col;
linenr_T lnum = curwin->w_cursor.lnum;
if (virtual_active() && curwin->w_cursor.coladd > 0) {
coladvance_force(getviscol());
}
- col = curwin->w_cursor.col;
- oldp = ml_get(lnum);
- oldlen = (int)STRLEN(oldp);
+ colnr_T col = curwin->w_cursor.col;
+ char_u *oldp = ml_get(lnum);
+ int oldlen = (int)STRLEN(oldp);
- newp = (char_u *)xmalloc((size_t)oldlen + (size_t)newlen + 1);
+ char_u *newp = (char_u *)xmalloc((size_t)oldlen + (size_t)newlen + 1);
if (col > 0) {
memmove(newp, oldp, (size_t)col);
}
@@ -694,7 +698,7 @@ void ins_str(char_u *s)
int bytes = oldlen - col + 1;
assert(bytes >= 0);
memmove(newp + col + newlen, oldp + col, (size_t)bytes);
- ml_replace(lnum, newp, false);
+ ml_replace(lnum, (char *)newp, false);
inserted_bytes(lnum, col, 0, newlen);
curwin->w_cursor.col += newlen;
}
@@ -718,13 +722,9 @@ int del_char(bool fixpos)
int del_chars(long count, int fixpos)
{
int bytes = 0;
- long i;
- char_u *p;
- int l;
-
- p = get_cursor_pos_ptr();
- for (i = 0; i < count && *p != NUL; i++) {
- l = utfc_ptr2len(p);
+ char_u *p = get_cursor_pos_ptr();
+ for (long i = 0; i < count && *p != NUL; i++) {
+ int l = utfc_ptr2len((char *)p);
bytes += l;
p += l;
}
@@ -765,17 +765,16 @@ int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine)
// If 'delcombine' is set and deleting (less than) one character, only
// delete the last combining character.
if (p_deco && use_delcombine
- && utfc_ptr2len(oldp + col) >= count) {
+ && utfc_ptr2len((char *)oldp + col) >= count) {
int cc[MAX_MCO];
- int n;
(void)utfc_ptr2char(oldp + col, cc);
if (cc[0] != NUL) {
// Find the last composing char, there can be several.
- n = col;
+ int n = col;
do {
col = n;
- count = utf_ptr2len(oldp + n);
+ count = utf_ptr2len((char *)oldp + n);
n += count;
} while (utf_composinglike(oldp + col, oldp + n));
fixpos = false;
@@ -789,7 +788,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);
@@ -811,7 +810,7 @@ int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine)
}
memmove(newp + col, oldp + col + count, (size_t)movelen);
if (!was_alloced) {
- ml_replace(lnum, newp, false);
+ ml_replace(lnum, (char *)newp, false);
}
// mark the buffer as changed and prepare for displaying
@@ -827,23 +826,18 @@ int copy_indent(int size, char_u *src)
{
char_u *p = NULL;
char_u *line = NULL;
- char_u *s;
- int todo;
int ind_len;
int line_len = 0;
int tab_pad;
- int ind_done;
- int round;
- int ind_col;
// Round 1: compute the number of characters needed for the indent
// Round 2: copy the characters.
- for (round = 1; round <= 2; round++) {
- todo = size;
+ for (int round = 1; round <= 2; round++) {
+ int todo = size;
ind_len = 0;
- ind_done = 0;
- ind_col = 0;
- s = src;
+ int ind_done = 0;
+ int ind_col = 0;
+ char_u *s = src;
// Count/copy the usable portion of the source line.
while (todo > 0 && ascii_iswhite(*s)) {
@@ -929,7 +923,7 @@ int copy_indent(int size, char_u *src)
memmove(p, get_cursor_line_ptr(), (size_t)line_len);
// Replace the line
- ml_replace(curwin->w_cursor.lnum, line, false);
+ ml_replace(curwin->w_cursor.lnum, (char *)line, false);
// Put the cursor after the indent.
curwin->w_cursor.col = ind_len;
@@ -938,10 +932,10 @@ int copy_indent(int size, char_u *src)
/// open_line: Add a new line below or above the current line.
///
-/// For VREPLACE mode, we only add a new line when we get to the end of the
-/// file, otherwise we just start replacing the next line.
+/// For MODE_VREPLACE state, we only add a new line when we get to the end of
+/// the file, otherwise we just start replacing the next line.
///
-/// Caller must take care of undo. Since VREPLACE may affect any number of
+/// Caller must take care of undo. Since MODE_VREPLACE may affect any number of
/// lines however, it may call u_save_cursor() again when starting to change a
/// new line.
/// "flags": OPENLINE_DELSPACES delete spaces after cursor
@@ -952,11 +946,13 @@ int copy_indent(int size, char_u *src)
///
/// "second_line_indent": indent for after ^^D in Insert mode or if flag
/// OPENLINE_COM_LIST
+/// "did_do_comment" is set to true when intentionally putting the comment
+/// leader in fromt of the new line.
///
/// @param dir FORWARD or BACKWARD
///
/// @return true on success, false on failure
-int open_line(int dir, int flags, int second_line_indent)
+int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
{
char_u *next_line = NULL; // copy of the next line
char_u *p_extra = NULL; // what goes to next line
@@ -969,14 +965,15 @@ int open_line(int dir, int flags, int second_line_indent)
bool retval = false; // return value
int extra_len = 0; // length of p_extra string
int lead_len; // length of comment leader
+ int comment_start = 0; // start index of the comment leader
char_u *lead_flags; // position in 'comments' for comment leader
char_u *leader = NULL; // copy of comment leader
char_u *allocated = NULL; // allocated memory
char_u *p;
char_u saved_char = NUL; // init for GCC
pos_T *pos;
- bool do_si = (!p_paste && curbuf->b_p_si && !curbuf->b_p_cin
- && *curbuf->b_p_inde == NUL);
+ bool do_si = may_do_si();
+ bool do_cindent;
bool no_si = false; // reset did_si afterwards
int first_char = NUL; // init for GCC
int vreplace_mode;
@@ -990,7 +987,7 @@ int open_line(int dir, int flags, int second_line_indent)
char_u *saved_line = vim_strsave(get_cursor_line_ptr());
if (State & VREPLACE_FLAG) {
- // With VREPLACE we make a copy of the next line, which we will be
+ // With MODE_VREPLACE we make a copy of the next line, which we will be
// starting to replace. First make the new line empty and let vim play
// with the indenting and comment leader to its heart's content. Then
// we grab what it ended up putting on the new line, put back the
@@ -1003,11 +1000,11 @@ int open_line(int dir, int flags, int second_line_indent)
next_line = vim_strsave((char_u *)"");
}
- // In VREPLACE mode, a NL replaces the rest of the line, and starts
- // replacing the next line, so push all of the characters left on the
- // line onto the replace stack. We'll push any other characters that
- // might be replaced at the start of the next line (due to autoindent
- // etc) a bit later.
+ // In MODE_VREPLACE state, a NL replaces the rest of the line, and
+ // starts replacing the next line, so push all of the characters left
+ // on the line onto the replace stack. We'll push any other characters
+ // that might be replaced at the start of the next line (due to
+ // autoindent etc) a bit later.
replace_push(NUL); // Call twice because BS over NL expects it
replace_push(NUL);
p = saved_line + curwin->w_cursor.col;
@@ -1017,11 +1014,10 @@ int open_line(int dir, int flags, int second_line_indent)
saved_line[curwin->w_cursor.col] = NUL;
}
- if ((State & INSERT)
- && !(State & VREPLACE_FLAG)) {
+ if ((State & MODE_INSERT) && (State & VREPLACE_FLAG) == 0) {
p_extra = saved_line + curwin->w_cursor.col;
if (do_si) { // need first char after new line break
- p = skipwhite(p_extra);
+ p = (char_u *)skipwhite((char *)p_extra);
first_char = *p;
}
extra_len = (int)STRLEN(p_extra);
@@ -1042,8 +1038,7 @@ int open_line(int dir, int flags, int second_line_indent)
// If 'autoindent' and/or 'smartindent' is set, try to figure out what
// indent to use for the new line.
- if (curbuf->b_p_ai
- || do_si) {
+ if (curbuf->b_p_ai || do_si) {
// count white space on current line
newindent = get_indent_str_vtab(saved_line,
curbuf->b_p_ts,
@@ -1060,18 +1055,16 @@ int open_line(int dir, int flags, int second_line_indent)
if (!trunc_line && do_si && *saved_line != NUL
&& (p_extra == NULL || first_char != '{')) {
char_u *ptr;
- char_u last_char;
old_cursor = curwin->w_cursor;
ptr = saved_line;
if (flags & OPENLINE_DO_COM) {
- lead_len = get_leader_len(ptr, NULL, false, true);
+ lead_len = get_leader_len((char *)ptr, NULL, false, true);
} else {
lead_len = 0;
}
if (dir == FORWARD) {
- // Skip preprocessor directives, unless they are
- // recognised as comments.
+ // Skip preprocessor directives, unless they are recognised as comments.
if (lead_len == 0 && ptr[0] == '#') {
while (ptr[0] == '#' && curwin->w_cursor.lnum > 1) {
ptr = ml_get(--curwin->w_cursor.lnum);
@@ -1079,7 +1072,7 @@ int open_line(int dir, int flags, int second_line_indent)
newindent = get_indent();
}
if (flags & OPENLINE_DO_COM) {
- lead_len = get_leader_len(ptr, NULL, false, true);
+ lead_len = get_leader_len((char *)ptr, NULL, false, true);
} else {
lead_len = 0;
}
@@ -1090,7 +1083,7 @@ int open_line(int dir, int flags, int second_line_indent)
// */
// #define IN_THE_WAY
// This should line up here;
- p = skipwhite(ptr);
+ p = (char_u *)skipwhite((char *)ptr);
if (p[0] == '/' && p[1] == '*') {
p++;
}
@@ -1114,7 +1107,7 @@ int open_line(int dir, int flags, int second_line_indent)
while (p > ptr && ascii_iswhite(*p)) {
p--;
}
- last_char = *p;
+ char_u last_char = *p;
// find the character just before the '{' or ';'
if (last_char == '{' || last_char == ';') {
@@ -1173,7 +1166,7 @@ int open_line(int dir, int flags, int second_line_indent)
newindent = get_indent();
}
}
- p = skipwhite(ptr);
+ p = (char_u *)skipwhite((char *)ptr);
if (*p == '}') { // if line starts with '}': do indent
did_si = true;
} else { // can delete indent when '{' typed
@@ -1189,11 +1182,31 @@ int open_line(int dir, int flags, int second_line_indent)
did_ai = true;
}
+ // May do indenting after opening a new line.
+ do_cindent = !p_paste && (curbuf->b_p_cin || *curbuf->b_p_inde != NUL)
+ && in_cinkeys(dir == FORWARD ? KEY_OPEN_FORW : KEY_OPEN_BACK,
+ ' ', linewhite(curwin->w_cursor.lnum));
+
// Find out if the current line starts with a comment leader.
// This may then be inserted in front of the new line.
end_comment_pending = NUL;
if (flags & OPENLINE_DO_COM) {
- lead_len = get_leader_len(saved_line, &lead_flags, dir == BACKWARD, true);
+ lead_len = get_leader_len((char *)saved_line, (char **)&lead_flags, dir == BACKWARD, true);
+ if (lead_len == 0 && curbuf->b_p_cin && do_cindent && dir == FORWARD
+ && (!has_format_option(FO_NO_OPEN_COMS) || (flags & OPENLINE_FORMAT))) {
+ // Check for a line comment after code.
+ comment_start = check_linecomment(saved_line);
+ if (comment_start != MAXCOL) {
+ lead_len = get_leader_len((char *)saved_line + comment_start,
+ (char **)&lead_flags, false, true);
+ if (lead_len != 0) {
+ lead_len += comment_start;
+ if (did_do_comment != NULL) {
+ *did_do_comment = true;
+ }
+ }
+ }
+ }
} else {
lead_len = 0;
}
@@ -1225,7 +1238,7 @@ int open_line(int dir, int flags, int second_line_indent)
}
// find start of middle part
- (void)copy_option_part(&p, lead_middle, COM_MAX_LEN, ",");
+ (void)copy_option_part((char **)&p, (char *)lead_middle, COM_MAX_LEN, ",");
require_blank = false;
}
@@ -1236,7 +1249,7 @@ int open_line(int dir, int flags, int second_line_indent)
}
p++;
}
- (void)copy_option_part(&p, lead_middle, COM_MAX_LEN, ",");
+ (void)copy_option_part((char **)&p, (char *)lead_middle, COM_MAX_LEN, ",");
while (*p && p[-1] != ':') { // find end of end flags
// Check whether we allow automatic ending of comments
@@ -1245,7 +1258,7 @@ int open_line(int dir, int flags, int second_line_indent)
}
p++;
}
- size_t n = copy_option_part(&p, lead_end, COM_MAX_LEN, ",");
+ size_t n = copy_option_part((char **)&p, (char *)lead_end, COM_MAX_LEN, ",");
if (end_comment_pending == -1) { // we can set it now
end_comment_pending = lead_end[n - 1];
@@ -1289,7 +1302,7 @@ int open_line(int dir, int flags, int second_line_indent)
// Remember where the end is, might want to use it to find the
// start (for C-comments).
if (dir == FORWARD) {
- comment_end = skipwhite(saved_line);
+ comment_end = (char_u *)skipwhite((char *)saved_line);
lead_len = 0;
break;
}
@@ -1300,8 +1313,7 @@ int open_line(int dir, int flags, int second_line_indent)
p--;
}
for (lead_repl = p; lead_repl > curbuf->b_p_com
- && lead_repl[-1] != ':'; lead_repl--) {
- }
+ && lead_repl[-1] != ':'; lead_repl--) {}
lead_repl_len = (int)(p - lead_repl);
// We can probably always add an extra space when doing "O" on
@@ -1349,6 +1361,13 @@ int open_line(int dir, int flags, int second_line_indent)
STRLCPY(leader, saved_line, lead_len + 1);
+ // TODO(vim): handle multi-byte and double width chars
+ for (int li = 0; li < comment_start; li++) {
+ if (!ascii_iswhite(leader[li])) {
+ leader[li] = ' ';
+ }
+ }
+
// Replace leader with lead_repl, right or left adjusted
if (lead_repl != NULL) {
int c = 0;
@@ -1358,7 +1377,7 @@ int open_line(int dir, int flags, int second_line_indent)
if (*p == COM_RIGHT || *p == COM_LEFT) {
c = *p++;
} else if (ascii_isdigit(*p) || *p == '-') {
- off = getdigits_int(&p, true, 0);
+ off = getdigits_int((char **)&p, true, 0);
} else {
p++;
}
@@ -1366,8 +1385,7 @@ int open_line(int dir, int flags, int second_line_indent)
if (c == COM_RIGHT) { // right adjusted leader
// find last non-white in the leader to line up with
for (p = leader + lead_len - 1; p > leader
- && ascii_iswhite(*p); p--) {
- }
+ && ascii_iswhite(*p); p--) {}
p++;
// Compute the length of the replaced characters in
@@ -1381,7 +1399,7 @@ int open_line(int dir, int flags, int second_line_indent)
while (old_size < repl_size && p > leader) {
MB_PTR_BACK(leader, p);
- old_size += ptr2cells(p);
+ old_size += ptr2cells((char *)p);
}
l = lead_repl_len - (int)(endp - p);
if (l != 0) {
@@ -1401,7 +1419,7 @@ int open_line(int dir, int flags, int second_line_indent)
if (l > 1) {
p -= l;
- if (ptr2cells(p) > 1) {
+ if (ptr2cells((char *)p) > 1) {
p[1] = ' ';
l--;
}
@@ -1414,7 +1432,7 @@ int open_line(int dir, int flags, int second_line_indent)
}
}
} else { // left adjusted leader
- p = skipwhite(leader);
+ p = (char_u *)skipwhite((char *)leader);
// Compute the length of the replaced characters in
// screen characters, not bytes. Move the part that is
// not to be overwritten.
@@ -1425,7 +1443,7 @@ int open_line(int dir, int flags, int second_line_indent)
int l;
for (i = 0; i < lead_len && p[i] != NUL; i += l) {
- l = utfc_ptr2len(p + i);
+ l = utfc_ptr2len((char *)p + i);
if (vim_strnsize(p, i + l) > repl_size) {
break;
}
@@ -1448,10 +1466,10 @@ int open_line(int dir, int flags, int second_line_indent)
lead_len--;
memmove(p, p + 1, (size_t)(leader + lead_len - p));
} else {
- int l = utfc_ptr2len(p);
+ int l = utfc_ptr2len((char *)p);
if (l > 1) {
- if (ptr2cells(p) > 1) {
+ if (ptr2cells((char *)p) > 1) {
// Replace a double-wide char with
// two spaces
l--;
@@ -1468,8 +1486,7 @@ int open_line(int dir, int flags, int second_line_indent)
}
// Recompute the indent, it may have changed.
- if (curbuf->b_p_ai
- || do_si) {
+ if (curbuf->b_p_ai || do_si) {
newindent = get_indent_str_vtab(leader,
curbuf->b_p_ts,
curbuf->b_p_vts_array, false);
@@ -1488,7 +1505,7 @@ int open_line(int dir, int flags, int second_line_indent)
while (off > 0 && lead_len > 0
&& leader[lead_len - 1] == ' ') {
// Don't do it when there is a tab before the space
- if (vim_strchr(skipwhite(leader), '\t') != NULL) {
+ if (vim_strchr(skipwhite((char *)leader), '\t') != NULL) {
break;
}
lead_len--;
@@ -1512,15 +1529,13 @@ int open_line(int dir, int flags, int second_line_indent)
// if a new indent will be set below, remove the indent that
// is in the comment leader
- if (newindent
- || did_si) {
+ if (newindent || did_si) {
while (lead_len && ascii_iswhite(*leader)) {
lead_len--;
newcol--;
leader++;
}
}
-
did_si = can_si = false;
} else if (comment_end != NULL) {
// We have finished a comment, so we don't use the leader.
@@ -1540,21 +1555,22 @@ int open_line(int dir, int flags, int second_line_indent)
}
}
- // (State == INSERT || State == REPLACE), only when dir == FORWARD
+ // (State == MODE_INSERT || State == MODE_REPLACE), only when dir == FORWARD
if (p_extra != NULL) {
*p_extra = saved_char; // restore char that NUL replaced
// When 'ai' set or "flags" has OPENLINE_DELSPACES, skip to the first
// non-blank.
//
- // When in REPLACE mode, put the deleted blanks on the replace stack,
- // preceded by a NUL, so they can be put back when a BS is entered.
+ // When in MODE_REPLACE state, put the deleted blanks on the replace
+ // stack, preceded by a NUL, so they can be put back when a BS is
+ // entered.
if (REPLACE_NORMAL(State)) {
replace_push(NUL); // end of extra blanks
}
if (curbuf->b_p_ai || (flags & OPENLINE_DELSPACES)) {
while ((*p_extra == ' ' || *p_extra == '\t')
- && !utf_iscomposing(utf_ptr2char(p_extra + 1))) {
+ && !utf_iscomposing(utf_ptr2char((char *)p_extra + 1))) {
if (REPLACE_NORMAL(State)) {
replace_push(*p_extra);
}
@@ -1600,8 +1616,8 @@ int open_line(int dir, int flags, int second_line_indent)
if (dir == BACKWARD) {
curwin->w_cursor.lnum--;
}
- if (!(State & VREPLACE_FLAG) || old_cursor.lnum >= orig_line_count) {
- if (ml_append(curwin->w_cursor.lnum, p_extra, (colnr_T)0, false) == FAIL) {
+ if ((State & VREPLACE_FLAG) == 0 || old_cursor.lnum >= orig_line_count) {
+ if (ml_append(curwin->w_cursor.lnum, (char *)p_extra, (colnr_T)0, false) == FAIL) {
goto theend;
}
// Postpone calling changed_lines(), because it would mess up folding
@@ -1615,7 +1631,7 @@ int open_line(int dir, int flags, int second_line_indent)
}
did_append = true;
} else {
- // In VREPLACE mode we are starting to replace the next line.
+ // In MODE_VREPLACE state we are starting to replace the next line.
curwin->w_cursor.lnum++;
if (curwin->w_cursor.lnum >= Insstart.lnum + vr_lines_changed) {
// In case we NL to a new line, BS to the previous one, and NL
@@ -1623,7 +1639,7 @@ int open_line(int dir, int flags, int second_line_indent)
(void)u_save_cursor(); // errors are ignored!
vr_lines_changed++;
}
- ml_replace(curwin->w_cursor.lnum, p_extra, true);
+ ml_replace(curwin->w_cursor.lnum, (char *)p_extra, true);
changed_bytes(curwin->w_cursor.lnum, 0);
// TODO(vigoux): extmark_splice_cols here??
curwin->w_cursor.lnum--;
@@ -1631,8 +1647,7 @@ int open_line(int dir, int flags, int second_line_indent)
}
inhibit_delete_count++;
- if (newindent
- || did_si) {
+ if (newindent || did_si) {
curwin->w_cursor.lnum++;
if (did_si) {
int sw = get_sw_value(curbuf);
@@ -1657,8 +1672,8 @@ int open_line(int dir, int flags, int second_line_indent)
ai_col = curwin->w_cursor.col;
- // In REPLACE mode, for each character in the new indent, there must
- // be a NUL on the replace stack, for when it is deleted with BS
+ // In MODE_REPLACE state, for each character in the new indent, there
+ // must be a NUL on the replace stack, for when it is deleted with BS
if (REPLACE_NORMAL(State)) {
for (colnr_T n = 0; n < curwin->w_cursor.col; n++) {
replace_push(NUL);
@@ -1671,8 +1686,8 @@ int open_line(int dir, int flags, int second_line_indent)
}
inhibit_delete_count--;
- // In REPLACE mode, for each character in the extra leader, there must be
- // a NUL on the replace stack, for when it is deleted with BS.
+ // In MODE_REPLACE state, for each character in the extra leader, there
+ // must be a NUL on the replace stack, for when it is deleted with BS.
if (REPLACE_NORMAL(State)) {
while (lead_len-- > 0) {
replace_push(NUL);
@@ -1682,14 +1697,14 @@ int open_line(int dir, int flags, int second_line_indent)
curwin->w_cursor = old_cursor;
if (dir == FORWARD) {
- if (trunc_line || (State & INSERT)) {
+ if (trunc_line || (State & MODE_INSERT)) {
// truncate current line at cursor
saved_line[curwin->w_cursor.col] = NUL;
// Remove trailing white space, unless OPENLINE_KEEPTRAIL used.
if (trunc_line && !(flags & OPENLINE_KEEPTRAIL)) {
truncate_spaces(saved_line);
}
- ml_replace(curwin->w_cursor.lnum, saved_line, false);
+ ml_replace(curwin->w_cursor.lnum, (char *)saved_line, false);
int new_len = (int)STRLEN(saved_line);
@@ -1715,8 +1730,8 @@ int open_line(int dir, int flags, int second_line_indent)
}
// Always move extmarks - Here we move only the line where the
// cursor is, the previous mark_adjust takes care of the lines after
- int cols_added = mincol-1+less_cols_off-less_cols;
- extmark_splice(curbuf, (int)lnum-1, mincol-1 - cols_spliced,
+ int cols_added = mincol - 1 + less_cols_off - less_cols;
+ extmark_splice(curbuf, (int)lnum - 1, mincol - 1 - cols_spliced,
0, less_cols_off, less_cols_off,
1, cols_added, 1 + cols_added, kExtmarkUndo);
} else {
@@ -1732,23 +1747,24 @@ int open_line(int dir, int flags, int second_line_indent)
changed_lines(curwin->w_cursor.lnum, 0, curwin->w_cursor.lnum, 1L, true);
// bail out and just get the final length of the line we just manipulated
bcount_t extra = (bcount_t)STRLEN(ml_get(curwin->w_cursor.lnum));
- extmark_splice(curbuf, (int)curwin->w_cursor.lnum-1, 0,
- 0, 0, 0, 1, 0, 1+extra, kExtmarkUndo);
+ extmark_splice(curbuf, (int)curwin->w_cursor.lnum - 1, 0,
+ 0, 0, 0, 1, 0, 1 + extra, kExtmarkUndo);
}
curbuf_splice_pending--;
curwin->w_cursor.col = newcol;
curwin->w_cursor.coladd = 0;
- // In VREPLACE mode, we are handling the replace stack ourselves, so stop
- // fixthisline() from doing it (via change_indent()) by telling it we're in
- // normal INSERT mode.
+ // In MODE_VREPLACE state, we are handling the replace stack ourselves, so
+ // stop fixthisline() from doing it (via change_indent()) by telling it
+ // we're in normal MODE_INSERT state.
if (State & VREPLACE_FLAG) {
vreplace_mode = State; // So we know to put things right later
- State = INSERT;
+ State = MODE_INSERT;
} else {
vreplace_mode = 0;
}
+
// May do lisp indenting.
if (!p_paste
&& leader == NULL
@@ -1757,30 +1773,26 @@ int open_line(int dir, int flags, int second_line_indent)
fixthisline(get_lisp_indent);
ai_col = (colnr_T)getwhitecols_curline();
}
+
// May do indenting after opening a new line.
- if (!p_paste
- && (curbuf->b_p_cin
- || *curbuf->b_p_inde != NUL
- )
- && in_cinkeys(dir == FORWARD
- ? KEY_OPEN_FORW
- : KEY_OPEN_BACK, ' ', linewhite(curwin->w_cursor.lnum))) {
+ if (do_cindent) {
do_c_expr_indent();
ai_col = (colnr_T)getwhitecols_curline();
}
+
if (vreplace_mode != 0) {
State = vreplace_mode;
}
- // Finally, VREPLACE gets the stuff on the new line, then puts back the
- // original line, and inserts the new stuff char by char, pushing old stuff
- // onto the replace stack (via ins_char()).
+ // Finally, MODE_VREPLACE gets the stuff on the new line, then puts back
+ // the original line, and inserts the new stuff char by char, pushing old
+ // stuff onto the replace stack (via ins_char()).
if (State & VREPLACE_FLAG) {
// Put new line in p_extra
p_extra = vim_strsave(get_cursor_line_ptr());
// Put back original line
- ml_replace(curwin->w_cursor.lnum, next_line, false);
+ ml_replace(curwin->w_cursor.lnum, (char *)next_line, false);
// Insert new stuff into line again
curwin->w_cursor.col = 0;
@@ -1797,7 +1809,7 @@ theend:
xfree(next_line);
xfree(allocated);
return retval;
-} // NOLINT(readability/fn_size)
+}
/// Delete from cursor to end of line.
/// Caller must have prepared for undo.
@@ -1813,7 +1825,7 @@ void truncate_line(int fixpos)
} else {
newp = vim_strnsave(ml_get(lnum), (size_t)col);
}
- ml_replace(lnum, newp, false);
+ ml_replace(lnum, (char *)newp, false);
// mark the buffer as changed and prepare for displaying
changed_bytes(lnum, curwin->w_cursor.col);
@@ -1869,20 +1881,19 @@ void del_lines(long nlines, bool undo)
/// When "flags" is not NULL, it is set to point to the flags of the recognized comment leader.
/// "backward" must be true for the "O" command.
/// If "include_space" is set, include trailing whitespace while calculating the length.
-int get_leader_len(char_u *line, char_u **flags, bool backward, bool include_space)
+int get_leader_len(char *line, char **flags, bool backward, bool include_space)
{
- int i, j;
- int result;
+ int j;
int got_com = false;
- int found_one;
- char_u part_buf[COM_MAX_LEN]; // buffer for one option part
- char_u *string; // pointer to comment string
- char_u *list;
+ char part_buf[COM_MAX_LEN]; // buffer for one option part
+ char *string; // pointer to comment string
+ char *list;
int middle_match_len = 0;
- char_u *prev_list;
- char_u *saved_flags = NULL;
+ char *prev_list;
+ char *saved_flags = NULL;
- result = i = 0;
+ int result = 0;
+ int i = 0;
while (ascii_iswhite(line[i])) { // leading white space is ignored
i++;
}
@@ -1890,8 +1901,8 @@ int get_leader_len(char_u *line, char_u **flags, bool backward, bool include_spa
// Repeat to match several nested comment strings.
while (line[i] != NUL) {
// scan through the 'comments' option for a match
- found_one = false;
- for (list = curbuf->b_p_com; *list;) {
+ int found_one = false;
+ for (list = (char *)curbuf->b_p_com; *list;) {
// Get one option part into part_buf[]. Advance "list" to next
// one. Put "string" at start of string.
if (!got_com && flags != NULL) {
@@ -1936,8 +1947,7 @@ int get_leader_len(char_u *line, char_u **flags, bool backward, bool include_spa
string++;
}
}
- for (j = 0; string[j] != NUL && string[j] == line[i + j]; j++) {
- }
+ for (j = 0; string[j] != NUL && string[j] == line[i + j]; j++) {}
if (string[j] != NUL) {
continue; // string doesn't match
}
@@ -2013,25 +2023,24 @@ int get_leader_len(char_u *line, char_u **flags, bool backward, bool include_spa
///
/// When "flags" is not null, it is set to point to the flags describing the
/// recognized comment leader.
-int get_last_leader_offset(char_u *line, char_u **flags)
+int get_last_leader_offset(char *line, char **flags)
{
int result = -1;
- int i, j;
+ int j;
int lower_check_bound = 0;
- char_u *string;
- char_u *com_leader;
- char_u *com_flags;
- char_u *list;
- int found_one;
- char_u part_buf[COM_MAX_LEN]; // buffer for one option part
+ char *string;
+ char *com_leader;
+ char *com_flags;
+ char *list;
+ char part_buf[COM_MAX_LEN]; // buffer for one option part
// Repeat to match several nested comment strings.
- i = (int)STRLEN(line);
+ int i = (int)STRLEN(line);
while (--i >= lower_check_bound) {
// scan through the 'comments' option for a match
- found_one = false;
- for (list = curbuf->b_p_com; *list;) {
- char_u *flags_save = list;
+ int found_one = false;
+ for (list = (char *)curbuf->b_p_com; *list;) {
+ char *flags_save = list;
// Get one option part into part_buf[]. Advance list to next one.
// put string at start of string.
@@ -2076,8 +2085,7 @@ int get_last_leader_offset(char_u *line, char_u **flags)
// whitespace. Otherwise we would think we are inside a
// comment if the middle part appears somewhere in the middle
// of the line. E.g. for C the "*" appears often.
- for (j = 0; j <= i && ascii_iswhite(line[j]); j++) {
- }
+ for (j = 0; j <= i && ascii_iswhite(line[j]); j++) {}
if (j < i) {
continue;
}
@@ -2095,7 +2103,7 @@ int get_last_leader_offset(char_u *line, char_u **flags)
}
if (found_one) {
- char_u part_buf2[COM_MAX_LEN]; // buffer for one option part
+ char part_buf2[COM_MAX_LEN]; // buffer for one option part
int len1, len2, off;
result = i;
@@ -2116,8 +2124,8 @@ int get_last_leader_offset(char_u *line, char_u **flags)
}
len1 = (int)STRLEN(com_leader);
- for (list = curbuf->b_p_com; *list;) {
- char_u *flags_save = list;
+ for (list = (char *)curbuf->b_p_com; *list;) {
+ char *flags_save = list;
(void)copy_option_part(&list, part_buf2, COM_MAX_LEN, ",");
if (flags_save == com_flags) {
diff --git a/src/nvim/change.h b/src/nvim/change.h
index e7c8a2b031..fdfa8a29ec 100644
--- a/src/nvim/change.h
+++ b/src/nvim/change.h
@@ -5,11 +5,12 @@
#include "nvim/pos.h" // for linenr_T
// flags for open_line()
-#define OPENLINE_DELSPACES 1 // delete spaces after cursor
-#define OPENLINE_DO_COM 2 // format comments
-#define OPENLINE_KEEPTRAIL 4 // keep trailing spaces
-#define OPENLINE_MARKFIX 8 // fix mark positions
-#define OPENLINE_COM_LIST 16 // format comments with list/2nd line indent
+#define OPENLINE_DELSPACES 0x01 // delete spaces after cursor
+#define OPENLINE_DO_COM 0x02 // format comments
+#define OPENLINE_KEEPTRAIL 0x04 // keep trailing spaces
+#define OPENLINE_MARKFIX 0x08 // fix mark positions
+#define OPENLINE_COM_LIST 0x10 // format comments with list/2nd line indent
+#define OPENLINE_FORMAT 0x20 // formatting long comment
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "change.h.generated.h"
diff --git a/src/nvim/channel.c b/src/nvim/channel.c
index cd5134fe5f..20fae3a206 100644
--- a/src/nvim/channel.c
+++ b/src/nvim/channel.c
@@ -25,7 +25,7 @@ static bool did_stdio = false;
/// next free id for a job or rpc channel
/// 1 is reserved for stdio channel
/// 2 is reserved for stderr channel
-static uint64_t next_chan_id = CHAN_STDERR+1;
+static uint64_t next_chan_id = CHAN_STDERR + 1;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "channel.c.generated.h"
@@ -138,8 +138,14 @@ bool channel_close(uint64_t id, ChannelPart part, const char **error)
*error = (const char *)e_invstream;
return false;
}
- api_free_luaref(chan->stream.internal.cb);
- chan->stream.internal.cb = LUA_NOREF;
+ if (chan->term) {
+ api_free_luaref(chan->stream.internal.cb);
+ chan->stream.internal.cb = LUA_NOREF;
+ chan->stream.internal.closed = true;
+ terminal_close(&chan->term, 0);
+ } else {
+ channel_decref(chan);
+ }
break;
default:
@@ -182,7 +188,7 @@ Channel *channel_alloc(ChannelStreamType type)
void channel_create_event(Channel *chan, const char *ext_source)
{
-#if MIN_LOG_LEVEL <= INFO_LOG_LEVEL
+#if MIN_LOG_LEVEL <= LOGLVL_INF
const char *source;
if (ext_source) {
@@ -273,13 +279,11 @@ static void channel_destroy_early(Channel *chan)
multiqueue_put(main_loop.events, free_channel_event, 1, chan);
}
-
static void close_cb(Stream *stream, void *data)
{
channel_decref(data);
}
-
/// Starts a job and returns the associated channel
///
/// @param[in] argv Arguments vector specifying the command to run,
@@ -410,7 +414,6 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout, CallbackReader
return chan;
}
-
uint64_t channel_connect(bool tcp, const char *address, bool rpc, CallbackReader on_output,
int timeout, const char **error)
{
@@ -536,7 +539,11 @@ size_t channel_send(uint64_t id, char *data, size_t len, bool data_owned, const
}
if (chan->streamtype == kChannelStreamInternal) {
- if (!chan->term) {
+ if (chan->is_rpc) {
+ *error = _("Can't send raw data to rpc channel");
+ goto retfree;
+ }
+ if (!chan->term || chan->stream.internal.closed) {
*error = _("Can't send data to closed stream");
goto retfree;
}
@@ -545,7 +552,6 @@ size_t channel_send(uint64_t id, char *data, size_t len, bool data_owned, const
goto retfree;
}
-
Stream *in = channel_instream(chan);
if (in->closed) {
*error = _("Can't send data to closed stream");
@@ -613,7 +619,6 @@ static void on_channel_output(Stream *stream, Channel *chan, RBuffer *buf, size_
} else {
if (chan->term) {
terminal_receive(chan->term, ptr, count);
- terminal_flush_output(chan->term);
}
rbuffer_consumed(buf, count);
@@ -700,7 +705,7 @@ static void channel_process_exit_cb(Process *proc, int status, void *data)
{
Channel *chan = data;
if (chan->term) {
- terminal_close(chan->term, status);
+ terminal_close(&chan->term, status);
}
// If process did not exit, we only closed the handle of a detached process.
@@ -730,13 +735,13 @@ static void channel_callback_call(Channel *chan, CallbackReader *reader)
tv_list_ref(argv[1].vval.v_list);
ga_clear(&reader->buffer);
cb = &reader->cb;
- argv[2].vval.v_string = (char_u *)reader->type;
+ argv[2].vval.v_string = (char *)reader->type;
} else {
argv[1].v_type = VAR_NUMBER;
argv[1].v_lock = VAR_UNLOCKED;
argv[1].vval.v_number = chan->exit_status;
cb = &chan->on_exit;
- argv[2].vval.v_string = (char_u *)"exit";
+ argv[2].vval.v_string = "exit";
}
argv[2].v_type = VAR_STRING;
@@ -747,7 +752,6 @@ static void channel_callback_call(Channel *chan, CallbackReader *reader)
tv_clear(&rettv);
}
-
/// Open terminal for channel
///
/// Channel `chan` is assumed to be an open pty channel,
@@ -794,8 +798,9 @@ static inline void term_delayed_free(void **argv)
return;
}
- terminal_destroy(chan->term);
- chan->term = NULL;
+ if (chan->term) {
+ terminal_destroy(&chan->term);
+ }
channel_decref(chan);
}
@@ -827,6 +832,7 @@ static void set_info_event(void **argv)
typval_T retval;
(void)object_to_vim(DICTIONARY_OBJ(info), &retval, NULL);
tv_dict_add_dict(dict, S_LEN("info"), retval.vval.v_dict);
+ tv_dict_set_keys_readonly(dict);
apply_autocmds(event, NULL, NULL, false, curbuf);
diff --git a/src/nvim/channel.h b/src/nvim/channel.h
index 81b75e2d31..0f1b481792 100644
--- a/src/nvim/channel.h
+++ b/src/nvim/channel.h
@@ -44,6 +44,7 @@ typedef struct {
typedef struct {
LuaRef cb;
+ bool closed;
} InternalState;
typedef struct {
@@ -96,6 +97,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
@@ -146,5 +149,4 @@ static inline Stream *channel_outstream(Channel *chan)
abort();
}
-
#endif // NVIM_CHANNEL_H
diff --git a/src/nvim/charset.c b/src/nvim/charset.c
index 599d662993..028dd70eb2 100644
--- a/src/nvim/charset.c
+++ b/src/nvim/charset.c
@@ -34,7 +34,6 @@
# include "charset.c.generated.h"
#endif
-
static bool chartab_initialized = false;
// b_chartab[] is an array with 256 bits, each bit representing one of the
@@ -159,21 +158,21 @@ int buf_init_chartab(buf_T *buf, int global)
if ((*p == '^') && (p[1] != NUL)) {
tilde = true;
- ++p;
+ p++;
}
if (ascii_isdigit(*p)) {
- c = getdigits_int((char_u **)&p, true, 0);
+ c = getdigits_int((char **)&p, true, 0);
} else {
c = mb_ptr2char_adv(&p);
}
c2 = -1;
if ((*p == '-') && (p[1] != NUL)) {
- ++p;
+ p++;
if (ascii_isdigit(*p)) {
- c2 = getdigits_int((char_u **)&p, true, 0);
+ c2 = getdigits_int((char **)&p, true, 0);
} else {
c2 = mb_ptr2char_adv(&p);
}
@@ -217,9 +216,7 @@ int buf_init_chartab(buf_T *buf, int global)
}
} else if (i == 1) {
// (re)set printable
- // For double-byte we keep the cell width, so
- // that we can detect it from the first byte.
- if (((c < ' ') || (c > '~'))) {
+ if (c < ' ' || c > '~') {
if (tilde) {
g_chartab[c] = (uint8_t)((g_chartab[c] & ~CT_CELL_MASK)
+ ((dy_flags & DY_UHEX) ? 4 : 2));
@@ -245,7 +242,7 @@ int buf_init_chartab(buf_T *buf, int global)
}
}
}
- ++c;
+ c++;
}
c = *p;
@@ -268,22 +265,19 @@ int buf_init_chartab(buf_T *buf, int global)
///
/// @param buf
/// @param bufsize
-void trans_characters(char_u *buf, int bufsize)
+void trans_characters(char *buf, int bufsize)
{
- int len; // length of string needing translation
- int room; // room in buffer after string
- char_u *trs; // translated character
- int trs_len; // length of trs[]
-
- len = (int)STRLEN(buf);
- room = bufsize - len;
+ char_u *trs; // translated character
+ int len = (int)STRLEN(buf); // length of string needing translation
+ int room = bufsize - len; // room in buffer after string
while (*buf != 0) {
+ int trs_len; // length of trs[]
// Assume a multi-byte character doesn't need translation.
if ((trs_len = utfc_ptr2len(buf)) > 1) {
len -= trs_len;
} else {
- trs = transchar_byte(*buf);
+ trs = transchar_byte((uint8_t)(*buf));
trs_len = (int)STRLEN(trs);
if (trs_len > 1) {
@@ -294,7 +288,7 @@ void trans_characters(char_u *buf, int bufsize)
memmove(buf + trs_len, buf + 1, (size_t)len);
}
memmove(buf, trs, (size_t)trs_len);
- --len;
+ len--;
}
buf += trs_len;
}
@@ -316,7 +310,7 @@ size_t transstr_len(const char *const s, bool untab)
size_t len = 0;
while (*p) {
- const size_t l = (size_t)utfc_ptr2len((const char_u *)p);
+ const size_t l = (size_t)utfc_ptr2len(p);
if (l > 1) {
int pcc[MAX_MCO + 1];
pcc[0] = utfc_ptr2char((const char_u *)p, &pcc[1]);
@@ -359,7 +353,7 @@ size_t transstr_buf(const char *const s, char *const buf, const size_t len, bool
char *const buf_e = buf_p + len - 1;
while (*p != NUL && buf_p < buf_e) {
- const size_t l = (size_t)utfc_ptr2len((const char_u *)p);
+ const size_t l = (size_t)utfc_ptr2len(p);
if (l > 1) {
if (buf_p + l > buf_e) {
break; // Exceeded `buf` size.
@@ -429,10 +423,10 @@ char_u *str_foldcase(char_u *str, int orglen, char_u *buf, int buflen)
int i;
int len = orglen;
-#define GA_CHAR(i) ((char_u *)ga.ga_data)[i]
-#define GA_PTR(i) ((char_u *)ga.ga_data + i)
+#define GA_CHAR(i) ((char *)ga.ga_data)[i]
+#define GA_PTR(i) ((char_u *)ga.ga_data + (i))
#define STR_CHAR(i) (buf == NULL ? GA_CHAR(i) : buf[i])
-#define STR_PTR(i) (buf == NULL ? GA_PTR(i) : buf + i)
+#define STR_PTR(i) (buf == NULL ? GA_PTR(i) : buf + (i))
// Copy "str" into "buf" or allocated memory, unmodified.
if (buf == NULL) {
@@ -458,8 +452,8 @@ char_u *str_foldcase(char_u *str, int orglen, char_u *buf, int buflen)
// Make each character lower case.
i = 0;
while (STR_CHAR(i) != NUL) {
- int c = utf_ptr2char(STR_PTR(i));
- int olen = utf_ptr2len(STR_PTR(i));
+ int c = utf_ptr2char((char *)STR_PTR(i));
+ int olen = utf_ptr2len((char *)STR_PTR(i));
int lc = mb_tolower(c);
// Only replace the character when it is not an invalid
@@ -493,14 +487,13 @@ char_u *str_foldcase(char_u *str, int orglen, char_u *buf, int buflen)
}
}
}
- (void)utf_char2bytes(lc, STR_PTR(i));
+ (void)utf_char2bytes(lc, (char *)STR_PTR(i));
}
// skip to next multi-byte char
- i += utfc_ptr2len(STR_PTR(i));
+ i += utfc_ptr2len((char *)STR_PTR(i));
}
-
if (buf == NULL) {
return (char_u *)ga.ga_data;
}
@@ -539,7 +532,7 @@ char_u *transchar_buf(const buf_T *buf, int c)
c = K_SECOND(c);
}
- if ((!chartab_initialized && (((c >= ' ') && (c <= '~'))))
+ if ((!chartab_initialized && (c >= ' ' && c <= '~'))
|| ((c <= 0xFF) && vim_isprintc_strict(c))) {
// printable character
transchar_charbuf[i] = (char_u)c;
@@ -659,6 +652,7 @@ static inline unsigned nr2hex(unsigned n)
///
/// @reeturn Number of display cells.
int byte2cells(int b)
+ FUNC_ATTR_PURE
{
if (b >= 0x80) {
return 0;
@@ -693,11 +687,12 @@ int char2cells(int c)
/// @param p
///
/// @return number of display cells.
-int ptr2cells(const char_u *p)
+int ptr2cells(const char *p_in)
{
+ uint8_t *p = (uint8_t *)p_in;
// For UTF-8 we need to look at more bytes if the first byte is >= 0x80.
if (*p >= 0x80) {
- return utf_ptr2cells(p);
+ return utf_ptr2cells(p_in);
}
// For DBCS we can tell the cell count from the first byte.
@@ -712,9 +707,9 @@ int ptr2cells(const char_u *p)
/// @param s
///
/// @return number of character cells.
-int vim_strsize(char_u *s)
+int vim_strsize(char *s)
{
- return vim_strnsize(s, MAXCOL);
+ return vim_strnsize((char_u *)s, MAXCOL);
}
/// Return the number of character cells string "s[len]" will take on the
@@ -731,8 +726,8 @@ int vim_strnsize(char_u *s, int len)
assert(s != NULL);
int size = 0;
while (*s != NUL && --len >= 0) {
- int l = utfc_ptr2len(s);
- size += ptr2cells(s);
+ int l = utfc_ptr2len((char *)s);
+ size += ptr2cells((char *)s);
s += l;
len -= l - 1;
}
@@ -810,7 +805,7 @@ bool vim_iswordp_buf(const char_u *const p, buf_T *const buf)
int c = *p;
if (MB_BYTE2LEN(c) > 1) {
- c = utf_ptr2char(p);
+ c = utf_ptr2char((char *)p);
}
return vim_iswordc_buf(c, buf);
}
@@ -875,14 +870,11 @@ bool vim_isprintc_strict(int c)
bool in_win_border(win_T *wp, colnr_T vcol)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1)
{
- int width1; // width of first line (after line number)
- int width2; // width of further lines
-
if (wp->w_width_inner == 0) {
// there is no border
return false;
}
- width1 = wp->w_width_inner - win_col_off(wp);
+ int width1 = wp->w_width_inner - win_col_off(wp); // width of first line (after line number)
if ((int)vcol < width1 - 1) {
return false;
@@ -891,7 +883,7 @@ bool in_win_border(win_T *wp, colnr_T vcol)
if ((int)vcol == width1 - 1) {
return true;
}
- width2 = width1 + win_col_off2(wp);
+ int width2 = width1 + win_col_off2(wp); // width of further lines
if (width2 <= 0) {
return false;
@@ -913,27 +905,26 @@ bool in_win_border(win_T *wp, colnr_T vcol)
/// @param end
void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *end)
{
- colnr_T vcol;
char_u *ptr; // points to current char
char_u *posptr; // points to char at pos->col
- char_u *line; // start of the line
int incr;
int head;
long *vts = wp->w_buffer->b_p_vts_array;
int ts = (int)wp->w_buffer->b_p_ts;
- int c;
- vcol = 0;
- line = ptr = ml_get_buf(wp->w_buffer, pos->lnum, false);
+ colnr_T vcol = 0;
+ char_u *line = ptr = ml_get_buf(wp->w_buffer, pos->lnum, false); // start of the line
if (pos->col == MAXCOL) {
// continue until the NUL
posptr = NULL;
} else {
- // Special check for an empty line, which can happen on exit, when
- // ml_get_buf() always returns an empty string.
- if (*ptr == NUL) {
- pos->col = 0;
+ // In a few cases the position can be beyond the end of the line.
+ for (colnr_T i = 0; i < pos->col; i++) {
+ if (ptr[i] == NUL) {
+ pos->col = i;
+ break;
+ }
}
posptr = ptr + pos->col;
posptr -= utf_head_off(line, posptr);
@@ -949,7 +940,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en
&& !wp->w_p_bri) {
for (;;) {
head = 0;
- c = *ptr;
+ int c = *ptr;
// make sure we don't go past the end of the line
if (c == NUL) {
@@ -965,7 +956,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en
// For utf-8, if the byte is >= 0x80, need to look at
// further bytes to find the cell width.
if (c >= 0x80) {
- incr = utf_ptr2cells(ptr);
+ incr = utf_ptr2cells((char *)ptr);
} else {
incr = g_chartab[c] & CT_CELL_MASK;
}
@@ -1023,7 +1014,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en
if (cursor != NULL) {
if ((*ptr == TAB)
- && (State & NORMAL)
+ && (State & MODE_NORMAL)
&& !wp->w_p_list
&& !virtual_active()
&& !(VIsual_active && ((*p_sel == 'e') || ltoreq(*pos, VIsual)))) {
@@ -1066,22 +1057,19 @@ colnr_T getvcol_nolist(pos_T *posp)
void getvvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *end)
{
colnr_T col;
- colnr_T coladd;
- colnr_T endadd;
- char_u *ptr;
if (virtual_active()) {
// For virtual mode, only want one value
getvcol(wp, pos, &col, NULL, NULL);
- coladd = pos->coladd;
- endadd = 0;
+ colnr_T coladd = pos->coladd;
+ colnr_T endadd = 0;
// Cannot put the cursor on part of a wide character.
- ptr = ml_get_buf(wp->w_buffer, pos->lnum, false);
+ char_u *ptr = ml_get_buf(wp->w_buffer, pos->lnum, false);
if (pos->col < (colnr_T)STRLEN(ptr)) {
- int c = utf_ptr2char(ptr + pos->col);
+ int c = utf_ptr2char((char *)ptr + pos->col);
if ((c != TAB) && vim_isprintc(c)) {
endadd = (colnr_T)(char2cells(c) - 1);
if (coladd > endadd) {
@@ -1155,11 +1143,11 @@ void getvcols(win_T *wp, pos_T *pos1, pos_T *pos2, colnr_T *left, colnr_T *right
/// @param[in] p String to skip in.
///
/// @return Pointer to character after the skipped whitespace.
-char_u *skipwhite(const char_u *const p)
+char *skipwhite(const char *const p)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
FUNC_ATTR_NONNULL_RET
{
- return skipwhite_len(p, STRLEN(p));
+ return (char *)skipwhite_len((char_u *)p, STRLEN(p));
}
/// Like `skipwhite`, but skip up to `len` characters.
@@ -1188,8 +1176,9 @@ intptr_t getwhitecols_curline(void)
}
intptr_t getwhitecols(const char_u *p)
+ FUNC_ATTR_PURE
{
- return skipwhite(p) - p;
+ return (char_u *)skipwhite((char *)p) - p;
}
/// Skip over digits
@@ -1197,16 +1186,16 @@ intptr_t getwhitecols(const char_u *p)
/// @param[in] q String to skip digits in.
///
/// @return Pointer to the character after the skipped digits.
-char_u *skipdigits(const char_u *q)
+char *skipdigits(const char *q)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
FUNC_ATTR_NONNULL_RET
{
- const char_u *p = q;
+ const char *p = q;
while (ascii_isdigit(*p)) {
// skip to next non-digit
p++;
}
- return (char_u *)p;
+ return (char *)p;
}
/// skip over binary digits
@@ -1234,6 +1223,7 @@ const char *skipbin(const char *q)
/// @return Pointer to the character after the skipped digits and hex
/// characters.
char_u *skiphex(char_u *q)
+ FUNC_ATTR_PURE
{
char_u *p = q;
while (ascii_isxdigit(*p)) {
@@ -1249,6 +1239,7 @@ char_u *skiphex(char_u *q)
///
/// @return Pointer to the digit or (NUL after the string).
char_u *skiptodigit(char_u *q)
+ FUNC_ATTR_PURE
{
char_u *p = q;
while (*p != NUL && !ascii_isdigit(*p)) {
@@ -1282,6 +1273,7 @@ const char *skiptobin(const char *q)
///
/// @return Pointer to the hex character or (NUL after the string).
char_u *skiptohex(char_u *q)
+ FUNC_ATTR_PURE
{
char_u *p = q;
while (*p != NUL && !ascii_isxdigit(*p)) {
@@ -1297,7 +1289,7 @@ char_u *skiptohex(char_u *q)
///
/// @return Pointer to the next whitespace or NUL character.
char_u *skiptowhite(const char_u *p)
- FUNC_ATTR_NONNULL_ALL
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE
{
while (*p != ' ' && *p != '\t' && *p != NUL) {
p++;
@@ -1310,13 +1302,14 @@ char_u *skiptowhite(const char_u *p)
/// @param p
///
/// @return Pointer to the next whitespace character.
-char_u *skiptowhite_esc(char_u *p)
+char *skiptowhite_esc(char *p)
+ FUNC_ATTR_PURE
{
while (*p != ' ' && *p != '\t' && *p != NUL) {
if (((*p == '\\') || (*p == Ctrl_V)) && (*(p + 1) != NUL)) {
- ++p;
+ p++;
}
- ++p;
+ p++;
}
return p;
}
@@ -1340,10 +1333,10 @@ char_u *skip_to_newline(const char_u *const p)
/// @param[out] nr Number read from the string.
///
/// @return true on success, false on error/overflow
-bool try_getdigits(char_u **pp, intmax_t *nr)
+bool try_getdigits(char **pp, intmax_t *nr)
{
errno = 0;
- *nr = strtoimax((char *)(*pp), (char **)pp, 10);
+ *nr = strtoimax(*pp, pp, 10);
if (errno == ERANGE && (*nr == INTMAX_MIN || *nr == INTMAX_MAX)) {
return false;
}
@@ -1361,7 +1354,7 @@ bool try_getdigits(char_u **pp, intmax_t *nr)
intmax_t getdigits(char_u **pp, bool strict, intmax_t def)
{
intmax_t number;
- int ok = try_getdigits(pp, &number);
+ int ok = try_getdigits((char **)pp, &number);
if (strict && !ok) {
abort();
}
@@ -1371,9 +1364,9 @@ intmax_t getdigits(char_u **pp, bool strict, intmax_t def)
/// Gets an int number from a string.
///
/// @see getdigits
-int getdigits_int(char_u **pp, bool strict, int def)
+int getdigits_int(char **pp, bool strict, int def)
{
- intmax_t number = getdigits(pp, strict, def);
+ intmax_t number = getdigits((char_u **)pp, strict, def);
#if SIZEOF_INTMAX_T > SIZEOF_INT
if (strict) {
assert(number >= INT_MIN && number <= INT_MAX);
@@ -1400,12 +1393,29 @@ long getdigits_long(char_u **pp, bool strict, long def)
return (long)number;
}
+/// Gets a int32_t number from a string.
+///
+/// @see getdigits
+int32_t getdigits_int32(char **pp, bool strict, long def)
+{
+ intmax_t number = getdigits((char_u **)pp, strict, def);
+#if SIZEOF_INTMAX_T > SIZEOF_INT32_T
+ if (strict) {
+ assert(number >= INT32_MIN && number <= INT32_MAX);
+ } else if (!(number >= INT32_MIN && number <= INT32_MAX)) {
+ return (int32_t)def;
+ }
+#endif
+ return (int32_t)number;
+}
+
/// Check that "lbuf" is empty or only contains blanks.
///
/// @param lbuf line buffer to check
bool vim_isblankline(char_u *lbuf)
+ FUNC_ATTR_PURE
{
- char_u *p = skipwhite(lbuf);
+ char_u *p = (char_u *)skipwhite((char *)lbuf);
return *p == NUL || *p == '\r' || *p == '\n';
}
@@ -1441,7 +1451,7 @@ bool vim_isblankline(char_u *lbuf)
/// @param unptr Returns the unsigned result.
/// @param maxlen Max length of string to check.
/// @param strict If true, fail if the number has unexpected trailing
-/// alpha-numeric chars: *len is set to 0 and nothing else is
+/// alphanumeric chars: *len is set to 0 and nothing else is
/// returned.
void vim_str2nr(const char_u *const start, int *const prep, int *const len, const int what,
varnumber_T *const nptr, uvarnumber_T *const unptr, const int maxlen,
@@ -1502,7 +1512,7 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, cons
} else if ((what & (STR2NR_HEX | STR2NR_OCT | STR2NR_OOCT | STR2NR_BIN))
&& !STRING_ENDED(ptr + 1) && ptr[0] == '0' && ptr[1] != '8'
&& ptr[1] != '9') {
- pre = ptr[1];
+ pre = (char_u)ptr[1];
// Detect hexadecimal: 0x or 0X followed by hex digit.
if ((what & STR2NR_HEX)
&& !STRING_ENDED(ptr + 2)
@@ -1546,6 +1556,7 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, cons
// Do the conversion manually to avoid sscanf() quirks.
abort(); // Should’ve used goto earlier.
+ // -V:PARSE_NUMBER:560
#define PARSE_NUMBER(base, cond, conv) \
do { \
const char *const after_prefix = ptr; \
@@ -1562,10 +1573,10 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, cons
} \
const uvarnumber_T digit = (uvarnumber_T)(conv); \
/* avoid ubsan error for overflow */ \
- if (un < UVARNUMBER_MAX / base \
- || (un == UVARNUMBER_MAX / base \
- && (base != 10 || digit <= UVARNUMBER_MAX % 10))) { \
- un = base * un + digit; \
+ if (un < UVARNUMBER_MAX / (base) \
+ || (un == UVARNUMBER_MAX / (base) \
+ && ((base) != 10 || digit <= UVARNUMBER_MAX % 10))) { \
+ un = (base) * un + digit; \
} else { \
un = UVARNUMBER_MAX; \
} \
@@ -1587,7 +1598,7 @@ vim_str2nr_hex:
#undef PARSE_NUMBER
vim_str2nr_proceed:
- // Check for an alpha-numeric character immediately following, that is
+ // Check for an alphanumeric character immediately following, that is
// most likely a typo.
if (strict && ptr - (const char *)start != maxlen && ASCII_ISALNUM(*ptr)) {
return;
@@ -1630,6 +1641,7 @@ vim_str2nr_proceed:
///
/// @return The value of the hex character.
int hex2nr(int c)
+ FUNC_ATTR_CONST
{
if ((c >= 'a') && (c <= 'f')) {
return c - 'a' + 10;
@@ -1644,6 +1656,7 @@ int hex2nr(int c)
/// Convert two hex characters to a byte.
/// Return -1 if one of the characters is not hex.
int hexhex2nr(char_u *p)
+ FUNC_ATTR_PURE
{
if (!ascii_isxdigit(p[0]) || !ascii_isxdigit(p[1])) {
return -1;
@@ -1686,7 +1699,7 @@ bool rem_backslash(const char_u *str)
/// @param p
void backslash_halve(char_u *p)
{
- for (; *p; ++p) {
+ for (; *p; p++) {
if (rem_backslash(p)) {
STRMOVE(p, p + 1);
}
diff --git a/src/nvim/context.c b/src/nvim/context.c
index 614a3ce30e..db26667009 100644
--- a/src/nvim/context.c
+++ b/src/nvim/context.c
@@ -33,6 +33,7 @@ void ctx_free_all(void)
/// Returns the size of the context stack.
size_t ctx_size(void)
+ FUNC_ATTR_PURE
{
return kv_size(ctx_stack);
}
@@ -40,6 +41,7 @@ size_t ctx_size(void)
/// Returns pointer to Context object with given zero-based index from the top
/// of context stack or NULL if index is out of bounds.
Context *ctx_get(size_t index)
+ FUNC_ATTR_PURE
{
if (index < kv_size(ctx_stack)) {
return &kv_Z(ctx_stack, index);
@@ -127,7 +129,7 @@ bool ctx_restore(Context *ctx, const int flags)
free_ctx = true;
}
- char_u *op_shada;
+ char *op_shada;
get_option_value("shada", NULL, &op_shada, OPT_GLOBAL);
set_option_value("shada", 0L, "!,'100,%", OPT_GLOBAL);
@@ -155,7 +157,7 @@ bool ctx_restore(Context *ctx, const int flags)
ctx_free(ctx);
}
- set_option_value("shada", 0L, (char *)op_shada, OPT_GLOBAL);
+ set_option_value("shada", 0L, op_shada, OPT_GLOBAL);
xfree(op_shada);
return true;
@@ -256,7 +258,8 @@ static inline void ctx_save_funcs(Context *ctx, bool scriptonly)
size_t cmd_len = sizeof("func! ") + STRLEN(name);
char *cmd = xmalloc(cmd_len);
snprintf(cmd, cmd_len, "func! %s", name);
- String func_body = nvim_exec(cstr_as_string(cmd), true, &err);
+ String func_body = nvim_exec(VIML_INTERNAL_CALL, cstr_as_string(cmd),
+ true, &err);
xfree(cmd);
if (!ERROR_SET(&err)) {
ADD(ctx->funcs, STRING_OBJ(func_body));
diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c
index 6e2c6232d7..1446257f7e 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"
@@ -24,9 +25,7 @@
# include "cursor.c.generated.h"
#endif
-/*
- * Get the screen position of the cursor.
- */
+/// @return the screen position of the cursor.
int getviscol(void)
{
colnr_T x;
@@ -35,9 +34,7 @@ int getviscol(void)
return (int)x;
}
-/*
- * Get the screen position of character col with a coladd in the cursor line.
- */
+/// @return the screen position of character col with a coladd in the cursor line.
int getviscol2(colnr_T col, colnr_T coladd)
{
colnr_T x;
@@ -50,11 +47,9 @@ int getviscol2(colnr_T col, colnr_T coladd)
return (int)x;
}
-/*
- * Go to column "wcol", and add/insert white space as necessary to get the
- * cursor in that column.
- * The caller must have saved the cursor line for undo!
- */
+/// Go to column "wcol", and add/insert white space as necessary to get the
+/// cursor in that column.
+/// The caller must have saved the cursor line for undo!
int coladvance_force(colnr_T wcol)
{
int rc = coladvance2(&curwin->w_cursor, true, false, wcol);
@@ -69,15 +64,13 @@ int coladvance_force(colnr_T wcol)
return rc;
}
-/*
- * Try to advance the Cursor to the specified screen column.
- * If virtual editing: fine tune the cursor position.
- * Note that all virtual positions off the end of a line should share
- * a curwin->w_cursor.col value (n.b. this is equal to STRLEN(line)),
- * beginning at coladd 0.
- *
- * return OK if desired column is reached, FAIL if not
- */
+/// Try to advance the Cursor to the specified screen column.
+/// If virtual editing: fine tune the cursor position.
+/// Note that all virtual positions off the end of a line should share
+/// a curwin->w_cursor.col value (n.b. this is equal to STRLEN(line)),
+/// beginning at coladd 0.
+///
+/// @return OK if desired column is reached, FAIL if not
int coladvance(colnr_T wcol)
{
int rc = getvpos(&curwin->w_cursor, wcol);
@@ -99,19 +92,16 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a
{
colnr_T wcol = wcol_arg;
int idx;
- char_u *ptr;
- char_u *line;
colnr_T col = 0;
- int csize = 0;
- int one_more;
int head = 0;
- one_more = (State & INSERT)
- || (State & TERM_FOCUS)
- || restart_edit != NUL
- || (VIsual_active && *p_sel != 'o')
- || ((ve_flags & VE_ONEMORE) && wcol < MAXCOL);
- line = ml_get_buf(curbuf, pos->lnum, false);
+ int one_more = (State & MODE_INSERT)
+ || (State & MODE_TERMINAL)
+ || restart_edit != NUL
+ || (VIsual_active && *p_sel != 'o')
+ || ((get_ve_flags() & VE_ONEMORE) && wcol < MAXCOL);
+
+ char_u *line = ml_get_buf(curbuf, pos->lnum, false);
if (wcol >= MAXCOL) {
idx = (int)STRLEN(line) - 1 + one_more;
@@ -120,11 +110,12 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a
if ((addspaces || finetune) && !VIsual_active) {
curwin->w_curswant = linetabsize(line) + one_more;
if (curwin->w_curswant > 0) {
- --curwin->w_curswant;
+ curwin->w_curswant--;
}
}
} else {
int width = curwin->w_width_inner - win_col_off(curwin);
+ int csize = 0;
if (finetune
&& curwin->w_p_wrap
@@ -137,16 +128,16 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a
}
if (wcol / width > (colnr_T)csize / width
- && ((State & INSERT) == 0 || (int)wcol > csize + 1)) {
- /* In case of line wrapping don't move the cursor beyond the
- * right screen edge. In Insert mode allow going just beyond
- * the last character (like what happens when typing and
- * reaching the right window edge). */
+ && ((State & MODE_INSERT) == 0 || (int)wcol > csize + 1)) {
+ // In case of line wrapping don't move the cursor beyond the
+ // right screen edge. In Insert mode allow going just beyond
+ // the last character (like what happens when typing and
+ // reaching the right window edge).
wcol = (csize / width + 1) * width - 1;
}
}
- ptr = line;
+ char_u *ptr = line;
while (col <= wcol && *ptr != NUL) {
// Count a tab for what it's worth (if list mode not on)
csize = win_lbr_chartabsize(curwin, line, ptr, col, &head);
@@ -154,12 +145,10 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a
col += csize;
}
idx = (int)(ptr - line);
- /*
- * Handle all the special cases. The virtual_active() check
- * is needed to ensure that a virtual position off the end of
- * a line has the correct indexing. The one_more comparison
- * replaces an explicit add of one_more later on.
- */
+ // Handle all the special cases. The virtual_active() check
+ // is needed to ensure that a virtual position off the end of
+ // a line has the correct indexing. The one_more comparison
+ // replaces an explicit add of one_more later on.
if (col > wcol || (!virtual_active() && one_more == 0)) {
idx -= 1;
// Don't count the chars from 'showbreak'.
@@ -171,8 +160,7 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a
&& addspaces
&& wcol >= 0
&& ((col != wcol && col != wcol + 1) || csize > 1)) {
- /* 'virtualedit' is set: The difference between wcol and col is
- * filled with spaces. */
+ // 'virtualedit' is set: The difference between wcol and col is filled with spaces.
if (line[idx] == NUL) {
// Append spaces
@@ -183,7 +171,7 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a
memcpy(newline, line, (size_t)idx);
memset(newline + idx, ' ', (size_t)correct);
- ml_replace(pos->lnum, newline, false);
+ ml_replace(pos->lnum, (char *)newline, false);
inserted_bytes(pos->lnum, (colnr_T)idx, 0, correct);
idx += correct;
col = wcol;
@@ -209,7 +197,7 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a
STRICT_SUB(n, 1, &n, size_t);
memcpy(newline + idx + csize, line + idx + 1, n);
- ml_replace(pos->lnum, newline, false);
+ ml_replace(pos->lnum, (char *)newline, false);
inserted_bytes(pos->lnum, idx, 1, csize);
idx += (csize - 1 + correct);
col += correct;
@@ -255,29 +243,23 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a
return OK;
}
-/*
- * Return in "pos" the position of the cursor advanced to screen column "wcol".
- * return OK if desired column is reached, FAIL if not
- */
+/// Return in "pos" the position of the cursor advanced to screen column "wcol".
+///
+/// @return OK if desired column is reached, FAIL if not
int getvpos(pos_T *pos, colnr_T wcol)
{
return coladvance2(pos, false, virtual_active(), wcol);
}
-/*
- * Increment the cursor position. See inc() for return values.
- */
+/// Increment the cursor position. See inc() for return values.
int inc_cursor(void)
{
return inc(&curwin->w_cursor);
}
-/*
- * dec(p)
- *
- * Decrement the line pointer 'p' crossing line boundaries as necessary.
- * Return 1 when crossing a line, -1 when at start of file, 0 otherwise.
- */
+/// Decrement the line pointer 'p' crossing line boundaries as necessary.
+///
+/// @return 1 when crossing a line, -1 when at start of file, 0 otherwise.
int dec_cursor(void)
{
return dec(&curwin->w_cursor);
@@ -313,34 +295,29 @@ linenr_T get_cursor_rel_lnum(win_T *wp, linenr_T lnum)
return (lnum < cursor) ? -retval : retval;
}
-// Make sure "pos.lnum" and "pos.col" are valid in "buf".
-// This allows for the col to be on the NUL byte.
+/// Make sure "pos.lnum" and "pos.col" are valid in "buf".
+/// This allows for the col to be on the NUL byte.
void check_pos(buf_T *buf, pos_T *pos)
{
- char_u *line;
- colnr_T len;
-
if (pos->lnum > buf->b_ml.ml_line_count) {
pos->lnum = buf->b_ml.ml_line_count;
}
if (pos->col > 0) {
- line = ml_get_buf(buf, pos->lnum, false);
- len = (colnr_T)STRLEN(line);
+ char_u *line = ml_get_buf(buf, pos->lnum, false);
+ colnr_T len = (colnr_T)STRLEN(line);
if (pos->col > len) {
pos->col = len;
}
}
}
-/*
- * Make sure curwin->w_cursor.lnum is valid.
- */
+/// Make sure curwin->w_cursor.lnum is valid.
void check_cursor_lnum(void)
{
if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) {
- /* If there is a closed fold at the end of the file, put the cursor in
- * its first line. Otherwise in the last line. */
+ // If there is a closed fold at the end of the file, put the cursor in
+ // its first line. Otherwise in the last line.
if (!hasFolding(curbuf->b_ml.ml_line_count,
&curwin->w_cursor.lnum, NULL)) {
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
@@ -351,9 +328,7 @@ void check_cursor_lnum(void)
}
}
-/*
- * Make sure curwin->w_cursor.col is valid.
- */
+/// Make sure curwin->w_cursor.col is valid.
void check_cursor_col(void)
{
check_cursor_col_win(curwin);
@@ -363,21 +338,21 @@ void check_cursor_col(void)
/// @see mb_check_adjust_col
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));
+ colnr_T len = (colnr_T)STRLEN(ml_get_buf(win->w_buffer, win->w_cursor.lnum, false));
if (len == 0) {
win->w_cursor.col = 0;
} else if (win->w_cursor.col >= len) {
- /* Allow cursor past end-of-line when:
- * - in Insert mode or restarting Insert mode
- * - in Visual mode and 'selection' isn't "old"
- * - 'virtualedit' is set */
- if ((State & INSERT) || restart_edit
+ // Allow cursor past end-of-line when:
+ // - in Insert mode or restarting Insert mode
+ // - in Visual mode and 'selection' isn't "old"
+ // - 'virtualedit' is set */
+ if ((State & MODE_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 +369,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;
@@ -417,32 +392,45 @@ void check_cursor_col_win(win_T *win)
}
}
-/*
- * make sure curwin->w_cursor in on a valid character
- */
+/// Make sure curwin->w_cursor in on a valid character
void check_cursor(void)
{
check_cursor_lnum();
check_cursor_col();
}
-/*
- * Make sure curwin->w_cursor is not on the NUL at the end of the line.
- * Allow it when in Visual mode and 'selection' is not "old".
- */
+/// Check if VIsual position is valid, correct it if not.
+/// Can be called when in Visual mode and a change has been made.
+void check_visual_pos(void)
+{
+ if (VIsual.lnum > curbuf->b_ml.ml_line_count) {
+ VIsual.lnum = curbuf->b_ml.ml_line_count;
+ VIsual.col = 0;
+ VIsual.coladd = 0;
+ } else {
+ int len = (int)STRLEN(ml_get(VIsual.lnum));
+
+ if (VIsual.col > len) {
+ VIsual.col = len;
+ VIsual.coladd = 0;
+ }
+ }
+}
+
+/// Make sure curwin->w_cursor is not on the NUL at the end of the line.
+/// Allow it when in Visual mode and 'selection' is not "old".
void adjust_cursor_col(void)
{
if (curwin->w_cursor.col > 0
&& (!VIsual_active || *p_sel == 'o')
&& gchar_cursor() == NUL) {
- --curwin->w_cursor.col;
+ curwin->w_cursor.col--;
}
}
-/*
- * When curwin->w_leftcol has changed, adjust the cursor position.
- * Return true if the cursor was moved.
- */
+/// When curwin->w_leftcol has changed, adjust the cursor position.
+///
+/// @return true if the cursor was moved.
bool leftcol_changed(void)
{
// TODO(hinidu): I think it should be colnr_T or int, but p_siso is long.
@@ -455,10 +443,8 @@ bool leftcol_changed(void)
lastcol = curwin->w_leftcol + curwin->w_width_inner - curwin_col_off() - 1;
validate_virtcol();
- /*
- * If the cursor is right or left of the screen, move it to last or first
- * character.
- */
+ // If the cursor is right or left of the screen, move it to last or first
+ // character.
if (curwin->w_virtcol > (colnr_T)(lastcol - p_siso)) {
retval = true;
coladvance((colnr_T)(lastcol - p_siso));
@@ -467,11 +453,9 @@ bool leftcol_changed(void)
coladvance((colnr_T)(curwin->w_leftcol + p_siso));
}
- /*
- * If the start of the character under the cursor is not on the screen,
- * advance the cursor one more char. If this fails (last char of the
- * line) adjust the scrolling.
- */
+ // If the start of the character under the cursor is not on the screen,
+ // advance the cursor one more char. If this fails (last char of the
+ // line) adjust the scrolling.
getvvcol(curwin, &curwin->w_cursor, &s, NULL, &e);
if (e > (colnr_T)lastcol) {
retval = true;
@@ -493,30 +477,24 @@ bool leftcol_changed(void)
int gchar_cursor(void)
{
- return utf_ptr2char(get_cursor_pos_ptr());
+ return utf_ptr2char((char *)get_cursor_pos_ptr());
}
-/*
- * Write a character at the current cursor position.
- * It is directly written into the block.
- */
+/// Write a character at the current cursor position.
+/// It is directly written into the block.
void pchar_cursor(char_u c)
{
*(ml_get_buf(curbuf, curwin->w_cursor.lnum, true)
+ curwin->w_cursor.col) = c;
}
-/*
- * Return pointer to cursor line.
- */
+/// @return pointer to cursor line.
char_u *get_cursor_line_ptr(void)
{
return ml_get_buf(curbuf, curwin->w_cursor.lnum, false);
}
-/*
- * Return pointer to cursor position.
- */
+/// @return pointer to cursor position.
char_u *get_cursor_pos_ptr(void)
{
return ml_get_buf(curbuf, curwin->w_cursor.lnum, false) +
diff --git a/src/nvim/cursor_shape.c b/src/nvim/cursor_shape.c
index 6b0a5dfe12..62cf60e03b 100644
--- a/src/nvim/cursor_shape.c
+++ b/src/nvim/cursor_shape.c
@@ -9,8 +9,8 @@
#include "nvim/charset.h"
#include "nvim/cursor_shape.h"
#include "nvim/ex_getln.h"
+#include "nvim/highlight_group.h"
#include "nvim/strings.h"
-#include "nvim/syntax.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
@@ -23,15 +23,15 @@ cursorentry_T shape_table[SHAPE_IDX_COUNT] =
{
// Values are set by 'guicursor' and 'mouseshape'.
// Adjust the SHAPE_IDX_ defines when changing this!
- { "normal", 0, 0, 0, 700L, 400L, 250L, 0, 0, "n", SHAPE_CURSOR+SHAPE_MOUSE },
- { "visual", 0, 0, 0, 700L, 400L, 250L, 0, 0, "v", SHAPE_CURSOR+SHAPE_MOUSE },
- { "insert", 0, 0, 0, 700L, 400L, 250L, 0, 0, "i", SHAPE_CURSOR+SHAPE_MOUSE },
- { "replace", 0, 0, 0, 700L, 400L, 250L, 0, 0, "r", SHAPE_CURSOR+SHAPE_MOUSE },
- { "cmdline_normal", 0, 0, 0, 700L, 400L, 250L, 0, 0, "c", SHAPE_CURSOR+SHAPE_MOUSE },
- { "cmdline_insert", 0, 0, 0, 700L, 400L, 250L, 0, 0, "ci", SHAPE_CURSOR+SHAPE_MOUSE },
- { "cmdline_replace", 0, 0, 0, 700L, 400L, 250L, 0, 0, "cr", SHAPE_CURSOR+SHAPE_MOUSE },
- { "operator", 0, 0, 0, 700L, 400L, 250L, 0, 0, "o", SHAPE_CURSOR+SHAPE_MOUSE },
- { "visual_select", 0, 0, 0, 700L, 400L, 250L, 0, 0, "ve", SHAPE_CURSOR+SHAPE_MOUSE },
+ { "normal", 0, 0, 0, 700L, 400L, 250L, 0, 0, "n", SHAPE_CURSOR + SHAPE_MOUSE },
+ { "visual", 0, 0, 0, 700L, 400L, 250L, 0, 0, "v", SHAPE_CURSOR + SHAPE_MOUSE },
+ { "insert", 0, 0, 0, 700L, 400L, 250L, 0, 0, "i", SHAPE_CURSOR + SHAPE_MOUSE },
+ { "replace", 0, 0, 0, 700L, 400L, 250L, 0, 0, "r", SHAPE_CURSOR + SHAPE_MOUSE },
+ { "cmdline_normal", 0, 0, 0, 700L, 400L, 250L, 0, 0, "c", SHAPE_CURSOR + SHAPE_MOUSE },
+ { "cmdline_insert", 0, 0, 0, 700L, 400L, 250L, 0, 0, "ci", SHAPE_CURSOR + SHAPE_MOUSE },
+ { "cmdline_replace", 0, 0, 0, 700L, 400L, 250L, 0, 0, "cr", SHAPE_CURSOR + SHAPE_MOUSE },
+ { "operator", 0, 0, 0, 700L, 400L, 250L, 0, 0, "o", SHAPE_CURSOR + SHAPE_MOUSE },
+ { "visual_select", 0, 0, 0, 700L, 400L, 250L, 0, 0, "ve", SHAPE_CURSOR + SHAPE_MOUSE },
{ "cmdline_hover", 0, 0, 0, 0L, 0L, 0L, 0, 0, "e", SHAPE_MOUSE },
{ "statusline_hover", 0, 0, 0, 0L, 0L, 0L, 0, 0, "s", SHAPE_MOUSE },
{ "statusline_drag", 0, 0, 0, 0L, 0L, 0L, 0, 0, "sd", SHAPE_MOUSE },
@@ -43,44 +43,45 @@ cursorentry_T shape_table[SHAPE_IDX_COUNT] =
};
/// Converts cursor_shapes into an Array of Dictionaries
+/// @param arena initialized arena where memory will be alocated
+///
/// @return Array of the form {[ "cursor_shape": ... ], ...}
-Array mode_style_array(void)
+Array mode_style_array(Arena *arena)
{
- Array all = ARRAY_DICT_INIT;
+ Array all = arena_array(arena, SHAPE_IDX_COUNT);
for (int i = 0; i < SHAPE_IDX_COUNT; i++) {
- Dictionary dic = ARRAY_DICT_INIT;
cursorentry_T *cur = &shape_table[i];
+ Dictionary dic = arena_dict(arena, 3 + ((cur->used_for & SHAPE_CURSOR) ? 9 : 0));
+ PUT_C(dic, "name", STRING_OBJ(cstr_as_string(cur->full_name)));
+ PUT_C(dic, "short_name", STRING_OBJ(cstr_as_string(cur->name)));
if (cur->used_for & SHAPE_MOUSE) {
- PUT(dic, "mouse_shape", INTEGER_OBJ(cur->mshape));
+ PUT_C(dic, "mouse_shape", INTEGER_OBJ(cur->mshape));
}
if (cur->used_for & SHAPE_CURSOR) {
String shape_str;
switch (cur->shape) {
case SHAPE_BLOCK:
- shape_str = cstr_to_string("block"); break;
+ shape_str = cstr_as_string("block"); break;
case SHAPE_VER:
- shape_str = cstr_to_string("vertical"); break;
+ shape_str = cstr_as_string("vertical"); break;
case SHAPE_HOR:
- shape_str = cstr_to_string("horizontal"); break;
+ shape_str = cstr_as_string("horizontal"); break;
default:
- shape_str = cstr_to_string("unknown");
+ shape_str = cstr_as_string("unknown");
}
- PUT(dic, "cursor_shape", STRING_OBJ(shape_str));
- PUT(dic, "cell_percentage", INTEGER_OBJ(cur->percentage));
- PUT(dic, "blinkwait", INTEGER_OBJ(cur->blinkwait));
- PUT(dic, "blinkon", INTEGER_OBJ(cur->blinkon));
- PUT(dic, "blinkoff", INTEGER_OBJ(cur->blinkoff));
- PUT(dic, "hl_id", INTEGER_OBJ(cur->id));
- PUT(dic, "id_lm", INTEGER_OBJ(cur->id_lm));
- PUT(dic, "attr_id", INTEGER_OBJ(cur->id ? syn_id2attr(cur->id) : 0));
- PUT(dic, "attr_id_lm", INTEGER_OBJ(cur->id_lm ? syn_id2attr(cur->id_lm)
- : 0));
+ PUT_C(dic, "cursor_shape", STRING_OBJ(shape_str));
+ PUT_C(dic, "cell_percentage", INTEGER_OBJ(cur->percentage));
+ PUT_C(dic, "blinkwait", INTEGER_OBJ(cur->blinkwait));
+ PUT_C(dic, "blinkon", INTEGER_OBJ(cur->blinkon));
+ PUT_C(dic, "blinkoff", INTEGER_OBJ(cur->blinkoff));
+ PUT_C(dic, "hl_id", INTEGER_OBJ(cur->id));
+ PUT_C(dic, "id_lm", INTEGER_OBJ(cur->id_lm));
+ PUT_C(dic, "attr_id", INTEGER_OBJ(cur->id ? syn_id2attr(cur->id) : 0));
+ PUT_C(dic, "attr_id_lm", INTEGER_OBJ(cur->id_lm ? syn_id2attr(cur->id_lm) : 0));
}
- PUT(dic, "name", STRING_OBJ(cstr_to_string(cur->full_name)));
- PUT(dic, "short_name", STRING_OBJ(cstr_to_string(cur->name)));
- ADD(all, DICTIONARY_OBJ(dic));
+ ADD_C(all, DICTIONARY_OBJ(dic));
}
return all;
@@ -95,12 +96,11 @@ Array mode_style_array(void)
/// @returns error message for an illegal option, NULL otherwise.
char *parse_shape_opt(int what)
{
- char_u *modep;
- char_u *colonp;
- char_u *commap;
- char_u *slashp;
- char_u *p = NULL;
- char_u *endp;
+ char *colonp;
+ char *commap;
+ char *slashp;
+ char *p = NULL;
+ char *endp;
int idx = 0; // init for GCC
int all_idx;
int len;
@@ -120,7 +120,7 @@ char *parse_shape_opt(int what)
}
}
// Repeat for all comma separated parts.
- modep = p_guicursor;
+ char *modep = (char *)p_guicursor;
while (modep != NULL && *modep != NUL) {
colonp = vim_strchr(modep, ':');
commap = vim_strchr(modep, ',');
@@ -147,7 +147,7 @@ char *parse_shape_opt(int what)
if (len == 1 && TOLOWER_ASC(modep[0]) == 'a') {
all_idx = SHAPE_IDX_COUNT - 1;
} else {
- for (idx = 0; idx < SHAPE_IDX_COUNT; ++idx) {
+ for (idx = 0; idx < SHAPE_IDX_COUNT; idx++) {
if (STRNICMP(modep, shape_table[idx].name, len) == 0) {
break;
}
@@ -170,10 +170,8 @@ char *parse_shape_opt(int what)
// Parse the part after the colon
for (p = colonp + 1; *p && *p != ',';) {
{
- /*
- * First handle the ones with a number argument.
- */
- i = *p;
+ // First handle the ones with a number argument.
+ i = (uint8_t)(*p);
len = 0;
if (STRNICMP(p, "ver", 3) == 0) {
len = 3;
@@ -230,11 +228,11 @@ char *parse_shape_opt(int what)
slashp = vim_strchr(p, '/');
if (slashp != NULL && slashp < endp) {
// "group/langmap_group"
- i = syn_check_group((char *)p, (int)(slashp - p));
+ i = syn_check_group(p, (size_t)(slashp - p));
p = slashp + 1;
}
if (round == 2) {
- shape_table[idx].id = syn_check_group((char *)p, (int)(endp - p));
+ shape_table[idx].id = syn_check_group(p, (size_t)(endp - p));
shape_table[idx].id_lm = shape_table[idx].id;
if (slashp != NULL && slashp < endp) {
shape_table[idx].id = i;
@@ -245,7 +243,7 @@ char *parse_shape_opt(int what)
} // if (what != SHAPE_MOUSE)
if (*p == '-') {
- ++p;
+ p++;
}
}
}
@@ -281,6 +279,7 @@ char *parse_shape_opt(int what)
///
/// @param exclusive If 'selection' option is "exclusive".
bool cursor_is_block_during_visual(bool exclusive)
+ FUNC_ATTR_PURE
{
int mode_idx = exclusive ? SHAPE_IDX_VE : SHAPE_IDX_V;
return (SHAPE_BLOCK == shape_table[mode_idx].shape
@@ -304,6 +303,7 @@ int cursor_mode_str2int(const char *mode)
/// Check if a syntax id is used as a cursor style.
bool cursor_mode_uses_syn_id(int syn_id)
+ FUNC_ATTR_PURE
{
if (*p_guicursor == NUL) {
return false;
@@ -317,19 +317,19 @@ bool cursor_mode_uses_syn_id(int syn_id)
return false;
}
-
/// Return the index into shape_table[] for the current mode.
int cursor_get_mode_idx(void)
+ FUNC_ATTR_PURE
{
- if (State == SHOWMATCH) {
+ if (State == MODE_SHOWMATCH) {
return SHAPE_IDX_SM;
} else if (State & VREPLACE_FLAG) {
return SHAPE_IDX_R;
} else if (State & REPLACE_FLAG) {
return SHAPE_IDX_R;
- } else if (State & INSERT) {
+ } else if (State & MODE_INSERT) {
return SHAPE_IDX_I;
- } else if (State & CMDLINE) {
+ } else if (State & MODE_CMDLINE) {
if (cmdline_at_end()) {
return SHAPE_IDX_C;
} else if (cmdline_overstrike()) {
diff --git a/src/nvim/debugger.c b/src/nvim/debugger.c
index b6e35f3047..0eaff06833 100644
--- a/src/nvim/debugger.c
+++ b/src/nvim/debugger.c
@@ -58,10 +58,9 @@ void do_debug(char_u *cmd)
tasave_T typeaheadbuf;
bool typeahead_saved = false;
int save_ignore_script = 0;
- int save_ex_normal_busy;
int n;
char_u *cmdline = NULL;
- char_u *p;
+ char *p;
char *tail = NULL;
static int last_cmd = 0;
#define CMD_CONT 1
@@ -75,7 +74,6 @@ void do_debug(char_u *cmd)
#define CMD_UP 9
#define CMD_DOWN 10
-
RedrawingDisabled++; // don't redisplay the window
no_wait_return++; // don't wait for return
did_emsg = false; // don't use error from debugged stuff
@@ -84,7 +82,7 @@ void do_debug(char_u *cmd)
emsg_silent = false; // display error messages
redir_off = true; // don't redirect debug commands
- State = NORMAL;
+ State = MODE_NORMAL;
debug_mode = true;
if (!debug_did_msg) {
@@ -101,7 +99,7 @@ void do_debug(char_u *cmd)
debug_newval = NULL;
}
if (sourcing_name != NULL) {
- msg((char *)sourcing_name);
+ msg(sourcing_name);
}
if (sourcing_lnum != 0) {
smsg(_("line %" PRId64 ": %s"), (int64_t)sourcing_lnum, cmd);
@@ -117,7 +115,7 @@ void do_debug(char_u *cmd)
// with the commands being executed. Reset "ex_normal_busy" to avoid
// the side effects of using ":normal". Save the stuff buffer and make
// it empty. Set ignore_script to avoid reading from script input.
- save_ex_normal_busy = ex_normal_busy;
+ int save_ex_normal_busy = ex_normal_busy;
ex_normal_busy = 0;
if (!debug_greedy) {
save_typeahead(&typeaheadbuf);
@@ -142,7 +140,7 @@ void do_debug(char_u *cmd)
// If this is a debug command, set "last_cmd".
// If not, reset "last_cmd".
// For a blank line use previous command.
- p = skipwhite(cmdline);
+ p = skipwhite((char *)cmdline);
if (*p != NUL) {
switch (*p) {
case 'c':
@@ -244,7 +242,7 @@ void do_debug(char_u *cmd)
do_showbacktrace(cmd);
} else {
p = skipwhite(p);
- do_setdebugtracelevel(p);
+ do_setdebugtracelevel((char_u *)p);
}
continue;
case CMD_UP:
@@ -264,11 +262,10 @@ void do_debug(char_u *cmd)
// don't debug this command
n = debug_break_level;
debug_break_level = -1;
- (void)do_cmdline(cmdline, getexline, NULL,
- DOCMD_VERBOSE|DOCMD_EXCRESET);
+ (void)do_cmdline((char *)cmdline, getexline, NULL, DOCMD_VERBOSE|DOCMD_EXCRESET);
debug_break_level = n;
}
- lines_left = (int)(Rows - 1);
+ lines_left = Rows - 1;
}
xfree(cmdline);
@@ -277,7 +274,7 @@ void do_debug(char_u *cmd)
redraw_all_later(NOT_VALID);
need_wait_return = false;
msg_scroll = save_msg_scroll;
- lines_left = (int)(Rows - 1);
+ lines_left = Rows - 1;
State = save_State;
debug_mode = false;
did_emsg = save_did_emsg;
@@ -295,7 +292,7 @@ static int get_maxbacktrace_level(void)
int maxbacktrace = 0;
if (sourcing_name != NULL) {
- char *p = (char *)sourcing_name;
+ char *p = sourcing_name;
char *q;
while ((q = strstr(p, "..")) != NULL) {
p = q + 2;
@@ -336,7 +333,7 @@ static void do_showbacktrace(char_u *cmd)
if (sourcing_name != NULL) {
int i = 0;
int max = get_maxbacktrace_level();
- char *cur = (char *)sourcing_name;
+ char *cur = sourcing_name;
while (!got_int) {
char *next = strstr(cur, "..");
if (next != NULL) {
@@ -368,7 +365,7 @@ void ex_debug(exarg_T *eap)
int debug_break_level_save = debug_break_level;
debug_break_level = 9999;
- do_cmdline_cmd((char *)eap->arg);
+ do_cmdline_cmd(eap->arg);
debug_break_level = debug_break_level_save;
}
@@ -390,11 +387,10 @@ static char_u *debug_skipped_name;
/// Called from do_one_cmd() before executing a command.
void dbg_check_breakpoint(exarg_T *eap)
{
- char *p;
-
debug_skipped = false;
if (debug_breakpoint_name != NULL) {
if (!eap->skip) {
+ char *p;
// replace K_SNR with "<SNR>"
if (debug_breakpoint_name[0] == K_SPECIAL
&& debug_breakpoint_name[1] == KS_EXTRA
@@ -408,7 +404,7 @@ void dbg_check_breakpoint(exarg_T *eap)
debug_breakpoint_name + (*p == NUL ? 0 : 3),
(int64_t)debug_breakpoint_lnum);
debug_breakpoint_name = NULL;
- do_debug(eap->cmd);
+ do_debug((char_u *)eap->cmd);
} else {
debug_skipped = true;
debug_skipped_name = debug_breakpoint_name;
@@ -416,7 +412,7 @@ void dbg_check_breakpoint(exarg_T *eap)
}
} else if (ex_nesting_level <= debug_break_level) {
if (!eap->skip) {
- do_debug(eap->cmd);
+ do_debug((char_u *)eap->cmd);
} else {
debug_skipped = true;
debug_skipped_name = NULL;
@@ -430,12 +426,10 @@ void dbg_check_breakpoint(exarg_T *eap)
/// @return true when the debug mode is entered this time.
bool dbg_check_skipped(exarg_T *eap)
{
- int prev_got_int;
-
if (debug_skipped) {
// Save the value of got_int and reset it. We don't want a previous
// interruption cause flushing the input buffer.
- prev_got_int = got_int;
+ int prev_got_int = got_int;
got_int = false;
debug_breakpoint_name = debug_skipped_name;
// eap->skip is true
@@ -450,7 +444,7 @@ bool dbg_check_skipped(exarg_T *eap)
static garray_T dbg_breakp = { 0, 0, sizeof(struct debuggy), 4, NULL };
#define BREAKP(idx) (((struct debuggy *)dbg_breakp.ga_data)[idx])
-#define DEBUGGY(gap, idx) (((struct debuggy *)gap->ga_data)[idx])
+#define DEBUGGY(gap, idx) (((struct debuggy *)(gap)->ga_data)[idx])
static int last_breakp = 0; // nr of last defined breakpoint
// Profiling uses file and func names similar to breakpoints.
@@ -466,7 +460,7 @@ static typval_T *eval_expr_no_emsg(struct debuggy *const bp)
{
// Disable error messages, a bad expression would make Vim unusable.
emsg_off++;
- typval_T *const tv = eval_expr(bp->dbg_name);
+ typval_T *const tv = eval_expr((char *)bp->dbg_name);
emsg_off--;
return tv;
}
@@ -480,14 +474,13 @@ static typval_T *eval_expr_no_emsg(struct debuggy *const bp)
/// @param gap either &dbg_breakp or &prof_ga
static int dbg_parsearg(char_u *arg, garray_T *gap)
{
- char_u *p = arg;
- char_u *q;
- struct debuggy *bp;
+ char *p = (char *)arg;
+ char *q;
bool here = false;
ga_grow(gap, 1);
- bp = &DEBUGGY(gap, gap->ga_len);
+ struct debuggy *bp = &DEBUGGY(gap, gap->ga_len);
// Find "func" or "file".
if (STRNCMP(p, "func", 4) == 0) {
@@ -513,7 +506,7 @@ static int dbg_parsearg(char_u *arg, garray_T *gap)
if (here) {
bp->dbg_lnum = curwin->w_cursor.lnum;
} else if (gap != &prof_ga && ascii_isdigit(*p)) {
- bp->dbg_lnum = getdigits_long(&p, true, 0);
+ bp->dbg_lnum = getdigits_int32(&p, true, 0);
p = skipwhite(p);
} else {
bp->dbg_lnum = 0;
@@ -522,17 +515,17 @@ static int dbg_parsearg(char_u *arg, garray_T *gap)
// Find the function or file name. Don't accept a function name with ().
if ((!here && *p == NUL)
|| (here && *p != NUL)
- || (bp->dbg_type == DBG_FUNC && strstr((char *)p, "()") != NULL)) {
+ || (bp->dbg_type == DBG_FUNC && strstr(p, "()") != NULL)) {
semsg(_(e_invarg2), arg);
return FAIL;
}
if (bp->dbg_type == DBG_FUNC) {
- bp->dbg_name = vim_strsave(p);
+ bp->dbg_name = vim_strsave((char_u *)p);
} else if (here) {
- bp->dbg_name = vim_strsave(curbuf->b_ffname);
+ bp->dbg_name = vim_strsave((char_u *)curbuf->b_ffname);
} else if (bp->dbg_type == DBG_EXPR) {
- bp->dbg_name = vim_strsave(p);
+ bp->dbg_name = vim_strsave((char_u *)p);
bp->dbg_val = eval_expr_no_emsg(bp);
} else {
// Expand the file name in the same way as do_source(). This means
@@ -548,10 +541,10 @@ static int dbg_parsearg(char_u *arg, garray_T *gap)
return FAIL;
}
if (*p != '*') {
- bp->dbg_name = (char_u *)fix_fname((char *)p);
+ bp->dbg_name = (char_u *)fix_fname(p);
xfree(p);
} else {
- bp->dbg_name = p;
+ bp->dbg_name = (char_u *)p;
}
}
@@ -564,20 +557,17 @@ static int dbg_parsearg(char_u *arg, garray_T *gap)
/// ":breakadd". Also used for ":profile".
void ex_breakadd(exarg_T *eap)
{
- struct debuggy *bp;
- garray_T *gap;
-
- gap = &dbg_breakp;
+ garray_T *gap = &dbg_breakp;
if (eap->cmdidx == CMD_profile) {
gap = &prof_ga;
}
- if (dbg_parsearg(eap->arg, gap) == OK) {
- bp = &DEBUGGY(gap, gap->ga_len);
+ if (dbg_parsearg((char_u *)eap->arg, gap) == OK) {
+ struct debuggy *bp = &DEBUGGY(gap, gap->ga_len);
bp->dbg_forceit = eap->forceit;
if (bp->dbg_type != DBG_EXPR) {
- char_u *pat = file_pat_to_reg_pat(bp->dbg_name, NULL, NULL, false);
+ char *pat = file_pat_to_reg_pat((char *)bp->dbg_name, NULL, NULL, false);
if (pat != NULL) {
bp->dbg_prog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
xfree(pat);
@@ -616,20 +606,18 @@ void ex_debuggreedy(exarg_T *eap)
void ex_breakdel(exarg_T *eap)
{
struct debuggy *bp, *bpi;
- int nr;
int todel = -1;
bool del_all = false;
linenr_T best_lnum = 0;
- garray_T *gap;
+ garray_T *gap = &dbg_breakp;
- gap = &dbg_breakp;
if (eap->cmdidx == CMD_profdel) {
gap = &prof_ga;
}
if (ascii_isdigit(*eap->arg)) {
// ":breakdel {nr}"
- nr = atoi((char *)eap->arg);
+ int nr = atoi(eap->arg);
for (int i = 0; i < gap->ga_len; i++) {
if (DEBUGGY(gap, i).dbg_nr == nr) {
todel = i;
@@ -641,7 +629,7 @@ void ex_breakdel(exarg_T *eap)
del_all = true;
} else {
// ":breakdel {func|file|expr} [lnum] {name}"
- if (dbg_parsearg(eap->arg, gap) == FAIL) {
+ if (dbg_parsearg((char_u *)eap->arg, gap) == FAIL) {
return;
}
bp = &DEBUGGY(gap, gap->ga_len);
@@ -693,15 +681,13 @@ void ex_breakdel(exarg_T *eap)
/// ":breaklist".
void ex_breaklist(exarg_T *eap)
{
- struct debuggy *bp;
-
if (GA_EMPTY(&dbg_breakp)) {
msg(_("No breakpoints defined"));
} else {
for (int i = 0; i < dbg_breakp.ga_len; i++) {
- bp = &BREAKP(i);
+ struct debuggy *bp = &BREAKP(i);
if (bp->dbg_type == DBG_FILE) {
- home_replace(NULL, bp->dbg_name, NameBuff, MAXPATHL, true);
+ home_replace(NULL, (char *)bp->dbg_name, (char *)NameBuff, MAXPATHL, true);
}
if (bp->dbg_type != DBG_EXPR) {
smsg(_("%3d %s %s line %" PRId64),
diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c
index c0f3c32f93..e7c76fe38e 100644
--- a/src/nvim/decoration.c
+++ b/src/nvim/decoration.c
@@ -1,20 +1,21 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+#include "nvim/api/ui.h"
+#include "nvim/buffer.h"
#include "nvim/decoration.h"
#include "nvim/extmark.h"
#include "nvim/highlight.h"
+#include "nvim/highlight_group.h"
#include "nvim/lua/executor.h"
+#include "nvim/move.h"
#include "nvim/screen.h"
-#include "nvim/syntax.h"
#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# 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 +34,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;
@@ -45,7 +46,7 @@ void bufhl_add_hl_pos_offset(buf_T *buf, int src_id, int hl_id, lpos_T pos_start
// substituted text. But it would be more consistent to highlight
// a space _after_ the previous line instead (like highlight EOL list
// char)
- hl_start = MAX(offset-1, 0);
+ hl_start = MAX(offset - 1, 0);
end_off = 1;
hl_end = 0;
} else if (lnum == pos_start.lnum && lnum < pos_end.lnum) {
@@ -53,69 +54,64 @@ void bufhl_add_hl_pos_offset(buf_T *buf, int src_id, int hl_id, lpos_T pos_start
end_off = 1;
hl_end = 0;
} else if (pos_start.lnum < lnum && lnum == pos_end.lnum) {
- hl_start = MAX(offset-1, 0);
+ hl_start = MAX(offset - 1, 0);
hl_end = pos_end.col + offset;
} else if (pos_start.lnum == lnum && pos_end.lnum == lnum) {
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) {
- redraw_buf_range_later(buf, row1+1, row2+1);
+ if (row2 >= row1) {
+ if (!decor || decor->hl_id || decor_has_sign(decor) || decor->conceal) {
+ redraw_buf_range_later(buf, row1 + 1, row2 + 1);
+ }
}
- if (kv_size(decor->virt_text)) {
- redraw_buf_line_later(buf, row1+1);
+ if (decor && decor_virt_pos(*decor)) {
+ 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)));
+ row1 + 1 + (decor->virt_lines_above?0:1)));
}
}
void decor_remove(buf_T *buf, int row, int row2, Decoration *decor)
{
- if (kv_size(decor->virt_lines)) {
- assert(buf->b_virt_line_blocks > 0);
- buf->b_virt_line_blocks--;
- }
decor_redraw(buf, row, row2, decor);
+ if (decor) {
+ if (kv_size(decor->virt_lines)) {
+ assert(buf->b_virt_line_blocks > 0);
+ buf->b_virt_line_blocks--;
+ }
+ if (decor_has_sign(decor)) {
+ assert(buf->b_signs > 0);
+ buf->b_signs--;
+ }
+ if (row2 >= row && decor->sign_text) {
+ buf_signcols_del_check(buf, row + 1, row2 + 1);
+ }
+ }
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);
}
kv_destroy(decor->virt_lines);
+ xfree(decor->sign_text);
xfree(decor);
}
}
@@ -134,17 +130,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,9 +158,27 @@ 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;
+ }
}
+/// @return true if decor has a virtual position (virtual text or ui_watched)
+static bool decor_virt_pos(Decoration decor)
+{
+ return kv_size(decor.virt_text) || decor.ui_watched;
+}
bool decor_redraw_start(buf_T *buf, int top_row, DecorState *state)
{
@@ -176,42 +189,36 @@ 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)) {
+ // Exclude start marks if the end mark position is above the top row
+ // Exclude end marks if we have already added the start mark
+ if ((mt_start(mark) && altpos.row < top_row && !decor_virt_pos(decor))
+ || (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, mark.ns, mark.id);
} 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, mark.ns, mark.id);
}
next_mark:
@@ -237,22 +244,22 @@ bool decor_redraw_line(buf_T *buf, int row, DecorState *state)
}
static void decor_add(DecorState *state, int start_row, int start_col, int end_row, int end_col,
- Decoration *decor, bool owned)
+ Decoration *decor, bool owned, uint64_t ns_id, uint64_t mark_id)
{
int attr_id = decor->hl_id > 0 ? syn_id2attr(decor->hl_id) : 0;
DecorRange range = { start_row, start_col, end_row, end_col,
*decor, attr_id,
- kv_size(decor->virt_text) && owned, -1 };
+ kv_size(decor->virt_text) && owned, -1, ns_id, mark_id };
kv_pushp(state->active);
size_t index;
- for (index = kv_size(state->active)-1; index > 0; index--) {
- DecorRange item = kv_A(state->active, index-1);
+ for (index = kv_size(state->active) - 1; index > 0; index--) {
+ DecorRange item = kv_A(state->active, index - 1);
if (item.decor.priority <= range.decor.priority) {
break;
}
- kv_A(state->active, index) = kv_A(state->active, index-1);
+ kv_A(state->active, index) = kv_A(state->active, index - 1);
}
kv_A(state->active, index) = range;
}
@@ -266,43 +273,29 @@ 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)) {
- 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, mark.ns, mark.id);
next_mark:
marktree_itr_next(buf->b_marktree, state->itr);
@@ -310,12 +303,16 @@ next_mark:
int attr = 0;
size_t j = 0;
+ bool conceal = 0;
+ int conceal_char = 0;
+ int conceal_attr = 0;
+
for (size_t i = 0; i < kv_size(state->active); i++) {
DecorRange item = kv_A(state->active, i);
bool active = false, keep = true;
if (item.end_row < state->row
|| (item.end_row == state->row && item.end_col <= col)) {
- if (!(item.start_row >= state->row && kv_size(item.decor.virt_text))) {
+ if (!(item.start_row >= state->row && decor_virt_pos(item.decor))) {
keep = false;
}
} else {
@@ -323,19 +320,27 @@ next_mark:
|| (item.start_row == state->row && item.start_col <= col)) {
active = true;
if (item.end_row == state->row && item.end_col > col) {
- state->col_until = MIN(state->col_until, item.end_col-1);
+ state->col_until = MIN(state->col_until, item.end_col - 1);
}
} else {
if (item.start_row == state->row) {
- state->col_until = MIN(state->col_until, item.start_col-1);
+ state->col_until = MIN(state->col_until, item.start_col - 1);
}
}
}
if (active && item.attr_id > 0) {
attr = hl_combine_attr(attr, item.attr_id);
}
+ if (active && item.decor.conceal) {
+ conceal = true;
+ if (item.start_row == state->row && item.start_col == col && item.decor.conceal_char) {
+ conceal_char = item.decor.conceal_char;
+ state->col_until = MIN(state->col_until, item.start_col);
+ conceal_attr = item.attr_id;
+ }
+ }
if ((item.start_row == state->row && item.start_col <= col)
- && kv_size(item.decor.virt_text)
+ && decor_virt_pos(item.decor)
&& item.decor.virt_text_pos == kVTOverlay && item.win_col == -1) {
item.win_col = (item.decor.virt_text_hide && hidden) ? -2 : win_col;
}
@@ -347,9 +352,150 @@ next_mark:
}
kv_size(state->active) = j;
state->current = attr;
+ state->conceal = conceal;
+ state->conceal_char = conceal_char;
+ state->conceal_attr = conceal_attr;
return attr;
}
+void decor_redraw_signs(buf_T *buf, int row, int *num_signs, sign_attrs_T sattrs[])
+{
+ if (!buf->b_signs) {
+ return;
+ }
+
+ MarkTreeIter itr[1] = { 0 };
+ marktree_itr_get(buf->b_marktree, row, 0, itr);
+
+ while (true) {
+ mtkey_t mark = marktree_itr_current(itr);
+ if (mark.pos.row < 0 || mark.pos.row > row) {
+ break;
+ }
+
+ if (mt_end(mark) || marktree_decor_level(mark) < kDecorLevelVisible) {
+ goto next_mark;
+ }
+
+ Decoration *decor = mark.decor_full;
+
+ if (!decor || !decor_has_sign(decor)) {
+ goto next_mark;
+ }
+
+ int j;
+ for (j = (*num_signs); j > 0; j--) {
+ if (sattrs[j].sat_prio <= decor->priority) {
+ break;
+ }
+ sattrs[j] = sattrs[j - 1];
+ }
+ if (j < SIGN_SHOW_MAX) {
+ memset(&sattrs[j], 0, sizeof(sign_attrs_T));
+ sattrs[j].sat_text = decor->sign_text;
+ if (decor->sign_hl_id != 0) {
+ sattrs[j].sat_texthl = syn_id2attr(decor->sign_hl_id);
+ }
+ if (decor->number_hl_id != 0) {
+ sattrs[j].sat_numhl = syn_id2attr(decor->number_hl_id);
+ }
+ if (decor->line_hl_id != 0) {
+ sattrs[j].sat_linehl = syn_id2attr(decor->line_hl_id);
+ }
+ if (decor->cursorline_hl_id != 0) {
+ sattrs[j].sat_culhl = syn_id2attr(decor->cursorline_hl_id);
+ }
+ sattrs[j].sat_prio = decor->priority;
+ (*num_signs)++;
+ }
+
+next_mark:
+ marktree_itr_next(buf->b_marktree, itr);
+ }
+}
+
+// Get the maximum required amount of sign columns needed between row and
+// end_row.
+int decor_signcols(buf_T *buf, DecorState *state, int row, int end_row, int max)
+{
+ int count = 0; // count for the number of signs on a given row
+ int count_remove = 0; // how much to decrement count by when iterating marks for a new row
+ int signcols = 0; // highest value of count
+ int currow = -1; // current row
+
+ if (max <= 1 && buf->b_signs >= (size_t)max) {
+ return max;
+ }
+
+ if (buf->b_signs == 0) {
+ return 0;
+ }
+
+ MarkTreeIter itr[1] = { 0 };
+ marktree_itr_get(buf->b_marktree, 0, -1, itr);
+ while (true) {
+ mtkey_t mark = marktree_itr_current(itr);
+ if (mark.pos.row < 0 || mark.pos.row > end_row) {
+ break;
+ }
+
+ if ((mark.pos.row < row && mt_end(mark))
+ || marktree_decor_level(mark) < kDecorLevelVisible
+ || !mark.decor_full) {
+ goto next_mark;
+ }
+
+ Decoration decor = get_decor(mark);
+
+ if (!decor.sign_text) {
+ goto next_mark;
+ }
+
+ if (mark.pos.row > currow) {
+ count -= count_remove;
+ count_remove = 0;
+ currow = mark.pos.row;
+ }
+
+ if (!mt_paired(mark)) {
+ if (mark.pos.row >= row) {
+ count++;
+ if (count > signcols) {
+ signcols = count;
+ if (signcols >= max) {
+ return max;
+ }
+ }
+ count_remove++;
+ }
+ goto next_mark;
+ }
+
+ mtpos_t altpos = marktree_get_altpos(buf->b_marktree, mark, NULL);
+
+ if (mt_end(mark)) {
+ if (mark.pos.row >= row && altpos.row <= end_row) {
+ count_remove++;
+ }
+ } else {
+ if (altpos.row >= row) {
+ count++;
+ if (count > signcols) {
+ signcols = count;
+ if (signcols >= max) {
+ return max;
+ }
+ }
+ }
+ }
+
+next_mark:
+ marktree_itr_next(buf->b_marktree, itr);
+ }
+
+ return signcols;
+}
+
void decor_redraw_end(DecorState *state)
{
state->buf = NULL;
@@ -362,7 +508,7 @@ bool decor_redraw_eol(buf_T *buf, DecorState *state, int *eol_attr, int eol_col)
bool has_virttext = false;
for (size_t i = 0; i < kv_size(state->active); i++) {
DecorRange item = kv_A(state->active, i);
- if (item.start_row == state->row && kv_size(item.decor.virt_text)) {
+ if (item.start_row == state->row && decor_virt_pos(item.decor)) {
has_virttext = true;
}
@@ -373,70 +519,16 @@ bool decor_redraw_eol(buf_T *buf, DecorState *state, int *eol_attr, int eol_col)
return has_virttext;
}
-void decor_add_ephemeral(int start_row, int start_col, int end_row, int end_col, Decoration *decor)
+void decor_add_ephemeral(int start_row, int start_col, int end_row, int end_col, Decoration *decor,
+ uint64_t ns_id, uint64_t mark_id)
{
if (end_row == -1) {
end_row = start_row;
end_col = start_col;
}
- decor_add(&decor_state, start_row, start_col, end_row, end_col, decor, true);
+ decor_add(&decor_state, start_row, start_col, end_row, end_col, decor, true, ns_id, mark_id);
}
-
-DecorProvider *get_decor_provider(NS ns_id, bool force)
-{
- size_t i;
- size_t len = kv_size(decor_providers);
- for (i = 0; i < len; i++) {
- DecorProvider *item = &kv_A(decor_providers, i);
- if (item->ns_id == ns_id) {
- return item;
- } else if (item->ns_id > ns_id) {
- break;
- }
- }
-
- if (!force) {
- return NULL;
- }
-
- // Adding a new provider, so allocate room in the vector
- (void)kv_a(decor_providers, len);
- if (i < len) {
- // New ns_id needs to be inserted between existing providers to maintain
- // ordering, so shift other providers with larger ns_id
- memmove(&kv_A(decor_providers, i + 1),
- &kv_A(decor_providers, i),
- (len - i) * sizeof(kv_a(decor_providers, i)));
- }
- DecorProvider *item = &kv_a(decor_providers, i);
- *item = DECORATION_PROVIDER_INIT(ns_id);
-
- return item;
-}
-
-void decor_provider_clear(DecorProvider *p)
-{
- if (p == NULL) {
- return;
- }
- NLUA_CLEAR_REF(p->redraw_start);
- NLUA_CLEAR_REF(p->redraw_buf);
- NLUA_CLEAR_REF(p->redraw_win);
- NLUA_CLEAR_REF(p->redraw_line);
- NLUA_CLEAR_REF(p->redraw_end);
- p->active = false;
-}
-
-void decor_free_all_mem(void)
-{
- for (size_t i = 0; i < kv_size(decor_providers); i++) {
- decor_provider_clear(&kv_A(decor_providers, i));
- }
- kv_destroy(decor_providers);
-}
-
-
int decor_virt_lines(win_T *wp, linenr_T lnum, VirtLines *lines)
{
buf_T *buf = wp->w_buffer;
@@ -452,18 +544,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..8f28442d41 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,13 +26,13 @@ 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)
-
typedef kvec_t(struct virt_line { VirtText line; bool left_col; }) VirtLines;
-
struct Decoration {
VirtText virt_text;
VirtLines virt_lines;
@@ -42,15 +44,25 @@ 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
+ bool conceal;
+ // TODO(bfredl): style, etc
DecorPriority priority;
int col; // fixed col value, like win_col
int virt_text_width; // width of virt_text
+ char_u *sign_text;
+ int sign_hl_id;
+ int number_hl_id;
+ int line_hl_id;
+ int cursorline_hl_id;
+ // TODO(bfredl): in principle this should be a schar_T, but we
+ // probably want some kind of glyph cache for that..
+ int conceal_char;
+ bool ui_watched; // watched for win_extmark
};
-#define DECORATION_INIT { KV_INITIAL_VALUE, KV_INITIAL_VALUE, 0, kVTEndOfLine, kHlModeUnknown, \
- false, false, false, false, DECOR_PRIORITY_BASE, 0, 0 }
+#define DECORATION_INIT { KV_INITIAL_VALUE, KV_INITIAL_VALUE, 0, kVTEndOfLine, \
+ kHlModeUnknown, false, false, false, false, DECOR_PRIORITY_BASE, \
+ 0, 0, NULL, 0, 0, 0, 0, 0, false }
typedef struct {
int start_row;
@@ -61,6 +73,8 @@ typedef struct {
int attr_id; // cached lookup of decor.hl_id
bool virt_text_owned;
int win_col;
+ uint64_t ns_id;
+ uint64_t mark_id;
} DecorRange;
typedef struct {
@@ -72,28 +86,22 @@ typedef struct {
int col_until;
int current;
int eol_col;
+
+ bool conceal;
+ int conceal_char;
+ int conceal_attr;
} DecorState;
-typedef struct {
- NS ns_id;
- bool active;
- LuaRef redraw_start;
- LuaRef redraw_buf;
- LuaRef redraw_win;
- LuaRef redraw_line;
- LuaRef redraw_end;
- LuaRef hl_def;
- int hl_valid;
-} DecorProvider;
-
-EXTERN kvec_t(DecorProvider) decor_providers INIT(= KV_INITIAL_VALUE);
EXTERN DecorState decor_state INIT(= { 0 });
-EXTERN bool provider_active INIT(= false);
-#define DECORATION_PROVIDER_INIT(ns_id) (DecorProvider) \
- { ns_id, false, LUA_NOREF, LUA_NOREF, \
- LUA_NOREF, LUA_NOREF, LUA_NOREF, \
- LUA_NOREF, -1 }
+static inline bool decor_has_sign(Decoration *decor)
+{
+ return decor->sign_text
+ || decor->sign_hl_id
+ || decor->number_hl_id
+ || decor->line_hl_id
+ || decor->cursorline_hl_id;
+}
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "decoration.h.generated.h"
diff --git a/src/nvim/decoration_provider.c b/src/nvim/decoration_provider.c
new file mode 100644
index 0000000000..0f6a260247
--- /dev/null
+++ b/src/nvim/decoration_provider.c
@@ -0,0 +1,228 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+#include "nvim/api/extmark.h"
+#include "nvim/api/private/helpers.h"
+#include "nvim/buffer.h"
+#include "nvim/decoration.h"
+#include "nvim/decoration_provider.h"
+#include "nvim/highlight.h"
+#include "nvim/lua/executor.h"
+
+static kvec_t(DecorProvider) decor_providers = KV_INITIAL_VALUE;
+
+#define DECORATION_PROVIDER_INIT(ns_id) (DecorProvider) \
+ { ns_id, false, LUA_NOREF, LUA_NOREF, \
+ LUA_NOREF, LUA_NOREF, LUA_NOREF, \
+ LUA_NOREF, -1 }
+
+static bool decor_provider_invoke(NS ns_id, const char *name, LuaRef ref, Array args,
+ bool default_true, char **perr)
+{
+ Error err = ERROR_INIT;
+
+ textlock++;
+ provider_active = true;
+ Object ret = nlua_call_ref(ref, name, args, true, &err);
+ provider_active = false;
+ textlock--;
+
+ if (!ERROR_SET(&err)
+ && api_object_to_bool(ret, "provider %s retval", default_true, &err)) {
+ return true;
+ }
+
+ if (ERROR_SET(&err)) {
+ const char *ns_name = describe_ns(ns_id);
+ ELOG("error in provider %s:%s: %s", ns_name, name, err.msg);
+ bool verbose_errs = true; // TODO(bfredl):
+ if (verbose_errs && perr && *perr == NULL) {
+ static char errbuf[IOSIZE];
+ snprintf(errbuf, sizeof errbuf, "%s: %s", ns_name, err.msg);
+ *perr = xstrdup(errbuf);
+ }
+ }
+
+ api_free_object(ret);
+ return false;
+}
+
+/// For each provider invoke the 'start' callback
+///
+/// @param[out] providers Decoration providers
+/// @param[out] err Provider err
+void decor_providers_start(DecorProviders *providers, int type, char **err)
+{
+ kvi_init(*providers);
+
+ for (size_t i = 0; i < kv_size(decor_providers); i++) {
+ DecorProvider *p = &kv_A(decor_providers, i);
+ if (!p->active) {
+ continue;
+ }
+
+ bool active;
+ if (p->redraw_start != LUA_NOREF) {
+ FIXED_TEMP_ARRAY(args, 2);
+ args.items[0] = INTEGER_OBJ((int)display_tick);
+ args.items[1] = INTEGER_OBJ(type);
+ active = decor_provider_invoke(p->ns_id, "start", p->redraw_start, args, true, err);
+ } else {
+ active = true;
+ }
+
+ if (active) {
+ kvi_push(*providers, p);
+ }
+ }
+}
+
+/// For each provider run 'win'. If result is not false, then collect the
+/// 'on_line' callback to call inside win_line
+///
+/// @param wp Window
+/// @param providers Decoration providers
+/// @param[out] line_providers Enabled line providers to invoke in win_line
+/// @param[out] err Provider error
+void decor_providers_invoke_win(win_T *wp, DecorProviders *providers,
+ DecorProviders *line_providers, char **err)
+{
+ kvi_init(*line_providers);
+
+ linenr_T knownmax = ((wp->w_valid & VALID_BOTLINE)
+ ? wp->w_botline
+ : (wp->w_topline + wp->w_height_inner));
+
+ for (size_t k = 0; k < kv_size(*providers); k++) {
+ DecorProvider *p = kv_A(*providers, k);
+ if (p && p->redraw_win != LUA_NOREF) {
+ FIXED_TEMP_ARRAY(args, 4);
+ args.items[0] = WINDOW_OBJ(wp->handle);
+ args.items[1] = BUFFER_OBJ(wp->w_buffer->handle);
+ // TODO(bfredl): we are not using this, but should be first drawn line?
+ args.items[2] = INTEGER_OBJ(wp->w_topline - 1);
+ args.items[3] = INTEGER_OBJ(knownmax);
+ if (decor_provider_invoke(p->ns_id, "win", p->redraw_win, args, true, err)) {
+ kvi_push(*line_providers, p);
+ }
+ }
+ }
+
+ win_check_ns_hl(wp);
+}
+
+/// For each provider invoke the 'line' callback for a given window row.
+///
+/// @param wp Window
+/// @param providers Decoration providers
+/// @param row Row to invoke line callback for
+/// @param[out] has_decor Set when at least one provider invokes a line callback
+/// @param[out] err Provider error
+void providers_invoke_line(win_T *wp, DecorProviders *providers, int row, bool *has_decor,
+ char **err)
+{
+ for (size_t k = 0; k < kv_size(*providers); k++) {
+ DecorProvider *p = kv_A(*providers, k);
+ if (p && p->redraw_line != LUA_NOREF) {
+ FIXED_TEMP_ARRAY(args, 3);
+ args.items[0] = WINDOW_OBJ(wp->handle);
+ args.items[1] = BUFFER_OBJ(wp->w_buffer->handle);
+ args.items[2] = INTEGER_OBJ(row);
+ if (decor_provider_invoke(p->ns_id, "line", p->redraw_line, args, true, err)) {
+ *has_decor = true;
+ } else {
+ // return 'false' or error: skip rest of this window
+ kv_A(*providers, k) = NULL;
+ }
+
+ win_check_ns_hl(wp);
+ }
+ }
+}
+
+/// For each provider invoke the 'buf' callback for a given buffer.
+///
+/// @param buf Buffer
+/// @param providers Decoration providers
+/// @param[out] err Provider error
+void decor_providers_invoke_buf(buf_T *buf, DecorProviders *providers, char **err)
+{
+ for (size_t i = 0; i < kv_size(*providers); i++) {
+ DecorProvider *p = kv_A(*providers, i);
+ if (p && p->redraw_buf != LUA_NOREF) {
+ FIXED_TEMP_ARRAY(args, 1);
+ args.items[0] = BUFFER_OBJ(buf->handle);
+ decor_provider_invoke(p->ns_id, "buf", p->redraw_buf, args, true, err);
+ }
+ }
+}
+
+/// For each provider invoke the 'end' callback
+///
+/// @param providers Decoration providers
+/// @param displaytick Display tick
+/// @param[out] err Provider error
+void decor_providers_invoke_end(DecorProviders *providers, char **err)
+{
+ for (size_t i = 0; i < kv_size(*providers); i++) {
+ DecorProvider *p = kv_A(*providers, i);
+ if (p && p->active && p->redraw_end != LUA_NOREF) {
+ FIXED_TEMP_ARRAY(args, 1);
+ args.items[0] = INTEGER_OBJ((int)display_tick);
+ decor_provider_invoke(p->ns_id, "end", p->redraw_end, args, true, err);
+ }
+ }
+}
+
+DecorProvider *get_decor_provider(NS ns_id, bool force)
+{
+ size_t i;
+ size_t len = kv_size(decor_providers);
+ for (i = 0; i < len; i++) {
+ DecorProvider *item = &kv_A(decor_providers, i);
+ if (item->ns_id == ns_id) {
+ return item;
+ } else if (item->ns_id > ns_id) {
+ break;
+ }
+ }
+
+ if (!force) {
+ return NULL;
+ }
+
+ // Adding a new provider, so allocate room in the vector
+ (void)kv_a(decor_providers, len);
+ if (i < len) {
+ // New ns_id needs to be inserted between existing providers to maintain
+ // ordering, so shift other providers with larger ns_id
+ memmove(&kv_A(decor_providers, i + 1),
+ &kv_A(decor_providers, i),
+ (len - i) * sizeof(kv_a(decor_providers, i)));
+ }
+ DecorProvider *item = &kv_a(decor_providers, i);
+ *item = DECORATION_PROVIDER_INIT(ns_id);
+
+ return item;
+}
+
+void decor_provider_clear(DecorProvider *p)
+{
+ if (p == NULL) {
+ return;
+ }
+ NLUA_CLEAR_REF(p->redraw_start);
+ NLUA_CLEAR_REF(p->redraw_buf);
+ NLUA_CLEAR_REF(p->redraw_win);
+ NLUA_CLEAR_REF(p->redraw_line);
+ NLUA_CLEAR_REF(p->redraw_end);
+ p->active = false;
+}
+
+void decor_free_all_mem(void)
+{
+ for (size_t i = 0; i < kv_size(decor_providers); i++) {
+ decor_provider_clear(&kv_A(decor_providers, i));
+ }
+ kv_destroy(decor_providers);
+}
diff --git a/src/nvim/decoration_provider.h b/src/nvim/decoration_provider.h
new file mode 100644
index 0000000000..3ec7c80357
--- /dev/null
+++ b/src/nvim/decoration_provider.h
@@ -0,0 +1,26 @@
+#ifndef NVIM_DECORATION_PROVIDER_H
+#define NVIM_DECORATION_PROVIDER_H
+
+#include "nvim/buffer_defs.h"
+
+typedef struct {
+ NS ns_id;
+ bool active;
+ LuaRef redraw_start;
+ LuaRef redraw_buf;
+ LuaRef redraw_win;
+ LuaRef redraw_line;
+ LuaRef redraw_end;
+ LuaRef hl_def;
+ int hl_valid;
+} DecorProvider;
+
+typedef kvec_withinit_t(DecorProvider *, 4) DecorProviders;
+
+EXTERN bool provider_active INIT(= false);
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "decoration_provider.h.generated.h"
+#endif
+
+#endif // NVIM_DECORATION_PROVIDER_H
diff --git a/src/nvim/diff.c b/src/nvim/diff.c
index 340fec230c..75021e90d6 100644
--- a/src/nvim/diff.c
+++ b/src/nvim/diff.c
@@ -85,9 +85,9 @@ typedef struct {
// used for recording hunks from xdiff
typedef struct {
linenr_T lnum_orig;
- long count_orig;
+ long count_orig;
linenr_T lnum_new;
- long count_new;
+ long count_new;
} diffhunk_T;
// two diff inputs and one result
@@ -166,8 +166,7 @@ void diff_buf_add(buf_T *buf)
return;
}
- int i;
- for (i = 0; i < DB_COUNT; ++i) {
+ for (int i = 0; i < DB_COUNT; i++) {
if (curtab->tp_diffbuf[i] == NULL) {
curtab->tp_diffbuf[i] = buf;
curtab->tp_diff_invalid = true;
@@ -201,7 +200,7 @@ static void diff_buf_clear(void)
static int diff_buf_idx(buf_T *buf)
{
int idx;
- for (idx = 0; idx < DB_COUNT; ++idx) {
+ for (idx = 0; idx < DB_COUNT; idx++) {
if (curtab->tp_diffbuf[idx] == buf) {
break;
}
@@ -218,7 +217,7 @@ static int diff_buf_idx(buf_T *buf)
static int diff_buf_idx_tp(buf_T *buf, tabpage_T *tp)
{
int idx;
- for (idx = 0; idx < DB_COUNT; ++idx) {
+ for (idx = 0; idx < DB_COUNT; idx++) {
if (tp->tp_diffbuf[idx] == buf) {
break;
}
@@ -249,7 +248,7 @@ void diff_invalidate(buf_T *buf)
/// @param line2
/// @param amount
/// @param amount_after
-void diff_mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after)
+void diff_mark_adjust(linenr_T line1, linenr_T line2, linenr_T amount, linenr_T amount_after)
{
// Handle all tab pages that use the current buffer in a diff.
FOR_ALL_TABS(tp) {
@@ -273,8 +272,8 @@ void diff_mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_a
/// @param line2
/// @param amount
/// @amount_after
-static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T line2, long amount,
- long amount_after)
+static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T line2,
+ linenr_T amount, linenr_T amount_after)
{
if (diff_internal()) {
// Will update diffs before redrawing. Set _invalid to update the
@@ -285,8 +284,8 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T
tp->tp_diff_update = true;
}
- int inserted;
- int deleted;
+ linenr_T inserted;
+ linenr_T deleted;
if (line2 == MAXLNUM) {
// mark_adjust(99, MAXLNUM, 9, 0): insert lines
inserted = amount;
@@ -304,10 +303,9 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T
diff_T *dprev = NULL;
diff_T *dp = tp->tp_first_diff;
- linenr_T last;
linenr_T lnum_deleted = line1; // lnum of remaining deletion
- int n;
- int off;
+ linenr_T n;
+ linenr_T off;
for (;;) {
// If the change is after the previous diff block and before the next
// diff block, thus not touching an existing change, create a new diff
@@ -323,7 +321,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T
dnext->df_lnum[idx] = line1;
dnext->df_count[idx] = inserted;
int i;
- for (i = 0; i < DB_COUNT; ++i) {
+ for (i = 0; i < DB_COUNT; i++) {
if ((tp->tp_diffbuf[i] != NULL) && (i != idx)) {
if (dprev == NULL) {
dnext->df_lnum[i] = line1;
@@ -354,7 +352,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T
// 3 5 6
// compute last line of this change
- last = dp->df_lnum[idx] + dp->df_count[idx] - 1;
+ linenr_T last = dp->df_lnum[idx] + dp->df_count[idx] - 1;
// 1. change completely above line1: nothing to do
if (last >= line1 - 1) {
@@ -421,7 +419,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T
}
int i;
- for (i = 0; i < DB_COUNT; ++i) {
+ for (i = 0; i < DB_COUNT; i++) {
if ((tp->tp_diffbuf[i] != NULL) && (i != idx)) {
dp->df_lnum[i] -= off;
dp->df_count[i] += n;
@@ -442,7 +440,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T
// Check if inserted lines are equal, may reduce the size of the
// diff.
//
- // TODO: also check for equal lines in the middle and perhaps split
+ // TODO(unknown): also check for equal lines in the middle and perhaps split
// the block.
diff_check_unchanged(tp, dp);
}
@@ -453,7 +451,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T
if ((dprev != NULL)
&& (dprev->df_lnum[idx] + dprev->df_count[idx] == dp->df_lnum[idx])) {
int i;
- for (i = 0; i < DB_COUNT; ++i) {
+ for (i = 0; i < DB_COUNT; i++) {
if (tp->tp_diffbuf[i] != NULL) {
dprev->df_count[i] += dp->df_count[i];
}
@@ -474,7 +472,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T
while (dp != NULL) {
// All counts are zero, remove this entry.
int i;
- for (i = 0; i < DB_COUNT; ++i) {
+ for (i = 0; i < DB_COUNT; i++) {
if ((tp->tp_diffbuf[i] != NULL) && (dp->df_count[i] != 0)) {
break;
}
@@ -542,7 +540,7 @@ static void diff_check_unchanged(tabpage_T *tp, diff_T *dp)
// Find the first buffers, use it as the original, compare the other
// buffer lines against this one.
int i_org;
- for (i_org = 0; i_org < DB_COUNT; ++i_org) {
+ for (i_org = 0; i_org < DB_COUNT; i_org++) {
if (tp->tp_diffbuf[i_org] != NULL) {
break;
}
@@ -558,8 +556,8 @@ static void diff_check_unchanged(tabpage_T *tp, diff_T *dp)
}
// First check lines at the top, then at the bottom.
- int off_org = 0;
- int off_new = 0;
+ linenr_T off_org = 0;
+ linenr_T off_new = 0;
int dir = FORWARD;
for (;;) {
// Repeat until a line is found which is different or the number of
@@ -574,7 +572,7 @@ static void diff_check_unchanged(tabpage_T *tp, diff_T *dp)
false));
int i_new;
- for (i_new = i_org + 1; i_new < DB_COUNT; ++i_new) {
+ for (i_new = i_org + 1; i_new < DB_COUNT; i_new++) {
if (tp->tp_diffbuf[i_new] == NULL) {
continue;
}
@@ -602,7 +600,7 @@ static void diff_check_unchanged(tabpage_T *tp, diff_T *dp)
}
// Line matched in all buffers, remove it from the diff.
- for (i_new = i_org; i_new < DB_COUNT; ++i_new) {
+ for (i_new = i_org; i_new < DB_COUNT; i_new++) {
if (tp->tp_diffbuf[i_new] != NULL) {
if (dir == FORWARD) {
dp->df_lnum[i_new]++;
@@ -628,8 +626,7 @@ static void diff_check_unchanged(tabpage_T *tp, diff_T *dp)
/// @return OK if the diff block doesn't contain invalid line numbers.
static int diff_check_sanity(tabpage_T *tp, diff_T *dp)
{
- int i;
- for (i = 0; i < DB_COUNT; ++i) {
+ for (int i = 0; i < DB_COUNT; i++) {
if (tp->tp_diffbuf[i] != NULL) {
if (dp->df_lnum[i] + dp->df_count[i] - 1
> tp->tp_diffbuf[i]->b_ml.ml_line_count) {
@@ -719,16 +716,13 @@ static void clear_diffout(diffout_T *dout)
/// @return FAIL for failure.
static int diff_write_buffer(buf_T *buf, diffin_T *din)
{
- linenr_T lnum;
- char_u *s;
long len = 0;
- char_u *ptr;
// xdiff requires one big block of memory with all the text.
- for (lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) {
+ for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) {
len += (long)STRLEN(ml_get_buf(buf, lnum, false)) + 1;
}
- ptr = try_malloc(len);
+ char_u *ptr = try_malloc((size_t)len);
if (ptr == NULL) {
// Allocating memory failed. This can happen, because we try to read
// the whole buffer text into memory. Set the failed flag, the diff
@@ -746,26 +740,32 @@ static int diff_write_buffer(buf_T *buf, diffin_T *din)
din->din_mmfile.size = len;
len = 0;
- for (lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) {
- for (s = ml_get_buf(buf, lnum, false); *s != NUL;) {
+ for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) {
+ for (char_u *s = ml_get_buf(buf, lnum, false); *s != NUL;) {
if (diff_flags & DIFF_ICASE) {
- char_u cbuf[MB_MAXBYTES + 1];
+ int c;
+ char cbuf[MB_MAXBYTES + 1];
- // xdiff doesn't support ignoring case, fold-case the text.
- int c = utf_ptr2char(s);
- c = utf_fold(c);
- const int orig_len = utfc_ptr2len(s);
- if (utf_char2bytes(c, cbuf) != orig_len) {
+ if (*s == NL) {
+ c = NUL;
+ } else {
+ // xdiff doesn't support ignoring case, fold-case the text.
+ c = utf_ptr2char((char *)s);
+ c = utf_fold(c);
+ }
+ const int orig_len = utfc_ptr2len((char *)s);
+ if (utf_char2bytes(c, (char *)cbuf) != orig_len) {
// TODO(Bram): handle byte length difference
- memmove(ptr + len, s, orig_len);
+ memmove(ptr + len, s, (size_t)orig_len);
} else {
- memmove(ptr + len, cbuf, orig_len);
+ memmove(ptr + len, cbuf, (size_t)orig_len);
}
s += orig_len;
len += orig_len;
} else {
- ptr[len++] = *s++;
+ ptr[len++] = *s == NL ? NUL : *s;
+ s++;
}
}
ptr[len++] = NL;
@@ -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);
- int r = buf_write(buf, din->din_fname, NULL,
+ const bool save_cmod_flags = cmdmod.cmod_flags;
+ // Writing the buffer is an implementation detail of performing the diff,
+ // so it shouldn't update the '[ and '] marks.
+ cmdmod.cmod_flags |= CMOD_LOCKMARKS;
+ int r = buf_write(buf, (char *)din->din_fname, NULL,
(linenr_T)1, buf->b_ml.ml_line_count,
NULL, false, false, false, true);
+ cmdmod.cmod_flags = save_cmod_flags;
free_string_option(buf->b_p_ff);
buf->b_p_ff = save_ff;
return r;
@@ -806,9 +811,6 @@ static int diff_write(buf_T *buf, diffin_T *din)
/// @param eap can be NULL
static void diff_try_update(diffio_T *dio, int idx_orig, exarg_T *eap)
{
- buf_T *buf;
- int idx_new;
-
if (dio->dio_internal) {
ga_init(&dio->dio_diff.dout_ga, sizeof(char *), 1000);
} else {
@@ -828,9 +830,11 @@ static void diff_try_update(diffio_T *dio, int idx_orig, exarg_T *eap)
goto theend;
}
+ buf_T *buf;
+
// :diffupdate!
if (eap != NULL && eap->forceit) {
- for (idx_new = idx_orig; idx_new < DB_COUNT; idx_new++) {
+ for (int idx_new = idx_orig; idx_new < DB_COUNT; idx_new++) {
buf = curtab->tp_diffbuf[idx_new];
if (buf_valid(buf)) {
buf_check_timestamp(buf);
@@ -845,7 +849,7 @@ static void diff_try_update(diffio_T *dio, int idx_orig, exarg_T *eap)
}
// Make a difference between the first buffer and every other.
- for (idx_new = idx_orig + 1; idx_new < DB_COUNT; idx_new++) {
+ for (int idx_new = idx_orig + 1; idx_new < DB_COUNT; idx_new++) {
buf = curtab->tp_diffbuf[idx_new];
if (buf == NULL || buf->b_ml.ml_mfp == NULL) {
continue; // skip buffer that isn't loaded
@@ -879,6 +883,7 @@ theend:
/// diff will be used anyway.
///
int diff_internal(void)
+ FUNC_ATTR_PURE
{
return (diff_flags & DIFF_INTERNAL) != 0 && *p_dex == NUL;
}
@@ -888,10 +893,8 @@ int diff_internal(void)
///
static int diff_internal_failed(void)
{
- int idx;
-
// Only need to do something when there is another buffer.
- for (idx = 0; idx < DB_COUNT; idx++) {
+ for (int idx = 0; idx < DB_COUNT; idx++) {
if (curtab->tp_diffbuf[idx] != NULL
&& curtab->tp_diffbuf[idx]->b_diff_failed) {
return true;
@@ -922,7 +925,7 @@ void ex_diffupdate(exarg_T *eap)
// Use the first buffer as the original text.
int idx_orig;
- for (idx_orig = 0; idx_orig < DB_COUNT; ++idx_orig) {
+ for (idx_orig = 0; idx_orig < DB_COUNT; idx_orig++) {
if (curtab->tp_diffbuf[idx_orig] != NULL) {
break;
}
@@ -934,7 +937,7 @@ void ex_diffupdate(exarg_T *eap)
// Only need to do something when there is another buffer.
int idx_new;
- for (idx_new = idx_orig + 1; idx_new < DB_COUNT; ++idx_new) {
+ for (idx_new = idx_orig + 1; idx_new < DB_COUNT; idx_new++) {
if (curtab->tp_diffbuf[idx_new] != NULL) {
break;
}
@@ -1069,7 +1072,7 @@ static int diff_file_internal(diffio_T *diffio)
memset(&emit_cfg, 0, sizeof(emit_cfg));
memset(&emit_cb, 0, sizeof(emit_cb));
- param.flags = diff_algorithm;
+ param.flags = (unsigned long)diff_algorithm;
if (diff_flags & DIFF_IWHITE) {
param.flags |= XDF_IGNORE_WHITESPACE_CHANGE;
@@ -1157,7 +1160,7 @@ void ex_diffpatch(exarg_T *eap)
{
char_u *buf = NULL;
win_T *old_curwin = curwin;
- char_u *newname = NULL; // name of patched file buffer
+ char *newname = NULL; // name of patched file buffer
char_u *esc_name = NULL;
#ifdef UNIX
@@ -1175,7 +1178,7 @@ void ex_diffpatch(exarg_T *eap)
}
// Write the current buffer to "tmp_orig".
- if (buf_write(curbuf, tmp_orig, NULL,
+ if (buf_write(curbuf, (char *)tmp_orig, NULL,
(linenr_T)1, curbuf->b_ml.ml_line_count,
NULL, false, false, false, true) == FAIL) {
goto theend;
@@ -1183,9 +1186,9 @@ void ex_diffpatch(exarg_T *eap)
#ifdef UNIX
// Get the absolute path of the patchfile, changing directory below.
- fullname = FullName_save((char *)eap->arg, false);
+ fullname = FullName_save(eap->arg, false);
esc_name =
- vim_strsave_shellescape((fullname != NULL ? (char_u *)fullname : eap->arg), true, true);
+ vim_strsave_shellescape((char_u *)(fullname != NULL ? fullname : eap->arg), true, true);
#else
esc_name = vim_strsave_shellescape(eap->arg, true, true);
#endif
@@ -1203,7 +1206,7 @@ void ex_diffpatch(exarg_T *eap)
|| (os_chdir((char *)dirbuf) != 0)) {
dirbuf[0] = NUL;
} else {
- char *tempdir = (char *)vim_gettempdir();
+ char *tempdir = vim_gettempdir();
if (tempdir == NULL) {
tempdir = "/tmp";
}
@@ -1216,7 +1219,7 @@ void ex_diffpatch(exarg_T *eap)
// Use 'patchexpr' to generate the new file.
#ifdef UNIX
eval_patch((char *)tmp_orig,
- (fullname != NULL ? fullname : (char *)eap->arg),
+ (fullname != NULL ? fullname : eap->arg),
(char *)tmp_new);
#else
eval_patch((char *)tmp_orig, (char *)eap->arg, (char *)tmp_new);
@@ -1255,17 +1258,17 @@ void ex_diffpatch(exarg_T *eap)
emsg(_("E816: Cannot read patch output"));
} else {
if (curbuf->b_fname != NULL) {
- newname = vim_strnsave(curbuf->b_fname, STRLEN(curbuf->b_fname) + 4);
+ newname = xstrnsave(curbuf->b_fname, STRLEN(curbuf->b_fname) + 4);
STRCAT(newname, ".new");
}
// don't use a new tab page, each tab page has its own diffs
- cmdmod.tab = 0;
+ cmdmod.cmod_tab = 0;
if (win_split(0, (diff_flags & DIFF_VERTICAL) ? WSP_VERT : 0) != FAIL) {
// Pretend it was a ":split fname" command
eap->cmdidx = CMD_split;
- eap->arg = tmp_new;
+ eap->arg = (char *)tmp_new;
do_exedit(eap, old_curwin);
// check that split worked and editing tmp_new
@@ -1280,7 +1283,7 @@ void ex_diffpatch(exarg_T *eap)
ex_file(eap);
// Do filetype detection with the new name.
- if (au_has_group((char_u *)"filetypedetect")) {
+ if (augroup_exists("filetypedetect")) {
do_cmdline_cmd(":doau filetypedetect BufRead");
}
}
@@ -1320,7 +1323,7 @@ void ex_diffsplit(exarg_T *eap)
set_fraction(curwin);
// don't use a new tab page, each tab page has its own diffs
- cmdmod.tab = 0;
+ cmdmod.cmod_tab = 0;
if (win_split(0, (diff_flags & DIFF_VERTICAL) ? WSP_VERT : 0) != FAIL) {
// Pretend it was a ":split fname" command
@@ -1368,7 +1371,6 @@ static void set_diff_option(win_T *wp, int value)
curbuf = curwin->w_buffer;
}
-
/// Set options in window "wp" for diff mode.
///
/// @param addbuf Add buffer to diff.
@@ -1406,8 +1408,7 @@ void diff_win_options(win_T *wp, int addbuf)
}
wp->w_p_fdm_save = vim_strsave(wp->w_p_fdm);
}
- set_string_option_direct("fdm", -1, (char_u *)"diff",
- OPT_LOCAL | OPT_FREE, 0);
+ set_string_option_direct("fdm", -1, "diff", OPT_LOCAL | OPT_FREE, 0);
curwin = old_curwin;
curbuf = curwin->w_buffer;
@@ -1537,10 +1538,10 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio)
diffout_T *dout = &dio->dio_diff;
char_u linebuf[LBUFLEN]; // only need to hold the diff line
char_u *line;
- long off;
+ linenr_T off;
int i;
int notset = true; // block "*dp" not set yet
- diffhunk_T *hunk;
+ diffhunk_T *hunk = NULL; // init to avoid gcc warning
enum {
DIFF_ED,
DIFF_UNIFIED,
@@ -1654,20 +1655,20 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio)
off = dp->df_lnum[idx_orig] - hunk->lnum_orig;
if (off > 0) {
- for (i = idx_orig; i < idx_new; ++i) {
+ for (i = idx_orig; i < idx_new; i++) {
if (curtab->tp_diffbuf[i] != NULL) {
dp->df_lnum[i] -= off;
}
}
dp->df_lnum[idx_new] = hunk->lnum_new;
- dp->df_count[idx_new] = hunk->count_new;
+ dp->df_count[idx_new] = (linenr_T)hunk->count_new;
} else if (notset) {
// new block inside existing one, adjust new block
dp->df_lnum[idx_new] = hunk->lnum_new + off;
- dp->df_count[idx_new] = hunk->count_new - off;
+ dp->df_count[idx_new] = (linenr_T)hunk->count_new - off;
} else {
// second overlap of new block with existing block
- dp->df_count[idx_new] += hunk->count_new - hunk->count_orig
+ dp->df_count[idx_new] += (linenr_T)hunk->count_new - (linenr_T)hunk->count_orig
+ dpl->df_lnum[idx_orig] +
dpl->df_count[idx_orig]
- (dp->df_lnum[idx_orig] +
@@ -1676,7 +1677,7 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio)
// Adjust the size of the block to include all the lines to the
// end of the existing block or the new diff, whatever ends last.
- off = (hunk->lnum_orig + hunk->count_orig)
+ off = (hunk->lnum_orig + (linenr_T)hunk->count_orig)
- (dpl->df_lnum[idx_orig] + dpl->df_count[idx_orig]);
if (off < 0) {
@@ -1688,7 +1689,7 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio)
off = 0;
}
- for (i = idx_orig; i < idx_new; ++i) {
+ for (i = idx_orig; i < idx_new; i++) {
if (curtab->tp_diffbuf[i] != NULL) {
dp->df_count[i] = dpl->df_lnum[i] + dpl->df_count[i]
- dp->df_lnum[i] + off;
@@ -1709,14 +1710,14 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio)
dp = diff_alloc_new(curtab, dprev, dp);
dp->df_lnum[idx_orig] = hunk->lnum_orig;
- dp->df_count[idx_orig] = hunk->count_orig;
+ dp->df_count[idx_orig] = (linenr_T)hunk->count_orig;
dp->df_lnum[idx_new] = hunk->lnum_new;
- dp->df_count[idx_new] = hunk->count_new;
+ dp->df_count[idx_new] = (linenr_T)hunk->count_new;
// Set values for other buffers, these must be equal to the
// original buffer, otherwise there would have been a change
// already.
- for (i = idx_orig + 1; i < idx_new; ++i) {
+ for (i = idx_orig + 1; i < idx_new; i++) {
if (curtab->tp_diffbuf[i] != NULL) {
diff_copy_entry(dprev, dp, idx_orig, i);
}
@@ -1752,7 +1753,7 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio)
/// @param idx_new
static void diff_copy_entry(diff_T *dprev, diff_T *dp, int idx_orig, int idx_new)
{
- long off;
+ linenr_T off;
if (dprev == NULL) {
off = 0;
@@ -1770,9 +1771,8 @@ static void diff_copy_entry(diff_T *dprev, diff_T *dp, int idx_orig, int idx_new
void diff_clear(tabpage_T *tp)
FUNC_ATTR_NONNULL_ALL
{
- diff_T *p;
diff_T *next_p;
- for (p = tp->tp_first_diff; p != NULL; p = next_p) {
+ for (diff_T *p = tp->tp_first_diff; p != NULL; p = next_p) {
next_p = p->df_next;
xfree(p);
}
@@ -1794,12 +1794,8 @@ void diff_clear(tabpage_T *tp)
/// @return diff status.
int diff_check(win_T *wp, linenr_T lnum)
{
- int idx; // index in tp_diffbuf[] for this buffer
diff_T *dp;
- int maxcount;
- int i;
buf_T *buf = wp->w_buffer;
- int cmp;
if (curtab->tp_diff_invalid) {
// update after a big change
@@ -1816,7 +1812,7 @@ int diff_check(win_T *wp, linenr_T lnum)
return 0;
}
- idx = diff_buf_idx(buf);
+ int idx = diff_buf_idx(buf); // index in tp_diffbuf[] for this buffer
if (idx == DB_COUNT) {
// no diffs for buffer "buf"
@@ -1845,9 +1841,9 @@ int diff_check(win_T *wp, linenr_T lnum)
// Changed or inserted line. If the other buffers have a count of
// zero, the lines were inserted. If the other buffers have the same
// count, check if the lines are identical.
- cmp = false;
+ int cmp = false;
- for (i = 0; i < DB_COUNT; ++i) {
+ for (int i = 0; i < DB_COUNT; i++) {
if ((i != idx) && (curtab->tp_diffbuf[i] != NULL)) {
if (dp->df_count[i] == 0) {
zero = true;
@@ -1864,7 +1860,7 @@ int diff_check(win_T *wp, linenr_T lnum)
if (cmp) {
// Compare all lines. If they are equal the lines were inserted
// in some buffers, deleted in others, but not changed.
- for (i = 0; i < DB_COUNT; ++i) {
+ for (int i = 0; i < DB_COUNT; i++) {
if ((i != idx)
&& (curtab->tp_diffbuf[i] != NULL)
&& (dp->df_count[i] != 0)) {
@@ -1893,8 +1889,8 @@ int diff_check(win_T *wp, linenr_T lnum)
// Insert filler lines above the line just below the change. Will return
// 0 when this buf had the max count.
- maxcount = 0;
- for (i = 0; i < DB_COUNT; ++i) {
+ linenr_T maxcount = 0;
+ for (int i = 0; i < DB_COUNT; i++) {
if ((curtab->tp_diffbuf[i] != NULL) && (dp->df_count[i] > maxcount)) {
maxcount = dp->df_count[i];
}
@@ -1939,15 +1935,15 @@ static bool diff_equal_entry(diff_T *dp, int idx1, int idx2)
// ignoring case) return true and set "len" to the number of bytes.
static bool diff_equal_char(const char_u *const p1, const char_u *const p2, int *const len)
{
- const int l = utfc_ptr2len(p1);
+ const int l = utfc_ptr2len((char *)p1);
- if (l != utfc_ptr2len(p2)) {
+ if (l != utfc_ptr2len((char *)p2)) {
return false;
}
if (l > 1) {
if (STRNCMP(p1, p2, l) != 0
&& (!(diff_flags & DIFF_ICASE)
- || utf_fold(utf_ptr2char(p1)) != utf_fold(utf_ptr2char(p2)))) {
+ || utf_fold(utf_ptr2char((char *)p1)) != utf_fold(utf_ptr2char((char *)p2)))) {
return false;
}
*len = l;
@@ -1972,7 +1968,7 @@ static bool diff_equal_char(const char_u *const p1, const char_u *const p2, int
static int diff_cmp(char_u *s1, char_u *s2)
{
if ((diff_flags & DIFF_IBLANK)
- && (*skipwhite(s1) == NUL || *skipwhite(s2) == NUL)) {
+ && (*(char_u *)skipwhite((char *)s1) == NUL || *skipwhite((char *)s2) == NUL)) {
return 0;
}
@@ -1984,8 +1980,8 @@ static int diff_cmp(char_u *s1, char_u *s2)
return mb_stricmp((const char *)s1, (const char *)s2);
}
- char_u *p1 = s1;
- char_u *p2 = s2;
+ char *p1 = (char *)s1;
+ char *p2 = (char *)s2;
// Ignore white space changes and possibly ignore case.
while (*p1 != NUL && *p2 != NUL) {
@@ -1997,7 +1993,7 @@ static int diff_cmp(char_u *s1, char_u *s2)
p2 = skipwhite(p2);
} else {
int l;
- if (!diff_equal_char(p1, p2, &l)) {
+ if (!diff_equal_char((char_u *)p1, (char_u *)p2, &l)) {
break;
}
p1 += l;
@@ -2025,8 +2021,6 @@ void diff_set_topline(win_T *fromwin, win_T *towin)
buf_T *frombuf = fromwin->w_buffer;
linenr_T lnum = fromwin->w_topline;
diff_T *dp;
- int max_count;
- int i;
int fromidx = diff_buf_idx(frombuf);
if (fromidx == DB_COUNT) {
@@ -2040,7 +2034,6 @@ void diff_set_topline(win_T *fromwin, win_T *towin)
}
towin->w_topfill = 0;
-
// search for a change that includes "lnum" in the list of diffblocks.
for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) {
if (lnum <= dp->df_lnum[fromidx] + dp->df_count[fromidx]) {
@@ -2066,9 +2059,9 @@ void diff_set_topline(win_T *fromwin, win_T *towin)
if (lnum >= dp->df_lnum[fromidx]) {
// Inside a change: compute filler lines. With three or more
// buffers we need to know the largest count.
- max_count = 0;
+ linenr_T max_count = 0;
- for (i = 0; i < DB_COUNT; ++i) {
+ for (int i = 0; i < DB_COUNT; i++) {
if ((curtab->tp_diffbuf[i] != NULL) && (max_count < dp->df_count[i])) {
max_count = dp->df_count[i];
}
@@ -2150,7 +2143,7 @@ int diffopt_changed(void)
diff_flags_new |= DIFF_FILLER;
} else if ((STRNCMP(p, "context:", 8) == 0) && ascii_isdigit(p[8])) {
p += 8;
- diff_context_new = getdigits_int(&p, false, diff_context_new);
+ diff_context_new = getdigits_int((char **)&p, false, diff_context_new);
} else if (STRNCMP(p, "iblank", 6) == 0) {
p += 6;
diff_flags_new |= DIFF_IBLANK;
@@ -2174,7 +2167,7 @@ int diffopt_changed(void)
diff_flags_new |= DIFF_VERTICAL;
} else if ((STRNCMP(p, "foldcolumn:", 11) == 0) && ascii_isdigit(p[11])) {
p += 11;
- diff_foldcolumn_new = getdigits_int(&p, false, diff_foldcolumn_new);
+ diff_foldcolumn_new = getdigits_int((char **)&p, false, diff_foldcolumn_new);
} else if (STRNCMP(p, "hiddenoff", 9) == 0) {
p += 9;
diff_flags_new |= DIFF_HIDDEN_OFF;
@@ -2214,7 +2207,7 @@ int diffopt_changed(void)
}
if (*p == ',') {
- ++p;
+ p++;
}
}
@@ -2255,6 +2248,7 @@ bool diffopt_horizontal(void)
// Return true if 'diffopt' contains "hiddenoff".
bool diffopt_hiddenoff(void)
+ FUNC_ATTR_PURE
{
return (diff_flags & DIFF_HIDDEN_OFF) != 0;
}
@@ -2312,12 +2306,13 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
if ((dp == NULL) || (diff_check_sanity(curtab, dp) == FAIL)) {
xfree(line_org);
+
return false;
}
- int off = lnum - dp->df_lnum[idx];
+ linenr_T off = lnum - dp->df_lnum[idx];
int i;
- for (i = 0; i < DB_COUNT; ++i) {
+ for (i = 0; i < DB_COUNT; i++) {
if ((curtab->tp_diffbuf[i] != NULL) && (i != idx)) {
// Skip lines that are not in the other change (filler lines).
if (off >= dp->df_count[i]) {
@@ -2337,8 +2332,8 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
|| ((diff_flags & DIFF_IWHITEALL)
&& (ascii_iswhite(line_org[si_org])
|| ascii_iswhite(line_new[si_new])))) {
- si_org = (int)(skipwhite(line_org + si_org) - line_org);
- si_new = (int)(skipwhite(line_new + si_new) - line_new);
+ si_org = (int)((char_u *)skipwhite((char *)line_org + si_org) - line_org);
+ si_new = (int)((char_u *)skipwhite((char *)line_new + si_new) - line_new);
} else {
if (!diff_equal_char(line_org + si_org, line_new + si_new, &l)) {
break;
@@ -2416,7 +2411,6 @@ bool diff_infold(win_T *wp, linenr_T lnum)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1)
{
bool other = false;
- diff_T *dp;
// Return if 'diff' isn't set.
if (!wp->w_p_diff) {
@@ -2425,7 +2419,7 @@ bool diff_infold(win_T *wp, linenr_T lnum)
int idx = -1;
int i;
- for (i = 0; i < DB_COUNT; ++i) {
+ for (i = 0; i < DB_COUNT; i++) {
if (curtab->tp_diffbuf[i] == wp->w_buffer) {
idx = i;
} else if (curtab->tp_diffbuf[i] != NULL) {
@@ -2448,7 +2442,7 @@ bool diff_infold(win_T *wp, linenr_T lnum)
return true;
}
- for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) {
+ for (diff_T *dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) {
// If this change is below the line there can't be any further match.
if (dp->df_lnum[idx] - diff_context > lnum) {
break;
@@ -2473,10 +2467,10 @@ void nv_diffgetput(bool put, size_t count)
return;
}
if (count == 0) {
- ea.arg = (char_u *)"";
+ ea.arg = "";
} else {
- vim_snprintf(buf, 30, "%zu", count);
- ea.arg = (char_u *)buf;
+ vim_snprintf(buf, sizeof(buf), "%zu", count);
+ ea.arg = buf;
}
if (put) {
@@ -2497,18 +2491,18 @@ void nv_diffgetput(bool put, size_t count)
void ex_diffgetput(exarg_T *eap)
{
linenr_T lnum;
- int count;
+ linenr_T count;
linenr_T off = 0;
diff_T *dp;
- diff_T *dprev;
diff_T *dfree;
int i;
int added;
char_u *p;
aco_save_T aco;
buf_T *buf;
- int start_skip, end_skip;
- int new_count;
+ linenr_T start_skip;
+ linenr_T end_skip;
+ linenr_T new_count;
int buf_empty;
int found_not_ma = false;
int idx_other;
@@ -2524,7 +2518,7 @@ void ex_diffgetput(exarg_T *eap)
if (*eap->arg == NUL) {
// No argument: Find the other buffer in the list of diff buffers.
- for (idx_other = 0; idx_other < DB_COUNT; ++idx_other) {
+ for (idx_other = 0; idx_other < DB_COUNT; idx_other++) {
if ((curtab->tp_diffbuf[idx_other] != curbuf)
&& (curtab->tp_diffbuf[idx_other] != NULL)) {
if ((eap->cmdidx != CMD_diffput)
@@ -2545,7 +2539,7 @@ void ex_diffgetput(exarg_T *eap)
}
// Check that there isn't a third buffer in the list
- for (i = idx_other + 1; i < DB_COUNT; ++i) {
+ for (i = idx_other + 1; i < DB_COUNT; i++) {
if ((curtab->tp_diffbuf[i] != curbuf)
&& (curtab->tp_diffbuf[i] != NULL)
&& ((eap->cmdidx != CMD_diffput)
@@ -2557,19 +2551,18 @@ void ex_diffgetput(exarg_T *eap)
}
} else {
// Buffer number or pattern given. Ignore trailing white space.
- p = eap->arg + STRLEN(eap->arg);
- while (p > eap->arg && ascii_iswhite(p[-1])) {
+ p = (char_u *)eap->arg + STRLEN(eap->arg);
+ while (p > (char_u *)eap->arg && ascii_iswhite(p[-1])) {
p--;
}
- for (i = 0; ascii_isdigit(eap->arg[i]) && eap->arg + i < p; ++i) {
- }
+ for (i = 0; ascii_isdigit(eap->arg[i]) && (char_u *)eap->arg + i < p; i++) {}
- if (eap->arg + i == p) {
+ if ((char_u *)eap->arg + i == p) {
// digits only
- i = atol((char *)eap->arg);
+ i = (int)atol(eap->arg);
} else {
- i = buflist_findpat(eap->arg, p, false, true, false);
+ i = buflist_findpat(eap->arg, (char *)p, false, true, false);
if (i < 0) {
// error message already given
@@ -2605,9 +2598,9 @@ void ex_diffgetput(exarg_T *eap)
&& (eap->line1 == curbuf->b_ml.ml_line_count)
&& (diff_check(curwin, eap->line1) == 0)
&& ((eap->line1 == 1) || (diff_check(curwin, eap->line1 - 1) == 0))) {
- ++eap->line2;
+ eap->line2++;
} else if (eap->line1 > 0) {
- --eap->line1;
+ eap->line1--;
}
}
@@ -2636,7 +2629,7 @@ void ex_diffgetput(exarg_T *eap)
}
}
- dprev = NULL;
+ diff_T *dprev = NULL;
for (dp = curtab->tp_first_diff; dp != NULL;) {
if (dp->df_lnum[idx_cur] > eap->line2 + off) {
@@ -2698,20 +2691,20 @@ void ex_diffgetput(exarg_T *eap)
buf_empty = buf_is_empty(curbuf);
added = 0;
- for (i = 0; i < count; ++i) {
+ for (i = 0; i < count; i++) {
// remember deleting the last line of the buffer
buf_empty = curbuf->b_ml.ml_line_count == 1;
ml_delete(lnum, false);
added--;
}
- for (i = 0; i < dp->df_count[idx_from] - start_skip - end_skip; ++i) {
+ for (i = 0; i < dp->df_count[idx_from] - start_skip - end_skip; i++) {
linenr_T nr = dp->df_lnum[idx_from] + start_skip + i;
if (nr > curtab->tp_diffbuf[idx_from]->b_ml.ml_line_count) {
break;
}
p = vim_strsave(ml_get_buf(curtab->tp_diffbuf[idx_from], nr, false));
- ml_append(lnum + i - 1, p, 0, false);
+ ml_append(lnum + i - 1, (char *)p, 0, false);
xfree(p);
added++;
if (buf_empty && (curbuf->b_ml.ml_line_count == 2)) {
@@ -2727,7 +2720,7 @@ void ex_diffgetput(exarg_T *eap)
if ((start_skip == 0) && (end_skip == 0)) {
// Check if there are any other buffers and if the diff is
// equal in them.
- for (i = 0; i < DB_COUNT; ++i) {
+ for (i = 0; i < DB_COUNT; i++) {
if ((curtab->tp_diffbuf[i] != NULL)
&& (i != idx_from)
&& (i != idx_to)
@@ -2751,7 +2744,7 @@ void ex_diffgetput(exarg_T *eap)
// Adjust marks. This will change the following entries!
if (added != 0) {
- mark_adjust(lnum, lnum + count - 1, (long)MAXLNUM, (long)added,
+ mark_adjust(lnum, lnum + count - 1, (long)MAXLNUM, added,
kExtmarkUndo);
if (curwin->w_cursor.lnum >= lnum) {
// Adjust the cursor position if it's in/after the changed
@@ -2763,7 +2756,7 @@ void ex_diffgetput(exarg_T *eap)
}
}
}
- changed_lines(lnum, 0, lnum + count, (long)added, true);
+ changed_lines(lnum, 0, lnum + count, added, true);
if (dfree != NULL) {
// Diff is deleted, update folds in other windows.
@@ -2830,7 +2823,7 @@ theend:
static void diff_fold_update(diff_T *dp, int skip_idx)
{
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- for (int i = 0; i < DB_COUNT; ++i) {
+ for (int i = 0; i < DB_COUNT; i++) {
if ((curtab->tp_diffbuf[i] == wp->w_buffer) && (i != skip_idx)) {
foldUpdate(wp, dp->df_lnum[i], dp->df_lnum[i] + dp->df_count[i]);
}
@@ -2919,13 +2912,10 @@ int diff_move_to(int dir, long count)
/// "buf1" in diff mode.
static linenr_T diff_get_corresponding_line_int(buf_T *buf1, linenr_T lnum1)
{
- int idx1;
- int idx2;
- diff_T *dp;
- int baseline = 0;
+ linenr_T baseline = 0;
- idx1 = diff_buf_idx(buf1);
- idx2 = diff_buf_idx(curbuf);
+ int idx1 = diff_buf_idx(buf1);
+ int idx2 = diff_buf_idx(curbuf);
if ((idx1 == DB_COUNT)
|| (idx2 == DB_COUNT)
@@ -2943,7 +2933,7 @@ static linenr_T diff_get_corresponding_line_int(buf_T *buf1, linenr_T lnum1)
return lnum1;
}
- for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) {
+ for (diff_T *dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) {
if (dp->df_lnum[idx1] > lnum1) {
return lnum1 - baseline;
}
@@ -2999,11 +2989,8 @@ linenr_T diff_get_corresponding_line(buf_T *buf1, linenr_T lnum1)
linenr_T diff_lnum_win(linenr_T lnum, win_T *wp)
{
diff_T *dp;
- int idx;
- int i;
- linenr_T n;
- idx = diff_buf_idx(curbuf);
+ int idx = diff_buf_idx(curbuf);
if (idx == DB_COUNT) {
// safety check
@@ -3029,14 +3016,14 @@ linenr_T diff_lnum_win(linenr_T lnum, win_T *wp)
}
// Find index for "wp".
- i = diff_buf_idx(wp->w_buffer);
+ int i = diff_buf_idx(wp->w_buffer);
if (i == DB_COUNT) {
// safety check
return (linenr_T)0;
}
- n = lnum + (dp->df_lnum[i] - dp->df_lnum[idx]);
+ linenr_T n = lnum + (dp->df_lnum[i] - dp->df_lnum[idx]);
if (n > dp->df_lnum[i] + dp->df_count[i]) {
n = dp->df_lnum[i] + dp->df_count[i];
}
@@ -3049,16 +3036,14 @@ linenr_T diff_lnum_win(linenr_T lnum, win_T *wp)
///
static int parse_diff_ed(char_u *line, diffhunk_T *hunk)
{
- char_u *p;
- long f1, l1, f2, l2;
- int difftype;
+ long l1, l2;
// The line must be one of three formats:
// change: {first}[,{last}]c{first}[,{last}]
// append: {first}a{first}[,{last}]
// delete: {first}[,{last}]d{first}
- p = line;
- f1 = getdigits(&p, true, 0);
+ char_u *p = line;
+ linenr_T f1 = getdigits_int32((char **)&p, true, 0);
if (*p == ',') {
p++;
l1 = getdigits(&p, true, 0);
@@ -3068,8 +3053,8 @@ static int parse_diff_ed(char_u *line, diffhunk_T *hunk)
if (*p != 'a' && *p != 'c' && *p != 'd') {
return FAIL; // invalid diff format
}
- difftype = *p++;
- f2 = getdigits(&p, true, 0);
+ int difftype = *p++;
+ long f2 = getdigits(&p, true, 0);
if (*p == ',') {
p++;
l2 = getdigits(&p, true, 0);
@@ -3088,10 +3073,10 @@ static int parse_diff_ed(char_u *line, diffhunk_T *hunk)
hunk->count_orig = l1 - f1 + 1;
}
if (difftype == 'd') {
- hunk->lnum_new = f2 + 1;
+ hunk->lnum_new = (linenr_T)f2 + 1;
hunk->count_new = 0;
} else {
- hunk->lnum_new = f2;
+ hunk->lnum_new = (linenr_T)f2;
hunk->count_new = l2 - f2 + 1;
}
return OK;
@@ -3103,14 +3088,14 @@ static int parse_diff_ed(char_u *line, diffhunk_T *hunk)
///
static int parse_diff_unified(char_u *line, diffhunk_T *hunk)
{
- char_u *p;
- long oldline, oldcount, newline, newcount;
-
// Parse unified diff hunk header:
// @@ -oldline,oldcount +newline,newcount @@
- p = line;
+ char_u *p = line;
if (*p++ == '@' && *p++ == '@' && *p++ == ' ' && *p++ == '-') {
- oldline = getdigits(&p, true, 0);
+ long oldcount;
+ long newline;
+ long newcount;
+ long oldline = getdigits(&p, true, 0);
if (*p == ',') {
p++;
oldcount = getdigits(&p, true, 0);
@@ -3139,9 +3124,9 @@ static int parse_diff_unified(char_u *line, diffhunk_T *hunk)
newline = 1;
}
- hunk->lnum_orig = oldline;
+ hunk->lnum_orig = (linenr_T)oldline;
hunk->count_orig = oldcount;
- hunk->lnum_new = newline;
+ hunk->lnum_new = (linenr_T)newline;
hunk->count_new = newcount;
return OK;
@@ -3154,16 +3139,15 @@ static int parse_diff_unified(char_u *line, diffhunk_T *hunk)
/// Callback function for the xdl_diff() function.
/// Stores the diff output in a grow array.
///
-static int xdiff_out(long start_a, long count_a, long start_b, long count_b,
- void *priv)
+static int xdiff_out(long start_a, long count_a, long start_b, long count_b, void *priv)
{
diffout_T *dout = (diffout_T *)priv;
diffhunk_T *p = xmalloc(sizeof(*p));
ga_grow(&dout->dout_ga, 1);
- p->lnum_orig = start_a + 1;
+ p->lnum_orig = (linenr_T)start_a + 1;
p->count_orig = count_a;
- p->lnum_new = start_b + 1;
+ p->lnum_new = (linenr_T)start_b + 1;
p->count_new = count_b;
((diffhunk_T **)dout->dout_ga.ga_data)[dout->dout_ga.ga_len++] = p;
return 0;
diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c
index 8eda173cac..355900c93f 100644
--- a/src/nvim/digraph.c
+++ b/src/nvim/digraph.c
@@ -12,11 +12,13 @@
#include "nvim/ascii.h"
#include "nvim/charset.h"
#include "nvim/digraph.h"
+#include "nvim/eval/typval.h"
#include "nvim/ex_cmds2.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_getln.h"
#include "nvim/garray.h"
#include "nvim/getchar.h"
+#include "nvim/mapping.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/message.h"
@@ -34,6 +36,12 @@ typedef struct digraph {
result_T result;
} digr_T;
+static char e_digraph_must_be_just_two_characters_str[]
+ = N_("E1214: Digraph must be just two characters: %s");
+static char e_digraph_argument_must_be_one_character_str[]
+ = N_("E1215: Digraph must be one character: %s");
+static char e_digraph_setlist_argument_must_be_list_of_lists_with_two_items[]
+ = N_("E1216: digraph_setlist() argument must be a list of lists with two items");
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "digraph.c.generated.h"
@@ -1458,7 +1466,7 @@ int do_digraph(int c)
backspaced = -1;
} else if (p_dg) {
if (backspaced >= 0) {
- c = getdigraph(backspaced, c, false);
+ c = digraph_get(backspaced, c, false);
}
backspaced = -1;
@@ -1475,17 +1483,16 @@ int do_digraph(int c)
char_u *get_digraph_for_char(int val_arg)
{
const int val = val_arg;
- digr_T *dp;
+ const digr_T *dp;
static char_u r[3];
for (int use_defaults = 0; use_defaults <= 1; use_defaults++) {
if (use_defaults == 0) {
- dp = (digr_T *)user_digraphs.ga_data;
+ dp = (const digr_T *)user_digraphs.ga_data;
} else {
dp = digraphdefault;
}
- for (int i = 0;
- use_defaults ? dp->char1 != NUL : i < user_digraphs.ga_len; i++) {
+ for (int i = 0; use_defaults ? dp->char1 != NUL : i < user_digraphs.ga_len; i++) {
if (dp->result == val) {
r[0] = dp->char1;
r[1] = dp->char2;
@@ -1506,10 +1513,11 @@ char_u *get_digraph_for_char(int val_arg)
/// @returns composed character, or NUL when ESC was used.
int get_digraph(bool cmdline)
{
- int cc;
no_mapping++;
+ allow_keys++;
int c = plain_vgetc();
no_mapping--;
+ allow_keys--;
if (c != ESC) {
// ESC cancels CTRL-K
@@ -1526,12 +1534,14 @@ int get_digraph(bool cmdline)
add_to_showcmd(c);
}
no_mapping++;
- cc = plain_vgetc();
+ allow_keys++;
+ int cc = plain_vgetc();
no_mapping--;
+ allow_keys--;
if (cc != ESC) {
// ESC cancels CTRL-K
- return getdigraph(c, cc, true);
+ return digraph_get(c, cc, true);
}
}
return NUL;
@@ -1546,6 +1556,7 @@ int get_digraph(bool cmdline)
/// @return If no match, return "char2". If "meta_char" is true and "char1"
// is a space, return "char2" | 0x80.
static int getexactdigraph(int char1, int char2, bool meta_char)
+ FUNC_ATTR_PURE
{
int retval = 0;
@@ -1554,25 +1565,25 @@ static int getexactdigraph(int char1, int char2, bool meta_char)
}
// Search user digraphs first.
- digr_T *dp = (digr_T *)user_digraphs.ga_data;
- for (int i = 0; i < user_digraphs.ga_len; ++i) {
+ const digr_T *dp = (const digr_T *)user_digraphs.ga_data;
+ for (int i = 0; i < user_digraphs.ga_len; i++) {
if (((int)dp->char1 == char1) && ((int)dp->char2 == char2)) {
retval = dp->result;
break;
}
- ++dp;
+ dp++;
}
// Search default digraphs.
if (retval == 0) {
dp = digraphdefault;
- for (int i = 0; dp->char1 != 0; ++i) {
+ for (int i = 0; dp->char1 != 0; i++) {
if (((int)dp->char1 == char1) && ((int)dp->char2 == char2)) {
retval = dp->result;
break;
}
- ++dp;
+ dp++;
}
}
@@ -1595,7 +1606,8 @@ static int getexactdigraph(int char1, int char2, bool meta_char)
/// @param meta_char
///
/// @return The digraph.
-int getdigraph(int char1, int char2, bool meta_char)
+int digraph_get(int char1, int char2, bool meta_char)
+ FUNC_ATTR_PURE
{
int retval;
@@ -1608,60 +1620,72 @@ int getdigraph(int char1, int char2, bool meta_char)
return retval;
}
+/// Add a digraph to the digraph table.
+static void registerdigraph(int char1, int char2, int n)
+{
+ // If the digraph already exists, replace "result".
+ digr_T *dp = (digr_T *)user_digraphs.ga_data;
+ for (int i = 0; i < user_digraphs.ga_len; i++) {
+ if ((int)dp->char1 == char1 && (int)dp->char2 == char2) {
+ dp->result = n;
+ return;
+ }
+ dp++;
+ }
+
+ // Add a new digraph to the table.
+ dp = GA_APPEND_VIA_PTR(digr_T, &user_digraphs);
+ dp->char1 = (char_u)char1;
+ dp->char2 = (char_u)char2;
+ dp->result = n;
+}
+
+/// Check the characters are valid for a digraph.
+/// If they are valid, returns true; otherwise, give an error message and
+/// returns false.
+bool check_digraph_chars_valid(int char1, int char2)
+{
+ if (char2 == 0) {
+ char_u msg[MB_MAXBYTES + 1];
+ msg[utf_char2bytes(char1, (char *)msg)] = NUL;
+ semsg(_(e_digraph_must_be_just_two_characters_str), msg);
+ return false;
+ }
+ if (char1 == ESC || char2 == ESC) {
+ emsg(_("E104: Escape not allowed in digraph"));
+ return false;
+ }
+ return true;
+}
+
/// Add the digraphs in the argument to the digraph table.
/// format: {c1}{c2} char {c1}{c2} char ...
///
/// @param str
void putdigraph(char_u *str)
{
- char_u char1, char2;
- digr_T *dp;
-
while (*str != NUL) {
- str = skipwhite(str);
+ str = (char_u *)skipwhite((char *)str);
if (*str == NUL) {
return;
}
- char1 = *str++;
- char2 = *str++;
+ char_u char1 = *str++;
+ char_u char2 = *str++;
- if (char2 == 0) {
- emsg(_(e_invarg));
+ if (!check_digraph_chars_valid(char1, char2)) {
return;
}
- if ((char1 == ESC) || (char2 == ESC)) {
- emsg(_("E104: Escape not allowed in digraph"));
- return;
- }
- str = skipwhite(str);
+ str = (char_u *)skipwhite((char *)str);
if (!ascii_isdigit(*str)) {
emsg(_(e_number_exp));
return;
}
- int n = getdigits_int(&str, true, 0);
+ int n = getdigits_int((char **)&str, true, 0);
- // If the digraph already exists, replace the result.
- dp = (digr_T *)user_digraphs.ga_data;
-
- int i;
- for (i = 0; i < user_digraphs.ga_len; ++i) {
- if (((int)dp->char1 == char1) && ((int)dp->char2 == char2)) {
- dp->result = n;
- break;
- }
- ++dp;
- }
-
- // Add a new digraph to the table.
- if (i == user_digraphs.ga_len) {
- dp = GA_APPEND_VIA_PTR(digr_T, &user_digraphs);
- dp->char1 = char1;
- dp->char2 = char2;
- dp->result = n;
- }
+ registerdigraph(char1, char2, n);
}
}
@@ -1677,14 +1701,13 @@ static void digraph_header(const char *msg)
void listdigraphs(bool use_headers)
{
- digr_T *dp;
result_T previous = 0;
msg_putchar('\n');
- dp = digraphdefault;
+ const digr_T *dp = digraphdefault;
- for (int i = 0; dp->char1 != NUL && !got_int; ++i) {
+ for (int i = 0; dp->char1 != NUL && !got_int; i++) {
digr_T tmp;
// May need to convert the result to 'encoding'.
@@ -1692,15 +1715,14 @@ void listdigraphs(bool use_headers)
tmp.char2 = dp->char2;
tmp.result = getexactdigraph(tmp.char1, tmp.char2, false);
- if ((tmp.result != 0)
- && (tmp.result != tmp.char2)) {
+ if (tmp.result != 0 && tmp.result != tmp.char2) {
printdigraph(&tmp, use_headers ? &previous : NULL);
}
dp++;
fast_breakcheck();
}
- dp = (digr_T *)user_digraphs.ga_data;
+ dp = (const digr_T *)user_digraphs.ga_data;
for (int i = 0; i < user_digraphs.ga_len && !got_int; i++) {
if (previous >= 0 && use_headers) {
digraph_header(_("Custom"));
@@ -1712,6 +1734,50 @@ void listdigraphs(bool use_headers)
}
}
+static void digraph_getlist_appendpair(const digr_T *dp, list_T *l)
+{
+ list_T *l2 = tv_list_alloc(2);
+ tv_list_append_list(l, l2);
+
+ char_u buf[30];
+ buf[0] = dp->char1;
+ buf[1] = dp->char2;
+ buf[2] = NUL;
+ tv_list_append_string(l2, (char *)buf, -1);
+
+ char_u *p = buf;
+ p += utf_char2bytes(dp->result, (char *)p);
+ *p = NUL;
+ tv_list_append_string(l2, (char *)buf, -1);
+}
+
+void digraph_getlist_common(bool list_all, typval_T *rettv)
+{
+ tv_list_alloc_ret(rettv, (int)sizeof(digraphdefault) + user_digraphs.ga_len);
+
+ const digr_T *dp;
+
+ if (list_all) {
+ dp = digraphdefault;
+ for (int i = 0; dp->char1 != NUL && !got_int; i++) {
+ digr_T tmp;
+ tmp.char1 = dp->char1;
+ tmp.char2 = dp->char2;
+ tmp.result = getexactdigraph(tmp.char1, tmp.char2, false);
+ if (tmp.result != 0 && tmp.result != tmp.char2) {
+ digraph_getlist_appendpair(&tmp, rettv->vval.v_list);
+ }
+ dp++;
+ }
+ }
+
+ dp = (const digr_T *)user_digraphs.ga_data;
+ for (int i = 0; i < user_digraphs.ga_len && !got_int; i++) {
+ digraph_getlist_appendpair(dp, rettv->vval.v_list);
+ dp++;
+ }
+}
+
struct dg_header_entry {
int dg_start;
const char *dg_header;
@@ -1749,11 +1815,7 @@ static void printdigraph(const digr_T *dp, result_T *previous)
FUNC_ATTR_NONNULL_ARG(1)
{
char_u buf[30];
- char_u *p;
-
- int list_width;
-
- list_width = 13;
+ int list_width = 13;
if (dp->result != 0) {
if (previous != NULL) {
@@ -1771,7 +1833,6 @@ static void printdigraph(const digr_T *dp, result_T *previous)
msg_putchar('\n');
}
-
// Make msg_col a multiple of list_width by using spaces.
if (msg_col % list_width != 0) {
int spaces = (msg_col / list_width + 1) * list_width - msg_col;
@@ -1780,19 +1841,19 @@ static void printdigraph(const digr_T *dp, result_T *previous)
}
}
- p = &buf[0];
+ char_u *p = &buf[0];
*p++ = dp->char1;
*p++ = dp->char2;
*p++ = ' ';
*p = NUL;
- msg_outtrans(buf);
+ msg_outtrans((char *)buf);
p = buf;
// add a space to draw a composing char on
if (utf_iscomposing(dp->result)) {
*p++ = ' ';
}
- p += utf_char2bytes(dp->result, p);
+ p += utf_char2bytes(dp->result, (char *)p);
*p = NUL;
msg_outtrans_attr(buf, HL_ATTR(HLF_8));
@@ -1802,10 +1863,151 @@ static void printdigraph(const digr_T *dp, result_T *previous)
}
assert(p >= buf);
vim_snprintf((char *)p, sizeof(buf) - (size_t)(p - buf), " %3d", dp->result);
- msg_outtrans(buf);
+ msg_outtrans((char *)buf);
}
}
+/// Get the two digraph characters from a typval.
+/// @return OK or FAIL.
+static int get_digraph_chars(const typval_T *arg, int *char1, int *char2)
+{
+ char buf_chars[NUMBUFLEN];
+ const char *chars = tv_get_string_buf_chk(arg, buf_chars);
+ const char_u *p = (const char_u *)chars;
+
+ if (p != NULL) {
+ if (*p != NUL) {
+ *char1 = mb_cptr2char_adv(&p);
+ if (*p != NUL) {
+ *char2 = mb_cptr2char_adv(&p);
+ if (*p == NUL) {
+ if (check_digraph_chars_valid(*char1, *char2)) {
+ return OK;
+ }
+ return FAIL;
+ }
+ }
+ }
+ }
+ semsg(_(e_digraph_must_be_just_two_characters_str), chars);
+ return FAIL;
+}
+
+static bool digraph_set_common(const typval_T *argchars, const typval_T *argdigraph)
+{
+ int char1, char2;
+ if (get_digraph_chars(argchars, &char1, &char2) == FAIL) {
+ return false;
+ }
+
+ char buf_digraph[NUMBUFLEN];
+ const char *digraph = tv_get_string_buf_chk(argdigraph, buf_digraph);
+ if (digraph == NULL) {
+ return false;
+ }
+ const char_u *p = (const char_u *)digraph;
+ int n = mb_cptr2char_adv(&p);
+ if (*p != NUL) {
+ semsg(_(e_digraph_argument_must_be_one_character_str), digraph);
+ return false;
+ }
+
+ registerdigraph(char1, char2, n);
+ return true;
+}
+
+/// "digraph_get()" function
+void f_digraph_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = NULL; // Return empty string for failure
+ const char *digraphs = tv_get_string_chk(&argvars[0]);
+
+ if (digraphs == NULL) {
+ return;
+ }
+ if (STRLEN(digraphs) != 2) {
+ semsg(_(e_digraph_must_be_just_two_characters_str), digraphs);
+ return;
+ }
+ int code = digraph_get(digraphs[0], digraphs[1], false);
+
+ char_u buf[NUMBUFLEN];
+ buf[utf_char2bytes(code, (char *)buf)] = NUL;
+ rettv->vval.v_string = (char *)vim_strsave(buf);
+}
+
+/// "digraph_getlist()" function
+void f_digraph_getlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ bool flag_list_all;
+
+ if (argvars[0].v_type == VAR_UNKNOWN) {
+ flag_list_all = false;
+ } else {
+ bool error = false;
+ varnumber_T flag = tv_get_number_chk(&argvars[0], &error);
+ if (error) {
+ return;
+ }
+ flag_list_all = flag != 0;
+ }
+
+ digraph_getlist_common(flag_list_all, rettv);
+}
+
+/// "digraph_set()" function
+void f_digraph_set(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ rettv->v_type = VAR_BOOL;
+ rettv->vval.v_bool = kBoolVarFalse;
+
+ if (!digraph_set_common(&argvars[0], &argvars[1])) {
+ return;
+ }
+
+ rettv->vval.v_bool = kBoolVarTrue;
+}
+
+/// "digraph_setlist()" function
+void f_digraph_setlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ rettv->v_type = VAR_BOOL;
+ rettv->vval.v_bool = kBoolVarFalse;
+
+ if (argvars[0].v_type != VAR_LIST) {
+ emsg(_(e_digraph_setlist_argument_must_be_list_of_lists_with_two_items));
+ return;
+ }
+
+ list_T *pl = argvars[0].vval.v_list;
+ if (pl == NULL) {
+ // Empty list always results in success.
+ rettv->vval.v_bool = kBoolVarTrue;
+ return;
+ }
+
+ TV_LIST_ITER_CONST(pl, pli, {
+ if (TV_LIST_ITEM_TV(pli)->v_type != VAR_LIST) {
+ emsg(_(e_digraph_setlist_argument_must_be_list_of_lists_with_two_items));
+ return;
+ }
+
+ list_T *l = TV_LIST_ITEM_TV(pli)->vval.v_list;
+ if (l == NULL || tv_list_len(l) != 2) {
+ emsg(_(e_digraph_setlist_argument_must_be_list_of_lists_with_two_items));
+ return;
+ }
+
+ if (!digraph_set_common(TV_LIST_ITEM_TV(tv_list_first(l)),
+ TV_LIST_ITEM_TV(TV_LIST_ITEM_NEXT(l, tv_list_first(l))))) {
+ return;
+ }
+ });
+
+ rettv->vval.v_bool = kBoolVarTrue;
+}
+
/// structure used for b_kmap_ga.ga_data
typedef struct {
char_u *from;
@@ -1814,7 +2016,6 @@ typedef struct {
#define KMAP_MAXLEN 20 // maximum length of "from" or "to"
-
/// Set up key mapping tables for the 'keymap' option.
///
/// @return NULL if OK, an error message for failure. This only needs to be
@@ -1863,13 +2064,11 @@ char *keymap_init(void)
/// @param eap
void ex_loadkeymap(exarg_T *eap)
{
- char_u *line;
- char_u *p;
char_u *s;
#define KMAP_LLEN 200 // max length of "to" and "from" together
char_u buf[KMAP_LLEN + 11];
- char_u *save_cpo = p_cpo;
+ char *save_cpo = p_cpo;
if (!getline_equal(eap->getline, eap->cookie, getsourceline)) {
emsg(_("E105: Using :loadkeymap not in a sourced file"));
@@ -1883,23 +2082,23 @@ void ex_loadkeymap(exarg_T *eap)
ga_init(&curbuf->b_kmap_ga, (int)sizeof(kmap_T), 20);
// Set 'cpoptions' to "C" to avoid line continuation.
- p_cpo = (char_u *)"C";
+ p_cpo = "C";
// Get each line of the sourced file, break at the end.
for (;;) {
- line = eap->getline(0, eap->cookie, 0, true);
+ char *line = eap->getline(0, eap->cookie, 0, true);
if (line == NULL) {
break;
}
- p = skipwhite(line);
+ char_u *p = (char_u *)skipwhite(line);
if ((*p != '"') && (*p != NUL)) {
kmap_T *kp = GA_APPEND_VIA_PTR(kmap_T, &curbuf->b_kmap_ga);
s = skiptowhite(p);
kp->from = vim_strnsave(p, (size_t)(s - p));
- p = skipwhite(s);
+ p = (char_u *)skipwhite((char *)s);
s = skiptowhite(p);
kp->to = vim_strnsave(p, (size_t)(s - p));
@@ -1911,7 +2110,7 @@ void ex_loadkeymap(exarg_T *eap)
}
xfree(kp->from);
xfree(kp->to);
- --curbuf->b_kmap_ga.ga_len;
+ curbuf->b_kmap_ga.ga_len--;
}
}
xfree(line);
@@ -1922,7 +2121,7 @@ void ex_loadkeymap(exarg_T *eap)
vim_snprintf((char *)buf, sizeof(buf), "<buffer> %s %s",
((kmap_T *)curbuf->b_kmap_ga.ga_data)[i].from,
((kmap_T *)curbuf->b_kmap_ga.ga_data)[i].to);
- (void)do_map(0, buf, LANGMAP, false);
+ (void)do_map(0, buf, MODE_LANGMAP, false);
}
p_cpo = save_cpo;
@@ -1944,23 +2143,22 @@ void keymap_ga_clear(garray_T *kmap_ga)
/// Stop using 'keymap'.
static void keymap_unload(void)
{
- char_u buf[KMAP_MAXLEN + 10];
- char_u *save_cpo = p_cpo;
- kmap_T *kp;
+ char buf[KMAP_MAXLEN + 10];
+ char *save_cpo = p_cpo;
if (!(curbuf->b_kmap_state & KEYMAP_LOADED)) {
return;
}
// Set 'cpoptions' to "C" to avoid line continuation.
- p_cpo = (char_u *)"C";
+ p_cpo = "C";
// clear the ":lmap"s
- kp = (kmap_T *)curbuf->b_kmap_ga.ga_data;
+ kmap_T *kp = (kmap_T *)curbuf->b_kmap_ga.ga_data;
for (int i = 0; i < curbuf->b_kmap_ga.ga_len; i++) {
- vim_snprintf((char *)buf, sizeof(buf), "<buffer> %s", kp[i].from);
- (void)do_map(1, buf, LANGMAP, false);
+ vim_snprintf(buf, sizeof(buf), "<buffer> %s", kp[i].from);
+ (void)do_map(1, (char_u *)buf, MODE_LANGMAP, false);
}
keymap_ga_clear(&curbuf->b_kmap_ga);
diff --git a/src/nvim/digraph.h b/src/nvim/digraph.h
index 71330ae9b1..039fc3370d 100644
--- a/src/nvim/digraph.h
+++ b/src/nvim/digraph.h
@@ -1,6 +1,7 @@
#ifndef NVIM_DIGRAPH_H
#define NVIM_DIGRAPH_H
+#include "nvim/eval/funcs.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/types.h"
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index aa37d1b2dd..0571e71cb5 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -26,10 +26,12 @@
#include "nvim/fileio.h"
#include "nvim/fold.h"
#include "nvim/getchar.h"
+#include "nvim/highlight_group.h"
#include "nvim/indent.h"
#include "nvim/indent_c.h"
-#include "nvim/keymap.h"
+#include "nvim/keycodes.h"
#include "nvim/main.h"
+#include "nvim/mapping.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
@@ -134,7 +136,6 @@ static char *ctrl_x_mode_names[] = {
};
static char e_hitend[] = N_("Hit end of paragraph");
-static char e_complwin[] = N_("E839: Completion function changed window");
static char e_compldel[] = N_("E840: Completion function deleted text");
/*
@@ -244,7 +245,6 @@ typedef struct insert_state {
char_u *ptr;
} InsertState;
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "edit.c.generated.h"
#endif
@@ -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()
@@ -289,7 +289,7 @@ static void insert_enter(InsertState *s)
{
s->did_backspace = true;
s->old_topfill = -1;
- s->replaceState = REPLACE;
+ s->replaceState = MODE_REPLACE;
s->cmdchar_todo = s->cmdchar;
// Remember whether editing was restarted after CTRL-O
did_restart_edit = restart_edit;
@@ -334,7 +334,7 @@ static void insert_enter(InsertState *s)
int save_state = State;
curwin->w_cursor = save_cursor;
- State = INSERT;
+ State = MODE_INSERT;
check_cursor_col();
State = save_state;
}
@@ -376,35 +376,36 @@ static void insert_enter(InsertState *s)
}
if (s->cmdchar == 'R') {
- State = REPLACE;
+ State = MODE_REPLACE;
} else if (s->cmdchar == 'V' || s->cmdchar == 'v') {
- State = VREPLACE;
- s->replaceState = VREPLACE;
+ State = MODE_VREPLACE;
+ s->replaceState = MODE_VREPLACE;
orig_line_count = curbuf->b_ml.ml_line_count;
vr_lines_changed = 1;
} else {
- State = INSERT;
+ State = MODE_INSERT;
}
- trigger_modechanged();
+ may_trigger_modechanged();
stop_insert_mode = false;
- // Need to recompute the cursor position, it might move when the cursor is
- // on a TAB or special character.
- curs_columns(curwin, true);
+ // need to position cursor again when on a TAB
+ if (gchar_cursor() == TAB) {
+ curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL);
+ }
// Enable langmap or IME, indicated by 'iminsert'.
// Note that IME may enabled/disabled without us noticing here, thus the
// 'iminsert' value may not reflect what is actually used. It is updated
// when hitting <Esc>.
if (curbuf->b_p_iminsert == B_IMODE_LMAP) {
- State |= LANGMAP;
+ State |= MODE_LANGMAP;
}
setmouse();
clear_showcmd();
// there is no reverse replace mode
- revins_on = (State == INSERT && p_ri);
+ revins_on = (State == MODE_INSERT && p_ri);
if (revins_on) {
undisplay_dollar();
}
@@ -438,7 +439,7 @@ static void insert_enter(InsertState *s)
if (s->ptr[1] == NUL) {
curwin->w_cursor.col++;
} else {
- s->i = utfc_ptr2len(s->ptr);
+ s->i = utfc_ptr2len((char *)s->ptr);
if (s->ptr[s->i] == NUL) {
curwin->w_cursor.col += s->i;
}
@@ -457,9 +458,8 @@ static void insert_enter(InsertState *s)
where_paste_started.lnum = 0;
can_cindent = true;
- // The cursor line is not in a closed fold, unless 'insertmode' is set or
- // restarting.
- if (!p_im && did_restart_edit == 0) {
+ // The cursor line is not in a closed fold, unless restarting.
+ if (did_restart_edit == 0) {
foldOpenCursor();
}
@@ -471,7 +471,7 @@ static void insert_enter(InsertState *s)
s->i = showmode();
}
- if (!p_im && did_restart_edit == 0) {
+ if (did_restart_edit == 0) {
change_warning(curbuf, s->i == 0 ? 0 : s->i + 1);
}
@@ -551,8 +551,8 @@ static int insert_check(VimState *state)
Insstart_orig = Insstart;
}
- if (stop_insert_mode && !pum_visible()) {
- // ":stopinsert" used or 'insertmode' reset
+ if (stop_insert_mode && !compl_started) {
+ // ":stopinsert" used
s->count = 0;
return 0; // exit insert mode
}
@@ -574,7 +574,6 @@ static int insert_check(VimState *state)
// When emsg() was called msg_scroll will have been set.
msg_scroll = false;
-
// Open fold at the cursor line, according to 'foldopen'.
if (fdo_flags & FDO_INSERT) {
foldOpenCursor();
@@ -663,8 +662,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;
}
@@ -721,7 +724,7 @@ static int insert_execute(VimState *state, int key)
if (str != NULL) {
for (p = str; *p != NUL; MB_PTR_ADV(p)) {
- ins_compl_addleader(utf_ptr2char(p));
+ ins_compl_addleader(utf_ptr2char((char *)p));
}
xfree(str);
} else {
@@ -750,20 +753,19 @@ static int insert_execute(VimState *state, int key)
}
// CTRL-\ CTRL-N goes to Normal mode,
- // CTRL-\ CTRL-G goes to mode selected with 'insertmode',
// CTRL-\ CTRL-O is like CTRL-O but without moving the cursor
if (s->c == Ctrl_BSL) {
// may need to redraw when no more chars available now
ins_redraw(false);
no_mapping++;
+ allow_keys++;
s->c = plain_vgetc();
no_mapping--;
+ allow_keys--;
if (s->c != Ctrl_N && s->c != Ctrl_G && s->c != Ctrl_O) {
// it's something else
vungetc(s->c);
s->c = Ctrl_BSL;
- } else if (s->c == Ctrl_G && p_im) {
- return 1; // continue
} else {
if (s->c == Ctrl_O) {
ins_ctrl_o();
@@ -834,16 +836,6 @@ static int insert_execute(VimState *state, int key)
return insert_handle_key(s);
}
-
-/// Return true when need to go to Insert mode because of 'insertmode'.
-///
-/// Don't do this when still processing a command or a mapping.
-/// Don't do this when inside a ":normal" command.
-bool goto_im(void)
-{
- return p_im && stuff_empty() && typebuf_typed();
-}
-
static int insert_handle_key(InsertState *s)
{
// The big switch to handle a character in insert mode.
@@ -875,26 +867,10 @@ static int insert_handle_key(InsertState *s)
}
}
- // when 'insertmode' set, and not halfway through a mapping, don't leave
- // Insert mode
- if (goto_im()) {
- if (got_int) {
- (void)vgetc(); // flush all buffers
- got_int = false;
- } else {
- vim_beep(BO_IM);
- }
- break;
- }
return 0; // exit insert mode
- case Ctrl_Z: // suspend when 'insertmode' set
- if (!p_im) {
- goto normalchar; // insert CTRL-Z as normal char
- }
- do_cmdline_cmd("stop");
- ui_cursor_shape(); // may need to update cursor shape
- break;
+ case Ctrl_Z:
+ goto normalchar; // insert CTRL-Z as normal char
case Ctrl_O: // execute one command
if (ctrl_x_mode == CTRL_X_OMNI) {
@@ -909,7 +885,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;
}
@@ -925,17 +901,12 @@ static int insert_handle_key(InsertState *s)
case K_SELECT: // end of Select mode mapping - ignore
break;
-
case K_HELP: // Help key works like <ESC> <Help>
case K_F1:
case K_XF1:
stuffcharReadbuff(K_HELP);
- if (p_im) {
- need_start_insertmode = true;
- }
return 0; // exit insert mode
-
case ' ':
if (mod_mask != MOD_MASK_CTRL) {
goto normalchar;
@@ -947,7 +918,7 @@ static int insert_handle_key(InsertState *s)
// For ^@ the trailing ESC will end the insert, unless there is an
// error.
if (stuff_inserted(NUL, 1L, (s->c == Ctrl_A)) == FAIL
- && s->c != Ctrl_A && !p_im) {
+ && s->c != Ctrl_A) {
return 0; // exit insert mode
}
s->inserted_space = false;
@@ -1082,11 +1053,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
@@ -1177,7 +1152,6 @@ check_pum:
}
break;
-
case K_S_TAB: // When not mapped, use like a normal TAB
s->c = TAB;
FALLTHROUGH;
@@ -1223,7 +1197,7 @@ check_pum:
}
break;
}
- if (!ins_eol(s->c) && !p_im) {
+ if (!ins_eol(s->c)) {
return 0; // out of memory
}
auto_format(false, false);
@@ -1275,13 +1249,6 @@ check_pum:
case Ctrl_L: // Whole line completion after ^X
if (ctrl_x_mode != CTRL_X_WHOLE_LINE) {
- // CTRL-L with 'insertmode' set: Leave Insert mode
- if (p_im) {
- if (echeck_abbr(Ctrl_L + ABBR_OFF)) {
- break;
- }
- return 0; // exit insert mode
- }
goto normalchar;
}
FALLTHROUGH;
@@ -1312,8 +1279,8 @@ normalchar:
if (!p_paste) {
// Trigger InsertCharPre.
- char_u *str = do_insert_char_pre(s->c);
- char_u *p;
+ char *str = (char *)do_insert_char_pre(s->c);
+ char *p;
if (str != NULL) {
if (*str != NUL && stop_arrow() != FAIL) {
@@ -1383,6 +1350,7 @@ static void insert_do_complete(InsertState *s)
compl_cont_status = 0;
}
compl_busy = false;
+ can_si = may_do_si(); // allow smartindenting
}
static void insert_do_cindent(InsertState *s)
@@ -1440,14 +1408,9 @@ bool edit(int cmdchar, bool startln, long count)
// Don't allow changes in the buffer while editing the cmdline. The
// caller of getcmdline() may get confused.
- if (textlock != 0) {
- emsg(_(e_secure));
- return false;
- }
-
// Don't allow recursive insert mode when busy with completion.
- if (compl_started || compl_busy || pum_visible()) {
- emsg(_(e_secure));
+ if (textlock != 0 || compl_started || compl_busy || pum_visible()) {
+ emsg(_(e_textlock));
return false;
}
@@ -1471,15 +1434,13 @@ bool edit(int cmdchar, bool startln, long count)
/// @param ready not busy with something
static void ins_redraw(bool ready)
{
- bool conceal_cursor_moved = false;
-
if (char_avail()) {
return;
}
// Trigger CursorMoved if the cursor moved. Not when the popup menu is
// visible, the command might delete it.
- if (ready && (has_event(EVENT_CURSORMOVEDI) || curwin->w_p_cole > 0)
+ if (ready && has_event(EVENT_CURSORMOVEDI)
&& !equalpos(curwin->w_last_cursormoved, curwin->w_cursor)
&& !pum_visible()) {
// Need to update the screen first, to make sure syntax
@@ -1489,13 +1450,10 @@ static void ins_redraw(bool ready)
if (syntax_present(curwin) && must_redraw) {
update_screen(0);
}
- if (has_event(EVENT_CURSORMOVEDI)) {
- // Make sure curswant is correct, an autocommand may call
- // getcurpos()
- update_curswant();
- ins_apply_autocmds(EVENT_CURSORMOVEDI);
- }
- conceal_cursor_moved = true;
+ // Make sure curswant is correct, an autocommand may call
+ // getcurpos()
+ update_curswant();
+ ins_apply_autocmds(EVENT_CURSORMOVEDI);
curwin->w_last_cursormoved = curwin->w_cursor;
}
@@ -1537,10 +1495,9 @@ static void ins_redraw(bool ready)
}
}
- // Trigger Scroll if viewport changed.
- if (ready && has_event(EVENT_WINSCROLLED)
- && win_did_scroll(curwin)) {
- do_autocmd_winscrolled(curwin);
+ if (ready) {
+ // Trigger Scroll if viewport changed.
+ may_trigger_winscrolled();
}
// Trigger BufModified if b_changed_invalid is set.
@@ -1551,11 +1508,6 @@ static void ins_redraw(bool ready)
curbuf->b_changed_invalid = false;
}
- if (curwin->w_p_cole > 0 && conceal_cursor_line(curwin)
- && conceal_cursor_moved) {
- redrawWinline(curwin, curwin->w_cursor.lnum);
- }
-
pum_check_clear();
if (must_redraw) {
update_screen(0);
@@ -1586,7 +1538,8 @@ static void ins_ctrl_v(void)
add_to_showcmd_c(Ctrl_V);
- c = get_literal();
+ // Do not include modifiers into the key for CTRL-SHIFT-V.
+ c = get_literal(mod_mask & MOD_MASK_SHIFT);
if (did_putchar) {
// when the line fits in 'columns' the '^' is at the start of the next
// line and will not removed by the redraw
@@ -1628,7 +1581,7 @@ void edit_putchar(int c, bool highlight)
pc_col = 0;
pc_status = PC_STATUS_UNSET;
if (curwin->w_p_rl) {
- pc_col += curwin->w_grid.Columns - 1 - curwin->w_wcol;
+ pc_col += curwin->w_grid.cols - 1 - curwin->w_wcol;
const int fix_col = grid_fix_col(&curwin->w_grid, pc_col, pc_row);
if (fix_col != pc_col) {
@@ -1654,16 +1607,17 @@ void edit_putchar(int c, bool highlight)
/// Return the effective prompt for the specified buffer.
char_u *buf_prompt_text(const buf_T *const buf)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
{
if (buf->b_prompt_text == NULL) {
return (char_u *)"% ";
}
- return buf->b_prompt_text;
+ return (char_u *)buf->b_prompt_text;
}
// Return the effective prompt for the current buffer.
-char_u *prompt_text(void) FUNC_ATTR_WARN_UNUSED_RESULT
+char_u *prompt_text(void)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
{
return buf_prompt_text(curbuf);
}
@@ -1680,9 +1634,9 @@ static void init_prompt(int cmdchar_todo)
if (STRNCMP(text, prompt, STRLEN(prompt)) != 0) {
// prompt is missing, insert it or append a line with it
if (*text == NUL) {
- ml_replace(curbuf->b_ml.ml_line_count, prompt, true);
+ ml_replace(curbuf->b_ml.ml_line_count, (char *)prompt, true);
} else {
- ml_append(curbuf->b_ml.ml_line_count, prompt, 0, false);
+ ml_append(curbuf->b_ml.ml_line_count, (char *)prompt, 0, false);
}
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
coladvance(MAXCOL);
@@ -1711,6 +1665,7 @@ static void init_prompt(int cmdchar_todo)
/// @return true if the cursor is in the editable position of the prompt line.
bool prompt_curpos_editable(void)
+ FUNC_ATTR_PURE
{
return curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count
&& curwin->w_cursor.col >= (int)STRLEN(prompt_text());
@@ -1753,7 +1708,7 @@ void display_dollar(colnr_T col)
char_u *p = get_cursor_line_ptr();
curwin->w_cursor.col -= utf_head_off(p, p + col);
curs_columns(curwin, false); // Recompute w_wrow and w_wcol
- if (curwin->w_wcol < curwin->w_grid.Columns) {
+ if (curwin->w_wcol < curwin->w_grid.cols) {
edit_putchar('$', false);
dollar_vcol = curwin->w_virtcol;
}
@@ -1787,7 +1742,6 @@ void change_indent(int type, int amount, int round, int replaced, int call_chang
int last_vcol;
int insstart_less; // reduction for Insstart.col
int new_cursor_col;
- int i;
char_u *ptr;
int save_p_list;
int start_col;
@@ -1795,7 +1749,7 @@ void change_indent(int type, int amount, int round, int replaced, int call_chang
colnr_T orig_col = 0; // init for GCC
char_u *new_line, *orig_line = NULL; // init for GCC
- // VREPLACE mode needs to know what the line was like before changing
+ // MODE_VREPLACE state needs to know what the line was like before changing
if (State & VREPLACE_FLAG) {
orig_line = vim_strsave(get_cursor_line_ptr()); // Deal with NULL below
orig_col = curwin->w_cursor.col;
@@ -1843,7 +1797,7 @@ void change_indent(int type, int amount, int round, int replaced, int call_chang
// Avoid being called recursively.
if (State & VREPLACE_FLAG) {
- State = INSERT;
+ State = MODE_INSERT;
}
shift_line(type == INDENT_DEC, round, 1, call_changed_bytes);
State = save_State;
@@ -1868,7 +1822,7 @@ void change_indent(int type, int amount, int round, int replaced, int call_chang
insstart_less = MAXCOL;
}
new_cursor_col += curwin->w_cursor.col;
- } else if (!(State & INSERT)) {
+ } else if (!(State & MODE_INSERT)) {
new_cursor_col = curwin->w_cursor.col;
} else {
/*
@@ -1886,7 +1840,7 @@ void change_indent(int type, int amount, int round, int replaced, int call_chang
while (vcol <= (int)curwin->w_virtcol) {
last_vcol = vcol;
if (new_cursor_col >= 0) {
- new_cursor_col += utfc_ptr2len(ptr + new_cursor_col);
+ new_cursor_col += utfc_ptr2len((char *)ptr + new_cursor_col);
} else {
new_cursor_col++;
}
@@ -1900,10 +1854,10 @@ void change_indent(int type, int amount, int round, int replaced, int call_chang
*/
if (vcol != (int)curwin->w_virtcol) {
curwin->w_cursor.col = (colnr_T)new_cursor_col;
- i = (int)curwin->w_virtcol - vcol;
+ size_t i = (size_t)(curwin->w_virtcol - vcol);
ptr = xmallocz(i);
memset(ptr, ' ', i);
- new_cursor_col += i;
+ new_cursor_col += (int)i;
ins_str(ptr);
xfree(ptr);
}
@@ -1928,7 +1882,7 @@ void change_indent(int type, int amount, int round, int replaced, int call_chang
/*
* May have to adjust the start of the insert.
*/
- if (State & INSERT) {
+ if (State & MODE_INSERT) {
if (curwin->w_cursor.lnum == Insstart.lnum && Insstart.col != 0) {
if ((int)Insstart.col <= insstart_less) {
Insstart.col = 0;
@@ -1943,13 +1897,11 @@ void change_indent(int type, int amount, int round, int replaced, int call_chang
}
}
- /*
- * For REPLACE mode, may have to fix the replace stack, if it's possible.
- * If the number of characters before the cursor decreased, need to pop a
- * few characters from the replace stack.
- * If the number of characters before the cursor increased, need to push a
- * few NULs onto the replace stack.
- */
+ // For MODE_REPLACE state, may have to fix the replace stack, if it's
+ // possible. If the number of characters before the cursor decreased, need
+ // to pop a few characters from the replace stack.
+ // If the number of characters before the cursor increased, need to push a
+ // few NULs onto the replace stack.
if (REPLACE_NORMAL(State) && start_col >= 0) {
while (start_col > (int)curwin->w_cursor.col) {
replace_join(0); // remove a NUL from the replace stack
@@ -1965,11 +1917,9 @@ void change_indent(int type, int amount, int round, int replaced, int call_chang
}
}
- /*
- * For VREPLACE mode, we also have to fix the replace stack. In this case
- * it is always possible because we backspace over the whole line and then
- * put it back again the way we wanted it.
- */
+ // For MODE_VREPLACE state, we also have to fix the replace stack. In this
+ // case it is always possible because we backspace over the whole line and
+ // then put it back again the way we wanted it.
if (State & VREPLACE_FLAG) {
// Save new line
new_line = vim_strsave(get_cursor_line_ptr());
@@ -1979,7 +1929,7 @@ void change_indent(int type, int amount, int round, int replaced, int call_chang
int new_col = curwin->w_cursor.col;
// Put back original line
- ml_replace(curwin->w_cursor.lnum, orig_line, false);
+ ml_replace(curwin->w_cursor.lnum, (char *)orig_line, false);
curwin->w_cursor.col = orig_col;
curbuf_splice_pending++;
@@ -1997,18 +1947,16 @@ void change_indent(int type, int amount, int round, int replaced, int call_chang
// TODO(bfredl): test for crazy edge cases, like we stand on a TAB or
// something? does this even do the right text change then?
int delta = orig_col - new_col;
- extmark_splice_cols(curbuf, curwin->w_cursor.lnum-1, new_col,
+ extmark_splice_cols(curbuf, (int)curwin->w_cursor.lnum - 1, new_col,
delta < 0 ? -delta : 0,
delta > 0 ? delta : 0,
kExtmarkUndo);
}
}
-/*
- * Truncate the space at the end of a line. This is to be used only in an
- * insert mode. It handles fixing the replace stack for REPLACE and VREPLACE
- * modes.
- */
+/// Truncate the space at the end of a line. This is to be used only in an
+/// insert mode. It handles fixing the replace stack for MODE_REPLACE and
+/// MODE_VREPLACE modes.
void truncate_spaces(char_u *line)
{
int i;
@@ -2022,12 +1970,10 @@ void truncate_spaces(char_u *line)
line[i + 1] = NUL;
}
-/*
- * Backspace the cursor until the given column. Handles REPLACE and VREPLACE
- * modes correctly. May also be used when not in insert mode at all.
- * Will attempt not to go before "col" even when there is a composing
- * character.
- */
+/// Backspace the cursor until the given column. Handles MODE_REPLACE and
+/// MODE_VREPLACE modes correctly. May also be used when not in insert mode at
+/// all. Will attempt not to go before "col" even when there is a composing
+/// character.
void backspace_until_column(int col)
{
while ((int)curwin->w_cursor.col > col) {
@@ -2056,7 +2002,7 @@ static bool del_char_after_col(int limit_col)
// composing character.
mb_adjust_cursor();
while (curwin->w_cursor.col < (colnr_T)limit_col) {
- int l = utf_ptr2len(get_cursor_pos_ptr());
+ int l = utf_ptr2len((char *)get_cursor_pos_ptr());
if (l == 0) { // end of line
break;
@@ -2096,11 +2042,12 @@ static void ins_ctrl_x(void)
ctrl_x_mode = CTRL_X_CMDLINE_CTRL_X;
}
- trigger_modechanged();
+ may_trigger_modechanged();
}
// Whether other than default completion has been selected.
bool ctrl_x_mode_not_default(void)
+ FUNC_ATTR_PURE
{
return ctrl_x_mode != CTRL_X_NORMAL;
}
@@ -2108,11 +2055,11 @@ bool ctrl_x_mode_not_default(void)
// Whether CTRL-X was typed without a following character,
// not including when in CTRL-X CTRL-V mode.
bool ctrl_x_mode_not_defined_yet(void)
+ FUNC_ATTR_PURE
{
return ctrl_x_mode == CTRL_X_NOT_DEFINED_YET;
}
-
/// Check that the "dict" or "tsr" option can be used.
///
/// @param dict_opt check "dict" when true, "tsr" when false.
@@ -2281,7 +2228,7 @@ int ins_compl_add_infercase(char_u *str_arg, int len, bool icase, char_u *fname,
? actual_len : actual_compl_length;
// Allocate wide character array for the completion and fill it.
- int *const wca = xmalloc(actual_len * sizeof(*wca));
+ int *const wca = xmalloc((size_t)actual_len * sizeof(*wca));
{
const char_u *p = str;
for (i = 0; i < actual_len; i++) {
@@ -2347,7 +2294,7 @@ int ins_compl_add_infercase(char_u *str_arg, int len, bool icase, char_u *fname,
char_u *p = IObuff;
i = 0;
while (i < actual_len && (p - IObuff + 6) < IOSIZE) {
- p += utf_char2bytes(wca[i++], p);
+ p += utf_char2bytes(wca[i++], (char *)p);
}
*p = NUL;
}
@@ -2399,9 +2346,9 @@ static int ins_compl_add(char_u *const str, int len, char_u *const fname,
}
#define FREE_CPTEXT(cptext, cptext_allocated) \
do { \
- if (cptext != NULL && cptext_allocated) { \
+ if ((cptext) != NULL && (cptext_allocated)) { \
for (size_t i = 0; i < CPT_COUNT; i++) { \
- xfree(cptext[i]); \
+ xfree((cptext)[i]); \
} \
} \
} while (0)
@@ -2441,7 +2388,7 @@ static int ins_compl_add(char_u *const str, int len, char_u *const fname,
if (flags & CP_ORIGINAL_TEXT) {
match->cp_number = 0;
}
- match->cp_str = vim_strnsave(str, len);
+ match->cp_str = vim_strnsave(str, (size_t)len);
// match-fname is:
// - compl_curr_match->cp_fname if it is a string equal to fname.
@@ -2559,8 +2506,8 @@ static void ins_compl_longest_match(compl_T *match)
p = compl_leader;
s = match->cp_str;
while (*p != NUL) {
- c1 = utf_ptr2char(p);
- c2 = utf_ptr2char(s);
+ c1 = utf_ptr2char((char *)p);
+ c2 = utf_ptr2char((char *)s);
if ((match->cp_flags & CP_ICASE)
? (mb_tolower(c1) != mb_tolower(c2))
@@ -2649,7 +2596,6 @@ void completeopt_was_set(void)
}
}
-
/*
* Start completion for the complete() function.
* "startcol" is where the matched text starts (1 is first column).
@@ -2674,7 +2620,7 @@ void set_completion(colnr_T startcol, list_T *list)
compl_length = (int)curwin->w_cursor.col - (int)startcol;
// compl_pattern doesn't need to be set
compl_orig_text = vim_strnsave(get_cursor_line_ptr() + compl_col,
- compl_length);
+ (size_t)compl_length);
if (p_ic) {
flags |= CP_ICASE;
}
@@ -2709,17 +2655,15 @@ void set_completion(colnr_T startcol, list_T *list)
show_pum(save_w_wrow, save_w_leftcol);
}
- trigger_modechanged();
+ may_trigger_modechanged();
ui_flush();
}
-
/* "compl_match_array" points the currently displayed list of entries in the
* popup menu. It is NULL when there is no popup menu. */
static pumitem_T *compl_match_array = NULL;
static int compl_match_arraysize;
-
/*
* Remove any popup menu.
*/
@@ -2736,7 +2680,7 @@ static bool pum_wanted(void)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
// "completeopt" must contain "menu" or "menuone"
- return vim_strchr(p_cot, 'm') != NULL;
+ return vim_strchr((char *)p_cot, 'm') != NULL;
}
/// Check that there are two or more matches to be shown in the popup menu.
@@ -2832,7 +2776,7 @@ void ins_compl_show_pum(void)
do {
if ((compl->cp_flags & CP_ORIGINAL_TEXT) == 0
&& (compl_leader == NULL
- || ins_compl_equal(compl, compl_leader, lead_len))) {
+ || ins_compl_equal(compl, compl_leader, (size_t)lead_len))) {
compl_match_arraysize++;
}
compl = compl->cp_next;
@@ -2842,9 +2786,9 @@ void ins_compl_show_pum(void)
}
assert(compl_match_arraysize >= 0);
- compl_match_array = xcalloc(compl_match_arraysize, sizeof(pumitem_T));
- /* If the current match is the original text don't find the first
- * match after it, don't highlight anything. */
+ compl_match_array = xcalloc((size_t)compl_match_arraysize, sizeof(pumitem_T));
+ // If the current match is the original text don't find the first
+ // match after it, don't highlight anything.
if (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) {
shown_match_ok = true;
}
@@ -2854,7 +2798,7 @@ void ins_compl_show_pum(void)
do {
if ((compl->cp_flags & CP_ORIGINAL_TEXT) == 0
&& (compl_leader == NULL
- || ins_compl_equal(compl, compl_leader, lead_len))) {
+ || ins_compl_equal(compl, compl_leader, (size_t)lead_len))) {
if (!shown_match_ok) {
if (compl == compl_shown_match || did_find_shown_match) {
/* This item is the shown match or this is the
@@ -2984,11 +2928,11 @@ static void ins_compl_dictionaries(char_u *dict_start, char_u *pat, int flags, i
size_t len = STRLEN(pat_esc) + 10;
ptr = xmalloc(len);
vim_snprintf((char *)ptr, len, "^\\s*\\zs\\V%s", pat_esc);
- regmatch.regprog = vim_regcomp(ptr, RE_MAGIC);
+ regmatch.regprog = vim_regcomp((char *)ptr, RE_MAGIC);
xfree(pat_esc);
xfree(ptr);
} else {
- regmatch.regprog = vim_regcomp(pat, p_magic ? RE_MAGIC : 0);
+ regmatch.regprog = vim_regcomp((char *)pat, p_magic ? RE_MAGIC : 0);
if (regmatch.regprog == NULL) {
goto theend;
}
@@ -3005,10 +2949,10 @@ static void ins_compl_dictionaries(char_u *dict_start, char_u *pat, int flags, i
/* Expand wildcards in the dictionary name, but do not allow
* backticks (for security, the 'dict' option may have been set in
* a modeline). */
- copy_option_part(&dict, buf, LSIZE, ",");
+ copy_option_part((char **)&dict, (char *)buf, LSIZE, ",");
if (!thesaurus && STRCMP(buf, "spell") == 0) {
count = -1;
- } else if (vim_strchr(buf, '`') != NULL
+ } else if (vim_strchr((char *)buf, '`') != NULL
|| expand_wildcards(1, &buf, &count, &files,
EW_FILE|EW_SILENT) != OK) {
count = 0;
@@ -3070,7 +3014,7 @@ static void ins_compl_files(int count, char_u **files, int thesaurus, int flags,
while (!got_int && !compl_interrupted
&& !vim_fgets(buf, LSIZE, fp)) {
ptr = buf;
- while (vim_regexec(regmatch, buf, (colnr_T)(ptr - buf))) {
+ while (vim_regexec(regmatch, (char *)buf, (colnr_T)(ptr - buf))) {
ptr = regmatch->startp[0];
if (CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)) {
ptr = find_line_end(ptr);
@@ -3101,7 +3045,7 @@ static void ins_compl_files(int count, char_u **files, int thesaurus, int flags,
// different classes, only separate words
// with single-byte non-word characters.
while (*ptr != NUL) {
- const int l = utfc_ptr2len(ptr);
+ const int l = utfc_ptr2len((char *)ptr);
if (l < 2 && !vim_iswordc(*ptr)) {
break;
@@ -3140,9 +3084,10 @@ static void ins_compl_files(int count, char_u **files, int thesaurus, int flags,
* Returns a pointer to the first char of the word. Also stops at a NUL.
*/
char_u *find_word_start(char_u *ptr)
+ FUNC_ATTR_PURE
{
while (*ptr != NUL && *ptr != '\n' && mb_get_class(ptr) <= 1) {
- ptr += utfc_ptr2len(ptr);
+ ptr += utfc_ptr2len((char *)ptr);
}
return ptr;
}
@@ -3152,11 +3097,12 @@ char_u *find_word_start(char_u *ptr)
* Returns a pointer to just after the word.
*/
char_u *find_word_end(char_u *ptr)
+ FUNC_ATTR_PURE
{
const int start_class = mb_get_class(ptr);
if (start_class > 1) {
while (*ptr != NUL) {
- ptr += utfc_ptr2len(ptr);
+ ptr += utfc_ptr2len((char *)ptr);
if (mb_get_class(ptr) != start_class) {
break;
}
@@ -3382,7 +3328,6 @@ static char_u *ins_compl_mode(void)
return (char_u *)"";
}
-
/*
* Delete one character before the cursor and show the subset of the matches
* that match the word that is now before the cursor.
@@ -3419,7 +3364,7 @@ static int ins_compl_bs(void)
line = get_cursor_line_ptr();
xfree(compl_leader);
- compl_leader = vim_strnsave(line + compl_col, (int)p_off - compl_col);
+ compl_leader = vim_strnsave(line + compl_col, (size_t)(p_off - (ptrdiff_t)compl_col));
ins_compl_new_leader();
if (compl_shown_match != NULL) {
// Make sure current match is not a hidden item.
@@ -3504,11 +3449,11 @@ static void ins_compl_addleader(int c)
return;
}
if ((cc = utf_char2len(c)) > 1) {
- char_u buf[MB_MAXBYTES + 1];
+ char buf[MB_MAXBYTES + 1];
- utf_char2bytes(c, buf);
+ utf_char2bytes(c, (char *)buf);
buf[cc] = NUL;
- ins_char_bytes(buf, cc);
+ ins_char_bytes((char_u *)buf, (size_t)cc);
} else {
ins_char(c);
}
@@ -3520,7 +3465,7 @@ static void ins_compl_addleader(int c)
xfree(compl_leader);
compl_leader = vim_strnsave(get_cursor_line_ptr() + compl_col,
- curwin->w_cursor.col - compl_col);
+ (size_t)(curwin->w_cursor.col - compl_col));
ins_compl_new_leader();
}
@@ -3580,8 +3525,7 @@ static void ins_compl_addfrommatch(void)
for (cp = compl_shown_match->cp_next; cp != NULL
&& cp != compl_first_match; cp = cp->cp_next) {
if (compl_leader == NULL
- || ins_compl_equal(cp, compl_leader,
- (int)STRLEN(compl_leader))) {
+ || ins_compl_equal(cp, compl_leader, STRLEN(compl_leader))) {
p = cp->cp_str;
break;
}
@@ -3594,7 +3538,7 @@ static void ins_compl_addfrommatch(void)
}
}
p += len;
- c = utf_ptr2char(p);
+ c = utf_ptr2char((char *)p);
ins_compl_addleader(c);
}
@@ -3799,6 +3743,7 @@ static bool ins_compl_prep(int c)
}
bool want_cindent = (can_cindent && cindent_on());
+
// When completing whole lines: fix indent for 'cindent'.
// Otherwise, break line if it's too long.
if (compl_cont_mode == CTRL_X_WHOLE_LINE) {
@@ -3889,7 +3834,7 @@ static bool ins_compl_prep(int c)
ins_apply_autocmds(EVENT_COMPLETEDONE);
}
- trigger_modechanged();
+ may_trigger_modechanged();
/* reset continue_* if we left expansion-mode, if we stay they'll be
* (re)set properly in ins_complete() */
@@ -3931,7 +3876,7 @@ static void ins_compl_fixRedoBufForLeader(char_u *ptr_arg)
} else {
len = 0;
}
- AppendToRedobuffLit(ptr + len, -1);
+ AppendToRedobuffLit((char *)ptr + len, -1);
}
/*
@@ -3952,8 +3897,7 @@ static buf_T *ins_compl_next_buf(buf_T *buf, int flag)
}
assert(wp);
while ((wp = (wp->w_next != NULL ? wp->w_next : firstwin)) != curwin
- && wp->w_buffer->b_scanned) {
- }
+ && wp->w_buffer->b_scanned) {}
buf = wp->w_buffer;
} else {
/* 'b' (just loaded buffers), 'u' (just non-loaded buffers) or 'U'
@@ -3964,13 +3908,11 @@ static buf_T *ins_compl_next_buf(buf_T *buf, int flag)
? buf->b_p_bl
: (!buf->b_p_bl
|| (buf->b_ml.ml_mfp == NULL) != (flag == 'u')))
- || buf->b_scanned)) {
- }
+ || buf->b_scanned)) {}
}
return buf;
}
-
/// Get the user-defined completion function name for completion 'type'
static char_u *get_complete_funcname(int type)
{
@@ -3996,8 +3938,6 @@ static void expand_by_function(int type, char_u *base)
dict_T *matchdict = NULL;
char_u *funcname;
pos_T pos;
- win_T *curwin_save;
- buf_T *curbuf_save;
typval_T rettv;
const int save_State = State;
@@ -4013,14 +3953,16 @@ static void expand_by_function(int type, char_u *base)
args[1].v_type = VAR_STRING;
args[2].v_type = VAR_UNKNOWN;
args[0].vval.v_number = 0;
- args[1].vval.v_string = base != NULL ? base : (char_u *)"";
+ args[1].vval.v_string = base != NULL ? (char *)base : "";
pos = curwin->w_cursor;
- curwin_save = curwin;
- curbuf_save = curbuf;
+ // Lock the text to avoid weird things from happening. Also disallow
+ // switching to another window, it should not be needed and may end up in
+ // Insert mode in another buffer.
+ textlock++;
// Call a function, which returns a list or dict.
- if (call_vim_function(funcname, 2, args, &rettv) == OK) {
+ if (call_vim_function((char *)funcname, 2, args, &rettv) == OK) {
switch (rettv.v_type) {
case VAR_LIST:
matchlist = rettv.vval.v_list;
@@ -4036,11 +3978,8 @@ static void expand_by_function(int type, char_u *base)
break;
}
}
+ textlock--;
- if (curwin_save != curwin || curbuf_save != curbuf) {
- emsg(_(e_complwin));
- goto theend;
- }
curwin->w_cursor = pos; // restore the cursor position
validate_cursor();
if (!equalpos(curwin->w_cursor, pos)) {
@@ -4254,9 +4193,8 @@ static int ins_compl_get_exp(pos_T *ini)
// Remember the first match so that the loop stops when we
// wrap and come back there a second time.
set_match_pos = true;
- } else if (vim_strchr((char_u *)"buwU", *e_cpt) != NULL
- && (ins_buf =
- ins_compl_next_buf(ins_buf, *e_cpt)) != curbuf) {
+ } else if (vim_strchr("buwU", *e_cpt) != NULL
+ && (ins_buf = ins_compl_next_buf(ins_buf, *e_cpt)) != curbuf) {
// Scan a buffer, but not the current one.
if (ins_buf->b_ml.ml_mfp != NULL) { // loaded buffer
compl_started = true;
@@ -4270,7 +4208,7 @@ static int ins_compl_get_exp(pos_T *ini)
continue;
}
type = CTRL_X_DICTIONARY;
- dict = ins_buf->b_fname;
+ dict = (char_u *)ins_buf->b_fname;
dict_f = DICT_EXACT;
}
msg_hist_off = true; // reset in msg_trunc_attr()
@@ -4310,7 +4248,7 @@ static int ins_compl_get_exp(pos_T *ini)
}
// in any case e_cpt is advanced to the next entry
- (void)copy_option_part(&e_cpt, IObuff, IOSIZE, ",");
+ (void)copy_option_part((char **)&e_cpt, (char *)IObuff, IOSIZE, ",");
found_all = true;
if (type == -1) {
@@ -4366,7 +4304,7 @@ static int ins_compl_get_exp(pos_T *ini)
if (find_tags(compl_pattern, &num_matches, &matches,
TAG_REGEXP | TAG_NAMES | TAG_NOIC | TAG_INS_COMP
| (l_ctrl_x_mode != CTRL_X_NORMAL ? TAG_VERBOSE : 0),
- TAG_MANY, curbuf->b_ffname) == OK && num_matches > 0) {
+ TAG_MANY, (char_u *)curbuf->b_ffname) == OK && num_matches > 0) {
ins_compl_add_matches(num_matches, matches, p_ic);
}
g_tag_at_cursor = false;
@@ -4507,7 +4445,7 @@ static int ins_compl_get_exp(pos_T *ini)
}
ptr = ml_get_buf(ins_buf, pos->lnum + 1, false);
if (!p_paste) {
- ptr = skipwhite(ptr);
+ ptr = (char_u *)skipwhite((char *)ptr);
}
}
len = (int)STRLEN(ptr);
@@ -4535,7 +4473,7 @@ static int ins_compl_get_exp(pos_T *ini)
// compl_length, so the next STRNCPY always works -- Acevedo
STRNCPY(IObuff, ptr, len);
ptr = ml_get_buf(ins_buf, pos->lnum + 1, false);
- tmp_ptr = ptr = skipwhite(ptr);
+ tmp_ptr = ptr = (char_u *)skipwhite((char *)ptr);
// Find start of next word.
tmp_ptr = find_word_start(tmp_ptr);
// Find end of next word.
@@ -4569,7 +4507,8 @@ static int ins_compl_get_exp(pos_T *ini)
}
}
}
- if (ins_compl_add_infercase(ptr, len, p_ic, ins_buf == curbuf ? NULL : ins_buf->b_sfname,
+ if (ins_compl_add_infercase(ptr, len, p_ic,
+ ins_buf == curbuf ? NULL : (char_u *)ins_buf->b_sfname,
0, cont_s_ipos) != NOTDONE) {
found_new_match = OK;
break;
@@ -4640,7 +4579,7 @@ static int ins_compl_get_exp(pos_T *ini)
compl_curr_match = compl_old_match;
}
}
- trigger_modechanged();
+ may_trigger_modechanged();
return i;
}
@@ -4745,12 +4684,10 @@ static int ins_compl_next(int allow_get_expansion, int count, int insert_match,
/* If we didn't find it searching forward, and compl_shows_dir is
* backward, find the last match. */
if (compl_shows_dir == BACKWARD
- && !ins_compl_equal(compl_shown_match,
- compl_leader, (int)STRLEN(compl_leader))
+ && !ins_compl_equal(compl_shown_match, compl_leader, STRLEN(compl_leader))
&& (compl_shown_match->cp_next == NULL
|| compl_shown_match->cp_next == compl_first_match)) {
- while (!ins_compl_equal(compl_shown_match,
- compl_leader, (int)STRLEN(compl_leader))
+ while (!ins_compl_equal(compl_shown_match, compl_leader, STRLEN(compl_leader))
&& compl_shown_match->cp_prev != NULL
&& compl_shown_match->cp_prev != compl_first_match) {
compl_shown_match = compl_shown_match->cp_prev;
@@ -4886,15 +4823,15 @@ static int ins_compl_next(int allow_get_expansion, int count, int insert_match,
*/
if (compl_shown_match->cp_fname != NULL) {
char *lead = _("match in file");
- int space = sc_col - vim_strsize((char_u *)lead) - 2;
- char_u *s;
- char_u *e;
+ int space = sc_col - vim_strsize(lead) - 2;
+ char *s;
+ char *e;
if (space > 0) {
// We need the tail that fits. With double-byte encoding going
// back from the end is very slow, thus go from the start and keep
// the text that fits in "space" between "s" and "e".
- for (s = e = compl_shown_match->cp_fname; *e != NUL; MB_PTR_ADV(e)) {
+ for (s = e = (char *)compl_shown_match->cp_fname; *e != NUL; MB_PTR_ADV(e)) {
space -= ptr2cells(e);
while (space < 0) {
space += ptr2cells(s);
@@ -4903,7 +4840,7 @@ static int ins_compl_next(int allow_get_expansion, int count, int insert_match,
}
msg_hist_off = true;
vim_snprintf((char *)IObuff, IOSIZE, "%s %s%s", lead,
- s > compl_shown_match->cp_fname ? "<" : "", s);
+ (char_u *)s > compl_shown_match->cp_fname ? "<" : "", s);
msg((char *)IObuff);
msg_hist_off = false;
redraw_cmdline = false; // don't overwrite!
@@ -4986,7 +4923,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 +4953,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 +4987,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;
@@ -5107,10 +5045,10 @@ static int ins_complete(int c, bool enable_pum)
|| ctrl_x_mode == CTRL_X_PATH_PATTERNS
|| ctrl_x_mode == CTRL_X_PATH_DEFINES) {
if (compl_startpos.lnum != curwin->w_cursor.lnum) {
- /* line (probably) wrapped, set compl_startpos to the
- * first non_blank in the line, if it is not a wordchar
- * include it to get a better pattern, but then we don't
- * want the "\\<" prefix, check it bellow */
+ // line (probably) wrapped, set compl_startpos to the
+ // first non_blank in the line, if it is not a wordchar
+ // include it to get a better pattern, but then we don't
+ // want the "\\<" prefix, check it below.
compl_col = (colnr_T)getwhitecols(line);
compl_startpos.col = compl_col;
compl_startpos.lnum = curwin->w_cursor.lnum;
@@ -5121,8 +5059,9 @@ static int ins_complete(int c, bool enable_pum)
* mode but first we need to redefine compl_startpos */
if (compl_cont_status & CONT_S_IPOS) {
compl_cont_status |= CONT_SOL;
- compl_startpos.col = (colnr_T)(skipwhite(line + compl_length
- + compl_startpos.col) - line);
+ compl_startpos.col = (colnr_T)((char_u *)skipwhite((char *)line
+ + compl_length
+ + compl_startpos.col) - line);
}
compl_col = compl_startpos.col;
}
@@ -5167,15 +5106,14 @@ static int ins_complete(int c, bool enable_pum)
if ((compl_cont_status & CONT_SOL)
|| ctrl_x_mode == CTRL_X_PATH_DEFINES) {
if (!(compl_cont_status & CONT_ADDING)) {
- while (--startcol >= 0 && vim_isIDc(line[startcol])) {
- }
+ while (--startcol >= 0 && vim_isIDc(line[startcol])) {}
compl_col += ++startcol;
compl_length = curs_col - startcol;
}
if (p_ic) {
compl_pattern = str_foldcase(line + compl_col, compl_length, NULL, 0);
} else {
- compl_pattern = vim_strnsave(line + compl_col, compl_length);
+ compl_pattern = vim_strnsave(line + compl_col, (size_t)compl_length);
}
} else if (compl_cont_status & CONT_ADDING) {
char_u *prefix = (char_u *)"\\<";
@@ -5239,7 +5177,7 @@ static int ins_complete(int c, bool enable_pum)
if (p_ic) {
compl_pattern = str_foldcase(line + compl_col, compl_length, NULL, 0);
} else {
- compl_pattern = vim_strnsave(line + compl_col, compl_length);
+ compl_pattern = vim_strnsave(line + compl_col, (size_t)compl_length);
}
} else if (ctrl_x_mode == CTRL_X_FILES) {
// Go back to just before the first filename character.
@@ -5247,10 +5185,10 @@ static int ins_complete(int c, bool enable_pum)
char_u *p = line + startcol;
MB_PTR_BACK(line, p);
- while (p > line && vim_isfilec(utf_ptr2char(p))) {
+ while (p > line && vim_isfilec(utf_ptr2char((char *)p))) {
MB_PTR_BACK(line, p);
}
- if (p == line && vim_isfilec(utf_ptr2char(p))) {
+ if (p == line && vim_isfilec(utf_ptr2char((char *)p))) {
startcol = 0;
} else {
startcol = (int)(p - line) + 1;
@@ -5259,9 +5197,9 @@ static int ins_complete(int c, bool enable_pum)
compl_col += startcol;
compl_length = (int)curs_col - startcol;
- compl_pattern = addstar(line + compl_col, compl_length, EXPAND_FILES);
+ compl_pattern = addstar(line + compl_col, (size_t)compl_length, EXPAND_FILES);
} else if (ctrl_x_mode == CTRL_X_CMDLINE || ctrl_x_mode == CTRL_X_CMDLINE_CTRL_X) {
- compl_pattern = vim_strnsave(line, curs_col);
+ compl_pattern = vim_strnsave(line, (size_t)curs_col);
set_cmd_context(&compl_xp, compl_pattern,
(int)STRLEN(compl_pattern), curs_col, false);
if (compl_xp.xp_context == EXPAND_UNSUCCESSFUL
@@ -5270,7 +5208,7 @@ static int ins_complete(int c, bool enable_pum)
// "pattern not found" message.
compl_col = curs_col;
} else {
- compl_col = (int)(compl_xp.xp_pattern - compl_pattern);
+ compl_col = (int)((char_u *)compl_xp.xp_pattern - compl_pattern);
}
compl_length = curs_col - compl_col;
} else if (ctrl_x_mode == CTRL_X_FUNCTION || ctrl_x_mode == CTRL_X_OMNI
@@ -5279,8 +5217,6 @@ static int ins_complete(int c, bool enable_pum)
// set to 1 to obtain the length of text to use for completion.
char_u *funcname;
pos_T pos;
- win_T *curwin_save;
- buf_T *curbuf_save;
const int save_State = State;
// Call 'completefunc' or 'omnifunc' and get pattern length as a string
@@ -5298,18 +5234,14 @@ static int ins_complete(int c, bool enable_pum)
args[1].v_type = VAR_STRING;
args[2].v_type = VAR_UNKNOWN;
args[0].vval.v_number = 1;
- args[1].vval.v_string = (char_u *)"";
+ args[1].vval.v_string = "";
pos = curwin->w_cursor;
- curwin_save = curwin;
- curbuf_save = curbuf;
- int col = call_func_retnr(funcname, 2, args);
+ textlock++;
+ colnr_T col = (colnr_T)call_func_retnr((char *)funcname, 2, args);
+ textlock--;
State = save_State;
- if (curwin_save != curwin || curbuf_save != curbuf) {
- emsg(_(e_complwin));
- return FAIL;
- }
curwin->w_cursor = pos; // restore the cursor position
validate_cursor();
if (!equalpos(curwin->w_cursor, pos)) {
@@ -5317,12 +5249,13 @@ static int ins_complete(int c, bool enable_pum)
return FAIL;
}
- /* Return value -2 means the user complete function wants to
- * cancel the complete without an error.
- * Return value -3 does the same as -2 and leaves CTRL-X mode.*/
- if (col == -2) {
+ // Return value -2 means the user complete function wants to cancel the
+ // complete without an error, do the same if the function did not execute
+ // successfully.
+ if (col == -2 || aborting()) {
return FAIL;
}
+ // Return value -3 does the same as -2 and leaves CTRL-X mode.
if (col == -3) {
ctrl_x_mode = CTRL_X_NORMAL;
edit_submode = NULL;
@@ -5348,7 +5281,7 @@ static int ins_complete(int c, bool enable_pum)
* it may have become invalid. */
line = ml_get(curwin->w_cursor.lnum);
compl_length = curs_col - compl_col;
- compl_pattern = vim_strnsave(line + compl_col, compl_length);
+ compl_pattern = vim_strnsave(line + compl_col, (size_t)compl_length);
} else if (ctrl_x_mode == CTRL_X_SPELL) {
if (spell_bad_len > 0) {
assert(spell_bad_len <= INT_MAX);
@@ -5365,7 +5298,7 @@ static int ins_complete(int c, bool enable_pum)
}
// Need to obtain "line" again, it may have become invalid.
line = ml_get(curwin->w_cursor.lnum);
- compl_pattern = vim_strnsave(line + compl_col, compl_length);
+ compl_pattern = vim_strnsave(line + compl_col, (size_t)compl_length);
} else {
internal_error("ins_complete()");
return FAIL;
@@ -5402,7 +5335,7 @@ static int ins_complete(int c, bool enable_pum)
// Always add completion for the original text.
xfree(compl_orig_text);
- compl_orig_text = vim_strnsave(line + compl_col, compl_length);
+ compl_orig_text = vim_strnsave(line + compl_col, (size_t)compl_length);
if (p_ic) {
flags |= CP_ICASE;
}
@@ -5436,7 +5369,6 @@ static int ins_complete(int c, bool enable_pum)
save_w_leftcol = curwin->w_leftcol;
n = ins_compl_next(true, ins_compl_key2count(c), insert_match, false);
-
if (n > 1) { // all matches have been found
compl_matches = n;
}
@@ -5586,7 +5518,7 @@ static unsigned quote_meta(char_u *dest, char_u *src, int len)
*dest++ = *src;
}
// Copy remaining bytes of a multibyte character.
- const int mb_len = utfc_ptr2len(src) - 1;
+ const int mb_len = utfc_ptr2len((char *)src) - 1;
if (mb_len > 0 && len >= mb_len) {
for (int i = 0; i < mb_len; i++) {
len--;
@@ -5604,13 +5536,13 @@ static unsigned quote_meta(char_u *dest, char_u *src, int len)
return m;
}
-/*
- * Next character is interpreted literally.
- * A one, two or three digit decimal number is interpreted as its byte value.
- * If one or two digits are entered, the next character is given to vungetc().
- * For Unicode a character > 255 may be returned.
- */
-int get_literal(void)
+/// Next character is interpreted literally.
+/// A one, two or three digit decimal number is interpreted as its byte value.
+/// If one or two digits are entered, the next character is given to vungetc().
+/// For Unicode a character > 255 may be returned.
+///
+/// @param no_simplify do not include modifiers into the key
+int get_literal(bool no_simplify)
{
int cc;
int nc;
@@ -5628,8 +5560,15 @@ int get_literal(void)
i = 0;
for (;;) {
nc = plain_vgetc();
- if (!(State & CMDLINE)
- && MB_BYTE2LEN_CHECK(nc) == 1) {
+ if (!no_simplify) {
+ nc = merge_modifiers(nc, &mod_mask);
+ }
+ 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 & MODE_CMDLINE) == 0 && MB_BYTE2LEN_CHECK(nc) == 1) {
add_to_showcmd(nc);
}
if (nc == 'x' || nc == 'X') {
@@ -5695,6 +5634,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;
@@ -5726,8 +5667,8 @@ static void insert_special(int c, int allow_modmask, int ctrlv)
}
p[len - 1] = NUL;
ins_str(p);
- AppendToRedobuffLit(p, -1);
- ctrlv = FALSE;
+ AppendToRedobuffLit((char *)p, -1);
+ ctrlv = false;
}
}
if (stop_arrow() == OK) {
@@ -5746,9 +5687,8 @@ static void insert_special(int c, int allow_modmask, int ctrlv)
*/
#define ISSPECIAL(c) ((c) < ' ' || (c) >= DEL || (c) == '0' || (c) == '^')
-#define WHITECHAR(cc) ( \
- ascii_iswhite(cc) \
- && !utf_iscomposing(utf_ptr2char(get_cursor_pos_ptr() + 1)))
+#define WHITECHAR(cc) (ascii_iswhite(cc) \
+ && !utf_iscomposing(utf_ptr2char((char *)get_cursor_pos_ptr() + 1)))
///
/// "flags": INSCHAR_FORMAT - force formatting
@@ -5771,21 +5711,19 @@ void insertchar(int c, int flags, int second_indent)
const int textwidth = comp_textwidth(force_format);
const bool fo_ins_blank = has_format_option(FO_INS_BLANK);
- /*
- * Try to break the line in two or more pieces when:
- * - Always do this if we have been called to do formatting only.
- * - Always do this when 'formatoptions' has the 'a' flag and the line
- * ends in white space.
- * - Otherwise:
- * - Don't do this if inserting a blank
- * - Don't do this if an existing character is being replaced, unless
- * we're in VREPLACE mode.
- * - Do this if the cursor is not on the line where insert started
- * or - 'formatoptions' doesn't have 'l' or the line was not too long
- * before the insert.
- * - 'formatoptions' doesn't have 'b' or a blank was inserted at or
- * before 'textwidth'
- */
+ // Try to break the line in two or more pieces when:
+ // - Always do this if we have been called to do formatting only.
+ // - Always do this when 'formatoptions' has the 'a' flag and the line
+ // ends in white space.
+ // - Otherwise:
+ // - Don't do this if inserting a blank
+ // - Don't do this if an existing character is being replaced, unless
+ // we're in MODE_VREPLACE state.
+ // - Do this if the cursor is not on the line where insert started
+ // or - 'formatoptions' doesn't have 'l' or the line was not too long
+ // before the insert.
+ // - 'formatoptions' doesn't have 'b' or a blank was inserted at or
+ // before 'textwidth'
if (textwidth > 0
&& (force_format
|| (!ascii_iswhite(c)
@@ -5821,22 +5759,18 @@ void insertchar(int c, int flags, int second_indent)
// Check whether this character should end a comment.
if (did_ai && c == end_comment_pending) {
- char_u *line;
char_u lead_end[COM_MAX_LEN]; // end-comment string
- int middle_len, end_len;
- int i;
- /*
- * Need to remove existing (middle) comment leader and insert end
- * comment leader. First, check what comment leader we can find.
- */
- i = get_leader_len(line = get_cursor_line_ptr(), &p, false, true);
- if (i > 0 && vim_strchr(p, COM_MIDDLE) != NULL) { // Just checking
+ // Need to remove existing (middle) comment leader and insert end
+ // comment leader. First, check what comment leader we can find.
+ char_u *line = get_cursor_line_ptr();
+ int i = get_leader_len((char *)line, (char **)&p, false, true);
+ if (i > 0 && vim_strchr((char *)p, COM_MIDDLE) != NULL) { // Just checking
// Skip middle-comment string
while (*p && p[-1] != ':') { // find end of middle flags
p++;
}
- middle_len = copy_option_part(&p, lead_end, COM_MAX_LEN, ",");
+ int middle_len = (int)copy_option_part((char **)&p, (char *)lead_end, COM_MAX_LEN, ",");
// Don't count trailing white space for middle_len
while (middle_len > 0 && ascii_iswhite(lead_end[middle_len - 1])) {
middle_len--;
@@ -5846,12 +5780,11 @@ void insertchar(int c, int flags, int second_indent)
while (*p && p[-1] != ':') { // find end of end flags
p++;
}
- end_len = copy_option_part(&p, lead_end, COM_MAX_LEN, ",");
+ int end_len = (int)copy_option_part((char **)&p, (char *)lead_end, COM_MAX_LEN, ",");
// Skip white space before the cursor
i = curwin->w_cursor.col;
- while (--i >= 0 && ascii_iswhite(line[i])) {
- }
+ while (--i >= 0 && ascii_iswhite(line[i])) {}
i++;
// Skip to before the middle leader
@@ -5864,7 +5797,7 @@ void insertchar(int c, int flags, int second_indent)
// Insert the end-comment string, except for the last
// character, which will get inserted as normal later.
- ins_bytes_len(lead_end, end_len - 1);
+ ins_bytes_len(lead_end, (size_t)(end_len - 1));
}
}
}
@@ -5896,7 +5829,7 @@ void insertchar(int c, int flags, int second_indent)
int i;
colnr_T virtcol = 0;
- buf[0] = c;
+ buf[0] = (char_u)c;
i = 1;
if (textwidth > 0) {
virtcol = get_nolist_virtcol();
@@ -5918,11 +5851,11 @@ void insertchar(int c, int flags, int second_indent)
if (p_hkmap && KeyTyped) {
c = hkmap(c); // Hebrew mode mapping
}
- buf[i++] = c;
+ buf[i++] = (char_u)c;
}
do_digraph(-1); // clear digraphs
- do_digraph(buf[i-1]); // may be the start of a digraph
+ do_digraph(buf[i - 1]); // may be the start of a digraph
buf[i] = NUL;
ins_str(buf);
if (flags & INSCHAR_CTRLV) {
@@ -5932,17 +5865,17 @@ void insertchar(int c, int flags, int second_indent)
i = 0;
}
if (buf[i] != NUL) {
- AppendToRedobuffLit(buf + i, -1);
+ AppendToRedobuffLit((char *)buf + i, -1);
}
} else {
int cc;
if ((cc = utf_char2len(c)) > 1) {
- char_u buf[MB_MAXBYTES + 1];
+ char buf[MB_MAXBYTES + 1];
- utf_char2bytes(c, buf);
+ utf_char2bytes(c, (char *)buf);
buf[cc] = NUL;
- ins_char_bytes(buf, cc);
+ ins_char_bytes((char_u *)buf, (size_t)cc);
AppendCharToRedobuff(c);
} else {
ins_char(c);
@@ -6006,6 +5939,7 @@ static void internal_format(int textwidth, int second_indent, int flags, int for
char_u *saved_text = NULL;
colnr_T col;
colnr_T end_col;
+ bool did_do_comment = false;
virtcol = get_nolist_virtcol()
+ char2cells(c != NUL ? c : gchar_cursor());
@@ -6022,7 +5956,18 @@ static void internal_format(int textwidth, int second_indent, int flags, int for
// Don't break until after the comment leader
if (do_comments) {
- leader_len = get_leader_len(get_cursor_line_ptr(), NULL, false, true);
+ char_u *line = get_cursor_line_ptr();
+ leader_len = get_leader_len((char *)line, NULL, false, true);
+ if (leader_len == 0 && curbuf->b_p_cin) {
+ // Check for a line comment after code.
+ int comment_start = check_linecomment(line);
+ if (comment_start != MAXCOL) {
+ leader_len = get_leader_len((char *)line + comment_start, NULL, false, true);
+ if (leader_len != 0) {
+ leader_len += comment_start;
+ }
+ }
+ }
} else {
leader_len = 0;
}
@@ -6121,8 +6066,7 @@ static void internal_format(int textwidth, int second_indent, int flags, int for
if (curwin->w_cursor.col <= (colnr_T)wantcol) {
break;
}
- } else if ((cc >= 0x100 || !utf_allow_break_before(cc))
- && fo_multibyte) {
+ } else if ((cc >= 0x100 || !utf_allow_break_before(cc)) && fo_multibyte) {
int ncc;
bool allow_break;
@@ -6226,11 +6170,9 @@ static void internal_format(int textwidth, int second_indent, int flags, int for
// Going to break the line, remove any "$" now.
undisplay_dollar();
- /*
- * Offset between cursor position and line break is used by replace
- * stack functions. VREPLACE does not use this, and backspaces
- * over the text instead.
- */
+ // Offset between cursor position and line break is used by replace
+ // stack functions. MODE_VREPLACE does not use this, and backspaces
+ // over the text instead.
if (State & VREPLACE_FLAG) {
orig_col = startcol; // Will start backspacing from here
} else {
@@ -6252,10 +6194,8 @@ static void internal_format(int textwidth, int second_indent, int flags, int for
}
if (State & VREPLACE_FLAG) {
- /*
- * In VREPLACE mode, we will backspace over the text to be
- * wrapped, so save a copy now to put on the next line.
- */
+ // In MODE_VREPLACE state, we will backspace over the text to be
+ // wrapped, so save a copy now to put on the next line.
saved_text = vim_strsave(get_cursor_pos_ptr());
curwin->w_cursor.col = orig_col;
saved_text[startcol] = NUL;
@@ -6278,12 +6218,20 @@ static void internal_format(int textwidth, int second_indent, int flags, int for
open_line(FORWARD, OPENLINE_DELSPACES + OPENLINE_MARKFIX
+ (fo_white_par ? OPENLINE_KEEPTRAIL : 0)
+ (do_comments ? OPENLINE_DO_COM : 0)
+ + OPENLINE_FORMAT
+ ((flags & INSCHAR_COM_LIST) ? OPENLINE_COM_LIST : 0),
- ((flags & INSCHAR_COM_LIST) ? second_indent : old_indent));
+ ((flags & INSCHAR_COM_LIST) ? second_indent : old_indent),
+ &did_do_comment);
if (!(flags & INSCHAR_COM_LIST)) {
old_indent = 0;
}
+ // If a comment leader was inserted, may also do this on a following
+ // line.
+ if (did_do_comment) {
+ no_leader = false;
+ }
+
replace_offset = 0;
if (first_line) {
if (!(flags & INSCHAR_COM_LIST)) {
@@ -6320,10 +6268,8 @@ static void internal_format(int textwidth, int second_indent, int flags, int for
}
if (State & VREPLACE_FLAG) {
- /*
- * In VREPLACE mode we have backspaced over the text to be
- * moved, now we re-insert it into the new line.
- */
+ // In MODE_VREPLACE state we have backspaced over the text to be
+ // moved, now we re-insert it into the new line.
ins_bytes(saved_text);
xfree(saved_text);
} else {
@@ -6349,7 +6295,7 @@ static void internal_format(int textwidth, int second_indent, int flags, int for
}
if (save_char != NUL) { // put back space after cursor
- pchar_cursor(save_char);
+ pchar_cursor((char_u)save_char);
}
curwin->w_p_lbr = has_lbr;
@@ -6411,7 +6357,7 @@ void auto_format(bool trailblank, bool prev_line)
// With the 'c' flag in 'formatoptions' and 't' missing: only format
// comments.
if (has_format_option(FO_WRAP_COMS) && !has_format_option(FO_WRAP)
- && get_leader_len(old, NULL, false, true) == 0) {
+ && get_leader_len((char *)old, NULL, false, true) == 0) {
return;
}
@@ -6432,7 +6378,7 @@ void auto_format(bool trailblank, bool prev_line)
* be adjusted for the text formatting.
*/
saved_cursor = pos;
- format_lines((linenr_T)-1, FALSE);
+ format_lines((linenr_T) - 1, false);
curwin->w_cursor = saved_cursor;
saved_cursor.lnum = 0;
@@ -6452,10 +6398,10 @@ void auto_format(bool trailblank, bool prev_line)
new = get_cursor_line_ptr();
len = (colnr_T)STRLEN(new);
if (curwin->w_cursor.col == len) {
- pnew = vim_strnsave(new, len + 2);
+ pnew = vim_strnsave(new, (size_t)len + 2);
pnew[len] = ' ';
pnew[len + 1] = NUL;
- ml_replace(curwin->w_cursor.lnum, pnew, false);
+ ml_replace(curwin->w_cursor.lnum, (char *)pnew, false);
// remove the space later
did_add_space = true;
} else {
@@ -6506,11 +6452,11 @@ static void check_auto_format(bool end_insert)
/// @param ff force formatting (for "gq" command)
int comp_textwidth(bool ff)
{
- int textwidth = curbuf->b_p_tw;
+ int textwidth = (int)curbuf->b_p_tw;
if (textwidth == 0 && curbuf->b_p_wm) {
// The width is the window width minus 'wrapmargin' minus all the
// things that add to the margin.
- textwidth = curwin->w_width_inner - curbuf->b_p_wm;
+ textwidth = curwin->w_width_inner - (int)curbuf->b_p_wm;
if (cmdwin_type != 0) {
textwidth -= 1;
}
@@ -6758,13 +6704,8 @@ static void stop_insert(pos_T *end_insert_pos, int esc, int nomove)
// <C-S-Right> may have started Visual mode, adjust the position for
// deleted characters.
- if (VIsual_active && VIsual.lnum == curwin->w_cursor.lnum) {
- int len = (int)STRLEN(get_cursor_line_ptr());
-
- if (VIsual.col > len) {
- VIsual.col = len;
- VIsual.coladd = 0;
- }
+ if (VIsual_active) {
+ check_visual_pos();
}
}
}
@@ -6812,34 +6753,6 @@ void free_last_insert(void)
#endif
-/// Add character "c" to buffer "s"
-///
-/// Escapes the special meaning of K_SPECIAL and CSI, handles multi-byte
-/// characters.
-///
-/// @param[in] c Character to add.
-/// @param[out] s Buffer to add to. Must have at least MB_MAXBYTES + 1 bytes.
-///
-/// @return Pointer to after the added bytes.
-char_u *add_char2buf(int c, char_u *s)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
-{
- char_u temp[MB_MAXBYTES + 1];
- 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.
- if (c == K_SPECIAL) {
- *s++ = K_SPECIAL;
- *s++ = KS_SPECIAL;
- *s++ = KE_FILLER;
- } else {
- *s++ = c;
- }
- }
- return s;
-}
-
/*
* move cursor to start of line
* if flags & BL_WHITE move to first non-white
@@ -6877,14 +6790,14 @@ void beginline(int flags)
int oneright(void)
{
- char_u *ptr;
+ char *ptr;
int l;
if (virtual_active()) {
pos_T prevpos = curwin->w_cursor;
// Adjust for multi-wide char (excluding TAB)
- ptr = get_cursor_pos_ptr();
+ ptr = (char *)get_cursor_pos_ptr();
coladvance(getviscol() + ((*ptr != TAB && vim_isprintc(utf_ptr2char(ptr))) ?
ptr2cells(ptr) : 1));
curwin->w_set_curswant = true;
@@ -6893,7 +6806,7 @@ int oneright(void)
|| prevpos.coladd != curwin->w_cursor.coladd) ? OK : FAIL;
}
- ptr = get_cursor_pos_ptr();
+ ptr = (char *)get_cursor_pos_ptr();
if (*ptr == NUL) {
return FAIL; // already at the very end
}
@@ -6902,8 +6815,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;
@@ -6936,12 +6848,9 @@ int oneleft(void)
}
if (curwin->w_cursor.coladd == 1) {
- char_u *ptr;
-
// Adjust for multi-wide char (not a TAB)
- ptr = get_cursor_pos_ptr();
- if (*ptr != TAB && vim_isprintc(utf_ptr2char(ptr))
- && ptr2cells(ptr) > 1) {
+ char *ptr = (char *)get_cursor_pos_ptr();
+ if (*ptr != TAB && vim_isprintc(utf_ptr2char(ptr)) && ptr2cells(ptr) > 1) {
curwin->w_cursor.coladd = 0;
}
}
@@ -6993,7 +6902,7 @@ int cursor_up(long n, int upd_topline)
// If we entered a fold, move to the beginning, unless in
// Insert mode or when 'foldopen' contains "all": it will open
// in a moment.
- if (n > 0 || !((State & INSERT) || (fdo_flags & FDO_ALL))) {
+ if (n > 0 || !((State & MODE_INSERT) || (fdo_flags & FDO_ALL))) {
(void)hasFolding(lnum, &lnum, NULL);
}
}
@@ -7001,7 +6910,7 @@ int cursor_up(long n, int upd_topline)
lnum = 1;
}
} else {
- lnum -= n;
+ lnum -= (linenr_T)n;
}
curwin->w_cursor.lnum = lnum;
}
@@ -7052,7 +6961,7 @@ int cursor_down(long n, int upd_topline)
lnum = curbuf->b_ml.ml_line_count;
}
} else {
- lnum += n;
+ lnum += (linenr_T)n;
}
curwin->w_cursor.lnum = lnum;
}
@@ -7110,9 +7019,7 @@ int stuff_inserted(int c, long count, int no_esc)
stuffReadbuff((const char *)ptr);
// A trailing "0" is inserted as "<C-V>048", "^" as "<C-V>^".
if (last) {
- stuffReadbuff((last == '0'
- ? "\026\060\064\070"
- : "\026^"));
+ stuffReadbuff(last == '0' ? "\026\060\064\070" : "\026^");
}
} while (--count > 0);
@@ -7133,6 +7040,7 @@ int stuff_inserted(int c, long count, int no_esc)
}
char_u *get_last_insert(void)
+ FUNC_ATTR_PURE
{
if (last_insert == NULL) {
return NULL;
@@ -7216,11 +7124,11 @@ void replace_push(int c)
if (replace_stack_len <= replace_stack_nr) {
replace_stack_len += 50;
- replace_stack = xrealloc(replace_stack, replace_stack_len);
+ replace_stack = xrealloc(replace_stack, (size_t)replace_stack_len);
}
char_u *p = replace_stack + replace_stack_nr - replace_offset;
if (replace_offset) {
- memmove(p + 1, p, replace_offset);
+ memmove(p + 1, p, (size_t)replace_offset);
}
*p = (char_u)c;
++replace_stack_nr;
@@ -7233,7 +7141,7 @@ void replace_push(int c)
*/
int replace_push_mb(char_u *p)
{
- int l = utfc_ptr2len(p);
+ int l = utfc_ptr2len((char *)p);
int j;
for (j = l - 1; j >= 0; --j) {
@@ -7256,9 +7164,7 @@ static int replace_pop(void)
/// @param off offset for which NUL to remove
static void replace_join(int off)
{
- int i;
-
- for (i = replace_stack_nr; --i >= 0;) {
+ for (ssize_t i = replace_stack_nr; --i >= 0;) {
if (replace_stack[i] == NUL && off-- <= 0) {
--replace_stack_nr;
memmove(replace_stack + i, replace_stack + i + 1,
@@ -7268,16 +7174,14 @@ static void replace_join(int off)
}
}
-/*
- * Pop bytes from the replace stack until a NUL is found, and insert them
- * before the cursor. Can only be used in REPLACE or VREPLACE mode.
- */
+/// Pop bytes from the replace stack until a NUL is found, and insert them
+/// before the cursor. Can only be used in MODE_REPLACE or MODE_VREPLACE state.
static void replace_pop_ins(void)
{
int cc;
int oldState = State;
- State = NORMAL; // don't want REPLACE here
+ State = MODE_NORMAL; // don't want MODE_REPLACE here
while ((cc = replace_pop()) > 0) {
mb_replace_pop_ins(cc);
dec_cursor();
@@ -7297,11 +7201,11 @@ static void mb_replace_pop_ins(int cc)
int c;
if ((n = MB_BYTE2LEN(cc)) > 1) {
- buf[0] = cc;
- for (i = 1; i < n; ++i) {
- buf[i] = replace_pop();
+ buf[0] = (char_u)cc;
+ for (i = 1; i < n; i++) {
+ buf[i] = (char_u)replace_pop();
}
- ins_bytes_len(buf, n);
+ ins_bytes_len(buf, (size_t)n);
} else {
ins_char(cc);
}
@@ -7316,21 +7220,21 @@ static void mb_replace_pop_ins(int cc)
// Not a multi-byte char, put it back.
replace_push(c);
break;
+ }
+
+ buf[0] = (char_u)c;
+ assert(n > 1);
+ for (i = 1; i < n; i++) {
+ buf[i] = (char_u)replace_pop();
+ }
+ if (utf_iscomposing(utf_ptr2char((char *)buf))) {
+ ins_bytes_len(buf, (size_t)n);
} else {
- buf[0] = c;
- assert(n > 1);
- for (i = 1; i < n; i++) {
- buf[i] = replace_pop();
- }
- if (utf_iscomposing(utf_ptr2char(buf))) {
- ins_bytes_len(buf, n);
- } else {
- // Not a composing char, put it back.
- for (i = n - 1; i >= 0; i--) {
- replace_push(buf[i]);
- }
- break;
+ // Not a composing char, put it back.
+ for (i = n - 1; i >= 0; i--) {
+ replace_push(buf[i]);
}
+ break;
}
}
}
@@ -7389,7 +7293,7 @@ static void replace_do_bs(int limit_col)
vcol = start_vcol;
for (i = 0; i < ins_len; i++) {
vcol += win_chartabsize(curwin, p + i, vcol);
- i += utfc_ptr2len(p) - 1;
+ i += utfc_ptr2len((char *)p) - 1;
}
vcol -= start_vcol;
@@ -7411,7 +7315,7 @@ static void replace_do_bs(int limit_col)
}
/// Check that C-indenting is on.
-static bool cindent_on(void)
+bool cindent_on(void)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
return !p_paste && (curbuf->b_p_cin || *curbuf->b_p_inde != NUL);
@@ -7510,7 +7414,7 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty)
// Does it look like a control character?
if (*look == '^' && look[1] >= '?' && look[1] <= '_') {
- if (try_match && keytyped == Ctrl_chr(look[1])) {
+ if (try_match && keytyped == CTRL_CHR(look[1])) {
return true;
}
look += 2;
@@ -7533,7 +7437,7 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty)
} else if (*look == 'e') {
if (try_match && keytyped == 'e' && curwin->w_cursor.col >= 4) {
p = get_cursor_line_ptr();
- if (skipwhite(p) == p + curwin->w_cursor.col - 4
+ if ((char_u *)skipwhite((char *)p) == p + curwin->w_cursor.col - 4
&& STRNCMP(p + curwin->w_cursor.col - 4, "else", 4) == 0) {
return true;
}
@@ -7573,7 +7477,7 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty)
// make up some named keys <o>, <O>, <e>, <0>, <>>, <<>, <*>,
// <:> and <!> so that people can re-indent on o, O, e, 0, <,
// >, *, : and ! keys if they really really want to.
- if (vim_strchr((char_u *)"<>!*oOe0:", look[1]) != NULL
+ if (vim_strchr("<>!*oOe0:", look[1]) != NULL
&& keytyped == look[1]) {
return true;
}
@@ -7600,7 +7504,7 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty)
} else {
icase = false;
}
- p = vim_strchr(look, ',');
+ p = (char_u *)vim_strchr((char *)look, ',');
if (p == NULL) {
p = look + STRLEN(look);
}
@@ -7679,6 +7583,7 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty)
* Map Hebrew keyboard when in hkmap mode.
*/
int hkmap(int c)
+ FUNC_ATTR_PURE
{
if (p_hkmapp) { // phonetic mapping, by Ilya Dogolazky
enum {
@@ -7691,7 +7596,7 @@ int hkmap(int c)
(char_u)BET, // b
(char_u)hKAF, // c
(char_u)DALET, // d
- (char_u)-1, // e
+ (char_u) - 1, // e
(char_u)PEIsofit, // f
(char_u)GIMEL, // g
(char_u)HEI, // h
@@ -7703,20 +7608,20 @@ int hkmap(int c)
(char_u)NUN, // n
(char_u)SAMEH, // o
(char_u)PEI, // p
- (char_u)-1, // q
+ (char_u) - 1, // q
(char_u)RESH, // r
(char_u)ZAIN, // s
(char_u)TAV, // t
(char_u)TET, // u
(char_u)VAV, // v
(char_u)hSHIN, // w
- (char_u)-1, // x
+ (char_u) - 1, // x
(char_u)AIN, // y
(char_u)ZADI, // z
};
if (c == 'N' || c == 'M' || c == 'P' || c == 'C' || c == 'Z') {
- return (int)(map[CharOrd(c)] - 1 + p_aleph);
+ return (int)(map[CHAR_ORD(c)] - 1 + p_aleph);
} else if (c == 'x') { // '-1'='sofit'
return 'X';
} else if (c == 'q') {
@@ -7732,7 +7637,7 @@ int hkmap(int c)
// do this the same was as 5.7 and previous, so it works correctly on
// all systems. Specifically, the e.g. Delete and Arrow keys are
// munged and won't work if e.g. searching for Hebrew text.
- return (int)(map[CharOrdLow(c)] + p_aleph);
+ return (int)(map[CHAR_ORD_LOW(c)] + p_aleph);
} else {
return c;
}
@@ -7757,17 +7662,17 @@ int hkmap(int c)
case ';':
c = 't'; break;
default: {
- static char str[] = "zqbcxlsjphmkwonu ydafe rig";
+ static char_u str[] = "zqbcxlsjphmkwonu ydafe rig";
if (c < 'a' || c > 'z') {
return c;
}
- c = str[CharOrdLow(c)];
+ c = str[CHAR_ORD_LOW(c)];
break;
}
}
- return (int)(CharOrdLow(c) + p_aleph);
+ return (int)(CHAR_ORD_LOW(c) + p_aleph);
}
}
@@ -7790,12 +7695,10 @@ static void ins_reg(void)
add_to_showcmd_c(Ctrl_R);
}
-
- /*
- * Don't map the register name. This also prevents the mode message to be
- * deleted when ESC is hit.
- */
- ++no_mapping;
+ // Don't map the register name. This also prevents the mode message to be
+ // deleted when ESC is hit.
+ no_mapping++;
+ allow_keys++;
regname = plain_vgetc();
LANGMAP_ADJUST(regname, TRUE);
if (regname == Ctrl_R || regname == Ctrl_O || regname == Ctrl_P) {
@@ -7805,7 +7708,8 @@ static void ins_reg(void)
regname = plain_vgetc();
LANGMAP_ADJUST(regname, TRUE);
}
- --no_mapping;
+ no_mapping--;
+ allow_keys--;
// Don't call u_sync() while typing the expression or giving an error
// message for it. Only call it explicitly.
@@ -7873,13 +7777,13 @@ static void ins_ctrl_g(void)
// Right after CTRL-X the cursor will be after the ruler.
setcursor();
- /*
- * Don't map the second key. This also prevents the mode message to be
- * deleted when ESC is hit.
- */
- ++no_mapping;
+ // Don't map the second key. This also prevents the mode message to be
+ // deleted when ESC is hit.
+ no_mapping++;
+ allow_keys++;
c = plain_vgetc();
- --no_mapping;
+ no_mapping--;
+ allow_keys--;
switch (c) {
// CTRL-G k and CTRL-G <Up>: cursor up to Insstart.col
case K_UP:
@@ -7924,14 +7828,14 @@ static void ins_ctrl_g(void)
*/
static void ins_ctrl_hat(void)
{
- if (map_to_exists_mode("", LANGMAP, false)) {
+ if (map_to_exists_mode("", MODE_LANGMAP, false)) {
// ":lmap" mappings exists, Toggle use of ":lmap" mappings.
- if (State & LANGMAP) {
+ if (State & MODE_LANGMAP) {
curbuf->b_p_iminsert = B_IMODE_NONE;
- State &= ~LANGMAP;
+ State &= ~MODE_LANGMAP;
} else {
curbuf->b_p_iminsert = B_IMODE_LMAP;
- State |= LANGMAP;
+ State |= MODE_LANGMAP;
}
}
set_iminsert_global();
@@ -7961,10 +7865,8 @@ static bool ins_esc(long *count, int cmdchar, bool nomove)
}
if (!arrow_used) {
// Don't append the ESC for "r<CR>" and "grx".
- // When 'insertmode' is set only CTRL-L stops Insert mode. Needed for
- // when "count" is non-zero.
if (cmdchar != 'r' && cmdchar != 'v') {
- AppendToRedobuff(p_im ? "\014" : ESC_STR);
+ AppendToRedobuff(ESC_STR);
}
/*
@@ -8008,8 +7910,9 @@ static bool ins_esc(long *count, int cmdchar, bool nomove)
}
// Remember the last Insert position in the '^ mark.
- if (!cmdmod.keepjumps) {
- RESET_FMARK(&curbuf->b_last_insert, curwin->w_cursor, curbuf->b_fnum);
+ if ((cmdmod.cmod_flags & CMOD_KEEPJUMPS) == 0) {
+ fmarkv_T view = mark_view_make(curwin->w_topline, curwin->w_cursor);
+ RESET_FMARK(&curbuf->b_last_insert, curwin->w_cursor, curbuf->b_fnum, view);
}
/*
@@ -8017,15 +7920,10 @@ static bool ins_esc(long *count, int cmdchar, bool nomove)
* Don't do it for CTRL-O, unless past the end of the line.
*/
if (!nomove
- && (curwin->w_cursor.col != 0
- || curwin->w_cursor.coladd > 0
- )
- && (restart_edit == NUL
- || (gchar_cursor() == NUL
- && !VIsual_active
- ))
+ && (curwin->w_cursor.col != 0 || curwin->w_cursor.coladd > 0)
+ && (restart_edit == NUL || (gchar_cursor() == NUL && !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++;
@@ -8037,11 +7935,12 @@ static bool ins_esc(long *count, int cmdchar, bool nomove)
}
}
-
- State = NORMAL;
- trigger_modechanged();
- // need to position cursor again (e.g. when on a TAB )
- changed_cline_bef_curs();
+ State = MODE_NORMAL;
+ may_trigger_modechanged();
+ // need to position cursor again when on a TAB
+ if (gchar_cursor() == TAB) {
+ curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL);
+ }
setmouse();
ui_cursor_shape(); // may show different cursor shape
@@ -8069,7 +7968,7 @@ static void ins_ctrl_(void)
}
}
p_ri = !p_ri;
- revins_on = (State == INSERT && p_ri);
+ revins_on = (State == MODE_INSERT && p_ri);
if (revins_on) {
revins_scol = curwin->w_cursor.col;
revins_legal++;
@@ -8133,15 +8032,15 @@ static bool ins_start_select(int c)
static void ins_insert(int replaceState)
{
set_vim_var_string(VV_INSERTMODE, ((State & REPLACE_FLAG) ? "i" :
- replaceState == VREPLACE ? "v" :
+ replaceState == MODE_VREPLACE ? "v" :
"r"), 1);
ins_apply_autocmds(EVENT_INSERTCHANGE);
if (State & REPLACE_FLAG) {
- State = INSERT | (State & LANGMAP);
+ State = MODE_INSERT | (State & MODE_LANGMAP);
} else {
- State = replaceState | (State & LANGMAP);
+ State = replaceState | (State & MODE_LANGMAP);
}
- trigger_modechanged();
+ may_trigger_modechanged();
AppendCharToRedobuff(K_INS);
showmode();
ui_cursor_shape(); // may show different cursor shape
@@ -8199,7 +8098,7 @@ static void ins_shift(int c, int lastc)
change_indent(c == Ctrl_D ? INDENT_DEC : INDENT_INC, 0, TRUE, 0, TRUE);
}
- if (did_ai && *skipwhite(get_cursor_line_ptr()) != NUL) {
+ if (did_ai && *skipwhite((char *)get_cursor_line_ptr()) != NUL) {
did_ai = false;
}
did_si = false;
@@ -8238,7 +8137,6 @@ static void ins_del(void)
AppendCharToRedobuff(K_DEL);
}
-
/*
* Delete one character for ins_bs().
*/
@@ -8278,6 +8176,7 @@ static bool ins_bs(int c, int mode, int *inserted_space_p)
int in_indent;
int oldState;
int cpc[MAX_MCO]; // composing characters
+ bool call_fix_indent = false;
// can't delete anything in an empty file
// can't backup past first character in buffer
@@ -8376,23 +8275,17 @@ static bool ins_bs(int c, int mode, int *inserted_space_p)
dec_cursor();
}
- /*
- * In REPLACE mode we have to put back the text that was replaced
- * by the NL. On the replace stack is first a NUL-terminated
- * sequence of characters that were deleted and then the
- * characters that NL replaced.
- */
+ // In MODE_REPLACE mode we have to put back the text that was
+ // replaced by the NL. On the replace stack is first a
+ // NUL-terminated sequence of characters that were deleted and then
+ // the characters that NL replaced.
if (State & REPLACE_FLAG) {
- /*
- * Do the next ins_char() in NORMAL state, to
- * prevent ins_char() from replacing characters and
- * avoiding showmatch().
- */
+ // Do the next ins_char() in MODE_NORMAL state, to
+ // prevent ins_char() from replacing characters and
+ // avoiding showmatch().
oldState = State;
- State = NORMAL;
- /*
- * restore characters (blanks) deleted after cursor
- */
+ State = MODE_NORMAL;
+ // restore characters (blanks) deleted after cursor
while (cc > 0) {
save_col = curwin->w_cursor.col;
mb_replace_pop_ins(cc);
@@ -8413,14 +8306,14 @@ static bool ins_bs(int c, int mode, int *inserted_space_p)
mincol = 0;
// keep indent
if (mode == BACKSPACE_LINE
- && (curbuf->b_p_ai
- || cindent_on()
- )
+ && (curbuf->b_p_ai || cindent_on())
&& !revins_on) {
save_col = curwin->w_cursor.col;
beginline(BL_WHITE);
if (curwin->w_cursor.col < save_col) {
mincol = curwin->w_cursor.col;
+ // should now fix the indent to match with the previous line
+ call_fix_indent = true;
}
curwin->w_cursor.col = save_col;
}
@@ -8460,7 +8353,7 @@ static bool ins_bs(int c, int mode, int *inserted_space_p)
}
// delete characters until we are at or before want_vcol
- while (vcol > want_vcol
+ while (vcol > want_vcol && curwin->w_cursor.col > 0
&& (cc = *(get_cursor_pos_ptr() - 1), ascii_iswhite(cc))) {
ins_bs_one(&vcol);
}
@@ -8555,6 +8448,11 @@ static bool ins_bs(int c, int mode, int *inserted_space_p)
if (curwin->w_cursor.col <= 1) {
did_ai = false;
}
+
+ if (call_fix_indent) {
+ fix_indent();
+ }
+
// It's a little strange to put backspaces into the redo
// buffer, but it makes auto-indent a lot easier to deal
// with.
@@ -8639,14 +8537,12 @@ static void ins_mousescroll(int dir)
}
// Don't scroll the window in which completion is being done.
- if (!pum_visible()
- || curwin != old_curwin) {
+ if (!pum_visible() || curwin != old_curwin) {
if (dir == MSCR_DOWN || dir == MSCR_UP) {
if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) {
- scroll_redraw(dir,
- (curwin->w_botline - curwin->w_topline));
+ scroll_redraw(dir, (long)(curwin->w_botline - curwin->w_topline));
} else {
- scroll_redraw(dir, 3L);
+ scroll_redraw(dir, p_mousescroll_vert);
}
} else {
mouse_scroll_horiz(dir);
@@ -8664,7 +8560,6 @@ static void ins_mousescroll(int dir)
}
}
-
static void ins_left(void)
{
pos_T tpos;
@@ -8685,7 +8580,7 @@ static void ins_left(void)
revins_legal++;
}
revins_chars++;
- } else if (vim_strchr(p_ww, '[') != NULL && curwin->w_cursor.lnum > 1) {
+ } else if (vim_strchr((char *)p_ww, '[') != NULL && curwin->w_cursor.lnum > 1) {
// if 'whichwrap' set for cursor in insert mode may go to previous line.
// always break undo when moving upwards/downwards, else undo may break
start_arrow(&tpos);
@@ -8771,14 +8666,14 @@ static void ins_right(void)
if (virtual_active()) {
oneright();
} else {
- curwin->w_cursor.col += utfc_ptr2len(get_cursor_pos_ptr());
+ curwin->w_cursor.col += utfc_ptr2len((char *)get_cursor_pos_ptr());
}
revins_legal++;
if (revins_chars) {
revins_chars--;
}
- } else if (vim_strchr(p_ww, ']') != NULL
+ } else if (vim_strchr((char *)p_ww, ']') != NULL
&& curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
// if 'whichwrap' set for cursor in insert mode, may move the
// cursor to the next line
@@ -8972,11 +8867,9 @@ static bool ins_tab(void)
curbuf->b_p_vts_array);
}
- /*
- * Insert the first space with ins_char(). It will delete one char in
- * replace mode. Insert the rest with ins_str(); it will not delete any
- * chars. For VREPLACE mode, we use ins_char() for all characters.
- */
+ // Insert the first space with ins_char(). It will delete one char in
+ // replace mode. Insert the rest with ins_str(); it will not delete any
+ // chars. For MODE_VREPLACE state, we use ins_char() for all characters.
ins_char(' ');
while (--temp > 0) {
if (State & VREPLACE_FLAG) {
@@ -9004,10 +8897,8 @@ static bool ins_tab(void)
int change_col = -1;
int save_list = curwin->w_p_list;
- /*
- * Get the current line. For VREPLACE mode, don't make real changes
- * yet, just work on a copy of the line.
- */
+ // Get the current line. For MODE_VREPLACE state, don't make real
+ // changes yet, just work on a copy of the line.
if (State & VREPLACE_FLAG) {
pos = curwin->w_cursor;
cursor = &pos;
@@ -9093,26 +8984,23 @@ static bool ins_tab(void)
}
}
if (!(State & VREPLACE_FLAG)) {
- extmark_splice_cols(curbuf, fpos.lnum - 1, change_col,
+ extmark_splice_cols(curbuf, (int)fpos.lnum - 1, change_col,
cursor->col - change_col, fpos.col - change_col,
kExtmarkUndo);
}
}
cursor->col -= i;
- /*
- * In VREPLACE mode, we haven't changed anything yet. Do it now by
- * backspacing over the changed spacing and then inserting the new
- * spacing.
- */
+ // In MODE_VREPLACE state, we haven't changed anything yet. Do it
+ // now by backspacing over the changed spacing and then inserting
+ // the new spacing.
if (State & VREPLACE_FLAG) {
// Backspace from real cursor to change_col
backspace_until_column(change_col);
// Insert each char in saved_line from changed_col to
// ptr-cursor
- ins_bytes_len(saved_line + change_col,
- cursor->col - change_col);
+ ins_bytes_len(saved_line + change_col, (size_t)(cursor->col - change_col));
}
}
@@ -9148,12 +9036,10 @@ static bool ins_eol(int c)
replace_push(NUL);
}
- /*
- * In VREPLACE mode, a NL replaces the rest of the line, and starts
- * replacing the next line, so we push all of the characters left on the
- * line onto the replace stack. This is not done here though, it is done
- * in open_line().
- */
+ // In MODE_VREPLACE state, a NL replaces the rest of the line, and starts
+ // replacing the next line, so we push all of the characters left on the
+ // line onto the replace stack. This is not done here though, it is done
+ // in open_line().
// Put cursor on NUL if on the last char and coladd is 1 (happens after
// CTRL-O).
@@ -9169,7 +9055,7 @@ static bool ins_eol(int c)
AppendToRedobuff(NL_STR);
bool i = open_line(FORWARD,
has_format_option(FO_RET_COMS) ? OPENLINE_DO_COM : 0,
- old_indent);
+ old_indent, NULL);
old_indent = 0;
can_cindent = true;
// When inserting a line the cursor line must never be in a closed fold.
@@ -9199,12 +9085,13 @@ static int ins_digraph(void)
add_to_showcmd_c(Ctrl_K);
}
-
// don't map the digraph chars. This also prevents the
// mode message to be deleted when ESC is hit
no_mapping++;
+ allow_keys++;
c = plain_vgetc();
no_mapping--;
+ allow_keys--;
if (did_putchar) {
// when the line fits in 'columns' the '?' is at the start of the next
// line and will not be removed by the redraw
@@ -9230,8 +9117,10 @@ static int ins_digraph(void)
add_to_showcmd_c(c);
}
no_mapping++;
+ allow_keys++;
cc = plain_vgetc();
no_mapping--;
+ allow_keys--;
if (did_putchar) {
// when the line fits in 'columns' the '?' is at the start of the
// next line and will not be removed by a redraw
@@ -9239,7 +9128,7 @@ static int ins_digraph(void)
}
if (cc != ESC) {
AppendToRedobuff(CTRL_V_STR);
- c = getdigraph(c, cc, true);
+ c = digraph_get(c, cc, true);
clear_showcmd();
return c;
}
@@ -9277,7 +9166,7 @@ int ins_copychar(linenr_T lnum)
ptr = prev_ptr;
}
- c = utf_ptr2char(ptr);
+ c = utf_ptr2char((char *)ptr);
if (c == NUL) {
vim_beep(BO_COPY);
}
@@ -9337,10 +9226,8 @@ static void ins_try_si(int c)
/*
* do some very smart indenting when entering '{' or '}'
*/
- if (((did_si || can_si_back) && c == '{') || (can_si && c == '}')) {
- /*
- * for '}' set indent equal to indent of line containing matching '{'
- */
+ if (((did_si || can_si_back) && c == '{') || (can_si && c == '}' && inindent(0))) {
+ // for '}' set indent equal to indent of line containing matching '{'
if (c == '}' && (pos = findmatch(NULL, '{')) != NULL) {
old_pos = curwin->w_cursor;
/*
@@ -9353,8 +9240,7 @@ static void ins_try_si(int c)
ptr = ml_get(pos->lnum);
i = pos->col;
if (i > 0) { // skip blanks before '{'
- while (--i > 0 && ascii_iswhite(ptr[i])) {
- }
+ while (--i > 0 && ascii_iswhite(ptr[i])) {}
}
curwin->w_cursor.lnum = pos->lnum;
curwin->w_cursor.col = i;
@@ -9376,7 +9262,7 @@ static void ins_try_si(int c)
old_pos = curwin->w_cursor;
i = get_indent();
while (curwin->w_cursor.lnum > 1) {
- ptr = skipwhite(ml_get(--(curwin->w_cursor.lnum)));
+ ptr = (char_u *)skipwhite((char *)ml_get(--(curwin->w_cursor.lnum)));
// ignore empty lines and lines starting with '#'.
if (*ptr != '#' && *ptr != NUL) {
@@ -9397,7 +9283,7 @@ static void ins_try_si(int c)
/*
* set indent of '#' always to 0
*/
- if (curwin->w_cursor.col > 0 && can_si && c == '#') {
+ if (curwin->w_cursor.col > 0 && can_si && c == '#' && inindent(0)) {
// remember current indent for next line
old_indent = get_indent();
(void)set_indent(0, SIN_CHANGED);
@@ -9442,7 +9328,7 @@ static char_u *do_insert_char_pre(int c)
if (!has_event(EVENT_INSERTCHARPRE)) {
return NULL;
}
- buf[utf_char2bytes(c, (char_u *)buf)] = NUL;
+ buf[utf_char2bytes(c, buf)] = NUL;
// Lock the text to avoid weird things from happening.
textlock++;
@@ -9454,7 +9340,7 @@ static char_u *do_insert_char_pre(int c)
// character. Only use it when changed, otherwise continue with the
// original character to avoid breaking autoindent.
if (STRCMP(buf, get_vim_var_str(VV_CHAR)) != 0) {
- res = vim_strsave(get_vim_var_str(VV_CHAR));
+ res = vim_strsave((char_u *)get_vim_var_str(VV_CHAR));
}
}
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 80aa3a3433..16c3e72c5b 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -6,6 +6,7 @@
*/
#include <math.h>
+#include <stdlib.h>
#include "auto/config.h"
@@ -27,11 +28,11 @@
#include "nvim/eval/typval.h"
#include "nvim/eval/userfunc.h"
#include "nvim/ex_cmds2.h"
-#include "nvim/ex_docmd.h"
#include "nvim/ex_getln.h"
#include "nvim/ex_session.h"
#include "nvim/fileio.h"
#include "nvim/getchar.h"
+#include "nvim/highlight_group.h"
#include "nvim/lua/executor.h"
#include "nvim/mark.h"
#include "nvim/memline.h"
@@ -39,7 +40,6 @@
#include "nvim/ops.h"
#include "nvim/option.h"
#include "nvim/os/input.h"
-#include "nvim/os/os.h"
#include "nvim/os/shell.h"
#include "nvim/path.h"
#include "nvim/quickfix.h"
@@ -49,16 +49,15 @@
#include "nvim/sign.h"
#include "nvim/syntax.h"
#include "nvim/ui.h"
+#include "nvim/ui_compositor.h"
#include "nvim/undo.h"
#include "nvim/version.h"
#include "nvim/window.h"
-
// TODO(ZyX-I): Remove DICT_MAXNEST, make users be non-recursive instead
#define DICT_MAXNEST 100 // maximum nesting of lists and dicts
-
static char *e_letunexp = N_("E18: Unexpected characters in :let");
static char *e_missbrac = N_("E111: Missing ']'");
static char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary");
@@ -66,14 +65,14 @@ static char *e_illvar = N_("E461: Illegal variable name: %s");
static char *e_cannot_mod = N_("E995: Cannot modify existing variable");
static char *e_nowhitespace
= N_("E274: No white space allowed before parenthesis");
-static char *e_invalwindow = N_("E957: Invalid window number");
static char *e_lock_unlock = N_("E940: Cannot lock or unlock variable %s");
static char *e_write2 = N_("E80: Error while writing: %s");
+static char *e_string_list_or_blob_required = N_("E1098: String, List or Blob required");
// TODO(ZyX-I): move to eval/executor
static char *e_letwrong = N_("E734: Wrong variable type for %s=");
-static char_u * const namespace_char = (char_u *)"abglstvw";
+static char * const namespace_char = "abglstvw";
/// Variable used for g:
static ScopeDictDictItem globvars_var;
@@ -103,7 +102,7 @@ static garray_T ga_scripts = { 0, 0, sizeof(scriptvar_T *), 4, NULL };
static int echo_attr = 0; // attributes used for ":echo"
// The names of packages that once were loaded are remembered.
-static garray_T ga_loaded = { 0, 0, sizeof(char_u *), 4, NULL };
+static garray_T ga_loaded = { 0, 0, sizeof(char *), 4, NULL };
/*
* Info used by a ":for" loop.
@@ -112,9 +111,11 @@ typedef struct {
int fi_semicolon; // TRUE if ending in '; var]'
int fi_varcount; // nr of variables in the list
listwatch_T fi_lw; // keep an eye on the item used.
- list_T *fi_list; // list being used
+ list_T *fi_list; // list being used
int fi_bi; // index of blob
blob_T *fi_blob; // blob being used
+ char *fi_string; // copy of string being used
+ int fi_byte_idx; // byte index in fi_string
} forinfo_T;
// values for vv_flags:
@@ -124,13 +125,13 @@ typedef struct {
#define VV(idx, name, type, flags) \
[idx] = { \
- .vv_name = name, \
+ .vv_name = (name), \
.vv_di = { \
- .di_tv = { .v_type = type }, \
+ .di_tv = { .v_type = (type) }, \
.di_flags = 0, \
.di_key = { 0 }, \
}, \
- .vv_flags = flags, \
+ .vv_flags = (flags), \
}
#define VIMVAR_KEY_LEN 16 // Maximum length of the key of v:variables
@@ -159,7 +160,7 @@ static struct vimvar {
VV(VV_STATUSMSG, "statusmsg", VAR_STRING, 0),
VV(VV_SHELL_ERROR, "shell_error", VAR_NUMBER, VV_RO),
VV(VV_THIS_SESSION, "this_session", VAR_STRING, 0),
- VV(VV_VERSION, "version", VAR_NUMBER, VV_COMPAT+VV_RO),
+ VV(VV_VERSION, "version", VAR_NUMBER, VV_COMPAT + VV_RO),
VV(VV_LNUM, "lnum", VAR_NUMBER, VV_RO_SBX),
VV(VV_TERMRESPONSE, "termresponse", VAR_STRING, VV_RO),
VV(VV_FNAME, "fname", VAR_STRING, VV_RO),
@@ -327,7 +328,7 @@ void restore_v_event(dict_T *v_event, save_v_event_T *sve)
}
}
-// Return "n1" divided by "n2", taking care of dividing by zero.
+/// @return "n1" divided by "n2", taking care of dividing by zero.
varnumber_T num_divide(varnumber_T n1, varnumber_T n2)
FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT
{
@@ -348,7 +349,7 @@ varnumber_T num_divide(varnumber_T n1, varnumber_T n2)
return result;
}
-// Return "n1" modulus "n2", taking care of dividing by zero.
+/// @return "n1" modulus "n2", taking care of dividing by zero.
varnumber_T num_modulus(varnumber_T n1, varnumber_T n2)
FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT
{
@@ -356,9 +357,7 @@ varnumber_T num_modulus(varnumber_T n1, varnumber_T n2)
return (n2 == 0) ? 0 : (n1 % n2);
}
-/*
- * Initialize the global and v: variables.
- */
+/// Initialize the global and v: variables.
void eval_init(void)
{
vimvars[VV_VERSION].vv_nr = VIM_VERSION_100;
@@ -470,7 +469,9 @@ void eval_clear(void)
hash_clear(&compat_hashtab);
free_scriptnames();
+# ifdef HAVE_WORKING_LIBINTL
free_locales();
+# endif
// global variables
vars_clear(&globvarht);
@@ -498,11 +499,9 @@ void eval_clear(void)
#endif
-/*
- * Set an internal variable to a string value. Creates the variable if it does
- * not already exist.
- */
-void set_internal_string_var(const char *name, char_u *value)
+/// Set an internal variable to a string value. Creates the variable if it does
+/// not already exist.
+void set_internal_string_var(const char *name, char *value)
FUNC_ATTR_NONNULL_ARG(1)
{
typval_T tv = {
@@ -515,14 +514,15 @@ void set_internal_string_var(const char *name, char_u *value)
static lval_T *redir_lval = NULL;
static garray_T redir_ga; // Only valid when redir_lval is not NULL.
-static char_u *redir_endp = NULL;
-static char_u *redir_varname = NULL;
+static char *redir_endp = NULL;
+static char *redir_varname = NULL;
/// Start recording command output to a variable
-/// Returns OK if successfully completed the setup. FAIL otherwise.
///
/// @param append append to an existing variable
-int var_redir_start(char_u *name, int append)
+///
+/// @return OK if successfully completed the setup. FAIL otherwise.
+int var_redir_start(char *name, int append)
{
int save_emsg;
int err;
@@ -535,7 +535,7 @@ int var_redir_start(char_u *name, int append)
}
// Make a copy of the name, it is used in redir_lval until redir ends.
- redir_varname = vim_strsave(name);
+ redir_varname = xstrdup(name);
redir_lval = xcalloc(1, sizeof(lval_T));
@@ -564,7 +564,7 @@ int var_redir_start(char_u *name, int append)
save_emsg = did_emsg;
did_emsg = FALSE;
tv.v_type = VAR_STRING;
- tv.vval.v_string = (char_u *)"";
+ tv.vval.v_string = "";
if (append) {
set_var_lval(redir_lval, redir_endp, &tv, true, false, ".");
} else {
@@ -582,16 +582,14 @@ int var_redir_start(char_u *name, int append)
return OK;
}
-/*
- * Append "value[value_len]" to the variable set by var_redir_start().
- * The actual appending is postponed until redirection ends, because the value
- * appended may in fact be the string we write to, changing it may cause freed
- * memory to be used:
- * :redir => foo
- * :let foo
- * :redir END
- */
-void var_redir_str(char_u *value, int value_len)
+/// Append "value[value_len]" to the variable set by var_redir_start().
+/// The actual appending is postponed until redirection ends, because the value
+/// appended may in fact be the string we write to, changing it may cause freed
+/// memory to be used:
+/// :redir => foo
+/// :let foo
+/// :redir END
+void var_redir_str(char *value, int value_len)
{
int len;
@@ -606,14 +604,12 @@ void var_redir_str(char_u *value, int value_len)
}
ga_grow(&redir_ga, len);
- memmove((char *)redir_ga.ga_data + redir_ga.ga_len, value, len);
+ memmove((char *)redir_ga.ga_data + redir_ga.ga_len, value, (size_t)len);
redir_ga.ga_len += len;
}
-/*
- * Stop redirecting command output to a variable.
- * Frees the allocated memory.
- */
+/// Stop redirecting command output to a variable.
+/// Frees the allocated memory.
void var_redir_stop(void)
{
typval_T tv;
@@ -651,7 +647,7 @@ int eval_charconvert(const char *const enc_from, const char *const enc_to,
set_vim_var_string(VV_CC_TO, enc_to, -1);
set_vim_var_string(VV_FNAME_IN, fname_from, -1);
set_vim_var_string(VV_FNAME_OUT, fname_to, -1);
- if (eval_to_bool(p_ccv, &err, NULL, false)) {
+ if (eval_to_bool((char *)p_ccv, &err, NULL, false)) {
err = true;
}
set_vim_var_string(VV_CC_FROM, NULL, -1);
@@ -671,7 +667,7 @@ int eval_printexpr(const char *const fname, const char *const args)
set_vim_var_string(VV_FNAME_IN, fname, -1);
set_vim_var_string(VV_CMDARG, args, -1);
- if (eval_to_bool(p_pexpr, &err, NULL, false)) {
+ if (eval_to_bool((char *)p_pexpr, &err, NULL, false)) {
err = true;
}
set_vim_var_string(VV_FNAME_IN, NULL, -1);
@@ -691,7 +687,7 @@ void eval_diff(const char *const origfile, const char *const newfile, const char
set_vim_var_string(VV_FNAME_IN, origfile, -1);
set_vim_var_string(VV_FNAME_NEW, newfile, -1);
set_vim_var_string(VV_FNAME_OUT, outfile, -1);
- (void)eval_to_bool(p_dex, &err, NULL, FALSE);
+ (void)eval_to_bool((char *)p_dex, &err, NULL, false);
set_vim_var_string(VV_FNAME_IN, NULL, -1);
set_vim_var_string(VV_FNAME_NEW, NULL, -1);
set_vim_var_string(VV_FNAME_OUT, NULL, -1);
@@ -704,7 +700,7 @@ void eval_patch(const char *const origfile, const char *const difffile, const ch
set_vim_var_string(VV_FNAME_IN, origfile, -1);
set_vim_var_string(VV_FNAME_DIFF, difffile, -1);
set_vim_var_string(VV_FNAME_OUT, outfile, -1);
- (void)eval_to_bool(p_pex, &err, NULL, FALSE);
+ (void)eval_to_bool((char *)p_pex, &err, NULL, false);
set_vim_var_string(VV_FNAME_IN, NULL, -1);
set_vim_var_string(VV_FNAME_DIFF, NULL, -1);
set_vim_var_string(VV_FNAME_OUT, NULL, -1);
@@ -716,7 +712,7 @@ void eval_patch(const char *const origfile, const char *const difffile, const ch
/// @param skip only parse, don't execute
///
/// @return TRUE or FALSE.
-int eval_to_bool(char_u *arg, bool *error, char_u **nextcmd, int skip)
+int eval_to_bool(char *arg, bool *error, char **nextcmd, int skip)
{
typval_T tv;
bool retval = false;
@@ -740,11 +736,11 @@ int eval_to_bool(char_u *arg, bool *error, char_u **nextcmd, int skip)
return retval;
}
-// Call eval1() and give an error message if not done at a lower level.
-static int eval1_emsg(char_u **arg, typval_T *rettv, bool evaluate)
+/// Call eval1() and give an error message if not done at a lower level.
+static int eval1_emsg(char **arg, typval_T *rettv, bool evaluate)
FUNC_ATTR_NONNULL_ARG(1, 2)
{
- const char_u *const start = *arg;
+ const char *const start = *arg;
const int did_emsg_before = did_emsg;
const int called_emsg_before = called_emsg;
@@ -763,13 +759,22 @@ static int eval1_emsg(char_u **arg, typval_T *rettv, bool evaluate)
return ret;
}
+/// @return whether a typval is a valid expression to pass to eval_expr_typval()
+/// or eval_expr_to_bool(). An empty string returns false;
+bool eval_expr_valid_arg(const typval_T *const tv)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_CONST
+{
+ return tv->v_type != VAR_UNKNOWN
+ && (tv->v_type != VAR_STRING || (tv->vval.v_string != NULL && *tv->vval.v_string != NUL));
+}
+
int eval_expr_typval(const typval_T *expr, typval_T *argv, int argc, typval_T *rettv)
FUNC_ATTR_NONNULL_ARG(1, 2, 4)
{
funcexe_T funcexe = FUNCEXE_INIT;
if (expr->v_type == VAR_FUNC) {
- const char_u *const s = expr->vval.v_string;
+ const char *const s = expr->vval.v_string;
if (s == NULL || *s == NUL) {
return FAIL;
}
@@ -779,7 +784,7 @@ int eval_expr_typval(const typval_T *expr, typval_T *argv, int argc, typval_T *r
}
} else if (expr->v_type == VAR_PARTIAL) {
partial_T *const partial = expr->vval.v_partial;
- const char_u *const s = partial_name(partial);
+ const char *const s = partial_name(partial);
if (s == NULL || *s == NUL) {
return FAIL;
}
@@ -790,7 +795,7 @@ int eval_expr_typval(const typval_T *expr, typval_T *argv, int argc, typval_T *r
}
} else {
char buf[NUMBUFLEN];
- char_u *s = (char_u *)tv_get_string_buf_chk(expr, buf);
+ char *s = (char *)tv_get_string_buf_chk(expr, buf);
if (s == NULL) {
return FAIL;
}
@@ -841,7 +846,7 @@ char *eval_to_string_skip(const char *arg, const char **nextcmd, const bool skip
if (skip) {
emsg_skip++;
}
- if (eval0((char_u *)arg, &tv, (char_u **)nextcmd, !skip) == FAIL || skip) {
+ if (eval0((char *)arg, &tv, (char **)nextcmd, !skip) == FAIL || skip) {
retval = NULL;
} else {
retval = xstrdup(tv_get_string(&tv));
@@ -854,16 +859,15 @@ char *eval_to_string_skip(const char *arg, const char **nextcmd, const bool skip
return retval;
}
-/*
- * Skip over an expression at "*pp".
- * Return FAIL for an error, OK otherwise.
- */
-int skip_expr(char_u **pp)
+/// Skip over an expression at "*pp".
+///
+/// @return FAIL for an error, OK otherwise.
+int skip_expr(char **pp)
{
typval_T rettv;
*pp = skipwhite(*pp);
- return eval1(pp, &rettv, FALSE);
+ return eval1(pp, &rettv, false);
}
/// Top level evaluation function, returning a string.
@@ -872,7 +876,7 @@ int skip_expr(char_u **pp)
/// a Float to a String.
///
/// @return pointer to allocated memory, or NULL for failure.
-char_u *eval_to_string(char_u *arg, char_u **nextcmd, bool convert)
+char *eval_to_string(char *arg, char **nextcmd, bool convert)
{
typval_T tv;
char *retval;
@@ -901,16 +905,16 @@ char_u *eval_to_string(char_u *arg, char_u **nextcmd, bool convert)
tv_clear(&tv);
}
- return (char_u *)retval;
+ return retval;
}
-/*
- * Call eval_to_string() without using current local variables and using
- * textlock. When "use_sandbox" is TRUE use the sandbox.
- */
-char_u *eval_to_string_safe(char_u *arg, char_u **nextcmd, int use_sandbox)
+/// Call eval_to_string() without using current local variables and using
+/// textlock.
+///
+/// @param use_sandbox when TRUE, use the sandbox.
+char *eval_to_string_safe(char *arg, char **nextcmd, int use_sandbox)
{
- char_u *retval;
+ char *retval;
funccal_entry_T funccal_entry;
save_funccal(&funccal_entry);
@@ -927,16 +931,15 @@ char_u *eval_to_string_safe(char_u *arg, char_u **nextcmd, int use_sandbox)
return retval;
}
-/*
- * Top level evaluation function, returning a number.
- * Evaluates "expr" silently.
- * Returns -1 for an error.
- */
-varnumber_T eval_to_number(char_u *expr)
+/// Top level evaluation function, returning a number.
+/// Evaluates "expr" silently.
+///
+/// @return -1 for an error.
+varnumber_T eval_to_number(char *expr)
{
typval_T rettv;
varnumber_T retval;
- char_u *p = skipwhite(expr);
+ char *p = skipwhite(expr);
++emsg_off;
@@ -951,10 +954,11 @@ varnumber_T eval_to_number(char_u *expr)
return retval;
}
-// Top level evaluation function.
-// Returns an allocated typval_T with the result.
-// Returns NULL when there is an error.
-typval_T *eval_expr(char_u *arg)
+/// Top level evaluation function.
+///
+/// @return an allocated typval_T with the result or
+/// NULL when there is an error.
+typval_T *eval_expr(char *arg)
{
typval_T *tv = xmalloc(sizeof(*tv));
if (eval0(arg, tv, NULL, true) == FAIL) {
@@ -963,11 +967,9 @@ typval_T *eval_expr(char_u *arg)
return tv;
}
-/*
- * Prepare v: variable "idx" to be used.
- * Save the current typeval in "save_tv".
- * When not used yet add the variable to the v: hashtable.
- */
+/// Prepare v: variable "idx" to be used.
+/// Save the current typeval in "save_tv".
+/// When not used yet add the variable to the v: hashtable.
void prepare_vimvar(int idx, typval_T *save_tv)
{
*save_tv = vimvars[idx].vv_tv;
@@ -976,17 +978,15 @@ void prepare_vimvar(int idx, typval_T *save_tv)
}
}
-/*
- * Restore v: variable "idx" to typeval "save_tv".
- * When no longer defined, remove the variable from the v: hashtable.
- */
+/// Restore v: variable "idx" to typeval "save_tv".
+/// When no longer defined, remove the variable from the v: hashtable.
void restore_vimvar(int idx, typval_T *save_tv)
{
hashitem_T *hi;
vimvars[idx].vv_tv = *save_tv;
if (vimvars[idx].vv_type == VAR_UNKNOWN) {
- hi = hash_find(&vimvarht, vimvars[idx].vv_di.di_key);
+ hi = hash_find(&vimvarht, (char *)vimvars[idx].vv_di.di_key);
if (HASHITEM_EMPTY(hi)) {
internal_error("restore_vimvar()");
} else {
@@ -1006,17 +1006,16 @@ void find_win_for_curbuf(void)
}
}
-/*
- * Evaluate an expression to a list with suggestions.
- * For the "expr:" part of 'spellsuggest'.
- * Returns NULL when there is an error.
- */
-list_T *eval_spell_expr(char_u *badword, char_u *expr)
+/// Evaluate an expression to a list with suggestions.
+/// For the "expr:" part of 'spellsuggest'.
+///
+/// @return NULL when there is an error.
+list_T *eval_spell_expr(char *badword, char *expr)
{
typval_T save_val;
typval_T rettv;
list_T *list = NULL;
- char_u *p = skipwhite(expr);
+ char *p = skipwhite(expr);
// Set "v:val" to the bad word.
prepare_vimvar(VV_VAL, &save_val);
@@ -1066,16 +1065,15 @@ int get_spellword(list_T *const list, const char **ret_word)
if (*ret_word == NULL) {
return -1;
}
- return tv_list_find_nr(list, -1, NULL);
+ return (int)tv_list_find_nr(list, -1, NULL);
}
-
// Call some vim script function and return the result in "*rettv".
// Uses argv[0] to argv[argc-1] for the function arguments. argv[argc]
// should have type VAR_UNKNOWN.
//
-// Return OK or FAIL.
-int call_vim_function(const char_u *func, int argc, typval_T *argv, typval_T *rettv)
+// @return OK or FAIL.
+int call_vim_function(const char *func, int argc, typval_T *argv, typval_T *rettv)
FUNC_ATTR_NONNULL_ALL
{
int ret;
@@ -1084,7 +1082,7 @@ int call_vim_function(const char_u *func, int argc, typval_T *argv, typval_T *re
if (len >= 6 && !memcmp(func, "v:lua.", 6)) {
func += 6;
- len = check_luafunc_name((const char *)func, false);
+ len = check_luafunc_name(func, false);
if (len == 0) {
ret = FAIL;
goto fail;
@@ -1114,13 +1112,13 @@ fail:
/// @param[in] argv Array with typval_T arguments.
///
/// @return -1 when calling function fails, result of function otherwise.
-varnumber_T call_func_retnr(const char_u *func, int argc, typval_T *argv)
+varnumber_T call_func_retnr(const char *func, int argc, typval_T *argv)
FUNC_ATTR_NONNULL_ALL
{
typval_T rettv;
varnumber_T retval;
- if (call_vim_function(func, argc, argv, &rettv) == FAIL) {
+ if (call_vim_function((char *)func, argc, argv, &rettv) == FAIL) {
return -1;
}
retval = tv_get_number_chk(&rettv, NULL);
@@ -1140,7 +1138,7 @@ char *call_func_retstr(const char *const func, int argc, typval_T *argv)
{
typval_T rettv;
// All arguments are passed as strings, no conversion to number.
- if (call_vim_function((const char_u *)func, argc, argv, &rettv)
+ if (call_vim_function(func, argc, argv, &rettv)
== FAIL) {
return NULL;
}
@@ -1157,13 +1155,13 @@ char *call_func_retstr(const char *const func, int argc, typval_T *argv)
///
/// @return [allocated] NULL when calling function fails or return tv is not a
/// List, allocated List otherwise.
-void *call_func_retlist(const char_u *func, int argc, typval_T *argv)
+void *call_func_retlist(const char *func, int argc, typval_T *argv)
FUNC_ATTR_NONNULL_ALL
{
typval_T rettv;
// All arguments are passed as strings, no conversion to number.
- if (call_vim_function(func, argc, argv, &rettv) == FAIL) {
+ if (call_vim_function((char *)func, argc, argv, &rettv) == FAIL) {
return NULL;
}
@@ -1211,12 +1209,9 @@ void prof_child_exit(proftime_T *tm)
script_prof_restore(tm);
}
-
-/*
- * Evaluate 'foldexpr'. Returns the foldlevel, and any character preceding
- * it in "*cp". Doesn't give error messages.
- */
-int eval_foldexpr(char_u *arg, int *cp)
+/// Evaluate 'foldexpr'. Returns the foldlevel, and any character preceding
+/// it in "*cp". Doesn't give error messages.
+int eval_foldexpr(char *arg, int *cp)
{
typval_T tv;
varnumber_T retval;
@@ -1239,11 +1234,11 @@ int eval_foldexpr(char_u *arg, int *cp)
} else {
// If the result is a string, check if there is a non-digit before
// the number.
- char_u *s = tv.vval.v_string;
+ char *s = tv.vval.v_string;
if (!ascii_isdigit(*s) && *s != '-') {
- *cp = *s++;
+ *cp = (char_u)(*s++);
}
- retval = atol((char *)s);
+ retval = atol(s);
}
tv_clear(&tv);
}
@@ -1256,33 +1251,34 @@ int eval_foldexpr(char_u *arg, int *cp)
return (int)retval;
}
-// ":cons[t] var = expr1" define constant
-// ":cons[t] [name1, name2, ...] = expr1" define constants unpacking list
-// ":cons[t] [name, ..., ; lastname] = expr" define constants unpacking list
+/// ":cons[t] var = expr1" define constant
+/// ":cons[t] [name1, name2, ...] = expr1" define constants unpacking list
+/// ":cons[t] [name, ..., ; lastname] = expr" define constants unpacking list
void ex_const(exarg_T *eap)
{
ex_let_const(eap, true);
}
-// Get a list of lines from a HERE document. The here document is a list of
-// lines surrounded by a marker.
-// cmd << {marker}
-// {line1}
-// {line2}
-// ....
-// {marker}
-//
-// The {marker} is a string. If the optional 'trim' word is supplied before the
-// marker, then the leading indentation before the lines (matching the
-// indentation in the 'cmd' line) is stripped.
-// Returns a List with {lines} or NULL.
-static list_T *heredoc_get(exarg_T *eap, char_u *cmd)
-{
- char_u *marker;
- char_u *p;
+/// Get a list of lines from a HERE document. The here document is a list of
+/// lines surrounded by a marker.
+/// cmd << {marker}
+/// {line1}
+/// {line2}
+/// ....
+/// {marker}
+///
+/// The {marker} is a string. If the optional 'trim' word is supplied before the
+/// marker, then the leading indentation before the lines (matching the
+/// indentation in the 'cmd' line) is stripped.
+///
+/// @return a List with {lines} or NULL.
+static list_T *heredoc_get(exarg_T *eap, char *cmd)
+{
+ char *marker;
+ char *p;
int marker_indent_len = 0;
int text_indent_len = 0;
- char_u *text_indent = NULL;
+ char *text_indent = NULL;
if (eap->getline == NULL) {
emsg(_("E991: cannot use =<< here"));
@@ -1310,7 +1306,7 @@ static list_T *heredoc_get(exarg_T *eap, char_u *cmd)
// The marker is the next word.
if (*cmd != NUL && *cmd != '"') {
marker = skipwhite(cmd);
- p = skiptowhite(marker);
+ p = (char *)skiptowhite((char_u *)marker);
if (*skipwhite(p) != NUL && *skipwhite(p) != '"') {
emsg(_(e_trailing));
return NULL;
@@ -1330,7 +1326,7 @@ static list_T *heredoc_get(exarg_T *eap, char_u *cmd)
int mi = 0;
int ti = 0;
- char_u *theline = eap->getline(NUL, eap->cookie, 0, false);
+ char *theline = eap->getline(NUL, eap->cookie, 0, false);
if (theline == NULL) {
semsg(_("E990: Missing end marker '%s'"), marker);
break;
@@ -1354,7 +1350,7 @@ static list_T *heredoc_get(exarg_T *eap, char_u *cmd)
p++;
text_indent_len++;
}
- text_indent = vim_strnsave(theline, text_indent_len);
+ text_indent = xstrnsave(theline, (size_t)text_indent_len);
}
// with "trim": skip the indent matching the first line
if (text_indent != NULL) {
@@ -1365,7 +1361,7 @@ static list_T *heredoc_get(exarg_T *eap, char_u *cmd)
}
}
- tv_list_append_string(l, (char *)(theline + ti), -1);
+ tv_list_append_string(l, theline + ti, -1);
xfree(theline);
}
xfree(text_indent);
@@ -1373,18 +1369,18 @@ static list_T *heredoc_get(exarg_T *eap, char_u *cmd)
return l;
}
-// ":let" list all variable values
-// ":let var1 var2" list variable values
-// ":let var = expr" assignment command.
-// ":let var += expr" assignment command.
-// ":let var -= expr" assignment command.
-// ":let var *= expr" assignment command.
-// ":let var /= expr" assignment command.
-// ":let var %= expr" assignment command.
-// ":let var .= expr" assignment command.
-// ":let var ..= expr" assignment command.
-// ":let [var1, var2] = expr" unpack list.
-// ":let [name, ..., ; lastname] = expr" unpack list.
+/// ":let" list all variable values
+/// ":let var1 var2" list variable values
+/// ":let var = expr" assignment command.
+/// ":let var += expr" assignment command.
+/// ":let var -= expr" assignment command.
+/// ":let var *= expr" assignment command.
+/// ":let var /= expr" assignment command.
+/// ":let var %= expr" assignment command.
+/// ":let var .= expr" assignment command.
+/// ":let var ..= expr" assignment command.
+/// ":let [var1, var2] = expr" unpack list.
+/// ":let [name, ..., ; lastname] = expr" unpack list.
void ex_let(exarg_T *eap)
{
ex_let_const(eap, false);
@@ -1392,17 +1388,17 @@ void ex_let(exarg_T *eap)
static void ex_let_const(exarg_T *eap, const bool is_const)
{
- char_u *arg = eap->arg;
- char_u *expr = NULL;
+ char *arg = eap->arg;
+ char *expr = NULL;
typval_T rettv;
int i;
int var_count = 0;
int semicolon = 0;
- char_u op[2];
- char_u *argend;
- int first = TRUE;
+ char op[2];
+ char *argend;
+ int first = true;
- argend = (char_u *)skip_var_list(arg, &var_count, &semicolon);
+ argend = (char *)skip_var_list(arg, &var_count, &semicolon);
if (argend == NULL) {
return;
}
@@ -1410,14 +1406,14 @@ static void ex_let_const(exarg_T *eap, const bool is_const)
argend--;
}
expr = skipwhite(argend);
- if (*expr != '=' && !((vim_strchr((char_u *)"+-*/%.", *expr) != NULL
+ if (*expr != '=' && !((vim_strchr("+-*/%.", *expr) != NULL
&& expr[1] == '=') || STRNCMP(expr, "..=", 3) == 0)) {
// ":let" without "=": list variables
if (*arg == '[') {
emsg(_(e_invarg));
} else if (!ends_excmd(*arg)) {
// ":let var1 var2"
- arg = (char_u *)list_arg_vars(eap, (const char *)arg, &first);
+ arg = (char *)list_arg_vars(eap, (const char *)arg, &first);
} else if (!eap->skip) {
// ":let"
list_glob_vars(&first);
@@ -1428,7 +1424,7 @@ static void ex_let_const(exarg_T *eap, const bool is_const)
list_func_vars(&first);
list_vim_vars(&first);
}
- eap->nextcmd = check_nextcmd(arg);
+ eap->nextcmd = (char *)check_nextcmd((char_u *)arg);
} else if (expr[0] == '=' && expr[1] == '<' && expr[2] == '<') {
// HERE document
list_T *l = heredoc_get(eap, expr + 3);
@@ -1438,7 +1434,7 @@ static void ex_let_const(exarg_T *eap, const bool is_const)
op[0] = '=';
op[1] = NUL;
(void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count,
- is_const, op);
+ is_const, (char *)op);
}
tv_clear(&rettv);
}
@@ -1446,7 +1442,7 @@ static void ex_let_const(exarg_T *eap, const bool is_const)
op[0] = '=';
op[1] = NUL;
if (*expr != '=') {
- if (vim_strchr((char_u *)"+-*/%.", *expr) != NULL) {
+ if (vim_strchr("+-*/%.", *expr) != NULL) {
op[0] = *expr; // +=, -=, *=, /=, %= or .=
if (expr[0] == '.' && expr[1] == '.') { // ..=
expr++;
@@ -1468,7 +1464,7 @@ static void ex_let_const(exarg_T *eap, const bool is_const)
emsg_skip--;
} else if (i != FAIL) {
(void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count,
- is_const, op);
+ is_const, (char *)op);
tv_clear(&rettv);
}
}
@@ -1486,10 +1482,10 @@ static void ex_let_const(exarg_T *eap, const bool is_const)
/// @param is_const lock variables for :const
///
/// @return OK or FAIL;
-static int ex_let_vars(char_u *arg_start, typval_T *tv, int copy, int semicolon, int var_count,
- int is_const, char_u *op)
+static int ex_let_vars(char *arg_start, typval_T *tv, int copy, int semicolon, int var_count,
+ int is_const, char *op)
{
- char_u *arg = arg_start;
+ char *arg = arg_start;
typval_T ltv;
if (*arg != '[') {
@@ -1523,11 +1519,10 @@ static int ex_let_vars(char_u *arg_start, typval_T *tv, int copy, int semicolon,
assert(l != NULL);
listitem_T *item = tv_list_first(l);
- size_t rest_len = tv_list_len(l);
+ size_t rest_len = (size_t)tv_list_len(l);
while (*arg != ']') {
arg = skipwhite(arg + 1);
- arg = ex_let_one(arg, TV_LIST_ITEM_TV(item), true, is_const,
- (const char_u *)",;]", op);
+ arg = ex_let_one(arg, TV_LIST_ITEM_TV(item), true, is_const, ",;]", op);
if (arg == NULL) {
return FAIL;
}
@@ -1538,7 +1533,7 @@ static int ex_let_vars(char_u *arg_start, typval_T *tv, int copy, int semicolon,
if (*arg == ';') {
/* Put the rest of the list (may be empty) in the var after ';'.
* Create a new list for this. */
- list_T *const rest_list = tv_list_alloc(rest_len);
+ list_T *const rest_list = tv_list_alloc((ptrdiff_t)rest_len);
while (item != NULL) {
tv_list_append_tv(rest_list, TV_LIST_ITEM_TV(item));
item = TV_LIST_ITEM_NEXT(l, item);
@@ -1549,8 +1544,7 @@ static int ex_let_vars(char_u *arg_start, typval_T *tv, int copy, int semicolon,
ltv.vval.v_list = rest_list;
tv_list_ref(rest_list);
- arg = ex_let_one(skipwhite(arg + 1), &ltv, false, is_const, (char_u *)"]",
- op);
+ arg = ex_let_one(skipwhite(arg + 1), &ltv, false, is_const, "]", op);
tv_clear(&ltv);
if (arg == NULL) {
return FAIL;
@@ -1565,24 +1559,23 @@ static int ex_let_vars(char_u *arg_start, typval_T *tv, int copy, int semicolon,
return OK;
}
-/*
- * Skip over assignable variable "var" or list of variables "[var, var]".
- * Used for ":let varvar = expr" and ":for varvar in expr".
- * For "[var, var]" increment "*var_count" for each variable.
- * for "[var, var; var]" set "semicolon".
- * Return NULL for an error.
- */
-static const char_u *skip_var_list(const char_u *arg, int *var_count, int *semicolon)
+/// Skip over assignable variable "var" or list of variables "[var, var]".
+/// Used for ":let varvar = expr" and ":for varvar in expr".
+/// For "[var, var]" increment "*var_count" for each variable.
+/// for "[var, var; var]" set "semicolon".
+///
+/// @return NULL for an error.
+static const char *skip_var_list(const char *arg, int *var_count, int *semicolon)
{
- const char_u *p;
- const char_u *s;
+ const char *p;
+ const char *s;
if (*arg == '[') {
// "[var, var]": find the matching ']'.
p = arg;
for (;;) {
p = skipwhite(p + 1); // skip whites after '[', ';' or ','
- s = skip_var_one(p);
+ s = skip_var_one((char *)p);
if (s == p) {
semsg(_(e_invarg2), p);
return NULL;
@@ -1605,27 +1598,24 @@ static const char_u *skip_var_list(const char_u *arg, int *var_count, int *semic
}
return p + 1;
} else {
- return skip_var_one(arg);
+ return skip_var_one((char *)arg);
}
}
-/*
- * Skip one (assignable) variable name, including @r, $VAR, &option, d.key,
- * l[idx].
- */
-static const char_u *skip_var_one(const char_u *arg)
+/// Skip one (assignable) variable name, including @r, $VAR, &option, d.key,
+/// l[idx].
+static const char *skip_var_one(const char *arg)
{
if (*arg == '@' && arg[1] != NUL) {
return arg + 1 + utfc_ptr2len(arg + 1);
}
- return find_name_end(*arg == '$' || *arg == '&' ? arg + 1 : arg,
- NULL, NULL, FNE_INCL_BR | FNE_CHECK_START);
+ return (char *)find_name_end(*arg == '$' || *arg == '&' ? arg + 1 : arg,
+ NULL, NULL, FNE_INCL_BR | FNE_CHECK_START);
}
-/*
- * List variables for hashtab "ht" with prefix "prefix".
- * If "empty" is TRUE also list NULL strings as empty strings.
- */
+/// List variables for hashtab "ht" with prefix "prefix".
+///
+/// @param empty if TRUE also list NULL strings as empty strings.
void list_hashtable_vars(hashtab_T *ht, const char *prefix, int empty, int *first)
{
hashitem_T *hi;
@@ -1654,47 +1644,37 @@ void list_hashtable_vars(hashtab_T *ht, const char *prefix, int empty, int *firs
}
}
-/*
- * List global variables.
- */
+/// List global variables.
static void list_glob_vars(int *first)
{
list_hashtable_vars(&globvarht, "", true, first);
}
-/*
- * List buffer variables.
- */
+/// List buffer variables.
static void list_buf_vars(int *first)
{
list_hashtable_vars(&curbuf->b_vars->dv_hashtab, "b:", true, first);
}
-/*
- * List window variables.
- */
+/// List window variables.
static void list_win_vars(int *first)
{
list_hashtable_vars(&curwin->w_vars->dv_hashtab, "w:", true, first);
}
-/*
- * List tab page variables.
- */
+/// List tab page variables.
static void list_tab_vars(int *first)
{
list_hashtable_vars(&curtab->tp_vars->dv_hashtab, "t:", true, first);
}
-/*
- * List Vim variables.
- */
+/// List Vim variables.
static void list_vim_vars(int *first)
{
list_hashtable_vars(&vimvarht, "v:", false, first);
}
-// List script-local variables, if there is a script.
+/// List script-local variables, if there is a script.
static void list_script_vars(int *first)
{
if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= ga_scripts.ga_len) {
@@ -1702,9 +1682,7 @@ static void list_script_vars(int *first)
}
}
-/*
- * List variables in "arg".
- */
+/// List variables in "arg".
static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first)
{
int error = FALSE;
@@ -1715,8 +1693,7 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first)
while (!ends_excmd(*arg) && !got_int) {
if (error || eap->skip) {
- arg = (const char *)find_name_end((char_u *)arg, NULL, NULL,
- FNE_INCL_BR | FNE_CHECK_START);
+ arg = find_name_end(arg, NULL, NULL, FNE_INCL_BR | FNE_CHECK_START);
if (!ascii_iswhite(*arg) && !ends_excmd(*arg)) {
emsg_severe = true;
emsg(_(e_trailing));
@@ -1746,9 +1723,7 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first)
} else {
// handle d.key, l[idx], f(expr)
const char *const arg_subsc = arg;
- if (handle_subscript(&arg, &tv, true, true, (const char_u *)name,
- (const char_u **)&name)
- == FAIL) {
+ if (handle_subscript(&arg, &tv, true, true, name, &name) == FAIL) {
error = true;
} else {
if (arg == arg_subsc && len == 2 && name[1] == ':') {
@@ -1790,7 +1765,7 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first)
xfree(tofree);
}
- arg = (const char *)skipwhite((const char_u *)arg);
+ arg = (const char *)skipwhite(arg);
}
return arg;
@@ -1809,14 +1784,14 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first)
///
/// @return a pointer to the char just after the var name or NULL in case of
/// error.
-static char_u *ex_let_one(char_u *arg, typval_T *const tv, const bool copy, const bool is_const,
- const char_u *const endchars, const char_u *const op)
+static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bool is_const,
+ const char *const endchars, const char *const op)
FUNC_ATTR_NONNULL_ARG(1, 2) FUNC_ATTR_WARN_UNUSED_RESULT
{
- char_u *arg_end = NULL;
+ char *arg_end = NULL;
int len;
int opt_flags;
- char_u *tofree = NULL;
+ char *tofree = NULL;
/*
* ":let $VAR = expr": Set environment variable.
@@ -1828,12 +1803,12 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, const bool copy, cons
}
// Find the end of the name.
arg++;
- char *name = (char *)arg;
- len = get_env_len((const char_u **)&arg);
+ char *name = arg;
+ len = get_env_len((const char **)&arg);
if (len == 0) {
semsg(_(e_invarg2), name - 1);
} else {
- if (op != NULL && vim_strchr((char_u *)"+-*/%", *op) != NULL) {
+ if (op != NULL && vim_strchr("+-*/%", *op) != NULL) {
semsg(_(e_letwrong), op);
} else if (endchars != NULL
&& vim_strchr(endchars, *skipwhite(arg)) == NULL) {
@@ -1846,7 +1821,7 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, const bool copy, cons
char *s = vim_getenv(name);
if (s != NULL) {
- tofree = concat_str((const char_u *)s, (const char_u *)p);
+ tofree = (char *)concat_str((const char_u *)s, (const char_u *)p);
p = (const char *)tofree;
xfree(s);
}
@@ -1879,7 +1854,7 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, const bool copy, cons
char *const p = (char *)find_option_end((const char **)&arg, &opt_flags);
if (p == NULL
|| (endchars != NULL
- && vim_strchr(endchars, *skipwhite((const char_u *)p)) == NULL)) {
+ && vim_strchr(endchars, *skipwhite(p)) == NULL)) {
emsg(_(e_letunexp));
} else {
int opt_type;
@@ -1895,8 +1870,7 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, const bool copy, cons
s = tv_get_string_chk(tv); // != NULL if number or string.
}
if (s != NULL && op != NULL && *op != '=') {
- opt_type = get_option_value((char *)arg, &numval, (char_u **)&stringval,
- opt_flags);
+ opt_type = get_option_value(arg, &numval, &stringval, opt_flags);
if ((opt_type == 1 && *op == '.')
|| (opt_type == 0 && *op != '.')) {
semsg(_(e_letwrong), op);
@@ -1927,7 +1901,7 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, const bool copy, cons
if (s != NULL || tv->v_type == VAR_BOOL
|| tv->v_type == VAR_SPECIAL) {
set_option_value((const char *)arg, n, s, opt_flags);
- arg_end = (char_u *)p;
+ arg_end = p;
}
*p = c1;
xfree(stringval);
@@ -1939,30 +1913,32 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, const bool copy, cons
return NULL;
}
arg++;
+
int regname = utf_ptr2char(arg);
int mblen = utf_ptr2len(arg);
- if (op != NULL && vim_strchr((char_u *)"+-*/%", *op) != NULL) {
+ if (op != NULL && vim_strchr("+-*/%", *op) != NULL) {
semsg(_(e_letwrong), op);
} else if (endchars != NULL
&& vim_strchr(endchars, *skipwhite(arg + mblen)) == NULL) {
emsg(_(e_letunexp));
} else {
- char_u *s;
+ char *s;
- char_u *ptofree = NULL;
+ char *ptofree = NULL;
const char *p = tv_get_string_chk(tv);
if (p != NULL && op != NULL && *op == '.') {
s = get_reg_contents(regname == '@' ? '"' : regname, kGRegExprSrc);
if (s != NULL) {
- ptofree = concat_str(s, (const char_u *)p);
+ ptofree = (char *)concat_str((char_u *)s, (const char_u *)p);
p = (const char *)ptofree;
xfree(s);
}
}
if (p != NULL) {
+
write_reg_contents(regname == '@' ? '"' : regname,
- (const char_u *)p, STRLEN(p), false);
+ (const char_u *)p, (ssize_t)STRLEN(p), false);
arg_end = arg + mblen;
}
xfree(ptofree);
@@ -1975,12 +1951,12 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, const bool copy, cons
else if (eval_isnamec1(*arg) || *arg == '{') {
lval_T lv;
- char_u *const p = get_lval(arg, tv, &lv, false, false, 0, FNE_CHECK_START);
+ char *const p = get_lval(arg, tv, &lv, false, false, 0, FNE_CHECK_START);
if (p != NULL && lv.ll_name != NULL) {
if (endchars != NULL && vim_strchr(endchars, *skipwhite(p)) == NULL) {
emsg(_(e_letunexp));
} else {
- set_var_lval(&lv, p, tv, copy, is_const, (const char *)op);
+ set_var_lval(&lv, p, tv, copy, is_const, op);
arg_end = p;
}
}
@@ -2017,8 +1993,8 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, const bool copy, cons
///
/// @return A pointer to just after the name, including indexes. Returns NULL
/// for a parsing error, but it is still needed to free items in lp.
-char_u *get_lval(char_u *const name, typval_T *const rettv, lval_T *const lp, const bool unlet,
- const bool skip, const int flags, const int fne_flags)
+char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const bool unlet,
+ const bool skip, const int flags, const int fne_flags)
FUNC_ATTR_NONNULL_ARG(1, 3)
{
dictitem_T *v;
@@ -2035,17 +2011,16 @@ char_u *get_lval(char_u *const name, typval_T *const rettv, lval_T *const lp, co
if (skip) {
// When skipping just find the end of the name.
lp->ll_name = (const char *)name;
- return (char_u *)find_name_end((const char_u *)name, NULL, NULL,
- FNE_INCL_BR | fne_flags);
+ return (char *)find_name_end(name, NULL, NULL,
+ FNE_INCL_BR | fne_flags);
}
// Find the end of the name.
- char_u *expr_start;
- char_u *expr_end;
- char_u *p = (char_u *)find_name_end(name,
- (const char_u **)&expr_start,
- (const char_u **)&expr_end,
- fne_flags);
+ char *expr_start;
+ char *expr_end;
+ char *p = (char *)find_name_end(name, (const char **)&expr_start,
+ (const char **)&expr_end,
+ fne_flags);
if (expr_start != NULL) {
// Don't expand the name when we already know there is an error.
if (unlet && !ascii_iswhite(*p) && !ends_excmd(*p)
@@ -2054,8 +2029,7 @@ char_u *get_lval(char_u *const name, typval_T *const rettv, lval_T *const lp, co
return NULL;
}
- lp->ll_exp_name = (char *)make_expanded_name(name, expr_start, expr_end,
- p);
+ lp->ll_exp_name = make_expanded_name(name, expr_start, expr_end, p);
lp->ll_name = lp->ll_exp_name;
if (lp->ll_exp_name == NULL) {
// Report an invalid expression in braces, unless the
@@ -2114,11 +2088,10 @@ char_u *get_lval(char_u *const name, typval_T *const rettv, lval_T *const lp, co
}
int len = -1;
- char_u *key = NULL;
+ char *key = NULL;
if (*p == '.') {
key = p + 1;
- for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; len++) {
- }
+ for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; len++) {}
if (len == 0) {
if (!quiet) {
emsg(_("E713: Cannot use empty key after ."));
@@ -2199,7 +2172,7 @@ char_u *get_lval(char_u *const name, typval_T *const rettv, lval_T *const lp, co
if (lp->ll_tv->v_type == VAR_DICT) {
if (len == -1) {
// "[key]": get key from "var1"
- key = (char_u *)tv_get_string(&var1); // is number or string
+ key = (char *)tv_get_string(&var1); // is number or string
}
lp->ll_list = NULL;
lp->ll_dict = lp->ll_tv->vval.v_dict;
@@ -2209,7 +2182,7 @@ char_u *get_lval(char_u *const name, typval_T *const rettv, lval_T *const lp, co
// variable name is valid (only variable name unless it is l: or
// g: dictionary). Disallow overwriting a builtin function.
if (rettv != NULL && lp->ll_dict->dv_scope != 0) {
- int prevval;
+ char prevval;
int wrong;
if (len != -1) {
@@ -2255,9 +2228,9 @@ char_u *get_lval(char_u *const name, typval_T *const rettv, lval_T *const lp, co
return NULL;
}
if (len == -1) {
- lp->ll_newkey = vim_strsave(key);
+ lp->ll_newkey = xstrdup(key);
} else {
- lp->ll_newkey = vim_strnsave(key, len);
+ lp->ll_newkey = xstrnsave(key, (size_t)len);
}
tv_clear(&var1);
break;
@@ -2315,11 +2288,11 @@ char_u *get_lval(char_u *const name, typval_T *const rettv, lval_T *const lp, co
lp->ll_dict = NULL;
lp->ll_list = lp->ll_tv->vval.v_list;
- lp->ll_li = tv_list_find(lp->ll_list, lp->ll_n1);
+ lp->ll_li = tv_list_find(lp->ll_list, (int)lp->ll_n1);
if (lp->ll_li == NULL) {
if (lp->ll_n1 < 0) {
lp->ll_n1 = 0;
- lp->ll_li = tv_list_find(lp->ll_list, lp->ll_n1);
+ lp->ll_li = tv_list_find(lp->ll_list, (int)lp->ll_n1);
}
}
if (lp->ll_li == NULL) {
@@ -2338,7 +2311,7 @@ char_u *get_lval(char_u *const name, typval_T *const rettv, lval_T *const lp, co
lp->ll_n2 = (long)tv_get_number(&var2); // Is number or string.
tv_clear(&var2);
if (lp->ll_n2 < 0) {
- ni = tv_list_find(lp->ll_list, lp->ll_n2);
+ ni = tv_list_find(lp->ll_list, (int)lp->ll_n2);
if (ni == NULL) {
if (!quiet) {
semsg(_(e_listidx), (int64_t)lp->ll_n2);
@@ -2370,9 +2343,7 @@ char_u *get_lval(char_u *const name, typval_T *const rettv, lval_T *const lp, co
// TODO(ZyX-I): move to eval/executor
-/*
- * Clear lval "lp" that was filled by get_lval().
- */
+/// Clear lval "lp" that was filled by get_lval().
void clear_lval(lval_T *lp)
{
xfree(lp->ll_exp_name);
@@ -2381,13 +2352,12 @@ void clear_lval(lval_T *lp)
// TODO(ZyX-I): move to eval/executor
-/*
- * Set a variable that was parsed by get_lval() to "rettv".
- * "endp" points to just after the parsed name.
- * "op" is NULL, "+" for "+=", "-" for "-=", "*" for "*=", "/" for "/=",
- * "%" for "%=", "." for ".=" or "=" for "=".
- */
-static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, const bool is_const,
+/// Set a variable that was parsed by get_lval() to "rettv".
+///
+/// @param endp points to just after the parsed name.
+/// @param op NULL, "+" for "+=", "-" for "-=", "*" for "*=", "/" for "/=",
+/// "%" for "%=", "." for ".=" or "=" for "=".
+static void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool is_const,
const char *op)
{
int cc;
@@ -2395,7 +2365,7 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, co
dictitem_T *di;
if (lp->ll_tv == NULL) {
- cc = *endp;
+ cc = (char_u)(*endp);
*endp = NUL;
if (lp->ll_blob != NULL) {
if (op != NULL && *op != '=') {
@@ -2419,12 +2389,12 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, co
lp->ll_n2 = tv_blob_len(lp->ll_blob);
}
- for (int il = lp->ll_n1, ir = 0; il <= lp->ll_n2; il++) {
+ for (int il = (int)lp->ll_n1, ir = 0; il <= (int)lp->ll_n2; il++) {
tv_blob_set(lp->ll_blob, il, tv_blob_get(rettv->vval.v_blob, ir++));
}
} else {
bool error = false;
- const char_u val = tv_get_number_chk(rettv, &error);
+ const char val = (char)tv_get_number_chk(rettv, &error);
if (!error) {
garray_T *const gap = &lp->ll_blob->bv_ga;
@@ -2432,7 +2402,7 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, co
// the end is an error otherwise.
if (lp->ll_n1 < gap->ga_len || lp->ll_n1 == gap->ga_len) {
ga_grow(&lp->ll_blob->bv_ga, 1);
- tv_blob_set(lp->ll_blob, lp->ll_n1, val);
+ tv_blob_set(lp->ll_blob, (int)lp->ll_n1, (char_u)val);
if (lp->ll_n1 == gap->ga_len) {
gap->ga_len++;
}
@@ -2445,7 +2415,7 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, co
if (is_const) {
emsg(_(e_cannot_mod));
- *endp = cc;
+ *endp = (char)cc;
return;
}
@@ -2464,14 +2434,15 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, co
} else {
set_var_const(lp->ll_name, lp->ll_name_len, rettv, copy, is_const);
}
- *endp = cc;
+ *endp = (char)cc;
} else if (var_check_lock(lp->ll_newkey == NULL
? lp->ll_tv->v_lock
: lp->ll_tv->vval.v_dict->dv_lock,
lp->ll_name, TV_CSTRING)) {
+ // Skip
} else if (lp->ll_range) {
listitem_T *ll_li = lp->ll_li;
- int ll_n1 = lp->ll_n1;
+ int ll_n1 = (int)lp->ll_n1;
if (is_const) {
emsg(_("E996: Cannot lock a range"));
@@ -2576,7 +2547,7 @@ notify:
if (watched) {
if (oldtv.v_type == VAR_UNKNOWN) {
assert(lp->ll_newkey != NULL);
- tv_dict_watcher_notify(dict, (char *)lp->ll_newkey, lp->ll_tv, NULL);
+ tv_dict_watcher_notify(dict, lp->ll_newkey, lp->ll_tv, NULL);
} else {
dictitem_T *di_ = lp->ll_di;
assert(di_->di_key != NULL);
@@ -2589,22 +2560,22 @@ notify:
// TODO(ZyX-I): move to eval/ex_cmds
-/*
- * Evaluate the expression used in a ":for var in expr" command.
- * "arg" points to "var".
- * Set "*errp" to TRUE for an error, FALSE otherwise;
- * Return a pointer that holds the info. Null when there is an error.
- */
-void *eval_for_line(const char_u *arg, bool *errp, char_u **nextcmdp, int skip)
+/// Evaluate the expression used in a ":for var in expr" command.
+/// "arg" points to "var".
+///
+/// @param[out] *errp set to TRUE for an error, FALSE otherwise;
+///
+/// @return a pointer that holds the info. Null when there is an error.
+void *eval_for_line(const char *arg, bool *errp, char **nextcmdp, int skip)
{
forinfo_T *fi = xcalloc(1, sizeof(forinfo_T));
- const char_u *expr;
+ const char *expr;
typval_T tv;
list_T *l;
*errp = true; // Default: there is an error.
- expr = skip_var_list(arg, &fi->fi_varcount, &fi->fi_semicolon);
+ expr = skip_var_list((char *)arg, &fi->fi_varcount, &fi->fi_semicolon);
if (expr == NULL) {
return fi;
}
@@ -2644,8 +2615,15 @@ void *eval_for_line(const char_u *arg, bool *errp, char_u **nextcmdp, int skip)
fi->fi_blob = btv.vval.v_blob;
}
tv_clear(&tv);
+ } else if (tv.v_type == VAR_STRING) {
+ fi->fi_byte_idx = 0;
+ fi->fi_string = tv.vval.v_string;
+ tv.vval.v_string = NULL;
+ if (fi->fi_string == NULL) {
+ fi->fi_string = xstrdup("");
+ }
} else {
- emsg(_(e_listblobreq));
+ emsg(_(e_string_list_or_blob_required));
tv_clear(&tv);
}
}
@@ -2659,13 +2637,12 @@ void *eval_for_line(const char_u *arg, bool *errp, char_u **nextcmdp, int skip)
// TODO(ZyX-I): move to eval/ex_cmds
-/*
- * Use the first item in a ":for" list. Advance to the next.
- * Assign the values to the variable (list). "arg" points to the first one.
- * Return TRUE when a valid item was found, FALSE when at end of list or
- * something wrong.
- */
-bool next_for_item(void *fi_void, char_u *arg)
+/// Use the first item in a ":for" list. Advance to the next.
+/// Assign the values to the variable (list). "arg" points to the first one.
+///
+/// @return true when a valid item was found, false when at end of list or
+/// something wrong.
+bool next_for_item(void *fi_void, char *arg)
{
forinfo_T *fi = (forinfo_T *)fi_void;
@@ -2678,8 +2655,23 @@ bool next_for_item(void *fi_void, char_u *arg)
tv.v_lock = VAR_FIXED;
tv.vval.v_number = tv_blob_get(fi->fi_blob, fi->fi_bi);
fi->fi_bi++;
- return ex_let_vars(arg, &tv, true,
- fi->fi_semicolon, fi->fi_varcount, false, NULL) == OK;
+ return ex_let_vars(arg, &tv, true, fi->fi_semicolon, fi->fi_varcount, false, NULL) == OK;
+ }
+
+ if (fi->fi_string != NULL) {
+ const int len = utfc_ptr2len(fi->fi_string + fi->fi_byte_idx);
+ if (len == 0) {
+ return false;
+ }
+ typval_T tv;
+ tv.v_type = VAR_STRING;
+ tv.v_lock = VAR_FIXED;
+ tv.vval.v_string = xstrnsave(fi->fi_string + fi->fi_byte_idx, (size_t)len);
+ fi->fi_byte_idx += len;
+ const int result
+ = ex_let_vars(arg, &tv, true, fi->fi_semicolon, fi->fi_varcount, false, NULL) == OK;
+ xfree(tv.vval.v_string);
+ return result;
}
listitem_T *item = fi->fi_lw.lw_item;
@@ -2694,34 +2686,35 @@ bool next_for_item(void *fi_void, char_u *arg)
// TODO(ZyX-I): move to eval/ex_cmds
-/*
- * Free the structure used to store info used by ":for".
- */
+/// Free the structure used to store info used by ":for".
void free_for_info(void *fi_void)
{
forinfo_T *fi = (forinfo_T *)fi_void;
- if (fi != NULL && fi->fi_list != NULL) {
+ if (fi == NULL) {
+ return;
+ }
+ if (fi->fi_list != NULL) {
tv_list_watch_remove(fi->fi_list, &fi->fi_lw);
tv_list_unref(fi->fi_list);
- }
- if (fi != NULL && fi->fi_blob != NULL) {
+ } else if (fi->fi_blob != NULL) {
tv_blob_unref(fi->fi_blob);
+ } else {
+ xfree(fi->fi_string);
}
xfree(fi);
}
-
-void set_context_for_expression(expand_T *xp, char_u *arg, cmdidx_T cmdidx)
+void set_context_for_expression(expand_T *xp, char *arg, cmdidx_T cmdidx)
FUNC_ATTR_NONNULL_ALL
{
int got_eq = FALSE;
int c;
- char_u *p;
+ char *p;
if (cmdidx == CMD_let || cmdidx == CMD_const) {
xp->xp_context = EXPAND_USER_VARS;
- if (vim_strpbrk(arg, (char_u *)"\"'+-*/%.=!?~|&$([<>,#") == NULL) {
+ if (strpbrk(arg, "\"'+-*/%.=!?~|&$([<>,#") == NULL) {
// ":let var1 var2 ...": find last space.
for (p = arg + STRLEN(arg); p >= arg;) {
xp->xp_pattern = p;
@@ -2736,11 +2729,10 @@ void set_context_for_expression(expand_T *xp, char_u *arg, cmdidx_T cmdidx)
xp->xp_context = cmdidx == CMD_call ? EXPAND_FUNCTIONS
: EXPAND_EXPRESSION;
}
- while ((xp->xp_pattern = vim_strpbrk(arg,
- (char_u *)"\"'+-*/%.=!?~|&$([<>,#")) != NULL) {
- c = *xp->xp_pattern;
+ while ((xp->xp_pattern = strpbrk(arg, "\"'+-*/%.=!?~|&$([<>,#")) != NULL) {
+ c = (uint8_t)(*xp->xp_pattern);
if (c == '&') {
- c = xp->xp_pattern[1];
+ c = (uint8_t)xp->xp_pattern[1];
if (c == '&') {
++xp->xp_pattern;
xp->xp_context = cmdidx != CMD_let || got_eq
@@ -2768,7 +2760,7 @@ void set_context_for_expression(expand_T *xp, char_u *arg, cmdidx_T cmdidx)
break;
} else if (cmdidx != CMD_let || got_eq) {
if (c == '"') { // string
- while ((c = *++xp->xp_pattern) != NUL && c != '"') {
+ while ((c = (uint8_t)(*++xp->xp_pattern)) != NUL && c != '"') {
if (c == '\\' && xp->xp_pattern[1] != NUL) {
xp->xp_pattern++;
}
@@ -2776,8 +2768,7 @@ void set_context_for_expression(expand_T *xp, char_u *arg, cmdidx_T cmdidx)
xp->xp_context = EXPAND_NOTHING;
} else if (c == '\'') { // literal string
// Trick: '' is like stopping and starting a literal string.
- while ((c = *++xp->xp_pattern) != NUL && c != '\'') {
- }
+ while ((c = (uint8_t)(*++xp->xp_pattern)) != NUL && c != '\'') {}
xp->xp_context = EXPAND_NOTHING;
} else if (c == '|') {
if (xp->xp_pattern[1] == '|') {
@@ -2796,8 +2787,7 @@ void set_context_for_expression(expand_T *xp, char_u *arg, cmdidx_T cmdidx)
}
arg = xp->xp_pattern;
if (*arg != NUL) {
- while ((c = *++arg) != NUL && (c == ' ' || c == '\t')) {
- }
+ while ((c = (char_u)(*++arg)) != NUL && (c == ' ' || c == '\t')) {}
}
}
@@ -2808,7 +2798,7 @@ void set_context_for_expression(expand_T *xp, char_u *arg, cmdidx_T cmdidx)
|| cmdidx == CMD_echomsg)
&& xp->xp_context == EXPAND_EXPRESSION) {
for (;;) {
- char_u *const n = skiptowhite(arg);
+ char *const n = (char *)skiptowhite((char_u *)arg);
if (n == arg || ascii_iswhite_or_nul(*skipwhite(n))) {
break;
@@ -2831,7 +2821,7 @@ void ex_unlet(exarg_T *eap)
/// ":lockvar" and ":unlockvar" commands
void ex_lockvar(exarg_T *eap)
{
- char_u *arg = eap->arg;
+ char *arg = eap->arg;
int deep = 2;
if (eap->forceit) {
@@ -2855,11 +2845,11 @@ void ex_lockvar(exarg_T *eap)
/// @param[in] deep Levels to (un)lock for :(un)lockvar, -1 to (un)lock
/// everything.
/// @param[in] callback Appropriate handler for the command.
-static void ex_unletlock(exarg_T *eap, char_u *argstart, int deep, ex_unletlock_callback callback)
+static void ex_unletlock(exarg_T *eap, char *argstart, int deep, ex_unletlock_callback callback)
FUNC_ATTR_NONNULL_ALL
{
- char_u *arg = argstart;
- char_u *name_end;
+ char *arg = argstart;
+ char *name_end;
bool error = false;
lval_T lv;
@@ -2868,7 +2858,7 @@ static void ex_unletlock(exarg_T *eap, char_u *argstart, int deep, ex_unletlock_
lv.ll_name = (const char *)arg;
lv.ll_tv = NULL;
arg++;
- if (get_env_len((const char_u **)&arg) == 0) {
+ if (get_env_len((const char **)&arg) == 0) {
semsg(_(e_invarg2), arg - 1);
return;
}
@@ -2906,7 +2896,7 @@ static void ex_unletlock(exarg_T *eap, char_u *argstart, int deep, ex_unletlock_
arg = skipwhite(name_end);
} while (!ends_excmd(*arg));
- eap->nextcmd = check_nextcmd(arg);
+ eap->nextcmd = (char *)check_nextcmd((char_u *)arg);
}
// TODO(ZyX-I): move to eval/ex_cmds
@@ -2919,7 +2909,7 @@ static void ex_unletlock(exarg_T *eap, char_u *argstart, int deep, ex_unletlock_
/// @param[in] deep Unused.
///
/// @return OK on success, or FAIL on failure.
-static int do_unlet_var(lval_T *lp, char_u *name_end, exarg_T *eap, int deep FUNC_ATTR_UNUSED)
+static int do_unlet_var(lval_T *lp, char *name_end, exarg_T *eap, int deep FUNC_ATTR_UNUSED)
FUNC_ATTR_NONNULL_ALL
{
int forceit = eap->forceit;
@@ -2927,7 +2917,7 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, exarg_T *eap, int deep FUN
int cc;
if (lp->ll_tv == NULL) {
- cc = *name_end;
+ cc = (char_u)(*name_end);
*name_end = NUL;
// Environment variable, normal name or expanded name.
@@ -2936,7 +2926,7 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, exarg_T *eap, int deep FUN
} else if (do_unlet(lp->ll_name, lp->ll_name_len, forceit) == FAIL) {
ret = FAIL;
}
- *name_end = cc;
+ *name_end = (char)cc;
} else if ((lp->ll_list != NULL
// ll_list is not NULL when lvalue is not in a list, NULL lists
// yield E689.
@@ -3034,7 +3024,7 @@ int do_unlet(const char *const name, const size_t name_len, const bool forceit)
}
}
- hashitem_T *hi = hash_find(ht, (const char_u *)varname);
+ hashitem_T *hi = hash_find(ht, varname);
if (HASHITEM_EMPTY(hi)) {
hi = find_hi_in_scoped_ht(name, &ht);
}
@@ -3085,7 +3075,7 @@ int do_unlet(const char *const name, const size_t name_len, const bool forceit)
/// @param[in] deep Levels to (un)lock, -1 to (un)lock everything.
///
/// @return OK on success, or FAIL on failure.
-static int do_lock_var(lval_T *lp, char_u *name_end FUNC_ATTR_UNUSED, exarg_T *eap, int deep)
+static int do_lock_var(lval_T *lp, char *name_end FUNC_ATTR_UNUSED, exarg_T *eap, int deep)
FUNC_ATTR_NONNULL_ARG(1, 3)
{
bool lock = eap->cmdidx == CMD_lockvar;
@@ -3116,7 +3106,7 @@ static int do_lock_var(lval_T *lp, char_u *name_end FUNC_ATTR_UNUSED, exarg_T *e
if (lock) {
di->di_flags |= DI_FLAGS_LOCK;
} else {
- di->di_flags &= ~DI_FLAGS_LOCK;
+ di->di_flags &= (uint8_t)(~DI_FLAGS_LOCK);
}
tv_item_lock(&di->di_tv, deep, lock, false);
}
@@ -3141,9 +3131,7 @@ static int do_lock_var(lval_T *lp, char_u *name_end FUNC_ATTR_UNUSED, exarg_T *e
return ret;
}
-/*
- * Delete all "menutrans_" variables.
- */
+/// Delete all "menutrans_" variables.
void del_menutrans_vars(void)
{
hash_lock(&globvarht);
@@ -3161,14 +3149,11 @@ void del_menutrans_vars(void)
* get_user_var_name().
*/
-
-static char_u *varnamebuf = NULL;
+static char *varnamebuf = NULL;
static size_t varnamebuflen = 0;
-/*
- * Function to concatenate a prefix and a variable name.
- */
-char_u *cat_prefix_varname(int prefix, const char_u *name)
+/// Function to concatenate a prefix and a variable name.
+char *cat_prefix_varname(int prefix, const char *name)
FUNC_ATTR_NONNULL_ALL
{
size_t len = STRLEN(name) + 3;
@@ -3179,17 +3164,15 @@ char_u *cat_prefix_varname(int prefix, const char_u *name)
varnamebuf = xmalloc(len);
varnamebuflen = len;
}
- *varnamebuf = prefix;
+ *varnamebuf = (char)prefix;
varnamebuf[1] = ':';
STRCPY(varnamebuf + 2, name);
return varnamebuf;
}
-/*
- * Function given to ExpandGeneric() to obtain the list of user defined
- * (global/buffer/window/built-in) variable names.
- */
-char_u *get_user_var_name(expand_T *xp, int idx)
+/// Function given to ExpandGeneric() to obtain the list of user defined
+/// (global/buffer/window/built-in) variable names.
+char *get_user_var_name(expand_T *xp, int idx)
{
static size_t gdone;
static size_t bdone;
@@ -3214,16 +3197,13 @@ char_u *get_user_var_name(expand_T *xp, int idx)
++hi;
}
if (STRNCMP("g:", xp->xp_pattern, 2) == 0) {
- return cat_prefix_varname('g', hi->hi_key);
+ return cat_prefix_varname('g', (char *)hi->hi_key);
}
- return hi->hi_key;
+ return (char *)hi->hi_key;
}
// b: variables
- // In cmdwin, the alternative buffer should be used.
- hashtab_T *ht = (cmdwin_type != 0 && get_cmdline_type() == NUL)
- ? &prevwin->w_buffer->b_vars->dv_hashtab
- : &curbuf->b_vars->dv_hashtab;
+ const hashtab_T *ht = &prevwin_curwin()->w_buffer->b_vars->dv_hashtab;
if (bdone < ht->ht_used) {
if (bdone++ == 0) {
hi = ht->ht_array;
@@ -3233,14 +3213,11 @@ char_u *get_user_var_name(expand_T *xp, int idx)
while (HASHITEM_EMPTY(hi)) {
++hi;
}
- return cat_prefix_varname('b', hi->hi_key);
+ return cat_prefix_varname('b', (char *)hi->hi_key);
}
// w: variables
- // In cmdwin, the alternative window should be used.
- ht = (cmdwin_type != 0 && get_cmdline_type() == NUL)
- ? &prevwin->w_vars->dv_hashtab
- : &curwin->w_vars->dv_hashtab;
+ ht = &prevwin_curwin()->w_vars->dv_hashtab;
if (wdone < ht->ht_used) {
if (wdone++ == 0) {
hi = ht->ht_array;
@@ -3250,7 +3227,7 @@ char_u *get_user_var_name(expand_T *xp, int idx)
while (HASHITEM_EMPTY(hi)) {
++hi;
}
- return cat_prefix_varname('w', hi->hi_key);
+ return cat_prefix_varname('w', (char *)hi->hi_key);
}
// t: variables
@@ -3264,12 +3241,12 @@ char_u *get_user_var_name(expand_T *xp, int idx)
while (HASHITEM_EMPTY(hi)) {
++hi;
}
- return cat_prefix_varname('t', hi->hi_key);
+ return cat_prefix_varname('t', (char *)hi->hi_key);
}
// v: variables
if (vidx < ARRAY_SIZE(vimvars)) {
- return cat_prefix_varname('v', (char_u *)vimvars[vidx++].vv_name);
+ return cat_prefix_varname('v', vimvars[vidx++].vv_name);
}
XFREE_CLEAR(varnamebuf);
@@ -3279,20 +3256,21 @@ char_u *get_user_var_name(expand_T *xp, int idx)
// TODO(ZyX-I): move to eval/expressions
-/// Return TRUE if "pat" matches "text".
/// Does not use 'cpo' and always uses 'magic'.
-static int pattern_match(char_u *pat, char_u *text, bool ic)
+///
+/// @return TRUE if "pat" matches "text".
+int pattern_match(char *pat, char *text, bool ic)
{
int matches = 0;
regmatch_T regmatch;
// avoid 'l' flag in 'cpoptions'
- char_u *save_cpo = p_cpo;
- p_cpo = (char_u *)"";
+ char *save_cpo = p_cpo;
+ p_cpo = "";
regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
if (regmatch.regprog != NULL) {
regmatch.rm_ic = ic;
- matches = vim_regexec_nl(&regmatch, text, (colnr_T)0);
+ matches = vim_regexec_nl(&regmatch, (char_u *)text, (colnr_T)0);
vim_regfree(regmatch.regprog);
}
p_cpo = save_cpo;
@@ -3301,30 +3279,30 @@ static int pattern_match(char_u *pat, char_u *text, bool ic)
/// Handle a name followed by "(". Both for just "name(arg)" and for
/// "expr->name(arg)".
-//
+///
/// @param arg Points to "(", will be advanced
/// @param basetv "expr" for "expr->name(arg)"
-//
+///
/// @return OK or FAIL.
-static int eval_func(char_u **const arg, char_u *const name, const int name_len,
- typval_T *const rettv, const bool evaluate, typval_T *const basetv)
+static int eval_func(char **const arg, char *const name, const int name_len, typval_T *const rettv,
+ const bool evaluate, typval_T *const basetv)
FUNC_ATTR_NONNULL_ARG(1, 2, 4)
{
- char_u *s = name;
+ char *s = name;
int len = name_len;
if (!evaluate) {
- check_vars((const char *)s, len);
+ check_vars((const char *)s, (size_t)len);
}
// If "s" is the name of a variable of type VAR_FUNC
// use its contents.
partial_T *partial;
- s = deref_func_name((const char *)s, &len, &partial, !evaluate);
+ s = (char *)deref_func_name((const char *)s, &len, &partial, !evaluate);
// Need to make a copy, in case evaluating the arguments makes
// the name invalid.
- s = xmemdupz(s, len);
+ s = xmemdupz(s, (size_t)len);
// Invoke the function.
funcexe_T funcexe = FUNCEXE_INIT;
@@ -3333,7 +3311,7 @@ static int eval_func(char_u **const arg, char_u *const name, const int name_len,
funcexe.evaluate = evaluate;
funcexe.partial = partial;
funcexe.basetv = basetv;
- int ret = get_func_tv(s, len, rettv, arg, &funcexe);
+ int ret = get_func_tv((char_u *)s, len, rettv, (char_u **)arg, &funcexe);
xfree(s);
@@ -3341,7 +3319,7 @@ static int eval_func(char_u **const arg, char_u *const name, const int name_len,
// get_func_tv, but it's needed in handle_subscript() to parse
// what follows. So set it here.
if (rettv->v_type == VAR_UNKNOWN && !evaluate && **arg == '(') {
- rettv->vval.v_string = (char_u *)tv_empty_string;
+ rettv->vval.v_string = (char *)tv_empty_string;
rettv->v_type = VAR_FUNC;
}
@@ -3365,17 +3343,16 @@ static int eval_func(char_u **const arg, char_u *const name, const int name_len,
* VAR_UNKNOWN. The function still returns FAIL for a syntax error.
*/
-/*
- * Handle zero level expression.
- * This calls eval1() and handles error message and nextcmd.
- * Put the result in "rettv" when returning OK and "evaluate" is TRUE.
- * Note: "rettv.v_lock" is not set.
- * Return OK or FAIL.
- */
-int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate)
+/// Handle zero level expression.
+/// This calls eval1() and handles error message and nextcmd.
+/// Put the result in "rettv" when returning OK and "evaluate" is TRUE.
+/// Note: "rettv.v_lock" is not set.
+///
+/// @return OK or FAIL.
+int eval0(char *arg, typval_T *rettv, char **nextcmd, int evaluate)
{
int ret;
- char_u *p;
+ char *p;
const int did_emsg_before = did_emsg;
const int called_emsg_before = called_emsg;
@@ -3396,7 +3373,7 @@ int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate)
ret = FAIL;
}
if (nextcmd != NULL) {
- *nextcmd = check_nextcmd(p);
+ *nextcmd = (char *)check_nextcmd((char_u *)p);
}
return ret;
@@ -3404,18 +3381,16 @@ int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate)
// TODO(ZyX-I): move to eval/expressions
-/*
- * Handle top level expression:
- * expr2 ? expr1 : expr1
- *
- * "arg" must point to the first non-white of the expression.
- * "arg" is advanced to the next non-white after the recognized expression.
- *
- * Note: "rettv.v_lock" is not set.
- *
- * Return OK or FAIL.
- */
-int eval1(char_u **arg, typval_T *rettv, int evaluate)
+/// Handle top level expression:
+/// expr2 ? expr1 : expr1
+///
+/// "arg" must point to the first non-white of the expression.
+/// "arg" is advanced to the next non-white after the recognized expression.
+///
+/// Note: "rettv.v_lock" is not set.
+///
+/// @return OK or FAIL.
+int eval1(char **arg, typval_T *rettv, int evaluate)
{
int result;
typval_T var2;
@@ -3480,16 +3455,14 @@ int eval1(char_u **arg, typval_T *rettv, int evaluate)
// TODO(ZyX-I): move to eval/expressions
-/*
- * Handle first level expression:
- * expr2 || expr2 || expr2 logical OR
- *
- * "arg" must point to the first non-white of the expression.
- * "arg" is advanced to the next non-white after the recognized expression.
- *
- * Return OK or FAIL.
- */
-static int eval2(char_u **arg, typval_T *rettv, int evaluate)
+/// Handle first level expression:
+/// expr2 || expr2 || expr2 logical OR
+///
+/// "arg" must point to the first non-white of the expression.
+/// "arg" is advanced to the next non-white after the recognized expression.
+///
+/// @return OK or FAIL.
+static int eval2(char **arg, typval_T *rettv, int evaluate)
{
typval_T var2;
long result;
@@ -3551,16 +3524,14 @@ static int eval2(char_u **arg, typval_T *rettv, int evaluate)
// TODO(ZyX-I): move to eval/expressions
-/*
- * Handle second level expression:
- * expr3 && expr3 && expr3 logical AND
- *
- * "arg" must point to the first non-white of the expression.
- * "arg" is advanced to the next non-white after the recognized expression.
- *
- * Return OK or FAIL.
- */
-static int eval3(char_u **arg, typval_T *rettv, int evaluate)
+/// Handle second level expression:
+/// expr3 && expr3 && expr3 logical AND
+///
+/// @param arg must point to the first non-white of the expression.
+/// `arg` is advanced to the next non-white after the recognized expression.
+///
+/// @return OK or FAIL.
+static int eval3(char **arg, typval_T *rettv, int evaluate)
{
typval_T var2;
long result;
@@ -3622,28 +3593,26 @@ static int eval3(char_u **arg, typval_T *rettv, int evaluate)
// TODO(ZyX-I): move to eval/expressions
-/*
- * Handle third level expression:
- * var1 == var2
- * var1 =~ var2
- * var1 != var2
- * var1 !~ var2
- * var1 > var2
- * var1 >= var2
- * var1 < var2
- * var1 <= var2
- * var1 is var2
- * var1 isnot var2
- *
- * "arg" must point to the first non-white of the expression.
- * "arg" is advanced to the next non-white after the recognized expression.
- *
- * Return OK or FAIL.
- */
-static int eval4(char_u **arg, typval_T *rettv, int evaluate)
+/// Handle third level expression:
+/// var1 == var2
+/// var1 =~ var2
+/// var1 != var2
+/// var1 !~ var2
+/// var1 > var2
+/// var1 >= var2
+/// var1 < var2
+/// var1 <= var2
+/// var1 is var2
+/// var1 isnot var2
+///
+/// "arg" must point to the first non-white of the expression.
+/// "arg" is advanced to the next non-white after the recognized expression.
+///
+/// @return OK or FAIL.
+static int eval4(char **arg, typval_T *rettv, int evaluate)
{
typval_T var2;
- char_u *p;
+ char *p;
exprtype_T type = EXPR_UNKNOWN;
int len = 2;
bool ic;
@@ -3733,26 +3702,24 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate)
// TODO(ZyX-I): move to eval/expressions
-/*
- * Handle fourth level expression:
- * + number addition
- * - number subtraction
- * . string concatenation
- * .. string concatenation
- *
- * "arg" must point to the first non-white of the expression.
- * "arg" is advanced to the next non-white after the recognized expression.
- *
- * Return OK or FAIL.
- */
-static int eval5(char_u **arg, typval_T *rettv, int evaluate)
+/// Handle fourth level expression:
+/// + number addition
+/// - number subtraction
+/// . string concatenation
+/// .. string concatenation
+///
+/// @param arg must point to the first non-white of the expression.
+/// `arg` is advanced to the next non-white after the recognized expression.
+///
+/// @return OK or FAIL.
+static int eval5(char **arg, typval_T *rettv, int evaluate)
{
typval_T var2;
typval_T var3;
int op;
varnumber_T n1, n2;
float_T f1 = 0, f2 = 0;
- char_u *p;
+ char *p;
/*
* Get the first variable.
@@ -3765,7 +3732,7 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate)
* Repeat computing, until no '+', '-' or '.' is following.
*/
for (;;) {
- op = **arg;
+ op = (char_u)(**arg);
if (op != '+' && op != '-' && op != '.') {
break;
}
@@ -3812,7 +3779,7 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate)
tv_clear(&var2);
return FAIL;
}
- p = concat_str((const char_u *)s1, (const char_u *)s2);
+ p = (char *)concat_str((const char_u *)s1, (const char_u *)s2);
tv_clear(rettv);
rettv->v_type = VAR_STRING;
rettv->vval.v_string = p;
@@ -3823,10 +3790,10 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate)
blob_T *const b = tv_blob_alloc();
for (int i = 0; i < tv_blob_len(b1); i++) {
- ga_append(&b->bv_ga, tv_blob_get(b1, i));
+ ga_append(&b->bv_ga, (char)tv_blob_get(b1, i));
}
for (int i = 0; i < tv_blob_len(b2); i++) {
- ga_append(&b->bv_ga, tv_blob_get(b2, i));
+ ga_append(&b->bv_ga, (char)tv_blob_get(b2, i));
}
tv_clear(rettv);
@@ -3860,7 +3827,7 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate)
return FAIL;
}
if (var2.v_type == VAR_FLOAT) {
- f1 = n1;
+ f1 = (float_T)n1;
}
}
if (var2.v_type == VAR_FLOAT) {
@@ -3874,7 +3841,7 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate)
return FAIL;
}
if (rettv->v_type == VAR_FLOAT) {
- f2 = n2;
+ f2 = (float_T)n2;
}
}
tv_clear(rettv);
@@ -3919,7 +3886,7 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate)
/// @param[in] want_string True if "." is string_concatenation, otherwise
/// float
/// @return OK or FAIL.
-static int eval6(char_u **arg, typval_T *rettv, int evaluate, int want_string)
+static int eval6(char **arg, typval_T *rettv, int evaluate, int want_string)
FUNC_ATTR_NO_SANITIZE_UNDEFINED
{
typval_T var2;
@@ -3940,7 +3907,7 @@ static int eval6(char_u **arg, typval_T *rettv, int evaluate, int want_string)
* Repeat computing, until no '*', '/' or '%' is following.
*/
for (;;) {
- op = **arg;
+ op = (char_u)(**arg);
if (op != '*' && op != '/' && op != '%') {
break;
}
@@ -3965,14 +3932,14 @@ static int eval6(char_u **arg, typval_T *rettv, int evaluate, int want_string)
* Get the second variable.
*/
*arg = skipwhite(*arg + 1);
- if (eval7(arg, &var2, evaluate, FALSE) == FAIL) {
+ if (eval7(arg, &var2, evaluate, false) == FAIL) {
return FAIL;
}
if (evaluate) {
if (var2.v_type == VAR_FLOAT) {
if (!use_float) {
- f1 = n1;
+ f1 = (float_T)n1;
use_float = true;
}
f2 = var2.vval.v_float;
@@ -3984,7 +3951,7 @@ static int eval6(char_u **arg, typval_T *rettv, int evaluate, int want_string)
return FAIL;
}
if (use_float) {
- f2 = n2;
+ f2 = (float_T)n2;
}
}
@@ -3996,19 +3963,16 @@ static int eval6(char_u **arg, typval_T *rettv, int evaluate, int want_string)
if (op == '*') {
f1 = f1 * f2;
} else if (op == '/') {
+ // uncrustify:off
+
// Division by zero triggers error from AddressSanitizer
- f1 = (f2 == 0
- ? (
+ f1 = (f2 == 0 ? (
#ifdef NAN
- f1 == 0
- ? NAN
- :
+ f1 == 0 ? (float_T)NAN :
#endif
- (f1 > 0
- ? INFINITY
- : -INFINITY)
- )
- : f1 / f2);
+ (f1 > 0 ? (float_T)INFINITY : (float_T)-INFINITY)) : f1 / f2);
+
+ // uncrustify:on
} else {
emsg(_("E804: Cannot use '%' with Float"));
return FAIL;
@@ -4063,14 +4027,14 @@ static int eval6(char_u **arg, typval_T *rettv, int evaluate, int want_string)
/// @param want_string after "." operator
///
/// @return OK or FAIL.
-static int eval7(char_u **arg, typval_T *rettv, int evaluate, int want_string)
+static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
{
varnumber_T n;
int len;
- char_u *s;
- const char_u *start_leader, *end_leader;
+ char *s;
+ const char *start_leader, *end_leader;
int ret = OK;
- char_u *alias;
+ char *alias;
// Initialise variable so that tv_clear() can't mistake this for a
// string and free a string that isn't there.
@@ -4095,7 +4059,7 @@ static int eval7(char_u **arg, typval_T *rettv, int evaluate, int want_string)
case '7':
case '8':
case '9': {
- char_u *p = skipdigits(*arg + 1);
+ char *p = skipdigits(*arg + 1);
int get_float = false;
// We accept a float when the format matches
@@ -4124,7 +4088,7 @@ static int eval7(char_u **arg, typval_T *rettv, int evaluate, int want_string)
if (get_float) {
float_T f;
- *arg += string2float((char *)*arg, &f);
+ *arg += string2float(*arg, &f);
if (evaluate) {
rettv->v_type = VAR_FLOAT;
rettv->vval.v_float = f;
@@ -4135,7 +4099,7 @@ static int eval7(char_u **arg, typval_T *rettv, int evaluate, int want_string)
if (evaluate) {
blob = tv_blob_alloc();
}
- char_u *bp;
+ char *bp;
for (bp = *arg + 2; ascii_isxdigit(bp[0]); bp += 2) {
if (!ascii_isxdigit(bp[1])) {
if (blob != NULL) {
@@ -4148,7 +4112,7 @@ static int eval7(char_u **arg, typval_T *rettv, int evaluate, int want_string)
break;
}
if (blob != NULL) {
- ga_append(&blob->bv_ga, (hex2nr(*bp) << 4) + hex2nr(*(bp + 1)));
+ ga_append(&blob->bv_ga, (char)((hex2nr(*bp) << 4) + hex2nr(*(bp + 1))));
}
if (bp[2] == '.' && ascii_isxdigit(bp[3])) {
bp++;
@@ -4160,7 +4124,7 @@ static int eval7(char_u **arg, typval_T *rettv, int evaluate, int want_string)
*arg = bp;
} else {
// decimal, hex or octal number
- vim_str2nr(*arg, NULL, &len, STR2NR_ALL, &n, NULL, 0, true);
+ vim_str2nr((char_u *)(*arg), NULL, &len, STR2NR_ALL, &n, NULL, 0, true);
if (len == 0) {
semsg(_(e_invexpr2), *arg);
ret = FAIL;
@@ -4203,7 +4167,7 @@ static int eval7(char_u **arg, typval_T *rettv, int evaluate, int want_string)
// Lambda: {arg, arg -> expr}
// Dictionary: {'key': val, 'key': val}
case '{':
- ret = get_lambda_tv(arg, rettv, evaluate);
+ ret = get_lambda_tv((char_u **)arg, rettv, evaluate);
if (ret == NOTDONE) {
ret = dict_get_tv(arg, rettv, evaluate, false);
}
@@ -4253,7 +4217,7 @@ static int eval7(char_u **arg, typval_T *rettv, int evaluate, int want_string)
// Must be a variable or function name.
// Can also be a curly-braces kind of name: {expr}.
s = *arg;
- len = get_name_len((const char **)arg, (char **)&alias, evaluate, true);
+ len = get_name_len((const char **)arg, &alias, evaluate, true);
if (alias != NULL) {
s = alias;
}
@@ -4266,7 +4230,7 @@ static int eval7(char_u **arg, typval_T *rettv, int evaluate, int want_string)
} else if (evaluate) {
ret = get_var_tv((const char *)s, len, rettv, NULL, true, false);
} else {
- check_vars((const char *)s, len);
+ check_vars((const char *)s, (size_t)len);
ret = OK;
}
}
@@ -4279,24 +4243,25 @@ static int eval7(char_u **arg, typval_T *rettv, int evaluate, int want_string)
// expr(expr), expr->name(expr)
if (ret == OK) {
ret = handle_subscript((const char **)arg, rettv, evaluate, true,
- start_leader, &end_leader);
+ (char *)start_leader, &end_leader);
}
// Apply logical NOT and unary '-', from right to left, ignore '+'.
if (ret == OK && evaluate && end_leader > start_leader) {
- ret = eval7_leader(rettv, start_leader, &end_leader);
+ ret = eval7_leader(rettv, (char *)start_leader, &end_leader);
}
return ret;
}
/// Apply the leading "!" and "-" before an eval7 expression to "rettv".
/// Adjusts "end_leaderp" until it is at "start_leader".
-/// @return OK on success, FAIL on failure.
-static int eval7_leader(typval_T *const rettv, const char_u *const start_leader,
- const char_u **const end_leaderp)
+///
+/// @return OK on success, FAIL on failure.
+static int eval7_leader(typval_T *const rettv, const char *const start_leader,
+ const char **const end_leaderp)
FUNC_ATTR_NONNULL_ALL
{
- const char_u *end_leader = *end_leaderp;
+ const char *end_leader = (char *)(*end_leaderp);
int ret = OK;
bool error = false;
varnumber_T val = 0;
@@ -4315,7 +4280,7 @@ static int eval7_leader(typval_T *const rettv, const char_u *const start_leader,
end_leader--;
if (*end_leader == '!') {
if (rettv->v_type == VAR_FLOAT) {
- f = !f;
+ f = !(bool)f;
} else {
val = !val;
}
@@ -4345,15 +4310,15 @@ static int eval7_leader(typval_T *const rettv, const char_u *const start_leader,
/// @param lua_funcname If `rettv` refers to a v:lua function, this must point
/// to the name of the Lua function to call (after the
/// "v:lua." prefix).
-/// @return OK on success, FAIL on failure.
-static int call_func_rettv(char_u **const arg, typval_T *const rettv, const bool evaluate,
+/// @return OK on success, FAIL on failure.
+static int call_func_rettv(char **const arg, typval_T *const rettv, const bool evaluate,
dict_T *const selfdict, typval_T *const basetv,
- const char_u *const lua_funcname)
+ const char *const lua_funcname)
FUNC_ATTR_NONNULL_ARG(1, 2)
{
partial_T *pt = NULL;
typval_T functv;
- const char_u *funcname;
+ const char *funcname;
bool is_lua = false;
// need to copy the funcref so that we can clear rettv
@@ -4370,7 +4335,7 @@ static int call_func_rettv(char_u **const arg, typval_T *const rettv, const bool
funcname = functv.vval.v_string;
}
} else {
- funcname = (char_u *)"";
+ funcname = "";
}
funcexe_T funcexe = FUNCEXE_INIT;
@@ -4380,8 +4345,8 @@ static int call_func_rettv(char_u **const arg, typval_T *const rettv, const bool
funcexe.partial = pt;
funcexe.selfdict = selfdict;
funcexe.basetv = basetv;
- const int ret = get_func_tv(funcname, is_lua ? *arg - funcname : -1, rettv,
- arg, &funcexe);
+ const int ret = get_func_tv((char_u *)funcname, is_lua ? (int)(*arg - funcname) : -1, rettv,
+ (char_u **)arg, &funcexe);
// Clear the funcref afterwards, so that deleting it while
// evaluating the arguments is possible (see test55).
@@ -4393,10 +4358,14 @@ static int call_func_rettv(char_u **const arg, typval_T *const rettv, const bool
}
/// Evaluate "->method()".
+///
/// @param verbose if true, give error messages.
-/// @note "*arg" points to the '-'.
-/// @return FAIL or OK. @note "*arg" is advanced to after the ')'.
-static int eval_lambda(char_u **const arg, typval_T *const rettv, const bool evaluate,
+/// @param *arg points to the '-'.
+///
+/// @return FAIL or OK.
+///
+/// @note "*arg" is advanced to after the ')'.
+static int eval_lambda(char **const arg, typval_T *const rettv, const bool evaluate,
const bool verbose)
FUNC_ATTR_NONNULL_ALL
{
@@ -4405,8 +4374,8 @@ static int eval_lambda(char_u **const arg, typval_T *const rettv, const bool eva
typval_T base = *rettv;
rettv->v_type = VAR_UNKNOWN;
- int ret = get_lambda_tv(arg, rettv, evaluate);
- if (ret == NOTDONE) {
+ int ret = get_lambda_tv((char_u **)arg, rettv, evaluate);
+ if (ret != OK) {
return FAIL;
} else if (**arg != '(') {
if (verbose) {
@@ -4432,9 +4401,11 @@ static int eval_lambda(char_u **const arg, typval_T *const rettv, const bool eva
}
/// Evaluate "->method()" or "->v:lua.method()".
-/// @note "*arg" points to the '-'.
-/// @return FAIL or OK. "*arg" is advanced to after the ')'.
-static int eval_method(char_u **const arg, typval_T *const rettv, const bool evaluate,
+///
+/// @param *arg points to the '-'.
+///
+/// @return FAIL or OK. "*arg" is advanced to after the ')'.
+static int eval_method(char **const arg, typval_T *const rettv, const bool evaluate,
const bool verbose)
FUNC_ATTR_NONNULL_ALL
{
@@ -4445,16 +4416,16 @@ static int eval_method(char_u **const arg, typval_T *const rettv, const bool eva
// Locate the method name.
int len;
- char_u *name = *arg;
- char_u *lua_funcname = NULL;
+ char *name = *arg;
+ char *lua_funcname = NULL;
if (STRNCMP(name, "v:lua.", 6) == 0) {
lua_funcname = name + 6;
- *arg = (char_u *)skip_luafunc_name((const char *)lua_funcname);
+ *arg = (char *)skip_luafunc_name((const char *)lua_funcname);
*arg = skipwhite(*arg); // to detect trailing whitespace later
- len = *arg - lua_funcname;
+ len = (int)(*arg - lua_funcname);
} else {
- char_u *alias;
- len = get_name_len((const char **)arg, (char **)&alias, evaluate, true);
+ char *alias;
+ len = get_name_len((const char **)arg, &alias, evaluate, true);
if (alias != NULL) {
name = alias;
}
@@ -4506,17 +4477,18 @@ static int eval_method(char_u **const arg, typval_T *const rettv, const bool eva
/// Evaluate an "[expr]" or "[expr:expr]" index. Also "dict.key".
/// "*arg" points to the '[' or '.'.
-/// Returns FAIL or OK. "*arg" is advanced to after the ']'.
///
/// @param verbose give error messages
-static int eval_index(char_u **arg, typval_T *rettv, int evaluate, int verbose)
+///
+/// @returns FAIL or OK. "*arg" is advanced to after the ']'.
+static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose)
{
bool empty1 = false;
bool empty2 = false;
long n1, n2 = 0;
ptrdiff_t len = -1;
int range = false;
- char_u *key = NULL;
+ char *key = NULL;
switch (rettv->v_type) {
case VAR_FUNC:
@@ -4556,8 +4528,7 @@ static int eval_index(char_u **arg, typval_T *rettv, int evaluate, int verbose)
* dict.name
*/
key = *arg + 1;
- for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; len++) {
- }
+ for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; len++) {}
if (len == 0) {
return FAIL;
}
@@ -4583,7 +4554,7 @@ static int eval_index(char_u **arg, typval_T *rettv, int evaluate, int verbose)
* Get the second variable from inside the [:].
*/
if (**arg == ':') {
- range = TRUE;
+ range = true;
*arg = skipwhite(*arg + 1);
if (**arg == ']') {
empty2 = true;
@@ -4668,7 +4639,7 @@ static int eval_index(char_u **arg, typval_T *rettv, int evaluate, int verbose)
}
tv_clear(rettv);
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = (char_u *)v;
+ rettv->vval.v_string = v;
break;
}
case VAR_BLOB:
@@ -4693,10 +4664,10 @@ static int eval_index(char_u **arg, typval_T *rettv, int evaluate, int verbose)
rettv->vval.v_blob = NULL;
} else {
blob_T *const blob = tv_blob_alloc();
- ga_grow(&blob->bv_ga, n2 - n1 + 1);
- blob->bv_ga.ga_len = n2 - n1 + 1;
+ ga_grow(&blob->bv_ga, (int)(n2 - n1 + 1));
+ blob->bv_ga.ga_len = (int)(n2 - n1 + 1);
for (long i = n1; i <= n2; i++) {
- tv_blob_set(blob, i - n1, tv_blob_get(rettv->vval.v_blob, i));
+ tv_blob_set(blob, (int)(i - n1), tv_blob_get(rettv->vval.v_blob, (int)i));
}
tv_clear(rettv);
tv_blob_set_ret(rettv, blob);
@@ -4708,7 +4679,7 @@ static int eval_index(char_u **arg, typval_T *rettv, int evaluate, int verbose)
n1 = len + n1;
}
if (n1 < len && n1 >= 0) {
- const int v = (int)tv_blob_get(rettv->vval.v_blob, n1);
+ const int v = (int)tv_blob_get(rettv->vval.v_blob, (int)n1);
tv_clear(rettv);
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = v;
@@ -4746,7 +4717,7 @@ static int eval_index(char_u **arg, typval_T *rettv, int evaluate, int verbose)
n2 = -1;
}
l = tv_list_alloc(n2 - n1 + 1);
- item = tv_list_find(rettv->vval.v_list, n1);
+ item = tv_list_find(rettv->vval.v_list, (int)n1);
while (n1++ <= n2) {
tv_list_append_tv(l, TV_LIST_ITEM_TV(item));
item = TV_LIST_ITEM_NEXT(rettv->vval.v_list, item);
@@ -4754,7 +4725,7 @@ static int eval_index(char_u **arg, typval_T *rettv, int evaluate, int verbose)
tv_clear(rettv);
tv_list_set_ret(rettv, l);
} else {
- tv_copy(TV_LIST_ITEM_TV(tv_list_find(rettv->vval.v_list, n1)), &var1);
+ tv_copy(TV_LIST_ITEM_TV(tv_list_find(rettv->vval.v_list, (int)n1)), &var1);
tv_clear(rettv);
*rettv = var1;
}
@@ -4771,7 +4742,7 @@ static int eval_index(char_u **arg, typval_T *rettv, int evaluate, int verbose)
}
if (len == -1) {
- key = (char_u *)tv_get_string_chk(&var1);
+ key = (char *)tv_get_string_chk(&var1);
if (key == NULL) {
tv_clear(&var1);
return FAIL;
@@ -4813,19 +4784,18 @@ static int eval_index(char_u **arg, typval_T *rettv, int evaluate, int verbose)
/// Get an option value
///
-/// @param[in,out] arg Points to the '&' or '+' before the option name. Is
+/// @param[in,out] arg Points to the '&' or '+' before the option name. Is
/// advanced to the character after the option name.
-/// @param[out] rettv Location where result is saved.
-/// @param[in] evaluate If not true, rettv is not populated.
+/// @param[out] rettv Location where result is saved.
+/// @param[in] evaluate If not true, rettv is not populated.
///
-/// @return OK or FAIL.
+/// @return OK or FAIL.
int get_option_tv(const char **const arg, typval_T *const rettv, const bool evaluate)
FUNC_ATTR_NONNULL_ARG(1)
{
long numval;
- char_u *stringval;
+ char *stringval;
int opt_type;
- int c;
bool working = (**arg == '+'); // has("+option")
int ret = OK;
int opt_flags;
@@ -4844,7 +4814,7 @@ int get_option_tv(const char **const arg, typval_T *const rettv, const bool eval
return OK;
}
- c = *option_end;
+ char c = *option_end;
*option_end = NUL;
opt_type = get_option_value(*arg, &numval,
rettv == NULL ? NULL : &stringval, opt_flags);
@@ -4878,13 +4848,12 @@ int get_option_tv(const char **const arg, typval_T *const rettv, const bool eval
return ret;
}
-/*
- * Allocate a variable for a string constant.
- * Return OK or FAIL.
- */
-static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
+/// Allocate a variable for a string constant.
+///
+/// @return OK or FAIL.
+static int get_string_tv(char **arg, typval_T *rettv, int evaluate)
{
- char_u *p;
+ char *p;
unsigned int extra = 0;
/*
@@ -4894,11 +4863,10 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
if (*p == '\\' && p[1] != NUL) {
p++;
// A "\<x>" form occupies at least 4 characters, and produces up
- // to 21 characters (3 * 6 for the char and 3 for a modifier):
- // reserve space for 18 extra.
- // Each byte in the char could be encoded as K_SPECIAL K_EXTRA x.
+ // to 9 characters (6 for the char and 3 for a modifier):
+ // reserve space for 5 extra.
if (*p == '<') {
- extra += 18;
+ extra += 5;
}
}
}
@@ -4919,7 +4887,7 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
* characters.
*/
const int len = (int)(p - *arg + extra);
- char_u *name = xmalloc(len);
+ char *name = xmalloc((size_t)len);
rettv->v_type = VAR_STRING;
rettv->vval.v_string = name;
@@ -4965,7 +4933,7 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
if (c != 'X') {
name += utf_char2bytes(nr, name);
} else {
- *name++ = nr;
+ *name++ = (char)nr;
}
}
break;
@@ -4979,19 +4947,24 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
case '5':
case '6':
case '7':
- *name = *p++ - '0';
+ *name = (char)(*p++ - '0');
if (*p >= '0' && *p <= '7') {
- *name = (*name << 3) + *p++ - '0';
+ *name = (char)((*name << 3) + *p++ - '0');
if (*p >= '0' && *p <= '7') {
- *name = (*name << 3) + *p++ - '0';
+ *name = (char)((*name << 3) + *p++ - '0');
}
}
++name;
break;
// Special key, e.g.: "\<C-W>"
- case '<':
- extra = trans_special((const char_u **)&p, STRLEN(p), name, true, true);
+ case '<': {
+ int flags = FSK_KEYCODE | FSK_IN_STRING;
+
+ if (p[1] != '*') {
+ flags |= FSK_SIMPLIFY;
+ }
+ extra = trans_special((const char_u **)&p, STRLEN(p), (char_u *)name, flags, false, NULL);
if (extra != 0) {
name += extra;
if (name >= rettv->vval.v_string + len) {
@@ -4999,14 +4972,15 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
}
break;
}
+ }
FALLTHROUGH;
default:
- mb_copy_char((const char_u **)&p, &name);
+ mb_copy_char((const char_u **)&p, (char_u **)&name);
break;
}
} else {
- mb_copy_char((const char_u **)&p, &name);
+ mb_copy_char((const char_u **)&p, (char_u **)&name);
}
}
*name = NUL;
@@ -5018,14 +4992,13 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
return OK;
}
-/*
- * Allocate a variable for a 'str''ing' constant.
- * Return OK or FAIL.
- */
-static int get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate)
+/// Allocate a variable for a 'str''ing' constant.
+///
+/// @return OK or FAIL.
+static int get_lit_string_tv(char **arg, typval_T *rettv, int evaluate)
{
- char_u *p;
- char_u *str;
+ char *p;
+ char *str;
int reduce = 0;
/*
@@ -5055,7 +5028,7 @@ static int get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate)
/*
* Copy the string into allocated memory, handling '' to ' reduction.
*/
- str = xmalloc((p - *arg) - reduce);
+ str = xmalloc((size_t)((p - *arg) - reduce));
rettv->v_type = VAR_STRING;
rettv->vval.v_string = str;
@@ -5066,7 +5039,7 @@ static int get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate)
}
++p;
}
- mb_copy_char((const char_u **)&p, &str);
+ mb_copy_char((const char_u **)&p, (char_u **)&str);
}
*str = NUL;
*arg = p + 1;
@@ -5074,13 +5047,14 @@ static int get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate)
return OK;
}
-/// @return the function name of the partial.
-char_u *partial_name(partial_T *pt)
+/// @return the function name of the partial.
+char *partial_name(partial_T *pt)
+ FUNC_ATTR_PURE
{
if (pt->pt_name != NULL) {
- return pt->pt_name;
+ return (char *)pt->pt_name;
}
- return pt->pt_func->uf_name;
+ return (char *)pt->pt_func->uf_name;
}
// TODO(ZyX-I): Move to eval/typval.h
@@ -5113,8 +5087,9 @@ void partial_unref(partial_T *pt)
}
/// Allocate a variable for a List and fill it from "*arg".
-/// Return OK or FAIL.
-static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate)
+///
+/// @return OK or FAIL.
+static int get_list_tv(char **arg, typval_T *rettv, int evaluate)
{
list_T *l = NULL;
@@ -5168,13 +5143,11 @@ bool func_equal(typval_T *tv1, typval_T *tv2, bool ic)
int a1, a2;
// empty and NULL function name considered the same
- s1 = tv1->v_type == VAR_FUNC ? tv1->vval.v_string
- : partial_name(tv1->vval.v_partial);
+ s1 = (char_u *)(tv1->v_type == VAR_FUNC ? tv1->vval.v_string : partial_name(tv1->vval.v_partial));
if (s1 != NULL && *s1 == NUL) {
s1 = NULL;
}
- s2 = tv2->v_type == VAR_FUNC ? tv2->vval.v_string
- : partial_name(tv2->vval.v_partial);
+ s2 = (char_u *)(tv2->v_type == VAR_FUNC ? tv2->vval.v_string : partial_name(tv2->vval.v_partial));
if (s2 != NULL && *s2 == NUL) {
s2 = NULL;
}
@@ -5251,7 +5224,8 @@ int get_copyID(void)
/// Do garbage collection for lists and dicts.
///
/// @param testing true if called from test_garbagecollect_now().
-/// @returns true if some memory was freed.
+///
+/// @return true if some memory was freed.
bool garbage_collect(bool testing)
{
bool abort = false;
@@ -5429,10 +5403,11 @@ bool garbage_collect(bool testing)
/// Free lists and dictionaries that are no longer referenced.
///
-/// @note This function may only be called from garbage_collect().
+/// @note This function may only be called from garbage_collect().
///
-/// @param copyID Free lists/dictionaries that don't have this ID.
-/// @return true, if something was freed.
+/// @param copyID Free lists/dictionaries that don't have this ID.
+///
+/// @return true, if something was freed.
static int free_unref_items(int copyID)
{
bool did_free = false;
@@ -5650,7 +5625,7 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, list_stack
break;
}
case VAR_FUNC:
- abort = set_ref_in_func(tv->vval.v_string, NULL, copyID);
+ abort = set_ref_in_func((char_u *)tv->vval.v_string, NULL, copyID);
break;
case VAR_UNKNOWN:
case VAR_BOOL:
@@ -5664,10 +5639,9 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, list_stack
return abort;
}
-
/// Mark all lists and dicts referenced in given mark
///
-/// @returns true if setting references failed somehow.
+/// @return true if setting references failed somehow.
static inline bool set_ref_in_fmark(fmark_T fm, int copyID)
FUNC_ATTR_WARN_UNUSED_RESULT
{
@@ -5681,7 +5655,7 @@ static inline bool set_ref_in_fmark(fmark_T fm, int copyID)
/// Mark all lists and dicts referenced in given list and the list itself
///
-/// @returns true if setting references failed somehow.
+/// @return true if setting references failed somehow.
static inline bool set_ref_list(list_T *list, int copyID)
FUNC_ATTR_WARN_UNUSED_RESULT
{
@@ -5697,7 +5671,7 @@ static inline bool set_ref_list(list_T *list, int copyID)
/// Mark all lists and dicts referenced in given dict and the dict itself
///
-/// @returns true if setting references failed somehow.
+/// @return true if setting references failed somehow.
static inline bool set_ref_dict(dict_T *dict, int copyID)
FUNC_ATTR_WARN_UNUSED_RESULT
{
@@ -5711,37 +5685,37 @@ static inline bool set_ref_dict(dict_T *dict, int copyID)
return false;
}
-
-// Get the key for *{key: val} into "tv" and advance "arg".
-// Return FAIL when there is no valid key.
-static int get_literal_key(char_u **arg, typval_T *tv)
+/// Get the key for *{key: val} into "tv" and advance "arg".
+///
+/// @return FAIL when there is no valid key.
+static int get_literal_key(char **arg, typval_T *tv)
FUNC_ATTR_NONNULL_ALL
{
- char_u *p;
+ char *p;
if (!ASCII_ISALNUM(**arg) && **arg != '_' && **arg != '-') {
return FAIL;
}
- for (p = *arg; ASCII_ISALNUM(*p) || *p == '_' || *p == '-'; p++) {
- }
+ for (p = *arg; ASCII_ISALNUM(*p) || *p == '_' || *p == '-'; p++) {}
tv->v_type = VAR_STRING;
- tv->vval.v_string = vim_strnsave(*arg, p - *arg);
+ tv->vval.v_string = xstrnsave(*arg, (size_t)(p - *arg));
*arg = skipwhite(p);
return OK;
}
-// Allocate a variable for a Dictionary and fill it from "*arg".
-// "literal" is true for *{key: val}
-// Return OK or FAIL. Returns NOTDONE for {expr}.
-static int dict_get_tv(char_u **arg, typval_T *rettv, int evaluate, bool literal)
+/// Allocate a variable for a Dictionary and fill it from "*arg".
+/// "literal" is true for *{key: val}
+///
+/// @return OK or FAIL. Returns NOTDONE for {expr}.
+static int dict_get_tv(char **arg, typval_T *rettv, int evaluate, bool literal)
{
dict_T *d = NULL;
typval_T tvkey;
typval_T tv;
- char_u *key = NULL;
+ char *key = NULL;
dictitem_T *item;
- char_u *start = skipwhite(*arg + 1);
+ char *start = skipwhite(*arg + 1);
char buf[NUMBUFLEN];
/*
@@ -5779,7 +5753,7 @@ static int dict_get_tv(char_u **arg, typval_T *rettv, int evaluate, bool literal
goto failret;
}
if (evaluate) {
- key = (char_u *)tv_get_string_buf_chk(&tvkey, buf);
+ key = (char *)tv_get_string_buf_chk(&tvkey, buf);
if (key == NULL) {
// "key" is NULL when tv_get_string_buf_chk() gave an errmsg
tv_clear(&tvkey);
@@ -5843,10 +5817,10 @@ failret:
/// This uses strtod(). setlocale(LC_NUMERIC, "C") has been used earlier to
/// make sure this always uses a decimal point.
///
-/// @param[in] text String to convert.
-/// @param[out] ret_value Location where conversion result is saved.
+/// @param[in] text String to convert.
+/// @param[out] ret_value Location where conversion result is saved.
///
-/// @return Length of the text that was consumed.
+/// @return Length of the text that was consumed.
size_t string2float(const char *const text, float_T *const ret_value)
FUNC_ATTR_NONNULL_ALL
{
@@ -5854,15 +5828,15 @@ size_t string2float(const char *const text, float_T *const ret_value)
// MS-Windows does not deal with "inf" and "nan" properly
if (STRNICMP(text, "inf", 3) == 0) {
- *ret_value = INFINITY;
+ *ret_value = (float_T)INFINITY;
return 3;
}
if (STRNICMP(text, "-inf", 3) == 0) {
- *ret_value = -INFINITY;
+ *ret_value = (float_T) - INFINITY;
return 4;
}
if (STRNICMP(text, "nan", 3) == 0) {
- *ret_value = NAN;
+ *ret_value = (float_T)NAN;
return 3;
}
*ret_value = strtod(text, &s);
@@ -5873,28 +5847,28 @@ size_t string2float(const char *const text, float_T *const ret_value)
///
/// If the environment variable was not set, silently assume it is empty.
///
-/// @param arg Points to the '$'. It is advanced to after the name.
-/// @return FAIL if the name is invalid.
+/// @param arg Points to the '$'. It is advanced to after the name.
///
-static int get_env_tv(char_u **arg, typval_T *rettv, int evaluate)
+/// @return FAIL if the name is invalid.
+static int get_env_tv(char **arg, typval_T *rettv, int evaluate)
{
- char_u *name;
- char_u *string = NULL;
+ char *name;
+ char *string = NULL;
int len;
int cc;
++*arg;
name = *arg;
- len = get_env_len((const char_u **)arg);
+ len = get_env_len((const char **)arg);
if (evaluate) {
if (len == 0) {
return FAIL; // Invalid empty name.
}
- cc = name[len];
+ cc = (char_u)name[len];
name[len] = NUL;
// First try vim_getenv(), fast for normal environment vars.
- string = (char_u *)vim_getenv((char *)name);
+ string = vim_getenv(name);
if (string == NULL || *string == NUL) {
xfree(string);
@@ -5904,7 +5878,7 @@ static int get_env_tv(char_u **arg, typval_T *rettv, int evaluate)
XFREE_CLEAR(string);
}
}
- name[len] = cc;
+ name[len] = (char)cc;
rettv->v_type = VAR_STRING;
rettv->vval.v_string = string;
}
@@ -5924,145 +5898,7 @@ void get_arglist_as_rettv(aentry_T *arglist, int argcount, typval_T *rettv)
}
}
-// Prepare "gap" for an assert error and add the sourcing position.
-void prepare_assert_error(garray_T *gap)
-{
- char buf[NUMBUFLEN];
-
- ga_init(gap, 1, 100);
- if (sourcing_name != NULL) {
- ga_concat(gap, (char *)sourcing_name);
- if (sourcing_lnum > 0) {
- ga_concat(gap, " ");
- }
- }
- if (sourcing_lnum > 0) {
- vim_snprintf(buf, ARRAY_SIZE(buf), "line %" PRId64, (int64_t)sourcing_lnum);
- ga_concat(gap, buf);
- }
- if (sourcing_name != NULL || sourcing_lnum > 0) {
- ga_concat(gap, ": ");
- }
-}
-
-// Append "p[clen]" to "gap", escaping unprintable characters.
-// Changes NL to \n, CR to \r, etc.
-static void ga_concat_esc(garray_T *gap, const char_u *p, int clen)
- FUNC_ATTR_NONNULL_ALL
-{
- char_u buf[NUMBUFLEN];
-
- if (clen > 1) {
- memmove(buf, p, clen);
- buf[clen] = NUL;
- ga_concat(gap, (char *)buf);
- } else {
- switch (*p) {
- case BS:
- ga_concat(gap, "\\b"); break;
- case ESC:
- ga_concat(gap, "\\e"); break;
- case FF:
- ga_concat(gap, "\\f"); break;
- case NL:
- ga_concat(gap, "\\n"); break;
- case TAB:
- ga_concat(gap, "\\t"); break;
- case CAR:
- ga_concat(gap, "\\r"); break;
- case '\\':
- ga_concat(gap, "\\\\"); break;
- default:
- if (*p < ' ') {
- vim_snprintf((char *)buf, NUMBUFLEN, "\\x%02x", *p);
- ga_concat(gap, (char *)buf);
- } else {
- ga_append(gap, *p);
- }
- break;
- }
- }
-}
-
-// Append "str" to "gap", escaping unprintable characters.
-// Changes NL to \n, CR to \r, etc.
-static void ga_concat_shorten_esc(garray_T *gap, const char_u *str)
- FUNC_ATTR_NONNULL_ARG(1)
-{
- char_u buf[NUMBUFLEN];
-
- if (str == NULL) {
- ga_concat(gap, "NULL");
- return;
- }
-
- for (const char_u *p = str; *p != NUL; p++) {
- int same_len = 1;
- const char_u *s = p;
- const int c = mb_ptr2char_adv(&s);
- const int clen = s - p;
- while (*s != NUL && c == utf_ptr2char(s)) {
- same_len++;
- s += clen;
- }
- if (same_len > 20) {
- ga_concat(gap, "\\[");
- ga_concat_esc(gap, p, clen);
- ga_concat(gap, " occurs ");
- vim_snprintf((char *)buf, NUMBUFLEN, "%d", same_len);
- ga_concat(gap, (char *)buf);
- ga_concat(gap, " times]");
- p = s - 1;
- } else {
- ga_concat_esc(gap, p, clen);
- }
- }
-}
-
-// Fill "gap" with information about an assert error.
-void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char_u *exp_str, typval_T *exp_tv,
- typval_T *got_tv, assert_type_T atype)
-{
- char_u *tofree;
-
- if (opt_msg_tv->v_type != VAR_UNKNOWN) {
- tofree = (char_u *)encode_tv2echo(opt_msg_tv, NULL);
- ga_concat(gap, (char *)tofree);
- xfree(tofree);
- ga_concat(gap, ": ");
- }
-
- if (atype == ASSERT_MATCH || atype == ASSERT_NOTMATCH) {
- ga_concat(gap, "Pattern ");
- } else if (atype == ASSERT_NOTEQUAL) {
- ga_concat(gap, "Expected not equal to ");
- } else {
- ga_concat(gap, "Expected ");
- }
-
- if (exp_str == NULL) {
- tofree = (char_u *)encode_tv2string(exp_tv, NULL);
- ga_concat_shorten_esc(gap, tofree);
- xfree(tofree);
- } else {
- ga_concat_shorten_esc(gap, exp_str);
- }
-
- if (atype != ASSERT_NOTEQUAL) {
- if (atype == ASSERT_MATCH) {
- ga_concat(gap, " does not match ");
- } else if (atype == ASSERT_NOTMATCH) {
- ga_concat(gap, " does match ");
- } else {
- ga_concat(gap, " but got ");
- }
- tofree = (char_u *)encode_tv2string(got_tv, NULL);
- ga_concat_shorten_esc(gap, tofree);
- xfree(tofree);
- }
-}
-
-// Add an assert error to v:errors.
+/// Add an assert error to v:errors.
void assert_error(garray_T *gap)
{
struct vimvar *vp = &vimvars[VV_ERRORS];
@@ -6075,328 +5911,6 @@ void assert_error(garray_T *gap)
(const char *)gap->ga_data, (ptrdiff_t)gap->ga_len);
}
-int assert_equal_common(typval_T *argvars, assert_type_T atype)
- FUNC_ATTR_NONNULL_ALL
-{
- garray_T ga;
-
- if (tv_equal(&argvars[0], &argvars[1], false, false)
- != (atype == ASSERT_EQUAL)) {
- prepare_assert_error(&ga);
- fill_assert_error(&ga, &argvars[2], NULL,
- &argvars[0], &argvars[1], atype);
- assert_error(&ga);
- ga_clear(&ga);
- return 1;
- }
- return 0;
-}
-
-int assert_equalfile(typval_T *argvars)
- FUNC_ATTR_NONNULL_ALL
-{
- char buf1[NUMBUFLEN];
- char buf2[NUMBUFLEN];
- const char *const fname1 = tv_get_string_buf_chk(&argvars[0], buf1);
- const char *const fname2 = tv_get_string_buf_chk(&argvars[1], buf2);
- garray_T ga;
-
- if (fname1 == NULL || fname2 == NULL) {
- return 0;
- }
-
- IObuff[0] = NUL;
- FILE *const fd1 = os_fopen(fname1, READBIN);
- char line1[200];
- char line2[200];
- ptrdiff_t lineidx = 0;
- if (fd1 == NULL) {
- snprintf((char *)IObuff, IOSIZE, (char *)e_notread, fname1);
- } else {
- FILE *const fd2 = os_fopen(fname2, READBIN);
- if (fd2 == NULL) {
- fclose(fd1);
- snprintf((char *)IObuff, IOSIZE, (char *)e_notread, fname2);
- } else {
- int64_t linecount = 1;
- for (int64_t count = 0;; count++) {
- const int c1 = fgetc(fd1);
- const int c2 = fgetc(fd2);
- if (c1 == EOF) {
- if (c2 != EOF) {
- STRCPY(IObuff, "first file is shorter");
- }
- break;
- } else if (c2 == EOF) {
- STRCPY(IObuff, "second file is shorter");
- break;
- } else {
- line1[lineidx] = c1;
- line2[lineidx] = c2;
- lineidx++;
- if (c1 != c2) {
- snprintf((char *)IObuff, IOSIZE,
- "difference at byte %" PRId64 ", line %" PRId64,
- count, linecount);
- break;
- }
- }
- if (c1 == NL) {
- linecount++;
- lineidx = 0;
- } else if (lineidx + 2 == (ptrdiff_t)sizeof(line1)) {
- memmove(line1, line1 + 100, lineidx - 100);
- memmove(line2, line2 + 100, lineidx - 100);
- lineidx -= 100;
- }
- }
- fclose(fd1);
- fclose(fd2);
- }
- }
- if (IObuff[0] != NUL) {
- prepare_assert_error(&ga);
- if (argvars[2].v_type != VAR_UNKNOWN) {
- char *const tofree = encode_tv2echo(&argvars[2], NULL);
- ga_concat(&ga, tofree);
- xfree(tofree);
- ga_concat(&ga, ": ");
- }
- ga_concat(&ga, (char *)IObuff);
- if (lineidx > 0) {
- line1[lineidx] = NUL;
- line2[lineidx] = NUL;
- ga_concat(&ga, " after \"");
- ga_concat(&ga, line1);
- if (STRCMP(line1, line2) != 0) {
- ga_concat(&ga, "\" vs \"");
- ga_concat(&ga, line2);
- }
- ga_concat(&ga, "\"");
- }
- assert_error(&ga);
- ga_clear(&ga);
- return 1;
- }
- return 0;
-}
-
-int assert_inrange(typval_T *argvars)
- FUNC_ATTR_NONNULL_ALL
-{
- bool error = false;
-
- if (argvars[0].v_type == VAR_FLOAT
- || argvars[1].v_type == VAR_FLOAT
- || argvars[2].v_type == VAR_FLOAT) {
- const float_T flower = tv_get_float(&argvars[0]);
- const float_T fupper = tv_get_float(&argvars[1]);
- const float_T factual = tv_get_float(&argvars[2]);
-
- if (factual < flower || factual > fupper) {
- garray_T ga;
- prepare_assert_error(&ga);
- if (argvars[3].v_type != VAR_UNKNOWN) {
- char_u *const tofree = (char_u *)encode_tv2string(&argvars[3], NULL);
- ga_concat(&ga, (char *)tofree);
- xfree(tofree);
- } else {
- char msg[80];
- vim_snprintf(msg, sizeof(msg), "Expected range %g - %g, but got %g",
- flower, fupper, factual);
- ga_concat(&ga, msg);
- }
- assert_error(&ga);
- ga_clear(&ga);
- return 1;
- }
- } else {
- const varnumber_T lower = tv_get_number_chk(&argvars[0], &error);
- const varnumber_T upper = tv_get_number_chk(&argvars[1], &error);
- const varnumber_T actual = tv_get_number_chk(&argvars[2], &error);
-
- if (error) {
- return 0;
- }
- if (actual < lower || actual > upper) {
- garray_T ga;
- prepare_assert_error(&ga);
-
- char msg[55];
- vim_snprintf(msg, sizeof(msg),
- "range %" PRIdVARNUMBER " - %" PRIdVARNUMBER ",",
- lower, upper); // -V576
- fill_assert_error(&ga, &argvars[3], (char_u *)msg, NULL, &argvars[2],
- ASSERT_INRANGE);
- assert_error(&ga);
- ga_clear(&ga);
- return 1;
- }
- }
- return 0;
-}
-
-// Common for assert_true() and assert_false().
-int assert_bool(typval_T *argvars, bool is_true)
- FUNC_ATTR_NONNULL_ALL
-{
- bool error = false;
- garray_T ga;
-
- if ((argvars[0].v_type != VAR_NUMBER
- || (tv_get_number_chk(&argvars[0], &error) == 0) == is_true
- || error)
- && (argvars[0].v_type != VAR_BOOL
- || (argvars[0].vval.v_bool
- != (BoolVarValue)(is_true
- ? kBoolVarTrue
- : kBoolVarFalse)))) {
- prepare_assert_error(&ga);
- fill_assert_error(&ga, &argvars[1],
- (char_u *)(is_true ? "True" : "False"),
- NULL, &argvars[0], ASSERT_OTHER);
- assert_error(&ga);
- ga_clear(&ga);
- return 1;
- }
- return 0;
-}
-
-int assert_exception(typval_T *argvars)
- FUNC_ATTR_NONNULL_ALL
-{
- garray_T ga;
-
- const char *const error = tv_get_string_chk(&argvars[0]);
- if (vimvars[VV_EXCEPTION].vv_str == NULL) {
- prepare_assert_error(&ga);
- ga_concat(&ga, "v:exception is not set");
- assert_error(&ga);
- ga_clear(&ga);
- return 1;
- } else if (error != NULL
- && strstr((char *)vimvars[VV_EXCEPTION].vv_str, error) == NULL) {
- prepare_assert_error(&ga);
- fill_assert_error(&ga, &argvars[1], NULL, &argvars[0],
- &vimvars[VV_EXCEPTION].vv_tv, ASSERT_OTHER);
- assert_error(&ga);
- ga_clear(&ga);
- return 1;
- }
- return 0;
-}
-
-static void assert_append_cmd_or_arg(garray_T *gap, typval_T *argvars, const char *cmd)
- FUNC_ATTR_NONNULL_ALL
-{
- if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) {
- char *const tofree = encode_tv2echo(&argvars[2], NULL);
- ga_concat(gap, tofree);
- xfree(tofree);
- } else {
- ga_concat(gap, cmd);
- }
-}
-
-int assert_beeps(typval_T *argvars, bool no_beep)
- FUNC_ATTR_NONNULL_ALL
-{
- const char *const cmd = tv_get_string_chk(&argvars[0]);
- int ret = 0;
-
- called_vim_beep = false;
- suppress_errthrow = true;
- emsg_silent = false;
- do_cmdline_cmd(cmd);
- if (no_beep ? called_vim_beep : !called_vim_beep) {
- garray_T ga;
- prepare_assert_error(&ga);
- if (no_beep) {
- ga_concat(&ga, "command did beep: ");
- } else {
- ga_concat(&ga, "command did not beep: ");
- }
- ga_concat(&ga, cmd);
- assert_error(&ga);
- ga_clear(&ga);
- ret = 1;
- }
-
- suppress_errthrow = false;
- emsg_on_display = false;
- return ret;
-}
-
-int assert_fails(typval_T *argvars)
- FUNC_ATTR_NONNULL_ALL
-{
- const char *const cmd = tv_get_string_chk(&argvars[0]);
- garray_T ga;
- int ret = 0;
- int save_trylevel = trylevel;
-
- // trylevel must be zero for a ":throw" command to be considered failed
- trylevel = 0;
- called_emsg = false;
- suppress_errthrow = true;
- emsg_silent = true;
-
- do_cmdline_cmd(cmd);
- if (!called_emsg) {
- prepare_assert_error(&ga);
- ga_concat(&ga, "command did not fail: ");
- assert_append_cmd_or_arg(&ga, argvars, cmd);
- assert_error(&ga);
- ga_clear(&ga);
- ret = 1;
- } else if (argvars[1].v_type != VAR_UNKNOWN) {
- char buf[NUMBUFLEN];
- const char *const error = tv_get_string_buf_chk(&argvars[1], buf);
-
- if (error == NULL
- || strstr((char *)vimvars[VV_ERRMSG].vv_str, error) == NULL) {
- prepare_assert_error(&ga);
- fill_assert_error(&ga, &argvars[2], NULL, &argvars[1],
- &vimvars[VV_ERRMSG].vv_tv, ASSERT_OTHER);
- ga_concat(&ga, ": ");
- assert_append_cmd_or_arg(&ga, argvars, cmd);
- assert_error(&ga);
- ga_clear(&ga);
- ret = 1;
- }
- }
-
- trylevel = save_trylevel;
- called_emsg = false;
- suppress_errthrow = false;
- emsg_silent = false;
- emsg_on_display = false;
- set_vim_var_string(VV_ERRMSG, NULL, 0);
- return ret;
-}
-
-int assert_match_common(typval_T *argvars, assert_type_T atype)
- FUNC_ATTR_NONNULL_ALL
-{
- char buf1[NUMBUFLEN];
- char buf2[NUMBUFLEN];
- const char *const pat = tv_get_string_buf_chk(&argvars[0], buf1);
- const char *const text = tv_get_string_buf_chk(&argvars[1], buf2);
-
- if (pat == NULL || text == NULL) {
- emsg(_(e_invarg));
- } else if (pattern_match((char_u *)pat, (char_u *)text, false)
- != (atype == ASSERT_MATCH)) {
- garray_T ga;
- prepare_assert_error(&ga);
- fill_assert_error(&ga, &argvars[2], NULL, &argvars[0], &argvars[1], atype);
- assert_error(&ga);
- ga_clear(&ga);
- return 1;
- }
- return 0;
-}
-
/// Find a window: When using a Window ID in any tab page, when using a number
/// in the current tab page.
win_T *find_win_by_nr_or_id(typval_T *vp)
@@ -6404,15 +5918,13 @@ win_T *find_win_by_nr_or_id(typval_T *vp)
int nr = (int)tv_get_number_chk(vp, NULL);
if (nr >= LOWEST_WIN_ID) {
- return win_id2wp(vp);
+ return win_id2wp((int)tv_get_number(vp));
}
return find_win_by_nr(vp, NULL);
}
-/*
- * Implementation of map() and filter().
- */
+/// Implementation of map() and filter().
void filter_map(typval_T *argvars, typval_T *rettv, int map)
{
typval_T *expr;
@@ -6426,7 +5938,7 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map)
blob_T *b = NULL;
int rem = false;
int todo;
- char_u *ermsg = (char_u *)(map ? "map()" : "filter()");
+ char *ermsg = map ? "map()" : "filter()";
const char *const arg_errmsg = (map
? N_("map() argument")
: N_("filter() argument"));
@@ -6472,6 +5984,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;
@@ -6486,7 +6002,7 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map)
break;
}
- vimvars[VV_KEY].vv_str = vim_strsave(di->di_key);
+ vimvars[VV_KEY].vv_str = (char *)vim_strsave(di->di_key);
int r = filter_map_one(&di->di_tv, expr, map, &rem);
tv_clear(&vimvars[VV_KEY].vv_tv);
if (r == FAIL || did_emsg) {
@@ -6502,6 +6018,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;
@@ -6520,11 +6037,11 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map)
}
if (map) {
if (tv.vval.v_number != val) {
- tv_blob_set(b, i, tv.vval.v_number);
+ tv_blob_set(b, i, (char_u)tv.vval.v_number);
}
} else if (rem) {
- char_u *const p = (char_u *)argvars[0].vval.v_blob->bv_ga.ga_data;
- memmove(p + i, p + i + 1, (size_t)b->bv_ga.ga_len - i - 1);
+ char *const p = argvars[0].vval.v_blob->bv_ga.ga_data;
+ memmove(p + i, p + i + 1, (size_t)(b->bv_ga.ga_len - i - 1));
b->bv_ga.ga_len--;
i--;
}
@@ -6534,6 +6051,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,
@@ -6552,6 +6073,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);
@@ -6599,11 +6121,11 @@ theend:
void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref, FunPtr fptr)
{
- char_u *s;
- char_u *name;
+ char *s;
+ char *name;
bool use_string = false;
partial_T *arg_pt = NULL;
- char_u *trans_name = NULL;
+ char *trans_name = NULL;
if (argvars[0].v_type == VAR_FUNC) {
// function(MyFunc, [arg], dict)
@@ -6616,15 +6138,15 @@ void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref, FunPtr
// TODO(bfredl): do the entire nlua_is_table_from_lua dance
} else {
// function('MyFunc', [arg], dict)
- s = (char_u *)tv_get_string(&argvars[0]);
+ s = (char *)tv_get_string(&argvars[0]);
use_string = true;
}
if ((use_string && vim_strchr(s, AUTOLOAD_CHAR) == NULL) || is_funcref) {
name = s;
- trans_name = trans_function_name(&name, false,
- TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD
- | TFN_NO_DEREF, NULL, NULL);
+ trans_name = (char *)trans_function_name((char_u **)&name, false,
+ TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD
+ | TFN_NO_DEREF, NULL, NULL);
if (*name != NUL) {
s = NULL;
}
@@ -6637,7 +6159,7 @@ void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref, FunPtr
// Don't check an autoload name for existence here.
} else if (trans_name != NULL
&& (is_funcref
- ? find_func(trans_name) == NULL
+ ? find_func((char_u *)trans_name) == NULL
: !translated_function_exists((const char *)trans_name))) {
semsg(_("E700: Unknown function: %s"), s);
} else {
@@ -6658,7 +6180,7 @@ void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref, FunPtr
STRCPY(name, sid_buf);
STRCAT(name, s + off);
} else {
- name = vim_strsave(s);
+ name = xstrdup(s);
}
if (argvars[1].v_type != VAR_UNKNOWN) {
@@ -6694,7 +6216,7 @@ void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref, FunPtr
if (tv_list_len(list) == 0) {
arg_idx = 0;
} else if (tv_list_len(list) > MAX_FUNC_ARGS) {
- emsg_funcname((char *)e_toomanyarg, s);
+ emsg_funcname((char *)e_toomanyarg, (char_u *)s);
xfree(name);
goto theend;
}
@@ -6709,7 +6231,7 @@ void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref, FunPtr
const int lv_len = tv_list_len(list);
pt->pt_argc = arg_len + lv_len;
- pt->pt_argv = xmalloc(sizeof(pt->pt_argv[0]) * pt->pt_argc);
+ pt->pt_argv = xmalloc(sizeof(pt->pt_argv[0]) * (size_t)pt->pt_argc);
int i = 0;
for (; i < arg_len; i++) {
tv_copy(&arg_pt->pt_argv[i], &pt->pt_argv[i]);
@@ -6743,12 +6265,12 @@ void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref, FunPtr
func_ptr_ref(pt->pt_func);
xfree(name);
} else if (is_funcref) {
- pt->pt_func = find_func(trans_name);
+ pt->pt_func = find_func((char_u *)trans_name);
func_ptr_ref(pt->pt_func);
xfree(name);
} else {
- pt->pt_name = name;
- func_ref(name);
+ pt->pt_name = (char_u *)name;
+ func_ref((char_u *)name);
}
rettv->v_type = VAR_PARTIAL;
@@ -6757,14 +6279,14 @@ void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref, FunPtr
// result is a VAR_FUNC
rettv->v_type = VAR_FUNC;
rettv->vval.v_string = name;
- func_ref(name);
+ func_ref((char_u *)name);
}
}
theend:
xfree(trans_name);
}
-/// Returns buffer options, variables and other attributes in a dictionary.
+/// @return buffer options, variables and other attributes in a dictionary.
dict_T *get_buffer_info(buf_T *buf)
{
dict_T *const dict = tv_dict_alloc();
@@ -6808,12 +6330,12 @@ dict_T *get_buffer_info(buf_T *buf)
///
/// @note Unlike tv_get_lnum(), this one supports only "$" special string.
///
-/// @param[in] tv Object to get value from. Is expected to be a number or
+/// @param[in] tv Object to get value from. Is expected to be a number or
/// a special string "$".
-/// @param[in] buf Buffer to take last line number from in case tv is "$". May
-/// be NULL, in this case "$" results in zero return.
+/// @param[in] buf Buffer to take last line number from in case tv is "$". May
+/// be NULL, in this case "$" results in zero return.
///
-/// @return Line number or 0 in case of error.
+/// @return Line number or 0 in case of error.
linenr_T tv_get_lnum_buf(const typval_T *const tv, const buf_T *const buf)
FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT
{
@@ -6823,7 +6345,7 @@ linenr_T tv_get_lnum_buf(const typval_T *const tv, const buf_T *const buf)
&& buf != NULL) {
return buf->b_ml.ml_line_count;
}
- return tv_get_number_chk(tv, NULL);
+ return (linenr_T)tv_get_number_chk(tv, NULL);
}
void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg, typval_T *rettv)
@@ -6849,8 +6371,8 @@ void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg, typval_T *rettv)
}
}
-/// Returns information (variables, options, etc.) about a tab page
-/// as a dictionary.
+/// @return information (variables, options, etc.) about a tab page
+/// as a dictionary.
dict_T *get_tabpage_info(tabpage_T *tp, int tp_idx)
{
dict_T *const dict = tv_dict_alloc();
@@ -6869,11 +6391,14 @@ dict_T *get_tabpage_info(tabpage_T *tp, int tp_idx)
return dict;
}
-/// Returns information about a window as a dictionary.
+/// @return information about a window as a dictionary.
dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr)
{
dict_T *const dict = tv_dict_alloc();
+ // make sure w_botline is valid
+ validate_botline(wp);
+
tv_dict_add_nr(dict, S_LEN("tabnr"), tpnr);
tv_dict_add_nr(dict, S_LEN("winnr"), winnr);
tv_dict_add_nr(dict, S_LEN("winid"), wp->handle);
@@ -6881,7 +6406,7 @@ dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr)
tv_dict_add_nr(dict, S_LEN("winrow"), wp->w_winrow + 1);
tv_dict_add_nr(dict, S_LEN("topline"), wp->w_topline);
tv_dict_add_nr(dict, S_LEN("botline"), wp->w_botline - 1);
- tv_dict_add_nr(dict, S_LEN("winbar"), 0);
+ tv_dict_add_nr(dict, S_LEN("winbar"), wp->w_winbar_height);
tv_dict_add_nr(dict, S_LEN("width"), wp->w_width);
tv_dict_add_nr(dict, S_LEN("bufnr"), wp->w_buffer->b_fnum);
tv_dict_add_nr(dict, S_LEN("wincol"), wp->w_wincol + 1);
@@ -6939,7 +6464,7 @@ win_T *find_tabwin(typval_T *wvp, typval_T *tvp)
if (tvp->v_type != VAR_UNKNOWN) {
long n = tv_get_number(tvp);
if (n >= 0) {
- tp = find_tabpage(n);
+ tp = find_tabpage((int)n);
}
} else {
tp = curtab;
@@ -6960,10 +6485,9 @@ win_T *find_tabwin(typval_T *wvp, typval_T *tvp)
/// @param off 1 for gettabwinvar()
void getwinvar(typval_T *argvars, typval_T *rettv, int off)
{
- win_T *win, *oldcurwin;
+ win_T *win;
dictitem_T *v;
tabpage_T *tp = NULL;
- tabpage_T *oldtabpage = NULL;
bool done = false;
if (off == 1) {
@@ -6983,8 +6507,8 @@ void getwinvar(typval_T *argvars, typval_T *rettv, int off)
// otherwise the window is not valid. Only do this when needed,
// autocommands get blocked.
bool need_switch_win = tp != curtab || win != curwin;
- if (!need_switch_win
- || switch_win(&oldcurwin, &oldtabpage, win, tp, true) == OK) {
+ switchwin_T switchwin;
+ if (!need_switch_win || switch_win(&switchwin, win, tp, true) == OK) {
if (*varname == '&') {
if (varname[1] == NUL) {
// get all window-local options in a dict
@@ -7012,7 +6536,7 @@ void getwinvar(typval_T *argvars, typval_T *rettv, int off)
if (need_switch_win) {
// restore previous notion of curwin
- restore_win(oldcurwin, oldtabpage, true);
+ restore_win(&switchwin, true);
}
}
emsg_off--;
@@ -7023,12 +6547,10 @@ void getwinvar(typval_T *argvars, typval_T *rettv, int off)
}
}
-/*
- * This function is used by f_input() and f_inputdialog() functions. The third
- * argument to f_input() specifies the type of completion to use at the
- * prompt. The third argument to f_inputdialog() specifies the value to return
- * when the user cancels the prompt.
- */
+/// This function is used by f_input() and f_inputdialog() functions. The third
+/// argument to f_input() specifies the type of completion to use at the
+/// prompt. The third argument to f_inputdialog() specifies the value to return
+/// when the user cancels the prompt.
void get_user_input(const typval_T *const argvars, typval_T *const rettv, const bool inputdialog,
const bool secret)
FUNC_ATTR_NONNULL_ALL
@@ -7038,7 +6560,8 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const
const char *prompt = "";
const char *defstr = "";
- const char *cancelreturn = NULL;
+ typval_T *cancelreturn = NULL;
+ typval_T cancelreturn_strarg2 = TV_INITIAL_VALUE;
const char *xp_name = NULL;
Callback input_callback = { .type = kCallbackNone };
char prompt_buf[NUMBUFLEN];
@@ -7060,13 +6583,9 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const
if (defstr == NULL) {
return;
}
- cancelreturn = tv_dict_get_string_buf_chk(dict, S_LEN("cancelreturn"),
- cancelreturn_buf, def);
- if (cancelreturn == NULL) { // error
- return;
- }
- if (*cancelreturn == NUL) {
- cancelreturn = NULL;
+ dictitem_T *cancelreturn_di = tv_dict_find(dict, S_LEN("cancelreturn"));
+ if (cancelreturn_di != NULL) {
+ cancelreturn = &cancelreturn_di->di_tv;
}
xp_name = tv_dict_get_string_buf_chk(dict, S_LEN("completion"),
xp_name_buf, def);
@@ -7090,15 +6609,16 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const
return;
}
if (argvars[2].v_type != VAR_UNKNOWN) {
- const char *const arg2 = tv_get_string_buf_chk(&argvars[2],
- cancelreturn_buf);
- if (arg2 == NULL) {
+ const char *const strarg2 = tv_get_string_buf_chk(&argvars[2], cancelreturn_buf);
+ if (strarg2 == NULL) {
return;
}
if (inputdialog) {
- cancelreturn = arg2;
+ cancelreturn_strarg2.v_type = VAR_STRING;
+ cancelreturn_strarg2.vval.v_string = (char *)strarg2;
+ cancelreturn = &cancelreturn_strarg2;
} else {
- xp_name = arg2;
+ xp_name = strarg2;
}
}
}
@@ -7110,9 +6630,9 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const
// input() with a third argument: completion
const int xp_namelen = (int)strlen(xp_name);
- uint32_t argt;
- if (parse_compl_arg((char_u *)xp_name, xp_namelen, &xp_type,
- &argt, (char_u **)&xp_arg) == FAIL) {
+ uint32_t argt = 0;
+ if (parse_compl_arg(xp_name, xp_namelen, &xp_type,
+ &argt, &xp_arg) == FAIL) {
return;
}
}
@@ -7126,7 +6646,7 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const
if (!ui_has(kUICmdline)) {
const char *lastnl = strrchr(prompt, '\n');
if (lastnl != NULL) {
- p = lastnl+1;
+ p = lastnl + 1;
msg_start();
msg_clr_eos();
msg_puts_attr_len(prompt, p - prompt, echo_attr);
@@ -7140,14 +6660,13 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const
const int save_ex_normal_busy = ex_normal_busy;
ex_normal_busy = 0;
- rettv->vval.v_string =
- (char_u *)getcmdline_prompt(secret ? NUL : '@', p, echo_attr,
- xp_type, xp_arg, input_callback);
+ rettv->vval.v_string = getcmdline_prompt(secret ? NUL : '@', p, echo_attr, xp_type, xp_arg,
+ input_callback);
ex_normal_busy = save_ex_normal_busy;
callback_free(&input_callback);
if (rettv->vval.v_string == NULL && cancelreturn != NULL) {
- rettv->vval.v_string = (char_u *)xstrdup(cancelreturn);
+ tv_copy(cancelreturn, rettv);
}
xfree(xp_arg);
@@ -7160,10 +6679,10 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const
/// Turn a dictionary into a list
///
-/// @param[in] tv Dictionary to convert. Is checked for actually being
-/// a dictionary, will give an error if not.
-/// @param[out] rettv Location where result will be saved.
-/// @param[in] what What to save in rettv.
+/// @param[in] tv Dictionary to convert. Is checked for actually being
+/// a dictionary, will give an error if not.
+/// @param[out] rettv Location where result will be saved.
+/// @param[in] what What to save in rettv.
void dict_list(typval_T *const tv, typval_T *const rettv, const DictListType what)
{
if (tv->v_type != VAR_DICT) {
@@ -7182,7 +6701,7 @@ void dict_list(typval_T *const tv, typval_T *const rettv, const DictListType wha
switch (what) {
case kDictListKeys:
tv_item.v_type = VAR_STRING;
- tv_item.vval.v_string = vim_strsave(di->di_key);
+ tv_item.vval.v_string = (char *)vim_strsave(di->di_key);
break;
case kDictListValues:
tv_copy(&di->di_tv, &tv_item);
@@ -7197,7 +6716,7 @@ void dict_list(typval_T *const tv, typval_T *const rettv, const DictListType wha
tv_list_append_owned_tv(sub_l, (typval_T) {
.v_type = VAR_STRING,
.v_lock = VAR_UNLOCKED,
- .vval.v_string = (char_u *)xstrdup((const char *)di->di_key),
+ .vval.v_string = xstrdup((const char *)di->di_key),
});
tv_list_append_tv(sub_l, &di->di_tv);
@@ -7216,7 +6735,7 @@ void dict_list(typval_T *const tv, typval_T *const rettv, const DictListType wha
/// @param[out] cmd Returns the command or executable name.
/// @param[out] executable Returns `false` if argv[0] is not executable.
///
-/// @returns Result of `shell_build_argv()` if `cmd_tv` is a String.
+/// @return Result of `shell_build_argv()` if `cmd_tv` is a String.
/// Else, string values of `cmd_tv` copied to a (char **) list with
/// argv[0] resolved to full path ($PATHEXT-resolved on Windows).
char **tv_to_argv(typval_T *cmd_tv, const char **cmd, bool *executable)
@@ -7259,7 +6778,7 @@ char **tv_to_argv(typval_T *cmd_tv, const char **cmd, bool *executable)
// Build the argument vector
int i = 0;
- char **argv = xcalloc(argc + 1, sizeof(char *));
+ char **argv = xcalloc((size_t)argc + 1, sizeof(char *));
TV_LIST_ITER_CONST(argl, arg, {
const char *a = tv_get_string_chk(TV_LIST_ITEM_TV(arg));
if (!a) {
@@ -7278,102 +6797,27 @@ char **tv_to_argv(typval_T *cmd_tv, const char **cmd, bool *executable)
return argv;
}
-/// Fill a dictionary with all applicable maparg() like dictionaries
-///
-/// @param dict The dictionary to be filled
-/// @param mp The maphash that contains the mapping information
-/// @param buffer_value The "buffer" value
-/// @param compatible True for compatible with old maparg() dict
-void mapblock_fill_dict(dict_T *const dict, const mapblock_T *const mp, long buffer_value,
- bool compatible)
- FUNC_ATTR_NONNULL_ALL
-{
- char *const lhs = str2special_save((const char *)mp->m_keys,
- compatible, !compatible);
- char *const mapmode = map_mode_to_chars(mp->m_mode);
- varnumber_T noremap_value;
-
- if (compatible) {
- // Keep old compatible behavior
- // This is unable to determine whether a mapping is a <script> mapping
- noremap_value = !!mp->m_noremap;
- } else {
- // Distinguish between <script> mapping
- // If it's not a <script> mapping, check if it's a noremap
- noremap_value = mp->m_noremap == REMAP_SCRIPT ? 2 : !!mp->m_noremap;
- }
-
- if (mp->m_luaref != LUA_NOREF) {
- tv_dict_add_nr(dict, S_LEN("callback"), mp->m_luaref);
- } else {
- if (compatible) {
- tv_dict_add_str(dict, S_LEN("rhs"), (const char *)mp->m_orig_str);
- } else {
- tv_dict_add_allocated_str(dict, S_LEN("rhs"),
- str2special_save((const char *)mp->m_str, false,
- true));
- }
- }
- if (mp->m_desc != NULL) {
- tv_dict_add_allocated_str(dict, S_LEN("desc"), xstrdup(mp->m_desc));
- }
- tv_dict_add_allocated_str(dict, S_LEN("lhs"), lhs);
- tv_dict_add_nr(dict, S_LEN("noremap"), noremap_value);
- tv_dict_add_nr(dict, S_LEN("script"), mp->m_noremap == REMAP_SCRIPT ? 1 : 0);
- tv_dict_add_nr(dict, S_LEN("expr"), mp->m_expr ? 1 : 0);
- tv_dict_add_nr(dict, S_LEN("silent"), mp->m_silent ? 1 : 0);
- tv_dict_add_nr(dict, S_LEN("sid"), (varnumber_T)mp->m_script_ctx.sc_sid);
- tv_dict_add_nr(dict, S_LEN("lnum"), (varnumber_T)mp->m_script_ctx.sc_lnum);
- tv_dict_add_nr(dict, S_LEN("buffer"), (varnumber_T)buffer_value);
- tv_dict_add_nr(dict, S_LEN("nowait"), mp->m_nowait ? 1 : 0);
- tv_dict_add_allocated_str(dict, S_LEN("mode"), mapmode);
-}
-
-int matchadd_dict_arg(typval_T *tv, const char **conceal_char, win_T **win)
-{
- dictitem_T *di;
-
- if (tv->v_type != VAR_DICT) {
- emsg(_(e_dictreq));
- return FAIL;
- }
-
- if ((di = tv_dict_find(tv->vval.v_dict, S_LEN("conceal"))) != NULL) {
- *conceal_char = tv_get_string(&di->di_tv);
- }
-
- if ((di = tv_dict_find(tv->vval.v_dict, S_LEN("window"))) != NULL) {
- *win = find_win_by_nr_or_id(&di->di_tv);
- if (*win == NULL) {
- emsg(_(e_invalwindow));
- return FAIL;
- }
- }
-
- return OK;
-}
-
void return_register(int regname, typval_T *rettv)
{
- char_u buf[2] = { regname, 0 };
+ char buf[2] = { (char)regname, 0 };
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = vim_strsave(buf);
+ rettv->vval.v_string = xstrdup(buf);
}
-void screenchar_adjust_grid(ScreenGrid **grid, int *row, int *col)
+void screenchar_adjust(ScreenGrid **grid, int *row, int *col)
{
// TODO(bfredl): this is a hack for legacy tests which use screenchar()
// to check printed messages on the screen (but not floats etc
// as these are not legacy features). If the compositor is refactored to
// have its own buffer, this should just read from it instead.
msg_scroll_flush();
- if (msg_grid.chars && msg_grid.comp_index > 0 && *row >= msg_grid.comp_row
- && *row < (msg_grid.Rows + msg_grid.comp_row)
- && *col < msg_grid.Columns) {
- *grid = &msg_grid;
- *row -= msg_grid.comp_row;
- }
+
+ *grid = ui_comp_get_grid_at_coord(*row, *col);
+
+ // Make `row` and `col` relative to the grid
+ *row -= (*grid)->comp_row;
+ *col -= (*grid)->comp_col;
}
/// Set line or list of lines in buffer "buf".
@@ -7390,6 +6834,7 @@ void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, const typval_T
buf_T *curbuf_save = NULL;
win_T *curwin_save = NULL;
const bool is_curbuf = buf == curbuf;
+ const bool save_VIsual_active = VIsual_active;
// When using the current buffer ml_mfp will be set if needed. Useful when
// setline() is used on startup. For other buffers the buffer must be
@@ -7400,6 +6845,7 @@ void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, const typval_T
}
if (!is_curbuf) {
+ VIsual_active = false;
curbuf_save = curbuf;
curwin_save = curwin;
curbuf = buf;
@@ -7449,8 +6895,8 @@ void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, const typval_T
// Existing line, replace it.
int old_len = (int)STRLEN(ml_get(lnum));
if (u_savesub(lnum) == OK
- && ml_replace(lnum, (char_u *)line, true) == OK) {
- inserted_bytes(lnum, 0, old_len, STRLEN(line));
+ && ml_replace(lnum, (char *)line, true) == OK) {
+ inserted_bytes(lnum, 0, old_len, (int)STRLEN(line));
if (is_curbuf && lnum == curwin->w_cursor.lnum) {
check_cursor_col();
}
@@ -7459,7 +6905,7 @@ void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, const typval_T
} else if (added > 0 || u_save(lnum - 1, lnum) == OK) {
// append the line.
added++;
- if (ml_append(lnum - 1, (char_u *)line, 0, false) == OK) {
+ if (ml_append(lnum - 1, (char *)line, 0, false) == OK) {
rettv->vval.v_number = 0; // OK
}
}
@@ -7480,7 +6926,7 @@ void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, const typval_T
if (wp->w_buffer == buf
&& (wp->w_buffer != curbuf || wp == curwin)
&& wp->w_cursor.lnum > append_lnum) {
- wp->w_cursor.lnum += added;
+ wp->w_cursor.lnum += (linenr_T)added;
}
}
check_cursor_col();
@@ -7490,6 +6936,7 @@ void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, const typval_T
if (!is_curbuf) {
curbuf = curbuf_save;
curwin = curwin_save;
+ VIsual_active = save_VIsual_active;
}
}
@@ -7514,11 +6961,9 @@ void setwinvar(typval_T *argvars, typval_T *rettv, int off)
typval_T *varp = &argvars[off + 2];
if (win != NULL && varname != NULL && varp != NULL) {
- win_T *save_curwin;
- tabpage_T *save_curtab;
bool need_switch_win = tp != curtab || win != curwin;
- if (!need_switch_win
- || switch_win(&save_curwin, &save_curtab, win, tp, true) == OK) {
+ switchwin_T switchwin;
+ if (!need_switch_win || switch_win(&switchwin, win, tp, true) == OK) {
if (*varname == '&') {
long numval;
bool error = false;
@@ -7540,7 +6985,7 @@ void setwinvar(typval_T *argvars, typval_T *rettv, int off)
}
}
if (need_switch_win) {
- restore_win(save_curwin, save_curtab, true);
+ restore_win(&switchwin, true);
}
}
}
@@ -7565,7 +7010,7 @@ void get_xdg_var_list(const XDGVarType xdg, typval_T *rettv)
if (dir != NULL && dir_len > 0) {
char *dir_with_nvim = xmemdupz(dir, dir_len);
dir_with_nvim = concat_fnames_realloc(dir_with_nvim, "nvim", true);
- tv_list_append_string(list, dir_with_nvim, strlen(dir_with_nvim));
+ tv_list_append_string(list, dir_with_nvim, (ssize_t)strlen(dir_with_nvim));
xfree(dir_with_nvim);
}
} while (iter != NULL);
@@ -7582,7 +7027,7 @@ static list_T *string_to_list(const char *str, size_t len, const bool keepempty)
return list;
}
-// os_system wrapper. Handles 'verbose', :profile, and v:shell_error.
+/// os_system wrapper. Handles 'verbose', :profile, and v:shell_error.
void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, bool retlist)
{
proftime_T wait_time;
@@ -7630,7 +7075,7 @@ void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, bool retlist
// execute the command
size_t nread = 0;
char *res = NULL;
- int status = os_system(argv, input, input_len, &res, &nread);
+ int status = os_system(argv, input, (size_t)input_len, &res, &nread);
if (profiling) {
prof_child_exit(&wait_time);
@@ -7645,7 +7090,7 @@ void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, bool retlist
// return an empty list when there's no output
tv_list_alloc_ret(rettv, 0);
} else {
- rettv->vval.v_string = (char_u *)xstrdup("");
+ rettv->vval.v_string = xstrdup("");
}
return;
}
@@ -7653,7 +7098,7 @@ void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, bool retlist
if (retlist) {
int keepempty = 0;
if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) {
- keepempty = tv_get_number(&argvars[2]);
+ keepempty = (int)tv_get_number(&argvars[2]);
}
rettv->vval.v_list = string_to_list(res, nread, (bool)keepempty);
tv_list_ref(rettv->vval.v_list);
@@ -7677,7 +7122,7 @@ void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, bool retlist
*d = NUL;
#endif
- rettv->vval.v_string = (char_u *)res;
+ rettv->vval.v_string = res;
}
}
@@ -7695,22 +7140,23 @@ bool callback_from_typval(Callback *const callback, typval_T *const arg)
&& ascii_isdigit(*arg->vval.v_string)) {
r = FAIL;
} else if (arg->v_type == VAR_FUNC || arg->v_type == VAR_STRING) {
- char_u *name = arg->vval.v_string;
+ char *name = arg->vval.v_string;
if (name == NULL) {
r = FAIL;
} else if (*name == NUL) {
callback->type = kCallbackNone;
callback->data.funcref = NULL;
} else {
- func_ref(name);
- callback->data.funcref = vim_strsave(name);
+ func_ref((char_u *)name);
+ callback->data.funcref = xstrdup(name);
callback->type = kCallbackFuncref;
}
} else if (nlua_is_table_from_lua(arg)) {
- char_u *name = nlua_register_table_as_callable(arg);
+ // TODO(tjdvries): UnifiedCallback
+ char *name = (char *)nlua_register_table_as_callable(arg);
if (name != NULL) {
- callback->data.funcref = vim_strsave(name);
+ callback->data.funcref = xstrdup(name);
callback->type = kCallbackFuncref;
} else {
r = FAIL;
@@ -7735,7 +7181,9 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co
FUNC_ATTR_NONNULL_ALL
{
partial_T *partial;
- char_u *name;
+ char *name;
+ Array args = ARRAY_DICT_INIT;
+ Object rv;
switch (callback->type) {
case kCallbackFuncref:
name = callback->data.funcref;
@@ -7747,6 +7195,15 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co
name = partial_name(partial);
break;
+ case kCallbackLua:
+ rv = nlua_call_ref(callback->data.luaref, NULL, args, true, NULL);
+ switch (rv.type) {
+ case kObjectTypeBoolean:
+ return rv.data.boolean;
+ default:
+ return false;
+ }
+
case kCallbackNone:
return false;
break;
@@ -7778,7 +7235,6 @@ static bool set_ref_in_callback(Callback *callback, int copyID, ht_stack_T **ht_
return set_ref_in_item(&tv, copyID, ht_stack, list_stack);
break;
-
default:
abort();
}
@@ -7803,7 +7259,7 @@ static bool set_ref_in_callback_reader(CallbackReader *reader, int copyID, ht_st
timer_T *find_timer_by_nr(varnumber_T xx)
{
- return pmap_get(uint64_t)(&timers, xx);
+ return pmap_get(uint64_t)(&timers, (uint64_t)xx);
}
void add_timer_info(typval_T *rettv, timer_T *timer)
@@ -7839,12 +7295,12 @@ void add_timer_info_all(typval_T *rettv)
})
}
-// invoked on the main loop
+/// invoked on the main loop
void timer_due_cb(TimeWatcher *tw, void *data)
{
timer_T *timer = (timer_T *)data;
int save_did_emsg = did_emsg;
- int save_called_emsg = called_emsg;
+ const int called_emsg_before = called_emsg;
const bool save_ex_pressedreturn = get_pressedreturn();
if (timer->stopped || timer->paused) {
@@ -7861,19 +7317,17 @@ void timer_due_cb(TimeWatcher *tw, void *data)
argv[0].v_type = VAR_NUMBER;
argv[0].vval.v_number = timer->timer_id;
typval_T rettv = TV_INITIAL_VALUE;
- called_emsg = false;
callback_call(&timer->callback, 1, argv, &rettv);
// Handle error message
- if (called_emsg && did_emsg) {
+ if (called_emsg > called_emsg_before && did_emsg) {
timer->emsg_count++;
if (current_exception != NULL) {
discard_current_exception();
}
}
did_emsg = save_did_emsg;
- called_emsg = save_called_emsg;
set_pressedreturn(save_ex_pressedreturn);
if (timer->emsg_count >= 3) {
@@ -7901,17 +7355,17 @@ uint64_t timer_start(const long timeout, const int repeat_count, const Callback
timer->emsg_count = 0;
timer->repeat_count = repeat_count;
timer->timeout = timeout;
- timer->timer_id = last_timer_id++;
+ timer->timer_id = (int)last_timer_id++;
timer->callback = *callback;
time_watcher_init(&main_loop, &timer->tw, timer);
timer->tw.events = multiqueue_new_child(main_loop.events);
// if main loop is blocked, don't queue up multiple events
timer->tw.blockable = true;
- time_watcher_start(&timer->tw, timer_due_cb, timeout, timeout);
+ time_watcher_start(&timer->tw, timer_due_cb, (uint64_t)timeout, (uint64_t)timeout);
- pmap_put(uint64_t)(&timers, timer->timer_id, timer);
- return timer->timer_id;
+ pmap_put(uint64_t)(&timers, (uint64_t)timer->timer_id, timer);
+ return (uint64_t)timer->timer_id;
}
void timer_stop(timer_T *timer)
@@ -7925,14 +7379,14 @@ void timer_stop(timer_T *timer)
time_watcher_close(&timer->tw, timer_close_cb);
}
-// This will be run on the main loop after the last timer_due_cb, so at this
-// point it is safe to free the callback.
+/// This will be run on the main loop after the last timer_due_cb, so at this
+/// point it is safe to free the callback.
static void timer_close_cb(TimeWatcher *tw, void *data)
{
timer_T *timer = (timer_T *)data;
multiqueue_free(timer->tw.events);
callback_free(&timer->callback);
- pmap_del(uint64_t)(&timers, timer->timer_id);
+ pmap_del(uint64_t)(&timers, (uint64_t)timer->timer_id);
timer_decref(timer);
}
@@ -8056,7 +7510,7 @@ bool read_blob(FILE *const fd, blob_T *const blob)
const int size = (int)os_fileinfo_size(&file_info);
ga_grow(&blob->bv_ga, size);
blob->bv_ga.ga_len = size;
- if (fread(blob->bv_ga.ga_data, 1, blob->bv_ga.ga_len, fd)
+ if (fread(blob->bv_ga.ga_data, 1, (size_t)blob->bv_ga.ga_len, fd)
< (size_t)blob->bv_ga.ga_len) {
return false;
}
@@ -8085,7 +7539,7 @@ char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl)
if (tv->v_type != VAR_LIST && tv->v_type != VAR_NUMBER) {
const char *ret = tv_get_string_chk(tv);
if (ret) {
- *len = strlen(ret);
+ *len = (ptrdiff_t)strlen(ret);
return xmemdupz(ret, (size_t)(*len));
} else {
*len = -1;
@@ -8094,10 +7548,10 @@ char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl)
}
if (tv->v_type == VAR_NUMBER) { // Treat number as a buffer-id.
- buf_T *buf = buflist_findnr(tv->vval.v_number);
+ buf_T *buf = buflist_findnr((int)tv->vval.v_number);
if (buf) {
for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) {
- for (char_u *p = ml_get_buf(buf, lnum, false); *p != NUL; p++) {
+ for (char *p = (char *)ml_get_buf(buf, lnum, false); *p != NUL; p++) {
*len += 1;
}
*len += 1;
@@ -8112,10 +7566,10 @@ char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl)
return NULL;
}
- char *ret = xmalloc(*len + 1);
+ char *ret = xmalloc((size_t)(*len) + 1);
char *end = ret;
for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) {
- for (char_u *p = ml_get_buf(buf, lnum, false); *p != NUL; p++) {
+ for (char *p = (char *)ml_get_buf(buf, lnum, false); *p != NUL; p++) {
*end++ = (*p == '\n') ? NUL : *p;
}
*end++ = '\n';
@@ -8129,14 +7583,14 @@ char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl)
// Pre-calculate the resulting length.
list_T *list = tv->vval.v_list;
TV_LIST_ITER_CONST(list, li, {
- *len += strlen(tv_get_string(TV_LIST_ITEM_TV(li))) + 1;
+ *len += (ptrdiff_t)strlen(tv_get_string(TV_LIST_ITEM_TV(li))) + 1;
});
if (*len == 0) {
return NULL;
}
- char *ret = xmalloc(*len + endnl);
+ char *ret = xmalloc((size_t)(*len) + endnl);
char *end = ret;
TV_LIST_ITER_CONST(list, li, {
for (const char *s = tv_get_string(TV_LIST_ITEM_TV(li)); *s != NUL; s++) {
@@ -8151,6 +7605,68 @@ char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl)
return ret;
}
+/// Convert the specified byte index of line 'lnum' in buffer 'buf' to a
+/// character index. Works only for loaded buffers. Returns -1 on failure.
+/// The index of the first byte and the first character is zero.
+int buf_byteidx_to_charidx(buf_T *buf, linenr_T lnum, int byteidx)
+{
+ if (buf == NULL || buf->b_ml.ml_mfp == NULL) {
+ return -1;
+ }
+
+ if (lnum > buf->b_ml.ml_line_count) {
+ lnum = buf->b_ml.ml_line_count;
+ }
+
+ char *str = (char *)ml_get_buf(buf, lnum, false);
+
+ if (*str == NUL) {
+ return 0;
+ }
+
+ // count the number of characters
+ char *t = str;
+ int count;
+ for (count = 0; *t != NUL && t <= str + byteidx; count++) {
+ t += utfc_ptr2len(t);
+ }
+
+ // In insert mode, when the cursor is at the end of a non-empty line,
+ // byteidx points to the NUL character immediately past the end of the
+ // string. In this case, add one to the character count.
+ if (*t == NUL && byteidx != 0 && t == str + byteidx) {
+ count++;
+ }
+
+ return count - 1;
+}
+
+/// Convert the specified character index of line 'lnum' in buffer 'buf' to a
+/// byte index. Works only for loaded buffers.
+/// The index of the first byte and the first character is zero.
+///
+/// @return -1 on failure.
+int buf_charidx_to_byteidx(buf_T *buf, linenr_T lnum, int charidx)
+{
+ if (buf == NULL || buf->b_ml.ml_mfp == NULL) {
+ return -1;
+ }
+
+ if (lnum > buf->b_ml.ml_line_count) {
+ lnum = buf->b_ml.ml_line_count;
+ }
+
+ char *str = (char *)ml_get_buf(buf, lnum, false);
+
+ // Convert the character offset to a byte offset
+ char *t = str;
+ while (*t != NUL && --charidx > 0) {
+ t += utfc_ptr2len(t);
+ }
+
+ return (int)(t - str);
+}
+
/// Translate a VimL object into a position
///
/// Accepts VAR_LIST and VAR_STRING objects. Does not give an error for invalid
@@ -8159,13 +7675,14 @@ char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl)
/// @param[in] tv Object to translate.
/// @param[in] dollar_lnum True when "$" is last line.
/// @param[out] ret_fnum Set to fnum for marks.
+/// @param[in] charcol True to return character column.
///
/// @return Pointer to position or NULL in case of error (e.g. invalid type).
-pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret_fnum)
+pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret_fnum,
+ const bool charcol)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
static pos_T pos;
- pos_T *pp;
// Argument can be [lnum, col, coladd].
if (tv->v_type == VAR_LIST) {
@@ -8180,18 +7697,22 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret
}
// Get the line number.
- pos.lnum = tv_list_find_nr(l, 0L, &error);
+ pos.lnum = (linenr_T)tv_list_find_nr(l, 0L, &error);
if (error || pos.lnum <= 0 || pos.lnum > curbuf->b_ml.ml_line_count) {
// Invalid line number.
return NULL;
}
// Get the column number.
- pos.col = tv_list_find_nr(l, 1L, &error);
+ pos.col = (colnr_T)tv_list_find_nr(l, 1L, &error);
if (error) {
return NULL;
}
- len = (long)STRLEN(ml_get(pos.lnum));
+ if (charcol) {
+ len = mb_charlen(ml_get(pos.lnum));
+ } else {
+ len = (int)STRLEN(ml_get(pos.lnum));
+ }
// We accept "$" for the column number: last column.
li = tv_list_find(l, 1L);
@@ -8209,7 +7730,7 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret
pos.col--;
// Get the virtual offset. Defaults to zero.
- pos.coladd = tv_list_find_nr(l, 2L, &error);
+ pos.coladd = (colnr_T)tv_list_find_nr(l, 2L, &error);
if (error) {
pos.coladd = 0;
}
@@ -8221,21 +7742,34 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret
if (name == NULL) {
return NULL;
}
- if (name[0] == '.') { // Cursor.
- return &curwin->w_cursor;
- }
- if (name[0] == 'v' && name[1] == NUL) { // Visual start.
+
+ pos.lnum = 0;
+ if (name[0] == '.') {
+ // cursor
+ pos = curwin->w_cursor;
+ } else if (name[0] == 'v' && name[1] == NUL) {
+ // Visual start
if (VIsual_active) {
- return &VIsual;
+ pos = VIsual;
+ } else {
+ pos = curwin->w_cursor;
}
- return &curwin->w_cursor;
- }
- if (name[0] == '\'') { // Mark.
- pp = getmark_buf_fnum(curbuf, (uint8_t)name[1], false, ret_fnum);
- if (pp == NULL || pp == (pos_T *)-1 || pp->lnum <= 0) {
+ } else if (name[0] == '\'') {
+ // mark
+ int mname = (uint8_t)name[1];
+ const fmark_T *const fm = mark_get(curbuf, curwin, NULL, kMarkAll, mname);
+ if (fm == NULL || fm->mark.lnum <= 0) {
return NULL;
}
- return pp;
+ pos = fm->mark;
+ // Vimscript behavior, only provide fnum if mark is global.
+ *ret_fnum = ASCII_ISUPPER(mname) || ascii_isdigit(mname) ? fm->fnum: *ret_fnum;
+ }
+ if (pos.lnum != 0) {
+ if (charcol) {
+ pos.col = buf_byteidx_to_charidx(curbuf, pos.lnum, pos.col);
+ }
+ return &pos;
}
pos.coladd = 0;
@@ -8260,25 +7794,28 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret
pos.col = 0;
} else {
pos.lnum = curwin->w_cursor.lnum;
- pos.col = (colnr_T)STRLEN(get_cursor_line_ptr());
+ if (charcol) {
+ pos.col = (colnr_T)mb_charlen(get_cursor_line_ptr());
+ } else {
+ pos.col = (colnr_T)STRLEN(get_cursor_line_ptr());
+ }
}
return &pos;
}
return NULL;
}
-/*
- * Convert list in "arg" into a position and optional file number.
- * When "fnump" is NULL there is no file number, only 3 items.
- * Note that the column is passed on as-is, the caller may want to decrement
- * it to use 1 for the first column.
- * Return FAIL when conversion is not possible, doesn't check the position for
- * validity.
- */
-int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp)
+/// Convert list in "arg" into a position and optional file number.
+/// When "fnump" is NULL there is no file number, only 3 items.
+/// Note that the column is passed on as-is, the caller may want to decrement
+/// it to use 1 for the first column.
+///
+/// @return FAIL when conversion is not possible, doesn't check the position for
+/// validity.
+int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp, bool charcol)
{
list_T *l;
- long i = 0;
+ int i = 0;
long n;
// List must be: [fnum, lnum, col, coladd, curswant], where "fnum" is only
@@ -8298,47 +7835,54 @@ int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp)
if (n == 0) {
n = curbuf->b_fnum; // Current buffer.
}
- *fnump = n;
+ *fnump = (int)n;
}
n = tv_list_find_nr(l, i++, NULL); // lnum
if (n < 0) {
return FAIL;
}
- posp->lnum = n;
+ posp->lnum = (linenr_T)n;
n = tv_list_find_nr(l, i++, NULL); // col
if (n < 0) {
return FAIL;
}
- posp->col = n;
+ // If character position is specified, then convert to byte position
+ if (charcol) {
+ // Get the text for the specified line in a loaded buffer
+ buf_T *buf = buflist_findnr(fnump == NULL ? curbuf->b_fnum : *fnump);
+ if (buf == NULL || buf->b_ml.ml_mfp == NULL) {
+ return FAIL;
+ }
+ n = buf_charidx_to_byteidx(buf, posp->lnum, (int)n) + 1;
+ }
+ posp->col = (colnr_T)n;
n = tv_list_find_nr(l, i, NULL); // off
if (n < 0) {
posp->coladd = 0;
} else {
- posp->coladd = n;
+ posp->coladd = (colnr_T)n;
}
if (curswantp != NULL) {
- *curswantp = tv_list_find_nr(l, i + 1, NULL); // curswant
+ *curswantp = (colnr_T)tv_list_find_nr(l, i + 1, NULL); // curswant
}
return OK;
}
-/*
- * Get the length of an environment variable name.
- * Advance "arg" to the first character after the name.
- * Return 0 for error.
- */
-static int get_env_len(const char_u **arg)
+/// Get the length of an environment variable name.
+/// Advance "arg" to the first character after the name.
+///
+/// @return 0 for error.
+static int get_env_len(const char **arg)
{
int len;
- const char_u *p;
- for (p = *arg; vim_isIDc(*p); p++) {
- }
+ const char *p;
+ for (p = *arg; vim_isIDc(*p); p++) {}
if (p == *arg) { // No name found.
return 0;
}
@@ -8348,9 +7892,11 @@ static int get_env_len(const char_u **arg)
return len;
}
-// Get the length of the name of a function or internal variable.
-// "arg" is advanced to the first non-white character after the name.
-// Return 0 if something is wrong.
+/// Get the length of the name of a function or internal variable.
+///
+/// @param arg is advanced to the first non-white character after the name.
+///
+/// @return 0 if something is wrong.
int get_id_len(const char **const arg)
{
int len;
@@ -8373,20 +7919,20 @@ int get_id_len(const char **const arg)
}
len = (int)(p - *arg);
- *arg = (const char *)skipwhite((const char_u *)p);
+ *arg = (const char *)skipwhite(p);
return len;
}
-/*
- * Get the length of the name of a variable or function.
- * Only the name is recognized, does not handle ".key" or "[idx]".
- * "arg" is advanced to the first non-white character after the name.
- * Return -1 if curly braces expansion failed.
- * Return 0 if something else is wrong.
- * If the name contains 'magic' {}'s, expand them and return the
- * expanded name in an allocated string via 'alias' - caller must free.
- */
+/// Get the length of the name of a variable or function.
+/// Only the name is recognized, does not handle ".key" or "[idx]".
+///
+/// @param arg is advanced to the first non-white character after the name.
+/// If the name contains 'magic' {}'s, expand them and return the
+/// expanded name in an allocated string via 'alias' - caller must free.
+///
+/// @return -1 if curly braces expansion failed or
+/// 0 if something else is wrong.
int get_name_len(const char **const arg, char **alias, bool evaluate, bool verbose)
{
int len;
@@ -8406,16 +7952,14 @@ int get_name_len(const char **const arg, char **alias, bool evaluate, bool verbo
}
// Find the end of the name; check for {} construction.
- char_u *expr_start;
- char_u *expr_end;
- const char *p = (const char *)find_name_end((char_u *)(*arg),
- (const char_u **)&expr_start,
- (const char_u **)&expr_end,
- len > 0 ? 0 : FNE_CHECK_START);
+ char *expr_start;
+ char *expr_end;
+ const char *p = find_name_end((*arg), (const char **)&expr_start, (const char **)&expr_end,
+ len > 0 ? 0 : FNE_CHECK_START);
if (expr_start != NULL) {
if (!evaluate) {
len += (int)(p - *arg);
- *arg = (const char *)skipwhite((const char_u *)p);
+ *arg = (const char *)skipwhite(p);
return len;
}
@@ -8423,13 +7967,12 @@ int get_name_len(const char **const arg, char **alias, bool evaluate, bool verbo
* Include any <SID> etc in the expanded string:
* Thus the -len here.
*/
- char_u *temp_string = make_expanded_name((char_u *)(*arg) - len, expr_start,
- expr_end, (char_u *)p);
+ char *temp_string = make_expanded_name(*arg - len, expr_start, expr_end, (char *)p);
if (temp_string == NULL) {
return -1;
}
- *alias = (char *)temp_string;
- *arg = (const char *)skipwhite((const char_u *)p);
+ *alias = temp_string;
+ *arg = (const char *)skipwhite(p);
return (int)STRLEN(temp_string);
}
@@ -8443,14 +7986,17 @@ int get_name_len(const char **const arg, char **alias, bool evaluate, bool verbo
return len;
}
-// Find the end of a variable or function name, taking care of magic braces.
-// If "expr_start" is not NULL then "expr_start" and "expr_end" are set to the
-// start and end of the first magic braces item.
-// "flags" can have FNE_INCL_BR and FNE_CHECK_START.
-// Return a pointer to just after the name. Equal to "arg" if there is no
-// valid name.
-const char_u *find_name_end(const char_u *arg, const char_u **expr_start, const char_u **expr_end,
- int flags)
+/// Find the end of a variable or function name, taking care of magic braces.
+///
+/// @param expr_start if not NULL, then `expr_start` and `expr_end` are set to the
+/// start and end of the first magic braces item.
+///
+/// @param flags can have FNE_INCL_BR and FNE_CHECK_START.
+///
+/// @return a pointer to just after the name. Equal to "arg" if there is no
+/// valid name.
+const char *find_name_end(const char *arg, const char **expr_start, const char **expr_end,
+ int flags)
{
int mb_nest = 0;
int br_nest = 0;
@@ -8466,7 +8012,7 @@ const char_u *find_name_end(const char_u *arg, const char_u **expr_start, const
return arg;
}
- const char_u *p;
+ const char *p;
for (p = arg; *p != NUL
&& (eval_isnamec(*p)
|| *p == '{'
@@ -8475,8 +8021,7 @@ const char_u *find_name_end(const char_u *arg, const char_u **expr_start, const
|| br_nest != 0); MB_PTR_ADV(p)) {
if (*p == '\'') {
// skip over 'string' to avoid counting [ and ] inside it.
- for (p = p + 1; *p != NUL && *p != '\''; MB_PTR_ADV(p)) {
- }
+ for (p = p + 1; *p != NUL && *p != '\''; MB_PTR_ADV(p)) {}
if (*p == NUL) {
break;
}
@@ -8526,26 +8071,24 @@ const char_u *find_name_end(const char_u *arg, const char_u **expr_start, const
return p;
}
-/*
- * Expands out the 'magic' {}'s in a variable/function name.
- * Note that this can call itself recursively, to deal with
- * constructs like foo{bar}{baz}{bam}
- * The four pointer arguments point to "foo{expre}ss{ion}bar"
- * "in_start" ^
- * "expr_start" ^
- * "expr_end" ^
- * "in_end" ^
- *
- * Returns a new allocated string, which the caller must free.
- * Returns NULL for failure.
- */
-static char_u *make_expanded_name(const char_u *in_start, char_u *expr_start, char_u *expr_end,
- char_u *in_end)
+/// Expands out the 'magic' {}'s in a variable/function name.
+/// Note that this can call itself recursively, to deal with
+/// constructs like foo{bar}{baz}{bam}
+/// The four pointer arguments point to "foo{expre}ss{ion}bar"
+/// "in_start" ^
+/// "expr_start" ^
+/// "expr_end" ^
+/// "in_end" ^
+///
+/// @return a new allocated string, which the caller must free or
+/// NULL for failure.
+static char *make_expanded_name(const char *in_start, char *expr_start, char *expr_end,
+ char *in_end)
{
- char_u c1;
- char_u *retval = NULL;
- char_u *temp_result;
- char_u *nextcmd = NULL;
+ char c1;
+ char *retval = NULL;
+ char *temp_result;
+ char *nextcmd = NULL;
if (expr_end == NULL || in_end == NULL) {
return NULL;
@@ -8557,8 +8100,8 @@ static char_u *make_expanded_name(const char_u *in_start, char_u *expr_start, ch
temp_result = eval_to_string(expr_start + 1, &nextcmd, false);
if (temp_result != NULL && nextcmd == NULL) {
- retval = xmalloc(STRLEN(temp_result) + (expr_start - in_start)
- + (in_end - expr_end) + 1);
+ retval = xmalloc(STRLEN(temp_result) + (size_t)(expr_start - in_start)
+ + (size_t)(in_end - expr_end) + 1);
STRCPY(retval, in_start);
STRCAT(retval, temp_result);
STRCAT(retval, expr_end + 1);
@@ -8570,9 +8113,9 @@ static char_u *make_expanded_name(const char_u *in_start, char_u *expr_start, ch
*expr_end = '}';
if (retval != NULL) {
- temp_result = (char_u *)find_name_end(retval,
- (const char_u **)&expr_start,
- (const char_u **)&expr_end, 0);
+ temp_result = (char *)find_name_end(retval,
+ (const char **)&expr_start,
+ (const char **)&expr_end, 0);
if (expr_start != NULL) {
// Further expansion!
temp_result = make_expanded_name(retval, expr_start,
@@ -8585,44 +8128,43 @@ static char_u *make_expanded_name(const char_u *in_start, char_u *expr_start, ch
return retval;
}
-/*
- * Return TRUE if character "c" can be used in a variable or function name.
- * Does not include '{' or '}' for magic braces.
- */
+/// @return TRUE if character "c" can be used in a variable or function name.
+/// Does not include '{' or '}' for magic braces.
int eval_isnamec(int c)
{
return ASCII_ISALNUM(c) || c == '_' || c == ':' || c == AUTOLOAD_CHAR;
}
-/*
- * Return TRUE if character "c" can be used as the first character in a
- * variable or function name (excluding '{' and '}').
- */
+/// @return TRUE if character "c" can be used as the first character in a
+/// variable or function name (excluding '{' and '}').
int eval_isnamec1(int c)
{
return ASCII_ISALPHA(c) || c == '_';
}
-/*
- * Get number v: variable value.
- */
+/// Get typval_T v: variable value.
+typval_T *get_vim_var_tv(int idx)
+{
+ return &vimvars[idx].vv_tv;
+}
+
+/// Get number v: variable value.
varnumber_T get_vim_var_nr(int idx) FUNC_ATTR_PURE
{
return vimvars[idx].vv_nr;
}
-// Get string v: variable value. Uses a static buffer, can only be used once.
-// If the String variable has never been set, return an empty string.
-// Never returns NULL;
-char_u *get_vim_var_str(int idx) FUNC_ATTR_PURE FUNC_ATTR_NONNULL_RET
+/// Get string v: variable value. Uses a static buffer, can only be used once.
+/// If the String variable has never been set, return an empty string.
+/// Never returns NULL.
+char *get_vim_var_str(int idx)
+ FUNC_ATTR_PURE FUNC_ATTR_NONNULL_RET
{
- return (char_u *)tv_get_string(&vimvars[idx].vv_tv);
+ return (char *)tv_get_string(&vimvars[idx].vv_tv);
}
-/*
- * Get List v: variable value. Caller must take care of reference count when
- * needed.
- */
+/// Get List v: variable value. Caller must take care of reference count when
+/// needed.
list_T *get_vim_var_list(int idx) FUNC_ATTR_PURE
{
return vimvars[idx].vv_list;
@@ -8635,21 +8177,18 @@ dict_T *get_vim_var_dict(int idx) FUNC_ATTR_PURE
return vimvars[idx].vv_dict;
}
-/*
- * Set v:char to character "c".
- */
+/// Set v:char to character "c".
void set_vim_var_char(int c)
{
char buf[MB_MAXBYTES + 1];
- buf[utf_char2bytes(c, (char_u *)buf)] = NUL;
+ buf[utf_char2bytes(c, buf)] = NUL;
set_vim_var_string(VV_CHAR, buf, -1);
}
-/*
- * Set v:count to "count" and v:count1 to "count1".
- * When "set_prevcount" is TRUE first set v:prevcount from v:count.
- */
+/// Set v:count to "count" and v:count1 to "count1".
+///
+/// @param set_prevcount if TRUE, first set v:prevcount from v:count.
void set_vcount(long count, long count1, int set_prevcount)
{
if (set_prevcount) {
@@ -8705,9 +8244,9 @@ void set_vim_var_string(const VimVarIndex idx, const char *const val, const ptrd
if (val == NULL) {
vimvars[idx].vv_str = NULL;
} else if (len == -1) {
- vimvars[idx].vv_str = (char_u *)xstrdup(val);
+ vimvars[idx].vv_str = xstrdup(val);
} else {
- vimvars[idx].vv_str = (char_u *)xstrndup(val, (size_t)len);
+ vimvars[idx].vv_str = xstrndup(val, (size_t)len);
}
}
@@ -8757,9 +8296,7 @@ void set_argv_var(char **argv, int argc)
set_vim_var_list(VV_ARGV, l);
}
-/*
- * Set v:register if needed.
- */
+/// Set v:register if needed.
void set_reg_var(int c)
{
char regname;
@@ -8767,7 +8304,7 @@ void set_reg_var(int c)
if (c == 0 || c == ' ') {
regname = '"';
} else {
- regname = c;
+ regname = (char)c;
}
// Avoid free/alloc when the value is already right.
if (vimvars[VV_REG].vv_str == NULL || vimvars[VV_REG].vv_str[0] != c) {
@@ -8775,13 +8312,11 @@ void set_reg_var(int c)
}
}
-/*
- * Get or set v:exception. If "oldval" == NULL, return the current value.
- * Otherwise, restore the value to "oldval" and return NULL.
- * Must always be called in pairs to save and restore v:exception! Does not
- * take care of memory allocations.
- */
-char_u *v_exception(char_u *oldval)
+/// Get or set v:exception. If "oldval" == NULL, return the current value.
+/// Otherwise, restore the value to "oldval" and return NULL.
+/// Must always be called in pairs to save and restore v:exception! Does not
+/// take care of memory allocations.
+char *v_exception(char *oldval)
{
if (oldval == NULL) {
return vimvars[VV_EXCEPTION].vv_str;
@@ -8791,13 +8326,11 @@ char_u *v_exception(char_u *oldval)
return NULL;
}
-/*
- * Get or set v:throwpoint. If "oldval" == NULL, return the current value.
- * Otherwise, restore the value to "oldval" and return NULL.
- * Must always be called in pairs to save and restore v:throwpoint! Does not
- * take care of memory allocations.
- */
-char_u *v_throwpoint(char_u *oldval)
+/// Get or set v:throwpoint. If "oldval" == NULL, return the current value.
+/// Otherwise, restore the value to "oldval" and return NULL.
+/// Must always be called in pairs to save and restore v:throwpoint! Does not
+/// take care of memory allocations.
+char *v_throwpoint(char *oldval)
{
if (oldval == NULL) {
return vimvars[VV_THROWPOINT].vv_str;
@@ -8807,15 +8340,13 @@ char_u *v_throwpoint(char_u *oldval)
return NULL;
}
-/*
- * Set v:cmdarg.
- * If "eap" != NULL, use "eap" to generate the value and return the old value.
- * If "oldarg" != NULL, restore the value to "oldarg" and return NULL.
- * Must always be called in pairs!
- */
-char_u *set_cmdarg(exarg_T *eap, char_u *oldarg)
+/// Set v:cmdarg.
+/// If "eap" != NULL, use "eap" to generate the value and return the old value.
+/// If "oldarg" != NULL, restore the value to "oldarg" and return NULL.
+/// Must always be called in pairs!
+char *set_cmdarg(exarg_T *eap, char *oldarg)
{
- char_u *oldval = vimvars[VV_CMDARG].vv_str;
+ char *oldval = vimvars[VV_CMDARG].vv_str;
if (eap == NULL) {
xfree(oldval);
vimvars[VV_CMDARG].vv_str = oldarg;
@@ -8844,12 +8375,12 @@ char_u *set_cmdarg(exarg_T *eap, char_u *oldarg)
}
const size_t newval_len = len + 1;
- char_u *newval = xmalloc(newval_len);
+ char *newval = xmalloc(newval_len);
if (eap->force_bin == FORCE_BIN) {
- snprintf((char *)newval, newval_len, " ++bin");
+ snprintf(newval, newval_len, " ++bin");
} else if (eap->force_bin == FORCE_NOBIN) {
- snprintf((char *)newval, newval_len, " ++nobin");
+ snprintf(newval, newval_len, " ++nobin");
} else {
*newval = NUL;
}
@@ -8859,12 +8390,12 @@ char_u *set_cmdarg(exarg_T *eap, char_u *oldarg)
}
if (eap->force_ff != 0) {
- snprintf((char *)newval + STRLEN(newval), newval_len, " ++ff=%s",
+ snprintf(newval + STRLEN(newval), newval_len, " ++ff=%s",
eap->force_ff == 'u' ? "unix" :
eap->force_ff == 'd' ? "dos" : "mac");
}
if (eap->force_enc != 0) {
- snprintf((char *)newval + STRLEN(newval), newval_len, " ++enc=%s",
+ snprintf(newval + STRLEN(newval), newval_len, " ++enc=%s",
eap->cmd + eap->force_enc);
}
if (eap->bad_char == BAD_KEEP) {
@@ -8872,7 +8403,7 @@ char_u *set_cmdarg(exarg_T *eap, char_u *oldarg)
} else if (eap->bad_char == BAD_DROP) {
STRCPY(newval + STRLEN(newval), " ++bad=drop");
} else if (eap->bad_char != 0) {
- snprintf((char *)newval + STRLEN(newval), newval_len, " ++bad=%c",
+ snprintf(newval + STRLEN(newval), newval_len, " ++bad=%c",
eap->bad_char);
}
vimvars[VV_CMDARG].vv_str = newval;
@@ -8934,6 +8465,7 @@ static void check_vars(const char *name, size_t len)
/// check if special v:lua value for calling lua functions
bool is_luafunc(partial_T *partial)
+ FUNC_ATTR_PURE
{
return partial == vvlua_partial;
}
@@ -8950,7 +8482,7 @@ static bool tv_is_luafunc(typval_T *tv)
const char *skip_luafunc_name(const char *p)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- while (ASCII_ISALNUM(*p) || *p == '_' || *p == '.' || *p == '\'') {
+ while (ASCII_ISALNUM(*p) || *p == '_' || *p == '-' || *p == '.' || *p == '\'') {
p++;
}
return p;
@@ -8964,7 +8496,7 @@ int check_luafunc_name(const char *const str, const bool paren)
if (*p != (paren ? '(' : NUL)) {
return 0;
} else {
- return (int)(p-str);
+ return (int)(p - str);
}
}
@@ -8981,11 +8513,11 @@ int check_luafunc_name(const char *const str, const bool paren)
/// @param start_leader start of '!' and '-' prefixes
/// @param end_leaderp end of '!' and '-' prefixes
int handle_subscript(const char **const arg, typval_T *rettv, int evaluate, int verbose,
- const char_u *const start_leader, const char_u **const end_leaderp)
+ const char *const start_leader, const char **const end_leaderp)
{
int ret = OK;
dict_T *selfdict = NULL;
- const char_u *lua_funcname = NULL;
+ const char *lua_funcname = NULL;
if (tv_is_luafunc(rettv)) {
if (**arg != '.') {
@@ -8994,7 +8526,7 @@ int handle_subscript(const char **const arg, typval_T *rettv, int evaluate, int
} else {
(*arg)++;
- lua_funcname = (char_u *)(*arg);
+ lua_funcname = *arg;
const int len = check_luafunc_name(*arg, true);
if (len == 0) {
tv_clear(rettv);
@@ -9011,8 +8543,7 @@ int handle_subscript(const char **const arg, typval_T *rettv, int evaluate, int
&& !ascii_iswhite(*(*arg - 1)))
|| (**arg == '-' && (*arg)[1] == '>'))) {
if (**arg == '(') {
- ret = call_func_rettv((char_u **)arg, rettv, evaluate, selfdict, NULL,
- lua_funcname);
+ ret = call_func_rettv((char **)arg, rettv, evaluate, selfdict, NULL, lua_funcname);
// Stop the expression evaluation when immediately aborting on
// error, or when an interrupt occurred or an exception was thrown
@@ -9029,15 +8560,15 @@ int handle_subscript(const char **const arg, typval_T *rettv, int evaluate, int
// Expression "-1.0->method()" applies the leader "-" before
// applying ->.
if (evaluate && *end_leaderp > start_leader) {
- ret = eval7_leader(rettv, start_leader, end_leaderp);
+ ret = eval7_leader(rettv, (char *)start_leader, end_leaderp);
}
if (ret == OK) {
if ((*arg)[2] == '{') {
// expr->{lambda}()
- ret = eval_lambda((char_u **)arg, rettv, evaluate, verbose);
+ ret = eval_lambda((char **)arg, rettv, evaluate, verbose);
} else {
// expr->name()
- ret = eval_method((char_u **)arg, rettv, evaluate, verbose);
+ ret = eval_method((char **)arg, rettv, evaluate, verbose);
}
}
} else { // **arg == '[' || **arg == '.'
@@ -9050,7 +8581,7 @@ int handle_subscript(const char **const arg, typval_T *rettv, int evaluate, int
} else {
selfdict = NULL;
}
- if (eval_index((char_u **)arg, rettv, evaluate, verbose) == FAIL) {
+ if (eval_index((char **)arg, rettv, evaluate, verbose) == FAIL) {
tv_clear(rettv);
ret = FAIL;
}
@@ -9077,11 +8608,12 @@ void set_selfdict(typval_T *const rettv, dict_T *const selfdict)
make_partial(selfdict, rettv);
}
-// Find variable "name" in the list of variables.
-// Return a pointer to it if found, NULL if not found.
-// Careful: "a:0" variables don't have a name.
-// When "htp" is not NULL we are writing to the variable, set "htp" to the
-// hashtab_T used.
+/// Find variable "name" in the list of variables.
+/// Careful: "a:0" variables don't have a name.
+/// When "htp" is not NULL we are writing to the variable, set "htp" to the
+/// hashtab_T used.
+///
+/// @return a pointer to it if found, NULL if not found.
dictitem_T *find_var(const char *const name, const size_t name_len, hashtab_T **htp,
int no_autoload)
{
@@ -9169,6 +8701,8 @@ dictitem_T *find_var_in_ht(hashtab_T *const ht, int htname, const char *const va
/// Finds the dict (g:, l:, s:, …) and hashtable used for a variable.
///
+/// Assigns SID if s: scope is accessed from Lua or anonymous Vimscript. #15994
+///
/// @param[in] name Variable name, possibly with scope prefix.
/// @param[in] name_len Variable name length.
/// @param[out] varname Will be set to the start of the name without scope
@@ -9231,10 +8765,32 @@ static hashtab_T *find_var_ht_dict(const char *name, const size_t name_len, cons
} else if (*name == 'l' && funccal != NULL) { // local variable
*d = &funccal->l_vars;
} else if (*name == 's' // script variable
- && (current_sctx.sc_sid > 0 || current_sctx.sc_sid == SID_STR)
+ && (current_sctx.sc_sid > 0 || current_sctx.sc_sid == SID_STR
+ || current_sctx.sc_sid == SID_LUA)
&& current_sctx.sc_sid <= ga_scripts.ga_len) {
// For anonymous scripts without a script item, create one now so script vars can be used
- if (current_sctx.sc_sid == SID_STR) {
+ if (current_sctx.sc_sid == SID_LUA) {
+ // try to resolve lua filename & line no so it can be shown in lastset messages.
+ nlua_set_sctx(&current_sctx);
+ if (current_sctx.sc_sid != SID_LUA) {
+ // Great we have valid location. Now here this out we'll create a new
+ // script context with the name and lineno of this one. why ?
+ // for behavioral consistency. With this different anonymous exec from
+ // same file can't access each others script local stuff. We need to do
+ // this all other cases except this will act like that otherwise.
+ const LastSet last_set = (LastSet){
+ .script_ctx = current_sctx,
+ .channel_id = LUA_INTERNAL_CALL,
+ };
+ bool should_free;
+ // should_free is ignored as script_sctx will be resolved to a fnmae
+ // & new_script_item will consume it.
+ char *sc_name = (char *)get_scriptname(last_set, &should_free);
+ new_script_item(sc_name, &current_sctx.sc_sid);
+ }
+ }
+ if (current_sctx.sc_sid == SID_STR || current_sctx.sc_sid == SID_LUA) {
+ // Create SID if s: scope is accessed from Lua or anon Vimscript. #15994
new_script_item(NULL, &current_sctx.sc_sid);
}
*d = &SCRIPT_SV(current_sctx.sc_sid)->sv_dict;
@@ -9258,11 +8814,10 @@ hashtab_T *find_var_ht(const char *name, const size_t name_len, const char **var
return find_var_ht_dict(name, name_len, varname, &d);
}
-/*
- * Get the string value of a (global/local) variable.
- * Note: see tv_get_string() for how long the pointer remains valid.
- * Returns NULL when it doesn't exist.
- */
+/// @return the string value of a (global/local) variable or
+/// NULL when it doesn't exist.
+///
+/// @see tv_get_string() for how long the pointer remains valid.
char_u *get_var_value(const char *const name)
{
dictitem_T *v;
@@ -9274,16 +8829,14 @@ char_u *get_var_value(const char *const name)
return (char_u *)tv_get_string(&v->di_tv);
}
-/*
- * Allocate a new hashtab for a sourced script. It will be used while
- * sourcing this script and when executing functions defined in the script.
- */
+/// Allocate a new hashtab for a sourced script. It will be used while
+/// sourcing this script and when executing functions defined in the script.
void new_script_vars(scid_T id)
{
hashtab_T *ht;
scriptvar_T *sv;
- ga_grow(&ga_scripts, (int)(id - ga_scripts.ga_len));
+ ga_grow(&ga_scripts, id - ga_scripts.ga_len);
{
/* Re-allocating ga_data means that an ht_array pointing to
* ht_smallarray becomes invalid. We can recognize this: ht_mask is
@@ -9305,11 +8858,9 @@ void new_script_vars(scid_T id)
}
}
-/*
- * Initialize dictionary "dict" as a scope and set variable "dict_var" to
- * point to it.
- */
-void init_var_dict(dict_T *dict, ScopeDictDictItem *dict_var, int scope)
+/// Initialize dictionary "dict" as a scope and set variable "dict_var" to
+/// point to it.
+void init_var_dict(dict_T *dict, ScopeDictDictItem *dict_var, ScopeType scope)
{
hash_init(&dict->dv_hashtab);
dict->dv_lock = VAR_UNLOCKED;
@@ -9324,9 +8875,7 @@ void init_var_dict(dict_T *dict, ScopeDictDictItem *dict_var, int scope)
QUEUE_INIT(&dict->watchers);
}
-/*
- * Unreference a dictionary initialized by init_var_dict().
- */
+/// Unreference a dictionary initialized by init_var_dict().
void unref_var_dict(dict_T *dict)
{
/* Now the dict needs to be freed if no one else is using it, go back to
@@ -9335,19 +8884,15 @@ void unref_var_dict(dict_T *dict)
tv_dict_unref(dict);
}
-/*
- * Clean up a list of internal variables.
- * Frees all allocated variables and the value they contain.
- * Clears hashtab "ht", does not free it.
- */
+/// Clean up a list of internal variables.
+/// Frees all allocated variables and the value they contain.
+/// Clears hashtab "ht", does not free it.
void vars_clear(hashtab_T *ht)
{
vars_clear_ext(ht, TRUE);
}
-/*
- * Like vars_clear(), but only free the value if "free_val" is TRUE.
- */
+/// Like vars_clear(), but only free the value if "free_val" is TRUE.
void vars_clear_ext(hashtab_T *ht, int free_val)
{
int todo;
@@ -9376,10 +8921,8 @@ void vars_clear_ext(hashtab_T *ht, int free_val)
ht->ht_used = 0;
}
-/*
- * Delete a variable from hashtab "ht" at item "hi".
- * Clear the variable value and free the dictitem.
- */
+/// Delete a variable from hashtab "ht" at item "hi".
+/// Clear the variable value and free the dictitem.
static void delete_var(hashtab_T *ht, hashitem_T *hi)
{
dictitem_T *di = TV_DICT_HI2DI(hi);
@@ -9389,13 +8932,11 @@ static void delete_var(hashtab_T *ht, hashitem_T *hi)
xfree(di);
}
-/*
- * List the value of one internal variable.
- */
+/// List the value of one internal variable.
static void list_one_var(dictitem_T *v, const char *prefix, int *first)
{
char *const s = encode_tv2echo(&v->di_tv, NULL);
- list_one_var_a(prefix, (const char *)v->di_key, STRLEN(v->di_key),
+ list_one_var_a(prefix, (const char *)v->di_key, (ptrdiff_t)STRLEN(v->di_key),
v->di_tv.v_type, (s == NULL ? "" : s), first);
xfree(s);
}
@@ -9404,7 +8945,7 @@ static void list_one_var(dictitem_T *v, const char *prefix, int *first)
/// will be used.
/// @param[in,out] first When true clear rest of screen and set to false.
static void list_one_var_a(const char *prefix, const char *name, const ptrdiff_t name_len,
- const int type, const char *string, int *first)
+ const VarType type, const char *string, int *first)
{
// don't use msg() or msg_attr() to avoid overwriting "v:statusmsg"
msg_start();
@@ -9432,7 +8973,7 @@ static void list_one_var_a(const char *prefix, const char *name, const ptrdiff_t
msg_putchar(' ');
}
- msg_outtrans((char_u *)string);
+ msg_outtrans((char *)string);
if (type == VAR_FUNC || type == VAR_PARTIAL) {
msg_puts("()");
@@ -9519,7 +9060,7 @@ static void set_var_const(const char *name, const size_t name_len, typval_T *con
// Careful: when assigning to v:errmsg and tv_get_string()
// causes an error message the variable will already be set.
if (v->di_tv.vval.v_string == NULL) {
- v->di_tv.vval.v_string = (char_u *)xstrdup(val);
+ v->di_tv.vval.v_string = xstrdup(val);
}
} else {
// Take over the string to avoid an extra alloc/free.
@@ -9689,7 +9230,7 @@ bool var_check_func_name(const char *const name, const bool new_var)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
// Allow for w: b: s: and t:.
- if (!(vim_strchr((char_u *)"wbst", name[0]) != NULL && name[1] == ':')
+ if (!(vim_strchr("wbst", name[0]) != NULL && name[1] == ':')
&& !ASCII_ISUPPER((name[0] != NUL && name[1] == ':')
? name[2] : name[0])) {
semsg(_("E704: Funcref variable name must start with a capital: %s"), name);
@@ -9773,11 +9314,11 @@ int var_item_copy(const vimconv_T *const conv, typval_T *const from, typval_T *c
} else {
to->v_type = VAR_STRING;
to->v_lock = VAR_UNLOCKED;
- if ((to->vval.v_string = string_convert((vimconv_T *)conv,
- from->vval.v_string,
- NULL))
+ if ((to->vval.v_string = (char *)string_convert((vimconv_T *)conv,
+ (char_u *)from->vval.v_string,
+ NULL))
== NULL) {
- to->vval.v_string = (char_u *)xstrdup((char *)from->vval.v_string);
+ to->vval.v_string = xstrdup(from->vval.v_string);
}
}
break;
@@ -9824,14 +9365,12 @@ int var_item_copy(const vimconv_T *const conv, typval_T *const from, typval_T *c
return ret;
}
-/*
- * ":echo expr1 ..." print each argument separated with a space, add a
- * newline at the end.
- * ":echon expr1 ..." print each argument plain.
- */
+/// ":echo expr1 ..." print each argument separated with a space, add a
+/// newline at the end.
+/// ":echon expr1 ..." print each argument plain.
void ex_echo(exarg_T *eap)
{
- char_u *arg = eap->arg;
+ char *arg = eap->arg;
typval_T rettv;
bool atstart = true;
bool need_clear = true;
@@ -9847,7 +9386,7 @@ void ex_echo(exarg_T *eap)
need_clr_eos = true;
{
- char_u *p = arg;
+ char *p = arg;
if (eval1(&arg, &rettv, !eap->skip) == FAIL) {
// Report the invalid expression unless the expression evaluation
// has been cancelled due to an aborting error, an interrupt, or an
@@ -9887,7 +9426,7 @@ void ex_echo(exarg_T *eap)
tv_clear(&rettv);
arg = skipwhite(arg);
}
- eap->nextcmd = check_nextcmd(arg);
+ eap->nextcmd = (char *)check_nextcmd((char_u *)arg);
if (eap->skip) {
emsg_skip--;
@@ -9902,24 +9441,20 @@ void ex_echo(exarg_T *eap)
}
}
-/*
- * ":echohl {name}".
- */
+/// ":echohl {name}".
void ex_echohl(exarg_T *eap)
{
- echo_attr = syn_name2attr(eap->arg);
+ echo_attr = syn_name2attr((char_u *)eap->arg);
}
-/*
- * ":execute expr1 ..." execute the result of an expression.
- * ":echomsg expr1 ..." Print a message
- * ":echoerr expr1 ..." Print an error
- * Each gets spaces around each argument and a newline at the end for
- * echo commands
- */
+/// ":execute expr1 ..." execute the result of an expression.
+/// ":echomsg expr1 ..." Print a message
+/// ":echoerr expr1 ..." Print an error
+/// Each gets spaces around each argument and a newline at the end for
+/// echo commands
void ex_execute(exarg_T *eap)
{
- char_u *arg = eap->arg;
+ char *arg = eap->arg;
typval_T rettv;
int ret = OK;
garray_T ga;
@@ -9943,7 +9478,7 @@ void ex_execute(exarg_T *eap)
? encode_tv2echo(&rettv, NULL)
: encode_tv2string(&rettv, NULL);
const size_t len = strlen(argstr);
- ga_grow(&ga, len + 2);
+ ga_grow(&ga, (int)len + 2);
if (!GA_EMPTY(&ga)) {
((char_u *)(ga.ga_data))[ga.ga_len++] = ' ';
}
@@ -9951,7 +9486,7 @@ void ex_execute(exarg_T *eap)
if (eap->cmdidx != CMD_execute) {
xfree((void *)argstr);
}
- ga.ga_len += len;
+ ga.ga_len += (int)len;
}
tv_clear(&rettv);
@@ -9979,8 +9514,7 @@ void ex_execute(exarg_T *eap)
did_emsg = save_did_emsg;
}
} else if (eap->cmdidx == CMD_execute) {
- do_cmdline((char_u *)ga.ga_data,
- eap->getline, eap->cookie, DOCMD_NOWAIT|DOCMD_VERBOSE);
+ do_cmdline(ga.ga_data, eap->getline, eap->cookie, DOCMD_NOWAIT|DOCMD_VERBOSE);
}
}
@@ -9990,15 +9524,15 @@ void ex_execute(exarg_T *eap)
--emsg_skip;
}
- eap->nextcmd = check_nextcmd(arg);
+ eap->nextcmd = (char *)check_nextcmd((char_u *)arg);
}
-/*
- * Skip over the name of an option: "&option", "&g:option" or "&l:option".
- * "arg" points to the "&" or '+' when called, to "option" when returning.
- * Returns NULL when no option name found. Otherwise pointer to the char
- * after the option name.
- */
+/// Skip over the name of an option: "&option", "&g:option" or "&l:option".
+///
+/// @param arg points to the "&" or '+' when called, to "option" when returning.
+///
+/// @return NULL when no option name found. Otherwise pointer to the char
+/// after the option name.
static const char *find_option_end(const char **const arg, int *const opt_flags)
{
const char *p = *arg;
@@ -10043,15 +9577,15 @@ void func_do_profile(ufunc_T *fp)
fp->uf_tm_total = profile_zero();
if (fp->uf_tml_count == NULL) {
- fp->uf_tml_count = xcalloc(len, sizeof(int));
+ fp->uf_tml_count = xcalloc((size_t)len, sizeof(int));
}
if (fp->uf_tml_total == NULL) {
- fp->uf_tml_total = xcalloc(len, sizeof(proftime_T));
+ fp->uf_tml_total = xcalloc((size_t)len, sizeof(proftime_T));
}
if (fp->uf_tml_self == NULL) {
- fp->uf_tml_self = xcalloc(len, sizeof(proftime_T));
+ fp->uf_tml_self = xcalloc((size_t)len, sizeof(proftime_T));
}
fp->uf_tml_idx = -1;
@@ -10061,9 +9595,7 @@ void func_do_profile(ufunc_T *fp)
fp->uf_profiling = TRUE;
}
-/*
- * Dump the profiling results for all functions in file "fd".
- */
+/// Dump the profiling results for all functions in file "fd".
void func_dump_profile(FILE *fd)
{
hashitem_T *hi;
@@ -10077,7 +9609,7 @@ void func_dump_profile(FILE *fd)
return; // nothing to dump
}
- sorttab = xmalloc(sizeof(ufunc_T *) * todo);
+ sorttab = xmalloc(sizeof(ufunc_T *) * (size_t)todo);
for (hi = func_hashtab.ht_array; todo > 0; ++hi) {
if (!HASHITEM_EMPTY(hi)) {
@@ -10097,7 +9629,7 @@ void func_dump_profile(FILE *fd)
.script_ctx = fp->uf_script_ctx,
.channel_id = 0,
};
- char_u *p = get_scriptname(last_set, &should_free);
+ char *p = (char *)get_scriptname(last_set, &should_free);
fprintf(fd, " Defined: %s:%" PRIdLINENR "\n",
p, fp->uf_script_ctx.sc_lnum);
if (should_free) {
@@ -10183,9 +9715,7 @@ static void prof_func_line(FILE *fd, int count, proftime_T *total, proftime_T *s
}
}
-/*
- * Compare function for total time sorting.
- */
+/// Compare function for total time sorting.
static int prof_total_cmp(const void *s1, const void *s2)
{
ufunc_T *p1 = *(ufunc_T **)s1;
@@ -10193,9 +9723,7 @@ static int prof_total_cmp(const void *s1, const void *s2)
return profile_cmp(p1->uf_tm_total, p2->uf_tm_total);
}
-/*
- * Compare function for self time sorting.
- */
+/// Compare function for self time sorting.
static int prof_self_cmp(const void *s1, const void *s2)
{
ufunc_T *p1 = *(ufunc_T **)s1;
@@ -10277,12 +9805,10 @@ bool script_autoload(const char *const name, const size_t name_len, const bool r
return ret;
}
-/*
- * Called when starting to read a function line.
- * "sourcing_lnum" must be correct!
- * When skipping lines it may not actually be executed, but we won't find out
- * until later and we need to store the time now.
- */
+/// Called when starting to read a function line.
+/// "sourcing_lnum" must be correct!
+/// When skipping lines it may not actually be executed, but we won't find out
+/// until later and we need to store the time now.
void func_line_start(void *cookie)
{
funccall_T *fcp = (funccall_T *)cookie;
@@ -10302,9 +9828,7 @@ void func_line_start(void *cookie)
}
}
-/*
- * Called when actually executing a function line.
- */
+/// Called when actually executing a function line.
void func_line_exec(void *cookie)
{
funccall_T *fcp = (funccall_T *)cookie;
@@ -10315,9 +9839,7 @@ void func_line_exec(void *cookie)
}
}
-/*
- * Called when done with a function line.
- */
+/// Called when done with a function line.
void func_line_end(void *cookie)
{
funccall_T *fcp = (funccall_T *)cookie;
@@ -10338,9 +9860,10 @@ void func_line_end(void *cookie)
}
}
-static var_flavour_T var_flavour(char_u *varname)
+static var_flavour_T var_flavour(char *varname)
+ FUNC_ATTR_PURE
{
- char_u *p = varname;
+ char *p = varname;
if (ASCII_ISUPPER(*p)) {
while (*(++p)) {
@@ -10377,7 +9900,7 @@ const void *var_shada_iter(const void *const iter, const char **const name, typv
hi = globvarht.ht_array;
while ((size_t)(hi - hifirst) < hinum
&& (HASHITEM_EMPTY(hi)
- || !(var_flavour(hi->hi_key) & flavour))) {
+ || !(var_flavour((char *)hi->hi_key) & flavour))) {
hi++;
}
if ((size_t)(hi - hifirst) == hinum) {
@@ -10389,7 +9912,7 @@ const void *var_shada_iter(const void *const iter, const char **const name, typv
*name = (char *)TV_DICT_HI2DI(hi)->di_key;
tv_copy(&TV_DICT_HI2DI(hi)->di_tv, rettv);
while ((size_t)(++hi - hifirst) < hinum) {
- if (!HASHITEM_EMPTY(hi) && (var_flavour(hi->hi_key) & flavour)) {
+ if (!HASHITEM_EMPTY(hi) && (var_flavour((char *)hi->hi_key) & flavour)) {
return hi;
}
}
@@ -10410,12 +9933,12 @@ int store_session_globals(FILE *fd)
TV_DICT_ITER(&globvardict, this_var, {
if ((this_var->di_tv.v_type == VAR_NUMBER
|| this_var->di_tv.v_type == VAR_STRING)
- && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) {
+ && var_flavour((char *)this_var->di_key) == VAR_FLAVOUR_SESSION) {
// Escape special characters with a backslash. Turn a LF and
// CR into \n and \r.
- char_u *const p = vim_strsave_escaped((const char_u *)tv_get_string(&this_var->di_tv),
- (const char_u *)"\\\"\n\r");
- for (char_u *t = p; *t != NUL; t++) {
+ char *const p = (char *)vim_strsave_escaped((const char_u *)tv_get_string(&this_var->di_tv),
+ (const char_u *)"\\\"\n\r");
+ for (char *t = p; *t != NUL; t++) {
if (*t == '\n') {
*t = 'n';
} else if (*t == '\r') {
@@ -10435,7 +9958,7 @@ int store_session_globals(FILE *fd)
}
xfree(p);
} else if (this_var->di_tv.v_type == VAR_FLOAT
- && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) {
+ && var_flavour((char *)this_var->di_key) == VAR_FLAVOUR_SESSION) {
float_T f = this_var->di_tv.vval.v_float;
int sign = ' ';
@@ -10452,10 +9975,8 @@ int store_session_globals(FILE *fd)
return OK;
}
-/*
- * Display script name where an item was last set.
- * Should only be invoked when 'verbose' is non-zero.
- */
+/// Display script name where an item was last set.
+/// Should only be invoked when 'verbose' is non-zero.
void last_set_msg(sctx_T script_ctx)
{
const LastSet last_set = (LastSet){
@@ -10472,10 +9993,10 @@ void option_last_set_msg(LastSet last_set)
{
if (last_set.script_ctx.sc_sid != 0) {
bool should_free;
- char_u *p = get_scriptname(last_set, &should_free);
+ char *p = (char *)get_scriptname(last_set, &should_free);
verbose_enter();
msg_puts(_("\n\tLast set from "));
- msg_puts((char *)p);
+ msg_puts(p);
if (last_set.script_ctx.sc_lnum > 0) {
msg_puts(_(line_msg));
msg_outnum((long)last_set.script_ctx.sc_lnum);
@@ -10511,20 +10032,21 @@ void reset_v_option_vars(void)
/// @param fnamep file name so far
/// @param bufp buffer for allocated file name or NULL
/// @param fnamelen length of fnamep
-int modify_fname(char_u *src, bool tilde_file, size_t *usedlen, char_u **fnamep, char_u **bufp,
+int modify_fname(char *src, bool tilde_file, size_t *usedlen, char **fnamep, char **bufp,
size_t *fnamelen)
{
int valid = 0;
- char_u *tail;
- char_u *s, *p, *pbuf;
- char_u dirname[MAXPATHL];
+ char *tail;
+ char *s, *p, *pbuf;
+ char dirname[MAXPATHL];
int c;
- int has_fullname = 0;
+ bool has_fullname = false;
+ bool has_homerelative = false;
repeat:
// ":p" - full path/file_name
if (src[*usedlen] == ':' && src[*usedlen + 1] == 'p') {
- has_fullname = 1;
+ has_fullname = true;
valid |= VALID_PATH;
*usedlen += 2;
@@ -10560,8 +10082,8 @@ repeat:
}
// FullName_save() is slow, don't use it when not needed.
- if (*p != NUL || !vim_isAbsName(*fnamep)) {
- *fnamep = (char_u *)FullName_save((char *)(*fnamep), *p != NUL);
+ if (*p != NUL || !vim_isAbsName((char_u *)(*fnamep))) {
+ *fnamep = FullName_save((*fnamep), *p != NUL);
xfree(*bufp); // free any allocated file name
*bufp = *fnamep;
if (*fnamep == NULL) {
@@ -10570,15 +10092,12 @@ repeat:
}
// Append a path separator to a directory.
- if (os_isdir(*fnamep)) {
+ if (os_isdir((char_u *)(*fnamep))) {
// Make room for one or two extra characters.
- *fnamep = vim_strnsave(*fnamep, STRLEN(*fnamep) + 2);
+ *fnamep = xstrnsave(*fnamep, STRLEN(*fnamep) + 2);
xfree(*bufp); // free any allocated file name
*bufp = *fnamep;
- if (*fnamep == NULL) {
- return -1;
- }
- add_pathsep((char *)*fnamep);
+ add_pathsep(*fnamep);
}
}
@@ -10586,45 +10105,61 @@ repeat:
// ":~" - path relative to the home directory
// ":8" - shortname path - postponed till after
while (src[*usedlen] == ':'
- && ((c = src[*usedlen + 1]) == '.' || c == '~' || c == '8')) {
+ && ((c = (char_u)src[*usedlen + 1]) == '.' || c == '~' || c == '8')) {
*usedlen += 2;
if (c == '8') {
continue;
}
pbuf = NULL;
// Need full path first (use expand_env() to remove a "~/")
- if (!has_fullname) {
- if (c == '.' && **fnamep == '~') {
+ if (!has_fullname && !has_homerelative) {
+ if (**fnamep == '~') {
p = pbuf = expand_env_save(*fnamep);
} else {
- p = pbuf = (char_u *)FullName_save((char *)*fnamep, FALSE);
+ p = pbuf = FullName_save(*fnamep, false);
}
} else {
p = *fnamep;
}
- has_fullname = 0;
+ has_fullname = false;
if (p != NULL) {
if (c == '.') {
- os_dirname(dirname, MAXPATHL);
- s = path_shorten_fname(p, dirname);
- if (s != NULL) {
- *fnamep = s;
- if (pbuf != NULL) {
- xfree(*bufp); // free any allocated file name
- *bufp = pbuf;
- pbuf = NULL;
+ os_dirname((char_u *)dirname, MAXPATHL);
+ if (has_homerelative) {
+ s = xstrdup(dirname);
+ home_replace(NULL, s, dirname, MAXPATHL, true);
+ xfree(s);
+ }
+ size_t namelen = STRLEN(dirname);
+
+ // Do not call shorten_fname() here since it removes the prefix
+ // even though the path does not have a prefix.
+ if (FNAMENCMP(p, dirname, namelen) == 0) {
+ p += namelen;
+ if (vim_ispathsep(*p)) {
+ while (*p && vim_ispathsep(*p)) {
+ p++;
+ }
+ *fnamep = p;
+ if (pbuf != NULL) {
+ // free any allocated file name
+ xfree(*bufp);
+ *bufp = pbuf;
+ pbuf = NULL;
+ }
}
}
} else {
home_replace(NULL, p, dirname, MAXPATHL, true);
// Only replace it when it starts with '~'
if (*dirname == '~') {
- s = vim_strsave(dirname);
+ s = xstrdup(dirname);
*fnamep = s;
xfree(*bufp);
*bufp = s;
+ has_homerelative = true;
}
}
xfree(pbuf);
@@ -10639,18 +10174,18 @@ repeat:
while (src[*usedlen] == ':' && src[*usedlen + 1] == 'h') {
valid |= VALID_HEAD;
*usedlen += 2;
- s = get_past_head(*fnamep);
- while (tail > s && after_pathsep((char *)s, (char *)tail)) {
+ s = (char *)get_past_head((char_u *)(*fnamep));
+ while (tail > s && after_pathsep(s, tail)) {
MB_PTR_BACK(*fnamep, tail);
}
*fnamelen = (size_t)(tail - *fnamep);
if (*fnamelen == 0) {
// Result is empty. Turn it into "." to make ":cd %:h" work.
xfree(*bufp);
- *bufp = *fnamep = tail = vim_strsave((char_u *)".");
+ *bufp = *fnamep = tail = xstrdup(".");
*fnamelen = 1;
} else {
- while (tail > s && !after_pathsep((char *)s, (char *)tail)) {
+ while (tail > s && !after_pathsep(s, tail)) {
MB_PTR_BACK(*fnamep, tail);
}
}
@@ -10661,7 +10196,6 @@ repeat:
*usedlen += 2;
}
-
// ":t" - tail, just the basename
if (src[*usedlen] == ':' && src[*usedlen + 1] == 't') {
*usedlen += 2;
@@ -10679,9 +10213,9 @@ repeat:
*/
const bool is_second_e = *fnamep > tail;
if (src[*usedlen + 1] == 'e' && is_second_e) {
- s = *fnamep - 2;
+ s = (*fnamep) - 2;
} else {
- s = *fnamep + *fnamelen - 1;
+ s = (*fnamep) + *fnamelen - 1;
}
for (; s > tail; s--) {
@@ -10692,8 +10226,8 @@ repeat:
if (src[*usedlen + 1] == 'e') {
if (s > tail || (0 && is_second_e && s == tail)) {
// we stopped at a '.' (so anchor to &'.' + 1)
- char_u *newstart = s + 1;
- size_t distance_stepped_back = *fnamep - newstart;
+ char *newstart = s + 1;
+ size_t distance_stepped_back = (size_t)(*fnamep - newstart);
*fnamelen += distance_stepped_back;
*fnamep = newstart;
} else if (*fnamep <= tail) {
@@ -10715,7 +10249,7 @@ repeat:
// "path/to/this.file.ext" :r:r:r
// ^ ^------------- tail
// +--------------------- *fnamep
- if (s > MAX(tail, *fnamep)) {
+ if (s > MAX(tail, (char *)(*fnamep))) {
*fnamelen = (size_t)(s - *fnamep);
}
}
@@ -10728,28 +10262,28 @@ repeat:
&& (src[*usedlen + 1] == 's'
|| (src[*usedlen + 1] == 'g' && src[*usedlen + 2] == 's'))) {
int sep;
- char_u *flags;
- int didit = FALSE;
+ char *flags;
+ int didit = false;
- flags = (char_u *)"";
+ flags = "";
s = src + *usedlen + 2;
if (src[*usedlen + 1] == 'g') {
- flags = (char_u *)"g";
- ++s;
+ flags = "g";
+ s++;
}
- sep = *s++;
+ sep = (char_u)(*s++);
if (sep) {
// find end of pattern
p = vim_strchr(s, sep);
if (p != NULL) {
- char_u *const pat = vim_strnsave(s, p - s);
+ char *const pat = xstrnsave(s, (size_t)(p - s));
s = p + 1;
// find end of substitution
p = vim_strchr(s, sep);
if (p != NULL) {
- char_u *const sub = vim_strnsave(s, p - s);
- char_u *const str = vim_strnsave(*fnamep, *fnamelen);
+ char *const sub = xstrnsave(s, (size_t)(p - s));
+ char *const str = xstrnsave(*fnamep, *fnamelen);
*usedlen = (size_t)(p + 1 - src);
s = do_string_sub(str, pat, sub, NULL, flags);
*fnamep = s;
@@ -10771,13 +10305,13 @@ repeat:
if (src[*usedlen] == ':' && src[*usedlen + 1] == 'S') {
// vim_strsave_shellescape() needs a NUL terminated string.
- c = (*fnamep)[*fnamelen];
+ c = (char_u)(*fnamep)[*fnamelen];
if (c != NUL) {
(*fnamep)[*fnamelen] = NUL;
}
- p = vim_strsave_shellescape(*fnamep, false, false);
+ p = (char *)vim_strsave_shellescape((char_u *)(*fnamep), false, false);
if (c != NUL) {
- (*fnamep)[*fnamelen] = c;
+ (*fnamep)[*fnamelen] = (char)c;
}
xfree(*bufp);
*bufp = *fnamep = p;
@@ -10791,21 +10325,22 @@ repeat:
/// Perform a substitution on "str" with pattern "pat" and substitute "sub".
/// When "sub" is NULL "expr" is used, must be a VAR_FUNC or VAR_PARTIAL.
/// "flags" can be "g" to do a global substitute.
-/// Returns an allocated string, NULL for error.
-char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, typval_T *expr, char_u *flags)
+///
+/// @return an allocated string, NULL for error.
+char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, char *flags)
{
int sublen;
regmatch_T regmatch;
int do_all;
- char_u *tail;
- char_u *end;
+ char *tail;
+ char *end;
garray_T ga;
- char_u *save_cpo;
- char_u *zero_width = NULL;
+ char *save_cpo;
+ char *zero_width = NULL;
// Make 'cpoptions' empty, so that the 'l' flag doesn't work here
save_cpo = p_cpo;
- p_cpo = empty_option;
+ p_cpo = (char *)empty_option;
ga_init(&ga, 1, 200);
@@ -10816,10 +10351,10 @@ char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, typval_T *expr, cha
if (regmatch.regprog != NULL) {
tail = str;
end = str + STRLEN(str);
- while (vim_regexec_nl(&regmatch, str, (colnr_T)(tail - str))) {
+ while (vim_regexec_nl(&regmatch, (char_u *)str, (colnr_T)(tail - str))) {
// Skip empty match except for first match.
if (regmatch.startp[0] == regmatch.endp[0]) {
- if (zero_width == regmatch.startp[0]) {
+ if ((char_u *)zero_width == regmatch.startp[0]) {
// avoid getting stuck on a match with an empty string
int i = utfc_ptr2len(tail);
memmove((char_u *)ga.ga_data + ga.ga_len, tail, (size_t)i);
@@ -10827,7 +10362,7 @@ char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, typval_T *expr, cha
tail += i;
continue;
}
- zero_width = regmatch.startp[0];
+ zero_width = (char *)regmatch.startp[0];
}
// Get some space for a temporary buffer to do the substitution
@@ -10835,18 +10370,19 @@ char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, typval_T *expr, cha
// - The text up to where the match is.
// - The substituted text.
// - The text after the match.
- sublen = vim_regsub(&regmatch, sub, expr, tail, false, true, false);
+ sublen = vim_regsub(&regmatch, (char_u *)sub, expr, (char_u *)tail, 0, REGSUB_MAGIC);
ga_grow(&ga, (int)((end - tail) + sublen -
(regmatch.endp[0] - regmatch.startp[0])));
// copy the text up to where the match is
- int i = (int)(regmatch.startp[0] - tail);
+ int i = (int)(regmatch.startp[0] - (char_u *)tail);
memmove((char_u *)ga.ga_data + ga.ga_len, tail, (size_t)i);
// add the substituted text
- (void)vim_regsub(&regmatch, sub, expr, (char_u *)ga.ga_data
- + ga.ga_len + i, true, true, false);
+ (void)vim_regsub(&regmatch, (char_u *)sub, expr,
+ (char_u *)ga.ga_data + ga.ga_len + i, sublen,
+ REGSUB_COPY | REGSUB_MAGIC);
ga.ga_len += i + sublen - 1;
- tail = regmatch.endp[0];
+ tail = (char *)regmatch.endp[0];
if (*tail == NUL) {
break;
}
@@ -10862,13 +10398,13 @@ char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, typval_T *expr, cha
vim_regfree(regmatch.regprog);
}
- char_u *ret = vim_strsave(ga.ga_data == NULL ? str : (char_u *)ga.ga_data);
+ char *ret = xstrdup(ga.ga_data == NULL ? str : ga.ga_data);
ga_clear(&ga);
- if (p_cpo == empty_option) {
+ if ((char_u *)p_cpo == empty_option) {
p_cpo = save_cpo;
} else {
// Darn, evaluating {sub} expression or {expr} changed the value.
- free_string_option(save_cpo);
+ free_string_option((char_u *)save_cpo);
}
return ret;
@@ -10901,7 +10437,6 @@ bool common_job_callbacks(dict_T *vopts, CallbackReader *on_stdout, CallbackRead
return false;
}
-
Channel *find_job(uint64_t id, bool show_error)
{
Channel *data = find_channel(id);
@@ -10919,7 +10454,6 @@ Channel *find_job(uint64_t id, bool show_error)
return data;
}
-
void script_host_eval(char *name, typval_T *argvars, typval_T *rettv)
{
if (check_secure()) {
@@ -10969,7 +10503,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments, boo
provider_call_nesting++;
typval_T argvars[3] = {
- { .v_type = VAR_STRING, .vval.v_string = (char_u *)method,
+ { .v_type = VAR_STRING, .vval.v_string = method,
.v_lock = VAR_UNLOCKED },
{ .v_type = VAR_LIST, .vval.v_list = arguments, .v_lock = VAR_UNLOCKED },
{ .v_type = VAR_UNKNOWN }
@@ -10981,7 +10515,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments, boo
funcexe.firstline = curwin->w_cursor.lnum;
funcexe.lastline = curwin->w_cursor.lnum;
funcexe.evaluate = true;
- (void)call_func((const char_u *)func, name_len, &rettv, 2, argvars, &funcexe);
+ (void)call_func(func, name_len, &rettv, 2, argvars, &funcexe);
tv_list_unref(arguments);
// Restore caller scope information
@@ -11001,10 +10535,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments, boo
bool eval_has_provider(const char *feat)
{
if (!strequal(feat, "clipboard")
- && !strequal(feat, "python")
&& !strequal(feat, "python3")
- && !strequal(feat, "python_compiled")
- && !strequal(feat, "python_dynamic")
&& !strequal(feat, "python3_compiled")
&& !strequal(feat, "python3_dynamic")
&& !strequal(feat, "perl")
@@ -11025,7 +10556,7 @@ bool eval_has_provider(const char *feat)
if (get_var_tv(buf, len, &tv, NULL, false, true) == FAIL) {
// Trigger autoload once.
len = snprintf(buf, sizeof(buf), "provider#%s#bogus", name);
- script_autoload(buf, len, false);
+ script_autoload(buf, (size_t)len, false);
// Retry the (non-autoload-style) variable.
len = snprintf(buf, sizeof(buf), "g:loaded_%s_provider", name);
@@ -11103,26 +10634,26 @@ void invoke_prompt_callback(void)
{
typval_T rettv;
typval_T argv[2];
- char_u *text;
- char_u *prompt;
+ char *text;
+ char *prompt;
linenr_T lnum = curbuf->b_ml.ml_line_count;
// Add a new line for the prompt before invoking the callback, so that
// text can always be inserted above the last line.
- ml_append(lnum, (char_u *)"", 0, false);
+ ml_append(lnum, "", 0, false);
curwin->w_cursor.lnum = lnum + 1;
curwin->w_cursor.col = 0;
if (curbuf->b_prompt_callback.type == kCallbackNone) {
return;
}
- text = ml_get(lnum);
- prompt = prompt_text();
+ text = (char *)ml_get(lnum);
+ prompt = (char *)prompt_text();
if (STRLEN(text) >= STRLEN(prompt)) {
text += STRLEN(prompt);
}
argv[0].v_type = VAR_STRING;
- argv[0].vval.v_string = vim_strsave(text);
+ argv[0].vval.v_string = xstrdup(text);
argv[1].v_type = VAR_UNKNOWN;
callback_call(&curbuf->b_prompt_callback, 1, argv, &rettv);
@@ -11130,7 +10661,7 @@ void invoke_prompt_callback(void)
tv_clear(&rettv);
}
-// Return true When the interrupt callback was invoked.
+/// @return true when the interrupt callback was invoked.
bool invoke_prompt_interrupt(void)
{
typval_T rettv;
@@ -11342,7 +10873,7 @@ int typval_compare(typval_T *typ1, typval_T *typ2, exprtype_T type, bool ic)
case EXPR_MATCH:
case EXPR_NOMATCH:
- n1 = pattern_match((char_u *)s2, (char_u *)s1, ic);
+ n1 = pattern_match((char *)s2, (char *)s1, ic);
if (type == EXPR_NOMATCH) {
n1 = !n1;
}
@@ -11383,9 +10914,7 @@ bool var_exists(const char *var)
n = get_var_tv(name, len, &tv, NULL, false, true) == OK;
if (n) {
// Handle d.key, l[idx], f(expr).
- n = handle_subscript(&var, &tv, true, false, (const char_u *)name,
- (const char_u **)&name)
- == OK;
+ n = handle_subscript(&var, &tv, true, false, name, &name) == OK;
if (n) {
tv_clear(&tv);
}
diff --git a/src/nvim/eval.h b/src/nvim/eval.h
index a9ec5d47a6..fa02b1ea0f 100644
--- a/src/nvim/eval.h
+++ b/src/nvim/eval.h
@@ -62,7 +62,7 @@ typedef struct lval_S {
long ll_n2; ///< Second index for list range.
dict_T *ll_dict; ///< The Dictionary or NULL.
dictitem_T *ll_di; ///< The dictitem or NULL.
- char_u *ll_newkey; ///< New key for Dict in allocated memory or NULL.
+ char *ll_newkey; ///< New key for Dict in allocated memory or NULL.
blob_T *ll_blob; ///< The Blob or NULL.
} lval_T;
@@ -164,7 +164,7 @@ typedef enum {
VV_ARGV,
VV_COLLATE,
VV_EXITING,
- // Neovim
+ // Nvim
VV_STDERR,
VV_MSGPACK_TYPES,
VV__NULL_STRING, // String with NULL value. For test purposes only.
@@ -185,8 +185,8 @@ typedef enum {
kMPArray,
kMPMap,
kMPExt,
-#define LAST_MSGPACK_TYPE kMPExt
} MessagePackType;
+#define LAST_MSGPACK_TYPE kMPExt
/// Array mapping values from MessagePackType to corresponding list pointers
extern const list_T *eval_msgpack_type_lists[LAST_MSGPACK_TYPE + 1];
@@ -199,7 +199,6 @@ typedef struct {
hashtab_T sve_hashtab;
} save_v_event_T;
-
/// trans_function_name() flags
typedef enum {
TFN_INT = 1, ///< May use internal function name
@@ -235,8 +234,7 @@ typedef struct {
} timer_T;
/// Type of assert_* check being performed
-typedef enum
-{
+typedef enum {
ASSERT_EQUAL,
ASSERT_NOTEQUAL,
ASSERT_MATCH,
@@ -267,7 +265,7 @@ typedef enum {
kDictListItems, ///< List dictionary contents: [keys, values].
} DictListType;
-typedef int (*ex_unletlock_callback)(lval_T *, char_u *, exarg_T *, int);
+typedef int (*ex_unletlock_callback)(lval_T *, char *, exarg_T *, int);
// Used for checking if local variables or arguments used in a lambda.
extern bool *eval_lavars_used;
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index e445a08227..3db0d27018 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -10,6 +10,7 @@
-- Defaults to BASE_NONE (function cannot be used as a method).
-- func Name of the C function which implements the VimL function. Defaults to
-- `f_{funcname}`.
+-- fast Function can run in |api-fast| events. Defaults to false.
local varargs = function(nr)
return {nr}
@@ -71,6 +72,7 @@ return {
chanclose={args={1, 2}},
chansend={args=2},
char2nr={args={1, 2}, base=1},
+ charcol={args=1, base=1},
charidx={args={2, 3}, base=1},
chdir={args=1, base=1},
cindent={args=1, base=1},
@@ -101,6 +103,10 @@ return {
did_filetype={},
diff_filler={args=1, base=1},
diff_hlID={args=2, base=1},
+ digraph_get={args=1, base=1},
+ digraph_getlist={args={0, 1}, base=1},
+ digraph_set={args=2, base=1},
+ digraph_setlist={args=1, base=1},
empty={args=1, base=1},
environ={},
escape={args=2, base=1},
@@ -144,14 +150,18 @@ return {
getchangelist={args={0, 1}, base=1},
getchar={args={0, 1}},
getcharmod={},
+ getcharpos={args=1, base=1},
getcharsearch={},
getcharstr={args={0, 1}},
+ getcmdcompltype={},
getcmdline={},
getcmdpos={},
+ getcmdscreenpos={},
getcmdtype={},
getcmdwintype={},
getcompletion={args={2, 3}, base=1},
- getcurpos={},
+ getcurpos={args={0, 1}, base=1},
+ getcursorcharpos={args={0, 1}, base=1},
getcwd={args={0, 2}, base=1},
getenv={args=1, base=1},
getfontname={args={0, 1}},
@@ -196,7 +206,7 @@ return {
hlID={args=1, base=1},
hlexists={args=1, base=1},
hostname={},
- iconv={args=3, base=1},
+ iconv={args=3, base=1, fast=true},
indent={args=1, base=1},
index={args={2, 4}, base=1},
input={args={1, 3}, base=1},
@@ -246,6 +256,8 @@ return {
matcharg={args=1, base=1},
matchdelete={args={1, 2}, base=1},
matchend={args={2, 4}, base=1},
+ matchfuzzy={args={2, 3}, base=1},
+ matchfuzzypos={args={2, 3}, base=1},
matchlist={args={2, 4}, base=1},
matchstr={args={2, 4}, base=1},
matchstrpos={args={2,4}, base=1},
@@ -259,7 +271,7 @@ return {
nextnonblank={args=1, base=1},
nr2char={args={1, 2}, base=1},
['or']={args=2, base=1},
- pathshorten={args=1, base=1},
+ pathshorten={args={1, 2}, base=1},
pow={args=2, base=1},
prevnonblank={args=1, base=1},
printf={args=varargs(1), base=2},
@@ -270,12 +282,14 @@ return {
pum_getpos={},
pumvisible={},
py3eval={args=1, base=1},
- pyeval={args=1, base=1},
- pyxeval={args=1, base=1},
+ pyeval={args=1, base=1, func="f_py3eval"},
+ pyxeval={args=1, base=1, func="f_py3eval"},
perleval={args=1, base=1},
+ rand={args={0, 1}, base=1},
range={args={1, 3}, base=1},
readdir={args={1, 2}, base=1},
readfile={args={1, 3}, base=1},
+ reduce={args={2, 3}, base=1},
reg_executing={},
reg_recording={},
reg_recorded={},
@@ -300,19 +314,21 @@ return {
screenpos={args=3, base=1},
screenrow={},
screenstring={args=2, base=1},
- search={args={1, 4}, base=1},
+ search={args={1, 5}, base=1},
searchcount={args={0, 1}, base=1},
searchdecl={args={1, 3}, base=1},
searchpair={args={3, 7}},
searchpairpos={args={3, 7}},
- searchpos={args={1, 4}, base=1},
+ searchpos={args={1, 5}, base=1},
serverlist={},
serverstart={args={0, 1}},
serverstop={args=1},
setbufline={args=3, base=3},
setbufvar={args=3, base=3},
+ setcharpos={args=2, base=2},
setcharsearch={args=1, base=1},
setcmdpos={args=1, base=1},
+ setcursorcharpos={args={1, 3}, base=1},
setenv={args=2, base=2},
setfperm={args=2, base=1},
setline={args=2, base=2},
@@ -348,6 +364,7 @@ return {
spellsuggest={args={1, 3}, base=1},
split={args={1, 3}, base=1},
sqrt={args=1, base=1, func="float_op_wrapper", data="&sqrt"},
+ srand={args={0, 1}, base=1},
stdpath={args=1},
str2float={args=1, base=1},
str2list={args={1, 2}, base=1},
@@ -413,6 +430,8 @@ return {
win_gotoid={args=1, base=1},
win_id2tabwin={args=1, base=1},
win_id2win={args=1, base=1},
+ win_move_separator={args=2, base=1},
+ win_move_statusline={args=2, base=1},
win_screenpos={args=1, base=1},
win_splitmove={args={2, 3}, base=1},
winbufnr={args=1, base=1},
diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c
index 797420c150..7b975ce775 100644
--- a/src/nvim/eval/decode.c
+++ b/src/nvim/eval/decode.c
@@ -290,8 +290,7 @@ typval_T decode_string(const char *const s, const size_t len, const TriState has
return (typval_T) {
.v_type = VAR_STRING,
.v_lock = VAR_UNLOCKED,
- .vval = { .v_string = (char_u *)(
- (s == NULL || s_allocated) ? (char *)s : xmemdupz(s, len)) },
+ .vval = { .v_string = ((s == NULL || s_allocated) ? (char *)s : xmemdupz(s, len)) },
};
}
}
@@ -376,7 +375,7 @@ static inline int parse_json_string(const char *const buf, const size_t buf_len,
"inside string: %.*s"), LENP(p, e));
goto parse_json_string_fail;
}
- const int ch = utf_ptr2char((char_u *)p);
+ const int ch = utf_ptr2char(p);
// All characters above U+007F are encoded using two or more bytes
// and thus cannot possibly be equal to *p. But utf_ptr2char({0xFF,
// 0}) will return 0xFF, even though 0xFF cannot start any UTF-8
@@ -393,7 +392,7 @@ static inline int parse_json_string(const char *const buf, const size_t buf_len,
goto parse_json_string_fail;
}
const size_t ch_len = (size_t)utf_char2len(ch);
- assert(ch_len == (size_t)(ch ? utf_ptr2len((char_u *)p) : 1));
+ assert(ch_len == (size_t)(ch ? utf_ptr2len(p) : 1));
len += ch_len;
p += ch_len;
}
@@ -415,9 +414,9 @@ static inline int parse_json_string(const char *const buf, const size_t buf_len,
bool hasnul = false;
#define PUT_FST_IN_PAIR(fst_in_pair, str_end) \
do { \
- if (fst_in_pair != 0) { \
- str_end += utf_char2bytes(fst_in_pair, (char_u *)str_end); \
- fst_in_pair = 0; \
+ if ((fst_in_pair) != 0) { \
+ (str_end) += utf_char2bytes(fst_in_pair, (str_end)); \
+ (fst_in_pair) = 0; \
} \
} while (0)
for (const char *t = s; t < p; t++) {
@@ -441,15 +440,14 @@ static inline int parse_json_string(const char *const buf, const size_t buf_len,
fst_in_pair = (int)ch;
} else if (SURROGATE_LO_START <= ch && ch <= SURROGATE_LO_END
&& fst_in_pair != 0) {
- const int full_char = (
- (int)(ch - SURROGATE_LO_START)
+ const int full_char = ((int)(ch - SURROGATE_LO_START)
+ ((fst_in_pair - SURROGATE_HI_START) << 10)
+ SURROGATE_FIRST_CHAR);
- str_end += utf_char2bytes(full_char, (char_u *)str_end);
+ str_end += utf_char2bytes(full_char, str_end);
fst_in_pair = 0;
} else {
PUT_FST_IN_PAIR(fst_in_pair, str_end);
- str_end += utf_char2bytes((int)ch, (char_u *)str_end);
+ str_end += utf_char2bytes((int)ch, str_end);
}
break;
}
diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c
index 6f4357421b..090939666d 100644
--- a/src/nvim/eval/encode.c
+++ b/src/nvim/eval/encode.c
@@ -29,8 +29,6 @@
#include "nvim/message.h"
#include "nvim/vim.h" // For _()
-#define utf_char2len(b) ((size_t)utf_char2len(b))
-
const char *const encode_bool_var_names[] = {
[kBoolVarTrue] = "true",
[kBoolVarFalse] = "false",
@@ -69,10 +67,10 @@ int encode_list_write(void *const data, const char *const buf, const size_t len)
line_end = xmemscan(buf, NL, len);
if (line_end != buf) {
const size_t line_length = (size_t)(line_end - buf);
- char *str = (char *)TV_LIST_ITEM_TV(li)->vval.v_string;
+ char *str = TV_LIST_ITEM_TV(li)->vval.v_string;
const size_t li_len = (str == NULL ? 0 : strlen(str));
TV_LIST_ITEM_TV(li)->vval.v_string = xrealloc(str, li_len + line_length + 1);
- str = (char *)TV_LIST_ITEM_TV(li)->vval.v_string + li_len;
+ str = TV_LIST_ITEM_TV(li)->vval.v_string + li_len;
memcpy(str, buf, line_length);
str[line_length] = 0;
memchrsub(str, NUL, NL, line_length);
@@ -132,9 +130,10 @@ static int conv_error(const char *const msg, const MPConvStack *const mpstack,
case kMPConvDict: {
typval_T key_tv = {
.v_type = VAR_STRING,
- .vval = { .v_string = (v.data.d.hi == NULL
- ? v.data.d.dict->dv_hashtab.ht_array
- : (v.data.d.hi - 1))->hi_key },
+ .vval = { .v_string =
+ (char *)(v.data.d.hi ==
+ NULL ? v.data.d.dict->dv_hashtab.ht_array : (v.data.d.hi -
+ 1))->hi_key },
};
char *const key = encode_tv2string(&key_tv, NULL);
vim_snprintf((char *)IObuff, IOSIZE, key_msg, key);
@@ -265,7 +264,7 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s
|| TV_LIST_ITEM_TV(state->li)->vval.v_string != NULL);
for (size_t i = state->offset; i < state->li_length && p < buf_end; i++) {
assert(TV_LIST_ITEM_TV(state->li)->vval.v_string != NULL);
- const char ch = (char)(TV_LIST_ITEM_TV(state->li)->vval.v_string[state->offset++]);
+ const char ch = TV_LIST_ITEM_TV(state->li)->vval.v_string[state->offset++];
*p++ = (char)(ch == (char)NL ? (char)NUL : ch);
}
if (p < buf_end) {
@@ -294,8 +293,8 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s
#define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \
do { \
- const char *const buf_ = (const char *)buf; \
- if (buf == NULL) { \
+ const char *const buf_ = (const char *)(buf); \
+ if ((buf) == NULL) { \
ga_concat(gap, "''"); \
} else { \
const size_t len_ = (len); \
@@ -384,14 +383,14 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s
#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len) \
do { \
- if (len != 0) { \
+ if ((len) != 0) { \
ga_concat(gap, ", "); \
} \
} while (0)
#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len) \
do { \
- if ((ptrdiff_t)len != -1) { \
+ if ((ptrdiff_t)(len) != -1) { \
ga_concat(gap, ", "); \
} \
} while (0)
@@ -453,12 +452,12 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s
size_t backref = 0; \
for (; backref < kv_size(*mpstack); backref++) { \
const MPConvStackVal mpval = kv_A(*mpstack, backref); \
- if (mpval.type == conv_type) { \
- if (conv_type == kMPConvDict) { \
+ if (mpval.type == (conv_type)) { \
+ if ((conv_type) == kMPConvDict) { \
if ((void *)mpval.data.d.dict == (void *)(val)) { \
break; \
} \
- } else if (conv_type == kMPConvList) { \
+ } else if ((conv_type) == kMPConvList) { \
if ((void *)mpval.data.l.list == (void *)(val)) { \
break; \
} \
@@ -488,19 +487,19 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s
size_t backref = 0; \
for (; backref < kv_size(*mpstack); backref++) { \
const MPConvStackVal mpval = kv_A(*mpstack, backref); \
- if (mpval.type == conv_type) { \
- if (conv_type == kMPConvDict) { \
- if ((void *)mpval.data.d.dict == (void *)val) { \
+ if (mpval.type == (conv_type)) { \
+ if ((conv_type) == kMPConvDict) { \
+ if ((void *)mpval.data.d.dict == (void *)(val)) { \
break; \
} \
- } else if (conv_type == kMPConvList) { \
- if ((void *)mpval.data.l.list == (void *)val) { \
+ } else if ((conv_type) == kMPConvList) { \
+ if ((void *)mpval.data.l.list == (void *)(val)) { \
break; \
} \
} \
} \
} \
- if (conv_type == kMPConvDict) { \
+ if ((conv_type) == kMPConvDict) { \
vim_snprintf(ebuf, ARRAY_SIZE(ebuf), "{...@%zu}", backref); \
} else { \
vim_snprintf(ebuf, ARRAY_SIZE(ebuf), "[...@%zu]", backref); \
@@ -610,10 +609,10 @@ static inline int convert_to_json_string(garray_T *const gap, const char *const
// This is done to make resulting values displayable on screen also not from
// Neovim.
#define ENCODE_RAW(ch) \
- (ch >= 0x20 && utf_printable(ch))
+ ((ch) >= 0x20 && utf_printable(ch))
for (size_t i = 0; i < utf_len;) {
- const int ch = utf_ptr2char((char_u *)utf_buf + i);
- const size_t shift = (ch == 0 ? 1 : ((size_t)utf_ptr2len((char_u *)utf_buf + i)));
+ const int ch = utf_ptr2char(utf_buf + i);
+ const size_t shift = (ch == 0 ? 1 : ((size_t)utf_ptr2len(utf_buf + i)));
assert(shift > 0);
i += shift;
switch (ch) {
@@ -652,11 +651,11 @@ static inline int convert_to_json_string(garray_T *const gap, const char *const
ga_append(gap, '"');
ga_grow(gap, (int)str_len);
for (size_t i = 0; i < utf_len;) {
- const int ch = utf_ptr2char((char_u *)utf_buf + i);
- const size_t shift = (ch == 0? 1: utf_char2len(ch));
+ const int ch = utf_ptr2char(utf_buf + i);
+ const size_t shift = (ch == 0 ? 1 : ((size_t)utf_char2len(ch)));
assert(shift > 0);
// Is false on invalid unicode, but this should already be handled.
- assert(ch == 0 || shift == ((size_t)utf_ptr2len((char_u *)utf_buf + i)));
+ assert(ch == 0 || shift == ((size_t)utf_ptr2len(utf_buf + i)));
switch (ch) {
case BS:
case TAB:
@@ -789,7 +788,7 @@ bool encode_check_json_key(const typval_T *const tv)
#undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK
#define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(label, key) \
do { \
- if (!encode_check_json_key(&key)) { \
+ if (!encode_check_json_key(&(key))) { \
emsg(_("E474: Invalid key in special dictionary")); \
goto label; \
} \
@@ -871,7 +870,7 @@ char *encode_tv2echo(typval_T *tv, size_t *len)
ga_init(&ga, (int)sizeof(char), 80);
if (tv->v_type == VAR_STRING || tv->v_type == VAR_FUNC) {
if (tv->vval.v_string != NULL) {
- ga_concat(&ga, (char *)tv->vval.v_string);
+ ga_concat(&ga, tv->vval.v_string);
}
} else {
const int eve_ret = encode_vim_to_echo(&ga, tv, N_(":echo argument"));
@@ -912,7 +911,7 @@ char *encode_tv2json(typval_T *tv, size_t *len)
#define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \
do { \
- if (buf == NULL) { \
+ if ((buf) == NULL) { \
msgpack_pack_bin(packer, 0); \
} else { \
const size_t len_ = (len); \
@@ -923,7 +922,7 @@ char *encode_tv2json(typval_T *tv, size_t *len)
#define TYPVAL_ENCODE_CONV_STR_STRING(tv, buf, len) \
do { \
- if (buf == NULL) { \
+ if ((buf) == NULL) { \
msgpack_pack_str(packer, 0); \
} else { \
const size_t len_ = (len); \
@@ -934,11 +933,11 @@ char *encode_tv2json(typval_T *tv, size_t *len)
#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type) \
do { \
- if (buf == NULL) { \
- msgpack_pack_ext(packer, 0, (int8_t)type); \
+ if ((buf) == NULL) { \
+ msgpack_pack_ext(packer, 0, (int8_t)(type)); \
} else { \
const size_t len_ = (len); \
- msgpack_pack_ext(packer, len_, (int8_t)type); \
+ msgpack_pack_ext(packer, len_, (int8_t)(type)); \
msgpack_pack_ext_body(packer, buf, len_); \
} \
} while (0)
diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c
index ed4f36f4c7..3e66150180 100644
--- a/src/nvim/eval/executor.c
+++ b/src/nvim/eval/executor.c
@@ -63,7 +63,7 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, const char *cons
if (tv2->v_type == VAR_LIST) {
break;
}
- if (vim_strchr((char_u *)"+-*/%", *op) != NULL) {
+ if (vim_strchr("+-*/%", *op) != NULL) {
// nr += nr or nr -= nr, nr *= nr, nr /= nr, nr %= nr
varnumber_T n = tv_get_number(tv1);
if (tv2->v_type == VAR_FLOAT) {
@@ -114,7 +114,7 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, const char *cons
numbuf));
tv_clear(tv1);
tv1->v_type = VAR_STRING;
- tv1->vval.v_string = (char_u *)s;
+ tv1->vval.v_string = s;
}
return OK;
case VAR_FLOAT: {
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 875655d0b3..7bed21e99b 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -16,27 +16,31 @@
#include "nvim/context.h"
#include "nvim/cursor.h"
#include "nvim/diff.h"
+#include "nvim/digraph.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/eval/decode.h"
#include "nvim/eval/encode.h"
#include "nvim/eval/executor.h"
#include "nvim/eval/funcs.h"
+#include "nvim/eval/typval.h"
#include "nvim/eval/userfunc.h"
-#include "nvim/ex_cmds2.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_getln.h"
#include "nvim/file_search.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
#include "nvim/globals.h"
+#include "nvim/highlight_group.h"
#include "nvim/if_cscope.h"
#include "nvim/indent.h"
#include "nvim/indent_c.h"
#include "nvim/input.h"
#include "nvim/lua/executor.h"
#include "nvim/macros.h"
+#include "nvim/mapping.h"
#include "nvim/mark.h"
+#include "nvim/match.h"
#include "nvim/math.h"
#include "nvim/memline.h"
#include "nvim/mouse.h"
@@ -61,11 +65,12 @@
#include "nvim/state.h"
#include "nvim/syntax.h"
#include "nvim/tag.h"
+#include "nvim/testing.h"
#include "nvim/ui.h"
#include "nvim/undo.h"
#include "nvim/version.h"
#include "nvim/vim.h"
-
+#include "nvim/window.h"
/// Describe data to return from find_some_match()
typedef enum {
@@ -76,9 +81,6 @@ typedef enum {
kSomeMatchStrPos, ///< Data for matchstrpos().
} SomeMatchType;
-KHASH_MAP_INIT_STR(functions, VimLFuncDef)
-
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval/funcs.c.generated.h"
@@ -96,10 +98,9 @@ PRAGMA_DIAG_POP
PRAGMA_DIAG_POP
#endif
-
-static char *e_listarg = N_("E686: Argument of %s must be a List");
static char *e_listblobarg = N_("E899: Argument of %s must be a List or Blob");
static char *e_invalwindow = N_("E957: Invalid window number");
+static char *e_reduceempty = N_("E998: Reduce of an empty %s with no initial value");
/// Dummy va_list for passing to vim_snprintf
///
@@ -109,10 +110,9 @@ static char *e_invalwindow = N_("E957: Invalid window number");
/// - using va_start() to initialize it gives "function with fixed args" error
static va_list dummy_ap;
-
/// Function given to ExpandGeneric() to obtain the list of internal
/// or user defined function names.
-char_u *get_function_name(expand_T *xp, int idx)
+char *get_function_name(expand_T *xp, int idx)
{
static int intidx = -1;
char_u *name;
@@ -121,24 +121,20 @@ char_u *get_function_name(expand_T *xp, int idx)
intidx = -1;
}
if (intidx < 0) {
- name = get_user_func_name(xp, idx);
+ name = (char_u *)get_user_func_name(xp, idx);
if (name != NULL) {
if (*name != NUL && *name != '<'
&& STRNCMP("g:", xp->xp_pattern, 2) == 0) {
- return cat_prefix_varname('g', name);
+ return cat_prefix_varname('g', (char *)name);
}
- return name;
+ return (char *)name;
}
}
- while ((size_t)++intidx < ARRAY_SIZE(functions)
- && functions[intidx].name[0] == '\0') {
- }
- if ((size_t)intidx >= ARRAY_SIZE(functions)) {
+ const char *const key = functions[++intidx].name;
+ if (!key) {
return NULL;
}
-
- const char *const key = functions[intidx].name;
const size_t key_len = strlen(key);
memcpy(IObuff, key, key_len);
IObuff[key_len] = '(';
@@ -148,12 +144,12 @@ char_u *get_function_name(expand_T *xp, int idx)
} else {
IObuff[key_len + 1] = NUL;
}
- return IObuff;
+ return (char *)IObuff;
}
/// Function given to ExpandGeneric() to obtain the list of internal or
/// user defined variable or function names.
-char_u *get_expr_name(expand_T *xp, int idx)
+char *get_expr_name(expand_T *xp, int idx)
{
static int intidx = -1;
char_u *name;
@@ -162,9 +158,9 @@ char_u *get_expr_name(expand_T *xp, int idx)
intidx = -1;
}
if (intidx < 0) {
- name = get_function_name(xp, idx);
+ name = (char_u *)get_function_name(xp, idx);
if (name != NULL) {
- return name;
+ return (char *)name;
}
}
return get_user_var_name(xp, ++intidx);
@@ -174,19 +170,20 @@ char_u *get_expr_name(expand_T *xp, int idx)
///
/// @param[in] name Name of the function.
///
-/// Returns pointer to the function definition or NULL if not found.
-const VimLFuncDef *find_internal_func(const char *const name)
+/// @return pointer to the function definition or NULL if not found.
+const EvalFuncDef *find_internal_func(const char *const name)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE FUNC_ATTR_NONNULL_ALL
{
size_t len = strlen(name);
- return find_internal_func_gperf(name, len);
+ int index = find_internal_func_hash(name, len);
+ return index >= 0 ? &functions[index] : NULL;
}
int call_internal_func(const char_u *const fname, const int argcount, typval_T *const argvars,
typval_T *const rettv)
FUNC_ATTR_NONNULL_ALL
{
- const VimLFuncDef *const fdef = find_internal_func((const char *)fname);
+ const EvalFuncDef *const fdef = find_internal_func((const char *)fname);
if (fdef == NULL) {
return ERROR_UNKNOWN;
} else if (argcount < fdef->min_argc) {
@@ -204,7 +201,7 @@ int call_internal_method(const char_u *const fname, const int argcount, typval_T
typval_T *const rettv, typval_T *const basetv)
FUNC_ATTR_NONNULL_ALL
{
- const VimLFuncDef *const fdef = find_internal_func((const char *)fname);
+ const EvalFuncDef *const fdef = find_internal_func((const char *)fname);
if (fdef == NULL) {
return ERROR_UNKNOWN;
} else if (fdef->base_arg == BASE_NONE) {
@@ -228,9 +225,7 @@ int call_internal_method(const char_u *const fname, const int argcount, typval_T
return ERROR_NONE;
}
-/*
- * Return TRUE for a non-zero Number and a non-empty String.
- */
+/// @return TRUE for a non-zero Number and a non-empty String.
static int non_zero_arg(typval_T *argvars)
{
return ((argvars[0].v_type == VAR_NUMBER
@@ -242,11 +237,11 @@ static int non_zero_arg(typval_T *argvars)
&& *argvars[0].vval.v_string != NUL));
}
-// Apply a floating point C function on a typval with one float_T.
-//
-// Some versions of glibc on i386 have an optimization that makes it harder to
-// call math functions indirectly from inside an inlined function, causing
-// compile-time errors. Avoid `inline` in that case. #3072
+/// Apply a floating point C function on a typval with one float_T.
+///
+/// Some versions of glibc on i386 have an optimization that makes it harder to
+/// call math functions indirectly from inside an inlined function, causing
+/// compile-time errors. Avoid `inline` in that case. #3072
static void float_op_wrapper(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
float_T f;
@@ -292,9 +287,7 @@ end:
api_clear_error(&err);
}
-/*
- * "abs(expr)" function
- */
+/// "abs(expr)" function
static void f_abs(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
if (argvars[0].v_type == VAR_FLOAT) {
@@ -314,9 +307,7 @@ static void f_abs(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "add(list, item)" function
- */
+/// "add(list, item)" function
static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = 1; // Default: failed.
@@ -344,16 +335,13 @@ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "and(expr, expr)" function
- */
+/// "and(expr, expr)" function
static void f_and(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL)
& tv_get_number_chk(&argvars[1], NULL);
}
-
/// "api_info()" function
static void f_api_info(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
@@ -362,7 +350,7 @@ static void f_api_info(typval_T *argvars, typval_T *rettv, FunPtr fptr)
api_free_dictionary(metadata);
}
-// "append(lnum, string/list)" function
+/// "append(lnum, string/list)" function
static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
const linenr_T lnum = tv_get_lnum(&argvars[0]);
@@ -370,7 +358,7 @@ static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr)
set_buffer_lines(curbuf, lnum, true, &argvars[1], rettv);
}
-// "appendbufline(buf, lnum, string/list)" function
+/// "appendbufline(buf, lnum, string/list)" function
static void f_appendbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
buf_T *const buf = tv_get_buf(&argvars[0], false);
@@ -402,9 +390,7 @@ static void f_argc(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "argidx()" function
- */
+/// "argidx()" function
static void f_argidx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = curwin->w_arg_idx;
@@ -420,9 +406,7 @@ static void f_arglistid(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "argv(nr)" function
- */
+/// "argv(nr)" function
static void f_argv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
aentry_T *arglist = NULL;
@@ -448,7 +432,7 @@ static void f_argv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_string = NULL;
int idx = tv_get_number_chk(&argvars[0], NULL);
if (arglist != NULL && idx >= 0 && idx < argcount) {
- rettv->vval.v_string = (char_u *)xstrdup((const char *)alist_name(&arglist[idx]));
+ rettv->vval.v_string = xstrdup((const char *)alist_name(&arglist[idx]));
} else if (idx == -1) {
get_arglist_as_rettv(arglist, argcount, rettv);
}
@@ -457,93 +441,7 @@ static void f_argv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-// "assert_beeps(cmd [, error])" function
-static void f_assert_beeps(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- rettv->vval.v_number = assert_beeps(argvars, false);
-}
-
-// "assert_nobeep(cmd [, error])" function
-static void f_assert_nobeep(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- rettv->vval.v_number = assert_beeps(argvars, true);
-}
-
-// "assert_equal(expected, actual[, msg])" function
-static void f_assert_equal(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- rettv->vval.v_number = assert_equal_common(argvars, ASSERT_EQUAL);
-}
-
-// "assert_equalfile(fname-one, fname-two[, msg])" function
-static void f_assert_equalfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- rettv->vval.v_number = assert_equalfile(argvars);
-}
-
-// "assert_notequal(expected, actual[, msg])" function
-static void f_assert_notequal(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- rettv->vval.v_number = assert_equal_common(argvars, ASSERT_NOTEQUAL);
-}
-
-/// "assert_report(msg)
-static void f_assert_report(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- garray_T ga;
-
- prepare_assert_error(&ga);
- ga_concat(&ga, tv_get_string(&argvars[0]));
- assert_error(&ga);
- ga_clear(&ga);
- rettv->vval.v_number = 1;
-}
-
-/// "assert_exception(string[, msg])" function
-static void f_assert_exception(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- rettv->vval.v_number = assert_exception(argvars);
-}
-
-/// "assert_fails(cmd [, error [, msg]])" function
-static void f_assert_fails(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- rettv->vval.v_number = assert_fails(argvars);
-}
-
-// "assert_false(actual[, msg])" function
-static void f_assert_false(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- rettv->vval.v_number = assert_bool(argvars, false);
-}
-
-/// "assert_inrange(lower, upper[, msg])" function
-static void f_assert_inrange(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- rettv->vval.v_number = assert_inrange(argvars);
-}
-
-/// "assert_match(pattern, actual[, msg])" function
-static void f_assert_match(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- rettv->vval.v_number = assert_match_common(argvars, ASSERT_MATCH);
-}
-
-/// "assert_notmatch(pattern, actual[, msg])" function
-static void f_assert_notmatch(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- rettv->vval.v_number = assert_match_common(argvars, ASSERT_NOTMATCH);
-}
-
-// "assert_true(actual[, msg])" function
-static void f_assert_true(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- rettv->vval.v_number = assert_bool(argvars, true);
-}
-
-/*
- * "atan2()" function
- */
+/// "atan2()" function
static void f_atan2(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
float_T fx;
@@ -557,27 +455,20 @@ static void f_atan2(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "browse(save, title, initdir, default)" function
- */
+/// "browse(save, title, initdir, default)" function
static void f_browse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_string = NULL;
rettv->v_type = VAR_STRING;
}
-/*
- * "browsedir(title, initdir)" function
- */
+/// "browsedir(title, initdir)" function
static void f_browsedir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
f_browse(argvars, rettv, NULL);
}
-
-/*
- * Find a buffer by number or exact name.
- */
+/// Find a buffer by number or exact name.
static buf_T *find_buffer(typval_T *avar)
{
buf_T *buf = NULL;
@@ -591,9 +482,7 @@ static buf_T *find_buffer(typval_T *avar)
* buffer, these don't use the full path. */
FOR_ALL_BUFFERS(bp) {
if (bp->b_fname != NULL
- && (path_with_url((char *)bp->b_fname)
- || bt_nofile(bp)
- )
+ && (path_with_url(bp->b_fname) || bt_nofilename(bp))
&& STRCMP(bp->b_fname, avar->vval.v_string) == 0) {
buf = bp;
break;
@@ -604,25 +493,21 @@ static buf_T *find_buffer(typval_T *avar)
return buf;
}
-// "bufadd(expr)" function
+/// "bufadd(expr)" function
static void f_bufadd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
char_u *name = (char_u *)tv_get_string(&argvars[0]);
- rettv->vval.v_number = buflist_add(*name == NUL ? NULL : name, 0);
+ rettv->vval.v_number = buflist_add(*name == NUL ? NULL : (char *)name, 0);
}
-/*
- * "bufexists(expr)" function
- */
+/// "bufexists(expr)" function
static void f_bufexists(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = (find_buffer(&argvars[0]) != NULL);
}
-/*
- * "buflisted(expr)" function
- */
+/// "buflisted(expr)" function
static void f_buflisted(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
buf_T *buf;
@@ -631,7 +516,7 @@ static void f_buflisted(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = (buf != NULL && buf->b_p_bl);
}
-// "bufload(expr)" function
+/// "bufload(expr)" function
static void f_bufload(typval_T *argvars, typval_T *unused, FunPtr fptr)
{
buf_T *buf = get_buf_arg(&argvars[0]);
@@ -646,9 +531,7 @@ static void f_bufload(typval_T *argvars, typval_T *unused, FunPtr fptr)
}
}
-/*
- * "bufloaded(expr)" function
- */
+/// "bufloaded(expr)" function
static void f_bufloaded(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
buf_T *buf;
@@ -657,9 +540,7 @@ static void f_bufloaded(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = (buf != NULL && buf->b_ml.ml_mfp != NULL);
}
-/*
- * "bufname(expr)" function
- */
+/// "bufname(expr)" function
static void f_bufname(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
const buf_T *buf;
@@ -671,13 +552,11 @@ static void f_bufname(typval_T *argvars, typval_T *rettv, FunPtr fptr)
buf = tv_get_buf_from_arg(&argvars[0]);
}
if (buf != NULL && buf->b_fname != NULL) {
- rettv->vval.v_string = (char_u *)xstrdup((char *)buf->b_fname);
+ rettv->vval.v_string = xstrdup(buf->b_fname);
}
}
-/*
- * "bufnr(expr)" function
- */
+/// "bufnr(expr)" function
static void f_bufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
const buf_T *buf;
@@ -707,7 +586,7 @@ static void f_bufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
&& tv_get_number_chk(&argvars[1], &error) != 0
&& !error
&& (name = tv_get_string_chk(&argvars[0])) != NULL) {
- buf = buflist_new((char_u *)name, NULL, 1, 0);
+ buf = buflist_new((char *)name, NULL, 1, 0);
}
if (buf != NULL) {
@@ -749,14 +628,12 @@ static void f_bufwinnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
buf_win_common(argvars, rettv, true);
}
-/*
- * Get buffer by number or pattern.
- */
+/// Get buffer by number or pattern.
buf_T *tv_get_buf(typval_T *tv, int curtab_only)
{
- char_u *name = tv->vval.v_string;
+ char_u *name = (char_u *)tv->vval.v_string;
int save_magic;
- char_u *save_cpo;
+ char *save_cpo;
buf_T *buf;
if (tv->v_type == VAR_NUMBER) {
@@ -776,9 +653,9 @@ buf_T *tv_get_buf(typval_T *tv, int curtab_only)
save_magic = p_magic;
p_magic = TRUE;
save_cpo = p_cpo;
- p_cpo = (char_u *)"";
+ p_cpo = "";
- buf = buflist_findnr(buflist_findpat(name, name + STRLEN(name),
+ buf = buflist_findnr(buflist_findpat((char *)name, (char *)name + STRLEN(name),
true, false, curtab_only));
p_magic = save_magic;
@@ -819,9 +696,7 @@ buf_T *get_buf_arg(typval_T *arg)
return buf;
}
-/*
- * "byte2line(byte)" function
- */
+/// "byte2line(byte)" function
static void f_byte2line(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
long boff = tv_get_number(&argvars[0]) - 1;
@@ -848,25 +723,21 @@ static void byteidx(typval_T *argvars, typval_T *rettv, int comp)
return;
}
if (comp) {
- t += utf_ptr2len((const char_u *)t);
+ t += utf_ptr2len(t);
} else {
- t += utfc_ptr2len((const char_u *)t);
+ t += utfc_ptr2len(t);
}
}
rettv->vval.v_number = (varnumber_T)(t - str);
}
-/*
- * "byteidx()" function
- */
+/// "byteidx()" function
static void f_byteidx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
byteidx(argvars, rettv, FALSE);
}
-/*
- * "byteidxcomp()" function
- */
+/// "byteidxcomp()" function
static void f_byteidxcomp(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
byteidx(argvars, rettv, TRUE);
@@ -888,11 +759,12 @@ static void f_call(typval_T *argvars, typval_T *rettv, FunPtr fptr)
partial_T *partial = NULL;
dict_T *selfdict = NULL;
if (argvars[0].v_type == VAR_FUNC) {
- func = argvars[0].vval.v_string;
+ func = (char_u *)argvars[0].vval.v_string;
} else if (argvars[0].v_type == VAR_PARTIAL) {
partial = argvars[0].vval.v_partial;
- func = partial_name(partial);
+ func = (char_u *)partial_name(partial);
} else if (nlua_is_table_from_lua(&argvars[0])) {
+ // TODO(tjdevries): UnifiedCallback
func = nlua_register_table_as_callable(&argvars[0]);
owned = true;
} else {
@@ -906,6 +778,9 @@ static void f_call(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (argvars[2].v_type != VAR_UNKNOWN) {
if (argvars[2].v_type != VAR_DICT) {
emsg(_(e_dictreq));
+ if (owned) {
+ func_unref(func);
+ }
return;
}
selfdict = argvars[2].vval.v_dict;
@@ -917,15 +792,13 @@ static void f_call(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "changenr()" function
- */
+/// "changenr()" function
static void f_changenr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = curbuf->b_u_seq_cur;
}
-// "chanclose(id[, stream])" function
+/// "chanclose(id[, stream])" function
static void f_chanclose(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->v_type = VAR_NUMBER;
@@ -943,7 +816,7 @@ static void f_chanclose(typval_T *argvars, typval_T *rettv, FunPtr fptr)
ChannelPart part = kChannelPartAll;
if (argvars[1].v_type == VAR_STRING) {
- char *stream = (char *)argvars[1].vval.v_string;
+ char *stream = argvars[1].vval.v_string;
if (!strcmp(stream, "stdin")) {
part = kChannelPartStdin;
} else if (!strcmp(stream, "stdout")) {
@@ -964,7 +837,7 @@ static void f_chanclose(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-// "chansend(id, data)" function
+/// "chansend(id, data)" function
static void f_chansend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->v_type = VAR_NUMBER;
@@ -1005,9 +878,7 @@ static void f_chansend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "char2nr(string)" function
- */
+/// "char2nr(string)" function
static void f_char2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
if (argvars[1].v_type != VAR_UNKNOWN) {
@@ -1016,10 +887,54 @@ static void f_char2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
- rettv->vval.v_number = utf_ptr2char((const char_u *)tv_get_string(&argvars[0]));
+ rettv->vval.v_number = utf_ptr2char(tv_get_string(&argvars[0]));
}
-// "charidx()" function
+/// Get the current cursor column and store it in 'rettv'.
+///
+/// @return the character index of the column if 'charcol' is true,
+/// otherwise the byte index of the column.
+static void get_col(typval_T *argvars, typval_T *rettv, bool charcol)
+{
+ colnr_T col = 0;
+ pos_T *fp;
+ int fnum = curbuf->b_fnum;
+
+ fp = var2fpos(&argvars[0], false, &fnum, charcol);
+ if (fp != NULL && fnum == curbuf->b_fnum) {
+ if (fp->col == MAXCOL) {
+ // '> can be MAXCOL, get the length of the line then
+ if (fp->lnum <= curbuf->b_ml.ml_line_count) {
+ col = (colnr_T)STRLEN(ml_get(fp->lnum)) + 1;
+ } else {
+ col = MAXCOL;
+ }
+ } else {
+ col = fp->col + 1;
+ // col(".") when the cursor is on the NUL at the end of the line
+ // because of "coladd" can be seen as an extra column.
+ if (virtual_active() && fp == &curwin->w_cursor) {
+ char_u *p = get_cursor_pos_ptr();
+ if (curwin->w_cursor.coladd >=
+ (colnr_T)win_chartabsize(curwin, p, curwin->w_virtcol - curwin->w_cursor.coladd)) {
+ int l;
+ if (*p != NUL && p[(l = utfc_ptr2len((char *)p))] == NUL) {
+ col += l;
+ }
+ }
+ }
+ }
+ }
+ rettv->vval.v_number = col;
+}
+
+/// "charcol()" function
+static void f_charcol(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ get_col(argvars, rettv, true);
+}
+
+/// "charidx()" function
static void f_charidx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = -1;
@@ -1046,7 +961,7 @@ static void f_charidx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
- int (*ptr2len)(const char_u *);
+ int (*ptr2len)(const char *);
if (countcc) {
ptr2len = utf_ptr2len;
} else {
@@ -1059,13 +974,13 @@ static void f_charidx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (*p == NUL) {
return;
}
- p += ptr2len((const char_u *)p);
+ p += ptr2len(p);
}
rettv->vval.v_number = len > 0 ? len - 1 : 0;
}
-// "chdir(dir)" function
+/// "chdir(dir)" function
static void f_chdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
char_u *cwd;
@@ -1086,7 +1001,7 @@ static void f_chdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
#ifdef BACKSLASH_IN_FILENAME
slash_adjust(cwd);
#endif
- rettv->vval.v_string = vim_strsave(cwd);
+ rettv->vval.v_string = (char *)vim_strsave(cwd);
}
xfree(cwd);
@@ -1102,9 +1017,7 @@ static void f_chdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "cindent(lnum)" function
- */
+/// "cindent(lnum)" function
static void f_cindent(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
pos_T pos;
@@ -1121,7 +1034,7 @@ static void f_cindent(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-static win_T *get_optional_window(typval_T *argvars, int idx)
+win_T *get_optional_window(typval_T *argvars, int idx)
{
win_T *win = curwin;
@@ -1135,65 +1048,16 @@ static win_T *get_optional_window(typval_T *argvars, int idx)
return win;
}
-/*
- * "clearmatches()" function
- */
-static void f_clearmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- win_T *win = get_optional_window(argvars, 0);
-
- if (win != NULL) {
- clear_matches(win);
- }
-}
-
-/*
- * "col(string)" function
- */
+/// "col(string)" function
static void f_col(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- colnr_T col = 0;
- pos_T *fp;
- int fnum = curbuf->b_fnum;
-
- fp = var2fpos(&argvars[0], FALSE, &fnum);
- if (fp != NULL && fnum == curbuf->b_fnum) {
- if (fp->col == MAXCOL) {
- // '> can be MAXCOL, get the length of the line then
- if (fp->lnum <= curbuf->b_ml.ml_line_count) {
- col = (colnr_T)STRLEN(ml_get(fp->lnum)) + 1;
- } else {
- col = MAXCOL;
- }
- } else {
- col = fp->col + 1;
- // col(".") when the cursor is on the NUL at the end of the line
- // because of "coladd" can be seen as an extra column.
- if (virtual_active() && fp == &curwin->w_cursor) {
- char_u *p = get_cursor_pos_ptr();
-
- if (curwin->w_cursor.coladd
- >= (colnr_T)win_chartabsize(curwin, p,
- (curwin->w_virtcol
- - curwin->w_cursor.coladd))) {
- int l;
-
- if (*p != NUL && p[(l = utfc_ptr2len(p))] == NUL) {
- col += l;
- }
- }
- }
- }
- }
- rettv->vval.v_number = col;
+ get_col(argvars, rettv, false);
}
-/*
- * "complete()" function
- */
+/// "complete()" function
static void f_complete(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- if ((State & INSERT) == 0) {
+ if ((State & MODE_INSERT) == 0) {
emsg(_("E785: complete() can only be used in Insert mode"));
return;
}
@@ -1206,28 +1070,21 @@ static void f_complete(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (argvars[1].v_type != VAR_LIST) {
emsg(_(e_invarg));
- return;
- }
-
- const colnr_T startcol = tv_get_number_chk(&argvars[0], NULL);
- if (startcol <= 0) {
- return;
+ } else {
+ const colnr_T startcol = tv_get_number_chk(&argvars[0], NULL);
+ if (startcol > 0) {
+ set_completion(startcol - 1, argvars[1].vval.v_list);
+ }
}
-
- set_completion(startcol - 1, argvars[1].vval.v_list);
}
-/*
- * "complete_add()" function
- */
+/// "complete_add()" function
static void f_complete_add(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = ins_compl_add_tv(&argvars[0], 0, false);
}
-/*
- * "complete_check()" function
- */
+/// "complete_check()" function
static void f_complete_check(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int saved = RedrawingDisabled;
@@ -1238,7 +1095,7 @@ static void f_complete_check(typval_T *argvars, typval_T *rettv, FunPtr fptr)
RedrawingDisabled = saved;
}
-// "complete_info()" function
+/// "complete_info()" function
static void f_complete_info(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
tv_dict_alloc_ret(rettv);
@@ -1255,9 +1112,7 @@ static void f_complete_info(typval_T *argvars, typval_T *rettv, FunPtr fptr)
get_complete_info(what_list, rettv->vval.v_dict);
}
-/*
- * "confirm(message, buttons[, default [, type]])" function
- */
+/// "confirm(message, buttons[, default [, type]])" function
static void f_confirm(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
char buf[NUMBUFLEN];
@@ -1312,17 +1167,13 @@ static void f_confirm(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "copy()" function
- */
+/// "copy()" function
static void f_copy(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
var_item_copy(NULL, &argvars[0], rettv, false, 0);
}
-/*
- * "count()" function
- */
+/// "count()" function
static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
long n = 0;
@@ -1335,7 +1186,7 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (argvars[0].v_type == VAR_STRING) {
const char_u *expr = (char_u *)tv_get_string_chk(&argvars[1]);
- const char_u *p = argvars[0].vval.v_string;
+ const char_u *p = (char_u *)argvars[0].vval.v_string;
if (!error && expr != NULL && *expr != NUL && p != NULL) {
if (ic) {
@@ -1413,11 +1264,9 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = n;
}
-/*
- * "cscope_connection([{num} , {dbpath} [, {prepend}]])" function
- *
- * Checks the existence of a cscope connection.
- */
+/// "cscope_connection([{num} , {dbpath} [, {prepend}]])" function
+///
+/// Checks the existence of a cscope connection.
static void f_cscope_connection(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int num = 0;
@@ -1548,24 +1397,21 @@ static void f_ctxsize(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = ctx_size();
}
-/// "cursor(lnum, col)" function, or
-/// "cursor(list)"
-///
-/// Moves the cursor to the specified line and column.
-///
-/// @returns 0 when the position could be set, -1 otherwise.
-static void f_cursor(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+/// Set the cursor position.
+/// If 'charcol' is true, then use the column number as a character offset.
+/// Otherwise use the column number as a byte offset.
+static void set_cursorpos(typval_T *argvars, typval_T *rettv, bool charcol)
{
long line, col;
long coladd = 0;
bool set_curswant = true;
rettv->vval.v_number = -1;
- if (argvars[1].v_type == VAR_UNKNOWN) {
+ if (argvars[0].v_type == VAR_LIST) {
pos_T pos;
colnr_T curswant = -1;
- if (list2fpos(argvars, &pos, NULL, &curswant) == FAIL) {
+ if (list2fpos(argvars, &pos, NULL, &curswant, charcol) == FAIL) {
emsg(_(e_invarg));
return;
}
@@ -1577,16 +1423,22 @@ static void f_cursor(typval_T *argvars, typval_T *rettv, FunPtr fptr)
curwin->w_curswant = curswant - 1;
set_curswant = false;
}
- } else {
+ } else if ((argvars[0].v_type == VAR_NUMBER || argvars[0].v_type == VAR_STRING)
+ && (argvars[1].v_type == VAR_NUMBER || argvars[1].v_type == VAR_STRING)) {
line = tv_get_lnum(argvars);
col = (long)tv_get_number_chk(&argvars[1], NULL);
+ if (charcol) {
+ col = buf_charidx_to_byteidx(curbuf, line, col) + 1;
+ }
if (argvars[2].v_type != VAR_UNKNOWN) {
coladd = (long)tv_get_number_chk(&argvars[2], NULL);
}
+ } else {
+ emsg(_(e_invarg));
+ return;
}
- if (line < 0 || col < 0
- || coladd < 0) {
- return; // type error; errmsg already given
+ if (line < 0 || col < 0 || coladd < 0) {
+ return; // type error; errmsg already given
}
if (line > 0) {
curwin->w_cursor.lnum = line;
@@ -1605,7 +1457,18 @@ static void f_cursor(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = 0;
}
-// "debugbreak()" function
+/// "cursor(lnum, col)" function, or
+/// "cursor(list)"
+///
+/// Moves the cursor to the specified line and column.
+///
+/// @return 0 when the position could be set, -1 otherwise.
+static void f_cursor(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ set_cursorpos(argvars, rettv, false);
+}
+
+/// "debugbreak()" function
static void f_debugbreak(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int pid;
@@ -1629,7 +1492,7 @@ static void f_debugbreak(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-// "deepcopy()" function
+/// "deepcopy()" function
static void f_deepcopy(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int noref = 0;
@@ -1646,7 +1509,7 @@ static void f_deepcopy(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-// "delete()" function
+/// "delete()" function
static void f_delete(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = -1;
@@ -1682,7 +1545,7 @@ static void f_delete(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-// dictwatcheradd(dict, key, funcref) function
+/// dictwatcheradd(dict, key, funcref) function
static void f_dictwatcheradd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
if (check_secure()) {
@@ -1720,7 +1583,7 @@ static void f_dictwatcheradd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
callback);
}
-// dictwatcherdel(dict, key, funcref) function
+/// dictwatcherdel(dict, key, funcref) function
static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
if (check_secure()) {
@@ -1768,6 +1631,7 @@ static void f_deletebufline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
const bool is_curbuf = buf == curbuf;
+ const bool save_VIsual_active = VIsual_active;
const linenr_T first = tv_get_lnum_buf(&argvars[1], buf);
if (argvars[2].v_type != VAR_UNKNOWN) {
@@ -1783,6 +1647,7 @@ static void f_deletebufline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
if (!is_curbuf) {
+ VIsual_active = false;
curbuf_save = curbuf;
curwin_save = curwin;
curbuf = buf;
@@ -1826,28 +1691,23 @@ static void f_deletebufline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (!is_curbuf) {
curbuf = curbuf_save;
curwin = curwin_save;
+ VIsual_active = save_VIsual_active;
}
}
-/*
- * "did_filetype()" function
- */
+/// "did_filetype()" function
static void f_did_filetype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = did_filetype;
}
-/*
- * "diff_filler()" function
- */
+/// "diff_filler()" function
static void f_diff_filler(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = MAX(0, diff_check(curwin, tv_get_lnum(argvars)));
}
-/*
- * "diff_hlID()" function
- */
+/// "diff_hlID()" function
static void f_diff_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
linenr_T lnum = tv_get_lnum(argvars);
@@ -1899,9 +1759,7 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = hlID == (hlf_T)0 ? 0 : (int)(hlID + 1);
}
-/*
- * "empty({expr})" function
- */
+/// "empty({expr})" function
static void f_empty(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
bool n = true;
@@ -1998,15 +1856,14 @@ static void f_environ(typval_T *argvars, typval_T *rettv, FunPtr fptr)
os_free_fullenv(env);
}
-/*
- * "escape({string}, {chars})" function
- */
+/// "escape({string}, {chars})" function
static void f_escape(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
char buf[NUMBUFLEN];
- rettv->vval.v_string = vim_strsave_escaped((const char_u *)tv_get_string(&argvars[0]),
- (const char_u *)tv_get_string_buf(&argvars[1], buf));
+ rettv->vval.v_string = (char *)vim_strsave_escaped((const char_u *)tv_get_string(&argvars[0]),
+ (const char_u *)tv_get_string_buf(&argvars[1],
+ buf));
rettv->v_type = VAR_STRING;
}
@@ -2020,22 +1877,20 @@ static void f_getenv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_special = kSpecialVarNull;
return;
}
- rettv->vval.v_string = p;
+ rettv->vval.v_string = (char *)p;
rettv->v_type = VAR_STRING;
}
-/*
- * "eval()" function
- */
+/// "eval()" function
static void f_eval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
const char *s = tv_get_string_chk(&argvars[0]);
if (s != NULL) {
- s = (const char *)skipwhite((const char_u *)s);
+ s = (const char *)skipwhite(s);
}
const char *const expr_start = s;
- if (s == NULL || eval1((char_u **)&s, rettv, true) == FAIL) {
+ if (s == NULL || eval1((char **)&s, rettv, true) == FAIL) {
if (expr_start != NULL && !aborting()) {
semsg(_(e_invexpr2), expr_start);
}
@@ -2047,17 +1902,13 @@ static void f_eval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "eventhandler()" function
- */
+/// "eventhandler()" function
static void f_eventhandler(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = vgetc_busy;
}
-/*
- * "executable()" function
- */
+/// "executable()" function
static void f_executable(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
if (tv_check_for_string(&argvars[0]) == FAIL) {
@@ -2073,7 +1924,7 @@ typedef struct {
const listitem_T *li;
} GetListLineCookie;
-static char_u *get_list_line(int c, void *cookie, int indent, bool do_concat)
+static char *get_list_line(int c, void *cookie, int indent, bool do_concat)
{
GetListLineCookie *const p = (GetListLineCookie *)cookie;
@@ -2084,7 +1935,7 @@ static char_u *get_list_line(int c, void *cookie, int indent, bool do_concat)
char buf[NUMBUFLEN];
const char *const s = tv_get_string_buf_chk(TV_LIST_ITEM_TV(item), buf);
p->li = TV_LIST_ITEM_NEXT(p->l, item);
- return (char_u *)(s == NULL ? NULL : xstrdup(s));
+ return s == NULL ? NULL : xstrdup(s);
}
static void execute_common(typval_T *argvars, typval_T *rettv, FunPtr fptr, int arg_off)
@@ -2165,36 +2016,24 @@ static void execute_common(typval_T *argvars, typval_T *rettv, FunPtr fptr, int
capture_ga = save_capture_ga;
}
-// "execute(command)" function
+/// "execute(command)" function
static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
execute_common(argvars, rettv, fptr, 0);
}
-// "win_execute(win_id, command)" function
+/// "win_execute(win_id, command)" function
static void f_win_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- tabpage_T *tp;
- win_T *wp = win_id2wp_tp(argvars, &tp);
- win_T *save_curwin;
- tabpage_T *save_curtab;
// Return an empty string if something fails.
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
+ int id = tv_get_number(argvars);
+ tabpage_T *tp;
+ win_T *wp = win_id2wp_tp(id, &tp);
if (wp != NULL && tp != NULL) {
- pos_T curpos = wp->w_cursor;
- if (switch_win_noblock(&save_curwin, &save_curtab, wp, tp, true) ==
- OK) {
- check_cursor();
- execute_common(argvars, rettv, fptr, 1);
- }
- restore_win_noblock(save_curwin, save_curtab, true);
-
- // Update the status line if the cursor moved.
- if (win_valid(wp) && !equalpos(curpos, wp->w_cursor)) {
- wp->w_redr_status = true;
- }
+ WIN_EXECUTE(wp, tp, execute_common(argvars, rettv, fptr, 1));
}
}
@@ -2209,13 +2048,17 @@ static void f_exepath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
(void)os_can_exe(tv_get_string(&argvars[0]), &path, true);
+#ifdef BACKSLASH_IN_FILENAME
+ if (path != NULL) {
+ slash_adjust((char_u *)path);
+ }
+#endif
+
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = (char_u *)path;
+ rettv->vval.v_string = path;
}
-/*
- * "exists()" function
- */
+/// "exists()" function
static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int n = false;
@@ -2227,7 +2070,7 @@ static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr)
n = true;
} else {
// Try expanding things like $VIM and ${HOME}.
- char_u *const exp = expand_env_save((char_u *)p);
+ char *const exp = expand_env_save((char *)p);
if (exp != NULL && *exp != '$') {
n = true;
}
@@ -2235,7 +2078,7 @@ static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
} else if (*p == '&' || *p == '+') { // Option.
n = (get_option_tv(&p, NULL, true) == OK);
- if (*skipwhite((const char_u *)p) != NUL) {
+ if (*skipwhite(p) != NUL) {
n = false; // Trailing garbage.
}
} else if (*p == '*') { // Internal or user defined function.
@@ -2255,9 +2098,7 @@ static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = n;
}
-/*
- * "expand()" function
- */
+/// "expand()" function
static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
size_t len;
@@ -2293,7 +2134,7 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
XFREE_CLEAR(result);
} else {
- rettv->vval.v_string = result;
+ rettv->vval.v_string = (char *)result;
}
} else {
// When the optional second argument is non-zero, don't remove matches
@@ -2309,8 +2150,8 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
options += WILD_ICASE;
}
if (rettv->v_type == VAR_STRING) {
- rettv->vval.v_string = ExpandOne(&xpc, (char_u *)s, NULL, options,
- WILD_ALL);
+ rettv->vval.v_string = (char *)ExpandOne(&xpc, (char_u *)s, NULL, options,
+ WILD_ALL);
} else {
ExpandOne(&xpc, (char_u *)s, NULL, options, WILD_ALL_KEEP);
tv_list_alloc_ret(rettv, xpc.xp_numfiles);
@@ -2329,7 +2170,6 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
#endif
}
-
/// "menu_get(path [, modes])" function
static void f_menu_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
@@ -2339,11 +2179,11 @@ static void f_menu_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
const char *const strmodes = tv_get_string(&argvars[1]);
modes = get_menu_cmd_modes(strmodes, false, NULL, NULL);
}
- menu_get((char_u *)tv_get_string(&argvars[0]), modes, rettv->vval.v_list);
+ menu_get((char *)tv_get_string(&argvars[0]), modes, rettv->vval.v_list);
}
-// "expandcmd()" function
-// Expand all the special characters in a command string.
+/// "expandcmd()" function
+/// Expand all the special characters in a command string.
static void f_expandcmd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
char *errormsg = NULL;
@@ -2352,8 +2192,8 @@ static void f_expandcmd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
char_u *cmdstr = (char_u *)xstrdup(tv_get_string(&argvars[0]));
exarg_T eap = {
- .cmd = cmdstr,
- .arg = cmdstr,
+ .cmd = (char *)cmdstr,
+ .arg = (char *)cmdstr,
.usefilter = false,
.nextcmd = NULL,
.cmdidx = CMD_USER,
@@ -2364,10 +2204,9 @@ static void f_expandcmd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (errormsg != NULL && *errormsg != NUL) {
emsg(errormsg);
}
- rettv->vval.v_string = cmdstr;
+ rettv->vval.v_string = (char *)cmdstr;
}
-
/// "flatten(list[, {maxdepth}])" function
static void f_flatten(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
@@ -2403,10 +2242,8 @@ static void f_flatten(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "extend(list, list [, idx])" function
- * "extend(dict, dict [, action])" function
- */
+/// "extend(list, list [, idx])" function
+/// "extend(dict, dict [, action])" function
static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
const char *const arg_errmsg = N_("extend() argument");
@@ -2483,9 +2320,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "feedkeys()" function
- */
+/// "feedkeys()" function
static void f_feedkeys(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
// This is not allowed in the sandbox. If the commands would still be
@@ -2514,17 +2349,15 @@ static void f_filereadable(typval_T *argvars, typval_T *rettv, FunPtr fptr)
(*p && !os_isdir((const char_u *)p) && os_file_is_readable(p));
}
-/*
- * Return 0 for not writable, 1 for writable file, 2 for a dir which we have
- * rights to write into.
- */
+/// @return 0 for not writable
+/// 1 for writable file
+/// 2 for a dir which we have rights to write into.
static void f_filewritable(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
const char *filename = tv_get_string(&argvars[0]);
rettv->vval.v_number = os_file_is_writable(filename);
}
-
static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what)
{
char_u *fresult = NULL;
@@ -2566,7 +2399,7 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what)
fresult = find_file_in_path_option(first ? (char_u *)fname : NULL,
first ? strlen(fname) : 0,
0, first, path,
- find_what, curbuf->b_ffname,
+ find_what, (char_u *)curbuf->b_ffname,
(find_what == FINDFILE_DIR
? (char_u *)""
: curbuf->b_p_sua));
@@ -2579,44 +2412,35 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what)
}
if (rettv->v_type == VAR_STRING) {
- rettv->vval.v_string = fresult;
+ rettv->vval.v_string = (char *)fresult;
}
}
-
-/*
- * "filter()" function
- */
+/// "filter()" function
static void f_filter(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
filter_map(argvars, rettv, FALSE);
}
-/*
- * "finddir({fname}[, {path}[, {count}]])" function
- */
+/// "finddir({fname}[, {path}[, {count}]])" function
static void f_finddir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
findfilendir(argvars, rettv, FINDFILE_DIR);
}
-/*
- * "findfile({fname}[, {path}[, {count}]])" function
- */
+/// "findfile({fname}[, {path}[, {count}]])" function
static void f_findfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
findfilendir(argvars, rettv, FINDFILE_FILE);
}
-/*
- * "float2nr({float})" function
- */
+/// "float2nr({float})" function
static void f_float2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
float_T f;
if (tv_get_float_chk(argvars, &f)) {
- if (f <= (float_T)-VARNUMBER_MAX + DBL_EPSILON) {
+ if (f <= (float_T) - VARNUMBER_MAX + DBL_EPSILON) {
rettv->vval.v_number = -VARNUMBER_MAX;
} else if (f >= (float_T)VARNUMBER_MAX - DBL_EPSILON) {
rettv->vval.v_number = VARNUMBER_MAX;
@@ -2626,9 +2450,7 @@ static void f_float2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "fmod()" function
- */
+/// "fmod()" function
static void f_fmod(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
float_T fx;
@@ -2642,18 +2464,14 @@ static void f_fmod(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "fnameescape({string})" function
- */
+/// "fnameescape({string})" function
static void f_fnameescape(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- rettv->vval.v_string = (char_u *)vim_strsave_fnameescape(tv_get_string(&argvars[0]), false);
+ rettv->vval.v_string = vim_strsave_fnameescape(tv_get_string(&argvars[0]), VSE_NONE);
rettv->v_type = VAR_STRING;
}
-/*
- * "fnamemodify({fname}, {mods})" function
- */
+/// "fnamemodify({fname}, {mods})" function
static void f_fnamemodify(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
char_u *fbuf = NULL;
@@ -2667,8 +2485,8 @@ static void f_fnamemodify(typval_T *argvars, typval_T *rettv, FunPtr fptr)
len = strlen(fname);
if (*mods != NUL) {
size_t usedlen = 0;
- (void)modify_fname((char_u *)mods, false, &usedlen,
- (char_u **)&fname, &fbuf, &len);
+ (void)modify_fname((char *)mods, false, &usedlen,
+ (char **)&fname, (char **)&fbuf, &len);
}
}
@@ -2676,15 +2494,12 @@ static void f_fnamemodify(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (fname == NULL) {
rettv->vval.v_string = NULL;
} else {
- rettv->vval.v_string = (char_u *)xmemdupz(fname, len);
+ rettv->vval.v_string = xmemdupz(fname, len);
}
xfree(fbuf);
}
-
-/*
- * "foldclosed()" function
- */
+/// "foldclosed()" function
static void foldclosed_both(typval_T *argvars, typval_T *rettv, int end)
{
const linenr_T lnum = tv_get_lnum(argvars);
@@ -2703,25 +2518,19 @@ static void foldclosed_both(typval_T *argvars, typval_T *rettv, int end)
rettv->vval.v_number = -1;
}
-/*
- * "foldclosed()" function
- */
+/// "foldclosed()" function
static void f_foldclosed(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
foldclosed_both(argvars, rettv, FALSE);
}
-/*
- * "foldclosedend()" function
- */
+/// "foldclosedend()" function
static void f_foldclosedend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
foldclosed_both(argvars, rettv, TRUE);
}
-/*
- * "foldlevel()" function
- */
+/// "foldlevel()" function
static void f_foldlevel(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
const linenr_T lnum = tv_get_lnum(argvars);
@@ -2730,9 +2539,7 @@ static void f_foldlevel(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "foldtext()" function
- */
+/// "foldtext()" function
static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
linenr_T foldstart;
@@ -2749,7 +2556,7 @@ static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr)
foldstart = (linenr_T)get_vim_var_nr(VV_FOLDSTART);
foldend = (linenr_T)get_vim_var_nr(VV_FOLDEND);
- dashes = get_vim_var_str(VV_FOLDDASHES);
+ dashes = (char_u *)get_vim_var_str(VV_FOLDDASHES);
if (foldstart > 0 && foldend <= curbuf->b_ml.ml_line_count) {
// Find first non-empty line in the fold.
for (lnum = foldstart; lnum < foldend; lnum++) {
@@ -2759,18 +2566,18 @@ static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
// Find interesting text in this line.
- s = skipwhite(ml_get(lnum));
+ s = (char_u *)skipwhite((char *)ml_get(lnum));
// skip C comment-start
if (s[0] == '/' && (s[1] == '*' || s[1] == '/')) {
- s = skipwhite(s + 2);
- if (*skipwhite(s) == NUL && lnum + 1 < foldend) {
- s = skipwhite(ml_get(lnum + 1));
+ s = (char_u *)skipwhite((char *)s + 2);
+ if (*skipwhite((char *)s) == NUL && lnum + 1 < foldend) {
+ s = (char_u *)skipwhite((char *)ml_get(lnum + 1));
if (*s == '*') {
- s = skipwhite(s + 1);
+ s = (char_u *)skipwhite((char *)s + 1);
}
}
}
- unsigned long count = (unsigned long)(foldend - foldstart + 1);
+ unsigned long count = (unsigned long)foldend - foldstart + 1;
txt = NGETTEXT("+-%s%3ld line: ", "+-%s%3ld lines: ", count);
r = xmalloc(STRLEN(txt)
+ STRLEN(dashes) // for %s
@@ -2781,13 +2588,11 @@ static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr)
STRCAT(r, s);
// remove 'foldmarker' and 'commentstring'
foldtext_cleanup(r + len);
- rettv->vval.v_string = r;
+ rettv->vval.v_string = (char *)r;
}
}
-/*
- * "foldtextresult(lnum)" function
- */
+/// "foldtextresult(lnum)" function
static void f_foldtextresult(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
char_u *text;
@@ -2812,18 +2617,15 @@ static void f_foldtextresult(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (text == buf) {
text = vim_strsave(text);
}
- rettv->vval.v_string = text;
+ rettv->vval.v_string = (char *)text;
}
entered = false;
}
-/*
- * "foreground()" function
- */
+/// "foreground()" function
static void f_foreground(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
-}
+{}
static void f_funcref(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
@@ -2847,9 +2649,7 @@ static void f_garbagecollect(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "get()" function
- */
+/// "get()" function
static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
listitem_T *li;
@@ -2899,7 +2699,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
pt = argvars[0].vval.v_partial;
} else {
memset(&fref_pt, 0, sizeof(fref_pt));
- fref_pt.pt_name = argvars[0].vval.v_string;
+ fref_pt.pt_name = (char_u *)argvars[0].vval.v_string;
pt = &fref_pt;
}
@@ -2910,9 +2710,9 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->v_type = (*what == 'f' ? VAR_FUNC : VAR_STRING);
const char *const n = (const char *)partial_name(pt);
assert(n != NULL);
- rettv->vval.v_string = (char_u *)xstrdup(n);
+ rettv->vval.v_string = xstrdup(n);
if (rettv->v_type == VAR_FUNC) {
- func_ref(rettv->vval.v_string);
+ func_ref((char_u *)rettv->vval.v_string);
}
} else if (strcmp(what, "dict") == 0) {
what_is_dict = true;
@@ -3009,12 +2809,12 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * Get line or list of lines from buffer "buf" into "rettv".
- * Return a range (from start to end) of lines in rettv from the specified
- * buffer.
- * If 'retlist' is TRUE, then the lines are returned as a Vim List.
- */
+/// Get line or list of lines from buffer "buf" into "rettv".
+///
+/// @param retlist if TRUE, then the lines are returned as a Vim List.
+///
+/// @return range (from start to end) of lines in rettv from the specified
+/// buffer.
static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, int retlist, typval_T *rettv)
{
rettv->v_type = (retlist ? VAR_LIST : VAR_STRING);
@@ -3041,15 +2841,13 @@ static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, int retli
}
} else {
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = ((start >= 1 && start <= buf->b_ml.ml_line_count)
- ? vim_strsave(ml_get_buf(buf, start, false))
- : NULL);
+ rettv->vval.v_string =
+ (char *)((start >= 1 && start <= buf->b_ml.ml_line_count)
+ ? vim_strsave(ml_get_buf(buf, start, false)) : NULL);
}
}
-/*
- * "getbufline()" function
- */
+/// "getbufline()" function
static void f_getbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
buf_T *const buf = tv_get_buf_from_arg(&argvars[0]);
@@ -3062,9 +2860,7 @@ static void f_getbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
get_buffer_lines(buf, lnum, end, true, rettv);
}
-/*
- * "getbufvar()" function
- */
+/// "getbufvar()" function
static void f_getbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
bool done = false;
@@ -3124,7 +2920,7 @@ f_getbufvar_end:
}
}
-// "getchangelist()" function
+/// "getchangelist()" function
static void f_getchangelist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
tv_list_alloc_ret(rettv, 2);
@@ -3164,7 +2960,7 @@ static void f_getchangelist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-// "getchar()" and "getcharstr()" functions
+/// "getchar()" and "getcharstr()" functions
static void getchar_common(typval_T *argvars, typval_T *rettv)
FUNC_ATTR_NONNULL_ALL
{
@@ -3172,6 +2968,7 @@ static void getchar_common(typval_T *argvars, typval_T *rettv)
bool error = false;
no_mapping++;
+ allow_keys++;
for (;;) {
// Position the cursor. Needed after a message that ends in a space,
// or if event processing caused a redraw.
@@ -3180,7 +2977,7 @@ static void getchar_common(typval_T *argvars, typval_T *rettv)
if (argvars[0].v_type == VAR_UNKNOWN) {
// getchar(): blocking wait.
// TODO(bfredl): deduplicate shared logic with state_enter ?
- if (!(char_avail() || using_script() || input_available())) {
+ if (!char_avail()) {
(void)os_inchar(NULL, 0, -1, 0, main_loop.events);
if (!multiqueue_empty(main_loop.events)) {
state_handle_k_event();
@@ -3209,6 +3006,7 @@ static void getchar_common(typval_T *argvars, typval_T *rettv)
break;
}
no_mapping--;
+ allow_keys--;
set_vim_var_nr(VV_MOUSE_WIN, 0);
set_vim_var_nr(VV_MOUSE_WINID, 0);
@@ -3216,7 +3014,7 @@ static void getchar_common(typval_T *argvars, typval_T *rettv)
set_vim_var_nr(VV_MOUSE_COL, 0);
rettv->vval.v_number = n;
- if (IS_SPECIAL(n) || mod_mask != 0) {
+ if (n != 0 && (IS_SPECIAL(n) || mod_mask != 0)) {
char_u temp[10]; // modifier: 3, mbyte-char: 6, NUL: 1
int i = 0;
@@ -3231,12 +3029,12 @@ static void getchar_common(typval_T *argvars, typval_T *rettv)
temp[i++] = K_SECOND(n);
temp[i++] = K_THIRD(n);
} else {
- i += utf_char2bytes(n, temp + i);
+ i += utf_char2bytes(n, (char *)temp + i);
}
assert(i < 10);
temp[i++] = NUL;
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = vim_strsave(temp);
+ rettv->vval.v_string = (char *)vim_strsave(temp);
if (is_mouse_key(n)) {
int row = mouse_row;
@@ -3266,43 +3064,100 @@ static void getchar_common(typval_T *argvars, typval_T *rettv)
}
}
-// "getchar()" function
+/// "getchar()" function
static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
getchar_common(argvars, rettv);
}
-// "getcharstr()" function
+/// "getcharstr()" function
static void f_getcharstr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
getchar_common(argvars, rettv);
if (rettv->v_type == VAR_NUMBER) {
- char_u temp[7]; // mbyte-char: 6, NUL: 1
+ char temp[7]; // mbyte-char: 6, NUL: 1
const varnumber_T n = rettv->vval.v_number;
int i = 0;
if (n != 0) {
- i += utf_char2bytes(n, temp);
+ i += utf_char2bytes(n, (char *)temp);
}
assert(i < 7);
temp[i++] = NUL;
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = vim_strsave(temp);
+ rettv->vval.v_string = xstrdup(temp);
}
}
-/*
- * "getcharmod()" function
- */
+/// "getcharmod()" function
static void f_getcharmod(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = mod_mask;
}
-/*
- * "getcharsearch()" function
- */
+static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos, bool charcol)
+{
+ pos_T *fp = NULL;
+ pos_T pos;
+ win_T *wp = curwin;
+ int fnum = -1;
+
+ if (getcurpos) {
+ if (argvars[0].v_type != VAR_UNKNOWN) {
+ wp = find_win_by_nr_or_id(&argvars[0]);
+ if (wp != NULL) {
+ fp = &wp->w_cursor;
+ }
+ } else {
+ fp = &curwin->w_cursor;
+ }
+ if (fp != NULL && charcol) {
+ pos = *fp;
+ pos.col = buf_byteidx_to_charidx(wp->w_buffer, pos.lnum, pos.col);
+ fp = &pos;
+ }
+ } else {
+ fp = var2fpos(&argvars[0], true, &fnum, charcol);
+ }
+
+ list_T *const l = tv_list_alloc_ret(rettv, 4 + getcurpos);
+ tv_list_append_number(l, (fnum != -1) ? (varnumber_T)fnum : (varnumber_T)0);
+ tv_list_append_number(l, ((fp != NULL) ? (varnumber_T)fp->lnum : (varnumber_T)0));
+ tv_list_append_number(l, ((fp != NULL)
+ ? (varnumber_T)(fp->col == MAXCOL ? MAXCOL : fp->col + 1)
+ : (varnumber_T)0));
+ tv_list_append_number(l, (fp != NULL) ? (varnumber_T)fp->coladd : (varnumber_T)0);
+ if (getcurpos) {
+ const int save_set_curswant = curwin->w_set_curswant;
+ const colnr_T save_curswant = curwin->w_curswant;
+ const colnr_T save_virtcol = curwin->w_virtcol;
+
+ if (wp == curwin) {
+ update_curswant();
+ }
+ tv_list_append_number(l, (wp == NULL) ? 0 : ((wp->w_curswant == MAXCOL)
+ ? (varnumber_T)MAXCOL
+ : (varnumber_T)wp->w_curswant + 1));
+
+ // Do not change "curswant", as it is unexpected that a get
+ // function has a side effect.
+ if (wp == curwin && save_set_curswant) {
+ curwin->w_set_curswant = save_set_curswant;
+ curwin->w_curswant = save_curswant;
+ curwin->w_virtcol = save_virtcol;
+ curwin->w_valid &= ~VALID_VIRTCOL;
+ }
+ }
+}
+
+/// "getcharpos()" function
+static void f_getcharpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ getpos_both(argvars, rettv, false, true);
+}
+
+/// "getcharsearch()" function
static void f_getcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
tv_dict_alloc_ret(rettv);
@@ -3314,26 +3169,33 @@ static void f_getcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr)
tv_dict_add_nr(dict, S_LEN("until"), last_csearch_until());
}
-/*
- * "getcmdline()" function
- */
+/// "getcmdcompltype()" function
+static void f_getcmdcompltype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = (char *)get_cmdline_completion();
+}
+
+/// "getcmdline()" function
static void f_getcmdline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = get_cmdline_str();
+ rettv->vval.v_string = (char *)get_cmdline_str();
}
-/*
- * "getcmdpos()" function
- */
+/// "getcmdpos()" function
static void f_getcmdpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = get_cmdline_pos() + 1;
}
-/*
- * "getcmdtype()" function
- */
+/// "getcmdscreenpos()" function
+static void f_getcmdscreenpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ rettv->vval.v_number = get_cmdline_screen_pos() + 1;
+}
+
+/// "getcmdtype()" function
static void f_getcmdtype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->v_type = VAR_STRING;
@@ -3341,9 +3203,7 @@ static void f_getcmdtype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_string[0] = get_cmdline_type();
}
-/*
- * "getcmdwintype()" function
- */
+/// "getcmdwintype()" function
static void f_getcmdwintype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->v_type = VAR_STRING;
@@ -3352,7 +3212,7 @@ static void f_getcmdwintype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_string[0] = cmdwin_type;
}
-// "getcompletion()" function
+/// "getcompletion()" function
static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
char_u *pat;
@@ -3394,7 +3254,7 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
ExpandInit(&xpc);
- xpc.xp_pattern = (char_u *)pattern;
+ xpc.xp_pattern = (char *)pattern;
xpc.xp_pattern_len = STRLEN(xpc.xp_pattern);
xpc.xp_context = cmdcomplete_str_to_type(type);
if (xpc.xp_context == EXPAND_NOTHING) {
@@ -3413,12 +3273,12 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
if (xpc.xp_context == EXPAND_SIGN) {
- set_context_in_sign_cmd(&xpc, xpc.xp_pattern);
+ set_context_in_sign_cmd(&xpc, (char_u *)xpc.xp_pattern);
xpc.xp_pattern_len = STRLEN(xpc.xp_pattern);
}
theend:
- pat = addstar(xpc.xp_pattern, xpc.xp_pattern_len, xpc.xp_context);
+ pat = addstar((char_u *)xpc.xp_pattern, xpc.xp_pattern_len, xpc.xp_context);
ExpandOne(&xpc, pat, NULL, options, WILD_ALL_KEEP);
tv_list_alloc_ret(rettv, xpc.xp_numfiles);
@@ -3451,8 +3311,8 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
[kCdScopeTabpage] = 0, // Number of tab to look at.
};
- char_u *cwd = NULL; // Current working directory to print
- char_u *from = NULL; // The original string to copy
+ char *cwd = NULL; // Current working directory to print
+ char *from = NULL; // The original string to copy
tabpage_T *tp = curtab; // The tabpage to look at.
win_T *win = curwin; // The window to look at.
@@ -3534,8 +3394,8 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
FALLTHROUGH; // In global directory, just need to get OS CWD.
case kCdScopeInvalid: // If called without any arguments, get OS CWD.
- if (os_dirname(cwd, MAXPATHL) == FAIL) {
- from = (char_u *)""; // Return empty string on failure.
+ if (os_dirname((char_u *)cwd, MAXPATHL) == FAIL) {
+ from = ""; // Return empty string on failure.
}
}
@@ -3543,7 +3403,7 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
STRLCPY(cwd, from, MAXPATHL);
}
- rettv->vval.v_string = vim_strsave(cwd);
+ rettv->vval.v_string = xstrdup(cwd);
#ifdef BACKSLASH_IN_FILENAME
slash_adjust(rettv->vval.v_string);
#endif
@@ -3551,18 +3411,14 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
xfree(cwd);
}
-/*
- * "getfontname()" function
- */
+/// "getfontname()" function
static void f_getfontname(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
}
-/*
- * "getfperm({fname})" function
- */
+/// "getfperm({fname})" function
static void f_getfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
char *perm = NULL;
@@ -3579,12 +3435,10 @@ static void f_getfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = (char_u *)perm;
+ rettv->vval.v_string = perm;
}
-/*
- * "getfsize({fname})" function
- */
+/// "getfsize({fname})" function
static void f_getfsize(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
const char *fname = tv_get_string(&argvars[0]);
@@ -3609,9 +3463,7 @@ static void f_getfsize(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "getftime({fname})" function
- */
+/// "getftime({fname})" function
static void f_getftime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
const char *fname = tv_get_string(&argvars[0]);
@@ -3624,9 +3476,7 @@ static void f_getftime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "getftype({fname})" function
- */
+/// "getftype({fname})" function
static void f_getftype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
char_u *type = NULL;
@@ -3657,10 +3507,10 @@ static void f_getftype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
type = vim_strsave((char_u *)t);
}
- rettv->vval.v_string = type;
+ rettv->vval.v_string = (char *)type;
}
-// "getjumplist()" function
+/// "getjumplist()" function
static void f_getjumplist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
tv_list_alloc_ret(rettv, kListLenMayKnow);
@@ -3686,14 +3536,12 @@ static void f_getjumplist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
tv_dict_add_nr(d, S_LEN("coladd"), wp->w_jumplist[i].fmark.mark.coladd);
tv_dict_add_nr(d, S_LEN("bufnr"), wp->w_jumplist[i].fmark.fnum);
if (wp->w_jumplist[i].fname != NULL) {
- tv_dict_add_str(d, S_LEN("filename"), (char *)wp->w_jumplist[i].fname);
+ tv_dict_add_str(d, S_LEN("filename"), wp->w_jumplist[i].fname);
}
}
}
-/*
- * "getline(lnum, [end])" function
- */
+/// "getline(lnum, [end])" function
static void f_getline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
linenr_T end;
@@ -3718,7 +3566,6 @@ static void f_getloclist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
get_qf_loc_list(false, wp, &argvars[1], rettv);
}
-
/// "getmarklist()" function
static void f_getmarklist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
@@ -3737,65 +3584,8 @@ static void f_getmarklist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
get_buf_local_marks(buf, rettv->vval.v_list);
}
-/*
- * "getmatches()" function
- */
-static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- matchitem_T *cur;
- int i;
- win_T *win = get_optional_window(argvars, 0);
-
- if (win == NULL) {
- return;
- }
-
- tv_list_alloc_ret(rettv, kListLenMayKnow);
- cur = win->w_match_head;
- while (cur != NULL) {
- dict_T *dict = tv_dict_alloc();
- if (cur->match.regprog == NULL) {
- // match added with matchaddpos()
- for (i = 0; i < MAXPOSMATCH; i++) {
- llpos_T *llpos;
- char buf[30]; // use 30 to avoid compiler warning
-
- llpos = &cur->pos.pos[i];
- if (llpos->lnum == 0) {
- break;
- }
- list_T *const l = tv_list_alloc(1 + (llpos->col > 0 ? 2 : 0));
- tv_list_append_number(l, (varnumber_T)llpos->lnum);
- if (llpos->col > 0) {
- tv_list_append_number(l, (varnumber_T)llpos->col);
- tv_list_append_number(l, (varnumber_T)llpos->len);
- }
- int len = snprintf(buf, sizeof(buf), "pos%d", i + 1);
- assert((size_t)len < sizeof(buf));
- tv_dict_add_list(dict, buf, (size_t)len, l);
- }
- } else {
- tv_dict_add_str(dict, S_LEN("pattern"), (const char *)cur->pattern);
- }
- tv_dict_add_str(dict, S_LEN("group"),
- (const char *)syn_id2name(cur->hlg_id));
- tv_dict_add_nr(dict, S_LEN("priority"), (varnumber_T)cur->priority);
- tv_dict_add_nr(dict, S_LEN("id"), (varnumber_T)cur->id);
-
- if (cur->conceal_char) {
- char buf[MB_MAXBYTES + 1];
-
- buf[utf_char2bytes(cur->conceal_char, (char_u *)buf)] = NUL;
- tv_dict_add_str(dict, S_LEN("conceal"), buf);
- }
-
- tv_list_append_dict(rettv->vval.v_list, dict);
- cur = cur->next;
- }
-}
-
-// "getmousepos()" function
-void f_getmousepos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+/// "getmousepos()" function
+static void f_getmousepos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
dict_T *d;
win_T *wp;
@@ -3805,7 +3595,7 @@ void f_getmousepos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
varnumber_T winid = 0;
varnumber_T winrow = 0;
varnumber_T wincol = 0;
- linenr_T line = 0;
+ linenr_T lnum = 0;
varnumber_T column = 0;
tv_dict_alloc_ret(rettv);
@@ -3816,26 +3606,16 @@ void f_getmousepos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
wp = mouse_find_win(&grid, &row, &col);
if (wp != NULL) {
- int height = wp->w_height + wp->w_status_height;
+ int height = wp->w_height + wp->w_hsep_height + wp->w_status_height;
// The height is adjusted by 1 when there is a bottom border. This is not
// necessary for a top border since `row` starts at -1 in that case.
if (row < height + wp->w_border_adj[2]) {
winid = wp->handle;
- winrow = row + 1 + wp->w_border_adj[0]; // Adjust by 1 for top border
- wincol = col + 1 + wp->w_border_adj[3]; // Adjust by 1 for left border
+ winrow = row + 1 + wp->w_winrow_off; // Adjust by 1 for top border
+ wincol = col + 1 + wp->w_wincol_off; // Adjust by 1 for left border
if (row >= 0 && row < wp->w_height && col >= 0 && col < wp->w_width) {
- char_u *p;
- int count;
-
- mouse_comp_pos(wp, &row, &col, &line);
-
- // limit to text length plus one
- p = ml_get_buf(wp->w_buffer, line, false);
- count = (int)STRLEN(p);
- if (col > count) {
- col = count;
- }
-
+ (void)mouse_comp_pos(wp, &row, &col, &lnum);
+ col = vcol2col(wp, lnum, col);
column = col + 1;
}
}
@@ -3843,73 +3623,31 @@ void f_getmousepos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
tv_dict_add_nr(d, S_LEN("winid"), winid);
tv_dict_add_nr(d, S_LEN("winrow"), winrow);
tv_dict_add_nr(d, S_LEN("wincol"), wincol);
- tv_dict_add_nr(d, S_LEN("line"), (varnumber_T)line);
+ tv_dict_add_nr(d, S_LEN("line"), (varnumber_T)lnum);
tv_dict_add_nr(d, S_LEN("column"), column);
}
-/*
- * "getpid()" function
- */
+/// "getpid()" function
static void f_getpid(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = os_get_pid();
}
-static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos)
+/// "getcurpos(string)" function
+static void f_getcurpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- pos_T *fp;
- int fnum = -1;
-
- if (getcurpos) {
- fp = &curwin->w_cursor;
- } else {
- fp = var2fpos(&argvars[0], true, &fnum);
- }
-
- list_T *const l = tv_list_alloc_ret(rettv, 4 + (!!getcurpos));
- tv_list_append_number(l, (fnum != -1) ? (varnumber_T)fnum : (varnumber_T)0);
- tv_list_append_number(l, ((fp != NULL)
- ? (varnumber_T)fp->lnum
- : (varnumber_T)0));
- tv_list_append_number(l, ((fp != NULL)
- ? (varnumber_T)(fp->col == MAXCOL ? MAXCOL : fp->col + 1)
- : (varnumber_T)0));
- tv_list_append_number(l, (fp != NULL) ? (varnumber_T)fp->coladd : (varnumber_T)0);
- if (getcurpos) {
- const int save_set_curswant = curwin->w_set_curswant;
- const colnr_T save_curswant = curwin->w_curswant;
- const colnr_T save_virtcol = curwin->w_virtcol;
-
- update_curswant();
- tv_list_append_number(l, (curwin->w_curswant == MAXCOL
- ? (varnumber_T)MAXCOL
- : (varnumber_T)curwin->w_curswant + 1));
-
- // Do not change "curswant", as it is unexpected that a get
- // function has a side effect.
- if (save_set_curswant) {
- curwin->w_set_curswant = save_set_curswant;
- curwin->w_curswant = save_curswant;
- curwin->w_virtcol = save_virtcol;
- curwin->w_valid &= ~VALID_VIRTCOL;
- }
- }
+ getpos_both(argvars, rettv, true, false);
}
-/*
- * "getcurpos(string)" function
- */
-static void f_getcurpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getcursorcharpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- getpos_both(argvars, rettv, true);
+ getpos_both(argvars, rettv, true, true);
}
-/*
- * "getpos(string)" function
- */
+/// "getpos(string)" function
static void f_getpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- getpos_both(argvars, rettv, false);
+ getpos_both(argvars, rettv, false, false);
}
/// "getqflist()" functions
@@ -3932,7 +3670,7 @@ static int getreg_get_regname(typval_T *argvars)
}
} else {
// Default to v:register
- strregname = get_vim_var_str(VV_REG);
+ strregname = (char_u *)get_vim_var_str(VV_REG);
}
return *strregname == 0 ? '"' : *strregname;
@@ -3991,7 +3729,7 @@ static void f_getregtype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
MotionType reg_type = get_reg_type(regname, &reglen);
format_reg_type(reg_type, reglen, buf, ARRAY_SIZE(buf));
- rettv->vval.v_string = (char_u *)xstrdup(buf);
+ rettv->vval.v_string = xstrdup(buf);
}
/// "gettabinfo()" function
@@ -4026,13 +3764,9 @@ static void f_gettabinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "gettabvar()" function
- */
+/// "gettabvar()" function
static void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- win_T *oldcurwin;
- tabpage_T *oldtabpage;
bool done = false;
rettv->v_type = VAR_STRING;
@@ -4046,7 +3780,8 @@ static void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
win_T *const window = tp == curtab || tp->tp_firstwin == NULL
? firstwin
: tp->tp_firstwin;
- if (switch_win(&oldcurwin, &oldtabpage, window, tp, true) == OK) {
+ switchwin_T switchwin;
+ if (switch_win(&switchwin, window, tp, true) == OK) {
// look up the variable
// Let gettabvar({nr}, "") return the "t:" dictionary.
const dictitem_T *const v = find_var_in_ht(&tp->tp_vars->dv_hashtab, 't',
@@ -4059,7 +3794,7 @@ static void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
// restore previous notion of curwin
- restore_win(oldcurwin, oldtabpage, true);
+ restore_win(&switchwin, true);
}
if (!done && argvars[2].v_type != VAR_UNKNOWN) {
@@ -4067,15 +3802,13 @@ static void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "gettabwinvar()" function
- */
+/// "gettabwinvar()" function
static void f_gettabwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
getwinvar(argvars, rettv, 1);
}
-// "gettagstack()" function
+/// "gettagstack()" function
static void f_gettagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
win_T *wp = curwin; // default is current window
@@ -4100,7 +3833,7 @@ static void f_getwininfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
tv_list_alloc_ret(rettv, kListLenMayKnow);
if (argvars[0].v_type != VAR_UNKNOWN) {
- wparg = win_id2wp(argvars);
+ wparg = win_id2wp(tv_get_number(&argvars[0]));
if (wparg == NULL) {
return;
}
@@ -4127,12 +3860,11 @@ static void f_getwininfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-// Dummy timer callback. Used by f_wait().
+/// Dummy timer callback. Used by f_wait().
static void dummy_timer_due_cb(TimeWatcher *tw, void *data)
-{
-}
+{}
-// Dummy timer close callback. Used by f_wait().
+/// Dummy timer close callback. Used by f_wait().
static void dummy_timer_close_cb(TimeWatcher *tw, void *data)
{
xfree(tw);
@@ -4170,15 +3902,14 @@ static void f_wait(typval_T *argvars, typval_T *rettv, FunPtr fptr)
typval_T argv = TV_INITIAL_VALUE;
typval_T exprval = TV_INITIAL_VALUE;
bool error = false;
- int save_called_emsg = called_emsg;
- called_emsg = false;
+ const int called_emsg_before = called_emsg;
LOOP_PROCESS_EVENTS_UNTIL(&main_loop, main_loop.events, timeout,
eval_expr_typval(&expr, &argv, 0, &exprval) != OK
|| tv_get_number_chk(&exprval, &error)
- || called_emsg || error || got_int);
+ || called_emsg > called_emsg_before || error || got_int);
- if (called_emsg || error) {
+ if (called_emsg > called_emsg_before || error) {
rettv->vval.v_number = -3;
} else if (got_int) {
got_int = false;
@@ -4188,14 +3919,12 @@ static void f_wait(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = 0;
}
- called_emsg = save_called_emsg;
-
// Stop dummy timer
time_watcher_stop(tw);
time_watcher_close(tw, dummy_timer_close_cb);
}
-// "win_screenpos()" function
+/// "win_screenpos()" function
static void f_win_screenpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
tv_list_alloc_ret(rettv, 2);
@@ -4204,16 +3933,14 @@ static void f_win_screenpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
tv_list_append_number(rettv->vval.v_list, wp == NULL ? 0 : wp->w_wincol + 1);
}
-//
-// Move the window wp into a new split of targetwin in a given direction
-//
+/// Move the window wp into a new split of targetwin in a given direction
static void win_move_into_split(win_T *wp, win_T *targetwin, int size, int flags)
{
int dir;
int height = wp->w_height;
win_T *oldwin = curwin;
- if (wp == targetwin) {
+ if (wp == targetwin || wp == aucmd_win) {
return;
}
@@ -4244,7 +3971,7 @@ static void win_move_into_split(win_T *wp, win_T *targetwin, int size, int flags
}
}
-// "win_splitmove()" function
+/// "win_splitmove()" function
static void f_win_splitmove(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
win_T *wp;
@@ -4284,7 +4011,7 @@ static void f_win_splitmove(typval_T *argvars, typval_T *rettv, FunPtr fptr)
win_move_into_split(wp, targetwin, size, flags);
}
-// "getwinpos({timeout})" function
+/// "getwinpos({timeout})" function
static void f_getwinpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
tv_list_alloc_ret(rettv, 2);
@@ -4292,17 +4019,13 @@ static void f_getwinpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
tv_list_append_number(rettv->vval.v_list, -1);
}
-/*
- * "getwinposx()" function
- */
+/// "getwinposx()" function
static void f_getwinposx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = -1;
}
-/*
- * "getwinposy()" function
- */
+/// "getwinposy()" function
static void f_getwinposy(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = -1;
@@ -4314,9 +4037,7 @@ static void f_getwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
getwinvar(argvars, rettv, 0);
}
-/*
- * "glob()" function
- */
+/// "glob()" function
static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int options = WILD_SILENT|WILD_USE_NL;
@@ -4347,8 +4068,9 @@ static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr)
options += WILD_ICASE;
}
if (rettv->v_type == VAR_STRING) {
- rettv->vval.v_string = ExpandOne(&xpc, (char_u *)tv_get_string(&argvars[0]), NULL, options,
- WILD_ALL);
+ rettv->vval.v_string = (char *)ExpandOne(&xpc, (char_u *)
+ tv_get_string(&argvars[0]), NULL, options,
+ WILD_ALL);
} else {
ExpandOne(&xpc, (char_u *)tv_get_string(&argvars[0]), NULL, options,
WILD_ALL_KEEP);
@@ -4399,7 +4121,7 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
globpath((char_u *)tv_get_string(&argvars[0]), (char_u *)file, &ga, flags);
if (rettv->v_type == VAR_STRING) {
- rettv->vval.v_string = (char_u *)ga_concat_strings_sep(&ga, "\n");
+ rettv->vval.v_string = ga_concat_strings_sep(&ga, "\n");
} else {
tv_list_alloc_ret(rettv, ga.ga_len);
for (int i = 0; i < ga.ga_len; i++) {
@@ -4414,16 +4136,13 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-// "glob2regpat()" function
+/// "glob2regpat()" function
static void f_glob2regpat(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
const char *const pat = tv_get_string_chk(&argvars[0]); // NULL on type error
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = ((pat == NULL)
- ? NULL
- : file_pat_to_reg_pat((char_u *)pat, NULL, NULL,
- false));
+ rettv->vval.v_string = (pat == NULL) ? NULL : file_pat_to_reg_pat(pat, NULL, NULL, false);
}
/// "has()" function
@@ -4433,6 +4152,12 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
#if defined(BSD) && !defined(__APPLE__)
"bsd",
#endif
+#ifdef __linux__
+ "linux",
+#endif
+#ifdef SUN_SYSTEM
+ "sun",
+#endif
#ifdef UNIX
"unix",
#endif
@@ -4504,6 +4229,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
"mouse",
"multi_byte",
"multi_lang",
+ "nanotime",
"num64",
"packages",
"path_extra",
@@ -4555,6 +4281,8 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
"userreg",
};
+ // XXX: eval_has_provider() may shell out :(
+ const int save_shell_error = get_vim_var_nr(VV_SHELL_ERROR);
bool n = false;
const char *const name = tv_get_string(&argvars[0]);
for (size_t i = 0; i < ARRAY_SIZE(has_list); i++) {
@@ -4611,6 +4339,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
n = true;
}
+ set_vim_var_nr(VV_SHELL_ERROR, save_shell_error);
rettv->vval.v_number = n;
}
@@ -4746,34 +4475,7 @@ static void f_haslocaldir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "hasmapto()" function
- */
-static void f_hasmapto(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- const char *mode;
- const char *const name = tv_get_string(&argvars[0]);
- bool abbr = false;
- char buf[NUMBUFLEN];
- if (argvars[1].v_type == VAR_UNKNOWN) {
- mode = "nvo";
- } else {
- mode = tv_get_string_buf(&argvars[1], buf);
- if (argvars[2].v_type != VAR_UNKNOWN) {
- abbr = tv_get_number(&argvars[2]);
- }
- }
-
- if (map_to_exists(name, mode, abbr)) {
- rettv->vval.v_number = true;
- } else {
- rettv->vval.v_number = false;
- }
-}
-
-/*
- * "histadd()" function
- */
+/// "histadd()" function
static void f_histadd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
HistoryType histype;
@@ -4796,9 +4498,7 @@ static void f_histadd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "histdel()" function
- */
+/// "histdel()" function
static void f_histdel(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int n;
@@ -4821,9 +4521,7 @@ static void f_histdel(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = n;
}
-/*
- * "histget()" function
- */
+/// "histget()" function
static void f_histget(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
HistoryType type;
@@ -4840,14 +4538,12 @@ static void f_histget(typval_T *argvars, typval_T *rettv, FunPtr fptr)
idx = (int)tv_get_number_chk(&argvars[1], NULL);
}
// -1 on type error
- rettv->vval.v_string = vim_strsave(get_history_entry(type, idx));
+ rettv->vval.v_string = (char *)vim_strsave(get_history_entry(type, idx));
}
rettv->v_type = VAR_STRING;
}
-/*
- * "histnr()" function
- */
+/// "histnr()" function
static void f_histnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
const char *const history = tv_get_string_chk(&argvars[0]);
@@ -4860,37 +4556,29 @@ static void f_histnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = i;
}
-/*
- * "highlightID(name)" function
- */
+/// "highlightID(name)" function
static void f_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = syn_name2id(tv_get_string(&argvars[0]));
}
-/*
- * "highlight_exists()" function
- */
+/// "highlight_exists()" function
static void f_hlexists(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = highlight_exists(tv_get_string(&argvars[0]));
}
-/*
- * "hostname()" function
- */
+/// "hostname()" function
static void f_hostname(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
char hostname[256];
os_get_hostname(hostname, 256);
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = vim_strsave((char_u *)hostname);
+ rettv->vval.v_string = (char *)vim_strsave((char_u *)hostname);
}
-/*
- * iconv() function
- */
+/// iconv() function
static void f_iconv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
vimconv_T vimconv;
@@ -4908,9 +4596,9 @@ static void f_iconv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// If the encodings are equal, no conversion needed.
if (vimconv.vc_type == CONV_NONE) {
- rettv->vval.v_string = (char_u *)xstrdup(str);
+ rettv->vval.v_string = xstrdup(str);
} else {
- rettv->vval.v_string = string_convert(&vimconv, (char_u *)str, NULL);
+ rettv->vval.v_string = (char *)string_convert(&vimconv, (char_u *)str, NULL);
}
convert_setup(&vimconv, NULL, NULL);
@@ -4918,9 +4606,7 @@ static void f_iconv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
xfree(to);
}
-/*
- * "indent()" function
- */
+/// "indent()" function
static void f_indent(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
const linenr_T lnum = tv_get_lnum(argvars);
@@ -4931,9 +4617,7 @@ static void f_indent(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "index()" function
- */
+/// "index()" function
static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
long idx = 0;
@@ -5007,26 +4691,20 @@ static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static bool inputsecret_flag = false;
-/*
- * "input()" function
- * Also handles inputsecret() when inputsecret is set.
- */
+/// "input()" function
+/// Also handles inputsecret() when inputsecret is set.
static void f_input(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
get_user_input(argvars, rettv, FALSE, inputsecret_flag);
}
-/*
- * "inputdialog()" function
- */
+/// "inputdialog()" function
static void f_inputdialog(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
get_user_input(argvars, rettv, TRUE, inputsecret_flag);
}
-/*
- * "inputlist()" function
- */
+/// "inputlist()" function
static void f_inputlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int selected;
@@ -5057,7 +4735,6 @@ static void f_inputlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = selected;
}
-
static garray_T ga_userinput = { 0, 0, sizeof(tasave_T), 4, NULL };
/// "inputrestore()" function
@@ -5092,9 +4769,7 @@ static void f_inputsecret(typval_T *argvars, typval_T *rettv, FunPtr fptr)
inputsecret_flag = false;
}
-/*
- * "insert()" function
- */
+/// "insert()" function
static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
list_T *l;
@@ -5166,43 +4841,37 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-// "interrupt()" function
+/// "interrupt()" function
static void f_interrupt(typval_T *argvars FUNC_ATTR_UNUSED, typval_T *rettv FUNC_ATTR_UNUSED,
FunPtr fptr FUNC_ATTR_UNUSED)
{
got_int = true;
}
-/*
- * "invert(expr)" function
- */
+/// "invert(expr)" function
static void f_invert(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = ~tv_get_number_chk(&argvars[0], NULL);
}
-/*
- * "isdirectory()" function
- */
+/// "isdirectory()" function
static void f_isdirectory(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = os_isdir((const char_u *)tv_get_string(&argvars[0]));
}
-/*
- * "islocked()" function
- */
+/// "islocked()" function
static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
lval_T lv;
dictitem_T *di;
rettv->vval.v_number = -1;
- const char_u *const end = get_lval((char_u *)tv_get_string(&argvars[0]),
- NULL,
- &lv, false, false,
- GLV_NO_AUTOLOAD|GLV_READ_ONLY,
- FNE_CHECK_START);
+ const char_u *const end = (char_u *)get_lval((char *)tv_get_string(&argvars[0]),
+ NULL,
+ &lv, false, false,
+ GLV_NO_AUTOLOAD|GLV_READ_ONLY,
+ FNE_CHECK_START);
if (end != NULL && lv.ll_name != NULL) {
if (*end != NUL) {
emsg(_(e_trailing));
@@ -5234,7 +4903,7 @@ static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr)
clear_lval(&lv);
}
-// "isinf()" function
+/// "isinf()" function
static void f_isinf(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
if (argvars[0].v_type == VAR_FLOAT
@@ -5243,7 +4912,7 @@ static void f_isinf(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-// "isnan()" function
+/// "isnan()" function
static void f_isnan(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = argvars[0].v_type == VAR_FLOAT
@@ -5257,19 +4926,16 @@ static void f_id(typval_T *argvars, typval_T *rettv, FunPtr fptr)
const int len = vim_vsnprintf_typval(NULL, 0, "%p", dummy_ap, argvars);
rettv->v_type = VAR_STRING;
rettv->vval.v_string = xmalloc(len + 1);
- vim_vsnprintf_typval((char *)rettv->vval.v_string, len + 1, "%p",
- dummy_ap, argvars);
+ vim_vsnprintf_typval(rettv->vval.v_string, len + 1, "%p", dummy_ap, argvars);
}
-/*
- * "items(dict)" function
- */
+/// "items(dict)" function
static void f_items(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
dict_list(argvars, rettv, 2);
}
-// "jobpid(id)" function
+/// "jobpid(id)" function
static void f_jobpid(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->v_type = VAR_NUMBER;
@@ -5293,7 +4959,7 @@ static void f_jobpid(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = proc->pid;
}
-// "jobresize(job, width, height)" function
+/// "jobresize(job, width, height)" function
static void f_jobresize(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->v_type = VAR_NUMBER;
@@ -5310,7 +4976,6 @@ static void f_jobresize(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
-
Channel *data = find_job(argvars[0].vval.v_number, true);
if (!data) {
return;
@@ -5402,6 +5067,16 @@ static dict_T *create_environment(const dictitem_T *job_env, const bool clear_en
tv_dict_add_str(env, S_LEN("TERM"), pty_term_name);
}
+ // Set $NVIM (in the child process) to v:servername. #3118
+ char *nvim_addr = get_vim_var_str(VV_SEND_SERVER);
+ if (nvim_addr[0] != '\0') {
+ dictitem_T *dv = tv_dict_find(env, S_LEN("NVIM"));
+ if (dv) {
+ tv_dict_item_remove(env, dv);
+ }
+ tv_dict_add_str(env, S_LEN("NVIM"), nvim_addr);
+ }
+
if (job_env) {
#ifdef WIN32
TV_DICT_ITER(job_env->di_tv.vval.v_dict, var, {
@@ -5440,7 +5115,7 @@ static dict_T *create_environment(const dictitem_T *job_env, const bool clear_en
return env;
}
-// "jobstart()" function
+/// "jobstart()" function
static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->v_type = VAR_NUMBER;
@@ -5465,7 +5140,6 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
-
dict_T *job_opts = NULL;
bool detach = false;
bool rpc = false;
@@ -5561,7 +5235,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-// "jobstop()" function
+/// "jobstop()" function
static void f_jobstop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->v_type = VAR_NUMBER;
@@ -5594,7 +5268,7 @@ static void f_jobstop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-// "jobwait(ids[, timeout])" function
+/// "jobwait(ids[, timeout])" function
static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->v_type = VAR_NUMBER;
@@ -5693,9 +5367,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_list = rv;
}
-/*
- * "join()" function
- */
+/// "join()" function
static void f_join(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
if (argvars[0].v_type != VAR_LIST) {
@@ -5713,7 +5385,7 @@ static void f_join(typval_T *argvars, typval_T *rettv, FunPtr fptr)
ga_init(&ga, (int)sizeof(char), 80);
tv_list_join(&ga, argvars[0].vval.v_list, sep);
ga_append(&ga, NUL);
- rettv->vval.v_string = (char_u *)ga.ga_data;
+ rettv->vval.v_string = ga.ga_data;
} else {
rettv->vval.v_string = NULL;
}
@@ -5757,20 +5429,16 @@ static void f_json_decode(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_json_encode(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = (char_u *)encode_tv2json(&argvars[0], NULL);
+ rettv->vval.v_string = encode_tv2json(&argvars[0], NULL);
}
-/*
- * "keys()" function
- */
+/// "keys()" function
static void f_keys(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
dict_list(argvars, rettv, 0);
}
-/*
- * "last_buffer_nr()" function.
- */
+/// "last_buffer_nr()" function.
static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int n = 0;
@@ -5784,9 +5452,7 @@ static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = n;
}
-/*
- * "len()" function
- */
+/// "len()" function
static void f_len(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
switch (argvars[0].v_type) {
@@ -5830,19 +5496,17 @@ static void libcall_common(typval_T *argvars, typval_T *rettv, int out_type)
return;
}
- const char *libname = (char *)argvars[0].vval.v_string;
- const char *funcname = (char *)argvars[1].vval.v_string;
+ const char *libname = argvars[0].vval.v_string;
+ const char *funcname = argvars[1].vval.v_string;
VarType in_type = argvars[2].v_type;
// input variables
- char *str_in = (in_type == VAR_STRING)
- ? (char *)argvars[2].vval.v_string : NULL;
+ char *str_in = (in_type == VAR_STRING) ? argvars[2].vval.v_string : NULL;
int int_in = argvars[2].vval.v_number;
// output variables
- char **str_out = (out_type == VAR_STRING)
- ? (char **)&rettv->vval.v_string : NULL;
+ char **str_out = (out_type == VAR_STRING) ? &rettv->vval.v_string : NULL;
int int_out = 0;
bool success = os_libcall(libname, funcname,
@@ -5859,23 +5523,19 @@ static void libcall_common(typval_T *argvars, typval_T *rettv, int out_type)
}
}
-/*
- * "libcall()" function
- */
+/// "libcall()" function
static void f_libcall(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
libcall_common(argvars, rettv, VAR_STRING);
}
-/*
- * "libcallnr()" function
- */
+/// "libcallnr()" function
static void f_libcallnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
libcall_common(argvars, rettv, VAR_NUMBER);
}
-// "line(string, [winid])" function
+/// "line(string, [winid])" function
static void f_line(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
linenr_T lnum = 0;
@@ -5883,23 +5543,21 @@ static void f_line(typval_T *argvars, typval_T *rettv, FunPtr fptr)
int fnum;
if (argvars[1].v_type != VAR_UNKNOWN) {
- tabpage_T *tp;
- win_T *save_curwin;
- tabpage_T *save_curtab;
-
// use window specified in the second argument
- win_T *wp = win_id2wp_tp(&argvars[1], &tp);
+ int id = (int)tv_get_number(&argvars[1]);
+ tabpage_T *tp;
+ win_T *wp = win_id2wp_tp(id, &tp);
if (wp != NULL && tp != NULL) {
- if (switch_win_noblock(&save_curwin, &save_curtab, wp, tp, true)
- == OK) {
+ switchwin_T switchwin;
+ if (switch_win_noblock(&switchwin, wp, tp, true) == OK) {
check_cursor();
- fp = var2fpos(&argvars[0], true, &fnum);
+ fp = var2fpos(&argvars[0], true, &fnum, false);
}
- restore_win_noblock(save_curwin, save_curtab, true);
+ restore_win_noblock(&switchwin, true);
}
} else {
// use current window
- fp = var2fpos(&argvars[0], true, &fnum);
+ fp = var2fpos(&argvars[0], true, &fnum, false);
}
if (fp != NULL) {
@@ -5908,9 +5566,7 @@ static void f_line(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = lnum;
}
-/*
- * "line2byte(lnum)" function
- */
+/// "line2byte(lnum)" function
static void f_line2byte(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
const linenr_T lnum = tv_get_lnum(argvars);
@@ -5924,9 +5580,7 @@ static void f_line2byte(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "lispindent(lnum)" function
- */
+/// "lispindent(lnum)" function
static void f_lispindent(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
const pos_T pos = curwin->w_cursor;
@@ -5940,7 +5594,7 @@ static void f_lispindent(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-// "list2str()" function
+/// "list2str()" function
static void f_list2str(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
garray_T ga;
@@ -5958,10 +5612,10 @@ static void f_list2str(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
ga_init(&ga, 1, 80);
- char_u buf[MB_MAXBYTES + 1];
+ char buf[MB_MAXBYTES + 1];
TV_LIST_ITER_CONST(l, li, {
- buf[utf_char2bytes(tv_get_number(TV_LIST_ITEM_TV(li)), buf)] = NUL;
+ buf[utf_char2bytes(tv_get_number(TV_LIST_ITEM_TV(li)), (char *)buf)] = NUL;
ga_concat(&ga, (char *)buf);
});
ga_append(&ga, NUL);
@@ -5969,82 +5623,12 @@ static void f_list2str(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_string = ga.ga_data;
}
-/*
- * "localtime()" function
- */
+/// "localtime()" function
static void f_localtime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = (varnumber_T)time(NULL);
}
-
-static void get_maparg(typval_T *argvars, typval_T *rettv, int exact)
-{
- char_u *keys_buf = NULL;
- char_u *rhs;
- LuaRef rhs_lua;
- int mode;
- int abbr = FALSE;
- int get_dict = FALSE;
- mapblock_T *mp;
- int buffer_local;
-
- // Return empty string for failure.
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = NULL;
-
- char_u *keys = (char_u *)tv_get_string(&argvars[0]);
- if (*keys == NUL) {
- return;
- }
-
- char buf[NUMBUFLEN];
- const char *which;
- if (argvars[1].v_type != VAR_UNKNOWN) {
- which = tv_get_string_buf_chk(&argvars[1], buf);
- if (argvars[2].v_type != VAR_UNKNOWN) {
- abbr = tv_get_number(&argvars[2]);
- if (argvars[3].v_type != VAR_UNKNOWN) {
- get_dict = tv_get_number(&argvars[3]);
- }
- }
- } else {
- which = "";
- }
- if (which == NULL) {
- return;
- }
-
- mode = get_map_mode((char_u **)&which, 0);
-
- keys = replace_termcodes(keys, STRLEN(keys), &keys_buf, true, true, true,
- CPO_TO_CPO_FLAGS);
- rhs = check_map(keys, mode, exact, false, abbr, &mp, &buffer_local, &rhs_lua);
- xfree(keys_buf);
-
- if (!get_dict) {
- // Return a string.
- if (rhs != NULL) {
- if (*rhs == NUL) {
- rettv->vval.v_string = vim_strsave((char_u *)"<Nop>");
- } else {
- rettv->vval.v_string = (char_u *)str2special_save((char *)rhs, false, false);
- }
- } else if (rhs_lua != LUA_NOREF) {
- size_t msglen = 100;
- char *msg = (char *)xmalloc(msglen);
- snprintf(msg, msglen, "<Lua function %d>", mp->m_luaref);
- rettv->vval.v_string = (char_u *)msg;
- }
- } else {
- tv_dict_alloc_ret(rettv);
- if (mp != NULL && (rhs != NULL || rhs_lua != LUA_NOREF)) {
- // Return a dictionary.
- mapblock_fill_dict(rettv->vval.v_dict, mp, buffer_local, true);
- }
- }
-}
-
/// luaeval() function implementation
static void f_luaeval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
FUNC_ATTR_NONNULL_ALL
@@ -6057,31 +5641,12 @@ static void f_luaeval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
nlua_typval_eval(cstr_as_string((char *)str), &argvars[1], rettv);
}
-/*
- * "map()" function
- */
+/// "map()" function
static void f_map(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
filter_map(argvars, rettv, TRUE);
}
-/*
- * "maparg()" function
- */
-static void f_maparg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- get_maparg(argvars, rettv, TRUE);
-}
-
-/*
- * "mapcheck()" function
- */
-static void f_mapcheck(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- get_maparg(argvars, rettv, FALSE);
-}
-
-
static void find_some_match(typval_T *const argvars, typval_T *const rettv,
const SomeMatchType type)
{
@@ -6089,7 +5654,7 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv,
long len = 0;
char_u *expr = NULL;
regmatch_T regmatch;
- char_u *save_cpo;
+ char *save_cpo;
long start = 0;
long nth = 1;
colnr_T startcol = 0;
@@ -6101,7 +5666,7 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv,
// Make 'cpoptions' empty, the 'l' flag should not be used here.
save_cpo = p_cpo;
- p_cpo = (char_u *)"";
+ p_cpo = "";
rettv->vval.v_number = -1;
switch (type) {
@@ -6182,7 +5747,7 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv,
}
}
- regmatch.regprog = vim_regcomp((char_u *)pat, RE_MAGIC + RE_STRING);
+ regmatch.regprog = vim_regcomp((char *)pat, RE_MAGIC + RE_STRING);
if (regmatch.regprog != NULL) {
regmatch.rm_ic = p_ic;
@@ -6215,7 +5780,7 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv,
idx++;
} else {
startcol = (colnr_T)(regmatch.startp[0]
- + utfc_ptr2len(regmatch.startp[0]) - str);
+ + utfc_ptr2len((char *)regmatch.startp[0]) - str);
if (startcol > (colnr_T)len || str + startcol <= regmatch.startp[0]) {
match = false;
break;
@@ -6261,9 +5826,9 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv,
if (l != NULL) {
tv_copy(TV_LIST_ITEM_TV(li), rettv);
} else {
- rettv->vval.v_string = (char_u *)xmemdupz((const char *)regmatch.startp[0],
- (size_t)(regmatch.endp[0] -
- regmatch.startp[0]));
+ rettv->vval.v_string = xmemdupz((const char *)regmatch.startp[0],
+ (size_t)(regmatch.endp[0] -
+ regmatch.startp[0]));
}
break;
case kSomeMatch:
@@ -6297,166 +5862,25 @@ theend:
p_cpo = save_cpo;
}
-/*
- * "match()" function
- */
+/// "match()" function
static void f_match(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
find_some_match(argvars, rettv, kSomeMatch);
}
-/*
- * "matchadd()" function
- */
-static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- char grpbuf[NUMBUFLEN];
- char patbuf[NUMBUFLEN];
- // group
- const char *const grp = tv_get_string_buf_chk(&argvars[0], grpbuf);
- // pattern
- const char *const pat = tv_get_string_buf_chk(&argvars[1], patbuf);
- // default priority
- int prio = 10;
- int id = -1;
- bool error = false;
- const char *conceal_char = NULL;
- win_T *win = curwin;
-
- rettv->vval.v_number = -1;
-
- if (grp == NULL || pat == NULL) {
- return;
- }
- if (argvars[2].v_type != VAR_UNKNOWN) {
- prio = tv_get_number_chk(&argvars[2], &error);
- if (argvars[3].v_type != VAR_UNKNOWN) {
- id = tv_get_number_chk(&argvars[3], &error);
- if (argvars[4].v_type != VAR_UNKNOWN
- && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL) {
- return;
- }
- }
- }
- if (error) {
- return;
- }
- if (id >= 1 && id <= 3) {
- semsg(_("E798: ID is reserved for \":match\": %" PRId64), (int64_t)id);
- return;
- }
-
- rettv->vval.v_number = match_add(win, grp, pat, prio, id, NULL, conceal_char);
-}
-
-static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- rettv->vval.v_number = -1;
-
- char buf[NUMBUFLEN];
- const char *const group = tv_get_string_buf_chk(&argvars[0], buf);
- if (group == NULL) {
- return;
- }
-
- if (argvars[1].v_type != VAR_LIST) {
- semsg(_(e_listarg), "matchaddpos()");
- return;
- }
-
- list_T *l;
- l = argvars[1].vval.v_list;
- if (l == NULL) {
- return;
- }
-
- bool error = false;
- int prio = 10;
- int id = -1;
- const char *conceal_char = NULL;
- win_T *win = curwin;
-
- if (argvars[2].v_type != VAR_UNKNOWN) {
- prio = tv_get_number_chk(&argvars[2], &error);
- if (argvars[3].v_type != VAR_UNKNOWN) {
- id = tv_get_number_chk(&argvars[3], &error);
- if (argvars[4].v_type != VAR_UNKNOWN
- && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL) {
- return;
- }
- }
- }
- if (error == true) {
- return;
- }
-
- // id == 3 is ok because matchaddpos() is supposed to substitute :3match
- if (id == 1 || id == 2) {
- semsg(_("E798: ID is reserved for \"match\": %" PRId64), (int64_t)id);
- return;
- }
-
- rettv->vval.v_number = match_add(win, group, NULL, prio, id, l, conceal_char);
-}
-
-/*
- * "matcharg()" function
- */
-static void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- const int id = tv_get_number(&argvars[0]);
-
- tv_list_alloc_ret(rettv, (id >= 1 && id <= 3
- ? 2
- : 0));
-
- if (id >= 1 && id <= 3) {
- matchitem_T *const m = get_match(curwin, id);
-
- if (m != NULL) {
- tv_list_append_string(rettv->vval.v_list,
- (const char *)syn_id2name(m->hlg_id), -1);
- tv_list_append_string(rettv->vval.v_list, (const char *)m->pattern, -1);
- } else {
- tv_list_append_string(rettv->vval.v_list, NULL, 0);
- tv_list_append_string(rettv->vval.v_list, NULL, 0);
- }
- }
-}
-
-/*
- * "matchdelete()" function
- */
-static void f_matchdelete(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- win_T *win = get_optional_window(argvars, 1);
- if (win == NULL) {
- rettv->vval.v_number = -1;
- } else {
- rettv->vval.v_number = match_delete(win,
- (int)tv_get_number(&argvars[0]), true);
- }
-}
-
-/*
- * "matchend()" function
- */
+/// "matchend()" function
static void f_matchend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
find_some_match(argvars, rettv, kSomeMatchEnd);
}
-/*
- * "matchlist()" function
- */
+/// "matchlist()" function
static void f_matchlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
find_some_match(argvars, rettv, kSomeMatchList);
}
-/*
- * "matchstr()" function
- */
+/// "matchstr()" function
static void f_matchstr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
find_some_match(argvars, rettv, kSomeMatchStr);
@@ -6517,25 +5941,19 @@ static void max_min(const typval_T *const tv, typval_T *const rettv, const bool
rettv->vval.v_number = n;
}
-/*
- * "max()" function
- */
+/// "max()" function
static void f_max(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
max_min(argvars, rettv, TRUE);
}
-/*
- * "min()" function
- */
+/// "min()" function
static void f_min(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
max_min(argvars, rettv, FALSE);
}
-/*
- * "mkdir()" function
- */
+/// "mkdir()" function
static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int prot = 0755; // -V536
@@ -6551,9 +5969,9 @@ static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
- if (*path_tail((char_u *)dir) == NUL) {
+ if (*path_tail(dir) == NUL) {
// Remove trailing slashes.
- *path_tail_with_sep((char_u *)dir) = NUL;
+ *path_tail_with_sep((char *)dir) = NUL;
}
if (argvars[1].v_type != VAR_UNKNOWN) {
@@ -6583,15 +6001,17 @@ static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// "mode()" function
static void f_mode(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char *mode = get_mode();
+ char buf[MODE_MAX_LENGTH];
+
+ get_mode(buf);
// Clear out the minor mode when the argument is not a non-zero number or
// non-empty string.
if (!non_zero_arg(&argvars[0])) {
- mode[1] = NUL;
+ buf[1] = NUL;
}
- rettv->vval.v_string = (char_u *)mode;
+ rettv->vval.v_string = xstrdup(buf);
rettv->v_type = VAR_STRING;
}
@@ -6750,9 +6170,7 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "nextnonblank()" function
- */
+/// "nextnonblank()" function
static void f_nextnonblank(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
linenr_T lnum;
@@ -6762,16 +6180,14 @@ static void f_nextnonblank(typval_T *argvars, typval_T *rettv, FunPtr fptr)
lnum = 0;
break;
}
- if (*skipwhite(ml_get(lnum)) != NUL) {
+ if (*skipwhite((char *)ml_get(lnum)) != NUL) {
break;
}
}
rettv->vval.v_number = lnum;
}
-/*
- * "nr2char()" function
- */
+/// "nr2char()" function
static void f_nr2char(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
if (argvars[1].v_type != VAR_UNKNOWN) {
@@ -6796,37 +6212,42 @@ static void f_nr2char(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
char buf[MB_MAXBYTES];
- const int len = utf_char2bytes((int)num, (char_u *)buf);
+ const int len = utf_char2bytes((int)num, buf);
rettv->v_type = VAR_STRING;
rettv->vval.v_string = xmemdupz(buf, (size_t)len);
}
-/*
- * "or(expr, expr)" function
- */
+/// "or(expr, expr)" function
static void f_or(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL)
| tv_get_number_chk(&argvars[1], NULL);
}
-/*
- * "pathshorten()" function
- */
+/// "pathshorten()" function
static void f_pathshorten(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
+ int trim_len = 1;
+
+ if (argvars[1].v_type != VAR_UNKNOWN) {
+ trim_len = (int)tv_get_number(&argvars[1]);
+ if (trim_len < 1) {
+ trim_len = 1;
+ }
+ }
+
rettv->v_type = VAR_STRING;
- const char *const s = tv_get_string_chk(&argvars[0]);
- if (!s) {
- return;
+ const char_u *p = (char_u *)tv_get_string_chk(&argvars[0]);
+ if (p == NULL) {
+ rettv->vval.v_string = NULL;
+ } else {
+ rettv->vval.v_string = (char *)vim_strsave(p);
+ shorten_dir_len((char_u *)rettv->vval.v_string, trim_len);
}
- rettv->vval.v_string = shorten_dir((char_u *)xstrdup(s));
}
-/*
- * "pow()" function
- */
+/// "pow()" function
static void f_pow(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
float_T fx;
@@ -6840,25 +6261,21 @@ static void f_pow(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "prevnonblank()" function
- */
+/// "prevnonblank()" function
static void f_prevnonblank(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
linenr_T lnum = tv_get_lnum(argvars);
if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) {
lnum = 0;
} else {
- while (lnum >= 1 && *skipwhite(ml_get(lnum)) == NUL) {
+ while (lnum >= 1 && *skipwhite((char *)ml_get(lnum)) == NUL) {
lnum--;
}
}
rettv->vval.v_number = lnum;
}
-/*
- * "printf()" function
- */
+/// "printf()" function
static void f_printf(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->v_type = VAR_STRING;
@@ -6874,14 +6291,14 @@ static void f_printf(typval_T *argvars, typval_T *rettv, FunPtr fptr)
len = vim_vsnprintf_typval(NULL, 0, fmt, dummy_ap, argvars + 1);
if (!did_emsg) {
char *s = xmalloc(len + 1);
- rettv->vval.v_string = (char_u *)s;
+ rettv->vval.v_string = s;
(void)vim_vsnprintf_typval(s, len + 1, fmt, dummy_ap, argvars + 1);
}
did_emsg |= saved_did_emsg;
}
}
-// "prompt_setcallback({buffer}, {callback})" function
+/// "prompt_setcallback({buffer}, {callback})" function
static void f_prompt_setcallback(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
buf_T *buf;
@@ -6905,7 +6322,7 @@ static void f_prompt_setcallback(typval_T *argvars, typval_T *rettv, FunPtr fptr
buf->b_prompt_callback = prompt_callback;
}
-// "prompt_setinterrupt({buffer}, {callback})" function
+/// "prompt_setinterrupt({buffer}, {callback})" function
static void f_prompt_setinterrupt(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
buf_T *buf;
@@ -6930,7 +6347,7 @@ static void f_prompt_setinterrupt(typval_T *argvars, typval_T *rettv, FunPtr fpt
}
/// "prompt_getprompt({buffer})" function
-void f_prompt_getprompt(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_prompt_getprompt(typval_T *argvars, typval_T *rettv, FunPtr fptr)
FUNC_ATTR_NONNULL_ALL
{
// return an empty string by default, e.g. it's not a prompt buffer
@@ -6946,38 +6363,33 @@ void f_prompt_getprompt(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
- rettv->vval.v_string = vim_strsave(buf_prompt_text(buf));
+ rettv->vval.v_string = (char *)vim_strsave(buf_prompt_text(buf));
}
-// "prompt_setprompt({buffer}, {text})" function
+/// "prompt_setprompt({buffer}, {text})" function
static void f_prompt_setprompt(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- buf_T *buf;
- const char_u *text;
-
if (check_secure()) {
return;
}
- buf = tv_get_buf(&argvars[0], false);
+ buf_T *buf = tv_get_buf(&argvars[0], false);
if (buf == NULL) {
return;
}
- text = (const char_u *)tv_get_string(&argvars[1]);
+ const char *text = tv_get_string(&argvars[1]);
xfree(buf->b_prompt_text);
- buf->b_prompt_text = vim_strsave(text);
+ buf->b_prompt_text = xstrdup(text);
}
-// "pum_getpos()" function
+/// "pum_getpos()" function
static void f_pum_getpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
tv_dict_alloc_ret(rettv);
pum_set_event_info(rettv->vval.v_dict);
}
-/*
- * "pumvisible()" function
- */
+/// "pumvisible()" function
static void f_pumvisible(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
if (pum_visible()) {
@@ -6985,50 +6397,183 @@ static void f_pumvisible(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "pyeval()" function
- */
-static void f_pyeval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+/// "py3eval()" and "pyxeval()" functions (always python3)
+static void f_py3eval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- script_host_eval("python", argvars, rettv);
+ script_host_eval("python3", argvars, rettv);
}
-/*
- * "py3eval()" function
- */
-static void f_py3eval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void init_srand(uint32_t *const x)
+ FUNC_ATTR_NONNULL_ALL
{
- script_host_eval("python3", argvars, rettv);
+#ifndef MSWIN
+ static int dev_urandom_state = NOTDONE; // FAIL or OK once tried
+
+ if (dev_urandom_state != FAIL) {
+ const int fd = os_open("/dev/urandom", O_RDONLY, 0);
+ struct {
+ union {
+ uint32_t number;
+ char bytes[sizeof(uint32_t)];
+ } contents;
+ } buf;
+
+ // Attempt reading /dev/urandom.
+ if (fd == -1) {
+ dev_urandom_state = FAIL;
+ } else {
+ buf.contents.number = 0;
+ if (read(fd, buf.contents.bytes, sizeof(uint32_t)) != sizeof(uint32_t)) {
+ dev_urandom_state = FAIL;
+ } else {
+ dev_urandom_state = OK;
+ *x = buf.contents.number;
+ }
+ os_close(fd);
+ }
+ }
+ if (dev_urandom_state != OK) {
+ // Reading /dev/urandom doesn't work, fall back to time().
+#endif
+ // uncrustify:off
+ *x = time(NULL);
+#ifndef MSWIN
+ }
+#endif
+ // uncrustify:on
}
-// "pyxeval()" function
-static void f_pyxeval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static inline uint32_t splitmix32(uint32_t *const x)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_ALWAYS_INLINE
{
- init_pyxversion();
- if (p_pyx == 2) {
- f_pyeval(argvars, rettv, NULL);
+ uint32_t z = (*x += 0x9e3779b9);
+ z = (z ^ (z >> 16)) * 0x85ebca6b;
+ z = (z ^ (z >> 13)) * 0xc2b2ae35;
+ return z ^ (z >> 16);
+}
+
+static inline uint32_t shuffle_xoshiro128starstar(uint32_t *const x, uint32_t *const y,
+ uint32_t *const z, uint32_t *const w)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_ALWAYS_INLINE
+{
+#define ROTL(x, k) (((x) << (k)) | ((x) >> (32 - (k))))
+ const uint32_t result = ROTL(*y * 5, 7) * 9;
+ const uint32_t t = *y << 9;
+ *z ^= *x;
+ *w ^= *y;
+ *y ^= *z;
+ *x ^= *w;
+ *z ^= t;
+ *w = ROTL(*w, 11);
+#undef ROTL
+ return result;
+}
+
+/// "rand()" function
+static void f_rand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ uint32_t result;
+
+ if (argvars[0].v_type == VAR_UNKNOWN) {
+ static uint32_t gx, gy, gz, gw;
+ static bool initialized = false;
+
+ // When no argument is given use the global seed list.
+ if (!initialized) {
+ // Initialize the global seed list.
+ uint32_t x;
+ init_srand(&x);
+
+ gx = splitmix32(&x);
+ gy = splitmix32(&x);
+ gz = splitmix32(&x);
+ gw = splitmix32(&x);
+ initialized = true;
+ }
+
+ result = shuffle_xoshiro128starstar(&gx, &gy, &gz, &gw);
+ } else if (argvars[0].v_type == VAR_LIST) {
+ list_T *const l = argvars[0].vval.v_list;
+ if (tv_list_len(l) != 4) {
+ goto theend;
+ }
+
+ typval_T *const tvx = TV_LIST_ITEM_TV(tv_list_find(l, 0L));
+ typval_T *const tvy = TV_LIST_ITEM_TV(tv_list_find(l, 1L));
+ typval_T *const tvz = TV_LIST_ITEM_TV(tv_list_find(l, 2L));
+ typval_T *const tvw = TV_LIST_ITEM_TV(tv_list_find(l, 3L));
+ if (tvx->v_type != VAR_NUMBER) {
+ goto theend;
+ }
+ if (tvy->v_type != VAR_NUMBER) {
+ goto theend;
+ }
+ if (tvz->v_type != VAR_NUMBER) {
+ goto theend;
+ }
+ if (tvw->v_type != VAR_NUMBER) {
+ goto theend;
+ }
+ uint32_t x = tvx->vval.v_number;
+ uint32_t y = tvy->vval.v_number;
+ uint32_t z = tvz->vval.v_number;
+ uint32_t w = tvw->vval.v_number;
+
+ result = shuffle_xoshiro128starstar(&x, &y, &z, &w);
+
+ tvx->vval.v_number = (varnumber_T)x;
+ tvy->vval.v_number = (varnumber_T)y;
+ tvz->vval.v_number = (varnumber_T)z;
+ tvw->vval.v_number = (varnumber_T)w;
} else {
- f_py3eval(argvars, rettv, NULL);
+ goto theend;
}
+
+ rettv->v_type = VAR_NUMBER;
+ rettv->vval.v_number = (varnumber_T)result;
+ return;
+
+theend:
+ semsg(_(e_invarg2), tv_get_string(&argvars[0]));
+ rettv->v_type = VAR_NUMBER;
+ rettv->vval.v_number = -1;
+}
+
+/// "srand()" function
+static void f_srand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ uint32_t x = 0;
+
+ tv_list_alloc_ret(rettv, 4);
+ if (argvars[0].v_type == VAR_UNKNOWN) {
+ init_srand(&x);
+ } else {
+ bool error = false;
+ x = tv_get_number_chk(&argvars[0], &error);
+ if (error) {
+ return;
+ }
+ }
+
+ tv_list_append_number(rettv->vval.v_list, (varnumber_T)splitmix32(&x));
+ tv_list_append_number(rettv->vval.v_list, (varnumber_T)splitmix32(&x));
+ tv_list_append_number(rettv->vval.v_list, (varnumber_T)splitmix32(&x));
+ tv_list_append_number(rettv->vval.v_list, (varnumber_T)splitmix32(&x));
}
-///
/// "perleval()" function
-///
static void f_perleval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
script_host_eval("perl", argvars, rettv);
}
-// "rubyeval()" function
+/// "rubyeval()" function
static void f_rubyeval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
script_host_eval("ruby", argvars, rettv);
}
-/*
- * "range()" function
- */
+/// "range()" function
static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
varnumber_T start;
@@ -7063,19 +6608,25 @@ static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-// Evaluate "expr" for readdir().
-static varnumber_T readdir_checkitem(typval_T *expr, const char *name)
+/// Evaluate "expr" (= "context") for readdir().
+static varnumber_T readdir_checkitem(void *context, const char *name)
+ FUNC_ATTR_NONNULL_ALL
{
+ typval_T *expr = (typval_T *)context;
typval_T save_val;
typval_T rettv;
typval_T argv[2];
varnumber_T retval = 0;
bool error = false;
+ if (expr->v_type == VAR_UNKNOWN) {
+ return 1;
+ }
+
prepare_vimvar(VV_VAL, &save_val);
set_vim_var_string(VV_VAL, name, -1);
argv[0].v_type = VAR_STRING;
- argv[0].vval.v_string = (char_u *)name;
+ argv[0].vval.v_string = (char *)name;
if (eval_expr_typval(expr, argv, 1, &rettv) == FAIL) {
goto theend;
@@ -7094,65 +6645,25 @@ theend:
return retval;
}
-// "readdir()" function
+/// "readdir()" function
static void f_readdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- typval_T *expr;
- const char *path;
- garray_T ga;
- Directory dir;
-
tv_list_alloc_ret(rettv, kListLenUnknown);
- path = tv_get_string(&argvars[0]);
- expr = &argvars[1];
- ga_init(&ga, (int)sizeof(char *), 20);
- if (!os_scandir(&dir, path)) {
- smsg(_(e_notopen), path);
- } else {
- for (;;) {
- bool ignore;
-
- path = os_scandir_next(&dir);
- if (path == NULL) {
- break;
- }
-
- ignore = (path[0] == '.'
- && (path[1] == NUL || (path[1] == '.' && path[2] == NUL)));
- if (!ignore && expr->v_type != VAR_UNKNOWN) {
- varnumber_T r = readdir_checkitem(expr, path);
-
- if (r < 0) {
- break;
- }
- if (r == 0) {
- ignore = true;
- }
- }
-
- if (!ignore) {
- ga_grow(&ga, 1);
- ((char **)ga.ga_data)[ga.ga_len++] = xstrdup(path);
- }
- }
-
- os_closedir(&dir);
- }
-
- if (rettv->vval.v_list != NULL && ga.ga_len > 0) {
- sort_strings((char_u **)ga.ga_data, ga.ga_len);
+ const char *path = tv_get_string(&argvars[0]);
+ typval_T *expr = &argvars[1];
+ garray_T ga;
+ int ret = readdir_core(&ga, path, (void *)expr, readdir_checkitem);
+ if (ret == OK && ga.ga_len > 0) {
for (int i = 0; i < ga.ga_len; i++) {
- path = ((const char **)ga.ga_data)[i];
- tv_list_append_string(rettv->vval.v_list, path, -1);
+ const char *p = ((const char **)ga.ga_data)[i];
+ tv_list_append_string(rettv->vval.v_list, p, -1);
}
}
ga_clear_strings(&ga);
}
-/*
- * "readfile()" function
- */
+/// "readfile()" function
static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
bool binary = false;
@@ -7250,7 +6761,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
tv_list_append_owned_tv(l, (typval_T) {
.v_type = VAR_STRING,
.v_lock = VAR_UNLOCKED,
- .vval.v_string = s,
+ .vval.v_string = (char *)s,
});
start = p + 1; // Step over newline.
@@ -7386,13 +6897,13 @@ static void f_getreginfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-// "reg_executing()" function
+/// "reg_executing()" function
static void f_reg_executing(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
return_register(reg_executing, rettv);
}
-// "reg_recording()" function
+/// "reg_recording()" function
static void f_reg_recording(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
return_register(reg_recording, rettv);
@@ -7490,13 +7001,11 @@ static void f_reltimestr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
if (list2proftime(&argvars[0], &tm) == OK) {
- rettv->vval.v_string = (char_u *)xstrdup(profile_msg(tm));
+ rettv->vval.v_string = xstrdup(profile_msg(tm));
}
}
-/*
- * "remove()" function
- */
+/// "remove()" function
static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
list_T *l;
@@ -7630,9 +7139,7 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "rename({from}, {to})" function
- */
+/// "rename({from}, {to})" function
static void f_rename(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
if (check_secure()) {
@@ -7644,9 +7151,7 @@ static void f_rename(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "repeat()" function
- */
+/// "repeat()" function
static void f_repeat(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
varnumber_T n = tv_get_number(&argvars[1]);
@@ -7679,13 +7184,11 @@ static void f_repeat(typval_T *argvars, typval_T *rettv, FunPtr fptr)
memmove(r + i * slen, p, slen);
}
- rettv->vval.v_string = (char_u *)r;
+ rettv->vval.v_string = r;
}
}
-/*
- * "resolve()" function
- */
+/// "resolve()" function
static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->v_type = VAR_STRING;
@@ -7765,11 +7268,11 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr)
q[-1] = NUL;
}
- q = (char *)path_tail((char_u *)p);
+ q = path_tail(p);
if (q > p && *q == NUL) {
// Ignore trailing path separator.
q[-1] = NUL;
- q = (char *)path_tail((char_u *)p);
+ q = path_tail(p);
}
if (q > p && !path_is_absolute((const char_u *)buf)) {
// Symlink is relative to directory of argument. Replace the
@@ -7777,7 +7280,7 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr)
const size_t p_len = strlen(p);
const size_t buf_len = strlen(buf);
p = xrealloc(p, p_len + buf_len + 1);
- memcpy(path_tail((char_u *)p), buf, buf_len + 1);
+ memcpy(path_tail(p), buf, buf_len + 1);
} else {
xfree(p);
p = xstrdup(buf);
@@ -7838,11 +7341,11 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (!has_trailing_pathsep) {
q = p + strlen(p);
if (after_pathsep(p, q)) {
- *path_tail_with_sep((char_u *)p) = NUL;
+ *path_tail_with_sep(p) = NUL;
}
}
- rettv->vval.v_string = (char_u *)p;
+ rettv->vval.v_string = p;
xfree(buf);
}
# else
@@ -7851,12 +7354,10 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr)
# endif
#endif
- simplify_filename(rettv->vval.v_string);
+ simplify_filename((char_u *)rettv->vval.v_string);
}
-/*
- * "reverse({list})" function
- */
+/// "reverse({list})" function
static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
if (argvars[0].v_type == VAR_BLOB) {
@@ -7881,6 +7382,102 @@ static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
+/// "reduce(list, { accumulator, element -> value } [, initial])" function
+static void f_reduce(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ if (argvars[0].v_type != VAR_LIST && argvars[0].v_type != VAR_BLOB) {
+ emsg(_(e_listblobreq));
+ return;
+ }
+
+ const char *func_name;
+ partial_T *partial = NULL;
+ if (argvars[1].v_type == VAR_FUNC) {
+ func_name = argvars[1].vval.v_string;
+ } else if (argvars[1].v_type == VAR_PARTIAL) {
+ partial = argvars[1].vval.v_partial;
+ func_name = partial_name(partial);
+ } else {
+ func_name = tv_get_string(&argvars[1]);
+ }
+ if (*func_name == NUL) {
+ return; // type error or empty name
+ }
+
+ funcexe_T funcexe = FUNCEXE_INIT;
+ funcexe.evaluate = true;
+ funcexe.partial = partial;
+
+ typval_T initial;
+ typval_T argv[3];
+ if (argvars[0].v_type == VAR_LIST) {
+ list_T *const l = argvars[0].vval.v_list;
+ const listitem_T *li;
+
+ if (argvars[2].v_type == VAR_UNKNOWN) {
+ if (tv_list_len(l) == 0) {
+ semsg(_(e_reduceempty), "List");
+ return;
+ }
+ const listitem_T *const first = tv_list_first(l);
+ initial = *TV_LIST_ITEM_TV(first);
+ li = TV_LIST_ITEM_NEXT(l, first);
+ } else {
+ initial = argvars[2];
+ li = tv_list_first(l);
+ }
+
+ tv_copy(&initial, rettv);
+
+ if (l != NULL) {
+ const VarLockStatus prev_locked = tv_list_locked(l);
+ const int called_emsg_start = called_emsg;
+
+ tv_list_set_lock(l, VAR_FIXED); // disallow the list changing here
+ for (; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) {
+ argv[0] = *rettv;
+ argv[1] = *TV_LIST_ITEM_TV(li);
+ rettv->v_type = VAR_UNKNOWN;
+ const int r = call_func((char *)func_name, -1, rettv, 2, argv, &funcexe);
+ tv_clear(&argv[0]);
+ if (r == FAIL || called_emsg != called_emsg_start) {
+ break;
+ }
+ }
+ tv_list_set_lock(l, prev_locked);
+ }
+ } else {
+ const blob_T *const b = argvars[0].vval.v_blob;
+ int i;
+
+ if (argvars[2].v_type == VAR_UNKNOWN) {
+ if (tv_blob_len(b) == 0) {
+ semsg(_(e_reduceempty), "Blob");
+ return;
+ }
+ initial.v_type = VAR_NUMBER;
+ initial.vval.v_number = tv_blob_get(b, 0);
+ i = 1;
+ } else if (argvars[2].v_type != VAR_NUMBER) {
+ emsg(_(e_number_exp));
+ return;
+ } else {
+ initial = argvars[2];
+ i = 0;
+ }
+
+ tv_copy(&initial, rettv);
+ for (; i < tv_blob_len(b); i++) {
+ argv[0] = *rettv;
+ argv[1].v_type = VAR_NUMBER;
+ argv[1].vval.v_number = tv_blob_get(b, i);
+ if (call_func((char *)func_name, -1, rettv, 2, argv, &funcexe) == FAIL) {
+ return;
+ }
+ }
+ }
+}
+
#define SP_NOMOVE 0x01 ///< don't move cursor
#define SP_REPEAT 0x02 ///< repeat to find outer pair
#define SP_RETCOUNT 0x04 ///< return matchcount
@@ -7890,11 +7487,10 @@ static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
#define SP_END 0x40 ///< leave cursor at end of match
#define SP_COLUMN 0x80 ///< start at cursor column
-/*
- * Get flags for a search function.
- * Possibly sets "p_ws".
- * Returns BACKWARD, FORWARD or zero (for an error).
- */
+/// Get flags for a search function.
+/// Possibly sets "p_ws".
+///
+/// @return BACKWARD, FORWARD or zero (for an error).
static int get_search_arg(typval_T *varp, int *flagsp)
{
int dir = FORWARD;
@@ -7952,7 +7548,7 @@ static int get_search_arg(typval_T *varp, int *flagsp)
return dir;
}
-// Shared by search() and searchpos() functions.
+/// Shared by search() and searchpos() functions.
static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp)
{
int flags;
@@ -7967,6 +7563,7 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp)
int options = SEARCH_KEEP;
int subpatnum;
searchit_arg_T sia;
+ bool use_skip = false;
const char *const pat = tv_get_string(&argvars[0]);
dir = get_search_arg(&argvars[1], flagsp); // May set p_ws.
@@ -7984,7 +7581,7 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp)
options |= SEARCH_COL;
}
- // Optional arguments: line number to stop searching and timeout.
+ // Optional arguments: line number to stop searching, timeout and skip.
if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) {
lnum_stop = tv_get_number_chk(&argvars[2], NULL);
if (lnum_stop < 0) {
@@ -7995,6 +7592,7 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp)
if (time_limit < 0) {
goto theend;
}
+ use_skip = eval_expr_valid_arg(&argvars[4]);
}
}
@@ -8014,11 +7612,49 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp)
}
pos = save_cursor = curwin->w_cursor;
+ pos_T firstpos = { 0 };
memset(&sia, 0, sizeof(sia));
sia.sa_stop_lnum = (linenr_T)lnum_stop;
sia.sa_tm = &tm;
- subpatnum = searchit(curwin, curbuf, &pos, NULL, dir, (char_u *)pat, 1,
- options, RE_SEARCH, &sia);
+
+ // Repeat until {skip} returns false.
+ for (;;) {
+ subpatnum
+ = searchit(curwin, curbuf, &pos, NULL, dir, (char_u *)pat, 1, options, RE_SEARCH, &sia);
+ // finding the first match again means there is no match where {skip}
+ // evaluates to zero.
+ if (firstpos.lnum != 0 && equalpos(pos, firstpos)) {
+ subpatnum = FAIL;
+ }
+
+ if (subpatnum == FAIL || !use_skip) {
+ // didn't find it or no skip argument
+ break;
+ }
+ firstpos = pos;
+
+ // If the skip expression matches, ignore this match.
+ {
+ const pos_T save_pos = curwin->w_cursor;
+
+ curwin->w_cursor = pos;
+ bool err = false;
+ const bool do_skip = eval_expr_to_bool(&argvars[4], &err);
+ curwin->w_cursor = save_pos;
+ if (err) {
+ // Evaluating {skip} caused an error, break here.
+ subpatnum = FAIL;
+ break;
+ }
+ if (!do_skip) {
+ break;
+ }
+ }
+
+ // clear the start flag to avoid getting stuck here
+ options &= ~SEARCH_START;
+ }
+
if (subpatnum != FAIL) {
if (flags & SP_SUBPAT) {
retval = subpatnum;
@@ -8051,7 +7687,7 @@ theend:
return retval;
}
-// "rpcnotify()" function
+/// "rpcnotify()" function
static void f_rpcnotify(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->v_type = VAR_NUMBER;
@@ -8086,7 +7722,7 @@ static void f_rpcnotify(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = 1;
}
-// "rpcrequest()" function
+/// "rpcrequest()" function
static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->v_type = VAR_NUMBER;
@@ -8114,7 +7750,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
sctx_T save_current_sctx;
- uint8_t *save_sourcing_name, *save_autocmd_fname, *save_autocmd_match;
+ char *save_sourcing_name, *save_autocmd_fname, *save_autocmd_match;
linenr_T save_sourcing_lnum;
int save_autocmd_bufnr;
funccal_entry_T funccal_entry;
@@ -8139,13 +7775,13 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
set_current_funccal((funccall_T *)(provider_caller_scope.funccalp));
}
-
Error err = ERROR_INIT;
uint64_t chan_id = (uint64_t)argvars[0].vval.v_number;
const char *method = tv_get_string(&argvars[1]);
- Object result = rpc_send_call(chan_id, method, args, &err);
+ ArenaMem res_mem = NULL;
+ Object result = rpc_send_call(chan_id, method, args, &res_mem, &err);
if (l_provider_call_nesting) {
current_sctx = save_current_sctx;
@@ -8180,11 +7816,11 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
end:
- api_free_object(result);
+ arena_mem_free(res_mem, NULL);
api_clear_error(&err);
}
-// "rpcstart()" function (DEPRECATED)
+/// "rpcstart()" function (DEPRECATED)
static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->v_type = VAR_NUMBER;
@@ -8228,7 +7864,7 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
char **argv = xmalloc(sizeof(char_u *) * argvl);
// Copy program name
- argv[0] = xstrdup((char *)argvars[0].vval.v_string);
+ argv[0] = xstrdup(argvars[0].vval.v_string);
int i = 1;
// Copy arguments to the vector
@@ -8251,7 +7887,7 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-// "rpcstop()" function
+/// "rpcstop()" function
static void f_rpcstop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->v_type = VAR_NUMBER;
@@ -8281,54 +7917,57 @@ static void f_rpcstop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-// "screenattr()" function
+/// "screenattr()" function
static void f_screenattr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int c;
+ ScreenGrid *grid;
int row = (int)tv_get_number_chk(&argvars[0], NULL) - 1;
int col = (int)tv_get_number_chk(&argvars[1], NULL) - 1;
- if (row < 0 || row >= default_grid.Rows
- || col < 0 || col >= default_grid.Columns) {
+
+ screenchar_adjust(&grid, &row, &col);
+
+ if (row < 0 || row >= grid->rows || col < 0 || col >= grid->cols) {
c = -1;
} else {
- ScreenGrid *grid = &default_grid;
- screenchar_adjust_grid(&grid, &row, &col);
c = grid->attrs[grid->line_offset[row] + col];
}
rettv->vval.v_number = c;
}
-// "screenchar()" function
+/// "screenchar()" function
static void f_screenchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int c;
+ ScreenGrid *grid;
int row = tv_get_number_chk(&argvars[0], NULL) - 1;
int col = tv_get_number_chk(&argvars[1], NULL) - 1;
- if (row < 0 || row >= default_grid.Rows
- || col < 0 || col >= default_grid.Columns) {
+
+ screenchar_adjust(&grid, &row, &col);
+
+ if (row < 0 || row >= grid->rows || col < 0 || col >= grid->cols) {
c = -1;
} else {
- ScreenGrid *grid = &default_grid;
- screenchar_adjust_grid(&grid, &row, &col);
- c = utf_ptr2char(grid->chars[grid->line_offset[row] + col]);
+ c = utf_ptr2char((char *)grid->chars[grid->line_offset[row] + col]);
}
rettv->vval.v_number = c;
}
-// "screenchars()" function
+/// "screenchars()" function
static void f_screenchars(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
+ ScreenGrid *grid;
int row = tv_get_number_chk(&argvars[0], NULL) - 1;
int col = tv_get_number_chk(&argvars[1], NULL) - 1;
- if (row < 0 || row >= default_grid.Rows
- || col < 0 || col >= default_grid.Columns) {
+
+ screenchar_adjust(&grid, &row, &col);
+
+ if (row < 0 || row >= grid->rows || col < 0 || col >= grid->cols) {
tv_list_alloc_ret(rettv, 0);
return;
}
- ScreenGrid *grid = &default_grid;
- screenchar_adjust_grid(&grid, &row, &col);
int pcc[MAX_MCO];
int c = utfc_ptr2char(grid->chars[grid->line_offset[row] + col], pcc);
int composing_len = 0;
@@ -8342,9 +7981,9 @@ static void f_screenchars(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-// "screencol()" function
-//
-// First column is 1 to be consistent with virtcol().
+/// "screencol()" function
+///
+/// First column is 1 to be consistent with virtcol().
static void f_screencol(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = ui_current_col() + 1;
@@ -8376,29 +8015,32 @@ static void f_screenpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
tv_dict_add_nr(dict, S_LEN("endcol"), ecol);
}
-// "screenrow()" function
+/// "screenrow()" function
static void f_screenrow(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = ui_current_row() + 1;
}
-// "screenstring()" function
+/// "screenstring()" function
static void f_screenstring(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_string = NULL;
rettv->v_type = VAR_STRING;
+
+ ScreenGrid *grid;
int row = tv_get_number_chk(&argvars[0], NULL) - 1;
int col = tv_get_number_chk(&argvars[1], NULL) - 1;
- if (row < 0 || row >= default_grid.Rows
- || col < 0 || col >= default_grid.Columns) {
+
+ screenchar_adjust(&grid, &row, &col);
+
+ if (row < 0 || row >= grid->rows || col < 0 || col >= grid->cols) {
return;
}
- ScreenGrid *grid = &default_grid;
- screenchar_adjust_grid(&grid, &row, &col);
- rettv->vval.v_string = vim_strsave(grid->chars[grid->line_offset[row] + col]);
+
+ rettv->vval.v_string = (char *)vim_strsave(grid->chars[grid->line_offset[row] + col]);
}
-// "search()" function
+/// "search()" function
static void f_search(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int flags = 0;
@@ -8406,9 +8048,7 @@ static void f_search(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = search_cmn(argvars, NULL, &flags);
}
-/*
- * "searchdecl()" function
- */
+/// "searchdecl()" function
static void f_searchdecl(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int locally = 1;
@@ -8430,9 +8070,7 @@ static void f_searchdecl(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * Used by searchpair() and searchpairpos()
- */
+/// Used by searchpair() and searchpairpos()
static int searchpair_cmn(typval_T *argvars, pos_T *match_pos)
{
bool save_p_ws = p_ws;
@@ -8478,13 +8116,9 @@ static int searchpair_cmn(typval_T *argvars, pos_T *match_pos)
|| argvars[4].v_type == VAR_UNKNOWN) {
skip = NULL;
} else {
+ // Type is checked later.
skip = &argvars[4];
- if (skip->v_type != VAR_FUNC
- && skip->v_type != VAR_PARTIAL
- && skip->v_type != VAR_STRING) {
- semsg(_(e_invarg2), tv_get_string(&argvars[4]));
- goto theend; // Type error.
- }
+
if (argvars[5].v_type != VAR_UNKNOWN) {
lnum_stop = tv_get_number_chk(&argvars[5], NULL);
if (lnum_stop < 0) {
@@ -8510,17 +8144,13 @@ theend:
return retval;
}
-/*
- * "searchpair()" function
- */
+/// "searchpair()" function
static void f_searchpair(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = searchpair_cmn(argvars, NULL);
}
-/*
- * "searchpairpos()" function
- */
+/// "searchpairpos()" function
static void f_searchpairpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
pos_T match_pos;
@@ -8556,7 +8186,7 @@ long do_searchpair(const char *spat, const char *mpat, const char *epat, int dir
long time_limit)
FUNC_ATTR_NONNULL_ARG(1, 2, 3)
{
- char_u *save_cpo;
+ char *save_cpo;
char_u *pat, *pat2 = NULL, *pat3 = NULL;
long retval = 0;
pos_T pos;
@@ -8572,7 +8202,7 @@ long do_searchpair(const char *spat, const char *mpat, const char *epat, int dir
// Make 'cpoptions' empty, the 'l' flag should not be used here.
save_cpo = p_cpo;
- p_cpo = empty_option;
+ p_cpo = (char *)empty_option;
// Set the time limit, if there is one.
tm = profile_setlimit(time_limit);
@@ -8595,10 +8225,7 @@ long do_searchpair(const char *spat, const char *mpat, const char *epat, int dir
}
if (skip != NULL) {
- // Empty string means to not use the skip expression.
- if (skip->v_type == VAR_STRING || skip->v_type == VAR_FUNC) {
- use_skip = skip->vval.v_string != NULL && *skip->vval.v_string != NUL;
- }
+ use_skip = eval_expr_valid_arg(skip);
}
save_cursor = curwin->w_cursor;
@@ -8699,19 +8326,17 @@ long do_searchpair(const char *spat, const char *mpat, const char *epat, int dir
xfree(pat2);
xfree(pat3);
- if (p_cpo == empty_option) {
+ if ((char_u *)p_cpo == empty_option) {
p_cpo = save_cpo;
} else {
// Darn, evaluating the {skip} expression changed the value.
- free_string_option(save_cpo);
+ free_string_option((char_u *)save_cpo);
}
return retval;
}
-/*
- * "searchpos()" function
- */
+/// "searchpos()" function
static void f_searchpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
pos_T match_pos;
@@ -8765,7 +8390,7 @@ static void f_serverstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
address = xstrdup(tv_get_string(argvars));
}
} else {
- address = server_address_new();
+ address = server_address_new(NULL);
}
int result = server_start(address);
@@ -8781,7 +8406,7 @@ static void f_serverstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// "localhost:" will now have a port), return the final value to the user.
size_t n;
char **addrs = server_address_list(&n);
- rettv->vval.v_string = (char_u *)addrs[n - 1];
+ rettv->vval.v_string = addrs[n - 1];
n--;
for (size_t i = 0; i < n; i++) {
@@ -8805,7 +8430,7 @@ static void f_serverstop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = 0;
if (argvars[0].vval.v_string) {
- bool rv = server_stop((char *)argvars[0].vval.v_string);
+ bool rv = server_stop(argvars[0].vval.v_string);
rettv->vval.v_number = (rv ? 1 : 0);
}
}
@@ -8825,9 +8450,7 @@ static void f_setbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "setbufvar()" function
- */
+/// "setbufvar()" function
static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
if (check_secure()
@@ -8871,6 +8494,49 @@ static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
+/// Set the cursor or mark position.
+/// If 'charpos' is TRUE, then use the column number as a character offset.
+/// Otherwise use the column number as a byte offset.
+static void set_position(typval_T *argvars, typval_T *rettv, bool charpos)
+{
+ pos_T pos;
+ int fnum;
+ colnr_T curswant = -1;
+
+ rettv->vval.v_number = -1;
+ const char *const name = tv_get_string_chk(argvars);
+ if (name != NULL) {
+ if (list2fpos(&argvars[1], &pos, &fnum, &curswant, charpos) == OK) {
+ if (pos.col != MAXCOL && --pos.col < 0) {
+ pos.col = 0;
+ }
+ if (name[0] == '.' && name[1] == NUL) {
+ // set cursor; "fnum" is ignored
+ curwin->w_cursor = pos;
+ if (curswant >= 0) {
+ curwin->w_curswant = curswant - 1;
+ curwin->w_set_curswant = false;
+ }
+ check_cursor();
+ rettv->vval.v_number = 0;
+ } else if (name[0] == '\'' && name[1] != NUL && name[2] == NUL) {
+ // set mark
+ if (setmark_pos((uint8_t)name[1], &pos, fnum, NULL) == OK) {
+ rettv->vval.v_number = 0;
+ }
+ } else {
+ emsg(_(e_invarg));
+ }
+ }
+ }
+}
+
+/// "setcharpos()" function
+static void f_setcharpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ set_position(argvars, rettv, true);
+}
+
static void f_setcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
dict_T *d;
@@ -8886,7 +8552,7 @@ static void f_setcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (csearch != NULL) {
int pcc[MAX_MCO];
const int c = utfc_ptr2char(csearch, pcc);
- set_last_csearch(c, csearch, utfc_ptr2len(csearch));
+ set_last_csearch(c, csearch, utfc_ptr2len((char *)csearch));
}
di = tv_dict_find(d, S_LEN("forward"));
@@ -8901,9 +8567,7 @@ static void f_setcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "setcmdpos()" function
- */
+/// "setcmdpos()" function
static void f_setcmdpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
const int pos = (int)tv_get_number(&argvars[0]) - 1;
@@ -8913,6 +8577,12 @@ static void f_setcmdpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
+/// "setcursorcharpos" function
+static void f_setcursorcharpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ set_cursorpos(argvars, rettv, true);
+}
+
/// "setenv()" function
static void f_setenv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
@@ -8959,9 +8629,7 @@ static void f_setfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = os_setperm(fname, mode) == OK;
}
-/*
- * "setline()" function
- */
+/// "setline()" function
static void f_setline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
linenr_T lnum = tv_get_lnum(&argvars[0]);
@@ -8988,7 +8656,7 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv)
{
static char *e_invact = N_("E927: Invalid action: '%s'");
const char *title = NULL;
- int action = ' ';
+ char action = ' ';
static int recursive = 0;
rettv->vval.v_number = -1;
dict_T *what = NULL;
@@ -9043,15 +8711,13 @@ skip_args:
recursive++;
list_T *const l = list_arg->vval.v_list;
- if (set_errorlist(wp, l, action, (char_u *)title, what) == OK) {
+ if (set_errorlist(wp, l, action, (char *)title, what) == OK) {
rettv->vval.v_number = 0;
}
recursive--;
}
-/*
- * "setloclist()" function
- */
+/// "setloclist()" function
static void f_setloclist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
win_T *win;
@@ -9064,151 +8730,13 @@ static void f_setloclist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "setmatches()" function
- */
-static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- dict_T *d;
- list_T *s = NULL;
- win_T *win = get_optional_window(argvars, 1);
-
- rettv->vval.v_number = -1;
- if (argvars[0].v_type != VAR_LIST) {
- emsg(_(e_listreq));
- return;
- }
- if (win == NULL) {
- return;
- }
-
- list_T *const l = argvars[0].vval.v_list;
- // To some extent make sure that we are dealing with a list from
- // "getmatches()".
- int li_idx = 0;
- TV_LIST_ITER_CONST(l, li, {
- if (TV_LIST_ITEM_TV(li)->v_type != VAR_DICT
- || (d = TV_LIST_ITEM_TV(li)->vval.v_dict) == NULL) {
- semsg(_("E474: List item %d is either not a dictionary "
- "or an empty one"), li_idx);
- return;
- }
- if (!(tv_dict_find(d, S_LEN("group")) != NULL
- && (tv_dict_find(d, S_LEN("pattern")) != NULL
- || tv_dict_find(d, S_LEN("pos1")) != NULL)
- && tv_dict_find(d, S_LEN("priority")) != NULL
- && tv_dict_find(d, S_LEN("id")) != NULL)) {
- semsg(_("E474: List item %d is missing one of the required keys"),
- li_idx);
- return;
- }
- li_idx++;
- });
-
- clear_matches(win);
- bool match_add_failed = false;
- TV_LIST_ITER_CONST(l, li, {
- int i = 0;
-
- d = TV_LIST_ITEM_TV(li)->vval.v_dict;
- dictitem_T *const di = tv_dict_find(d, S_LEN("pattern"));
- if (di == NULL) {
- if (s == NULL) {
- s = tv_list_alloc(9);
- }
-
- // match from matchaddpos()
- for (i = 1; i < 9; i++) {
- char buf[30]; // use 30 to avoid compiler warning
- snprintf(buf, sizeof(buf), "pos%d", i);
- dictitem_T *const pos_di = tv_dict_find(d, buf, -1);
- if (pos_di != NULL) {
- if (pos_di->di_tv.v_type != VAR_LIST) {
- return;
- }
-
- tv_list_append_tv(s, &pos_di->di_tv);
- tv_list_ref(s);
- } else {
- break;
- }
- }
- }
-
- // Note: there are three number buffers involved:
- // - group_buf below.
- // - numbuf in tv_dict_get_string().
- // - mybuf in tv_get_string().
- //
- // If you change this code make sure that buffers will not get
- // accidentally reused.
- char group_buf[NUMBUFLEN];
- const char *const group = tv_dict_get_string_buf(d, "group", group_buf);
- const int priority = (int)tv_dict_get_number(d, "priority");
- const int id = (int)tv_dict_get_number(d, "id");
- dictitem_T *const conceal_di = tv_dict_find(d, S_LEN("conceal"));
- const char *const conceal = (conceal_di != NULL
- ? tv_get_string(&conceal_di->di_tv)
- : NULL);
- if (i == 0) {
- if (match_add(win, group,
- tv_dict_get_string(d, "pattern", false),
- priority, id, NULL, conceal) != id) {
- match_add_failed = true;
- }
- } else {
- if (match_add(win, group, NULL, priority, id, s, conceal) != id) {
- match_add_failed = true;
- }
- tv_list_unref(s);
- s = NULL;
- }
- });
- if (!match_add_failed) {
- rettv->vval.v_number = 0;
- }
-}
-
-/*
- * "setpos()" function
- */
+/// "setpos()" function
static void f_setpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- pos_T pos;
- int fnum;
- colnr_T curswant = -1;
-
- rettv->vval.v_number = -1;
- const char *const name = tv_get_string_chk(argvars);
- if (name != NULL) {
- if (list2fpos(&argvars[1], &pos, &fnum, &curswant) == OK) {
- if (pos.col != MAXCOL && --pos.col < 0) {
- pos.col = 0;
- }
- if (name[0] == '.' && name[1] == NUL) {
- // set cursor; "fnum" is ignored
- curwin->w_cursor = pos;
- if (curswant >= 0) {
- curwin->w_curswant = curswant - 1;
- curwin->w_set_curswant = false;
- }
- check_cursor();
- rettv->vval.v_number = 0;
- } else if (name[0] == '\'' && name[1] != NUL && name[2] == NUL) {
- // set mark
- if (setmark_pos((uint8_t)name[1], &pos, fnum) == OK) {
- rettv->vval.v_number = 0;
- }
- } else {
- emsg(_(e_invarg));
- }
- }
- }
+ set_position(argvars, rettv, false);
}
-/*
- * "setqflist()" function
- */
+/// "setqflist()" function
static void f_setqflist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
set_qf_ll_list(NULL, argvars, rettv);
@@ -9244,12 +8772,9 @@ static int get_yank_type(char_u **const pp, MotionType *const yank_type, long *c
return OK;
}
-/*
- * "setreg()" function
- */
+/// "setreg()" function
static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- int regname;
bool append = false;
MotionType yank_type;
long block_len;
@@ -9263,13 +8788,13 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (strregname == NULL) {
return; // Type error; errmsg already given.
}
- regname = (uint8_t)(*strregname);
+ char regname = (uint8_t)(*strregname);
if (regname == 0 || regname == '@') {
regname = '"';
}
const typval_T *regcontents = NULL;
- int pointreg = 0;
+ char pointreg = 0;
if (argvars[1].v_type == VAR_DICT) {
dict_T *const d = argvars[1].vval.v_dict;
@@ -9387,14 +8912,11 @@ free_lstval:
if (set_unnamed) {
// Discard the result. We already handle the error case.
- if (op_reg_set_previous(regname)) {
- }
+ op_reg_set_previous(regname);
}
}
-/*
- * "settabvar()" function
- */
+/// "settabvar()" function
static void f_settabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = 0;
@@ -9425,21 +8947,19 @@ static void f_settabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "settabwinvar()" function
- */
+/// "settabwinvar()" function
static void f_settabwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
setwinvar(argvars, rettv, 1);
}
-// "settagstack()" function
+/// "settagstack()" function
static void f_settagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
static char *e_invact2 = N_("E962: Invalid action: '%s'");
win_T *wp;
dict_T *d;
- int action = 'r';
+ char action = 'r';
rettv->vval.v_number = -1;
@@ -9486,9 +9006,7 @@ static void f_settagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "setwinvar()" function
- */
+/// "setwinvar()" function
static void f_setwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
setwinvar(argvars, rettv, 0);
@@ -9501,26 +9019,22 @@ static void f_sha256(typval_T *argvars, typval_T *rettv, FunPtr fptr)
const char *hash = sha256_bytes((const uint8_t *)p, strlen(p), NULL, 0);
// make a copy of the hash (sha256_bytes returns a static buffer)
- rettv->vval.v_string = (char_u *)xstrdup(hash);
+ rettv->vval.v_string = xstrdup(hash);
rettv->v_type = VAR_STRING;
}
-/*
- * "shellescape({string})" function
- */
+/// "shellescape({string})" function
static void f_shellescape(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
const bool do_special = non_zero_arg(&argvars[1]);
- rettv->vval.v_string = vim_strsave_shellescape((const char_u *)tv_get_string(
- &argvars[0]), do_special,
- do_special);
+ rettv->vval.v_string =
+ (char *)vim_strsave_shellescape((const char_u *)tv_get_string(&argvars[0]), do_special,
+ do_special);
rettv->v_type = VAR_STRING;
}
-/*
- * shiftwidth() function
- */
+/// shiftwidth() function
static void f_shiftwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = 0;
@@ -9538,280 +9052,12 @@ static void f_shiftwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = get_sw_value(curbuf);
}
-/// "sign_define()" function
-static void f_sign_define(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- const char *name;
-
- if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN) {
- // Define multiple signs
- tv_list_alloc_ret(rettv, kListLenMayKnow);
-
- sign_define_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
- return;
- }
-
- // Define a single sign
- rettv->vval.v_number = -1;
-
- name = tv_get_string_chk(&argvars[0]);
- if (name == NULL) {
- return;
- }
-
- if (argvars[1].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_DICT) {
- emsg(_(e_dictreq));
- return;
- }
-
- rettv->vval.v_number = sign_define_from_dict(name,
- argvars[1].v_type ==
- VAR_DICT ? argvars[1].vval.v_dict : NULL);
-}
-
-/// "sign_getdefined()" function
-static void f_sign_getdefined(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- const char *name = NULL;
-
- tv_list_alloc_ret(rettv, 0);
-
- if (argvars[0].v_type != VAR_UNKNOWN) {
- name = tv_get_string(&argvars[0]);
- }
-
- sign_getlist((const char_u *)name, rettv->vval.v_list);
-}
-
-/// "sign_getplaced()" function
-static void f_sign_getplaced(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- buf_T *buf = NULL;
- dict_T *dict;
- dictitem_T *di;
- linenr_T lnum = 0;
- int sign_id = 0;
- const char *group = NULL;
- bool notanum = false;
-
- tv_list_alloc_ret(rettv, 0);
-
- if (argvars[0].v_type != VAR_UNKNOWN) {
- // get signs placed in the specified buffer
- buf = get_buf_arg(&argvars[0]);
- if (buf == NULL) {
- return;
- }
-
- if (argvars[1].v_type != VAR_UNKNOWN) {
- if (argvars[1].v_type != VAR_DICT
- || ((dict = argvars[1].vval.v_dict) == NULL)) {
- emsg(_(e_dictreq));
- return;
- }
- if ((di = tv_dict_find(dict, "lnum", -1)) != NULL) {
- // get signs placed at this line
- lnum = (linenr_T)tv_get_number_chk(&di->di_tv, &notanum);
- if (notanum) {
- return;
- }
- (void)lnum;
- lnum = tv_get_lnum(&di->di_tv);
- }
- if ((di = tv_dict_find(dict, "id", -1)) != NULL) {
- // get sign placed with this identifier
- sign_id = (int)tv_get_number_chk(&di->di_tv, &notanum);
- if (notanum) {
- return;
- }
- }
- if ((di = tv_dict_find(dict, "group", -1)) != NULL) {
- group = tv_get_string_chk(&di->di_tv);
- if (group == NULL) {
- return;
- }
- if (*group == '\0') { // empty string means global group
- group = NULL;
- }
- }
- }
- }
-
- sign_get_placed(buf, lnum, sign_id, (const char_u *)group,
- rettv->vval.v_list);
-}
-
-/// "sign_jump()" function
-static void f_sign_jump(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- int sign_id;
- char *sign_group = NULL;
- buf_T *buf;
- bool notanum = false;
-
- rettv->vval.v_number = -1;
-
- // Sign identifier
- sign_id = (int)tv_get_number_chk(&argvars[0], &notanum);
- if (notanum) {
- return;
- }
- if (sign_id <= 0) {
- emsg(_(e_invarg));
- return;
- }
-
- // Sign group
- const char *sign_group_chk = tv_get_string_chk(&argvars[1]);
- if (sign_group_chk == NULL) {
- return;
- }
- if (sign_group_chk[0] == '\0') {
- sign_group = NULL; // global sign group
- } else {
- sign_group = xstrdup(sign_group_chk);
- }
-
- // Buffer to place the sign
- buf = get_buf_arg(&argvars[2]);
- if (buf == NULL) {
- goto cleanup;
- }
-
- rettv->vval.v_number = sign_jump(sign_id, (char_u *)sign_group, buf);
-
-cleanup:
- xfree(sign_group);
-}
-
-/// "sign_place()" function
-static void f_sign_place(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- dict_T *dict = NULL;
-
- rettv->vval.v_number = -1;
-
- if (argvars[4].v_type != VAR_UNKNOWN
- && (argvars[4].v_type != VAR_DICT
- || ((dict = argvars[4].vval.v_dict) == NULL))) {
- emsg(_(e_dictreq));
- return;
- }
-
- rettv->vval.v_number = sign_place_from_dict(&argvars[0], &argvars[1], &argvars[2], &argvars[3],
- dict);
-}
-
-/// "sign_placelist()" function. Place multiple signs.
-static void f_sign_placelist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- int sign_id;
-
- tv_list_alloc_ret(rettv, kListLenMayKnow);
-
- if (argvars[0].v_type != VAR_LIST) {
- emsg(_(e_listreq));
- return;
- }
-
- // Process the List of sign attributes
- TV_LIST_ITER_CONST(argvars[0].vval.v_list, li, {
- sign_id = -1;
- if (TV_LIST_ITEM_TV(li)->v_type == VAR_DICT) {
- sign_id = sign_place_from_dict(NULL, NULL, NULL, NULL, TV_LIST_ITEM_TV(li)->vval.v_dict);
- } else {
- emsg(_(e_dictreq));
- }
- tv_list_append_number(rettv->vval.v_list, sign_id);
- });
-}
-
-/// "sign_undefine()" function
-static void f_sign_undefine(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- const char *name;
-
- if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN) {
- // Undefine multiple signs
- tv_list_alloc_ret(rettv, kListLenMayKnow);
-
- sign_undefine_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
- return;
- }
-
- rettv->vval.v_number = -1;
-
- if (argvars[0].v_type == VAR_UNKNOWN) {
- // Free all the signs
- free_signs();
- rettv->vval.v_number = 0;
- } else {
- // Free only the specified sign
- name = tv_get_string_chk(&argvars[0]);
- if (name == NULL) {
- return;
- }
-
- if (sign_undefine_by_name((const char_u *)name) == OK) {
- rettv->vval.v_number = 0;
- }
- }
-}
-
-/// "sign_unplace()" function
-static void f_sign_unplace(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- dict_T *dict = NULL;
-
- rettv->vval.v_number = -1;
-
- if (argvars[0].v_type != VAR_STRING) {
- emsg(_(e_invarg));
- return;
- }
-
- if (argvars[1].v_type != VAR_UNKNOWN) {
- if (argvars[1].v_type != VAR_DICT) {
- emsg(_(e_dictreq));
- return;
- }
- dict = argvars[1].vval.v_dict;
- }
-
- rettv->vval.v_number = sign_unplace_from_dict(&argvars[0], dict);
-}
-
-/// "sign_unplacelist()" function
-static void f_sign_unplacelist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- int retval;
-
- tv_list_alloc_ret(rettv, kListLenMayKnow);
-
- if (argvars[0].v_type != VAR_LIST) {
- emsg(_(e_listreq));
- return;
- }
-
- TV_LIST_ITER_CONST(argvars[0].vval.v_list, li, {
- retval = -1;
- if (TV_LIST_ITEM_TV(li)->v_type == VAR_DICT) {
- retval = sign_unplace_from_dict(NULL, TV_LIST_ITEM_TV(li)->vval.v_dict);
- } else {
- emsg(_(e_dictreq));
- }
- tv_list_append_number(rettv->vval.v_list, retval);
- });
-}
-
-/*
- * "simplify()" function
- */
+/// "simplify()" function
static void f_simplify(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
const char *const p = tv_get_string(&argvars[0]);
- rettv->vval.v_string = (char_u *)xstrdup(p);
- simplify_filename(rettv->vval.v_string); // Simplify in place.
+ rettv->vval.v_string = xstrdup(p);
+ simplify_filename((char_u *)rettv->vval.v_string); // Simplify in place.
rettv->v_type = VAR_STRING;
}
@@ -9883,9 +9129,7 @@ static sortinfo_T *sortinfo = NULL;
#define ITEM_COMPARE_FAIL 999
-/*
- * Compare functions for f_sort() and f_uniq() below.
- */
+/// Compare functions for f_sort() and f_uniq() below.
static int item_compare(const void *s1, const void *s2, bool keep_zero)
{
ListSortItem *const si1 = (ListSortItem *)s1;
@@ -9924,7 +9168,7 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero)
if (tv2->v_type != VAR_STRING || sortinfo->item_compare_numeric) {
p1 = "'";
} else {
- p1 = (char *)tv1->vval.v_string;
+ p1 = tv1->vval.v_string;
}
} else {
tofree1 = p1 = encode_tv2string(tv1, NULL);
@@ -9933,7 +9177,7 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero)
if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric) {
p2 = "'";
} else {
- p2 = (char *)tv2->vval.v_string;
+ p2 = tv2->vval.v_string;
}
} else {
tofree2 = p2 = encode_tv2string(tv2, NULL);
@@ -10014,7 +9258,7 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero)
funcexe.evaluate = true;
funcexe.partial = partial;
funcexe.selfdict = sortinfo->item_compare_selfdict;
- res = call_func((const char_u *)func_name, -1, &rettv, 2, argv, &funcexe);
+ res = call_func(func_name, -1, &rettv, 2, argv, &funcexe);
tv_clear(&argv[0]);
tv_clear(&argv[1]);
@@ -10022,6 +9266,11 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero)
res = ITEM_COMPARE_FAIL;
} else {
res = tv_get_number_chk(&rettv, &sortinfo->item_compare_func_err);
+ if (res > 0) {
+ res = 1;
+ } else if (res < 0) {
+ res = -1;
+ }
}
if (sortinfo->item_compare_func_err) {
res = ITEM_COMPARE_FAIL; // return value has wrong type
@@ -10049,9 +9298,7 @@ static int item_compare2_not_keeping_zero(const void *s1, const void *s2)
return item_compare2(s1, s2, false);
}
-/*
- * "sort({list})" function
- */
+/// "sort({list})" function
static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
{
ListSortItem *ptrs;
@@ -10209,7 +9456,6 @@ static void f_stdioopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
-
bool rpc = false;
CallbackReader on_stdin = CALLBACK_READER_INIT;
dict_T *opts = argvars[0].vval.v_dict;
@@ -10218,6 +9464,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;
@@ -10229,7 +9479,6 @@ static void f_stdioopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
semsg(e_stdiochan2, error);
}
-
rettv->vval.v_number = (varnumber_T)id;
rettv->v_type = VAR_NUMBER;
}
@@ -10240,7 +9489,7 @@ static void f_uniq(typval_T *argvars, typval_T *rettv, FunPtr fptr)
do_sort_uniq(argvars, rettv, false);
}
-// "reltimefloat()" function
+/// "reltimefloat()" function
static void f_reltimefloat(typval_T *argvars, typval_T *rettv, FunPtr fptr)
FUNC_ATTR_NONNULL_ALL
{
@@ -10253,19 +9502,15 @@ static void f_reltimefloat(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "soundfold({word})" function
- */
+/// "soundfold({word})" function
static void f_soundfold(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->v_type = VAR_STRING;
const char *const s = tv_get_string(&argvars[0]);
- rettv->vval.v_string = (char_u *)eval_soundfold(s);
+ rettv->vval.v_string = eval_soundfold(s);
}
-/*
- * "spellbadword()" function
- */
+/// "spellbadword()" function
static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
const char *word = "";
@@ -10324,9 +9569,7 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr)
NULL), -1);
}
-/*
- * "spellsuggest()" function
- */
+/// "spellsuggest()" function
static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
bool typeerr = false;
@@ -10346,26 +9589,24 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
- if (*curwin->w_s->b_p_spl != NUL) {
- const char *const str = tv_get_string(&argvars[0]);
- if (argvars[1].v_type != VAR_UNKNOWN) {
- maxcount = tv_get_number_chk(&argvars[1], &typeerr);
- if (maxcount <= 0) {
+ const char *const str = tv_get_string(&argvars[0]);
+ if (argvars[1].v_type != VAR_UNKNOWN) {
+ maxcount = tv_get_number_chk(&argvars[1], &typeerr);
+ if (maxcount <= 0) {
+ goto f_spellsuggest_return;
+ }
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ need_capital = tv_get_number_chk(&argvars[2], &typeerr);
+ if (typeerr) {
goto f_spellsuggest_return;
}
- if (argvars[2].v_type != VAR_UNKNOWN) {
- need_capital = tv_get_number_chk(&argvars[2], &typeerr);
- if (typeerr) {
- goto f_spellsuggest_return;
- }
- }
- } else {
- maxcount = 25;
}
-
- spell_suggest_list(&ga, (char_u *)str, maxcount, need_capital, false);
+ } else {
+ maxcount = 25;
}
+ spell_suggest_list(&ga, (char_u *)str, maxcount, need_capital, false);
+
f_spellsuggest_return:
tv_list_alloc_ret(rettv, (ptrdiff_t)ga.ga_len);
for (int i = 0; i < ga.ga_len; i++) {
@@ -10378,7 +9619,7 @@ f_spellsuggest_return:
static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *save_cpo;
+ char *save_cpo;
int match;
colnr_T col = 0;
bool keepempty = false;
@@ -10386,7 +9627,7 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// Make 'cpoptions' empty, the 'l' flag should not be used here.
save_cpo = p_cpo;
- p_cpo = (char_u *)"";
+ p_cpo = "";
const char *str = tv_get_string(&argvars[0]);
const char *pat = NULL;
@@ -10411,7 +9652,7 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
regmatch_T regmatch = {
- .regprog = vim_regcomp((char_u *)pat, RE_MAGIC + RE_STRING),
+ .regprog = vim_regcomp((char *)pat, RE_MAGIC + RE_STRING),
.startp = { NULL },
.endp = { NULL },
.rm_ic = false,
@@ -10443,7 +9684,7 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr)
col = 0;
} else {
// Don't get stuck at the same match.
- col = utfc_ptr2len(regmatch.endp[0]);
+ col = utfc_ptr2len((char *)regmatch.endp[0]);
}
str = (const char *)regmatch.endp[0];
}
@@ -10467,11 +9708,17 @@ static void f_stdpath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
if (strequal(p, "config")) {
- rettv->vval.v_string = (char_u *)get_xdg_home(kXDGConfigHome);
+ rettv->vval.v_string = get_xdg_home(kXDGConfigHome);
} else if (strequal(p, "data")) {
- rettv->vval.v_string = (char_u *)get_xdg_home(kXDGDataHome);
+ rettv->vval.v_string = get_xdg_home(kXDGDataHome);
} else if (strequal(p, "cache")) {
- rettv->vval.v_string = (char_u *)get_xdg_home(kXDGCacheHome);
+ rettv->vval.v_string = get_xdg_home(kXDGCacheHome);
+ } else if (strequal(p, "state")) {
+ rettv->vval.v_string = get_xdg_home(kXDGStateHome);
+ } else if (strequal(p, "log")) {
+ rettv->vval.v_string = get_xdg_home(kXDGStateHome);
+ } else if (strequal(p, "run")) {
+ rettv->vval.v_string = stdpaths_get_xdg_var(kXDGRuntimeDir);
} else if (strequal(p, "config_dirs")) {
get_xdg_var_list(kXDGConfigDirs, rettv);
} else if (strequal(p, "data_dirs")) {
@@ -10481,36 +9728,34 @@ static void f_stdpath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "str2float()" function
- */
+/// "str2float()" function
static void f_str2float(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- char_u *p = skipwhite((const char_u *)tv_get_string(&argvars[0]));
+ char *p = skipwhite(tv_get_string(&argvars[0]));
bool isneg = (*p == '-');
if (*p == '+' || *p == '-') {
p = skipwhite(p + 1);
}
- (void)string2float((char *)p, &rettv->vval.v_float);
+ (void)string2float(p, &rettv->vval.v_float);
if (isneg) {
rettv->vval.v_float *= -1;
}
rettv->v_type = VAR_FLOAT;
}
-// "str2list()" function
+/// "str2list()" function
static void f_str2list(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
tv_list_alloc_ret(rettv, kListLenUnknown);
const char_u *p = (const char_u *)tv_get_string(&argvars[0]);
- for (; *p != NUL; p += utf_ptr2len(p)) {
- tv_list_append_number(rettv->vval.v_list, utf_ptr2char(p));
+ for (; *p != NUL; p += utf_ptr2len((char *)p)) {
+ tv_list_append_number(rettv->vval.v_list, utf_ptr2char((char *)p));
}
}
-// "str2nr()" function
+/// "str2nr()" function
static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int base = 10;
@@ -10528,10 +9773,10 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
- char_u *p = skipwhite((const char_u *)tv_get_string(&argvars[0]));
+ char_u *p = (char_u *)skipwhite(tv_get_string(&argvars[0]));
bool isneg = (*p == '-');
if (*p == '+' || *p == '-') {
- p = skipwhite(p + 1);
+ p = (char_u *)skipwhite((char *)p + 1);
}
switch (base) {
case 2:
@@ -10553,9 +9798,7 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "strftime({format}[, {time}])" function
- */
+/// "strftime({format}[, {time}])" function
static void f_strftime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
time_t seconds;
@@ -10573,7 +9816,7 @@ static void f_strftime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
struct tm *curtime_ptr = os_localtime_r(&seconds, &curtime);
// MSVC returns NULL for an invalid value of seconds.
if (curtime_ptr == NULL) {
- rettv->vval.v_string = vim_strsave((char_u *)_("(Invalid)"));
+ rettv->vval.v_string = xstrdup(_("(Invalid)"));
} else {
vimconv_T conv;
char_u *enc;
@@ -10596,9 +9839,9 @@ static void f_strftime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
convert_setup(&conv, enc, p_enc);
if (conv.vc_type != CONV_NONE) {
- rettv->vval.v_string = string_convert(&conv, (char_u *)result_buf, NULL);
+ rettv->vval.v_string = (char *)string_convert(&conv, (char_u *)result_buf, NULL);
} else {
- rettv->vval.v_string = (char_u *)xstrdup(result_buf);
+ rettv->vval.v_string = xstrdup(result_buf);
}
// Release conversion descriptors.
@@ -10607,7 +9850,7 @@ static void f_strftime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-// "strgetchar()" function
+/// "strgetchar()" function
static void f_strgetchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = -1;
@@ -10627,17 +9870,15 @@ static void f_strgetchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
while (charidx >= 0 && byteidx < len) {
if (charidx == 0) {
- rettv->vval.v_number = utf_ptr2char((const char_u *)str + byteidx);
+ rettv->vval.v_number = utf_ptr2char(str + byteidx);
break;
}
charidx--;
- byteidx += utf_ptr2len((const char_u *)str + byteidx);
+ byteidx += utf_ptr2len(str + byteidx);
}
}
-/*
- * "stridx()" function
- */
+/// "stridx()" function
static void f_stridx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = -1;
@@ -10669,26 +9910,20 @@ static void f_stridx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "string()" function
- */
-void f_string(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+/// "string()" function
+static void f_string(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = (char_u *)encode_tv2string(&argvars[0], NULL);
+ rettv->vval.v_string = encode_tv2string(&argvars[0], NULL);
}
-/*
- * "strlen()" function
- */
+/// "strlen()" function
static void f_strlen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = (varnumber_T)strlen(tv_get_string(&argvars[0]));
}
-/*
- * "strchars()" function
- */
+/// "strchars()" function
static void f_strchars(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
const char *s = tv_get_string(&argvars[0]);
@@ -10711,9 +9946,7 @@ static void f_strchars(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "strdisplaywidth()" function
- */
+/// "strdisplaywidth()" function
static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
const char *const s = tv_get_string(&argvars[0]);
@@ -10726,17 +9959,15 @@ static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, (char_u *)s) - col);
}
-/*
- * "strwidth()" function
- */
+/// "strwidth()" function
static void f_strwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
const char *const s = tv_get_string(&argvars[0]);
- rettv->vval.v_number = (varnumber_T)mb_string2cells((const char_u *)s);
+ rettv->vval.v_number = (varnumber_T)mb_string2cells(s);
}
-// "strcharpart()" function
+/// "strcharpart()" function
static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
const char *const p = tv_get_string(&argvars[0]);
@@ -10748,7 +9979,7 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (!error) {
if (nchar > 0) {
while (nchar > 0 && (size_t)nbyte < slen) {
- nbyte += utf_ptr2len((const char_u *)p + nbyte);
+ nbyte += utf_ptr2len(p + nbyte);
nchar--;
}
} else {
@@ -10764,7 +9995,7 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (off < 0) {
len += 1;
} else {
- len += utf_ptr2len((const char_u *)p + off);
+ len += utf_ptr2len(p + off);
}
charlen--;
}
@@ -10787,12 +10018,10 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = (char_u *)xstrndup(p + nbyte, (size_t)len);
+ rettv->vval.v_string = xstrndup(p + nbyte, (size_t)len);
}
-/*
- * "strpart()" function
- */
+/// "strpart()" function
static void f_strpart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
bool error = false;
@@ -10829,16 +10058,16 @@ static void f_strpart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// length in characters
for (off = n; off < (int)slen && len > 0; len--) {
- off += utfc_ptr2len((char_u *)p + off);
+ off += utfc_ptr2len(p + off);
}
len = off - n;
}
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = (char_u *)xmemdupz(p + n, (size_t)len);
+ rettv->vval.v_string = xmemdupz(p + n, (size_t)len);
}
-// "strptime({format}, {timestring})" function
+/// "strptime({format}, {timestring})" function
static void f_strptime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
char fmt_buf[NUMBUFLEN];
@@ -10870,9 +10099,7 @@ static void f_strptime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
xfree(enc);
}
-/*
- * "strridx()" function
- */
+/// "strridx()" function
static void f_strridx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
char buf[NUMBUFLEN];
@@ -10915,18 +10142,14 @@ static void f_strridx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "strtrans()" function
- */
+/// "strtrans()" function
static void f_strtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = (char_u *)transstr(tv_get_string(&argvars[0]), true);
+ rettv->vval.v_string = transstr(tv_get_string(&argvars[0]), true);
}
-/*
- * "submatch()" function
- */
+/// "submatch()" function
static void f_submatch(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
bool error = false;
@@ -10950,16 +10173,14 @@ static void f_submatch(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (retList == 0) {
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = reg_submatch(no);
+ rettv->vval.v_string = (char *)reg_submatch(no);
} else {
rettv->v_type = VAR_LIST;
rettv->vval.v_list = reg_submatch_list(no);
}
}
-/*
- * "substitute()" function
- */
+/// "substitute()" function
static void f_substitute(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
char patbuf[NUMBUFLEN];
@@ -10983,8 +10204,8 @@ static void f_substitute(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|| flg == NULL) {
rettv->vval.v_string = NULL;
} else {
- rettv->vval.v_string = do_string_sub((char_u *)str, (char_u *)pat,
- (char_u *)sub, expr, (char_u *)flg);
+ rettv->vval.v_string = do_string_sub((char *)str, (char *)pat,
+ (char *)sub, expr, (char *)flg);
}
}
@@ -11005,7 +10226,7 @@ static void f_swapname(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|| buf->b_ml.ml_mfp->mf_fname == NULL) {
rettv->vval.v_string = NULL;
} else {
- rettv->vval.v_string = vim_strsave(buf->b_ml.ml_mfp->mf_fname);
+ rettv->vval.v_string = (char *)vim_strsave(buf->b_ml.ml_mfp->mf_fname);
}
}
@@ -11028,9 +10249,7 @@ static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = id;
}
-/*
- * "synIDattr(id, what [, mode])" function
- */
+/// "synIDattr(id, what [, mode])" function
static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
const int id = (int)tv_get_number(&argvars[0]);
@@ -11049,7 +10268,6 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
modec = 'c';
}
-
const char *p = NULL;
switch (TOLOWER_ASC(what[0])) {
case 'b':
@@ -11086,21 +10304,35 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
break;
case 'u':
- if (STRLEN(what) <= 5 || TOLOWER_ASC(what[5]) != 'c') { // underline
- p = highlight_has_attr(id, HL_UNDERLINE, modec);
- } else { // undercurl
- p = highlight_has_attr(id, HL_UNDERCURL, modec);
+ if (STRLEN(what) >= 9) {
+ if (TOLOWER_ASC(what[5]) == 'l') {
+ // underline
+ p = highlight_has_attr(id, HL_UNDERLINE, modec);
+ } else if (TOLOWER_ASC(what[5]) != 'd') {
+ // undercurl
+ p = highlight_has_attr(id, HL_UNDERCURL, modec);
+ } else if (TOLOWER_ASC(what[6]) != 'o') {
+ // underdashed
+ p = highlight_has_attr(id, HL_UNDERDASHED, modec);
+ } else if (TOLOWER_ASC(what[7]) == 'u') {
+ // underdouble
+ p = highlight_has_attr(id, HL_UNDERDOUBLE, modec);
+ } else {
+ // underdotted
+ p = highlight_has_attr(id, HL_UNDERDOTTED, modec);
+ }
+ } else {
+ // ul
+ p = highlight_color(id, what, modec);
}
break;
}
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = (char_u *)(p == NULL ? p : xstrdup(p));
+ rettv->vval.v_string = (char *)(p == NULL ? p : xstrdup(p));
}
-/*
- * "synIDtrans(id)" function
- */
+/// "synIDtrans(id)" function
static void f_synIDtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int id = tv_get_number(&argvars[0]);
@@ -11114,9 +10346,7 @@ static void f_synIDtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = id;
}
-/*
- * "synconcealed(lnum, col)" function
- */
+/// "synconcealed(lnum, col)" function
static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int syntax_flags = 0;
@@ -11146,7 +10376,7 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr)
: curwin->w_p_lcs_chars.conceal;
}
if (cchar != NUL) {
- utf_char2bytes(cchar, str);
+ utf_char2bytes(cchar, (char *)str);
}
}
}
@@ -11158,9 +10388,7 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr)
tv_list_append_number(rettv->vval.v_list, matchid);
}
-/*
- * "synstack(lnum, col)" function
- */
+/// "synstack(lnum, col)" function
static void f_synstack(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
tv_list_set_ret(rettv, NULL);
@@ -11195,10 +10423,7 @@ static void f_systemlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
get_system_output_as_rettv(argvars, rettv, true);
}
-
-/*
- * "tabpagebuflist()" function
- */
+/// "tabpagebuflist()" function
static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
win_T *wp = NULL;
@@ -11220,9 +10445,7 @@ static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "tabpagenr()" function
- */
+/// "tabpagenr()" function
static void f_tabpagenr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int nr = 1;
@@ -11234,9 +10457,7 @@ static void f_tabpagenr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (strcmp(arg, "$") == 0) {
nr = tabpage_index(NULL) - 1;
} else if (strcmp(arg, "#") == 0) {
- nr = valid_tabpage(lastused_tabpage)
- ? tabpage_index(lastused_tabpage)
- : nr;
+ nr = valid_tabpage(lastused_tabpage) ? tabpage_index(lastused_tabpage) : 0;
} else {
semsg(_(e_invexpr2), arg);
}
@@ -11247,10 +10468,7 @@ static void f_tabpagenr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = nr;
}
-
-/*
- * Common code for tabpagewinnr() and winnr().
- */
+/// Common code for tabpagewinnr() and winnr().
static int get_winnr(tabpage_T *tp, typval_T *argvar)
{
win_T *twin;
@@ -11315,9 +10533,7 @@ static int get_winnr(tabpage_T *tp, typval_T *argvar)
return nr;
}
-/*
- * "tabpagewinnr()" function
- */
+/// "tabpagewinnr()" function
static void f_tabpagewinnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int nr = 1;
@@ -11330,9 +10546,7 @@ static void f_tabpagewinnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = nr;
}
-/*
- * "tagfiles()" function
- */
+/// "tagfiles()" function
static void f_tagfiles(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
char *fname;
@@ -11351,9 +10565,7 @@ static void f_tagfiles(typval_T *argvars, typval_T *rettv, FunPtr fptr)
xfree(fname);
}
-/*
- * "taglist()" function
- */
+/// "taglist()" function
static void f_taglist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
const char *const tag_pattern = tv_get_string(&argvars[0]);
@@ -11371,16 +10583,14 @@ static void f_taglist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
(char_u *)tag_pattern, (char_u *)fname);
}
-/*
- * "tempname()" function
- */
+/// "tempname()" function
static void f_tempname(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = vim_tempname();
+ rettv->vval.v_string = (char *)vim_tempname();
}
-// "termopen(cmd[, cwd])" function
+/// "termopen(cmd[, cwd])" function
static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
if (check_secure()) {
@@ -11466,19 +10676,25 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// "./…" => "/home/foo/…"
vim_FullName(cwd, (char *)NameBuff, sizeof(NameBuff), false);
// "/home/foo/…" => "~/…"
- size_t len = home_replace(NULL, NameBuff, IObuff, sizeof(IObuff), true);
+ size_t len = home_replace(NULL, (char *)NameBuff, (char *)IObuff, sizeof(IObuff), true);
// Trim slash.
- if (IObuff[len - 1] == '\\' || IObuff[len - 1] == '/') {
+ if (len != 1 && (IObuff[len - 1] == '\\' || IObuff[len - 1] == '/')) {
IObuff[len - 1] = '\0';
}
+ if (len == 1 && IObuff[0] == '/') {
+ // Avoid ambiguity in the URI when CWD is root directory.
+ IObuff[1] = '.';
+ IObuff[2] = '\0';
+ }
+
// Terminal URI: "term://$CWD//$PID:$CMD"
snprintf((char *)NameBuff, sizeof(NameBuff), "term://%s//%d:%s",
(char *)IObuff, pid, cmd);
// at this point the buffer has no terminal instance associated yet, so unset
// the 'swapfile' option to ensure no swap file will be created
curbuf->b_p_swf = false;
- (void)setfname(curbuf, NameBuff, NULL, true);
+ (void)setfname(curbuf, (char *)NameBuff, NULL, true);
// Save the job id and pid in b:terminal_job_{id,pid}
Error err = ERROR_INIT;
// deprecated: use 'channel' buffer option
@@ -11493,24 +10709,6 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
channel_create_event(chan, NULL);
}
-// "test_garbagecollect_now()" function
-static void f_test_garbagecollect_now(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- // This is dangerous, any Lists and Dicts used internally may be freed
- // while still in use.
- garbage_collect(true);
-}
-
-// "test_write_list_log()" function
-static void f_test_write_list_log(typval_T *const argvars, typval_T *const rettv, FunPtr fptr)
-{
- const char *const fname = tv_get_string_chk(&argvars[0]);
- if (fname == NULL) {
- return;
- }
- list_write_log(fname);
-}
-
/// "timer_info([timer])" function
static void f_timer_info(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
@@ -11583,8 +10781,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr)
timer_start(tv_get_number(&argvars[0]), repeat, &callback);
}
-
-// "timer_stop(timerid)" function
+/// "timer_stop(timerid)" function
static void f_timer_stop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
if (argvars[0].v_type != VAR_NUMBER) {
@@ -11605,29 +10802,21 @@ static void f_timer_stopall(typval_T *argvars, typval_T *unused, FunPtr fptr)
timer_stop_all();
}
-/*
- * "tolower(string)" function
- */
+/// "tolower(string)" function
static void f_tolower(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = (char_u *)strcase_save(tv_get_string(&argvars[0]),
- false);
+ rettv->vval.v_string = strcase_save(tv_get_string(&argvars[0]), false);
}
-/*
- * "toupper(string)" function
- */
+/// "toupper(string)" function
static void f_toupper(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = (char_u *)strcase_save(tv_get_string(&argvars[0]),
- true);
+ rettv->vval.v_string = strcase_save(tv_get_string(&argvars[0]), true);
}
-/*
- * "tr(string, fromstr, tostr)" function
- */
+/// "tr(string, fromstr, tostr)" function
static void f_tr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
char buf[NUMBUFLEN];
@@ -11650,16 +10839,16 @@ static void f_tr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
bool first = true;
while (*in_str != NUL) {
const char *cpstr = in_str;
- const int inlen = utfc_ptr2len((const char_u *)in_str);
+ const int inlen = utfc_ptr2len(in_str);
int cplen = inlen;
int idx = 0;
int fromlen;
for (const char *p = fromstr; *p != NUL; p += fromlen) {
- fromlen = utfc_ptr2len((const char_u *)p);
+ fromlen = utfc_ptr2len(p);
if (fromlen == inlen && STRNCMP(in_str, p, inlen) == 0) {
int tolen;
for (p = tostr; *p != NUL; p += tolen) {
- tolen = utfc_ptr2len((const char_u *)p);
+ tolen = utfc_ptr2len(p);
if (idx-- == 0) {
cplen = tolen;
cpstr = (char *)p;
@@ -11681,7 +10870,7 @@ static void f_tr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
first = false;
int tolen;
for (const char *p = tostr; *p != NUL; p += tolen) {
- tolen = utfc_ptr2len((const char_u *)p);
+ tolen = utfc_ptr2len(p);
idx--;
}
if (idx != 0) {
@@ -11704,10 +10893,9 @@ static void f_tr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
error:
semsg(_(e_invarg2), fromstr);
ga_clear(&ga);
- return;
}
-// "trim({expr})" function
+/// "trim({expr})" function
static void f_trim(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
char buf1[NUMBUFLEN];
@@ -11745,14 +10933,14 @@ static void f_trim(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (dir == 0 || dir == 1) {
// Trim leading characters
while (*head != NUL) {
- c1 = utf_ptr2char(head);
+ c1 = utf_ptr2char((char *)head);
if (mask == NULL) {
if (c1 > ' ' && c1 != 0xa0) {
break;
}
} else {
for (p = mask; *p != NUL; MB_PTR_ADV(p)) {
- if (c1 == utf_ptr2char(p)) {
+ if (c1 == utf_ptr2char((char *)p)) {
break;
}
}
@@ -11770,14 +10958,14 @@ static void f_trim(typval_T *argvars, typval_T *rettv, FunPtr fptr)
for (; tail > head; tail = prev) {
prev = tail;
MB_PTR_BACK(head, prev);
- c1 = utf_ptr2char(prev);
+ c1 = utf_ptr2char((char *)prev);
if (mask == NULL) {
if (c1 > ' ' && c1 != 0xa0) {
break;
}
} else {
for (p = mask; *p != NUL; MB_PTR_ADV(p)) {
- if (c1 == utf_ptr2char(p)) {
+ if (c1 == utf_ptr2char((char *)p)) {
break;
}
}
@@ -11787,12 +10975,10 @@ static void f_trim(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
}
- rettv->vval.v_string = vim_strnsave(head, tail - head);
+ rettv->vval.v_string = (char *)vim_strnsave(head, tail - head);
}
-/*
- * "type(expr)" function
- */
+/// "type(expr)" function
static void f_type(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int n = -1;
@@ -11824,9 +11010,7 @@ static void f_type(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = n;
}
-/*
- * "undofile(name)" function
- */
+/// "undofile(name)" function
static void f_undofile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->v_type = VAR_STRING;
@@ -11839,15 +11023,13 @@ static void f_undofile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
char *ffname = FullName_save(fname, true);
if (ffname != NULL) {
- rettv->vval.v_string = (char_u *)u_get_undo_file_name(ffname, false);
+ rettv->vval.v_string = u_get_undo_file_name(ffname, false);
}
xfree(ffname);
}
}
-/*
- * "undotree()" function
- */
+/// "undotree()" function
static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
tv_dict_alloc_ret(rettv);
@@ -11865,24 +11047,20 @@ static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr)
tv_dict_add_list(dict, S_LEN("entries"), u_eval_tree(curbuf->b_u_oldhead));
}
-/*
- * "values(dict)" function
- */
+/// "values(dict)" function
static void f_values(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
dict_list(argvars, rettv, 1);
}
-/*
- * "virtcol(string)" function
- */
+/// "virtcol(string)" function
static void f_virtcol(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
colnr_T vcol = 0;
pos_T *fp;
int fnum = curbuf->b_fnum;
- fp = var2fpos(&argvars[0], FALSE, &fnum);
+ fp = var2fpos(&argvars[0], false, &fnum, false);
if (fp != NULL && fp->lnum <= curbuf->b_ml.ml_line_count
&& fnum == curbuf->b_fnum) {
// Limit the column to a valid value, getvvcol() doesn't check.
@@ -11901,9 +11079,7 @@ static void f_virtcol(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = vcol;
}
-/*
- * "visualmode()" function
- */
+/// "visualmode()" function
static void f_visualmode(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
char_u str[2];
@@ -11911,7 +11087,7 @@ static void f_visualmode(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->v_type = VAR_STRING;
str[0] = curbuf->b_visual_mode_eval;
str[1] = NUL;
- rettv->vval.v_string = vim_strsave(str);
+ rettv->vval.v_string = (char *)vim_strsave(str);
// A non-zero number or non-empty string argument: reset mode.
if (non_zero_arg(&argvars[0])) {
@@ -11919,12 +11095,10 @@ static void f_visualmode(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "wildmenumode()" function
- */
+/// "wildmenumode()" function
static void f_wildmenumode(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- if (wild_menu_showing || ((State & CMDLINE) && pum_visible())) {
+ if (wild_menu_showing || ((State & MODE_CMDLINE) && pum_visible())) {
rettv->vval.v_number = 1;
}
}
@@ -11952,21 +11126,20 @@ static void f_win_gettype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (argvars[0].v_type != VAR_UNKNOWN) {
wp = find_win_by_nr_or_id(&argvars[0]);
if (wp == NULL) {
- rettv->vval.v_string = vim_strsave((char_u *)"unknown");
+ rettv->vval.v_string = (char *)vim_strsave((char_u *)"unknown");
return;
}
}
if (wp == aucmd_win) {
- rettv->vval.v_string = vim_strsave((char_u *)"autocmd");
+ rettv->vval.v_string = xstrdup("autocmd");
} else if (wp->w_p_pvw) {
- rettv->vval.v_string = vim_strsave((char_u *)"preview");
+ rettv->vval.v_string = xstrdup("preview");
} else if (wp->w_floating) {
- rettv->vval.v_string = vim_strsave((char_u *)"popup");
+ rettv->vval.v_string = xstrdup("popup");
} else if (wp == curwin && cmdwin_type != 0) {
- rettv->vval.v_string = vim_strsave((char_u *)"command");
+ rettv->vval.v_string = xstrdup("command");
} else if (bt_quickfix(wp->w_buffer)) {
- rettv->vval.v_string = vim_strsave((char_u *)(wp->w_llist_ref != NULL ?
- "loclist" : "quickfix"));
+ rettv->vval.v_string = xstrdup((wp->w_llist_ref != NULL ? "loclist" : "quickfix"));
}
}
@@ -11988,6 +11161,42 @@ static void f_win_id2win(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = win_id2win(argvars);
}
+/// "win_move_separator()" function
+static void f_win_move_separator(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ win_T *wp;
+ int offset;
+
+ rettv->vval.v_number = false;
+
+ wp = find_win_by_nr_or_id(&argvars[0]);
+ if (wp == NULL || wp->w_floating) {
+ return;
+ }
+
+ offset = (int)tv_get_number(&argvars[1]);
+ win_drag_vsep_line(wp, offset);
+ rettv->vval.v_number = true;
+}
+
+/// "win_move_statusline()" function
+static void f_win_move_statusline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ win_T *wp;
+ int offset;
+
+ rettv->vval.v_number = false;
+
+ wp = find_win_by_nr_or_id(&argvars[0]);
+ if (wp == NULL || wp->w_floating) {
+ return;
+ }
+
+ offset = (int)tv_get_number(&argvars[1]);
+ win_drag_status_line(wp, offset);
+ rettv->vval.v_number = true;
+}
+
/// "winbufnr(nr)" function
static void f_winbufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
@@ -11999,9 +11208,7 @@ static void f_winbufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "wincol()" function
- */
+/// "wincol()" function
static void f_wincol(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
validate_cursor();
@@ -12019,7 +11226,7 @@ static void f_winheight(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-// "winlayout()" function
+/// "winlayout()" function
static void f_winlayout(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
tabpage_T *tp;
@@ -12038,18 +11245,14 @@ static void f_winlayout(typval_T *argvars, typval_T *rettv, FunPtr fptr)
get_framelayout(tp->tp_topframe, rettv->vval.v_list, true);
}
-/*
- * "winline()" function
- */
+/// "winline()" function
static void f_winline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
validate_cursor();
rettv->vval.v_number = curwin->w_wrow + 1;
}
-/*
- * "winnr()" function
- */
+/// "winnr()" function
static void f_winnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int nr = 1;
@@ -12058,9 +11261,7 @@ static void f_winnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = nr;
}
-/*
- * "winrestcmd()" function
- */
+/// "winrestcmd()" function
static void f_winrestcmd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
garray_T ga;
@@ -12087,9 +11288,7 @@ static void f_winrestcmd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->v_type = VAR_STRING;
}
-/*
- * "winrestview()" function
- */
+/// "winrestview()" function
static void f_winrestview(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
dict_T *dict;
@@ -12140,9 +11339,7 @@ static void f_winrestview(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "winsaveview()" function
- */
+/// "winsaveview()" function
static void f_winsaveview(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
dict_T *dict;
@@ -12173,11 +11370,11 @@ static void f_winwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-// "windowsversion()" function
+/// "windowsversion()" function
static void f_windowsversion(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = (char_u *)xstrdup(windowsVersion);
+ rettv->vval.v_string = xstrdup(windowsVersion);
}
/// "wordcount()" function
@@ -12264,9 +11461,8 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
}
-/*
- * "xor(expr, expr)" function
- */
+
+/// "xor(expr, expr)" function
static void f_xor(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL)
diff --git a/src/nvim/eval/funcs.h b/src/nvim/eval/funcs.h
index c6a0cb959e..5f8d81c989 100644
--- a/src/nvim/eval/funcs.h
+++ b/src/nvim/eval/funcs.h
@@ -9,19 +9,20 @@ typedef void (*FunPtr)(void);
/// Prototype of C function that implements VimL function
typedef void (*VimLFunc)(typval_T *args, typval_T *rvar, FunPtr data);
-/// Special flags for base_arg @see VimLFuncDef
+/// Special flags for base_arg @see EvalFuncDef
#define BASE_NONE 0 ///< Not a method (no base argument).
#define BASE_LAST UINT8_MAX ///< Use the last argument as the method base.
/// Structure holding VimL function definition
-typedef struct fst {
+typedef struct {
char *name; ///< Name of the function.
uint8_t min_argc; ///< Minimal number of arguments.
uint8_t max_argc; ///< Maximal number of arguments.
uint8_t base_arg; ///< Method base arg # (1-indexed), BASE_NONE or BASE_LAST.
+ bool fast; ///< Can be run in |api-fast| events
VimLFunc func; ///< Function implementation.
FunPtr data; ///< Userdata for function implementation.
-} VimLFuncDef;
+} EvalFuncDef;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval/funcs.h.generated.h"
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index 42ac1839e6..e19cf411c0 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -8,6 +8,7 @@
#include <stdlib.h>
#include <string.h>
+#include "lauxlib.h"
#include "nvim/ascii.h"
#include "nvim/assert.h"
#include "nvim/charset.h"
@@ -28,11 +29,11 @@
#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/message.h"
+#include "nvim/os/fileio.h"
#include "nvim/os/input.h"
#include "nvim/pos.h"
#include "nvim/types.h"
#include "nvim/vim.h"
-#include "nvim/os/fileio.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval/typval.c.generated.h"
@@ -564,7 +565,7 @@ void tv_list_append_allocated_string(list_T *const l, char *const str)
tv_list_append_owned_tv(l, (typval_T) {
.v_type = VAR_STRING,
.v_lock = VAR_UNLOCKED,
- .vval.v_string = (char_u *)str,
+ .vval.v_string = str,
});
}
@@ -878,9 +879,9 @@ void tv_list_reverse(list_T *const l)
list_log(l, NULL, NULL, "reverse");
#define SWAP(a, b) \
do { \
- tmp = a; \
- a = b; \
- b = tmp; \
+ tmp = (a); \
+ (a) = (b); \
+ (b) = tmp; \
} while (0)
listitem_T *tmp;
@@ -1123,6 +1124,8 @@ bool tv_callback_equal(const Callback *cb1, const Callback *cb2)
// FIXME: this is inconsistent with tv_equal but is needed for precision
// maybe change dictwatcheradd to return a watcher id instead?
return cb1->data.partial == cb2->data.partial;
+ case kCallbackLua:
+ return cb1->data.luaref == cb2->data.luaref;
case kCallbackNone:
return true;
}
@@ -1136,12 +1139,15 @@ void callback_free(Callback *callback)
{
switch (callback->type) {
case kCallbackFuncref:
- func_unref(callback->data.funcref);
+ func_unref((char_u *)callback->data.funcref);
xfree(callback->data.funcref);
break;
case kCallbackPartial:
partial_unref(callback->data.partial);
break;
+ case kCallbackLua:
+ NLUA_CLEAR_REF(callback->data.luaref);
+ break;
case kCallbackNone:
break;
}
@@ -1161,9 +1167,14 @@ void callback_put(Callback *cb, typval_T *tv)
break;
case kCallbackFuncref:
tv->v_type = VAR_FUNC;
- tv->vval.v_string = vim_strsave(cb->data.funcref);
- func_ref(cb->data.funcref);
+ tv->vval.v_string = xstrdup(cb->data.funcref);
+ func_ref((char_u *)cb->data.funcref);
break;
+ case kCallbackLua:
+ // TODO(tjdevries): Unified Callback.
+ // At this point this isn't possible, but it'd be nice to put
+ // these handled more neatly in one place.
+ // So instead, we just do the default and put nil
default:
tv->v_type = VAR_SPECIAL;
tv->vval.v_special = kSpecialVarNull;
@@ -1182,8 +1193,11 @@ void callback_copy(Callback *dest, Callback *src)
dest->data.partial->pt_refcount++;
break;
case kCallbackFuncref:
- dest->data.funcref = vim_strsave(src->data.funcref);
- func_ref(src->data.funcref);
+ dest->data.funcref = xstrdup(src->data.funcref);
+ func_ref((char_u *)src->data.funcref);
+ break;
+ case kCallbackLua:
+ dest->data.luaref = api_new_luaref(src->data.luaref);
break;
default:
dest->data.funcref = NULL;
@@ -1191,6 +1205,30 @@ void callback_copy(Callback *dest, Callback *src)
}
}
+/// Generate a string description of a callback
+char *callback_to_string(Callback *cb)
+{
+ size_t msglen = 100;
+ char *msg = (char *)xmallocz(msglen);
+
+ switch (cb->type) {
+ case kCallbackLua:
+ snprintf(msg, msglen, "<lua: %d>", cb->data.luaref);
+ break;
+ case kCallbackFuncref:
+ // TODO(tjdevries): Is this enough space for this?
+ snprintf(msg, msglen, "<vim function: %s>", cb->data.funcref);
+ break;
+ case kCallbackPartial:
+ snprintf(msg, msglen, "<vim partial: %s>", cb->data.partial->pt_name);
+ break;
+ default:
+ snprintf(msg, msglen, "%s", "");
+ break;
+ }
+ return msg;
+}
+
/// Remove watcher from a dictionary
///
/// @param dict Dictionary to remove watcher from.
@@ -1274,7 +1312,7 @@ void tv_dict_watcher_notify(dict_T *const dict, const char *const key, typval_T
argv[0].vval.v_dict = dict;
argv[1].v_type = VAR_STRING;
argv[1].v_lock = VAR_UNLOCKED;
- argv[1].vval.v_string = (char_u *)xstrdup(key);
+ argv[1].vval.v_string = xstrdup(key);
argv[2].v_type = VAR_DICT;
argv[2].v_lock = VAR_UNLOCKED;
argv[2].vval.v_dict = tv_dict_alloc();
@@ -1396,7 +1434,7 @@ dictitem_T *tv_dict_item_copy(dictitem_T *const di)
void tv_dict_item_remove(dict_T *const dict, dictitem_T *const item)
FUNC_ATTR_NONNULL_ALL
{
- hashitem_T *const hi = hash_find(&dict->dv_hashtab, item->di_key);
+ hashitem_T *const hi = hash_find(&dict->dv_hashtab, (char *)item->di_key);
if (HASHITEM_EMPTY(hi)) {
semsg(_(e_intern2), "tv_dict_item_remove()");
} else {
@@ -1501,7 +1539,6 @@ void tv_dict_free(dict_T *const d)
}
}
-
/// Unreference a dictionary
///
/// Decrements the reference count and frees dictionary when it becomes zero.
@@ -1530,7 +1567,7 @@ dictitem_T *tv_dict_find(const dict_T *const d, const char *const key, const ptr
return NULL;
}
hashitem_T *const hi = (len < 0
- ? hash_find(&d->dv_hashtab, (const char_u *)key)
+ ? hash_find(&d->dv_hashtab, key)
: hash_find_len(&d->dv_hashtab, key, (size_t)len));
if (HASHITEM_EMPTY(hi)) {
return NULL;
@@ -1574,8 +1611,6 @@ varnumber_T tv_dict_get_number(const dict_T *const d, const char *const key)
}
/// Converts a dict to an environment
-///
-///
char **tv_dict_to_env(dict_T *denv)
{
size_t env_size = (size_t)tv_dict_len(denv);
@@ -1894,7 +1929,7 @@ int tv_dict_add_allocated_str(dict_T *const d, const char *const key, const size
dictitem_T *const item = tv_dict_item_alloc_len(key, key_len);
item->di_tv.v_type = VAR_STRING;
- item->di_tv.vval.v_string = (char_u *)val;
+ item->di_tv.vval.v_string = val;
if (tv_dict_add(d, item) == FAIL) {
tv_dict_item_free(item);
return FAIL;
@@ -2251,36 +2286,36 @@ void tv_blob_copy(typval_T *const from, typval_T *const to)
#define TYPVAL_ENCODE_CONV_NIL(tv) \
do { \
- tv->vval.v_special = kSpecialVarNull; \
- tv->v_lock = VAR_UNLOCKED; \
+ (tv)->vval.v_special = kSpecialVarNull; \
+ (tv)->v_lock = VAR_UNLOCKED; \
} while (0)
#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \
do { \
- tv->vval.v_bool = kBoolVarFalse; \
- tv->v_lock = VAR_UNLOCKED; \
+ (tv)->vval.v_bool = kBoolVarFalse; \
+ (tv)->v_lock = VAR_UNLOCKED; \
} while (0)
#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \
do { \
- (void)num; \
- tv->vval.v_number = 0; \
- tv->v_lock = VAR_UNLOCKED; \
+ (void)(num); \
+ (tv)->vval.v_number = 0; \
+ (tv)->v_lock = VAR_UNLOCKED; \
} while (0)
#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, num)
#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
do { \
- tv->vval.v_float = 0; \
- tv->v_lock = VAR_UNLOCKED; \
+ (tv)->vval.v_float = 0; \
+ (tv)->v_lock = VAR_UNLOCKED; \
} while (0)
#define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \
do { \
xfree(buf); \
- tv->vval.v_string = NULL; \
- tv->v_lock = VAR_UNLOCKED; \
+ (tv)->vval.v_string = NULL; \
+ (tv)->v_lock = VAR_UNLOCKED; \
} while (0)
#define TYPVAL_ENCODE_CONV_STR_STRING(tv, buf, len)
@@ -2289,9 +2324,9 @@ void tv_blob_copy(typval_T *const from, typval_T *const to)
#define TYPVAL_ENCODE_CONV_BLOB(tv, blob, len) \
do { \
- tv_blob_unref(tv->vval.v_blob); \
- tv->vval.v_blob = NULL; \
- tv->v_lock = VAR_UNLOCKED; \
+ tv_blob_unref((tv)->vval.v_blob); \
+ (tv)->vval.v_blob = NULL; \
+ (tv)->v_lock = VAR_UNLOCKED; \
} while (0)
static inline int _nothing_conv_func_start(typval_T *const tv, char_u *const fun)
@@ -2348,9 +2383,9 @@ static inline void _nothing_conv_func_end(typval_T *const tv, const int copyID)
#define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \
do { \
- tv_list_unref(tv->vval.v_list); \
- tv->vval.v_list = NULL; \
- tv->v_lock = VAR_UNLOCKED; \
+ tv_list_unref((tv)->vval.v_list); \
+ (tv)->vval.v_list = NULL; \
+ (tv)->v_lock = VAR_UNLOCKED; \
} while (0)
static inline void _nothing_conv_empty_dict(typval_T *const tv, dict_T **const dictp)
@@ -2364,8 +2399,8 @@ static inline void _nothing_conv_empty_dict(typval_T *const tv, dict_T **const d
}
#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \
do { \
- assert((void *)&dict != (void *)&TYPVAL_ENCODE_NODICT_VAR); \
- _nothing_conv_empty_dict(tv, ((dict_T **)&dict)); \
+ assert((void *)&(dict) != (void *)&TYPVAL_ENCODE_NODICT_VAR); \
+ _nothing_conv_empty_dict(tv, ((dict_T **)&(dict))); \
} while (0)
static inline int _nothing_conv_real_list_after_start(typval_T *const tv,
@@ -2386,7 +2421,7 @@ static inline int _nothing_conv_real_list_after_start(typval_T *const tv,
#define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv) \
do { \
- if (_nothing_conv_real_list_after_start(tv, &mpsv) != NOTDONE) { \
+ if (_nothing_conv_real_list_after_start(tv, &(mpsv)) != NOTDONE) { \
goto typval_encode_stop_converting_one_item; \
} \
} while (0)
@@ -2426,8 +2461,9 @@ static inline int _nothing_conv_real_dict_after_start(typval_T *const tv, dict_T
#define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv) \
do { \
- if (_nothing_conv_real_dict_after_start(tv, (dict_T **)&dict, (void *)&TYPVAL_ENCODE_NODICT_VAR, \
- &mpsv) != NOTDONE) { \
+ if (_nothing_conv_real_dict_after_start(tv, (dict_T **)&(dict), \
+ (void *)&TYPVAL_ENCODE_NODICT_VAR, &(mpsv)) \
+ != NOTDONE) { \
goto typval_encode_stop_converting_one_item; \
} \
} while (0)
@@ -2446,7 +2482,7 @@ static inline void _nothing_conv_dict_end(typval_T *const tv, dict_T **const dic
}
}
#define TYPVAL_ENCODE_CONV_DICT_END(tv, dict) \
- _nothing_conv_dict_end(tv, (dict_T **)&dict, \
+ _nothing_conv_dict_end(tv, (dict_T **)&(dict), \
(void *)&TYPVAL_ENCODE_NODICT_VAR)
#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type)
@@ -2520,7 +2556,7 @@ void tv_free(typval_T *tv)
partial_unref(tv->vval.v_partial);
break;
case VAR_FUNC:
- func_unref(tv->vval.v_string);
+ func_unref((char_u *)tv->vval.v_string);
FALLTHROUGH;
case VAR_STRING:
xfree(tv->vval.v_string);
@@ -2571,9 +2607,9 @@ void tv_copy(const typval_T *const from, typval_T *const to)
case VAR_STRING:
case VAR_FUNC:
if (from->vval.v_string != NULL) {
- to->vval.v_string = vim_strsave(from->vval.v_string);
+ to->vval.v_string = xstrdup(from->vval.v_string);
if (from->v_type == VAR_FUNC) {
- func_ref(to->vval.v_string);
+ func_ref((char_u *)to->vval.v_string);
}
}
break;
@@ -2628,9 +2664,9 @@ void tv_item_lock(typval_T *const tv, const int deep, const bool lock, const boo
// lock/unlock the item itself
#define CHANGE_LOCK(lock, var) \
do { \
- var = ((VarLockStatus[]) { \
- [VAR_UNLOCKED] = (lock ? VAR_LOCKED : VAR_UNLOCKED), \
- [VAR_LOCKED] = (lock ? VAR_LOCKED : VAR_UNLOCKED), \
+ (var) = ((VarLockStatus[]) { \
+ [VAR_UNLOCKED] = ((lock) ? VAR_LOCKED : VAR_UNLOCKED), \
+ [VAR_LOCKED] = ((lock) ? VAR_LOCKED : VAR_UNLOCKED), \
[VAR_FIXED] = VAR_FIXED, \
})[var]; \
} while (0)
@@ -3059,7 +3095,7 @@ varnumber_T tv_get_number_chk(const typval_T *const tv, bool *const ret_error)
case VAR_STRING: {
varnumber_T n = 0;
if (tv->vval.v_string != NULL) {
- vim_str2nr(tv->vval.v_string, NULL, NULL, STR2NR_ALL, &n, NULL, 0,
+ vim_str2nr((char_u *)tv->vval.v_string, NULL, NULL, STR2NR_ALL, &n, NULL, 0,
false);
}
return n;
@@ -3091,7 +3127,7 @@ linenr_T tv_get_lnum(const typval_T *const tv)
linenr_T lnum = (linenr_T)tv_get_number_chk(tv, NULL);
if (lnum == 0) { // No valid number, try using same function as line() does.
int fnum;
- pos_T *const fp = var2fpos(tv, true, &fnum);
+ pos_T *const fp = var2fpos(tv, true, &fnum, false);
if (fp != NULL) {
lnum = fp->lnum;
}
@@ -3205,8 +3241,9 @@ const char *tv_get_string_buf_chk(const typval_T *const tv, char *const buf)
case VAR_BLOB:
case VAR_UNKNOWN:
emsg(_(str_errors[tv->v_type]));
- return false;
+ return NULL;
}
+ abort();
return NULL;
}
diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h
index d1275d6512..c02351947b 100644
--- a/src/nvim/eval/typval.h
+++ b/src/nvim/eval/typval.h
@@ -72,16 +72,20 @@ typedef enum {
kCallbackNone = 0,
kCallbackFuncref,
kCallbackPartial,
+ kCallbackLua,
} CallbackType;
typedef struct {
union {
- char_u *funcref;
+ char *funcref;
partial_T *partial;
+ LuaRef luaref;
} 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 {
@@ -129,19 +133,19 @@ typedef enum {
/// Structure that holds an internal variable value
typedef struct {
- VarType v_type; ///< Variable type.
- VarLockStatus v_lock; ///< Variable lock status.
+ VarType v_type; ///< Variable type.
+ VarLockStatus v_lock; ///< Variable lock status.
union typval_vval_union {
- varnumber_T v_number; ///< Number, for VAR_NUMBER.
+ varnumber_T v_number; ///< Number, for VAR_NUMBER.
BoolVarValue v_bool; ///< Bool value, for VAR_BOOL
SpecialVarValue v_special; ///< Special value, for VAR_SPECIAL.
- float_T v_float; ///< Floating-point number, for VAR_FLOAT.
- char_u *v_string; ///< String, for VAR_STRING and VAR_FUNC, can be NULL.
- list_T *v_list; ///< List for VAR_LIST, can be NULL.
- dict_T *v_dict; ///< Dictionary for VAR_DICT, can be NULL.
- partial_T *v_partial; ///< Closure: function with args.
- blob_T *v_blob; ///< Blob for VAR_BLOB, can be NULL.
- } vval; ///< Actual value.
+ float_T v_float; ///< Floating-point number, for VAR_FLOAT.
+ char *v_string; ///< String, for VAR_STRING and VAR_FUNC, can be NULL.
+ list_T *v_list; ///< List for VAR_LIST, can be NULL.
+ dict_T *v_dict; ///< Dictionary for VAR_DICT, can be NULL.
+ partial_T *v_partial; ///< Closure: function with args.
+ blob_T *v_blob; ///< Blob for VAR_BLOB, can be NULL.
+ } vval; ///< Actual value.
} typval_T;
/// Values for (struct dictvar_S).dv_scope
diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h
index ece51cb046..73b36b8611 100644
--- a/src/nvim/eval/typval_encode.c.h
+++ b/src/nvim/eval/typval_encode.c.h
@@ -1,3 +1,5 @@
+// uncrustify:off
+
/// @file eval/typval_encode.c.h
///
/// Contains set of macros used to convert (possibly recursive) typval_T into
@@ -250,6 +252,8 @@
#include "nvim/func_attr.h"
#include "nvim/lib/kvec.h"
+// -V::1063
+
/// Dummy variable used because some macros need lvalue
///
/// Must not be written to, if needed one must check that address of the
@@ -335,7 +339,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
tv_blob_len(tv->vval.v_blob));
break;
case VAR_FUNC:
- TYPVAL_ENCODE_CONV_FUNC_START(tv, tv->vval.v_string);
+ TYPVAL_ENCODE_CONV_FUNC_START(tv, (char_u *)tv->vval.v_string);
TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, 0);
TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, -1);
TYPVAL_ENCODE_CONV_FUNC_END(tv);
@@ -343,8 +347,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
case VAR_PARTIAL: {
partial_T *const pt = tv->vval.v_partial;
(void)pt;
- TYPVAL_ENCODE_CONV_FUNC_START( // -V547
- tv, (pt == NULL ? NULL : partial_name(pt)));
+ TYPVAL_ENCODE_CONV_FUNC_START(tv, (pt == NULL ? NULL : (char_u *)partial_name(pt))); // -V547
_mp_push(*mpstack, ((MPConvStackVal) { // -V779
.type = kMPConvPartial,
.tv = tv,
diff --git a/src/nvim/eval/typval_encode.h b/src/nvim/eval/typval_encode.h
index d5cf431870..ed70ba87ec 100644
--- a/src/nvim/eval/typval_encode.h
+++ b/src/nvim/eval/typval_encode.h
@@ -85,9 +85,7 @@ static inline size_t tv_strlen(const typval_T *const tv)
static inline size_t tv_strlen(const typval_T *const tv)
{
assert(tv->v_type == VAR_STRING);
- return (tv->vval.v_string == NULL
- ? 0
- : strlen((char *)tv->vval.v_string));
+ return (tv->vval.v_string == NULL ? 0 : strlen(tv->vval.v_string));
}
/// Code for checking whether container references itself
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index eb241eb8ae..c2579944e4 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -70,7 +70,7 @@ static int get_function_args(char_u **argp, char_u endchar, garray_T *newargs, i
bool mustend = false;
char_u *arg = *argp;
char_u *p = arg;
- int c;
+ char_u c;
int i;
if (newargs != NULL) {
@@ -125,14 +125,14 @@ static int get_function_args(char_u **argp, char_u endchar, garray_T *newargs, i
*p = c;
}
- if (*skipwhite(p) == '=' && default_args != NULL) {
+ if (*skipwhite((char *)p) == '=' && default_args != NULL) {
typval_T rettv;
any_default = true;
- p = skipwhite(p) + 1;
- p = skipwhite(p);
+ p = (char_u *)skipwhite((char *)p) + 1;
+ p = (char_u *)skipwhite((char *)p);
char_u *expr = p;
- if (eval1(&p, &rettv, false) != FAIL) {
+ if (eval1((char **)&p, &rettv, false) != FAIL) {
ga_grow(default_args, 1);
// trim trailing whitespace
@@ -159,7 +159,7 @@ static int get_function_args(char_u **argp, char_u endchar, garray_T *newargs, i
mustend = true;
}
}
- p = skipwhite(p);
+ p = (char_u *)skipwhite((char *)p);
if (mustend && *p != endchar) {
if (!skip) {
semsg(_(e_invarg2), *argp);
@@ -200,8 +200,7 @@ static void register_closure(ufunc_T *fp)
[current_funccal->fc_funcs.ga_len++] = fp;
}
-
-/// Get a name for a lambda. Returned in static memory.
+/// @return a name for a lambda. Returned in static memory.
char_u *get_lambda_name(void)
{
static char_u name[30];
@@ -222,7 +221,7 @@ int get_lambda_tv(char_u **arg, typval_T *rettv, bool evaluate)
partial_T *pt = NULL;
int varargs;
int ret;
- char_u *start = skipwhite(*arg + 1);
+ char_u *start = (char_u *)skipwhite((char *)(*arg) + 1);
char_u *s, *e;
bool *old_eval_lavars = eval_lavars_used;
bool eval_lavars = false;
@@ -239,7 +238,7 @@ int get_lambda_tv(char_u **arg, typval_T *rettv, bool evaluate)
} else {
pnewargs = NULL;
}
- *arg = skipwhite(*arg + 1);
+ *arg = (char_u *)skipwhite((char *)(*arg) + 1);
ret = get_function_args(arg, '-', pnewargs, &varargs, NULL, false);
if (ret == FAIL || **arg != '>') {
goto errret;
@@ -251,21 +250,21 @@ int get_lambda_tv(char_u **arg, typval_T *rettv, bool evaluate)
}
// Get the start and the end of the expression.
- *arg = skipwhite(*arg + 1);
+ *arg = (char_u *)skipwhite((char *)(*arg) + 1);
s = *arg;
- ret = skip_expr(arg);
+ ret = skip_expr((char **)arg);
if (ret == FAIL) {
goto errret;
}
e = *arg;
- *arg = skipwhite(*arg);
+ *arg = (char_u *)skipwhite((char *)(*arg));
if (**arg != '}') {
goto errret;
}
(*arg)++;
if (evaluate) {
- int len, flags = 0;
+ int flags = 0;
char_u *p;
garray_T newlines;
@@ -278,7 +277,7 @@ int get_lambda_tv(char_u **arg, typval_T *rettv, bool evaluate)
ga_grow(&newlines, 1);
// Add "return " before the expression.
- len = 7 + e - s + 1;
+ size_t len = (size_t)(7 + e - s + 1);
p = (char_u *)xmalloc(len);
((char_u **)(newlines.ga_data))[newlines.ga_len++] = p;
STRCPY(p, "return ");
@@ -359,7 +358,7 @@ char_u *deref_func_name(const char *name, int *lenp, partial_T **const partialp,
return (char_u *)"";
}
*lenp = (int)STRLEN(v->di_tv.vval.v_string);
- return v->di_tv.vval.v_string;
+ return (char_u *)v->di_tv.vval.v_string;
}
if (v != NULL && v->di_tv.v_type == VAR_PARTIAL) {
@@ -372,7 +371,7 @@ char_u *deref_func_name(const char *name, int *lenp, partial_T **const partialp,
if (partialp != NULL) {
*partialp = pt;
}
- char_u *s = partial_name(pt);
+ char_u *s = (char_u *)partial_name(pt);
*lenp = (int)STRLEN(s);
return s;
}
@@ -422,11 +421,11 @@ int get_func_tv(const char_u *name, int len, typval_T *rettv, char_u **arg, func
argp = *arg;
while (argcount < MAX_FUNC_ARGS
- (funcexe->partial == NULL ? 0 : funcexe->partial->pt_argc)) {
- argp = skipwhite(argp + 1); // skip the '(' or ','
+ argp = (char_u *)skipwhite((char *)argp + 1); // skip the '(' or ','
if (*argp == ')' || *argp == ',' || *argp == NUL) {
break;
}
- if (eval1(&argp, &argvars[argcount], funcexe->evaluate) == FAIL) {
+ if (eval1((char **)&argp, &argvars[argcount], funcexe->evaluate) == FAIL) {
ret = FAIL;
break;
}
@@ -455,7 +454,7 @@ int get_func_tv(const char_u *name, int len, typval_T *rettv, char_u **arg, func
((typval_T **)funcargs.ga_data)[funcargs.ga_len++] = &argvars[i];
}
}
- ret = call_func(name, len, rettv, argcount, argvars, funcexe);
+ ret = call_func((char *)name, len, rettv, argcount, argvars, funcexe);
funcargs.ga_len -= i;
} else if (!aborting()) {
@@ -470,7 +469,7 @@ int get_func_tv(const char_u *name, int len, typval_T *rettv, char_u **arg, func
tv_clear(&argvars[argcount]);
}
- *arg = skipwhite(argp);
+ *arg = (char_u *)skipwhite((char *)argp);
return ret;
}
@@ -516,22 +515,22 @@ static char_u *fname_trans_sid(const char_u *const name, char_u *const fname_buf
if (llen > 0) {
fname_buf[0] = K_SPECIAL;
fname_buf[1] = KS_EXTRA;
- fname_buf[2] = (int)KE_SNR;
+ fname_buf[2] = KE_SNR;
int i = 3;
if (eval_fname_sid((const char *)name)) { // "<SID>" or "s:"
if (current_sctx.sc_sid <= 0) {
*error = ERROR_SCRIPT;
} else {
- snprintf((char *)fname_buf + i, FLEN_FIXED + 1 - i, "%" PRId64 "_",
+ snprintf((char *)fname_buf + i, (size_t)(FLEN_FIXED + 1 - i), "%" PRId64 "_",
(int64_t)current_sctx.sc_sid);
i = (int)STRLEN(fname_buf);
}
}
- if (i + STRLEN(name + llen) < FLEN_FIXED) {
+ if ((size_t)i + STRLEN(name + llen) < FLEN_FIXED) {
STRCPY(fname_buf + i, name + llen);
fname = fname_buf;
} else {
- fname = xmalloc(i + STRLEN(name + llen) + 1);
+ fname = xmalloc((size_t)i + STRLEN(name + llen) + 1);
*tofree = fname;
memmove(fname, fname_buf, (size_t)i);
STRCPY(fname + i, name + llen);
@@ -544,23 +543,20 @@ static char_u *fname_trans_sid(const char_u *const name, char_u *const fname_buf
}
/// Find a function by name, return pointer to it in ufuncs.
-/// @return NULL for unknown function.
+///
+/// @return NULL for unknown function.
ufunc_T *find_func(const char_u *name)
{
- hashitem_T *hi;
-
- hi = hash_find(&func_hashtab, name);
+ hashitem_T *hi = hash_find(&func_hashtab, (char *)name);
if (!HASHITEM_EMPTY(hi)) {
return HI2UF(hi);
}
return NULL;
}
-/*
- * Copy the function name of "fp" to buffer "buf".
- * "buf" must be able to hold the function name plus three bytes.
- * Takes care of script-local function names.
- */
+/// Copy the function name of "fp" to buffer "buf".
+/// "buf" must be able to hold the function name plus three bytes.
+/// Takes care of script-local function names.
static void cat_func_name(char_u *buf, ufunc_T *fp)
{
if (fp->uf_name[0] == K_SPECIAL) {
@@ -571,9 +567,7 @@ static void cat_func_name(char_u *buf, ufunc_T *fp)
}
}
-/*
- * Add a number variable "name" to dict "dp" with value "nr".
- */
+/// Add a number variable "name" to dict "dp" with value "nr".
static void add_nr_var(dict_T *dp, dictitem_T *v, char *name, varnumber_T nr)
{
#ifndef __clang_analyzer__
@@ -586,7 +580,7 @@ static void add_nr_var(dict_T *dp, dictitem_T *v, char *name, varnumber_T nr)
v->di_tv.vval.v_number = nr;
}
-// Free "fc"
+/// Free "fc"
static void free_funccal(funccall_T *fc)
{
for (int i = 0; i < fc->fc_funcs.ga_len; i++) {
@@ -606,9 +600,9 @@ static void free_funccal(funccall_T *fc)
xfree(fc);
}
-// Free "fc" and what it contains.
-// Can be called only when "fc" is kept beyond the period of it called,
-// i.e. after cleanup_function_call(fc).
+/// Free "fc" and what it contains.
+/// Can be called only when "fc" is kept beyond the period of it called,
+/// i.e. after cleanup_function_call(fc).
static void free_funccal_contents(funccall_T *fc)
{
// Free all l: variables.
@@ -728,7 +722,7 @@ static void funccal_unref(funccall_T *fc, ufunc_T *fp, bool force)
/// @return true if the entry was deleted, false if it wasn't found.
static bool func_remove(ufunc_T *fp)
{
- hashitem_T *hi = hash_find(&func_hashtab, UF2HIKEY(fp));
+ hashitem_T *hi = hash_find(&func_hashtab, (char *)UF2HIKEY(fp));
if (!HASHITEM_EMPTY(hi)) {
hash_remove(&func_hashtab, hi);
@@ -757,7 +751,7 @@ static void func_clear_items(ufunc_T *fp)
/// Free all things that a function contains. Does not free the function
/// itself, use func_free() for that.
///
-/// param[in] force When true, we are exiting.
+/// @param[in] force When true, we are exiting.
static void func_clear(ufunc_T *fp, bool force)
{
if (fp->uf_cleared) {
@@ -773,7 +767,7 @@ static void func_clear(ufunc_T *fp, bool force)
/// Free a function and remove it from the list of functions. Does not free
/// what a function contains, call func_clear() first.
///
-/// param[in] fp The function to free.
+/// @param[in] fp The function to free.
static void func_free(ufunc_T *fp)
{
// only remove it when not done already, otherwise we would remove a newer
@@ -786,7 +780,7 @@ static void func_free(ufunc_T *fp)
/// Free all things that a function contains and free the function itself.
///
-/// param[in] force When true, we are exiting.
+/// @param[in] force When true, we are exiting.
static void func_clear_free(ufunc_T *fp, bool force)
{
func_clear(fp, force);
@@ -795,13 +789,13 @@ static void func_clear_free(ufunc_T *fp, bool force)
/// Call a user function
///
-/// @param fp Function to call.
-/// @param[in] argcount Number of arguments.
-/// @param argvars Arguments.
-/// @param[out] rettv Return value.
-/// @param[in] firstline First line of range.
-/// @param[in] lastline Last line of range.
-/// @param selfdict Dictionary for "self" for dictionary functions.
+/// @param fp Function to call.
+/// @param[in] argcount Number of arguments.
+/// @param argvars Arguments.
+/// @param[out] rettv Return value.
+/// @param[in] firstline First line of range.
+/// @param[in] lastline Last line of range.
+/// @param selfdict Dictionary for "self" for dictionary functions.
void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rettv,
linenr_T firstline, linenr_T lastline, dict_T *selfdict)
FUNC_ATTR_NONNULL_ARG(1, 3, 4)
@@ -944,7 +938,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
default_expr = ((char_u **)(fp->uf_def_args.ga_data))
[ai + fp->uf_def_args.ga_len];
- if (eval1(&default_expr, &def_rettv, true) == FAIL) {
+ if (eval1((char **)&default_expr, &def_rettv, true) == FAIL) {
default_arg_err = true;
break;
}
@@ -997,7 +991,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
// Don't redraw while executing the function.
RedrawingDisabled++;
- save_sourcing_name = sourcing_name;
+ save_sourcing_name = (char_u *)sourcing_name;
save_sourcing_lnum = sourcing_lnum;
sourcing_lnum = 1;
@@ -1017,7 +1011,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
{
if (save_sourcing_name != NULL
&& STRNCMP(save_sourcing_name, "function ", 9) == 0) {
- vim_snprintf((char *)sourcing_name,
+ vim_snprintf(sourcing_name,
len,
"%s[%" PRId64 "]..",
save_sourcing_name,
@@ -1025,7 +1019,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
} else {
STRCPY(sourcing_name, "function ");
}
- cat_func_name(sourcing_name + STRLEN(sourcing_name), fp);
+ cat_func_name((char_u *)sourcing_name + STRLEN(sourcing_name), fp);
if (p_verbose >= 12) {
++no_wait_return;
@@ -1048,9 +1042,8 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
if (tofree != NULL) {
char *s = tofree;
char buf[MSG_BUF_LEN];
- if (vim_strsize((char_u *)s) > MSG_BUF_CLEN) {
- trunc_string((char_u *)s, (char_u *)buf, MSG_BUF_CLEN,
- sizeof(buf));
+ if (vim_strsize(s) > MSG_BUF_CLEN) {
+ trunc_string(s, buf, MSG_BUF_CLEN, sizeof(buf));
s = buf;
}
msg_puts(s);
@@ -1106,7 +1099,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
// A Lambda always has the command "return {expr}". It is much faster
// to evaluate {expr} directly.
ex_nesting_level++;
- (void)eval1(&p, rettv, true);
+ (void)eval1((char **)&p, rettv, true);
ex_nesting_level--;
} else {
// call do_cmdline() to execute the lines
@@ -1153,14 +1146,14 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
smsg(_("%s returning #%" PRId64 ""),
sourcing_name, (int64_t)fc->rettv->vval.v_number);
} else {
- char_u buf[MSG_BUF_LEN];
+ char buf[MSG_BUF_LEN];
// The value may be very long. Skip the middle part, so that we
// have some idea how it starts and ends. smsg() would always
// truncate it at the end. Don't want errors such as E724 here.
emsg_off++;
- char_u *s = (char_u *)encode_tv2string(fc->rettv, NULL);
- char_u *tofree = s;
+ char *s = encode_tv2string(fc->rettv, NULL);
+ char *tofree = s;
emsg_off--;
if (s != NULL) {
if (vim_strsize(s) > MSG_BUF_CLEN) {
@@ -1178,7 +1171,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
}
xfree(sourcing_name);
- sourcing_name = save_sourcing_name;
+ sourcing_name = (char *)save_sourcing_name;
sourcing_lnum = save_sourcing_lnum;
current_sctx = save_current_sctx;
if (do_profiling_yes) {
@@ -1230,8 +1223,8 @@ static bool func_name_refcount(char_u *name)
static funccal_entry_T *funccal_stack = NULL;
-// Save the current function call pointer, and set it to NULL.
-// Used when executing autocommands and for ":source".
+/// Save the current function call pointer, and set it to NULL.
+/// Used when executing autocommands and for ":source".
void save_funccal(funccal_entry_T *entry)
{
entry->top_funccal = current_funccal;
@@ -1373,7 +1366,7 @@ int func_call(char_u *name, typval_T *args, partial_T *partial, dict_T *selfdict
funcexe.evaluate = true;
funcexe.partial = partial;
funcexe.selfdict = selfdict;
- r = call_func(name, -1, rettv, argc, argv, &funcexe);
+ r = call_func((char *)name, -1, rettv, argc, argv, &funcexe);
func_call_skip_call:
// Free the arguments.
@@ -1384,8 +1377,8 @@ func_call_skip_call:
return r;
}
-// Give an error message for the result of a function.
-// Nothing if "error" is FCERR_NONE.
+/// Give an error message for the result of a function.
+/// Nothing if "error" is FCERR_NONE.
static void user_func_error(int error, const char_u *name)
FUNC_ATTR_NONNULL_ALL
{
@@ -1425,7 +1418,7 @@ static void argv_add_base(typval_T *const basetv, typval_T **const argvars, int
{
if (basetv != NULL) {
// Method call: base->Method()
- memmove(&new_argvars[1], *argvars, sizeof(typval_T) * (*argcount));
+ memmove(&new_argvars[1], *argvars, sizeof(typval_T) * (size_t)(*argcount));
new_argvars[0] = *basetv;
(*argcount)++;
*argvars = new_argvars;
@@ -1445,8 +1438,8 @@ static void argv_add_base(typval_T *const basetv, typval_T **const argvars, int
/// @return FAIL if function cannot be called, else OK (even if an error
/// occurred while executing the function! Set `msg_list` to capture
/// the error, see do_cmdline()).
-int call_func(const char_u *funcname, int len, typval_T *rettv, int argcount_in,
- typval_T *argvars_in, funcexe_T *funcexe)
+int call_func(const char *funcname, int len, typval_T *rettv, int argcount_in, typval_T *argvars_in,
+ funcexe_T *funcexe)
FUNC_ATTR_NONNULL_ARG(1, 3, 5, 6)
{
int ret = FAIL;
@@ -1478,7 +1471,7 @@ int call_func(const char_u *funcname, int len, typval_T *rettv, int argcount_in,
if (fp == NULL) {
// Make a copy of the name, if it comes from a funcref variable it could
// be changed or deleted in the called function.
- name = vim_strnsave(funcname, len);
+ name = vim_strnsave((char_u *)funcname, (size_t)len);
fname = fname_trans_sid(name, fname_buf, &tofree, &error);
}
@@ -1525,11 +1518,11 @@ int call_func(const char_u *funcname, int len, typval_T *rettv, int argcount_in,
if (len > 0) {
error = ERROR_NONE;
argv_add_base(funcexe->basetv, &argvars, &argcount, argv, &argv_base);
- nlua_typval_call((const char *)funcname, len, argvars, argcount, rettv);
+ nlua_typval_call(funcname, (size_t)len, argvars, argcount, rettv);
} else {
// v:lua was called directly; show its name in the emsg
XFREE_CLEAR(name);
- funcname = (const char_u *)"v:lua";
+ funcname = "v:lua";
}
} else if (fp != NULL || !builtin_function((const char *)rfname, -1)) {
// User defined function.
@@ -1539,7 +1532,7 @@ int call_func(const char_u *funcname, int len, typval_T *rettv, int argcount_in,
// Trigger FuncUndefined event, may load the function.
if (fp == NULL
- && apply_autocmds(EVENT_FUNCUNDEFINED, rfname, rfname, true, NULL)
+ && apply_autocmds(EVENT_FUNCUNDEFINED, (char *)rfname, (char *)rfname, true, NULL)
&& !aborting()) {
// executed an autocommand, search for the function again
fp = find_func(rfname);
@@ -1611,7 +1604,7 @@ theend:
// Report an error unless the argument evaluation or function call has been
// cancelled due to an aborting error, an interrupt, or an exception.
if (!aborting()) {
- user_func_error(error, (name != NULL) ? name : funcname);
+ user_func_error(error, (name != NULL) ? name : (char_u *)funcname);
}
// clear the copies made from the partial
@@ -1713,10 +1706,10 @@ char_u *trans_function_name(char_u **pp, bool skip, int flags, funcdict_T *fdp,
// Check for hard coded <SNR>: already translated function ID (from a user
// command).
if ((*pp)[0] == K_SPECIAL && (*pp)[1] == KS_EXTRA
- && (*pp)[2] == (int)KE_SNR) {
+ && (*pp)[2] == KE_SNR) {
*pp += 3;
len = get_id_len((const char **)pp) + 3;
- return (char_u *)xmemdupz(start, len);
+ return (char_u *)xmemdupz(start, (size_t)len);
}
// A name starting with "<SID>" or "<SNR>" is local to a script. But
@@ -1727,8 +1720,8 @@ char_u *trans_function_name(char_u **pp, bool skip, int flags, funcdict_T *fdp,
}
// Note that TFN_ flags use the same values as GLV_ flags.
- end = get_lval((char_u *)start, NULL, &lv, false, skip, flags | GLV_READ_ONLY,
- lead > 2 ? 0 : FNE_CHECK_START);
+ end = (char_u *)get_lval((char *)start, NULL, &lv, false, skip, flags | GLV_READ_ONLY,
+ lead > 2 ? 0 : FNE_CHECK_START);
if (end == start) {
if (!skip) {
emsg(_("E129: Function name required"));
@@ -1746,7 +1739,7 @@ char_u *trans_function_name(char_u **pp, bool skip, int flags, funcdict_T *fdp,
semsg(_(e_invarg2), start);
}
} else {
- *pp = (char_u *)find_name_end(start, NULL, NULL, FNE_INCL_BR);
+ *pp = (char_u *)find_name_end((char *)start, NULL, NULL, FNE_INCL_BR);
}
goto theend;
}
@@ -1754,26 +1747,26 @@ char_u *trans_function_name(char_u **pp, bool skip, int flags, funcdict_T *fdp,
if (lv.ll_tv != NULL) {
if (fdp != NULL) {
fdp->fd_dict = lv.ll_dict;
- fdp->fd_newkey = lv.ll_newkey;
+ fdp->fd_newkey = (char_u *)lv.ll_newkey;
lv.ll_newkey = NULL;
fdp->fd_di = lv.ll_di;
}
if (lv.ll_tv->v_type == VAR_FUNC && lv.ll_tv->vval.v_string != NULL) {
- name = vim_strsave(lv.ll_tv->vval.v_string);
+ name = vim_strsave((char_u *)lv.ll_tv->vval.v_string);
*pp = (char_u *)end;
} else if (lv.ll_tv->v_type == VAR_PARTIAL
&& lv.ll_tv->vval.v_partial != NULL) {
if (is_luafunc(lv.ll_tv->vval.v_partial) && *end == '.') {
- len = check_luafunc_name((const char *)end+1, true);
+ len = check_luafunc_name((const char *)end + 1, true);
if (len == 0) {
semsg(e_invexpr2, "v:lua");
goto theend;
}
- name = xmallocz(len);
- memcpy(name, end+1, len);
- *pp = (char_u *)end+1+len;
+ name = xmallocz((size_t)len);
+ memcpy(name, end + 1, (size_t)len);
+ *pp = (char_u *)end + 1 + len;
} else {
- name = vim_strsave(partial_name(lv.ll_tv->vval.v_partial));
+ name = vim_strsave((char_u *)partial_name(lv.ll_tv->vval.v_partial));
*pp = (char_u *)end;
}
if (partial != NULL) {
@@ -1821,7 +1814,7 @@ char_u *trans_function_name(char_u **pp, bool skip, int flags, funcdict_T *fdp,
// Change "<SNR>" to the byte sequence.
name[0] = K_SPECIAL;
name[1] = KS_EXTRA;
- name[2] = (int)KE_SNR;
+ name[2] = KE_SNR;
memmove(name + 3, name + 5, strlen((char *)name + 5) + 1);
}
goto theend;
@@ -1864,12 +1857,11 @@ char_u *trans_function_name(char_u **pp, bool skip, int flags, funcdict_T *fdp,
emsg(_(e_usingsid));
goto theend;
}
- sid_buf_len = snprintf(sid_buf, sizeof(sid_buf),
- "%" PRIdSCID "_", current_sctx.sc_sid);
- lead += sid_buf_len;
+ sid_buf_len =
+ (size_t)snprintf(sid_buf, sizeof(sid_buf), "%" PRIdSCID "_", current_sctx.sc_sid);
+ lead += (int)sid_buf_len;
}
- } else if (!(flags & TFN_INT)
- && builtin_function(lv.ll_name, lv.ll_name_len)) {
+ } else if (!(flags & TFN_INT) && builtin_function(lv.ll_name, (int)lv.ll_name_len)) {
semsg(_("E128: Function name must start with a capital or \"s:\": %s"),
start);
goto theend;
@@ -1884,16 +1876,16 @@ char_u *trans_function_name(char_u **pp, bool skip, int flags, funcdict_T *fdp,
}
}
- name = xmalloc(len + lead + 1);
+ name = xmalloc((size_t)len + (size_t)lead + 1);
if (!skip && lead > 0) {
name[0] = K_SPECIAL;
name[1] = KS_EXTRA;
- name[2] = (int)KE_SNR;
+ name[2] = KE_SNR;
if (sid_buf_len > 0) { // If it's "<SID>"
memcpy(name + 3, sid_buf, sid_buf_len);
}
}
- memmove(name + lead, lv.ll_name, len);
+ memmove(name + lead, lv.ll_name, (size_t)len);
name[lead + len] = NUL;
*pp = (char_u *)end;
@@ -1902,14 +1894,12 @@ theend:
return name;
}
-/*
- * ":function"
- */
+/// ":function"
void ex_function(exarg_T *eap)
{
char_u *theline;
char_u *line_to_free = NULL;
- int c;
+ char_u c;
int saved_did_emsg;
bool saved_wait_return = need_wait_return;
char_u *name = NULL;
@@ -1959,7 +1949,7 @@ void ex_function(exarg_T *eap)
}
}
}
- eap->nextcmd = check_nextcmd(eap->arg);
+ eap->nextcmd = (char *)check_nextcmd((char_u *)eap->arg);
return;
}
@@ -1967,7 +1957,7 @@ void ex_function(exarg_T *eap)
* ":function /pat": list functions matching pattern.
*/
if (*eap->arg == '/') {
- p = skip_regexp(eap->arg + 1, '/', TRUE, NULL);
+ p = skip_regexp((char_u *)eap->arg + 1, '/', true, NULL);
if (!eap->skip) {
regmatch_T regmatch;
@@ -1984,7 +1974,7 @@ void ex_function(exarg_T *eap)
--todo;
fp = HI2UF(hi);
if (!isdigit(*fp->uf_name)
- && vim_regexec(&regmatch, fp->uf_name, 0)) {
+ && vim_regexec(&regmatch, (char *)fp->uf_name, 0)) {
list_func_head(fp, false, false);
}
}
@@ -1995,7 +1985,7 @@ void ex_function(exarg_T *eap)
if (*p == '/') {
++p;
}
- eap->nextcmd = check_nextcmd(p);
+ eap->nextcmd = (char *)check_nextcmd(p);
return;
}
@@ -2013,9 +2003,9 @@ void ex_function(exarg_T *eap)
// "fudi.fd_di" set, "fudi.fd_newkey" == NULL
// s:func script-local function name
// g:func global function name, same as "func"
- p = eap->arg;
+ p = (char_u *)eap->arg;
name = trans_function_name(&p, eap->skip, TFN_NO_AUTOLOAD, &fudi, NULL);
- paren = (vim_strchr(p, '(') != NULL);
+ paren = (vim_strchr((char *)p, '(') != NULL);
if (name == NULL && (fudi.fd_dict == NULL || !paren) && !eap->skip) {
/*
* Return on an invalid expression in braces, unless the expression
@@ -2045,11 +2035,11 @@ void ex_function(exarg_T *eap)
// - exclude line numbers from function body
//
if (!paren) {
- if (!ends_excmd(*skipwhite(p))) {
+ if (!ends_excmd(*skipwhite((char *)p))) {
emsg(_(e_trailing));
goto ret_free;
}
- eap->nextcmd = check_nextcmd(p);
+ eap->nextcmd = (char *)check_nextcmd(p);
if (eap->nextcmd != NULL) {
*p = NUL;
}
@@ -2089,18 +2079,18 @@ void ex_function(exarg_T *eap)
/*
* ":function name(arg1, arg2)" Define function.
*/
- p = skipwhite(p);
+ p = (char_u *)skipwhite((char *)p);
if (*p != '(') {
if (!eap->skip) {
semsg(_("E124: Missing '(': %s"), eap->arg);
goto ret_free;
}
// attempt to continue by skipping some text
- if (vim_strchr(p, '(') != NULL) {
- p = vim_strchr(p, '(');
+ if (vim_strchr((char *)p, '(') != NULL) {
+ p = (char_u *)vim_strchr((char *)p, '(');
}
}
- p = skipwhite(p + 1);
+ p = (char_u *)skipwhite((char *)p + 1);
ga_init(&newargs, (int)sizeof(char_u *), 3);
ga_init(&newlines, (int)sizeof(char_u *), 3);
@@ -2141,7 +2131,7 @@ void ex_function(exarg_T *eap)
// find extra arguments "range", "dict", "abort" and "closure"
for (;;) {
- p = skipwhite(p);
+ p = (char_u *)skipwhite((char *)p);
if (STRNCMP(p, "range", 5) == 0) {
flags |= FC_RANGE;
p += 5;
@@ -2213,7 +2203,7 @@ void ex_function(exarg_T *eap)
if (line_arg != NULL) {
// Use eap->arg, split up in parts by line breaks.
theline = line_arg;
- p = vim_strchr(theline, '\n');
+ p = (char_u *)vim_strchr((char *)theline, '\n');
if (p == NULL) {
line_arg += STRLEN(line_arg);
} else {
@@ -2225,7 +2215,7 @@ void ex_function(exarg_T *eap)
if (eap->getline == NULL) {
theline = getcmdline(':', 0L, indent, do_concat);
} else {
- theline = eap->getline(':', eap->cookie, indent, do_concat);
+ theline = (char_u *)eap->getline(':', eap->cookie, indent, do_concat);
}
line_to_free = theline;
}
@@ -2255,13 +2245,13 @@ void ex_function(exarg_T *eap)
// * ":python <<EOF" and "EOF"
// * ":let {var-name} =<< [trim] {marker}" and "{marker}"
if (heredoc_trimmed == NULL
- || (is_heredoc && skipwhite(theline) == theline)
+ || (is_heredoc && (char_u *)skipwhite((char *)theline) == theline)
|| STRNCMP(theline, heredoc_trimmed,
STRLEN(heredoc_trimmed)) == 0) {
if (heredoc_trimmed == NULL) {
p = theline;
} else if (is_heredoc) {
- p = skipwhite(theline) == theline
+ p = (char_u *)skipwhite((char *)theline) == theline
? theline : theline + STRLEN(heredoc_trimmed);
} else {
p = theline + STRLEN(heredoc_trimmed);
@@ -2275,18 +2265,17 @@ void ex_function(exarg_T *eap)
}
} else {
// skip ':' and blanks
- for (p = theline; ascii_iswhite(*p) || *p == ':'; p++) {
- }
+ for (p = theline; ascii_iswhite(*p) || *p == ':'; p++) {}
// Check for "endfunction".
- if (checkforcmd(&p, "endfunction", 4) && nesting-- == 0) {
+ if (checkforcmd((char **)&p, "endfunction", 4) && nesting-- == 0) {
if (*p == '!') {
p++;
}
char_u *nextcmd = NULL;
if (*p == '|') {
nextcmd = p + 1;
- } else if (line_arg != NULL && *skipwhite(line_arg) != NUL) {
+ } else if (line_arg != NULL && *skipwhite((char *)line_arg) != NUL) {
nextcmd = line_arg;
} else if (*p != NUL && *p != '"' && p_verbose > 0) {
give_warning2((char_u *)_("W22: Text found after :endfunction: %s"),
@@ -2296,10 +2285,10 @@ void ex_function(exarg_T *eap)
// Another command follows. If the line came from "eap" we
// can simply point into it, otherwise we need to change
// "eap->cmdlinep".
- eap->nextcmd = nextcmd;
+ eap->nextcmd = (char *)nextcmd;
if (line_to_free != NULL) {
xfree(*eap->cmdlinep);
- *eap->cmdlinep = line_to_free;
+ *eap->cmdlinep = (char *)line_to_free;
line_to_free = NULL;
}
}
@@ -2318,20 +2307,20 @@ void ex_function(exarg_T *eap)
}
// Check for defining a function inside this function.
- if (checkforcmd(&p, "function", 2)) {
+ if (checkforcmd((char **)&p, "function", 2)) {
if (*p == '!') {
- p = skipwhite(p + 1);
+ p = (char_u *)skipwhite((char *)p + 1);
}
p += eval_fname_script((const char *)p);
xfree(trans_function_name(&p, true, 0, NULL, NULL));
- if (*skipwhite(p) == '(') {
+ if (*skipwhite((char *)p) == '(') {
nesting++;
indent += 2;
}
}
// Check for ":append", ":change", ":insert".
- p = skip_range(p, NULL);
+ p = (char_u *)skip_range((char *)p, NULL);
if ((p[0] == 'a' && (!ASCII_ISALPHA(p[1]) || p[1] == 'p'))
|| (p[0] == 'c'
&& (!ASCII_ISALPHA(p[1])
@@ -2347,7 +2336,7 @@ void ex_function(exarg_T *eap)
}
// heredoc: Check for ":python <<EOF", ":lua <<EOF", etc.
- arg = skipwhite(skiptowhite(p));
+ arg = (char_u *)skipwhite((char *)skiptowhite(p));
if (arg[0] == '<' && arg[1] == '<'
&& ((p[0] == 'p' && p[1] == 'y'
&& (!ASCII_ISALNUM(p[2]) || p[2] == 't'
@@ -2364,7 +2353,7 @@ void ex_function(exarg_T *eap)
|| (p[0] == 'm' && p[1] == 'z'
&& (!ASCII_ISALPHA(p[2]) || p[2] == 's')))) {
// ":python <<" continues until a dot, like ":append"
- p = skipwhite(arg + 2);
+ p = (char_u *)skipwhite((char *)arg + 2);
if (*p == NUL) {
skip_until = vim_strsave((char_u *)".");
} else {
@@ -2374,12 +2363,12 @@ void ex_function(exarg_T *eap)
// Check for ":let v =<< [trim] EOF"
// and ":let [a, b] =<< [trim] EOF"
- arg = skipwhite(skiptowhite(p));
+ arg = (char_u *)skipwhite((char *)skiptowhite(p));
if (*arg == '[') {
- arg = vim_strchr(arg, ']');
+ arg = (char_u *)vim_strchr((char *)arg, ']');
}
if (arg != NULL) {
- arg = skipwhite(skiptowhite(arg));
+ arg = (char_u *)skipwhite((char *)skiptowhite(arg));
if (arg[0] == '='
&& arg[1] == '<'
&& arg[2] == '<'
@@ -2387,14 +2376,14 @@ void ex_function(exarg_T *eap)
&& p[1] == 'e'
&& (!ASCII_ISALNUM(p[2])
|| (p[2] == 't' && !ASCII_ISALNUM(p[3]))))) {
- p = skipwhite(arg + 3);
+ p = (char_u *)skipwhite((char *)arg + 3);
if (STRNCMP(p, "trim", 4) == 0) {
// Ignore leading white space.
- p = skipwhite(p + 4);
+ p = (char_u *)skipwhite((char *)p + 4);
heredoc_trimmed =
- vim_strnsave(theline, skipwhite(theline) - theline);
+ vim_strnsave(theline, (size_t)((char_u *)skipwhite((char *)theline) - theline));
}
- skip_until = vim_strnsave(p, skiptowhite(p) - p);
+ skip_until = vim_strnsave(p, (size_t)(skiptowhite(p) - p));
do_concat = false;
is_heredoc = true;
}
@@ -2402,7 +2391,7 @@ void ex_function(exarg_T *eap)
}
// Add the line to the function.
- ga_grow(&newlines, 1 + sourcing_lnum_off);
+ ga_grow(&newlines, 1 + (int)sourcing_lnum_off);
// Copy the line to newly allocated memory. get_one_sourceline()
// allocates 250 bytes per line, this saves 80% on average. The cost
@@ -2497,7 +2486,7 @@ void ex_function(exarg_T *eap)
}
if (fp == NULL) {
- if (fudi.fd_dict == NULL && vim_strchr(name, AUTOLOAD_CHAR) != NULL) {
+ if (fudi.fd_dict == NULL && vim_strchr((char *)name, AUTOLOAD_CHAR) != NULL) {
int slen, plen;
char_u *scriptname;
@@ -2505,10 +2494,10 @@ void ex_function(exarg_T *eap)
int j = FAIL;
if (sourcing_name != NULL) {
scriptname = (char_u *)autoload_name((const char *)name, STRLEN(name));
- p = vim_strchr(scriptname, '/');
+ p = (char_u *)vim_strchr((char *)scriptname, '/');
plen = (int)STRLEN(p);
slen = (int)STRLEN(sourcing_name);
- if (slen > plen && fnamecmp(p,
+ if (slen > plen && FNAMECMP(p,
sourcing_name + slen - plen) == 0) {
j = OK;
}
@@ -2537,7 +2526,7 @@ void ex_function(exarg_T *eap)
tv_clear(&fudi.fd_di->di_tv);
}
fudi.fd_di->di_tv.v_type = VAR_FUNC;
- fudi.fd_di->di_tv.vval.v_string = vim_strsave(name);
+ fudi.fd_di->di_tv.vval.v_string = (char *)vim_strsave(name);
// behave like "dict" was used
flags |= FC_DICT;
@@ -2546,7 +2535,7 @@ void ex_function(exarg_T *eap)
// insert the new function in the function list
STRCPY(fp->uf_name, name);
if (overwrite) {
- hi = hash_find(&func_hashtab, name);
+ hi = hash_find(&func_hashtab, (char *)name);
hi->hi_key = UF2HIKEY(fp);
} else if (hash_add(&func_hashtab, UF2HIKEY(fp)) == FAIL) {
xfree(fp);
@@ -2573,6 +2562,7 @@ void ex_function(exarg_T *eap)
fp->uf_calls = 0;
fp->uf_script_ctx = current_sctx;
fp->uf_script_ctx.sc_lnum += sourcing_lnum_top;
+ nlua_set_sctx(&fp->uf_script_ctx);
goto ret_free;
@@ -2592,13 +2582,11 @@ ret_free:
if (show_block) {
ui_ext_cmdline_block_leave();
}
-} // NOLINT(readability/fn_size)
+}
-/*
- * Return 5 if "p" starts with "<SID>" or "<SNR>" (ignoring case).
- * Return 2 if "p" starts with "s:".
- * Return 0 otherwise.
- */
+/// @return 5 if "p" starts with "<SID>" or "<SNR>" (ignoring case).
+/// 2 if "p" starts with "s:".
+/// 0 otherwise.
int eval_fname_script(const char *const p)
{
// Use mb_strnicmp() because in Turkish comparing the "I" may not work with
@@ -2624,10 +2612,10 @@ bool translated_function_exists(const char *name)
/// Check whether function with the given name exists
///
-/// @param[in] name Function name.
-/// @param[in] no_deref Whether to dereference a Funcref.
+/// @param[in] name Function name.
+/// @param[in] no_deref Whether to dereference a Funcref.
///
-/// @return True if it exists, false otherwise.
+/// @return true if it exists, false otherwise.
bool function_exists(const char *const name, bool no_deref)
{
const char_u *nm = (const char_u *)name;
@@ -2639,7 +2627,7 @@ bool function_exists(const char *const name, bool no_deref)
}
char *const p = (char *)trans_function_name((char_u **)&nm, false, flag, NULL,
NULL);
- nm = skipwhite(nm);
+ nm = (char_u *)skipwhite((char *)nm);
// Only accept "funcname", "funcname ", "funcname (..." and
// "funcname(...", not "funcname!...".
@@ -2650,11 +2638,9 @@ bool function_exists(const char *const name, bool no_deref)
return n;
}
-/*
- * Function given to ExpandGeneric() to obtain the list of user defined
- * function names.
- */
-char_u *get_user_func_name(expand_T *xp, int idx)
+/// Function given to ExpandGeneric() to obtain the list of user defined
+/// function names.
+char *get_user_func_name(expand_T *xp, int idx)
{
static size_t done;
static hashitem_T *hi;
@@ -2676,11 +2662,11 @@ char_u *get_user_func_name(expand_T *xp, int idx)
if ((fp->uf_flags & FC_DICT)
|| STRNCMP(fp->uf_name, "<lambda>", 8) == 0) {
- return (char_u *)""; // don't show dict and lambda functions
+ return ""; // don't show dict and lambda functions
}
if (STRLEN(fp->uf_name) + 4 >= IOSIZE) {
- return fp->uf_name; // Prevent overflow.
+ return (char *)fp->uf_name; // Prevent overflow.
}
cat_func_name(IObuff, fp);
@@ -2690,7 +2676,7 @@ char_u *get_user_func_name(expand_T *xp, int idx)
STRCAT(IObuff, ")");
}
}
- return IObuff;
+ return (char *)IObuff;
}
return NULL;
}
@@ -2703,7 +2689,7 @@ void ex_delfunction(exarg_T *eap)
char_u *name;
funcdict_T fudi;
- p = eap->arg;
+ p = (char_u *)eap->arg;
name = trans_function_name(&p, eap->skip, 0, &fudi, NULL);
xfree(fudi.fd_newkey);
if (name == NULL) {
@@ -2712,12 +2698,12 @@ void ex_delfunction(exarg_T *eap)
}
return;
}
- if (!ends_excmd(*skipwhite(p))) {
+ if (!ends_excmd(*skipwhite((char *)p))) {
xfree(name);
emsg(_(e_trailing));
return;
}
- eap->nextcmd = check_nextcmd(p);
+ eap->nextcmd = (char *)check_nextcmd(p);
if (eap->nextcmd != NULL) {
*p = NUL;
}
@@ -2770,10 +2756,8 @@ void ex_delfunction(exarg_T *eap)
}
}
-/*
- * Unreference a Function: decrement the reference count and free it when it
- * becomes zero.
- */
+/// Unreference a Function: decrement the reference count and free it when it
+/// becomes zero.
void func_unref(char_u *name)
{
ufunc_T *fp = NULL;
@@ -2867,12 +2851,10 @@ static int can_free_funccal(funccall_T *fc, int copyID)
&& fc->fc_copyID != copyID;
}
-/*
- * ":return [expr]"
- */
+/// ":return [expr]"
void ex_return(exarg_T *eap)
{
- char_u *arg = eap->arg;
+ char_u *arg = (char_u *)eap->arg;
typval_T rettv;
int returning = FALSE;
@@ -2887,7 +2869,7 @@ void ex_return(exarg_T *eap)
eap->nextcmd = NULL;
if ((*arg != NUL && *arg != '|' && *arg != '\n')
- && eval0(arg, &rettv, &eap->nextcmd, !eap->skip) != FAIL) {
+ && eval0((char *)arg, &rettv, &eap->nextcmd, !eap->skip) != FAIL) {
if (!eap->skip) {
returning = do_return(eap, false, true, &rettv);
} else {
@@ -2910,7 +2892,7 @@ void ex_return(exarg_T *eap)
if (returning) {
eap->nextcmd = NULL;
} else if (eap->nextcmd == NULL) { // no argument
- eap->nextcmd = check_nextcmd(arg);
+ eap->nextcmd = (char *)check_nextcmd(arg);
}
if (eap->skip) {
@@ -2920,12 +2902,10 @@ void ex_return(exarg_T *eap)
// TODO(ZyX-I): move to eval/ex_cmds
-/*
- * ":1,25call func(arg1, arg2)" function call.
- */
+/// ":1,25call func(arg1, arg2)" function call.
void ex_call(exarg_T *eap)
{
- char_u *arg = eap->arg;
+ char_u *arg = (char_u *)eap->arg;
char_u *startarg;
char_u *name;
char_u *tofree;
@@ -2974,7 +2954,7 @@ void ex_call(exarg_T *eap)
// Skip white space to allow ":call func ()". Not good, but required for
// backward compatibility.
- startarg = skipwhite(arg);
+ startarg = (char_u *)skipwhite((char *)arg);
rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this.
if (*startarg != '(') {
@@ -3011,7 +2991,7 @@ void ex_call(exarg_T *eap)
// Handle a function returning a Funcref, Dictionary or List.
if (handle_subscript((const char **)&arg, &rettv, true, true,
- (const char_u *)name, (const char_u **)&name)
+ (const char *)name, (const char **)&name)
== FAIL) {
failed = true;
break;
@@ -3031,16 +3011,17 @@ void ex_call(exarg_T *eap)
}
}
- // When inside :try we need to check for following "| catch".
- if (!failed || eap->cstack->cs_trylevel > 0) {
+ // When inside :try we need to check for following "| catch" or "| endtry".
+ // Not when there was an error, but do check if an exception was thrown.
+ if ((!aborting() || current_exception != NULL) && (!failed || eap->cstack->cs_trylevel > 0)) {
// Check for trailing illegal characters and a following command.
if (!ends_excmd(*arg)) {
- if (!failed) {
+ if (!failed && !aborting()) {
emsg_severe = true;
emsg(_(e_trailing));
}
} else {
- eap->nextcmd = check_nextcmd(arg);
+ eap->nextcmd = (char *)check_nextcmd(arg);
}
}
@@ -3049,14 +3030,16 @@ end:
xfree(tofree);
}
-/*
- * Return from a function. Possibly makes the return pending. Also called
- * for a pending return at the ":endtry" or after returning from an extra
- * do_cmdline(). "reanimate" is used in the latter case. "is_cmd" is set
- * when called due to a ":return" command. "rettv" may point to a typval_T
- * with the return rettv. Returns TRUE when the return can be carried out,
- * FALSE when the return gets pending.
- */
+/// Return from a function. Possibly makes the return pending. Also called
+/// for a pending return at the ":endtry" or after returning from an extra
+/// do_cmdline(). "reanimate" is used in the latter case.
+///
+/// @param reanimate used after returning from an extra do_cmdline().
+/// @param is_cmd set when called due to a ":return" command.
+/// @param rettv may point to a typval_T with the return rettv.
+///
+/// @return TRUE when the return can be carried out,
+/// FALSE when the return gets pending.
int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv)
{
int idx;
@@ -3067,12 +3050,10 @@ int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv)
current_funccal->returned = false;
}
- //
// Cleanup (and deactivate) conditionals, but stop when a try conditional
// not in its finally clause (which then is to be executed next) is found.
// In this case, make the ":return" pending for execution at the ":endtry".
// Otherwise, return normally.
- //
idx = cleanup_conditionals(eap->cstack, 0, true);
if (idx >= 0) {
cstack->cs_pending[idx] = CSTP_RETURN;
@@ -3125,10 +3106,8 @@ int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv)
return idx < 0;
}
-/*
- * Generate a return command for producing the value of "rettv". The result
- * is an allocated string. Used by report_pending() for verbose messages.
- */
+/// Generate a return command for producing the value of "rettv". The result
+/// is an allocated string. Used by report_pending() for verbose messages.
char_u *get_return_cmd(void *rettv)
{
char_u *s = NULL;
@@ -3150,12 +3129,11 @@ char_u *get_return_cmd(void *rettv)
return vim_strsave(IObuff);
}
-/*
- * Get next function line.
- * Called by do_cmdline() to get the next line.
- * Returns allocated string, or NULL for end of function.
- */
-char_u *get_func_line(int c, void *cookie, int indent, bool do_concat)
+/// Get next function line.
+/// Called by do_cmdline() to get the next line.
+///
+/// @return allocated string, or NULL for end of function.
+char *get_func_line(int c, void *cookie, int indent, bool do_concat)
{
funccall_T *fcp = (funccall_T *)cookie;
ufunc_T *fp = fcp->func;
@@ -3202,13 +3180,11 @@ char_u *get_func_line(int c, void *cookie, int indent, bool do_concat)
fcp->dbg_tick = debug_tick;
}
- return retval;
+ return (char *)retval;
}
-/*
- * Return TRUE if the currently active function should be ended, because a
- * return was encountered or an error occurred. Used inside a ":while".
- */
+/// @return TRUE if the currently active function should be ended, because a
+/// return was encountered or an error occurred. Used inside a ":while".
int func_has_ended(void *cookie)
{
funccall_T *fcp = (funccall_T *)cookie;
@@ -3219,9 +3195,7 @@ int func_has_ended(void *cookie)
|| fcp->returned;
}
-/*
- * return TRUE if cookie indicates a function which "abort"s on errors.
- */
+/// @return TRUE if cookie indicates a function which "abort"s on errors.
int func_has_abort(void *cookie)
{
return ((funccall_T *)cookie)->func->uf_flags & FC_ABORT;
@@ -3241,7 +3215,7 @@ void make_partial(dict_T *const selfdict, typval_T *const rettv)
fp = rettv->vval.v_partial->pt_func;
} else {
fname = rettv->v_type == VAR_FUNC || rettv->v_type == VAR_STRING
- ? rettv->vval.v_string
+ ? (char_u *)rettv->vval.v_string
: rettv->vval.v_partial->pt_name;
// Translate "s:func" to the stored function name.
fname = fname_trans_sid(fname, fname_buf, &tofree, &error);
@@ -3258,7 +3232,7 @@ void make_partial(dict_T *const selfdict, typval_T *const rettv)
pt->pt_auto = true;
if (rettv->v_type == VAR_FUNC || rettv->v_type == VAR_STRING) {
// Just a function: Take over the function name and use selfdict.
- pt->pt_name = rettv->vval.v_string;
+ pt->pt_name = (char_u *)rettv->vval.v_string;
} else {
partial_T *ret_pt = rettv->vval.v_partial;
int i;
@@ -3274,7 +3248,7 @@ void make_partial(dict_T *const selfdict, typval_T *const rettv)
func_ptr_ref(pt->pt_func);
}
if (ret_pt->pt_argc > 0) {
- size_t arg_size = sizeof(typval_T) * ret_pt->pt_argc;
+ size_t arg_size = sizeof(typval_T) * (size_t)ret_pt->pt_argc;
pt->pt_argv = (typval_T *)xmalloc(arg_size);
pt->pt_argc = ret_pt->pt_argc;
for (i = 0; i < pt->pt_argc; i++) {
@@ -3288,41 +3262,31 @@ void make_partial(dict_T *const selfdict, typval_T *const rettv)
}
}
-/*
- * Return the name of the executed function.
- */
+/// @return the name of the executed function.
char_u *func_name(void *cookie)
{
return ((funccall_T *)cookie)->func->uf_name;
}
-/*
- * Return the address holding the next breakpoint line for a funccall cookie.
- */
+/// @return the address holding the next breakpoint line for a funccall cookie.
linenr_T *func_breakpoint(void *cookie)
{
return &((funccall_T *)cookie)->breakpoint;
}
-/*
- * Return the address holding the debug tick for a funccall cookie.
- */
+/// @return the address holding the debug tick for a funccall cookie.
int *func_dbg_tick(void *cookie)
{
return &((funccall_T *)cookie)->dbg_tick;
}
-/*
- * Return the nesting level for a funccall cookie.
- */
+/// @return the nesting level for a funccall cookie.
int func_level(void *cookie)
{
return ((funccall_T *)cookie)->level;
}
-/*
- * Return TRUE when a function was ended by a ":return" command.
- */
+/// @return TRUE when a function was ended by a ":return" command.
int current_func_returned(void)
{
return current_funccal->returned;
@@ -3371,8 +3335,8 @@ funccall_T *get_funccal(void)
return funccal;
}
-/// Return the hashtable used for local variables in the current funccal.
-/// Return NULL if there is no current funccal.
+/// @return hashtable used for local variables in the current funccal or
+/// NULL if there is no current funccal.
hashtab_T *get_funccal_local_ht(void)
{
if (current_funccal == NULL) {
@@ -3381,8 +3345,8 @@ hashtab_T *get_funccal_local_ht(void)
return &get_funccal()->l_vars.dv_hashtab;
}
-/// Return the l: scope variable.
-/// Return NULL if there is no current funccal.
+/// @return the l: scope variable or
+/// NULL if there is no current funccal.
dictitem_T *get_funccal_local_var(void)
{
if (current_funccal == NULL) {
@@ -3391,8 +3355,8 @@ dictitem_T *get_funccal_local_var(void)
return (dictitem_T *)&get_funccal()->l_vars_var;
}
-/// Return the hashtable used for argument in the current funccal.
-/// Return NULL if there is no current funccal.
+/// @return the hashtable used for argument in the current funccal or
+/// NULL if there is no current funccal.
hashtab_T *get_funccal_args_ht(void)
{
if (current_funccal == NULL) {
@@ -3401,8 +3365,8 @@ hashtab_T *get_funccal_args_ht(void)
return &get_funccal()->l_avars.dv_hashtab;
}
-/// Return the a: scope variable.
-/// Return NULL if there is no current funccal.
+/// @return the a: scope variable or
+/// NULL if there is no current funccal.
dictitem_T *get_funccal_args_var(void)
{
if (current_funccal == NULL) {
@@ -3411,9 +3375,7 @@ dictitem_T *get_funccal_args_var(void)
return (dictitem_T *)&current_funccal->l_avars_var;
}
-/*
- * List function variables, if there is a function.
- */
+/// List function variables, if there is a function.
void list_func_vars(int *first)
{
if (current_funccal != NULL) {
@@ -3422,9 +3384,8 @@ void list_func_vars(int *first)
}
}
-/// If "ht" is the hashtable for local variables in the current funccal, return
-/// the dict that contains it.
-/// Otherwise return NULL.
+/// @return if "ht" is the hashtable for local variables in the current
+/// funccal, return the dict that contains it. Otherwise return NULL.
dict_T *get_current_funccal_dict(hashtab_T *ht)
{
if (current_funccal != NULL && ht == &current_funccal->l_vars.dv_hashtab) {
@@ -3450,7 +3411,7 @@ hashitem_T *find_hi_in_scoped_ht(const char *name, hashtab_T **pht)
while (current_funccal != NULL) {
hashtab_T *ht = find_var_ht(name, namelen, &varname);
if (ht != NULL && *varname != NUL) {
- hi = hash_find_len(ht, varname, namelen - (varname - name));
+ hi = hash_find_len(ht, varname, namelen - (size_t)(varname - name));
if (!HASHITEM_EMPTY(hi)) {
*pht = ht;
break;
@@ -3588,7 +3549,7 @@ bool set_ref_in_func_args(int copyID)
/// "list_stack" is used to add lists to be marked. Can be NULL.
/// "ht_stack" is used to add hashtabs to be marked. Can be NULL.
///
-/// @return true if setting references failed somehow.
+/// @return true if setting references failed somehow.
bool set_ref_in_func(char_u *name, ufunc_T *fp_in, int copyID)
{
ufunc_T *fp = fp_in;
@@ -3633,5 +3594,6 @@ char_u *register_cfunc(cfunc_T cb, cfunc_free_T cb_free, void *state)
STRCPY(fp->uf_name, name);
hash_add(&func_hashtab, UF2HIKEY(fp));
+ // coverity[leaked_storage]
return fp->uf_name;
}
diff --git a/src/nvim/event/loop.c b/src/nvim/event/loop.c
index 89fced59c5..1b5cc23b09 100644
--- a/src/nvim/event/loop.c
+++ b/src/nvim/event/loop.c
@@ -13,7 +13,6 @@
# include "event/loop.c.generated.h"
#endif
-
void loop_init(Loop *loop, void *data)
{
uv_loop_init(&loop->uv);
@@ -28,6 +27,7 @@ void loop_init(Loop *loop, void *data)
uv_signal_init(&loop->uv, &loop->children_watcher);
uv_timer_init(&loop->uv, &loop->children_kill_timer);
uv_timer_init(&loop->uv, &loop->poll_timer);
+ uv_timer_init(&loop->uv, &loop->exit_delay_timer);
loop->poll_timer.data = xmalloc(sizeof(bool)); // "timeout expired" flag
}
@@ -137,13 +137,14 @@ bool loop_close(Loop *loop, bool wait)
uv_close((uv_handle_t *)&loop->children_watcher, NULL);
uv_close((uv_handle_t *)&loop->children_kill_timer, NULL);
uv_close((uv_handle_t *)&loop->poll_timer, timer_close_cb);
+ uv_close((uv_handle_t *)&loop->exit_delay_timer, NULL);
uv_close((uv_handle_t *)&loop->async, NULL);
uint64_t start = wait ? os_hrtime() : 0;
bool didstop = false;
while (true) {
// Run the loop to tickle close-callbacks (which should then free memory).
// Use UV_RUN_NOWAIT to avoid a hang. #11820
- uv_run(&loop->uv, didstop ? UV_RUN_DEFAULT : UV_RUN_NOWAIT);
+ uv_run(&loop->uv, didstop ? UV_RUN_DEFAULT : UV_RUN_NOWAIT); // -V547
if ((uv_loop_close(&loop->uv) != UV_EBUSY) || !wait) {
break;
}
diff --git a/src/nvim/event/loop.h b/src/nvim/event/loop.h
index acd1d1a334..65980c6c05 100644
--- a/src/nvim/event/loop.h
+++ b/src/nvim/event/loop.h
@@ -10,8 +10,8 @@
typedef void *WatcherPtr;
-#define _noop(x)
-KLIST_INIT(WatcherPtr, WatcherPtr, _noop)
+#define _NOOP(x)
+KLIST_INIT(WatcherPtr, WatcherPtr, _NOOP)
typedef struct loop {
uv_loop_t uv;
@@ -36,6 +36,8 @@ typedef struct loop {
// generic timer, used by loop_poll_events()
uv_timer_t poll_timer;
+ uv_timer_t exit_delay_timer;
+
uv_async_t async;
uv_mutex_t mutex;
int recursive;
@@ -82,7 +84,6 @@ typedef struct loop {
} \
} while (0)
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "event/loop.h.generated.h"
#endif
diff --git a/src/nvim/event/multiqueue.c b/src/nvim/event/multiqueue.c
index a90cbc4e80..40d20033e0 100644
--- a/src/nvim/event/multiqueue.c
+++ b/src/nvim/event/multiqueue.c
@@ -82,7 +82,6 @@ typedef struct {
int refcount;
} MulticastEvent; ///< Event present on multiple queues.
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "event/multiqueue.c.generated.h"
#endif
diff --git a/src/nvim/event/multiqueue.h b/src/nvim/event/multiqueue.h
index dc60fbb4c7..2c5ba9d436 100644
--- a/src/nvim/event/multiqueue.h
+++ b/src/nvim/event/multiqueue.h
@@ -12,7 +12,6 @@ typedef void (*PutCallback)(MultiQueue *multiq, void *data);
#define multiqueue_put(q, h, ...) \
multiqueue_put_event(q, event_create(h, __VA_ARGS__));
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "event/multiqueue.h.generated.h"
#endif
diff --git a/src/nvim/event/process.c b/src/nvim/event/process.c
index dae4dad16d..e029f778f6 100644
--- a/src/nvim/event/process.c
+++ b/src/nvim/event/process.c
@@ -120,7 +120,7 @@ int process_spawn(Process *proc, bool in, bool out, bool err)
proc->internal_close_cb = decref;
proc->refcount++;
kl_push(WatcherPtr, proc->loop->children, proc);
- DLOG("new: pid=%d argv=[%s]", proc->pid, *proc->argv);
+ DLOG("new: pid=%d argv=[%s]", proc->pid, proc->argv[0]);
return 0;
}
@@ -237,7 +237,7 @@ void process_stop(Process *proc) FUNC_ATTR_NONNULL_ALL
KILL_TIMEOUT_MS, 0);
}
-// Frees process-owned resources.
+/// Frees process-owned resources.
void process_free(Process *proc) FUNC_ATTR_NONNULL_ALL
{
if (proc->argv != NULL) {
@@ -386,11 +386,13 @@ static void process_close_handles(void **argv)
{
Process *proc = argv[0];
+ exit_need_delay++;
flush_stream(proc, &proc->out);
flush_stream(proc, &proc->err);
process_close_streams(proc);
process_close(proc);
+ exit_need_delay--;
}
static void on_process_exit(Process *proc)
diff --git a/src/nvim/event/process.h b/src/nvim/event/process.h
index c20feb2c7a..30254bfe07 100644
--- a/src/nvim/event/process.h
+++ b/src/nvim/event/process.h
@@ -33,7 +33,6 @@ struct process {
MultiQueue *events;
};
-
static inline Process process_init(Loop *loop, ProcessType type, void *data)
{
return (Process) {
diff --git a/src/nvim/event/rstream.c b/src/nvim/event/rstream.c
index 3c43d1f98d..2847788ef8 100644
--- a/src/nvim/event/rstream.c
+++ b/src/nvim/event/rstream.c
@@ -11,8 +11,8 @@
#include "nvim/event/loop.h"
#include "nvim/event/rstream.h"
#include "nvim/log.h"
-#include "nvim/memory.h"
#include "nvim/main.h"
+#include "nvim/memory.h"
#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -42,7 +42,6 @@ void rstream_init(Stream *stream, size_t bufsize)
stream->buffer->nonfull_cb = on_rbuffer_nonfull;
}
-
/// Starts watching for events from a `Stream` instance.
///
/// @param stream The `Stream` instance
@@ -85,7 +84,7 @@ static void on_rbuffer_nonfull(RBuffer *buf, void *data)
// Callbacks used by libuv
-// Called by libuv to allocate memory for reading.
+/// Called by libuv to allocate memory for reading.
static void alloc_cb(uv_handle_t *handle, size_t suggested, uv_buf_t *buf)
{
Stream *stream = handle->data;
@@ -95,9 +94,9 @@ static void alloc_cb(uv_handle_t *handle, size_t suggested, uv_buf_t *buf)
buf->len = UV_BUF_LEN(write_count);
}
-// Callback invoked by libuv after it copies the data into the buffer provided
-// by `alloc_cb`. This is also called on EOF or when `alloc_cb` returns a
-// 0-length buffer.
+/// Callback invoked by libuv after it copies the data into the buffer provided
+/// by `alloc_cb`. This is also called on EOF or when `alloc_cb` returns a
+/// 0-length buffer.
static void read_cb(uv_stream_t *uvstream, ssize_t cnt, const uv_buf_t *buf)
{
Stream *stream = uvstream->data;
@@ -134,11 +133,11 @@ static void read_cb(uv_stream_t *uvstream, ssize_t cnt, const uv_buf_t *buf)
invoke_read_cb(stream, nread, false);
}
-// Called by the by the 'idle' handle to emulate a reading event
-//
-// Idle callbacks are invoked once per event loop:
-// - to perform some very low priority activity.
-// - to keep the loop "alive" (so there is always an event to process)
+/// Called by the by the 'idle' handle to emulate a reading event
+///
+/// Idle callbacks are invoked once per event loop:
+/// - to perform some very low priority activity.
+/// - to keep the loop "alive" (so there is always an event to process)
static void fread_idle_cb(uv_idle_t *handle)
{
uv_fs_t req;
diff --git a/src/nvim/event/rstream.h b/src/nvim/event/rstream.h
index 77418c59a2..23ed00bfea 100644
--- a/src/nvim/event/rstream.h
+++ b/src/nvim/event/rstream.h
@@ -8,7 +8,6 @@
#include "nvim/event/loop.h"
#include "nvim/event/stream.h"
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "event/rstream.h.generated.h"
#endif
diff --git a/src/nvim/event/signal.c b/src/nvim/event/signal.c
index fec46da4ff..4a45a2ec2f 100644
--- a/src/nvim/event/signal.c
+++ b/src/nvim/event/signal.c
@@ -10,7 +10,6 @@
# include "event/signal.c.generated.h"
#endif
-
void signal_watcher_init(Loop *loop, SignalWatcher *watcher, void *data)
FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_NONNULL_ARG(2)
{
diff --git a/src/nvim/event/socket.c b/src/nvim/event/socket.c
index 7948a7be83..9496a568b9 100644
--- a/src/nvim/event/socket.c
+++ b/src/nvim/event/socket.c
@@ -38,7 +38,7 @@ int socket_watcher_init(Loop *loop, SocketWatcher *watcher, const char *endpoint
char *port = host_end + 1;
intmax_t iport;
- int ok = try_getdigits(&(char_u *){ (char_u *)port }, &iport);
+ int ok = try_getdigits(&(char *){ port }, &iport);
if (!ok || iport < 0 || iport > UINT16_MAX) {
ELOG("Invalid port: %s", port);
return UV_EINVAL;
@@ -53,10 +53,8 @@ int socket_watcher_init(Loop *loop, SocketWatcher *watcher, const char *endpoint
uv_getaddrinfo_t request;
int retval = uv_getaddrinfo(&loop->uv, &request, NULL, addr, port,
- &(struct addrinfo){
- .ai_family = AF_UNSPEC,
- .ai_socktype = SOCK_STREAM,
- });
+ &(struct addrinfo){ .ai_family = AF_UNSPEC,
+ .ai_socktype = SOCK_STREAM, });
if (retval != 0) {
ELOG("Host lookup failed: %s", endpoint);
return retval;
@@ -103,13 +101,12 @@ int socket_watcher_start(SocketWatcher *watcher, int backlog, socket_cb cb)
// contain 0 in this case, unless uv_tcp_getsockname() is used first.
uv_tcp_getsockname(&watcher->uv.tcp.handle, (struct sockaddr *)&sas,
&(int){ sizeof(sas) });
- uint16_t port = (uint16_t)(
- (sas.ss_family == AF_INET)
- ? (STRUCT_CAST(struct sockaddr_in, &sas))->sin_port
- : (STRUCT_CAST(struct sockaddr_in6, &sas))->sin6_port);
+ uint16_t port = (uint16_t)((sas.ss_family == AF_INET)
+ ? (STRUCT_CAST(struct sockaddr_in, &sas))->sin_port
+ : (STRUCT_CAST(struct sockaddr_in6, &sas))->sin6_port);
// v:servername uses the string from watcher->addr
size_t len = strlen(watcher->addr);
- snprintf(watcher->addr+len, sizeof(watcher->addr)-len, ":%" PRIu16,
+ snprintf(watcher->addr + len, sizeof(watcher->addr) - len, ":%" PRIu16,
ntohs(port));
break;
}
@@ -127,7 +124,7 @@ int socket_watcher_start(SocketWatcher *watcher, int backlog, socket_cb cb)
if (result == UV_EACCES) {
// Libuv converts ENOENT to EACCES for Windows compatibility, but if
// the parent directory does not exist, ENOENT would be more accurate.
- *path_tail((char_u *)watcher->addr) = NUL;
+ *path_tail(watcher->addr) = NUL;
if (!os_path_exists((char_u *)watcher->addr)) {
result = UV_ENOENT;
}
@@ -228,7 +225,7 @@ bool socket_connect(Loop *loop, Stream *stream, bool is_tcp, const char *address
.ai_socktype = SOCK_STREAM,
.ai_flags = AI_NUMERICSERV };
int retval = uv_getaddrinfo(&loop->uv, &addr_req, NULL,
- addr, host_end+1, &hints);
+ addr, host_end + 1, &hints);
if (retval != 0) {
*error = _("failed to lookup host or port");
goto cleanup;
diff --git a/src/nvim/event/time.c b/src/nvim/event/time.c
index aa7b9cf2a1..c997e3c558 100644
--- a/src/nvim/event/time.c
+++ b/src/nvim/event/time.c
@@ -11,7 +11,6 @@
# include "event/time.c.generated.h"
#endif
-
void time_watcher_init(Loop *loop, TimeWatcher *watcher, void *data)
FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_NONNULL_ARG(2)
{
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 95390b1a70..28e1893b31 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -14,7 +14,6 @@
#include <string.h>
#include "nvim/api/buffer.h"
-#include "nvim/api/extmark.h"
#include "nvim/api/private/defs.h"
#include "nvim/ascii.h"
#include "nvim/buffer.h"
@@ -38,6 +37,7 @@
#include "nvim/garray.h"
#include "nvim/getchar.h"
#include "nvim/highlight.h"
+#include "nvim/highlight_group.h"
#include "nvim/indent.h"
#include "nvim/input.h"
#include "nvim/log.h"
@@ -72,7 +72,6 @@
#include "nvim/vim.h"
#include "nvim/window.h"
-
/// Case matching style to use for :substitute
typedef enum {
kSubHonorOptions = 0, ///< Honor the user's 'ignorecase'/'smartcase' options
@@ -111,12 +110,10 @@ typedef struct {
# include "ex_cmds.c.generated.h"
#endif
-static int preview_bufnr = 0;
-
/// ":ascii" and "ga" implementation
void do_ascii(const exarg_T *const eap)
{
- char_u *dig;
+ char *dig;
int cc[MAX_MCO];
int c = utfc_ptr2char(get_cursor_pos_ptr(), cc);
if (c == NUL) {
@@ -136,8 +133,8 @@ void do_ascii(const exarg_T *const eap)
: c);
char buf1[20];
if (vim_isprintc_strict(c) && (c < ' ' || c > '~')) {
- char_u buf3[7];
- transchar_nonprint(curbuf, buf3, c);
+ char buf3[7];
+ transchar_nonprint(curbuf, (char_u *)buf3, c);
vim_snprintf(buf1, sizeof(buf1), " <%s>", (char *)buf3);
} else {
buf1[0] = NUL;
@@ -145,19 +142,17 @@ void do_ascii(const exarg_T *const eap)
char buf2[20];
buf2[0] = NUL;
- dig = get_digraph_for_char(cval);
+ dig = (char *)get_digraph_for_char(cval);
if (dig != NULL) {
- iobuff_len += (
- vim_snprintf((char *)IObuff + iobuff_len,
- sizeof(IObuff) - iobuff_len,
- _("<%s>%s%s %d, Hex %02x, Oct %03o, Digr %s"),
- transchar(c), buf1, buf2, cval, cval, cval, dig));
+ iobuff_len += (size_t)vim_snprintf((char *)IObuff + iobuff_len,
+ sizeof(IObuff) - iobuff_len,
+ _("<%s>%s%s %d, Hex %02x, Oct %03o, Digr %s"),
+ transchar(c), buf1, buf2, cval, cval, cval, dig);
} else {
- iobuff_len += (
- vim_snprintf((char *)IObuff + iobuff_len,
- sizeof(IObuff) - iobuff_len,
- _("<%s>%s%s %d, Hex %02x, Octal %03o"),
- transchar(c), buf1, buf2, cval, cval, cval));
+ iobuff_len += (size_t)vim_snprintf((char *)IObuff + iobuff_len,
+ sizeof(IObuff) - iobuff_len,
+ _("<%s>%s%s %d, Hex %02x, Octal %03o"),
+ transchar(c), buf1, buf2, cval, cval, cval);
}
c = cc[ci++];
@@ -193,25 +188,23 @@ void do_ascii(const exarg_T *const eap)
if (utf_iscomposing(c)) {
IObuff[iobuff_len++] = ' '; // Draw composing char on top of a space.
}
- iobuff_len += utf_char2bytes(c, IObuff + iobuff_len);
+ iobuff_len += (size_t)utf_char2bytes(c, (char *)IObuff + iobuff_len);
- dig = get_digraph_for_char(c);
+ dig = (char *)get_digraph_for_char(c);
if (dig != NULL) {
- iobuff_len += (
- vim_snprintf((char *)IObuff + iobuff_len,
- sizeof(IObuff) - iobuff_len,
- (c < 0x10000
- ? _("> %d, Hex %04x, Oct %o, Digr %s")
- : _("> %d, Hex %08x, Oct %o, Digr %s")),
- c, c, c, dig));
+ iobuff_len += (size_t)vim_snprintf((char *)IObuff + iobuff_len,
+ sizeof(IObuff) - iobuff_len,
+ (c < 0x10000
+ ? _("> %d, Hex %04x, Oct %o, Digr %s")
+ : _("> %d, Hex %08x, Oct %o, Digr %s")),
+ c, c, c, dig);
} else {
- iobuff_len += (
- vim_snprintf((char *)IObuff + iobuff_len,
- sizeof(IObuff) - iobuff_len,
- (c < 0x10000
- ? _("> %d, Hex %04x, Octal %o")
- : _("> %d, Hex %08x, Octal %o")),
- c, c, c));
+ iobuff_len += (size_t)vim_snprintf((char *)IObuff + iobuff_len,
+ sizeof(IObuff) - iobuff_len,
+ (c < 0x10000
+ ? _("> %d, Hex %04x, Octal %o")
+ : _("> %d, Hex %08x, Octal %o")),
+ c, c, c);
}
if (ci == MAX_MCO) {
break;
@@ -225,9 +218,7 @@ void do_ascii(const exarg_T *const eap)
msg((char *)IObuff);
}
-/*
- * ":left", ":center" and ":right": align text.
- */
+/// ":left", ":center" and ":right": align text.
void ex_align(exarg_T *eap)
{
pos_T save_curpos;
@@ -246,7 +237,7 @@ void ex_align(exarg_T *eap)
}
}
- width = atoi((char *)eap->arg);
+ width = atoi(eap->arg);
save_curpos = curwin->w_cursor;
if (eap->cmdidx == CMD_left) { // width is used for new indent
if (width >= 0) {
@@ -259,10 +250,10 @@ void ex_align(exarg_T *eap)
* if invalid value, use 80
*/
if (width <= 0) {
- width = curbuf->b_p_tw;
+ width = (int)curbuf->b_p_tw;
}
if (width == 0 && curbuf->b_p_wm > 0) {
- width = curwin->w_width_inner - curbuf->b_p_wm;
+ width = curwin->w_width_inner - (int)curbuf->b_p_wm;
}
if (width <= 0) {
width = 80;
@@ -324,20 +315,17 @@ void ex_align(exarg_T *eap)
beginline(BL_WHITE | BL_FIX);
}
-/*
- * Get the length of the current line, excluding trailing white space.
- */
+/// @return the length of the current line, excluding trailing white space.
static int linelen(int *has_tab)
{
- char_u *line;
- char_u *first;
- char_u *last;
- int save;
+ char *line;
+ char *first;
+ char *last;
int len;
// Get the line. If it's empty bail out early (could be the empty string
// for an unloaded buffer).
- line = get_cursor_line_ptr();
+ line = (char *)get_cursor_line_ptr();
if (*line == NUL) {
return 0;
}
@@ -346,12 +334,11 @@ static int linelen(int *has_tab)
// find the character after the last non-blank character
for (last = first + STRLEN(first);
- last > first && ascii_iswhite(last[-1]); last--) {
- }
- save = *last;
+ last > first && ascii_iswhite(last[-1]); last--) {}
+ char save = *last;
*last = NUL;
// Get line length.
- len = linetabsize(line);
+ len = linetabsize((char_u *)line);
// Check for embedded TAB.
if (has_tab != NULL) {
*has_tab = vim_strchr(first, TAB) != NULL;
@@ -363,8 +350,8 @@ static int linelen(int *has_tab)
// Buffer for two lines used during sorting. They are allocated to
// contain the longest line being sorted.
-static char_u *sortbuf1;
-static char_u *sortbuf2;
+static char *sortbuf1;
+static char *sortbuf2;
static int sort_lc; ///< sort using locale
static int sort_ic; ///< ignore case
@@ -436,10 +423,10 @@ static int sort_compare(const void *s1, const void *s2)
// guarantee that the first pointer becomes invalid when obtaining the
// second one.
memcpy(sortbuf1, ml_get(l1.lnum) + l1.st_u.line.start_col_nr,
- l1.st_u.line.end_col_nr - l1.st_u.line.start_col_nr + 1);
+ (size_t)(l1.st_u.line.end_col_nr - l1.st_u.line.start_col_nr + 1));
sortbuf1[l1.st_u.line.end_col_nr - l1.st_u.line.start_col_nr] = NUL;
memcpy(sortbuf2, ml_get(l2.lnum) + l2.st_u.line.start_col_nr,
- l2.st_u.line.end_col_nr - l2.st_u.line.start_col_nr + 1);
+ (size_t)(l2.st_u.line.end_col_nr - l2.st_u.line.start_col_nr + 1));
sortbuf2[l2.st_u.line.end_col_nr - l2.st_u.line.start_col_nr] = NUL;
result = string_compare(sortbuf1, sortbuf2);
@@ -447,24 +434,24 @@ static int sort_compare(const void *s1, const void *s2)
// If two lines have the same value, preserve the original line order.
if (result == 0) {
- return (int)(l1.lnum - l2.lnum);
+ return l1.lnum - l2.lnum;
}
return result;
}
-// ":sort".
+/// ":sort".
void ex_sort(exarg_T *eap)
{
regmatch_T regmatch;
int len;
linenr_T lnum;
long maxlen = 0;
- size_t count = (size_t)(eap->line2 - eap->line1 + 1);
+ size_t count = (size_t)(eap->line2 - eap->line1) + 1;
size_t i;
- char_u *p;
- char_u *s;
- char_u *s2;
- char_u c; // temporary character storage
+ char *p;
+ char *s;
+ char *s2;
+ char c; // temporary character storage
bool unique = false;
long deleted;
colnr_T start_col;
@@ -488,8 +475,9 @@ void ex_sort(exarg_T *eap)
size_t format_found = 0;
bool change_occurred = false; // Buffer contents changed.
- for (p = eap->arg; *p != NUL; ++p) {
+ for (p = eap->arg; *p != NUL; p++) {
if (ascii_iswhite(*p)) {
+ // Skip
} else if (*p == 'i') {
sort_ic = true;
} else if (*p == 'l') {
@@ -516,11 +504,11 @@ void ex_sort(exarg_T *eap)
} else if (*p == '"') {
// comment start
break;
- } else if (check_nextcmd(p) != NULL) {
- eap->nextcmd = check_nextcmd(p);
+ } else if (check_nextcmd((char_u *)p) != NULL) {
+ eap->nextcmd = (char *)check_nextcmd((char_u *)p);
break;
} else if (!ASCII_ISALPHA(*p) && regmatch.regprog == NULL) {
- s = skip_regexp(p + 1, *p, true, NULL);
+ s = (char *)skip_regexp((char_u *)p + 1, *p, true, NULL);
if (*s != *p) {
emsg(_(e_invalpat));
goto sortend;
@@ -532,7 +520,7 @@ void ex_sort(exarg_T *eap)
emsg(_(e_noprevre));
goto sortend;
}
- regmatch.regprog = vim_regcomp(last_search_pat(), RE_MAGIC);
+ regmatch.regprog = vim_regcomp((char *)last_search_pat(), RE_MAGIC);
} else {
regmatch.regprog = vim_regcomp(p + 1, RE_MAGIC);
}
@@ -563,8 +551,8 @@ void ex_sort(exarg_T *eap)
// numbers sorting it's the number to sort on. This means the pattern
// matching and number conversion only has to be done once per line.
// Also get the longest line length for allocating "sortbuf".
- for (lnum = eap->line1; lnum <= eap->line2; ++lnum) {
- s = ml_get(lnum);
+ for (lnum = eap->line1; lnum <= eap->line2; lnum++) {
+ s = (char *)ml_get(lnum);
len = (int)STRLEN(s);
if (maxlen < len) {
maxlen = len;
@@ -574,10 +562,10 @@ void ex_sort(exarg_T *eap)
end_col = len;
if (regmatch.regprog != NULL && vim_regexec(&regmatch, s, 0)) {
if (sort_rx) {
- start_col = (colnr_T)(regmatch.startp[0] - s);
- end_col = (colnr_T)(regmatch.endp[0] - s);
+ start_col = (colnr_T)(regmatch.startp[0] - (char_u *)s);
+ end_col = (colnr_T)(regmatch.endp[0] - (char_u *)s);
} else {
- start_col = (colnr_T)(regmatch.endp[0] - s);
+ start_col = (colnr_T)(regmatch.endp[0] - (char_u *)s);
}
} else if (regmatch.regprog != NULL) {
end_col = 0;
@@ -593,11 +581,11 @@ void ex_sort(exarg_T *eap)
p = s + start_col;
if (sort_nr) {
if (sort_what & STR2NR_HEX) {
- s = skiptohex(p);
+ s = (char *)skiptohex((char_u *)p);
} else if (sort_what & STR2NR_BIN) {
- s = (char_u *)skiptobin((char *)p);
+ s = (char *)skiptobin(p);
} else {
- s = skiptodigit(p);
+ s = (char *)skiptodigit((char_u *)p);
}
if (s > p && s[-1] == '-') {
s--; // include preceding negative sign
@@ -608,7 +596,7 @@ void ex_sort(exarg_T *eap)
nrs[lnum - eap->line1].st_u.num.value = 0;
} else {
nrs[lnum - eap->line1].st_u.num.is_number = true;
- vim_str2nr(s, NULL, NULL, sort_what,
+ vim_str2nr((char_u *)s, NULL, NULL, sort_what,
&nrs[lnum - eap->line1].st_u.num.value, NULL, 0, false);
}
} else {
@@ -621,7 +609,7 @@ void ex_sort(exarg_T *eap)
// empty line should sort before any number
nrs[lnum - eap->line1].st_u.value_flt = -DBL_MAX;
} else {
- nrs[lnum - eap->line1].st_u.value_flt = strtod((char *)s, NULL);
+ nrs[lnum - eap->line1].st_u.value_flt = strtod(s, NULL);
}
}
*s2 = c;
@@ -642,8 +630,8 @@ void ex_sort(exarg_T *eap)
}
// Allocate a buffer that can hold the longest line.
- sortbuf1 = xmalloc(maxlen + 1);
- sortbuf2 = xmalloc(maxlen + 1);
+ sortbuf1 = xmalloc((size_t)maxlen + 1);
+ sortbuf2 = xmalloc((size_t)maxlen + 1);
// Sort the array of line numbers. Note: can't be interrupted!
qsort((void *)nrs, count, sizeof(sorti_T), sort_compare);
@@ -665,9 +653,9 @@ void ex_sort(exarg_T *eap)
change_occurred = true;
}
- s = ml_get(get_lnum);
+ s = (char *)ml_get(get_lnum);
size_t bytelen = STRLEN(s) + 1; // include EOL in bytelen
- old_count += bytelen;
+ old_count += (bcount_t)bytelen;
if (!unique || i == 0 || string_compare(s, sortbuf1) != 0) {
// Copy the line into a buffer, it may become invalid in
// ml_append(). And it's needed for "unique".
@@ -675,7 +663,7 @@ void ex_sort(exarg_T *eap)
if (ml_append(lnum++, sortbuf1, (colnr_T)0, false) == FAIL) {
break;
}
- new_count += bytelen;
+ new_count += (bcount_t)bytelen;
}
fast_breakcheck();
if (got_int) {
@@ -693,21 +681,21 @@ void ex_sort(exarg_T *eap)
}
// Adjust marks for deleted (or added) lines and prepare for displaying.
- deleted = (long)(count - (lnum - eap->line2));
+ deleted = (long)count - (lnum - eap->line2);
if (deleted > 0) {
- mark_adjust(eap->line2 - deleted, eap->line2, (long)MAXLNUM, -deleted,
+ mark_adjust(eap->line2 - (linenr_T)deleted, eap->line2, (long)MAXLNUM, (linenr_T)(-deleted),
kExtmarkNOOP);
msgmore(-deleted);
} else if (deleted < 0) {
- mark_adjust(eap->line2, MAXLNUM, -deleted, 0L, kExtmarkNOOP);
+ mark_adjust(eap->line2, MAXLNUM, (linenr_T)(-deleted), 0L, kExtmarkNOOP);
}
if (change_occurred || deleted != 0) {
- extmark_splice(curbuf, eap->line1-1, 0,
- count, 0, old_count,
+ extmark_splice(curbuf, eap->line1 - 1, 0,
+ (int)count, 0, old_count,
lnum - eap->line2, 0, new_count, kExtmarkUndo);
- changed_lines(eap->line1, 0, eap->line2 + 1, -deleted, true);
+ changed_lines(eap->line1, 0, eap->line2 + 1, (linenr_T)(-deleted), true);
}
curwin->w_cursor.lnum = eap->line1;
@@ -723,9 +711,7 @@ sortend:
}
}
-/*
- * ":retab".
- */
+/// ":retab".
void ex_retab(exarg_T *eap)
{
linenr_T lnum;
@@ -738,11 +724,11 @@ void ex_retab(exarg_T *eap)
long start_col = 0; // For start of white-space string
long start_vcol = 0; // For start of white-space string
long old_len;
- char_u *ptr;
- char_u *new_line = (char_u *)1; // init to non-NULL
+ char *ptr;
+ char *new_line = (char *)1; // init to non-NULL
bool did_undo; // called u_save for current line
long *new_vts_array = NULL;
- char_u *new_ts_str; // string value of tab argument
+ char *new_ts_str; // string value of tab argument
int save_list;
linenr_T first_line = 0; // first changed line
@@ -752,7 +738,7 @@ void ex_retab(exarg_T *eap)
curwin->w_p_list = 0; // don't want list mode here
new_ts_str = eap->arg;
- if (!tabstop_set(eap->arg, &new_vts_array)) {
+ if (!tabstop_set((char_u *)eap->arg, &new_vts_array)) {
return;
}
while (ascii_isdigit(*(eap->arg)) || *(eap->arg) == ',') {
@@ -766,10 +752,10 @@ void ex_retab(exarg_T *eap)
new_vts_array = curbuf->b_p_vts_array;
new_ts_str = NULL;
} else {
- new_ts_str = vim_strnsave(new_ts_str, eap->arg - new_ts_str);
+ new_ts_str = xstrnsave(new_ts_str, (size_t)(eap->arg - new_ts_str));
}
for (lnum = eap->line1; !got_int && lnum <= eap->line2; lnum++) {
- ptr = ml_get(lnum);
+ ptr = (char *)ml_get(lnum);
col = 0;
vcol = 0;
did_undo = false;
@@ -795,7 +781,7 @@ void ex_retab(exarg_T *eap)
if (!curbuf->b_p_et) {
int t, s;
- tabstop_fromto(start_vcol, vcol,
+ tabstop_fromto((colnr_T)start_vcol, (colnr_T)vcol,
curbuf->b_p_ts, new_vts_array, &t, &s);
num_tabs = t;
num_spaces = s;
@@ -814,8 +800,12 @@ void ex_retab(exarg_T *eap)
// len is actual number of white characters used
len = num_spaces + num_tabs;
old_len = (long)STRLEN(ptr);
- long new_len = old_len - col + start_col + len + 1;
- new_line = xmalloc(new_len);
+ const long new_len = old_len - col + start_col + len + 1;
+ if (new_len <= 0 || new_len >= MAXCOL) {
+ emsg(_(e_resulting_text_too_long));
+ break;
+ }
+ new_line = xmalloc((size_t)new_len);
if (start_col > 0) {
memmove(new_line, ptr, (size_t)start_col);
@@ -828,7 +818,7 @@ void ex_retab(exarg_T *eap)
}
if (ml_replace(lnum, new_line, false) == OK) {
// "new_line" may have been copied
- new_line = curbuf->b_ml.ml_line_ptr;
+ new_line = (char *)curbuf->b_ml.ml_line_ptr;
extmark_splice_cols(curbuf, lnum - 1, 0, (colnr_T)old_len,
(colnr_T)new_len - 1, kExtmarkUndo);
}
@@ -846,7 +836,11 @@ void ex_retab(exarg_T *eap)
if (ptr[col] == NUL) {
break;
}
- vcol += win_chartabsize(curwin, ptr + col, (colnr_T)vcol);
+ vcol += win_chartabsize(curwin, (char_u *)ptr + col, (colnr_T)vcol);
+ if (vcol >= MAXCOL) {
+ emsg(_(e_resulting_text_too_long));
+ break;
+ }
col += utfc_ptr2len(ptr + col);
}
if (new_line == NULL) { // out of memory
@@ -882,8 +876,7 @@ void ex_retab(exarg_T *eap)
long *old_vts_ary = curbuf->b_p_vts_array;
if (tabstop_count(old_vts_ary) > 0 || tabstop_count(new_vts_array) > 1) {
- set_string_option_direct("vts", -1, new_ts_str,
- OPT_FREE | OPT_LOCAL, 0);
+ set_string_option_direct("vts", -1, new_ts_str, OPT_FREE | OPT_LOCAL, 0);
curbuf->b_p_vts_array = new_vts_array;
xfree(old_vts_ary);
} else {
@@ -899,14 +892,12 @@ void ex_retab(exarg_T *eap)
u_clearline();
}
-/*
- * :move command - move lines line1-line2 to line dest
- *
- * return FAIL for failure, OK otherwise
- */
+/// :move command - move lines line1-line2 to line dest
+///
+/// @return FAIL for failure, OK otherwise
int do_move(linenr_T line1, linenr_T line2, linenr_T dest)
{
- char_u *str;
+ char *str;
linenr_T l;
linenr_T extra; // Num lines added before line1
linenr_T num_lines; // Num lines moved
@@ -931,9 +922,9 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest)
}
bcount_t start_byte = ml_find_line_or_offset(curbuf, line1, NULL, true);
- bcount_t end_byte = ml_find_line_or_offset(curbuf, line2+1, NULL, true);
- bcount_t extent_byte = end_byte-start_byte;
- bcount_t dest_byte = ml_find_line_or_offset(curbuf, dest+1, NULL, true);
+ bcount_t end_byte = ml_find_line_or_offset(curbuf, line2 + 1, NULL, true);
+ bcount_t extent_byte = end_byte - start_byte;
+ bcount_t dest_byte = ml_find_line_or_offset(curbuf, dest + 1, NULL, true);
num_lines = line2 - line1 + 1;
@@ -945,7 +936,7 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest)
return FAIL;
}
for (extra = 0, l = line1; l <= line2; l++) {
- str = vim_strsave(ml_get(l + extra));
+ str = (char *)vim_strsave(ml_get(l + extra));
ml_append(dest + l - line1, str, (colnr_T)0, false);
xfree(str);
if (dest < line1) {
@@ -970,7 +961,11 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest)
*/
last_line = curbuf->b_ml.ml_line_count;
mark_adjust_nofold(line1, line2, last_line - line2, 0L, kExtmarkNOOP);
+
+ disable_fold_update++;
changed_lines(last_line - num_lines + 1, 0, last_line + 1, num_lines, false);
+ disable_fold_update--;
+
int line_off = 0;
bcount_t byte_off = 0;
if (dest >= line2) {
@@ -980,8 +975,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.cmod_flags & CMOD_LOCKMARKS) == 0) {
+ 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,17 +988,23 @@ 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.cmod_flags & CMOD_LOCKMARKS) == 0) {
+ curbuf->b_op_start.lnum = dest + 1;
+ curbuf->b_op_end.lnum = dest + num_lines;
+ }
+ }
+ if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) {
+ 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);
+ disable_fold_update++;
changed_lines(last_line - num_lines + 1, 0, last_line + 1, -extra, false);
+ disable_fold_update--;
// send update regarding the new lines that were added
- buf_updates_send_changes(curbuf, dest + 1, num_lines, 0, true);
+ buf_updates_send_changes(curbuf, dest + 1, num_lines, 0);
/*
* Now we delete the original text -- webb
@@ -1017,9 +1020,9 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest)
smsg(NGETTEXT("1 line moved", "%" PRId64 " lines moved", num_lines), (int64_t)num_lines);
}
- extmark_move_region(curbuf, line1-1, 0, start_byte,
- line2-line1+1, 0, extent_byte,
- dest+line_off, 0, dest_byte+byte_off,
+ extmark_move_region(curbuf, line1 - 1, 0, start_byte,
+ line2 - line1 + 1, 0, extent_byte,
+ dest + line_off, 0, dest_byte + byte_off,
kExtmarkUndo);
/*
@@ -1043,23 +1046,23 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest)
}
// send nvim_buf_lines_event regarding lines that were deleted
- buf_updates_send_changes(curbuf, line1 + extra, 0, num_lines, true);
+ buf_updates_send_changes(curbuf, line1 + extra, 0, num_lines);
return OK;
}
-/*
- * ":copy"
- */
+/// ":copy"
void ex_copy(linenr_T line1, linenr_T line2, linenr_T n)
{
linenr_T count;
- char_u *p;
+ char *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.cmod_flags & CMOD_LOCKMARKS) == 0) {
+ 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:
@@ -1080,7 +1083,7 @@ void ex_copy(linenr_T line1, linenr_T line2, linenr_T n)
while (line1 <= line2) {
// need to use vim_strsave() because the line will be unlocked within
// ml_append()
- p = vim_strsave(ml_get(line1));
+ p = (char *)vim_strsave(ml_get(line1));
ml_append(curwin->w_cursor.lnum, p, (colnr_T)0, false);
xfree(p);
@@ -1099,11 +1102,14 @@ 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);
}
-static char_u *prevcmd = NULL; // the previous command
+static char *prevcmd = NULL; // the previous command
#if defined(EXITFREE)
void free_prev_shellcmd(void)
@@ -1113,23 +1119,21 @@ void free_prev_shellcmd(void)
#endif
-/*
- * Handle the ":!cmd" command. Also for ":r !cmd" and ":w !cmd"
- * Bangs in the argument are replaced with the previously entered command.
- * Remember the argument.
- */
+/// Handle the ":!cmd" command. Also for ":r !cmd" and ":w !cmd"
+/// Bangs in the argument are replaced with the previously entered command.
+/// Remember the argument.
void do_bang(int addr_count, exarg_T *eap, bool forceit, bool do_in, bool do_out)
FUNC_ATTR_NONNULL_ALL
{
- char_u *arg = eap->arg; // command
+ char *arg = eap->arg; // command
linenr_T line1 = eap->line1; // start of range
linenr_T line2 = eap->line2; // end of range
- char_u *newcmd = NULL; // the new command
+ char *newcmd = NULL; // the new command
bool free_newcmd = false; // need to free() newcmd
- char_u *t;
- char_u *p;
- char_u *trailarg;
- int len;
+ char *t;
+ char *p;
+ char *trailarg;
+ size_t len;
int scroll_save = msg_scroll;
//
@@ -1153,9 +1157,9 @@ void do_bang(int addr_count, exarg_T *eap, bool forceit, bool do_in, bool do_out
bool ins_prevcmd = forceit;
trailarg = arg;
do {
- len = (int)STRLEN(trailarg) + 1;
+ len = STRLEN(trailarg) + 1;
if (newcmd != NULL) {
- len += (int)STRLEN(newcmd);
+ len += STRLEN(newcmd);
}
if (ins_prevcmd) {
if (prevcmd == NULL) {
@@ -1163,7 +1167,7 @@ void do_bang(int addr_count, exarg_T *eap, bool forceit, bool do_in, bool do_out
xfree(newcmd);
return;
}
- len += (int)STRLEN(prevcmd);
+ len += STRLEN(prevcmd);
}
t = xmalloc(len);
*t = NUL;
@@ -1205,7 +1209,7 @@ void do_bang(int addr_count, exarg_T *eap, bool forceit, bool do_in, bool do_out
// If % or # appears in the command, it must have been escaped.
// Reescape them, so that redoing them does not substitute them by the
// buffername.
- char_u *cmd = vim_strsave_escaped(prevcmd, (char_u *)"%#");
+ char *cmd = (char *)vim_strsave_escaped((char_u *)prevcmd, (char_u *)"%#");
AppendToRedobuffLit(cmd, -1);
xfree(cmd);
@@ -1258,23 +1262,29 @@ void do_bang(int addr_count, exarg_T *eap, bool forceit, bool do_in, bool do_out
/// We use output redirection if do_out is true.
///
/// @param eap for forced 'ff' and 'fenc'
-static void do_filter(linenr_T line1, linenr_T line2, exarg_T *eap, char_u *cmd, bool do_in,
+static void do_filter(linenr_T line1, linenr_T line2, exarg_T *eap, char *cmd, bool do_in,
bool do_out)
{
- char_u *itmp = NULL;
- char_u *otmp = NULL;
+ char *itmp = NULL;
+ char *otmp = NULL;
linenr_T linecount;
linenr_T read_linecount;
pos_T cursor_save;
- char_u *cmd_buf;
+ char *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 int save_cmod_flags = cmdmod.cmod_flags;
+ // Temporarily disable lockmarks since that's needed to propagate changed
+ // regions of the buffer for foldUpdate(), linecount, etc.
+ cmdmod.cmod_flags &= ~CMOD_LOCKMARKS;
cursor_save = curwin->w_cursor;
linecount = line2 - line1 + 1;
@@ -1316,8 +1326,8 @@ static void do_filter(linenr_T line1, linenr_T line2, exarg_T *eap, char_u *cmd,
curbuf->b_op_start.lnum = line1;
curbuf->b_op_end.lnum = line2;
curwin->w_cursor.lnum = line2;
- } else if ((do_in && (itmp = vim_tempname()) == NULL)
- || (do_out && (otmp = vim_tempname()) == NULL)) {
+ } else if ((do_in && (itmp = (char *)vim_tempname()) == NULL)
+ || (do_out && (otmp = (char *)vim_tempname()) == NULL)) {
emsg(_(e_notmp));
goto filterend;
}
@@ -1326,7 +1336,7 @@ static void do_filter(linenr_T line1, linenr_T line2, exarg_T *eap, char_u *cmd,
* The writing and reading of temp files will not be shown.
* Vi also doesn't do this and the messages are not very informative.
*/
- ++no_wait_return; // don't call wait_return() while busy
+ no_wait_return++; // don't call wait_return() while busy
if (itmp != NULL && buf_write(curbuf, itmp, NULL, line1, line2, eap,
false, false, false, true) == FAIL) {
msg_putchar('\n'); // Keep message from buf_write().
@@ -1349,7 +1359,7 @@ static void do_filter(linenr_T line1, linenr_T line2, exarg_T *eap, char_u *cmd,
ui_cursor_goto(Rows - 1, 0);
if (do_out) {
- if (u_save((line2), (linenr_T)(line2 + 1)) == FAIL) {
+ if (u_save(line2, (linenr_T)(line2 + 1)) == FAIL) {
xfree(cmd_buf);
goto error;
}
@@ -1358,7 +1368,7 @@ static void do_filter(linenr_T line1, linenr_T line2, exarg_T *eap, char_u *cmd,
read_linecount = curbuf->b_ml.ml_line_count;
// Pass on the kShellOptDoOut flag when the output is being redirected.
- call_shell(cmd_buf, kShellOptFilter | shell_flags, NULL);
+ call_shell((char_u *)cmd_buf, (ShellOpts)(kShellOptFilter | shell_flags), NULL);
xfree(cmd_buf);
did_check_timestamps = FALSE;
@@ -1373,7 +1383,7 @@ static void do_filter(linenr_T line1, linenr_T line2, exarg_T *eap, char_u *cmd,
if (do_out) {
if (otmp != NULL) {
if (readfile(otmp, NULL, line2, (linenr_T)0, (linenr_T)MAXLNUM, eap,
- READ_FILTER) != OK) {
+ READ_FILTER, false) != OK) {
if (!aborting()) {
msg_putchar('\n');
semsg(_(e_notread), otmp);
@@ -1394,7 +1404,8 @@ static void do_filter(linenr_T line1, linenr_T line2, exarg_T *eap, char_u *cmd,
}
if (do_in) {
- if (cmdmod.keepmarks || vim_strchr(p_cpo, CPO_REMMARK) == NULL) {
+ if ((cmdmod.cmod_flags & CMOD_KEEPMARKS)
+ || vim_strchr(p_cpo, CPO_REMMARK) == NULL) {
// TODO(bfredl): Currently not active for extmarks. What would we
// do if columns don't match, assume added/deleted bytes at the
// end of each line?
@@ -1455,15 +1466,20 @@ error:
filterend:
+ cmdmod.cmod_flags = save_cmod_flags;
if (curbuf != old_curbuf) {
no_wait_return--;
emsg(_("E135: *Filter* Autocommands must not change current buffer"));
+ } else if (cmdmod.cmod_flags & CMOD_LOCKMARKS) {
+ curbuf->b_op_start = orig_start;
+ curbuf->b_op_end = orig_end;
}
+
if (itmp != NULL) {
- os_remove((char *)itmp);
+ os_remove(itmp);
}
if (otmp != NULL) {
- os_remove((char *)otmp);
+ os_remove(otmp);
}
xfree(itmp);
xfree(otmp);
@@ -1473,7 +1489,7 @@ filterend:
/// When "cmd" is NULL start an interactive shell.
///
/// @param flags may be SHELL_DOOUT when output is redirected
-void do_shell(char_u *cmd, int flags)
+void do_shell(char *cmd, int flags)
{
// Disallow shell commands from .exrc and .vimrc in current directory for
// security reasons.
@@ -1482,7 +1498,6 @@ void do_shell(char_u *cmd, int flags)
return;
}
-
/*
* For autocommands we want to get the output on the current screen, to
* avoid having to type return below.
@@ -1505,7 +1520,7 @@ void do_shell(char_u *cmd, int flags)
// This ui_cursor_goto is required for when the '\n' resulted in a "delete line
// 1" command to the terminal.
ui_cursor_goto(msg_row, msg_col);
- (void)call_shell(cmd, flags, NULL);
+ (void)call_shell((char_u *)cmd, (ShellOpts)flags, NULL);
msg_didout = true;
did_check_timestamps = false;
need_check_timestamps = true;
@@ -1544,7 +1559,7 @@ static char *find_pipe(const char *cmd)
/// @param itmp NULL or the input file.
/// @param otmp NULL or the output file.
/// @returns an allocated string with the shell command.
-char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp)
+char *make_filter_cmd(char *cmd, char *itmp, char *otmp)
{
bool is_fish_shell =
#if defined(UNIX)
@@ -1552,14 +1567,18 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp)
#else
false;
#endif
+ bool is_pwsh = STRNCMP(invocation_path_tail(p_sh, NULL), "pwsh", 4) == 0
+ || STRNCMP(invocation_path_tail(p_sh, NULL), "powershell", 10) == 0;
size_t len = STRLEN(cmd) + 1; // At least enough space for cmd + NULL.
len += is_fish_shell ? sizeof("begin; " "; end") - 1
- : sizeof("(" ")") - 1;
+ : is_pwsh ? sizeof("Start-Process ")
+ : sizeof("(" ")") - 1;
if (itmp != NULL) {
- len += STRLEN(itmp) + sizeof(" { " " < " " } ") - 1;
+ len += is_pwsh ? STRLEN(itmp) + sizeof(" -RedirectStandardInput ")
+ : STRLEN(itmp) + sizeof(" { " " < " " } ") - 1;
}
if (otmp != NULL) {
len += STRLEN(otmp) + STRLEN(p_srr) + 2; // two extra spaces (" "),
@@ -1569,22 +1588,34 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp)
#if defined(UNIX)
// Put delimiters around the command (for concatenated commands) when
// redirecting input and/or output.
- if (itmp != NULL || otmp != NULL) {
+ if (is_pwsh) {
+ xstrlcpy(buf, "Start-Process ", len);
+ xstrlcat(buf, cmd, len);
+ } else if (itmp != NULL || otmp != NULL) {
char *fmt = is_fish_shell ? "begin; %s; end"
: "(%s)";
- vim_snprintf(buf, len, fmt, (char *)cmd);
+ vim_snprintf(buf, len, fmt, cmd);
} else {
- xstrlcpy(buf, (char *)cmd, len);
+ xstrlcpy(buf, cmd, len);
}
if (itmp != NULL) {
- xstrlcat(buf, " < ", len - 1);
- xstrlcat(buf, (const char *)itmp, len - 1);
+ if (is_pwsh) {
+ xstrlcat(buf, " -RedirectStandardInput ", len - 1);
+ } else {
+ xstrlcat(buf, " < ", len - 1);
+ }
+ xstrlcat(buf, itmp, len - 1);
}
#else
// For shells that don't understand braces around commands, at least allow
// the use of commands in a pipe.
- xstrlcpy(buf, (char *)cmd, len);
+ if (is_pwsh) {
+ xstrlcpy(buf, "Start-Process ", len);
+ xstrlcat(buf, cmd, len);
+ } else {
+ xstrlcpy(buf, cmd, len);
+ }
if (itmp != NULL) {
// If there is a pipe, we have to put the '<' in front of it.
// Don't do this when 'shellquote' is not empty, otherwise the
@@ -1595,10 +1626,14 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp)
*p = NUL;
}
}
- xstrlcat(buf, " < ", len);
- xstrlcat(buf, (const char *)itmp, len);
+ if (is_pwsh) {
+ xstrlcat(buf, " -RedirectStandardInput ", len);
+ } else {
+ xstrlcat(buf, " < ", len);
+ }
+ xstrlcat(buf, itmp, len);
if (*p_shq == NUL) {
- const char *const p = find_pipe((const char *)cmd);
+ const char *const p = find_pipe(cmd);
if (p != NULL) {
xstrlcat(buf, " ", len - 1); // Insert a space before the '|' for DOS
xstrlcat(buf, p, len - 1);
@@ -1607,9 +1642,9 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp)
}
#endif
if (otmp != NULL) {
- append_redir(buf, len, (char *)p_srr, (char *)otmp);
+ append_redir(buf, len, (char *)p_srr, otmp);
}
- return (char_u *)buf;
+ return buf;
}
/// Append output redirection for the given file to the end of the buffer
@@ -1636,9 +1671,9 @@ void append_redir(char *const buf, const size_t buflen, const char *const opt,
}
if (p != NULL) {
*end = ' '; // not really needed? Not with sh, ksh or bash
- vim_snprintf(end + 1, (size_t)(buflen - (end + 1 - buf)), opt, fname);
+ vim_snprintf(end + 1, (size_t)((ptrdiff_t)buflen - (end + 1 - buf)), opt, fname);
} else {
- vim_snprintf(end, (size_t)(buflen - (end - buf)), " %s %s", opt, fname);
+ vim_snprintf(end, (size_t)((ptrdiff_t)buflen - (end - buf)), " %s %s", opt, fname);
}
}
@@ -1654,9 +1689,7 @@ void print_line_no_prefix(linenr_T lnum, int use_number, int list)
msg_prt_line(ml_get(lnum), list);
}
-/*
- * Print a text line. Also in silent mode ("ex -s").
- */
+/// Print a text line. Also in silent mode ("ex -s").
void print_line(linenr_T lnum, int use_number, int list)
{
int save_silent = silent_mode;
@@ -1678,9 +1711,9 @@ void print_line(linenr_T lnum, int use_number, int list)
info_message = false;
}
-int rename_buffer(char_u *new_fname)
+int rename_buffer(char *new_fname)
{
- char_u *fname, *sfname, *xfname;
+ char *fname, *sfname, *xfname;
buf_T *buf;
buf = curbuf;
@@ -1712,7 +1745,7 @@ int rename_buffer(char_u *new_fname)
curbuf->b_flags |= BF_NOTEDITED;
if (xfname != NULL && *xfname != NUL) {
buf = buflist_new(fname, xfname, curwin->w_cursor.lnum, 0);
- if (buf != NULL && !cmdmod.keepalt) {
+ if (buf != NULL && (cmdmod.cmod_flags & CMOD_KEEPALT) == 0) {
curwin->w_alt_fnum = buf->b_fnum;
}
}
@@ -1724,9 +1757,7 @@ int rename_buffer(char_u *new_fname)
return OK;
}
-/*
- * ":file[!] [fname]".
- */
+/// ":file[!] [fname]".
void ex_file(exarg_T *eap)
{
// ":0file" removes the file name. Check for illegal uses ":3file",
@@ -1752,9 +1783,7 @@ void ex_file(exarg_T *eap)
}
}
-/*
- * ":update".
- */
+/// ":update".
void ex_update(exarg_T *eap)
{
if (curbufIsChanged()) {
@@ -1762,9 +1791,7 @@ void ex_update(exarg_T *eap)
}
}
-/*
- * ":write" and ":saveas".
- */
+/// ":write" and ":saveas".
void ex_write(exarg_T *eap)
{
if (eap->cmdidx == CMD_saveas) {
@@ -1780,21 +1807,19 @@ void ex_write(exarg_T *eap)
}
}
-/*
- * write current buffer to file 'eap->arg'
- * if 'eap->append' is TRUE, append to the file
- *
- * if *eap->arg == NUL write to current file
- *
- * return FAIL for failure, OK otherwise
- */
+/// write current buffer to file 'eap->arg'
+/// if 'eap->append' is TRUE, append to the file
+///
+/// if *eap->arg == NUL write to current file
+///
+/// @return FAIL for failure, OK otherwise
int do_write(exarg_T *eap)
{
int other;
- char_u *fname = NULL; // init to shut up gcc
- char_u *ffname;
+ char *fname = NULL; // init to shut up gcc
+ char *ffname;
int retval = FAIL;
- char_u *free_fname = NULL;
+ char *free_fname = NULL;
buf_T *alt_buf = NULL;
int name_was_missing;
@@ -1811,11 +1836,9 @@ int do_write(exarg_T *eap)
other = FALSE;
} else {
fname = ffname;
- free_fname = (char_u *)fix_fname((char *)ffname);
- /*
- * When out-of-memory, keep unexpanded file name, because we MUST be
- * able to write the file in this situation.
- */
+ free_fname = fix_fname(ffname);
+ // When out-of-memory, keep unexpanded file name, because we MUST be
+ // able to write the file in this situation.
if (free_fname != NULL) {
ffname = free_fname;
}
@@ -1852,15 +1875,13 @@ int do_write(exarg_T *eap)
if (!other) {
ffname = curbuf->b_ffname;
fname = curbuf->b_fname;
- /*
- * Not writing the whole file is only allowed with '!'.
- */
+ // Not writing the whole file is only allowed with '!'.
if ((eap->line1 != 1
|| eap->line2 != curbuf->b_ml.ml_line_count)
&& !eap->forceit
&& !eap->append
&& !p_wa) {
- if (p_confirm || cmdmod.confirm) {
+ if (p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) {
if (vim_dialog_yesno(VIM_QUESTION, NULL,
(char_u *)_("Write partial file?"), 2) != VIM_YES) {
goto theend;
@@ -1913,8 +1934,8 @@ int do_write(exarg_T *eap)
// If 'filetype' was empty try detecting it now.
if (*curbuf->b_p_ft == NUL) {
- if (au_has_group((char_u *)"filetypedetect")) {
- (void)do_doautocmd((char_u *)"filetypedetect BufRead", true, NULL);
+ if (augroup_exists("filetypedetect")) {
+ (void)do_doautocmd("filetypedetect BufRead", true, NULL);
}
do_modelines(0);
}
@@ -1926,7 +1947,7 @@ int do_write(exarg_T *eap)
name_was_missing = curbuf->b_ffname == NULL;
retval = buf_write(curbuf, ffname, fname, eap->line1, eap->line2,
- eap, eap->append, eap->forceit, TRUE, FALSE);
+ eap, eap->append, eap->forceit, true, false);
// After ":saveas fname" reset 'readonly'.
if (eap->cmdidx == CMD_saveas) {
@@ -1957,33 +1978,30 @@ theend:
/// @param other writing under other name
///
/// @return OK if it's OK, FAIL if it is not.
-int check_overwrite(exarg_T *eap, buf_T *buf, char_u *fname, char_u *ffname, int other)
+int check_overwrite(exarg_T *eap, buf_T *buf, char *fname, char *ffname, int other)
{
- /*
- * write to other file or b_flags set or not writing the whole file:
- * overwriting only allowed with '!'
- */
+ // Write to another file or b_flags set or not writing the whole file:
+ // overwriting only allowed with '!'
if ((other
|| (buf->b_flags & BF_NOTEDITED)
|| ((buf->b_flags & BF_NEW)
&& vim_strchr(p_cpo, CPO_OVERNEW) == NULL)
|| (buf->b_flags & BF_READERR))
&& !p_wa
- && !bt_nofile(buf)
- && os_path_exists(ffname)) {
+ && os_path_exists((char_u *)ffname)) {
if (!eap->forceit && !eap->append) {
#ifdef UNIX
// It is possible to open a directory on Unix.
- if (os_isdir(ffname)) {
+ if (os_isdir((char_u *)ffname)) {
semsg(_(e_isadir2), ffname);
return FAIL;
}
#endif
- if (p_confirm || cmdmod.confirm) {
- char_u buff[DIALOG_MSG_SIZE];
+ if (p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) {
+ char buff[DIALOG_MSG_SIZE];
- dialog_msg(buff, _("Overwrite existing file \"%s\"?"), fname);
- if (vim_dialog_yesno(VIM_QUESTION, NULL, buff, 2) != VIM_YES) {
+ dialog_msg((char *)buff, _("Overwrite existing file \"%s\"?"), fname);
+ if (vim_dialog_yesno(VIM_QUESTION, NULL, (char_u *)buff, 2) != VIM_YES) {
return FAIL;
}
eap->forceit = TRUE;
@@ -1995,9 +2013,9 @@ int check_overwrite(exarg_T *eap, buf_T *buf, char_u *fname, char_u *ffname, int
// For ":w! filename" check that no swap file exists for "filename".
if (other && !emsg_silent) {
- char_u *dir;
- char_u *p;
- char_u *swapname;
+ char *dir;
+ char *p;
+ char *swapname;
// We only try the first entry in 'directory', without checking if
// it's writable. If the "." directory is not writable the write
@@ -2009,19 +2027,19 @@ int check_overwrite(exarg_T *eap, buf_T *buf, char_u *fname, char_u *ffname, int
STRCPY(dir, ".");
} else {
dir = xmalloc(MAXPATHL);
- p = p_dir;
+ p = (char *)p_dir;
copy_option_part(&p, dir, MAXPATHL, ",");
}
- swapname = makeswapname(fname, ffname, curbuf, dir);
+ swapname = (char *)makeswapname((char_u *)fname, (char_u *)ffname, curbuf, (char_u *)dir);
xfree(dir);
- if (os_path_exists(swapname)) {
- if (p_confirm || cmdmod.confirm) {
- char_u buff[DIALOG_MSG_SIZE];
+ if (os_path_exists((char_u *)swapname)) {
+ if (p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) {
+ char buff[DIALOG_MSG_SIZE];
- dialog_msg(buff,
+ dialog_msg((char *)buff,
_("Swap file \"%s\" exists, overwrite anyway?"),
swapname);
- if (vim_dialog_yesno(VIM_QUESTION, NULL, buff, 2)
+ if (vim_dialog_yesno(VIM_QUESTION, NULL, (char_u *)buff, 2)
!= VIM_YES) {
xfree(swapname);
return FAIL;
@@ -2040,9 +2058,7 @@ int check_overwrite(exarg_T *eap, buf_T *buf, char_u *fname, char_u *ffname, int
return OK;
}
-/*
- * Handle ":wnext", ":wNext" and ":wprevious" commands.
- */
+/// Handle ":wnext", ":wNext" and ":wprevious" commands.
void ex_wnext(exarg_T *eap)
{
int i;
@@ -2059,9 +2075,7 @@ void ex_wnext(exarg_T *eap)
}
}
-/*
- * ":wall", ":wqall" and ":xall": Write all changed files (and exit).
- */
+/// ":wall", ":wqall" and ":xall": Write all changed files (and exit).
void do_wqall(exarg_T *eap)
{
int error = 0;
@@ -2095,9 +2109,8 @@ void do_wqall(exarg_T *eap)
semsg(_("E141: No file name for buffer %" PRId64), (int64_t)buf->b_fnum);
error++;
} else if (check_readonly(&eap->forceit, buf)
- || check_overwrite(eap, buf, buf->b_fname, buf->b_ffname,
- FALSE) == FAIL) {
- ++error;
+ || check_overwrite(eap, buf, buf->b_fname, buf->b_ffname, false) == FAIL) {
+ error++;
} else {
bufref_T bufref;
set_bufref(&bufref, buf);
@@ -2119,8 +2132,9 @@ void do_wqall(exarg_T *eap)
}
}
-// Check the 'write' option.
-// Return true and give a message when it's not st.
+/// Check the 'write' option.
+///
+/// @return true and give a message when it's not st.
bool not_writing(void)
{
if (p_write) {
@@ -2130,33 +2144,31 @@ bool not_writing(void)
return true;
}
-/*
- * Check if a buffer is read-only (either 'readonly' option is set or file is
- * read-only). Ask for overruling in a dialog. Return TRUE and give an error
- * message when the buffer is readonly.
- */
+/// Check if a buffer is read-only (either 'readonly' option is set or file is
+/// read-only). Ask for overruling in a dialog. Return TRUE and give an error
+/// message when the buffer is readonly.
static int check_readonly(int *forceit, buf_T *buf)
{
// Handle a file being readonly when the 'readonly' option is set or when
// the file exists and permissions are read-only.
if (!*forceit && (buf->b_p_ro
- || (os_path_exists(buf->b_ffname)
- && !os_file_is_writable((char *)buf->b_ffname)))) {
- if ((p_confirm || cmdmod.confirm) && buf->b_fname != NULL) {
- char_u buff[DIALOG_MSG_SIZE];
+ || (os_path_exists((char_u *)buf->b_ffname)
+ && !os_file_is_writable(buf->b_ffname)))) {
+ if ((p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) && buf->b_fname != NULL) {
+ char buff[DIALOG_MSG_SIZE];
if (buf->b_p_ro) {
- dialog_msg(buff,
+ dialog_msg((char *)buff,
_("'readonly' option is set for \"%s\".\nDo you wish to write anyway?"),
buf->b_fname);
} else {
- dialog_msg(buff,
- _(
- "File permissions of \"%s\" are read-only.\nIt may still be possible to write it.\nDo you wish to try?"),
+ dialog_msg((char *)buff,
+ _("File permissions of \"%s\" are read-only.\nIt may still be possible to "
+ "write it.\nDo you wish to try?"),
buf->b_fname);
}
- if (vim_dialog_yesno(VIM_QUESTION, NULL, buff, 2) == VIM_YES) {
+ if (vim_dialog_yesno(VIM_QUESTION, NULL, (char_u *)buff, 2) == VIM_YES) {
// Set forceit, to force the writing of a readonly file
*forceit = TRUE;
return FALSE;
@@ -2175,22 +2187,23 @@ static int check_readonly(int *forceit, buf_T *buf)
return FALSE;
}
-// Try to abandon the current file and edit a new or existing file.
-// "fnum" is the number of the file, if zero use "ffname_arg"/"sfname_arg".
-// "lnum" is the line number for the cursor in the new file (if non-zero).
-//
-// Return:
-// GETFILE_ERROR for "normal" error,
-// GETFILE_NOT_WRITTEN for "not written" error,
-// GETFILE_SAME_FILE for success
-// GETFILE_OPEN_OTHER for successfully opening another file.
-int getfile(int fnum, char_u *ffname_arg, char_u *sfname_arg, int setpm, linenr_T lnum, int forceit)
+/// Try to abandon the current file and edit a new or existing file.
+///
+/// @param fnum the number of the file, if zero use "ffname_arg"/"sfname_arg".
+/// @param lnum the line number for the cursor in the new file (if non-zero).
+///
+/// @return:
+/// GETFILE_ERROR for "normal" error,
+/// GETFILE_NOT_WRITTEN for "not written" error,
+/// GETFILE_SAME_FILE for success
+/// GETFILE_OPEN_OTHER for successfully opening another file.
+int getfile(int fnum, char *ffname_arg, char *sfname_arg, int setpm, linenr_T lnum, int forceit)
{
- char_u *ffname = ffname_arg;
- char_u *sfname = sfname_arg;
+ char *ffname = ffname_arg;
+ char *sfname = sfname_arg;
int other;
int retval;
- char_u *free_me = NULL;
+ char *free_me = NULL;
if (text_locked()) {
return GETFILE_ERROR;
@@ -2279,19 +2292,19 @@ theend:
/// info of the previous buffer for "oldwin" is stored.
///
/// @return FAIL for failure, OK otherwise
-int do_ecmd(int fnum, char_u *ffname, char_u *sfname, exarg_T *eap, linenr_T newlnum, int flags,
+int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum, int flags,
win_T *oldwin)
{
bool other_file; // true if editing another file
int oldbuf; // TRUE if using existing buffer
bool auto_buf = false; // true if autocommands brought us
// into the buffer unexpectedly
- char_u *new_name = NULL;
+ char *new_name = NULL;
bool did_set_swapcommand = false;
buf_T *buf;
bufref_T bufref;
bufref_T old_curbuf;
- char_u *free_fname = NULL;
+ char *free_fname = NULL;
int retval = FAIL;
long n;
pos_T orig_pos;
@@ -2299,7 +2312,7 @@ int do_ecmd(int fnum, char_u *ffname, char_u *sfname, exarg_T *eap, linenr_T new
int newcol = -1;
int solcol = -1;
pos_T *pos;
- char_u *command = NULL;
+ char *command = NULL;
bool did_get_winopts = false;
int readfile_flags = 0;
bool did_inc_redrawing_disabled = false;
@@ -2341,7 +2354,7 @@ int do_ecmd(int fnum, char_u *ffname, char_u *sfname, exarg_T *eap, linenr_T new
ffname = curbuf->b_ffname;
sfname = curbuf->b_fname;
}
- free_fname = (char_u *)fix_fname((char *)ffname); // may expand to full path name
+ free_fname = fix_fname(ffname); // may expand to full path name
if (free_fname != NULL) {
ffname = free_fname;
}
@@ -2399,8 +2412,10 @@ int do_ecmd(int fnum, char_u *ffname, char_u *sfname, exarg_T *eap, linenr_T new
* Otherwise we re-use the current buffer.
*/
if (other_file) {
+ const int prev_alt_fnum = curwin->w_alt_fnum;
+
if (!(flags & (ECMD_ADDBUF | ECMD_ALTBUF))) {
- if (!cmdmod.keepalt) {
+ if ((cmdmod.cmod_flags & CMOD_KEEPALT) == 0) {
curwin->w_alt_fnum = curbuf->b_fnum;
}
if (oldwin != NULL) {
@@ -2417,7 +2432,7 @@ int do_ecmd(int fnum, char_u *ffname, char_u *sfname, exarg_T *eap, linenr_T new
linenr_T tlnum = 0;
if (command != NULL) {
- tlnum = atol((char *)command);
+ tlnum = (linenr_T)atol(command);
if (tlnum <= 0) {
tlnum = 1L;
}
@@ -2442,6 +2457,10 @@ int do_ecmd(int fnum, char_u *ffname, char_u *sfname, exarg_T *eap, linenr_T new
if (buf == NULL) {
goto theend;
}
+ if (curwin->w_alt_fnum == buf->b_fnum && prev_alt_fnum != 0) {
+ // reusing the buffer, keep the old alternate file
+ curwin->w_alt_fnum = prev_alt_fnum;
+ }
if (buf->b_ml.ml_mfp == NULL) {
// No memfile yet.
oldbuf = false;
@@ -2464,7 +2483,7 @@ int do_ecmd(int fnum, char_u *ffname, char_u *sfname, exarg_T *eap, linenr_T new
// May jump to last used line number for a loaded buffer or when asked
// for explicitly
if ((oldbuf && newlnum == ECMD_LASTL) || newlnum == ECMD_LAST) {
- pos = buflist_findfpos(buf);
+ pos = &buflist_findfmark(buf)->mark;
newlnum = pos->lnum;
solcol = pos->col;
}
@@ -2490,18 +2509,21 @@ int do_ecmd(int fnum, char_u *ffname, char_u *sfname, exarg_T *eap, linenr_T new
// - If we ended up in the new buffer already, need to skip a few
// things, set auto_buf.
if (buf->b_fname != NULL) {
- new_name = vim_strsave(buf->b_fname);
+ new_name = xstrdup(buf->b_fname);
}
+ const bufref_T save_au_new_curbuf = au_new_curbuf;
set_bufref(&au_new_curbuf, buf);
apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, false, curbuf);
cmdwin_type = save_cmdwin_type;
if (!bufref_valid(&au_new_curbuf)) {
// New buffer has been deleted.
delbuf_msg(new_name); // Frees new_name.
+ au_new_curbuf = save_au_new_curbuf;
goto theend;
}
if (aborting()) { // autocmds may abort script processing
xfree(new_name);
+ au_new_curbuf = save_au_new_curbuf;
goto theend;
}
if (buf == curbuf) { // already in new buffer
@@ -2522,9 +2544,9 @@ int do_ecmd(int fnum, char_u *ffname, char_u *sfname, exarg_T *eap, linenr_T new
// Close the link to the current buffer. This will set
// oldwin->w_buffer to NULL.
u_sync(false);
- const bool did_decrement = close_buffer(oldwin, curbuf,
- (flags & ECMD_HIDE) || curbuf->terminal ? 0 : DOBUF_UNLOAD,
- false);
+ const bool did_decrement
+ = close_buffer(oldwin, curbuf, (flags & ECMD_HIDE) || curbuf->terminal ? 0 : DOBUF_UNLOAD,
+ false, false);
// Autocommands may have closed the window.
if (win_valid(the_curwin)) {
@@ -2535,12 +2557,14 @@ int do_ecmd(int fnum, char_u *ffname, char_u *sfname, exarg_T *eap, linenr_T new
// autocmds may abort script processing
if (aborting() && curwin->w_buffer != NULL) {
xfree(new_name);
+ au_new_curbuf = save_au_new_curbuf;
goto theend;
}
// Be careful again, like above.
if (!bufref_valid(&au_new_curbuf)) {
// New buffer has been deleted.
delbuf_msg(new_name); // Frees new_name.
+ au_new_curbuf = save_au_new_curbuf;
goto theend;
}
if (buf == curbuf) { // already in new buffer
@@ -2580,8 +2604,7 @@ int do_ecmd(int fnum, char_u *ffname, char_u *sfname, exarg_T *eap, linenr_T new
did_get_winopts = true;
}
xfree(new_name);
- au_new_curbuf.br_buf = NULL;
- au_new_curbuf.br_buf_free_count = 0;
+ au_new_curbuf = save_au_new_curbuf;
}
curwin->w_pcmark.lnum = 1;
@@ -2636,7 +2659,7 @@ int do_ecmd(int fnum, char_u *ffname, char_u *sfname, exarg_T *eap, linenr_T new
}
buf = curbuf;
if (buf->b_fname != NULL) {
- new_name = vim_strsave(buf->b_fname);
+ new_name = (char *)vim_strsave((char_u *)buf->b_fname);
} else {
new_name = NULL;
}
@@ -2760,7 +2783,7 @@ int do_ecmd(int fnum, char_u *ffname, char_u *sfname, exarg_T *eap, linenr_T new
// keep it. Also when it moves within a line. But not when it moves
// to the first non-blank.
if (!equalpos(curwin->w_cursor, orig_pos)) {
- const char_u *text = get_cursor_line_ptr();
+ const char *text = (char *)get_cursor_line_ptr();
if (curwin->w_cursor.lnum != orig_pos.lnum
|| curwin->w_cursor.col != (int)(skipwhite(text) - text)) {
@@ -2870,14 +2893,9 @@ int do_ecmd(int fnum, char_u *ffname, char_u *sfname, exarg_T *eap, linenr_T new
redraw_curbuf_later(NOT_VALID); // redraw this buffer later
}
- if (p_im) {
- need_start_insertmode = true;
- }
-
// Change directories when the 'acd' option is set.
do_autochdir();
-
theend:
if (bufref_valid(&old_curbuf) && old_curbuf.br_buf->terminal != NULL) {
terminal_check_size(old_curbuf.br_buf->terminal);
@@ -2893,10 +2911,10 @@ theend:
return retval;
}
-static void delbuf_msg(char_u *name)
+static void delbuf_msg(char *name)
{
semsg(_("E143: Autocommands unexpectedly deleted new buffer %s"),
- name == NULL ? (char_u *)"" : name);
+ name == NULL ? "" : name);
xfree(name);
au_new_curbuf.br_buf = NULL;
au_new_curbuf.br_buf_free_count = 0;
@@ -2904,16 +2922,14 @@ static void delbuf_msg(char_u *name)
static int append_indent = 0; // autoindent for first line
-/*
- * ":insert" and ":append", also used by ":change"
- */
+/// ":insert" and ":append", also used by ":change"
void ex_append(exarg_T *eap)
{
- char_u *theline;
+ char *theline;
bool did_undo = false;
linenr_T lnum = eap->line2;
int indent = 0;
- char_u *p;
+ char *p;
int vcol;
int empty = (curbuf->b_ml.ml_flags & ML_EMPTY);
@@ -2936,9 +2952,9 @@ void ex_append(exarg_T *eap)
lnum = 0;
}
- State = INSERT; // behave like in Insert mode
+ State = MODE_INSERT; // behave like in Insert mode
if (curbuf->b_p_iminsert == B_IMODE_LMAP) {
- State |= LANGMAP;
+ State |= MODE_LANGMAP;
}
for (;;) {
@@ -2962,18 +2978,17 @@ void ex_append(exarg_T *eap)
if (p == NULL) {
p = eap->nextcmd + STRLEN(eap->nextcmd);
}
- theline = vim_strnsave(eap->nextcmd, p - eap->nextcmd);
+ theline = xstrnsave(eap->nextcmd, (size_t)(p - eap->nextcmd));
if (*p != NUL) {
p++;
}
eap->nextcmd = p;
} else {
- // Set State to avoid the cursor shape to be set to INSERT mode
- // when getline() returns.
int save_State = State;
- State = CMDLINE;
- theline = eap->getline(eap->cstack->cs_looplevel > 0 ? -1 :
- NUL, eap->cookie, indent, true);
+ // Set State to avoid the cursor shape to be set to MODE_INSERT
+ // state when getline() returns.
+ State = MODE_CMDLINE;
+ theline = eap->getline(eap->cstack->cs_looplevel > 0 ? -1 : NUL, eap->cookie, indent, true);
State = save_State;
}
lines_left = Rows - 1;
@@ -3021,7 +3036,7 @@ void ex_append(exarg_T *eap)
empty = 0;
}
}
- State = NORMAL;
+ State = MODE_NORMAL;
if (eap->forceit) {
curbuf->b_p_ai = !curbuf->b_p_ai;
@@ -3031,14 +3046,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.cmod_flags & CMOD_LOCKMARKS) == 0) {
+ 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);
@@ -3047,9 +3063,7 @@ void ex_append(exarg_T *eap)
ex_no_reprint = true;
}
-/*
- * ":change"
- */
+/// ":change"
void ex_change(exarg_T *eap)
{
linenr_T lnum;
@@ -3082,9 +3096,9 @@ void ex_change(exarg_T *eap)
void ex_z(exarg_T *eap)
{
- char_u *x;
+ char *x;
int64_t bigness;
- char_u *kind;
+ char *kind;
int minus = 0;
linenr_T start, end, curs, i;
int j;
@@ -3118,7 +3132,7 @@ void ex_z(exarg_T *eap)
emsg(_("E144: non-numeric argument to :z"));
return;
}
- bigness = atol((char *)x);
+ bigness = atol(x);
// bigness could be < 0 if atol(x) overflows.
if (bigness > 2 * curbuf->b_ml.ml_line_count || bigness < 0) {
@@ -3133,44 +3147,43 @@ void ex_z(exarg_T *eap)
// the number of '-' and '+' multiplies the distance
if (*kind == '-' || *kind == '+') {
- for (x = kind + 1; *x == *kind; x++) {
- }
+ for (x = kind + 1; *x == *kind; x++) {}
}
switch (*kind) {
case '-':
- start = lnum - bigness * (linenr_T)(x - kind) + 1;
- end = start + bigness - 1;
+ start = lnum - (linenr_T)bigness * (linenr_T)(x - kind) + 1;
+ end = start + (linenr_T)bigness - 1;
curs = end;
break;
case '=':
- start = lnum - (bigness + 1) / 2 + 1;
- end = lnum + (bigness + 1) / 2 - 1;
+ start = lnum - ((linenr_T)bigness + 1) / 2 + 1;
+ end = lnum + ((linenr_T)bigness + 1) / 2 - 1;
curs = lnum;
minus = 1;
break;
case '^':
- start = lnum - bigness * 2;
- end = lnum - bigness;
- curs = lnum - bigness;
+ start = lnum - (linenr_T)bigness * 2;
+ end = lnum - (linenr_T)bigness;
+ curs = lnum - (linenr_T)bigness;
break;
case '.':
- start = lnum - (bigness + 1) / 2 + 1;
- end = lnum + (bigness + 1) / 2 - 1;
+ start = lnum - ((linenr_T)bigness + 1) / 2 + 1;
+ end = lnum + ((linenr_T)bigness + 1) / 2 - 1;
curs = end;
break;
default: // '+'
start = lnum;
if (*kind == '+') {
- start += bigness * (linenr_T)(x - kind - 1) + 1;
+ start += (linenr_T)bigness * (linenr_T)(x - kind - 1) + 1;
} else if (eap->addr_count == 0) {
++start;
}
- end = start + bigness - 1;
+ end = start + (linenr_T)bigness - 1;
curs = end;
break;
}
@@ -3216,9 +3229,9 @@ void ex_z(exarg_T *eap)
ex_no_reprint = true;
}
-// Check if the secure flag is set (.exrc or .vimrc in current directory).
-// If so, give an error message and return true.
-// Otherwise, return false.
+/// @return true if the secure flag is set (.exrc or .vimrc in current directory)
+/// and also give an error message.
+/// Otherwise, return false.
bool check_secure(void)
{
if (secure) {
@@ -3274,7 +3287,7 @@ void sub_set_replacement(SubReplacementString sub)
/// @param[in] save Save pattern to options, history
///
/// @returns true if :substitute can be replaced with a join command
-static bool sub_joining_lines(exarg_T *eap, char_u *pat, char_u *sub, char_u *cmd, bool save)
+static bool sub_joining_lines(exarg_T *eap, char *pat, char *sub, char *cmd, bool save)
FUNC_ATTR_NONNULL_ARG(1, 3, 4)
{
// TODO(vim): find a generic solution to make line-joining operations more
@@ -3304,7 +3317,7 @@ static bool sub_joining_lines(exarg_T *eap, char_u *pat, char_u *sub, char_u *cm
// plus one extra line if not at the end of file.
+ (eap->line2 < curbuf->b_ml.ml_line_count ? 1 : 0);
if (joined_lines_count > 1) {
- do_join(joined_lines_count, FALSE, TRUE, FALSE, true);
+ do_join((size_t)joined_lines_count, false, true, false, true);
sub_nsubs = joined_lines_count - 1;
sub_nlines = 1;
do_sub_msg(false);
@@ -3312,10 +3325,10 @@ static bool sub_joining_lines(exarg_T *eap, char_u *pat, char_u *sub, char_u *cm
}
if (save) {
- if (!cmdmod.keeppatterns) {
- save_re_pat(RE_SUBST, pat, p_magic);
+ if ((cmdmod.cmod_flags & CMOD_KEEPPATTERNS) == 0) {
+ save_re_pat(RE_SUBST, (char_u *)pat, p_magic);
}
- add_to_history(HIST_SEARCH, pat, true, NUL);
+ add_to_history(HIST_SEARCH, (char_u *)pat, true, NUL);
}
return true;
@@ -3333,17 +3346,17 @@ static bool sub_joining_lines(exarg_T *eap, char_u *pat, char_u *sub, char_u *cm
/// @param[in] needed_len amount of memory needed
///
/// @returns pointer to the end of the allocated memory
-static char_u *sub_grow_buf(char_u **new_start, int needed_len)
+static char *sub_grow_buf(char **new_start, int needed_len)
FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_NONNULL_RET
{
int new_start_len = 0;
- char_u *new_end;
+ char *new_end;
if (*new_start == NULL) {
// Get some space for a temporary buffer to do the
// substitution into (and some extra space to avoid
// too many calls to xmalloc()/free()).
new_start_len = needed_len + 50;
- *new_start = xmalloc(new_start_len);
+ *new_start = xmalloc((size_t)new_start_len);
**new_start = NUL;
new_end = *new_start;
} else {
@@ -3351,10 +3364,10 @@ static char_u *sub_grow_buf(char_u **new_start, int needed_len)
// substitution into. If not, make it larger (with a bit
// extra to avoid too many calls to xmalloc()/free()).
size_t len = STRLEN(*new_start);
- needed_len += len;
+ needed_len += (int)len;
if (needed_len > new_start_len) {
new_start_len = needed_len + 50;
- *new_start = xrealloc(*new_start, new_start_len);
+ *new_start = xrealloc(*new_start, (size_t)new_start_len);
}
new_end = *new_start + len;
}
@@ -3369,7 +3382,7 @@ static char_u *sub_grow_buf(char_u **new_start, int needed_len)
/// @param[in,out] which_pat pattern type from which to get default search
///
/// @returns pointer to the end of the flags, which may be the end of the string
-static char_u *sub_parse_flags(char_u *cmd, subflags_T *subflags, int *which_pat)
+static char *sub_parse_flags(char *cmd, subflags_T *subflags, int *which_pat)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET
{
// Find trailing options. When '&' is used, keep old options.
@@ -3440,8 +3453,8 @@ static int check_regexp_delim(int c)
/// The usual escapes are supported as described in the regexp docs.
///
/// @param do_buf_event If `true`, send buffer updates.
-/// @return buffer used for 'inccommand' preview
-static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle_T bufnr)
+/// @return 0, 1 or 2. See show_cmdpreview() for more information on what the return value means.
+static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T cmdpreview_bufnr)
{
long i = 0;
regmmatch_T regmatch;
@@ -3455,28 +3468,23 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle
.do_number = false,
.do_ic = kSubHonorOptions
};
- char_u *pat = NULL, *sub = NULL; // init for GCC
+ char *pat = NULL, *sub = NULL; // init for GCC
int delimiter;
bool has_second_delim = false;
int sublen;
bool got_quit = false;
bool got_match = false;
int which_pat;
- char_u *cmd = eap->arg;
+ char *cmd = eap->arg;
linenr_T first_line = 0; // first changed line
linenr_T last_line= 0; // below last changed line AFTER the change
linenr_T old_line_count = curbuf->b_ml.ml_line_count;
- char_u *sub_firstline; // allocated copy of first sub line
+ char *sub_firstline; // allocated copy of first sub line
bool endcolumn = false; // cursor in last column when done
PreviewLines preview_lines = { KV_INITIAL_VALUE, 0 };
- static int pre_src_id = 0; // Source id for the preview highlight
static int pre_hl_id = 0;
- buf_T *orig_buf = curbuf; // save to reset highlighting
pos_T old_cursor = curwin->w_cursor;
- int start_nsubs;
- int save_ma = 0;
- int save_b_changed = curbuf->b_changed;
- bool preview = (State & CMDPREVIEW);
+ long start_nsubs;
bool did_save = false;
@@ -3493,32 +3501,32 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle
}
// new pattern and substitution
if (eap->cmd[0] == 's' && *cmd != NUL && !ascii_iswhite(*cmd)
- && vim_strchr((char_u *)"0123456789cegriIp|\"", *cmd) == NULL) {
+ && vim_strchr("0123456789cegriIp|\"", *cmd) == NULL) {
// don't accept alphanumeric for separator
if (check_regexp_delim(*cmd) == FAIL) {
- return NULL;
+ return 0;
}
// undocumented vi feature:
// "\/sub/" and "\?sub?" use last used search pattern (almost like
// //sub/r). "\&sub&" use last substitute pattern (like //sub/).
if (*cmd == '\\') {
- ++cmd;
- if (vim_strchr((char_u *)"/?&", *cmd) == NULL) {
+ cmd++;
+ if (vim_strchr("/?&", *cmd) == NULL) {
emsg(_(e_backslash));
- return NULL;
+ return 0;
}
if (*cmd != '&') {
which_pat = RE_SEARCH; // use last '/' pattern
}
- pat = (char_u *)""; // empty search pattern
- delimiter = *cmd++; // remember delimiter character
+ pat = ""; // empty search pattern
+ delimiter = (char_u)(*cmd++); // remember delimiter character
has_second_delim = true;
} else { // find the end of the regexp
which_pat = RE_LAST; // use last used regexp
- delimiter = *cmd++; // remember delimiter character
+ delimiter = (char_u)(*cmd++); // remember delimiter character
pat = cmd; // remember start of search pat
- cmd = skip_regexp(cmd, delimiter, p_magic, &eap->arg);
+ cmd = (char *)skip_regexp((char_u *)cmd, delimiter, p_magic, (char_u **)&eap->arg);
if (cmd[0] == delimiter) { // end delimiter found
*cmd++ = NUL; // replace it with a NUL
has_second_delim = true;
@@ -3542,9 +3550,9 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle
MB_PTR_ADV(cmd);
}
- if (!eap->skip && !preview) {
+ if (!eap->skip && !cmdpreview) {
sub_set_replacement((SubReplacementString) {
- .sub = xstrdup((char *)sub),
+ .sub = xstrdup(sub),
.timestamp = os_time(),
.additional_elements = NULL,
});
@@ -3552,18 +3560,18 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle
} else if (!eap->skip) { // use previous pattern and substitution
if (old_sub.sub == NULL) { // there is no previous command
emsg(_(e_nopresub));
- return NULL;
+ return 0;
}
pat = NULL; // search_regcomp() will use previous pattern
- sub = (char_u *)old_sub.sub;
+ sub = old_sub.sub;
// Vi compatibility quirk: repeating with ":s" keeps the cursor in the
// last column after using "$".
endcolumn = (curwin->w_curswant == MAXCOL);
}
- if (sub != NULL && sub_joining_lines(eap, pat, sub, cmd, !preview)) {
- return NULL;
+ if (sub != NULL && sub_joining_lines(eap, pat, sub, cmd, !cmdpreview)) {
+ return 0;
}
cmd = sub_parse_flags(cmd, &subflags, &which_pat);
@@ -3574,13 +3582,13 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle
// check for a trailing count
cmd = skipwhite(cmd);
if (ascii_isdigit(*cmd)) {
- i = getdigits_long(&cmd, true, 0);
+ i = getdigits_long((char_u **)&cmd, true, 0);
if (i <= 0 && !eap->skip && subflags.do_error) {
emsg(_(e_zerocount));
- return NULL;
+ return 0;
}
eap->line1 = eap->line2;
- eap->line2 += i - 1;
+ eap->line2 += (linenr_T)i - 1;
if (eap->line2 > curbuf->b_ml.ml_line_count) {
eap->line2 = curbuf->b_ml.ml_line_count;
}
@@ -3591,29 +3599,29 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle
*/
cmd = skipwhite(cmd);
if (*cmd && *cmd != '"') { // if not end-of-line or comment
- eap->nextcmd = check_nextcmd(cmd);
+ eap->nextcmd = (char *)check_nextcmd((char_u *)cmd);
if (eap->nextcmd == NULL) {
emsg(_(e_trailing));
- return NULL;
+ return 0;
}
}
if (eap->skip) { // not executing commands, only parsing
- return NULL;
+ return 0;
}
if (!subflags.do_count && !MODIFIABLE(curbuf)) {
// Substitution is not allowed in non-'modifiable' buffer
emsg(_(e_modifiable));
- return NULL;
+ return 0;
}
- if (search_regcomp(pat, RE_SUBST, which_pat, (preview ? 0 : SEARCH_HIS),
+ if (search_regcomp((char_u *)pat, RE_SUBST, which_pat, (cmdpreview ? 0 : SEARCH_HIS),
&regmatch) == FAIL) {
if (subflags.do_error) {
emsg(_(e_invcmd));
}
- return NULL;
+ return 0;
}
// the 'i' or 'I' flag overrules 'ignorecase' and 'smartcase'
@@ -3625,12 +3633,32 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle
sub_firstline = NULL;
- // ~ in the substitute pattern is replaced with the old pattern.
- // We do it here once to avoid it to be replaced over and over again.
- // But don't do it when it starts with "\=", then it's an expression.
assert(sub != NULL);
- if (!(sub[0] == '\\' && sub[1] == '=')) {
- sub = regtilde(sub, p_magic);
+
+ bool sub_needs_free = false;
+ char *sub_copy = NULL;
+
+ // If the substitute pattern starts with "\=" then it's an expression.
+ // Make a copy, a recursive function may free it.
+ // Otherwise, '~' in the substitute pattern is replaced with the old
+ // pattern. We do it here once to avoid it to be replaced over and over
+ // again.
+ if (sub[0] == '\\' && sub[1] == '=') {
+ sub = xstrdup(sub);
+ sub_copy = sub;
+ } else {
+ char *source = sub;
+ sub = (char *)regtilde((char_u *)sub, p_magic, cmdpreview);
+ // When previewing, the new pattern allocated by regtilde() needs to be freed
+ // in this function because it will not be used or freed by regtilde() later.
+ sub_needs_free = cmdpreview && sub != source;
+ }
+
+ bool cmdheight0 = p_ch < 1 && !ui_has(kUIMessages);
+ if (cmdheight0) {
+ // If cmdheight is 0, cmdheight must be set to 1 when we enter command line.
+ set_option_value("ch", 1L, NULL, 0);
+ redraw_statuslines();
}
// Check for a match on each line.
@@ -3639,7 +3667,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle
for (linenr_T lnum = eap->line1;
lnum <= line2 && !got_quit && !aborting()
- && (!preview || preview_lines.lines_needed <= (linenr_T)p_cwh
+ && (!cmdpreview || preview_lines.lines_needed <= (linenr_T)p_cwh
|| lnum <= curwin->w_botline);
lnum++) {
long nmatch = vim_regexec_multi(&regmatch, curwin, curbuf, lnum,
@@ -3648,8 +3676,8 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle
colnr_T copycol;
colnr_T matchcol;
colnr_T prev_matchcol = MAXCOL;
- char_u *new_end, *new_start = NULL;
- char_u *p1;
+ char *new_end, *new_start = NULL;
+ char *p1;
bool did_sub = false;
int lastone;
long nmatch_tl = 0; // nr of lines matched below lnum
@@ -3749,7 +3777,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle
break;
}
if (sub_firstline == NULL) {
- sub_firstline = vim_strsave(ml_get(sub_firstlnum));
+ sub_firstline = (char *)vim_strsave(ml_get(sub_firstlnum));
}
// Save the line number of the last change for the final
@@ -3806,13 +3834,13 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle
}
}
- if (subflags.do_ask && !preview) {
+ if (subflags.do_ask && !cmdpreview) {
int typed = 0;
- // change State to CONFIRM, so that the mouse works
+ // change State to MODE_CONFIRM, so that the mouse works
// properly
int save_State = State;
- State = CONFIRM;
+ State = MODE_CONFIRM;
setmouse(); // disable mouse in xterm
curwin->w_cursor.col = regmatch.startpos[0].col;
@@ -3823,7 +3851,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle
// When 'cpoptions' contains "u" don't sync undo when
// asking for confirmation.
if (vim_strchr(p_cpo, CPO_UNDO) != NULL) {
- ++no_u_sync;
+ no_u_sync++;
}
/*
@@ -3832,7 +3860,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle
while (subflags.do_ask) {
if (exmode_active) {
char *prompt;
- char_u *resp;
+ char *resp;
colnr_T sc, ec;
print_line_no_prefix(lnum, subflags.do_number, subflags.do_list);
@@ -3843,25 +3871,34 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle
curwin->w_cursor.col = 0;
}
getvcol(curwin, &curwin->w_cursor, NULL, NULL, &ec);
+ curwin->w_cursor.col = regmatch.startpos[0].col;
if (subflags.do_number || curwin->w_p_nu) {
int numw = number_width(curwin) + 1;
sc += numw;
ec += numw;
}
- prompt = xmallocz(ec + 1);
- memset(prompt, ' ', sc);
- memset(prompt + sc, '^', ec - sc + 1);
- resp = (char_u *)getcmdline_prompt(NUL, prompt, 0, EXPAND_NOTHING,
- NULL, CALLBACK_NONE);
+ prompt = xmallocz((size_t)ec + 1);
+ memset(prompt, ' ', (size_t)sc);
+ memset(prompt + sc, '^', (size_t)(ec - sc) + 1);
+ resp = getcmdline_prompt(-1, prompt, 0, EXPAND_NOTHING, NULL, CALLBACK_NONE);
msg_putchar('\n');
xfree(prompt);
if (resp != NULL) {
- typed = *resp;
+ typed = (char_u)(*resp);
xfree(resp);
+ } else {
+ // getcmdline_prompt() returns NULL if there is no command line to return.
+ typed = NUL;
+ }
+ // When ":normal" runs out of characters we get
+ // an empty line. Use "q" to get out of the
+ // loop.
+ if (ex_normal_busy && typed == NUL) {
+ typed = 'q';
}
} else {
- char_u *orig_line = NULL;
+ char *orig_line = NULL;
int len_change = 0;
const bool save_p_lz = p_lz;
int save_p_fen = curwin->w_p_fen;
@@ -3881,8 +3918,9 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle
// really update the line, it would change
// what matches. Temporarily replace the line
// and change it back afterwards.
- orig_line = vim_strsave(ml_get(lnum));
- char_u *new_line = concat_str(new_start, sub_firstline + copycol);
+ orig_line = (char *)vim_strsave(ml_get(lnum));
+ char *new_line = (char *)concat_str((char_u *)new_start,
+ (char_u *)sub_firstline + copycol);
// Position the cursor relative to the end of the line, the
// previous substitute may have inserted or deleted characters
@@ -3916,15 +3954,17 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle
msg_ext_set_kind("confirm_sub");
smsg_attr(HL_ATTR(HLF_R), // Same highlight as wait_return().
_("replace with %s (y/n/a/q/l/^E/^Y)?"), sub);
- msg_no_more = FALSE;
- msg_scroll = i;
+ msg_no_more = false;
+ msg_scroll = (int)i;
showruler(true);
ui_cursor_goto(msg_row, msg_col);
RedrawingDisabled = temp;
no_mapping++; // don't map this key
+ allow_keys++; // allow special keys
typed = plain_vgetc();
no_mapping--;
+ allow_keys--;
// clear the question
msg_didout = false; // don't scroll up
@@ -3968,7 +4008,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle
State = save_State;
setmouse();
if (vim_strchr(p_cpo, CPO_UNDO) != NULL) {
- --no_u_sync;
+ no_u_sync--;
}
if (typed == 'n') {
@@ -3996,7 +4036,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle
// go beyond the last line of the buffer.
if (nmatch > curbuf->b_ml.ml_line_count - sub_firstlnum + 1) {
nmatch = curbuf->b_ml.ml_line_count - sub_firstlnum + 1;
- current_match.end.lnum = sub_firstlnum + nmatch;
+ current_match.end.lnum = sub_firstlnum + (linenr_T)nmatch;
skip_match = true;
}
@@ -4005,9 +4045,9 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle
/* For a multi-line match, make a copy of the last matched */ \
/* line and continue in that one. */ \
if (nmatch > 1) { \
- sub_firstlnum += nmatch - 1; \
+ sub_firstlnum += (linenr_T)nmatch - 1; \
xfree(sub_firstline); \
- sub_firstline = vim_strsave(ml_get(sub_firstlnum)); \
+ sub_firstline = (char *)vim_strsave(ml_get(sub_firstlnum)); \
/* When going beyond the last line, stop substituting. */ \
if (sub_firstlnum <= line2) { \
do_again = true; \
@@ -4019,7 +4059,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle
/* Already hit end of the buffer, sub_firstlnum is one */ \
/* less than what it ought to be. */ \
xfree(sub_firstline); \
- sub_firstline = vim_strsave((char_u *)""); \
+ sub_firstline = xstrdup(""); \
copycol = 0; \
} \
} while (0)
@@ -4027,25 +4067,25 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle
// Save the line numbers for the preview buffer
// NOTE: If the pattern matches a final newline, the next line will
// be shown also, but should not be highlighted. Intentional for now.
- if (preview && !has_second_delim) {
+ if (cmdpreview && !has_second_delim) {
current_match.start.col = regmatch.startpos[0].col;
if (current_match.end.lnum == 0) {
- current_match.end.lnum = sub_firstlnum + nmatch - 1;
+ current_match.end.lnum = sub_firstlnum + (linenr_T)nmatch - 1;
}
current_match.end.col = regmatch.endpos[0].col;
ADJUST_SUB_FIRSTLNUM();
- lnum += nmatch - 1;
+ lnum += (linenr_T)nmatch - 1;
goto skip;
}
-
// 3. Substitute the string. During 'inccommand' preview only do this if
// there is a replace pattern.
- if (!preview || has_second_delim) {
+ if (!cmdpreview || has_second_delim) {
long lnum_start = lnum; // save the start lnum
- save_ma = curbuf->b_p_ma;
+ int save_ma = curbuf->b_p_ma;
+ int save_sandbox = sandbox;
if (subflags.do_count) {
// prevent accidentally changing the buffer by a function
curbuf->b_p_ma = false;
@@ -4054,19 +4094,24 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle
// Save flags for recursion. They can change for e.g.
// :s/^/\=execute("s#^##gn")
subflags_T subflags_save = subflags;
- // get length of substitution part
+
+ // Disallow changing text or switching window in an expression.
+ textlock++;
+ // Get length of substitution part, including the NUL.
+ // When it fails sublen is zero.
sublen = vim_regsub_multi(&regmatch,
sub_firstlnum - regmatch.startpos[0].lnum,
- sub, sub_firstline, false, p_magic, true);
+ (char_u *)sub, (char_u *)sub_firstline, 0,
+ REGSUB_BACKSLASH | (p_magic ? REGSUB_MAGIC : 0));
+ textlock--;
+
// If getting the substitute string caused an error, don't do
// the replacement.
// Don't keep flags set by a recursive call
subflags = subflags_save;
- if (aborting() || subflags.do_count) {
+ if (sublen == 0 || aborting() || subflags.do_count) {
curbuf->b_p_ma = save_ma;
- if (sandbox > 0) {
- sandbox--;
- }
+ sandbox = save_sandbox;
goto skip;
}
@@ -4078,13 +4123,13 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle
if (nmatch == 1) {
p1 = sub_firstline;
} else {
- p1 = ml_get(sub_firstlnum + nmatch - 1);
+ p1 = (char *)ml_get(sub_firstlnum + (linenr_T)nmatch - 1);
nmatch_tl += nmatch - 1;
}
- size_t copy_len = regmatch.startpos[0].col - copycol;
+ size_t copy_len = (size_t)(regmatch.startpos[0].col - copycol);
new_end = sub_grow_buf(&new_start,
- (STRLEN(p1) - regmatch.endpos[0].col)
- + copy_len + sublen + 1);
+ (colnr_T)STRLEN(p1) - regmatch.endpos[0].col
+ + (colnr_T)copy_len + sublen + 1);
// copy the text up to the part that matched
memmove(new_end, sub_firstline + copycol, copy_len);
@@ -4092,12 +4137,15 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle
// Finally, at this point we can know where the match actually will
// start in the new text
- int start_col = new_end - new_start;
+ int start_col = (int)(new_end - new_start);
current_match.start.col = start_col;
+ textlock++;
(void)vim_regsub_multi(&regmatch,
sub_firstlnum - regmatch.startpos[0].lnum,
- sub, new_end, true, p_magic, true);
+ (char_u *)sub, (char_u *)new_end, sublen,
+ REGSUB_COPY | REGSUB_BACKSLASH | (p_magic ? REGSUB_MAGIC : 0));
+ textlock--;
sub_nsubs++;
did_sub = true;
@@ -4113,12 +4161,11 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle
// TODO(bfredl): this has some robustness issues, look into later.
bcount_t replaced_bytes = 0;
lpos_T start = regmatch.startpos[0], end = regmatch.endpos[0];
- for (i = 0; i < nmatch-1; i++) {
- replaced_bytes += STRLEN(ml_get(lnum_start+i)) + 1;
+ for (i = 0; i < nmatch - 1; i++) {
+ replaced_bytes += (bcount_t)STRLEN(ml_get((linenr_T)(lnum_start + i))) + 1;
}
replaced_bytes += end.col - start.col;
-
// Now the trick is to replace CTRL-M chars with a real line
// break. This would make it impossible to insert a CTRL-M in
// the text. The line break can be avoided by preceding the
@@ -4127,6 +4174,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle
// That is Vi compatible.
for (p1 = new_end; *p1; p1++) {
if (p1[0] == '\\' && p1[1] != NUL) { // remove backslash
+ sublen--; // correct the byte counts for extmark_splice()
STRMOVE(p1, p1 + 1);
} else if (*p1 == CAR) {
if (u_inssub(lnum) == OK) { // prepare for undo
@@ -4157,7 +4205,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle
p1 += utfc_ptr2len(p1) - 1;
}
}
- size_t new_endcol = STRLEN(new_start);
+ colnr_T new_endcol = (colnr_T)STRLEN(new_start);
current_match.end.col = new_endcol;
current_match.end.lnum = lnum;
@@ -4169,12 +4217,11 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle
u_save_cursor();
did_save = true;
}
- extmark_splice(curbuf, lnum_start-1, start_col,
- end.lnum-start.lnum, matchcols, replaced_bytes,
- lnum-lnum_start, subcols, sublen-1, kExtmarkUndo);
+ extmark_splice(curbuf, (int)lnum_start - 1, start_col,
+ end.lnum - start.lnum, matchcols, replaced_bytes,
+ lnum - (linenr_T)lnum_start, subcols, sublen - 1, kExtmarkUndo);
}
-
// 4. If subflags.do_all is set, find next match.
// Prevent endless loop with patterns that match empty
// strings, e.g. :s/$/pat/g or :s/[a-z]* /(&)/g.
@@ -4241,13 +4288,13 @@ skip:
for (i = 0; i < nmatch_tl; i++) {
ml_delete(lnum, false);
}
- mark_adjust(lnum, lnum + nmatch_tl - 1,
- (long)MAXLNUM, -nmatch_tl, kExtmarkNOOP);
+ mark_adjust(lnum, lnum + (linenr_T)nmatch_tl - 1,
+ (long)MAXLNUM, (linenr_T)(-nmatch_tl), kExtmarkNOOP);
if (subflags.do_ask) {
- deleted_lines(lnum, nmatch_tl);
+ deleted_lines(lnum, (linenr_T)nmatch_tl);
}
lnum--;
- line2 -= nmatch_tl; // nr of lines decreases
+ line2 -= (linenr_T)nmatch_tl; // nr of lines decreases
nmatch_tl = 0;
}
@@ -4287,21 +4334,29 @@ skip:
lnum -= regmatch.startpos[0].lnum;
}
+ // uncrustify:off
+
#define PUSH_PREVIEW_LINES() \
do { \
- linenr_T match_lines = current_match.end.lnum \
- - current_match.start.lnum +1; \
- if (preview_lines.subresults.size > 0) { \
- linenr_T last = kv_last(preview_lines.subresults).end.lnum; \
- if (last == current_match.start.lnum) { \
- preview_lines.lines_needed += match_lines - 1; \
+ if (cmdpreview) { \
+ linenr_T match_lines = current_match.end.lnum \
+ - current_match.start.lnum +1; \
+ if (preview_lines.subresults.size > 0) { \
+ linenr_T last = kv_last(preview_lines.subresults).end.lnum; \
+ if (last == current_match.start.lnum) { \
+ preview_lines.lines_needed += match_lines - 1; \
+ } else { \
+ preview_lines.lines_needed += match_lines; \
+ } \
+ } else { \
+ preview_lines.lines_needed += match_lines; \
} \
- } else { \
- preview_lines.lines_needed += match_lines; \
+ kv_push(preview_lines.subresults, current_match); \
} \
- kv_push(preview_lines.subresults, current_match); \
} while (0)
+ // uncrustify:on
+
// Push the match to preview_lines.
PUSH_PREVIEW_LINES();
@@ -4335,12 +4390,11 @@ skip:
// the line number before the change (same as adding the number of
// deleted lines).
i = curbuf->b_ml.ml_line_count - old_line_count;
- changed_lines(first_line, 0, last_line - i, i, false);
+ changed_lines(first_line, 0, last_line - (linenr_T)i, (linenr_T)i, false);
int64_t num_added = last_line - first_line;
int64_t num_removed = num_added - i;
- buf_updates_send_changes(curbuf, first_line, num_added, num_removed,
- do_buf_event);
+ buf_updates_send_changes(curbuf, first_line, num_added, num_removed);
}
xfree(sub_firstline); // may have to free allocated copy of the line
@@ -4351,10 +4405,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.cmod_flags & CMOD_LOCKMARKS) == 0) {
+ // 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
@@ -4365,7 +4421,7 @@ skip:
beginline(BL_WHITE | BL_FIX);
}
}
- if (!preview && !do_sub_msg(subflags.do_count) && subflags.do_ask) {
+ if (!cmdpreview && !do_sub_msg(subflags.do_count) && subflags.do_ask) {
msg("");
}
} else {
@@ -4393,42 +4449,39 @@ skip:
}
vim_regfree(regmatch.regprog);
+ xfree(sub_copy);
+ if (sub_needs_free) {
+ xfree(sub);
+ }
// Restore the flag values, they can be used for ":&&".
subflags.do_all = save_do_all;
subflags.do_ask = save_do_ask;
+ int retv = 0;
+
// Show 'inccommand' preview if there are matched lines.
- buf_T *preview_buf = NULL;
- size_t subsize = preview_lines.subresults.size;
- if (preview && !aborting()) {
+ if (cmdpreview && !aborting()) {
if (got_quit || profile_passed_limit(timeout)) { // Too slow, disable.
- set_string_option_direct("icm", -1, (char_u *)"", OPT_FREE,
- SID_NONE);
+ set_string_option_direct("icm", -1, "", OPT_FREE, SID_NONE);
} else if (*p_icm != NUL && pat != NULL) {
- if (pre_src_id == 0) {
- // Get a unique new src_id, saved in a static
- pre_src_id = (int)nvim_create_namespace((String)STRING_INIT);
- }
if (pre_hl_id == 0) {
pre_hl_id = syn_check_group(S_LEN("Substitute"));
}
- curbuf->b_changed = save_b_changed; // preserve 'modified' during preview
- preview_buf = show_sub(eap, old_cursor, &preview_lines,
- pre_hl_id, pre_src_id, bufnr);
- if (subsize > 0) {
- extmark_clear(orig_buf, pre_src_id, eap->line1-1, 0,
- kv_last(preview_lines.subresults).end.lnum-1, MAXCOL);
- }
+ retv = show_sub(eap, old_cursor, &preview_lines, pre_hl_id, cmdpreview_ns, cmdpreview_bufnr);
}
}
- kv_destroy(preview_lines.subresults);
+ if (cmdheight0) {
+ // Restore cmdheight
+ set_option_value("ch", 0L, NULL, 0);
+ }
- return preview_buf;
+ kv_destroy(preview_lines.subresults);
+ return retv;
#undef ADJUST_SUB_FIRSTLNUM
#undef PUSH_PREVIEW_LINES
-} // NOLINT(readability/fn_size)
+}
/// Give message for number of substitutions.
/// Can also be used after a ":global" command.
@@ -4479,42 +4532,40 @@ bool do_sub_msg(bool count_only)
return false;
}
-static void global_exe_one(char_u *const cmd, const linenr_T lnum)
+static void global_exe_one(char *const cmd, const linenr_T lnum)
{
curwin->w_cursor.lnum = lnum;
curwin->w_cursor.col = 0;
if (*cmd == NUL || *cmd == '\n') {
- do_cmdline((char_u *)"p", NULL, NULL, DOCMD_NOWAIT);
+ do_cmdline("p", NULL, NULL, DOCMD_NOWAIT);
} else {
do_cmdline(cmd, NULL, NULL, DOCMD_NOWAIT);
}
}
-/*
- * Execute a global command of the form:
- *
- * g/pattern/X : execute X on all lines where pattern matches
- * v/pattern/X : execute X on all lines where pattern does not match
- *
- * where 'X' is an EX command
- *
- * The command character (as well as the trailing slash) is optional, and
- * is assumed to be 'p' if missing.
- *
- * This is implemented in two passes: first we scan the file for the pattern and
- * set a mark for each line that (not) matches. Secondly we execute the command
- * for each line that has a mark. This is required because after deleting
- * lines we do not know where to search for the next match.
- */
+/// Execute a global command of the form:
+///
+/// g/pattern/X : execute X on all lines where pattern matches
+/// v/pattern/X : execute X on all lines where pattern does not match
+///
+/// where 'X' is an EX command
+///
+/// The command character (as well as the trailing slash) is optional, and
+/// is assumed to be 'p' if missing.
+///
+/// This is implemented in two passes: first we scan the file for the pattern and
+/// set a mark for each line that (not) matches. Secondly we execute the command
+/// for each line that has a mark. This is required because after deleting
+/// lines we do not know where to search for the next match.
void ex_global(exarg_T *eap)
{
linenr_T lnum; // line number according to old situation
int ndone = 0;
int type; // first char of cmd: 'v' or 'g'
- char_u *cmd; // command argument
+ char *cmd; // command argument
- char_u delim; // delimiter, normally '/'
- char_u *pat;
+ char delim; // delimiter, normally '/'
+ char *pat;
regmmatch_T regmatch;
int match;
int which_pat;
@@ -4531,7 +4582,7 @@ void ex_global(exarg_T *eap)
if (eap->forceit) { // ":global!" is like ":vglobal"
type = 'v';
} else {
- type = *eap->cmd;
+ type = (uint8_t)(*eap->cmd);
}
cmd = eap->arg;
which_pat = RE_LAST; // default: use last used regexp
@@ -4542,8 +4593,8 @@ void ex_global(exarg_T *eap)
* "\&": use previous substitute pattern.
*/
if (*cmd == '\\') {
- ++cmd;
- if (vim_strchr((char_u *)"/?&", *cmd) == NULL) {
+ cmd++;
+ if (vim_strchr("/?&", *cmd) == NULL) {
emsg(_(e_backslash));
return;
}
@@ -4552,8 +4603,8 @@ void ex_global(exarg_T *eap)
} else {
which_pat = RE_SEARCH; // use previous search pattern
}
- ++cmd;
- pat = (char_u *)"";
+ cmd++;
+ pat = "";
} else if (*cmd == NUL) {
emsg(_("E148: Regular expression missing from global"));
return;
@@ -4561,25 +4612,22 @@ void ex_global(exarg_T *eap)
return;
} else {
delim = *cmd; // get the delimiter
- if (delim) {
- ++cmd; // skip delimiter if there is one
- }
+ cmd++; // skip delimiter if there is one
pat = cmd; // remember start of pattern
- cmd = skip_regexp(cmd, delim, p_magic, &eap->arg);
+ cmd = (char *)skip_regexp((char_u *)cmd, delim, p_magic, (char_u **)&eap->arg);
if (cmd[0] == delim) { // end delimiter found
*cmd++ = NUL; // replace it with a NUL
}
}
- if (search_regcomp(pat, RE_BOTH, which_pat, SEARCH_HIS, &regmatch) == FAIL) {
+ if (search_regcomp((char_u *)pat, RE_BOTH, which_pat, SEARCH_HIS, &regmatch) == FAIL) {
emsg(_(e_invcmd));
return;
}
if (global_busy) {
lnum = curwin->w_cursor.lnum;
- match = vim_regexec_multi(&regmatch, curwin, curbuf, lnum,
- (colnr_T)0, NULL, NULL);
+ match = (int)vim_regexec_multi(&regmatch, curwin, curbuf, lnum, 0, NULL, NULL);
if ((type == 'g' && match) || (type == 'v' && !match)) {
global_exe_one(cmd, lnum);
}
@@ -4587,8 +4635,7 @@ void ex_global(exarg_T *eap)
// pass 1: set marks for each (not) matching line
for (lnum = eap->line1; lnum <= eap->line2 && !got_int; lnum++) {
// a match on this line?
- match = vim_regexec_multi(&regmatch, curwin, curbuf, lnum,
- (colnr_T)0, NULL, NULL);
+ match = (int)vim_regexec_multi(&regmatch, curwin, curbuf, lnum, 0, NULL, NULL);
if (regmatch.regprog == NULL) {
break; // re-compiling regprog failed
}
@@ -4617,12 +4664,11 @@ void ex_global(exarg_T *eap)
}
/// Execute `cmd` on lines marked with ml_setmarked().
-void global_exe(char_u *cmd)
+void global_exe(char *cmd)
{
linenr_T old_lcount; // b_ml.ml_line_count before the command
buf_T *old_buf = curbuf; // remember what buffer we started in
linenr_T lnum; // line number according to old situation
- int save_mapped_ctrl_c = mapped_ctrl_c;
// Set current position only once for a global command.
// If global_busy is set, setpcmark() will not do anything.
@@ -4631,8 +4677,6 @@ void global_exe(char_u *cmd)
// When the command writes a message, don't overwrite the command.
msg_didout = true;
- // Disable CTRL-C mapping, let it interrupt (potentially long output).
- mapped_ctrl_c = 0;
sub_nsubs = 0;
sub_nlines = 0;
@@ -4645,7 +4689,6 @@ void global_exe(char_u *cmd)
os_breakcheck();
}
- mapped_ctrl_c = save_mapped_ctrl_c;
global_busy = 0;
if (global_need_beginline) {
beginline(BL_WHITE | BL_FIX);
@@ -4713,33 +4756,30 @@ bool prepare_tagpreview(bool undo_sync)
and 'cursorbind' */
curwin->w_p_diff = false; // no 'diff'
set_string_option_direct("fdc", -1, // no 'foldcolumn'
- (char_u *)"0", OPT_FREE, SID_NONE);
+ "0", OPT_FREE, SID_NONE);
return true;
}
}
return false;
}
-
-/*
- * ":help": open a read-only window on a help file
- */
+/// ":help": open a read-only window on a help file
void ex_help(exarg_T *eap)
{
- char_u *arg;
- char_u *tag;
+ char *arg;
+ char *tag;
FILE *helpfd; // file descriptor of help file
int n;
int i;
win_T *wp;
int num_matches;
- char_u **matches;
- char_u *p;
+ char **matches;
+ char *p;
int empty_fnum = 0;
int alt_fnum = 0;
buf_T *buf;
int len;
- char_u *lang;
+ char *lang;
const bool old_KeyTyped = KeyTyped;
if (eap != NULL) {
@@ -4747,7 +4787,7 @@ void ex_help(exarg_T *eap)
* A ":help" command ends at the first LF, or at a '|' that is
* followed by some text. Set nextcmd to the following command.
*/
- for (arg = eap->arg; *arg; ++arg) {
+ for (arg = eap->arg; *arg; arg++) {
if (*arg == '\n' || *arg == '\r'
|| (*arg == '|' && arg[1] != NUL && arg[1] != '|')) {
*arg++ = NUL;
@@ -4766,7 +4806,7 @@ void ex_help(exarg_T *eap)
return;
}
} else {
- arg = (char_u *)"";
+ arg = "";
}
// remove trailing blanks
@@ -4780,14 +4820,13 @@ void ex_help(exarg_T *eap)
// When no argument given go to the index.
if (*arg == NUL) {
- arg = (char_u *)"help.txt";
+ arg = "help.txt";
}
/*
* Check if there is a match for the argument.
*/
- n = find_help_tags(arg, &num_matches, &matches,
- eap != NULL && eap->forceit);
+ n = find_help_tags(arg, &num_matches, &matches, eap != NULL && eap->forceit);
i = 0;
if (n != FAIL && lang != NULL) {
@@ -4807,22 +4846,21 @@ void ex_help(exarg_T *eap)
semsg(_("E149: Sorry, no help for %s"), arg);
}
if (n != FAIL) {
- FreeWild(num_matches, matches);
+ FreeWild(num_matches, (char_u **)matches);
}
return;
}
// The first match (in the requested language) is the best match.
- tag = vim_strsave(matches[i]);
- FreeWild(num_matches, matches);
+ tag = xstrdup(matches[i]);
+ FreeWild(num_matches, (char_u **)matches);
/*
* Re-use an existing help window or open a new one.
* Always open a new one for ":tab help".
*/
- if (!bt_help(curwin->w_buffer)
- || cmdmod.tab != 0) {
- if (cmdmod.tab != 0) {
+ if (!bt_help(curwin->w_buffer) || cmdmod.cmod_tab != 0) {
+ if (cmdmod.cmod_tab != 0) {
wp = NULL;
} else {
wp = NULL;
@@ -4848,7 +4886,7 @@ void ex_help(exarg_T *eap)
// specified, the current window is vertically split and
// narrow.
n = WSP_HELP;
- if (cmdmod.split == 0 && curwin->w_width != Columns
+ if (cmdmod.cmod_split == 0 && curwin->w_width != Columns
&& curwin->w_width < 80) {
n |= WSP_TOP;
}
@@ -4868,23 +4906,22 @@ void ex_help(exarg_T *eap)
alt_fnum = curbuf->b_fnum;
(void)do_ecmd(0, NULL, NULL, NULL, ECMD_LASTL,
ECMD_HIDE + ECMD_SET_HELP,
- NULL // buffer is still open, don't store info
- );
- if (!cmdmod.keepalt) {
+ NULL); // buffer is still open, don't store info
+
+ if ((cmdmod.cmod_flags & CMOD_KEEPALT) == 0) {
curwin->w_alt_fnum = alt_fnum;
}
empty_fnum = curbuf->b_fnum;
}
}
- if (!p_im) {
- restart_edit = 0; // don't want insert mode in help file
- }
+ restart_edit = 0; // don't want insert mode in help file
+
// Restore KeyTyped, setting 'filetype=help' may reset it.
// It is needed for do_tag top open folds under the cursor.
KeyTyped = old_KeyTyped;
- do_tag(tag, DT_HELP, 1, FALSE, TRUE);
+ do_tag((char_u *)tag, DT_HELP, 1, false, true);
// Delete the empty buffer if we're not using it. Careful: autocommands
// may have jumped to another window, check that the buffer is not in a
@@ -4897,7 +4934,8 @@ void ex_help(exarg_T *eap)
}
// keep the previous alternate file
- if (alt_fnum != 0 && curwin->w_alt_fnum == empty_fnum && !cmdmod.keepalt) {
+ if (alt_fnum != 0 && curwin->w_alt_fnum == empty_fnum
+ && (cmdmod.cmod_flags & CMOD_KEEPALT) == 0) {
curwin->w_alt_fnum = alt_fnum;
}
@@ -4905,13 +4943,11 @@ erret:
xfree(tag);
}
-
-/*
- * In an argument search for a language specifiers in the form "@xx".
- * Changes the "@" to NUL if found, and returns a pointer to "xx".
- * Returns NULL if not found.
- */
-char_u *check_help_lang(char_u *arg)
+/// In an argument search for a language specifiers in the form "@xx".
+/// Changes the "@" to NUL if found, and returns a pointer to "xx".
+///
+/// @return NULL if not found.
+char *check_help_lang(char *arg)
{
int len = (int)STRLEN(arg);
@@ -4937,10 +4973,11 @@ char_u *check_help_lang(char_u *arg)
/// @param wrong_case no matching case
///
/// @return a heuristic indicating how well the given string matches.
-int help_heuristic(char_u *matched_string, int offset, int wrong_case)
+int help_heuristic(char *matched_string, int offset, int wrong_case)
+ FUNC_ATTR_PURE
{
int num_letters;
- char_u *p;
+ char *p;
num_letters = 0;
for (p = matched_string; *p; p++) {
@@ -4973,13 +5010,11 @@ int help_heuristic(char_u *matched_string, int offset, int wrong_case)
if (matched_string[0] == '+' && matched_string[1] != NUL) {
offset += 100;
}
- return (int)(100 * num_letters + STRLEN(matched_string) + offset);
+ return 100 * num_letters + (int)STRLEN(matched_string) + offset;
}
-/*
- * Compare functions for qsort() below, that checks the help heuristics number
- * that has been put after the tagname by find_tags().
- */
+/// Compare functions for qsort() below, that checks the help heuristics number
+/// that has been put after the tagname by find_tags().
static int help_compare(const void *s1, const void *s2)
{
char *p1;
@@ -4987,47 +5022,77 @@ static int help_compare(const void *s1, const void *s2)
p1 = *(char **)s1 + strlen(*(char **)s1) + 1;
p2 = *(char **)s2 + strlen(*(char **)s2) + 1;
- return strcmp(p1, p2);
+
+ // Compare by help heuristic number first.
+ int cmp = strcmp(p1, p2);
+ if (cmp != 0) {
+ return cmp;
+ }
+
+ // Compare by strings as tie-breaker when same heuristic number.
+ return strcmp(*(char **)s1, *(char **)s2);
}
-// Find all help tags matching "arg", sort them and return in matches[], with
-// the number of matches in num_matches.
-// The matches will be sorted with a "best" match algorithm.
-// When "keep_lang" is true try keeping the language of the current buffer.
-int find_help_tags(const char_u *arg, int *num_matches, char_u ***matches, bool keep_lang)
+/// Find all help tags matching "arg", sort them and return in matches[], with
+/// the number of matches in num_matches.
+/// The matches will be sorted with a "best" match algorithm.
+/// When "keep_lang" is true try keeping the language of the current buffer.
+int find_help_tags(const char *arg, int *num_matches, char ***matches, bool keep_lang)
{
int i;
- static const char *(mtable[]) = {
- "*", "g*", "[*", "]*",
- "/*", "/\\*", "\"*", "**",
- "/\\(\\)", "/\\%(\\)",
- "?", ":?", "?<CR>", "g?", "g?g?", "g??",
- "-?", "q?", "v_g?",
- "/\\?", "/\\z(\\)", "\\=", ":s\\=",
- "[count]", "[quotex]",
- "[range]", ":[range]",
- "[pattern]", "\\|", "\\%$",
- "s/\\~", "s/\\U", "s/\\L",
- "s/\\1", "s/\\2", "s/\\3", "s/\\9"
- };
- static const char *(rtable[]) = {
- "star", "gstar", "[star", "]star",
- "/star", "/\\\\star", "quotestar", "starstar",
- "/\\\\(\\\\)", "/\\\\%(\\\\)",
- "?", ":?", "?<CR>", "g?", "g?g?", "g??",
- "-?", "q?", "v_g?",
- "/\\\\?", "/\\\\z(\\\\)", "\\\\=", ":s\\\\=",
- "\\[count]", "\\[quotex]",
- "\\[range]", ":\\[range]",
- "\\[pattern]", "\\\\bar", "/\\\\%\\$",
- "s/\\\\\\~", "s/\\\\U", "s/\\\\L",
- "s/\\\\1", "s/\\\\2", "s/\\\\3", "s/\\\\9"
+
+ // Specific tags that either have a specific replacement or won't go
+ // throught the generic rules.
+ static char *(except_tbl[][2]) = {
+ { "*", "star" },
+ { "g*", "gstar" },
+ { "[*", "[star" },
+ { "]*", "]star" },
+ { ":*", ":star" },
+ { "/*", "/star" }, // NOLINT
+ { "/\\*", "/\\\\star" },
+ { "\"*", "quotestar" },
+ { "**", "starstar" },
+ { "cpo-*", "cpo-star" },
+ { "/\\(\\)", "/\\\\(\\\\)" },
+ { "/\\%(\\)", "/\\\\%(\\\\)" },
+ { "?", "?" },
+ { "??", "??" },
+ { ":?", ":?" },
+ { "?<CR>", "?<CR>" },
+ { "g?", "g?" },
+ { "g?g?", "g?g?" },
+ { "g??", "g??" },
+ { "-?", "-?" },
+ { "q?", "q?" },
+ { "v_g?", "v_g?" },
+ { "/\\?", "/\\\\?" },
+ { "/\\z(\\)", "/\\\\z(\\\\)" },
+ { "\\=", "\\\\=" },
+ { ":s\\=", ":s\\\\=" },
+ { "[count]", "\\[count]" },
+ { "[quotex]", "\\[quotex]" },
+ { "[range]", "\\[range]" },
+ { ":[range]", ":\\[range]" },
+ { "[pattern]", "\\[pattern]" },
+ { "\\|", "\\\\bar" },
+ { "\\%$", "/\\\\%\\$" },
+ { "s/\\~", "s/\\\\\\~" },
+ { "s/\\U", "s/\\\\U" },
+ { "s/\\L", "s/\\\\L" },
+ { "s/\\1", "s/\\\\1" },
+ { "s/\\2", "s/\\\\2" },
+ { "s/\\3", "s/\\\\3" },
+ { "s/\\9", "s/\\\\9" },
+ { NULL, NULL }
};
+
static const char *(expr_table[]) = {
"!=?", "!~?", "<=?", "<?", "==?", "=~?",
">=?", ">?", "is?", "isnot?"
};
- char_u *d = IObuff; // assume IObuff is long enough!
+ char *d = (char *)IObuff; // assume IObuff is long enough!
+ d[0] = NUL;
if (STRNICMP(arg, "expr-", 5) == 0) {
// When the string starting with "expr-" and containing '?' and matches
@@ -5049,16 +5114,16 @@ int find_help_tags(const char_u *arg, int *num_matches, char_u ***matches, bool
}
} else {
// Recognize a few exceptions to the rule. Some strings that contain
- // '*' with "star". Otherwise '*' is recognized as a wildcard.
- for (i = (int)ARRAY_SIZE(mtable); --i >= 0;) {
- if (STRCMP(arg, mtable[i]) == 0) {
- STRCPY(d, rtable[i]);
+ // '*'are changed to "star", otherwise '*' is recognized as a wildcard.
+ for (i = 0; except_tbl[i][0] != NULL; i++) {
+ if (STRCMP(arg, except_tbl[i][0]) == 0) {
+ STRCPY(d, except_tbl[i][1]);
break;
}
}
}
- if (i < 0) { // no match in table
+ if (d[0] == NUL) { // no match in table
// Replace "\S" with "/\\S", etc. Otherwise every tag is matched.
// Also replace "\%^" and "\%(", they match every tag too.
// Also "\zs", "\z1", etc.
@@ -5066,10 +5131,9 @@ int find_help_tags(const char_u *arg, int *num_matches, char_u ***matches, bool
// And also "\_$" and "\_^".
if (arg[0] == '\\'
&& ((arg[1] != NUL && arg[2] == NUL)
- || (vim_strchr((char_u *)"%_z@", arg[1]) != NULL
+ || (vim_strchr("%_z@", arg[1]) != NULL
&& arg[2] != NUL))) {
- STRCPY(d, "/\\\\");
- STRCPY(d + 3, arg + 1);
+ vim_snprintf(d, IOSIZE, "/\\\\%s", arg + 1);
// Check for "/\\_$", should be "/\\_\$"
if (d[3] == '_' && d[4] == '$') {
STRCPY(d + 4, "\\$");
@@ -5089,14 +5153,14 @@ int find_help_tags(const char_u *arg, int *num_matches, char_u ***matches, bool
if (*arg == '(' && arg[1] == '\'') {
arg++;
}
- for (const char_u *s = arg; *s; s++) {
+ for (const char *s = arg; *s; s++) {
// Replace "|" with "bar" and '"' with "quote" to match the name of
// the tags for these commands.
// Replace "*" with ".*" and "?" with "." to match command line
// completion.
// Insert a backslash before '~', '$' and '.' to avoid their
// special meaning.
- if (d - IObuff > IOSIZE - 10) { // getting too long!?
+ if ((char_u *)d - IObuff > IOSIZE - 10) { // getting too long!?
break;
}
switch (*s) {
@@ -5126,17 +5190,16 @@ int find_help_tags(const char_u *arg, int *num_matches, char_u ***matches, bool
* ":help i_^_CTRL-D" work.
* Insert '-' before and after "CTRL-X" when applicable.
*/
- if (*s < ' ' || (*s == '^' && s[1] && (ASCII_ISALPHA(s[1])
- || vim_strchr((char_u *)
- "?@[\\]^",
- s[1]) != NULL))) {
- if (d > IObuff && d[-1] != '_' && d[-1] != '\\') {
+ if (*s < ' '
+ || (*s == '^' && s[1]
+ && (ASCII_ISALPHA(s[1]) || vim_strchr("?@[\\]^", s[1]) != NULL))) {
+ if ((char_u *)d > IObuff && d[-1] != '_' && d[-1] != '\\') {
*d++ = '_'; // prepend a '_' to make x_CTRL-x
}
STRCPY(d, "CTRL-");
d += 5;
if (*s < ' ') {
- *d++ = *s + '@';
+ *d++ = (char)(*s + '@');
if (d[-1] == '\\') {
*d++ = '\\'; // double a backslash
}
@@ -5188,15 +5251,15 @@ int find_help_tags(const char_u *arg, int *num_matches, char_u ***matches, bool
*d = NUL;
if (*IObuff == '`') {
- if (d > IObuff + 2 && d[-1] == '`') {
+ if ((char_u *)d > IObuff + 2 && d[-1] == '`') {
// remove the backticks from `command`
memmove(IObuff, IObuff + 1, STRLEN(IObuff));
d[-2] = NUL;
- } else if (d > IObuff + 3 && d[-2] == '`' && d[-1] == ',') {
+ } else if ((char_u *)d > IObuff + 3 && d[-2] == '`' && d[-1] == ',') {
// remove the backticks and comma from `command`,
memmove(IObuff, IObuff + 1, STRLEN(IObuff));
d[-3] = NUL;
- } else if (d > IObuff + 4 && d[-3] == '`'
+ } else if ((char_u *)d > IObuff + 4 && d[-3] == '`'
&& d[-2] == '\\' && d[-1] == '.') {
// remove the backticks and dot from `command`\.
memmove(IObuff, IObuff + 1, STRLEN(IObuff));
@@ -5212,7 +5275,7 @@ int find_help_tags(const char_u *arg, int *num_matches, char_u ***matches, bool
if (keep_lang) {
flags |= TAG_KEEP_LANG;
}
- if (find_tags(IObuff, num_matches, matches, flags, MAXCOL, NULL) == OK
+ if (find_tags(IObuff, num_matches, (char_u ***)matches, flags, MAXCOL, NULL) == OK
&& *num_matches > 0) {
// Sort the matches found on the heuristic number that is after the
// tag name.
@@ -5230,15 +5293,14 @@ int find_help_tags(const char_u *arg, int *num_matches, char_u ***matches, bool
static void prepare_help_buffer(void)
{
curbuf->b_help = true;
- set_string_option_direct("buftype", -1, (char_u *)"help",
- OPT_FREE|OPT_LOCAL, 0);
+ set_string_option_direct("buftype", -1, "help", OPT_FREE|OPT_LOCAL, 0);
// Always set these options after jumping to a help tag, because the
// user may have an autocommand that gets in the way.
// Accept all ASCII chars for keywords, except ' ', '*', '"', '|', and
// latin1 word characters (for translated help files).
// Only set it when needed, buf_init_chartab() is some work.
- char_u *p = (char_u *)"!-~,^*,^|,^\",192-255";
+ char *p = "!-~,^*,^|,^\",192-255";
if (STRCMP(curbuf->b_p_isk, p) != 0) {
set_string_option_direct("isk", -1, p, OPT_FREE|OPT_LOCAL, 0);
check_buf_options(curbuf);
@@ -5246,8 +5308,7 @@ static void prepare_help_buffer(void)
}
// Don't use the global foldmethod.
- set_string_option_direct("fdm", -1, (char_u *)"manual",
- OPT_FREE|OPT_LOCAL, 0);
+ set_string_option_direct("fdm", -1, "manual", OPT_FREE|OPT_LOCAL, 0);
curbuf->b_p_ts = 8; // 'tabstop' is 8.
curwin->w_p_list = FALSE; // No list mode.
@@ -5266,14 +5327,12 @@ static void prepare_help_buffer(void)
set_buflisted(FALSE);
}
-/*
- * After reading a help file: May cleanup a help buffer when syntax
- * highlighting is not used.
- */
+/// After reading a help file: May cleanup a help buffer when syntax
+/// highlighting is not used.
void fix_help_buffer(void)
{
linenr_T lnum;
- char_u *line;
+ char *line;
bool in_example = false;
// Set filetype to "help".
@@ -5285,13 +5344,13 @@ void fix_help_buffer(void)
if (!syntax_present(curwin)) {
for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; lnum++) {
- line = ml_get_buf(curbuf, lnum, false);
+ line = (char *)ml_get_buf(curbuf, lnum, false);
const size_t len = STRLEN(line);
if (in_example && len > 0 && !ascii_iswhite(line[0])) {
// End of example: non-white or '<' in first column.
if (line[0] == '<') {
// blank-out a '<' in the first column
- line = ml_get_buf(curbuf, lnum, true);
+ line = (char *)ml_get_buf(curbuf, lnum, true);
line[0] = ' ';
}
in_example = false;
@@ -5299,12 +5358,12 @@ void fix_help_buffer(void)
if (!in_example && len > 0) {
if (line[len - 1] == '>' && (len == 1 || line[len - 2] == ' ')) {
// blank-out a '>' in the last column (start of example)
- line = ml_get_buf(curbuf, lnum, true);
+ line = (char *)ml_get_buf(curbuf, lnum, true);
line[len - 1] = ' ';
in_example = true;
} else if (line[len - 1] == '~') {
// blank-out a '~' at the end of line (header marker)
- line = ml_get_buf(curbuf, lnum, true);
+ line = (char *)ml_get_buf(curbuf, lnum, true);
line[len - 1] = ' ';
}
}
@@ -5315,32 +5374,32 @@ void fix_help_buffer(void)
* In the "help.txt" and "help.abx" file, add the locally added help
* files. This uses the very first line in the help file.
*/
- char_u *const fname = path_tail(curbuf->b_fname);
- if (fnamecmp(fname, "help.txt") == 0
- || (fnamencmp(fname, "help.", 5) == 0
+ char *const fname = path_tail(curbuf->b_fname);
+ if (FNAMECMP(fname, "help.txt") == 0
+ || (FNAMENCMP(fname, "help.", 5) == 0
&& ASCII_ISALPHA(fname[5])
&& ASCII_ISALPHA(fname[6])
&& TOLOWER_ASC(fname[7]) == 'x'
&& fname[8] == NUL)) {
for (lnum = 1; lnum < curbuf->b_ml.ml_line_count; lnum++) {
- line = ml_get_buf(curbuf, lnum, false);
- if (strstr((char *)line, "*local-additions*") == NULL) {
+ line = (char *)ml_get_buf(curbuf, lnum, false);
+ if (strstr(line, "*local-additions*") == NULL) {
continue;
}
// Go through all directories in 'runtimepath', skipping
// $VIMRUNTIME.
- char_u *p = p_rtp;
+ char *p = (char *)p_rtp;
while (*p != NUL) {
- copy_option_part(&p, NameBuff, MAXPATHL, ",");
- char_u *const rt = (char_u *)vim_getenv("VIMRUNTIME");
+ copy_option_part(&p, (char *)NameBuff, MAXPATHL, ",");
+ char *const rt = vim_getenv("VIMRUNTIME");
if (rt != NULL
- && path_full_compare(rt, NameBuff, false, true) != kEqualFiles) {
+ && path_full_compare(rt, (char *)NameBuff, false, true) != kEqualFiles) {
int fcount;
- char_u **fnames;
- char_u *s;
+ char **fnames;
+ char *s;
vimconv_T vc;
- char_u *cp;
+ char *cp;
// Find all "doc/ *.txt" files in this directory.
if (!add_pathsep((char *)NameBuff)
@@ -5352,9 +5411,9 @@ void fix_help_buffer(void)
// Note: We cannot just do `&NameBuff` because it is a statically sized array
// so `NameBuff == &NameBuff` according to C semantics.
- char_u *buff_list[1] = { NameBuff };
- if (gen_expand_wildcards(1, buff_list, &fcount,
- &fnames, EW_FILE|EW_SILENT) == OK
+ char *buff_list[1] = { (char *)NameBuff };
+ if (gen_expand_wildcards(1, (char_u **)buff_list, &fcount,
+ (char_u ***)&fnames, EW_FILE|EW_SILENT) == OK
&& fcount > 0) {
// If foo.abx is found use it instead of foo.txt in
// the same directory.
@@ -5366,27 +5425,27 @@ void fix_help_buffer(void)
if (fnames[i1] == NULL || fnames[i2] == NULL) {
continue;
}
- const char_u *const f1 = fnames[i1];
- const char_u *const f2 = fnames[i2];
- const char_u *const t1 = path_tail(f1);
- const char_u *const t2 = path_tail(f2);
- const char_u *const e1 = STRRCHR(t1, '.');
- const char_u *const e2 = STRRCHR(t2, '.');
+ const char *const f1 = fnames[i1];
+ const char *const f2 = fnames[i2];
+ const char *const t1 = path_tail(f1);
+ const char *const t2 = path_tail(f2);
+ const char *const e1 = (char *)STRRCHR(t1, '.');
+ const char *const e2 = (char *)STRRCHR(t2, '.');
if (e1 == NULL || e2 == NULL) {
continue;
}
- if (fnamecmp(e1, ".txt") != 0
- && fnamecmp(e1, fname + 4) != 0) {
+ if (FNAMECMP(e1, ".txt") != 0
+ && FNAMECMP(e1, fname + 4) != 0) {
// Not .txt and not .abx, remove it.
XFREE_CLEAR(fnames[i1]);
continue;
}
if (e1 - f1 != e2 - f2
- || fnamencmp(f1, f2, e1 - f1) != 0) {
+ || FNAMENCMP(f1, f2, e1 - f1) != 0) {
continue;
}
- if (fnamecmp(e1, ".txt") == 0
- && fnamecmp(e2, fname + 4) == 0) {
+ if (FNAMECMP(e1, ".txt") == 0
+ && FNAMECMP(e2, fname + 4) == 0) {
// use .abx instead of .txt
XFREE_CLEAR(fnames[i1]);
}
@@ -5397,13 +5456,13 @@ void fix_help_buffer(void)
continue;
}
- FILE *const fd = os_fopen((char *)fnames[fi], "r");
+ FILE *const fd = os_fopen(fnames[fi], "r");
if (fd == NULL) {
continue;
}
vim_fgets(IObuff, IOSIZE, fd);
if (IObuff[0] == '*'
- && (s = vim_strchr(IObuff + 1, '*'))
+ && (s = vim_strchr((char *)IObuff + 1, '*'))
!= NULL) {
TriState this_utf = kNone;
// Change tag definition to a
@@ -5417,7 +5476,7 @@ void fix_help_buffer(void)
// The text is utf-8 when a byte
// above 127 is found and no
// illegal byte sequence is found.
- if (*s >= 0x80 && this_utf != kFalse) {
+ if ((char_u)(*s) >= 0x80 && this_utf != kFalse) {
this_utf = kTrue;
const int l = utf_ptr2len(s);
if (l == 1) {
@@ -5436,26 +5495,26 @@ void fix_help_buffer(void)
p_enc);
if (vc.vc_type == CONV_NONE) {
// No conversion needed.
- cp = IObuff;
+ cp = (char *)IObuff;
} else {
// Do the conversion. If it fails
// use the unconverted text.
- cp = string_convert(&vc, IObuff, NULL);
+ cp = (char *)string_convert(&vc, IObuff, NULL);
if (cp == NULL) {
- cp = IObuff;
+ cp = (char *)IObuff;
}
}
convert_setup(&vc, NULL, NULL);
ml_append(lnum, cp, (colnr_T)0, false);
- if (cp != IObuff) {
+ if ((char_u *)cp != IObuff) {
xfree(cp);
}
lnum++;
}
fclose(fd);
}
- FreeWild(fcount, fnames);
+ FreeWild(fcount, (char_u **)fnames);
}
}
xfree(rt);
@@ -5465,23 +5524,18 @@ void fix_help_buffer(void)
}
}
-/*
- * ":exusage"
- */
+/// ":exusage"
void ex_exusage(exarg_T *eap)
{
do_cmdline_cmd("help ex-cmd-index");
}
-/*
- * ":viusage"
- */
+/// ":viusage"
void ex_viusage(exarg_T *eap)
{
do_cmdline_cmd("help normal-index");
}
-
/// Generate tags in one help directory
///
/// @param dir Path to the doc directory
@@ -5490,15 +5544,15 @@ void ex_viusage(exarg_T *eap)
/// French)
/// @param add_help_tags Whether to add the "help-tags" tag
/// @param ignore_writeerr ignore write error
-static void helptags_one(char_u *dir, const char_u *ext, const char_u *tagfname, bool add_help_tags,
+static void helptags_one(char *dir, const char *ext, const char *tagfname, bool add_help_tags,
bool ignore_writeerr)
FUNC_ATTR_NONNULL_ALL
{
garray_T ga;
int filecount;
- char_u **files;
- char_u *p1, *p2;
- char_u *s;
+ char **files;
+ char *p1, *p2;
+ char *s;
TriState utf8 = kNone;
bool mix = false; // detected mixed encodings
@@ -5513,8 +5567,8 @@ static void helptags_one(char_u *dir, const char_u *ext, const char_u *tagfname,
// Note: We cannot just do `&NameBuff` because it is a statically sized array
// so `NameBuff == &NameBuff` according to C semantics.
- char_u *buff_list[1] = { NameBuff };
- if (gen_expand_wildcards(1, buff_list, &filecount, &files,
+ char *buff_list[1] = { (char *)NameBuff };
+ if (gen_expand_wildcards(1, (char_u **)buff_list, &filecount, (char_u ***)&files,
EW_FILE|EW_SILENT) == FAIL
|| filecount == 0) {
if (!got_int) {
@@ -5539,7 +5593,7 @@ static void helptags_one(char_u *dir, const char_u *ext, const char_u *tagfname,
if (!ignore_writeerr) {
semsg(_("E152: Cannot open %s for writing"), NameBuff);
}
- FreeWild(filecount, files);
+ FreeWild(filecount, (char_u **)files);
return;
}
@@ -5547,30 +5601,29 @@ static void helptags_one(char_u *dir, const char_u *ext, const char_u *tagfname,
// add the "help-tags" tag.
ga_init(&ga, (int)sizeof(char_u *), 100);
if (add_help_tags
- || path_full_compare((char_u *)"$VIMRUNTIME/doc",
- dir, false, true) == kEqualFiles) {
+ || path_full_compare("$VIMRUNTIME/doc", dir, false, true) == kEqualFiles) {
size_t s_len = 18 + STRLEN(tagfname);
s = xmalloc(s_len);
- snprintf((char *)s, s_len, "help-tags\t%s\t1\n", tagfname);
- GA_APPEND(char_u *, &ga, s);
+ snprintf(s, s_len, "help-tags\t%s\t1\n", tagfname);
+ GA_APPEND(char *, &ga, s);
}
// Go over all the files and extract the tags.
for (int fi = 0; fi < filecount && !got_int; fi++) {
- FILE *const fd = os_fopen((char *)files[fi], "r");
+ FILE *const fd = os_fopen(files[fi], "r");
if (fd == NULL) {
semsg(_("E153: Unable to open %s for reading"), files[fi]);
continue;
}
- const char_u *const fname = files[fi] + dirlen + 1;
+ const char *const fname = files[fi] + dirlen + 1;
bool firstline = true;
while (!vim_fgets(IObuff, IOSIZE, fd) && !got_int) {
if (firstline) {
// Detect utf-8 file by a non-ASCII char in the first line.
TriState this_utf8 = kNone;
- for (s = IObuff; *s != NUL; s++) {
- if (*s >= 0x80) {
+ for (s = (char *)IObuff; *s != NUL; s++) {
+ if ((char_u)(*s) >= 0x80) {
this_utf8 = kTrue;
const int l = utf_ptr2len(s);
if (l == 1) {
@@ -5594,9 +5647,9 @@ static void helptags_one(char_u *dir, const char_u *ext, const char_u *tagfname,
}
firstline = false;
}
- p1 = vim_strchr(IObuff, '*'); // find first '*'
+ p1 = vim_strchr((char *)IObuff, '*'); // find first '*'
while (p1 != NULL) {
- p2 = (char_u *)strchr((const char *)p1 + 1, '*'); // Find second '*'.
+ p2 = strchr((const char *)p1 + 1, '*'); // Find second '*'.
if (p2 != NULL && p2 > p1 + 1) { // Skip "*" and "**".
for (s = p1 + 1; s < p2; s++) {
if (*s == ' ' || *s == '\t' || *s == '|') {
@@ -5608,15 +5661,15 @@ static void helptags_one(char_u *dir, const char_u *ext, const char_u *tagfname,
// characters, there is white space before it and is
// followed by a white character or end-of-line.
if (s == p2
- && (p1 == IObuff || p1[-1] == ' ' || p1[-1] == '\t')
- && (vim_strchr((char_u *)" \t\n\r", s[1]) != NULL
+ && ((char_u *)p1 == IObuff || p1[-1] == ' ' || p1[-1] == '\t')
+ && (vim_strchr(" \t\n\r", s[1]) != NULL
|| s[1] == '\0')) {
*p2 = '\0';
p1++;
- size_t s_len= (p2 - p1) + STRLEN(fname) + 2;
+ size_t s_len= (size_t)(p2 - p1) + STRLEN(fname) + 2;
s = xmalloc(s_len);
- GA_APPEND(char_u *, &ga, s);
- snprintf((char *)s, s_len, "%s\t%s", p1, fname);
+ GA_APPEND(char *, &ga, s);
+ snprintf(s, s_len, "%s\t%s", p1, fname);
// find next '*'
p2 = vim_strchr(p2 + 1, '*');
@@ -5630,7 +5683,7 @@ static void helptags_one(char_u *dir, const char_u *ext, const char_u *tagfname,
fclose(fd);
}
- FreeWild(filecount, files);
+ FreeWild(filecount, (char_u **)files);
if (!got_int && ga.ga_data != NULL) {
// Sort the tags.
@@ -5638,8 +5691,8 @@ static void helptags_one(char_u *dir, const char_u *ext, const char_u *tagfname,
// Check for duplicates.
for (int i = 1; i < ga.ga_len; i++) {
- p1 = ((char_u **)ga.ga_data)[i - 1];
- p2 = ((char_u **)ga.ga_data)[i];
+ p1 = ((char **)ga.ga_data)[i - 1];
+ p2 = ((char **)ga.ga_data)[i];
while (*p1 == *p2) {
if (*p2 == '\t') {
*p2 = NUL;
@@ -5661,10 +5714,10 @@ static void helptags_one(char_u *dir, const char_u *ext, const char_u *tagfname,
// Write the tags into the file.
for (int i = 0; i < ga.ga_len; i++) {
- s = ((char_u **)ga.ga_data)[i];
+ s = ((char **)ga.ga_data)[i];
if (STRNCMP(s, "help-tags\t", 10) == 0) {
// help-tags entry was added in formatted form
- fputs((char *)s, fd_tags);
+ fputs(s, fd_tags);
} else {
fprintf(fd_tags, "%s\t/" "*", s);
for (p1 = s; *p1 != '\t'; p1++) {
@@ -5687,16 +5740,16 @@ static void helptags_one(char_u *dir, const char_u *ext, const char_u *tagfname,
}
/// Generate tags in one help directory, taking care of translations.
-static void do_helptags(char_u *dirname, bool add_help_tags, bool ignore_writeerr)
+static void do_helptags(char *dirname, bool add_help_tags, bool ignore_writeerr)
FUNC_ATTR_NONNULL_ALL
{
int len;
garray_T ga;
- char_u lang[2];
- char_u ext[5];
- char_u fname[8];
+ char lang[2];
+ char ext[5];
+ char fname[8];
int filecount;
- char_u **files;
+ char **files;
// Get a list of all files in the help directory and in subdirectories.
STRLCPY(NameBuff, dirname, sizeof(NameBuff));
@@ -5708,8 +5761,8 @@ static void do_helptags(char_u *dirname, bool add_help_tags, bool ignore_writeer
// Note: We cannot just do `&NameBuff` because it is a statically sized array
// so `NameBuff == &NameBuff` according to C semantics.
- char_u *buff_list[1] = { NameBuff };
- if (gen_expand_wildcards(1, buff_list, &filecount, &files,
+ char *buff_list[1] = { (char *)NameBuff };
+ if (gen_expand_wildcards(1, (char_u **)buff_list, &filecount, (char_u ***)&files,
EW_FILE|EW_SILENT) == FAIL
|| filecount == 0) {
semsg(_("E151: No match: %s"), NameBuff);
@@ -5734,8 +5787,8 @@ static void do_helptags(char_u *dirname, bool add_help_tags, bool ignore_writeer
&& ASCII_ISALPHA(files[i][len - 2])
&& TOLOWER_ASC(files[i][len - 1]) == 'x') {
// ".abx" -> language "ab"
- lang[0] = TOLOWER_ASC(files[i][len - 3]);
- lang[1] = TOLOWER_ASC(files[i][len - 2]);
+ lang[0] = (char)TOLOWER_ASC(files[i][len - 3]);
+ lang[1] = (char)TOLOWER_ASC(files[i][len - 2]);
} else {
continue;
}
@@ -5749,8 +5802,8 @@ static void do_helptags(char_u *dirname, bool add_help_tags, bool ignore_writeer
if (j == ga.ga_len) {
// New language, add it.
ga_grow(&ga, 2);
- ((char_u *)ga.ga_data)[ga.ga_len++] = lang[0];
- ((char_u *)ga.ga_data)[ga.ga_len++] = lang[1];
+ ((char *)ga.ga_data)[ga.ga_len++] = lang[0];
+ ((char *)ga.ga_data)[ga.ga_len++] = lang[1];
}
}
@@ -5759,8 +5812,8 @@ static void do_helptags(char_u *dirname, bool add_help_tags, bool ignore_writeer
*/
for (j = 0; j < ga.ga_len; j += 2) {
STRCPY(fname, "tags-xx");
- fname[5] = ((char_u *)ga.ga_data)[j];
- fname[6] = ((char_u *)ga.ga_data)[j + 1];
+ fname[5] = ((char *)ga.ga_data)[j];
+ fname[6] = ((char *)ga.ga_data)[j + 1];
if (fname[5] == 'e' && fname[6] == 'n') {
// English is an exception: use ".txt" and "tags".
fname[4] = NUL;
@@ -5771,26 +5824,24 @@ static void do_helptags(char_u *dirname, bool add_help_tags, bool ignore_writeer
ext[1] = fname[5];
ext[2] = fname[6];
}
- helptags_one(dirname, ext, fname, add_help_tags, ignore_writeerr);
+ helptags_one(dirname, (char *)ext, (char *)fname, add_help_tags, ignore_writeerr);
}
ga_clear(&ga);
- FreeWild(filecount, files);
+ FreeWild(filecount, (char_u **)files);
}
-static void helptags_cb(char_u *fname, void *cookie)
+static void helptags_cb(char *fname, void *cookie)
FUNC_ATTR_NONNULL_ALL
{
do_helptags(fname, *(bool *)cookie, true);
}
-/*
- * ":helptags"
- */
+/// ":helptags"
void ex_helptags(exarg_T *eap)
{
expand_T xpc;
- char_u *dirname;
+ char *dirname;
bool add_help_tags = false;
// Check for ":helptags ++t {dir}".
@@ -5804,9 +5855,9 @@ void ex_helptags(exarg_T *eap)
} else {
ExpandInit(&xpc);
xpc.xp_context = EXPAND_DIRECTORIES;
- dirname = ExpandOne(&xpc, eap->arg, NULL,
- WILD_LIST_NOTFOUND|WILD_SILENT, WILD_EXPAND_FREE);
- if (dirname == NULL || !os_isdir(dirname)) {
+ dirname = (char *)ExpandOne(&xpc, (char_u *)eap->arg, NULL,
+ WILD_LIST_NOTFOUND|WILD_SILENT, WILD_EXPAND_FREE);
+ if (dirname == NULL || !os_isdir((char_u *)dirname)) {
semsg(_("E150: Not a directory: %s"), eap->arg);
} else {
do_helptags(dirname, add_help_tags, false);
@@ -5815,65 +5866,36 @@ void ex_helptags(exarg_T *eap)
}
}
-/*
- * ":helpclose": Close one help window
- */
+/// ":helpclose": Close one help window
void ex_helpclose(exarg_T *eap)
{
FOR_ALL_WINDOWS_IN_TAB(win, curtab) {
if (bt_help(win->w_buffer)) {
- win_close(win, false);
+ win_close(win, false, eap->forceit);
return;
}
}
}
-/// Tries to enter to an existing window of given buffer. If no existing buffer
-/// is found, creates a new split.
-///
-/// Returns OK/FAIL.
-int sub_preview_win(buf_T *preview_buf)
-{
- if (preview_buf != NULL) {
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_buffer == preview_buf) {
- win_enter(wp, false);
-
- return OK;
- }
- }
- }
- return win_split((int)p_cwh, WSP_BOT);
-}
-
/// Shows the effects of the :substitute command being typed ('inccommand').
/// If inccommand=split, shows a preview window and later restores the layout.
-static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines, int hl_id,
- int src_id, handle_T bufnr)
+///
+/// @return 1 if preview window isn't needed, 2 if preview window is needed.
+static int show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines, int hl_id,
+ long cmdpreview_ns, handle_T cmdpreview_bufnr)
FUNC_ATTR_NONNULL_ALL
{
- win_T *save_curwin = curwin;
- cmdmod_T save_cmdmod = cmdmod;
- char_u *save_shm_p = vim_strsave(p_shm);
+ char *save_shm_p = (char *)vim_strsave(p_shm);
PreviewLines lines = *preview_lines;
buf_T *orig_buf = curbuf;
-
// We keep a special-purpose buffer around, but don't assume it exists.
- buf_T *preview_buf = bufnr ? buflist_findnr(bufnr) : 0;
- cmdmod.split = 0; // disable :leftabove/botright modifiers
- cmdmod.tab = 0; // disable :tab modifier
- cmdmod.noswapfile = true; // disable swap for preview buffer
+ buf_T *cmdpreview_buf = NULL;
+
// disable file info message
- set_string_option_direct("shm", -1, (char_u *)"F", OPT_FREE,
- SID_NONE);
+ set_string_option_direct("shm", -1, "F", OPT_FREE, SID_NONE);
- bool outside_curline = (eap->line1 != old_cusr.lnum
- || eap->line2 != old_cusr.lnum);
- bool preview = outside_curline && (*p_icm != 'n');
- if (preview_buf == curbuf) { // Preview buffer cannot preview itself!
- preview = false;
- preview_buf = NULL;
- }
+ // Update the topline to ensure that main window is on the correct line
+ update_topline(curwin);
// Place cursor on nearest matching line, to undo do_sub() cursor placement.
for (size_t i = 0; i < lines.subresults.size; i++) {
@@ -5888,27 +5910,17 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines
// Width of the "| lnum|..." column which displays the line numbers.
linenr_T highest_num_line = 0;
int col_width = 0;
+ // Use preview window only when inccommand=split and range is not just the current line
+ bool preview = (*p_icm == 's') && (eap->line1 != old_cusr.lnum || eap->line2 != old_cusr.lnum);
- if (preview && sub_preview_win(preview_buf) != FAIL) {
- buf_open_scratch(preview_buf ? bufnr : 0, "[Preview]");
- buf_clear();
- preview_buf = curbuf;
- curbuf->b_p_bl = false;
- curbuf->b_p_ma = true;
- curbuf->b_p_ul = -1;
- curbuf->b_p_tw = 0; // Reset 'textwidth' (was set by ftplugin)
- curwin->w_p_cul = false;
- curwin->w_p_cuc = false;
- curwin->w_p_spell = false;
- curwin->w_p_fen = false;
+ if (preview) {
+ cmdpreview_buf = buflist_findnr(cmdpreview_bufnr);
+ assert(cmdpreview_buf != NULL);
if (lines.subresults.size > 0) {
highest_num_line = kv_last(lines.subresults).end.lnum;
- col_width = log10(highest_num_line) + 1 + 3;
+ col_width = (int)log10(highest_num_line) + 1 + 3;
}
- } else {
- // Failed to split the window, don't show 'inccommand' preview.
- preview_buf = NULL;
}
char *str = NULL; // construct the line to show in here
@@ -5918,10 +5930,13 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines
linenr_T linenr_origbuf = 0; // last line added to original buffer
linenr_T next_linenr = 0; // next line to show for the match
+ // Temporarily switch to preview buffer
+ aco_save_T aco;
+
for (size_t matchidx = 0; matchidx < lines.subresults.size; matchidx++) {
SubResult match = lines.subresults.items[matchidx];
- if (preview_buf) {
+ if (cmdpreview_buf) {
lpos_T p_start = { 0, match.start.col }; // match starts here in preview
lpos_T p_end = { 0, match.end.col }; // ... and ends here
@@ -5949,7 +5964,7 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines
line = "";
} else {
line = (char *)ml_get_buf(orig_buf, next_linenr, false);
- line_size = strlen(line) + col_width + 1;
+ line_size = strlen(line) + (size_t)col_width + 1;
// Reallocate if line not long enough
if (line_size > old_line_size) {
@@ -5958,125 +5973,61 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines
}
}
// Put "|lnum| line" into `str` and append it to the preview buffer.
- snprintf(str, line_size, "|%*ld| %s", col_width - 3,
+ snprintf(str, line_size, "|%*" PRIdLINENR "| %s", col_width - 3,
next_linenr, line);
+ // Temporarily switch to preview buffer
+ aucmd_prepbuf(&aco, cmdpreview_buf);
if (linenr_preview == 0) {
- ml_replace(1, (char_u *)str, true);
+ ml_replace(1, str, true);
} else {
- ml_append(linenr_preview, (char_u *)str, (colnr_T)line_size, false);
+ ml_append(linenr_preview, str, (colnr_T)line_size, false);
}
+ aucmd_restbuf(&aco);
linenr_preview += 1;
}
linenr_origbuf = match.end.lnum;
- bufhl_add_hl_pos_offset(preview_buf, src_id, hl_id, p_start,
- p_end, col_width);
+ bufhl_add_hl_pos_offset(cmdpreview_buf, (int)cmdpreview_ns, hl_id, p_start, p_end, col_width);
}
- bufhl_add_hl_pos_offset(orig_buf, src_id, hl_id, match.start,
- match.end, 0);
+ bufhl_add_hl_pos_offset(orig_buf, (int)cmdpreview_ns, hl_id, match.start, match.end, 0);
}
- xfree(str);
- redraw_later(curwin, SOME_VALID);
- win_enter(save_curwin, false); // Return to original window
- update_topline(curwin);
-
- // Update screen now.
- int save_rd = RedrawingDisabled;
- RedrawingDisabled = 0;
- update_screen(SOME_VALID);
- RedrawingDisabled = save_rd;
+ xfree(str);
set_string_option_direct("shm", -1, save_shm_p, OPT_FREE, SID_NONE);
xfree(save_shm_p);
- cmdmod = save_cmdmod;
-
- return preview_buf;
+ return preview ? 2 : 1;
}
-/// Closes any open windows for inccommand preview buffer.
-void close_preview_windows(void)
+/// :substitute command.
+void ex_substitute(exarg_T *eap)
{
- block_autocmds();
- buf_T *buf = preview_bufnr ? buflist_findnr(preview_bufnr) : NULL;
- if (buf != NULL) {
- close_windows(buf, false);
- }
- unblock_autocmds();
+ (void)do_sub(eap, profile_zero(), 0, 0);
}
-/// :substitute command
-///
-/// If 'inccommand' is empty: calls do_sub().
-/// If 'inccommand' is set: shows a "live" preview then removes the changes.
-/// from undo history.
-void ex_substitute(exarg_T *eap)
+/// :substitute command preview callback.
+int ex_substitute_preview(exarg_T *eap, long cmdpreview_ns, handle_T cmdpreview_bufnr)
{
- bool preview = (State & CMDPREVIEW);
- if (*p_icm == NUL || !preview) { // 'inccommand' is disabled
- close_preview_windows();
- (void)do_sub(eap, profile_zero(), true, preview_bufnr);
-
- return;
+ // Only preview once the pattern delimiter has been typed
+ if (*eap->arg && !ASCII_ISALNUM(*eap->arg)) {
+ char *save_eap = eap->arg;
+ int retv = do_sub(eap, profile_setlimit(p_rdt), cmdpreview_ns, cmdpreview_bufnr);
+ eap->arg = save_eap;
+ return retv;
}
- block_autocmds(); // Disable events during command preview.
-
- char_u *save_eap = eap->arg;
- garray_T save_view;
- win_size_save(&save_view); // Save current window sizes.
- save_search_patterns();
- int save_changedtick = buf_get_changedtick(curbuf);
- time_t save_b_u_time_cur = curbuf->b_u_time_cur;
- u_header_T *save_b_u_newhead = curbuf->b_u_newhead;
- long save_b_p_ul = curbuf->b_p_ul;
- int save_w_p_cul = curwin->w_p_cul;
- int save_w_p_cuc = curwin->w_p_cuc;
-
- curbuf->b_p_ul = LONG_MAX; // make sure we can undo all changes
- curwin->w_p_cul = false; // Disable 'cursorline'
- curwin->w_p_cuc = false; // Disable 'cursorcolumn'
-
- // Don't show search highlighting during live substitution
- bool save_hls = p_hls;
- p_hls = false;
- buf_T *preview_buf = do_sub(eap, profile_setlimit(p_rdt), false,
- preview_bufnr);
- p_hls = save_hls;
-
- if (preview_buf != NULL) {
- preview_bufnr = preview_buf->handle;
- }
-
- if (save_changedtick != buf_get_changedtick(curbuf)) {
- // Undo invisibly. This also moves the cursor!
- if (!u_undo_and_forget(1)) {
- abort();
- }
- // Restore newhead. It is meaningless when curhead is valid, but we must
- // restore it so that undotree() is identical before/after the preview.
- curbuf->b_u_newhead = save_b_u_newhead;
- curbuf->b_u_time_cur = save_b_u_time_cur;
- buf_set_changedtick(curbuf, save_changedtick);
- }
-
- curbuf->b_p_ul = save_b_p_ul;
- curwin->w_p_cul = save_w_p_cul; // Restore 'cursorline'
- curwin->w_p_cuc = save_w_p_cuc; // Restore 'cursorcolumn'
- eap->arg = save_eap;
- restore_search_patterns();
- win_size_restore(&save_view);
- ga_clear(&save_view);
- unblock_autocmds();
+ return 0;
}
/// Skip over the pattern argument of ":vimgrep /pat/[g][j]".
/// Put the start of the pattern in "*s", unless "s" is NULL.
-/// If "flags" is not NULL put the flags in it: VGR_GLOBAL, VGR_NOJUMP.
-/// If "s" is not NULL terminate the pattern with a NUL.
-/// Return a pointer to the char just past the pattern plus flags.
-char_u *skip_vimgrep_pat(char_u *p, char_u **s, int *flags)
+///
+/// @param flags if not NULL, put the flags in it: VGR_GLOBAL, VGR_NOJUMP.
+/// @param s if not NULL, terminate the pattern with a NUL.
+///
+/// @return a pointer to the char just past the pattern plus flags.
+char *skip_vimgrep_pat(char *p, char **s, int *flags)
{
int c;
@@ -6085,7 +6036,7 @@ char_u *skip_vimgrep_pat(char_u *p, char_u **s, int *flags)
if (s != NULL) {
*s = p;
}
- p = skiptowhite(p);
+ p = (char *)skiptowhite((char_u *)p);
if (s != NULL && *p != NUL) {
*p++ = NUL;
}
@@ -6094,8 +6045,8 @@ char_u *skip_vimgrep_pat(char_u *p, char_u **s, int *flags)
if (s != NULL) {
*s = p + 1;
}
- c = *p;
- p = skip_regexp(p + 1, c, true, NULL);
+ c = (char_u)(*p);
+ p = (char *)skip_regexp((char_u *)p + 1, c, true, NULL);
if (*p != c) {
return NULL;
}
@@ -6107,12 +6058,14 @@ char_u *skip_vimgrep_pat(char_u *p, char_u **s, int *flags)
p++;
// Find the flags
- while (*p == 'g' || *p == 'j') {
+ while (*p == 'g' || *p == 'j' || *p == 'f') {
if (flags != NULL) {
if (*p == 'g') {
*flags |= VGR_GLOBAL;
- } else {
+ } else if (*p == 'j') {
*flags |= VGR_NOJUMP;
+ } else {
+ *flags |= VGR_FUZZY;
}
}
p++;
@@ -6141,7 +6094,7 @@ void ex_oldfiles(exarg_T *eap)
if (!message_filtered((char_u *)fname)) {
msg_outnum(nr);
msg_puts(": ");
- msg_outtrans((char_u *)tv_get_string(TV_LIST_ITEM_TV(li)));
+ msg_outtrans((char *)tv_get_string(TV_LIST_ITEM_TV(li)));
msg_clr_eos();
msg_putchar('\n');
ui_flush(); // output one line at a time
@@ -6153,19 +6106,19 @@ void ex_oldfiles(exarg_T *eap)
got_int = false;
// File selection prompt on ":browse oldfiles"
- if (cmdmod.browse) {
+ if (cmdmod.cmod_flags & CMOD_BROWSE) {
quit_more = false;
nr = prompt_for_number(false);
msg_starthere();
if (nr > 0 && nr <= tv_list_len(l)) {
- const char *const p = tv_list_find_str(l, nr - 1);
+ const char *const p = tv_list_find_str(l, (int)nr - 1);
if (p == NULL) {
return;
}
- char *const s = (char *)expand_env_save((char_u *)p);
- eap->arg = (char_u *)s;
+ char *const s = expand_env_save((char *)p);
+ eap->arg = s;
eap->cmdidx = CMD_edit;
- cmdmod.browse = false;
+ cmdmod.cmod_flags &= ~CMOD_BROWSE;
do_exedit(eap, NULL);
xfree(s);
}
diff --git a/src/nvim/ex_cmds.h b/src/nvim/ex_cmds.h
index 241674c24c..a55e74a789 100644
--- a/src/nvim/ex_cmds.h
+++ b/src/nvim/ex_cmds.h
@@ -21,7 +21,7 @@
// for lnum argument in do_ecmd()
#define ECMD_LASTL (linenr_T)0 // use last position in loaded file
-#define ECMD_LAST (linenr_T)-1 // use last position in all files
+#define ECMD_LAST ((linenr_T) - 1) // use last position in all files
#define ECMD_ONE (linenr_T)1 // use first line
/// Previous :substitute replacement string definition
diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua
index c388373ac1..a5ba5e0b30 100644
--- a/src/nvim/ex_cmds.lua
+++ b/src/nvim/ex_cmds.lua
@@ -4,28 +4,30 @@ local module = {}
-- Description of the values below is contained in ex_cmds_defs.h file.
-- "EX_" prefix is omitted.
-local RANGE = 0x001
-local BANG = 0x002
-local EXTRA = 0x004
-local XFILE = 0x008
-local NOSPC = 0x010
-local DFLALL = 0x020
-local WHOLEFOLD = 0x040
-local NEEDARG = 0x080
-local TRLBAR = 0x100
-local REGSTR = 0x200
-local COUNT = 0x400
-local NOTRLCOM = 0x800
-local ZEROR = 0x1000
-local CTRLV = 0x2000
-local CMDARG = 0x4000
-local BUFNAME = 0x8000
-local BUFUNL = 0x10000
-local ARGOPT = 0x20000
-local SBOXOK = 0x40000
-local CMDWIN = 0x80000
-local MODIFY = 0x100000
-local FLAGS = 0x200000
+local RANGE = 0x001
+local BANG = 0x002
+local EXTRA = 0x004
+local XFILE = 0x008
+local NOSPC = 0x010
+local DFLALL = 0x020
+local WHOLEFOLD = 0x040
+local NEEDARG = 0x080
+local TRLBAR = 0x100
+local REGSTR = 0x200
+local COUNT = 0x400
+local NOTRLCOM = 0x800
+local ZEROR = 0x1000
+local CTRLV = 0x2000
+local CMDARG = 0x4000
+local BUFNAME = 0x8000
+local BUFUNL = 0x10000
+local ARGOPT = 0x20000
+local SBOXOK = 0x40000
+local CMDWIN = 0x80000
+local MODIFY = 0x100000
+local FLAGS = 0x200000
+local LOCK_OK = 0x1000000
+local PREVIEW = 0x8000000
local FILES = bit.bor(XFILE, EXTRA)
local WORD1 = bit.bor(EXTRA, NOSPC)
local FILE1 = bit.bor(FILES, NOSPC)
@@ -33,25 +35,26 @@ local FILE1 = bit.bor(FILES, NOSPC)
module.flags = {
RANGE = RANGE,
DFLALL = DFLALL,
+ PREVIEW = PREVIEW
}
-- The following table is described in ex_cmds_defs.h file.
module.cmds = {
{
command='append',
- flags=bit.bor(BANG, RANGE, ZEROR, TRLBAR, CMDWIN, MODIFY),
+ flags=bit.bor(BANG, RANGE, ZEROR, TRLBAR, CMDWIN, LOCK_OK, MODIFY),
addr_type='ADDR_LINES',
func='ex_append',
},
{
command='abbreviate',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_abbreviate',
},
{
command='abclear',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_abclear',
},
@@ -69,13 +72,13 @@ module.cmds = {
},
{
command='amenu',
- flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
{
command='anoremenu',
- flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
@@ -129,25 +132,25 @@ module.cmds = {
},
{
command='ascii',
- flags=bit.bor(TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='do_ascii',
},
{
command='autocmd',
- flags=bit.bor(BANG, EXTRA, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(BANG, EXTRA, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_autocmd',
},
{
command='augroup',
- flags=bit.bor(BANG, WORD1, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, WORD1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_autocmd',
},
{
command='aunmenu',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_menu',
},
@@ -171,13 +174,13 @@ module.cmds = {
},
{
command='badd',
- flags=bit.bor(NEEDARG, FILE1, CMDARG, TRLBAR, CMDWIN),
+ flags=bit.bor(NEEDARG, FILE1, CMDARG, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_edit',
},
{
command='balt',
- flags=bit.bor(NEEDARG, FILE1, CMDARG, TRLBAR, CMDWIN),
+ flags=bit.bor(NEEDARG, FILE1, CMDARG, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_edit',
},
@@ -189,7 +192,7 @@ module.cmds = {
},
{
command='behave',
- flags=bit.bor(NEEDARG, WORD1, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, NEEDARG, WORD1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_behave',
},
@@ -243,37 +246,37 @@ module.cmds = {
},
{
command='break',
- flags=bit.bor(TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_break',
},
{
command='breakadd',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_breakadd',
},
{
command='breakdel',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_breakdel',
},
{
command='breaklist',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_breaklist',
},
{
command='browse',
- flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, CMDWIN),
+ flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_wrongmodifier',
},
{
command='buffers',
- flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='buflist_list',
},
@@ -297,7 +300,7 @@ module.cmds = {
},
{
command='change',
- flags=bit.bor(BANG, WHOLEFOLD, RANGE, COUNT, TRLBAR, CMDWIN, MODIFY),
+ flags=bit.bor(BANG, WHOLEFOLD, RANGE, COUNT, TRLBAR, CMDWIN, LOCK_OK, MODIFY),
addr_type='ADDR_LINES',
func='ex_change',
},
@@ -315,13 +318,13 @@ module.cmds = {
},
{
command='cabbrev',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_abbreviate',
},
{
command='cabclear',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_abclear',
},
@@ -357,13 +360,13 @@ module.cmds = {
},
{
command='call',
- flags=bit.bor(RANGE, NEEDARG, EXTRA, NOTRLCOM, SBOXOK, CMDWIN),
+ flags=bit.bor(RANGE, NEEDARG, EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_call',
},
{
command='catch',
- flags=bit.bor(EXTRA, SBOXOK, CMDWIN),
+ flags=bit.bor(EXTRA, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_catch',
},
@@ -405,7 +408,7 @@ module.cmds = {
},
{
command='cd',
- flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_cd',
},
@@ -417,7 +420,7 @@ module.cmds = {
},
{
command='center',
- flags=bit.bor(TRLBAR, RANGE, WHOLEFOLD, EXTRA, CMDWIN, MODIFY),
+ flags=bit.bor(TRLBAR, RANGE, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK, MODIFY),
addr_type='ADDR_LINES',
func='ex_align',
},
@@ -467,13 +470,13 @@ module.cmds = {
},
{
command='chdir',
- flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_cd',
},
{
command='changes',
- flags=bit.bor(TRLBAR, CMDWIN),
+ flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_changes',
},
@@ -485,7 +488,7 @@ module.cmds = {
},
{
command='checkpath',
- flags=bit.bor(TRLBAR, BANG, CMDWIN),
+ flags=bit.bor(TRLBAR, BANG, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_checkpath',
},
@@ -503,7 +506,7 @@ module.cmds = {
},
{
command='clist',
- flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='qf_list',
},
@@ -515,31 +518,31 @@ module.cmds = {
},
{
command='close',
- flags=bit.bor(BANG, RANGE, COUNT, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, RANGE, COUNT, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_WINDOWS',
func='ex_close',
},
{
command='clearjumps',
- flags=bit.bor(TRLBAR, CMDWIN),
+ flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_clearjumps',
},
{
command='cmap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
{
command='cmapclear',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_mapclear',
},
{
command='cmenu',
- flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
@@ -563,25 +566,25 @@ module.cmds = {
},
{
command='cnoremap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
{
command='cnoreabbrev',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_abbreviate',
},
{
command='cnoremenu',
- flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
{
command='copy',
- flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, TRLBAR, CMDWIN, MODIFY),
+ flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, TRLBAR, CMDWIN, LOCK_OK, MODIFY),
addr_type='ADDR_LINES',
func='ex_copymove',
},
@@ -593,43 +596,43 @@ module.cmds = {
},
{
command='colorscheme',
- flags=bit.bor(WORD1, TRLBAR, CMDWIN),
+ flags=bit.bor(WORD1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_colorscheme',
},
{
command='command',
- flags=bit.bor(EXTRA, BANG, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, BANG, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_command',
},
{
command='comclear',
- flags=bit.bor(TRLBAR, CMDWIN),
+ flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_comclear',
},
{
command='compiler',
- flags=bit.bor(BANG, TRLBAR, WORD1, CMDWIN),
+ flags=bit.bor(BANG, TRLBAR, WORD1, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_compiler',
},
{
command='continue',
- flags=bit.bor(TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_continue',
},
{
command='confirm',
- flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, CMDWIN),
+ flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_wrongmodifier',
},
{
command='const',
- flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, CMDWIN),
+ flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_const',
},
@@ -677,19 +680,19 @@ module.cmds = {
},
{
command='cunmap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_unmap',
},
{
command='cunabbrev',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_abbreviate',
},
{
command='cunmenu',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_menu',
},
@@ -701,43 +704,43 @@ module.cmds = {
},
{
command='delete',
- flags=bit.bor(RANGE, WHOLEFOLD, REGSTR, COUNT, TRLBAR, CMDWIN, MODIFY),
+ flags=bit.bor(RANGE, WHOLEFOLD, REGSTR, COUNT, TRLBAR, CMDWIN, LOCK_OK, MODIFY),
addr_type='ADDR_LINES',
func='ex_operators',
},
{
command='delmarks',
- flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_delmarks',
},
{
command='debug',
- flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, SBOXOK, CMDWIN),
+ flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_debug',
},
{
command='debuggreedy',
- flags=bit.bor(RANGE, ZEROR, TRLBAR, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_debuggreedy',
},
{
command='delcommand',
- flags=bit.bor(BANG, NEEDARG, WORD1, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, NEEDARG, WORD1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_delcommand',
},
{
command='delfunction',
- flags=bit.bor(BANG, NEEDARG, WORD1, CMDWIN),
+ flags=bit.bor(BANG, NEEDARG, WORD1, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_delfunction',
},
{
command='display',
- flags=bit.bor(EXTRA, NOTRLCOM, TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(EXTRA, NOTRLCOM, TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_display',
},
@@ -785,7 +788,7 @@ module.cmds = {
},
{
command='digraphs',
- flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_digraphs',
},
@@ -797,19 +800,19 @@ module.cmds = {
},
{
command='dlist',
- flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA, CMDWIN),
+ flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_findpat',
},
{
command='doautocmd',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_doautocmd',
},
{
command='doautoall',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_doautoall',
},
@@ -821,7 +824,7 @@ module.cmds = {
},
{
command='dsearch',
- flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA, CMDWIN),
+ flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_findpat',
},
@@ -839,85 +842,85 @@ module.cmds = {
},
{
command='earlier',
- flags=bit.bor(TRLBAR, EXTRA, NOSPC, CMDWIN),
+ flags=bit.bor(TRLBAR, EXTRA, NOSPC, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_later',
},
{
command='echo',
- flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN),
+ flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_echo',
},
{
command='echoerr',
- flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN),
+ flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_execute',
},
{
command='echohl',
- flags=bit.bor(EXTRA, TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_echohl',
},
{
command='echomsg',
- flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN),
+ flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_execute',
},
{
command='echon',
- flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN),
+ flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_echo',
},
{
command='else',
- flags=bit.bor(TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_else',
},
{
command='elseif',
- flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN),
+ flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_else',
},
{
command='emenu',
- flags=bit.bor(NEEDARG, EXTRA, TRLBAR, NOTRLCOM, RANGE, CMDWIN),
+ flags=bit.bor(NEEDARG, EXTRA, TRLBAR, NOTRLCOM, RANGE, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_emenu',
},
{
command='endif',
- flags=bit.bor(TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_endif',
},
{
command='endfunction',
- flags=bit.bor(TRLBAR, CMDWIN),
+ flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_endfunction',
},
{
command='endfor',
- flags=bit.bor(TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_endwhile',
},
{
command='endtry',
- flags=bit.bor(TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_endtry',
},
{
command='endwhile',
- flags=bit.bor(TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_endwhile',
},
@@ -929,7 +932,7 @@ module.cmds = {
},
{
command='eval',
- flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN),
+ flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_eval',
},
@@ -941,13 +944,13 @@ module.cmds = {
},
{
command='execute',
- flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN),
+ flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_execute',
},
{
command='exit',
- flags=bit.bor(RANGE, WHOLEFOLD, BANG, FILE1, ARGOPT, DFLALL, TRLBAR, CMDWIN),
+ flags=bit.bor(RANGE, WHOLEFOLD, BANG, FILE1, ARGOPT, DFLALL, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_exit',
},
@@ -965,13 +968,13 @@ module.cmds = {
},
{
command='files',
- flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='buflist_list',
},
{
command='filetype',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_filetype',
},
@@ -989,13 +992,13 @@ module.cmds = {
},
{
command='finally',
- flags=bit.bor(TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_finally',
},
{
command='finish',
- flags=bit.bor(TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_finish',
},
@@ -1007,13 +1010,13 @@ module.cmds = {
},
{
command='fold',
- flags=bit.bor(RANGE, WHOLEFOLD, TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(RANGE, WHOLEFOLD, TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_fold',
},
{
command='foldclose',
- flags=bit.bor(RANGE, BANG, WHOLEFOLD, TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(RANGE, BANG, WHOLEFOLD, TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_foldopen',
},
@@ -1031,31 +1034,31 @@ module.cmds = {
},
{
command='foldopen',
- flags=bit.bor(RANGE, BANG, WHOLEFOLD, TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(RANGE, BANG, WHOLEFOLD, TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_foldopen',
},
{
command='for',
- flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN),
+ flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_while',
},
{
command='function',
- flags=bit.bor(EXTRA, BANG, SBOXOK, CMDWIN),
+ flags=bit.bor(EXTRA, BANG, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_function',
},
{
command='global',
- flags=bit.bor(RANGE, WHOLEFOLD, BANG, EXTRA, DFLALL, SBOXOK, CMDWIN),
+ flags=bit.bor(RANGE, WHOLEFOLD, BANG, EXTRA, DFLALL, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_global',
},
{
command='goto',
- flags=bit.bor(RANGE, COUNT, TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(RANGE, COUNT, TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_goto',
},
@@ -1073,13 +1076,13 @@ module.cmds = {
},
{
command='gui',
- flags=bit.bor(BANG, FILES, CMDARG, ARGOPT, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, FILES, CMDARG, ARGOPT, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_nogui',
},
{
command='gvim',
- flags=bit.bor(BANG, FILES, CMDARG, ARGOPT, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, FILES, CMDARG, ARGOPT, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_nogui',
},
@@ -1103,7 +1106,7 @@ module.cmds = {
},
{
command='helptags',
- flags=bit.bor(NEEDARG, FILES, TRLBAR, CMDWIN),
+ flags=bit.bor(NEEDARG, FILES, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_helptags',
},
@@ -1115,7 +1118,7 @@ module.cmds = {
},
{
command='highlight',
- flags=bit.bor(BANG, EXTRA, TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(BANG, EXTRA, TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_highlight',
},
@@ -1127,31 +1130,31 @@ module.cmds = {
},
{
command='history',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_history',
},
{
command='insert',
- flags=bit.bor(BANG, RANGE, TRLBAR, CMDWIN, MODIFY),
+ flags=bit.bor(BANG, RANGE, TRLBAR, CMDWIN, LOCK_OK, MODIFY),
addr_type='ADDR_LINES',
func='ex_append',
},
{
command='iabbrev',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_abbreviate',
},
{
command='iabclear',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_abclear',
},
{
command='if',
- flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN),
+ flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_if',
},
@@ -1163,55 +1166,55 @@ module.cmds = {
},
{
command='ilist',
- flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA, CMDWIN),
+ flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_findpat',
},
{
command='imap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
{
command='imapclear',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_mapclear',
},
{
command='imenu',
- flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
{
command='inoremap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
{
command='inoreabbrev',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_abbreviate',
},
{
command='inoremenu',
- flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
{
command='intro',
- flags=bit.bor(TRLBAR, CMDWIN),
+ flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_intro',
},
{
command='isearch',
- flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA, CMDWIN),
+ flags=bit.bor(BANG, RANGE, DFLALL, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_findpat',
},
@@ -1223,37 +1226,37 @@ module.cmds = {
},
{
command='iunmap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_unmap',
},
{
command='iunabbrev',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_abbreviate',
},
{
command='iunmenu',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_menu',
},
{
command='join',
- flags=bit.bor(BANG, RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, MODIFY),
+ flags=bit.bor(BANG, RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, LOCK_OK, MODIFY),
addr_type='ADDR_LINES',
func='ex_join',
},
{
command='jumps',
- flags=bit.bor(TRLBAR, CMDWIN),
+ flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_jumps',
},
{
command='k',
- flags=bit.bor(RANGE, WORD1, TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(RANGE, WORD1, TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_mark',
},
@@ -1283,7 +1286,7 @@ module.cmds = {
},
{
command='list',
- flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN),
+ flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_print',
},
@@ -1313,7 +1316,7 @@ module.cmds = {
},
{
command='language',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_language',
},
@@ -1343,7 +1346,7 @@ module.cmds = {
},
{
command='later',
- flags=bit.bor(TRLBAR, EXTRA, NOSPC, CMDWIN),
+ flags=bit.bor(TRLBAR, EXTRA, NOSPC, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_later',
},
@@ -1373,13 +1376,13 @@ module.cmds = {
},
{
command='lcd',
- flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_cd',
},
{
command='lchdir',
- flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_cd',
},
@@ -1403,7 +1406,7 @@ module.cmds = {
},
{
command='left',
- flags=bit.bor(TRLBAR, RANGE, WHOLEFOLD, EXTRA, CMDWIN, MODIFY),
+ flags=bit.bor(TRLBAR, RANGE, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK, MODIFY),
addr_type='ADDR_LINES',
func='ex_align',
},
@@ -1415,7 +1418,7 @@ module.cmds = {
},
{
command='let',
- flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN),
+ flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_let',
},
@@ -1501,19 +1504,19 @@ module.cmds = {
},
{
command='llist',
- flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='qf_list',
},
{
command='lmap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
{
command='lmapclear',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_mapclear',
},
@@ -1525,7 +1528,7 @@ module.cmds = {
},
{
command='lnoremap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
@@ -1555,7 +1558,7 @@ module.cmds = {
},
{
command='loadkeymap',
- flags=bit.bor(CMDWIN),
+ flags=bit.bor(CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_loadkeymap',
},
@@ -1567,7 +1570,7 @@ module.cmds = {
},
{
command='lockvar',
- flags=bit.bor(BANG, EXTRA, NEEDARG, SBOXOK, CMDWIN),
+ flags=bit.bor(BANG, EXTRA, NEEDARG, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_lockvar',
},
@@ -1609,37 +1612,37 @@ module.cmds = {
},
{
command='lunmap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_unmap',
},
{
command='lua',
- flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_lua',
},
{
command='luado',
- flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_luado',
},
{
command='luafile',
- flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_luafile',
},
{
command='lvimgrep',
- flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE),
+ flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_vimgrep',
},
{
command='lvimgrepadd',
- flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE),
+ flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_vimgrep',
},
@@ -1651,19 +1654,19 @@ module.cmds = {
},
{
command='ls',
- flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='buflist_list',
},
{
command='move',
- flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, TRLBAR, CMDWIN, MODIFY),
+ flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, TRLBAR, CMDWIN, LOCK_OK, MODIFY),
addr_type='ADDR_LINES',
func='ex_copymove',
},
{
command='mark',
- flags=bit.bor(RANGE, WORD1, TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(RANGE, WORD1, TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_mark',
},
@@ -1675,49 +1678,49 @@ module.cmds = {
},
{
command='map',
- flags=bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
{
command='mapclear',
- flags=bit.bor(EXTRA, BANG, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, BANG, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_mapclear',
},
{
command='marks',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_marks',
},
{
command='match',
- flags=bit.bor(RANGE, EXTRA, CMDWIN),
+ flags=bit.bor(RANGE, EXTRA, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_match',
},
{
command='menu',
- flags=bit.bor(RANGE, ZEROR, BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
{
command='menutranslate',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_menutranslate',
},
{
command='messages',
- flags=bit.bor(EXTRA, TRLBAR, RANGE, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, RANGE, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_messages',
},
{
command='mkexrc',
- flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_mkrc',
},
@@ -1735,7 +1738,7 @@ module.cmds = {
},
{
command='mkvimrc',
- flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_mkrc',
},
@@ -1747,19 +1750,19 @@ module.cmds = {
},
{
command='mode',
- flags=bit.bor(WORD1, TRLBAR, CMDWIN),
+ flags=bit.bor(WORD1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_mode',
},
{
command='mzscheme',
- flags=bit.bor(RANGE, EXTRA, DFLALL, NEEDARG, CMDWIN, SBOXOK),
+ flags=bit.bor(RANGE, EXTRA, DFLALL, NEEDARG, CMDWIN, LOCK_OK, SBOXOK),
addr_type='ADDR_LINES',
func='ex_script_ni',
},
{
command='mzfile',
- flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_ni',
},
@@ -1777,37 +1780,37 @@ module.cmds = {
},
{
command='nmap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
{
command='nmapclear',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_mapclear',
},
{
command='nmenu',
- flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
{
command='nnoremap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
{
command='nnoremenu',
- flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
{
command='noremap',
- flags=bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
@@ -1819,19 +1822,19 @@ module.cmds = {
},
{
command='nohlsearch',
- flags=bit.bor(TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_nohlsearch',
},
{
command='noreabbrev',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_abbreviate',
},
{
command='noremenu',
- flags=bit.bor(RANGE, ZEROR, BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
@@ -1843,49 +1846,49 @@ module.cmds = {
},
{
command='normal',
- flags=bit.bor(RANGE, BANG, EXTRA, NEEDARG, NOTRLCOM, CTRLV, SBOXOK, CMDWIN),
+ flags=bit.bor(RANGE, BANG, EXTRA, NEEDARG, NOTRLCOM, CTRLV, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_normal',
},
{
command='number',
- flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN),
+ flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_print',
},
{
command='nunmap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_unmap',
},
{
command='nunmenu',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_menu',
},
{
command='oldfiles',
- flags=bit.bor(BANG, TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(BANG, TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_oldfiles',
},
{
command='omap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
{
command='omapclear',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_mapclear',
},
{
command='omenu',
- flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
@@ -1897,13 +1900,13 @@ module.cmds = {
},
{
command='onoremap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
{
command='onoremenu',
- flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
@@ -1915,37 +1918,37 @@ module.cmds = {
},
{
command='ounmap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_unmap',
},
{
command='ounmenu',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_menu',
},
{
command='ownsyntax',
- flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN),
+ flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_ownsyntax',
},
{
command='print',
- flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, SBOXOK),
+ flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, LOCK_OK, SBOXOK),
addr_type='ADDR_LINES',
func='ex_print',
},
{
command='packadd',
- flags=bit.bor(BANG, FILE1, NEEDARG, TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(BANG, FILE1, NEEDARG, TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_packadd',
},
{
command='packloadall',
- flags=bit.bor(BANG, TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(BANG, TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_packloadall',
},
@@ -1957,19 +1960,19 @@ module.cmds = {
},
{
command='perl',
- flags=bit.bor(RANGE, EXTRA, DFLALL, NEEDARG, SBOXOK, CMDWIN),
+ flags=bit.bor(RANGE, EXTRA, DFLALL, NEEDARG, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_perl',
},
{
command='perldo',
- flags=bit.bor(RANGE, EXTRA, DFLALL, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, EXTRA, DFLALL, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_perldo',
},
{
command='perlfile',
- flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_perlfile',
},
@@ -1987,9 +1990,9 @@ module.cmds = {
},
{
command='popup',
- flags=bit.bor(NEEDARG, EXTRA, BANG, TRLBAR, NOTRLCOM, CMDWIN),
+ flags=bit.bor(NEEDARG, EXTRA, BANG, TRLBAR, NOTRLCOM, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
- func='ex_ni',
+ func='ex_popup',
},
{
command='ppop',
@@ -2011,13 +2014,13 @@ module.cmds = {
},
{
command='profile',
- flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_profile',
},
{
command='profdel',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_breakdel',
},
@@ -2083,85 +2086,85 @@ module.cmds = {
},
{
command='put',
- flags=bit.bor(RANGE, WHOLEFOLD, BANG, REGSTR, TRLBAR, ZEROR, CMDWIN, MODIFY),
+ flags=bit.bor(RANGE, WHOLEFOLD, BANG, REGSTR, TRLBAR, ZEROR, CMDWIN, LOCK_OK, MODIFY),
addr_type='ADDR_LINES',
func='ex_put',
},
{
command='pwd',
- flags=bit.bor(TRLBAR, CMDWIN),
+ flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_pwd',
},
{
command='python',
- flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
- func='ex_python',
+ func='ex_python3',
},
{
command='pydo',
- flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
- func='ex_pydo',
+ func='ex_pydo3',
},
{
command='pyfile',
- flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
- func='ex_pyfile',
+ func='ex_py3file',
},
{
command='py3',
- flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_python3',
},
{
command='py3do',
- flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_pydo3',
},
{
command='python3',
- flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_python3',
},
{
command='py3file',
- flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_py3file',
},
{
command='pyx',
- flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
- func='ex_pyx',
+ func='ex_python3',
},
{
command='pyxdo',
- flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
- func='ex_pyxdo',
+ func='ex_pydo3',
},
{
command='pythonx',
- flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
- func='ex_pyx',
+ func='ex_python3',
},
{
command='pyxfile',
- flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
- func='ex_pyxfile',
+ func='ex_py3file',
},
{
command='quit',
- flags=bit.bor(BANG, RANGE, COUNT, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, RANGE, COUNT, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_WINDOWS',
func='ex_quit',
},
@@ -2173,13 +2176,13 @@ module.cmds = {
},
{
command='qall',
- flags=bit.bor(BANG, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_quit_all',
},
{
command='read',
- flags=bit.bor(BANG, RANGE, WHOLEFOLD, FILE1, ARGOPT, TRLBAR, ZEROR, CMDWIN, MODIFY),
+ flags=bit.bor(BANG, RANGE, WHOLEFOLD, FILE1, ARGOPT, TRLBAR, ZEROR, CMDWIN, LOCK_OK, MODIFY),
addr_type='ADDR_LINES',
func='ex_read',
},
@@ -2191,55 +2194,55 @@ module.cmds = {
},
{
command='redo',
- flags=bit.bor(TRLBAR, CMDWIN),
+ flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_redo',
},
{
command='redir',
- flags=bit.bor(BANG, FILES, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, FILES, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_redir',
},
{
command='redraw',
- flags=bit.bor(BANG, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_redraw',
},
{
command='redrawstatus',
- flags=bit.bor(BANG, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_redrawstatus',
},
{
command='redrawtabline',
- flags=bit.bor(TRLBAR, CMDWIN),
+ flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_redrawtabline',
},
{
command='registers',
- flags=bit.bor(EXTRA, NOTRLCOM, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, NOTRLCOM, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_display',
},
{
command='resize',
- flags=bit.bor(RANGE, TRLBAR, WORD1, CMDWIN),
+ flags=bit.bor(RANGE, TRLBAR, WORD1, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_resize',
},
{
command='retab',
- flags=bit.bor(TRLBAR, RANGE, WHOLEFOLD, DFLALL, BANG, WORD1, CMDWIN, MODIFY),
+ flags=bit.bor(TRLBAR, RANGE, WHOLEFOLD, DFLALL, BANG, WORD1, CMDWIN, LOCK_OK, MODIFY),
addr_type='ADDR_LINES',
func='ex_retab',
},
{
command='return',
- flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN),
+ flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_return',
},
@@ -2251,7 +2254,7 @@ module.cmds = {
},
{
command='right',
- flags=bit.bor(TRLBAR, RANGE, WHOLEFOLD, EXTRA, CMDWIN, MODIFY),
+ flags=bit.bor(TRLBAR, RANGE, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK, MODIFY),
addr_type='ADDR_LINES',
func='ex_align',
},
@@ -2263,13 +2266,13 @@ module.cmds = {
},
{
command='rshada',
- flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_shada',
},
{
command='runtime',
- flags=bit.bor(BANG, NEEDARG, FILES, TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(BANG, NEEDARG, FILES, TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_runtime',
},
@@ -2281,33 +2284,34 @@ module.cmds = {
},
{
command='ruby',
- flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_ruby',
},
{
command='rubydo',
- flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_rubydo',
},
{
command='rubyfile',
- flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN),
+ flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_rubyfile',
},
{
command='rviminfo',
- flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_shada',
},
{
command='substitute',
- flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN),
+ flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK, PREVIEW),
addr_type='ADDR_LINES',
func='ex_substitute',
+ preview_func='ex_substitute_preview',
},
{
command='sNext',
@@ -2335,7 +2339,7 @@ module.cmds = {
},
{
command='saveas',
- flags=bit.bor(BANG, FILE1, ARGOPT, CMDWIN, TRLBAR),
+ flags=bit.bor(BANG, FILE1, ARGOPT, CMDWIN, LOCK_OK, TRLBAR),
addr_type='ADDR_NONE',
func='ex_write',
},
@@ -2395,13 +2399,13 @@ module.cmds = {
},
{
command='scriptnames',
- flags=bit.bor(BANG, RANGE, COUNT, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, RANGE, COUNT, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_scriptnames',
},
{
command='scriptencoding',
- flags=bit.bor(WORD1, TRLBAR, CMDWIN),
+ flags=bit.bor(WORD1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_scriptencoding',
},
@@ -2413,25 +2417,25 @@ module.cmds = {
},
{
command='set',
- flags=bit.bor(TRLBAR, EXTRA, CMDWIN, SBOXOK),
+ flags=bit.bor(BANG, TRLBAR, EXTRA, CMDWIN, LOCK_OK, SBOXOK),
addr_type='ADDR_NONE',
func='ex_set',
},
{
command='setfiletype',
- flags=bit.bor(TRLBAR, EXTRA, NEEDARG, CMDWIN),
+ flags=bit.bor(TRLBAR, EXTRA, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_setfiletype',
},
{
command='setglobal',
- flags=bit.bor(TRLBAR, EXTRA, CMDWIN, SBOXOK),
+ flags=bit.bor(BANG, TRLBAR, EXTRA, CMDWIN, LOCK_OK, SBOXOK),
addr_type='ADDR_NONE',
func='ex_set',
},
{
command='setlocal',
- flags=bit.bor(TRLBAR, EXTRA, CMDWIN, SBOXOK),
+ flags=bit.bor(BANG, TRLBAR, EXTRA, CMDWIN, LOCK_OK, SBOXOK),
addr_type='ADDR_NONE',
func='ex_set',
},
@@ -2449,25 +2453,25 @@ module.cmds = {
},
{
command='simalt',
- flags=bit.bor(NEEDARG, WORD1, TRLBAR, CMDWIN),
+ flags=bit.bor(NEEDARG, WORD1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_ni',
},
{
command='sign',
- flags=bit.bor(NEEDARG, RANGE, EXTRA, CMDWIN),
+ flags=bit.bor(NEEDARG, RANGE, EXTRA, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_sign',
},
{
command='silent',
- flags=bit.bor(NEEDARG, EXTRA, BANG, NOTRLCOM, SBOXOK, CMDWIN),
+ flags=bit.bor(NEEDARG, EXTRA, BANG, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_wrongmodifier',
},
{
command='sleep',
- flags=bit.bor(BANG, RANGE, COUNT, EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, RANGE, COUNT, EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_sleep',
},
@@ -2479,25 +2483,26 @@ module.cmds = {
},
{
command='smagic',
- flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN),
+ flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK, PREVIEW),
addr_type='ADDR_LINES',
func='ex_submagic',
+ preview_func='ex_submagic_preview',
},
{
command='smap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
{
command='smapclear',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_mapclear',
},
{
command='smenu',
- flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
@@ -2509,25 +2514,26 @@ module.cmds = {
},
{
command='snomagic',
- flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN),
+ flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK, PREVIEW),
addr_type='ADDR_LINES',
func='ex_submagic',
+ preview_func='ex_submagic_preview',
},
{
command='snoremap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
{
command='snoremenu',
- flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
{
command='source',
- flags=bit.bor(RANGE, DFLALL, WHOLEFOLD, BANG, FILE1, TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(RANGE, DFLALL, WHOLEFOLD, BANG, FILE1, TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_source',
},
@@ -2599,7 +2605,7 @@ module.cmds = {
},
{
command='stop',
- flags=bit.bor(TRLBAR, BANG, CMDWIN),
+ flags=bit.bor(TRLBAR, BANG, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_stop',
},
@@ -2611,25 +2617,25 @@ module.cmds = {
},
{
command='startinsert',
- flags=bit.bor(BANG, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_startinsert',
},
{
command='startgreplace',
- flags=bit.bor(BANG, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_startinsert',
},
{
command='startreplace',
- flags=bit.bor(BANG, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_startinsert',
},
{
command='stopinsert',
- flags=bit.bor(BANG, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_stopinsert',
},
@@ -2653,19 +2659,19 @@ module.cmds = {
},
{
command='sunmap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_unmap',
},
{
command='sunmenu',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_menu',
},
{
command='suspend',
- flags=bit.bor(TRLBAR, BANG, CMDWIN),
+ flags=bit.bor(TRLBAR, BANG, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_stop',
},
@@ -2677,19 +2683,19 @@ module.cmds = {
},
{
command='swapname',
- flags=bit.bor(TRLBAR, CMDWIN),
+ flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_swapname',
},
{
command='syntax',
- flags=bit.bor(EXTRA, NOTRLCOM, CMDWIN),
+ flags=bit.bor(EXTRA, NOTRLCOM, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_syntax',
},
{
command='syntime',
- flags=bit.bor(NEEDARG, WORD1, TRLBAR, CMDWIN),
+ flags=bit.bor(NEEDARG, WORD1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_syntime',
},
@@ -2701,19 +2707,19 @@ module.cmds = {
},
{
command='t',
- flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, TRLBAR, CMDWIN, MODIFY),
+ flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, TRLBAR, CMDWIN, LOCK_OK, MODIFY),
addr_type='ADDR_LINES',
func='ex_copymove',
},
{
command='tcd',
- flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_cd',
},
{
command='tchdir',
- flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_cd',
},
@@ -2731,7 +2737,7 @@ module.cmds = {
},
{
command='tags',
- flags=bit.bor(TRLBAR, CMDWIN),
+ flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='do_tags',
},
@@ -2743,7 +2749,7 @@ module.cmds = {
},
{
command='tabclose',
- flags=bit.bor(BANG, RANGE, ZEROR, EXTRA, NOSPC, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, RANGE, ZEROR, EXTRA, NOSPC, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_TABS',
func='ex_tabclose',
},
@@ -2797,7 +2803,7 @@ module.cmds = {
},
{
command='tabonly',
- flags=bit.bor(BANG, RANGE, ZEROR, EXTRA, NOSPC, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, RANGE, ZEROR, EXTRA, NOSPC, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_TABS',
func='ex_tabonly',
},
@@ -2821,31 +2827,31 @@ module.cmds = {
},
{
command='tabs',
- flags=bit.bor(TRLBAR, CMDWIN),
+ flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_tabs',
},
{
command='tcl',
- flags=bit.bor(RANGE,EXTRA,NEEDARG,CMDWIN),
+ flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_script_ni',
},
{
command='tcldo',
- flags=bit.bor(RANGE,DFLALL,EXTRA,NEEDARG,CMDWIN),
+ flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_ni',
},
{
command='tclfile',
- flags=bit.bor(RANGE,FILE1,NEEDARG,CMDWIN),
+ flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_ni',
},
{
command='terminal',
- flags=bit.bor(BANG, FILES, CMDWIN),
+ flags=bit.bor(BANG, FILES, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_terminal',
},
@@ -2857,7 +2863,7 @@ module.cmds = {
},
{
command='throw',
- flags=bit.bor(EXTRA, NEEDARG, SBOXOK, CMDWIN),
+ flags=bit.bor(EXTRA, NEEDARG, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_throw',
},
@@ -2874,20 +2880,38 @@ module.cmds = {
func='ex_tag',
},
{
+ command='tlmenu',
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
+ addr_type='ADDR_OTHER',
+ func='ex_menu',
+ },
+ {
+ command='tlnoremenu',
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
+ addr_type='ADDR_OTHER',
+ func='ex_menu',
+ },
+ {
+ command='tlunmenu',
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
+ addr_type='ADDR_OTHER',
+ func='ex_menu',
+ },
+ {
command='tmenu',
- flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
{
command='tmap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
{
command='tmapclear',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_mapclear',
},
@@ -2899,7 +2923,7 @@ module.cmds = {
},
{
command='tnoremap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
@@ -2923,7 +2947,7 @@ module.cmds = {
},
{
command='try',
- flags=bit.bor(TRLBAR, SBOXOK, CMDWIN),
+ flags=bit.bor(TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_try',
},
@@ -2935,37 +2959,37 @@ module.cmds = {
},
{
command='tunmenu',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_menu',
},
{
command='tunmap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_unmap',
},
{
command='undo',
- flags=bit.bor(RANGE, COUNT, ZEROR, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, RANGE, COUNT, ZEROR, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_undo',
},
{
command='undojoin',
- flags=bit.bor(TRLBAR, CMDWIN),
+ flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_undojoin',
},
{
command='undolist',
- flags=bit.bor(TRLBAR, CMDWIN),
+ flags=bit.bor(TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_undolist',
},
{
command='unabbreviate',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_abbreviate',
},
@@ -2977,31 +3001,31 @@ module.cmds = {
},
{
command='unlet',
- flags=bit.bor(BANG, EXTRA, NEEDARG, SBOXOK, CMDWIN),
+ flags=bit.bor(BANG, EXTRA, NEEDARG, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_unlet',
},
{
command='unlockvar',
- flags=bit.bor(BANG, EXTRA, NEEDARG, SBOXOK, CMDWIN),
+ flags=bit.bor(BANG, EXTRA, NEEDARG, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_lockvar',
},
{
command='unmap',
- flags=bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_unmap',
},
{
command='unmenu',
- flags=bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(BANG, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_menu',
},
{
command='unsilent',
- flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, SBOXOK, CMDWIN),
+ flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_wrongmodifier',
},
@@ -3013,19 +3037,19 @@ module.cmds = {
},
{
command='vglobal',
- flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, DFLALL, CMDWIN),
+ flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, DFLALL, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_global',
},
{
command='version',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_version',
},
{
command='verbose',
- flags=bit.bor(NEEDARG, RANGE, EXTRA, NOTRLCOM, SBOXOK, CMDWIN),
+ flags=bit.bor(NEEDARG, RANGE, EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_wrongmodifier',
},
@@ -3049,13 +3073,13 @@ module.cmds = {
},
{
command='vimgrep',
- flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE),
+ flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_vimgrep',
},
{
command='vimgrepadd',
- flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE),
+ flags=bit.bor(RANGE, BANG, NEEDARG, EXTRA, NOTRLCOM, TRLBAR, XFILE, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_vimgrep',
},
@@ -3067,25 +3091,25 @@ module.cmds = {
},
{
command='vmap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
{
command='vmapclear',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_mapclear',
},
{
command='vmenu',
- flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
{
command='vnoremap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
@@ -3097,7 +3121,7 @@ module.cmds = {
},
{
command='vnoremenu',
- flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
@@ -3109,19 +3133,19 @@ module.cmds = {
},
{
command='vunmap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_unmap',
},
{
command='vunmenu',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_menu',
},
{
command='write',
- flags=bit.bor(RANGE, WHOLEFOLD, BANG, FILE1, ARGOPT, DFLALL, TRLBAR, CMDWIN),
+ flags=bit.bor(RANGE, WHOLEFOLD, BANG, FILE1, ARGOPT, DFLALL, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_write',
},
@@ -3133,13 +3157,13 @@ module.cmds = {
},
{
command='wall',
- flags=bit.bor(BANG, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='do_wqall',
},
{
command='while',
- flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN),
+ flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_while',
},
@@ -3151,7 +3175,7 @@ module.cmds = {
},
{
command='wincmd',
- flags=bit.bor(NEEDARG, WORD1, RANGE, CMDWIN),
+ flags=bit.bor(NEEDARG, WORD1, RANGE, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_wincmd',
},
@@ -3163,7 +3187,7 @@ module.cmds = {
},
{
command='winpos',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_ni',
},
@@ -3193,7 +3217,7 @@ module.cmds = {
},
{
command='wshada',
- flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_shada',
},
@@ -3205,13 +3229,13 @@ module.cmds = {
},
{
command='wviminfo',
- flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN),
+ flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_shada',
},
{
command='xit',
- flags=bit.bor(RANGE, WHOLEFOLD, BANG, FILE1, ARGOPT, DFLALL, TRLBAR, CMDWIN),
+ flags=bit.bor(RANGE, WHOLEFOLD, BANG, FILE1, ARGOPT, DFLALL, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_exit',
},
@@ -3223,120 +3247,122 @@ module.cmds = {
},
{
command='xmap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
{
command='xmapclear',
- flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_mapclear',
},
{
command='xmenu',
- flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
{
command='xnoremap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_map',
},
{
command='xnoremenu',
- flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_menu',
},
{
command='xunmap',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_unmap',
},
{
command='xunmenu',
- flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
func='ex_menu',
},
{
command='yank',
- flags=bit.bor(RANGE, WHOLEFOLD, REGSTR, COUNT, TRLBAR, CMDWIN),
+ flags=bit.bor(RANGE, WHOLEFOLD, REGSTR, COUNT, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_operators',
},
{
command='z',
- flags=bit.bor(RANGE, WHOLEFOLD, BANG, EXTRA, FLAGS, TRLBAR, CMDWIN),
+ flags=bit.bor(RANGE, WHOLEFOLD, BANG, EXTRA, FLAGS, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_z',
},
+ -- commands that don't start with a letter
{
command='!',
enum='CMD_bang',
- flags=bit.bor(RANGE, WHOLEFOLD, BANG, FILES, CMDWIN),
+ flags=bit.bor(RANGE, WHOLEFOLD, BANG, FILES, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_bang',
},
{
command='#',
enum='CMD_pound',
- flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN),
+ flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_print',
},
{
command='&',
enum='CMD_and',
- flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN, MODIFY),
+ flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK, MODIFY),
addr_type='ADDR_LINES',
func='ex_substitute',
},
{
command='<',
enum='CMD_lshift',
- flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, MODIFY),
+ flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, LOCK_OK, MODIFY),
addr_type='ADDR_LINES',
func='ex_operators',
},
{
command='=',
enum='CMD_equal',
- flags=bit.bor(RANGE, TRLBAR, DFLALL, FLAGS, CMDWIN),
+ flags=bit.bor(RANGE, TRLBAR, DFLALL, FLAGS, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_equal',
},
{
command='>',
enum='CMD_rshift',
- flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, MODIFY),
+ flags=bit.bor(RANGE, WHOLEFOLD, COUNT, FLAGS, TRLBAR, CMDWIN, LOCK_OK, MODIFY),
addr_type='ADDR_LINES',
func='ex_operators',
},
{
command='@',
enum='CMD_at',
- flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, TRLBAR, CMDWIN),
+ flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, TRLBAR, CMDWIN, LOCK_OK),
addr_type='ADDR_LINES',
func='ex_at',
},
{
- command='Next',
- flags=bit.bor(EXTRA, RANGE, COUNT, BANG, CMDARG, ARGOPT, TRLBAR),
- addr_type='ADDR_OTHER',
- func='ex_previous',
- },
- {
command='~',
enum='CMD_tilde',
- flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN, MODIFY),
+ flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN, LOCK_OK, MODIFY),
addr_type='ADDR_LINES',
func='ex_substitute',
},
+ -- commands that start with an uppercase letter
+ {
+ command='Next',
+ flags=bit.bor(EXTRA, RANGE, COUNT, BANG, CMDARG, ARGOPT, TRLBAR),
+ addr_type='ADDR_OTHER',
+ func='ex_previous',
+ },
}
return module
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index 2e8d39ec30..e57dc5d13f 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -12,6 +12,7 @@
#include <string.h>
#include "nvim/ascii.h"
+#include "nvim/globals.h"
#include "nvim/vim.h"
#ifdef HAVE_LOCALE_H
# include <locale.h>
@@ -69,7 +70,7 @@ typedef struct sn_prl_S {
/// sourcing can be done recursively.
struct source_cookie {
FILE *fp; ///< opened file for sourcing
- char_u *nextline; ///< if not NULL: line that was read ahead
+ char *nextline; ///< if not NULL: line that was read ahead
linenr_T sourcing_lnum; ///< line number of the source file
int finished; ///< ":finish" used
#if defined(USE_CRNL)
@@ -77,7 +78,7 @@ struct source_cookie {
bool error; ///< true if LF found after CR-LF
#endif
linenr_T breakpoint; ///< next line with breakpoint or zero
- char_u *fname; ///< name of sourced file
+ char *fname; ///< name of sourced file
int dbg_tick; ///< debug_tick when breakpoint was set
int level; ///< top nesting level of sourced file
vimconv_T conv; ///< type of conversion
@@ -89,23 +90,23 @@ struct source_cookie {
# include "ex_cmds2.c.generated.h"
#endif
-static char_u *profile_fname = NULL;
+static char *profile_fname = NULL;
/// ":profile cmd args"
void ex_profile(exarg_T *eap)
{
static proftime_T pause_time;
- char_u *e;
+ char *e;
int len;
- e = skiptowhite(eap->arg);
+ e = (char *)skiptowhite((char_u *)eap->arg);
len = (int)(e - eap->arg);
e = skipwhite(e);
if (len == 5 && STRNCMP(eap->arg, "start", 5) == 0 && *e != NUL) {
xfree(profile_fname);
- profile_fname = expand_env_save_opt(e, true);
+ profile_fname = (char *)expand_env_save_opt((char_u *)e, true);
do_profiling = PROF_YES;
profile_set_wait(profile_zero());
set_vim_var_nr(VV_PROFILING, 1L);
@@ -135,21 +136,6 @@ void ex_profile(exarg_T *eap)
}
}
-void ex_python(exarg_T *eap)
-{
- script_host_execute("python", eap);
-}
-
-void ex_pyfile(exarg_T *eap)
-{
- script_host_execute_file("python", eap);
-}
-
-void ex_pydo(exarg_T *eap)
-{
- script_host_do_range("python", eap);
-}
-
void ex_ruby(exarg_T *eap)
{
script_host_execute("ruby", eap);
@@ -214,11 +200,12 @@ static char *pexpand_cmds[] = {
/// Function given to ExpandGeneric() to obtain the profile command
/// specific expansion.
-char_u *get_profile_name(expand_T *xp, int idx)
+char *get_profile_name(expand_T *xp, int idx)
+ FUNC_ATTR_PURE
{
switch (pexpand_what) {
case PEXP_SUBCMD:
- return (char_u *)pexpand_cmds[idx];
+ return pexpand_cmds[idx];
// case PEXP_FUNC: TODO
default:
return NULL;
@@ -231,7 +218,7 @@ void set_context_in_profile_cmd(expand_T *xp, const char *arg)
// Default: expand subcommands.
xp->xp_context = EXPAND_PROFILE;
pexpand_what = PEXP_SUBCMD;
- xp->xp_pattern = (char_u *)arg;
+ xp->xp_pattern = (char *)arg;
char_u *const end_subcmd = skiptowhite((const char_u *)arg);
if (*end_subcmd == NUL) {
@@ -240,7 +227,7 @@ void set_context_in_profile_cmd(expand_T *xp, const char *arg)
if ((const char *)end_subcmd - arg == 5 && strncmp(arg, "start", 5) == 0) {
xp->xp_context = EXPAND_FILES;
- xp->xp_pattern = skipwhite((const char_u *)end_subcmd);
+ xp->xp_pattern = skipwhite((char *)end_subcmd);
return;
}
@@ -254,7 +241,7 @@ void profile_dump(void)
FILE *fd;
if (profile_fname != NULL) {
- fd = os_fopen((char *)profile_fname, "w");
+ fd = os_fopen(profile_fname, "w");
if (fd == NULL) {
semsg(_(e_notopen), profile_fname);
} else {
@@ -450,9 +437,10 @@ static void script_dump_profile(FILE *fd)
}
}
-/// Return true when a function defined in the current script should be
-/// profiled.
+/// @return true when a function defined in the current script should be
+/// profiled.
bool prof_def_func(void)
+ FUNC_ATTR_PURE
{
if (current_sctx.sc_sid > 0) {
return SCRIPT_ITEM(current_sctx.sc_sid).sn_pr_force;
@@ -506,7 +494,7 @@ void autowrite_all(void)
}
}
-/// Return true if buffer was changed and cannot be abandoned.
+/// @return true if buffer was changed and cannot be abandoned.
/// For flags use the CCGD_ values.
bool check_changed(buf_T *buf, int flags)
{
@@ -518,7 +506,7 @@ bool check_changed(buf_T *buf, int flags)
&& bufIsChanged(buf)
&& ((flags & CCGD_MULTWIN) || buf->b_nwindows <= 1)
&& (!(flags & CCGD_AW) || autowrite(buf, forceit) == FAIL)) {
- if ((p_confirm || cmdmod.confirm) && p_write) {
+ if ((p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) && p_write) {
int count = 0;
if (flags & CCGD_ALLBUF) {
@@ -549,7 +537,6 @@ bool check_changed(buf_T *buf, int flags)
return false;
}
-
/// Ask the user what to do when abandoning a changed buffer.
/// Must check 'write' option first!
///
@@ -557,7 +544,7 @@ bool check_changed(buf_T *buf, int flags)
/// @param checkall may abandon all changed buffers
void dialog_changed(buf_T *buf, bool checkall)
{
- char_u buff[DIALOG_MSG_SIZE];
+ char buff[DIALOG_MSG_SIZE];
int ret;
// Init ea pseudo-structure, this is needed for the check_overwrite()
// function.
@@ -566,20 +553,16 @@ void dialog_changed(buf_T *buf, bool checkall)
.forceit = false,
};
- dialog_msg(buff, _("Save changes to \"%s\"?"), buf->b_fname);
+ dialog_msg((char *)buff, _("Save changes to \"%s\"?"), buf->b_fname);
if (checkall) {
- ret = vim_dialog_yesnoallcancel(VIM_QUESTION, NULL, buff, 1);
+ ret = vim_dialog_yesnoallcancel(VIM_QUESTION, NULL, (char_u *)buff, 1);
} else {
- ret = vim_dialog_yesnocancel(VIM_QUESTION, NULL, buff, 1);
+ ret = vim_dialog_yesnocancel(VIM_QUESTION, NULL, (char_u *)buff, 1);
}
if (ret == VIM_YES) {
if (buf->b_fname != NULL
- && check_overwrite(&ea,
- buf,
- buf->b_fname,
- buf->b_ffname,
- false) == OK) {
+ && check_overwrite(&ea, buf, buf->b_fname, buf->b_ffname, false) == OK) {
// didn't hit Cancel
(void)buf_write_all(buf, false);
}
@@ -595,8 +578,7 @@ void dialog_changed(buf_T *buf, bool checkall)
set_bufref(&bufref, buf2);
if (buf2->b_fname != NULL
- && check_overwrite(&ea, buf2, buf2->b_fname,
- buf2->b_ffname, false) == OK) {
+ && check_overwrite(&ea, buf2, buf2->b_fname, buf2->b_ffname, false) == OK) {
// didn't hit Cancel
(void)buf_write_all(buf2, false);
}
@@ -620,17 +602,17 @@ void dialog_changed(buf_T *buf, bool checkall)
/// @return bool Whether to close the buffer or not.
bool dialog_close_terminal(buf_T *buf)
{
- char_u buff[DIALOG_MSG_SIZE];
+ char buff[DIALOG_MSG_SIZE];
dialog_msg(buff, _("Close \"%s\"?"),
- (buf->b_fname != NULL) ? buf->b_fname : (char_u *)"?");
+ (buf->b_fname != NULL) ? buf->b_fname : "?");
- int ret = vim_dialog_yesnocancel(VIM_QUESTION, NULL, buff, 1);
+ int ret = vim_dialog_yesnocancel(VIM_QUESTION, NULL, (char_u *)buff, 1);
return ret == VIM_YES;
}
-/// Return true if the buffer "buf" can be abandoned, either by making it
+/// @return true if the buffer "buf" can be abandoned, either by making it
/// hidden, autowriting it or unloading it.
bool can_abandon(buf_T *buf, int forceit)
{
@@ -641,7 +623,6 @@ bool can_abandon(buf_T *buf, int forceit)
|| forceit;
}
-
/// Add a buffer number to "bufnrs", unless it's already there.
static void add_bufnum(int *bufnrs, int *bufnump, int nr)
{
@@ -737,7 +718,7 @@ bool check_changed_any(bool hidden, bool unload)
ret = true;
exiting = false;
// When ":confirm" used, don't give an error message.
- if (!(p_confirm || cmdmod.confirm)) {
+ if (!(p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM))) {
// There must be a wait_return for this message, do_buffer()
// may cause a redraw. But wait_return() is a no-op when vgetc()
// is busy (Quit used from window menu), then make sure we don't
@@ -785,8 +766,8 @@ theend:
return ret;
}
-/// Return FAIL if there is no file name, OK if there is one.
-/// Give error message for FAIL.
+/// @return FAIL if there is no file name, OK if there is one.
+/// Give error message for FAIL.
int check_fname(void)
{
if (curbuf->b_ffname == NULL) {
@@ -798,7 +779,7 @@ int check_fname(void)
/// Flush the contents of a buffer, unless it has no file name.
///
-/// @return FAIL for failure, OK otherwise
+/// @return FAIL for failure, OK otherwise
int buf_write_all(buf_T *buf, int forceit)
{
int retval;
@@ -822,17 +803,18 @@ int buf_write_all(buf_T *buf, int forceit)
/// Isolate one argument, taking backticks.
/// Changes the argument in-place, puts a NUL after it. Backticks remain.
-/// Return a pointer to the start of the next argument.
-static char_u *do_one_arg(char_u *str)
+///
+/// @return a pointer to the start of the next argument.
+static char *do_one_arg(char *str)
{
- char_u *p;
+ char *p;
bool inbacktick;
inbacktick = false;
for (p = str; *str; str++) {
// When the backslash is used for escaping the special meaning of a
// character we need to keep it until wildcard expansion.
- if (rem_backslash(str)) {
+ if (rem_backslash((char_u *)str)) {
*p++ = *str++;
*p++ = *str;
} else {
@@ -854,11 +836,11 @@ static char_u *do_one_arg(char_u *str)
/// Separate the arguments in "str" and return a list of pointers in the
/// growarray "gap".
-static void get_arglist(garray_T *gap, char_u *str, int escaped)
+static void get_arglist(garray_T *gap, char *str, int escaped)
{
ga_init(gap, (int)sizeof(char_u *), 20);
while (*str != NUL) {
- GA_APPEND(char_u *, gap, str);
+ GA_APPEND(char *, gap, str);
// If str is escaped, don't handle backslashes or spaces
if (!escaped) {
@@ -873,13 +855,13 @@ static void get_arglist(garray_T *gap, char_u *str, int escaped)
/// Parse a list of arguments (file names), expand them and return in
/// "fnames[fcountp]". When "wig" is true, removes files matching 'wildignore'.
///
-/// @return FAIL or OK.
+/// @return FAIL or OK.
int get_arglist_exp(char_u *str, int *fcountp, char_u ***fnamesp, bool wig)
{
garray_T ga;
int i;
- get_arglist(&ga, str, true);
+ get_arglist(&ga, (char *)str, true);
if (wig) {
i = expand_wildcards(ga.ga_len, (char_u **)ga.ga_data,
@@ -893,7 +875,6 @@ int get_arglist_exp(char_u *str, int *fcountp, char_u ***fnamesp, bool wig)
return i;
}
-
/// @param str
/// @param what
/// AL_SET: Redefine the argument list to 'str'.
@@ -903,14 +884,14 @@ int get_arglist_exp(char_u *str, int *fcountp, char_u ***fnamesp, bool wig)
/// 0 means before first one
/// @param will_edit will edit added argument
///
-/// @return FAIL for failure, OK otherwise.
-static int do_arglist(char_u *str, int what, int after, bool will_edit)
+/// @return FAIL for failure, OK otherwise.
+static int do_arglist(char *str, int what, int after, bool will_edit)
FUNC_ATTR_NONNULL_ALL
{
garray_T new_ga;
int exp_count;
- char_u **exp_files;
- char_u *p;
+ char **exp_files;
+ char *p;
int match;
int arg_escaped = true;
@@ -934,7 +915,7 @@ static int do_arglist(char_u *str, int what, int after, bool will_edit)
// argument list.
regmatch.rm_ic = p_fic; // ignore case when 'fileignorecase' is set
for (int i = 0; i < new_ga.ga_len && !got_int; i++) {
- p = ((char_u **)new_ga.ga_data)[i];
+ p = ((char **)new_ga.ga_data)[i];
p = file_pat_to_reg_pat(p, NULL, NULL, false);
if (p == NULL) {
break;
@@ -947,8 +928,7 @@ static int do_arglist(char_u *str, int what, int after, bool will_edit)
didone = false;
for (match = 0; match < ARGCOUNT; match++) {
- if (vim_regexec(&regmatch, alist_name(&ARGLIST[match]),
- (colnr_T)0)) {
+ if (vim_regexec(&regmatch, alist_name(&ARGLIST[match]), (colnr_T)0)) {
didone = true;
xfree(ARGLIST[match].ae_fname);
memmove(ARGLIST + match, ARGLIST + match + 1,
@@ -970,7 +950,7 @@ static int do_arglist(char_u *str, int what, int after, bool will_edit)
ga_clear(&new_ga);
} else {
int i = expand_wildcards(new_ga.ga_len, (char_u **)new_ga.ga_data,
- &exp_count, &exp_files,
+ &exp_count, (char_u ***)&exp_files,
EW_DIR|EW_FILE|EW_ADDSLASH|EW_NOTFOUND);
ga_clear(&new_ga);
if (i == FAIL || exp_count == 0) {
@@ -1002,8 +982,8 @@ static void alist_check_arg_idx(void)
}
}
-/// Return true if window "win" is editing the file at the current argument
-/// index.
+/// @return true if window "win" is editing the file at the current argument
+/// index.
static bool editing_arg_idx(win_T *win)
{
return !(win->w_arg_idx >= WARGCOUNT(win)
@@ -1011,7 +991,8 @@ static bool editing_arg_idx(win_T *win)
!= WARGLIST(win)[win->w_arg_idx].ae_fnum
&& (win->w_buffer->b_ffname == NULL
|| !(path_full_compare(alist_name(&WARGLIST(win)[win->w_arg_idx]),
- win->w_buffer->b_ffname, true, true) & kEqualFiles))));
+ win->w_buffer->b_ffname, true,
+ true) & kEqualFiles))));
}
/// Check if window "win" is editing the w_arg_idx file in its argument list.
@@ -1037,8 +1018,7 @@ void check_arg_idx(win_T *win)
// We are editing the current entry in the argument list.
// Set "arg_had_last" if it's also the last one
win->w_arg_idx_invalid = false;
- if (win->w_arg_idx == WARGCOUNT(win) - 1
- && win->w_alist == &global_alist) {
+ if (win->w_arg_idx == WARGCOUNT(win) - 1 && win->w_alist == &global_alist) {
arg_had_last = true;
}
}
@@ -1063,14 +1043,14 @@ void ex_args(exarg_T *eap)
} else if (eap->cmdidx == CMD_args) {
// ":args": list arguments.
if (ARGCOUNT > 0) {
- char_u **items = xmalloc(sizeof(char_u *) * (size_t)ARGCOUNT);
+ char **items = xmalloc(sizeof(char_u *) * (size_t)ARGCOUNT);
// Overwrite the command, for a short list there is no scrolling
// required and no wait_return().
gotocmdline(true);
for (int i = 0; i < ARGCOUNT; i++) {
items[i] = alist_name(&ARGLIST[i]);
}
- list_in_columns(items, ARGCOUNT, curwin->w_arg_idx);
+ list_in_columns((char_u **)items, ARGCOUNT, curwin->w_arg_idx);
xfree(items);
}
} else if (eap->cmdidx == CMD_arglocal) {
@@ -1130,7 +1110,7 @@ void ex_argument(exarg_T *eap)
void do_argfile(exarg_T *eap, int argn)
{
int other;
- char_u *p;
+ char *p;
int old_arg_idx = curwin->w_arg_idx;
if (argn < 0 || argn >= ARGCOUNT) {
@@ -1145,7 +1125,7 @@ void do_argfile(exarg_T *eap, int argn)
setpcmark();
// split window or create new tab page first
- if (*eap->cmd == 's' || cmdmod.tab != 0) {
+ if (*eap->cmd == 's' || cmdmod.cmod_tab != 0) {
if (win_split(0, 0) == FAIL) {
return;
}
@@ -1155,7 +1135,7 @@ void do_argfile(exarg_T *eap, int argn)
// the same buffer
other = true;
if (buf_hide(curbuf)) {
- p = (char_u *)fix_fname((char *)alist_name(&ARGLIST[argn]));
+ p = fix_fname(alist_name(&ARGLIST[argn]));
other = otherfile(p);
xfree(p);
}
@@ -1169,8 +1149,7 @@ void do_argfile(exarg_T *eap, int argn)
}
curwin->w_arg_idx = argn;
- if (argn == ARGCOUNT - 1
- && curwin->w_alist == &global_alist) {
+ if (argn == ARGCOUNT - 1 && curwin->w_alist == &global_alist) {
arg_had_last = true;
}
@@ -1300,8 +1279,8 @@ void ex_listdo(exarg_T *eap)
win_T *wp;
tabpage_T *tp;
int next_fnum = 0;
- char_u *save_ei = NULL;
- char_u *p_shm_save;
+ char *save_ei = NULL;
+ char *p_shm_save;
if (eap->cmdidx != CMD_windo && eap->cmdidx != CMD_tabdo) {
// Don't do syntax HL autocommands. Skipping the syntax file is a
@@ -1392,10 +1371,10 @@ void ex_listdo(exarg_T *eap)
if (curwin->w_arg_idx != i || !editing_arg_idx(curwin)) {
// Clear 'shm' to avoid that the file message overwrites
// any output from the command.
- p_shm_save = vim_strsave(p_shm);
+ p_shm_save = (char *)vim_strsave(p_shm);
set_option_value("shm", 0L, "", 0);
do_argfile(eap, i);
- set_option_value("shm", 0L, (char *)p_shm_save, 0);
+ set_option_value("shm", 0L, p_shm_save, 0);
xfree(p_shm_save);
}
if (curwin->w_arg_idx != i) {
@@ -1438,8 +1417,7 @@ void ex_listdo(exarg_T *eap)
i++;
// execute the command
if (execute) {
- do_cmdline(eap->arg, eap->getline, eap->cookie,
- DOCMD_VERBOSE + DOCMD_NOWAIT);
+ do_cmdline(eap->arg, eap->getline, eap->cookie, DOCMD_VERBOSE + DOCMD_NOWAIT);
}
if (eap->cmdidx == CMD_bufdo) {
@@ -1462,10 +1440,10 @@ void ex_listdo(exarg_T *eap)
// Go to the next buffer. Clear 'shm' to avoid that the file
// message overwrites any output from the command.
- p_shm_save = vim_strsave(p_shm);
+ p_shm_save = (char *)vim_strsave(p_shm);
set_option_value("shm", 0L, "", 0);
goto_buffer(eap, DOBUF_FIRST, FORWARD, next_fnum);
- set_option_value("shm", 0L, (char *)p_shm_save, 0);
+ set_option_value("shm", 0L, p_shm_save, 0);
xfree(p_shm_save);
// If autocommands took us elsewhere, quit here.
@@ -1483,7 +1461,13 @@ void ex_listdo(exarg_T *eap)
size_t qf_idx = qf_get_cur_idx(eap);
+ // Clear 'shm' to avoid that the file message overwrites
+ // any output from the command.
+ p_shm_save = (char *)vim_strsave(p_shm);
+ set_option_value("shm", 0L, "", 0);
ex_cnext(eap);
+ set_option_value("shm", 0L, p_shm_save, 0);
+ xfree(p_shm_save);
// If jumping to the next quickfix entry fails, quit here.
if (qf_get_cur_idx(eap) == qf_idx) {
@@ -1524,12 +1508,11 @@ void ex_listdo(exarg_T *eap)
// buffer was opened while Syntax autocommands were disabled,
// need to trigger them now.
if (buf == curbuf) {
- apply_autocmds(EVENT_SYNTAX, curbuf->b_p_syn,
- curbuf->b_fname, true, curbuf);
+ apply_autocmds(EVENT_SYNTAX, (char *)curbuf->b_p_syn, curbuf->b_fname, true,
+ curbuf);
} else {
aucmd_prepbuf(&aco, buf);
- apply_autocmds(EVENT_SYNTAX, buf->b_p_syn,
- buf->b_fname, true, buf);
+ apply_autocmds(EVENT_SYNTAX, (char *)buf->b_p_syn, buf->b_fname, true, buf);
aucmd_restbuf(&aco);
}
@@ -1546,7 +1529,7 @@ void ex_listdo(exarg_T *eap)
///
/// @param after: where to add: 0 = before first one
/// @param will_edit will edit adding argument
-static void alist_add_list(int count, char_u **files, int after, bool will_edit)
+static void alist_add_list(int count, char **files, int after, bool will_edit)
FUNC_ATTR_NONNULL_ALL
{
int old_argcount = ARGCOUNT;
@@ -1564,7 +1547,7 @@ static void alist_add_list(int count, char_u **files, int after, bool will_edit)
}
for (int i = 0; i < count; i++) {
const int flags = BLN_LISTED | (will_edit ? BLN_CURBUF : 0);
- ARGLIST[after + i].ae_fname = files[i];
+ ARGLIST[after + i].ae_fname = (char_u *)files[i];
ARGLIST[after + i].ae_fnum = buflist_add(files[i], flags);
}
ALIST(curwin)->al_ga.ga_len += count;
@@ -1577,7 +1560,7 @@ static void alist_add_list(int count, char_u **files, int after, bool will_edit)
// Function given to ExpandGeneric() to obtain the possible arguments of the
// argedit and argdelete commands.
-char_u *get_arglist_name(expand_T *xp FUNC_ATTR_UNUSED, int idx)
+char *get_arglist_name(expand_T *xp FUNC_ATTR_UNUSED, int idx)
{
if (idx >= ARGCOUNT) {
return NULL;
@@ -1588,9 +1571,9 @@ char_u *get_arglist_name(expand_T *xp FUNC_ATTR_UNUSED, int idx)
/// ":compiler[!] {name}"
void ex_compiler(exarg_T *eap)
{
- char_u *buf;
- char_u *old_cur_comp = NULL;
- char_u *p;
+ char *buf;
+ char *old_cur_comp = NULL;
+ char *p;
if (*eap->arg == NUL) {
// List all compiler scripts.
@@ -1609,20 +1592,20 @@ void ex_compiler(exarg_T *eap)
// plugin will then skip the settings. Afterwards set
// "b:current_compiler" and restore "current_compiler".
// Explicitly prepend "g:" to make it work in a function.
- old_cur_comp = get_var_value("g:current_compiler");
+ old_cur_comp = (char *)get_var_value("g:current_compiler");
if (old_cur_comp != NULL) {
- old_cur_comp = vim_strsave(old_cur_comp);
+ old_cur_comp = xstrdup(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);
- snprintf((char *)buf, bufsize, "compiler/%s.vim", eap->arg);
- if (source_runtime((char *)buf, DIP_ALL) == FAIL) {
+ snprintf(buf, bufsize, "compiler/%s.vim", eap->arg);
+ if (source_runtime(buf, DIP_ALL) == FAIL) {
// Try lua compiler
- snprintf((char *)buf, bufsize, "compiler/%s.lua", eap->arg);
- if (source_runtime((char *)buf, DIP_ALL) == FAIL) {
+ snprintf(buf, bufsize, "compiler/%s.lua", eap->arg);
+ if (source_runtime(buf, DIP_ALL) == FAIL) {
semsg(_("E666: compiler not supported: %s"), eap->arg);
}
}
@@ -1631,7 +1614,7 @@ void ex_compiler(exarg_T *eap)
do_cmdline_cmd(":delcommand CompilerSet");
// Set "b:current_compiler" from "current_compiler".
- p = get_var_value("g:current_compiler");
+ p = (char *)get_var_value("g:current_compiler");
if (p != NULL) {
set_internal_string_var("b:current_compiler", p);
}
@@ -1639,8 +1622,7 @@ void ex_compiler(exarg_T *eap)
// Restore "current_compiler" for ":compiler {name}".
if (!eap->forceit) {
if (old_cur_comp != NULL) {
- set_internal_string_var("g:current_compiler",
- old_cur_comp);
+ set_internal_string_var("g:current_compiler", old_cur_comp);
xfree(old_cur_comp);
} else {
do_unlet(S_LEN("g:current_compiler"), true);
@@ -1649,135 +1631,17 @@ void ex_compiler(exarg_T *eap)
}
}
-
/// ":options"
void ex_options(exarg_T *eap)
{
- os_setenv("OPTWIN_CMD", cmdmod.tab ? "tab" : "", 1);
- os_setenv("OPTWIN_CMD",
- cmdmod.tab ? "tab" :
- (cmdmod.split & WSP_VERT) ? "vert" : "", 1);
- cmd_source((char_u *)SYS_OPTWIN_FILE, NULL);
-}
-
-// Detect Python 3 or 2, and initialize 'pyxversion'.
-void init_pyxversion(void)
-{
- if (p_pyx == 0) {
- if (eval_has_provider("python3")) {
- p_pyx = 3;
- } else if (eval_has_provider("python")) {
- p_pyx = 2;
- }
- }
-}
-
-// Does a file contain one of the following strings at the beginning of any
-// line?
-// "#!(any string)python2" => returns 2
-// "#!(any string)python3" => returns 3
-// "# requires python 2.x" => returns 2
-// "# requires python 3.x" => returns 3
-// otherwise return 0.
-static int requires_py_version(char_u *filename)
-{
- FILE *file;
- int requires_py_version = 0;
- int i, lines;
-
- lines = (int)p_mls;
- if (lines < 0) {
- lines = 5;
- }
-
- file = os_fopen((char *)filename, "r");
- if (file != NULL) {
- for (i = 0; i < lines; i++) {
- if (vim_fgets(IObuff, IOSIZE, file)) {
- break;
- }
- if (i == 0 && IObuff[0] == '#' && IObuff[1] == '!') {
- // Check shebang.
- if (strstr((char *)IObuff + 2, "python2") != NULL) {
- requires_py_version = 2;
- break;
- }
- if (strstr((char *)IObuff + 2, "python3") != NULL) {
- requires_py_version = 3;
- break;
- }
- }
- IObuff[21] = '\0';
- if (STRCMP("# requires python 2.x", IObuff) == 0) {
- requires_py_version = 2;
- break;
- }
- if (STRCMP("# requires python 3.x", IObuff) == 0) {
- requires_py_version = 3;
- break;
- }
- }
- fclose(file);
- }
- return requires_py_version;
-}
-
-
-// Source a python file using the requested python version.
-static void source_pyx_file(exarg_T *eap, char_u *fname)
-{
- exarg_T ex;
- long int v = requires_py_version(fname);
+ char buf[500];
+ bool multi_mods = 0;
- init_pyxversion();
- if (v == 0) {
- // user didn't choose a preference, 'pyx' is used
- v = p_pyx;
- }
-
- // now source, if required python version is not supported show
- // unobtrusive message.
- if (eap == NULL) {
- memset(&ex, 0, sizeof(ex));
- } else {
- ex = *eap;
- }
- ex.arg = fname;
- ex.cmd = (char_u *)(v == 2 ? "pyfile" : "pyfile3");
+ buf[0] = NUL;
+ (void)add_win_cmd_modifers(buf, &cmdmod, &multi_mods);
- if (v == 2) {
- ex_pyfile(&ex);
- } else {
- ex_py3file(&ex);
- }
-}
-
-// ":pyxfile {fname}"
-void ex_pyxfile(exarg_T *eap)
-{
- source_pyx_file(eap, eap->arg);
-}
-
-// ":pyx"
-void ex_pyx(exarg_T *eap)
-{
- init_pyxversion();
- if (p_pyx == 2) {
- ex_python(eap);
- } else {
- ex_python3(eap);
- }
-}
-
-// ":pyxdo"
-void ex_pyxdo(exarg_T *eap)
-{
- init_pyxversion();
- if (p_pyx == 2) {
- ex_pydo(eap);
- } else {
- ex_pydo3(eap);
- }
+ os_setenv("OPTWIN_CMD", buf, 1);
+ cmd_source(SYS_OPTWIN_FILE, NULL);
}
/// ":source [{fname}]"
@@ -1786,7 +1650,7 @@ void ex_source(exarg_T *eap)
cmd_source(eap->arg, eap);
}
-static void cmd_source(char_u *fname, exarg_T *eap)
+static void cmd_source(char *fname, exarg_T *eap)
{
if (eap != NULL && *fname == NUL) {
cmd_source_buffer(eap);
@@ -1798,11 +1662,11 @@ static void cmd_source(char_u *fname, exarg_T *eap)
// - after ":argdo", ":windo" or ":bufdo"
// - another command follows
// - inside a loop
- openscript(fname, global_busy || listcmd_busy || eap->nextcmd != NULL
+ openscript((char_u *)fname, global_busy || listcmd_busy || eap->nextcmd != NULL
|| eap->cstack->cs_idx >= 0);
// ":source" read ex commands
- } else if (do_source((char *)fname, false, DOSO_NONE) == FAIL) {
+ } else if (do_source(fname, false, DOSO_NONE) == FAIL) {
semsg(_(e_notopen), fname);
}
}
@@ -1823,8 +1687,8 @@ static bool concat_continued_line(garray_T *const ga, const int init_growsize,
const char_u *const p, size_t len)
FUNC_ATTR_NONNULL_ALL
{
- const char_u *const line = skipwhite_len(p, len);
- len -= (size_t)(line - p);
+ const char *const line = (char *)skipwhite_len(p, len);
+ len -= (size_t)((char_u *)line - p);
// Skip lines starting with '\" ', concat lines starting with '\'
if (len >= 3 && STRNCMP(line, "\"\\ ", 3) == 0) {
return true;
@@ -1832,9 +1696,9 @@ static bool concat_continued_line(garray_T *const ga, const int init_growsize,
return false;
}
if (ga->ga_len > init_growsize) {
- ga_set_growsize(ga, MAX(ga->ga_len, 8000));
+ ga_set_growsize(ga, MIN(ga->ga_len, 8000));
}
- ga_concat_len(ga, (const char *)line + 1, len - 1);
+ ga_concat_len(ga, line + 1, len - 1);
return true;
}
@@ -1851,14 +1715,15 @@ linenr_T *source_breakpoint(void *cookie)
return &((struct source_cookie *)cookie)->breakpoint;
}
-/// Return the address holding the debug tick for a source cookie.
+/// @return the address holding the debug tick for a source cookie.
int *source_dbg_tick(void *cookie)
{
return &((struct source_cookie *)cookie)->dbg_tick;
}
-/// Return the nesting level for a source cookie.
+/// @return the nesting level for a source cookie.
int source_level(void *cookie)
+ FUNC_ATTR_PURE
{
return ((struct source_cookie *)cookie)->level;
}
@@ -1883,7 +1748,7 @@ static FILE *fopen_noinh_readbin(char *filename)
}
typedef struct {
- char_u *buf;
+ char *buf;
size_t offset;
} GetStrLineCookie;
@@ -1892,25 +1757,25 @@ typedef struct {
///
/// @return pointer to allocated line, or NULL for end-of-file or
/// some error.
-static char_u *get_str_line(int c, void *cookie, int indent, bool do_concat)
+static char *get_str_line(int c, void *cookie, int indent, bool do_concat)
{
GetStrLineCookie *p = cookie;
if (STRLEN(p->buf) <= p->offset) {
return NULL;
}
- const char_u *line = p->buf + p->offset;
- const char_u *eol = skip_to_newline(line);
+ const char *line = p->buf + p->offset;
+ const char *eol = (char *)skip_to_newline((char_u *)line);
garray_T ga;
ga_init(&ga, sizeof(char_u), 400);
- ga_concat_len(&ga, (const char *)line, (size_t)(eol - line));
+ ga_concat_len(&ga, line, (size_t)(eol - line));
if (do_concat && vim_strchr(p_cpo, CPO_CONCAT) == NULL) {
while (eol[0] != NUL) {
line = eol + 1;
- const char_u *const next_eol = skip_to_newline(line);
- if (!concat_continued_line(&ga, 400, line, (size_t)(next_eol - line))) {
+ const char_u *const next_eol = skip_to_newline((char_u *)line);
+ if (!concat_continued_line(&ga, 400, (char_u *)line, (size_t)(next_eol - (char_u *)line))) {
break;
}
- eol = next_eol;
+ eol = (char *)next_eol;
}
}
ga_append(&ga, NUL);
@@ -1922,32 +1787,33 @@ static char_u *get_str_line(int c, void *cookie, int indent, bool do_concat)
///
/// @param name File name of the script. NULL for anonymous :source.
/// @param[out] sid_out SID of the new item.
-/// @return pointer to the created script item.
-scriptitem_T *new_script_item(char_u *const name, scid_T *const sid_out)
+///
+/// @return pointer to the created script item.
+scriptitem_T *new_script_item(char *const name, scid_T *const sid_out)
{
static scid_T last_current_SID = 0;
const scid_T sid = ++last_current_SID;
if (sid_out != NULL) {
*sid_out = sid;
}
- ga_grow(&script_items, (int)(sid - script_items.ga_len));
+ ga_grow(&script_items, sid - script_items.ga_len);
while (script_items.ga_len < sid) {
script_items.ga_len++;
SCRIPT_ITEM(script_items.ga_len).sn_name = NULL;
SCRIPT_ITEM(script_items.ga_len).sn_prof_on = false;
}
- SCRIPT_ITEM(sid).sn_name = name;
+ SCRIPT_ITEM(sid).sn_name = (char_u *)name;
new_script_vars(sid); // Allocate the local script variables to use for this script.
return &SCRIPT_ITEM(sid);
}
static int source_using_linegetter(void *cookie, LineGetter fgetline, const char *traceback_name)
{
- char_u *save_sourcing_name = sourcing_name;
+ char *save_sourcing_name = sourcing_name;
linenr_T save_sourcing_lnum = sourcing_lnum;
- char_u sourcing_name_buf[256];
+ char sourcing_name_buf[256];
if (save_sourcing_name == NULL) {
- sourcing_name = (char_u *)traceback_name;
+ sourcing_name = (char *)traceback_name;
} else {
snprintf((char *)sourcing_name_buf, sizeof(sourcing_name_buf),
"%s called at %s:%" PRIdLINENR, traceback_name, save_sourcing_name,
@@ -1957,7 +1823,9 @@ static int source_using_linegetter(void *cookie, LineGetter fgetline, const char
sourcing_lnum = 0;
const sctx_T save_current_sctx = current_sctx;
- current_sctx.sc_sid = SID_STR;
+ if (current_sctx.sc_sid != SID_LUA) {
+ current_sctx.sc_sid = SID_STR;
+ }
current_sctx.sc_seq = 0;
current_sctx.sc_lnum = save_sourcing_lnum;
funccal_entry_T entry;
@@ -1984,7 +1852,7 @@ static void cmd_source_buffer(const exarg_T *const eap)
for (linenr_T curr_lnum = eap->line1; curr_lnum <= final_lnum; curr_lnum++) {
// Adjust growsize to current length to speed up concatenating many lines.
if (ga.ga_len > 400) {
- ga_set_growsize(&ga, MAX(ga.ga_len, 8000));
+ ga_set_growsize(&ga, MIN(ga.ga_len, 8000));
}
ga_concat(&ga, (char *)ml_get(curr_lnum));
ga_append(&ga, NL);
@@ -2011,7 +1879,7 @@ static void cmd_source_buffer(const exarg_T *const eap)
int do_source_str(const char *cmd, const char *traceback_name)
{
GetStrLineCookie cookie = {
- .buf = (char_u *)cmd,
+ .buf = (char *)cmd,
.offset = 0,
};
return source_using_linegetter((void *)&cookie, get_str_line, traceback_name);
@@ -2028,32 +1896,31 @@ int do_source_str(const char *cmd, const char *traceback_name)
/// @param check_other check for .vimrc and _vimrc
/// @param is_vimrc DOSO_ value
///
-/// @return FAIL if file could not be opened, OK otherwise
+/// @return FAIL if file could not be opened, OK otherwise
int do_source(char *fname, int check_other, int is_vimrc)
{
struct source_cookie cookie;
- char_u *save_sourcing_name;
+ char *save_sourcing_name;
linenr_T save_sourcing_lnum;
- char_u *p;
- char_u *fname_exp;
- char_u *firstline = NULL;
+ char *p;
+ char *fname_exp;
+ uint8_t *firstline = NULL;
int retval = FAIL;
- static int last_current_SID_seq = 0;
int save_debug_break_level = debug_break_level;
scriptitem_T *si = NULL;
proftime_T wait_start;
bool trigger_source_post = false;
- p = expand_env_save((char_u *)fname);
+ p = expand_env_save(fname);
if (p == NULL) {
return retval;
}
- fname_exp = (char_u *)fix_fname((char *)p);
+ fname_exp = fix_fname(p);
xfree(p);
if (fname_exp == NULL) {
return retval;
}
- if (os_isdir(fname_exp)) {
+ if (os_isdir((char_u *)fname_exp)) {
smsg(_("Cannot source a directory: \"%s\""), fname);
goto theend;
}
@@ -2073,7 +1940,7 @@ int do_source(char *fname, int check_other, int is_vimrc)
// Apply SourcePre autocommands, they may get the file.
apply_autocmds(EVENT_SOURCEPRE, fname_exp, fname_exp, false, curbuf);
- cookie.fp = fopen_noinh_readbin((char *)fname_exp);
+ cookie.fp = fopen_noinh_readbin(fname_exp);
if (cookie.fp == NULL && check_other) {
// Try again, replacing file name ".vimrc" by "_vimrc" or vice versa,
// and ".exrc" by "_exrc" or vice versa.
@@ -2081,12 +1948,12 @@ int do_source(char *fname, int check_other, int is_vimrc)
if ((*p == '.' || *p == '_')
&& (STRICMP(p + 1, "nvimrc") == 0 || STRICMP(p + 1, "exrc") == 0)) {
*p = (*p == '_') ? '.' : '_';
- cookie.fp = fopen_noinh_readbin((char *)fname_exp);
+ cookie.fp = fopen_noinh_readbin(fname_exp);
}
}
if (cookie.fp == NULL) {
- if (p_verbose > 0) {
+ if (p_verbose > 1) {
verbose_enter();
if (sourcing_name == NULL) {
smsg(_("could not source \"%s\""), fname);
@@ -2113,7 +1980,7 @@ int do_source(char *fname, int check_other, int is_vimrc)
verbose_leave();
}
if (is_vimrc == DOSO_VIMRC) {
- vimrc_found((char *)fname_exp, "MYVIMRC");
+ vimrc_found(fname_exp, "MYVIMRC");
}
#ifdef USE_CRNL
@@ -2131,7 +1998,7 @@ int do_source(char *fname, int check_other, int is_vimrc)
cookie.finished = false;
// Check if this script has a breakpoint.
- cookie.breakpoint = dbg_find_breakpoint(true, fname_exp, (linenr_T)0);
+ cookie.breakpoint = dbg_find_breakpoint(true, (char_u *)fname_exp, (linenr_T)0);
cookie.fname = fname_exp;
cookie.dbg_tick = debug_tick;
@@ -2162,37 +2029,8 @@ int do_source(char *fname, int check_other, int is_vimrc)
funccal_entry_T funccalp_entry;
save_funccal(&funccalp_entry);
- // Check if this script was sourced before to find its SID.
- // If it's new, generate a new SID.
- // Always use a new sequence number.
const sctx_T save_current_sctx = current_sctx;
- current_sctx.sc_seq = ++last_current_SID_seq;
- current_sctx.sc_lnum = 0;
- FileID file_id;
- bool file_id_ok = os_fileid((char *)fname_exp, &file_id);
- assert(script_items.ga_len >= 0);
- for (current_sctx.sc_sid = script_items.ga_len; current_sctx.sc_sid > 0;
- current_sctx.sc_sid--) {
- si = &SCRIPT_ITEM(current_sctx.sc_sid);
- // Compare dev/ino when possible, it catches symbolic links.
- // Also compare file names, the inode may change when the file was edited.
- bool file_id_equal = file_id_ok && si->file_id_valid
- && os_fileid_equal(&(si->file_id), &file_id);
- if (si->sn_name != NULL
- && (file_id_equal || fnamecmp(si->sn_name, fname_exp) == 0)) {
- break;
- }
- }
- if (current_sctx.sc_sid == 0) {
- si = new_script_item(fname_exp, &current_sctx.sc_sid);
- fname_exp = vim_strsave(si->sn_name); // used for autocmd
- if (file_id_ok) {
- si->file_id_valid = true;
- si->file_id = file_id;
- } else {
- si->file_id_valid = false;
- }
- }
+ si = get_current_script_id((char_u *)fname_exp, &current_sctx);
if (l_do_profiling == PROF_YES) {
bool forceit = false;
@@ -2212,34 +2050,32 @@ int do_source(char *fname, int check_other, int is_vimrc)
cookie.conv.vc_type = CONV_NONE; // no conversion
// Read the first line so we can check for a UTF-8 BOM.
- firstline = getsourceline(0, (void *)&cookie, 0, true);
+ firstline = (uint8_t *)getsourceline(0, (void *)&cookie, 0, true);
if (firstline != NULL && STRLEN(firstline) >= 3 && firstline[0] == 0xef
&& firstline[1] == 0xbb && firstline[2] == 0xbf) {
// Found BOM; setup conversion, skip over BOM and recode the line.
convert_setup(&cookie.conv, (char_u *)"utf-8", p_enc);
- p = string_convert(&cookie.conv, firstline + 3, NULL);
+ p = (char *)string_convert(&cookie.conv, (char_u *)firstline + 3, NULL);
if (p == NULL) {
- p = vim_strsave(firstline + 3);
+ p = xstrdup((char *)firstline + 3);
}
xfree(firstline);
- firstline = p;
+ firstline = (uint8_t *)p;
}
- if (path_with_extension((const char *)fname, "lua")) {
- // TODO(shadmansaleh): Properly handle :verbose for lua
- // For now change currennt_sctx before sourcing lua files
- // So verbose doesn't say everything was done in line 1 since we don't know
+ if (path_with_extension((const char *)fname_exp, "lua")) {
const sctx_T current_sctx_backup = current_sctx;
const linenr_T sourcing_lnum_backup = sourcing_lnum;
+ current_sctx.sc_sid = SID_LUA;
current_sctx.sc_lnum = 0;
sourcing_lnum = 0;
// Source the file as lua
- nlua_exec_file((const char *)fname);
+ nlua_exec_file((const char *)fname_exp);
current_sctx = current_sctx_backup;
sourcing_lnum = sourcing_lnum_backup;
} else {
// Call do_cmdline, which will call getsourceline() to get the lines.
- do_cmdline(firstline, getsourceline, (void *)&cookie,
+ do_cmdline((char *)firstline, getsourceline, (void *)&cookie,
DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_REPEAT);
}
retval = OK;
@@ -2306,6 +2142,42 @@ theend:
return retval;
}
+/// Check if fname was sourced before to finds its SID.
+/// If it's new, generate a new SID.
+///
+/// @param[in] fname file path of script
+/// @param[out] ret_sctx sctx of this script
+scriptitem_T *get_current_script_id(char_u *fname, sctx_T *ret_sctx)
+{
+ static int last_current_SID_seq = 0;
+
+ sctx_T script_sctx = { .sc_seq = ++last_current_SID_seq,
+ .sc_lnum = 0,
+ .sc_sid = 0 };
+ scriptitem_T *si = NULL;
+
+ assert(script_items.ga_len >= 0);
+ for (script_sctx.sc_sid = script_items.ga_len; script_sctx.sc_sid > 0; script_sctx.sc_sid--) {
+ // We used to check inode here, but that doesn't work:
+ // - If a script is edited and written, it may get a different
+ // inode number, even though to the user it is the same script.
+ // - If a script is deleted and another script is written, with a
+ // different name, the inode may be re-used.
+ si = &SCRIPT_ITEM(script_sctx.sc_sid);
+ if (si->sn_name != NULL && FNAMECMP(si->sn_name, fname) == 0) {
+ // Found it!
+ break;
+ }
+ }
+ if (script_sctx.sc_sid == 0) {
+ si = new_script_item((char *)vim_strsave(fname), &script_sctx.sc_sid);
+ }
+ if (ret_sctx != NULL) {
+ *ret_sctx = script_sctx;
+ }
+
+ return si;
+}
/// ":scriptnames"
void ex_scriptnames(exarg_T *eap)
@@ -2315,7 +2187,7 @@ void ex_scriptnames(exarg_T *eap)
if (eap->line2 < 1 || eap->line2 > script_items.ga_len) {
emsg(_(e_invarg));
} else {
- eap->arg = SCRIPT_ITEM(eap->line2).sn_name;
+ eap->arg = (char *)SCRIPT_ITEM(eap->line2).sn_name;
do_exedit(eap, NULL);
}
return;
@@ -2323,9 +2195,13 @@ 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, (char *)SCRIPT_ITEM(i).sn_name, (char *)NameBuff, MAXPATHL, true);
+ vim_snprintf((char *)IObuff, IOSIZE, "%3d: %s", i, NameBuff);
+ if (!message_filtered(IObuff)) {
+ msg_putchar('\n');
+ msg_outtrans((char *)IObuff);
+ line_breakcheck();
+ }
}
}
}
@@ -2370,7 +2246,7 @@ char_u *get_scriptname(LastSet last_set, bool *should_free)
case SID_STR:
return (char_u *)_("anonymous :source");
default: {
- char_u *const sname = SCRIPT_ITEM(last_set.script_ctx.sc_sid).sn_name;
+ char *const sname = (char *)SCRIPT_ITEM(last_set.script_ctx.sc_sid).sn_name;
if (sname == NULL) {
snprintf((char *)IObuff, IOSIZE, _("anonymous :source (script id %d)"),
last_set.script_ctx.sc_sid);
@@ -2378,7 +2254,7 @@ char_u *get_scriptname(LastSet last_set, bool *should_free)
}
*should_free = true;
- return home_replace_save(NULL, sname);
+ return (char_u *)home_replace_save(NULL, sname);
}
}
}
@@ -2394,27 +2270,27 @@ void free_scriptnames(void)
#endif
linenr_T get_sourced_lnum(LineGetter fgetline, void *cookie)
+ FUNC_ATTR_PURE
{
return fgetline == getsourceline
? ((struct source_cookie *)cookie)->sourcing_lnum
: sourcing_lnum;
}
-
/// Get one full line from a sourced file.
/// Called by do_cmdline() when it's called from do_source().
///
/// @return pointer to the line in allocated memory, or NULL for end-of-file or
/// some error.
-char_u *getsourceline(int c, void *cookie, int indent, bool do_concat)
+char *getsourceline(int c, void *cookie, int indent, bool do_concat)
{
struct source_cookie *sp = (struct source_cookie *)cookie;
- char_u *line;
- char_u *p;
+ char *line;
+ char *p;
// If breakpoints have been added/deleted need to check for it.
if (sp->dbg_tick < debug_tick) {
- sp->breakpoint = dbg_find_breakpoint(true, sp->fname, sourcing_lnum);
+ sp->breakpoint = dbg_find_breakpoint(true, (char_u *)sp->fname, sourcing_lnum);
sp->dbg_tick = debug_tick;
}
if (do_profiling == PROF_YES) {
@@ -2454,9 +2330,9 @@ char_u *getsourceline(int c, void *cookie, int indent, bool do_concat)
garray_T ga;
ga_init(&ga, (int)sizeof(char_u), 400);
- ga_concat(&ga, (char *)line);
+ ga_concat(&ga, line);
while (sp->nextline != NULL
- && concat_continued_line(&ga, 400, sp->nextline,
+ && concat_continued_line(&ga, 400, (char_u *)sp->nextline,
STRLEN(sp->nextline))) {
xfree(sp->nextline);
sp->nextline = get_one_sourceline(sp);
@@ -2468,10 +2344,10 @@ char_u *getsourceline(int c, void *cookie, int indent, bool do_concat)
}
if (line != NULL && sp->conv.vc_type != CONV_NONE) {
- char_u *s;
+ char *s;
// Convert the encoding of the script line.
- s = string_convert(&sp->conv, line, NULL);
+ s = (char *)string_convert(&sp->conv, (char_u *)line, NULL);
if (s != NULL) {
xfree(line);
line = s;
@@ -2480,21 +2356,21 @@ char_u *getsourceline(int c, void *cookie, int indent, bool do_concat)
// Did we encounter a breakpoint?
if (sp->breakpoint != 0 && sp->breakpoint <= sourcing_lnum) {
- dbg_breakpoint(sp->fname, sourcing_lnum);
+ dbg_breakpoint((char_u *)sp->fname, sourcing_lnum);
// Find next breakpoint.
- sp->breakpoint = dbg_find_breakpoint(true, sp->fname, sourcing_lnum);
+ sp->breakpoint = dbg_find_breakpoint(true, (char_u *)sp->fname, sourcing_lnum);
sp->dbg_tick = debug_tick;
}
return line;
}
-static char_u *get_one_sourceline(struct source_cookie *sp)
+static char *get_one_sourceline(struct source_cookie *sp)
{
garray_T ga;
int len;
int c;
- char_u *buf;
+ char *buf;
#ifdef USE_CRNL
int has_cr; // CR-LF found
#endif
@@ -2508,11 +2384,11 @@ static char_u *get_one_sourceline(struct source_cookie *sp)
for (;;) {
// make room to read at least 120 (more) characters
ga_grow(&ga, 120);
- buf = (char_u *)ga.ga_data;
+ buf = ga.ga_data;
retry:
errno = 0;
- if (fgets((char *)buf + ga.ga_len, ga.ga_maxlen - ga.ga_len,
+ if (fgets(buf + ga.ga_len, ga.ga_maxlen - ga.ga_len,
sp->fp) == NULL) {
if (errno == EINTR) {
goto retry;
@@ -2584,7 +2460,7 @@ retry:
}
if (have_read) {
- return (char_u *)ga.ga_data;
+ return ga.ga_data;
}
xfree(ga.ga_data);
@@ -2607,8 +2483,7 @@ void script_line_start(void)
if (si->sn_prof_on && sourcing_lnum >= 1) {
// Grow the array before starting the timer, so that the time spent
// here isn't counted.
- (void)ga_grow(&si->sn_prl_ga,
- (int)(sourcing_lnum - si->sn_prl_ga.ga_len));
+ (void)ga_grow(&si->sn_prl_ga, sourcing_lnum - si->sn_prl_ga.ga_len);
si->sn_prl_idx = sourcing_lnum - 1;
while (si->sn_prl_ga.ga_len <= si->sn_prl_idx
&& si->sn_prl_ga.ga_len < si->sn_prl_ga.ga_maxlen) {
@@ -2670,7 +2545,7 @@ void script_line_end(void)
void ex_scriptencoding(exarg_T *eap)
{
struct source_cookie *sp;
- char_u *name;
+ char *name;
if (!getline_equal(eap->getline, eap->cookie, getsourceline)) {
emsg(_("E167: :scriptencoding used outside of a sourced file"));
@@ -2678,14 +2553,14 @@ void ex_scriptencoding(exarg_T *eap)
}
if (*eap->arg != NUL) {
- name = enc_canonize(eap->arg);
+ name = (char *)enc_canonize((char_u *)eap->arg);
} else {
name = eap->arg;
}
// Setup for conversion from the specified encoding to 'encoding'.
sp = (struct source_cookie *)getline_cookie(eap->getline, eap->cookie);
- convert_setup(&sp->conv, name, p_enc);
+ convert_setup(&sp->conv, (char_u *)name, p_enc);
if (name != eap->arg) {
xfree(name);
@@ -2728,10 +2603,9 @@ void do_finish(exarg_T *eap, int reanimate)
}
}
-
-/// Return true when a sourced file had the ":finish" command: Don't give error
-/// message for missing ":endif".
-/// Return false when not sourcing a file.
+/// @return true when a sourced file had the ":finish" command: Don't give error
+/// message for missing ":endif".
+/// false when not sourcing a file.
bool source_finished(LineGetter fgetline, void *cookie)
{
return getline_equal(fgetline, cookie, getsourceline)
@@ -2768,8 +2642,8 @@ static char *get_locale_val(int what)
}
#endif
-// Return true when "lang" starts with a valid language name.
-// Rejects NULL, empty string, "C", "C.UTF-8" and others.
+/// @return true when "lang" starts with a valid language name.
+/// Rejects NULL, empty string, "C", "C.UTF-8" and others.
static bool is_valid_mess_lang(char *lang)
{
return lang != NULL && ASCII_ISALPHA(lang[0]) && ASCII_ISALPHA(lang[1]);
@@ -2806,21 +2680,21 @@ char *get_mess_lang(void)
// Complicated #if; matches with where get_mess_env() is used below.
#ifdef HAVE_WORKING_LIBINTL
/// Get the language used for messages from the environment.
-static char_u *get_mess_env(void)
+static char *get_mess_env(void)
{
- char_u *p;
+ char *p;
- p = (char_u *)os_getenv("LC_ALL");
+ p = (char *)os_getenv("LC_ALL");
if (p == NULL) {
- p = (char_u *)os_getenv("LC_MESSAGES");
+ p = (char *)os_getenv("LC_MESSAGES");
if (p == NULL) {
- p = (char_u *)os_getenv("LANG");
+ p = (char *)os_getenv("LANG");
if (p != NULL && ascii_isdigit(*p)) {
p = NULL; // ignore something like "1043"
}
# ifdef HAVE_GET_LOCALE_VAL
if (p == NULL) {
- p = (char_u *)get_locale_val(LC_CTYPE);
+ p = get_locale_val(LC_CTYPE);
}
# endif
}
@@ -2830,7 +2704,6 @@ static char_u *get_mess_env(void)
#endif
-
/// Set the "v:lang" variable according to the current locale setting.
/// Also do "v:lc_time"and "v:ctype".
void set_lang_var(void)
@@ -2848,7 +2721,7 @@ void set_lang_var(void)
// When LC_MESSAGES isn't defined use the value from $LC_MESSAGES, fall
// back to LC_CTYPE if it's empty.
#ifdef HAVE_WORKING_LIBINTL
- loc = (char *)get_mess_env();
+ loc = get_mess_env();
#elif defined(LC_MESSAGES)
loc = get_locale_val(LC_MESSAGES);
#else
@@ -2872,16 +2745,15 @@ void set_lang_var(void)
}
#ifdef HAVE_WORKING_LIBINTL
-///
+
/// ":language": Set the language (locale).
///
/// @param eap
-///
void ex_language(exarg_T *eap)
{
char *loc;
- char_u *p;
- char_u *name;
+ char *p;
+ char *name;
int what = LC_ALL;
char *whatstr = "";
# ifdef LC_MESSAGES
@@ -2895,7 +2767,7 @@ void ex_language(exarg_T *eap)
// Check for "messages {name}", "ctype {name}" or "time {name}" argument.
// Allow abbreviation, but require at least 3 characters to avoid
// confusion with a two letter language name "me" or "ct".
- p = skiptowhite(eap->arg);
+ p = (char *)skiptowhite((char_u *)eap->arg);
if ((*p == NUL || ascii_iswhite(*p)) && p - eap->arg >= 3) {
if (STRNICMP(eap->arg, "messages", p - eap->arg) == 0) {
what = VIM_LC_MESSAGES;
@@ -2917,17 +2789,13 @@ void ex_language(exarg_T *eap)
}
if (*name == NUL) {
-# ifdef HAVE_WORKING_LIBINTL
if (what == VIM_LC_MESSAGES) {
p = get_mess_env();
} else {
-# endif
- p = (char_u *)setlocale(what, NULL);
-# ifdef HAVE_WORKING_LIBINTL
- }
-# endif
+ p = setlocale(what, NULL);
+ }
if (p == NULL || *p == NUL) {
- p = (char_u *)"Unknown";
+ p = "Unknown";
}
smsg(_("Current %slanguage: \"%s\""), whatstr, p);
} else {
@@ -2936,7 +2804,7 @@ void ex_language(exarg_T *eap)
loc = "";
} else {
# endif
- loc = setlocale(what, (char *)name);
+ loc = setlocale(what, name);
# ifdef LC_NUMERIC
// Make sure strtod() uses a decimal point, not a comma.
setlocale(LC_NUMERIC, "C");
@@ -2961,14 +2829,14 @@ void ex_language(exarg_T *eap)
// Tell gettext() what to translate to. It apparently doesn't
// use the currently effective locale.
if (what == LC_ALL) {
- os_setenv("LANG", (char *)name, 1);
+ os_setenv("LANG", name, 1);
// Clear $LANGUAGE because GNU gettext uses it.
os_setenv("LANGUAGE", "", 1);
}
if (what != LC_CTYPE) {
- os_setenv("LC_MESSAGES", (char *)name, 1);
- set_helplang_default((char *)name);
+ os_setenv("LC_MESSAGES", name, 1);
+ set_helplang_default(name);
}
}
@@ -2979,24 +2847,24 @@ void ex_language(exarg_T *eap)
}
}
-
-static char_u **locales = NULL; // Array of all available locales
+static char **locales = NULL; // Array of all available locales
# ifndef WIN32
static bool did_init_locales = false;
-/// Return an array of strings for all available locales + NULL for the
-/// last element. Return NULL in case of error.
-static char_u **find_locales(void)
+/// @return an array of strings for all available locales + NULL for the
+/// last element or,
+/// NULL in case of error.
+static char **find_locales(void)
{
garray_T locales_ga;
- char_u *loc;
+ char *loc;
char *saveptr = NULL;
// Find all available locales by running command "locale -a". If this
// doesn't work we won't have completion.
- char_u *locale_a = get_cmd_output((char_u *)"locale -a", NULL,
- kShellOptSilent, NULL);
+ char *locale_a = (char *)get_cmd_output((char_u *)"locale -a", NULL,
+ kShellOptSilent, NULL);
if (locale_a == NULL) {
return NULL;
}
@@ -3004,18 +2872,18 @@ static char_u **find_locales(void)
// Transform locale_a string where each locale is separated by "\n"
// into an array of locale strings.
- loc = (char_u *)os_strtok((char *)locale_a, "\n", &saveptr);
+ loc = os_strtok(locale_a, "\n", &saveptr);
while (loc != NULL) {
- loc = vim_strsave(loc);
- GA_APPEND(char_u *, &locales_ga, loc);
- loc = (char_u *)os_strtok(NULL, "\n", &saveptr);
+ loc = xstrdup(loc);
+ GA_APPEND(char *, &locales_ga, loc);
+ loc = os_strtok(NULL, "\n", &saveptr);
}
xfree(locale_a);
// Guarantee that .ga_data is NULL terminated
ga_grow(&locales_ga, 1);
((char_u **)locales_ga.ga_data)[locales_ga.ga_len] = NULL;
- return (char_u **)locales_ga.ga_data;
+ return locales_ga.ga_data;
}
# endif
@@ -3046,19 +2914,19 @@ void free_locales(void)
/// Function given to ExpandGeneric() to obtain the possible arguments of the
/// ":language" command.
-char_u *get_lang_arg(expand_T *xp, int idx)
+char *get_lang_arg(expand_T *xp, int idx)
{
if (idx == 0) {
- return (char_u *)"messages";
+ return "messages";
}
if (idx == 1) {
- return (char_u *)"ctype";
+ return "ctype";
}
if (idx == 2) {
- return (char_u *)"time";
+ return "time";
}
if (idx == 3) {
- return (char_u *)"collate";
+ return "collate";
}
init_locales();
@@ -3069,7 +2937,7 @@ char_u *get_lang_arg(expand_T *xp, int idx)
}
/// Function given to ExpandGeneric() to obtain the available locales.
-char_u *get_locales(expand_T *xp, int idx)
+char *get_locales(expand_T *xp, int idx)
{
init_locales();
if (locales == NULL) {
@@ -3080,7 +2948,6 @@ char_u *get_locales(expand_T *xp, int idx)
#endif
-
static void script_host_execute(char *name, exarg_T *eap)
{
size_t len;
@@ -3102,7 +2969,7 @@ static void script_host_execute_file(char *name, exarg_T *eap)
{
if (!eap->skip) {
uint8_t buffer[MAXPATHL];
- vim_FullName((char *)eap->arg, (char *)buffer, sizeof(buffer), false);
+ vim_FullName(eap->arg, (char *)buffer, sizeof(buffer), false);
list_T *args = tv_list_alloc(3);
// filename
@@ -3148,7 +3015,7 @@ void ex_drop(exarg_T *eap)
return;
}
- if (cmdmod.tab) {
+ if (cmdmod.cmod_tab) {
// ":tab drop file ...": open a tab for each argument that isn't
// edited in a window yet. It's like ":tab all" but without closing
// windows or tabs.
diff --git a/src/nvim/ex_cmds2.h b/src/nvim/ex_cmds2.h
index d426ff28f8..c463bfa5ab 100644
--- a/src/nvim/ex_cmds2.h
+++ b/src/nvim/ex_cmds2.h
@@ -6,7 +6,6 @@
#include "nvim/ex_docmd.h"
#include "nvim/runtime.h"
-
//
// flags for check_changed()
//
@@ -16,12 +15,8 @@
#define CCGD_ALLBUF 8 // may write all buffers
#define CCGD_EXCMD 16 // may suggest using !
-/// Also store the dev/ino, so that we don't have to stat() each
-/// script when going through the list.
typedef struct scriptitem_S {
char_u *sn_name;
- bool file_id_valid;
- FileID file_id;
bool sn_prof_on; ///< true when script is/was profiled
bool sn_pr_force; ///< forceit: profile functions in this script
proftime_T sn_pr_child; ///< time set when going into first child
diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h
index e5eab61f9e..052926fa1f 100644
--- a/src/nvim/ex_cmds_defs.h
+++ b/src/nvim/ex_cmds_defs.h
@@ -4,6 +4,7 @@
#include <stdbool.h>
#include <stdint.h>
+#include "nvim/eval/typval.h"
#include "nvim/normal.h"
#include "nvim/pos.h" // for linenr_T
#include "nvim/regexp_defs.h"
@@ -56,11 +57,14 @@
#define EX_BUFUNL 0x10000 // accepts unlisted buffer too
#define EX_ARGOPT 0x20000 // allow "++opt=val" argument
#define EX_SBOXOK 0x40000 // allowed in the sandbox
-#define EX_CMDWIN 0x80000 // allowed in cmdline window; when missing
- // disallows editing another buffer when
- // current buffer is locked
+#define EX_CMDWIN 0x80000 // allowed in cmdline window
#define EX_MODIFY 0x100000 // forbidden in non-'modifiable' buffer
#define EX_FLAGS 0x200000 // allow flags after count in argument
+#define EX_LOCK_OK 0x1000000 // command can be executed when textlock is
+ // set; when missing disallows editing another
+ // buffer when current buffer is locked
+#define EX_KEEPSCRIPT 0x4000000 // keep sctx of where command was invoked
+#define EX_PREVIEW 0x8000000 // allow incremental command preview
#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
@@ -85,19 +89,50 @@ typedef struct exarg exarg_T;
// behavior for bad character, "++bad=" argument
#define BAD_REPLACE '?' // replace it with '?' (default)
-#define BAD_KEEP -1 // leave it
-#define BAD_DROP -2 // erase it
+#define BAD_KEEP (-1) // leave it
+#define BAD_DROP (-2) // erase it
typedef void (*ex_func_T)(exarg_T *eap);
+typedef int (*ex_preview_func_T)(exarg_T *eap, long cmdpreview_ns, handle_T cmdpreview_bufnr);
-typedef char_u *(*LineGetter)(int, void *, int, bool);
+// NOTE: These possible could be removed and changed so that
+// Callback could take a "command" style string, and simply
+// execute that (instead of it being a function).
+//
+// But it's still a bit weird to do that.
+//
+// Another option would be that we just make a callback reference to
+// "execute($INPUT)" or something like that, so whatever the user
+// sends in via autocmds is just executed via this.
+//
+// However, that would probably have some performance cost (probably
+// very marginal, but still some cost either way).
+typedef enum {
+ CALLABLE_NONE,
+ CALLABLE_EX,
+ CALLABLE_CB,
+} AucmdExecutableType;
+
+typedef struct aucmd_executable_t AucmdExecutable;
+struct aucmd_executable_t {
+ AucmdExecutableType type;
+ union {
+ char *cmd;
+ Callback cb;
+ } callable;
+};
+
+#define AUCMD_EXECUTABLE_INIT { .type = CALLABLE_NONE }
+
+typedef char *(*LineGetter)(int, void *, int, bool);
/// Structure for command definition.
typedef struct cmdname {
- char_u *cmd_name; ///< Name of the command.
- ex_func_T cmd_func; ///< Function with implementation of this command.
- uint32_t cmd_argt; ///< Relevant flags from the declared above.
- cmd_addr_T cmd_addr_type; ///< Flag for address type
+ char *cmd_name; ///< Name of the command.
+ ex_func_T cmd_func; ///< Function with implementation of this command.
+ ex_preview_func_T cmd_preview_func; ///< Preview callback function of this command.
+ uint32_t cmd_argt; ///< Relevant flags from the declared above.
+ cmd_addr_T cmd_addr_type; ///< Flag for address type.
} CommandDefinition;
// A list used for saving values of "emsg_silent". Used by ex_try() to save the
@@ -143,10 +178,13 @@ enum {
/// Arguments used for Ex commands.
struct exarg {
- char_u *arg; ///< argument of the command
- char_u *nextcmd; ///< next command (NULL if none)
- char_u *cmd; ///< the name of the command (except for :make)
- char_u **cmdlinep; ///< pointer to pointer of allocated cmdline
+ char *arg; ///< argument of the command
+ char **args; ///< starting position of command arguments
+ size_t *arglens; ///< length of command arguments
+ size_t argc; ///< number of command arguments
+ char *nextcmd; ///< next command (NULL if none)
+ char *cmd; ///< the name of the command (except for :make)
+ char **cmdlinep; ///< pointer to pointer of allocated cmdline
cmdidx_T cmdidx; ///< the index for the command
uint32_t argt; ///< flags for the command
int skip; ///< don't execute the command, only parse it
@@ -156,7 +194,7 @@ struct exarg {
linenr_T line2; ///< the second line number or count
cmd_addr_T addr_type; ///< type of the count/range
int flags; ///< extra flags after count: EXFLAG_
- char_u *do_ecmd_cmd; ///< +command arg to be used in edited file
+ char *do_ecmd_cmd; ///< +command arg to be used in edited file
linenr_T do_ecmd_lnum; ///< the line number in an edited file
int append; ///< TRUE with ":w >>file" command
int usefilter; ///< TRUE with ":w !command" and ":r!command"
@@ -170,12 +208,8 @@ struct exarg {
int useridx; ///< user command index
char *errmsg; ///< returned error message
LineGetter getline; ///< Function used to get the next line
- void *cookie; ///< argument for getline()
+ void *cookie; ///< argument for getline()
cstack_T *cstack; ///< condition stack for ":if" etc.
- long verbose_save; ///< saved value of p_verbose
- int save_msg_silent; ///< saved value of msg_silent
- int did_esilent; ///< how many times emsg_silent was incremented
- bool did_sandbox; ///< when true did sandbox++
};
#define FORCE_BIN 1 // ":edit ++bin file"
@@ -188,10 +222,10 @@ struct exarg {
// used for completion on the command line
struct expand {
- char_u *xp_pattern; // start of item to expand
+ char *xp_pattern; // start of item to expand
int xp_context; // type of expansion
size_t xp_pattern_len; // bytes in xp_pattern before cursor
- char_u *xp_arg; // completion function
+ char *xp_arg; // completion function
LuaRef xp_luaref; // Ref to Lua completion function
sctx_T xp_script_ctx; // SCTX for completion function
int xp_backslash; // one of the XP_BS_ values
@@ -201,8 +235,8 @@ struct expand {
#endif
int xp_numfiles; // number of files found by file name completion
int xp_col; // cursor position in line
- char_u **xp_files; // list of files
- char_u *xp_line; // text being completed
+ char **xp_files; // list of files
+ char *xp_line; // text being completed
};
// values for xp_backslash
@@ -210,24 +244,53 @@ struct expand {
#define XP_BS_ONE 1 // uses one backslash before a space
#define XP_BS_THREE 2 // uses three backslashes before a space
+enum {
+ CMOD_SANDBOX = 0x0001, ///< ":sandbox"
+ CMOD_SILENT = 0x0002, ///< ":silent"
+ CMOD_ERRSILENT = 0x0004, ///< ":silent!"
+ CMOD_UNSILENT = 0x0008, ///< ":unsilent"
+ CMOD_NOAUTOCMD = 0x0010, ///< ":noautocmd"
+ CMOD_HIDE = 0x0020, ///< ":hide"
+ CMOD_BROWSE = 0x0040, ///< ":browse" - invoke file dialog
+ CMOD_CONFIRM = 0x0080, ///< ":confirm" - invoke yes/no dialog
+ CMOD_KEEPALT = 0x0100, ///< ":keepalt"
+ CMOD_KEEPMARKS = 0x0200, ///< ":keepmarks"
+ CMOD_KEEPJUMPS = 0x0400, ///< ":keepjumps"
+ CMOD_LOCKMARKS = 0x0800, ///< ":lockmarks"
+ CMOD_KEEPPATTERNS = 0x1000, ///< ":keeppatterns"
+ CMOD_NOSWAPFILE = 0x2000, ///< ":noswapfile"
+};
+
/// Command modifiers ":vertical", ":browse", ":confirm", ":hide", etc. set a
/// flag. This needs to be saved for recursive commands, put them in a
/// structure for easy manipulation.
typedef struct {
- int split; ///< flags for win_split()
- int tab; ///< > 0 when ":tab" was used
- bool browse; ///< true to invoke file dialog
- bool confirm; ///< true to invoke yes/no dialog
- bool hide; ///< true when ":hide" was used
- bool keepalt; ///< true when ":keepalt" was used
- bool keepjumps; ///< true when ":keepjumps" was used
- bool keepmarks; ///< true when ":keepmarks" was used
- bool keeppatterns; ///< true when ":keeppatterns" was used
- bool lockmarks; ///< true when ":lockmarks" was used
- bool noswapfile; ///< true when ":noswapfile" was used
- char_u *save_ei; ///< saved value of 'eventignore'
- regmatch_T filter_regmatch; ///< set by :filter /pat/
- bool filter_force; ///< set for :filter!
+ int cmod_flags; ///< CMOD_ flags
+
+ int cmod_split; ///< flags for win_split()
+ int cmod_tab; ///< > 0 when ":tab" was used
+ char *cmod_filter_pat;
+ regmatch_T cmod_filter_regmatch; ///< set by :filter /pat/
+ bool cmod_filter_force; ///< set for :filter!
+
+ int cmod_verbose; ///< 0 if not set, > 0 to set 'verbose' to cmod_verbose - 1
+
+ // values for undo_cmdmod()
+ char *cmod_save_ei; ///< saved value of 'eventignore'
+ int cmod_did_sandbox; ///< set when "sandbox" was incremented
+ long cmod_verbose_save; ///< if 'verbose' was set: value of p_verbose plus one
+ int cmod_save_msg_silent; ///< if non-zero: saved value of msg_silent + 1
+ int cmod_save_msg_scroll; ///< for restoring msg_scroll
+ int cmod_did_esilent; ///< incremented when emsg_silent is
} cmdmod_T;
+/// Stores command modifier info used by `nvim_parse_cmd`
+typedef struct {
+ cmdmod_T cmdmod;
+ struct {
+ bool file;
+ bool bar;
+ } magic;
+} CmdParseInfo;
+
#endif // NVIM_EX_CMDS_DEFS_H
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 3a285cdf90..c4d2821e79 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -38,12 +38,15 @@
#include "nvim/getchar.h"
#include "nvim/globals.h"
#include "nvim/hardcopy.h"
+#include "nvim/highlight_group.h"
#include "nvim/if_cscope.h"
#include "nvim/input.h"
-#include "nvim/keymap.h"
+#include "nvim/keycodes.h"
#include "nvim/lua/executor.h"
#include "nvim/main.h"
+#include "nvim/mapping.h"
#include "nvim/mark.h"
+#include "nvim/match.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
@@ -59,6 +62,7 @@
#include "nvim/os/time.h"
#include "nvim/os_unix.h"
#include "nvim/path.h"
+#include "nvim/popupmnu.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
#include "nvim/screen.h"
@@ -74,21 +78,28 @@
#include "nvim/terminal.h"
#include "nvim/ui.h"
#include "nvim/undo.h"
+#include "nvim/undo_defs.h"
#include "nvim/version.h"
#include "nvim/vim.h"
#include "nvim/window.h"
+static char e_no_such_user_defined_command_str[]
+ = N_("E184: No such user-defined command: %s");
+static char e_ambiguous_use_of_user_defined_command[]
+ = N_("E464: Ambiguous use of user-defined command");
+static char e_not_an_editor_command[]
+ = N_("E492: Not an editor command");
+static char e_no_such_user_defined_command_in_current_buffer_str[]
+ = N_("E1237: No such user-defined command in current buffer: %s");
+
static int quitmore = 0;
static bool ex_pressedreturn = false;
garray_T ucmds = { 0, 0, sizeof(ucmd_T), 4, NULL };
-// Whether a command index indicates a user command.
-#define IS_USER_CMDIDX(idx) ((int)(idx) < 0)
-
// Struct for storing a line inside a while/for loop
typedef struct {
- char_u *line; // command line
+ char *line; // command line
linenr_T lnum; // sourcing_lnum of the line
} wcmd_T;
@@ -104,18 +115,17 @@ struct loop_cookie {
int current_line; // last read line from growarray
int repeating; // TRUE when looping a second time
// When "repeating" is FALSE use "getline" and "cookie" to get lines
- char_u *(*getline)(int, void *, int, bool);
+ char *(*getline)(int, void *, int, bool);
void *cookie;
};
-
// Struct to save a few things while debugging. Used in do_cmdline() only.
struct dbg_stuff {
int trylevel;
int force_abort;
except_T *caught_stack;
- char_u *vv_exception;
- char_u *vv_throwpoint;
+ char *vv_exception;
+ char *vv_throwpoint;
int did_emsg;
int got_int;
int need_rethrow;
@@ -138,7 +148,7 @@ struct dbg_stuff {
# include "ex_cmds_defs.generated.h"
#endif
-static char_u dollar_command[2] = { '$', 0 };
+static char dollar_command[2] = { '$', 0 };
static void save_dbg_stuff(struct dbg_stuff *dsp)
{
@@ -177,11 +187,11 @@ void do_exmode(void)
int save_msg_scroll;
int prev_msg_row;
linenr_T prev_line;
- int changedtick;
+ varnumber_T changedtick;
exmode_active = true;
- State = NORMAL;
- trigger_modechanged();
+ State = MODE_NORMAL;
+ may_trigger_modechanged();
// When using ":global /pat/ visual" and then "Q" we return to continue
// the :global command.
@@ -217,6 +227,8 @@ void do_exmode(void)
emsg(_(e_emptybuf));
} else {
if (ex_pressedreturn) {
+ // Make sure the message overwrites the right line and isn't throttled.
+ msg_scroll_flush();
// go up one line, to overwrite the ":<CR>" line, so the
// output doesn't contain empty lines.
msg_row = prev_msg_row;
@@ -245,9 +257,10 @@ void do_exmode(void)
msg_scroll = save_msg_scroll;
}
-// Print the executed command for when 'verbose' is set.
-// When "lnum" is 0 only print the command.
-static void msg_verbose_cmd(linenr_T lnum, char_u *cmd)
+/// Print the executed command for when 'verbose' is set.
+///
+/// @param lnum if 0, only print the command.
+static void msg_verbose_cmd(linenr_T lnum, char *cmd)
FUNC_ATTR_NONNULL_ALL
{
no_wait_return++;
@@ -266,13 +279,10 @@ static void msg_verbose_cmd(linenr_T lnum, char_u *cmd)
no_wait_return--;
}
-/*
- * Execute a simple command line. Used for translated commands like "*".
- */
+/// Execute a simple command line. Used for translated commands like "*".
int do_cmdline_cmd(const char *cmd)
{
- return do_cmdline((char_u *)cmd, NULL, NULL,
- DOCMD_NOWAIT|DOCMD_KEYTYPED);
+ return do_cmdline((char *)cmd, NULL, NULL, DOCMD_NOWAIT|DOCMD_KEYTYPED);
}
/// do_cmdline(): execute one Ex command line
@@ -290,15 +300,14 @@ int do_cmdline_cmd(const char *cmd)
/// DOCMD_KEYTYPED - Don't reset KeyTyped.
/// DOCMD_EXCRESET - Reset the exception environment (used for debugging).
/// DOCMD_KEEPLINE - Store first typed line (for repeating with ".").
-/// DOCMD_PREVIEW - During 'inccommand' preview.
///
/// @param cookie argument for fgetline()
///
/// @return FAIL if cmdline could not be executed, OK otherwise
-int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags)
+int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
{
- char_u *next_cmdline; // next cmd to execute
- char_u *cmdline_copy = NULL; // copy of cmd line
+ char *next_cmdline; // next cmd to execute
+ char *cmdline_copy = NULL; // copy of cmd line
bool used_getline = false; // used "fgetline" to obtain command
static int recursive = 0; // recursive depth
bool msg_didout_before_start = false;
@@ -310,7 +319,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags)
};
garray_T lines_ga; // keep lines for ":while"/":for"
int current_line = 0; // active line in lines_ga
- char_u *fname = NULL; // function or script name
+ char *fname = NULL; // function or script name
linenr_T *breakpoint = NULL; // ptr to breakpoint field in cookie
int *dbg_tick = NULL; // ptr to dbg_tick field in cookie
struct dbg_stuff debug_saved; // saved things for debug mode
@@ -319,7 +328,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags)
struct msglist *private_msg_list;
// "fgetline" and "cookie" passed to do_one_cmd()
- char_u *(*cmd_getline)(int, void *, int, bool);
+ char *(*cmd_getline)(int, void *, int, bool);
void *cmd_cookie;
struct loop_cookie cmd_loop_cookie;
void *real_cookie;
@@ -340,10 +349,10 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags)
// here. The value of 200 allows nested function calls, ":source", etc.
// Allow 200 or 'maxfuncdepth', whatever is larger.
if (call_depth >= 200 && call_depth >= p_mfd) {
- emsg(_("E169: Command too recursive"));
+ emsg(_(e_command_too_recursive));
// When converting to an exception, we do not include the command name
// since this is not an error of the specific command.
- do_errthrow((cstack_T *)NULL, (char_u *)NULL);
+ do_errthrow((cstack_T *)NULL, NULL);
msg_list = saved_msg_list;
return FAIL;
}
@@ -363,7 +372,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags)
// Get the function or script name and the address where the next breakpoint
// line and the debug tick for a function or script are stored.
if (getline_is_func) {
- fname = func_name(real_cookie);
+ fname = (char *)func_name(real_cookie);
breakpoint = func_breakpoint(real_cookie);
dbg_tick = func_dbg_tick(real_cookie);
} else if (getline_equal(fgetline, cookie, getsourceline)) {
@@ -460,7 +469,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags)
if (breakpoint != NULL && dbg_tick != NULL
&& *dbg_tick != debug_tick) {
*breakpoint = dbg_find_breakpoint(getline_equal(fgetline, cookie, getsourceline),
- fname, sourcing_lnum);
+ (char_u *)fname, sourcing_lnum);
*dbg_tick = debug_tick;
}
@@ -470,10 +479,10 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags)
// Did we encounter a breakpoint?
if (breakpoint != NULL && *breakpoint != 0
&& *breakpoint <= sourcing_lnum) {
- dbg_breakpoint(fname, sourcing_lnum);
+ dbg_breakpoint((char_u *)fname, sourcing_lnum);
// Find next breakpoint.
*breakpoint = dbg_find_breakpoint(getline_equal(fgetline, cookie, getsourceline),
- fname, sourcing_lnum);
+ (char_u *)fname, sourcing_lnum);
*dbg_tick = debug_tick;
}
if (do_profiling == PROF_YES) {
@@ -534,15 +543,14 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags)
if (flags & DOCMD_KEEPLINE) {
xfree(repeat_cmdline);
if (count == 0) {
- repeat_cmdline = vim_strsave(next_cmdline);
+ repeat_cmdline = vim_strsave((char_u *)next_cmdline);
} else {
repeat_cmdline = NULL;
}
}
- }
- // 3. Make a copy of the command so we can mess with it.
- else if (cmdline_copy == NULL) {
- next_cmdline = vim_strsave(next_cmdline);
+ } else if (cmdline_copy == NULL) {
+ // 3. Make a copy of the command so we can mess with it.
+ next_cmdline = xstrdup(next_cmdline);
}
cmdline_copy = next_cmdline;
@@ -587,16 +595,9 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags)
* "cmdline_copy" can change, e.g. for '%' and '#' expansion.
*/
recursive++;
- next_cmdline = do_one_cmd(&cmdline_copy, flags,
- &cstack,
- cmd_getline, cmd_cookie);
+ next_cmdline = do_one_cmd(&cmdline_copy, flags, &cstack, cmd_getline, cmd_cookie);
recursive--;
- // Ignore trailing '|'-separated commands in preview-mode ('inccommand').
- if ((State & CMDPREVIEW) && (flags & DOCMD_PREVIEW)) {
- next_cmdline = NULL;
- }
-
if (cmd_cookie == (void *)&cmd_loop_cookie) {
// Use "current_line" from "cmd_loop_cookie", it may have been
// incremented when defining a function.
@@ -622,7 +623,6 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags)
next_cmdline = cmdline_copy;
}
-
// reset did_emsg for a function that is not aborted by an error
if (did_emsg && !force_abort
&& getline_equal(fgetline, cookie, get_func_line)
@@ -661,8 +661,8 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags)
// or ":for".
if (breakpoint != NULL) {
*breakpoint = dbg_find_breakpoint(getline_equal(fgetline, cookie, getsourceline),
- fname,
- ((wcmd_T *)lines_ga.ga_data)[current_line].lnum-1);
+ (char_u *)fname,
+ ((wcmd_T *)lines_ga.ga_data)[current_line].lnum - 1);
*dbg_tick = debug_tick;
}
} else {
@@ -795,8 +795,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags)
// If a missing ":endtry", ":endwhile", ":endfor", or ":endif" or a memory
// lack was reported above and the error message is to be converted to an
// exception, do this now after rewinding the cstack.
- do_errthrow(&cstack, getline_equal(fgetline, cookie, get_func_line)
- ? (char_u *)"endfunction" : (char_u *)NULL);
+ do_errthrow(&cstack, getline_equal(fgetline, cookie, get_func_line) ? "endfunction" : NULL);
if (trylevel == 0) {
// When an exception is being thrown out of the outermost try
@@ -805,8 +804,8 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags)
// commands are executed.
if (current_exception) {
char *p = NULL;
- char_u *saved_sourcing_name;
- int saved_sourcing_lnum;
+ char *saved_sourcing_name;
+ linenr_T saved_sourcing_lnum;
struct msglist *messages = NULL;
struct msglist *next;
@@ -911,6 +910,15 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags)
msg_list = saved_msg_list;
+ // Cleanup if "cs_emsg_silent_list" remains.
+ if (cstack.cs_emsg_silent_list != NULL) {
+ eslist_T *elem, *temp;
+ for (elem = cstack.cs_emsg_silent_list; elem != NULL; elem = temp) {
+ temp = elem->next;
+ xfree(elem);
+ }
+ }
+
/*
* If there was too much output to fit on the command line, ask the user to
* hit return before redrawing the screen. With the ":global" command we do
@@ -947,14 +955,12 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags)
return retval;
}
-/*
- * Obtain a line when inside a ":while" or ":for" loop.
- */
-static char_u *get_loop_line(int c, void *cookie, int indent, bool do_concat)
+/// Obtain a line when inside a ":while" or ":for" loop.
+static char *get_loop_line(int c, void *cookie, int indent, bool do_concat)
{
struct loop_cookie *cp = (struct loop_cookie *)cookie;
wcmd_T *wp;
- char_u *line;
+ char *line;
if (cp->current_line + 1 >= cp->lines_gap->ga_len) {
if (cp->repeating) {
@@ -962,7 +968,7 @@ static char_u *get_loop_line(int c, void *cookie, int indent, bool do_concat)
}
// First time inside the ":while"/":for": get line normally.
if (cp->getline == NULL) {
- line = getcmdline(c, 0L, indent, do_concat);
+ line = (char *)getcmdline(c, 0L, indent, do_concat);
} else {
line = cp->getline(c, cp->cookie, indent, do_concat);
}
@@ -978,16 +984,14 @@ static char_u *get_loop_line(int c, void *cookie, int indent, bool do_concat)
cp->current_line++;
wp = (wcmd_T *)(cp->lines_gap->ga_data) + cp->current_line;
sourcing_lnum = wp->lnum;
- return vim_strsave(wp->line);
+ return xstrdup(wp->line);
}
-/*
- * Store a line in "gap" so that a ":while" loop can execute it again.
- */
-static void store_loop_line(garray_T *gap, char_u *line)
+/// Store a line in "gap" so that a ":while" loop can execute it again.
+static void store_loop_line(garray_T *gap, char *line)
{
wcmd_T *p = GA_APPEND_VIA_PTR(wcmd_T, gap);
- p->line = vim_strsave(line);
+ p->line = xstrdup(line);
p->lnum = sourcing_lnum;
}
@@ -1033,16 +1037,15 @@ void *getline_cookie(LineGetter fgetline, void *cookie)
return cp;
}
-/*
- * Helper function to apply an offset for buffer commands, i.e. ":bdelete",
- * ":bwipeout", etc.
- * Returns the buffer number.
- */
-static int compute_buffer_local_count(int addr_type, int lnum, int offset)
+/// Helper function to apply an offset for buffer commands, i.e. ":bdelete",
+/// ":bwipeout", etc.
+///
+/// @return the buffer number.
+static int compute_buffer_local_count(cmd_addr_T addr_type, linenr_T lnum, long offset)
{
buf_T *buf;
buf_T *nextbuf;
- int count = offset;
+ long count = offset;
buf = firstbuf;
while (buf->b_next != NULL && buf->b_fnum < lnum) {
@@ -1079,8 +1082,8 @@ static int compute_buffer_local_count(int addr_type, int lnum, int offset)
return buf->b_fnum;
}
-// Return the window number of "win".
-// When "win" is NULL return the number of windows.
+/// @return the window number of "win" or,
+/// the number of windows if "win" is NULL
static int current_win_nr(const win_T *win)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
@@ -1113,9 +1116,8 @@ static int current_tab_nr(tabpage_T *tab)
#define CURRENT_TAB_NR current_tab_nr(curtab)
#define LAST_TAB_NR current_tab_nr(NULL)
-
/// Figure out the address type for ":wincmd".
-static void get_wincmd_addr_type(char_u *arg, exarg_T *eap)
+static void get_wincmd_addr_type(char *arg, exarg_T *eap)
{
switch (*arg) {
case 'S':
@@ -1206,7 +1208,7 @@ static void get_wincmd_addr_type(char_u *arg, exarg_T *eap)
/// non-colon, non-whitespace character.
//
/// @param skipleadingwhite Skip leading whitespace too
-static char_u *skip_colon_white(const char_u *p, bool skipleadingwhite)
+static char *skip_colon_white(const char *p, bool skipleadingwhite)
{
if (skipleadingwhite) {
p = skipwhite(p);
@@ -1216,7 +1218,489 @@ static char_u *skip_colon_white(const char_u *p, bool skipleadingwhite)
p = skipwhite(p + 1);
}
- return (char_u *)p;
+ return (char *)p;
+}
+
+/// Set the addr type for command
+///
+/// @param p pointer to character after command name in cmdline
+void set_cmd_addr_type(exarg_T *eap, char *p)
+{
+ // ea.addr_type for user commands is set by find_ucmd
+ if (IS_USER_CMDIDX(eap->cmdidx)) {
+ return;
+ }
+ if (eap->cmdidx != CMD_SIZE) {
+ eap->addr_type = cmdnames[(int)eap->cmdidx].cmd_addr_type;
+ } else {
+ eap->addr_type = ADDR_LINES;
+ }
+ // :wincmd range depends on the argument
+ if (eap->cmdidx == CMD_wincmd && p != NULL) {
+ get_wincmd_addr_type(skipwhite(p), eap);
+ }
+ // :.cc in quickfix window uses line number
+ if ((eap->cmdidx == CMD_cc || eap->cmdidx == CMD_ll) && bt_quickfix(curbuf)) {
+ eap->addr_type = ADDR_OTHER;
+ }
+}
+
+/// Get default range number for command based on its address type
+linenr_T get_cmd_default_range(exarg_T *eap)
+{
+ switch (eap->addr_type) {
+ case ADDR_LINES:
+ case ADDR_OTHER:
+ // Default is the cursor line number. Avoid using an invalid
+ // line number though.
+ return MIN(curwin->w_cursor.lnum, curbuf->b_ml.ml_line_count);
+ break;
+ case ADDR_WINDOWS:
+ return CURRENT_WIN_NR;
+ break;
+ case ADDR_ARGUMENTS:
+ return MIN(curwin->w_arg_idx + 1, ARGCOUNT);
+ break;
+ case ADDR_LOADED_BUFFERS:
+ case ADDR_BUFFERS:
+ return curbuf->b_fnum;
+ break;
+ case ADDR_TABS:
+ return CURRENT_TAB_NR;
+ break;
+ case ADDR_TABS_RELATIVE:
+ case ADDR_UNSIGNED:
+ return 1;
+ break;
+ case ADDR_QUICKFIX:
+ return (linenr_T)qf_get_cur_idx(eap);
+ break;
+ case ADDR_QUICKFIX_VALID:
+ return qf_get_cur_valid_idx(eap);
+ break;
+ default:
+ return 0;
+ // Will give an error later if a range is found.
+ break;
+ }
+}
+
+/// Set default command range for -range=% based on the addr type of the command
+void set_cmd_dflall_range(exarg_T *eap)
+{
+ buf_T *buf;
+
+ eap->line1 = 1;
+ switch (eap->addr_type) {
+ case ADDR_LINES:
+ case ADDR_OTHER:
+ eap->line2 = curbuf->b_ml.ml_line_count;
+ break;
+ case ADDR_LOADED_BUFFERS:
+ buf = firstbuf;
+ while (buf->b_next != NULL && buf->b_ml.ml_mfp == NULL) {
+ buf = buf->b_next;
+ }
+ eap->line1 = buf->b_fnum;
+ buf = lastbuf;
+ while (buf->b_prev != NULL && buf->b_ml.ml_mfp == NULL) {
+ buf = buf->b_prev;
+ }
+ eap->line2 = buf->b_fnum;
+ break;
+ case ADDR_BUFFERS:
+ eap->line1 = firstbuf->b_fnum;
+ eap->line2 = lastbuf->b_fnum;
+ break;
+ case ADDR_WINDOWS:
+ eap->line2 = LAST_WIN_NR;
+ break;
+ case ADDR_TABS:
+ eap->line2 = LAST_TAB_NR;
+ break;
+ case ADDR_TABS_RELATIVE:
+ eap->line2 = 1;
+ break;
+ case ADDR_ARGUMENTS:
+ if (ARGCOUNT == 0) {
+ eap->line1 = eap->line2 = 0;
+ } else {
+ eap->line2 = ARGCOUNT;
+ }
+ break;
+ case ADDR_QUICKFIX_VALID:
+ eap->line2 = (linenr_T)qf_get_valid_size(eap);
+ if (eap->line2 == 0) {
+ eap->line2 = 1;
+ }
+ break;
+ case ADDR_NONE:
+ case ADDR_UNSIGNED:
+ case ADDR_QUICKFIX:
+ iemsg(_("INTERNAL: Cannot use EX_DFLALL "
+ "with ADDR_NONE, ADDR_UNSIGNED or ADDR_QUICKFIX"));
+ break;
+ }
+}
+
+static void parse_register(exarg_T *eap)
+{
+ // Accept numbered register only when no count allowed (:put)
+ if ((eap->argt & EX_REGSTR)
+ && *eap->arg != NUL
+ // Do not allow register = for user commands
+ && (!IS_USER_CMDIDX(eap->cmdidx) || *eap->arg != '=')
+ && !((eap->argt & EX_COUNT) && ascii_isdigit(*eap->arg))) {
+ if (valid_yank_reg(*eap->arg, (eap->cmdidx != CMD_put
+ && !IS_USER_CMDIDX(eap->cmdidx)))) {
+ eap->regname = (uint8_t)(*eap->arg++);
+ // for '=' register: accept the rest of the line as an expression
+ if (eap->arg[-1] == '=' && eap->arg[0] != NUL) {
+ if (!eap->skip) {
+ set_expr_line(vim_strsave((char_u *)eap->arg));
+ }
+ eap->arg += STRLEN(eap->arg);
+ }
+ eap->arg = skipwhite(eap->arg);
+ }
+ }
+}
+
+// Change line1 and line2 of Ex command to use count
+void set_cmd_count(exarg_T *eap, long count, bool validate)
+{
+ if (eap->addr_type != ADDR_LINES) { // e.g. :buffer 2, :sleep 3
+ eap->line2 = (linenr_T)count;
+ if (eap->addr_count == 0) {
+ eap->addr_count = 1;
+ }
+ } else {
+ eap->line1 = eap->line2;
+ eap->line2 += (linenr_T)count - 1;
+ eap->addr_count++;
+ // Be vi compatible: no error message for out of range.
+ if (validate && eap->line2 > curbuf->b_ml.ml_line_count) {
+ eap->line2 = curbuf->b_ml.ml_line_count;
+ }
+ }
+}
+
+static int parse_count(exarg_T *eap, char **errormsg, bool validate)
+{
+ // Check for a count. When accepting a EX_BUFNAME, don't use "123foo" as a
+ // count, it's a buffer name.
+ char *p;
+ long n;
+
+ if ((eap->argt & EX_COUNT) && ascii_isdigit(*eap->arg)
+ && (!(eap->argt & EX_BUFNAME) || *(p = skipdigits(eap->arg + 1)) == NUL
+ || ascii_iswhite(*p))) {
+ n = getdigits_long((char_u **)&eap->arg, false, -1);
+ eap->arg = skipwhite(eap->arg);
+ if (n <= 0 && (eap->argt & EX_ZEROR) == 0) {
+ if (errormsg != NULL) {
+ *errormsg = _(e_zerocount);
+ }
+ return FAIL;
+ }
+ set_cmd_count(eap, n, validate);
+ }
+
+ return OK;
+}
+
+/// Check if command is not implemented
+bool is_cmd_ni(cmdidx_T cmdidx)
+{
+ return !IS_USER_CMDIDX(cmdidx) && (cmdnames[cmdidx].cmd_func == ex_ni
+ || cmdnames[cmdidx].cmd_func == ex_script_ni);
+}
+
+/// Parse command line and return information about the first command.
+/// If parsing is done successfully, need to free cmod_filter_pat and cmod_filter_regmatch.regprog
+/// after calling, usually done using undo_cmdmod() or execute_cmd().
+///
+/// @param cmdline Command line string
+/// @param[out] eap Ex command arguments
+/// @param[out] cmdinfo Command parse information
+/// @param[out] errormsg Error message, if any
+///
+/// @return Success or failure
+bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **errormsg)
+{
+ char *cmd;
+ char *p;
+ char *after_modifier = NULL;
+
+ // Initialize cmdinfo
+ memset(cmdinfo, 0, sizeof(*cmdinfo));
+
+ // Initialize eap
+ memset(eap, 0, sizeof(*eap));
+ eap->line1 = 1;
+ eap->line2 = 1;
+ eap->cmd = cmdline;
+ eap->cmdlinep = &cmdline;
+ eap->getline = NULL;
+ eap->cookie = NULL;
+
+ const bool save_ex_pressedreturn = ex_pressedreturn;
+ // Parse command modifiers
+ if (parse_command_modifiers(eap, errormsg, &cmdinfo->cmdmod, false) == FAIL) {
+ ex_pressedreturn = save_ex_pressedreturn;
+ goto err;
+ }
+ ex_pressedreturn = save_ex_pressedreturn;
+ after_modifier = eap->cmd;
+
+ // Save location after command modifiers
+ cmd = eap->cmd;
+ // Skip ranges to find command name since we need the command to know what kind of range it uses
+ eap->cmd = skip_range(eap->cmd, NULL);
+ if (*eap->cmd == '*') {
+ eap->cmd = skipwhite(eap->cmd + 1);
+ }
+ p = find_ex_command(eap, NULL);
+ if (p == NULL) {
+ *errormsg = _(e_ambiguous_use_of_user_defined_command);
+ goto err;
+ }
+
+ // Set command address type and parse command range
+ set_cmd_addr_type(eap, p);
+ eap->cmd = cmd;
+ if (parse_cmd_address(eap, errormsg, false) == FAIL) {
+ goto err;
+ }
+
+ // Skip colon and whitespace
+ eap->cmd = skip_colon_white(eap->cmd, true);
+ // Fail if command is a comment or if command doesn't exist
+ if (*eap->cmd == NUL || *eap->cmd == '"') {
+ goto err;
+ }
+ // Fail if command is invalid
+ if (eap->cmdidx == CMD_SIZE) {
+ STRCPY(IObuff, _(e_not_an_editor_command));
+ // If the modifier was parsed OK the error must be in the following command
+ char *cmdname = after_modifier ? after_modifier : cmdline;
+ append_command(cmdname);
+ *errormsg = (char *)IObuff;
+ goto err;
+ }
+
+ // Correctly set 'forceit' for commands
+ if (*p == '!' && eap->cmdidx != CMD_substitute
+ && eap->cmdidx != CMD_smagic && eap->cmdidx != CMD_snomagic) {
+ p++;
+ eap->forceit = true;
+ } else {
+ eap->forceit = false;
+ }
+
+ // Parse arguments.
+ if (!IS_USER_CMDIDX(eap->cmdidx)) {
+ eap->argt = cmdnames[(int)eap->cmdidx].cmd_argt;
+ }
+ // Skip to start of argument.
+ // Don't do this for the ":!" command, because ":!! -l" needs the space.
+ if (eap->cmdidx == CMD_bang) {
+ eap->arg = p;
+ } else {
+ eap->arg = skipwhite(p);
+ }
+
+ // Don't treat ":r! filter" like a bang
+ if (eap->cmdidx == CMD_read) {
+ if (eap->forceit) {
+ eap->forceit = false; // :r! filter
+ }
+ }
+
+ // Check for '|' to separate commands and '"' to start comments.
+ // Don't do this for ":read !cmd" and ":write !cmd".
+ if ((eap->argt & EX_TRLBAR)) {
+ separate_nextcmd(eap);
+ }
+ // Fail if command doesn't support bang but is used with a bang
+ if (!(eap->argt & EX_BANG) && eap->forceit) {
+ *errormsg = _(e_nobang);
+ goto err;
+ }
+ // Fail if command doesn't support a range but it is given a range
+ if (!(eap->argt & EX_RANGE) && eap->addr_count > 0) {
+ *errormsg = _(e_norange);
+ goto err;
+ }
+ // Set default range for command if required
+ if ((eap->argt & EX_DFLALL) && eap->addr_count == 0) {
+ set_cmd_dflall_range(eap);
+ }
+
+ // Parse register and count
+ parse_register(eap);
+ if (parse_count(eap, errormsg, false) == FAIL) {
+ goto err;
+ }
+
+ // Remove leading whitespace and colon from next command
+ if (eap->nextcmd) {
+ eap->nextcmd = skip_colon_white(eap->nextcmd, true);
+ }
+
+ // Set the "magic" values (characters that get treated specially)
+ if (eap->argt & EX_XFILE) {
+ cmdinfo->magic.file = true;
+ }
+ if (eap->argt & EX_TRLBAR) {
+ cmdinfo->magic.bar = true;
+ }
+
+ return true;
+err:
+ undo_cmdmod(&cmdinfo->cmdmod);
+ return false;
+}
+
+/// Execute an Ex command using parsed command line information.
+/// Does not do any validation of the Ex command arguments.
+///
+/// @param eap Ex-command arguments
+/// @param cmdinfo Command parse information
+/// @param preview Execute command preview callback instead of actual command
+int execute_cmd(exarg_T *eap, CmdParseInfo *cmdinfo, bool preview)
+{
+ char *errormsg = NULL;
+ int retv = 0;
+
+#define ERROR(msg) \
+ do { \
+ errormsg = msg; \
+ goto end; \
+ } while (0)
+
+ cmdmod_T save_cmdmod = cmdmod;
+ cmdmod = cmdinfo->cmdmod;
+
+ // Apply command modifiers
+ apply_cmdmod(&cmdmod);
+
+ if (!MODIFIABLE(curbuf) && (eap->argt & EX_MODIFY)
+ // allow :put in terminals
+ && !(curbuf->terminal && eap->cmdidx == CMD_put)) {
+ ERROR(_(e_modifiable));
+ }
+ if (!IS_USER_CMDIDX(eap->cmdidx)) {
+ if (cmdwin_type != 0 && !(eap->argt & EX_CMDWIN)) {
+ // Command not allowed in the command line window
+ ERROR(_(e_cmdwin));
+ }
+ if (text_locked() && !(eap->argt & EX_LOCK_OK)) {
+ // Command not allowed when text is locked
+ ERROR(_(get_text_locked_msg()));
+ }
+ }
+ // Disallow editing another buffer when "curbuf->b_ro_locked" is set.
+ // Do allow ":checktime" (it is postponed).
+ // Do allow ":edit" (check for an argument later).
+ // Do allow ":file" with no arguments
+ if (!(eap->argt & EX_CMDWIN)
+ && eap->cmdidx != CMD_checktime
+ && eap->cmdidx != CMD_edit
+ && !(eap->cmdidx == CMD_file && *eap->arg == NUL)
+ && !IS_USER_CMDIDX(eap->cmdidx)
+ && curbuf_locked()) {
+ ERROR(_(e_cannot_edit_other_buf));
+ }
+
+ if (((eap->argt & EX_WHOLEFOLD) || eap->addr_count >= 2) && !global_busy
+ && eap->addr_type == ADDR_LINES) {
+ // Put the first line at the start of a closed fold, put the last line
+ // at the end of a closed fold.
+ (void)hasFolding(eap->line1, &eap->line1, NULL);
+ (void)hasFolding(eap->line2, NULL, &eap->line2);
+ }
+
+ // If filename expansion is enabled, expand filenames
+ if (cmdinfo->magic.file) {
+ if (expand_filename(eap, (char_u **)eap->cmdlinep, &errormsg) == FAIL) {
+ goto end;
+ }
+ }
+
+ // Accept buffer name. Cannot be used at the same time with a buffer
+ // number. Don't do this for a user command.
+ if ((eap->argt & EX_BUFNAME) && *eap->arg != NUL && eap->addr_count == 0
+ && !IS_USER_CMDIDX(eap->cmdidx)) {
+ if (eap->args == NULL) {
+ // If argument positions are not specified, search the argument for the buffer name.
+ // :bdelete, :bwipeout and :bunload take several arguments, separated by spaces:
+ // find next space (skipping over escaped characters).
+ // The others take one argument: ignore trailing spaces.
+ char *p;
+
+ if (eap->cmdidx == CMD_bdelete || eap->cmdidx == CMD_bwipeout
+ || eap->cmdidx == CMD_bunload) {
+ p = skiptowhite_esc(eap->arg);
+ } else {
+ p = eap->arg + STRLEN(eap->arg);
+ while (p > eap->arg && ascii_iswhite(p[-1])) {
+ p--;
+ }
+ }
+ eap->line2 = buflist_findpat(eap->arg, p, (eap->argt & EX_BUFUNL) != 0,
+ false, false);
+ eap->addr_count = 1;
+ eap->arg = skipwhite(p);
+ } else {
+ // If argument positions are specified, just use the first argument
+ eap->line2 = buflist_findpat(eap->args[0],
+ eap->args[0] + eap->arglens[0],
+ (eap->argt & EX_BUFUNL) != 0, false, false);
+ eap->addr_count = 1;
+ // Shift each argument by 1
+ for (size_t i = 0; i < eap->argc - 1; i++) {
+ eap->args[i] = eap->args[i + 1];
+ }
+ // Make the last argument point to the NUL terminator at the end of string
+ eap->args[eap->argc - 1] = eap->args[eap->argc - 1] + eap->arglens[eap->argc - 1];
+ eap->argc -= 1;
+
+ eap->arg = eap->args[0];
+ }
+ if (eap->line2 < 0) { // failed
+ goto end;
+ }
+ }
+
+ // Execute the command
+ if (IS_USER_CMDIDX(eap->cmdidx)) {
+ // Execute a user-defined command.
+ retv = do_ucmd(eap, preview);
+ } else {
+ // Call the function to execute the command or the preview callback.
+ eap->errmsg = NULL;
+
+ if (preview) {
+ retv = (cmdnames[eap->cmdidx].cmd_preview_func)(eap, cmdpreview_get_ns(),
+ cmdpreview_get_bufnr());
+ } else {
+ (cmdnames[eap->cmdidx].cmd_func)(eap);
+ }
+ if (eap->errmsg != NULL) {
+ errormsg = _(eap->errmsg);
+ }
+ }
+
+end:
+ if (errormsg != NULL && *errormsg != NUL) {
+ emsg(errormsg);
+ }
+ // Undo command modifiers
+ undo_cmdmod(&cmdmod);
+ cmdmod = save_cmdmod;
+ return retv;
+#undef ERROR
}
/// Execute one Ex command.
@@ -1236,19 +1720,18 @@ static char_u *skip_colon_white(const char_u *p, bool skipleadingwhite)
/// This function may be called recursively!
///
/// @param cookie argument for fgetline()
-static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGetter fgetline,
- void *cookie)
+static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter fgetline,
+ void *cookie)
{
- char_u *p;
+ char *p;
linenr_T lnum;
- long n;
char *errormsg = NULL; // error message
- char_u *after_modifier = NULL;
+ char *after_modifier = NULL;
exarg_T ea;
- const int save_msg_scroll = msg_scroll;
cmdmod_T save_cmdmod;
const int save_reg_executing = reg_executing;
- char_u *cmd;
+ const bool save_pending_end_reg_executing = pending_end_reg_executing;
+ char *cmd;
memset(&ea, 0, sizeof(ea));
ea.line1 = 1;
@@ -1286,9 +1769,10 @@ static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGe
ea.cookie = cookie;
ea.cstack = cstack;
- if (parse_command_modifiers(&ea, &errormsg, false) == FAIL) {
+ if (parse_command_modifiers(&ea, &errormsg, &cmdmod, false) == FAIL) {
goto doend;
}
+ apply_cmdmod(&cmdmod);
after_modifier = ea.cmd;
@@ -1306,7 +1790,7 @@ static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGe
if (*ea.cmd == '*') {
ea.cmd = skipwhite(ea.cmd + 1);
}
- p = find_command(&ea, NULL);
+ p = find_ex_command(&ea, NULL);
// Count this line for profiling if skip is TRUE.
if (do_profiling == PROF_YES
@@ -1363,23 +1847,7 @@ static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGe
// The ea.cmd pointer is updated to point to the first character following the
// range spec. If an initial address is found, but no second, the upper bound
// is equal to the lower.
-
- // ea.addr_type for user commands is set by find_ucmd
- if (!IS_USER_CMDIDX(ea.cmdidx)) {
- if (ea.cmdidx != CMD_SIZE) {
- ea.addr_type = cmdnames[(int)ea.cmdidx].cmd_addr_type;
- } else {
- ea.addr_type = ADDR_LINES;
- }
- // :wincmd range depends on the argument
- if (ea.cmdidx == CMD_wincmd && p != NULL) {
- get_wincmd_addr_type(skipwhite(p), &ea);
- }
- // :.cc in quickfix window uses line number
- if ((ea.cmdidx == CMD_cc || ea.cmdidx == CMD_ll) && bt_quickfix(curbuf)) {
- ea.addr_type = ADDR_OTHER;
- }
- }
+ set_cmd_addr_type(&ea, p);
ea.cmd = cmd;
if (parse_cmd_address(&ea, &errormsg, false) == FAIL) {
@@ -1400,7 +1868,7 @@ static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGe
* If we find a '|' or '\n' we set ea.nextcmd.
*/
if (*ea.cmd == NUL || *ea.cmd == '"'
- || (ea.nextcmd = check_nextcmd(ea.cmd)) != NULL) {
+ || (ea.nextcmd = (char *)check_nextcmd((char_u *)ea.cmd)) != NULL) {
// strange vi behaviour:
// ":3" jumps to line 3
// ":3|..." prints line 3
@@ -1443,27 +1911,27 @@ static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGe
while (ASCII_ISALNUM(*p)) {
++p;
}
- p = vim_strnsave(ea.cmd, p - ea.cmd);
+ p = xstrnsave(ea.cmd, (size_t)(p - ea.cmd));
int ret = apply_autocmds(EVENT_CMDUNDEFINED, p, p, true, NULL);
xfree(p);
// If the autocommands did something and didn't cause an error, try
// finding the command again.
- p = (ret && !aborting()) ? find_command(&ea, NULL) : ea.cmd;
+ p = (ret && !aborting()) ? find_ex_command(&ea, NULL) : ea.cmd;
}
if (p == NULL) {
if (!ea.skip) {
- errormsg = _("E464: Ambiguous use of user-defined command");
+ errormsg = _(e_ambiguous_use_of_user_defined_command);
}
goto doend;
}
// Check for wrong commands.
if (ea.cmdidx == CMD_SIZE) {
if (!ea.skip) {
- STRCPY(IObuff, _("E492: Not an editor command"));
+ STRCPY(IObuff, _(e_not_an_editor_command));
// If the modifier was parsed OK the error must be in the following
// command
- char_u *cmdname = after_modifier ? after_modifier : *cmdlinep;
+ char *cmdname = after_modifier ? after_modifier : *cmdlinep;
if (!(flags & DOCMD_VERBOSE)) {
append_command(cmdname);
}
@@ -1475,10 +1943,7 @@ static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGe
}
// set when Not Implemented
- const int ni = !IS_USER_CMDIDX(ea.cmdidx)
- && (cmdnames[ea.cmdidx].cmd_func == ex_ni
- || cmdnames[ea.cmdidx].cmd_func == ex_script_ni);
-
+ const int ni = is_cmd_ni(ea.cmdidx);
// Forced commands.
if (*p == '!' && ea.cmdidx != CMD_substitute
@@ -1508,11 +1973,17 @@ static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGe
goto doend;
}
- if (text_locked() && !(ea.argt & EX_CMDWIN)
- && !IS_USER_CMDIDX(ea.cmdidx)) {
- // Command not allowed when editing the command line.
- errormsg = _(get_text_locked_msg());
- goto doend;
+ if (!IS_USER_CMDIDX(ea.cmdidx)) {
+ if (cmdwin_type != 0 && !(ea.argt & EX_CMDWIN)) {
+ // Command not allowed in the command line window
+ errormsg = _(e_cmdwin);
+ goto doend;
+ }
+ if (text_locked() && !(ea.argt & EX_LOCK_OK)) {
+ // Command not allowed when text is locked
+ errormsg = _(get_text_locked_msg());
+ goto doend;
+ }
}
// Disallow editing another buffer when "curbuf->b_ro_locked" is set.
@@ -1626,7 +2097,7 @@ static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGe
goto doend;
}
ea.arg = skipwhite(ea.arg + 1);
- ea.append = TRUE;
+ ea.append = true;
} else if (*ea.arg == '!' && ea.cmdidx == CMD_write) { // :w !filter
++ea.arg;
ea.usefilter = TRUE;
@@ -1646,8 +2117,8 @@ static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGe
if (ea.cmdidx == CMD_lshift || ea.cmdidx == CMD_rshift) {
ea.amount = 1;
while (*ea.arg == *ea.cmd) { // count number of '>' or '<'
- ++ea.arg;
- ++ea.amount;
+ ea.arg++;
+ ea.amount++;
}
ea.arg = skipwhite(ea.arg);
}
@@ -1692,106 +2163,13 @@ static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGe
}
if ((ea.argt & EX_DFLALL) && ea.addr_count == 0) {
- buf_T *buf;
-
- ea.line1 = 1;
- switch (ea.addr_type) {
- case ADDR_LINES:
- case ADDR_OTHER:
- ea.line2 = curbuf->b_ml.ml_line_count;
- break;
- case ADDR_LOADED_BUFFERS:
- buf = firstbuf;
- while (buf->b_next != NULL && buf->b_ml.ml_mfp == NULL) {
- buf = buf->b_next;
- }
- ea.line1 = buf->b_fnum;
- buf = lastbuf;
- while (buf->b_prev != NULL && buf->b_ml.ml_mfp == NULL) {
- buf = buf->b_prev;
- }
- ea.line2 = buf->b_fnum;
- break;
- case ADDR_BUFFERS:
- ea.line1 = firstbuf->b_fnum;
- ea.line2 = lastbuf->b_fnum;
- break;
- case ADDR_WINDOWS:
- ea.line2 = LAST_WIN_NR;
- break;
- case ADDR_TABS:
- ea.line2 = LAST_TAB_NR;
- break;
- case ADDR_TABS_RELATIVE:
- ea.line2 = 1;
- break;
- case ADDR_ARGUMENTS:
- if (ARGCOUNT == 0) {
- ea.line1 = ea.line2 = 0;
- } else {
- ea.line2 = ARGCOUNT;
- }
- break;
- case ADDR_QUICKFIX_VALID:
- ea.line2 = qf_get_valid_size(&ea);
- if (ea.line2 == 0) {
- ea.line2 = 1;
- }
- break;
- case ADDR_NONE:
- case ADDR_UNSIGNED:
- case ADDR_QUICKFIX:
- iemsg(_("INTERNAL: Cannot use EX_DFLALL "
- "with ADDR_NONE, ADDR_UNSIGNED or ADDR_QUICKFIX"));
- break;
- }
- }
-
- // accept numbered register only when no count allowed (:put)
- if ((ea.argt & EX_REGSTR)
- && *ea.arg != NUL
- // Do not allow register = for user commands
- && (!IS_USER_CMDIDX(ea.cmdidx) || *ea.arg != '=')
- && !((ea.argt & EX_COUNT) && ascii_isdigit(*ea.arg))) {
- if (valid_yank_reg(*ea.arg, (ea.cmdidx != CMD_put
- && !IS_USER_CMDIDX(ea.cmdidx)))) {
- ea.regname = *ea.arg++;
- // for '=' register: accept the rest of the line as an expression
- if (ea.arg[-1] == '=' && ea.arg[0] != NUL) {
- set_expr_line(vim_strsave(ea.arg));
- ea.arg += STRLEN(ea.arg);
- }
- ea.arg = skipwhite(ea.arg);
- }
+ set_cmd_dflall_range(&ea);
}
- //
- // Check for a count. When accepting a EX_BUFNAME, don't use "123foo" as a
- // count, it's a buffer name.
- ///
- if ((ea.argt & EX_COUNT) && ascii_isdigit(*ea.arg)
- && (!(ea.argt & EX_BUFNAME) || *(p = skipdigits(ea.arg + 1)) == NUL
- || ascii_iswhite(*p))) {
- n = getdigits_long(&ea.arg, false, -1);
- ea.arg = skipwhite(ea.arg);
- if (n <= 0 && !ni && (ea.argt & EX_ZEROR) == 0) {
- errormsg = _(e_zerocount);
- goto doend;
- }
- if (ea.addr_type != ADDR_LINES) { // e.g. :buffer 2, :sleep 3
- ea.line2 = n;
- if (ea.addr_count == 0) {
- ea.addr_count = 1;
- }
- } else {
- ea.line1 = ea.line2;
- ea.line2 += n - 1;
- ++ea.addr_count;
- // Be vi compatible: no error message for out of range.
- if (ea.line2 > curbuf->b_ml.ml_line_count) {
- ea.line2 = curbuf->b_ml.ml_line_count;
- }
- }
+ // Parse register and count
+ parse_register(&ea);
+ if (parse_count(&ea, &errormsg, true) == FAIL) {
+ goto doend;
}
/*
@@ -1911,7 +2289,7 @@ static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGe
}
if (ea.argt & EX_XFILE) {
- if (expand_filename(&ea, cmdlinep, &errormsg) == FAIL) {
+ if (expand_filename(&ea, (char_u **)cmdlinep, &errormsg) == FAIL) {
goto doend;
}
}
@@ -1933,7 +2311,7 @@ static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGe
} else {
p = ea.arg + STRLEN(ea.arg);
while (p > ea.arg && ascii_iswhite(p[-1])) {
- --p;
+ p--;
}
}
ea.line2 = buflist_findpat(ea.arg, p, (ea.argt & EX_BUFUNL) != 0,
@@ -1947,12 +2325,12 @@ static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGe
// The :try command saves the emsg_silent flag, reset it here when
// ":silent! try" was used, it should only apply to :try itself.
- if (ea.cmdidx == CMD_try && ea.did_esilent > 0) {
- emsg_silent -= ea.did_esilent;
+ if (ea.cmdidx == CMD_try && cmdmod.cmod_did_esilent > 0) {
+ emsg_silent -= cmdmod.cmod_did_esilent;
if (emsg_silent < 0) {
emsg_silent = 0;
}
- ea.did_esilent = 0;
+ cmdmod.cmod_did_esilent = 0;
}
// 7. Execute the command.
@@ -1960,7 +2338,7 @@ static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGe
/*
* Execute a user-defined command.
*/
- do_ucmd(&ea);
+ do_ucmd(&ea, false);
} else {
/*
* Call the function to execute the command.
@@ -1989,7 +2367,7 @@ static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGe
do_return(&ea, TRUE, FALSE, NULL);
}
}
- need_rethrow = check_cstack = FALSE;
+ need_rethrow = check_cstack = false;
doend:
// can happen with zero line number
@@ -2009,17 +2387,13 @@ doend:
emsg(errormsg);
}
do_errthrow(cstack,
- (ea.cmdidx != CMD_SIZE && !IS_USER_CMDIDX(ea.cmdidx))
- ? cmdnames[(int)ea.cmdidx].cmd_name
- : (char_u *)NULL);
+ (ea.cmdidx != CMD_SIZE
+ && !IS_USER_CMDIDX(ea.cmdidx)) ? cmdnames[(int)ea.cmdidx].cmd_name : NULL);
- undo_cmdmod(&ea, save_msg_scroll);
+ undo_cmdmod(&cmdmod);
cmdmod = save_cmdmod;
reg_executing = save_reg_executing;
-
- if (ea.did_sandbox) {
- sandbox--;
- }
+ pending_end_reg_executing = save_pending_end_reg_executing;
if (ea.nextcmd && *ea.nextcmd == NUL) { // not really a next command
ea.nextcmd = NULL;
@@ -2030,25 +2404,40 @@ doend:
return ea.nextcmd;
}
-// Parse and skip over command modifiers:
-// - update eap->cmd
-// - store flags in "cmdmod".
-// - Set ex_pressedreturn for an empty command line.
-// - set msg_silent for ":silent"
-// - set 'eventignore' to "all" for ":noautocmd"
-// - set p_verbose for ":verbose"
-// - Increment "sandbox" for ":sandbox"
-// When "skip_only" is true the global variables are not changed, except for
-// "cmdmod".
-// Return FAIL when the command is not to be executed.
-// May set "errormsg" to an error message.
-int parse_command_modifiers(exarg_T *eap, char **errormsg, bool skip_only)
+static char ex_error_buf[MSG_BUF_LEN];
+
+/// @return an error message with argument included.
+/// Uses a static buffer, only the last error will be kept.
+/// "msg" will be translated, caller should use N_().
+char *ex_errmsg(const char *const msg, const char *const arg)
+ FUNC_ATTR_NONNULL_ALL
+{
+ vim_snprintf(ex_error_buf, MSG_BUF_LEN, _(msg), arg);
+ return ex_error_buf;
+}
+
+/// Parse and skip over command modifiers:
+/// - update eap->cmd
+/// - store flags in "cmod".
+/// - Set ex_pressedreturn for an empty command line.
+///
+/// @param skip_only if false, undo_cmdmod() must be called later to free
+/// any cmod_filter_pat and cmod_filter_regmatch.regprog,
+/// and ex_pressedreturn may be set.
+/// @param[out] errormsg potential error message.
+///
+/// Call apply_cmdmod() to get the side effects of the modifiers:
+/// - Increment "sandbox" for ":sandbox"
+/// - set p_verbose for ":verbose"
+/// - set msg_silent for ":silent"
+/// - set 'eventignore' to "all" for ":noautocmd"
+///
+/// @return FAIL when the command is not to be executed.
+int parse_command_modifiers(exarg_T *eap, char **errormsg, cmdmod_T *cmod, bool skip_only)
{
- char_u *p;
+ char *p;
- memset(&cmdmod, 0, sizeof(cmdmod));
- eap->verbose_save = -1;
- eap->save_msg_silent = -1;
+ CLEAR_POINTER(cmod);
// Repeat until no more command modifiers are found.
for (;;) {
@@ -2062,7 +2451,7 @@ int parse_command_modifiers(exarg_T *eap, char **errormsg, bool skip_only)
if (*eap->cmd == NUL && exmode_active
&& getline_equal(eap->getline, eap->cookie, getexline)
&& curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
- eap->cmd = (char_u *)"+";
+ eap->cmd = "+";
if (!skip_only) {
ex_pressedreturn = true;
}
@@ -2086,58 +2475,58 @@ int parse_command_modifiers(exarg_T *eap, char **errormsg, bool skip_only)
if (!checkforcmd(&eap->cmd, "aboveleft", 3)) {
break;
}
- cmdmod.split |= WSP_ABOVE;
+ cmod->cmod_split |= WSP_ABOVE;
continue;
case 'b':
if (checkforcmd(&eap->cmd, "belowright", 3)) {
- cmdmod.split |= WSP_BELOW;
+ cmod->cmod_split |= WSP_BELOW;
continue;
}
if (checkforcmd(&eap->cmd, "browse", 3)) {
- cmdmod.browse = true;
+ cmod->cmod_flags |= CMOD_BROWSE;
continue;
}
if (!checkforcmd(&eap->cmd, "botright", 2)) {
break;
}
- cmdmod.split |= WSP_BOT;
+ cmod->cmod_split |= WSP_BOT;
continue;
case 'c':
if (!checkforcmd(&eap->cmd, "confirm", 4)) {
break;
}
- cmdmod.confirm = true;
+ cmod->cmod_flags |= CMOD_CONFIRM;
continue;
case 'k':
if (checkforcmd(&eap->cmd, "keepmarks", 3)) {
- cmdmod.keepmarks = true;
+ cmod->cmod_flags |= CMOD_KEEPMARKS;
continue;
}
if (checkforcmd(&eap->cmd, "keepalt", 5)) {
- cmdmod.keepalt = true;
+ cmod->cmod_flags |= CMOD_KEEPALT;
continue;
}
if (checkforcmd(&eap->cmd, "keeppatterns", 5)) {
- cmdmod.keeppatterns = true;
+ cmod->cmod_flags |= CMOD_KEEPPATTERNS;
continue;
}
if (!checkforcmd(&eap->cmd, "keepjumps", 5)) {
break;
}
- cmdmod.keepjumps = true;
+ cmod->cmod_flags |= CMOD_KEEPJUMPS;
continue;
case 'f': { // only accept ":filter {pat} cmd"
- char_u *reg_pat;
+ char *reg_pat;
if (!checkforcmd(&p, "filter", 4) || *p == NUL || ends_excmd(*p)) {
break;
}
if (*p == '!') {
- cmdmod.filter_force = true;
+ cmod->cmod_filter_force = true;
p = skipwhite(p + 1);
if (*p == NUL || ends_excmd(*p)) {
break;
@@ -2153,8 +2542,9 @@ int parse_command_modifiers(exarg_T *eap, char **errormsg, bool skip_only)
break;
}
if (!skip_only) {
- cmdmod.filter_regmatch.regprog = vim_regcomp(reg_pat, RE_MAGIC);
- if (cmdmod.filter_regmatch.regprog == NULL) {
+ cmod->cmod_filter_pat = xstrdup(reg_pat);
+ cmod->cmod_filter_regmatch.regprog = vim_regcomp(reg_pat, RE_MAGIC);
+ if (cmod->cmod_filter_regmatch.regprog == NULL) {
break;
}
}
@@ -2169,87 +2559,69 @@ int parse_command_modifiers(exarg_T *eap, char **errormsg, bool skip_only)
break;
}
eap->cmd = p;
- cmdmod.hide = true;
+ cmod->cmod_flags |= CMOD_HIDE;
continue;
case 'l':
if (checkforcmd(&eap->cmd, "lockmarks", 3)) {
- cmdmod.lockmarks = true;
+ cmod->cmod_flags |= CMOD_LOCKMARKS;
continue;
}
if (!checkforcmd(&eap->cmd, "leftabove", 5)) {
break;
}
- cmdmod.split |= WSP_ABOVE;
+ cmod->cmod_split |= WSP_ABOVE;
continue;
case 'n':
if (checkforcmd(&eap->cmd, "noautocmd", 3)) {
- if (cmdmod.save_ei == NULL && !skip_only) {
- // Set 'eventignore' to "all". Restore the
- // existing option value later.
- cmdmod.save_ei = vim_strsave(p_ei);
- set_string_option_direct("ei", -1,
- (char_u *)"all", OPT_FREE, SID_NONE);
- }
+ cmod->cmod_flags |= CMOD_NOAUTOCMD;
continue;
}
if (!checkforcmd(&eap->cmd, "noswapfile", 3)) {
break;
}
- cmdmod.noswapfile = true;
+ cmod->cmod_flags |= CMOD_NOSWAPFILE;
continue;
case 'r':
if (!checkforcmd(&eap->cmd, "rightbelow", 6)) {
break;
}
- cmdmod.split |= WSP_BELOW;
+ cmod->cmod_split |= WSP_BELOW;
continue;
case 's':
if (checkforcmd(&eap->cmd, "sandbox", 3)) {
- if (!skip_only) {
- if (!eap->did_sandbox) {
- sandbox++;
- }
- eap->did_sandbox = true;
- }
+ cmod->cmod_flags |= CMOD_SANDBOX;
continue;
}
if (!checkforcmd(&eap->cmd, "silent", 3)) {
break;
}
- if (!skip_only) {
- if (eap->save_msg_silent == -1) {
- eap->save_msg_silent = msg_silent;
- }
- msg_silent++;
- }
+ cmod->cmod_flags |= CMOD_SILENT;
if (*eap->cmd == '!' && !ascii_iswhite(eap->cmd[-1])) {
// ":silent!", but not "silent !cmd"
eap->cmd = skipwhite(eap->cmd + 1);
- if (!skip_only) {
- emsg_silent++;
- eap->did_esilent++;
- }
+ cmod->cmod_flags |= CMOD_ERRSILENT;
}
continue;
case 't':
if (checkforcmd(&p, "tab", 3)) {
if (!skip_only) {
- long tabnr = get_address(eap, &eap->cmd, ADDR_TABS, eap->skip, skip_only, false, 1);
+ int tabnr = (int)get_address(eap, &eap->cmd, ADDR_TABS, eap->skip, skip_only,
+ false, 1);
if (tabnr == MAXLNUM) {
- cmdmod.tab = tabpage_index(curtab) + 1;
+ cmod->cmod_tab = tabpage_index(curtab) + 1;
} else {
if (tabnr < 0 || tabnr > LAST_TAB_NR) {
*errormsg = _(e_invrange);
return false;
}
- cmdmod.tab = tabnr + 1;
+ cmod->cmod_tab = tabnr + 1;
}
}
eap->cmd = p;
@@ -2258,38 +2630,29 @@ int parse_command_modifiers(exarg_T *eap, char **errormsg, bool skip_only)
if (!checkforcmd(&eap->cmd, "topleft", 2)) {
break;
}
- cmdmod.split |= WSP_TOP;
+ cmod->cmod_split |= WSP_TOP;
continue;
case 'u':
if (!checkforcmd(&eap->cmd, "unsilent", 3)) {
break;
}
- if (!skip_only) {
- if (eap->save_msg_silent == -1) {
- eap->save_msg_silent = msg_silent;
- }
- msg_silent = 0;
- }
+ cmod->cmod_flags |= CMOD_UNSILENT;
continue;
case 'v':
if (checkforcmd(&eap->cmd, "vertical", 4)) {
- cmdmod.split |= WSP_VERT;
+ cmod->cmod_split |= WSP_VERT;
continue;
}
if (!checkforcmd(&p, "verbose", 4)) {
break;
}
- if (!skip_only) {
- if (eap->verbose_save < 0) {
- eap->verbose_save = p_verbose;
- }
- if (ascii_isdigit(*eap->cmd)) {
- p_verbose = atoi((char *)eap->cmd);
- } else {
- p_verbose = 1;
- }
+ if (ascii_isdigit(*eap->cmd)) {
+ // zero means not set, one is verbose == 0, etc.
+ cmod->cmod_verbose = atoi(eap->cmd) + 1;
+ } else {
+ cmod->cmod_verbose = 2; // default: verbose == 1
}
eap->cmd = p;
continue;
@@ -2300,98 +2663,116 @@ int parse_command_modifiers(exarg_T *eap, char **errormsg, bool skip_only)
return OK;
}
-// Undo and free contents of "cmdmod".
-static void undo_cmdmod(const exarg_T *eap, int save_msg_scroll)
+/// Apply the command modifiers. Saves current state in "cmdmod", call
+/// undo_cmdmod() later.
+static void apply_cmdmod(cmdmod_T *cmod)
+{
+ if ((cmod->cmod_flags & CMOD_SANDBOX) && !cmod->cmod_did_sandbox) {
+ sandbox++;
+ cmod->cmod_did_sandbox = true;
+ }
+ if (cmod->cmod_verbose > 0) {
+ if (cmod->cmod_verbose_save == 0) {
+ cmod->cmod_verbose_save = p_verbose + 1;
+ }
+ p_verbose = cmod->cmod_verbose - 1;
+ }
+
+ if ((cmod->cmod_flags & (CMOD_SILENT | CMOD_UNSILENT))
+ && cmod->cmod_save_msg_silent == 0) {
+ cmod->cmod_save_msg_silent = msg_silent + 1;
+ cmod->cmod_save_msg_scroll = msg_scroll;
+ }
+ if (cmod->cmod_flags & CMOD_SILENT) {
+ msg_silent++;
+ }
+ if (cmod->cmod_flags & CMOD_UNSILENT) {
+ msg_silent = 0;
+ }
+
+ if (cmod->cmod_flags & CMOD_ERRSILENT) {
+ emsg_silent++;
+ cmod->cmod_did_esilent++;
+ }
+
+ if ((cmod->cmod_flags & CMOD_NOAUTOCMD) && cmod->cmod_save_ei == NULL) {
+ // Set 'eventignore' to "all".
+ // First save the existing option value for restoring it later.
+ cmod->cmod_save_ei = (char *)vim_strsave(p_ei);
+ set_string_option_direct("ei", -1, "all", OPT_FREE, SID_NONE);
+ }
+}
+
+/// Undo and free contents of "cmod".
+void undo_cmdmod(cmdmod_T *cmod)
FUNC_ATTR_NONNULL_ALL
{
- if (eap->verbose_save >= 0) {
- p_verbose = eap->verbose_save;
+ if (cmod->cmod_verbose_save > 0) {
+ p_verbose = cmod->cmod_verbose_save - 1;
+ cmod->cmod_verbose_save = 0;
+ }
+
+ if (cmod->cmod_did_sandbox) {
+ sandbox--;
+ cmod->cmod_did_sandbox = false;
}
- if (cmdmod.save_ei != NULL) {
+ if (cmod->cmod_save_ei != NULL) {
// Restore 'eventignore' to the value before ":noautocmd".
- set_string_option_direct("ei", -1, cmdmod.save_ei, OPT_FREE, SID_NONE);
- free_string_option(cmdmod.save_ei);
+ set_string_option_direct("ei", -1, cmod->cmod_save_ei, OPT_FREE, SID_NONE);
+ free_string_option((char_u *)cmod->cmod_save_ei);
+ cmod->cmod_save_ei = NULL;
}
- vim_regfree(cmdmod.filter_regmatch.regprog);
+ xfree(cmod->cmod_filter_pat);
+ vim_regfree(cmod->cmod_filter_regmatch.regprog);
- if (eap->save_msg_silent != -1) {
+ if (cmod->cmod_save_msg_silent > 0) {
// messages could be enabled for a serious error, need to check if the
// counters don't become negative
- if (!did_emsg || msg_silent > eap->save_msg_silent) {
- msg_silent = eap->save_msg_silent;
+ if (!did_emsg || msg_silent > cmod->cmod_save_msg_silent - 1) {
+ msg_silent = cmod->cmod_save_msg_silent - 1;
}
- emsg_silent -= eap->did_esilent;
+ emsg_silent -= cmod->cmod_did_esilent;
if (emsg_silent < 0) {
emsg_silent = 0;
}
// Restore msg_scroll, it's set by file I/O commands, even when no
// message is actually displayed.
- msg_scroll = save_msg_scroll;
+ msg_scroll = cmod->cmod_save_msg_scroll;
// "silent reg" or "silent echo x" inside "redir" leaves msg_col
// somewhere in the line. Put it back in the first column.
if (redirecting()) {
msg_col = 0;
}
+
+ cmod->cmod_save_msg_silent = 0;
+ cmod->cmod_did_esilent = 0;
}
}
-
-// Parse the address range, if any, in "eap".
-// May set the last search pattern, unless "silent" is true.
-// Return FAIL and set "errormsg" or return OK.
+/// Parse the address range, if any, in "eap".
+/// May set the last search pattern, unless "silent" is true.
+///
+/// @return FAIL and set "errormsg" or return OK.
int parse_cmd_address(exarg_T *eap, char **errormsg, bool silent)
FUNC_ATTR_NONNULL_ALL
{
int address_count = 1;
linenr_T lnum;
+ bool need_check_cursor = false;
+ int ret = FAIL;
// Repeat for all ',' or ';' separated addresses.
for (;;) {
eap->line1 = eap->line2;
- switch (eap->addr_type) {
- case ADDR_LINES:
- case ADDR_OTHER:
- // default is current line number
- eap->line2 = curwin->w_cursor.lnum;
- break;
- case ADDR_WINDOWS:
- eap->line2 = CURRENT_WIN_NR;
- break;
- case ADDR_ARGUMENTS:
- eap->line2 = curwin->w_arg_idx + 1;
- if (eap->line2 > ARGCOUNT) {
- eap->line2 = ARGCOUNT;
- }
- break;
- case ADDR_LOADED_BUFFERS:
- case ADDR_BUFFERS:
- eap->line2 = curbuf->b_fnum;
- break;
- case ADDR_TABS:
- eap->line2 = CURRENT_TAB_NR;
- break;
- case ADDR_TABS_RELATIVE:
- case ADDR_UNSIGNED:
- eap->line2 = 1;
- break;
- case ADDR_QUICKFIX:
- eap->line2 = qf_get_cur_idx(eap);
- break;
- case ADDR_QUICKFIX_VALID:
- eap->line2 = qf_get_cur_valid_idx(eap);
- break;
- case ADDR_NONE:
- // Will give an error later if a range is found.
- break;
- }
+ eap->line2 = get_cmd_default_range(eap);
eap->cmd = skipwhite(eap->cmd);
lnum = get_address(eap, &eap->cmd, eap->addr_type, eap->skip, silent,
eap->addr_count == 0, address_count++);
if (eap->cmd == NULL) { // error detected
- return FAIL;
+ goto theend;
}
if (lnum == MAXLNUM) {
if (*eap->cmd == '%') { // '%' - all lines
@@ -2430,14 +2811,14 @@ int parse_cmd_address(exarg_T *eap, char **errormsg, bool silent)
// there is no Vim command which uses '%' and
// ADDR_WINDOWS or ADDR_TABS
*errormsg = _(e_invrange);
- return FAIL;
+ goto theend;
}
break;
case ADDR_TABS_RELATIVE:
case ADDR_UNSIGNED:
case ADDR_QUICKFIX:
*errormsg = _(e_invrange);
- return FAIL;
+ goto theend;
case ADDR_ARGUMENTS:
if (ARGCOUNT == 0) {
eap->line1 = eap->line2 = 0;
@@ -2448,7 +2829,7 @@ int parse_cmd_address(exarg_T *eap, char **errormsg, bool silent)
break;
case ADDR_QUICKFIX_VALID:
eap->line1 = 1;
- eap->line2 = qf_get_valid_size(eap);
+ eap->line2 = (linenr_T)qf_get_valid_size(eap);
if (eap->line2 == 0) {
eap->line2 = 1;
}
@@ -2462,21 +2843,21 @@ int parse_cmd_address(exarg_T *eap, char **errormsg, bool silent)
// '*' - visual area
if (eap->addr_type != ADDR_LINES) {
*errormsg = _(e_invrange);
- return FAIL;
+ goto theend;
}
eap->cmd++;
if (!eap->skip) {
- pos_T *fp = getmark('<', false);
- if (check_mark(fp) == FAIL) {
- return FAIL;
+ fmark_T *fm = mark_get_visual(curbuf, '<');
+ if (!mark_check(fm)) {
+ goto theend;
}
- eap->line1 = fp->lnum;
- fp = getmark('>', false);
- if (check_mark(fp) == FAIL) {
- return FAIL;
+ eap->line1 = fm->mark.lnum;
+ fm = mark_get_visual(curbuf, '>');
+ if (!mark_check(fm)) {
+ goto theend;
}
- eap->line2 = fp->lnum;
+ eap->line2 = fm->mark.lnum;
eap->addr_count++;
}
}
@@ -2488,11 +2869,17 @@ int parse_cmd_address(exarg_T *eap, char **errormsg, bool silent)
if (*eap->cmd == ';') {
if (!eap->skip) {
curwin->w_cursor.lnum = eap->line2;
+
// Don't leave the cursor on an illegal line or column, but do
- // accept zero as address, so 0;/PATTERN/ works correctly.
+ // accept zero as address, so 0;/PATTERN/ works correctly
+ // (where zero usually means to use the first line).
+ // Check the cursor position before returning.
if (eap->line2 > 0) {
check_cursor();
+ } else {
+ check_cursor_col();
}
+ need_check_cursor = true;
}
} else if (*eap->cmd != ',') {
break;
@@ -2508,7 +2895,13 @@ int parse_cmd_address(exarg_T *eap, char **errormsg, bool silent)
eap->addr_count = 0;
}
}
- return OK;
+ ret = OK;
+
+theend:
+ if (need_check_cursor) {
+ check_cursor();
+ }
+ return ret;
}
/// Check for an Ex command with optional tail.
@@ -2517,56 +2910,64 @@ int parse_cmd_address(exarg_T *eap, char **errormsg, bool silent)
/// @param pp start of command
/// @param cmd name of command
/// @param len required length
-int checkforcmd(char_u **pp, char *cmd, int len)
+int checkforcmd(char **pp, char *cmd, int len)
{
int i;
- for (i = 0; cmd[i] != NUL; ++i) {
- if (((char_u *)cmd)[i] != (*pp)[i]) {
+ for (i = 0; cmd[i] != NUL; i++) {
+ if ((cmd)[i] != (*pp)[i]) {
break;
}
}
if (i >= len && !isalpha((*pp)[i])) {
*pp = skipwhite(*pp + i);
- return TRUE;
+ return true;
}
return FALSE;
}
-/*
- * Append "cmd" to the error message in IObuff.
- * Takes care of limiting the length and handling 0xa0, which would be
- * invisible otherwise.
- */
-static void append_command(char_u *cmd)
+/// Append "cmd" to the error message in IObuff.
+/// Takes care of limiting the length and handling 0xa0, which would be
+/// invisible otherwise.
+static void append_command(char *cmd)
{
- char_u *s = cmd;
- char_u *d;
+ size_t len = STRLEN(IObuff);
+ char *s = cmd;
+ char *d;
+ if (len > IOSIZE - 100) {
+ // Not enough space, truncate and put in "...".
+ d = (char *)IObuff + IOSIZE - 100;
+ d -= utf_head_off(IObuff, (const char_u *)d);
+ STRCPY(d, "...");
+ }
STRCAT(IObuff, ": ");
- d = IObuff + STRLEN(IObuff);
- while (*s != NUL && d - IObuff < IOSIZE - 7) {
- if (s[0] == 0xc2 && s[1] == 0xa0) {
+ d = (char *)IObuff + STRLEN(IObuff);
+ while (*s != NUL && (char_u *)d - IObuff + 5 < IOSIZE) {
+ if ((char_u)s[0] == 0xc2 && (char_u)s[1] == 0xa0) {
s += 2;
STRCPY(d, "<a0>");
d += 4;
+ } else if ((char_u *)d - IObuff + utfc_ptr2len(s) + 1 >= IOSIZE) {
+ break;
} else {
- mb_copy_char((const char_u **)&s, &d);
+ mb_copy_char((const char_u **)&s, (char_u **)&d);
}
}
*d = NUL;
}
-// Find an Ex command by its name, either built-in or user.
-// Start of the name can be found at eap->cmd.
-// Sets eap->cmdidx and returns a pointer to char after the command name.
-// "full" is set to TRUE if the whole command name matched.
-// Returns NULL for an ambiguous user command.
-static char_u *find_command(exarg_T *eap, int *full)
+/// Find an Ex command by its name, either built-in or user.
+/// Start of the name can be found at eap->cmd.
+/// Sets eap->cmdidx and returns a pointer to char after the command name.
+/// "full" is set to TRUE if the whole command name matched.
+///
+/// @return NULL for an ambiguous user command.
+char *find_ex_command(exarg_T *eap, int *full)
FUNC_ATTR_NONNULL_ARG(1)
{
int len;
- char_u *p;
+ char *p;
int i;
/*
@@ -2606,15 +3007,15 @@ static char_u *find_command(exarg_T *eap, int *full)
}
// check for non-alpha command
- if (p == eap->cmd && vim_strchr((char_u *)"@!=><&~#", *p) != NULL) {
- ++p;
+ if (p == eap->cmd && vim_strchr("@!=><&~#", *p) != NULL) {
+ p++;
}
len = (int)(p - eap->cmd);
if (*eap->cmd == 'd' && (p[-1] == 'l' || p[-1] == 'p')) {
// Check for ":dl", ":dell", etc. to ":deletel": that's
// :delete with the 'l' flag. Same for 'p'.
for (i = 0; i < len; i++) {
- if (eap->cmd[i] != ((char_u *)"delete")[i]) {
+ if (eap->cmd[i] != ("delete")[i]) {
break;
}
}
@@ -2629,7 +3030,7 @@ static char_u *find_command(exarg_T *eap, int *full)
}
if (ASCII_ISLOWER(eap->cmd[0])) {
- const int c1 = eap->cmd[0];
+ const int c1 = (char_u)eap->cmd[0];
const int c2 = len == 1 ? NUL : eap->cmd[1];
if (command_count != CMD_SIZE) {
@@ -2639,17 +3040,19 @@ static char_u *find_command(exarg_T *eap, int *full)
// Use a precomputed index for fast look-up in cmdnames[]
// taking into account the first 2 letters of eap->cmd.
- eap->cmdidx = cmdidxs1[CharOrdLow(c1)];
+ eap->cmdidx = cmdidxs1[CHAR_ORD_LOW(c1)];
if (ASCII_ISLOWER(c2)) {
- eap->cmdidx += cmdidxs2[CharOrdLow(c1)][CharOrdLow(c2)];
+ eap->cmdidx += cmdidxs2[CHAR_ORD_LOW(c1)][CHAR_ORD_LOW(c2)];
}
+ } else if (ASCII_ISUPPER(eap->cmd[0])) {
+ eap->cmdidx = CMD_Next;
} else {
eap->cmdidx = CMD_bang;
}
for (; (int)eap->cmdidx < CMD_SIZE;
eap->cmdidx = (cmdidx_T)((int)eap->cmdidx + 1)) {
- if (STRNCMP(cmdnames[(int)eap->cmdidx].cmd_name, (char *)eap->cmd,
+ if (STRNCMP(cmdnames[(int)eap->cmdidx].cmd_name, eap->cmd,
(size_t)len) == 0) {
if (full != NULL
&& cmdnames[(int)eap->cmdidx].cmd_name[len] == NUL) {
@@ -2685,27 +3088,25 @@ static char_u *find_command(exarg_T *eap, int *full)
/// @param full set to TRUE for a full match
/// @param xp used for completion, NULL otherwise
/// @param complp completion flags or NULL
-static char_u *find_ucmd(exarg_T *eap, char_u *p, int *full, expand_T *xp, int *complp)
+static char *find_ucmd(exarg_T *eap, char *p, int *full, expand_T *xp, int *complp)
{
int len = (int)(p - eap->cmd);
int j, k, matchlen = 0;
ucmd_T *uc;
bool found = false;
bool possible = false;
- char_u *cp, *np; // Point into typed cmd and test name
+ char *cp, *np; // Point into typed cmd and test name
garray_T *gap;
bool amb_local = false; // Found ambiguous buffer-local command,
// only full match global is accepted.
- /*
- * Look for buffer-local user commands first, then global ones.
- */
- gap = &curbuf->b_ucmds;
+ // Look for buffer-local user commands first, then global ones.
+ gap = &prevwin_curwin()->w_buffer->b_ucmds;
for (;;) {
for (j = 0; j < gap->ga_len; j++) {
uc = USER_CMD_GA(gap, j);
cp = eap->cmd;
- np = uc->uc_name;
+ np = (char *)uc->uc_name;
k = 0;
while (k < len && *np != NUL && *cp++ == *np++) {
k++;
@@ -2746,7 +3147,7 @@ static char_u *find_ucmd(exarg_T *eap, char_u *p, int *full, expand_T *xp, int *
}
if (xp != NULL) {
xp->xp_luaref = uc->uc_compl_luaref;
- xp->xp_arg = uc->uc_compl_arg;
+ xp->xp_arg = (char *)uc->uc_compl_arg;
xp->xp_script_ctx = uc->uc_script_ctx;
xp->xp_script_ctx.sc_lnum += sourcing_lnum;
}
@@ -2817,13 +3218,11 @@ static struct cmdmod {
{ "vertical", 4, false },
};
-/*
- * Return length of a command modifier (including optional count).
- * Return zero when it's not a modifier.
- */
-int modifier_len(char_u *cmd)
+/// @return length of a command modifier (including optional count) or,
+/// zero when it's not a modifier.
+int modifier_len(char *cmd)
{
- char_u *p = cmd;
+ char *p = cmd;
if (ascii_isdigit(*cmd)) {
p = skipwhite(skipdigits(cmd + 1));
@@ -2844,15 +3243,13 @@ int modifier_len(char_u *cmd)
return 0;
}
-/*
- * Return > 0 if an Ex command "name" exists.
- * Return 2 if there is an exact match.
- * Return 3 if there is an ambiguous match.
- */
+/// @return > 0 if an Ex command "name" exists or,
+/// 2 if there is an exact match or,
+/// 3 if there is an ambiguous match.
int cmd_exists(const char *const name)
{
exarg_T ea;
- char_u *p;
+ char *p;
// Check command modifiers.
for (int i = 0; i < (int)ARRAY_SIZE(cmdmods); i++) {
@@ -2869,10 +3266,10 @@ int cmd_exists(const char *const name)
// Check built-in commands and user defined commands.
// For ":2match" and ":3match" we need to skip the number.
- ea.cmd = (char_u *)((*name == '2' || *name == '3') ? name + 1 : name);
+ ea.cmd = (char *)((*name == '2' || *name == '3') ? name + 1 : name);
ea.cmdidx = (cmdidx_T)0;
int full = false;
- p = find_command(&ea, &full);
+ p = find_ex_command(&ea, &full);
if (p == NULL) {
return 3;
}
@@ -2885,29 +3282,34 @@ int cmd_exists(const char *const name)
return ea.cmdidx == CMD_SIZE ? 0 : (full ? 2 : 1);
}
-// "fullcommand" function
+/// "fullcommand" function
void f_fullcommand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
exarg_T ea;
- char_u *name = argvars[0].vval.v_string;
+ char *name = argvars[0].vval.v_string;
+
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = NULL;
+ if (name == NULL) {
+ return;
+ }
- while (name[0] != NUL && name[0] == ':') {
+ 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);
+ char *p = find_ex_command(&ea, NULL);
if (p == NULL || ea.cmdidx == CMD_SIZE) {
return;
}
- rettv->vval.v_string = vim_strsave(IS_USER_CMDIDX(ea.cmdidx)
- ? get_user_commands(NULL, ea.useridx)
- : cmdnames[ea.cmdidx].cmd_name);
+ rettv->vval.v_string = (char *)vim_strsave(IS_USER_CMDIDX(ea.cmdidx)
+ ? (char_u *)get_user_command_name(ea.useridx,
+ ea.cmdidx)
+ : (char_u *)cmdnames[ea.cmdidx].cmd_name);
}
/// This is all pretty much copied from do_one_cmd(), with all the extra stuff
@@ -2926,16 +3328,15 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
bool usefilter = false; // Filter instead of file name.
ExpandInit(xp);
- xp->xp_pattern = (char_u *)buff;
- xp->xp_line = (char_u *)buff;
+ xp->xp_pattern = (char *)buff;
+ xp->xp_line = (char *)buff;
xp->xp_context = EXPAND_COMMANDS; // Default until we get past command
ea.argt = 0;
// 2. skip comment lines and leading space, colons or bars
const char *cmd;
- for (cmd = buff; vim_strchr((const char_u *)" \t:|", *cmd) != NULL; cmd++) {
- }
- xp->xp_pattern = (char_u *)cmd;
+ for (cmd = buff; vim_strchr(" \t:|", *cmd) != NULL; cmd++) {}
+ xp->xp_pattern = (char *)cmd;
if (*cmd == NUL) {
return NULL;
@@ -2948,12 +3349,12 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
/*
* 3. parse a range specifier of the form: addr [,addr] [;addr] ..
*/
- cmd = (const char *)skip_range((const char_u *)cmd, &xp->xp_context);
+ cmd = (const char *)skip_range(cmd, &xp->xp_context);
/*
* 4. parse command
*/
- xp->xp_pattern = (char_u *)cmd;
+ xp->xp_pattern = (char *)cmd;
if (*cmd == NUL) {
return NULL;
}
@@ -2995,7 +3396,7 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
}
}
// check for non-alpha command
- if (p == cmd && vim_strchr((const char_u *)"@*!=><&~#", *p) != NULL) {
+ if (p == cmd && vim_strchr("@*!=><&~#", *p) != NULL) {
p++;
}
len = (size_t)(p - cmd);
@@ -3027,12 +3428,12 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
}
if (ea.cmdidx == CMD_SIZE) {
- if (*cmd == 's' && vim_strchr((const char_u *)"cgriI", cmd[1]) != NULL) {
+ if (*cmd == 's' && vim_strchr("cgriI", cmd[1]) != NULL) {
ea.cmdidx = CMD_substitute;
p = cmd + 1;
} else if (cmd[0] >= 'A' && cmd[0] <= 'Z') {
- ea.cmd = (char_u *)cmd;
- p = (const char *)find_ucmd(&ea, (char_u *)p, NULL, xp, &context);
+ ea.cmd = (char *)cmd;
+ p = (const char *)find_ucmd(&ea, (char *)p, NULL, xp, &context);
if (p == NULL) {
ea.cmdidx = CMD_SIZE; // Ambiguous user command.
}
@@ -3058,7 +3459,7 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
ea.argt = cmdnames[(int)ea.cmdidx].cmd_argt;
}
- const char *arg = (const char *)skipwhite((const char_u *)p);
+ const char *arg = (const char *)skipwhite(p);
// Skip over ++argopt argument
if ((ea.argt & EX_ARGOPT) && *arg != NUL && strncmp(arg, "++", 2) == 0) {
@@ -3066,7 +3467,7 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
while (*p && !ascii_isspace(*p)) {
MB_PTR_ADV(p);
}
- arg = (const char *)skipwhite((const char_u *)p);
+ arg = (const char *)skipwhite(p);
}
if (ea.cmdidx == CMD_write || ea.cmdidx == CMD_update) {
@@ -3074,7 +3475,7 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
if (*++arg == '>') {
arg++;
}
- arg = (const char *)skipwhite((const char_u *)arg);
+ arg = (const char *)skipwhite(arg);
} else if (*arg == '!' && ea.cmdidx == CMD_write) { // :w !filter
arg++;
usefilter = true;
@@ -3093,14 +3494,14 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
while (*arg == *cmd) { // allow any number of '>' or '<'
arg++;
}
- arg = (const char *)skipwhite((const char_u *)arg);
+ arg = (const char *)skipwhite(arg);
}
// Does command allow "+command"?
if ((ea.argt & EX_CMDARG) && !usefilter && *arg == '+') {
// Check if we're in the +command
p = arg + 1;
- arg = (const char *)skip_cmd_arg((char_u *)arg, false);
+ arg = (const char *)skip_cmd_arg((char *)arg, false);
// Still touching the command after '+'?
if (*arg == NUL) {
@@ -3108,7 +3509,7 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
}
// Skip space(s) after +command to get to the real argument.
- arg = (const char *)skipwhite((const char_u *)arg);
+ arg = (const char *)skipwhite(arg);
}
/*
@@ -3147,12 +3548,12 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
// Find start of last argument (argument just before cursor):
p = buff;
- xp->xp_pattern = (char_u *)p;
+ xp->xp_pattern = (char *)p;
len = strlen(buff);
while (*p && p < buff + len) {
if (*p == ' ' || *p == TAB) {
// Argument starts after a space.
- xp->xp_pattern = (char_u *)++p;
+ xp->xp_pattern = (char *)++p;
} else {
if (*p == '\\' && *(p + 1) != NUL) {
p++; // skip over escaped character
@@ -3170,15 +3571,15 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
* Allow spaces within back-quotes to count as part of the argument
* being expanded.
*/
- xp->xp_pattern = skipwhite((const char_u *)arg);
+ xp->xp_pattern = skipwhite(arg);
p = (const char *)xp->xp_pattern;
while (*p != NUL) {
- c = utf_ptr2char((const char_u *)p);
+ c = utf_ptr2char(p);
if (c == '\\' && p[1] != NUL) {
p++;
} else if (c == '`') {
if (!in_quote) {
- xp->xp_pattern = (char_u *)p;
+ xp->xp_pattern = (char *)p;
bow = p + 1;
}
in_quote = !in_quote;
@@ -3191,17 +3592,17 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
|| ascii_iswhite(c)) {
len = 0; // avoid getting stuck when space is in 'isfname'
while (*p != NUL) {
- c = utf_ptr2char((const char_u *)p);
+ c = utf_ptr2char(p);
if (c == '`' || vim_isfilec_or_wc(c)) {
break;
}
- len = (size_t)utfc_ptr2len((const char_u *)p);
+ len = (size_t)utfc_ptr2len(p);
MB_PTR_ADV(p);
}
if (in_quote) {
bow = p;
} else {
- xp->xp_pattern = (char_u *)p;
+ xp->xp_pattern = (char *)p;
}
p -= len;
}
@@ -3213,7 +3614,7 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
* expand from there.
*/
if (bow != NULL && in_quote) {
- xp->xp_pattern = (char_u *)bow;
+ xp->xp_pattern = (char *)bow;
}
xp->xp_context = EXPAND_FILES;
@@ -3223,7 +3624,7 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
xp->xp_shell = TRUE;
#endif
// When still after the command name expand executables.
- if (xp->xp_pattern == skipwhite((const char_u *)arg)) {
+ if (xp->xp_pattern == skipwhite(arg)) {
xp->xp_context = EXPAND_SHELLCMD;
}
}
@@ -3246,13 +3647,12 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
}
// Check for user names.
if (*xp->xp_pattern == '~') {
- for (p = (const char *)xp->xp_pattern + 1; *p != NUL && *p != '/'; p++) {
- }
+ for (p = (const char *)xp->xp_pattern + 1; *p != NUL && *p != '/'; p++) {}
// Complete ~user only if it partially matches a user name.
// A full match ~user<Tab> will be replaced by user's home
// directory i.e. something like ~user<Tab> -> /home/user/
if (*p == NUL && p > (const char *)xp->xp_pattern + 1
- && match_user(xp->xp_pattern + 1) >= 1) {
+ && match_user((char_u *)xp->xp_pattern + 1) >= 1) {
xp->xp_context = EXPAND_USER;
++xp->xp_pattern;
}
@@ -3282,7 +3682,7 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
break;
case CMD_help:
xp->xp_context = EXPAND_HELP;
- xp->xp_pattern = (char_u *)arg;
+ xp->xp_pattern = (char *)arg;
break;
/* Command modifiers: return the argument.
@@ -3323,19 +3723,19 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
case CMD_filter:
if (*arg != NUL) {
- arg = (const char *)skip_vimgrep_pat((char_u *)arg, NULL, NULL);
+ arg = (const char *)skip_vimgrep_pat((char *)arg, NULL, NULL);
}
if (arg == NULL || *arg == NUL) {
xp->xp_context = EXPAND_NOTHING;
return NULL;
}
- return (const char *)skipwhite((const char_u *)arg);
+ return (const char *)skipwhite(arg);
case CMD_match:
if (*arg == NUL || !ends_excmd(*arg)) {
// also complete "None"
set_context_in_echohl_cmd(xp, arg);
- arg = (const char *)skipwhite(skiptowhite((const char_u *)arg));
+ arg = (const char *)skipwhite((char *)skiptowhite((const char_u *)arg));
if (*arg != NUL) {
xp->xp_context = EXPAND_NOTHING;
arg = (const char *)skip_regexp((char_u *)arg + 1, (uint8_t)(*arg),
@@ -3359,7 +3759,7 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
if (p == NULL) {
// No "=", so complete attribute names.
xp->xp_context = EXPAND_USER_CMD_FLAGS;
- xp->xp_pattern = (char_u *)arg;
+ xp->xp_pattern = (char *)arg;
return NULL;
}
@@ -3367,36 +3767,36 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
// their arguments as well.
if (STRNICMP(arg, "complete", p - arg) == 0) {
xp->xp_context = EXPAND_USER_COMPLETE;
- xp->xp_pattern = (char_u *)p + 1;
+ xp->xp_pattern = (char *)p + 1;
return NULL;
} else if (STRNICMP(arg, "nargs", p - arg) == 0) {
xp->xp_context = EXPAND_USER_NARGS;
- xp->xp_pattern = (char_u *)p + 1;
+ xp->xp_pattern = (char *)p + 1;
return NULL;
} else if (STRNICMP(arg, "addr", p - arg) == 0) {
xp->xp_context = EXPAND_USER_ADDR_TYPE;
- xp->xp_pattern = (char_u *)p + 1;
+ xp->xp_pattern = (char *)p + 1;
return NULL;
}
return NULL;
}
- arg = (const char *)skipwhite((char_u *)p);
+ arg = (const char *)skipwhite(p);
}
// After the attributes comes the new command name.
p = (const char *)skiptowhite((const char_u *)arg);
if (*p == NUL) {
xp->xp_context = EXPAND_USER_COMMANDS;
- xp->xp_pattern = (char_u *)arg;
+ xp->xp_pattern = (char *)arg;
break;
}
// And finally comes a normal command.
- return (const char *)skipwhite((const char_u *)p);
+ return (const char *)skipwhite(p);
case CMD_delcommand:
xp->xp_context = EXPAND_USER_COMMANDS;
- xp->xp_pattern = (char_u *)arg;
+ xp->xp_pattern = (char *)arg;
break;
case CMD_global:
@@ -3453,7 +3853,7 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
case CMD_isplit:
case CMD_dsplit:
// Skip count.
- arg = (const char *)skipwhite(skipdigits((const char_u *)arg));
+ arg = (const char *)skipwhite(skipdigits(arg));
if (*arg == '/') { // Match regexp, not just whole words.
for (++arg; *arg && *arg != '/'; arg++) {
if (*arg == '\\' && arg[1] != NUL) {
@@ -3461,7 +3861,7 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
}
}
if (*arg) {
- arg = (const char *)skipwhite((const char_u *)arg + 1);
+ arg = (const char *)skipwhite(arg + 1);
// Check for trailing illegal characters.
if (*arg && strchr("|\"\n", *arg) == NULL) {
@@ -3473,11 +3873,11 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
}
break;
case CMD_autocmd:
- return (const char *)set_context_in_autocmd(xp, (char_u *)arg, false);
+ return (const char *)set_context_in_autocmd(xp, (char *)arg, false);
case CMD_doautocmd:
case CMD_doautoall:
- return (const char *)set_context_in_autocmd(xp, (char_u *)arg, true);
+ return (const char *)set_context_in_autocmd(xp, (char *)arg, true);
case CMD_set:
set_context_in_set_cmd(xp, (char_u *)arg, 0);
break;
@@ -3502,11 +3902,11 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
} else {
xp->xp_context = EXPAND_TAGS;
}
- xp->xp_pattern = (char_u *)arg;
+ xp->xp_pattern = (char *)arg;
break;
case CMD_augroup:
xp->xp_context = EXPAND_AUGROUP;
- xp->xp_pattern = (char_u *)arg;
+ xp->xp_pattern = (char *)arg;
break;
case CMD_syntax:
set_context_in_syntax_cmd(xp, arg);
@@ -3530,16 +3930,16 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
case CMD_lexpr:
case CMD_laddexpr:
case CMD_lgetexpr:
- set_context_for_expression(xp, (char_u *)arg, ea.cmdidx);
+ set_context_for_expression(xp, (char *)arg, ea.cmdidx);
break;
case CMD_unlet:
- while ((xp->xp_pattern = (char_u *)strchr(arg, ' ')) != NULL) {
+ while ((xp->xp_pattern = strchr(arg, ' ')) != NULL) {
arg = (const char *)xp->xp_pattern + 1;
}
xp->xp_context = EXPAND_USER_VARS;
- xp->xp_pattern = (char_u *)arg;
+ xp->xp_pattern = (char *)arg;
if (*xp->xp_pattern == '$') {
xp->xp_context = EXPAND_ENV_VARS;
@@ -3551,7 +3951,7 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
case CMD_function:
case CMD_delfunction:
xp->xp_context = EXPAND_USER_FUNC;
- xp->xp_pattern = (char_u *)arg;
+ xp->xp_pattern = (char *)arg;
break;
case CMD_echohl:
@@ -3571,7 +3971,7 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
case CMD_bdelete:
case CMD_bwipeout:
case CMD_bunload:
- while ((xp->xp_pattern = (char_u *)strchr(arg, ' ')) != NULL) {
+ while ((xp->xp_pattern = strchr(arg, ' ')) != NULL) {
arg = (const char *)xp->xp_pattern + 1;
}
FALLTHROUGH;
@@ -3579,14 +3979,14 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
case CMD_sbuffer:
case CMD_checktime:
xp->xp_context = EXPAND_BUFFERS;
- xp->xp_pattern = (char_u *)arg;
+ xp->xp_pattern = (char *)arg;
break;
case CMD_diffget:
case CMD_diffput:
// If current buffer is in diff mode, complete buffer names
// which are in diff mode, and different than current buffer.
xp->xp_context = EXPAND_DIFF_BUFFERS;
- xp->xp_pattern = (char_u *)arg;
+ xp->xp_pattern = (char *)arg;
break;
case CMD_USER:
case CMD_USER_BUF:
@@ -3594,8 +3994,7 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
// EX_XFILE: file names are handled above.
if (!(ea.argt & EX_XFILE)) {
if (context == EXPAND_MENUS) {
- return (const char *)set_context_in_menu_cmd(xp, cmd,
- (char_u *)arg, forceit);
+ return (const char *)set_context_in_menu_cmd(xp, cmd, (char *)arg, forceit);
} else if (context == EXPAND_COMMANDS) {
return arg;
} else if (context == EXPAND_MAPPINGS) {
@@ -3614,7 +4013,7 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
}
MB_PTR_ADV(p);
}
- xp->xp_pattern = (char_u *)arg;
+ xp->xp_pattern = (char *)arg;
}
xp->xp_context = context;
}
@@ -3660,7 +4059,7 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
case CMD_smapclear:
case CMD_xmapclear:
xp->xp_context = EXPAND_MAPCLEAR;
- xp->xp_pattern = (char_u *)arg;
+ xp->xp_pattern = (char *)arg;
break;
case CMD_abbreviate:
@@ -3697,35 +4096,38 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
case CMD_cmenu:
case CMD_cnoremenu:
case CMD_cunmenu:
+ case CMD_tlmenu:
+ case CMD_tlnoremenu:
+ case CMD_tlunmenu:
case CMD_tmenu:
case CMD_tunmenu:
case CMD_popup:
case CMD_emenu:
- return (const char *)set_context_in_menu_cmd(xp, cmd, (char_u *)arg, forceit);
+ return (const char *)set_context_in_menu_cmd(xp, cmd, (char *)arg, forceit);
case CMD_colorscheme:
xp->xp_context = EXPAND_COLORS;
- xp->xp_pattern = (char_u *)arg;
+ xp->xp_pattern = (char *)arg;
break;
case CMD_compiler:
xp->xp_context = EXPAND_COMPILER;
- xp->xp_pattern = (char_u *)arg;
+ xp->xp_pattern = (char *)arg;
break;
case CMD_ownsyntax:
xp->xp_context = EXPAND_OWNSYNTAX;
- xp->xp_pattern = (char_u *)arg;
+ xp->xp_pattern = (char *)arg;
break;
case CMD_setfiletype:
xp->xp_context = EXPAND_FILETYPE;
- xp->xp_pattern = (char_u *)arg;
+ xp->xp_pattern = (char *)arg;
break;
case CMD_packadd:
xp->xp_context = EXPAND_PACKADD;
- xp->xp_pattern = (char_u *)arg;
+ xp->xp_pattern = (char *)arg;
break;
#ifdef HAVE_WORKING_LIBINTL
@@ -3733,14 +4135,14 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
p = (const char *)skiptowhite((const char_u *)arg);
if (*p == NUL) {
xp->xp_context = EXPAND_LANGUAGE;
- xp->xp_pattern = (char_u *)arg;
+ xp->xp_pattern = (char *)arg;
} else {
- if (strncmp(arg, "messages", p - arg) == 0
- || strncmp(arg, "ctype", p - arg) == 0
- || strncmp(arg, "time", p - arg) == 0
- || strncmp(arg, "collate", p - arg) == 0) {
+ if (strncmp(arg, "messages", (size_t)(p - arg)) == 0
+ || strncmp(arg, "ctype", (size_t)(p - arg)) == 0
+ || strncmp(arg, "time", (size_t)(p - arg)) == 0
+ || strncmp(arg, "collate", (size_t)(p - arg)) == 0) {
xp->xp_context = EXPAND_LOCALES;
- xp->xp_pattern = skipwhite((const char_u *)p);
+ xp->xp_pattern = skipwhite(p);
} else {
xp->xp_context = EXPAND_NOTHING;
}
@@ -3752,33 +4154,33 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
break;
case CMD_checkhealth:
xp->xp_context = EXPAND_CHECKHEALTH;
- xp->xp_pattern = (char_u *)arg;
+ xp->xp_pattern = (char *)arg;
break;
case CMD_behave:
xp->xp_context = EXPAND_BEHAVE;
- xp->xp_pattern = (char_u *)arg;
+ xp->xp_pattern = (char *)arg;
break;
case CMD_messages:
xp->xp_context = EXPAND_MESSAGES;
- xp->xp_pattern = (char_u *)arg;
+ xp->xp_pattern = (char *)arg;
break;
case CMD_history:
xp->xp_context = EXPAND_HISTORY;
- xp->xp_pattern = (char_u *)arg;
+ xp->xp_pattern = (char *)arg;
break;
case CMD_syntime:
xp->xp_context = EXPAND_SYNTIME;
- xp->xp_pattern = (char_u *)arg;
+ xp->xp_pattern = (char *)arg;
break;
case CMD_argdelete:
- while ((xp->xp_pattern = vim_strchr((const char_u *)arg, ' ')) != NULL) {
+ while ((xp->xp_pattern = vim_strchr(arg, ' ')) != NULL) {
arg = (const char *)(xp->xp_pattern + 1);
}
xp->xp_context = EXPAND_ARGLIST;
- xp->xp_pattern = (char_u *)arg;
+ xp->xp_pattern = (char *)arg;
break;
case CMD_lua:
@@ -3801,11 +4203,11 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
/// @param ctx pointer to xp_context or NULL
///
/// @return the "cmd" pointer advanced to beyond the range.
-char_u *skip_range(const char_u *cmd, int *ctx)
+char *skip_range(const char *cmd, int *ctx)
{
unsigned delim;
- while (vim_strchr((char_u *)" \t0123456789.$%'/?-+,;\\", *cmd) != NULL) {
+ while (vim_strchr(" \t0123456789.$%'/?-+,;\\", *cmd) != NULL) {
if (*cmd == '\\') {
if (cmd[1] == '?' || cmd[1] == '/' || cmd[1] == '&') {
cmd++;
@@ -3817,8 +4219,8 @@ char_u *skip_range(const char_u *cmd, int *ctx)
*ctx = EXPAND_NOTHING;
}
} else if (*cmd == '/' || *cmd == '?') {
- delim = *cmd++;
- while (*cmd != NUL && *cmd != delim) {
+ delim = (unsigned)(*cmd++);
+ while (*cmd != NUL && *cmd != (char)delim) {
if (*cmd++ == '\\' && *cmd != NUL) {
++cmd;
}
@@ -3833,9 +4235,9 @@ char_u *skip_range(const char_u *cmd, int *ctx)
}
// Skip ":" and white space.
- cmd = skip_colon_white(cmd, false);
+ cmd = skip_colon_white((char *)cmd, false);
- return (char_u *)cmd;
+ return (char *)cmd;
}
static void addr_error(cmd_addr_T addr_type)
@@ -3859,16 +4261,15 @@ static void addr_error(cmd_addr_T addr_type)
/// @param address_count 1 for first, >1 after comma
///
/// @return MAXLNUM when no Ex address was found.
-static linenr_T get_address(exarg_T *eap, char_u **ptr, cmd_addr_T addr_type, int skip, bool silent,
+static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int skip, bool silent,
int to_other_file, int address_count)
FUNC_ATTR_NONNULL_ALL
{
int c;
int i;
- long n;
- char_u *cmd;
+ linenr_T n;
+ char *cmd;
pos_T pos;
- pos_T *fp;
linenr_T lnum;
buf_T *buf;
@@ -3904,7 +4305,7 @@ static linenr_T get_address(exarg_T *eap, char_u **ptr, cmd_addr_T addr_type, in
goto error;
break;
case ADDR_QUICKFIX:
- lnum = qf_get_cur_idx(eap);
+ lnum = (linenr_T)qf_get_cur_idx(eap);
break;
case ADDR_QUICKFIX_VALID:
lnum = qf_get_cur_valid_idx(eap);
@@ -3949,13 +4350,13 @@ static linenr_T get_address(exarg_T *eap, char_u **ptr, cmd_addr_T addr_type, in
goto error;
break;
case ADDR_QUICKFIX:
- lnum = qf_get_size(eap);
+ lnum = (linenr_T)qf_get_size(eap);
if (lnum == 0) {
lnum = 1;
}
break;
case ADDR_QUICKFIX_VALID:
- lnum = qf_get_valid_size(eap);
+ lnum = (linenr_T)qf_get_valid_size(eap);
if (lnum == 0) {
lnum = 1;
}
@@ -3978,31 +4379,32 @@ static linenr_T get_address(exarg_T *eap, char_u **ptr, cmd_addr_T addr_type, in
} else {
// Only accept a mark in another file when it is
// used by itself: ":'M".
- fp = getmark(*cmd, to_other_file && cmd[1] == NUL);
- ++cmd;
- if (fp == (pos_T *)-1) {
+ MarkGet flag = to_other_file && cmd[1] == NUL ? kMarkAll : kMarkBufLocal;
+ fmark_T *fm = mark_get(curbuf, curwin, NULL, flag, *cmd);
+ cmd++;
+ if (fm != NULL && fm->fnum != curbuf->handle) {
// Jumped to another file.
lnum = curwin->w_cursor.lnum;
} else {
- if (check_mark(fp) == FAIL) {
+ if (!mark_check(fm)) {
cmd = NULL;
goto error;
}
- lnum = fp->lnum;
+ lnum = fm->mark.lnum;
}
}
break;
case '/':
case '?': // '/' or '?' - search
- c = *cmd++;
+ c = (char_u)(*cmd++);
if (addr_type != ADDR_LINES) {
addr_error(addr_type);
cmd = NULL;
goto error;
}
if (skip) { // skip "/pat/"
- cmd = skip_regexp(cmd, c, p_magic, NULL);
+ cmd = (char *)skip_regexp((char_u *)cmd, c, p_magic, NULL);
if (*cmd == c) {
++cmd;
}
@@ -4013,8 +4415,9 @@ static linenr_T get_address(exarg_T *eap, char_u **ptr, cmd_addr_T addr_type, in
// When '/' or '?' follows another address, start from
// there.
- if (lnum != MAXLNUM) {
- curwin->w_cursor.lnum = lnum;
+ if (lnum > 0 && lnum != MAXLNUM) {
+ curwin->w_cursor.lnum
+ = lnum > curbuf->b_ml.ml_line_count ? curbuf->b_ml.ml_line_count : lnum;
}
// Start a forward search at the end of the line (unless
@@ -4030,7 +4433,7 @@ static linenr_T get_address(exarg_T *eap, char_u **ptr, cmd_addr_T addr_type, in
}
searchcmdlen = 0;
flags = silent ? 0 : SEARCH_HIS | SEARCH_MSG;
- if (!do_search(NULL, c, c, cmd, 1L, flags, NULL)) {
+ if (!do_search(NULL, c, c, (char_u *)cmd, 1L, flags, NULL)) {
curwin->w_cursor = pos;
cmd = NULL;
goto error;
@@ -4079,7 +4482,7 @@ static linenr_T get_address(exarg_T *eap, char_u **ptr, cmd_addr_T addr_type, in
default:
if (ascii_isdigit(*cmd)) { // absolute line number
- lnum = getdigits_long(&cmd, false, 0);
+ lnum = (linenr_T)getdigits((char_u **)&cmd, false, 0);
}
}
@@ -4113,7 +4516,7 @@ static linenr_T get_address(exarg_T *eap, char_u **ptr, cmd_addr_T addr_type, in
lnum = 1;
break;
case ADDR_QUICKFIX:
- lnum = qf_get_cur_idx(eap);
+ lnum = (linenr_T)qf_get_cur_idx(eap);
break;
case ADDR_QUICKFIX_VALID:
lnum = qf_get_cur_valid_idx(eap);
@@ -4128,12 +4531,16 @@ static linenr_T get_address(exarg_T *eap, char_u **ptr, cmd_addr_T addr_type, in
if (ascii_isdigit(*cmd)) {
i = '+'; // "number" is same as "+number"
} else {
- i = *cmd++;
+ i = (char_u)(*cmd++);
}
if (!ascii_isdigit(*cmd)) { // '+' is '+1', but '+0' is not '+1'
n = 1;
} else {
- n = getdigits(&cmd, true, 0);
+ n = getdigits_int32(&cmd, false, MAXLNUM);
+ if (n == MAXLNUM) {
+ emsg(_(e_line_number_out_of_range));
+ goto error;
+ }
}
if (addr_type == ADDR_TABS_RELATIVE) {
@@ -4152,6 +4559,10 @@ static linenr_T get_address(exarg_T *eap, char_u **ptr, cmd_addr_T addr_type, in
if (i == '-') {
lnum -= n;
} else {
+ if (n >= INT32_MAX - lnum) {
+ emsg(_(e_line_number_out_of_range));
+ goto error;
+ }
lnum += n;
}
}
@@ -4163,12 +4574,10 @@ error:
return lnum;
}
-/*
- * Get flags from an Ex command argument.
- */
+/// Get flags from an Ex command argument.
static void get_flags(exarg_T *eap)
{
- while (vim_strchr((char_u *)"lp#", *eap->arg) != NULL) {
+ while (vim_strchr("lp#", *eap->arg) != NULL) {
if (*eap->arg == 'l') {
eap->flags |= EXFLAG_LIST;
} else if (*eap->arg == 'p') {
@@ -4200,11 +4609,10 @@ static void ex_script_ni(exarg_T *eap)
}
}
-/*
- * Check range in Ex command for validity.
- * Return NULL when valid, error message when invalid.
- */
-static char *invalid_range(exarg_T *eap)
+/// Check range in Ex command for validity.
+///
+/// @return NULL when valid, error message when invalid.
+char *invalid_range(exarg_T *eap)
{
buf_T *buf;
if (eap->line1 < 0 || eap->line2 < 0 || eap->line1 > eap->line2) {
@@ -4226,8 +4634,9 @@ static char *invalid_range(exarg_T *eap)
}
break;
case ADDR_BUFFERS:
- if (eap->line1 < firstbuf->b_fnum
- || eap->line2 > lastbuf->b_fnum) {
+ // Only a boundary check, not whether the buffers actually
+ // exist.
+ if (eap->line1 < 1 || eap->line2 > get_highest_fnum()) {
return _(e_invrange);
}
break;
@@ -4289,9 +4698,7 @@ static char *invalid_range(exarg_T *eap)
return NULL;
}
-/*
- * Correct the range for zero line number, if required.
- */
+/// Correct the range for zero line number, if required.
static void correct_range(exarg_T *eap)
{
if (!(eap->argt & EX_ZEROR)) { // zero in range not allowed
@@ -4304,14 +4711,11 @@ static void correct_range(exarg_T *eap)
}
}
-
-/*
- * For a ":vimgrep" or ":vimgrepadd" command return a pointer past the
- * pattern. Otherwise return eap->arg.
- */
-static char_u *skip_grep_pat(exarg_T *eap)
+/// For a ":vimgrep" or ":vimgrepadd" command return a pointer past the
+/// pattern. Otherwise return eap->arg.
+static char *skip_grep_pat(exarg_T *eap)
{
- char_u *p = eap->arg;
+ char *p = eap->arg;
if (*p != NUL && (eap->cmdidx == CMD_vimgrep || eap->cmdidx == CMD_lvimgrep
|| eap->cmdidx == CMD_vimgrepadd
@@ -4325,18 +4729,16 @@ static char_u *skip_grep_pat(exarg_T *eap)
return p;
}
-/*
- * For the ":make" and ":grep" commands insert the 'makeprg'/'grepprg' option
- * in the command line, so that things like % get expanded.
- */
-static char_u *replace_makeprg(exarg_T *eap, char_u *p, char_u **cmdlinep)
+/// For the ":make" and ":grep" commands insert the 'makeprg'/'grepprg' option
+/// in the command line, so that things like % get expanded.
+char *replace_makeprg(exarg_T *eap, char *p, char **cmdlinep)
{
- char_u *new_cmdline;
- char_u *program;
- char_u *pos;
- char_u *ptr;
+ char *new_cmdline;
+ char *program;
+ char *pos;
+ char *ptr;
int len;
- int i;
+ size_t i;
/*
* Don't do it when ":vimgrep" is used for ":grep".
@@ -4349,31 +4751,31 @@ static char_u *replace_makeprg(exarg_T *eap, char_u *p, char_u **cmdlinep)
if (eap->cmdidx == CMD_grep || eap->cmdidx == CMD_lgrep
|| eap->cmdidx == CMD_grepadd || eap->cmdidx == CMD_lgrepadd) {
if (*curbuf->b_p_gp == NUL) {
- program = p_gp;
+ program = (char *)p_gp;
} else {
- program = curbuf->b_p_gp;
+ program = (char *)curbuf->b_p_gp;
}
} else {
if (*curbuf->b_p_mp == NUL) {
- program = p_mp;
+ program = (char *)p_mp;
} else {
- program = curbuf->b_p_mp;
+ program = (char *)curbuf->b_p_mp;
}
}
p = skipwhite(p);
- if ((pos = (char_u *)strstr((char *)program, "$*")) != NULL) {
+ if ((pos = strstr(program, "$*")) != NULL) {
// replace $* by given arguments
i = 1;
- while ((pos = (char_u *)strstr((char *)pos + 2, "$*")) != NULL) {
- ++i;
+ while ((pos = strstr(pos + 2, "$*")) != NULL) {
+ i++;
}
len = (int)STRLEN(p);
- new_cmdline = xmalloc(STRLEN(program) + (size_t)i * (len - 2) + 1);
+ new_cmdline = xmalloc(STRLEN(program) + i * (size_t)(len - 2) + 1);
ptr = new_cmdline;
- while ((pos = (char_u *)strstr((char *)program, "$*")) != NULL) {
- i = (int)(pos - program);
+ while ((pos = strstr(program, "$*")) != NULL) {
+ i = (size_t)(pos - program);
memcpy(ptr, program, i);
STRCPY(ptr += i, p);
ptr += len;
@@ -4386,7 +4788,7 @@ static char_u *replace_makeprg(exarg_T *eap, char_u *p, char_u **cmdlinep)
STRCAT(new_cmdline, " ");
STRCAT(new_cmdline, p);
}
- msg_make(p);
+ msg_make((char_u *)p);
// 'eap->cmd' is not set here, because it is not used at CMD_make
xfree(*cmdlinep);
@@ -4396,15 +4798,16 @@ static char_u *replace_makeprg(exarg_T *eap, char_u *p, char_u **cmdlinep)
return p;
}
-// Expand file name in Ex command argument.
-// When an error is detected, "errormsgp" is set to a non-NULL pointer.
-// Return FAIL for failure, OK otherwise.
+/// Expand file name in Ex command argument.
+/// When an error is detected, "errormsgp" is set to a non-NULL pointer.
+///
+/// @return FAIL for failure, OK otherwise.
int expand_filename(exarg_T *eap, char_u **cmdlinep, char **errormsgp)
{
int has_wildcards; // need to expand wildcards
- char_u *repl;
+ char *repl;
size_t srclen;
- char_u *p;
+ char *p;
int escaped;
// Skip a regexp pattern for ":vimgrep[add] pat file..."
@@ -4415,7 +4818,7 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char **errormsgp)
* the file name contains a wildcard it should not cause expanding.
* (it will be expanded anyway if there is a wildcard before replacing).
*/
- has_wildcards = path_has_wildcard(p);
+ has_wildcards = path_has_wildcard((char_u *)p);
while (*p != NUL) {
// Skip over `=expr`, wildcards in it are not expanded.
if (p[0] == '`' && p[1] == '=') {
@@ -4430,16 +4833,16 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char **errormsgp)
* Quick check if this cannot be the start of a special string.
* Also removes backslash before '%', '#' and '<'.
*/
- if (vim_strchr((char_u *)"%#<", *p) == NULL) {
- ++p;
+ if (vim_strchr("%#<", *p) == NULL) {
+ p++;
continue;
}
/*
* Try to find a match at this position.
*/
- repl = eval_vars(p, eap->arg, &srclen, &(eap->do_ecmd_lnum),
- errormsgp, &escaped);
+ repl = (char *)eval_vars((char_u *)p, (char_u *)eap->arg, &srclen, &(eap->do_ecmd_lnum),
+ errormsgp, &escaped);
if (*errormsgp != NULL) { // error detected
return FAIL;
}
@@ -4451,7 +4854,7 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char **errormsgp)
// Wildcards won't be expanded below, the replacement is taken
// literally. But do expand "~/file", "~user/file" and "$HOME/file".
if (vim_strchr(repl, '$') != NULL || vim_strchr(repl, '~') != NULL) {
- char_u *l = repl;
+ char *l = repl;
repl = expand_env_save(repl);
xfree(l);
@@ -4473,19 +4876,19 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char **errormsgp)
&& eap->cmdidx != CMD_make
&& eap->cmdidx != CMD_terminal
&& !(eap->argt & EX_NOSPC)) {
- char_u *l;
+ char *l;
#ifdef BACKSLASH_IN_FILENAME
// Don't escape a backslash here, because rem_backslash() doesn't
// remove it later.
- static char_u *nobslash = (char_u *)" \t\"|";
+ static char *nobslash = " \t\"|";
# define ESCAPE_CHARS nobslash
#else
# define ESCAPE_CHARS escape_chars
#endif
- for (l = repl; *l; ++l) {
- if (vim_strchr(ESCAPE_CHARS, *l) != NULL) {
- l = vim_strsave_escaped(repl, ESCAPE_CHARS);
+ for (l = repl; *l; l++) {
+ if (vim_strchr((char *)ESCAPE_CHARS, *l) != NULL) {
+ l = (char *)vim_strsave_escaped((char_u *)repl, ESCAPE_CHARS);
xfree(repl);
repl = l;
break;
@@ -4497,15 +4900,15 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char **errormsgp)
if ((eap->usefilter
|| eap->cmdidx == CMD_bang
|| eap->cmdidx == CMD_terminal)
- && vim_strpbrk(repl, (char_u *)"!") != NULL) {
- char_u *l;
+ && strpbrk(repl, "!") != NULL) {
+ char *l;
- l = vim_strsave_escaped(repl, (char_u *)"!");
+ l = (char *)vim_strsave_escaped((char_u *)repl, (char_u *)"!");
xfree(repl);
repl = l;
}
- p = repl_cmdline(eap, p, srclen, repl, cmdlinep);
+ p = repl_cmdline(eap, p, srclen, repl, (char **)cmdlinep);
xfree(repl);
}
@@ -4525,14 +4928,14 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char **errormsgp)
*/
if (vim_strchr(eap->arg, '$') != NULL
|| vim_strchr(eap->arg, '~') != NULL) {
- expand_env_esc(eap->arg, NameBuff, MAXPATHL, true, true, NULL);
+ expand_env_esc((char_u *)eap->arg, NameBuff, MAXPATHL, true, true, NULL);
has_wildcards = path_has_wildcard(NameBuff);
- p = NameBuff;
+ p = (char *)NameBuff;
} else {
p = NULL;
}
if (p != NULL) {
- (void)repl_cmdline(eap, eap->arg, STRLEN(eap->arg), p, cmdlinep);
+ (void)repl_cmdline(eap, eap->arg, STRLEN(eap->arg), p, (char **)cmdlinep);
}
}
@@ -4544,7 +4947,7 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char **errormsgp)
#ifdef UNIX
if (!has_wildcards)
#endif
- backslash_halve(eap->arg);
+ backslash_halve((char_u *)eap->arg);
if (has_wildcards) {
expand_T xpc;
@@ -4555,26 +4958,24 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char **errormsgp)
if (p_wic) {
options += WILD_ICASE;
}
- p = ExpandOne(&xpc, eap->arg, NULL, options, WILD_EXPAND_FREE);
+ p = (char *)ExpandOne(&xpc, (char_u *)eap->arg, NULL, options, WILD_EXPAND_FREE);
if (p == NULL) {
return FAIL;
}
- (void)repl_cmdline(eap, eap->arg, STRLEN(eap->arg), p, cmdlinep);
+ (void)repl_cmdline(eap, eap->arg, STRLEN(eap->arg), p, (char **)cmdlinep);
xfree(p);
}
}
return OK;
}
-/*
- * Replace part of the command line, keeping eap->cmd, eap->arg and
- * eap->nextcmd correct.
- * "src" points to the part that is to be replaced, of length "srclen".
- * "repl" is the replacement string.
- * Returns a pointer to the character after the replaced string.
- */
-static char_u *repl_cmdline(exarg_T *eap, char_u *src, size_t srclen, char_u *repl,
- char_u **cmdlinep)
+/// Replace part of the command line, keeping eap->cmd, eap->arg, eap->args and
+/// eap->nextcmd correct.
+/// "src" points to the part that is to be replaced, of length "srclen".
+/// "repl" is the replacement string.
+///
+/// @return a pointer to the character after the replaced string.
+static char *repl_cmdline(exarg_T *eap, char *src, size_t srclen, char *repl, char **cmdlinep)
{
/*
* The new command line is build in new_cmdline[].
@@ -4586,7 +4987,8 @@ static char_u *repl_cmdline(exarg_T *eap, char_u *src, size_t srclen, char_u *re
if (eap->nextcmd != NULL) {
i += STRLEN(eap->nextcmd); // add space for next command
}
- char_u *new_cmdline = xmalloc(i);
+ char *new_cmdline = xmalloc(i);
+ size_t offset = (size_t)(src - *cmdlinep);
/*
* Copy the stuff before the expanded part.
@@ -4594,7 +4996,7 @@ static char_u *repl_cmdline(exarg_T *eap, char_u *src, size_t srclen, char_u *re
* Copy what came after the expanded part.
* Copy the next commands, if there are any.
*/
- i = (size_t)(src - *cmdlinep); // length of part before match
+ i = offset; // length of part before match
memmove(new_cmdline, *cmdlinep, i);
memmove(new_cmdline + i, repl, len);
@@ -4609,6 +5011,19 @@ static char_u *repl_cmdline(exarg_T *eap, char_u *src, size_t srclen, char_u *re
}
eap->cmd = new_cmdline + (eap->cmd - *cmdlinep);
eap->arg = new_cmdline + (eap->arg - *cmdlinep);
+
+ for (size_t j = 0; j < eap->argc; j++) {
+ if (offset >= (size_t)(eap->args[j] - *cmdlinep)) {
+ // If replaced text is after or in the same position as the argument,
+ // the argument's position relative to the beginning of the cmdline stays the same.
+ eap->args[j] = new_cmdline + (eap->args[j] - *cmdlinep);
+ } else {
+ // Otherwise, argument gets shifted alongside the replaced text.
+ // The amount of the shift is equal to the difference of the old and new string length.
+ eap->args[j] = new_cmdline + (eap->args[j] - *cmdlinep) + (len - srclen);
+ }
+ }
+
if (eap->do_ecmd_cmd != NULL && eap->do_ecmd_cmd != dollar_command) {
eap->do_ecmd_cmd = new_cmdline + (eap->do_ecmd_cmd - *cmdlinep);
}
@@ -4618,14 +5033,10 @@ static char_u *repl_cmdline(exarg_T *eap, char_u *src, size_t srclen, char_u *re
return src;
}
-/*
- * Check for '|' to separate commands and '"' to start comments.
- */
+/// Check for '|' to separate commands and '"' to start comments.
void separate_nextcmd(exarg_T *eap)
{
- char_u *p;
-
- p = skip_grep_pat(eap);
+ char *p = skip_grep_pat(eap);
for (; *p; MB_PTR_ADV(p)) {
if (*p == Ctrl_V) {
@@ -4653,9 +5064,7 @@ void separate_nextcmd(exarg_T *eap)
&& !(eap->argt & EX_NOTRLCOM)
&& (eap->cmdidx != CMD_at || p != eap->arg)
&& (eap->cmdidx != CMD_redir
- || p != eap->arg + 1 || p[-1] != '@'))
- || *p == '|'
- || *p == '\n') {
+ || p != eap->arg + 1 || p[-1] != '@')) || *p == '|' || *p == '\n') {
// We remove the '\' before the '|', unless EX_CTRLV is used
// AND 'b' is present in 'cpoptions'.
if ((vim_strchr(p_cpo, CPO_BAR) == NULL
@@ -4663,7 +5072,7 @@ void separate_nextcmd(exarg_T *eap)
STRMOVE(p - 1, p); // remove the '\'
p--;
} else {
- eap->nextcmd = check_nextcmd(p);
+ eap->nextcmd = (char *)check_nextcmd((char_u *)p);
*p = NUL;
break;
}
@@ -4671,22 +5080,20 @@ void separate_nextcmd(exarg_T *eap)
}
if (!(eap->argt & EX_NOTRLCOM)) { // remove trailing spaces
- del_trailing_spaces(eap->arg);
+ del_trailing_spaces((char_u *)eap->arg);
}
}
-/*
- * get + command from ex argument
- */
-static char_u *getargcmd(char_u **argp)
+/// get + command from ex argument
+static char *getargcmd(char **argp)
{
- char_u *arg = *argp;
- char_u *command = NULL;
+ char *arg = *argp;
+ char *command = NULL;
if (*arg == '+') { // +[command]
++arg;
if (ascii_isspace(*arg) || *arg == '\0') {
- command = dollar_command;
+ command = (char *)dollar_command;
} else {
command = arg;
arg = skip_cmd_arg(command, TRUE);
@@ -4704,7 +5111,7 @@ static char_u *getargcmd(char_u **argp)
/// Find end of "+command" argument. Skip over "\ " and "\\".
///
/// @param rembs TRUE to halve the number of backslashes
-static char_u *skip_cmd_arg(char_u *p, int rembs)
+static char *skip_cmd_arg(char *p, int rembs)
{
while (*p && !ascii_isspace(*p)) {
if (*p == '\\' && p[1] != NUL) {
@@ -4734,16 +5141,15 @@ int get_bad_opt(const char_u *p, exarg_T *eap)
return OK;
}
-/*
- * Get "++opt=arg" argument.
- * Return FAIL or OK.
- */
+/// Get "++opt=arg" argument.
+///
+/// @return FAIL or OK.
static int getargopt(exarg_T *eap)
{
- char_u *arg = eap->arg + 2;
+ char *arg = eap->arg + 2;
int *pp = NULL;
int bad_char_idx;
- char_u *p;
+ char *p;
// ":edit ++[no]bin[ary] file"
if (STRNCMP(arg, "bin", 3) == 0 || STRNCMP(arg, "nobin", 5) == 0) {
@@ -4762,7 +5168,7 @@ static int getargopt(exarg_T *eap)
// ":read ++edit file"
if (STRNCMP(arg, "edit", 4) == 0) {
- eap->read_edit = TRUE;
+ eap->read_edit = true;
eap->arg = skipwhite(arg + 4);
return OK;
}
@@ -4789,26 +5195,26 @@ static int getargopt(exarg_T *eap)
return FAIL;
}
- ++arg;
+ arg++;
*pp = (int)(arg - eap->cmd);
- arg = skip_cmd_arg(arg, FALSE);
+ arg = skip_cmd_arg(arg, false);
eap->arg = skipwhite(arg);
*arg = NUL;
if (pp == &eap->force_ff) {
- if (check_ff_value(eap->cmd + eap->force_ff) == FAIL) {
+ if (check_ff_value((char_u *)eap->cmd + eap->force_ff) == FAIL) {
return FAIL;
}
- eap->force_ff = eap->cmd[eap->force_ff];
+ eap->force_ff = (char_u)eap->cmd[eap->force_ff];
} else if (pp == &eap->force_enc) {
// Make 'fileencoding' lower case.
- for (p = eap->cmd + eap->force_enc; *p != NUL; ++p) {
- *p = TOLOWER_ASC(*p);
+ for (p = eap->cmd + eap->force_enc; *p != NUL; p++) {
+ *p = (char)TOLOWER_ASC(*p);
}
} else {
// Check ++bad= argument. Must be a single-byte character, "keep" or
// "drop".
- if (get_bad_opt(eap->cmd + bad_char_idx, eap) == FAIL) {
+ if (get_bad_opt((char_u *)eap->cmd + bad_char_idx, eap) == FAIL) {
return FAIL;
}
}
@@ -4817,16 +5223,17 @@ static int getargopt(exarg_T *eap)
}
/// Handle the argument for a tabpage related ex command.
-/// Returns a tabpage number.
/// When an error is encountered then eap->errmsg is set.
+///
+/// @return a tabpage number.
static int get_tabpage_arg(exarg_T *eap)
{
int tab_number = 0;
int unaccept_arg0 = (eap->cmdidx == CMD_tabmove) ? 0 : 1;
if (eap->arg && *eap->arg != NUL) {
- char_u *p = eap->arg;
- char_u *p_save;
+ char *p = eap->arg;
+ char *p_save;
int relative = 0; // argument +N/-N means: go to N places to the
// right/left relative to the current position.
@@ -4839,13 +5246,19 @@ static int get_tabpage_arg(exarg_T *eap)
}
p_save = p;
- tab_number = getdigits(&p, false, tab_number);
+ tab_number = (int)getdigits((char_u **)&p, false, tab_number);
if (relative == 0) {
if (STRCMP(p, "$") == 0) {
tab_number = LAST_TAB_NR;
} else if (STRCMP(p, "#") == 0) {
- tab_number = tabpage_index(lastused_tabpage);
+ if (valid_tabpage(lastused_tabpage)) {
+ tab_number = tabpage_index(lastused_tabpage);
+ } else {
+ eap->errmsg = ex_errmsg(e_invargval, eap->arg);
+ tab_number = 0;
+ goto theend;
+ }
} else if (p == p_save || *p_save == '-' || *p != NUL
|| tab_number > LAST_TAB_NR) {
// No numbers as argument.
@@ -4873,8 +5286,10 @@ static int get_tabpage_arg(exarg_T *eap)
eap->errmsg = e_invrange;
tab_number = 0;
} else {
- tab_number = eap->line2;
- if (!unaccept_arg0 && *skipwhite(*eap->cmdlinep) == '-') {
+ tab_number = (int)eap->line2;
+ char *cmdp = eap->cmd;
+ while (--cmdp > *eap->cmdlinep && (*cmdp == ' ' || ascii_isdigit(*cmdp))) {}
+ if (!unaccept_arg0 && *cmdp == '-') {
tab_number--;
if (tab_number < unaccept_arg0) {
eap->errmsg = e_invarg;
@@ -4901,55 +5316,6 @@ theend:
return tab_number;
}
-/*
- * ":abbreviate" and friends.
- */
-static void ex_abbreviate(exarg_T *eap)
-{
- do_exmap(eap, TRUE); // almost the same as mapping
-}
-
-/*
- * ":map" and friends.
- */
-static void ex_map(exarg_T *eap)
-{
- /*
- * If we are sourcing .exrc or .vimrc in current directory we
- * print the mappings for security reasons.
- */
- if (secure) {
- secure = 2;
- msg_outtrans(eap->cmd);
- msg_putchar('\n');
- }
- do_exmap(eap, FALSE);
-}
-
-/*
- * ":unmap" and friends.
- */
-static void ex_unmap(exarg_T *eap)
-{
- do_exmap(eap, FALSE);
-}
-
-/*
- * ":mapclear" and friends.
- */
-static void ex_mapclear(exarg_T *eap)
-{
- map_clear_mode(eap->cmd, eap->arg, eap->forceit, false);
-}
-
-/*
- * ":abclear" and friends.
- */
-static void ex_abclear(exarg_T *eap)
-{
- map_clear_mode(eap->cmd, eap->arg, true, true);
-}
-
static void ex_autocmd(exarg_T *eap)
{
// Disallow autocommands from .exrc and .vimrc in current
@@ -4964,12 +5330,10 @@ static void ex_autocmd(exarg_T *eap)
}
}
-/*
- * ":doautocmd": Apply the automatic commands to the current buffer.
- */
+/// ":doautocmd": Apply the automatic commands to the current buffer.
static void ex_doautocmd(exarg_T *eap)
{
- char_u *arg = eap->arg;
+ char *arg = eap->arg;
int call_do_modelines = check_nomodeline(&arg);
bool did_aucmd;
@@ -4980,24 +5344,22 @@ static void ex_doautocmd(exarg_T *eap)
}
}
-/*
- * :[N]bunload[!] [N] [bufname] unload buffer
- * :[N]bdelete[!] [N] [bufname] delete buffer from buffer list
- * :[N]bwipeout[!] [N] [bufname] delete buffer really
- */
+/// :[N]bunload[!] [N] [bufname] unload buffer
+/// :[N]bdelete[!] [N] [bufname] delete buffer from buffer list
+/// :[N]bwipeout[!] [N] [bufname] delete buffer really
static void ex_bunload(exarg_T *eap)
{
- eap->errmsg = do_bufdel(eap->cmdidx == CMD_bdelete ? DOBUF_DEL
- : eap->cmdidx == CMD_bwipeout ? DOBUF_WIPE
- : DOBUF_UNLOAD,
- eap->arg,
- eap->addr_count, (int)eap->line1, (int)eap->line2, eap->forceit);
+ eap->errmsg = do_bufdel(eap->cmdidx == CMD_bdelete
+ ? DOBUF_DEL
+ : eap->cmdidx == CMD_bwipeout
+ ? DOBUF_WIPE
+ : DOBUF_UNLOAD,
+ eap->arg, eap->addr_count, (int)eap->line1, (int)eap->line2,
+ eap->forceit);
}
-/*
- * :[N]buffer [N] to buffer N
- * :[N]sbuffer [N] to buffer N
- */
+/// :[N]buffer [N] to buffer N
+/// :[N]sbuffer [N] to buffer N
static void ex_buffer(exarg_T *eap)
{
if (*eap->arg) {
@@ -5009,72 +5371,62 @@ static void ex_buffer(exarg_T *eap)
goto_buffer(eap, DOBUF_FIRST, FORWARD, (int)eap->line2);
}
if (eap->do_ecmd_cmd != NULL) {
- do_cmdline_cmd((char *)eap->do_ecmd_cmd);
+ do_cmdline_cmd(eap->do_ecmd_cmd);
}
}
}
-/*
- * :[N]bmodified [N] to next mod. buffer
- * :[N]sbmodified [N] to next mod. buffer
- */
+/// :[N]bmodified [N] to next mod. buffer
+/// :[N]sbmodified [N] to next mod. buffer
static void ex_bmodified(exarg_T *eap)
{
goto_buffer(eap, DOBUF_MOD, FORWARD, (int)eap->line2);
if (eap->do_ecmd_cmd != NULL) {
- do_cmdline_cmd((char *)eap->do_ecmd_cmd);
+ do_cmdline_cmd(eap->do_ecmd_cmd);
}
}
-/*
- * :[N]bnext [N] to next buffer
- * :[N]sbnext [N] split and to next buffer
- */
+/// :[N]bnext [N] to next buffer
+/// :[N]sbnext [N] split and to next buffer
static void ex_bnext(exarg_T *eap)
{
goto_buffer(eap, DOBUF_CURRENT, FORWARD, (int)eap->line2);
if (eap->do_ecmd_cmd != NULL) {
- do_cmdline_cmd((char *)eap->do_ecmd_cmd);
+ do_cmdline_cmd(eap->do_ecmd_cmd);
}
}
-/*
- * :[N]bNext [N] to previous buffer
- * :[N]bprevious [N] to previous buffer
- * :[N]sbNext [N] split and to previous buffer
- * :[N]sbprevious [N] split and to previous buffer
- */
+/// :[N]bNext [N] to previous buffer
+/// :[N]bprevious [N] to previous buffer
+/// :[N]sbNext [N] split and to previous buffer
+/// :[N]sbprevious [N] split and to previous buffer
static void ex_bprevious(exarg_T *eap)
{
goto_buffer(eap, DOBUF_CURRENT, BACKWARD, (int)eap->line2);
if (eap->do_ecmd_cmd != NULL) {
- do_cmdline_cmd((char *)eap->do_ecmd_cmd);
+ do_cmdline_cmd(eap->do_ecmd_cmd);
}
}
-/*
- * :brewind to first buffer
- * :bfirst to first buffer
- * :sbrewind split and to first buffer
- * :sbfirst split and to first buffer
- */
+/// :brewind to first buffer
+/// :bfirst to first buffer
+/// :sbrewind split and to first buffer
+/// :sbfirst split and to first buffer
static void ex_brewind(exarg_T *eap)
{
goto_buffer(eap, DOBUF_FIRST, FORWARD, 0);
if (eap->do_ecmd_cmd != NULL) {
- do_cmdline_cmd((char *)eap->do_ecmd_cmd);
+ do_cmdline_cmd(eap->do_ecmd_cmd);
}
}
-/*
- * :blast to last buffer
- * :sblast split and to last buffer
- */
+/// :blast to last buffer
+/// :sblast split and to last buffer
static void ex_blast(exarg_T *eap)
{
goto_buffer(eap, DOBUF_LAST, BACKWARD, 0);
if (eap->do_ecmd_cmd != NULL) {
- do_cmdline_cmd((char *)eap->do_ecmd_cmd);
+ do_cmdline_cmd(eap->do_ecmd_cmd);
}
}
@@ -5083,10 +5435,8 @@ int ends_excmd(int c) FUNC_ATTR_CONST
return c == NUL || c == '|' || c == '"' || c == '\n';
}
-/*
- * Return the next command, after the first '|' or '\n'.
- * Return NULL if not found.
- */
+/// @return the next command, after the first '|' or '\n' or,
+/// NULL if not found.
char_u *find_nextcmd(const char_u *p)
{
while (*p != '|' && *p != '\n') {
@@ -5099,13 +5449,14 @@ char_u *find_nextcmd(const char_u *p)
}
/// Check if *p is a separator between Ex commands, skipping over white space.
-/// Return NULL if it isn't, the following character if it is.
+///
+/// @return NULL if it isn't, the following character if it is.
char_u *check_nextcmd(char_u *p)
{
- char_u *s = skipwhite(p);
+ char *s = skipwhite((char *)p);
if (*s == '|' || *s == '\n') {
- return (s + 1);
+ return (char_u *)(s + 1);
} else {
return NULL;
}
@@ -5115,9 +5466,10 @@ char_u *check_nextcmd(char_u *p)
/// - and this is the last window
/// - and forceit not used
/// - and not repeated twice on a row
-/// @return FAIL and give error message if 'message' TRUE, return OK otherwise
///
/// @param message when FALSE check only, no messages
+///
+/// @return FAIL and give error message if 'message' TRUE, return OK otherwise
static int check_more(int message, bool forceit)
{
int n = ARGCOUNT - curwin->w_arg_idx - 1;
@@ -5125,13 +5477,13 @@ static int check_more(int message, bool forceit)
if (!forceit && only_one_window()
&& ARGCOUNT > 1 && !arg_had_last && n > 0 && quitmore == 0) {
if (message) {
- if ((p_confirm || cmdmod.confirm) && curbuf->b_fname != NULL) {
- char_u buff[DIALOG_MSG_SIZE];
+ if ((p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) && curbuf->b_fname != NULL) {
+ char buff[DIALOG_MSG_SIZE];
vim_snprintf((char *)buff, DIALOG_MSG_SIZE,
NGETTEXT("%d more file to edit. Quit anyway?",
"%d more files to edit. Quit anyway?", (unsigned long)n), n);
- if (vim_dialog_yesno(VIM_QUESTION, NULL, buff, 1) == VIM_YES) {
+ if (vim_dialog_yesno(VIM_QUESTION, NULL, (char_u *)buff, 1) == VIM_YES) {
return OK;
}
return FAIL;
@@ -5145,33 +5497,53 @@ static int check_more(int message, bool forceit)
return OK;
}
-/*
- * Function given to ExpandGeneric() to obtain the list of command names.
- */
-char_u *get_command_name(expand_T *xp, int idx)
+/// Function given to ExpandGeneric() to obtain the list of command names.
+char *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;
}
-int uc_add_command(char_u *name, size_t name_len, char_u *rep, uint32_t argt, long def, int flags,
- int compl, char_u *compl_arg, LuaRef compl_luaref, cmd_addr_T addr_type,
- LuaRef luaref, bool force)
+/// Check for a valid user command name
+///
+/// If the given {name} is valid, then a pointer to the end of the valid name is returned.
+/// Otherwise, returns NULL.
+char *uc_validate_name(char *name)
+{
+ if (ASCII_ISALPHA(*name)) {
+ while (ASCII_ISALNUM(*name)) {
+ name++;
+ }
+ }
+ if (!ends_excmd(*name) && !ascii_iswhite(*name)) {
+ return NULL;
+ }
+
+ return name;
+}
+
+/// Create a new user command {name}, if one doesn't already exist.
+///
+/// This function takes ownership of compl_arg, compl_luaref, and luaref.
+///
+/// @return OK if the command is created, FAIL otherwise.
+int uc_add_command(char *name, size_t name_len, char *rep, uint32_t argt, long def, int flags,
+ int compl, char *compl_arg, LuaRef compl_luaref, LuaRef preview_luaref,
+ cmd_addr_T addr_type, LuaRef luaref, bool force)
FUNC_ATTR_NONNULL_ARG(1, 3)
{
ucmd_T *cmd = NULL;
int i;
int cmp = 1;
- char_u *rep_buf = NULL;
+ char *rep_buf = NULL;
garray_T *gap;
- replace_termcodes(rep, STRLEN(rep), &rep_buf, false, false, true,
- CPO_TO_CPO_FLAGS);
+ replace_termcodes(rep, STRLEN(rep), &rep_buf, 0, NULL, CPO_TO_CPO_FLAGS);
if (rep_buf == NULL) {
// Can't replace termcodes - try using the string as is
- rep_buf = vim_strsave(rep);
+ rep_buf = xstrdup(rep);
}
// get address of growarray: global or in curbuf
@@ -5214,6 +5586,7 @@ int uc_add_command(char_u *name, size_t name_len, char_u *rep, uint32_t argt, lo
XFREE_CLEAR(cmd->uc_compl_arg);
NLUA_CLEAR_REF(cmd->uc_luaref);
NLUA_CLEAR_REF(cmd->uc_compl_luaref);
+ NLUA_CLEAR_REF(cmd->uc_preview_luaref);
break;
}
@@ -5227,24 +5600,26 @@ int uc_add_command(char_u *name, size_t name_len, char_u *rep, uint32_t argt, lo
if (cmp != 0) {
ga_grow(gap, 1);
- char_u *const p = vim_strnsave(name, name_len);
+ char *const p = xstrnsave(name, name_len);
cmd = USER_CMD_GA(gap, i);
- memmove(cmd + 1, cmd, (gap->ga_len - i) * sizeof(ucmd_T));
+ memmove(cmd + 1, cmd, (size_t)(gap->ga_len - i) * sizeof(ucmd_T));
++gap->ga_len;
- cmd->uc_name = p;
+ cmd->uc_name = (char_u *)p;
}
- cmd->uc_rep = rep_buf;
+ cmd->uc_rep = (char_u *)rep_buf;
cmd->uc_argt = argt;
cmd->uc_def = def;
cmd->uc_compl = compl;
cmd->uc_script_ctx = current_sctx;
cmd->uc_script_ctx.sc_lnum += sourcing_lnum;
- cmd->uc_compl_arg = compl_arg;
+ nlua_set_sctx(&cmd->uc_script_ctx);
+ cmd->uc_compl_arg = (char_u *)compl_arg;
cmd->uc_compl_luaref = compl_luaref;
+ cmd->uc_preview_luaref = preview_luaref;
cmd->uc_addr_type = addr_type;
cmd->uc_luaref = luaref;
@@ -5255,10 +5630,10 @@ fail:
xfree(compl_arg);
NLUA_CLEAR_REF(luaref);
NLUA_CLEAR_REF(compl_luaref);
+ NLUA_CLEAR_REF(preview_luaref);
return FAIL;
}
-
static struct {
cmd_addr_T expand;
char *name;
@@ -5335,7 +5710,7 @@ static char *get_command_complete(int arg)
}
}
-static void uc_list(char_u *name, size_t name_len)
+static void uc_list(char *name, size_t name_len)
{
int i, j;
bool found = false;
@@ -5343,9 +5718,7 @@ static void uc_list(char_u *name, size_t name_len)
uint32_t a;
// In cmdwin, the alternative buffer should be used.
- garray_T *gap = (cmdwin_type != 0 && get_cmdline_type() == NUL)
- ? &prevwin->w_buffer->b_ucmds
- : &curbuf->b_ucmds;
+ const garray_T *gap = &prevwin_curwin()->w_buffer->b_ucmds;
for (;;) {
for (i = 0; i < gap->ga_len; i++) {
cmd = USER_CMD_GA(gap, i);
@@ -5476,7 +5849,7 @@ static void uc_list(char_u *name, size_t name_len)
} while (len < 25 - over);
IObuff[len] = '\0';
- msg_outtrans(IObuff);
+ msg_outtrans((char *)IObuff);
msg_outtrans_special(cmd->uc_rep, false,
name_len == 0 ? Columns - 47 : 0);
@@ -5499,11 +5872,11 @@ static void uc_list(char_u *name, size_t name_len)
}
}
-static int uc_scan_attr(char_u *attr, size_t len, uint32_t *argt, long *def, int *flags,
- int *complp, char_u **compl_arg, cmd_addr_T *addr_type_arg)
+static int uc_scan_attr(char *attr, size_t len, uint32_t *argt, long *def, int *flags, int *complp,
+ char_u **compl_arg, cmd_addr_T *addr_type_arg)
FUNC_ATTR_NONNULL_ALL
{
- char_u *p;
+ char *p;
if (len == 0) {
emsg(_("E175: No attribute specified"));
@@ -5517,11 +5890,13 @@ 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 {
int i;
- char_u *val = NULL;
+ char *val = NULL;
size_t vallen = 0;
size_t attrlen = len;
@@ -5529,8 +5904,8 @@ static int uc_scan_attr(char_u *attr, size_t len, uint32_t *argt, long *def, int
for (i = 0; i < (int)len; i++) {
if (attr[i] == '=') {
val = &attr[i + 1];
- vallen = len - i - 1;
- attrlen = i;
+ vallen = len - (size_t)i - 1;
+ attrlen = (size_t)i;
break;
}
}
@@ -5567,7 +5942,7 @@ two_count:
return FAIL;
}
- *def = getdigits_long(&p, true, 0);
+ *def = getdigits_long((char_u **)&p, true, 0);
*argt |= EX_ZEROR;
if (p != val + vallen || vallen == 0) {
@@ -5593,7 +5968,7 @@ invalid_count:
goto two_count;
}
- *def = getdigits_long(&p, true, 0);
+ *def = getdigits_long((char_u **)&p, true, 0);
if (p != val + vallen) {
goto invalid_count;
@@ -5609,7 +5984,7 @@ invalid_count:
return FAIL;
}
- if (parse_compl_arg(val, (int)vallen, complp, argt, compl_arg)
+ if (parse_compl_arg(val, (int)vallen, complp, argt, (char **)compl_arg)
== FAIL) {
return FAIL;
}
@@ -5626,7 +6001,7 @@ invalid_count:
*argt |= EX_ZEROR;
}
} else {
- char_u ch = attr[len];
+ char ch = attr[len];
attr[len] = '\0';
semsg(_("E181: Invalid attribute: %s"), attr);
attr[len] = ch;
@@ -5639,30 +6014,28 @@ invalid_count:
static char e_complete_used_without_nargs[] = N_("E1208: -complete used without -nargs");
-/*
- * ":command ..."
- */
+/// ":command ..."
static void ex_command(exarg_T *eap)
{
- char_u *name;
- char_u *end;
- char_u *p;
+ char *name;
+ char *end;
+ char *p;
uint32_t argt = 0;
long def = -1;
int flags = 0;
int compl = EXPAND_NOTHING;
- char_u *compl_arg = NULL;
+ char *compl_arg = NULL;
cmd_addr_T addr_type_arg = ADDR_NONE;
int has_attr = (eap->arg[0] == '-');
- int name_len;
+ size_t name_len;
p = eap->arg;
// Check for attributes
while (*p == '-') {
- ++p;
- end = skiptowhite(p);
- if (uc_scan_attr(p, end - p, &argt, &def, &flags, &compl, &compl_arg,
+ p++;
+ end = (char *)skiptowhite((char_u *)p);
+ if (uc_scan_attr(p, (size_t)(end - p), &argt, &def, &flags, &compl, (char_u **)&compl_arg,
&addr_type_arg) == FAIL) {
return;
}
@@ -5671,23 +6044,18 @@ static void ex_command(exarg_T *eap)
// Get the name (if any) and skip to the following argument.
name = p;
- if (ASCII_ISALPHA(*p)) {
- while (ASCII_ISALNUM(*p)) {
- p++;
- }
- }
- if (!ends_excmd(*p) && !ascii_iswhite(*p)) {
+ end = uc_validate_name(name);
+ if (!end) {
emsg(_("E182: Invalid command name"));
return;
}
- end = p;
- name_len = (int)(end - name);
+ name_len = (size_t)(end - name);
// If there is nothing after the name, and no attributes were specified,
// we are listing commands
p = skipwhite(end);
if (!has_attr && ends_excmd(*p)) {
- uc_list(name, end - name);
+ uc_list(name, name_len);
} else if (!ASCII_ISUPPER(*name)) {
emsg(_("E183: User defined commands must start with an uppercase letter"));
} else if (name_len <= 4 && STRNCMP(name, "Next", name_len) == 0) {
@@ -5695,15 +6063,13 @@ static void ex_command(exarg_T *eap)
} else if (compl > 0 && (argt & EX_EXTRA) == 0) {
emsg(_(e_complete_used_without_nargs));
} else {
- uc_add_command(name, end - name, p, argt, def, flags, compl, compl_arg, LUA_NOREF,
+ uc_add_command(name, name_len, p, argt, def, flags, compl, compl_arg, LUA_NOREF, LUA_NOREF,
addr_type_arg, LUA_NOREF, eap->forceit);
}
}
-/*
- * ":comclear"
- * Clear all user commands, global and for current buffer.
- */
+/// ":comclear"
+/// Clear all user commands, global and for current buffer.
void ex_comclear(exarg_T *eap)
{
uc_clear(&ucmds);
@@ -5717,11 +6083,10 @@ void free_ucmd(ucmd_T *cmd)
xfree(cmd->uc_compl_arg);
NLUA_CLEAR_REF(cmd->uc_compl_luaref);
NLUA_CLEAR_REF(cmd->uc_luaref);
+ NLUA_CLEAR_REF(cmd->uc_preview_luaref);
}
-/*
- * Clear all user commands for "gap".
- */
+/// Clear all user commands for "gap".
void uc_clear(garray_T *gap)
{
GA_DEEP_CLEAR(gap, ucmd_T, free_ucmd);
@@ -5731,26 +6096,36 @@ static void ex_delcommand(exarg_T *eap)
{
int i = 0;
ucmd_T *cmd = NULL;
- int cmp = -1;
+ int res = -1;
garray_T *gap;
+ const char *arg = eap->arg;
+ bool buffer_only = false;
+
+ if (STRNCMP(arg, "-buffer", 7) == 0 && ascii_iswhite(arg[7])) {
+ buffer_only = true;
+ arg = skipwhite(arg + 7);
+ }
gap = &curbuf->b_ucmds;
for (;;) {
for (i = 0; i < gap->ga_len; i++) {
cmd = USER_CMD_GA(gap, i);
- cmp = STRCMP(eap->arg, cmd->uc_name);
- if (cmp <= 0) {
+ res = STRCMP(arg, cmd->uc_name);
+ if (res <= 0) {
break;
}
}
- if (gap == &ucmds || cmp == 0) {
+ if (gap == &ucmds || res == 0 || buffer_only) {
break;
}
gap = &ucmds;
}
- if (cmp != 0) {
- semsg(_("E184: No such user-defined command: %s"), eap->arg);
+ if (res != 0) {
+ semsg(_(buffer_only
+ ? e_no_such_user_defined_command_in_current_buffer_str
+ : e_no_such_user_defined_command_str),
+ arg);
return;
}
@@ -5759,80 +6134,167 @@ static void ex_delcommand(exarg_T *eap)
--gap->ga_len;
if (i < gap->ga_len) {
- memmove(cmd, cmd + 1, (gap->ga_len - i) * sizeof(ucmd_T));
+ memmove(cmd, cmd + 1, (size_t)(gap->ga_len - i) * sizeof(ucmd_T));
}
}
-/*
- * split and quote args for <f-args>
- */
-static char_u *uc_split_args(char_u *arg, size_t *lenp)
+/// Split a string by unescaped whitespace (space & tab), used for f-args on Lua commands callback.
+/// Similar to uc_split_args(), but does not allocate, add quotes, add commas and is an iterator.
+///
+/// @param[in] arg String to split
+/// @param[in] arglen Length of {arg}
+/// @param[inout] end Index of last character of previous iteration
+/// @param[out] buf Buffer to copy string into
+/// @param[out] len Length of string in {buf}
+///
+/// @return true if iteration is complete, else false
+bool uc_split_args_iter(const char *arg, size_t arglen, size_t *end, char *buf, size_t *len)
+{
+ if (!arglen) {
+ return true;
+ }
+
+ size_t pos = *end;
+ while (pos < arglen && ascii_iswhite(arg[pos])) {
+ pos++;
+ }
+
+ size_t l = 0;
+ for (; pos < arglen - 1; pos++) {
+ if (arg[pos] == '\\' && (arg[pos + 1] == '\\' || ascii_iswhite(arg[pos + 1]))) {
+ buf[l++] = arg[++pos];
+ } else {
+ buf[l++] = arg[pos];
+ if (ascii_iswhite(arg[pos + 1])) {
+ *end = pos + 1;
+ *len = l;
+ return false;
+ }
+ }
+ }
+
+ if (pos < arglen && !ascii_iswhite(arg[pos])) {
+ buf[l++] = arg[pos];
+ }
+
+ *len = l;
+ return true;
+}
+
+/// split and quote args for <f-args>
+static char *uc_split_args(char *arg, char **args, size_t *arglens, size_t argc, size_t *lenp)
{
- char_u *buf;
- char_u *p;
- char_u *q;
+ char *buf;
+ char *p;
+ char *q;
int len;
// Precalculate length
- p = arg;
len = 2; // Initial and final quotes
+ if (args == NULL) {
+ p = arg;
- while (*p) {
- if (p[0] == '\\' && p[1] == '\\') {
- len += 2;
- p += 2;
- } else if (p[0] == '\\' && ascii_iswhite(p[1])) {
- len += 1;
- p += 2;
- } else if (*p == '\\' || *p == '"') {
- len += 2;
- p += 1;
- } else if (ascii_iswhite(*p)) {
- p = skipwhite(p);
- if (*p == NUL) {
- break;
+ while (*p) {
+ if (p[0] == '\\' && p[1] == '\\') {
+ len += 2;
+ p += 2;
+ } else if (p[0] == '\\' && ascii_iswhite(p[1])) {
+ len += 1;
+ p += 2;
+ } else if (*p == '\\' || *p == '"') {
+ len += 2;
+ p += 1;
+ } else if (ascii_iswhite(*p)) {
+ p = skipwhite(p);
+ if (*p == NUL) {
+ break;
+ }
+ len += 3; // ","
+ } else {
+ const int charlen = utfc_ptr2len(p);
+
+ len += charlen;
+ p += charlen;
}
- len += 3; // ","
- } else {
- const int charlen = utfc_ptr2len(p);
+ }
+ } else {
+ for (size_t i = 0; i < argc; i++) {
+ p = args[i];
+ const char *arg_end = args[i] + arglens[i];
+
+ while (p < arg_end) {
+ if (*p == '\\' || *p == '"') {
+ len += 2;
+ p += 1;
+ } else {
+ const int charlen = utfc_ptr2len(p);
- len += charlen;
- p += charlen;
+ len += charlen;
+ p += charlen;
+ }
+ }
+
+ if (i != argc - 1) {
+ len += 3; // ","
+ }
}
}
- buf = xmalloc(len + 1);
+ buf = xmalloc((size_t)len + 1);
- p = arg;
q = buf;
*q++ = '"';
- while (*p) {
- if (p[0] == '\\' && p[1] == '\\') {
- *q++ = '\\';
- *q++ = '\\';
- p += 2;
- } else if (p[0] == '\\' && ascii_iswhite(p[1])) {
- *q++ = p[1];
- p += 2;
- } else if (*p == '\\' || *p == '"') {
- *q++ = '\\';
- *q++ = *p++;
- } else if (ascii_iswhite(*p)) {
- p = skipwhite(p);
- if (*p == NUL) {
- break;
+
+ if (args == NULL) {
+ p = arg;
+ while (*p) {
+ if (p[0] == '\\' && p[1] == '\\') {
+ *q++ = '\\';
+ *q++ = '\\';
+ p += 2;
+ } else if (p[0] == '\\' && ascii_iswhite(p[1])) {
+ *q++ = p[1];
+ p += 2;
+ } else if (*p == '\\' || *p == '"') {
+ *q++ = '\\';
+ *q++ = *p++;
+ } else if (ascii_iswhite(*p)) {
+ p = skipwhite(p);
+ if (*p == NUL) {
+ break;
+ }
+ *q++ = '"';
+ *q++ = ',';
+ *q++ = '"';
+ } else {
+ mb_copy_char((const char_u **)&p, (char_u **)&q);
+ }
+ }
+ } else {
+ for (size_t i = 0; i < argc; i++) {
+ p = args[i];
+ const char *arg_end = args[i] + arglens[i];
+
+ while (p < arg_end) {
+ if (*p == '\\' || *p == '"') {
+ *q++ = '\\';
+ *q++ = *p++;
+ } else {
+ mb_copy_char((const char_u **)&p, (char_u **)&q);
+ }
+ }
+ if (i != argc - 1) {
+ *q++ = '"';
+ *q++ = ',';
+ *q++ = '"';
}
- *q++ = '"';
- *q++ = ',';
- *q++ = '"';
- } else {
- mb_copy_char((const char_u **)&p, &q);
}
}
+
*q++ = '"';
*q = 0;
- *lenp = len;
+ *lenp = (size_t)len;
return buf;
}
@@ -5865,11 +6327,11 @@ static size_t add_cmd_modifier(char *buf, char *mod_str, bool *multi_mods)
///
/// @return the length of the replacement, which has been added to "buf".
/// Return -1 if there was no match, and only the "<" has been copied.
-static size_t uc_check_code(char_u *code, size_t len, char_u *buf, ucmd_T *cmd, exarg_T *eap,
- char_u **split_buf, size_t *split_len)
+static size_t uc_check_code(char *code, size_t len, char *buf, ucmd_T *cmd, exarg_T *eap,
+ char **split_buf, size_t *split_len)
{
size_t result = 0;
- char_u *p = code + 1;
+ char *p = code + 1;
size_t l = len - 2;
int quote = 0;
enum {
@@ -5885,7 +6347,7 @@ static size_t uc_check_code(char_u *code, size_t len, char_u *buf, ucmd_T *cmd,
ct_NONE,
} type = ct_NONE;
- if ((vim_strchr((char_u *)"qQfF", *p) != NULL) && p[1] == '-') {
+ if ((vim_strchr("qQfF", *p) != NULL) && p[1] == '-') {
quote = (*p == 'q' || *p == 'Q') ? 1 : 2;
p += 2;
l -= 2;
@@ -5965,7 +6427,7 @@ static size_t uc_check_code(char_u *code, size_t len, char_u *buf, ucmd_T *cmd,
case 2: // Quote and split (<f-args>)
// This is hard, so only do it once, and cache the result
if (*split_buf == NULL) {
- *split_buf = uc_split_args(eap->arg, split_len);
+ *split_buf = uc_split_args(eap->arg, eap->args, eap->arglens, eap->argc, split_len);
}
result = *split_len;
@@ -6029,20 +6491,7 @@ static size_t uc_check_code(char_u *code, size_t len, char_u *buf, ucmd_T *cmd,
}
case ct_MODS:
- result = quote ? 2 : 0;
- if (buf != NULL) {
- if (quote) {
- *buf++ = '"';
- }
- *buf = '\0';
- }
-
- result += uc_mods((char *)buf);
-
- if (quote && buf != NULL) {
- buf += result - 2;
- *buf = '"';
- }
+ result = uc_mods(buf, &cmdmod, quote);
break;
case ct_REGISTER:
@@ -6055,7 +6504,7 @@ static size_t uc_check_code(char_u *code, size_t len, char_u *buf, ucmd_T *cmd,
*buf++ = '\'';
}
if (eap->regname) {
- *buf++ = eap->regname;
+ *buf++ = (char)eap->regname;
}
if (quote) {
*buf = '\'';
@@ -6082,91 +6531,124 @@ static size_t uc_check_code(char_u *code, size_t len, char_u *buf, ucmd_T *cmd,
return result;
}
-size_t uc_mods(char *buf)
+/// Add modifiers from "cmod->cmod_split" to "buf". Set "multi_mods" when one
+/// was added.
+///
+/// @return the number of bytes added
+size_t add_win_cmd_modifers(char *buf, const cmdmod_T *cmod, bool *multi_mods)
{
size_t result = 0;
- bool multi_mods = false;
// :aboveleft and :leftabove
- if (cmdmod.split & WSP_ABOVE) {
- result += add_cmd_modifier(buf, "aboveleft", &multi_mods);
+ if (cmod->cmod_split & WSP_ABOVE) {
+ result += add_cmd_modifier(buf, "aboveleft", multi_mods);
}
// :belowright and :rightbelow
- if (cmdmod.split & WSP_BELOW) {
- result += add_cmd_modifier(buf, "belowright", &multi_mods);
+ if (cmod->cmod_split & WSP_BELOW) {
+ result += add_cmd_modifier(buf, "belowright", multi_mods);
}
// :botright
- if (cmdmod.split & WSP_BOT) {
- result += add_cmd_modifier(buf, "botright", &multi_mods);
+ if (cmod->cmod_split & WSP_BOT) {
+ result += add_cmd_modifier(buf, "botright", multi_mods);
+ }
+
+ // :tab
+ if (cmod->cmod_tab > 0) {
+ result += add_cmd_modifier(buf, "tab", multi_mods);
}
+ // :topleft
+ if (cmod->cmod_split & WSP_TOP) {
+ result += add_cmd_modifier(buf, "topleft", multi_mods);
+ }
+ // :vertical
+ if (cmod->cmod_split & WSP_VERT) {
+ result += add_cmd_modifier(buf, "vertical", multi_mods);
+ }
+ return result;
+}
+
+/// Generate text for the "cmod" command modifiers.
+/// If "buf" is NULL just return the length.
+size_t uc_mods(char *buf, const cmdmod_T *cmod, bool quote)
+{
+ size_t result = 0;
+ bool multi_mods = false;
typedef struct {
- bool *set;
+ int flag;
char *name;
} mod_entry_T;
static mod_entry_T mod_entries[] = {
- { &cmdmod.browse, "browse" },
- { &cmdmod.confirm, "confirm" },
- { &cmdmod.hide, "hide" },
- { &cmdmod.keepalt, "keepalt" },
- { &cmdmod.keepjumps, "keepjumps" },
- { &cmdmod.keepmarks, "keepmarks" },
- { &cmdmod.keeppatterns, "keeppatterns" },
- { &cmdmod.lockmarks, "lockmarks" },
- { &cmdmod.noswapfile, "noswapfile" }
+ { CMOD_BROWSE, "browse" },
+ { CMOD_CONFIRM, "confirm" },
+ { CMOD_HIDE, "hide" },
+ { CMOD_KEEPALT, "keepalt" },
+ { CMOD_KEEPJUMPS, "keepjumps" },
+ { CMOD_KEEPMARKS, "keepmarks" },
+ { CMOD_KEEPPATTERNS, "keeppatterns" },
+ { CMOD_LOCKMARKS, "lockmarks" },
+ { CMOD_NOSWAPFILE, "noswapfile" },
+ { CMOD_UNSILENT, "unsilent" },
+ { CMOD_NOAUTOCMD, "noautocmd" },
+ { CMOD_SANDBOX, "sandbox" },
};
+
+ result = quote ? 2 : 0;
+ if (buf != NULL) {
+ if (quote) {
+ *buf++ = '"';
+ }
+ *buf = '\0';
+ }
+
// the modifiers that are simple flags
for (size_t i = 0; i < ARRAY_SIZE(mod_entries); i++) {
- if (*mod_entries[i].set) {
+ if (cmod->cmod_flags & mod_entries[i].flag) {
result += add_cmd_modifier(buf, mod_entries[i].name, &multi_mods);
}
}
- // TODO(vim): How to support :noautocmd?
- // TODO(vim): How to support :sandbox?
-
// :silent
- if (msg_silent > 0) {
- result += add_cmd_modifier(buf, emsg_silent > 0 ? "silent!" : "silent", &multi_mods);
- }
- // :tab
- if (cmdmod.tab > 0) {
- result += add_cmd_modifier(buf, "tab", &multi_mods);
- }
- // :topleft
- if (cmdmod.split & WSP_TOP) {
- result += add_cmd_modifier(buf, "topleft", &multi_mods);
+ if (cmod->cmod_flags & CMOD_SILENT) {
+ result += add_cmd_modifier(buf,
+ (cmod->cmod_flags & CMOD_ERRSILENT) ? "silent!" : "silent",
+ &multi_mods);
}
-
- // TODO(vim): How to support :unsilent?
-
// :verbose
- if (p_verbose > 0) {
- result += add_cmd_modifier(buf, "verbose", &multi_mods);
- }
- // :vertical
- if (cmdmod.split & WSP_VERT) {
- result += add_cmd_modifier(buf, "vertical", &multi_mods);
+ if (cmod->cmod_verbose > 0) {
+ int verbose_value = cmod->cmod_verbose - 1;
+ if (verbose_value == 1) {
+ result += add_cmd_modifier(buf, "verbose", &multi_mods);
+ } else {
+ char verbose_buf[NUMBUFLEN];
+ snprintf(verbose_buf, NUMBUFLEN, "%dverbose", verbose_value);
+ result += add_cmd_modifier(buf, verbose_buf, &multi_mods);
+ }
}
+ // flags from cmod->cmod_split
+ result += add_win_cmd_modifers(buf, cmod, &multi_mods);
+ if (quote && buf != NULL) {
+ buf += result - 2;
+ *buf = '"';
+ }
return result;
}
-static void do_ucmd(exarg_T *eap)
+static int do_ucmd(exarg_T *eap, bool preview)
{
- char_u *buf;
- char_u *p;
- char_u *q;
+ char *buf;
+ char *p;
+ char *q;
- char_u *start;
- char_u *end = NULL;
- char_u *ksp;
+ char *start;
+ char *end = NULL;
+ char *ksp;
size_t len, totlen;
size_t split_len = 0;
- char_u *split_buf = NULL;
+ char *split_buf = NULL;
ucmd_T *cmd;
- const sctx_T save_current_sctx = current_sctx;
if (eap->cmdidx == CMD_USER) {
cmd = USER_CMD(eap->useridx);
@@ -6174,9 +6656,14 @@ static void do_ucmd(exarg_T *eap)
cmd = USER_CMD_GA(&curbuf->b_ucmds, eap->useridx);
}
+ if (preview) {
+ assert(cmd->uc_preview_luaref > 0);
+ return nlua_do_ucmd(cmd, eap, true);
+ }
+
if (cmd->uc_luaref > 0) {
- nlua_do_ucmd(cmd, eap);
- return;
+ nlua_do_ucmd(cmd, eap, false);
+ return 0;
}
/*
@@ -6186,7 +6673,7 @@ static void do_ucmd(exarg_T *eap)
*/
buf = NULL;
for (;;) {
- p = cmd->uc_rep; // source
+ p = (char *)cmd->uc_rep; // source
q = buf; // destination
totlen = 0;
@@ -6196,21 +6683,19 @@ static void do_ucmd(exarg_T *eap)
end = vim_strchr(start + 1, '>');
}
if (buf != NULL) {
- for (ksp = p; *ksp != NUL && *ksp != K_SPECIAL; ksp++) {
- }
- if (*ksp == K_SPECIAL
+ for (ksp = p; *ksp != NUL && (char_u)(*ksp) != K_SPECIAL; ksp++) {}
+ if ((char_u)(*ksp) == K_SPECIAL
&& (start == NULL || ksp < start || end == NULL)
- && (ksp[1] == KS_SPECIAL && ksp[2] == KE_FILLER)) {
+ && ((char_u)ksp[1] == KS_SPECIAL && ksp[2] == KE_FILLER)) {
// 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;
+ len = (size_t)(ksp - p);
if (len > 0) {
memmove(q, p, len);
q += len;
}
- *q++ = K_SPECIAL;
+ *q++ = (char)K_SPECIAL;
p = ksp + 3;
continue;
}
@@ -6225,7 +6710,7 @@ static void do_ucmd(exarg_T *eap)
++end;
// Take everything up to the '<'
- len = start - p;
+ len = (size_t)(start - p);
if (buf == NULL) {
totlen += len;
} else {
@@ -6233,8 +6718,7 @@ static void do_ucmd(exarg_T *eap)
q += len;
}
- len = uc_check_code(start, end - start, q, cmd, eap,
- &split_buf, &split_len);
+ len = uc_check_code(start, (size_t)(end - start), q, cmd, eap, &split_buf, &split_len);
if (len == (size_t)-1) {
// no match, continue after '<'
p = start + 1;
@@ -6257,96 +6741,117 @@ static void do_ucmd(exarg_T *eap)
buf = xmalloc(totlen + 1);
}
- current_sctx.sc_sid = cmd->uc_script_ctx.sc_sid;
+ sctx_T save_current_sctx;
+ bool restore_current_sctx = false;
+ if ((cmd->uc_argt & EX_KEEPSCRIPT) == 0) {
+ restore_current_sctx = true;
+ save_current_sctx = current_sctx;
+ 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;
+
+ // Careful: Do not use "cmd" here, it may have become invalid if a user
+ // command was added.
+ if (restore_current_sctx) {
+ current_sctx = save_current_sctx;
+ }
xfree(buf);
xfree(split_buf);
+
+ return 0;
}
-static char_u *get_user_command_name(int idx)
+static char *expand_user_command_name(int idx)
{
return get_user_commands(NULL, idx - CMD_SIZE);
}
-/*
- * Function given to ExpandGeneric() to obtain the list of user address type names.
- */
-char_u *get_user_cmd_addr_type(expand_T *xp, int idx)
+
+/// Function given to ExpandGeneric() to obtain the list of user address type names.
+char *get_user_cmd_addr_type(expand_T *xp, int idx)
{
- return (char_u *)addr_type_complete[idx].name;
+ return addr_type_complete[idx].name;
}
-/*
- * Function given to ExpandGeneric() to obtain the list of user command names.
- */
-char_u *get_user_commands(expand_T *xp FUNC_ATTR_UNUSED, int idx)
+/// Function given to ExpandGeneric() to obtain the list of user command names.
+char *get_user_commands(expand_T *xp FUNC_ATTR_UNUSED, int idx)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
// In cmdwin, the alternative buffer should be used.
- const buf_T *const buf = (cmdwin_type != 0 && get_cmdline_type() == NUL)
- ? prevwin->w_buffer
- : curbuf;
+ const buf_T *const buf = prevwin_curwin()->w_buffer;
if (idx < buf->b_ucmds.ga_len) {
- return USER_CMD_GA(&buf->b_ucmds, idx)->uc_name;
+ return (char *)USER_CMD_GA(&buf->b_ucmds, idx)->uc_name;
}
idx -= buf->b_ucmds.ga_len;
if (idx < ucmds.ga_len) {
- return USER_CMD(idx)->uc_name;
+ return (char *)USER_CMD(idx)->uc_name;
}
return NULL;
}
-/*
- * Function given to ExpandGeneric() to obtain the list of user command
- * attributes.
- */
-char_u *get_user_cmd_flags(expand_T *xp, int idx)
+/// Get the name of user command "idx". "cmdidx" can be CMD_USER or
+/// CMD_USER_BUF.
+///
+/// @return NULL if the command is not found.
+static char *get_user_command_name(int idx, int cmdidx)
+{
+ if (cmdidx == CMD_USER && idx < ucmds.ga_len) {
+ return (char *)USER_CMD(idx)->uc_name;
+ }
+ if (cmdidx == CMD_USER_BUF) {
+ // In cmdwin, the alternative buffer should be used.
+ const buf_T *const buf = prevwin_curwin()->w_buffer;
+
+ if (idx < buf->b_ucmds.ga_len) {
+ return (char *)USER_CMD_GA(&buf->b_ucmds, idx)->uc_name;
+ }
+ }
+ return NULL;
+}
+
+/// Function given to ExpandGeneric() to obtain the list of user command
+/// attributes.
+char *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;
}
- return (char_u *)user_cmd_flags[idx];
+ return user_cmd_flags[idx];
}
-/*
- * Function given to ExpandGeneric() to obtain the list of values for -nargs.
- */
-char_u *get_user_cmd_nargs(expand_T *xp, int idx)
+/// Function given to ExpandGeneric() to obtain the list of values for -nargs.
+char *get_user_cmd_nargs(expand_T *xp, int idx)
{
static char *user_cmd_nargs[] = { "0", "1", "*", "?", "+" };
if (idx >= (int)ARRAY_SIZE(user_cmd_nargs)) {
return NULL;
}
- return (char_u *)user_cmd_nargs[idx];
+ return user_cmd_nargs[idx];
}
-/*
- * Function given to ExpandGeneric() to obtain the list of values for -complete.
- */
-char_u *get_user_cmd_complete(expand_T *xp, int idx)
+/// Function given to ExpandGeneric() to obtain the list of values for -complete.
+char *get_user_cmd_complete(expand_T *xp, int idx)
{
if (idx >= (int)ARRAY_SIZE(command_complete)) {
return NULL;
}
char *cmd_compl = get_command_complete(idx);
if (cmd_compl == NULL) {
- return (char_u *)"";
+ return "";
} else {
- return (char_u *)cmd_compl;
+ return cmd_compl;
}
}
-/*
- * Parse address type argument
- */
-int parse_addr_type_arg(char_u *value, int vallen, cmd_addr_T *addr_type_arg)
+/// Parse address type argument
+int parse_addr_type_arg(char *value, int vallen, cmd_addr_T *addr_type_arg)
FUNC_ATTR_NONNULL_ALL
{
int i, a, b;
@@ -6361,7 +6866,7 @@ int parse_addr_type_arg(char_u *value, int vallen, cmd_addr_T *addr_type_arg)
}
if (addr_type_complete[i].expand == ADDR_NONE) {
- char_u *err = value;
+ char *err = value;
for (i = 0; err[i] != NUL && !ascii_iswhite(err[i]); i++) {}
err[i] = NUL;
@@ -6372,18 +6877,16 @@ int parse_addr_type_arg(char_u *value, int vallen, cmd_addr_T *addr_type_arg)
return OK;
}
-/*
- * Parse a completion argument "value[vallen]".
- * The detected completion goes in "*complp", argument type in "*argt".
- * When there is an argument, for function and user defined completion, it's
- * copied to allocated memory and stored in "*compl_arg".
- * Returns FAIL if something is wrong.
- */
-int parse_compl_arg(const char_u *value, int vallen, int *complp, uint32_t *argt,
- char_u **compl_arg)
+/// Parse a completion argument "value[vallen]".
+/// The detected completion goes in "*complp", argument type in "*argt".
+/// When there is an argument, for function and user defined completion, it's
+/// copied to allocated memory and stored in "*compl_arg".
+///
+/// @return FAIL if something is wrong.
+int parse_compl_arg(const char *value, int vallen, int *complp, uint32_t *argt, char **compl_arg)
FUNC_ATTR_NONNULL_ALL
{
- const char_u *arg = NULL;
+ const char *arg = NULL;
size_t arglen = 0;
int i;
int valend = vallen;
@@ -6391,8 +6894,8 @@ int parse_compl_arg(const char_u *value, int vallen, int *complp, uint32_t *argt
// Look for any argument part - which is the part after any ','
for (i = 0; i < vallen; ++i) {
if (value[i] == ',') {
- arg = &value[i + 1];
- arglen = vallen - i - 1;
+ arg = (char *)&value[i + 1];
+ arglen = (size_t)(vallen - i - 1);
valend = i;
break;
}
@@ -6432,7 +6935,7 @@ int parse_compl_arg(const char_u *value, int vallen, int *complp, uint32_t *argt
}
if (arg != NULL) {
- *compl_arg = vim_strnsave(arg, arglen);
+ *compl_arg = xstrnsave(arg, arglen);
}
return OK;
}
@@ -6455,8 +6958,8 @@ int cmdcomplete_str_to_type(const char *complete_str)
static void ex_colorscheme(exarg_T *eap)
{
if (*eap->arg == NUL) {
- char_u *expr = vim_strsave((char_u *)"g:colors_name");
- char_u *p = NULL;
+ char *expr = xstrdup("g:colors_name");
+ char *p = NULL;
emsg_off++;
p = eval_to_string(expr, NULL, false);
@@ -6464,12 +6967,12 @@ static void ex_colorscheme(exarg_T *eap)
xfree(expr);
if (p != NULL) {
- msg((char *)p);
+ msg(p);
xfree(p);
} else {
msg("default");
}
- } else if (load_colors(eap->arg) == FAIL) {
+ } else if (load_colors((char_u *)eap->arg) == FAIL) {
semsg(_("E185: Cannot find color scheme '%s'"), eap->arg);
}
}
@@ -6482,11 +6985,8 @@ static void ex_highlight(exarg_T *eap)
do_highlight((const char *)eap->arg, eap->forceit, false);
}
-
-/*
- * Call this function if we thought we were going to exit, but we won't
- * (because of an error). May need to restore the terminal mode.
- */
+/// Call this function if we thought we were going to exit, but we won't
+/// (because of an error). May need to restore the terminal mode.
void not_exiting(void)
{
exiting = false;
@@ -6521,8 +7021,8 @@ bool before_quit_autocmds(win_T *wp, bool quit_all, bool forceit)
return false;
}
-// ":quit": quit current window, quit Vim if the last window is closed.
-// ":{nr}quit": quit window {nr}
+/// ":quit": quit current window, quit Vim if the last window is closed.
+/// ":{nr}quit": quit window {nr}
static void ex_quit(exarg_T *eap)
{
if (cmdwin_type != 0) {
@@ -6538,7 +7038,7 @@ static void ex_quit(exarg_T *eap)
win_T *wp;
if (eap->addr_count > 0) {
- int wnr = eap->line2;
+ linenr_T wnr = eap->line2;
for (wp = firstwin; wp->w_next != NULL; wp = wp->w_next) {
if (--wnr <= 0) {
@@ -6582,12 +7082,13 @@ static void ex_quit(exarg_T *eap)
}
not_exiting();
// close window; may free buffer
- win_close(wp, !buf_hide(wp->w_buffer) || eap->forceit);
+ win_close(wp, !buf_hide(wp->w_buffer) || eap->forceit, eap->forceit);
}
}
/// ":cquit".
static void ex_cquit(exarg_T *eap)
+ FUNC_ATTR_NORETURN
{
// this does not always pass on the exit code to the Manx compiler. why?
getout(eap->addr_count > 0 ? (int)eap->line2 : EXIT_FAILURE);
@@ -6622,9 +7123,7 @@ static void ex_quit_all(exarg_T *eap)
not_exiting();
}
-/*
- * ":close": close current window, unless it is the last one
- */
+/// ":close": close current window, unless it is the last one
static void ex_close(exarg_T *eap)
{
win_T *win = NULL;
@@ -6650,9 +7149,7 @@ static void ex_close(exarg_T *eap)
}
}
-/*
- * ":pclose": Close any preview window.
- */
+/// ":pclose": Close any preview window.
static void ex_pclose(exarg_T *eap)
{
FOR_ALL_WINDOWS_IN_TAB(win, curtab) {
@@ -6680,7 +7177,7 @@ void ex_win_close(int forceit, win_T *win, tabpage_T *tp)
need_hide = (bufIsChanged(buf) && buf->b_nwindows <= 1);
if (need_hide && !buf_hide(buf) && !forceit) {
- if ((p_confirm || cmdmod.confirm) && p_write) {
+ if ((p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) && p_write) {
bufref_T bufref;
set_bufref(&bufref, buf);
dialog_changed(buf, false);
@@ -6694,19 +7191,16 @@ void ex_win_close(int forceit, win_T *win, tabpage_T *tp)
}
}
-
// free buffer when not hiding it or when it's a scratch buffer
if (tp == NULL) {
- win_close(win, !need_hide && !buf_hide(buf));
+ win_close(win, !need_hide && !buf_hide(buf), forceit);
} else {
win_close_othertab(win, !need_hide && !buf_hide(buf), tp);
}
}
-/*
- * ":tabclose": close current tab page, unless it is the last one.
- * ":tabclose N": close tab page N.
- */
+/// ":tabclose": close current tab page, unless it is the last one.
+/// ":tabclose N": close tab page N.
static void ex_tabclose(exarg_T *eap)
{
tabpage_T *tp;
@@ -6767,9 +7261,7 @@ static void ex_tabonly(exarg_T *eap)
}
}
-/*
- * Close the current tab page.
- */
+/// Close the current tab page.
void tabpage_close(int forceit)
{
// First close all the windows but the current one. If that worked then
@@ -6785,18 +7277,16 @@ void tabpage_close(int forceit)
}
}
-/*
- * Close tab page "tp", which is not the current tab page.
- * Note that autocommands may make "tp" invalid.
- * Also takes care of the tab pages line disappearing when closing the
- * last-but-one tab page.
- */
+/// Close tab page "tp", which is not the current tab page.
+/// Note that autocommands may make "tp" invalid.
+/// Also takes care of the tab pages line disappearing when closing the
+/// last-but-one tab page.
void tabpage_close_other(tabpage_T *tp, int forceit)
{
int done = 0;
win_T *wp;
int h = tabline_height();
- char_u prev_idx[NUMBUFLEN];
+ char prev_idx[NUMBUFLEN];
// Limit to 1000 windows, autocommands may add a window while we close
// one. OK, so I'm paranoid...
@@ -6807,24 +7297,22 @@ void tabpage_close_other(tabpage_T *tp, int forceit)
// Autocommands may delete the tab page under our fingers and we may
// fail to close a window with a modified buffer.
- if (!valid_tabpage(tp) || tp->tp_firstwin == wp) {
+ if (!valid_tabpage(tp) || tp->tp_lastwin == wp) {
break;
}
}
redraw_tabline = true;
if (h != tabline_height()) {
- shell_new_rows();
+ win_new_screen_rows();
}
}
-/*
- * ":only".
- */
+/// ":only".
static void ex_only(exarg_T *eap)
{
win_T *wp;
- int wnr;
+ linenr_T wnr;
if (eap->addr_count > 0) {
wnr = eap->line2;
@@ -6844,10 +7332,8 @@ static void ex_only(exarg_T *eap)
close_others(TRUE, eap->forceit);
}
-/*
- * ":all" and ":sall".
- * Also used for ":tab drop file ..." after setting the argument list.
- */
+/// ":all" and ":sall".
+/// Also used for ":tab drop file ..." after setting the argument list.
void ex_all(exarg_T *eap)
{
if (eap->addr_count == 0) {
@@ -6861,7 +7347,7 @@ static void ex_hide(exarg_T *eap)
// ":hide" or ":hide | cmd": hide current window
if (!eap->skip) {
if (eap->addr_count == 0) {
- win_close(curwin, false); // don't free buffer
+ win_close(curwin, false, eap->forceit); // don't free buffer
} else {
int winnr = 0;
win_T *win = NULL;
@@ -6876,7 +7362,7 @@ static void ex_hide(exarg_T *eap)
if (win == NULL) {
win = lastwin;
}
- win_close(win, false);
+ win_close(win, false, eap->forceit);
}
}
}
@@ -6902,7 +7388,7 @@ static void ex_stop(exarg_T *eap)
apply_autocmds(EVENT_VIMRESUME, NULL, NULL, false, NULL);
}
-// ":exit", ":xit" and ":wq": Write file and quit the current window.
+/// ":exit", ":xit" and ":wq": Write file and quit the current window.
static void ex_exit(exarg_T *eap)
{
if (cmdwin_type != 0) {
@@ -6934,13 +7420,11 @@ static void ex_exit(exarg_T *eap)
}
not_exiting();
// Quit current window, may free the buffer.
- win_close(curwin, !buf_hide(curwin->w_buffer));
+ win_close(curwin, !buf_hide(curwin->w_buffer), eap->forceit);
}
}
-/*
- * ":print", ":list", ":number".
- */
+/// ":print", ":list", ":number".
static void ex_print(exarg_T *eap)
{
if (curbuf->b_ml.ml_flags & ML_EMPTY) {
@@ -6970,29 +7454,22 @@ static void ex_goto(exarg_T *eap)
goto_byte(eap->line2);
}
-/*
- * Clear an argument list: free all file names and reset it to zero entries.
- */
+/// Clear an argument list: free all file names and reset it to zero entries.
void alist_clear(alist_T *al)
{
-#define FREE_AENTRY_FNAME(arg) xfree(arg->ae_fname)
+#define FREE_AENTRY_FNAME(arg) xfree((arg)->ae_fname)
GA_DEEP_CLEAR(&al->al_ga, aentry_T, FREE_AENTRY_FNAME);
}
-/*
- * Init an argument list.
- */
+/// Init an argument list.
void alist_init(alist_T *al)
{
ga_init(&al->al_ga, (int)sizeof(aentry_T), 5);
}
-
-/*
- * Remove a reference from an argument list.
- * Ignored when the argument list is the global one.
- * If the argument list is no longer used by any window, free it.
- */
+/// Remove a reference from an argument list.
+/// Ignored when the argument list is the global one.
+/// If the argument list is no longer used by any window, free it.
void alist_unlink(alist_T *al)
{
if (al != &global_alist && --al->al_refcount <= 0) {
@@ -7001,9 +7478,7 @@ void alist_unlink(alist_T *al)
}
}
-/*
- * Create a new argument list and use it for the current window.
- */
+/// Create a new argument list and use it for the current window.
void alist_new(void)
{
curwin->w_alist = xmalloc(sizeof(*curwin->w_alist));
@@ -7013,18 +7488,17 @@ void alist_new(void)
}
#if !defined(UNIX)
-/*
- * Expand the file names in the global argument list.
- * If "fnum_list" is not NULL, use "fnum_list[fnum_len]" as a list of buffer
- * numbers to be re-used.
- */
+
+/// Expand the file names in the global argument list.
+/// If "fnum_list" is not NULL, use "fnum_list[fnum_len]" as a list of buffer
+/// numbers to be re-used.
void alist_expand(int *fnum_list, int fnum_len)
{
- char_u **old_arg_files;
+ char **old_arg_files;
int old_arg_count;
- char_u **new_arg_files;
+ char **new_arg_files;
int new_arg_file_count;
- char_u *save_p_su = p_su;
+ char *save_p_su = p_su;
int i;
/* Don't use 'suffixes' here. This should work like the shell did the
@@ -7048,11 +7522,9 @@ void alist_expand(int *fnum_list, int fnum_len)
}
#endif
-/*
- * Set the argument list for the current window.
- * Takes over the allocated files[] and the allocated fnames in it.
- */
-void alist_set(alist_T *al, int count, char_u **files, int use_curbuf, int *fnum_list, int fnum_len)
+/// Set the argument list for the current window.
+/// Takes over the allocated files[] and the allocated fnames in it.
+void alist_set(alist_T *al, int count, char **files, int use_curbuf, int *fnum_list, int fnum_len)
{
int i;
static int recursive = 0;
@@ -7098,7 +7570,7 @@ void alist_set(alist_T *al, int count, char_u **files, int use_curbuf, int *fnum
/// "fname" must have been allocated and "al" must have been checked for room.
///
/// @param set_fnum 1: set buffer number; 2: re-use curbuf
-void alist_add(alist_T *al, char_u *fname, int set_fnum)
+void alist_add(alist_T *al, char *fname, int set_fnum)
{
if (fname == NULL) { // don't add NULL file names
return;
@@ -7106,7 +7578,7 @@ void alist_add(alist_T *al, char_u *fname, int set_fnum)
#ifdef BACKSLASH_IN_FILENAME
slash_adjust(fname);
#endif
- AARGLIST(al)[al->al_ga.ga_len].ae_fname = fname;
+ AARGLIST(al)[al->al_ga.ga_len].ae_fname = (char_u *)fname;
if (set_fnum > 0) {
AARGLIST(al)[al->al_ga.ga_len].ae_fnum =
buflist_add(fname, BLN_LISTED | (set_fnum == 2 ? BLN_CURBUF : 0));
@@ -7115,9 +7587,8 @@ void alist_add(alist_T *al, char_u *fname, int set_fnum)
}
#if defined(BACKSLASH_IN_FILENAME)
-/*
- * Adjust slashes in file names. Called after 'shellslash' was set.
- */
+
+/// Adjust slashes in file names. Called after 'shellslash' was set.
void alist_slash_adjust(void)
{
for (int i = 0; i < GARGCOUNT; ++i) {
@@ -7142,7 +7613,6 @@ void alist_slash_adjust(void)
/// ":preserve".
static void ex_preserve(exarg_T *eap)
{
- curbuf->b_flags |= BF_PRESERVED;
ml_preserve(curbuf, true, true);
}
@@ -7163,38 +7633,34 @@ static void ex_recover(exarg_T *eap)
recoverymode = false;
}
-/*
- * Command modifier used in a wrong way.
- */
+/// Command modifier used in a wrong way.
static void ex_wrongmodifier(exarg_T *eap)
{
eap->errmsg = e_invcmd;
}
-/*
- * :sview [+command] file split window with new file, read-only
- * :split [[+command] file] split window with current or new file
- * :vsplit [[+command] file] split window vertically with current or new file
- * :new [[+command] file] split window with no or new file
- * :vnew [[+command] file] split vertically window with no or new file
- * :sfind [+command] file split window with file in 'path'
- *
- * :tabedit open new Tab page with empty window
- * :tabedit [+command] file open new Tab page and edit "file"
- * :tabnew [[+command] file] just like :tabedit
- * :tabfind [+command] file open new Tab page and find "file"
- */
+/// :sview [+command] file split window with new file, read-only
+/// :split [[+command] file] split window with current or new file
+/// :vsplit [[+command] file] split window vertically with current or new file
+/// :new [[+command] file] split window with no or new file
+/// :vnew [[+command] file] split vertically window with no or new file
+/// :sfind [+command] file split window with file in 'path'
+///
+/// :tabedit open new Tab page with empty window
+/// :tabedit [+command] file open new Tab page and edit "file"
+/// :tabnew [[+command] file] just like :tabedit
+/// :tabfind [+command] file open new Tab page and find "file"
void ex_splitview(exarg_T *eap)
{
win_T *old_curwin = curwin;
- char_u *fname = NULL;
+ char *fname = NULL;
const bool use_tab = eap->cmdidx == CMD_tabedit
|| eap->cmdidx == CMD_tabfind
|| eap->cmdidx == CMD_tabnew;
// A ":split" in the quickfix window works like ":new". Don't want two
// quickfix windows. But it's OK when doing ":tab split".
- if (bt_quickfix(curbuf) && cmdmod.tab == 0) {
+ if (bt_quickfix(curbuf) && cmdmod.cmod_tab == 0) {
if (eap->cmdidx == CMD_split) {
eap->cmdidx = CMD_new;
}
@@ -7204,8 +7670,8 @@ void ex_splitview(exarg_T *eap)
}
if (eap->cmdidx == CMD_sfind || eap->cmdidx == CMD_tabfind) {
- fname = find_file_in_path(eap->arg, STRLEN(eap->arg),
- FNAME_MESS, TRUE, curbuf->b_ffname);
+ fname = (char *)find_file_in_path((char_u *)eap->arg, STRLEN(eap->arg),
+ FNAME_MESS, true, (char_u *)curbuf->b_ffname);
if (fname == NULL) {
goto theend;
}
@@ -7216,8 +7682,8 @@ void ex_splitview(exarg_T *eap)
* Either open new tab page or split the window.
*/
if (use_tab) {
- if (win_new_tabpage(cmdmod.tab != 0 ? cmdmod.tab : eap->addr_count == 0
- ? 0 : (int)eap->line2 + 1, eap->arg) != FAIL) {
+ if (win_new_tabpage(cmdmod.cmod_tab != 0 ? cmdmod.cmod_tab : eap->addr_count == 0
+ ? 0 : (int)eap->line2 + 1, (char_u *)eap->arg) != FAIL) {
do_exedit(eap, old_curwin);
apply_autocmds(EVENT_TABNEWENTERED, NULL, NULL, false, curbuf);
@@ -7225,7 +7691,7 @@ void ex_splitview(exarg_T *eap)
if (curwin != old_curwin
&& win_valid(old_curwin)
&& old_curwin->w_buffer != curbuf
- && !cmdmod.keepalt) {
+ && (cmdmod.cmod_flags & CMOD_KEEPALT) == 0) {
old_curwin->w_alt_fnum = curbuf->b_fnum;
}
}
@@ -7241,28 +7707,23 @@ void ex_splitview(exarg_T *eap)
do_exedit(eap, old_curwin);
}
-
theend:
xfree(fname);
}
-/*
- * Open a new tab page.
- */
+/// Open a new tab page.
void tabpage_new(void)
{
exarg_T ea;
memset(&ea, 0, sizeof(ea));
ea.cmdidx = CMD_tabnew;
- ea.cmd = (char_u *)"tabn";
- ea.arg = (char_u *)"";
+ ea.cmd = "tabn";
+ ea.arg = "";
ex_splitview(&ea);
}
-/*
- * :tabnext command
- */
+/// :tabnext command
static void ex_tabnext(exarg_T *eap)
{
int tab_number;
@@ -7278,9 +7739,9 @@ static void ex_tabnext(exarg_T *eap)
case CMD_tabprevious:
case CMD_tabNext:
if (eap->arg && *eap->arg != NUL) {
- char_u *p = eap->arg;
- char_u *p_save = p;
- tab_number = getdigits(&p, false, 0);
+ char *p = eap->arg;
+ char *p_save = p;
+ tab_number = (int)getdigits((char_u **)&p, false, 0);
if (p == p_save || *p_save == '-' || *p_save == '+' || *p != NUL
|| tab_number == 0) {
// No numbers as argument.
@@ -7291,7 +7752,7 @@ static void ex_tabnext(exarg_T *eap)
if (eap->addr_count == 0) {
tab_number = 1;
} else {
- tab_number = eap->line2;
+ tab_number = (int)eap->line2;
if (tab_number < 1) {
eap->errmsg = e_invrange;
return;
@@ -7309,9 +7770,7 @@ static void ex_tabnext(exarg_T *eap)
}
}
-/*
- * :tabmove command
- */
+/// :tabmove command
static void ex_tabmove(exarg_T *eap)
{
int tab_number = get_tabpage_arg(eap);
@@ -7320,9 +7779,7 @@ static void ex_tabmove(exarg_T *eap)
}
}
-/*
- * :tabs command: List tabs and their contents.
- */
+/// :tabs command: List tabs and their contents.
static void ex_tabs(exarg_T *eap)
{
int tabcount = 1;
@@ -7358,20 +7815,17 @@ static void ex_tabs(exarg_T *eap)
if (buf_spname(wp->w_buffer) != NULL) {
STRLCPY(IObuff, buf_spname(wp->w_buffer), IOSIZE);
} else {
- home_replace(wp->w_buffer, wp->w_buffer->b_fname, IObuff, IOSIZE, true);
+ home_replace(wp->w_buffer, wp->w_buffer->b_fname, (char *)IObuff, IOSIZE, true);
}
- msg_outtrans(IObuff);
+ msg_outtrans((char *)IObuff);
ui_flush(); // output one line at a time
os_breakcheck();
}
}
}
-
-/*
- * ":mode":
- * If no argument given, get the screen size and redraw.
- */
+/// ":mode":
+/// If no argument given, get the screen size and redraw.
static void ex_mode(exarg_T *eap)
{
if (*eap->arg == NUL) {
@@ -7382,23 +7836,20 @@ static void ex_mode(exarg_T *eap)
}
}
-/*
- * ":resize".
- * set, increment or decrement current window height
- */
+/// ":resize".
+/// set, increment or decrement current window height
static void ex_resize(exarg_T *eap)
{
int n;
win_T *wp = curwin;
if (eap->addr_count > 0) {
- n = eap->line2;
- for (wp = firstwin; wp->w_next != NULL && --n > 0; wp = wp->w_next) {
- }
+ n = (int)eap->line2;
+ for (wp = firstwin; wp->w_next != NULL && --n > 0; wp = wp->w_next) {}
}
- n = atol((char *)eap->arg);
- if (cmdmod.split & WSP_VERT) {
+ n = (int)atol(eap->arg);
+ if (cmdmod.cmod_split & WSP_VERT) {
if (*eap->arg == '-' || *eap->arg == '+') {
n += wp->w_width;
} else if (n == 0 && eap->arg[0] == NUL) { // default is very wide
@@ -7409,29 +7860,27 @@ static void ex_resize(exarg_T *eap)
if (*eap->arg == '-' || *eap->arg == '+') {
n += wp->w_height;
} else if (n == 0 && eap->arg[0] == NUL) { // default is very high
- n = Rows-1;
+ n = Rows - 1;
}
win_setheight_win(n, wp);
}
}
-/*
- * ":find [+command] <file>" command.
- */
+/// ":find [+command] <file>" command.
static void ex_find(exarg_T *eap)
{
- char_u *fname;
- int count;
+ char *fname;
+ linenr_T count;
- fname = find_file_in_path(eap->arg, STRLEN(eap->arg),
- FNAME_MESS, TRUE, curbuf->b_ffname);
+ fname = (char *)find_file_in_path((char_u *)eap->arg, STRLEN(eap->arg),
+ FNAME_MESS, true, (char_u *)curbuf->b_ffname);
if (eap->addr_count > 0) {
// Repeat finding the file "count" times. This matters when it
// appears several times in the path.
count = eap->line2;
while (fname != NULL && --count > 0) {
xfree(fname);
- fname = find_file_in_path(NULL, 0, FNAME_MESS, FALSE, curbuf->b_ffname);
+ fname = (char *)find_file_in_path(NULL, 0, FNAME_MESS, false, (char_u *)curbuf->b_ffname);
}
}
@@ -7448,7 +7897,7 @@ static void ex_edit(exarg_T *eap)
do_exedit(eap, NULL);
}
-/// ":edit <file>" command and alikes.
+/// ":edit <file>" command and alike.
///
/// @param old_curwin curwin before doing a split or NULL
void do_exedit(exarg_T *eap, win_T *old_curwin)
@@ -7480,9 +7929,11 @@ void do_exedit(exarg_T *eap, win_T *old_curwin)
need_wait_return = false;
msg_scroll = 0;
redraw_all_later(NOT_VALID);
+ pending_exmode_active = true;
normal_enter(false, true);
+ pending_exmode_active = false;
RedrawingDisabled = rd;
no_wait_return = nwr;
msg_scroll = ms;
@@ -7517,7 +7968,7 @@ void do_exedit(exarg_T *eap, win_T *old_curwin)
if (eap->cmdidx != CMD_balt && eap->cmdidx != CMD_badd) {
setpcmark();
}
- if (do_ecmd(0, (eap->cmdidx == CMD_enew ? NULL : eap->arg),
+ if (do_ecmd(0, eap->cmdidx == CMD_enew ? NULL : eap->arg,
NULL, eap, eap->do_ecmd_lnum,
(buf_hide(curbuf) ? ECMD_HIDE : 0)
+ (eap->forceit ? ECMD_FORCEIT : 0)
@@ -7535,7 +7986,7 @@ void do_exedit(exarg_T *eap, win_T *old_curwin)
// Reset the error/interrupt/exception state here so that
// aborting() returns FALSE when closing a window.
enter_cleanup(&cs);
- win_close(curwin, !need_hide && !buf_hide(curbuf));
+ win_close(curwin, !need_hide && !buf_hide(curbuf), false);
// Restore the error/interrupt/exception state if not
// discarded by a new aborting error, interrupt, or
@@ -7553,7 +8004,7 @@ void do_exedit(exarg_T *eap, win_T *old_curwin)
readonlymode = n;
} else {
if (eap->do_ecmd_cmd != NULL) {
- do_cmdline_cmd((char *)eap->do_ecmd_cmd);
+ do_cmdline_cmd(eap->do_ecmd_cmd);
}
n = curwin->w_arg_idx_invalid;
check_arg_idx(curwin);
@@ -7571,7 +8022,7 @@ void do_exedit(exarg_T *eap, win_T *old_curwin)
&& curwin != old_curwin
&& win_valid(old_curwin)
&& old_curwin->w_buffer != curbuf
- && !cmdmod.keepalt) {
+ && (cmdmod.cmod_flags & CMOD_KEEPALT) == 0) {
old_curwin->w_alt_fnum = curbuf->b_fnum;
}
@@ -7584,6 +8035,10 @@ static void ex_nogui(exarg_T *eap)
eap->errmsg = N_("E25: Nvim does not have a built-in GUI");
}
+static void ex_popup(exarg_T *eap)
+{
+ pum_make_popup(eap->arg, eap->forceit);
+}
static void ex_swapname(exarg_T *eap)
{
@@ -7594,11 +8049,9 @@ static void ex_swapname(exarg_T *eap)
}
}
-/*
- * ":syncbind" forces all 'scrollbind' windows to have the same relative
- * offset.
- * (1998-11-02 16:21:01 R. Edward Ralston <eralston@computer.org>)
- */
+/// ":syncbind" forces all 'scrollbind' windows to have the same relative
+/// offset.
+/// (1998-11-02 16:21:01 R. Edward Ralston <eralston@computer.org>)
static void ex_syncbind(exarg_T *eap)
{
win_T *save_curwin = curwin;
@@ -7629,7 +8082,6 @@ static void ex_syncbind(exarg_T *eap)
topline = 1;
}
-
/*
* Set all scrollbind windows to the same topline.
*/
@@ -7655,7 +8107,7 @@ static void ex_syncbind(exarg_T *eap)
did_syncbind = true;
checkpcmark();
if (old_linenr != curwin->w_cursor.lnum) {
- char_u ctrl_o[2];
+ char ctrl_o[2];
ctrl_o[0] = Ctrl_O;
ctrl_o[1] = 0;
@@ -7664,7 +8116,6 @@ static void ex_syncbind(exarg_T *eap)
}
}
-
static void ex_read(exarg_T *eap)
{
int i;
@@ -7683,13 +8134,13 @@ static void ex_read(exarg_T *eap)
return;
}
i = readfile(curbuf->b_ffname, curbuf->b_fname,
- eap->line2, (linenr_T)0, (linenr_T)MAXLNUM, eap, 0);
+ eap->line2, (linenr_T)0, (linenr_T)MAXLNUM, eap, 0, false);
} else {
if (vim_strchr(p_cpo, CPO_ALTREAD) != NULL) {
(void)setaltfname(eap->arg, eap->arg, (linenr_T)1);
}
i = readfile(eap->arg, NULL,
- eap->line2, (linenr_T)0, (linenr_T)MAXLNUM, eap, 0);
+ eap->line2, (linenr_T)0, (linenr_T)MAXLNUM, eap, 0, false);
}
if (i != OK) {
if (!aborting()) {
@@ -7718,7 +8169,7 @@ static void ex_read(exarg_T *eap)
}
}
-static char_u *prev_dir = NULL;
+static char *prev_dir = NULL;
#if defined(EXITFREE)
void free_cd_dir(void)
@@ -7729,8 +8180,8 @@ void free_cd_dir(void)
#endif
-// Get the previous directory for the given chdir scope.
-static char_u *get_prevdir(CdScope scope)
+/// Get the previous directory for the given chdir scope.
+static char *get_prevdir(CdScope scope)
{
switch (scope) {
case kCdScopeTabpage:
@@ -7747,7 +8198,7 @@ static char_u *get_prevdir(CdScope scope)
/// Deal with the side effects of changing the current directory.
///
/// @param scope Scope of the function call (global, tab or window).
-void post_chdir(CdScope scope, bool trigger_dirchanged)
+static void post_chdir(CdScope scope, bool trigger_dirchanged)
{
// Always overwrite the window-local CWD.
XFREE_CLEAR(curwin->w_localdir);
@@ -7758,10 +8209,10 @@ void post_chdir(CdScope scope, bool trigger_dirchanged)
}
if (scope < kCdScopeGlobal) {
- char_u *pdir = get_prevdir(scope);
+ char *pdir = get_prevdir(scope);
// If still in global directory, set CWD as the global directory.
if (globaldir == NULL && pdir != NULL) {
- globaldir = vim_strsave(pdir);
+ globaldir = xstrdup(pdir);
}
}
@@ -7775,10 +8226,10 @@ void post_chdir(CdScope scope, bool trigger_dirchanged)
XFREE_CLEAR(globaldir);
break;
case kCdScopeTabpage:
- curtab->tp_localdir = (char_u *)xstrdup(cwd);
+ curtab->tp_localdir = xstrdup(cwd);
break;
case kCdScopeWindow:
- curwin->w_localdir = (char_u *)xstrdup(cwd);
+ curwin->w_localdir = xstrdup(cwd);
break;
case kCdScopeInvalid:
abort();
@@ -7788,7 +8239,7 @@ void post_chdir(CdScope scope, bool trigger_dirchanged)
shorten_fnames(true);
if (trigger_dirchanged) {
- do_autocmd_dirchanged(cwd, scope, kCdCauseManual);
+ do_autocmd_dirchanged(cwd, scope, kCdCauseManual, false);
}
}
@@ -7796,16 +8247,13 @@ void post_chdir(CdScope scope, bool trigger_dirchanged)
/// @param new_dir The directory to change to.
/// @param scope Scope of the function call (global, tab or window).
/// @return true if the directory is successfully changed.
-bool changedir_func(char_u *new_dir, CdScope scope)
+bool changedir_func(char *new_dir, CdScope scope)
{
- char_u *tofree;
- char_u *pdir = NULL;
- bool retval = false;
-
if (new_dir == NULL || allbuf_locked()) {
return false;
}
+ char *pdir = NULL;
// ":cd -": Change to previous directory
if (STRCMP(new_dir, "-") == 0) {
pdir = get_prevdir(scope);
@@ -7816,26 +8264,12 @@ bool changedir_func(char_u *new_dir, CdScope scope)
new_dir = pdir;
}
- // Free the previous directory
- tofree = get_prevdir(scope);
-
if (os_dirname(NameBuff, MAXPATHL) == OK) {
- pdir = vim_strsave(NameBuff);
+ pdir = (char *)vim_strsave(NameBuff);
} else {
pdir = NULL;
}
- switch (scope) {
- case kCdScopeTabpage:
- curtab->tp_prevdir = pdir;
- break;
- case kCdScopeWindow:
- curwin->w_prevdir = pdir;
- break;
- default:
- prev_dir = pdir;
- }
-
// For UNIX ":cd" means: go to home directory.
// On other systems too if 'cdhome' is set.
#if defined(UNIX)
@@ -7845,27 +8279,42 @@ bool changedir_func(char_u *new_dir, CdScope scope)
#endif
// Use NameBuff for home directory name.
expand_env((char_u *)"$HOME", NameBuff, MAXPATHL);
- new_dir = NameBuff;
+ new_dir = (char *)NameBuff;
}
- bool dir_differs = new_dir == NULL || pdir == NULL
- || pathcmp((char *)pdir, (char *)new_dir, -1) != 0;
- if (new_dir != NULL && (!dir_differs || vim_chdir(new_dir) == 0)) {
- post_chdir(scope, dir_differs);
- retval = true;
- } else {
- emsg(_(e_failed));
+ bool dir_differs = pdir == NULL || pathcmp(pdir, new_dir, -1) != 0;
+ if (dir_differs) {
+ do_autocmd_dirchanged(new_dir, scope, kCdCauseManual, true);
+ if (vim_chdir((char_u *)new_dir) != 0) {
+ emsg(_(e_failed));
+ xfree(pdir);
+ return false;
+ }
}
- xfree(tofree);
- return retval;
+ char **pp;
+ switch (scope) {
+ case kCdScopeTabpage:
+ pp = &curtab->tp_prevdir;
+ break;
+ case kCdScopeWindow:
+ pp = &curwin->w_prevdir;
+ break;
+ default:
+ pp = &prev_dir;
+ }
+ xfree(*pp);
+ *pp = pdir;
+
+ post_chdir(scope, dir_differs);
+
+ return true;
}
/// ":cd", ":tcd", ":lcd", ":chdir", "tchdir" and ":lchdir".
void ex_cd(exarg_T *eap)
{
- char_u *new_dir;
- new_dir = eap->arg;
+ char *new_dir = eap->arg;
#if !defined(UNIX)
// for non-UNIX ":cd" means: print current directory unless 'cdhome' is set
if (*new_dir == NUL && !p_cdh) {
@@ -7895,9 +8344,7 @@ void ex_cd(exarg_T *eap)
}
}
-/*
- * ":pwd".
- */
+/// ":pwd".
static void ex_pwd(exarg_T *eap)
{
if (os_dirname(NameBuff, MAXPATHL) == OK) {
@@ -7922,9 +8369,7 @@ static void ex_pwd(exarg_T *eap)
}
}
-/*
- * ":=".
- */
+/// ":=".
static void ex_equal(exarg_T *eap)
{
smsg("%" PRId64, (int64_t)eap->line2);
@@ -7955,9 +8400,7 @@ static void ex_sleep(exarg_T *eap)
do_sleep(len);
}
-/*
- * Sleep for "msec" milliseconds, but keep checking for a CTRL-C every second.
- */
+/// Sleep for "msec" milliseconds, but keep checking for a CTRL-C every second.
void do_sleep(long msec)
{
ui_flush(); // flush before waiting
@@ -7974,31 +8417,10 @@ void do_sleep(long msec)
}
}
-static void do_exmap(exarg_T *eap, int isabbrev)
-{
- int mode;
- char_u *cmdp;
-
- cmdp = eap->cmd;
- mode = get_map_mode(&cmdp, eap->forceit || isabbrev);
-
- switch (do_map((*cmdp == 'n') ? 2 : (*cmdp == 'u'),
- eap->arg, mode, isabbrev)) {
- case 1:
- emsg(_(e_invarg));
- break;
- case 2:
- emsg(isabbrev ? _(e_noabbr) : _(e_nomap));
- break;
- }
-}
-
-/*
- * ":winsize" command (obsolete).
- */
+/// ":winsize" command (obsolete).
static void ex_winsize(exarg_T *eap)
{
- char_u *arg = eap->arg;
+ char *arg = eap->arg;
if (!ascii_isdigit(*arg)) {
semsg(_(e_invarg2), arg);
@@ -8006,7 +8428,7 @@ static void ex_winsize(exarg_T *eap)
}
int w = getdigits_int(&arg, false, 10);
arg = skipwhite(arg);
- char_u *p = arg;
+ char *p = arg;
int h = getdigits_int(&arg, false, 10);
if (*p != NUL && *arg == NUL) {
screen_resize(w, h);
@@ -8018,7 +8440,7 @@ static void ex_winsize(exarg_T *eap)
static void ex_wincmd(exarg_T *eap)
{
int xchar = NUL;
- char_u *p;
+ char *p;
if (*eap->arg == 'g' || *eap->arg == Ctrl_G) {
// CTRL-W g and CTRL-W CTRL-G have an extra command character
@@ -8026,29 +8448,27 @@ static void ex_wincmd(exarg_T *eap)
emsg(_(e_invarg));
return;
}
- xchar = eap->arg[1];
+ xchar = (uint8_t)eap->arg[1];
p = eap->arg + 2;
} else {
p = eap->arg + 1;
}
- eap->nextcmd = check_nextcmd(p);
+ eap->nextcmd = (char *)check_nextcmd((char_u *)p);
p = skipwhite(p);
if (*p != NUL && *p != '"' && eap->nextcmd == NULL) {
emsg(_(e_invarg));
} else if (!eap->skip) {
// Pass flags on for ":vertical wincmd ]".
- postponed_split_flags = cmdmod.split;
- postponed_split_tab = cmdmod.tab;
+ postponed_split_flags = cmdmod.cmod_split;
+ postponed_split_tab = cmdmod.cmod_tab;
do_window(*eap->arg, eap->addr_count > 0 ? eap->line2 : 0L, xchar);
postponed_split_flags = 0;
postponed_split_tab = 0;
}
}
-/*
- * Handle command that work like operators: ":delete", ":yank", ":>" and ":<".
- */
+/// Handle command that work like operators: ":delete", ":yank", ":>" and ":<".
static void ex_operators(exarg_T *eap)
{
oparg_T oa;
@@ -8078,7 +8498,7 @@ static void ex_operators(exarg_T *eap)
case CMD_yank:
oa.op_type = OP_YANK;
- (void)op_yank(&oa, true, false);
+ (void)op_yank(&oa, true);
break;
default: // CMD_rshift or CMD_lshift
@@ -8095,9 +8515,7 @@ static void ex_operators(exarg_T *eap)
ex_may_print(eap);
}
-/*
- * ":put".
- */
+/// ":put".
static void ex_put(exarg_T *eap)
{
// ":0put" works like ":1put!".
@@ -8106,13 +8524,12 @@ static void ex_put(exarg_T *eap)
eap->forceit = TRUE;
}
curwin->w_cursor.lnum = eap->line2;
+ check_cursor_col();
do_put(eap->regname, NULL, eap->forceit ? BACKWARD : FORWARD, 1,
PUT_LINE|PUT_CURSLINE);
}
-/*
- * Handle ":copy" and ":move".
- */
+/// Handle ":copy" and ":move".
static void ex_copymove(exarg_T *eap)
{
long n = get_address(eap, &eap->arg, eap->addr_type, false, false, false, 1);
@@ -8131,20 +8548,18 @@ static void ex_copymove(exarg_T *eap)
}
if (eap->cmdidx == CMD_move) {
- if (do_move(eap->line1, eap->line2, n) == FAIL) {
+ if (do_move(eap->line1, eap->line2, (linenr_T)n) == FAIL) {
return;
}
} else {
- ex_copy(eap->line1, eap->line2, n);
+ ex_copy(eap->line1, eap->line2, (linenr_T)n);
}
u_clearline();
beginline(BL_SOL | BL_FIX);
ex_may_print(eap);
}
-/*
- * Print the current line if flags were given to the Ex command.
- */
+/// Print the current line if flags were given to the Ex command.
void ex_may_print(exarg_T *eap)
{
if (eap->flags != 0) {
@@ -8164,9 +8579,19 @@ static void ex_submagic(exarg_T *eap)
p_magic = magic_save;
}
-/*
- * ":join".
- */
+/// ":smagic" and ":snomagic" preview callback.
+static int ex_submagic_preview(exarg_T *eap, long cmdpreview_ns, handle_T cmdpreview_bufnr)
+{
+ int magic_save = p_magic;
+
+ p_magic = (eap->cmdidx == CMD_smagic);
+ int retv = ex_substitute_preview(eap, cmdpreview_ns, cmdpreview_bufnr);
+ p_magic = magic_save;
+
+ return retv;
+}
+
+/// ":join".
static void ex_join(exarg_T *eap)
{
curwin->w_cursor.lnum = eap->line1;
@@ -8180,14 +8605,12 @@ static void ex_join(exarg_T *eap)
}
++eap->line2;
}
- do_join(eap->line2 - eap->line1 + 1, !eap->forceit, TRUE, TRUE, true);
+ do_join((size_t)((ssize_t)eap->line2 - eap->line1 + 1), !eap->forceit, true, true, true);
beginline(BL_WHITE | BL_FIX);
ex_may_print(eap);
}
-/*
- * ":[addr]@r": execute register
- */
+/// ":[addr]@r": execute register
static void ex_at(exarg_T *eap)
{
int prev_len = typebuf.tb_len;
@@ -8196,14 +8619,13 @@ static void ex_at(exarg_T *eap)
check_cursor_col();
// Get the register name. No name means use the previous one.
- int c = *eap->arg;
+ int c = (uint8_t)(*eap->arg);
if (c == NUL) {
c = '@';
}
// Put the register in the typeahead buffer with the "silent" flag.
- if (do_execreg(c, TRUE, vim_strchr(p_cpo, CPO_EXECBUF) != NULL, TRUE)
- == FAIL) {
+ if (do_execreg(c, true, vim_strchr(p_cpo, CPO_EXECBUF) != NULL, true) == FAIL) {
beep_flush();
} else {
bool save_efr = exec_from_reg;
@@ -8223,40 +8645,64 @@ static void ex_at(exarg_T *eap)
}
}
-/*
- * ":!".
- */
+/// ":!".
static void ex_bang(exarg_T *eap)
{
do_bang(eap->addr_count, eap, eap->forceit, true, true);
}
-/*
- * ":undo".
- */
+/// ":undo".
static void ex_undo(exarg_T *eap)
{
- if (eap->addr_count == 1) { // :undo 123
- undo_time(eap->line2, false, false, true);
- } else {
- u_undo(1);
+ if (eap->addr_count != 1) {
+ if (eap->forceit) {
+ u_undo_and_forget(1); // :undo!
+ } else {
+ u_undo(1); // :undo
+ }
+ return;
+ }
+
+ long step = eap->line2;
+
+ if (eap->forceit) { // undo! 123
+ // change number for "undo!" must be lesser than current change number
+ if (step >= curbuf->b_u_seq_cur) {
+ emsg(_(e_undobang_cannot_redo_or_move_branch));
+ return;
+ }
+ // ensure that target change number is in same branch
+ // while also counting the amount of undoes it'd take to reach target
+ u_header_T *uhp;
+ int count = 0;
+
+ for (uhp = curbuf->b_u_curhead ? curbuf->b_u_curhead : curbuf->b_u_newhead;
+ uhp != NULL && uhp->uh_seq > step;
+ uhp = uhp->uh_next.ptr, ++count) {}
+ if (step != 0 && (uhp == NULL || uhp->uh_seq < step)) {
+ emsg(_(e_undobang_cannot_redo_or_move_branch));
+ return;
+ }
+ u_undo_and_forget(count);
+ } else { // :undo 123
+ undo_time(step, false, false, true);
}
}
static void ex_wundo(exarg_T *eap)
{
- char_u hash[UNDO_HASH_SIZE];
+ char hash[UNDO_HASH_SIZE];
- u_compute_hash(curbuf, hash);
- u_write_undo((char *)eap->arg, eap->forceit, curbuf, hash);
+ u_compute_hash(curbuf, (char_u *)hash);
+ u_write_undo(eap->arg, eap->forceit, curbuf, (char_u *)hash);
}
static void ex_rundo(exarg_T *eap)
{
- char_u hash[UNDO_HASH_SIZE];
+ char hash[UNDO_HASH_SIZE];
- u_compute_hash(curbuf, hash);
- u_read_undo((char *)eap->arg, hash, NULL);
+ u_compute_hash(curbuf, (char_u *)hash);
+ u_read_undo(eap->arg, (char_u *)hash, NULL);
}
/// ":redo".
@@ -8271,12 +8717,12 @@ static void ex_later(exarg_T *eap)
long count = 0;
bool sec = false;
bool file = false;
- char_u *p = eap->arg;
+ char *p = eap->arg;
if (*p == NUL) {
count = 1;
} else if (isdigit(*p)) {
- count = getdigits_long(&p, false, 0);
+ count = getdigits_long((char_u **)&p, false, 0);
switch (*p) {
case 's':
++p; sec = true; break;
@@ -8299,14 +8745,12 @@ static void ex_later(exarg_T *eap)
}
}
-/*
- * ":redir": start/stop redirection.
- */
+/// ":redir": start/stop redirection.
static void ex_redir(exarg_T *eap)
{
char *mode;
- char_u *fname;
- char_u *arg = eap->arg;
+ char *fname;
+ char *arg = eap->arg;
if (STRICMP(eap->arg, "END") == 0) {
close_redir();
@@ -8329,14 +8773,14 @@ static void ex_redir(exarg_T *eap)
return;
}
- redir_fd = open_exfile(fname, eap->forceit, mode);
+ redir_fd = open_exfile((char_u *)fname, eap->forceit, mode);
xfree(fname);
} else if (*arg == '@') {
// redirect to a register a-z (resp. A-Z for appending)
close_redir();
++arg;
if (valid_yank_reg(*arg, true) && *arg != '_') {
- redir_reg = *arg++;
+ redir_reg = (char_u)(*arg++);
if (*arg == '>' && arg[1] == '>') { // append
arg += 2;
} else {
@@ -8388,7 +8832,7 @@ static void ex_redir(exarg_T *eap)
/// ":redraw": force redraw
static void ex_redraw(exarg_T *eap)
{
- if (State & CMDPREVIEW) {
+ if (cmdpreview) {
return; // Ignore :redraw during 'inccommand' preview. #9777
}
int r = RedrawingDisabled;
@@ -8419,10 +8863,10 @@ static void ex_redraw(exarg_T *eap)
ui_flush();
}
-/// ":redrawstatus": force redraw of status line(s)
+/// ":redrawstatus": force redraw of status line(s) and window bar(s)
static void ex_redrawstatus(exarg_T *eap)
{
- if (State & CMDPREVIEW) {
+ if (cmdpreview) {
return; // Ignore :redrawstatus during 'inccommand' preview. #9777
}
int r = RedrawingDisabled;
@@ -8435,14 +8879,13 @@ static void ex_redrawstatus(exarg_T *eap)
} else {
status_redraw_curbuf();
}
- update_screen(VIsual_active ? INVERTED :
- 0);
+ update_screen(VIsual_active ? INVERTED : 0);
RedrawingDisabled = r;
p_lz = p;
ui_flush();
}
-// ":redrawtabline": force redraw of the tabline
+/// ":redrawtabline": force redraw of the tabline
static void ex_redrawtabline(exarg_T *eap FUNC_ATTR_UNUSED)
{
const int r = RedrawingDisabled;
@@ -8516,9 +8959,7 @@ FILE *open_exfile(char_u *fname, int forceit, char *mode)
return fd;
}
-/*
- * ":mark" and ":k".
- */
+/// ":mark" and ":k".
static void ex_mark(exarg_T *eap)
{
pos_T pos;
@@ -8538,9 +8979,7 @@ static void ex_mark(exarg_T *eap)
}
}
-/*
- * Update w_topline, w_leftcol and the cursor position.
- */
+/// Update w_topline, w_leftcol and the cursor position.
void update_topline_cursor(void)
{
check_cursor(); // put cursor on valid line
@@ -8551,8 +8990,9 @@ void update_topline_cursor(void)
update_curswant();
}
-// Save the current State and go to Normal mode.
-// Return true if the typeahead could be saved.
+/// Save the current State and go to Normal mode.
+///
+/// @return true if the typeahead could be saved.
bool save_current_state(save_state_T *sst)
FUNC_ATTR_NONNULL_ALL
{
@@ -8560,14 +9000,13 @@ bool save_current_state(save_state_T *sst)
sst->save_restart_edit = restart_edit;
sst->save_msg_didout = msg_didout;
sst->save_State = State;
- sst->save_insertmode = p_im;
sst->save_finish_op = finish_op;
sst->save_opcount = opcount;
sst->save_reg_executing = reg_executing;
+ sst->save_pending_end_reg_executing = pending_end_reg_executing;
msg_scroll = false; // no msg scrolling in Normal mode
restart_edit = 0; // don't go to Insert mode
- p_im = false; // don't use 'insertmode
// Save the current typeahead. This is required to allow using ":normal"
// from an event handler and makes sure we don't hang when the argument
@@ -8590,10 +9029,10 @@ void restore_current_state(save_state_T *sst)
// override the value of restart_edit anyway.
restart_edit = sst->save_restart_edit;
}
- p_im = sst->save_insertmode;
finish_op = sst->save_finish_op;
opcount = sst->save_opcount;
reg_executing = sst->save_reg_executing;
+ pending_end_reg_executing = sst->save_pending_end_reg_executing;
// don't reset msg_didout now
msg_didout |= sst->save_msg_didout;
@@ -8604,19 +9043,17 @@ void restore_current_state(save_state_T *sst)
ui_cursor_shape(); // may show different cursor shape
}
-/*
- * ":normal[!] {commands}": Execute normal mode commands.
- */
+/// ":normal[!] {commands}": Execute normal mode commands.
static void ex_normal(exarg_T *eap)
{
- if (curbuf->terminal && State & TERM_FOCUS) {
+ if (curbuf->terminal && State & MODE_TERMINAL) {
emsg("Can't re-enter normal mode from terminal mode");
return;
}
save_state_T save_state;
- char_u *arg = NULL;
+ char *arg = NULL;
int l;
- char_u *p;
+ char *p;
if (ex_normal_lock > 0) {
emsg(_(e_secure));
@@ -8627,7 +9064,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.
{
@@ -8636,21 +9073,20 @@ 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 == (char)K_SPECIAL) { // trailbyte K_SPECIAL
len += 2;
}
}
}
if (len > 0) {
- arg = xmalloc(STRLEN(eap->arg) + len + 1);
+ arg = xmalloc(STRLEN(eap->arg) + (size_t)len + 1);
len = 0;
- for (p = eap->arg; *p != NUL; ++p) {
+ for (p = eap->arg; *p != NUL; p++) {
arg[len++] = *p;
for (l = utfc_ptr2len(p) - 1; l > 0; l--) {
arg[len++] = *++p;
- if (*p == K_SPECIAL) {
- arg[len++] = KS_SPECIAL;
+ if (*p == (char)K_SPECIAL) {
+ arg[len++] = (char)KS_SPECIAL;
arg[len++] = KE_FILLER;
}
}
@@ -8671,7 +9107,7 @@ static void ex_normal(exarg_T *eap)
check_cursor_moved(curwin);
}
- exec_normal_cmd(arg != NULL ? arg : eap->arg,
+ exec_normal_cmd((char_u *)(arg != NULL ? arg : eap->arg),
eap->forceit ? REMAP_NONE : REMAP_YES, false);
} while (eap->addr_count > 0 && eap->line1 <= eap->line2 && !got_int);
}
@@ -8688,9 +9124,7 @@ static void ex_normal(exarg_T *eap)
xfree(arg);
}
-/*
- * ":startinsert", ":startreplace" and ":startgreplace"
- */
+/// ":startinsert", ":startreplace" and ":startgreplace"
static void ex_startinsert(exarg_T *eap)
{
if (eap->forceit) {
@@ -8703,7 +9137,7 @@ static void ex_startinsert(exarg_T *eap)
// Ignore the command when already in Insert mode. Inserting an
// expression register that invokes a function can do this.
- if (State & INSERT) {
+ if (State & MODE_INSERT) {
return;
}
@@ -8727,9 +9161,7 @@ static void ex_startinsert(exarg_T *eap)
}
}
-/*
- * ":stopinsert"
- */
+/// ":stopinsert"
static void ex_stopinsert(exarg_T *eap)
{
restart_edit = 0;
@@ -8737,14 +9169,12 @@ static void ex_stopinsert(exarg_T *eap)
clearmode();
}
-/*
- * Execute normal mode command "cmd".
- * "remap" can be REMAP_NONE or REMAP_YES.
- */
+/// Execute normal mode command "cmd".
+/// "remap" can be REMAP_NONE or REMAP_YES.
void exec_normal_cmd(char_u *cmd, int remap, bool silent)
{
// Stuff the argument into the typeahead buffer.
- ins_typebuf(cmd, remap, 0, true, silent);
+ ins_typebuf((char *)cmd, remap, 0, true, silent);
exec_normal(false);
}
@@ -8773,12 +9203,10 @@ static void ex_checkpath(exarg_T *eap)
(linenr_T)1, (linenr_T)MAXLNUM);
}
-/*
- * ":psearch"
- */
+/// ":psearch"
static void ex_psearch(exarg_T *eap)
{
- g_do_tagpreview = p_pvh;
+ g_do_tagpreview = (int)p_pvh;
ex_findpat(eap);
g_do_tagpreview = 0;
}
@@ -8787,7 +9215,7 @@ static void ex_findpat(exarg_T *eap)
{
bool whole = true;
long n;
- char_u *p;
+ char *p;
int action;
switch (cmdnames[eap->cmdidx].cmd_name[2]) {
@@ -8811,13 +9239,13 @@ static void ex_findpat(exarg_T *eap)
n = 1;
if (ascii_isdigit(*eap->arg)) { // get count
- n = getdigits_long(&eap->arg, false, 0);
+ n = getdigits_long((char_u **)&eap->arg, false, 0);
eap->arg = skipwhite(eap->arg);
}
if (*eap->arg == '/') { // Match regexp, not just whole words
whole = false;
eap->arg++;
- p = skip_regexp(eap->arg, '/', p_magic, NULL);
+ p = (char *)skip_regexp((char_u *)eap->arg, '/', p_magic, NULL);
if (*p) {
*p++ = NUL;
p = skipwhite(p);
@@ -8826,36 +9254,31 @@ static void ex_findpat(exarg_T *eap)
if (!ends_excmd(*p)) {
eap->errmsg = e_trailing;
} else {
- eap->nextcmd = check_nextcmd(p);
+ eap->nextcmd = (char *)check_nextcmd((char_u *)p);
}
}
}
if (!eap->skip) {
- find_pattern_in_path(eap->arg, 0, STRLEN(eap->arg), whole, !eap->forceit,
+ find_pattern_in_path((char_u *)eap->arg, 0, STRLEN(eap->arg), whole, !eap->forceit,
*eap->cmd == 'd' ? FIND_DEFINE : FIND_ANY,
n, action, eap->line1, eap->line2);
}
}
-
-/*
- * ":ptag", ":ptselect", ":ptjump", ":ptnext", etc.
- */
+/// ":ptag", ":ptselect", ":ptjump", ":ptnext", etc.
static void ex_ptag(exarg_T *eap)
{
- g_do_tagpreview = p_pvh; // will be reset to 0 in ex_tag_cmd()
+ g_do_tagpreview = (int)p_pvh; // will be reset to 0 in ex_tag_cmd()
ex_tag_cmd(eap, cmdnames[eap->cmdidx].cmd_name + 1);
}
-/*
- * ":pedit"
- */
+/// ":pedit"
static void ex_pedit(exarg_T *eap)
{
win_T *curwin_save = curwin;
// Open the preview window or popup and make it the current window.
- g_do_tagpreview = p_pvh;
+ g_do_tagpreview = (int)p_pvh;
prepare_tagpreview(true);
// Edit the file.
@@ -8870,28 +9293,24 @@ static void ex_pedit(exarg_T *eap)
g_do_tagpreview = 0;
}
-/*
- * ":stag", ":stselect" and ":stjump".
- */
+/// ":stag", ":stselect" and ":stjump".
static void ex_stag(exarg_T *eap)
{
postponed_split = -1;
- postponed_split_flags = cmdmod.split;
- postponed_split_tab = cmdmod.tab;
+ postponed_split_flags = cmdmod.cmod_split;
+ postponed_split_tab = cmdmod.cmod_tab;
ex_tag_cmd(eap, cmdnames[eap->cmdidx].cmd_name + 1);
postponed_split_flags = 0;
postponed_split_tab = 0;
}
-/*
- * ":tag", ":tselect", ":tjump", ":tnext", etc.
- */
+/// ":tag", ":tselect", ":tjump", ":tnext", etc.
static void ex_tag(exarg_T *eap)
{
ex_tag_cmd(eap, cmdnames[eap->cmdidx].cmd_name);
}
-static void ex_tag_cmd(exarg_T *eap, char_u *name)
+static void ex_tag_cmd(exarg_T *eap, char *name)
{
int cmd;
@@ -8932,8 +9351,8 @@ static void ex_tag_cmd(exarg_T *eap, char_u *name)
cmd = DT_LTAG;
}
- do_tag(eap->arg, cmd, eap->addr_count > 0 ? (int)eap->line2 : 1,
- eap->forceit, TRUE);
+ do_tag((char_u *)eap->arg, cmd, eap->addr_count > 0 ? (int)eap->line2 : 1,
+ eap->forceit, true);
}
enum {
@@ -8954,11 +9373,9 @@ enum {
// SPEC_CLIENT,
};
-/*
- * Check "str" for starting with a special cmdline variable.
- * If found return one of the SPEC_ values and set "*usedlen" to the length of
- * the variable. Otherwise return -1 and "*usedlen" is unchanged.
- */
+/// Check "str" for starting with a special cmdline variable.
+/// If found return one of the SPEC_ values and set "*usedlen" to the length of
+/// the variable. Otherwise return -1 and "*usedlen" is unchanged.
ssize_t find_cmdline_var(const char_u *src, size_t *usedlen)
FUNC_ATTR_NONNULL_ALL
{
@@ -9023,9 +9440,9 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum
int *escaped)
{
int i;
- char_u *s;
- char_u *result;
- char_u *resultbuf = NULL;
+ char *s;
+ char *result;
+ char *resultbuf = NULL;
size_t resultlen;
buf_T *buf;
int valid = VALID_HEAD | VALID_PATH; // Assume valid result.
@@ -9063,7 +9480,7 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum
if (spec_idx == SPEC_CWORD
|| spec_idx == SPEC_CCWORD
|| spec_idx == SPEC_CEXPR) {
- resultlen = find_ident_under_cursor(&result,
+ resultlen = find_ident_under_cursor((char_u **)&result,
spec_idx == SPEC_CWORD
? (FIND_IDENT | FIND_STRING)
: (spec_idx == SPEC_CEXPR
@@ -9084,7 +9501,7 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum
switch (spec_idx) {
case SPEC_PERC: // '%': current file
if (curbuf->b_fname == NULL) {
- result = (char_u *)"";
+ result = "";
valid = 0; // Must have ":p:h" to be valid
} else {
result = curbuf->b_fname;
@@ -9103,16 +9520,16 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum
skip_mod = true;
break;
}
- s = src + 1;
+ s = (char *)src + 1;
if (*s == '<') { // "#<99" uses v:oldfiles.
s++;
}
i = getdigits_int(&s, false, 0);
- if (s == src + 2 && src[1] == '-') {
+ if ((char_u *)s == src + 2 && src[1] == '-') {
// just a minus sign, don't skip over it
s--;
}
- *usedlen = (size_t)(s - src); // length of what we expand
+ *usedlen = (size_t)((char_u *)s - src); // length of what we expand
if (src[1] == '<' && i != 0) {
if (*usedlen < 2) {
@@ -9120,8 +9537,7 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum
*usedlen = 1;
return NULL;
}
- result = (char_u *)tv_list_find_str(get_vim_var_list(VV_OLDFILES),
- i - 1);
+ result = (char *)tv_list_find_str(get_vim_var_list(VV_OLDFILES), i - 1);
if (result == NULL) {
*errormsg = "";
return NULL;
@@ -9139,7 +9555,7 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum
*lnump = ECMD_LAST;
}
if (buf->b_fname == NULL) {
- result = (char_u *)"";
+ result = "";
valid = 0; // Must have ":p:h" to be valid
} else {
result = buf->b_fname;
@@ -9149,7 +9565,7 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum
break;
case SPEC_CFILE: // file name under cursor
- result = file_name_at_cursor(FNAME_MESS|FNAME_HYP, 1L, NULL);
+ result = (char *)file_name_at_cursor(FNAME_MESS|FNAME_HYP, 1L, NULL);
if (result == NULL) {
*errormsg = "";
return NULL;
@@ -9159,12 +9575,12 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum
case SPEC_AFILE: // file name for autocommand
if (autocmd_fname != NULL
- && !path_is_absolute(autocmd_fname)
+ && !path_is_absolute((char_u *)autocmd_fname)
// For CmdlineEnter and related events, <afile> is not a path! #9348
- && !strequal("/", (char *)autocmd_fname)) {
+ && !strequal("/", autocmd_fname)) {
// Still need to turn the fname into a full path. It was
// postponed to avoid a delay when <afile> is not used.
- result = (char_u *)FullName_save((char *)autocmd_fname, false);
+ result = FullName_save(autocmd_fname, false);
// Copy into `autocmd_fname`, don't reassign it. #8165
STRLCPY(autocmd_fname, result, MAXPATHL);
xfree(result);
@@ -9174,7 +9590,7 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum
*errormsg = _("E495: no autocommand file name to substitute for \"<afile>\"");
return NULL;
}
- result = path_try_shorten_fname(result);
+ result = (char *)path_try_shorten_fname((char_u *)result);
break;
case SPEC_ABUF: // buffer number for autocommand
@@ -9183,7 +9599,7 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum
return NULL;
}
snprintf(strbuf, sizeof(strbuf), "%d", autocmd_bufnr);
- result = (char_u *)strbuf;
+ result = strbuf;
break;
case SPEC_AMATCH: // match name for autocommand
@@ -9208,7 +9624,7 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum
return NULL;
}
snprintf(strbuf, sizeof(strbuf), "%" PRIdLINENR, sourcing_lnum);
- result = (char_u *)strbuf;
+ result = strbuf;
break;
case SPEC_SFLNUM: // line in script file
@@ -9218,7 +9634,7 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum
}
snprintf((char *)strbuf, sizeof(strbuf), "%" PRIdLINENR,
current_sctx.sc_lnum + sourcing_lnum);
- result = (char_u *)strbuf;
+ result = strbuf;
break;
case SPEC_SID:
@@ -9228,13 +9644,13 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum
}
snprintf(strbuf, sizeof(strbuf), "<SNR>%" PRIdSCID "_",
current_sctx.sc_sid);
- result = (char_u *)strbuf;
+ result = strbuf;
break;
default:
// should not happen
*errormsg = "";
- result = (char_u *)""; // avoid gcc warning
+ result = ""; // avoid gcc warning
break;
}
@@ -9243,11 +9659,12 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum
// Remove the file name extension.
if (src[*usedlen] == '<') {
(*usedlen)++;
- if ((s = STRRCHR(result, '.')) != NULL && s >= path_tail(result)) {
+ if ((s = (char *)STRRCHR(result, '.')) != NULL
+ && s >= path_tail(result)) {
resultlen = (size_t)(s - result);
}
} else if (!skip_mod) {
- valid |= modify_fname(src, tilde_file, usedlen, &result,
+ valid |= modify_fname((char *)src, tilde_file, usedlen, &result,
&resultbuf, &resultlen);
if (result == NULL) {
*errormsg = "";
@@ -9265,23 +9682,21 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum
}
result = NULL;
} else {
- result = vim_strnsave(result, resultlen);
+ result = xstrnsave(result, resultlen);
}
xfree(resultbuf);
- return result;
+ return (char_u *)result;
}
-/*
- * Concatenate all files in the argument list, separated by spaces, and return
- * it in one allocated string.
- * Spaces and backslashes in the file names are escaped with a backslash.
- */
-static char_u *arg_all(void)
+/// Concatenate all files in the argument list, separated by spaces, and return
+/// it in one allocated string.
+/// Spaces and backslashes in the file names are escaped with a backslash.
+static char *arg_all(void)
{
int len;
int idx;
- char_u *retval = NULL;
- char_u *p;
+ char *retval = NULL;
+ char *p;
/*
* Do this loop two times:
@@ -9290,7 +9705,7 @@ static char_u *arg_all(void)
*/
for (;;) {
len = 0;
- for (idx = 0; idx < ARGCOUNT; ++idx) {
+ for (idx = 0; idx < ARGCOUNT; idx++) {
p = alist_name(&ARGLIST[idx]);
if (p == NULL) {
continue;
@@ -9328,35 +9743,33 @@ static char_u *arg_all(void)
}
// allocate memory
- retval = xmalloc(len + 1);
+ retval = xmalloc((size_t)len + 1);
}
return retval;
}
-/*
- * Expand the <sfile> string in "arg".
- *
- * Returns an allocated string, or NULL for any error.
- */
-char_u *expand_sfile(char_u *arg)
+/// Expand the <sfile> string in "arg".
+///
+/// @return an allocated string, or NULL for any error.
+char *expand_sfile(char *arg)
{
char *errormsg;
size_t len;
- char_u *result;
- char_u *newres;
- char_u *repl;
+ char *result;
+ char *newres;
+ char *repl;
size_t srclen;
- char_u *p;
+ char *p;
- result = vim_strsave(arg);
+ result = xstrdup(arg);
for (p = result; *p;) {
if (STRNCMP(p, "<sfile>", 7) != 0) {
++p;
} else {
// replace "<sfile>" with the sourced file name, and do ":" stuff
- repl = eval_vars(p, result, &srclen, NULL, &errormsg, NULL);
+ repl = (char *)eval_vars((char_u *)p, (char_u *)result, &srclen, NULL, &errormsg, NULL);
if (errormsg != NULL) {
if (*errormsg) {
emsg(errormsg);
@@ -9384,40 +9797,34 @@ char_u *expand_sfile(char_u *arg)
return result;
}
-/*
- * ":rshada" and ":wshada".
- */
+/// ":rshada" and ":wshada".
static void ex_shada(exarg_T *eap)
{
- char_u *save_shada;
+ char *save_shada;
- save_shada = p_shada;
+ save_shada = (char *)p_shada;
if (*p_shada == NUL) {
p_shada = (char_u *)"'100";
}
if (eap->cmdidx == CMD_rviminfo || eap->cmdidx == CMD_rshada) {
- (void)shada_read_everything((char *)eap->arg, eap->forceit, false);
+ (void)shada_read_everything(eap->arg, eap->forceit, false);
} else {
- shada_write_file((char *)eap->arg, eap->forceit);
+ shada_write_file(eap->arg, eap->forceit);
}
- p_shada = save_shada;
+ p_shada = (char_u *)save_shada;
}
-/*
- * Make a dialog message in "buff[DIALOG_MSG_SIZE]".
- * "format" must contain "%s".
- */
-void dialog_msg(char_u *buff, char *format, char_u *fname)
+/// Make a dialog message in "buff[DIALOG_MSG_SIZE]".
+/// "format" must contain "%s".
+void dialog_msg(char *buff, char *format, char *fname)
{
if (fname == NULL) {
- fname = (char_u *)_("Untitled");
+ fname = _("Untitled");
}
- vim_snprintf((char *)buff, DIALOG_MSG_SIZE, format, fname);
+ vim_snprintf(buff, DIALOG_MSG_SIZE, format, fname);
}
-/*
- * ":behave {mswin,xterm}"
- */
+/// ":behave {mswin,xterm}"
static void ex_behave(exarg_T *eap)
{
if (STRCMP(eap->arg, "mswin") == 0) {
@@ -9435,35 +9842,33 @@ static void ex_behave(exarg_T *eap)
}
}
-/*
- * Function given to ExpandGeneric() to obtain the possible arguments of the
- * ":behave {mswin,xterm}" command.
- */
-char_u *get_behave_arg(expand_T *xp, int idx)
+/// Function given to ExpandGeneric() to obtain the possible arguments of the
+/// ":behave {mswin,xterm}" command.
+char *get_behave_arg(expand_T *xp, int idx)
{
if (idx == 0) {
- return (char_u *)"mswin";
+ return "mswin";
}
if (idx == 1) {
- return (char_u *)"xterm";
+ return "xterm";
}
return NULL;
}
-// Function given to ExpandGeneric() to obtain the possible arguments of the
-// ":messages {clear}" command.
-char_u *get_messages_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx)
+/// Function given to ExpandGeneric() to obtain the possible arguments of the
+/// ":messages {clear}" command.
+char *get_messages_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx)
{
if (idx == 0) {
- return (char_u *)"clear";
+ return "clear";
}
return NULL;
}
-char_u *get_mapclear_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx)
+char *get_mapclear_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx)
{
if (idx == 0) {
- return (char_u *)"<buffer>";
+ return "<buffer>";
}
return NULL;
}
@@ -9472,18 +9877,16 @@ static TriState filetype_detect = kNone;
static TriState filetype_plugin = kNone;
static TriState filetype_indent = kNone;
-/*
- * ":filetype [plugin] [indent] {on,off,detect}"
- * on: Load the filetype.vim file to install autocommands for file types.
- * off: Load the ftoff.vim file to remove all autocommands for file types.
- * plugin on: load filetype.vim and ftplugin.vim
- * plugin off: load ftplugof.vim
- * indent on: load filetype.vim and indent.vim
- * indent off: load indoff.vim
- */
+/// ":filetype [plugin] [indent] {on,off,detect}"
+/// on: Load the filetype.vim file to install autocommands for file types.
+/// off: Load the ftoff.vim file to remove all autocommands for file types.
+/// plugin on: load filetype.vim and ftplugin.vim
+/// plugin off: load ftplugof.vim
+/// indent on: load filetype.vim and indent.vim
+/// indent off: load indoff.vim
static void ex_filetype(exarg_T *eap)
{
- char_u *arg = eap->arg;
+ char *arg = eap->arg;
bool plugin = false;
bool indent = false;
@@ -9491,8 +9894,8 @@ static void ex_filetype(exarg_T *eap)
// Print current status.
smsg("filetype detection:%s plugin:%s indent:%s",
filetype_detect == kTrue ? "ON" : "OFF",
- filetype_plugin == kTrue ? (filetype_detect == kTrue ? "ON" : "(on)") : "OFF", // NOLINT(whitespace/line_length)
- filetype_indent == kTrue ? (filetype_detect == kTrue ? "ON" : "(on)") : "OFF"); // NOLINT(whitespace/line_length)
+ filetype_plugin == kTrue ? (filetype_detect == kTrue ? "ON" : "(on)") : "OFF",
+ filetype_indent == kTrue ? (filetype_detect == kTrue ? "ON" : "(on)") : "OFF");
return;
}
@@ -9524,7 +9927,7 @@ static void ex_filetype(exarg_T *eap)
}
}
if (*arg == 'd') {
- (void)do_doautocmd((char_u *)"filetypedetect BufRead", true, NULL);
+ (void)do_doautocmd("filetypedetect BufRead", true, NULL);
do_modelines(0);
}
} else if (STRCMP(arg, "off") == 0) {
@@ -9546,16 +9949,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 overridden) 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;
@@ -9566,17 +9965,29 @@ 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)
{
if (!did_filetype) {
- char_u *arg = eap->arg;
+ char *arg = eap->arg;
if (STRNCMP(arg, "FALLBACK ", 9) == 0) {
arg += 9;
}
- set_option_value("filetype", 0L, (char *)arg, OPT_LOCAL);
+ set_option_value("filetype", 0L, arg, OPT_LOCAL);
if (arg != eap->arg) {
did_filetype = false;
}
@@ -9586,103 +9997,25 @@ static void ex_setfiletype(exarg_T *eap)
static void ex_digraphs(exarg_T *eap)
{
if (*eap->arg != NUL) {
- putdigraph(eap->arg);
+ putdigraph((char_u *)eap->arg);
} else {
listdigraphs(eap->forceit);
}
}
-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;
set_vim_var_nr(VV_HLSEARCH, !no_hlsearch && p_hls);
}
-/*
- * ":nohlsearch"
- */
+/// ":nohlsearch"
static void ex_nohlsearch(exarg_T *eap)
{
set_no_hlsearch(true);
redraw_all_later(SOME_VALID);
}
-// ":[N]match {group} {pattern}"
-// Sets nextcmd to the start of the next command, if any. Also called when
-// skipping commands to find the next command.
-static void ex_match(exarg_T *eap)
-{
- char_u *p;
- char_u *g = NULL;
- char_u *end;
- int c;
- int id;
-
- if (eap->line2 <= 3) {
- id = eap->line2;
- } else {
- emsg(e_invcmd);
- return;
- }
-
- // First clear any old pattern.
- if (!eap->skip) {
- match_delete(curwin, id, false);
- }
-
- if (ends_excmd(*eap->arg)) {
- end = eap->arg;
- } else if ((STRNICMP(eap->arg, "none", 4) == 0
- && (ascii_iswhite(eap->arg[4]) || ends_excmd(eap->arg[4])))) {
- end = eap->arg + 4;
- } else {
- p = skiptowhite(eap->arg);
- if (!eap->skip) {
- g = vim_strnsave(eap->arg, p - eap->arg);
- }
- p = skipwhite(p);
- if (*p == NUL) {
- // There must be two arguments.
- xfree(g);
- semsg(_(e_invarg2), eap->arg);
- return;
- }
- end = skip_regexp(p + 1, *p, true, NULL);
- if (!eap->skip) {
- if (*end != NUL && !ends_excmd(*skipwhite(end + 1))) {
- xfree(g);
- eap->errmsg = e_trailing;
- return;
- }
- if (*end != *p) {
- xfree(g);
- semsg(_(e_invarg2), p);
- return;
- }
-
- c = *end;
- *end = NUL;
- match_add(curwin, (const char *)g, (const char *)p + 1, 10, id,
- NULL, NULL);
- xfree(g);
- *end = c;
- }
- }
- eap->nextcmd = find_nextcmd(end);
-}
-
static void ex_fold(exarg_T *eap)
{
if (foldManualAllowed(true)) {
@@ -9712,8 +10045,8 @@ static void ex_folddo(exarg_T *eap)
ml_clearmarked(); // clear rest of the marks
}
-// Returns true if the supplied Ex cmdidx is for a location list command
-// instead of a quickfix command.
+/// @return true if the supplied Ex cmdidx is for a location list command
+/// instead of a quickfix command.
bool is_loclist_cmd(int cmdidx)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
@@ -9739,7 +10072,7 @@ static void ex_terminal(exarg_T *eap)
char ex_cmd[1024];
if (*eap->arg != NUL) { // Run {cmd} in 'shell'.
- char *name = (char *)vim_strsave_escaped(eap->arg, (char_u *)"\"\\");
+ char *name = (char *)vim_strsave_escaped((char_u *)eap->arg, (char_u *)"\"\\");
snprintf(ex_cmd, sizeof(ex_cmd),
":enew%s | call termopen(\"%s\")",
eap->forceit ? "!" : "", name);
@@ -9770,51 +10103,6 @@ static void ex_terminal(exarg_T *eap)
do_cmdline_cmd(ex_cmd);
}
-/// Checks if `cmd` is "previewable" (i.e. supported by 'inccommand').
-///
-/// @param[in] cmd Commandline to check. May start with a range or modifier.
-///
-/// @return true if `cmd` is previewable
-bool cmd_can_preview(char_u *cmd)
-{
- if (cmd == NULL) {
- return false;
- }
-
- // Ignore additional colons at the start...
- cmd = skip_colon_white(cmd, true);
-
- // Ignore any leading modifiers (:keeppatterns, :verbose, etc.)
- for (int len = modifier_len(cmd); len != 0; len = modifier_len(cmd)) {
- cmd += len;
- cmd = skip_colon_white(cmd, true);
- }
-
- exarg_T ea;
- memset(&ea, 0, sizeof(ea));
- // parse the command line
- ea.cmd = skip_range(cmd, NULL);
- if (*ea.cmd == '*') {
- ea.cmd = skipwhite(ea.cmd + 1);
- }
- char_u *end = find_command(&ea, NULL);
-
- switch (ea.cmdidx) {
- case CMD_substitute:
- case CMD_smagic:
- case CMD_snomagic:
- // Only preview once the pattern delimiter has been typed
- if (*end && !ASCII_ISALNUM(*end)) {
- return true;
- }
- break;
- default:
- break;
- }
-
- return false;
-}
-
/// Gets a map of maps describing user-commands defined for buffer `buf` or
/// defined globally if `buf` is NULL.
///
@@ -9838,6 +10126,8 @@ 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)));
+ PUT(d, "preview", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_PREVIEW)));
switch (cmd->uc_argt & (EX_EXTRA | EX_NOSPC | EX_NEEDARG)) {
case 0:
@@ -9898,9 +10188,9 @@ Dictionary commands_array(buf_T *buf)
return rv;
}
-void verify_command(char_u *cmd)
+void verify_command(char *cmd)
{
- if (strcmp("smile", (char *)cmd)) {
+ if (strcmp("smile", cmd)) {
return; // acceptable non-existing command
}
msg(" #xxn` #xnxx` ,+x@##@Mz;` .xxx"
@@ -10187,3 +10477,9 @@ void verify_command(char_u *cmd)
msg("` `.:.`.,:iii;;;;;;;;iii;;;:` `.`` "
" `nW");
}
+
+/// Get argt of command with id
+uint32_t get_cmd_argt(cmdidx_T cmdidx)
+{
+ return cmdnames[(int)cmdidx].cmd_argt;
+}
diff --git a/src/nvim/ex_docmd.h b/src/nvim/ex_docmd.h
index abf6ec347b..7e0d3016bc 100644
--- a/src/nvim/ex_docmd.h
+++ b/src/nvim/ex_docmd.h
@@ -1,8 +1,8 @@
#ifndef NVIM_EX_DOCMD_H
#define NVIM_EX_DOCMD_H
-#include "nvim/ex_cmds_defs.h"
#include "nvim/eval/funcs.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/globals.h"
// flags for do_cmdline()
@@ -12,12 +12,14 @@
#define DOCMD_KEYTYPED 0x08 // don't reset KeyTyped
#define DOCMD_EXCRESET 0x10 // reset exception environment (for debugging
#define DOCMD_KEEPLINE 0x20 // keep typed line for repeating with "."
-#define DOCMD_PREVIEW 0x40 // during 'inccommand' preview
// defines for eval_vars()
#define VALID_PATH 1
#define VALID_HEAD 2
+// Whether a command index indicates a user command.
+#define IS_USER_CMDIDX(idx) ((int)(idx) < 0)
+
// Structure used to save the current state. Used when executing Normal mode
// commands while in any other mode.
typedef struct {
@@ -25,10 +27,10 @@ typedef struct {
int save_restart_edit;
bool save_msg_didout;
int save_State;
- int save_insertmode;
bool save_finish_op;
long save_opcount;
int save_reg_executing;
+ bool save_pending_end_reg_executing;
tasave_T tabuf;
} save_state_T;
@@ -42,6 +44,7 @@ typedef struct ucmd {
sctx_T uc_script_ctx; // SCTX where the command was defined
char_u *uc_compl_arg; // completion argument if any
LuaRef uc_compl_luaref; // Reference to Lua completion function
+ LuaRef uc_preview_luaref; // Reference to Lua preview function
LuaRef uc_luaref; // Reference to Lua function
} ucmd_T;
diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c
index b1c59a607c..46b9528546 100644
--- a/src/nvim/ex_eval.c
+++ b/src/nvim/ex_eval.c
@@ -75,7 +75,10 @@
|| (cstack->cs_idx > 0 \
&& !(cstack->cs_flags[cstack->cs_idx - 1] & CSF_ACTIVE)))
-#define discard_pending_return(p) tv_free((typval_T *)(p))
+static void discard_pending_return(typval_T *p)
+{
+ tv_free(p);
+}
/*
* When several errors appear in a row, setting "force_abort" is delayed until
@@ -88,27 +91,26 @@
*/
static int cause_abort = FALSE;
-// Return true when immediately aborting on error, or when an interrupt
-// occurred or an exception was thrown but not caught. Use for ":{range}call"
-// to check whether an aborted function that does not handle a range itself
-// should be called again for the next line in the range. Also used for
-// cancelling expression evaluation after a function call caused an immediate
-// abort. Note that the first emsg() call temporarily resets "force_abort"
-// until the throw point for error messages has been reached. That is, during
-// cancellation of an expression evaluation after an aborting function call or
-// due to a parsing error, aborting() always returns the same value.
-// "got_int" is also set by calling interrupt().
+/// @return true when immediately aborting on error, or when an interrupt
+/// occurred or an exception was thrown but not caught.
+///
+/// Use for ":{range}call" to check whether an aborted function that does not
+/// handle a range itself should be called again for the next line in the range.
+/// Also used for cancelling expression evaluation after a function call caused
+/// an immediate abort. Note that the first emsg() call temporarily resets
+/// "force_abort" until the throw point for error messages has been reached.
+/// That is, during cancellation of an expression evaluation after an aborting
+/// function call or due to a parsing error, aborting() always returns the same
+/// value. "got_int" is also set by calling interrupt().
int aborting(void)
{
return (did_emsg && force_abort) || got_int || current_exception;
}
-/*
- * The value of "force_abort" is temporarily reset by the first emsg() call
- * during an expression evaluation, and "cause_abort" is used instead. It might
- * be necessary to restore "force_abort" even before the throw point for the
- * error message has been reached. update_force_abort() should be called then.
- */
+/// The value of "force_abort" is temporarily reset by the first emsg() call
+/// during an expression evaluation, and "cause_abort" is used instead. It might
+/// be necessary to restore "force_abort" even before the throw point for the
+/// error message has been reached. update_force_abort() should be called then.
void update_force_abort(void)
{
if (cause_abort) {
@@ -116,38 +118,37 @@ void update_force_abort(void)
}
}
-/*
- * Return TRUE if a command with a subcommand resulting in "retcode" should
- * abort the script processing. Can be used to suppress an autocommand after
- * execution of a failing subcommand as long as the error message has not been
- * displayed and actually caused the abortion.
- */
+/// @return TRUE if a command with a subcommand resulting in "retcode" should
+/// abort the script processing. Can be used to suppress an autocommand after
+/// execution of a failing subcommand as long as the error message has not been
+/// displayed and actually caused the abortion.
int should_abort(int retcode)
{
return (retcode == FAIL && trylevel != 0 && !emsg_silent) || aborting();
}
-/*
- * Return TRUE if a function with the "abort" flag should not be considered
- * ended on an error. This means that parsing commands is continued in order
- * to find finally clauses to be executed, and that some errors in skipped
- * commands are still reported.
- */
+/// @return TRUE if a function with the "abort" flag should not be considered
+/// ended on an error. This means that parsing commands is continued in order
+/// to find finally clauses to be executed, and that some errors in skipped
+/// commands are still reported.
int aborted_in_try(void)
+ FUNC_ATTR_PURE
{
// This function is only called after an error. In this case, "force_abort"
// determines whether searching for finally clauses is necessary.
return force_abort;
}
-// cause_errthrow(): Cause a throw of an error exception if appropriate.
-// Return true if the error message should not be displayed by emsg().
-// Sets "ignore", if the emsg() call should be ignored completely.
-//
-// When several messages appear in the same command, the first is usually the
-// most specific one and used as the exception value. The "severe" flag can be
-// set to true, if a later but severer message should be used instead.
-bool cause_errthrow(const char_u *mesg, bool severe, bool *ignore)
+/// cause_errthrow(): Cause a throw of an error exception if appropriate.
+///
+/// @return true if the error message should not be displayed by emsg().
+///
+/// Sets "ignore", if the emsg() call should be ignored completely.
+///
+/// When several messages appear in the same command, the first is usually the
+/// most specific one and used as the exception value. The "severe" flag can be
+/// set to true, if a later but severer message should be used instead.
+bool cause_errthrow(const char *mesg, bool severe, bool *ignore)
FUNC_ATTR_NONNULL_ALL
{
struct msglist *elem;
@@ -196,7 +197,7 @@ bool cause_errthrow(const char_u *mesg, bool severe, bool *ignore)
* interrupt exception is catchable by the innermost try conditional and
* not replaced by an interrupt message error exception.
*/
- if (mesg == (char_u *)_(e_interr)) {
+ if (mesg == _(e_interr)) {
*ignore = true;
return true;
}
@@ -254,7 +255,7 @@ bool cause_errthrow(const char_u *mesg, bool severe, bool *ignore)
}
elem = xmalloc(sizeof(struct msglist));
- elem->msg = (char *)vim_strsave(mesg);
+ elem->msg = xstrdup(mesg);
elem->next = NULL;
elem->throw_msg = NULL;
*plist = elem;
@@ -279,9 +280,7 @@ bool cause_errthrow(const char_u *mesg, bool severe, bool *ignore)
}
}
-/*
- * Free a "msg_list" and the messages it contains.
- */
+/// Free a "msg_list" and the messages it contains.
static void free_msglist(struct msglist *l)
{
struct msglist *messages, *next;
@@ -295,22 +294,18 @@ static void free_msglist(struct msglist *l)
}
}
-/*
- * Free global "*msg_list" and the messages it contains, then set "*msg_list"
- * to NULL.
- */
+/// Free global "*msg_list" and the messages it contains, then set "*msg_list"
+/// to NULL.
void free_global_msglist(void)
{
free_msglist(*msg_list);
*msg_list = NULL;
}
-/*
- * Throw the message specified in the call to cause_errthrow() above as an
- * error exception. If cstack is NULL, postpone the throw until do_cmdline()
- * has returned (see do_one_cmd()).
- */
-void do_errthrow(cstack_T *cstack, char_u *cmdname)
+/// Throw the message specified in the call to cause_errthrow() above as an
+/// error exception. If cstack is NULL, postpone the throw until do_cmdline()
+/// has returned (see do_one_cmd()).
+void do_errthrow(cstack_T *cstack, char *cmdname)
{
/*
* Ensure that all commands in nested function calls and sourced files
@@ -339,11 +334,11 @@ void do_errthrow(cstack_T *cstack, char_u *cmdname)
*msg_list = NULL;
}
-/*
- * do_intthrow(): Replace the current exception by an interrupt or interrupt
- * exception if appropriate. Return TRUE if the current exception is discarded,
- * FALSE otherwise.
- */
+/// do_intthrow(): Replace the current exception by an interrupt or interrupt
+/// exception if appropriate.
+///
+/// @return TRUE if the current exception is discarded or,
+/// FALSE otherwise.
int do_intthrow(cstack_T *cstack)
{
// If no interrupt occurred or no try conditional is active and no exception
@@ -386,8 +381,8 @@ int do_intthrow(cstack_T *cstack)
return true;
}
-// Get an exception message that is to be stored in current_exception->value.
-char *get_exception_string(void *value, except_type_T type, char_u *cmdname, int *should_free)
+/// Get an exception message that is to be stored in current_exception->value.
+char *get_exception_string(void *value, except_type_T type, char *cmdname, int *should_free)
{
char *ret, *mesg;
char *p, *val;
@@ -397,12 +392,12 @@ char *get_exception_string(void *value, except_type_T type, char_u *cmdname, int
mesg = ((struct msglist *)value)->throw_msg;
if (cmdname != NULL && *cmdname != NUL) {
size_t cmdlen = STRLEN(cmdname);
- ret = (char *)vim_strnsave((char_u *)"Vim(", 4 + cmdlen + 2 + STRLEN(mesg));
+ ret = xstrnsave("Vim(", 4 + cmdlen + 2 + STRLEN(mesg));
STRCPY(&ret[4], cmdname);
STRCPY(&ret[4 + cmdlen], "):");
val = ret + 4 + cmdlen + 2;
} else {
- ret = (char *)vim_strnsave((char_u *)"Vim:", 4 + STRLEN(mesg));
+ ret = xstrnsave("Vim:", 4 + STRLEN(mesg));
val = ret + 4;
}
@@ -422,7 +417,7 @@ char *get_exception_string(void *value, except_type_T type, char_u *cmdname, int
STRCAT(val, mesg); // 'E123' missing or at beginning
} else {
// '"filename" E123: message text'
- if (mesg[0] != '"' || p-2 < &mesg[1]
+ if (mesg[0] != '"' || p - 2 < &mesg[1]
|| p[-2] != '"' || p[-1] != ' ') {
// "E123:" is part of the file name.
continue;
@@ -430,7 +425,7 @@ char *get_exception_string(void *value, except_type_T type, char_u *cmdname, int
STRCAT(val, p);
p[-2] = NUL;
- sprintf((char *)(val + STRLEN(p)), " (%s)", &mesg[1]);
+ snprintf(val + STRLEN(p), strlen(" (%s)"), " (%s)", &mesg[1]);
p[-2] = '"';
}
break;
@@ -444,12 +439,13 @@ char *get_exception_string(void *value, except_type_T type, char_u *cmdname, int
return ret;
}
-
-// Throw a new exception. Return FAIL when out of memory or it was tried to
-// throw an illegal user exception. "value" is the exception string for a
-// user or interrupt exception, or points to a message list in case of an
-// error exception.
-static int throw_exception(void *value, except_type_T type, char_u *cmdname)
+/// Throw a new exception. "value" is the exception string for a
+/// user or interrupt exception, or points to a message list in case of an
+/// error exception.
+///
+/// @return FAIL when out of memory or it was tried to throw an illegal user
+/// exception.
+static int throw_exception(void *value, except_type_T type, char *cmdname)
{
except_T *excp;
int should_free;
@@ -482,8 +478,7 @@ static int throw_exception(void *value, except_type_T type, char_u *cmdname)
}
excp->type = type;
- excp->throw_name = vim_strsave(sourcing_name == NULL
- ? (char_u *)"" : sourcing_name);
+ excp->throw_name = xstrdup(sourcing_name == NULL ? "" : sourcing_name);
excp->throw_lnum = sourcing_lnum;
if (p_verbose >= 13 || debug_break_level > 0) {
@@ -524,13 +519,11 @@ fail:
return FAIL;
}
-/*
- * Discard an exception. "was_finished" is set when the exception has been
- * caught and the catch clause has been ended normally.
- */
+/// Discard an exception. "was_finished" is set when the exception has been
+/// caught and the catch clause has been ended normally.
static void discard_exception(except_T *excp, bool was_finished)
{
- char_u *saved_IObuff;
+ char *saved_IObuff;
if (current_exception == excp) {
current_exception = NULL;
@@ -543,7 +536,7 @@ static void discard_exception(except_T *excp, bool was_finished)
if (p_verbose >= 13 || debug_break_level > 0) {
int save_msg_silent = msg_silent;
- saved_IObuff = vim_strsave(IObuff);
+ saved_IObuff = (char *)vim_strsave(IObuff);
if (debug_break_level > 0) {
msg_silent = FALSE; // display messages
} else {
@@ -579,9 +572,7 @@ static void discard_exception(except_T *excp, bool was_finished)
xfree(excp);
}
-/*
- * Discard the exception currently being thrown.
- */
+/// Discard the exception currently being thrown.
void discard_current_exception(void)
{
if (current_exception != NULL) {
@@ -592,14 +583,12 @@ void discard_current_exception(void)
need_rethrow = false;
}
-/*
- * Put an exception on the caught stack.
- */
+/// Put an exception on the caught stack.
static void catch_exception(except_T *excp)
{
excp->caught = caught_stack;
caught_stack = excp;
- set_vim_var_string(VV_EXCEPTION, (char *)excp->value, -1);
+ set_vim_var_string(VV_EXCEPTION, excp->value, -1);
if (*excp->throw_name != NUL) {
if (excp->throw_lnum != 0) {
vim_snprintf((char *)IObuff, IOSIZE, _("%s, line %" PRId64),
@@ -640,9 +629,7 @@ static void catch_exception(except_T *excp)
}
}
-/*
- * Remove an exception from the caught stack.
- */
+/// Remove an exception from the caught stack.
static void finish_exception(except_T *excp)
{
if (excp != caught_stack) {
@@ -650,7 +637,7 @@ static void finish_exception(except_T *excp)
}
caught_stack = caught_stack->caught;
if (caught_stack != NULL) {
- set_vim_var_string(VV_EXCEPTION, (char *)caught_stack->value, -1);
+ set_vim_var_string(VV_EXCEPTION, caught_stack->value, -1);
if (*caught_stack->throw_name != NUL) {
if (caught_stack->throw_lnum != 0) {
vim_snprintf((char *)IObuff, IOSIZE,
@@ -682,13 +669,11 @@ static void finish_exception(except_T *excp)
#define RP_RESUME 1
#define RP_DISCARD 2
-/*
- * Report information about something pending in a finally clause if required by
- * the 'verbose' option or when debugging. "action" tells whether something is
- * made pending or something pending is resumed or discarded. "pending" tells
- * what is pending. "value" specifies the return value for a pending ":return"
- * or the exception value for a pending exception.
- */
+/// Report information about something pending in a finally clause if required by
+/// the 'verbose' option or when debugging. "action" tells whether something is
+/// made pending or something pending is resumed or discarded. "pending" tells
+/// what is pending. "value" specifies the return value for a pending ":return"
+/// or the exception value for a pending exception.
static void report_pending(int action, int pending, void *value)
{
char *mesg;
@@ -733,7 +718,7 @@ static void report_pending(int action, int pending, void *value)
vim_snprintf((char *)IObuff, IOSIZE,
mesg, _("Exception"));
mesg = (char *)concat_str(IObuff, (char_u *)": %s");
- s = (char *)((except_T *)value)->value;
+ s = ((except_T *)value)->value;
} else if ((pending & CSTP_ERROR) && (pending & CSTP_INTERRUPT)) {
s = _("Error and interrupt");
} else if (pending & CSTP_ERROR) {
@@ -764,10 +749,8 @@ static void report_pending(int action, int pending, void *value)
}
}
-/*
- * If something is made pending in a finally clause, report it if required by
- * the 'verbose' option or when debugging.
- */
+/// If something is made pending in a finally clause, report it if required by
+/// the 'verbose' option or when debugging.
void report_make_pending(int pending, void *value)
{
if (p_verbose >= 14 || debug_break_level > 0) {
@@ -781,10 +764,8 @@ void report_make_pending(int pending, void *value)
}
}
-/*
- * If something pending in a finally clause is resumed at the ":endtry", report
- * it if required by the 'verbose' option or when debugging.
- */
+/// If something pending in a finally clause is resumed at the ":endtry", report
+/// it if required by the 'verbose' option or when debugging.
void report_resume_pending(int pending, void *value)
{
if (p_verbose >= 14 || debug_break_level > 0) {
@@ -798,10 +779,8 @@ void report_resume_pending(int pending, void *value)
}
}
-/*
- * If something pending in a finally clause is discarded, report it if required
- * by the 'verbose' option or when debugging.
- */
+/// If something pending in a finally clause is discarded, report it if required
+/// by the 'verbose' option or when debugging.
void report_discard_pending(int pending, void *value)
{
if (p_verbose >= 14 || debug_break_level > 0) {
@@ -815,7 +794,7 @@ void report_discard_pending(int pending, void *value)
}
}
-// ":eval".
+/// Handle ":eval".
void ex_eval(exarg_T *eap)
{
typval_T tv;
@@ -825,9 +804,7 @@ void ex_eval(exarg_T *eap)
}
}
-/*
- * ":if".
- */
+/// Handle ":if".
void ex_if(exarg_T *eap)
{
int skip;
@@ -856,9 +833,7 @@ void ex_if(exarg_T *eap)
}
}
-/*
- * ":endif".
- */
+/// Handle ":endif".
void ex_endif(exarg_T *eap)
{
did_endif = true;
@@ -883,9 +858,7 @@ void ex_endif(exarg_T *eap)
}
}
-/*
- * ":else" and ":elseif".
- */
+/// Handle ":else" and ":elseif".
void ex_else(exarg_T *eap)
{
int result;
@@ -958,9 +931,7 @@ void ex_else(exarg_T *eap)
}
}
-/*
- * Handle ":while" and ":for".
- */
+/// Handle ":while" and ":for".
void ex_while(exarg_T *eap)
{
bool error;
@@ -1041,9 +1012,7 @@ void ex_while(exarg_T *eap)
}
}
-/*
- * ":continue"
- */
+/// Handle ":continue"
void ex_continue(exarg_T *eap)
{
int idx;
@@ -1075,9 +1044,7 @@ void ex_continue(exarg_T *eap)
}
}
-/*
- * ":break"
- */
+/// Handle ":break"
void ex_break(exarg_T *eap)
{
int idx;
@@ -1098,9 +1065,7 @@ void ex_break(exarg_T *eap)
}
}
-/*
- * ":endwhile" and ":endfor"
- */
+/// Handle ":endwhile" and ":endfor"
void ex_endwhile(exarg_T *eap)
{
cstack_T *const cstack = eap->cstack;
@@ -1120,7 +1085,7 @@ void ex_endwhile(exarg_T *eap)
if (cstack->cs_looplevel <= 0 || cstack->cs_idx < 0) {
eap->errmsg = err;
} else {
- fl = cstack->cs_flags[cstack->cs_idx];
+ fl = cstack->cs_flags[cstack->cs_idx];
if (!(fl & csf)) {
// If we are in a ":while" or ":for" but used the wrong endloop
// command, do not rewind to the next enclosing ":for"/":while".
@@ -1174,10 +1139,7 @@ void ex_endwhile(exarg_T *eap)
}
}
-
-/*
- * ":throw expr"
- */
+/// Handle ":throw expr"
void ex_throw(exarg_T *eap)
{
const char *arg = (const char *)eap->arg;
@@ -1202,11 +1164,9 @@ void ex_throw(exarg_T *eap)
}
}
-/*
- * Throw the current exception through the specified cstack. Common routine
- * for ":throw" (user exception) and error and interrupt exceptions. Also
- * used for rethrowing an uncaught exception.
- */
+/// Throw the current exception through the specified cstack. Common routine
+/// for ":throw" (user exception) and error and interrupt exceptions. Also
+/// used for rethrowing an uncaught exception.
void do_throw(cstack_T *cstack)
{
int idx;
@@ -1263,9 +1223,7 @@ void do_throw(cstack_T *cstack)
}
}
-/*
- * ":try"
- */
+/// Handle ":try"
void ex_try(exarg_T *eap)
{
int skip;
@@ -1315,22 +1273,20 @@ void ex_try(exarg_T *eap)
}
}
-/*
- * ":catch /{pattern}/" and ":catch"
- */
+/// Handle ":catch /{pattern}/" and ":catch"
void ex_catch(exarg_T *eap)
{
int idx = 0;
bool give_up = false;
bool skip = false;
bool caught = false;
- char_u *end;
- char_u save_char = 0;
- char_u *save_cpo;
+ char *end;
+ char save_char = 0;
+ char *save_cpo;
regmatch_T regmatch;
int prev_got_int;
cstack_T *const cstack = eap->cstack;
- char_u *pat;
+ char *pat;
if (cstack->cs_trylevel <= 0 || cstack->cs_idx < 0) {
eap->errmsg = N_("E603: :catch without :try");
@@ -1359,12 +1315,12 @@ void ex_catch(exarg_T *eap)
}
if (ends_excmd(*eap->arg)) { // no argument, catch all errors
- pat = (char_u *)".*";
+ pat = ".*";
end = NULL;
- eap->nextcmd = find_nextcmd(eap->arg);
+ eap->nextcmd = (char *)find_nextcmd((char_u *)eap->arg);
} else {
pat = eap->arg + 1;
- end = skip_regexp(pat, *eap->arg, TRUE, NULL);
+ end = (char *)skip_regexp((char_u *)pat, *eap->arg, true, NULL);
}
if (!give_up) {
@@ -1403,8 +1359,8 @@ void ex_catch(exarg_T *eap)
save_char = *end;
*end = NUL;
}
- save_cpo = p_cpo;
- p_cpo = (char_u *)"";
+ save_cpo = p_cpo;
+ p_cpo = "";
// Disable error messages, it will make current exception
// invalid
emsg_off++;
@@ -1467,13 +1423,11 @@ void ex_catch(exarg_T *eap)
}
if (end != NULL) {
- eap->nextcmd = find_nextcmd(end);
+ eap->nextcmd = (char *)find_nextcmd((char_u *)end);
}
}
-/*
- * ":finally"
- */
+/// Handle ":finally"
void ex_finally(exarg_T *eap)
{
int idx;
@@ -1595,14 +1549,12 @@ void ex_finally(exarg_T *eap)
}
}
-/*
- * ":endtry"
- */
+/// Handle ":endtry"
void ex_endtry(exarg_T *eap)
{
int idx;
bool rethrow = false;
- int pending = CSTP_NONE;
+ char pending = CSTP_NONE;
void *rettv = NULL;
cstack_T *const cstack = eap->cstack;
@@ -1620,10 +1572,11 @@ void ex_endtry(exarg_T *eap)
// the finally clause. The latter case need not be tested since then
// anything pending has already been discarded.
bool skip = did_emsg || got_int || current_exception
- || !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE);
+ || !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE);
if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) {
eap->errmsg = get_end_emsg(cstack);
+
// Find the matching ":try" and report what's missing.
idx = cstack->cs_idx;
do {
@@ -1643,6 +1596,9 @@ void ex_endtry(exarg_T *eap)
if (current_exception) {
discard_current_exception();
}
+
+ // report eap->errmsg, also when there already was an error
+ did_emsg = false;
} else {
idx = cstack->cs_idx;
@@ -1713,8 +1669,10 @@ void ex_endtry(exarg_T *eap)
*/
(void)cleanup_conditionals(cstack, CSF_TRY | CSF_SILENT, TRUE);
- --cstack->cs_idx;
- --cstack->cs_trylevel;
+ if (cstack->cs_idx >= 0 && (cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) {
+ cstack->cs_idx--;
+ }
+ cstack->cs_trylevel--;
if (!skip) {
report_resume_pending(pending,
@@ -1784,14 +1742,12 @@ void ex_endtry(exarg_T *eap)
* error/interrupt/exception state.
*/
-/*
- * This function works a bit like ex_finally() except that there was not
- * actually an extra try block around the part that failed and an error or
- * interrupt has not (yet) been converted to an exception. This function
- * saves the error/interrupt/ exception state and prepares for the call to
- * do_cmdline() that is going to be made for the cleanup autocommand
- * execution.
- */
+/// This function works a bit like ex_finally() except that there was not
+/// actually an extra try block around the part that failed and an error or
+/// interrupt has not (yet) been converted to an exception. This function
+/// saves the error/interrupt/ exception state and prepares for the call to
+/// do_cmdline() that is going to be made for the cleanup autocommand
+/// execution.
void enter_cleanup(cleanup_T *csp)
{
int pending = CSTP_NONE;
@@ -1834,21 +1790,19 @@ void enter_cleanup(cleanup_T *csp)
}
}
-/*
- * See comment above enter_cleanup() for how this function is used.
- *
- * This function is a bit like ex_endtry() except that there was not actually
- * an extra try block around the part that failed and an error or interrupt
- * had not (yet) been converted to an exception when the cleanup autocommand
- * sequence was invoked.
- *
- * This function has to be called with the address of the cleanup_T structure
- * filled by enter_cleanup() as an argument; it restores the error/interrupt/
- * exception state saved by that function - except there was an aborting
- * error, an interrupt or an uncaught exception during execution of the
- * cleanup autocommands. In the latter case, the saved error/interrupt/
- * exception state is discarded.
- */
+/// This function is a bit like ex_endtry() except that there was not actually
+/// an extra try block around the part that failed and an error or interrupt
+/// had not (yet) been converted to an exception when the cleanup autocommand
+/// sequence was invoked.
+///
+/// See comment above enter_cleanup() for how this function is used.
+///
+/// This function has to be called with the address of the cleanup_T structure
+/// filled by enter_cleanup() as an argument; it restores the error/interrupt/
+/// exception state saved by that function - except there was an aborting
+/// error, an interrupt or an uncaught exception during execution of the
+/// cleanup autocommands. In the latter case, the saved error/interrupt/
+/// exception state is discarded.
void leave_cleanup(cleanup_T *csp)
{
int pending = csp->pending;
@@ -1912,23 +1866,25 @@ void leave_cleanup(cleanup_T *csp)
}
}
-
-/*
- * Make conditionals inactive and discard what's pending in finally clauses
- * until the conditional type searched for or a try conditional not in its
- * finally clause is reached. If this is in an active catch clause, finish
- * the caught exception.
- * Return the cstack index where the search stopped.
- * Values used for "searched_cond" are (CSF_WHILE | CSF_FOR) or CSF_TRY or 0,
- * the latter meaning the innermost try conditional not in its finally clause.
- * "inclusive" tells whether the conditional searched for should be made
- * inactive itself (a try conditional not in its finally clause possibly find
- * before is always made inactive). If "inclusive" is TRUE and
- * "searched_cond" is CSF_TRY|CSF_SILENT, the saved former value of
- * "emsg_silent", if reset when the try conditional finally reached was
- * entered, is restored (used by ex_endtry()). This is normally done only
- * when such a try conditional is left.
- */
+/// Make conditionals inactive and discard what's pending in finally clauses
+/// until the conditional type searched for or a try conditional not in its
+/// finally clause is reached. If this is in an active catch clause, finish
+/// the caught exception.
+///
+///
+/// @param searched_cond Possible values are (CSF_WHILE | CSF_FOR) or CSF_TRY or 0,
+/// the latter meaning the innermost try conditional not
+/// in its finally clause.
+/// @param inclusive tells whether the conditional searched for should be made
+/// inactive itself (a try conditional not in its finally
+/// clause possibly find before is always made inactive).
+///
+/// If "inclusive" is TRUE and "searched_cond" is CSF_TRY|CSF_SILENT, the saved
+/// former value of "emsg_silent", if reset when the try conditional finally
+/// reached was entered, is restored (used by ex_endtry()). This is normally
+/// done only when such a try conditional is left.
+///
+/// @return the cstack index where the search stopped.
int cleanup_conditionals(cstack_T *cstack, int searched_cond, int inclusive)
{
int idx;
@@ -1963,7 +1919,7 @@ int cleanup_conditionals(cstack_T *cstack, int searched_cond, int inclusive)
default:
if (cstack->cs_flags[idx] & CSF_FINALLY) {
- if (cstack->cs_pending[idx] & CSTP_THROW) {
+ if ((cstack->cs_pending[idx] & CSTP_THROW) && cstack->cs_exception[idx] != NULL) {
// Cancel the pending exception. This is in the
// finally clause, so that the stack of the
// caught exceptions is not involved.
@@ -1984,8 +1940,9 @@ int cleanup_conditionals(cstack_T *cstack, int searched_cond, int inclusive)
*/
if (!(cstack->cs_flags[idx] & CSF_FINALLY)) {
if ((cstack->cs_flags[idx] & CSF_ACTIVE)
- && (cstack->cs_flags[idx] & CSF_CAUGHT)) {
+ && (cstack->cs_flags[idx] & CSF_CAUGHT) && !(cstack->cs_flags[idx] & CSF_FINISHED)) {
finish_exception((except_T *)cstack->cs_exception[idx]);
+ cstack->cs_flags[idx] |= CSF_FINISHED;
}
// Stop at this try conditional - except the try block never
// got active (because of an inactive surrounding conditional
@@ -2037,9 +1994,7 @@ int cleanup_conditionals(cstack_T *cstack, int searched_cond, int inclusive)
return idx;
}
-/*
- * Return an appropriate error message for a missing endwhile/endfor/endif.
- */
+/// @return an appropriate error message for a missing endwhile/endfor/endif.
static char *get_end_emsg(cstack_T *cstack)
{
if (cstack->cs_flags[cstack->cs_idx] & CSF_WHILE) {
@@ -2051,14 +2006,11 @@ static char *get_end_emsg(cstack_T *cstack)
return e_endif;
}
-
-/*
- * Rewind conditionals until index "idx" is reached. "cond_type" and
- * "cond_level" specify a conditional type and the address of a level variable
- * which is to be decremented with each skipped conditional of the specified
- * type.
- * Also free "for info" structures where needed.
- */
+/// Rewind conditionals until index "idx" is reached. "cond_type" and
+/// "cond_level" specify a conditional type and the address of a level variable
+/// which is to be decremented with each skipped conditional of the specified
+/// type.
+/// Also free "for info" structures where needed.
void rewind_conditionals(cstack_T *cstack, int idx, int cond_type, int *cond_level)
{
while (cstack->cs_idx > idx) {
@@ -2072,18 +2024,14 @@ void rewind_conditionals(cstack_T *cstack, int idx, int cond_type, int *cond_lev
}
}
-/*
- * ":endfunction" when not after a ":function"
- */
+/// Handle ":endfunction" when not after a ":function"
void ex_endfunction(exarg_T *eap)
{
emsg(_("E193: :endfunction not inside a function"));
}
-/*
- * Return TRUE if the string "p" looks like a ":while" or ":for" command.
- */
-int has_loop_cmd(char_u *p)
+/// @return TRUE if the string "p" looks like a ":while" or ":for" command.
+int has_loop_cmd(char *p)
{
int len;
@@ -2104,4 +2052,3 @@ int has_loop_cmd(char_u *p)
}
return FALSE;
}
-
diff --git a/src/nvim/ex_eval.h b/src/nvim/ex_eval.h
index 15da4b2d60..235875fb91 100644
--- a/src/nvim/ex_eval.h
+++ b/src/nvim/ex_eval.h
@@ -14,9 +14,10 @@
#define CSF_TRY 0x0100 // is a ":try"
#define CSF_FINALLY 0x0200 // ":finally" has been passed
-#define CSF_THROWN 0x0400 // exception thrown to this try conditional
-#define CSF_CAUGHT 0x0800 // exception caught by this try conditional
-#define CSF_SILENT 0x1000 // "emsg_silent" reset by ":try"
+#define CSF_THROWN 0x0800 // exception thrown to this try conditional
+#define CSF_CAUGHT 0x1000 // exception caught by this try conditional
+#define CSF_FINISHED 0x2000 // CSF_CAUGHT was handled by finish_exception()
+#define CSF_SILENT 0x4000 // "emsg_silent" reset by ":try"
// Note that CSF_ELSE is only used when CSF_TRY and CSF_WHILE are unset
// (an ":if"), and CSF_SILENT is only used when CSF_TRY is set.
@@ -46,8 +47,7 @@ struct msglist {
};
// The exception types.
-typedef enum
-{
+typedef enum {
ET_USER, // exception caused by ":throw" command
ET_ERROR, // error exception
ET_INTERRUPT, // interrupt exception triggered by Ctrl-C
@@ -62,7 +62,7 @@ struct vim_exception {
except_type_T type; // exception type
char *value; // exception value
struct msglist *messages; // message(s) causing error exception
- char_u *throw_name; // name of the throw point
+ char *throw_name; // name of the throw point
linenr_T throw_lnum; // line number of the throw point
except_T *caught; // next exception on the caught stack
};
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 78b8e43e65..4c26cfe500 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -11,7 +11,9 @@
#include <stdlib.h>
#include <string.h>
+#include "nvim/api/extmark.h"
#include "nvim/api/private/helpers.h"
+#include "nvim/api/vim.h"
#include "nvim/arabic.h"
#include "nvim/ascii.h"
#include "nvim/assert.h"
@@ -33,15 +35,18 @@
#include "nvim/func_attr.h"
#include "nvim/garray.h"
#include "nvim/getchar.h"
+#include "nvim/globals.h"
#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
+#include "nvim/highlight_group.h"
#include "nvim/if_cscope.h"
#include "nvim/indent.h"
-#include "nvim/keymap.h"
+#include "nvim/keycodes.h"
#include "nvim/lib/kvec.h"
#include "nvim/log.h"
#include "nvim/lua/executor.h"
#include "nvim/main.h"
+#include "nvim/mapping.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
@@ -67,6 +72,7 @@
#include "nvim/syntax.h"
#include "nvim/tag.h"
#include "nvim/ui.h"
+#include "nvim/undo.h"
#include "nvim/vim.h"
#include "nvim/viml/parser/expressions.h"
#include "nvim/viml/parser/parser.h"
@@ -103,11 +109,9 @@ typedef enum {
kCmdRedrawAll,
} CmdRedraw;
-/*
- * Variables shared between getcmdline(), redrawcmdline() and others.
- * These need to be saved when using CTRL-R |, that's why they are in a
- * structure.
- */
+// Variables shared between getcmdline(), redrawcmdline() and others.
+// These need to be saved when using CTRL-R |, that's why they are in a
+// structure.
struct cmdline_info {
char_u *cmdbuff; // pointer to command line buffer
int cmdbufflen; // length of cmdbuff
@@ -134,6 +138,7 @@ struct cmdline_info {
bool special_shift; ///< shift of last putcmdline char
CmdRedraw redraw_state; ///< needed redraw for external cmdline
};
+
/// Last value of prompt_id, incremented when doing new prompt
static unsigned last_prompt_id = 0;
@@ -191,13 +196,12 @@ typedef struct command_line_state {
typedef struct cmdline_info CmdlineInfo;
-/* The current cmdline_info. It is initialized in getcmdline() and after that
- * used by other functions. When invoking getcmdline() recursively it needs
- * to be saved with save_cmdline() and restored with restore_cmdline().
- * TODO: make it local to getcmdline() and pass it around. */
+/// The current cmdline_info. It is initialized in getcmdline() and after that
+/// used by other functions. When invoking getcmdline() recursively it needs
+/// to be saved with save_cmdline() and restored with restore_cmdline().
static struct cmdline_info ccline;
-static int cmd_showtail; // Only show path tail in lists ?
+static int cmd_showtail; // Only show path tail in lists ?
static int new_cmdpos; // position set by set_cmdline_pos()
@@ -229,27 +233,13 @@ static int compl_match_arraysize;
static int compl_startcol;
static int compl_selected;
-/// |:checkhealth| completion items
-///
-/// Regenerates on every new command line prompt, to accomodate changes on the
-/// runtime files.
-typedef struct {
- garray_T names; // healthcheck names
- unsigned last_gen; // last_prompt_id where names were generated
-} CheckhealthComp;
-
-/// Cookie used when converting filepath to name
-struct healthchecks_cookie {
- garray_T *names; // global healthchecks
- bool is_lua; // true if the current entry is a Lua healthcheck
-};
-
-static CheckhealthComp healthchecks = { GA_INIT(sizeof(char_u *), 10), 0 };
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ex_getln.c.generated.h"
#endif
+static handle_T cmdpreview_bufnr = 0;
+static long cmdpreview_ns = 0;
+
static int cmd_hkmap = 0; // Hebrew mapping during command line
static void save_viewstate(viewstate_T *vs)
@@ -292,61 +282,25 @@ static void init_incsearch_state(incsearch_state_T *s)
/// Given to ExpandGeneric() to obtain all available heathcheck names.
/// @param[in] idx Index of the healthcheck item.
/// @param[in] xp Not used.
-static char_u *get_healthcheck_names(expand_T *xp, int idx)
-{
- // Generate the first time or on new prompt.
- if (healthchecks.last_gen == 0 || healthchecks.last_gen != last_prompt_id) {
- ga_clear_strings(&healthchecks.names);
- char *patterns[3] = { "autoload/health/**.vim", "lua/**/**/health/init.lua", // NOLINT
- "lua/**/**/health.lua" }; // NOLINT
- for (int i = 0; i < 3; i++) {
- struct healthchecks_cookie hcookie = { .names = &healthchecks.names, .is_lua = i != 0 };
- do_in_runtimepath((char_u *)patterns[i], DIP_ALL, get_healthcheck_cb, &hcookie);
-
- if (healthchecks.names.ga_len > 0) {
- ga_remove_duplicate_strings(&healthchecks.names);
- }
- }
- // Tracked to regenerate items on next prompt.
- healthchecks.last_gen = last_prompt_id;
- }
- return idx <
- (int)healthchecks.names.ga_len ? ((char_u **)(healthchecks.names.ga_data))[idx] : NULL;
-}
-
-/// Transform healthcheck file path into it's name.
-///
-/// Used as a callback for do_in_runtimepath
-/// @param[in] path Expanded path to a possible healthcheck.
-/// @param[out] cookie Array where names will be inserted.
-static void get_healthcheck_cb(char_u *path, void *cookie)
+static char *get_healthcheck_names(expand_T *xp, int idx)
{
- if (path != NULL) {
- struct healthchecks_cookie *hcookie = (struct healthchecks_cookie *)cookie;
- char *pattern;
- char *sub = "\\1";
- char_u *res;
-
- if (hcookie->is_lua) {
- // Lua: transform "../lua/vim/lsp/health.lua" into "vim.lsp"
- pattern = ".*lua[\\/]\\(.\\{-}\\)[\\/]health\\([\\/]init\\)\\?\\.lua$";
- } else {
- // Vim: transform "../autoload/health/provider.vim" into "provider"
- pattern = ".*[\\/]\\([^\\/]*\\)\\.vim$";
- }
+ static Object names = OBJECT_INIT;
+ static unsigned last_gen = 0;
- res = do_string_sub(path, (char_u *)pattern, (char_u *)sub, NULL, (char_u *)"g");
- if (hcookie->is_lua && res != NULL) {
- // Replace slashes with dots as represented by the healthcheck plugin.
- char_u *ares = do_string_sub(res, (char_u *)"[\\/]", (char_u *)".", NULL, (char_u *)"g");
- xfree(res);
- res = ares;
- }
+ if (last_gen != last_prompt_id || last_gen == 0) {
+ Array a = ARRAY_DICT_INIT;
+ Error err = ERROR_INIT;
+ Object res = nlua_exec(STATIC_CSTR_AS_STRING("return vim.health._complete()"), a, &err);
+ api_clear_error(&err);
+ api_free_object(names);
+ names = res;
+ last_gen = last_prompt_id;
+ }
- if (res != NULL) {
- GA_APPEND(char_u *, hcookie->names, res);
- }
+ if (names.type == kObjectTypeArray && idx < (int)names.data.array.size) {
+ return names.data.array.items[idx].data.string.data;
}
+ return NULL;
}
// Return true when 'incsearch' highlighting is to be done.
@@ -355,12 +309,11 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s
int *skiplen, int *patlen)
FUNC_ATTR_NONNULL_ALL
{
- char_u *cmd;
- cmdmod_T save_cmdmod = cmdmod;
- char_u *p;
+ char *cmd;
+ char *p;
bool delim_optional = false;
int delim;
- char_u *end;
+ char *end;
char *dummy;
exarg_T ea;
pos_T save_cursor;
@@ -390,14 +343,14 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s
memset(&ea, 0, sizeof(ea));
ea.line1 = 1;
ea.line2 = 1;
- ea.cmd = ccline.cmdbuff;
+ ea.cmd = (char *)ccline.cmdbuff;
ea.addr_type = ADDR_LINES;
- parse_command_modifiers(&ea, &dummy, true);
- cmdmod = save_cmdmod;
+ cmdmod_T dummy_cmdmod;
+ parse_command_modifiers(&ea, &dummy, &dummy_cmdmod, true);
cmd = skip_range(ea.cmd, NULL);
- if (vim_strchr((char_u *)"sgvl", *cmd) == NULL) {
+ if (vim_strchr("sgvl", *cmd) == NULL) {
goto theend;
}
@@ -421,7 +374,7 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s
if (*p == '!') {
p = skipwhite(p + 1);
}
- while (ASCII_ISALPHA(*(p = skipwhite(p)))) {
+ while (ASCII_ISALPHA(*(p = skipwhite((char *)p)))) {
p++;
}
if (*p == NUL) {
@@ -449,7 +402,7 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s
p = skipwhite(p);
delim = (delim_optional && vim_isIDc(*p)) ? ' ' : *p++;
*search_delim = delim;
- end = skip_regexp(p, delim, p_magic, NULL);
+ end = (char *)skip_regexp((char_u *)p, delim, p_magic, NULL);
use_last_pat = end == p && *end == delim;
if (end == p && !use_last_pat) {
@@ -458,11 +411,11 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s
// Don't do 'hlsearch' highlighting if the pattern matches everything.
if (!use_last_pat) {
- char_u c = *end;
+ char c = *end;
int empty;
*end = NUL;
- empty = empty_pattern(p);
+ empty = empty_pattern((char_u *)p);
*end = c;
if (empty) {
goto theend;
@@ -470,7 +423,7 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s
}
// found a non-empty pattern or //
- *skiplen = (int)(p - ccline.cmdbuff);
+ *skiplen = (int)((char_u *)p - ccline.cmdbuff);
*patlen = (int)(end - p);
// parse the address range
@@ -632,7 +585,7 @@ static void may_do_incsearch_highlighting(int firstc, long count, incsearch_stat
validate_cursor();
// May redraw the status line to show the cursor position.
- if (p_ru && curwin->w_status_height > 0) {
+ if (p_ru && (curwin->w_status_height > 0 || global_stl_height() > 0)) {
curwin->w_redr_status = true;
}
@@ -686,8 +639,7 @@ static int may_add_char_to_search(int firstc, int *c, incsearch_state_T *s)
*c = mb_tolower(*c);
}
if (*c == search_delim
- || vim_strchr((char_u *)(p_magic ? "\\~^$.*[" : "\\^$"), *c)
- != NULL) {
+ || vim_strchr((p_magic ? "\\~^$.*[" : "\\^$"), *c) != NULL) {
// put a backslash before special characters
stuffcharReadbuff(*c);
*c = '\\';
@@ -731,14 +683,25 @@ static void finish_incsearch_highlighting(int gotesc, incsearch_state_T *s, bool
/// Internal entry point for cmdline mode.
///
-/// caller must use save_cmdline and restore_cmdline. Best is to use
-/// getcmdline or getcmdline_prompt, instead of calling this directly.
-static uint8_t *command_line_enter(int firstc, long count, int indent)
+/// @param count only used for incremental search
+/// @param indent indent for inside conditionals
+/// @param init_ccline clear ccline first
+static uint8_t *command_line_enter(int firstc, long count, int indent, bool init_ccline)
{
+ bool cmdheight0 = p_ch < 1 && !ui_has(kUIMessages);
+
+ if (cmdheight0) {
+ // If cmdheight is 0, cmdheight must be set to 1 when we enter command line.
+ set_option_value("ch", 1L, NULL, 0);
+ update_screen(VALID); // redraw the screen NOW
+ }
+
// can be invoked recursively, identify each level
static int cmdline_level = 0;
cmdline_level++;
+ bool save_cmdpreview = cmdpreview;
+ cmdpreview = false;
CommandLineState state = {
.firstc = firstc,
.count = count,
@@ -750,6 +713,20 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
CommandLineState *s = &state;
s->save_p_icm = vim_strsave(p_icm);
init_incsearch_state(&s->is_state);
+ CmdlineInfo save_ccline;
+ bool did_save_ccline = false;
+
+ if (ccline.cmdbuff != NULL) {
+ // Currently ccline can never be in use if init_ccline is false.
+ // Some changes will be needed if this is no longer the case.
+ assert(init_ccline);
+ // Being called recursively. Since ccline is global, we need to save
+ // the current buffer and restore it when returning.
+ save_cmdline(&save_ccline);
+ did_save_ccline = true;
+ } else if (init_ccline) {
+ memset(&ccline, 0, sizeof(struct cmdline_info));
+ }
if (s->firstc == -1) {
s->firstc = NUL;
@@ -772,7 +749,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;
@@ -789,6 +766,12 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
ccline.cmdlen = s->indent;
}
+ if (cmdline_level == 50) {
+ // Somehow got into a loop recursively calling getcmdline(), bail out.
+ emsg(_(e_command_too_recursive));
+ goto theend;
+ }
+
ExpandInit(&s->xpc);
ccline.xpc = &s->xpc;
@@ -818,15 +801,15 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
if (ccline.input_fn) {
s->xpc.xp_context = ccline.xp_context;
- s->xpc.xp_pattern = ccline.cmdbuff;
- s->xpc.xp_arg = ccline.xp_arg;
+ s->xpc.xp_pattern = (char *)ccline.cmdbuff;
+ s->xpc.xp_arg = (char *)ccline.xp_arg;
}
// Avoid scrolling when called by a recursive do_cmdline(), e.g. when
// doing ":@0" when register 0 doesn't contain a CR.
msg_scroll = false;
- State = CMDLINE;
+ State = MODE_CMDLINE;
if (s->firstc == '/' || s->firstc == '?' || s->firstc == '@') {
// Use ":lmap" mappings for search pattern and input().
@@ -837,7 +820,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
}
if (*s->b_im_ptr == B_IMODE_LMAP) {
- State |= LANGMAP;
+ State |= MODE_LANGMAP;
}
}
@@ -892,11 +875,9 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
tv_dict_set_keys_readonly(dict);
try_enter(&tstate);
- apply_autocmds(EVENT_CMDLINEENTER, (char_u *)firstcbuf, (char_u *)firstcbuf,
- false, curbuf);
+ apply_autocmds(EVENT_CMDLINEENTER, firstcbuf, firstcbuf, false, curbuf);
restore_v_event(dict, &save_v_event);
-
tl_ret = try_leave(&tstate, &err);
if (!tl_ret && ERROR_SET(&err)) {
msg_putchar('\n');
@@ -906,7 +887,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
}
tl_ret = true;
}
- trigger_modechanged();
+ may_trigger_modechanged();
state_enter(&s->state);
@@ -918,8 +899,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
tv_dict_add_bool(dict, S_LEN("abort"),
s->gotesc ? kBoolVarTrue : kBoolVarFalse);
try_enter(&tstate);
- apply_autocmds(EVENT_CMDLINELEAVE, (char_u *)firstcbuf, (char_u *)firstcbuf,
- false, curbuf);
+ apply_autocmds(EVENT_CMDLINELEAVE, firstcbuf, firstcbuf, false, curbuf);
// error printed below, to avoid redraw issues
tl_ret = try_leave(&tstate, &err);
if (tv_dict_get_number(dict, "abort") != 0) {
@@ -933,11 +913,6 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
ExpandCleanup(&s->xpc);
ccline.xpc = NULL;
- if (s->gotesc) {
- // There might be a preview window open for inccommand. Close it.
- close_preview_windows();
- }
-
finish_incsearch_highlighting(s->gotesc, &s->is_state, false);
if (ccline.cmdbuff != NULL) {
@@ -977,17 +952,21 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
need_wait_return = false;
}
- set_string_option_direct("icm", -1, s->save_p_icm, OPT_FREE,
- SID_NONE);
+ set_string_option_direct("icm", -1, (char *)s->save_p_icm, OPT_FREE, SID_NONE);
State = s->save_State;
+ if (cmdpreview != save_cmdpreview) {
+ cmdpreview = save_cmdpreview; // restore preview state
+ redraw_all_later(SOME_VALID);
+ }
setmouse();
ui_cursor_shape(); // may show different cursor shape
+ sb_text_end_cmdline();
+
+theend:
xfree(s->save_p_icm);
xfree(ccline.last_colors.cmdbuff);
kv_destroy(ccline.last_colors.colors);
- sb_text_end_cmdline();
-
char_u *p = ccline.cmdbuff;
if (ui_has(kUICmdline)) {
@@ -996,6 +975,21 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
}
cmdline_level--;
+
+ if (did_save_ccline) {
+ restore_cmdline(&save_ccline);
+ } else {
+ ccline.cmdbuff = NULL;
+ }
+
+ if (cmdheight0) {
+ // Restore cmdheight
+ set_option_value("ch", 0L, NULL, 0);
+
+ // Redraw is needed for command line completion
+ redraw_all_later(CLEAR);
+ }
+
return p;
}
@@ -1188,7 +1182,7 @@ static int command_line_execute(VimState *state, int key)
// cursor
int found = false;
- int j = (int)(s->xpc.xp_pattern - ccline.cmdbuff);
+ int j = (int)((char_u *)s->xpc.xp_pattern - ccline.cmdbuff);
int i = 0;
while (--j > 0) {
// check for start of menu name
@@ -1243,7 +1237,7 @@ static int command_line_execute(VimState *state, int key)
int found = false;
int j = ccline.cmdpos;
- int i = (int)(s->xpc.xp_pattern - ccline.cmdbuff);
+ int i = (int)((char_u *)s->xpc.xp_pattern - ccline.cmdbuff);
while (--j > i) {
j -= utf_head_off(ccline.cmdbuff, ccline.cmdbuff + j);
if (vim_ispathsep(ccline.cmdbuff[j])) {
@@ -1264,7 +1258,7 @@ static int command_line_execute(VimState *state, int key)
int found = false;
int j = ccline.cmdpos - 1;
- int i = (int)(s->xpc.xp_pattern - ccline.cmdbuff);
+ int i = (int)((char_u *)s->xpc.xp_pattern - ccline.cmdbuff);
while (--j > i) {
j -= utf_head_off(ccline.cmdbuff, ccline.cmdbuff + j);
if (vim_ispathsep(ccline.cmdbuff[j])
@@ -1309,12 +1303,13 @@ static int command_line_execute(VimState *state, int key)
}
}
- // CTRL-\ CTRL-N goes to Normal mode, CTRL-\ CTRL-G goes to Insert
- // mode when 'insertmode' is set, CTRL-\ e prompts for an expression.
+ // CTRL-\ CTRL-N goes to Normal mode, CTRL-\ e prompts for an expression.
if (s->c == Ctrl_BSL) {
no_mapping++;
+ allow_keys++;
s->c = plain_vgetc();
no_mapping--;
+ allow_keys--;
// CTRL-\ e doesn't work when obtaining an expression, unless it
// is in a mapping.
if (s->c != Ctrl_N
@@ -1339,15 +1334,9 @@ static int command_line_execute(VimState *state, int key)
s->c = get_expr_register();
if (s->c == '=') {
- // Need to save and restore ccline. And set "textlock"
- // to avoid nasty things like going to another buffer when
- // evaluating an expression.
- CmdlineInfo save_ccline;
- save_cmdline(&save_ccline);
textlock++;
p = get_expr_line();
textlock--;
- restore_cmdline(&save_ccline);
if (p != NULL) {
len = (int)STRLEN(p);
@@ -1376,9 +1365,6 @@ static int command_line_execute(VimState *state, int key)
redrawcmd();
return command_line_not_changed(s);
} else {
- if (s->c == Ctrl_G && p_im && restart_edit == 0) {
- restart_edit = 'a';
- }
s->gotesc = true; // will free ccline.cmdbuff after putting it
// in history
return 0; // back to Normal mode
@@ -1587,9 +1573,12 @@ static int may_do_command_line_next_incsearch(int firstc, long count, incsearch_
int search_flags = SEARCH_NOOF;
char_u save;
-
if (search_delim == ccline.cmdbuff[skiplen]) {
pat = last_search_pattern();
+ if (pat == NULL) {
+ restore_last_search_pattern();
+ return FAIL;
+ }
skiplen = 0;
patlen = (int)STRLEN(pat);
} else {
@@ -1758,7 +1747,7 @@ static int command_line_handle_key(CommandLineState *s)
}
if (mb_get_class(p) != i) {
- p += utfc_ptr2len(p);
+ p += utfc_ptr2len((char *)p);
}
}
@@ -1809,11 +1798,11 @@ static int command_line_handle_key(CommandLineState *s)
return command_line_not_changed(s);
case Ctrl_HAT:
- if (map_to_exists_mode("", LANGMAP, false)) {
+ if (map_to_exists_mode("", MODE_LANGMAP, false)) {
// ":lmap" mappings exists, toggle use of mappings.
- State ^= LANGMAP;
+ State ^= MODE_LANGMAP;
if (s->b_im_ptr != NULL) {
- if (State & LANGMAP) {
+ if (State & MODE_LANGMAP) {
*s->b_im_ptr = B_IMODE_LMAP;
} else {
*s->b_im_ptr = B_IMODE_NONE;
@@ -1869,6 +1858,7 @@ static int command_line_handle_key(CommandLineState *s)
case Ctrl_R: { // insert register
putcmdline('"', true);
no_mapping++;
+ allow_keys++;
int i = s->c = plain_vgetc(); // CTRL-R <char>
if (i == Ctrl_O) {
i = Ctrl_R; // CTRL-R CTRL-O == CTRL-R CTRL-R
@@ -1877,7 +1867,8 @@ static int command_line_handle_key(CommandLineState *s)
if (i == Ctrl_R) {
s->c = plain_vgetc(); // CTRL-R CTRL-R <char>
}
- --no_mapping;
+ no_mapping--;
+ allow_keys--;
// Insert the result of an expression.
// Need to save the current command line, to be able to enter
// a new one...
@@ -1888,10 +1879,7 @@ static int command_line_handle_key(CommandLineState *s)
beep_flush();
s->c = ESC;
} else {
- CmdlineInfo save_ccline;
- save_cmdline(&save_ccline);
s->c = get_expr_register();
- restore_cmdline(&save_ccline);
}
}
@@ -1943,7 +1931,7 @@ static int command_line_handle_key(CommandLineState *s)
}
ccline.cmdspos += cells;
- ccline.cmdpos += utfc_ptr2len(ccline.cmdbuff + ccline.cmdpos);
+ ccline.cmdpos += utfc_ptr2len((char *)ccline.cmdbuff + ccline.cmdpos);
} while ((s->c == K_S_RIGHT || s->c == K_C_RIGHT
|| (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_CTRL)))
&& ccline.cmdbuff[ccline.cmdpos] != ' ');
@@ -1978,7 +1966,6 @@ static int command_line_handle_key(CommandLineState *s)
// Ignore mouse event or open_cmdwin() result.
return command_line_not_changed(s);
-
case K_MIDDLEDRAG:
case K_MIDDLERELEASE:
return command_line_not_changed(s); // Ignore mouse
@@ -1988,7 +1975,6 @@ static int command_line_handle_key(CommandLineState *s)
redrawcmd();
return command_line_changed(s);
-
case K_LEFTDRAG:
case K_LEFTRELEASE:
case K_RIGHTDRAG:
@@ -2018,7 +2004,7 @@ static int command_line_handle_key(CommandLineState *s)
// Count ">" for double-wide char that doesn't fit.
correct_screencol(ccline.cmdpos, cells, &ccline.cmdspos);
- ccline.cmdpos += utfc_ptr2len(ccline.cmdbuff + ccline.cmdpos) - 1;
+ ccline.cmdpos += utfc_ptr2len((char *)ccline.cmdbuff + ccline.cmdpos) - 1;
ccline.cmdspos += cells;
}
return command_line_not_changed(s);
@@ -2038,7 +2024,6 @@ static int command_line_handle_key(CommandLineState *s)
case K_MOUSEMOVE:
return command_line_not_changed(s);
-
case K_SELECT: // end of Select mode mapping - ignore
return command_line_not_changed(s);
@@ -2119,7 +2104,7 @@ static int command_line_handle_key(CommandLineState *s)
int len = 0;
int old_firstc;
- xfree(ccline.cmdbuff);
+ XFREE_CLEAR(ccline.cmdbuff);
s->xpc.xp_context = EXPAND_NOTHING;
if (s->hiscnt == hislen) {
p = s->lookfor; // back to the old one
@@ -2191,7 +2176,11 @@ static int command_line_handle_key(CommandLineState *s)
case Ctrl_Q:
s->ignore_drag_release = true;
putcmdline('^', true);
- s->c = get_literal(); // get next (two) character(s)
+
+ // Get next (two) characters.
+ // Do not include modifiers into the key for CTRL-SHIFT-V.
+ s->c = get_literal(mod_mask & MOD_MASK_SHIFT);
+
s->do_abbr = false; // don't do abbreviation now
ccline.special_char = NUL;
// may need to remove ^ when composing char was typed
@@ -2251,14 +2240,13 @@ static int command_line_handle_key(CommandLineState *s)
if (IS_SPECIAL(s->c) || mod_mask != 0) {
put_on_cmdline(get_special_key_name(s->c, mod_mask), -1, true);
} else {
- int j = utf_char2bytes(s->c, IObuff);
+ int j = utf_char2bytes(s->c, (char *)IObuff);
IObuff[j] = NUL; // exclude composing chars
put_on_cmdline(IObuff, j, true);
}
return command_line_changed(s);
}
-
static int command_line_not_changed(CommandLineState *s)
{
// Incremental searches for "/" and "?":
@@ -2280,12 +2268,272 @@ static int empty_pattern(char_u *p)
// remove trailing \v and the like
while (n >= 2 && p[n - 2] == '\\'
- && vim_strchr((char_u *)"mMvVcCZ", p[n - 1]) != NULL) {
+ && vim_strchr("mMvVcCZ", p[n - 1]) != NULL) {
n -= 2;
}
return n == 0 || (n >= 2 && p[n - 2] == '\\' && p[n - 1] == '|');
}
+handle_T cmdpreview_get_bufnr(void)
+{
+ return cmdpreview_bufnr;
+}
+
+long cmdpreview_get_ns(void)
+{
+ return cmdpreview_ns;
+}
+
+/// Sets up command preview buffer.
+///
+/// @return Pointer to command preview buffer if succeeded, NULL if failed.
+static buf_T *cmdpreview_open_buf(void)
+{
+ buf_T *cmdpreview_buf = cmdpreview_bufnr ? buflist_findnr(cmdpreview_bufnr) : NULL;
+
+ // If preview buffer doesn't exist, open one.
+ if (cmdpreview_buf == NULL) {
+ Error err = ERROR_INIT;
+ handle_T bufnr = nvim_create_buf(false, true, &err);
+
+ if (ERROR_SET(&err)) {
+ return NULL;
+ }
+
+ cmdpreview_buf = buflist_findnr(bufnr);
+ }
+
+ // Preview buffer cannot preview itself!
+ if (cmdpreview_buf == curbuf) {
+ return NULL;
+ }
+
+ // Rename preview buffer.
+ aco_save_T aco;
+ aucmd_prepbuf(&aco, cmdpreview_buf);
+ int retv = rename_buffer("[Preview]");
+ aucmd_restbuf(&aco);
+
+ if (retv == FAIL) {
+ return NULL;
+ }
+
+ // Temporarily switch to preview buffer to set it up for previewing.
+ aucmd_prepbuf(&aco, cmdpreview_buf);
+ buf_clear();
+ curbuf->b_p_ma = true;
+ curbuf->b_p_ul = -1;
+ curbuf->b_p_tw = 0; // Reset 'textwidth' (was set by ftplugin)
+ aucmd_restbuf(&aco);
+ cmdpreview_bufnr = cmdpreview_buf->handle;
+
+ return cmdpreview_buf;
+}
+
+/// Open command preview window if it's not already open.
+/// Returns to original window after opening command preview window.
+///
+/// @param cmdpreview_buf Pointer to command preview buffer
+///
+/// @return Pointer to command preview window if succeeded, NULL if failed.
+static win_T *cmdpreview_open_win(buf_T *cmdpreview_buf)
+{
+ win_T *save_curwin = curwin;
+
+ // Open preview window.
+ if (win_split((int)p_cwh, WSP_BOT) == FAIL) {
+ return NULL;
+ }
+
+ win_T *preview_win = curwin;
+ Error err = ERROR_INIT;
+
+ // Switch to preview buffer
+ try_start();
+ int result = do_buffer(DOBUF_GOTO, DOBUF_FIRST, FORWARD, cmdpreview_buf->handle, 0);
+ if (try_end(&err) || result == FAIL) {
+ api_clear_error(&err);
+ return NULL;
+ }
+
+ curwin->w_p_cul = false;
+ curwin->w_p_cuc = false;
+ curwin->w_p_spell = false;
+ curwin->w_p_fen = false;
+
+ win_enter(save_curwin, false);
+ return preview_win;
+}
+
+/// Closes any open command preview windows.
+static void cmdpreview_close_win(void)
+{
+ buf_T *buf = cmdpreview_bufnr ? buflist_findnr(cmdpreview_bufnr) : NULL;
+ if (buf != NULL) {
+ close_windows(buf, false);
+ }
+}
+
+/// Show 'inccommand' preview if command is previewable. It works like this:
+/// 1. Store current undo information so we can revert to current state later.
+/// 2. Execute the preview callback with the parsed command, preview buffer number and preview
+/// namespace number as arguments. The preview callback sets the highlight and does the
+/// changes required for the preview if needed.
+/// 3. Preview callback returns 0, 1 or 2. 0 means no preview is shown. 1 means preview is shown
+/// but preview window doesn't need to be opened. 2 means preview is shown and preview window
+/// needs to be opened if inccommand=split.
+/// 4. Use the return value of the preview callback to determine whether to
+/// open the preview window or not and open preview window if needed.
+/// 5. If the return value of the preview callback is not 0, update the screen while the effects
+/// of the preview are still in place.
+/// 6. Revert all changes made by the preview callback.
+///
+/// @return whether preview is shown or not.
+static bool cmdpreview_may_show(CommandLineState *s)
+{
+ // Parse the command line and return if it fails.
+ exarg_T ea;
+ CmdParseInfo cmdinfo;
+ // Copy the command line so we can modify it.
+ int cmdpreview_type = 0;
+ char *cmdline = xstrdup((char *)ccline.cmdbuff);
+ char *errormsg = NULL;
+ emsg_off++; // Block errors when parsing the command line, and don't update v:errmsg
+ if (!parse_cmdline(cmdline, &ea, &cmdinfo, &errormsg)) {
+ emsg_off--;
+ goto end;
+ }
+ emsg_off--;
+
+ // Check if command is previewable, if not, don't attempt to show preview
+ if (!(ea.argt & EX_PREVIEW)) {
+ undo_cmdmod(&cmdinfo.cmdmod);
+ goto end;
+ }
+
+ // Swap invalid command range if needed
+ if ((ea.argt & EX_RANGE) && ea.line1 > ea.line2) {
+ linenr_T lnum = ea.line1;
+ ea.line1 = ea.line2;
+ ea.line2 = lnum;
+ }
+
+ time_t save_b_u_time_cur = curbuf->b_u_time_cur;
+ long save_b_u_seq_cur = curbuf->b_u_seq_cur;
+ u_header_T *save_b_u_newhead = curbuf->b_u_newhead;
+ long save_b_p_ul = curbuf->b_p_ul;
+ int save_b_changed = curbuf->b_changed;
+ int save_w_p_cul = curwin->w_p_cul;
+ int save_w_p_cuc = curwin->w_p_cuc;
+ bool save_hls = p_hls;
+ varnumber_T save_changedtick = buf_get_changedtick(curbuf);
+ bool icm_split = *p_icm == 's'; // inccommand=split
+ buf_T *cmdpreview_buf;
+ win_T *cmdpreview_win;
+ cmdmod_T save_cmdmod = cmdmod;
+
+ cmdpreview = true;
+ emsg_silent++; // Block error reporting as the command may be incomplete,
+ // but still update v:errmsg
+ msg_silent++; // Block messages, namely ones that prompt
+ block_autocmds(); // Block events
+ garray_T save_view;
+ win_size_save(&save_view); // Save current window sizes
+ save_search_patterns(); // Save search patterns
+ curbuf->b_p_ul = LONG_MAX; // Make sure we can undo all changes
+ curwin->w_p_cul = false; // Disable 'cursorline' so it doesn't mess up the highlights
+ curwin->w_p_cuc = false; // Disable 'cursorcolumn' so it doesn't mess up the highlights
+ p_hls = false; // Don't show search highlighting during live substitution
+ cmdmod.cmod_split = 0; // Disable :leftabove/botright modifiers
+ cmdmod.cmod_tab = 0; // Disable :tab modifier
+ cmdmod.cmod_flags |= CMOD_NOSWAPFILE; // Disable swap for preview buffer
+
+ // Open preview buffer if inccommand=split.
+ if (!icm_split) {
+ cmdpreview_bufnr = 0;
+ } else if ((cmdpreview_buf = cmdpreview_open_buf()) == NULL) {
+ abort();
+ }
+
+ // Setup preview namespace if it's not already set.
+ if (!cmdpreview_ns) {
+ cmdpreview_ns = (int)nvim_create_namespace((String)STRING_INIT);
+ }
+
+ // Execute the preview callback and use its return value to determine whether to show preview or
+ // open the preview window. The preview callback also handles doing the changes and highlights for
+ // the preview.
+ Error err = ERROR_INIT;
+ try_start();
+ cmdpreview_type = execute_cmd(&ea, &cmdinfo, true);
+ if (try_end(&err)) {
+ api_clear_error(&err);
+ cmdpreview_type = 0;
+ }
+
+ // If inccommand=split and preview callback returns 2, open preview window.
+ if (icm_split && cmdpreview_type == 2
+ && (cmdpreview_win = cmdpreview_open_win(cmdpreview_buf)) == NULL) {
+ // If there's not enough room to open the preview window, just preview without the window.
+ cmdpreview_type = 1;
+ }
+
+ // If preview callback is nonzero, update screen now.
+ if (cmdpreview_type != 0) {
+ int save_rd = RedrawingDisabled;
+ RedrawingDisabled = 0;
+ update_screen(SOME_VALID);
+ RedrawingDisabled = save_rd;
+ }
+
+ // Close preview window if it's open.
+ if (icm_split && cmdpreview_type == 2 && cmdpreview_win != NULL) {
+ cmdpreview_close_win();
+ }
+ // Clear preview highlights.
+ extmark_clear(curbuf, (uint32_t)cmdpreview_ns, 0, 0, MAXLNUM, MAXCOL);
+
+ curbuf->b_changed = save_b_changed; // Preserve 'modified' during preview
+
+ if (curbuf->b_u_seq_cur != save_b_u_seq_cur) {
+ // Undo invisibly. This also moves the cursor!
+ while (curbuf->b_u_seq_cur != save_b_u_seq_cur) {
+ if (!u_undo_and_forget(1)) {
+ abort();
+ }
+ }
+ // Restore newhead. It is meaningless when curhead is valid, but we must
+ // restore it so that undotree() is identical before/after the preview.
+ curbuf->b_u_newhead = save_b_u_newhead;
+ curbuf->b_u_time_cur = save_b_u_time_cur;
+ }
+ if (save_changedtick != buf_get_changedtick(curbuf)) {
+ buf_set_changedtick(curbuf, save_changedtick);
+ }
+
+ cmdmod = save_cmdmod; // Restore cmdmod
+ p_hls = save_hls; // Restore 'hlsearch'
+ curwin->w_p_cul = save_w_p_cul; // Restore 'cursorline'
+ curwin->w_p_cuc = save_w_p_cuc; // Restore 'cursorcolumn'
+ curbuf->b_p_ul = save_b_p_ul; // Restore 'undolevels'
+ restore_search_patterns(); // Restore search patterns
+ win_size_restore(&save_view); // Restore window sizes
+ ga_clear(&save_view);
+ unblock_autocmds(); // Unblock events
+ msg_silent--; // Unblock messages
+ emsg_silent--; // Unblock error reporting
+
+ // Restore the window "view".
+ curwin->w_cursor = s->is_state.save_cursor;
+ restore_viewstate(&s->is_state.old_viewstate);
+ update_topline(curwin);
+
+ redrawcmdline();
+end:
+ xfree(cmdline);
+ return cmdpreview_type != 0;
+}
+
static int command_line_changed(CommandLineState *s)
{
// Trigger CmdlineChanged autocommands.
@@ -2305,8 +2553,7 @@ static int command_line_changed(CommandLineState *s)
tv_dict_set_keys_readonly(dict);
try_enter(&tstate);
- apply_autocmds(EVENT_CMDLINECHANGED, (char_u *)firstcbuf,
- (char_u *)firstcbuf, false, curbuf);
+ apply_autocmds(EVENT_CMDLINECHANGED, firstcbuf, firstcbuf, false, curbuf);
restore_v_event(dict, &save_v_event);
bool tl_ret = try_leave(&tstate, &err);
@@ -2318,35 +2565,16 @@ static int command_line_changed(CommandLineState *s)
}
}
- // 'incsearch' highlighting.
if (s->firstc == ':'
&& current_sctx.sc_sid == 0 // only if interactive
&& *p_icm != NUL // 'inccommand' is set
&& curbuf->b_p_ma // buffer is modifiable
&& cmdline_star == 0 // not typing a password
- && cmd_can_preview(ccline.cmdbuff)
- && !vpeekc_any()) {
- // Show 'inccommand' preview. It works like this:
- // 1. Do the command.
- // 2. Command implementation detects CMDPREVIEW state, then:
- // - Update the screen while the effects are in place.
- // - Immediately undo the effects.
- State |= CMDPREVIEW;
- emsg_silent++; // Block error reporting as the command may be incomplete
- msg_silent++; // Block messages, namely ones that prompt
- do_cmdline(ccline.cmdbuff, NULL, NULL, DOCMD_KEEPLINE|DOCMD_NOWAIT|DOCMD_PREVIEW);
- msg_silent--; // Unblock messages
- emsg_silent--; // Unblock error reporting
-
- // Restore the window "view".
- curwin->w_cursor = s->is_state.save_cursor;
- restore_viewstate(&s->is_state.old_viewstate);
- update_topline(curwin);
-
- redrawcmdline();
- } else if (State & CMDPREVIEW) {
- State = (State & ~CMDPREVIEW);
- close_preview_windows();
+ && !vpeekc_any()
+ && cmdpreview_may_show(s)) {
+ // 'inccommand' preview has been shown.
+ } else if (cmdpreview) {
+ cmdpreview = false;
update_screen(SOME_VALID); // Clear 'inccommand' preview.
} else {
if (s->xpc.xp_context == EXPAND_NOTHING && (KeyTyped || vpeekc() == NUL)) {
@@ -2402,14 +2630,7 @@ static void abandon_cmdline(void)
/// @param indent indent for inside conditionals
char_u *getcmdline(int firstc, long count, int indent, bool do_concat FUNC_ATTR_UNUSED)
{
- // Be prepared for situations where cmdline can be invoked recursively.
- // That includes cmd mappings, event handlers, as well as update_screen()
- // (custom status line eval), which all may invoke ":normal :".
- CmdlineInfo save_ccline;
- save_cmdline(&save_ccline);
- char_u *retval = command_line_enter(firstc, count, indent);
- restore_cmdline(&save_ccline);
- return retval;
+ return command_line_enter(firstc, count, indent, true);
}
/// Get a command line with a prompt
@@ -2425,7 +2646,7 @@ char_u *getcmdline(int firstc, long count, int indent, bool do_concat FUNC_ATTR_
/// @param[in] highlight_callback Callback used for highlighting user input.
///
/// @return [allocated] Command line or NULL.
-char *getcmdline_prompt(const char firstc, const char *const prompt, const int attr,
+char *getcmdline_prompt(const int firstc, const char *const prompt, const int attr,
const int xp_context, const char *const xp_arg,
const Callback highlight_callback)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC
@@ -2433,8 +2654,14 @@ char *getcmdline_prompt(const char firstc, const char *const prompt, const int a
const int msg_col_save = msg_col;
CmdlineInfo save_ccline;
- save_cmdline(&save_ccline);
-
+ bool did_save_ccline = false;
+ if (ccline.cmdbuff != NULL) {
+ // Save the values of the current cmdline and restore them below.
+ save_cmdline(&save_ccline);
+ did_save_ccline = true;
+ } else {
+ memset(&ccline, 0, sizeof(struct cmdline_info));
+ }
ccline.prompt_id = last_prompt_id++;
ccline.cmdprompt = (char_u *)prompt;
ccline.cmdattr = attr;
@@ -2446,9 +2673,11 @@ char *getcmdline_prompt(const char firstc, const char *const prompt, const int a
int msg_silent_saved = msg_silent;
msg_silent = 0;
- char *const ret = (char *)command_line_enter(firstc, 1L, 0);
+ char *const ret = (char *)command_line_enter(firstc, 1L, 0, false);
- restore_cmdline(&save_ccline);
+ if (did_save_ccline) {
+ restore_cmdline(&save_ccline);
+ }
msg_silent = msg_silent_saved;
// Restore msg_col, the prompt from input() may have changed it.
// But only if called recursively and the commandline is therefore being
@@ -2461,14 +2690,18 @@ char *getcmdline_prompt(const char firstc, const char *const prompt, const int a
return ret;
}
-/*
- * Return TRUE when the text must not be changed and we can't switch to
- * another window or buffer. Used when editing the command line etc.
- */
-int text_locked(void)
+// Return current cmdline prompt
+char_u *get_cmdprompt(void)
+{
+ return ccline.cmdprompt;
+}
+
+/// Return true when the text must not be changed and we can't switch to
+/// another window or buffer. True when editing the command line etc.
+bool text_locked(void)
{
if (cmdwin_type != 0) {
- return TRUE;
+ return true;
}
return textlock != 0;
}
@@ -2487,8 +2720,19 @@ char *get_text_locked_msg(void)
if (cmdwin_type != 0) {
return e_cmdwin;
} else {
- return e_secure;
+ return e_textlock;
+ }
+}
+
+/// Check for text, window or buffer locked.
+/// Give an error message and return true if something is locked.
+bool text_or_buf_locked(void)
+{
+ if (text_locked()) {
+ text_locked_msg();
+ return true;
}
+ return curbuf_locked();
}
/// Check if "curbuf->b_ro_locked" or "allbuf_lock" is set and
@@ -2496,7 +2740,7 @@ char *get_text_locked_msg(void)
bool curbuf_locked(void)
{
if (curbuf->b_ro_locked > 0) {
- emsg(_("E788: Not allowed to edit another buffer now"));
+ emsg(_(e_cannot_edit_other_buf));
return true;
}
return allbuf_locked();
@@ -2518,7 +2762,7 @@ static int cmdline_charsize(int idx)
if (cmdline_star > 0) { // showing '*', always 1 position
return 1;
}
- return ptr2cells(ccline.cmdbuff + idx);
+ return ptr2cells((char *)ccline.cmdbuff + idx);
}
/// Compute the offset of the cursor on the command line for the prompt and
@@ -2528,7 +2772,6 @@ static int cmd_startcol(void)
return ccline.cmdindent + ((ccline.cmdfirstc != NUL) ? 1 : 0);
}
-
/// Compute the column position for a byte position on the command line.
static int cmd_screencol(int bytepos)
{
@@ -2545,7 +2788,7 @@ static int cmd_screencol(int bytepos)
}
for (int i = 0; i < ccline.cmdlen && i < bytepos;
- i += utfc_ptr2len(ccline.cmdbuff + i)) {
+ i += utfc_ptr2len((char *)ccline.cmdbuff + i)) {
int c = cmdline_charsize(i);
// Count ">" for double-wide multi-byte char that doesn't fit.
correct_screencol(i, c, &col);
@@ -2564,8 +2807,8 @@ static int cmd_screencol(int bytepos)
/// character that doesn't fit, so that a ">" must be displayed.
static void correct_screencol(int idx, int cells, int *col)
{
- if (utfc_ptr2len(ccline.cmdbuff + idx) > 1
- && utf_ptr2cells(ccline.cmdbuff + idx) > 1
+ if (utfc_ptr2len((char *)ccline.cmdbuff + idx) > 1
+ && utf_ptr2cells((char *)ccline.cmdbuff + idx) > 1
&& (*col) % Columns + cells > Columns) {
(*col)++;
}
@@ -2575,24 +2818,25 @@ static void correct_screencol(int idx, int cells, int *col)
///
/// @param c normally ':', NUL for ":append"
/// @param indent indent for inside conditionals
-char_u *getexline(int c, void *cookie, int indent, bool do_concat)
+char *getexline(int c, void *cookie, int indent, bool do_concat)
{
// When executing a register, remove ':' that's in front of each line.
if (exec_from_reg && vpeekc() == ':') {
(void)vgetc();
}
- return getcmdline(c, 1L, indent, do_concat);
+ return (char *)getcmdline(c, 1L, indent, do_concat);
}
bool cmdline_overstrike(void)
+ FUNC_ATTR_PURE
{
return ccline.overstrike;
}
-
/// Return true if the cursor is at the end of the cmdline.
bool cmdline_at_end(void)
+ FUNC_ATTR_PURE
{
return (ccline.cmdpos >= ccline.cmdlen);
}
@@ -2600,7 +2844,6 @@ bool cmdline_at_end(void)
/*
* Allocate a new command line buffer.
* Assigns the new buffer to ccline.cmdbuff and ccline.cmdbufflen.
- * Returns the new value of ccline.cmdbuff and ccline.cmdbufflen.
*/
static void alloc_cmdbuff(int len)
{
@@ -2638,17 +2881,17 @@ static void realloc_cmdbuff(int len)
&& ccline.xpc->xp_pattern != NULL
&& ccline.xpc->xp_context != EXPAND_NOTHING
&& ccline.xpc->xp_context != EXPAND_UNSUCCESSFUL) {
- int i = (int)(ccline.xpc->xp_pattern - p);
+ int i = (int)((char_u *)ccline.xpc->xp_pattern - p);
// If xp_pattern points inside the old cmdbuff it needs to be adjusted
// to point into the newly allocated memory.
if (i >= 0 && i <= ccline.cmdlen) {
- ccline.xpc->xp_pattern = ccline.cmdbuff + i;
+ ccline.xpc->xp_pattern = (char *)ccline.cmdbuff + i;
}
}
}
-static char_u *arshape_buf = NULL;
+static char *arshape_buf = NULL;
#if defined(EXITFREE)
void free_arshape_buf(void)
@@ -2766,7 +3009,7 @@ static bool color_cmdline(CmdlineInfo *colored_ccline)
bool arg_allocated = false;
typval_T arg = {
.v_type = VAR_STRING,
- .vval.v_string = colored_ccline->cmdbuff,
+ .vval.v_string = (char *)colored_ccline->cmdbuff,
};
typval_T tv = { .v_type = VAR_UNKNOWN };
@@ -2927,7 +3170,7 @@ color_cmdline_end:
// Note: errors “output” is cached just as well as regular results.
ccline_colors->prompt_id = colored_ccline->prompt_id;
if (arg_allocated) {
- ccline_colors->cmdbuff = (char *)arg.vval.v_string;
+ ccline_colors->cmdbuff = arg.vval.v_string;
} else {
ccline_colors->cmdbuff = xmemdupz((const char *)colored_ccline->cmdbuff,
(size_t)colored_ccline->cmdlen);
@@ -2969,7 +3212,7 @@ static void draw_cmdline(int start, int len)
if (cmdline_star > 0) {
for (int i = 0; i < len; i++) {
msg_putchar('*');
- i += utfc_ptr2len(ccline.cmdbuff + start + i) - 1;
+ i += utfc_ptr2len((char *)ccline.cmdbuff + start + i) - 1;
}
} else if (p_arshape && !p_tbidi && len > 0) {
bool do_arabicshape = false;
@@ -2979,7 +3222,7 @@ static void draw_cmdline(int start, int len)
int u8cc[MAX_MCO];
int u8c = utfc_ptr2char_len(p, u8cc, start + len - i);
mb_l = utfc_ptr2len_len(p, start + len - i);
- if (arabic_char(u8c)) {
+ if (ARABIC_CHAR(u8c)) {
do_arabicshape = true;
break;
}
@@ -3002,7 +3245,7 @@ static void draw_cmdline(int start, int len)
}
int newlen = 0;
- if (utf_iscomposing(utf_ptr2char(ccline.cmdbuff + start))) {
+ if (utf_iscomposing(utf_ptr2char((char *)ccline.cmdbuff + start))) {
// Prepend a space to draw the leading composing char on.
arshape_buf[0] = ' ';
newlen = 1;
@@ -3015,7 +3258,7 @@ static void draw_cmdline(int start, int len)
int u8cc[MAX_MCO];
int u8c = utfc_ptr2char_len(p, u8cc, start + len - i);
mb_l = utfc_ptr2len_len(p, start + len - i);
- if (arabic_char(u8c)) {
+ if (ARABIC_CHAR(u8c)) {
int pc;
int pc1 = 0;
int nc = 0;
@@ -3028,7 +3271,7 @@ static void draw_cmdline(int start, int len)
if (i + mb_l >= start + len) {
nc = NUL;
} else {
- nc = utf_ptr2char(p + mb_l);
+ nc = utf_ptr2char((char *)p + mb_l);
}
} else {
// Displaying from left to right.
@@ -3060,7 +3303,7 @@ static void draw_cmdline(int start, int len)
}
}
- msg_outtrans_len(arshape_buf, newlen);
+ msg_outtrans_len((char_u *)arshape_buf, newlen);
} else {
draw_cmdline_no_arabicshape:
if (kv_size(ccline.last_colors.colors)) {
@@ -3082,45 +3325,53 @@ draw_cmdline_no_arabicshape:
static void ui_ext_cmdline_show(CmdlineInfo *line)
{
+ Arena arena = ARENA_EMPTY;
+ arena_start(&arena, &ui_ext_fixblk);
Array content = ARRAY_DICT_INIT;
if (cmdline_star) {
+ content = arena_array(&arena, 1);
size_t len = 0;
for (char_u *p = ccline.cmdbuff; *p; MB_PTR_ADV(p)) {
len++;
}
- char *buf = xmallocz(len);
+ char *buf = arena_alloc(&arena, len, false);
memset(buf, '*', len);
- Array item = ARRAY_DICT_INIT;
- ADD(item, INTEGER_OBJ(0));
- ADD(item, STRING_OBJ(((String) { .data = buf, .size = len })));
- ADD(content, ARRAY_OBJ(item));
+ Array item = arena_array(&arena, 2);
+ ADD_C(item, INTEGER_OBJ(0));
+ ADD_C(item, STRING_OBJ(cbuf_as_string(buf, len)));
+ ADD_C(content, ARRAY_OBJ(item));
} else if (kv_size(line->last_colors.colors)) {
+ content = arena_array(&arena, kv_size(line->last_colors.colors));
for (size_t i = 0; i < kv_size(line->last_colors.colors); i++) {
CmdlineColorChunk chunk = kv_A(line->last_colors.colors, i);
- Array item = ARRAY_DICT_INIT;
- ADD(item, INTEGER_OBJ(chunk.attr));
+ Array item = arena_array(&arena, 2);
+ ADD_C(item, INTEGER_OBJ(chunk.attr));
assert(chunk.end >= chunk.start);
- ADD(item, STRING_OBJ(cbuf_to_string((char *)line->cmdbuff + chunk.start,
- (size_t)(chunk.end-chunk.start))));
- ADD(content, ARRAY_OBJ(item));
+ ADD_C(item, STRING_OBJ(cbuf_as_string((char *)line->cmdbuff + chunk.start,
+ (size_t)(chunk.end - chunk.start))));
+ ADD_C(content, ARRAY_OBJ(item));
}
} else {
- Array item = ARRAY_DICT_INIT;
- ADD(item, INTEGER_OBJ(0));
- ADD(item, STRING_OBJ(cstr_to_string((char *)(line->cmdbuff))));
- ADD(content, ARRAY_OBJ(item));
+ Array item = arena_array(&arena, 2);
+ ADD_C(item, INTEGER_OBJ(0));
+ ADD_C(item, STRING_OBJ(cstr_as_string((char *)(line->cmdbuff))));
+ content = arena_array(&arena, 1);
+ ADD_C(content, ARRAY_OBJ(item));
}
+ char charbuf[2] = { (char)line->cmdfirstc, 0 };
ui_call_cmdline_show(content, line->cmdpos,
- cchar_to_string((char)line->cmdfirstc),
- cstr_to_string((char *)(line->cmdprompt)),
+ cstr_as_string(charbuf),
+ cstr_as_string((char *)(line->cmdprompt)),
line->cmdindent,
line->level);
if (line->special_char) {
- ui_call_cmdline_special_char(cchar_to_string(line->special_char),
+ charbuf[0] = line->special_char;
+ ui_call_cmdline_special_char(cstr_as_string(charbuf),
line->special_shift,
line->level);
}
+ arena_mem_free(arena_finish(&arena), &ui_ext_fixblk);
}
void ui_ext_cmdline_block_append(size_t indent, const char *line)
@@ -3136,9 +3387,9 @@ void ui_ext_cmdline_block_append(size_t indent, const char *line)
ADD(content, ARRAY_OBJ(item));
ADD(cmdline_block, ARRAY_OBJ(content));
if (cmdline_block.size > 1) {
- ui_call_cmdline_block_append(copy_array(content));
+ ui_call_cmdline_block_append(content);
} else {
- ui_call_cmdline_block_show(copy_array(cmdline_block));
+ ui_call_cmdline_block_show(cmdline_block);
}
}
@@ -3158,10 +3409,10 @@ void cmdline_screen_cleared(void)
}
if (cmdline_block.size) {
- ui_call_cmdline_block_show(copy_array(cmdline_block));
+ ui_call_cmdline_block_show(cmdline_block);
}
- int prev_level = ccline.level-1;
+ int prev_level = ccline.level - 1;
CmdlineInfo *line = ccline.prev_ccline;
while (prev_level > 0 && line) {
if (line->level == prev_level) {
@@ -3215,7 +3466,8 @@ void putcmdline(char c, int shift)
}
msg_no_more = false;
} else if (ccline.redraw_state != kCmdRedrawAll) {
- ui_call_cmdline_special_char(cchar_to_string(c), shift,
+ char charbuf[2] = { c, 0 };
+ ui_call_cmdline_special_char(cstr_as_string(charbuf), shift,
ccline.level);
}
cursorcmd();
@@ -3234,7 +3486,7 @@ void unputcmdline(void)
if (ccline.cmdlen == ccline.cmdpos && !ui_has(kUICmdline)) {
msg_putchar(' ');
} else {
- draw_cmdline(ccline.cmdpos, utfc_ptr2len(ccline.cmdbuff + ccline.cmdpos));
+ draw_cmdline(ccline.cmdpos, utfc_ptr2len((char *)ccline.cmdbuff + ccline.cmdpos));
}
msg_no_more = false;
cursorcmd();
@@ -3270,13 +3522,13 @@ void put_on_cmdline(char_u *str, int len, int redraw)
} else {
// Count nr of characters in the new string.
m = 0;
- for (i = 0; i < len; i += utfc_ptr2len(str + i)) {
+ for (i = 0; i < len; i += utfc_ptr2len((char *)str + i)) {
m++;
}
// Count nr of bytes in cmdline that are overwritten by these
// characters.
for (i = ccline.cmdpos; i < ccline.cmdlen && m > 0;
- i += utfc_ptr2len(ccline.cmdbuff + i)) {
+ i += utfc_ptr2len((char *)ccline.cmdbuff + i)) {
m--;
}
if (i < ccline.cmdlen) {
@@ -3294,17 +3546,17 @@ void put_on_cmdline(char_u *str, int len, int redraw)
// When the inserted text starts with a composing character,
// backup to the character before it. There could be two of them.
i = 0;
- c = utf_ptr2char(ccline.cmdbuff + ccline.cmdpos);
+ c = utf_ptr2char((char *)ccline.cmdbuff + ccline.cmdpos);
while (ccline.cmdpos > 0 && utf_iscomposing(c)) {
i = utf_head_off(ccline.cmdbuff, ccline.cmdbuff + ccline.cmdpos - 1) + 1;
ccline.cmdpos -= i;
len += i;
- c = utf_ptr2char(ccline.cmdbuff + ccline.cmdpos);
+ c = utf_ptr2char((char *)ccline.cmdbuff + ccline.cmdpos);
}
if (i == 0 && ccline.cmdpos > 0 && arabic_maycombine(c)) {
// Check the previous character for Arabic combining pair.
i = utf_head_off(ccline.cmdbuff, ccline.cmdbuff + ccline.cmdpos - 1) + 1;
- if (arabic_combine(utf_ptr2char(ccline.cmdbuff + ccline.cmdpos - i), c)) {
+ if (arabic_combine(utf_ptr2char((char *)ccline.cmdbuff + ccline.cmdpos - i), c)) {
ccline.cmdpos -= i;
len += i;
} else {
@@ -3313,7 +3565,7 @@ void put_on_cmdline(char_u *str, int len, int redraw)
}
if (i != 0) {
// Also backup the cursor position.
- i = ptr2cells(ccline.cmdbuff + ccline.cmdpos);
+ i = ptr2cells((char *)ccline.cmdbuff + ccline.cmdpos);
ccline.cmdspos -= i;
msg_col -= i;
if (msg_col < 0) {
@@ -3352,7 +3604,7 @@ void put_on_cmdline(char_u *str, int len, int redraw)
if (ccline.cmdspos + c < m) {
ccline.cmdspos += c;
}
- c = utfc_ptr2len(ccline.cmdbuff + ccline.cmdpos) - 1;
+ c = utfc_ptr2len((char *)ccline.cmdbuff + ccline.cmdpos) - 1;
if (c > len - i - 1) {
c = len - i - 1;
}
@@ -3366,53 +3618,23 @@ void put_on_cmdline(char_u *str, int len, int redraw)
}
}
-/*
- * Save ccline, because obtaining the "=" register may execute "normal :cmd"
- * and overwrite it. But get_cmdline_str() may need it, thus make it
- * available globally in prev_ccline.
- */
+/// Save ccline, because obtaining the "=" register may execute "normal :cmd"
+/// and overwrite it.
static void save_cmdline(struct cmdline_info *ccp)
{
*ccp = ccline;
+ memset(&ccline, 0, sizeof(struct cmdline_info));
ccline.prev_ccline = ccp;
- ccline.cmdbuff = NULL;
- ccline.cmdprompt = NULL;
- ccline.xpc = NULL;
- ccline.special_char = NUL;
- ccline.level = 0;
+ ccline.cmdbuff = NULL; // signal that ccline is not in use
}
-/*
- * Restore ccline after it has been saved with save_cmdline().
- */
+/// Restore ccline after it has been saved with save_cmdline().
static void restore_cmdline(struct cmdline_info *ccp)
FUNC_ATTR_NONNULL_ALL
{
ccline = *ccp;
}
-/*
- * Save the command line into allocated memory. Returns a pointer to be
- * passed to restore_cmdline_alloc() later.
- */
-char_u *save_cmdline_alloc(void)
- FUNC_ATTR_NONNULL_RET
-{
- struct cmdline_info *p = xmalloc(sizeof(struct cmdline_info));
- save_cmdline(p);
- return (char_u *)p;
-}
-
-/*
- * Restore the command line from the return value of save_cmdline_alloc().
- */
-void restore_cmdline_alloc(char_u *p)
- FUNC_ATTR_NONNULL_ALL
-{
- restore_cmdline((struct cmdline_info *)p);
- xfree(p);
-}
-
/// Paste a yank register into the command line.
/// Used by CTRL-R command in command-line mode.
/// insert_reg() can't be used here, because special characters from the
@@ -3428,7 +3650,6 @@ static bool cmdline_paste(int regname, bool literally, bool remcr)
char_u *arg;
char_u *p;
bool allocated;
- struct cmdline_info save_ccline;
// check for valid regname; also accept special characters for CTRL-R in
// the command line
@@ -3445,14 +3666,11 @@ static bool cmdline_paste(int regname, bool literally, bool remcr)
return FAIL;
}
-
- // Need to save and restore ccline. And set "textlock" to avoid nasty
- // things like going to another buffer when evaluating an expression.
- save_cmdline(&save_ccline);
+ // Need to set "textlock" to avoid nasty things like going to another
+ // buffer when evaluating an expression.
textlock++;
const bool i = get_spec_reg(regname, &arg, &allocated, true);
textlock--;
- restore_cmdline(&save_ccline);
if (i) {
// Got the value of a special register in "arg".
@@ -3470,7 +3688,7 @@ static bool cmdline_paste(int regname, bool literally, bool remcr)
// Locate start of last word in the cmd buffer.
for (w = ccline.cmdbuff + ccline.cmdpos; w > ccline.cmdbuff;) {
len = utf_head_off(ccline.cmdbuff, w - 1) + 1;
- if (!vim_iswordc(utf_ptr2char(w - len))) {
+ if (!vim_iswordc(utf_ptr2char((char *)w - len))) {
break;
}
w -= len;
@@ -3603,7 +3821,7 @@ void redrawcmd(void)
msg_no_more = TRUE;
draw_cmdline(0, ccline.cmdlen);
msg_clr_eos();
- msg_no_more = FALSE;
+ msg_no_more = false;
ccline.cmdspos = cmd_screencol(ccline.cmdpos);
@@ -3631,7 +3849,7 @@ void compute_cmdrow(void)
} else {
win_T *wp = lastwin_nofloating();
cmdline_row = wp->w_winrow + wp->w_height
- + wp->w_status_height;
+ + wp->w_hsep_height + wp->w_status_height + global_stl_height();
}
lines_left = cmdline_row;
}
@@ -3651,7 +3869,7 @@ static void cursorcmd(void)
}
if (cmdmsg_rl) {
- msg_row = cmdline_row + (ccline.cmdspos / (Columns - 1));
+ msg_row = cmdline_row + (ccline.cmdspos / (Columns - 1));
msg_col = Columns - (ccline.cmdspos % (Columns - 1)) - 1;
if (msg_row <= 0) {
msg_row = Rows - 1;
@@ -3670,7 +3888,7 @@ static void cursorcmd(void)
static void cmd_cursor_goto(int row, int col)
{
ScreenGrid *grid = &msg_grid_adj;
- screen_adjust_grid(&grid, &row, &col);
+ grid_adjust(&grid, &row, &col);
ui_grid_cursor_goto(grid->handle, row, col);
}
@@ -3770,7 +3988,7 @@ static int nextwild(expand_T *xp, int type, int options, int escape)
ui_flush();
}
- i = (int)(xp->xp_pattern - ccline.cmdbuff);
+ i = (int)((char_u *)xp->xp_pattern - ccline.cmdbuff);
assert(ccline.cmdpos >= i);
xp->xp_pattern_len = (size_t)ccline.cmdpos - (size_t)i;
@@ -3779,7 +3997,7 @@ static int nextwild(expand_T *xp, int type, int options, int escape)
p2 = ExpandOne(xp, NULL, NULL, 0, type);
} else {
// Translate string into pattern and expand it.
- p1 = addstar(xp->xp_pattern, xp->xp_pattern_len, xp->xp_context);
+ p1 = addstar((char_u *)xp->xp_pattern, xp->xp_pattern_len, xp->xp_context);
const int use_options = (
options
| WILD_HOME_REPLACE
@@ -3793,7 +4011,7 @@ static int nextwild(expand_T *xp, int type, int options, int escape)
// xp->xp_pattern might have been modified by ExpandOne (for example,
// in lua completion), so recompute the pattern index and length
- i = (int)(xp->xp_pattern - ccline.cmdbuff);
+ i = (int)((char_u *)xp->xp_pattern - ccline.cmdbuff);
xp->xp_pattern_len = (size_t)ccline.cmdpos - (size_t)i;
// Longest match: make sure it is not shorter, happens with :help.
@@ -3814,7 +4032,7 @@ static int nextwild(expand_T *xp, int type, int options, int escape)
difflen = (int)STRLEN(p2) - (int)(xp->xp_pattern_len);
if (ccline.cmdlen + difflen + 4 > ccline.cmdbufflen) {
realloc_cmdbuff(ccline.cmdlen + difflen + 4);
- xp->xp_pattern = ccline.cmdbuff + i;
+ xp->xp_pattern = (char *)ccline.cmdbuff + i;
}
assert(ccline.cmdpos <= ccline.cmdlen);
memmove(&ccline.cmdbuff[ccline.cmdpos + difflen],
@@ -3924,13 +4142,13 @@ char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode
compl_selected = findex;
cmdline_pum_display(false);
} else if (p_wmnu) {
- win_redr_status_matches(xp, xp->xp_numfiles, xp->xp_files,
+ win_redr_status_matches(xp, xp->xp_numfiles, (char_u **)xp->xp_files,
findex, cmd_showtail);
}
if (findex == -1) {
return vim_strsave(orig_save);
}
- return vim_strsave(xp->xp_files[findex]);
+ return vim_strsave((char_u *)xp->xp_files[findex]);
} else {
return NULL;
}
@@ -3940,12 +4158,12 @@ char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode
ss = vim_strsave(orig_save ? orig_save : (char_u *)"");
} else if (mode == WILD_APPLY) {
ss = vim_strsave(findex == -1 ? (orig_save ? orig_save : (char_u *)"") :
- xp->xp_files[findex]);
+ (char_u *)xp->xp_files[findex]);
}
// free old names
if (xp->xp_numfiles != -1 && mode != WILD_ALL && mode != WILD_LONGEST) {
- FreeWild(xp->xp_numfiles, xp->xp_files);
+ FreeWild(xp->xp_numfiles, (char_u **)xp->xp_files);
xp->xp_numfiles = -1;
XFREE_CLEAR(orig_save);
}
@@ -3963,7 +4181,7 @@ char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode
/*
* Do the expansion.
*/
- if (ExpandFromContext(xp, str, &xp->xp_numfiles, &xp->xp_files,
+ if (ExpandFromContext(xp, str, &xp->xp_numfiles, (char_u ***)&xp->xp_files,
options) == FAIL) {
#ifdef FNAME_ILLEGAL
/* Illegal file name has been silently skipped. But when there
@@ -3980,7 +4198,7 @@ char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode
}
} else {
// Escape the matches for use on the command line.
- ExpandEscape(xp, str, xp->xp_numfiles, xp->xp_files, options);
+ ExpandEscape(xp, str, xp->xp_numfiles, (char_u **)xp->xp_files, options);
/*
* Check for matching suffixes in file names.
@@ -4001,9 +4219,9 @@ char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode
* expand_wildcards, only need to check the first two.
*/
non_suf_match = 0;
- for (i = 0; i < 2; ++i) {
- if (match_suffix(xp->xp_files[i])) {
- ++non_suf_match;
+ for (i = 0; i < 2; i++) {
+ if (match_suffix((char_u *)xp->xp_files[i])) {
+ non_suf_match++;
}
}
}
@@ -4020,7 +4238,7 @@ char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode
}
}
if (!(non_suf_match != 1 && mode == WILD_EXPAND_FREE)) {
- ss = vim_strsave(xp->xp_files[0]);
+ ss = vim_strsave((char_u *)xp->xp_files[0]);
}
}
}
@@ -4055,7 +4273,7 @@ char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode
}
}
- ss = (char_u *)xstrndup((char *)xp->xp_files[0], len);
+ ss = (char_u *)xstrndup(xp->xp_files[0], len);
findex = -1; // next p_wc gets first one
}
@@ -4105,7 +4323,7 @@ void ExpandInit(expand_T *xp)
void ExpandCleanup(expand_T *xp)
{
if (xp->xp_numfiles >= 0) {
- FreeWild(xp->xp_numfiles, xp->xp_files);
+ FreeWild(xp->xp_numfiles, (char_u **)xp->xp_files);
xp->xp_numfiles = -1;
}
}
@@ -4114,6 +4332,7 @@ void ExpandEscape(expand_T *xp, char_u *str, int numfiles, char_u **files, int o
{
int i;
char_u *p;
+ const int vse_what = xp->xp_context == EXPAND_BUFFERS ? VSE_BUFFER : VSE_NONE;
/*
* May change home directory back to "~"
@@ -4145,10 +4364,10 @@ void ExpandEscape(expand_T *xp, char_u *str, int numfiles, char_u **files, int o
#endif
}
#ifdef BACKSLASH_IN_FILENAME
- p = (char_u *)vim_strsave_fnameescape((const char *)files[i], false);
+ p = (char_u *)vim_strsave_fnameescape((const char *)files[i], vse_what);
#else
p = (char_u *)vim_strsave_fnameescape((const char *)files[i],
- xp->xp_shell);
+ xp->xp_shell ? VSE_SHELL : vse_what);
#endif
xfree(files[i]);
files[i] = p;
@@ -4180,25 +4399,30 @@ void ExpandEscape(expand_T *xp, char_u *str, int numfiles, char_u **files, int o
}
}
-/// Escape special characters in a file name for use as a command argument
+/// Escape special characters in "fname", depending on "what":
///
/// @param[in] fname File name to escape.
-/// @param[in] shell What to escape for: if false, escapes for VimL command,
-/// if true then it escapes for a shell command.
+/// @param[in] what What to escape for:
+/// - VSE_NONE: for when used as a file name argument after a Vim command.
+/// - VSE_SHELL: for a shell command.
+/// - VSE_BUFFER: for the ":buffer" command.
///
/// @return [allocated] escaped file name.
-char *vim_strsave_fnameescape(const char *const fname, const bool shell FUNC_ATTR_UNUSED)
+char *vim_strsave_fnameescape(const char *const fname, const int what)
FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL
{
#ifdef BACKSLASH_IN_FILENAME
# define PATH_ESC_CHARS " \t\n*?[{`%#'\"|!<"
+# define BUFFER_ESC_CHARS ((char_u *)" \t\n*?[`%#'\"|!<")
char_u buf[sizeof(PATH_ESC_CHARS)];
int j = 0;
- // Don't escape '[', '{' and '!' if they are in 'isfname'.
- for (const char *s = PATH_ESC_CHARS; *s != NUL; s++) {
- if ((*s != '[' && *s != '{' && *s != '!') || !vim_isfilec(*s)) {
- buf[j++] = *s;
+ // Don't escape '[', '{' and '!' if they are in 'isfname' and for the
+ // ":buffer" command.
+ for (const char *p = what == VSE_BUFFER ? BUFFER_ESC_CHARS : PATH_ESC_CHARS;
+ *p != NUL; p++) {
+ if ((*p != '[' && *p != '{' && *p != '!') || !vim_isfilec(*p)) {
+ buf[j++] = *p;
}
}
buf[j] = NUL;
@@ -4207,9 +4431,12 @@ char *vim_strsave_fnameescape(const char *const fname, const bool shell FUNC_ATT
#else
# define PATH_ESC_CHARS ((char_u *)" \t\n*?[{`$\\%#'\"|!<")
# define SHELL_ESC_CHARS ((char_u *)" \t\n*?[{`$\\%#'\"|!<>();&")
+# define BUFFER_ESC_CHARS ((char_u *)" \t\n*?[`$\\%#'\"|!<")
char *p =
- (char *)vim_strsave_escaped((const char_u *)fname, (shell ? SHELL_ESC_CHARS : PATH_ESC_CHARS));
- if (shell && csh_like_shell()) {
+ (char *)vim_strsave_escaped((const char_u *)fname,
+ what == VSE_SHELL ? SHELL_ESC_CHARS
+ : what == VSE_BUFFER ? BUFFER_ESC_CHARS : PATH_ESC_CHARS);
+ if (what == VSE_SHELL && csh_like_shell()) {
// For csh and similar shells need to put two backslashes before '!'.
// One is taken by Vim, one by the shell.
char *s = (char *)vim_strsave_escaped((const char_u *)p,
@@ -4246,14 +4473,13 @@ static void escape_fname(char_u **pp)
*/
void tilde_replace(char_u *orig_pat, int num_files, char_u **files)
{
- int i;
- char_u *p;
+ char *p;
if (orig_pat[0] == '~' && vim_ispathsep(orig_pat[1])) {
- for (i = 0; i < num_files; ++i) {
- p = home_replace_save(NULL, files[i]);
+ for (int i = 0; i < num_files; i++) {
+ p = home_replace_save(NULL, (char *)files[i]);
xfree(files[i]);
- files[i] = p;
+ files[i] = (char_u *)p;
}
}
}
@@ -4294,7 +4520,7 @@ static int showmatches(expand_T *xp, int wildmenu)
}
} else {
num_files = xp->xp_numfiles;
- files_found = xp->xp_files;
+ files_found = (char_u **)xp->xp_files;
showtail = cmd_showtail;
}
@@ -4312,7 +4538,7 @@ static int showmatches(expand_T *xp, int wildmenu)
compl_match_array[i].pum_text = L_SHOWFILE(i);
}
char_u *endpos = (showtail
- ? sm_gettail(xp->xp_pattern, true) : xp->xp_pattern);
+ ? sm_gettail((char_u *)xp->xp_pattern, true) : (char_u *)xp->xp_pattern);
if (ui_has(kUICmdline)) {
compl_startcol = (int)(endpos - ccline.cmdbuff);
} else {
@@ -4344,10 +4570,10 @@ static int showmatches(expand_T *xp, int wildmenu)
if (!showtail && (xp->xp_context == EXPAND_FILES
|| xp->xp_context == EXPAND_SHELLCMD
|| xp->xp_context == EXPAND_BUFFERS)) {
- home_replace(NULL, files_found[i], NameBuff, MAXPATHL, TRUE);
- j = vim_strsize(NameBuff);
+ home_replace(NULL, (char *)files_found[i], (char *)NameBuff, MAXPATHL, true);
+ j = vim_strsize((char *)NameBuff);
} else {
- j = vim_strsize(L_SHOWFILE(i));
+ j = vim_strsize((char *)L_SHOWFILE(i));
}
if (j > maxlen) {
maxlen = j;
@@ -4414,8 +4640,7 @@ static int showmatches(expand_T *xp, int wildmenu)
if (showtail) {
p = L_SHOWFILE(k);
} else {
- home_replace(NULL, files_found[k], NameBuff, MAXPATHL,
- TRUE);
+ home_replace(NULL, (char *)files_found[k], (char *)NameBuff, MAXPATHL, true);
p = NameBuff;
}
} else {
@@ -4466,7 +4691,7 @@ char_u *sm_gettail(char_u *s, bool eager)
#endif
) {
if (eager) {
- t = p+1;
+ t = p + 1;
} else {
had_sep = true;
}
@@ -4496,18 +4721,18 @@ static int expand_showtail(expand_T *xp)
return FALSE;
}
- end = path_tail(xp->xp_pattern);
- if (end == xp->xp_pattern) { // there is no path separator
- return FALSE;
+ end = (char_u *)path_tail(xp->xp_pattern);
+ if (end == (char_u *)xp->xp_pattern) { // there is no path separator
+ return false;
}
- for (s = xp->xp_pattern; s < end; s++) {
- /* Skip escaped wildcards. Only when the backslash is not a path
- * separator, on DOS the '*' "path\*\file" must not be skipped. */
+ for (s = (char_u *)xp->xp_pattern; s < end; s++) {
+ // Skip escaped wildcards. Only when the backslash is not a path
+ // separator, on DOS the '*' "path\*\file" must not be skipped.
if (rem_backslash(s)) {
- ++s;
- } else if (vim_strchr((char_u *)"*?[", *s) != NULL) {
- return FALSE;
+ s++;
+ } else if (vim_strchr("*?[", *s) != NULL) {
+ return false;
}
}
return TRUE;
@@ -4623,7 +4848,7 @@ char_u *addstar(char_u *fname, size_t len, int context)
* ` could be anywhere in the file name.
* When the name ends in '$' don't add a star, remove the '$'.
*/
- tail = path_tail(retval);
+ tail = (char_u *)path_tail((char *)retval);
ends_in_star = (len > 0 && retval[len - 1] == '*');
#ifndef BACKSLASH_IN_FILENAME
for (ssize_t k = (ssize_t)len - 2; k >= 0; k--) {
@@ -4635,8 +4860,8 @@ char_u *addstar(char_u *fname, size_t len, int context)
#endif
if ((*retval != '~' || tail != retval)
&& !ends_in_star
- && vim_strchr(tail, '$') == NULL
- && vim_strchr(retval, '`') == NULL) {
+ && vim_strchr((char *)tail, '$') == NULL
+ && vim_strchr((char *)retval, '`') == NULL) {
retval[len++] = '*';
} else if (len > 0 && retval[len - 1] == '$') {
--len;
@@ -4689,7 +4914,7 @@ char_u *addstar(char_u *fname, size_t len, int context)
* EXPAND_ENV_VARS Complete environment variable names
* EXPAND_USER Complete user names
*/
-static void set_expand_context(expand_T *xp)
+void set_expand_context(expand_T *xp)
{
// only expansion for ':', '>' and '=' command-lines
if (ccline.cmdfirstc != ':'
@@ -4721,11 +4946,11 @@ void set_cmd_context(expand_T *xp, char_u *str, int len, int col, int use_ccline
if (use_ccline && ccline.cmdfirstc == '=') {
// pass CMD_SIZE because there is no real command
- set_context_for_expression(xp, str, CMD_SIZE);
+ set_context_for_expression(xp, (char *)str, CMD_SIZE);
} else if (use_ccline && ccline.input_fn) {
xp->xp_context = ccline.xp_context;
- xp->xp_pattern = ccline.cmdbuff;
- xp->xp_arg = ccline.xp_arg;
+ xp->xp_pattern = (char *)ccline.cmdbuff;
+ xp->xp_arg = (char *)ccline.xp_arg;
} else {
while (nextcomm != NULL) {
nextcomm = set_one_cmd_context(xp, nextcomm);
@@ -4734,7 +4959,7 @@ void set_cmd_context(expand_T *xp, char_u *str, int len, int col, int use_ccline
/* Store the string here so that call_user_expand_func() can get to them
* easily. */
- xp->xp_line = str;
+ xp->xp_line = (char *)str;
xp->xp_col = col;
str[col] = old_char;
@@ -4769,9 +4994,9 @@ int expand_cmdline(expand_T *xp, char_u *str, int col, int *matchcount, char_u *
}
// add star to file name, or convert to regexp if not exp. files.
- assert((str + col) - xp->xp_pattern >= 0);
- xp->xp_pattern_len = (size_t)((str + col) - xp->xp_pattern);
- file_str = addstar(xp->xp_pattern, xp->xp_pattern_len, xp->xp_context);
+ assert((str + col) - (char_u *)xp->xp_pattern >= 0);
+ xp->xp_pattern_len = (size_t)((str + col) - (char_u *)xp->xp_pattern);
+ file_str = addstar((char_u *)xp->xp_pattern, xp->xp_pattern_len, xp->xp_context);
if (p_wic) {
options += WILD_ICASE;
@@ -4839,7 +5064,7 @@ static void cleanup_help_tags(int num_file, char_u **file)
}
}
-typedef char_u *(*ExpandFunc)(expand_T *, int);
+typedef char *(*ExpandFunc)(expand_T *, int);
/// Do the expansion based on xp->xp_context and "pat".
///
@@ -4938,8 +5163,8 @@ static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char_u **
if (xp->xp_context == EXPAND_HELP) {
/* With an empty argument we would get all the help tags, which is
* very slow. Get matches for "help" instead. */
- if (find_help_tags(*pat == NUL ? (char_u *)"help" : pat,
- num_file, file, false) == OK) {
+ if (find_help_tags(*pat == NUL ? "help" : (char *)pat,
+ num_file, (char ***)file, false) == OK) {
cleanup_help_tags(*num_file, *file);
return OK;
}
@@ -4956,10 +5181,10 @@ static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char_u **
return OK;
}
if (xp->xp_context == EXPAND_BUFFERS) {
- return ExpandBufnames(pat, num_file, file, options);
+ return ExpandBufnames((char *)pat, num_file, (char ***)file, options);
}
if (xp->xp_context == EXPAND_DIFF_BUFFERS) {
- return ExpandBufnames(pat, num_file, file, options | BUF_DIFF_FILTER);
+ return ExpandBufnames((char *)pat, num_file, (char ***)file, options | BUF_DIFF_FILTER);
}
if (xp->xp_context == EXPAND_TAGS
|| xp->xp_context == EXPAND_TAGS_LISTFILES) {
@@ -5008,7 +5233,7 @@ static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char_u **
return nlua_expand_pat(xp, pat, num_file, file);
}
- regmatch.regprog = vim_regcomp(pat, p_magic ? RE_MAGIC : 0);
+ regmatch.regprog = vim_regcomp((char *)pat, p_magic ? RE_MAGIC : 0);
if (regmatch.regprog == NULL) {
return FAIL;
}
@@ -5049,8 +5274,8 @@ static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char_u **
{ EXPAND_SYNTAX, get_syntax_name, true, true },
{ EXPAND_SYNTIME, get_syntime_arg, true, true },
{ EXPAND_HIGHLIGHT, (ExpandFunc)get_highlight_name, true, true },
- { EXPAND_EVENTS, get_event_name, true, true },
- { EXPAND_AUGROUP, get_augroup_name, true, true },
+ { EXPAND_EVENTS, expand_get_event_name, true, true },
+ { EXPAND_AUGROUP, expand_get_augroup_name, true, true },
{ EXPAND_CSCOPE, get_cscope_name, true, true },
{ EXPAND_SIGN, get_sign_name, true, true },
{ EXPAND_PROFILE, get_profile_name, true, true },
@@ -5104,16 +5329,16 @@ static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, int *num_file, cha
char_u *str;
// count the number of matching names
- for (i = 0;; ++i) {
- str = (*func)(xp, i);
+ for (i = 0;; i++) {
+ str = (char_u *)(*func)(xp, i);
if (str == NULL) { // end of list
break;
}
if (*str == NUL) { // skip empty strings
continue;
}
- if (vim_regexec(regmatch, str, (colnr_T)0)) {
- ++count;
+ if (vim_regexec(regmatch, (char *)str, (colnr_T)0)) {
+ count++;
}
}
if (count == 0) {
@@ -5126,14 +5351,14 @@ static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, int *num_file, cha
// copy the matching names into allocated memory
count = 0;
for (i = 0;; i++) {
- str = (*func)(xp, i);
+ str = (char_u *)(*func)(xp, i);
if (str == NULL) { // End of list.
break;
}
if (*str == NUL) { // Skip empty strings.
continue;
}
- if (vim_regexec(regmatch, str, (colnr_T)0)) {
+ if (vim_regexec(regmatch, (char *)str, (colnr_T)0)) {
if (escaped) {
str = vim_strsave_escaped(str, (char_u *)" \t\\.");
} else {
@@ -5225,7 +5450,7 @@ static void expand_shellcmd(char_u *filepat, int *num_file, char_u ***file, int
hashtab_T found_ht;
hash_init(&found_ht);
for (s = path;; s = e) {
- e = vim_strchr(s, ENV_SEPCHAR);
+ e = (char_u *)vim_strchr((char *)s, ENV_SEPCHAR);
if (e == NULL) {
e = s + STRLEN(s);
}
@@ -5306,7 +5531,6 @@ static void *call_user_expand_func(user_expand_func_T user_expand_func, expand_T
typval_T args[4];
char_u *pat = NULL;
const sctx_T save_current_sctx = current_sctx;
- struct cmdline_info save_ccline;
if (xp->xp_arg == NULL || xp->xp_arg[0] == '\0' || xp->xp_line == NULL) {
return NULL;
@@ -5319,24 +5543,19 @@ static void *call_user_expand_func(user_expand_func_T user_expand_func, expand_T
ccline.cmdbuff[ccline.cmdlen] = 0;
}
- pat = vim_strnsave(xp->xp_pattern, xp->xp_pattern_len);
+ pat = vim_strnsave((char_u *)xp->xp_pattern, xp->xp_pattern_len);
args[0].v_type = VAR_STRING;
args[1].v_type = VAR_STRING;
args[2].v_type = VAR_NUMBER;
args[3].v_type = VAR_UNKNOWN;
- args[0].vval.v_string = pat;
+ args[0].vval.v_string = (char *)pat;
args[1].vval.v_string = xp->xp_line;
args[2].vval.v_number = xp->xp_col;
- // Save the cmdline, we don't know what the function may do.
- save_ccline = ccline;
- ccline.cmdbuff = NULL;
- ccline.cmdprompt = NULL;
current_sctx = xp->xp_script_ctx;
- void *const ret = user_expand_func(xp->xp_arg, 3, args);
+ void *const ret = user_expand_func((char_u *)xp->xp_arg, 3, args);
- ccline = save_ccline;
current_sctx = save_current_sctx;
if (ccline.cmdbuff != NULL) {
ccline.cmdbuff[ccline.cmdlen] = keep;
@@ -5363,7 +5582,7 @@ static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file,
ga_init(&ga, (int)sizeof(char *), 3);
for (char_u *s = retstr; *s != NUL; s = e) {
- e = vim_strchr(s, '\n');
+ e = (char_u *)vim_strchr((char *)s, '\n');
if (e == NULL) {
e = s + STRLEN(s);
}
@@ -5371,7 +5590,7 @@ static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file,
*e = NUL;
const bool skip = xp->xp_pattern[0]
- && vim_regexec(regmatch, s, (colnr_T)0) == 0;
+ && vim_regexec(regmatch, (char *)s, (colnr_T)0) == 0;
*e = keep;
if (!skip) {
GA_APPEND(char_u *, &ga, vim_strnsave(s, (size_t)(e - s)));
@@ -5582,8 +5801,8 @@ static int ExpandPackAddDir(char_u *pat, int *num_file, char_u ***file)
for (int i = 0; i < ga.ga_len; i++) {
char_u *match = ((char_u **)ga.ga_data)[i];
- s = path_tail(match);
- memmove(match, s, STRLEN(s)+1);
+ s = (char_u *)path_tail((char *)match);
+ memmove(match, s, STRLEN(s) + 1);
}
if (GA_EMPTY(&ga)) {
@@ -5599,7 +5818,6 @@ static int ExpandPackAddDir(char_u *pat, int *num_file, char_u ***file)
return OK;
}
-
/// Expand `file` for all comma-separated directories in `path`.
/// Adds matches to `ga`.
void globpath(char_u *path, char_u *file, garray_T *ga, int expand_options)
@@ -5613,7 +5831,7 @@ void globpath(char_u *path, char_u *file, garray_T *ga, int expand_options)
// Loop over all entries in {path}.
while (*path != NUL) {
// Copy one item of the path to buf[] and concatenate the file name.
- copy_option_part(&path, buf, MAXPATHL, ",");
+ copy_option_part((char **)&path, (char *)buf, MAXPATHL, ",");
if (STRLEN(buf) + STRLEN(file) + 2 < MAXPATHL) {
add_pathsep((char *)buf);
STRCAT(buf, file); // NOLINT
@@ -5640,7 +5858,6 @@ void globpath(char_u *path, char_u *file, garray_T *ga, int expand_options)
xfree(buf);
}
-
/*********************************
* Command line history stuff *
*********************************/
@@ -5689,7 +5906,7 @@ static char *(history_names[]) =
* Function given to ExpandGeneric() to obtain the possible first
* arguments of the ":history command.
*/
-static char_u *get_history_arg(expand_T *xp, int idx)
+static char *get_history_arg(expand_T *xp, int idx)
{
static char_u compl[2] = { NUL, NUL };
char *short_names = ":=@>?/";
@@ -5698,13 +5915,13 @@ static char_u *get_history_arg(expand_T *xp, int idx)
if (idx < short_names_count) {
compl[0] = (char_u)short_names[idx];
- return compl;
+ return (char *)compl;
}
if (idx < short_names_count + history_name_count) {
- return (char_u *)history_names[idx - short_names_count];
+ return history_names[idx - short_names_count];
}
if (idx == short_names_count + history_name_count) {
- return (char_u *)"all";
+ return "all";
}
return NULL;
}
@@ -5864,7 +6081,7 @@ HistoryType get_histtype(const char *const name, const size_t len, const bool re
}
}
- if (vim_strchr((char_u *)":=@>?/", name[0]) != NULL && len == 1) {
+ if (vim_strchr(":=@>?/", name[0]) != NULL && len == 1) {
return hist_char2type(name[0]);
}
@@ -5874,7 +6091,7 @@ HistoryType get_histtype(const char *const name, const size_t len, const bool re
static int last_maptick = -1; // last seen maptick
/// Add the given string to the given history. If the string is already in the
-/// history then it is moved to the front. "histype" may be one of he HIST_
+/// history then it is moved to the front. "histype" may be one of the HIST_
/// values.
///
/// @parma in_map consider maptick when inside a mapping
@@ -5888,7 +6105,7 @@ void add_to_history(int histype, char_u *new_entry, int in_map, int sep)
}
assert(histype != HIST_DEFAULT);
- if (cmdmod.keeppatterns && histype == HIST_SEARCH) {
+ if ((cmdmod.cmod_flags & CMOD_KEEPPATTERNS) && histype == HIST_SEARCH) {
return;
}
@@ -5930,7 +6147,6 @@ void add_to_history(int histype, char_u *new_entry, int in_map, int sep)
}
}
-
/*
* Get identifier of newest history entry.
* "histype" may be one of the HIST_ values.
@@ -5945,14 +6161,11 @@ int get_history_idx(int histype)
return history[histype][hisidx[histype]].hisnum;
}
-
-/*
- * Get pointer to the command line info to use. cmdline_paste() may clear
- * ccline and put the previous value in prev_ccline.
- */
+/// Get pointer to the command line info to use. save_cmdline() may clear
+/// ccline and put the previous value in ccline.prev_ccline.
static struct cmdline_info *get_ccline_ptr(void)
{
- if ((State & CMDLINE) == 0) {
+ if ((State & MODE_CMDLINE) == 0) {
return NULL;
} else if (ccline.cmdbuff != NULL) {
return &ccline;
@@ -5963,6 +6176,25 @@ static struct cmdline_info *get_ccline_ptr(void)
}
}
+/// Get the current command-line completion type.
+char_u *get_cmdline_completion(void)
+{
+ if (cmdline_star > 0) {
+ return NULL;
+ }
+ struct cmdline_info *p = get_ccline_ptr();
+
+ if (p != NULL && p->xpc != NULL) {
+ set_expand_context(p->xpc);
+ char *cmd_compl = get_user_cmd_complete(p->xpc, p->xpc->xp_context);
+ if (cmd_compl != NULL) {
+ return vim_strsave((char_u *)cmd_compl);
+ }
+ }
+
+ return NULL;
+}
+
/*
* Get the current command line in allocated memory.
* Only works when the command line is being edited.
@@ -5997,6 +6229,17 @@ int get_cmdline_pos(void)
return p->cmdpos;
}
+/// Get the command line cursor screen position.
+int get_cmdline_screen_pos(void)
+{
+ struct cmdline_info *p = get_ccline_ptr();
+
+ if (p == NULL) {
+ return -1;
+ }
+ return p->cmdspos;
+}
+
/*
* Set the command line byte position to "pos". Zero is the first position.
* Only works when the command line is being edited.
@@ -6067,7 +6310,7 @@ static int calc_hist_idx(int histype, int num)
wrapped = TRUE;
}
}
- if (hist[i].hisnum == num && hist[i].hisstr != NULL) {
+ if (i >= 0 && hist[i].hisnum == num && hist[i].hisstr != NULL) {
return i;
}
} else if (-num <= hislen) {
@@ -6136,7 +6379,7 @@ int del_history_entry(int histype, char_u *str)
&& histype < HIST_COUNT
&& *str != NUL
&& (idx = hisidx[histype]) >= 0
- && (regmatch.regprog = vim_regcomp(str, RE_MAGIC + RE_STRING))
+ && (regmatch.regprog = vim_regcomp((char *)str, RE_MAGIC + RE_STRING))
!= NULL) {
i = last = idx;
do {
@@ -6144,7 +6387,7 @@ int del_history_entry(int histype, char_u *str)
if (hisptr->hisstr == NULL) {
break;
}
- if (vim_regexec(&regmatch, hisptr->hisstr, (colnr_T)0)) {
+ if (vim_regexec(&regmatch, (char *)hisptr->hisstr, (colnr_T)0)) {
found = true;
hist_free_entry(hisptr);
} else {
@@ -6217,20 +6460,20 @@ int get_list_range(char_u **str, int *num1, int *num2)
int first = false;
varnumber_T num;
- *str = skipwhite(*str);
+ *str = (char_u *)skipwhite((char *)(*str));
if (**str == '-' || ascii_isdigit(**str)) { // parse "from" part of range
vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0, false);
*str += len;
*num1 = (int)num;
first = true;
}
- *str = skipwhite(*str);
+ *str = (char_u *)skipwhite((char *)(*str));
if (**str == ',') { // parse "to" part of range
- *str = skipwhite(*str + 1);
+ *str = (char_u *)skipwhite((char *)(*str) + 1);
vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0, false);
if (len > 0) {
*num2 = (int)num;
- *str = skipwhite(*str + len);
+ *str = (char_u *)skipwhite((char *)(*str) + len);
} else if (!first) { // no number given at all
return FAIL;
}
@@ -6253,7 +6496,7 @@ void ex_history(exarg_T *eap)
int idx;
int i, j, k;
char_u *end;
- char_u *arg = eap->arg;
+ char_u *arg = (char_u *)eap->arg;
if (hislen == 0) {
msg(_("'history' option is zero"));
@@ -6263,14 +6506,14 @@ void ex_history(exarg_T *eap)
if (!(ascii_isdigit(*arg) || *arg == '-' || *arg == ',')) {
end = arg;
while (ASCII_ISALPHA(*end)
- || vim_strchr((char_u *)":=@>/?", *end) != NULL) {
+ || vim_strchr(":=@>/?", *end) != NULL) {
end++;
}
histype1 = get_histtype((const char *)arg, (size_t)(end - arg), false);
if (histype1 == HIST_INVALID) {
if (STRNICMP(arg, "all", end - arg) == 0) {
histype1 = 0;
- histype2 = HIST_COUNT-1;
+ histype2 = HIST_COUNT - 1;
} else {
emsg(_(e_trailing));
return;
@@ -6296,10 +6539,10 @@ void ex_history(exarg_T *eap)
j = hisidx1;
k = hisidx2;
if (j < 0) {
- j = (-j > hislen) ? 0 : hist[(hislen+j+idx+1) % hislen].hisnum;
+ j = (-j > hislen) ? 0 : hist[(hislen + j + idx + 1) % hislen].hisnum;
}
if (k < 0) {
- k = (-k > hislen) ? 0 : hist[(hislen+k+idx+1) % hislen].hisnum;
+ k = (-k > hislen) ? 0 : hist[(hislen + k + idx + 1) % hislen].hisnum;
}
if (idx >= 0 && j <= k) {
for (i = idx + 1; !got_int; ++i) {
@@ -6311,13 +6554,13 @@ void ex_history(exarg_T *eap)
msg_putchar('\n');
snprintf((char *)IObuff, IOSIZE, "%c%6d ", i == idx ? '>' : ' ',
hist[i].hisnum);
- if (vim_strsize(hist[i].hisstr) > Columns - 10) {
- trunc_string(hist[i].hisstr, IObuff + STRLEN(IObuff),
+ if (vim_strsize((char *)hist[i].hisstr) > Columns - 10) {
+ trunc_string((char *)hist[i].hisstr, (char *)IObuff + STRLEN(IObuff),
Columns - 10, IOSIZE - (int)STRLEN(IObuff));
} else {
STRCAT(IObuff, hist[i].hisstr);
}
- msg_outtrans(IObuff);
+ msg_outtrans((char *)IObuff);
ui_flush();
}
if (i == idx) {
@@ -6349,6 +6592,11 @@ int hist_type2char(int type)
return NUL;
}
+void cmdline_init(void)
+{
+ memset(&ccline, 0, sizeof(struct cmdline_info));
+}
+
/// Open a window on the current command line and history. Allow editing in
/// the window. Returns when the window is closed.
/// Returns:
@@ -6357,7 +6605,6 @@ int hist_type2char(int type)
/// K_IGNORE if editing continues
static int open_cmdwin(void)
{
- struct cmdline_info save_ccline;
bufref_T old_curbuf;
bufref_T bufref;
win_T *old_curwin = curwin;
@@ -6365,15 +6612,15 @@ static int open_cmdwin(void)
int i;
linenr_T lnum;
garray_T winsizes;
- char_u typestr[2];
+ char typestr[2];
int save_restart_edit = restart_edit;
int save_State = State;
bool save_exmode = exmode_active;
int save_cmdmsg_rl = cmdmsg_rl;
+ // Can't do this when text or buffer is locked.
// Can't do this recursively. Can't do it when typing a password.
- if (cmdwin_type != 0
- || cmdline_star > 0) {
+ if (text_or_buf_locked() || cmdwin_type != 0 || cmdline_star > 0) {
beep_flush();
return K_IGNORE;
}
@@ -6388,12 +6635,13 @@ static int open_cmdwin(void)
pum_undisplay(true);
// don't use a new tab page
- cmdmod.tab = 0;
- cmdmod.noswapfile = 1;
+ cmdmod.cmod_tab = 0;
+ cmdmod.cmod_flags |= CMOD_NOSWAPFILE;
// Create a window for the command-line buffer.
if (win_split((int)p_cwh, WSP_BOT) == FAIL) {
beep_flush();
+ ga_clear(&winsizes);
return K_IGNORE;
}
cmdwin_type = get_cmdline_type();
@@ -6417,8 +6665,8 @@ static int open_cmdwin(void)
const int histtype = hist_char2type(cmdwin_type);
if (histtype == HIST_CMD || histtype == HIST_DEBUG) {
if (p_wc == TAB) {
- add_map((char_u *)"<buffer> <Tab> <C-X><C-V>", INSERT, false);
- add_map((char_u *)"<buffer> <Tab> a<C-X><C-V>", NORMAL, false);
+ add_map("<Tab>", "<C-X><C-V>", MODE_INSERT, true);
+ add_map("<Tab>", "a<C-X><C-V>", MODE_NORMAL, true);
}
set_option_value("ft", 0L, "vim", OPT_LOCAL);
}
@@ -6439,7 +6687,7 @@ static int open_cmdwin(void)
i = 0;
}
if (history[histtype][i].hisstr != NULL) {
- ml_append(lnum++, history[histtype][i].hisstr, (colnr_T)0, false);
+ ml_append(lnum++, (char *)history[histtype][i].hisstr, (colnr_T)0, false);
}
} while (i != hisidx[histtype]);
}
@@ -6447,7 +6695,7 @@ static int open_cmdwin(void)
// Replace the empty last line with the current command-line and put the
// cursor there.
- ml_replace(curbuf->b_ml.ml_line_count, ccline.cmdbuff, true);
+ ml_replace(curbuf->b_ml.ml_line_count, (char *)ccline.cmdbuff, true);
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
curwin->w_cursor.col = ccline.cmdpos;
changed_line_abv_curs();
@@ -6458,20 +6706,17 @@ static int open_cmdwin(void)
}
redraw_later(curwin, SOME_VALID);
- // Save the command line info, can be used recursively.
- save_cmdline(&save_ccline);
-
// No Ex mode here!
exmode_active = false;
- State = NORMAL;
+ State = MODE_NORMAL;
setmouse();
// Reset here so it can be set by a CmdWinEnter autocommand.
cmdwin_result = 0;
// Trigger CmdwinEnter autocommands.
- typestr[0] = (char_u)cmdwin_type;
+ typestr[0] = (char)cmdwin_type;
typestr[1] = NUL;
apply_autocmds(EVENT_CMDWINENTER, typestr, typestr, false, curbuf);
if (restart_edit != 0) { // autocmd with ":startinsert"
@@ -6498,8 +6743,6 @@ static int open_cmdwin(void)
// Restore KeyTyped in case it is modified by autocommands
KeyTyped = save_KeyTyped;
- // Restore the command line info.
- restore_cmdline(&save_ccline);
cmdwin_type = 0;
cmdwin_level = 0;
@@ -6563,13 +6806,13 @@ static int open_cmdwin(void)
set_bufref(&bufref, curbuf);
win_goto(old_curwin);
if (win_valid(wp) && wp != curwin) {
- win_close(wp, true);
+ win_close(wp, true, false);
}
// win_close() may have already wiped the buffer when 'bh' is
// set to 'wipe', autocommands may have closed other windows
if (bufref_valid(&bufref) && bufref.br_buf != curbuf) {
- close_buffer(NULL, bufref.br_buf, DOBUF_WIPE, false);
+ close_buffer(NULL, bufref.br_buf, DOBUF_WIPE, false, false);
}
// Restore window sizes.
@@ -6581,12 +6824,19 @@ static int open_cmdwin(void)
cmdmsg_rl = save_cmdmsg_rl;
State = save_State;
- trigger_modechanged();
+ may_trigger_modechanged();
setmouse();
return cmdwin_result;
}
+/// @return true if in the cmdwin, not editing the command line.
+bool is_in_cmdwin(void)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ return cmdwin_type != 0 && get_cmdline_type() == NUL;
+}
+
/// Get script string
///
/// Used for commands which accept either `:command script` or
@@ -6616,13 +6866,10 @@ char *script_get(exarg_T *const eap, size_t *const lenp)
ga_init(&ga, 1, 0x400);
}
- const char *const end_pattern = (
- cmd[2] != NUL
- ? (const char *)skipwhite((const char_u *)cmd + 2)
- : ".");
+ const char *const end_pattern = (cmd[2] != NUL ? (const char *)skipwhite(cmd + 2) : ".");
for (;;) {
- char *const theline = (char *)eap->getline(eap->cstack->cs_looplevel > 0 ? -1 :
- NUL, eap->cookie, 0, true);
+ char *const theline = eap->getline(eap->cstack->cs_looplevel > 0 ? -1 : NUL, eap->cookie, 0,
+ true);
if (theline == NULL || strcmp(end_pattern, theline) == 0) {
xfree(theline);
diff --git a/src/nvim/ex_getln.h b/src/nvim/ex_getln.h
index fe2bf958b5..1cc6faf87c 100644
--- a/src/nvim/ex_getln.h
+++ b/src/nvim/ex_getln.h
@@ -34,6 +34,11 @@
#define WILD_BUFLASTUSED 0x1000
#define BUF_DIFF_FILTER 0x2000
+// flags used by vim_strsave_fnameescape()
+#define VSE_NONE 0
+#define VSE_SHELL 1 ///< escape for a shell command
+#define VSE_BUFFER 2 ///< escape for a ":buffer" command
+
/// Present history tables
typedef enum {
HIST_DEFAULT = -2, ///< Default (current) history.
@@ -48,7 +53,7 @@ typedef enum {
/// Number of history tables
#define HIST_COUNT (HIST_DEBUG + 1)
-typedef char_u *(*CompleteListItemGetter)(expand_T *, int);
+typedef char *(*CompleteListItemGetter)(expand_T *, int);
/// History entry definition
typedef struct hist_entry {
diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c
index a37cad9f2d..6ca6da9cd0 100644
--- a/src/nvim/ex_session.c
+++ b/src/nvim/ex_session.c
@@ -25,9 +25,9 @@
#include "nvim/file_search.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
-#include "nvim/getchar.h"
#include "nvim/globals.h"
-#include "nvim/keymap.h"
+#include "nvim/keycodes.h"
+#include "nvim/mapping.h"
#include "nvim/move.h"
#include "nvim/option.h"
#include "nvim/os/input.h"
@@ -72,7 +72,7 @@ static int ses_winsizes(FILE *fd, int restore_size, win_T *tab_firstwin)
n++;
// restore height when not full height
- if (wp->w_height + wp->w_status_height < topframe->fr_height
+ if (wp->w_height + wp->w_hsep_height + wp->w_status_height < topframe->fr_height
&& (fprintf(fd,
"exe '%dresize ' . ((&lines * %" PRId64
" + %" PRId64 ") / %" PRId64 ")\n",
@@ -98,10 +98,11 @@ static int ses_winsizes(FILE *fd, int restore_size, win_T *tab_firstwin)
return OK;
}
-// Write commands to "fd" to recursively create windows for frame "fr",
-// horizontally and vertically split.
-// After the commands the last window in the frame is the current window.
-// Returns FAIL when writing the commands to "fd" fails.
+/// Write commands to "fd" to recursively create windows for frame "fr",
+/// horizontally and vertically split.
+/// After the commands the last window in the frame is the current window.
+///
+/// @return FAIL when writing the commands to "fd" fails.
static int ses_win_rec(FILE *fd, frame_T *fr)
{
frame_T *frc;
@@ -144,8 +145,9 @@ static int ses_win_rec(FILE *fd, frame_T *fr)
return OK;
}
-// Skip frames that don't contain windows we want to save in the Session.
-// Returns NULL when there none.
+/// Skip frames that don't contain windows we want to save in the Session.
+///
+/// @return NULL when there none.
static frame_T *ses_skipframe(frame_T *fr)
{
frame_T *frc;
@@ -158,8 +160,8 @@ static frame_T *ses_skipframe(frame_T *fr)
return frc;
}
-// Return true if frame "fr" has a window somewhere that we want to save in
-// the Session.
+/// @return true if frame "fr" has a window somewhere that we want to save in
+/// the Session.
static bool ses_do_frame(const frame_T *fr)
FUNC_ATTR_NONNULL_ARG(1)
{
@@ -176,12 +178,16 @@ static bool ses_do_frame(const frame_T *fr)
return false;
}
-/// Return non-zero if window "wp" is to be stored in the Session.
+/// @return non-zero if window "wp" is to be stored in the Session.
static int ses_do_win(win_T *wp)
{
+ // Skip floating windows to avoid issues when restoring the Session. #18432
+ if (wp->w_floating) {
+ return false;
+ }
if (wp->w_buffer->b_fname == NULL
// When 'buftype' is "nofile" can't restore the window contents.
- || (!wp->w_buffer->terminal && bt_nofile(wp->w_buffer))) {
+ || (!wp->w_buffer->terminal && bt_nofilename(wp->w_buffer))) {
return ssop_flags & SSOP_BLANK;
}
if (bt_help(wp->w_buffer)) {
@@ -209,7 +215,7 @@ static int ses_arglist(FILE *fd, char *cmd, garray_T *gap, int fullname, unsigne
}
for (int i = 0; i < gap->ga_len; i++) {
// NULL file names are skipped (only happens when out of memory).
- s = alist_name(&((aentry_T *)gap->ga_data)[i]);
+ s = (char_u *)alist_name(&((aentry_T *)gap->ga_data)[i]);
if (s != NULL) {
if (fullname) {
buf = xmalloc(MAXPATHL);
@@ -229,7 +235,7 @@ static int ses_arglist(FILE *fd, char *cmd, garray_T *gap, int fullname, unsigne
return OK;
}
-/// Gets the buffer name for `buf`.
+/// @return the buffer name for `buf`.
static char *ses_get_fname(buf_T *buf, unsigned *flagp)
{
// Use the short file name if the current directory is known at the time
@@ -242,14 +248,15 @@ static char *ses_get_fname(buf_T *buf, unsigned *flagp)
&& (ssop_flags & (SSOP_CURDIR | SSOP_SESDIR))
&& !p_acd
&& !did_lcd) {
- return (char *)buf->b_sfname;
+ return buf->b_sfname;
}
- return (char *)buf->b_ffname;
+ return buf->b_ffname;
}
/// Write a buffer name to the session file.
/// Also ends the line, if "add_eol" is true.
-/// Returns FAIL if writing fails.
+///
+/// @return FAIL if writing fails.
static int ses_fname(FILE *fd, buf_T *buf, unsigned *flagp, bool add_eol)
{
char *name = ses_get_fname(buf, flagp);
@@ -260,15 +267,15 @@ static int ses_fname(FILE *fd, buf_T *buf, unsigned *flagp, bool add_eol)
return OK;
}
-// Escapes a filename for session writing.
-// Takes care of "slash" flag in 'sessionoptions' and escapes special
-// characters.
-//
-// Returns allocated string or NULL.
+/// Escapes a filename for session writing.
+/// Takes care of "slash" flag in 'sessionoptions' and escapes special
+/// characters.
+///
+/// @return allocated string or NULL.
static char *ses_escape_fname(char *name, unsigned *flagp)
{
char *p;
- char *sname = (char *)home_replace_save(NULL, (char_u *)name);
+ char *sname = home_replace_save(NULL, name);
// Always SSOP_SLASH: change all backslashes to forward slashes.
for (p = sname; *p != NUL; MB_PTR_ADV(p)) {
@@ -278,15 +285,16 @@ static char *ses_escape_fname(char *name, unsigned *flagp)
}
// Escape special characters.
- p = vim_strsave_fnameescape(sname, false);
+ p = vim_strsave_fnameescape(sname, VSE_NONE);
xfree(sname);
return p;
}
-// Write a file name to the session file.
-// Takes care of the "slash" option in 'sessionoptions' and escapes special
-// characters.
-// Returns FAIL if writing fails.
+/// Write a file name to the session file.
+/// Takes care of the "slash" option in 'sessionoptions' and escapes special
+/// characters.
+///
+/// @return FAIL if writing fails.
static int ses_put_fname(FILE *fd, char_u *name, unsigned *flagp)
{
char *p = ses_escape_fname((char *)name, flagp);
@@ -338,14 +346,26 @@ static int put_view(FILE *fd, win_T *wp, int add_edit, unsigned *flagp, int curr
// Edit the file. Skip this when ":next" already did it.
if (add_edit && (!did_next || wp->w_arg_idx_invalid)) {
- char *fname_esc =
- ses_escape_fname(ses_get_fname(wp->w_buffer, flagp), flagp);
- //
- // Load the file.
- //
- if (wp->w_buffer->b_ffname != NULL
- && (!bt_nofile(wp->w_buffer)
- || wp->w_buffer->terminal)) {
+ char *fname_esc = ses_escape_fname(ses_get_fname(wp->w_buffer, flagp), flagp);
+ if (bt_help(wp->w_buffer)) {
+ char *curtag = "";
+
+ // A help buffer needs some options to be set.
+ // First, create a new empty buffer with "buftype=help".
+ // Then ":help" will re-use both the buffer and the window and set
+ // the options, even when "options" is not in 'sessionoptions'.
+ if (0 < wp->w_tagstackidx && wp->w_tagstackidx <= wp->w_tagstacklen) {
+ curtag = (char *)wp->w_tagstack[wp->w_tagstackidx - 1].tagname;
+ }
+
+ if (put_line(fd, "enew | setl bt=help") == FAIL
+ || fprintf(fd, "help %s", curtag) < 0 || put_eol(fd) == FAIL) {
+ return FAIL;
+ }
+ } else if (wp->w_buffer->b_ffname != NULL
+ && (!bt_nofilename(wp->w_buffer) || wp->w_buffer->terminal)) {
+ // Load the file.
+
// Editing a file in this buffer: use ":edit file".
// This may have side effects! (e.g., compressed or network file).
//
@@ -353,7 +373,7 @@ static int put_view(FILE *fd, win_T *wp, int add_edit, unsigned *flagp, int curr
// edit that buffer, to not lose folding information (:edit resets
// folds in other buffers)
if (fprintf(fd,
- "if bufexists(\"%s\") | buffer %s | else | edit %s | endif\n"
+ "if bufexists(fnamemodify(\"%s\", \":p\")) | buffer %s | else | edit %s | endif\n"
// Fixup :terminal buffer name. #7836
"if &buftype ==# 'terminal'\n"
" silent file %s\n"
@@ -497,7 +517,7 @@ static int put_view(FILE *fd, win_T *wp, int add_edit, unsigned *flagp, int curr
if (wp->w_localdir != NULL
&& (flagp != &vop_flags || (*flagp & SSOP_CURDIR))) {
if (fputs("lcd ", fd) < 0
- || ses_put_fname(fd, wp->w_localdir, flagp) == FAIL
+ || ses_put_fname(fd, (char_u *)wp->w_localdir, flagp) == FAIL
|| fprintf(fd, "\n") < 0) {
return FAIL;
}
@@ -522,7 +542,7 @@ static int makeopens(FILE *fd, char_u *dirnow)
int nr;
int restore_size = true;
win_T *wp;
- char_u *sname;
+ char *sname;
win_T *edited_win = NULL;
int tabnr;
win_T *tab_firstwin;
@@ -555,8 +575,8 @@ static int makeopens(FILE *fd, char_u *dirnow)
if (ssop_flags & SSOP_SESDIR) {
PUTLINE_FAIL("exe \"cd \" . escape(expand(\"<sfile>:p:h\"), ' ')");
} else if (ssop_flags & SSOP_CURDIR) {
- sname = home_replace_save(NULL, globaldir != NULL ? globaldir : dirnow);
- char *fname_esc = ses_escape_fname((char *)sname, &ssop_flags);
+ sname = home_replace_save(NULL, globaldir != NULL ? globaldir : (char *)dirnow);
+ char *fname_esc = ses_escape_fname(sname, &ssop_flags);
if (fprintf(fd, "cd %s\n", fname_esc) < 0) {
xfree(fname_esc);
xfree(sname);
@@ -573,12 +593,41 @@ static int makeopens(FILE *fd, char_u *dirnow)
"if expand('%') == '' && !&modified && line('$') <= 1"
" && getline(1) == ''\n"
" let s:wipebuf = bufnr('%')\n"
- "endif\n"
- // Now save the current files, current buffer first.
- "set shortmess=aoO\n") < 0) {
+ "endif\n") < 0) {
return FAIL;
}
+ // save 'shortmess' if not storing options
+ if ((ssop_flags & SSOP_OPTIONS) == 0) {
+ PUTLINE_FAIL("let s:shortmess_save = &shortmess");
+ }
+
+ // set 'shortmess' for the following. Add the 'A' flag if it was there
+ PUTLINE_FAIL("if &shortmess =~ 'A'");
+ PUTLINE_FAIL(" set shortmess=aoOA");
+ PUTLINE_FAIL("else");
+ PUTLINE_FAIL(" set shortmess=aoO");
+ PUTLINE_FAIL("endif");
+
+ // Now save the current files, current buffer first.
+ // Put all buffers into the buffer list.
+ // Do it very early to preserve buffer order after loading session (which
+ // can be disrupted by prior `edit` or `tabedit` calls).
+ FOR_ALL_BUFFERS(buf) {
+ if (!(only_save_windows && buf->b_nwindows == 0)
+ && !(buf->b_help && !(ssop_flags & SSOP_HELP))
+ && buf->b_fname != NULL
+ && buf->b_p_bl) {
+ if (fprintf(fd, "badd +%" PRId64 " ",
+ buf->b_wininfo == NULL
+ ? (int64_t)1L
+ : (int64_t)buf->b_wininfo->wi_mark.mark.lnum) < 0
+ || ses_fname(fd, buf, &ssop_flags, true) == FAIL) {
+ return FAIL;
+ }
+ }
+ }
+
// the global argument list
if (ses_arglist(fd, "argglobal", &global_alist.al_ga,
!(ssop_flags & SSOP_CURDIR), &ssop_flags) == FAIL) {
@@ -614,7 +663,10 @@ static int makeopens(FILE *fd, char_u *dirnow)
// Similar to ses_win_rec() below, populate the tab pages first so
// later local options won't be copied to the new tabs.
FOR_ALL_TABS(tp) {
- if (tp->tp_next != NULL && put_line(fd, "tabnew") == FAIL) {
+ // Use `bufhidden=wipe` to remove empty "placeholder" buffers once
+ // they are not needed. This prevents creating extra buffers (see
+ // cause of Vim patch 8.1.0829)
+ if (tp->tp_next != NULL && put_line(fd, "tabnew +setlocal\\ bufhidden=wipe") == FAIL) {
return FAIL;
}
}
@@ -654,7 +706,7 @@ static int makeopens(FILE *fd, char_u *dirnow)
if (ses_do_win(wp)
&& wp->w_buffer->b_ffname != NULL
&& !bt_help(wp->w_buffer)
- && !bt_nofile(wp->w_buffer)) {
+ && !bt_nofilename(wp->w_buffer)) {
if (need_tabnext && put_line(fd, "tabnext") == FAIL) {
return FAIL;
}
@@ -769,7 +821,7 @@ static int makeopens(FILE *fd, char_u *dirnow)
// Take care of tab-local working directories if applicable
if (tp->tp_localdir) {
if (fputs("if exists(':tcd') == 2 | tcd ", fd) < 0
- || ses_put_fname(fd, tp->tp_localdir, &ssop_flags) == FAIL
+ || ses_put_fname(fd, (char_u *)tp->tp_localdir, &ssop_flags) == FAIL
|| fputs(" | endif\n", fd) < 0) {
return FAIL;
}
@@ -792,25 +844,6 @@ static int makeopens(FILE *fd, char_u *dirnow)
return FAIL;
}
- // Now put the remaining buffers into the buffer list.
- // This is near the end, so that when 'hidden' is set we don't create extra
- // buffers. If the buffer was already created with another command the
- // ":badd" will have no effect.
- FOR_ALL_BUFFERS(buf) {
- if (!(only_save_windows && buf->b_nwindows == 0)
- && !(buf->b_help && !(ssop_flags & SSOP_HELP))
- && buf->b_fname != NULL
- && buf->b_p_bl) {
- if (fprintf(fd, "badd +%" PRId64 " ",
- buf->b_wininfo == NULL
- ? (int64_t)1L
- : (int64_t)buf->b_wininfo->wi_fpos.lnum) < 0
- || ses_fname(fd, buf, &ssop_flags, true) == FAIL) {
- return FAIL;
- }
- }
- }
-
//
// Wipe out an empty unnamed buffer we started in.
//
@@ -824,15 +857,21 @@ static int makeopens(FILE *fd, char_u *dirnow)
return FAIL;
}
- // Re-apply 'winheight', 'winwidth' and 'shortmess'.
- if (fprintf(fd,
- "set winheight=%" PRId64 " winwidth=%" PRId64
- " shortmess=%s\n",
- (int64_t)p_wh,
- (int64_t)p_wiw,
- p_shm) < 0) {
+ // Re-apply 'winheight' and 'winwidth'.
+ if (fprintf(fd, "set winheight=%" PRId64 " winwidth=%" PRId64 "\n",
+ (int64_t)p_wh, (int64_t)p_wiw) < 0) {
return FAIL;
}
+
+ // Restore 'shortmess'.
+ if (ssop_flags & SSOP_OPTIONS) {
+ if (fprintf(fd, "set shortmess=%s\n", p_shm) < 0) {
+ return FAIL;
+ }
+ } else {
+ PUTLINE_FAIL("let &shortmess = s:shortmess_save");
+ }
+
if (tab_firstwin != NULL && tab_firstwin->w_next != NULL) {
// Restore 'winminheight' and 'winminwidth'.
PUTLINE_FAIL("let &winminheight = s:save_winminheight");
@@ -900,7 +939,7 @@ void ex_mkrc(exarg_T *eap)
viewFile = fname;
using_vdir = true;
} else if (*eap->arg != NUL) {
- fname = (char *)eap->arg;
+ fname = eap->arg;
} else if (eap->cmdidx == CMD_mkvimrc) {
fname = VIMRC_FILE;
} else if (eap->cmdidx == CMD_mksession) {
@@ -962,12 +1001,12 @@ void ex_mkrc(exarg_T *eap)
*dirnow = NUL;
}
if (*dirnow != NUL && (ssop_flags & SSOP_SESDIR)) {
- if (vim_chdirfile((char_u *)fname, kCdCauseOther) == OK) {
+ if (vim_chdirfile(fname, kCdCauseOther) == OK) {
shorten_fnames(true);
}
} else if (*dirnow != NUL
&& (ssop_flags & SSOP_CURDIR) && globaldir != NULL) {
- if (os_chdir((char *)globaldir) == 0) {
+ if (os_chdir(globaldir) == 0) {
shorten_fnames(true);
}
}
@@ -982,15 +1021,6 @@ void ex_mkrc(exarg_T *eap)
emsg(_(e_prev_dir));
}
shorten_fnames(true);
- // restore original dir
- if (*dirnow != NUL && ((ssop_flags & SSOP_SESDIR)
- || ((ssop_flags & SSOP_CURDIR) && globaldir !=
- NULL))) {
- if (os_chdir((char *)dirnow) != 0) {
- emsg(_(e_prev_dir));
- }
- shorten_fnames(true);
- }
}
xfree(dirnow);
} else {
@@ -1038,14 +1068,14 @@ void ex_mkrc(exarg_T *eap)
xfree(viewFile);
}
-/// Get the name of the view file for the current buffer.
+/// @return the name of the view file for the current buffer.
static char *get_view_file(int c)
{
if (curbuf->b_ffname == NULL) {
emsg(_(e_noname));
return NULL;
}
- char *sname = (char *)home_replace_save(NULL, curbuf->b_ffname);
+ char *sname = home_replace_save(NULL, curbuf->b_ffname);
// We want a file name without separators, because we're not going to make
// a directory.
@@ -1086,7 +1116,7 @@ static char *get_view_file(int c)
return retval;
}
-// TODO(justinmk): remove this, not needed after 5ba3cecb68cd.
+/// TODO(justinmk): remove this, not needed after 5ba3cecb68cd.
int put_eol(FILE *fd)
{
if (putc('\n', fd) < 0) {
@@ -1095,7 +1125,7 @@ int put_eol(FILE *fd)
return OK;
}
-// TODO(justinmk): remove this, not needed after 5ba3cecb68cd.
+/// TODO(justinmk): remove this, not needed after 5ba3cecb68cd.
int put_line(FILE *fd, char *s)
{
if (fprintf(fd, "%s\n", s) < 0) {
diff --git a/src/nvim/ex_session.h b/src/nvim/ex_session.h
index 8d3ea5b91a..7ee2894c6e 100644
--- a/src/nvim/ex_session.h
+++ b/src/nvim/ex_session.h
@@ -10,4 +10,3 @@
#endif
#endif // NVIM_EX_SESSION_H
-
diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c
index c4d8f75a21..1639f72990 100644
--- a/src/nvim/extmark.c
+++ b/src/nvim/extmark.c
@@ -48,28 +48,32 @@
# 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->conceal
+ || decor_has_sign(decor)
+ || decor->ui_watched) {
+ 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,74 +81,94 @@ 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) {
if (kv_size(decor->virt_lines)) {
buf->b_virt_line_blocks++;
}
+ if (decor_has_sign(decor)) {
+ buf->b_signs++;
+ }
+ if (decor->sign_text) {
+ // TODO(lewis6991): smarter invalidation
+ buf_signcols_add_check(buf, NULL);
+ }
decor_redraw(buf, row, end_row > -1 ? end_row : row, decor);
}
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;
}
@@ -152,47 +176,38 @@ static bool extmark_setraw(buf_T *buf, uint64_t mark, int row, colnr_T col)
return true;
}
-// Remove an extmark
-// Returns 0 on missing id
-bool extmark_del(buf_T *buf, uint64_t ns_id, uint64_t id)
+/// Remove an extmark
+///
+/// @return 0 on missing 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)
+/// Free extmarks in a ns between lines
+/// if ns = 0, it means clear all namespaces
+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 +216,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 +276,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);
@@ -284,13 +289,14 @@ bool extmark_clear(buf_T *buf, uint64_t ns_id, int l_row, colnr_T l_col, int u_r
return marks_cleared;
}
-// Returns the position of marks between a range,
-// marks found at the start or end index will be included,
-// if upper_lnum or upper_col are negative the buffer
-// 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,
+/// @return the position of marks between a range,
+/// marks found at the start or end index will be included.
+///
+/// if upper_lnum or upper_col are negative the buffer
+/// 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, 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 +306,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) {
@@ -335,72 +337,60 @@ next_mark:
return array;
}
-// Lookup an extmark by id
-ExtmarkInfo extmark_from_id(buf_T *buf, uint64_t ns_id, uint64_t id)
+/// Lookup an extmark by 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) {
- return ret;
- }
-
- uint64_t mark = map_get(uint64_t, uint64_t)(ns->map, id);
- if (!mark) {
+ 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;
}
-
- 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;
}
-
-// free extmarks from the buffer
+/// free extmarks from the buffer
void extmark_free_all(buf_T *buf)
{
if (!map_size(buf->b_extmark_ns)) {
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);
+}
-// Save info for undo/redo of set marks
+/// Save info for undo/redo of set marks
static void u_extmark_set(buf_T *buf, uint64_t mark, int row, colnr_T col)
{
u_header_T *uhp = u_force_get_undo_header(buf);
@@ -435,18 +425,18 @@ void u_extmark_copy(buf_T *buf, int l_row, colnr_T l_col, int u_row, colnr_T u_c
ExtmarkUndoObject undo;
MarkTreeIter itr[1] = { 0 };
- marktree_itr_get(buf->b_marktree, l_row, l_col, itr);
+ marktree_itr_get(buf->b_marktree, (int32_t)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;
@@ -509,10 +499,9 @@ void extmark_apply_undo(ExtmarkUndoObject undo_info, bool undo)
}
}
-
-// Adjust extmark row for inserted/deleted rows (columns stay fixed).
-void extmark_adjust(buf_T *buf, linenr_T line1, linenr_T line2, long amount, long amount_after,
- ExtmarkOp undo)
+/// Adjust extmark row for inserted/deleted rows (columns stay fixed).
+void extmark_adjust(buf_T *buf, linenr_T line1, linenr_T line2, linenr_T amount,
+ linenr_T amount_after, ExtmarkOp undo)
{
if (curbuf_splice_pending) {
return;
@@ -521,7 +510,7 @@ void extmark_adjust(buf_T *buf, linenr_T line1, linenr_T line2, long amount, lon
bcount_t old_byte = 0, new_byte = 0;
int old_row, new_row;
if (amount == MAXLNUM) {
- old_row = (int)(line2 - line1+1);
+ old_row = (int)(line2 - line1 + 1);
// TODO(bfredl): ej kasta?
old_byte = (bcount_t)buf->deleted_bytes2;
@@ -535,11 +524,11 @@ void extmark_adjust(buf_T *buf, linenr_T line1, linenr_T line2, long amount, lon
new_row = (int)amount;
}
if (new_row > 0) {
- new_byte = ml_find_line_or_offset(buf, line1+new_row, NULL, true)
+ new_byte = ml_find_line_or_offset(buf, line1 + new_row, NULL, true)
- start_byte;
}
extmark_splice_impl(buf,
- (int)line1-1, 0, start_byte,
+ (int)line1 - 1, 0, start_byte,
old_row, 0, old_byte,
new_row, 0, new_byte, undo);
}
@@ -601,8 +590,7 @@ void extmark_splice_impl(buf_T *buf, int start_row, colnr_T start_col, bcount_t
u_extmark_copy(buf, start_row, start_col, end_row, end_col);
}
-
- marktree_splice(buf->b_marktree, start_row, start_col,
+ marktree_splice(buf->b_marktree, (int32_t)start_row, start_col,
old_row, old_col,
new_row, new_col);
@@ -618,18 +606,18 @@ void extmark_splice_impl(buf_T *buf, int start_row, colnr_T start_col, bcount_t
// merge algorithm later.
if (old_row == 0 && new_row == 0 && kv_size(uhp->uh_extmark)) {
ExtmarkUndoObject *item = &kv_A(uhp->uh_extmark,
- kv_size(uhp->uh_extmark)-1);
+ kv_size(uhp->uh_extmark) - 1);
if (item->type == kExtmarkSplice) {
ExtmarkSplice *splice = &item->data.splice;
if (splice->start_row == start_row && splice->old_row == 0
&& splice->new_row == 0) {
if (old_col == 0 && start_col >= splice->start_col
- && start_col <= splice->start_col+splice->new_col) {
+ && start_col <= splice->start_col + splice->new_col) {
splice->new_col += new_col;
splice->new_byte += new_byte;
merged = true;
} else if (new_col == 0
- && start_col == splice->start_col+splice->new_col) {
+ && start_col == splice->start_col + splice->new_col) {
splice->old_col += old_col;
splice->old_byte += old_byte;
merged = true;
@@ -692,7 +680,6 @@ void extmark_move_region(buf_T *buf, int start_row, colnr_T start_col, bcount_t
0, 0, 0,
extent_row, extent_col, extent_byte);
-
if (undo == kExtmarkUndo) {
u_header_T *uhp = u_force_get_undo_header(buf);
if (!uhp) {
diff --git a/src/nvim/extmark.h b/src/nvim/extmark.h
index c70db9f7aa..c144504076 100644
--- a/src/nvim/extmark.h
+++ b/src/nvim/extmark.h
@@ -2,6 +2,7 @@
#define NVIM_EXTMARK_H
#include "nvim/buffer_defs.h"
+#include "nvim/decoration.h"
#include "nvim/extmark_defs.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;
@@ -23,7 +26,6 @@ typedef kvec_t(ExtmarkInfo) ExtmarkInfoArray;
// TODO(bfredl): good enough name for now.
typedef ptrdiff_t bcount_t;
-
// delete the columns between mincol and endcol
typedef struct {
int start_row;
@@ -77,7 +79,6 @@ struct undo_object {
} data;
};
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "extmark.h.generated.h"
#endif
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..ca276b8a40 100644
--- a/src/nvim/file_search.c
+++ b/src/nvim/file_search.c
@@ -146,7 +146,6 @@ typedef struct ff_visited_list_hdr {
ff_visited_T *ffvl_visited_list;
} ff_visited_list_hdr_T;
-
/*
* '**' can be expanded to several directory levels.
* Set the default maximum depth.
@@ -298,7 +297,7 @@ void *vim_findfile_init(char_u *path, char_u *filename, char_u *stopdirs, int le
&& (vim_ispathsep(path[1]) || path[1] == NUL)
&& (!tagfile || vim_strchr(p_cpo, CPO_DOTTAG) == NULL)
&& rel_fname != NULL) {
- size_t len = (size_t)(path_tail(rel_fname) - rel_fname);
+ size_t len = (size_t)((char_u *)path_tail((char *)rel_fname) - rel_fname);
if (!vim_isAbsName(rel_fname) && len + 1 < MAXPATHL) {
// Make the start dir an absolute path name.
@@ -371,23 +370,23 @@ void *vim_findfile_init(char_u *path, char_u *filename, char_u *stopdirs, int le
ptr = xrealloc(search_ctx->ffsc_stopdirs_v,
(dircount + 1) * sizeof(char_u *));
search_ctx->ffsc_stopdirs_v = ptr;
- walker = vim_strchr(walker, ';');
+ walker = (char_u *)vim_strchr((char *)walker, ';');
if (walker) {
assert(walker - helper >= 0);
- search_ctx->ffsc_stopdirs_v[dircount-1] =
+ search_ctx->ffsc_stopdirs_v[dircount - 1] =
vim_strnsave(helper, (size_t)(walker - helper));
walker++;
} else {
/* this might be "", which means ascent till top
* of directory tree.
*/
- search_ctx->ffsc_stopdirs_v[dircount-1] =
+ search_ctx->ffsc_stopdirs_v[dircount - 1] =
vim_strsave(helper);
}
dircount++;
} while (walker != NULL);
- search_ctx->ffsc_stopdirs_v[dircount-1] = NULL;
+ search_ctx->ffsc_stopdirs_v[dircount - 1] = NULL;
}
search_ctx->ffsc_level = level;
@@ -396,7 +395,7 @@ void *vim_findfile_init(char_u *path, char_u *filename, char_u *stopdirs, int le
* -fix path
* -wildcard_stuff (might be NULL)
*/
- wc_part = vim_strchr(path, '*');
+ wc_part = (char_u *)vim_strchr((char *)path, '*');
if (wc_part != NULL) {
int64_t llevel;
int len;
@@ -477,14 +476,20 @@ void *vim_findfile_init(char_u *path, char_u *filename, char_u *stopdirs, int le
STRCAT(ff_expand_buffer, search_ctx->ffsc_fix_path);
add_pathsep((char *)ff_expand_buffer);
} else {
- char_u *p = path_tail(search_ctx->ffsc_fix_path);
+ char_u *p = (char_u *)path_tail((char *)search_ctx->ffsc_fix_path);
char_u *wc_path = NULL;
char_u *temp = NULL;
int len = 0;
if (p > search_ctx->ffsc_fix_path) {
+ // do not add '..' to the path and start upwards searching
len = (int)(p - search_ctx->ffsc_fix_path) - 1;
- STRNCAT(ff_expand_buffer, search_ctx->ffsc_fix_path, len);
+ if ((len >= 2 && STRNCMP(search_ctx->ffsc_fix_path, "..", 2) == 0)
+ && (len == 2 || search_ctx->ffsc_fix_path[2] == PATHSEP)) {
+ xfree(buf);
+ goto error_return;
+ }
+ STRLCAT(ff_expand_buffer, search_ctx->ffsc_fix_path, eb_len + (size_t)len + 1);
add_pathsep((char *)ff_expand_buffer);
} else {
len = (int)STRLEN(search_ctx->ffsc_fix_path);
@@ -523,9 +528,7 @@ error_return:
return NULL;
}
-/*
- * Get the stopdir string. Check that ';' is not escaped.
- */
+/// @return the stopdir string. Check that ';' is not escaped.
char_u *vim_findfile_stopdir(char_u *buf)
{
char_u *r_ptr = buf;
@@ -548,9 +551,7 @@ char_u *vim_findfile_stopdir(char_u *buf)
return r_ptr;
}
-/*
- * Clean up the given search context. Can handle a NULL pointer.
- */
+/// Clean up the given search context. Can handle a NULL pointer.
void vim_findfile_cleanup(void *ctx)
{
if (ctx == NULL) {
@@ -562,18 +563,19 @@ void vim_findfile_cleanup(void *ctx)
xfree(ctx);
}
-/*
- * Find a file in a search context.
- * The search context was created with vim_findfile_init() above.
- * Return a pointer to an allocated file name or NULL if nothing found.
- * To get all matching files call this function until you get NULL.
- *
- * If the passed search_context is NULL, NULL is returned.
- *
- * The search algorithm is depth first. To change this replace the
- * stack with a list (don't forget to leave partly searched directories on the
- * top of the list).
- */
+/// Find a file in a search context.
+/// The search context was created with vim_findfile_init() above.
+///
+/// To get all matching files call this function until you get NULL.
+///
+/// If the passed search_context is NULL, NULL is returned.
+///
+/// The search algorithm is depth first. To change this replace the
+/// stack with a list (don't forget to leave partly searched directories on the
+/// top of the list).
+///
+/// @return a pointer to an allocated file name or,
+/// NULL if nothing found.
char_u *vim_findfile(void *search_ctx_arg)
{
char_u *file_path;
@@ -891,8 +893,7 @@ char_u *vim_findfile(void *search_ctx_arg)
break;
}
assert(MAXPATHL >= len);
- copy_option_part(&suf, file_path + len,
- MAXPATHL - len, ",");
+ copy_option_part((char **)&suf, (char *)file_path + len, MAXPATHL - len, ",");
}
}
} else {
@@ -921,8 +922,8 @@ char_u *vim_findfile(void *search_ctx_arg)
*/
if (STRNCMP(stackp->ffs_wc_path, "**", 2) == 0) {
for (int i = stackp->ffs_filearray_cur;
- i < stackp->ffs_filearray_size; ++i) {
- if (fnamecmp(stackp->ffs_filearray[i],
+ i < stackp->ffs_filearray_size; i++) {
+ if (FNAMECMP(stackp->ffs_filearray[i],
stackp->ffs_fix_path) == 0) {
continue; // don't repush same directory
}
@@ -993,10 +994,8 @@ fail:
return NULL;
}
-/*
- * Free the list of lists of visited files and directories
- * Can handle it if the passed search_context is NULL;
- */
+/// Free the list of lists of visited files and directories
+/// Can handle it if the passed search_context is NULL;
void vim_findfile_free_visited(void *search_ctx_arg)
{
ff_search_ctx_T *search_ctx;
@@ -1038,10 +1037,8 @@ static void ff_free_visited_list(ff_visited_T *vl)
vl = NULL;
}
-/*
- * Returns the already visited list for the given filename. If none is found it
- * allocates a new one.
- */
+/// @return the already visited list for the given filename. If none is found it
+/// allocates a new one.
static ff_visited_list_hdr_T *ff_get_visited_list(char_u *filename,
ff_visited_list_hdr_T **list_headp)
{
@@ -1051,7 +1048,7 @@ static ff_visited_list_hdr_T *ff_get_visited_list(char_u *filename,
if (*list_headp != NULL) {
retptr = *list_headp;
while (retptr != NULL) {
- if (fnamecmp(filename, retptr->ffvl_filename) == 0) {
+ if (FNAMECMP(filename, retptr->ffvl_filename) == 0) {
#ifdef FF_VERBOSE
if (p_verbose >= 5) {
verbose_enter_scroll();
@@ -1088,13 +1085,13 @@ static ff_visited_list_hdr_T *ff_get_visited_list(char_u *filename,
return retptr;
}
-// Check if two wildcard paths are equal.
-// They are equal if:
-// - both paths are NULL
-// - they have the same length
-// - char by char comparison is OK
-// - the only differences are in the counters behind a '**', so
-// '**\20' is equal to '**\24'
+/// Check if two wildcard paths are equal.
+/// They are equal if:
+/// - both paths are NULL
+/// - they have the same length
+/// - char by char comparison is OK
+/// - the only differences are in the counters behind a '**', so
+/// '**\20' is equal to '**\24'
static bool ff_wc_equal(char_u *s1, char_u *s2)
{
int i, j;
@@ -1112,8 +1109,8 @@ static bool ff_wc_equal(char_u *s1, char_u *s2)
}
for (i = 0, j = 0; s1[i] != NUL && s2[j] != NUL;) {
- c1 = utf_ptr2char(s1 + i);
- c2 = utf_ptr2char(s2 + j);
+ c1 = utf_ptr2char((char *)s1 + i);
+ c2 = utf_ptr2char((char *)s2 + j);
if ((p_fic ? mb_tolower(c1) != mb_tolower(c2) : c1 != c2)
&& (prev1 != '*' || prev2 != '*')) {
@@ -1122,17 +1119,16 @@ static bool ff_wc_equal(char_u *s1, char_u *s2)
prev2 = prev1;
prev1 = c1;
- i += utfc_ptr2len(s1 + i);
- j += utfc_ptr2len(s2 + j);
+ i += utfc_ptr2len((char *)s1 + i);
+ j += utfc_ptr2len((char *)s2 + j);
}
return s1[i] == s2[j];
}
-/*
- * maintains the list of already visited files and dirs
- * returns FAIL if the given file/dir is already in the list
- * returns OK if it is newly added
- */
+/// maintains the list of already visited files and dirs
+///
+/// @return FAIL if the given file/dir is already in the list or,
+/// OK if it is newly added
static int ff_check_visited(ff_visited_T **visited_list, char_u *fname, char_u *wc_path)
{
ff_visited_T *vp;
@@ -1153,7 +1149,7 @@ static int ff_check_visited(ff_visited_T **visited_list, char_u *fname, char_u *
// check against list of already visited files
for (vp = *visited_list; vp != NULL; vp = vp->ffv_next) {
- if ((url && fnamecmp(vp->ffv_fname, ff_expand_buffer) == 0)
+ if ((url && FNAMECMP(vp->ffv_fname, ff_expand_buffer) == 0)
|| (!url && vp->file_id_valid
&& os_fileid_equal(&(vp->file_id), &file_id))) {
// are the wildcard parts equal
@@ -1190,9 +1186,7 @@ static int ff_check_visited(ff_visited_T **visited_list, char_u *fname, char_u *
return OK;
}
-/*
- * create stack element from given path pieces
- */
+/// create stack element from given path pieces
static ff_stack_T *ff_create_stack_element(char_u *fix_part, char_u *wc_part, int level,
int star_star_empty)
{
@@ -1220,9 +1214,7 @@ static ff_stack_T *ff_create_stack_element(char_u *fix_part, char_u *wc_part, in
return new;
}
-/*
- * Push a dir on the directory stack.
- */
+/// Push a dir on the directory stack.
static void ff_push(ff_search_ctx_T *search_ctx, ff_stack_T *stack_ptr)
{
/* check for NULL pointer, not to return an error to the user, but
@@ -1233,10 +1225,9 @@ static void ff_push(ff_search_ctx_T *search_ctx, ff_stack_T *stack_ptr)
}
}
-/*
- * Pop a dir from the directory stack.
- * Returns NULL if stack is empty.
- */
+/// Pop a dir from the directory stack.
+///
+/// @return NULL if stack is empty.
static ff_stack_T *ff_pop(ff_search_ctx_T *search_ctx)
{
ff_stack_T *sptr;
@@ -1249,9 +1240,7 @@ static ff_stack_T *ff_pop(ff_search_ctx_T *search_ctx)
return sptr;
}
-/*
- * free the given stack element
- */
+/// free the given stack element
static void ff_free_stack_element(ff_stack_T *const stack_ptr)
{
if (stack_ptr == NULL) {
@@ -1269,9 +1258,7 @@ static void ff_free_stack_element(ff_stack_T *const stack_ptr)
xfree(stack_ptr);
}
-/*
- * Clear the search context, but NOT the visited list.
- */
+/// Clear the search context, but NOT the visited list.
static void ff_clear(ff_search_ctx_T *search_ctx)
{
ff_stack_T *sptr;
@@ -1305,10 +1292,9 @@ static void ff_clear(ff_search_ctx_T *search_ctx)
search_ctx->ffsc_level = 0;
}
-/*
- * check if the given path is in the stopdirs
- * returns TRUE if yes else FALSE
- */
+/// check if the given path is in the stopdirs
+///
+/// @return TRUE if yes else FALSE
static int ff_path_in_stoplist(char_u *path, int path_len, char_u **stopdirs_v)
{
int i = 0;
@@ -1329,13 +1315,13 @@ static int ff_path_in_stoplist(char_u *path, int path_len, char_u **stopdirs_v)
* '/home/rks'. Check for PATHSEP in stopdirs_v[i], else
* '/home/r' would also match '/home/rks'
*/
- if (fnamencmp(stopdirs_v[i], path, path_len) == 0
+ if (FNAMENCMP(stopdirs_v[i], path, path_len) == 0
&& vim_ispathsep(stopdirs_v[i][path_len])) {
return TRUE;
}
} else {
- if (fnamecmp(stopdirs_v[i], path) == 0) {
- return TRUE;
+ if (FNAMECMP(stopdirs_v[i], path) == 0) {
+ return true;
}
}
}
@@ -1433,7 +1419,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;
@@ -1489,7 +1479,7 @@ char_u *find_file_in_path_option(char_u *ptr, size_t len, int options, int first
&& rel_fname != NULL
&& STRLEN(rel_fname) + l < MAXPATHL) {
STRCPY(NameBuff, rel_fname);
- STRCPY(path_tail(NameBuff), ff_file_to_find);
+ STRCPY(path_tail((char *)NameBuff), ff_file_to_find);
l = STRLEN(NameBuff);
} else {
STRCPY(NameBuff, ff_file_to_find);
@@ -1512,7 +1502,7 @@ char_u *find_file_in_path_option(char_u *ptr, size_t len, int options, int first
break;
}
assert(MAXPATHL >= l);
- copy_option_part(&buf, NameBuff + l, MAXPATHL - l, ",");
+ copy_option_part((char **)&buf, (char *)NameBuff + l, MAXPATHL - l, ",");
}
}
}
@@ -1552,7 +1542,7 @@ char_u *find_file_in_path_option(char_u *ptr, size_t len, int options, int first
// copy next path
buf[0] = 0;
- copy_option_part(&dir, buf, MAXPATHL, " ,");
+ copy_option_part((char **)&dir, (char *)buf, MAXPATHL, " ,");
// get the stopdir string
r_ptr = vim_findfile_stopdir(buf);
@@ -1590,11 +1580,13 @@ theend:
return file_name;
}
-void do_autocmd_dirchanged(char *new_dir, CdScope scope, CdCause cause)
+void do_autocmd_dirchanged(char *new_dir, CdScope scope, CdCause cause, bool pre)
{
static bool recursive = false;
- if (recursive || !has_event(EVENT_DIRCHANGED)) {
+ event_T event = pre ? EVENT_DIRCHANGEDPRE : EVENT_DIRCHANGED;
+
+ if (recursive || !has_event(event)) {
// No autocommand was defined or we changed
// the directory from this autocommand.
return;
@@ -1628,8 +1620,12 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope, CdCause cause)
new_dir = new_dir_buf;
#endif
+ if (pre) {
+ tv_dict_add_str(dict, S_LEN("directory"), new_dir);
+ } else {
+ tv_dict_add_str(dict, S_LEN("cwd"), new_dir);
+ }
tv_dict_add_str(dict, S_LEN("scope"), buf); // -V614
- tv_dict_add_str(dict, S_LEN("cwd"), new_dir);
tv_dict_add_bool(dict, S_LEN("changed_window"), cause == kCdCauseWindow);
tv_dict_set_keys_readonly(dict);
@@ -1645,8 +1641,7 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope, CdCause cause)
abort();
}
- apply_autocmds(EVENT_DIRCHANGED, (char_u *)buf, (char_u *)new_dir, false,
- curbuf);
+ apply_autocmds(event, buf, new_dir, false, curbuf);
restore_v_event(dict, &save_v_event);
@@ -1655,13 +1650,14 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope, CdCause cause)
/// Change to a file's directory.
/// Caller must call shorten_fnames()!
-/// @return OK or FAIL
-int vim_chdirfile(char_u *fname, CdCause cause)
+///
+/// @return OK or FAIL
+int vim_chdirfile(char *fname, CdCause cause)
{
char dir[MAXPATHL];
STRLCPY(dir, fname, MAXPATHL);
- *path_tail_with_sep((char_u *)dir) = NUL;
+ *path_tail_with_sep(dir) = NUL;
if (os_dirname(NameBuff, sizeof(NameBuff)) != OK) {
NameBuff[0] = NUL;
@@ -1672,12 +1668,16 @@ int vim_chdirfile(char_u *fname, CdCause cause)
return OK;
}
+ if (cause != kCdCauseOther) {
+ do_autocmd_dirchanged(dir, kCdScopeWindow, cause, true);
+ }
+
if (os_chdir(dir) != 0) {
return FAIL;
}
if (cause != kCdCauseOther) {
- do_autocmd_dirchanged(dir, kCdScopeWindow, cause);
+ do_autocmd_dirchanged(dir, kCdScopeWindow, cause, false);
}
return OK;
@@ -1687,7 +1687,7 @@ int vim_chdirfile(char_u *fname, CdCause cause)
int vim_chdir(char_u *new_dir)
{
char *dir_name = (char *)find_directory_in_path(new_dir, STRLEN(new_dir),
- FNAME_MESS, curbuf->b_ffname);
+ FNAME_MESS, (char_u *)curbuf->b_ffname);
if (dir_name == NULL) {
return -1;
}
@@ -1696,4 +1696,3 @@ int vim_chdir(char_u *new_dir)
xfree(dir_name);
return r;
}
-
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index f8cf341836..6782465ef1 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -19,6 +19,7 @@
#include "nvim/cursor.h"
#include "nvim/diff.h"
#include "nvim/edit.h"
+#include "nvim/eval/typval.h"
#include "nvim/eval/userfunc.h"
#include "nvim/ex_cmds.h"
#include "nvim/ex_docmd.h"
@@ -77,7 +78,7 @@
#define FIO_ENDIAN_L 0x80 // little endian
#define FIO_NOCONVERT 0x2000 // skip encoding conversion
#define FIO_UCSBOM 0x4000 // check for BOM at start of file
-#define FIO_ALL -1 // allow all formats
+#define FIO_ALL (-1) // allow all formats
/* When converting, a read() or write() may leave some bytes to be converted
* for the next call. The value is guessed... */
@@ -146,7 +147,6 @@ void filemess(buf_T *buf, char_u *name, char_u *s, int attr)
msg_scrolled_ign = false;
}
-
/// Read lines from file "fname" into the buffer after line "from".
///
/// 1. We allocate blocks with try_malloc, as big as possible.
@@ -172,10 +172,10 @@ void filemess(buf_T *buf, char_u *name, char_u *s, int attr)
/// @param eap can be NULL!
///
/// @return FAIL for failure, NOTDONE for directory (failure), or OK
-int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_skip,
- linenr_T lines_to_read, exarg_T *eap, int flags)
+int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
+ linenr_T lines_to_read, exarg_T *eap, int flags, bool silent)
{
- int fd = 0;
+ int fd = stdin_fd >= 0 ? stdin_fd : 0;
int newfile = (flags & READ_NEW);
int check_readonly;
int filtering = (flags & READ_FILTER);
@@ -186,12 +186,12 @@ int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_ski
|| (eap != NULL && eap->read_edit);
linenr_T read_buf_lnum = 1; // next line to read from curbuf
colnr_T read_buf_col = 0; // next char to read from this line
- char_u c;
+ char c;
linenr_T lnum = from;
- char_u *ptr = NULL; // pointer into read buffer
- char_u *buffer = NULL; // read buffer
- char_u *new_buffer = NULL; // init to shut up gcc
- char_u *line_start = NULL; // init to shut up gcc
+ char *ptr = NULL; // pointer into read buffer
+ char *buffer = NULL; // read buffer
+ char *new_buffer = NULL; // init to shut up gcc
+ char *line_start = NULL; // init to shut up gcc
int wasempty; // buffer was empty before reading
colnr_T len;
long size = 0;
@@ -226,11 +226,11 @@ int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_ski
int bad_char_behavior = BAD_REPLACE;
// BAD_KEEP, BAD_DROP or character to
// replace with
- char_u *tmpname = NULL; // name of 'charconvert' output file
+ char *tmpname = NULL; // name of 'charconvert' output file
int fio_flags = 0;
- char_u *fenc; // fileencoding to use
+ char *fenc; // fileencoding to use
bool fenc_alloced; // fenc_next is in allocated memory
- char_u *fenc_next = NULL; // next item in 'fencs' or NULL
+ char *fenc_next = NULL; // next item in 'fencs' or NULL
bool advance_fenc = false;
long real_size = 0;
#ifdef HAVE_ICONV
@@ -240,11 +240,12 @@ int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_ski
#endif
bool converted = false; // true if conversion done
bool notconverted = false; // true if conversion wanted but it wasn't possible
- char_u conv_rest[CONV_RESTLEN];
+ char 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;
+ char *old_b_ffname;
+ char *old_b_fname;
int using_b_ffname;
int using_b_fname;
static char *msg_is_a_directory = N_("is a directory");
@@ -264,7 +265,7 @@ int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_ski
&& fname != NULL
&& vim_strchr(p_cpo, CPO_FNAMER) != NULL
&& !(flags & READ_DUMMY)) {
- if (set_rw_fname(fname, sfname) == FAIL) {
+ if (set_rw_fname((char_u *)fname, (char_u *)sfname) == FAIL) {
return FAIL;
}
}
@@ -276,8 +277,7 @@ int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_ski
old_curbuf = curbuf;
old_b_ffname = curbuf->b_ffname;
old_b_fname = curbuf->b_fname;
- using_b_ffname = (fname == curbuf->b_ffname)
- || (sfname == curbuf->b_ffname);
+ using_b_ffname = (fname == curbuf->b_ffname) || (sfname == curbuf->b_ffname);
using_b_fname = (fname == curbuf->b_fname) || (sfname == curbuf->b_fname);
// After reading a file the cursor line changes but we don't want to
@@ -298,14 +298,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 +331,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) {
@@ -349,7 +345,7 @@ int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_ski
// If the name is too long we might crash further on, quit here.
if (namelen >= MAXPATHL) {
- filemess(curbuf, fname, (char_u *)_("Illegal file name"), 0);
+ filemess(curbuf, (char_u *)fname, (char_u *)_("Illegal file name"), 0);
msg_end();
msg_scroll = msg_save;
return FAIL;
@@ -358,16 +354,18 @@ int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_ski
// If the name ends in a path separator, we can't open it. Check here,
// because reading the file may actually work, but then creating the
// swap file may destroy it! Reported on MS-DOS and Win 95.
- if (after_pathsep((const char *)fname, (const char *)(fname + namelen))) {
- filemess(curbuf, fname, (char_u *)_(msg_is_a_directory), 0);
+ if (after_pathsep(fname, fname + namelen)) {
+ if (!silent) {
+ filemess(curbuf, (char_u *)fname, (char_u *)_(msg_is_a_directory), 0);
+ }
msg_end();
msg_scroll = msg_save;
- return FAIL;
+ return NOTDONE;
}
}
if (!read_buffer && !read_stdin && !read_fifo) {
- perm = os_getperm((const char *)fname);
+ perm = os_getperm(fname);
// On Unix it is possible to read a directory, so we have to
// check for it before os_open().
if (perm >= 0 && !S_ISREG(perm) // not a regular file ...
@@ -379,9 +377,11 @@ int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_ski
#endif
) {
if (S_ISDIR(perm)) {
- filemess(curbuf, fname, (char_u *)_(msg_is_a_directory), 0);
+ if (!silent) {
+ filemess(curbuf, (char_u *)fname, (char_u *)_(msg_is_a_directory), 0);
+ }
} else {
- filemess(curbuf, fname, (char_u *)_("is not a file"), 0);
+ filemess(curbuf, (char_u *)fname, (char_u *)_("is not a file"), 0);
}
msg_end();
msg_scroll = msg_save;
@@ -405,9 +405,10 @@ int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_ski
if (newfile && !read_stdin && !read_buffer && !read_fifo) {
// Remember time of file.
- if (os_fileinfo((char *)fname, &file_info)) {
+ if (os_fileinfo(fname, &file_info)) {
buf_store_file_info(curbuf, &file_info);
curbuf->b_mtime_read = curbuf->b_mtime;
+ curbuf->b_mtime_read_ns = curbuf->b_mtime_ns;
#ifdef UNIX
/*
* Use the protection bits of the original file for the swap file.
@@ -420,11 +421,13 @@ int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_ski
* not be able to write to the file ourselves.
* Setting the bits is done below, after creating the swap file.
*/
- swap_mode = (file_info.stat.st_mode & 0644) | 0600;
+ swap_mode = ((int)file_info.stat.st_mode & 0644) | 0600;
#endif
} else {
curbuf->b_mtime = 0;
+ curbuf->b_mtime_ns = 0;
curbuf->b_mtime_read = 0;
+ curbuf->b_mtime_read_ns = 0;
curbuf->b_orig_size = 0;
curbuf->b_orig_mode = 0;
}
@@ -438,10 +441,10 @@ int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_ski
bool file_readonly = false;
if (!read_buffer && !read_stdin) {
if (!newfile || readonlymode || !(perm & 0222)
- || !os_file_is_writable((char *)fname)) {
+ || !os_file_is_writable(fname)) {
file_readonly = true;
}
- fd = os_open((char *)fname, O_RDONLY, 0);
+ fd = os_open(fname, O_RDONLY, 0);
}
if (fd < 0) { // cannot open at all
@@ -469,10 +472,12 @@ int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_ski
return FAIL;
}
}
- if (dir_of_file_exists(fname)) {
- filemess(curbuf, sfname, (char_u *)new_file_message(), 0);
- } else {
- filemess(curbuf, sfname, (char_u *)_("[New DIRECTORY]"), 0);
+ if (!silent) {
+ if (dir_of_file_exists((char_u *)fname)) {
+ filemess(curbuf, (char_u *)sfname, (char_u *)new_file_message(), 0);
+ } else {
+ filemess(curbuf, (char_u *)sfname, (char_u *)_("[New DIRECTORY]"), 0);
+ }
}
// Even though this is a new file, it might have been
// edited before and deleted. Get the old marks.
@@ -491,15 +496,16 @@ int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_ski
}
return OK; // a new file is not an error
} else {
- filemess(curbuf, sfname, (char_u *)(
- (fd == UV_EFBIG) ? _("[File too big]") :
+ filemess(curbuf, (char_u *)sfname, (char_u *)((fd == UV_EFBIG) ? _("[File too big]") :
#if defined(UNIX) && defined(EOVERFLOW)
- // libuv only returns -errno in Unix and in Windows open() does not
- // set EOVERFLOW
- (fd == -EOVERFLOW) ? _("[File too big]") :
+ // libuv only returns -errno
+ // in Unix and in Windows
+ // open() does not set
+ // EOVERFLOW
+ (fd == -EOVERFLOW) ? _("[File too big]") :
#endif
- _("[Permission Denied]")), 0);
- curbuf->b_p_ro = TRUE; // must use "w!" now
+ _("[Permission Denied]")), 0);
+ curbuf->b_p_ro = true; // must use "w!" now
}
return FAIL;
@@ -555,7 +561,8 @@ int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_ski
if (os_fileinfo(swap_fname, &swap_info)
&& file_info.stat.st_gid != swap_info.stat.st_gid
- && os_fchown(curbuf->b_ml.ml_mfp->mf_fd, -1, file_info.stat.st_gid)
+ && os_fchown(curbuf->b_ml.ml_mfp->mf_fd, (uv_uid_t)(-1),
+ (uv_gid_t)file_info.stat.st_gid)
== -1) {
swap_mode &= 0600;
}
@@ -576,9 +583,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 +624,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;
@@ -639,8 +646,8 @@ int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_ski
if (!read_stdin && (curbuf != old_curbuf
|| (using_b_ffname && (old_b_ffname != curbuf->b_ffname))
|| (using_b_fname && (old_b_fname != curbuf->b_fname))
- || (fd = os_open((char *)fname, O_RDONLY, 0)) < 0)) {
- --no_wait_return;
+ || (fd = os_open(fname, O_RDONLY, 0)) < 0)) {
+ no_wait_return--;
msg_scroll = msg_save;
if (fd < 0) {
emsg(_("E200: *ReadPre autocommands made the file unreadable"));
@@ -655,9 +662,9 @@ int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_ski
// Autocommands may add lines to the file, need to check if it is empty
wasempty = (curbuf->b_ml.ml_flags & ML_EMPTY);
- if (!recoverymode && !filtering && !(flags & READ_DUMMY)) {
+ if (!recoverymode && !filtering && !(flags & READ_DUMMY) && !silent) {
if (!read_stdin && !read_buffer) {
- filemess(curbuf, sfname, (char_u *)"", 0);
+ filemess(curbuf, (char_u *)sfname, (char_u *)"", 0);
}
}
@@ -683,27 +690,27 @@ int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_ski
* Decide which 'encoding' to use or use first.
*/
if (eap != NULL && eap->force_enc != 0) {
- fenc = enc_canonize(eap->cmd + eap->force_enc);
+ fenc = (char *)enc_canonize((char_u *)eap->cmd + eap->force_enc);
fenc_alloced = true;
keep_dest_enc = true;
} else if (curbuf->b_p_bin) {
- fenc = (char_u *)""; // binary: don't convert
+ fenc = ""; // binary: don't convert
fenc_alloced = false;
} else if (curbuf->b_help) {
// Help files are either utf-8 or latin1. Try utf-8 first, if this
// fails it must be latin1.
// It is needed when the first line contains non-ASCII characters.
// That is only in *.??x files.
- fenc_next = (char_u *)"latin1";
- fenc = (char_u *)"utf-8";
+ fenc_next = "latin1";
+ fenc = "utf-8";
fenc_alloced = false;
} else if (*p_fencs == NUL) {
- fenc = curbuf->b_p_fenc; // use format from buffer
+ fenc = (char *)curbuf->b_p_fenc; // use format from buffer
fenc_alloced = false;
} else {
- fenc_next = p_fencs; // try items in 'fileencodings'
- fenc = next_fenc(&fenc_next, &fenc_alloced);
+ fenc_next = (char *)p_fencs; // try items in 'fileencodings'
+ fenc = (char *)next_fenc((char_u **)&fenc_next, &fenc_alloced);
}
/*
@@ -790,21 +797,21 @@ retry:
if (fenc_alloced) {
xfree(fenc);
}
- fenc = (char_u *)"";
+ fenc = "";
fenc_alloced = false;
} else {
if (fenc_alloced) {
xfree(fenc);
}
if (fenc_next != NULL) {
- fenc = next_fenc(&fenc_next, &fenc_alloced);
+ fenc = (char *)next_fenc((char_u **)&fenc_next, &fenc_alloced);
} else {
- fenc = (char_u *)"";
+ fenc = "";
fenc_alloced = false;
}
}
if (tmpname != NULL) {
- os_remove((char *)tmpname); // delete converted file
+ os_remove(tmpname); // delete converted file
XFREE_CLEAR(tmpname);
}
}
@@ -814,7 +821,7 @@ retry:
* from 'encoding' or 'encoding' is UTF-16, UCS-2 or UCS-4.
*/
fio_flags = 0;
- converted = need_conversion(fenc);
+ converted = need_conversion((char_u *)fenc);
if (converted) {
// "ucs-bom" means we need to check the first bytes of the file
// for a BOM.
@@ -828,15 +835,14 @@ retry:
// appears not to handle this correctly. This works just like
// conversion to UTF-8 except how the resulting character is put in
// the buffer.
- fio_flags = get_fio_flags(fenc);
+ fio_flags = get_fio_flags((char_u *)fenc);
}
-
#ifdef HAVE_ICONV
// Try using iconv() if we can't convert internally.
if (fio_flags == 0
&& !did_iconv) {
- iconv_fd = (iconv_t)my_iconv_open((char_u *)"utf-8", fenc);
+ iconv_fd = (iconv_t)my_iconv_open((char_u *)"utf-8", (char_u *)fenc);
}
#endif
@@ -856,7 +862,7 @@ retry:
// Skip conversion when it's already done (retry for wrong
// "fileformat").
if (tmpname == NULL) {
- tmpname = readfile_charconvert(fname, fenc, &fd);
+ tmpname = (char *)readfile_charconvert((char_u *)fname, (char_u *)fenc, &fd);
if (tmpname == NULL) {
// Conversion failed. Try another one.
advance_fenc = true;
@@ -978,7 +984,7 @@ retry:
#endif
if (conv_restlen > 0) {
// Insert unconverted bytes from previous line.
- memmove(ptr, conv_rest, conv_restlen); // -V614
+ memmove(ptr, conv_rest, (size_t)conv_restlen); // -V614
ptr += conv_restlen;
size -= conv_restlen;
}
@@ -1007,32 +1013,32 @@ retry:
if (p[ni] == NL) {
ptr[tlen++] = NUL;
} else {
- ptr[tlen++] = p[ni];
+ ptr[tlen++] = (char)p[ni];
}
}
read_buf_col += n;
break;
- } else {
- // Append whole line and new-line. Change NL
- // to NUL to reverse the effect done below.
- for (ni = 0; ni < n; ni++) {
- if (p[ni] == NL) {
- ptr[tlen++] = NUL;
- } else {
- ptr[tlen++] = p[ni];
- }
+ }
+
+ // Append whole line and new-line. Change NL
+ // to NUL to reverse the effect done below.
+ for (ni = 0; ni < n; ni++) {
+ if (p[ni] == NL) {
+ ptr[tlen++] = NUL;
+ } else {
+ ptr[tlen++] = (char)p[ni];
}
- ptr[tlen++] = NL;
- read_buf_col = 0;
- if (++read_buf_lnum > from) {
- // When the last line didn't have an
- // end-of-line don't add it now either.
- if (!curbuf->b_p_eol) {
- --tlen;
- }
- size = tlen;
- break;
+ }
+ ptr[tlen++] = NL;
+ read_buf_col = 0;
+ if (++read_buf_lnum > from) {
+ // When the last line didn't have an
+ // end-of-line don't add it now either.
+ if (!curbuf->b_p_eol) {
+ tlen--;
}
+ size = tlen;
+ break;
}
}
}
@@ -1040,7 +1046,7 @@ retry:
/*
* Read bytes from the file.
*/
- size = read_eintr(fd, ptr, size);
+ size = read_eintr(fd, ptr, (size_t)size);
}
if (size <= 0) {
@@ -1085,8 +1091,8 @@ retry:
#endif
)) {
while (conv_restlen > 0) {
- *(--ptr) = bad_char_behavior;
- --conv_restlen;
+ *(--ptr) = (char)bad_char_behavior;
+ conv_restlen--;
}
}
fio_flags = 0; // don't convert this
@@ -1115,14 +1121,14 @@ retry:
&& tmpname == NULL
&& (*fenc == 'u' || *fenc == NUL)))) {
char_u *ccname;
- int blen;
+ int blen = 0;
// no BOM detection in a short file or in binary mode
if (size < 2 || curbuf->b_p_bin) {
ccname = NULL;
} else {
- ccname = check_for_bom(ptr, size, &blen,
- fio_flags == FIO_UCSBOM ? FIO_ALL : get_fio_flags(fenc));
+ ccname = check_for_bom((char_u *)ptr, size, &blen,
+ fio_flags == FIO_UCSBOM ? FIO_ALL : get_fio_flags((char_u *)fenc));
}
if (ccname != NULL) {
// Remove BOM from the text
@@ -1144,7 +1150,7 @@ retry:
if (fenc_alloced) {
xfree(fenc);
}
- fenc = ccname;
+ fenc = (char *)ccname;
fenc_alloced = false;
}
// retry reading without getting new bytes or rewinding
@@ -1166,20 +1172,12 @@ retry:
#ifdef HAVE_ICONV
if (iconv_fd != (iconv_t)-1) {
- /*
- * Attempt conversion of the read bytes to 'encoding' using
- * iconv().
- */
- const char *fromp;
- char *top;
- size_t from_size;
- size_t to_size;
-
- fromp = (char *)ptr;
- from_size = size;
+ // Attempt conversion of the read bytes to 'encoding' using iconv().
+ const char *fromp = ptr;
+ size_t from_size = (size_t)size;
ptr += size;
- top = (char *)ptr;
- to_size = real_size - size;
+ char *top = ptr;
+ size_t to_size = (size_t)(real_size - size);
/*
* If there is conversion error or not enough room try using
@@ -1194,8 +1192,7 @@ retry:
goto rewind_retry;
}
if (conv_error == 0) {
- conv_error = readfile_linenr(linecnt,
- ptr, (char_u *)top);
+ conv_error = readfile_linenr(linecnt, (char_u *)ptr, (char_u *)top);
}
// Deal with a bad byte and continue with the next.
@@ -1205,8 +1202,8 @@ retry:
*top++ = *(fromp - 1);
--to_size;
} else if (bad_char_behavior != BAD_DROP) {
- *top++ = bad_char_behavior;
- --to_size;
+ *top++ = (char)bad_char_behavior;
+ to_size--;
}
}
@@ -1220,14 +1217,14 @@ retry:
// move the linerest to before the converted characters
line_start = ptr - linerest;
memmove(line_start, buffer, (size_t)linerest);
- size = ((char_u *)top - ptr);
+ size = (top - ptr);
}
#endif
if (fio_flags != 0) {
unsigned int u8c;
- char_u *dest;
- char_u *tail = NULL;
+ char *dest;
+ char *tail = NULL;
// Convert Unicode or Latin1 to UTF-8.
// Go from end to start through the buffer, because the number
@@ -1236,7 +1233,7 @@ retry:
// to after the next character to convert.
dest = ptr + real_size;
if (fio_flags == FIO_LATIN1 || fio_flags == FIO_UTF8) {
- p = ptr + size;
+ p = (uint8_t *)ptr + size;
if (fio_flags == FIO_UTF8) {
// Check for a trailing incomplete UTF-8 sequence
tail = ptr + size - 1;
@@ -1246,35 +1243,35 @@ retry:
if (tail + utf_byte2len(*tail) <= ptr + size) {
tail = NULL;
} else {
- p = tail;
+ p = (uint8_t *)tail;
}
}
} else if (fio_flags & (FIO_UCS2 | FIO_UTF16)) {
// Check for a trailing byte
- p = ptr + (size & ~1);
+ p = (uint8_t *)ptr + (size & ~1);
if (size & 1) {
- tail = p;
+ tail = (char *)p;
}
- if ((fio_flags & FIO_UTF16) && p > ptr) {
+ if ((fio_flags & FIO_UTF16) && p > (uint8_t *)ptr) {
// Check for a trailing leading word
if (fio_flags & FIO_ENDIAN_L) {
- u8c = (*--p << 8);
+ u8c = (unsigned)(*--p) << 8;
u8c += *--p;
} else {
u8c = *--p;
- u8c += (*--p << 8);
+ u8c += (unsigned)(*--p) << 8;
}
if (u8c >= 0xd800 && u8c <= 0xdbff) {
- tail = p;
+ tail = (char *)p;
} else {
p += 2;
}
}
} else { // FIO_UCS4
// Check for trailing 1, 2 or 3 bytes
- p = ptr + (size & ~3);
+ p = (uint8_t *)ptr + (size & ~3);
if (size & 3) {
- tail = p;
+ tail = (char *)p;
}
}
@@ -1282,40 +1279,38 @@ retry:
// conv_rest[].
if (tail != NULL) {
conv_restlen = (int)((ptr + size) - tail);
- memmove(conv_rest, tail, conv_restlen);
+ memmove(conv_rest, tail, (size_t)conv_restlen);
size -= conv_restlen;
}
-
- while (p > ptr) {
+ while (p > (uint8_t *)ptr) {
if (fio_flags & FIO_LATIN1) {
u8c = *--p;
} else if (fio_flags & (FIO_UCS2 | FIO_UTF16)) {
if (fio_flags & FIO_ENDIAN_L) {
- u8c = (*--p << 8);
+ u8c = (unsigned)(*--p) << 8;
u8c += *--p;
} else {
u8c = *--p;
- u8c += (*--p << 8);
+ u8c += (unsigned)(*--p) << 8;
}
if ((fio_flags & FIO_UTF16)
&& u8c >= 0xdc00 && u8c <= 0xdfff) {
int u16c;
- if (p == ptr) {
+ if (p == (uint8_t *)ptr) {
// Missing leading word.
if (can_retry) {
goto rewind_retry;
}
if (conv_error == 0) {
- conv_error = readfile_linenr(linecnt,
- ptr, p);
+ conv_error = readfile_linenr(linecnt, (char_u *)ptr, p);
}
if (bad_char_behavior == BAD_DROP) {
continue;
}
if (bad_char_behavior != BAD_KEEP) {
- u8c = bad_char_behavior;
+ u8c = (unsigned)bad_char_behavior;
}
}
@@ -1328,7 +1323,7 @@ retry:
u16c = *--p;
u16c += (*--p << 8);
}
- u8c = 0x10000 + ((u16c & 0x3ff) << 10)
+ u8c = 0x10000 + (((unsigned)u16c & 0x3ff) << 10)
+ (u8c & 0x3ff);
// Check if the word is indeed a leading word.
@@ -1337,14 +1332,13 @@ retry:
goto rewind_retry;
}
if (conv_error == 0) {
- conv_error = readfile_linenr(linecnt,
- ptr, p);
+ conv_error = readfile_linenr(linecnt, (char_u *)ptr, p);
}
if (bad_char_behavior == BAD_DROP) {
continue;
}
if (bad_char_behavior != BAD_KEEP) {
- u8c = bad_char_behavior;
+ u8c = (unsigned)bad_char_behavior;
}
}
}
@@ -1368,9 +1362,9 @@ retry:
if (*--p < 0x80) {
u8c = *p;
} else {
- len = utf_head_off(ptr, p);
+ len = utf_head_off((char_u *)ptr, p);
p -= len;
- u8c = utf_ptr2char(p);
+ u8c = (unsigned)utf_ptr2char((char *)p);
if (len == 0) {
// Not a valid UTF-8 character, retry with
// another fenc when possible, otherwise just
@@ -1379,14 +1373,13 @@ retry:
goto rewind_retry;
}
if (conv_error == 0) {
- conv_error = readfile_linenr(linecnt,
- ptr, p);
+ conv_error = readfile_linenr(linecnt, (char_u *)ptr, p);
}
if (bad_char_behavior == BAD_DROP) {
continue;
}
if (bad_char_behavior != BAD_KEEP) {
- u8c = bad_char_behavior;
+ u8c = (unsigned)bad_char_behavior;
}
}
}
@@ -1406,8 +1399,8 @@ retry:
bool incomplete_tail = false;
// Reading UTF-8: Check if the bytes are valid UTF-8.
- for (p = ptr;; p++) {
- int todo = (int)((ptr + size) - p);
+ for (p = (uint8_t *)ptr;; p++) {
+ int todo = (int)(((uint8_t *)ptr + size) - p);
int l;
if (todo <= 0) {
@@ -1424,15 +1417,15 @@ retry:
// a truncated file is more likely, or attempting
// to read the rest of an incomplete sequence when
// we have already done so.
- if (p > ptr || filesize > 0) {
+ if (p > (uint8_t *)ptr || filesize > 0) {
incomplete_tail = true;
}
// Incomplete byte sequence, move it to conv_rest[]
// and try to read the rest of it, unless we've
// already done so.
- if (p > ptr) {
+ if (p > (uint8_t *)ptr) {
conv_restlen = todo;
- memmove(conv_rest, p, conv_restlen);
+ memmove(conv_rest, p, (size_t)conv_restlen);
size -= conv_restlen;
break;
}
@@ -1447,28 +1440,28 @@ retry:
#ifdef HAVE_ICONV
// When we did a conversion report an error.
if (iconv_fd != (iconv_t)-1 && conv_error == 0) {
- conv_error = readfile_linenr(linecnt, ptr, p);
+ conv_error = readfile_linenr(linecnt, (char_u *)ptr, p);
}
#endif
// Remember the first linenr with an illegal byte
if (conv_error == 0 && illegal_byte == 0) {
- illegal_byte = readfile_linenr(linecnt, ptr, p);
+ illegal_byte = readfile_linenr(linecnt, (char_u *)ptr, p);
}
// Drop, keep or replace the bad byte.
if (bad_char_behavior == BAD_DROP) {
- memmove(p, p + 1, todo - 1);
- --p;
- --size;
+ memmove(p, p + 1, (size_t)(todo - 1));
+ p--;
+ size--;
} else if (bad_char_behavior != BAD_KEEP) {
- *p = bad_char_behavior;
+ *p = (uint8_t)bad_char_behavior;
}
} else {
p += l - 1;
}
}
}
- if (p < ptr + size && !incomplete_tail) {
+ if (p < (uint8_t *)ptr + size && !incomplete_tail) {
// Detected a UTF-8 error.
rewind_retry:
// Retry reading with another conversion.
@@ -1502,10 +1495,10 @@ rewind_retry:
try_mac = 1;
}
- for (p = ptr; p < ptr + size; ++p) {
+ for (p = (uint8_t *)ptr; p < (uint8_t *)ptr + size; p++) {
if (*p == NL) {
if (!try_unix
- || (try_dos && p > ptr && p[-1] == CAR)) {
+ || (try_dos && p > (uint8_t *)ptr && p[-1] == CAR)) {
fileformat = EOL_DOS;
} else {
fileformat = EOL_UNIX;
@@ -1521,10 +1514,9 @@ rewind_retry:
// Need to reset the counters when retrying fenc.
try_mac = 1;
try_unix = 1;
- for (; p >= ptr && *p != CAR; p--) {
- }
- if (p >= ptr) {
- for (p = ptr; p < ptr + size; ++p) {
+ for (; p >= (uint8_t *)ptr && *p != CAR; p--) {}
+ if (p >= (uint8_t *)ptr) {
+ for (p = (uint8_t *)ptr; p < (uint8_t *)ptr + size; p++) {
if (*p == NL) {
try_unix++;
} else if (*p == CAR) {
@@ -1583,7 +1575,7 @@ rewind_retry:
break;
}
if (read_undo_file) {
- sha256_update(&sha_ctx, line_start, len);
+ sha256_update(&sha_ctx, (char_u *)line_start, (size_t)len);
}
++lnum;
if (--read_count == 0) {
@@ -1639,7 +1631,7 @@ rewind_retry:
break;
}
if (read_undo_file) {
- sha256_update(&sha_ctx, line_start, len);
+ sha256_update(&sha_ctx, (char_u *)line_start, (size_t)len);
}
++lnum;
if (--read_count == 0) {
@@ -1686,7 +1678,7 @@ failed:
error = true;
} else {
if (read_undo_file) {
- sha256_update(&sha_ctx, line_start, len);
+ sha256_update(&sha_ctx, (char_u *)line_start, (size_t)len);
}
read_no_eol_lnum = ++lnum;
}
@@ -1716,21 +1708,23 @@ failed:
xfree(buffer);
if (read_stdin) {
- close(0);
+ close(fd);
+ if (stdin_fd < 0) {
#ifndef WIN32
- // On Unix, use stderr for stdin, makes shell commands work.
- vim_ignored = dup(2);
+ // On Unix, use stderr for stdin, makes shell commands work.
+ vim_ignored = dup(2);
#else
- // On Windows, use the console input handle for stdin.
- HANDLE conin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES)NULL,
- OPEN_EXISTING, 0, (HANDLE)NULL);
- vim_ignored = _open_osfhandle(conin, _O_RDONLY);
+ // On Windows, use the console input handle for stdin.
+ HANDLE conin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES)NULL,
+ OPEN_EXISTING, 0, (HANDLE)NULL);
+ vim_ignored = _open_osfhandle(conin, _O_RDONLY);
#endif
+ }
}
if (tmpname != NULL) {
- os_remove((char *)tmpname); // delete converted file
+ os_remove(tmpname); // delete converted file
xfree(tmpname);
}
--no_wait_return; // may wait for return now
@@ -1775,7 +1769,7 @@ failed:
if (got_int) {
if (!(flags & READ_DUMMY)) {
- filemess(curbuf, sfname, (char_u *)_(e_interr), 0);
+ filemess(curbuf, (char_u *)sfname, (char_u *)_(e_interr), 0);
if (newfile) {
curbuf->b_p_ro = TRUE; // must use "w!" now
}
@@ -1785,7 +1779,7 @@ failed:
return OK; // an interrupt isn't really an error
}
- if (!filtering && !(flags & READ_DUMMY)) {
+ if (!filtering && !(flags & READ_DUMMY) && !silent) {
add_quoted_fname((char *)IObuff, IOSIZE, curbuf, (const char *)sfname);
c = false;
@@ -1888,13 +1882,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.cmod_flags & CMOD_LOCKMARKS) == 0) {
+ // 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;
@@ -1924,7 +1918,7 @@ failed:
char_u hash[UNDO_HASH_SIZE];
sha256_finish(&sha_ctx, hash);
- u_read_undo(NULL, hash, fname);
+ u_read_undo(NULL, hash, (char_u *)fname);
}
if (!read_stdin && !read_fifo && (!read_buffer || sfname != NULL)) {
@@ -1952,8 +1946,7 @@ failed:
if (!au_did_filetype && *curbuf->b_p_ft != NUL) {
// EVENT_FILETYPE was not triggered but the buffer already has a
// filetype. Trigger EVENT_FILETYPE using the existing filetype.
- apply_autocmds(EVENT_FILETYPE, curbuf->b_p_ft, curbuf->b_fname,
- true, curbuf);
+ apply_autocmds(EVENT_FILETYPE, (char *)curbuf->b_p_ft, curbuf->b_fname, true, curbuf);
}
} else {
apply_autocmds_exarg(EVENT_FILEREADPOST, sfname, sfname,
@@ -1980,18 +1973,17 @@ failed:
/// Do not accept "/dev/fd/[012]", opening these may hang Vim.
///
/// @param fname file name to check
-bool is_dev_fd_file(char_u *fname)
+bool is_dev_fd_file(char *fname)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
return STRNCMP(fname, "/dev/fd/", 8) == 0
- && ascii_isdigit(fname[8])
+ && ascii_isdigit((uint8_t)fname[8])
&& *skipdigits(fname + 9) == NUL
&& (fname[9] != NUL
|| (fname[8] != '0' && fname[8] != '1' && fname[8] != '2'));
}
#endif
-
/// From the current line count and characters read after that, estimate the
/// line number where we are now.
/// Used for error messages that include a line number.
@@ -2013,17 +2005,15 @@ static linenr_T readfile_linenr(linenr_T linecnt, char_u *p, char_u *endp)
return lnum;
}
-/*
- * Fill "*eap" to force the 'fileencoding', 'fileformat' and 'binary to be
- * equal to the buffer "buf". Used for calling readfile().
- */
+/// Fill "*eap" to force the 'fileencoding', 'fileformat' and 'binary' to be
+/// equal to the buffer "buf". Used for calling readfile().
void prep_exarg(exarg_T *eap, const buf_T *buf)
FUNC_ATTR_NONNULL_ALL
{
const size_t cmd_len = 15 + STRLEN(buf->b_p_fenc);
eap->cmd = xmalloc(cmd_len);
- snprintf((char *)eap->cmd, cmd_len, "e ++enc=%s", buf->b_p_fenc);
+ snprintf(eap->cmd, cmd_len, "e ++enc=%s", buf->b_p_fenc);
eap->force_enc = 8;
eap->bad_char = buf->b_bad_char;
eap->force_ff = *buf->b_p_ff;
@@ -2033,9 +2023,7 @@ void prep_exarg(exarg_T *eap, const buf_T *buf)
eap->forceit = FALSE;
}
-/*
- * Set default or forced 'fileformat' and 'binary'.
- */
+/// Set default or forced 'fileformat' and 'binary'.
void set_file_options(int set_options, exarg_T *eap)
{
// set default 'fileformat'
@@ -2056,24 +2044,22 @@ void set_file_options(int set_options, exarg_T *eap)
}
}
-/*
- * Set forced 'fileencoding'.
- */
+/// Set forced 'fileencoding'.
void set_forced_fenc(exarg_T *eap)
{
if (eap->force_enc != 0) {
- char_u *fenc = enc_canonize(eap->cmd + eap->force_enc);
- set_string_option_direct("fenc", -1, fenc, OPT_FREE|OPT_LOCAL, 0);
+ char_u *fenc = enc_canonize((char_u *)eap->cmd + eap->force_enc);
+ set_string_option_direct("fenc", -1, (char *)fenc, OPT_FREE|OPT_LOCAL, 0);
xfree(fenc);
}
}
-// Find next fileencoding to use from 'fileencodings'.
-// "pp" points to fenc_next. It's advanced to the next item.
-// When there are no more items, an empty string is returned and *pp is set to
-// NULL.
-// When *pp is not set to NULL, the result is in allocated memory and "alloced"
-// is set to true.
+/// Find next fileencoding to use from 'fileencodings'.
+/// "pp" points to fenc_next. It's advanced to the next item.
+/// When there are no more items, an empty string is returned and *pp is set to
+/// NULL.
+/// When *pp is not set to NULL, the result is in allocated memory and "alloced"
+/// is set to true.
static char_u *next_fenc(char_u **pp, bool *alloced)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET
{
@@ -2085,12 +2071,12 @@ static char_u *next_fenc(char_u **pp, bool *alloced)
*pp = NULL;
return (char_u *)"";
}
- p = vim_strchr(*pp, ',');
+ p = (char_u *)vim_strchr((char *)(*pp), ',');
if (p == NULL) {
r = enc_canonize(*pp);
*pp += STRLEN(*pp);
} else {
- r = vim_strnsave(*pp, p - *pp);
+ r = vim_strnsave(*pp, (size_t)(p - *pp));
*pp = p + 1;
p = enc_canonize(r);
xfree(r);
@@ -2148,11 +2134,8 @@ static char_u *readfile_charconvert(char_u *fname, char_u *fenc, int *fdp)
return tmpname;
}
-
-/*
- * Read marks for the current buffer from the ShaDa file, when we support
- * buffer marks and the buffer has a name.
- */
+/// Read marks for the current buffer from the ShaDa file, when we support
+/// buffer marks and the buffer has a name.
static void check_marks_read(void)
{
if (!curbuf->b_marks_read && get_shada_parameter('\'') > 0
@@ -2188,34 +2171,34 @@ char *new_file_message(void)
/// @param append append to the file
///
/// @return FAIL for failure, OK otherwise
-int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_T end, exarg_T *eap,
+int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T end, exarg_T *eap,
int append, int forceit, int reset_changed, int filtering)
{
int fd;
- char_u *backup = NULL;
- int backup_copy = FALSE; // copy the original file?
+ char *backup = NULL;
+ int backup_copy = false; // copy the original file?
int dobackup;
- char_u *ffname;
- char_u *wfname = NULL; // name of file to write to
- char_u *s;
- char_u *ptr;
- char_u c;
+ char *ffname;
+ char *wfname = NULL; // name of file to write to
+ char *s;
+ char *ptr;
+ char c;
int len;
linenr_T lnum;
long nchars;
#define SET_ERRMSG_NUM(num, msg) \
- errnum = num, errmsg = msg, errmsgarg = 0
+ errnum = (num), errmsg = (msg), errmsgarg = 0
#define SET_ERRMSG_ARG(msg, error) \
- errnum = NULL, errmsg = msg, errmsgarg = error
+ errnum = NULL, errmsg = (msg), errmsgarg = error
#define SET_ERRMSG(msg) \
- errnum = NULL, errmsg = msg, errmsgarg = 0
+ errnum = NULL, errmsg = (msg), errmsgarg = 0
const char *errnum = NULL;
char *errmsg = NULL;
int errmsgarg = 0;
bool errmsg_allocated = false;
- char_u *buffer;
- char_u smallbuf[SMBUFSIZE];
- char_u *backup_ext;
+ char *buffer;
+ char smallbuf[SMBUFSIZE];
+ char *backup_ext;
int bufsize;
long perm; // file permissions
int retval = OK;
@@ -2238,10 +2221,10 @@ int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_
int fileformat;
int write_bin;
struct bw_info write_info; // info for buf_write_bytes()
- int converted = FALSE;
- int notconverted = FALSE;
- char_u *fenc; // effective 'fileencoding'
- char_u *fenc_tofree = NULL; // allocated "fenc"
+ int converted = false;
+ int notconverted = false;
+ char *fenc; // effective 'fileencoding'
+ char *fenc_tofree = NULL; // allocated "fenc"
#ifdef HAS_BW_FLAGS
int wb_flags = 0;
#endif
@@ -2252,6 +2235,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;
@@ -2301,11 +2286,11 @@ int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_
&& reset_changed
&& whole
&& buf == curbuf
- && !bt_nofile(buf)
+ && !bt_nofilename(buf)
&& !filtering
&& (!append || vim_strchr(p_cpo, CPO_FNAMEAPP) != NULL)
&& vim_strchr(p_cpo, CPO_FNAMEW) != NULL) {
- if (set_rw_fname(fname, sfname) == FAIL) {
+ if (set_rw_fname((char_u *)fname, (char_u *)sfname) == FAIL) {
return FAIL;
}
buf = curbuf; // just in case autocmds made "buf" invalid
@@ -2324,8 +2309,8 @@ int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_
fname = sfname;
#endif
- if (buf->b_ffname != NULL && fnamecmp(ffname, buf->b_ffname) == 0) {
- overwriting = TRUE;
+ if (buf->b_ffname != NULL && FNAMECMP(ffname, buf->b_ffname) == 0) {
+ overwriting = true;
} else {
overwriting = FALSE;
}
@@ -2357,16 +2342,16 @@ int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_
* Careful: The autocommands may call buf_write() recursively!
*/
if (ffname == buf->b_ffname) {
- buf_ffname = TRUE;
+ buf_ffname = true;
}
if (sfname == buf->b_sfname) {
- buf_sfname = TRUE;
+ buf_sfname = true;
}
if (fname == buf->b_ffname) {
- buf_fname_f = TRUE;
+ buf_fname_f = true;
}
if (fname == buf->b_sfname) {
- buf_fname_s = TRUE;
+ buf_fname_s = true;
}
// Set curwin/curbuf to buf and save a few things.
@@ -2375,22 +2360,22 @@ int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_
if (append) {
if (!(did_cmd = apply_autocmds_exarg(EVENT_FILEAPPENDCMD,
- sfname, sfname, FALSE, curbuf, eap))) {
- if (overwriting && bt_nofile(curbuf)) {
- nofile_err = TRUE;
+ sfname, sfname, false, curbuf, eap))) {
+ if (overwriting && bt_nofilename(curbuf)) {
+ nofile_err = true;
} else {
apply_autocmds_exarg(EVENT_FILEAPPENDPRE,
- sfname, sfname, FALSE, curbuf, eap);
+ sfname, sfname, false, curbuf, eap);
}
}
} else if (filtering) {
apply_autocmds_exarg(EVENT_FILTERWRITEPRE,
- NULL, sfname, FALSE, curbuf, eap);
+ NULL, sfname, false, curbuf, eap);
} else if (reset_changed && whole) {
int was_changed = curbufIsChanged();
did_cmd = apply_autocmds_exarg(EVENT_BUFWRITECMD,
- sfname, sfname, FALSE, curbuf, eap);
+ sfname, sfname, false, curbuf, eap);
if (did_cmd) {
if (was_changed && !curbufIsChanged()) {
/* Written everything correctly and BufWriteCmd has reset
@@ -2400,21 +2385,21 @@ int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_
u_update_save_nr(curbuf);
}
} else {
- if (overwriting && bt_nofile(curbuf)) {
- nofile_err = TRUE;
+ if (overwriting && bt_nofilename(curbuf)) {
+ nofile_err = true;
} else {
apply_autocmds_exarg(EVENT_BUFWRITEPRE,
- sfname, sfname, FALSE, curbuf, eap);
+ sfname, sfname, false, curbuf, eap);
}
}
} else {
if (!(did_cmd = apply_autocmds_exarg(EVENT_FILEWRITECMD,
- sfname, sfname, FALSE, curbuf, eap))) {
- if (overwriting && bt_nofile(curbuf)) {
- nofile_err = TRUE;
+ sfname, sfname, false, curbuf, eap))) {
+ if (overwriting && bt_nofilename(curbuf)) {
+ nofile_err = true;
} else {
apply_autocmds_exarg(EVENT_FILEWRITEPRE,
- sfname, sfname, FALSE, curbuf, eap);
+ sfname, sfname, false, curbuf, eap);
}
}
}
@@ -2432,7 +2417,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.cmod_flags & CMOD_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"));
@@ -2461,8 +2452,7 @@ int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_
}
if (reset_changed && buf->b_changed && !append
&& (overwriting || vim_strchr(p_cpo, CPO_PLUS) != NULL)) {
- /* Buffer still changed, the autocommands didn't work
- * properly. */
+ // Buffer still changed, the autocommands didn't work properly.
return FAIL;
}
return OK;
@@ -2513,6 +2503,11 @@ int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_
}
}
+ if (cmdmod.cmod_flags & CMOD_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
@@ -2522,9 +2517,9 @@ int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_
if (!filtering) {
filemess(buf,
#ifndef UNIX
- sfname,
+ (char_u *)sfname,
#else
- fname,
+ (char_u *)fname,
#endif
(char_u *)"", 0); // show that we are busy
}
@@ -2546,16 +2541,16 @@ int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_
FileInfo file_info_old;
#if defined(UNIX)
perm = -1;
- if (!os_fileinfo((char *)fname, &file_info_old)) {
- newfile = TRUE;
+ if (!os_fileinfo(fname, &file_info_old)) {
+ newfile = true;
} else {
- perm = file_info_old.stat.st_mode;
+ perm = (long)file_info_old.stat.st_mode;
if (!S_ISREG(file_info_old.stat.st_mode)) { // not a file
if (S_ISDIR(file_info_old.stat.st_mode)) {
SET_ERRMSG_NUM("E502", _("is a directory"));
goto fail;
}
- if (os_nodetype((char *)fname) != NODE_WRITABLE) {
+ if (os_nodetype(fname) != NODE_WRITABLE) {
SET_ERRMSG_NUM("E503", _("is not a file or writable device"));
goto fail;
}
@@ -2596,7 +2591,7 @@ int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_
* Check if the file is really writable (when renaming the file to
* make a backup we won't discover it later).
*/
- file_readonly = !os_file_is_writable((char *)fname);
+ file_readonly = !os_file_is_writable(fname);
if (!forceit && file_readonly) {
if (vim_strchr(p_cpo, CPO_FWRITE) != NULL) {
@@ -2623,7 +2618,7 @@ int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_
* For systems that support ACL: get the ACL from the original file.
*/
if (!newfile) {
- acl = mch_get_acl(fname);
+ acl = mch_get_acl((char_u *)fname);
}
#endif
@@ -2631,8 +2626,8 @@ int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_
* If 'backupskip' is not empty, don't make a backup for some files.
*/
dobackup = (p_wb || p_bk || *p_pm != NUL);
- if (dobackup && *p_bsk != NUL && match_file_list(p_bsk, sfname, ffname)) {
- dobackup = FALSE;
+ if (dobackup && *p_bsk != NUL && match_file_list(p_bsk, (char_u *)sfname, (char_u *)ffname)) {
+ dobackup = false;
}
/*
@@ -2670,7 +2665,7 @@ int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_
* - we don't have write permission in the directory
*/
if (os_fileinfo_hardlinks(&file_info_old) > 1
- || !os_fileinfo_link((char *)fname, &file_info)
+ || !os_fileinfo_link(fname, &file_info)
|| !os_fileinfo_id_equal(&file_info, &file_info_old)) {
backup_copy = TRUE;
} else {
@@ -2682,18 +2677,20 @@ int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_
*/
STRCPY(IObuff, fname);
for (i = 4913;; i += 123) {
- sprintf((char *)path_tail(IObuff), "%d", i);
+ char *tail = path_tail((char *)IObuff);
+ size_t size = (size_t)((char_u *)tail - IObuff);
+ snprintf(tail, IOSIZE - size, "%d", i);
if (!os_fileinfo_link((char *)IObuff, &file_info)) {
break;
}
}
fd = os_open((char *)IObuff,
- O_CREAT|O_WRONLY|O_EXCL|O_NOFOLLOW, perm);
+ O_CREAT|O_WRONLY|O_EXCL|O_NOFOLLOW, (int)perm);
if (fd < 0) { // can't write in directory
backup_copy = TRUE;
} else {
#ifdef UNIX
- os_fchown(fd, file_info_old.stat.st_uid, file_info_old.stat.st_gid);
+ os_fchown(fd, (uv_uid_t)file_info_old.stat.st_uid, (uv_gid_t)file_info_old.stat.st_gid);
if (!os_fileinfo((char *)IObuff, &file_info)
|| file_info.stat.st_uid != file_info_old.stat.st_uid
|| file_info.stat.st_gid != file_info_old.stat.st_gid
@@ -2714,7 +2711,7 @@ int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_
*/
if ((bkc & BKC_BREAKSYMLINK) || (bkc & BKC_BREAKHARDLINK)) {
#ifdef UNIX
- bool file_info_link_ok = os_fileinfo_link((char *)fname, &file_info);
+ bool file_info_link_ok = os_fileinfo_link(fname, &file_info);
// Symlinks.
if ((bkc & BKC_BREAKSYMLINK)
@@ -2735,17 +2732,17 @@ int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_
// make sure we have a valid backup extension to use
if (*p_bex == NUL) {
- backup_ext = (char_u *)".bak";
+ backup_ext = ".bak";
} else {
- backup_ext = p_bex;
+ backup_ext = (char *)p_bex;
}
if (backup_copy) {
- char_u *wp;
+ char *wp;
int some_error = false;
- char_u *dirp;
- char_u *rootname;
- char_u *p;
+ char *dirp;
+ char *rootname;
+ char *p;
/*
* Try to make the backup in each directory in the 'bdir' option.
@@ -2759,14 +2756,14 @@ int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_
* For these reasons, the existing writable file must be truncated
* and reused. Creation of a backup COPY will be attempted.
*/
- dirp = p_bdir;
+ dirp = (char *)p_bdir;
while (*dirp) {
/*
* Isolate one directory name, using an entry in 'bdir'.
*/
- size_t dir_len = copy_option_part(&dirp, IObuff, IOSIZE, ",");
- p = IObuff + dir_len;
- bool trailing_pathseps = after_pathsep((char *)IObuff, (char *)p) && p[-1] == p[-2];
+ size_t dir_len = copy_option_part(&dirp, (char *)IObuff, IOSIZE, ",");
+ p = (char *)IObuff + dir_len;
+ bool trailing_pathseps = after_pathsep((char *)IObuff, p) && p[-1] == p[-2];
if (trailing_pathseps) {
IObuff[dir_len - 2] = NUL;
}
@@ -2781,15 +2778,14 @@ int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_
}
if (trailing_pathseps) {
// Ends with '//', Use Full path
- if ((p = (char_u *)make_percent_swname((char *)IObuff, (char *)fname))
+ if ((p = make_percent_swname((char *)IObuff, fname))
!= NULL) {
- backup = (char_u *)modname((char *)p, (char *)backup_ext,
- no_prepend_dot);
+ backup = modname(p, backup_ext, no_prepend_dot);
xfree(p);
}
}
- rootname = get_file_in_dir(fname, IObuff);
+ rootname = (char *)get_file_in_dir((char_u *)fname, IObuff);
if (rootname == NULL) {
some_error = TRUE; // out of memory
goto nobackup;
@@ -2801,8 +2797,7 @@ int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_
// Make the backup file name.
//
if (backup == NULL) {
- backup = (char_u *)modname((char *)rootname, (char *)backup_ext,
- no_prepend_dot);
+ backup = modname(rootname, backup_ext, no_prepend_dot);
}
if (backup == NULL) {
@@ -2814,7 +2809,7 @@ int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_
/*
* Check if backup file already exists.
*/
- if (os_fileinfo((char *)backup, &file_info_new)) {
+ if (os_fileinfo(backup, &file_info_new)) {
if (os_fileinfo_id_equal(&file_info_new, &file_info_old)) {
//
// Backup file is same as original file.
@@ -2833,9 +2828,8 @@ int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_
wp = backup;
}
*wp = 'z';
- while (*wp > 'a'
- && os_fileinfo((char *)backup, &file_info_new)) {
- --*wp;
+ while (*wp > 'a' && os_fileinfo(backup, &file_info_new)) {
+ (*wp)--;
}
// They all exist??? Must be something wrong.
if (*wp == 'a') {
@@ -2851,7 +2845,7 @@ int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_
*/
if (backup != NULL) {
// remove old backup, if present
- os_remove((char *)backup);
+ os_remove(backup);
// set file protection same as original file, but
// strip s-bit.
@@ -2864,26 +2858,26 @@ int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_
// protection bits for others.
//
if (file_info_new.stat.st_gid != file_info_old.stat.st_gid
- && os_chown((char *)backup, -1, file_info_old.stat.st_gid) != 0) {
+ && os_chown(backup, (uv_uid_t)-1, (uv_gid_t)file_info_old.stat.st_gid) != 0) {
os_setperm((const char *)backup,
- (perm & 0707) | ((perm & 07) << 3));
+ ((int)perm & 0707) | (((int)perm & 07) << 3));
}
#endif
// copy the file
- if (os_copy((char *)fname, (char *)backup, UV_FS_COPYFILE_FICLONE)
+ if (os_copy(fname, backup, UV_FS_COPYFILE_FICLONE)
!= 0) {
SET_ERRMSG(_("E506: Can't write to backup file "
"(add ! to override)"));
}
#ifdef UNIX
- os_file_settime((char *)backup,
- file_info_old.stat.st_atim.tv_sec,
- file_info_old.stat.st_mtim.tv_sec);
+ os_file_settime(backup,
+ (double)file_info_old.stat.st_atim.tv_sec,
+ (double)file_info_old.stat.st_mtim.tv_sec);
#endif
#ifdef HAVE_ACL
- mch_set_acl(backup, acl);
+ mch_set_acl((char_u *)backup, acl);
#endif
break;
}
@@ -2900,9 +2894,9 @@ nobackup:
}
SET_ERRMSG(NULL);
} else {
- char_u *dirp;
- char_u *p;
- char_u *rootname;
+ char *dirp;
+ char *p;
+ char *rootname;
/*
* Make a backup by renaming the original file.
@@ -2923,14 +2917,14 @@ nobackup:
* path/fo.o.h.bak Try all directories in 'backupdir', first one
* that works is used.
*/
- dirp = p_bdir;
+ dirp = (char *)p_bdir;
while (*dirp) {
/*
* Isolate one directory name and make the backup file name.
*/
- size_t dir_len = copy_option_part(&dirp, IObuff, IOSIZE, ",");
- p = IObuff + dir_len;
- bool trailing_pathseps = after_pathsep((char *)IObuff, (char *)p) && p[-1] == p[-2];
+ size_t dir_len = copy_option_part(&dirp, (char *)IObuff, IOSIZE, ",");
+ p = (char *)IObuff + dir_len;
+ bool trailing_pathseps = after_pathsep((char *)IObuff, p) && p[-1] == p[-2];
if (trailing_pathseps) {
IObuff[dir_len - 2] = NUL;
}
@@ -2945,21 +2939,19 @@ nobackup:
}
if (trailing_pathseps) {
// path ends with '//', use full path
- if ((p = (char_u *)make_percent_swname((char *)IObuff, (char *)fname))
+ if ((p = make_percent_swname((char *)IObuff, fname))
!= NULL) {
- backup = (char_u *)modname((char *)p, (char *)backup_ext,
- no_prepend_dot);
+ backup = modname(p, backup_ext, no_prepend_dot);
xfree(p);
}
}
if (backup == NULL) {
- rootname = get_file_in_dir(fname, IObuff);
+ rootname = (char *)get_file_in_dir((char_u *)fname, IObuff);
if (rootname == NULL) {
backup = NULL;
} else {
- backup = (char_u *)modname((char *)rootname, (char *)backup_ext,
- no_prepend_dot);
+ backup = modname(rootname, backup_ext, no_prepend_dot);
xfree(rootname);
}
}
@@ -2970,13 +2962,13 @@ nobackup:
* delete an existing one, try to use another name.
* Change one character, just before the extension.
*/
- if (!p_bk && os_path_exists(backup)) {
+ if (!p_bk && os_path_exists((char_u *)backup)) {
p = backup + STRLEN(backup) - 1 - STRLEN(backup_ext);
if (p < backup) { // empty file name ???
p = backup;
}
*p = 'z';
- while (*p > 'a' && os_path_exists(backup)) {
+ while (*p > 'a' && os_path_exists((char_u *)backup)) {
(*p)--;
}
// They all exist??? Must be something wrong!
@@ -2994,7 +2986,7 @@ nobackup:
// If the renaming of the original file to the backup file
// works, quit here.
///
- if (vim_rename(fname, backup) == 0) {
+ if (vim_rename((char_u *)fname, (char_u *)backup) == 0) {
break;
}
@@ -3014,7 +3006,7 @@ nobackup:
&& file_info_old.stat.st_uid == getuid()
&& vim_strchr(p_cpo, CPO_FWRITE) == NULL) {
perm |= 0200;
- (void)os_setperm((const char *)fname, perm);
+ (void)os_setperm((const char *)fname, (int)perm);
made_writable = true;
}
#endif
@@ -3048,7 +3040,6 @@ nobackup:
}
}
-
// Default: write the file directly. May write to a temp file for
// multi-byte conversion.
wfname = fname;
@@ -3056,26 +3047,26 @@ nobackup:
// Check for forced 'fileencoding' from "++opt=val" argument.
if (eap != NULL && eap->force_enc != 0) {
fenc = eap->cmd + eap->force_enc;
- fenc = enc_canonize(fenc);
+ fenc = (char *)enc_canonize((char_u *)fenc);
fenc_tofree = fenc;
} else {
- fenc = buf->b_p_fenc;
+ fenc = (char *)buf->b_p_fenc;
}
// Check if the file needs to be converted.
- converted = need_conversion(fenc);
+ converted = need_conversion((char_u *)fenc);
// Check if UTF-8 to UCS-2/4 or Latin1 conversion needs to be done. Or
// Latin1 to Unicode conversion. This is handled in buf_write_bytes().
// Prepare the flags for it and allocate bw_conv_buf when needed.
if (converted) {
- wb_flags = get_fio_flags(fenc);
+ wb_flags = get_fio_flags((char_u *)fenc);
if (wb_flags & (FIO_UCS2 | FIO_UCS4 | FIO_UTF16 | FIO_UTF8)) {
// Need to allocate a buffer to translate into.
if (wb_flags & (FIO_UCS2 | FIO_UTF16 | FIO_UTF8)) {
- write_info.bw_conv_buflen = bufsize * 2;
+ write_info.bw_conv_buflen = (size_t)bufsize * 2;
} else { // FIO_UCS4
- write_info.bw_conv_buflen = bufsize * 4;
+ write_info.bw_conv_buflen = (size_t)bufsize * 4;
}
write_info.bw_conv_buf = verbose_try_malloc(write_info.bw_conv_buflen);
if (!write_info.bw_conv_buf) {
@@ -3084,15 +3075,14 @@ nobackup:
}
}
-
if (converted && wb_flags == 0) {
#ifdef HAVE_ICONV
// Use iconv() conversion when conversion is needed and it's not done
// internally.
- write_info.bw_iconv_fd = (iconv_t)my_iconv_open(fenc, (char_u *)"utf-8");
+ write_info.bw_iconv_fd = (iconv_t)my_iconv_open((char_u *)fenc, (char_u *)"utf-8");
if (write_info.bw_iconv_fd != (iconv_t)-1) {
// We're going to use iconv(), allocate a buffer to convert in.
- write_info.bw_conv_buflen = bufsize * ICONV_MULT;
+ write_info.bw_conv_buflen = (size_t)bufsize * ICONV_MULT;
write_info.bw_conv_buf = verbose_try_malloc(write_info.bw_conv_buflen);
if (!write_info.bw_conv_buf) {
end = 0;
@@ -3107,7 +3097,7 @@ nobackup:
* overwrite the original file.
*/
if (*p_ccv != NUL) {
- wfname = vim_tempname();
+ wfname = (char *)vim_tempname();
if (wfname == NULL) { // Can't write without a tempfile!
SET_ERRMSG(_("E214: Can't find temp file for writing"));
goto restore_backup;
@@ -3151,7 +3141,7 @@ nobackup:
// quotum for number of files).
// Appending will fail if the file does not exist and forceit is
// FALSE.
- while ((fd = os_open((char *)wfname,
+ while ((fd = os_open(wfname,
O_WRONLY |
(append ?
(forceit ? (O_APPEND | O_CREAT) : O_APPEND)
@@ -3166,7 +3156,7 @@ nobackup:
// Don't delete the file when it's a hard or symbolic link.
if ((!newfile && os_fileinfo_hardlinks(&file_info_old) > 1)
- || (os_fileinfo_link((char *)fname, &file_info)
+ || (os_fileinfo_link(fname, &file_info)
&& !os_fileinfo_id_equal(&file_info, &file_info_old))) {
SET_ERRMSG(_("E166: Can't open linked file for writing"));
} else {
@@ -3187,7 +3177,7 @@ nobackup:
}
#endif
if (!append) { // don't remove when appending
- os_remove((char *)wfname);
+ os_remove(wfname);
}
continue;
}
@@ -3208,21 +3198,21 @@ restore_backup:
// This may not work if the vim_rename() fails.
// In that case we leave the copy around.
// If file does not exist, put the copy in its place
- if (!os_path_exists(fname)) {
- vim_rename(backup, fname);
+ if (!os_path_exists((char_u *)fname)) {
+ vim_rename((char_u *)backup, (char_u *)fname);
}
// if original file does exist throw away the copy
- if (os_path_exists(fname)) {
- os_remove((char *)backup);
+ if (os_path_exists((char_u *)fname)) {
+ os_remove(backup);
}
} else {
// try to put the original file back
- vim_rename(backup, fname);
+ vim_rename((char_u *)backup, (char_u *)fname);
}
}
// if original file no longer exists give an extra warning
- if (!newfile && !os_path_exists(fname)) {
+ if (!newfile && !os_path_exists((char_u *)fname)) {
end = 0;
}
}
@@ -3236,7 +3226,7 @@ restore_backup:
}
SET_ERRMSG(NULL);
- write_info.bw_buf = buffer;
+ write_info.bw_buf = (char_u *)buffer;
nchars = 0;
// use "++bin", "++nobin" or 'binary'
@@ -3249,7 +3239,7 @@ restore_backup:
// Skip the BOM when appending and the file already existed, the BOM
// only makes sense at the start of the file.
if (buf->b_p_bomb && !write_bin && (!append || perm < 0)) {
- write_info.bw_len = make_bom(buffer, fenc);
+ write_info.bw_len = make_bom((char_u *)buffer, (char_u *)fenc);
if (write_info.bw_len > 0) {
// don't convert
write_info.bw_flags = FIO_NOCONVERT | wb_flags;
@@ -3279,9 +3269,9 @@ restore_backup:
for (lnum = start; lnum <= end; lnum++) {
// The next while loop is done once for each character written.
// Keep it fast!
- ptr = ml_get_buf(buf, lnum, false) - 1;
+ ptr = (char *)ml_get_buf(buf, lnum, false) - 1;
if (write_undo_file) {
- sha256_update(&sha_ctx, ptr + 1, (uint32_t)(STRLEN(ptr + 1) + 1));
+ sha256_update(&sha_ctx, (char_u *)ptr + 1, (uint32_t)(STRLEN(ptr + 1) + 1));
}
while ((c = *++ptr) != NUL) {
if (c == NL) {
@@ -3308,7 +3298,7 @@ restore_backup:
if (end == 0
|| (lnum == end
&& (write_bin || !buf->b_p_fixeol)
- && (lnum == buf->b_no_eol_lnum
+ && ((write_bin && lnum == buf->b_no_eol_lnum)
|| (lnum == buf->b_ml.ml_line_count && !buf->b_p_eol)))) {
lnum++; // written the line, count it
no_eol = true;
@@ -3390,12 +3380,12 @@ restore_backup:
// don't change the owner when it's already OK, some systems remove
// permission or ACL stuff
FileInfo file_info;
- if (!os_fileinfo((char *)wfname, &file_info)
+ if (!os_fileinfo(wfname, &file_info)
|| file_info.stat.st_uid != file_info_old.stat.st_uid
|| file_info.stat.st_gid != file_info_old.stat.st_gid) {
- os_fchown(fd, file_info_old.stat.st_uid, file_info_old.stat.st_gid);
+ os_fchown(fd, (uv_uid_t)file_info_old.stat.st_uid, (uv_gid_t)file_info_old.stat.st_gid);
if (perm >= 0) { // Set permission again, may have changed.
- (void)os_setperm((const char *)wfname, perm);
+ (void)os_setperm(wfname, (int)perm);
}
}
buf_set_file_id(buf);
@@ -3416,13 +3406,13 @@ restore_backup:
}
#endif
if (perm >= 0) { // Set perm. of new file same as old file.
- (void)os_setperm((const char *)wfname, perm);
+ (void)os_setperm((const char *)wfname, (int)perm);
}
#ifdef HAVE_ACL
// Probably need to set the ACL before changing the user (can't set the
// ACL on a file the user doesn't own).
if (!backup_copy) {
- mch_set_acl(wfname, acl);
+ mch_set_acl((char_u *)wfname, acl);
}
#endif
@@ -3430,13 +3420,12 @@ restore_backup:
// The file was written to a temp file, now it needs to be converted
// with 'charconvert' to (overwrite) the output file.
if (end != 0) {
- if (eval_charconvert("utf-8", (char *)fenc,
- (char *)wfname, (char *)fname) == FAIL) {
+ if (eval_charconvert("utf-8", fenc, wfname, fname) == FAIL) {
write_info.bw_conv_error = true;
end = 0;
}
}
- os_remove((char *)wfname);
+ os_remove(wfname);
xfree(wfname);
}
}
@@ -3480,12 +3469,12 @@ restore_backup:
}
// copy the file.
- if (os_copy((char *)backup, (char *)fname, UV_FS_COPYFILE_FICLONE)
+ if (os_copy(backup, fname, UV_FS_COPYFILE_FICLONE)
== 0) {
end = 1; // success
}
} else {
- if (vim_rename(backup, fname) == 0) {
+ if (vim_rename((char_u *)backup, (char_u *)fname) == 0) {
end = 1;
}
}
@@ -3577,7 +3566,7 @@ restore_backup:
* the backup file our 'original' file.
*/
if (*p_pm && dobackup) {
- char *const org = modname((char *)fname, (char *)p_pm, false);
+ char *const org = modname(fname, (char *)p_pm, false);
if (backup != NULL) {
/*
@@ -3587,12 +3576,12 @@ restore_backup:
if (org == NULL) {
emsg(_("E205: Patchmode: can't save original file"));
} else if (!os_path_exists((char_u *)org)) {
- vim_rename(backup, (char_u *)org);
+ vim_rename((char_u *)backup, (char_u *)org);
XFREE_CLEAR(backup); // don't delete the file
#ifdef UNIX
os_file_settime(org,
- file_info_old.stat.st_atim.tv_sec,
- file_info_old.stat.st_mtim.tv_sec);
+ (double)file_info_old.stat.st_atim.tv_sec,
+ (double)file_info_old.stat.st_mtim.tv_sec);
#endif
}
}
@@ -3623,7 +3612,7 @@ restore_backup:
*/
if (!p_bk && backup != NULL
&& !write_info.bw_conv_error
- && os_remove((char *)backup) != 0) {
+ && os_remove(backup) != 0) {
emsg(_("E207: Can't delete backup file"));
}
@@ -3685,11 +3674,12 @@ nofail:
msg_puts_attr(_("don't quit the editor until the file is successfully written!"),
attr | MSG_HIST);
- /* Update the timestamp to avoid an "overwrite changed file"
- * prompt when writing again. */
- if (os_fileinfo((char *)fname, &file_info_old)) {
+ // Update the timestamp to avoid an "overwrite changed file"
+ // prompt when writing again.
+ if (os_fileinfo(fname, &file_info_old)) {
buf_store_file_info(buf, &file_info_old);
buf->b_mtime_read = buf->b_mtime;
+ buf->b_mtime_read_ns = buf->b_mtime_ns;
}
}
}
@@ -3700,10 +3690,10 @@ nofail:
* file.
*/
if (retval == OK && write_undo_file) {
- char_u hash[UNDO_HASH_SIZE];
+ char hash[UNDO_HASH_SIZE];
- sha256_finish(&sha_ctx, hash);
- u_write_undo(NULL, FALSE, buf, hash);
+ sha256_finish(&sha_ctx, (char_u *)hash);
+ u_write_undo(NULL, false, buf, (char_u *)hash);
}
if (!should_abort(retval)) {
@@ -3747,10 +3737,8 @@ nofail:
#undef SET_ERRMSG_NUM
}
-/*
- * Set the name of the current buffer. Use when the buffer doesn't have a
- * name and a ":r" or ":w" command with a file name is used.
- */
+/// Set the name of the current buffer. Use when the buffer doesn't have a
+/// name and a ":r" or ":w" command with a file name is used.
static int set_rw_fname(char_u *fname, char_u *sfname)
{
buf_T *buf = curbuf;
@@ -3769,7 +3757,7 @@ static int set_rw_fname(char_u *fname, char_u *sfname)
return FAIL;
}
- if (setfname(curbuf, fname, sfname, false) == OK) {
+ if (setfname(curbuf, (char *)fname, (char *)sfname, false) == OK) {
curbuf->b_flags |= BF_NOTEDITED;
}
@@ -3784,8 +3772,8 @@ static int set_rw_fname(char_u *fname, char_u *sfname)
// Do filetype detection now if 'filetype' is empty.
if (*curbuf->b_p_ft == NUL) {
- if (au_has_group((char_u *)"filetypedetect")) {
- (void)do_doautocmd((char_u *)"filetypedetect BufRead", false, NULL);
+ if (augroup_exists("filetypedetect")) {
+ (void)do_doautocmd("filetypedetect BufRead", false, NULL);
}
do_modelines(0);
}
@@ -3809,8 +3797,7 @@ static void add_quoted_fname(char *const ret_buf, const size_t buf_len, const bu
fname = "-stdin-";
}
ret_buf[0] = '"';
- home_replace(buf, (const char_u *)fname, (char_u *)ret_buf + 1,
- (int)buf_len - 4, true);
+ home_replace(buf, fname, ret_buf + 1, buf_len - 4, true);
xstrlcat(ret_buf, "\" ", buf_len);
}
@@ -3840,9 +3827,7 @@ static bool msg_add_fileformat(int eol_type)
return false;
}
-/*
- * Append line and character count to IObuff.
- */
+/// Append line and character count to IObuff.
void msg_add_lines(int insert_space, long lnum, off_T nchars)
{
char_u *p;
@@ -3853,38 +3838,33 @@ void msg_add_lines(int insert_space, long lnum, off_T nchars)
*p++ = ' ';
}
if (shortmess(SHM_LINES)) {
- vim_snprintf((char *)p, IOSIZE - (p - IObuff), "%" PRId64 "L, %" PRId64 "C",
+ vim_snprintf((char *)p, (size_t)(IOSIZE - (p - IObuff)), "%" PRId64 "L, %" PRId64 "B",
(int64_t)lnum, (int64_t)nchars);
} else {
- vim_snprintf((char *)p, IOSIZE - (p - IObuff),
+ vim_snprintf((char *)p, (size_t)(IOSIZE - (p - IObuff)),
NGETTEXT("%" PRId64 " line, ", "%" PRId64 " lines, ", lnum),
(int64_t)lnum);
p += STRLEN(p);
- vim_snprintf((char *)p, IOSIZE - (p - IObuff),
- NGETTEXT("%" PRId64 " character", "%" PRId64 " characters", nchars),
+ vim_snprintf((char *)p, (size_t)(IOSIZE - (p - IObuff)),
+ NGETTEXT("%" PRId64 " byte", "%" PRId64 " bytes", nchars),
(int64_t)nchars);
}
}
-/*
- * Append message for missing line separator to IObuff.
- */
+/// Append message for missing line separator to IObuff.
static void msg_add_eol(void)
{
STRCAT(IObuff,
shortmess(SHM_LAST) ? _("[noeol]") : _("[Incomplete last line]"));
}
-/*
- * Check modification time of file, before writing to it.
- * The size isn't checked, because using a tool like "gzip" takes care of
- * using the same timestamp but can't set the size.
- */
+/// Check modification time of file, before writing to it.
+/// The size isn't checked, because using a tool like "gzip" takes care of
+/// using the same timestamp but can't set the size.
static int check_mtime(buf_T *buf, FileInfo *file_info)
{
if (buf->b_mtime_read != 0
- && time_differs(file_info->stat.st_mtim.tv_sec,
- buf->b_mtime_read)) {
+ && time_differs(file_info, buf->b_mtime_read, buf->b_mtime_read_ns)) {
msg_scroll = true; // Don't overwrite messages here.
msg_silent = 0; // Must give this prompt.
// Don't use emsg() here, don't want to flush the buffers.
@@ -3898,28 +3878,24 @@ static int check_mtime(buf_T *buf, FileInfo *file_info)
return OK;
}
-/// Return true if the times differ
-///
-/// @param t1 first time
-/// @param t2 second time
-static bool time_differs(long t1, long t2) FUNC_ATTR_CONST
+static bool time_differs(const FileInfo *file_info, long mtime, long mtime_ns) FUNC_ATTR_CONST
{
+ return file_info->stat.st_mtim.tv_nsec != mtime_ns
#if defined(__linux__) || defined(MSWIN)
- // On a FAT filesystem, esp. under Linux, there are only 5 bits to store
- // the seconds. Since the roundoff is done when flushing the inode, the
- // time may change unexpectedly by one second!!!
- return t1 - t2 > 1 || t2 - t1 > 1;
+ // On a FAT filesystem, esp. under Linux, there are only 5 bits to store
+ // the seconds. Since the roundoff is done when flushing the inode, the
+ // time may change unexpectedly by one second!!!
+ || file_info->stat.st_mtim.tv_sec - mtime > 1
+ || mtime - file_info->stat.st_mtim.tv_sec > 1;
#else
- return t1 != t2;
+ || (long)file_info->stat.st_mtim.tv_sec != mtime;
#endif
}
-/*
- * Call write() to write a number of bytes to the file.
- * Handles 'encoding' conversion.
- *
- * Return FAIL for failure, OK otherwise.
- */
+/// Call write() to write a number of bytes to the file.
+/// Handles 'encoding' conversion.
+///
+/// @return FAIL for failure, OK otherwise.
static int buf_write_bytes(struct bw_info *ip)
{
int wlen;
@@ -3942,8 +3918,8 @@ static int buf_write_bytes(struct bw_info *ip)
* Convert latin1 in the buffer to UTF-8 in the file.
*/
p = ip->bw_conv_buf; // translate to buffer
- for (wlen = 0; wlen < len; ++wlen) {
- p += utf_char2bytes(buf[wlen], p);
+ for (wlen = 0; wlen < len; wlen++) {
+ p += utf_char2bytes(buf[wlen], (char *)p);
}
buf = ip->bw_conv_buf;
len = (int)(p - ip->bw_conv_buf);
@@ -3981,7 +3957,7 @@ static int buf_write_bytes(struct bw_info *ip)
break;
}
if (n > 1) {
- c = utf_ptr2char(ip->bw_rest);
+ c = (unsigned)utf_ptr2char((char *)ip->bw_rest);
} else {
c = ip->bw_rest[0];
}
@@ -4009,7 +3985,7 @@ static int buf_write_bytes(struct bw_info *ip)
break;
}
if (n > 1) {
- c = utf_ptr2char(buf + wlen);
+ c = (unsigned)utf_ptr2char((char *)buf + wlen);
} else {
c = buf[wlen];
}
@@ -4045,7 +4021,7 @@ static int buf_write_bytes(struct bw_info *ip)
/* Need to concatenate the remainder of the previous call and
* the bytes of the current call. Use the end of the
* conversion buffer for this. */
- fromlen = len + ip->bw_restlen;
+ fromlen = (size_t)len + (size_t)ip->bw_restlen;
fp = (char *)ip->bw_conv_buf + ip->bw_conv_buflen - fromlen;
memmove(fp, ip->bw_rest, (size_t)ip->bw_restlen);
memmove(fp + ip->bw_restlen, buf, (size_t)len);
@@ -4053,7 +4029,7 @@ static int buf_write_bytes(struct bw_info *ip)
tolen = ip->bw_conv_buflen - fromlen;
} else {
from = (const char *)buf;
- fromlen = len;
+ fromlen = (size_t)len;
tolen = ip->bw_conv_buflen;
}
to = (char *)ip->bw_conv_buf;
@@ -4100,7 +4076,7 @@ static int buf_write_bytes(struct bw_info *ip)
// Only checking conversion, which is OK if we get here.
return OK;
}
- wlen = write_eintr(ip->bw_fd, buf, len);
+ wlen = (int)write_eintr(ip->bw_fd, buf, (size_t)len);
return (wlen < len) ? FAIL : OK;
}
@@ -4117,18 +4093,17 @@ static bool ucs2bytes(unsigned c, char_u **pp, int flags) FUNC_ATTR_NONNULL_ALL
bool error = false;
int cc;
-
if (flags & FIO_UCS4) {
if (flags & FIO_ENDIAN_L) {
- *p++ = c;
- *p++ = (c >> 8);
- *p++ = (c >> 16);
- *p++ = (c >> 24);
+ *p++ = (uint8_t)c;
+ *p++ = (uint8_t)(c >> 8);
+ *p++ = (uint8_t)(c >> 16);
+ *p++ = (uint8_t)(c >> 24);
} else {
- *p++ = (c >> 24);
- *p++ = (c >> 16);
- *p++ = (c >> 8);
- *p++ = c;
+ *p++ = (uint8_t)(c >> 24);
+ *p++ = (uint8_t)(c >> 16);
+ *p++ = (uint8_t)(c >> 8);
+ *p++ = (uint8_t)c;
}
} else if (flags & (FIO_UCS2 | FIO_UTF16)) {
if (c >= 0x10000) {
@@ -4139,13 +4114,13 @@ static bool ucs2bytes(unsigned c, char_u **pp, int flags) FUNC_ATTR_NONNULL_ALL
if (c >= 0x100000) {
error = true;
}
- cc = ((c >> 10) & 0x3ff) + 0xd800;
+ cc = (int)(((c >> 10) & 0x3ff) + 0xd800);
if (flags & FIO_ENDIAN_L) {
- *p++ = cc;
- *p++ = ((unsigned)cc >> 8);
+ *p++ = (uint8_t)cc;
+ *p++ = (uint8_t)(cc >> 8);
} else {
- *p++ = ((unsigned)cc >> 8);
- *p++ = cc;
+ *p++ = (uint8_t)(cc >> 8);
+ *p++ = (uint8_t)cc;
}
c = (c & 0x3ff) + 0xdc00;
} else {
@@ -4153,18 +4128,18 @@ static bool ucs2bytes(unsigned c, char_u **pp, int flags) FUNC_ATTR_NONNULL_ALL
}
}
if (flags & FIO_ENDIAN_L) {
- *p++ = c;
- *p++ = (c >> 8);
+ *p++ = (uint8_t)c;
+ *p++ = (uint8_t)(c >> 8);
} else {
- *p++ = (c >> 8);
- *p++ = c;
+ *p++ = (uint8_t)(c >> 8);
+ *p++ = (uint8_t)c;
}
} else { // Latin1
if (c >= 0x100) {
error = true;
*p++ = 0xBF;
} else {
- *p++ = c;
+ *p++ = (uint8_t)c;
}
}
@@ -4246,13 +4221,11 @@ static int get_fio_flags(const char_u *name)
return 0;
}
-
-/*
- * Check for a Unicode BOM (Byte Order Mark) at the start of p[size].
- * "size" must be at least 2.
- * Return the name of the encoding and set "*lenp" to the length.
- * Returns NULL when no BOM found.
- */
+/// Check for a Unicode BOM (Byte Order Mark) at the start of p[size].
+/// "size" must be at least 2.
+///
+/// @return the name of the encoding and set "*lenp" to the length or,
+/// NULL when no BOM found.
static char_u *check_for_bom(char_u *p, long size, int *lenp, int flags)
{
char *name = NULL;
@@ -4293,10 +4266,9 @@ static char_u *check_for_bom(char_u *p, long size, int *lenp, int flags)
return (char_u *)name;
}
-/*
- * Generate a BOM in "buf[4]" for encoding "name".
- * Return the length of the BOM (zero when no BOM).
- */
+/// Generate a BOM in "buf[4]" for encoding "name".
+///
+/// @return the length of the BOM (zero when no BOM).
static int make_bom(char_u *buf, char_u *name)
{
int flags;
@@ -4321,28 +4293,30 @@ static int make_bom(char_u *buf, char_u *name)
}
/// Shorten filename of a buffer.
-/// When "force" is TRUE: Use full path from now on for files currently being
-/// edited, both for file name and swap file name. Try to shorten the file
-/// names a bit, if safe to do so.
-/// When "force" is FALSE: Only try to shorten absolute file names.
+///
+/// @param force when TRUE: Use full path from now on for files currently being
+/// edited, both for file name and swap file name. Try to shorten the file
+/// names a bit, if safe to do so.
+/// when FALSE: Only try to shorten absolute file names.
+///
/// For buffers that have buftype "nofile" or "scratch": never change the file
/// name.
void shorten_buf_fname(buf_T *buf, char_u *dirname, int force)
{
- char_u *p;
+ char *p;
if (buf->b_fname != NULL
- && !bt_nofile(buf)
- && !path_with_url((char *)buf->b_fname)
+ && !bt_nofilename(buf)
+ && !path_with_url(buf->b_fname)
&& (force
|| buf->b_sfname == NULL
- || path_is_absolute(buf->b_sfname))) {
+ || path_is_absolute((char_u *)buf->b_sfname))) {
if (buf->b_sfname != buf->b_ffname) {
XFREE_CLEAR(buf->b_sfname);
}
- p = path_shorten_fname(buf->b_ffname, dirname);
+ p = (char *)path_shorten_fname((char_u *)buf->b_ffname, dirname);
if (p != NULL) {
- buf->b_sfname = vim_strsave(p);
+ buf->b_sfname = xstrdup(p);
buf->b_fname = buf->b_sfname;
}
if (p == NULL) {
@@ -4441,7 +4415,7 @@ char *modname(const char *fname, const char *ext, bool prepend_dot)
char *e;
// Prepend the dot if needed.
- if (prepend_dot && *(e = (char *)path_tail((char_u *)retval)) != '.') {
+ if (prepend_dot && *(e = path_tail(retval)) != '.') {
STRMOVE(e + 1, e);
*e = '.';
}
@@ -4502,7 +4476,8 @@ bool vim_fgets(char_u *buf, int size, FILE *fp) FUNC_ATTR_NONNULL_ALL
}
/// Read 2 bytes from "fd" and turn them into an int, MSB first.
-/// Returns -1 when encountering EOF.
+///
+/// @return -1 when encountering EOF.
int get2c(FILE *fd)
{
const int n = getc(fd);
@@ -4517,7 +4492,8 @@ int get2c(FILE *fd)
}
/// Read 3 bytes from "fd" and turn them into an int, MSB first.
-/// Returns -1 when encountering EOF.
+///
+/// @return -1 when encountering EOF.
int get3c(FILE *fd)
{
int n = getc(fd);
@@ -4537,7 +4513,8 @@ int get3c(FILE *fd)
}
/// Read 4 bytes from "fd" and turn them into an int, MSB first.
-/// Returns -1 when encountering EOF.
+///
+/// @return -1 when encountering EOF.
int get4c(FILE *fd)
{
// Use unsigned rather than int otherwise result is undefined
@@ -4568,7 +4545,8 @@ int get4c(FILE *fd)
}
/// Read 8 bytes from `fd` and turn them into a time_t, MSB first.
-/// Returns -1 when encountering EOF.
+///
+/// @return -1 when encountering EOF.
time_t get8ctime(FILE *fd)
{
time_t n = 0;
@@ -4584,7 +4562,8 @@ time_t get8ctime(FILE *fd)
}
/// Reads a string of length "cnt" from "fd" into allocated memory.
-/// @return pointer to the string or NULL when unable to read that many bytes.
+///
+/// @return pointer to the string or NULL when unable to read that many bytes.
char *read_string(FILE *fd, size_t cnt)
{
char *str = xmallocz(cnt);
@@ -4600,7 +4579,8 @@ char *read_string(FILE *fd, size_t cnt)
}
/// Writes a number to file "fd", most significant bit first, in "len" bytes.
-/// @returns false in case of an error.
+///
+/// @return false in case of an error.
bool put_bytes(FILE *fd, uintmax_t number, size_t len)
{
assert(len > 0);
@@ -4613,7 +4593,8 @@ bool put_bytes(FILE *fd, uintmax_t number, size_t len)
}
/// Writes time_t to file "fd" in 8 bytes.
-/// @returns FAIL when the write failed.
+///
+/// @return FAIL when the write failed.
int put_time(FILE *fd, time_t time_)
{
uint8_t buf[8];
@@ -4624,7 +4605,7 @@ int put_time(FILE *fd, time_t time_)
/// os_rename() only works if both files are on the same file system, this
/// function will (attempts to?) copy the file across if rename fails -- webb
///
-/// @return -1 for failure, 0 for success
+/// @return -1 for failure, 0 for success
int vim_rename(const char_u *from, const char_u *to)
FUNC_ATTR_NONNULL_ALL
{
@@ -4644,8 +4625,8 @@ int vim_rename(const char_u *from, const char_u *to)
* to the same file (ignoring case and slash/backslash differences) but
* the file name differs we need to go through a temp file.
*/
- if (fnamecmp(from, to) == 0) {
- if (p_fic && (STRCMP(path_tail((char_u *)from), path_tail((char_u *)to))
+ if (FNAMECMP(from, to) == 0) {
+ if (p_fic && (STRCMP(path_tail((char *)from), path_tail((char *)to))
!= 0)) {
use_tmp_file = true;
} else {
@@ -4680,8 +4661,8 @@ int vim_rename(const char_u *from, const char_u *to)
}
STRCPY(tempname, from);
for (n = 123; n < 99999; n++) {
- char *tail = (char *)path_tail(tempname);
- snprintf(tail, (MAXPATHL + 1) - (tail - (char *)tempname - 1), "%d", n);
+ char *tail = path_tail((char *)tempname);
+ snprintf(tail, (size_t)((MAXPATHL + 1) - (tail - (char *)tempname - 1)), "%d", n);
if (!os_path_exists(tempname)) {
if (os_rename(from, tempname) == OK) {
@@ -4755,8 +4736,8 @@ int vim_rename(const char_u *from, const char_u *to)
return -1;
}
- while ((n = read_eintr(fd_in, buffer, BUFSIZE)) > 0) {
- if (write_eintr(fd_out, buffer, n) != n) {
+ while ((n = (int)read_eintr(fd_in, buffer, BUFSIZE)) > 0) {
+ if (write_eintr(fd_out, buffer, (size_t)n) != n) {
errmsg = _("E208: Error writing to \"%s\"");
break;
}
@@ -4849,11 +4830,10 @@ int check_timestamps(int focus)
return didit;
}
-/*
- * Move all the lines from buffer "frombuf" to buffer "tobuf".
- * Return OK or FAIL. When FAIL "tobuf" is incomplete and/or "frombuf" is not
- * empty.
- */
+/// Move all the lines from buffer "frombuf" to buffer "tobuf".
+///
+/// @return OK or FAIL.
+/// When FAIL "tobuf" is incomplete and/or "frombuf" is not empty.
static int move_lines(buf_T *frombuf, buf_T *tobuf)
{
buf_T *tbuf = curbuf;
@@ -4865,7 +4845,7 @@ static int move_lines(buf_T *frombuf, buf_T *tobuf)
curbuf = tobuf;
for (lnum = 1; lnum <= frombuf->b_ml.ml_line_count; lnum++) {
p = vim_strsave(ml_get_buf(frombuf, lnum, false));
- if (ml_append(lnum - 1, p, 0, false) == FAIL) {
+ if (ml_append(lnum - 1, (char *)p, 0, false) == FAIL) {
xfree(p);
retval = FAIL;
break;
@@ -4890,13 +4870,12 @@ static int move_lines(buf_T *frombuf, buf_T *tobuf)
return retval;
}
-/*
- * Check if buffer "buf" has been changed.
- * Also check if the file for a new buffer unexpectedly appeared.
- * return 1 if a changed buffer was found.
- * return 2 if a message has been displayed.
- * return 0 otherwise.
- */
+/// Check if buffer "buf" has been changed.
+/// Also check if the file for a new buffer unexpectedly appeared.
+///
+/// @return 1 if a changed buffer was found or,
+/// 2 if a message has been displayed or,
+/// 0 otherwise.
int buf_check_timestamp(buf_T *buf)
FUNC_ATTR_NONNULL_ALL
{
@@ -4905,7 +4884,13 @@ int buf_check_timestamp(buf_T *buf)
char *mesg = NULL;
char *mesg2 = "";
bool helpmesg = false;
- bool reload = false;
+
+ enum {
+ RELOAD_NONE,
+ RELOAD_NORMAL,
+ RELOAD_DETECT,
+ } reload = RELOAD_NONE;
+
bool can_reload = false;
uint64_t orig_size = buf->b_orig_size;
int orig_mode = buf->b_orig_mode;
@@ -4932,8 +4917,8 @@ int buf_check_timestamp(buf_T *buf)
bool file_info_ok;
if (!(buf->b_flags & BF_NOTEDITED)
&& buf->b_mtime != 0
- && (!(file_info_ok = os_fileinfo((char *)buf->b_ffname, &file_info))
- || time_differs(file_info.stat.st_mtim.tv_sec, buf->b_mtime)
+ && (!(file_info_ok = os_fileinfo(buf->b_ffname, &file_info))
+ || time_differs(&file_info, buf->b_mtime, buf->b_mtime_ns)
|| (int)file_info.stat.st_mode != buf->b_orig_mode)) {
const long prev_b_mtime = buf->b_mtime;
@@ -4950,15 +4935,14 @@ int buf_check_timestamp(buf_T *buf)
buf_store_file_info(buf, &file_info);
}
- // Don't do anything for a directory. Might contain the file
- // explorer.
- if (os_isdir(buf->b_fname)) {
+ if (os_isdir((char_u *)buf->b_fname)) {
+ // Don't do anything for a directory. Might contain the file explorer.
} else if ((buf->b_p_ar >= 0 ? buf->b_p_ar : p_ar)
&& !bufIsChanged(buf) && file_info_ok) {
// If 'autoread' is set, the buffer has no changes and the file still
// exists, reload the buffer. Use the buffer-local option value if it
// was set, the global option value otherwise.
- reload = true;
+ reload = RELOAD_NORMAL;
} else {
if (!file_info_ok) {
reason = "deleted";
@@ -4979,17 +4963,18 @@ int buf_check_timestamp(buf_T *buf)
set_vim_var_string(VV_FCS_REASON, reason, -1);
set_vim_var_string(VV_FCS_CHOICE, "", -1);
allbuf_lock++;
- bool n = apply_autocmds(EVENT_FILECHANGEDSHELL,
- buf->b_fname, buf->b_fname, false, buf);
+ bool n = apply_autocmds(EVENT_FILECHANGEDSHELL, buf->b_fname, buf->b_fname, false, buf);
allbuf_lock--;
busy = false;
if (n) {
if (!bufref_valid(&bufref)) {
emsg(_("E246: FileChangedShell autocommand deleted buffer"));
}
- s = get_vim_var_str(VV_FCS_CHOICE);
+ s = (char_u *)get_vim_var_str(VV_FCS_CHOICE);
if (STRCMP(s, "reload") == 0 && *reason != 'd') {
- reload = true;
+ reload = RELOAD_NORMAL;
+ } else if (STRCMP(s, "edit") == 0) {
+ reload = RELOAD_DETECT;
} else if (STRCMP(s, "ask") == 0) {
n = false;
} else {
@@ -5024,12 +5009,13 @@ int buf_check_timestamp(buf_T *buf)
// Only timestamp changed, store it to avoid a warning
// in check_mtime() later.
buf->b_mtime_read = buf->b_mtime;
+ buf->b_mtime_read_ns = buf->b_mtime_ns;
}
}
}
}
} else if ((buf->b_flags & BF_NEW) && !(buf->b_flags & BF_NEW_W)
- && os_path_exists(buf->b_ffname)) {
+ && os_path_exists((char_u *)buf->b_ffname)) {
retval = 1;
mesg = _("W13: Warning: File \"%s\" has been created after editing started");
buf->b_flags |= BF_NEW_W;
@@ -5037,7 +5023,7 @@ int buf_check_timestamp(buf_T *buf)
}
if (mesg != NULL) {
- path = home_replace_save(buf, buf->b_fname);
+ path = (char_u *)home_replace_save(buf, buf->b_fname);
if (!helpmesg) {
mesg2 = "";
}
@@ -5052,11 +5038,17 @@ int buf_check_timestamp(buf_T *buf)
xstrlcat(tbuf, "\n", tbuf_len - 1);
xstrlcat(tbuf, mesg2, tbuf_len - 1);
}
- if (do_dialog(VIM_WARNING, (char_u *)_("Warning"), (char_u *)tbuf,
- (char_u *)_("&OK\n&Load File"), 1, NULL, true) == 2) {
- reload = true;
+ switch (do_dialog(VIM_WARNING, (char_u *)_("Warning"), (char_u *)tbuf,
+ (char_u *)_("&OK\n&Load File\nLoad File &and Options"),
+ 1, NULL, true)) {
+ case 2:
+ reload = RELOAD_NORMAL;
+ break;
+ case 3:
+ reload = RELOAD_DETECT;
+ break;
}
- } else if (State > NORMAL_BUSY || (State & CMDLINE) || already_warned) {
+ } else if (State > MODE_NORMAL_BUSY || (State & MODE_CMDLINE) || already_warned) {
if (*mesg2 != NUL) {
xstrlcat(tbuf, "; ", tbuf_len - 1);
xstrlcat(tbuf, mesg2, tbuf_len - 1);
@@ -5088,9 +5080,9 @@ int buf_check_timestamp(buf_T *buf)
xfree(tbuf);
}
- if (reload) {
+ if (reload != RELOAD_NONE) {
// Reload the buffer.
- buf_reload(buf, orig_mode);
+ buf_reload(buf, orig_mode, reload == RELOAD_DETECT);
if (buf->b_p_udf && buf->b_ffname != NULL) {
char_u hash[UNDO_HASH_SIZE];
@@ -5102,19 +5094,16 @@ int buf_check_timestamp(buf_T *buf)
// Trigger FileChangedShell when the file was changed in any way.
if (bufref_valid(&bufref) && retval != 0) {
- (void)apply_autocmds(EVENT_FILECHANGEDSHELLPOST, buf->b_fname, buf->b_fname,
- false, buf);
+ (void)apply_autocmds(EVENT_FILECHANGEDSHELLPOST, buf->b_fname, buf->b_fname, false, buf);
}
return retval;
}
-/*
- * Reload a buffer that is already loaded.
- * Used when the file was changed outside of Vim.
- * "orig_mode" is buf->b_orig_mode before the need for reloading was detected.
- * buf->b_orig_mode may have been reset already.
- */
-void buf_reload(buf_T *buf, int orig_mode)
+/// Reload a buffer that is already loaded.
+/// Used when the file was changed outside of Vim.
+/// "orig_mode" is buf->b_orig_mode before the need for reloading was detected.
+/// buf->b_orig_mode may have been reset already.
+void buf_reload(buf_T *buf, int orig_mode, bool reload_options)
{
exarg_T ea;
pos_T old_cursor;
@@ -5129,11 +5118,15 @@ void buf_reload(buf_T *buf, int orig_mode)
// set curwin/curbuf for "buf" and save some things
aucmd_prepbuf(&aco, buf);
- // We only want to read the text from the file, not reset the syntax
- // highlighting, clear marks, diff status, etc. Force the fileformat and
- // encoding to be the same.
+ // Unless reload_options is set, we only want to read the text from the
+ // file, not reset the syntax highlighting, clear marks, diff status, etc.
+ // Force the fileformat and encoding to be the same.
+ if (reload_options) {
+ memset(&ea, 0, sizeof(ea));
+ } else {
+ prep_exarg(&ea, buf);
+ }
- prep_exarg(&ea, buf);
old_cursor = curwin->w_cursor;
old_topline = curwin->w_topline;
@@ -5176,7 +5169,7 @@ void buf_reload(buf_T *buf, int orig_mode)
curbuf->b_flags |= BF_CHECK_RO; // check for RO again
keep_filetype = true; // don't detect 'filetype'
if (readfile(buf->b_ffname, buf->b_fname, (linenr_T)0, (linenr_T)0,
- (linenr_T)MAXLNUM, &ea, flags) != OK) {
+ (linenr_T)MAXLNUM, &ea, flags, false) != OK) {
if (!aborting()) {
semsg(_("E321: Could not reload \"%s\""), buf->b_fname);
}
@@ -5252,14 +5245,13 @@ void buf_store_file_info(buf_T *buf, FileInfo *file_info)
FUNC_ATTR_NONNULL_ALL
{
buf->b_mtime = file_info->stat.st_mtim.tv_sec;
+ buf->b_mtime_ns = file_info->stat.st_mtim.tv_nsec;
buf->b_orig_size = os_fileinfo_size(file_info);
buf->b_orig_mode = (int)file_info->stat.st_mode;
}
-/*
- * Adjust the line with missing eol, used for the next write.
- * Used for do_filter(), when the input lines for the filter are deleted.
- */
+/// Adjust the line with missing eol, used for the next write.
+/// Used for do_filter(), when the input lines for the filter are deleted.
void write_lnum_adjust(linenr_T offset)
{
if (curbuf->b_no_eol_lnum != 0) { // only if there is a missing eol
@@ -5286,79 +5278,164 @@ void forward_slash(char_u *fname)
}
#endif
-/// Name of Vim's own temp dir. Ends in a slash.
-static char_u *vim_tempdir = NULL;
+/// Path to Nvim's own temp dir. Ends in a slash.
+static char *vim_tempdir = NULL;
-/// Create a directory for private use by this instance of Neovim.
-/// This is done once, and the same directory is used for all temp files.
+/// Creates a directory for private use by this instance of Nvim, trying each of
+/// `TEMP_DIR_NAMES` until one succeeds.
+///
+/// Only done once, the same directory is used for all temp files.
/// This method avoids security problems because of symlink attacks et al.
/// It's also a bit faster, because we only need to check for an existing
/// file when creating the directory and not for each temp file.
-static void vim_maketempdir(void)
+static void vim_mktempdir(void)
{
- static const char *temp_dirs[] = TEMP_DIR_NAMES;
- // Try the entries in `TEMP_DIR_NAMES` to create the temp directory.
- char_u template[TEMP_FILE_PATH_MAXLEN];
- char_u path[TEMP_FILE_PATH_MAXLEN];
+ static const char *temp_dirs[] = TEMP_DIR_NAMES; // Try each of these until one succeeds.
+ char tmp[TEMP_FILE_PATH_MAXLEN];
+ char path[TEMP_FILE_PATH_MAXLEN];
+ char user[40] = { 0 };
+
+ (void)os_get_username(user, sizeof(user));
// Make sure the umask doesn't remove the executable bit.
// "repl" has been reported to use "0177".
mode_t umask_save = umask(0077);
for (size_t i = 0; i < ARRAY_SIZE(temp_dirs); i++) {
- // Expand environment variables, leave room for "/nvimXXXXXX/999999999"
- expand_env((char_u *)temp_dirs[i], template, TEMP_FILE_PATH_MAXLEN - 22);
- if (!os_isdir(template)) { // directory doesn't exist
+ // Expand environment variables, leave room for "/tmp/nvim.<user>/XXXXXX/999999999".
+ expand_env((char_u *)temp_dirs[i], (char_u *)tmp, TEMP_FILE_PATH_MAXLEN - 64);
+ if (!os_isdir((char_u *)tmp)) {
continue;
}
- add_pathsep((char *)template);
- // Concatenate with temporary directory name pattern
- STRCAT(template, "nvimXXXXXX");
+ // "/tmp/" exists, now try to create "/tmp/nvim.<user>/".
+ add_pathsep(tmp);
+ xstrlcat(tmp, "nvim.", sizeof(tmp));
+ xstrlcat(tmp, user, sizeof(tmp));
+ (void)os_mkdir(tmp, 0700); // Always create, to avoid a race.
+ bool owned = os_file_owned(tmp);
+ bool isdir = os_isdir((char_u *)tmp);
+#ifdef UNIX
+ int perm = os_getperm(tmp); // XDG_RUNTIME_DIR must be owned by the user, mode 0700.
+ bool valid = isdir && owned && 0700 == (perm & 0777);
+#else
+ bool valid = isdir && owned; // TODO(justinmk): Windows ACL?
+#endif
+ if (valid) {
+ add_pathsep(tmp);
+ } else {
+ if (!owned) {
+ ELOG("tempdir root not owned by current user (%s): %s", user, tmp);
+ } else if (!isdir) {
+ ELOG("tempdir root not a directory: %s", tmp);
+ }
+#ifdef UNIX
+ if (0700 != (perm & 0777)) {
+ ELOG("tempdir root has invalid permissions (%o): %s", perm, tmp);
+ }
+#endif
+ // If our "root" tempdir is invalid or fails, proceed without "<user>/".
+ // Else user1 could break user2 by creating "/tmp/nvim.user2/".
+ tmp[strlen(tmp) - strlen(user)] = '\0';
+ }
- if (os_mkdtemp((const char *)template, (char *)path) != 0) {
+ // Now try to create "/tmp/nvim.<user>/XXXXXX".
+ xstrlcat(tmp, "XXXXXX", sizeof(tmp)); // mkdtemp "template", will be replaced with random alphanumeric chars.
+ int r = os_mkdtemp(tmp, path);
+ if (r != 0) {
+ WLOG("tempdir create failed: %s: %s", os_strerror(r), tmp);
continue;
}
- if (vim_settempdir((char *)path)) {
+ if (vim_settempdir(path)) {
// Successfully created and set temporary directory so stop trying.
break;
} else {
// Couldn't set `vim_tempdir` to `path` so remove created directory.
- os_rmdir((char *)path);
+ os_rmdir(path);
}
}
(void)umask(umask_save);
}
+/// Core part of "readdir()" function.
+/// Retrieve the list of files/directories of "path" into "gap".
+///
+/// @return OK for success, FAIL for failure.
+int readdir_core(garray_T *gap, const char *path, void *context, CheckItem checkitem)
+ FUNC_ATTR_NONNULL_ARG(1, 2)
+{
+ ga_init(gap, (int)sizeof(char *), 20);
+
+ Directory dir;
+ if (!os_scandir(&dir, path)) {
+ smsg(_(e_notopen), path);
+ return FAIL;
+ }
+
+ for (;;) {
+ const char *p = os_scandir_next(&dir);
+ if (p == NULL) {
+ break;
+ }
+
+ bool ignore = (p[0] == '.' && (p[1] == NUL || (p[1] == '.' && p[2] == NUL)));
+ if (!ignore && checkitem != NULL) {
+ varnumber_T r = checkitem(context, p);
+ if (r < 0) {
+ break;
+ }
+ if (r == 0) {
+ ignore = true;
+ }
+ }
+
+ if (!ignore) {
+ ga_grow(gap, 1);
+ ((char **)gap->ga_data)[gap->ga_len++] = xstrdup(p);
+ }
+ }
+
+ os_closedir(&dir);
+
+ if (gap->ga_len > 0) {
+ sort_strings((char_u **)gap->ga_data, gap->ga_len);
+ }
+
+ return OK;
+}
+
/// Delete "name" and everything in it, recursively.
-/// @param name The path which should be deleted.
-/// @return 0 for success, -1 if some file was not deleted.
+///
+/// @param name The path which should be deleted.
+///
+/// @return 0 for success, -1 if some file was not deleted.
int delete_recursive(const char *name)
+ FUNC_ATTR_NONNULL_ALL
{
int result = 0;
if (os_isrealdir(name)) {
- snprintf((char *)NameBuff, MAXPATHL, "%s/*", name); // NOLINT
-
- char_u **files;
- int file_count;
- char_u *exp = vim_strsave(NameBuff);
- if (gen_expand_wildcards(1, &exp, &file_count, &files,
- EW_DIR | EW_FILE | EW_SILENT | EW_ALLLINKS
- | EW_DODOT | EW_EMPTYOK) == OK) {
- for (int i = 0; i < file_count; i++) {
- if (delete_recursive((const char *)files[i]) != 0) {
+ char *exp = xstrdup(name);
+ garray_T ga;
+ if (readdir_core(&ga, exp, NULL, NULL) == OK) {
+ for (int i = 0; i < ga.ga_len; i++) {
+ vim_snprintf((char *)NameBuff, MAXPATHL, "%s/%s", exp, ((char_u **)ga.ga_data)[i]);
+ if (delete_recursive((const char *)NameBuff) != 0) {
+ // Remember the failure but continue deleting any further
+ // entries.
result = -1;
}
}
- FreeWild(file_count, files);
+ ga_clear_strings(&ga);
+ if (os_rmdir(exp) != 0) {
+ result = -1;
+ }
} else {
result = -1;
}
-
xfree(exp);
- os_rmdir(name);
} else {
+ // Delete symlink only.
result = os_remove(name) == 0 ? 0 : -1;
}
@@ -5371,25 +5448,26 @@ void vim_deltempdir(void)
if (vim_tempdir != NULL) {
// remove the trailing path separator
path_tail(vim_tempdir)[-1] = NUL;
- delete_recursive((const char *)vim_tempdir);
+ delete_recursive(vim_tempdir);
XFREE_CLEAR(vim_tempdir);
}
}
-/// Get the name of temp directory. This directory would be created on the first
-/// call to this function.
-char_u *vim_gettempdir(void)
+/// Gets path to Nvim's own temp dir (ending with slash).
+///
+/// Creates the directory on the first call.
+char *vim_gettempdir(void)
{
if (vim_tempdir == NULL) {
- vim_maketempdir();
+ vim_mktempdir();
}
return vim_tempdir;
}
-/// Set Neovim own temporary directory name to `tempdir`. This directory should
-/// be already created. Expand this name to a full path and put it in
-/// `vim_tempdir`. This avoids that using `:cd` would confuse us.
+/// Sets Nvim's own temporary directory name to `tempdir`. This directory must
+/// already exist. Expands the name to a full path and put it in `vim_tempdir`.
+/// This avoids that using `:cd` would confuse us.
///
/// @param tempdir must be no longer than MAXPATHL.
///
@@ -5402,7 +5480,7 @@ static bool vim_settempdir(char *tempdir)
}
vim_FullName(tempdir, buf, MAXPATHL, false);
add_pathsep(buf);
- vim_tempdir = (char_u *)xstrdup(buf);
+ vim_tempdir = xstrdup(buf);
xfree(buf);
return true;
}
@@ -5411,14 +5489,14 @@ static bool vim_settempdir(char *tempdir)
///
/// @note The temp file is NOT created.
///
-/// @return pointer to the temp file name or NULL if Neovim can't create
-/// temporary directory for its own temporary files.
+/// @return pointer to the temp file name or NULL if Nvim can't create
+/// temporary directory for its own temporary files.
char_u *vim_tempname(void)
{
// Temp filename counter.
static uint64_t temp_count;
- char_u *tempdir = vim_gettempdir();
+ char *tempdir = vim_gettempdir();
if (!tempdir) {
return NULL;
}
@@ -5431,7 +5509,6 @@ char_u *vim_tempname(void)
return vim_strsave(template);
}
-
/// Tries matching a filename with a "pattern" ("prog" is NULL), or use the
/// precompiled regprog "prog" ("pattern" is NULL). That avoids calling
/// vim_regcomp() often.
@@ -5446,7 +5523,7 @@ char_u *vim_tempname(void)
/// @param allow_dirs Allow matching with dir
///
/// @return true if there is a match, false otherwise
-bool match_file_pat(char_u *pattern, regprog_T **prog, char_u *fname, char_u *sfname, char_u *tail,
+bool match_file_pat(char *pattern, regprog_T **prog, char *fname, char *sfname, char *tail,
int allow_dirs)
{
regmatch_T regmatch;
@@ -5503,17 +5580,18 @@ bool match_file_list(char_u *list, char_u *sfname, char_u *ffname)
bool match;
char_u *p;
- tail = path_tail(sfname);
+ tail = (char_u *)path_tail((char *)sfname);
// try all patterns in 'wildignore'
p = list;
while (*p) {
- copy_option_part(&p, buf, ARRAY_SIZE(buf), ",");
- regpat = file_pat_to_reg_pat(buf, NULL, &allow_dirs, false);
+ copy_option_part((char **)&p, (char *)buf, ARRAY_SIZE(buf), ",");
+ regpat = (char_u *)file_pat_to_reg_pat((char *)buf, NULL, &allow_dirs, false);
if (regpat == NULL) {
break;
}
- match = match_file_pat(regpat, NULL, ffname, sfname, tail, (int)allow_dirs);
+ match = match_file_pat((char *)regpat, NULL, (char *)ffname, (char *)sfname, (char *)tail,
+ (int)allow_dirs);
xfree(regpat);
if (match) {
return true;
@@ -5533,13 +5611,12 @@ bool match_file_list(char_u *list, char_u *sfname, char_u *ffname)
/// @param no_bslash Don't use a backward slash as pathsep
///
/// @return NULL on failure.
-char_u *file_pat_to_reg_pat(const char_u *pat, const char_u *pat_end, char *allow_dirs,
- int no_bslash)
+char *file_pat_to_reg_pat(const char *pat, const char *pat_end, char *allow_dirs, int no_bslash)
FUNC_ATTR_NONNULL_ARG(1)
{
- const char_u *endp;
- char_u *reg_pat;
- const char_u *p;
+ const char *endp;
+ char *reg_pat;
+ const char *p;
int nested = 0;
bool add_dollar = true;
@@ -5551,7 +5628,7 @@ char_u *file_pat_to_reg_pat(const char_u *pat, const char_u *pat_end, char *allo
}
if (pat_end == pat) {
- return (char_u *)xstrdup("^$");
+ return xstrdup("^$");
}
size_t size = 2; // '^' at start, '$' at end.
@@ -5723,10 +5800,9 @@ char_u *file_pat_to_reg_pat(const char_u *pat, const char_u *pat_end, char *allo
}
#if defined(EINTR)
-/*
- * Version of read() that retries when interrupted by EINTR (possibly
- * by a SIGWINCH).
- */
+
+/// Version of read() that retries when interrupted by EINTR (possibly
+/// by a SIGWINCH).
long read_eintr(int fd, void *buf, size_t bufsize)
{
long ret;
@@ -5740,19 +5816,16 @@ long read_eintr(int fd, void *buf, size_t bufsize)
return ret;
}
-/*
- * Version of write() that retries when interrupted by EINTR (possibly
- * by a SIGWINCH).
- */
+/// Version of write() that retries when interrupted by EINTR (possibly
+/// by a SIGWINCH).
long write_eintr(int fd, void *buf, size_t bufsize)
{
long ret = 0;
- long wlen;
// Repeat the write() so long it didn't fail, other than being interrupted
// by a signal.
while (ret < (long)bufsize) {
- wlen = write(fd, (char *)buf + ret, bufsize - ret);
+ long wlen = write(fd, (char *)buf + ret, bufsize - (size_t)ret);
if (wlen < 0) {
if (errno != EINTR) {
break;
diff --git a/src/nvim/fileio.h b/src/nvim/fileio.h
index 186d0b90ab..62d8b6142e 100644
--- a/src/nvim/fileio.h
+++ b/src/nvim/fileio.h
@@ -3,6 +3,8 @@
#include "nvim/autocmd.h"
#include "nvim/buffer_defs.h"
+#include "nvim/eval/typval.h"
+#include "nvim/garray.h"
#include "nvim/os/os.h"
// Values for readfile() flags
@@ -17,6 +19,8 @@
#define READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y))
+typedef varnumber_T (*CheckItem)(void *expr, const char *name);
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
// Events for autocommands
# include "fileio.h.generated.h"
diff --git a/src/nvim/fold.c b/src/nvim/fold.c
index 546345eeac..8f26e03a94 100644
--- a/src/nvim/fold.c
+++ b/src/nvim/fold.c
@@ -3,9 +3,7 @@
// vim: set fdm=marker fdl=1 fdc=3
-/*
- * fold.c: code for folding
- */
+// fold.c: code for folding
#include <inttypes.h>
#include <string.h>
@@ -41,12 +39,12 @@
// local declarations. {{{1
// typedef fold_T {{{2
-/*
- * The toplevel folds for each window are stored in the w_folds growarray.
- * Each toplevel fold can contain an array of second level folds in the
- * fd_nested growarray.
- * The info stored in both growarrays is the same: An array of fold_T.
- */
+
+// The toplevel folds for each window are stored in the w_folds growarray.
+// Each toplevel fold can contain an array of second level folds in the
+// fd_nested growarray.
+// The info stored in both growarrays is the same: An array of fold_T.
+
typedef struct {
linenr_T fd_top; // first line of fold; for nested fold
// relative to parent
@@ -93,20 +91,16 @@ typedef void (*LevelGetter)(fline_T *);
#endif
static char *e_nofold = N_("E490: No fold found");
-/*
- * While updating the folds lines between invalid_top and invalid_bot have an
- * undefined fold level. Only used for the window currently being updated.
- */
+// While updating the folds lines between invalid_top and invalid_bot have an
+// undefined fold level. Only used for the window currently being updated.
static linenr_T invalid_top = (linenr_T)0;
static linenr_T invalid_bot = (linenr_T)0;
-/*
- * When using 'foldexpr' we sometimes get the level of the next line, which
- * calls foldlevel() to get the level of the current line, which hasn't been
- * stored yet. To get around this chicken-egg problem the level of the
- * previous line is stored here when available. prev_lnum is zero when the
- * level is not available.
- */
+// When using 'foldexpr' we sometimes get the level of the next line, which
+// calls foldlevel() to get the level of the current line, which hasn't been
+// stored yet. To get around this chicken-egg problem the level of the
+// previous line is stored here when available. prev_lnum is zero when the
+// level is not available.
static linenr_T prev_lnum = 0;
static int prev_lnum_lvl = -1;
@@ -121,9 +115,7 @@ static size_t foldendmarkerlen;
// Exported folding functions. {{{1
// copyFoldingState() {{{2
-/*
- * Copy that folding state from window "wp_from" to window "wp_to".
- */
+/// Copy that folding state from window "wp_from" to window "wp_to".
void copyFoldingState(win_T *wp_from, win_T *wp_to)
{
wp_to->w_fold_manual = wp_from->w_fold_manual;
@@ -132,9 +124,7 @@ void copyFoldingState(win_T *wp_from, win_T *wp_to)
}
// hasAnyFolding() {{{2
-/*
- * Return TRUE if there may be folded lines in the current window.
- */
+/// @return true if there may be folded lines in the current window.
int hasAnyFolding(win_T *win)
{
// very simple now, but can become more complex later
@@ -164,16 +154,6 @@ bool hasFolding(linenr_T lnum, linenr_T *firstp, linenr_T *lastp)
bool hasFoldingWin(win_T *const win, const linenr_T lnum, linenr_T *const firstp,
linenr_T *const lastp, const bool cache, foldinfo_T *const infop)
{
- bool had_folded = false;
- linenr_T first = 0;
- linenr_T last = 0;
- linenr_T lnum_rel = lnum;
- fold_T *fp;
- int level = 0;
- bool use_level = false;
- bool maybe_small = false;
- int low_level = 0;
-
checkupdate(win);
// Return quickly when there is no folding at all in this window.
@@ -184,11 +164,13 @@ bool hasFoldingWin(win_T *const win, const linenr_T lnum, linenr_T *const firstp
return false;
}
+ bool had_folded = false;
+ linenr_T first = 0;
+ linenr_T last = 0;
+
if (cache) {
- /*
- * First look in cached info for displayed lines. This is probably
- * the fastest, but it can only be used if the entry is still valid.
- */
+ // First look in cached info for displayed lines. This is probably
+ // the fastest, but it can only be used if the entry is still valid.
const int x = find_wl_entry(win, lnum);
if (x >= 0) {
first = win->w_lines[x].wl_lnum;
@@ -197,10 +179,15 @@ bool hasFoldingWin(win_T *const win, const linenr_T lnum, linenr_T *const firstp
}
}
+ linenr_T lnum_rel = lnum;
+ int level = 0;
+ int low_level = 0;
+ fold_T *fp;
+ bool maybe_small = false;
+ bool use_level = false;
+
if (first == 0) {
- /*
- * Recursively search for a fold that contains "lnum".
- */
+ // Recursively search for a fold that contains "lnum".
garray_T *gap = &win->w_folds;
for (;;) {
if (!foldFind(gap, lnum_rel, &fp)) {
@@ -228,7 +215,7 @@ bool hasFoldingWin(win_T *const win, const linenr_T lnum, linenr_T *const firstp
// relative to containing fold.
gap = &fp->fd_nested;
lnum_rel -= fp->fd_top;
- ++level;
+ level++;
}
}
@@ -259,9 +246,7 @@ bool hasFoldingWin(win_T *const win, const linenr_T lnum, linenr_T *const firstp
}
// foldLevel() {{{2
-/*
- * Return fold level at line number "lnum" in the current window.
- */
+/// @return fold level at line number "lnum" in the current window.
int foldLevel(linenr_T lnum)
{
// While updating the folds lines between invalid_top and invalid_bot have
@@ -283,15 +268,16 @@ int foldLevel(linenr_T lnum)
}
// lineFolded() {{{2
-// Low level function to check if a line is folded. Doesn't use any caching.
-// Return true if line is folded.
-// Return false if line is not folded.
+/// Low level function to check if a line is folded. Doesn't use any caching.
+///
+/// @return true if line is folded or,
+/// false if line is not folded.
bool lineFolded(win_T *const win, const linenr_T lnum)
{
return fold_info(win, lnum).fi_lines != 0;
}
-/// fold_info() {{{2
+// fold_info() {{{2
///
/// Count the number of lines that are folded at line number "lnum".
/// Normally "lnum" is the first line of a possible fold, and the returned
@@ -316,61 +302,49 @@ foldinfo_T fold_info(win_T *win, linenr_T lnum)
}
// foldmethodIsManual() {{{2
-/*
- * Return TRUE if 'foldmethod' is "manual"
- */
+/// @return true if 'foldmethod' is "manual"
int foldmethodIsManual(win_T *wp)
{
return wp->w_p_fdm[3] == 'u';
}
// foldmethodIsIndent() {{{2
-/*
- * Return TRUE if 'foldmethod' is "indent"
- */
+/// @return true if 'foldmethod' is "indent"
int foldmethodIsIndent(win_T *wp)
{
return wp->w_p_fdm[0] == 'i';
}
// foldmethodIsExpr() {{{2
-/*
- * Return TRUE if 'foldmethod' is "expr"
- */
+/// @return true if 'foldmethod' is "expr"
int foldmethodIsExpr(win_T *wp)
{
return wp->w_p_fdm[1] == 'x';
}
// foldmethodIsMarker() {{{2
-/*
- * Return TRUE if 'foldmethod' is "marker"
- */
+/// @return true if 'foldmethod' is "marker"
int foldmethodIsMarker(win_T *wp)
{
return wp->w_p_fdm[2] == 'r';
}
// foldmethodIsSyntax() {{{2
-/*
- * Return TRUE if 'foldmethod' is "syntax"
- */
+/// @return true if 'foldmethod' is "syntax"
int foldmethodIsSyntax(win_T *wp)
{
return wp->w_p_fdm[0] == 's';
}
// foldmethodIsDiff() {{{2
-/*
- * Return TRUE if 'foldmethod' is "diff"
- */
+/// @return true if 'foldmethod' is "diff"
int foldmethodIsDiff(win_T *wp)
{
return wp->w_p_fdm[0] == 'd';
}
// closeFold() {{{2
-/// Close fold for current window at line "lnum".
+/// Close fold for current window at position "pos".
/// Repeat "count" times.
void closeFold(pos_T pos, long count)
{
@@ -378,9 +352,7 @@ void closeFold(pos_T pos, long count)
}
// closeFoldRecurse() {{{2
-/*
- * Close fold for current window at line "lnum" recursively.
- */
+/// Close fold for current window at position `pos` recursively.
void closeFoldRecurse(pos_T pos)
{
(void)setManualFold(pos, false, true, NULL);
@@ -391,18 +363,17 @@ void closeFoldRecurse(pos_T pos)
/// Open or Close folds for current window in lines "first" to "last".
/// Used for "zo", "zO", "zc" and "zC" in Visual mode.
///
-/// @param opening TRUE to open, FALSE to close
-/// @param recurse TRUE to do it recursively
-/// @param had_visual TRUE when Visual selection used
+/// @param opening true to open, false to close
+/// @param recurse true to do it recursively
+/// @param had_visual true when Visual selection used
void opFoldRange(pos_T firstpos, pos_T lastpos, int opening, int recurse, int had_visual)
{
int done = DONE_NOTHING; // avoid error messages
linenr_T first = firstpos.lnum;
linenr_T last = lastpos.lnum;
- linenr_T lnum;
linenr_T lnum_next;
- for (lnum = first; lnum <= last; lnum = lnum_next + 1) {
+ for (linenr_T lnum = first; lnum <= last; lnum = lnum_next + 1) {
pos_T temp = { lnum, 0, 0 };
lnum_next = lnum;
// Opening one level only: next fold to open is after the one going to
@@ -427,36 +398,28 @@ void opFoldRange(pos_T firstpos, pos_T lastpos, int opening, int recurse, int ha
}
// openFold() {{{2
-/*
- * Open fold for current window at line "lnum".
- * Repeat "count" times.
- */
+/// Open fold for current window at position "pos".
+/// Repeat "count" times.
void openFold(pos_T pos, long count)
{
setFoldRepeat(pos, count, true);
}
// openFoldRecurse() {{{2
-/*
- * Open fold for current window at line "lnum" recursively.
- */
+/// Open fold for current window at position `pos` recursively.
void openFoldRecurse(pos_T pos)
{
(void)setManualFold(pos, true, true, NULL);
}
// foldOpenCursor() {{{2
-/*
- * Open folds until the cursor line is not in a closed fold.
- */
+/// Open folds until the cursor line is not in a closed fold.
void foldOpenCursor(void)
{
- int done;
-
checkupdate(curwin);
if (hasAnyFolding(curwin)) {
for (;;) {
- done = DONE_NOTHING;
+ int done = DONE_NOTHING;
(void)setManualFold(curwin->w_cursor, true, false, &done);
if (!(done & DONE_ACTION)) {
break;
@@ -466,17 +429,13 @@ void foldOpenCursor(void)
}
// newFoldLevel() {{{2
-/*
- * Set new foldlevel for current window.
- */
+/// Set new foldlevel for current window.
void newFoldLevel(void)
{
newFoldLevelWin(curwin);
if (foldmethodIsDiff(curwin) && curwin->w_p_scb) {
- /*
- * Set the same foldlevel in other windows in diff mode.
- */
+ // Set the same foldlevel in other windows in diff mode.
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp != curwin && foldmethodIsDiff(wp) && wp->w_p_scb) {
wp->w_p_fdl = curwin->w_p_fdl;
@@ -488,15 +447,13 @@ void newFoldLevel(void)
static void newFoldLevelWin(win_T *wp)
{
- fold_T *fp;
-
checkupdate(wp);
if (wp->w_fold_manual) {
// Set all flags for the first level of folds to FD_LEVEL. Following
// manual open/close will then change the flags to FD_OPEN or
// FD_CLOSED for those folds that don't use 'foldlevel'.
- fp = (fold_T *)wp->w_folds.ga_data;
- for (int i = 0; i < wp->w_folds.ga_len; ++i) {
+ fold_T *fp = (fold_T *)wp->w_folds.ga_data;
+ for (int i = 0; i < wp->w_folds.ga_len; i++) {
fp[i].fd_flags = FD_LEVEL;
}
wp->w_fold_manual = false;
@@ -505,9 +462,7 @@ static void newFoldLevelWin(win_T *wp)
}
// foldCheckClose() {{{2
-/*
- * Apply 'foldlevel' to all folds that don't contain the cursor.
- */
+/// Apply 'foldlevel' to all folds that don't contain the cursor.
void foldCheckClose(void)
{
if (*p_fcl != NUL) { // can only be "all" right now
@@ -520,19 +475,18 @@ void foldCheckClose(void)
}
// checkCloseRec() {{{2
-static int checkCloseRec(garray_T *gap, linenr_T lnum, int level)
+static bool checkCloseRec(garray_T *gap, linenr_T lnum, int level)
{
- fold_T *fp;
- int retval = FALSE;
+ bool retval = false;
- fp = (fold_T *)gap->ga_data;
- for (int i = 0; i < gap->ga_len; ++i) {
+ fold_T *fp = (fold_T *)gap->ga_data;
+ for (int i = 0; i < gap->ga_len; i++) {
// Only manually opened folds may need to be closed.
if (fp[i].fd_flags == FD_OPEN) {
if (level <= 0 && (lnum < fp[i].fd_top
|| lnum >= fp[i].fd_top + fp[i].fd_len)) {
fp[i].fd_flags = FD_LEVEL;
- retval = TRUE;
+ retval = true;
} else {
retval |= checkCloseRec(&fp[i].fd_nested, lnum - fp[i].fd_top,
level - 1);
@@ -543,19 +497,19 @@ static int checkCloseRec(garray_T *gap, linenr_T lnum, int level)
}
// foldCreateAllowed() {{{2
-/// Return TRUE if it's allowed to manually create or delete a fold.
-/// Give an error message and return FALSE if not.
+/// @return true if it's allowed to manually create or delete a fold or,
+/// give an error message and return false if not.
int foldManualAllowed(bool create)
{
if (foldmethodIsManual(curwin) || foldmethodIsMarker(curwin)) {
- return TRUE;
+ return true;
}
if (create) {
emsg(_("E350: Cannot create fold with current 'foldmethod'"));
} else {
emsg(_("E351: Cannot delete fold with current 'foldmethod'"));
}
- return FALSE;
+ return false;
}
// foldCreate() {{{2
@@ -563,13 +517,8 @@ int foldManualAllowed(bool create)
/// window.
void foldCreate(win_T *wp, pos_T start, pos_T end)
{
- fold_T *fp;
- garray_T *gap;
- garray_T fold_ga;
- int i;
- int cont;
- int use_level = FALSE;
- int closed = FALSE;
+ int use_level = false;
+ int closed = false;
int level = 0;
pos_T start_rel = start;
pos_T end_rel = end;
@@ -590,11 +539,14 @@ void foldCreate(win_T *wp, pos_T start, pos_T end)
checkupdate(wp);
+ int i;
+
// Find the place to insert the new fold
- gap = &wp->w_folds;
+ garray_T *gap = &wp->w_folds;
if (gap->ga_len == 0) {
i = 0;
} else {
+ fold_T *fp;
for (;;) {
if (!foldFind(gap, start_rel.lnum, &fp)) {
break;
@@ -628,10 +580,12 @@ void foldCreate(win_T *wp, pos_T start, pos_T end)
ga_grow(gap, 1);
{
- fp = (fold_T *)gap->ga_data + i;
+ fold_T *fp = (fold_T *)gap->ga_data + i;
+ garray_T fold_ga;
ga_init(&fold_ga, (int)sizeof(fold_T), 10);
// Count number of folds that will be contained in the new fold.
+ int cont;
for (cont = 0; i + cont < gap->ga_len; cont++) {
if (fp[cont].fd_top > end_rel.lnum) {
break;
@@ -689,7 +643,6 @@ void foldCreate(win_T *wp, pos_T start, pos_T end)
}
}
-
// deleteFold() {{{2
/// @param start delete all folds from start to end when not 0
/// @param end delete all folds from start to end when not 0
@@ -698,7 +651,6 @@ void foldCreate(win_T *wp, pos_T start, pos_T end)
void deleteFold(win_T *const wp, const linenr_T start, const linenr_T end, const int recursive,
const bool had_visual)
{
- fold_T *fp;
fold_T *found_fp = NULL;
linenr_T found_off = 0;
bool maybe_small = false;
@@ -717,6 +669,7 @@ void deleteFold(win_T *const wp, const linenr_T start, const linenr_T end, const
linenr_T lnum_off = 0;
bool use_level = false;
for (;;) {
+ fold_T *fp;
if (!foldFind(gap, lnum - lnum_off, &fp)) {
break;
}
@@ -734,10 +687,10 @@ void deleteFold(win_T *const wp, const linenr_T start, const linenr_T end, const
// check nested folds
gap = &fp->fd_nested;
lnum_off += fp->fd_top;
- ++level;
+ level++;
}
if (found_ga == NULL) {
- ++lnum;
+ lnum++;
} else {
lnum = found_fp->fd_top + found_fp->fd_len + found_off;
@@ -784,15 +737,12 @@ void deleteFold(win_T *const wp, const linenr_T start, const linenr_T end, const
// the modification of the *first* line of the fold, but we send through a
// notification that includes every line that was part of the fold
int64_t num_changed = last_lnum - first_lnum;
- buf_updates_send_changes(wp->w_buffer, first_lnum, num_changed,
- num_changed, true);
+ buf_updates_send_changes(wp->w_buffer, first_lnum, num_changed, num_changed);
}
}
// clearFolding() {{{2
-/*
- * Remove all folding for window "win".
- */
+/// Remove all folding for window "win".
void clearFolding(win_T *win)
{
deleteFoldRecurse(win->w_buffer, &win->w_folds);
@@ -800,15 +750,13 @@ void clearFolding(win_T *win)
}
// foldUpdate() {{{2
-/*
- * Update folds for changes in the buffer of a window.
- * Note that inserted/deleted lines must have already been taken care of by
- * calling foldMarkAdjust().
- * The changes in lines from top to bot (inclusive).
- */
+/// Update folds for changes in the buffer of a window.
+/// Note that inserted/deleted lines must have already been taken care of by
+/// calling foldMarkAdjust().
+/// The changes in lines from top to bot (inclusive).
void foldUpdate(win_T *wp, linenr_T top, linenr_T bot)
{
- if (compl_busy || State & INSERT) {
+ if (disable_fold_update || compl_busy || State & MODE_INSERT) {
return;
}
@@ -818,11 +766,18 @@ void foldUpdate(win_T *wp, linenr_T top, linenr_T bot)
}
if (wp->w_folds.ga_len > 0) {
- // Mark all folds from top to bot as maybe-small.
+ linenr_T maybe_small_start = top;
+ linenr_T maybe_small_end = bot;
+
+ // Mark all folds from top to bot (or bot to top) as maybe-small.
+ if (top > bot) {
+ maybe_small_start = bot;
+ maybe_small_end = top;
+ }
fold_T *fp;
- (void)foldFind(&wp->w_folds, top, &fp);
+ (void)foldFind(&wp->w_folds, maybe_small_start, &fp);
while (fp < (fold_T *)wp->w_folds.ga_data + wp->w_folds.ga_len
- && fp->fd_top < bot) {
+ && fp->fd_top <= maybe_small_end) {
fp->fd_small = kNone;
fp++;
}
@@ -836,7 +791,7 @@ void foldUpdate(win_T *wp, linenr_T top, linenr_T bot)
int save_got_int = got_int;
// reset got_int here, otherwise it won't work
- got_int = FALSE;
+ got_int = false;
foldUpdateIEMS(wp, top, bot);
got_int |= save_got_int;
}
@@ -856,12 +811,10 @@ void foldUpdateAfterInsert(void)
}
// foldUpdateAll() {{{2
-/*
- * Update all lines in a window for folding.
- * Used when a fold setting changes or after reloading the buffer.
- * The actual updating is postponed until fold info is used, to avoid doing
- * every time a setting is changed or a syntax item is added.
- */
+/// Update all lines in a window for folding.
+/// Used when a fold setting changes or after reloading the buffer.
+/// The actual updating is postponed until fold info is used, to avoid doing
+/// every time a setting is changed or a syntax item is added.
void foldUpdateAll(win_T *win)
{
win->w_foldinvalid = true;
@@ -974,7 +927,7 @@ int foldMoveTo(const bool updown, const int dir, const long count)
// Check nested folds (if any).
gap = &fp->fd_nested;
lnum_off += fp->fd_top;
- ++level;
+ level++;
}
if (lnum_found != curwin->w_cursor.lnum) {
if (retval == FAIL) {
@@ -992,26 +945,21 @@ int foldMoveTo(const bool updown, const int dir, const long count)
}
// foldInitWin() {{{2
-/*
- * Init the fold info in a new window.
- */
+/// Init the fold info in a new window.
void foldInitWin(win_T *new_win)
{
ga_init(&new_win->w_folds, (int)sizeof(fold_T), 10);
}
// find_wl_entry() {{{2
-/*
- * Find an entry in the win->w_lines[] array for buffer line "lnum".
- * Only valid entries are considered (for entries where wl_valid is FALSE the
- * line number can be wrong).
- * Returns index of entry or -1 if not found.
- */
+/// Find an entry in the win->w_lines[] array for buffer line "lnum".
+/// Only valid entries are considered (for entries where wl_valid is false the
+/// line number can be wrong).
+///
+/// @return index of entry or -1 if not found.
int find_wl_entry(win_T *win, linenr_T lnum)
{
- int i;
-
- for (i = 0; i < win->w_lines_valid; ++i) {
+ for (int i = 0; i < win->w_lines_valid; i++) {
if (win->w_lines[i].wl_valid) {
if (lnum < win->w_lines[i].wl_lnum) {
return -1;
@@ -1025,18 +973,16 @@ int find_wl_entry(win_T *win, linenr_T lnum)
}
// foldAdjustVisual() {{{2
-/*
- * Adjust the Visual area to include any fold at the start or end completely.
- */
+/// Adjust the Visual area to include any fold at the start or end completely.
void foldAdjustVisual(void)
{
- pos_T *start, *end;
- char_u *ptr;
-
if (!VIsual_active || !hasAnyFolding(curwin)) {
return;
}
+ pos_T *start, *end;
+ char_u *ptr;
+
if (ltoreq(VIsual, curwin->w_cursor)) {
start = &VIsual;
end = &curwin->w_cursor;
@@ -1059,9 +1005,7 @@ void foldAdjustVisual(void)
}
// cursor_foldstart() {{{2
-/*
- * Move the cursor to the first line of a closed fold.
- */
+/// Move the cursor to the first line of a closed fold.
void foldAdjustCursor(void)
{
(void)hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL);
@@ -1069,14 +1013,9 @@ void foldAdjustCursor(void)
// Internal functions for "fold_T" {{{1
// cloneFoldGrowArray() {{{2
-/*
- * Will "clone" (i.e deep copy) a garray_T of folds.
- */
+/// Will "clone" (i.e deep copy) a garray_T of folds.
void cloneFoldGrowArray(garray_T *from, garray_T *to)
{
- fold_T *from_p;
- fold_T *to_p;
-
ga_init(to, from->ga_itemsize, from->ga_growsize);
if (GA_EMPTY(from)) {
@@ -1085,8 +1024,8 @@ void cloneFoldGrowArray(garray_T *from, garray_T *to)
ga_grow(to, from->ga_len);
- from_p = (fold_T *)from->ga_data;
- to_p = (fold_T *)to->ga_data;
+ fold_T *from_p = (fold_T *)from->ga_data;
+ fold_T *to_p = (fold_T *)to->ga_data;
for (int i = 0; i < from->ga_len; i++) {
to_p->fd_top = from_p->fd_top;
@@ -1094,36 +1033,31 @@ void cloneFoldGrowArray(garray_T *from, garray_T *to)
to_p->fd_flags = from_p->fd_flags;
to_p->fd_small = from_p->fd_small;
cloneFoldGrowArray(&from_p->fd_nested, &to_p->fd_nested);
- ++to->ga_len;
- ++from_p;
- ++to_p;
+ to->ga_len++;
+ from_p++;
+ to_p++;
}
}
// foldFind() {{{2
/// Search for line "lnum" in folds of growarray "gap".
-/// Set *fpp to the fold struct for the fold that contains "lnum" or
+/// Set "*fpp" to the fold struct for the fold that contains "lnum" or
/// the first fold below it (careful: it can be beyond the end of the array!).
///
/// @return false when there is no fold that contains "lnum".
static bool foldFind(const garray_T *gap, linenr_T lnum, fold_T **fpp)
{
- linenr_T low, high;
- fold_T *fp;
-
if (gap->ga_len == 0) {
*fpp = NULL;
return false;
}
- /*
- * Perform a binary search.
- * "low" is lowest index of possible match.
- * "high" is highest index of possible match.
- */
- fp = (fold_T *)gap->ga_data;
- low = 0;
- high = gap->ga_len - 1;
+ // Perform a binary search.
+ // "low" is lowest index of possible match.
+ // "high" is highest index of possible match.
+ fold_T *fp = (fold_T *)gap->ga_data;
+ linenr_T low = 0;
+ linenr_T high = gap->ga_len - 1;
while (low <= high) {
linenr_T i = (low + high) / 2;
if (fp[i].fd_top > lnum) {
@@ -1143,18 +1077,15 @@ static bool foldFind(const garray_T *gap, linenr_T lnum, fold_T **fpp)
}
// foldLevelWin() {{{2
-/*
- * Return fold level at line number "lnum" in window "wp".
- */
+/// @return fold level at line number "lnum" in window "wp".
static int foldLevelWin(win_T *wp, linenr_T lnum)
{
fold_T *fp;
linenr_T lnum_rel = lnum;
int level = 0;
- garray_T *gap;
// Recursively search for a fold that contains "lnum".
- gap = &wp->w_folds;
+ garray_T *gap = &wp->w_folds;
for (;;) {
if (!foldFind(gap, lnum_rel, &fp)) {
break;
@@ -1162,16 +1093,14 @@ static int foldLevelWin(win_T *wp, linenr_T lnum)
// Check nested folds. Line number is relative to containing fold.
gap = &fp->fd_nested;
lnum_rel -= fp->fd_top;
- ++level;
+ level++;
}
return level;
}
// checkupdate() {{{2
-/*
- * Check if the folds in window "wp" are invalid and update them if needed.
- */
+/// Check if the folds in window "wp" are invalid and update them if needed.
static void checkupdate(win_T *wp)
{
if (wp->w_foldinvalid) {
@@ -1181,17 +1110,12 @@ static void checkupdate(win_T *wp)
}
// setFoldRepeat() {{{2
-/*
- * Open or close fold for current window at line "lnum".
- * Repeat "count" times.
- */
+/// Open or close fold for current window at position `pos`.
+/// Repeat "count" times.
static void setFoldRepeat(pos_T pos, long count, int do_open)
{
- int done;
- long n;
-
- for (n = 0; n < count; ++n) {
- done = DONE_NOTHING;
+ for (int n = 0; n < count; n++) {
+ int done = DONE_NOTHING;
(void)setManualFold(pos, do_open, false, &done);
if (!(done & DONE_ACTION)) {
// Only give an error message when no fold could be opened.
@@ -1204,22 +1128,18 @@ static void setFoldRepeat(pos_T pos, long count, int do_open)
}
// setManualFold() {{{2
-///
/// Open or close the fold in the current window which contains "lnum".
/// Also does this for other windows in diff mode when needed.
///
-/// @param opening TRUE when opening, FALSE when closing
-/// @param recurse TRUE when closing/opening recursive
+/// @param opening true when opening, false when closing
+/// @param recurse true when closing/opening recursive
static linenr_T setManualFold(pos_T pos, int opening, int recurse, int *donep)
{
- linenr_T lnum = pos.lnum;
if (foldmethodIsDiff(curwin) && curwin->w_p_scb) {
linenr_T dlnum;
- /*
- * Do the same operation in other windows in diff mode. Calculate the
- * line number from the diffs.
- */
+ // Do the same operation in other windows in diff mode. Calculate the
+ // line number from the diffs.
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp != curwin && foldmethodIsDiff(wp) && wp->w_p_scb) {
dlnum = diff_lnum_win(curwin->w_cursor.lnum, wp);
@@ -1230,7 +1150,7 @@ static linenr_T setManualFold(pos_T pos, int opening, int recurse, int *donep)
}
}
- return setManualFoldWin(curwin, lnum, opening, recurse, donep);
+ return setManualFoldWin(curwin, pos.lnum, opening, recurse, donep);
}
// setManualFoldWin() {{{2
@@ -1240,31 +1160,27 @@ static linenr_T setManualFold(pos_T pos, int opening, int recurse, int *donep)
/// When "donep" is NULL give an error message when no fold was found for
/// "lnum", but only if "wp" is "curwin".
///
-/// @param opening TRUE when opening, FALSE when closing
-/// @param recurse TRUE when closing/opening recursive
+/// @param opening true when opening, false when closing
+/// @param recurse true when closing/opening recursive
///
/// @return the line number of the next line that could be closed.
-/// It's only valid when "opening" is TRUE!
+/// It's only valid when "opening" is true!
static linenr_T setManualFoldWin(win_T *wp, linenr_T lnum, int opening, int recurse, int *donep)
{
fold_T *fp;
fold_T *fp2;
fold_T *found = NULL;
- int j;
int level = 0;
- int use_level = FALSE;
- int found_fold = FALSE;
- garray_T *gap;
+ bool use_level = false;
+ bool found_fold = false;
linenr_T next = MAXLNUM;
linenr_T off = 0;
int done = 0;
checkupdate(wp);
- /*
- * Find the fold, open or close it.
- */
- gap = &wp->w_folds;
+ // Find the fold, open or close it.
+ garray_T *gap = &wp->w_folds;
for (;;) {
if (!foldFind(gap, lnum, &fp)) {
// If there is a following fold, continue there next time.
@@ -1275,7 +1191,7 @@ static linenr_T setManualFoldWin(win_T *wp, linenr_T lnum, int opening, int recu
}
// lnum is inside this fold
- found_fold = TRUE;
+ found_fold = true;
// If there is a following fold, continue there next time.
if (fp + 1 < (fold_T *)gap->ga_data + gap->ga_len) {
@@ -1284,14 +1200,14 @@ static linenr_T setManualFoldWin(win_T *wp, linenr_T lnum, int opening, int recu
// Change from level-dependent folding to manual.
if (use_level || fp->fd_flags == FD_LEVEL) {
- use_level = TRUE;
+ use_level = true;
if (level >= wp->w_p_fdl) {
fp->fd_flags = FD_CLOSED;
} else {
fp->fd_flags = FD_OPEN;
}
fp2 = (fold_T *)fp->fd_nested.ga_data;
- for (j = 0; j < fp->fd_nested.ga_len; ++j) {
+ for (int j = 0; j < fp->fd_nested.ga_len; j++) {
fp2[j].fd_flags = FD_LEVEL;
}
}
@@ -1319,7 +1235,7 @@ static linenr_T setManualFoldWin(win_T *wp, linenr_T lnum, int opening, int recu
gap = &fp->fd_nested;
lnum -= fp->fd_top;
off += fp->fd_top;
- ++level;
+ level++;
}
if (found_fold) {
// When closing and not recurse, close deepest open fold.
@@ -1344,24 +1260,21 @@ static linenr_T setManualFoldWin(win_T *wp, linenr_T lnum, int opening, int recu
}
// foldOpenNested() {{{2
-/*
- * Open all nested folds in fold "fpr" recursively.
- */
+/// Open all nested folds in fold "fpr" recursively.
static void foldOpenNested(fold_T *fpr)
{
- fold_T *fp;
-
- fp = (fold_T *)fpr->fd_nested.ga_data;
- for (int i = 0; i < fpr->fd_nested.ga_len; ++i) {
+ fold_T *fp = (fold_T *)fpr->fd_nested.ga_data;
+ for (int i = 0; i < fpr->fd_nested.ga_len; i++) {
foldOpenNested(&fp[i]);
fp[i].fd_flags = FD_OPEN;
}
}
// deleteFoldEntry() {{{2
-// Delete fold "idx" from growarray "gap".
-// When "recursive" is true also delete all the folds contained in it.
-// When "recursive" is false contained folds are moved one level up.
+/// Delete fold "idx" from growarray "gap".
+///
+/// @param recursive when true, also delete all the folds contained in it.
+/// when false, contained folds are moved one level up.
static void deleteFoldEntry(win_T *const wp, garray_T *const gap, const int idx,
const bool recursive)
{
@@ -1408,9 +1321,7 @@ static void deleteFoldEntry(win_T *const wp, garray_T *const gap, const int idx,
}
// deleteFoldRecurse() {{{2
-/*
- * Delete nested folds in a fold.
- */
+/// Delete nested folds in a fold.
void deleteFoldRecurse(buf_T *bp, garray_T *gap)
{
#define DELETE_FOLD_NESTED(fd) deleteFoldRecurse(bp, &((fd)->fd_nested))
@@ -1418,10 +1329,9 @@ void deleteFoldRecurse(buf_T *bp, garray_T *gap)
}
// foldMarkAdjust() {{{2
-/*
- * Update line numbers of folds for inserted/deleted lines.
- */
-void foldMarkAdjust(win_T *wp, linenr_T line1, linenr_T line2, long amount, long amount_after)
+/// Update line numbers of folds for inserted/deleted lines.
+void foldMarkAdjust(win_T *wp, linenr_T line1, linenr_T line2, linenr_T amount,
+ linenr_T amount_after)
{
// If deleting marks from line1 to line2, but not deleting all those
// lines, set line2 so that only deleted lines have their folds removed.
@@ -1430,7 +1340,7 @@ void foldMarkAdjust(win_T *wp, linenr_T line1, linenr_T line2, long amount, long
}
// If appending a line in Insert mode, it should be included in the fold
// just above the line.
- if ((State & INSERT) && amount == (linenr_T)1 && line2 == MAXLNUM) {
+ if ((State & MODE_INSERT) && amount == (linenr_T)1 && line2 == MAXLNUM) {
line1--;
}
foldMarkAdjustRecurse(wp, &wp->w_folds, line1, line2, amount, amount_after);
@@ -1438,44 +1348,39 @@ void foldMarkAdjust(win_T *wp, linenr_T line1, linenr_T line2, long amount, long
// foldMarkAdjustRecurse() {{{2
static void foldMarkAdjustRecurse(win_T *wp, garray_T *gap, linenr_T line1, linenr_T line2,
- long amount, long amount_after)
+ linenr_T amount, linenr_T amount_after)
{
- fold_T *fp;
- linenr_T last;
- linenr_T top;
-
if (gap->ga_len == 0) {
return;
}
+ linenr_T top;
+
// In Insert mode an inserted line at the top of a fold is considered part
// of the fold, otherwise it isn't.
- if ((State & INSERT) && amount == (linenr_T)1 && line2 == MAXLNUM) {
+ if ((State & MODE_INSERT) && amount == (linenr_T)1 && line2 == MAXLNUM) {
top = line1 + 1;
} else {
top = line1;
}
// Find the fold containing or just below "line1".
+ fold_T *fp;
(void)foldFind(gap, line1, &fp);
- /*
- * Adjust all folds below "line1" that are affected.
- */
- for (int i = (int)(fp - (fold_T *)gap->ga_data); i < gap->ga_len; ++i, ++fp) {
- /*
- * Check for these situations:
- * 1 2 3
- * 1 2 3
- * line1 2 3 4 5
- * 2 3 4 5
- * 2 3 4 5
- * line2 2 3 4 5
- * 3 5 6
- * 3 5 6
- */
-
- last = fp->fd_top + fp->fd_len - 1; // last line of fold
+ // Adjust all folds below "line1" that are affected.
+ for (int i = (int)(fp - (fold_T *)gap->ga_data); i < gap->ga_len; i++, fp++) {
+ // Check for these situations:
+ // 1 2 3
+ // 1 2 3
+ // line1 2 3 4 5
+ // 2 3 4 5
+ // 2 3 4 5
+ // line2 2 3 4 5
+ // 3 5 6
+ // 3 5 6
+
+ linenr_T last = fp->fd_top + fp->fd_len - 1; // last line of fold
// 1. fold completely above line1: nothing to do
if (last < line1) {
@@ -1538,10 +1443,8 @@ static void foldMarkAdjustRecurse(win_T *wp, garray_T *gap, linenr_T line1, line
}
// getDeepestNesting() {{{2
-/*
- * Get the lowest 'foldlevel' value that makes the deepest nested fold in the
- * current window open.
- */
+/// Get the lowest 'foldlevel' value that makes the deepest nested fold in
+/// window `wp`.
int getDeepestNesting(win_T *wp)
{
checkupdate(wp);
@@ -1550,13 +1453,11 @@ int getDeepestNesting(win_T *wp)
static int getDeepestNestingRecurse(garray_T *gap)
{
- int level;
int maxlevel = 0;
- fold_T *fp;
- fp = (fold_T *)gap->ga_data;
- for (int i = 0; i < gap->ga_len; ++i) {
- level = getDeepestNestingRecurse(&fp[i].fd_nested) + 1;
+ fold_T *fp = (fold_T *)gap->ga_data;
+ for (int i = 0; i < gap->ga_len; i++) {
+ int level = getDeepestNestingRecurse(&fp[i].fd_nested) + 1;
if (level > maxlevel) {
maxlevel = level;
}
@@ -1633,7 +1534,7 @@ static void checkSmall(win_T *const wp, fold_T *const fp, const linenr_T lnum_of
}
// setSmallMaybe() {{{2
-// Set small flags in "gap" to kNone.
+/// Set small flags in "gap" to kNone.
static void setSmallMaybe(garray_T *gap)
{
fold_T *fp = (fold_T *)gap->ga_data;
@@ -1643,10 +1544,8 @@ static void setSmallMaybe(garray_T *gap)
}
// foldCreateMarkers() {{{2
-/*
- * Create a fold from line "start" to line "end" (inclusive) in the current
- * window by adding markers.
- */
+/// Create a fold from line "start" to line "end" (inclusive) in window `wp`
+/// by adding markers.
static void foldCreateMarkers(win_T *wp, pos_T start, pos_T end)
{
buf_T *buf = wp->w_buffer;
@@ -1668,24 +1567,21 @@ static void foldCreateMarkers(win_T *wp, pos_T start, pos_T end)
// u_save() is unable to save the buffer line, but we send the
// nvim_buf_lines_event anyway since it won't do any harm.
int64_t num_changed = 1 + end.lnum - start.lnum;
- buf_updates_send_changes(buf, start.lnum, num_changed, num_changed, true);
+ buf_updates_send_changes(buf, start.lnum, num_changed, num_changed);
}
// foldAddMarker() {{{2
-/*
- * Add "marker[markerlen]" in 'commentstring' to line "lnum".
- */
+/// Add "marker[markerlen]" in 'commentstring' to position `pos`.
static void foldAddMarker(buf_T *buf, pos_T pos, const char_u *marker, size_t markerlen)
{
char_u *cms = buf->b_p_cms;
- char_u *line;
char_u *newline;
char_u *p = (char_u *)strstr((char *)buf->b_p_cms, "%s");
bool line_is_comment = false;
linenr_T lnum = pos.lnum;
// Allocate a new line: old-line + 'cms'-start + marker + 'cms'-end
- line = ml_get_buf(buf, lnum, false);
+ char_u *line = ml_get_buf(buf, lnum, false);
size_t line_len = STRLEN(line);
size_t added = 0;
@@ -1702,11 +1598,11 @@ static void foldAddMarker(buf_T *buf, pos_T pos, const char_u *marker, size_t ma
STRCPY(newline + line_len, cms);
memcpy(newline + line_len + (p - cms), marker, markerlen);
STRCPY(newline + line_len + (p - cms) + markerlen, p + 2);
- added = markerlen + STRLEN(cms)-2;
+ added = markerlen + STRLEN(cms) - 2;
}
ml_replace_buf(buf, lnum, newline, false);
if (added) {
- extmark_splice_cols(buf, (int)lnum-1, (int)line_len,
+ extmark_splice_cols(buf, (int)lnum - 1, (int)line_len,
0, (int)added, kExtmarkUndo);
}
}
@@ -1724,28 +1620,25 @@ static void deleteFoldMarkers(win_T *wp, fold_T *fp, int recursive, linenr_T lnu
lnum_off + fp->fd_top);
}
}
- foldDelMarker(wp->w_buffer, fp->fd_top+lnum_off, wp->w_p_fmr,
+ foldDelMarker(wp->w_buffer, fp->fd_top + lnum_off, wp->w_p_fmr,
foldstartmarkerlen);
foldDelMarker(wp->w_buffer, fp->fd_top + lnum_off + fp->fd_len - 1,
foldendmarker, foldendmarkerlen);
}
// foldDelMarker() {{{2
-//
-// Delete marker "marker[markerlen]" at the end of line "lnum".
-// Delete 'commentstring' if it matches.
-// If the marker is not found, there is no error message. Could be a missing
-// close-marker.
+/// Delete marker "marker[markerlen]" at the end of line "lnum".
+/// Delete 'commentstring' if it matches.
+/// If the marker is not found, there is no error message. Could be a missing
+/// close-marker.
static void foldDelMarker(buf_T *buf, linenr_T lnum, char_u *marker, size_t markerlen)
{
- char_u *newline;
- char_u *cms = buf->b_p_cms;
- char_u *cms2;
-
// end marker may be missing and fold extends below the last line
if (lnum > buf->b_ml.ml_line_count) {
return;
}
+
+ char_u *cms = buf->b_p_cms;
char_u *line = ml_get_buf(buf, lnum, false);
for (char_u *p = line; *p != NUL; p++) {
if (STRNCMP(p, marker, markerlen) != 0) {
@@ -1754,11 +1647,11 @@ static void foldDelMarker(buf_T *buf, linenr_T lnum, char_u *marker, size_t mark
// Found the marker, include a digit if it's there.
size_t len = markerlen;
if (ascii_isdigit(p[len])) {
- ++len;
+ len++;
}
if (*cms != NUL) {
// Also delete 'commentstring' if it matches.
- cms2 = (char_u *)strstr((char *)cms, "%s");
+ char_u *cms2 = (char_u *)strstr((char *)cms, "%s");
if (p - line >= cms2 - cms
&& STRNCMP(p - (cms2 - cms), cms, cms2 - cms) == 0
&& STRNCMP(p + len, cms2 + 2, STRLEN(cms2 + 2)) == 0) {
@@ -1768,12 +1661,12 @@ static void foldDelMarker(buf_T *buf, linenr_T lnum, char_u *marker, size_t mark
}
if (u_save(lnum - 1, lnum + 1) == OK) {
// Make new line: text-before-marker + text-after-marker
- newline = xmalloc(STRLEN(line) - len + 1);
+ char_u *newline = xmalloc(STRLEN(line) - len + 1);
assert(p >= line);
memcpy(newline, line, (size_t)(p - line));
STRCPY(newline + (p - line), p + len);
ml_replace_buf(buf, lnum, newline, false);
- extmark_splice_cols(buf, (int)lnum-1, (int)(p - line),
+ extmark_splice_cols(buf, (int)lnum - 1, (int)(p - line),
(int)len, 0, kExtmarkUndo);
}
break;
@@ -1794,26 +1687,23 @@ char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, foldinfo_T foldin
{
char_u *text = NULL;
// an error occurred when evaluating 'fdt' setting
- static int got_fdt_error = FALSE;
+ static bool got_fdt_error = false;
int save_did_emsg = did_emsg;
static win_T *last_wp = NULL;
static linenr_T last_lnum = 0;
if (last_wp == NULL || last_wp != wp || last_lnum > lnum || last_lnum == 0) {
// window changed, try evaluating foldtext setting once again
- got_fdt_error = FALSE;
+ got_fdt_error = false;
}
if (!got_fdt_error) {
// a previous error should not abort evaluating 'foldexpr'
- did_emsg = FALSE;
+ did_emsg = false;
}
if (*wp->w_p_fdt != NUL) {
char dashes[MAX_LEVEL + 2];
- win_T *save_curwin;
- int level;
- char_u *p;
// Set "v:foldstart" and "v:foldend".
set_vim_var_nr(VV_FOLDSTART, (varnumber_T)lnum);
@@ -1821,7 +1711,7 @@ char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, foldinfo_T foldin
// Set "v:folddashes" to a string of "level" dashes.
// Set "v:foldlevel" to "level".
- level = foldinfo.fi_level;
+ int level = foldinfo.fi_level;
if (level > (int)sizeof(dashes) - 1) {
level = (int)sizeof(dashes) - 1;
}
@@ -1832,16 +1722,18 @@ char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, foldinfo_T foldin
// skip evaluating foldtext on errors
if (!got_fdt_error) {
- save_curwin = curwin;
+ win_T *save_curwin = curwin;
curwin = wp;
curbuf = wp->w_buffer;
emsg_silent++; // handle exceptions, but don't display errors
- text = eval_to_string_safe(wp->w_p_fdt, NULL, was_set_insecurely(wp, "foldtext", OPT_LOCAL));
+ text =
+ (char_u *)eval_to_string_safe((char *)wp->w_p_fdt, NULL,
+ was_set_insecurely(wp, "foldtext", OPT_LOCAL));
emsg_silent--;
if (text == NULL || did_emsg) {
- got_fdt_error = TRUE;
+ got_fdt_error = true;
}
curwin = save_curwin;
@@ -1858,17 +1750,18 @@ char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, foldinfo_T foldin
if (text != NULL) {
// Replace unprintable characters, if there are any. But
// replace a TAB with a space.
+ char_u *p;
for (p = text; *p != NUL; p++) {
- int len = utfc_ptr2len(p);
+ int len = utfc_ptr2len((char *)p);
if (len > 1) {
- if (!vim_isprintc(utf_ptr2char(p))) {
+ if (!vim_isprintc(utf_ptr2char((char *)p))) {
break;
}
p += len - 1;
} else if (*p == TAB) {
*p = ' ';
- } else if (ptr2cells(p) > 1) {
+ } else if (ptr2cells((char *)p) > 1) {
break;
}
}
@@ -1892,18 +1785,11 @@ char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, foldinfo_T foldin
}
// foldtext_cleanup() {{{2
-/*
- * Remove 'foldmarker' and 'commentstring' from "str" (in-place).
- */
+/// Remove 'foldmarker' and 'commentstring' from "str" (in-place).
void foldtext_cleanup(char_u *str)
{
- char_u *s;
- char_u *p;
- bool did1 = false;
- bool did2 = false;
-
// Ignore leading and trailing white space in 'commentstring'.
- char_u *cms_start = skipwhite(curbuf->b_p_cms);
+ char_u *cms_start = (char_u *)skipwhite((char *)curbuf->b_p_cms);
size_t cms_slen = STRLEN(cms_start);
while (cms_slen > 0 && ascii_iswhite(cms_start[cms_slen - 1])) {
--cms_slen;
@@ -1922,13 +1808,16 @@ void foldtext_cleanup(char_u *str)
}
// skip "%s" and white space after it
- s = skipwhite(cms_end + 2);
+ char_u *s = (char_u *)skipwhite((char *)cms_end + 2);
cms_elen -= (size_t)(s - cms_end);
cms_end = s;
}
parseMarker(curwin);
- for (s = str; *s != NUL;) {
+ bool did1 = false;
+ bool did2 = false;
+
+ for (char_u *s = str; *s != NUL;) {
size_t len = 0;
if (STRNCMP(s, curwin->w_p_fmr, foldstartmarkerlen) == 0) {
len = foldstartmarkerlen;
@@ -1937,13 +1826,13 @@ void foldtext_cleanup(char_u *str)
}
if (len > 0) {
if (ascii_isdigit(s[len])) {
- ++len;
+ len++;
}
// May remove 'commentstring' start. Useful when it's a double
// quote and we already removed a double quote.
- for (p = s; p > str && ascii_iswhite(p[-1]); p--) {
- }
+ char_u *p;
+ for (p = s; p > str && ascii_iswhite(p[-1]); p--) {}
if (p >= str + cms_slen
&& STRNCMP(p - cms_slen, cms_start, cms_slen) == 0) {
len += (size_t)(s - p) + cms_slen;
@@ -1961,7 +1850,7 @@ void foldtext_cleanup(char_u *str)
}
if (len != 0) {
while (ascii_iswhite(s[len])) {
- ++len;
+ len++;
}
STRMOVE(s, s + len);
} else {
@@ -1974,16 +1863,10 @@ void foldtext_cleanup(char_u *str)
// Function declarations. {{{2
// foldUpdateIEMS() {{{2
-/*
- * Update the folding for window "wp", at least from lines "top" to "bot".
- * IEMS = "Indent Expr Marker Syntax"
- */
+/// Update the folding for window "wp", at least from lines "top" to "bot".
+/// IEMS = "Indent Expr Marker Syntax"
static void foldUpdateIEMS(win_T *const wp, linenr_T top, linenr_T bot)
{
- fline_T fline;
- LevelGetter getlevel = NULL;
- fold_T *fp;
-
// Avoid problems when being called recursively.
if (invalid_top != (linenr_T)0) {
return;
@@ -1995,7 +1878,7 @@ static void foldUpdateIEMS(win_T *const wp, linenr_T top, linenr_T bot)
bot = wp->w_buffer->b_ml.ml_line_count;
wp->w_foldinvalid = false;
- // Mark all folds a maybe-small.
+ // Mark all folds as maybe-small.
setSmallMaybe(&wp->w_folds);
}
@@ -2015,6 +1898,8 @@ static void foldUpdateIEMS(win_T *const wp, linenr_T top, linenr_T bot)
top = wp->w_buffer->b_ml.ml_line_count;
}
+ fline_T fline;
+
fold_changed = false;
fline.wp = wp;
fline.off = 0;
@@ -2027,6 +1912,8 @@ static void foldUpdateIEMS(win_T *const wp, linenr_T top, linenr_T bot)
invalid_top = top;
invalid_bot = bot;
+ LevelGetter getlevel = NULL;
+
if (foldmethodIsMarker(wp)) {
getlevel = foldlevelMarker;
@@ -2062,7 +1949,7 @@ static void foldUpdateIEMS(win_T *const wp, linenr_T top, linenr_T bot)
// start one line back, because a "<1" may indicate the end of a
// fold in the topline
if (top > 1) {
- --fline.lnum;
+ fline.lnum--;
}
} else if (foldmethodIsSyntax(wp)) {
getlevel = foldlevelSyntax;
@@ -2070,6 +1957,12 @@ static void foldUpdateIEMS(win_T *const wp, linenr_T top, linenr_T bot)
getlevel = foldlevelDiff;
} else {
getlevel = foldlevelIndent;
+ // Start one line back, because if the line above "top" has an
+ // undefined fold level, folding it relies on the line under it,
+ // which is "top".
+ if (top > 1) {
+ fline.lnum--;
+ }
}
// Backup to a line for which the fold level is defined. Since it's
@@ -2086,13 +1979,11 @@ static void foldUpdateIEMS(win_T *const wp, linenr_T top, linenr_T bot)
}
}
- /*
- * If folding is defined by the syntax, it is possible that a change in
- * one line will cause all sub-folds of the current fold to change (e.g.,
- * closing a C-style comment can cause folds in the subsequent lines to
- * appear). To take that into account we should adjust the value of "bot"
- * to point to the end of the current fold:
- */
+ // If folding is defined by the syntax, it is possible that a change in
+ // one line will cause all sub-folds of the current fold to change (e.g.,
+ // closing a C-style comment can cause folds in the subsequent lines to
+ // appear). To take that into account we should adjust the value of "bot"
+ // to point to the end of the current fold:
if (foldlevelSyntax == getlevel) {
garray_T *gap = &wp->w_folds;
fold_T *fpn = NULL;
@@ -2104,7 +1995,7 @@ static void foldUpdateIEMS(win_T *const wp, linenr_T top, linenr_T bot)
if (!foldFind(gap, lnum_rel, &fpn)) {
break;
}
- ++current_fdl;
+ current_fdl++;
fold_start_lnum += fpn->fd_top;
gap = &fpn->fd_nested;
@@ -2125,6 +2016,9 @@ static void foldUpdateIEMS(win_T *const wp, linenr_T top, linenr_T bot)
if (start > end && end < wp->w_buffer->b_ml.ml_line_count) {
end = start;
}
+
+ fold_T *fp;
+
while (!got_int) {
// Always stop at the end of the file ("end" can be past the end of
// the file).
@@ -2169,7 +2063,7 @@ static void foldUpdateIEMS(win_T *const wp, linenr_T top, linenr_T bot)
if (fline.lnum == wp->w_buffer->b_ml.ml_line_count) {
break;
}
- ++fline.lnum;
+ fline.lnum++;
fline.lvl = fline.lvl_next;
getlevel(&fline);
}
@@ -2227,23 +2121,12 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *const gap, const int level,
const linenr_T startlnum, fline_T *const flp,
LevelGetter getlevel, linenr_T bot, const char topflags)
{
- linenr_T ll;
fold_T *fp = NULL;
- fold_T *fp2;
- int lvl = level;
- linenr_T startlnum2 = startlnum;
- const linenr_T firstlnum = flp->lnum; // first lnum we got
- int i;
- bool finish = false;
- const linenr_T linecount = flp->wp->w_buffer->b_ml.ml_line_count - flp->off;
- int concat;
-
- /*
- * If using the marker method, the start line is not the start of a fold
- * at the level we're dealing with and the level is non-zero, we must use
- * the previous fold. But ignore a fold that starts at or below
- * startlnum, it must be deleted.
- */
+
+ // If using the marker method, the start line is not the start of a fold
+ // at the level we're dealing with and the level is non-zero, we must use
+ // the previous fold. But ignore a fold that starts at or below
+ // startlnum, it must be deleted.
if (getlevel == foldlevelMarker && flp->start <= flp->lvl - level
&& flp->lvl > 0) {
(void)foldFind(gap, startlnum - 1, &fp);
@@ -2254,17 +2137,22 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *const gap, const int level,
}
}
- /*
- * Loop over all lines in this fold, or until "bot" is hit.
- * Handle nested folds inside of this fold.
- * "flp->lnum" is the current line. When finding the end of the fold, it
- * is just below the end of the fold.
- * "*flp" contains the level of the line "flp->lnum" or a following one if
- * there are lines with an invalid fold level. "flp->lnum_save" is the
- * line number that was used to get the fold level (below "flp->lnum" when
- * it has an invalid fold level). When called the fold level is always
- * valid, thus "flp->lnum_save" is equal to "flp->lnum".
- */
+ fold_T *fp2;
+ int lvl = level;
+ linenr_T startlnum2 = startlnum;
+ const linenr_T firstlnum = flp->lnum; // first lnum we got
+ bool finish = false;
+ const linenr_T linecount = flp->wp->w_buffer->b_ml.ml_line_count - flp->off;
+
+ // Loop over all lines in this fold, or until "bot" is hit.
+ // Handle nested folds inside of this fold.
+ // "flp->lnum" is the current line. When finding the end of the fold, it
+ // is just below the end of the fold.
+ // "*flp" contains the level of the line "flp->lnum" or a following one if
+ // there are lines with an invalid fold level. "flp->lnum_save" is the
+ // line number that was used to get the fold level (below "flp->lnum" when
+ // it has an invalid fold level). When called the fold level is always
+ // valid, thus "flp->lnum_save" is equal to "flp->lnum".
flp->lnum_save = flp->lnum;
while (!got_int) {
// Updating folds can be slow, check for CTRL-C.
@@ -2294,15 +2182,15 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *const gap, const int level,
&& getlevel != foldlevelSyntax) {
break;
}
- i = 0;
+ int i = 0;
fp2 = fp;
if (lvl >= level) {
// Compute how deep the folds currently are, if it's deeper
// than "lvl" then some must be deleted, need to update
// at least one nested fold.
- ll = flp->lnum - fp->fd_top;
+ int ll = flp->lnum - fp->fd_top;
while (foldFind(&fp2->fd_nested, ll, &fp2)) {
- ++i;
+ i++;
ll -= fp2->fd_top;
}
}
@@ -2327,13 +2215,12 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *const gap, const int level,
|| flp->start != 0
|| flp->had_end <= MAX_LEVEL
|| flp->lnum == linecount)) {
- /*
- * Remove or update folds that have lines between startlnum and
- * firstlnum.
- */
+ // Remove or update folds that have lines between startlnum and
+ // firstlnum.
while (!got_int) {
// set concat to 1 if it's allowed to concatenate this fold
// with a previous one that touches it.
+ int concat;
if (flp->start != 0 || flp->had_end <= MAX_LEVEL) {
concat = 0;
} else {
@@ -2376,6 +2263,7 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *const gap, const int level,
}
fp->fd_len += fp->fd_top - firstlnum;
fp->fd_top = firstlnum;
+ fp->fd_small = kNone;
fold_changed = true;
} else if ((flp->start != 0 && lvl == level)
|| (firstlnum != startlnum)) {
@@ -2400,7 +2288,7 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *const gap, const int level,
}
foldRemove(flp->wp, &fp->fd_nested, breakstart - fp->fd_top,
breakend - fp->fd_top);
- i = (int)(fp - (fold_T *)gap->ga_data);
+ int i = (int)(fp - (fold_T *)gap->ga_data);
foldSplit(flp->wp->w_buffer, gap, i, breakstart, breakend - 1);
fp = (fold_T *)gap->ga_data + i + 1;
// If using the "marker" or "syntax" method, we
@@ -2413,7 +2301,7 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *const gap, const int level,
}
}
if (fp->fd_top == startlnum && concat) {
- i = (int)(fp - (fold_T *)gap->ga_data);
+ int i = (int)(fp - (fold_T *)gap->ga_data);
if (i != 0) {
fp2 = fp - 1;
if (fp2->fd_top + fp2->fd_len == fp->fd_top) {
@@ -2442,6 +2330,7 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *const gap, const int level,
} else {
// Insert new fold. Careful: ga_data may be NULL and it
// may change!
+ int i;
if (gap->ga_len == 0) {
i = 0;
} else {
@@ -2482,17 +2371,13 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *const gap, const int level,
}
if (lvl < level || flp->lnum > linecount) {
- /*
- * Found a line with a lower foldlevel, this fold ends just above
- * "flp->lnum".
- */
+ // Found a line with a lower foldlevel, this fold ends just above
+ // "flp->lnum".
break;
}
- /*
- * The fold includes the line "flp->lnum" and "flp->lnum_save".
- * Check "fp" for safety.
- */
+ // The fold includes the line "flp->lnum" and "flp->lnum_save".
+ // Check "fp" for safety.
if (lvl > level && fp != NULL) {
// There is a nested fold, handle it recursively.
// At least do one line (can happen when finish is true).
@@ -2504,7 +2389,7 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *const gap, const int level,
// this fold.
flp->lnum = flp->lnum_save - fp->fd_top;
flp->off += fp->fd_top;
- i = (int)(fp - (fold_T *)gap->ga_data);
+ int i = (int)(fp - (fold_T *)gap->ga_data);
bot = foldUpdateIEMSRecurse(&fp->fd_nested, level + 1,
startlnum2 - fp->fd_top, flp, getlevel,
bot - fp->fd_top, fp->fd_flags);
@@ -2517,14 +2402,12 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *const gap, const int level,
// This fold may end at the same line, don't incr. flp->lnum.
} else {
- /*
- * Get the level of the next line, then continue the loop to check
- * if it ends there.
- * Skip over undefined lines, to find the foldlevel after it.
- * For the last line in the file the foldlevel is always valid.
- */
+ // Get the level of the next line, then continue the loop to check
+ // if it ends there.
+ // Skip over undefined lines, to find the foldlevel after it.
+ // For the last line in the file the foldlevel is always valid.
flp->lnum = flp->lnum_save;
- ll = flp->lnum + 1;
+ int ll = flp->lnum + 1;
while (!got_int) {
// Make the previous level available to foldlevel().
prev_lnum = flp->lnum;
@@ -2555,11 +2438,9 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *const gap, const int level,
return bot;
}
- /*
- * Get here when:
- * lvl < level: the folds ends just above "flp->lnum"
- * lvl >= level: fold continues below "bot"
- */
+ // Get here when:
+ // lvl < level: the folds ends just above "flp->lnum"
+ // lvl >= level: fold continues below "bot"
// Current fold at least extends until lnum.
if (fp->fd_len < flp->lnum - fp->fd_top) {
@@ -2591,7 +2472,7 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *const gap, const int level,
} else {
// indent or expr method: split fold to create a new one
// below bot
- i = (int)(fp - (fold_T *)gap->ga_data);
+ int i = (int)(fp - (fold_T *)gap->ga_data);
foldSplit(flp->wp->w_buffer, gap, i, flp->lnum, bot);
fp = (fold_T *)gap->ga_data + i;
}
@@ -2614,7 +2495,7 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *const gap, const int level,
// Make fold that includes lnum start at lnum.
foldMarkAdjustRecurse(flp->wp, &fp2->fd_nested,
(linenr_T)0, (flp->lnum - fp2->fd_top - 1),
- (linenr_T)MAXLNUM, (fp2->fd_top-flp->lnum));
+ (linenr_T)MAXLNUM, (fp2->fd_top - flp->lnum));
fp2->fd_len -= flp->lnum - fp2->fd_top;
fp2->fd_top = flp->lnum;
fold_changed = true;
@@ -2640,16 +2521,12 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *const gap, const int level,
}
// foldInsert() {{{2
-/*
- * Insert a new fold in "gap" at position "i".
- */
+/// Insert a new fold in "gap" at position "i".
static void foldInsert(garray_T *gap, int i)
{
- fold_T *fp;
-
ga_grow(gap, 1);
- fp = (fold_T *)gap->ga_data + i;
+ fold_T *fp = (fold_T *)gap->ga_data + i;
if (gap->ga_len > 0 && i < gap->ga_len) {
memmove(fp + 1, fp, sizeof(fold_T) * (size_t)(gap->ga_len - i));
}
@@ -2658,13 +2535,11 @@ static void foldInsert(garray_T *gap, int i)
}
// foldSplit() {{{2
-/*
- * Split the "i"th fold in "gap", which starts before "top" and ends below
- * "bot" in two pieces, one ending above "top" and the other starting below
- * "bot".
- * The caller must first have taken care of any nested folds from "top" to
- * "bot"!
- */
+/// Split the "i"th fold in "gap", which starts before "top" and ends below
+/// "bot" in two pieces, one ending above "top" and the other starting below
+/// "bot".
+/// The caller must first have taken care of any nested folds from "top" to
+/// "bot"!
static void foldSplit(buf_T *buf, garray_T *const gap, const int i, const linenr_T top,
const linenr_T bot)
{
@@ -2686,7 +2561,8 @@ static void foldSplit(buf_T *buf, garray_T *const gap, const int i, const linenr
// any between top and bot, they have been removed by the caller.
garray_T *const gap1 = &fp->fd_nested;
garray_T *const gap2 = &fp[1].fd_nested;
- if (foldFind(gap1, bot + 1 - fp->fd_top, &fp2)) {
+ (void)foldFind(gap1, bot + 1 - fp->fd_top, &fp2);
+ if (fp2 != NULL) {
const int len = (int)((fold_T *)gap1->ga_data + gap1->ga_len - fp2);
if (len > 0) {
ga_grow(gap2, len);
@@ -2704,32 +2580,30 @@ static void foldSplit(buf_T *buf, garray_T *const gap, const int i, const linenr
}
// foldRemove() {{{2
-/*
- * Remove folds within the range "top" to and including "bot".
- * Check for these situations:
- * 1 2 3
- * 1 2 3
- * top 2 3 4 5
- * 2 3 4 5
- * bot 2 3 4 5
- * 3 5 6
- * 3 5 6
- *
- * 1: not changed
- * 2: truncate to stop above "top"
- * 3: split in two parts, one stops above "top", other starts below "bot".
- * 4: deleted
- * 5: made to start below "bot".
- * 6: not changed
- */
+/// Remove folds within the range "top" to and including "bot".
+/// Check for these situations:
+/// 1 2 3
+/// 1 2 3
+/// top 2 3 4 5
+/// 2 3 4 5
+/// bot 2 3 4 5
+/// 3 5 6
+/// 3 5 6
+///
+/// 1: not changed
+/// 2: truncate to stop above "top"
+/// 3: split in two parts, one stops above "top", other starts below "bot".
+/// 4: deleted
+/// 5: made to start below "bot".
+/// 6: not changed
static void foldRemove(win_T *const wp, garray_T *gap, linenr_T top, linenr_T bot)
{
- fold_T *fp = NULL;
-
if (bot < top) {
return; // nothing to do
}
+ fold_T *fp = NULL;
+
while (gap->ga_len > 0) {
// Find fold that includes top or a following one.
if (foldFind(gap, top, &fp) && fp->fd_top < top) {
@@ -2786,35 +2660,35 @@ static void foldReverseOrder(garray_T *gap, const linenr_T start_arg, const line
}
// foldMoveRange() {{{2
-// Move folds within the inclusive range "line1" to "line2" to after "dest"
-// require "line1" <= "line2" <= "dest"
-//
-// There are the following situations for the first fold at or below line1 - 1.
-// 1 2 3 4
-// 1 2 3 4
-// line1 2 3 4
-// 2 3 4 5 6 7
-// line2 3 4 5 6 7
-// 3 4 6 7 8 9
-// dest 4 7 8 9
-// 4 7 8 10
-// 4 7 8 10
-//
-// In the following descriptions, "moved" means moving in the buffer, *and* in
-// the fold array.
-// Meanwhile, "shifted" just means moving in the buffer.
-// 1. not changed
-// 2. truncated above line1
-// 3. length reduced by line2 - line1, folds starting between the end of 3 and
-// dest are truncated and shifted up
-// 4. internal folds moved (from [line1, line2] to dest)
-// 5. moved to dest.
-// 6. truncated below line2 and moved.
-// 7. length reduced by line2 - dest, folds starting between line2 and dest are
-// removed, top is moved down by move_len.
-// 8. truncated below dest and shifted up.
-// 9. shifted up
-// 10. not changed
+/// Move folds within the inclusive range "line1" to "line2" to after "dest"
+/// require "line1" <= "line2" <= "dest"
+///
+/// There are the following situations for the first fold at or below line1 - 1.
+/// 1 2 3 4
+/// 1 2 3 4
+/// line1 2 3 4
+/// 2 3 4 5 6 7
+/// line2 3 4 5 6 7
+/// 3 4 6 7 8 9
+/// dest 4 7 8 9
+/// 4 7 8 10
+/// 4 7 8 10
+///
+/// In the following descriptions, "moved" means moving in the buffer, *and* in
+/// the fold array.
+/// Meanwhile, "shifted" just means moving in the buffer.
+/// 1. not changed
+/// 2. truncated above line1
+/// 3. length reduced by line2 - line1, folds starting between the end of 3 and
+/// dest are truncated and shifted up
+/// 4. internal folds moved (from [line1, line2] to dest)
+/// 5. moved to dest.
+/// 6. truncated below line2 and moved.
+/// 7. length reduced by line2 - dest, folds starting between line2 and dest are
+/// removed, top is moved down by move_len.
+/// 8. truncated below dest and shifted up.
+/// 9. shifted up
+/// 10. not changed
static void truncate_fold(win_T *const wp, fold_T *fp, linenr_T end)
{
// I want to stop *at here*, foldRemove() stops *above* top
@@ -2824,9 +2698,10 @@ static void truncate_fold(win_T *const wp, fold_T *fp, linenr_T end)
}
#define FOLD_END(fp) ((fp)->fd_top + (fp)->fd_len - 1)
+// -V:VALID_FOLD:V560
#define VALID_FOLD(fp, gap) \
((gap)->ga_len > 0 && (fp) < ((fold_T *)(gap)->ga_data + (gap)->ga_len))
-#define FOLD_INDEX(fp, gap) ((size_t)(fp - ((fold_T *)(gap)->ga_data)))
+#define FOLD_INDEX(fp, gap) ((size_t)((fp) - ((fold_T *)(gap)->ga_data)))
void foldMoveRange(win_T *const wp, garray_T *gap, const linenr_T line1, const linenr_T line2,
const linenr_T dest)
{
@@ -2929,35 +2804,32 @@ void foldMoveRange(win_T *const wp, garray_T *gap, const linenr_T line1, const l
#undef FOLD_INDEX
// foldMerge() {{{2
-/*
- * Merge two adjacent folds (and the nested ones in them).
- * This only works correctly when the folds are really adjacent! Thus "fp1"
- * must end just above "fp2".
- * The resulting fold is "fp1", nested folds are moved from "fp2" to "fp1".
- * Fold entry "fp2" in "gap" is deleted.
- */
+/// Merge two adjacent folds (and the nested ones in them).
+/// This only works correctly when the folds are really adjacent! Thus "fp1"
+/// must end just above "fp2".
+/// The resulting fold is "fp1", nested folds are moved from "fp2" to "fp1".
+/// Fold entry "fp2" in "gap" is deleted.
static void foldMerge(win_T *const wp, fold_T *fp1, garray_T *gap, fold_T *fp2)
{
fold_T *fp3;
fold_T *fp4;
- int idx;
garray_T *gap1 = &fp1->fd_nested;
garray_T *gap2 = &fp2->fd_nested;
// If the last nested fold in fp1 touches the first nested fold in fp2,
// merge them recursively.
- if (foldFind(gap1, fp1->fd_len - 1L, &fp3) && foldFind(gap2, 0L, &fp4)) {
+ if (foldFind(gap1, fp1->fd_len - 1, &fp3) && foldFind(gap2, 0L, &fp4)) {
foldMerge(wp, fp3, gap2, fp4);
}
// Move nested folds in fp2 to the end of fp1.
if (!GA_EMPTY(gap2)) {
ga_grow(gap1, gap2->ga_len);
- for (idx = 0; idx < gap2->ga_len; ++idx) {
+ for (int idx = 0; idx < gap2->ga_len; idx++) {
((fold_T *)gap1->ga_data)[gap1->ga_len]
= ((fold_T *)gap2->ga_data)[idx];
((fold_T *)gap1->ga_data)[gap1->ga_len].fd_top += fp1->fd_len;
- ++gap1->ga_len;
+ gap1->ga_len++;
}
gap2->ga_len = 0;
}
@@ -2968,23 +2840,20 @@ static void foldMerge(win_T *const wp, fold_T *fp1, garray_T *gap, fold_T *fp2)
}
// foldlevelIndent() {{{2
-/*
- * Low level function to get the foldlevel for the "indent" method.
- * Doesn't use any caching.
- * Returns a level of -1 if the foldlevel depends on surrounding lines.
- */
+/// Low level function to get the foldlevel for the "indent" method.
+/// Doesn't use any caching.
+///
+/// @return a level of -1 if the foldlevel depends on surrounding lines.
static void foldlevelIndent(fline_T *flp)
{
- char_u *s;
- buf_T *buf;
linenr_T lnum = flp->lnum + flp->off;
- buf = flp->wp->w_buffer;
- s = skipwhite(ml_get_buf(buf, lnum, false));
+ buf_T *buf = flp->wp->w_buffer;
+ char_u *s = (char_u *)skipwhite((char *)ml_get_buf(buf, lnum, false));
// empty line or lines starting with a character in 'foldignore': level
// depends on surrounding lines
- if (*s == NUL || vim_strchr(flp->wp->w_p_fdi, *s) != NULL) {
+ if (*s == NUL || vim_strchr((char *)flp->wp->w_p_fdi, *s) != NULL) {
// first and last line can't be undefined, use level 0
if (lnum == 1 || lnum == buf->b_ml.ml_line_count) {
flp->lvl = 0;
@@ -3000,10 +2869,8 @@ static void foldlevelIndent(fline_T *flp)
}
// foldlevelDiff() {{{2
-/*
- * Low level function to get the foldlevel for the "diff" method.
- * Doesn't use any caching.
- */
+/// Low level function to get the foldlevel for the "diff" method.
+/// Doesn't use any caching.
static void foldlevelDiff(fline_T *flp)
{
if (diff_infold(flp->wp, flp->lnum + flp->off)) {
@@ -3014,18 +2881,15 @@ static void foldlevelDiff(fline_T *flp)
}
// foldlevelExpr() {{{2
-/*
- * Low level function to get the foldlevel for the "expr" method.
- * Doesn't use any caching.
- * Returns a level of -1 if the foldlevel depends on surrounding lines.
- */
+/// Low level function to get the foldlevel for the "expr" method.
+/// Doesn't use any caching.
+///
+/// @return a level of -1 if the foldlevel depends on surrounding lines.
static void foldlevelExpr(fline_T *flp)
{
- win_T *win;
- int c;
linenr_T lnum = flp->lnum + flp->off;
- win = curwin;
+ win_T *win = curwin;
curwin = flp->wp;
curbuf = flp->wp->w_buffer;
set_vim_var_nr(VV_LNUM, (varnumber_T)lnum);
@@ -3040,7 +2904,9 @@ static void foldlevelExpr(fline_T *flp)
// KeyTyped may be reset to 0 when calling a function which invokes
// do_cmdline(). To make 'foldopen' work correctly restore KeyTyped.
const bool save_keytyped = KeyTyped;
- const int n = eval_foldexpr(flp->wp->w_p_fde, &c);
+
+ int c;
+ const int n = eval_foldexpr((char *)flp->wp->w_p_fde, &c);
KeyTyped = save_keytyped;
switch (c) {
@@ -3113,55 +2979,46 @@ static void foldlevelExpr(fline_T *flp)
}
// parseMarker() {{{2
-/*
- * Parse 'foldmarker' and set "foldendmarker", "foldstartmarkerlen" and
- * "foldendmarkerlen".
- * Relies on the option value to have been checked for correctness already.
- */
+/// Parse 'foldmarker' and set "foldendmarker", "foldstartmarkerlen" and
+/// "foldendmarkerlen".
+/// Relies on the option value to have been checked for correctness already.
static void parseMarker(win_T *wp)
{
- foldendmarker = vim_strchr(wp->w_p_fmr, ',');
+ foldendmarker = (char_u *)vim_strchr((char *)wp->w_p_fmr, ',');
foldstartmarkerlen = (size_t)(foldendmarker++ - wp->w_p_fmr);
foldendmarkerlen = STRLEN(foldendmarker);
}
// foldlevelMarker() {{{2
-/*
- * Low level function to get the foldlevel for the "marker" method.
- * "foldendmarker", "foldstartmarkerlen" and "foldendmarkerlen" must have been
- * set before calling this.
- * Requires that flp->lvl is set to the fold level of the previous line!
- * Careful: This means you can't call this function twice on the same line.
- * Doesn't use any caching.
- * Sets flp->start when a start marker was found.
- */
+/// Low level function to get the foldlevel for the "marker" method.
+/// "foldendmarker", "foldstartmarkerlen" and "foldendmarkerlen" must have been
+/// set before calling this.
+/// Requires that flp->lvl is set to the fold level of the previous line!
+/// Careful: This means you can't call this function twice on the same line.
+/// Doesn't use any caching.
+/// Sets flp->start when a start marker was found.
static void foldlevelMarker(fline_T *flp)
{
- char_u *startmarker;
- int cstart;
- int cend;
int start_lvl = flp->lvl;
- char_u *s;
- int n;
// cache a few values for speed
- startmarker = flp->wp->w_p_fmr;
- cstart = *startmarker;
- ++startmarker;
- cend = *foldendmarker;
+ char_u *startmarker = flp->wp->w_p_fmr;
+ int cstart = *startmarker;
+ startmarker++;
+ int cend = *foldendmarker;
// Default: no start found, next level is same as current level
flp->start = 0;
flp->lvl_next = flp->lvl;
- s = ml_get_buf(flp->wp->w_buffer, flp->lnum + flp->off, false);
+ char_u *s = ml_get_buf(flp->wp->w_buffer, flp->lnum + flp->off, false);
while (*s) {
if (*s == cstart
&& STRNCMP(s + 1, startmarker, foldstartmarkerlen - 1) == 0) {
// found startmarker: set flp->lvl
s += foldstartmarkerlen;
if (ascii_isdigit(*s)) {
- n = atoi((char *)s);
+ int n = atoi((char *)s);
if (n > 0) {
flp->lvl = n;
flp->lvl_next = n;
@@ -3172,16 +3029,16 @@ static void foldlevelMarker(fline_T *flp)
}
}
} else {
- ++flp->lvl;
- ++flp->lvl_next;
- ++flp->start;
+ flp->lvl++;
+ flp->lvl_next++;
+ flp->start++;
}
} else if (*s == cend && STRNCMP(s + 1, foldendmarker + 1,
foldendmarkerlen - 1) == 0) {
// found endmarker: set flp->lvl_next
s += foldendmarkerlen;
if (ascii_isdigit(*s)) {
- n = atoi((char *)s);
+ int n = atoi((char *)s);
if (n > 0) {
flp->lvl = n;
flp->lvl_next = n - 1;
@@ -3205,20 +3062,17 @@ static void foldlevelMarker(fline_T *flp)
}
// foldlevelSyntax() {{{2
-/*
- * Low level function to get the foldlevel for the "syntax" method.
- * Doesn't use any caching.
- */
+/// Low level function to get the foldlevel for the "syntax" method.
+/// Doesn't use any caching.
static void foldlevelSyntax(fline_T *flp)
{
linenr_T lnum = flp->lnum + flp->off;
- int n;
// Use the maximum fold level at the start of this line and the next.
flp->lvl = syn_get_foldlevel(flp->wp, lnum);
flp->start = 0;
if (lnum < flp->wp->w_buffer->b_ml.ml_line_count) {
- n = syn_get_foldlevel(flp->wp, lnum + 1);
+ int n = syn_get_foldlevel(flp->wp, lnum + 1);
if (n > flp->lvl) {
flp->start = n - flp->lvl; // fold(s) start here
flp->lvl = n;
@@ -3228,11 +3082,9 @@ static void foldlevelSyntax(fline_T *flp)
// functions for storing the fold state in a View {{{1
// put_folds() {{{2
-
-/*
- * Write commands to "fd" to restore the manual folds in window "wp".
- * Return FAIL if writing fails.
- */
+/// Write commands to "fd" to restore the manual folds in window "wp".
+///
+/// @return FAIL if writing fails.
int put_folds(FILE *fd, win_T *wp)
{
if (foldmethodIsManual(wp)) {
@@ -3252,10 +3104,9 @@ int put_folds(FILE *fd, win_T *wp)
}
// put_folds_recurse() {{{2
-/*
- * Write commands to "fd" to recreate manually created folds.
- * Returns FAIL when writing failed.
- */
+/// Write commands to "fd" to recreate manually created folds.
+///
+/// @return FAIL when writing failed.
static int put_folds_recurse(FILE *fd, garray_T *gap, linenr_T off)
{
fold_T *fp = (fold_T *)gap->ga_data;
@@ -3270,20 +3121,17 @@ static int put_folds_recurse(FILE *fd, garray_T *gap, linenr_T off)
|| put_eol(fd) == FAIL) {
return FAIL;
}
- ++fp;
+ fp++;
}
return OK;
}
// put_foldopen_recurse() {{{2
-/*
- * Write commands to "fd" to open and close manually opened/closed folds.
- * Returns FAIL when writing failed.
- */
+/// Write commands to "fd" to open and close manually opened/closed folds.
+///
+/// @return FAIL when writing failed.
static int put_foldopen_recurse(FILE *fd, win_T *wp, garray_T *gap, linenr_T off)
{
- int level;
-
fold_T *fp = (fold_T *)gap->ga_data;
for (int i = 0; i < gap->ga_len; i++) {
if (fp->fd_flags != FD_LEVEL) {
@@ -3309,7 +3157,7 @@ static int put_foldopen_recurse(FILE *fd, win_T *wp, garray_T *gap, linenr_T off
// Open or close the leaf according to the window foldlevel.
// Do not close a leaf that is already closed, as it will close
// the parent.
- level = foldLevelWin(wp, off + fp->fd_top);
+ int level = foldLevelWin(wp, off + fp->fd_top);
if ((fp->fd_flags == FD_CLOSED && wp->w_p_fdl >= level)
|| (fp->fd_flags != FD_CLOSED && wp->w_p_fdl < level)) {
if (put_fold_open_close(fd, fp, off) == FAIL) {
@@ -3318,20 +3166,19 @@ static int put_foldopen_recurse(FILE *fd, win_T *wp, garray_T *gap, linenr_T off
}
}
}
- ++fp;
+ fp++;
}
return OK;
}
// put_fold_open_close() {{{2
-/*
- * Write the open or close command to "fd".
- * Returns FAIL when writing failed.
- */
+/// Write the open or close command to "fd".
+///
+/// @return FAIL when writing failed.
static int put_fold_open_close(FILE *fd, fold_T *fp, linenr_T off)
{
- if (fprintf(fd, "%" PRId64, (int64_t)(fp->fd_top + off)) < 0
+ if (fprintf(fd, "%" PRIdLINENR, fp->fd_top + off) < 0
|| put_eol(fd) == FAIL
|| fprintf(fd, "normal! z%c",
fp->fd_flags == FD_CLOSED ? 'c' : 'o') < 0
diff --git a/src/nvim/fold.h b/src/nvim/fold.h
index 6b29214760..60ea4b322e 100644
--- a/src/nvim/fold.h
+++ b/src/nvim/fold.h
@@ -23,6 +23,7 @@ typedef struct foldinfo {
#define FOLDINFO_INIT { 0, 0, 0, 0 }
+EXTERN int disable_fold_update INIT(= 0);
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "fold.h.generated.h"
diff --git a/src/nvim/func_attr.h b/src/nvim/func_attr.h
index afbd87f2be..6c049df6ff 100644
--- a/src/nvim/func_attr.h
+++ b/src/nvim/func_attr.h
@@ -113,7 +113,7 @@
// void myfunc(void) REAL_FATTR_ALWAYS_INLINE;
# define REAL_FATTR_MALLOC __attribute__((malloc))
# define REAL_FATTR_ALLOC_ALIGN(x) __attribute__((alloc_align(x)))
-# define REAL_FATTR_PURE __attribute__ ((pure))
+# define REAL_FATTR_PURE __attribute__((pure))
# define REAL_FATTR_CONST __attribute__((const))
# define REAL_FATTR_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
# define REAL_FATTR_ALWAYS_INLINE __attribute__((always_inline))
diff --git a/src/nvim/garray.c b/src/nvim/garray.c
index 7a3cc4a944..0c76e1a919 100644
--- a/src/nvim/garray.c
+++ b/src/nvim/garray.c
@@ -123,7 +123,7 @@ void ga_remove_duplicate_strings(garray_T *gap)
// loop over the growing array in reverse
for (int i = gap->ga_len - 1; i > 0; i--) {
- if (fnamecmp(fnames[i - 1], fnames[i]) == 0) {
+ if (FNAMECMP(fnames[i - 1], fnames[i]) == 0) {
xfree(fnames[i]);
// close the gap (move all strings one slot lower)
diff --git a/src/nvim/generators/c_grammar.lua b/src/nvim/generators/c_grammar.lua
index f35817c466..70a7be86b5 100644
--- a/src/nvim/generators/c_grammar.lua
+++ b/src/nvim/generators/c_grammar.lua
@@ -49,6 +49,7 @@ local c_proto = Ct(
(fill * Cg((P('FUNC_API_REMOTE_IMPL') * Cc(true)), 'remote_impl') ^ -1) *
(fill * Cg((P('FUNC_API_BRIDGE_IMPL') * Cc(true)), 'bridge_impl') ^ -1) *
(fill * Cg((P('FUNC_API_COMPOSITOR_IMPL') * Cc(true)), 'compositor_impl') ^ -1) *
+ (fill * Cg((P('FUNC_API_CLIENT_IMPL') * Cc(true)), 'client_impl') ^ -1) *
fill * P(';')
)
diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua
index c6dd25154b..4cf282770d 100644
--- a/src/nvim/generators/gen_api_dispatch.lua
+++ b/src/nvim/generators/gen_api_dispatch.lua
@@ -16,6 +16,10 @@ local functions = {}
local nvimdir = arg[1]
package.path = nvimdir .. '/?.lua;' .. package.path
+_G.vim = loadfile(nvimdir..'/../../runtime/lua/vim/shared.lua')()
+
+local hashy = require'generators.hashy'
+
-- names of all headers relative to the source root (for inclusion in the
-- generated file)
local headers = {}
@@ -208,8 +212,8 @@ for i = 1, #functions do
output:write('Object handle_'..fn.name..'(uint64_t channel_id, Array args, Error *error)')
output:write('\n{')
- output:write('\n#if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL')
- output:write('\n logmsg(DEBUG_LOG_LEVEL, "RPC: ", NULL, -1, true, "ch %" PRIu64 ": invoke '
+ output:write('\n#if MIN_LOG_LEVEL <= LOGLVL_DBG')
+ output:write('\n logmsg(LOGLVL_DBG, "RPC: ", NULL, -1, true, "ch %" PRIu64 ": invoke '
..fn.name..'", channel_id);')
output:write('\n#endif')
output:write('\n Object ret = NIL;')
@@ -288,7 +292,7 @@ for i = 1, #functions do
if fn.check_textlock then
output:write('\n if (textlock != 0) {')
- output:write('\n api_set_error(error, kErrorTypeException, "%s", e_secure);')
+ output:write('\n api_set_error(error, kErrorTypeException, "%s", e_textlock);')
output:write('\n goto cleanup;')
output:write('\n }\n')
end
@@ -339,24 +343,27 @@ for i = 1, #functions do
end
end
--- Generate a function that initializes method names with handler functions
-output:write([[
-void msgpack_rpc_init_method_table(void)
-{
-]])
-
-for i = 1, #functions do
- local fn = functions[i]
+local remote_fns = {}
+for _,fn in ipairs(functions) do
if fn.remote then
- output:write(' msgpack_rpc_add_method_handler('..
- '(String) {.data = "'..fn.name..'", '..
- '.size = sizeof("'..fn.name..'") - 1}, '..
- '(MsgpackRpcRequestHandler) {.fn = handle_'.. (fn.impl_name or fn.name)..
- ', .fast = '..tostring(fn.fast)..'});\n')
+ remote_fns[fn.name] = fn
end
end
+remote_fns.redraw = {impl_name="ui_client_redraw", fast=true}
+
+local hashorder, hashfun = hashy.hashy_hash("msgpack_rpc_get_handler_for", vim.tbl_keys(remote_fns), function (idx)
+ return "method_handlers["..idx.."].name"
+end)
+
+output:write("static const MsgpackRpcRequestHandler method_handlers[] = {\n")
+for _, name in ipairs(hashorder) do
+ local fn = remote_fns[name]
+ output:write(' { .name = "'..name..'", .fn = handle_'.. (fn.impl_name or fn.name)..
+ ', .fast = '..tostring(fn.fast)..'},\n')
+end
+output:write("};\n\n")
+output:write(hashfun)
-output:write('\n}\n\n')
output:close()
local mpack_output = io.open(mpack_outputf, 'wb')
@@ -428,7 +435,7 @@ local function process_function(fn)
if fn.check_textlock then
write_shifted_output(output, [[
if (textlock != 0) {
- api_set_error(&err, kErrorTypeException, "%s", e_secure);
+ api_set_error(&err, kErrorTypeException, "%s", e_textlock);
goto exit_0;
}
]])
diff --git a/src/nvim/generators/gen_api_ui_events.lua b/src/nvim/generators/gen_api_ui_events.lua
index 3cb117d8b5..93bbaab74c 100644..100755
--- a/src/nvim/generators/gen_api_ui_events.lua
+++ b/src/nvim/generators/gen_api_ui_events.lua
@@ -3,17 +3,21 @@ local mpack = require('mpack')
local nvimdir = arg[1]
package.path = nvimdir .. '/?.lua;' .. package.path
-assert(#arg == 7)
+assert(#arg == 8)
local input = io.open(arg[2], 'rb')
local proto_output = io.open(arg[3], 'wb')
local call_output = io.open(arg[4], 'wb')
local remote_output = io.open(arg[5], 'wb')
local bridge_output = io.open(arg[6], 'wb')
local metadata_output = io.open(arg[7], 'wb')
+local client_output = io.open(arg[8], 'wb')
local c_grammar = require('generators.c_grammar')
local events = c_grammar.grammar:match(input:read('*all'))
+_G.vim = loadfile(nvimdir..'/../../runtime/lua/vim/shared.lua')()
+local hashy = require'generators.hashy'
+
local function write_signature(output, ev, prefix, notype)
output:write('('..prefix)
if prefix == "" and #ev.parameters == 0 then
@@ -32,24 +36,62 @@ local function write_signature(output, ev, prefix, notype)
output:write(')')
end
-local function write_arglist(output, ev, need_copy)
- output:write(' Array args = ARRAY_DICT_INIT;\n')
+local function write_arglist(output, ev)
for j = 1, #ev.parameters do
local param = ev.parameters[j]
local kind = string.upper(param[1])
- local do_copy = need_copy and (kind == "ARRAY" or kind == "DICTIONARY" or kind == "STRING" or kind == "OBJECT")
- output:write(' ADD(args, ')
- if do_copy then
- output:write('copy_object(')
- end
+ output:write(' ADD_C(args, ')
output:write(kind..'_OBJ('..param[2]..')')
- if do_copy then
- output:write(')')
- end
output:write(');\n')
end
end
+local function call_ui_event_method(output, ev)
+ output:write('void ui_client_event_'..ev.name..'(Array args)\n{\n')
+
+ local hlattrs_args_count = 0
+ if #ev.parameters > 0 then
+ output:write(' if (args.size < '..(#ev.parameters))
+ for j = 1, #ev.parameters do
+ local kind = ev.parameters[j][1]
+ if kind ~= "Object" then
+ if kind == 'HlAttrs' then kind = 'Dictionary' end
+ output:write('\n || args.items['..(j-1)..'].type != kObjectType'..kind..'')
+ end
+ end
+ output:write(') {\n')
+ output:write(' ELOG("Error handling ui event \''..ev.name..'\'");\n')
+ output:write(' return;\n')
+ output:write(' }\n')
+ end
+
+ for j = 1, #ev.parameters do
+ local param = ev.parameters[j]
+ local kind = param[1]
+ output:write(' '..kind..' arg_'..j..' = ')
+ if kind == 'HlAttrs' then
+ -- The first HlAttrs argument is rgb_attrs and second is cterm_attrs
+ output:write('ui_client_dict2hlattrs(args.items['..(j-1)..'].data.dictionary, '..(hlattrs_args_count == 0 and 'true' or 'false')..');\n')
+ hlattrs_args_count = hlattrs_args_count + 1
+ elseif kind == 'Object' then
+ output:write('args.items['..(j-1)..'];\n')
+ else
+ output:write('args.items['..(j-1)..'].data.'..string.lower(kind)..';\n')
+ end
+ end
+
+ output:write(' ui_call_'..ev.name..'(')
+ for j = 1, #ev.parameters do
+ output:write('arg_'..j)
+ if j ~= #ev.parameters then
+ output:write(', ')
+ end
+ end
+ output:write(');\n')
+
+ output:write('}\n\n')
+end
+
for i = 1, #events do
local ev = events[i]
assert(ev.return_type == 'void')
@@ -69,7 +111,9 @@ for i = 1, #events do
remote_output:write('static void remote_ui_'..ev.name)
write_signature(remote_output, ev, 'UI *ui')
remote_output:write('\n{\n')
- write_arglist(remote_output, ev, true)
+ remote_output:write(' UIData *data = ui->data;\n')
+ remote_output:write(' Array args = data->call_buf;\n')
+ write_arglist(remote_output, ev)
remote_output:write(' push_call(ui, "'..ev.name..'", args);\n')
remote_output:write('}\n\n')
end
@@ -136,9 +180,10 @@ for i = 1, #events do
write_signature(call_output, ev, '')
call_output:write('\n{\n')
if ev.remote_only then
- write_arglist(call_output, ev, false)
+ call_output:write(' Array args = call_buf;\n')
+ write_arglist(call_output, ev)
call_output:write(' UI_LOG('..ev.name..');\n')
- call_output:write(' ui_event("'..ev.name..'", args);\n')
+ call_output:write(' ui_call_event("'..ev.name..'", args);\n')
elseif ev.compositor_impl then
call_output:write(' UI_CALL')
write_signature(call_output, ev, '!ui->composed, '..ev.name..', ui', true)
@@ -160,12 +205,36 @@ for i = 1, #events do
call_output:write(";\n")
call_output:write("}\n\n")
end
+
+ if (not ev.remote_only) and (not ev.noexport) and (not ev.client_impl) then
+ call_ui_event_method(client_output, ev)
+ end
+end
+
+local client_events = {}
+for _,ev in ipairs(events) do
+ if (not ev.noexport) and ((not ev.remote_only) or ev.client_impl) then
+ client_events[ev.name] = ev
+ end
+end
+
+local hashorder, hashfun = hashy.hashy_hash("ui_client_handler", vim.tbl_keys(client_events), function (idx)
+ return "event_handlers["..idx.."].name"
+end)
+
+client_output:write("static const UIClientHandler event_handlers[] = {\n")
+
+for _, name in ipairs(hashorder) do
+ client_output:write(' { .name = "'..name..'", .fn = ui_client_event_'..name..'},\n')
end
+client_output:write('\n};\n\n')
+client_output:write(hashfun)
+
proto_output:close()
call_output:close()
remote_output:close()
-bridge_output:close()
+client_output:close()
-- don't expose internal attributes like "impl_name" in public metadata
local exported_attributes = {'name', 'parameters',
diff --git a/src/nvim/generators/gen_char_blob.lua b/src/nvim/generators/gen_char_blob.lua
index 3ec1ff2caf..11f6cbcc13 100644
--- a/src/nvim/generators/gen_char_blob.lua
+++ b/src/nvim/generators/gen_char_blob.lua
@@ -28,16 +28,19 @@ local target = io.open(target_file, 'w')
target:write('#include <stdint.h>\n\n')
+local index_items = {}
+
local warn_on_missing_compiler = true
-local varnames = {}
+local modnames = {}
for argi = 2, #arg, 2 do
local source_file = arg[argi]
- local varname = arg[argi + 1]
- if varnames[varname] then
- error(string.format("varname %q is already specified for file %q", varname, varnames[varname]))
+ local modname = arg[argi + 1]
+ if modnames[modname] then
+ error(string.format("modname %q is already specified for file %q", modname, modnames[modname]))
end
- varnames[varname] = source_file
+ modnames[modname] = source_file
+ local varname = string.gsub(modname,'%.','_dot_').."_module"
target:write(('static const uint8_t %s[] = {\n'):format(varname))
local output
@@ -78,6 +81,13 @@ for argi = 2, #arg, 2 do
end
target:write(' 0};\n')
+ if modname ~= "_" then
+ table.insert(index_items, ' { "'..modname..'", '..varname..', sizeof '..varname..' },\n\n')
+ end
end
+target:write('static ModuleDef builtin_modules[] = {\n')
+target:write(table.concat(index_items))
+target:write('};\n')
+
target:close()
diff --git a/src/nvim/generators/gen_eval.lua b/src/nvim/generators/gen_eval.lua
index 945fa5099f..c72249161b 100644
--- a/src/nvim/generators/gen_eval.lua
+++ b/src/nvim/generators/gen_eval.lua
@@ -1,9 +1,12 @@
local mpack = require('mpack')
local nvimsrcdir = arg[1]
-local autodir = arg[2]
-local metadata_file = arg[3]
-local funcs_file = arg[4]
+local shared_file = arg[2]
+local autodir = arg[3]
+local metadata_file = arg[4]
+local funcs_file = arg[5]
+
+_G.vim = loadfile(shared_file)()
if nvimsrcdir == '--help' then
print([[
@@ -20,7 +23,9 @@ package.path = nvimsrcdir .. '/?.lua;' .. package.path
local funcsfname = autodir .. '/funcs.generated.h'
-local gperfpipe = io.open(funcsfname .. '.gperf', 'wb')
+local hashy = require'generators.hashy'
+
+local hashpipe = io.open(funcsfname, 'wb')
local funcs = require('eval').funcs
local metadata = mpack.unpack(io.open(metadata_file, 'rb'):read("*all"))
@@ -38,21 +43,15 @@ local funcsdata = io.open(funcs_file, 'w')
funcsdata:write(mpack.pack(funcs))
funcsdata:close()
-gperfpipe:write([[
-%language=ANSI-C
-%global-table
-%readonly-tables
-%define initializer-suffix ,0,0,BASE_NONE,NULL,NULL
-%define word-array-name functions
-%define hash-function-name hash_internal_func_gperf
-%define lookup-function-name find_internal_func_gperf
-%omit-struct-type
-%struct-type
-VimLFuncDef;
-%%
-]])
-for name, def in pairs(funcs) do
+local names = vim.tbl_keys(funcs)
+
+local neworder, hashfun = hashy.hashy_hash("find_internal_func", names, function (idx)
+ return "functions["..idx.."].name"
+end)
+hashpipe:write("static const EvalFuncDef functions[] = {\n")
+for _, name in ipairs(neworder) do
+ local def = funcs[name]
local args = def.args or 0
if type(args) == 'number' then
args = {args, args}
@@ -62,7 +61,11 @@ for name, def in pairs(funcs) do
local base = def.base or "BASE_NONE"
local func = def.func or ('f_' .. name)
local data = def.data or "NULL"
- gperfpipe:write(('%s, %s, %s, %s, &%s, (FunPtr)%s\n')
- :format(name, args[1], args[2], base, func, data))
+ local fast = def.fast and 'true' or 'false'
+ hashpipe:write((' { "%s", %s, %s, %s, %s, &%s, (FunPtr)%s },\n')
+ :format(name, args[1], args[2], base, fast, func, data))
end
-gperfpipe:close()
+hashpipe:write(' { NULL, 0, 0, BASE_NONE, false, NULL, NULL },\n')
+hashpipe:write("};\n\n")
+hashpipe:write(hashfun)
+hashpipe:close()
diff --git a/src/nvim/generators/gen_ex_cmds.lua b/src/nvim/generators/gen_ex_cmds.lua
index 844661adc3..255c415a4d 100644
--- a/src/nvim/generators/gen_ex_cmds.lua
+++ b/src/nvim/generators/gen_ex_cmds.lua
@@ -65,20 +65,31 @@ for _, cmd in ipairs(defs) do
assert(cmd.addr_type ~= 'ADDR_OTHER' and cmd.addr_type ~= 'ADDR_NONE',
string.format('ex_cmds.lua:%s: Missing misplaced DFLALL\n', cmd.command))
end
+ if bit.band(cmd.flags, flags.PREVIEW) == flags.PREVIEW then
+ assert(cmd.preview_func ~= nil,
+ string.format('ex_cmds.lua:%s: Missing preview_func\n', cmd.command))
+ end
local enumname = cmd.enum or ('CMD_' .. cmd.command)
local byte_cmd = cmd.command:sub(1, 1):byte()
if byte_a <= byte_cmd and byte_cmd <= byte_z then
table.insert(cmds, cmd.command)
end
+ local preview_func
+ if cmd.preview_func then
+ preview_func = string.format("(ex_preview_func_T)&%s", cmd.preview_func)
+ else
+ preview_func = "NULL"
+ end
enumfile:write(' ' .. enumname .. ',\n')
defsfile:write(string.format([[
[%s] = {
- .cmd_name = (char_u *) "%s",
+ .cmd_name = "%s",
.cmd_func = (ex_func_T)&%s,
+ .cmd_preview_func = %s,
.cmd_argt = %uL,
.cmd_addr_type = %s
},
-]], enumname, cmd.command, cmd.func, cmd.flags, cmd.addr_type))
+]], enumname, cmd.command, cmd.func, preview_func, cmd.flags, cmd.addr_type))
end
for i = #cmds, 1, -1 do
local cmd = cmds[i]
diff --git a/src/nvim/generators/gen_keysets.lua b/src/nvim/generators/gen_keysets.lua
index 01d8c1d357..633c5da184 100644
--- a/src/nvim/generators/gen_keysets.lua
+++ b/src/nvim/generators/gen_keysets.lua
@@ -27,7 +27,8 @@ local defspipe = io.open(defs_file, 'wb')
local keysets = require'api.keysets'
local keywords = {
- register = true,
+ register = true;
+ default = true;
}
local function sanitize(key)
diff --git a/src/nvim/generators/hashy.lua b/src/nvim/generators/hashy.lua
index fac24c810a..b10bafb9f9 100644
--- a/src/nvim/generators/hashy.lua
+++ b/src/nvim/generators/hashy.lua
@@ -77,7 +77,7 @@ function M.switcher(put, tab, maxlen, worst_buck_size)
put "break;\n"
end
put " default: break;\n"
- put " }\n "
+ put " }\n "
else
local startidx = #neworder
table.insert(neworder, posbuck[keys[1]][1])
@@ -85,7 +85,7 @@ function M.switcher(put, tab, maxlen, worst_buck_size)
put("low = "..startidx.."; ")
if bucky then put("high = "..endidx.."; ") end
end
- put " break;\n"
+ put "break;\n"
end
end
put " default: break;\n"
@@ -105,17 +105,23 @@ function M.hashy_hash(name, strings, access)
end
local neworder = M.switcher(put, len_pos_buckets, maxlen, worst_buck_size)
if worst_buck_size > 1 then
- error [[ not implemented yet ]] -- TODO(bfredl)
+ put ([[
+ for (int i = low; i < high; i++) {
+ if (!memcmp(str, ]]..access("i")..[[, len)) {
+ return i;
+ }
+ }
+ return -1;
+]])
else
- put [[
- if (low < 0) {
+ put ([[
+ if (low < 0 || memcmp(str, ]]..access("low")..[[, len)) {
return -1;
}
- ]]
- put("if(memcmp(str, "..access("low")..", len)) {\n return -1;\n }\n")
- put " return low;\n"
- put "}\n\n"
+ return low;
+]])
end
+ put "}\n\n"
return neworder, table.concat(stats)
end
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index 5565e17597..00372d4f3d 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -1,14 +1,8 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
-/*
- * getchar.c
- *
- * functions related with getting a character from the user/mapping/redo/...
- *
- * manipulations with redo buffer and stuff buffer
- * mappings and abbreviations
- */
+// getchar.c: Code related to getting a character from the user or a script
+// file, manipulations with redo buffer and stuff buffer.
#include <assert.h>
#include <inttypes.h>
@@ -22,18 +16,15 @@
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/edit.h"
-#include "nvim/eval.h"
#include "nvim/event/loop.h"
-#include "nvim/ex_docmd.h"
#include "nvim/ex_getln.h"
-#include "nvim/ex_session.h"
-#include "nvim/func_attr.h"
#include "nvim/garray.h"
#include "nvim/getchar.h"
#include "nvim/input.h"
-#include "nvim/keymap.h"
+#include "nvim/keycodes.h"
#include "nvim/lua/executor.h"
#include "nvim/main.h"
+#include "nvim/mapping.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
@@ -46,7 +37,6 @@
#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/plines.h"
-#include "nvim/regexp.h"
#include "nvim/screen.h"
#include "nvim/state.h"
#include "nvim/strings.h"
@@ -54,30 +44,22 @@
#include "nvim/undo.h"
#include "nvim/vim.h"
-
/// Index in scriptin
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
@@ -99,26 +81,6 @@ static int typeahead_char = 0; // typeahead char that's not flushed
*/
static int block_redo = FALSE;
-// Make a hash value for a mapping.
-// "mode" is the lower 4 bits of the State for the mapping.
-// "c1" is the first character of the "lhs".
-// Returns a value between 0 and 255, index in maphash.
-// Put Normal/Visual mode mappings mostly separately from Insert/Cmdline mode.
-#define MAP_HASH(mode, \
- c1) (((mode) & \
- (NORMAL + VISUAL + SELECTMODE + \
- OP_PENDING + TERM_FOCUS)) ? (c1) : ((c1) ^ 0x80))
-
-// Each mapping is put in one of the MAX_MAPHASH hash lists,
-// to speed up finding it.
-static mapblock_T *(maphash[MAX_MAPHASH]);
-static bool maphash_valid = false;
-
-/*
- * List used for abbreviations.
- */
-static mapblock_T *first_abbr = NULL; // first entry in abbrlist
-
static int KeyNoremap = 0; // remapping flags
/*
@@ -170,10 +132,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 +165,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 +197,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 +206,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 +254,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 +278,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];
@@ -317,7 +288,7 @@ static void add_char_buff(buffheader_T *buf, int c)
if (IS_SPECIAL(c)) {
len = 1;
} else {
- len = utf_char2bytes(c, bytes);
+ len = utf_char2bytes(c, (char *)bytes);
}
for (int i = 0; i < len; i++) {
@@ -340,12 +311,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;
@@ -397,6 +366,7 @@ static void start_stuff(void)
* Return TRUE if the stuff buffer is empty.
*/
int stuff_empty(void)
+ FUNC_ATTR_PURE
{
return (readbuf1.bh_first.b_next == NULL && readbuf2.bh_first.b_next == NULL);
}
@@ -406,6 +376,7 @@ int stuff_empty(void)
* redbuf2.
*/
int readbuf1_empty(void)
+ FUNC_ATTR_PURE
{
return (readbuf1.bh_first.b_next == NULL);
}
@@ -428,8 +399,7 @@ void flush_buffers(flush_buffers_T flush_typeahead)
init_typebuf();
start_stuff();
- while (read_readbuffers(TRUE) != NUL) {
- }
+ while (read_readbuffers(true) != NUL) {}
if (flush_typeahead == FLUSH_MINIMAL) {
// remove mapped characters at the start only
@@ -441,8 +411,7 @@ void flush_buffers(flush_buffers_T flush_typeahead)
// We have to get all characters, because we may delete the first
// part of an escape sequence. In an xterm we get one char at a
// time and we have to get them all.
- while (inchar(typebuf.tb_buf, typebuf.tb_buflen - 1, 10L) != 0) {
- }
+ while (inchar(typebuf.tb_buf, typebuf.tb_buflen - 1, 10L) != 0) {}
}
typebuf.tb_off = MAXMAPLEN;
typebuf.tb_len = 0;
@@ -492,8 +461,7 @@ void CancelRedo(void)
redobuff = old_redobuff;
old_redobuff.bh_first.b_next = NULL;
start_stuff();
- while (read_readbuffers(TRUE) != NUL) {
- }
+ while (read_readbuffers(true) != NUL) {}
}
}
@@ -524,10 +492,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,22 +502,22 @@ 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.
-void AppendToRedobuffLit(const char_u *str, int len)
+void AppendToRedobuffLit(const char *str, int len)
{
if (block_redo) {
return;
}
- const char *s = (const char *)str;
- while (len < 0 ? *s != NUL : s - (const char *)str < len) {
+ const char *s = str;
+ while (len < 0 ? *s != NUL : s - str < len) {
// Put a string of normal characters in the redo buffer (that's
// faster).
const char *start = s;
- while (*s >= ' ' && *s < DEL && (len < 0 || s - (const char *)str < len)) {
+ while (*s >= ' ' && *s < DEL && (len < 0 || s - str < len)) {
s++;
}
@@ -564,7 +530,7 @@ void AppendToRedobuffLit(const char_u *str, int len)
add_buff(&redobuff, start, (long)(s - start));
}
- if (*s == NUL || (len >= 0 && s - (const char *)str >= len)) {
+ if (*s == NUL || (len >= 0 && s - str >= len)) {
break;
}
@@ -584,10 +550,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 +569,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 +588,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) {
@@ -639,7 +599,7 @@ void stuffReadbuffSpec(const char *s)
stuffReadbuffLen(s, 3);
s += 3;
} else {
- int c = mb_ptr2char_adv((const char_u **)&s);
+ int c = mb_cptr2char_adv((const char_u **)&s);
if (c == CAR || c == NL || c == ESC) {
c = ' ';
}
@@ -648,10 +608,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 +623,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;
@@ -711,7 +669,7 @@ static int read_redo(bool init, bool old_redo)
buf[i] = (char_u)c;
if (i == n - 1) { // last byte of a character
if (n != 1) {
- c = utf_ptr2char(buf);
+ c = utf_ptr2char((char *)buf);
}
break;
}
@@ -724,9 +682,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;
@@ -813,7 +771,7 @@ int start_redo_ins(void)
// skip the count and the command character
while ((c = read_redo(false, false)) != NUL) {
- if (vim_strchr((char_u *)"AaIiRrOo", c) != NULL) {
+ if (vim_strchr("AaIiRrOo", c) != NULL) {
if (c == 'O' || c == 'o') {
add_buff(&readbuf2, NL_STR, -1L);
}
@@ -849,12 +807,10 @@ static void init_typebuf(void)
}
}
-void init_default_mappings(void)
+/// @return true when keys cannot be remapped.
+bool noremap_keys(void)
{
- add_map((char_u *)"Y y$", NORMAL, true);
- add_map((char_u *)"<C-L> <Cmd>nohlsearch<Bar>diffupdate<CR><C-L>", NORMAL, true);
- add_map((char_u *)"<C-U> <C-G>u<C-U>", INSERT, true);
- add_map((char_u *)"<C-W> <C-G>u<C-W>", INSERT, true);
+ return KeyNoremap & (RM_NONE|RM_SCRIPT);
}
// Insert a string in position 'offset' in the typeahead buffer (for "@r"
@@ -874,13 +830,11 @@ void init_default_mappings(void)
// If silent is true, cmd_silent is set when the characters are obtained.
//
// return FAIL for failure, OK otherwise
-int ins_typebuf(char_u *str, int noremap, int offset, bool nottyped, bool silent)
+int ins_typebuf(char *str, int noremap, int offset, bool nottyped, bool silent)
{
char_u *s1, *s2;
- int newlen;
int addlen;
int i;
- int newoff;
int val;
int nrm;
@@ -906,13 +860,15 @@ int ins_typebuf(char_u *str, int noremap, int offset, bool nottyped, bool silent
// In typebuf.tb_buf there must always be room for 3 * (MAXMAPLEN + 4)
// characters. We add some extra room to avoid having to allocate too
// often.
- newoff = MAXMAPLEN + 4;
- newlen = typebuf.tb_len + addlen + newoff + 4 * (MAXMAPLEN + 4);
- if (newlen < 0) { // string is getting too long
+ int newoff = MAXMAPLEN + 4;
+ int extra = addlen + newoff + 4 * (MAXMAPLEN + 4);
+ if (typebuf.tb_len > 2147483674 - extra) {
+ // string is getting too long for 32 bit int
emsg(_(e_toocompl)); // also calls flush_buffers
setcursor();
return FAIL;
}
+ int newlen = typebuf.tb_len + extra;
s1 = xmalloc((size_t)newlen);
s2 = xmalloc((size_t)newlen);
typebuf.tb_buflen = newlen;
@@ -992,36 +948,20 @@ 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 modifiers)
{
- char_u buf[MB_MAXBYTES + 1];
- if (IS_SPECIAL(c)) {
- buf[0] = K_SPECIAL;
- buf[1] = (char_u)K_SECOND(c);
- buf[2] = (char_u)K_THIRD(c);
- buf[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);
- *p++ = K_SPECIAL;
- *p++ = is_csi ? KS_EXTRA : KS_SPECIAL;
- *p++ = is_csi ? KE_CSI : KE_FILLER;
- } else {
- p++;
- }
- }
- }
- (void)ins_typebuf(buf, KeyNoremap, 0, !KeyTyped, cmd_silent);
+ char_u buf[MB_MAXBYTES * 3 + 4];
+ unsigned int len = special_to_buf(c, modifiers, true, buf);
+ assert(len < sizeof(buf));
+ buf[len] = NUL;
+ (void)ins_typebuf((char *)buf, KeyNoremap, 0, !KeyTyped, cmd_silent);
+ return (int)len;
}
/// Return TRUE if the typeahead buffer was changed (while waiting for a
@@ -1034,10 +974,10 @@ void ins_char_typebuf(int c)
///
/// @param tb_change_cnt old value of typebuf.tb_change_cnt
bool typebuf_changed(int tb_change_cnt)
+ FUNC_ATTR_PURE
{
return tb_change_cnt != 0 && (typebuf.tb_change_cnt != tb_change_cnt
- || typebuf_was_filled
- );
+ || typebuf_was_filled);
}
/*
@@ -1045,6 +985,7 @@ bool typebuf_changed(int tb_change_cnt)
* not been typed (result from a mapping or come from ":normal").
*/
int typebuf_typed(void)
+ FUNC_ATTR_PURE
{
return typebuf.tb_maplen == 0;
}
@@ -1053,6 +994,7 @@ int typebuf_typed(void)
* Return the number of characters that are mapped (or not typed).
*/
int typebuf_maplen(void)
+ FUNC_ATTR_PURE
{
return typebuf.tb_maplen;
}
@@ -1181,6 +1123,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.
@@ -1191,7 +1145,7 @@ static void gotchars(const char_u *chars, size_t len)
*/
void may_sync_undo(void)
{
- if ((!(State & (INSERT + CMDLINE)) || arrow_used)
+ if ((!(State & (MODE_INSERT | MODE_CMDLINE)) || arrow_used)
&& scriptin[curscript] == NULL) {
u_sync(false);
}
@@ -1250,7 +1204,14 @@ static int old_mod_mask; // mod_mask for ungotten character
static int old_mouse_grid; // mouse_grid related to old_char
static int old_mouse_row; // mouse_row related to old_char
static int old_mouse_col; // mouse_col related to old_char
+static int old_KeyStuffed; // whether old_char was stuffed
+static bool can_get_old_char(void)
+{
+ // If the old character was not stuffed and characters have been added to
+ // the stuff buffer, need to first get the stuffed characters instead.
+ return old_char != -1 && (old_KeyStuffed || stuff_empty());
+}
/*
* Save all three kinds of typeahead, so that the user must type at a prompt.
@@ -1338,14 +1299,12 @@ void openscript(char_u *name, bool directly)
int oldcurscript;
int save_State = State;
int save_restart_edit = restart_edit;
- int save_insertmode = p_im;
int save_finish_op = finish_op;
int save_msg_scroll = msg_scroll;
- State = NORMAL;
+ State = MODE_NORMAL;
msg_scroll = false; // no msg scrolling in Normal mode
restart_edit = 0; // don't go to Insert mode
- p_im = false; // don't use 'insertmode'
clear_oparg(&oa);
finish_op = false;
@@ -1359,7 +1318,6 @@ void openscript(char_u *name, bool directly)
State = save_State;
msg_scroll = save_msg_scroll;
restart_edit = save_restart_edit;
- p_im = save_insertmode;
finish_op = save_finish_op;
}
}
@@ -1393,6 +1351,7 @@ void close_all_scripts(void)
* Return TRUE when reading keys from a script file.
*/
int using_script(void)
+ FUNC_ATTR_PURE
{
return scriptin[curscript] != NULL;
}
@@ -1427,15 +1386,35 @@ 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".
- */
+/// Merge "modifiers" into "c_arg".
+int merge_modifiers(int c_arg, int *modifiers)
+{
+ int c = c_arg;
+
+ if (*modifiers & MOD_MASK_CTRL) {
+ if ((c >= '`' && c <= 0x7f) || (c >= '@' && c <= '_')) {
+ c &= 0x1f;
+ if (c == NUL) {
+ c = K_ZERO;
+ }
+ } else if (c == '6') {
+ // CTRL-6 is equivalent to CTRL-^
+ c = 0x1e;
+ }
+ if (c != c_arg) {
+ *modifiers &= ~MOD_MASK_CTRL;
+ }
+ }
+ return 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 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;
@@ -1453,7 +1432,7 @@ int vgetc(void)
* If a character was put back with vungetc, it was already processed.
* Return it directly.
*/
- if (old_char != -1) {
+ if (can_get_old_char()) {
c = old_char;
old_char = -1;
mod_mask = old_mod_mask;
@@ -1461,25 +1440,39 @@ int vgetc(void)
mouse_row = old_mouse_row;
mouse_col = old_mouse_col;
} else {
- mod_mask = 0x0;
- last_recorded_len = 0;
+ // number of characters recorded from the last vgetc() call
+ static size_t last_vgetc_recorded_len = 0;
+
+ mod_mask = 0;
+ vgetc_mod_mask = 0;
+ vgetc_char = 0;
+
+ // last_recorded_len can be larger than last_vgetc_recorded_len
+ // if peeking records more
+ last_recorded_len -= last_vgetc_recorded_len;
+
for (;;) { // this is done twice if there are modifiers
bool did_inc = false;
if (mod_mask) { // no mapping after modifier has been read
no_mapping++;
+ allow_keys++;
did_inc = true; // mod_mask may change value
}
c = vgetorpeek(true);
if (did_inc) {
no_mapping--;
+ allow_keys--;
}
// Get two extra bytes for special keys
if (c == K_SPECIAL) {
+ int save_allow_keys = allow_keys;
no_mapping++;
+ allow_keys = 0; // make sure BS is not found
c2 = vgetorpeek(true); // no mapping for these chars
c = vgetorpeek(true);
no_mapping--;
+ allow_keys = save_allow_keys;
if (c2 == KS_MODIFIER) {
mod_mask = c;
continue;
@@ -1552,12 +1545,16 @@ int vgetc(void)
}
break;
+ case K_KUP:
case K_XUP:
c = K_UP; break;
+ case K_KDOWN:
case K_XDOWN:
c = K_DOWN; break;
+ case K_KLEFT:
case K_XLEFT:
c = K_LEFT; break;
+ case K_KRIGHT:
case K_XRIGHT:
c = K_RIGHT; break;
}
@@ -1572,34 +1569,37 @@ 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--;
- c = utf_ptr2char(buf);
+ c = utf_ptr2char((char *)buf);
+ }
+
+ if (vgetc_char == 0) {
+ vgetc_mod_mask = mod_mask;
+ vgetc_char = c;
}
// If mappings are enabled (i.e., not Ctrl-v) and the user directly typed
// something with a meta- or alt- modifier that was not mapped, interpret
// <M-x> as <Esc>x rather than as an unbound meta keypress. #8213
// In Terminal mode, however, this is not desirable. #16220
- if (!no_mapping && KeyTyped && !(State & TERM_FOCUS)
+ if (!no_mapping && KeyTyped && !(State & MODE_TERMINAL)
&& (mod_mask == MOD_MASK_ALT || mod_mask == MOD_MASK_META)) {
mod_mask = 0;
- ins_char_typebuf(c);
- ins_char_typebuf(ESC);
+ int len = ins_char_typebuf(c, 0);
+ (void)ins_char_typebuf(ESC, 0);
+ ungetchars(len + 3); // The ALT/META modifier takes three more bytes
continue;
}
break;
}
+
+ last_vgetc_recorded_len = last_recorded_len;
}
/*
@@ -1654,7 +1654,7 @@ int plain_vgetc(void)
*/
int vpeekc(void)
{
- if (old_char != -1) {
+ if (can_get_old_char()) {
return old_char;
}
return vgetorpeek(false);
@@ -1697,11 +1697,101 @@ typedef enum {
map_result_nomatch, // no matching mapping, get char
} map_result_T;
+/// Put "string[new_slen]" in typebuf.
+/// Remove "slen" bytes.
+/// @return FAIL for error, OK otherwise.
+static int put_string_in_typebuf(int offset, int slen, char_u *string, int new_slen)
+{
+ int extra = new_slen - slen;
+ string[new_slen] = NUL;
+ if (extra < 0) {
+ // remove matched chars, taking care of noremap
+ del_typebuf(-extra, offset);
+ } else if (extra > 0) {
+ // insert the extra space we need
+ if (ins_typebuf((char *)string + slen, REMAP_YES, offset, false, false) == FAIL) {
+ return FAIL;
+ }
+ }
+ // Careful: del_typebuf() and ins_typebuf() may have reallocated
+ // typebuf.tb_buf[]!
+ memmove(typebuf.tb_buf + typebuf.tb_off + offset, string, (size_t)new_slen);
+ return OK;
+}
+
+/// Check if the bytes at the start of the typeahead buffer are a character used
+/// in Insert mode completion. This includes the form with a CTRL modifier.
+static bool at_ins_compl_key(void)
+{
+ char_u *p = typebuf.tb_buf + typebuf.tb_off;
+ int c = *p;
+
+ if (typebuf.tb_len > 3 && c == K_SPECIAL && p[1] == KS_MODIFIER && (p[2] & MOD_MASK_CTRL)) {
+ c = p[3] & 0x1f;
+ }
+ return (ctrl_x_mode_not_default() && vim_is_ctrl_x_key(c))
+ || ((compl_cont_status & CONT_LOCAL) && (c == Ctrl_N || c == Ctrl_P));
+}
+
+/// Check if typebuf.tb_buf[] contains a modifer plus key that can be changed
+/// into just a key, apply that.
+/// Check from typebuf.tb_buf[typebuf.tb_off] to typebuf.tb_buf[typebuf.tb_off + "max_offset"].
+/// @return the length of the replaced bytes, 0 if nothing changed, -1 for error.
+static int check_simplify_modifier(int max_offset)
+{
+ for (int offset = 0; offset < max_offset; offset++) {
+ if (offset + 3 >= typebuf.tb_len) {
+ break;
+ }
+ char_u *tp = typebuf.tb_buf + typebuf.tb_off + offset;
+ if (tp[0] == K_SPECIAL && tp[1] == KS_MODIFIER) {
+ // A modifier was not used for a mapping, apply it to ASCII
+ // keys. Shift would already have been applied.
+ int modifier = tp[2];
+ int c = tp[3];
+ int new_c = merge_modifiers(c, &modifier);
+
+ if (new_c != c) {
+ if (offset == 0) {
+ // At the start: remember the character and mod_mask before
+ // merging, in some cases, e.g. at the hit-return prompt,
+ // they are put back in the typeahead buffer.
+ vgetc_char = c;
+ vgetc_mod_mask = tp[2];
+ }
+ char_u new_string[MB_MAXBYTES];
+ int len;
+ if (IS_SPECIAL(new_c)) {
+ new_string[0] = K_SPECIAL;
+ new_string[1] = (char_u)K_SECOND(new_c);
+ new_string[2] = (char_u)K_THIRD(new_c);
+ len = 3;
+ } else {
+ len = utf_char2bytes(new_c, (char *)new_string);
+ }
+ if (modifier == 0) {
+ if (put_string_in_typebuf(offset, 4, new_string, len) == FAIL) {
+ return -1;
+ }
+ } else {
+ tp[2] = (char_u)modifier;
+ if (put_string_in_typebuf(offset + 3, 1, new_string, len) == FAIL) {
+ return -1;
+ }
+ }
+ return len;
+ }
+ }
+ }
+ return 0;
+}
+
/// Handle mappings in the typeahead buffer.
/// - When something was mapped, return map_result_retry for recursive mappings.
/// - When nothing mapped and typeahead has a character: return map_result_get.
/// - When there is no match yet, return map_result_nomatch, need to get more
/// typeahead.
+/// - On failure (out of memory) return map_result_fail.
static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
{
mapblock_T *mp = NULL;
@@ -1715,6 +1805,15 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
int keylen = *keylenp;
int i;
int local_State = get_real_state();
+ bool is_plug_map = false;
+
+ // If typehead starts with <Plug> then remap, even for a "noremap" mapping.
+ if (typebuf.tb_len >= 3
+ && typebuf.tb_buf[typebuf.tb_off] == K_SPECIAL
+ && typebuf.tb_buf[typebuf.tb_off + 1] == KS_EXTRA
+ && typebuf.tb_buf[typebuf.tb_off + 2] == KE_PLUG) {
+ is_plug_map = true;
+ }
// Check for a mappable key sequence.
// Walk through one maphash[] list until we find an entry that matches.
@@ -1728,27 +1827,25 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
// - waiting for a char with --more--
// - in Ctrl-X mode, and we get a valid char for that mode
tb_c1 = typebuf.tb_buf[typebuf.tb_off];
- if (no_mapping == 0 && maphash_valid
+ if (no_mapping == 0
&& (no_zero_mapping == 0 || tb_c1 != '0')
- && (typebuf.tb_maplen == 0
- || (p_remap
- && !(typebuf.tb_noremap[typebuf.tb_off] & (RM_NONE|RM_ABBR))))
- && !(p_paste && (State & (INSERT + CMDLINE)))
- && !(State == HITRETURN && (tb_c1 == CAR || tb_c1 == ' '))
- && State != ASKMORE
- && State != CONFIRM
- && !((ctrl_x_mode_not_default() && vim_is_ctrl_x_key(tb_c1))
- || ((compl_cont_status & CONT_LOCAL)
- && (tb_c1 == Ctrl_N || tb_c1 == Ctrl_P)))) {
+ && (typebuf.tb_maplen == 0 || is_plug_map
+ || (!(typebuf.tb_noremap[typebuf.tb_off] & (RM_NONE|RM_ABBR))))
+ && !(p_paste && (State & (MODE_INSERT | MODE_CMDLINE)))
+ && !(State == MODE_HITRETURN && (tb_c1 == CAR || tb_c1 == ' '))
+ && State != MODE_ASKMORE
+ && State != MODE_CONFIRM
+ && !at_ins_compl_key()) {
if (tb_c1 == K_SPECIAL) {
nolmaplen = 2;
} else {
- LANGMAP_ADJUST(tb_c1, !(State & (CMDLINE | INSERT)) && get_real_state() != SELECTMODE);
+ LANGMAP_ADJUST(tb_c1, ((State & (MODE_CMDLINE | MODE_INSERT)) == 0
+ && get_real_state() != MODE_SELECT));
nolmaplen = 0;
}
// First try buffer-local mappings.
- mp = curbuf->b_maphash[MAP_HASH(local_State, tb_c1)];
- mp2 = maphash[MAP_HASH(local_State, tb_c1)];
+ mp = get_buf_maphash_list(local_State, tb_c1);
+ mp2 = get_maphash_list(local_State, tb_c1);
if (mp == NULL) {
// There are no buffer-local mappings.
mp = mp2;
@@ -1766,7 +1863,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
// for the current state.
// Skip ":lmap" mappings if keys were mapped.
if (mp->m_keys[0] == tb_c1 && (mp->m_mode & local_State)
- && ((mp->m_mode & LANGMAP) == 0 || typebuf.tb_maplen == 0)) {
+ && ((mp->m_mode & MODE_LANGMAP) == 0 || typebuf.tb_maplen == 0)) {
int nomap = nolmaplen;
int c2;
// find the match length of this mapping
@@ -1790,7 +1887,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
char_u *p1 = mp->m_keys;
char_u *p2 = (char_u *)mb_unescape((const char **)&p1);
- if (p2 != NULL && MB_BYTE2LEN(tb_c1) > utfc_ptr2len(p2)) {
+ if (p2 != NULL && MB_BYTE2LEN(tb_c1) > utfc_ptr2len((char *)p2)) {
mlen = 0;
}
@@ -1818,7 +1915,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
break;
}
}
- if (n >= 0) {
+ if (!is_plug_map && n >= 0) {
continue;
}
@@ -1831,8 +1928,8 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
} else if (keylen > mp_match_len
|| (keylen == mp_match_len
&& mp_match != NULL
- && (mp_match->m_mode & LANGMAP) == 0
- && (mp->m_mode & LANGMAP) != 0)) {
+ && (mp_match->m_mode & MODE_LANGMAP) == 0
+ && (mp->m_mode & MODE_LANGMAP) != 0)) {
// found a longer match
mp_match = mp;
mp_match_len = keylen;
@@ -1847,14 +1944,14 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
}
// If no partly match found, use the longest full match.
- if (keylen != KEYLEN_PART_MAP) {
+ if (keylen != KEYLEN_PART_MAP && mp_match != NULL) {
mp = mp_match;
keylen = mp_match_len;
}
}
// Check for match with 'pastetoggle'
- if (*p_pt != NUL && mp == NULL && (State & (INSERT|NORMAL))) {
+ if (*p_pt != NUL && mp == NULL && (State & (MODE_INSERT | MODE_NORMAL))) {
bool match = typebuf_match_len(p_pt, &mlen);
if (match) {
// write chars to script file(s)
@@ -1865,7 +1962,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
del_typebuf(mlen, 0); // remove the chars
set_option_value("paste", !p_paste, NULL, 0);
- if (!(State & INSERT)) {
+ if (!(State & MODE_INSERT)) {
msg_col = 0;
msg_row = Rows - 1;
msg_clr_eos(); // clear ruler
@@ -1886,17 +1983,54 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
}
}
- if ((mp == NULL || max_mlen >= mp_match_len) && keylen != KEYLEN_PART_MAP) {
- // No matching mapping found or found a non-matching mapping that
- // matches at least what the matching mapping matched
- keylen = 0;
- (void)keylen; // suppress clang/dead assignment
- // If there was no mapping, use the character from the typeahead
- // buffer right here. Otherwise, use the mapping (loop around).
- if (mp == NULL) {
+ if ((mp == NULL || max_mlen > mp_match_len) && keylen != KEYLEN_PART_MAP) {
+ // When no matching mapping found or found a non-matching mapping that
+ // matches at least what the matching mapping matched:
+ // Try to include the modifier into the key when mapping is allowed.
+ if (no_mapping == 0 || allow_keys != 0) {
+ if (tb_c1 == K_SPECIAL
+ && (typebuf.tb_len < 2
+ || (typebuf.tb_buf[typebuf.tb_off + 1] == KS_MODIFIER && typebuf.tb_len < 4))) {
+ // Incomplete modifier sequence: cannot decide whether to simplify yet.
+ keylen = KEYLEN_PART_KEY;
+ } else if (keylen == KEYLEN_PART_KEY && !*timedout) {
+ // If 'pastetoggle' matched partially, don't simplify.
+ // When the last characters were not typed, don't wait for a typed character to
+ // complete 'pastetoggle'.
+ if (typebuf.tb_len == typebuf.tb_maplen) {
+ keylen = 0;
+ }
+ } else {
+ // Try to include the modifier into the key.
+ keylen = check_simplify_modifier(max_mlen + 1);
+ if (keylen < 0) {
+ // ins_typebuf() failed
+ return map_result_fail;
+ }
+ }
+ } else {
+ keylen = 0;
+ }
+ if (keylen == 0) { // no simplication has been done
+ // If there was no mapping at all use the character from the
+ // typeahead buffer right here.
+ if (mp == NULL) {
+ *keylenp = keylen;
+ return map_result_get; // get character from typeahead
+ }
+ }
+
+ if (keylen > 0) { // keys have been simplified
*keylenp = keylen;
- return map_result_get; // get character from typeahead
+ return map_result_retry; // try mapping again
+ }
+
+ if (keylen < 0) {
+ // Incomplete key sequence: get some more characters.
+ assert(keylen == KEYLEN_PART_KEY);
} else {
+ assert(mp != NULL);
+ // When a matching mapping was found use that one.
keylen = mp_match_len;
}
}
@@ -1904,13 +2038,10 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
// complete match
if (keylen >= 0 && keylen <= typebuf.tb_len) {
char_u *map_str = NULL;
- int save_m_expr;
- int save_m_noremap;
- int save_m_silent;
// Write chars to script file(s).
// Note: :lmap mappings are written *after* being applied. #5658
- if (keylen > typebuf.tb_maplen && (mp->m_mode & LANGMAP) == 0) {
+ if (keylen > typebuf.tb_maplen && (mp->m_mode & MODE_LANGMAP) == 0) {
gotchars(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_maplen,
(size_t)(keylen - typebuf.tb_maplen));
}
@@ -1922,7 +2053,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
// The depth check catches ":map x y" and ":map y x".
if (++*mapdepth >= p_mmd) {
emsg(_("E223: recursive mapping"));
- if (State & CMDLINE) {
+ if (State & MODE_CMDLINE) {
redrawcmdline();
} else {
setcursor();
@@ -1935,17 +2066,17 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
// In Select mode and a Visual mode mapping is used: Switch to Visual
// mode temporarily. Append K_SELECT to switch back to Select mode.
- if (VIsual_active && VIsual_select && (mp->m_mode & VISUAL)) {
+ if (VIsual_active && VIsual_select && (mp->m_mode & MODE_VISUAL)) {
VIsual_select = false;
- (void)ins_typebuf(K_SELECT_STRING, REMAP_NONE, 0, true, false);
+ (void)ins_typebuf((char *)K_SELECT_STRING, REMAP_NONE, 0, true, false);
}
// Copy the values from *mp that are used, because evaluating the
// expression may invoke a function that redefines the mapping, thereby
// making *mp invalid.
- save_m_expr = mp->m_expr;
- save_m_noremap = mp->m_noremap;
- save_m_silent = mp->m_silent;
+ char save_m_expr = mp->m_expr;
+ int save_m_noremap = mp->m_noremap;
+ char save_m_silent = mp->m_silent;
char_u *save_m_keys = NULL; // only saved when needed
char_u *save_m_str = NULL; // only saved when needed
LuaRef save_m_luaref = mp->m_luaref;
@@ -1954,8 +2085,12 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
// expression. Also save and restore the command line
// for "normal :".
if (mp->m_expr) {
- int save_vgetc_busy = vgetc_busy;
+ const int save_vgetc_busy = vgetc_busy;
const bool save_may_garbage_collect = may_garbage_collect;
+ const int save_cursor_row = ui_current_row();
+ const int save_cursor_col = ui_current_col();
+ const handle_T save_cursor_grid = ui_cursor_grid();
+ const int prev_did_emsg = did_emsg;
vgetc_busy = 0;
may_garbage_collect = false;
@@ -1965,6 +2100,32 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
save_m_str = vim_strsave(mp->m_str);
}
map_str = eval_map_expr(mp, NUL);
+
+ // The mapping may do anything, but we expect it to take care of
+ // redrawing. Do put the cursor back where it was.
+ ui_grid_cursor_goto(save_cursor_grid, save_cursor_row, save_cursor_col);
+ ui_flush();
+
+ // If an error was displayed and the expression returns an empty
+ // string, generate a <Nop> to allow for a redraw.
+ if (prev_did_emsg != did_emsg && (map_str == NULL || *map_str == NUL)) {
+ char_u buf[4];
+ xfree(map_str);
+ buf[0] = K_SPECIAL;
+ buf[1] = KS_EXTRA;
+ buf[2] = KE_IGNORE;
+ buf[3] = NUL;
+ map_str = vim_strsave(buf);
+ if (State & MODE_CMDLINE) {
+ // redraw the command below the error
+ msg_didout = true;
+ if (msg_row < cmdline_row) {
+ msg_row = cmdline_row;
+ }
+ redrawcmd();
+ }
+ }
+
vgetc_busy = save_vgetc_busy;
may_garbage_collect = save_may_garbage_collect;
} else {
@@ -1982,7 +2143,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
// If this is a LANGMAP mapping, then we didn't record the keys
// at the start of the function and have to record them now.
- if (keylen > typebuf.tb_maplen && (mp->m_mode & LANGMAP) != 0) {
+ if (keylen > typebuf.tb_maplen && (mp->m_mode & MODE_LANGMAP) != 0) {
gotchars(map_str, STRLEN(map_str));
}
@@ -1994,7 +2155,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
} else {
noremap = REMAP_SKIP;
}
- i = ins_typebuf(map_str, noremap, 0, true, cmd_silent || save_m_silent);
+ i = ins_typebuf((char *)map_str, noremap, 0, true, cmd_silent || save_m_silent);
if (save_m_expr) {
xfree(map_str);
}
@@ -2012,7 +2173,9 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
return map_result_nomatch;
}
-// unget one character (can only be done once!)
+/// unget one character (can only be done once!)
+/// If the character was stuffed, vgetc() will get it next time it is called.
+/// Otherwise vgetc() will only get it when the stuff buffer is empty.
void vungetc(int c)
{
old_char = c;
@@ -2020,6 +2183,21 @@ void vungetc(int c)
old_mouse_grid = mouse_grid;
old_mouse_row = mouse_row;
old_mouse_col = mouse_col;
+ old_KeyStuffed = KeyStuffed;
+}
+
+/// When peeking and not getting a character, reg_executing cannot be cleared
+/// yet, so set a flag to clear it later.
+void check_end_reg_executing(bool advance)
+{
+ if (reg_executing != 0 && (typebuf.tb_maplen == 0 || pending_end_reg_executing)) {
+ if (advance) {
+ reg_executing = 0;
+ pending_end_reg_executing = false;
+ } else {
+ pending_end_reg_executing = true;
+ }
+ }
}
/// Gets a byte:
@@ -2043,7 +2221,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;
@@ -2076,9 +2254,7 @@ static int vgetorpeek(bool advance)
init_typebuf();
start_stuff();
- if (advance && typebuf.tb_maplen == 0) {
- reg_executing = 0;
- }
+ check_end_reg_executing(advance);
do {
// get a character: 1. from the stuffbuffer
if (typeahead_char != 0) {
@@ -2105,13 +2281,19 @@ static int vgetorpeek(bool advance)
// If a mapped key sequence is found we go back to the start to
// try re-mapping.
for (;;) {
+ check_end_reg_executing(advance);
// os_breakcheck() is slow, don't use it too often when
// inside a mapping. But call it each time for typed
// characters.
if (typebuf.tb_maplen) {
line_breakcheck();
} else {
+ // os_breakcheck() can call input_enqueue()
+ if ((mapped_ctrl_c | curbuf->b_mapped_ctrl_c) & get_real_state()) {
+ ctrl_c_interrupts = false;
+ }
os_breakcheck(); // check for CTRL-C
+ ctrl_c_interrupts = true;
}
int keylen = 0;
if (got_int) {
@@ -2124,7 +2306,7 @@ static int vgetorpeek(bool advance)
// As a result typing CTRL-C in insert mode will
// really insert a CTRL-C.
if ((c || typebuf.tb_maplen)
- && (State & (INSERT + CMDLINE))) {
+ && (State & (MODE_INSERT | MODE_CMDLINE))) {
c = ESC;
} else {
c = Ctrl_C;
@@ -2193,7 +2375,7 @@ static int vgetorpeek(bool advance)
&& !no_mapping
&& ex_normal_busy == 0
&& typebuf.tb_maplen == 0
- && (State & INSERT)
+ && (State & MODE_INSERT)
&& (p_timeout || (keylen == KEYLEN_PART_KEY && p_ttimeout))
&& (c = inchar(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_len, 3, 25L)) == 0) {
colnr_T col = 0, vcol;
@@ -2221,7 +2403,7 @@ static int vgetorpeek(bool advance)
curwin->w_wcol = vcol;
}
vcol += lbr_chartabsize(ptr, ptr + col, vcol);
- col += utfc_ptr2len(ptr + col);
+ col += utfc_ptr2len((char *)ptr + col);
}
curwin->w_wrow = curwin->w_cline_row
+ curwin->w_wcol / curwin->w_width_inner;
@@ -2242,7 +2424,7 @@ static int vgetorpeek(bool advance)
// of a double-wide character.
ptr = get_cursor_line_ptr();
col -= utf_head_off(ptr, ptr + col);
- if (utf_ptr2cells(ptr + col) > 1) {
+ if (utf_ptr2cells((char *)ptr + col) > 1) {
curwin->w_wcol--;
}
}
@@ -2281,23 +2463,26 @@ static int vgetorpeek(bool advance)
timedout = true;
continue;
}
- // When 'insertmode' is set, ESC just beeps in Insert
- // mode. Use CTRL-L to make edit() return.
// In Ex-mode \n is compatible with original Vim behaviour.
// For the command line only CTRL-C always breaks it.
// For the cmdline window: Alternate between ESC and
// CTRL-C: ESC for most situations and CTRL-C to close the
// cmdline window.
- if (p_im && (State & INSERT)) {
- c = Ctrl_L;
- } else if (exmode_active) {
- c = '\n';
- } else if ((State & CMDLINE) || (cmdwin_type > 0 && tc == ESC)) {
+ if ((State & MODE_CMDLINE) || (cmdwin_type > 0 && tc == ESC)) {
c = Ctrl_C;
} else {
c = ESC;
}
tc = c;
+
+ // return 0 in normal_check()
+ if (pending_exmode_active) {
+ exmode_active = true;
+ }
+
+ // no chars to block abbreviations for
+ typebuf.tb_no_abbr_cnt = 0;
+
break;
}
@@ -2310,7 +2495,7 @@ static int vgetorpeek(bool advance)
// changed text so far. Also for when 'lazyredraw' is set and
// redrawing was postponed because there was something in the
// input buffer (e.g., termresponse).
- if (((State & INSERT) != 0 || p_lz) && (State & CMDLINE) == 0
+ if (((State & MODE_INSERT) != 0 || p_lz) && (State & MODE_CMDLINE) == 0
&& advance && must_redraw != 0 && !need_wait_return) {
update_screen(0);
setcursor(); // put cursor back where it belongs
@@ -2322,10 +2507,11 @@ static int vgetorpeek(bool advance)
int showcmd_idx = 0;
c1 = 0;
if (typebuf.tb_len > 0 && advance && !exmode_active) {
- if (((State & (NORMAL | INSERT)) || State == LANGMAP) && State != HITRETURN) {
+ if (((State & (MODE_NORMAL | MODE_INSERT)) || State == MODE_LANGMAP)
+ && State != MODE_HITRETURN) {
// this looks nice when typing a dead character map
- if (State & INSERT
- && ptr2cells(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_len - 1) == 1) {
+ if (State & MODE_INSERT
+ && ptr2cells((char *)typebuf.tb_buf + typebuf.tb_off + typebuf.tb_len - 1) == 1) {
edit_putchar(typebuf.tb_buf[typebuf.tb_off + typebuf.tb_len - 1], false);
setcursor(); // put cursor back where it belongs
c1 = 1;
@@ -2347,10 +2533,10 @@ static int vgetorpeek(bool advance)
}
// this looks nice when typing a dead character map
- if ((State & CMDLINE) && cmdline_star == 0) {
- char_u *p = typebuf.tb_buf + typebuf.tb_off + typebuf.tb_len - 1;
- if (ptr2cells(p) == 1 && *p < 128) {
- putcmdline((char)(*p), false);
+ if ((State & MODE_CMDLINE) && cmdline_star == 0) {
+ char *p = (char *)typebuf.tb_buf + typebuf.tb_off + typebuf.tb_len - 1;
+ if (ptr2cells(p) == 1 && (uint8_t)(*p) < 128) {
+ putcmdline(*p, false);
c1 = 1;
}
}
@@ -2358,8 +2544,8 @@ static int vgetorpeek(bool advance)
// get a character: 3. from the user - get it
if (typebuf.tb_len == 0) {
- // timedout may have been set while waiting for a mapping
- // that has a <Nop> RHS.
+ // timedout may have been set if a mapping with empty RHS
+ // fully matched while longer mappings timed out.
timedout = false;
}
@@ -2385,10 +2571,10 @@ static int vgetorpeek(bool advance)
pop_showcmd();
}
if (c1 == 1) {
- if (State & INSERT) {
+ if (State & MODE_INSERT) {
edit_unputchar();
}
- if (State & CMDLINE) {
+ if (State & MODE_CMDLINE) {
unputcmdline();
} else {
setcursor(); // put cursor back where it belongs
@@ -2420,7 +2606,7 @@ static int vgetorpeek(bool advance)
// The "INSERT" message is taken care of here:
// if we return an ESC to exit insert mode, the message is deleted
// if we don't return an ESC but deleted the message before, redisplay it
- if (advance && p_smd && msg_silent == 0 && (State & INSERT)) {
+ if (advance && p_smd && msg_silent == 0 && (State & MODE_INSERT)) {
if (c == ESC && !mode_deleted && !no_mapping && mode_displayed) {
if (typebuf.tb_len && !KeyTyped) {
redraw_cmdline = true; // delete mode later
@@ -2456,7 +2642,7 @@ static int vgetorpeek(bool advance)
/// 1. a scriptfile
/// 2. the keyboard
///
-/// As much characters as we can get (up to 'maxlen') are put in "buf" and
+/// As many characters as we can get (up to 'maxlen') are put in "buf" and
/// NUL terminated (buffer length must be 'maxlen' + 1).
/// Minimum for "maxlen" is 3!!!!
///
@@ -2491,7 +2677,7 @@ int inchar(char_u *buf, int maxlen, long wait_time)
* recursive loop may result (write error in swapfile, hit-return, timeout
* on char wait, flush swapfile, write error....).
*/
- if (State != HITRETURN) {
+ if (State != MODE_HITRETURN) {
did_outofmem_msg = false; // display out of memory message (again)
did_swapwrite_msg = false; // display swap file write error again
}
@@ -2529,12 +2715,12 @@ int inchar(char_u *buf, int maxlen, long wait_time)
// Don't use buf[] here, closescript() may have freed typebuf.tb_buf[]
// and buf may be pointing inside typebuf.tb_buf[].
if (got_int) {
-#define DUM_LEN MAXMAPLEN * 3 + 3
+#define DUM_LEN (MAXMAPLEN * 3 + 3)
char_u dum[DUM_LEN + 1];
for (;;) {
len = os_inchar(dum, DUM_LEN, 0L, 0, NULL);
- if (len == 0 || (len == 1 && dum[0] == 3)) {
+ if (len == 0 || (len == 1 && dum[0] == Ctrl_C)) {
break;
}
}
@@ -2573,7 +2759,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 +2770,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
@@ -2603,1930 +2788,6 @@ int fix_input_buffer(char_u *buf, int len)
return len;
}
-/// Replace termcodes in the given LHS and RHS and store the results into the
-/// `lhs` and `rhs` of the given @ref MapArguments struct.
-///
-/// `rhs` and `orig_rhs` will both point to new allocated buffers. `orig_rhs`
-/// will hold a copy of the given `orig_rhs`.
-///
-/// The `*_len` variables will be set appropriately. If the length of
-/// the final `lhs` exceeds `MAXMAPLEN`, `lhs_len` will be set equal to the
-/// original larger length and `lhs` will be truncated.
-///
-/// If RHS is equal to "<Nop>", `rhs` will be the empty string, `rhs_len`
-/// will be zero, and `rhs_is_noop` will be set to true.
-///
-/// Any memory allocated by @ref replace_termcodes is freed before this function
-/// returns.
-///
-/// @param[in] orig_lhs Original mapping LHS, with characters to replace.
-/// @param[in] orig_lhs_len `strlen` of orig_lhs.
-/// @param[in] orig_rhs Original mapping RHS, with characters to replace.
-/// @param[in] rhs_lua Lua reference for Lua maps.
-/// @param[in] orig_rhs_len `strlen` of orig_rhs.
-/// @param[in] cpo_flags See param docs for @ref replace_termcodes.
-/// @param[out] mapargs MapArguments struct holding the replaced strings.
-void set_maparg_lhs_rhs(const char_u *orig_lhs, const size_t orig_lhs_len,
- const char_u *orig_rhs, const size_t orig_rhs_len,
- LuaRef rhs_lua, int cpo_flags, MapArguments *mapargs)
-{
- char_u *lhs_buf = NULL;
- char_u *rhs_buf = NULL;
-
- // If mapping has been given as ^V<C_UP> say, then replace the term codes
- // with the appropriate two bytes. If it is a shifted special key, unshift
- // it too, giving another two bytes.
- //
- // replace_termcodes() may move the result to allocated memory, which
- // needs to be freed later (*lhs_buf and *rhs_buf).
- // replace_termcodes() also removes CTRL-Vs and sometimes backslashes.
- char_u *replaced = replace_termcodes(orig_lhs, orig_lhs_len, &lhs_buf,
- true, true, true, cpo_flags);
- mapargs->lhs_len = STRLEN(replaced);
- STRLCPY(mapargs->lhs, replaced, sizeof(mapargs->lhs));
- mapargs->rhs_lua = rhs_lua;
-
- if (rhs_lua == LUA_NOREF) {
- mapargs->orig_rhs_len = orig_rhs_len;
- mapargs->orig_rhs = xcalloc(mapargs->orig_rhs_len + 1, sizeof(char_u));
- STRLCPY(mapargs->orig_rhs, orig_rhs, mapargs->orig_rhs_len + 1);
-
- if (STRICMP(orig_rhs, "<nop>") == 0) { // "<Nop>" means nothing
- mapargs->rhs = xcalloc(1, sizeof(char_u)); // single null-char
- mapargs->rhs_len = 0;
- mapargs->rhs_is_noop = true;
- } else {
- replaced = replace_termcodes(orig_rhs, orig_rhs_len, &rhs_buf,
- false, true, true, cpo_flags);
- mapargs->rhs_len = STRLEN(replaced);
- mapargs->rhs_is_noop = false;
- mapargs->rhs = xcalloc(mapargs->rhs_len + 1, sizeof(char_u));
- STRLCPY(mapargs->rhs, replaced, mapargs->rhs_len + 1);
- }
- } else {
- char tmp_buf[64];
- // stores <lua>ref_no<cr> in map_str
- mapargs->orig_rhs_len = (size_t)vim_snprintf(S_LEN(tmp_buf), "<LUA>%d<CR>", rhs_lua);
- mapargs->orig_rhs = vim_strsave((char_u *)tmp_buf);
- mapargs->rhs_len = (size_t)vim_snprintf(S_LEN(tmp_buf), "%c%c%c%d\r", K_SPECIAL,
- (char_u)KEY2TERMCAP0(K_LUA), KEY2TERMCAP1(K_LUA),
- rhs_lua);
- mapargs->rhs = vim_strsave((char_u *)tmp_buf);
- }
-
- xfree(lhs_buf);
- xfree(rhs_buf);
-}
-
-/// Parse a string of |:map-arguments| into a @ref MapArguments struct.
-///
-/// Termcodes, backslashes, CTRL-V's, etc. inside the extracted {lhs} and
-/// {rhs} are replaced by @ref set_maparg_lhs_rhs.
-///
-/// rhs and orig_rhs in the returned mapargs will be set to null or a pointer
-/// to allocated memory and should be freed even on error.
-///
-/// @param[in] strargs String of map args, e.g. "<buffer> <expr><silent>".
-/// May contain leading or trailing whitespace.
-/// @param[in] is_unmap True, if strargs should be parsed like an |:unmap|
-/// command. |:unmap| commands interpret *all* text to the
-/// right of the last map argument as the {lhs} of the
-/// mapping, i.e. a literal ' ' character is treated like
-/// a "<space>", rather than separating the {lhs} from the
-/// {rhs}.
-/// @param[out] mapargs MapArguments struct holding all extracted argument
-/// values.
-/// @return 0 on success, 1 if invalid arguments are detected.
-int str_to_mapargs(const char_u *strargs, bool is_unmap, MapArguments *mapargs)
-{
- const char_u *to_parse = strargs;
- to_parse = skipwhite(to_parse);
- MapArguments parsed_args; // copy these into mapargs "all at once" when done
- memset(&parsed_args, 0, sizeof(parsed_args));
-
- // Accept <buffer>, <nowait>, <silent>, <expr>, <script>, and <unique> in
- // any order.
- while (true) {
- if (STRNCMP(to_parse, "<buffer>", 8) == 0) {
- to_parse = skipwhite(to_parse + 8);
- parsed_args.buffer = true;
- continue;
- }
-
- if (STRNCMP(to_parse, "<nowait>", 8) == 0) {
- to_parse = skipwhite(to_parse + 8);
- parsed_args.nowait = true;
- continue;
- }
-
- if (STRNCMP(to_parse, "<silent>", 8) == 0) {
- to_parse = skipwhite(to_parse + 8);
- parsed_args.silent = true;
- continue;
- }
-
- // Ignore obsolete "<special>" modifier.
- if (STRNCMP(to_parse, "<special>", 9) == 0) {
- to_parse = skipwhite(to_parse + 9);
- continue;
- }
-
- if (STRNCMP(to_parse, "<script>", 8) == 0) {
- to_parse = skipwhite(to_parse + 8);
- parsed_args.script = true;
- continue;
- }
-
- if (STRNCMP(to_parse, "<expr>", 6) == 0) {
- to_parse = skipwhite(to_parse + 6);
- parsed_args.expr = true;
- continue;
- }
-
- if (STRNCMP(to_parse, "<unique>", 8) == 0) {
- to_parse = skipwhite(to_parse + 8);
- parsed_args.unique = true;
- continue;
- }
- break;
- }
-
- // Find the next whitespace character, call that the end of {lhs}.
- //
- // If a character (e.g. whitespace) is immediately preceded by a CTRL-V,
- // "scan past" that character, i.e. don't "terminate" LHS with that character
- // if it's whitespace.
- //
- // Treat backslash like CTRL-V when 'cpoptions' does not contain 'B'.
- //
- // With :unmap, literal white space is included in the {lhs}; there is no
- // separate {rhs}.
- const char_u *lhs_end = to_parse;
- bool do_backslash = (vim_strchr(p_cpo, CPO_BSLASH) == NULL);
- while (*lhs_end && (is_unmap || !ascii_iswhite(*lhs_end))) {
- if ((lhs_end[0] == Ctrl_V || (do_backslash && lhs_end[0] == '\\'))
- && lhs_end[1] != NUL) {
- lhs_end++; // skip CTRL-V or backslash
- }
- lhs_end++;
- }
-
- // {lhs_end} is a pointer to the "terminating whitespace" after {lhs}.
- // Use that to initialize {rhs_start}.
- const char_u *rhs_start = skipwhite(lhs_end);
-
- // Given {lhs} might be larger than MAXMAPLEN before replace_termcodes
- // (e.g. "<Space>" is longer than ' '), so first copy into a buffer.
- size_t orig_lhs_len = (size_t)(lhs_end - to_parse);
- char_u *lhs_to_replace = xcalloc(orig_lhs_len + 1, sizeof(char_u));
- STRLCPY(lhs_to_replace, to_parse, orig_lhs_len + 1);
-
- size_t orig_rhs_len = STRLEN(rhs_start);
- set_maparg_lhs_rhs(lhs_to_replace, orig_lhs_len,
- rhs_start, orig_rhs_len, LUA_NOREF,
- CPO_TO_CPO_FLAGS, &parsed_args);
-
- xfree(lhs_to_replace);
-
- *mapargs = parsed_args;
-
- if (parsed_args.lhs_len > MAXMAPLEN) {
- return 1;
- }
- return 0;
-}
-
-/// Sets or removes a mapping or abbreviation in buffer `buf`.
-///
-/// @param maptype @see do_map
-/// @param args Fully parsed and "preprocessed" arguments for the
-/// (un)map/abbrev command. Termcodes should have already been
-/// replaced; whitespace, `<` and `>` signs, etc. in {lhs} and
-/// {rhs} are assumed to be literal components of the mapping.
-/// @param mode @see do_map
-/// @param is_abbrev @see do_map
-/// @param buf Target Buffer
-int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, buf_T *buf)
-{
- mapblock_T *mp, **mpp;
- char_u *p;
- int n;
- int len = 0; // init for GCC
- int did_it = false;
- int did_local = false;
- int round;
- int retval = 0;
- int hash;
- int new_hash;
- mapblock_T **abbr_table;
- mapblock_T **map_table;
- int noremap;
-
- map_table = maphash;
- abbr_table = &first_abbr;
-
- // For ":noremap" don't remap, otherwise do remap.
- if (maptype == 2) {
- noremap = REMAP_NONE;
- } else {
- noremap = REMAP_YES;
- }
-
- if (args->buffer) {
- // If <buffer> was given, we'll be searching through the buffer's
- // mappings/abbreviations, not the globals.
- map_table = buf->b_maphash;
- abbr_table = &buf->b_first_abbr;
- }
- if (args->script) {
- noremap = REMAP_SCRIPT;
- }
-
- validate_maphash();
-
- bool has_lhs = (args->lhs[0] != NUL);
- bool has_rhs = args->rhs_lua != LUA_NOREF || (args->rhs[0] != NUL) || args->rhs_is_noop;
-
- // check for :unmap without argument
- if (maptype == 1 && !has_lhs) {
- retval = 1;
- goto theend;
- }
-
- char_u *lhs = (char_u *)&args->lhs;
- char_u *rhs = args->rhs;
- char_u *orig_rhs = args->orig_rhs;
-
- // check arguments and translate function keys
- if (has_lhs) {
- len = (int)args->lhs_len;
- if (len > MAXMAPLEN) {
- retval = 1;
- goto theend;
- }
-
- if (is_abbrev && maptype != 1) {
- //
- // If an abbreviation ends in a keyword character, the
- // rest must be all keyword-char or all non-keyword-char.
- // Otherwise we won't be able to find the start of it in a
- // vi-compatible way.
- //
- int same = -1;
-
- const int first = vim_iswordp(lhs);
- int last = first;
- p = lhs + utfc_ptr2len(lhs);
- n = 1;
- while (p < lhs + len) {
- n++; // nr of (multi-byte) chars
- last = vim_iswordp(p); // type of last char
- if (same == -1 && last != first) {
- same = n - 1; // count of same char type
- }
- p += utfc_ptr2len(p);
- }
- if (last && n > 2 && same >= 0 && same < n - 1) {
- retval = 1;
- goto theend;
- }
- // An abbreviation cannot contain white space.
- for (n = 0; n < len; n++) {
- if (ascii_iswhite(lhs[n])) {
- retval = 1;
- goto theend;
- }
- } // for
- }
- }
-
- if (has_lhs && has_rhs && is_abbrev) { // if we will add an abbreviation,
- no_abbr = false; // reset flag that indicates there are no abbreviations
- }
-
- if (!has_lhs || (maptype != 1 && !has_rhs)) {
- msg_start();
- }
-
- // Check if a new local mapping wasn't already defined globally.
- if (map_table == buf->b_maphash && has_lhs && has_rhs && maptype != 1) {
- // need to loop over all global hash lists
- for (hash = 0; hash < 256 && !got_int; hash++) {
- if (is_abbrev) {
- if (hash != 0) { // there is only one abbreviation list
- break;
- }
- mp = first_abbr;
- } else {
- mp = maphash[hash];
- }
- for (; mp != NULL && !got_int; mp = mp->m_next) {
- // check entries with the same mode
- if ((mp->m_mode & mode) != 0
- && mp->m_keylen == len
- && args->unique
- && STRNCMP(mp->m_keys, lhs, (size_t)len) == 0) {
- if (is_abbrev) {
- semsg(_("E224: global abbreviation already exists for %s"),
- mp->m_keys);
- } else {
- semsg(_("E225: global mapping already exists for %s"), mp->m_keys);
- }
- retval = 5;
- goto theend;
- }
- }
- }
- }
-
- // When listing global mappings, also list buffer-local ones here.
- if (map_table != buf->b_maphash && !has_rhs && maptype != 1) {
- // need to loop over all global hash lists
- for (hash = 0; hash < 256 && !got_int; hash++) {
- if (is_abbrev) {
- if (hash != 0) { // there is only one abbreviation list
- break;
- }
- mp = buf->b_first_abbr;
- } else {
- mp = buf->b_maphash[hash];
- }
- for (; mp != NULL && !got_int; mp = mp->m_next) {
- // check entries with the same mode
- if ((mp->m_mode & mode) != 0) {
- if (!has_lhs) { // show all entries
- showmap(mp, true);
- did_local = true;
- } else {
- n = mp->m_keylen;
- if (STRNCMP(mp->m_keys, lhs, (size_t)(n < len ? n : len)) == 0) {
- showmap(mp, true);
- did_local = true;
- }
- }
- }
- }
- }
- }
-
- // Find an entry in the maphash[] list that matches.
- // For :unmap we may loop two times: once to try to unmap an entry with a
- // matching 'from' part, a second time, if the first fails, to unmap an
- // entry with a matching 'to' part. This was done to allow ":ab foo bar"
- // to be unmapped by typing ":unab foo", where "foo" will be replaced by
- // "bar" because of the abbreviation.
- for (round = 0; (round == 0 || maptype == 1) && round <= 1
- && !did_it && !got_int; round++) {
- // need to loop over all hash lists
- for (hash = 0; hash < 256 && !got_int; hash++) {
- if (is_abbrev) {
- if (hash > 0) { // there is only one abbreviation list
- break;
- }
- mpp = abbr_table;
- } else {
- mpp = &(map_table[hash]);
- }
- for (mp = *mpp; mp != NULL && !got_int; mp = *mpp) {
- if (!(mp->m_mode & mode)) { // skip entries with wrong mode
- mpp = &(mp->m_next);
- continue;
- }
- if (!has_lhs) { // show all entries
- showmap(mp, map_table != maphash);
- did_it = true;
- } else { // do we have a match?
- if (round) { // second round: Try unmap "rhs" string
- n = (int)STRLEN(mp->m_str);
- p = mp->m_str;
- } else {
- n = mp->m_keylen;
- p = mp->m_keys;
- }
- if (STRNCMP(p, lhs, (size_t)(n < len ? n : len)) == 0) {
- if (maptype == 1) { // delete entry
- // Only accept a full match. For abbreviations we
- // ignore trailing space when matching with the
- // "lhs", since an abbreviation can't have
- // trailing space.
- if (n != len && (!is_abbrev || round || n > len
- || *skipwhite(lhs + n) != NUL)) {
- mpp = &(mp->m_next);
- continue;
- }
- // We reset the indicated mode bits. If nothing is
- // left the entry is deleted below.
- mp->m_mode &= ~mode;
- did_it = true; // remember we did something
- } else if (!has_rhs) { // show matching entry
- showmap(mp, map_table != maphash);
- did_it = true;
- } else if (n != len) { // new entry is ambiguous
- mpp = &(mp->m_next);
- continue;
- } else if (args->unique) {
- if (is_abbrev) {
- semsg(_("E226: abbreviation already exists for %s"), p);
- } else {
- semsg(_("E227: mapping already exists for %s"), p);
- }
- retval = 5;
- goto theend;
- } else { // new rhs for existing entry
- mp->m_mode &= ~mode; // remove mode bits
- if (mp->m_mode == 0 && !did_it) { // reuse entry
- XFREE_CLEAR(mp->m_str);
- XFREE_CLEAR(mp->m_orig_str);
- XFREE_CLEAR(mp->m_desc);
- NLUA_CLEAR_REF(mp->m_luaref);
-
- mp->m_str = vim_strsave(rhs);
- mp->m_orig_str = vim_strsave(orig_rhs);
- mp->m_luaref = args->rhs_lua;
- mp->m_noremap = noremap;
- mp->m_nowait = args->nowait;
- mp->m_silent = args->silent;
- mp->m_mode = mode;
- mp->m_expr = args->expr;
- mp->m_script_ctx = current_sctx;
- mp->m_script_ctx.sc_lnum += sourcing_lnum;
- if (args->desc != NULL) {
- mp->m_desc = xstrdup(args->desc);
- }
- did_it = true;
- }
- }
- if (mp->m_mode == 0) { // entry can be deleted
- mapblock_free(mpp);
- continue; // continue with *mpp
- }
-
- // May need to put this entry into another hash list.
- new_hash = MAP_HASH(mp->m_mode, mp->m_keys[0]);
- if (!is_abbrev && new_hash != hash) {
- *mpp = mp->m_next;
- mp->m_next = map_table[new_hash];
- map_table[new_hash] = mp;
-
- continue; // continue with *mpp
- }
- }
- }
- mpp = &(mp->m_next);
- }
- }
- }
-
- if (maptype == 1) { // delete entry
- if (!did_it) {
- retval = 2; // no match
- } else if (*lhs == Ctrl_C) {
- // If CTRL-C has been unmapped, reuse it for Interrupting.
- if (map_table == buf->b_maphash) {
- buf->b_mapped_ctrl_c &= ~mode;
- } else {
- mapped_ctrl_c &= ~mode;
- }
- }
- goto theend;
- }
-
- if (!has_lhs || !has_rhs) { // print entries
- if (!did_it && !did_local) {
- if (is_abbrev) {
- msg(_("No abbreviation found"));
- } else {
- msg(_("No mapping found"));
- }
- }
- goto theend; // listing finished
- }
-
- if (did_it) { // have added the new entry already
- goto theend;
- }
-
- // Get here when adding a new entry to the maphash[] list or abbrlist.
- mp = xmalloc(sizeof(mapblock_T));
-
- // If CTRL-C has been mapped, don't always use it for Interrupting.
- if (*lhs == Ctrl_C) {
- if (map_table == buf->b_maphash) {
- buf->b_mapped_ctrl_c |= mode;
- } else {
- mapped_ctrl_c |= mode;
- }
- }
-
- mp->m_keys = vim_strsave(lhs);
- mp->m_str = vim_strsave(rhs);
- mp->m_orig_str = vim_strsave(orig_rhs);
- mp->m_luaref = args->rhs_lua;
- mp->m_keylen = (int)STRLEN(mp->m_keys);
- mp->m_noremap = noremap;
- mp->m_nowait = args->nowait;
- mp->m_silent = args->silent;
- mp->m_mode = mode;
- mp->m_expr = args->expr;
- mp->m_script_ctx = current_sctx;
- mp->m_script_ctx.sc_lnum += sourcing_lnum;
- mp->m_desc = NULL;
- if (args->desc != NULL) {
- mp->m_desc = xstrdup(args->desc);
- }
-
- // add the new entry in front of the abbrlist or maphash[] list
- if (is_abbrev) {
- mp->m_next = *abbr_table;
- *abbr_table = mp;
- } else {
- n = MAP_HASH(mp->m_mode, mp->m_keys[0]);
- mp->m_next = map_table[n];
- map_table[n] = mp;
- }
-
-theend:
- return retval;
-}
-
-
-/// Set or remove a mapping or an abbreviation in the current buffer, OR
-/// display (matching) mappings/abbreviations.
-///
-/// ```vim
-/// map[!] " show all key mappings
-/// map[!] {lhs} " show key mapping for {lhs}
-/// map[!] {lhs} {rhs} " set key mapping for {lhs} to {rhs}
-/// noremap[!] {lhs} {rhs} " same, but no remapping for {rhs}
-/// unmap[!] {lhs} " remove key mapping for {lhs}
-/// abbr " show all abbreviations
-/// abbr {lhs} " show abbreviations for {lhs}
-/// abbr {lhs} {rhs} " set abbreviation for {lhs} to {rhs}
-/// noreabbr {lhs} {rhs} " same, but no remapping for {rhs}
-/// unabbr {lhs} " remove abbreviation for {lhs}
-///
-/// for :map mode is NORMAL + VISUAL + SELECTMODE + OP_PENDING
-/// for :map! mode is INSERT + CMDLINE
-/// for :cmap mode is CMDLINE
-/// for :imap mode is INSERT
-/// for :lmap mode is LANGMAP
-/// for :nmap mode is NORMAL
-/// for :vmap mode is VISUAL + SELECTMODE
-/// for :xmap mode is VISUAL
-/// for :smap mode is SELECTMODE
-/// for :omap mode is OP_PENDING
-/// for :tmap mode is TERM_FOCUS
-///
-/// for :abbr mode is INSERT + CMDLINE
-/// for :iabbr mode is INSERT
-/// for :cabbr mode is CMDLINE
-/// ```
-///
-/// @param maptype 0 for |:map|, 1 for |:unmap|, 2 for |noremap|.
-/// @param arg C-string containing the arguments of the map/abbrev
-/// command, i.e. everything except the initial `:[X][nore]map`.
-/// - Cannot be a read-only string; it will be modified.
-/// @param mode Bitflags representing the mode in which to set the mapping.
-/// See @ref get_map_mode.
-/// @param is_abbrev True if setting an abbreviation, false otherwise.
-///
-/// @return 0 on success. On failure, will return one of the following:
-/// - 1 for invalid arguments
-/// - 2 for no match
-/// - 4 for out of mem (deprecated, WON'T HAPPEN)
-/// - 5 for entry not unique
-///
-int do_map(int maptype, char_u *arg, int mode, bool is_abbrev)
-{
- MapArguments parsed_args;
- int result = str_to_mapargs(arg, maptype == 1, &parsed_args);
- switch (result) {
- case 0:
- break;
- case 1:
- // invalid arguments
- goto free_and_return;
- default:
- assert(false && "Unknown return code from str_to_mapargs!");
- result = -1;
- goto free_and_return;
- } // switch
-
- result = buf_do_map(maptype, &parsed_args, mode, is_abbrev, curbuf);
-
-free_and_return:
- xfree(parsed_args.rhs);
- xfree(parsed_args.orig_rhs);
- return result;
-}
-
-/*
- * Delete one entry from the abbrlist or maphash[].
- * "mpp" is a pointer to the m_next field of the PREVIOUS entry!
- */
-static void mapblock_free(mapblock_T **mpp)
-{
- mapblock_T *mp;
-
- mp = *mpp;
- xfree(mp->m_keys);
- NLUA_CLEAR_REF(mp->m_luaref);
- XFREE_CLEAR(mp->m_str);
- XFREE_CLEAR(mp->m_orig_str);
- XFREE_CLEAR(mp->m_desc);
- *mpp = mp->m_next;
- xfree(mp);
-}
-
-/*
- * Initialize maphash[] for first use.
- */
-static void validate_maphash(void)
-{
- if (!maphash_valid) {
- memset(maphash, 0, sizeof(maphash));
- maphash_valid = TRUE;
- }
-}
-
-/*
- * Get the mapping mode from the command name.
- */
-int get_map_mode(char_u **cmdp, bool forceit)
-{
- char_u *p;
- int modec;
- int mode;
-
- p = *cmdp;
- modec = *p++;
- if (modec == 'i') {
- mode = INSERT; // :imap
- } else if (modec == 'l') {
- mode = LANGMAP; // :lmap
- } else if (modec == 'c') {
- mode = CMDLINE; // :cmap
- } else if (modec == 'n' && *p != 'o') { // avoid :noremap
- mode = NORMAL; // :nmap
- } else if (modec == 'v') {
- mode = VISUAL + SELECTMODE; // :vmap
- } else if (modec == 'x') {
- mode = VISUAL; // :xmap
- } else if (modec == 's') {
- mode = SELECTMODE; // :smap
- } else if (modec == 'o') {
- mode = OP_PENDING; // :omap
- } else if (modec == 't') {
- mode = TERM_FOCUS; // :tmap
- } else {
- p--;
- if (forceit) {
- mode = INSERT + CMDLINE; // :map !
- } else {
- mode = VISUAL + SELECTMODE + NORMAL + OP_PENDING; // :map
- }
- }
-
- *cmdp = p;
- return mode;
-}
-
-/*
- * Clear all mappings or abbreviations.
- * 'abbr' should be FALSE for mappings, TRUE for abbreviations.
- */
-void map_clear_mode(char_u *cmdp, char_u *arg, int forceit, int abbr)
-{
- int mode;
- int local;
-
- local = (STRCMP(arg, "<buffer>") == 0);
- if (!local && *arg != NUL) {
- emsg(_(e_invarg));
- return;
- }
-
- mode = get_map_mode(&cmdp, forceit);
- map_clear_int(curbuf, mode,
- local,
- abbr);
-}
-
-/// Clear all mappings in "mode".
-///
-/// @param buf, buffer for local mappings
-/// @param mode mode in which to delete
-/// @param local true for buffer-local mappings
-/// @param abbr true for abbreviations
-void map_clear_int(buf_T *buf, int mode, bool local, bool abbr)
-{
- mapblock_T *mp, **mpp;
- int hash;
- int new_hash;
-
- validate_maphash();
-
- for (hash = 0; hash < 256; ++hash) {
- if (abbr) {
- if (hash > 0) { // there is only one abbrlist
- break;
- }
- if (local) {
- mpp = &buf->b_first_abbr;
- } else {
- mpp = &first_abbr;
- }
- } else {
- if (local) {
- mpp = &buf->b_maphash[hash];
- } else {
- mpp = &maphash[hash];
- }
- }
- while (*mpp != NULL) {
- mp = *mpp;
- if (mp->m_mode & mode) {
- mp->m_mode &= ~mode;
- if (mp->m_mode == 0) { // entry can be deleted
- mapblock_free(mpp);
- continue;
- }
- /*
- * May need to put this entry into another hash list.
- */
- new_hash = MAP_HASH(mp->m_mode, mp->m_keys[0]);
- if (!abbr && new_hash != hash) {
- *mpp = mp->m_next;
- if (local) {
- mp->m_next = buf->b_maphash[new_hash];
- buf->b_maphash[new_hash] = mp;
- } else {
- mp->m_next = maphash[new_hash];
- maphash[new_hash] = mp;
- }
- continue; // continue with *mpp
- }
- }
- mpp = &(mp->m_next);
- }
- }
-}
-
-/// Return characters to represent the map mode in an allocated string
-///
-/// @return [allocated] NUL-terminated string with characters.
-char *map_mode_to_chars(int mode)
- FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_RET
-{
- garray_T mapmode;
-
- ga_init(&mapmode, 1, 7);
-
- if ((mode & (INSERT + CMDLINE)) == INSERT + CMDLINE) {
- ga_append(&mapmode, '!'); // :map!
- } else if (mode & INSERT) {
- ga_append(&mapmode, 'i'); // :imap
- } else if (mode & LANGMAP) {
- ga_append(&mapmode, 'l'); // :lmap
- } else if (mode & CMDLINE) {
- ga_append(&mapmode, 'c'); // :cmap
- } else if ((mode & (NORMAL + VISUAL + SELECTMODE + OP_PENDING))
- == NORMAL + VISUAL + SELECTMODE + OP_PENDING) {
- ga_append(&mapmode, ' '); // :map
- } else {
- if (mode & NORMAL) {
- ga_append(&mapmode, 'n'); // :nmap
- }
- if (mode & OP_PENDING) {
- ga_append(&mapmode, 'o'); // :omap
- }
- if (mode & TERM_FOCUS) {
- ga_append(&mapmode, 't'); // :tmap
- }
- if ((mode & (VISUAL + SELECTMODE)) == VISUAL + SELECTMODE) {
- ga_append(&mapmode, 'v'); // :vmap
- } else {
- if (mode & VISUAL) {
- ga_append(&mapmode, 'x'); // :xmap
- }
- if (mode & SELECTMODE) {
- ga_append(&mapmode, 's'); // :smap
- }
- }
- }
-
- ga_append(&mapmode, NUL);
- return (char *)mapmode.ga_data;
-}
-
-/// @param local true for buffer-local map
-static void showmap(mapblock_T *mp, bool local)
-{
- size_t len = 1;
-
- if (message_filtered(mp->m_keys)
- && mp->m_str != NULL && message_filtered(mp->m_str)) {
- return;
- }
-
- if (msg_didout || msg_silent != 0) {
- msg_putchar('\n');
- if (got_int) { // 'q' typed at MORE prompt
- return;
- }
- }
-
- {
- char *const mapchars = map_mode_to_chars(mp->m_mode);
- msg_puts(mapchars);
- len = strlen(mapchars);
- xfree(mapchars);
- }
-
- while (++len <= 3) {
- msg_putchar(' ');
- }
-
- // Display the LHS. Get length of what we write.
- len = (size_t)msg_outtrans_special(mp->m_keys, true, 0);
- do {
- msg_putchar(' '); // padd with blanks
- len++;
- } while (len < 12);
-
- if (mp->m_noremap == REMAP_NONE) {
- msg_puts_attr("*", HL_ATTR(HLF_8));
- } else if (mp->m_noremap == REMAP_SCRIPT) {
- msg_puts_attr("&", HL_ATTR(HLF_8));
- } else {
- msg_putchar(' ');
- }
-
- if (local) {
- msg_putchar('@');
- } else {
- msg_putchar(' ');
- }
-
- /* Use FALSE below if we only want things like <Up> to show up as such on
- * the rhs, and not M-x etc, TRUE gets both -- webb */
- if (mp->m_luaref != LUA_NOREF) {
- char msg[100];
- snprintf(msg, sizeof(msg), "<Lua function %d>", mp->m_luaref);
- msg_puts_attr(msg, HL_ATTR(HLF_8));
- } 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
- // as typeahead.
- char_u *s = vim_strsave(mp->m_str);
- vim_unescape_csi(s);
- msg_outtrans_special(s, false, 0);
- xfree(s);
- }
-
- if (mp->m_desc != NULL) {
- msg_puts("\n "); // Shift line to same level as rhs.
- msg_puts(mp->m_desc);
- }
- if (p_verbose > 0) {
- last_set_msg(mp->m_script_ctx);
- }
- ui_flush(); // show one line at a time
-}
-
-/// Check if a map exists that has given string in the rhs
-///
-/// Also checks mappings local to the current buffer.
-///
-/// @param[in] str String which mapping must have in the rhs. Termcap codes
-/// are recognized in this argument.
-/// @param[in] modechars Mode(s) in which mappings are checked.
-/// @param[in] abbr true if checking abbreviations in place of mappings.
-///
-/// @return true if there is at least one mapping with given parameters.
-bool map_to_exists(const char *const str, const char *const modechars, const bool abbr)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
-{
- int mode = 0;
- int retval;
-
- char_u *buf;
- char_u *const rhs = replace_termcodes((const char_u *)str, strlen(str), &buf,
- false, true, true,
- CPO_TO_CPO_FLAGS);
-
-#define MAPMODE(mode, modechars, chr, modeflags) \
- do { \
- if (strchr(modechars, chr) != NULL) { \
- mode |= modeflags; \
- } \
- } while (0)
- MAPMODE(mode, modechars, 'n', NORMAL);
- MAPMODE(mode, modechars, 'v', VISUAL|SELECTMODE);
- MAPMODE(mode, modechars, 'x', VISUAL);
- MAPMODE(mode, modechars, 's', SELECTMODE);
- MAPMODE(mode, modechars, 'o', OP_PENDING);
- MAPMODE(mode, modechars, 'i', INSERT);
- MAPMODE(mode, modechars, 'l', LANGMAP);
- MAPMODE(mode, modechars, 'c', CMDLINE);
-#undef MAPMODE
-
- retval = map_to_exists_mode((const char *)rhs, mode, abbr);
- xfree(buf);
-
- return retval;
-}
-
-/// Check if a map exists that has given string in the rhs
-///
-/// Also checks mappings local to the current buffer.
-///
-/// @param[in] rhs String which mapping must have in the rhs. Termcap codes
-/// are recognized in this argument.
-/// @param[in] mode Mode(s) in which mappings are checked.
-/// @param[in] abbr true if checking abbreviations in place of mappings.
-///
-/// @return true if there is at least one mapping with given parameters.
-int map_to_exists_mode(const char *const rhs, const int mode, const bool abbr)
-{
- mapblock_T *mp;
- int hash;
- bool exp_buffer = false;
-
- validate_maphash();
-
- // Do it twice: once for global maps and once for local maps.
- for (;;) {
- for (hash = 0; hash < 256; hash++) {
- if (abbr) {
- if (hash > 0) { // There is only one abbr list.
- break;
- }
- if (exp_buffer) {
- mp = curbuf->b_first_abbr;
- } else {
- mp = first_abbr;
- }
- } else if (exp_buffer) {
- mp = curbuf->b_maphash[hash];
- } else {
- mp = maphash[hash];
- }
- for (; mp; mp = mp->m_next) {
- if ((mp->m_mode & mode)
- && mp->m_str != NULL && strstr((char *)mp->m_str, rhs) != NULL) {
- return true;
- }
- }
- }
- if (exp_buffer) {
- break;
- }
- exp_buffer = true;
- }
-
- return false;
-}
-
-/*
- * Used below when expanding mapping/abbreviation names.
- */
-static int expand_mapmodes = 0;
-static bool expand_isabbrev = false;
-static bool expand_buffer = false;
-
-/// Work out what to complete when doing command line completion of mapping
-/// or abbreviation names.
-///
-/// @param forceit true if '!' given
-/// @param isabbrev true if abbreviation
-/// @param isunmap true if unmap/unabbrev command
-char_u *set_context_in_map_cmd(expand_T *xp, char_u *cmd, char_u *arg, bool forceit, bool isabbrev,
- bool isunmap, cmdidx_T cmdidx)
-{
- if (forceit && cmdidx != CMD_map && cmdidx != CMD_unmap) {
- xp->xp_context = EXPAND_NOTHING;
- } else {
- if (isunmap) {
- expand_mapmodes = get_map_mode(&cmd, forceit || isabbrev);
- } else {
- expand_mapmodes = INSERT + CMDLINE;
- if (!isabbrev) {
- expand_mapmodes += VISUAL + SELECTMODE + NORMAL + OP_PENDING;
- }
- }
- expand_isabbrev = isabbrev;
- xp->xp_context = EXPAND_MAPPINGS;
- expand_buffer = false;
- for (;;) {
- if (STRNCMP(arg, "<buffer>", 8) == 0) {
- expand_buffer = true;
- arg = skipwhite(arg + 8);
- continue;
- }
- if (STRNCMP(arg, "<unique>", 8) == 0) {
- arg = skipwhite(arg + 8);
- continue;
- }
- if (STRNCMP(arg, "<nowait>", 8) == 0) {
- arg = skipwhite(arg + 8);
- continue;
- }
- if (STRNCMP(arg, "<silent>", 8) == 0) {
- arg = skipwhite(arg + 8);
- continue;
- }
- if (STRNCMP(arg, "<special>", 9) == 0) {
- arg = skipwhite(arg + 9);
- continue;
- }
- if (STRNCMP(arg, "<script>", 8) == 0) {
- arg = skipwhite(arg + 8);
- continue;
- }
- if (STRNCMP(arg, "<expr>", 6) == 0) {
- arg = skipwhite(arg + 6);
- continue;
- }
- break;
- }
- xp->xp_pattern = arg;
- }
-
- return NULL;
-}
-
-// Find all mapping/abbreviation names that match regexp "regmatch".
-// For command line expansion of ":[un]map" and ":[un]abbrev" in all modes.
-// Return OK if matches found, FAIL otherwise.
-int ExpandMappings(regmatch_T *regmatch, int *num_file, char_u ***file)
-{
- mapblock_T *mp;
- int hash;
- int count;
- int round;
- char_u *p;
- int i;
-
- validate_maphash();
-
- *num_file = 0; // return values in case of FAIL
- *file = NULL;
-
- /*
- * round == 1: Count the matches.
- * round == 2: Build the array to keep the matches.
- */
- for (round = 1; round <= 2; ++round) {
- count = 0;
-
- for (i = 0; i < 7; i++) {
- if (i == 0) {
- p = (char_u *)"<silent>";
- } else if (i == 1) {
- p = (char_u *)"<unique>";
- } else if (i == 2) {
- p = (char_u *)"<script>";
- } else if (i == 3) {
- p = (char_u *)"<expr>";
- } else if (i == 4 && !expand_buffer) {
- p = (char_u *)"<buffer>";
- } else if (i == 5) {
- p = (char_u *)"<nowait>";
- } else if (i == 6) {
- p = (char_u *)"<special>";
- } else {
- continue;
- }
-
- if (vim_regexec(regmatch, p, (colnr_T)0)) {
- if (round == 1) {
- ++count;
- } else {
- (*file)[count++] = vim_strsave(p);
- }
- }
- }
-
- for (hash = 0; hash < 256; ++hash) {
- if (expand_isabbrev) {
- if (hash > 0) { // only one abbrev list
- break; // for (hash)
- }
- mp = first_abbr;
- } else if (expand_buffer) {
- mp = curbuf->b_maphash[hash];
- } else {
- mp = maphash[hash];
- }
- for (; mp; mp = mp->m_next) {
- if (mp->m_mode & expand_mapmodes) {
- p = translate_mapping(mp->m_keys, CPO_TO_CPO_FLAGS);
- if (p != NULL && vim_regexec(regmatch, p, (colnr_T)0)) {
- if (round == 1) {
- ++count;
- } else {
- (*file)[count++] = p;
- p = NULL;
- }
- }
- xfree(p);
- }
- } // for (mp)
- } // for (hash)
-
- if (count == 0) { // no match found
- break; // for (round)
- }
-
- if (round == 1) {
- *file = (char_u **)xmalloc((size_t)count * sizeof(char_u *));
- }
- } // for (round)
-
- if (count > 1) {
- char_u **ptr1;
- char_u **ptr2;
- char_u **ptr3;
-
- // Sort the matches
- sort_strings(*file, count);
-
- // Remove multiple entries
- ptr1 = *file;
- ptr2 = ptr1 + 1;
- ptr3 = ptr1 + count;
-
- while (ptr2 < ptr3) {
- if (STRCMP(*ptr1, *ptr2)) {
- *++ptr1 = *ptr2++;
- } else {
- xfree(*ptr2++);
- count--;
- }
- }
- }
-
- *num_file = count;
- return count == 0 ? FAIL : OK;
-}
-
-// Check for an abbreviation.
-// Cursor is at ptr[col].
-// When inserting, mincol is where insert started.
-// For the command line, mincol is what is to be skipped over.
-// "c" is the character typed before check_abbr was called. It may have
-// ABBR_OFF added to avoid prepending a CTRL-V to it.
-//
-// Historic vi practice: The last character of an abbreviation must be an id
-// character ([a-zA-Z0-9_]). The characters in front of it must be all id
-// characters or all non-id characters. This allows for abbr. "#i" to
-// "#include".
-//
-// Vim addition: Allow for abbreviations that end in a non-keyword character.
-// Then there must be white space before the abbr.
-//
-// Return true if there is an abbreviation, false if not.
-bool check_abbr(int c, char_u *ptr, int col, int mincol)
-{
- int len;
- int scol; // starting column of the abbr.
- int j;
- char_u *s;
- char_u tb[MB_MAXBYTES + 4];
- mapblock_T *mp;
- mapblock_T *mp2;
- int clen = 0; // length in characters
- bool is_id = true;
-
- if (typebuf.tb_no_abbr_cnt) { // abbrev. are not recursive
- return false;
- }
-
- // no remapping implies no abbreviation, except for CTRL-]
- if ((KeyNoremap & (RM_NONE|RM_SCRIPT)) != 0 && c != Ctrl_RSB) {
- return false;
- }
-
- // Check for word before the cursor: If it ends in a keyword char all
- // chars before it must be keyword chars or non-keyword chars, but not
- // white space. If it ends in a non-keyword char we accept any characters
- // before it except white space.
- if (col == 0) { // cannot be an abbr.
- return false;
- }
-
- {
- bool vim_abbr;
- char_u *p = mb_prevptr(ptr, ptr + col);
- if (!vim_iswordp(p)) {
- vim_abbr = true; // Vim added abbr.
- } else {
- vim_abbr = false; // vi compatible abbr.
- if (p > ptr) {
- is_id = vim_iswordp(mb_prevptr(ptr, p));
- }
- }
- clen = 1;
- while (p > ptr + mincol) {
- p = mb_prevptr(ptr, p);
- if (ascii_isspace(*p) || (!vim_abbr && is_id != vim_iswordp(p))) {
- p += utfc_ptr2len(p);
- break;
- }
- ++clen;
- }
- scol = (int)(p - ptr);
- }
-
- if (scol < mincol) {
- scol = mincol;
- }
- if (scol < col) { // there is a word in front of the cursor
- ptr += scol;
- len = col - scol;
- mp = curbuf->b_first_abbr;
- mp2 = first_abbr;
- if (mp == NULL) {
- mp = mp2;
- mp2 = NULL;
- }
- for (; mp;
- mp->m_next == NULL ? (mp = mp2, mp2 = NULL) :
- (mp = mp->m_next)) {
- int qlen = mp->m_keylen;
- char_u *q = mp->m_keys;
- int match;
-
- if (strchr((const char *)mp->m_keys, K_SPECIAL) != NULL) {
- // Might have CSI escaped mp->m_keys.
- q = vim_strsave(mp->m_keys);
- vim_unescape_csi(q);
- qlen = (int)STRLEN(q);
- }
- // find entries with right mode and keys
- match = (mp->m_mode & State)
- && qlen == len
- && !STRNCMP(q, ptr, (size_t)len);
- if (q != mp->m_keys) {
- xfree(q);
- }
- if (match) {
- break;
- }
- }
- if (mp != NULL) {
- /*
- * Found a match:
- * Insert the rest of the abbreviation in typebuf.tb_buf[].
- * This goes from end to start.
- *
- * Characters 0x000 - 0x100: normal chars, may need CTRL-V,
- * except K_SPECIAL: Becomes K_SPECIAL KS_SPECIAL KE_FILLER
- * Characters where IS_SPECIAL() == TRUE: key codes, need
- * K_SPECIAL. Other characters (with ABBR_OFF): don't use CTRL-V.
- *
- * Character CTRL-] is treated specially - it completes the
- * abbreviation, but is not inserted into the input stream.
- */
- j = 0;
- if (c != Ctrl_RSB) {
- // special key code, split up
- if (IS_SPECIAL(c) || c == K_SPECIAL) {
- tb[j++] = K_SPECIAL;
- tb[j++] = (char_u)K_SECOND(c);
- tb[j++] = (char_u)K_THIRD(c);
- } else {
- if (c < ABBR_OFF && (c < ' ' || c > '~')) {
- tb[j++] = Ctrl_V; // special char needs CTRL-V
- }
- // if ABBR_OFF has been added, remove it here.
- if (c >= ABBR_OFF) {
- c -= ABBR_OFF;
- }
- 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);
- if (escaped != NULL) {
- newlen = (int)STRLEN(escaped);
- memmove(tb + j, escaped, (size_t)newlen);
- j += newlen;
- xfree(escaped);
- }
- }
- tb[j] = NUL;
- // insert the last typed char
- (void)ins_typebuf(tb, 1, 0, true, mp->m_silent);
- }
- if (mp->m_expr) {
- s = eval_map_expr(mp, c);
- } else {
- s = mp->m_str;
- }
- if (s != NULL) {
- // insert the to string
- (void)ins_typebuf(s, mp->m_noremap, 0, true, mp->m_silent);
- // no abbrev. for these chars
- typebuf.tb_no_abbr_cnt += (int)STRLEN(s) + j + 1;
- if (mp->m_expr) {
- xfree(s);
- }
- }
-
- tb[0] = Ctrl_H;
- tb[1] = NUL;
- len = clen; // Delete characters instead of bytes
- while (len-- > 0) { // delete the from string
- (void)ins_typebuf(tb, 1, 0, true, mp->m_silent);
- }
- return true;
- }
- }
- return false;
-}
-
-/// Evaluate the RHS of a mapping or abbreviations and take care of escaping
-/// special characters.
-///
-/// @param c NUL or typed character for abbreviation
-static char_u *eval_map_expr(mapblock_T *mp, int c)
-{
- char_u *res;
- char_u *p = NULL;
- char_u *expr = NULL;
- char_u *save_cmd;
- pos_T save_cursor;
- int save_msg_col;
- int save_msg_row;
-
- /* Remove escaping of CSI, 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);
- }
-
- save_cmd = save_cmdline_alloc();
-
- // Forbid changing text or using ":normal" to avoid most of the bad side
- // effects. Also restore the cursor position.
- textlock++;
- ex_normal_lock++;
- set_vim_var_char(c); // set v:char to the typed character
- save_cursor = curwin->w_cursor;
- save_msg_col = msg_col;
- save_msg_row = msg_row;
- if (mp->m_luaref != LUA_NOREF) {
- Error err = ERROR_INIT;
- Array args = ARRAY_DICT_INIT;
- Object ret = nlua_call_ref(mp->m_luaref, NULL, args, true, &err);
- if (ret.type == kObjectTypeString) {
- p = (char_u *)xstrndup(ret.data.string.data, ret.data.string.size);
- }
- api_free_object(ret);
- if (err.type != kErrorTypeNone) {
- semsg_multiline("E5108: %s", err.msg);
- api_clear_error(&err);
- }
- } else {
- p = eval_to_string(expr, NULL, false);
- xfree(expr);
- }
- textlock--;
- ex_normal_lock--;
- curwin->w_cursor = save_cursor;
- msg_col = save_msg_col;
- msg_row = save_msg_row;
-
- restore_cmdline_alloc(save_cmd);
-
- 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);
- 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)
-{
- // Need a buffer to hold up to three times as much. Four in case of an
- // illegal utf-8 byte:
- // 0xc0 -> 0xc3 - 0x80 -> 0xc3 K_SPECIAL KS_SPECIAL KE_FILLER
- char_u *res = xmalloc(STRLEN(p) * 4 + 1);
- char_u *d = res;
- for (char_u *s = p; *s != NUL;) {
- if (s[0] == K_SPECIAL && s[1] != NUL && s[2] != NUL) {
- // Copy special key unmodified.
- *d++ = *s++;
- *d++ = *s++;
- *d++ = *s++;
- } else {
- // Add character, possibly multi-byte to destination, escaping
- // CSI and K_SPECIAL. Be careful, it can be an illegal byte!
- d = add_char2buf(utf_ptr2char(s), d);
- s += utf_ptr2len(s);
- }
- }
- *d = NUL;
-
- 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)
-{
- char_u *s = p, *d = p;
-
- while (*s != NUL) {
- 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++;
- }
- }
- *d = NUL;
-}
-
-/// Write map commands for the current mappings to an .exrc file.
-/// Return FAIL on error, OK otherwise.
-///
-/// @param buf buffer for local mappings or NULL
-int makemap(FILE *fd, buf_T *buf)
-{
- mapblock_T *mp;
- char_u c1, c2, c3;
- char_u *p;
- char *cmd;
- int abbr;
- int hash;
- bool did_cpo = false;
-
- validate_maphash();
-
- // Do the loop twice: Once for mappings, once for abbreviations.
- // Then loop over all map hash lists.
- for (abbr = 0; abbr < 2; abbr++) {
- for (hash = 0; hash < 256; hash++) {
- if (abbr) {
- if (hash > 0) { // there is only one abbr list
- break;
- }
- if (buf != NULL) {
- mp = buf->b_first_abbr;
- } else {
- mp = first_abbr;
- }
- } else {
- if (buf != NULL) {
- mp = buf->b_maphash[hash];
- } else {
- mp = maphash[hash];
- }
- }
-
- for (; mp; mp = mp->m_next) {
- // skip script-local mappings
- if (mp->m_noremap == REMAP_SCRIPT) {
- continue;
- }
-
- // skip lua mappings and mappings that contain a <SNR> (script-local thing),
- // they probably don't work when loaded again
- if (mp->m_luaref != LUA_NOREF) {
- continue;
- }
- for (p = mp->m_str; *p != NUL; p++) {
- if (p[0] == K_SPECIAL && p[1] == KS_EXTRA
- && p[2] == (int)KE_SNR) {
- break;
- }
- }
- if (*p != NUL) {
- continue;
- }
-
- // It's possible to create a mapping and then ":unmap" certain
- // modes. We recreate this here by mapping the individual
- // modes, which requires up to three of them.
- c1 = NUL;
- c2 = NUL;
- c3 = NUL;
- if (abbr) {
- cmd = "abbr";
- } else {
- cmd = "map";
- }
- switch (mp->m_mode) {
- case NORMAL + VISUAL + SELECTMODE + OP_PENDING:
- break;
- case NORMAL:
- c1 = 'n';
- break;
- case VISUAL:
- c1 = 'x';
- break;
- case SELECTMODE:
- c1 = 's';
- break;
- case OP_PENDING:
- c1 = 'o';
- break;
- case NORMAL + VISUAL:
- c1 = 'n';
- c2 = 'x';
- break;
- case NORMAL + SELECTMODE:
- c1 = 'n';
- c2 = 's';
- break;
- case NORMAL + OP_PENDING:
- c1 = 'n';
- c2 = 'o';
- break;
- case VISUAL + SELECTMODE:
- c1 = 'v';
- break;
- case VISUAL + OP_PENDING:
- c1 = 'x';
- c2 = 'o';
- break;
- case SELECTMODE + OP_PENDING:
- c1 = 's';
- c2 = 'o';
- break;
- case NORMAL + VISUAL + SELECTMODE:
- c1 = 'n';
- c2 = 'v';
- break;
- case NORMAL + VISUAL + OP_PENDING:
- c1 = 'n';
- c2 = 'x';
- c3 = 'o';
- break;
- case NORMAL + SELECTMODE + OP_PENDING:
- c1 = 'n';
- c2 = 's';
- c3 = 'o';
- break;
- case VISUAL + SELECTMODE + OP_PENDING:
- c1 = 'v';
- c2 = 'o';
- break;
- case CMDLINE + INSERT:
- if (!abbr) {
- cmd = "map!";
- }
- break;
- case CMDLINE:
- c1 = 'c';
- break;
- case INSERT:
- c1 = 'i';
- break;
- case LANGMAP:
- c1 = 'l';
- break;
- case TERM_FOCUS:
- c1 = 't';
- break;
- default:
- iemsg(_("E228: makemap: Illegal mode"));
- return FAIL;
- }
- do {
- // do this twice if c2 is set, 3 times with c3 */
- // When outputting <> form, need to make sure that 'cpo'
- // is set to the Vim default.
- if (!did_cpo) {
- if (*mp->m_str == NUL) { // Will use <Nop>.
- did_cpo = true;
- } else {
- const char specials[] = { (char)(uint8_t)K_SPECIAL, NL, NUL };
- if (strpbrk((const char *)mp->m_str, specials) != NULL
- || strpbrk((const char *)mp->m_keys, specials) != NULL) {
- did_cpo = true;
- }
- }
- if (did_cpo) {
- if (fprintf(fd, "let s:cpo_save=&cpo") < 0
- || put_eol(fd) < 0
- || fprintf(fd, "set cpo&vim") < 0
- || put_eol(fd) < 0) {
- return FAIL;
- }
- }
- }
- if (c1 && putc(c1, fd) < 0) {
- return FAIL;
- }
- if (mp->m_noremap != REMAP_YES && fprintf(fd, "nore") < 0) {
- return FAIL;
- }
- if (fputs(cmd, fd) < 0) {
- return FAIL;
- }
- if (buf != NULL && fputs(" <buffer>", fd) < 0) {
- return FAIL;
- }
- if (mp->m_nowait && fputs(" <nowait>", fd) < 0) {
- return FAIL;
- }
- if (mp->m_silent && fputs(" <silent>", fd) < 0) {
- return FAIL;
- }
- if (mp->m_expr && fputs(" <expr>", fd) < 0) {
- return FAIL;
- }
-
- if (putc(' ', fd) < 0
- || put_escstr(fd, mp->m_keys, 0) == FAIL
- || putc(' ', fd) < 0
- || put_escstr(fd, mp->m_str, 1) == FAIL
- || put_eol(fd) < 0) {
- return FAIL;
- }
- c1 = c2;
- c2 = c3;
- c3 = NUL;
- } while (c1 != NUL);
- }
- }
- }
- if (did_cpo) {
- if (fprintf(fd, "let &cpo=s:cpo_save") < 0
- || put_eol(fd) < 0
- || fprintf(fd, "unlet s:cpo_save") < 0
- || put_eol(fd) < 0) {
- return FAIL;
- }
- }
- return OK;
-}
-
-// write escape string to file
-// "what": 0 for :map lhs, 1 for :map rhs, 2 for :set
-//
-// return FAIL for failure, OK otherwise
-int put_escstr(FILE *fd, char_u *strstart, int what)
-{
- char_u *str = strstart;
- int c;
-
- // :map xx <Nop>
- if (*str == NUL && what == 1) {
- if (fprintf(fd, "<Nop>") < 0) {
- return FAIL;
- }
- return OK;
- }
-
- for (; *str != NUL; str++) {
- // Check for a multi-byte character, which may contain escaped
- // K_SPECIAL and CSI bytes.
- const char *p = mb_unescape((const char **)&str);
- if (p != NULL) {
- while (*p != NUL) {
- if (fputc(*p++, fd) < 0) {
- return FAIL;
- }
- }
- --str;
- continue;
- }
-
- c = *str;
- /*
- * Special key codes have to be translated to be able to make sense
- * when they are read back.
- */
- if (c == K_SPECIAL && what != 2) {
- int modifiers = 0;
- if (str[1] == KS_MODIFIER) {
- modifiers = str[2];
- str += 3;
- c = *str;
- }
- if (c == K_SPECIAL) {
- c = TO_SPECIAL(str[1], str[2]);
- str += 2;
- }
- if (IS_SPECIAL(c) || modifiers) { // special key
- if (fputs((char *)get_special_key_name(c, modifiers), fd) < 0) {
- return FAIL;
- }
- continue;
- }
- }
-
- /*
- * A '\n' in a map command should be written as <NL>.
- * A '\n' in a set command should be written as \^V^J.
- */
- if (c == NL) {
- if (what == 2) {
- if (fprintf(fd, "\\\026\n") < 0) {
- return FAIL;
- }
- } else {
- if (fprintf(fd, "<NL>") < 0) {
- return FAIL;
- }
- }
- continue;
- }
-
- /*
- * Some characters have to be escaped with CTRL-V to
- * prevent them from misinterpreted in DoOneCmd().
- * A space, Tab and '"' has to be escaped with a backslash to
- * prevent it to be misinterpreted in do_set().
- * A space has to be escaped with a CTRL-V when it's at the start of a
- * ":map" rhs.
- * A '<' has to be escaped with a CTRL-V to prevent it being
- * interpreted as the start of a special key name.
- * A space in the lhs of a :map needs a CTRL-V.
- */
- if (what == 2 && (ascii_iswhite(c) || c == '"' || c == '\\')) {
- if (putc('\\', fd) < 0) {
- return FAIL;
- }
- } else if (c < ' ' || c > '~' || c == '|'
- || (what == 0 && c == ' ')
- || (what == 1 && str == strstart && c == ' ')
- || (what != 2 && c == '<')) {
- if (putc(Ctrl_V, fd) < 0) {
- return FAIL;
- }
- }
- if (putc(c, fd) < 0) {
- return FAIL;
- }
- }
- return OK;
-}
-
-/// Check the string "keys" against the lhs of all mappings.
-/// Return pointer to rhs of mapping (mapblock->m_str).
-/// NULL when no mapping found.
-///
-/// @param exact require exact match
-/// @param ign_mod ignore preceding modifier
-/// @param abbr do abbreviations
-/// @param mp_ptr return: pointer to mapblock or NULL
-/// @param local_ptr return: buffer-local mapping or NULL
-char_u *check_map(char_u *keys, int mode, int exact, int ign_mod, int abbr, mapblock_T **mp_ptr,
- int *local_ptr, int *rhs_lua)
-{
- int len, minlen;
- mapblock_T *mp;
- *rhs_lua = LUA_NOREF;
-
- validate_maphash();
-
- len = (int)STRLEN(keys);
- for (int local = 1; local >= 0; local--) {
- // loop over all hash lists
- for (int hash = 0; hash < 256; hash++) {
- if (abbr) {
- if (hash > 0) { // there is only one list.
- break;
- }
- if (local) {
- mp = curbuf->b_first_abbr;
- } else {
- mp = first_abbr;
- }
- } else if (local) {
- mp = curbuf->b_maphash[hash];
- } else {
- mp = maphash[hash];
- }
- for (; mp != NULL; mp = mp->m_next) {
- /* skip entries with wrong mode, wrong length and not matching
- * ones */
- if ((mp->m_mode & mode) && (!exact || mp->m_keylen == len)) {
- char_u *s = mp->m_keys;
- int keylen = mp->m_keylen;
- if (ign_mod && keylen >= 3
- && s[0] == K_SPECIAL && s[1] == KS_MODIFIER) {
- s += 3;
- keylen -= 3;
- }
- minlen = keylen < len ? keylen : len;
- if (STRNCMP(s, keys, minlen) == 0) {
- if (mp_ptr != NULL) {
- *mp_ptr = mp;
- }
- if (local_ptr != NULL) {
- *local_ptr = local;
- }
- *rhs_lua = mp->m_luaref;
- return mp->m_luaref == LUA_NOREF ? mp->m_str : NULL;
- }
- }
- }
- }
- }
-
- return NULL;
-}
-
-
-/// Add a mapping. Unlike @ref do_map this copies the {map} argument, so
-/// static or read-only strings can be used.
-///
-/// @param map C-string containing the arguments of the map/abbrev command,
-/// i.e. everything except the initial `:[X][nore]map`.
-/// @param mode Bitflags representing the mode in which to set the mapping.
-/// See @ref get_map_mode.
-/// @param nore If true, make a non-recursive mapping.
-void add_map(char_u *map, int mode, bool nore)
-{
- char_u *s;
- char_u *cpo_save = p_cpo;
-
- p_cpo = (char_u *)""; // Allow <> notation
- // Need to put string in allocated memory, because do_map() will modify it.
- s = vim_strsave(map);
- (void)do_map(nore ? 2 : 0, s, mode, false);
- xfree(s);
- p_cpo = cpo_save;
-}
-
-/// Translate an internal mapping/abbreviation representation into the
-/// corresponding external one recognized by :map/:abbrev commands.
-///
-/// This function is called when expanding mappings/abbreviations on the
-/// command-line.
-///
-/// It uses a growarray to build the translation string since the latter can be
-/// wider than the original description. The caller has to free the string
-/// afterwards.
-///
-/// @param cpo_flags Value of various flags present in &cpo
-///
-/// @return NULL when there is a problem.
-static char_u *translate_mapping(char_u *str, int cpo_flags)
-{
- garray_T ga;
- ga_init(&ga, 1, 40);
-
- bool cpo_bslash = !(cpo_flags&FLAG_CPO_BSLASH);
-
- for (; *str; ++str) {
- int c = *str;
- if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL) {
- int modifiers = 0;
- if (str[1] == KS_MODIFIER) {
- str++;
- modifiers = *++str;
- c = *++str;
- }
-
- if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL) {
- c = TO_SPECIAL(str[1], str[2]);
- if (c == K_ZERO) {
- // display <Nul> as ^@
- c = NUL;
- }
- str += 2;
- }
- if (IS_SPECIAL(c) || modifiers) { // special key
- ga_concat(&ga, (char *)get_special_key_name(c, modifiers));
- continue; // for (str)
- }
- }
-
- if (c == ' ' || c == '\t' || c == Ctrl_J || c == Ctrl_V
- || (c == '\\' && !cpo_bslash)) {
- ga_append(&ga, cpo_bslash ? Ctrl_V : '\\');
- }
-
- if (c) {
- ga_append(&ga, (char)c);
- }
- }
- ga_append(&ga, NUL);
- return (char_u *)(ga.ga_data);
-}
-
static bool typebuf_match_len(const uint8_t *str, int *mlen)
{
int i;
@@ -4539,22 +2800,8 @@ static bool typebuf_match_len(const uint8_t *str, int *mlen)
return str[i] == NUL; // matched the whole string
}
-/// Retrieve the mapblock at the index either globally or for a certain buffer
-///
-/// @param index The index in the maphash[]
-/// @param buf The buffer to get the maphash from. NULL for global
-mapblock_T *get_maphash(int index, buf_T *buf)
- FUNC_ATTR_PURE
-{
- if (index >= MAX_MAPHASH) {
- return NULL;
- }
-
- return (buf == NULL) ? maphash[index] : buf->b_maphash[index];
-}
-
/// Get command argument for <Cmd> key
-char_u *getcmdkeycmd(int promptc, void *cookie, int indent, bool do_concat)
+char *getcmdkeycmd(int promptc, void *cookie, int indent, bool do_concat)
{
garray_T line_ga;
int c1 = -1, c2;
@@ -4590,7 +2837,6 @@ char_u *getcmdkeycmd(int promptc, void *cookie, int indent, bool do_concat)
c1 = TO_SPECIAL(c1, c2);
}
-
if (got_int) {
aborted = true;
} else if (c1 == '\r' || c1 == '\n') {
@@ -4601,15 +2847,21 @@ char_u *getcmdkeycmd(int promptc, void *cookie, int indent, bool do_concat)
// special case to give nicer error message
emsg(e_cmdmap_repeated);
aborted = true;
- } else if (IS_SPECIAL(c1)) {
- if (c1 == K_SNR) {
- ga_concat(&line_ga, "<SNR>");
+ } else if (c1 == K_SNR) {
+ ga_concat(&line_ga, "<SNR>");
+ } else {
+ if (cmod != 0) {
+ ga_append(&line_ga, (char)K_SPECIAL);
+ ga_append(&line_ga, (char)KS_MODIFIER);
+ ga_append(&line_ga, (char)cmod);
+ }
+ if (IS_SPECIAL(c1)) {
+ ga_append(&line_ga, (char)K_SPECIAL);
+ ga_append(&line_ga, (char)K_SECOND(c1));
+ ga_append(&line_ga, (char)K_THIRD(c1));
} else {
- semsg(e_cmdmap_key, get_special_key_name(c1, cmod));
- aborted = true;
+ ga_append(&line_ga, (char)c1);
}
- } else {
- ga_append(&line_ga, (char)c1);
}
cmod = 0;
@@ -4621,7 +2873,7 @@ char_u *getcmdkeycmd(int promptc, void *cookie, int indent, bool do_concat)
ga_clear(&line_ga);
}
- return (char_u *)line_ga.ga_data;
+ return line_ga.ga_data;
}
bool map_execute_lua(void)
diff --git a/src/nvim/getchar.h b/src/nvim/getchar.h
index be10e150e5..6996b00c6e 100644
--- a/src/nvim/getchar.h
+++ b/src/nvim/getchar.h
@@ -1,10 +1,7 @@
#ifndef NVIM_GETCHAR_H
#define NVIM_GETCHAR_H
-#include "nvim/buffer_defs.h"
-#include "nvim/ex_cmds_defs.h"
#include "nvim/os/fileio.h"
-#include "nvim/types.h"
#include "nvim/vim.h"
/// Values for "noremap" argument of ins_typebuf()
@@ -24,45 +21,8 @@ typedef enum {
FLUSH_INPUT, // flush typebuf and inchar() input
} flush_buffers_T;
-/// All possible |:map-arguments| usable in a |:map| command.
-///
-/// The <special> argument has no effect on mappings and is excluded from this
-/// struct declaration. |noremap| is included, since it behaves like a map
-/// argument when used in a mapping.
-///
-/// @see mapblock_T
-struct map_arguments {
- bool buffer;
- bool expr;
- bool noremap;
- bool nowait;
- bool script;
- bool silent;
- bool unique;
-
- /// The {lhs} of the mapping.
- ///
- /// vim limits this to MAXMAPLEN characters, allowing us to use a static
- /// buffer. Setting lhs_len to a value larger than MAXMAPLEN can signal
- /// that {lhs} was too long and truncated.
- char_u lhs[MAXMAPLEN + 1];
- size_t lhs_len;
-
- char_u *rhs; /// The {rhs} of the mapping.
- size_t rhs_len;
- LuaRef rhs_lua; /// lua function as rhs
- bool rhs_is_noop; /// True when the {orig_rhs} is <nop>.
-
- char_u *orig_rhs; /// The original text of the {rhs}.
- size_t orig_rhs_len;
- char *desc; /// map escription
-};
-typedef struct map_arguments MapArguments;
-#define MAP_ARGUMENTS_INIT { false, false, false, false, false, false, false, \
- { 0 }, 0, NULL, 0, LUA_NOREF, false, NULL, 0, NULL }
-
-#define KEYLEN_PART_KEY -1 // keylen value for incomplete key-code
-#define KEYLEN_PART_MAP -2 // keylen value for incomplete mapping
+#define KEYLEN_PART_KEY (-1) // keylen value for incomplete key-code
+#define KEYLEN_PART_MAP (-2) // keylen value for incomplete mapping
/// Maximum number of streams to read script from
enum { NSCRIPT = 15, };
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 40c61d01b5..8d896aef31 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -15,7 +15,7 @@
#include "nvim/syntax_defs.h"
#include "nvim/types.h"
-#define IOSIZE (1024+1) // file I/O and sprintf buffer size
+#define IOSIZE (1024 + 1) // file I/O and sprintf buffer size
#define MSG_BUF_LEN 480 // length of buffer for small messages
#define MSG_BUF_CLEN (MSG_BUF_LEN / 6) // cell length (worst case: utf-8
@@ -76,7 +76,8 @@
EXTERN struct nvim_stats_s {
int64_t fsync;
int64_t redraw;
-} g_stats INIT(= { 0, 0 });
+ int16_t log_skip; // How many logs were tried and skipped before log_init.
+} g_stats INIT(= { 0, 0, 0 });
// Values for "starting".
#define NO_SCREEN 2 // no screen updating yet
@@ -84,7 +85,7 @@ EXTERN struct nvim_stats_s {
// 0 not starting anymore
// Number of Rows and Columns in the screen.
-// Note: Use default_grid.Rows and default_grid.Columns to access items in
+// Note: Use default_grid.rows and default_grid.cols to access items in
// default_grid.chars[]. They may have different values when the screen
// wasn't (re)allocated yet after setting Rows or Columns (e.g., when starting
// up).
@@ -96,7 +97,6 @@ EXTERN int Columns INIT(= DFLT_COLS); // nr of columns in the screen
EXTERN NS ns_hl_active INIT(= 0); // current ns that defines highlights
EXTERN bool ns_hl_changed INIT(= false); // highlight need update
-
// We use 64-bit file functions here, if available. E.g. ftello() returns
// off_t instead of long, which helps if long is 32 bit and off_t is 64 bit.
// We assume that when fseeko() is available then ftello() is too.
@@ -127,8 +127,11 @@ 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
+// The value of "mod_mask" and the unmodified character before calling merge_modifiers().
+EXTERN int vgetc_mod_mask INIT(= 0);
+EXTERN int vgetc_char INIT(= 0);
// Cmdline_row is the row where the command line starts, just below the
// last window.
@@ -199,7 +202,6 @@ EXTERN bool msg_scrolled_ign INIT(= false);
// is reset before the screen is redrawn, so we need to keep track of this.
EXTERN bool msg_did_scroll INIT(= false);
-
EXTERN char_u *keep_msg INIT(= NULL); // msg to be shown after redraw
EXTERN int keep_msg_attr INIT(= 0); // highlight attr for keep_msg
EXTERN bool need_fileinfo INIT(= false); // do fileinfo() after redraw
@@ -222,12 +224,12 @@ EXTERN dict_T vimvardict; // Dictionary with v: variables
EXTERN dict_T globvardict; // Dictionary with g: variables
/// g: value
#define globvarht globvardict.dv_hashtab
-EXTERN int did_emsg; // set by emsg() when the message
+EXTERN bool did_emsg; // set by emsg() when the message
// is displayed or thrown
EXTERN bool called_vim_beep; // set if vim_beep() is called
EXTERN bool did_emsg_syntax; // did_emsg set because of a
// syntax error
-EXTERN int called_emsg; // always set by emsg()
+EXTERN int called_emsg; // always incremented by emsg()
EXTERN int ex_exitval INIT(= 0); // exit value for ex mode
EXTERN bool emsg_on_display INIT(= false); // there is an error message
EXTERN bool rc_did_emsg INIT(= false); // vim_regcomp() called emsg()
@@ -250,7 +252,7 @@ EXTERN int lines_left INIT(= -1); // lines left for listing
EXTERN int msg_no_more INIT(= false); // don't use more prompt, truncate
// messages
-EXTERN char_u *sourcing_name INIT(= NULL); // name of error message source
+EXTERN char *sourcing_name INIT(= NULL); // name of error message source
EXTERN linenr_T sourcing_lnum INIT(= 0); // line number of the source file
EXTERN int ex_nesting_level INIT(= 0); // nesting level
@@ -272,11 +274,11 @@ EXTERN except_T *current_exception;
/// Set when a throw that cannot be handled in do_cmdline() must be propagated
/// to the cstack of the previously called do_cmdline().
-EXTERN int need_rethrow INIT(= false);
+EXTERN bool need_rethrow INIT(= false);
/// Set when a ":finish" or ":return" that cannot be handled in do_cmdline()
/// must be propagated to the cstack of the previously called do_cmdline().
-EXTERN int check_cstack INIT(= false);
+EXTERN bool check_cstack INIT(= false);
/// Number of nested try conditionals (across function calls and ":source"
/// commands).
@@ -312,7 +314,6 @@ EXTERN bool suppress_errthrow INIT(= false);
/// cstacks; the pending exceptions, however, are not on the caught stack.
EXTERN except_T *caught_stack INIT(= NULL);
-
///
/// Garbage collection can only take place when we are sure there are no Lists
/// or Dictionaries being used internally. This is flagged with
@@ -326,38 +327,45 @@ EXTERN int want_garbage_collect INIT(= false);
EXTERN int garbage_collect_at_exit INIT(= false);
// Special values for current_SID.
-#define SID_MODELINE -1 // when using a modeline
-#define SID_CMDARG -2 // for "--cmd" argument
-#define SID_CARG -3 // for "-c" argument
-#define SID_ENV -4 // for sourcing environment variable
-#define SID_ERROR -5 // option was reset because of an error
-#define SID_NONE -6 // don't set scriptID
-#define SID_WINLAYOUT -7 // changing window size
-#define SID_LUA -8 // for Lua scripts/chunks
-#define SID_API_CLIENT -9 // for API clients
-#define SID_STR -10 // for sourcing a string with no script item
+#define SID_MODELINE (-1) // when using a modeline
+#define SID_CMDARG (-2) // for "--cmd" argument
+#define SID_CARG (-3) // for "-c" argument
+#define SID_ENV (-4) // for sourcing environment variable
+#define SID_ERROR (-5) // option was reset because of an error
+#define SID_NONE (-6) // don't set scriptID
+#define SID_WINLAYOUT (-7) // changing window size
+#define SID_LUA (-8) // for Lua scripts/chunks
+#define SID_API_CLIENT (-9) // for API clients
+#define SID_STR (-10) // for sourcing a string with no script item
// Script CTX being sourced or was sourced to define the current function.
EXTERN sctx_T current_sctx INIT(= { 0 COMMA 0 COMMA 0 });
// ID of the current channel making a client API call
EXTERN uint64_t current_channel_id INIT(= 0);
+// ID of the client channel. Used by ui client
+EXTERN uint64_t ui_client_channel_id INIT(= 0);
+
EXTERN bool did_source_packages INIT(= false);
// Scope information for the code that indirectly triggered the current
// provider function call
EXTERN struct caller_scope {
sctx_T script_ctx;
- uint8_t *sourcing_name, *autocmd_fname, *autocmd_match;
+ char *sourcing_name, *autocmd_fname, *autocmd_match;
linenr_T sourcing_lnum;
int autocmd_bufnr;
void *funccalp;
} provider_caller_scope;
EXTERN int provider_call_nesting INIT(= 0);
-
EXTERN int t_colors INIT(= 256); // int value of T_CCO
+// Flags to indicate an additional string for highlight name completion.
+EXTERN int include_none INIT(= 0); // when 1 include "None"
+EXTERN int include_default INIT(= 0); // when 1 include "default"
+EXTERN int include_link INIT(= 0); // when 2 include "link" and "clear"
+
// When highlight_match is true, highlight a match, starting at the cursor
// position. Search_match_lines is the number of lines after the match (0 for
// a match within one line), search_match_endcol the column number of the
@@ -410,10 +418,6 @@ EXTERN vimmenu_T *root_menu INIT(= NULL);
// overruling of menus that the user already defined.
EXTERN int sys_menu INIT(= false);
-// While redrawing the screen this flag is set. It means the screen size
-// ('lines' and 'rows') must not be changed.
-EXTERN int updating_screen INIT(= 0);
-
// All windows are linked in a list. firstwin points to the first entry,
// lastwin to the last entry (can be the same as firstwin) and curwin to the
// currently active window.
@@ -422,7 +426,7 @@ EXTERN win_T *lastwin; // last window
EXTERN win_T *prevwin INIT(= NULL); // previous window
#define ONE_WINDOW (firstwin == lastwin)
#define FOR_ALL_FRAMES(frp, first_frame) \
- for (frp = first_frame; frp != NULL; frp = frp->fr_next) // NOLINT
+ for ((frp) = first_frame; (frp) != NULL; (frp) = (frp)->fr_next) // NOLINT
// When using this macro "break" only breaks out of the inner loop. Use "goto"
// to break out of the tabpage loop.
@@ -445,14 +449,15 @@ EXTERN int aucmd_win_used INIT(= false); // aucmd_win is being used
EXTERN frame_T *topframe; // top of the window frame tree
// Tab pages are alternative topframes. "first_tabpage" points to the first
-// one in the list, "curtab" is the current one.
+// one in the list, "curtab" is the current one. "lastused_tabpage" is the
+// last used one.
EXTERN tabpage_T *first_tabpage;
-EXTERN tabpage_T *lastused_tabpage;
EXTERN tabpage_T *curtab;
+EXTERN tabpage_T *lastused_tabpage;
EXTERN bool redraw_tabline INIT(= false); // need to redraw tabline
// Iterates over all tabs in the tab list
-#define FOR_ALL_TABS(tp) for (tabpage_T *tp = first_tabpage; tp != NULL; tp = tp->tp_next)
+#define FOR_ALL_TABS(tp) for (tabpage_T *(tp) = first_tabpage; (tp) != NULL; (tp) = (tp)->tp_next)
// All buffers are linked in a list. 'firstbuf' points to the first entry,
// 'lastbuf' to the last entry and 'curbuf' to the currently active buffer.
@@ -468,8 +473,7 @@ EXTERN buf_T *curbuf INIT(= NULL); // currently active buffer
// Iterate through all the signs placed in a buffer
#define FOR_ALL_SIGNS_IN_BUF(buf, sign) \
- for (sign = buf->b_signlist; sign != NULL; sign = sign->se_next) // NOLINT
-
+ for ((sign) = (buf)->b_signlist; (sign) != NULL; (sign) = (sign)->se_next) // NOLINT
// List of files being edited (global argument list). curwin->w_alist points
// to this when the window is using the global argument list.
@@ -495,15 +499,17 @@ EXTERN int v_dying INIT(= 0);
EXTERN int stdin_isatty INIT(= true);
// is stdout a terminal?
EXTERN int stdout_isatty INIT(= true);
+/// filedesc set by embedder for reading first buffer like `cmd | nvim -`
+EXTERN int stdin_fd INIT(= -1);
+
// true when doing full-screen output, otherwise only writing some messages.
-// volatile because it is used in a signal handler.
-EXTERN volatile int full_screen INIT(= false);
+EXTERN int full_screen INIT(= false);
/// Non-zero when only "safe" commands are allowed, e.g. when sourcing .exrc or
/// .vimrc in current directory.
EXTERN int secure INIT(= 0);
-/// Non-zero when changing text and jumping to another window/buffer is not
+/// Non-zero when changing text and jumping to another window or editing another buffer is not
/// allowed.
EXTERN int textlock INIT(= 0);
@@ -524,6 +530,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.
@@ -588,8 +596,8 @@ EXTERN pos_T Insstart; // This is where the latest
// op_insert(), to detect correctly where inserting by the user started.
EXTERN pos_T Insstart_orig;
-// Stuff for VREPLACE mode.
-EXTERN int orig_line_count INIT(= 0); // Line count when "gR" started
+// Stuff for MODE_VREPLACE state.
+EXTERN linenr_T orig_line_count INIT(= 0); // Line count when "gR" started
EXTERN int vr_lines_changed INIT(= 0); // #Lines changed by "gR" so far
// increase around internal delete/replace
@@ -607,20 +615,20 @@ EXTERN int inhibit_delete_count INIT(= 0);
#define DBCS_CHT 950 // taiwan
#define DBCS_CHTU 9950 // euc-tw
#define DBCS_2BYTE 1 // 2byte-
-#define DBCS_DEBUG -1
+#define DBCS_DEBUG (-1)
/// Encoding used when 'fencs' is set to "default"
EXTERN char_u *fenc_default INIT(= NULL);
/// "State" is the main state of Vim.
/// There are other variables that modify the state:
-/// Visual_mode: When State is NORMAL or INSERT.
-/// finish_op : When State is NORMAL, after typing the operator and
+/// Visual_mode: When State is MODE_NORMAL or MODE_INSERT.
+/// finish_op : When State is MODE_NORMAL, after typing the operator and
/// before typing the motion command.
/// motion_force: Last motion_force from do_pending_operator()
/// debug_mode: Debug mode
-EXTERN int State INIT(= NORMAL); // This is the current state of the
- // command interpreter.
+EXTERN int State INIT(= MODE_NORMAL);
+
EXTERN bool debug_mode INIT(= false);
EXTERN bool finish_op INIT(= false); // true while an operator is pending
EXTERN long opcount INIT(= 0); // count for pending operator
@@ -628,14 +636,24 @@ EXTERN int motion_force INIT(=0); // motion force for pending operator
// Ex Mode (Q) state
EXTERN bool exmode_active INIT(= false); // true if Ex mode is active
+
+/// Flag set when normal_check() should return 0 when entering Ex mode.
+EXTERN bool pending_exmode_active INIT(= false);
+
EXTERN bool ex_no_reprint INIT(=false); // No need to print after z or p.
+// 'inccommand' command preview state
+EXTERN bool cmdpreview INIT(= false);
+
EXTERN int reg_recording INIT(= 0); // register for recording or zero
EXTERN int reg_executing INIT(= 0); // register being executed or zero
+// Flag set when peeking a character and found the end of executed register
+EXTERN bool pending_end_reg_executing INIT(= false);
EXTERN int reg_recorded INIT(= 0); // last recorded register or zero
EXTERN int no_mapping INIT(= false); // currently no mapping allowed
EXTERN int no_zero_mapping INIT(= 0); // mapping zero not allowed
+EXTERN int allow_keys INIT(= false); // allow key codes when no_mapping is set
EXTERN int no_u_sync INIT(= 0); // Don't call u_sync()
EXTERN int u_sync_once INIT(= 0); // Call u_sync() once when evaluating
// an expression.
@@ -653,6 +671,7 @@ EXTERN bool ins_at_eol INIT(= false); // put cursor after eol when
EXTERN bool no_abbr INIT(= true); // true when no abbreviations loaded
EXTERN int mapped_ctrl_c INIT(= 0); // Modes where CTRL-C is mapped.
+EXTERN bool ctrl_c_interrupts INIT(= true); // CTRL-C sets got_int
EXTERN cmdmod_T cmdmod; // Ex command modifiers
@@ -667,11 +686,8 @@ EXTERN bool cmd_silent INIT(= false); // don't echo the command line
#define SEA_QUIT 2 // quit editing the file
#define SEA_RECOVER 3 // recover the file
-EXTERN int swap_exists_action INIT(= SEA_NONE);
-// For dialog when swap file already
-// exists.
-EXTERN bool swap_exists_did_quit INIT(= false);
-// Selected "quit" at the dialog.
+EXTERN int swap_exists_action INIT(= SEA_NONE); ///< For dialog when swap file already exists.
+EXTERN bool swap_exists_did_quit INIT(= false); ///< Selected "quit" at the dialog.
EXTERN char_u IObuff[IOSIZE]; ///< Buffer for sprintf, I/O, etc.
EXTERN char_u NameBuff[MAXPATHL]; ///< Buffer for expanding file names
@@ -696,7 +712,7 @@ EXTERN typebuf_T typebuf INIT(= { NULL, NULL, 0, 0, 0, 0, 0, 0, 0 });
EXTERN int ex_normal_busy INIT(= 0); // recursiveness of ex_normal()
EXTERN int ex_normal_lock INIT(= 0); // forbid use of ex_normal()
EXTERN int ignore_script INIT(= false); // ignore script input
-EXTERN int stop_insert_mode; // for ":stopinsert" and 'insertmode'
+EXTERN int stop_insert_mode; // for ":stopinsert"
EXTERN bool KeyTyped; // true if user typed current char
EXTERN int KeyStuffed; // true if current char from stuffbuf
EXTERN int maptick INIT(= 0); // tick for each non-mapped char
@@ -711,9 +727,9 @@ EXTERN bool need_highlight_changed INIT(= true);
EXTERN FILE *scriptout INIT(= NULL); ///< Stream to write script to.
-// volatile because it is used in a signal handler.
-EXTERN volatile int got_int INIT(= false); // set to true when interrupt
- // signal occurred
+// Note that even when handling SIGINT, volatile is not necessary because the
+// callback is not called directly from the signal handlers.
+EXTERN bool got_int INIT(= false); // set to true when interrupt signal occurred
EXTERN bool bangredo INIT(= false); // set to true with ! command
EXTERN int searchcmdlen; // length of previous search cmd
EXTERN int reg_do_extmatch INIT(= 0); // Used when compiling regexp:
@@ -724,27 +740,27 @@ EXTERN reg_extmatch_T *re_extmatch_in INIT(= NULL);
// Set by vim_regexec() to store \z\(...\) matches
EXTERN reg_extmatch_T *re_extmatch_out INIT(= NULL);
-EXTERN bool did_outofmem_msg INIT(= false);
-// set after out of memory msg
-EXTERN bool did_swapwrite_msg INIT(= false);
-// set after swap write error msg
-EXTERN int global_busy INIT(= 0); // set when :global is executing
-EXTERN bool listcmd_busy INIT(= false); // set when :argdo, :windo or
- // :bufdo is executing
-EXTERN bool need_start_insertmode INIT(= false);
-// start insert mode soon
-EXTERN char *last_mode INIT(= NULL);
+EXTERN bool did_outofmem_msg INIT(= false); ///< set after out of memory msg
+EXTERN bool did_swapwrite_msg INIT(= false); ///< set after swap write error msg
+EXTERN int global_busy INIT(= 0); ///< set when :global is executing
+EXTERN bool listcmd_busy INIT(= false); ///< set when :argdo, :windo or :bufdo is executing
+EXTERN bool need_start_insertmode INIT(= false); ///< start insert mode soon
+
+#define MODE_MAX_LENGTH 4 // max mode length returned in get_mode(),
+ // including the terminating NUL
+
+EXTERN char last_mode[MODE_MAX_LENGTH] INIT(= "n");
EXTERN char_u *last_cmdline INIT(= NULL); // last command line (for ":)
EXTERN char_u *repeat_cmdline INIT(= NULL); // command line for "."
EXTERN char_u *new_last_cmdline INIT(= NULL); // new value for last_cmdline
-EXTERN char_u *autocmd_fname INIT(= NULL); // fname for <afile> on cmdline
+EXTERN char *autocmd_fname INIT(= NULL); // fname for <afile> on cmdline
EXTERN int autocmd_bufnr INIT(= 0); // fnum for <abuf> on cmdline
-EXTERN char_u *autocmd_match INIT(= NULL); // name for <amatch> on cmdline
+EXTERN char *autocmd_match INIT(= NULL); // name for <amatch> on cmdline
EXTERN bool did_cursorhold INIT(= false); // set when CursorHold t'gerd
EXTERN int postponed_split INIT(= 0); // for CTRL-W CTRL-] command
EXTERN int postponed_split_flags INIT(= 0); // args for win_split()
-EXTERN int postponed_split_tab INIT(= 0); // cmdmod.tab
+EXTERN int postponed_split_tab INIT(= 0); // cmdmod.cmod_tab
EXTERN int g_do_tagpreview INIT(= 0); // for tag preview commands:
// height of preview window
EXTERN bool g_tag_at_cursor INIT(= false); // whether the tag command comes
@@ -753,8 +769,7 @@ EXTERN bool g_tag_at_cursor INIT(= false); // whether the tag command comes
EXTERN int replace_offset INIT(= 0); // offset for replace_push()
-EXTERN char_u *escape_chars INIT(= (char_u *)" \t\\\"|");
-// need backslash in cmd line
+EXTERN char_u *escape_chars INIT(= (char_u *)" \t\\\"|"); // need backslash in cmd line
EXTERN int keep_help_flag INIT(= false); // doing :ta from help file
@@ -780,7 +795,6 @@ enum {
WM_LIST = 3, ///< cmdline CTRL-D
};
-
// Some file names are stored in pathdef.c, which is generated from the
// Makefile to make their value depend on the Makefile.
#ifdef HAVE_PATHDEF
@@ -794,7 +808,7 @@ extern char_u *compiled_sys;
// When a window has a local directory, the absolute path of the global
// current directory is stored here (in allocated memory). If the current
// directory is not a local directory, globaldir is NULL.
-EXTERN char_u *globaldir INIT(= NULL);
+EXTERN char *globaldir INIT(= NULL);
EXTERN char *last_chdir_reason INIT(= NULL);
@@ -829,11 +843,9 @@ EXTERN bool no_hlsearch INIT(= false);
// Page number used for %N in 'pageheader' and 'guitablabel'.
EXTERN linenr_T printer_page_num;
-
EXTERN bool typebuf_was_filled INIT(= false); // received text from client
// or from feedkeys()
-
#ifdef BACKSLASH_IN_FILENAME
EXTERN char psepc INIT(= '\\'); // normal path separator character
EXTERN char psepcN INIT(= '/'); // abnormal path separator character
@@ -861,7 +873,8 @@ EXTERN char e_api_spawn_failed[] INIT(= N_("E903: Could not spawn API job"));
EXTERN char e_argreq[] INIT(= N_("E471: Argument required"));
EXTERN char e_backslash[] INIT(= N_("E10: \\ should be followed by /, ? or &"));
EXTERN char e_cmdwin[] INIT(= N_("E11: Invalid in command-line window; <CR> executes, CTRL-C quits"));
-EXTERN char e_curdir[] INIT(= N_( "E12: Command not allowed from exrc/vimrc in current dir or tag search"));
+EXTERN char e_curdir[] INIT(= N_("E12: Command not allowed from exrc/vimrc in current dir or tag search"));
+EXTERN char e_command_too_recursive[] INIT(= N_("E169: Command too recursive"));
EXTERN char e_endif[] INIT(= N_("E171: Missing :endif"));
EXTERN char e_endtry[] INIT(= N_("E600: Missing :endtry"));
EXTERN char e_endwhile[] INIT(= N_("E170: Missing :endwhile"));
@@ -946,6 +959,7 @@ EXTERN char e_listdictblobarg[] INIT(= N_("E896: Argument of %s must be a List,
EXTERN char e_readerrf[] INIT(= N_("E47: Error while reading errorfile"));
EXTERN char e_sandbox[] INIT(= N_("E48: Not allowed in sandbox"));
EXTERN char e_secure[] INIT(= N_("E523: Not allowed here"));
+EXTERN char e_textlock[] INIT(= N_("E565: Not allowed to change text or change window"));
EXTERN char e_screenmode[] INIT(= N_("E359: Screen mode setting not supported"));
EXTERN char e_scroll[] INIT(= N_("E49: Invalid scroll size"));
EXTERN char e_shellempty[] INIT(= N_("E91: 'shell' option is empty"));
@@ -976,15 +990,18 @@ EXTERN char e_notset[] INIT(= N_("E764: Option '%s' is not set"));
EXTERN char e_invalidreg[] INIT(= N_("E850: Invalid register name"));
EXTERN char e_dirnotf[] INIT(= N_("E919: Directory not found in '%s': \"%s\""));
EXTERN char e_au_recursive[] INIT(= N_("E952: Autocommand caused recursive behavior"));
+EXTERN char e_menuothermode[] INIT(= N_("E328: Menu only exists in another mode"));
EXTERN char e_autocmd_close[] INIT(= N_("E813: Cannot close autocmd window"));
+EXTERN char e_listarg[] INIT(= N_("E686: Argument of %s must be a List"));
EXTERN char e_unsupportedoption[] INIT(= N_("E519: Option not supported"));
EXTERN char e_fnametoolong[] INIT(= N_("E856: Filename too long"));
EXTERN char e_float_as_string[] INIT(= N_("E806: using Float as a String"));
+EXTERN char e_cannot_edit_other_buf[] INIT(= N_("E788: Not allowed to edit another buffer now"));
EXTERN char e_autocmd_err[] INIT(= N_("E5500: autocmd has thrown an exception: %s"));
EXTERN char e_cmdmap_err[] INIT(= N_("E5520: <Cmd> mapping must end with <CR>"));
-EXTERN char e_cmdmap_repeated[] INIT(= N_("E5521: <Cmd> mapping must end with <CR> before second <Cmd>"));
-EXTERN char e_cmdmap_key[] INIT(= N_("E5522: <Cmd> mapping must not include %s key"));
+EXTERN char e_cmdmap_repeated[]
+INIT(= N_("E5521: <Cmd> mapping must end with <CR> before second <Cmd>"));
EXTERN char e_api_error[] INIT(= N_("E5555: API call: %s"));
@@ -997,8 +1014,15 @@ EXTERN char e_non_empty_string_required[] INIT(= N_("E1142: Non-empty string req
EXTERN char e_cannot_define_autocommands_for_all_events[] INIT(= N_("E1155: Cannot define autocommands for ALL events"));
+EXTERN char e_resulting_text_too_long[] INIT(= N_("E1240: Resulting text too long"));
+
+EXTERN char e_line_number_out_of_range[] INIT(= N_("E1247: Line number out of range"));
+
EXTERN char e_highlight_group_name_too_long[] INIT(= N_("E1249: Highlight group name too long"));
+EXTERN char e_undobang_cannot_redo_or_move_branch[]
+INIT(= N_("E5767: Cannot use :undo! to redo or move to a different undo branch"));
+
EXTERN char top_bot_msg[] INIT(= N_("search hit TOP, continuing at BOTTOM"));
EXTERN char bot_top_msg[] INIT(= N_("search hit BOTTOM, continuing at TOP"));
@@ -1014,7 +1038,7 @@ EXTERN FILE *time_fd INIT(= NULL); // where to write startup timing
// the warning.
EXTERN int vim_ignored;
-// Start a msgpack-rpc channel over stdin/stdout.
+// stdio is an RPC channel (--embed).
EXTERN bool embedded_mode INIT(= false);
// Do not start a UI nor read/write to stdio (unless embedding).
EXTERN bool headless_mode INIT(= false);
@@ -1056,4 +1080,6 @@ typedef enum {
// Only filled for Win32.
EXTERN char windowsVersion[20] INIT(= { 0 });
+EXTERN int exit_need_delay INIT(= 0);
+
#endif // NVIM_GLOBALS_H
diff --git a/src/nvim/grid.c b/src/nvim/grid.c
new file mode 100644
index 0000000000..1268f987e1
--- /dev/null
+++ b/src/nvim/grid.c
@@ -0,0 +1,785 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+#include "nvim/arabic.h"
+#include "nvim/grid.h"
+#include "nvim/highlight.h"
+#include "nvim/screen.h"
+#include "nvim/ui.h"
+#include "nvim/vim.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "grid.c.generated.h"
+#endif
+
+// temporary buffer for rendering a single screenline, so it can be
+// compared with previous contents to calculate smallest delta.
+// Per-cell attributes
+static size_t linebuf_size = 0;
+
+/// Determine if dedicated window grid should be used or the default_grid
+///
+/// If UI did not request multigrid support, draw all windows on the
+/// default_grid.
+///
+/// NB: this function can only been used with window grids in a context where
+/// win_grid_alloc already has been called!
+///
+/// If the default_grid is used, adjust window relative positions to global
+/// screen positions.
+void grid_adjust(ScreenGrid **grid, int *row_off, int *col_off)
+{
+ if ((*grid)->target) {
+ *row_off += (*grid)->row_offset;
+ *col_off += (*grid)->col_offset;
+ *grid = (*grid)->target;
+ }
+}
+
+/// Put a unicode char, and up to MAX_MCO composing chars, in a screen cell.
+int schar_from_cc(char_u *p, int c, int u8cc[MAX_MCO])
+{
+ int len = utf_char2bytes(c, (char *)p);
+ for (int i = 0; i < MAX_MCO; i++) {
+ if (u8cc[i] == 0) {
+ break;
+ }
+ len += utf_char2bytes(u8cc[i], (char *)p + len);
+ }
+ p[len] = 0;
+ return len;
+}
+
+/// clear a line in the grid starting at "off" until "width" characters
+/// are cleared.
+void grid_clear_line(ScreenGrid *grid, size_t off, int width, bool valid)
+{
+ for (int col = 0; col < width; col++) {
+ schar_from_ascii(grid->chars[off + (size_t)col], ' ');
+ }
+ int fill = valid ? 0 : -1;
+ (void)memset(grid->attrs + off, fill, (size_t)width * sizeof(sattr_T));
+}
+
+void grid_invalidate(ScreenGrid *grid)
+{
+ (void)memset(grid->attrs, -1, sizeof(sattr_T) * (size_t)grid->rows * (size_t)grid->cols);
+}
+
+bool grid_invalid_row(ScreenGrid *grid, int row)
+{
+ return grid->attrs[grid->line_offset[row]] < 0;
+}
+
+static int line_off2cells(schar_T *line, size_t off, size_t max_off)
+{
+ return (off + 1 < max_off && line[off + 1][0] == 0) ? 2 : 1;
+}
+
+/// Return number of display cells for char at grid->chars[off].
+/// We make sure that the offset used is less than "max_off".
+static int grid_off2cells(ScreenGrid *grid, size_t off, size_t max_off)
+{
+ return line_off2cells(grid->chars, off, max_off);
+}
+
+/// Return true if the character at "row"/"col" on the screen is the left side
+/// of a double-width character.
+///
+/// Caller must make sure "row" and "col" are not invalid!
+bool grid_lefthalve(ScreenGrid *grid, int row, int col)
+{
+ grid_adjust(&grid, &row, &col);
+
+ return grid_off2cells(grid, grid->line_offset[row] + (size_t)col,
+ grid->line_offset[row] + (size_t)grid->cols) > 1;
+}
+
+/// Correct a position on the screen, if it's the right half of a double-wide
+/// char move it to the left half. Returns the corrected column.
+int grid_fix_col(ScreenGrid *grid, int col, int row)
+{
+ int coloff = 0;
+ grid_adjust(&grid, &row, &coloff);
+
+ col += coloff;
+ if (grid->chars != NULL && col > 0
+ && grid->chars[grid->line_offset[row] + (size_t)col][0] == 0) {
+ return col - 1 - coloff;
+ }
+ return col - coloff;
+}
+
+/// output a single character directly to the grid
+void grid_putchar(ScreenGrid *grid, int c, int row, int col, int attr)
+{
+ char buf[MB_MAXBYTES + 1];
+
+ buf[utf_char2bytes(c, buf)] = NUL;
+ grid_puts(grid, (char_u *)buf, row, col, attr);
+}
+
+/// get a single character directly from grid.chars into "bytes[]".
+/// Also return its attribute in *attrp;
+void grid_getbytes(ScreenGrid *grid, int row, int col, char_u *bytes, int *attrp)
+{
+ size_t off;
+
+ grid_adjust(&grid, &row, &col);
+
+ // safety check
+ if (grid->chars != NULL && row < grid->rows && col < grid->cols) {
+ off = grid->line_offset[row] + (size_t)col;
+ *attrp = grid->attrs[off];
+ schar_copy(bytes, grid->chars[off]);
+ }
+}
+
+/// put string '*text' on the window grid at position 'row' and 'col', with
+/// attributes 'attr', and update chars[] and attrs[].
+/// Note: only outputs within one row, message is truncated at grid boundary!
+/// Note: if grid, row and/or col is invalid, nothing is done.
+void grid_puts(ScreenGrid *grid, char_u *text, int row, int col, int attr)
+{
+ grid_puts_len(grid, text, -1, row, col, attr);
+}
+
+static ScreenGrid *put_dirty_grid = NULL;
+static int put_dirty_row = -1;
+static int put_dirty_first = INT_MAX;
+static int put_dirty_last = 0;
+
+/// Start a group of grid_puts_len calls that builds a single grid line.
+///
+/// Must be matched with a grid_puts_line_flush call before moving to
+/// another line.
+void grid_puts_line_start(ScreenGrid *grid, int row)
+{
+ int col = 0; // unused
+ grid_adjust(&grid, &row, &col);
+ assert(put_dirty_row == -1);
+ put_dirty_row = row;
+ put_dirty_grid = grid;
+}
+
+void grid_put_schar(ScreenGrid *grid, int row, int col, char_u *schar, int attr)
+{
+ assert(put_dirty_row == row);
+ size_t off = grid->line_offset[row] + (size_t)col;
+ if (grid->attrs[off] != attr || schar_cmp(grid->chars[off], schar)) {
+ schar_copy(grid->chars[off], schar);
+ grid->attrs[off] = attr;
+
+ put_dirty_first = MIN(put_dirty_first, col);
+ // TODO(bfredl): Y U NO DOUBLEWIDTH?
+ put_dirty_last = MAX(put_dirty_last, col + 1);
+ }
+}
+
+/// like grid_puts(), but output "text[len]". When "len" is -1 output up to
+/// a NUL.
+void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, int col, int attr)
+{
+ size_t off;
+ char_u *ptr = text;
+ int len = textlen;
+ int c;
+ size_t max_off;
+ int mbyte_blen = 1;
+ int mbyte_cells = 1;
+ int u8c = 0;
+ int u8cc[MAX_MCO];
+ bool clear_next_cell = false;
+ int prev_c = 0; // previous Arabic character
+ int pc, nc, nc1;
+ int pcc[MAX_MCO];
+ int need_redraw;
+ bool do_flush = false;
+
+ grid_adjust(&grid, &row, &col);
+
+ // Safety check. The check for negative row and column is to fix issue
+ // vim/vim#4102. TODO(neovim): find out why row/col could be negative.
+ if (grid->chars == NULL
+ || row >= grid->rows || row < 0
+ || col >= grid->cols || col < 0) {
+ return;
+ }
+
+ if (put_dirty_row == -1) {
+ grid_puts_line_start(grid, row);
+ do_flush = true;
+ } else {
+ if (grid != put_dirty_grid || row != put_dirty_row) {
+ abort();
+ }
+ }
+ off = grid->line_offset[row] + (size_t)col;
+
+ // When drawing over the right half of a double-wide char clear out the
+ // left half. Only needed in a terminal.
+ if (grid != &default_grid && col == 0 && grid_invalid_row(grid, row)) {
+ // redraw the previous cell, make it empty
+ put_dirty_first = -1;
+ put_dirty_last = MAX(put_dirty_last, 1);
+ }
+
+ max_off = grid->line_offset[row] + (size_t)grid->cols;
+ while (col < grid->cols
+ && (len < 0 || (int)(ptr - text) < len)
+ && *ptr != NUL) {
+ c = *ptr;
+ // check if this is the first byte of a multibyte
+ if (len > 0) {
+ mbyte_blen = utfc_ptr2len_len(ptr, (int)((text + len) - ptr));
+ } else {
+ mbyte_blen = utfc_ptr2len((char *)ptr);
+ }
+ if (len >= 0) {
+ u8c = utfc_ptr2char_len(ptr, u8cc, (int)((text + len) - ptr));
+ } else {
+ u8c = utfc_ptr2char(ptr, u8cc);
+ }
+ mbyte_cells = utf_char2cells(u8c);
+ if (p_arshape && !p_tbidi && ARABIC_CHAR(u8c)) {
+ // Do Arabic shaping.
+ if (len >= 0 && (int)(ptr - text) + mbyte_blen >= len) {
+ // Past end of string to be displayed.
+ nc = NUL;
+ nc1 = NUL;
+ } else {
+ nc = utfc_ptr2char_len(ptr + mbyte_blen, pcc,
+ (int)((text + len) - ptr - mbyte_blen));
+ nc1 = pcc[0];
+ }
+ pc = prev_c;
+ prev_c = u8c;
+ u8c = arabic_shape(u8c, &c, &u8cc[0], nc, nc1, pc);
+ } else {
+ prev_c = u8c;
+ }
+ if (col + mbyte_cells > grid->cols) {
+ // Only 1 cell left, but character requires 2 cells:
+ // display a '>' in the last column to avoid wrapping. */
+ c = '>';
+ u8c = '>';
+ u8cc[0] = 0;
+ mbyte_cells = 1;
+ }
+
+ schar_T buf;
+ schar_from_cc(buf, u8c, u8cc);
+
+ need_redraw = schar_cmp(grid->chars[off], buf)
+ || (mbyte_cells == 2 && grid->chars[off + 1][0] != 0)
+ || grid->attrs[off] != attr
+ || exmode_active;
+
+ if (need_redraw) {
+ // When at the end of the text and overwriting a two-cell
+ // character with a one-cell character, need to clear the next
+ // cell. Also when overwriting the left half of a two-cell char
+ // with the right half of a two-cell char. Do this only once
+ // (utf8_off2cells() may return 2 on the right half).
+ if (clear_next_cell) {
+ clear_next_cell = false;
+ } else if ((len < 0 ? ptr[mbyte_blen] == NUL : ptr + mbyte_blen >= text + len)
+ && ((mbyte_cells == 1
+ && grid_off2cells(grid, off, max_off) > 1)
+ || (mbyte_cells == 2
+ && grid_off2cells(grid, off, max_off) == 1
+ && grid_off2cells(grid, off + 1, max_off) > 1))) {
+ clear_next_cell = true;
+ }
+
+ // When at the start of the text and overwriting the right half of a
+ // two-cell character in the same grid, truncate that into a '>'.
+ if (ptr == text && col > 0 && grid->chars[off][0] == 0) {
+ grid->chars[off - 1][0] = '>';
+ grid->chars[off - 1][1] = 0;
+ }
+
+ schar_copy(grid->chars[off], buf);
+ grid->attrs[off] = attr;
+ if (mbyte_cells == 2) {
+ grid->chars[off + 1][0] = 0;
+ grid->attrs[off + 1] = attr;
+ }
+ put_dirty_first = MIN(put_dirty_first, col);
+ put_dirty_last = MAX(put_dirty_last, col + mbyte_cells);
+ }
+
+ off += (size_t)mbyte_cells;
+ col += mbyte_cells;
+ ptr += mbyte_blen;
+ if (clear_next_cell) {
+ // This only happens at the end, display one space next.
+ ptr = (char_u *)" ";
+ len = -1;
+ }
+ }
+
+ if (do_flush) {
+ grid_puts_line_flush(true);
+ }
+}
+
+/// End a group of grid_puts_len calls and send the screen buffer to the UI
+/// layer.
+///
+/// @param set_cursor Move the visible cursor to the end of the changed region.
+/// This is a workaround for not yet refactored code paths
+/// and shouldn't be used in new code.
+void grid_puts_line_flush(bool set_cursor)
+{
+ assert(put_dirty_row != -1);
+ if (put_dirty_first < put_dirty_last) {
+ if (set_cursor) {
+ ui_grid_cursor_goto(put_dirty_grid->handle, put_dirty_row,
+ MIN(put_dirty_last, put_dirty_grid->cols - 1));
+ }
+ if (!put_dirty_grid->throttled) {
+ ui_line(put_dirty_grid, put_dirty_row, put_dirty_first, put_dirty_last,
+ put_dirty_last, 0, false);
+ } else if (put_dirty_grid->dirty_col) {
+ if (put_dirty_last > put_dirty_grid->dirty_col[put_dirty_row]) {
+ put_dirty_grid->dirty_col[put_dirty_row] = put_dirty_last;
+ }
+ }
+ put_dirty_first = INT_MAX;
+ put_dirty_last = 0;
+ }
+ put_dirty_row = -1;
+ put_dirty_grid = NULL;
+}
+
+/// Fill the grid from "start_row" to "end_row" (exclusive), from "start_col"
+/// to "end_col" (exclusive) with character "c1" in first column followed by
+/// "c2" in the other columns. Use attributes "attr".
+void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col, int end_col, int c1,
+ int c2, int attr)
+{
+ schar_T sc;
+
+ int row_off = 0, col_off = 0;
+ grid_adjust(&grid, &row_off, &col_off);
+ start_row += row_off;
+ end_row += row_off;
+ start_col += col_off;
+ end_col += col_off;
+
+ // safety check
+ if (end_row > grid->rows) {
+ end_row = grid->rows;
+ }
+ if (end_col > grid->cols) {
+ end_col = grid->cols;
+ }
+
+ // nothing to do
+ if (start_row >= end_row || start_col >= end_col) {
+ return;
+ }
+
+ for (int row = start_row; row < end_row; row++) {
+ // When drawing over the right half of a double-wide char clear
+ // out the left half. When drawing over the left half of a
+ // double wide-char clear out the right half. Only needed in a
+ // terminal.
+ if (start_col > 0 && grid_fix_col(grid, start_col, row) != start_col) {
+ grid_puts_len(grid, (char_u *)" ", 1, row, start_col - 1, 0);
+ }
+ if (end_col < grid->cols
+ && grid_fix_col(grid, end_col, row) != end_col) {
+ grid_puts_len(grid, (char_u *)" ", 1, row, end_col, 0);
+ }
+
+ // if grid was resized (in ext_multigrid mode), the UI has no redraw updates
+ // for the newly resized grid. It is better mark everything as dirty and
+ // send all the updates.
+ int dirty_first = INT_MAX;
+ int dirty_last = 0;
+
+ int col = start_col;
+ schar_from_char(sc, c1);
+ size_t lineoff = grid->line_offset[row];
+ for (col = start_col; col < end_col; col++) {
+ size_t off = lineoff + (size_t)col;
+ if (schar_cmp(grid->chars[off], sc)
+ || grid->attrs[off] != attr) {
+ schar_copy(grid->chars[off], sc);
+ grid->attrs[off] = attr;
+ if (dirty_first == INT_MAX) {
+ dirty_first = col;
+ }
+ dirty_last = col + 1;
+ }
+ if (col == start_col) {
+ schar_from_char(sc, c2);
+ }
+ }
+ if (dirty_last > dirty_first) {
+ // TODO(bfredl): support a cleared suffix even with a batched line?
+ if (put_dirty_row == row) {
+ put_dirty_first = MIN(put_dirty_first, dirty_first);
+ put_dirty_last = MAX(put_dirty_last, dirty_last);
+ } else if (grid->throttled) {
+ // Note: assumes msg_grid is the only throttled grid
+ assert(grid == &msg_grid);
+ int dirty = 0;
+ if (attr != HL_ATTR(HLF_MSG) || c2 != ' ') {
+ dirty = dirty_last;
+ } else if (c1 != ' ') {
+ dirty = dirty_first + 1;
+ }
+ if (grid->dirty_col && dirty > grid->dirty_col[row]) {
+ grid->dirty_col[row] = dirty;
+ }
+ } else {
+ int last = c2 != ' ' ? dirty_last : dirty_first + (c1 != ' ');
+ ui_line(grid, row, dirty_first, last, dirty_last, attr, false);
+ }
+ }
+
+ if (end_col == grid->cols) {
+ grid->line_wraps[row] = false;
+ }
+ }
+}
+
+/// Check whether the given character needs redrawing:
+/// - the (first byte of the) character is different
+/// - the attributes are different
+/// - the character is multi-byte and the next byte is different
+/// - the character is two cells wide and the second cell differs.
+static int grid_char_needs_redraw(ScreenGrid *grid, size_t off_from, size_t off_to, int cols)
+{
+ return (cols > 0
+ && ((schar_cmp(linebuf_char[off_from], grid->chars[off_to])
+ || linebuf_attr[off_from] != grid->attrs[off_to]
+ || (line_off2cells(linebuf_char, off_from, off_from + (size_t)cols) > 1
+ && schar_cmp(linebuf_char[off_from + 1],
+ grid->chars[off_to + 1])))
+ || rdb_flags & RDB_NODELTA));
+}
+
+/// Move one buffered line to the window grid, but only the characters that
+/// have actually changed. Handle insert/delete character.
+/// "coloff" gives the first column on the grid for this line.
+/// "endcol" gives the columns where valid characters are.
+/// "clear_width" is the width of the window. It's > 0 if the rest of the line
+/// needs to be cleared, negative otherwise.
+/// "rlflag" is TRUE in a rightleft window:
+/// When TRUE and "clear_width" > 0, clear columns 0 to "endcol"
+/// When FALSE and "clear_width" > 0, clear columns "endcol" to "clear_width"
+/// If "wrap" is true, then hint to the UI that "row" contains a line
+/// which has wrapped into the next row.
+void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int clear_width,
+ int rlflag, win_T *wp, int bg_attr, bool wrap)
+{
+ size_t max_off_from;
+ size_t max_off_to;
+ int col = 0;
+ bool redraw_this; // Does character need redraw?
+ bool redraw_next; // redraw_this for next character
+ bool clear_next = false;
+ int char_cells; // 1: normal char
+ // 2: occupies two display cells
+ int start_dirty = -1, end_dirty = 0;
+
+ // TODO(bfredl): check all callsites and eliminate
+ // Check for illegal row and col, just in case
+ if (row >= grid->rows) {
+ row = grid->rows - 1;
+ }
+ if (endcol > grid->cols) {
+ endcol = grid->cols;
+ }
+
+ grid_adjust(&grid, &row, &coloff);
+
+ // Safety check. Avoids clang warnings down the call stack.
+ if (grid->chars == NULL || row >= grid->rows || coloff >= grid->cols) {
+ DLOG("invalid state, skipped");
+ return;
+ }
+
+ size_t off_from = 0;
+ size_t off_to = grid->line_offset[row] + (size_t)coloff;
+ max_off_from = linebuf_size;
+ max_off_to = grid->line_offset[row] + (size_t)grid->cols;
+
+ if (rlflag) {
+ // Clear rest first, because it's left of the text.
+ if (clear_width > 0) {
+ while (col <= endcol && grid->chars[off_to][0] == ' '
+ && grid->chars[off_to][1] == NUL
+ && grid->attrs[off_to] == bg_attr) {
+ off_to++;
+ col++;
+ }
+ if (col <= endcol) {
+ grid_fill(grid, row, row + 1, col + coloff, endcol + coloff + 1, ' ', ' ', bg_attr);
+ }
+ }
+ col = endcol + 1;
+ off_to = grid->line_offset[row] + (size_t)col + (size_t)coloff;
+ off_from += (size_t)col;
+ endcol = (clear_width > 0 ? clear_width : -clear_width);
+ }
+
+ if (bg_attr) {
+ for (int c = col; c < endcol; c++) {
+ linebuf_attr[off_from + (size_t)c] =
+ hl_combine_attr(bg_attr, linebuf_attr[off_from + (size_t)c]);
+ }
+ }
+
+ redraw_next = grid_char_needs_redraw(grid, off_from, off_to, endcol - col);
+
+ while (col < endcol) {
+ char_cells = 1;
+ if (col + 1 < endcol) {
+ char_cells = line_off2cells(linebuf_char, off_from, max_off_from);
+ }
+ redraw_this = redraw_next;
+ redraw_next = grid_char_needs_redraw(grid, off_from + (size_t)char_cells,
+ off_to + (size_t)char_cells,
+ endcol - col - char_cells);
+
+ if (redraw_this) {
+ if (start_dirty == -1) {
+ start_dirty = col;
+ }
+ end_dirty = col + char_cells;
+ // When writing a single-width character over a double-width
+ // character and at the end of the redrawn text, need to clear out
+ // the right half of the old character.
+ // Also required when writing the right half of a double-width
+ // char over the left half of an existing one
+ if (col + char_cells == endcol
+ && ((char_cells == 1
+ && grid_off2cells(grid, off_to, max_off_to) > 1)
+ || (char_cells == 2
+ && grid_off2cells(grid, off_to, max_off_to) == 1
+ && grid_off2cells(grid, off_to + 1, max_off_to) > 1))) {
+ clear_next = true;
+ }
+
+ schar_copy(grid->chars[off_to], linebuf_char[off_from]);
+ if (char_cells == 2) {
+ schar_copy(grid->chars[off_to + 1], linebuf_char[off_from + 1]);
+ }
+
+ grid->attrs[off_to] = linebuf_attr[off_from];
+ // For simplicity set the attributes of second half of a
+ // double-wide character equal to the first half.
+ if (char_cells == 2) {
+ grid->attrs[off_to + 1] = linebuf_attr[off_from];
+ }
+ }
+
+ off_to += (size_t)char_cells;
+ off_from += (size_t)char_cells;
+ col += char_cells;
+ }
+
+ if (clear_next) {
+ // Clear the second half of a double-wide character of which the left
+ // half was overwritten with a single-wide character.
+ schar_from_ascii(grid->chars[off_to], ' ');
+ end_dirty++;
+ }
+
+ int clear_end = -1;
+ if (clear_width > 0 && !rlflag) {
+ // blank out the rest of the line
+ // TODO(bfredl): we could cache winline widths
+ while (col < clear_width) {
+ if (grid->chars[off_to][0] != ' '
+ || grid->chars[off_to][1] != NUL
+ || grid->attrs[off_to] != bg_attr) {
+ grid->chars[off_to][0] = ' ';
+ grid->chars[off_to][1] = NUL;
+ grid->attrs[off_to] = bg_attr;
+ if (start_dirty == -1) {
+ start_dirty = col;
+ end_dirty = col;
+ } else if (clear_end == -1) {
+ end_dirty = endcol;
+ }
+ clear_end = col + 1;
+ }
+ col++;
+ off_to++;
+ }
+ }
+
+ if (clear_width > 0 || wp->w_width != grid->cols) {
+ // If we cleared after the end of the line, it did not wrap.
+ // For vsplit, line wrapping is not possible.
+ grid->line_wraps[row] = false;
+ }
+
+ if (clear_end < end_dirty) {
+ clear_end = end_dirty;
+ }
+ if (start_dirty == -1) {
+ start_dirty = end_dirty;
+ }
+ if (clear_end > start_dirty) {
+ ui_line(grid, row, coloff + start_dirty, coloff + end_dirty, coloff + clear_end,
+ bg_attr, wrap);
+ }
+}
+
+void grid_alloc(ScreenGrid *grid, int rows, int columns, bool copy, bool valid)
+{
+ int new_row;
+ ScreenGrid new = *grid;
+ assert(rows >= 0 && columns >= 0);
+ size_t ncells = (size_t)rows * (size_t)columns;
+ new.chars = xmalloc(ncells * sizeof(schar_T));
+ new.attrs = xmalloc(ncells * sizeof(sattr_T));
+ new.line_offset = xmalloc((size_t)rows * sizeof(*new.line_offset));
+ new.line_wraps = xmalloc((size_t)rows * sizeof(*new.line_wraps));
+
+ new.rows = rows;
+ new.cols = columns;
+
+ for (new_row = 0; new_row < new.rows; new_row++) {
+ new.line_offset[new_row] = (size_t)new_row * (size_t)new.cols;
+ new.line_wraps[new_row] = false;
+
+ grid_clear_line(&new, new.line_offset[new_row], columns, valid);
+
+ if (copy) {
+ // If the screen is not going to be cleared, copy as much as
+ // possible from the old screen to the new one and clear the rest
+ // (used when resizing the window at the "--more--" prompt or when
+ // executing an external command, for the GUI).
+ if (new_row < grid->rows && grid->chars != NULL) {
+ int len = MIN(grid->cols, new.cols);
+ memmove(new.chars + new.line_offset[new_row],
+ grid->chars + grid->line_offset[new_row],
+ (size_t)len * sizeof(schar_T));
+ memmove(new.attrs + new.line_offset[new_row],
+ grid->attrs + grid->line_offset[new_row],
+ (size_t)len * sizeof(sattr_T));
+ }
+ }
+ }
+ grid_free(grid);
+ *grid = new;
+
+ // Share a single scratch buffer for all grids, by
+ // ensuring it is as wide as the widest grid.
+ if (linebuf_size < (size_t)columns) {
+ xfree(linebuf_char);
+ xfree(linebuf_attr);
+ linebuf_char = xmalloc((size_t)columns * sizeof(schar_T));
+ linebuf_attr = xmalloc((size_t)columns * sizeof(sattr_T));
+ linebuf_size = (size_t)columns;
+ }
+}
+
+void grid_free(ScreenGrid *grid)
+{
+ xfree(grid->chars);
+ xfree(grid->attrs);
+ xfree(grid->line_offset);
+ xfree(grid->line_wraps);
+
+ grid->chars = NULL;
+ grid->attrs = NULL;
+ grid->line_offset = NULL;
+ grid->line_wraps = NULL;
+}
+
+/// Doesn't allow reinit, so must only be called by free_all_mem!
+void grid_free_all_mem(void)
+{
+ grid_free(&default_grid);
+ xfree(linebuf_char);
+ xfree(linebuf_attr);
+}
+
+/// (Re)allocates a window grid if size changed while in ext_multigrid mode.
+/// Updates size, offsets and handle for the grid regardless.
+///
+/// If "doclear" is true, don't try to copy from the old grid rather clear the
+/// resized grid.
+void win_grid_alloc(win_T *wp)
+{
+ ScreenGrid *grid = &wp->w_grid;
+ ScreenGrid *grid_allocated = &wp->w_grid_alloc;
+
+ int rows = wp->w_height_inner;
+ int cols = wp->w_width_inner;
+ int total_rows = wp->w_height_outer;
+ int total_cols = wp->w_width_outer;
+
+ bool want_allocation = ui_has(kUIMultigrid) || wp->w_floating;
+ bool has_allocation = (grid_allocated->chars != NULL);
+
+ if (grid->rows != rows) {
+ wp->w_lines_valid = 0;
+ xfree(wp->w_lines);
+ wp->w_lines = xcalloc((size_t)rows + 1, sizeof(wline_T));
+ }
+
+ int was_resized = false;
+ if (want_allocation && (!has_allocation
+ || grid_allocated->rows != total_rows
+ || grid_allocated->cols != total_cols)) {
+ grid_alloc(grid_allocated, total_rows, total_cols,
+ wp->w_grid_alloc.valid, false);
+ grid_allocated->valid = true;
+ if (wp->w_floating && wp->w_float_config.border) {
+ wp->w_redr_border = true;
+ }
+ was_resized = true;
+ } else if (!want_allocation && has_allocation) {
+ // Single grid mode, all rendering will be redirected to default_grid.
+ // Only keep track of the size and offset of the window.
+ grid_free(grid_allocated);
+ grid_allocated->valid = false;
+ was_resized = true;
+ } else if (want_allocation && has_allocation && !wp->w_grid_alloc.valid) {
+ grid_invalidate(grid_allocated);
+ grid_allocated->valid = true;
+ }
+
+ grid->rows = rows;
+ grid->cols = cols;
+
+ if (want_allocation) {
+ grid->target = grid_allocated;
+ grid->row_offset = wp->w_winrow_off;
+ grid->col_offset = wp->w_wincol_off;
+ } else {
+ grid->target = &default_grid;
+ grid->row_offset = wp->w_winrow + wp->w_winrow_off;
+ grid->col_offset = wp->w_wincol + wp->w_wincol_off;
+ }
+
+ // send grid resize event if:
+ // - a grid was just resized
+ // - screen_resize was called and all grid sizes must be sent
+ // - the UI wants multigrid event (necessary)
+ if ((resizing_screen || was_resized) && want_allocation) {
+ ui_call_grid_resize(grid_allocated->handle,
+ grid_allocated->cols, grid_allocated->rows);
+ }
+}
+
+/// assign a handle to the grid. The grid need not be allocated.
+void grid_assign_handle(ScreenGrid *grid)
+{
+ static int last_grid_handle = DEFAULT_GRID_HANDLE;
+
+ // only assign a grid handle if not already
+ if (grid->handle == 0) {
+ grid->handle = ++last_grid_handle;
+ }
+}
diff --git a/src/nvim/grid.h b/src/nvim/grid.h
new file mode 100644
index 0000000000..c38748940d
--- /dev/null
+++ b/src/nvim/grid.h
@@ -0,0 +1,57 @@
+#ifndef NVIM_GRID_H
+#define NVIM_GRID_H
+
+#include <stdbool.h>
+
+#include "nvim/ascii.h"
+#include "nvim/buffer_defs.h"
+#include "nvim/grid_defs.h"
+
+/// By default, all windows are drawn on a single rectangular grid, represented by
+/// this ScreenGrid instance. In multigrid mode each window will have its own
+/// grid, then this is only used for global screen elements that hasn't been
+/// externalized.
+///
+/// Note: before the screen is initialized and when out of memory these can be
+/// NULL.
+EXTERN ScreenGrid default_grid INIT(= SCREEN_GRID_INIT);
+
+#define DEFAULT_GRID_HANDLE 1 // handle for the default_grid
+
+EXTERN schar_T *linebuf_char INIT(= NULL);
+EXTERN sattr_T *linebuf_attr INIT(= NULL);
+
+// Low-level functions to manipulate individual character cells on the
+// screen grid.
+
+/// Put a ASCII character in a screen cell.
+static inline void schar_from_ascii(char_u *p, const char c)
+{
+ p[0] = (char_u)c;
+ p[1] = 0;
+}
+
+/// Put a unicode character in a screen cell.
+static inline int schar_from_char(char_u *p, int c)
+{
+ int len = utf_char2bytes(c, (char *)p);
+ p[len] = NUL;
+ return len;
+}
+
+/// compare the contents of two screen cells.
+static inline int schar_cmp(char_u *sc1, char_u *sc2)
+{
+ return strncmp((char *)sc1, (char *)sc2, sizeof(schar_T));
+}
+
+/// copy the contents of screen cell `sc2` into cell `sc1`
+static inline void schar_copy(char_u *sc1, char_u *sc2)
+{
+ xstrlcpy((char *)sc1, (char *)sc2, sizeof(schar_T));
+}
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "grid.h.generated.h"
+#endif
+#endif // NVIM_GRID_H
diff --git a/src/nvim/grid_defs.h b/src/nvim/grid_defs.h
index bf0a5d63a8..1571340849 100644
--- a/src/nvim/grid_defs.h
+++ b/src/nvim/grid_defs.h
@@ -10,7 +10,7 @@
#define MAX_MCO 6 // fixed value for 'maxcombine'
// The characters and attributes drawn on grids.
-typedef char_u schar_T[(MAX_MCO+1) * 4 + 1];
+typedef char_u schar_T[(MAX_MCO + 1) * 4 + 1];
typedef int sattr_T;
enum {
@@ -21,7 +21,6 @@ enum {
kZIndexCmdlinePopupMenu = 250,
};
-
/// ScreenGrid represents a resizable rectuangular grid displayed by UI clients.
///
/// chars[] contains the UTF-8 text that is currently displayed on the grid.
@@ -50,7 +49,7 @@ struct ScreenGrid {
schar_T *chars;
sattr_T *attrs;
- unsigned *line_offset;
+ size_t *line_offset;
char_u *line_wraps;
// last column that was drawn (not cleared with the default background).
@@ -58,8 +57,8 @@ struct ScreenGrid {
int *dirty_col;
// the size of the allocated grid.
- int Rows;
- int Columns;
+ int rows;
+ int cols;
// The state of the grid is valid. Otherwise it needs to be redrawn.
bool valid;
@@ -111,4 +110,22 @@ struct ScreenGrid {
false, 0, 0, NULL, false, true, 0, \
0, 0, 0, 0, 0, false }
+/// Status line click definition
+typedef struct {
+ enum {
+ kStlClickDisabled = 0, ///< Clicks to this area are ignored.
+ kStlClickTabSwitch, ///< Switch to the given tab.
+ kStlClickTabClose, ///< Close given tab.
+ kStlClickFuncRun, ///< Run user function.
+ } type; ///< Type of the click.
+ int tabnr; ///< Tab page number.
+ char *func; ///< Function to run.
+} StlClickDefinition;
+
+/// Used for tabline clicks
+typedef struct {
+ StlClickDefinition def; ///< Click definition.
+ const char *start; ///< Location where region starts.
+} StlClickRecord;
+
#endif // NVIM_GRID_DEFS_H
diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c
index 6fc70144ac..a4cf65e816 100644
--- a/src/nvim/hardcopy.c
+++ b/src/nvim/hardcopy.c
@@ -22,6 +22,7 @@
#include "nvim/fileio.h"
#include "nvim/garray.h"
#include "nvim/hardcopy.h"
+#include "nvim/highlight_group.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
@@ -115,7 +116,6 @@ static option_table_T printer_opts[OPT_PRINT_NUM_OPTIONS]
}
;
-
static const uint32_t cterm_color_8[8] = {
0x000000, 0xff0000, 0x00ff00, 0xffff00,
0x0000ff, 0xff00ff, 0x00ffff, 0xffffff
@@ -310,12 +310,12 @@ static char *parse_list_options(char_u *option_str, option_table_T *table, size_
*/
stringp = option_str;
while (*stringp) {
- colonp = vim_strchr(stringp, ':');
+ colonp = (char_u *)vim_strchr((char *)stringp, ':');
if (colonp == NULL) {
ret = N_("E550: Missing colon");
break;
}
- commap = vim_strchr(stringp, ',');
+ commap = (char_u *)vim_strchr((char *)stringp, ',');
if (commap == NULL) {
commap = option_str + STRLEN(option_str);
}
@@ -342,7 +342,7 @@ static char *parse_list_options(char_u *option_str, option_table_T *table, size_
break;
}
- table[idx].number = getdigits_int(&p, false, 0);
+ table[idx].number = getdigits_int((char **)&p, false, 0);
}
table[idx].string = p;
@@ -365,7 +365,6 @@ static char *parse_list_options(char_u *option_str, option_table_T *table, size_
return ret;
}
-
/*
* If using a dark background, the colors will probably be too bright to show
* up well on white paper, so reduce their brightness.
@@ -373,8 +372,8 @@ static char *parse_list_options(char_u *option_str, option_table_T *table, size_
static uint32_t darken_rgb(uint32_t rgb)
{
return ((rgb >> 17) << 16)
- + (((rgb & 0xff00) >> 9) << 8)
- + ((rgb & 0xff) >> 1);
+ + (((rgb & 0xff00) >> 9) << 8)
+ + ((rgb & 0xff) >> 1);
}
static uint32_t prt_get_term_color(int colorindex)
@@ -386,30 +385,46 @@ static uint32_t prt_get_term_color(int colorindex)
return cterm_color_8[colorindex % 8];
}
-static void prt_get_attr(int hl_id, prt_text_attr_T *pattr, int modec)
+static uint32_t prt_get_color(int hl_id, int modec)
{
int colorindex;
uint32_t fg_color;
+ const char *color = highlight_color(hl_id, "fg#", 'g');
+ if (color != NULL) {
+ RgbValue rgb = name_to_color(color, &colorindex);
+ if (rgb != -1) {
+ return (uint32_t)rgb;
+ }
+ }
+
+ color = highlight_color(hl_id, "fg", modec);
+ if (color == NULL) {
+ colorindex = 0;
+ } else {
+ colorindex = atoi(color);
+ }
+
+ if (colorindex >= 0 && colorindex < t_colors) {
+ fg_color = prt_get_term_color(colorindex);
+ } else {
+ fg_color = PRCOLOR_BLACK;
+ }
+
+ return fg_color;
+}
+
+static void prt_get_attr(int hl_id, prt_text_attr_T *pattr, int modec)
+{
pattr->bold = (highlight_has_attr(hl_id, HL_BOLD, modec) != NULL);
pattr->italic = (highlight_has_attr(hl_id, HL_ITALIC, modec) != NULL);
pattr->underline = (highlight_has_attr(hl_id, HL_UNDERLINE, modec) != NULL);
pattr->undercurl = (highlight_has_attr(hl_id, HL_UNDERCURL, modec) != NULL);
+ pattr->underdouble = (highlight_has_attr(hl_id, HL_UNDERDOUBLE, modec) != NULL);
+ pattr->underdotted = (highlight_has_attr(hl_id, HL_UNDERDOTTED, modec) != NULL);
+ pattr->underdashed = (highlight_has_attr(hl_id, HL_UNDERDASHED, modec) != NULL);
- {
- const char *color = highlight_color(hl_id, "fg", modec);
- if (color == NULL) {
- colorindex = 0;
- } else {
- colorindex = atoi(color);
- }
-
- if (colorindex >= 0 && colorindex < t_colors) {
- fg_color = prt_get_term_color(colorindex);
- } else {
- fg_color = PRCOLOR_BLACK;
- }
- }
+ uint32_t fg_color = prt_get_color(hl_id, modec);
if (fg_color == PRCOLOR_WHITE) {
fg_color = PRCOLOR_BLACK;
@@ -552,8 +567,8 @@ static void prt_header(prt_settings_T *const psettings, const int pagenum, const
printer_page_num = pagenum;
use_sandbox = was_set_insecurely(curwin, "printheader", 0);
- build_stl_str_hl(curwin, tbuf, (size_t)width + IOSIZE,
- p_header, use_sandbox,
+ build_stl_str_hl(curwin, (char *)tbuf, (size_t)width + IOSIZE,
+ (char *)p_header, use_sandbox,
' ', width, NULL, NULL);
// Reset line numbers
@@ -572,7 +587,7 @@ static void prt_header(prt_settings_T *const psettings, const int pagenum, const
int page_line = 0 - prt_header_height();
mch_print_start_line(true, page_line);
for (char_u *p = tbuf; *p != NUL;) {
- const int l = utfc_ptr2len(p);
+ const int l = utfc_ptr2len((char *)p);
assert(l >= 0);
if (mch_print_text_out(p, (size_t)l)) {
page_line++;
@@ -624,15 +639,15 @@ void ex_hardcopy(exarg_T *eap)
char *errormsg = NULL;
// Expand things like "%.ps".
- if (expand_filename(eap, eap->cmdlinep, &errormsg) == FAIL) {
+ if (expand_filename(eap, (char_u **)eap->cmdlinep, &errormsg) == FAIL) {
if (errormsg != NULL) {
emsg(errormsg);
}
return;
}
- settings.outfile = skipwhite(eap->arg + 1);
+ settings.outfile = (char_u *)skipwhite(eap->arg + 1);
} else if (*eap->arg != NUL) {
- settings.arguments = eap->arg;
+ settings.arguments = (char_u *)eap->arg;
}
/*
@@ -643,8 +658,9 @@ void ex_hardcopy(exarg_T *eap)
* PS.)
*/
if (mch_print_init(&settings,
- curbuf->b_fname == NULL ? buf_spname(curbuf) : curbuf->b_sfname ==
- NULL ? curbuf->b_fname : curbuf->b_sfname, eap->forceit) == FAIL) {
+ curbuf->b_fname == NULL ? (char_u *)buf_spname(curbuf) : curbuf->b_sfname ==
+ NULL ? (char_u *)curbuf->b_fname : (char_u *)curbuf->b_sfname,
+ eap->forceit) == FAIL) {
return;
}
@@ -681,7 +697,7 @@ void ex_hardcopy(exarg_T *eap)
* Estimate the total lines to be printed
*/
for (lnum = eap->line1; lnum <= eap->line2; lnum++) {
- bytes_to_print += STRLEN(skipwhite(ml_get(lnum)));
+ bytes_to_print += STRLEN(skipwhite((char *)ml_get(lnum)));
}
if (bytes_to_print == 0) {
msg(_("No text to be printed"));
@@ -789,7 +805,7 @@ void ex_hardcopy(exarg_T *eap)
if (prtpos.column == 0) {
// finished a file line
prtpos.bytes_printed +=
- STRLEN(skipwhite(ml_get(prtpos.file_line)));
+ STRLEN(skipwhite((char *)ml_get(prtpos.file_line)));
if (++prtpos.file_line > eap->line2) {
break; // reached the end
}
@@ -879,7 +895,7 @@ static colnr_T hardcopy_line(prt_settings_T *psettings, int page_line, prt_pos_T
* Loop over the columns until the end of the file line or right margin.
*/
for (col = ppos->column; line[col] != NUL && !need_break; col += outputlen) {
- if ((outputlen = utfc_ptr2len(line + col)) < 1) {
+ if ((outputlen = utfc_ptr2len((char *)line + col)) < 1) {
outputlen = 1;
}
// syntax highlighting stuff.
@@ -932,7 +948,7 @@ static colnr_T hardcopy_line(prt_settings_T *psettings, int page_line, prt_pos_T
need_break = 1;
} else {
need_break = mch_print_text_out(line + col, (size_t)outputlen);
- print_pos += utf_ptr2cells(line + col);
+ print_pos += utf_ptr2cells((char *)line + col);
}
}
@@ -953,7 +969,6 @@ static colnr_T hardcopy_line(prt_settings_T *psettings, int page_line, prt_pos_T
return col;
}
-
/*
* PS printer stuff.
*
@@ -1225,7 +1240,6 @@ static struct prt_ps_mbfont_S prt_ps_mbfonts[] =
#define PRT_RESOURCE_ENCODING "Encoding"
#define PRT_RESOURCE_CMAP "CMap"
-
/* Data for table based DSC comment recognition, easy to extend if VIM needs to
* read more comments. */
#define PRT_DSC_MISC_TYPE (-1)
@@ -1237,7 +1251,6 @@ static struct prt_ps_mbfont_S prt_ps_mbfonts[] =
#define PRT_DSC_VERSION "%%Version:"
#define PRT_DSC_ENDCOMMENTS "%%EndComments:"
-
#define SIZEOF_CSTR(s) (sizeof(s) - 1)
static struct prt_dsc_comment_S prt_dsc_table[] =
{
@@ -1248,7 +1261,6 @@ static struct prt_dsc_comment_S prt_dsc_table[] =
PRT_DSC_ENDCOMMENTS_TYPE }
};
-
/*
* Variables for the output PostScript file.
*/
@@ -1546,7 +1558,7 @@ static void prt_flush_buffer(void)
}
}
-static void prt_resource_name(char_u *filename, void *cookie)
+static void prt_resource_name(char *filename, void *cookie)
{
char_u *resource_filename = cookie;
@@ -1559,7 +1571,7 @@ static void prt_resource_name(char_u *filename, void *cookie)
static int prt_find_resource(char *name, struct prt_ps_resource_S *resource)
{
- char_u *buffer;
+ char *buffer;
int retval;
buffer = xmallocz(MAXPATHL);
@@ -1567,7 +1579,7 @@ static int prt_find_resource(char *name, struct prt_ps_resource_S *resource)
STRLCPY(resource->name, name, 64);
// Look for named resource file in runtimepath
STRCPY(buffer, "print");
- add_pathsep((char *)buffer);
+ add_pathsep(buffer);
STRLCAT(buffer, name, MAXPATHL);
STRLCAT(buffer, ".ps", MAXPATHL);
resource->filename[0] = NUL;
@@ -1852,8 +1864,6 @@ static void prt_dsc_text(char *comment, char *text)
prt_write_file(prt_line_buffer);
}
-#define prt_dsc_atend(c) prt_dsc_text((c), "atend")
-
static void prt_dsc_ints(char *comment, int count, int *ints)
{
int i;
@@ -2038,7 +2048,6 @@ static void prt_font_metrics(int font_scale)
prt_char_width = PRT_PS_FONT_TO_USER(font_scale, prt_ps_font->wx);
}
-
static int prt_get_cpl(void)
{
if (prt_use_number()) {
@@ -2153,7 +2162,6 @@ int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit)
struct prt_ps_encoding_S *p_mbenc_first;
struct prt_ps_charset_S *p_mbchar = NULL;
-
/*
* Set up font and encoding.
*/
@@ -2329,7 +2337,7 @@ int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit)
* Set up the font size.
*/
fontsize = PRT_PS_DEFAULT_FONTSIZE;
- for (p = p_pfn; (p = vim_strchr(p, ':')) != NULL; ++p) {
+ for (p = p_pfn; (p = (char_u *)vim_strchr((char *)p, ':')) != NULL; p++) {
if (p[1] == 'h' && ascii_isdigit(p[2])) {
fontsize = atoi((char *)p + 2);
}
@@ -2398,7 +2406,7 @@ int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit)
}
prt_ps_fd = os_fopen((char *)prt_ps_file_name, WRITEBIN);
} else {
- p = expand_env_save(psettings->outfile);
+ p = (char_u *)expand_env_save((char *)psettings->outfile);
if (p != NULL) {
prt_ps_fd = os_fopen((char *)p, WRITEBIN);
xfree(p);
@@ -2501,7 +2509,7 @@ bool mch_print_begin(prt_settings_T *psettings)
*/
prt_dsc_start();
prt_dsc_textline("Title", (char *)psettings->jobname);
- if (os_get_user_name(buffer, 256) == FAIL) {
+ if (os_get_username(buffer, 256) == FAIL) {
STRCPY(buffer, "Unknown");
}
prt_dsc_textline("For", buffer);
@@ -2510,14 +2518,14 @@ bool mch_print_begin(prt_settings_T *psettings)
char ctime_buf[50];
char *p_time = os_ctime(ctime_buf, sizeof(ctime_buf));
// Note: os_ctime() adds a \n so we have to remove it :-(
- p = vim_strchr((char_u *)p_time, '\n');
+ p = (char_u *)vim_strchr(p_time, '\n');
if (p != NULL) {
*p = NUL;
}
prt_dsc_textline("CreationDate", p_time);
prt_dsc_textline("DocumentData", "Clean8Bit");
prt_dsc_textline("Orientation", "Portrait");
- prt_dsc_atend("Pages");
+ prt_dsc_text(("Pages"), "atend");
prt_dsc_textline("PageOrder", "Ascend");
// The bbox does not change with orientation - it is always in the default
// user coordinate system! We have to recalculate right and bottom
@@ -2987,7 +2995,7 @@ int mch_print_text_out(char_u *const textp, size_t len)
}
}
if (prt_out_mbyte) {
- const bool half_width = (utf_ptr2cells(p) == 1);
+ const bool half_width = (utf_ptr2cells((char *)p) == 1);
if (half_width) {
char_width /= 2;
}
@@ -3180,4 +3188,3 @@ void mch_print_set_fg(uint32_t fgcol)
prt_need_fgcol = true;
}
}
-
diff --git a/src/nvim/hardcopy.h b/src/nvim/hardcopy.h
index eba769d342..9ef4eb0074 100644
--- a/src/nvim/hardcopy.h
+++ b/src/nvim/hardcopy.h
@@ -18,6 +18,9 @@ typedef struct {
TriState italic;
TriState underline;
int undercurl;
+ int underdouble;
+ int underdotted;
+ int underdashed;
} prt_text_attr_T;
/*
@@ -77,7 +80,6 @@ typedef struct {
#define PRINT_NUMBER_WIDTH 8
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "hardcopy.h.generated.h"
#endif
diff --git a/src/nvim/hashtab.c b/src/nvim/hashtab.c
index ca6f033c47..95ae7a152c 100644
--- a/src/nvim/hashtab.c
+++ b/src/nvim/hashtab.c
@@ -85,9 +85,9 @@ void hash_clear_all(hashtab_T *ht, unsigned int off)
/// used for that key.
/// WARNING: Returned pointer becomes invalid as soon as the hash table
/// is changed in any way.
-hashitem_T *hash_find(const hashtab_T *const ht, const char_u *const key)
+hashitem_T *hash_find(const hashtab_T *const ht, const char *const key)
{
- return hash_lookup(ht, (const char *)key, STRLEN(key), hash_hash(key));
+ return hash_lookup(ht, key, STRLEN(key), hash_hash((char_u *)key));
}
/// Like hash_find, but key is not NUL-terminated
diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c
index 3050ca02de..0f20eb1905 100644
--- a/src/nvim/highlight.c
+++ b/src/nvim/highlight.c
@@ -5,15 +5,16 @@
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
+#include "nvim/decoration_provider.h"
#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
+#include "nvim/highlight_group.h"
#include "nvim/lua/executor.h"
#include "nvim/map.h"
#include "nvim/message.h"
#include "nvim/option.h"
#include "nvim/popupmnu.h"
#include "nvim/screen.h"
-#include "nvim/syntax.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
@@ -144,13 +145,19 @@ int hl_get_syn_attr(int ns_id, int idx, HlAttrs at_en)
}
}
-void ns_hl_def(NS ns_id, int hl_id, HlAttrs attrs, int link_id)
+void ns_hl_def(NS ns_id, int hl_id, HlAttrs attrs, int link_id, Dict(highlight) *dict)
{
- DecorProvider *p = get_decor_provider(ns_id, true);
if ((attrs.rgb_ae_attr & HL_DEFAULT)
&& map_has(ColorKey, ColorItem)(&ns_hl, ColorKey(ns_id, hl_id))) {
return;
}
+ if (ns_id == 0) {
+ assert(dict);
+ // set in global (':highlight') namespace
+ set_hl_group(hl_id, attrs, dict, link_id);
+ return;
+ }
+ DecorProvider *p = get_decor_provider(ns_id, true);
int attr_id = link_id > 0 ? -1 : hl_get_syn_attr(ns_id, hl_id, attrs);
ColorItem it = { .attr_id = attr_id,
.link_id = link_id,
@@ -192,27 +199,21 @@ int ns_get_hl(NS ns_id, int hl_id, bool link, bool nodefault)
int tmp = false;
HlAttrs attrs = HLATTRS_INIT;
if (ret.type == kObjectTypeDictionary) {
- Dictionary dict = ret.data.dictionary;
fallback = false;
- attrs = dict2hlattrs(dict, true, &it.link_id, &err);
- for (size_t i = 0; i < dict.size; i++) {
- char *key = dict.items[i].key.data;
- Object val = dict.items[i].value;
- bool truthy = api_object_to_bool(val, key, false, &err);
-
- if (strequal(key, "fallback")) {
- fallback = truthy;
- } else if (strequal(key, "temp")) {
- tmp = truthy;
+ Dict(highlight) dict = { 0 };
+ if (api_dict_to_keydict(&dict, KeyDict_highlight_get_field,
+ ret.data.dictionary, &err)) {
+ attrs = dict2hlattrs(&dict, true, &it.link_id, &err);
+ fallback = api_object_to_bool(dict.fallback, "fallback", true, &err);
+ tmp = api_object_to_bool(dict.fallback, "tmp", false, &err);
+ if (it.link_id >= 0) {
+ fallback = true;
}
}
- if (it.link_id >= 0) {
- fallback = true;
- }
}
it.attr_id = fallback ? -1 : hl_get_syn_attr((int)ns_id, hl_id, attrs);
- it.version = p->hl_valid-tmp;
+ it.version = p->hl_valid - tmp;
it.is_default = attrs.rgb_ae_attr & HL_DEFAULT;
map_put(ColorKey, ColorItem)(&ns_hl, ColorKey(ns_id, hl_id), it);
}
@@ -228,7 +229,6 @@ int ns_get_hl(NS ns_id, int hl_id, bool link, bool nodefault)
}
}
-
bool win_check_ns_hl(win_T *wp)
{
if (ns_hl_changed) {
@@ -327,7 +327,7 @@ void update_window_hl(win_T *wp, bool invalid)
wp->w_hl_attr_normal);
}
- for (int hlf = 0; hlf < (int)HLF_COUNT; hlf++) {
+ for (int hlf = 0; hlf < HLF_COUNT; hlf++) {
int attr;
if (wp->w_hl_ids[hlf] != 0) {
attr = hl_get_ui_attr(hlf, wp->w_hl_ids[hlf], false);
@@ -558,7 +558,7 @@ int hl_blend_attrs(int back_attr, int front_attr, bool *through)
cattrs = battrs;
cattrs.rgb_fg_color = rgb_blend(ratio, battrs.rgb_fg_color,
fattrs.rgb_bg_color);
- if (cattrs.rgb_ae_attr & (HL_UNDERLINE|HL_UNDERCURL)) {
+ if (cattrs.rgb_ae_attr & (HL_ANY_UNDERLINE)) {
cattrs.rgb_sp_color = rgb_blend(ratio, battrs.rgb_sp_color,
fattrs.rgb_bg_color);
} else {
@@ -576,7 +576,7 @@ int hl_blend_attrs(int back_attr, int front_attr, bool *through)
}
cattrs.rgb_fg_color = rgb_blend(ratio/2, battrs.rgb_fg_color,
fattrs.rgb_fg_color);
- if (cattrs.rgb_ae_attr & (HL_UNDERLINE|HL_UNDERCURL)) {
+ if (cattrs.rgb_ae_attr & (HL_ANY_UNDERLINE)) {
cattrs.rgb_sp_color = rgb_blend(ratio/2, battrs.rgb_bg_color,
fattrs.rgb_sp_color);
} else {
@@ -601,7 +601,7 @@ int hl_blend_attrs(int back_attr, int front_attr, bool *through)
static int rgb_blend(int ratio, int rgb1, int rgb2)
{
- int a = ratio, b = 100-ratio;
+ int a = ratio, b = 100 - ratio;
int r1 = (rgb1 & 0xFF0000) >> 16;
int g1 = (rgb1 & 0x00FF00) >> 8;
int b1 = (rgb1 & 0x0000FF) >> 0;
@@ -747,6 +747,18 @@ Dictionary hlattrs2dict(HlAttrs ae, bool use_rgb)
PUT(hl, "undercurl", BOOLEAN_OBJ(true));
}
+ if (mask & HL_UNDERDOUBLE) {
+ PUT(hl, "underdouble", BOOLEAN_OBJ(true));
+ }
+
+ if (mask & HL_UNDERDOTTED) {
+ PUT(hl, "underdotted", BOOLEAN_OBJ(true));
+ }
+
+ if (mask & HL_UNDERDASHED) {
+ PUT(hl, "underdashed", BOOLEAN_OBJ(true));
+ }
+
if (mask & HL_ITALIC) {
PUT(hl, "italic", BOOLEAN_OBJ(true));
}
@@ -780,11 +792,11 @@ Dictionary hlattrs2dict(HlAttrs ae, bool use_rgb)
PUT(hl, "special", INTEGER_OBJ(ae.rgb_sp_color));
}
} else {
- if (cterm_normal_fg_color != ae.cterm_fg_color && ae.cterm_fg_color != 0) {
+ if (ae.cterm_fg_color != 0) {
PUT(hl, "foreground", INTEGER_OBJ(ae.cterm_fg_color - 1));
}
- if (cterm_normal_bg_color != ae.cterm_bg_color && ae.cterm_bg_color != 0) {
+ if (ae.cterm_bg_color != 0) {
PUT(hl, "background", INTEGER_OBJ(ae.cterm_bg_color - 1));
}
}
@@ -796,112 +808,123 @@ Dictionary hlattrs2dict(HlAttrs ae, bool use_rgb)
return hl;
}
-HlAttrs dict2hlattrs(Dictionary dict, bool use_rgb, int *link_id, Error *err)
+HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *err)
{
HlAttrs hlattrs = HLATTRS_INIT;
-
int32_t fg = -1, bg = -1, ctermfg = -1, ctermbg = -1, sp = -1;
+ int blend = -1;
int16_t mask = 0;
int16_t cterm_mask = 0;
bool cterm_mask_provided = false;
- for (size_t i = 0; i < dict.size; i++) {
- char *key = dict.items[i].key.data;
- Object val = dict.items[i].value;
-
- struct {
- const char *name;
- int16_t flag;
- } flags[] = {
- { "bold", HL_BOLD },
- { "standout", HL_STANDOUT },
- { "underline", HL_UNDERLINE },
- { "undercurl", HL_UNDERCURL },
- { "italic", HL_ITALIC },
- { "reverse", HL_INVERSE },
- { "default", HL_DEFAULT },
- { "global", HL_GLOBAL },
- { NULL, 0 },
- };
-
- int j;
- for (j = 0; flags[j].name; j++) {
- if (strequal(flags[j].name, key)) {
- if (api_object_to_bool(val, key, false, err)) {
- mask = mask | flags[j].flag;
- }
- break;
- }
+#define CHECK_FLAG(d, m, name, extra, flag) \
+ if (api_object_to_bool(d->name##extra, #name, false, err)) { \
+ m = m | flag; \
+ }
+
+ CHECK_FLAG(dict, mask, bold, , HL_BOLD);
+ CHECK_FLAG(dict, mask, standout, , HL_STANDOUT);
+ CHECK_FLAG(dict, mask, underline, , HL_UNDERLINE);
+ CHECK_FLAG(dict, mask, undercurl, , HL_UNDERCURL);
+ CHECK_FLAG(dict, mask, underdouble, , HL_UNDERDOUBLE);
+ CHECK_FLAG(dict, mask, underdotted, , HL_UNDERDOTTED);
+ CHECK_FLAG(dict, mask, underdashed, , HL_UNDERDASHED);
+ CHECK_FLAG(dict, mask, italic, , HL_ITALIC);
+ CHECK_FLAG(dict, mask, reverse, , HL_INVERSE);
+ CHECK_FLAG(dict, mask, strikethrough, , HL_STRIKETHROUGH);
+ CHECK_FLAG(dict, mask, nocombine, , HL_NOCOMBINE);
+ CHECK_FLAG(dict, mask, default, _, HL_DEFAULT);
+ CHECK_FLAG(dict, mask, global, , HL_GLOBAL);
+
+ if (HAS_KEY(dict->fg)) {
+ fg = object_to_color(dict->fg, "fg", true, err);
+ } else if (HAS_KEY(dict->foreground)) {
+ fg = object_to_color(dict->foreground, "foreground", true, err);
+ }
+ if (ERROR_SET(err)) {
+ return hlattrs;
+ }
+
+ if (HAS_KEY(dict->bg)) {
+ bg = object_to_color(dict->bg, "bg", true, err);
+ } else if (HAS_KEY(dict->background)) {
+ bg = object_to_color(dict->background, "background", true, err);
+ }
+ if (ERROR_SET(err)) {
+ return hlattrs;
+ }
+
+ if (HAS_KEY(dict->sp)) {
+ sp = object_to_color(dict->sp, "sp", true, err);
+ } else if (HAS_KEY(dict->special)) {
+ sp = object_to_color(dict->special, "special", true, err);
+ }
+ if (ERROR_SET(err)) {
+ return hlattrs;
+ }
+
+ if (dict->blend.type == kObjectTypeInteger) {
+ Integer blend0 = dict->blend.data.integer;
+ if (blend0 < 0 || blend0 > 100) {
+ api_set_error(err, kErrorTypeValidation, "'blend' is not between 0 to 100");
+ } else {
+ blend = (int)blend0;
}
+ } else if (HAS_KEY(dict->blend)) {
+ api_set_error(err, kErrorTypeValidation, "'blend' must be an integer");
+ }
+ if (ERROR_SET(err)) {
+ return hlattrs;
+ }
- // Handle cterm attrs
- if (strequal(key, "cterm") && val.type == kObjectTypeDictionary) {
- cterm_mask_provided = true;
- Dictionary cterm_dict = val.data.dictionary;
- for (size_t l = 0; l < cterm_dict.size; l++) {
- char *cterm_dict_key = cterm_dict.items[l].key.data;
- Object cterm_dict_val = cterm_dict.items[l].value;
- for (int m = 0; flags[m].name; m++) {
- if (strequal(flags[m].name, cterm_dict_key)) {
- if (api_object_to_bool(cterm_dict_val, cterm_dict_key, false,
- err)) {
- cterm_mask |= flags[m].flag;
- }
- break;
- }
- }
+ if (HAS_KEY(dict->link)) {
+ if (link_id) {
+ *link_id = object_to_hl_id(dict->link, "link", err);
+ if (ERROR_SET(err)) {
+ return hlattrs;
}
+ } else {
+ api_set_error(err, kErrorTypeValidation, "Invalid Key: 'link'");
}
+ }
- struct {
- const char *name;
- const char *shortname;
- int *dest;
- } colors[] = {
- { "foreground", "fg", &fg },
- { "background", "bg", &bg },
- { "ctermfg", NULL, &ctermfg },
- { "ctermbg", NULL, &ctermbg },
- { "special", "sp", &sp },
- { NULL, NULL, NULL },
- };
-
- int k;
- for (k = 0; (!flags[j].name) && colors[k].name; k++) {
- if (strequal(colors[k].name, key) || strequal(colors[k].shortname, key)) {
- if (val.type == kObjectTypeInteger) {
- *colors[k].dest = (int)val.data.integer;
- } else if (val.type == kObjectTypeString) {
- String str = val.data.string;
- // TODO(bfredl): be more fancy with "bg", "fg" etc
- if (str.size) {
- *colors[k].dest = name_to_color(str.data);
- }
- } else {
- api_set_error(err, kErrorTypeValidation,
- "'%s' must be string or integer", key);
- }
- break;
- }
+ // Handle cterm attrs
+ if (dict->cterm.type == kObjectTypeDictionary) {
+ Dict(highlight_cterm) cterm[1] = { 0 };
+ if (!api_dict_to_keydict(cterm, KeyDict_highlight_cterm_get_field,
+ dict->cterm.data.dictionary, err)) {
+ return hlattrs;
}
- if (flags[j].name || colors[k].name) {
- // handled above
- } else if (link_id && strequal(key, "link")) {
- if (val.type == kObjectTypeString) {
- String str = val.data.string;
- *link_id = syn_check_group(str.data, (int)str.size);
- } else if (val.type == kObjectTypeInteger) {
- // TODO(bfredl): validate range?
- *link_id = (int)val.data.integer;
- } else {
- api_set_error(err, kErrorTypeValidation,
- "'link' must be string or integer");
- }
+ cterm_mask_provided = true;
+ CHECK_FLAG(cterm, cterm_mask, bold, , HL_BOLD);
+ CHECK_FLAG(cterm, cterm_mask, standout, , HL_STANDOUT);
+ CHECK_FLAG(cterm, cterm_mask, underline, , HL_UNDERLINE);
+ CHECK_FLAG(cterm, cterm_mask, undercurl, , HL_UNDERCURL);
+ CHECK_FLAG(cterm, cterm_mask, italic, , HL_ITALIC);
+ CHECK_FLAG(cterm, cterm_mask, reverse, , HL_INVERSE);
+ CHECK_FLAG(cterm, cterm_mask, strikethrough, , HL_STRIKETHROUGH);
+ CHECK_FLAG(cterm, cterm_mask, nocombine, , HL_NOCOMBINE);
+ } else if (dict->cterm.type == kObjectTypeArray && dict->cterm.data.array.size == 0) {
+ // empty list from Lua API should clear all cterm attributes
+ // TODO(clason): handle via gen_api_dispatch
+ cterm_mask_provided = true;
+ } else if (HAS_KEY(dict->cterm)) {
+ api_set_error(err, kErrorTypeValidation, "'cterm' must be a Dictionary.");
+ }
+#undef CHECK_FLAG
+
+ if (HAS_KEY(dict->ctermfg)) {
+ ctermfg = object_to_color(dict->ctermfg, "ctermfg", false, err);
+ if (ERROR_SET(err)) {
+ return hlattrs;
}
+ }
+ if (HAS_KEY(dict->ctermbg)) {
+ ctermbg = object_to_color(dict->ctermbg, "ctermbg", false, err);
if (ERROR_SET(err)) {
- return hlattrs; // error set, caller should not use retval
+ return hlattrs;
}
}
@@ -914,19 +937,46 @@ HlAttrs dict2hlattrs(Dictionary dict, bool use_rgb, int *link_id, Error *err)
hlattrs.rgb_bg_color = bg;
hlattrs.rgb_fg_color = fg;
hlattrs.rgb_sp_color = sp;
- hlattrs.cterm_bg_color =
- ctermbg == -1 ? cterm_normal_bg_color : ctermbg + 1;
- hlattrs.cterm_fg_color =
- ctermfg == -1 ? cterm_normal_fg_color : ctermfg + 1;
+ hlattrs.hl_blend = blend;
+ hlattrs.cterm_bg_color = ctermbg == -1 ? 0 : ctermbg + 1;
+ hlattrs.cterm_fg_color = ctermfg == -1 ? 0 : ctermfg + 1;
hlattrs.cterm_ae_attr = cterm_mask;
} else {
+ hlattrs.cterm_bg_color = ctermbg == -1 ? 0 : ctermbg + 1;
+ hlattrs.cterm_fg_color = ctermfg == -1 ? 0 : ctermfg + 1;
hlattrs.cterm_ae_attr = cterm_mask;
- hlattrs.cterm_bg_color = bg == -1 ? cterm_normal_bg_color : bg + 1;
- hlattrs.cterm_fg_color = fg == -1 ? cterm_normal_fg_color : fg + 1;
}
return hlattrs;
}
+
+int object_to_color(Object val, char *key, bool rgb, Error *err)
+{
+ if (val.type == kObjectTypeInteger) {
+ return (int)val.data.integer;
+ } else if (val.type == kObjectTypeString) {
+ String str = val.data.string;
+ // TODO(bfredl): be more fancy with "bg", "fg" etc
+ if (!str.size || STRICMP(str.data, "NONE") == 0) {
+ return -1;
+ }
+ int color;
+ if (rgb) {
+ int dummy;
+ color = name_to_color(str.data, &dummy);
+ } else {
+ color = name_to_ctermcolor(str.data);
+ }
+ if (color < 0) {
+ api_set_error(err, kErrorTypeValidation, "'%s' is not a valid color", str.data);
+ }
+ return color;
+ } else {
+ api_set_error(err, kErrorTypeValidation, "'%s' must be string or integer", key);
+ return 0;
+ }
+}
+
Array hl_inspect(int attr)
{
Array ret = ARRAY_DICT_INIT;
diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h
index 50a03e0c02..f41f980054 100644
--- a/src/nvim/highlight_defs.h
+++ b/src/nvim/highlight_defs.h
@@ -17,13 +17,17 @@ typedef enum {
HL_ITALIC = 0x04,
HL_UNDERLINE = 0x08,
HL_UNDERCURL = 0x10,
- HL_STANDOUT = 0x20,
- HL_STRIKETHROUGH = 0x40,
- HL_NOCOMBINE = 0x80,
- HL_BG_INDEXED = 0x0100,
- HL_FG_INDEXED = 0x0200,
- HL_DEFAULT = 0x0400,
- HL_GLOBAL = 0x0800,
+ HL_UNDERDOUBLE = 0x20,
+ HL_UNDERDOTTED = 0x40,
+ HL_UNDERDASHED = 0x80,
+ HL_STANDOUT = 0x0100,
+ HL_NOCOMBINE = 0x0200,
+ HL_STRIKETHROUGH = 0x0400,
+ HL_BG_INDEXED = 0x0800,
+ HL_FG_INDEXED = 0x1000,
+ HL_DEFAULT = 0x2000,
+ HL_GLOBAL = 0x4000,
+ HL_ANY_UNDERLINE = HL_UNDERLINE | HL_UNDERDOUBLE | HL_UNDERCURL | HL_UNDERDOTTED | HL_UNDERDASHED,
} HlAttrFlags;
/// Stores a complete highlighting entry, including colors and attributes
@@ -59,6 +63,7 @@ typedef enum {
HLF_E, // error messages
HLF_I, // incremental search
HLF_L, // last search string
+ HLF_LC, // current search match
HLF_M, // "--More--" message
HLF_CM, // Mode (e.g., "-- INSERT --")
HLF_N, // line number for ":number" and ":#" commands
@@ -70,7 +75,8 @@ typedef enum {
HLF_R, // return to continue message and yes/no questions
HLF_S, // status lines
HLF_SNC, // status lines of not-current windows
- HLF_C, // column to separate vertically split windows
+ HLF_C, // window split separators
+ HLF_VSP, // VertSplit
HLF_T, // Titles for output from ":set all", ":autocmd" etc.
HLF_V, // Visual mode
HLF_VNC, // Visual mode, autoselecting and not clipboard owner
@@ -105,7 +111,10 @@ typedef enum {
HLF_NFLOAT, // Floating window
HLF_MSG, // Message area
HLF_BORDER, // Floating window border
- HLF_COUNT, // MUST be the last one
+ HLF_WBR, // Window bars
+ HLF_WBRNC, // Window bars of not-current windows
+ HLF_CU, // Cursor
+ HLF_COUNT, // MUST be the last one
} hlf_T;
EXTERN const char *hlf_names[] INIT(= {
@@ -118,6 +127,7 @@ EXTERN const char *hlf_names[] INIT(= {
[HLF_E] = "ErrorMsg",
[HLF_I] = "IncSearch",
[HLF_L] = "Search",
+ [HLF_LC] = "CurSearch",
[HLF_M] = "MoreMsg",
[HLF_CM] = "ModeMsg",
[HLF_N] = "LineNr",
@@ -129,10 +139,11 @@ EXTERN const char *hlf_names[] INIT(= {
[HLF_R] = "Question",
[HLF_S] = "StatusLine",
[HLF_SNC] = "StatusLineNC",
- [HLF_C] = "VertSplit",
+ [HLF_C] = "WinSeparator",
[HLF_T] = "Title",
[HLF_V] = "Visual",
[HLF_VNC] = "VisualNC",
+ [HLF_VSP] = "VertSplit",
[HLF_W] = "WarningMsg",
[HLF_WM] = "WildMenu",
[HLF_FL] = "Folded",
@@ -164,9 +175,11 @@ EXTERN const char *hlf_names[] INIT(= {
[HLF_NFLOAT] = "NormalFloat",
[HLF_MSG] = "MsgArea",
[HLF_BORDER] = "FloatBorder",
+ [HLF_WBR] = "WinBar",
+ [HLF_WBRNC] = "WinBarNC",
+ [HLF_CU] = "Cursor",
});
-
EXTERN int highlight_attr[HLF_COUNT]; // Highl. attr for each context.
EXTERN int highlight_attr_last[HLF_COUNT]; // copy for detecting changed groups
EXTERN int highlight_user[9]; // User[1-9] attributes
@@ -210,5 +223,4 @@ typedef struct {
#define COLOR_ITEM_INITIALIZER { .attr_id = -1, .link_id = -1, \
.version = -1, .is_default = false }
-
#endif // NVIM_HIGHLIGHT_DEFS_H
diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c
new file mode 100644
index 0000000000..d958b7b344
--- /dev/null
+++ b/src/nvim/highlight_group.c
@@ -0,0 +1,2827 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+// highlight_group.c: code for managing highlight groups
+
+#include <stdbool.h>
+
+#include "nvim/api/private/helpers.h"
+#include "nvim/autocmd.h"
+#include "nvim/charset.h"
+#include "nvim/cursor_shape.h"
+#include "nvim/fold.h"
+#include "nvim/highlight.h"
+#include "nvim/highlight_group.h"
+#include "nvim/lua/executor.h"
+#include "nvim/match.h"
+#include "nvim/option.h"
+#include "nvim/runtime.h"
+#include "nvim/screen.h"
+
+/// \addtogroup SG_SET
+/// @{
+#define SG_CTERM 2 // cterm has been set
+#define SG_GUI 4 // gui has been set
+#define SG_LINK 8 // link has been set
+/// @}
+
+#define MAX_SYN_NAME 200
+
+// builtin |highlight-groups|
+static garray_T highlight_ga = GA_EMPTY_INIT_VALUE;
+
+// arena for object with same lifetime as highlight_ga (aka hl_table)
+Arena highlight_arena = ARENA_EMPTY;
+
+Map(cstr_t, int) highlight_unames = MAP_INIT;
+
+/// The "term", "cterm" and "gui" arguments can be any combination of the
+/// following names, separated by commas (but no spaces!).
+static char *(hl_name_table[]) =
+{ "bold", "standout", "underline",
+ "undercurl", "underdouble", "underdotted", "underdashed",
+ "italic", "reverse", "inverse", "strikethrough", "nocombine", "NONE" };
+static int hl_attr_table[] =
+{ HL_BOLD, HL_STANDOUT, HL_UNDERLINE,
+ HL_UNDERCURL, HL_UNDERDOUBLE, HL_UNDERDOTTED, HL_UNDERDASHED,
+ HL_ITALIC, HL_INVERSE, HL_INVERSE, HL_STRIKETHROUGH, HL_NOCOMBINE, 0 };
+
+/// Structure that stores information about a highlight group.
+/// The ID of a highlight group is also called group ID. It is the index in
+/// the highlight_ga array PLUS ONE.
+typedef struct {
+ char_u *sg_name; ///< highlight group name
+ char *sg_name_u; ///< uppercase of sg_name
+ bool sg_cleared; ///< "hi clear" was used
+ int sg_attr; ///< Screen attr @see ATTR_ENTRY
+ int sg_link; ///< link to this highlight group ID
+ int sg_deflink; ///< default link; restored in highlight_clear()
+ int sg_set; ///< combination of flags in \ref SG_SET
+ sctx_T sg_deflink_sctx; ///< script where the default link was set
+ sctx_T sg_script_ctx; ///< script in which the group was last set
+ // for terminal UIs
+ int sg_cterm; ///< "cterm=" highlighting attr
+ ///< (combination of \ref HlAttrFlags)
+ int sg_cterm_fg; ///< terminal fg color number + 1
+ int sg_cterm_bg; ///< terminal bg color number + 1
+ bool sg_cterm_bold; ///< bold attr was set for light color
+ // for RGB UIs
+ int sg_gui; ///< "gui=" highlighting attributes
+ ///< (combination of \ref HlAttrFlags)
+ RgbValue sg_rgb_fg; ///< RGB foreground color
+ RgbValue sg_rgb_bg; ///< RGB background color
+ RgbValue sg_rgb_sp; ///< RGB special color
+ int sg_rgb_fg_idx; ///< RGB foreground color index
+ int sg_rgb_bg_idx; ///< RGB background color index
+ int sg_rgb_sp_idx; ///< RGB special color index
+
+ int sg_blend; ///< blend level (0-100 inclusive), -1 if unset
+} HlGroup;
+
+enum {
+ kColorIdxNone = -1,
+ kColorIdxHex = -2,
+ kColorIdxFg = -3,
+ kColorIdxBg = -4,
+};
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "highlight_group.c.generated.h"
+#endif
+
+#define hl_table ((HlGroup *)((highlight_ga.ga_data)))
+
+// The default highlight groups. These are compiled-in for fast startup and
+// they still work when the runtime files can't be found.
+//
+// When making changes here, also change runtime/colors/default.vim!
+
+static const char *highlight_init_both[] = {
+ "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey",
+ "Cursor guibg=fg guifg=bg",
+ "lCursor guibg=fg guifg=bg",
+ "DiffText cterm=bold ctermbg=Red gui=bold guibg=Red",
+ "ErrorMsg ctermbg=DarkRed ctermfg=White guibg=Red guifg=White",
+ "IncSearch cterm=reverse gui=reverse",
+ "ModeMsg cterm=bold gui=bold",
+ "NonText ctermfg=Blue gui=bold guifg=Blue",
+ "Normal cterm=NONE gui=NONE",
+ "PmenuSbar ctermbg=Grey guibg=Grey",
+ "StatusLine cterm=reverse,bold gui=reverse,bold",
+ "StatusLineNC cterm=reverse gui=reverse",
+ "TabLineFill cterm=reverse gui=reverse",
+ "TabLineSel cterm=bold gui=bold",
+ "TermCursor cterm=reverse gui=reverse",
+ "WinBar cterm=bold gui=bold",
+ "WildMenu ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black",
+ "default link VertSplit Normal",
+ "default link WinSeparator VertSplit",
+ "default link WinBarNC WinBar",
+ "default link EndOfBuffer NonText",
+ "default link LineNrAbove LineNr",
+ "default link LineNrBelow LineNr",
+ "default link QuickFixLine Search",
+ "default link CursorLineSign SignColumn",
+ "default link CursorLineFold FoldColumn",
+ "default link Substitute Search",
+ "default link Whitespace NonText",
+ "default link MsgSeparator StatusLine",
+ "default link NormalFloat Pmenu",
+ "default link FloatBorder WinSeparator",
+ "default FloatShadow blend=80 guibg=Black",
+ "default FloatShadowThrough blend=100 guibg=Black",
+ "RedrawDebugNormal cterm=reverse gui=reverse",
+ "RedrawDebugClear ctermbg=Yellow guibg=Yellow",
+ "RedrawDebugComposed ctermbg=Green guibg=Green",
+ "RedrawDebugRecompose ctermbg=Red guibg=Red",
+ "Error term=reverse cterm=NONE ctermfg=White ctermbg=Red gui=NONE guifg=White guibg=Red",
+ "Todo term=standout cterm=NONE ctermfg=Black ctermbg=Yellow gui=NONE guifg=Blue guibg=Yellow",
+ "default link String Constant",
+ "default link Character Constant",
+ "default link Number Constant",
+ "default link Boolean Constant",
+ "default link Float Number",
+ "default link Function Identifier",
+ "default link Conditional Statement",
+ "default link Repeat Statement",
+ "default link Label Statement",
+ "default link Operator Statement",
+ "default link Keyword Statement",
+ "default link Exception Statement",
+ "default link Include PreProc",
+ "default link Define PreProc",
+ "default link Macro PreProc",
+ "default link PreCondit PreProc",
+ "default link StorageClass Type",
+ "default link Structure Type",
+ "default link Typedef Type",
+ "default link Tag Special",
+ "default link SpecialChar Special",
+ "default link Delimiter Special",
+ "default link SpecialComment Special",
+ "default link Debug Special",
+ "default DiagnosticError ctermfg=1 guifg=Red",
+ "default DiagnosticWarn ctermfg=3 guifg=Orange",
+ "default DiagnosticInfo ctermfg=4 guifg=LightBlue",
+ "default DiagnosticHint ctermfg=7 guifg=LightGrey",
+ "default DiagnosticUnderlineError cterm=underline gui=underline guisp=Red",
+ "default DiagnosticUnderlineWarn cterm=underline gui=underline guisp=Orange",
+ "default DiagnosticUnderlineInfo cterm=underline gui=underline guisp=LightBlue",
+ "default DiagnosticUnderlineHint cterm=underline gui=underline guisp=LightGrey",
+ "default link DiagnosticVirtualTextError DiagnosticError",
+ "default link DiagnosticVirtualTextWarn DiagnosticWarn",
+ "default link DiagnosticVirtualTextInfo DiagnosticInfo",
+ "default link DiagnosticVirtualTextHint DiagnosticHint",
+ "default link DiagnosticFloatingError DiagnosticError",
+ "default link DiagnosticFloatingWarn DiagnosticWarn",
+ "default link DiagnosticFloatingInfo DiagnosticInfo",
+ "default link DiagnosticFloatingHint DiagnosticHint",
+ "default link DiagnosticSignError DiagnosticError",
+ "default link DiagnosticSignWarn DiagnosticWarn",
+ "default link DiagnosticSignInfo DiagnosticInfo",
+ "default link DiagnosticSignHint DiagnosticHint",
+ NULL
+};
+
+// Default colors only used with a light background.
+static const char *highlight_init_light[] = {
+ "ColorColumn ctermbg=LightRed guibg=LightRed",
+ "CursorColumn ctermbg=LightGrey guibg=Grey90",
+ "CursorLine cterm=underline guibg=Grey90",
+ "CursorLineNr cterm=underline ctermfg=Brown gui=bold guifg=Brown",
+ "DiffAdd ctermbg=LightBlue guibg=LightBlue",
+ "DiffChange ctermbg=LightMagenta guibg=LightMagenta",
+ "DiffDelete ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan",
+ "Directory ctermfg=DarkBlue guifg=Blue",
+ "FoldColumn ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue",
+ "Folded ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue",
+ "LineNr ctermfg=Brown guifg=Brown",
+ "MatchParen ctermbg=Cyan guibg=Cyan",
+ "MoreMsg ctermfg=DarkGreen gui=bold guifg=SeaGreen",
+ "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta",
+ "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey",
+ "PmenuThumb ctermbg=Black guibg=Black",
+ "Question ctermfg=DarkGreen gui=bold guifg=SeaGreen",
+ "Search ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE",
+ "SignColumn ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue",
+ "SpecialKey ctermfg=DarkBlue guifg=Blue",
+ "SpellBad ctermbg=LightRed guisp=Red gui=undercurl",
+ "SpellCap ctermbg=LightBlue guisp=Blue gui=undercurl",
+ "SpellLocal ctermbg=Cyan guisp=DarkCyan gui=undercurl",
+ "SpellRare ctermbg=LightMagenta guisp=Magenta gui=undercurl",
+ "TabLine cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey",
+ "Title ctermfg=DarkMagenta gui=bold guifg=Magenta",
+ "Visual guibg=LightGrey",
+ "WarningMsg ctermfg=DarkRed guifg=Red",
+ "Comment term=bold cterm=NONE ctermfg=DarkBlue ctermbg=NONE gui=NONE guifg=Blue guibg=NONE",
+ "Constant term=underline cterm=NONE ctermfg=DarkRed ctermbg=NONE gui=NONE guifg=Magenta guibg=NONE",
+ "Special term=bold cterm=NONE ctermfg=DarkMagenta ctermbg=NONE gui=NONE guifg=#6a5acd guibg=NONE",
+ "Identifier term=underline cterm=NONE ctermfg=DarkCyan ctermbg=NONE gui=NONE guifg=DarkCyan guibg=NONE",
+ "Statement term=bold cterm=NONE ctermfg=Brown ctermbg=NONE gui=bold guifg=Brown guibg=NONE",
+ "PreProc term=underline cterm=NONE ctermfg=DarkMagenta ctermbg=NONE gui=NONE guifg=#6a0dad guibg=NONE",
+ "Type term=underline cterm=NONE ctermfg=DarkGreen ctermbg=NONE gui=bold guifg=SeaGreen guibg=NONE",
+ "Underlined term=underline cterm=underline ctermfg=DarkMagenta gui=underline guifg=SlateBlue",
+ "Ignore term=NONE cterm=NONE ctermfg=white ctermbg=NONE gui=NONE guifg=bg guibg=NONE",
+ NULL
+};
+
+// Default colors only used with a dark background.
+static const char *highlight_init_dark[] = {
+ "ColorColumn ctermbg=DarkRed guibg=DarkRed",
+ "CursorColumn ctermbg=DarkGrey guibg=Grey40",
+ "CursorLine cterm=underline guibg=Grey40",
+ "CursorLineNr cterm=underline ctermfg=Yellow gui=bold guifg=Yellow",
+ "DiffAdd ctermbg=DarkBlue guibg=DarkBlue",
+ "DiffChange ctermbg=DarkMagenta guibg=DarkMagenta",
+ "DiffDelete ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan",
+ "Directory ctermfg=LightCyan guifg=Cyan",
+ "FoldColumn ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan",
+ "Folded ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan",
+ "LineNr ctermfg=Yellow guifg=Yellow",
+ "MatchParen ctermbg=DarkCyan guibg=DarkCyan",
+ "MoreMsg ctermfg=LightGreen gui=bold guifg=SeaGreen",
+ "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta",
+ "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey",
+ "PmenuThumb ctermbg=White guibg=White",
+ "Question ctermfg=LightGreen gui=bold guifg=Green",
+ "Search ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black",
+ "SignColumn ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan",
+ "SpecialKey ctermfg=LightBlue guifg=Cyan",
+ "SpellBad ctermbg=Red guisp=Red gui=undercurl",
+ "SpellCap ctermbg=Blue guisp=Blue gui=undercurl",
+ "SpellLocal ctermbg=Cyan guisp=Cyan gui=undercurl",
+ "SpellRare ctermbg=Magenta guisp=Magenta gui=undercurl",
+ "TabLine cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey",
+ "Title ctermfg=LightMagenta gui=bold guifg=Magenta",
+ "Visual guibg=DarkGrey",
+ "WarningMsg ctermfg=LightRed guifg=Red",
+ "Comment term=bold cterm=NONE ctermfg=Cyan ctermbg=NONE gui=NONE guifg=#80a0ff guibg=NONE",
+ "Constant term=underline cterm=NONE ctermfg=Magenta ctermbg=NONE gui=NONE guifg=#ffa0a0 guibg=NONE",
+ "Special term=bold cterm=NONE ctermfg=LightRed ctermbg=NONE gui=NONE guifg=Orange guibg=NONE",
+ "Identifier term=underline cterm=bold ctermfg=Cyan ctermbg=NONE gui=NONE guifg=#40ffff guibg=NONE",
+ "Statement term=bold cterm=NONE ctermfg=Yellow ctermbg=NONE gui=bold guifg=#ffff60 guibg=NONE",
+ "PreProc term=underline cterm=NONE ctermfg=LightBlue ctermbg=NONE gui=NONE guifg=#ff80ff guibg=NONE",
+ "Type term=underline cterm=NONE ctermfg=LightGreen ctermbg=NONE gui=bold guifg=#60ff60 guibg=NONE",
+ "Underlined term=underline cterm=underline ctermfg=LightBlue gui=underline guifg=#80a0ff",
+ "Ignore term=NONE cterm=NONE ctermfg=black ctermbg=NONE gui=NONE guifg=bg guibg=NONE",
+ NULL
+};
+
+const char *const highlight_init_cmdline[] = {
+ // XXX When modifying a list modify it in both valid and invalid halves.
+ // TODO(ZyX-I): merge valid and invalid groups via a macros.
+
+ // NvimInternalError should appear only when highlighter has a bug.
+ "NvimInternalError ctermfg=Red ctermbg=Red guifg=Red guibg=Red",
+
+ // Highlight groups (links) used by parser:
+
+ "default link NvimAssignment Operator",
+ "default link NvimPlainAssignment NvimAssignment",
+ "default link NvimAugmentedAssignment NvimAssignment",
+ "default link NvimAssignmentWithAddition NvimAugmentedAssignment",
+ "default link NvimAssignmentWithSubtraction NvimAugmentedAssignment",
+ "default link NvimAssignmentWithConcatenation NvimAugmentedAssignment",
+
+ "default link NvimOperator Operator",
+
+ "default link NvimUnaryOperator NvimOperator",
+ "default link NvimUnaryPlus NvimUnaryOperator",
+ "default link NvimUnaryMinus NvimUnaryOperator",
+ "default link NvimNot NvimUnaryOperator",
+
+ "default link NvimBinaryOperator NvimOperator",
+ "default link NvimComparison NvimBinaryOperator",
+ "default link NvimComparisonModifier NvimComparison",
+ "default link NvimBinaryPlus NvimBinaryOperator",
+ "default link NvimBinaryMinus NvimBinaryOperator",
+ "default link NvimConcat NvimBinaryOperator",
+ "default link NvimConcatOrSubscript NvimConcat",
+ "default link NvimOr NvimBinaryOperator",
+ "default link NvimAnd NvimBinaryOperator",
+ "default link NvimMultiplication NvimBinaryOperator",
+ "default link NvimDivision NvimBinaryOperator",
+ "default link NvimMod NvimBinaryOperator",
+
+ "default link NvimTernary NvimOperator",
+ "default link NvimTernaryColon NvimTernary",
+
+ "default link NvimParenthesis Delimiter",
+ "default link NvimLambda NvimParenthesis",
+ "default link NvimNestingParenthesis NvimParenthesis",
+ "default link NvimCallingParenthesis NvimParenthesis",
+
+ "default link NvimSubscript NvimParenthesis",
+ "default link NvimSubscriptBracket NvimSubscript",
+ "default link NvimSubscriptColon NvimSubscript",
+ "default link NvimCurly NvimSubscript",
+
+ "default link NvimContainer NvimParenthesis",
+ "default link NvimDict NvimContainer",
+ "default link NvimList NvimContainer",
+
+ "default link NvimIdentifier Identifier",
+ "default link NvimIdentifierScope NvimIdentifier",
+ "default link NvimIdentifierScopeDelimiter NvimIdentifier",
+ "default link NvimIdentifierName NvimIdentifier",
+ "default link NvimIdentifierKey NvimIdentifier",
+
+ "default link NvimColon Delimiter",
+ "default link NvimComma Delimiter",
+ "default link NvimArrow Delimiter",
+
+ "default link NvimRegister SpecialChar",
+ "default link NvimNumber Number",
+ "default link NvimFloat NvimNumber",
+ "default link NvimNumberPrefix Type",
+
+ "default link NvimOptionSigil Type",
+ "default link NvimOptionName NvimIdentifier",
+ "default link NvimOptionScope NvimIdentifierScope",
+ "default link NvimOptionScopeDelimiter NvimIdentifierScopeDelimiter",
+
+ "default link NvimEnvironmentSigil NvimOptionSigil",
+ "default link NvimEnvironmentName NvimIdentifier",
+
+ "default link NvimString String",
+ "default link NvimStringBody NvimString",
+ "default link NvimStringQuote NvimString",
+ "default link NvimStringSpecial SpecialChar",
+
+ "default link NvimSingleQuote NvimStringQuote",
+ "default link NvimSingleQuotedBody NvimStringBody",
+ "default link NvimSingleQuotedQuote NvimStringSpecial",
+
+ "default link NvimDoubleQuote NvimStringQuote",
+ "default link NvimDoubleQuotedBody NvimStringBody",
+ "default link NvimDoubleQuotedEscape NvimStringSpecial",
+
+ "default link NvimFigureBrace NvimInternalError",
+ "default link NvimSingleQuotedUnknownEscape NvimInternalError",
+
+ "default link NvimSpacing Normal",
+
+ // NvimInvalid groups:
+
+ "default link NvimInvalidSingleQuotedUnknownEscape NvimInternalError",
+
+ "default link NvimInvalid Error",
+
+ "default link NvimInvalidAssignment NvimInvalid",
+ "default link NvimInvalidPlainAssignment NvimInvalidAssignment",
+ "default link NvimInvalidAugmentedAssignment NvimInvalidAssignment",
+ "default link NvimInvalidAssignmentWithAddition NvimInvalidAugmentedAssignment",
+ "default link NvimInvalidAssignmentWithSubtraction NvimInvalidAugmentedAssignment",
+ "default link NvimInvalidAssignmentWithConcatenation NvimInvalidAugmentedAssignment",
+
+ "default link NvimInvalidOperator NvimInvalid",
+
+ "default link NvimInvalidUnaryOperator NvimInvalidOperator",
+ "default link NvimInvalidUnaryPlus NvimInvalidUnaryOperator",
+ "default link NvimInvalidUnaryMinus NvimInvalidUnaryOperator",
+ "default link NvimInvalidNot NvimInvalidUnaryOperator",
+
+ "default link NvimInvalidBinaryOperator NvimInvalidOperator",
+ "default link NvimInvalidComparison NvimInvalidBinaryOperator",
+ "default link NvimInvalidComparisonModifier NvimInvalidComparison",
+ "default link NvimInvalidBinaryPlus NvimInvalidBinaryOperator",
+ "default link NvimInvalidBinaryMinus NvimInvalidBinaryOperator",
+ "default link NvimInvalidConcat NvimInvalidBinaryOperator",
+ "default link NvimInvalidConcatOrSubscript NvimInvalidConcat",
+ "default link NvimInvalidOr NvimInvalidBinaryOperator",
+ "default link NvimInvalidAnd NvimInvalidBinaryOperator",
+ "default link NvimInvalidMultiplication NvimInvalidBinaryOperator",
+ "default link NvimInvalidDivision NvimInvalidBinaryOperator",
+ "default link NvimInvalidMod NvimInvalidBinaryOperator",
+
+ "default link NvimInvalidTernary NvimInvalidOperator",
+ "default link NvimInvalidTernaryColon NvimInvalidTernary",
+
+ "default link NvimInvalidDelimiter NvimInvalid",
+
+ "default link NvimInvalidParenthesis NvimInvalidDelimiter",
+ "default link NvimInvalidLambda NvimInvalidParenthesis",
+ "default link NvimInvalidNestingParenthesis NvimInvalidParenthesis",
+ "default link NvimInvalidCallingParenthesis NvimInvalidParenthesis",
+
+ "default link NvimInvalidSubscript NvimInvalidParenthesis",
+ "default link NvimInvalidSubscriptBracket NvimInvalidSubscript",
+ "default link NvimInvalidSubscriptColon NvimInvalidSubscript",
+ "default link NvimInvalidCurly NvimInvalidSubscript",
+
+ "default link NvimInvalidContainer NvimInvalidParenthesis",
+ "default link NvimInvalidDict NvimInvalidContainer",
+ "default link NvimInvalidList NvimInvalidContainer",
+
+ "default link NvimInvalidValue NvimInvalid",
+
+ "default link NvimInvalidIdentifier NvimInvalidValue",
+ "default link NvimInvalidIdentifierScope NvimInvalidIdentifier",
+ "default link NvimInvalidIdentifierScopeDelimiter NvimInvalidIdentifier",
+ "default link NvimInvalidIdentifierName NvimInvalidIdentifier",
+ "default link NvimInvalidIdentifierKey NvimInvalidIdentifier",
+
+ "default link NvimInvalidColon NvimInvalidDelimiter",
+ "default link NvimInvalidComma NvimInvalidDelimiter",
+ "default link NvimInvalidArrow NvimInvalidDelimiter",
+
+ "default link NvimInvalidRegister NvimInvalidValue",
+ "default link NvimInvalidNumber NvimInvalidValue",
+ "default link NvimInvalidFloat NvimInvalidNumber",
+ "default link NvimInvalidNumberPrefix NvimInvalidNumber",
+
+ "default link NvimInvalidOptionSigil NvimInvalidIdentifier",
+ "default link NvimInvalidOptionName NvimInvalidIdentifier",
+ "default link NvimInvalidOptionScope NvimInvalidIdentifierScope",
+ "default link NvimInvalidOptionScopeDelimiter NvimInvalidIdentifierScopeDelimiter",
+
+ "default link NvimInvalidEnvironmentSigil NvimInvalidOptionSigil",
+ "default link NvimInvalidEnvironmentName NvimInvalidIdentifier",
+
+ // Invalid string bodies and specials are still highlighted as valid ones to
+ // minimize the red area.
+ "default link NvimInvalidString NvimInvalidValue",
+ "default link NvimInvalidStringBody NvimStringBody",
+ "default link NvimInvalidStringQuote NvimInvalidString",
+ "default link NvimInvalidStringSpecial NvimStringSpecial",
+
+ "default link NvimInvalidSingleQuote NvimInvalidStringQuote",
+ "default link NvimInvalidSingleQuotedBody NvimInvalidStringBody",
+ "default link NvimInvalidSingleQuotedQuote NvimInvalidStringSpecial",
+
+ "default link NvimInvalidDoubleQuote NvimInvalidStringQuote",
+ "default link NvimInvalidDoubleQuotedBody NvimInvalidStringBody",
+ "default link NvimInvalidDoubleQuotedEscape NvimInvalidStringSpecial",
+ "default link NvimInvalidDoubleQuotedUnknownEscape NvimInvalidValue",
+
+ "default link NvimInvalidFigureBrace NvimInvalidDelimiter",
+
+ "default link NvimInvalidSpacing ErrorMsg",
+
+ // Not actually invalid, but we show the user that they are doing something
+ // wrong.
+ "default link NvimDoubleQuotedUnknownEscape NvimInvalidValue",
+ NULL,
+};
+
+/// Returns the number of highlight groups.
+int highlight_num_groups(void)
+{
+ return highlight_ga.ga_len;
+}
+
+/// Returns the name of a highlight group.
+char_u *highlight_group_name(int id)
+{
+ return hl_table[id].sg_name;
+}
+
+/// Returns the ID of the link to a highlight group.
+int highlight_link_id(int id)
+{
+ return hl_table[id].sg_link;
+}
+
+/// Create default links for Nvim* highlight groups used for cmdline coloring
+void syn_init_cmdline_highlight(bool reset, bool init)
+{
+ for (size_t i = 0; highlight_init_cmdline[i] != NULL; i++) {
+ do_highlight(highlight_init_cmdline[i], reset, init);
+ }
+}
+
+/// Load colors from a file if "g:colors_name" is set, otherwise load builtin
+/// colors
+///
+/// @param both include groups where 'bg' doesn't matter
+/// @param reset clear groups first
+void init_highlight(bool both, bool reset)
+{
+ static int had_both = false;
+
+ // Try finding the color scheme file. Used when a color file was loaded
+ // and 'background' or 't_Co' is changed.
+ char_u *p = get_var_value("g:colors_name");
+ if (p != NULL) {
+ // Value of g:colors_name could be freed in load_colors() and make
+ // p invalid, so copy it.
+ char_u *copy_p = vim_strsave(p);
+ bool okay = load_colors(copy_p);
+ xfree(copy_p);
+ if (okay) {
+ return;
+ }
+ }
+
+ // Didn't use a color file, use the compiled-in colors.
+ if (both) {
+ had_both = true;
+ const char *const *const pp = highlight_init_both;
+ for (size_t i = 0; pp[i] != NULL; i++) {
+ do_highlight(pp[i], reset, true);
+ }
+ } else if (!had_both) {
+ // Don't do anything before the call with both == true from main().
+ // Not everything has been setup then, and that call will overrule
+ // everything anyway.
+ return;
+ }
+
+ const char *const *const pp = ((*p_bg == 'l')
+ ? highlight_init_light
+ : highlight_init_dark);
+ for (size_t i = 0; pp[i] != NULL; i++) {
+ do_highlight(pp[i], reset, true);
+ }
+
+ // Reverse looks ugly, but grey may not work for 8 colors. Thus let it
+ // depend on the number of colors available.
+ // With 8 colors brown is equal to yellow, need to use black for Search fg
+ // to avoid Statement highlighted text disappears.
+ // Clear the attributes, needed when changing the t_Co value.
+ if (t_colors > 8) {
+ do_highlight((*p_bg == 'l'
+ ? "Visual cterm=NONE ctermbg=LightGrey"
+ : "Visual cterm=NONE ctermbg=DarkGrey"), false, true);
+ } else {
+ do_highlight("Visual cterm=reverse ctermbg=NONE", false, true);
+ if (*p_bg == 'l') {
+ do_highlight("Search ctermfg=black", false, true);
+ }
+ }
+
+ syn_init_cmdline_highlight(false, false);
+}
+
+/// Load color file "name".
+/// Return OK for success, FAIL for failure.
+int load_colors(char_u *name)
+{
+ char_u *buf;
+ int retval = FAIL;
+ static bool recursive = false;
+
+ // When being called recursively, this is probably because setting
+ // 'background' caused the highlighting to be reloaded. This means it is
+ // working, thus we should return OK.
+ if (recursive) {
+ return OK;
+ }
+
+ recursive = true;
+ size_t buflen = STRLEN(name) + 12;
+ buf = xmalloc(buflen);
+ apply_autocmds(EVENT_COLORSCHEMEPRE, (char *)name, curbuf->b_fname, false, curbuf);
+ snprintf((char *)buf, buflen, "colors/%s.vim", name);
+ retval = source_runtime((char *)buf, DIP_START + DIP_OPT);
+ if (retval == FAIL) {
+ snprintf((char *)buf, buflen, "colors/%s.lua", name);
+ retval = source_runtime((char *)buf, DIP_START + DIP_OPT);
+ }
+ xfree(buf);
+ apply_autocmds(EVENT_COLORSCHEME, (char *)name, curbuf->b_fname, false, curbuf);
+
+ recursive = false;
+
+ return retval;
+}
+
+static char *(color_names[28]) = {
+ "Black", "DarkBlue", "DarkGreen", "DarkCyan",
+ "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
+ "Gray", "Grey", "LightGray", "LightGrey",
+ "DarkGray", "DarkGrey",
+ "Blue", "LightBlue", "Green", "LightGreen",
+ "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
+ "LightMagenta", "Yellow", "LightYellow", "White", "NONE"
+};
+// indices:
+// 0, 1, 2, 3,
+// 4, 5, 6, 7,
+// 8, 9, 10, 11,
+// 12, 13,
+// 14, 15, 16, 17,
+// 18, 19, 20, 21, 22,
+// 23, 24, 25, 26, 27
+static int color_numbers_16[28] = { 0, 1, 2, 3,
+ 4, 5, 6, 6,
+ 7, 7, 7, 7,
+ 8, 8,
+ 9, 9, 10, 10,
+ 11, 11, 12, 12, 13,
+ 13, 14, 14, 15, -1 };
+// for xterm with 88 colors...
+static int color_numbers_88[28] = { 0, 4, 2, 6,
+ 1, 5, 32, 72,
+ 84, 84, 7, 7,
+ 82, 82,
+ 12, 43, 10, 61,
+ 14, 63, 9, 74, 13,
+ 75, 11, 78, 15, -1 };
+// for xterm with 256 colors...
+static int color_numbers_256[28] = { 0, 4, 2, 6,
+ 1, 5, 130, 3,
+ 248, 248, 7, 7,
+ 242, 242,
+ 12, 81, 10, 121,
+ 14, 159, 9, 224, 13,
+ 225, 11, 229, 15, -1 };
+// for terminals with less than 16 colors...
+static int color_numbers_8[28] = { 0, 4, 2, 6,
+ 1, 5, 3, 3,
+ 7, 7, 7, 7,
+ 0 + 8, 0 + 8,
+ 4 + 8, 4 + 8, 2 + 8, 2 + 8,
+ 6 + 8, 6 + 8, 1 + 8, 1 + 8, 5 + 8,
+ 5 + 8, 3 + 8, 3 + 8, 7 + 8, -1 };
+
+// Lookup the "cterm" value to be used for color with index "idx" in
+// color_names[].
+// "boldp" will be set to TRUE or FALSE for a foreground color when using 8
+// colors, otherwise it will be unchanged.
+int lookup_color(const int idx, const bool foreground, TriState *const boldp)
+{
+ int color = color_numbers_16[idx];
+
+ // Use the _16 table to check if it's a valid color name.
+ if (color < 0) {
+ return -1;
+ }
+
+ if (t_colors == 8) {
+ // t_Co is 8: use the 8 colors table
+ color = color_numbers_8[idx];
+ if (foreground) {
+ // set/reset bold attribute to get light foreground
+ // colors (on some terminals, e.g. "linux")
+ if (color & 8) {
+ *boldp = kTrue;
+ } else {
+ *boldp = kFalse;
+ }
+ }
+ color &= 7; // truncate to 8 colors
+ } else if (t_colors == 16) {
+ color = color_numbers_8[idx];
+ } else if (t_colors == 88) {
+ color = color_numbers_88[idx];
+ } else if (t_colors >= 256) {
+ color = color_numbers_256[idx];
+ }
+ return color;
+}
+
+void set_hl_group(int id, HlAttrs attrs, Dict(highlight) *dict, int link_id)
+{
+ int idx = id - 1; // Index is ID minus one.
+
+ bool is_default = attrs.rgb_ae_attr & HL_DEFAULT;
+
+ // Return if "default" was used and the group already has settings
+ if (is_default && hl_has_settings(idx, true)) {
+ return;
+ }
+
+ HlGroup *g = &hl_table[idx];
+
+ if (link_id > 0) {
+ g->sg_cleared = false;
+ g->sg_link = link_id;
+ g->sg_script_ctx = current_sctx;
+ g->sg_script_ctx.sc_lnum += sourcing_lnum;
+ g->sg_set |= SG_LINK;
+ if (is_default) {
+ g->sg_deflink = link_id;
+ g->sg_deflink_sctx = current_sctx;
+ g->sg_deflink_sctx.sc_lnum += sourcing_lnum;
+ }
+ return;
+ }
+
+ g->sg_cleared = false;
+ g->sg_link = 0;
+ g->sg_gui = attrs.rgb_ae_attr;
+
+ g->sg_rgb_fg = attrs.rgb_fg_color;
+ g->sg_rgb_bg = attrs.rgb_bg_color;
+ g->sg_rgb_sp = attrs.rgb_sp_color;
+
+ struct {
+ int *dest; RgbValue val; Object name;
+ } cattrs[] = {
+ { &g->sg_rgb_fg_idx, g->sg_rgb_fg, HAS_KEY(dict->fg) ? dict->fg : dict->foreground },
+ { &g->sg_rgb_bg_idx, g->sg_rgb_bg, HAS_KEY(dict->bg) ? dict->bg : dict->background },
+ { &g->sg_rgb_sp_idx, g->sg_rgb_sp, HAS_KEY(dict->sp) ? dict->sp : dict->special },
+ { NULL, -1, NIL },
+ };
+
+ for (int j = 0; cattrs[j].dest; j++) {
+ if (cattrs[j].val < 0) {
+ *cattrs[j].dest = kColorIdxNone;
+ } else if (cattrs[j].name.type == kObjectTypeString && cattrs[j].name.data.string.size) {
+ name_to_color(cattrs[j].name.data.string.data, cattrs[j].dest);
+ } else {
+ *cattrs[j].dest = kColorIdxHex;
+ }
+ }
+
+ g->sg_cterm = attrs.cterm_ae_attr;
+ g->sg_cterm_bg = attrs.cterm_bg_color;
+ g->sg_cterm_fg = attrs.cterm_fg_color;
+ g->sg_cterm_bold = g->sg_cterm & HL_BOLD;
+ g->sg_blend = attrs.hl_blend;
+
+ g->sg_script_ctx = current_sctx;
+ g->sg_script_ctx.sc_lnum += sourcing_lnum;
+
+ g->sg_attr = hl_get_syn_attr(0, id, attrs);
+
+ // 'Normal' is special
+ if (STRCMP(g->sg_name_u, "NORMAL") == 0) {
+ cterm_normal_fg_color = g->sg_cterm_fg;
+ cterm_normal_bg_color = g->sg_cterm_bg;
+ normal_fg = g->sg_rgb_fg;
+ normal_bg = g->sg_rgb_bg;
+ normal_sp = g->sg_rgb_sp;
+ ui_default_colors_set();
+ } else {
+ // a cursor style uses this syn_id, make sure its attribute is updated.
+ if (cursor_mode_uses_syn_id(id)) {
+ ui_mode_info_set();
+ }
+ }
+}
+
+/// Handle ":highlight" command
+///
+/// When using ":highlight clear" this is called recursively for each group with
+/// forceit and init being both true.
+///
+/// @param[in] line Command arguments.
+/// @param[in] forceit True when bang is given, allows to link group even if
+/// it has its own settings.
+/// @param[in] init True when initializing.
+void do_highlight(const char *line, const bool forceit, const bool init)
+ FUNC_ATTR_NONNULL_ALL
+{
+ const char *name_end;
+ const char *linep;
+ const char *key_start;
+ const char *arg_start;
+ int off;
+ int len;
+ int attr;
+ int id;
+ int idx;
+ HlGroup item_before;
+ bool did_change = false;
+ bool dodefault = false;
+ bool doclear = false;
+ bool dolink = false;
+ bool error = false;
+ int color;
+ bool is_normal_group = false; // "Normal" group
+ bool did_highlight_changed = false;
+
+ // If no argument, list current highlighting.
+ if (ends_excmd((uint8_t)(*line))) {
+ for (int i = 1; i <= highlight_ga.ga_len && !got_int; i++) {
+ // TODO(brammool): only call when the group has attributes set
+ highlight_list_one(i);
+ }
+ return;
+ }
+
+ // Isolate the name.
+ name_end = (const char *)skiptowhite((const char_u *)line);
+ linep = (const char *)skipwhite(name_end);
+
+ // Check for "default" argument.
+ if (strncmp(line, "default", (size_t)(name_end - line)) == 0) {
+ dodefault = true;
+ line = linep;
+ name_end = (const char *)skiptowhite((const char_u *)line);
+ linep = (const char *)skipwhite(name_end);
+ }
+
+ // Check for "clear" or "link" argument.
+ if (strncmp(line, "clear", (size_t)(name_end - line)) == 0) {
+ doclear = true;
+ } else if (strncmp(line, "link", (size_t)(name_end - line)) == 0) {
+ dolink = true;
+ }
+
+ // ":highlight {group-name}": list highlighting for one group.
+ if (!doclear && !dolink && ends_excmd((uint8_t)(*linep))) {
+ id = syn_name2id_len(line, (size_t)(name_end - line));
+ if (id == 0) {
+ semsg(_("E411: highlight group not found: %s"), line);
+ } else {
+ highlight_list_one(id);
+ }
+ return;
+ }
+
+ // Handle ":highlight link {from} {to}" command.
+ if (dolink) {
+ const char *from_start = linep;
+ const char *from_end;
+ const char *to_start;
+ const char *to_end;
+ int from_id;
+ int to_id;
+ HlGroup *hlgroup = NULL;
+
+ from_end = (const char *)skiptowhite((const char_u *)from_start);
+ to_start = (const char *)skipwhite(from_end);
+ to_end = (const char *)skiptowhite((const char_u *)to_start);
+
+ if (ends_excmd((uint8_t)(*from_start))
+ || ends_excmd((uint8_t)(*to_start))) {
+ semsg(_("E412: Not enough arguments: \":highlight link %s\""),
+ from_start);
+ return;
+ }
+
+ if (!ends_excmd(*skipwhite(to_end))) {
+ semsg(_("E413: Too many arguments: \":highlight link %s\""), from_start);
+ return;
+ }
+
+ from_id = syn_check_group(from_start, (size_t)(from_end - from_start));
+ if (strncmp(to_start, "NONE", 4) == 0) {
+ to_id = 0;
+ } else {
+ to_id = syn_check_group(to_start, (size_t)(to_end - to_start));
+ }
+
+ if (from_id > 0) {
+ hlgroup = &hl_table[from_id - 1];
+ if (dodefault && (forceit || hlgroup->sg_deflink == 0)) {
+ hlgroup->sg_deflink = to_id;
+ hlgroup->sg_deflink_sctx = current_sctx;
+ hlgroup->sg_deflink_sctx.sc_lnum += sourcing_lnum;
+ nlua_set_sctx(&hlgroup->sg_deflink_sctx);
+ }
+ }
+
+ if (from_id > 0 && (!init || hlgroup->sg_set == 0)) {
+ // Don't allow a link when there already is some highlighting
+ // for the group, unless '!' is used
+ if (to_id > 0 && !forceit && !init
+ && hl_has_settings(from_id - 1, dodefault)) {
+ if (sourcing_name == NULL && !dodefault) {
+ emsg(_("E414: group has settings, highlight link ignored"));
+ }
+ } else if (hlgroup->sg_link != to_id
+ || hlgroup->sg_script_ctx.sc_sid != current_sctx.sc_sid
+ || hlgroup->sg_cleared) {
+ if (!init) {
+ hlgroup->sg_set |= SG_LINK;
+ }
+ hlgroup->sg_link = to_id;
+ hlgroup->sg_script_ctx = current_sctx;
+ hlgroup->sg_script_ctx.sc_lnum += sourcing_lnum;
+ nlua_set_sctx(&hlgroup->sg_script_ctx);
+ hlgroup->sg_cleared = false;
+ redraw_all_later(SOME_VALID);
+
+ // Only call highlight changed() once after multiple changes
+ need_highlight_changed = true;
+ }
+ }
+
+ return;
+ }
+
+ if (doclear) {
+ // ":highlight clear [group]" command.
+ line = linep;
+ if (ends_excmd((uint8_t)(*line))) {
+ do_unlet(S_LEN("colors_name"), true);
+ restore_cterm_colors();
+
+ // Clear all default highlight groups and load the defaults.
+ for (int j = 0; j < highlight_ga.ga_len; j++) {
+ highlight_clear(j);
+ }
+ init_highlight(true, true);
+ highlight_changed();
+ redraw_all_later(NOT_VALID);
+ return;
+ }
+ name_end = (const char *)skiptowhite((const char_u *)line);
+ linep = (const char *)skipwhite(name_end);
+ }
+
+ // Find the group name in the table. If it does not exist yet, add it.
+ id = syn_check_group(line, (size_t)(name_end - line));
+ if (id == 0) { // Failed (out of memory).
+ return;
+ }
+ idx = id - 1; // Index is ID minus one.
+
+ // Return if "default" was used and the group already has settings
+ if (dodefault && hl_has_settings(idx, true)) {
+ return;
+ }
+
+ // Make a copy so we can check if any attribute actually changed
+ item_before = hl_table[idx];
+ is_normal_group = (STRCMP(hl_table[idx].sg_name_u, "NORMAL") == 0);
+
+ // Clear the highlighting for ":hi clear {group}" and ":hi clear".
+ if (doclear || (forceit && init)) {
+ highlight_clear(idx);
+ if (!doclear) {
+ hl_table[idx].sg_set = 0;
+ }
+ }
+
+ char key[64];
+ char arg[512];
+ if (!doclear) {
+ while (!ends_excmd((uint8_t)(*linep))) {
+ key_start = linep;
+ if (*linep == '=') {
+ semsg(_("E415: unexpected equal sign: %s"), key_start);
+ error = true;
+ break;
+ }
+
+ // Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg",
+ // "guibg" or "guisp").
+ while (*linep && !ascii_iswhite(*linep) && *linep != '=') {
+ linep++;
+ }
+ size_t key_len = (size_t)(linep - key_start);
+ if (key_len > sizeof key - 1) {
+ semsg(_("E423: Illegal argument"));
+ error = true;
+ break;
+ }
+ memcpy(key, key_start, key_len);
+ key[key_len] = NUL;
+ vim_strup((char_u *)key);
+ linep = (const char *)skipwhite(linep);
+
+ if (strcmp(key, "NONE") == 0) {
+ if (!init || hl_table[idx].sg_set == 0) {
+ if (!init) {
+ hl_table[idx].sg_set |= SG_CTERM + SG_GUI;
+ }
+ highlight_clear(idx);
+ }
+ continue;
+ }
+
+ // Check for the equal sign.
+ if (*linep != '=') {
+ semsg(_("E416: missing equal sign: %s"), key_start);
+ error = true;
+ break;
+ }
+ linep++;
+
+ // Isolate the argument.
+ linep = (const char *)skipwhite(linep);
+ if (*linep == '\'') { // guifg='color name'
+ arg_start = ++linep;
+ linep = strchr(linep, '\'');
+ if (linep == NULL) {
+ semsg(_(e_invarg2), key_start);
+ error = true;
+ break;
+ }
+ } else {
+ arg_start = linep;
+ linep = (const char *)skiptowhite((const char_u *)linep);
+ }
+ if (linep == arg_start) {
+ semsg(_("E417: missing argument: %s"), key_start);
+ error = true;
+ break;
+ }
+ size_t arg_len = (size_t)(linep - arg_start);
+ if (arg_len > sizeof arg - 1) {
+ semsg(_("E423: Illegal argument"));
+ error = true;
+ break;
+ }
+ memcpy(arg, arg_start, arg_len);
+ arg[arg_len] = NUL;
+
+ if (*linep == '\'') {
+ linep++;
+ }
+
+ // Store the argument.
+ if (strcmp(key, "TERM") == 0
+ || strcmp(key, "CTERM") == 0
+ || strcmp(key, "GUI") == 0) {
+ attr = 0;
+ off = 0;
+ int i;
+ while (arg[off] != NUL) {
+ for (i = ARRAY_SIZE(hl_attr_table); --i >= 0;) {
+ len = (int)STRLEN(hl_name_table[i]);
+ if (STRNICMP(arg + off, hl_name_table[i], len) == 0) {
+ attr |= hl_attr_table[i];
+ off += len;
+ break;
+ }
+ }
+ if (i < 0) {
+ semsg(_("E418: Illegal value: %s"), arg);
+ error = true;
+ break;
+ }
+ if (arg[off] == ',') { // Another one follows.
+ off++;
+ }
+ }
+ if (error) {
+ break;
+ }
+ if (*key == 'C') {
+ if (!init || !(hl_table[idx].sg_set & SG_CTERM)) {
+ if (!init) {
+ hl_table[idx].sg_set |= SG_CTERM;
+ }
+ hl_table[idx].sg_cterm = attr;
+ hl_table[idx].sg_cterm_bold = false;
+ }
+ } else if (*key == 'G') {
+ if (!init || !(hl_table[idx].sg_set & SG_GUI)) {
+ if (!init) {
+ hl_table[idx].sg_set |= SG_GUI;
+ }
+ hl_table[idx].sg_gui = attr;
+ }
+ }
+ } else if (STRCMP(key, "FONT") == 0) {
+ // in non-GUI fonts are simply ignored
+ } else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0) {
+ if (!init || !(hl_table[idx].sg_set & SG_CTERM)) {
+ if (!init) {
+ hl_table[idx].sg_set |= SG_CTERM;
+ }
+
+ // When setting the foreground color, and previously the "bold"
+ // flag was set for a light color, reset it now
+ if (key[5] == 'F' && hl_table[idx].sg_cterm_bold) {
+ hl_table[idx].sg_cterm &= ~HL_BOLD;
+ hl_table[idx].sg_cterm_bold = false;
+ }
+
+ if (ascii_isdigit(*arg)) {
+ color = atoi(arg);
+ } else if (STRICMP(arg, "fg") == 0) {
+ if (cterm_normal_fg_color) {
+ color = cterm_normal_fg_color - 1;
+ } else {
+ emsg(_("E419: FG color unknown"));
+ error = true;
+ break;
+ }
+ } else if (STRICMP(arg, "bg") == 0) {
+ if (cterm_normal_bg_color > 0) {
+ color = cterm_normal_bg_color - 1;
+ } else {
+ emsg(_("E420: BG color unknown"));
+ error = true;
+ break;
+ }
+ } else {
+ // Reduce calls to STRICMP a bit, it can be slow.
+ off = TOUPPER_ASC(*arg);
+ int i;
+ for (i = ARRAY_SIZE(color_names); --i >= 0;) {
+ if (off == color_names[i][0]
+ && STRICMP(arg + 1, color_names[i] + 1) == 0) {
+ break;
+ }
+ }
+ if (i < 0) {
+ semsg(_("E421: Color name or number not recognized: %s"),
+ key_start);
+ error = true;
+ break;
+ }
+
+ TriState bold = kNone;
+ color = lookup_color(i, key[5] == 'F', &bold);
+
+ // set/reset bold attribute to get light foreground
+ // colors (on some terminals, e.g. "linux")
+ if (bold == kTrue) {
+ hl_table[idx].sg_cterm |= HL_BOLD;
+ hl_table[idx].sg_cterm_bold = true;
+ } else if (bold == kFalse) {
+ hl_table[idx].sg_cterm &= ~HL_BOLD;
+ }
+ }
+ // Add one to the argument, to avoid zero. Zero is used for
+ // "NONE", then "color" is -1.
+ if (key[5] == 'F') {
+ hl_table[idx].sg_cterm_fg = color + 1;
+ if (is_normal_group) {
+ cterm_normal_fg_color = color + 1;
+ }
+ } else {
+ hl_table[idx].sg_cterm_bg = color + 1;
+ if (is_normal_group) {
+ cterm_normal_bg_color = color + 1;
+ if (!ui_rgb_attached()) {
+ if (color >= 0) {
+ int dark = -1;
+
+ if (t_colors < 16) {
+ dark = (color == 0 || color == 4);
+ } else if (color < 16) {
+ // Limit the heuristic to the standard 16 colors
+ dark = (color < 7 || color == 8);
+ }
+ // Set the 'background' option if the value is
+ // wrong.
+ if (dark != -1
+ && dark != (*p_bg == 'd')
+ && !option_was_set("bg")) {
+ set_option_value("bg", 0L, (dark ? "dark" : "light"), 0);
+ reset_option_was_set("bg");
+ }
+ }
+ }
+ }
+ }
+ }
+ } else if (strcmp(key, "GUIFG") == 0) {
+ int *indexp = &hl_table[idx].sg_rgb_fg_idx;
+
+ if (!init || !(hl_table[idx].sg_set & SG_GUI)) {
+ if (!init) {
+ hl_table[idx].sg_set |= SG_GUI;
+ }
+
+ RgbValue old_color = hl_table[idx].sg_rgb_fg;
+ int old_idx = hl_table[idx].sg_rgb_fg_idx;
+
+ if (strcmp(arg, "NONE") != 0) {
+ hl_table[idx].sg_rgb_fg = name_to_color(arg, indexp);
+ } else {
+ hl_table[idx].sg_rgb_fg = -1;
+ hl_table[idx].sg_rgb_fg_idx = kColorIdxNone;
+ }
+
+ did_change = hl_table[idx].sg_rgb_fg != old_color || hl_table[idx].sg_rgb_fg != old_idx;
+ }
+
+ if (is_normal_group) {
+ normal_fg = hl_table[idx].sg_rgb_fg;
+ }
+ } else if (STRCMP(key, "GUIBG") == 0) {
+ int *indexp = &hl_table[idx].sg_rgb_bg_idx;
+
+ if (!init || !(hl_table[idx].sg_set & SG_GUI)) {
+ if (!init) {
+ hl_table[idx].sg_set |= SG_GUI;
+ }
+
+ RgbValue old_color = hl_table[idx].sg_rgb_bg;
+ int old_idx = hl_table[idx].sg_rgb_bg_idx;
+
+ if (STRCMP(arg, "NONE") != 0) {
+ hl_table[idx].sg_rgb_bg = name_to_color(arg, indexp);
+ } else {
+ hl_table[idx].sg_rgb_bg = -1;
+ hl_table[idx].sg_rgb_bg_idx = kColorIdxNone;
+ }
+
+ did_change = hl_table[idx].sg_rgb_bg != old_color || hl_table[idx].sg_rgb_bg != old_idx;
+ }
+
+ if (is_normal_group) {
+ normal_bg = hl_table[idx].sg_rgb_bg;
+ }
+ } else if (strcmp(key, "GUISP") == 0) {
+ int *indexp = &hl_table[idx].sg_rgb_sp_idx;
+
+ if (!init || !(hl_table[idx].sg_set & SG_GUI)) {
+ if (!init) {
+ hl_table[idx].sg_set |= SG_GUI;
+ }
+
+ RgbValue old_color = hl_table[idx].sg_rgb_sp;
+ int old_idx = hl_table[idx].sg_rgb_sp_idx;
+
+ if (strcmp(arg, "NONE") != 0) {
+ hl_table[idx].sg_rgb_sp = name_to_color(arg, indexp);
+ } else {
+ hl_table[idx].sg_rgb_sp = -1;
+ }
+
+ did_change = hl_table[idx].sg_rgb_sp != old_color || hl_table[idx].sg_rgb_sp != old_idx;
+ }
+
+ if (is_normal_group) {
+ normal_sp = hl_table[idx].sg_rgb_sp;
+ }
+ } else if (strcmp(key, "START") == 0 || strcmp(key, "STOP") == 0) {
+ // Ignored for now
+ } else if (strcmp(key, "BLEND") == 0) {
+ if (strcmp(arg, "NONE") != 0) {
+ hl_table[idx].sg_blend = (int)strtol(arg, NULL, 10);
+ } else {
+ hl_table[idx].sg_blend = -1;
+ }
+ } else {
+ semsg(_("E423: Illegal argument: %s"), key_start);
+ error = true;
+ break;
+ }
+ hl_table[idx].sg_cleared = false;
+
+ // When highlighting has been given for a group, don't link it.
+ if (!init || !(hl_table[idx].sg_set & SG_LINK)) {
+ hl_table[idx].sg_link = 0;
+ }
+
+ // Continue with next argument.
+ linep = (const char *)skipwhite(linep);
+ }
+ }
+
+ if (!error && is_normal_group) {
+ // Need to update all groups, because they might be using "bg" and/or
+ // "fg", which have been changed now.
+ highlight_attr_set_all();
+
+ if (!ui_has(kUILinegrid) && starting == 0) {
+ // Older UIs assume that we clear the screen after normal group is
+ // changed
+ ui_refresh();
+ } else {
+ // TUI and newer UIs will repaint the screen themselves. NOT_VALID
+ // redraw below will still handle usages of guibg=fg etc.
+ ui_default_colors_set();
+ }
+ did_highlight_changed = true;
+ redraw_all_later(NOT_VALID);
+ } else {
+ set_hl_attr(idx);
+ }
+ hl_table[idx].sg_script_ctx = current_sctx;
+ hl_table[idx].sg_script_ctx.sc_lnum += sourcing_lnum;
+ nlua_set_sctx(&hl_table[idx].sg_script_ctx);
+
+ // Only call highlight_changed() once, after a sequence of highlight
+ // commands, and only if an attribute actually changed
+ if ((did_change
+ || memcmp(&hl_table[idx], &item_before, sizeof(item_before)) != 0)
+ && !did_highlight_changed) {
+ // Do not trigger a redraw when highlighting is changed while
+ // redrawing. This may happen when evaluating 'statusline' changes the
+ // StatusLine group.
+ if (!updating_screen) {
+ redraw_all_later(NOT_VALID);
+ }
+ need_highlight_changed = true;
+ }
+}
+
+#if defined(EXITFREE)
+void free_highlight(void)
+{
+ ga_clear(&highlight_ga);
+ map_destroy(cstr_t, int)(&highlight_unames);
+ arena_mem_free(arena_finish(&highlight_arena), NULL);
+}
+
+#endif
+
+/// Reset the cterm colors to what they were before Vim was started, if
+/// possible. Otherwise reset them to zero.
+void restore_cterm_colors(void)
+{
+ normal_fg = -1;
+ normal_bg = -1;
+ normal_sp = -1;
+ cterm_normal_fg_color = 0;
+ cterm_normal_bg_color = 0;
+}
+
+/// @param check_link if true also check for an existing link.
+///
+/// @return TRUE if highlight group "idx" has any settings.
+static int hl_has_settings(int idx, bool check_link)
+{
+ return hl_table[idx].sg_cleared == 0
+ && (hl_table[idx].sg_attr != 0
+ || hl_table[idx].sg_cterm_fg != 0
+ || hl_table[idx].sg_cterm_bg != 0
+ || hl_table[idx].sg_rgb_fg_idx != kColorIdxNone
+ || hl_table[idx].sg_rgb_bg_idx != kColorIdxNone
+ || hl_table[idx].sg_rgb_sp_idx != kColorIdxNone
+ || (check_link && (hl_table[idx].sg_set & SG_LINK)));
+}
+
+/// Clear highlighting for one group.
+static void highlight_clear(int idx)
+{
+ hl_table[idx].sg_cleared = true;
+
+ hl_table[idx].sg_attr = 0;
+ hl_table[idx].sg_cterm = 0;
+ hl_table[idx].sg_cterm_bold = false;
+ hl_table[idx].sg_cterm_fg = 0;
+ hl_table[idx].sg_cterm_bg = 0;
+ hl_table[idx].sg_gui = 0;
+ hl_table[idx].sg_rgb_fg = -1;
+ hl_table[idx].sg_rgb_bg = -1;
+ hl_table[idx].sg_rgb_sp = -1;
+ hl_table[idx].sg_rgb_fg_idx = kColorIdxNone;
+ hl_table[idx].sg_rgb_bg_idx = kColorIdxNone;
+ hl_table[idx].sg_rgb_sp_idx = kColorIdxNone;
+ hl_table[idx].sg_blend = -1;
+ // Restore default link and context if they exist. Otherwise clears.
+ hl_table[idx].sg_link = hl_table[idx].sg_deflink;
+ // Since we set the default link, set the location to where the default
+ // link was set.
+ hl_table[idx].sg_script_ctx = hl_table[idx].sg_deflink_sctx;
+}
+
+/// \addtogroup LIST_XXX
+/// @{
+#define LIST_ATTR 1
+#define LIST_STRING 2
+#define LIST_INT 3
+/// @}
+
+static void highlight_list_one(const int id)
+{
+ const HlGroup *sgp = &hl_table[id - 1]; // index is ID minus one
+ bool didh = false;
+
+ if (message_filtered(sgp->sg_name)) {
+ return;
+ }
+
+ didh = highlight_list_arg(id, didh, LIST_ATTR,
+ sgp->sg_cterm, NULL, "cterm");
+ didh = highlight_list_arg(id, didh, LIST_INT,
+ sgp->sg_cterm_fg, NULL, "ctermfg");
+ didh = highlight_list_arg(id, didh, LIST_INT,
+ sgp->sg_cterm_bg, NULL, "ctermbg");
+
+ didh = highlight_list_arg(id, didh, LIST_ATTR,
+ sgp->sg_gui, NULL, "gui");
+ char hexbuf[8];
+ didh = highlight_list_arg(id, didh, LIST_STRING, 0,
+ coloridx_to_name(sgp->sg_rgb_fg_idx, sgp->sg_rgb_fg, hexbuf), "guifg");
+ didh = highlight_list_arg(id, didh, LIST_STRING, 0,
+ coloridx_to_name(sgp->sg_rgb_bg_idx, sgp->sg_rgb_bg, hexbuf), "guibg");
+ didh = highlight_list_arg(id, didh, LIST_STRING, 0,
+ coloridx_to_name(sgp->sg_rgb_sp_idx, sgp->sg_rgb_sp, hexbuf), "guisp");
+
+ didh = highlight_list_arg(id, didh, LIST_INT,
+ sgp->sg_blend + 1, NULL, "blend");
+
+ if (sgp->sg_link && !got_int) {
+ (void)syn_list_header(didh, 0, id, true);
+ didh = true;
+ msg_puts_attr("links to", HL_ATTR(HLF_D));
+ msg_putchar(' ');
+ msg_outtrans((char *)hl_table[hl_table[id - 1].sg_link - 1].sg_name);
+ }
+
+ if (!didh) {
+ highlight_list_arg(id, didh, LIST_STRING, 0, "cleared", "");
+ }
+ if (p_verbose > 0) {
+ last_set_msg(sgp->sg_script_ctx);
+ }
+}
+
+Dictionary get_global_hl_defs(void)
+{
+ Dictionary rv = ARRAY_DICT_INIT;
+ for (int i = 1; i <= highlight_ga.ga_len && !got_int; i++) {
+ Dictionary attrs = ARRAY_DICT_INIT;
+ HlGroup *h = &hl_table[i - 1];
+ if (h->sg_attr > 0) {
+ attrs = hlattrs2dict(syn_attr2entry(h->sg_attr), true);
+ } else if (h->sg_link > 0) {
+ const char *link = (const char *)hl_table[h->sg_link - 1].sg_name;
+ PUT(attrs, "link", STRING_OBJ(cstr_to_string(link)));
+ }
+ PUT(rv, (const char *)h->sg_name, DICTIONARY_OBJ(attrs));
+ }
+
+ return rv;
+}
+
+/// Outputs a highlight when doing ":hi MyHighlight"
+///
+/// @param type one of \ref LIST_XXX
+/// @param iarg integer argument used if \p type == LIST_INT
+/// @param sarg string used if \p type == LIST_STRING
+static bool highlight_list_arg(const int id, bool didh, const int type, int iarg, const char *sarg,
+ const char *const name)
+{
+ char buf[100];
+
+ if (got_int) {
+ return false;
+ }
+ if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0)) {
+ const char *ts = buf;
+ if (type == LIST_INT) {
+ snprintf((char *)buf, sizeof(buf), "%d", iarg - 1);
+ } else if (type == LIST_STRING) {
+ ts = sarg;
+ } else { // type == LIST_ATTR
+ buf[0] = NUL;
+ for (int i = 0; hl_attr_table[i] != 0; i++) {
+ if (iarg & hl_attr_table[i]) {
+ if (buf[0] != NUL) {
+ xstrlcat(buf, ",", 100);
+ }
+ xstrlcat(buf, hl_name_table[i], 100);
+ iarg &= ~hl_attr_table[i]; // don't want "inverse"
+ }
+ }
+ }
+
+ (void)syn_list_header(didh, vim_strsize((char *)ts) + (int)STRLEN(name) + 1, id, false);
+ didh = true;
+ if (!got_int) {
+ if (*name != NUL) {
+ msg_puts_attr(name, HL_ATTR(HLF_D));
+ msg_puts_attr("=", HL_ATTR(HLF_D));
+ }
+ msg_outtrans((char *)ts);
+ }
+ }
+ return didh;
+}
+
+/// Check whether highlight group has attribute
+///
+/// @param[in] id Highlight group to check.
+/// @param[in] flag Attribute to check.
+/// @param[in] modec 'g' for GUI, 'c' for term.
+///
+/// @return "1" if highlight group has attribute, NULL otherwise.
+const char *highlight_has_attr(const int id, const int flag, const int modec)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
+{
+ int attr;
+
+ if (id <= 0 || id > highlight_ga.ga_len) {
+ return NULL;
+ }
+
+ if (modec == 'g') {
+ attr = hl_table[id - 1].sg_gui;
+ } else {
+ attr = hl_table[id - 1].sg_cterm;
+ }
+
+ return (attr & flag) ? "1" : NULL;
+}
+
+/// Return color name of the given highlight group
+///
+/// @param[in] id Highlight group to work with.
+/// @param[in] what What to return: one of "font", "fg", "bg", "sp", "fg#",
+/// "bg#" or "sp#".
+/// @param[in] modec 'g' for GUI, 'c' for cterm and 't' for term.
+///
+/// @return color name, possibly in a static buffer. Buffer will be overwritten
+/// on next highlight_color() call. May return NULL.
+const char *highlight_color(const int id, const char *const what, const int modec)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
+{
+ static char name[20];
+ int n;
+ bool fg = false;
+ bool sp = false;
+ bool font = false;
+
+ if (id <= 0 || id > highlight_ga.ga_len) {
+ return NULL;
+ }
+
+ if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g') {
+ fg = true;
+ } else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
+ && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't') {
+ font = true;
+ } else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p') {
+ sp = true;
+ } else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g')) {
+ return NULL;
+ }
+ if (modec == 'g') {
+ if (what[2] == '#' && ui_rgb_attached()) {
+ if (fg) {
+ n = hl_table[id - 1].sg_rgb_fg;
+ } else if (sp) {
+ n = hl_table[id - 1].sg_rgb_sp;
+ } else {
+ n = hl_table[id - 1].sg_rgb_bg;
+ }
+ if (n < 0 || n > 0xffffff) {
+ return NULL;
+ }
+ snprintf(name, sizeof(name), "#%06x", n);
+ return name;
+ }
+ if (fg) {
+ return coloridx_to_name(hl_table[id - 1].sg_rgb_fg_idx, hl_table[id - 1].sg_rgb_fg, name);
+ } else if (sp) {
+ return coloridx_to_name(hl_table[id - 1].sg_rgb_sp_idx, hl_table[id - 1].sg_rgb_sp, name);
+ } else {
+ return coloridx_to_name(hl_table[id - 1].sg_rgb_bg_idx, hl_table[id - 1].sg_rgb_bg, name);
+ }
+ }
+ if (font || sp) {
+ return NULL;
+ }
+ if (modec == 'c') {
+ if (fg) {
+ n = hl_table[id - 1].sg_cterm_fg - 1;
+ } else {
+ n = hl_table[id - 1].sg_cterm_bg - 1;
+ }
+ if (n < 0) {
+ return NULL;
+ }
+ snprintf(name, sizeof(name), "%d", n);
+ return name;
+ }
+ // term doesn't have color.
+ return NULL;
+}
+
+/// Output the syntax list header.
+///
+/// @param did_header did header already
+/// @param outlen length of string that comes
+/// @param id highlight group id
+/// @param force_newline always start a new line
+/// @return true when started a new line.
+bool syn_list_header(const bool did_header, const int outlen, const int id, bool force_newline)
+{
+ int endcol = 19;
+ bool newline = true;
+ int name_col = 0;
+ bool adjust = true;
+
+ if (!did_header) {
+ msg_putchar('\n');
+ if (got_int) {
+ return true;
+ }
+ msg_outtrans((char *)hl_table[id - 1].sg_name);
+ name_col = msg_col;
+ endcol = 15;
+ } else if ((ui_has(kUIMessages) || msg_silent) && !force_newline) {
+ msg_putchar(' ');
+ adjust = false;
+ } else if (msg_col + outlen + 1 >= Columns || force_newline) {
+ msg_putchar('\n');
+ if (got_int) {
+ return true;
+ }
+ } else {
+ if (msg_col >= endcol) { // wrap around is like starting a new line
+ newline = false;
+ }
+ }
+
+ if (adjust) {
+ if (msg_col >= endcol) {
+ // output at least one space
+ endcol = msg_col + 1;
+ }
+
+ msg_advance(endcol);
+ }
+
+ // Show "xxx" with the attributes.
+ if (!did_header) {
+ if (endcol == Columns - 1 && endcol <= name_col) {
+ msg_putchar(' ');
+ }
+ msg_puts_attr("xxx", syn_id2attr(id));
+ msg_putchar(' ');
+ }
+
+ return newline;
+}
+
+/// Set the attribute numbers for a highlight group.
+/// Called after one of the attributes has changed.
+/// @param idx corrected highlight index
+static void set_hl_attr(int idx)
+{
+ HlAttrs at_en = HLATTRS_INIT;
+ HlGroup *sgp = hl_table + idx;
+
+ at_en.cterm_ae_attr = (int16_t)sgp->sg_cterm;
+ at_en.cterm_fg_color = sgp->sg_cterm_fg;
+ at_en.cterm_bg_color = sgp->sg_cterm_bg;
+ at_en.rgb_ae_attr = (int16_t)sgp->sg_gui;
+ // FIXME(tarruda): The "unset value" for rgb is -1, but since hlgroup is
+ // initialized with 0(by garray functions), check for sg_rgb_{f,b}g_name
+ // before setting attr_entry->{f,g}g_color to a other than -1
+ at_en.rgb_fg_color = sgp->sg_rgb_fg_idx != kColorIdxNone ? sgp->sg_rgb_fg : -1;
+ at_en.rgb_bg_color = sgp->sg_rgb_bg_idx != kColorIdxNone ? sgp->sg_rgb_bg : -1;
+ at_en.rgb_sp_color = sgp->sg_rgb_sp_idx != kColorIdxNone ? sgp->sg_rgb_sp : -1;
+ at_en.hl_blend = sgp->sg_blend;
+
+ sgp->sg_attr = hl_get_syn_attr(0, idx + 1, at_en);
+
+ // a cursor style uses this syn_id, make sure its attribute is updated.
+ if (cursor_mode_uses_syn_id(idx + 1)) {
+ ui_mode_info_set();
+ }
+}
+
+int syn_name2id(const char *name)
+ FUNC_ATTR_NONNULL_ALL
+{
+ return syn_name2id_len(name, STRLEN(name));
+}
+
+/// Lookup a highlight group name and return its ID.
+///
+/// @param highlight name e.g. 'Cursor', 'Normal'
+/// @return the highlight id, else 0 if \p name does not exist
+int syn_name2id_len(const char *name, size_t len)
+ FUNC_ATTR_NONNULL_ALL
+{
+ char name_u[MAX_SYN_NAME + 1];
+
+ if (len == 0 || len > MAX_SYN_NAME) {
+ return 0;
+ }
+
+ // Avoid using stricmp() too much, it's slow on some systems */
+ // Avoid alloc()/free(), these are slow too.
+ memcpy(name_u, name, len);
+ name_u[len] = '\0';
+ vim_strup((char_u *)name_u);
+
+ // map_get(..., int) returns 0 when no key is present, which is
+ // the expected value for missing highlight group.
+ return map_get(cstr_t, int)(&highlight_unames, name_u);
+}
+
+/// Lookup a highlight group name and return its attributes.
+/// Return zero if not found.
+int syn_name2attr(const char_u *name)
+ FUNC_ATTR_NONNULL_ALL
+{
+ int id = syn_name2id((char *)name);
+
+ if (id != 0) {
+ return syn_id2attr(id);
+ }
+ return 0;
+}
+
+/// Return TRUE if highlight group "name" exists.
+int highlight_exists(const char *name)
+{
+ return syn_name2id(name) > 0;
+}
+
+/// Return the name of highlight group "id".
+/// When not a valid ID return an empty string.
+char_u *syn_id2name(int id)
+{
+ if (id <= 0 || id > highlight_ga.ga_len) {
+ return (char_u *)"";
+ }
+ return hl_table[id - 1].sg_name;
+}
+
+/// Find highlight group name in the table and return its ID.
+/// If it doesn't exist yet, a new entry is created.
+///
+/// @param pp Highlight group name
+/// @param len length of \p pp
+///
+/// @return 0 for failure else the id of the group
+int syn_check_group(const char *name, size_t len)
+{
+ if (len > MAX_SYN_NAME) {
+ emsg(_(e_highlight_group_name_too_long));
+ return 0;
+ }
+ int id = syn_name2id_len(name, len);
+ if (id == 0) { // doesn't exist yet
+ return syn_add_group(name, len);
+ }
+ return id;
+}
+
+/// Add new highlight group and return its ID.
+///
+/// @param name must be an allocated string, it will be consumed.
+/// @return 0 for failure, else the allocated group id
+/// @see syn_check_group
+static int syn_add_group(const char *name, size_t len)
+{
+ // Check that the name is ASCII letters, digits and underscore.
+ for (size_t i = 0; i < len; i++) {
+ int c = (uint8_t)name[i];
+ if (!vim_isprintc(c)) {
+ emsg(_("E669: Unprintable character in group name"));
+ return 0;
+ } else if (!ASCII_ISALNUM(c) && c != '_') {
+ // This is an error, but since there previously was no check only give a warning.
+ msg_source(HL_ATTR(HLF_W));
+ msg(_("W18: Invalid character in group name"));
+ break;
+ }
+ }
+
+ // First call for this growarray: init growing array.
+ if (highlight_ga.ga_data == NULL) {
+ highlight_ga.ga_itemsize = sizeof(HlGroup);
+ ga_set_growsize(&highlight_ga, 10);
+ // 265 builtin groups, will always be used, plus some space
+ ga_grow(&highlight_ga, 300);
+ }
+
+ if (highlight_ga.ga_len >= MAX_HL_ID) {
+ emsg(_("E849: Too many highlight and syntax groups"));
+ return 0;
+ }
+
+ // Append another syntax_highlight entry.
+ HlGroup *hlgp = GA_APPEND_VIA_PTR(HlGroup, &highlight_ga);
+ memset(hlgp, 0, sizeof(*hlgp));
+ hlgp->sg_name = (char_u *)arena_memdupz(&highlight_arena, name, len);
+ hlgp->sg_rgb_bg = -1;
+ hlgp->sg_rgb_fg = -1;
+ hlgp->sg_rgb_sp = -1;
+ hlgp->sg_rgb_bg_idx = kColorIdxNone;
+ hlgp->sg_rgb_fg_idx = kColorIdxNone;
+ hlgp->sg_rgb_sp_idx = kColorIdxNone;
+ hlgp->sg_blend = -1;
+ hlgp->sg_name_u = arena_memdupz(&highlight_arena, name, len);
+ vim_strup((char_u *)hlgp->sg_name_u);
+
+ int id = highlight_ga.ga_len; // ID is index plus one
+
+ map_put(cstr_t, int)(&highlight_unames, hlgp->sg_name_u, id);
+
+ return id;
+}
+
+/// Translate a group ID to highlight attributes.
+/// @see syn_attr2entry
+int syn_id2attr(int hl_id)
+{
+ hl_id = syn_get_final_id(hl_id);
+ HlGroup *sgp = &hl_table[hl_id - 1]; // index is ID minus one
+
+ int attr = ns_get_hl(-1, hl_id, false, sgp->sg_set);
+ if (attr >= 0) {
+ return attr;
+ }
+ return sgp->sg_attr;
+}
+
+/// Translate a group ID to the final group ID (following links).
+int syn_get_final_id(int hl_id)
+{
+ int count;
+
+ if (hl_id > highlight_ga.ga_len || hl_id < 1) {
+ return 0; // Can be called from eval!!
+ }
+
+ // Follow links until there is no more.
+ // Look out for loops! Break after 100 links.
+ for (count = 100; --count >= 0;) {
+ HlGroup *sgp = &hl_table[hl_id - 1]; // index is ID minus one
+
+ // ACHTUNG: when using "tmp" attribute (no link) the function might be
+ // called twice. it needs be smart enough to remember attr only to
+ // syn_id2attr time
+ int check = ns_get_hl(-1, hl_id, true, sgp->sg_set);
+ if (check == 0) {
+ return hl_id; // how dare! it broke the link!
+ } else if (check > 0) {
+ hl_id = check;
+ continue;
+ }
+
+ if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len) {
+ break;
+ }
+ hl_id = sgp->sg_link;
+ }
+
+ return hl_id;
+}
+
+/// Refresh the color attributes of all highlight groups.
+void highlight_attr_set_all(void)
+{
+ for (int idx = 0; idx < highlight_ga.ga_len; idx++) {
+ HlGroup *sgp = &hl_table[idx];
+ if (sgp->sg_rgb_bg_idx == kColorIdxFg) {
+ sgp->sg_rgb_bg = normal_fg;
+ } else if (sgp->sg_rgb_bg_idx == kColorIdxBg) {
+ sgp->sg_rgb_bg = normal_bg;
+ }
+ if (sgp->sg_rgb_fg_idx == kColorIdxFg) {
+ sgp->sg_rgb_fg = normal_fg;
+ } else if (sgp->sg_rgb_fg_idx == kColorIdxBg) {
+ sgp->sg_rgb_fg = normal_bg;
+ }
+ if (sgp->sg_rgb_sp_idx == kColorIdxFg) {
+ sgp->sg_rgb_sp = normal_fg;
+ } else if (sgp->sg_rgb_sp_idx == kColorIdxBg) {
+ sgp->sg_rgb_sp = normal_bg;
+ }
+ set_hl_attr(idx);
+ }
+}
+
+// Apply difference between User[1-9] and HLF_S to HLF_SNC.
+static void combine_stl_hlt(int id, int id_S, int id_alt, int hlcnt, int i, int hlf, int *table)
+ FUNC_ATTR_NONNULL_ALL
+{
+ HlGroup *const hlt = hl_table;
+
+ if (id_alt == 0) {
+ memset(&hlt[hlcnt + i], 0, sizeof(HlGroup));
+ hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
+ hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
+ } else {
+ memmove(&hlt[hlcnt + i], &hlt[id_alt - 1], sizeof(HlGroup));
+ }
+ hlt[hlcnt + i].sg_link = 0;
+
+ hlt[hlcnt + i].sg_cterm ^= hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
+ if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg) {
+ hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
+ }
+ if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg) {
+ hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
+ }
+ hlt[hlcnt + i].sg_gui ^= hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
+ if (hlt[id - 1].sg_rgb_fg != hlt[id_S - 1].sg_rgb_fg) {
+ hlt[hlcnt + i].sg_rgb_fg = hlt[id - 1].sg_rgb_fg;
+ }
+ if (hlt[id - 1].sg_rgb_bg != hlt[id_S - 1].sg_rgb_bg) {
+ hlt[hlcnt + i].sg_rgb_bg = hlt[id - 1].sg_rgb_bg;
+ }
+ if (hlt[id - 1].sg_rgb_sp != hlt[id_S - 1].sg_rgb_sp) {
+ hlt[hlcnt + i].sg_rgb_sp = hlt[id - 1].sg_rgb_sp;
+ }
+ highlight_ga.ga_len = hlcnt + i + 1;
+ set_hl_attr(hlcnt + i); // At long last we can apply
+ table[i] = syn_id2attr(hlcnt + i + 1);
+}
+
+/// Translate highlight groups into attributes in highlight_attr[] and set up
+/// the user highlights User1..9. A set of corresponding highlights to use on
+/// top of HLF_SNC is computed. Called only when nvim starts and upon first
+/// screen redraw after any :highlight command.
+void highlight_changed(void)
+{
+ int id;
+ char userhl[30]; // use 30 to avoid compiler warning
+ int id_S = -1;
+ int id_SNC = 0;
+ int hlcnt;
+
+ need_highlight_changed = false;
+
+ /// Translate builtin highlight groups into attributes for quick lookup.
+ for (int hlf = 0; hlf < HLF_COUNT; hlf++) {
+ id = syn_check_group(hlf_names[hlf], STRLEN(hlf_names[hlf]));
+ if (id == 0) {
+ abort();
+ }
+ int final_id = syn_get_final_id(id);
+ if (hlf == HLF_SNC) {
+ id_SNC = final_id;
+ } else if (hlf == HLF_S) {
+ id_S = final_id;
+ }
+
+ highlight_attr[hlf] = hl_get_ui_attr(hlf, final_id,
+ (hlf == HLF_INACTIVE || hlf == HLF_LC));
+
+ if (highlight_attr[hlf] != highlight_attr_last[hlf]) {
+ if (hlf == HLF_MSG) {
+ clear_cmdline = true;
+ }
+ ui_call_hl_group_set(cstr_as_string((char *)hlf_names[hlf]),
+ highlight_attr[hlf]);
+ highlight_attr_last[hlf] = highlight_attr[hlf];
+ }
+ }
+
+ //
+ // Setup the user highlights
+ //
+ // Temporarily utilize 10 more hl entries:
+ // 9 for User1-User9 combined with StatusLineNC
+ // 1 for StatusLine default
+ // Must to be in there simultaneously in case of table overflows in
+ // get_attr_entry()
+ ga_grow(&highlight_ga, 10);
+ hlcnt = highlight_ga.ga_len;
+ if (id_S == -1) {
+ // Make sure id_S is always valid to simplify code below. Use the last entry
+ memset(&hl_table[hlcnt + 9], 0, sizeof(HlGroup));
+ id_S = hlcnt + 10;
+ }
+ for (int i = 0; i < 9; i++) {
+ snprintf(userhl, sizeof(userhl), "User%d", i + 1);
+ id = syn_name2id(userhl);
+ if (id == 0) {
+ highlight_user[i] = 0;
+ highlight_stlnc[i] = 0;
+ } else {
+ highlight_user[i] = syn_id2attr(id);
+ combine_stl_hlt(id, id_S, id_SNC, hlcnt, i, HLF_SNC, highlight_stlnc);
+ }
+ }
+ highlight_ga.ga_len = hlcnt;
+}
+
+/// Handle command line completion for :highlight command.
+void set_context_in_highlight_cmd(expand_T *xp, const char *arg)
+{
+ // Default: expand group names.
+ xp->xp_context = EXPAND_HIGHLIGHT;
+ xp->xp_pattern = (char *)arg;
+ include_link = 2;
+ include_default = 1;
+
+ // (part of) subcommand already typed
+ if (*arg != NUL) {
+ const char *p = (const char *)skiptowhite((const char_u *)arg);
+ if (*p != NUL) { // Past "default" or group name.
+ include_default = 0;
+ if (strncmp("default", arg, (unsigned)(p - arg)) == 0) {
+ arg = (const char *)skipwhite(p);
+ xp->xp_pattern = (char *)arg;
+ p = (const char *)skiptowhite((const char_u *)arg);
+ }
+ if (*p != NUL) { // past group name
+ include_link = 0;
+ if (arg[1] == 'i' && arg[0] == 'N') {
+ highlight_list();
+ }
+ if (strncmp("link", arg, (unsigned)(p - arg)) == 0
+ || strncmp("clear", arg, (unsigned)(p - arg)) == 0) {
+ xp->xp_pattern = skipwhite(p);
+ p = (const char *)skiptowhite((char_u *)xp->xp_pattern);
+ if (*p != NUL) { // Past first group name.
+ xp->xp_pattern = skipwhite(p);
+ p = (const char *)skiptowhite((char_u *)xp->xp_pattern);
+ }
+ }
+ if (*p != NUL) { // Past group name(s).
+ xp->xp_context = EXPAND_NOTHING;
+ }
+ }
+ }
+ }
+}
+
+/// List highlighting matches in a nice way.
+static void highlight_list(void)
+{
+ int i;
+
+ for (i = 10; --i >= 0;) {
+ highlight_list_two(i, HL_ATTR(HLF_D));
+ }
+ for (i = 40; --i >= 0;) {
+ highlight_list_two(99, 0);
+ }
+}
+
+static void highlight_list_two(int cnt, int attr)
+{
+ msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
+ msg_clr_eos();
+ ui_flush();
+ os_delay(cnt == 99 ? 40L : (uint64_t)cnt * 50L, false);
+}
+
+/// Function given to ExpandGeneric() to obtain the list of group names.
+const char *get_highlight_name(expand_T *const xp, int idx)
+ FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ return get_highlight_name_ext(xp, idx, true);
+}
+
+/// Obtain a highlight group name.
+///
+/// @param skip_cleared if true don't return a cleared entry.
+const char *get_highlight_name_ext(expand_T *xp, int idx, bool skip_cleared)
+ FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ if (idx < 0) {
+ return NULL;
+ }
+
+ // Items are never removed from the table, skip the ones that were cleared.
+ if (skip_cleared && idx < highlight_ga.ga_len && hl_table[idx].sg_cleared) {
+ return "";
+ }
+
+ if (idx == highlight_ga.ga_len && include_none != 0) {
+ return "none";
+ } else if (idx == highlight_ga.ga_len + include_none
+ && include_default != 0) {
+ return "default";
+ } else if (idx == highlight_ga.ga_len + include_none + include_default
+ && include_link != 0) {
+ return "link";
+ } else if (idx == highlight_ga.ga_len + include_none + include_default + 1
+ && include_link != 0) {
+ return "clear";
+ } else if (idx >= highlight_ga.ga_len) {
+ return NULL;
+ }
+ return (const char *)hl_table[idx].sg_name;
+}
+
+color_name_table_T color_name_table[] = {
+ // Colors from rgb.txt
+ { "AliceBlue", RGB_(0xf0, 0xf8, 0xff) },
+ { "AntiqueWhite", RGB_(0xfa, 0xeb, 0xd7) },
+ { "AntiqueWhite1", RGB_(0xff, 0xef, 0xdb) },
+ { "AntiqueWhite2", RGB_(0xee, 0xdf, 0xcc) },
+ { "AntiqueWhite3", RGB_(0xcd, 0xc0, 0xb0) },
+ { "AntiqueWhite4", RGB_(0x8b, 0x83, 0x78) },
+ { "Aqua", RGB_(0x00, 0xff, 0xff) },
+ { "Aquamarine", RGB_(0x7f, 0xff, 0xd4) },
+ { "Aquamarine1", RGB_(0x7f, 0xff, 0xd4) },
+ { "Aquamarine2", RGB_(0x76, 0xee, 0xc6) },
+ { "Aquamarine3", RGB_(0x66, 0xcd, 0xaa) },
+ { "Aquamarine4", RGB_(0x45, 0x8b, 0x74) },
+ { "Azure", RGB_(0xf0, 0xff, 0xff) },
+ { "Azure1", RGB_(0xf0, 0xff, 0xff) },
+ { "Azure2", RGB_(0xe0, 0xee, 0xee) },
+ { "Azure3", RGB_(0xc1, 0xcd, 0xcd) },
+ { "Azure4", RGB_(0x83, 0x8b, 0x8b) },
+ { "Beige", RGB_(0xf5, 0xf5, 0xdc) },
+ { "Bisque", RGB_(0xff, 0xe4, 0xc4) },
+ { "Bisque1", RGB_(0xff, 0xe4, 0xc4) },
+ { "Bisque2", RGB_(0xee, 0xd5, 0xb7) },
+ { "Bisque3", RGB_(0xcd, 0xb7, 0x9e) },
+ { "Bisque4", RGB_(0x8b, 0x7d, 0x6b) },
+ { "Black", RGB_(0x00, 0x00, 0x00) },
+ { "BlanchedAlmond", RGB_(0xff, 0xeb, 0xcd) },
+ { "Blue", RGB_(0x00, 0x00, 0xff) },
+ { "Blue1", RGB_(0x0, 0x0, 0xff) },
+ { "Blue2", RGB_(0x0, 0x0, 0xee) },
+ { "Blue3", RGB_(0x0, 0x0, 0xcd) },
+ { "Blue4", RGB_(0x0, 0x0, 0x8b) },
+ { "BlueViolet", RGB_(0x8a, 0x2b, 0xe2) },
+ { "Brown", RGB_(0xa5, 0x2a, 0x2a) },
+ { "Brown1", RGB_(0xff, 0x40, 0x40) },
+ { "Brown2", RGB_(0xee, 0x3b, 0x3b) },
+ { "Brown3", RGB_(0xcd, 0x33, 0x33) },
+ { "Brown4", RGB_(0x8b, 0x23, 0x23) },
+ { "BurlyWood", RGB_(0xde, 0xb8, 0x87) },
+ { "Burlywood1", RGB_(0xff, 0xd3, 0x9b) },
+ { "Burlywood2", RGB_(0xee, 0xc5, 0x91) },
+ { "Burlywood3", RGB_(0xcd, 0xaa, 0x7d) },
+ { "Burlywood4", RGB_(0x8b, 0x73, 0x55) },
+ { "CadetBlue", RGB_(0x5f, 0x9e, 0xa0) },
+ { "CadetBlue1", RGB_(0x98, 0xf5, 0xff) },
+ { "CadetBlue2", RGB_(0x8e, 0xe5, 0xee) },
+ { "CadetBlue3", RGB_(0x7a, 0xc5, 0xcd) },
+ { "CadetBlue4", RGB_(0x53, 0x86, 0x8b) },
+ { "ChartReuse", RGB_(0x7f, 0xff, 0x00) },
+ { "Chartreuse1", RGB_(0x7f, 0xff, 0x0) },
+ { "Chartreuse2", RGB_(0x76, 0xee, 0x0) },
+ { "Chartreuse3", RGB_(0x66, 0xcd, 0x0) },
+ { "Chartreuse4", RGB_(0x45, 0x8b, 0x0) },
+ { "Chocolate", RGB_(0xd2, 0x69, 0x1e) },
+ { "Chocolate1", RGB_(0xff, 0x7f, 0x24) },
+ { "Chocolate2", RGB_(0xee, 0x76, 0x21) },
+ { "Chocolate3", RGB_(0xcd, 0x66, 0x1d) },
+ { "Chocolate4", RGB_(0x8b, 0x45, 0x13) },
+ { "Coral", RGB_(0xff, 0x7f, 0x50) },
+ { "Coral1", RGB_(0xff, 0x72, 0x56) },
+ { "Coral2", RGB_(0xee, 0x6a, 0x50) },
+ { "Coral3", RGB_(0xcd, 0x5b, 0x45) },
+ { "Coral4", RGB_(0x8b, 0x3e, 0x2f) },
+ { "CornFlowerBlue", RGB_(0x64, 0x95, 0xed) },
+ { "Cornsilk", RGB_(0xff, 0xf8, 0xdc) },
+ { "Cornsilk1", RGB_(0xff, 0xf8, 0xdc) },
+ { "Cornsilk2", RGB_(0xee, 0xe8, 0xcd) },
+ { "Cornsilk3", RGB_(0xcd, 0xc8, 0xb1) },
+ { "Cornsilk4", RGB_(0x8b, 0x88, 0x78) },
+ { "Crimson", RGB_(0xdc, 0x14, 0x3c) },
+ { "Cyan", RGB_(0x00, 0xff, 0xff) },
+ { "Cyan1", RGB_(0x0, 0xff, 0xff) },
+ { "Cyan2", RGB_(0x0, 0xee, 0xee) },
+ { "Cyan3", RGB_(0x0, 0xcd, 0xcd) },
+ { "Cyan4", RGB_(0x0, 0x8b, 0x8b) },
+ { "DarkBlue", RGB_(0x00, 0x00, 0x8b) },
+ { "DarkCyan", RGB_(0x00, 0x8b, 0x8b) },
+ { "DarkGoldenRod", RGB_(0xb8, 0x86, 0x0b) },
+ { "DarkGoldenrod1", RGB_(0xff, 0xb9, 0xf) },
+ { "DarkGoldenrod2", RGB_(0xee, 0xad, 0xe) },
+ { "DarkGoldenrod3", RGB_(0xcd, 0x95, 0xc) },
+ { "DarkGoldenrod4", RGB_(0x8b, 0x65, 0x8) },
+ { "DarkGray", RGB_(0xa9, 0xa9, 0xa9) },
+ { "DarkGreen", RGB_(0x00, 0x64, 0x00) },
+ { "DarkGrey", RGB_(0xa9, 0xa9, 0xa9) },
+ { "DarkKhaki", RGB_(0xbd, 0xb7, 0x6b) },
+ { "DarkMagenta", RGB_(0x8b, 0x00, 0x8b) },
+ { "DarkOliveGreen", RGB_(0x55, 0x6b, 0x2f) },
+ { "DarkOliveGreen1", RGB_(0xca, 0xff, 0x70) },
+ { "DarkOliveGreen2", RGB_(0xbc, 0xee, 0x68) },
+ { "DarkOliveGreen3", RGB_(0xa2, 0xcd, 0x5a) },
+ { "DarkOliveGreen4", RGB_(0x6e, 0x8b, 0x3d) },
+ { "DarkOrange", RGB_(0xff, 0x8c, 0x00) },
+ { "DarkOrange1", RGB_(0xff, 0x7f, 0x0) },
+ { "DarkOrange2", RGB_(0xee, 0x76, 0x0) },
+ { "DarkOrange3", RGB_(0xcd, 0x66, 0x0) },
+ { "DarkOrange4", RGB_(0x8b, 0x45, 0x0) },
+ { "DarkOrchid", RGB_(0x99, 0x32, 0xcc) },
+ { "DarkOrchid1", RGB_(0xbf, 0x3e, 0xff) },
+ { "DarkOrchid2", RGB_(0xb2, 0x3a, 0xee) },
+ { "DarkOrchid3", RGB_(0x9a, 0x32, 0xcd) },
+ { "DarkOrchid4", RGB_(0x68, 0x22, 0x8b) },
+ { "DarkRed", RGB_(0x8b, 0x00, 0x00) },
+ { "DarkSalmon", RGB_(0xe9, 0x96, 0x7a) },
+ { "DarkSeaGreen", RGB_(0x8f, 0xbc, 0x8f) },
+ { "DarkSeaGreen1", RGB_(0xc1, 0xff, 0xc1) },
+ { "DarkSeaGreen2", RGB_(0xb4, 0xee, 0xb4) },
+ { "DarkSeaGreen3", RGB_(0x9b, 0xcd, 0x9b) },
+ { "DarkSeaGreen4", RGB_(0x69, 0x8b, 0x69) },
+ { "DarkSlateBlue", RGB_(0x48, 0x3d, 0x8b) },
+ { "DarkSlateGray", RGB_(0x2f, 0x4f, 0x4f) },
+ { "DarkSlateGray1", RGB_(0x97, 0xff, 0xff) },
+ { "DarkSlateGray2", RGB_(0x8d, 0xee, 0xee) },
+ { "DarkSlateGray3", RGB_(0x79, 0xcd, 0xcd) },
+ { "DarkSlateGray4", RGB_(0x52, 0x8b, 0x8b) },
+ { "DarkSlateGrey", RGB_(0x2f, 0x4f, 0x4f) },
+ { "DarkTurquoise", RGB_(0x00, 0xce, 0xd1) },
+ { "DarkViolet", RGB_(0x94, 0x00, 0xd3) },
+ { "DarkYellow", RGB_(0xbb, 0xbb, 0x00) },
+ { "DeepPink", RGB_(0xff, 0x14, 0x93) },
+ { "DeepPink1", RGB_(0xff, 0x14, 0x93) },
+ { "DeepPink2", RGB_(0xee, 0x12, 0x89) },
+ { "DeepPink3", RGB_(0xcd, 0x10, 0x76) },
+ { "DeepPink4", RGB_(0x8b, 0xa, 0x50) },
+ { "DeepSkyBlue", RGB_(0x00, 0xbf, 0xff) },
+ { "DeepSkyBlue1", RGB_(0x0, 0xbf, 0xff) },
+ { "DeepSkyBlue2", RGB_(0x0, 0xb2, 0xee) },
+ { "DeepSkyBlue3", RGB_(0x0, 0x9a, 0xcd) },
+ { "DeepSkyBlue4", RGB_(0x0, 0x68, 0x8b) },
+ { "DimGray", RGB_(0x69, 0x69, 0x69) },
+ { "DimGrey", RGB_(0x69, 0x69, 0x69) },
+ { "DodgerBlue", RGB_(0x1e, 0x90, 0xff) },
+ { "DodgerBlue1", RGB_(0x1e, 0x90, 0xff) },
+ { "DodgerBlue2", RGB_(0x1c, 0x86, 0xee) },
+ { "DodgerBlue3", RGB_(0x18, 0x74, 0xcd) },
+ { "DodgerBlue4", RGB_(0x10, 0x4e, 0x8b) },
+ { "Firebrick", RGB_(0xb2, 0x22, 0x22) },
+ { "Firebrick1", RGB_(0xff, 0x30, 0x30) },
+ { "Firebrick2", RGB_(0xee, 0x2c, 0x2c) },
+ { "Firebrick3", RGB_(0xcd, 0x26, 0x26) },
+ { "Firebrick4", RGB_(0x8b, 0x1a, 0x1a) },
+ { "FloralWhite", RGB_(0xff, 0xfa, 0xf0) },
+ { "ForestGreen", RGB_(0x22, 0x8b, 0x22) },
+ { "Fuchsia", RGB_(0xff, 0x00, 0xff) },
+ { "Gainsboro", RGB_(0xdc, 0xdc, 0xdc) },
+ { "GhostWhite", RGB_(0xf8, 0xf8, 0xff) },
+ { "Gold", RGB_(0xff, 0xd7, 0x00) },
+ { "Gold1", RGB_(0xff, 0xd7, 0x0) },
+ { "Gold2", RGB_(0xee, 0xc9, 0x0) },
+ { "Gold3", RGB_(0xcd, 0xad, 0x0) },
+ { "Gold4", RGB_(0x8b, 0x75, 0x0) },
+ { "GoldenRod", RGB_(0xda, 0xa5, 0x20) },
+ { "Goldenrod1", RGB_(0xff, 0xc1, 0x25) },
+ { "Goldenrod2", RGB_(0xee, 0xb4, 0x22) },
+ { "Goldenrod3", RGB_(0xcd, 0x9b, 0x1d) },
+ { "Goldenrod4", RGB_(0x8b, 0x69, 0x14) },
+ { "Gray", RGB_(0x80, 0x80, 0x80) },
+ { "Gray0", RGB_(0x0, 0x0, 0x0) },
+ { "Gray1", RGB_(0x3, 0x3, 0x3) },
+ { "Gray10", RGB_(0x1a, 0x1a, 0x1a) },
+ { "Gray100", RGB_(0xff, 0xff, 0xff) },
+ { "Gray11", RGB_(0x1c, 0x1c, 0x1c) },
+ { "Gray12", RGB_(0x1f, 0x1f, 0x1f) },
+ { "Gray13", RGB_(0x21, 0x21, 0x21) },
+ { "Gray14", RGB_(0x24, 0x24, 0x24) },
+ { "Gray15", RGB_(0x26, 0x26, 0x26) },
+ { "Gray16", RGB_(0x29, 0x29, 0x29) },
+ { "Gray17", RGB_(0x2b, 0x2b, 0x2b) },
+ { "Gray18", RGB_(0x2e, 0x2e, 0x2e) },
+ { "Gray19", RGB_(0x30, 0x30, 0x30) },
+ { "Gray2", RGB_(0x5, 0x5, 0x5) },
+ { "Gray20", RGB_(0x33, 0x33, 0x33) },
+ { "Gray21", RGB_(0x36, 0x36, 0x36) },
+ { "Gray22", RGB_(0x38, 0x38, 0x38) },
+ { "Gray23", RGB_(0x3b, 0x3b, 0x3b) },
+ { "Gray24", RGB_(0x3d, 0x3d, 0x3d) },
+ { "Gray25", RGB_(0x40, 0x40, 0x40) },
+ { "Gray26", RGB_(0x42, 0x42, 0x42) },
+ { "Gray27", RGB_(0x45, 0x45, 0x45) },
+ { "Gray28", RGB_(0x47, 0x47, 0x47) },
+ { "Gray29", RGB_(0x4a, 0x4a, 0x4a) },
+ { "Gray3", RGB_(0x8, 0x8, 0x8) },
+ { "Gray30", RGB_(0x4d, 0x4d, 0x4d) },
+ { "Gray31", RGB_(0x4f, 0x4f, 0x4f) },
+ { "Gray32", RGB_(0x52, 0x52, 0x52) },
+ { "Gray33", RGB_(0x54, 0x54, 0x54) },
+ { "Gray34", RGB_(0x57, 0x57, 0x57) },
+ { "Gray35", RGB_(0x59, 0x59, 0x59) },
+ { "Gray36", RGB_(0x5c, 0x5c, 0x5c) },
+ { "Gray37", RGB_(0x5e, 0x5e, 0x5e) },
+ { "Gray38", RGB_(0x61, 0x61, 0x61) },
+ { "Gray39", RGB_(0x63, 0x63, 0x63) },
+ { "Gray4", RGB_(0xa, 0xa, 0xa) },
+ { "Gray40", RGB_(0x66, 0x66, 0x66) },
+ { "Gray41", RGB_(0x69, 0x69, 0x69) },
+ { "Gray42", RGB_(0x6b, 0x6b, 0x6b) },
+ { "Gray43", RGB_(0x6e, 0x6e, 0x6e) },
+ { "Gray44", RGB_(0x70, 0x70, 0x70) },
+ { "Gray45", RGB_(0x73, 0x73, 0x73) },
+ { "Gray46", RGB_(0x75, 0x75, 0x75) },
+ { "Gray47", RGB_(0x78, 0x78, 0x78) },
+ { "Gray48", RGB_(0x7a, 0x7a, 0x7a) },
+ { "Gray49", RGB_(0x7d, 0x7d, 0x7d) },
+ { "Gray5", RGB_(0xd, 0xd, 0xd) },
+ { "Gray50", RGB_(0x7f, 0x7f, 0x7f) },
+ { "Gray51", RGB_(0x82, 0x82, 0x82) },
+ { "Gray52", RGB_(0x85, 0x85, 0x85) },
+ { "Gray53", RGB_(0x87, 0x87, 0x87) },
+ { "Gray54", RGB_(0x8a, 0x8a, 0x8a) },
+ { "Gray55", RGB_(0x8c, 0x8c, 0x8c) },
+ { "Gray56", RGB_(0x8f, 0x8f, 0x8f) },
+ { "Gray57", RGB_(0x91, 0x91, 0x91) },
+ { "Gray58", RGB_(0x94, 0x94, 0x94) },
+ { "Gray59", RGB_(0x96, 0x96, 0x96) },
+ { "Gray6", RGB_(0xf, 0xf, 0xf) },
+ { "Gray60", RGB_(0x99, 0x99, 0x99) },
+ { "Gray61", RGB_(0x9c, 0x9c, 0x9c) },
+ { "Gray62", RGB_(0x9e, 0x9e, 0x9e) },
+ { "Gray63", RGB_(0xa1, 0xa1, 0xa1) },
+ { "Gray64", RGB_(0xa3, 0xa3, 0xa3) },
+ { "Gray65", RGB_(0xa6, 0xa6, 0xa6) },
+ { "Gray66", RGB_(0xa8, 0xa8, 0xa8) },
+ { "Gray67", RGB_(0xab, 0xab, 0xab) },
+ { "Gray68", RGB_(0xad, 0xad, 0xad) },
+ { "Gray69", RGB_(0xb0, 0xb0, 0xb0) },
+ { "Gray7", RGB_(0x12, 0x12, 0x12) },
+ { "Gray70", RGB_(0xb3, 0xb3, 0xb3) },
+ { "Gray71", RGB_(0xb5, 0xb5, 0xb5) },
+ { "Gray72", RGB_(0xb8, 0xb8, 0xb8) },
+ { "Gray73", RGB_(0xba, 0xba, 0xba) },
+ { "Gray74", RGB_(0xbd, 0xbd, 0xbd) },
+ { "Gray75", RGB_(0xbf, 0xbf, 0xbf) },
+ { "Gray76", RGB_(0xc2, 0xc2, 0xc2) },
+ { "Gray77", RGB_(0xc4, 0xc4, 0xc4) },
+ { "Gray78", RGB_(0xc7, 0xc7, 0xc7) },
+ { "Gray79", RGB_(0xc9, 0xc9, 0xc9) },
+ { "Gray8", RGB_(0x14, 0x14, 0x14) },
+ { "Gray80", RGB_(0xcc, 0xcc, 0xcc) },
+ { "Gray81", RGB_(0xcf, 0xcf, 0xcf) },
+ { "Gray82", RGB_(0xd1, 0xd1, 0xd1) },
+ { "Gray83", RGB_(0xd4, 0xd4, 0xd4) },
+ { "Gray84", RGB_(0xd6, 0xd6, 0xd6) },
+ { "Gray85", RGB_(0xd9, 0xd9, 0xd9) },
+ { "Gray86", RGB_(0xdb, 0xdb, 0xdb) },
+ { "Gray87", RGB_(0xde, 0xde, 0xde) },
+ { "Gray88", RGB_(0xe0, 0xe0, 0xe0) },
+ { "Gray89", RGB_(0xe3, 0xe3, 0xe3) },
+ { "Gray9", RGB_(0x17, 0x17, 0x17) },
+ { "Gray90", RGB_(0xe5, 0xe5, 0xe5) },
+ { "Gray91", RGB_(0xe8, 0xe8, 0xe8) },
+ { "Gray92", RGB_(0xeb, 0xeb, 0xeb) },
+ { "Gray93", RGB_(0xed, 0xed, 0xed) },
+ { "Gray94", RGB_(0xf0, 0xf0, 0xf0) },
+ { "Gray95", RGB_(0xf2, 0xf2, 0xf2) },
+ { "Gray96", RGB_(0xf5, 0xf5, 0xf5) },
+ { "Gray97", RGB_(0xf7, 0xf7, 0xf7) },
+ { "Gray98", RGB_(0xfa, 0xfa, 0xfa) },
+ { "Gray99", RGB_(0xfc, 0xfc, 0xfc) },
+ { "Green", RGB_(0x00, 0x80, 0x00) },
+ { "Green1", RGB_(0x0, 0xff, 0x0) },
+ { "Green2", RGB_(0x0, 0xee, 0x0) },
+ { "Green3", RGB_(0x0, 0xcd, 0x0) },
+ { "Green4", RGB_(0x0, 0x8b, 0x0) },
+ { "GreenYellow", RGB_(0xad, 0xff, 0x2f) },
+ { "Grey", RGB_(0x80, 0x80, 0x80) },
+ { "Grey0", RGB_(0x0, 0x0, 0x0) },
+ { "Grey1", RGB_(0x3, 0x3, 0x3) },
+ { "Grey10", RGB_(0x1a, 0x1a, 0x1a) },
+ { "Grey100", RGB_(0xff, 0xff, 0xff) },
+ { "Grey11", RGB_(0x1c, 0x1c, 0x1c) },
+ { "Grey12", RGB_(0x1f, 0x1f, 0x1f) },
+ { "Grey13", RGB_(0x21, 0x21, 0x21) },
+ { "Grey14", RGB_(0x24, 0x24, 0x24) },
+ { "Grey15", RGB_(0x26, 0x26, 0x26) },
+ { "Grey16", RGB_(0x29, 0x29, 0x29) },
+ { "Grey17", RGB_(0x2b, 0x2b, 0x2b) },
+ { "Grey18", RGB_(0x2e, 0x2e, 0x2e) },
+ { "Grey19", RGB_(0x30, 0x30, 0x30) },
+ { "Grey2", RGB_(0x5, 0x5, 0x5) },
+ { "Grey20", RGB_(0x33, 0x33, 0x33) },
+ { "Grey21", RGB_(0x36, 0x36, 0x36) },
+ { "Grey22", RGB_(0x38, 0x38, 0x38) },
+ { "Grey23", RGB_(0x3b, 0x3b, 0x3b) },
+ { "Grey24", RGB_(0x3d, 0x3d, 0x3d) },
+ { "Grey25", RGB_(0x40, 0x40, 0x40) },
+ { "Grey26", RGB_(0x42, 0x42, 0x42) },
+ { "Grey27", RGB_(0x45, 0x45, 0x45) },
+ { "Grey28", RGB_(0x47, 0x47, 0x47) },
+ { "Grey29", RGB_(0x4a, 0x4a, 0x4a) },
+ { "Grey3", RGB_(0x8, 0x8, 0x8) },
+ { "Grey30", RGB_(0x4d, 0x4d, 0x4d) },
+ { "Grey31", RGB_(0x4f, 0x4f, 0x4f) },
+ { "Grey32", RGB_(0x52, 0x52, 0x52) },
+ { "Grey33", RGB_(0x54, 0x54, 0x54) },
+ { "Grey34", RGB_(0x57, 0x57, 0x57) },
+ { "Grey35", RGB_(0x59, 0x59, 0x59) },
+ { "Grey36", RGB_(0x5c, 0x5c, 0x5c) },
+ { "Grey37", RGB_(0x5e, 0x5e, 0x5e) },
+ { "Grey38", RGB_(0x61, 0x61, 0x61) },
+ { "Grey39", RGB_(0x63, 0x63, 0x63) },
+ { "Grey4", RGB_(0xa, 0xa, 0xa) },
+ { "Grey40", RGB_(0x66, 0x66, 0x66) },
+ { "Grey41", RGB_(0x69, 0x69, 0x69) },
+ { "Grey42", RGB_(0x6b, 0x6b, 0x6b) },
+ { "Grey43", RGB_(0x6e, 0x6e, 0x6e) },
+ { "Grey44", RGB_(0x70, 0x70, 0x70) },
+ { "Grey45", RGB_(0x73, 0x73, 0x73) },
+ { "Grey46", RGB_(0x75, 0x75, 0x75) },
+ { "Grey47", RGB_(0x78, 0x78, 0x78) },
+ { "Grey48", RGB_(0x7a, 0x7a, 0x7a) },
+ { "Grey49", RGB_(0x7d, 0x7d, 0x7d) },
+ { "Grey5", RGB_(0xd, 0xd, 0xd) },
+ { "Grey50", RGB_(0x7f, 0x7f, 0x7f) },
+ { "Grey51", RGB_(0x82, 0x82, 0x82) },
+ { "Grey52", RGB_(0x85, 0x85, 0x85) },
+ { "Grey53", RGB_(0x87, 0x87, 0x87) },
+ { "Grey54", RGB_(0x8a, 0x8a, 0x8a) },
+ { "Grey55", RGB_(0x8c, 0x8c, 0x8c) },
+ { "Grey56", RGB_(0x8f, 0x8f, 0x8f) },
+ { "Grey57", RGB_(0x91, 0x91, 0x91) },
+ { "Grey58", RGB_(0x94, 0x94, 0x94) },
+ { "Grey59", RGB_(0x96, 0x96, 0x96) },
+ { "Grey6", RGB_(0xf, 0xf, 0xf) },
+ { "Grey60", RGB_(0x99, 0x99, 0x99) },
+ { "Grey61", RGB_(0x9c, 0x9c, 0x9c) },
+ { "Grey62", RGB_(0x9e, 0x9e, 0x9e) },
+ { "Grey63", RGB_(0xa1, 0xa1, 0xa1) },
+ { "Grey64", RGB_(0xa3, 0xa3, 0xa3) },
+ { "Grey65", RGB_(0xa6, 0xa6, 0xa6) },
+ { "Grey66", RGB_(0xa8, 0xa8, 0xa8) },
+ { "Grey67", RGB_(0xab, 0xab, 0xab) },
+ { "Grey68", RGB_(0xad, 0xad, 0xad) },
+ { "Grey69", RGB_(0xb0, 0xb0, 0xb0) },
+ { "Grey7", RGB_(0x12, 0x12, 0x12) },
+ { "Grey70", RGB_(0xb3, 0xb3, 0xb3) },
+ { "Grey71", RGB_(0xb5, 0xb5, 0xb5) },
+ { "Grey72", RGB_(0xb8, 0xb8, 0xb8) },
+ { "Grey73", RGB_(0xba, 0xba, 0xba) },
+ { "Grey74", RGB_(0xbd, 0xbd, 0xbd) },
+ { "Grey75", RGB_(0xbf, 0xbf, 0xbf) },
+ { "Grey76", RGB_(0xc2, 0xc2, 0xc2) },
+ { "Grey77", RGB_(0xc4, 0xc4, 0xc4) },
+ { "Grey78", RGB_(0xc7, 0xc7, 0xc7) },
+ { "Grey79", RGB_(0xc9, 0xc9, 0xc9) },
+ { "Grey8", RGB_(0x14, 0x14, 0x14) },
+ { "Grey80", RGB_(0xcc, 0xcc, 0xcc) },
+ { "Grey81", RGB_(0xcf, 0xcf, 0xcf) },
+ { "Grey82", RGB_(0xd1, 0xd1, 0xd1) },
+ { "Grey83", RGB_(0xd4, 0xd4, 0xd4) },
+ { "Grey84", RGB_(0xd6, 0xd6, 0xd6) },
+ { "Grey85", RGB_(0xd9, 0xd9, 0xd9) },
+ { "Grey86", RGB_(0xdb, 0xdb, 0xdb) },
+ { "Grey87", RGB_(0xde, 0xde, 0xde) },
+ { "Grey88", RGB_(0xe0, 0xe0, 0xe0) },
+ { "Grey89", RGB_(0xe3, 0xe3, 0xe3) },
+ { "Grey9", RGB_(0x17, 0x17, 0x17) },
+ { "Grey90", RGB_(0xe5, 0xe5, 0xe5) },
+ { "Grey91", RGB_(0xe8, 0xe8, 0xe8) },
+ { "Grey92", RGB_(0xeb, 0xeb, 0xeb) },
+ { "Grey93", RGB_(0xed, 0xed, 0xed) },
+ { "Grey94", RGB_(0xf0, 0xf0, 0xf0) },
+ { "Grey95", RGB_(0xf2, 0xf2, 0xf2) },
+ { "Grey96", RGB_(0xf5, 0xf5, 0xf5) },
+ { "Grey97", RGB_(0xf7, 0xf7, 0xf7) },
+ { "Grey98", RGB_(0xfa, 0xfa, 0xfa) },
+ { "Grey99", RGB_(0xfc, 0xfc, 0xfc) },
+ { "Honeydew", RGB_(0xf0, 0xff, 0xf0) },
+ { "Honeydew1", RGB_(0xf0, 0xff, 0xf0) },
+ { "Honeydew2", RGB_(0xe0, 0xee, 0xe0) },
+ { "Honeydew3", RGB_(0xc1, 0xcd, 0xc1) },
+ { "Honeydew4", RGB_(0x83, 0x8b, 0x83) },
+ { "HotPink", RGB_(0xff, 0x69, 0xb4) },
+ { "HotPink1", RGB_(0xff, 0x6e, 0xb4) },
+ { "HotPink2", RGB_(0xee, 0x6a, 0xa7) },
+ { "HotPink3", RGB_(0xcd, 0x60, 0x90) },
+ { "HotPink4", RGB_(0x8b, 0x3a, 0x62) },
+ { "IndianRed", RGB_(0xcd, 0x5c, 0x5c) },
+ { "IndianRed1", RGB_(0xff, 0x6a, 0x6a) },
+ { "IndianRed2", RGB_(0xee, 0x63, 0x63) },
+ { "IndianRed3", RGB_(0xcd, 0x55, 0x55) },
+ { "IndianRed4", RGB_(0x8b, 0x3a, 0x3a) },
+ { "Indigo", RGB_(0x4b, 0x00, 0x82) },
+ { "Ivory", RGB_(0xff, 0xff, 0xf0) },
+ { "Ivory1", RGB_(0xff, 0xff, 0xf0) },
+ { "Ivory2", RGB_(0xee, 0xee, 0xe0) },
+ { "Ivory3", RGB_(0xcd, 0xcd, 0xc1) },
+ { "Ivory4", RGB_(0x8b, 0x8b, 0x83) },
+ { "Khaki", RGB_(0xf0, 0xe6, 0x8c) },
+ { "Khaki1", RGB_(0xff, 0xf6, 0x8f) },
+ { "Khaki2", RGB_(0xee, 0xe6, 0x85) },
+ { "Khaki3", RGB_(0xcd, 0xc6, 0x73) },
+ { "Khaki4", RGB_(0x8b, 0x86, 0x4e) },
+ { "Lavender", RGB_(0xe6, 0xe6, 0xfa) },
+ { "LavenderBlush", RGB_(0xff, 0xf0, 0xf5) },
+ { "LavenderBlush1", RGB_(0xff, 0xf0, 0xf5) },
+ { "LavenderBlush2", RGB_(0xee, 0xe0, 0xe5) },
+ { "LavenderBlush3", RGB_(0xcd, 0xc1, 0xc5) },
+ { "LavenderBlush4", RGB_(0x8b, 0x83, 0x86) },
+ { "LawnGreen", RGB_(0x7c, 0xfc, 0x00) },
+ { "LemonChiffon", RGB_(0xff, 0xfa, 0xcd) },
+ { "LemonChiffon1", RGB_(0xff, 0xfa, 0xcd) },
+ { "LemonChiffon2", RGB_(0xee, 0xe9, 0xbf) },
+ { "LemonChiffon3", RGB_(0xcd, 0xc9, 0xa5) },
+ { "LemonChiffon4", RGB_(0x8b, 0x89, 0x70) },
+ { "LightBlue", RGB_(0xad, 0xd8, 0xe6) },
+ { "LightBlue1", RGB_(0xbf, 0xef, 0xff) },
+ { "LightBlue2", RGB_(0xb2, 0xdf, 0xee) },
+ { "LightBlue3", RGB_(0x9a, 0xc0, 0xcd) },
+ { "LightBlue4", RGB_(0x68, 0x83, 0x8b) },
+ { "LightCoral", RGB_(0xf0, 0x80, 0x80) },
+ { "LightCyan", RGB_(0xe0, 0xff, 0xff) },
+ { "LightCyan1", RGB_(0xe0, 0xff, 0xff) },
+ { "LightCyan2", RGB_(0xd1, 0xee, 0xee) },
+ { "LightCyan3", RGB_(0xb4, 0xcd, 0xcd) },
+ { "LightCyan4", RGB_(0x7a, 0x8b, 0x8b) },
+ { "LightGoldenrod", RGB_(0xee, 0xdd, 0x82) },
+ { "LightGoldenrod1", RGB_(0xff, 0xec, 0x8b) },
+ { "LightGoldenrod2", RGB_(0xee, 0xdc, 0x82) },
+ { "LightGoldenrod3", RGB_(0xcd, 0xbe, 0x70) },
+ { "LightGoldenrod4", RGB_(0x8b, 0x81, 0x4c) },
+ { "LightGoldenRodYellow", RGB_(0xfa, 0xfa, 0xd2) },
+ { "LightGray", RGB_(0xd3, 0xd3, 0xd3) },
+ { "LightGreen", RGB_(0x90, 0xee, 0x90) },
+ { "LightGrey", RGB_(0xd3, 0xd3, 0xd3) },
+ { "LightMagenta", RGB_(0xff, 0xbb, 0xff) },
+ { "LightPink", RGB_(0xff, 0xb6, 0xc1) },
+ { "LightPink1", RGB_(0xff, 0xae, 0xb9) },
+ { "LightPink2", RGB_(0xee, 0xa2, 0xad) },
+ { "LightPink3", RGB_(0xcd, 0x8c, 0x95) },
+ { "LightPink4", RGB_(0x8b, 0x5f, 0x65) },
+ { "LightRed", RGB_(0xff, 0xbb, 0xbb) },
+ { "LightSalmon", RGB_(0xff, 0xa0, 0x7a) },
+ { "LightSalmon1", RGB_(0xff, 0xa0, 0x7a) },
+ { "LightSalmon2", RGB_(0xee, 0x95, 0x72) },
+ { "LightSalmon3", RGB_(0xcd, 0x81, 0x62) },
+ { "LightSalmon4", RGB_(0x8b, 0x57, 0x42) },
+ { "LightSeaGreen", RGB_(0x20, 0xb2, 0xaa) },
+ { "LightSkyBlue", RGB_(0x87, 0xce, 0xfa) },
+ { "LightSkyBlue1", RGB_(0xb0, 0xe2, 0xff) },
+ { "LightSkyBlue2", RGB_(0xa4, 0xd3, 0xee) },
+ { "LightSkyBlue3", RGB_(0x8d, 0xb6, 0xcd) },
+ { "LightSkyBlue4", RGB_(0x60, 0x7b, 0x8b) },
+ { "LightSlateBlue", RGB_(0x84, 0x70, 0xff) },
+ { "LightSlateGray", RGB_(0x77, 0x88, 0x99) },
+ { "LightSlateGrey", RGB_(0x77, 0x88, 0x99) },
+ { "LightSteelBlue", RGB_(0xb0, 0xc4, 0xde) },
+ { "LightSteelBlue1", RGB_(0xca, 0xe1, 0xff) },
+ { "LightSteelBlue2", RGB_(0xbc, 0xd2, 0xee) },
+ { "LightSteelBlue3", RGB_(0xa2, 0xb5, 0xcd) },
+ { "LightSteelBlue4", RGB_(0x6e, 0x7b, 0x8b) },
+ { "LightYellow", RGB_(0xff, 0xff, 0xe0) },
+ { "LightYellow1", RGB_(0xff, 0xff, 0xe0) },
+ { "LightYellow2", RGB_(0xee, 0xee, 0xd1) },
+ { "LightYellow3", RGB_(0xcd, 0xcd, 0xb4) },
+ { "LightYellow4", RGB_(0x8b, 0x8b, 0x7a) },
+ { "Lime", RGB_(0x00, 0xff, 0x00) },
+ { "LimeGreen", RGB_(0x32, 0xcd, 0x32) },
+ { "Linen", RGB_(0xfa, 0xf0, 0xe6) },
+ { "Magenta", RGB_(0xff, 0x00, 0xff) },
+ { "Magenta1", RGB_(0xff, 0x0, 0xff) },
+ { "Magenta2", RGB_(0xee, 0x0, 0xee) },
+ { "Magenta3", RGB_(0xcd, 0x0, 0xcd) },
+ { "Magenta4", RGB_(0x8b, 0x0, 0x8b) },
+ { "Maroon", RGB_(0x80, 0x00, 0x00) },
+ { "Maroon1", RGB_(0xff, 0x34, 0xb3) },
+ { "Maroon2", RGB_(0xee, 0x30, 0xa7) },
+ { "Maroon3", RGB_(0xcd, 0x29, 0x90) },
+ { "Maroon4", RGB_(0x8b, 0x1c, 0x62) },
+ { "MediumAquamarine", RGB_(0x66, 0xcd, 0xaa) },
+ { "MediumBlue", RGB_(0x00, 0x00, 0xcd) },
+ { "MediumOrchid", RGB_(0xba, 0x55, 0xd3) },
+ { "MediumOrchid1", RGB_(0xe0, 0x66, 0xff) },
+ { "MediumOrchid2", RGB_(0xd1, 0x5f, 0xee) },
+ { "MediumOrchid3", RGB_(0xb4, 0x52, 0xcd) },
+ { "MediumOrchid4", RGB_(0x7a, 0x37, 0x8b) },
+ { "MediumPurple", RGB_(0x93, 0x70, 0xdb) },
+ { "MediumPurple1", RGB_(0xab, 0x82, 0xff) },
+ { "MediumPurple2", RGB_(0x9f, 0x79, 0xee) },
+ { "MediumPurple3", RGB_(0x89, 0x68, 0xcd) },
+ { "MediumPurple4", RGB_(0x5d, 0x47, 0x8b) },
+ { "MediumSeaGreen", RGB_(0x3c, 0xb3, 0x71) },
+ { "MediumSlateBlue", RGB_(0x7b, 0x68, 0xee) },
+ { "MediumSpringGreen", RGB_(0x00, 0xfa, 0x9a) },
+ { "MediumTurquoise", RGB_(0x48, 0xd1, 0xcc) },
+ { "MediumVioletRed", RGB_(0xc7, 0x15, 0x85) },
+ { "MidnightBlue", RGB_(0x19, 0x19, 0x70) },
+ { "MintCream", RGB_(0xf5, 0xff, 0xfa) },
+ { "MistyRose", RGB_(0xff, 0xe4, 0xe1) },
+ { "MistyRose1", RGB_(0xff, 0xe4, 0xe1) },
+ { "MistyRose2", RGB_(0xee, 0xd5, 0xd2) },
+ { "MistyRose3", RGB_(0xcd, 0xb7, 0xb5) },
+ { "MistyRose4", RGB_(0x8b, 0x7d, 0x7b) },
+ { "Moccasin", RGB_(0xff, 0xe4, 0xb5) },
+ { "NavajoWhite", RGB_(0xff, 0xde, 0xad) },
+ { "NavajoWhite1", RGB_(0xff, 0xde, 0xad) },
+ { "NavajoWhite2", RGB_(0xee, 0xcf, 0xa1) },
+ { "NavajoWhite3", RGB_(0xcd, 0xb3, 0x8b) },
+ { "NavajoWhite4", RGB_(0x8b, 0x79, 0x5e) },
+ { "Navy", RGB_(0x00, 0x00, 0x80) },
+ { "NavyBlue", RGB_(0x0, 0x0, 0x80) },
+ { "OldLace", RGB_(0xfd, 0xf5, 0xe6) },
+ { "Olive", RGB_(0x80, 0x80, 0x00) },
+ { "OliveDrab", RGB_(0x6b, 0x8e, 0x23) },
+ { "OliveDrab1", RGB_(0xc0, 0xff, 0x3e) },
+ { "OliveDrab2", RGB_(0xb3, 0xee, 0x3a) },
+ { "OliveDrab3", RGB_(0x9a, 0xcd, 0x32) },
+ { "OliveDrab4", RGB_(0x69, 0x8b, 0x22) },
+ { "Orange", RGB_(0xff, 0xa5, 0x00) },
+ { "Orange1", RGB_(0xff, 0xa5, 0x0) },
+ { "Orange2", RGB_(0xee, 0x9a, 0x0) },
+ { "Orange3", RGB_(0xcd, 0x85, 0x0) },
+ { "Orange4", RGB_(0x8b, 0x5a, 0x0) },
+ { "OrangeRed", RGB_(0xff, 0x45, 0x00) },
+ { "OrangeRed1", RGB_(0xff, 0x45, 0x0) },
+ { "OrangeRed2", RGB_(0xee, 0x40, 0x0) },
+ { "OrangeRed3", RGB_(0xcd, 0x37, 0x0) },
+ { "OrangeRed4", RGB_(0x8b, 0x25, 0x0) },
+ { "Orchid", RGB_(0xda, 0x70, 0xd6) },
+ { "Orchid1", RGB_(0xff, 0x83, 0xfa) },
+ { "Orchid2", RGB_(0xee, 0x7a, 0xe9) },
+ { "Orchid3", RGB_(0xcd, 0x69, 0xc9) },
+ { "Orchid4", RGB_(0x8b, 0x47, 0x89) },
+ { "PaleGoldenRod", RGB_(0xee, 0xe8, 0xaa) },
+ { "PaleGreen", RGB_(0x98, 0xfb, 0x98) },
+ { "PaleGreen1", RGB_(0x9a, 0xff, 0x9a) },
+ { "PaleGreen2", RGB_(0x90, 0xee, 0x90) },
+ { "PaleGreen3", RGB_(0x7c, 0xcd, 0x7c) },
+ { "PaleGreen4", RGB_(0x54, 0x8b, 0x54) },
+ { "PaleTurquoise", RGB_(0xaf, 0xee, 0xee) },
+ { "PaleTurquoise1", RGB_(0xbb, 0xff, 0xff) },
+ { "PaleTurquoise2", RGB_(0xae, 0xee, 0xee) },
+ { "PaleTurquoise3", RGB_(0x96, 0xcd, 0xcd) },
+ { "PaleTurquoise4", RGB_(0x66, 0x8b, 0x8b) },
+ { "PaleVioletRed", RGB_(0xdb, 0x70, 0x93) },
+ { "PaleVioletRed1", RGB_(0xff, 0x82, 0xab) },
+ { "PaleVioletRed2", RGB_(0xee, 0x79, 0x9f) },
+ { "PaleVioletRed3", RGB_(0xcd, 0x68, 0x89) },
+ { "PaleVioletRed4", RGB_(0x8b, 0x47, 0x5d) },
+ { "PapayaWhip", RGB_(0xff, 0xef, 0xd5) },
+ { "PeachPuff", RGB_(0xff, 0xda, 0xb9) },
+ { "PeachPuff1", RGB_(0xff, 0xda, 0xb9) },
+ { "PeachPuff2", RGB_(0xee, 0xcb, 0xad) },
+ { "PeachPuff3", RGB_(0xcd, 0xaf, 0x95) },
+ { "PeachPuff4", RGB_(0x8b, 0x77, 0x65) },
+ { "Peru", RGB_(0xcd, 0x85, 0x3f) },
+ { "Pink", RGB_(0xff, 0xc0, 0xcb) },
+ { "Pink1", RGB_(0xff, 0xb5, 0xc5) },
+ { "Pink2", RGB_(0xee, 0xa9, 0xb8) },
+ { "Pink3", RGB_(0xcd, 0x91, 0x9e) },
+ { "Pink4", RGB_(0x8b, 0x63, 0x6c) },
+ { "Plum", RGB_(0xdd, 0xa0, 0xdd) },
+ { "Plum1", RGB_(0xff, 0xbb, 0xff) },
+ { "Plum2", RGB_(0xee, 0xae, 0xee) },
+ { "Plum3", RGB_(0xcd, 0x96, 0xcd) },
+ { "Plum4", RGB_(0x8b, 0x66, 0x8b) },
+ { "PowderBlue", RGB_(0xb0, 0xe0, 0xe6) },
+ { "Purple", RGB_(0x80, 0x00, 0x80) },
+ { "Purple1", RGB_(0x9b, 0x30, 0xff) },
+ { "Purple2", RGB_(0x91, 0x2c, 0xee) },
+ { "Purple3", RGB_(0x7d, 0x26, 0xcd) },
+ { "Purple4", RGB_(0x55, 0x1a, 0x8b) },
+ { "RebeccaPurple", RGB_(0x66, 0x33, 0x99) },
+ { "Red", RGB_(0xff, 0x00, 0x00) },
+ { "Red1", RGB_(0xff, 0x0, 0x0) },
+ { "Red2", RGB_(0xee, 0x0, 0x0) },
+ { "Red3", RGB_(0xcd, 0x0, 0x0) },
+ { "Red4", RGB_(0x8b, 0x0, 0x0) },
+ { "RosyBrown", RGB_(0xbc, 0x8f, 0x8f) },
+ { "RosyBrown1", RGB_(0xff, 0xc1, 0xc1) },
+ { "RosyBrown2", RGB_(0xee, 0xb4, 0xb4) },
+ { "RosyBrown3", RGB_(0xcd, 0x9b, 0x9b) },
+ { "RosyBrown4", RGB_(0x8b, 0x69, 0x69) },
+ { "RoyalBlue", RGB_(0x41, 0x69, 0xe1) },
+ { "RoyalBlue1", RGB_(0x48, 0x76, 0xff) },
+ { "RoyalBlue2", RGB_(0x43, 0x6e, 0xee) },
+ { "RoyalBlue3", RGB_(0x3a, 0x5f, 0xcd) },
+ { "RoyalBlue4", RGB_(0x27, 0x40, 0x8b) },
+ { "SaddleBrown", RGB_(0x8b, 0x45, 0x13) },
+ { "Salmon", RGB_(0xfa, 0x80, 0x72) },
+ { "Salmon1", RGB_(0xff, 0x8c, 0x69) },
+ { "Salmon2", RGB_(0xee, 0x82, 0x62) },
+ { "Salmon3", RGB_(0xcd, 0x70, 0x54) },
+ { "Salmon4", RGB_(0x8b, 0x4c, 0x39) },
+ { "SandyBrown", RGB_(0xf4, 0xa4, 0x60) },
+ { "SeaGreen", RGB_(0x2e, 0x8b, 0x57) },
+ { "SeaGreen1", RGB_(0x54, 0xff, 0x9f) },
+ { "SeaGreen2", RGB_(0x4e, 0xee, 0x94) },
+ { "SeaGreen3", RGB_(0x43, 0xcd, 0x80) },
+ { "SeaGreen4", RGB_(0x2e, 0x8b, 0x57) },
+ { "SeaShell", RGB_(0xff, 0xf5, 0xee) },
+ { "Seashell1", RGB_(0xff, 0xf5, 0xee) },
+ { "Seashell2", RGB_(0xee, 0xe5, 0xde) },
+ { "Seashell3", RGB_(0xcd, 0xc5, 0xbf) },
+ { "Seashell4", RGB_(0x8b, 0x86, 0x82) },
+ { "Sienna", RGB_(0xa0, 0x52, 0x2d) },
+ { "Sienna1", RGB_(0xff, 0x82, 0x47) },
+ { "Sienna2", RGB_(0xee, 0x79, 0x42) },
+ { "Sienna3", RGB_(0xcd, 0x68, 0x39) },
+ { "Sienna4", RGB_(0x8b, 0x47, 0x26) },
+ { "Silver", RGB_(0xc0, 0xc0, 0xc0) },
+ { "SkyBlue", RGB_(0x87, 0xce, 0xeb) },
+ { "SkyBlue1", RGB_(0x87, 0xce, 0xff) },
+ { "SkyBlue2", RGB_(0x7e, 0xc0, 0xee) },
+ { "SkyBlue3", RGB_(0x6c, 0xa6, 0xcd) },
+ { "SkyBlue4", RGB_(0x4a, 0x70, 0x8b) },
+ { "SlateBlue", RGB_(0x6a, 0x5a, 0xcd) },
+ { "SlateBlue1", RGB_(0x83, 0x6f, 0xff) },
+ { "SlateBlue2", RGB_(0x7a, 0x67, 0xee) },
+ { "SlateBlue3", RGB_(0x69, 0x59, 0xcd) },
+ { "SlateBlue4", RGB_(0x47, 0x3c, 0x8b) },
+ { "SlateGray", RGB_(0x70, 0x80, 0x90) },
+ { "SlateGray1", RGB_(0xc6, 0xe2, 0xff) },
+ { "SlateGray2", RGB_(0xb9, 0xd3, 0xee) },
+ { "SlateGray3", RGB_(0x9f, 0xb6, 0xcd) },
+ { "SlateGray4", RGB_(0x6c, 0x7b, 0x8b) },
+ { "SlateGrey", RGB_(0x70, 0x80, 0x90) },
+ { "Snow", RGB_(0xff, 0xfa, 0xfa) },
+ { "Snow1", RGB_(0xff, 0xfa, 0xfa) },
+ { "Snow2", RGB_(0xee, 0xe9, 0xe9) },
+ { "Snow3", RGB_(0xcd, 0xc9, 0xc9) },
+ { "Snow4", RGB_(0x8b, 0x89, 0x89) },
+ { "SpringGreen", RGB_(0x00, 0xff, 0x7f) },
+ { "SpringGreen1", RGB_(0x0, 0xff, 0x7f) },
+ { "SpringGreen2", RGB_(0x0, 0xee, 0x76) },
+ { "SpringGreen3", RGB_(0x0, 0xcd, 0x66) },
+ { "SpringGreen4", RGB_(0x0, 0x8b, 0x45) },
+ { "SteelBlue", RGB_(0x46, 0x82, 0xb4) },
+ { "SteelBlue1", RGB_(0x63, 0xb8, 0xff) },
+ { "SteelBlue2", RGB_(0x5c, 0xac, 0xee) },
+ { "SteelBlue3", RGB_(0x4f, 0x94, 0xcd) },
+ { "SteelBlue4", RGB_(0x36, 0x64, 0x8b) },
+ { "Tan", RGB_(0xd2, 0xb4, 0x8c) },
+ { "Tan1", RGB_(0xff, 0xa5, 0x4f) },
+ { "Tan2", RGB_(0xee, 0x9a, 0x49) },
+ { "Tan3", RGB_(0xcd, 0x85, 0x3f) },
+ { "Tan4", RGB_(0x8b, 0x5a, 0x2b) },
+ { "Teal", RGB_(0x00, 0x80, 0x80) },
+ { "Thistle", RGB_(0xd8, 0xbf, 0xd8) },
+ { "Thistle1", RGB_(0xff, 0xe1, 0xff) },
+ { "Thistle2", RGB_(0xee, 0xd2, 0xee) },
+ { "Thistle3", RGB_(0xcd, 0xb5, 0xcd) },
+ { "Thistle4", RGB_(0x8b, 0x7b, 0x8b) },
+ { "Tomato", RGB_(0xff, 0x63, 0x47) },
+ { "Tomato1", RGB_(0xff, 0x63, 0x47) },
+ { "Tomato2", RGB_(0xee, 0x5c, 0x42) },
+ { "Tomato3", RGB_(0xcd, 0x4f, 0x39) },
+ { "Tomato4", RGB_(0x8b, 0x36, 0x26) },
+ { "Turquoise", RGB_(0x40, 0xe0, 0xd0) },
+ { "Turquoise1", RGB_(0x0, 0xf5, 0xff) },
+ { "Turquoise2", RGB_(0x0, 0xe5, 0xee) },
+ { "Turquoise3", RGB_(0x0, 0xc5, 0xcd) },
+ { "Turquoise4", RGB_(0x0, 0x86, 0x8b) },
+ { "Violet", RGB_(0xee, 0x82, 0xee) },
+ { "VioletRed", RGB_(0xd0, 0x20, 0x90) },
+ { "VioletRed1", RGB_(0xff, 0x3e, 0x96) },
+ { "VioletRed2", RGB_(0xee, 0x3a, 0x8c) },
+ { "VioletRed3", RGB_(0xcd, 0x32, 0x78) },
+ { "VioletRed4", RGB_(0x8b, 0x22, 0x52) },
+ { "WebGray", RGB_(0x80, 0x80, 0x80) },
+ { "WebGreen", RGB_(0x0, 0x80, 0x0) },
+ { "WebGrey", RGB_(0x80, 0x80, 0x80) },
+ { "WebMaroon", RGB_(0x80, 0x0, 0x0) },
+ { "WebPurple", RGB_(0x80, 0x0, 0x80) },
+ { "Wheat", RGB_(0xf5, 0xde, 0xb3) },
+ { "Wheat1", RGB_(0xff, 0xe7, 0xba) },
+ { "Wheat2", RGB_(0xee, 0xd8, 0xae) },
+ { "Wheat3", RGB_(0xcd, 0xba, 0x96) },
+ { "Wheat4", RGB_(0x8b, 0x7e, 0x66) },
+ { "White", RGB_(0xff, 0xff, 0xff) },
+ { "WhiteSmoke", RGB_(0xf5, 0xf5, 0xf5) },
+ { "X11Gray", RGB_(0xbe, 0xbe, 0xbe) },
+ { "X11Green", RGB_(0x0, 0xff, 0x0) },
+ { "X11Grey", RGB_(0xbe, 0xbe, 0xbe) },
+ { "X11Maroon", RGB_(0xb0, 0x30, 0x60) },
+ { "X11Purple", RGB_(0xa0, 0x20, 0xf0) },
+ { "Yellow", RGB_(0xff, 0xff, 0x00) },
+ { "Yellow1", RGB_(0xff, 0xff, 0x0) },
+ { "Yellow2", RGB_(0xee, 0xee, 0x0) },
+ { "Yellow3", RGB_(0xcd, 0xcd, 0x0) },
+ { "Yellow4", RGB_(0x8b, 0x8b, 0x0) },
+ { "YellowGreen", RGB_(0x9a, 0xcd, 0x32) },
+ { NULL, 0 },
+};
+
+/// Translate to RgbValue if \p name is an hex value (e.g. #XXXXXX),
+/// else look into color_name_table to translate a color name to its
+/// hex value
+///
+/// @param[in] name string value to convert to RGB
+/// @param[out] idx index in color table or special value
+/// return the hex value or -1 if could not find a correct value
+RgbValue name_to_color(const char *name, int *idx)
+{
+ if (name[0] == '#' && isxdigit(name[1]) && isxdigit(name[2])
+ && isxdigit(name[3]) && isxdigit(name[4]) && isxdigit(name[5])
+ && isxdigit(name[6]) && name[7] == NUL) {
+ // rgb hex string
+ *idx = kColorIdxHex;
+ return (RgbValue)strtol((char *)(name + 1), NULL, 16);
+ } else if (!STRICMP(name, "bg") || !STRICMP(name, "background")) {
+ *idx = kColorIdxBg;
+ return normal_bg;
+ } else if (!STRICMP(name, "fg") || !STRICMP(name, "foreground")) {
+ *idx = kColorIdxFg;
+ return normal_fg;
+ }
+
+ int lo = 0;
+ int hi = ARRAY_SIZE(color_name_table) - 1; // don't count NULL element
+ while (lo < hi) {
+ int m = (lo + hi) / 2;
+ int cmp = STRICMP(name, color_name_table[m].name);
+ if (cmp < 0) {
+ hi = m;
+ } else if (cmp > 0) {
+ lo = m + 1;
+ } else { // found match
+ *idx = m;
+ return color_name_table[m].color;
+ break;
+ }
+ }
+
+ *idx = kColorIdxNone;
+ return -1;
+}
+
+const char *coloridx_to_name(int idx, int val, char hexbuf[8])
+{
+ if (idx >= 0) {
+ return color_name_table[idx].name;
+ }
+ switch (idx) {
+ case kColorIdxNone:
+ return NULL;
+ case kColorIdxFg:
+ return "fg";
+ case kColorIdxBg:
+ return "bg";
+ case kColorIdxHex:
+ snprintf(hexbuf, 7 + 1, "#%06x", val);
+ return hexbuf;
+ default:
+ abort();
+ }
+}
+
+int name_to_ctermcolor(const char *name)
+{
+ int i;
+ int off = TOUPPER_ASC(*name);
+ for (i = ARRAY_SIZE(color_names); --i >= 0;) {
+ if (off == color_names[i][0]
+ && STRICMP(name + 1, color_names[i] + 1) == 0) {
+ break;
+ }
+ }
+ if (i < 0) {
+ return -1;
+ }
+ TriState bold = kNone;
+ return lookup_color(i, false, &bold);
+}
diff --git a/src/nvim/highlight_group.h b/src/nvim/highlight_group.h
new file mode 100644
index 0000000000..1474588889
--- /dev/null
+++ b/src/nvim/highlight_group.h
@@ -0,0 +1,19 @@
+#ifndef NVIM_HIGHLIGHT_GROUP_H
+#define NVIM_HIGHLIGHT_GROUP_H
+
+#include "nvim/eval.h"
+#include "nvim/types.h"
+
+#define MAX_HL_ID 20000 // maximum value for a highlight ID.
+
+typedef struct {
+ char *name;
+ RgbValue color;
+} color_name_table_T;
+extern color_name_table_T color_name_table[];
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "highlight_group.h.generated.h"
+#endif
+
+#endif // NVIM_HIGHLIGHT_GROUP_H
diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c
index daef8db267..8d08c2fc19 100644
--- a/src/nvim/if_cscope.c
+++ b/src/nvim/if_cscope.c
@@ -32,6 +32,7 @@
#include "nvim/quickfix.h"
#include "nvim/strings.h"
#include "nvim/tag.h"
+#include "nvim/window.h"
#if defined(UNIX)
# include <sys/wait.h>
#endif
@@ -67,7 +68,6 @@ static void cs_usage_msg(csid_e x)
(void)semsg(_("E560: Usage: cs[cope] %s"), cs_cmds[(int)x].usage);
}
-
static enum {
EXP_CSCOPE_SUBCMD, // expand ":cscope" sub-commands
EXP_SCSCOPE_SUBCMD, // expand ":scscope" sub-commands
@@ -79,7 +79,7 @@ static enum {
* Function given to ExpandGeneric() to obtain the cscope command
* expansion.
*/
-char_u *get_cscope_name(expand_T *xp, int idx)
+char *get_cscope_name(expand_T *xp, int idx)
{
int current_idx;
@@ -87,7 +87,7 @@ char_u *get_cscope_name(expand_T *xp, int idx)
case EXP_CSCOPE_SUBCMD:
// Complete with sub-commands of ":cscope":
// add, find, help, kill, reset, show
- return (char_u *)cs_cmds[idx].name;
+ return cs_cmds[idx].name;
case EXP_SCSCOPE_SUBCMD: {
// Complete with sub-commands of ":scscope": same sub-commands as
// ":cscope" but skip commands which don't support split windows
@@ -99,7 +99,7 @@ char_u *get_cscope_name(expand_T *xp, int idx)
}
}
}
- return (char_u *)cs_cmds[i].name;
+ return cs_cmds[i].name;
}
case EXP_CSCOPE_FIND: {
const char *query_type[] =
@@ -111,7 +111,7 @@ char_u *get_cscope_name(expand_T *xp, int idx)
// {query_type} can be letters (c, d, ... a) or numbers (0, 1,
// ..., 9) but only complete with letters, since numbers are
// redundant.
- return (char_u *)query_type[idx];
+ return (char *)query_type[idx];
}
case EXP_CSCOPE_KILL: {
static char connection[5];
@@ -127,10 +127,10 @@ char_u *get_cscope_name(expand_T *xp, int idx)
}
if (current_idx++ == idx) {
vim_snprintf(connection, sizeof(connection), "%zu", i);
- return (char_u *)connection;
+ return connection;
}
}
- return (current_idx == idx && idx > 0) ? (char_u *)"-1" : NULL;
+ return (current_idx == idx && idx > 0) ? "-1" : NULL;
}
default:
return NULL;
@@ -144,7 +144,7 @@ void set_context_in_cscope_cmd(expand_T *xp, const char *arg, cmdidx_T cmdidx)
{
// Default: expand subcommands.
xp->xp_context = EXPAND_CSCOPE;
- xp->xp_pattern = (char_u *)arg;
+ xp->xp_pattern = (char *)arg;
expand_what = ((cmdidx == CMD_scscope)
? EXP_SCSCOPE_SUBCMD : EXP_CSCOPE_SUBCMD);
@@ -152,8 +152,8 @@ void set_context_in_cscope_cmd(expand_T *xp, const char *arg, cmdidx_T cmdidx)
if (*arg != NUL) {
const char *p = (const char *)skiptowhite((const char_u *)arg);
if (*p != NUL) { // Past first word.
- xp->xp_pattern = skipwhite((const char_u *)p);
- if (*skiptowhite(xp->xp_pattern) != NUL) {
+ xp->xp_pattern = skipwhite(p);
+ if (*skiptowhite((char_u *)xp->xp_pattern) != NUL) {
xp->xp_context = EXPAND_NOTHING;
} else if (STRNICMP(arg, "add", p - arg) == 0) {
xp->xp_context = EXPAND_FILES;
@@ -168,7 +168,6 @@ void set_context_in_cscope_cmd(expand_T *xp, const char *arg, cmdidx_T cmdidx)
}
}
-
/// Find the command, print help if invalid, and then call the corresponding
/// command function.
///
@@ -188,8 +187,8 @@ static void do_cscope_general(exarg_T *eap, int make_split)
return;
}
postponed_split = -1;
- postponed_split_flags = cmdmod.split;
- postponed_split_tab = cmdmod.tab;
+ postponed_split_flags = cmdmod.cmod_split;
+ postponed_split_tab = cmdmod.cmod_tab;
}
cmdp->func(eap);
@@ -223,8 +222,8 @@ void ex_cstag(exarg_T *eap)
switch (p_csto) {
case 0:
if (cs_check_for_connections()) {
- ret = cs_find_common("g", (char *)(eap->arg), eap->forceit, false,
- false, *eap->cmdlinep);
+ ret = cs_find_common("g", eap->arg, eap->forceit, false,
+ false, (char_u *)(*eap->cmdlinep));
if (ret == false) {
cs_free_tags();
if (msg_col) {
@@ -232,32 +231,32 @@ void ex_cstag(exarg_T *eap)
}
if (cs_check_for_tags()) {
- ret = do_tag(eap->arg, DT_JUMP, 0, eap->forceit, FALSE);
+ ret = do_tag((char_u *)eap->arg, DT_JUMP, 0, eap->forceit, false);
}
}
} else if (cs_check_for_tags()) {
- ret = do_tag(eap->arg, DT_JUMP, 0, eap->forceit, FALSE);
+ ret = do_tag((char_u *)eap->arg, DT_JUMP, 0, eap->forceit, false);
}
break;
case 1:
if (cs_check_for_tags()) {
- ret = do_tag(eap->arg, DT_JUMP, 0, eap->forceit, FALSE);
- if (ret == FALSE) {
+ ret = do_tag((char_u *)eap->arg, DT_JUMP, 0, eap->forceit, false);
+ if (ret == false) {
if (msg_col) {
msg_putchar('\n');
}
if (cs_check_for_connections()) {
- ret = cs_find_common("g", (char *)(eap->arg), eap->forceit,
- false, false, *eap->cmdlinep);
+ ret = cs_find_common("g", eap->arg, eap->forceit,
+ false, false, (char_u *)(*eap->cmdlinep));
if (ret == false) {
cs_free_tags();
}
}
}
} else if (cs_check_for_connections()) {
- ret = cs_find_common("g", (char *)(eap->arg), eap->forceit, false,
- false, *eap->cmdlinep);
+ ret = cs_find_common("g", eap->arg, eap->forceit, false,
+ false, (char_u *)(*eap->cmdlinep));
if (ret == false) {
cs_free_tags();
}
@@ -273,7 +272,6 @@ void ex_cstag(exarg_T *eap)
}
}
-
/// This simulates a vim_fgets(), but for cscope, returns the next line
/// from the cscope output. should only be called from find_tags()
///
@@ -291,7 +289,6 @@ bool cs_fgets(char_u *buf, int size)
return false;
}
-
/// Called only from do_tag(), when popping the tag stack.
void cs_free_tags(void)
{
@@ -379,7 +376,6 @@ bool cs_connection(int num, char_u *dbpath, char_u *ppath)
return false;
} // cs_connection
-
/*
* PRIVATE functions
****************************************************************************/
@@ -407,7 +403,6 @@ static void cs_stat_emsg(char *fname)
(void)semsg(_("E563: stat(%s) error: %d"), fname, err);
}
-
/// The common routine to add a new cscope connection. Called by
/// cs_add() and cs_reset(). I really don't like to do this, but this
/// routine uses a number of goto statements.
@@ -428,12 +423,12 @@ static int cs_add_common(char *arg1, char *arg2, char *flags)
expand_env((char_u *)arg1, (char_u *)fname, MAXPATHL);
size_t len = STRLEN(fname);
fbuf = (char_u *)fname;
- (void)modify_fname((char_u *)":p", false, &usedlen,
- (char_u **)&fname, &fbuf, &len);
+ (void)modify_fname(":p", false, &usedlen,
+ &fname, (char **)&fbuf, &len);
if (fname == NULL) {
goto add_err;
}
- fname = (char *)vim_strnsave((char_u *)fname, len);
+ fname = xstrnsave(fname, len);
xfree(fbuf);
FileInfo file_info;
bool file_info_ok = os_fileinfo(fname, &file_info);
@@ -459,9 +454,9 @@ staterr:
if (S_ISDIR(file_info.stat.st_mode)) {
fname2 = (char *)xmalloc(strlen(CSCOPE_DBFILE) + strlen(fname) + 2);
- while (fname[strlen(fname)-1] == '/'
+ while (fname[strlen(fname) - 1] == '/'
) {
- fname[strlen(fname)-1] = '\0';
+ fname[strlen(fname) - 1] = '\0';
if (fname[0] == '\0') {
break;
}
@@ -519,7 +514,6 @@ add_err:
return CSCOPE_FAILURE;
}
-
static bool cs_check_for_connections(void)
{
return cs_cnt_connections() > 0;
@@ -612,7 +606,6 @@ static int cs_cnt_matches(size_t idx)
return nlines;
}
-
/// Creates the actual cscope command query from what the user entered.
static char *cs_create_cmd(char *csoption, char *pattern)
{
@@ -679,7 +672,6 @@ static char *cs_create_cmd(char *csoption, char *pattern)
return cmd;
}
-
/// This piece of code was taken/adapted from nvi. do we need to add
/// the BSD license notice?
static int cs_create_connection(size_t i)
@@ -878,7 +870,6 @@ err_closing:
return CSCOPE_SUCCESS;
}
-
/// Query cscope using command line interface. Parse the output and use tselect
/// to allow choices. Like Nvi, creates a pipe to send to/from query/cscope.
///
@@ -898,7 +889,7 @@ static int cs_find(exarg_T *eap)
}
pat = opt + strlen(opt) + 1;
- if (pat >= (char *)eap->arg + eap_arg_len) {
+ if (pat >= eap->arg + eap_arg_len) {
cs_usage_msg(Find);
return false;
}
@@ -914,13 +905,12 @@ static int cs_find(exarg_T *eap)
}
return cs_find_common(opt, pat, eap->forceit, true,
- eap->cmdidx == CMD_lcscope, *eap->cmdlinep);
+ eap->cmdidx == CMD_lcscope, (char_u *)(*eap->cmdlinep));
}
-
/// Common code for cscope find, shared by cs_find() and ex_cstag().
-static bool cs_find_common(char *opt, char *pat, int forceit, int verbose,
- bool use_ll, char_u *cmdline)
+static bool cs_find_common(char *opt, char *pat, int forceit, int verbose, bool use_ll,
+ char_u *cmdline)
{
char *cmd;
int *nummatches;
@@ -961,7 +951,7 @@ static bool cs_find_common(char *opt, char *pat, int forceit, int verbose,
cmdletter = opt[0];
}
- qfpos = (char *)vim_strchr(p_csqf, cmdletter);
+ qfpos = vim_strchr((char *)p_csqf, cmdletter);
if (qfpos != NULL) {
qfpos++;
// next symbol must be + or -
@@ -971,8 +961,7 @@ static bool cs_find_common(char *opt, char *pat, int forceit, int verbose,
}
if (*qfpos != '0'
- && apply_autocmds(EVENT_QUICKFIXCMDPRE, (char_u *)"cscope",
- curbuf->b_fname, true, curbuf)) {
+ && apply_autocmds(EVENT_QUICKFIXCMDPRE, "cscope", curbuf->b_fname, true, curbuf)) {
if (aborting()) {
return false;
}
@@ -1039,8 +1028,8 @@ static bool cs_find_common(char *opt, char *pat, int forceit, int verbose,
wp = curwin;
}
// '-' starts a new error list
- if (qf_init(wp, tmp, (char_u *)"%f%*\\t%l%*\\t%m",
- *qfpos == '-', cmdline, NULL) > 0) {
+ if (qf_init(wp, (char *)tmp, "%f%*\\t%l%*\\t%m",
+ *qfpos == '-', (char *)cmdline, NULL) > 0) {
if (postponed_split != 0) {
(void)win_split(postponed_split > 0 ? postponed_split : 0,
postponed_split_flags);
@@ -1048,8 +1037,7 @@ static bool cs_find_common(char *opt, char *pat, int forceit, int verbose,
postponed_split = 0;
}
- apply_autocmds(EVENT_QUICKFIXCMDPOST, (char_u *)"cscope",
- curbuf->b_fname, TRUE, curbuf);
+ apply_autocmds(EVENT_QUICKFIXCMDPOST, "cscope", curbuf->b_fname, true, curbuf);
if (use_ll) {
/*
* In the location list window, use the displayed location
@@ -1090,7 +1078,7 @@ static int cs_help(exarg_T *eap)
(void)msg_puts(_("cscope commands:\n"));
while (cmdp->name != NULL) {
char *help = _(cmdp->help);
- int space_cnt = 30 - vim_strsize((char_u *)help);
+ int space_cnt = 30 - vim_strsize(help);
// Use %*s rather than %30s to ensure proper alignment in utf-8
if (space_cnt < 0) {
@@ -1120,7 +1108,6 @@ static int cs_help(exarg_T *eap)
return CSCOPE_SUCCESS;
}
-
static void clear_csinfo(size_t i)
{
csinfo[i].fname = NULL;
@@ -1194,7 +1181,6 @@ static int cs_insert_filelist(char *fname, char *ppath, char *flags, FileInfo *f
return (int)i;
}
-
/// Find cscope command in command table.
static cscmd_T *cs_lookup_cmd(exarg_T *eap)
{
@@ -1209,7 +1195,7 @@ static cscmd_T *cs_lookup_cmd(exarg_T *eap)
// Store length of eap->arg before it gets modified by strtok().
eap_arg_len = (int)STRLEN(eap->arg);
- if ((stok = strtok((char *)(eap->arg), (const char *)" ")) == NULL) {
+ if ((stok = strtok(eap->arg, (const char *)" ")) == NULL) { // NOLINT(runtime/threadsafe_fn)
return NULL;
}
@@ -1222,7 +1208,6 @@ static cscmd_T *cs_lookup_cmd(exarg_T *eap)
return NULL;
}
-
/// Nuke em.
static int cs_kill(exarg_T *eap)
{
@@ -1282,7 +1267,6 @@ static int cs_kill(exarg_T *eap)
return CSCOPE_SUCCESS;
}
-
/// Actually kills a specific cscope connection.
///
/// @param i cscope table index
@@ -1297,7 +1281,6 @@ static void cs_kill_execute(size_t i, char *cname)
cs_release_csp(i, TRUE);
}
-
/// Convert the cscope output into a ctags style entry (as might be found
/// in a ctags tags file). there's one catch though: cscope doesn't tell you
/// the type of the tag you are looking for. for example, in Darren Hiebert's
@@ -1344,7 +1327,6 @@ static char *cs_make_vim_style_matches(char *fname, char *slno, char *search, ch
return buf;
}
-
/// This is kind of hokey, but i don't see an easy way round this.
///
/// Store: keep a ptr to the (malloc'd) memory of matches originally
@@ -1416,7 +1398,6 @@ static char *cs_manage_matches(char **matches, char **contexts, size_t totmatche
return p;
}
-
/// Parse cscope output.
static char *cs_parse_results(size_t cnumber, char *buf, int bufsize, char **context,
char **linenumber, char **search)
@@ -1443,8 +1424,7 @@ retry:
// If the line's too long for the buffer, discard it.
if ((p = strchr(buf, '\n')) == NULL) {
- while ((ch = getc(csinfo[cnumber].fr_fp)) != EOF && ch != '\n') {
- }
+ while ((ch = getc(csinfo[cnumber].fr_fp)) != EOF && ch != '\n') {}
return NULL;
}
*p = '\0';
@@ -1583,7 +1563,6 @@ static void cs_fill_results(char *tagstr, size_t totmatches, int *nummatches_a,
xfree(buf);
}
-
// get the requested path components
static char *cs_pathcomponents(char *path)
{
@@ -1593,9 +1572,7 @@ static char *cs_pathcomponents(char *path)
char *s = path + strlen(path) - 1;
for (int i = 0; i < p_cspc; i++) {
- while (s > path && *--s != '/') {
- continue;
- }
+ while (s > path && *--s != '/') {}
}
if ((s > path && *s == '/')) {
s++;
@@ -1813,7 +1790,6 @@ static int cs_read_prompt(size_t i)
static void sig_handler(int s)
{
// do nothing
- return;
}
#endif
@@ -1934,7 +1910,6 @@ static void cs_release_csp(size_t i, bool freefnpp)
clear_csinfo(i);
}
-
/// Calls cs_kill on all cscope connections then reinits.
static int cs_reset(exarg_T *eap)
{
@@ -1985,7 +1960,6 @@ static int cs_reset(exarg_T *eap)
return CSCOPE_SUCCESS;
}
-
/// Construct the full pathname to a file found in the cscope database.
/// (Prepends ppath, if there is one and if it's not already prepended,
/// otherwise just uses the name found.)
@@ -2011,8 +1985,8 @@ static char *cs_resolve_file(size_t i, char *name)
// path in path resolution.
csdir = xmalloc(MAXPATHL);
STRLCPY(csdir, csinfo[i].fname,
- path_tail((char_u *)csinfo[i].fname)
- - (char_u *)csinfo[i].fname + 1);
+ path_tail(csinfo[i].fname)
+ - csinfo[i].fname + 1);
len += STRLEN(csdir);
}
@@ -2036,7 +2010,6 @@ static char *cs_resolve_file(size_t i, char *name)
return fullname;
}
-
/// Show all cscope connections.
static int cs_show(exarg_T *eap)
{
@@ -2064,7 +2037,6 @@ static int cs_show(exarg_T *eap)
return CSCOPE_SUCCESS;
}
-
/// Only called when VIM exits to quit any cscope sessions.
void cs_end(void)
{
diff --git a/src/nvim/indent.c b/src/nvim/indent.c
index 8cc5bc2436..271498d41a 100644
--- a/src/nvim/indent.c
+++ b/src/nvim/indent.c
@@ -26,7 +26,6 @@
#include "nvim/strings.h"
#include "nvim/undo.h"
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "indent.c.generated.h"
#endif
@@ -40,7 +39,6 @@ int get_indent(void)
false);
}
-
// Count the size (in window cells) of the indent in line "lnum".
int get_indent_lnum(linenr_T lnum)
{
@@ -50,7 +48,6 @@ int get_indent_lnum(linenr_T lnum)
false);
}
-
// Count the size (in window cells) of the indent in line "lnum" of buffer
// "buf".
int get_indent_buf(buf_T *buf, linenr_T lnum)
@@ -61,7 +58,6 @@ int get_indent_buf(buf_T *buf, linenr_T lnum)
false);
}
-
/// Count the size (in window cells) of the indent in line "ptr", with
/// 'tabstop' at "ts".
/// If @param list is true, count only screen size for tabs.
@@ -70,7 +66,7 @@ int get_indent_str(const char_u *ptr, int ts, bool list)
{
int count = 0;
- for (; *ptr; ++ptr) {
+ for (; *ptr; ptr++) {
// Count a tab for what it is worth.
if (*ptr == TAB) {
if (!list || curwin->w_p_lcs_chars.tab1) {
@@ -79,7 +75,7 @@ int get_indent_str(const char_u *ptr, int ts, bool list)
} else {
// In list mode, when tab is not set, count screen char width
// for Tab, displays: ^I
- count += ptr2cells(ptr);
+ count += ptr2cells((char *)ptr);
}
} else if (*ptr == ' ') {
// Count a space for one.
@@ -105,7 +101,7 @@ int get_indent_str_vtab(const char_u *ptr, long ts, long *vts, bool list)
} else {
// In list mode, when tab is not set, count screen char width
// for Tab, displays: ^I
- count += ptr2cells(ptr);
+ count += ptr2cells((char *)ptr);
}
} else if (*ptr == ' ') {
count++; // count a space for one
@@ -243,7 +239,7 @@ int set_indent(int size, int flags)
if (flags & SIN_INSERT) {
p = oldline;
} else {
- p = skipwhite(p);
+ p = (char_u *)skipwhite((char *)p);
}
line_len = (int)STRLEN(p) + 1;
@@ -325,7 +321,7 @@ int set_indent(int size, int flags)
todo -= tab_pad;
ind_done += tab_pad;
}
- p = skipwhite(p);
+ p = (char_u *)skipwhite((char *)p);
}
for (;;) {
@@ -353,10 +349,10 @@ int set_indent(int size, int flags)
const colnr_T new_offset = (colnr_T)(s - newline);
// this may free "newline"
- ml_replace(curwin->w_cursor.lnum, newline, false);
+ ml_replace(curwin->w_cursor.lnum, (char *)newline, false);
if (!(flags & SIN_NOMARK)) {
extmark_splice_cols(curbuf,
- (int)curwin->w_cursor.lnum-1,
+ (int)curwin->w_cursor.lnum - 1,
skipcols,
old_offset - skipcols,
new_offset - skipcols,
@@ -387,7 +383,6 @@ int set_indent(int size, int flags)
return retval;
}
-
// Return the indent of the current line after a number. Return -1 if no
// number was found. Used for 'n' in 'formatoptions': numbered list.
// Since a pattern is used it can actually handle more than numbers.
@@ -404,17 +399,17 @@ int get_number_indent(linenr_T lnum)
pos.lnum = 0;
// In format_lines() (i.e. not insert mode), fo+=q is needed too...
- if ((State & INSERT) || has_format_option(FO_Q_COMS)) {
- lead_len = get_leader_len(ml_get(lnum), NULL, false, true);
+ if ((State & MODE_INSERT) || has_format_option(FO_Q_COMS)) {
+ lead_len = get_leader_len((char *)ml_get(lnum), NULL, false, true);
}
- regmatch.regprog = vim_regcomp(curbuf->b_p_flp, RE_MAGIC);
+ regmatch.regprog = vim_regcomp((char *)curbuf->b_p_flp, RE_MAGIC);
if (regmatch.regprog != NULL) {
regmatch.rm_ic = false;
// vim_regexec() expects a pointer to a line. This lets us
// start matching for the flp beyond any comment leader...
- if (vim_regexec(&regmatch, ml_get(lnum) + lead_len, (colnr_T)0)) {
+ if (vim_regexec(&regmatch, (char *)ml_get(lnum) + lead_len, (colnr_T)0)) {
pos.lnum = lnum;
pos.col = (colnr_T)(*regmatch.endp - ml_get(lnum));
pos.coladd = 0;
@@ -442,10 +437,9 @@ int get_breakindent_win(win_T *wp, char_u *line)
static long *prev_vts = NULL; // Cached vartabs values.
int bri = 0;
// window width minus window margin space, i.e. what rests for text
- const int eff_wwidth = wp->w_width_inner
- - ((wp->w_p_nu || wp->w_p_rnu)
- && (vim_strchr(p_cpo, CPO_NUMCOL) == NULL)
- ? number_width(wp) + 1 : 0);
+ const int eff_wwidth = wp->w_width_inner -
+ ((wp->w_p_nu || wp->w_p_rnu)
+ && (vim_strchr(p_cpo, CPO_NUMCOL) == NULL) ? number_width(wp) + 1 : 0);
// used cached indent, unless pointer or 'tabstop' changed
if (prev_line != line || prev_ts != wp->w_buffer->b_p_ts
@@ -468,13 +462,13 @@ int get_breakindent_win(win_T *wp, char_u *line)
// add additional indent for numbered lists
if (wp->w_briopt_list != 0) {
regmatch_T regmatch = {
- .regprog = vim_regcomp(curbuf->b_p_flp,
+ .regprog = vim_regcomp((char *)curbuf->b_p_flp,
RE_MAGIC + RE_STRING + RE_AUTO + RE_STRICT),
};
if (regmatch.regprog != NULL) {
regmatch.rm_ic = false;
- if (vim_regexec(&regmatch, line, 0)) {
+ if (vim_regexec(&regmatch, (char *)line, 0)) {
if (wp->w_briopt_list > 0) {
bri += wp->w_briopt_list;
} else {
@@ -487,7 +481,7 @@ int get_breakindent_win(win_T *wp, char_u *line)
// indent minus the length of the showbreak string
if (wp->w_briopt_sbr) {
- bri -= vim_strsize(get_showbreak_value(wp));
+ bri -= vim_strsize((char *)get_showbreak_value(wp));
}
// never indent past left window margin
@@ -512,7 +506,7 @@ int inindent(int extra)
char_u *ptr;
colnr_T col;
- for (col = 0, ptr = get_cursor_line_ptr(); ascii_iswhite(*ptr); ++col) {
+ for (col = 0, ptr = get_cursor_line_ptr(); ascii_iswhite(*ptr); col++) {
ptr++;
}
@@ -523,6 +517,11 @@ int inindent(int extra)
}
}
+/// @return true if the conditions are OK for smart indenting.
+bool may_do_si(void)
+{
+ return curbuf->b_p_si && !curbuf->b_p_cin && *curbuf->b_p_inde == NUL && !p_paste;
+}
// Get indent level from 'indentexpr'.
int get_expr_indent(void)
@@ -549,7 +548,7 @@ int get_expr_indent(void)
// Need to make a copy, the 'indentexpr' option could be changed while
// evaluating it.
char_u *inde_copy = vim_strsave(curbuf->b_p_inde);
- indent = (int)eval_to_number(inde_copy);
+ indent = (int)eval_to_number((char *)inde_copy);
xfree(inde_copy);
if (use_sandbox) {
@@ -561,7 +560,7 @@ int get_expr_indent(void)
// Pretend to be in Insert mode, allow cursor past end of line for "o"
// command.
save_State = State;
- State = INSERT;
+ State = MODE_INSERT;
curwin->w_cursor = save_pos;
curwin->w_curswant = save_curswant;
curwin->w_set_curswant = save_set_curswant;
@@ -576,7 +575,6 @@ int get_expr_indent(void)
return indent;
}
-
// When 'p' is present in 'cpoptions, a Vi compatible method is used.
// The incompatible newer method is quite a bit better at indenting
// code in lisp-like languages than the traditional one; it's still
@@ -630,7 +628,7 @@ int get_lisp_indent(void)
continue;
}
- for (that = get_cursor_line_ptr(); *that != NUL; ++that) {
+ for (that = get_cursor_line_ptr(); *that != NUL; that++) {
if (*that == ';') {
while (*(that + 1) != NUL) {
that++;
@@ -658,6 +656,9 @@ int get_lisp_indent(void)
}
}
}
+ if (*that == NUL) {
+ break;
+ }
}
if ((*that == '(') || (*that == '[')) {
parencount++;
@@ -699,8 +700,10 @@ int get_lisp_indent(void)
&& lisp_match(that + 1)) {
amount += 2;
} else {
- that++;
- amount++;
+ if (*that != NUL) {
+ that++;
+ amount++;
+ }
firsttry = amount;
while (ascii_iswhite(*that)) {
@@ -762,7 +765,6 @@ int get_lisp_indent(void)
return amount;
}
-
static int lisp_match(char_u *p)
{
char_u buf[LSIZE];
@@ -770,7 +772,7 @@ static int lisp_match(char_u *p)
char_u *word = *curbuf->b_p_lw != NUL ? curbuf->b_p_lw : p_lispwords;
while (*word != NUL) {
- (void)copy_option_part(&word, buf, LSIZE, ",");
+ (void)copy_option_part((char **)&word, (char *)buf, LSIZE, ",");
len = (int)STRLEN(buf);
if ((STRNCMP(buf, p, len) == 0) && (p[len] == ' ')) {
diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c
index 0f0cab33ea..3c74b4bd8d 100644
--- a/src/nvim/indent_c.c
+++ b/src/nvim/indent_c.c
@@ -1,12 +1,9 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
-// uncrustify:off
-
#include <assert.h>
#include <inttypes.h>
-#include "nvim/vim.h"
#include "nvim/ascii.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
@@ -19,11 +16,12 @@
#include "nvim/option.h"
#include "nvim/search.h"
#include "nvim/strings.h"
+#include "nvim/vim.h"
// Find result cache for cpp_baseclass
typedef struct {
- int found;
- lpos_T lpos;
+ int found;
+ lpos_T lpos;
} cpp_baseclass_cache_T;
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -41,25 +39,22 @@ static pos_T *ind_find_start_comment(void) // XXX
pos_T *find_start_comment(int ind_maxcomment) // XXX
{
- pos_T *pos;
- char_u *line;
- char_u *p;
+ pos_T *pos;
int64_t cur_maxcomment = ind_maxcomment;
- for (;; ) {
+ for (;;) {
pos = findmatchlimit(NULL, '*', FM_BACKWARD, cur_maxcomment);
- if (pos == NULL)
+ if (pos == NULL) {
break;
+ }
/*
* Check if the comment start we found is inside a string.
* If it is then restrict the search to below this line and try again.
*/
- line = ml_get(pos->lnum);
- for (p = line; *p && (colnr_T)(p - line) < pos->col; ++p)
- p = skip_string(p);
- if ((colnr_T)(p - line) <= pos->col)
+ if (!is_pos_in_string(ml_get(pos->lnum), pos->col)) {
break;
+ }
cur_maxcomment = curwin->w_cursor.lnum - pos->lnum - 1;
if (cur_maxcomment <= 0) {
pos = NULL;
@@ -109,41 +104,34 @@ static pos_T *ind_find_start_CORS(linenr_T *is_raw)
*/
static pos_T *find_start_rawstring(int ind_maxcomment) // XXX
{
- pos_T *pos;
- char_u *line;
- char_u *p;
- long cur_maxcomment = ind_maxcomment;
+ pos_T *pos;
+ long cur_maxcomment = ind_maxcomment;
- for (;;)
- {
- pos = findmatchlimit(NULL, 'R', FM_BACKWARD, cur_maxcomment);
- if (pos == NULL)
- break;
+ for (;;) {
+ pos = findmatchlimit(NULL, 'R', FM_BACKWARD, cur_maxcomment);
+ if (pos == NULL) {
+ break;
+ }
- /*
- * Check if the raw string start we found is inside a string.
- * If it is then restrict the search to below this line and try again.
- */
- line = ml_get(pos->lnum);
- for (p = line; *p && (colnr_T)(p - line) < pos->col; ++p)
- p = skip_string(p);
- if ((colnr_T)(p - line) <= pos->col)
- break;
- cur_maxcomment = curwin->w_cursor.lnum - pos->lnum - 1;
- if (cur_maxcomment <= 0)
- {
- pos = NULL;
- break;
- }
+ // Check if the raw string start we found is inside a string.
+ // If it is then restrict the search to below this line and try again.
+ if (!is_pos_in_string(ml_get(pos->lnum), pos->col)) {
+ break;
+ }
+ cur_maxcomment = curwin->w_cursor.lnum - pos->lnum - 1;
+ if (cur_maxcomment <= 0) {
+ pos = NULL;
+ break;
}
- return pos;
+ }
+ return pos;
}
/*
* Skip to the end of a "string" and a 'c' character.
* If there is no string or character, return argument unmodified.
*/
-static char_u *skip_string(char_u *p)
+static const char_u *skip_string(const char_u *p)
{
int i;
@@ -152,17 +140,17 @@ static char_u *skip_string(char_u *p)
*/
for (;; p++) {
if (p[0] == '\'') { // 'c' or '\n' or '\000'
- if (!p[1]) { // ' at end of line
+ if (p[1] == NUL) { // ' at end of line
break;
}
i = 2;
- if (p[1] == '\\') { // '\n' or '\000'
+ if (p[1] == '\\' && p[2] != NUL) { // '\n' or '\000'
i++;
while (ascii_isdigit(p[i - 1])) { // '\000'
i++;
}
}
- if (p[i] == '\'') { // check for trailing '
+ if (p[i - 1] != NUL && p[i] == '\'') { // check for trailing '
p += i;
continue;
}
@@ -178,24 +166,24 @@ static char_u *skip_string(char_u *p)
continue; // continue for another string
}
} else if (p[0] == 'R' && p[1] == '"') {
- // Raw string: R"[delim](...)[delim]"
- char_u *delim = p + 2;
- char_u *paren = vim_strchr(delim, '(');
-
- if (paren != NULL) {
- const ptrdiff_t delim_len = paren - delim;
-
- for (p += 3; *p; ++p)
- if (p[0] == ')' && STRNCMP(p + 1, delim, delim_len) == 0
- && p[delim_len + 1] == '"')
- {
- p += delim_len + 1;
- break;
- }
- if (p[0] == '"') {
- continue; // continue for another string
- }
+ // Raw string: R"[delim](...)[delim]"
+ const char *delim = (char *)p + 2;
+ const char *paren = vim_strchr((char *)delim, '(');
+
+ if (paren != NULL) {
+ const ptrdiff_t delim_len = paren - delim;
+
+ for (p += 3; *p; p++) {
+ if (p[0] == ')' && STRNCMP(p + 1, delim, delim_len) == 0
+ && p[delim_len + 1] == '"') {
+ p += delim_len + 1;
+ break;
+ }
}
+ if (p[0] == '"') {
+ continue; // continue for another string
+ }
+ }
}
break; // no string found
}
@@ -205,6 +193,16 @@ static char_u *skip_string(char_u *p)
return p;
}
+/// @returns true if "line[col]" is inside a C string.
+int is_pos_in_string(const char_u *line, colnr_T col)
+{
+ const char_u *p;
+
+ for (p = line; *p && (colnr_T)(p - line) < col; p++) {
+ p = skip_string(p);
+ }
+ return !((colnr_T)(p - line) <= col);
+}
/*
* Functions for C-indenting.
@@ -214,20 +212,19 @@ static char_u *skip_string(char_u *p)
* Below "XXX" means that this function may unlock the current line.
*/
-
/*
* Return true if the string "line" starts with a word from 'cinwords'.
*/
-bool cin_is_cinword(char_u *line)
+bool cin_is_cinword(const char_u *line)
{
bool retval = false;
size_t cinw_len = STRLEN(curbuf->b_p_cinw) + 1;
char_u *cinw_buf = xmalloc(cinw_len);
- line = skipwhite(line);
+ line = (char_u *)skipwhite((char *)line);
- for (char_u *cinw = curbuf->b_p_cinw; *cinw; ) {
- size_t len = copy_option_part(&cinw, cinw_buf, cinw_len, ",");
+ for (char_u *cinw = curbuf->b_p_cinw; *cinw;) {
+ size_t len = copy_option_part((char **)&cinw, (char *)cinw_buf, cinw_len, ",");
if (STRNCMP(line, cinw_buf, len) == 0
&& (!vim_iswordc(line[len]) || !vim_iswordc(line[len - 1]))) {
retval = true;
@@ -240,18 +237,16 @@ bool cin_is_cinword(char_u *line)
return retval;
}
-
-
/*
* Skip over white space and C comments within the line.
* Also skip over Perl/shell comments if desired.
*/
-static char_u *cin_skipcomment(char_u *s)
+static const char_u *cin_skipcomment(const char_u *s)
{
while (*s) {
- char_u *prev_s = s;
+ const char_u *prev_s = s;
- s = skipwhite(s);
+ s = (char_u *)skipwhite((char *)s);
/* Perl/shell # comment comment continues until eol. Require a space
* before # to avoid recognizing $#array. */
@@ -267,8 +262,9 @@ static char_u *cin_skipcomment(char_u *s)
s += STRLEN(s);
break;
}
- if (*s != '*')
+ if (*s != '*') {
break;
+ }
for (++s; *s; s++) { // skip slash-star comment
if (s[0] == '*' && s[1] == '/') {
s += 2;
@@ -283,7 +279,7 @@ static char_u *cin_skipcomment(char_u *s)
* Return TRUE if there is no code at *s. White space and comments are
* not considered code.
*/
-static int cin_nocode(char_u *s)
+static int cin_nocode(const char_u *s)
{
return *cin_skipcomment(s) == NUL;
}
@@ -294,27 +290,28 @@ static int cin_nocode(char_u *s)
static pos_T *find_line_comment(void) // XXX
{
static pos_T pos;
- char_u *line;
- char_u *p;
+ char_u *line;
+ char_u *p;
pos = curwin->w_cursor;
while (--pos.lnum > 0) {
line = ml_get(pos.lnum);
- p = skipwhite(line);
+ p = (char_u *)skipwhite((char *)line);
if (cin_islinecomment(p)) {
pos.col = (int)(p - line);
return &pos;
}
- if (*p != NUL)
+ if (*p != NUL) {
break;
+ }
}
return NULL;
}
/// Checks if `text` starts with "key:".
-static bool cin_has_js_key(char_u *text)
+static bool cin_has_js_key(const char_u *text)
{
- char_u *s = skipwhite(text);
+ const char_u *s = (char_u *)skipwhite((char *)text);
char_u quote = 0;
if (*s == '\'' || *s == '"') {
@@ -322,8 +319,8 @@ static bool cin_has_js_key(char_u *text)
quote = *s;
++s;
}
- if (!vim_isIDc(*s)) { // need at least one ID character
- return FALSE;
+ if (!vim_isIDc(*s)) { // need at least one ID character
+ return false;
}
while (vim_isIDc(*s)) {
@@ -341,15 +338,16 @@ static bool cin_has_js_key(char_u *text)
/// Checks if string matches "label:"; move to character after ':' if true.
/// "*s" must point to the start of the label, if there is one.
-static bool cin_islabel_skip(char_u **s)
+static bool cin_islabel_skip(const char_u **s)
FUNC_ATTR_NONNULL_ALL
{
if (!vim_isIDc(**s)) { // need at least one ID character
return false;
}
- while (vim_isIDc(**s))
+ while (vim_isIDc(**s)) {
(*s)++;
+ }
*s = cin_skipcomment(*s);
@@ -361,7 +359,7 @@ static bool cin_islabel_skip(char_u **s)
// Note: curwin->w_cursor must be where we are looking for the label.
bool cin_islabel(void) // XXX
{
- char_u *s = cin_skipcomment(get_cursor_line_ptr());
+ const char_u *s = cin_skipcomment(get_cursor_line_ptr());
// Exclude "default" from labels, since it should be indented
// like a switch label. Same for C++ scope declarations.
@@ -380,8 +378,8 @@ bool cin_islabel(void) // XXX
* label.
*/
pos_T cursor_save;
- pos_T *trypos;
- char_u *line;
+ pos_T *trypos;
+ const char_u *line;
cursor_save = curwin->w_cursor;
while (curwin->w_cursor.lnum > 1) {
@@ -424,15 +422,16 @@ bool cin_islabel(void) // XXX
*/
static int cin_isinit(void)
{
- char_u *s;
- static char *skip[] = {"static", "public", "protected", "private"};
+ const char_u *s;
+ static char *skip[] = { "static", "public", "protected", "private" };
s = cin_skipcomment(get_cursor_line_ptr());
- if (cin_starts_with(s, "typedef"))
+ if (cin_starts_with(s, "typedef")) {
s = cin_skipcomment(s + 7);
+ }
- for (;; ) {
+ for (;;) {
int i, l;
for (i = 0; i < (int)ARRAY_SIZE(skip); ++i) {
@@ -443,26 +442,26 @@ static int cin_isinit(void)
break;
}
}
- if (l != 0)
+ if (l != 0) {
break;
+ }
}
- if (cin_starts_with(s, "enum"))
- return TRUE;
+ if (cin_starts_with(s, "enum")) {
+ return true;
+ }
- if (cin_ends_in(s, (char_u *)"=", (char_u *)"{"))
- return TRUE;
+ if (cin_ends_in(s, (char_u *)"=", (char_u *)"{")) {
+ return true;
+ }
return FALSE;
}
-/*
- * Recognize a switch label: "case .*:" or "default:".
- */
-bool cin_iscase(
- char_u *s,
- bool strict // Allow relaxed check of case statement for JS
-)
+/// Recognize a switch label: "case .*:" or "default:".
+///
+/// @param strict Allow relaxed check of case statement for JS
+bool cin_iscase(const char_u *s, bool strict)
{
s = cin_skipcomment(s);
if (cin_starts_with(s, "case")) {
@@ -503,50 +502,61 @@ bool cin_iscase(
/*
* Recognize a "default" switch label.
*/
-static int cin_isdefault(char_u *s)
+static int cin_isdefault(const char_u *s)
{
return STRNCMP(s, "default", 7) == 0
&& *(s = cin_skipcomment(s + 7)) == ':'
&& s[1] != ':';
}
-/*
- * Recognize a "public/private/protected" scope declaration label.
- */
-bool cin_isscopedecl(char_u *s)
+/// Recognize a scope declaration label set in 'cinscopedecls'.
+bool cin_isscopedecl(const char_u *p)
{
- int i;
+ const char_u *s = cin_skipcomment(p);
- s = cin_skipcomment(s);
- if (STRNCMP(s, "public", 6) == 0) {
- i = 6;
- } else if (STRNCMP(s, "protected", 9) == 0) {
- i = 9;
- } else if (STRNCMP(s, "private", 7) == 0) {
- i = 7;
- } else {
- return false;
+ const size_t cinsd_len = STRLEN(curbuf->b_p_cinsd) + 1;
+ char_u *cinsd_buf = xmalloc(cinsd_len);
+
+ bool found = false;
+
+ for (char_u *cinsd = curbuf->b_p_cinsd; *cinsd;) {
+ const size_t len = copy_option_part((char **)&cinsd, (char *)cinsd_buf, cinsd_len, ",");
+ if (STRNCMP(s, cinsd_buf, len) == 0) {
+ const char_u *skip = cin_skipcomment(s + len);
+ if (*skip == ':' && skip[1] != ':') {
+ found = true;
+ break;
+ }
+ }
}
- return *(s = cin_skipcomment(s + i)) == ':' && s[1] != ':';
+
+ xfree(cinsd_buf);
+
+ return found;
}
// Maximum number of lines to search back for a "namespace" line.
#define FIND_NAMESPACE_LIM 20
// Recognize a "namespace" scope declaration.
-static bool cin_is_cpp_namespace(char_u *s)
+static bool cin_is_cpp_namespace(const char_u *s)
{
- char_u *p;
+ const char_u *p;
bool has_name = false;
bool has_name_start = false;
s = cin_skipcomment(s);
+
+ if (STRNCMP(s, "inline", 6) == 0 && (s[6] == NUL || !vim_iswordc(s[6]))) {
+ s = cin_skipcomment((char_u *)skipwhite((char *)s + 6));
+ }
+
if (STRNCMP(s, "namespace", 9) == 0 && (s[9] == NUL || !vim_iswordc(s[9]))) {
- p = cin_skipcomment(skipwhite(s + 9));
+ p = cin_skipcomment((char_u *)skipwhite((char *)s + 9));
while (*p != NUL) {
if (ascii_iswhite(*p)) {
has_name = true; // found end of a name
- p = cin_skipcomment(skipwhite(p));
+ p = cin_skipcomment((char_u *)skipwhite((char *)p));
} else if (*p == '{') {
break;
} else if (vim_iswordc(*p)) {
@@ -573,10 +583,10 @@ static bool cin_is_cpp_namespace(char_u *s)
/*
* Return a pointer to the first non-empty non-comment character after a ':'.
* Return NULL if not found.
- * case 234: a = b;
- * ^
+ * case 234: a = b;
+ * ^
*/
-static char_u *after_label(char_u *l)
+static const char_u *after_label(const char_u *l)
{
for (; *l; ++l) {
if (*l == ':') {
@@ -589,11 +599,13 @@ static char_u *after_label(char_u *l)
l += 2; // skip over 'x'
}
}
- if (*l == NUL)
+ if (*l == NUL) {
return NULL;
+ }
l = cin_skipcomment(l + 1);
- if (*l == NUL)
+ if (*l == NUL) {
return NULL;
+ }
return l;
}
@@ -603,15 +615,16 @@ static char_u *after_label(char_u *l)
*/
static int get_indent_nolabel(linenr_T lnum) // XXX
{
- char_u *l;
+ const char_u *l;
pos_T fp;
colnr_T col;
- char_u *p;
+ const char_u *p;
l = ml_get(lnum);
p = after_label(l);
- if (p == NULL)
+ if (p == NULL) {
return 0;
+ }
fp.col = (colnr_T)(p - l);
fp.lnum = lnum;
@@ -622,12 +635,12 @@ static int get_indent_nolabel(linenr_T lnum) // XXX
/*
* Find indent for line "lnum", ignoring any case or jump label.
* Also return a pointer to the text (after the label) in "pp".
- * label: if (asdf && asdfasdf)
- * ^
+ * label: if (asdf && asdfasdf)
+ * ^
*/
-static int skip_label(linenr_T lnum, char_u **pp)
+static int skip_label(linenr_T lnum, const char_u **pp)
{
- char_u *l;
+ const char_u *l;
int amount;
pos_T cursor_save;
@@ -653,44 +666,45 @@ static int skip_label(linenr_T lnum, char_u **pp)
/*
* Return the indent of the first variable name after a type in a declaration.
- * int a, indent of "a"
- * static struct foo b, indent of "b"
- * enum bla c, indent of "c"
+ * int a, indent of "a"
+ * static struct foo b, indent of "b"
+ * enum bla c, indent of "c"
* Returns zero when it doesn't look like a declaration.
*/
static int cin_first_id_amount(void)
{
- char_u *line, *p, *s;
+ char_u *line, *p, *s;
int len;
pos_T fp;
colnr_T col;
line = get_cursor_line_ptr();
- p = skipwhite(line);
+ p = (char_u *)skipwhite((char *)line);
len = (int)(skiptowhite(p) - p);
if (len == 6 && STRNCMP(p, "static", 6) == 0) {
- p = skipwhite(p + 6);
+ p = (char_u *)skipwhite((char *)p + 6);
len = (int)(skiptowhite(p) - p);
}
- if (len == 6 && STRNCMP(p, "struct", 6) == 0)
- p = skipwhite(p + 6);
- else if (len == 4 && STRNCMP(p, "enum", 4) == 0)
- p = skipwhite(p + 4);
- else if ((len == 8 && STRNCMP(p, "unsigned", 8) == 0)
- || (len == 6 && STRNCMP(p, "signed", 6) == 0)) {
- s = skipwhite(p + len);
+ if (len == 6 && STRNCMP(p, "struct", 6) == 0) {
+ p = (char_u *)skipwhite((char *)p + 6);
+ } else if (len == 4 && STRNCMP(p, "enum", 4) == 0) {
+ p = (char_u *)skipwhite((char *)p + 4);
+ } else if ((len == 8 && STRNCMP(p, "unsigned", 8) == 0)
+ || (len == 6 && STRNCMP(p, "signed", 6) == 0)) {
+ s = (char_u *)skipwhite((char *)p + len);
if ((STRNCMP(s, "int", 3) == 0 && ascii_iswhite(s[3]))
|| (STRNCMP(s, "long", 4) == 0 && ascii_iswhite(s[4]))
|| (STRNCMP(s, "short", 5) == 0 && ascii_iswhite(s[5]))
- || (STRNCMP(s, "char", 4) == 0 && ascii_iswhite(s[4])))
+ || (STRNCMP(s, "char", 4) == 0 && ascii_iswhite(s[4]))) {
p = s;
+ }
}
- for (len = 0; vim_isIDc(p[len]); ++len)
- ;
- if (len == 0 || !ascii_iswhite(p[len]) || cin_nocode(p))
+ for (len = 0; vim_isIDc(p[len]); len++) {}
+ if (len == 0 || !ascii_iswhite(p[len]) || cin_nocode(p)) {
return 0;
+ }
- p = skipwhite(p + len);
+ p = (char_u *)skipwhite((char *)p + len);
fp.lnum = curwin->w_cursor.lnum;
fp.col = (colnr_T)(p - line);
getvcol(curwin, &fp, &col, NULL, NULL);
@@ -703,36 +717,39 @@ static int cin_first_id_amount(void)
* Return zero if no (useful) equal sign found.
* Return -1 if the line above "lnum" ends in a backslash.
* foo = "asdf\
- * asdf\
- * here";
+ * asdf\
+ * here";
*/
static int cin_get_equal_amount(linenr_T lnum)
{
- char_u *line;
- char_u *s;
+ const char_u *line;
+ const char_u *s;
colnr_T col;
pos_T fp;
if (lnum > 1) {
line = ml_get(lnum - 1);
- if (*line != NUL && line[STRLEN(line) - 1] == '\\')
+ if (*line != NUL && line[STRLEN(line) - 1] == '\\') {
return -1;
+ }
}
line = s = ml_get(lnum);
- while (*s != NUL && vim_strchr((char_u *)"=;{}\"'", *s) == NULL) {
+ while (*s != NUL && vim_strchr("=;{}\"'", *s) == NULL) {
if (cin_iscomment(s)) { // ignore comments
s = cin_skipcomment(s);
} else {
s++;
}
}
- if (*s != '=')
+ if (*s != '=') {
return 0;
+ }
- s = skipwhite(s + 1);
- if (cin_nocode(s))
+ s = (char_u *)skipwhite((char *)s + 1);
+ if (cin_nocode(s)) {
return 0;
+ }
if (*s == '"') { // nice alignment for continued strings
s++;
@@ -747,10 +764,11 @@ static int cin_get_equal_amount(linenr_T lnum)
/*
* Recognize a preprocessor statement: Any line that starts with '#'.
*/
-static int cin_ispreproc(char_u *s)
+static int cin_ispreproc(const char_u *s)
{
- if (*skipwhite(s) == '#')
- return TRUE;
+ if (*skipwhite((char *)s) == '#') {
+ return true;
+ }
return FALSE;
}
@@ -758,9 +776,9 @@ static int cin_ispreproc(char_u *s)
/// continuation line of a preprocessor statement. Decrease "*lnump" to the
/// start and return the line in "*pp".
/// Put the amount of indent in "*amount".
-static int cin_ispreproc_cont(char_u **pp, linenr_T *lnump, int *amount)
+static int cin_ispreproc_cont(const char_u **pp, linenr_T *lnump, int *amount)
{
- char_u *line = *pp;
+ const char_u *line = *pp;
linenr_T lnum = *lnump;
int retval = false;
int candidate_amount = *amount;
@@ -769,17 +787,19 @@ static int cin_ispreproc_cont(char_u **pp, linenr_T *lnump, int *amount)
candidate_amount = get_indent_lnum(lnum);
}
- for (;; ) {
+ for (;;) {
if (cin_ispreproc(line)) {
retval = TRUE;
*lnump = lnum;
break;
}
- if (lnum == 1)
+ if (lnum == 1) {
break;
+ }
line = ml_get(--lnum);
- if (*line == NUL || line[STRLEN(line) - 1] != '\\')
+ if (*line == NUL || line[STRLEN(line) - 1] != '\\') {
break;
+ }
}
if (lnum != *lnump) {
@@ -794,7 +814,7 @@ static int cin_ispreproc_cont(char_u **pp, linenr_T *lnump, int *amount)
/*
* Recognize the start of a C or C++ comment.
*/
-static int cin_iscomment(char_u *p)
+static int cin_iscomment(const char_u *p)
{
return p[0] == '/' && (p[1] == '*' || p[1] == '/');
}
@@ -802,26 +822,23 @@ static int cin_iscomment(char_u *p)
/*
* Recognize the start of a "//" comment.
*/
-static int cin_islinecomment(char_u *p)
+static int cin_islinecomment(const char_u *p)
{
return p[0] == '/' && p[1] == '/';
}
-/*
- * Recognize a line that starts with '{' or '}', or ends with ';', ',', '{' or
- * '}'.
- * Don't consider "} else" a terminated line.
- * If a line begins with an "else", only consider it terminated if no unmatched
- * opening braces follow (handle "else { foo();" correctly).
- * Return the character terminating the line (ending char's have precedence if
- * both apply in order to determine initializations).
- */
-static char_u
-cin_isterminated (
- char_u *s,
- int incl_open, // include '{' at the end as terminator
- int incl_comma // recognize a trailing comma
-)
+/// Recognize a line that starts with '{' or '}', or ends with ';', ',', '{' or
+/// '}'.
+/// Don't consider "} else" a terminated line.
+/// If a line begins with an "else", only consider it terminated if no unmatched
+/// opening braces follow (handle "else { foo();" correctly).
+///
+/// @param incl_open include '{' at the end as terminator
+/// @param incl_comma recognize a trailing comma
+///
+/// @return the character terminating the line (ending char's have precedence if
+/// both apply in order to determine initializations).
+static char_u cin_isterminated(const char_u *s, int incl_open, int incl_comma)
{
char_u found_start = 0;
unsigned n_open = 0;
@@ -829,30 +846,35 @@ cin_isterminated (
s = cin_skipcomment(s);
- if (*s == '{' || (*s == '}' && !cin_iselse(s)))
+ if (*s == '{' || (*s == '}' && !cin_iselse(s))) {
found_start = *s;
+ }
- if (!found_start)
+ if (!found_start) {
is_else = cin_iselse(s);
+ }
while (*s) {
// skip over comments, "" strings and 'c'haracters
s = skip_string(cin_skipcomment(s));
- if (*s == '}' && n_open > 0)
- --n_open;
+ if (*s == '}' && n_open > 0) {
+ n_open--;
+ }
if ((!is_else || n_open == 0)
&& (*s == ';' || *s == '}' || (incl_comma && *s == ','))
- && cin_nocode(s + 1))
+ && cin_nocode(s + 1)) {
return *s;
- else if (*s == '{') {
- if (incl_open && cin_nocode(s + 1))
+ } else if (*s == '{') {
+ if (incl_open && cin_nocode(s + 1)) {
return *s;
- else
- ++n_open;
+ } else {
+ n_open++;
+ }
}
- if (*s)
+ if (*s) {
s++;
+ }
}
return found_start;
}
@@ -867,19 +889,20 @@ cin_isterminated (
/// lines here.
/// @param[in] first_lnum Where to start looking.
/// @param[in] min_lnum The line before which we will not be looking.
-static int cin_isfuncdecl(char_u **sp, linenr_T first_lnum, linenr_T min_lnum)
+static int cin_isfuncdecl(const char_u **sp, linenr_T first_lnum, linenr_T min_lnum)
{
- char_u *s;
+ const char_u *s;
linenr_T lnum = first_lnum;
linenr_T save_lnum = curwin->w_cursor.lnum;
int retval = false;
- pos_T *trypos;
- int just_started = TRUE;
+ pos_T *trypos;
+ int just_started = true;
- if (sp == NULL)
+ if (sp == NULL) {
s = ml_get(lnum);
- else
+ } else {
s = *sp;
+ }
curwin->w_cursor.lnum = lnum;
if (find_last_paren(s, '(', ')')
@@ -929,8 +952,9 @@ static int cin_isfuncdecl(char_u **sp, linenr_T first_lnum, linenr_T min_lnum)
// defined(y)
lnum = first_lnum - 1;
s = ml_get(lnum);
- if (*s == NUL || s[STRLEN(s) - 1] != '\\')
- retval = TRUE;
+ if (*s == NUL || s[STRLEN(s) - 1] != '\\') {
+ retval = true;
+ }
goto done;
}
if ((*s == ',' && cin_nocode(s + 1)) || s[1] == NUL || cin_nocode(s)) {
@@ -940,20 +964,24 @@ static int cin_isfuncdecl(char_u **sp, linenr_T first_lnum, linenr_T min_lnum)
* At the end: check for ',' in the next line, for this style:
* func(arg1
* , arg2) */
- for (;; ) {
- if (lnum >= curbuf->b_ml.ml_line_count)
+ for (;;) {
+ if (lnum >= curbuf->b_ml.ml_line_count) {
break;
+ }
s = ml_get(++lnum);
- if (!cin_ispreproc(s))
+ if (!cin_ispreproc(s)) {
break;
+ }
}
- if (lnum >= curbuf->b_ml.ml_line_count)
+ if (lnum >= curbuf->b_ml.ml_line_count) {
break;
- /* Require a comma at end of the line or a comma or ')' at the
- * start of next line. */
- s = skipwhite(s);
- if (!just_started && (!comma && *s != ',' && *s != ')'))
+ }
+ // Require a comma at end of the line or a comma or ')' at the
+ // start of next line.
+ s = (char_u *)skipwhite((char *)s);
+ if (!just_started && (!comma && *s != ',' && *s != ')')) {
break;
+ }
just_started = false;
} else if (cin_iscomment(s)) { // ignore comments
s = cin_skipcomment(s);
@@ -964,18 +992,19 @@ static int cin_isfuncdecl(char_u **sp, linenr_T first_lnum, linenr_T min_lnum)
}
done:
- if (lnum != first_lnum && sp != NULL)
+ if (lnum != first_lnum && sp != NULL) {
*sp = ml_get(first_lnum);
+ }
return retval;
}
-static int cin_isif(char_u *p)
+static int cin_isif(const char_u *p)
{
return STRNCMP(p, "if", 2) == 0 && !vim_isIDc(p[2]);
}
-static int cin_iselse(char_u *p)
+static int cin_iselse(const char_u *p)
{
if (*p == '}') { // accept "} else"
p = cin_skipcomment(p + 1);
@@ -983,7 +1012,7 @@ static int cin_iselse(char_u *p)
return STRNCMP(p, "else", 4) == 0 && !vim_isIDc(p[4]);
}
-static int cin_isdo(char_u *p)
+static int cin_isdo(const char_u *p)
{
return STRNCMP(p, "do", 2) == 0 && !vim_isIDc(p[2]);
}
@@ -993,11 +1022,11 @@ static int cin_isdo(char_u *p)
* We only accept a "while (condition) ;", with only white space between the
* ')' and ';'. The condition may be spread over several lines.
*/
-static int cin_iswhileofdo(char_u *p, linenr_T lnum) // XXX
+static int cin_iswhileofdo(const char_u *p, linenr_T lnum) // XXX
{
pos_T cursor_save;
- pos_T *trypos;
- int retval = FALSE;
+ pos_T *trypos;
+ int retval = false;
p = cin_skipcomment(p);
if (*p == '}') { // accept "} while (cond);"
@@ -1012,10 +1041,10 @@ static int cin_iswhileofdo(char_u *p, linenr_T lnum) // XXX
p++;
curwin->w_cursor.col++;
}
- if ((trypos = findmatchlimit(NULL, 0, 0,
- curbuf->b_ind_maxparen)) != NULL
- && *cin_skipcomment(ml_get_pos(trypos) + 1) == ';')
- retval = TRUE;
+ if ((trypos = findmatchlimit(NULL, 0, 0, curbuf->b_ind_maxparen)) != NULL
+ && *cin_skipcomment(ml_get_pos(trypos) + 1) == ';') {
+ retval = true;
+ }
curwin->w_cursor = cursor_save;
}
return retval;
@@ -1027,28 +1056,33 @@ static int cin_iswhileofdo(char_u *p, linenr_T lnum) // XXX
* Otherwise return !0 and update "*poffset" to point to the place where the
* string was found.
*/
-static int cin_is_if_for_while_before_offset(char_u *line, int *poffset)
+static int cin_is_if_for_while_before_offset(const char_u *line, int *poffset)
{
int offset = *poffset;
- if (offset-- < 2)
+ if (offset-- < 2) {
return 0;
- while (offset > 2 && ascii_iswhite(line[offset]))
- --offset;
+ }
+ while (offset > 2 && ascii_iswhite(line[offset])) {
+ offset--;
+ }
offset -= 1;
- if (!STRNCMP(line + offset, "if", 2))
+ if (!STRNCMP(line + offset, "if", 2)) {
goto probablyFound;
+ }
if (offset >= 1) {
offset -= 1;
- if (!STRNCMP(line + offset, "for", 3))
+ if (!STRNCMP(line + offset, "for", 3)) {
goto probablyFound;
+ }
if (offset >= 2) {
offset -= 2;
- if (!STRNCMP(line + offset, "while", 5))
+ if (!STRNCMP(line + offset, "while", 5)) {
goto probablyFound;
+ }
}
}
return 0;
@@ -1066,15 +1100,15 @@ probablyFound:
* do
* nothing;
* while (foo
- * && bar); <-- here
+ * && bar); <-- here
* Adjust the cursor to the line with "while".
*/
static int cin_iswhileofdo_end(int terminated)
{
- char_u *line;
- char_u *p;
- char_u *s;
- pos_T *trypos;
+ const char_u *line;
+ const char_u *p;
+ const char_u *s;
+ pos_T *trypos;
int i;
if (terminated != ';') { // there must be a ';' at the end
@@ -1085,7 +1119,7 @@ static int cin_iswhileofdo_end(int terminated)
while (*p != NUL) {
p = cin_skipcomment(p);
if (*p == ')') {
- s = skipwhite(p + 1);
+ s = (char_u *)skipwhite((char *)p + 1);
if (*s == ';' && cin_nocode(s + 1)) {
/* Found ");" at end of the line, now check there is "while"
* before the matching '('. XXX */
@@ -1108,13 +1142,14 @@ static int cin_iswhileofdo_end(int terminated)
p = line + i;
}
}
- if (*p != NUL)
- ++p;
+ if (*p != NUL) {
+ p++;
+ }
}
return FALSE;
}
-static int cin_isbreak(char_u *p)
+static int cin_isbreak(const char_u *p)
{
return STRNCMP(p, "break", 5) == 0 && !vim_isIDc(p[5]);
}
@@ -1124,20 +1159,21 @@ static int cin_isbreak(char_u *p)
* constructor-initialization. eg:
*
* class MyClass :
- * baseClass <-- here
+ * baseClass <-- here
* class MyClass : public baseClass,
- * anotherBaseClass <-- here (should probably lineup ??)
+ * anotherBaseClass <-- here (should probably lineup ??)
* MyClass::MyClass(...) :
- * baseClass(...) <-- here (constructor-initialization)
+ * baseClass(...) <-- here (constructor-initialization)
*
* This is a lot of guessing. Watch out for "cond ? func() : foo".
*/
-static int cin_is_cpp_baseclass(cpp_baseclass_cache_T *cached) {
+static int cin_is_cpp_baseclass(cpp_baseclass_cache_T *cached)
+{
lpos_T *pos = &cached->lpos; // find position
- char_u *s;
+ const char_u *s;
int class_or_struct, lookfor_ctor_init, cpp_base_class;
linenr_T lnum = curwin->w_cursor.lnum;
- char_u *line = get_cursor_line_ptr();
+ const char_u *line = get_cursor_line_ptr();
if (pos->lnum <= lnum) {
return cached->found; // Use the cached result
@@ -1145,51 +1181,56 @@ static int cin_is_cpp_baseclass(cpp_baseclass_cache_T *cached) {
pos->col = 0;
- s = skipwhite(line);
+ s = (char_u *)skipwhite((char *)line);
if (*s == '#') { // skip #define FOO x ? (x) : x
return false;
}
s = cin_skipcomment(s);
- if (*s == NUL)
- return FALSE;
+ if (*s == NUL) {
+ return false;
+ }
cpp_base_class = lookfor_ctor_init = class_or_struct = FALSE;
/* Search for a line starting with '#', empty, ending in ';' or containing
* '{' or '}' and start below it. This handles the following situations:
- * a = cond ?
- * func() :
- * asdf;
- * func::foo()
- * : something
- * {}
- * Foo::Foo (int one, int two)
- * : something(4),
- * somethingelse(3)
- * {}
+ * a = cond ?
+ * func() :
+ * asdf;
+ * func::foo()
+ * : something
+ * {}
+ * Foo::Foo (int one, int two)
+ * : something(4),
+ * somethingelse(3)
+ * {}
*/
while (lnum > 1) {
line = ml_get(lnum - 1);
- s = skipwhite(line);
- if (*s == '#' || *s == NUL)
+ s = (char_u *)skipwhite((char *)line);
+ if (*s == '#' || *s == NUL) {
break;
+ }
while (*s != NUL) {
s = cin_skipcomment(s);
if (*s == '{' || *s == '}'
- || (*s == ';' && cin_nocode(s + 1)))
+ || (*s == ';' && cin_nocode(s + 1))) {
break;
- if (*s != NUL)
- ++s;
+ }
+ if (*s != NUL) {
+ s++;
+ }
}
- if (*s != NUL)
+ if (*s != NUL) {
break;
- --lnum;
+ }
+ lnum--;
}
pos->lnum = lnum;
line = ml_get(lnum);
s = line;
- for (;; ) {
+ for (;;) {
if (*s == NUL) {
if (lnum == curwin->w_cursor.lnum) {
break;
@@ -1204,13 +1245,14 @@ static int cin_is_cpp_baseclass(cpp_baseclass_cache_T *cached) {
break;
}
s = cin_skipcomment(line);
- if (*s == NUL)
+ if (*s == NUL) {
continue;
+ }
}
- if (s[0] == '"' || (s[0] == 'R' && s[1] == '"'))
+ if (s[0] == '"' || (s[0] == 'R' && s[1] == '"')) {
s = skip_string(s) + 1;
- else if (s[0] == ':') {
+ } else if (s[0] == ':') {
if (s[1] == ':') {
/* skip double colon. It can't be a constructor
* initialization any more */
@@ -1223,17 +1265,19 @@ static int cin_is_cpp_baseclass(cpp_baseclass_cache_T *cached) {
lookfor_ctor_init = class_or_struct = false;
pos->col = 0;
s = cin_skipcomment(s + 1);
- } else
+ } else {
s = cin_skipcomment(s + 1);
+ }
} else if ((STRNCMP(s, "class", 5) == 0 && !vim_isIDc(s[5]))
|| (STRNCMP(s, "struct", 6) == 0 && !vim_isIDc(s[6]))) {
class_or_struct = TRUE;
lookfor_ctor_init = FALSE;
- if (*s == 'c')
+ if (*s == 'c') {
s = cin_skipcomment(s + 5);
- else
+ } else {
s = cin_skipcomment(s + 6);
+ }
} else {
if (s[0] == '{' || s[0] == '}' || s[0] == ';') {
cpp_base_class = lookfor_ctor_init = class_or_struct = FALSE;
@@ -1279,7 +1323,7 @@ static int get_baseclass_amount(int col)
{
int amount;
colnr_T vcol;
- pos_T *trypos;
+ pos_T *trypos;
if (col == 0) {
amount = get_indent();
@@ -1295,8 +1339,9 @@ static int get_baseclass_amount(int col)
getvcol(curwin, &curwin->w_cursor, &vcol, NULL, NULL);
amount = (int)vcol;
}
- if (amount < curbuf->b_ind_cpp_baseclass)
+ if (amount < curbuf->b_ind_cpp_baseclass) {
amount = curbuf->b_ind_cpp_baseclass;
+ }
return amount;
}
@@ -1305,23 +1350,26 @@ static int get_baseclass_amount(int col)
* white space and comments. Skip strings and comments.
* Ignore "ignore" after "find" if it's not NULL.
*/
-static int cin_ends_in(char_u *s, char_u *find, char_u *ignore)
+static int cin_ends_in(const char_u *s, const char_u *find, const char_u *ignore)
{
- char_u *p = s;
- char_u *r;
+ const char_u *p = s;
+ const char_u *r;
int len = (int)STRLEN(find);
while (*p != NUL) {
p = cin_skipcomment(p);
if (STRNCMP(p, find, len) == 0) {
- r = skipwhite(p + len);
- if (ignore != NULL && STRNCMP(r, ignore, STRLEN(ignore)) == 0)
- r = skipwhite(r + STRLEN(ignore));
- if (cin_nocode(r))
- return TRUE;
+ r = (char_u *)skipwhite((char *)p + len);
+ if (ignore != NULL && STRNCMP(r, ignore, STRLEN(ignore)) == 0) {
+ r = (char_u *)skipwhite((char *)r + STRLEN(ignore));
+ }
+ if (cin_nocode(r)) {
+ return true;
+ }
+ }
+ if (*p != NUL) {
+ p++;
}
- if (*p != NUL)
- ++p;
}
return FALSE;
}
@@ -1329,7 +1377,7 @@ static int cin_ends_in(char_u *s, char_u *find, char_u *ignore)
/*
* Return TRUE when "s" starts with "word" and then a non-ID character.
*/
-static int cin_starts_with(char_u *s, char *word)
+static int cin_starts_with(const char_u *s, const char *word)
{
int l = (int)STRLEN(word);
@@ -1337,17 +1385,17 @@ static int cin_starts_with(char_u *s, char *word)
}
/// Recognize a `extern "C"` or `extern "C++"` linkage specifications.
-static int cin_is_cpp_extern_c(char_u *s)
+static int cin_is_cpp_extern_c(const char_u *s)
{
- char_u *p;
- int has_string_literal = false;
+ const char_u *p;
+ int has_string_literal = false;
s = cin_skipcomment(s);
if (STRNCMP(s, "extern", 6) == 0 && (s[6] == NUL || !vim_iswordc(s[6]))) {
- p = cin_skipcomment(skipwhite(s + 6));
+ p = cin_skipcomment((char_u *)skipwhite((char *)s + 6));
while (*p != NUL) {
if (ascii_iswhite(*p)) {
- p = cin_skipcomment(skipwhite(p));
+ p = cin_skipcomment((char_u *)skipwhite((char *)p));
} else if (*p == '{') {
break;
} else if (p[0] == '"' && p[1] == 'C' && p[2] == '"') {
@@ -1372,16 +1420,15 @@ static int cin_is_cpp_extern_c(char_u *s)
return false;
}
-
/*
* Skip strings, chars and comments until at or past "trypos".
* Return the column found.
*/
static int cin_skip2pos(pos_T *trypos)
{
- char_u *line;
- char_u *p;
- char_u *new_p;
+ const char_u *line;
+ const char_u *p;
+ const char_u *new_p;
p = line = ml_get(trypos->lnum);
while (*p && (colnr_T)(p - line) < trypos->col) {
@@ -1411,8 +1458,8 @@ static int cin_skip2pos(pos_T *trypos)
static pos_T *find_start_brace(void) // XXX
{
pos_T cursor_save;
- pos_T *trypos;
- pos_T *pos;
+ pos_T *trypos;
+ pos_T *pos;
static pos_T pos_copy;
cursor_save = curwin->w_cursor;
@@ -1427,7 +1474,7 @@ static pos_T *find_start_brace(void) // XXX
break;
}
if (pos != NULL) {
- curwin->w_cursor.lnum = pos->lnum;
+ curwin->w_cursor = *pos;
}
}
curwin->w_cursor = cursor_save;
@@ -1441,10 +1488,10 @@ static pos_T *find_match_paren(int ind_maxparen)
return find_match_char('(', ind_maxparen);
}
-static pos_T * find_match_char(char_u c, int ind_maxparen)
+static pos_T *find_match_char(char_u c, int ind_maxparen)
{
pos_T cursor_save;
- pos_T *trypos;
+ pos_T *trypos;
static pos_T pos_copy;
int ind_maxp_wk;
@@ -1454,7 +1501,7 @@ retry:
if ((trypos = findmatchlimit(NULL, c, 0, ind_maxp_wk)) != NULL) {
// check if the ( is in a // comment
if ((colnr_T)cin_skip2pos(trypos) > trypos->col) {
- ind_maxp_wk = ind_maxparen - (int)(cursor_save.lnum - trypos->lnum);
+ ind_maxp_wk = ind_maxparen - (cursor_save.lnum - trypos->lnum);
if (ind_maxp_wk > 0) {
curwin->w_cursor = *trypos;
curwin->w_cursor.col = 0; // XXX
@@ -1468,8 +1515,7 @@ retry:
trypos = &pos_copy;
curwin->w_cursor = *trypos;
if ((trypos_wk = ind_find_start_CORS(NULL)) != NULL) { // XXX
- ind_maxp_wk = ind_maxparen - (int)(cursor_save.lnum
- - trypos_wk->lnum);
+ ind_maxp_wk = ind_maxparen - (cursor_save.lnum - trypos_wk->lnum);
if (ind_maxp_wk > 0) {
curwin->w_cursor = *trypos_wk;
goto retry;
@@ -1504,7 +1550,6 @@ static pos_T *find_match_paren_after_brace(int ind_maxparen)
return trypos;
}
-
/*
* Return ind_maxparen corrected for the difference in line number between the
* cursor position and "startpos". This makes sure that searching for a
@@ -1515,8 +1560,9 @@ static int corr_ind_maxparen(pos_T *startpos)
{
long n = (long)startpos->lnum - (long)curwin->w_cursor.lnum;
- if (n > 0 && n < curbuf->b_ind_maxparen / 2)
+ if (n > 0 && n < curbuf->b_ind_maxparen / 2) {
return curbuf->b_ind_maxparen - (int)n;
+ }
return curbuf->b_ind_maxparen;
}
@@ -1524,7 +1570,7 @@ static int corr_ind_maxparen(pos_T *startpos)
* Set w_cursor.col to the column number of the last unmatched ')' or '{' in
* line "l". "l" must point to the start of the line.
*/
-static int find_last_paren(char_u *l, int start, int end)
+static int find_last_paren(const char_u *l, int start, int end)
{
int i;
int retval = FALSE;
@@ -1555,8 +1601,8 @@ static int find_last_paren(char_u *l, int start, int end)
*/
void parse_cino(buf_T *buf)
{
- char_u *p;
- char_u *l;
+ char_u *p;
+ char_u *l;
int divider;
int fraction = 0;
int sw = get_sw_value(buf);
@@ -1694,13 +1740,13 @@ void parse_cino(buf_T *buf)
// Handle C #pragma directives
buf->b_ind_pragma = 0;
- for (p = buf->b_p_cino; *p; ) {
+ for (p = buf->b_p_cino; *p;) {
l = p++;
if (*p == '-') {
p++;
}
char_u *digits_start = p; // remember where the digits start
- int n = getdigits_int(&p, true, 0);
+ int n = getdigits_int((char **)&p, true, 0);
divider = 0;
if (*p == '.') { // ".5s" means a fraction.
fraction = atoi((char *)++p);
@@ -1718,57 +1764,134 @@ void parse_cino(buf_T *buf)
n = sw; // just "s" is one 'shiftwidth'.
} else {
n *= sw;
- if (divider)
+ if (divider) {
n += (sw * fraction + divider / 2) / divider;
+ }
}
++p;
}
- if (l[1] == '-')
+ if (l[1] == '-') {
n = -n;
+ }
/* When adding an entry here, also update the default 'cinoptions' in
* doc/indent.txt, and add explanation for it! */
switch (*l) {
- case '>': buf->b_ind_level = n; break;
- case 'e': buf->b_ind_open_imag = n; break;
- case 'n': buf->b_ind_no_brace = n; break;
- case 'f': buf->b_ind_first_open = n; break;
- case '{': buf->b_ind_open_extra = n; break;
- case '}': buf->b_ind_close_extra = n; break;
- case '^': buf->b_ind_open_left_imag = n; break;
- case 'L': buf->b_ind_jump_label = n; break;
- case ':': buf->b_ind_case = n; break;
- case '=': buf->b_ind_case_code = n; break;
- case 'b': buf->b_ind_case_break = n; break;
- case 'p': buf->b_ind_param = n; break;
- case 't': buf->b_ind_func_type = n; break;
- case '/': buf->b_ind_comment = n; break;
- case 'c': buf->b_ind_in_comment = n; break;
- case 'C': buf->b_ind_in_comment2 = n; break;
- case 'i': buf->b_ind_cpp_baseclass = n; break;
- case '+': buf->b_ind_continuation = n; break;
- case '(': buf->b_ind_unclosed = n; break;
- case 'u': buf->b_ind_unclosed2 = n; break;
- case 'U': buf->b_ind_unclosed_noignore = n; break;
- case 'W': buf->b_ind_unclosed_wrapped = n; break;
- case 'w': buf->b_ind_unclosed_whiteok = n; break;
- case 'm': buf->b_ind_matching_paren = n; break;
- case 'M': buf->b_ind_paren_prev = n; break;
- case ')': buf->b_ind_maxparen = n; break;
- case '*': buf->b_ind_maxcomment = n; break;
- case 'g': buf->b_ind_scopedecl = n; break;
- case 'h': buf->b_ind_scopedecl_code = n; break;
- case 'j': buf->b_ind_java = n; break;
- case 'J': buf->b_ind_js = n; break;
- case 'l': buf->b_ind_keep_case_label = n; break;
- case '#': buf->b_ind_hash_comment = n; break;
- case 'N': buf->b_ind_cpp_namespace = n; break;
- case 'k': buf->b_ind_if_for_while = n; break;
- case 'E': buf->b_ind_cpp_extern_c = n; break;
- case 'P': buf->b_ind_pragma = n; break;
- }
- if (*p == ',')
- ++p;
+ case '>':
+ buf->b_ind_level = n;
+ break;
+ case 'e':
+ buf->b_ind_open_imag = n;
+ break;
+ case 'n':
+ buf->b_ind_no_brace = n;
+ break;
+ case 'f':
+ buf->b_ind_first_open = n;
+ break;
+ case '{':
+ buf->b_ind_open_extra = n;
+ break;
+ case '}':
+ buf->b_ind_close_extra = n;
+ break;
+ case '^':
+ buf->b_ind_open_left_imag = n;
+ break;
+ case 'L':
+ buf->b_ind_jump_label = n;
+ break;
+ case ':':
+ buf->b_ind_case = n;
+ break;
+ case '=':
+ buf->b_ind_case_code = n;
+ break;
+ case 'b':
+ buf->b_ind_case_break = n;
+ break;
+ case 'p':
+ buf->b_ind_param = n;
+ break;
+ case 't':
+ buf->b_ind_func_type = n;
+ break;
+ case '/':
+ buf->b_ind_comment = n;
+ break;
+ case 'c':
+ buf->b_ind_in_comment = n;
+ break;
+ case 'C':
+ buf->b_ind_in_comment2 = n;
+ break;
+ case 'i':
+ buf->b_ind_cpp_baseclass = n;
+ break;
+ case '+':
+ buf->b_ind_continuation = n;
+ break;
+ case '(':
+ buf->b_ind_unclosed = n;
+ break;
+ case 'u':
+ buf->b_ind_unclosed2 = n;
+ break;
+ case 'U':
+ buf->b_ind_unclosed_noignore = n;
+ break;
+ case 'W':
+ buf->b_ind_unclosed_wrapped = n;
+ break;
+ case 'w':
+ buf->b_ind_unclosed_whiteok = n;
+ break;
+ case 'm':
+ buf->b_ind_matching_paren = n;
+ break;
+ case 'M':
+ buf->b_ind_paren_prev = n;
+ break;
+ case ')':
+ buf->b_ind_maxparen = n;
+ break;
+ case '*':
+ buf->b_ind_maxcomment = n;
+ break;
+ case 'g':
+ buf->b_ind_scopedecl = n;
+ break;
+ case 'h':
+ buf->b_ind_scopedecl_code = n;
+ break;
+ case 'j':
+ buf->b_ind_java = n;
+ break;
+ case 'J':
+ buf->b_ind_js = n;
+ break;
+ case 'l':
+ buf->b_ind_keep_case_label = n;
+ break;
+ case '#':
+ buf->b_ind_hash_comment = n;
+ break;
+ case 'N':
+ buf->b_ind_cpp_namespace = n;
+ break;
+ case 'k':
+ buf->b_ind_if_for_while = n;
+ break;
+ case 'E':
+ buf->b_ind_cpp_extern_c = n;
+ break;
+ case 'P':
+ buf->b_ind_pragma = n;
+ break;
+ }
+ if (*p == ',') {
+ p++;
+ }
}
}
@@ -1783,21 +1906,21 @@ int get_c_indent(void)
int scope_amount;
int cur_amount = MAXCOL;
colnr_T col;
- char_u *theline;
- char_u *linecopy;
- pos_T *trypos;
- pos_T *comment_pos;
- pos_T *tryposBrace = NULL;
- pos_T tryposCopy;
+ char_u *theline;
+ char_u *linecopy;
+ pos_T *trypos;
+ pos_T *comment_pos;
+ pos_T *tryposBrace = NULL;
+ pos_T tryposCopy;
pos_T our_paren_pos;
- char_u *start;
+ char_u *start;
int start_brace;
#define BRACE_IN_COL0 1 // '{' is in column 0
#define BRACE_AT_START 2 // '{' is at start of line
#define BRACE_AT_END 3 // '{' is at end of line
linenr_T ourscope;
- char_u *l;
- char_u *look;
+ const char_u *l;
+ const char_u *look;
char_u terminated;
int lookfor;
#define LOOKFOR_INITIAL 0
@@ -1848,12 +1971,13 @@ int get_c_indent(void)
* For unknown reasons the cursor might be past the end of the line, thus
* check for that.
*/
- if ((State & INSERT)
+ if ((State & MODE_INSERT)
&& curwin->w_cursor.col < (colnr_T)STRLEN(linecopy)
- && linecopy[curwin->w_cursor.col] == ')')
+ && linecopy[curwin->w_cursor.col] == ')') {
linecopy[curwin->w_cursor.col] = NUL;
+ }
- theline = skipwhite(linecopy);
+ theline = (char_u *)skipwhite((char *)linecopy);
// move the cursor to the start of the line
@@ -1880,20 +2004,17 @@ int get_c_indent(void)
// #defines and so on go at the left when included in 'cinkeys',
// exluding pragmas when customized in 'cinoptions'
if (*theline == '#' && (*linecopy == '#' || in_cinkeys('#', ' ', true))) {
- const char_u *const directive = skipwhite(theline + 1);
+ const char_u *const directive = (char_u *)skipwhite((char *)theline + 1);
if (curbuf->b_ind_pragma == 0 || STRNCMP(directive, "pragma", 6) != 0) {
amount = curbuf->b_ind_hash_comment;
goto theend;
}
}
- /*
- * Is it a non-case label? Then that goes at the left margin too unless:
- * - JS flag is set.
- * - 'L' item has a positive value.
- */
- if (original_line_islabel && !curbuf->b_ind_js
- && curbuf->b_ind_jump_label < 0) {
+ // Is it a non-case label? Then that goes at the left margin too unless:
+ // - JS flag is set.
+ // - 'L' item has a positive value.
+ if (original_line_islabel && !curbuf->b_ind_js && curbuf->b_ind_jump_label < 0) {
amount = 0;
goto theend;
}
@@ -1901,12 +2022,25 @@ int get_c_indent(void)
* If we're inside a "//" comment and there is a "//" comment in a
* previous line, lineup with that one.
*/
- if (cin_islinecomment(theline)
- && (trypos = find_line_comment()) != NULL) { // XXX
- // find how indented the line beginning the comment is
- getvcol(curwin, trypos, &col, NULL, NULL);
- amount = col;
- goto theend;
+ if (cin_islinecomment(theline)) {
+ pos_T linecomment_pos;
+
+ trypos = find_line_comment(); // XXX
+ if (trypos == NULL && curwin->w_cursor.lnum > 1) {
+ // There may be a statement before the comment, search from the end
+ // of the line for a comment start.
+ linecomment_pos.col = check_linecomment(ml_get(curwin->w_cursor.lnum - 1));
+ if (linecomment_pos.col != MAXCOL) {
+ trypos = &linecomment_pos;
+ trypos->lnum = curwin->w_cursor.lnum - 1;
+ }
+ }
+ if (trypos != NULL) {
+ // find how indented the line beginning the comment is
+ getvcol(curwin, trypos, &col, NULL, NULL);
+ amount = col;
+ goto theend;
+ }
}
/*
* If we're inside a comment and not looking at the start of the
@@ -1915,10 +2049,10 @@ int get_c_indent(void)
if (!cin_iscomment(theline) && comment_pos != NULL) { // XXX
int lead_start_len = 2;
int lead_middle_len = 1;
- char_u lead_start[COM_MAX_LEN]; // start-comment string
- char_u lead_middle[COM_MAX_LEN]; // middle-comment string
- char_u lead_end[COM_MAX_LEN]; // end-comment string
- char_u *p;
+ char lead_start[COM_MAX_LEN]; // start-comment string
+ char lead_middle[COM_MAX_LEN]; // middle-comment string
+ char lead_end[COM_MAX_LEN]; // end-comment string
+ char_u *p;
int start_align = 0;
int start_off = 0;
int done = FALSE;
@@ -1941,15 +2075,16 @@ int get_c_indent(void)
} else if (*p == COM_LEFT || *p == COM_RIGHT) {
align = *p++;
} else if (ascii_isdigit(*p) || *p == '-') {
- off = getdigits_int(&p, true, 0);
+ off = getdigits_int((char **)&p, true, 0);
} else {
p++;
}
}
- if (*p == ':')
- ++p;
- (void)copy_option_part(&p, lead_end, COM_MAX_LEN, ",");
+ if (*p == ':') {
+ p++;
+ }
+ (void)copy_option_part((char **)&p, lead_end, COM_MAX_LEN, ",");
if (what == COM_START) {
STRCPY(lead_start, lead_end);
lead_start_len = (int)STRLEN(lead_start);
@@ -1969,25 +2104,24 @@ int get_c_indent(void)
* line, use the indent of that line plus offset. If
* the middle comment string matches in the previous
* line, use the indent of that line. XXX */
- look = skipwhite(ml_get(curwin->w_cursor.lnum - 1));
- if (STRNCMP(look, lead_start, lead_start_len) == 0)
+ look = (char_u *)skipwhite((char *)ml_get(curwin->w_cursor.lnum - 1));
+ if (STRNCMP(look, lead_start, lead_start_len) == 0) {
amount = get_indent_lnum(curwin->w_cursor.lnum - 1);
- else if (STRNCMP(look, lead_middle,
- lead_middle_len) == 0) {
+ } else if (STRNCMP(look, lead_middle, lead_middle_len) == 0) {
amount = get_indent_lnum(curwin->w_cursor.lnum - 1);
break;
} else if (STRNCMP(ml_get(comment_pos->lnum) + comment_pos->col,
- lead_start, lead_start_len) != 0) {
- /* If the start comment string doesn't match with the
- * start of the comment, skip this entry. XXX */
+ lead_start, lead_start_len) != 0) {
+ // If the start comment string doesn't match with the
+ // start of the comment, skip this entry. XXX
continue;
}
}
- if (start_off != 0)
+ if (start_off != 0) {
amount += start_off;
- else if (start_align == COM_RIGHT)
- amount += vim_strsize(lead_start)
- - vim_strsize(lead_middle);
+ } else if (start_align == COM_RIGHT) {
+ amount += vim_strsize(lead_start) - vim_strsize(lead_middle);
+ }
break;
}
@@ -2012,18 +2146,16 @@ int get_c_indent(void)
* asterisk in the comment opener; otherwise, line up
* with the first character of the comment text.
*/
- if (done)
- ;
- else if (theline[0] == '*')
+ if (done) {
+ // skip
+ } else if (theline[0] == '*') {
amount += 1;
- else {
- /*
- * If we are more than one line away from the comment opener, take
- * the indent of the previous non-empty line. If 'cino' has "CO"
- * and we are just below the comment opener and there are any
- * white characters after it line up with the text after it;
- * otherwise, add the amount specified by "c" in 'cino'
- */
+ } else {
+ // If we are more than one line away from the comment opener, take
+ // the indent of the previous non-empty line. If 'cino' has "CO"
+ // and we are just below the comment opener and there are any
+ // white characters after it line up with the text after it;
+ // otherwise, add the amount specified by "c" in 'cino'
amount = -1;
for (lnum = cur_curpos.lnum - 1; lnum > comment_pos->lnum; lnum--) {
if (linewhite(lnum)) { // skip blank lines
@@ -2037,20 +2169,21 @@ int get_c_indent(void)
start = ml_get(comment_pos->lnum);
look = start + comment_pos->col + 2; // skip / and *
if (*look != NUL) { // if something after it
- comment_pos->col = (colnr_T)(skipwhite(look) - start);
+ comment_pos->col = (colnr_T)((char_u *)skipwhite((char *)look) - start);
}
}
getvcol(curwin, comment_pos, &col, NULL, NULL);
amount = col;
- if (curbuf->b_ind_in_comment2 || *look == NUL)
+ if (curbuf->b_ind_in_comment2 || *look == NUL) {
amount += curbuf->b_ind_in_comment;
+ }
}
}
- goto theend;
+ goto theend;
}
// Are we looking at a ']' that has a match?
- if (*skipwhite(theline) == ']'
- && (trypos = find_match_char('[', curbuf->b_ind_maxparen)) != NULL) {
+ if (*skipwhite((char *)theline) == ']'
+ && (trypos = find_match_char('[', curbuf->b_ind_maxparen)) != NULL) {
// align with the line containing the '['.
amount = get_indent_lnum(trypos->lnum);
goto theend;
@@ -2058,18 +2191,19 @@ int get_c_indent(void)
// Are we inside parentheses or braces?
// XXX
if (((trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL
- && curbuf->b_ind_java == 0)
- || (tryposBrace = find_start_brace()) != NULL
- || trypos != NULL) {
+ && curbuf->b_ind_java == 0)
+ || (tryposBrace = find_start_brace()) != NULL
+ || trypos != NULL) {
if (trypos != NULL && tryposBrace != NULL) {
/* Both an unmatched '(' and '{' is found. Use the one which is
* closer to the current cursor position, set the other to NULL. */
if (trypos->lnum != tryposBrace->lnum
? trypos->lnum < tryposBrace->lnum
- : trypos->col < tryposBrace->col)
+ : trypos->col < tryposBrace->col) {
trypos = NULL;
- else
+ } else {
tryposBrace = NULL;
+ }
}
if (trypos != NULL) {
@@ -2083,8 +2217,8 @@ int get_c_indent(void)
amount = get_indent_lnum(curwin->w_cursor.lnum - 1); // XXX
} else {
amount = -1;
- for (lnum = cur_curpos.lnum - 1; lnum > our_paren_pos.lnum; --lnum) {
- l = skipwhite(ml_get(lnum));
+ for (lnum = cur_curpos.lnum - 1; lnum > our_paren_pos.lnum; lnum--) {
+ l = (char_u *)skipwhite((char *)ml_get(lnum));
if (cin_nocode(l)) { // skip comment lines
continue;
}
@@ -2100,16 +2234,16 @@ int get_c_indent(void)
}
// XXX
- if ((trypos = find_match_paren(
- corr_ind_maxparen(&cur_curpos))) != NULL
+ if ((trypos = find_match_paren(corr_ind_maxparen(&cur_curpos))) != NULL
&& trypos->lnum == our_paren_pos.lnum
&& trypos->col == our_paren_pos.col) {
amount = get_indent_lnum(lnum); // XXX
if (theline[0] == ')') {
if (our_paren_pos.lnum != lnum
- && cur_amount > amount)
+ && cur_amount > amount) {
cur_amount = amount;
+ }
amount = -1;
}
break;
@@ -2132,7 +2266,7 @@ int get_c_indent(void)
pos_T cursor_save = curwin->w_cursor;
pos_T outermost;
- char_u *line;
+ char_u *line;
trypos = &our_paren_pos;
do {
@@ -2152,10 +2286,10 @@ int get_c_indent(void)
}
amount = skip_label(our_paren_pos.lnum, &look);
- look = skipwhite(look);
+ look = (char_u *)skipwhite((char *)look);
if (*look == '(') {
linenr_T save_lnum = curwin->w_cursor.lnum;
- char_u *line;
+ char_u *line;
int look_col;
/* Ignore a '(' in front of the line that has a match before
@@ -2165,11 +2299,12 @@ int get_c_indent(void)
look_col = (int)(look - line);
curwin->w_cursor.col = look_col + 1;
if ((trypos = findmatchlimit(NULL, ')', 0,
- curbuf->b_ind_maxparen))
+ curbuf->b_ind_maxparen))
!= NULL
&& trypos->lnum == our_paren_pos.lnum
- && trypos->col < our_paren_pos.col)
+ && trypos->col < our_paren_pos.col) {
ignore_paren_col = trypos->col + 1;
+ }
curwin->w_cursor.lnum = save_lnum;
look = ml_get(our_paren_pos.lnum) + look_col;
@@ -2198,24 +2333,28 @@ int get_c_indent(void)
for (col = 0; col < our_paren_pos.col; ++col) {
switch (l[col]) {
case '(':
- case '{': ++n;
+ case '{':
+ n++;
break;
case ')':
- case '}': if (n > 1)
- --n;
+ case '}':
+ if (n > 1) {
+ n--;
+ }
break;
}
}
our_paren_pos.col = 0;
amount += n * curbuf->b_ind_unclosed_wrapped;
- } else if (curbuf->b_ind_unclosed_whiteok)
+ } else if (curbuf->b_ind_unclosed_whiteok) {
our_paren_pos.col++;
- else {
+ } else {
col = our_paren_pos.col + 1;
- while (ascii_iswhite(l[col]))
+ while (ascii_iswhite(l[col])) {
col++;
+ }
if (l[col] != NUL) { // In case of trailing space
our_paren_pos.col = col;
} else {
@@ -2230,8 +2369,9 @@ int get_c_indent(void)
*/
if (our_paren_pos.col > 0) {
getvcol(curwin, &our_paren_pos, &col, NULL, NULL);
- if (cur_amount > (int)col)
+ if (cur_amount > (int)col) {
cur_amount = col;
+ }
}
}
@@ -2240,8 +2380,9 @@ int get_c_indent(void)
} else if ((curbuf->b_ind_unclosed == 0 && is_if_for_while == 0)
|| (!curbuf->b_ind_unclosed_noignore
&& *look == '(' && ignore_paren_col == 0)) {
- if (cur_amount != MAXCOL)
+ if (cur_amount != MAXCOL) {
amount = cur_amount;
+ }
} else {
/* Add b_ind_unclosed2 for each '(' before our matching one,
* but ignore (void) before the line (ignore_paren_col). */
@@ -2249,10 +2390,12 @@ int get_c_indent(void)
while ((int)our_paren_pos.col > ignore_paren_col) {
--our_paren_pos.col;
switch (*ml_get_pos(&our_paren_pos)) {
- case '(': amount += curbuf->b_ind_unclosed2;
+ case '(':
+ amount += curbuf->b_ind_unclosed2;
col = our_paren_pos.col;
break;
- case ')': amount -= curbuf->b_ind_unclosed2;
+ case ')':
+ amount -= curbuf->b_ind_unclosed2;
col = MAXCOL;
break;
}
@@ -2260,9 +2403,9 @@ int get_c_indent(void)
/* Use b_ind_unclosed once, when the first '(' is not inside
* braces */
- if (col == MAXCOL)
+ if (col == MAXCOL) {
amount += curbuf->b_ind_unclosed;
- else {
+ } else {
curwin->w_cursor.lnum = our_paren_pos.lnum;
curwin->w_cursor.col = col;
if (find_match_paren_after_brace(curbuf->b_ind_maxparen)) {
@@ -2279,12 +2422,13 @@ int get_c_indent(void)
* For a line starting with ')' use the minimum of the two
* positions, to avoid giving it more indent than the previous
* lines:
- * func_long_name( if (x
- * arg && yy
- * ) ^ not here ) ^ not here
+ * func_long_name( if (x
+ * arg && yy
+ * ) ^ not here ) ^ not here
*/
- if (cur_amount < amount)
+ if (cur_amount < amount) {
amount = cur_amount;
+ }
}
}
@@ -2309,14 +2453,15 @@ int get_c_indent(void)
* otherwise, check out the indentation of the line as
* a whole and then add the "imaginary indent" to that.
*/
- look = skipwhite(start);
+ look = (char_u *)skipwhite((char *)start);
if (*look == '{') {
getvcol(curwin, trypos, &col, NULL, NULL);
amount = col;
- if (*start == '{')
+ if (*start == '{') {
start_brace = BRACE_IN_COL0;
- else
+ } else {
start_brace = BRACE_AT_START;
+ }
} else {
// That opening brace might have been on a continuation
// line. If so, find the start of the line.
@@ -2331,11 +2476,11 @@ int get_c_indent(void)
}
// It could have been something like
- // case 1: if (asdf &&
- // ldfd) {
- // }
+ // case 1: if (asdf &&
+ // ldfd) {
+ // }
if ((curbuf->b_ind_js || curbuf->b_ind_keep_case_label)
- && cin_iscase(skipwhite(get_cursor_line_ptr()), false)) {
+ && cin_iscase((char_u *)skipwhite((char *)get_cursor_line_ptr()), false)) {
amount = get_indent();
} else if (curbuf->b_ind_js) {
amount = get_indent_lnum(lnum);
@@ -2402,7 +2547,7 @@ int get_c_indent(void)
if (start_brace == BRACE_AT_END) { // '{' is at end of line
amount += curbuf->b_ind_open_imag;
- l = skipwhite(get_cursor_line_ptr());
+ l = (char_u *)skipwhite((char *)get_cursor_line_ptr());
if (cin_is_cpp_namespace(l)) {
amount += curbuf->b_ind_cpp_namespace;
} else if (cin_is_cpp_extern_c(l)) {
@@ -2411,8 +2556,9 @@ int get_c_indent(void)
} else {
// Compensate for adding b_ind_open_extra later.
amount -= curbuf->b_ind_open_extra;
- if (amount < 0)
+ if (amount < 0) {
amount = 0;
+ }
}
}
@@ -2444,7 +2590,7 @@ int get_c_indent(void)
// the usual amount relative to the conditional
// that opens the block.
curwin->w_cursor = cur_curpos;
- for (;; ) {
+ for (;;) {
curwin->w_cursor.lnum--;
curwin->w_cursor.col = 0;
@@ -2468,10 +2614,11 @@ int get_c_indent(void)
/* nothing found (abuse curbuf->b_ind_maxparen as
* limit) assume terminated line (i.e. a variable
* initialization) */
- if (cont_amount > 0)
+ if (cont_amount > 0) {
amount = cont_amount;
- else if (!curbuf->b_ind_js)
+ } else if (!curbuf->b_ind_js) {
amount += ind_continuation;
+ }
break;
}
@@ -2495,8 +2642,9 @@ int get_c_indent(void)
continue;
}
- if (cin_nocode(l))
+ if (cin_nocode(l)) {
continue;
+ }
terminated = cin_isterminated(l, FALSE, TRUE);
@@ -2514,14 +2662,16 @@ int get_c_indent(void)
* declaration is split over multiple lines:
* cin_isfuncdecl returns FALSE then.
*/
- if (terminated == ',')
+ if (terminated == ',') {
break;
+ }
/* if it is an enum declaration or an assignment,
* we are done.
*/
- if (terminated != ';' && cin_isinit())
+ if (terminated != ';' && cin_isinit()) {
break;
+ }
// nothing useful found
if (terminated == 0 || terminated == '{') {
@@ -2535,12 +2685,13 @@ int get_c_indent(void)
// will take us back to the start of the line.
// XXX
trypos = NULL;
- if (find_last_paren(l, '(', ')'))
- trypos = find_match_paren(
- curbuf->b_ind_maxparen);
+ if (find_last_paren(l, '(', ')')) {
+ trypos = find_match_paren(curbuf->b_ind_maxparen);
+ }
- if (trypos == NULL && find_last_paren(l, '{', '}'))
+ if (trypos == NULL && find_last_paren(l, '{', '}')) {
trypos = find_start_brace();
+ }
if (trypos != NULL) {
curwin->w_cursor.lnum = trypos->lnum + 1;
@@ -2554,15 +2705,17 @@ int get_c_indent(void)
* int a,
* b;
*/
- if (cont_amount > 0)
+ if (cont_amount > 0) {
amount = cont_amount;
- else
+ } else {
amount += ind_continuation;
+ }
} else if (lookfor == LOOKFOR_UNTERM) {
- if (cont_amount > 0)
+ if (cont_amount > 0) {
amount = cont_amount;
- else
+ } else {
amount += ind_continuation;
+ }
} else {
if (lookfor != LOOKFOR_TERM
&& lookfor != LOOKFOR_CPP_BASECLASS
@@ -2579,13 +2732,15 @@ int get_c_indent(void)
* Looking for C++ namespace, need to look further
* back.
*/
- if (curwin->w_cursor.lnum == ourscope)
+ if (curwin->w_cursor.lnum == ourscope) {
continue;
+ }
if (curwin->w_cursor.lnum == 0
|| curwin->w_cursor.lnum
- < ourscope - FIND_NAMESPACE_LIM)
+ < ourscope - FIND_NAMESPACE_LIM) {
break;
+ }
l = get_cursor_line_ptr();
@@ -2613,8 +2768,9 @@ int get_c_indent(void)
break;
}
- if (cin_nocode(l))
+ if (cin_nocode(l)) {
continue;
+ }
}
}
break;
@@ -2639,25 +2795,25 @@ int get_c_indent(void)
if (iscase || cin_isscopedecl(l)) {
/* we are only looking for cpp base class
* declaration/initialization any longer */
- if (lookfor == LOOKFOR_CPP_BASECLASS)
+ if (lookfor == LOOKFOR_CPP_BASECLASS) {
break;
+ }
/* When looking for a "do" we are not interested in
* labels. */
- if (whilelevel > 0)
+ if (whilelevel > 0) {
continue;
+ }
- /*
- * case xx:
- * c = 99 + <- this indent plus continuation
- **-> here;
- */
- if (lookfor == LOOKFOR_UNTERM
- || lookfor == LOOKFOR_ENUM_OR_INIT) {
- if (cont_amount > 0)
+ // case xx:
+ // c = 99 + <- this indent plus continuation
+ // -> here;
+ if (lookfor == LOOKFOR_UNTERM || lookfor == LOOKFOR_ENUM_OR_INIT) {
+ if (cont_amount > 0) {
amount = cont_amount;
- else
+ } else {
amount += ind_continuation;
+ }
break;
}
@@ -2680,40 +2836,39 @@ int get_c_indent(void)
n = get_indent_nolabel(curwin->w_cursor.lnum); // XXX
- /*
- * case xx: if (cond) <- line up with this if
- * y = y + 1;
- * -> s = 99;
- *
- * case xx:
- * if (cond) <- line up with this line
- * y = y + 1;
- * -> s = 99;
- */
+ // case xx: if (cond) <- line up with this if
+ // y = y + 1;
+ // -> s = 99;
+ //
+ // case xx:
+ // if (cond) <- line up with this line
+ // y = y + 1;
+ // -> s = 99;
if (lookfor == LOOKFOR_TERM) {
- if (n)
+ if (n) {
amount = n;
+ }
- if (!lookfor_break)
+ if (!lookfor_break) {
break;
+ }
}
- /*
- * case xx: x = x + 1; <- line up with this x
- * -> y = y + 1;
- *
- * case xx: if (cond) <- line up with this if
- * -> y = y + 1;
- */
+ // case xx: x = x + 1; <- line up with this x
+ // -> y = y + 1;
+ //
+ // case xx: if (cond) <- line up with this if
+ // -> y = y + 1;
if (n) {
amount = n;
l = after_label(get_cursor_line_ptr());
if (l != NULL && cin_is_cinword(l)) {
- if (theline[0] == '{')
+ if (theline[0] == '{') {
amount += curbuf->b_ind_open_extra;
- else
+ } else {
amount += curbuf->b_ind_level
+ curbuf->b_ind_no_brace;
+ }
}
break;
}
@@ -2722,8 +2877,8 @@ int get_c_indent(void)
* Try to get the indent of a statement before the switch
* label. If nothing is found, line up relative to the
* switch label.
- * break; <- may line up with this line
- * case xx:
+ * break; <- may line up with this line
+ * case xx:
* -> y = 1;
*/
scope_amount = get_indent() + (iscase // XXX
@@ -2752,8 +2907,9 @@ int get_c_indent(void)
*/
if (!curbuf->b_ind_js && cin_islabel()) {
l = after_label(get_cursor_line_ptr());
- if (l == NULL || cin_nocode(l))
+ if (l == NULL || cin_nocode(l)) {
continue;
+ }
}
/*
@@ -2778,10 +2934,11 @@ int get_c_indent(void)
}
if (n) {
if (lookfor == LOOKFOR_UNTERM) {
- if (cont_amount > 0)
+ if (cont_amount > 0) {
amount = cont_amount;
- else
+ } else {
amount += ind_continuation;
+ }
} else if (theline[0] == '{') {
// Need to find start of the declaration.
lookfor = LOOKFOR_UNTERM;
@@ -2796,10 +2953,11 @@ int get_c_indent(void)
/* only look, whether there is a cpp base class
* declaration or initialization before the opening brace.
*/
- if (cin_isterminated(l, TRUE, FALSE))
+ if (cin_isterminated(l, true, false)) {
break;
- else
+ } else {
continue;
+ }
}
/*
@@ -2808,7 +2966,7 @@ int get_c_indent(void)
* there is another unterminated statement behind, eg:
* 123,
* sizeof
- * here
+ * here
* Otherwise check whether it is an enumeration or structure
* initialisation (not indented) or a variable declaration
* (indented).
@@ -2816,7 +2974,7 @@ int get_c_indent(void)
terminated = cin_isterminated(l, FALSE, TRUE);
if (js_cur_has_key) {
- js_cur_has_key = false; // only check the first line
+ js_cur_has_key = false; // only check the first line
if (curbuf->b_ind_js && terminated == ',') {
// For Javascript we might be inside an object:
// key: something, <- align with this
@@ -2855,13 +3013,13 @@ int get_c_indent(void)
if (terminated == 0 || (lookfor != LOOKFOR_UNTERM
&& terminated == ',')) {
if (lookfor != LOOKFOR_ENUM_OR_INIT
- && (*skipwhite(l) == '[' || l[STRLEN(l) - 1] == '[')) {
+ && (*skipwhite((char *)l) == '[' || l[STRLEN(l) - 1] == '[')) {
amount += ind_continuation;
}
// If we're in the middle of a paren thing, Go back to the line
// that starts it so we can get the right prevailing indent
- // if ( foo &&
- // bar )
+ // if ( foo &&
+ // bar )
// Position the cursor over the rightmost paren, so that
// matching it will take us back to the start of the line.
@@ -2877,15 +3035,16 @@ int get_c_indent(void)
// If we are looking for ',', we also look for matching
// braces.
if (trypos == NULL && terminated == ','
- && find_last_paren(l, '{', '}'))
+ && find_last_paren(l, '{', '}')) {
trypos = find_start_brace();
+ }
if (trypos != NULL) {
/*
* Check if we are on a case label now. This is
* handled above.
* case xx: if ( asdf &&
- * asdf)
+ * asdf)
*/
curwin->w_cursor = *trypos;
l = get_cursor_line_ptr();
@@ -2900,22 +3059,23 @@ int get_c_indent(void)
* Skip over continuation lines to find the one to get the
* indent from
* char *usethis = "bla\
- * bla",
+ * bla",
* here;
*/
if (terminated == ',') {
while (curwin->w_cursor.lnum > 1) {
l = ml_get(curwin->w_cursor.lnum - 1);
- if (*l == NUL || l[STRLEN(l) - 1] != '\\')
+ if (*l == NUL || l[STRLEN(l) - 1] != '\\') {
break;
- --curwin->w_cursor.lnum;
+ }
+ curwin->w_cursor.lnum--;
curwin->w_cursor.col = 0;
}
}
/*
* Get indent and pointer to text for current line,
- * ignoring any jump label. XXX
+ * ignoring any jump label. XXX
*/
if (curbuf->b_ind_js) {
cur_amount = get_indent();
@@ -2925,9 +3085,9 @@ int get_c_indent(void)
/*
* If this is just above the line we are indenting, and it
* starts with a '{', line it up with this line.
- * while (not)
- * -> {
- * }
+ * while (not)
+ * -> {
+ * }
*/
if (terminated != ',' && lookfor != LOOKFOR_TERM
&& theline[0] == '{') {
@@ -2936,11 +3096,12 @@ int get_c_indent(void)
* Only add b_ind_open_extra when the current line
* doesn't start with a '{', which must have a match
* in the same line (scope is the same). Probably:
- * { 1, 2 },
- * -> { 3, 4 }
+ * { 1, 2 },
+ * -> { 3, 4 }
*/
- if (*skipwhite(l) != '{')
+ if (*skipwhite((char *)l) != '{') {
amount += curbuf->b_ind_open_extra;
+ }
if (curbuf->b_ind_cpp_baseclass && !curbuf->b_ind_js) {
/* have to look back, whether it is a cpp base
@@ -2955,39 +3116,39 @@ int get_c_indent(void)
* Check if we are after an "if", "while", etc.
* Also allow " } else".
*/
- if (cin_is_cinword(l) || cin_iselse(skipwhite(l))) {
- /*
- * Found an unterminated line after an if (), line up
- * with the last one.
- * if (cond)
- * 100 +
- * -> here;
- */
+ if (cin_is_cinword(l) || cin_iselse((char_u *)skipwhite((char *)l))) {
+ // Found an unterminated line after an if (), line up
+ // with the last one.
+ // if (cond)
+ // 100 +
+ // -> here;
if (lookfor == LOOKFOR_UNTERM
|| lookfor == LOOKFOR_ENUM_OR_INIT) {
- if (cont_amount > 0)
+ if (cont_amount > 0) {
amount = cont_amount;
- else
+ } else {
amount += ind_continuation;
+ }
break;
}
/*
* If this is just above the line we are indenting, we
* are finished.
- * while (not)
- * -> here;
+ * while (not)
+ * -> here;
* Otherwise this indent can be used when the line
* before this is terminated.
- * yyy;
- * if (stat)
- * while (not)
- * xxx;
- * -> here;
+ * yyy;
+ * if (stat)
+ * while (not)
+ * xxx;
+ * -> here;
*/
amount = cur_amount;
- if (theline[0] == '{')
+ if (theline[0] == '{') {
amount += curbuf->b_ind_open_extra;
+ }
if (lookfor != LOOKFOR_TERM) {
amount += curbuf->b_ind_level
+ curbuf->b_ind_no_brace;
@@ -2998,14 +3159,15 @@ int get_c_indent(void)
* Special trick: when expecting the while () after a
* do, line up with the while()
* do
- * x = 1;
+ * x = 1;
* -> here
*/
- l = skipwhite(get_cursor_line_ptr());
+ l = (char_u *)skipwhite((char *)get_cursor_line_ptr());
if (cin_isdo(l)) {
- if (whilelevel == 0)
+ if (whilelevel == 0) {
break;
- --whilelevel;
+ }
+ whilelevel--;
}
/*
@@ -3018,14 +3180,16 @@ int get_c_indent(void)
/* If we're looking at "} else", let's make sure we
* find the opening brace of the enclosing scope,
* not the one from "if () {". */
- if (*l == '}')
+ if (*l == '}') {
curwin->w_cursor.col =
(colnr_T)(l - get_cursor_line_ptr()) + 1;
+ }
if ((trypos = find_start_brace()) == NULL
|| find_match(LOOKFOR_IF, trypos->lnum)
- == FAIL)
+ == FAIL) {
break;
+ }
}
}
/*
@@ -3039,8 +3203,8 @@ int get_c_indent(void)
* Found two unterminated lines on a row, line up with
* the last one.
* c = 99 +
- * 100 +
- * -> here;
+ * 100 +
+ * -> here;
*/
if (lookfor == LOOKFOR_UNTERM) {
// When line ends in a comma add extra indent
@@ -3057,8 +3221,9 @@ int get_c_indent(void)
* opening brace or we are looking just for
* enumerations/initializations. */
if (terminated == ',') {
- if (curbuf->b_ind_cpp_baseclass == 0)
+ if (curbuf->b_ind_cpp_baseclass == 0) {
break;
+ }
lookfor = LOOKFOR_CPP_BASECLASS;
continue;
@@ -3072,15 +3237,15 @@ int get_c_indent(void)
} else {
// Found first unterminated line on a row, may
// line up with this line, remember its indent
- // 100 + // NOLINT(whitespace/tab)
- // -> here; // NOLINT(whitespace/tab)
+ // 100 + // NOLINT(whitespace/tab)
+ // -> here; // NOLINT(whitespace/tab)
l = get_cursor_line_ptr();
amount = cur_amount;
n = (int)STRLEN(l);
if (terminated == ','
- && (*skipwhite(l) == ']'
- || (n >=2 && l[n - 2] == ']'))) {
+ && (*skipwhite((char *)l) == ']'
+ || (n >= 2 && l[n - 2] == ']'))) {
break;
}
@@ -3106,7 +3271,7 @@ int get_c_indent(void)
// 4 *
// 5,
// 6,
- if (cin_iscomment(skipwhite(l))) {
+ if (cin_iscomment((char_u *)skipwhite((char *)l))) {
break;
}
lookfor = LOOKFOR_COMMA;
@@ -3128,7 +3293,7 @@ int get_c_indent(void)
&& *l != NUL
&& l[STRLEN(l) - 1] == '\\') {
// XXX
- cont_amount = cin_get_equal_amount( curwin->w_cursor.lnum);
+ cont_amount = cin_get_equal_amount(curwin->w_cursor.lnum);
}
if (lookfor != LOOKFOR_TERM
&& lookfor != LOOKFOR_JS_KEY
@@ -3148,16 +3313,17 @@ int get_c_indent(void)
/*
* Found an unterminated line after a while ();, line up
* with the last one.
- * while (cond);
- * 100 + <- line up with this one
- * -> here;
+ * while (cond);
+ * 100 + <- line up with this one
+ * -> here;
*/
if (lookfor == LOOKFOR_UNTERM
|| lookfor == LOOKFOR_ENUM_OR_INIT) {
- if (cont_amount > 0)
+ if (cont_amount > 0) {
amount = cont_amount;
- else
+ } else {
amount += ind_continuation;
+ }
break;
}
@@ -3183,7 +3349,7 @@ int get_c_indent(void)
* may be lined up with the case label.
*/
if (lookfor == LOOKFOR_NOBREAK
- && cin_isbreak(skipwhite(get_cursor_line_ptr()))) {
+ && cin_isbreak((char_u *)skipwhite((char *)get_cursor_line_ptr()))) {
lookfor = LOOKFOR_ANY;
continue;
}
@@ -3203,35 +3369,37 @@ int get_c_indent(void)
/*
* Found a terminated line above an unterminated line. Add
* the amount for a continuation line.
- * x = 1;
- * y = foo +
- * -> here;
+ * x = 1;
+ * y = foo +
+ * -> here;
* or
- * int x = 1;
- * int foo,
- * -> here;
+ * int x = 1;
+ * int foo,
+ * -> here;
*/
if (lookfor == LOOKFOR_UNTERM
|| lookfor == LOOKFOR_ENUM_OR_INIT) {
- if (cont_amount > 0)
+ if (cont_amount > 0) {
amount = cont_amount;
- else
+ } else {
amount += ind_continuation;
+ }
break;
}
/*
* Found a terminated line above a terminated line or "if"
* etc. line. Use the amount of the line below us.
- * x = 1; x = 1;
- * if (asdf) y = 2;
- * while (asdf) ->here;
- * here;
+ * x = 1; x = 1;
+ * if (asdf) y = 2;
+ * while (asdf) ->here;
+ * here;
* ->foo;
*/
if (lookfor == LOOKFOR_TERM) {
- if (!lookfor_break && whilelevel == 0)
+ if (!lookfor_break && whilelevel == 0) {
break;
+ }
}
/*
* First line above the one we're indenting is terminated.
@@ -3244,20 +3412,17 @@ int get_c_indent(void)
* that matching it will take us back to the start of
* the line. Helps for:
* func(asdr,
- * asdfasdf);
+ * asdfasdf);
* here;
*/
term_again:
l = get_cursor_line_ptr();
if (find_last_paren(l, '(', ')')
- && (trypos = find_match_paren(
- curbuf->b_ind_maxparen)) != NULL) {
- /*
- * Check if we are on a case label now. This is
- * handled above.
- * case xx: if ( asdf &&
- * asdf)
- */
+ && (trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL) {
+ // Check if we are on a case label now. This is
+ // handled above.
+ // case xx: if ( asdf &&
+ // asdf)
curwin->w_cursor = *trypos;
l = get_cursor_line_ptr();
if (cin_iscase(l, false) || cin_isscopedecl(l)) {
@@ -3270,10 +3435,10 @@ term_again:
/* When aligning with the case statement, don't align
* with a statement after it.
* case 1: { <-- don't use this { position
- * stat;
+ * stat;
* }
* case 2:
- * stat;
+ * stat;
* }
*/
iscase = curbuf->b_ind_keep_case_label && cin_iscase(l, false);
@@ -3284,19 +3449,21 @@ term_again:
*/
amount = skip_label(curwin->w_cursor.lnum, &l);
- if (theline[0] == '{')
+ if (theline[0] == '{') {
amount += curbuf->b_ind_open_extra;
+ }
// See remark above: "Only add b_ind_open_extra.."
- l = skipwhite(l);
- if (*l == '{')
+ l = (char_u *)skipwhite((char *)l);
+ if (*l == '{') {
amount -= curbuf->b_ind_open_extra;
+ }
lookfor = iscase ? LOOKFOR_ANY : LOOKFOR_TERM;
/*
* When a terminated line starts with "else" skip to
* the matching "if":
* else 3;
- * indent this;
+ * indent this;
* Need to use the scope of this "else". XXX
* If whilelevel != 0 continue looking for a "do {".
*/
@@ -3306,8 +3473,9 @@ term_again:
&& whilelevel == 0) {
if ((trypos = find_start_brace()) == NULL
|| find_match(LOOKFOR_IF, trypos->lnum)
- == FAIL)
+ == FAIL) {
break;
+ }
continue;
}
@@ -3322,9 +3490,10 @@ term_again:
// if not "else {" check for terminated again
// but skip block for "} else {"
l = cin_skipcomment(get_cursor_line_ptr());
- if (*l == '}' || !cin_iselse(l))
+ if (*l == '}' || !cin_iselse(l)) {
goto term_again;
- ++curwin->w_cursor.lnum;
+ }
+ curwin->w_cursor.lnum++;
curwin->w_cursor.col = 0;
}
}
@@ -3357,8 +3526,8 @@ term_again:
// of a function
if (theline[0] == '{') {
- amount = curbuf->b_ind_first_open;
- goto theend;
+ amount = curbuf->b_ind_first_open;
+ goto theend;
}
/*
* If the NEXT line is a function declaration, the current
@@ -3368,14 +3537,13 @@ term_again:
* contains { or }: "void f() {\n if (1)"
*/
if (cur_curpos.lnum < curbuf->b_ml.ml_line_count
- && !cin_nocode(theline)
- && vim_strchr(theline, '{') == NULL
- && vim_strchr(theline, '}') == NULL
- && !cin_ends_in(theline, (char_u *)":", NULL)
- && !cin_ends_in(theline, (char_u *)",", NULL)
- && cin_isfuncdecl(NULL, cur_curpos.lnum + 1,
- cur_curpos.lnum + 1)
- && !cin_isterminated(theline, false, true)) {
+ && !cin_nocode(theline)
+ && vim_strchr((char *)theline, '{') == NULL
+ && vim_strchr((char *)theline, '}') == NULL
+ && !cin_ends_in(theline, (char_u *)":", NULL)
+ && !cin_ends_in(theline, (char_u *)",", NULL)
+ && cin_isfuncdecl(NULL, cur_curpos.lnum + 1, cur_curpos.lnum + 1)
+ && !cin_isterminated(theline, false, true)) {
amount = curbuf->b_ind_func_type;
goto theend;
}
@@ -3418,8 +3586,9 @@ term_again:
continue;
}
- if (cin_nocode(l))
+ if (cin_nocode(l)) {
continue;
+ }
/*
* If the previous line ends in ',', use one level of
@@ -3444,23 +3613,26 @@ term_again:
/* For a line ending in ',' that is a continuation line go
* back to the first line with a backslash:
* char *foo = "bla\
- * bla",
+ * bla",
* here;
*/
while (n == 0 && curwin->w_cursor.lnum > 1) {
l = ml_get(curwin->w_cursor.lnum - 1);
- if (*l == NUL || l[STRLEN(l) - 1] != '\\')
+ if (*l == NUL || l[STRLEN(l) - 1] != '\\') {
break;
- --curwin->w_cursor.lnum;
+ }
+ curwin->w_cursor.lnum--;
curwin->w_cursor.col = 0;
}
amount = get_indent(); // XXX
- if (amount == 0)
+ if (amount == 0) {
amount = cin_first_id_amount();
- if (amount == 0)
+ }
+ if (amount == 0) {
amount = ind_continuation;
+ }
break;
}
@@ -3477,17 +3649,18 @@ term_again:
* Finding the closing '}' of a previous function. Put
* current line at the left margin. For when 'cino' has "fs".
*/
- if (*skipwhite(l) == '}')
+ if (*skipwhite((char *)l) == '}') {
break;
+ }
- /* (matching {)
- * If the previous line ends on '};' (maybe followed by
- * comments) align at column 0. For example:
- * char *string_array[] = { "foo",
- * / * x * / "b};ar" }; / * foobar * /
- */
- if (cin_ends_in(l, (char_u *)"};", NULL))
+ // (matching {)
+ // If the previous line ends on '};' (maybe followed by
+ // comments) align at column 0. For example:
+ // char *string_array[] = { "foo",
+ // / * x * / "b};ar" }; / * foobar * /
+ if (cin_ends_in(l, (char_u *)"};", NULL)) {
break;
+ }
// If the previous line ends on '[' we are probably in an
// array constant:
@@ -3503,7 +3676,7 @@ term_again:
* line ending in '}', e.g. before an #endif. Don't increase
* indent then.
*/
- if (*(look = skipwhite(l)) == ';' && cin_nocode(look + 1)) {
+ if (*(look = (char_u *)skipwhite((char *)l)) == ';' && cin_nocode(look + 1)) {
pos_T curpos_save = curwin->w_cursor;
while (curwin->w_cursor.lnum > 1) {
@@ -3514,8 +3687,9 @@ term_again:
}
}
if (curwin->w_cursor.lnum > 0
- && cin_ends_in(look, (char_u *)"}", NULL))
+ && cin_ends_in(look, (char_u *)"}", NULL)) {
break;
+ }
curwin->w_cursor = curpos_save;
}
@@ -3540,8 +3714,9 @@ term_again:
if (cin_ends_in(l, (char_u *)";", NULL)) {
l = ml_get(curwin->w_cursor.lnum - 1);
if (cin_ends_in(l, (char_u *)",", NULL)
- || (*l != NUL && l[STRLEN(l) - 1] == '\\'))
+ || (*l != NUL && l[STRLEN(l) - 1] == '\\')) {
break;
+ }
l = get_cursor_line_ptr();
}
@@ -3554,8 +3729,9 @@ term_again:
*/
(void)find_last_paren(l, '(', ')');
- if ((trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL)
+ if ((trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL) {
curwin->w_cursor = *trypos;
+ }
amount = get_indent(); // XXX
break;
}
@@ -3565,26 +3741,27 @@ term_again:
amount += curbuf->b_ind_comment;
}
- /* add extra indent if the previous line ended in a backslash:
- * "asdfasdf\
- * here";
- * char *foo = "asdf\
- * here";
- */
+ // add extra indent if the previous line ended in a backslash:
+ // "asdfasdf{backslash}
+ // here";
+ // char *foo = "asdf{backslash}
+ // here";
if (cur_curpos.lnum > 1) {
l = ml_get(cur_curpos.lnum - 1);
if (*l != NUL && l[STRLEN(l) - 1] == '\\') {
cur_amount = cin_get_equal_amount(cur_curpos.lnum - 1);
- if (cur_amount > 0)
+ if (cur_amount > 0) {
amount = cur_amount;
- else if (cur_amount == 0)
+ } else if (cur_amount == 0) {
amount += ind_continuation;
+ }
}
}
theend:
- if (amount < 0)
+ if (amount < 0) {
amount = 0;
+ }
laterend:
// put the cursor back where it belongs
@@ -3597,9 +3774,9 @@ laterend:
static int find_match(int lookfor, linenr_T ourscope)
{
- char_u *look;
- pos_T *theirscope;
- char_u *mightbeif;
+ const char_u *look;
+ pos_T *theirscope;
+ const char_u *mightbeif;
int elselevel;
int whilelevel;
@@ -3639,16 +3816,18 @@ static int find_match(int lookfor, linenr_T ourscope)
* back than the one enclosing the else, we're
* out of luck too.
*/
- if (theirscope->lnum < ourscope)
+ if (theirscope->lnum < ourscope) {
break;
+ }
/*
* and if they're enclosed in a *deeper* brace,
* then we can ignore it because it's in a
* different scope...
*/
- if (theirscope->lnum > ourscope)
+ if (theirscope->lnum > ourscope) {
continue;
+ }
/*
* if it was an "else" (that's not an "else if")
@@ -3658,8 +3837,9 @@ static int find_match(int lookfor, linenr_T ourscope)
look = cin_skipcomment(get_cursor_line_ptr());
if (cin_iselse(look)) {
mightbeif = cin_skipcomment(look + 4);
- if (!cin_isif(mightbeif))
- ++elselevel;
+ if (!cin_isif(mightbeif)) {
+ elselevel++; // NOLINT(readability/braces)
+ }
continue;
}
@@ -3680,8 +3860,9 @@ static int find_match(int lookfor, linenr_T ourscope)
* When looking for an "if" ignore "while"s that
* get in the way.
*/
- if (elselevel == 0 && lookfor == LOOKFOR_IF)
+ if (elselevel == 0 && lookfor == LOOKFOR_IF) {
whilelevel = 0;
+ }
}
// If it's a "do" decrement whilelevel
@@ -3706,8 +3887,9 @@ static int find_match(int lookfor, linenr_T ourscope)
*/
void do_c_expr_indent(void)
{
- if (*curbuf->b_p_inde != NUL)
+ if (*curbuf->b_p_inde != NUL) {
fixthisline(get_expr_indent);
- else
+ } else {
fixthisline(get_c_indent);
+ }
}
diff --git a/src/nvim/input.c b/src/nvim/input.c
index 2f7c5c2c16..37c2903cfd 100644
--- a/src/nvim/input.c
+++ b/src/nvim/input.c
@@ -9,9 +9,9 @@
#include "nvim/func_attr.h"
#include "nvim/getchar.h"
+#include "nvim/input.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
-#include "nvim/input.h"
#include "nvim/mouse.h"
#include "nvim/os/input.h"
#include "nvim/ui.h"
@@ -39,9 +39,10 @@ int ask_yesno(const char *const str, const bool direct)
const int save_State = State;
no_wait_return++;
- State = CONFIRM; // Mouse behaves like with :confirm.
+ State = MODE_CONFIRM; // Mouse behaves like with :confirm.
setmouse(); // Disable mouse in xterm.
no_mapping++;
+ allow_keys++; // no mapping here, but recognize keys
int r = ' ';
while (r != 'y' && r != 'n') {
@@ -62,6 +63,7 @@ int ask_yesno(const char *const str, const bool direct)
State = save_State;
setmouse();
no_mapping--;
+ allow_keys--;
return r;
}
@@ -105,7 +107,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;
@@ -142,7 +144,7 @@ int get_keystroke(MultiQueue *events)
continue;
}
buf[len >= buflen ? buflen - 1 : len] = NUL;
- n = utf_ptr2char(buf);
+ n = utf_ptr2char((char *)buf);
break;
}
xfree(buf);
@@ -172,6 +174,7 @@ int get_number(int colon, int *mouse_used)
}
no_mapping++;
+ allow_keys++; // no mapping here, but recognize keys
for (;;) {
ui_cursor_goto(msg_row, msg_col);
c = safe_vgetc();
@@ -205,6 +208,7 @@ int get_number(int colon, int *mouse_used)
}
}
no_mapping--;
+ allow_keys--;
return n;
}
@@ -231,7 +235,7 @@ int prompt_for_number(int *mouse_used)
save_cmdline_row = cmdline_row;
cmdline_row = 0;
save_State = State;
- State = ASKMORE; // prevents a screen update when using a timer
+ State = MODE_ASKMORE; // prevents a screen update when using a timer
// May show different mouse shape.
setmouse();
diff --git a/src/nvim/keymap.c b/src/nvim/keycodes.c
index 533c9f3053..cd3c7316bf 100644
--- a/src/nvim/keymap.c
+++ b/src/nvim/keycodes.c
@@ -9,7 +9,7 @@
#include "nvim/charset.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
-#include "nvim/keymap.h"
+#include "nvim/keycodes.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/mouse.h"
@@ -17,12 +17,10 @@
#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "keymap.c.generated.h"
+# include "keycodes.c.generated.h"
#endif
-/*
- * Some useful tables.
- */
+// Some useful tables.
static const struct modmasktable {
uint16_t mod_mask; ///< Bit-mask for particular key modifier.
@@ -43,10 +41,9 @@ static const struct modmasktable {
// NOTE: when adding an entry, update MAX_KEY_NAME_LEN!
};
-/*
- * Shifted key terminal codes and their unshifted equivalent.
- * Don't add mouse codes here, they are handled separately!
- */
+// Shifted key terminal codes and their unshifted equivalent.
+// Don't add mouse codes here, they are handled separately!
+
#define MOD_KEYS_ENTRY_SIZE 5
static char_u modifier_keys_table[] =
@@ -158,7 +155,6 @@ static const struct key_name_entry {
{ ESC, "Esc" },
{ ESC, "Escape" }, // Alternative name
{ CSI, "CSI" },
- { K_CSI, "xCSI" },
{ '|', "Bar" },
{ '\\', "Bslash" },
{ K_DEL, "Del" },
@@ -222,6 +218,35 @@ static const struct key_name_entry {
{ K_F35, "F35" },
{ K_F36, "F36" },
{ K_F37, "F37" },
+ { K_F38, "F38" },
+ { K_F39, "F39" },
+ { K_F40, "F40" },
+
+ { K_F41, "F41" },
+ { K_F42, "F42" },
+ { K_F43, "F43" },
+ { K_F44, "F44" },
+ { K_F45, "F45" },
+ { K_F46, "F46" },
+ { K_F47, "F47" },
+ { K_F48, "F48" },
+ { K_F49, "F49" },
+ { K_F50, "F50" },
+
+ { K_F51, "F51" },
+ { K_F52, "F52" },
+ { K_F53, "F53" },
+ { K_F54, "F54" },
+ { K_F55, "F55" },
+ { K_F56, "F56" },
+ { K_F57, "F57" },
+ { K_F58, "F58" },
+ { K_F59, "F59" },
+ { K_F60, "F60" },
+
+ { K_F61, "F61" },
+ { K_F62, "F62" },
+ { K_F63, "F63" },
{ K_F38, "F38" },
{ K_F39, "F39" },
@@ -460,10 +485,7 @@ int handle_x_keys(const int key)
return key;
}
-/*
- * Return a string which contains the name of the given key when the given
- * modifiers are down.
- */
+/// @return a string which contains the name of the given key when the given modifiers are down.
char_u *get_special_key_name(int c, int modifiers)
{
static char_u string[MAX_KEY_NAME_LEN + 1];
@@ -480,10 +502,8 @@ char_u *get_special_key_name(int c, int modifiers)
c = KEY2TERMCAP1(c);
}
- /*
- * Translate shifted special keys into unshifted keys and set modifier.
- * Same for CTRL and ALT modifiers.
- */
+ // Translate shifted special keys into unshifted keys and set modifier.
+ // Same for CTRL and ALT modifiers.
if (IS_SPECIAL(c)) {
for (i = 0; modifier_keys_table[i] != 0; i += MOD_KEYS_ENTRY_SIZE) {
if (KEY2TERMCAP0(c) == (int)modifier_keys_table[i + 1]
@@ -499,10 +519,8 @@ char_u *get_special_key_name(int c, int modifiers)
// try to find the key in the special key table
table_idx = find_special_key_in_table(c);
- /*
- * When not a known special key, and not a printable character, try to
- * extract modifiers.
- */
+ // When not a known special key, and not a printable character, try to
+ // extract modifiers.
if (c > 0
&& utf_char2len(c) == 1) {
if (table_idx < 0
@@ -537,7 +555,7 @@ char_u *get_special_key_name(int c, int modifiers)
} else {
// Not a special key, only modifiers, output directly.
if (utf_char2len(c) > 1) {
- idx += utf_char2bytes(c, string + idx);
+ idx += utf_char2bytes(c, (char *)string + idx);
} else if (vim_isprintc(c)) {
string[idx++] = (char_u)c;
} else {
@@ -567,31 +585,30 @@ char_u *get_special_key_name(int c, int modifiers)
/// @param[in] src_len Length of the srcp.
/// @param[out] dst Location where translation result will be kept. It must
// be at least 19 bytes per "<x>" form.
-/// @param[in] keycode Prefer key code, e.g. K_DEL in place of DEL.
-/// @param[in] in_string Inside a double quoted string
+/// @param[in] flags FSK_ values
+/// @param[in] escape_ks escape K_SPECIAL bytes in the character
+/// @param[out] did_simplify found <C-H>, etc.
///
/// @return Number of characters added to dst, zero for no match.
-unsigned int trans_special(const char_u **srcp, const size_t src_len, char_u *const dst,
- const bool keycode, const bool in_string)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+unsigned int trans_special(const char_u **const srcp, const size_t src_len, char_u *const dst,
+ const int flags, const bool escape_ks, bool *const did_simplify)
+ FUNC_ATTR_NONNULL_ARG(1, 3) FUNC_ATTR_WARN_UNUSED_RESULT
{
int modifiers = 0;
- int key;
-
- key = find_special_key(srcp, src_len, &modifiers, keycode, false, in_string);
+ int key = find_special_key(srcp, src_len, &modifiers, flags, did_simplify);
if (key == 0) {
return 0;
}
- return special_to_buf(key, modifiers, keycode, dst);
+ return special_to_buf(key, modifiers, escape_ks, dst);
}
/// Put the character sequence for "key" with "modifiers" into "dst" and return
/// the resulting length.
-/// When "keycode" is true prefer key code, e.g. K_DEL instead of DEL.
+/// When "escape_ks" is true escape K_SPECIAL bytes in the character.
/// The sequence is not NUL terminated.
/// This is how characters in a string are encoded.
-unsigned int special_to_buf(int key, int modifiers, bool keycode, char_u *dst)
+unsigned int special_to_buf(int key, int modifiers, bool escape_ks, char_u *dst)
{
unsigned int dlen = 0;
@@ -606,12 +623,12 @@ unsigned int special_to_buf(int key, int modifiers, bool keycode, char_u *dst)
dst[dlen++] = K_SPECIAL;
dst[dlen++] = (char_u)KEY2TERMCAP0(key);
dst[dlen++] = KEY2TERMCAP1(key);
- } else if (!keycode) {
- dlen += (unsigned int)utf_char2bytes(key, dst + dlen);
- } else {
+ } else if (escape_ks) {
char_u *after = add_char2buf(key, dst + dlen);
assert(after >= dst && (uintmax_t)(after - dst) <= UINT_MAX);
dlen = (unsigned int)(after - dst);
+ } else {
+ dlen += (unsigned int)utf_char2bytes(key, (char *)dst + dlen);
}
return dlen;
@@ -622,20 +639,20 @@ unsigned int special_to_buf(int key, int modifiers, bool keycode, char_u *dst)
/// @param[in,out] srcp Translated <> name. Is advanced to after the <> name.
/// @param[in] src_len srcp length.
/// @param[out] modp Location where information about modifiers is saved.
-/// @param[in] keycode Prefer key code, e.g. K_DEL in place of DEL.
-/// @param[in] keep_x_key Don’t translate xHome to Home key.
-/// @param[in] in_string In string, double quote is escaped
+/// @param[in] flags FSK_ values
+/// @param[out] did_simplify FSK_SIMPLIFY and found <C-H>, etc.
///
/// @return Key and modifiers or 0 if there is no match.
-int find_special_key(const char_u **srcp, const size_t src_len, int *const modp, const bool keycode,
- const bool keep_x_key, const bool in_string)
- FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
+int find_special_key(const char_u **const srcp, const size_t src_len, int *const modp,
+ const int flags, bool *const did_simplify)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1, 3)
{
const char_u *last_dash;
const char_u *end_of_name;
const char_u *src;
const char_u *bp;
const char_u *const end = *srcp + src_len - 1;
+ const bool in_string = flags & FSK_IN_STRING;
int modifiers;
int bit;
int key;
@@ -650,6 +667,9 @@ int find_special_key(const char_u **srcp, const size_t src_len, int *const modp,
if (src[0] != '<') {
return 0;
}
+ if (src[1] == '*') { // <*xxx>: do not simplify
+ src++;
+ }
// Find end of modifier list
last_dash = src;
@@ -661,7 +681,7 @@ int find_special_key(const char_u **srcp, const size_t src_len, int *const modp,
// Anything accepted, like <C-?>.
// <C-"> or <M-"> are not special in strings as " is
// the string delimiter. With a backslash it works: <M-\">
- if (end - bp > l && !(in_string && bp[1] == '"') && bp[l+1] == '>') {
+ if (end - bp > l && !(in_string && bp[1] == '"') && bp[l + 1] == '>') {
bp += l;
} else if (end - bp > 2 && in_string && bp[1] == '\\'
&& bp[2] == '"' && bp[3] == '>') {
@@ -716,13 +736,13 @@ int find_special_key(const char_u **srcp, const size_t src_len, int *const modp,
// Special case for a double-quoted string
off = l = 2;
} else {
- l = utfc_ptr2len(last_dash + 1);
+ l = utfc_ptr2len((char *)last_dash + 1);
}
if (modifiers != 0 && last_dash[l + 1] == '>') {
- key = utf_ptr2char(last_dash + off);
+ key = utf_ptr2char((char *)last_dash + off);
} else {
key = get_special_key_code(last_dash + off);
- if (!keep_x_key) {
+ if (!(flags & FSK_KEEP_X_KEY)) {
key = handle_x_keys(key);
}
}
@@ -735,7 +755,7 @@ int find_special_key(const char_u **srcp, const size_t src_len, int *const modp,
// includes the modifier.
key = simplify_key(key, &modifiers);
- if (!keycode) {
+ if (!(flags & FSK_KEYCODE)) {
// don't want keycode, use single byte code
if (key == K_BS) {
key = BS;
@@ -747,7 +767,7 @@ int find_special_key(const char_u **srcp, const size_t src_len, int *const modp,
// Normal Key with modifier:
// Try to make a single byte code (except for Alt/Meta modifiers).
if (!IS_SPECIAL(key)) {
- key = extract_modifiers(key, &modifiers);
+ key = extract_modifiers(key, &modifiers, flags & FSK_SIMPLIFY, did_simplify);
}
*modp = modifiers;
@@ -761,7 +781,10 @@ int find_special_key(const char_u **srcp, const size_t src_len, int *const modp,
/// Try to include modifiers (except alt/meta) in the key.
/// Changes "Shift-a" to 'A', "Ctrl-@" to <Nul>, etc.
-static int extract_modifiers(int key, int *modp)
+/// @param[in] simplify if false, don't do Ctrl
+/// @param[out] did_simplify set when it is not NULL and "simplify" is true and
+/// Ctrl is removed from modifiers
+static int extract_modifiers(int key, int *modp, const bool simplify, bool *const did_simplify)
{
int modifiers = *modp;
@@ -772,23 +795,28 @@ static int extract_modifiers(int key, int *modp)
modifiers &= ~MOD_MASK_SHIFT;
}
}
- if ((modifiers & MOD_MASK_CTRL)
+ // <C-H> and <C-h> mean the same thing, always use "H"
+ if ((modifiers & MOD_MASK_CTRL) && ASCII_ISALPHA(key)) {
+ key = TOUPPER_ASC(key);
+ }
+ if (simplify && (modifiers & MOD_MASK_CTRL)
&& ((key >= '?' && key <= '_') || ASCII_ISALPHA(key))) {
- key = Ctrl_chr(key);
+ key = CTRL_CHR(key);
modifiers &= ~MOD_MASK_CTRL;
- if (key == 0) { // <C-@> is <Nul>
+ if (key == NUL) { // <C-@> is <Nul>
key = K_ZERO;
}
+ if (did_simplify != NULL) {
+ *did_simplify = true;
+ }
}
*modp = modifiers;
return key;
}
-/*
- * Try to find key "c" in the special key table.
- * Return the index when found, -1 when not found.
- */
+/// Try to find key "c" in the special key table.
+/// @return the index when found, -1 when not found.
int find_special_key_in_table(int c)
{
int i;
@@ -831,10 +859,8 @@ int get_special_key_code(const char_u *name)
return 0;
}
-/*
- * Look up the given mouse code to return the relevant information in the other
- * arguments. Return which button is down or was released.
- */
+/// Look up the given mouse code to return the relevant information in the other arguments.
+/// @return which button is down or was released.
int get_mouse_button(int code, bool *is_click, bool *is_drag)
{
int i;
@@ -849,55 +875,57 @@ int get_mouse_button(int code, bool *is_click, bool *is_drag)
return 0; // Shouldn't get here
}
-/// Replace any terminal code strings with the equivalent internal
-/// representation
+/// Replace any terminal code strings with the equivalent internal representation.
///
-/// Used for the "from" and "to" part of a mapping, and the "to" part of
-/// a menu command. Any strings like "<C-UP>" are also replaced, unless
-/// `special` is false. K_SPECIAL by itself is replaced by K_SPECIAL
-/// KS_SPECIAL KE_FILLER.
+/// Used for the "from" and "to" part of a mapping, and the "to" part of a menu command.
+/// Any strings like "<C-UP>" are also replaced, unless `special` is false.
+/// K_SPECIAL by itself is replaced by K_SPECIAL KS_SPECIAL KE_FILLER.
+///
+/// When "flags" has REPTERM_FROM_PART, trailing <C-v> is included, otherwise it is removed (to make
+/// ":map xx ^V" map xx to nothing). When cpo_flags contains FLAG_CPO_BSLASH, a backslash can be
+/// used in place of <C-v>. All other <C-v> characters are removed.
///
/// @param[in] from What characters to replace.
/// @param[in] from_len Length of the "from" argument.
-/// @param[out] bufp Location where results were saved in case of success
-/// (allocated). Will be set to NULL in case of failure.
-/// @param[in] do_lt If true, also translate <lt>.
-/// @param[in] from_part If true, trailing <C-v> is included, otherwise it is
-/// removed (to make ":map xx ^V" map xx to nothing).
-/// When cpo_flags contains #FLAG_CPO_BSLASH, a backslash
-/// can be used in place of <C-v>. All other <C-v>
-/// characters are removed.
-/// @param[in] special Replace keycodes, e.g. <CR> becomes a "\n" char.
-/// @param[in] cpo_flags Relevant flags derived from p_cpo, see
-/// #CPO_TO_CPO_FLAGS.
+/// @param[out] bufp Location where results were saved in case of success (allocated).
+/// if *bufp is non-NULL, it will be used directly. it is
+/// assumed to be 128 bytes long (enough for transcoding LHS
+/// of mapping)
+/// Will be set to NULL in case of failure.
+/// @param[in] flags REPTERM_FROM_PART see above
+/// REPTERM_DO_LT also translate <lt>
+/// REPTERM_NO_SPECIAL do not accept <key> notation
+/// REPTERM_NO_SIMPLIFY do not simplify <C-H> into 0x08, etc.
+/// @param[out] did_simplify set when some <C-H> code was simplied, unless it is NULL.
+/// @param[in] cpo_flags Relevant flags derived from p_cpo, see CPO_TO_CPO_FLAGS.
///
-/// @return Pointer to an allocated memory in case of success, "from" in case of
-/// failure. In case of success returned pointer is also saved to
-/// "bufp".
-char_u *replace_termcodes(const char_u *from, const size_t from_len, char_u **bufp,
- const bool from_part, const bool do_lt, const bool special, int cpo_flags)
- FUNC_ATTR_NONNULL_ALL
+/// @return Pointer to an allocated memory, which is also saved to "bufp".
+char *replace_termcodes(const char *const from, const size_t from_len, char **const bufp,
+ const int flags, bool *const did_simplify, const int cpo_flags)
+ FUNC_ATTR_NONNULL_ARG(1, 3)
{
ssize_t i;
size_t slen;
char_u key;
size_t dlen = 0;
const char_u *src;
- const char_u *const end = from + from_len - 1;
- int do_backslash; // backslash is a special character
+ const char_u *const end = (char_u *)from + from_len - 1;
char_u *result; // buffer for resulting string
- do_backslash = !(cpo_flags&FLAG_CPO_BSLASH);
+ const bool do_backslash = !(cpo_flags & FLAG_CPO_BSLASH); // backslash is a special character
+ const bool do_special = !(flags & REPTERM_NO_SPECIAL);
+
+ bool allocated = (*bufp == NULL);
// Allocate space for the translation. Worst case a single character is
// replaced by 6 bytes (shifted special key), plus a NUL at the end.
- const size_t buf_len = from_len * 6 + 1;
- result = xmalloc(buf_len);
+ const size_t buf_len = allocated ? from_len * 6 + 1 : 128;
+ result = allocated ? xmalloc(buf_len) : *bufp;
- src = from;
+ src = (char_u *)from;
// Check for #n at start only: function key n
- if (from_part && from_len > 1 && src[0] == '#'
+ if ((flags & REPTERM_FROM_PART) && from_len > 1 && src[0] == '#'
&& ascii_isdigit(src[1])) { // function key
result[dlen++] = K_SPECIAL;
result[dlen++] = 'k';
@@ -911,9 +939,12 @@ char_u *replace_termcodes(const char_u *from, const size_t from_len, char_u **bu
// Copy each byte from *from to result[dlen]
while (src <= end) {
+ if (!allocated && dlen + 64 > buf_len) {
+ return NULL;
+ }
// Check for special <> keycodes, like "<C-S-LeftMouse>"
- if (special && (do_lt || ((end - src) >= 3
- && STRNCMP(src, "<lt>", 4) != 0))) {
+ if (do_special && ((flags & REPTERM_DO_LT) || ((end - src) >= 3
+ && STRNCMP(src, "<lt>", 4) != 0))) {
// Replace <SID> by K_SNR <script-nr> _.
// (room: 5 * 6 = 30 bytes; needed: 3 + <nr> + 1 <= 14)
if (end - src >= 4 && STRNICMP(src, "<SID>", 5) == 0) {
@@ -932,15 +963,16 @@ char_u *replace_termcodes(const char_u *from, const size_t from_len, char_u **bu
}
}
- slen = trans_special(&src, (size_t)(end - src) + 1, result + dlen, true,
- false);
+ slen = trans_special(&src, (size_t)(end - src) + 1, result + dlen,
+ FSK_KEYCODE | ((flags & REPTERM_NO_SIMPLIFY) ? 0 : FSK_SIMPLIFY),
+ true, did_simplify);
if (slen) {
dlen += slen;
continue;
}
}
- if (special) {
+ if (do_special) {
char_u *p, *s, len;
// Replace <Leader> by the value of "mapleader".
@@ -980,7 +1012,7 @@ char_u *replace_termcodes(const char_u *from, const size_t from_len, char_u **bu
if (key == Ctrl_V || (do_backslash && key == '\\')) {
src++; // skip CTRL-V or backslash
if (src > end) {
- if (from_part) {
+ if (flags & REPTERM_FROM_PART) {
result[dlen++] = key;
}
break;
@@ -991,7 +1023,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;
@@ -999,16 +1030,90 @@ char_u *replace_termcodes(const char_u *from, const size_t from_len, char_u **bu
} else {
result[dlen++] = *src;
}
- ++src;
+ src++;
}
}
result[dlen] = NUL;
- *bufp = xrealloc(result, dlen + 1);
+ if (allocated) {
+ *bufp = xrealloc(result, dlen + 1);
+ }
return *bufp;
}
+/// Add character "c" to buffer "s"
+///
+/// Escapes the special meaning of K_SPECIAL, handles multi-byte
+/// characters.
+///
+/// @param[in] c Character to add.
+/// @param[out] s Buffer to add to. Must have at least MB_MAXBYTES + 1 bytes.
+///
+/// @return Pointer to after the added bytes.
+char_u *add_char2buf(int c, char_u *s)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ char_u temp[MB_MAXBYTES + 1];
+ const int len = utf_char2bytes(c, (char *)temp);
+ for (int i = 0; i < len; i++) {
+ c = (uint8_t)temp[i];
+ // Need to escape K_SPECIAL like in the typeahead buffer.
+ if (c == K_SPECIAL) {
+ *s++ = K_SPECIAL;
+ *s++ = KS_SPECIAL;
+ *s++ = KE_FILLER;
+ } else {
+ *s++ = (char_u)c;
+ }
+ }
+ return s;
+}
+
+/// Copy "p" to allocated memory, escaping K_SPECIAL so that the result
+/// can be put in the typeahead buffer.
+char *vim_strsave_escape_ks(char *p)
+{
+ // Need a buffer to hold up to three times as much. Four in case of an
+ // illegal utf-8 byte:
+ // 0xc0 -> 0xc3 - 0x80 -> 0xc3 K_SPECIAL KS_SPECIAL KE_FILLER
+ char_u *res = xmalloc(STRLEN(p) * 4 + 1);
+ char_u *d = res;
+ for (char_u *s = (char_u *)p; *s != NUL;) {
+ if (s[0] == K_SPECIAL && s[1] != NUL && s[2] != NUL) {
+ // Copy special key unmodified.
+ *d++ = *s++;
+ *d++ = *s++;
+ *d++ = *s++;
+ } else {
+ // Add character, possibly multi-byte to destination, escaping
+ // K_SPECIAL. Be careful, it can be an illegal byte!
+ d = add_char2buf(utf_ptr2char((char *)s), d);
+ s += utf_ptr2len((char *)s);
+ }
+ }
+ *d = NUL;
+
+ return (char *)res;
+}
+
+/// 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;
+
+ while (*s != NUL) {
+ if (s[0] == K_SPECIAL && s[1] == KS_SPECIAL && s[2] == KE_FILLER) {
+ *d++ = K_SPECIAL;
+ s += 3;
+ } else {
+ *d++ = *s++;
+ }
+ }
+ *d = NUL;
+}
+
/// Logs a single key as a human-readable keycode.
void log_key(int log_level, int key)
{
diff --git a/src/nvim/keymap.h b/src/nvim/keycodes.h
index 5c8b81891c..67ec092f60 100644
--- a/src/nvim/keymap.h
+++ b/src/nvim/keycodes.h
@@ -1,13 +1,11 @@
-#ifndef NVIM_KEYMAP_H
-#define NVIM_KEYMAP_H
+#ifndef NVIM_KEYCODES_H
+#define NVIM_KEYCODES_H
#include "nvim/strings.h"
-/*
- * Keycode definitions for special keys.
- *
- * Any special key code sequences are replaced by these codes.
- */
+// Keycode definitions for special keys.
+//
+// Any special key code sequences are replaced by these codes.
//
// For MS-DOS some keys produce codes larger than 0xff. They are split into two
@@ -15,66 +13,49 @@
//
#define K_NUL (0xce) // for MS-DOS: special key follows
-/*
- * K_SPECIAL is the first byte of a special key code and is always followed by
- * two bytes.
- * The second byte can have any value. ASCII is used for normal termcap
- * entries, 0x80 and higher for special keys, see below.
- * The third byte is guaranteed to be between 0x02 and 0x7f.
- */
-
+/// K_SPECIAL is the first byte of a special key code and is always followed by
+/// two bytes.
+/// The second byte can have any value. ASCII is used for normal termcap
+/// entries, 0x80 and higher for special keys, see below.
+/// The third byte is guaranteed to be between 0x02 and 0x7f.
#define K_SPECIAL (0x80)
-/*
- * Positive characters are "normal" characters.
- * Negative characters are special key codes. Only characters below -0x200
- * are used to so that the absolute value can't be mistaken for a single-byte
- * character.
- */
+/// Positive characters are "normal" characters.
+/// Negative characters are special key codes. Only characters below -0x200
+/// are used to so that the absolute value can't be mistaken for a single-byte
+/// character.
#define IS_SPECIAL(c) ((c) < 0)
-/*
- * Characters 0x0100 - 0x01ff have a special meaning for abbreviations.
- * Multi-byte characters also have ABBR_OFF added, thus are above 0x0200.
- */
+/// Characters 0x0100 - 0x01ff have a special meaning for abbreviations.
+/// Multi-byte characters also have ABBR_OFF added, thus are above 0x0200.
#define ABBR_OFF 0x100
-/*
- * NUL cannot be in the input string, therefore it is replaced by
- * K_SPECIAL KS_ZERO KE_FILLER
- */
+/// NUL cannot be in the input string, therefore it is replaced by
+/// K_SPECIAL KS_ZERO KE_FILLER
#define KS_ZERO 255
-/*
- * K_SPECIAL cannot be in the input string, therefore it is replaced by
- * K_SPECIAL KS_SPECIAL KE_FILLER
- */
+/// K_SPECIAL cannot be in the input string, therefore it is replaced by
+/// K_SPECIAL KS_SPECIAL KE_FILLER
#define KS_SPECIAL 254
-/*
- * KS_EXTRA is used for keys that have no termcap name
- * K_SPECIAL KS_EXTRA KE_xxx
- */
+/// KS_EXTRA is used for keys that have no termcap name
+/// K_SPECIAL KS_EXTRA KE_xxx
#define KS_EXTRA 253
-/*
- * KS_MODIFIER is used when a modifier is given for a (special) key
- * K_SPECIAL KS_MODIFIER bitmask
- */
+/// KS_MODIFIER is used when a modifier is given for a (special) key
+/// K_SPECIAL KS_MODIFIER bitmask
#define KS_MODIFIER 252
-/*
- * These are used for the GUI
- * K_SPECIAL KS_xxx KE_FILLER
- */
+// These are used for the GUI
+// K_SPECIAL KS_xxx KE_FILLER
+
#define KS_MOUSE 251
#define KS_MENU 250
#define KS_VER_SCROLLBAR 249
#define KS_HOR_SCROLLBAR 248
-/*
- * Used for switching Select mode back on after a mapping or menu.
- */
+// Used for switching Select mode back on after a mapping or menu.
+
#define KS_SELECT 245
#define K_SELECT_STRING (char_u *)"\200\365X"
@@ -87,30 +68,24 @@
// Used for menu in a tab pages line.
#define KS_TABMENU 239
-/*
- * Filler used after KS_SPECIAL and others
- */
+/// Filler used after KS_SPECIAL and others
#define KE_FILLER ('X')
-/*
- * translation of three byte code "K_SPECIAL a b" into int "K_xxx" and back
- */
+// translation of three byte code "K_SPECIAL a b" into int "K_xxx" and back
+
#define TERMCAP2KEY(a, b) (-((a) + ((int)(b) << 8)))
#define KEY2TERMCAP0(x) ((-(x)) & 0xff)
#define KEY2TERMCAP1(x) (((unsigned)(-(x)) >> 8) & 0xff)
-/*
- * get second or third byte when translating special key code into three bytes
- */
+// get second or third byte when translating special key code into three bytes
+
#define K_SECOND(c) ((c) == K_SPECIAL ? KS_SPECIAL : (c) == \
NUL ? KS_ZERO : KEY2TERMCAP0(c))
#define K_THIRD(c) (((c) == K_SPECIAL || (c) == \
NUL) ? KE_FILLER : KEY2TERMCAP1(c))
-/*
- * get single int code from second byte after K_SPECIAL
- */
+/// get single int code from second byte after K_SPECIAL
#define TO_SPECIAL(a, b) ((a) == KS_SPECIAL ? K_SPECIAL : (a) == \
KS_ZERO ? K_ZERO : TERMCAP2KEY(a, b))
@@ -122,8 +97,6 @@
//
// Entries must be in the range 0x02-0x7f (see comment at K_SPECIAL).
enum key_extra {
- KE_NAME = 3, // name of this terminal entry
-
KE_S_UP = 4, // shift-up
KE_S_DOWN = 5, // shift-down
@@ -220,7 +193,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
@@ -249,9 +222,8 @@ enum key_extra {
KE_COMMAND = 104, // <Cmd> special key
};
-/*
- * the three byte codes are replaced with the following int when using vgetc()
- */
+// the three byte codes are replaced with the following int when using vgetc()
+
#define K_ZERO TERMCAP2KEY(KS_ZERO, KE_FILLER)
#define K_UP TERMCAP2KEY('k', 'u')
@@ -327,6 +299,35 @@ enum key_extra {
#define K_F35 TERMCAP2KEY('F', 'P')
#define K_F36 TERMCAP2KEY('F', 'Q')
#define K_F37 TERMCAP2KEY('F', 'R')
+#define K_F38 TERMCAP2KEY('F', 'S')
+#define K_F39 TERMCAP2KEY('F', 'T')
+#define K_F40 TERMCAP2KEY('F', 'U')
+
+#define K_F41 TERMCAP2KEY('F', 'V')
+#define K_F42 TERMCAP2KEY('F', 'W')
+#define K_F43 TERMCAP2KEY('F', 'X')
+#define K_F44 TERMCAP2KEY('F', 'Y')
+#define K_F45 TERMCAP2KEY('F', 'Z')
+#define K_F46 TERMCAP2KEY('F', 'a')
+#define K_F47 TERMCAP2KEY('F', 'b')
+#define K_F48 TERMCAP2KEY('F', 'c')
+#define K_F49 TERMCAP2KEY('F', 'd')
+#define K_F50 TERMCAP2KEY('F', 'e')
+
+#define K_F51 TERMCAP2KEY('F', 'f')
+#define K_F52 TERMCAP2KEY('F', 'g')
+#define K_F53 TERMCAP2KEY('F', 'h')
+#define K_F54 TERMCAP2KEY('F', 'i')
+#define K_F55 TERMCAP2KEY('F', 'j')
+#define K_F56 TERMCAP2KEY('F', 'k')
+#define K_F57 TERMCAP2KEY('F', 'l')
+#define K_F58 TERMCAP2KEY('F', 'm')
+#define K_F59 TERMCAP2KEY('F', 'n')
+#define K_F60 TERMCAP2KEY('F', 'o')
+
+#define K_F61 TERMCAP2KEY('F', 'p')
+#define K_F62 TERMCAP2KEY('F', 'q')
+#define K_F63 TERMCAP2KEY('F', 'r')
#define K_F38 TERMCAP2KEY('F', 'S')
#define K_F39 TERMCAP2KEY('F', 'T')
@@ -430,10 +431,9 @@ enum key_extra {
#define K_TABLINE TERMCAP2KEY(KS_TABLINE, KE_FILLER)
#define K_TABMENU TERMCAP2KEY(KS_TABMENU, KE_FILLER)
-/*
- * Symbols for pseudo keys which are translated from the real key symbols
- * above.
- */
+// Symbols for pseudo keys which are translated from the real key symbols
+// above.
+
#define K_LEFTMOUSE TERMCAP2KEY(KS_EXTRA, KE_LEFTMOUSE)
#define K_LEFTMOUSE_NM TERMCAP2KEY(KS_EXTRA, KE_LEFTMOUSE_NM)
#define K_LEFTDRAG TERMCAP2KEY(KS_EXTRA, KE_LEFTDRAG)
@@ -462,7 +462,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)
@@ -487,11 +486,8 @@ enum key_extra {
#define MOD_MASK_MULTI_CLICK (MOD_MASK_2CLICK|MOD_MASK_3CLICK| \
MOD_MASK_4CLICK)
-/*
- * The length of the longest special key name, including modifiers.
- * Current longest is <M-C-S-T-D-A-4-ScrollWheelRight> (length includes '<' and
- * '>').
- */
+/// The length of the longest special key name, including modifiers.
+/// Current longest is <M-C-S-T-D-A-4-ScrollWheelRight> (length includes '<' and '>').
#define MAX_KEY_NAME_LEN 32
// Maximum length of a special key event as tokens. This includes modifiers.
@@ -504,11 +500,27 @@ enum key_extra {
#define MAX_KEY_CODE_LEN 6
#define FLAG_CPO_BSLASH 0x01
-#define CPO_TO_CPO_FLAGS ((vim_strchr(p_cpo, CPO_BSLASH) == NULL) \
+#define CPO_TO_CPO_FLAGS ((vim_strchr((char *)p_cpo, CPO_BSLASH) == NULL) \
? 0 \
: FLAG_CPO_BSLASH)
+// Flags for replace_termcodes()
+enum {
+ REPTERM_FROM_PART = 1,
+ REPTERM_DO_LT = 2,
+ REPTERM_NO_SPECIAL = 4,
+ REPTERM_NO_SIMPLIFY = 8,
+};
+
+// Flags for find_special_key()
+enum {
+ FSK_KEYCODE = 0x01, ///< prefer key code, e.g. K_DEL in place of DEL
+ FSK_KEEP_X_KEY = 0x02, ///< don’t translate xHome to Home key
+ FSK_IN_STRING = 0x04, ///< in string, double quote is escaped
+ FSK_SIMPLIFY = 0x08, ///< simplify <C-H>, etc.
+};
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "keymap.h.generated.h"
+# include "keycodes.h.generated.h"
#endif
-#endif // NVIM_KEYMAP_H
+#endif // NVIM_KEYCODES_H
diff --git a/src/nvim/lib/kbtree.h b/src/nvim/lib/kbtree.h
index 617773a79a..99f79952d7 100644
--- a/src/nvim/lib/kbtree.h
+++ b/src/nvim/lib/kbtree.h
@@ -51,7 +51,7 @@
struct kbnode_##name##_s { \
int32_t n; \
bool is_internal; \
- key_t key[2*T-1]; \
+ key_t key[2*T - 1]; \
kbnode_##name##_t *ptr[]; \
}; \
typedef struct { \
@@ -103,11 +103,11 @@
int mid = (begin + end) >> 1; \
if (__cmp(__KB_KEY(key_t, x)[mid], *k) < 0) begin = mid + 1; \
else end = mid; \
- } \
- if (begin == x->n) { *rr = 1; return x->n - 1; } \
- if ((*rr = __cmp(*k, __KB_KEY(key_t, x)[begin])) < 0) --begin; \
- return begin; \
- }
+ } \
+ if (begin == x->n) { *rr = 1; return x->n - 1; } \
+ if ((*rr = __cmp(*k, __KB_KEY(key_t, x)[begin])) < 0) --begin; \
+ return begin; \
+ }
#define __KB_GET(name, key_t, kbnode_t) \
static key_t *kb_getp_##name(kbtree_##name##_t *b, key_t * __restrict k) \
@@ -195,35 +195,34 @@
if (__KB_PTR(b, x)[i]->n == 2 * T - 1) { \
__kb_split_##name(b, x, i, __KB_PTR(b, x)[i]); \
if (__cmp(*k, __KB_KEY(key_t, x)[i]) > 0) ++i; \
- } \
- ret = __kb_putp_aux_##name(b, __KB_PTR(b, x)[i], k); \
} \
- return ret; \
+ ret = __kb_putp_aux_##name(b, __KB_PTR(b, x)[i], k); \
} \
- static inline key_t *kb_putp_##name(kbtree_##name##_t *b, key_t * __restrict k) \
- { \
- if (!b->root) { \
- b->root = (kbnode_t *)xcalloc(1, ILEN); \
- ++b->n_nodes; \
- } \
- kbnode_t *r, *s; \
- ++b->n_keys; \
- r = b->root; \
- if (r->n == 2 * T - 1) { \
- ++b->n_nodes; \
- s = (kbnode_t *)xcalloc(1, ILEN); \
- b->root = s; s->is_internal = 1; s->n = 0; \
- __KB_PTR(b, s)[0] = r; \
- __kb_split_##name(b, s, 0, r); \
- r = s; \
- } \
- return __kb_putp_aux_##name(b, r, k); \
+ return ret; \
+ } \
+ static inline key_t *kb_putp_##name(kbtree_##name##_t *b, key_t * __restrict k) \
+ { \
+ if (!b->root) { \
+ b->root = (kbnode_t *)xcalloc(1, ILEN); \
+ ++b->n_nodes; \
} \
- static inline void kb_put_##name(kbtree_##name##_t *b, key_t k) \
- { \
- kb_putp_##name(b, &k); \
- }
-
+ kbnode_t *r, *s; \
+ ++b->n_keys; \
+ r = b->root; \
+ if (r->n == 2 * T - 1) { \
+ ++b->n_nodes; \
+ s = (kbnode_t *)xcalloc(1, ILEN); \
+ b->root = s; s->is_internal = 1; s->n = 0; \
+ __KB_PTR(b, s)[0] = r; \
+ __kb_split_##name(b, s, 0, r); \
+ r = s; \
+ } \
+ return __kb_putp_aux_##name(b, r, k); \
+ } \
+ static inline void kb_put_##name(kbtree_##name##_t *b, key_t k) \
+ { \
+ kb_putp_##name(b, &k); \
+ }
#define __KB_DEL(name, key_t, kbnode_t, T) \
static inline key_t __kb_delp_aux_##name(kbtree_##name##_t *b, kbnode_t *x, key_t * __restrict k, \
@@ -380,62 +379,62 @@
} \
--itr->p; \
if (itr->p->x && itr->p->i < itr->p->x->n) return 1; \
+ } \
+ } \
+ static inline int kb_itr_prev_##name(kbtree_##name##_t *b, kbitr_##name##_t *itr) \
+ { \
+ if (itr->p == NULL) return 0; \
+ for (;;) { \
+ while (itr->p->x && itr->p->i >= 0) { \
+ itr->p[1].x = itr->p->x->is_internal? __KB_PTR(b, itr->p->x)[itr->p->i] : 0; \
+ itr->p[1].i = itr->p[1].x ? itr->p[1].x->n : -1; \
+ ++itr->p; \
} \
+ if (itr->p == itr->stack) { \
+ itr->p = NULL; \
+ return 0; \
} \
- static inline int kb_itr_prev_##name(kbtree_##name##_t *b, kbitr_##name##_t *itr) \
- { \
- if (itr->p == NULL) return 0; \
- for (;;) { \
- while (itr->p->x && itr->p->i >= 0) { \
- itr->p[1].x = itr->p->x->is_internal? __KB_PTR(b, itr->p->x)[itr->p->i] : 0; \
- itr->p[1].i = itr->p[1].x ? itr->p[1].x->n : -1; \
- ++itr->p; \
- } \
- if (itr->p == itr->stack) { \
- itr->p = NULL; \
- return 0; \
- } \
- --itr->p; \
- --itr->p->i; \
- if (itr->p->x && itr->p->i >= 0) return 1; \
- } \
- } \
- static inline int kb_itr_getp_##name(kbtree_##name##_t *b, key_t * __restrict k, \
- kbitr_##name##_t *itr) \
- { \
- if (b->n_keys == 0) { \
- itr->p = NULL; \
- return 0; \
- } \
- int i, r = 0; \
- itr->p = itr->stack; \
- itr->p->x = b->root; \
- while (itr->p->x) { \
- i = __kb_getp_aux_##name(itr->p->x, k, &r); \
- itr->p->i = i; \
- if (i >= 0 && r == 0) return 1; \
- ++itr->p->i; \
- assert(itr->p->i <= 21); \
- itr->p[1].x = itr->p->x->is_internal? __KB_PTR(b, itr->p->x)[i + 1] : 0; \
- ++itr->p; \
- } \
- itr->p->i = 0; \
- return 0; \
- } \
- static inline int kb_itr_get_##name(kbtree_##name##_t *b, key_t k, kbitr_##name##_t *itr) \
- { \
- return kb_itr_getp_##name(b, &k, itr); \
- } \
- static inline void kb_del_itr_##name(kbtree_##name##_t *b, kbitr_##name##_t *itr) \
- { \
- key_t k = kb_itr_key(itr); \
- kb_delp_##name(b, &k); \
- kb_itr_getp_##name(b, &k, itr); \
- }
+ --itr->p; \
+ --itr->p->i; \
+ if (itr->p->x && itr->p->i >= 0) return 1; \
+ } \
+ } \
+ static inline int kb_itr_getp_##name(kbtree_##name##_t *b, key_t * __restrict k, \
+ kbitr_##name##_t *itr) \
+ { \
+ if (b->n_keys == 0) { \
+ itr->p = NULL; \
+ return 0; \
+ } \
+ int i, r = 0; \
+ itr->p = itr->stack; \
+ itr->p->x = b->root; \
+ while (itr->p->x) { \
+ i = __kb_getp_aux_##name(itr->p->x, k, &r); \
+ itr->p->i = i; \
+ if (i >= 0 && r == 0) return 1; \
+ ++itr->p->i; \
+ assert(itr->p->i <= 21); \
+ itr->p[1].x = itr->p->x->is_internal? __KB_PTR(b, itr->p->x)[i + 1] : 0; \
+ ++itr->p; \
+ } \
+ itr->p->i = 0; \
+ return 0; \
+ } \
+ static inline int kb_itr_get_##name(kbtree_##name##_t *b, key_t k, kbitr_##name##_t *itr) \
+ { \
+ return kb_itr_getp_##name(b, &k, itr); \
+ } \
+ static inline void kb_del_itr_##name(kbtree_##name##_t *b, kbitr_##name##_t *itr) \
+ { \
+ key_t k = kb_itr_key(itr); \
+ kb_delp_##name(b, &k); \
+ kb_itr_getp_##name(b, &k, itr); \
+ }
#define KBTREE_INIT(name, key_t, __cmp, T) \
KBTREE_INIT_IMPL(name, key_t, kbnode_##name##_t, __cmp, T, \
- (sizeof(kbnode_##name##_t)+(2*T)*sizeof(void *)))
+ (sizeof(kbnode_##name##_t) + (2*T)*sizeof(void *)))
#define KBTREE_INIT_IMPL(name, key_t, kbnode_t, __cmp, T, ILEN) \
__KB_TREE_T(name, key_t, T) \
diff --git a/src/nvim/lib/khash.h b/src/nvim/lib/khash.h
index e81db43038..57a41f9c13 100644
--- a/src/nvim/lib/khash.h
+++ b/src/nvim/lib/khash.h
@@ -113,7 +113,6 @@
* Added destructor
*/
-
#ifndef NVIM_LIB_KHASH_H
#define NVIM_LIB_KHASH_H
diff --git a/src/nvim/lib/klist.h b/src/nvim/lib/klist.h
index c8489e298b..a9abbc6dc2 100644
--- a/src/nvim/lib/klist.h
+++ b/src/nvim/lib/klist.h
@@ -32,7 +32,6 @@
#include "nvim/func_attr.h"
#include "nvim/memory.h"
-
#define KMEMPOOL_INIT(name, kmptype_t, kmpfree_f) \
typedef struct { \
size_t cnt, n, max; \
diff --git a/src/nvim/lib/kvec.h b/src/nvim/lib/kvec.h
index 34332ba34b..b5b3adf7d2 100644
--- a/src/nvim/lib/kvec.h
+++ b/src/nvim/lib/kvec.h
@@ -94,17 +94,26 @@
memcpy((v1).items, (v0).items, sizeof((v1).items[0]) * (v0).size); \
} while (0)
-#define kv_splice(v1, v0) \
+/// fit at least "len" more items
+#define kv_ensure_space(v, len) \
do { \
- if ((v1).capacity < (v1).size + (v0).size) { \
- (v1).capacity = (v1).size + (v0).size; \
- kv_roundup32((v1).capacity); \
- kv_resize((v1), (v1).capacity); \
+ if ((v).capacity < (v).size + len) { \
+ (v).capacity = (v).size + len; \
+ kv_roundup32((v).capacity); \
+ kv_resize((v), (v).capacity); \
} \
- memcpy((v1).items + (v1).size, (v0).items, sizeof((v1).items[0]) * (v0).size); \
- (v1).size = (v1).size + (v0).size; \
} while (0)
+#define kv_concat_len(v, data, len) \
+ do { \
+ kv_ensure_space(v, len); \
+ memcpy((v).items + (v).size, data, sizeof((v).items[0]) * len); \
+ (v).size = (v).size + len; \
+ } while (0)
+
+#define kv_concat(v, str) kv_concat_len(v, str, STRLEN(str))
+#define kv_splice(v1, v0) kv_concat_len(v1, (v0).items, (v0).size)
+
#define kv_pushp(v) \
((((v).size == (v).capacity) ? (kv_resize_full(v), 0) : 0), \
((v).items + ((v).size++)))
@@ -112,6 +121,8 @@
#define kv_push(v, x) \
(*kv_pushp(v) = (x))
+#define kv_pushp_c(v) ((v).items + ((v).size++))
+#define kv_push_c(v, x) (*kv_pushp_c(v) = (x))
#define kv_a(v, i) \
(*(((v).capacity <= (size_t)(i) \
@@ -123,6 +134,8 @@
: 0UL)), \
&(v).items[(i)]))
+#define kv_printf(v, ...) kv_do_printf(&(v), __VA_ARGS__)
+
/// Type of a vector with a few first members allocated on stack
///
/// Is compatible with #kv_A, #kv_pop, #kv_size, #kv_max, #kv_last.
diff --git a/src/nvim/lib/queue.h b/src/nvim/lib/queue.h
index e5574094bc..c6d3f74ec1 100644
--- a/src/nvim/lib/queue.h
+++ b/src/nvim/lib/queue.h
@@ -44,7 +44,6 @@ typedef struct _queue {
(q) = next; \
}
-
// ffi.cdef is unable to swallow `bool` in place of `int` here.
static inline int QUEUE_EMPTY(const QUEUE *const q)
FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
diff --git a/src/nvim/log.c b/src/nvim/log.c
index 5539e3d6c5..57c7c4758b 100644
--- a/src/nvim/log.c
+++ b/src/nvim/log.c
@@ -16,16 +16,19 @@
#include <uv.h>
#include "auto/config.h"
+#include "nvim/eval.h"
#include "nvim/log.h"
+#include "nvim/main.h"
+#include "nvim/message.h"
#include "nvim/os/os.h"
#include "nvim/os/time.h"
+#include "nvim/path.h"
#include "nvim/types.h"
-#define LOG_FILE_ENV "NVIM_LOG_FILE"
-
/// Cached location of the expanded log file path decided by log_path_init().
static char log_file_path[MAXPATHL + 1] = { 0 };
+static bool did_log_init = false;
static uv_mutex_t mutex;
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -49,36 +52,29 @@ static bool log_try_create(char *fname)
return true;
}
-/// Initializes path to log file. Sets $NVIM_LOG_FILE if empty.
-///
-/// Tries $NVIM_LOG_FILE, or falls back to $XDG_CACHE_HOME/nvim/log. Path to log
-/// file is cached, so only the first call has effect, unless first call was not
-/// successful. Failed initialization indicates either a bug in expand_env()
-/// or both $NVIM_LOG_FILE and $HOME environment variables are undefined.
+/// Initializes the log file path and sets $NVIM_LOG_FILE if empty.
///
-/// @return true if path was initialized, false otherwise.
-static bool log_path_init(void)
+/// Tries $NVIM_LOG_FILE, or falls back to $XDG_STATE_HOME/nvim/log. Failed
+/// initialization indicates either a bug in expand_env() or both $NVIM_LOG_FILE
+/// and $HOME environment variables are undefined.
+static void log_path_init(void)
{
- if (log_file_path[0]) {
- return true;
- }
size_t size = sizeof(log_file_path);
- expand_env((char_u *)"$" LOG_FILE_ENV, (char_u *)log_file_path,
- (int)size - 1);
- if (strequal("$" LOG_FILE_ENV, log_file_path)
+ expand_env((char_u *)"$" ENV_LOGFILE, (char_u *)log_file_path, (int)size - 1);
+ if (strequal("$" ENV_LOGFILE, log_file_path)
|| log_file_path[0] == '\0'
|| os_isdir((char_u *)log_file_path)
|| !log_try_create(log_file_path)) {
- // Make kXDGCacheHome if it does not exist.
- char *cachehome = get_xdg_home(kXDGCacheHome);
+ // Make $XDG_STATE_HOME if it does not exist.
+ char *loghome = get_xdg_home(kXDGStateHome);
char *failed_dir = NULL;
bool log_dir_failure = false;
- if (!os_isdir((char_u *)cachehome)) {
- log_dir_failure = (os_mkdir_recurse(cachehome, 0700, &failed_dir) != 0);
+ if (!os_isdir((char_u *)loghome)) {
+ log_dir_failure = (os_mkdir_recurse(loghome, 0700, &failed_dir) != 0);
}
- XFREE_CLEAR(cachehome);
+ XFREE_CLEAR(loghome);
// Invalid $NVIM_LOG_FILE or failed to expand; fall back to default.
- char *defaultpath = stdpaths_user_cache_subpath("log");
+ char *defaultpath = stdpaths_user_state_subpath("log", 0, true);
size_t len = xstrlcpy(log_file_path, defaultpath, size);
xfree(defaultpath);
// Fall back to .nvimlog
@@ -88,22 +84,23 @@ static bool log_path_init(void)
// Fall back to stderr
if (len >= size || !log_try_create(log_file_path)) {
log_file_path[0] = '\0';
- return false;
+ return;
}
- os_setenv(LOG_FILE_ENV, log_file_path, true);
+ os_setenv(ENV_LOGFILE, log_file_path, true);
if (log_dir_failure) {
WLOG("Failed to create directory %s for writing logs: %s",
failed_dir, os_strerror(log_dir_failure));
}
XFREE_CLEAR(failed_dir);
}
- return true;
}
void log_init(void)
{
- uv_mutex_init(&mutex);
+ uv_mutex_init_recursive(&mutex);
+ // AFTER init_homedir ("~", XDG) and set_init_1 (env vars). 22b52dd462e5 #11501
log_path_init();
+ did_log_init = true;
}
void log_lock(void)
@@ -124,10 +121,20 @@ void log_unlock(void)
/// @param line_num Source line number, or -1
/// @param eol Append linefeed "\n"
/// @param fmt printf-style format string
+///
+/// @return true if log was emitted normally, false if failed or recursive
bool logmsg(int log_level, const char *context, const char *func_name, int line_num, bool eol,
const char *fmt, ...)
FUNC_ATTR_UNUSED FUNC_ATTR_PRINTF(6, 7)
{
+ static bool recursive = false;
+ static bool did_msg = false; // Showed recursion message?
+ if (!did_log_init) {
+ g_stats.log_skip++;
+ // set_init_1 may try logging before we are ready. 6f27f5ef91b3 #10183
+ return false;
+ }
+
if (log_level < MIN_LOG_LEVEL) {
return false;
}
@@ -139,13 +146,19 @@ bool logmsg(int log_level, const char *context, const char *func_name, int line_
#endif
log_lock();
+ if (recursive) {
+ if (!did_msg) {
+ did_msg = true;
+ msg_schedule_semsg("E5430: %s:%d: recursive log!", func_name ? func_name : context, line_num);
+ }
+ g_stats.log_skip++;
+ log_unlock();
+ return false;
+ }
+ recursive = true;
bool ret = false;
FILE *log_file = open_log_file();
- if (log_file == NULL) {
- goto end;
- }
-
va_list args;
va_start(args, fmt);
ret = v_do_log_to_file(log_file, log_level, context, func_name, line_num,
@@ -155,7 +168,8 @@ bool logmsg(int log_level, const char *context, const char *func_name, int line_
if (log_file != stderr && log_file != stdout) {
fclose(log_file);
}
-end:
+
+ recursive = false;
log_unlock();
return ret;
}
@@ -166,51 +180,36 @@ void log_uv_handles(void *loop)
log_lock();
FILE *log_file = open_log_file();
- if (log_file == NULL) {
- goto end;
- }
-
uv_print_all_handles(l, log_file);
if (log_file != stderr && log_file != stdout) {
fclose(log_file);
}
-end:
+
log_unlock();
}
/// Open the log file for appending.
///
-/// @return FILE* decided by log_path_init() or stderr in case of error
+/// @return Log file, or stderr on failure
FILE *open_log_file(void)
{
- static bool opening_log_file = false;
- // Disallow recursion. (This only matters for log_path_init; for logmsg and
- // friends we use a mutex: log_lock).
- if (opening_log_file) {
- do_log_to_file(stderr, ERROR_LOG_LEVEL, NULL, __func__, __LINE__, true,
- "Cannot LOG() recursively.");
- return stderr;
- }
-
- FILE *log_file = NULL;
- opening_log_file = true;
- if (log_path_init()) {
- log_file = fopen(log_file_path, "a");
- }
- opening_log_file = false;
-
- if (log_file != NULL) {
- return log_file;
+ errno = 0;
+ if (log_file_path[0]) {
+ FILE *f = fopen(log_file_path, "a");
+ if (f != NULL) {
+ return f;
+ }
}
// May happen if:
- // - LOG() is called before early_init()
+ // - fopen() failed
+ // - LOG() is called before log_init()
// - Directory does not exist
// - File is not writable
- do_log_to_file(stderr, ERROR_LOG_LEVEL, NULL, __func__, __LINE__, true,
- "Logging to stderr, failed to open $" LOG_FILE_ENV ": %s",
- log_file_path);
+ do_log_to_file(stderr, LOGLVL_ERR, NULL, __func__, __LINE__, true,
+ "failed to open $" ENV_LOGFILE " (%s): %s",
+ strerror(errno), log_file_path);
return stderr;
}
@@ -237,8 +236,7 @@ void log_callstack_to_file(FILE *log_file, const char *const func_name, const in
// Now we have a command string like:
// addr2line -e /path/to/exe -f -p 0x123 0x456 ...
- do_log_to_file(log_file, DEBUG_LOG_LEVEL, NULL, func_name, line_num, true,
- "trace:");
+ do_log_to_file(log_file, LOGLVL_DBG, NULL, func_name, line_num, true, "trace:");
FILE *fp = popen(cmdbuf, "r");
char linebuf[IOSIZE];
while (fgets(linebuf, sizeof(linebuf) - 1, fp) != NULL) {
@@ -255,13 +253,7 @@ void log_callstack(const char *const func_name, const int line_num)
{
log_lock();
FILE *log_file = open_log_file();
- if (log_file == NULL) {
- goto end;
- }
-
log_callstack_to_file(log_file, func_name, line_num);
-
-end:
log_unlock();
}
#endif
@@ -282,14 +274,18 @@ static bool do_log_to_file(FILE *log_file, int log_level, const char *context,
static bool v_do_log_to_file(FILE *log_file, int log_level, const char *context,
const char *func_name, int line_num, bool eol, const char *fmt,
va_list args)
+ FUNC_ATTR_PRINTF(7, 0)
{
+ // Name of the Nvim instance that produced the log.
+ static char name[16] = { 0 };
+
static const char *log_levels[] = {
- [DEBUG_LOG_LEVEL] = "DEBUG",
- [INFO_LOG_LEVEL] = "INFO ",
- [WARN_LOG_LEVEL] = "WARN ",
- [ERROR_LOG_LEVEL] = "ERROR",
+ [LOGLVL_DBG] = "DBG",
+ [LOGLVL_INF] = "INF",
+ [LOGLVL_WRN] = "WRN",
+ [LOGLVL_ERR] = "ERR",
};
- assert(log_level >= DEBUG_LOG_LEVEL && log_level <= ERROR_LOG_LEVEL);
+ assert(log_level >= LOGLVL_DBG && log_level <= LOGLVL_ERR);
// Format the timestamp.
struct tm local_time;
@@ -297,8 +293,7 @@ static bool v_do_log_to_file(FILE *log_file, int log_level, const char *context,
return false;
}
char date_time[20];
- if (strftime(date_time, sizeof(date_time), "%Y-%m-%dT%H:%M:%S",
- &local_time) == 0) {
+ if (strftime(date_time, sizeof(date_time), "%Y-%m-%dT%H:%M:%S", &local_time) == 0) {
return false;
}
@@ -308,16 +303,37 @@ static bool v_do_log_to_file(FILE *log_file, int log_level, const char *context,
millis = (int)curtime.tv_usec / 1000;
}
+ // Get a name for this Nvim instance.
+ // TODO(justinmk): expose this as v:name ?
+ if (name[0] == '\0') {
+ // Parent servername.
+ const char *parent = path_tail(os_getenv(ENV_NVIM));
+ // Servername. Empty until starting=false.
+ const char *serv = path_tail(get_vim_var_str(VV_SEND_SERVER));
+ if (parent && parent[0] != NUL) {
+ snprintf(name, sizeof(name), "%s/c", parent); // "/c" indicates child.
+ } else if (serv[0] != NUL) {
+ snprintf(name, sizeof(name), "%s", serv);
+ } else {
+ int64_t pid = os_get_pid();
+ snprintf(name, sizeof(name), "?.%-5" PRId64, pid);
+ }
+ }
+
// Print the log message.
- int64_t pid = os_get_pid();
int rv = (line_num == -1 || func_name == NULL)
- ? fprintf(log_file, "%s %s.%03d %-5" PRId64 " %s",
- log_levels[log_level], date_time, millis, pid,
+ ? fprintf(log_file, "%s %s.%03d %-10s %s",
+ log_levels[log_level], date_time, millis, name,
(context == NULL ? "?:" : context))
- : fprintf(log_file, "%s %s.%03d %-5" PRId64 " %s%s:%d: ",
- log_levels[log_level], date_time, millis, pid,
+ : fprintf(log_file, "%s %s.%03d %-10s %s%s:%d: ",
+ log_levels[log_level], date_time, millis, name,
(context == NULL ? "" : context),
func_name, line_num);
+ if (name[0] == '?') {
+ // No v:servername yet. Clear `name` so that the next log can try again.
+ name[0] = '\0';
+ }
+
if (rv < 0) {
return false;
}
diff --git a/src/nvim/log.h b/src/nvim/log.h
index 81e39d1cdd..cbee0e0f81 100644
--- a/src/nvim/log.h
+++ b/src/nvim/log.h
@@ -16,12 +16,11 @@
# define NVIM_PROBE(name, n, ...)
#endif
-
-#define TRACE_LOG_LEVEL 0
-#define DEBUG_LOG_LEVEL 1
-#define INFO_LOG_LEVEL 2
-#define WARN_LOG_LEVEL 3
-#define ERROR_LOG_LEVEL 4
+#define LOGLVL_TRC 0
+#define LOGLVL_DBG 1
+#define LOGLVL_INF 2
+#define LOGLVL_WRN 3
+#define LOGLVL_ERR 4
#define DLOG(...)
#define DLOGN(...)
@@ -33,46 +32,37 @@
#define ELOGN(...)
#ifndef MIN_LOG_LEVEL
-# define MIN_LOG_LEVEL INFO_LOG_LEVEL
+# define MIN_LOG_LEVEL LOGLVL_INF
#endif
-#define LOG(level, ...) logmsg((level), NULL, __func__, __LINE__, true, \
- __VA_ARGS__)
+#define LOG(level, ...) logmsg((level), NULL, __func__, __LINE__, true, __VA_ARGS__)
-#if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL
+#if MIN_LOG_LEVEL <= LOGLVL_DBG
# undef DLOG
# undef DLOGN
-# define DLOG(...) logmsg(DEBUG_LOG_LEVEL, NULL, __func__, __LINE__, true, \
- __VA_ARGS__)
-# define DLOGN(...) logmsg(DEBUG_LOG_LEVEL, NULL, __func__, __LINE__, false, \
- __VA_ARGS__)
+# define DLOG(...) logmsg(LOGLVL_DBG, NULL, __func__, __LINE__, true, __VA_ARGS__)
+# define DLOGN(...) logmsg(LOGLVL_DBG, NULL, __func__, __LINE__, false, __VA_ARGS__)
#endif
-#if MIN_LOG_LEVEL <= INFO_LOG_LEVEL
+#if MIN_LOG_LEVEL <= LOGLVL_INF
# undef ILOG
# undef ILOGN
-# define ILOG(...) logmsg(INFO_LOG_LEVEL, NULL, __func__, __LINE__, true, \
- __VA_ARGS__)
-# define ILOGN(...) logmsg(INFO_LOG_LEVEL, NULL, __func__, __LINE__, false, \
- __VA_ARGS__)
+# define ILOG(...) logmsg(LOGLVL_INF, NULL, __func__, __LINE__, true, __VA_ARGS__)
+# define ILOGN(...) logmsg(LOGLVL_INF, NULL, __func__, __LINE__, false, __VA_ARGS__)
#endif
-#if MIN_LOG_LEVEL <= WARN_LOG_LEVEL
+#if MIN_LOG_LEVEL <= LOGLVL_WRN
# undef WLOG
# undef WLOGN
-# define WLOG(...) logmsg(WARN_LOG_LEVEL, NULL, __func__, __LINE__, true, \
- __VA_ARGS__)
-# define WLOGN(...) logmsg(WARN_LOG_LEVEL, NULL, __func__, __LINE__, false, \
- __VA_ARGS__)
+# define WLOG(...) logmsg(LOGLVL_WRN, NULL, __func__, __LINE__, true, __VA_ARGS__)
+# define WLOGN(...) logmsg(LOGLVL_WRN, NULL, __func__, __LINE__, false, __VA_ARGS__)
#endif
-#if MIN_LOG_LEVEL <= ERROR_LOG_LEVEL
+#if MIN_LOG_LEVEL <= LOGLVL_ERR
# undef ELOG
# undef ELOGN
-# define ELOG(...) logmsg(ERROR_LOG_LEVEL, NULL, __func__, __LINE__, true, \
- __VA_ARGS__)
-# define ELOGN(...) logmsg(ERROR_LOG_LEVEL, NULL, __func__, __LINE__, false, \
- __VA_ARGS__)
+# define ELOG(...) logmsg(LOGLVL_ERR, NULL, __func__, __LINE__, true, __VA_ARGS__)
+# define ELOGN(...) logmsg(LOGLVL_ERR, NULL, __func__, __LINE__, false, __VA_ARGS__)
#endif
#ifdef HAVE_EXECINFO_BACKTRACE
diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c
index 8a702ddd60..4d6e6090b8 100644
--- a/src/nvim/lua/converter.c
+++ b/src/nvim/lua/converter.c
@@ -49,7 +49,6 @@ typedef struct {
#define LUA_PUSH_STATIC_STRING(lstate, s) \
lua_pushlstring(lstate, s, sizeof(s) - 1)
-
static LuaTableProps nlua_traverse_table(lua_State *const lstate)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
@@ -156,7 +155,7 @@ static LuaTableProps nlua_traverse_table(lua_State *const lstate)
&& ret.string_keys_num == 0)) {
ret.type = kObjectTypeArray;
if (tsize == 0 && lua_getmetatable(lstate, -1)) {
- nlua_pushref(lstate, nlua_empty_dict_ref);
+ nlua_pushref(lstate, nlua_global_refs->empty_dict_ref);
if (lua_rawequal(lstate, -2, -1)) {
ret.type = kObjectTypeDictionary;
}
@@ -286,9 +285,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv)
break;
case LUA_TBOOLEAN:
cur.tv->v_type = VAR_BOOL;
- cur.tv->vval.v_bool = (lua_toboolean(lstate, -1)
- ? kBoolVarTrue
- : kBoolVarFalse);
+ cur.tv->vval.v_bool = (lua_toboolean(lstate, -1) ? kBoolVarTrue : kBoolVarFalse);
break;
case LUA_TSTRING: {
size_t len;
@@ -316,7 +313,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv)
LuaRef table_ref = LUA_NOREF;
if (lua_getmetatable(lstate, -1)) {
lua_pop(lstate, 1);
- table_ref = nlua_ref(lstate, -1);
+ table_ref = nlua_ref_global(lstate, -1);
}
const LuaTableProps table_props = nlua_traverse_table(lstate);
@@ -389,19 +386,19 @@ nlua_pop_typval_table_processing_end:
}
case LUA_TFUNCTION: {
LuaCFunctionState *state = xmalloc(sizeof(LuaCFunctionState));
- state->lua_callable.func_ref = nlua_ref(lstate, -1);
+ state->lua_callable.func_ref = nlua_ref_global(lstate, -1);
char_u *name = register_cfunc(&nlua_CFunction_func_call,
&nlua_CFunction_func_free,
state);
cur.tv->v_type = VAR_FUNC;
- cur.tv->vval.v_string = vim_strsave(name);
+ cur.tv->vval.v_string = (char *)vim_strsave(name);
break;
}
case LUA_TUSERDATA: {
// TODO(bfredl): check mt.__call and convert to function?
- nlua_pushref(lstate, nlua_nil_ref);
+ nlua_pushref(lstate, nlua_global_refs->nil_ref);
bool is_nil = lua_rawequal(lstate, -2, -1);
lua_pop(lstate, 1);
if (is_nil) {
@@ -445,7 +442,7 @@ static bool typval_conv_special = false;
if (typval_conv_special) { \
lua_pushnil(lstate); \
} else { \
- nlua_pushref(lstate, nlua_nil_ref); \
+ nlua_pushref(lstate, nlua_global_refs->nil_ref); \
} \
} while (0)
@@ -478,7 +475,12 @@ static bool typval_conv_special = false;
#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
do { \
- TYPVAL_ENCODE_CONV_NIL(tv); \
+ ufunc_T *fp = find_func(fun); \
+ if (fp != NULL && fp->uf_cb == nlua_CFunction_func_call) { \
+ nlua_pushref(lstate, ((LuaCFunctionState *)fp->uf_cb_state)->lua_callable.func_ref); \
+ } else { \
+ TYPVAL_ENCODE_CONV_NIL(tv); \
+ } \
goto typval_encode_stop_converting_one_item; \
} while (0)
@@ -495,7 +497,7 @@ static bool typval_conv_special = false;
nlua_create_typed_table(lstate, 0, 0, kObjectTypeDictionary); \
} else { \
lua_createtable(lstate, 0, 0); \
- nlua_pushref(lstate, nlua_empty_dict_ref); \
+ nlua_pushref(lstate, nlua_global_refs->empty_dict_ref); \
lua_setmetatable(lstate, -2); \
} \
} while (0)
@@ -551,8 +553,8 @@ static bool typval_conv_special = false;
const MPConvStackVal mpval = kv_A(*mpstack, backref - 1); \
if (mpval.type == conv_type) { \
if (conv_type == kMPConvDict \
- ? (void *)mpval.data.d.dict == (void *)(val) \
- : (void *)mpval.data.l.list == (void *)(val)) { \
+ ? (void *)mpval.data.d.dict == (void *)(val) \
+ : (void *)mpval.data.l.list == (void *)(val)) { \
lua_pushvalue(lstate, \
-((int)((kv_size(*mpstack) - backref + 1) * 2))); \
break; \
@@ -671,7 +673,6 @@ static inline void nlua_create_typed_table(lua_State *lstate, const size_t narr,
lua_rawset(lstate, -3);
}
-
/// Convert given String to lua string
///
/// Leaves converted string on top of the stack.
@@ -726,7 +727,7 @@ void nlua_push_Dictionary(lua_State *lstate, const Dictionary dict, bool special
} else {
lua_createtable(lstate, 0, (int)dict.size);
if (dict.size == 0 && !special) {
- nlua_pushref(lstate, nlua_empty_dict_ref);
+ nlua_pushref(lstate, nlua_global_refs->empty_dict_ref);
lua_setmetatable(lstate, -2);
}
}
@@ -774,7 +775,7 @@ void nlua_push_Object(lua_State *lstate, const Object obj, bool special)
if (special) {
lua_pushnil(lstate);
} else {
- nlua_pushref(lstate, nlua_nil_ref);
+ nlua_pushref(lstate, nlua_global_refs->nil_ref);
}
break;
case kObjectTypeLuaRef: {
@@ -805,7 +806,6 @@ void nlua_push_Object(lua_State *lstate, const Object obj, bool special)
}
}
-
/// Convert lua value to string
///
/// Always pops one value from the stack.
@@ -1125,10 +1125,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err)
case LUA_TSTRING: {
size_t len;
const char *s = lua_tolstring(lstate, -1, &len);
- *cur.obj = STRING_OBJ(((String) {
- .data = xmemdupz(s, len),
- .size = len,
- }));
+ *cur.obj = STRING_OBJ(((String) { .data = xmemdupz(s, len), .size = len }));
break;
}
case LUA_TNUMBER: {
@@ -1146,11 +1143,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err)
switch (table_props.type) {
case kObjectTypeArray:
- *cur.obj = ARRAY_OBJ(((Array) {
- .items = NULL,
- .size = 0,
- .capacity = 0,
- }));
+ *cur.obj = ARRAY_OBJ(((Array) { .items = NULL, .size = 0, .capacity = 0 }));
if (table_props.maxidx != 0) {
cur.obj->data.array.items =
xcalloc(table_props.maxidx,
@@ -1161,11 +1154,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err)
}
break;
case kObjectTypeDictionary:
- *cur.obj = DICTIONARY_OBJ(((Dictionary) {
- .items = NULL,
- .size = 0,
- .capacity = 0,
- }));
+ *cur.obj = DICTIONARY_OBJ(((Dictionary) { .items = NULL, .size = 0, .capacity = 0 }));
if (table_props.string_keys_num != 0) {
cur.obj->data.dictionary.items =
xcalloc(table_props.string_keys_num,
@@ -1191,14 +1180,14 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err)
case LUA_TFUNCTION:
if (ref) {
- *cur.obj = LUAREF_OBJ(nlua_ref(lstate, -1));
+ *cur.obj = LUAREF_OBJ(nlua_ref_global(lstate, -1));
} else {
goto type_error;
}
break;
case LUA_TUSERDATA: {
- nlua_pushref(lstate, nlua_nil_ref);
+ nlua_pushref(lstate, nlua_global_refs->nil_ref);
bool is_nil = lua_rawequal(lstate, -2, -1);
lua_pop(lstate, 1);
if (is_nil) {
@@ -1232,7 +1221,7 @@ type_error:
LuaRef nlua_pop_LuaRef(lua_State *const lstate, Error *err)
{
- LuaRef rv = nlua_ref(lstate, -1);
+ LuaRef rv = nlua_ref_global(lstate, -1);
lua_pop(lstate, 1);
return rv;
}
@@ -1244,7 +1233,7 @@ LuaRef nlua_pop_LuaRef(lua_State *const lstate, Error *err)
type ret; \
if (lua_type(lstate, -1) != LUA_TNUMBER) { \
api_set_error(err, kErrorTypeValidation, "Expected Lua number"); \
- ret = (type)-1; \
+ ret = (type) - 1; \
} else { \
ret = (type)lua_tonumber(lstate, -1); \
} \
@@ -1305,7 +1294,6 @@ void nlua_init_types(lua_State *const lstate)
lua_rawset(lstate, -3);
}
-
void nlua_pop_keydict(lua_State *L, void *retval, field_hash hashy, Error *err)
{
if (!lua_istable(L, -1)) {
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index cfdbe7b344..ad03ebd1ed 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"
@@ -14,6 +15,7 @@
#include "nvim/buffer_defs.h"
#include "nvim/change.h"
#include "nvim/cursor.h"
+#include "nvim/eval/typval.h"
#include "nvim/eval/userfunc.h"
#include "nvim/event/loop.h"
#include "nvim/event/time.h"
@@ -34,21 +36,33 @@
#include "nvim/message.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/os/os.h"
+#include "nvim/profile.h"
#include "nvim/screen.h"
#include "nvim/undo.h"
#include "nvim/version.h"
#include "nvim/vim.h"
+#include "nvim/window.h"
static int in_fast_callback = 0;
// Initialized in nlua_init().
static lua_State *global_lstate = NULL;
+static LuaRef require_ref = LUA_REFNIL;
+
+static uv_thread_t main_thread;
+
typedef struct {
Error err;
String lua_err_str;
} LuaError;
+typedef struct {
+ char *name;
+ const uint8_t *data;
+ size_t size;
+} ModuleDef;
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "lua/executor.c.generated.h"
# include "lua/vim_module.generated.h"
@@ -64,11 +78,16 @@ typedef struct {
}
#if __has_feature(address_sanitizer)
-static PMap(handle_T) nlua_ref_markers = MAP_INIT;
static bool nlua_track_refs = false;
# define NLUA_TRACK_REFS
#endif
+typedef enum luv_err_type {
+ kCallback,
+ kThread,
+ kThreadCallback,
+} luv_err_t;
+
/// Convert lua error into a Vim error message
///
/// @param lstate Lua interpreter state.
@@ -77,7 +96,22 @@ static void nlua_error(lua_State *const lstate, const char *const msg)
FUNC_ATTR_NONNULL_ALL
{
size_t len;
- const char *const str = lua_tolstring(lstate, -1, &len);
+ const char *str = NULL;
+
+ if (luaL_getmetafield(lstate, -1, "__tostring")) {
+ if (lua_isfunction(lstate, -1) && luaL_callmeta(lstate, -2, "__tostring")) {
+ // call __tostring, convert the result and pop result.
+ str = lua_tolstring(lstate, -1, &len);
+ lua_pop(lstate, 1);
+ }
+ // pop __tostring.
+ lua_pop(lstate, 1);
+ }
+
+ if (!str) {
+ // defer to lua default conversion, this will render tables as [NULL].
+ str = lua_tolstring(lstate, -1, &len);
+ }
msg_ext_set_kind("lua_error");
semsg_multiline(msg, (int)len, str);
@@ -105,7 +139,6 @@ static int nlua_pcall(lua_State *lstate, int nargs, int nresults)
return status;
}
-
/// Gets the version of the current Nvim build.
///
/// @param lstate Lua interpreter state.
@@ -117,12 +150,24 @@ static int nlua_nvim_version(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
return 1;
}
-
static void nlua_luv_error_event(void **argv)
{
char *error = (char *)argv[0];
+ luv_err_t type = (luv_err_t)(intptr_t)argv[1];
msg_ext_set_kind("lua_error");
- semsg_multiline("Error executing luv callback:\n%s", error);
+ switch (type) {
+ case kCallback:
+ semsg_multiline("Error executing luv callback:\n%s", error);
+ break;
+ case kThread:
+ semsg_multiline("Error in luv thread:\n%s", error);
+ break;
+ case kThreadCallback:
+ semsg_multiline("Error in luv callback, thread:\n%s", error);
+ break;
+ default:
+ break;
+ }
xfree(error);
}
@@ -147,7 +192,7 @@ static int nlua_luv_cfpcall(lua_State *lstate, int nargs, int nresult, int flags
const char *error = lua_tostring(lstate, -1);
multiqueue_put(main_loop.events, nlua_luv_error_event,
- 1, xstrdup(error));
+ 2, xstrdup(error), (intptr_t)kCallback);
lua_pop(lstate, 1); // error message
retval = -status;
} else { // LUA_OK
@@ -161,12 +206,109 @@ static int nlua_luv_cfpcall(lua_State *lstate, int nargs, int nresult, int flags
return retval;
}
+static int nlua_luv_thread_cb_cfpcall(lua_State *lstate, int nargs, int nresult, int flags)
+{
+ return nlua_luv_thread_common_cfpcall(lstate, nargs, nresult, flags, true);
+}
+
+static int nlua_luv_thread_cfpcall(lua_State *lstate, int nargs, int nresult, int flags)
+ FUNC_ATTR_NONNULL_ALL
+{
+ return nlua_luv_thread_common_cfpcall(lstate, nargs, nresult, flags, false);
+}
+
+static int nlua_luv_thread_cfcpcall(lua_State *lstate, lua_CFunction func, void *ud, int flags)
+ FUNC_ATTR_NONNULL_ARG(1, 2)
+{
+ lua_pushcfunction(lstate, func);
+ lua_pushlightuserdata(lstate, ud);
+ int retval = nlua_luv_thread_cfpcall(lstate, 1, 0, flags);
+ return retval;
+}
+
+static int nlua_luv_thread_common_cfpcall(lua_State *lstate, int nargs, int nresult, int flags,
+ bool is_callback)
+ FUNC_ATTR_NONNULL_ALL
+{
+ int retval;
+
+ int top = lua_gettop(lstate);
+ int status = lua_pcall(lstate, nargs, nresult, 0);
+ if (status) {
+ if (status == LUA_ERRMEM && !(flags & LUVF_CALLBACK_NOEXIT)) {
+ // Terminate this thread, as the main thread may be able to continue
+ // execution.
+ mch_errmsg(e_outofmem);
+ mch_errmsg("\n");
+ lua_close(lstate);
+#ifdef WIN32
+ ExitThread(0);
+#else
+ pthread_exit(0);
+#endif
+ }
+ const char *error = lua_tostring(lstate, -1);
+
+ loop_schedule_deferred(&main_loop,
+ event_create(nlua_luv_error_event, 2,
+ xstrdup(error),
+ is_callback
+ ? (intptr_t)kThreadCallback
+ : (intptr_t)kThread));
+ lua_pop(lstate, 1); // error message
+ retval = -status;
+ } else { // LUA_OK
+ if (nresult == LUA_MULTRET) {
+ nresult = lua_gettop(lstate) - top + nargs + 1;
+ }
+ retval = nresult;
+ }
+
+ return retval;
+}
+
+static int nlua_thr_api_nvim__get_runtime(lua_State *lstate)
+{
+ if (lua_gettop(lstate) != 3) {
+ return luaL_error(lstate, "Expected 3 arguments");
+ }
+
+ luaL_checktype(lstate, -1, LUA_TTABLE);
+ lua_getfield(lstate, -1, "is_lua");
+ if (!lua_isboolean(lstate, -1)) {
+ return luaL_error(lstate, "is_lua is not a boolean");
+ }
+ bool is_lua = lua_toboolean(lstate, -1);
+ lua_pop(lstate, 2);
+
+ luaL_checktype(lstate, -1, LUA_TBOOLEAN);
+ bool all = lua_toboolean(lstate, -1);
+ lua_pop(lstate, 1);
+
+ Error err = ERROR_INIT;
+ const Array pat = nlua_pop_Array(lstate, &err);
+ if (ERROR_SET(&err)) {
+ luaL_where(lstate, 1);
+ lua_pushstring(lstate, err.msg);
+ api_clear_error(&err);
+ lua_concat(lstate, 2);
+ return lua_error(lstate);
+ }
+
+ ArrayOf(String) ret = runtime_get_named_thread(is_lua, pat, all);
+ nlua_push_Array(lstate, ret, true);
+ api_free_array(ret);
+ api_free_array(pat);
+
+ return 1;
+}
+
static void nlua_schedule_event(void **argv)
{
LuaRef cb = (LuaRef)(ptrdiff_t)argv[0];
lua_State *const lstate = global_lstate;
nlua_pushref(lstate, cb);
- nlua_unref(lstate, cb);
+ nlua_unref_global(lstate, cb);
if (nlua_pcall(lstate, 0, 0)) {
nlua_error(lstate, _("Error executing vim.schedule lua callback: %.*s"));
}
@@ -183,7 +325,7 @@ static int nlua_schedule(lua_State *const lstate)
return lua_error(lstate);
}
- LuaRef cb = nlua_ref(lstate, 1);
+ LuaRef cb = nlua_ref_global(lstate, 1);
multiqueue_put(main_loop.events, nlua_schedule_event,
1, (void *)(ptrdiff_t)cb);
@@ -192,8 +334,7 @@ static int nlua_schedule(lua_State *const lstate)
// Dummy timer callback. Used by f_wait().
static void dummy_timer_due_cb(TimeWatcher *tw, void *data)
-{
-}
+{}
// Dummy timer close callback. Used by f_wait().
static void dummy_timer_close_cb(TimeWatcher *tw, void *data)
@@ -219,7 +360,7 @@ static int nlua_wait(lua_State *lstate)
{
intptr_t timeout = luaL_checkinteger(lstate, 1);
if (timeout < 0) {
- return luaL_error(lstate, "timeout must be > 0");
+ return luaL_error(lstate, "timeout must be >= 0");
}
int lua_top = lua_gettop(lstate);
@@ -246,7 +387,7 @@ static int nlua_wait(lua_State *lstate)
if (lua_top >= 3 && !lua_isnil(lstate, 3)) {
interval = luaL_checkinteger(lstate, 3);
if (interval < 0) {
- return luaL_error(lstate, "interval must be > 0");
+ return luaL_error(lstate, "interval must be >= 0");
}
}
@@ -301,10 +442,154 @@ static int nlua_wait(lua_State *lstate)
return 2;
}
+static nlua_ref_state_t *nlua_new_ref_state(lua_State *lstate, bool is_thread)
+ FUNC_ATTR_NONNULL_ALL
+{
+ nlua_ref_state_t *ref_state = lua_newuserdata(lstate, sizeof(*ref_state));
+ memset(ref_state, 0, sizeof(*ref_state));
+ ref_state->nil_ref = LUA_NOREF;
+ ref_state->empty_dict_ref = LUA_NOREF;
+ if (!is_thread) {
+ nlua_global_refs = ref_state;
+ }
+ return ref_state;
+}
+
+static nlua_ref_state_t *nlua_get_ref_state(lua_State *lstate)
+ FUNC_ATTR_NONNULL_ALL
+{
+ lua_getfield(lstate, LUA_REGISTRYINDEX, "nlua.ref_state");
+ nlua_ref_state_t *ref_state = lua_touserdata(lstate, -1);
+ lua_pop(lstate, 1);
+
+ return ref_state;
+}
+
+LuaRef nlua_get_nil_ref(lua_State *lstate)
+ FUNC_ATTR_NONNULL_ALL
+{
+ nlua_ref_state_t *ref_state = nlua_get_ref_state(lstate);
+ return ref_state->nil_ref;
+}
+
+LuaRef nlua_get_empty_dict_ref(lua_State *lstate)
+ FUNC_ATTR_NONNULL_ALL
+{
+ nlua_ref_state_t *ref_state = nlua_get_ref_state(lstate);
+ return ref_state->empty_dict_ref;
+}
+
+int nlua_get_global_ref_count(void)
+{
+ return nlua_global_refs->ref_count;
+}
+
+static void nlua_common_vim_init(lua_State *lstate, bool is_thread)
+ FUNC_ATTR_NONNULL_ARG(1)
+{
+ nlua_ref_state_t *ref_state = nlua_new_ref_state(lstate, is_thread);
+ lua_setfield(lstate, LUA_REGISTRYINDEX, "nlua.ref_state");
+
+ // vim.is_thread
+ lua_pushboolean(lstate, is_thread);
+ lua_setfield(lstate, LUA_REGISTRYINDEX, "nvim.thread");
+ lua_pushcfunction(lstate, &nlua_is_thread);
+ lua_setfield(lstate, -2, "is_thread");
+
+ // vim.NIL
+ lua_newuserdata(lstate, 0);
+ lua_createtable(lstate, 0, 0);
+ lua_pushcfunction(lstate, &nlua_nil_tostring);
+ lua_setfield(lstate, -2, "__tostring");
+ lua_setmetatable(lstate, -2);
+ ref_state->nil_ref = nlua_ref(lstate, ref_state, -1);
+ lua_pushvalue(lstate, -1);
+ lua_setfield(lstate, LUA_REGISTRYINDEX, "mpack.NIL");
+ lua_setfield(lstate, -2, "NIL");
+
+ // vim._empty_dict_mt
+ lua_createtable(lstate, 0, 0);
+ lua_pushcfunction(lstate, &nlua_empty_dict_tostring);
+ lua_setfield(lstate, -2, "__tostring");
+ ref_state->empty_dict_ref = nlua_ref(lstate, ref_state, -1);
+ lua_pushvalue(lstate, -1);
+ lua_setfield(lstate, LUA_REGISTRYINDEX, "mpack.empty_dict");
+ lua_setfield(lstate, -2, "_empty_dict_mt");
+
+ // vim.loop
+ if (is_thread) {
+ luv_set_callback(lstate, nlua_luv_thread_cb_cfpcall);
+ luv_set_thread(lstate, nlua_luv_thread_cfpcall);
+ luv_set_cthread(lstate, nlua_luv_thread_cfcpcall);
+ } else {
+ luv_set_loop(lstate, &main_loop.uv);
+ luv_set_callback(lstate, nlua_luv_cfpcall);
+ }
+ luaopen_luv(lstate);
+ lua_pushvalue(lstate, -1);
+ lua_setfield(lstate, -3, "loop");
+
+ // package.loaded.luv = vim.loop
+ // otherwise luv will be reinitialized when require'luv'
+ lua_getglobal(lstate, "package");
+ lua_getfield(lstate, -1, "loaded");
+ lua_pushvalue(lstate, -3);
+ lua_setfield(lstate, -2, "luv");
+ lua_pop(lstate, 3);
+}
+
+static int nlua_module_preloader(lua_State *lstate)
+{
+ size_t i = (size_t)lua_tointeger(lstate, lua_upvalueindex(1));
+ ModuleDef def = builtin_modules[i];
+ char name[256];
+ name[0] = '@';
+ size_t off = xstrlcpy(name + 1, def.name, (sizeof name) - 2);
+ strchrsub(name + 1, '.', '/');
+ xstrlcpy(name + 1 + off, ".lua", (sizeof name) - 2 - off);
+
+ if (luaL_loadbuffer(lstate, (const char *)def.data, def.size - 1, name)) {
+ return lua_error(lstate);
+ }
+
+ lua_call(lstate, 0, 1); // propagates error to caller
+ return 1;
+}
+
+static bool nlua_init_packages(lua_State *lstate)
+ FUNC_ATTR_NONNULL_ALL
+{
+ // put builtin packages in preload
+ lua_getglobal(lstate, "package"); // [package]
+ lua_getfield(lstate, -1, "preload"); // [package, preload]
+ for (size_t i = 0; i < ARRAY_SIZE(builtin_modules); i++) {
+ ModuleDef def = builtin_modules[i];
+ lua_pushinteger(lstate, (long)i); // [package, preload, i]
+ lua_pushcclosure(lstate, nlua_module_preloader, 1); // [package, preload, cclosure]
+ lua_setfield(lstate, -2, def.name); // [package, preload]
+
+ if (nlua_disable_preload && strequal(def.name, "vim.inspect")) {
+ break;
+ }
+ }
+
+ lua_pop(lstate, 2); // []
+
+ lua_getglobal(lstate, "require");
+ lua_pushstring(lstate, "vim._init_packages");
+ if (nlua_pcall(lstate, 1, 0)) {
+ mch_errmsg(lua_tostring(lstate, -1));
+ mch_errmsg("\n");
+ return false;
+ }
+
+ return true;
+}
+
/// Initialize lua interpreter state
///
/// Called by lua interpreter itself to initialize state.
-static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
+static bool nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
{
// print
lua_pushcfunction(lstate, &nlua_print);
@@ -361,117 +646,30 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
lua_pushcfunction(lstate, &nlua_wait);
lua_setfield(lstate, -2, "wait");
- // vim.NIL
- lua_newuserdata(lstate, 0);
- lua_createtable(lstate, 0, 0);
- lua_pushcfunction(lstate, &nlua_nil_tostring);
- lua_setfield(lstate, -2, "__tostring");
- lua_setmetatable(lstate, -2);
- nlua_nil_ref = nlua_ref(lstate, -1);
- lua_pushvalue(lstate, -1);
- lua_setfield(lstate, LUA_REGISTRYINDEX, "mpack.NIL");
- lua_setfield(lstate, -2, "NIL");
+ nlua_common_vim_init(lstate, false);
- // vim._empty_dict_mt
- lua_createtable(lstate, 0, 0);
- lua_pushcfunction(lstate, &nlua_empty_dict_tostring);
- lua_setfield(lstate, -2, "__tostring");
- nlua_empty_dict_ref = nlua_ref(lstate, -1);
- lua_pushvalue(lstate, -1);
- lua_setfield(lstate, LUA_REGISTRYINDEX, "mpack.empty_dict");
- lua_setfield(lstate, -2, "_empty_dict_mt");
+ // patch require() (only for --startuptime)
+ if (time_fd != NULL) {
+ lua_getglobal(lstate, "require");
+ // Must do this after nlua_common_vim_init where nlua_global_refs is initialized.
+ require_ref = nlua_ref_global(lstate, -1);
+ lua_pop(lstate, 1);
+ lua_pushcfunction(lstate, &nlua_require);
+ lua_setglobal(lstate, "require");
+ }
// internal vim._treesitter... API
nlua_add_treesitter(lstate);
- // vim.loop
- luv_set_loop(lstate, &main_loop.uv);
- luv_set_callback(lstate, nlua_luv_cfpcall);
- luaopen_luv(lstate);
- lua_pushvalue(lstate, -1);
- lua_setfield(lstate, -3, "loop");
-
- // package.loaded.luv = vim.loop
- // otherwise luv will be reinitialized when require'luv'
- lua_getglobal(lstate, "package");
- lua_getfield(lstate, -1, "loaded");
- lua_pushvalue(lstate, -3);
- lua_setfield(lstate, -2, "luv");
- lua_pop(lstate, 3);
-
- nlua_state_add_stdlib(lstate);
+ nlua_state_add_stdlib(lstate, false);
lua_setglobal(lstate, "vim");
- {
- const char *code = (char *)&shared_module[0];
- if (luaL_loadbuffer(lstate, code, sizeof(shared_module) - 1, "@vim/shared.lua")
- || nlua_pcall(lstate, 0, 0)) {
- nlua_error(lstate, _("E5106: Error while creating shared module: %.*s\n"));
- return 1;
- }
- }
-
- {
- lua_getglobal(lstate, "package"); // [package]
- lua_getfield(lstate, -1, "loaded"); // [package, loaded]
-
- const char *code = (char *)&inspect_module[0];
- if (luaL_loadbuffer(lstate, code, sizeof(inspect_module) - 1, "@vim/inspect.lua")
- || nlua_pcall(lstate, 0, 1)) {
- nlua_error(lstate, _("E5106: Error while creating inspect module: %.*s\n"));
- return 1;
- }
- // [package, loaded, inspect]
- lua_setfield(lstate, -2, "vim.inspect"); // [package, loaded]
-
- code = (char *)&lua_F_module[0];
- if (luaL_loadbuffer(lstate, code, sizeof(lua_F_module) - 1, "@vim/F.lua")
- || nlua_pcall(lstate, 0, 1)) {
- nlua_error(lstate, _("E5106: Error while creating vim.F module: %.*s\n"));
- return 1;
- }
- // [package, loaded, module]
- lua_setfield(lstate, -2, "vim.F"); // [package, loaded]
-
- code = (char *)&lua_filetype_module[0];
- if (luaL_loadbuffer(lstate, code, sizeof(lua_filetype_module) - 1, "@vim/filetype.lua")
- || nlua_pcall(lstate, 0, 1)) {
- nlua_error(lstate, _("E5106: Error while creating vim.filetype module: %.*s"));
- return 1;
- }
- // [package, loaded, module]
- lua_setfield(lstate, -2, "vim.filetype"); // [package, loaded]
-
- lua_pop(lstate, 2); // []
- }
-
- {
- const char *code = (char *)&vim_module[0];
- if (luaL_loadbuffer(lstate, code, sizeof(vim_module) - 1, "@vim.lua")
- || nlua_pcall(lstate, 0, 0)) {
- nlua_error(lstate, _("E5106: Error while creating vim module: %.*s\n"));
- return 1;
- }
- }
-
- {
- lua_getglobal(lstate, "package"); // [package]
- lua_getfield(lstate, -1, "loaded"); // [package, loaded]
-
- const char *code = (char *)&lua_meta_module[0];
- if (luaL_loadbuffer(lstate, code, sizeof(lua_meta_module) - 1, "@vim/_meta.lua")
- || nlua_pcall(lstate, 0, 1)) {
- nlua_error(lstate, _("E5106: Error while creating vim._meta module: %.*s\n"));
- return 1;
- }
- // [package, loaded, module]
- lua_setfield(lstate, -2, "vim._meta"); // [package, loaded]
-
- lua_pop(lstate, 2); // []
+ if (!nlua_init_packages(lstate)) {
+ return false;
}
- return 0;
+ return true;
}
/// Initialize global lua interpreter
@@ -488,15 +686,66 @@ void nlua_init(void)
lua_State *lstate = luaL_newstate();
if (lstate == NULL) {
- emsg(_("E970: Failed to initialize lua interpreter"));
- preserve_exit();
+ mch_errmsg(_("E970: Failed to initialize lua interpreter\n"));
+ os_exit(1);
}
luaL_openlibs(lstate);
- nlua_state_init(lstate);
+ if (!nlua_state_init(lstate)) {
+ mch_errmsg(_("E970: Failed to initialize builtin lua modules\n"));
+ os_exit(1);
+ }
+
+ luv_set_thread_cb(nlua_thread_acquire_vm, nlua_common_free_all_mem);
global_lstate = lstate;
+
+ main_thread = uv_thread_self();
}
+static lua_State *nlua_thread_acquire_vm(void)
+{
+ // If it is called from the main thread, it will attempt to rebuild the cache.
+ const uv_thread_t self = uv_thread_self();
+ if (uv_thread_equal(&main_thread, &self)) {
+ runtime_search_path_validate();
+ }
+
+ lua_State *lstate = luaL_newstate();
+
+ // Add in the lua standard libraries
+ luaL_openlibs(lstate);
+
+ // print
+ lua_pushcfunction(lstate, &nlua_print);
+ lua_setglobal(lstate, "print");
+
+ lua_pushinteger(lstate, 0);
+ lua_setfield(lstate, LUA_REGISTRYINDEX, "nlua.refcount");
+
+ // vim
+ lua_newtable(lstate);
+
+ nlua_common_vim_init(lstate, true);
+
+ nlua_state_add_stdlib(lstate, true);
+
+ lua_createtable(lstate, 0, 0);
+ lua_pushcfunction(lstate, nlua_thr_api_nvim__get_runtime);
+ lua_setfield(lstate, -2, "nvim__get_runtime");
+ lua_setfield(lstate, -2, "api");
+
+ lua_setglobal(lstate, "vim");
+
+ nlua_init_packages(lstate);
+
+ lua_getglobal(lstate, "package");
+ lua_getfield(lstate, -1, "loaded");
+ lua_getglobal(lstate, "vim");
+ lua_setfield(lstate, -2, "vim");
+ lua_pop(lstate, 2);
+
+ return lstate;
+}
void nlua_free_all_mem(void)
{
@@ -504,30 +753,35 @@ void nlua_free_all_mem(void)
return;
}
lua_State *lstate = global_lstate;
+ nlua_unref_global(lstate, require_ref);
+ nlua_common_free_all_mem(lstate);
+}
- nlua_unref(lstate, nlua_nil_ref);
- nlua_unref(lstate, nlua_empty_dict_ref);
+static void nlua_common_free_all_mem(lua_State *lstate)
+ FUNC_ATTR_NONNULL_ALL
+{
+ nlua_ref_state_t *ref_state = nlua_get_ref_state(lstate);
+ nlua_unref(lstate, ref_state, ref_state->nil_ref);
+ nlua_unref(lstate, ref_state, ref_state->empty_dict_ref);
#ifdef NLUA_TRACK_REFS
- if (nlua_refcount) {
- fprintf(stderr, "%d lua references were leaked!", nlua_refcount);
+ if (ref_state->ref_count) {
+ fprintf(stderr, "%d lua references were leaked!", ref_state->ref_count);
}
if (nlua_track_refs) {
// in case there are leaked luarefs, leak the associated memory
// to get LeakSanitizer stacktraces on exit
- pmap_destroy(handle_T)(&nlua_ref_markers);
+ pmap_destroy(handle_T)(&ref_state->ref_markers);
}
#endif
- nlua_refcount = 0;
lua_close(lstate);
}
-
static void nlua_print_event(void **argv)
{
char *str = argv[0];
- const size_t len = (size_t)(intptr_t)argv[1]-1; // exclude final NUL
+ const size_t len = (size_t)(intptr_t)argv[1] - 1; // exclude final NUL
for (size_t i = 0; i < len;) {
if (got_int) {
@@ -601,9 +855,18 @@ static int nlua_print(lua_State *const lstate)
#undef PRINT_ERROR
ga_append(&msg_ga, NUL);
- if (in_fast_callback) {
+ lua_getfield(lstate, LUA_REGISTRYINDEX, "nvim.thread");
+ bool is_thread = lua_toboolean(lstate, -1);
+ lua_pop(lstate, 1);
+
+ if (is_thread) {
+ loop_schedule_deferred(&main_loop,
+ event_create(nlua_print_event, 2,
+ msg_ga.ga_data,
+ (intptr_t)msg_ga.ga_len));
+ } else if (in_fast_callback) {
multiqueue_put(main_loop.events, nlua_print_event,
- 2, msg_ga.ga_data, msg_ga.ga_len);
+ 2, msg_ga.ga_data, (intptr_t)msg_ga.ga_len);
} else {
nlua_print_event((void *[]){ msg_ga.ga_data,
(void *)(intptr_t)msg_ga.ga_len });
@@ -612,13 +875,71 @@ static int nlua_print(lua_State *const lstate)
nlua_print_error:
ga_clear(&msg_ga);
+ char *buff = xmalloc(IOSIZE);
const char *fmt = _("E5114: Error while converting print argument #%i: %.*s");
- size_t len = (size_t)vim_snprintf((char *)IObuff, IOSIZE, fmt, curargidx,
+ size_t len = (size_t)vim_snprintf(buff, IOSIZE, fmt, curargidx,
(int)errmsg_len, errmsg);
- lua_pushlstring(lstate, (char *)IObuff, len);
+ lua_pushlstring(lstate, buff, len);
+ xfree(buff);
return lua_error(lstate);
}
+/// require() for --startuptime
+///
+/// @param lstate Lua interpreter state.
+static int nlua_require(lua_State *const lstate)
+ FUNC_ATTR_NONNULL_ALL
+{
+ const char *name = luaL_checkstring(lstate, 1);
+ lua_settop(lstate, 1);
+ // [ name ]
+
+ // try cached module from package.loaded first
+ lua_getfield(lstate, LUA_REGISTRYINDEX, "_LOADED");
+ lua_getfield(lstate, 2, name);
+ // [ name package.loaded module ]
+ if (lua_toboolean(lstate, -1)) {
+ return 1;
+ }
+ lua_pop(lstate, 2);
+ // [ name ]
+
+ // push original require below the module name
+ nlua_pushref(lstate, require_ref);
+ lua_insert(lstate, 1);
+ // [ require name ]
+
+ if (time_fd == NULL) {
+ // after log file was closed, try to restore
+ // global require to the original function...
+ lua_getglobal(lstate, "require");
+ // ...only if it's still referencing this wrapper,
+ // to not overwrite it in case someone happened to
+ // patch it in the meantime...
+ if (lua_iscfunction(lstate, -1) && lua_tocfunction(lstate, -1) == nlua_require) {
+ lua_pushvalue(lstate, 1);
+ lua_setglobal(lstate, "require");
+ }
+ lua_pop(lstate, 1);
+
+ // ...and then call require directly.
+ lua_call(lstate, 1, 1);
+ return 1;
+ }
+
+ proftime_T rel_time;
+ proftime_T start_time;
+ time_push(&rel_time, &start_time);
+ int status = lua_pcall(lstate, 1, 1, 0);
+ if (status == 0) {
+ vim_snprintf((char *)IObuff, IOSIZE, "require('%s')", name);
+ time_msg((char *)IObuff, &start_time);
+ }
+ time_pop(rel_time);
+
+ return status == 0 ? 1 : lua_error(lstate);
+}
+
/// debug.debug: interaction with user while debugging.
///
/// @param lstate Lua interpreter state.
@@ -629,7 +950,7 @@ static int nlua_debug(lua_State *lstate)
{
.v_lock = VAR_FIXED,
.v_type = VAR_STRING,
- .vval.v_string = (char_u *)"lua_debug> ",
+ .vval.v_string = "lua_debug> ",
},
{
.v_type = VAR_UNKNOWN,
@@ -664,16 +985,26 @@ int nlua_in_fast_event(lua_State *lstate)
return 1;
}
+static bool viml_func_is_fast(const char *name)
+{
+ const EvalFuncDef *const fdef = find_internal_func((const char *)name);
+ if (fdef) {
+ return fdef->fast;
+ }
+ // Not a vimL function
+ return false;
+}
+
int nlua_call(lua_State *lstate)
{
Error err = ERROR_INIT;
size_t name_len;
- const char_u *name = (const char_u *)luaL_checklstring(lstate, 1, &name_len);
- if (!nlua_is_deferred_safe()) {
+ const char *name = luaL_checklstring(lstate, 1, &name_len);
+ if (!nlua_is_deferred_safe() && !viml_func_is_fast(name)) {
return luaL_error(lstate, e_luv_api_disabled, "vimL function");
}
- int nargs = lua_gettop(lstate)-1;
+ int nargs = lua_gettop(lstate) - 1;
if (nargs > MAX_FUNC_ARGS) {
return luaL_error(lstate, "Function called with too many arguments");
}
@@ -681,10 +1012,10 @@ int nlua_call(lua_State *lstate)
typval_T vim_args[MAX_FUNC_ARGS + 1];
int i = 0; // also used for freeing the variables
for (; i < nargs; i++) {
- lua_pushvalue(lstate, (int)i+2);
+ lua_pushvalue(lstate, i + 2);
if (!nlua_pop_typval(lstate, &vim_args[i])) {
api_set_error(&err, kErrorTypeException,
- "error converting argument %d", i+1);
+ "error converting argument %d", i + 1);
goto free_vim_args;
}
}
@@ -704,7 +1035,7 @@ int nlua_call(lua_State *lstate)
funcexe.evaluate = true;
// call_func() retval is deceptive, ignore it. Instead we set `msg_list`
// (TRY_WRAP) to capture abort-causing non-exception errors.
- (void)call_func(name, (int)name_len, &rettv, nargs, vim_args, &funcexe);
+ (void)call_func((char *)name, (int)name_len, &rettv, nargs, vim_args, &funcexe);
if (!try_end(&err)) {
nlua_push_typval(lstate, &rettv, false);
}
@@ -741,12 +1072,12 @@ static int nlua_rpc(lua_State *lstate, bool request)
size_t name_len;
uint64_t chan_id = (uint64_t)luaL_checkinteger(lstate, 1);
const char *name = luaL_checklstring(lstate, 2, &name_len);
- int nargs = lua_gettop(lstate)-2;
+ int nargs = lua_gettop(lstate) - 2;
Error err = ERROR_INIT;
Array args = ARRAY_DICT_INIT;
for (int i = 0; i < nargs; i++) {
- lua_pushvalue(lstate, (int)i+3);
+ lua_pushvalue(lstate, i + 3);
ADD(args, nlua_pop_Object(lstate, false, &err));
if (ERROR_SET(&err)) {
api_free_array(args);
@@ -755,10 +1086,11 @@ static int nlua_rpc(lua_State *lstate, bool request)
}
if (request) {
- Object result = rpc_send_call(chan_id, name, args, &err);
+ ArenaMem res_mem = NULL;
+ Object result = rpc_send_call(chan_id, name, args, &res_mem, &err);
if (!ERROR_SET(&err)) {
nlua_push_Object(lstate, result, false);
- api_free_object(result);
+ arena_mem_free(res_mem, NULL);
}
} else {
if (!rpc_send_event(chan_id, name, args)) {
@@ -789,7 +1121,6 @@ static int nlua_empty_dict_tostring(lua_State *lstate)
return 1;
}
-
#ifdef WIN32
/// os.getenv: override os.getenv to maintain coherency. #9681
///
@@ -803,42 +1134,52 @@ static int nlua_getenv(lua_State *lstate)
}
#endif
-
/// add the value to the registry
-LuaRef nlua_ref(lua_State *lstate, int index)
+/// The current implementation does not support calls from threads.
+LuaRef nlua_ref(lua_State *lstate, nlua_ref_state_t *ref_state, int index)
{
lua_pushvalue(lstate, index);
LuaRef ref = luaL_ref(lstate, LUA_REGISTRYINDEX);
if (ref > 0) {
- nlua_refcount++;
+ ref_state->ref_count++;
#ifdef NLUA_TRACK_REFS
if (nlua_track_refs) {
// dummy allocation to make LeakSanitizer track our luarefs
- pmap_put(handle_T)(&nlua_ref_markers, ref, xmalloc(3));
+ pmap_put(handle_T)(&ref_state->ref_markers, ref, xmalloc(3));
}
#endif
}
return ref;
}
+LuaRef nlua_ref_global(lua_State *lstate, int index)
+{
+ return nlua_ref(lstate, nlua_global_refs, index);
+}
+
/// remove the value from the registry
-void nlua_unref(lua_State *lstate, LuaRef ref)
+void nlua_unref(lua_State *lstate, nlua_ref_state_t *ref_state, LuaRef ref)
{
if (ref > 0) {
- nlua_refcount--;
+ ref_state->ref_count--;
#ifdef NLUA_TRACK_REFS
// NB: don't remove entry from map to track double-unref
if (nlua_track_refs) {
- xfree(pmap_get(handle_T)(&nlua_ref_markers, ref));
+ xfree(pmap_get(handle_T)(&ref_state->ref_markers, ref));
}
#endif
luaL_unref(lstate, LUA_REGISTRYINDEX, ref);
}
}
+void nlua_unref_global(lua_State *lstate, LuaRef ref)
+{
+ nlua_unref(lstate, nlua_global_refs, ref);
+}
+
void api_free_luaref(LuaRef ref)
{
- nlua_unref(global_lstate, ref);
+ nlua_unref_global(global_lstate, ref);
}
/// push a value referenced in the registry
@@ -847,7 +1188,6 @@ void nlua_pushref(lua_State *lstate, LuaRef ref)
lua_rawgeti(lstate, LUA_REGISTRYINDEX, ref);
}
-
/// Gets a new reference to an object stored at original_ref
///
/// NOTE: It does not copy the value, it creates a new ref to the lua object.
@@ -860,12 +1200,11 @@ LuaRef api_new_luaref(LuaRef original_ref)
lua_State *const lstate = global_lstate;
nlua_pushref(lstate, original_ref);
- LuaRef new_ref = nlua_ref(lstate, -1);
+ LuaRef new_ref = nlua_ref_global(lstate, -1);
lua_pop(lstate, 1);
return new_ref;
}
-
/// Evaluate lua string
///
/// Used for luaeval().
@@ -930,8 +1269,8 @@ void nlua_call_user_expand_func(expand_T *xp, typval_T *ret_tv)
lua_State *const lstate = global_lstate;
nlua_pushref(lstate, xp->xp_luaref);
- lua_pushstring(lstate, (char *)xp->xp_pattern);
- lua_pushstring(lstate, (char *)xp->xp_line);
+ lua_pushstring(lstate, xp->xp_pattern);
+ lua_pushstring(lstate, xp->xp_line);
lua_pushinteger(lstate, xp->xp_col);
if (nlua_pcall(lstate, 3, 1)) {
@@ -984,7 +1323,7 @@ int nlua_source_using_linegetter(LineGetter fgetline, void *cookie, char *name)
char_u *line = NULL;
ga_init(&ga, (int)sizeof(char_u *), 10);
- while ((line = fgetline(0, cookie, 0, false)) != NULL) {
+ while ((line = (char_u *)fgetline(0, cookie, 0, false)) != NULL) {
GA_APPEND(char_u *, &ga, line);
}
char *code = ga_concat_strings_sep(&ga, "\n");
@@ -1062,6 +1401,19 @@ Object nlua_exec(const String str, const Array args, Error *err)
return nlua_pop_Object(lstate, false, err);
}
+bool nlua_ref_is_function(LuaRef ref)
+{
+ lua_State *const lstate = global_lstate;
+ nlua_pushref(lstate, ref);
+
+ // TODO(tjdevries): This should probably check for callable tables as well.
+ // We should put some work maybe into simplifying how all of that works
+ bool is_function = (lua_type(lstate, -1) == LUA_TFUNCTION);
+ lua_pop(lstate, 1);
+
+ return is_function;
+}
+
/// call a LuaRef as a function (or table with __call metamethod)
///
/// @param ref the reference to call (not consumed)
@@ -1136,7 +1488,7 @@ void ex_lua(exarg_T *const eap)
// lua nlua_typval_exec doesn't expect null terminated string so len
// needs to end before null byte.
char *code_buf = xmallocz(len);
- vim_snprintf(code_buf, len+1, "vim.pretty_print(%s)", code+1);
+ vim_snprintf(code_buf, len + 1, "vim.pretty_print(%s)", code + 1);
xfree(code);
code = code_buf;
}
@@ -1217,7 +1569,7 @@ void ex_luado(exarg_T *const eap)
new_line_transformed[i] = '\n';
}
}
- ml_replace(l, (char_u *)new_line_transformed, false);
+ ml_replace(l, new_line_transformed, false);
inserted_bytes(l, 0, (int)old_line_len, (int)new_line_len);
}
lua_pop(lstate, 1);
@@ -1240,6 +1592,9 @@ void ex_luafile(exarg_T *const eap)
/// execute lua code from a file.
///
+/// Note: we call the lua global loadfile as opposed to calling luaL_loadfile
+/// in case loadfile has been overridden in the users environment.
+///
/// @param path path of the file
///
/// @return true if everything ok, false if there was an error (echoed)
@@ -1248,11 +1603,30 @@ bool nlua_exec_file(const char *path)
{
lua_State *const lstate = global_lstate;
- if (luaL_loadfile(lstate, path)) {
+ lua_getglobal(lstate, "loadfile");
+ lua_pushstring(lstate, path);
+
+ if (nlua_pcall(lstate, 1, 2)) {
+ nlua_error(lstate, _("E5111: Error calling lua: %.*s"));
+ return false;
+ }
+
+ // loadstring() returns either:
+ // 1. nil, error
+ // 2. chunk, nil
+
+ if (lua_isnil(lstate, -2)) {
+ // 1
nlua_error(lstate, _("E5112: Error while creating lua chunk: %.*s"));
+ assert(lua_isnil(lstate, -1));
+ lua_pop(lstate, 1);
return false;
}
+ // 2
+ assert(lua_isnil(lstate, -1));
+ lua_pop(lstate, 1);
+
if (nlua_pcall(lstate, 0, 0)) {
nlua_error(lstate, _("E5113: Error while calling lua chunk: %.*s"));
return false;
@@ -1267,6 +1641,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 +1668,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)
@@ -1338,9 +1721,7 @@ int nlua_expand_pat(expand_T *xp, char_u *pat, int *num_results, char_u ***resul
goto cleanup_array;
}
- GA_APPEND(char_u *,
- &result_array,
- vim_strsave((char_u *)v.data.string.data));
+ GA_APPEND(char_u *, &result_array, (char_u *)string_to_cstr(v.data.string));
}
xp->xp_pattern += prefix_len;
@@ -1359,6 +1740,13 @@ cleanup:
return ret;
}
+static int nlua_is_thread(lua_State *lstate)
+{
+ lua_getfield(lstate, LUA_REGISTRYINDEX, "nvim.thread");
+
+ return 1;
+}
+
// Required functions for lua c functions as VimL callbacks
int nlua_CFunction_func_call(int argcount, typval_T *argvars, typval_T *rettv, void *state)
@@ -1375,7 +1763,7 @@ void nlua_CFunction_func_free(void *state)
lua_State *const lstate = global_lstate;
LuaCFunctionState *funcstate = (LuaCFunctionState *)state;
- nlua_unref(lstate, funcstate->lua_callable.func_ref);
+ nlua_unref_global(lstate, funcstate->lua_callable.func_ref);
xfree(funcstate);
}
@@ -1425,12 +1813,11 @@ char_u *nlua_register_table_as_callable(typval_T *const arg)
lua_pop(lstate, 2); // [table]
LuaCFunctionState *state = xmalloc(sizeof(LuaCFunctionState));
- state->lua_callable.func_ref = nlua_ref(lstate, -1);
+ state->lua_callable.func_ref = nlua_ref_global(lstate, -1);
char_u *name = register_cfunc(&nlua_CFunction_func_call,
&nlua_CFunction_func_free, state);
-
lua_pop(lstate, 1); // []
assert(top == lua_gettop(lstate));
@@ -1458,10 +1845,13 @@ void nlua_execute_on_key(int c)
// [ vim, vim._on_key, buf ]
lua_pushlstring(lstate, (const char *)buf, buf_len);
+ int save_got_int = got_int;
+ got_int = false; // avoid interrupts when the key typed is Ctrl-C
if (nlua_pcall(lstate, 1, 0)) {
nlua_error(lstate,
_("Error executing vim.on_key Lua callback: %.*s"));
}
+ got_int |= save_got_int;
// [ vim ]
lua_pop(lstate, 1);
@@ -1472,11 +1862,64 @@ void nlua_execute_on_key(int c)
#endif
}
-void nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap)
+// Sets the editor "script context" during Lua execution. Used by :verbose.
+// @param[out] current
+void nlua_set_sctx(sctx_T *current)
{
+ if (p_verbose <= 0 || current->sc_sid != SID_LUA) {
+ return;
+ }
lua_State *const lstate = global_lstate;
+ lua_Debug *info = (lua_Debug *)xmalloc(sizeof(lua_Debug));
+
+ // Files where internal wrappers are defined so we can ignore them
+ // like vim.o/opt etc are defined in _meta.lua
+ char *ignorelist[] = {
+ "vim/_meta.lua",
+ "vim/keymap.lua",
+ };
+ int ignorelist_size = sizeof(ignorelist) / sizeof(ignorelist[0]);
- nlua_pushref(lstate, cmd->uc_luaref);
+ for (int level = 1; true; level++) {
+ if (lua_getstack(lstate, level, info) != 1) {
+ goto cleanup;
+ }
+ if (lua_getinfo(lstate, "nSl", info) == 0) {
+ goto cleanup;
+ }
+
+ bool is_ignored = false;
+ if (info->what[0] == 'C' || info->source[0] != '@') {
+ is_ignored = true;
+ } else {
+ for (int i = 0; i < ignorelist_size; i++) {
+ if (strncmp(ignorelist[i], info->source + 1, strlen(ignorelist[i])) == 0) {
+ is_ignored = true;
+ break;
+ }
+ }
+ }
+ if (is_ignored) {
+ continue;
+ }
+ break;
+ }
+ char *source_path = fix_fname(info->source + 1);
+ get_current_script_id((char_u *)source_path, current);
+ xfree(source_path);
+ current->sc_lnum = info->currentline;
+ current->sc_seq = -1;
+
+cleanup:
+ xfree(info);
+}
+
+/// @param preview Invoke the callback as a |:command-preview| handler.
+int nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap, bool preview)
+{
+ lua_State *const lstate = global_lstate;
+
+ nlua_pushref(lstate, preview ? cmd->uc_preview_luaref : cmd->uc_luaref);
lua_newtable(lstate);
lua_pushboolean(lstate, eap->forceit == 1);
@@ -1488,8 +1931,42 @@ void nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap)
lua_pushinteger(lstate, eap->line2);
lua_setfield(lstate, -2, "line2");
+ lua_newtable(lstate); // f-args table
lua_pushstring(lstate, (const char *)eap->arg);
- lua_setfield(lstate, -2, "args");
+ lua_pushvalue(lstate, -1); // Reference for potential use on f-args
+ lua_setfield(lstate, -4, "args");
+
+ // Split args by unescaped whitespace |<f-args>| (nargs dependent)
+ if (cmd->uc_argt & EX_NOSPC) {
+ // Commands where nargs = 1 or "?" fargs is the same as args
+ lua_rawseti(lstate, -2, 1);
+ } else if (eap->args == NULL) {
+ // For commands with more than one possible argument, split if argument list isn't available.
+ lua_pop(lstate, 1); // Pop the reference of opts.args
+ size_t length = STRLEN(eap->arg);
+ size_t end = 0;
+ size_t len = 0;
+ int i = 1;
+ char *buf = xcalloc(length, sizeof(char));
+ bool done = false;
+ while (!done) {
+ done = uc_split_args_iter(eap->arg, length, &end, buf, &len);
+ if (len > 0) {
+ lua_pushlstring(lstate, buf, len);
+ lua_rawseti(lstate, -2, i);
+ i++;
+ }
+ }
+ xfree(buf);
+ } else {
+ // If argument list is available, just use it.
+ lua_pop(lstate, 1);
+ for (size_t i = 0; i < eap->argc; i++) {
+ lua_pushlstring(lstate, eap->args[i], eap->arglens[i]);
+ lua_rawseti(lstate, -2, (int)i + 1);
+ }
+ }
+ lua_setfield(lstate, -2, "fargs");
lua_pushstring(lstate, (const char *)&eap->regname);
lua_setfield(lstate, -2, "reg");
@@ -1508,12 +1985,93 @@ void nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap)
// every possible modifier (with room to spare). If the list of possible
// modifiers grows this may need to be updated.
char buf[200] = { 0 };
- (void)uc_mods(buf);
+ (void)uc_mods(buf, &cmdmod, false);
lua_pushstring(lstate, buf);
lua_setfield(lstate, -2, "mods");
- if (nlua_pcall(lstate, 1, 0)) {
+ lua_newtable(lstate); // smods table
+
+ lua_pushinteger(lstate, cmdmod.cmod_tab);
+ lua_setfield(lstate, -2, "tab");
+
+ lua_pushinteger(lstate, cmdmod.cmod_verbose - 1);
+ lua_setfield(lstate, -2, "verbose");
+
+ if (cmdmod.cmod_split & WSP_ABOVE) {
+ lua_pushstring(lstate, "aboveleft");
+ } else if (cmdmod.cmod_split & WSP_BELOW) {
+ lua_pushstring(lstate, "belowright");
+ } else if (cmdmod.cmod_split & WSP_TOP) {
+ lua_pushstring(lstate, "topleft");
+ } else if (cmdmod.cmod_split & WSP_BOT) {
+ lua_pushstring(lstate, "botright");
+ } else {
+ lua_pushstring(lstate, "");
+ }
+ lua_setfield(lstate, -2, "split");
+
+ lua_pushboolean(lstate, cmdmod.cmod_split & WSP_VERT);
+ lua_setfield(lstate, -2, "vertical");
+ lua_pushboolean(lstate, cmdmod.cmod_flags & CMOD_SILENT);
+ lua_setfield(lstate, -2, "silent");
+ lua_pushboolean(lstate, cmdmod.cmod_flags & CMOD_ERRSILENT);
+ lua_setfield(lstate, -2, "emsg_silent");
+ lua_pushboolean(lstate, cmdmod.cmod_flags & CMOD_UNSILENT);
+ lua_setfield(lstate, -2, "unsilent");
+ lua_pushboolean(lstate, cmdmod.cmod_flags & CMOD_SANDBOX);
+ lua_setfield(lstate, -2, "sandbox");
+ lua_pushboolean(lstate, cmdmod.cmod_flags & CMOD_NOAUTOCMD);
+ lua_setfield(lstate, -2, "noautocmd");
+
+ typedef struct {
+ int flag;
+ char *name;
+ } mod_entry_T;
+ static mod_entry_T mod_entries[] = {
+ { CMOD_BROWSE, "browse" },
+ { CMOD_CONFIRM, "confirm" },
+ { CMOD_HIDE, "hide" },
+ { CMOD_KEEPALT, "keepalt" },
+ { CMOD_KEEPJUMPS, "keepjumps" },
+ { CMOD_KEEPMARKS, "keepmarks" },
+ { CMOD_KEEPPATTERNS, "keeppatterns" },
+ { CMOD_LOCKMARKS, "lockmarks" },
+ { CMOD_NOSWAPFILE, "noswapfile" }
+ };
+
+ // The modifiers that are simple flags
+ for (size_t i = 0; i < ARRAY_SIZE(mod_entries); i++) {
+ lua_pushboolean(lstate, cmdmod.cmod_flags & mod_entries[i].flag);
+ lua_setfield(lstate, -2, mod_entries[i].name);
+ }
+
+ lua_setfield(lstate, -2, "smods");
+
+ if (preview) {
+ lua_pushinteger(lstate, cmdpreview_get_ns());
+
+ handle_T cmdpreview_bufnr = cmdpreview_get_bufnr();
+ if (cmdpreview_bufnr != 0) {
+ lua_pushinteger(lstate, cmdpreview_bufnr);
+ } else {
+ lua_pushnil(lstate);
+ }
+ }
+
+ if (nlua_pcall(lstate, preview ? 3 : 1, preview ? 1 : 0)) {
nlua_error(lstate, _("Error executing Lua callback: %.*s"));
+ return 0;
+ }
+
+ int retv = 0;
+
+ if (preview) {
+ if (lua_isnumber(lstate, -1) && (retv = (int)lua_tointeger(lstate, -1)) >= 0 && retv <= 2) {
+ lua_pop(lstate, 1);
+ } else {
+ retv = 0;
+ }
}
-}
+ return retv;
+}
diff --git a/src/nvim/lua/executor.h b/src/nvim/lua/executor.h
index bf78f7ec5e..e96494ec5a 100644
--- a/src/nvim/lua/executor.h
+++ b/src/nvim/lua/executor.h
@@ -5,6 +5,7 @@
#include <lua.h>
#include "nvim/api/private/defs.h"
+#include "nvim/assert.h"
#include "nvim/eval/typval.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
@@ -14,18 +15,14 @@
// Generated by msgpack-gen.lua
void nlua_add_api_functions(lua_State *lstate) REAL_FATTR_NONNULL_ALL;
-EXTERN LuaRef nlua_nil_ref INIT(= LUA_NOREF);
-EXTERN LuaRef nlua_empty_dict_ref INIT(= LUA_NOREF);
-
-EXTERN int nlua_refcount INIT(= 0);
-
-#define set_api_error(s, err) \
- do { \
- Error *err_ = (err); \
- err_->type = kErrorTypeException; \
- err_->set = true; \
- memcpy(&err_->msg[0], s, sizeof(s)); \
- } while (0)
+typedef struct {
+ LuaRef nil_ref;
+ LuaRef empty_dict_ref;
+ int ref_count;
+#if __has_feature(address_sanitizer)
+ PMap(handle_T) ref_markers;
+#endif
+} nlua_ref_state_t;
#define NLUA_CLEAR_REF(x) \
do { \
@@ -39,4 +36,8 @@ EXTERN int nlua_refcount INIT(= 0);
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "lua/executor.h.generated.h"
#endif
+
+EXTERN nlua_ref_state_t *nlua_global_refs INIT(= NULL);
+EXTERN bool nlua_disable_preload INIT(= false);
+
#endif // NVIM_LUA_EXECUTOR_H
diff --git a/src/nvim/lua/spell.c b/src/nvim/lua/spell.c
index 3a63f61200..31a2b2d19f 100644
--- a/src/nvim/lua/spell.c
+++ b/src/nvim/lua/spell.c
@@ -1,12 +1,12 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
-#include <lua.h>
#include <lauxlib.h>
+#include <lua.h>
+#include "nvim/lua/spell.h"
#include "nvim/spell.h"
#include "nvim/vim.h"
-#include "nvim/lua/spell.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "lua/spell.c.generated.h"
@@ -45,7 +45,7 @@ int nlua_spell_check(lua_State *lstate)
size_t pos = 0;
int capcol = -1;
int no_res = 0;
- const char * result;
+ const char *result;
lua_createtable(lstate, 0, 0);
@@ -90,7 +90,7 @@ int nlua_spell_check(lua_State *lstate)
static const luaL_Reg spell_functions[] = {
{ "check", nlua_spell_check },
- { NULL , NULL }
+ { NULL, NULL }
};
int luaopen_spell(lua_State *L)
diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c
index 18a579ed0f..8fde85b163 100644
--- a/src/nvim/lua/stdlib.c
+++ b/src/nvim/lua/stdlib.c
@@ -25,12 +25,13 @@
#include "nvim/func_attr.h"
#include "nvim/garray.h"
#include "nvim/getchar.h"
+#include "nvim/globals.h"
#include "nvim/lua/converter.h"
#include "nvim/lua/executor.h"
+#include "nvim/lua/spell.h"
#include "nvim/lua/stdlib.h"
#include "nvim/lua/treesitter.h"
#include "nvim/lua/xdiff.h"
-#include "nvim/lua/spell.h"
#include "nvim/macros.h"
#include "nvim/map.h"
#include "nvim/memline.h"
@@ -54,12 +55,12 @@ static int regex_match(lua_State *lstate, regprog_T **prog, char_u *str)
regmatch_T rm;
rm.regprog = *prog;
rm.rm_ic = false;
- bool match = vim_regexec(&rm, str, 0);
+ bool match = vim_regexec(&rm, (char *)str, 0);
*prog = rm.regprog;
if (match) {
- lua_pushinteger(lstate, (lua_Integer)(rm.startp[0]-str));
- lua_pushinteger(lstate, (lua_Integer)(rm.endp[0]-str));
+ lua_pushinteger(lstate, (lua_Integer)(rm.startp[0] - str));
+ lua_pushinteger(lstate, (lua_Integer)(rm.endp[0] - str));
return 2;
}
return 0;
@@ -88,7 +89,7 @@ static int regex_match_line(lua_State *lstate)
}
long bufnr = luaL_checkinteger(lstate, 2);
- long rownr = luaL_checkinteger(lstate, 3);
+ linenr_T rownr = (linenr_T)luaL_checkinteger(lstate, 3);
long start = 0, end = -1;
if (narg >= 4) {
start = luaL_checkinteger(lstate, 4);
@@ -109,7 +110,7 @@ static int regex_match_line(lua_State *lstate)
return luaL_error(lstate, "invalid row");
}
- char_u *line = ml_get_buf(buf, rownr+1, false);
+ char_u *line = ml_get_buf(buf, rownr + 1, false);
size_t len = STRLEN(line);
if (start < 0 || (size_t)start > len) {
@@ -125,7 +126,7 @@ static int regex_match_line(lua_State *lstate)
line[end] = NUL;
}
- int nret = regex_match(lstate, prog, line+start);
+ int nret = regex_match(lstate, prog, line + start);
if (end >= 0) {
line[end] = save;
@@ -207,7 +208,7 @@ static int nlua_str_utf_pos(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
size_t idx = 1;
size_t clen;
for (size_t i = 0; i < s1_len && s1[i] != NUL; i += clen) {
- clen = (size_t)utf_ptr2len_len((const char_u *)(s1)+i, (int)(s1_len-i));
+ clen = (size_t)utf_ptr2len_len((const char_u *)(s1) + i, (int)(s1_len - i));
lua_pushinteger(lstate, (long)i + 1);
lua_rawseti(lstate, -2, (int)idx);
idx++;
@@ -251,7 +252,7 @@ static int nlua_str_utf_end(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
if (offset < 0 || offset > (intptr_t)s1_len) {
return luaL_error(lstate, "index out of range");
}
- int tail_offset = mb_tail_off((char_u *)s1, (char_u *)s1 + offset - 1);
+ int tail_offset = mb_tail_off(s1, s1 + offset - 1);
lua_pushinteger(lstate, tail_offset);
return 1;
}
@@ -294,12 +295,14 @@ int nlua_regex(lua_State *lstate)
TRY_WRAP({
try_start();
- prog = vim_regcomp((char_u *)text, RE_AUTO | RE_MAGIC | RE_STRICT);
+ prog = vim_regcomp((char *)text, RE_AUTO | RE_MAGIC | RE_STRICT);
try_end(&err);
});
if (ERROR_SET(&err)) {
- return luaL_error(lstate, "couldn't parse regex: %s", err.msg);
+ nlua_push_errstr(lstate, "couldn't parse regex: %s", err.msg);
+ api_clear_error(&err);
+ return lua_error(lstate);
}
assert(prog);
@@ -337,18 +340,19 @@ static dict_T *nlua_get_var_scope(lua_State *lstate)
dict = tabpage->tp_vars;
}
} else {
- luaL_error(lstate, "invalid scope", err.msg);
+ luaL_error(lstate, "invalid scope");
return NULL;
}
if (ERROR_SET(&err)) {
- luaL_error(lstate, "FAIL: %s", err.msg);
+ nlua_push_errstr(lstate, "scoped variable: %s", err.msg);
+ api_clear_error(&err);
+ lua_error(lstate);
return NULL;
}
return dict;
}
-
int nlua_setvar(lua_State *lstate)
{
// non-local return if not found
@@ -408,6 +412,12 @@ int nlua_getvar(lua_State *lstate)
const char *name = luaL_checklstring(lstate, 3, &len);
dictitem_T *di = tv_dict_find(dict, name, (ptrdiff_t)len);
+ if (di == NULL && dict == &globvardict) { // try to autoload script
+ if (!script_autoload(name, len, false) || aborting()) {
+ return 0; // nil
+ }
+ di = tv_dict_find(dict, name, (ptrdiff_t)len);
+ }
if (di == NULL) {
return 0; // nil
}
@@ -463,44 +473,52 @@ static int nlua_stricmp(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
return 1;
}
-
-void nlua_state_add_stdlib(lua_State *const lstate)
+void nlua_state_add_stdlib(lua_State *const lstate, bool is_thread)
{
- // stricmp
- lua_pushcfunction(lstate, &nlua_stricmp);
- lua_setfield(lstate, -2, "stricmp");
- // str_utfindex
- lua_pushcfunction(lstate, &nlua_str_utfindex);
- lua_setfield(lstate, -2, "str_utfindex");
- // str_byteindex
- lua_pushcfunction(lstate, &nlua_str_byteindex);
- lua_setfield(lstate, -2, "str_byteindex");
- // str_utf_pos
- lua_pushcfunction(lstate, &nlua_str_utf_pos);
- lua_setfield(lstate, -2, "str_utf_pos");
- // str_utf_start
- lua_pushcfunction(lstate, &nlua_str_utf_start);
- lua_setfield(lstate, -2, "str_utf_start");
- // str_utf_end
- lua_pushcfunction(lstate, &nlua_str_utf_end);
- lua_setfield(lstate, -2, "str_utf_end");
- // regex
- lua_pushcfunction(lstate, &nlua_regex);
- lua_setfield(lstate, -2, "regex");
- luaL_newmetatable(lstate, "nvim_regex");
- luaL_register(lstate, NULL, regex_meta);
-
- lua_pushvalue(lstate, -1); // [meta, meta]
- lua_setfield(lstate, -2, "__index"); // [meta]
- lua_pop(lstate, 1); // don't use metatable now
-
- // _getvar
- lua_pushcfunction(lstate, &nlua_getvar);
- lua_setfield(lstate, -2, "_getvar");
-
- // _setvar
- lua_pushcfunction(lstate, &nlua_setvar);
- lua_setfield(lstate, -2, "_setvar");
+ if (!is_thread) {
+ // TODO(bfredl): some of basic string functions should already be
+ // (or be easy to make) threadsafe
+
+ // stricmp
+ lua_pushcfunction(lstate, &nlua_stricmp);
+ lua_setfield(lstate, -2, "stricmp");
+ // str_utfindex
+ lua_pushcfunction(lstate, &nlua_str_utfindex);
+ lua_setfield(lstate, -2, "str_utfindex");
+ // str_byteindex
+ lua_pushcfunction(lstate, &nlua_str_byteindex);
+ lua_setfield(lstate, -2, "str_byteindex");
+ // str_utf_pos
+ lua_pushcfunction(lstate, &nlua_str_utf_pos);
+ lua_setfield(lstate, -2, "str_utf_pos");
+ // str_utf_start
+ lua_pushcfunction(lstate, &nlua_str_utf_start);
+ lua_setfield(lstate, -2, "str_utf_start");
+ // str_utf_end
+ lua_pushcfunction(lstate, &nlua_str_utf_end);
+ lua_setfield(lstate, -2, "str_utf_end");
+ // regex
+ lua_pushcfunction(lstate, &nlua_regex);
+ lua_setfield(lstate, -2, "regex");
+ luaL_newmetatable(lstate, "nvim_regex");
+ luaL_register(lstate, NULL, regex_meta);
+
+ lua_pushvalue(lstate, -1); // [meta, meta]
+ lua_setfield(lstate, -2, "__index"); // [meta]
+ lua_pop(lstate, 1); // don't use metatable now
+
+ // _getvar
+ lua_pushcfunction(lstate, &nlua_getvar);
+ lua_setfield(lstate, -2, "_getvar");
+
+ // _setvar
+ lua_pushcfunction(lstate, &nlua_setvar);
+ lua_setfield(lstate, -2, "_setvar");
+
+ // vim.spell
+ luaopen_spell(lstate);
+ lua_setfield(lstate, -2, "spell");
+ }
// vim.mpack
luaopen_mpack(lstate);
@@ -519,10 +537,18 @@ void nlua_state_add_stdlib(lua_State *const lstate)
lua_pushcfunction(lstate, &nlua_xdl_diff);
lua_setfield(lstate, -2, "diff");
- // vim.spell
- luaopen_spell(lstate);
- lua_setfield(lstate, -2, "spell");
-
+ // vim.json
lua_cjson_new(lstate);
lua_setfield(lstate, -2, "json");
}
+
+/// like luaL_error, but allow cleanup
+void nlua_push_errstr(lua_State *L, const char *fmt, ...)
+{
+ va_list argp;
+ va_start(argp, fmt);
+ luaL_where(L, 1);
+ lua_pushvfstring(L, fmt, argp);
+ va_end(argp);
+ lua_concat(L, 2);
+}
diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c
index f4067ad02f..b96193d199 100644
--- a/src/nvim/lua/treesitter.c
+++ b/src/nvim/lua/treesitter.c
@@ -16,6 +16,7 @@
#include "nvim/api/private/helpers.h"
#include "nvim/buffer.h"
+#include "nvim/lib/kvec.h"
#include "nvim/lua/treesitter.h"
#include "nvim/memline.h"
#include "tree_sitter/api.h"
@@ -104,6 +105,7 @@ static struct luaL_Reg treecursor_meta[] = {
{ NULL, NULL }
};
+static kvec_t(TSQueryCursor *) cursors = KV_INITIAL_VALUE;
static PMap(cstr_t) langs = MAP_INIT;
static void build_meta(lua_State *L, const char *tname, const luaL_Reg *meta)
@@ -208,7 +210,7 @@ int tslua_inspect_lang(lua_State *L)
size_t nsymbols = (size_t)ts_language_symbol_count(lang);
- lua_createtable(L, nsymbols-1, 1); // [retval, symbols]
+ lua_createtable(L, nsymbols - 1, 1); // [retval, symbols]
for (size_t i = 0; i < nsymbols; i++) {
TSSymbolType t = ts_language_symbol_type(lang, i);
if (t == TSSymbolTypeAuxiliary) {
@@ -298,15 +300,15 @@ static const char *input_cb(void *payload, uint32_t byte_index, TSPoint position
*bytes_read = 0;
return "";
}
- char_u *line = ml_get_buf(bp, position.row+1, false);
+ char_u *line = ml_get_buf(bp, position.row + 1, false);
size_t len = STRLEN(line);
if (position.column > len) {
*bytes_read = 0;
return "";
}
- size_t tocopy = MIN(len-position.column, BUFSIZE);
+ size_t tocopy = MIN(len - position.column, BUFSIZE);
- memcpy(buf, line+position.column, tocopy);
+ memcpy(buf, line + position.column, tocopy);
// Translate embedded \n to NUL
memchrsub(buf, '\n', '\0', tocopy);
*bytes_read = (uint32_t)tocopy;
@@ -334,7 +336,7 @@ static void push_ranges(lua_State *L, const TSRange *ranges, const unsigned int
lua_pushinteger(L, ranges[i].end_point.column);
lua_rawseti(L, -2, 4);
- lua_rawseti(L, -2, i+1);
+ lua_rawseti(L, -2, i + 1);
}
}
@@ -389,7 +391,7 @@ static int parser_parse(lua_State *L)
return luaL_error(L, "An error occurred when parsing.");
}
- // The new tree will be pushed to the stack, without copy, owwership is now to
+ // The new tree will be pushed to the stack, without copy, ownership is now to
// the lua GC.
// Old tree is still owned by the lua GC.
uint32_t n_ranges = 0;
@@ -527,7 +529,6 @@ static int parser_set_ranges(lua_State *L)
size_t tbl_len = lua_objlen(L, 2);
TSRange *ranges = xmalloc(sizeof(TSRange) * tbl_len);
-
// [ parser, ranges ]
for (size_t index = 0; index < tbl_len; index++) {
lua_rawgeti(L, 2, index + 1); // [ parser, ranges, range ]
@@ -556,7 +557,6 @@ static int parser_get_ranges(lua_State *L)
return 1;
}
-
// Tree methods
/// push tree interface on lua stack.
@@ -654,7 +654,6 @@ static bool node_check(lua_State *L, int index, TSNode *res)
return false;
}
-
static int node_tostring(lua_State *L)
{
TSNode node;
@@ -1037,7 +1036,7 @@ static void set_match(lua_State *L, TSQueryMatch *match, int nodeidx)
{
for (int i = 0; i < match->capture_count; i++) {
push_node(L, match->captures[i].node, nodeidx);
- lua_rawseti(L, -2, match->captures[i].index+1);
+ lua_rawseti(L, -2, match->captures[i].index + 1);
}
}
@@ -1049,7 +1048,7 @@ static int query_next_match(lua_State *L)
TSQuery *query = query_check(L, lua_upvalueindex(3));
TSQueryMatch match;
if (ts_query_cursor_next_match(cursor, &match)) {
- lua_pushinteger(L, match.pattern_index+1); // [index]
+ lua_pushinteger(L, match.pattern_index + 1); // [index]
lua_createtable(L, ts_query_capture_count(query), 2); // [index, match]
set_match(L, &match, lua_upvalueindex(2));
return 2;
@@ -1057,7 +1056,6 @@ static int query_next_match(lua_State *L)
return 0;
}
-
static int query_next_capture(lua_State *L)
{
// Upvalues are:
@@ -1082,7 +1080,7 @@ static int query_next_capture(lua_State *L)
if (ts_query_cursor_next_capture(cursor, &match, &capture_index)) {
TSQueryCapture capture = match.captures[capture_index];
- lua_pushinteger(L, capture.index+1); // [index]
+ lua_pushinteger(L, capture.index + 1); // [index]
push_node(L, capture.node, lua_upvalueindex(2)); // [index, node]
// Now check if we need to run the predicates
@@ -1094,7 +1092,7 @@ static int query_next_capture(lua_State *L)
lua_pushvalue(L, lua_upvalueindex(4)); // [index, node, match]
set_match(L, &match, lua_upvalueindex(2));
- lua_pushinteger(L, match.pattern_index+1);
+ lua_pushinteger(L, match.pattern_index + 1);
lua_setfield(L, -2, "pattern");
if (match.capture_count > 1) {
@@ -1116,13 +1114,17 @@ static int node_rawquery(lua_State *L)
return 0;
}
TSQuery *query = query_check(L, 2);
- // TODO(bfredl): these are expensive allegedly,
- // use a reuse list later on?
- TSQueryCursor *cursor = ts_query_cursor_new();
+
+ TSQueryCursor *cursor;
+ if (kv_size(cursors) > 0) {
+ cursor = kv_pop(cursors);
+ } else {
+ cursor = ts_query_cursor_new();
+ }
// TODO(clason): API introduced after tree-sitter release 0.19.5
// remove guard when minimum ts version is bumped to 0.19.6+
#ifdef NVIM_TS_HAS_SET_MATCH_LIMIT
- ts_query_cursor_set_match_limit(cursor, 32);
+ ts_query_cursor_set_match_limit(cursor, 64);
#endif
ts_query_cursor_exec(cursor, query, node);
@@ -1161,7 +1163,8 @@ static int node_rawquery(lua_State *L)
static int querycursor_gc(lua_State *L)
{
TSLua_cursor *ud = luaL_checkudata(L, 1, TS_META_QUERYCURSOR);
- ts_query_cursor_delete(ud->cursor);
+ kv_push(cursors, ud->cursor);
+ ud->cursor = NULL;
return 0;
}
@@ -1198,7 +1201,6 @@ int tslua_parse_query(lua_State *L)
return 1;
}
-
static const char *query_err_string(TSQueryError err)
{
switch (err) {
@@ -1273,7 +1275,7 @@ static int query_inspect(lua_State *L)
&strlen);
lua_pushlstring(L, str, strlen); // [retval, patterns, pat, pred, item]
} else if (step[k].type == TSQueryPredicateStepTypeCapture) {
- lua_pushnumber(L, step[k].value_id+1); // [..., pat, pred, item]
+ lua_pushnumber(L, step[k].value_id + 1); // [..., pat, pred, item]
} else {
abort();
}
@@ -1281,7 +1283,7 @@ static int query_inspect(lua_State *L)
}
// last predicate should have ended with TypeDone
lua_pop(L, 1); // [retval, patters, pat]
- lua_rawseti(L, -2, i+1); // [retval, patterns]
+ lua_rawseti(L, -2, i + 1); // [retval, patterns]
}
lua_setfield(L, -2, "patterns"); // [retval]
@@ -1291,7 +1293,7 @@ static int query_inspect(lua_State *L)
uint32_t strlen;
const char *str = ts_query_capture_name_for_id(query, i, &strlen);
lua_pushlstring(L, str, strlen); // [retval, captures, capture]
- lua_rawseti(L, -2, i+1);
+ lua_rawseti(L, -2, i + 1);
}
lua_setfield(L, -2, "captures"); // [retval]
diff --git a/src/nvim/lua/xdiff.c b/src/nvim/lua/xdiff.c
index b2e971f9f3..71f85385b6 100644
--- a/src/nvim/lua/xdiff.c
+++ b/src/nvim/lua/xdiff.c
@@ -74,7 +74,7 @@ static int hunk_locations_cb(long start_a, long count_a, long start_b, long coun
lua_pushinteger(lstate, count_b);
lua_rawseti(lstate, -2, 4);
- lua_rawseti(lstate, -2, (signed)lua_objlen(lstate, -2)+1);
+ lua_rawseti(lstate, -2, (signed)lua_objlen(lstate, -2) + 1);
return 0;
}
@@ -171,6 +171,7 @@ static NluaXdiffMode process_xdl_diff_opts(lua_State *lstate, xdemitconf_t *cfg,
goto exit_1;
}
if (strequal("unified", v->data.string.data)) {
+ // the default
} else if (strequal("indices", v->data.string.data)) {
had_result_type_indices = true;
} else {
@@ -184,11 +185,11 @@ static NluaXdiffMode process_xdl_diff_opts(lua_State *lstate, xdemitconf_t *cfg,
if (strequal("myers", v->data.string.data)) {
// default
} else if (strequal("minimal", v->data.string.data)) {
- cfg->flags |= XDF_NEED_MINIMAL;
+ params->flags |= XDF_NEED_MINIMAL;
} else if (strequal("patience", v->data.string.data)) {
- cfg->flags |= XDF_PATIENCE_DIFF;
+ params->flags |= XDF_PATIENCE_DIFF;
} else if (strequal("histogram", v->data.string.data)) {
- cfg->flags |= XDF_HISTOGRAM_DIFF;
+ params->flags |= XDF_HISTOGRAM_DIFF;
} else {
api_set_error(err, kErrorTypeValidation, "not a valid algorithm");
goto exit_1;
diff --git a/src/nvim/macros.h b/src/nvim/macros.h
index c2b2c89abf..a896a406d1 100644
--- a/src/nvim/macros.h
+++ b/src/nvim/macros.h
@@ -56,12 +56,14 @@
// Returns empty string if it is NULL.
#define EMPTY_IF_NULL(x) (char *)((x) ? (x) : (char_u *)"")
-// Adjust chars in a language according to 'langmap' option.
-// NOTE that there is no noticeable overhead if 'langmap' is not set.
-// When set the overhead for characters < 256 is small.
-// Don't apply 'langmap' if the character comes from the Stuff buffer or from a
-// mapping and the langnoremap option was set.
-// The do-while is just to ignore a ';' after the macro.
+/// Adjust chars in a language according to 'langmap' option.
+/// NOTE that there is no noticeable overhead if 'langmap' is not set.
+/// When set the overhead for characters < 256 is small.
+/// Don't apply 'langmap' if the character comes from the Stuff buffer or from a
+/// mapping and the langnoremap option was set.
+/// The do-while is just to ignore a ';' after the macro.
+///
+/// -V:LANGMAP_ADJUST:560
#define LANGMAP_ADJUST(c, condition) \
do { \
if (*p_langmap \
@@ -83,27 +85,29 @@
// mch_open_rw(): invoke os_open() with third argument for user R/W.
#if defined(UNIX) // open in rw------- mode
-# define mch_open_rw(n, f) os_open((n), (f), (mode_t)0600)
+# define MCH_OPEN_RW(n, f) os_open((n), (f), (mode_t)0600)
#elif defined(WIN32)
-# define mch_open_rw(n, f) os_open((n), (f), S_IREAD | S_IWRITE)
+# define MCH_OPEN_RW(n, f) os_open((n), (f), S_IREAD | S_IWRITE)
#else
-# define mch_open_rw(n, f) os_open((n), (f), 0)
+# define MCH_OPEN_RW(n, f) os_open((n), (f), 0)
#endif
#define REPLACE_NORMAL(s) (((s) & REPLACE_FLAG) && !((s) & VREPLACE_FLAG))
-
// MB_PTR_ADV(): advance a pointer to the next character, taking care of
// multi-byte characters if needed. Skip over composing chars.
-#define MB_PTR_ADV(p) (p += utfc_ptr2len((char_u *)p))
+#define MB_PTR_ADV(p) (p += utfc_ptr2len((char *)p))
// Advance multi-byte pointer, do not skip over composing chars.
-#define MB_CPTR_ADV(p) (p += utf_ptr2len(p))
+#define MB_CPTR_ADV(p) (p += utf_ptr2len((char *)p))
// MB_PTR_BACK(): backup a pointer to the previous character, taking care of
// multi-byte characters if needed. Only use with "p" > "s" !
#define MB_PTR_BACK(s, p) \
- (p -= utf_head_off((char_u *)s, (char_u *)p - 1) + 1)
+ (p -= utf_head_off((char_u *)(s), (char_u *)(p) - 1) + 1)
+
+// MB_CHAR2BYTES(): convert character to bytes and advance pointer to bytes
+#define MB_CHAR2BYTES(c, b) ((b) += utf_char2bytes((c), ((char *)b)))
#define RESET_BINDING(wp) \
do { \
@@ -130,7 +134,7 @@
#define ARRAY_LAST_ENTRY(arr) (arr)[ARRAY_SIZE(arr) - 1]
// Duplicated in os/win_defs.h to avoid include-order sensitivity.
-#define RGB_(r, g, b) ((r << 16) | (g << 8) | b)
+#define RGB_(r, g, b) (((r) << 16) | ((g) << 8) | (b))
#define STR_(x) #x
#define STR(x) STR_(x)
diff --git a/src/nvim/main.c b/src/nvim/main.c
index cbd1f53727..b06b9630e2 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -9,30 +9,35 @@
#include <string.h>
#include "nvim/ascii.h"
-#include "nvim/aucmd.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/channel.h"
#include "nvim/charset.h"
#include "nvim/decoration.h"
+#include "nvim/decoration_provider.h"
#include "nvim/diff.h"
#include "nvim/eval.h"
#include "nvim/ex_cmds.h"
#include "nvim/ex_cmds2.h"
#include "nvim/ex_docmd.h"
+#include "nvim/ex_getln.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
-#include "nvim/getchar.h"
#include "nvim/hashtab.h"
#include "nvim/highlight.h"
+#include "nvim/highlight_group.h"
#include "nvim/iconv.h"
#include "nvim/if_cscope.h"
#include "nvim/lua/executor.h"
#include "nvim/main.h"
+#include "nvim/mapping.h"
+#include "nvim/ui_client.h"
#include "nvim/vim.h"
#ifdef HAVE_LOCALE_H
# include <locale.h>
#endif
#include "nvim/garray.h"
+#include "nvim/grid.h"
#include "nvim/log.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
@@ -110,14 +115,12 @@ static const char *err_too_many_args = N_("Too many edit arguments");
static const char *err_extra_cmd =
N_("Too many \"+command\", \"-c command\" or \"--cmd command\" arguments");
-
void event_init(void)
{
loop_init(&main_loop, NULL);
resize_events = multiqueue_new_child(main_loop.events);
// early msgpack-rpc initialization
- msgpack_rpc_init_method_table();
msgpack_rpc_helpers_init();
input_init();
signal_init();
@@ -125,6 +128,7 @@ void event_init(void)
channel_init();
terminal_init();
ui_init();
+ TIME_MSG("event init");
}
/// @returns false if main_loop could not be closed gracefully
@@ -154,10 +158,11 @@ bool event_teardown(void)
void early_init(mparm_T *paramp)
{
env_init();
- fs_init();
+ cmdline_init();
eval_init(); // init global variables
init_path(argv0 ? argv0 : "nvim");
init_normal_cmds(); // Init the table of Normal mode commands.
+ runtime_init();
highlight_init();
#ifdef WIN32
@@ -168,6 +173,8 @@ void early_init(mparm_T *paramp)
(int)ovi.dwMajorVersion, (int)ovi.dwMinorVersion);
#endif
+ TIME_MSG("early init");
+
#if defined(HAVE_LOCALE_H)
// Setup to use the current locale (for ctype() and many other things).
// NOTE: Translated messages with encodings other than latin1 will not
@@ -180,8 +187,7 @@ void early_init(mparm_T *paramp)
if (!win_alloc_first()) {
os_exit(0);
}
-
- init_yank(); // init yank buffers
+ TIME_MSG("init first window");
alist_init(&global_alist); // Init the argument list to empty.
global_alist.id = 0;
@@ -230,6 +236,10 @@ int main(int argc, char **argv)
// `argc` and `argv` are also copied, so that they can be changed.
init_params(&params, argc, argv);
+ // Since os_open is called during the init_startuptime, we need to call
+ // fs_init before it.
+ fs_init();
+
init_startuptime(&params);
// Need to find "--clean" before actually parsing arguments.
@@ -249,12 +259,14 @@ int main(int argc, char **argv)
// Check if we have an interactive window.
check_and_set_isatty(&params);
- nlua_init();
-
// Process the command line arguments. File names are put in the global
// argument list "global_alist".
command_line_scan(&params);
+ nlua_init();
+
+ TIME_MSG("init lua interpreter");
+
if (embedded_mode) {
const char *err;
if (!channel_from_stdio(true, CALLBACK_READER_INIT, &err)) {
@@ -263,6 +275,9 @@ int main(int argc, char **argv)
}
server_init(params.listen_addr);
+ if (params.remote) {
+ remote_request(&params, params.remote, params.server_addr, argc, argv);
+ }
if (GARGCOUNT > 0) {
fname = get_fname(&params, cwd);
@@ -335,15 +350,30 @@ int main(int argc, char **argv)
TIME_MSG("init screen for UI");
}
- init_default_mappings(); // Default mappings.
+ if (ui_client_channel_id) {
+ ui_client_init(ui_client_channel_id);
+ ui_client_execute(ui_client_channel_id);
+ abort(); // unreachable
+ }
+
+ // Default mappings (incl. menus)
+ Error err = ERROR_INIT;
+ Object o = nlua_exec(STATIC_CSTR_AS_STRING("return vim._init_default_mappings()"),
+ (Array)ARRAY_DICT_INIT, &err);
+ assert(!ERROR_SET(&err));
+ api_clear_error(&err);
+ assert(o.type == kObjectTypeNil);
+ api_free_object(o);
TIME_MSG("init default mappings");
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 +381,23 @@ int main(int argc, char **argv)
// Execute --cmd arguments.
exe_pre_commands(&params);
+ if (!vimrc_none || params.clean) {
+ // 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 || params.clean) {
+ // 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();
}
@@ -426,7 +465,7 @@ int main(int argc, char **argv)
// writing end of the pipe doesn't like, e.g., in case stdin and stderr
// are the same terminal: "cat | vim -".
// Using autocommands here may cause trouble...
- if (params.edit_type == EDIT_STDIN && !recoverymode) {
+ if ((params.edit_type == EDIT_STDIN || stdin_fd >= 0) && !recoverymode) {
read_stdin();
}
@@ -485,7 +524,7 @@ int main(int argc, char **argv)
// Need to jump to the tag before executing the '-c command'.
// Makes "vim -c '/return' -t main" work.
- handle_tag(params.tagname);
+ handle_tag((char_u *)params.tagname);
// Execute any "+", "-c" and "-S" arguments.
if (params.n_commands > 0) {
@@ -501,11 +540,6 @@ int main(int argc, char **argv)
// 'autochdir' has been postponed.
do_autochdir();
- // start in insert mode
- if (p_im) {
- need_start_insertmode = true;
- }
-
set_vim_var_nr(VV_VIM_DID_ENTER, 1L);
apply_autocmds(EVENT_VIMENTER, NULL, NULL, false, curbuf);
TIME_MSG("VimEnter autocommands");
@@ -614,8 +648,7 @@ void getout(int exitval)
bufref_T bufref;
set_bufref(&bufref, buf);
- apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname,
- buf->b_fname, false, buf);
+ apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname, buf->b_fname, false, buf);
if (bufref_valid(&bufref)) {
buf_set_changedtick(buf, -1); // note that we did it already
}
@@ -776,9 +809,9 @@ static void init_locale(void)
char localepath[MAXPATHL] = { 0 };
snprintf(localepath, sizeof(localepath), "%s", get_vim_var_str(VV_PROGPATH));
- char *tail = (char *)path_tail_with_sep((char_u *)localepath);
+ char *tail = path_tail_with_sep(localepath);
*tail = NUL;
- tail = (char *)path_tail((char_u *)localepath);
+ tail = path_tail(localepath);
xstrlcpy(tail, "share/locale",
sizeof(localepath) - (size_t)(tail - localepath));
bindtextdomain(PROJECT_NAME, localepath);
@@ -787,6 +820,112 @@ static void init_locale(void)
}
#endif
+static uint64_t server_connect(char *server_addr, const char **errmsg)
+{
+ if (server_addr == NULL) {
+ *errmsg = "no address specified";
+ return 0;
+ }
+ CallbackReader on_data = CALLBACK_READER_INIT;
+ const char *error = NULL;
+ bool is_tcp = strrchr(server_addr, ':') ? true : false;
+ // connected to channel
+ uint64_t chan = channel_connect(is_tcp, server_addr, true, on_data, 50, &error);
+ if (error) {
+ *errmsg = error;
+ return 0;
+ }
+ return chan;
+}
+
+/// Handle remote subcommands
+static void remote_request(mparm_T *params, int remote_args, char *server_addr, int argc,
+ char **argv)
+{
+ const char *connect_error = NULL;
+ uint64_t chan = server_connect(server_addr, &connect_error);
+ Object rvobj = OBJECT_INIT;
+
+ if (strequal(argv[remote_args], "--remote-ui-test")) {
+ if (!chan) {
+ emsg(connect_error);
+ exit(1);
+ }
+
+ ui_client_channel_id = chan;
+ return;
+ }
+
+ Array args = ARRAY_DICT_INIT;
+ String arg_s;
+ for (int t_argc = remote_args; t_argc < argc; t_argc++) {
+ arg_s = cstr_to_string(argv[t_argc]);
+ ADD(args, STRING_OBJ(arg_s));
+ }
+
+ Error err = ERROR_INIT;
+ Array a = ARRAY_DICT_INIT;
+ ADD(a, INTEGER_OBJ((int)chan));
+ ADD(a, CSTR_TO_OBJ(server_addr));
+ ADD(a, CSTR_TO_OBJ(connect_error));
+ ADD(a, ARRAY_OBJ(args));
+ String s = STATIC_CSTR_AS_STRING("return vim._cs_remote(...)");
+ Object o = nlua_exec(s, a, &err);
+ api_free_array(a);
+ if (ERROR_SET(&err)) {
+ mch_errmsg(err.msg);
+ mch_errmsg("\n");
+ os_exit(2);
+ }
+
+ if (o.type == kObjectTypeDictionary) {
+ rvobj.data.dictionary = o.data.dictionary;
+ } else {
+ mch_errmsg("vim._cs_remote returned unexpected value\n");
+ os_exit(2);
+ }
+
+ TriState should_exit = kNone;
+ TriState tabbed = kNone;
+
+ for (size_t i = 0; i < rvobj.data.dictionary.size; i++) {
+ if (strcmp(rvobj.data.dictionary.items[i].key.data, "errmsg") == 0) {
+ if (rvobj.data.dictionary.items[i].value.type != kObjectTypeString) {
+ mch_errmsg("vim._cs_remote returned an unexpected type for 'errmsg'\n");
+ os_exit(2);
+ }
+ mch_errmsg(rvobj.data.dictionary.items[i].value.data.string.data);
+ mch_errmsg("\n");
+ os_exit(2);
+ } else if (strcmp(rvobj.data.dictionary.items[i].key.data, "tabbed") == 0) {
+ if (rvobj.data.dictionary.items[i].value.type != kObjectTypeBoolean) {
+ mch_errmsg("vim._cs_remote returned an unexpected type for 'tabbed'\n");
+ os_exit(2);
+ }
+ tabbed = rvobj.data.dictionary.items[i].value.data.boolean ? kTrue : kFalse;
+ } else if (strcmp(rvobj.data.dictionary.items[i].key.data, "should_exit") == 0) {
+ if (rvobj.data.dictionary.items[i].value.type != kObjectTypeBoolean) {
+ mch_errmsg("vim._cs_remote returned an unexpected type for 'should_exit'\n");
+ os_exit(2);
+ }
+ should_exit = rvobj.data.dictionary.items[i].value.data.boolean ? kTrue : kFalse;
+ }
+ }
+ if (should_exit == kNone || tabbed == kNone) {
+ mch_errmsg("vim._cs_remote didn't return a value for should_exit or tabbed, bailing\n");
+ os_exit(2);
+ }
+ api_free_object(o);
+
+ if (should_exit == kTrue) {
+ os_exit(0);
+ }
+ if (tabbed == kTrue) {
+ params->window_count = argc - remote_args - 1;
+ params->window_layout = WIN_TABS;
+ }
+}
+
/// Decides whether text (as opposed to commands) will be read from stdin.
/// @see EDIT_STDIN
static bool edit_stdin(bool explicit, mparm_T *parmp)
@@ -808,7 +947,6 @@ static void command_line_scan(mparm_T *parmp)
bool had_stdin_file = false; // found explicit "-" argument
bool had_minmin = false; // found "--" argument
int want_argument; // option argument with argument
- int c;
long n;
argc--;
@@ -830,7 +968,7 @@ static void command_line_scan(mparm_T *parmp)
// Optional argument.
} else if (argv[0][0] == '-' && !had_minmin) {
want_argument = false;
- c = argv[0][argv_idx++];
+ char c = argv[0][argv_idx++];
switch (c) {
case NUL: // "nvim -" read from stdin
if (exmode_active) {
@@ -853,6 +991,8 @@ static void command_line_scan(mparm_T *parmp)
// "--version" give version message
// "--noplugin[s]" skip plugins
// "--cmd <cmd>" execute cmd before vimrc
+ // "--remote" execute commands remotey on a server
+ // "--server" name of vim server to send remote commands to
if (STRICMP(argv[0] + argv_idx, "help") == 0) {
usage();
os_exit(0);
@@ -891,6 +1031,11 @@ static void command_line_scan(mparm_T *parmp)
argv_idx += 6;
} else if (STRNICMP(argv[0] + argv_idx, "literal", 7) == 0) {
// Do nothing: file args are always literal. #7679
+ } else if (STRNICMP(argv[0] + argv_idx, "remote", 6) == 0) {
+ parmp->remote = parmp->argc - argc;
+ } else if (STRNICMP(argv[0] + argv_idx, "server", 6) == 0) {
+ want_argument = true;
+ argv_idx += 6;
} else if (STRNICMP(argv[0] + argv_idx, "noplugin", 8) == 0) {
p_lpl = false;
} else if (STRNICMP(argv[0] + argv_idx, "cmd", 3) == 0) {
@@ -903,6 +1048,8 @@ static void command_line_scan(mparm_T *parmp)
parmp->use_vimrc = "NONE";
parmp->clean = true;
set_option_value("shadafile", 0L, "NONE", 0);
+ } else if (STRNICMP(argv[0] + argv_idx, "luamod-dev", 9) == 0) {
+ nlua_disable_preload = true;
} else {
if (argv[0][argv_idx]) {
mainerr(err_opt_unknown, argv[0]);
@@ -987,7 +1134,7 @@ static void command_line_scan(mparm_T *parmp)
}
parmp->edit_type = EDIT_QF;
if (argv[0][argv_idx]) { // "-q{errorfile}"
- parmp->use_ef = (char_u *)argv[0] + argv_idx;
+ parmp->use_ef = argv[0] + argv_idx;
argv_idx = -1;
} else if (argc > 1) { // "-q {errorfile}"
want_argument = true;
@@ -1016,7 +1163,7 @@ static void command_line_scan(mparm_T *parmp)
}
parmp->edit_type = EDIT_TAG;
if (argv[0][argv_idx]) { // "-t{tag}"
- parmp->tagname = (char_u *)argv[0] + argv_idx;
+ parmp->tagname = argv[0] + argv_idx;
argv_idx = -1;
} else { // "-t {tag}"
want_argument = true;
@@ -1120,12 +1267,15 @@ static void command_line_scan(mparm_T *parmp)
} else if (strequal(argv[-1], "--listen")) {
// "--listen {address}"
parmp->listen_addr = argv[0];
+ } else if (strequal(argv[-1], "--server")) {
+ // "--server {address}"
+ parmp->server_addr = argv[0];
}
// "--startuptime <file>" already handled
break;
case 'q': // "-q {errorfile}" QuickFix mode
- parmp->use_ef = (char_u *)argv[0];
+ parmp->use_ef = argv[0];
break;
case 'i': // "-i {shada}" use for shada
@@ -1166,7 +1316,7 @@ scripterror:
}
case 't': // "-t {tag}"
- parmp->tagname = (char_u *)argv[0];
+ parmp->tagname = argv[0];
break;
case 'u': // "-u {vimrc}" vim inits file
parmp->use_vimrc = argv[0];
@@ -1211,12 +1361,11 @@ scripterror:
// Add the file to the global argument list.
ga_grow(&global_alist.al_ga, 1);
- char_u *p = vim_strsave((char_u *)argv[0]);
+ char *p = xstrdup(argv[0]);
- if (parmp->diff_mode && os_isdir(p) && GARGCOUNT > 0
- && !os_isdir(alist_name(&GARGLIST[0]))) {
- char_u *r = (char_u *)concat_fnames((char *)p,
- (char *)path_tail(alist_name(&GARGLIST[0])), true);
+ if (parmp->diff_mode && os_isdir((char_u *)p) && GARGCOUNT > 0
+ && !os_isdir((char_u *)alist_name(&GARGLIST[0]))) {
+ char *r = concat_fnames(p, path_tail(alist_name(&GARGLIST[0])), true);
xfree(p);
p = r;
}
@@ -1274,6 +1423,8 @@ static void init_params(mparm_T *paramp, int argc, char **argv)
paramp->use_debug_break_level = -1;
paramp->window_count = -1;
paramp->listen_addr = NULL;
+ paramp->server_addr = NULL;
+ paramp->remote = 0;
}
/// Initialize global startuptime file if "--startuptime" passed as an argument.
@@ -1320,7 +1471,7 @@ static void init_path(const char *exename)
path_guess_exepath(exename, exepath, sizeof(exepath));
}
set_vim_var_string(VV_PROGPATH, exepath, -1);
- set_vim_var_string(VV_PROGNAME, (char *)path_tail((char_u *)exename), -1);
+ set_vim_var_string(VV_PROGNAME, path_tail(exename), -1);
#ifdef WIN32
// Append the process start directory to $PATH, so that ":!foo" finds tools
@@ -1332,7 +1483,7 @@ static void init_path(const char *exename)
/// Get filename from command line, if any.
static char_u *get_fname(mparm_T *parmp, char_u *cwd)
{
- return alist_name(&GARGLIST[0]);
+ return (char_u *)alist_name(&GARGLIST[0]);
}
/*
@@ -1349,7 +1500,6 @@ static void set_window_layout(mparm_T *paramp)
}
}
-
/*
* "-q errorfile": Load the error file now.
* If the error file can't be read, exit before doing anything else.
@@ -1361,7 +1511,7 @@ static void handle_quickfix(mparm_T *paramp)
set_string_option_direct("ef", -1, paramp->use_ef, OPT_FREE, SID_CARG);
}
vim_snprintf((char *)IObuff, IOSIZE, "cfile %s", p_ef);
- if (qf_init(NULL, p_ef, p_efm, true, IObuff, p_menc) < 0) {
+ if (qf_init(NULL, (char *)p_ef, p_efm, true, (char *)IObuff, (char *)p_menc) < 0) {
msg_putchar('\n');
os_exit(3);
}
@@ -1549,7 +1699,7 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd)
// When w_arg_idx is -1 remove the window (see create_windows()).
if (curwin->w_arg_idx == -1) {
- win_close(curwin, true);
+ win_close(curwin, true, false);
advance = false;
}
@@ -1561,7 +1711,7 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd)
// When w_arg_idx is -1 remove the window (see create_windows()).
if (curwin->w_arg_idx == -1) {
arg_idx++;
- win_close(curwin, true);
+ win_close(curwin, true, false);
advance = false;
continue;
}
@@ -1599,8 +1749,8 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd)
// at the ATTENTION prompt close the window.
swap_exists_did_quit = false;
(void)do_ecmd(0, arg_idx < GARGCOUNT
- ? alist_name(&GARGLIST[arg_idx]) : NULL,
- NULL, NULL, ECMD_LASTL, ECMD_HIDE, curwin);
+ ? alist_name(&GARGLIST[arg_idx])
+ : NULL, NULL, NULL, ECMD_LASTL, ECMD_HIDE, curwin);
if (swap_exists_did_quit) {
// abort or quit selected
if (got_int || only_one_window()) {
@@ -1608,7 +1758,7 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd)
did_emsg = FALSE; // avoid hit-enter prompt
getout(1);
}
- win_close(curwin, true);
+ win_close(curwin, true, false);
advance = false;
}
if (arg_idx == GARGCOUNT - 1) {
@@ -1663,7 +1813,7 @@ static void exe_pre_commands(mparm_T *parmp)
if (cnt > 0) {
curwin->w_cursor.lnum = 0; // just in case..
- sourcing_name = (char_u *)_("pre-vimrc command line");
+ sourcing_name = _("pre-vimrc command line");
current_sctx.sc_sid = SID_CMDARG;
for (i = 0; i < cnt; i++) {
do_cmdline_cmd(cmds[i]);
@@ -1690,7 +1840,7 @@ static void exe_commands(mparm_T *parmp)
if (parmp->tagname == NULL && curwin->w_cursor.lnum <= 1) {
curwin->w_cursor.lnum = 0;
}
- sourcing_name = (char_u *)"command line";
+ sourcing_name = "command line";
current_sctx.sc_sid = SID_CARG;
current_sctx.sc_seq = 0;
for (i = 0; i < parmp->n_commands; i++) {
@@ -1802,9 +1952,7 @@ static bool do_user_initialization(void)
if (do_source((char *)user_vimrc, true, DOSO_VIMRC) != FAIL) {
do_exrc = p_exrc;
if (do_exrc) {
- do_exrc = (path_full_compare((char_u *)VIMRC_FILE, user_vimrc,
- false, true)
- != kEqualFiles);
+ do_exrc = (path_full_compare(VIMRC_FILE, (char *)user_vimrc, false, true) != kEqualFiles);
}
xfree(user_vimrc);
return do_exrc;
@@ -1830,8 +1978,7 @@ static bool do_user_initialization(void)
if (do_source(vimrc, true, DOSO_VIMRC) != FAIL) {
do_exrc = p_exrc;
if (do_exrc) {
- do_exrc = (path_full_compare((char_u *)VIMRC_FILE, (char_u *)vimrc,
- false, true) != kEqualFiles);
+ do_exrc = (path_full_compare(VIMRC_FILE, vimrc, false, true) != kEqualFiles);
}
xfree(vimrc);
xfree(config_dirs);
@@ -1876,14 +2023,14 @@ static void source_startup_scripts(const mparm_T *const parmp)
// do_user_initialization.
#if defined(UNIX)
// If vimrc file is not owned by user, set 'secure' mode.
- if (!file_owned(VIMRC_FILE))
+ if (!os_file_owned(VIMRC_FILE)) // NOLINT(readability/braces)
#endif
secure = p_secure;
if (do_source(VIMRC_FILE, true, DOSO_VIMRC) == FAIL) {
#if defined(UNIX)
// if ".exrc" is not owned by user set 'secure' mode
- if (!file_owned(EXRC_FILE)) {
+ if (!os_file_owned(EXRC_FILE)) {
secure = p_secure;
} else {
secure = 0;
@@ -1911,16 +2058,16 @@ static int execute_env(char *env)
{
const char *initstr = os_getenv(env);
if (initstr != NULL) {
- char_u *save_sourcing_name = sourcing_name;
+ char_u *save_sourcing_name = (char_u *)sourcing_name;
linenr_T save_sourcing_lnum = sourcing_lnum;
- sourcing_name = (char_u *)env;
+ sourcing_name = env;
sourcing_lnum = 0;
const sctx_T save_current_sctx = current_sctx;
current_sctx.sc_sid = SID_ENV;
current_sctx.sc_seq = 0;
current_sctx.sc_lnum = 0;
do_cmdline_cmd((char *)initstr);
- sourcing_name = save_sourcing_name;
+ sourcing_name = (char *)save_sourcing_name;
sourcing_lnum = save_sourcing_lnum;
current_sctx = save_current_sctx;
return OK;
@@ -1928,23 +2075,6 @@ static int execute_env(char *env)
return FAIL;
}
-#ifdef UNIX
-/// Checks if user owns file.
-/// Use both uv_fs_stat() and uv_fs_lstat() through os_fileinfo() and
-/// os_fileinfo_link() respectively for extra security.
-static bool file_owned(const char *fname)
-{
- assert(fname != NULL);
- uid_t uid = getuid();
- FileInfo file_info;
- bool file_owned = os_fileinfo(fname, &file_info)
- && file_info.stat.st_uid == uid;
- bool link_owned = os_fileinfo_link(fname, &file_info)
- && file_info.stat.st_uid == uid;
- return file_owned && link_owned;
-}
-#endif
-
/// Prints the following then exits:
/// - An error message `errstr`
/// - A string `str` if not null
@@ -1952,8 +2082,9 @@ static bool file_owned(const char *fname)
/// @param errstr string containing an error message
/// @param str string to append to the primary error message, or NULL
static void mainerr(const char *errstr, const char *str)
+ FUNC_ATTR_NORETURN
{
- char *prgname = (char *)path_tail((char_u *)argv0);
+ char *prgname = path_tail(argv0);
signal_stop(); // kill us with CTRL-C here, if you like
@@ -1975,6 +2106,8 @@ static void mainerr(const char *errstr, const char *str)
/// Prints version information for "nvim -v" or "nvim --version".
static void version(void)
{
+ // TODO(bfred): not like this?
+ nlua_init();
info_message = true; // use mch_msg(), not mch_errmsg()
list_version();
msg_putchar('\n');
@@ -2022,11 +2155,12 @@ static void usage(void)
mch_msg(_(" --headless Don't start a user interface\n"));
mch_msg(_(" --listen <address> Serve RPC API from this address\n"));
mch_msg(_(" --noplugin Don't load plugins\n"));
+ mch_msg(_(" --remote[-subcommand] Execute commands remotely on a server\n"));
+ mch_msg(_(" --server <address> Specify RPC server to send commands to\n"));
mch_msg(_(" --startuptime <file> Write startup timing messages to <file>\n"));
mch_msg(_("\nSee \":help startup-options\" for all options.\n"));
}
-
/*
* Check the result of the ATTENTION dialog:
* When "Quit" selected, exit Vim.
diff --git a/src/nvim/main.h b/src/nvim/main.h
index f73af5c288..d5384ecc95 100644
--- a/src/nvim/main.h
+++ b/src/nvim/main.h
@@ -19,13 +19,13 @@ typedef struct {
int n_commands; // no. of commands from + or -c
char *commands[MAX_ARG_CMDS]; // commands from + or -c arg
- char_u cmds_tofree[MAX_ARG_CMDS]; // commands that need free()
+ char cmds_tofree[MAX_ARG_CMDS]; // commands that need free()
int n_pre_commands; // no. of commands from --cmd
char *pre_commands[MAX_ARG_CMDS]; // commands from --cmd argument
int edit_type; // type of editing to do
- char_u *tagname; // tag from -t argument
- char_u *use_ef; // 'errorfile' from -q argument
+ char *tagname; // tag from -t argument
+ char *use_ef; // 'errorfile' from -q argument
bool input_isatty; // stdin is a terminal
bool output_isatty; // stdout is a terminal
@@ -39,6 +39,8 @@ typedef struct {
int diff_mode; // start with 'diff' set
char *listen_addr; // --listen {address}
+ int remote; // --remote-[subcmd] {file1} {file2}
+ char *server_addr; // --server {address}
} mparm_T;
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/map.c b/src/nvim/map.c
index 11102b022c..86e7317b56 100644
--- a/src/nvim/map.c
+++ b/src/nvim/map.c
@@ -14,7 +14,6 @@
#include <stdlib.h>
#include <string.h>
-#include "nvim/api/private/dispatch.h"
#include "nvim/lib/khash.h"
#include "nvim/map.h"
#include "nvim/map_defs.h"
@@ -31,14 +30,15 @@
#define int_eq kh_int_hash_equal
#define handle_T_hash kh_int_hash_func
#define handle_T_eq kh_int_hash_equal
-
+#define KittyKey_hash kh_int_hash_func
+#define KittyKey_eq kh_int_hash_equal
#if defined(ARCH_64)
-# define ptr_t_hash(key) uint64_t_hash((uint64_t)key)
-# define ptr_t_eq(a, b) uint64_t_eq((uint64_t)a, (uint64_t)b)
+# define ptr_t_hash(key) uint64_t_hash((uint64_t)(key))
+# define ptr_t_eq(a, b) uint64_t_eq((uint64_t)(a), (uint64_t)(b))
#elif defined(ARCH_32)
-# define ptr_t_hash(key) uint32_t_hash((uint32_t)key)
-# define ptr_t_eq(a, b) uint32_t_eq((uint32_t)a, (uint32_t)b)
+# define ptr_t_hash(key) uint32_t_hash((uint32_t)(key))
+# define ptr_t_eq(a, b) uint32_t_eq((uint32_t)(a), (uint32_t)(b))
#endif
#define INITIALIZER(T, U) T##_##U##_initializer
@@ -163,28 +163,27 @@ static inline bool ColorKey_eq(ColorKey ae1, ColorKey ae2)
return memcmp(&ae1, &ae2, sizeof(ae1)) == 0;
}
-
MAP_IMPL(ptr_t, int, DEFAULT_INITIALIZER)
MAP_IMPL(int, ptr_t, DEFAULT_INITIALIZER)
MAP_IMPL(int, int, DEFAULT_INITIALIZER)
+MAP_IMPL(int, cstr_t, DEFAULT_INITIALIZER)
MAP_IMPL(cstr_t, ptr_t, DEFAULT_INITIALIZER)
MAP_IMPL(cstr_t, int, DEFAULT_INITIALIZER)
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)
MAP_IMPL(HlEntry, int, DEFAULT_INITIALIZER)
MAP_IMPL(String, handle_T, 0)
+MAP_IMPL(String, int, DEFAULT_INITIALIZER)
+MAP_IMPL(int, String, DEFAULT_INITIALIZER)
MAP_IMPL(ColorKey, ColorItem, COLOR_ITEM_INITIALIZER)
+MAP_IMPL(KittyKey, cstr_t, DEFAULT_INITIALIZER)
+
/// Deletes a key:value pair from a string:pointer map, and frees the
/// storage of both key and value.
///
diff --git a/src/nvim/map.h b/src/nvim/map.h
index d94cfa92e8..acc796bd98 100644
--- a/src/nvim/map.h
+++ b/src/nvim/map.h
@@ -4,10 +4,11 @@
#include <stdbool.h>
#include "nvim/api/private/defs.h"
-#include "nvim/api/private/dispatch.h"
#include "nvim/extmark_defs.h"
#include "nvim/highlight_defs.h"
#include "nvim/map_defs.h"
+#include "nvim/tui/input_defs.h"
+#include "nvim/ui_client.h"
#if defined(__NetBSD__)
# undef uint64_t
@@ -36,29 +37,25 @@
MAP_DECLS(int, int)
MAP_DECLS(int, ptr_t)
MAP_DECLS(ptr_t, int)
+MAP_DECLS(int, cstr_t)
MAP_DECLS(cstr_t, ptr_t)
MAP_DECLS(cstr_t, int)
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)
MAP_DECLS(String, handle_T)
+MAP_DECLS(String, int)
+MAP_DECLS(int, String)
MAP_DECLS(ColorKey, ColorItem)
+MAP_DECLS(KittyKey, cstr_t)
+
#define MAP_INIT { { 0, 0, 0, 0, NULL, NULL, NULL } }
#define map_init(k, v, map) do { *(map) = (Map(k, v)) MAP_INIT; } while (false)
diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c
new file mode 100644
index 0000000000..5a11ac686e
--- /dev/null
+++ b/src/nvim/mapping.c
@@ -0,0 +1,2547 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+// mapping.c: Code for mappings and abbreviations.
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "nvim/api/private/converter.h"
+#include "nvim/api/private/helpers.h"
+#include "nvim/ascii.h"
+#include "nvim/assert.h"
+#include "nvim/buffer_defs.h"
+#include "nvim/charset.h"
+#include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/ex_docmd.h"
+#include "nvim/ex_session.h"
+#include "nvim/func_attr.h"
+#include "nvim/garray.h"
+#include "nvim/getchar.h"
+#include "nvim/keycodes.h"
+#include "nvim/lua/executor.h"
+#include "nvim/mapping.h"
+#include "nvim/mbyte.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
+#include "nvim/option.h"
+#include "nvim/regexp.h"
+#include "nvim/ui.h"
+#include "nvim/vim.h"
+
+/// List used for abbreviations.
+static mapblock_T *first_abbr = NULL; // first entry in abbrlist
+
+// Each mapping is put in one of the MAX_MAPHASH hash lists,
+// to speed up finding it.
+static mapblock_T *(maphash[MAX_MAPHASH]) = { 0 };
+
+// Make a hash value for a mapping.
+// "mode" is the lower 4 bits of the State for the mapping.
+// "c1" is the first character of the "lhs".
+// Returns a value between 0 and 255, index in maphash.
+// Put Normal/Visual mode mappings mostly separately from Insert/Cmdline mode.
+#define MAP_HASH(mode, \
+ c1) (((mode) & \
+ (MODE_NORMAL | MODE_VISUAL | MODE_SELECT | \
+ MODE_OP_PENDING | MODE_TERMINAL)) ? (c1) : ((c1) ^ 0x80))
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "mapping.c.generated.h"
+#endif
+
+/// Get the start of the hashed map list for "state" and first character "c".
+mapblock_T *get_maphash_list(int state, int c)
+{
+ return maphash[MAP_HASH(state, c)];
+}
+
+/// Get the buffer-local hashed map list for "state" and first character "c".
+mapblock_T *get_buf_maphash_list(int state, int c)
+{
+ return curbuf->b_maphash[MAP_HASH(state, c)];
+}
+
+/// Retrieve the mapblock at the index either globally or for a certain buffer
+///
+/// @param index The index in the maphash[]
+/// @param buf The buffer to get the maphash from. NULL for global
+mapblock_T *get_maphash(int index, buf_T *buf)
+ FUNC_ATTR_PURE
+{
+ if (index >= MAX_MAPHASH) {
+ return NULL;
+ }
+
+ return (buf == NULL) ? maphash[index] : buf->b_maphash[index];
+}
+
+/// Delete one entry from the abbrlist or maphash[].
+/// "mpp" is a pointer to the m_next field of the PREVIOUS entry!
+static void mapblock_free(mapblock_T **mpp)
+{
+ mapblock_T *mp;
+
+ mp = *mpp;
+ xfree(mp->m_keys);
+ if (!mp->m_simplified) {
+ NLUA_CLEAR_REF(mp->m_luaref);
+ xfree(mp->m_str);
+ xfree(mp->m_orig_str);
+ }
+ xfree(mp->m_desc);
+ *mpp = mp->m_next;
+ xfree(mp);
+}
+
+/// Return characters to represent the map mode in an allocated string
+///
+/// @return [allocated] NUL-terminated string with characters.
+static char *map_mode_to_chars(int mode)
+ FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_RET
+{
+ garray_T mapmode;
+
+ ga_init(&mapmode, 1, 7);
+
+ if ((mode & (MODE_INSERT | MODE_CMDLINE)) == (MODE_INSERT | MODE_CMDLINE)) {
+ ga_append(&mapmode, '!'); // :map!
+ } else if (mode & MODE_INSERT) {
+ ga_append(&mapmode, 'i'); // :imap
+ } else if (mode & MODE_LANGMAP) {
+ ga_append(&mapmode, 'l'); // :lmap
+ } else if (mode & MODE_CMDLINE) {
+ ga_append(&mapmode, 'c'); // :cmap
+ } else if ((mode & (MODE_NORMAL | MODE_VISUAL | MODE_SELECT | MODE_OP_PENDING))
+ == (MODE_NORMAL | MODE_VISUAL | MODE_SELECT | MODE_OP_PENDING)) {
+ ga_append(&mapmode, ' '); // :map
+ } else {
+ if (mode & MODE_NORMAL) {
+ ga_append(&mapmode, 'n'); // :nmap
+ }
+ if (mode & MODE_OP_PENDING) {
+ ga_append(&mapmode, 'o'); // :omap
+ }
+ if (mode & MODE_TERMINAL) {
+ ga_append(&mapmode, 't'); // :tmap
+ }
+ if ((mode & (MODE_VISUAL | MODE_SELECT)) == (MODE_VISUAL | MODE_SELECT)) {
+ ga_append(&mapmode, 'v'); // :vmap
+ } else {
+ if (mode & MODE_VISUAL) {
+ ga_append(&mapmode, 'x'); // :xmap
+ }
+ if (mode & MODE_SELECT) {
+ ga_append(&mapmode, 's'); // :smap
+ }
+ }
+ }
+
+ ga_append(&mapmode, NUL);
+ return (char *)mapmode.ga_data;
+}
+
+/// @param local true for buffer-local map
+static void showmap(mapblock_T *mp, bool local)
+{
+ size_t len = 1;
+
+ if (message_filtered(mp->m_keys) && message_filtered(mp->m_str)
+ && (mp->m_desc == NULL || message_filtered((char_u *)mp->m_desc))) {
+ return;
+ }
+
+ if (msg_didout || msg_silent != 0) {
+ msg_putchar('\n');
+ if (got_int) { // 'q' typed at MORE prompt
+ return;
+ }
+ }
+
+ {
+ char *const mapchars = map_mode_to_chars(mp->m_mode);
+ msg_puts(mapchars);
+ len = strlen(mapchars);
+ xfree(mapchars);
+ }
+
+ while (++len <= 3) {
+ msg_putchar(' ');
+ }
+
+ // Display the LHS. Get length of what we write.
+ len = (size_t)msg_outtrans_special(mp->m_keys, true, 0);
+ do {
+ msg_putchar(' '); // padd with blanks
+ len++;
+ } while (len < 12);
+
+ if (mp->m_noremap == REMAP_NONE) {
+ msg_puts_attr("*", HL_ATTR(HLF_8));
+ } else if (mp->m_noremap == REMAP_SCRIPT) {
+ msg_puts_attr("&", HL_ATTR(HLF_8));
+ } else {
+ msg_putchar(' ');
+ }
+
+ if (local) {
+ msg_putchar('@');
+ } else {
+ msg_putchar(' ');
+ }
+
+ // Use false below if we only want things like <Up> to show up as such on
+ // the rhs, and not M-x etc, true gets both -- webb
+ if (mp->m_luaref != LUA_NOREF) {
+ char msg[100];
+ snprintf(msg, sizeof(msg), "<Lua function %d>", mp->m_luaref);
+ msg_puts_attr(msg, HL_ATTR(HLF_8));
+ } else if (mp->m_str[0] == NUL) {
+ msg_puts_attr("<Nop>", HL_ATTR(HLF_8));
+ } else {
+ msg_outtrans_special(mp->m_str, false, 0);
+ }
+
+ if (mp->m_desc != NULL) {
+ msg_puts("\n "); // Shift line to same level as rhs.
+ msg_puts(mp->m_desc);
+ }
+ if (p_verbose > 0) {
+ last_set_msg(mp->m_script_ctx);
+ }
+ msg_clr_eos();
+ ui_flush(); // show one line at a time
+}
+
+/// Replace termcodes in the given LHS and RHS and store the results into the
+/// `lhs` and `rhs` of the given @ref MapArguments struct.
+///
+/// `rhs` and `orig_rhs` will both point to new allocated buffers. `orig_rhs`
+/// will hold a copy of the given `orig_rhs`.
+///
+/// The `*_len` variables will be set appropriately. If the length of
+/// the final `lhs` exceeds `MAXMAPLEN`, `lhs_len` will be set equal to the
+/// original larger length and `lhs` will be truncated.
+///
+/// If RHS should be <Nop>, `rhs` will be an empty string, `rhs_len` will be
+/// zero, and `rhs_is_noop` will be set to true.
+///
+/// Any memory allocated by @ref replace_termcodes is freed before this function
+/// returns.
+///
+/// @param[in] orig_lhs Original mapping LHS, with characters to replace.
+/// @param[in] orig_lhs_len `strlen` of orig_lhs.
+/// @param[in] orig_rhs Original mapping RHS, with characters to replace.
+/// @param[in] rhs_lua Lua reference for Lua maps.
+/// @param[in] orig_rhs_len `strlen` of orig_rhs.
+/// @param[in] cpo_flags See param docs for @ref replace_termcodes.
+/// @param[out] mapargs MapArguments struct holding the replaced strings.
+static bool set_maparg_lhs_rhs(const char *const orig_lhs, const size_t orig_lhs_len,
+ const char *const orig_rhs, const size_t orig_rhs_len,
+ const LuaRef rhs_lua, const int cpo_flags,
+ MapArguments *const mapargs)
+{
+ char lhs_buf[128];
+
+ // If mapping has been given as ^V<C_UP> say, then replace the term codes
+ // with the appropriate two bytes. If it is a shifted special key, unshift
+ // it too, giving another two bytes.
+ //
+ // replace_termcodes() may move the result to allocated memory, which
+ // needs to be freed later (*lhs_buf and *rhs_buf).
+ // replace_termcodes() also removes CTRL-Vs and sometimes backslashes.
+ // If something like <C-H> is simplified to 0x08 then mark it as simplified.
+ bool did_simplify = false;
+ const int flags = REPTERM_FROM_PART | REPTERM_DO_LT;
+ char *bufarg = lhs_buf;
+ char *replaced = replace_termcodes(orig_lhs, orig_lhs_len, &bufarg, flags, &did_simplify,
+ cpo_flags);
+ if (replaced == NULL) {
+ return false;
+ }
+ mapargs->lhs_len = STRLEN(replaced);
+ STRLCPY(mapargs->lhs, replaced, sizeof(mapargs->lhs));
+ if (did_simplify) {
+ replaced = replace_termcodes(orig_lhs, orig_lhs_len, &bufarg, flags | REPTERM_NO_SIMPLIFY,
+ NULL, cpo_flags);
+ if (replaced == NULL) {
+ return false;
+ }
+ mapargs->alt_lhs_len = STRLEN(replaced);
+ STRLCPY(mapargs->alt_lhs, replaced, sizeof(mapargs->alt_lhs));
+ } else {
+ mapargs->alt_lhs_len = 0;
+ }
+
+ mapargs->rhs_lua = rhs_lua;
+
+ if (rhs_lua == LUA_NOREF) {
+ mapargs->orig_rhs_len = orig_rhs_len;
+ mapargs->orig_rhs = xcalloc(mapargs->orig_rhs_len + 1, sizeof(char_u));
+ STRLCPY(mapargs->orig_rhs, orig_rhs, mapargs->orig_rhs_len + 1);
+
+ if (STRICMP(orig_rhs, "<nop>") == 0) { // "<Nop>" means nothing
+ mapargs->rhs = xcalloc(1, sizeof(char_u)); // single null-char
+ mapargs->rhs_len = 0;
+ mapargs->rhs_is_noop = true;
+ } else {
+ char *rhs_buf = NULL;
+ replaced = replace_termcodes(orig_rhs, orig_rhs_len, &rhs_buf, REPTERM_DO_LT, NULL,
+ cpo_flags);
+ mapargs->rhs_len = STRLEN(replaced);
+ // XXX: replace_termcodes may produce an empty string even if orig_rhs is non-empty
+ // (e.g. a single ^V, see :h map-empty-rhs)
+ mapargs->rhs_is_noop = orig_rhs_len != 0 && mapargs->rhs_len == 0;
+ mapargs->rhs = (char_u *)replaced;
+ }
+ } else {
+ char tmp_buf[64];
+ // orig_rhs is not used for Lua mappings, but still needs to be a string.
+ mapargs->orig_rhs = xcalloc(1, sizeof(char_u));
+ mapargs->orig_rhs_len = 0;
+ // stores <lua>ref_no<cr> in map_str
+ mapargs->rhs_len = (size_t)vim_snprintf(S_LEN(tmp_buf), "%c%c%c%d\r", K_SPECIAL,
+ (char_u)KS_EXTRA, KE_LUA, rhs_lua);
+ mapargs->rhs = vim_strsave((char_u *)tmp_buf);
+ }
+ return true;
+}
+
+/// Parse a string of |:map-arguments| into a @ref MapArguments struct.
+///
+/// Termcodes, backslashes, CTRL-V's, etc. inside the extracted {lhs} and
+/// {rhs} are replaced by @ref set_maparg_lhs_rhs.
+///
+/// rhs and orig_rhs in the returned mapargs will be set to null or a pointer
+/// to allocated memory and should be freed even on error.
+///
+/// @param[in] strargs String of map args, e.g. "<buffer> <expr><silent>".
+/// May contain leading or trailing whitespace.
+/// @param[in] is_unmap True, if strargs should be parsed like an |:unmap|
+/// command. |:unmap| commands interpret *all* text to the
+/// right of the last map argument as the {lhs} of the
+/// mapping, i.e. a literal ' ' character is treated like
+/// a "<space>", rather than separating the {lhs} from the
+/// {rhs}.
+/// @param[out] mapargs MapArguments struct holding all extracted argument
+/// values.
+/// @return 0 on success, 1 if invalid arguments are detected.
+static int str_to_mapargs(const char_u *strargs, bool is_unmap, MapArguments *mapargs)
+{
+ const char_u *to_parse = strargs;
+ to_parse = (char_u *)skipwhite((char *)to_parse);
+ memset(mapargs, 0, sizeof(*mapargs));
+
+ // Accept <buffer>, <nowait>, <silent>, <expr>, <script>, and <unique> in
+ // any order.
+ while (true) {
+ if (STRNCMP(to_parse, "<buffer>", 8) == 0) {
+ to_parse = (char_u *)skipwhite((char *)to_parse + 8);
+ mapargs->buffer = true;
+ continue;
+ }
+
+ if (STRNCMP(to_parse, "<nowait>", 8) == 0) {
+ to_parse = (char_u *)skipwhite((char *)to_parse + 8);
+ mapargs->nowait = true;
+ continue;
+ }
+
+ if (STRNCMP(to_parse, "<silent>", 8) == 0) {
+ to_parse = (char_u *)skipwhite((char *)to_parse + 8);
+ mapargs->silent = true;
+ continue;
+ }
+
+ // Ignore obsolete "<special>" modifier.
+ if (STRNCMP(to_parse, "<special>", 9) == 0) {
+ to_parse = (char_u *)skipwhite((char *)to_parse + 9);
+ continue;
+ }
+
+ if (STRNCMP(to_parse, "<script>", 8) == 0) {
+ to_parse = (char_u *)skipwhite((char *)to_parse + 8);
+ mapargs->script = true;
+ continue;
+ }
+
+ if (STRNCMP(to_parse, "<expr>", 6) == 0) {
+ to_parse = (char_u *)skipwhite((char *)to_parse + 6);
+ mapargs->expr = true;
+ continue;
+ }
+
+ if (STRNCMP(to_parse, "<unique>", 8) == 0) {
+ to_parse = (char_u *)skipwhite((char *)to_parse + 8);
+ mapargs->unique = true;
+ continue;
+ }
+ break;
+ }
+
+ // Find the next whitespace character, call that the end of {lhs}.
+ //
+ // If a character (e.g. whitespace) is immediately preceded by a CTRL-V,
+ // "scan past" that character, i.e. don't "terminate" LHS with that character
+ // if it's whitespace.
+ //
+ // Treat backslash like CTRL-V when 'cpoptions' does not contain 'B'.
+ //
+ // With :unmap, literal white space is included in the {lhs}; there is no
+ // separate {rhs}.
+ const char *lhs_end = (char *)to_parse;
+ bool do_backslash = (vim_strchr(p_cpo, CPO_BSLASH) == NULL);
+ while (*lhs_end && (is_unmap || !ascii_iswhite(*lhs_end))) {
+ if ((lhs_end[0] == Ctrl_V || (do_backslash && lhs_end[0] == '\\'))
+ && lhs_end[1] != NUL) {
+ lhs_end++; // skip CTRL-V or backslash
+ }
+ lhs_end++;
+ }
+
+ // {lhs_end} is a pointer to the "terminating whitespace" after {lhs}.
+ // Use that to initialize {rhs_start}.
+ const char_u *rhs_start = (char_u *)skipwhite((char *)lhs_end);
+
+ // Given {lhs} might be larger than MAXMAPLEN before replace_termcodes
+ // (e.g. "<Space>" is longer than ' '), so first copy into a buffer.
+ size_t orig_lhs_len = (size_t)((char_u *)lhs_end - to_parse);
+ if (orig_lhs_len >= 256) {
+ return 1;
+ }
+ char_u lhs_to_replace[256];
+ STRLCPY(lhs_to_replace, to_parse, orig_lhs_len + 1);
+
+ size_t orig_rhs_len = STRLEN(rhs_start);
+ if (!set_maparg_lhs_rhs((char *)lhs_to_replace, orig_lhs_len,
+ (char *)rhs_start, orig_rhs_len, LUA_NOREF,
+ CPO_TO_CPO_FLAGS, mapargs)) {
+ return 1;
+ }
+
+ if (mapargs->lhs_len > MAXMAPLEN) {
+ return 1;
+ }
+ return 0;
+}
+
+/// Sets or removes a mapping or abbreviation in buffer `buf`.
+///
+/// @param maptype @see do_map
+/// @param args Fully parsed and "preprocessed" arguments for the
+/// (un)map/abbrev command. Termcodes should have already been
+/// replaced; whitespace, `<` and `>` signs, etc. in {lhs} and
+/// {rhs} are assumed to be literal components of the mapping.
+/// @param mode @see do_map
+/// @param is_abbrev @see do_map
+/// @param buf Target Buffer
+static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, buf_T *buf)
+{
+ mapblock_T *mp, **mpp;
+ const char_u *p;
+ int n;
+ int retval = 0;
+ mapblock_T **abbr_table;
+ mapblock_T **map_table;
+ int noremap;
+
+ map_table = maphash;
+ abbr_table = &first_abbr;
+
+ // For ":noremap" don't remap, otherwise do remap.
+ if (maptype == 2) {
+ noremap = REMAP_NONE;
+ } else {
+ noremap = REMAP_YES;
+ }
+
+ if (args->buffer) {
+ // If <buffer> was given, we'll be searching through the buffer's
+ // mappings/abbreviations, not the globals.
+ map_table = buf->b_maphash;
+ abbr_table = &buf->b_first_abbr;
+ }
+ if (args->script) {
+ noremap = REMAP_SCRIPT;
+ }
+
+ const bool has_lhs = (args->lhs[0] != NUL);
+ const bool has_rhs = args->rhs_lua != LUA_NOREF || (args->rhs[0] != NUL) || args->rhs_is_noop;
+ const bool do_print = !has_lhs || (maptype != 1 && !has_rhs);
+
+ // check for :unmap without argument
+ if (maptype == 1 && !has_lhs) {
+ retval = 1;
+ goto theend;
+ }
+
+ const char_u *lhs = (char_u *)&args->lhs;
+ const bool did_simplify = args->alt_lhs_len != 0;
+
+ // The following is done twice if we have two versions of keys
+ for (int keyround = 1; keyround <= 2; keyround++) {
+ bool did_it = false;
+ bool did_local = false;
+ bool keyround1_simplified = keyround == 1 && did_simplify;
+ int len = (int)args->lhs_len;
+
+ if (keyround == 2) {
+ if (!did_simplify) {
+ break;
+ }
+ lhs = (char_u *)&args->alt_lhs;
+ len = (int)args->alt_lhs_len;
+ } else if (did_simplify && do_print) {
+ // when printing always use the not-simplified map
+ lhs = (char_u *)&args->alt_lhs;
+ len = (int)args->alt_lhs_len;
+ }
+
+ // check arguments and translate function keys
+ if (has_lhs) {
+ if (len > MAXMAPLEN) {
+ retval = 1;
+ goto theend;
+ }
+
+ if (is_abbrev && maptype != 1) {
+ //
+ // If an abbreviation ends in a keyword character, the
+ // rest must be all keyword-char or all non-keyword-char.
+ // Otherwise we won't be able to find the start of it in a
+ // vi-compatible way.
+ //
+ int same = -1;
+
+ const int first = vim_iswordp(lhs);
+ int last = first;
+ p = lhs + utfc_ptr2len((char *)lhs);
+ n = 1;
+ while (p < lhs + len) {
+ n++; // nr of (multi-byte) chars
+ last = vim_iswordp(p); // type of last char
+ if (same == -1 && last != first) {
+ same = n - 1; // count of same char type
+ }
+ p += utfc_ptr2len((char *)p);
+ }
+ if (last && n > 2 && same >= 0 && same < n - 1) {
+ retval = 1;
+ goto theend;
+ }
+ // An abbreviation cannot contain white space.
+ for (n = 0; n < len; n++) {
+ if (ascii_iswhite(lhs[n])) {
+ retval = 1;
+ goto theend;
+ }
+ } // for
+ }
+ }
+
+ if (has_lhs && has_rhs && is_abbrev) { // if we will add an abbreviation,
+ no_abbr = false; // reset flag that indicates there are no abbreviations
+ }
+
+ if (do_print) {
+ msg_start();
+ }
+
+ // Check if a new local mapping wasn't already defined globally.
+ if (args->unique && map_table == buf->b_maphash && has_lhs && has_rhs && maptype != 1) {
+ // need to loop over all global hash lists
+ for (int hash = 0; hash < 256 && !got_int; hash++) {
+ if (is_abbrev) {
+ if (hash != 0) { // there is only one abbreviation list
+ break;
+ }
+ mp = first_abbr;
+ } else {
+ mp = maphash[hash];
+ }
+ for (; mp != NULL && !got_int; mp = mp->m_next) {
+ // check entries with the same mode
+ if ((mp->m_mode & mode) != 0
+ && mp->m_keylen == len
+ && STRNCMP(mp->m_keys, lhs, (size_t)len) == 0) {
+ if (is_abbrev) {
+ semsg(_("E224: global abbreviation already exists for %s"),
+ mp->m_keys);
+ } else {
+ semsg(_("E225: global mapping already exists for %s"), mp->m_keys);
+ }
+ retval = 5;
+ goto theend;
+ }
+ }
+ }
+ }
+
+ // When listing global mappings, also list buffer-local ones here.
+ if (map_table != buf->b_maphash && !has_rhs && maptype != 1) {
+ // need to loop over all global hash lists
+ for (int hash = 0; hash < 256 && !got_int; hash++) {
+ if (is_abbrev) {
+ if (hash != 0) { // there is only one abbreviation list
+ break;
+ }
+ mp = buf->b_first_abbr;
+ } else {
+ mp = buf->b_maphash[hash];
+ }
+ for (; mp != NULL && !got_int; mp = mp->m_next) {
+ // check entries with the same mode
+ if (!mp->m_simplified && (mp->m_mode & mode) != 0) {
+ if (!has_lhs) { // show all entries
+ showmap(mp, true);
+ did_local = true;
+ } else {
+ n = mp->m_keylen;
+ if (STRNCMP(mp->m_keys, lhs, (size_t)(n < len ? n : len)) == 0) {
+ showmap(mp, true);
+ did_local = true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Find an entry in the maphash[] list that matches.
+ // For :unmap we may loop two times: once to try to unmap an entry with a
+ // matching 'from' part, a second time, if the first fails, to unmap an
+ // entry with a matching 'to' part. This was done to allow ":ab foo bar"
+ // to be unmapped by typing ":unab foo", where "foo" will be replaced by
+ // "bar" because of the abbreviation.
+ for (int round = 0; (round == 0 || maptype == 1) && round <= 1
+ && !did_it && !got_int; round++) {
+ int hash_start, hash_end;
+ if (has_lhs || is_abbrev) {
+ // just use one hash
+ hash_start = is_abbrev ? 0 : MAP_HASH(mode, lhs[0]);
+ hash_end = hash_start + 1;
+ } else {
+ // need to loop over all hash lists
+ hash_start = 0;
+ hash_end = 256;
+ }
+ for (int hash = hash_start; hash < hash_end && !got_int; hash++) {
+ mpp = is_abbrev ? abbr_table : &(map_table[hash]);
+ for (mp = *mpp; mp != NULL && !got_int; mp = *mpp) {
+ if ((mp->m_mode & mode) == 0) {
+ // skip entries with wrong mode
+ mpp = &(mp->m_next);
+ continue;
+ }
+ if (!has_lhs) { // show all entries
+ if (!mp->m_simplified) {
+ showmap(mp, map_table != maphash);
+ did_it = true;
+ }
+ } else { // do we have a match?
+ if (round) { // second round: Try unmap "rhs" string
+ n = (int)STRLEN(mp->m_str);
+ p = mp->m_str;
+ } else {
+ n = mp->m_keylen;
+ p = mp->m_keys;
+ }
+ if (STRNCMP(p, lhs, (size_t)(n < len ? n : len)) == 0) {
+ if (maptype == 1) {
+ // Delete entry.
+ // Only accept a full match. For abbreviations
+ // we ignore trailing space when matching with
+ // the "lhs", since an abbreviation can't have
+ // trailing space.
+ if (n != len && (!is_abbrev || round || n > len
+ || *skipwhite((char *)lhs + n) != NUL)) {
+ mpp = &(mp->m_next);
+ continue;
+ }
+ // In keyround for simplified keys, don't unmap
+ // a mapping without m_simplified flag.
+ if (keyround1_simplified && !mp->m_simplified) {
+ break;
+ }
+ // We reset the indicated mode bits. If nothing
+ // is left the entry is deleted below.
+ mp->m_mode &= ~mode;
+ did_it = true; // remember we did something
+ } else if (!has_rhs) { // show matching entry
+ if (!mp->m_simplified) {
+ showmap(mp, map_table != maphash);
+ did_it = true;
+ }
+ } else if (n != len) { // new entry is ambiguous
+ mpp = &(mp->m_next);
+ continue;
+ } else if (keyround1_simplified && !mp->m_simplified) {
+ // In keyround for simplified keys, don't replace
+ // a mapping without m_simplified flag.
+ did_it = true;
+ break;
+ } else if (args->unique) {
+ if (is_abbrev) {
+ semsg(_("E226: abbreviation already exists for %s"), p);
+ } else {
+ semsg(_("E227: mapping already exists for %s"), p);
+ }
+ retval = 5;
+ goto theend;
+ } else {
+ // new rhs for existing entry
+ mp->m_mode &= ~mode; // remove mode bits
+ if (mp->m_mode == 0 && !did_it) { // reuse entry
+ XFREE_CLEAR(mp->m_desc);
+ if (!mp->m_simplified) {
+ NLUA_CLEAR_REF(mp->m_luaref);
+ XFREE_CLEAR(mp->m_str);
+ XFREE_CLEAR(mp->m_orig_str);
+ }
+ mp->m_str = args->rhs;
+ mp->m_orig_str = args->orig_rhs;
+ mp->m_luaref = args->rhs_lua;
+ if (!keyround1_simplified) {
+ args->rhs = NULL;
+ args->orig_rhs = NULL;
+ args->rhs_lua = LUA_NOREF;
+ }
+ mp->m_noremap = noremap;
+ mp->m_nowait = args->nowait;
+ mp->m_silent = args->silent;
+ mp->m_mode = mode;
+ mp->m_simplified = keyround1_simplified;
+ mp->m_expr = args->expr;
+ mp->m_script_ctx = current_sctx;
+ mp->m_script_ctx.sc_lnum += sourcing_lnum;
+ nlua_set_sctx(&mp->m_script_ctx);
+ if (args->desc != NULL) {
+ mp->m_desc = xstrdup(args->desc);
+ }
+ did_it = true;
+ }
+ }
+ if (mp->m_mode == 0) { // entry can be deleted
+ mapblock_free(mpp);
+ continue; // continue with *mpp
+ }
+
+ // May need to put this entry into another hash list.
+ int new_hash = MAP_HASH(mp->m_mode, mp->m_keys[0]);
+ if (!is_abbrev && new_hash != hash) {
+ *mpp = mp->m_next;
+ mp->m_next = map_table[new_hash];
+ map_table[new_hash] = mp;
+
+ continue; // continue with *mpp
+ }
+ }
+ }
+ mpp = &(mp->m_next);
+ }
+ }
+ }
+
+ if (maptype == 1) {
+ // delete entry
+ if (!did_it) {
+ if (!keyround1_simplified) {
+ retval = 2; // no match
+ }
+ } else if (*lhs == Ctrl_C) {
+ // If CTRL-C has been unmapped, reuse it for Interrupting.
+ if (map_table == buf->b_maphash) {
+ buf->b_mapped_ctrl_c &= ~mode;
+ } else {
+ mapped_ctrl_c &= ~mode;
+ }
+ }
+ continue;
+ }
+
+ if (!has_lhs || !has_rhs) {
+ // print entries
+ if (!did_it && !did_local) {
+ if (is_abbrev) {
+ msg(_("No abbreviation found"));
+ } else {
+ msg(_("No mapping found"));
+ }
+ }
+ goto theend; // listing finished
+ }
+
+ if (did_it) {
+ continue; // have added the new entry already
+ }
+
+ // Get here when adding a new entry to the maphash[] list or abbrlist.
+ mp = xmalloc(sizeof(mapblock_T));
+
+ // If CTRL-C has been mapped, don't always use it for Interrupting.
+ if (*lhs == Ctrl_C) {
+ if (map_table == buf->b_maphash) {
+ buf->b_mapped_ctrl_c |= mode;
+ } else {
+ mapped_ctrl_c |= mode;
+ }
+ }
+
+ mp->m_keys = vim_strsave(lhs);
+ mp->m_str = args->rhs;
+ mp->m_orig_str = args->orig_rhs;
+ mp->m_luaref = args->rhs_lua;
+ if (!keyround1_simplified) {
+ args->rhs = NULL;
+ args->orig_rhs = NULL;
+ args->rhs_lua = LUA_NOREF;
+ }
+ mp->m_keylen = (int)STRLEN(mp->m_keys);
+ mp->m_noremap = noremap;
+ mp->m_nowait = args->nowait;
+ mp->m_silent = args->silent;
+ mp->m_mode = mode;
+ mp->m_simplified = keyround1_simplified; // Notice this when porting patch 8.2.0807
+ mp->m_expr = args->expr;
+ mp->m_script_ctx = current_sctx;
+ mp->m_script_ctx.sc_lnum += sourcing_lnum;
+ nlua_set_sctx(&mp->m_script_ctx);
+ mp->m_desc = NULL;
+ if (args->desc != NULL) {
+ mp->m_desc = xstrdup(args->desc);
+ }
+
+ // add the new entry in front of the abbrlist or maphash[] list
+ if (is_abbrev) {
+ mp->m_next = *abbr_table;
+ *abbr_table = mp;
+ } else {
+ n = MAP_HASH(mp->m_mode, mp->m_keys[0]);
+ mp->m_next = map_table[n];
+ map_table[n] = mp;
+ }
+ }
+
+theend:
+ return retval;
+}
+
+/// Set or remove a mapping or an abbreviation in the current buffer, OR
+/// display (matching) mappings/abbreviations.
+///
+/// ```vim
+/// map[!] " show all key mappings
+/// map[!] {lhs} " show key mapping for {lhs}
+/// map[!] {lhs} {rhs} " set key mapping for {lhs} to {rhs}
+/// noremap[!] {lhs} {rhs} " same, but no remapping for {rhs}
+/// unmap[!] {lhs} " remove key mapping for {lhs}
+/// abbr " show all abbreviations
+/// abbr {lhs} " show abbreviations for {lhs}
+/// abbr {lhs} {rhs} " set abbreviation for {lhs} to {rhs}
+/// noreabbr {lhs} {rhs} " same, but no remapping for {rhs}
+/// unabbr {lhs} " remove abbreviation for {lhs}
+///
+/// for :map mode is MODE_NORMAL | MODE_VISUAL | MODE_SELECT | MODE_OP_PENDING
+/// for :map! mode is MODE_INSERT | MODE_CMDLINE
+/// for :cmap mode is MODE_CMDLINE
+/// for :imap mode is MODE_INSERT
+/// for :lmap mode is MODE_LANGMAP
+/// for :nmap mode is MODE_NORMAL
+/// for :vmap mode is MODE_VISUAL | MODE_SELECT
+/// for :xmap mode is MODE_VISUAL
+/// for :smap mode is MODE_SELECT
+/// for :omap mode is MODE_OP_PENDING
+/// for :tmap mode is MODE_TERMINAL
+///
+/// for :abbr mode is MODE_INSERT | MODE_CMDLINE
+/// for :iabbr mode is MODE_INSERT
+/// for :cabbr mode is MODE_CMDLINE
+/// ```
+///
+/// @param maptype 0 for |:map|, 1 for |:unmap|, 2 for |noremap|.
+/// @param arg C-string containing the arguments of the map/abbrev
+/// command, i.e. everything except the initial `:[X][nore]map`.
+/// - Cannot be a read-only string; it will be modified.
+/// @param mode Bitflags representing the mode in which to set the mapping.
+/// See @ref get_map_mode.
+/// @param is_abbrev True if setting an abbreviation, false otherwise.
+///
+/// @return 0 on success. On failure, will return one of the following:
+/// - 1 for invalid arguments
+/// - 2 for no match
+/// - 4 for out of mem (deprecated, WON'T HAPPEN)
+/// - 5 for entry not unique
+///
+int do_map(int maptype, char_u *arg, int mode, bool is_abbrev)
+{
+ MapArguments parsed_args;
+ int result = str_to_mapargs(arg, maptype == 1, &parsed_args);
+ switch (result) {
+ case 0:
+ break;
+ case 1:
+ // invalid arguments
+ goto free_and_return;
+ default:
+ assert(false && "Unknown return code from str_to_mapargs!");
+ result = -1;
+ goto free_and_return;
+ } // switch
+
+ result = buf_do_map(maptype, &parsed_args, mode, is_abbrev, curbuf);
+
+free_and_return:
+ xfree(parsed_args.rhs);
+ xfree(parsed_args.orig_rhs);
+ return result;
+}
+
+/// Get the mapping mode from the command name.
+static int get_map_mode(char **cmdp, bool forceit)
+{
+ char *p;
+ int modec;
+ int mode;
+
+ p = *cmdp;
+ modec = (uint8_t)(*p++);
+ if (modec == 'i') {
+ mode = MODE_INSERT; // :imap
+ } else if (modec == 'l') {
+ mode = MODE_LANGMAP; // :lmap
+ } else if (modec == 'c') {
+ mode = MODE_CMDLINE; // :cmap
+ } else if (modec == 'n' && *p != 'o') { // avoid :noremap
+ mode = MODE_NORMAL; // :nmap
+ } else if (modec == 'v') {
+ mode = MODE_VISUAL | MODE_SELECT; // :vmap
+ } else if (modec == 'x') {
+ mode = MODE_VISUAL; // :xmap
+ } else if (modec == 's') {
+ mode = MODE_SELECT; // :smap
+ } else if (modec == 'o') {
+ mode = MODE_OP_PENDING; // :omap
+ } else if (modec == 't') {
+ mode = MODE_TERMINAL; // :tmap
+ } else {
+ p--;
+ if (forceit) {
+ mode = MODE_INSERT | MODE_CMDLINE; // :map !
+ } else {
+ mode = MODE_VISUAL | MODE_SELECT | MODE_NORMAL | MODE_OP_PENDING; // :map
+ }
+ }
+
+ *cmdp = p;
+ return mode;
+}
+
+/// Clear all mappings (":mapclear") or abbreviations (":abclear").
+/// "abbr" should be false for mappings, true for abbreviations.
+/// This function used to be called map_clear().
+static void do_mapclear(char_u *cmdp, char_u *arg, int forceit, int abbr)
+{
+ int mode;
+ int local;
+
+ local = (STRCMP(arg, "<buffer>") == 0);
+ if (!local && *arg != NUL) {
+ emsg(_(e_invarg));
+ return;
+ }
+
+ mode = get_map_mode((char **)&cmdp, forceit);
+ map_clear_mode(curbuf, mode, local, abbr);
+}
+
+/// Clear all mappings in "mode".
+///
+/// @param buf, buffer for local mappings
+/// @param mode mode in which to delete
+/// @param local true for buffer-local mappings
+/// @param abbr true for abbreviations
+void map_clear_mode(buf_T *buf, int mode, bool local, bool abbr)
+{
+ mapblock_T *mp, **mpp;
+ int hash;
+ int new_hash;
+
+ for (hash = 0; hash < 256; hash++) {
+ if (abbr) {
+ if (hash > 0) { // there is only one abbrlist
+ break;
+ }
+ if (local) {
+ mpp = &buf->b_first_abbr;
+ } else {
+ mpp = &first_abbr;
+ }
+ } else {
+ if (local) {
+ mpp = &buf->b_maphash[hash];
+ } else {
+ mpp = &maphash[hash];
+ }
+ }
+ while (*mpp != NULL) {
+ mp = *mpp;
+ if (mp->m_mode & mode) {
+ mp->m_mode &= ~mode;
+ if (mp->m_mode == 0) { // entry can be deleted
+ mapblock_free(mpp);
+ continue;
+ }
+ // May need to put this entry into another hash list.
+ new_hash = MAP_HASH(mp->m_mode, mp->m_keys[0]);
+ if (!abbr && new_hash != hash) {
+ *mpp = mp->m_next;
+ if (local) {
+ mp->m_next = buf->b_maphash[new_hash];
+ buf->b_maphash[new_hash] = mp;
+ } else {
+ mp->m_next = maphash[new_hash];
+ maphash[new_hash] = mp;
+ }
+ continue; // continue with *mpp
+ }
+ }
+ mpp = &(mp->m_next);
+ }
+ }
+}
+
+/// Check if a map exists that has given string in the rhs
+///
+/// Also checks mappings local to the current buffer.
+///
+/// @param[in] str String which mapping must have in the rhs. Termcap codes
+/// are recognized in this argument.
+/// @param[in] modechars Mode(s) in which mappings are checked.
+/// @param[in] abbr true if checking abbreviations in place of mappings.
+///
+/// @return true if there is at least one mapping with given parameters.
+bool map_to_exists(const char *const str, const char *const modechars, const bool abbr)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
+{
+ int mode = 0;
+ int retval;
+
+ char_u *buf = NULL;
+ const char_u *const rhs = (char_u *)replace_termcodes(str, strlen(str),
+ (char **)&buf, REPTERM_DO_LT,
+ NULL, CPO_TO_CPO_FLAGS);
+
+#define MAPMODE(mode, modechars, chr, modeflags) \
+ do { \
+ if (strchr(modechars, chr) != NULL) { \
+ (mode) |= (modeflags); \
+ } \
+ } while (0)
+ MAPMODE(mode, modechars, 'n', MODE_NORMAL);
+ MAPMODE(mode, modechars, 'v', MODE_VISUAL | MODE_SELECT);
+ MAPMODE(mode, modechars, 'x', MODE_VISUAL);
+ MAPMODE(mode, modechars, 's', MODE_SELECT);
+ MAPMODE(mode, modechars, 'o', MODE_OP_PENDING);
+ MAPMODE(mode, modechars, 'i', MODE_INSERT);
+ MAPMODE(mode, modechars, 'l', MODE_LANGMAP);
+ MAPMODE(mode, modechars, 'c', MODE_CMDLINE);
+#undef MAPMODE
+
+ retval = map_to_exists_mode((char *)rhs, mode, abbr);
+ xfree(buf);
+
+ return retval;
+}
+
+/// Check if a map exists that has given string in the rhs
+///
+/// Also checks mappings local to the current buffer.
+///
+/// @param[in] rhs String which mapping must have in the rhs. Termcap codes
+/// are recognized in this argument.
+/// @param[in] mode Mode(s) in which mappings are checked.
+/// @param[in] abbr true if checking abbreviations in place of mappings.
+///
+/// @return true if there is at least one mapping with given parameters.
+int map_to_exists_mode(const char *const rhs, const int mode, const bool abbr)
+{
+ mapblock_T *mp;
+ int hash;
+ bool exp_buffer = false;
+
+ // Do it twice: once for global maps and once for local maps.
+ for (;;) {
+ for (hash = 0; hash < 256; hash++) {
+ if (abbr) {
+ if (hash > 0) { // There is only one abbr list.
+ break;
+ }
+ if (exp_buffer) {
+ mp = curbuf->b_first_abbr;
+ } else {
+ mp = first_abbr;
+ }
+ } else if (exp_buffer) {
+ mp = curbuf->b_maphash[hash];
+ } else {
+ mp = maphash[hash];
+ }
+ for (; mp; mp = mp->m_next) {
+ if ((mp->m_mode & mode) && strstr((char *)mp->m_str, rhs) != NULL) {
+ return true;
+ }
+ }
+ }
+ if (exp_buffer) {
+ break;
+ }
+ exp_buffer = true;
+ }
+
+ return false;
+}
+
+/// Used below when expanding mapping/abbreviation names.
+static int expand_mapmodes = 0;
+static bool expand_isabbrev = false;
+static bool expand_buffer = false;
+
+/// Translate an internal mapping/abbreviation representation into the
+/// corresponding external one recognized by :map/:abbrev commands.
+///
+/// This function is called when expanding mappings/abbreviations on the
+/// command-line.
+///
+/// It uses a growarray to build the translation string since the latter can be
+/// wider than the original description. The caller has to free the string
+/// afterwards.
+///
+/// @param cpo_flags Value of various flags present in &cpo
+///
+/// @return NULL when there is a problem.
+static char_u *translate_mapping(char_u *str, int cpo_flags)
+{
+ garray_T ga;
+ ga_init(&ga, 1, 40);
+
+ bool cpo_bslash = !(cpo_flags&FLAG_CPO_BSLASH);
+
+ for (; *str; str++) {
+ int c = *str;
+ if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL) {
+ int modifiers = 0;
+ if (str[1] == KS_MODIFIER) {
+ str++;
+ modifiers = *++str;
+ c = *++str;
+ }
+
+ if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL) {
+ c = TO_SPECIAL(str[1], str[2]);
+ if (c == K_ZERO) {
+ // display <Nul> as ^@
+ c = NUL;
+ }
+ str += 2;
+ }
+ if (IS_SPECIAL(c) || modifiers) { // special key
+ ga_concat(&ga, (char *)get_special_key_name(c, modifiers));
+ continue; // for (str)
+ }
+ }
+
+ if (c == ' ' || c == '\t' || c == Ctrl_J || c == Ctrl_V
+ || (c == '\\' && !cpo_bslash)) {
+ ga_append(&ga, cpo_bslash ? Ctrl_V : '\\');
+ }
+
+ if (c) {
+ ga_append(&ga, (char)c);
+ }
+ }
+ ga_append(&ga, NUL);
+ return (char_u *)(ga.ga_data);
+}
+
+/// Work out what to complete when doing command line completion of mapping
+/// or abbreviation names.
+///
+/// @param forceit true if '!' given
+/// @param isabbrev true if abbreviation
+/// @param isunmap true if unmap/unabbrev command
+char_u *set_context_in_map_cmd(expand_T *xp, char_u *cmd, char_u *arg, bool forceit, bool isabbrev,
+ bool isunmap, cmdidx_T cmdidx)
+{
+ if (forceit && cmdidx != CMD_map && cmdidx != CMD_unmap) {
+ xp->xp_context = EXPAND_NOTHING;
+ } else {
+ if (isunmap) {
+ expand_mapmodes = get_map_mode((char **)&cmd, forceit || isabbrev);
+ } else {
+ expand_mapmodes = MODE_INSERT | MODE_CMDLINE;
+ if (!isabbrev) {
+ expand_mapmodes |= MODE_VISUAL | MODE_SELECT | MODE_NORMAL | MODE_OP_PENDING;
+ }
+ }
+ expand_isabbrev = isabbrev;
+ xp->xp_context = EXPAND_MAPPINGS;
+ expand_buffer = false;
+ for (;;) {
+ if (STRNCMP(arg, "<buffer>", 8) == 0) {
+ expand_buffer = true;
+ arg = (char_u *)skipwhite((char *)arg + 8);
+ continue;
+ }
+ if (STRNCMP(arg, "<unique>", 8) == 0) {
+ arg = (char_u *)skipwhite((char *)arg + 8);
+ continue;
+ }
+ if (STRNCMP(arg, "<nowait>", 8) == 0) {
+ arg = (char_u *)skipwhite((char *)arg + 8);
+ continue;
+ }
+ if (STRNCMP(arg, "<silent>", 8) == 0) {
+ arg = (char_u *)skipwhite((char *)arg + 8);
+ continue;
+ }
+ if (STRNCMP(arg, "<special>", 9) == 0) {
+ arg = (char_u *)skipwhite((char *)arg + 9);
+ continue;
+ }
+ if (STRNCMP(arg, "<script>", 8) == 0) {
+ arg = (char_u *)skipwhite((char *)arg + 8);
+ continue;
+ }
+ if (STRNCMP(arg, "<expr>", 6) == 0) {
+ arg = (char_u *)skipwhite((char *)arg + 6);
+ continue;
+ }
+ break;
+ }
+ xp->xp_pattern = (char *)arg;
+ }
+
+ return NULL;
+}
+
+/// Find all mapping/abbreviation names that match regexp "regmatch".
+/// For command line expansion of ":[un]map" and ":[un]abbrev" in all modes.
+/// @return OK if matches found, FAIL otherwise.
+int ExpandMappings(regmatch_T *regmatch, int *num_file, char_u ***file)
+{
+ mapblock_T *mp;
+ int hash;
+ int count;
+ int round;
+ char_u *p;
+ int i;
+
+ *num_file = 0; // return values in case of FAIL
+ *file = NULL;
+
+ // round == 1: Count the matches.
+ // round == 2: Build the array to keep the matches.
+ for (round = 1; round <= 2; round++) {
+ count = 0;
+
+ for (i = 0; i < 7; i++) {
+ if (i == 0) {
+ p = (char_u *)"<silent>";
+ } else if (i == 1) {
+ p = (char_u *)"<unique>";
+ } else if (i == 2) {
+ p = (char_u *)"<script>";
+ } else if (i == 3) {
+ p = (char_u *)"<expr>";
+ } else if (i == 4 && !expand_buffer) {
+ p = (char_u *)"<buffer>";
+ } else if (i == 5) {
+ p = (char_u *)"<nowait>";
+ } else if (i == 6) {
+ p = (char_u *)"<special>";
+ } else {
+ continue;
+ }
+
+ if (vim_regexec(regmatch, (char *)p, (colnr_T)0)) {
+ if (round == 1) {
+ count++;
+ } else {
+ (*file)[count++] = vim_strsave(p);
+ }
+ }
+ }
+
+ for (hash = 0; hash < 256; hash++) {
+ if (expand_isabbrev) {
+ if (hash > 0) { // only one abbrev list
+ break; // for (hash)
+ }
+ mp = first_abbr;
+ } else if (expand_buffer) {
+ mp = curbuf->b_maphash[hash];
+ } else {
+ mp = maphash[hash];
+ }
+ for (; mp; mp = mp->m_next) {
+ if (mp->m_mode & expand_mapmodes) {
+ p = translate_mapping(mp->m_keys, CPO_TO_CPO_FLAGS);
+ if (p != NULL && vim_regexec(regmatch, (char *)p, (colnr_T)0)) {
+ if (round == 1) {
+ count++;
+ } else {
+ (*file)[count++] = p;
+ p = NULL;
+ }
+ }
+ xfree(p);
+ }
+ } // for (mp)
+ } // for (hash)
+
+ if (count == 0) { // no match found
+ break; // for (round)
+ }
+
+ if (round == 1) {
+ *file = (char_u **)xmalloc((size_t)count * sizeof(char_u *));
+ }
+ } // for (round)
+
+ if (count > 1) {
+ char_u **ptr1;
+ char_u **ptr2;
+ char_u **ptr3;
+
+ // Sort the matches
+ sort_strings(*file, count);
+
+ // Remove multiple entries
+ ptr1 = *file;
+ ptr2 = ptr1 + 1;
+ ptr3 = ptr1 + count;
+
+ while (ptr2 < ptr3) {
+ if (STRCMP(*ptr1, *ptr2)) {
+ *++ptr1 = *ptr2++;
+ } else {
+ xfree(*ptr2++);
+ count--;
+ }
+ }
+ }
+
+ *num_file = count;
+ return count == 0 ? FAIL : OK;
+}
+
+// Check for an abbreviation.
+// Cursor is at ptr[col].
+// When inserting, mincol is where insert started.
+// For the command line, mincol is what is to be skipped over.
+// "c" is the character typed before check_abbr was called. It may have
+// ABBR_OFF added to avoid prepending a CTRL-V to it.
+//
+// Historic vi practice: The last character of an abbreviation must be an id
+// character ([a-zA-Z0-9_]). The characters in front of it must be all id
+// characters or all non-id characters. This allows for abbr. "#i" to
+// "#include".
+//
+// Vim addition: Allow for abbreviations that end in a non-keyword character.
+// Then there must be white space before the abbr.
+//
+// Return true if there is an abbreviation, false if not.
+bool check_abbr(int c, char_u *ptr, int col, int mincol)
+{
+ int len;
+ int scol; // starting column of the abbr.
+ int j;
+ char_u *s;
+ char_u tb[MB_MAXBYTES + 4];
+ mapblock_T *mp;
+ mapblock_T *mp2;
+ int clen = 0; // length in characters
+ bool is_id = true;
+
+ if (typebuf.tb_no_abbr_cnt) { // abbrev. are not recursive
+ return false;
+ }
+
+ // no remapping implies no abbreviation, except for CTRL-]
+ if (noremap_keys() && c != Ctrl_RSB) {
+ return false;
+ }
+
+ // Check for word before the cursor: If it ends in a keyword char all
+ // chars before it must be keyword chars or non-keyword chars, but not
+ // white space. If it ends in a non-keyword char we accept any characters
+ // before it except white space.
+ if (col == 0) { // cannot be an abbr.
+ return false;
+ }
+
+ {
+ bool vim_abbr;
+ char_u *p = mb_prevptr(ptr, ptr + col);
+ if (!vim_iswordp(p)) {
+ vim_abbr = true; // Vim added abbr.
+ } else {
+ vim_abbr = false; // vi compatible abbr.
+ if (p > ptr) {
+ is_id = vim_iswordp(mb_prevptr(ptr, p));
+ }
+ }
+ clen = 1;
+ while (p > ptr + mincol) {
+ p = mb_prevptr(ptr, p);
+ if (ascii_isspace(*p) || (!vim_abbr && is_id != vim_iswordp(p))) {
+ p += utfc_ptr2len((char *)p);
+ break;
+ }
+ clen++;
+ }
+ scol = (int)(p - ptr);
+ }
+
+ if (scol < mincol) {
+ scol = mincol;
+ }
+ if (scol < col) { // there is a word in front of the cursor
+ ptr += scol;
+ len = col - scol;
+ mp = curbuf->b_first_abbr;
+ mp2 = first_abbr;
+ if (mp == NULL) {
+ mp = mp2;
+ mp2 = NULL;
+ }
+ for (; mp;
+ mp->m_next == NULL ? (mp = mp2, mp2 = NULL) :
+ (mp = mp->m_next)) {
+ int qlen = mp->m_keylen;
+ char_u *q = mp->m_keys;
+ int match;
+
+ if (strchr((const char *)mp->m_keys, K_SPECIAL) != NULL) {
+ // Might have K_SPECIAL escaped mp->m_keys.
+ q = vim_strsave(mp->m_keys);
+ vim_unescape_ks(q);
+ qlen = (int)STRLEN(q);
+ }
+ // find entries with right mode and keys
+ match = (mp->m_mode & State)
+ && qlen == len
+ && !STRNCMP(q, ptr, (size_t)len);
+ if (q != mp->m_keys) {
+ xfree(q);
+ }
+ if (match) {
+ break;
+ }
+ }
+ if (mp != NULL) {
+ // Found a match:
+ // Insert the rest of the abbreviation in typebuf.tb_buf[].
+ // This goes from end to start.
+ //
+ // Characters 0x000 - 0x100: normal chars, may need CTRL-V,
+ // except K_SPECIAL: Becomes K_SPECIAL KS_SPECIAL KE_FILLER
+ // Characters where IS_SPECIAL() == true: key codes, need
+ // K_SPECIAL. Other characters (with ABBR_OFF): don't use CTRL-V.
+ //
+ // Character CTRL-] is treated specially - it completes the
+ // abbreviation, but is not inserted into the input stream.
+ j = 0;
+ if (c != Ctrl_RSB) {
+ // special key code, split up
+ if (IS_SPECIAL(c) || c == K_SPECIAL) {
+ tb[j++] = K_SPECIAL;
+ tb[j++] = (char_u)K_SECOND(c);
+ tb[j++] = (char_u)K_THIRD(c);
+ } else {
+ if (c < ABBR_OFF && (c < ' ' || c > '~')) {
+ tb[j++] = Ctrl_V; // special char needs CTRL-V
+ }
+ // if ABBR_OFF has been added, remove it here.
+ if (c >= ABBR_OFF) {
+ c -= ABBR_OFF;
+ }
+ int newlen = utf_char2bytes(c, (char *)tb + j);
+ tb[j + newlen] = NUL;
+ // Need to escape K_SPECIAL.
+ char_u *escaped = (char_u *)vim_strsave_escape_ks((char *)tb + j);
+ if (escaped != NULL) {
+ newlen = (int)STRLEN(escaped);
+ memmove(tb + j, escaped, (size_t)newlen);
+ j += newlen;
+ xfree(escaped);
+ }
+ }
+ tb[j] = NUL;
+ // insert the last typed char
+ (void)ins_typebuf((char *)tb, 1, 0, true, mp->m_silent);
+ }
+ if (mp->m_expr) {
+ s = eval_map_expr(mp, c);
+ } else {
+ s = mp->m_str;
+ }
+ if (s != NULL) {
+ // insert the to string
+ (void)ins_typebuf((char *)s, mp->m_noremap, 0, true, mp->m_silent);
+ // no abbrev. for these chars
+ typebuf.tb_no_abbr_cnt += (int)STRLEN(s) + j + 1;
+ if (mp->m_expr) {
+ xfree(s);
+ }
+ }
+
+ tb[0] = Ctrl_H;
+ tb[1] = NUL;
+ len = clen; // Delete characters instead of bytes
+ while (len-- > 0) { // delete the from string
+ (void)ins_typebuf((char *)tb, 1, 0, true, mp->m_silent);
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+/// Evaluate the RHS of a mapping or abbreviations and take care of escaping
+/// special characters.
+///
+/// @param c NUL or typed character for abbreviation
+char_u *eval_map_expr(mapblock_T *mp, int c)
+{
+ char_u *res;
+ char_u *p = NULL;
+ char_u *expr = NULL;
+ pos_T save_cursor;
+ int save_msg_col;
+ int save_msg_row;
+
+ // 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_ks(expr);
+ }
+
+ // Forbid changing text or using ":normal" to avoid most of the bad side
+ // effects. Also restore the cursor position.
+ textlock++;
+ ex_normal_lock++;
+ set_vim_var_char(c); // set v:char to the typed character
+ save_cursor = curwin->w_cursor;
+ save_msg_col = msg_col;
+ save_msg_row = msg_row;
+ if (mp->m_luaref != LUA_NOREF) {
+ Error err = ERROR_INIT;
+ Array args = ARRAY_DICT_INIT;
+ Object ret = nlua_call_ref(mp->m_luaref, NULL, args, true, &err);
+ if (ret.type == kObjectTypeString) {
+ p = (char_u *)xstrndup(ret.data.string.data, ret.data.string.size);
+ }
+ api_free_object(ret);
+ if (err.type != kErrorTypeNone) {
+ semsg_multiline("E5108: %s", err.msg);
+ api_clear_error(&err);
+ }
+ } else {
+ p = (char_u *)eval_to_string((char *)expr, NULL, false);
+ xfree(expr);
+ }
+ textlock--;
+ ex_normal_lock--;
+ curwin->w_cursor = save_cursor;
+ msg_col = save_msg_col;
+ msg_row = save_msg_row;
+
+ if (p == NULL) {
+ return NULL;
+ }
+ // Escape K_SPECIAL in the result to be able to use the string as typeahead.
+ res = (char_u *)vim_strsave_escape_ks((char *)p);
+ xfree(p);
+
+ return res;
+}
+
+/// Write map commands for the current mappings to an .exrc file.
+/// Return FAIL on error, OK otherwise.
+///
+/// @param buf buffer for local mappings or NULL
+int makemap(FILE *fd, buf_T *buf)
+{
+ mapblock_T *mp;
+ char_u c1, c2, c3;
+ char_u *p;
+ char *cmd;
+ int abbr;
+ int hash;
+ bool did_cpo = false;
+
+ // Do the loop twice: Once for mappings, once for abbreviations.
+ // Then loop over all map hash lists.
+ for (abbr = 0; abbr < 2; abbr++) {
+ for (hash = 0; hash < 256; hash++) {
+ if (abbr) {
+ if (hash > 0) { // there is only one abbr list
+ break;
+ }
+ if (buf != NULL) {
+ mp = buf->b_first_abbr;
+ } else {
+ mp = first_abbr;
+ }
+ } else {
+ if (buf != NULL) {
+ mp = buf->b_maphash[hash];
+ } else {
+ mp = maphash[hash];
+ }
+ }
+
+ for (; mp; mp = mp->m_next) {
+ // skip script-local mappings
+ if (mp->m_noremap == REMAP_SCRIPT) {
+ continue;
+ }
+
+ // skip lua mappings and mappings that contain a <SNR> (script-local thing),
+ // they probably don't work when loaded again
+ if (mp->m_luaref != LUA_NOREF) {
+ continue;
+ }
+ for (p = mp->m_str; *p != NUL; p++) {
+ if (p[0] == K_SPECIAL && p[1] == KS_EXTRA
+ && p[2] == KE_SNR) {
+ break;
+ }
+ }
+ if (*p != NUL) {
+ continue;
+ }
+
+ // It's possible to create a mapping and then ":unmap" certain
+ // modes. We recreate this here by mapping the individual
+ // modes, which requires up to three of them.
+ c1 = NUL;
+ c2 = NUL;
+ c3 = NUL;
+ if (abbr) {
+ cmd = "abbr";
+ } else {
+ cmd = "map";
+ }
+ switch (mp->m_mode) {
+ case MODE_NORMAL | MODE_VISUAL | MODE_SELECT | MODE_OP_PENDING:
+ break;
+ case MODE_NORMAL:
+ c1 = 'n';
+ break;
+ case MODE_VISUAL:
+ c1 = 'x';
+ break;
+ case MODE_SELECT:
+ c1 = 's';
+ break;
+ case MODE_OP_PENDING:
+ c1 = 'o';
+ break;
+ case MODE_NORMAL | MODE_VISUAL:
+ c1 = 'n';
+ c2 = 'x';
+ break;
+ case MODE_NORMAL | MODE_SELECT:
+ c1 = 'n';
+ c2 = 's';
+ break;
+ case MODE_NORMAL | MODE_OP_PENDING:
+ c1 = 'n';
+ c2 = 'o';
+ break;
+ case MODE_VISUAL | MODE_SELECT:
+ c1 = 'v';
+ break;
+ case MODE_VISUAL | MODE_OP_PENDING:
+ c1 = 'x';
+ c2 = 'o';
+ break;
+ case MODE_SELECT | MODE_OP_PENDING:
+ c1 = 's';
+ c2 = 'o';
+ break;
+ case MODE_NORMAL | MODE_VISUAL | MODE_SELECT:
+ c1 = 'n';
+ c2 = 'v';
+ break;
+ case MODE_NORMAL | MODE_VISUAL | MODE_OP_PENDING:
+ c1 = 'n';
+ c2 = 'x';
+ c3 = 'o';
+ break;
+ case MODE_NORMAL | MODE_SELECT | MODE_OP_PENDING:
+ c1 = 'n';
+ c2 = 's';
+ c3 = 'o';
+ break;
+ case MODE_VISUAL | MODE_SELECT | MODE_OP_PENDING:
+ c1 = 'v';
+ c2 = 'o';
+ break;
+ case MODE_CMDLINE | MODE_INSERT:
+ if (!abbr) {
+ cmd = "map!";
+ }
+ break;
+ case MODE_CMDLINE:
+ c1 = 'c';
+ break;
+ case MODE_INSERT:
+ c1 = 'i';
+ break;
+ case MODE_LANGMAP:
+ c1 = 'l';
+ break;
+ case MODE_TERMINAL:
+ c1 = 't';
+ break;
+ default:
+ iemsg(_("E228: makemap: Illegal mode"));
+ return FAIL;
+ }
+ do {
+ // do this twice if c2 is set, 3 times with c3 */
+ // When outputting <> form, need to make sure that 'cpo'
+ // is set to the Vim default.
+ if (!did_cpo) {
+ if (*mp->m_str == NUL) { // Will use <Nop>.
+ did_cpo = true;
+ } else {
+ const char specials[] = { (char)(uint8_t)K_SPECIAL, NL, NUL };
+ if (strpbrk((const char *)mp->m_str, specials) != NULL
+ || strpbrk((const char *)mp->m_keys, specials) != NULL) {
+ did_cpo = true;
+ }
+ }
+ if (did_cpo) {
+ if (fprintf(fd, "let s:cpo_save=&cpo") < 0
+ || put_eol(fd) < 0
+ || fprintf(fd, "set cpo&vim") < 0
+ || put_eol(fd) < 0) {
+ return FAIL;
+ }
+ }
+ }
+ if (c1 && putc(c1, fd) < 0) {
+ return FAIL;
+ }
+ if (mp->m_noremap != REMAP_YES && fprintf(fd, "nore") < 0) {
+ return FAIL;
+ }
+ if (fputs(cmd, fd) < 0) {
+ return FAIL;
+ }
+ if (buf != NULL && fputs(" <buffer>", fd) < 0) {
+ return FAIL;
+ }
+ if (mp->m_nowait && fputs(" <nowait>", fd) < 0) {
+ return FAIL;
+ }
+ if (mp->m_silent && fputs(" <silent>", fd) < 0) {
+ return FAIL;
+ }
+ if (mp->m_expr && fputs(" <expr>", fd) < 0) {
+ return FAIL;
+ }
+
+ if (putc(' ', fd) < 0
+ || put_escstr(fd, mp->m_keys, 0) == FAIL
+ || putc(' ', fd) < 0
+ || put_escstr(fd, mp->m_str, 1) == FAIL
+ || put_eol(fd) < 0) {
+ return FAIL;
+ }
+ c1 = c2;
+ c2 = c3;
+ c3 = NUL;
+ } while (c1 != NUL);
+ }
+ }
+ }
+ if (did_cpo) {
+ if (fprintf(fd, "let &cpo=s:cpo_save") < 0
+ || put_eol(fd) < 0
+ || fprintf(fd, "unlet s:cpo_save") < 0
+ || put_eol(fd) < 0) {
+ return FAIL;
+ }
+ }
+ return OK;
+}
+
+// write escape string to file
+// "what": 0 for :map lhs, 1 for :map rhs, 2 for :set
+//
+// return FAIL for failure, OK otherwise
+int put_escstr(FILE *fd, char_u *strstart, int what)
+{
+ char_u *str = strstart;
+ int c;
+
+ // :map xx <Nop>
+ if (*str == NUL && what == 1) {
+ if (fprintf(fd, "<Nop>") < 0) {
+ return FAIL;
+ }
+ return OK;
+ }
+
+ for (; *str != NUL; str++) {
+ // Check for a multi-byte character, which may contain escaped
+ // K_SPECIAL bytes.
+ const char *p = mb_unescape((const char **)&str);
+ if (p != NULL) {
+ while (*p != NUL) {
+ if (fputc(*p++, fd) < 0) {
+ return FAIL;
+ }
+ }
+ str--;
+ continue;
+ }
+
+ c = *str;
+ // Special key codes have to be translated to be able to make sense
+ // when they are read back.
+ if (c == K_SPECIAL && what != 2) {
+ int modifiers = 0;
+ if (str[1] == KS_MODIFIER) {
+ modifiers = str[2];
+ str += 3;
+ c = *str;
+ }
+ if (c == K_SPECIAL) {
+ c = TO_SPECIAL(str[1], str[2]);
+ str += 2;
+ }
+ if (IS_SPECIAL(c) || modifiers) { // special key
+ if (fputs((char *)get_special_key_name(c, modifiers), fd) < 0) {
+ return FAIL;
+ }
+ continue;
+ }
+ }
+
+ // A '\n' in a map command should be written as <NL>.
+ // A '\n' in a set command should be written as \^V^J.
+ if (c == NL) {
+ if (what == 2) {
+ if (fprintf(fd, "\\\026\n") < 0) {
+ return FAIL;
+ }
+ } else {
+ if (fprintf(fd, "<NL>") < 0) {
+ return FAIL;
+ }
+ }
+ continue;
+ }
+
+ // Some characters have to be escaped with CTRL-V to
+ // prevent them from misinterpreted in DoOneCmd().
+ // A space, Tab and '"' has to be escaped with a backslash to
+ // prevent it to be misinterpreted in do_set().
+ // A space has to be escaped with a CTRL-V when it's at the start of a
+ // ":map" rhs.
+ // A '<' has to be escaped with a CTRL-V to prevent it being
+ // interpreted as the start of a special key name.
+ // A space in the lhs of a :map needs a CTRL-V.
+ if (what == 2 && (ascii_iswhite(c) || c == '"' || c == '\\')) {
+ if (putc('\\', fd) < 0) {
+ return FAIL;
+ }
+ } else if (c < ' ' || c > '~' || c == '|'
+ || (what == 0 && c == ' ')
+ || (what == 1 && str == strstart && c == ' ')
+ || (what != 2 && c == '<')) {
+ if (putc(Ctrl_V, fd) < 0) {
+ return FAIL;
+ }
+ }
+ if (putc(c, fd) < 0) {
+ return FAIL;
+ }
+ }
+ return OK;
+}
+
+/// Check the string "keys" against the lhs of all mappings.
+/// Return pointer to rhs of mapping (mapblock->m_str).
+/// NULL when no mapping found.
+///
+/// @param exact require exact match
+/// @param ign_mod ignore preceding modifier
+/// @param abbr do abbreviations
+/// @param mp_ptr return: pointer to mapblock or NULL
+/// @param local_ptr return: buffer-local mapping or NULL
+char_u *check_map(char_u *keys, int mode, int exact, int ign_mod, int abbr, mapblock_T **mp_ptr,
+ int *local_ptr, int *rhs_lua)
+{
+ int len, minlen;
+ mapblock_T *mp;
+ *rhs_lua = LUA_NOREF;
+
+ len = (int)STRLEN(keys);
+ for (int local = 1; local >= 0; local--) {
+ // loop over all hash lists
+ for (int hash = 0; hash < 256; hash++) {
+ if (abbr) {
+ if (hash > 0) { // there is only one list.
+ break;
+ }
+ if (local) {
+ mp = curbuf->b_first_abbr;
+ } else {
+ mp = first_abbr;
+ }
+ } else if (local) {
+ mp = curbuf->b_maphash[hash];
+ } else {
+ mp = maphash[hash];
+ }
+ for (; mp != NULL; mp = mp->m_next) {
+ // skip entries with wrong mode, wrong length and not matching ones
+ if ((mp->m_mode & mode) && (!exact || mp->m_keylen == len)) {
+ char_u *s = mp->m_keys;
+ int keylen = mp->m_keylen;
+ if (ign_mod && keylen >= 3
+ && s[0] == K_SPECIAL && s[1] == KS_MODIFIER) {
+ s += 3;
+ keylen -= 3;
+ }
+ minlen = keylen < len ? keylen : len;
+ if (STRNCMP(s, keys, minlen) == 0) {
+ if (mp_ptr != NULL) {
+ *mp_ptr = mp;
+ }
+ if (local_ptr != NULL) {
+ *local_ptr = local;
+ }
+ *rhs_lua = mp->m_luaref;
+ return mp->m_luaref == LUA_NOREF ? mp->m_str : NULL;
+ }
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/// "hasmapto()" function
+void f_hasmapto(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ const char *mode;
+ const char *const name = tv_get_string(&argvars[0]);
+ bool abbr = false;
+ char buf[NUMBUFLEN];
+ if (argvars[1].v_type == VAR_UNKNOWN) {
+ mode = "nvo";
+ } else {
+ mode = tv_get_string_buf(&argvars[1], buf);
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ abbr = tv_get_number(&argvars[2]);
+ }
+ }
+
+ if (map_to_exists(name, mode, abbr)) {
+ rettv->vval.v_number = true;
+ } else {
+ rettv->vval.v_number = false;
+ }
+}
+
+/// Fill a dictionary with all applicable maparg() like dictionaries
+///
+/// @param dict The dictionary to be filled
+/// @param mp The maphash that contains the mapping information
+/// @param buffer_value The "buffer" value
+/// @param compatible True for compatible with old maparg() dict
+static void mapblock_fill_dict(dict_T *const dict, const mapblock_T *const mp, long buffer_value,
+ bool compatible)
+ FUNC_ATTR_NONNULL_ALL
+{
+ char *const lhs = str2special_save((const char *)mp->m_keys,
+ compatible, !compatible);
+ char *const mapmode = map_mode_to_chars(mp->m_mode);
+ varnumber_T noremap_value;
+
+ if (compatible) {
+ // Keep old compatible behavior
+ // This is unable to determine whether a mapping is a <script> mapping
+ noremap_value = !!mp->m_noremap;
+ } else {
+ // Distinguish between <script> mapping
+ // If it's not a <script> mapping, check if it's a noremap
+ noremap_value = mp->m_noremap == REMAP_SCRIPT ? 2 : !!mp->m_noremap;
+ }
+
+ if (mp->m_luaref != LUA_NOREF) {
+ tv_dict_add_nr(dict, S_LEN("callback"), mp->m_luaref);
+ } else {
+ if (compatible) {
+ tv_dict_add_str(dict, S_LEN("rhs"), (const char *)mp->m_orig_str);
+ } else {
+ tv_dict_add_allocated_str(dict, S_LEN("rhs"),
+ str2special_save((const char *)mp->m_str, false,
+ true));
+ }
+ }
+ if (mp->m_desc != NULL) {
+ tv_dict_add_allocated_str(dict, S_LEN("desc"), xstrdup(mp->m_desc));
+ }
+ tv_dict_add_allocated_str(dict, S_LEN("lhs"), lhs);
+ tv_dict_add_nr(dict, S_LEN("noremap"), noremap_value);
+ tv_dict_add_nr(dict, S_LEN("script"), mp->m_noremap == REMAP_SCRIPT ? 1 : 0);
+ tv_dict_add_nr(dict, S_LEN("expr"), mp->m_expr ? 1 : 0);
+ tv_dict_add_nr(dict, S_LEN("silent"), mp->m_silent ? 1 : 0);
+ tv_dict_add_nr(dict, S_LEN("sid"), (varnumber_T)mp->m_script_ctx.sc_sid);
+ tv_dict_add_nr(dict, S_LEN("lnum"), (varnumber_T)mp->m_script_ctx.sc_lnum);
+ tv_dict_add_nr(dict, S_LEN("buffer"), (varnumber_T)buffer_value);
+ tv_dict_add_nr(dict, S_LEN("nowait"), mp->m_nowait ? 1 : 0);
+ tv_dict_add_allocated_str(dict, S_LEN("mode"), mapmode);
+}
+
+static void get_maparg(typval_T *argvars, typval_T *rettv, int exact)
+{
+ char *keys_buf = NULL;
+ char_u *alt_keys_buf = NULL;
+ bool did_simplify = false;
+ char_u *rhs;
+ LuaRef rhs_lua;
+ int mode;
+ bool abbr = false;
+ bool get_dict = false;
+ mapblock_T *mp;
+ int buffer_local;
+ int flags = REPTERM_FROM_PART | REPTERM_DO_LT;
+
+ // Return empty string for failure.
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = NULL;
+
+ char *keys = (char *)tv_get_string(&argvars[0]);
+ if (*keys == NUL) {
+ return;
+ }
+
+ char buf[NUMBUFLEN];
+ const char *which;
+ if (argvars[1].v_type != VAR_UNKNOWN) {
+ which = tv_get_string_buf_chk(&argvars[1], buf);
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ abbr = (bool)tv_get_number(&argvars[2]);
+ if (argvars[3].v_type != VAR_UNKNOWN) {
+ get_dict = (bool)tv_get_number(&argvars[3]);
+ }
+ }
+ } else {
+ which = "";
+ }
+ if (which == NULL) {
+ return;
+ }
+
+ mode = get_map_mode((char **)&which, 0);
+
+ char_u *keys_simplified
+ = (char_u *)replace_termcodes(keys,
+ STRLEN(keys), &keys_buf, flags, &did_simplify,
+ CPO_TO_CPO_FLAGS);
+ rhs = check_map(keys_simplified, mode, exact, false, abbr, &mp, &buffer_local, &rhs_lua);
+ if (did_simplify) {
+ // When the lhs is being simplified the not-simplified keys are
+ // preferred for printing, like in do_map().
+ (void)replace_termcodes(keys,
+ STRLEN(keys),
+ (char **)&alt_keys_buf, flags | REPTERM_NO_SIMPLIFY, NULL,
+ CPO_TO_CPO_FLAGS);
+ rhs = check_map(alt_keys_buf, mode, exact, false, abbr, &mp, &buffer_local, &rhs_lua);
+ }
+
+ if (!get_dict) {
+ // Return a string.
+ if (rhs != NULL) {
+ if (*rhs == NUL) {
+ rettv->vval.v_string = xstrdup("<Nop>");
+ } else {
+ rettv->vval.v_string = str2special_save((char *)rhs, false, false);
+ }
+ } else if (rhs_lua != LUA_NOREF) {
+ size_t msglen = 100;
+ char *msg = (char *)xmalloc(msglen);
+ snprintf(msg, msglen, "<Lua function %d>", mp->m_luaref);
+ rettv->vval.v_string = msg;
+ }
+ } else {
+ tv_dict_alloc_ret(rettv);
+ if (mp != NULL && (rhs != NULL || rhs_lua != LUA_NOREF)) {
+ // Return a dictionary.
+ mapblock_fill_dict(rettv->vval.v_dict, mp, buffer_local, true);
+ }
+ }
+
+ xfree(keys_buf);
+ xfree(alt_keys_buf);
+}
+
+/// "maparg()" function
+void f_maparg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ get_maparg(argvars, rettv, true);
+}
+
+/// "mapcheck()" function
+void f_mapcheck(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ get_maparg(argvars, rettv, false);
+}
+
+/// Add a mapping. Unlike @ref do_map this copies the string arguments, so
+/// static or read-only strings can be used.
+///
+/// @param lhs C-string containing the lhs of the mapping
+/// @param rhs C-string containing the rhs of the mapping
+/// @param mode Bitflags representing the mode in which to set the mapping.
+/// See @ref get_map_mode.
+/// @param buffer If true, make a buffer-local mapping for curbuf
+void add_map(char *lhs, char *rhs, int mode, bool buffer)
+{
+ MapArguments args = { 0 };
+ set_maparg_lhs_rhs(lhs, strlen(lhs), rhs, strlen(rhs), LUA_NOREF, 0, &args);
+ args.buffer = buffer;
+
+ buf_do_map(2, &args, mode, false, curbuf);
+ xfree(args.rhs);
+ xfree(args.orig_rhs);
+}
+
+/// Any character has an equivalent 'langmap' character. This is used for
+/// keyboards that have a special language mode that sends characters above
+/// 128 (although other characters can be translated too). The "to" field is a
+/// Vim command character. This avoids having to switch the keyboard back to
+/// ASCII mode when leaving Insert mode.
+///
+/// langmap_mapchar[] maps any of 256 chars to an ASCII char used for Vim
+/// commands.
+/// langmap_mapga.ga_data is a sorted table of langmap_entry_T.
+/// This does the same as langmap_mapchar[] for characters >= 256.
+///
+/// With multi-byte support use growarray for 'langmap' chars >= 256
+typedef struct {
+ int from;
+ int to;
+} langmap_entry_T;
+
+static garray_T langmap_mapga = GA_EMPTY_INIT_VALUE;
+
+/// Search for an entry in "langmap_mapga" for "from". If found set the "to"
+/// field. If not found insert a new entry at the appropriate location.
+static void langmap_set_entry(int from, int to)
+{
+ langmap_entry_T *entries = (langmap_entry_T *)(langmap_mapga.ga_data);
+ unsigned int a = 0;
+ assert(langmap_mapga.ga_len >= 0);
+ unsigned int b = (unsigned int)langmap_mapga.ga_len;
+
+ // Do a binary search for an existing entry.
+ while (a != b) {
+ unsigned int i = (a + b) / 2;
+ int d = entries[i].from - from;
+
+ if (d == 0) {
+ entries[i].to = to;
+ return;
+ }
+ if (d < 0) {
+ a = i + 1;
+ } else {
+ b = i;
+ }
+ }
+
+ ga_grow(&langmap_mapga, 1);
+
+ // insert new entry at position "a"
+ entries = (langmap_entry_T *)(langmap_mapga.ga_data) + a;
+ memmove(entries + 1, entries,
+ ((unsigned int)langmap_mapga.ga_len - a) * sizeof(langmap_entry_T));
+ langmap_mapga.ga_len++;
+ entries[0].from = from;
+ entries[0].to = to;
+}
+
+/// Apply 'langmap' to multi-byte character "c" and return the result.
+int langmap_adjust_mb(int c)
+{
+ langmap_entry_T *entries = (langmap_entry_T *)(langmap_mapga.ga_data);
+ int a = 0;
+ int b = langmap_mapga.ga_len;
+
+ while (a != b) {
+ int i = (a + b) / 2;
+ int d = entries[i].from - c;
+
+ if (d == 0) {
+ return entries[i].to; // found matching entry
+ }
+ if (d < 0) {
+ a = i + 1;
+ } else {
+ b = i;
+ }
+ }
+ return c; // no entry found, return "c" unmodified
+}
+
+void langmap_init(void)
+{
+ for (int i = 0; i < 256; i++) {
+ langmap_mapchar[i] = (char_u)i; // we init with a one-to-one map
+ }
+ ga_init(&langmap_mapga, sizeof(langmap_entry_T), 8);
+}
+
+/// Called when langmap option is set; the language map can be
+/// changed at any time!
+void langmap_set(void)
+{
+ char_u *p;
+ char_u *p2;
+ int from, to;
+
+ ga_clear(&langmap_mapga); // clear the previous map first
+ langmap_init(); // back to one-to-one map
+
+ for (p = p_langmap; p[0] != NUL;) {
+ for (p2 = p; p2[0] != NUL && p2[0] != ',' && p2[0] != ';';
+ MB_PTR_ADV(p2)) {
+ if (p2[0] == '\\' && p2[1] != NUL) {
+ p2++;
+ }
+ }
+ if (p2[0] == ';') {
+ p2++; // abcd;ABCD form, p2 points to A
+ } else {
+ p2 = NULL; // aAbBcCdD form, p2 is NULL
+ }
+ while (p[0]) {
+ if (p[0] == ',') {
+ p++;
+ break;
+ }
+ if (p[0] == '\\' && p[1] != NUL) {
+ p++;
+ }
+ from = utf_ptr2char((char *)p);
+ to = NUL;
+ if (p2 == NULL) {
+ MB_PTR_ADV(p);
+ if (p[0] != ',') {
+ if (p[0] == '\\') {
+ p++;
+ }
+ to = utf_ptr2char((char *)p);
+ }
+ } else {
+ if (p2[0] != ',') {
+ if (p2[0] == '\\') {
+ p2++;
+ }
+ to = utf_ptr2char((char *)p2);
+ }
+ }
+ if (to == NUL) {
+ semsg(_("E357: 'langmap': Matching character missing for %s"),
+ transchar(from));
+ return;
+ }
+
+ if (from >= 256) {
+ langmap_set_entry(from, to);
+ } else {
+ assert(to <= UCHAR_MAX);
+ langmap_mapchar[from & 255] = (char_u)to;
+ }
+
+ // Advance to next pair
+ MB_PTR_ADV(p);
+ if (p2 != NULL) {
+ MB_PTR_ADV(p2);
+ if (*p == ';') {
+ p = p2;
+ if (p[0] != NUL) {
+ if (p[0] != ',') {
+ semsg(_("E358: 'langmap': Extra characters after semicolon: %s"),
+ p);
+ return;
+ }
+ p++;
+ }
+ break;
+ }
+ }
+ }
+ }
+}
+
+static void do_exmap(exarg_T *eap, int isabbrev)
+{
+ int mode;
+ char *cmdp = eap->cmd;
+ mode = get_map_mode(&cmdp, eap->forceit || isabbrev);
+
+ switch (do_map((*cmdp == 'n') ? 2 : (*cmdp == 'u'),
+ (char_u *)eap->arg, mode, isabbrev)) {
+ case 1:
+ emsg(_(e_invarg));
+ break;
+ case 2:
+ emsg(isabbrev ? _(e_noabbr) : _(e_nomap));
+ break;
+ }
+}
+
+/// ":abbreviate" and friends.
+void ex_abbreviate(exarg_T *eap)
+{
+ do_exmap(eap, true); // almost the same as mapping
+}
+
+/// ":map" and friends.
+void ex_map(exarg_T *eap)
+{
+ // If we are sourcing .exrc or .vimrc in current directory we
+ // print the mappings for security reasons.
+ if (secure) {
+ secure = 2;
+ msg_outtrans(eap->cmd);
+ msg_putchar('\n');
+ }
+ do_exmap(eap, false);
+}
+
+/// ":unmap" and friends.
+void ex_unmap(exarg_T *eap)
+{
+ do_exmap(eap, false);
+}
+
+/// ":mapclear" and friends.
+void ex_mapclear(exarg_T *eap)
+{
+ do_mapclear((char_u *)eap->cmd, (char_u *)eap->arg, eap->forceit, false);
+}
+
+/// ":abclear" and friends.
+void ex_abclear(exarg_T *eap)
+{
+ do_mapclear((char_u *)eap->cmd, (char_u *)eap->arg, true, true);
+}
+
+/// Set, tweak, or remove a mapping in a mode. Acts as the implementation for
+/// functions like @ref nvim_buf_set_keymap.
+///
+/// Arguments are handled like @ref nvim_set_keymap unless noted.
+/// @param buffer Buffer handle for a specific buffer, or 0 for the current
+/// buffer, or -1 to signify global behavior ("all buffers")
+/// @param is_unmap When true, removes the mapping that matches {lhs}.
+void modify_keymap(uint64_t channel_id, Buffer buffer, bool is_unmap, String mode, String lhs,
+ String rhs, Dict(keymap) *opts, Error *err)
+{
+ LuaRef lua_funcref = LUA_NOREF;
+ bool global = (buffer == -1);
+ if (global) {
+ buffer = 0;
+ }
+ buf_T *target_buf = find_buffer_by_handle(buffer, err);
+
+ if (!target_buf) {
+ return;
+ }
+
+ const sctx_T save_current_sctx = api_set_sctx(channel_id);
+
+ if (opts != NULL && opts->callback.type == kObjectTypeLuaRef) {
+ lua_funcref = opts->callback.data.luaref;
+ opts->callback.data.luaref = LUA_NOREF;
+ }
+ MapArguments parsed_args = MAP_ARGUMENTS_INIT;
+ if (opts) {
+#define KEY_TO_BOOL(name) \
+ parsed_args.name = api_object_to_bool(opts->name, #name, false, err); \
+ if (ERROR_SET(err)) { \
+ goto fail_and_free; \
+ }
+
+ KEY_TO_BOOL(nowait);
+ KEY_TO_BOOL(noremap);
+ KEY_TO_BOOL(silent);
+ KEY_TO_BOOL(script);
+ KEY_TO_BOOL(expr);
+ KEY_TO_BOOL(unique);
+#undef KEY_TO_BOOL
+ }
+ parsed_args.buffer = !global;
+
+ if (!set_maparg_lhs_rhs(lhs.data, lhs.size,
+ rhs.data, rhs.size, lua_funcref,
+ CPO_TO_CPO_FLAGS, &parsed_args)) {
+ api_set_error(err, kErrorTypeValidation, "LHS exceeds maximum map length: %s", lhs.data);
+ goto fail_and_free;
+ }
+
+ if (opts != NULL && opts->desc.type == kObjectTypeString) {
+ parsed_args.desc = string_to_cstr(opts->desc.data.string);
+ } else {
+ parsed_args.desc = NULL;
+ }
+ if (parsed_args.lhs_len > MAXMAPLEN || parsed_args.alt_lhs_len > MAXMAPLEN) {
+ api_set_error(err, kErrorTypeValidation, "LHS exceeds maximum map length: %s", lhs.data);
+ goto fail_and_free;
+ }
+
+ if (mode.size > 1) {
+ api_set_error(err, kErrorTypeValidation, "Shortname is too long: %s", mode.data);
+ goto fail_and_free;
+ }
+ int mode_val; // integer value of the mapping mode, to be passed to do_map()
+ char *p = (mode.size) ? mode.data : "m";
+ if (STRNCMP(p, "!", 2) == 0) {
+ mode_val = get_map_mode(&p, true); // mapmode-ic
+ } else {
+ mode_val = get_map_mode(&p, false);
+ if (mode_val == (MODE_VISUAL | MODE_SELECT | MODE_NORMAL | MODE_OP_PENDING) && mode.size > 0) {
+ // get_map_mode() treats unrecognized mode shortnames as ":map".
+ // This is an error unless the given shortname was empty string "".
+ api_set_error(err, kErrorTypeValidation, "Invalid mode shortname: \"%s\"", p);
+ goto fail_and_free;
+ }
+ }
+
+ if (parsed_args.lhs_len == 0) {
+ api_set_error(err, kErrorTypeValidation, "Invalid (empty) LHS");
+ goto fail_and_free;
+ }
+
+ bool is_noremap = parsed_args.noremap;
+ assert(!(is_unmap && is_noremap));
+
+ if (!is_unmap && lua_funcref == LUA_NOREF
+ && (parsed_args.rhs_len == 0 && !parsed_args.rhs_is_noop)) {
+ if (rhs.size == 0) { // assume that the user wants RHS to be a <Nop>
+ parsed_args.rhs_is_noop = true;
+ } else {
+ abort(); // should never happen
+ }
+ } else if (is_unmap && (parsed_args.rhs_len || parsed_args.rhs_lua != LUA_NOREF)) {
+ if (parsed_args.rhs_len) {
+ api_set_error(err, kErrorTypeValidation,
+ "Gave nonempty RHS in unmap command: %s", parsed_args.rhs);
+ } else {
+ api_set_error(err, kErrorTypeValidation, "Gave nonempty RHS for unmap");
+ }
+ goto fail_and_free;
+ }
+
+ // buf_do_map() reads noremap/unmap as its own argument.
+ int maptype_val = 0;
+ if (is_unmap) {
+ maptype_val = 1;
+ } else if (is_noremap) {
+ maptype_val = 2;
+ }
+
+ switch (buf_do_map(maptype_val, &parsed_args, mode_val, 0, target_buf)) {
+ case 0:
+ break;
+ case 1:
+ api_set_error(err, kErrorTypeException, (char *)e_invarg, 0);
+ goto fail_and_free;
+ case 2:
+ api_set_error(err, kErrorTypeException, (char *)e_nomap, 0);
+ goto fail_and_free;
+ case 5:
+ api_set_error(err, kErrorTypeException,
+ "E227: mapping already exists for %s", parsed_args.lhs);
+ goto fail_and_free;
+ default:
+ assert(false && "Unrecognized return code!");
+ goto fail_and_free;
+ } // switch
+
+fail_and_free:
+ current_sctx = save_current_sctx;
+ NLUA_CLEAR_REF(parsed_args.rhs_lua);
+ xfree(parsed_args.rhs);
+ xfree(parsed_args.orig_rhs);
+ xfree(parsed_args.desc);
+}
+
+/// Get an array containing dictionaries describing mappings
+/// based on mode and buffer id
+///
+/// @param mode The abbreviation for the mode
+/// @param buf The buffer to get the mapping array. NULL for global
+/// @param from_lua Whether it is called from internal lua api.
+/// @returns Array of maparg()-like dictionaries describing mappings
+ArrayOf(Dictionary) keymap_array(String mode, buf_T *buf, bool from_lua)
+{
+ Array mappings = ARRAY_DICT_INIT;
+ dict_T *const dict = tv_dict_alloc();
+
+ // Convert the string mode to the integer mode
+ // that is stored within each mapblock
+ char *p = mode.data;
+ int int_mode = get_map_mode(&p, 0);
+
+ // Determine the desired buffer value
+ long buffer_value = (buf == NULL) ? 0 : buf->handle;
+
+ for (int i = 0; i < MAX_MAPHASH; i++) {
+ for (const mapblock_T *current_maphash = get_maphash(i, buf);
+ current_maphash;
+ current_maphash = current_maphash->m_next) {
+ if (current_maphash->m_simplified) {
+ continue;
+ }
+ // Check for correct mode
+ if (int_mode & current_maphash->m_mode) {
+ mapblock_fill_dict(dict, current_maphash, buffer_value, false);
+ Object api_dict = vim_to_object((typval_T[]) { { .v_type = VAR_DICT,
+ .vval.v_dict = dict } });
+ if (from_lua) {
+ Dictionary d = api_dict.data.dictionary;
+ for (size_t j = 0; j < d.size; j++) {
+ if (strequal("callback", d.items[j].key.data)) {
+ d.items[j].value.type = kObjectTypeLuaRef;
+ d.items[j].value.data.luaref = api_new_luaref((LuaRef)d.items[j].value.data.integer);
+ break;
+ }
+ }
+ }
+ ADD(mappings, api_dict);
+ tv_dict_clear(dict);
+ }
+ }
+ }
+ tv_dict_free(dict);
+
+ return mappings;
+}
diff --git a/src/nvim/mapping.h b/src/nvim/mapping.h
new file mode 100644
index 0000000000..4b0622ffa1
--- /dev/null
+++ b/src/nvim/mapping.h
@@ -0,0 +1,54 @@
+#ifndef NVIM_MAPPING_H
+#define NVIM_MAPPING_H
+
+#include "nvim/buffer_defs.h"
+#include "nvim/eval/funcs.h"
+#include "nvim/ex_cmds_defs.h"
+#include "nvim/types.h"
+#include "nvim/vim.h"
+
+/// All possible |:map-arguments| usable in a |:map| command.
+///
+/// The <special> argument has no effect on mappings and is excluded from this
+/// struct declaration. |noremap| is included, since it behaves like a map
+/// argument when used in a mapping.
+///
+/// @see mapblock_T
+struct map_arguments {
+ bool buffer;
+ bool expr;
+ bool noremap;
+ bool nowait;
+ bool script;
+ bool silent;
+ bool unique;
+
+ /// The {lhs} of the mapping.
+ ///
+ /// vim limits this to MAXMAPLEN characters, allowing us to use a static
+ /// buffer. Setting lhs_len to a value larger than MAXMAPLEN can signal
+ /// that {lhs} was too long and truncated.
+ char_u lhs[MAXMAPLEN + 1];
+ size_t lhs_len;
+
+ /// Unsimplifed {lhs} of the mapping. If no simplification has been done then alt_lhs_len is 0.
+ char_u alt_lhs[MAXMAPLEN + 1];
+ size_t alt_lhs_len;
+
+ char_u *rhs; /// The {rhs} of the mapping.
+ size_t rhs_len;
+ LuaRef rhs_lua; /// lua function as {rhs}
+ bool rhs_is_noop; /// True when the {rhs} should be <Nop>.
+
+ char_u *orig_rhs; /// The original text of the {rhs}.
+ size_t orig_rhs_len;
+ char *desc; /// map description
+};
+typedef struct map_arguments MapArguments;
+#define MAP_ARGUMENTS_INIT { false, false, false, false, false, false, false, \
+ { 0 }, 0, { 0 }, 0, NULL, 0, LUA_NOREF, false, NULL, 0, NULL }
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "mapping.h.generated.h"
+#endif
+#endif // NVIM_MAPPING_H
diff --git a/src/nvim/mark.c b/src/nvim/mark.c
index 39f18b333d..66855c66b5 100644
--- a/src/nvim/mark.c
+++ b/src/nvim/mark.c
@@ -13,7 +13,9 @@
#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
+#include "nvim/cursor.h"
#include "nvim/diff.h"
+#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/ex_cmds.h"
#include "nvim/extmark.h"
@@ -24,6 +26,7 @@
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
+#include "nvim/move.h"
#include "nvim/normal.h"
#include "nvim/option.h"
#include "nvim/os/input.h"
@@ -61,7 +64,8 @@ static xfmark_T namedfm[NGLOBALMARKS];
*/
int setmark(int c)
{
- return setmark_pos(c, &curwin->w_cursor, curbuf->b_fnum);
+ fmarkv_T view = mark_view_make(curwin->w_topline, curwin->w_cursor);
+ return setmark_pos(c, &curwin->w_cursor, curbuf->b_fnum, &view);
}
/// Free fmark_T item
@@ -90,9 +94,10 @@ void clear_fmark(fmark_T *fm)
* When "c" is upper case use file "fnum".
* Returns OK on success, FAIL if bad name given.
*/
-int setmark_pos(int c, pos_T *pos, int fnum)
+int setmark_pos(int c, pos_T *pos, int fnum, fmarkv_T *view_pt)
{
int i;
+ fmarkv_T view = view_pt != NULL ? *view_pt : (fmarkv_T)INIT_FMARKV;
// Check for a special key (may cause islower() to crash).
if (c < 0) {
@@ -117,7 +122,7 @@ int setmark_pos(int c, pos_T *pos, int fnum)
}
if (c == '"') {
- RESET_FMARK(&buf->b_last_cursor, *pos, buf->b_fnum);
+ RESET_FMARK(&buf->b_last_cursor, *pos, buf->b_fnum, view);
return OK;
}
@@ -147,7 +152,7 @@ int setmark_pos(int c, pos_T *pos, int fnum)
if (ASCII_ISLOWER(c)) {
i = c - 'a';
- RESET_FMARK(buf->b_namedm + i, *pos, fnum);
+ RESET_FMARK(buf->b_namedm + i, *pos, fnum, view);
return OK;
}
if (ASCII_ISUPPER(c) || ascii_isdigit(c)) {
@@ -156,7 +161,7 @@ int setmark_pos(int c, pos_T *pos, int fnum)
} else {
i = c - 'A';
}
- RESET_XFMARK(namedfm + i, *pos, fnum, NULL);
+ RESET_XFMARK(namedfm + i, *pos, fnum, view, NULL);
return OK;
}
return FAIL;
@@ -171,7 +176,7 @@ void setpcmark(void)
xfmark_T *fm;
// for :global the mark is set only once
- if (global_busy || listcmd_busy || cmdmod.keepjumps) {
+ if (global_busy || listcmd_busy || (cmdmod.cmod_flags & CMOD_KEEPJUMPS)) {
return;
}
@@ -202,7 +207,8 @@ void setpcmark(void)
curwin->w_jumplistidx = curwin->w_jumplistlen;
fm = &curwin->w_jumplist[curwin->w_jumplistlen - 1];
- SET_XFMARK(fm, curwin->w_pcmark, curbuf->b_fnum, NULL);
+ fmarkv_T view = mark_view_make(curwin->w_topline, curwin->w_pcmark);
+ SET_XFMARK(fm, curwin->w_pcmark, curbuf->b_fnum, view, NULL);
}
/*
@@ -217,249 +223,411 @@ void checkpcmark(void)
&& (equalpos(curwin->w_pcmark, curwin->w_cursor)
|| curwin->w_pcmark.lnum == 0)) {
curwin->w_pcmark = curwin->w_prev_pcmark;
- curwin->w_prev_pcmark.lnum = 0; // Show it has been checked
}
+ curwin->w_prev_pcmark.lnum = 0; // it has been checked
}
-/*
- * move "count" positions in the jump list (count may be negative)
- */
-pos_T *movemark(int count)
+/// Get mark in "count" position in the |jumplist| relative to the current index.
+///
+/// If the mark is in a different buffer, it will be skipped unless the buffer exists.
+///
+/// @note cleanup_jumplist() is run, which removes duplicate marks, and
+/// changes win->w_jumplistidx.
+/// @param[in] win window to get jumplist from.
+/// @param[in] count count to move may be negative.
+///
+/// @return mark, NULL if out of jumplist bounds.
+fmark_T *get_jumplist(win_T *win, int count)
{
- pos_T *pos;
- xfmark_T *jmp;
+ xfmark_T *jmp = NULL;
- cleanup_jumplist(curwin, true);
+ cleanup_jumplist(win, true);
- if (curwin->w_jumplistlen == 0) { // nothing to jump to
- return (pos_T *)NULL;
+ if (win->w_jumplistlen == 0) { // nothing to jump to
+ return NULL;
}
for (;;) {
- if (curwin->w_jumplistidx + count < 0
- || curwin->w_jumplistidx + count >= curwin->w_jumplistlen) {
- return (pos_T *)NULL;
+ if (win->w_jumplistidx + count < 0
+ || win->w_jumplistidx + count >= win->w_jumplistlen) {
+ return NULL;
}
- /*
- * if first CTRL-O or CTRL-I command after a jump, add cursor position
- * to list. Careful: If there are duplicates (CTRL-O immediately after
- * starting Vim on a file), another entry may have been removed.
- */
- if (curwin->w_jumplistidx == curwin->w_jumplistlen) {
+ // if first CTRL-O or CTRL-I command after a jump, add cursor position
+ // to list. Careful: If there are duplicates (CTRL-O immediately after
+ // starting Vim on a file), another entry may have been removed.
+ if (win->w_jumplistidx == win->w_jumplistlen) {
setpcmark();
- --curwin->w_jumplistidx; // skip the new entry
- if (curwin->w_jumplistidx + count < 0) {
- return (pos_T *)NULL;
+ win->w_jumplistidx--; // skip the new entry
+ if (win->w_jumplistidx + count < 0) {
+ return NULL;
}
}
- curwin->w_jumplistidx += count;
+ win->w_jumplistidx += count;
- jmp = curwin->w_jumplist + curwin->w_jumplistidx;
+ jmp = win->w_jumplist + win->w_jumplistidx;
if (jmp->fmark.fnum == 0) {
+ // Resolve the fnum (buff number) in the mark before returning it (shada)
fname2fnum(jmp);
}
if (jmp->fmark.fnum != curbuf->b_fnum) {
- // jump to other file
- if (buflist_findnr(jmp->fmark.fnum) == NULL) { // Skip this one ..
+ // Needs to switch buffer, if it can't find it skip the mark
+ if (buflist_findnr(jmp->fmark.fnum) == NULL) {
count += count < 0 ? -1 : 1;
continue;
}
- if (buflist_getfile(jmp->fmark.fnum, jmp->fmark.mark.lnum,
- 0, FALSE) == FAIL) {
- return (pos_T *)NULL;
- }
- // Set lnum again, autocommands my have changed it
- curwin->w_cursor = jmp->fmark.mark;
- pos = (pos_T *)-1;
- } else {
- pos = &(jmp->fmark.mark);
}
- return pos;
+ break;
}
+ return &jmp->fmark;
}
-/*
- * Move "count" positions in the changelist (count may be negative).
- */
-pos_T *movechangelist(int count)
+/// Get mark in "count" position in the |changelist| relative to the current index.
+///
+/// @note Changes the win->w_changelistidx.
+/// @param[in] win window to get jumplist from.
+/// @param[in] count count to move may be negative.
+///
+/// @return mark, NULL if out of bounds.
+fmark_T *get_changelist(buf_T *buf, win_T *win, int count)
{
int n;
+ fmark_T *fm;
- if (curbuf->b_changelistlen == 0) { // nothing to jump to
- return (pos_T *)NULL;
+ if (buf->b_changelistlen == 0) { // nothing to jump to
+ return NULL;
}
- n = curwin->w_changelistidx;
+ n = win->w_changelistidx;
if (n + count < 0) {
if (n == 0) {
- return (pos_T *)NULL;
+ return NULL;
}
n = 0;
- } else if (n + count >= curbuf->b_changelistlen) {
- if (n == curbuf->b_changelistlen - 1) {
- return (pos_T *)NULL;
+ } else if (n + count >= buf->b_changelistlen) {
+ if (n == buf->b_changelistlen - 1) {
+ return NULL;
}
- n = curbuf->b_changelistlen - 1;
+ n = buf->b_changelistlen - 1;
} else {
n += count;
}
- curwin->w_changelistidx = n;
- return &(curbuf->b_changelist[n].mark);
+ win->w_changelistidx = n;
+ fm = &(buf->b_changelist[n]);
+ // Changelist marks are always buffer local, Shada does not set it when loading
+ fm->fnum = curbuf->handle;
+ return &(buf->b_changelist[n]);
}
-/*
- * Find mark "c" in buffer pointed to by "buf".
- * If "changefile" is TRUE it's allowed to edit another file for '0, 'A, etc.
- * If "fnum" is not NULL store the fnum there for '0, 'A etc., don't edit
- * another file.
- * Returns:
- * - pointer to pos_T if found. lnum is 0 when mark not set, -1 when mark is
- * in another file which can't be gotten. (caller needs to check lnum!)
- * - NULL if there is no mark called 'c'.
- * - -1 if mark is in other file and jumped there (only if changefile is TRUE)
- */
-pos_T *getmark_buf(buf_T *buf, int c, bool changefile)
+/// Get a named mark.
+///
+/// All types of marks, even those that are not technically a mark will be returned as such. Use
+/// mark_move_to() to move to the mark.
+/// @note Some of the pointers are statically allocated, if in doubt make a copy. For more
+/// information read mark_get_local().
+/// @param buf Buffer to get the mark from.
+/// @param win Window to get or calculate the mark from (motion type marks, context mark).
+/// @param fmp[out] Optional pointer to store the result in, as a workaround for the note above.
+/// @param flag MarkGet value
+/// @param name Name of the mark.
+///
+/// @return Mark if found, otherwise NULL. For @c kMarkBufLocal, NULL is returned
+/// when no mark is found in @a buf.
+fmark_T *mark_get(buf_T *buf, win_T *win, fmark_T *fmp, MarkGet flag, int name)
{
- return getmark_buf_fnum(buf, c, changefile, NULL);
+ fmark_T *fm = NULL;
+ if (ASCII_ISUPPER(name) || ascii_isdigit(name)) {
+ // Global marks
+ xfmark_T *xfm = mark_get_global(!(flag & kMarkAllNoResolve), name);
+ fm = &xfm->fmark;
+ // Only wanted marks belonging to the buffer
+ if ((flag & kMarkBufLocal) && xfm->fmark.fnum != buf->handle) {
+ return NULL;
+ }
+ } else if (name > 0 && name < NMARK_LOCAL_MAX) {
+ // Local Marks
+ fm = mark_get_local(buf, win, name);
+ }
+ if (fmp != NULL && fm != NULL) {
+ *fmp = *fm;
+ return fmp;
+ }
+ return fm;
}
-pos_T *getmark(int c, bool changefile)
+/// Get a global mark {A-Z0-9}.
+///
+/// @param name the name of the mark.
+/// @param resolve Whether to try resolving the mark fnum (i.e., load the buffer stored in
+/// the mark fname and update the xfmark_T (expensive)).
+///
+/// @return Mark
+xfmark_T *mark_get_global(bool resolve, int name)
{
- return getmark_buf_fnum(curbuf, c, changefile, NULL);
+ xfmark_T *mark;
+
+ if (ascii_isdigit(name)) {
+ name = name - '0' + NMARKS;
+ } else if (ASCII_ISUPPER(name)) {
+ name -= 'A';
+ } else {
+ // Not a valid mark name
+ assert(false);
+ }
+ mark = &namedfm[name];
+
+ if (resolve && mark->fmark.fnum == 0) {
+ // Resolve filename to fnum (SHADA marks)
+ fname2fnum(mark);
+ }
+ return mark;
}
-pos_T *getmark_buf_fnum(buf_T *buf, int c, bool changefile, int *fnum)
+/// Get a local mark (lowercase and symbols).
+///
+/// Some marks are not actually marks, but positions that are never adjusted or motions presented as
+/// marks. Search first for marks and fallback to finding motion type marks. If it's known
+/// ahead of time that the mark is actually a motion use the mark_get_motion() directly.
+///
+/// @note Lowercase, last_cursor '"', last insert '^', last change '.' are not statically
+/// allocated, everything else is.
+/// @param name the name of the mark.
+/// @param win window to retrieve marks that belong to it (motions and context mark).
+/// @param buf buf to retrieve marks that belong to it.
+///
+/// @return Mark, NULL if not found.
+fmark_T *mark_get_local(buf_T *buf, win_T *win, int name)
{
- pos_T *posp;
- pos_T *startp, *endp;
- static pos_T pos_copy;
+ fmark_T *mark = NULL;
+ if (ASCII_ISLOWER(name)) {
+ // normal named mark
+ mark = &buf->b_namedm[name - 'a'];
+ // to start of previous operator
+ } else if (name == '[') {
+ mark = pos_to_mark(buf, NULL, buf->b_op_start);
+ // to end of previous operator
+ } else if (name == ']') {
+ mark = pos_to_mark(buf, NULL, buf->b_op_end);
+ // visual marks
+ } else if (name == '<' || name == '>') {
+ mark = mark_get_visual(buf, name);
+ // previous context mark
+ } else if (name == '\'' || name == '`') {
+ // TODO(muniter): w_pcmark should be stored as a mark, but causes a nasty bug.
+ mark = pos_to_mark(curbuf, NULL, win->w_pcmark);
+ // to position when leaving buffer
+ } else if (name == '"') {
+ mark = &(buf->b_last_cursor);
+ // to where last Insert mode stopped
+ } else if (name == '^') {
+ mark = &(buf->b_last_insert);
+ // to where last change was made
+ } else if (name == '.') {
+ mark = &buf->b_last_change;
+ // Mark that are actually not marks but motions, e.g {, }, (, ), ...
+ } else {
+ mark = mark_get_motion(buf, win, name);
+ }
- posp = NULL;
+ if (mark) {
+ mark->fnum = buf->b_fnum;
+ }
- // Check for special key, can't be a mark name and might cause islower()
- // to crash.
- if (c < 0) {
- return posp;
- }
- if (c > '~') { // check for islower()/isupper()
- } else if (c == '\'' || c == '`') { // previous context mark
- pos_copy = curwin->w_pcmark; // need to make a copy because
- posp = &pos_copy; // w_pcmark may be changed soon
- } else if (c == '"') { // to pos when leaving buffer
- posp = &(buf->b_last_cursor.mark);
- } else if (c == '^') { // to where Insert mode stopped
- posp = &(buf->b_last_insert.mark);
- } else if (c == '.') { // to where last change was made
- posp = &(buf->b_last_change.mark);
- } else if (c == '[') { // to start of previous operator
- posp = &(buf->b_op_start);
- } else if (c == ']') { // to end of previous operator
- posp = &(buf->b_op_end);
- } else if (c == '{' || c == '}') { // to previous/next paragraph
- pos_T pos;
+ return mark;
+}
+
+/// Get marks that are actually motions but return them as marks
+///
+/// Gets the following motions as marks: '{', '}', '(', ')'
+/// @param name name of the mark
+/// @param win window to retrieve the cursor to calculate the mark.
+/// @param buf buf to wrap motion marks with it's buffer number (fm->fnum).
+///
+/// @return[static] Mark.
+fmark_T *mark_get_motion(buf_T *buf, win_T *win, int name)
+{
+ fmark_T *mark = NULL;
+ const pos_T pos = curwin->w_cursor;
+ const bool slcb = listcmd_busy;
+ listcmd_busy = true; // avoid that '' is changed
+ if (name == '{' || name == '}') { // to previous/next paragraph
oparg_T oa;
- bool slcb = listcmd_busy;
-
- pos = curwin->w_cursor;
- listcmd_busy = true; // avoid that '' is changed
- if (findpar(&oa.inclusive,
- c == '}' ? FORWARD : BACKWARD, 1L, NUL, FALSE)) {
- pos_copy = curwin->w_cursor;
- posp = &pos_copy;
+ if (findpar(&oa.inclusive, name == '}' ? FORWARD : BACKWARD, 1L, NUL, false)) {
+ mark = pos_to_mark(buf, NULL, win->w_cursor);
}
- curwin->w_cursor = pos;
- listcmd_busy = slcb;
- } else if (c == '(' || c == ')') { // to previous/next sentence
- pos_T pos;
- bool slcb = listcmd_busy;
-
- pos = curwin->w_cursor;
- listcmd_busy = true; // avoid that '' is changed
- if (findsent(c == ')' ? FORWARD : BACKWARD, 1L)) {
- pos_copy = curwin->w_cursor;
- posp = &pos_copy;
+ } else if (name == '(' || name == ')') { // to previous/next sentence
+ if (findsent(name == ')' ? FORWARD : BACKWARD, 1L)) {
+ mark = pos_to_mark(buf, NULL, win->w_cursor);
}
- curwin->w_cursor = pos;
- listcmd_busy = slcb;
- } else if (c == '<' || c == '>') { // start/end of visual area
- startp = &buf->b_visual.vi_start;
- endp = &buf->b_visual.vi_end;
- if (((c == '<') == lt(*startp, *endp) || endp->lnum == 0)
- && startp->lnum != 0) {
- posp = startp;
+ }
+ curwin->w_cursor = pos;
+ listcmd_busy = slcb;
+ return mark;
+}
+
+/// Get visual marks '<', '>'
+///
+/// This marks are different to normal marks:
+/// 1. Never adjusted.
+/// 2. Different behavior depending on editor state (visual mode).
+/// 3. Not saved in shada.
+/// 4. Re-ordered when defined in reverse.
+/// @param buf Buffer to get the mark from.
+/// @param name Mark name '<' or '>'.
+///
+/// @return[static] Mark
+fmark_T *mark_get_visual(buf_T *buf, int name)
+{
+ fmark_T *mark = NULL;
+ if (name == '<' || name == '>') {
+ // start/end of visual area
+ pos_T startp = buf->b_visual.vi_start;
+ pos_T endp = buf->b_visual.vi_end;
+ if (((name == '<') == lt(startp, endp) || endp.lnum == 0)
+ && startp.lnum != 0) {
+ mark = pos_to_mark(buf, NULL, startp);
} else {
- posp = endp;
+ mark = pos_to_mark(buf, NULL, endp);
}
- // For Visual line mode, set mark at begin or end of line
- if (buf->b_visual.vi_mode == 'V') {
- pos_copy = *posp;
- posp = &pos_copy;
- if (c == '<') {
- pos_copy.col = 0;
+ if (mark != NULL && buf->b_visual.vi_mode == 'V') {
+ if (name == '<') {
+ mark->mark.col = 0;
} else {
- pos_copy.col = MAXCOL;
+ mark->mark.col = MAXCOL;
}
- pos_copy.coladd = 0;
- }
- } else if (ASCII_ISLOWER(c)) { // normal named mark
- posp = &(buf->b_namedm[c - 'a'].mark);
- } else if (ASCII_ISUPPER(c) || ascii_isdigit(c)) { // named file mark
- if (ascii_isdigit(c)) {
- c = c - '0' + NMARKS;
- } else {
- c -= 'A';
+ mark->mark.coladd = 0;
}
- posp = &(namedfm[c].fmark.mark);
+ }
+ return mark;
+}
+
+/// Wrap a pos_T into an fmark_T, used to abstract marks handling.
+///
+/// Pass an fmp if multiple c
+/// @note view fields are set to 0.
+/// @param buf for fmark->fnum.
+/// @param pos for fmrak->mark.
+/// @param fmp pointer to save the mark.
+///
+/// @return[static] Mark with the given information.
+fmark_T *pos_to_mark(buf_T *buf, fmark_T *fmp, pos_T pos)
+{
+ static fmark_T fms = INIT_FMARK;
+ fmark_T *fm = fmp == NULL ? &fms : fmp;
+ fm->fnum = buf->handle;
+ fm->mark = pos;
+ return fm;
+}
- if (namedfm[c].fmark.fnum == 0) {
- fname2fnum(&namedfm[c]);
+/// Attempt to switch to the buffer of the given global mark
+///
+/// @param fm
+/// @param pcmark_on_switch leave a context mark when switching buffer.
+/// @return whether the buffer was switched or not.
+static MarkMoveRes switch_to_mark_buf(fmark_T *fm, bool pcmark_on_switch)
+{
+ bool res;
+ if (fm->fnum != curbuf->b_fnum) {
+ // Switch to another file.
+ int getfile_flag = pcmark_on_switch ? GETF_SETMARK : 0;
+ res = buflist_getfile(fm->fnum, (linenr_T)1, getfile_flag, false) == OK;
+ return res == true ? kMarkSwitchedBuf : kMarkMoveFailed;
+ }
+ return 0;
+}
+
+/// Move to the given file mark, changing the buffer and cursor position.
+///
+/// Validate the mark, switch to the buffer, and move the cursor.
+/// @param fm Mark, can be NULL will raise E78: Unknown mark
+/// @param flags MarkMove flags to configure the movement to the mark.
+///
+/// @return MarkMovekRes flags representing the outcome
+MarkMoveRes mark_move_to(fmark_T *fm, MarkMove flags)
+{
+ static fmark_T fm_copy = INIT_FMARK;
+ MarkMoveRes res = kMarkMoveSuccess;
+ if (!mark_check(fm)) {
+ res = kMarkMoveFailed;
+ goto end;
+ }
+
+ if (fm->fnum != curbuf->handle) {
+ // Need to change buffer
+ fm_copy = *fm; // Copy, autocommand may change it
+ fm = &fm_copy;
+ res |= switch_to_mark_buf(fm, !(flags & kMarkJumpList));
+ // Failed switching buffer
+ if (res & kMarkMoveFailed) {
+ goto end;
+ }
+ // Check line count now that the **destination buffer is loaded**.
+ if (!mark_check_line_bounds(curbuf, fm)) {
+ res |= kMarkMoveFailed;
+ goto end;
}
+ } else if (flags & kMarkContext) {
+ // Doing it in this condition avoids double context mark when switching buffer.
+ setpcmark();
+ }
+ // Move the cursor while keeping track of what changed for the caller
+ pos_T prev_pos = curwin->w_cursor;
+ pos_T pos = fm->mark;
+ curwin->w_cursor = fm->mark;
+ if (flags & kMarkBeginLine) {
+ beginline(BL_WHITE | BL_FIX);
+ }
+ res = prev_pos.lnum != pos.lnum ? res | kMarkChangedLine | kMarkChangedCursor : res;
+ res = prev_pos.col != pos.col ? res | kMarkChangedCol | kMarkChangedCursor : res;
+ if (flags & kMarkSetView) {
+ mark_view_restore(fm);
+ }
- if (fnum != NULL) {
- *fnum = namedfm[c].fmark.fnum;
- } else if (namedfm[c].fmark.fnum != buf->b_fnum) {
- // mark is in another file
- posp = &pos_copy;
-
- if (namedfm[c].fmark.mark.lnum != 0
- && changefile && namedfm[c].fmark.fnum) {
- if (buflist_getfile(namedfm[c].fmark.fnum,
- (linenr_T)1, GETF_SETMARK, FALSE) == OK) {
- // Set the lnum now, autocommands could have changed it
- curwin->w_cursor = namedfm[c].fmark.mark;
- return (pos_T *)-1;
- }
- pos_copy.lnum = -1; // can't get file
- } else {
- pos_copy.lnum = 0; // mark exists, but is not valid in current buffer
- }
+ if (res & kMarkSwitchedBuf || res & kMarkChangedCursor) {
+ check_cursor();
+ }
+end:
+ return res;
+}
+
+/// Restore the mark view.
+/// By remembering the offset between topline and mark lnum at the time of
+/// definition, this function restores the "view".
+/// @note Assumes the mark has been checked, is valid.
+/// @param fm the named mark.
+void mark_view_restore(fmark_T *fm)
+{
+ if (fm != NULL && fm->view.topline_offset >= 0) {
+ linenr_T topline = fm->mark.lnum - fm->view.topline_offset;
+ // If the mark does not have a view, topline_offset is MAXLNUM,
+ // and this check can prevent restoring mark view in that case.
+ if (topline >= 1) {
+ set_topline(curwin, topline);
}
}
+}
- return posp;
+fmarkv_T mark_view_make(linenr_T topline, pos_T pos)
+{
+ return (fmarkv_T){ pos.lnum - topline };
}
-/// Search for the next named mark in the current file.
+/// Search for the next named mark in the current file from a start position.
///
-/// @param startpos where to start
-/// @param dir direction for search
+/// @param startpos where to start.
+/// @param dir direction for search.
///
-/// @return pointer to pos_T of the next mark or NULL if no mark is found.
-pos_T *getnextmark(pos_T *startpos, int dir, int begin_line)
+/// @return next mark or NULL if no mark is found.
+fmark_T *getnextmark(pos_T *startpos, int dir, int begin_line)
{
int i;
- pos_T *result = NULL;
+ fmark_T *result = NULL;
pos_T pos;
pos = *startpos;
- // When searching backward and leaving the cursor on the first non-blank,
- // position must be in a previous line.
- // When searching forward and leaving the cursor on the first non-blank,
- // position must be in a next line.
if (dir == BACKWARD && begin_line) {
pos.col = 0;
} else if (dir == FORWARD && begin_line) {
@@ -469,14 +637,14 @@ pos_T *getnextmark(pos_T *startpos, int dir, int begin_line)
for (i = 0; i < NMARKS; i++) {
if (curbuf->b_namedm[i].mark.lnum > 0) {
if (dir == FORWARD) {
- if ((result == NULL || lt(curbuf->b_namedm[i].mark, *result))
+ if ((result == NULL || lt(curbuf->b_namedm[i].mark, result->mark))
&& lt(pos, curbuf->b_namedm[i].mark)) {
- result = &curbuf->b_namedm[i].mark;
+ result = &curbuf->b_namedm[i];
}
} else {
- if ((result == NULL || lt(*result, curbuf->b_namedm[i].mark))
+ if ((result == NULL || lt(result->mark, curbuf->b_namedm[i].mark))
&& lt(curbuf->b_namedm[i].mark, pos)) {
- result = &curbuf->b_namedm[i].mark;
+ result = &curbuf->b_namedm[i];
}
}
}
@@ -518,7 +686,7 @@ static void fname2fnum(xfmark_T *fm)
p = path_shorten_fname(NameBuff, IObuff);
// buflist_new() will call fmarks_check_names()
- (void)buflist_new(NameBuff, p, (linenr_T)1, 0);
+ (void)buflist_new((char *)NameBuff, (char *)p, (linenr_T)1, 0);
}
}
@@ -529,7 +697,7 @@ static void fname2fnum(xfmark_T *fm)
*/
void fmarks_check_names(buf_T *buf)
{
- char_u *name = buf->b_ffname;
+ char_u *name = (char_u *)buf->b_ffname;
int i;
if (buf->b_ffname == NULL) {
@@ -551,35 +719,54 @@ static void fmarks_check_one(xfmark_T *fm, char_u *name, buf_T *buf)
{
if (fm->fmark.fnum == 0
&& fm->fname != NULL
- && fnamecmp(name, fm->fname) == 0) {
+ && FNAMECMP(name, fm->fname) == 0) {
fm->fmark.fnum = buf->b_fnum;
XFREE_CLEAR(fm->fname);
}
}
-/*
- * Check a if a position from a mark is valid.
- * Give and error message and return FAIL if not.
- */
-int check_mark(pos_T *pos)
+/// Check the position in @a fm is valid.
+///
+/// Emit error message and return accordingly.
+///
+/// Checks for:
+/// - NULL raising unknown mark error.
+/// - Line number <= 0 raising mark not set.
+/// - Line number > buffer line count, raising invalid mark.
+/// @param fm[in] File mark to check.
+///
+/// @return true if the mark passes all the above checks, else false.
+bool mark_check(fmark_T *fm)
{
- if (pos == NULL) {
+ if (fm == NULL) {
emsg(_(e_umark));
- return FAIL;
- }
- if (pos->lnum <= 0) {
- // lnum is negative if mark is in another file can can't get that
- // file, error message already give then.
- if (pos->lnum == 0) {
+ return false;
+ } else if (fm->mark.lnum <= 0) {
+ // In both cases it's an error but only raise when equals to 0
+ if (fm->mark.lnum == 0) {
emsg(_(e_marknotset));
}
- return FAIL;
+ return false;
}
- if (pos->lnum > curbuf->b_ml.ml_line_count) {
+ // Only check for valid line number if the buffer is loaded.
+ if (fm->fnum == curbuf->handle && !mark_check_line_bounds(curbuf, fm)) {
+ return false;
+ }
+ return true;
+}
+
+/// Check if a mark line number is greater than the buffer line count, and set e_markinval.
+/// @note Should be done after the buffer is loaded into memory.
+/// @param buf Buffer where the mark is set.
+/// @param fm Mark to check.
+/// @return true if below line count else false.
+bool mark_check_line_bounds(buf_T *buf, fmark_T *fm)
+{
+ if (buf != NULL && fm->mark.lnum > buf->b_ml.ml_line_count) {
emsg(_(e_markinval));
- return FAIL;
+ return false;
}
- return OK;
+ return true;
}
/// Clear all marks and change list in the given buffer
@@ -615,7 +802,7 @@ char_u *fm_getname(fmark_T *fmark, int lead_len)
if (fmark->fnum == curbuf->b_fnum) { // current buffer
return mark_line(&(fmark->mark), lead_len);
}
- return buflist_nr2name(fmark->fnum, FALSE, TRUE);
+ return (char_u *)buflist_nr2name(fmark->fnum, false, true);
}
/*
@@ -630,14 +817,14 @@ static char_u *mark_line(pos_T *mp, int lead_len)
if (mp->lnum == 0 || mp->lnum > curbuf->b_ml.ml_line_count) {
return vim_strsave((char_u *)"-invalid-");
}
- assert(Columns >= 0 && (size_t)Columns <= SIZE_MAX);
+ assert(Columns >= 0);
// Allow for up to 5 bytes per character.
- s = vim_strnsave(skipwhite(ml_get(mp->lnum)), (size_t)Columns * 5);
+ s = vim_strnsave((char_u *)skipwhite((char *)ml_get(mp->lnum)), (size_t)Columns * 5);
// Truncate the line to fit it in the window
len = 0;
for (p = s; *p != NUL; MB_PTR_ADV(p)) {
- len += ptr2cells(p);
+ len += ptr2cells((char *)p);
if (len >= Columns - lead_len) {
break;
}
@@ -651,7 +838,7 @@ static char_u *mark_line(pos_T *mp, int lead_len)
*/
void ex_marks(exarg_T *eap)
{
- char_u *arg = eap->arg;
+ char_u *arg = (char_u *)eap->arg;
int i;
char_u *name;
pos_T *posp, *startp, *endp;
@@ -668,7 +855,7 @@ void ex_marks(exarg_T *eap)
if (namedfm[i].fmark.fnum != 0) {
name = fm_getname(&namedfm[i].fmark, 15);
} else {
- name = namedfm[i].fname;
+ name = (char_u *)namedfm[i].fname;
}
if (name != NULL) {
show_one_mark(i >= NMARKS ? i - NMARKS + '0' : i + 'A',
@@ -717,7 +904,7 @@ static void show_one_mark(int c, char_u *arg, pos_T *p, char_u *name_arg, int cu
}
}
} else if (!got_int
- && (arg == NULL || vim_strchr(arg, c) != NULL)
+ && (arg == NULL || vim_strchr((char *)arg, c) != NULL)
&& p->lnum != 0) {
// don't output anything if 'q' typed at --more-- prompt
if (name == NULL && current) {
@@ -732,8 +919,8 @@ static void show_one_mark(int c, char_u *arg, pos_T *p, char_u *name_arg, int cu
}
msg_putchar('\n');
if (!got_int) {
- snprintf((char *)IObuff, IOSIZE, " %c %6ld %4d ", c, p->lnum, p->col);
- msg_outtrans(IObuff);
+ snprintf((char *)IObuff, IOSIZE, " %c %6" PRIdLINENR " %4d ", c, p->lnum, p->col);
+ msg_outtrans((char *)IObuff);
if (name != NULL) {
msg_outtrans_attr(name, current ? HL_ATTR(HLF_D) : 0);
}
@@ -767,7 +954,7 @@ void ex_delmarks(exarg_T *eap)
emsg(_(e_argreq));
} else {
// clear specified marks only
- for (p = eap->arg; *p != NUL; ++p) {
+ for (p = (char_u *)eap->arg; *p != NUL; p++) {
lower = ASCII_ISLOWER(*p);
digit = ascii_isdigit(*p);
if (lower || digit || ASCII_ISUPPER(*p)) {
@@ -844,6 +1031,11 @@ void ex_jumps(exarg_T *eap)
if (curwin->w_jumplist[i].fmark.mark.lnum != 0) {
name = fm_getname(&curwin->w_jumplist[i].fmark, 16);
+ // Make sure to output the current indicator, even when on an wiped
+ // out buffer. ":filter" may still skip it.
+ if (name == NULL && i == curwin->w_jumplistidx) {
+ name = vim_strsave((char_u *)"-invalid-");
+ }
// apply :filter /pat/ or file name not available
if (name == NULL || message_filtered(name)) {
xfree(name);
@@ -855,13 +1047,11 @@ void ex_jumps(exarg_T *eap)
xfree(name);
break;
}
- sprintf((char *)IObuff, "%c %2d %5ld %4d ",
- i == curwin->w_jumplistidx ? '>' : ' ',
- i > curwin->w_jumplistidx ? i - curwin->w_jumplistidx
- : curwin->w_jumplistidx - i,
- curwin->w_jumplist[i].fmark.mark.lnum,
- curwin->w_jumplist[i].fmark.mark.col);
- msg_outtrans(IObuff);
+ snprintf((char *)IObuff, IOSIZE, "%c %2d %5" PRIdLINENR " %4d ",
+ i == curwin->w_jumplistidx ? '>' : ' ',
+ i > curwin->w_jumplistidx ? i - curwin->w_jumplistidx : curwin->w_jumplistidx - i,
+ curwin->w_jumplist[i].fmark.mark.lnum, curwin->w_jumplist[i].fmark.mark.col);
+ msg_outtrans((char *)IObuff);
msg_outtrans_attr(name,
curwin->w_jumplist[i].fmark.fnum == curbuf->b_fnum
? HL_ATTR(HLF_D) : 0);
@@ -905,7 +1095,7 @@ void ex_changes(exarg_T *eap)
: curwin->w_changelistidx - i,
(long)curbuf->b_changelist[i].mark.lnum,
curbuf->b_changelist[i].mark.col);
- msg_outtrans(IObuff);
+ msg_outtrans((char *)IObuff);
name = mark_line(&curbuf->b_changelist[i].mark, 17);
msg_outtrans_attr(name, HL_ATTR(HLF_D));
xfree(name);
@@ -918,7 +1108,7 @@ void ex_changes(exarg_T *eap)
}
}
-#define one_adjust(add) \
+#define ONE_ADJUST(add) \
{ \
lp = add; \
if (*lp >= line1 && *lp <= line2) \
@@ -933,7 +1123,7 @@ void ex_changes(exarg_T *eap)
}
// don't delete the line, just put at first deleted line
-#define one_adjust_nodel(add) \
+#define ONE_ADJUST_NODEL(add) \
{ \
lp = add; \
if (*lp >= line1 && *lp <= line2) \
@@ -958,7 +1148,8 @@ void ex_changes(exarg_T *eap)
* Example: Insert two lines below 55: mark_adjust(56, MAXLNUM, 2, 0);
* or: mark_adjust(56, 55, MAXLNUM, 2);
*/
-void mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after, ExtmarkOp op)
+void mark_adjust(linenr_T line1, linenr_T line2, linenr_T amount, linenr_T amount_after,
+ ExtmarkOp op)
{
mark_adjust_internal(line1, line2, amount, amount_after, true, op);
}
@@ -968,14 +1159,14 @@ void mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after,
// This is only useful when folds need to be moved in a way different to
// calling foldMarkAdjust() with arguments line1, line2, amount, amount_after,
// for an example of why this may be necessary, see do_move().
-void mark_adjust_nofold(linenr_T line1, linenr_T line2, long amount, long amount_after,
+void mark_adjust_nofold(linenr_T line1, linenr_T line2, linenr_T amount, linenr_T amount_after,
ExtmarkOp op)
{
mark_adjust_internal(line1, line2, amount, amount_after, false, op);
}
-static void mark_adjust_internal(linenr_T line1, linenr_T line2, long amount, long amount_after,
- bool adjust_folds, ExtmarkOp op)
+static void mark_adjust_internal(linenr_T line1, linenr_T line2, linenr_T amount,
+ linenr_T amount_after, bool adjust_folds, ExtmarkOp op)
{
int i;
int fnum = curbuf->b_fnum;
@@ -986,40 +1177,39 @@ static void mark_adjust_internal(linenr_T line1, linenr_T line2, long amount, lo
return;
}
- if (!cmdmod.lockmarks) {
+ if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) {
// named marks, lower case and upper case
for (i = 0; i < NMARKS; i++) {
- one_adjust(&(curbuf->b_namedm[i].mark.lnum));
+ ONE_ADJUST(&(curbuf->b_namedm[i].mark.lnum));
if (namedfm[i].fmark.fnum == fnum) {
- one_adjust_nodel(&(namedfm[i].fmark.mark.lnum));
+ ONE_ADJUST_NODEL(&(namedfm[i].fmark.mark.lnum));
}
}
for (i = NMARKS; i < NGLOBALMARKS; i++) {
if (namedfm[i].fmark.fnum == fnum) {
- one_adjust_nodel(&(namedfm[i].fmark.mark.lnum));
+ ONE_ADJUST_NODEL(&(namedfm[i].fmark.mark.lnum));
}
}
// last Insert position
- one_adjust(&(curbuf->b_last_insert.mark.lnum));
+ ONE_ADJUST(&(curbuf->b_last_insert.mark.lnum));
// last change position
- one_adjust(&(curbuf->b_last_change.mark.lnum));
+ ONE_ADJUST(&(curbuf->b_last_change.mark.lnum));
// last cursor position, if it was set
if (!equalpos(curbuf->b_last_cursor.mark, initpos)) {
- one_adjust(&(curbuf->b_last_cursor.mark.lnum));
+ ONE_ADJUST(&(curbuf->b_last_cursor.mark.lnum));
}
-
// list of change positions
- for (i = 0; i < curbuf->b_changelistlen; ++i) {
- one_adjust_nodel(&(curbuf->b_changelist[i].mark.lnum));
+ for (i = 0; i < curbuf->b_changelistlen; i++) {
+ ONE_ADJUST_NODEL(&(curbuf->b_changelist[i].mark.lnum));
}
// Visual area
- one_adjust_nodel(&(curbuf->b_visual.vi_start.lnum));
- one_adjust_nodel(&(curbuf->b_visual.vi_end.lnum));
+ ONE_ADJUST_NODEL(&(curbuf->b_visual.vi_start.lnum));
+ ONE_ADJUST_NODEL(&(curbuf->b_visual.vi_end.lnum));
// quickfix marks
if (!qf_mark_adjust(NULL, line1, line2, amount, amount_after)) {
@@ -1042,44 +1232,44 @@ static void mark_adjust_internal(linenr_T line1, linenr_T line2, long amount, lo
}
// previous context mark
- one_adjust(&(curwin->w_pcmark.lnum));
+ ONE_ADJUST(&(curwin->w_pcmark.lnum));
// previous pcmark
- one_adjust(&(curwin->w_prev_pcmark.lnum));
+ ONE_ADJUST(&(curwin->w_prev_pcmark.lnum));
// saved cursor for formatting
if (saved_cursor.lnum != 0) {
- one_adjust_nodel(&(saved_cursor.lnum));
+ ONE_ADJUST_NODEL(&(saved_cursor.lnum));
}
/*
* Adjust items in all windows related to the current buffer.
*/
FOR_ALL_TAB_WINDOWS(tab, win) {
- if (!cmdmod.lockmarks) {
+ if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) {
// Marks in the jumplist. When deleting lines, this may create
// duplicate marks in the jumplist, they will be removed later.
for (i = 0; i < win->w_jumplistlen; i++) {
if (win->w_jumplist[i].fmark.fnum == fnum) {
- one_adjust_nodel(&(win->w_jumplist[i].fmark.mark.lnum));
+ ONE_ADJUST_NODEL(&(win->w_jumplist[i].fmark.mark.lnum));
}
}
}
if (win->w_buffer == curbuf) {
- if (!cmdmod.lockmarks) {
+ if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) {
// marks in the tag stack
for (i = 0; i < win->w_tagstacklen; i++) {
if (win->w_tagstack[i].fmark.fnum == fnum) {
- one_adjust_nodel(&(win->w_tagstack[i].fmark.mark.lnum));
+ ONE_ADJUST_NODEL(&(win->w_tagstack[i].fmark.mark.lnum));
}
}
}
// the displayed Visual area
if (win->w_old_cursor_lnum != 0) {
- one_adjust_nodel(&(win->w_old_cursor_lnum));
- one_adjust_nodel(&(win->w_old_visual_lnum));
+ ONE_ADJUST_NODEL(&(win->w_old_cursor_lnum));
+ ONE_ADJUST_NODEL(&(win->w_old_visual_lnum));
}
// topline and cursor position for windows with the same buffer
@@ -1127,14 +1317,14 @@ static void mark_adjust_internal(linenr_T line1, linenr_T line2, long amount, lo
}
// This code is used often, needs to be fast.
-#define col_adjust(pp) \
+#define COL_ADJUST(pp) \
{ \
posp = pp; \
if (posp->lnum == lnum && posp->col >= mincol) \
{ \
posp->lnum += lnum_amount; \
assert(col_amount > INT_MIN && col_amount <= INT_MAX); \
- if (col_amount < 0 && posp->col <= (colnr_T)-col_amount) { \
+ if (col_amount < 0 && posp->col <= (colnr_T) - col_amount) { \
posp->col = 0; \
} else if (posp->col < spaces_removed) { \
posp->col = (int)col_amount + spaces_removed; \
@@ -1149,52 +1339,52 @@ static void mark_adjust_internal(linenr_T line1, linenr_T line2, long amount, lo
// position.
// "spaces_removed" is the number of spaces that were removed, matters when the
// cursor is inside them.
-void mark_col_adjust(linenr_T lnum, colnr_T mincol, long lnum_amount, long col_amount,
+void mark_col_adjust(linenr_T lnum, colnr_T mincol, linenr_T lnum_amount, long col_amount,
int spaces_removed)
{
int i;
int fnum = curbuf->b_fnum;
pos_T *posp;
- if ((col_amount == 0L && lnum_amount == 0L) || cmdmod.lockmarks) {
+ if ((col_amount == 0L && lnum_amount == 0L) || (cmdmod.cmod_flags & CMOD_LOCKMARKS)) {
return; // nothing to do
}
// named marks, lower case and upper case
for (i = 0; i < NMARKS; i++) {
- col_adjust(&(curbuf->b_namedm[i].mark));
+ COL_ADJUST(&(curbuf->b_namedm[i].mark));
if (namedfm[i].fmark.fnum == fnum) {
- col_adjust(&(namedfm[i].fmark.mark));
+ COL_ADJUST(&(namedfm[i].fmark.mark));
}
}
for (i = NMARKS; i < NGLOBALMARKS; i++) {
if (namedfm[i].fmark.fnum == fnum) {
- col_adjust(&(namedfm[i].fmark.mark));
+ COL_ADJUST(&(namedfm[i].fmark.mark));
}
}
// last Insert position
- col_adjust(&(curbuf->b_last_insert.mark));
+ COL_ADJUST(&(curbuf->b_last_insert.mark));
// last change position
- col_adjust(&(curbuf->b_last_change.mark));
+ COL_ADJUST(&(curbuf->b_last_change.mark));
// list of change positions
- for (i = 0; i < curbuf->b_changelistlen; ++i) {
- col_adjust(&(curbuf->b_changelist[i].mark));
+ for (i = 0; i < curbuf->b_changelistlen; i++) {
+ COL_ADJUST(&(curbuf->b_changelist[i].mark));
}
// Visual area
- col_adjust(&(curbuf->b_visual.vi_start));
- col_adjust(&(curbuf->b_visual.vi_end));
+ COL_ADJUST(&(curbuf->b_visual.vi_start));
+ COL_ADJUST(&(curbuf->b_visual.vi_end));
// previous context mark
- col_adjust(&(curwin->w_pcmark));
+ COL_ADJUST(&(curwin->w_pcmark));
// previous pcmark
- col_adjust(&(curwin->w_prev_pcmark));
+ COL_ADJUST(&(curwin->w_prev_pcmark));
// saved cursor for formatting
- col_adjust(&saved_cursor);
+ COL_ADJUST(&saved_cursor);
/*
* Adjust items in all windows related to the current buffer.
@@ -1203,7 +1393,7 @@ void mark_col_adjust(linenr_T lnum, colnr_T mincol, long lnum_amount, long col_a
// marks in the jumplist
for (i = 0; i < win->w_jumplistlen; ++i) {
if (win->w_jumplist[i].fmark.fnum == fnum) {
- col_adjust(&(win->w_jumplist[i].fmark.mark));
+ COL_ADJUST(&(win->w_jumplist[i].fmark.mark));
}
}
@@ -1211,13 +1401,13 @@ void mark_col_adjust(linenr_T lnum, colnr_T mincol, long lnum_amount, long col_a
// marks in the tag stack
for (i = 0; i < win->w_tagstacklen; i++) {
if (win->w_tagstack[i].fmark.fnum == fnum) {
- col_adjust(&(win->w_tagstack[i].fmark.mark));
+ COL_ADJUST(&(win->w_tagstack[i].fmark.mark));
}
}
// cursor position for other windows with the same buffer
if (win != curwin) {
- col_adjust(&win->w_cursor);
+ COL_ADJUST(&win->w_cursor);
}
}
}
@@ -1306,7 +1496,7 @@ void copy_jumplist(win_T *from, win_T *to)
for (i = 0; i < from->w_jumplistlen; ++i) {
to->w_jumplist[i] = from->w_jumplist[i];
if (from->w_jumplist[i].fname != NULL) {
- to->w_jumplist[i].fname = vim_strsave(from->w_jumplist[i].fname);
+ to->w_jumplist[i].fname = xstrdup(from->w_jumplist[i].fname);
}
}
to->w_jumplistlen = from->w_jumplistlen;
@@ -1315,7 +1505,7 @@ void copy_jumplist(win_T *from, win_T *to)
/// Iterate over jumplist items
///
-/// @warning No jumplist-editing functions must be run while iteration is in
+/// @warning No jumplist-editing functions must be called while iteration is in
/// progress.
///
/// @param[in] iter Iterator. Pass NULL to start iteration.
@@ -1328,7 +1518,7 @@ const void *mark_jumplist_iter(const void *const iter, const win_T *const win, x
FUNC_ATTR_NONNULL_ARG(2, 3) FUNC_ATTR_WARN_UNUSED_RESULT
{
if (iter == NULL && win->w_jumplistlen == 0) {
- *fm = (xfmark_T) { { { 0, 0, 0 }, 0, 0, NULL }, NULL };
+ *fm = (xfmark_T)INIT_XFMARK;
return NULL;
}
const xfmark_T *const iter_mark =
@@ -1345,7 +1535,7 @@ const void *mark_jumplist_iter(const void *const iter, const win_T *const win, x
/// Iterate over global marks
///
-/// @warning No mark-editing functions must be run while iteration is in
+/// @warning No mark-editing functions must be called while iteration is in
/// progress.
///
/// @param[in] iter Iterator. Pass NULL to start iteration.
@@ -1419,7 +1609,7 @@ static inline const fmark_T *next_buffer_mark(const buf_T *const buf, char *cons
/// Iterate over buffer marks
///
-/// @warning No mark-editing functions must be run while iteration is in
+/// @warning No mark-editing functions must be called while iteration is in
/// progress.
///
/// @param[in] iter Iterator. Pass NULL to start iteration.
@@ -1536,7 +1726,7 @@ void free_jumplist(win_T *wp)
void set_last_cursor(win_T *win)
{
if (win->w_buffer != NULL) {
- RESET_FMARK(&win->w_buffer->b_last_cursor, win->w_cursor, 0);
+ RESET_FMARK(&win->w_buffer->b_last_cursor, win->w_cursor, 0, ((fmarkv_T)INIT_FMARKV));
}
}
@@ -1574,14 +1764,13 @@ void mark_mb_adjustpos(buf_T *buf, pos_T *lp)
// double-wide character.
if (lp->coladd == 1
&& p[lp->col] != TAB
- && vim_isprintc(utf_ptr2char(p + lp->col))
- && ptr2cells(p + lp->col) > 1) {
+ && vim_isprintc(utf_ptr2char((char *)p + lp->col))
+ && ptr2cells((char *)p + lp->col) > 1) {
lp->coladd = 0;
}
}
}
-
// Add information about mark 'mname' to list 'l'
static int add_mark(list_T *l, const char *mname, const pos_T *pos, int bufnr, const char *fname)
FUNC_ATTR_NONNULL_ARG(1, 2, 3)
@@ -1609,7 +1798,6 @@ static int add_mark(list_T *l, const char *mname, const pos_T *pos, int bufnr, c
return OK;
}
-
/// Get information about marks local to a buffer.
///
/// @param[in] buf Buffer to get the marks from
@@ -1639,9 +1827,10 @@ void get_buf_local_marks(const buf_T *buf, list_T *l)
/// Get a global mark
///
+/// @note Mark might not have it's fnum resolved.
/// @param[in] Name of named mark
/// @param[out] Global/file mark
-xfmark_T get_global_mark(char name)
+xfmark_T get_raw_global_mark(char name)
{
return namedfm[mark_global_index(name)];
}
@@ -1658,9 +1847,9 @@ void get_global_marks(list_T *l)
// Marks 'A' to 'Z' and '0' to '9'
for (int i = 0; i < NMARKS + EXTRA_MARKS; i++) {
if (namedfm[i].fmark.fnum != 0) {
- name = (char *)buflist_nr2name(namedfm[i].fmark.fnum, true, true);
+ name = buflist_nr2name(namedfm[i].fmark.fnum, true, true);
} else {
- name = (char *)namedfm[i].fname;
+ name = namedfm[i].fname;
}
if (name != NULL) {
mname[1] = i >= NMARKS ? (char)(i - NMARKS + '0') : (char)(i + 'A');
diff --git a/src/nvim/mark.h b/src/nvim/mark.h
index a55f733d9a..6da976e8d3 100644
--- a/src/nvim/mark.h
+++ b/src/nvim/mark.h
@@ -13,42 +13,43 @@
#include "nvim/pos.h"
/// Set fmark using given value
-#define SET_FMARK(fmarkp_, mark_, fnum_) \
+#define SET_FMARK(fmarkp_, mark_, fnum_, view_) \
do { \
fmark_T *const fmarkp__ = fmarkp_; \
fmarkp__->mark = mark_; \
fmarkp__->fnum = fnum_; \
fmarkp__->timestamp = os_time(); \
+ fmarkp__->view = view_; \
fmarkp__->additional_data = NULL; \
} while (0)
/// Free and set fmark using given value
-#define RESET_FMARK(fmarkp_, mark_, fnum_) \
+#define RESET_FMARK(fmarkp_, mark_, fnum_, view_) \
do { \
fmark_T *const fmarkp___ = fmarkp_; \
free_fmark(*fmarkp___); \
- SET_FMARK(fmarkp___, mark_, fnum_); \
+ SET_FMARK(fmarkp___, mark_, fnum_, view_); \
} while (0)
/// Clear given fmark
#define CLEAR_FMARK(fmarkp_) \
- RESET_FMARK(fmarkp_, ((pos_T) { 0, 0, 0 }), 0)
+ RESET_FMARK(fmarkp_, ((pos_T) { 0, 0, 0 }), 0, ((fmarkv_T) { 0 }))
/// Set given extended mark (regular mark + file name)
-#define SET_XFMARK(xfmarkp_, mark_, fnum_, fname_) \
+#define SET_XFMARK(xfmarkp_, mark_, fnum_, view_, fname_) \
do { \
xfmark_T *const xfmarkp__ = xfmarkp_; \
xfmarkp__->fname = fname_; \
- SET_FMARK(&(xfmarkp__->fmark), mark_, fnum_); \
+ SET_FMARK(&(xfmarkp__->fmark), mark_, fnum_, view_); \
} while (0)
/// Free and set given extended mark (regular mark + file name)
-#define RESET_XFMARK(xfmarkp_, mark_, fnum_, fname_) \
+#define RESET_XFMARK(xfmarkp_, mark_, fnum_, view_, fname_) \
do { \
xfmark_T *const xfmarkp__ = xfmarkp_; \
free_xfmark(*xfmarkp__); \
xfmarkp__->fname = fname_; \
- SET_FMARK(&(xfmarkp__->fmark), mark_, fnum_); \
+ SET_FMARK(&(xfmarkp__->fmark), mark_, fnum_, view_); \
} while (0)
/// Convert mark name to the offset
diff --git a/src/nvim/mark_defs.h b/src/nvim/mark_defs.h
index 51199a09e0..a78056c5f9 100644
--- a/src/nvim/mark_defs.h
+++ b/src/nvim/mark_defs.h
@@ -10,6 +10,33 @@
* (a normal mark is a lnum/col pair, the same as a file position)
*/
+/// Flags for outcomes when moving to a mark.
+typedef enum {
+ kMarkMoveSuccess = 1, ///< Successful move.
+ kMarkMoveFailed = 2, ///< Failed to move.
+ kMarkSwitchedBuf = 4, ///< Switched curbuf.
+ kMarkChangedCol = 8, ///< Changed the cursor col.
+ kMarkChangedLine = 16, ///< Changed the cursor line.
+ kMarkChangedCursor = 32, ///< Changed the cursor.
+ kMarkChangedView = 64, ///< Changed the view.
+} MarkMoveRes;
+
+/// Flags to configure the movement to a mark.
+typedef enum {
+ kMarkBeginLine = 1, ///< Move cursor to the beginning of the line.
+ kMarkContext = 2, ///< Leave context mark when moving the cursor.
+ KMarkNoContext = 4, ///< Don't leave a context mark.
+ kMarkSetView = 8, ///< Set the mark view after moving
+ kMarkJumpList = 16, ///< Special case, don't leave context mark when switching buffer
+} MarkMove;
+
+/// Options when getting a mark
+typedef enum {
+ kMarkBufLocal, ///< Only return marks that belong to the buffer.
+ kMarkAll, ///< Return all types of marks.
+ kMarkAllNoResolve, ///< Return all types of marks but don't resolve fnum (global marks).
+} MarkGet;
+
/// Number of possible numbered global marks
#define EXTRA_MARKS ('9' - '0' + 1)
@@ -25,24 +52,40 @@
/// but they are not saved in ShaDa files.
#define NLOCALMARKS (NMARKS + 3)
+/// Max value of local mark
+#define NMARK_LOCAL_MAX 126 // Index of '~'
+
/// Maximum number of marks in jump list
#define JUMPLISTSIZE 100
/// Maximum number of tags in tag stack
#define TAGSTACKSIZE 20
+/// Represents view in which the mark was created
+typedef struct fmarkv {
+ linenr_T topline_offset; ///< Amount of lines from the mark lnum to the top of the window.
+ ///< Use MAXLNUM to indicate that the mark does not have a view.
+} fmarkv_T;
+
+#define INIT_FMARKV { MAXLNUM }
+
/// Structure defining single local mark
typedef struct filemark {
pos_T mark; ///< Cursor position.
int fnum; ///< File number.
Timestamp timestamp; ///< Time when this mark was last set.
+ fmarkv_T view; ///< View the mark was created on
dict_T *additional_data; ///< Additional data from ShaDa file.
} fmark_T;
+#define INIT_FMARK { { 0, 0, 0 }, 0, 0, INIT_FMARKV, NULL }
+
/// Structure defining extended mark (mark with file name attached)
typedef struct xfilemark {
fmark_T fmark; ///< Actual mark.
- char_u *fname; ///< File name, used when fnum == 0.
+ char *fname; ///< File name, used when fnum == 0.
} xfmark_T;
+#define INIT_XFMARK { INIT_FMARK, NULL }
+
#endif // NVIM_MARK_DEFS_H
diff --git a/src/nvim/marktree.c b/src/nvim/marktree.c
index 38014ab375..03340a99d6 100644
--- a/src/nvim/marktree.c
+++ b/src/nvim/marktree.c
@@ -54,17 +54,11 @@
#include "nvim/marktree.h"
#define T MT_BRANCH_FACTOR
-#define ILEN (sizeof(mtnode_t)+(2 * T) * sizeof(void *))
+#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])
+#define rawkey(itr) ((itr)->node->key[(itr)->i])
static bool pos_leq(mtpos_t a, mtpos_t b)
{
@@ -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
@@ -164,7 +158,7 @@ static inline void split_node(MarkTree *b, mtnode_t *x, const int i)
z->level = y->level;
z->n = T - 1;
memcpy(z->key, &y->key[T], sizeof(mtkey_t) * (T - 1));
- for (int j = 0; j < T-1; j++) {
+ for (int j = 0; j < T - 1; j++) {
refkey(b, z, j);
}
if (y->level) {
@@ -185,11 +179,11 @@ static inline void split_node(MarkTree *b, mtnode_t *x, const int i)
refkey(b, x, i);
x->n++;
- for (int j = 0; j < T-1; j++) {
+ for (int j = 0; j < T - 1; j++) {
relative(x->key[i].pos, &z->key[j].pos);
}
if (i > 0) {
- unrelative(x->key[i-1].pos, &x->key[i].pos);
+ unrelative(x->key[i - 1].pos, &x->key[i].pos);
}
}
@@ -204,7 +198,7 @@ static inline void marktree_putp_aux(MarkTree *b, mtnode_t *x, mtkey_t k)
(size_t)(x->n - i - 1) * sizeof(mtkey_t));
}
x->key[i + 1] = k;
- refkey(b, x, i+1);
+ refkey(b, x, i + 1);
x->n++;
} else {
i = marktree_getp_aux(x, k, 0) + 1;
@@ -215,44 +209,34 @@ static inline void marktree_putp_aux(MarkTree *b, mtnode_t *x, mtkey_t k)
}
}
if (i > 0) {
- relative(x->key[i-1].pos, &k.pos);
+ relative(x->key[i - 1].pos, &k.pos);
}
marktree_putp_aux(b, x->ptr[i], 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++;
@@ -263,7 +247,7 @@ void marktree_put_key(MarkTree *b, int row, int col, uint64_t id)
if (r->n == 2 * T - 1) {
b->n_nodes++;
s = (mtnode_t *)xcalloc(1, ILEN);
- b->root = s; s->level = r->level+1; s->n = 0;
+ b->root = s; s->level = r->level + 1; s->n = 0;
s->ptr[0] = r;
r->parent = s;
split_node(b, s, 0);
@@ -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) {
@@ -320,9 +304,9 @@ void marktree_del_itr(MarkTree *b, MarkTreeIter *itr, bool rev)
mtnode_t *x = itr->node;
assert(x->level == 0);
mtkey_t intkey = x->key[itr->i];
- if (x->n > itr->i+1) {
- memmove(&x->key[itr->i], &x->key[itr->i+1],
- sizeof(mtkey_t) * (size_t)(x->n - itr->i-1));
+ if (x->n > itr->i + 1) {
+ memmove(&x->key[itr->i], &x->key[itr->i + 1],
+ sizeof(mtkey_t) * (size_t)(x->n - itr->i - 1));
}
x->n--;
@@ -331,7 +315,7 @@ void marktree_del_itr(MarkTree *b, MarkTreeIter *itr, bool rev)
// abort();
// }
if (adjustment == -1) {
- int ilvl = itr->lvl-1;
+ int ilvl = itr->lvl - 1;
const mtnode_t *lnode = x;
do {
const mtnode_t *const p = lnode->parent;
@@ -341,7 +325,7 @@ void marktree_del_itr(MarkTree *b, MarkTreeIter *itr, bool rev)
const int i = itr->s[ilvl].i;
assert(p->ptr[i] == lnode);
if (i > 0) {
- unrelative(p->key[i-1].pos, &intkey.pos);
+ unrelative(p->key[i - 1].pos, &intkey.pos);
}
lnode = p;
ilvl--;
@@ -351,7 +335,7 @@ void marktree_del_itr(MarkTree *b, MarkTreeIter *itr, bool rev)
cur->key[curi] = intkey;
refkey(b, cur, curi);
relative(intkey.pos, &deleted.pos);
- mtnode_t *y = cur->ptr[curi+1];
+ mtnode_t *y = cur->ptr[curi + 1];
if (deleted.pos.row || deleted.pos.col) {
while (y) {
for (int k = 0; k < y->n; k++) {
@@ -364,37 +348,37 @@ 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;
- int rlvl = itr->lvl-1;
+ int rlvl = itr->lvl - 1;
int *lasti = &itr->i;
while (x != b->root) {
assert(rlvl >= 0);
mtnode_t *p = x->parent;
- if (x->n >= T-1) {
+ if (x->n >= T - 1) {
// we are done, if this node is fine the rest of the tree will be
break;
}
int pi = itr->s[rlvl].i;
assert(p->ptr[pi] == x);
- if (pi > 0 && p->ptr[pi-1]->n > T-1) {
+ if (pi > 0 && p->ptr[pi - 1]->n > T - 1) {
*lasti += 1;
itr_dirty = true;
// steal one key from the left neighbour
- pivot_right(b, p, pi-1);
+ pivot_right(b, p, pi - 1);
break;
- } else if (pi < p->n && p->ptr[pi+1]->n > T-1) {
+ } else if (pi < p->n && p->ptr[pi + 1]->n > T - 1) {
// steal one key from right neighbour
pivot_left(b, p, pi);
break;
} else if (pi > 0) {
// fprintf(stderr, "LEFT ");
- assert(p->ptr[pi-1]->n == T-1);
+ assert(p->ptr[pi - 1]->n == T - 1);
// merge with left neighbour
*lasti += T;
- x = merge_node(b, p, pi-1);
+ x = merge_node(b, p, pi - 1);
if (lasti == &itr->i) {
// TRICKY: we merged the node the iterator was on
itr->node = x;
@@ -403,7 +387,7 @@ void marktree_del_itr(MarkTree *b, MarkTreeIter *itr, bool rev)
itr_dirty = true;
} else {
// fprintf(stderr, "RIGHT ");
- assert(pi < p->n && p->ptr[pi+1]->n == T-1);
+ assert(pi < p->n && p->ptr[pi + 1]->n == T - 1);
merge_node(b, p, pi);
// no iter adjustment needed
}
@@ -415,7 +399,7 @@ void marktree_del_itr(MarkTree *b, MarkTreeIter *itr, bool rev)
// 6.
if (b->root->n == 0) {
if (itr->lvl > 0) {
- memmove(itr->s, itr->s+1, (size_t)(itr->lvl-1) * sizeof(*itr->s));
+ memmove(itr->s, itr->s + 1, (size_t)(itr->lvl - 1) * sizeof(*itr->s));
itr->lvl--;
}
if (b->root->level) {
@@ -457,26 +441,26 @@ void marktree_del_itr(MarkTree *b, MarkTreeIter *itr, bool rev)
static mtnode_t *merge_node(MarkTree *b, mtnode_t *p, int i)
{
- mtnode_t *x = p->ptr[i], *y = p->ptr[i+1];
+ mtnode_t *x = p->ptr[i], *y = p->ptr[i + 1];
x->key[x->n] = p->key[i];
refkey(b, x, x->n);
if (i > 0) {
- relative(p->key[i-1].pos, &x->key[x->n].pos);
+ relative(p->key[i - 1].pos, &x->key[x->n].pos);
}
- memmove(&x->key[x->n+1], y->key, (size_t)y->n * sizeof(mtkey_t));
+ memmove(&x->key[x->n + 1], y->key, (size_t)y->n * sizeof(mtkey_t));
for (int k = 0; k < y->n; k++) {
- refkey(b, x, x->n+1+k);
- unrelative(x->key[x->n].pos, &x->key[x->n+1+k].pos);
+ refkey(b, x, x->n + 1 + k);
+ unrelative(x->key[x->n].pos, &x->key[x->n + 1 + k].pos);
}
if (x->level) {
- memmove(&x->ptr[x->n+1], y->ptr, ((size_t)y->n + 1) * sizeof(mtnode_t *));
- for (int k = 0; k < y->n+1; k++) {
- x->ptr[x->n+k+1]->parent = x;
+ memmove(&x->ptr[x->n + 1], y->ptr, ((size_t)y->n + 1) * sizeof(mtnode_t *));
+ for (int k = 0; k < y->n + 1; k++) {
+ x->ptr[x->n + k + 1]->parent = x;
}
}
- x->n += y->n+1;
+ x->n += y->n + 1;
memmove(&p->key[i], &p->key[i + 1], (size_t)(p->n - i - 1) * sizeof(mtkey_t));
memmove(&p->ptr[i + 1], &p->ptr[i + 2],
(size_t)(p->n - i - 1) * sizeof(mtkey_t *));
@@ -490,7 +474,7 @@ static mtnode_t *merge_node(MarkTree *b, mtnode_t *p, int i)
// the two nodes instead of stealing just one key
static void pivot_right(MarkTree *b, mtnode_t *p, int i)
{
- mtnode_t *x = p->ptr[i], *y = p->ptr[i+1];
+ mtnode_t *x = p->ptr[i], *y = p->ptr[i + 1];
memmove(&y->key[1], y->key, (size_t)y->n * sizeof(mtkey_t));
if (y->level) {
memmove(&y->ptr[1], y->ptr, ((size_t)y->n + 1) * sizeof(mtnode_t *));
@@ -506,7 +490,7 @@ static void pivot_right(MarkTree *b, mtnode_t *p, int i)
x->n--;
y->n++;
if (i > 0) {
- unrelative(p->key[i-1].pos, &p->key[i].pos);
+ unrelative(p->key[i - 1].pos, &p->key[i].pos);
}
relative(p->key[i].pos, &y->key[0].pos);
for (int k = 1; k < y->n; k++) {
@@ -516,7 +500,7 @@ static void pivot_right(MarkTree *b, mtnode_t *p, int i)
static void pivot_left(MarkTree *b, mtnode_t *p, int i)
{
- mtnode_t *x = p->ptr[i], *y = p->ptr[i+1];
+ mtnode_t *x = p->ptr[i], *y = p->ptr[i + 1];
// reverse from how we "always" do it. but pivot_left
// is just the inverse of pivot_right, so reverse it literally.
@@ -525,7 +509,7 @@ static void pivot_left(MarkTree *b, mtnode_t *p, int i)
}
unrelative(p->key[i].pos, &y->key[0].pos);
if (i > 0) {
- relative(p->key[i-1].pos, &p->key[i].pos);
+ relative(p->key[i - 1].pos, &p->key[i].pos);
}
x->key[x->n] = p->key[i];
@@ -533,10 +517,10 @@ static void pivot_left(MarkTree *b, mtnode_t *p, int i)
p->key[i] = y->key[0];
refkey(b, p, i);
if (x->level) {
- x->ptr[x->n+1] = y->ptr[0];
- x->ptr[x->n+1]->parent = x;
+ x->ptr[x->n + 1] = y->ptr[0];
+ x->ptr[x->n + 1]->parent = x;
}
- memmove(y->key, &y->key[1], (size_t)(y->n-1) * sizeof(mtkey_t));
+ memmove(y->key, &y->key[1], (size_t)(y->n - 1) * sizeof(mtkey_t));
if (y->level) {
memmove(y->ptr, &y->ptr[1], (size_t)y->n * sizeof(mtnode_t *));
}
@@ -562,7 +546,7 @@ void marktree_clear(MarkTree *b)
void marktree_free_node(mtnode_t *x)
{
if (x->level) {
- for (int i = 0; i < x->n+1; i++) {
+ for (int i = 0; i < x->n + 1; i++) {
marktree_free_node(x->ptr[i]);
}
}
@@ -570,30 +554,35 @@ 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)(rawkey(itr).flags & (uint16_t) ~MT_FLAG_DECOR_MASK);
+ rawkey(itr).flags = (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
}
// itr functions
// TODO(bfredl): static inline?
-bool marktree_itr_get(MarkTree *b, int row, int col, MarkTreeIter *itr)
+bool marktree_itr_get(MarkTree *b, int32_t row, int col, MarkTreeIter *itr)
{
return marktree_itr_get_ext(b, (mtpos_t){ row, col },
itr, false, false, NULL);
@@ -602,14 +591,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;
@@ -617,7 +607,7 @@ bool marktree_itr_get_ext(MarkTree *b, mtpos_t p, MarkTreeIter *itr, bool last,
oldbase[itr->lvl] = itr->pos;
}
while (true) {
- itr->i = marktree_getp_aux(itr->node, k, 0)+1;
+ itr->i = marktree_getp_aux(itr->node, k, 0) + 1;
if (itr->node->level == 0) {
break;
@@ -627,8 +617,8 @@ bool marktree_itr_get_ext(MarkTree *b, mtpos_t p, MarkTreeIter *itr, bool last,
itr->s[itr->lvl].oldcol = itr->pos.col;
if (itr->i > 0) {
- compose(&itr->pos, itr->node->key[itr->i-1].pos);
- relative(itr->node->key[itr->i-1].pos, &k.pos);
+ compose(&itr->pos, itr->node->key[itr->i - 1].pos);
+ relative(itr->node->key[itr->i - 1].pos, &k.pos);
}
itr->node = itr->node->ptr[itr->i];
itr->lvl++;
@@ -685,7 +675,7 @@ int marktree_itr_last(MarkTree *b, MarkTreeIter *itr)
itr->s[itr->lvl].oldcol = itr->pos.col;
assert(itr->i > 0);
- compose(&itr->pos, itr->node->key[itr->i-1].pos);
+ compose(&itr->pos, itr->node->key[itr->i - 1].pos);
itr->node = itr->node->ptr[itr->i];
itr->lvl++;
@@ -721,7 +711,7 @@ static bool marktree_itr_next_skip(MarkTree *b, MarkTreeIter *itr, bool skip, mt
itr->lvl--;
itr->i = itr->s[itr->lvl].i;
if (itr->i > 0) {
- itr->pos.row -= itr->node->key[itr->i-1].pos.row;
+ itr->pos.row -= itr->node->key[itr->i - 1].pos.row;
itr->pos.col = itr->s[itr->lvl].oldcol;
}
}
@@ -732,10 +722,10 @@ static bool marktree_itr_next_skip(MarkTree *b, MarkTreeIter *itr, bool skip, mt
// internal key, there is always a child after
if (itr->i > 0) {
itr->s[itr->lvl].oldcol = itr->pos.col;
- compose(&itr->pos, itr->node->key[itr->i-1].pos);
+ compose(&itr->pos, itr->node->key[itr->i - 1].pos);
}
if (oldbase && itr->i == 0) {
- oldbase[itr->lvl+1] = oldbase[itr->lvl];
+ oldbase[itr->lvl + 1] = oldbase[itr->lvl];
}
itr->s[itr->lvl].i = itr->i;
assert(itr->node->ptr[itr->i]->parent == itr->node);
@@ -766,7 +756,7 @@ bool marktree_itr_prev(MarkTree *b, MarkTreeIter *itr)
return false;
}
itr->lvl--;
- itr->i = itr->s[itr->lvl].i-1;
+ itr->i = itr->s[itr->lvl].i - 1;
if (itr->i >= 0) {
itr->pos.row -= itr->node->key[itr->i].pos.row;
itr->pos.col = itr->s[itr->lvl].oldcol;
@@ -779,7 +769,7 @@ bool marktree_itr_prev(MarkTree *b, MarkTreeIter *itr)
// internal key, there is always a child before
if (itr->i > 0) {
itr->s[itr->lvl].oldcol = itr->pos.col;
- compose(&itr->pos, itr->node->key[itr->i-1].pos);
+ compose(&itr->pos, itr->node->key[itr->i - 1].pos);
}
itr->s[itr->lvl].i = itr->i;
assert(itr->node->ptr[itr->i]->parent == itr->node);
@@ -805,10 +795,9 @@ void marktree_itr_rewind(MarkTree *b, MarkTreeIter *itr)
bool marktree_itr_node_done(MarkTreeIter *itr)
{
- return !itr->node || itr->i == itr->node->n-1;
+ return !itr->node || itr->i == itr->node->n - 1;
}
-
mtpos_t marktree_itr_pos(MarkTreeIter *itr)
{
mtpos_t pos = rawkey(itr).pos;
@@ -816,28 +805,32 @@ 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 void swap_id(uint64_t *id1, uint64_t *id2)
+static bool itr_eq(MarkTreeIter *itr1, MarkTreeIter *itr2)
{
- uint64_t temp = *id1;
- *id1 = *id2;
- *id2 = temp;
+ return (&rawkey(itr1) == &rawkey(itr2));
}
-bool marktree_splice(MarkTree *b, int start_line, int start_col, int old_extent_line,
+static void itr_swap(MarkTreeIter *itr1, MarkTreeIter *itr2)
+{
+ 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, int32_t start_line, int start_col, int old_extent_line,
int old_extent_col, int new_extent_line, int new_extent_col)
{
mtpos_t start = { start_line, start_col };
@@ -859,13 +852,13 @@ bool marktree_splice(MarkTree *b, int start_line, int start_col, int old_extent_
return false;
}
mtpos_t delta = { new_extent.row - old_extent.row,
- new_extent.col-old_extent.col };
+ new_extent.col - old_extent.col };
if (may_delete) {
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 +888,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,20 +904,20 @@ 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;
}
moved = true;
if (itr->node->level) {
- oldbase[itr->lvl+1] = rawkey(itr).pos;
- unrelative(oldbase[itr->lvl], &oldbase[itr->lvl+1]);
+ oldbase[itr->lvl + 1] = rawkey(itr).pos;
+ unrelative(oldbase[itr->lvl], &oldbase[itr->lvl + 1]);
rawkey(itr).pos = loc_start;
marktree_itr_next_skip(b, itr, false, oldbase);
} else {
rawkey(itr).pos = loc_start;
- if (itr->i < itr->node->n-1) {
+ if (itr->i < itr->node->n - 1) {
itr->i++;
if (!past_right) {
goto continue_same_node;
@@ -951,12 +944,12 @@ past_continue_same_node:
rawkey(itr).pos = loc_new;
moved = true;
if (itr->node->level) {
- oldbase[itr->lvl+1] = oldpos;
- unrelative(oldbase[itr->lvl], &oldbase[itr->lvl+1]);
+ oldbase[itr->lvl + 1] = oldpos;
+ unrelative(oldbase[itr->lvl], &oldbase[itr->lvl + 1]);
marktree_itr_next_skip(b, itr, false, oldbase);
} else {
- if (itr->i < itr->node->n-1) {
+ if (itr->i < itr->node->n - 1) {
itr->i++;
goto past_continue_same_node;
} else {
@@ -966,7 +959,6 @@ past_continue_same_node:
}
}
-
while (itr->node) {
unrelative(oldbase[itr->lvl], &rawkey(itr).pos);
int realrow = rawkey(itr).pos.row;
@@ -1006,13 +998,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 +1016,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;
@@ -1055,7 +1053,7 @@ found: {}
}
while (n->parent != NULL) {
mtnode_t *p = n->parent;
- for (i = 0; i < p->n+1; i++) {
+ for (i = 0; i < p->n + 1; i++) {
if (p->ptr[i] == n) {
goto found_node;
}
@@ -1063,17 +1061,31 @@ found: {}
abort();
found_node:
if (itr) {
- itr->s[b->root->level-p->level].i = i;
+ 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)
@@ -1084,7 +1096,7 @@ static void marktree_itr_fix_pos(MarkTree *b, MarkTreeIter *itr)
itr->s[lvl].oldcol = itr->pos.col;
int i = itr->s[lvl].i;
if (i > 0) {
- compose(&itr->pos, x->key[i-1].pos);
+ compose(&itr->pos, x->key[i - 1].pos);
}
assert(x->level);
x = x->ptr[i];
@@ -1092,6 +1104,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
@@ -1118,7 +1144,7 @@ static size_t check_node(MarkTree *b, mtnode_t *x, mtpos_t *last, bool *last_rig
{
assert(x->n <= 2 * T - 1);
// TODO(bfredl): too strict if checking "in repair" post-delete tree.
- assert(x->n >= (x != b->root ? T-1 : 0));
+ assert(x->n >= (x != b->root ? T - 1 : 0));
size_t n_keys = (size_t)x->n;
for (int i = 0; i < x->n; i++) {
@@ -1128,33 +1154,31 @@ static size_t check_node(MarkTree *b, mtnode_t *x, mtpos_t *last, bool *last_rig
*last = (mtpos_t) { 0, 0 };
}
if (i > 0) {
- unrelative(x->key[i-1].pos, last);
- }
- if (x->level) {
+ unrelative(x->key[i - 1].pos, last);
}
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) {
n_keys += check_node(b, x->ptr[x->n], last, last_right);
- unrelative(x->key[x->n-1].pos, last);
+ unrelative(x->key[x->n - 1].pos, last);
- for (int i = 0; i < x->n+1; i++) {
+ for (int i = 0; i < x->n + 1; i++) {
assert(x->ptr[i]->parent == x);
- assert(x->ptr[i]->level == x->level-1);
+ assert(x->ptr[i]->level == x->level - 1);
// PARANOIA: check no double node ref
for (int j = 0; j < i; j++) {
assert(x->ptr[i] != x->ptr[j]);
}
}
} else {
- *last = x->key[x->n-1].pos;
+ *last = x->key[x->n - 1].pos;
}
return n_keys;
}
@@ -1182,11 +1206,10 @@ void mt_inspect_node(MarkTree *b, garray_T *ga, mtnode_t *n, mtpos_t off)
snprintf((char *)buf, sizeof(buf), "%d/%d", p.row, p.col);
ga_concat(ga, buf);
if (n->level) {
- mt_inspect_node(b, ga, n->ptr[i+1], p);
+ mt_inspect_node(b, ga, n->ptr[i + 1], p);
} else {
ga_concat(ga, ",");
}
}
ga_concat(ga, "]");
}
-
diff --git a/src/nvim/marktree.h b/src/nvim/marktree.h
index a1dcdf5164..e2e05eebd5 100644
--- a/src/nvim/marktree.h
+++ b/src/nvim/marktree.h
@@ -1,11 +1,14 @@
#ifndef NVIM_MARKTREE_H
#define NVIM_MARKTREE_H
+#include <assert.h>
#include <stdint.h>
+#include "nvim/assert.h"
#include "nvim/garray.h"
#include "nvim/map.h"
#include "nvim/pos.h"
+#include "nvim/types.h"
#define MT_MAX_DEPTH 20
#define MT_BRANCH_FACTOR 10
@@ -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;
@@ -36,15 +32,81 @@ typedef struct {
iterstate_t s[MT_MAX_DEPTH];
} MarkTreeIter;
-
// 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_start(mtkey_t key)
+{
+ return mt_paired(key) && !mt_end(key);
+}
+
+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,27 +123,13 @@ 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];
} MarkTree;
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# 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/match.c b/src/nvim/match.c
new file mode 100644
index 0000000000..e17a95569c
--- /dev/null
+++ b/src/nvim/match.c
@@ -0,0 +1,1213 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+// match.c: functions for highlighting matches
+
+#include <stdbool.h>
+
+#include "nvim/buffer_defs.h"
+#include "nvim/charset.h"
+#include "nvim/fold.h"
+#include "nvim/highlight_group.h"
+#include "nvim/match.h"
+#include "nvim/memline.h"
+#include "nvim/regexp.h"
+#include "nvim/runtime.h"
+#include "nvim/screen.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "match.c.generated.h"
+#endif
+
+static char *e_invalwindow = N_("E957: Invalid window number");
+
+#define SEARCH_HL_PRIORITY 0
+
+/// Add match to the match list of window 'wp'. The pattern 'pat' will be
+/// highlighted with the group 'grp' with priority 'prio'.
+/// Optionally, a desired ID 'id' can be specified (greater than or equal to 1).
+///
+/// @param[in] id a desired ID 'id' can be specified
+/// (greater than or equal to 1). -1 must be specified if no
+/// particular ID is desired
+/// @param[in] conceal_char pointer to conceal replacement char
+/// @return ID of added match, -1 on failure.
+static int match_add(win_T *wp, const char *const grp, const char *const pat, int prio, int id,
+ list_T *pos_list, const char *const conceal_char)
+ FUNC_ATTR_NONNULL_ARG(1, 2)
+{
+ matchitem_T *cur;
+ matchitem_T *prev;
+ matchitem_T *m;
+ int hlg_id;
+ regprog_T *regprog = NULL;
+ int rtype = SOME_VALID;
+
+ if (*grp == NUL || (pat != NULL && *pat == NUL)) {
+ return -1;
+ }
+ if (id < -1 || id == 0) {
+ semsg(_("E799: Invalid ID: %" PRId64
+ " (must be greater than or equal to 1)"),
+ (int64_t)id);
+ return -1;
+ }
+ if (id != -1) {
+ cur = wp->w_match_head;
+ while (cur != NULL) {
+ if (cur->id == id) {
+ semsg(_("E801: ID already taken: %" PRId64), (int64_t)id);
+ return -1;
+ }
+ cur = cur->next;
+ }
+ }
+ if ((hlg_id = syn_check_group(grp, strlen(grp))) == 0) {
+ return -1;
+ }
+ if (pat != NULL && (regprog = vim_regcomp((char *)pat, RE_MAGIC)) == NULL) {
+ semsg(_(e_invarg2), pat);
+ return -1;
+ }
+
+ // Find available match ID.
+ while (id == -1) {
+ cur = wp->w_match_head;
+ while (cur != NULL && cur->id != wp->w_next_match_id) {
+ cur = cur->next;
+ }
+ if (cur == NULL) {
+ id = wp->w_next_match_id;
+ }
+ wp->w_next_match_id++;
+ }
+
+ // Build new match.
+ m = xcalloc(1, sizeof(matchitem_T));
+ m->id = id;
+ m->priority = prio;
+ m->pattern = pat == NULL ? NULL: xstrdup(pat);
+ m->hlg_id = hlg_id;
+ m->match.regprog = regprog;
+ m->match.rmm_ic = false;
+ m->match.rmm_maxcol = 0;
+ m->conceal_char = 0;
+ if (conceal_char != NULL) {
+ m->conceal_char = utf_ptr2char(conceal_char);
+ }
+
+ // Set up position matches
+ if (pos_list != NULL) {
+ linenr_T toplnum = 0;
+ linenr_T botlnum = 0;
+
+ int i = 0;
+ TV_LIST_ITER(pos_list, li, {
+ linenr_T lnum = 0;
+ colnr_T col = 0;
+ int len = 1;
+ bool error = false;
+
+ if (TV_LIST_ITEM_TV(li)->v_type == VAR_LIST) {
+ const list_T *const subl = TV_LIST_ITEM_TV(li)->vval.v_list;
+ const listitem_T *subli = tv_list_first(subl);
+ if (subli == NULL) {
+ semsg(_("E5030: Empty list at position %d"),
+ (int)tv_list_idx_of_item(pos_list, li));
+ goto fail;
+ }
+ lnum = (linenr_T)tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error);
+ if (error) {
+ goto fail;
+ }
+ if (lnum <= 0) {
+ continue;
+ }
+ m->pos.pos[i].lnum = lnum;
+ subli = TV_LIST_ITEM_NEXT(subl, subli);
+ if (subli != NULL) {
+ col = (colnr_T)tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error);
+ if (error) {
+ goto fail;
+ }
+ if (col < 0) {
+ continue;
+ }
+ subli = TV_LIST_ITEM_NEXT(subl, subli);
+ if (subli != NULL) {
+ len = (colnr_T)tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error);
+ if (len < 0) {
+ continue;
+ }
+ if (error) {
+ goto fail;
+ }
+ }
+ }
+ m->pos.pos[i].col = col;
+ m->pos.pos[i].len = len;
+ } else if (TV_LIST_ITEM_TV(li)->v_type == VAR_NUMBER) {
+ if (TV_LIST_ITEM_TV(li)->vval.v_number <= 0) {
+ continue;
+ }
+ m->pos.pos[i].lnum = (linenr_T)TV_LIST_ITEM_TV(li)->vval.v_number;
+ m->pos.pos[i].col = 0;
+ m->pos.pos[i].len = 0;
+ } else {
+ semsg(_("E5031: List or number required at position %d"),
+ (int)tv_list_idx_of_item(pos_list, li));
+ goto fail;
+ }
+ if (toplnum == 0 || lnum < toplnum) {
+ toplnum = lnum;
+ }
+ if (botlnum == 0 || lnum >= botlnum) {
+ botlnum = lnum + 1;
+ }
+ i++;
+ if (i >= MAXPOSMATCH) {
+ break;
+ }
+ });
+
+ // Calculate top and bottom lines for redrawing area
+ if (toplnum != 0) {
+ if (wp->w_buffer->b_mod_set) {
+ if (wp->w_buffer->b_mod_top > toplnum) {
+ wp->w_buffer->b_mod_top = toplnum;
+ }
+ if (wp->w_buffer->b_mod_bot < botlnum) {
+ wp->w_buffer->b_mod_bot = botlnum;
+ }
+ } else {
+ wp->w_buffer->b_mod_set = true;
+ wp->w_buffer->b_mod_top = toplnum;
+ wp->w_buffer->b_mod_bot = botlnum;
+ wp->w_buffer->b_mod_xlines = 0;
+ }
+ m->pos.toplnum = toplnum;
+ m->pos.botlnum = botlnum;
+ rtype = VALID;
+ }
+ }
+
+ // Insert new match. The match list is in ascending order with regard to
+ // the match priorities.
+ cur = wp->w_match_head;
+ prev = cur;
+ while (cur != NULL && prio >= cur->priority) {
+ prev = cur;
+ cur = cur->next;
+ }
+ if (cur == prev) {
+ wp->w_match_head = m;
+ } else {
+ prev->next = m;
+ }
+ m->next = cur;
+
+ redraw_later(wp, rtype);
+ return id;
+
+fail:
+ xfree(m);
+ return -1;
+}
+
+/// Delete match with ID 'id' in the match list of window 'wp'.
+///
+/// @param perr print error messages if true.
+static int match_delete(win_T *wp, int id, bool perr)
+{
+ matchitem_T *cur = wp->w_match_head;
+ matchitem_T *prev = cur;
+ int rtype = SOME_VALID;
+
+ if (id < 1) {
+ if (perr) {
+ semsg(_("E802: Invalid ID: %" PRId64
+ " (must be greater than or equal to 1)"),
+ (int64_t)id);
+ }
+ return -1;
+ }
+ while (cur != NULL && cur->id != id) {
+ prev = cur;
+ cur = cur->next;
+ }
+ if (cur == NULL) {
+ if (perr) {
+ semsg(_("E803: ID not found: %" PRId64), (int64_t)id);
+ }
+ return -1;
+ }
+ if (cur == prev) {
+ wp->w_match_head = cur->next;
+ } else {
+ prev->next = cur->next;
+ }
+ vim_regfree(cur->match.regprog);
+ xfree(cur->pattern);
+ if (cur->pos.toplnum != 0) {
+ if (wp->w_buffer->b_mod_set) {
+ if (wp->w_buffer->b_mod_top > cur->pos.toplnum) {
+ wp->w_buffer->b_mod_top = cur->pos.toplnum;
+ }
+ if (wp->w_buffer->b_mod_bot < cur->pos.botlnum) {
+ wp->w_buffer->b_mod_bot = cur->pos.botlnum;
+ }
+ } else {
+ wp->w_buffer->b_mod_set = true;
+ wp->w_buffer->b_mod_top = cur->pos.toplnum;
+ wp->w_buffer->b_mod_bot = cur->pos.botlnum;
+ wp->w_buffer->b_mod_xlines = 0;
+ }
+ rtype = VALID;
+ }
+ xfree(cur);
+ redraw_later(wp, rtype);
+ return 0;
+}
+
+/// Delete all matches in the match list of window 'wp'.
+void clear_matches(win_T *wp)
+{
+ matchitem_T *m;
+
+ while (wp->w_match_head != NULL) {
+ m = wp->w_match_head->next;
+ vim_regfree(wp->w_match_head->match.regprog);
+ xfree(wp->w_match_head->pattern);
+ xfree(wp->w_match_head);
+ wp->w_match_head = m;
+ }
+ redraw_later(wp, SOME_VALID);
+}
+
+/// Get match from ID 'id' in window 'wp'.
+/// Return NULL if match not found.
+matchitem_T *get_match(win_T *wp, int id)
+{
+ matchitem_T *cur = wp->w_match_head;
+
+ while (cur != NULL && cur->id != id) {
+ cur = cur->next;
+ }
+ return cur;
+}
+
+/// Init for calling prepare_search_hl().
+void init_search_hl(win_T *wp, match_T *search_hl)
+ FUNC_ATTR_NONNULL_ALL
+{
+ // Setup for match and 'hlsearch' highlighting. Disable any previous
+ // match
+ matchitem_T *cur = wp->w_match_head;
+ while (cur != NULL) {
+ cur->hl.rm = cur->match;
+ if (cur->hlg_id == 0) {
+ cur->hl.attr = 0;
+ } else {
+ cur->hl.attr = syn_id2attr(cur->hlg_id);
+ }
+ cur->hl.buf = wp->w_buffer;
+ cur->hl.lnum = 0;
+ cur->hl.first_lnum = 0;
+ // Set the time limit to 'redrawtime'.
+ cur->hl.tm = profile_setlimit(p_rdt);
+ cur = cur->next;
+ }
+ search_hl->buf = wp->w_buffer;
+ search_hl->lnum = 0;
+ search_hl->first_lnum = 0;
+ search_hl->attr = win_hl_attr(wp, HLF_L);
+
+ // time limit is set at the toplevel, for all windows
+}
+
+/// @param shl points to a match. Fill on match.
+/// @param posmatch match positions
+/// @param mincol minimal column for a match
+///
+/// @return one on match, otherwise return zero.
+static int next_search_hl_pos(match_T *shl, linenr_T lnum, posmatch_T *posmatch, colnr_T mincol)
+ FUNC_ATTR_NONNULL_ALL
+{
+ int i;
+ int found = -1;
+
+ shl->lnum = 0;
+ for (i = posmatch->cur; i < MAXPOSMATCH; i++) {
+ llpos_T *pos = &posmatch->pos[i];
+
+ if (pos->lnum == 0) {
+ break;
+ }
+ if (pos->len == 0 && pos->col < mincol) {
+ continue;
+ }
+ if (pos->lnum == lnum) {
+ if (found >= 0) {
+ // if this match comes before the one at "found" then swap
+ // them
+ if (pos->col < posmatch->pos[found].col) {
+ llpos_T tmp = *pos;
+
+ *pos = posmatch->pos[found];
+ posmatch->pos[found] = tmp;
+ }
+ } else {
+ found = i;
+ }
+ }
+ }
+ posmatch->cur = 0;
+ if (found >= 0) {
+ colnr_T start = posmatch->pos[found].col == 0
+ ? 0: posmatch->pos[found].col - 1;
+ colnr_T end = posmatch->pos[found].col == 0
+ ? MAXCOL : start + posmatch->pos[found].len;
+
+ shl->lnum = lnum;
+ shl->rm.startpos[0].lnum = 0;
+ shl->rm.startpos[0].col = start;
+ shl->rm.endpos[0].lnum = 0;
+ shl->rm.endpos[0].col = end;
+ shl->is_addpos = true;
+ shl->has_cursor = false;
+ posmatch->cur = found + 1;
+ return 1;
+ }
+ return 0;
+}
+
+/// Search for a next 'hlsearch' or match.
+/// Uses shl->buf.
+/// Sets shl->lnum and shl->rm contents.
+/// Note: Assumes a previous match is always before "lnum", unless
+/// shl->lnum is zero.
+/// Careful: Any pointers for buffer lines will become invalid.
+///
+/// @param shl points to search_hl or a match
+/// @param mincol minimal column for a match
+/// @param cur to retrieve match positions if any
+static void next_search_hl(win_T *win, match_T *search_hl, match_T *shl, linenr_T lnum,
+ colnr_T mincol, matchitem_T *cur)
+ FUNC_ATTR_NONNULL_ARG(2)
+{
+ linenr_T l;
+ colnr_T matchcol;
+ long nmatched = 0;
+ const int called_emsg_before = called_emsg;
+
+ // for :{range}s/pat only highlight inside the range
+ if (lnum < search_first_line || lnum > search_last_line) {
+ shl->lnum = 0;
+ return;
+ }
+
+ if (shl->lnum != 0) {
+ // Check for three situations:
+ // 1. If the "lnum" is below a previous match, start a new search.
+ // 2. If the previous match includes "mincol", use it.
+ // 3. Continue after the previous match.
+ l = shl->lnum + shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum;
+ if (lnum > l) {
+ shl->lnum = 0;
+ } else if (lnum < l || shl->rm.endpos[0].col > mincol) {
+ return;
+ }
+ }
+
+ // Repeat searching for a match until one is found that includes "mincol"
+ // or none is found in this line.
+ for (;;) {
+ // Stop searching after passing the time limit.
+ if (profile_passed_limit(shl->tm)) {
+ shl->lnum = 0; // no match found in time
+ break;
+ }
+ // Three situations:
+ // 1. No useful previous match: search from start of line.
+ // 2. Not Vi compatible or empty match: continue at next character.
+ // Break the loop if this is beyond the end of the line.
+ // 3. Vi compatible searching: continue at end of previous match.
+ if (shl->lnum == 0) {
+ matchcol = 0;
+ } else if (vim_strchr(p_cpo, CPO_SEARCH) == NULL
+ || (shl->rm.endpos[0].lnum == 0
+ && shl->rm.endpos[0].col <= shl->rm.startpos[0].col)) {
+ char_u *ml;
+
+ matchcol = shl->rm.startpos[0].col;
+ ml = ml_get_buf(shl->buf, lnum, false) + matchcol;
+ if (*ml == NUL) {
+ matchcol++;
+ shl->lnum = 0;
+ break;
+ }
+ matchcol += utfc_ptr2len((char *)ml);
+ } else {
+ matchcol = shl->rm.endpos[0].col;
+ }
+
+ shl->lnum = lnum;
+ if (shl->rm.regprog != NULL) {
+ // Remember whether shl->rm is using a copy of the regprog in
+ // cur->match.
+ bool regprog_is_copy = (shl != search_hl
+ && cur != NULL
+ && shl == &cur->hl
+ && cur->match.regprog == cur->hl.rm.regprog);
+ int timed_out = false;
+
+ nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum, matchcol,
+ &(shl->tm), &timed_out);
+ // Copy the regprog, in case it got freed and recompiled.
+ if (regprog_is_copy) {
+ cur->match.regprog = cur->hl.rm.regprog;
+ }
+ if (called_emsg > called_emsg_before || got_int || timed_out) {
+ // Error while handling regexp: stop using this regexp.
+ if (shl == search_hl) {
+ // don't free regprog in the match list, it's a copy
+ vim_regfree(shl->rm.regprog);
+ set_no_hlsearch(true);
+ }
+ shl->rm.regprog = NULL;
+ shl->lnum = 0;
+ got_int = false; // avoid the "Type :quit to exit Vim" message
+ break;
+ }
+ } else if (cur != NULL) {
+ nmatched = next_search_hl_pos(shl, lnum, &(cur->pos), matchcol);
+ }
+ if (nmatched == 0) {
+ shl->lnum = 0; // no match found
+ break;
+ }
+ if (shl->rm.startpos[0].lnum > 0
+ || shl->rm.startpos[0].col >= mincol
+ || nmatched > 1
+ || shl->rm.endpos[0].col > mincol) {
+ shl->lnum += shl->rm.startpos[0].lnum;
+ break; // useful match found
+ }
+ }
+}
+
+/// Advance to the match in window "wp" line "lnum" or past it.
+void prepare_search_hl(win_T *wp, match_T *search_hl, linenr_T lnum)
+ FUNC_ATTR_NONNULL_ALL
+{
+ matchitem_T *cur; // points to the match list
+ match_T *shl; // points to search_hl or a match
+ bool shl_flag; // flag to indicate whether search_hl
+ // has been processed or not
+
+ // When using a multi-line pattern, start searching at the top
+ // of the window or just after a closed fold.
+ // Do this both for search_hl and the match list.
+ cur = wp->w_match_head;
+ shl_flag = false;
+ while (cur != NULL || shl_flag == false) {
+ if (shl_flag == false) {
+ shl = search_hl;
+ shl_flag = true;
+ } else {
+ shl = &cur->hl; // -V595
+ }
+ if (shl->rm.regprog != NULL
+ && shl->lnum == 0
+ && re_multiline(shl->rm.regprog)) {
+ if (shl->first_lnum == 0) {
+ for (shl->first_lnum = lnum;
+ shl->first_lnum > wp->w_topline;
+ shl->first_lnum--) {
+ if (hasFoldingWin(wp, shl->first_lnum - 1, NULL, NULL, true, NULL)) {
+ break;
+ }
+ }
+ }
+ if (cur != NULL) {
+ cur->pos.cur = 0;
+ }
+ bool pos_inprogress = true; // mark that a position match search is
+ // in progress
+ int n = 0;
+ while (shl->first_lnum < lnum && (shl->rm.regprog != NULL
+ || (cur != NULL && pos_inprogress))) {
+ next_search_hl(wp, search_hl, shl, shl->first_lnum, (colnr_T)n,
+ shl == search_hl ? NULL : cur);
+ pos_inprogress = !(cur == NULL || cur->pos.cur == 0);
+ if (shl->lnum != 0) {
+ shl->first_lnum = shl->lnum
+ + shl->rm.endpos[0].lnum
+ - shl->rm.startpos[0].lnum;
+ n = shl->rm.endpos[0].col;
+ } else {
+ shl->first_lnum++;
+ n = 0;
+ }
+ }
+ }
+ if (shl != search_hl && cur != NULL) {
+ cur = cur->next;
+ }
+ }
+}
+
+/// Update "shl->has_cursor" based on the match in "shl" and the cursor
+/// position.
+static void check_cur_search_hl(win_T *wp, match_T *shl)
+{
+ linenr_T linecount = shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum;
+
+ if (wp->w_cursor.lnum >= shl->lnum
+ && wp->w_cursor.lnum <= shl->lnum + linecount
+ && (wp->w_cursor.lnum > shl->lnum || wp->w_cursor.col >= shl->rm.startpos[0].col)
+ && (wp->w_cursor.lnum < shl->lnum + linecount || wp->w_cursor.col < shl->rm.endpos[0].col)) {
+ shl->has_cursor = true;
+ } else {
+ shl->has_cursor = false;
+ }
+}
+
+/// Prepare for 'hlsearch' and match highlighting in one window line.
+/// Return true if there is such highlighting and set "search_attr" to the
+/// current highlight attribute.
+bool prepare_search_hl_line(win_T *wp, linenr_T lnum, colnr_T mincol, char_u **line,
+ match_T *search_hl, int *search_attr, bool *search_attr_from_match)
+{
+ matchitem_T *cur = wp->w_match_head; // points to the match list
+ match_T *shl; // points to search_hl or a match
+ bool shl_flag = false; // flag to indicate whether search_hl
+ // has been processed or not
+ bool area_highlighting = false;
+
+ // Handle highlighting the last used search pattern and matches.
+ // Do this for both search_hl and the match list.
+ while (cur != NULL || !shl_flag) {
+ if (!shl_flag) {
+ shl = search_hl;
+ shl_flag = true;
+ } else {
+ shl = &cur->hl; // -V595
+ }
+ shl->startcol = MAXCOL;
+ shl->endcol = MAXCOL;
+ shl->attr_cur = 0;
+ shl->is_addpos = false;
+ shl->has_cursor = false;
+ if (cur != NULL) {
+ cur->pos.cur = 0;
+ }
+ next_search_hl(wp, search_hl, shl, lnum, mincol,
+ shl == search_hl ? NULL : cur);
+
+ // Need to get the line again, a multi-line regexp may have made it
+ // invalid.
+ *line = ml_get_buf(wp->w_buffer, lnum, false);
+
+ if (shl->lnum != 0 && shl->lnum <= lnum) {
+ if (shl->lnum == lnum) {
+ shl->startcol = shl->rm.startpos[0].col;
+ } else {
+ shl->startcol = 0;
+ }
+ if (lnum == shl->lnum + shl->rm.endpos[0].lnum
+ - shl->rm.startpos[0].lnum) {
+ shl->endcol = shl->rm.endpos[0].col;
+ } else {
+ shl->endcol = MAXCOL;
+ }
+
+ // check if the cursor is in the match before changing the columns
+ if (shl == search_hl) {
+ check_cur_search_hl(wp, shl);
+ }
+
+ // Highlight one character for an empty match.
+ if (shl->startcol == shl->endcol) {
+ if ((*line)[shl->endcol] != NUL) {
+ shl->endcol += utfc_ptr2len((char *)(*line) + shl->endcol);
+ } else {
+ shl->endcol++;
+ }
+ }
+ if ((long)shl->startcol < mincol) { // match at leftcol
+ shl->attr_cur = shl->attr;
+ *search_attr = shl->attr;
+ *search_attr_from_match = shl != search_hl;
+ }
+ area_highlighting = true;
+ }
+ if (shl != search_hl && cur != NULL) {
+ cur = cur->next;
+ }
+ }
+ return area_highlighting;
+}
+
+/// For a position in a line: Check for start/end of 'hlsearch' and other
+/// matches.
+/// After end, check for start/end of next match.
+/// When another match, have to check for start again.
+/// Watch out for matching an empty string!
+/// Return the updated search_attr.
+int update_search_hl(win_T *wp, linenr_T lnum, colnr_T col, char_u **line, match_T *search_hl,
+ int *has_match_conc, int *match_conc, int lcs_eol_one,
+ bool *search_attr_from_match)
+{
+ matchitem_T *cur = wp->w_match_head; // points to the match list
+ match_T *shl; // points to search_hl or a match
+ bool shl_flag = false; // flag to indicate whether search_hl
+ // has been processed or not
+ int search_attr = 0;
+
+ // Do this for 'search_hl' and the match list (ordered by priority).
+ while (cur != NULL || !shl_flag) {
+ if (!shl_flag
+ && (cur == NULL || cur->priority > SEARCH_HL_PRIORITY)) {
+ shl = search_hl;
+ shl_flag = true;
+ } else {
+ shl = &cur->hl;
+ }
+ if (cur != NULL) {
+ cur->pos.cur = 0;
+ }
+ bool pos_inprogress = true; // mark that a position match search is
+ // in progress
+ while (shl->rm.regprog != NULL
+ || (cur != NULL && pos_inprogress)) {
+ if (shl->startcol != MAXCOL
+ && col >= shl->startcol
+ && col < shl->endcol) {
+ int next_col = col + utfc_ptr2len((char *)(*line) + col);
+
+ if (shl->endcol < next_col) {
+ shl->endcol = next_col;
+ }
+ // Highlight the match were the cursor is using the CurSearch
+ // group.
+ if (shl == search_hl && shl->has_cursor && (HL_ATTR(HLF_LC) || wp->w_hl_ids[HLF_LC])) {
+ shl->attr_cur = win_hl_attr(wp, HLF_LC) ? win_hl_attr(wp, HLF_LC) : HL_ATTR(HLF_LC);
+ } else {
+ shl->attr_cur = shl->attr;
+ }
+ // Match with the "Conceal" group results in hiding
+ // the match.
+ if (cur != NULL
+ && shl != search_hl
+ && syn_name2id("Conceal") == cur->hlg_id) {
+ *has_match_conc = col == shl->startcol ? 2 : 1;
+ *match_conc = cur->conceal_char;
+ } else {
+ *has_match_conc = 0;
+ }
+ } else if (col == shl->endcol) {
+ shl->attr_cur = 0;
+
+ next_search_hl(wp, search_hl, shl, lnum, col,
+ shl == search_hl ? NULL : cur);
+ pos_inprogress = !(cur == NULL || cur->pos.cur == 0);
+
+ // Need to get the line again, a multi-line regexp
+ // may have made it invalid.
+ *line = ml_get_buf(wp->w_buffer, lnum, false);
+
+ if (shl->lnum == lnum) {
+ shl->startcol = shl->rm.startpos[0].col;
+ if (shl->rm.endpos[0].lnum == 0) {
+ shl->endcol = shl->rm.endpos[0].col;
+ } else {
+ shl->endcol = MAXCOL;
+ }
+
+ // check if the cursor is in the match
+ if (shl == search_hl) {
+ check_cur_search_hl(wp, shl);
+ }
+
+ if (shl->startcol == shl->endcol) {
+ // highlight empty match, try again after it
+ shl->endcol += utfc_ptr2len((char *)(*line) + shl->endcol);
+ }
+
+ // Loop to check if the match starts at the
+ // current position
+ continue;
+ }
+ }
+ break;
+ }
+ if (shl != search_hl && cur != NULL) {
+ cur = cur->next;
+ }
+ }
+
+ // Use attributes from match with highest priority among
+ // 'search_hl' and the match list.
+ *search_attr_from_match = false;
+ search_attr = search_hl->attr_cur;
+ cur = wp->w_match_head;
+ shl_flag = false;
+ while (cur != NULL || !shl_flag) {
+ if (!shl_flag
+ && (cur == NULL || cur->priority > SEARCH_HL_PRIORITY)) {
+ shl = search_hl;
+ shl_flag = true;
+ } else {
+ shl = &cur->hl;
+ }
+ if (shl->attr_cur != 0) {
+ search_attr = shl->attr_cur;
+ *search_attr_from_match = shl != search_hl;
+ }
+ if (shl != search_hl && cur != NULL) {
+ cur = cur->next;
+ }
+ }
+ // Only highlight one character after the last column.
+ if (*(*line + col) == NUL && (wp->w_p_list && lcs_eol_one == -1)) {
+ search_attr = 0;
+ }
+ return search_attr;
+}
+
+bool get_prevcol_hl_flag(win_T *wp, match_T *search_hl, long curcol)
+{
+ long prevcol = curcol;
+ matchitem_T *cur; // points to the match list
+
+ // we're not really at that column when skipping some text
+ if ((long)(wp->w_p_wrap ? wp->w_skipcol : wp->w_leftcol) > prevcol) {
+ prevcol++;
+ }
+
+ if (!search_hl->is_addpos && prevcol == search_hl->startcol) {
+ return true;
+ } else {
+ cur = wp->w_match_head;
+ while (cur != NULL) {
+ if (!cur->hl.is_addpos && prevcol == cur->hl.startcol) {
+ return true;
+ }
+ cur = cur->next;
+ }
+ }
+ return false;
+}
+
+/// Get highlighting for the char after the text in "char_attr" from 'hlsearch'
+/// or match highlighting.
+void get_search_match_hl(win_T *wp, match_T *search_hl, long col, int *char_attr)
+{
+ matchitem_T *cur = wp->w_match_head; // points to the match list
+ match_T *shl; // points to search_hl or a match
+ bool shl_flag = false; // flag to indicate whether search_hl
+ // has been processed or not
+
+ *char_attr = search_hl->attr;
+ while (cur != NULL || !shl_flag) {
+ if (!shl_flag
+ && (cur == NULL || cur->priority > SEARCH_HL_PRIORITY)) {
+ shl = search_hl;
+ shl_flag = true;
+ } else {
+ shl = &cur->hl;
+ }
+ if (col - 1 == (long)shl->startcol
+ && (shl == search_hl || !shl->is_addpos)) {
+ *char_attr = shl->attr;
+ }
+ if (shl != search_hl && cur != NULL) {
+ cur = cur->next;
+ }
+ }
+}
+
+static int matchadd_dict_arg(typval_T *tv, const char **conceal_char, win_T **win)
+{
+ dictitem_T *di;
+
+ if (tv->v_type != VAR_DICT) {
+ emsg(_(e_dictreq));
+ return FAIL;
+ }
+
+ if ((di = tv_dict_find(tv->vval.v_dict, S_LEN("conceal"))) != NULL) {
+ *conceal_char = tv_get_string(&di->di_tv);
+ }
+
+ if ((di = tv_dict_find(tv->vval.v_dict, S_LEN("window"))) != NULL) {
+ *win = find_win_by_nr_or_id(&di->di_tv);
+ if (*win == NULL) {
+ emsg(_(e_invalwindow));
+ return FAIL;
+ }
+ }
+
+ return OK;
+}
+
+/// "clearmatches()" function
+void f_clearmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ win_T *win = get_optional_window(argvars, 0);
+
+ if (win != NULL) {
+ clear_matches(win);
+ }
+}
+
+/// "getmatches()" function
+void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ matchitem_T *cur;
+ int i;
+ win_T *win = get_optional_window(argvars, 0);
+
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
+ if (win == NULL) {
+ return;
+ }
+
+ cur = win->w_match_head;
+ while (cur != NULL) {
+ dict_T *dict = tv_dict_alloc();
+ if (cur->match.regprog == NULL) {
+ // match added with matchaddpos()
+ for (i = 0; i < MAXPOSMATCH; i++) {
+ llpos_T *llpos;
+ char buf[30]; // use 30 to avoid compiler warning
+
+ llpos = &cur->pos.pos[i];
+ if (llpos->lnum == 0) {
+ break;
+ }
+ list_T *const l = tv_list_alloc(1 + (llpos->col > 0 ? 2 : 0));
+ tv_list_append_number(l, (varnumber_T)llpos->lnum);
+ if (llpos->col > 0) {
+ tv_list_append_number(l, (varnumber_T)llpos->col);
+ tv_list_append_number(l, (varnumber_T)llpos->len);
+ }
+ int len = snprintf(buf, sizeof(buf), "pos%d", i + 1);
+ assert((size_t)len < sizeof(buf));
+ tv_dict_add_list(dict, buf, (size_t)len, l);
+ }
+ } else {
+ tv_dict_add_str(dict, S_LEN("pattern"), (const char *)cur->pattern);
+ }
+ tv_dict_add_str(dict, S_LEN("group"),
+ (const char *)syn_id2name(cur->hlg_id));
+ tv_dict_add_nr(dict, S_LEN("priority"), (varnumber_T)cur->priority);
+ tv_dict_add_nr(dict, S_LEN("id"), (varnumber_T)cur->id);
+
+ if (cur->conceal_char) {
+ char buf[MB_MAXBYTES + 1];
+
+ buf[utf_char2bytes(cur->conceal_char, buf)] = NUL;
+ tv_dict_add_str(dict, S_LEN("conceal"), buf);
+ }
+
+ tv_list_append_dict(rettv->vval.v_list, dict);
+ cur = cur->next;
+ }
+}
+
+/// "setmatches()" function
+void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ dict_T *d;
+ list_T *s = NULL;
+ win_T *win = get_optional_window(argvars, 1);
+
+ rettv->vval.v_number = -1;
+ if (argvars[0].v_type != VAR_LIST) {
+ emsg(_(e_listreq));
+ return;
+ }
+ if (win == NULL) {
+ return;
+ }
+
+ list_T *const l = argvars[0].vval.v_list;
+ // To some extent make sure that we are dealing with a list from
+ // "getmatches()".
+ int li_idx = 0;
+ TV_LIST_ITER_CONST(l, li, {
+ if (TV_LIST_ITEM_TV(li)->v_type != VAR_DICT
+ || (d = TV_LIST_ITEM_TV(li)->vval.v_dict) == NULL) {
+ semsg(_("E474: List item %d is either not a dictionary "
+ "or an empty one"), li_idx);
+ return;
+ }
+ if (!(tv_dict_find(d, S_LEN("group")) != NULL
+ && (tv_dict_find(d, S_LEN("pattern")) != NULL
+ || tv_dict_find(d, S_LEN("pos1")) != NULL)
+ && tv_dict_find(d, S_LEN("priority")) != NULL
+ && tv_dict_find(d, S_LEN("id")) != NULL)) {
+ semsg(_("E474: List item %d is missing one of the required keys"),
+ li_idx);
+ return;
+ }
+ li_idx++;
+ });
+
+ clear_matches(win);
+ bool match_add_failed = false;
+ TV_LIST_ITER_CONST(l, li, {
+ int i = 0;
+
+ d = TV_LIST_ITEM_TV(li)->vval.v_dict;
+ dictitem_T *const di = tv_dict_find(d, S_LEN("pattern"));
+ if (di == NULL) {
+ if (s == NULL) {
+ s = tv_list_alloc(9);
+ }
+
+ // match from matchaddpos()
+ for (i = 1; i < 9; i++) {
+ char buf[30]; // use 30 to avoid compiler warning
+ snprintf(buf, sizeof(buf), "pos%d", i);
+ dictitem_T *const pos_di = tv_dict_find(d, buf, -1);
+ if (pos_di != NULL) {
+ if (pos_di->di_tv.v_type != VAR_LIST) {
+ return;
+ }
+
+ tv_list_append_tv(s, &pos_di->di_tv);
+ tv_list_ref(s);
+ } else {
+ break;
+ }
+ }
+ }
+
+ // Note: there are three number buffers involved:
+ // - group_buf below.
+ // - numbuf in tv_dict_get_string().
+ // - mybuf in tv_get_string().
+ //
+ // If you change this code make sure that buffers will not get
+ // accidentally reused.
+ char group_buf[NUMBUFLEN];
+ const char *const group = tv_dict_get_string_buf(d, "group", group_buf);
+ const int priority = (int)tv_dict_get_number(d, "priority");
+ const int id = (int)tv_dict_get_number(d, "id");
+ dictitem_T *const conceal_di = tv_dict_find(d, S_LEN("conceal"));
+ const char *const conceal = (conceal_di != NULL
+ ? tv_get_string(&conceal_di->di_tv)
+ : NULL);
+ if (i == 0) {
+ if (match_add(win, group,
+ tv_dict_get_string(d, "pattern", false),
+ priority, id, NULL, conceal) != id) {
+ match_add_failed = true;
+ }
+ } else {
+ if (match_add(win, group, NULL, priority, id, s, conceal) != id) {
+ match_add_failed = true;
+ }
+ tv_list_unref(s);
+ s = NULL;
+ }
+ });
+ if (!match_add_failed) {
+ rettv->vval.v_number = 0;
+ }
+}
+
+/// "matchadd()" function
+void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ char grpbuf[NUMBUFLEN];
+ char patbuf[NUMBUFLEN];
+ // group
+ const char *const grp = tv_get_string_buf_chk(&argvars[0], grpbuf);
+ // pattern
+ const char *const pat = tv_get_string_buf_chk(&argvars[1], patbuf);
+ // default priority
+ int prio = 10;
+ int id = -1;
+ bool error = false;
+ const char *conceal_char = NULL;
+ win_T *win = curwin;
+
+ rettv->vval.v_number = -1;
+
+ if (grp == NULL || pat == NULL) {
+ return;
+ }
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ prio = (int)tv_get_number_chk(&argvars[2], &error);
+ if (argvars[3].v_type != VAR_UNKNOWN) {
+ id = (int)tv_get_number_chk(&argvars[3], &error);
+ if (argvars[4].v_type != VAR_UNKNOWN
+ && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL) {
+ return;
+ }
+ }
+ }
+ if (error) {
+ return;
+ }
+ if (id >= 1 && id <= 3) {
+ semsg(_("E798: ID is reserved for \":match\": %" PRId64), (int64_t)id);
+ return;
+ }
+
+ rettv->vval.v_number = match_add(win, grp, pat, prio, id, NULL, conceal_char);
+}
+
+/// "matchaddpo()" function
+void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ rettv->vval.v_number = -1;
+
+ char buf[NUMBUFLEN];
+ const char *const group = tv_get_string_buf_chk(&argvars[0], buf);
+ if (group == NULL) {
+ return;
+ }
+
+ if (argvars[1].v_type != VAR_LIST) {
+ semsg(_(e_listarg), "matchaddpos()");
+ return;
+ }
+
+ list_T *l;
+ l = argvars[1].vval.v_list;
+ if (l == NULL) {
+ return;
+ }
+
+ bool error = false;
+ int prio = 10;
+ int id = -1;
+ const char *conceal_char = NULL;
+ win_T *win = curwin;
+
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ prio = (int)tv_get_number_chk(&argvars[2], &error);
+ if (argvars[3].v_type != VAR_UNKNOWN) {
+ id = (int)tv_get_number_chk(&argvars[3], &error);
+ if (argvars[4].v_type != VAR_UNKNOWN
+ && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL) {
+ return;
+ }
+ }
+ }
+ if (error == true) {
+ return;
+ }
+
+ // id == 3 is ok because matchaddpos() is supposed to substitute :3match
+ if (id == 1 || id == 2) {
+ semsg(_("E798: ID is reserved for \"match\": %" PRId64), (int64_t)id);
+ return;
+ }
+
+ rettv->vval.v_number = match_add(win, group, NULL, prio, id, l, conceal_char);
+}
+
+/// "matcharg()" function
+void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ const int id = (int)tv_get_number(&argvars[0]);
+
+ tv_list_alloc_ret(rettv, (id >= 1 && id <= 3
+ ? 2
+ : 0));
+
+ if (id >= 1 && id <= 3) {
+ matchitem_T *const m = get_match(curwin, id);
+
+ if (m != NULL) {
+ tv_list_append_string(rettv->vval.v_list,
+ (const char *)syn_id2name(m->hlg_id), -1);
+ tv_list_append_string(rettv->vval.v_list, (const char *)m->pattern, -1);
+ } else {
+ tv_list_append_string(rettv->vval.v_list, NULL, 0);
+ tv_list_append_string(rettv->vval.v_list, NULL, 0);
+ }
+ }
+}
+
+/// "matchdelete()" function
+void f_matchdelete(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ win_T *win = get_optional_window(argvars, 1);
+ if (win == NULL) {
+ rettv->vval.v_number = -1;
+ } else {
+ rettv->vval.v_number = match_delete(win,
+ (int)tv_get_number(&argvars[0]), true);
+ }
+}
+
+/// ":[N]match {group} {pattern}"
+/// Sets nextcmd to the start of the next command, if any. Also called when
+/// skipping commands to find the next command.
+void ex_match(exarg_T *eap)
+{
+ char_u *p;
+ char_u *g = NULL;
+ char_u *end;
+ int c;
+ int id;
+
+ if (eap->line2 <= 3) {
+ id = (int)eap->line2;
+ } else {
+ emsg(e_invcmd);
+ return;
+ }
+
+ // First clear any old pattern.
+ if (!eap->skip) {
+ match_delete(curwin, id, false);
+ }
+
+ if (ends_excmd(*eap->arg)) {
+ end = (char_u *)eap->arg;
+ } else if ((STRNICMP(eap->arg, "none", 4) == 0
+ && (ascii_iswhite(eap->arg[4]) || ends_excmd(eap->arg[4])))) {
+ end = (char_u *)eap->arg + 4;
+ } else {
+ p = skiptowhite((char_u *)eap->arg);
+ if (!eap->skip) {
+ g = vim_strnsave((char_u *)eap->arg, (size_t)(p - (char_u *)eap->arg));
+ }
+ p = (char_u *)skipwhite((char *)p);
+ if (*p == NUL) {
+ // There must be two arguments.
+ xfree(g);
+ semsg(_(e_invarg2), eap->arg);
+ return;
+ }
+ end = skip_regexp(p + 1, *p, true, NULL);
+ if (!eap->skip) {
+ if (*end != NUL && !ends_excmd(*skipwhite((char *)end + 1))) {
+ xfree(g);
+ eap->errmsg = e_trailing;
+ return;
+ }
+ if (*end != *p) {
+ xfree(g);
+ semsg(_(e_invarg2), p);
+ return;
+ }
+
+ c = *end;
+ *end = NUL;
+ match_add(curwin, (const char *)g, (const char *)p + 1, 10, id,
+ NULL, NULL);
+ xfree(g);
+ *end = (char_u)c;
+ }
+ }
+ eap->nextcmd = (char *)find_nextcmd(end);
+}
diff --git a/src/nvim/match.h b/src/nvim/match.h
new file mode 100644
index 0000000000..fdcec0ae05
--- /dev/null
+++ b/src/nvim/match.h
@@ -0,0 +1,12 @@
+#ifndef NVIM_MATCH_H
+#define NVIM_MATCH_H
+
+#include "nvim/buffer_defs.h"
+#include "nvim/eval/funcs.h"
+#include "nvim/ex_cmds_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "match.h.generated.h"
+#endif
+
+#endif // NVIM_MATCH_H
diff --git a/src/nvim/math.c b/src/nvim/math.c
index 63a29509bd..b427688083 100644
--- a/src/nvim/math.c
+++ b/src/nvim/math.c
@@ -1,7 +1,9 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+// uncrustify:off
#include <math.h>
+// uncrustify:on
#include <stdint.h>
#include <string.h>
@@ -29,10 +31,12 @@ int xfpclassify(double d)
return m ? FP_NAN : FP_INFINITE;
}
}
+
int xisinf(double d)
{
return FP_INFINITE == xfpclassify(d);
}
+
int xisnan(double d)
{
return FP_NAN == xfpclassify(d);
diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c
index 5eb209a6f6..a9792cf1b9 100644
--- a/src/nvim/mbyte.c
+++ b/src/nvim/mbyte.c
@@ -334,10 +334,9 @@ enc_alias_table[] =
* Returns -1 if not found.
*/
static int enc_canon_search(const char_u *name)
+ FUNC_ATTR_PURE
{
- int i;
-
- for (i = 0; i < IDX_COUNT; ++i) {
+ for (int i = 0; i < IDX_COUNT; i++) {
if (STRCMP(name, enc_canon_table[i].name) == 0) {
return i;
}
@@ -345,16 +344,14 @@ static int enc_canon_search(const char_u *name)
return -1;
}
-
/*
* Find canonical encoding "name" in the list and return its properties.
* Returns 0 if not found.
*/
int enc_canon_props(const char_u *name)
+ FUNC_ATTR_PURE
{
- int i;
-
- i = enc_canon_search(name);
+ int i = enc_canon_search(name);
if (i >= 0) {
return enc_canon_table[i].prop;
} else if (STRNCMP(name, "2byte-", 6) == 0) {
@@ -373,6 +370,7 @@ int enc_canon_props(const char_u *name)
* 3 - UTF-8 BOM
*/
int bomb_size(void)
+ FUNC_ATTR_PURE
{
int n = 0;
@@ -414,11 +412,13 @@ void remove_bom(char_u *s)
* >2 for other word characters
*/
int mb_get_class(const char_u *p)
+ FUNC_ATTR_PURE
{
return mb_get_class_tab(p, curbuf->b_chartab);
}
int mb_get_class_tab(const char_u *p, const uint64_t *const chartab)
+ FUNC_ATTR_PURE
{
if (MB_BYTE2LEN(p[0]) == 1) {
if (p[0] == NUL || ascii_iswhite(p[0])) {
@@ -429,13 +429,14 @@ int mb_get_class_tab(const char_u *p, const uint64_t *const chartab)
}
return 1;
}
- return utf_class_tab(utf_ptr2char(p), chartab);
+ return utf_class_tab(utf_ptr2char((char *)p), chartab);
}
/*
* Return true if "c" is in "table".
*/
static bool intable(const struct interval *table, size_t n_items, int c)
+ FUNC_ATTR_PURE
{
int mid, bot, top;
@@ -471,27 +472,12 @@ static bool intable(const struct interval *table, size_t n_items, int c)
int utf_char2cells(int c)
{
if (c >= 0x100) {
-#ifdef USE_WCHAR_FUNCTIONS
- //
- // Assume the library function wcwidth() works better than our own
- // stuff. It should return 1 for ambiguous width chars!
- //
- int n = wcwidth(c);
-
- if (n < 0) {
- return 6; // unprintable, displays <xxxx>
- }
- if (n > 1) {
- return n;
- }
-#else
if (!utf_printable(c)) {
return 6; // unprintable, displays <xxxx>
}
if (intable(doublewidth, ARRAY_SIZE(doublewidth), c)) {
return 2;
}
-#endif
if (p_emoji && intable(emoji_width, ARRAY_SIZE(emoji_width), c)) {
return 2;
}
@@ -510,12 +496,12 @@ int utf_char2cells(int c)
/// Return the number of display cells character at "*p" occupies.
/// This doesn't take care of unprintable characters, use ptr2cells() for that.
-int utf_ptr2cells(const char_u *p)
+int utf_ptr2cells(const char *p)
{
int c;
// Need to convert to a character number.
- if (*p >= 0x80) {
+ if ((uint8_t)(*p) >= 0x80) {
c = utf_ptr2char(p);
// An illegal byte is displayed as <xx>.
if (utf_ptr2len(p) == 1 || c == NUL) {
@@ -541,9 +527,9 @@ int utf_ptr2cells_len(const char_u *p, int size)
if (utf_ptr2len_len(p, size) < utf8len_tab[*p]) {
return 1; // truncated
}
- c = utf_ptr2char(p);
+ c = utf_ptr2char((char *)p);
// An illegal byte is displayed as <xx>.
- if (utf_ptr2len(p) == 1 || c == NUL) {
+ if (utf_ptr2len((char *)p) == 1 || c == NUL) {
return 4;
}
// If the char is ASCII it must be an overlong sequence.
@@ -560,12 +546,12 @@ int utf_ptr2cells_len(const char_u *p, int size)
/// @param str The source string, may not be NULL, must be a NUL-terminated
/// string.
/// @return The number of cells occupied by string `str`
-size_t mb_string2cells(const char_u *str)
+size_t mb_string2cells(const char *str)
{
size_t clen = 0;
- for (const char_u *p = str; *p != NUL; p += utfc_ptr2len(p)) {
- clen += utf_ptr2cells(p);
+ for (const char_u *p = (char_u *)str; *p != NUL; p += utfc_ptr2len((char *)p)) {
+ clen += utf_ptr2cells((char *)p);
}
return clen;
@@ -577,14 +563,14 @@ size_t mb_string2cells(const char_u *str)
/// string.
/// @param size maximum length of string. It will terminate on earlier NUL.
/// @return The number of cells occupied by string `str`
-size_t mb_string2cells_len(const char_u *str, size_t size)
+size_t mb_string2cells_len(const char *str, size_t size)
FUNC_ATTR_NONNULL_ARG(1)
{
size_t clen = 0;
- for (const char_u *p = str; *p != NUL && p < str+size;
- p += utfc_ptr2len_len(p, size+(p-str))) {
- clen += utf_ptr2cells(p);
+ for (const char_u *p = (char_u *)str; *p != NUL && p < (char_u *)str + size;
+ p += utfc_ptr2len_len(p, size + (p - (char_u *)str))) {
+ clen += utf_ptr2cells((char *)p);
}
return clen;
@@ -600,9 +586,10 @@ size_t mb_string2cells_len(const char_u *str, size_t size)
/// @param[in] p String to convert.
///
/// @return Unicode codepoint or byte value.
-int utf_ptr2char(const char_u *const p)
+int utf_ptr2char(const char *const p_in)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
+ uint8_t *p = (uint8_t *)p_in;
if (p[0] < 0x80) { // Be quick for ASCII.
return p[0];
}
@@ -677,7 +664,7 @@ static int utf_safe_read_char_adv(const char_u **s, size_t *n)
// We have a multibyte sequence and it isn't truncated by buffer
// limits so utf_ptr2char() is safe to use. Or the first byte is
// illegal (k=0), and it's also safe to use utf_ptr2char().
- c = utf_ptr2char(*s);
+ c = utf_ptr2char((char *)(*s));
// On failure, utf_ptr2char() returns the first byte, so here we
// check equality with the first byte. The only non-ASCII character
@@ -704,8 +691,8 @@ int mb_ptr2char_adv(const char_u **const pp)
{
int c;
- c = utf_ptr2char(*pp);
- *pp += utfc_ptr2len(*pp);
+ c = utf_ptr2char((char *)(*pp));
+ *pp += utfc_ptr2len((char *)(*pp));
return c;
}
@@ -717,8 +704,8 @@ int mb_cptr2char_adv(const char_u **pp)
{
int c;
- c = utf_ptr2char(*pp);
- *pp += utf_ptr2len(*pp);
+ c = utf_ptr2char((char *)(*pp));
+ *pp += utf_ptr2len((char *)(*pp));
return c;
}
@@ -731,14 +718,14 @@ bool utf_composinglike(const char_u *p1, const char_u *p2)
{
int c2;
- c2 = utf_ptr2char(p2);
+ c2 = utf_ptr2char((char *)p2);
if (utf_iscomposing(c2)) {
return true;
}
if (!arabic_maycombine(c2)) {
return false;
}
- return arabic_combine(utf_ptr2char(p1), c2);
+ return arabic_combine(utf_ptr2char((char *)p1), c2);
}
/// Convert a UTF-8 string to a wide character
@@ -756,21 +743,21 @@ int utfc_ptr2char(const char_u *p, int *pcc)
int cc;
int i = 0;
- c = utf_ptr2char(p);
- len = utf_ptr2len(p);
+ c = utf_ptr2char((char *)p);
+ len = utf_ptr2len((char *)p);
// Only accept a composing char when the first char isn't illegal.
if ((len > 1 || *p < 0x80)
&& p[len] >= 0x80
&& utf_composinglike(p, p + len)) {
- cc = utf_ptr2char(p + len);
+ cc = utf_ptr2char((char *)p + len);
for (;;) {
pcc[i++] = cc;
if (i == MAX_MCO) {
break;
}
- len += utf_ptr2len(p + len);
- if (p[len] < 0x80 || !utf_iscomposing(cc = utf_ptr2char(p + len))) {
+ len += utf_ptr2len((char *)p + len);
+ if (p[len] < 0x80 || !utf_iscomposing(cc = utf_ptr2char((char *)p + len))) {
break;
}
}
@@ -798,15 +785,15 @@ int utfc_ptr2char_len(const char_u *p, int *pcc, int maxlen)
int len = utf_ptr2len_len(p, maxlen);
// Is it safe to use utf_ptr2char()?
bool safe = len > 1 && len <= maxlen;
- int c = safe ? utf_ptr2char(p) : *p;
+ int c = safe ? utf_ptr2char((char *)p) : *p;
// Only accept a composing char when the first char isn't illegal.
if ((safe || c < 0x80) && len < maxlen && p[len] >= 0x80) {
for (; i < MAX_MCO; i++) {
int len_cc = utf_ptr2len_len(p + len, maxlen - len);
safe = len_cc > 1 && len_cc <= maxlen - len;
- if (!safe || (pcc[i] = utf_ptr2char(p + len)) < 0x80
- || !(i == 0 ? utf_composinglike(p, p+len) : utf_iscomposing(pcc[i]))) {
+ if (!safe || (pcc[i] = utf_ptr2char((char *)p + len)) < 0x80
+ || !(i == 0 ? utf_composinglike(p, p + len) : utf_iscomposing(pcc[i]))) {
break;
}
len += len_cc;
@@ -828,9 +815,10 @@ int utfc_ptr2char_len(const char_u *p, int *pcc, int maxlen)
///
/// @return Sequence length, 0 for empty string and 1 for non-UTF-8 byte
/// sequence.
-int utf_ptr2len(const char_u *const p)
+int utf_ptr2len(const char *const p_in)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
+ uint8_t *p = (uint8_t *)p_in;
if (*p == NUL) {
return 0;
}
@@ -887,10 +875,11 @@ int utf_ptr2len_len(const char_u *p, int size)
/// Return the number of bytes occupied by a UTF-8 character in a string
///
/// This includes following composing characters.
-int utfc_ptr2len(const char_u *const p)
+int utfc_ptr2len(const char *const p_in)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
- uint8_t b0 = (uint8_t)(*p);
+ uint8_t *p = (uint8_t *)p_in;
+ uint8_t b0 = *p;
if (b0 == NUL) {
return 0;
@@ -900,7 +889,7 @@ int utfc_ptr2len(const char_u *const p)
}
// Skip over first UTF-8 char, stopping at a NUL byte.
- int len = utf_ptr2len(p);
+ int len = utf_ptr2len((char *)p);
// Check for illegal byte.
if (len == 1 && b0 >= 0x80) {
@@ -917,7 +906,7 @@ int utfc_ptr2len(const char_u *const p)
// Skip over composing char.
prevlen = len;
- len += utf_ptr2len(p + len);
+ len += utf_ptr2len((char *)p + len);
}
}
@@ -1002,7 +991,7 @@ int utf_char2len(const int c)
/// @param c character to convert to \p buf
/// @param[out] buf UTF-8 string generated from \p c, does not add \0
/// @return Number of bytes (1-6).
-int utf_char2bytes(const int c, char_u *const buf)
+int utf_char2bytes(const int c, char *const buf)
{
if (c < 0x80) { // 7 bits
buf[0] = c;
@@ -1056,23 +1045,16 @@ bool utf_iscomposing(int c)
*/
bool utf_printable(int c)
{
-#ifdef USE_WCHAR_FUNCTIONS
- /*
- * Assume the iswprint() library function works better than our own stuff.
- */
- return iswprint(c);
-#else
// Sorted list of non-overlapping intervals.
// 0xd800-0xdfff is reserved for UTF-16, actually illegal.
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 }
};
return !intable(nonprint, ARRAY_SIZE(nonprint), c);
-#endif
}
/*
@@ -1087,6 +1069,7 @@ int utf_class(const int c)
}
int utf_class_tab(const int c, const uint64_t *const chartab)
+ FUNC_ATTR_PURE
{
// sorted list of non-overlapping intervals
static struct clinterval {
@@ -1317,10 +1300,16 @@ bool mb_isupper(int a)
return mb_tolower(a) != a;
}
+bool mb_isalpha(int a)
+ FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ return mb_islower(a) || mb_isupper(a);
+}
+
static int utf_strnicmp(const char_u *s1, const char_u *s2, size_t n1, size_t n2)
{
int c1, c2, cdiff;
- char_u buffer[6];
+ char buffer[6];
for (;;) {
c1 = utf_safe_read_char_adv(&s1, &n1);
@@ -1358,11 +1347,11 @@ static int utf_strnicmp(const char_u *s1, const char_u *s2, size_t n1, size_t n2
// to fold just one character to determine the result of comparison.
if (c1 != -1 && c2 == -1) {
- n1 = utf_char2bytes(utf_fold(c1), buffer);
- s1 = buffer;
+ n1 = utf_char2bytes(utf_fold(c1), (char *)buffer);
+ s1 = (char_u *)buffer;
} else if (c2 != -1 && c1 == -1) {
- n2 = utf_char2bytes(utf_fold(c2), buffer);
- s2 = buffer;
+ n2 = utf_char2bytes(utf_fold(c2), (char *)buffer);
+ s2 = (char_u *)buffer;
}
while (n1 > 0 && n2 > 0 && *s1 != NUL && *s2 != NUL) {
@@ -1498,10 +1487,10 @@ void mb_utflen(const char_u *s, size_t len, size_t *codepoints, size_t *codeunit
size_t count = 0, extra = 0;
size_t clen;
for (size_t i = 0; i < len && s[i] != NUL; i += clen) {
- clen = utf_ptr2len_len(s+i, len-i);
+ clen = utf_ptr2len_len(s + i, len - i);
// NB: gets the byte value of invalid sequence bytes.
// we only care whether the char fits in the BMP or not
- int c = (clen > 1) ? utf_ptr2char(s+i) : s[i];
+ int c = (clen > 1) ? utf_ptr2char((char *)s + i) : s[i];
count++;
if (c > 0xFFFF) {
extra++;
@@ -1520,22 +1509,21 @@ ssize_t mb_utf_index_to_bytes(const char_u *s, size_t len, size_t index, bool us
return 0;
}
for (i = 0; i < len && s[i] != NUL; i += clen) {
- clen = utf_ptr2len_len(s+i, len-i);
+ clen = utf_ptr2len_len(s + i, len - i);
// NB: gets the byte value of invalid sequence bytes.
// we only care whether the char fits in the BMP or not
- int c = (clen > 1) ? utf_ptr2char(s+i) : s[i];
+ int c = (clen > 1) ? utf_ptr2char((char *)s + i) : s[i];
count++;
if (use_utf16_units && c > 0xFFFF) {
count++;
}
if (count >= index) {
- return i+clen;
+ return i + clen;
}
}
return -1;
}
-
/*
* Version of strnicmp() that handles multi-byte characters.
* Needed for Big5, Shift-JIS and UTF-8 encoding. Other DBCS encodings can
@@ -1581,7 +1569,7 @@ void show_utf8(void)
// Get the byte length of the char under the cursor, including composing
// characters.
line = get_cursor_pos_ptr();
- len = utfc_ptr2len(line);
+ len = utfc_ptr2len((char *)line);
if (len == 0) {
msg("NUL");
return;
@@ -1595,7 +1583,7 @@ void show_utf8(void)
STRCPY(IObuff + rlen, "+ ");
rlen += 2;
}
- clen = utf_ptr2len(line + i);
+ clen = utf_ptr2len((char *)line + i);
}
sprintf((char *)IObuff + rlen, "%02x ",
(line[i] == NL) ? NUL : line[i]); // NUL is stored as NL
@@ -1645,7 +1633,7 @@ int utf_head_off(const char_u *base, const char_u *p)
break;
}
- c = utf_ptr2char(q);
+ c = utf_ptr2char((char *)q);
if (utf_iscomposing(c)) {
continue;
}
@@ -1658,7 +1646,7 @@ int utf_head_off(const char_u *base, const char_u *p)
while (j > base && (*j & 0xc0) == 0x80) {
--j;
}
- if (arabic_combine(utf_ptr2char(j), c)) {
+ if (arabic_combine(utf_ptr2char((char *)j), c)) {
continue;
}
}
@@ -1814,19 +1802,17 @@ bool utf_allow_break(int cc, int ncc)
/// @param[in,out] tp Destination to copy to.
void mb_copy_char(const char_u **const fp, char_u **const tp)
{
- const size_t l = (size_t)utfc_ptr2len(*fp);
+ const size_t l = (size_t)utfc_ptr2len((char *)(*fp));
memmove(*tp, *fp, l);
*tp += l;
*fp += l;
}
-/*
- * Return the offset from "p" to the first byte of a character. When "p" is
- * at the start of a character 0 is returned, otherwise the offset to the next
- * character. Can start anywhere in a stream of bytes.
- */
-int mb_off_next(char_u *base, char_u *p)
+/// Return the offset from "p" to the first byte of a character. When "p" is
+/// at the start of a character 0 is returned, otherwise the offset to the next
+/// character. Can start anywhere in a stream of bytes.
+int mb_off_next(const char_u *base, const char_u *p)
{
int i;
int j;
@@ -1854,8 +1840,9 @@ int mb_off_next(char_u *base, char_u *p)
/// Return the offset from "p" to the last byte of the character it points
/// into. Can start anywhere in a stream of bytes.
/// Composing characters are not included.
-int mb_tail_off(char_u *base, char_u *p)
+int mb_tail_off(const char *base, const char *p_in)
{
+ const uint8_t *p = (uint8_t *)p_in;
int i;
int j;
@@ -1867,7 +1854,7 @@ int mb_tail_off(char_u *base, char_u *p)
for (i = 0; (p[i + 1] & 0xc0) == 0x80; i++) {}
// Check for illegal sequence.
- for (j = 0; p - j > base; j++) {
+ for (j = 0; p_in - j > base; j++) {
if ((p[-j] & 0xc0) != 0x80) {
break;
}
@@ -1879,15 +1866,15 @@ int mb_tail_off(char_u *base, char_u *p)
return i;
}
-
/// Return the offset from "p" to the first byte of the character it points
/// into. Can start anywhere in a stream of bytes.
+/// Unlike utf_head_off() this doesn't include composing characters and returns a negative value.
///
/// @param[in] base Pointer to start of string
/// @param[in] p Pointer to byte for which to return the offset to the previous codepoint
//
/// @return 0 if invalid sequence, else offset to previous codepoint
-int mb_head_off(char_u *base, char_u *p)
+int mb_head_off(const char_u *base, const char_u *p)
{
int i;
int j;
@@ -1947,9 +1934,9 @@ void utf_find_illegal(void)
while (*p != NUL) {
// Illegal means that there are not enough trail bytes (checked by
// utf_ptr2len()) or too many of them (overlong sequence).
- len = utf_ptr2len(p);
+ len = utf_ptr2len((char *)p);
if (*p >= 0x80 && (len == 1
- || utf_char2len(utf_ptr2char(p)) != len)) {
+ || utf_char2len(utf_ptr2char((char *)p)) != len)) {
if (vimconv.vc_type == CONV_NONE) {
curwin->w_cursor.col += (colnr_T)(p - get_cursor_pos_ptr());
} else {
@@ -1957,7 +1944,7 @@ void utf_find_illegal(void)
len = (int)(p - tofree);
for (p = get_cursor_pos_ptr(); *p != NUL && len-- > 0; p += l) {
- l = utf_ptr2len(p);
+ l = utf_ptr2len((char *)p);
curwin->w_cursor.col += l;
}
}
@@ -1981,6 +1968,31 @@ theend:
convert_setup(&vimconv, NULL, NULL);
}
+/// @return true if string "s" is a valid utf-8 string.
+/// When "end" is NULL stop at the first NUL.
+/// When "end" is positive stop there.
+bool utf_valid_string(const char_u *s, const char_u *end)
+{
+ const char_u *p = s;
+
+ while (end == NULL ? *p != NUL : p < end) {
+ int l = utf8len_tab_zero[*p];
+ if (l == 0) {
+ return false; // invalid lead byte
+ }
+ if (end != NULL && p + l > end) {
+ return false; // incomplete byte sequence
+ }
+ p++;
+ while (--l > 0) {
+ if ((*p++ & 0xc0) != 0x80) {
+ return false; // invalid trail byte
+ }
+ }
+ }
+ return true;
+}
+
/*
* If the cursor moves on an trail byte, set the cursor on the lead byte.
* Thus it moves left if necessary.
@@ -2001,7 +2013,7 @@ void mb_check_adjust_col(void *win_)
// Column 0 is always valid.
if (oldcol != 0) {
- char_u *p = ml_get_buf(win->w_buffer, win->w_cursor.lnum, false);
+ char *p = (char *)ml_get_buf(win->w_buffer, win->w_cursor.lnum, false);
colnr_T len = (colnr_T)STRLEN(p);
// Empty line or invalid column?
@@ -2013,7 +2025,7 @@ void mb_check_adjust_col(void *win_)
win->w_cursor.col = len - 1;
}
// Move the cursor to the head byte.
- win->w_cursor.col -= utf_head_off(p, p + win->w_cursor.col);
+ win->w_cursor.col -= utf_head_off((char_u *)p, (char_u *)p + win->w_cursor.col);
}
// Reset `coladd` when the cursor would be on the right half of a
@@ -2037,13 +2049,11 @@ char_u *mb_prevptr(char_u *line, char_u *p)
return p;
}
-/*
- * Return the character length of "str". Each multi-byte character (with
- * following composing characters) counts as one.
- */
-int mb_charlen(char_u *str)
+/// Return the character length of "str". Each multi-byte character (with
+/// following composing characters) counts as one.
+int mb_charlen(const char_u *str)
{
- char_u *p = str;
+ const char_u *p = str;
int count;
if (p == NULL) {
@@ -2051,22 +2061,20 @@ int mb_charlen(char_u *str)
}
for (count = 0; *p != NUL; count++) {
- p += utfc_ptr2len(p);
+ p += utfc_ptr2len((char *)p);
}
return count;
}
-/*
- * Like mb_charlen() but for a string with specified length.
- */
-int mb_charlen_len(char_u *str, int len)
+/// Like mb_charlen() but for a string with specified length.
+int mb_charlen_len(const char_u *str, int len)
{
- char_u *p = str;
+ const char_u *p = str;
int count;
for (count = 0; *p != NUL && p < str + len; count++) {
- p += utfc_ptr2len(p);
+ p += utfc_ptr2len((char *)p);
}
return count;
@@ -2089,8 +2097,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 +2105,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 {
@@ -2112,7 +2114,7 @@ const char *mb_unescape(const char **const pp)
// Return a multi-byte character if it's found. An illegal sequence
// will result in a 1 here.
- if (utf_ptr2len((const char_u *)buf) > 1) {
+ if (utf_ptr2len(buf) > 1) {
*pp = (const char *)str + str_idx + 1;
return buf;
}
@@ -2125,7 +2127,6 @@ const char *mb_unescape(const char **const pp)
return NULL;
}
-
/*
* Skip the Vim specific head of a 'encoding' name.
*/
@@ -2207,11 +2208,9 @@ char_u *enc_canonize(char_u *enc) FUNC_ATTR_NONNULL_RET
return r;
}
-/*
- * Search for an encoding alias of "name".
- * Returns -1 when not found.
- */
-static int enc_alias_search(char_u *name)
+/// Search for an encoding alias of "name".
+/// Returns -1 when not found.
+static int enc_alias_search(const char_u *name)
{
int i;
@@ -2223,7 +2222,6 @@ static int enc_alias_search(char_u *name)
return -1;
}
-
#ifdef HAVE_LANGINFO_H
# include <langinfo.h>
#endif
@@ -2265,7 +2263,7 @@ char_u *enc_locale(void)
// Make the name lowercase and replace '_' with '-'.
// Exception: "ja_JP.EUC" == "euc-jp", "zh_CN.EUC" = "euc-cn",
// "ko_KR.EUC" == "euc-kr"
- const char *p = (char *)vim_strchr((char_u *)s, '.');
+ const char *p = vim_strchr(s, '.');
if (p != NULL) {
if (p > s + 2 && !STRNICMP(p + 1, "EUC", 3)
&& !isalnum((int)p[4]) && p[4] != '-' && p[-3] == '_') {
@@ -2297,7 +2295,6 @@ enc_locale_copy_enc:
#if defined(HAVE_ICONV)
-
/*
* Call iconv_open() with a check if iconv() works properly (there are broken
* versions).
@@ -2404,7 +2401,7 @@ static char_u *iconv_string(const vimconv_T *const vcp, char_u *str, size_t slen
// conversion from 'encoding' to something else. In other
// situations we don't know what to skip anyway.
*to++ = '?';
- if (utf_ptr2cells((char_u *)from) > 1) {
+ if (utf_ptr2cells(from) > 1) {
*to++ = '?';
}
l = utfc_ptr2len_len((const char_u *)from, (int)fromlen);
@@ -2427,7 +2424,6 @@ static char_u *iconv_string(const vimconv_T *const vcp, char_u *str, size_t slen
#endif // HAVE_ICONV
-
/*
* Setup "vcp" for conversion from "from" to "to".
* The names must have been made canonical with enc_canonize().
@@ -2589,7 +2585,7 @@ char_u *string_convert_ext(const vimconv_T *const vcp, char_u *ptr, size_t *lenp
case 0xbe:
c = 0x0178; break; // Y
}
- d += utf_char2bytes(c, d);
+ d += utf_char2bytes(c, (char *)d);
}
*d = NUL;
if (lenp != NULL) {
@@ -2620,7 +2616,7 @@ char_u *string_convert_ext(const vimconv_T *const vcp, char_u *ptr, size_t *lenp
}
*d++ = ptr[i];
} else {
- c = utf_ptr2char(ptr + i);
+ c = utf_ptr2char((char *)ptr + i);
if (vcp->vc_type == CONV_TO_LATIN9) {
switch (c) {
case 0x20ac:
diff --git a/src/nvim/memfile.c b/src/nvim/memfile.c
index 2a72d1e6a0..c828334eaf 100644
--- a/src/nvim/memfile.c
+++ b/src/nvim/memfile.c
@@ -60,7 +60,6 @@
#define MEMFILE_PAGE_SIZE 4096 /// default page size
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "memfile.c.generated.h"
#endif
@@ -795,7 +794,7 @@ static bool mf_do_open(memfile_T *mfp, char_u *fname, int flags)
emsg(_("E300: Swap file already exists (symlink attack?)"));
} else {
// try to open the file
- mfp->mf_fd = mch_open_rw((char *)mfp->mf_fname, flags | O_NOFOLLOW);
+ mfp->mf_fd = MCH_OPEN_RW((char *)mfp->mf_fname, flags | O_NOFOLLOW);
}
// If the file cannot be opened, use memory only
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index 9925971783..5f74440747 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -3,7 +3,7 @@
// for debugging
// #define CHECK(c, s) do { if (c) emsg(s); } while (0)
-#define CHECK(c, s) do { } while (0)
+#define CHECK(c, s) do {} while (0)
/*
* memline.c: Contains the functions for appending, deleting and changing the
@@ -230,7 +230,7 @@ static linenr_T lowest_marked = 0;
#define ML_INSERT 0x12 // insert line
#define ML_FIND 0x13 // just find the line
#define ML_FLUSH 0x02 // flush locked block
-#define ML_SIMPLE(x) (x & 0x10) // DEL, INS or FIND
+#define ML_SIMPLE(x) ((x) & 0x10) // DEL, INS or FIND
// argument for ml_upd_block0()
typedef enum {
@@ -242,11 +242,9 @@ typedef enum {
# include "memline.c.generated.h"
#endif
-/*
- * Open a new memline for "buf".
- *
- * Return FAIL for failure, OK otherwise.
- */
+/// Open a new memline for "buf".
+///
+/// @return FAIL for failure, OK otherwise.
int ml_open(buf_T *buf)
{
bhdr_T *hp = NULL;
@@ -266,7 +264,7 @@ int ml_open(buf_T *buf)
buf->b_ml.ml_chunksize = NULL;
buf->b_ml.ml_usedchunks = 0;
- if (cmdmod.noswapfile) {
+ if (cmdmod.cmod_flags & CMOD_NOSWAPFILE) {
buf->b_p_swf = false;
}
@@ -290,7 +288,6 @@ int ml_open(buf_T *buf)
buf->b_ml.ml_line_count = 1;
curwin->w_nrwidth_line_count = 0;
-
/*
* fill block0 struct and write page 0
*/
@@ -303,7 +300,7 @@ int ml_open(buf_T *buf)
b0p->b0_id[0] = BLOCK0_ID0;
b0p->b0_id[1] = BLOCK0_ID1;
- b0p->b0_magic_long = (long)B0_MAGIC_LONG;
+ b0p->b0_magic_long = B0_MAGIC_LONG;
b0p->b0_magic_int = (int)B0_MAGIC_INT;
b0p->b0_magic_short = (short)B0_MAGIC_SHORT;
b0p->b0_magic_char = B0_MAGIC_CHAR;
@@ -314,7 +311,7 @@ int ml_open(buf_T *buf)
b0p->b0_dirty = buf->b_changed ? B0_DIRTY : 0;
b0p->b0_flags = get_fileformat(buf) + 1;
set_b0_fname(b0p, buf);
- (void)os_get_user_name((char *)b0p->b0_uname, B0_UNAME_SIZE);
+ (void)os_get_username((char *)b0p->b0_uname, B0_UNAME_SIZE);
b0p->b0_uname[B0_UNAME_SIZE - 1] = NUL;
os_get_hostname((char *)b0p->b0_hname, B0_HNAME_SIZE);
b0p->b0_hname[B0_HNAME_SIZE - 1] = NUL;
@@ -379,10 +376,8 @@ error:
return FAIL;
}
-/*
- * ml_setname() is called when the file name of "buf" has been changed.
- * It may rename the swap file.
- */
+/// ml_setname() is called when the file name of "buf" has been changed.
+/// It may rename the swap file.
void ml_setname(buf_T *buf)
{
bool success = false;
@@ -396,7 +391,7 @@ void ml_setname(buf_T *buf)
* When 'updatecount' is 0 and 'noswapfile' there is no swap file.
* For help files we will make a swap file now.
*/
- if (p_uc != 0 && !cmdmod.noswapfile) {
+ if (p_uc != 0 && (cmdmod.cmod_flags & CMOD_NOSWAPFILE) == 0) {
ml_open_file(buf); // create a swap file
}
return;
@@ -422,7 +417,7 @@ void ml_setname(buf_T *buf)
}
// if the file name is the same we don't have to do anything
- if (fnamecmp(fname, mfp->mf_fname) == 0) {
+ if (FNAMECMP(fname, mfp->mf_fname) == 0) {
xfree(fname);
success = true;
break;
@@ -458,11 +453,9 @@ void ml_setname(buf_T *buf)
}
}
-/*
- * Open a file for the memfile for all buffers that are not readonly or have
- * been modified.
- * Used when 'updatecount' changes from zero to non-zero.
- */
+/// Open a file for the memfile for all buffers that are not readonly or have
+/// been modified.
+/// Used when 'updatecount' changes from zero to non-zero.
void ml_open_files(void)
{
FOR_ALL_BUFFERS(buf) {
@@ -472,11 +465,9 @@ void ml_open_files(void)
}
}
-/*
- * Open a swap file for an existing memfile, if there is no swap file yet.
- * If we are unable to find a file name, mf_fname will be NULL
- * and the memfile will be in memory only (no recovery possible).
- */
+/// Open a swap file for an existing memfile, if there is no swap file yet.
+/// If we are unable to find a file name, mf_fname will be NULL
+/// and the memfile will be in memory only (no recovery possible).
void ml_open_file(buf_T *buf)
{
memfile_T *mfp;
@@ -484,7 +475,8 @@ void ml_open_file(buf_T *buf)
char_u *dirp;
mfp = buf->b_ml.ml_mfp;
- if (mfp == NULL || mfp->mf_fd >= 0 || !buf->b_p_swf || cmdmod.noswapfile
+ if (mfp == NULL || mfp->mf_fd >= 0 || !buf->b_p_swf
+ || (cmdmod.cmod_flags & CMOD_NOSWAPFILE)
|| buf->terminal) {
return; // nothing to do
}
@@ -540,7 +532,7 @@ void ml_open_file(buf_T *buf)
no_wait_return++;
(void)semsg(_("E303: Unable to open swap file for \"%s\", recovery impossible"),
buf_spname(buf) != NULL ? buf_spname(buf) : buf->b_fname);
- --no_wait_return;
+ no_wait_return--;
}
// don't try to open a swap file again
@@ -563,10 +555,9 @@ void check_need_swap(bool newfile)
msg_silent = old_msg_silent;
}
-/*
- * Close memline for buffer 'buf'.
- * If 'del_file' is TRUE, delete the swap file
- */
+/// Close memline for buffer 'buf'.
+///
+/// @param del_file if TRUE, delete the swap file
void ml_close(buf_T *buf, int del_file)
{
if (buf->b_ml.ml_mfp == NULL) { // not open
@@ -585,25 +576,21 @@ void ml_close(buf_T *buf, int del_file)
buf->b_flags &= ~BF_RECOVERED;
}
-/*
- * Close all existing memlines and memfiles.
- * Only used when exiting.
- * When 'del_file' is TRUE, delete the memfiles.
- * But don't delete files that were ":preserve"d when we are POSIX compatible.
- */
-void ml_close_all(int del_file)
+/// Close all existing memlines and memfiles.
+/// Only used when exiting.
+///
+/// @param del_file if true, delete the memfiles.
+void ml_close_all(bool del_file)
{
FOR_ALL_BUFFERS(buf) {
- ml_close(buf, del_file && ((buf->b_flags & BF_PRESERVED) == 0));
+ ml_close(buf, del_file);
}
spell_delete_wordlist(); // delete the internal wordlist
vim_deltempdir(); // delete created temp directory
}
-/*
- * Close all memfiles for not modified buffers.
- * Only use just before exiting!
- */
+/// Close all memfiles for not modified buffers.
+/// Only use just before exiting!
void ml_close_notmod(void)
{
FOR_ALL_BUFFERS(buf) {
@@ -613,10 +600,8 @@ void ml_close_notmod(void)
}
}
-/*
- * Update the timestamp in the .swp file.
- * Used when the file has been written.
- */
+/// Update the timestamp in the .swp file.
+/// Used when the file has been written.
void ml_timestamp(buf_T *buf)
{
ml_upd_block0(buf, UB_FNAME);
@@ -639,9 +624,7 @@ static bool ml_check_b0_strings(ZERO_BL *b0p)
&& memchr(b0p->b0_fname, NUL, B0_FNAME_SIZE_CRYPT)); // -V512
}
-/*
- * Update the timestamp or the B0_SAME_DIR flag of the .swp file.
- */
+/// Update the timestamp or the B0_SAME_DIR flag of the .swp file.
static void ml_upd_block0(buf_T *buf, upd_block0_T what)
{
memfile_T *mfp;
@@ -665,11 +648,9 @@ static void ml_upd_block0(buf_T *buf, upd_block0_T what)
mf_put(mfp, hp, true, false);
}
-/*
- * Write file name and timestamp into block 0 of a swap file.
- * Also set buf->b_mtime.
- * Don't use NameBuff[]!!!
- */
+/// Write file name and timestamp into block 0 of a swap file.
+/// Also set buf->b_mtime.
+/// Don't use NameBuff[]!!!
static void set_b0_fname(ZERO_BL *b0p, buf_T *buf)
{
if (buf->b_ffname == NULL) {
@@ -684,11 +665,11 @@ static void set_b0_fname(ZERO_BL *b0p, buf_T *buf)
* First replace home dir path with "~/" with home_replace().
* Then insert the user name to get "~user/".
*/
- home_replace(NULL, buf->b_ffname, b0p->b0_fname,
- B0_FNAME_SIZE_CRYPT, TRUE);
+ home_replace(NULL, buf->b_ffname, (char *)b0p->b0_fname,
+ B0_FNAME_SIZE_CRYPT, true);
if (b0p->b0_fname[0] == '~') {
// If there is no user name or it is too long, don't use "~/"
- int retval = os_get_user_name(uname, B0_UNAME_SIZE);
+ int retval = os_get_username(uname, B0_UNAME_SIZE);
size_t ulen = STRLEN(uname);
size_t flen = STRLEN(b0p->b0_fname);
if (retval == FAIL || ulen + flen > B0_FNAME_SIZE_CRYPT - 1) {
@@ -699,16 +680,19 @@ static void set_b0_fname(ZERO_BL *b0p, buf_T *buf)
}
}
FileInfo file_info;
- if (os_fileinfo((char *)buf->b_ffname, &file_info)) {
+ if (os_fileinfo(buf->b_ffname, &file_info)) {
long_to_char(file_info.stat.st_mtim.tv_sec, b0p->b0_mtime);
long_to_char((long)os_fileinfo_inode(&file_info), b0p->b0_ino);
buf_store_file_info(buf, &file_info);
buf->b_mtime_read = buf->b_mtime;
+ buf->b_mtime_read_ns = buf->b_mtime_ns;
} else {
long_to_char(0L, b0p->b0_mtime);
long_to_char(0L, b0p->b0_ino);
buf->b_mtime = 0;
+ buf->b_mtime_ns = 0;
buf->b_mtime_read = 0;
+ buf->b_mtime_read_ns = 0;
buf->b_orig_size = 0;
buf->b_orig_mode = 0;
}
@@ -718,24 +702,20 @@ static void set_b0_fname(ZERO_BL *b0p, buf_T *buf)
add_b0_fenc(b0p, curbuf);
}
-/*
- * Update the B0_SAME_DIR flag of the swap file. It's set if the file and the
- * swapfile for "buf" are in the same directory.
- * This is fail safe: if we are not sure the directories are equal the flag is
- * not set.
- */
+/// Update the B0_SAME_DIR flag of the swap file. It's set if the file and the
+/// swapfile for "buf" are in the same directory.
+/// This is fail safe: if we are not sure the directories are equal the flag is
+/// not set.
static void set_b0_dir_flag(ZERO_BL *b0p, buf_T *buf)
{
- if (same_directory(buf->b_ml.ml_mfp->mf_fname, buf->b_ffname)) {
+ if (same_directory(buf->b_ml.ml_mfp->mf_fname, (char_u *)buf->b_ffname)) {
b0p->b0_flags |= B0_SAME_DIR;
} else {
b0p->b0_flags &= ~B0_SAME_DIR;
}
}
-/*
- * When there is room, add the 'fileencoding' to block zero.
- */
+/// When there is room, add the 'fileencoding' to block zero.
static void add_b0_fenc(ZERO_BL *b0p, buf_T *buf)
{
int n;
@@ -752,10 +732,10 @@ static void add_b0_fenc(ZERO_BL *b0p, buf_T *buf)
}
}
-
/// Try to recover curbuf from the .swp file.
-/// @param checkext If true, check the extension and detect whether it is a
-/// swap file.
+///
+/// @param checkext if true, check the extension and detect whether it is a
+/// swap file.
void ml_recover(bool checkext)
{
buf_T *buf = NULL;
@@ -796,15 +776,14 @@ void ml_recover(bool checkext)
// If the file name ends in ".s[a-w][a-z]" we assume this is the swap file.
// Otherwise a search is done to find the swap file(s).
- fname = curbuf->b_fname;
+ fname = (char_u *)curbuf->b_fname;
if (fname == NULL) { // When there is no file name
fname = (char_u *)"";
}
len = (int)STRLEN(fname);
if (checkext && len >= 4
&& STRNICMP(fname + len - 4, ".s", 2) == 0
- && vim_strchr((char_u *)"abcdefghijklmnopqrstuvw",
- TOLOWER_ASC(fname[len - 2])) != NULL
+ && vim_strchr("abcdefghijklmnopqrstuvw", TOLOWER_ASC(fname[len - 2])) != NULL
&& ASCII_ISALPHA(fname[len - 1])) {
directly = true;
fname_used = vim_strsave(fname); // make a copy for mf_open()
@@ -954,18 +933,18 @@ void ml_recover(bool checkext)
*/
if (directly) {
expand_env(b0p->b0_fname, NameBuff, MAXPATHL);
- if (setfname(curbuf, NameBuff, NULL, true) == FAIL) {
+ if (setfname(curbuf, (char *)NameBuff, NULL, true) == FAIL) {
goto theend;
}
}
- home_replace(NULL, mfp->mf_fname, NameBuff, MAXPATHL, TRUE);
+ home_replace(NULL, (char *)mfp->mf_fname, (char *)NameBuff, MAXPATHL, true);
smsg(_("Using swap file \"%s\""), NameBuff);
if (buf_spname(curbuf) != NULL) {
STRLCPY(NameBuff, buf_spname(curbuf), MAXPATHL);
} else {
- home_replace(NULL, curbuf->b_ffname, NameBuff, MAXPATHL, TRUE);
+ home_replace(NULL, curbuf->b_ffname, (char *)NameBuff, MAXPATHL, true);
}
smsg(_("Original file \"%s\""), NameBuff);
msg_putchar('\n');
@@ -977,7 +956,7 @@ void ml_recover(bool checkext)
FileInfo swp_file_info;
mtime = char_to_long(b0p->b0_mtime);
if (curbuf->b_ffname != NULL
- && os_fileinfo((char *)curbuf->b_ffname, &org_file_info)
+ && os_fileinfo(curbuf->b_ffname, &org_file_info)
&& ((os_fileinfo((char *)mfp->mf_fname, &swp_file_info)
&& org_file_info.stat.st_mtim.tv_sec
> swp_file_info.stat.st_mtim.tv_sec)
@@ -991,8 +970,7 @@ void ml_recover(bool checkext)
if (b0p->b0_flags & B0_HAS_FENC) {
int fnsize = B0_FNAME_SIZE_NOCRYPT;
- for (p = b0p->b0_fname + fnsize; p > b0p->b0_fname && p[-1] != NUL; p--) {
- }
+ for (p = b0p->b0_fname + fnsize; p > b0p->b0_fname && p[-1] != NUL; p--) {}
b0_fenc = vim_strnsave(p, b0p->b0_fname + fnsize - p);
}
@@ -1013,7 +991,7 @@ void ml_recover(bool checkext)
*/
if (curbuf->b_ffname != NULL) {
orig_file_status = readfile(curbuf->b_ffname, NULL, (linenr_T)0,
- (linenr_T)0, (linenr_T)MAXLNUM, NULL, READ_NEW);
+ (linenr_T)0, (linenr_T)MAXLNUM, NULL, READ_NEW, false);
}
// Use the 'fileformat' and 'fileencoding' as stored in the swap file.
@@ -1055,8 +1033,8 @@ void ml_recover(bool checkext)
semsg(_("E309: Unable to read block 1 from %s"), mfp->mf_fname);
goto theend;
}
- ++error;
- ml_append(lnum++, (char_u *)_("???MANY LINES MISSING"),
+ error++;
+ ml_append(lnum++, _("???MANY LINES MISSING"),
(colnr_T)0, true);
} else { // there is a block
pp = hp->bh_data;
@@ -1067,14 +1045,14 @@ void ml_recover(bool checkext)
line_count -= pp->pb_pointer[i].pe_line_count;
}
if (line_count != 0) {
- ++error;
- ml_append(lnum++, (char_u *)_("???LINE COUNT WRONG"),
+ error++;
+ ml_append(lnum++, _("???LINE COUNT WRONG"),
(colnr_T)0, true);
}
}
if (pp->pb_count == 0) {
- ml_append(lnum++, (char_u *)_("???EMPTY BLOCK"),
+ ml_append(lnum++, _("???EMPTY BLOCK"),
(colnr_T)0, true);
error++;
} else if (idx < (int)pp->pb_count) { // go a block deeper
@@ -1088,15 +1066,15 @@ void ml_recover(bool checkext)
line_count = pp->pb_pointer[idx].pe_line_count;
if (readfile(curbuf->b_ffname, NULL, lnum,
pp->pb_pointer[idx].pe_old_lnum - 1, line_count,
- NULL, 0) != OK) {
+ NULL, 0, false) != OK) {
cannot_open = true;
} else {
lnum += line_count;
}
}
if (cannot_open) {
- ++error;
- ml_append(lnum++, (char_u *)_("???LINES MISSING"),
+ error++;
+ ml_append(lnum++, _("???LINES MISSING"),
(colnr_T)0, true);
}
++idx; // get same block again for next index
@@ -1125,8 +1103,8 @@ void ml_recover(bool checkext)
mfp->mf_fname);
goto theend;
}
- ++error;
- ml_append(lnum++, (char_u *)_("???BLOCK MISSING"),
+ error++;
+ ml_append(lnum++, _("???BLOCK MISSING"),
(colnr_T)0, true);
} else {
// it is a data block
@@ -1136,8 +1114,7 @@ void ml_recover(bool checkext)
// if wrong, use length in pointer block
if (page_count * mfp->mf_page_size != dp->db_txt_end) {
ml_append(lnum++,
- (char_u *)_("??? from here until ???END lines"
- " may be messed up"),
+ _("??? from here until ???END lines" " may be messed up"),
(colnr_T)0, true);
error++;
has_error = true;
@@ -1153,8 +1130,8 @@ void ml_recover(bool checkext)
*/
if (line_count != dp->db_line_count) {
ml_append(lnum++,
- (char_u *)_("??? from here until ???END lines"
- " may have been inserted/deleted"),
+ _("??? from here until ???END lines"
+ " may have been inserted/deleted"),
(colnr_T)0, true);
error++;
has_error = true;
@@ -1169,10 +1146,10 @@ void ml_recover(bool checkext)
} else {
p = (char_u *)dp + txt_start;
}
- ml_append(lnum++, p, (colnr_T)0, true);
+ ml_append(lnum++, (char *)p, (colnr_T)0, true);
}
if (has_error) {
- ml_append(lnum++, (char_u *)_("???END"), (colnr_T)0, true);
+ ml_append(lnum++, _("???END"), (colnr_T)0, true);
}
}
}
@@ -1269,8 +1246,8 @@ theend:
if (serious_error && called_from_main) {
ml_close(curbuf, TRUE);
} else {
- apply_autocmds(EVENT_BUFREADPOST, NULL, curbuf->b_fname, FALSE, curbuf);
- apply_autocmds(EVENT_BUFWINENTER, NULL, curbuf->b_fname, FALSE, curbuf);
+ apply_autocmds(EVENT_BUFREADPOST, NULL, curbuf->b_fname, false, curbuf);
+ apply_autocmds(EVENT_BUFWINENTER, NULL, curbuf->b_fname, false, curbuf);
}
}
@@ -1328,7 +1305,7 @@ int recover_names(char_u *fname, int list, int nr, char_u **fname_out)
// Isolate a directory name from *dirp and put it in dir_name (we know
// it is large enough, so use 31000 for length).
// Advance dirp to next directory name.
- (void)copy_option_part(&dirp, dir_name, 31000, ",");
+ (void)copy_option_part((char **)&dirp, (char *)dir_name, 31000, ",");
if (dir_name[0] == '.' && dir_name[1] == NUL) { // check current dir
if (fname == NULL) {
@@ -1359,8 +1336,8 @@ int recover_names(char_u *fname, int list, int nr, char_u **fname_out)
tail = (char_u *)make_percent_swname((char *)dir_name,
(char *)fname_res);
} else {
- tail = path_tail(fname_res);
- tail = (char_u *)concat_fnames((char *)dir_name, (char *)tail, TRUE);
+ tail = (char_u *)path_tail((char *)fname_res);
+ tail = (char_u *)concat_fnames((char *)dir_name, (char *)tail, true);
}
num_names = recov_file_names(names, tail, FALSE);
xfree(tail);
@@ -1400,7 +1377,7 @@ int recover_names(char_u *fname, int list, int nr, char_u **fname_out)
for (int i = 0; i < num_files; i++) {
// Do not expand wildcards, on Windows would try to expand
// "%tmp%" in "%tmp%file"
- if (path_full_compare(p, files[i], true, false) & kEqualFiles) {
+ if (path_full_compare((char *)p, (char *)files[i], true, false) & kEqualFiles) {
// Remove the name from files[i]. Move further entries
// down. When the array becomes empty free it here, since
// FreeWild() won't be called below.
@@ -1439,7 +1416,7 @@ int recover_names(char_u *fname, int list, int nr, char_u **fname_out)
// print the swap file name
msg_outnum((long)++file_count);
msg_puts(". ");
- msg_puts((const char *)path_tail(files[i]));
+ msg_puts((const char *)path_tail((char *)files[i]));
msg_putchar('\n');
(void)swapfile_info(files[i]);
}
@@ -1462,10 +1439,8 @@ int recover_names(char_u *fname, int list, int nr, char_u **fname_out)
return file_count;
}
-/*
- * Append the full path to name with path separators made into percent
- * signs, to dir. An unnamed buffer is handled as "" (<currentdir>/"")
- */
+/// Append the full path to name with path separators made into percent
+/// signs, to dir. An unnamed buffer is handled as "" (<currentdir>/"")
char *make_percent_swname(const char *dir, const char *name)
FUNC_ATTR_NONNULL_ARG(1)
{
@@ -1487,8 +1462,9 @@ char *make_percent_swname(const char *dir, const char *name)
static bool process_still_running;
-/// Return information found in swapfile "fname" in dictionary "d".
/// This is used by the swapinfo() function.
+///
+/// @return information found in swapfile "fname" in dictionary "d".
void get_b0_dict(const char *fname, dict_T *d)
{
int fd;
@@ -1525,7 +1501,8 @@ void get_b0_dict(const char *fname, dict_T *d)
}
/// Give information about an existing swap file.
-/// Returns timestamp (0 when unknown).
+///
+/// @return timestamp (0 when unknown).
static time_t swapfile_info(char_u *fname)
{
assert(fname != NULL);
@@ -1543,7 +1520,7 @@ static time_t swapfile_info(char_u *fname)
// print name of owner of the file
if (os_get_uname(file_info.stat.st_uid, uname, B0_UNAME_SIZE) == OK) {
msg_puts(_(" owned by: "));
- msg_outtrans((char_u *)uname);
+ msg_outtrans(uname);
msg_puts(_(" dated: "));
} else
#endif
@@ -1570,7 +1547,7 @@ static time_t swapfile_info(char_u *fname)
if (b0.b0_fname[0] == NUL) {
msg_puts(_("[No Name]"));
} else {
- msg_outtrans(b0.b0_fname);
+ msg_outtrans((char *)b0.b0_fname);
}
msg_puts(_("\n modified: "));
@@ -1578,7 +1555,7 @@ static time_t swapfile_info(char_u *fname)
if (*(b0.b0_uname) != NUL) {
msg_puts(_("\n user name: "));
- msg_outtrans(b0.b0_uname);
+ msg_outtrans((char *)b0.b0_uname);
}
if (*(b0.b0_hname) != NUL) {
@@ -1587,7 +1564,7 @@ static time_t swapfile_info(char_u *fname)
} else {
msg_puts(_("\n host name: "));
}
- msg_outtrans(b0.b0_hname);
+ msg_outtrans((char *)b0.b0_hname);
}
if (char_to_long(b0.b0_pid) != 0L) {
@@ -1615,9 +1592,9 @@ static time_t swapfile_info(char_u *fname)
return x;
}
-/// Returns TRUE if the swap file looks OK and there are no changes, thus it
-/// can be safely deleted.
-static time_t swapfile_unchanged(char *fname)
+/// @return true if the swap file looks OK and there are no changes, thus it
+/// can be safely deleted.
+static bool swapfile_unchanged(char *fname)
{
struct block0 b0;
int ret = true;
@@ -1695,13 +1672,12 @@ static int recov_file_names(char_u **names, char_u *path, int prepend_dot)
return num_names;
}
-/*
- * sync all memlines
- *
- * If 'check_file' is TRUE, check if original file exists and was not changed.
- * If 'check_char' is TRUE, stop syncing when character becomes available, but
- * always sync at least one block.
- */
+/// sync all memlines
+///
+/// @param check_file if TRUE, check if original file exists and was not changed.
+/// @param check_char if TRUE, stop syncing when character becomes available, but
+///
+/// always sync at least one block.
void ml_sync_all(int check_file, int check_char, bool do_fsync)
{
FOR_ALL_BUFFERS(buf) {
@@ -1718,8 +1694,9 @@ void ml_sync_all(int check_file, int check_char, bool do_fsync)
* call ml_preserve() to get rid of all negative numbered blocks.
*/
FileInfo file_info;
- if (!os_fileinfo((char *)buf->b_ffname, &file_info)
+ if (!os_fileinfo(buf->b_ffname, &file_info)
|| file_info.stat.st_mtim.tv_sec != buf->b_mtime_read
+ || file_info.stat.st_mtim.tv_nsec != buf->b_mtime_read_ns
|| os_fileinfo_size(&file_info) != buf->b_orig_size) {
ml_preserve(buf, false, do_fsync);
did_check_timestamps = false;
@@ -1736,16 +1713,14 @@ void ml_sync_all(int check_file, int check_char, bool do_fsync)
}
}
-/*
- * sync one buffer, including negative blocks
- *
- * after this all the blocks are in the swap file
- *
- * Used for the :preserve command and when the original file has been
- * changed or deleted.
- *
- * when message is TRUE the success of preserving is reported
- */
+/// sync one buffer, including negative blocks
+///
+/// after this all the blocks are in the swap file
+///
+/// Used for the :preserve command and when the original file has been
+/// changed or deleted.
+///
+/// @param message if TRUE, the success of preserving is reported.
void ml_preserve(buf_T *buf, int message, bool do_fsync)
{
bhdr_T *hp;
@@ -1821,27 +1796,24 @@ theend:
* line2 = ml_get(2); // line1 is now invalid!
* Make a copy of the line if necessary.
*/
-/*
- * Return a pointer to a (read-only copy of a) line.
- *
- * On failure an error message is given and IObuff is returned (to avoid
- * having to check for error everywhere).
- */
+
+/// @return a pointer to a (read-only copy of a) line.
+///
+/// On failure an error message is given and IObuff is returned (to avoid
+/// having to check for error everywhere).
char_u *ml_get(linenr_T lnum)
{
return ml_get_buf(curbuf, lnum, false);
}
-/*
- * Return pointer to position "pos".
- */
+/// @return pointer to position "pos".
char_u *ml_get_pos(const pos_T *pos)
FUNC_ATTR_NONNULL_ALL
{
return ml_get_buf(curbuf, pos->lnum, false) + pos->col;
}
-/// get codepoint at pos. pos must be either valid or have col set to MAXCOL!
+/// @return codepoint at pos. pos must be either valid or have col set to MAXCOL!
int gchar_pos(pos_T *pos)
FUNC_ATTR_NONNULL_ARG(1)
{
@@ -1849,12 +1821,12 @@ int gchar_pos(pos_T *pos)
if (pos->col == MAXCOL) {
return NUL;
}
- return utf_ptr2char(ml_get_pos(pos));
+ return utf_ptr2char((char *)ml_get_pos(pos));
}
-/// Return a pointer to a line in a specific buffer
-///
/// @param will_change true mark the buffer dirty (chars in the line will be changed)
+///
+/// @return a pointer to a line in a specific buffer
char_u *ml_get_buf(buf_T *buf, linenr_T lnum, bool will_change)
FUNC_ATTR_NONNULL_ALL
{
@@ -1862,6 +1834,7 @@ char_u *ml_get_buf(buf_T *buf, linenr_T lnum, bool will_change)
DATA_BL *dp;
char_u *ptr;
static int recursive = 0;
+ static char_u questions[4];
if (lnum > buf->b_ml.ml_line_count) { // invalid line number
if (recursive == 0) {
@@ -1871,9 +1844,12 @@ char_u *ml_get_buf(buf_T *buf, linenr_T lnum, bool will_change)
siemsg(_("E315: ml_get: invalid lnum: %" PRId64), (int64_t)lnum);
recursive--;
}
+ ml_flush_line(buf);
+ buf->b_ml.ml_flags &= ~ML_LINE_DIRTY;
errorret:
- STRCPY(IObuff, "???");
- return IObuff;
+ STRCPY(questions, "???");
+ buf->b_ml.ml_line_lnum = lnum;
+ return questions;
}
if (lnum <= 0) { // pretend line 0 is line 1
lnum = 1;
@@ -1927,10 +1903,8 @@ errorret:
return buf->b_ml.ml_line_ptr;
}
-/*
- * Check if a line that was just obtained by a call to ml_get
- * is in allocated memory.
- */
+/// Check if a line that was just obtained by a call to ml_get
+/// is in allocated memory.
int ml_line_alloced(void)
{
return curbuf->b_ml.ml_flags & ML_LINE_DIRTY;
@@ -1951,7 +1925,7 @@ int ml_line_alloced(void)
/// @param newfile flag, see above
///
/// @return FAIL for failure, OK otherwise
-int ml_append(linenr_T lnum, char_u *line, colnr_T len, bool newfile)
+int ml_append(linenr_T lnum, char *line, colnr_T len, bool newfile)
{
// When starting up, we might still need to create the memfile
if (curbuf->b_ml.ml_mfp == NULL && open_buffer(FALSE, NULL, 0) == FAIL) {
@@ -1961,7 +1935,7 @@ int ml_append(linenr_T lnum, char_u *line, colnr_T len, bool newfile)
if (curbuf->b_ml.ml_line_lnum != 0) {
ml_flush_line(curbuf);
}
- return ml_append_int(curbuf, lnum, line, len, newfile, FALSE);
+ return ml_append_int(curbuf, lnum, (char_u *)line, len, newfile, false);
}
/// Like ml_append() but for an arbitrary buffer. The buffer must already have
@@ -2353,95 +2327,88 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b
* We are finished, break the loop here.
*/
break;
- } else { // pointer block full
- /*
- * split the pointer block
- * allocate a new pointer block
- * move some of the pointer into the new block
- * prepare for updating the parent block
- */
- for (;;) { // do this twice when splitting block 1
- hp_new = ml_new_ptr(mfp);
- if (hp_new == NULL) { // TODO: try to fix tree
- return FAIL;
- }
- pp_new = hp_new->bh_data;
-
- if (hp->bh_bnum != 1) {
- break;
- }
-
- /*
- * if block 1 becomes full the tree is given an extra level
- * The pointers from block 1 are moved into the new block.
- * block 1 is updated to point to the new block
- * then continue to split the new block
- */
- memmove(pp_new, pp, (size_t)page_size);
- pp->pb_count = 1;
- pp->pb_pointer[0].pe_bnum = hp_new->bh_bnum;
- pp->pb_pointer[0].pe_line_count = buf->b_ml.ml_line_count;
- pp->pb_pointer[0].pe_old_lnum = 1;
- pp->pb_pointer[0].pe_page_count = 1;
- mf_put(mfp, hp, true, false); // release block 1
- hp = hp_new; // new block is to be split
- pp = pp_new;
- CHECK(stack_idx != 0, _("stack_idx should be 0"));
- ip->ip_index = 0;
- ++stack_idx; // do block 1 again later
- }
- /*
- * move the pointers after the current one to the new block
- * If there are none, the new entry will be in the new block.
- */
- total_moved = pp->pb_count - pb_idx - 1;
- if (total_moved) {
- memmove(&pp_new->pb_pointer[0],
- &pp->pb_pointer[pb_idx + 1],
- (size_t)(total_moved) * sizeof(PTR_EN));
- pp_new->pb_count = total_moved;
- pp->pb_count -= total_moved - 1;
- pp->pb_pointer[pb_idx + 1].pe_bnum = bnum_right;
- pp->pb_pointer[pb_idx + 1].pe_line_count = line_count_right;
- pp->pb_pointer[pb_idx + 1].pe_page_count = page_count_right;
- if (lnum_right) {
- pp->pb_pointer[pb_idx + 1].pe_old_lnum = lnum_right;
- }
- } else {
- pp_new->pb_count = 1;
- pp_new->pb_pointer[0].pe_bnum = bnum_right;
- pp_new->pb_pointer[0].pe_line_count = line_count_right;
- pp_new->pb_pointer[0].pe_page_count = page_count_right;
- pp_new->pb_pointer[0].pe_old_lnum = lnum_right;
- }
- pp->pb_pointer[pb_idx].pe_bnum = bnum_left;
- pp->pb_pointer[pb_idx].pe_line_count = line_count_left;
- pp->pb_pointer[pb_idx].pe_page_count = page_count_left;
- if (lnum_left) {
- pp->pb_pointer[pb_idx].pe_old_lnum = lnum_left;
+ }
+ // pointer block full
+ //
+ // split the pointer block
+ // allocate a new pointer block
+ // move some of the pointer into the new block
+ // prepare for updating the parent block
+ for (;;) { // do this twice when splitting block 1
+ hp_new = ml_new_ptr(mfp);
+ if (hp_new == NULL) { // TODO(vim): try to fix tree
+ return FAIL;
}
- lnum_left = 0;
- lnum_right = 0;
+ pp_new = hp_new->bh_data;
- /*
- * recompute line counts
- */
- line_count_right = 0;
- for (i = 0; i < (int)pp_new->pb_count; ++i) {
- line_count_right += pp_new->pb_pointer[i].pe_line_count;
+ if (hp->bh_bnum != 1) {
+ break;
}
- line_count_left = 0;
- for (i = 0; i < (int)pp->pb_count; ++i) {
- line_count_left += pp->pb_pointer[i].pe_line_count;
+
+ // if block 1 becomes full the tree is given an extra level
+ // The pointers from block 1 are moved into the new block.
+ // block 1 is updated to point to the new block
+ // then continue to split the new block
+ memmove(pp_new, pp, (size_t)page_size);
+ pp->pb_count = 1;
+ pp->pb_pointer[0].pe_bnum = hp_new->bh_bnum;
+ pp->pb_pointer[0].pe_line_count = buf->b_ml.ml_line_count;
+ pp->pb_pointer[0].pe_old_lnum = 1;
+ pp->pb_pointer[0].pe_page_count = 1;
+ mf_put(mfp, hp, true, false); // release block 1
+ hp = hp_new; // new block is to be split
+ pp = pp_new;
+ CHECK(stack_idx != 0, _("stack_idx should be 0"));
+ ip->ip_index = 0;
+ stack_idx++; // do block 1 again later
+ }
+ // move the pointers after the current one to the new block
+ // If there are none, the new entry will be in the new block.
+ total_moved = pp->pb_count - pb_idx - 1;
+ if (total_moved) {
+ memmove(&pp_new->pb_pointer[0],
+ &pp->pb_pointer[pb_idx + 1],
+ (size_t)(total_moved) * sizeof(PTR_EN));
+ pp_new->pb_count = total_moved;
+ pp->pb_count -= total_moved - 1;
+ pp->pb_pointer[pb_idx + 1].pe_bnum = bnum_right;
+ pp->pb_pointer[pb_idx + 1].pe_line_count = line_count_right;
+ pp->pb_pointer[pb_idx + 1].pe_page_count = page_count_right;
+ if (lnum_right) {
+ pp->pb_pointer[pb_idx + 1].pe_old_lnum = lnum_right;
}
+ } else {
+ pp_new->pb_count = 1;
+ pp_new->pb_pointer[0].pe_bnum = bnum_right;
+ pp_new->pb_pointer[0].pe_line_count = line_count_right;
+ pp_new->pb_pointer[0].pe_page_count = page_count_right;
+ pp_new->pb_pointer[0].pe_old_lnum = lnum_right;
+ }
+ pp->pb_pointer[pb_idx].pe_bnum = bnum_left;
+ pp->pb_pointer[pb_idx].pe_line_count = line_count_left;
+ pp->pb_pointer[pb_idx].pe_page_count = page_count_left;
+ if (lnum_left) {
+ pp->pb_pointer[pb_idx].pe_old_lnum = lnum_left;
+ }
+ lnum_left = 0;
+ lnum_right = 0;
- bnum_left = hp->bh_bnum;
- bnum_right = hp_new->bh_bnum;
- page_count_left = 1;
- page_count_right = 1;
- mf_put(mfp, hp, true, false);
- mf_put(mfp, hp_new, true, false);
+ // recompute line counts
+ line_count_right = 0;
+ for (i = 0; i < (int)pp_new->pb_count; i++) {
+ line_count_right += pp_new->pb_pointer[i].pe_line_count;
+ }
+ line_count_left = 0;
+ for (i = 0; i < (int)pp->pb_count; i++) {
+ line_count_left += pp->pb_pointer[i].pe_line_count;
}
+
+ bnum_left = hp->bh_bnum;
+ bnum_right = hp_new->bh_bnum;
+ page_count_left = 1;
+ page_count_right = 1;
+ mf_put(mfp, hp, true, false);
+ mf_put(mfp, hp_new, true, false);
}
/*
@@ -2471,8 +2438,8 @@ void ml_add_deleted_len_buf(buf_T *buf, char_u *ptr, ssize_t len)
if (len == -1) {
len = STRLEN(ptr);
}
- curbuf->deleted_bytes += len+1;
- curbuf->deleted_bytes2 += len+1;
+ curbuf->deleted_bytes += len + 1;
+ curbuf->deleted_bytes2 += len + 1;
if (curbuf->update_need_codepoints) {
mb_utflen(ptr, len, &curbuf->deleted_codepoints,
&curbuf->deleted_codeunits);
@@ -2481,23 +2448,23 @@ void ml_add_deleted_len_buf(buf_T *buf, char_u *ptr, ssize_t len)
}
}
-
-int ml_replace(linenr_T lnum, char_u *line, bool copy)
+int ml_replace(linenr_T lnum, char *line, bool copy)
{
- return ml_replace_buf(curbuf, lnum, line, copy);
+ return ml_replace_buf(curbuf, lnum, (char_u *)line, copy);
}
-// Replace line "lnum", with buffering, in current buffer.
-//
-// If "copy" is true, make a copy of the line, otherwise the line has been
-// copied to allocated memory already.
-// If "copy" is false the "line" may be freed to add text properties!
-// Do not use it after calling ml_replace().
-//
-// Check: The caller of this function should probably also call
-// changed_lines(), unless update_screen(NOT_VALID) is used.
-//
-// return FAIL for failure, OK otherwise
+/// Replace line "lnum", with buffering, in current buffer.
+///
+/// @param copy if true, make a copy of the line, otherwise the line has been
+/// copied to allocated memory already.
+/// if false, the "line" may be freed to add text properties!
+///
+/// Do not use it after calling ml_replace().
+///
+/// Check: The caller of this function should probably also call
+/// changed_lines(), unless update_screen(NOT_VALID) is used.
+///
+/// @return FAIL for failure, OK otherwise
int ml_replace_buf(buf_T *buf, linenr_T lnum, char_u *line, bool copy)
{
if (line == NULL) { // just checking...
@@ -2540,7 +2507,8 @@ int ml_replace_buf(buf_T *buf, linenr_T lnum, char_u *line, bool copy)
/// deleted_lines() after this.
///
/// @param message Show "--No lines in buffer--" message.
-/// @return FAIL for failure, OK otherwise
+///
+/// @return FAIL for failure, OK otherwise
int ml_delete(linenr_T lnum, bool message)
{
ml_flush_line(curbuf);
@@ -2578,7 +2546,7 @@ static int ml_delete_int(buf_T *buf, linenr_T lnum, bool message)
set_keep_msg(_(no_lines_msg), 0);
}
- i = ml_replace((linenr_T)1, (char_u *)"", true);
+ i = ml_replace((linenr_T)1, "", true);
buf->b_ml.ml_flags |= ML_EMPTY;
return i;
@@ -2616,7 +2584,7 @@ static int ml_delete_int(buf_T *buf, linenr_T lnum, bool message)
// Line should always have an NL char internally (represented as NUL),
// even if 'noeol' is set.
assert(line_size >= 1);
- ml_add_deleted_len_buf(buf, (char_u *)dp + line_start, line_size-1);
+ ml_add_deleted_len_buf(buf, (char_u *)dp + line_start, line_size - 1);
/*
* special case: If there is only one line in the data block it becomes empty.
@@ -2697,9 +2665,7 @@ static int ml_delete_int(buf_T *buf, linenr_T lnum, bool message)
return OK;
}
-/*
- * set the B_MARKED flag for line 'lnum'
- */
+/// set the B_MARKED flag for line 'lnum'
void ml_setmarked(linenr_T lnum)
{
bhdr_T *hp;
@@ -2726,9 +2692,7 @@ void ml_setmarked(linenr_T lnum)
curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
}
-/*
- * find the first line with its B_MARKED flag set
- */
+/// find the first line with its B_MARKED flag set
linenr_T ml_firstmarked(void)
{
bhdr_T *hp;
@@ -2769,9 +2733,7 @@ linenr_T ml_firstmarked(void)
return (linenr_T)0;
}
-/*
- * clear all DB_MARKED flags
- */
+/// clear all DB_MARKED flags
void ml_clearmarked(void)
{
bhdr_T *hp;
@@ -2820,9 +2782,7 @@ size_t ml_flush_deleted_bytes(buf_T *buf, size_t *codepoints, size_t *codeunits)
return ret;
}
-/*
- * flush ml_line if necessary
- */
+/// flush ml_line if necessary
static void ml_flush_line(buf_T *buf)
{
bhdr_T *hp;
@@ -2919,9 +2879,7 @@ static void ml_flush_line(buf_T *buf)
buf->b_ml.ml_line_offset = 0;
}
-/*
- * create a new, empty, data block
- */
+/// create a new, empty, data block
static bhdr_T *ml_new_data(memfile_T *mfp, bool negative, int page_count)
{
assert(page_count >= 0);
@@ -2935,9 +2893,7 @@ static bhdr_T *ml_new_data(memfile_T *mfp, bool negative, int page_count)
return hp;
}
-/*
- * create a new, empty, pointer block
- */
+/// create a new, empty, pointer block
static bhdr_T *ml_new_ptr(memfile_T *mfp)
{
bhdr_T *hp = mf_new(mfp, false, 1);
@@ -2949,21 +2905,19 @@ static bhdr_T *ml_new_ptr(memfile_T *mfp)
return hp;
}
-/*
- * lookup line 'lnum' in a memline
- *
- * action: if ML_DELETE or ML_INSERT the line count is updated while searching
- * if ML_FLUSH only flush a locked block
- * if ML_FIND just find the line
- *
- * If the block was found it is locked and put in ml_locked.
- * The stack is updated to lead to the locked block. The ip_high field in
- * the stack is updated to reflect the last line in the block AFTER the
- * insert or delete, also if the pointer block has not been updated yet. But
- * if ml_locked != NULL ml_locked_lineadd must be added to ip_high.
- *
- * return: NULL for failure, pointer to block header otherwise
- */
+/// lookup line 'lnum' in a memline
+///
+/// @param action: if ML_DELETE or ML_INSERT the line count is updated while searching
+/// if ML_FLUSH only flush a locked block
+/// if ML_FIND just find the line
+///
+/// If the block was found it is locked and put in ml_locked.
+/// The stack is updated to lead to the locked block. The ip_high field in
+/// the stack is updated to reflect the last line in the block AFTER the
+/// insert or delete, also if the pointer block has not been updated yet. But
+/// if ml_locked != NULL ml_locked_lineadd must be added to ip_high.
+///
+/// @return NULL for failure, pointer to block header otherwise
static bhdr_T *ml_find_line(buf_T *buf, linenr_T lnum, int action)
{
DATA_BL *dp;
@@ -3144,11 +3098,9 @@ error_noblock:
return NULL;
}
-/*
- * add an entry to the info pointer stack
- *
- * return number of the new entry
- */
+/// add an entry to the info pointer stack
+///
+/// @return number of the new entry
static int ml_add_stack(buf_T *buf)
{
int top = buf->b_ml.ml_stack_top;
@@ -3166,16 +3118,14 @@ static int ml_add_stack(buf_T *buf)
return top;
}
-/*
- * Update the pointer blocks on the stack for inserted/deleted lines.
- * The stack itself is also updated.
- *
- * When an insert/delete line action fails, the line is not inserted/deleted,
- * but the pointer blocks have already been updated. That is fixed here by
- * walking through the stack.
- *
- * Count is the number of lines added, negative if lines have been deleted.
- */
+/// Update the pointer blocks on the stack for inserted/deleted lines.
+/// The stack itself is also updated.
+///
+/// When an insert/delete line action fails, the line is not inserted/deleted,
+/// but the pointer blocks have already been updated. That is fixed here by
+/// walking through the stack.
+///
+/// Count is the number of lines added, negative if lines have been deleted.
static void ml_lineadd(buf_T *buf, int count)
{
int idx;
@@ -3202,13 +3152,13 @@ static void ml_lineadd(buf_T *buf, int count)
}
#if defined(HAVE_READLINK)
-/*
- * Resolve a symlink in the last component of a file name.
- * Note that f_resolve() does it for every part of the path, we don't do that
- * here.
- * If it worked returns OK and the resolved link in "buf[MAXPATHL]".
- * Otherwise returns FAIL.
- */
+
+/// Resolve a symlink in the last component of a file name.
+/// Note that f_resolve() does it for every part of the path, we don't do that
+/// here.
+///
+/// @return OK if it worked and the resolved link in "buf[MAXPATHL]",
+/// FAIL otherwise
int resolve_symlink(const char_u *fname, char_u *buf)
{
char_u tmp[MAXPATHL];
@@ -3255,7 +3205,7 @@ int resolve_symlink(const char_u *fname, char_u *buf)
if (path_is_absolute(buf)) {
STRCPY(tmp, buf);
} else {
- char_u *tail = path_tail(tmp);
+ char_u *tail = (char_u *)path_tail((char *)tmp);
if (STRLEN(tail) + STRLEN(buf) >= MAXPATHL) {
return FAIL;
}
@@ -3272,10 +3222,9 @@ int resolve_symlink(const char_u *fname, char_u *buf)
}
#endif
-/*
- * Make swap file name out of the file name and a directory name.
- * Returns pointer to allocated memory or NULL.
- */
+/// Make swap file name out of the file name and a directory name.
+///
+/// @return pointer to allocated memory or NULL.
char_u *makeswapname(char_u *fname, char_u *ffname, buf_T *buf, char_u *dir_name)
{
char_u *r, *s;
@@ -3335,7 +3284,7 @@ char_u *get_file_in_dir(char_u *fname, char_u *dname)
char_u *retval;
int save_char;
- tail = path_tail(fname);
+ tail = (char_u *)path_tail((char *)fname);
if (dname[0] == '.' && dname[1] == NUL) {
retval = vim_strsave(fname);
@@ -3357,7 +3306,6 @@ char_u *get_file_in_dir(char_u *fname, char_u *dname)
return retval;
}
-
/// Print the ATTENTION message: info about an existing swap file.
///
/// @param buf buffer being edited
@@ -3376,7 +3324,7 @@ static void attention_message(buf_T *buf, char_u *fname)
msg_outtrans(buf->b_fname);
msg_puts("\"\n");
FileInfo file_info;
- if (!os_fileinfo((char *)buf->b_fname, &file_info)) {
+ if (!os_fileinfo(buf->b_fname, &file_info)) {
msg_puts(_(" CANNOT BE FOUND"));
} else {
msg_puts(_(" dated: "));
@@ -3398,24 +3346,22 @@ static void attention_message(buf_T *buf, char_u *fname)
msg_outtrans(buf->b_fname);
msg_puts(_("\"\n to recover the changes (see \":help recovery\").\n"));
msg_puts(_(" If you did this already, delete the swap file \""));
- msg_outtrans(fname);
+ msg_outtrans((char *)fname);
msg_puts(_("\"\n to avoid this message.\n"));
cmdline_row = msg_row;
--no_wait_return;
}
-
-/*
- * Trigger the SwapExists autocommands.
- * Returns a value for equivalent to do_dialog() (see below):
- * 0: still need to ask for a choice
- * 1: open read-only
- * 2: edit anyway
- * 3: recover
- * 4: delete it
- * 5: quit
- * 6: abort
- */
+/// Trigger the SwapExists autocommands.
+///
+/// @return a value for equivalent to do_dialog() (see below):
+/// 0: still need to ask for a choice
+/// 1: open read-only
+/// 2: edit anyway
+/// 3: recover
+/// 4: delete it
+/// 5: quit
+/// 6: abort
static int do_swapexists(buf_T *buf, char_u *fname)
{
set_vim_var_string(VV_SWAPNAME, (char *)fname, -1);
@@ -3476,7 +3422,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
char *fname;
size_t n;
char *dir_name;
- char *buf_fname = (char *)buf->b_fname;
+ char *buf_fname = buf->b_fname;
/*
* Isolate a directory name from *dirp and put it in dir_name.
@@ -3484,12 +3430,12 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
*/
const size_t dir_len = strlen(*dirp) + 1;
dir_name = xmalloc(dir_len);
- (void)copy_option_part((char_u **)dirp, (char_u *)dir_name, dir_len, ",");
+ (void)copy_option_part(dirp, dir_name, dir_len, ",");
/*
* we try different names until we find one that does not exist yet
*/
- fname = (char *)makeswapname((char_u *)buf_fname, buf->b_ffname, buf,
+ fname = (char *)makeswapname((char_u *)buf_fname, (char_u *)buf->b_ffname, buf,
(char_u *)dir_name);
for (;;) {
@@ -3510,7 +3456,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
}
// A file name equal to old_fname is OK to use.
- if (old_fname != NULL && fnamecmp(fname, old_fname) == 0) {
+ if (old_fname != NULL && FNAMECMP(fname, old_fname) == 0) {
break;
}
@@ -3535,14 +3481,14 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
// buffer don't compare the directory names, they can
// have a different mountpoint.
if (b0.b0_flags & B0_SAME_DIR) {
- if (fnamecmp(path_tail(buf->b_ffname),
- path_tail(b0.b0_fname)) != 0
- || !same_directory((char_u *)fname, buf->b_ffname)) {
+ if (FNAMECMP(path_tail((char *)buf->b_ffname),
+ path_tail((char *)b0.b0_fname)) != 0
+ || !same_directory((char_u *)fname, (char_u *)buf->b_ffname)) {
// Symlinks may point to the same file even
// when the name differs, need to check the
// inode too.
expand_env(b0.b0_fname, NameBuff, MAXPATHL);
- if (fnamecmp_ino(buf->b_ffname, NameBuff,
+ if (fnamecmp_ino((char_u *)buf->b_ffname, NameBuff,
char_to_long(b0.b0_ino))) {
differ = TRUE;
}
@@ -3551,7 +3497,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
// The name in the swap file may be
// "~user/path/file". Expand it first.
expand_env(b0.b0_fname, NameBuff, MAXPATHL);
- if (fnamecmp_ino(buf->b_ffname, NameBuff,
+ if (fnamecmp_ino((char_u *)buf->b_ffname, NameBuff,
char_to_long(b0.b0_ino))) {
differ = TRUE;
}
@@ -3563,14 +3509,14 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
// give the ATTENTION message when there is an old swap file
// for the current file, and the buffer was not recovered.
if (differ == false && !(curbuf->b_flags & BF_RECOVERED)
- && vim_strchr(p_shm, SHM_ATTENTION) == NULL) {
+ && vim_strchr((char *)p_shm, SHM_ATTENTION) == NULL) {
int choice = 0;
process_still_running = false;
// It's safe to delete the swap file if all these are true:
// - the edited file exists
// - the swap file has no changes and looks OK
- if (os_path_exists(buf->b_fname) && swapfile_unchanged(fname)) {
+ if (os_path_exists((char_u *)buf->b_fname) && swapfile_unchanged(fname)) {
choice = 4;
if (p_verbose > 0) {
verb_msg(_("Found a swap file that is not useful, deleting it"));
@@ -3581,7 +3527,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
// response, trigger it. It may return 0 to ask the user anyway.
if (choice == 0
&& swap_exists_action != SEA_NONE
- && has_autocmd(EVENT_SWAPEXISTS, (char_u *)buf_fname, buf)) {
+ && has_autocmd(EVENT_SWAPEXISTS, buf_fname, buf)) {
choice = do_swapexists(buf, (char_u *)fname);
}
@@ -3610,8 +3556,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
char *const name = xmalloc(name_len);
memcpy(name, sw_msg_1, sw_msg_1_len + 1);
- home_replace(NULL, (char_u *)fname, (char_u *)&name[sw_msg_1_len],
- fname_len, true);
+ home_replace(NULL, fname, &name[sw_msg_1_len], fname_len, true);
xstrlcat(name, sw_msg_2, name_len);
choice = do_dialog(VIM_WARNING, (char_u *)_("VIM - ATTENTION"),
(char_u *)name,
@@ -3705,7 +3650,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
static int b0_magic_wrong(ZERO_BL *b0p)
{
- return b0p->b0_magic_long != (long)B0_MAGIC_LONG
+ return b0p->b0_magic_long != B0_MAGIC_LONG
|| b0p->b0_magic_int != (int)B0_MAGIC_INT
|| b0p->b0_magic_short != (short)B0_MAGIC_SHORT
|| b0p->b0_magic_char != B0_MAGIC_CHAR;
@@ -3809,10 +3754,8 @@ static bool fnamecmp_ino(char_u *fname_c, char_u *fname_s, long ino_block0)
return true;
}
-/*
- * Move a long integer into a four byte character array.
- * Used for machine independency in block zero.
- */
+/// Move a long integer into a four byte character array.
+/// Used for machine independency in block zero.
static void long_to_char(long n, char_u *s)
{
s[0] = (char_u)(n & 0xff);
@@ -3839,12 +3782,10 @@ static long char_to_long(char_u *s)
return retval;
}
-/*
- * Set the flags in the first block of the swap file:
- * - file is modified or not: buf->b_changed
- * - 'fileformat'
- * - 'fileencoding'
- */
+/// Set the flags in the first block of the swap file:
+/// - file is modified or not: buf->b_changed
+/// - 'fileformat'
+/// - 'fileencoding'
void ml_setflags(buf_T *buf)
{
bhdr_T *hp;
@@ -3870,13 +3811,13 @@ void ml_setflags(buf_T *buf)
#define MLCS_MAXL 800 // max no of lines in chunk
#define MLCS_MINL 400 // should be half of MLCS_MAXL
-/*
- * Keep information for finding byte offset of a line, updtype may be one of:
- * ML_CHNK_ADDLINE: Add len to parent chunk, possibly splitting it
- * Careful: ML_CHNK_ADDLINE may cause ml_find_line() to be called.
- * ML_CHNK_DELLINE: Subtract len from parent chunk, possibly deleting it
- * ML_CHNK_UPDLINE: Add len to parent chunk, as a signed entity.
- */
+/// Keep information for finding byte offset of a line
+///
+/// @param updtype may be one of:
+/// ML_CHNK_ADDLINE: Add len to parent chunk, possibly splitting it
+/// Careful: ML_CHNK_ADDLINE may cause ml_find_line() to be called.
+/// ML_CHNK_DELLINE: Subtract len from parent chunk, possibly deleting it
+/// ML_CHNK_UPDLINE: Add len to parent chunk, as a signed entity.
static void ml_updatechunk(buf_T *buf, linenr_T line, long len, int updtype)
{
static buf_T *ml_upd_lastbuf = NULL;
@@ -3999,10 +3940,8 @@ static void ml_updatechunk(buf_T *buf, linenr_T line, long len, int updtype)
} else if (buf->b_ml.ml_chunksize[curix].mlcs_numlines >= MLCS_MINL
&& curix == buf->b_ml.ml_usedchunks - 1
&& buf->b_ml.ml_line_count - line <= 1) {
- /*
- * We are in the last chunk and it is cheap to crate a new one
- * after this. Do it now to avoid the loop above later on
- */
+ // We are in the last chunk and it is cheap to create a new one
+ // after this. Do it now to avoid the loop above later on
curchnk = buf->b_ml.ml_chunksize + curix + 1;
buf->b_ml.ml_usedchunks++;
if (line == buf->b_ml.ml_line_count) {
@@ -4079,7 +4018,7 @@ static void ml_updatechunk(buf_T *buf, linenr_T line, long len, int updtype)
/// Should be NULL when getting offset of line
/// @param no_ff ignore 'fileformat' option, always use one byte for NL.
///
-/// @return -1 if information is not available
+/// @return -1 if information is not available
long ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp, bool no_ff)
{
linenr_T curline;
@@ -4254,17 +4193,18 @@ void goto_byte(long cnt)
}
/// Increment the line pointer "lp" crossing line boundaries as necessary.
-/// Return 1 when going to the next line.
-/// Return 2 when moving forward onto a NUL at the end of the line).
-/// Return -1 when at the end of file.
-/// Return 0 otherwise.
+///
+/// @return 1 when going to the next line.
+/// 2 when moving forward onto a NUL at the end of the line).
+/// -1 when at the end of file.
+/// 0 otherwise.
int inc(pos_T *lp)
{
// when searching position may be set to end of a line
if (lp->col != MAXCOL) {
const char_u *const p = ml_get_pos(lp);
if (*p != NUL) { // still within line, move to next char (may be NUL)
- const int l = utfc_ptr2len(p);
+ const int l = utfc_ptr2len((char *)p);
lp->col += l;
return ((p[l] != NUL) ? 0 : 2);
diff --git a/src/nvim/memory.c b/src/nvim/memory.c
index 677ff8f522..4d5cf047f9 100644
--- a/src/nvim/memory.c
+++ b/src/nvim/memory.c
@@ -10,10 +10,12 @@
#include "nvim/api/extmark.h"
#include "nvim/context.h"
-#include "nvim/decoration.h"
+#include "nvim/decoration_provider.h"
#include "nvim/eval.h"
#include "nvim/highlight.h"
+#include "nvim/highlight_group.h"
#include "nvim/lua/executor.h"
+#include "nvim/mapping.h"
#include "nvim/memfile.h"
#include "nvim/memory.h"
#include "nvim/message.h"
@@ -523,6 +525,95 @@ void time_to_bytes(time_t time_, uint8_t buf[8])
}
}
+#define ARENA_BLOCK_SIZE 4096
+
+void arena_start(Arena *arena, ArenaMem *reuse_blk)
+{
+ if (reuse_blk && *reuse_blk) {
+ arena->cur_blk = (char *)(*reuse_blk);
+ *reuse_blk = NULL;
+ } else {
+ arena->cur_blk = xmalloc(ARENA_BLOCK_SIZE);
+ }
+ arena->pos = 0;
+ arena->size = ARENA_BLOCK_SIZE;
+ // address is the same as as (struct consumed_blk *)arena->cur_blk
+ struct consumed_blk *blk = arena_alloc(arena, sizeof(struct consumed_blk), true);
+ assert((char *)blk == (char *)arena->cur_blk);
+ blk->prev = NULL;
+}
+
+/// Finnish the allocations in an arena.
+///
+/// This does not immedately free the memory, but leaves existing allocated
+/// objects valid, and returns an opaque ArenaMem handle, which can be used to
+/// free the allocations using `arena_mem_free`, when the objects allocated
+/// from the arena are not needed anymore.
+ArenaMem arena_finish(Arena *arena)
+{
+ struct consumed_blk *res = (struct consumed_blk *)arena->cur_blk;
+ *arena = (Arena)ARENA_EMPTY;
+ return res;
+}
+
+void *arena_alloc(Arena *arena, size_t size, bool align)
+{
+ if (align) {
+ arena->pos = (arena->pos + (ARENA_ALIGN - 1)) & ~(ARENA_ALIGN - 1);
+ }
+ if (arena->pos + size > arena->size) {
+ if (size > (arena->size - sizeof(struct consumed_blk)) >> 1) {
+ // if allocation is too big, allocate a large block with the requested
+ // size, but still with block pointer head. We do this even for
+ // arena->size / 2, as there likely is space left for the next
+ // small allocation in the current block.
+ char *alloc = xmalloc(size + sizeof(struct consumed_blk));
+ struct consumed_blk *cur_blk = (struct consumed_blk *)arena->cur_blk;
+ struct consumed_blk *fix_blk = (struct consumed_blk *)alloc;
+ fix_blk->prev = cur_blk->prev;
+ cur_blk->prev = fix_blk;
+ return (alloc + sizeof(struct consumed_blk));
+ } else {
+ struct consumed_blk *prev_blk = (struct consumed_blk *)arena->cur_blk;
+ arena->cur_blk = xmalloc(ARENA_BLOCK_SIZE);
+ arena->pos = 0;
+ arena->size = ARENA_BLOCK_SIZE;
+ struct consumed_blk *blk = arena_alloc(arena, sizeof(struct consumed_blk), true);
+ blk->prev = prev_blk;
+ }
+ }
+
+ char *mem = arena->cur_blk + arena->pos;
+ arena->pos += size;
+ return mem;
+}
+
+void arena_mem_free(ArenaMem mem, ArenaMem *reuse_blk)
+{
+ struct consumed_blk *b = mem;
+ // peel of the first block, as it is guaranteed to be ARENA_BLOCK_SIZE,
+ // not a custom fix_blk
+ if (reuse_blk && *reuse_blk == NULL && b != NULL) {
+ *reuse_blk = b;
+ b = b->prev;
+ (*reuse_blk)->prev = NULL;
+ }
+
+ while (b) {
+ struct consumed_blk *prev = b->prev;
+ xfree(b);
+ b = prev;
+ }
+}
+
+char *arena_memdupz(Arena *arena, const char *buf, size_t size)
+{
+ char *mem = arena_alloc(arena, size + 1, false);
+ memcpy(mem, buf, size);
+ mem[size] = NUL;
+ return mem;
+}
+
#if defined(EXITFREE)
# include "nvim/buffer.h"
@@ -596,14 +687,13 @@ void free_all_mem(void)
// Clear menus.
do_cmdline_cmd("aunmenu *");
+ do_cmdline_cmd("tlunmenu *");
do_cmdline_cmd("menutranslate clear");
// Clear mappings, abbreviations, breakpoints.
- do_cmdline_cmd("lmapclear");
- do_cmdline_cmd("xmapclear");
- do_cmdline_cmd("mapclear");
- do_cmdline_cmd("mapclear!");
- do_cmdline_cmd("abclear");
+ // NB: curbuf not used with local=false arg
+ map_clear_mode(curbuf, MAP_ALL_MODES, false, false);
+ map_clear_mode(curbuf, MAP_ALL_MODES, false, true);
do_cmdline_cmd("breakdel *");
do_cmdline_cmd("profdel *");
do_cmdline_cmd("set keymap=");
@@ -630,7 +720,6 @@ void free_all_mem(void)
clear_sb_text(true); // free any scrollback text
// Free some global vars.
- xfree(last_mode);
xfree(last_cmdline);
xfree(new_last_cmdline);
set_keep_msg(NULL, 0);
@@ -661,7 +750,6 @@ void free_all_mem(void)
ResetRedobuff();
ResetRedobuff();
-
// highlight info
free_highlight();
@@ -690,13 +778,13 @@ void free_all_mem(void)
bufref_T bufref;
set_bufref(&bufref, buf);
nextbuf = buf->b_next;
- close_buffer(NULL, buf, DOBUF_WIPE, false);
+ close_buffer(NULL, buf, DOBUF_WIPE, false, false);
// Didn't work, try next one.
buf = bufref_valid(&bufref) ? nextbuf : firstbuf;
}
// free screenlines (can't display anything now!)
- screen_free_all_mem();
+ grid_free_all_mem();
clear_hl_tables(false);
list_free_log();
@@ -706,7 +794,7 @@ void free_all_mem(void)
decor_free_all_mem();
nlua_free_all_mem();
+ ui_free_all_mem();
}
#endif
-
diff --git a/src/nvim/memory.h b/src/nvim/memory.h
index a4be2643d8..63d607c2ce 100644
--- a/src/nvim/memory.h
+++ b/src/nvim/memory.h
@@ -37,6 +37,24 @@ extern MemRealloc mem_realloc;
extern bool entered_free_all_mem;
#endif
+typedef struct consumed_blk {
+ struct consumed_blk *prev;
+} *ArenaMem;
+
+#define ARENA_ALIGN sizeof(void *)
+
+typedef struct {
+ char *cur_blk;
+ size_t pos, size;
+} Arena;
+
+// inits an empty arena. use arena_start() to actually allocate space!
+#define ARENA_EMPTY { .cur_blk = NULL, .pos = 0, .size = 0 }
+
+#define kv_fixsize_arena(a, v, s) \
+ ((v).capacity = (s), \
+ (v).items = (void *)arena_alloc(a, sizeof((v).items[0]) * (v).capacity, true))
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "memory.h.generated.h"
#endif
diff --git a/src/nvim/menu.c b/src/nvim/menu.c
index 0db9d69a7e..018c62d604 100644
--- a/src/nvim/menu.c
+++ b/src/nvim/menu.c
@@ -11,6 +11,7 @@
#include <string.h>
#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/eval.h"
@@ -18,10 +19,11 @@
#include "nvim/ex_docmd.h"
#include "nvim/garray.h"
#include "nvim/getchar.h"
-#include "nvim/keymap.h"
+#include "nvim/keycodes.h"
#include "nvim/memory.h"
#include "nvim/menu.h"
#include "nvim/message.h"
+#include "nvim/popupmnu.h"
#include "nvim/screen.h"
#include "nvim/state.h"
#include "nvim/strings.h"
@@ -31,27 +33,24 @@
#define MENUDEPTH 10 // maximum depth of menus
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "menu.c.generated.h"
#endif
-
/// The character for each menu mode
-static char_u menu_mode_chars[] = { 'n', 'v', 's', 'o', 'i', 'c', 't' };
+static char *menu_mode_chars[] = { "n", "v", "s", "o", "i", "c", "tl", "t" };
-static char_u e_notsubmenu[] = N_("E327: Part of menu-item path is not sub-menu");
-static char_u e_othermode[] = N_("E328: Menu only exists in another mode");
-static char_u e_nomenu[] = N_("E329: No menu \"%s\"");
+static char e_notsubmenu[] = N_("E327: Part of menu-item path is not sub-menu");
+static char e_nomenu[] = N_("E329: No menu \"%s\"");
// Return true if "name" is a window toolbar menu name.
-static bool menu_is_winbar(const char_u *const name)
+static bool menu_is_winbar(const char *const name)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
return (STRNCMP(name, "WinBar", 6) == 0);
}
-static vimmenu_T **get_root_menu(const char_u *const name)
+static vimmenu_T **get_root_menu(const char *const name)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
return &root_menu;
@@ -63,20 +62,20 @@ void ex_menu(exarg_T *eap)
{
char *menu_path;
int modes;
- char_u *map_to; // command mapped to the menu entry
+ char *map_to; // command mapped to the menu entry
int noremap;
bool silent = false;
int unmenu;
- char_u *map_buf;
- char_u *arg;
- char_u *p;
+ char *map_buf;
+ char *arg;
+ char *p;
int i;
long pri_tab[MENUDEPTH + 1];
TriState enable = kNone; // kTrue for "menu enable",
// kFalse for "menu disable
vimmenu_T menuarg;
- modes = get_menu_cmd_modes((char *)eap->cmd, eap->forceit, &noremap, &unmenu);
+ modes = get_menu_cmd_modes(eap->cmd, eap->forceit, &noremap, &unmenu);
arg = eap->arg;
for (;;) {
@@ -98,7 +97,6 @@ void ex_menu(exarg_T *eap)
break;
}
-
// Locate an optional "icon=filename" argument
// TODO(nvim): Currently this is only parsed. Should expose it to UIs.
if (STRNCMP(arg, "icon=", 5) == 0) {
@@ -123,7 +121,7 @@ void ex_menu(exarg_T *eap)
}
if (ascii_iswhite(*p)) {
for (i = 0; i < MENUDEPTH && !ascii_iswhite(*arg); i++) {
- pri_tab[i] = getdigits_long(&arg, false, 0);
+ pri_tab[i] = getdigits_long((char_u **)&arg, false, 0);
if (pri_tab[i] == 0) {
pri_tab[i] = 500;
}
@@ -162,8 +160,7 @@ void ex_menu(exarg_T *eap)
return;
}
-
- menu_path = (char *)arg;
+ menu_path = arg;
if (*menu_path == '.') {
semsg(_(e_invarg2), menu_path);
goto theend;
@@ -175,14 +172,14 @@ void ex_menu(exarg_T *eap)
* If there is only a menu name, display menus with that name.
*/
if (*map_to == NUL && !unmenu && enable == kNone) {
- show_menus((char_u *)menu_path, modes);
+ show_menus(menu_path, modes);
goto theend;
} else if (*map_to != NUL && (unmenu || enable != kNone)) {
emsg(_(e_trailing));
goto theend;
}
- vimmenu_T **root_menu_ptr = get_root_menu((char_u *)menu_path);
+ vimmenu_T **root_menu_ptr = get_root_menu(menu_path);
if (enable != kNone) {
// Change sensitivity of the menu.
@@ -201,7 +198,7 @@ void ex_menu(exarg_T *eap)
}
}
}
- menu_enable_recurse(*root_menu_ptr, (char_u *)menu_path, modes, enable);
+ menu_enable_recurse(*root_menu_ptr, menu_path, modes, enable);
} else if (unmenu) {
/*
* Delete menu(s).
@@ -224,25 +221,26 @@ void ex_menu(exarg_T *eap)
}
// Careful: remove_menu() changes menu_path
- remove_menu(root_menu_ptr, (char_u *)menu_path, modes, false);
+ remove_menu(root_menu_ptr, menu_path, modes, false);
} else {
/*
* Add menu(s).
* Replace special key codes.
*/
if (STRICMP(map_to, "<nop>") == 0) { // "<Nop>" means nothing
- map_to = (char_u *)"";
+ map_to = "";
map_buf = NULL;
} else if (modes & MENU_TIP_MODE) {
map_buf = NULL; // Menu tips are plain text.
} else {
- map_to = replace_termcodes(map_to, STRLEN(map_to), &map_buf, false, true,
- true, CPO_TO_CPO_FLAGS);
+ map_buf = NULL;
+ map_to = replace_termcodes(map_to, STRLEN(map_to), &map_buf,
+ REPTERM_DO_LT, NULL, CPO_TO_CPO_FLAGS);
}
menuarg.modes = modes;
menuarg.noremap[0] = noremap;
menuarg.silent[0] = silent;
- add_menu_path((char_u *)menu_path, &menuarg, pri_tab, map_to);
+ add_menu_path(menu_path, &menuarg, pri_tab, map_to);
/*
* For the PopUp menu, add a menu for each mode separately.
@@ -268,35 +266,34 @@ theend:
;
}
-
/// Add the menu with the given name to the menu hierarchy
///
/// @param[out] menuarg menu entry
/// @param[] pri_tab priority table
/// @param[in] call_data Right hand side command
-static int add_menu_path(const char_u *const menu_path, vimmenu_T *menuarg,
- const long *const pri_tab, const char_u *const call_data)
+static int add_menu_path(const char *const menu_path, vimmenu_T *menuarg, const long *const pri_tab,
+ const char *const call_data)
{
- char_u *path_name;
+ char *path_name;
int modes = menuarg->modes;
vimmenu_T *menu = NULL;
vimmenu_T *parent;
vimmenu_T **lower_pri;
- char_u *p;
- char_u *name;
- char_u *dname;
- char_u *next_name;
- char_u c;
- char_u d;
+ char *p;
+ char *name;
+ char *dname;
+ char *next_name;
+ char c;
+ char d;
int i;
int pri_idx = 0;
int old_modes = 0;
int amenu;
- char_u *en_name;
- char_u *map_to = NULL;
+ char *en_name;
+ char *map_to = NULL;
// Make a copy so we can stuff around with it, since it could be const
- path_name = vim_strsave(menu_path);
+ path_name = xstrdup(menu_path);
vimmenu_T **root_menu_ptr = get_root_menu(menu_path);
vimmenu_T **menup = root_menu_ptr;
parent = NULL;
@@ -366,11 +363,11 @@ static int add_menu_path(const char_u *const menu_path, vimmenu_T *menuarg,
menu->modes = modes;
menu->enabled = MENU_ALL_MODES;
- menu->name = vim_strsave(name);
+ menu->name = xstrdup(name);
// separate mnemonic and accelerator text from actual menu name
menu->dname = menu_text(name, &menu->mnemonic, &menu->actext);
if (en_name != NULL) {
- menu->en_name = vim_strsave(en_name);
+ menu->en_name = xstrdup(en_name);
menu->en_dname = menu_text(en_name, NULL, NULL);
} else {
menu->en_name = NULL;
@@ -398,7 +395,6 @@ static int add_menu_path(const char_u *const menu_path, vimmenu_T *menuarg,
}
}
-
menup = &menu->children;
parent = menu;
name = next_name;
@@ -420,7 +416,7 @@ static int add_menu_path(const char_u *const menu_path, vimmenu_T *menuarg,
}
if (menu != NULL && modes) {
- p = (call_data == NULL) ? NULL : vim_strsave(call_data);
+ p = (call_data == NULL) ? NULL : xstrdup(call_data);
// loop over all modes, may add more than one
for (i = 0; i < MENU_MODES; ++i) {
@@ -459,7 +455,6 @@ static int add_menu_path(const char_u *const menu_path, vimmenu_T *menuarg,
if (c == Ctrl_C) {
int len = (int)STRLEN(menu->strings[i]);
- // Append CTRL-\ CTRL-G to obey 'insertmode'.
menu->strings[i][len] = Ctrl_BSL;
menu->strings[i][len + 1] = Ctrl_G;
menu->strings[i][len + 2] = NUL;
@@ -486,8 +481,7 @@ erret:
} else {
menup = &parent->parent->children;
}
- for (; *menup != NULL && *menup != parent; menup = &((*menup)->next)) {
- }
+ for (; *menup != NULL && *menup != parent; menup = &((*menup)->next)) {}
if (*menup == NULL) { // safety check
break;
}
@@ -501,9 +495,9 @@ erret:
* Set the (sub)menu with the given name to enabled or disabled.
* Called recursively.
*/
-static int menu_enable_recurse(vimmenu_T *menu, char_u *name, int modes, int enable)
+static int menu_enable_recurse(vimmenu_T *menu, char *name, int modes, int enable)
{
- char_u *p;
+ char *p;
if (menu == NULL) {
return OK; // Got to bottom of hierarchy
@@ -544,7 +538,6 @@ static int menu_enable_recurse(vimmenu_T *menu, char_u *name, int modes, int ena
return FAIL;
}
-
return OK;
}
@@ -552,11 +545,11 @@ static int menu_enable_recurse(vimmenu_T *menu, char_u *name, int modes, int ena
/// Called recursively.
///
/// @param silent don't give error messages
-static int remove_menu(vimmenu_T **menup, char_u *name, int modes, bool silent)
+static int remove_menu(vimmenu_T **menup, char *name, int modes, bool silent)
{
vimmenu_T *menu;
vimmenu_T *child;
- char_u *p;
+ char *p;
if (*menup == NULL) {
return OK; // Got to bottom of hierarchy
@@ -579,7 +572,7 @@ static int remove_menu(vimmenu_T **menup, char_u *name, int modes, bool silent)
}
} else if (*name != NUL) {
if (!silent) {
- emsg(_(e_othermode));
+ emsg(_(e_menuothermode));
}
return FAIL;
}
@@ -616,7 +609,6 @@ static int remove_menu(vimmenu_T **menup, char_u *name, int modes, bool silent)
return FAIL;
}
-
// Recalculate modes for menu based on the new updated children
menu->modes &= ~modes;
child = menu->children;
@@ -646,7 +638,6 @@ static void free_menu(vimmenu_T **menup)
menu = *menup;
-
// Don't change *menup until after calling gui_mch_destroy_menu(). The
// MacOS code needs the original structure to properly delete the menu.
*menup = menu->next;
@@ -696,23 +687,23 @@ static dict_T *menu_get_recursive(const vimmenu_T *menu, int modes)
}
dict = tv_dict_alloc();
- tv_dict_add_str(dict, S_LEN("name"), (char *)menu->dname);
+ tv_dict_add_str(dict, S_LEN("name"), menu->dname);
tv_dict_add_nr(dict, S_LEN("priority"), (int)menu->priority);
tv_dict_add_nr(dict, S_LEN("hidden"), menu_is_hidden(menu->dname));
if (menu->mnemonic) {
char buf[MB_MAXCHAR + 1] = { 0 }; // > max value of utf8_char2bytes
- utf_char2bytes(menu->mnemonic, (char_u *)buf);
+ utf_char2bytes(menu->mnemonic, buf);
tv_dict_add_str(dict, S_LEN("shortcut"), buf);
}
if (menu->actext) {
- tv_dict_add_str(dict, S_LEN("actext"), (char *)menu->actext);
+ tv_dict_add_str(dict, S_LEN("actext"), menu->actext);
}
if (menu->modes & MENU_TIP_MODE && menu->strings[MENU_INDEX_TIP]) {
tv_dict_add_str(dict, S_LEN("tooltip"),
- (char *)menu->strings[MENU_INDEX_TIP]);
+ menu->strings[MENU_INDEX_TIP]);
}
if (!menu->children) {
@@ -724,7 +715,7 @@ static dict_T *menu_get_recursive(const vimmenu_T *menu, int modes)
if ((menu->modes & modes & (1 << bit)) != 0) {
dict_T *impl = tv_dict_alloc();
tv_dict_add_allocated_str(impl, S_LEN("rhs"),
- str2special_save((char *)menu->strings[bit],
+ str2special_save(menu->strings[bit],
false, false));
tv_dict_add_nr(impl, S_LEN("silent"), menu->silent[bit]);
tv_dict_add_nr(impl, S_LEN("enabled"),
@@ -733,7 +724,7 @@ static dict_T *menu_get_recursive(const vimmenu_T *menu, int modes)
(menu->noremap[bit] & REMAP_NONE) ? 1 : 0);
tv_dict_add_nr(impl, S_LEN("sid"),
(menu->noremap[bit] & REMAP_SCRIPT) ? 1 : 0);
- tv_dict_add_dict(commands, (char *)&menu_mode_chars[bit], 1, impl);
+ tv_dict_add_dict(commands, menu_mode_chars[bit], 1, impl);
}
}
} else {
@@ -750,14 +741,13 @@ static dict_T *menu_get_recursive(const vimmenu_T *menu, int modes)
return dict;
}
-
/// Export menus matching path \p path_name
///
/// @param path_name
/// @param modes supported modes, see \ref MENU_MODES
/// @param[in,out] list must be allocated
/// @return false if could not find path_name
-bool menu_get(char_u *const path_name, int modes, list_T *list)
+bool menu_get(char *const path_name, int modes, list_T *list)
{
vimmenu_T *menu = find_menu(*get_root_menu(path_name), path_name, modes);
if (!menu) {
@@ -777,15 +767,14 @@ bool menu_get(char_u *const path_name, int modes, list_T *list)
return true;
}
-
/// Find menu matching `name` and `modes`.
///
/// @param menu top menu to start looking from
/// @param name path towards the menu
/// @return menu if \p name is null, found menu or NULL
-static vimmenu_T *find_menu(vimmenu_T *menu, char_u *name, int modes)
+static vimmenu_T *find_menu(vimmenu_T *menu, char *name, int modes)
{
- char_u *p;
+ char *p;
while (*name) {
// find the end of one dot-separated name and put a NUL at the dot
@@ -797,7 +786,7 @@ static vimmenu_T *find_menu(vimmenu_T *menu, char_u *name, int modes)
emsg(_(e_notsubmenu));
return NULL;
} else if ((menu->modes & modes) == 0x0) {
- emsg(_(e_othermode));
+ emsg(_(e_menuothermode));
return NULL;
} else if (*p == NUL) { // found a full match
return menu;
@@ -819,7 +808,7 @@ static vimmenu_T *find_menu(vimmenu_T *menu, char_u *name, int modes)
}
/// Show the mapping associated with a menu item or hierarchy in a sub-menu.
-static int show_menus(char_u *const path_name, int modes)
+static int show_menus(char *const path_name, int modes)
{
// First, find the (sub)menu with the given name
vimmenu_T *menu = find_menu(*get_root_menu(path_name), path_name, modes);
@@ -858,7 +847,7 @@ static void show_menus_recursive(vimmenu_T *menu, int modes, int depth)
msg_puts(" ");
}
// Same highlighting as for directories!?
- msg_outtrans_attr(menu->name, HL_ATTR(HLF_D));
+ msg_outtrans_attr((char_u *)menu->name, HL_ATTR(HLF_D));
}
if (menu != NULL && menu->children == NULL) {
@@ -871,7 +860,7 @@ static void show_menus_recursive(vimmenu_T *menu, int modes, int depth)
for (i = 0; i < depth + 2; i++) {
msg_puts(" ");
}
- msg_putchar(menu_mode_chars[bit]);
+ msg_puts(menu_mode_chars[bit]);
if (menu->noremap[bit] == REMAP_NONE) {
msg_putchar('*');
} else if (menu->noremap[bit] == REMAP_SCRIPT) {
@@ -893,7 +882,7 @@ static void show_menus_recursive(vimmenu_T *menu, int modes, int depth)
if (*menu->strings[bit] == NUL) {
msg_puts_attr("<Nop>", HL_ATTR(HLF_8));
} else {
- msg_outtrans_special(menu->strings[bit], false, 0);
+ msg_outtrans_special((char_u *)menu->strings[bit], false, 0);
}
}
}
@@ -914,7 +903,6 @@ static void show_menus_recursive(vimmenu_T *menu, int modes, int depth)
}
}
-
/*
* Used when expanding menu names.
*/
@@ -925,20 +913,19 @@ static int expand_emenu; // TRUE for ":emenu" command
/*
* Work out what to complete when doing command line completion of menu names.
*/
-char_u *set_context_in_menu_cmd(expand_T *xp, const char *cmd, char_u *arg, bool forceit)
+char *set_context_in_menu_cmd(expand_T *xp, const char *cmd, char *arg, bool forceit)
FUNC_ATTR_NONNULL_ALL
{
- char_u *after_dot;
- char_u *p;
- char_u *path_name = NULL;
- char_u *name;
+ char *after_dot;
+ char *p;
+ char *path_name = NULL;
+ char *name;
int unmenu;
vimmenu_T *menu;
int expand_menus;
xp->xp_context = EXPAND_UNSUCCESSFUL;
-
// Check for priority numbers, enable and disable
for (p = arg; *p; ++p) {
if (!ascii_isdigit(*p) && *p != '.') {
@@ -1035,11 +1022,11 @@ char_u *set_context_in_menu_cmd(expand_T *xp, const char *cmd, char_u *arg, bool
* Function given to ExpandGeneric() to obtain the list of (sub)menus (not
* entries).
*/
-char_u *get_menu_name(expand_T *xp, int idx)
+char *get_menu_name(expand_T *xp, int idx)
{
static vimmenu_T *menu = NULL;
- char_u *str;
- static int should_advance = FALSE;
+ char *str;
+ static int should_advance = false;
if (idx == 0) { // first call: start at first item
menu = expand_menu;
@@ -1067,7 +1054,7 @@ char_u *get_menu_name(expand_T *xp, int idx)
}
}
} else {
- str = (char_u *)"";
+ str = "";
}
if (should_advance) {
@@ -1084,12 +1071,12 @@ char_u *get_menu_name(expand_T *xp, int idx)
* Function given to ExpandGeneric() to obtain the list of menus and menu
* entries.
*/
-char_u *get_menu_names(expand_T *xp, int idx)
+char *get_menu_names(expand_T *xp, int idx)
{
static vimmenu_T *menu = NULL;
#define TBUFFER_LEN 256
- static char_u tbuffer[TBUFFER_LEN]; //hack
- char_u *str;
+ static char tbuffer[TBUFFER_LEN]; // hack
+ char *str;
static bool should_advance = false;
if (idx == 0) { // first call: start at first item
@@ -1134,7 +1121,7 @@ char_u *get_menu_names(expand_T *xp, int idx)
}
}
} else {
- str = (char_u *)"";
+ str = "";
}
if (should_advance) {
@@ -1147,15 +1134,14 @@ char_u *get_menu_names(expand_T *xp, int idx)
return str;
}
-
/// Skip over this element of the menu path and return the start of the next
/// element. Any \ and ^Vs are removed from the current element.
///
/// @param name may be modified.
/// @return start of the next element
-char_u *menu_name_skip(char_u *const name)
+char *menu_name_skip(char *const name)
{
- char_u *p;
+ char *p;
for (p = name; *p && *p != '.'; MB_PTR_ADV(p)) {
if (*p == '\\' || *p == Ctrl_V) {
@@ -1175,7 +1161,7 @@ char_u *menu_name_skip(char_u *const name)
* Return TRUE when "name" matches with menu "menu". The name is compared in
* two ways: raw menu name and menu name without '&'. ignore part after a TAB.
*/
-static bool menu_name_equal(const char_u *const name, vimmenu_T *const menu)
+static bool menu_name_equal(const char *const name, vimmenu_T *const menu)
{
if (menu->en_name != NULL
&& (menu_namecmp(name, menu->en_name)
@@ -1185,7 +1171,7 @@ static bool menu_name_equal(const char_u *const name, vimmenu_T *const menu)
return menu_namecmp(name, menu->name) || menu_namecmp(name, menu->dname);
}
-static bool menu_namecmp(const char_u *const name, const char_u *const mname)
+static bool menu_namecmp(const char *const name, const char *const mname)
{
int i;
@@ -1198,7 +1184,6 @@ static bool menu_namecmp(const char_u *const name, const char_u *const mname)
&& (mname[i] == NUL || mname[i] == TAB);
}
-
/// Returns the \ref MENU_MODES specified by menu command `cmd`.
/// (eg :menu! returns MENU_CMDLINE_MODE | MENU_INSERT_MODE)
///
@@ -1229,6 +1214,11 @@ int get_menu_cmd_modes(const char *cmd, bool forceit, int *noremap, int *unmenu)
modes = MENU_INSERT_MODE;
break;
case 't':
+ if (*cmd == 'l') { // tlmenu, tlunmenu, tlnoremenu
+ modes = MENU_TERMINAL_MODE;
+ cmd++;
+ break;
+ }
modes = MENU_TIP_MODE; // tmenu
break;
case 'c': // cmenu
@@ -1270,19 +1260,22 @@ int get_menu_cmd_modes(const char *cmd, bool forceit, int *noremap, int *unmenu)
* Modify a menu name starting with "PopUp" to include the mode character.
* Returns the name in allocated memory.
*/
-static char_u *popup_mode_name(char *name, int idx)
+static char *popup_mode_name(char *name, int idx)
{
size_t len = STRLEN(name);
assert(len >= 4);
- char_u *p = vim_strnsave((char_u *)name, len + 1);
- memmove(p + 6, p + 5, len - 4);
- p[5] = menu_mode_chars[idx];
+ char *mode_chars = menu_mode_chars[idx];
+ size_t mode_chars_len = strlen(mode_chars);
+ char *p = xstrnsave(name, len + mode_chars_len);
+ memmove(p + 5 + mode_chars_len, p + 5, len - 4);
+ for (size_t i = 0; i < mode_chars_len; i++) {
+ p[5 + i] = menu_mode_chars[idx][i];
+ }
return p;
}
-
/// Duplicate the menu item text and then process to see if a mnemonic key
/// and/or accelerator text has been identified.
///
@@ -1294,23 +1287,23 @@ static char_u *popup_mode_name(char *name, int idx)
/// allocated.
///
/// @return a pointer to allocated memory.
-static char_u *menu_text(const char_u *str, int *mnemonic, char_u **actext)
+static char *menu_text(const char *str, int *mnemonic, char **actext)
FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT
FUNC_ATTR_NONNULL_ARG(1)
{
- char_u *p;
- char_u *text;
+ char *p;
+ char *text;
// Locate accelerator text, after the first TAB
p = vim_strchr(str, TAB);
if (p != NULL) {
if (actext != NULL) {
- *actext = vim_strsave(p + 1);
+ *actext = xstrdup(p + 1);
}
assert(p >= str);
- text = vim_strnsave(str, (size_t)(p - str));
+ text = xstrnsave(str, (size_t)(p - str));
} else {
- text = vim_strsave(str);
+ text = xstrdup(str);
}
// Find mnemonic characters "&a" and reduce "&&" to "&".
@@ -1321,7 +1314,7 @@ static char_u *menu_text(const char_u *str, int *mnemonic, char_u **actext)
break;
}
if (mnemonic != NULL && p[1] != '&') {
- *mnemonic = p[1];
+ *mnemonic = (char_u)p[1];
}
STRMOVE(p, p + 1);
p = p + 1;
@@ -1331,7 +1324,7 @@ static char_u *menu_text(const char_u *str, int *mnemonic, char_u **actext)
}
// Return true if "name" can be a menu in the MenuBar.
-bool menu_is_menubar(const char_u *const name)
+bool menu_is_menubar(const char *const name)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
return !menu_is_popup((char *)name)
@@ -1347,9 +1340,8 @@ bool menu_is_popup(const char *const name)
return STRNCMP(name, "PopUp", 5) == 0;
}
-
// Return true if "name" is a toolbar menu name.
-bool menu_is_toolbar(const char_u *const name)
+bool menu_is_toolbar(const char *const name)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
return STRNCMP(name, "ToolBar", 7) == 0;
@@ -1359,92 +1351,156 @@ bool menu_is_toolbar(const char_u *const name)
* Return TRUE if the name is a menu separator identifier: Starts and ends
* with '-'
*/
-int menu_is_separator(char_u *name)
+int menu_is_separator(char *name)
{
return name[0] == '-' && name[STRLEN(name) - 1] == '-';
}
-
/// True if a popup menu or starts with \ref MNU_HIDDEN_CHAR
///
/// @return true if the menu is hidden
-static int menu_is_hidden(char_u *name)
+static int menu_is_hidden(char *name)
{
return (name[0] == MNU_HIDDEN_CHAR)
- || (menu_is_popup((char *)name) && name[5] != NUL);
+ || (menu_is_popup(name) && name[5] != NUL);
}
-// Execute "menu". Use by ":emenu" and the window toolbar.
-// "eap" is NULL for the window toolbar.
-static void execute_menu(const exarg_T *eap, vimmenu_T *menu)
- FUNC_ATTR_NONNULL_ARG(2)
+static int get_menu_mode(void)
{
- int idx = -1;
- char *mode;
-
- // Use the Insert mode entry when returning to Insert mode.
- if (((State & INSERT) || restart_edit) && !current_sctx.sc_sid) {
- mode = "Insert";
- idx = MENU_INDEX_INSERT;
- } else if (State & CMDLINE) {
- mode = "Command";
- idx = MENU_INDEX_CMDLINE;
- } else if (get_real_state() & VISUAL) {
- /* Detect real visual mode -- if we are really in visual mode we
- * don't need to do any guesswork to figure out what the selection
- * is. Just execute the visual binding for the menu. */
- mode = "Visual";
- idx = MENU_INDEX_VISUAL;
- } else if (eap != NULL && eap->addr_count) {
- pos_T tpos;
-
- mode = "Visual";
- idx = MENU_INDEX_VISUAL;
-
- // GEDDES: This is not perfect - but it is a
- // quick way of detecting whether we are doing this from a
- // selection - see if the range matches up with the visual
- // select start and end.
- if ((curbuf->b_visual.vi_start.lnum == eap->line1)
- && (curbuf->b_visual.vi_end.lnum) == eap->line2) {
- // Set it up for visual mode - equivalent to gv.
- VIsual_mode = curbuf->b_visual.vi_mode;
- tpos = curbuf->b_visual.vi_end;
- curwin->w_cursor = curbuf->b_visual.vi_start;
- curwin->w_curswant = curbuf->b_visual.vi_curswant;
- } else {
- // Set it up for line-wise visual mode
- VIsual_mode = 'V';
- curwin->w_cursor.lnum = eap->line1;
- curwin->w_cursor.col = 1;
- tpos.lnum = eap->line2;
- tpos.col = MAXCOL;
- tpos.coladd = 0;
+ if (State & MODE_TERMINAL) {
+ return MENU_INDEX_TERMINAL;
+ }
+ if (VIsual_active) {
+ if (VIsual_select) {
+ return MENU_INDEX_SELECT;
+ }
+ return MENU_INDEX_VISUAL;
+ }
+ if (State & MODE_INSERT) {
+ return MENU_INDEX_INSERT;
+ }
+ if ((State & MODE_CMDLINE) || State == MODE_ASKMORE || State == MODE_HITRETURN) {
+ return MENU_INDEX_CMDLINE;
+ }
+ if (finish_op) {
+ return MENU_INDEX_OP_PENDING;
+ }
+ if (State & MODE_NORMAL) {
+ return MENU_INDEX_NORMAL;
+ }
+ if (State & MODE_LANGMAP) { // must be a "r" command, like Insert mode
+ return MENU_INDEX_INSERT;
+ }
+ return MENU_INDEX_INVALID;
+}
+
+int get_menu_mode_flag(void)
+{
+ int mode = get_menu_mode();
+
+ if (mode == MENU_INDEX_INVALID) {
+ return 0;
+ }
+ return 1 << mode;
+}
+
+/// Display the Special "PopUp" menu as a pop-up at the current mouse
+/// position. The "PopUpn" menu is for Normal mode, "PopUpi" for Insert mode,
+/// etc.
+void show_popupmenu(void)
+{
+ int menu_mode = get_menu_mode();
+ if (menu_mode == MENU_INDEX_INVALID) {
+ return;
+ }
+ char *mode = menu_mode_chars[menu_mode];
+ size_t mode_len = strlen(mode);
+
+ apply_autocmds(EVENT_MENUPOPUP, mode, NULL, false, curbuf);
+
+ vimmenu_T *menu;
+
+ for (menu = root_menu; menu != NULL; menu = menu->next) {
+ if (STRNCMP("PopUp", menu->name, 5) == 0 && STRNCMP(menu->name + 5, mode, mode_len) == 0) {
+ break;
}
+ }
+
+ // Only show a popup when it is defined and has entries
+ if (menu != NULL && menu->children != NULL) {
+ pum_show_popupmenu(menu);
+ }
+}
+
+/// Execute "menu". Use by ":emenu" and the window toolbar.
+/// @param eap NULL for the window toolbar.
+/// @param mode_idx specify a MENU_INDEX_ value, use -1 to depend on the current state
+void execute_menu(const exarg_T *eap, vimmenu_T *menu, int mode_idx)
+ FUNC_ATTR_NONNULL_ARG(2)
+{
+ int idx = mode_idx;
+
+ if (idx < 0) {
+ // Use the Insert mode entry when returning to Insert mode.
+ if (((State & MODE_INSERT) || restart_edit) && !current_sctx.sc_sid) {
+ idx = MENU_INDEX_INSERT;
+ } else if (State & MODE_CMDLINE) {
+ idx = MENU_INDEX_CMDLINE;
+ } else if (State & MODE_TERMINAL) {
+ idx = MENU_INDEX_TERMINAL;
+ } else if (get_real_state() & MODE_VISUAL) {
+ // Detect real visual mode -- if we are really in visual mode we
+ // don't need to do any guesswork to figure out what the selection
+ // is. Just execute the visual binding for the menu.
+ idx = MENU_INDEX_VISUAL;
+ } else if (eap != NULL && eap->addr_count) {
+ pos_T tpos;
+
+ idx = MENU_INDEX_VISUAL;
+
+ // GEDDES: This is not perfect - but it is a
+ // quick way of detecting whether we are doing this from a
+ // selection - see if the range matches up with the visual
+ // select start and end.
+ if ((curbuf->b_visual.vi_start.lnum == eap->line1)
+ && (curbuf->b_visual.vi_end.lnum) == eap->line2) {
+ // Set it up for visual mode - equivalent to gv.
+ VIsual_mode = curbuf->b_visual.vi_mode;
+ tpos = curbuf->b_visual.vi_end;
+ curwin->w_cursor = curbuf->b_visual.vi_start;
+ curwin->w_curswant = curbuf->b_visual.vi_curswant;
+ } else {
+ // Set it up for line-wise visual mode
+ VIsual_mode = 'V';
+ curwin->w_cursor.lnum = eap->line1;
+ curwin->w_cursor.col = 1;
+ tpos.lnum = eap->line2;
+ tpos.col = MAXCOL;
+ tpos.coladd = 0;
+ }
- // Activate visual mode
- VIsual_active = TRUE;
- VIsual_reselect = TRUE;
- check_cursor();
- VIsual = curwin->w_cursor;
- curwin->w_cursor = tpos;
+ // Activate visual mode
+ VIsual_active = true;
+ VIsual_reselect = true;
+ check_cursor();
+ VIsual = curwin->w_cursor;
+ curwin->w_cursor = tpos;
- check_cursor();
+ check_cursor();
- // Adjust the cursor to make sure it is in the correct pos
- // for exclusive mode
- if (*p_sel == 'e' && gchar_cursor() != NUL) {
- curwin->w_cursor.col++;
+ // Adjust the cursor to make sure it is in the correct pos
+ // for exclusive mode
+ if (*p_sel == 'e' && gchar_cursor() != NUL) {
+ curwin->w_cursor.col++;
+ }
}
}
- if (idx == -1 || eap == NULL) {
- mode = "Normal";
+ if (idx == MENU_INDEX_INVALID || eap == NULL) {
idx = MENU_INDEX_NORMAL;
}
- assert(idx != MENU_INDEX_INVALID);
- if (menu->strings[idx] != NULL) {
+ if (menu->strings[idx] != NULL && (menu->modes & (1 << idx))) {
// When executing a script or function execute the commands right now.
// Also for the window toolbar
// Otherwise put them in the typeahead buffer.
@@ -1453,7 +1509,7 @@ static void execute_menu(const exarg_T *eap, vimmenu_T *menu)
ex_normal_busy++;
if (save_current_state(&save_state)) {
- exec_normal_cmd(menu->strings[idx], menu->noremap[idx],
+ exec_normal_cmd((char_u *)menu->strings[idx], menu->noremap[idx],
menu->silent[idx]);
}
restore_current_state(&save_state);
@@ -1463,6 +1519,30 @@ static void execute_menu(const exarg_T *eap, vimmenu_T *menu)
menu->silent[idx]);
}
} else if (eap != NULL) {
+ char *mode;
+ switch (idx) {
+ case MENU_INDEX_VISUAL:
+ mode = "Visual";
+ break;
+ case MENU_INDEX_SELECT:
+ mode = "Select";
+ break;
+ case MENU_INDEX_OP_PENDING:
+ mode = "Op-pending";
+ break;
+ case MENU_INDEX_TERMINAL:
+ mode = "Terminal";
+ break;
+ case MENU_INDEX_INSERT:
+ mode = "Insert";
+ break;
+ case MENU_INDEX_CMDLINE:
+ mode = "Cmdline";
+ break;
+ // case MENU_INDEX_TIP: cannot happen
+ default:
+ mode = "Normal";
+ }
semsg(_("E335: Menu not defined for %s mode"), mode);
}
}
@@ -1471,17 +1551,52 @@ static void execute_menu(const exarg_T *eap, vimmenu_T *menu)
// execute it.
void ex_emenu(exarg_T *eap)
{
- char_u *saved_name = vim_strsave(eap->arg);
+ char *arg = eap->arg;
+ int mode_idx = -1;
+
+ if (arg[0] && ascii_iswhite(arg[1])) {
+ switch (arg[0]) {
+ case 'n':
+ mode_idx = MENU_INDEX_NORMAL;
+ break;
+ case 'v':
+ mode_idx = MENU_INDEX_VISUAL;
+ break;
+ case 's':
+ mode_idx = MENU_INDEX_SELECT;
+ break;
+ case 'o':
+ mode_idx = MENU_INDEX_OP_PENDING;
+ break;
+ case 't':
+ mode_idx = MENU_INDEX_TERMINAL;
+ break;
+ case 'i':
+ mode_idx = MENU_INDEX_INSERT;
+ break;
+ case 'c':
+ mode_idx = MENU_INDEX_CMDLINE;
+ break;
+ default:
+ semsg(_(e_invarg2), arg);
+ return;
+ }
+ arg = skipwhite(arg + 2);
+ }
+
+ char *saved_name = xstrdup(arg);
vimmenu_T *menu = *get_root_menu(saved_name);
- char_u *name = saved_name;
+ char *name = saved_name;
+ bool gave_emsg = false;
while (*name) {
// Find in the menu hierarchy
- char_u *p = menu_name_skip(name);
+ char *p = menu_name_skip(name);
while (menu != NULL) {
if (menu_name_equal(name, menu)) {
if (*p == NUL && menu->children != NULL) {
emsg(_("E333: Menu path must lead to a menu item"));
+ gave_emsg = true;
menu = NULL;
} else if (*p != NUL && menu->children == NULL) {
emsg(_(e_notsubmenu));
@@ -1499,12 +1614,60 @@ void ex_emenu(exarg_T *eap)
}
xfree(saved_name);
if (menu == NULL) {
- semsg(_("E334: Menu not found: %s"), eap->arg);
+ if (!gave_emsg) {
+ semsg(_("E334: Menu not found: %s"), arg);
+ }
return;
}
// Found the menu, so execute.
- execute_menu(eap, menu);
+ execute_menu(eap, menu, mode_idx);
+}
+
+/// Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy.
+vimmenu_T *menu_find(const char *path_name)
+{
+ vimmenu_T *menu = *get_root_menu(path_name);
+ char *saved_name = xstrdup(path_name);
+ char *name = saved_name;
+ while (*name) {
+ // find the end of one dot-separated name and put a NUL at the dot
+ char *p = menu_name_skip(name);
+
+ while (menu != NULL) {
+ if (menu_name_equal(name, menu)) {
+ if (menu->children == NULL) {
+ // found a menu item instead of a sub-menu
+ if (*p == NUL) {
+ emsg(_("E336: Menu path must lead to a sub-menu"));
+ } else {
+ emsg(_(e_notsubmenu));
+ }
+ menu = NULL;
+ goto theend;
+ }
+ if (*p == NUL) { // found a full match
+ goto theend;
+ }
+ break;
+ }
+ menu = menu->next;
+ }
+ if (menu == NULL) { // didn't find it
+ break;
+ }
+
+ // Found a match, search the sub-menu.
+ menu = menu->children;
+ name = p;
+ }
+
+ if (menu == NULL) {
+ emsg(_("E337: Menu not found - check menu names"));
+ }
+theend:
+ xfree(saved_name);
+ return menu;
}
/*
@@ -1512,9 +1675,9 @@ void ex_emenu(exarg_T *eap)
*/
typedef struct {
- char_u *from; // English name
- char_u *from_noamp; // same, without '&'
- char_u *to; // translated name
+ char *from; // English name
+ char *from_noamp; // same, without '&'
+ char *to; // translated name
} menutrans_T;
static garray_T menutrans_ga = GA_EMPTY_INIT_VALUE;
@@ -1532,8 +1695,8 @@ static garray_T menutrans_ga = GA_EMPTY_INIT_VALUE;
*/
void ex_menutranslate(exarg_T *eap)
{
- char_u *arg = eap->arg;
- char_u *from, *from_noamp, *to;
+ char *arg = eap->arg;
+ char *from, *from_noamp, *to;
if (menutrans_ga.ga_itemsize == 0) {
ga_init(&menutrans_ga, (int)sizeof(menutrans_T), 5);
@@ -1557,10 +1720,10 @@ void ex_menutranslate(exarg_T *eap)
if (arg == to) {
emsg(_(e_invarg));
} else {
- from = vim_strsave(from);
+ from = xstrdup(from);
from_noamp = menu_text(from, NULL, NULL);
assert(arg >= to);
- to = vim_strnsave(to, (size_t)(arg - to));
+ to = xstrnsave(to, (size_t)(arg - to));
menu_translate_tab_and_shift(from);
menu_translate_tab_and_shift(to);
menu_unescape_name(from);
@@ -1576,7 +1739,7 @@ void ex_menutranslate(exarg_T *eap)
/*
* Find the character just after one part of a menu name.
*/
-static char_u *menu_skip_part(char_u *p)
+static char *menu_skip_part(char *p)
{
while (*p != NUL && *p != '.' && !ascii_iswhite(*p)) {
if ((*p == '\\' || *p == Ctrl_V) && p[1] != NUL) {
@@ -1591,10 +1754,10 @@ static char_u *menu_skip_part(char_u *p)
* Lookup part of a menu name in the translations.
* Return a pointer to the translation or NULL if not found.
*/
-static char_u *menutrans_lookup(char_u *name, int len)
+static char *menutrans_lookup(char *name, int len)
{
menutrans_T *tp = (menutrans_T *)menutrans_ga.ga_data;
- char_u *dname;
+ char *dname;
for (int i = 0; i < menutrans_ga.ga_len; i++) {
if (STRNICMP(name, tp[i].from, len) == 0 && tp[i].from[len] == NUL) {
@@ -1603,7 +1766,7 @@ static char_u *menutrans_lookup(char_u *name, int len)
}
// Now try again while ignoring '&' characters.
- char_u c = name[len];
+ char c = name[len];
name[len] = NUL;
dname = menu_text(name, NULL, NULL);
name[len] = c;
@@ -1621,9 +1784,9 @@ static char_u *menutrans_lookup(char_u *name, int len)
/*
* Unescape the name in the translate dictionary table.
*/
-static void menu_unescape_name(char_u *name)
+static void menu_unescape_name(char *name)
{
- char_u *p;
+ char *p;
for (p = name; *p && *p != '.'; MB_PTR_ADV(p)) {
if (*p == '\\') {
@@ -1636,9 +1799,9 @@ static void menu_unescape_name(char_u *name)
* Isolate the menu name.
* Skip the menu name, and translate <Tab> into a real TAB.
*/
-static char_u *menu_translate_tab_and_shift(char_u *arg_start)
+static char *menu_translate_tab_and_shift(char *arg_start)
{
- char_u *arg = arg_start;
+ char *arg = arg_start;
while (*arg && !ascii_iswhite(*arg)) {
if ((*arg == '\\' || *arg == Ctrl_V) && arg[1] != NUL) {
@@ -1656,4 +1819,3 @@ static char_u *menu_translate_tab_and_shift(char_u *arg_start)
return arg;
}
-
diff --git a/src/nvim/menu.h b/src/nvim/menu.h
index 5c65918d79..9a60ebfb83 100644
--- a/src/nvim/menu.h
+++ b/src/nvim/menu.h
@@ -18,6 +18,7 @@
#define MENU_OP_PENDING_MODE (1 << MENU_INDEX_OP_PENDING)
#define MENU_INSERT_MODE (1 << MENU_INDEX_INSERT)
#define MENU_CMDLINE_MODE (1 << MENU_INDEX_CMDLINE)
+#define MENU_TERMINAL_MODE (1 << MENU_INDEX_TERMINAL)
#define MENU_TIP_MODE (1 << MENU_INDEX_TIP)
#define MENU_ALL_MODES ((1 << MENU_INDEX_TIP) - 1)
/// @}
diff --git a/src/nvim/message.c b/src/nvim/message.c
index 39b023132e..2c96613bb3 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -18,13 +18,14 @@
#include "nvim/eval.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
+#include "nvim/ex_getln.h"
#include "nvim/fileio.h"
#include "nvim/func_attr.h"
#include "nvim/garray.h"
#include "nvim/getchar.h"
#include "nvim/highlight.h"
#include "nvim/input.h"
-#include "nvim/keymap.h"
+#include "nvim/keycodes.h"
#include "nvim/main.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
@@ -114,7 +115,6 @@ bool keep_msg_more = false; // keep_msg was set by msgmore()
* This is an allocated string or NULL when not used.
*/
-
// Extended msg state, currently used for external UIs with ext_messages
static const char *msg_ext_kind = NULL;
static Array msg_ext_chunks = ARRAY_DICT_INIT;
@@ -125,6 +125,8 @@ static size_t msg_ext_cur_len = 0;
static bool msg_ext_overwrite = false; ///< will overwrite last message
static int msg_ext_visible = 0; ///< number of messages currently visible
+static bool msg_ext_history_visible = false;
+
/// Shouldn't clear message after leaving cmdline
static bool msg_ext_keep_after_cmdline = false;
@@ -134,7 +136,7 @@ static int msg_grid_scroll_discount = 0;
static void ui_ext_msg_set_pos(int row, bool scrolled)
{
char buf[MAX_MCO + 1];
- size_t size = utf_char2bytes(curwin->w_p_fcs_chars.msgsep, (char_u *)buf);
+ size_t size = (size_t)utf_char2bytes(curwin->w_p_fcs_chars.msgsep, buf);
buf[size] = '\0';
ui_call_msg_set_pos(msg_grid.handle, row, scrolled,
(String){ .data = buf, .size = size });
@@ -162,7 +164,8 @@ void msg_grid_validate(void)
{
grid_assign_handle(&msg_grid);
bool should_alloc = msg_use_grid();
- if (should_alloc && (msg_grid.Rows != Rows || msg_grid.Columns != Columns
+ int max_rows = Rows - (int)p_ch;
+ if (should_alloc && (msg_grid.rows != Rows || msg_grid.cols != Columns
|| !msg_grid.chars)) {
// TODO(bfredl): eventually should be set to "invalid". I e all callers
// will use the grid including clear to EOS if necessary.
@@ -170,20 +173,20 @@ void msg_grid_validate(void)
msg_grid.zindex = kZIndexMessages;
xfree(msg_grid.dirty_col);
- msg_grid.dirty_col = xcalloc(Rows, sizeof(*msg_grid.dirty_col));
+ msg_grid.dirty_col = xcalloc((size_t)Rows, sizeof(*msg_grid.dirty_col));
// Tricky: allow resize while pager is active
- int pos = msg_scrolled ? msg_grid_pos : Rows - p_ch;
- ui_comp_put_grid(&msg_grid, pos, 0, msg_grid.Rows, msg_grid.Columns,
+ int pos = msg_scrolled ? msg_grid_pos : max_rows;
+ ui_comp_put_grid(&msg_grid, pos, 0, msg_grid.rows, msg_grid.cols,
false, true);
- ui_call_grid_resize(msg_grid.handle, msg_grid.Columns, msg_grid.Rows);
+ ui_call_grid_resize(msg_grid.handle, msg_grid.cols, msg_grid.rows);
msg_grid.throttled = false; // don't throttle in 'cmdheight' area
msg_scrolled_at_flush = msg_scrolled;
msg_grid.focusable = false;
msg_grid_adj.target = &msg_grid;
if (!msg_scrolled) {
- msg_grid_set_pos(Rows - p_ch, false);
+ msg_grid_set_pos(max_rows, false);
}
} else if (!should_alloc && msg_grid.chars) {
ui_comp_remove_grid(&msg_grid);
@@ -194,8 +197,8 @@ void msg_grid_validate(void)
msg_grid_adj.row_offset = 0;
msg_grid_adj.target = &default_grid;
redraw_cmdline = true;
- } else if (msg_grid.chars && !msg_scrolled && msg_grid_pos != Rows - p_ch) {
- msg_grid_set_pos(Rows - p_ch, false);
+ } else if (msg_grid.chars && !msg_scrolled && msg_grid_pos != max_rows) {
+ msg_grid_set_pos(max_rows, false);
}
if (msg_grid.chars && cmdline_row < msg_grid_pos) {
@@ -206,11 +209,10 @@ void msg_grid_validate(void)
}
}
-/*
- * msg(s) - displays the string 's' on the status line
- * When terminal not initialized (yet) mch_errmsg(..) is used.
- * return TRUE if wait_return not called
- */
+/// Displays the string 's' on the status line
+/// When terminal not initialized (yet) mch_errmsg(..) is used.
+///
+/// @return TRUE if wait_return not called
int msg(char *s)
{
return msg_attr_keep(s, 0, false, false);
@@ -232,7 +234,7 @@ int msg_attr(const char *s, const int attr)
return msg_attr_keep(s, attr, false, false);
}
-/// similar to msg_outtrans_attr, but support newlines and tabs.
+/// Similar to msg_outtrans_attr, but support newlines and tabs.
void msg_multiline_attr(const char *s, int attr, bool check_int, bool *need_clear)
FUNC_ATTR_NONNULL_ALL
{
@@ -246,7 +248,7 @@ void msg_multiline_attr(const char *s, int attr, bool check_int, bool *need_clea
if (next_spec != NULL) {
// Printing all char that are before the char found by strpbrk
- msg_outtrans_len_attr((const char_u *)s, next_spec - s, attr);
+ msg_outtrans_len_attr((const char_u *)s, (int)(next_spec - s), attr);
if (*next_spec != TAB && *need_clear) {
msg_clr_eos();
@@ -262,9 +264,26 @@ void msg_multiline_attr(const char *s, int attr, bool check_int, bool *need_clea
if (*s != NUL) {
msg_outtrans_attr((char_u *)s, attr);
}
- return;
}
+void msg_multiattr(HlMessage hl_msg, const char *kind, bool history)
+{
+ no_wait_return++;
+ msg_start();
+ msg_clr_eos();
+ bool need_clear = false;
+ for (uint32_t i = 0; i < kv_size(hl_msg); i++) {
+ HlMessageChunk chunk = kv_A(hl_msg, i);
+ msg_multiline_attr((const char *)chunk.text.data, chunk.attr,
+ true, &need_clear);
+ }
+ msg_ext_set_kind(kind);
+ if (history && kv_size(hl_msg)) {
+ add_msg_hist_multiattr(NULL, 0, 0, true, hl_msg);
+ }
+ no_wait_return--;
+ msg_end();
+}
/// @param keep set keep_msg if it doesn't scroll
bool msg_attr_keep(const char *s, int attr, bool keep, bool multiline)
@@ -329,18 +348,20 @@ bool msg_attr_keep(const char *s, int attr, bool keep, bool multiline)
}
retval = msg_end();
- if (keep && retval && vim_strsize((char_u *)s) < (int)(Rows - cmdline_row - 1)
- * Columns + sc_col) {
+ if (keep && retval && vim_strsize((char *)s) < (Rows - cmdline_row - 1) * Columns + sc_col) {
set_keep_msg((char *)s, 0);
}
+ need_fileinfo = false;
+
xfree(buf);
--entered;
return retval;
}
/// Truncate a string such that it can be printed without causing a scroll.
-/// Returns an allocated string or NULL when no truncating is done.
+///
+/// @return an allocated string or NULL when no truncating is done.
///
/// @param force always truncate
char_u *msg_strtrunc(char_u *s, int force)
@@ -353,38 +374,43 @@ char_u *msg_strtrunc(char_u *s, int force)
if ((!msg_scroll && !need_wait_return && shortmess(SHM_TRUNCALL)
&& !exmode_active && msg_silent == 0 && !ui_has(kUIMessages))
|| force) {
- len = vim_strsize(s);
+ len = vim_strsize((char *)s);
if (msg_scrolled != 0) {
// Use all the columns.
- room = (int)(Rows - msg_row) * Columns - 1;
+ room = (Rows - msg_row) * Columns - 1;
} else {
// Use up to 'showcmd' column.
- room = (int)(Rows - msg_row - 1) * Columns + sc_col - 1;
+ room = (Rows - msg_row - 1) * Columns + sc_col - 1;
}
if (len > room && room > 0) {
// may have up to 18 bytes per cell (6 per char, up to two
// composing chars)
len = (room + 2) * 18;
- buf = xmalloc(len);
- trunc_string(s, buf, room, len);
+ buf = xmalloc((size_t)len);
+ trunc_string((char *)s, (char *)buf, room, len);
}
}
return buf;
}
-/*
- * Truncate a string "s" to "buf" with cell width "room".
- * "s" and "buf" may be equal.
- */
-void trunc_string(char_u *s, char_u *buf, int room_in, int buflen)
+/// Truncate a string "s" to "buf" with cell width "room".
+/// "s" and "buf" may be equal.
+void trunc_string(char *s, char *buf, int room_in, int buflen)
{
- size_t room = room_in - 3; // "..." takes 3 chars
- size_t half;
- size_t len = 0;
+ int room = room_in - 3; // "..." takes 3 chars
+ int half;
+ int len = 0;
int e;
int i;
int n;
+ if (*s == NUL) {
+ if (buflen > 0) {
+ *buf = NUL;
+ }
+ return;
+ }
+
if (room_in < 3) {
room = 0;
}
@@ -415,7 +441,7 @@ void trunc_string(char_u *s, char_u *buf, int room_in, int buflen)
half = i = (int)STRLEN(s);
for (;;) {
do {
- half = half - utf_head_off(s, s + half - 1) - 1;
+ half = half - utf_head_off((char_u *)s, (char_u *)s + half - 1) - 1;
} while (half > 0 && utf_iscomposing(utf_ptr2char(s + half)));
n = ptr2cells(s + half);
if (len + n > room || half == 0) {
@@ -428,25 +454,25 @@ void trunc_string(char_u *s, char_u *buf, int room_in, int buflen)
if (i <= e + 3) {
// text fits without truncating
if (s != buf) {
- len = STRLEN(s);
- if (len >= (size_t)buflen) {
+ len = (int)STRLEN(s);
+ if (len >= buflen) {
len = buflen - 1;
}
len = len - e + 1;
if (len < 1) {
buf[e - 1] = NUL;
} else {
- memmove(buf + e, s + e, len);
+ memmove(buf + e, s + e, (size_t)len);
}
}
} else if (e + 3 < buflen) {
// set the middle and copy the last part
memmove(buf + e, "...", (size_t)3);
- len = STRLEN(s + i) + 1;
- if (len >= (size_t)buflen - e - 3) {
+ len = (int)STRLEN(s + i) + 1;
+ if (len >= buflen - e - 3) {
len = buflen - e - 3 - 1;
}
- memmove(buf + e + 3, s + i, len);
+ memmove(buf + e + 3, s + i, (size_t)len);
buf[e + 3 + len - 1] = NUL;
} else {
// can't fit in the "...", just truncate it
@@ -499,19 +525,15 @@ int smsg_attr_keep(int attr, const char *s, ...)
static int last_sourcing_lnum = 0;
static char_u *last_sourcing_name = NULL;
-/*
- * Reset the last used sourcing name/lnum. Makes sure it is displayed again
- * for the next error message;
- */
+/// Reset the last used sourcing name/lnum. Makes sure it is displayed again
+/// for the next error message;
void reset_last_sourcing(void)
{
XFREE_CLEAR(last_sourcing_name);
last_sourcing_lnum = 0;
}
-/*
- * Return TRUE if "sourcing_name" differs from "last_sourcing_name".
- */
+/// @return TRUE if "sourcing_name" differs from "last_sourcing_name".
static int other_sourcing_name(void)
{
if (sourcing_name != NULL) {
@@ -561,11 +583,9 @@ static char *get_emsg_lnum(void)
return NULL;
}
-/*
- * Display name and line number for the source of an error.
- * Remember the file name and line number, so that for the next error the info
- * is only displayed if it changed.
- */
+/// Display name and line number for the source of an error.
+/// Remember the file name and line number, so that for the next error the info
+/// is only displayed if it changed.
void msg_source(int attr)
{
no_wait_return++;
@@ -587,22 +607,20 @@ void msg_source(int attr)
if (sourcing_name == NULL) {
last_sourcing_name = NULL;
} else {
- last_sourcing_name = vim_strsave(sourcing_name);
+ last_sourcing_name = vim_strsave((char_u *)sourcing_name);
}
}
--no_wait_return;
}
-/*
- * Return TRUE if not giving error messages right now:
- * If "emsg_off" is set: no error messages at the moment.
- * If "msg" is in 'debug': do error message but without side effects.
- * If "emsg_skip" is set: never do error messages.
- */
+/// @return TRUE if not giving error messages right now:
+/// If "emsg_off" is set: no error messages at the moment.
+/// If "msg" is in 'debug': do error message but without side effects.
+/// If "emsg_skip" is set: never do error messages.
int emsg_not_now(void)
{
- if ((emsg_off > 0 && vim_strchr(p_debug, 'm') == NULL
- && vim_strchr(p_debug, 't') == NULL)
+ if ((emsg_off > 0 && vim_strchr((char *)p_debug, 'm') == NULL
+ && vim_strchr((char *)p_debug, 't') == NULL)
|| emsg_skip > 0) {
return TRUE;
}
@@ -619,24 +637,22 @@ static bool emsg_multiline(const char *s, bool multiline)
return true;
}
- called_emsg = true;
+ called_emsg++;
// If "emsg_severe" is true: When an error exception is to be thrown,
// prefer this message over previous messages for the same command.
bool severe = emsg_severe;
emsg_severe = false;
- if (!emsg_off || vim_strchr(p_debug, 't') != NULL) {
- /*
- * Cause a throw of an error exception if appropriate. Don't display
- * the error message in this case. (If no matching catch clause will
- * be found, the message will be displayed later on.) "ignore" is set
- * when the message should be ignored completely (used for the
- * interrupt message).
- */
- if (cause_errthrow((char_u *)s, severe, &ignore)) {
+ if (!emsg_off || vim_strchr((char *)p_debug, 't') != NULL) {
+ // Cause a throw of an error exception if appropriate. Don't display
+ // the error message in this case. (If no matching catch clause will
+ // be found, the message will be displayed later on.) "ignore" is set
+ // when the message should be ignored completely (used for the
+ // interrupt message).
+ if (cause_errthrow(s, severe, &ignore)) {
if (!ignore) {
- did_emsg++;
+ did_emsg = true;
}
return true;
}
@@ -655,17 +671,17 @@ static bool emsg_multiline(const char *s, bool multiline)
if (p != NULL) {
const size_t p_len = strlen(p);
p[p_len] = '\n';
- redir_write(p, p_len + 1);
+ redir_write(p, (ptrdiff_t)p_len + 1);
xfree(p);
}
p = get_emsg_lnum();
if (p != NULL) {
const size_t p_len = strlen(p);
p[p_len] = '\n';
- redir_write(p, p_len + 1);
+ redir_write(p, (ptrdiff_t)p_len + 1);
xfree(p);
}
- redir_write(s, strlen(s));
+ redir_write(s, (ptrdiff_t)strlen(s));
}
// Log (silent) errors as debug messages.
@@ -701,7 +717,7 @@ static bool emsg_multiline(const char *s, bool multiline)
} else {
flush_buffers(FLUSH_MINIMAL); // flush internal buffers
}
- did_emsg++; // flag for DoOneCmd()
+ did_emsg = true; // flag for DoOneCmd()
}
emsg_on_display = true; // remember there is an error message
@@ -763,7 +779,6 @@ bool semsg_multiline(const char *const fmt, ...)
bool ret;
va_list ap;
-
static char errbuf[MULTILINE_BUFSIZE];
if (emsg_not_now()) {
return true;
@@ -838,13 +853,14 @@ void msg_schedule_semsg(const char *const fmt, ...)
va_end(ap);
char *s = xstrdup((char *)IObuff);
- multiqueue_put(main_loop.events, msg_semsg_event, 1, s);
+ loop_schedule_deferred(&main_loop, event_create(msg_semsg_event, 1, s));
}
-// Like msg(), but truncate to a single line if p_shm contains 't', or when
-// "force" is true. This truncates in another way as for normal messages.
-// Careful: The string may be changed by msg_may_trunc()!
-// Returns a pointer to the printed message, if wait_return() not called.
+/// Like msg(), but truncate to a single line if p_shm contains 't', or when
+/// "force" is true. This truncates in another way as for normal messages.
+/// Careful: The string may be changed by msg_may_trunc()!
+///
+/// @return a pointer to the printed message, if wait_return() not called.
char *msg_trunc_attr(char *s, bool force, int attr)
{
int n;
@@ -864,19 +880,19 @@ char *msg_trunc_attr(char *s, bool force, int attr)
return NULL;
}
-/*
- * Check if message "s" should be truncated at the start (for filenames).
- * Return a pointer to where the truncated message starts.
- * Note: May change the message by replacing a character with '<'.
- */
+/// Check if message "s" should be truncated at the start (for filenames).
+///
+/// @return a pointer to where the truncated message starts.
+///
+/// @note: May change the message by replacing a character with '<'.
char_u *msg_may_trunc(bool force, char_u *s)
{
int room;
- room = (int)(Rows - cmdline_row - 1) * Columns + sc_col - 1;
+ room = (Rows - cmdline_row - 1) * Columns + sc_col - 1;
if ((force || (shortmess(SHM_TRUNC) && !exmode_active))
- && (int)STRLEN(s) - room > 0) {
- int size = vim_strsize(s);
+ && (int)STRLEN(s) - room > 0 && p_ch > 0) {
+ int size = vim_strsize((char *)s);
// There may be room anyway when there are multibyte chars.
if (size <= room) {
@@ -884,8 +900,8 @@ char_u *msg_may_trunc(bool force, char_u *s)
}
int n;
for (n = 0; size >= room;) {
- size -= utf_ptr2cells(s + n);
- n += utfc_ptr2len(s + n);
+ size -= utf_ptr2cells((char *)s + n);
+ n += utfc_ptr2len((char *)s + n);
}
n--;
s += n;
@@ -894,44 +910,34 @@ char_u *msg_may_trunc(bool force, char_u *s)
return s;
}
-void clear_hl_msg(HlMessage *hl_msg)
+void hl_msg_free(HlMessage hl_msg)
{
- for (size_t i = 0; i < kv_size(*hl_msg); i++) {
- xfree(kv_A(*hl_msg, i).text.data);
+ for (size_t i = 0; i < kv_size(hl_msg); i++) {
+ xfree(kv_A(hl_msg, i).text.data);
}
- kv_destroy(*hl_msg);
- *hl_msg = (HlMessage)KV_INITIAL_VALUE;
+ kv_destroy(hl_msg);
}
#define LINE_BUFFER_SIZE 4096
void add_hl_msg_hist(HlMessage hl_msg)
{
- // TODO(notomo): support multi highlighted message history
- size_t pos = 0;
- char buf[LINE_BUFFER_SIZE];
- for (uint32_t i = 0; i < kv_size(hl_msg); i++) {
- HlMessageChunk chunk = kv_A(hl_msg, i);
- for (uint32_t j = 0; j < chunk.text.size; j++) {
- if (pos == LINE_BUFFER_SIZE - 1) {
- buf[pos] = NUL;
- add_msg_hist((const char *)buf, -1, MSG_HIST, true);
- pos = 0;
- continue;
- }
- buf[pos++] = chunk.text.data[j];
- }
- }
- if (pos != 0) {
- buf[pos] = NUL;
- add_msg_hist((const char *)buf, -1, MSG_HIST, true);
+ if (kv_size(hl_msg)) {
+ add_msg_hist_multiattr(NULL, 0, 0, true, hl_msg);
}
}
/// @param[in] len Length of s or -1.
static void add_msg_hist(const char *s, int len, int attr, bool multiline)
{
+ add_msg_hist_multiattr(s, len, attr, multiline, (HlMessage)KV_INITIAL_VALUE);
+}
+
+static void add_msg_hist_multiattr(const char *s, int len, int attr, bool multiline,
+ HlMessage multiattr)
+{
if (msg_hist_off || msg_silent != 0) {
+ hl_msg_free(multiattr);
return;
}
@@ -942,21 +948,26 @@ static void add_msg_hist(const char *s, int len, int attr, bool multiline)
// allocate an entry and add the message at the end of the history
struct msg_hist *p = xmalloc(sizeof(struct msg_hist));
- if (len < 0) {
- len = (int)STRLEN(s);
- }
- // remove leading and trailing newlines
- while (len > 0 && *s == '\n') {
- ++s;
- --len;
- }
- while (len > 0 && s[len - 1] == '\n') {
- len--;
+ if (s) {
+ if (len < 0) {
+ len = (int)STRLEN(s);
+ }
+ // remove leading and trailing newlines
+ while (len > 0 && *s == '\n') {
+ s++;
+ len--;
+ }
+ while (len > 0 && s[len - 1] == '\n') {
+ len--;
+ }
+ p->msg = (char_u *)xmemdupz(s, (size_t)len);
+ } else {
+ p->msg = NULL;
}
- p->msg = (char_u *)xmemdupz(s, (size_t)len);
p->next = NULL;
p->attr = attr;
p->multiline = multiline;
+ p->multiattr = multiattr;
p->kind = msg_ext_kind;
if (last_msg_hist != NULL) {
last_msg_hist->next = p;
@@ -968,10 +979,9 @@ static void add_msg_hist(const char *s, int len, int attr, bool multiline)
msg_hist_len++;
}
-/*
- * Delete the first (oldest) message from the history.
- * Returns FAIL if there are no messages.
- */
+/// Delete the first (oldest) message from the history.
+///
+/// @return FAIL if there are no messages.
int delete_first_msg(void)
{
struct msg_hist *p;
@@ -986,6 +996,7 @@ int delete_first_msg(void)
last_msg_hist = NULL;
}
xfree(p->msg);
+ hl_msg_free(p->multiattr);
xfree(p);
--msg_hist_len;
return OK;
@@ -1013,7 +1024,6 @@ void ex_messages(void *const eap_p)
return;
}
-
p = first_msg_hist;
if (eap->addr_count != 0) {
@@ -1025,31 +1035,48 @@ void ex_messages(void *const eap_p)
c -= eap->line2;
// Skip without number of messages specified
- for (p = first_msg_hist; p != NULL && !got_int && c > 0; p = p->next, c--) {
- }
+ for (p = first_msg_hist; p != NULL && !got_int && c > 0; p = p->next, c--) {}
}
// Display what was not skipped.
if (ui_has(kUIMessages)) {
+ if (msg_silent) {
+ return;
+ }
Array entries = ARRAY_DICT_INIT;
for (; p != NULL; p = p->next) {
- if (p->msg != NULL && p->msg[0] != NUL) {
+ if (kv_size(p->multiattr) || (p->msg && p->msg[0])) {
Array entry = ARRAY_DICT_INIT;
ADD(entry, STRING_OBJ(cstr_to_string(p->kind)));
- Array content_entry = ARRAY_DICT_INIT;
- ADD(content_entry, INTEGER_OBJ(p->attr));
- ADD(content_entry, STRING_OBJ(cstr_to_string((char *)(p->msg))));
Array content = ARRAY_DICT_INIT;
- ADD(content, ARRAY_OBJ(content_entry));
+ if (kv_size(p->multiattr)) {
+ for (uint32_t i = 0; i < kv_size(p->multiattr); i++) {
+ HlMessageChunk chunk = kv_A(p->multiattr, i);
+ Array content_entry = ARRAY_DICT_INIT;
+ ADD(content_entry, INTEGER_OBJ(chunk.attr));
+ ADD(content_entry, STRING_OBJ(copy_string(chunk.text)));
+ ADD(content, ARRAY_OBJ(content_entry));
+ }
+ } else if (p->msg && p->msg[0]) {
+ Array content_entry = ARRAY_DICT_INIT;
+ ADD(content_entry, INTEGER_OBJ(p->attr));
+ ADD(content_entry, STRING_OBJ(cstr_to_string((char *)(p->msg))));
+ ADD(content, ARRAY_OBJ(content_entry));
+ }
ADD(entry, ARRAY_OBJ(content));
ADD(entries, ARRAY_OBJ(entry));
}
}
ui_call_msg_history_show(entries);
+ api_free_array(entries);
+ msg_ext_history_visible = true;
+ wait_return(false);
} else {
msg_hist_off = true;
for (; p != NULL && !got_int; p = p->next) {
- if (p->msg != NULL) {
+ if (kv_size(p->multiattr)) {
+ msg_multiattr(p->multiattr, p->kind, false);
+ } else if (p->msg != NULL) {
msg_attr_keep((char *)p->msg, p->attr, false, p->multiline);
}
}
@@ -1057,10 +1084,8 @@ void ex_messages(void *const eap_p)
}
}
-/*
- * Call this after prompting the user. This will avoid a hit-return message
- * and a delay.
- */
+/// Call this after prompting the user. This will avoid a hit-return message
+/// and a delay.
void msg_end_prompt(void)
{
msg_ext_clear_later();
@@ -1074,9 +1099,9 @@ void msg_end_prompt(void)
/// Wait for the user to hit a key (normally Enter)
///
-/// If 'redraw' is true, redraw the entire screen NOT_VALID
-/// If 'redraw' is false, do a normal redraw
-/// If 'redraw' is -1, don't redraw at all
+/// @param redraw if true, redraw the entire screen NOT_VALID
+/// if false, do a normal redraw
+/// if -1, don't redraw at all
void wait_return(int redraw)
{
int c;
@@ -1095,6 +1120,10 @@ void wait_return(int redraw)
return;
}
+ if (headless_mode && !ui_active()) {
+ return;
+ }
+
/*
* When inside vgetc(), we can't wait for a typed character at all.
* With the global command (and some others) we only need one return at
@@ -1127,7 +1156,7 @@ void wait_return(int redraw)
// just changed.
screenalloc();
- State = HITRETURN;
+ State = MODE_HITRETURN;
setmouse();
cmdline_row = msg_row;
// Avoid the sequence that the user types ":" at the hit-return prompt
@@ -1147,6 +1176,7 @@ void wait_return(int redraw)
// Don't do mappings here, we put the character back in the
// typeahead buffer.
no_mapping++;
+ allow_keys++;
// Temporarily disable Recording. If Recording is active, the
// character will be recorded later, since it will be added to the
@@ -1160,10 +1190,10 @@ void wait_return(int redraw)
got_int = false;
}
no_mapping--;
+ allow_keys--;
reg_recording = save_reg_recording;
scriptout = save_scriptout;
-
/*
* Allow scrolling back in the messages.
* Also accept scroll-down commands when messages fill the screen,
@@ -1212,10 +1242,10 @@ void wait_return(int redraw)
if (c == K_LEFTMOUSE || c == K_MIDDLEMOUSE || c == K_RIGHTMOUSE
|| c == K_X1MOUSE || c == K_X2MOUSE) {
(void)jump_to_mouse(MOUSE_SETPOS, NULL, 0);
- } else if (vim_strchr((char_u *)"\r\n ", c) == NULL && c != Ctrl_C) {
+ } else if (vim_strchr("\r\n ", c) == NULL && c != Ctrl_C) {
// Put the character back in the typeahead buffer. Don't use the
// stuff buffer, because lmaps wouldn't work.
- ins_char_typebuf(c);
+ ins_char_typebuf(vgetc_char, vgetc_mod_mask);
do_redraw = true; // need a redraw even though there is
// typeahead
}
@@ -1233,11 +1263,11 @@ void wait_return(int redraw)
msg_ext_keep_after_cmdline = true;
}
- // If the window size changed set_shellsize() will redraw the screen.
+ // If the screen size changed screen_resize() will redraw the screen.
// Otherwise the screen is only redrawn if 'redraw' is set and no ':'
// typed.
tmpState = State;
- State = oldState; // restore State before set_shellsize
+ State = oldState; // restore State before screen_resize()
setmouse();
msg_check();
need_wait_return = false;
@@ -1245,12 +1275,12 @@ void wait_return(int redraw)
emsg_on_display = false; // can delete error message now
lines_left = -1; // reset lines_left at next msg_start()
reset_last_sourcing();
- if (keep_msg != NULL && vim_strsize(keep_msg) >=
+ if (keep_msg != NULL && vim_strsize((char *)keep_msg) >=
(Rows - cmdline_row - 1) * Columns + sc_col) {
XFREE_CLEAR(keep_msg); // don't redisplay message, it's too long
}
- if (tmpState == SETWSIZE) { // got resize event while in vgetc()
+ if (tmpState == MODE_SETWSIZE) { // got resize event while in vgetc()
ui_refresh();
} else if (!skip_redraw) {
if (redraw == true || (msg_scrolled != 0 && redraw != -1)) {
@@ -1262,9 +1292,7 @@ void wait_return(int redraw)
}
}
-/*
- * Write the hit-return prompt.
- */
+/// Write the hit-return prompt.
static void hit_return_msg(void)
{
int save_p_more = p_more;
@@ -1285,9 +1313,7 @@ static void hit_return_msg(void)
p_more = save_p_more;
}
-/*
- * Set "keep_msg" to "s". Free the old value and check for NULL pointer.
- */
+/// Set "keep_msg" to "s". Free the old value and check for NULL pointer.
void set_keep_msg(char *s, int attr)
{
xfree(keep_msg);
@@ -1342,7 +1368,6 @@ void msgmore(long n)
}
}
-
void msg_ext_set_kind(const char *msg_kind)
{
// Don't change the label of an existing batch:
@@ -1354,18 +1379,19 @@ void msg_ext_set_kind(const char *msg_kind)
msg_ext_kind = msg_kind;
}
-/*
- * Prepare for outputting characters in the command line.
- */
+/// Prepare for outputting characters in the command line.
void msg_start(void)
{
int did_return = false;
if (!msg_silent) {
XFREE_CLEAR(keep_msg); // don't display old message now
+ need_fileinfo = false;
}
- if (need_clr_eos) {
+ bool no_msg_area = !ui_has(kUIMessages) && p_ch < 1;
+
+ if (need_clr_eos || (no_msg_area && redrawing_cmdline)) {
// Halfway an ":echo" command and getting an (error) message: clear
// any text from the command.
need_clr_eos = false;
@@ -1374,10 +1400,11 @@ void msg_start(void)
if (!msg_scroll && full_screen) { // overwrite last message
msg_row = cmdline_row;
- msg_col =
- cmdmsg_rl ? Columns - 1 :
- 0;
- } else if (msg_didout) { // start message on next line
+ msg_col = cmdmsg_rl ? Columns - 1 : 0;
+ if (no_msg_area && get_cmdprompt() == NULL) {
+ msg_row -= 1;
+ }
+ } else if (msg_didout || no_msg_area) { // start message on next line
msg_putchar('\n');
did_return = true;
cmdline_row = msg_row;
@@ -1403,9 +1430,7 @@ void msg_start(void)
}
}
-/*
- * Note that the current msg position is where messages start.
- */
+/// Note that the current msg position is where messages start.
void msg_starthere(void)
{
lines_left = cmdline_row;
@@ -1419,7 +1444,7 @@ void msg_putchar(int c)
void msg_putchar_attr(int c, int attr)
{
- char_u buf[MB_MAXBYTES + 1];
+ char buf[MB_MAXBYTES + 1];
if (IS_SPECIAL(c)) {
buf[0] = (char)K_SPECIAL;
@@ -1427,7 +1452,7 @@ void msg_putchar_attr(int c, int attr)
buf[2] = (char)K_THIRD(c);
buf[3] = NUL;
} else {
- buf[utf_char2bytes(c, buf)] = NUL;
+ buf[utf_char2bytes(c, (char *)buf)] = NUL;
}
msg_puts_attr((const char *)buf, attr);
}
@@ -1452,22 +1477,19 @@ void msg_home_replace_hl(char_u *fname)
static void msg_home_replace_attr(char_u *fname, int attr)
{
- char_u *name;
-
- name = home_replace_save(NULL, fname);
- msg_outtrans_attr(name, attr);
+ char *name = home_replace_save(NULL, (char *)fname);
+ msg_outtrans_attr((char_u *)name, attr);
xfree(name);
}
-/*
- * Output 'len' characters in 'str' (including NULs) with translation
- * if 'len' is -1, output up to a NUL character.
- * Use attributes 'attr'.
- * Return the number of characters it takes on the screen.
- */
-int msg_outtrans(char_u *str)
+/// Output 'len' characters in 'str' (including NULs) with translation
+/// if 'len' is -1, output up to a NUL character.
+/// Use attributes 'attr'.
+///
+/// @return the number of characters it takes on the screen.
+int msg_outtrans(char *str)
{
- return msg_outtrans_attr(str, 0);
+ return msg_outtrans_attr((char_u *)str, 0);
}
int msg_outtrans_attr(const char_u *str, int attr)
@@ -1480,15 +1502,15 @@ int msg_outtrans_len(const char_u *str, int len)
return msg_outtrans_len_attr(str, len, 0);
}
-/*
- * Output one character at "p". Return pointer to the next character.
- * Handles multi-byte characters.
- */
+/// Output one character at "p".
+/// Handles multi-byte characters.
+///
+/// @return pointer to the next character.
char_u *msg_outtrans_one(char_u *p, int attr)
{
int l;
- if ((l = utfc_ptr2len(p)) > 1) {
+ if ((l = utfc_ptr2len((char *)p)) > 1) {
msg_outtrans_len_attr(p, l, attr);
return p + l;
}
@@ -1504,6 +1526,10 @@ int msg_outtrans_len_attr(const char_u *msgstr, int len, int attr)
char_u *s;
int mb_l;
int c;
+ int save_got_int = got_int;
+
+ // Only quit when got_int was set in here.
+ got_int = false;
// if MSG_HIST flag set, add message to history
if (attr & MSG_HIST) {
@@ -1513,7 +1539,7 @@ int msg_outtrans_len_attr(const char_u *msgstr, int len, int attr)
// If the string starts with a composing character first draw a space on
// which the composing char can be drawn.
- if (utf_iscomposing(utf_ptr2char(msgstr))) {
+ if (utf_iscomposing(utf_ptr2char((char *)msgstr))) {
msg_puts_attr(" ", attr);
}
@@ -1521,14 +1547,14 @@ int msg_outtrans_len_attr(const char_u *msgstr, int len, int attr)
* Go over the string. Special characters are translated and printed.
* Normal characters are printed several at a time.
*/
- while (--len >= 0) {
+ while (--len >= 0 && !got_int) {
// Don't include composing chars after the end.
mb_l = utfc_ptr2len_len((char_u *)str, len + 1);
if (mb_l > 1) {
- c = utf_ptr2char((char_u *)str);
+ c = utf_ptr2char(str);
if (vim_isprintc(c)) {
// Printable multi-byte char: count the cells.
- retval += utf_ptr2cells((char_u *)str);
+ retval += utf_ptr2cells(str);
} else {
// Unprintable multi-byte char: print the printable chars so
// far and the translation of the unprintable char.
@@ -1560,11 +1586,13 @@ int msg_outtrans_len_attr(const char_u *msgstr, int len, int attr)
}
}
- if (str > plain_start) {
+ if (str > plain_start && !got_int) {
// Print the printable chars at the end.
msg_puts_attr_len(plain_start, str - plain_start, attr);
}
+ got_int |= save_got_int;
+
return retval;
}
@@ -1573,8 +1601,8 @@ void msg_make(char_u *arg)
int i;
static char_u *str = (char_u *)"eeffoc", *rs = (char_u *)"Plon#dqg#vxjduB";
- arg = skipwhite(arg);
- for (i = 5; *arg && i >= 0; --i) {
+ arg = (char_u *)skipwhite((char *)arg);
+ for (i = 5; *arg && i >= 0; i--) {
if (*arg++ != str[i]) {
break;
}
@@ -1620,13 +1648,17 @@ int msg_outtrans_special(const char_u *strstart, bool from, int maxlen)
} else {
text = str2special((const char **)&str, from, false);
}
- const int len = vim_strsize((char_u *)text);
+ if (text[0] != NUL && text[1] == NUL) {
+ // single-byte character or illegal byte
+ text = (char *)transchar_byte((uint8_t)text[0]);
+ }
+ const int len = vim_strsize((char *)text);
if (maxlen > 0 && retval + len >= maxlen) {
break;
}
// Highlight special keys
msg_puts_attr(text, (len > 1
- && utfc_ptr2len((char_u *)text) <= 1
+ && utfc_ptr2len(text) <= 1
? attr : 0));
retval += len;
}
@@ -1668,16 +1700,19 @@ char *str2special_save(const char *const str, const bool replace_spaces, const b
/// @return Converted key code, in a static buffer. Buffer is always one and the
/// same, so save converted string somewhere before running str2special
/// for the second time.
+/// On illegal byte return a string with only that byte.
const char *str2special(const char **const sp, const bool replace_spaces, const bool replace_lt)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET
{
static char buf[7];
- // Try to un-escape a multi-byte character. Return the un-escaped
- // string if it is a multi-byte character.
- const char *const p = mb_unescape(sp);
- if (p != NULL) {
- return p;
+ {
+ // Try to un-escape a multi-byte character. Return the un-escaped
+ // string if it is a multi-byte character.
+ const char *const p = mb_unescape(sp);
+ if (p != NULL) {
+ return p;
+ }
}
const char *str = *sp;
@@ -1699,26 +1734,26 @@ const char *str2special(const char **const sp, const bool replace_spaces, const
}
}
- if (!IS_SPECIAL(c)) {
- const int len = utf_ptr2len((const char_u *)str);
-
- // Check for an illegal byte.
- if (MB_BYTE2LEN((uint8_t)(*str)) > len) {
- transchar_nonprint(curbuf, (char_u *)buf, c);
+ if (!IS_SPECIAL(c) && MB_BYTE2LEN(c) > 1) {
+ *sp = str;
+ // Try to un-escape a multi-byte character after modifiers.
+ const char *p = mb_unescape(sp);
+ if (p != NULL) {
+ // Since 'special' is true the multi-byte character 'c' will be
+ // processed by get_special_key_name().
+ c = utf_ptr2char(p);
+ } else {
+ // illegal byte
*sp = str + 1;
- return buf;
}
- // Since 'special' is TRUE the multi-byte character 'c' will be
- // processed by get_special_key_name().
- c = utf_ptr2char((const char_u *)str);
- *sp = str + len;
} else {
+ // single-byte character or illegal byte
*sp = str + 1;
}
- // Make unprintable characters in <> form, also <M-Space> and <Tab>.
+ // Make special keys and C0 control characters in <> form, also <M-Space>.
if (special
- || char2cells(c) > 1
+ || c < ' '
|| (replace_spaces && c == ' ')
|| (replace_lt && c == '<')) {
return (const char *)get_special_key_name(c, modifiers);
@@ -1749,9 +1784,7 @@ void str2specialbuf(const char *sp, char *buf, size_t len)
*buf = NUL;
}
-/*
- * print line for :print or :list command
- */
+/// print line for :print or :list command
void msg_prt_line(char_u *s, int list)
{
int c;
@@ -1781,7 +1814,7 @@ void msg_prt_line(char_u *s, int list)
}
}
// find end of leading whitespace
- if (curwin->w_p_lcs_chars.lead) {
+ if (curwin->w_p_lcs_chars.lead || curwin->w_p_lcs_chars.leadmultispace != NULL) {
lead = s;
while (ascii_iswhite(lead[0])) {
lead++;
@@ -1809,16 +1842,16 @@ void msg_prt_line(char_u *s, int list)
assert(p_extra != NULL);
c = *p_extra++;
}
- } else if ((l = utfc_ptr2len(s)) > 1) {
- col += utf_ptr2cells(s);
+ } else if ((l = utfc_ptr2len((char *)s)) > 1) {
+ col += utf_ptr2cells((char *)s);
char buf[MB_MAXBYTES + 1];
if (l >= MB_MAXBYTES) {
xstrlcpy(buf, "?", sizeof(buf));
} else if (curwin->w_p_lcs_chars.nbsp != NUL && list
- && (utf_ptr2char(s) == 160
- || utf_ptr2char(s) == 0x202f)) {
- utf_char2bytes(curwin->w_p_lcs_chars.nbsp, (char_u *)buf);
- buf[utfc_ptr2len((char_u *)buf)] = NUL;
+ && (utf_ptr2char((char *)s) == 160
+ || utf_ptr2char((char *)s) == 0x202f)) {
+ int len = utf_char2bytes(curwin->w_p_lcs_chars.nbsp, buf);
+ buf[len] = NUL;
} else {
memmove(buf, s, (size_t)l);
buf[l] = NUL;
@@ -1871,13 +1904,21 @@ void msg_prt_line(char_u *s, int list)
// the same in plain text.
attr = HL_ATTR(HLF_0);
} else if (c == ' ') {
- if (lead != NULL && s <= lead) {
+ if (list && lead != NULL && s <= lead && in_multispace
+ && curwin->w_p_lcs_chars.leadmultispace != NULL) {
+ c = curwin->w_p_lcs_chars.leadmultispace[multispace_pos++];
+ if (curwin->w_p_lcs_chars.leadmultispace[multispace_pos] == NUL) {
+ multispace_pos = 0;
+ }
+ attr = HL_ATTR(HLF_0);
+ } else if (lead != NULL && s <= lead && curwin->w_p_lcs_chars.lead != NUL) {
c = curwin->w_p_lcs_chars.lead;
attr = HL_ATTR(HLF_0);
} else if (trail != NULL && s > trail) {
c = curwin->w_p_lcs_chars.trail;
attr = HL_ATTR(HLF_0);
- } else if (list && in_multispace && curwin->w_p_lcs_chars.multispace != NULL) {
+ } else if (list && in_multispace
+ && curwin->w_p_lcs_chars.multispace != NULL) {
c = curwin->w_p_lcs_chars.multispace[multispace_pos++];
if (curwin->w_p_lcs_chars.multispace[multispace_pos] == NUL) {
multispace_pos = 0;
@@ -1900,15 +1941,16 @@ void msg_prt_line(char_u *s, int list)
msg_clr_eos();
}
-// Use grid_puts() to output one multi-byte character.
-// Return the pointer "s" advanced to the next character.
+/// Use grid_puts() to output one multi-byte character.
+///
+/// @return the pointer "s" advanced to the next character.
static char_u *screen_puts_mbyte(char_u *s, int l, int attr)
{
int cw;
attr = hl_combine_attr(HL_ATTR(HLF_MSG), attr);
msg_didout = true; // remember that line is not empty
- cw = utf_ptr2cells(s);
+ cw = utf_ptr2cells((char *)s);
if (cw > 1
&& (cmdmsg_rl ? msg_col <= 1 : msg_col == Columns - 1)) {
// Doesn't fit, print a highlighted '>' to fill it up.
@@ -1933,10 +1975,8 @@ static char_u *screen_puts_mbyte(char_u *s, int l, int attr)
return s + l;
}
-/*
- * Output a string to the screen at position msg_row, msg_col.
- * Update msg_row and msg_col for the next message.
- */
+/// Output a string to the screen at position msg_row, msg_col.
+/// Update msg_row and msg_col for the next message.
void msg_puts(const char *s)
{
msg_puts_attr(s, 0);
@@ -1947,11 +1987,9 @@ void msg_puts_title(const char *s)
msg_puts_attr(s, HL_ATTR(HLF_T));
}
-/*
- * Show a message in such a way that it always fits in the line. Cut out a
- * part in the middle and replace it with "..." when necessary.
- * Does not handle multi-byte characters!
- */
+/// Show a message in such a way that it always fits in the line. Cut out a
+/// part in the middle and replace it with "..." when necessary.
+/// Does not handle multi-byte characters!
void msg_outtrans_long_attr(char_u *longstr, int attr)
{
msg_outtrans_long_len_attr(longstr, (int)STRLEN(longstr), attr);
@@ -1971,9 +2009,7 @@ void msg_outtrans_long_len_attr(char_u *longstr, int len, int attr)
msg_outtrans_len_attr(longstr + len - slen, slen, attr);
}
-/*
- * Basic function for writing a message with highlight attributes.
- */
+/// Basic function for writing a message with highlight attributes.
void msg_puts_attr(const char *const s, const int attr)
{
msg_puts_attr_len(s, -1, attr);
@@ -1987,7 +2023,7 @@ void msg_puts_attr(const char *const s, const int attr)
void msg_puts_attr_len(const char *const str, const ptrdiff_t len, int attr)
FUNC_ATTR_NONNULL_ALL
{
- assert(len < 0 || memchr(str, 0, len) == NULL);
+ assert(len < 0 || memchr(str, 0, (size_t)len) == NULL);
// If redirection is on, also write to the redirection file.
redir_write(str, len);
@@ -2036,8 +2072,10 @@ void msg_puts_attr_len(const char *const str, const ptrdiff_t len, int attr)
}
}
if (!msg_use_printf() || (headless_mode && default_grid.chars)) {
- msg_puts_display((const char_u *)str, len, attr, false);
+ msg_puts_display((const char_u *)str, (int)len, attr, false);
}
+
+ need_fileinfo = false;
}
/// Print a formatted message
@@ -2054,7 +2092,7 @@ void msg_printf_attr(const int attr, const char *const fmt, ...)
va_list ap;
va_start(ap, fmt);
- const size_t len = vim_vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
+ const size_t len = (size_t)vim_vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
va_end(ap);
msg_scroll = true;
@@ -2075,10 +2113,8 @@ static void msg_ext_emit_chunk(void)
ADD(msg_ext_chunks, ARRAY_OBJ(chunk));
}
-/*
- * The display part of msg_puts_attr_len().
- * May be called recursively to display scroll-back text.
- */
+/// The display part of msg_puts_attr_len().
+/// May be called recursively to display scroll-back text.
static void msg_puts_display(const char_u *str, int maxlen, int attr, int recurse)
{
const char_u *s = str;
@@ -2117,12 +2153,12 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, int recurs
&& (*s == '\n' || (cmdmsg_rl
? (msg_col <= 1
|| (*s == TAB && msg_col <= 7)
- || (utf_ptr2cells(s) > 1
+ || (utf_ptr2cells((char *)s) > 1
&& msg_col <= 2))
: ((*s != '\r' && msg_col + t_col >= Columns - 1)
|| (*s == TAB
&& msg_col + t_col >= ((Columns - 1) & ~7))
- || (utf_ptr2cells(s) > 1
+ || (utf_ptr2cells((char *)s) > 1
&& msg_col + t_col >= Columns - 2))))) {
// The screen is scrolled up when at the last row (some terminals
// scroll automatically, some don't. To avoid problems we scroll
@@ -2152,7 +2188,7 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, int recurs
// Avoid including composing chars after the end.
l = utfc_ptr2len_len(s, (int)((str + maxlen) - s));
} else {
- l = utfc_ptr2len(s);
+ l = utfc_ptr2len((char *)s);
}
s = screen_puts_mbyte((char_u *)s, l, attr);
did_last_char = true;
@@ -2188,7 +2224,7 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, int recurs
if (lines_left > 0) {
--lines_left;
}
- if (p_more && lines_left == 0 && State != HITRETURN
+ if (p_more && lines_left == 0 && State != MODE_HITRETURN
&& !msg_no_more && !exmode_active) {
if (do_more_prompt(NUL)) {
s = confirm_msg_tail;
@@ -2207,7 +2243,7 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, int recurs
wrap = *s == '\n'
|| msg_col + t_col >= Columns
- || (utf_ptr2cells(s) > 1
+ || (utf_ptr2cells((char *)s) > 1
&& msg_col + t_col >= Columns - 1)
;
if (t_col > 0 && (wrap || *s == '\r' || *s == '\b'
@@ -2244,12 +2280,12 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, int recurs
} else if (*s == BELL) { // beep (from ":sh")
vim_beep(BO_SH);
} else if (*s >= 0x20) { // printable char
- cw = utf_ptr2cells(s);
+ cw = utf_ptr2cells((char *)s);
if (maxlen >= 0) {
// avoid including composing chars after the end
l = utfc_ptr2len_len(s, (int)((str + maxlen) - s));
} else {
- l = utfc_ptr2len(s);
+ l = utfc_ptr2len((char *)s);
}
// When drawing from right to left or when a double-wide character
// doesn't fit, draw a single character here. Otherwise collect
@@ -2283,22 +2319,22 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, int recurs
msg_check();
}
-/// Return true when ":filter pattern" was used and "msg" does not match
-/// "pattern".
+/// @return true when ":filter pattern" was used and "msg" does not match
+/// "pattern".
bool message_filtered(char_u *msg)
{
- if (cmdmod.filter_regmatch.regprog == NULL) {
+ if (cmdmod.cmod_filter_regmatch.regprog == NULL) {
return false;
}
- bool match = vim_regexec(&cmdmod.filter_regmatch, msg, (colnr_T)0);
- return cmdmod.filter_force ? match : !match;
+ bool match = vim_regexec(&cmdmod.cmod_filter_regmatch, (char *)msg, (colnr_T)0);
+ return cmdmod.cmod_filter_force ? match : !match;
}
/// including horizontal separator
int msg_scrollsize(void)
{
- return msg_scrolled + p_ch + 1;
+ return msg_scrolled + (int)p_ch + 1;
}
bool msg_use_msgsep(void)
@@ -2322,18 +2358,18 @@ void msg_scroll_up(bool may_throttle)
msg_did_scroll = true;
if (msg_use_msgsep()) {
if (msg_grid_pos > 0) {
- msg_grid_set_pos(msg_grid_pos-1, true);
+ msg_grid_set_pos(msg_grid_pos - 1, true);
} else {
- grid_del_lines(&msg_grid, 0, 1, msg_grid.Rows, 0, msg_grid.Columns);
- memmove(msg_grid.dirty_col, msg_grid.dirty_col+1,
- (msg_grid.Rows-1) * sizeof(*msg_grid.dirty_col));
- msg_grid.dirty_col[msg_grid.Rows-1] = 0;
+ grid_del_lines(&msg_grid, 0, 1, msg_grid.rows, 0, msg_grid.cols);
+ memmove(msg_grid.dirty_col, msg_grid.dirty_col + 1,
+ (size_t)(msg_grid.rows - 1) * sizeof(*msg_grid.dirty_col));
+ msg_grid.dirty_col[msg_grid.rows - 1] = 0;
}
} else {
grid_del_lines(&msg_grid_adj, 0, 1, Rows, 0, Columns);
}
- grid_fill(&msg_grid_adj, Rows-1, Rows, 0, Columns, ' ', ' ',
+ grid_fill(&msg_grid_adj, Rows - 1, Rows, 0, Columns, ' ', ' ',
HL_ATTR(HLF_MSG));
}
@@ -2360,13 +2396,13 @@ void msg_scroll_flush(void)
msg_grid.throttled = false;
int pos_delta = msg_grid_pos_at_flush - msg_grid_pos;
assert(pos_delta >= 0);
- int delta = MIN(msg_scrolled - msg_scrolled_at_flush, msg_grid.Rows);
+ int delta = MIN(msg_scrolled - msg_scrolled_at_flush, msg_grid.rows);
if (pos_delta > 0) {
ui_ext_msg_set_pos(msg_grid_pos, true);
}
- int to_scroll = delta-pos_delta-msg_grid_scroll_discount;
+ int to_scroll = delta - pos_delta - msg_grid_scroll_discount;
assert(to_scroll >= 0);
// TODO(bfredl): msg_grid_pos should be 0 already when starting scrolling
@@ -2375,10 +2411,10 @@ void msg_scroll_flush(void)
ui_call_grid_scroll(msg_grid.handle, 0, Rows, 0, Columns, to_scroll, 0);
}
- for (int i = MAX(Rows-MAX(delta, 1), 0); i < Rows; i++) {
- int row = i-msg_grid_pos;
+ for (int i = MAX(Rows - MAX(delta, 1), 0); i < Rows; i++) {
+ int row = i - msg_grid_pos;
assert(row >= 0);
- ui_line(&msg_grid, row, 0, msg_grid.dirty_col[row], msg_grid.Columns,
+ ui_line(&msg_grid, row, 0, msg_grid.dirty_col[row], msg_grid.cols,
HL_ATTR(HLF_MSG), false);
msg_grid.dirty_col[row] = 0;
}
@@ -2400,13 +2436,13 @@ void msg_reset_scroll(void)
msg_grid.throttled = false;
// TODO(bfredl): risk for extra flicker i e with
// "nvim -o has_swap also_has_swap"
- msg_grid_set_pos(Rows - p_ch, false);
+ msg_grid_set_pos(Rows - (int)p_ch, false);
clear_cmdline = true;
if (msg_grid.chars) {
// non-displayed part of msg_grid is considered invalid.
- for (int i = 0; i < MIN(msg_scrollsize(), msg_grid.Rows); i++) {
+ for (int i = 0; i < MIN(msg_scrollsize(), msg_grid.rows); i++) {
grid_clear_line(&msg_grid, msg_grid.line_offset[i],
- msg_grid.Columns, false);
+ msg_grid.cols, false);
}
}
} else {
@@ -2416,13 +2452,11 @@ void msg_reset_scroll(void)
msg_scrolled_at_flush = 0;
}
-/*
- * Increment "msg_scrolled".
- */
+/// Increment "msg_scrolled".
static void inc_msg_scrolled(void)
{
if (*get_vim_var_str(VV_SCROLLSTART) == NUL) {
- char *p = (char *)sourcing_name;
+ char *p = sourcing_name;
char *tofree = NULL;
// v:scrollstart is empty, set it to the script/function name and line
@@ -2473,11 +2507,11 @@ static void store_sb_text(char_u **sb_str, char_u *s, int attr, int *sb_col, int
}
if (s > *sb_str) {
- mp = xmalloc((sizeof(msgchunk_T) + (s - *sb_str)));
- mp->sb_eol = finish;
+ mp = xmalloc((sizeof(msgchunk_T) + (size_t)(s - *sb_str)));
+ mp->sb_eol = (char)finish;
mp->sb_msg_col = *sb_col;
mp->sb_attr = attr;
- memcpy(mp->sb_text, *sb_str, s - *sb_str);
+ memcpy(mp->sb_text, *sb_str, (size_t)(s - *sb_str));
mp->sb_text[s - *sb_str] = NUL;
if (last_msgchunk == NULL) {
@@ -2497,9 +2531,7 @@ static void store_sb_text(char_u **sb_str, char_u *s, int attr, int *sb_col, int
*sb_col = 0;
}
-/*
- * Finished showing messages, clear the scroll-back text on the next message.
- */
+/// Finished showing messages, clear the scroll-back text on the next message.
void may_clear_sb_text(void)
{
do_clear_sb_text = SB_CLEAR_ALL;
@@ -2542,9 +2574,7 @@ void clear_sb_text(int all)
}
}
-/*
- * "g<" command.
- */
+/// "g<" command.
void show_sb_text(void)
{
msgchunk_T *mp;
@@ -2560,9 +2590,7 @@ void show_sb_text(void)
}
}
-/*
- * Move to the start of screen line in already displayed text.
- */
+/// Move to the start of screen line in already displayed text.
static msgchunk_T *msg_sb_start(msgchunk_T *mps)
{
msgchunk_T *mp = mps;
@@ -2573,9 +2601,7 @@ static msgchunk_T *msg_sb_start(msgchunk_T *mps)
return mp;
}
-/*
- * Mark the last message chunk as finishing the line.
- */
+/// Mark the last message chunk as finishing the line.
void msg_sb_eol(void)
{
if (last_msgchunk != NULL) {
@@ -2583,10 +2609,9 @@ void msg_sb_eol(void)
}
}
-/*
- * Display a screen line from previously displayed text at row "row".
- * Returns a pointer to the text for the next line (can be NULL).
- */
+/// Display a screen line from previously displayed text at row "row".
+///
+/// @return a pointer to the text for the next line (can be NULL).
static msgchunk_T *disp_sb_line(int row, msgchunk_T *smp)
{
msgchunk_T *mp = smp;
@@ -2609,9 +2634,7 @@ static msgchunk_T *disp_sb_line(int row, msgchunk_T *smp)
return mp->sb_next;
}
-/*
- * Output any postponed text for msg_puts_attr_len().
- */
+/// Output any postponed text for msg_puts_attr_len().
static void t_puts(int *t_col, const char_u *t_s, const char_u *s, int attr)
{
attr = hl_combine_attr(HL_ATTR(HLF_MSG), attr);
@@ -2623,7 +2646,7 @@ static void t_puts(int *t_col, const char_u *t_s, const char_u *s, int attr)
*t_col = 0;
// If the string starts with a composing character don't increment the
// column position for it.
- if (utf_iscomposing(utf_ptr2char(t_s))) {
+ if (utf_iscomposing(utf_ptr2char((char *)t_s))) {
msg_col--;
}
if (msg_col >= Columns) {
@@ -2632,9 +2655,9 @@ static void t_puts(int *t_col, const char_u *t_s, const char_u *s, int attr)
}
}
-// Returns TRUE when messages should be printed to stdout/stderr:
-// - "batch mode" ("silent mode", -es/-Es)
-// - no UI and not embedded
+/// @return TRUE when messages should be printed to stdout/stderr:
+/// - "batch mode" ("silent mode", -es/-Es)
+/// - no UI and not embedded
int msg_use_printf(void)
{
return !embedded_mode && !ui_active();
@@ -2647,15 +2670,26 @@ 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 *)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);
+ int len = utf_ptr2len(s);
if (!(silent_mode && p_verbose == 0)) {
// NL --> CR NL translation (for Unix, not for "--version")
p = &buf[0];
if (*s == '\n' && !info_message) {
*p++ = '\r';
}
- memcpy(p, s, len);
+ memcpy(p, s, (size_t)len);
*(p + len) = '\0';
if (info_message) {
mch_msg(buf);
@@ -2664,7 +2698,7 @@ static void msg_puts_printf(const char *str, const ptrdiff_t maxlen)
}
}
- int cw = utf_char2cells(utf_ptr2char((const char_u *)s));
+ int cw = utf_char2cells(utf_ptr2char(s));
// primitive way to compute the current column
if (cmdmsg_rl) {
if (*s == '\r' || *s == '\n') {
@@ -2684,13 +2718,12 @@ static void msg_puts_printf(const char *str, const ptrdiff_t maxlen)
msg_didout = true; // assume that line is not empty
}
-/*
- * Show the more-prompt and handle the user response.
- * This takes care of scrolling back and displaying previously displayed text.
- * When at hit-enter prompt "typed_char" is the already typed character,
- * otherwise it's NUL.
- * Returns TRUE when jumping ahead to "confirm_msg_tail".
- */
+/// Show the more-prompt and handle the user response.
+/// This takes care of scrolling back and displaying previously displayed text.
+/// When at hit-enter prompt "typed_char" is the already typed character,
+/// otherwise it's NUL.
+///
+/// @return TRUE when jumping ahead to "confirm_msg_tail".
static int do_more_prompt(int typed_char)
{
static bool entered = false;
@@ -2712,7 +2745,7 @@ static int do_more_prompt(int typed_char)
// We get called recursively when a timer callback outputs a message. In
// that case don't show another prompt. Also when at the hit-Enter prompt
// and nothing was typed.
- if (no_need_more || entered || (State == HITRETURN && typed_char == 0)) {
+ if (no_need_more || entered || (State == MODE_HITRETURN && typed_char == 0)) {
return false;
}
entered = true;
@@ -2726,7 +2759,7 @@ static int do_more_prompt(int typed_char)
}
}
- State = ASKMORE;
+ State = MODE_ASKMORE;
setmouse();
if (typed_char == NUL) {
msg_moremsg(FALSE);
@@ -2742,7 +2775,6 @@ static int do_more_prompt(int typed_char)
c = get_keystroke(resize_events);
}
-
toscroll = 0;
switch (c) {
case BS: // scroll one line back
@@ -2892,7 +2924,7 @@ static int do_more_prompt(int typed_char)
// scroll up, display line at bottom
msg_scroll_up(true);
inc_msg_scrolled();
- grid_fill(&msg_grid_adj, Rows-2, Rows-1, 0, Columns, ' ', ' ',
+ grid_fill(&msg_grid_adj, Rows - 2, Rows - 1, 0, Columns, ' ', ' ',
HL_ATTR(HLF_MSG));
mp_last = disp_sb_line(Rows - 2, mp_last);
toscroll--;
@@ -2948,7 +2980,7 @@ void mch_errmsg(char *str)
}
}
-// Give a message. To be used when the UI is not initialized yet.
+/// Give a message. To be used when the UI is not initialized yet.
void mch_msg(char *str)
{
assert(str != NULL);
@@ -2963,10 +2995,8 @@ void mch_msg(char *str)
}
#endif // WIN32
-/*
- * Put a character on the screen at the current message position and advance
- * to the next position. Only for printable ASCII!
- */
+/// Put a character on the screen at the current message position and advance
+/// to the next position. Only for printable ASCII!
static void msg_screen_putchar(int c, int attr)
{
attr = hl_combine_attr(HL_ATTR(HLF_MSG), attr);
@@ -2995,25 +3025,23 @@ void msg_moremsg(int full)
if (full) {
grid_puts(&msg_grid_adj, (char_u *)
_(" SPACE/d/j: screen/page/line down, b/u/k: up, q: quit "),
- Rows - 1, vim_strsize(s), attr);
+ Rows - 1, vim_strsize((char *)s), attr);
}
}
-/*
- * Repeat the message for the current mode: ASKMORE, EXTERNCMD, CONFIRM or
- * exmode_active.
- */
+/// Repeat the message for the current mode: MODE_ASKMORE, MODE_EXTERNCMD,
+/// MODE_CONFIRM or exmode_active.
void repeat_message(void)
{
- if (State == ASKMORE) {
- msg_moremsg(TRUE); // display --more-- message again
+ if (State == MODE_ASKMORE) {
+ msg_moremsg(true); // display --more-- message again
msg_row = Rows - 1;
- } else if (State == CONFIRM) {
+ } else if (State == MODE_CONFIRM) {
display_confirm_msg(); // display ":confirm" message again
msg_row = Rows - 1;
- } else if (State == EXTERNCMD) {
+ } else if (State == MODE_EXTERNCMD) {
ui_cursor_goto(msg_row, msg_col); // put cursor back
- } else if (State == HITRETURN || State == SETWSIZE) {
+ } else if (State == MODE_HITRETURN || State == MODE_SETWSIZE) {
if (msg_row == Rows - 1) {
// Avoid drawing the "hit-enter" prompt below the previous one,
// overwrite it. Esp. useful when regaining focus and a
@@ -3027,22 +3055,18 @@ void repeat_message(void)
}
}
-/*
- * Clear from current message position to end of screen.
- * Skip this when ":silent" was used, no need to clear for redirection.
- */
+/// Clear from current message position to end of screen.
+/// Skip this when ":silent" was used, no need to clear for redirection.
void msg_clr_eos(void)
{
- if (msg_silent == 0) {
+ if (msg_silent == 0 && p_ch > 0) {
msg_clr_eos_force();
}
}
-/*
- * Clear from current message position to end of screen.
- * Note: msg_col is not updated, so we remember the end of the message
- * for msg_check().
- */
+/// Clear from current message position to end of screen.
+/// Note: msg_col is not updated, so we remember the end of the message
+/// for msg_check().
void msg_clr_eos_force(void)
{
if (ui_has(kUIMessages)) {
@@ -3057,21 +3081,21 @@ void msg_clr_eos_force(void)
msg_row = msg_grid_pos;
}
- grid_fill(&msg_grid_adj, msg_row, msg_row + 1, msg_startcol, msg_endcol, ' ',
- ' ', HL_ATTR(HLF_MSG));
- grid_fill(&msg_grid_adj, msg_row + 1, Rows, 0, Columns, ' ', ' ',
- HL_ATTR(HLF_MSG));
+ if (p_ch > 0) {
+ grid_fill(&msg_grid_adj, msg_row, msg_row + 1, msg_startcol, msg_endcol,
+ ' ', ' ', HL_ATTR(HLF_MSG));
+ grid_fill(&msg_grid_adj, msg_row + 1, Rows, 0, Columns,
+ ' ', ' ', HL_ATTR(HLF_MSG));
+ }
redraw_cmdline = true; // overwritten the command line
- if (msg_row < Rows-1 || msg_col == (cmdmsg_rl ? Columns : 0)) {
+ if (msg_row < Rows - 1 || msg_col == (cmdmsg_rl ? Columns : 0)) {
clear_cmdline = false; // command line has been cleared
mode_displayed = false; // mode cleared or overwritten
}
}
-/*
- * Clear the command line.
- */
+/// Clear the command line.
void msg_clr_cmdline(void)
{
msg_row = cmdline_row;
@@ -3079,11 +3103,10 @@ void msg_clr_cmdline(void)
msg_clr_eos_force();
}
-/*
- * end putting a message on the screen
- * call wait_return if the message does not fit in the available space
- * return TRUE if wait_return not called.
- */
+/// end putting a message on the screen
+/// call wait_return if the message does not fit in the available space
+///
+/// @return TRUE if wait_return not called.
int msg_end(void)
{
/*
@@ -3092,9 +3115,9 @@ int msg_end(void)
* we have to redraw the window.
* Do not do this if we are abandoning the file or editing the command line.
*/
- if (!exiting && need_wait_return && !(State & CMDLINE)) {
- wait_return(FALSE);
- return FALSE;
+ if (!exiting && need_wait_return && !(State & MODE_CMDLINE)) {
+ wait_return(false);
+ return false;
}
// NOTE: ui_flush() used to be called here. This had to be removed, as it
@@ -3113,12 +3136,13 @@ void msg_ext_ui_flush(void)
msg_ext_emit_chunk();
if (msg_ext_chunks.size > 0) {
- ui_call_msg_show(cstr_to_string(msg_ext_kind),
+ ui_call_msg_show(cstr_as_string((char *)msg_ext_kind),
msg_ext_chunks, msg_ext_overwrite);
if (!msg_ext_overwrite) {
msg_ext_visible++;
}
msg_ext_kind = NULL;
+ api_free_array(msg_ext_chunks);
msg_ext_chunks = (Array)ARRAY_DICT_INIT;
msg_ext_cur_len = 0;
msg_ext_overwrite = false;
@@ -3132,6 +3156,7 @@ void msg_ext_flush_showmode(void)
if (ui_has(kUIMessages)) {
msg_ext_emit_chunk();
ui_call_msg_showmode(msg_ext_chunks);
+ api_free_array(msg_ext_chunks);
msg_ext_chunks = (Array)ARRAY_DICT_INIT;
msg_ext_cur_len = 0;
}
@@ -3144,6 +3169,10 @@ void msg_ext_clear(bool force)
msg_ext_visible = 0;
msg_ext_overwrite = false; // nothing to overwrite
}
+ if (msg_ext_history_visible) {
+ ui_call_msg_history_clear();
+ msg_ext_history_visible = false;
+ }
// Only keep once.
msg_ext_keep_after_cmdline = false;
@@ -3173,10 +3202,8 @@ bool msg_ext_is_visible(void)
return ui_has(kUIMessages) && msg_ext_visible > 0;
}
-/*
- * If the written message runs into the shown command or ruler, we have to
- * wait for hit-return and redraw the window later.
- */
+/// If the written message runs into the shown command or ruler, we have to
+/// wait for hit-return and redraw the window later.
void msg_check(void)
{
if (ui_has(kUIMessages)) {
@@ -3188,10 +3215,9 @@ void msg_check(void)
}
}
-/*
- * May write a string to the redirection file.
- * When "maxlen" is -1 write the whole string, otherwise up to "maxlen" bytes.
- */
+/// May write a string to the redirection file.
+///
+/// @param maxlen if -1, write the whole string, otherwise up to "maxlen" bytes.
static void redir_write(const char *const str, const ptrdiff_t maxlen)
{
const char_u *s = (char_u *)str;
@@ -3221,7 +3247,7 @@ static void redir_write(const char *const str, const ptrdiff_t maxlen)
if (redir_reg) {
write_reg_contents(redir_reg, (char_u *)" ", 1, true);
} else if (redir_vname) {
- var_redir_str((char_u *)" ", -1);
+ var_redir_str(" ", -1);
} else if (redir_fd != NULL) {
fputs(" ", redir_fd);
}
@@ -3237,10 +3263,10 @@ static void redir_write(const char *const str, const ptrdiff_t maxlen)
ga_concat_len(capture_ga, str, len);
}
if (redir_reg) {
- write_reg_contents(redir_reg, s, len, true);
+ write_reg_contents(redir_reg, s, (ssize_t)len, true);
}
if (redir_vname) {
- var_redir_str((char_u *)s, maxlen);
+ var_redir_str((char *)s, (int)maxlen);
}
// Write and adjust the current column.
@@ -3276,10 +3302,8 @@ int redirecting(void)
|| redir_reg || redir_vname || capture_ga != NULL;
}
-/*
- * Before giving verbose message.
- * Must always be called paired with verbose_leave()!
- */
+/// Before giving verbose message.
+/// Must always be called paired with verbose_leave()!
void verbose_enter(void)
{
if (*p_vfile != NUL) {
@@ -3287,10 +3311,8 @@ void verbose_enter(void)
}
}
-/*
- * After giving verbose message.
- * Must always be called paired with verbose_enter()!
- */
+/// After giving verbose message.
+/// Must always be called paired with verbose_enter()!
void verbose_leave(void)
{
if (*p_vfile != NUL) {
@@ -3300,9 +3322,7 @@ void verbose_leave(void)
}
}
-/*
- * Like verbose_enter() and set msg_scroll when displaying the message.
- */
+/// Like verbose_enter() and set msg_scroll when displaying the message.
void verbose_enter_scroll(void)
{
if (*p_vfile != NUL) {
@@ -3313,9 +3333,7 @@ void verbose_enter_scroll(void)
}
}
-/*
- * Like verbose_leave() and set cmdline_row when displaying the message.
- */
+/// Like verbose_leave() and set cmdline_row when displaying the message.
void verbose_leave_scroll(void)
{
if (*p_vfile != NUL) {
@@ -3327,9 +3345,7 @@ void verbose_leave_scroll(void)
}
}
-/*
- * Called when 'verbosefile' is set: stop writing to the file.
- */
+/// Called when 'verbosefile' is set: stop writing to the file.
void verbose_stop(void)
{
if (verbose_fd != NULL) {
@@ -3339,10 +3355,9 @@ void verbose_stop(void)
verbose_did_open = FALSE;
}
-/*
- * Open the file 'verbosefile'.
- * Return FAIL or OK.
- */
+/// Open the file 'verbosefile'.
+///
+/// @return FAIL or OK.
int verbose_open(void)
{
if (verbose_fd == NULL && !verbose_did_open) {
@@ -3358,11 +3373,10 @@ int verbose_open(void)
return OK;
}
-/*
- * Give a warning message (for searching).
- * Use 'w' highlighting and may repeat the message after redrawing
- */
-void give_warning(char_u *message, bool hl) FUNC_ATTR_NONNULL_ARG(1)
+/// Give a warning message (for searching).
+/// Use 'w' highlighting and may repeat the message after redrawing
+void give_warning(char *message, bool hl)
+ FUNC_ATTR_NONNULL_ARG(1)
{
// Don't do this for ":silent".
if (msg_silent != 0) {
@@ -3372,7 +3386,7 @@ void give_warning(char_u *message, bool hl) FUNC_ATTR_NONNULL_ARG(1)
// Don't want a hit-enter prompt here.
no_wait_return++;
- set_vim_var_string(VV_WARNINGMSG, (char *)message, -1);
+ set_vim_var_string(VV_WARNINGMSG, message, -1);
XFREE_CLEAR(keep_msg);
if (hl) {
keep_msg_attr = HL_ATTR(HLF_W);
@@ -3385,7 +3399,7 @@ void give_warning(char_u *message, bool hl) FUNC_ATTR_NONNULL_ARG(1)
}
if (msg_attr((const char *)message, keep_msg_attr) && msg_scrolled == 0) {
- set_keep_msg((char *)message, keep_msg_attr);
+ set_keep_msg(message, keep_msg_attr);
}
msg_didout = false; // Overwrite this message.
msg_nowait = true; // Don't wait for this message.
@@ -3397,12 +3411,10 @@ void give_warning(char_u *message, bool hl) FUNC_ATTR_NONNULL_ARG(1)
void give_warning2(char_u *const message, char_u *const a1, bool hl)
{
vim_snprintf((char *)IObuff, IOSIZE, (char *)message, a1);
- give_warning(IObuff, hl);
+ give_warning((char *)IObuff, hl);
}
-/*
- * Advance msg cursor to column "col".
- */
+/// Advance msg cursor to column "col".
void msg_advance(int col)
{
if (msg_silent != 0) { // nothing to advance to
@@ -3450,6 +3462,7 @@ void msg_advance(int col)
///
/// @param textfiel IObuff for inputdialog(), NULL otherwise
/// @param ex_cmd when TRUE pressing : accepts default and starts Ex command
+/// @returns 0 if cancelled, otherwise the nth button (1-indexed).
int do_dialog(int type, char_u *title, char_u *message, char_u *buttons, int dfltbutton,
char_u *textfield, int ex_cmd)
{
@@ -3464,12 +3477,11 @@ int do_dialog(int type, char_u *title, char_u *message, char_u *buttons, int dfl
return dfltbutton; // return default option
}
-
int save_msg_silent = msg_silent;
int oldState = State;
msg_silent = 0; // If dialog prompts for input, user needs to see it! #8788
- State = CONFIRM;
+ State = MODE_CONFIRM;
setmouse();
/*
@@ -3497,7 +3509,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;
}
@@ -3505,10 +3517,10 @@ int do_dialog(int type, char_u *title, char_u *message, char_u *buttons, int dfl
c = mb_tolower(c);
retval = 1;
for (i = 0; hotkeys[i]; i++) {
- if (utf_ptr2char(hotkeys + i) == c) {
+ if (utf_ptr2char((char *)hotkeys + i) == c) {
break;
}
- i += utfc_ptr2len(hotkeys + i) - 1;
+ i += utfc_ptr2len((char *)hotkeys + i) - 1;
retval++;
}
if (hotkeys[i]) {
@@ -3531,7 +3543,6 @@ int do_dialog(int type, char_u *title, char_u *message, char_u *buttons, int dfl
return retval;
}
-
/// Copy one character from "*from" to "*to", taking care of multi-byte
/// characters. Return the length of the character in bytes.
///
@@ -3540,10 +3551,10 @@ static int copy_char(const char_u *from, char_u *to, bool lowercase)
FUNC_ATTR_NONNULL_ALL
{
if (lowercase) {
- int c = mb_tolower(utf_ptr2char(from));
- return utf_char2bytes(c, to);
+ int c = mb_tolower(utf_ptr2char((char *)from));
+ return utf_char2bytes(c, (char *)to);
}
- int len = utfc_ptr2len(from);
+ int len = utfc_ptr2len((char *)from);
memmove(to, from, (size_t)len);
return len;
}
@@ -3602,24 +3613,21 @@ static char_u *console_dialog_alloc(const char_u *message, char_u *buttons, bool
len += 2; // "x" -> "[x]"
}
-
// Now allocate space for the strings
xfree(confirm_msg);
- confirm_msg = xmalloc(len);
+ confirm_msg = xmalloc((size_t)len);
*confirm_msg = NUL;
- return xmalloc(lenhotkey);
+ return xmalloc((size_t)lenhotkey);
}
-/*
- * Format the dialog string, and display it at the bottom of
- * the screen. Return a string of hotkey chars (if defined) for
- * each 'button'. If a button has no hotkey defined, the first character of
- * the button is used.
- * The hotkeys can be multi-byte characters, but without combining chars.
- *
- * Returns an allocated string with hotkeys.
- */
+/// Format the dialog string, and display it at the bottom of
+/// the screen. Return a string of hotkey chars (if defined) for
+/// each 'button'. If a button has no hotkey defined, the first character of
+/// the button is used.
+/// The hotkeys can be multi-byte characters, but without combining chars.
+///
+/// @return an allocated string with hotkeys.
static char_u *msg_show_console_dialog(char_u *message, char_u *buttons, int dfltbutton)
FUNC_ATTR_NONNULL_RET
{
@@ -3712,9 +3720,7 @@ static void copy_hotkeys_and_msg(const char_u *message, char_u *buttons, int def
*msgp = NUL;
}
-/*
- * Display the ":confirm" message. Also called when screen resized.
- */
+/// Display the ":confirm" message. Also called when screen resized.
void display_confirm_msg(void)
{
// Avoid that 'q' at the more prompt truncates the message here.
diff --git a/src/nvim/message.h b/src/nvim/message.h
index 316b2df7a4..2de2890213 100644
--- a/src/nvim/message.h
+++ b/src/nvim/message.h
@@ -44,6 +44,7 @@ typedef struct msg_hist {
const char *kind; ///< Message kind (for msg_ext)
int attr; ///< Message highlighting.
bool multiline; ///< Multiline message.
+ HlMessage multiattr; ///< multiattr message.
} MessageHistoryEntry;
/// First message
@@ -69,7 +70,6 @@ EXTERN ScreenGrid msg_grid_adj INIT(= SCREEN_GRID_INIT);
// value of msg_scrolled at latest msg_scroll_flush.
EXTERN int msg_scrolled_at_flush INIT(= 0);
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "message.h.generated.h"
#endif
diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index 5d007fb173..a4a521fa80 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -30,6 +30,49 @@
static linenr_T orig_topline = 0;
static int orig_topfill = 0;
+/// Translate window coordinates to buffer position without any side effects
+int get_fpos_of_mouse(pos_T *mpos)
+{
+ int grid = mouse_grid;
+ int row = mouse_row;
+ int col = mouse_col;
+
+ if (row < 0 || col < 0) { // check if it makes sense
+ return IN_UNKNOWN;
+ }
+
+ // find the window where the row is in
+ win_T *wp = mouse_find_win(&grid, &row, &col);
+ if (wp == NULL) {
+ return IN_UNKNOWN;
+ }
+
+ // winpos and height may change in win_enter()!
+ if (row + wp->w_winbar_height >= wp->w_height) { // In (or below) status line
+ return IN_STATUS_LINE;
+ }
+ if (col >= wp->w_width) { // In vertical separator line
+ return IN_SEP_LINE;
+ }
+
+ if (wp != curwin) {
+ return IN_UNKNOWN;
+ }
+
+ // compute the position in the buffer line from the posn on the screen
+ if (mouse_comp_pos(curwin, &row, &col, &mpos->lnum)) {
+ return IN_STATUS_LINE; // past bottom
+ }
+
+ mpos->col = vcol2col(wp, mpos->lnum, col);
+
+ if (mpos->col > 0) {
+ mpos->col--;
+ }
+ mpos->coladd = 0;
+ return IN_BUFFER;
+}
+
/// Return true if "c" is a mouse key.
bool is_mouse_key(int c)
{
@@ -68,12 +111,12 @@ bool is_mouse_key(int c)
/// mouse was previously on a status line, then the status line may be dragged.
///
/// If flags has MOUSE_MAY_VIS, then VIsual mode will be started before the
-/// cursor is moved unless the cursor was on a status line.
+/// cursor is moved unless the cursor was on a status line or window bar.
/// This function returns one of IN_UNKNOWN, IN_BUFFER, IN_STATUS_LINE or
/// IN_SEP_LINE depending on where the cursor was clicked.
///
/// If flags has MOUSE_MAY_STOP_VIS, then Visual mode will be stopped, unless
-/// the mouse is on the status line of the same window.
+/// the mouse is on the status line or window bar of the same window.
///
/// If flags has MOUSE_DID_MOVE, nothing is done if the mouse didn't move since
/// the last call.
@@ -85,8 +128,11 @@ bool is_mouse_key(int c)
/// @param which_button MOUSE_LEFT, MOUSE_RIGHT, MOUSE_MIDDLE
int jump_to_mouse(int flags, bool *inclusive, int which_button)
{
- static int on_status_line = 0; // #lines below bottom of window
- static int on_sep_line = 0; // on separator right of window
+ static int status_line_offset = 0; // #lines offset from status line
+ static int sep_line_offset = 0; // #cols offset from sep line
+ static bool on_status_line = false;
+ static bool on_sep_line = false;
+ static bool on_winbar = false;
static int prev_row = -1;
static int prev_col = -1;
static win_T *dragwin = NULL; // window being dragged
@@ -100,6 +146,7 @@ int jump_to_mouse(int flags, bool *inclusive, int which_button)
int col = mouse_col;
int grid = mouse_grid;
int fdc = 0;
+ bool keep_focus = flags & MOUSE_FOCUS;
mouse_past_bottom = false;
mouse_past_eol = false;
@@ -120,12 +167,15 @@ int jump_to_mouse(int flags, bool *inclusive, int which_button)
retnomove:
// before moving the cursor for a left click which is NOT in a status
// line, stop Visual mode
- if (on_status_line) {
+ if (status_line_offset) {
return IN_STATUS_LINE;
}
- if (on_sep_line) {
+ if (sep_line_offset) {
return IN_SEP_LINE;
}
+ if (on_winbar) {
+ return IN_OTHER_WIN | MOUSE_WINBAR;
+ }
if (flags & MOUSE_MAY_STOP_VIS) {
end_visual_mode();
redraw_curbuf_later(INVERTED); // delete the inversion
@@ -142,47 +192,78 @@ retnomove:
old_curwin = curwin;
old_cursor = curwin->w_cursor;
- if (!(flags & MOUSE_FOCUS)) {
- if (row < 0 || col < 0) { // check if it makes sense
- return IN_UNKNOWN;
+ if (row < 0 || col < 0) { // check if it makes sense
+ return IN_UNKNOWN;
+ }
+
+ // find the window where the row is in
+ wp = mouse_find_win(&grid, &row, &col);
+ if (wp == NULL) {
+ return IN_UNKNOWN;
+ }
+
+ on_status_line = (grid == DEFAULT_GRID_HANDLE && row + wp->w_winbar_height >= wp->w_height)
+ ? row + wp->w_winbar_height - wp->w_height + 1 == 1
+ : false;
+
+ on_winbar = (row == -1)
+ ? wp->w_winbar_height != 0
+ : false;
+
+ on_sep_line = grid == DEFAULT_GRID_HANDLE && col >= wp->w_width
+ ? col - wp->w_width + 1 == 1
+ : false;
+
+ // The rightmost character of the status line might be a vertical
+ // separator character if there is no connecting window to the right.
+ if (on_status_line && on_sep_line) {
+ if (stl_connected(wp)) {
+ on_sep_line = false;
+ } else {
+ on_status_line = false;
}
+ }
- // find the window where the row is in
- wp = mouse_find_win(&grid, &row, &col);
- if (wp == NULL) {
- return IN_UNKNOWN;
+ if (keep_focus) {
+ // If we can't change focus, set the value of row, col and grid back to absolute values
+ // since the values relative to the window are only used when keep_focus is false
+ row = mouse_row;
+ col = mouse_col;
+ grid = mouse_grid;
+ }
+
+ if (!keep_focus) {
+ if (on_winbar) {
+ return IN_OTHER_WIN | MOUSE_WINBAR;
}
+
fdc = win_fdccol_count(wp);
dragwin = NULL;
- if (row == -1) {
- return IN_OTHER_WIN;
- }
-
// winpos and height may change in win_enter()!
- if (grid == DEFAULT_GRID_HANDLE && row >= wp->w_height) {
+ if (grid == DEFAULT_GRID_HANDLE && row + wp->w_winbar_height >= wp->w_height) {
// In (or below) status line
- on_status_line = row - wp->w_height + 1;
+ status_line_offset = row + wp->w_winbar_height - wp->w_height + 1;
dragwin = wp;
} else {
- on_status_line = 0;
+ status_line_offset = 0;
}
if (grid == DEFAULT_GRID_HANDLE && col >= wp->w_width) {
// In separator line
- on_sep_line = col - wp->w_width + 1;
+ sep_line_offset = col - wp->w_width + 1;
dragwin = wp;
} else {
- on_sep_line = 0;
+ sep_line_offset = 0;
}
// The rightmost character of the status line might be a vertical
// separator character if there is no connecting window to the right.
- if (on_status_line && on_sep_line) {
+ if (status_line_offset && sep_line_offset) {
if (stl_connected(wp)) {
- on_sep_line = 0;
+ sep_line_offset = 0;
} else {
- on_status_line = 0;
+ status_line_offset = 0;
}
}
@@ -190,8 +271,8 @@ retnomove:
// click, stop Visual mode.
if (VIsual_active
&& (wp->w_buffer != curwin->w_buffer
- || (!on_status_line
- && !on_sep_line
+ || (!status_line_offset
+ && !sep_line_offset
&& (wp->w_p_rl
? col < wp->w_width_inner - fdc
: col >= fdc + (cmdwin_type == 0 && wp == curwin ? 0 : 1))
@@ -202,7 +283,7 @@ retnomove:
if (cmdwin_type != 0 && wp != curwin) {
// A click outside the command-line window: Use modeless
// selection if possible. Allow dragging the status lines.
- on_sep_line = 0;
+ sep_line_offset = 0;
row = 0;
col += wp->w_wincol;
wp = curwin;
@@ -217,7 +298,7 @@ retnomove:
if (curwin != old_curwin) {
set_mouse_topline(curwin);
}
- if (on_status_line) { // In (or below) status line
+ if (status_line_offset) { // In (or below) status line
// Don't use start_arrow() if we're in the same window
if (curwin == old_curwin) {
return IN_STATUS_LINE;
@@ -225,7 +306,7 @@ retnomove:
return IN_STATUS_LINE | CURSOR_MOVED;
}
}
- if (on_sep_line) { // In (or below) status line
+ if (sep_line_offset) { // In (or below) status line
// Don't use start_arrow() if we're in the same window
if (curwin == old_curwin) {
return IN_SEP_LINE;
@@ -235,24 +316,29 @@ retnomove:
}
curwin->w_cursor.lnum = curwin->w_topline;
- } else if (on_status_line && which_button == MOUSE_LEFT) {
- if (dragwin != NULL) {
+ } else if (status_line_offset) {
+ if (which_button == MOUSE_LEFT && dragwin != NULL) {
// Drag the status line
count = row - dragwin->w_winrow - dragwin->w_height + 1
- - on_status_line;
+ - status_line_offset;
win_drag_status_line(dragwin, count);
did_drag |= count;
}
return IN_STATUS_LINE; // Cursor didn't move
- } else if (on_sep_line && which_button == MOUSE_LEFT) {
+ } else if (sep_line_offset && which_button == MOUSE_LEFT) {
if (dragwin != NULL) {
// Drag the separator column
count = col - dragwin->w_wincol - dragwin->w_width + 1
- - on_sep_line;
+ - sep_line_offset;
win_drag_vsep_line(dragwin, count);
did_drag |= count;
}
return IN_SEP_LINE; // Cursor didn't move
+ } else if (on_status_line && which_button == MOUSE_RIGHT) {
+ return IN_STATUS_LINE;
+ } else if (on_winbar && which_button == MOUSE_RIGHT) {
+ // After a click on the window bar don't start Visual mode.
+ return IN_OTHER_WIN | MOUSE_WINBAR;
} else {
// keep_window_focus must be true
// before moving the cursor for a left click, stop Visual mode
@@ -262,8 +348,11 @@ retnomove:
}
if (grid == 0) {
- row -= curwin->w_grid_alloc.comp_row+curwin->w_grid.row_offset;
- col -= curwin->w_grid_alloc.comp_col+curwin->w_grid.col_offset;
+ row -= curwin->w_grid_alloc.comp_row + curwin->w_grid.row_offset;
+ col -= curwin->w_grid_alloc.comp_col + curwin->w_grid.col_offset;
+ } else if (grid != DEFAULT_GRID_HANDLE) {
+ row -= curwin->w_grid.row_offset;
+ col -= curwin->w_grid.col_offset;
}
// When clicking beyond the end of the window, scroll the screen.
@@ -468,7 +557,6 @@ win_T *mouse_find_win(int *gridp, int *rowp, int *colp)
return NULL;
}
-
frame_T *fp;
fp = topframe;
@@ -497,6 +585,7 @@ win_T *mouse_find_win(int *gridp, int *rowp, int *colp)
// exist.
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp == fp->fr_win) {
+ *rowp -= wp->w_winbar_height;
return wp;
}
}
@@ -512,8 +601,8 @@ static win_T *mouse_find_grid_win(int *gridp, int *rowp, int *colp)
win_T *wp = get_win_by_grid_handle(*gridp);
if (wp && wp->w_grid_alloc.chars
&& !(wp->w_floating && !wp->w_float_config.focusable)) {
- *rowp = MIN(*rowp-wp->w_grid.row_offset, wp->w_grid.Rows-1);
- *colp = MIN(*colp-wp->w_grid.col_offset, wp->w_grid.Columns-1);
+ *rowp = MIN(*rowp - wp->w_grid.row_offset, wp->w_grid.rows - 1);
+ *colp = MIN(*colp - wp->w_grid.col_offset, wp->w_grid.cols - 1);
return wp;
}
} else if (*gridp == 0) {
@@ -523,8 +612,8 @@ static win_T *mouse_find_grid_win(int *gridp, int *rowp, int *colp)
continue;
}
*gridp = grid->handle;
- *rowp -= grid->comp_row+wp->w_grid.row_offset;
- *colp -= grid->comp_col+wp->w_grid.col_offset;
+ *rowp -= grid->comp_row + wp->w_grid.row_offset;
+ *colp -= grid->comp_col + wp->w_grid.col_offset;
return wp;
}
@@ -535,6 +624,22 @@ static win_T *mouse_find_grid_win(int *gridp, int *rowp, int *colp)
return NULL;
}
+/// Convert a virtual (screen) column to a character column.
+/// The first column is one.
+colnr_T vcol2col(win_T *const wp, const linenr_T lnum, const colnr_T vcol)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ // try to advance to the specified column
+ char_u *ptr = ml_get_buf(wp->w_buffer, lnum, false);
+ char_u *const line = ptr;
+ colnr_T count = 0;
+ while (count < vcol && *ptr != NUL) {
+ count += win_lbr_chartabsize(wp, line, ptr, count, NULL);
+ MB_PTR_ADV(ptr);
+ }
+ return (colnr_T)(ptr - line);
+}
+
/// Set UI mouse depending on current mode and 'mouse'.
///
/// Emits mouse_on/mouse_off UI event (unless 'mouse' is empty).
@@ -544,7 +649,6 @@ void setmouse(void)
ui_check_mouse();
}
-
// Set orig_topline. Used when jumping to another window, so that a double
// click still works.
void set_mouse_topline(win_T *wp)
@@ -618,7 +722,7 @@ bool mouse_scroll_horiz(int dir)
return false;
}
- int step = 6;
+ int step = (int)p_mousescroll_hor;
if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) {
step = curwin->w_width_inner;
}
@@ -686,7 +790,7 @@ static int mouse_adjust_click(win_T *wp, int row, int col)
vcol = 0;
while (vcol < offset && *ptr != NUL) {
vcol += win_chartabsize(curwin, ptr, vcol);
- ptr += utfc_ptr2len(ptr);
+ ptr += utfc_ptr2len((char *)ptr);
}
ptr_row_offset = ptr;
@@ -697,7 +801,7 @@ static int mouse_adjust_click(win_T *wp, int row, int col)
ptr_end = ptr_row_offset;
while (vcol < col && *ptr_end != NUL) {
vcol += win_chartabsize(curwin, ptr_end, vcol);
- ptr_end += utfc_ptr2len(ptr_end);
+ ptr_end += utfc_ptr2len((char *)ptr_end);
}
int matchid;
@@ -707,8 +811,8 @@ static int mouse_adjust_click(win_T *wp, int row, int col)
vcol = offset;
-#define INCR() nudge++; ptr_end += utfc_ptr2len(ptr_end)
-#define DECR() nudge--; ptr_end -= utfc_ptr2len(ptr_end)
+#define INCR() nudge++; ptr_end += utfc_ptr2len((char *)ptr_end)
+#define DECR() nudge--; ptr_end -= utfc_ptr2len((char *)ptr_end)
while (ptr < ptr_end && *ptr != NUL) {
cwidth = win_chartabsize(curwin, ptr, vcol);
@@ -739,7 +843,7 @@ static int mouse_adjust_click(win_T *wp, int row, int col)
while (prev_matchid == matchid && *ptr != NUL) {
INCR();
- ptr += utfc_ptr2len(ptr);
+ ptr += utfc_ptr2len((char *)ptr);
matchid = syn_get_concealed_id(wp, lnum, (colnr_T)(ptr - line));
}
@@ -747,7 +851,7 @@ static int mouse_adjust_click(win_T *wp, int row, int col)
}
}
- ptr += utfc_ptr2len(ptr);
+ ptr += utfc_ptr2len((char *)ptr);
}
return col + nudge;
@@ -768,8 +872,8 @@ int mouse_check_fold(void)
wp = mouse_find_win(&click_grid, &click_row, &click_col);
if (wp && multigrid) {
- max_row = wp->w_grid_alloc.Rows;
- max_col = wp->w_grid_alloc.Columns;
+ max_row = wp->w_grid_alloc.rows;
+ max_col = wp->w_grid_alloc.cols;
}
if (wp && mouse_row >= 0 && mouse_row < max_row
@@ -782,8 +886,8 @@ int mouse_check_fold(void)
// Remember the character under the mouse, might be one of foldclose or
// foldopen fillchars in the fold column.
if (gp->chars != NULL) {
- mouse_char = utf_ptr2char(gp->chars[gp->line_offset[row]
- + (unsigned)col]);
+ mouse_char = utf_ptr2char((char *)gp->chars[gp->line_offset[row]
+ + (unsigned)col]);
}
// Check for position outside of the fold column.
diff --git a/src/nvim/mouse.h b/src/nvim/mouse.h
index bf4f9c57e5..08261e4a30 100644
--- a/src/nvim/mouse.h
+++ b/src/nvim/mouse.h
@@ -38,9 +38,8 @@
// Direction for nv_mousescroll() and ins_mousescroll()
#define MSCR_DOWN 0 // DOWN must be FALSE
#define MSCR_UP 1
-#define MSCR_LEFT -1
-#define MSCR_RIGHT -2
-
+#define MSCR_LEFT (-1)
+#define MSCR_RIGHT (-2)
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "mouse.h.generated.h"
diff --git a/src/nvim/move.c b/src/nvim/move.c
index 67ec19903f..bd68ad6f97 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -31,6 +31,7 @@
#include "nvim/plines.h"
#include "nvim/popupmnu.h"
#include "nvim/screen.h"
+#include "nvim/search.h"
#include "nvim/strings.h"
#include "nvim/window.h"
@@ -44,7 +45,6 @@ typedef struct {
# include "move.c.generated.h"
#endif
-
/*
* Compute wp->w_botline for the current wp->w_topline. Can be called after
* wp->w_topline changed.
@@ -95,33 +95,39 @@ static void comp_botline(win_T *wp)
win_check_anchored_floats(wp);
}
-void reset_cursorline(void)
+/// Redraw when w_cline_row changes and 'relativenumber' or 'cursorline' is set.
+/// Also when concealing is on and 'concealcursor' is not active.
+void redraw_for_cursorline(win_T *wp)
+ FUNC_ATTR_NONNULL_ALL
{
- curwin->w_last_cursorline = 0;
+ if ((wp->w_valid & VALID_CROW) == 0 && !pum_visible()
+ && (wp->w_p_rnu || win_cursorline_standout(wp))) {
+ // win_line() will redraw the number column and cursorline only.
+ redraw_later(wp, VALID);
+ }
}
-// Redraw when w_cline_row changes and 'relativenumber' or 'cursorline' is set.
-void redraw_for_cursorline(win_T *wp)
+/// Redraw when w_virtcol changes and 'cursorcolumn' is set or 'cursorlineopt'
+/// contains "screenline" or when the "CurSearch" highlight is in use.
+/// Also when concealing is on and 'concealcursor' is active.
+static void redraw_for_cursorcolumn(win_T *wp)
FUNC_ATTR_NONNULL_ALL
{
- if ((wp->w_p_rnu || win_cursorline_standout(wp))
- && (wp->w_valid & VALID_CROW) == 0
- && !pum_visible()) {
- if (wp->w_p_rnu) {
- // win_line() will redraw the number column only.
+ if ((wp->w_valid & VALID_VIRTCOL) == 0 && !pum_visible()) {
+ if (wp->w_p_cuc || ((HL_ATTR(HLF_LC) || wp->w_hl_ids[HLF_LC]) && using_hlsearch())) {
+ // When 'cursorcolumn' is set or "CurSearch" is in use
+ // need to redraw with SOME_VALID.
+ redraw_later(wp, SOME_VALID);
+ } else if (wp->w_p_cul && (wp->w_p_culopt_flags & CULOPT_SCRLINE)) {
+ // When 'cursorlineopt' contains "screenline" need to redraw with VALID.
redraw_later(wp, VALID);
}
- if (win_cursorline_standout(wp)) {
- if (wp->w_redr_type <= VALID && wp->w_last_cursorline != 0) {
- // "w_last_cursorline" may be outdated, worst case we redraw
- // too much. This is optimized for moving the cursor around in
- // the current window.
- redrawWinline(wp, wp->w_last_cursorline);
- redrawWinline(wp, wp->w_cursor.lnum);
- } else {
- redraw_later(wp, SOME_VALID);
- }
- }
+ }
+ // If the cursor moves horizontally when 'concealcursor' is active, then the
+ // current line needs to be redrawn in order to calculate the correct
+ // cursor position.
+ if ((wp->w_valid & VALID_VIRTCOL) == 0 && wp->w_p_cole > 0 && conceal_cursor_line(wp)) {
+ redrawWinline(wp, wp->w_cursor.lnum);
}
}
@@ -346,10 +352,10 @@ void update_topline(win_T *wp)
*/
void update_topline_win(win_T *win)
{
- win_T *save_curwin;
- switch_win(&save_curwin, NULL, win, NULL, true);
+ switchwin_T switchwin;
+ switch_win(&switchwin, win, NULL, true);
update_topline(curwin);
- restore_win(save_curwin, NULL, true);
+ restore_win(&switchwin, true);
}
/*
@@ -641,11 +647,8 @@ void validate_virtcol_win(win_T *wp)
check_cursor_moved(wp);
if (!(wp->w_valid & VALID_VIRTCOL)) {
getvvcol(wp, &wp->w_cursor, NULL, &(wp->w_virtcol), NULL);
+ redraw_for_cursorcolumn(wp);
wp->w_valid |= VALID_VIRTCOL;
- if (wp->w_p_cuc
- && !pum_visible()) {
- redraw_later(wp, SOME_VALID);
- }
}
}
@@ -776,10 +779,14 @@ void curs_columns(win_T *wp, int may_scroll)
int textwidth = wp->w_width_inner - extra;
if (textwidth <= 0) {
// No room for text, put cursor in last char of window.
+ // If not wrapping, the last non-empty line.
wp->w_wcol = wp->w_width_inner - 1;
- wp->w_wrow = wp->w_height_inner - 1;
- } else if (wp->w_p_wrap
- && wp->w_width_inner != 0) {
+ if (wp->w_p_wrap) {
+ wp->w_wrow = wp->w_height_inner - 1;
+ } else {
+ wp->w_wrow = wp->w_height_inner - 1 - wp->w_empty_rows;
+ }
+ } else if (wp->w_p_wrap && wp->w_width_inner != 0) {
width = textwidth + win_col_off2(wp);
// long line wrapping, adjust wp->w_wrow
@@ -792,7 +799,7 @@ void curs_columns(win_T *wp, int may_scroll)
// When cursor wraps to first char of next line in Insert
// mode, the 'showbreak' string isn't shown, backup to first
// column
- char_u *const sbr = get_showbreak_value(wp);
+ char *const sbr = (char *)get_showbreak_value(wp);
if (*sbr && *get_cursor_pos_ptr() == NUL
&& wp->w_wcol == vim_strsize(sbr)) {
wp->w_wcol = 0;
@@ -948,11 +955,7 @@ void curs_columns(win_T *wp, int may_scroll)
redraw_later(wp, NOT_VALID);
}
- // Redraw when w_virtcol changes and 'cursorcolumn' is set
- if (wp->w_p_cuc && (wp->w_valid & VALID_VIRTCOL) == 0
- && !pum_visible()) {
- redraw_later(wp, SOME_VALID);
- }
+ redraw_for_cursorcolumn(curwin);
// now w_leftcol is valid, avoid check_cursor_moved() thinking otherwise
wp->w_valid_leftcol = wp->w_leftcol;
@@ -1011,7 +1014,7 @@ void textpos2screenpos(win_T *wp, pos_T *pos, int *rowp, int *scolp, int *ccolp,
col -= wp->w_leftcol;
if (col >= 0 && col < wp->w_width) {
- coloff = col - scol + (local ? 0 : wp->w_wincol + wp->w_border_adj[3]) + 1;
+ coloff = col - scol + (local ? 0 : wp->w_wincol + wp->w_wincol_off) + 1;
} else {
scol = ccol = ecol = 0;
// character is left or right of the window
@@ -1022,7 +1025,7 @@ void textpos2screenpos(win_T *wp, pos_T *pos, int *rowp, int *scolp, int *ccolp,
}
}
}
- *rowp = (local ? 0 : wp->w_winrow + wp->w_border_adj[0]) + row + rowoff;
+ *rowp = (local ? 0 : wp->w_winrow + wp->w_winrow_off) + row + rowoff;
*scolp = scol + coloff;
*ccolp = ccol + coloff;
*ecolp = ecol + coloff;
@@ -1079,8 +1082,7 @@ bool scrolldown(long line_count, int byfold)
* and move the cursor onto the displayed part of the window.
*/
int wrow = curwin->w_wrow;
- if (curwin->w_p_wrap
- && curwin->w_width_inner != 0) {
+ if (curwin->w_p_wrap && curwin->w_width_inner != 0) {
validate_virtcol();
validate_cheight();
wrow += curwin->w_cline_height - 1 -
@@ -1142,8 +1144,8 @@ bool scrollup(long line_count, int byfold)
curwin->w_botline += lnum - curwin->w_topline;
curwin->w_topline = lnum;
} else {
- curwin->w_topline += line_count;
- curwin->w_botline += line_count; // approximate w_botline
+ curwin->w_topline += (linenr_T)line_count;
+ curwin->w_botline += (linenr_T)line_count; // approximate w_botline
}
if (curwin->w_topline > curbuf->b_ml.ml_line_count) {
@@ -1514,12 +1516,10 @@ void set_empty_rows(win_T *wp, int used)
}
}
-/*
- * Recompute topline to put the cursor at the bottom of the window.
- * Scroll at least "min_scroll" lines.
- * If "set_topbot" is true, set topline and botline first (for "zb").
- * This is messy stuff!!!
- */
+/// Recompute topline to put the cursor at the bottom of the window.
+/// When scrolling scroll at least "min_scroll" lines.
+/// If "set_topbot" is true, set topline and botline first (for "zb").
+/// This is messy stuff!!!
void scroll_cursor_bot(int min_scroll, int set_topbot)
{
int used;
@@ -1858,7 +1858,6 @@ void cursor_correct(void)
curwin->w_viewport_invalid = true;
}
-
/*
* move screen 'count' pages up or down and update screen
*
@@ -1900,7 +1899,7 @@ int onepage(Direction dir, long count)
if (p_window <= 2) {
++curwin->w_topline;
} else {
- curwin->w_topline += p_window - 2;
+ curwin->w_topline += (linenr_T)p_window - 2;
}
if (curwin->w_topline > curbuf->b_ml.ml_line_count) {
curwin->w_topline = curbuf->b_ml.ml_line_count;
@@ -1936,12 +1935,12 @@ int onepage(Direction dir, long count)
if (p_window <= 2) {
--curwin->w_topline;
} else {
- curwin->w_topline -= p_window - 2;
+ curwin->w_topline -= (linenr_T)p_window - 2;
}
if (curwin->w_topline < 1) {
curwin->w_topline = 1;
}
- curwin->w_cursor.lnum = curwin->w_topline + p_window - 1;
+ curwin->w_cursor.lnum = curwin->w_topline + (linenr_T)p_window - 1;
if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) {
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
}
@@ -2272,9 +2271,7 @@ void do_check_cursorbind(void)
int restart_edit_save = restart_edit;
restart_edit = true;
check_cursor();
- if (win_cursorline_standout(curwin) || curwin->w_p_cuc) {
- validate_cursor();
- }
+ validate_cursor();
restart_edit = restart_edit_save;
}
// Correct cursor for multi-byte character.
@@ -2297,4 +2294,3 @@ void do_check_cursorbind(void)
curwin = old_curwin;
curbuf = old_curbuf;
}
-
diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c
index 299651ee97..de01443313 100644
--- a/src/nvim/msgpack_rpc/channel.c
+++ b/src/nvim/msgpack_rpc/channel.c
@@ -26,12 +26,13 @@
#include "nvim/message.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/msgpack_rpc/helpers.h"
+#include "nvim/msgpack_rpc/unpacker.h"
#include "nvim/os/input.h"
#include "nvim/os_unix.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
-#if MIN_LOG_LEVEL > DEBUG_LOG_LEVEL
+#if MIN_LOG_LEVEL > LOGLVL_DBG
# define log_client_msg(...)
# define log_server_msg(...)
#endif
@@ -49,21 +50,21 @@ void rpc_init(void)
msgpack_sbuffer_init(&out_buffer);
}
-
void rpc_start(Channel *channel)
{
channel_incref(channel);
channel->is_rpc = true;
RpcState *rpc = &channel->rpc;
rpc->closed = false;
- rpc->unpacker = msgpack_unpacker_new(MSGPACK_UNPACKER_INIT_BUFFER_SIZE);
+ rpc->unpacker = xcalloc(1, sizeof *rpc->unpacker);
+ unpacker_init(rpc->unpacker);
rpc->next_request_id = 1;
rpc->info = (Dictionary)ARRAY_DICT_INIT;
kv_init(rpc->call_stack);
if (channel->streamtype != kChannelStreamInternal) {
Stream *out = channel_outstream(channel);
-#if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL
+#if MIN_LOG_LEVEL <= LOGLVL_DBG
Stream *in = channel_instream(channel);
DLOG("rpc ch %" PRIu64 " in-stream=%p out-stream=%p", channel->id,
(void *)in, (void *)out);
@@ -73,7 +74,6 @@ void rpc_start(Channel *channel)
}
}
-
static Channel *find_rpc_channel(uint64_t id)
{
Channel *chan = find_channel(id);
@@ -114,7 +114,8 @@ bool rpc_send_event(uint64_t id, const char *name, Array args)
/// @param args Array with method arguments
/// @param[out] error True if the return value is an error
/// @return Whatever the remote method returned
-Object rpc_send_call(uint64_t id, const char *method_name, Array args, Error *err)
+Object rpc_send_call(uint64_t id, const char *method_name, Array args, ArenaMem *result_mem,
+ Error *err)
{
Channel *channel = NULL;
@@ -131,7 +132,7 @@ Object rpc_send_call(uint64_t id, const char *method_name, Array args, Error *er
send_request(channel, request_id, method_name, args);
// Push the frame
- ChannelCallFrame frame = { request_id, false, false, NIL };
+ ChannelCallFrame frame = { request_id, false, false, NIL, NULL };
kv_push(rpc->call_stack, &frame);
LOOP_PROCESS_EVENTS_UNTIL(&main_loop, channel->events, -1, frame.returned);
(void)kv_pop(rpc->call_stack);
@@ -156,11 +157,15 @@ Object rpc_send_call(uint64_t id, const char *method_name, Array args, Error *er
api_set_error(err, kErrorTypeException, "%s", "unknown error");
}
- api_free_object(frame.result);
+ // frame.result was allocated in an arena
+ arena_mem_free(frame.result_mem, &rpc->unpacker->reuse_blk);
+ frame.result_mem = NULL;
}
channel_decref(channel);
+ *result_mem = frame.result_mem;
+
return frame.errored ? NIL : frame.result;
}
@@ -211,20 +216,20 @@ static void receive_msgpack(Stream *stream, RBuffer *rbuf, size_t c, void *data,
char buf[256];
snprintf(buf, sizeof(buf), "ch %" PRIu64 " was closed by the client",
channel->id);
- call_set_error(channel, buf, INFO_LOG_LEVEL);
+ chan_close_with_error(channel, buf, LOGLVL_INF);
goto end;
}
- size_t count = rbuffer_size(rbuf);
DLOG("ch %" PRIu64 ": parsing %zu bytes from msgpack Stream: %p",
- channel->id, count, (void *)stream);
-
- // Feed the unpacker with data
- msgpack_unpacker_reserve_buffer(channel->rpc.unpacker, count);
- rbuffer_read(rbuf, msgpack_unpacker_buffer(channel->rpc.unpacker), count);
- msgpack_unpacker_buffer_consumed(channel->rpc.unpacker, count);
+ channel->id, rbuffer_size(rbuf), (void *)stream);
+ Unpacker *p = channel->rpc.unpacker;
+ size_t size = 0;
+ p->read_ptr = rbuffer_read_ptr(rbuf, &size);
+ p->read_size = size;
parse_msgpack(channel);
+ size_t consumed = size - p->read_size;
+ rbuffer_consumed_compact(rbuf, consumed);
end:
channel_decref(channel);
@@ -232,111 +237,71 @@ end:
static void parse_msgpack(Channel *channel)
{
- msgpack_unpacked unpacked;
- msgpack_unpacked_init(&unpacked);
- msgpack_unpack_return result;
-
- // Deserialize everything we can.
- while ((result = msgpack_unpacker_next(channel->rpc.unpacker, &unpacked)) ==
- MSGPACK_UNPACK_SUCCESS) {
- bool is_response = is_rpc_response(&unpacked.data);
- log_client_msg(channel->id, !is_response, unpacked.data);
-
- if (is_response) {
- if (is_valid_rpc_response(&unpacked.data, channel)) {
- complete_call(&unpacked.data, channel);
- } else {
+ Unpacker *p = channel->rpc.unpacker;
+ while (unpacker_advance(p)) {
+ if (p->type == kMessageTypeResponse) {
+ ChannelCallFrame *frame = kv_last(channel->rpc.call_stack);
+ if (p->request_id != frame->request_id) {
char buf[256];
snprintf(buf, sizeof(buf),
"ch %" PRIu64 " returned a response with an unknown request "
"id. Ensure the client is properly synchronized",
channel->id);
- call_set_error(channel, buf, ERROR_LOG_LEVEL);
+ chan_close_with_error(channel, buf, LOGLVL_ERR);
+ }
+ frame->returned = true;
+ frame->errored = (p->error.type != kObjectTypeNil);
+
+ if (frame->errored) {
+ frame->result = p->error;
+ // TODO(bfredl): p->result should not even be decoded
+ // api_free_object(p->result);
+ } else {
+ frame->result = p->result;
}
- msgpack_unpacked_destroy(&unpacked);
+ frame->result_mem = arena_finish(&p->arena);
} else {
- handle_request(channel, &unpacked.data);
- }
- }
+ log_client_msg(channel->id, p->type == kMessageTypeRequest, p->handler.name);
- if (result == MSGPACK_UNPACK_NOMEM_ERROR) {
- mch_errmsg(e_outofmem);
- mch_errmsg("\n");
- channel_decref(channel);
- preserve_exit();
+ Object res = p->result;
+ if (p->result.type != kObjectTypeArray) {
+ chan_close_with_error(channel, "msgpack-rpc request args has to be an array", LOGLVL_ERR);
+ return;
+ }
+ Array arg = res.data.array;
+ handle_request(channel, p, arg);
+ }
}
- if (result == MSGPACK_UNPACK_PARSE_ERROR) {
- // See src/msgpack/unpack_template.h in msgpack source tree for
- // causes for this error(search for 'goto _failed')
- //
- // A not so uncommon cause for this might be deserializing objects with
- // a high nesting level: msgpack will break when its internal parse stack
- // size exceeds MSGPACK_EMBED_STACK_SIZE (defined as 32 by default)
- send_error(channel, kMessageTypeRequest, 0,
- "Invalid msgpack payload. "
- "This error can also happen when deserializing "
- "an object with high level of nesting");
+ if (unpacker_closed(p)) {
+ chan_close_with_error(channel, p->unpack_error.msg, LOGLVL_ERR);
+ api_clear_error(&p->unpack_error);
}
}
/// Handles requests and notifications received on the channel.
-static void handle_request(Channel *channel, msgpack_object *request)
+static void handle_request(Channel *channel, Unpacker *p, Array args)
FUNC_ATTR_NONNULL_ALL
{
- uint32_t request_id;
- Error error = ERROR_INIT;
- MessageType type = msgpack_rpc_validate(&request_id, request, &error);
+ assert(p->type == kMessageTypeRequest || p->type == kMessageTypeNotification);
- if (ERROR_SET(&error)) {
- // Validation failed, send response with error
- if (channel_write(channel,
- serialize_response(channel->id,
- type,
- request_id,
- &error,
- NIL,
- &out_buffer))) {
- char buf[256];
- snprintf(buf, sizeof(buf),
- "ch %" PRIu64 " sent an invalid message, closed.",
- channel->id);
- call_set_error(channel, buf, ERROR_LOG_LEVEL);
- }
- api_clear_error(&error);
- return;
- }
- assert(type == kMessageTypeRequest || type == kMessageTypeNotification);
-
- MsgpackRpcRequestHandler handler;
- msgpack_object *method = msgpack_rpc_method(request);
- handler = msgpack_rpc_get_handler_for(method->via.bin.ptr,
- method->via.bin.size,
- &error);
-
- // check method arguments
- Array args = ARRAY_DICT_INIT;
- if (!ERROR_SET(&error)
- && !msgpack_rpc_to_array(msgpack_rpc_args(request), &args)) {
- api_set_error(&error, kErrorTypeException, "Invalid method arguments");
- }
-
- if (ERROR_SET(&error)) {
- send_error(channel, type, request_id, error.msg);
- api_clear_error(&error);
- api_free_array(args);
+ if (!p->handler.fn) {
+ send_error(channel, p->type, p->request_id, p->unpack_error.msg);
+ api_clear_error(&p->unpack_error);
+ arena_mem_free(arena_finish(&p->arena), &p->reuse_blk);
return;
}
RequestEvent *evdata = xmalloc(sizeof(RequestEvent));
- evdata->type = type;
+ evdata->type = p->type;
evdata->channel = channel;
- evdata->handler = handler;
+ evdata->handler = p->handler;
evdata->args = args;
- evdata->request_id = request_id;
+ evdata->used_mem = arena_finish(&p->arena);
+ evdata->request_id = p->request_id;
channel_incref(channel);
- if (handler.fast) {
- bool is_get_mode = handler.fn == handle_nvim_get_mode;
+ if (p->handler.fast) {
+ bool is_get_mode = p->handler.fn == handle_nvim_get_mode;
if (is_get_mode && !input_blocking()) {
// Defer the event to a special queue used by os/input.c. #6247
@@ -346,7 +311,7 @@ static void handle_request(Channel *channel, msgpack_object *request)
request_event((void **)&evdata);
}
} else {
- bool is_resize = handler.fn == handle_nvim_ui_try_resize;
+ bool is_resize = p->handler.fn == handle_nvim_ui_try_resize;
if (is_resize) {
Event ev = event_create_oneshot(event_create(request_event, 1, evdata),
2);
@@ -354,12 +319,11 @@ static void handle_request(Channel *channel, msgpack_object *request)
multiqueue_put_event(resize_events, ev);
} else {
multiqueue_put(channel->events, request_event, 1, evdata);
- DLOG("RPC: scheduled %.*s", method->via.bin.size, method->via.bin.ptr);
+ DLOG("RPC: scheduled %.*s", (int)p->method_name_len, p->handler.name);
}
}
}
-
/// Handles a message, depending on the type:
/// - Request: invokes method and writes the response (or error).
/// - Notification: invokes method (emits `nvim_error_event` on error).
@@ -389,12 +353,24 @@ static void request_event(void **argv)
}
free_ret:
- api_free_array(e->args);
+ // e->args is allocated in an arena
+ arena_mem_free(e->used_mem, &channel->rpc.unpacker->reuse_blk);
channel_decref(channel);
xfree(e);
api_clear_error(&error);
}
+bool rpc_write_raw(uint64_t id, WBuffer *buffer)
+{
+ Channel *channel = find_rpc_channel(id);
+ if (!channel) {
+ wstream_release_wbuffer(buffer);
+ return false;
+ }
+
+ return channel_write(channel, buffer);
+}
+
static bool channel_write(Channel *channel, WBuffer *buffer)
{
bool success;
@@ -413,7 +389,6 @@ static bool channel_write(Channel *channel, WBuffer *buffer)
success = wstream_write(in, buffer);
}
-
if (!success) {
// If the write failed for any reason, close the channel
char buf[256];
@@ -422,7 +397,7 @@ static bool channel_write(Channel *channel, WBuffer *buffer)
"ch %" PRIu64 ": stream write failed. "
"RPC canceled; closing channel",
channel->id);
- call_set_error(channel, buf, ERROR_LOG_LEVEL);
+ chan_close_with_error(channel, buf, LOGLVL_ERR);
}
return success;
@@ -432,14 +407,19 @@ static void internal_read_event(void **argv)
{
Channel *channel = argv[0];
WBuffer *buffer = argv[1];
+ Unpacker *p = channel->rpc.unpacker;
- msgpack_unpacker_reserve_buffer(channel->rpc.unpacker, buffer->size);
- memcpy(msgpack_unpacker_buffer(channel->rpc.unpacker),
- buffer->data, buffer->size);
- msgpack_unpacker_buffer_consumed(channel->rpc.unpacker, buffer->size);
-
+ p->read_ptr = buffer->data;
+ p->read_size = buffer->size;
parse_msgpack(channel);
+ if (p->read_size) {
+ // This should not happen, as WBuffer is one single serialized message.
+ if (!channel->rpc.closed) {
+ chan_close_with_error(channel, "internal channel: internal error", LOGLVL_ERR);
+ }
+ }
+
channel_decref(channel);
wstream_release_wbuffer(buffer);
}
@@ -535,7 +515,6 @@ static void unsubscribe(Channel *channel, char *event)
xfree(event_string);
}
-
/// Mark rpc state as closed, and release its reference to the channel.
/// Don't call this directly, call channel_close(id, kChannelPartRpc, &error)
void rpc_close(Channel *channel)
@@ -547,13 +526,25 @@ void rpc_close(Channel *channel)
channel->rpc.closed = true;
channel_decref(channel);
- if (channel->streamtype == kChannelStreamStdio) {
+ if (channel->streamtype == kChannelStreamStdio
+ || channel->id == ui_client_channel_id) {
multiqueue_put(main_loop.fast_events, exit_event, 0);
}
}
+static void exit_delay_cb(uv_timer_t *handle)
+{
+ uv_timer_stop(&main_loop.exit_delay_timer);
+ multiqueue_put(main_loop.fast_events, exit_event, 0);
+}
+
static void exit_event(void **argv)
{
+ if (exit_need_delay) {
+ uv_timer_start(&main_loop.exit_delay_timer, exit_delay_cb, 0, 0);
+ return;
+ }
+
if (!exiting) {
os_exit(0);
}
@@ -562,7 +553,8 @@ static void exit_event(void **argv)
void rpc_free(Channel *channel)
{
remote_ui_disconnect(channel->id);
- msgpack_unpacker_free(channel->rpc.unpacker);
+ unpacker_teardown(channel->rpc.unpacker);
+ xfree(channel->rpc.unpacker);
// Unsubscribe from all events
char *event_string;
@@ -575,48 +567,13 @@ void rpc_free(Channel *channel)
api_free_dictionary(channel->rpc.info);
}
-static bool is_rpc_response(msgpack_object *obj)
-{
- return obj->type == MSGPACK_OBJECT_ARRAY
- && obj->via.array.size == 4
- && obj->via.array.ptr[0].type == MSGPACK_OBJECT_POSITIVE_INTEGER
- && obj->via.array.ptr[0].via.u64 == 1
- && obj->via.array.ptr[1].type == MSGPACK_OBJECT_POSITIVE_INTEGER;
-}
-
-static bool is_valid_rpc_response(msgpack_object *obj, Channel *channel)
-{
- uint32_t response_id = (uint32_t)obj->via.array.ptr[1].via.u64;
- if (kv_size(channel->rpc.call_stack) == 0) {
- return false;
- }
-
- // Must be equal to the frame at the stack's bottom
- ChannelCallFrame *frame = kv_last(channel->rpc.call_stack);
- return response_id == frame->request_id;
-}
-
-static void complete_call(msgpack_object *obj, Channel *channel)
-{
- ChannelCallFrame *frame = kv_last(channel->rpc.call_stack);
- frame->returned = true;
- frame->errored = obj->via.array.ptr[2].type != MSGPACK_OBJECT_NIL;
-
- if (frame->errored) {
- msgpack_rpc_to_object(&obj->via.array.ptr[2], &frame->result);
- } else {
- msgpack_rpc_to_object(&obj->via.array.ptr[3], &frame->result);
- }
-}
-
-static void call_set_error(Channel *channel, char *msg, int loglevel)
+static void chan_close_with_error(Channel *channel, char *msg, int loglevel)
{
LOG(loglevel, "RPC: %s", msg);
for (size_t i = 0; i < kv_size(channel->rpc.call_stack); i++) {
ChannelCallFrame *frame = kv_A(channel->rpc.call_stack, i);
frame->returned = true;
frame->errored = true;
- api_free_object(frame->result);
frame->result = STRING_OBJ(cstr_to_string(msg));
}
@@ -697,7 +654,7 @@ const char *rpc_client_name(Channel *chan)
return NULL;
}
-#if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL
+#if MIN_LOG_LEVEL <= LOGLVL_DBG
# define REQ "[request] "
# define RES "[response] "
# define NOT "[notify] "
@@ -727,7 +684,8 @@ static void log_server_msg(uint64_t channel_id, msgpack_sbuffer *packed)
log_lock();
FILE *f = open_log_file();
fprintf(f, type ? (type == 1 ? RES : NOT) : REQ);
- log_msg_close(f, unpacked.data);
+ msgpack_object_print(f, unpacked.data);
+ log_close(f);
msgpack_unpacked_destroy(&unpacked);
break;
}
@@ -738,30 +696,24 @@ static void log_server_msg(uint64_t channel_id, msgpack_sbuffer *packed)
log_lock();
FILE *f = open_log_file();
fprintf(f, ERR);
- log_msg_close(f, (msgpack_object) {
- .type = MSGPACK_OBJECT_STR,
- .via.str = {
- .ptr = (char *)msgpack_error_messages[result + MUR_OFF],
- .size = (uint32_t)strlen(msgpack_error_messages[result + MUR_OFF]),
- },
- });
+ fprintf(f, "%s", msgpack_error_messages[result + MUR_OFF]);
+ log_close(f);
break;
}
}
}
-static void log_client_msg(uint64_t channel_id, bool is_request, msgpack_object msg)
+static void log_client_msg(uint64_t channel_id, bool is_request, const char *name)
{
DLOGN("RPC <-ch %" PRIu64 ": ", channel_id);
log_lock();
FILE *f = open_log_file();
- fprintf(f, is_request ? REQ : RES);
- log_msg_close(f, msg);
+ fprintf(f, "%s: %s", is_request ? REQ : RES, name);
+ log_close(f);
}
-static void log_msg_close(FILE *f, msgpack_object msg)
+static void log_close(FILE *f)
{
- msgpack_object_print(f, msg);
fputc('\n', f);
fflush(f);
fclose(f);
diff --git a/src/nvim/msgpack_rpc/channel.h b/src/nvim/msgpack_rpc/channel.h
index eb0de47437..ac7911bb2c 100644
--- a/src/nvim/msgpack_rpc/channel.h
+++ b/src/nvim/msgpack_rpc/channel.h
@@ -17,7 +17,6 @@
/// of os_inchar(), so they are processed "just-in-time".
EXTERN MultiQueue *ch_before_blocking_events INIT(= NULL);
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "msgpack_rpc/channel.h.generated.h"
#endif
diff --git a/src/nvim/msgpack_rpc/channel_defs.h b/src/nvim/msgpack_rpc/channel_defs.h
index 6647779db9..e622ebddf5 100644
--- a/src/nvim/msgpack_rpc/channel_defs.h
+++ b/src/nvim/msgpack_rpc/channel_defs.h
@@ -6,16 +6,19 @@
#include <uv.h>
#include "nvim/api/private/defs.h"
+#include "nvim/api/private/dispatch.h"
#include "nvim/event/process.h"
#include "nvim/event/socket.h"
#include "nvim/vim.h"
typedef struct Channel Channel;
+typedef struct Unpacker Unpacker;
typedef struct {
uint32_t request_id;
bool returned, errored;
Object result;
+ ArenaMem result_mem;
} ChannelCallFrame;
typedef struct {
@@ -24,12 +27,13 @@ typedef struct {
MsgpackRpcRequestHandler handler;
Array args;
uint32_t request_id;
+ ArenaMem used_mem;
} RequestEvent;
typedef struct {
PMap(cstr_t) subscribed_events[1];
bool closed;
- msgpack_unpacker *unpacker;
+ Unpacker *unpacker;
uint32_t next_request_id;
kvec_t(ChannelCallFrame *) call_stack;
Dictionary info;
diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c
index 32014fcf2b..488321be42 100644
--- a/src/nvim/msgpack_rpc/helpers.c
+++ b/src/nvim/msgpack_rpc/helpers.c
@@ -22,7 +22,6 @@
static msgpack_zone zone;
static msgpack_sbuffer sbuffer;
-
void msgpack_rpc_helpers_init(void)
{
msgpack_zone_init(&zone, 0xfff);
@@ -252,7 +251,6 @@ bool msgpack_rpc_to_dictionary(const msgpack_object *const obj, Dictionary *cons
arg->size = obj->via.array.size;
arg->items = xcalloc(obj->via.map.size, sizeof(KeyValuePair));
-
for (uint32_t i = 0; i < obj->via.map.size; i++) {
if (!msgpack_rpc_to_string(&obj->via.map.ptr[i].key,
&arg->items[i].key)) {
diff --git a/src/nvim/msgpack_rpc/helpers.h b/src/nvim/msgpack_rpc/helpers.h
index e5fd92374d..dab8a16b6b 100644
--- a/src/nvim/msgpack_rpc/helpers.h
+++ b/src/nvim/msgpack_rpc/helpers.h
@@ -21,4 +21,3 @@
#endif
#endif // NVIM_MSGPACK_RPC_HELPERS_H
-
diff --git a/src/nvim/msgpack_rpc/server.c b/src/nvim/msgpack_rpc/server.c
index e954e4b3a3..b252f0998e 100644
--- a/src/nvim/msgpack_rpc/server.c
+++ b/src/nvim/msgpack_rpc/server.c
@@ -22,7 +22,7 @@
#include "nvim/vim.h"
#define MAX_CONNECTIONS 32
-#define LISTEN_ADDRESS_ENV_VAR "NVIM_LISTEN_ADDRESS"
+#define ENV_LISTEN "NVIM_LISTEN_ADDRESS" // deprecated
static garray_T watchers = GA_EMPTY_INIT_VALUE;
@@ -35,20 +35,29 @@ bool server_init(const char *listen_addr)
{
ga_init(&watchers, sizeof(SocketWatcher *), 1);
- // $NVIM_LISTEN_ADDRESS
- const char *env_addr = os_getenv(LISTEN_ADDRESS_ENV_VAR);
- int rv = listen_addr == NULL ? 1 : server_start(listen_addr);
+ // $NVIM_LISTEN_ADDRESS (deprecated)
+ if (!listen_addr && os_env_exists(ENV_LISTEN)) {
+ listen_addr = os_getenv(ENV_LISTEN);
+ }
+ int rv = listen_addr ? server_start(listen_addr) : 1;
if (0 != rv) {
- rv = env_addr == NULL ? 1 : server_start(env_addr);
- if (0 != rv) {
- listen_addr = server_address_new();
- if (listen_addr == NULL) {
- return false;
- }
- rv = server_start(listen_addr);
- xfree((char *)listen_addr);
+ listen_addr = server_address_new(NULL);
+ if (!listen_addr) {
+ return false;
}
+ rv = server_start(listen_addr);
+ xfree((char *)listen_addr);
+ }
+
+ if (os_env_exists(ENV_LISTEN)) {
+ // Unset $NVIM_LISTEN_ADDRESS, it's a liability hereafter.
+ os_unsetenv(ENV_LISTEN);
+ }
+
+ // TODO(justinmk): this is for logging_spec. Can remove this after nvim_log #7062 is merged.
+ if (os_env_exists("__NVIM_TEST_LOG")) {
+ ELOG("test log message");
}
return rv == 0;
@@ -60,8 +69,8 @@ static void close_socket_watcher(SocketWatcher **watcher)
socket_watcher_close(*watcher, free_server);
}
-/// Set v:servername to the first server in the server list, or unset it if no
-/// servers are known.
+/// Sets the "primary address" (v:servername and $NVIM) to the first server in
+/// the server list, or unsets if no servers are known.
static void set_vservername(garray_T *srvs)
{
char *default_server = (srvs->ga_len > 0)
@@ -78,23 +87,26 @@ void server_teardown(void)
/// Generates unique address for local server.
///
-/// In Windows this is a named pipe in the format
-/// \\.\pipe\nvim-<PID>-<COUNTER>.
-///
-/// For other systems it is a path returned by vim_tempname().
-///
-/// This function is NOT thread safe
-char *server_address_new(void)
+/// Named pipe format:
+/// - Windows: "\\.\pipe\<name>.<pid>.<counter>"
+/// - Other: "/tmp/nvim.user/xxx/<name>.<pid>.<counter>"
+char *server_address_new(const char *name)
{
-#ifdef WIN32
static uint32_t count = 0;
- char template[ADDRESS_MAX_SIZE];
- snprintf(template, ADDRESS_MAX_SIZE,
- "\\\\.\\pipe\\nvim-%" PRIu64 "-%" PRIu32, os_get_pid(), count++);
- return xstrdup(template);
+ char fmt[ADDRESS_MAX_SIZE];
+#ifdef WIN32
+ int r = snprintf(fmt, sizeof(fmt), "\\\\.\\pipe\\%s.%" PRIu64 ".%" PRIu32,
+ name ? name : "nvim", os_get_pid(), count++);
#else
- return (char *)vim_tempname();
+ char *dir = stdpaths_get_xdg_var(kXDGRuntimeDir);
+ int r = snprintf(fmt, sizeof(fmt), "%s/%s.%" PRIu64 ".%" PRIu32,
+ dir, name ? name : "nvim", os_get_pid(), count++);
+ xfree(dir);
#endif
+ if ((size_t)r >= sizeof(fmt)) {
+ ELOG("truncated server address");
+ }
+ return xstrdup(fmt);
}
/// Check if this instance owns a pipe address.
@@ -109,35 +121,35 @@ bool server_owns_pipe_address(const char *path)
return false;
}
-/// Starts listening for API calls.
+/// Starts listening for RPC calls.
///
-/// The socket type is determined by parsing `endpoint`: If it's a valid IPv4
-/// or IPv6 address in 'ip:[port]' format, then it will be a TCP socket.
-/// Otherwise it will be a Unix socket or named pipe (Windows).
+/// Socket type is decided by the format of `addr`:
+/// - TCP socket if it looks like an IPv4/6 address ("ip:[port]").
+/// - If [port] is omitted, a random one is assigned.
+/// - Unix socket (or named pipe on Windows) otherwise.
+/// - If the name doesn't contain slashes it is appended to a generated path. #8519
///
-/// If no port is given, a random one will be assigned.
-///
-/// @param endpoint Address of the server. Either a 'ip:[port]' string or an
-/// arbitrary identifier (trimmed to 256 bytes) for the Unix
-/// socket or named pipe.
-/// @returns 0: success, 1: validation error, 2: already listening,
-/// -errno: failed to bind or listen.
-int server_start(const char *endpoint)
+/// @param addr Server address: a "ip:[port]" string or arbitrary name or filepath (max 256 bytes)
+/// for the Unix socket or named pipe.
+/// @returns 0: success, 1: validation error, 2: already listening, -errno: failed to bind/listen.
+int server_start(const char *addr)
{
- if (endpoint == NULL || endpoint[0] == '\0') {
- WLOG("Empty or NULL endpoint");
+ if (addr == NULL || addr[0] == '\0') {
+ WLOG("Empty or NULL address");
return 1;
}
+ bool isname = !strstr(addr, ":") && !strstr(addr, "/") && !strstr(addr, "\\");
+ char *addr_gen = isname ? server_address_new(addr) : NULL;
SocketWatcher *watcher = xmalloc(sizeof(SocketWatcher));
-
- int result = socket_watcher_init(&main_loop, watcher, endpoint);
+ int result = socket_watcher_init(&main_loop, watcher, isname ? addr_gen : addr);
+ xfree(addr_gen);
if (result < 0) {
xfree(watcher);
return result;
}
- // Check if a watcher for the endpoint already exists
+ // Check if a watcher for the address already exists.
for (int i = 0; i < watchers.ga_len; i++) {
if (!strcmp(watcher->addr, ((SocketWatcher **)watchers.ga_data)[i]->addr)) {
ELOG("Already listening on %s", watcher->addr);
@@ -156,12 +168,6 @@ int server_start(const char *endpoint)
return result;
}
- // Update $NVIM_LISTEN_ADDRESS, if not set.
- const char *listen_address = os_getenv(LISTEN_ADDRESS_ENV_VAR);
- if (listen_address == NULL) {
- os_setenv(LISTEN_ADDRESS_ENV_VAR, watcher->addr, 1);
- }
-
// Add the watcher to the list.
ga_grow(&watchers, 1);
((SocketWatcher **)watchers.ga_data)[watchers.ga_len++] = watcher;
@@ -200,12 +206,6 @@ bool server_stop(char *endpoint)
return false;
}
- // Unset $NVIM_LISTEN_ADDRESS if it is the stopped address.
- const char *listen_address = os_getenv(LISTEN_ADDRESS_ENV_VAR);
- if (listen_address && STRCMP(addr, listen_address) == 0) {
- os_unsetenv(LISTEN_ADDRESS_ENV_VAR);
- }
-
socket_watcher_close(watcher, free_server);
// Remove this server from the list by swapping it with the last item.
@@ -215,8 +215,8 @@ bool server_stop(char *endpoint)
}
watchers.ga_len--;
- // If v:servername is the stopped address, re-initialize it.
- if (STRCMP(addr, get_vim_var_str(VV_SEND_SERVER)) == 0) {
+ // Bump v:servername to the next available server, if any.
+ if (strequal(addr, (char *)get_vim_var_str(VV_SEND_SERVER))) {
set_vservername(&watchers);
}
diff --git a/src/nvim/msgpack_rpc/unpacker.c b/src/nvim/msgpack_rpc/unpacker.c
new file mode 100644
index 0000000000..26c1843026
--- /dev/null
+++ b/src/nvim/msgpack_rpc/unpacker.c
@@ -0,0 +1,322 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+#include "nvim/api/private/helpers.h"
+#include "nvim/log.h"
+#include "nvim/memory.h"
+#include "nvim/msgpack_rpc/helpers.h"
+#include "nvim/msgpack_rpc/unpacker.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "msgpack_rpc/unpacker.c.generated.h"
+#endif
+
+Object unpack(const char *data, size_t size, Error *err)
+{
+ Unpacker unpacker;
+ mpack_parser_init(&unpacker.parser, 0);
+ unpacker.parser.data.p = &unpacker;
+
+ int result = mpack_parse(&unpacker.parser, &data, &size,
+ api_parse_enter, api_parse_exit);
+
+ if (result == MPACK_NOMEM) {
+ api_set_error(err, kErrorTypeException, "object was too deep to unpack");
+ } else if (result == MPACK_EOF) {
+ api_set_error(err, kErrorTypeException, "incomplete msgpack string");
+ } else if (result == MPACK_ERROR) {
+ api_set_error(err, kErrorTypeException, "invalid msgpack string");
+ } else if (result == MPACK_OK && size) {
+ api_set_error(err, kErrorTypeException, "trailing data in msgpack string");
+ }
+
+ return unpacker.result;
+}
+
+static void api_parse_enter(mpack_parser_t *parser, mpack_node_t *node)
+{
+ Unpacker *p = parser->data.p;
+ Object *result = NULL;
+ String *key_location = NULL;
+
+ mpack_node_t *parent = MPACK_PARENT_NODE(node);
+ if (parent) {
+ switch (parent->tok.type) {
+ case MPACK_TOKEN_ARRAY: {
+ Object *obj = parent->data[0].p;
+ result = &kv_A(obj->data.array, parent->pos);
+ break;
+ }
+ case MPACK_TOKEN_MAP: {
+ Object *obj = parent->data[0].p;
+ KeyValuePair *kv = &kv_A(obj->data.dictionary, parent->pos);
+ if (!parent->key_visited) {
+ // TODO(bfredl): when implementing interrupt parse on error,
+ // stop parsing here when node is not a STR/BIN
+ kv->key = (String)STRING_INIT;
+ key_location = &kv->key;
+ }
+ result = &kv->value;
+ break;
+ }
+
+ case MPACK_TOKEN_STR:
+ case MPACK_TOKEN_BIN:
+ case MPACK_TOKEN_EXT:
+ assert(node->tok.type == MPACK_TOKEN_CHUNK);
+ break;
+
+ default:
+ abort();
+ }
+ } else {
+ result = &p->result;
+ }
+
+ switch (node->tok.type) {
+ case MPACK_TOKEN_NIL:
+ *result = NIL;
+ break;
+ case MPACK_TOKEN_BOOLEAN:
+ *result = BOOL(mpack_unpack_boolean(node->tok));
+ break;
+ case MPACK_TOKEN_SINT:
+ *result = INTEGER_OBJ(mpack_unpack_sint(node->tok));
+ break;
+ case MPACK_TOKEN_UINT:
+ *result = INTEGER_OBJ((Integer)mpack_unpack_uint(node->tok));
+ break;
+ case MPACK_TOKEN_FLOAT:
+ *result = FLOAT_OBJ(mpack_unpack_float(node->tok));
+ break;
+
+ case MPACK_TOKEN_BIN:
+ case MPACK_TOKEN_STR: {
+ char *mem = arena_alloc(&p->arena, node->tok.length + 1, false);
+ mem[node->tok.length] = NUL;
+ String str = { .data = mem, .size = node->tok.length };
+ if (key_location) {
+ *key_location = str;
+ } else {
+ *result = STRING_OBJ(str);
+ }
+ node->data[0].p = str.data;
+ break;
+ }
+ case MPACK_TOKEN_EXT:
+ // handled in chunk; but save result location
+ node->data[0].p = result;
+ break;
+ case MPACK_TOKEN_CHUNK:
+ assert(parent);
+ if (parent->tok.type == MPACK_TOKEN_STR || parent->tok.type == MPACK_TOKEN_BIN) {
+ char *data = parent->data[0].p;
+ memcpy(data + parent->pos,
+ node->tok.data.chunk_ptr, node->tok.length);
+ } else {
+ Object *res = parent->data[0].p;
+
+ size_t endlen = parent->pos + node->tok.length;
+ if (endlen > MAX_EXT_LEN) {
+ *res = NIL;
+ break;
+ }
+ memcpy(p->ext_buf + parent->pos,
+ node->tok.data.chunk_ptr, node->tok.length);
+ if (parent->pos + node->tok.length < parent->tok.length) {
+ break; // EOF, let's get back to it later
+ }
+ const char *buf = p->ext_buf;
+ size_t size = parent->tok.length;
+ mpack_token_t ext_tok;
+ int status = mpack_rtoken(&buf, &size, &ext_tok);
+ if (status || ext_tok.type != MPACK_TOKEN_UINT) {
+ // TODO(bfredl): once we fixed memory management, we can set
+ // p->unpack_error and a flag like p->interrupted
+ *res = NIL;
+ break;
+ }
+ int ext_type = parent->tok.data.ext_type;
+ if (0 <= ext_type && ext_type <= EXT_OBJECT_TYPE_MAX) {
+ res->type = (ObjectType)(ext_type + EXT_OBJECT_TYPE_SHIFT);
+ res->data.integer = (int64_t)mpack_unpack_uint(ext_tok);
+ } else {
+ *res = NIL;
+ break;
+ }
+ }
+ break;
+
+ case MPACK_TOKEN_ARRAY: {
+ Array arr = KV_INITIAL_VALUE;
+ kv_fixsize_arena(&p->arena, arr, node->tok.length);
+ kv_size(arr) = node->tok.length;
+ *result = ARRAY_OBJ(arr);
+ node->data[0].p = result;
+ break;
+ }
+ case MPACK_TOKEN_MAP: {
+ Dictionary dict = KV_INITIAL_VALUE;
+ kv_fixsize_arena(&p->arena, dict, node->tok.length);
+ kv_size(dict) = node->tok.length;
+ *result = DICTIONARY_OBJ(dict);
+ node->data[0].p = result;
+ break;
+ }
+
+ default:
+ abort();
+ }
+}
+
+static void api_parse_exit(mpack_parser_t *parser, mpack_node_t *node)
+{}
+
+void unpacker_init(Unpacker *p)
+{
+ mpack_parser_init(&p->parser, 0);
+ p->parser.data.p = p;
+ mpack_tokbuf_init(&p->reader);
+ p->unpack_error = (Error)ERROR_INIT;
+
+ p->arena = (Arena)ARENA_EMPTY;
+ p->reuse_blk = NULL;
+}
+
+void unpacker_teardown(Unpacker *p)
+{
+ arena_mem_free(p->reuse_blk, NULL);
+ arena_mem_free(arena_finish(&p->arena), NULL);
+}
+
+bool unpacker_parse_header(Unpacker *p)
+{
+ mpack_token_t tok;
+ int result;
+
+ const char *data = p->read_ptr;
+ size_t size = p->read_size;
+
+ assert(!ERROR_SET(&p->unpack_error));
+
+#define NEXT(tok) \
+ result = mpack_read(&p->reader, &data, &size, &tok); \
+ if (result) { goto error; }
+
+ NEXT(tok);
+ if (tok.type != MPACK_TOKEN_ARRAY || tok.length < 3 || tok.length > 4) {
+ goto error;
+ }
+ size_t array_length = tok.length;
+
+ NEXT(tok);
+ if (tok.type != MPACK_TOKEN_UINT) {
+ goto error;
+ }
+ uint32_t type = (uint32_t)mpack_unpack_uint(tok);
+ if ((array_length == 3) ? type != 2 : (type >= 2)) {
+ goto error;
+ }
+ p->type = (MessageType)type;
+ p->request_id = 0;
+
+ if (p->type != kMessageTypeNotification) {
+ NEXT(tok);
+ if (tok.type != MPACK_TOKEN_UINT) {
+ goto error;
+ }
+ p->request_id = (uint32_t)mpack_unpack_uint(tok);
+ }
+
+ if (p->type != kMessageTypeResponse) {
+ NEXT(tok);
+ if ((tok.type != MPACK_TOKEN_STR && tok.type != MPACK_TOKEN_BIN)
+ || tok.length > 100) {
+ goto error;
+ }
+ p->method_name_len = tok.length;
+
+ if (p->method_name_len > 0) {
+ NEXT(tok);
+ assert(tok.type == MPACK_TOKEN_CHUNK);
+ }
+ if (tok.length < p->method_name_len) {
+ result = MPACK_EOF;
+ goto error;
+ }
+ // if this fails, p->handler.fn will be NULL
+ p->handler = msgpack_rpc_get_handler_for(tok.length ? tok.data.chunk_ptr : "",
+ tok.length, &p->unpack_error);
+ }
+
+ p->read_ptr = data;
+ p->read_size = size;
+ return true;
+#undef NEXT
+
+error:
+ if (result == MPACK_EOF) {
+ // recover later by retrying from scratch
+ // when more data is available.
+ mpack_tokbuf_init(&p->reader);
+ } else {
+ api_set_error(&p->unpack_error, kErrorTypeValidation, "failed to decode msgpack");
+ p->state = -1;
+ }
+ return false;
+}
+
+// BASIC BITCH STATE MACHINE
+//
+// With some basic assumptions, we can parse the overall structure of msgpack-rpc
+// messages with a hand-rolled FSM of just 3 states (<x> = p->state):
+//
+// <0>[0, request_id, method_name, <2>args]
+// <0>[1, request_id, <1>err, <2>result]
+// <0>[2, method_name, <2>args]
+//
+// The assumption here is that the header of the message, which we define as the
+// initial array head, the kind integer, request_id and/or method name (when needed),
+// is relatively small, just ~10 bytes + the method name. Thus we can simply refuse
+// to advance the stream beyond the header until it can be parsed in its entirety.
+//
+// Of course, later on, we want to specialize state 2 into sub-states depending
+// on the specific method. "nvim_exec_lua" should just decode direct into lua
+// objects, and "redraw/grid_line" should use a hand-rolled decoder to avoid
+// a blizzard of small objects for each screen cell.
+
+bool unpacker_advance(Unpacker *p)
+{
+ assert(p->state >= 0);
+ if (p->state == 0) {
+ if (!unpacker_parse_header(p)) {
+ return false;
+ }
+ p->state = p->type == kMessageTypeResponse ? 1 : 2;
+ arena_start(&p->arena, &p->reuse_blk);
+ }
+
+ int result;
+
+rerun:
+ result = mpack_parse(&p->parser, &p->read_ptr, &p->read_size,
+ api_parse_enter, api_parse_exit);
+
+ if (result == MPACK_EOF) {
+ return false;
+ } else if (result != MPACK_OK) {
+ api_set_error(&p->unpack_error, kErrorTypeValidation, "failed to parse msgpack");
+ p->state = -1;
+ return false;
+ }
+
+ if (p->state == 1) {
+ p->error = p->result;
+ p->state = 2;
+ goto rerun;
+ } else {
+ assert(p->state == 2);
+ p->state = 0;
+ }
+ return true;
+}
diff --git a/src/nvim/msgpack_rpc/unpacker.h b/src/nvim/msgpack_rpc/unpacker.h
new file mode 100644
index 0000000000..e0dc6f0a68
--- /dev/null
+++ b/src/nvim/msgpack_rpc/unpacker.h
@@ -0,0 +1,46 @@
+#ifndef NVIM_MSGPACK_RPC_UNPACKER_H
+#define NVIM_MSGPACK_RPC_UNPACKER_H
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "mpack/mpack_core.h"
+#include "mpack/object.h"
+#include "nvim/api/private/dispatch.h"
+#include "nvim/api/private/helpers.h"
+#include "nvim/memory.h"
+#include "nvim/msgpack_rpc/channel_defs.h"
+
+struct Unpacker {
+ mpack_parser_t parser;
+ mpack_tokbuf_t reader;
+
+ const char *read_ptr;
+ size_t read_size;
+
+#define MAX_EXT_LEN 9 // byte + 8-byte integer
+ char ext_buf[MAX_EXT_LEN];
+
+ int state;
+ MessageType type;
+ uint32_t request_id;
+ size_t method_name_len;
+ MsgpackRpcRequestHandler handler;
+ Object error; // error return
+ Object result; // arg list or result
+ Error unpack_error;
+
+ Arena arena;
+ // one lenght free-list of reusable blocks
+ ArenaMem reuse_blk;
+};
+
+// unrecovareble error. unpack_error should be set!
+#define unpacker_closed(p) ((p)->state < 0)
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "msgpack_rpc/unpacker.h.generated.h"
+#endif
+
+#endif // NVIM_MSGPACK_RPC_UNPACKER_H
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 60bf393085..b675abfb7d 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -32,10 +32,12 @@
#include "nvim/fold.h"
#include "nvim/getchar.h"
#include "nvim/globals.h"
+#include "nvim/grid_defs.h"
#include "nvim/indent.h"
-#include "nvim/keymap.h"
+#include "nvim/keycodes.h"
#include "nvim/log.h"
#include "nvim/main.h"
+#include "nvim/mapping.h"
#include "nvim/mark.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
@@ -85,7 +87,6 @@ typedef struct normal_state {
static int VIsual_mode_orig = NUL; // saved Visual mode
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "normal.c.generated.h"
#endif
@@ -97,18 +98,14 @@ static inline void normal_state_init(NormalState *s)
s->state.execute = normal_execute;
}
-/*
- * nv_*(): functions called to handle Normal and Visual mode commands.
- * n_*(): functions called to handle Normal mode commands.
- * v_*(): functions called to handle Visual mode commands.
- */
+// nv_*(): functions called to handle Normal and Visual mode commands.
+// n_*(): functions called to handle Normal mode commands.
+// v_*(): functions called to handle Visual mode commands.
static char *e_noident = N_("E349: No identifier under cursor");
-/*
- * Function to be called for a Normal or Visual mode command.
- * The argument is a cmdarg_T.
- */
+/// Function to be called for a Normal or Visual mode command.
+/// The argument is a cmdarg_T.
typedef void (*nv_func_T)(cmdarg_T *cap);
// Values for cmd_flags.
@@ -124,26 +121,22 @@ typedef void (*nv_func_T)(cmdarg_T *cap);
#define NV_KEEPREG 0x100 // don't clear regname
#define NV_NCW 0x200 // not allowed in command-line window
-/*
- * Generally speaking, every Normal mode command should either clear any
- * pending operator (with *clearop*()), or set the motion type variable
- * oap->motion_type.
- *
- * When a cursor motion command is made, it is marked as being a character or
- * line oriented motion. Then, if an operator is in effect, the operation
- * becomes character or line oriented accordingly.
- */
-
-/*
- * This table contains one entry for every Normal or Visual mode command.
- * The order doesn't matter, init_normal_cmds() will create a sorted index.
- * It is faster when all keys from zero to '~' are present.
- */
+// Generally speaking, every Normal mode command should either clear any
+// pending operator (with *clearop*()), or set the motion type variable
+// oap->motion_type.
+//
+// When a cursor motion command is made, it is marked as being a character or
+// line oriented motion. Then, if an operator is in effect, the operation
+// becomes character or line oriented accordingly.
+
+/// This table contains one entry for every Normal or Visual mode command.
+/// The order doesn't matter, init_normal_cmds() will create a sorted index.
+/// It is faster when all keys from zero to '~' are present.
static const struct nv_cmd {
- int cmd_char; // (first) command character
- nv_func_T cmd_func; // function for this command
- uint16_t cmd_flags; // NV_ flags
- short cmd_arg; // value for ca.arg
+ int cmd_char; ///< (first) command character
+ nv_func_T cmd_func; ///< function for this command
+ uint16_t cmd_flags; ///< NV_ flags
+ int16_t cmd_arg; ///< value for ca.arg
} nv_cmds[] =
{
{ NUL, nv_error, 0, 0 },
@@ -164,7 +157,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 },
@@ -341,23 +334,21 @@ static const struct nv_cmd {
#define NV_CMDS_SIZE ARRAY_SIZE(nv_cmds)
// Sorted index of commands in nv_cmds[].
-static short nv_cmd_idx[NV_CMDS_SIZE];
+static int16_t nv_cmd_idx[NV_CMDS_SIZE];
// The highest index for which
// nv_cmds[idx].cmd_char == nv_cmd_idx[nv_cmds[idx].cmd_char]
static int nv_max_linear;
-/*
- * Compare functions for qsort() below, that checks the command character
- * through the index in nv_cmd_idx[].
- */
+/// Compare functions for qsort() below, that checks the command character
+/// through the index in nv_cmd_idx[].
static int nv_compare(const void *s1, const void *s2)
{
int c1, c2;
// The commands are sorted on absolute value.
- c1 = nv_cmds[*(const short *)s1].cmd_char;
- c2 = nv_cmds[*(const short *)s2].cmd_char;
+ c1 = nv_cmds[*(const int16_t *)s1].cmd_char;
+ c2 = nv_cmds[*(const int16_t *)s2].cmd_char;
if (c1 < 0) {
c1 = -c1;
}
@@ -367,24 +358,22 @@ static int nv_compare(const void *s1, const void *s2)
return c1 - c2;
}
-/*
- * Initialize the nv_cmd_idx[] table.
- */
+/// Initialize the nv_cmd_idx[] table.
void init_normal_cmds(void)
{
assert(NV_CMDS_SIZE <= SHRT_MAX);
// Fill the index table with a one to one relation.
- for (short int i = 0; i < (short int)NV_CMDS_SIZE; ++i) {
+ for (int16_t i = 0; i < (int16_t)NV_CMDS_SIZE; i++) {
nv_cmd_idx[i] = i;
}
// Sort the commands by the command character.
- qsort(&nv_cmd_idx, NV_CMDS_SIZE, sizeof(short), nv_compare);
+ qsort(&nv_cmd_idx, NV_CMDS_SIZE, sizeof(int16_t), nv_compare);
// Find the first entry that can't be indexed by the command character.
- short int i;
- for (i = 0; i < (short int)NV_CMDS_SIZE; ++i) {
+ int16_t i;
+ for (i = 0; i < (int16_t)NV_CMDS_SIZE; i++) {
if (i != nv_cmds[nv_cmd_idx[i]].cmd_char) {
break;
}
@@ -392,10 +381,9 @@ void init_normal_cmds(void)
nv_max_linear = i - 1;
}
-/*
- * Search for a command in the commands table.
- * Returns -1 for invalid command.
- */
+/// Search for a command in the commands table.
+///
+/// @return -1 for invalid command.
static int find_command(int cmdchar)
{
int i;
@@ -444,15 +432,27 @@ static int find_command(int cmdchar)
return idx;
}
-// Normal state entry point. This is called on:
-//
-// - Startup, In this case the function never returns.
-// - The command-line window is opened(`q:`). Returns when `cmdwin_result` != 0.
-// - The :visual command is called from :global in ex mode, `:global/PAT/visual`
-// for example. Returns when re-entering ex mode(because ex mode recursion is
-// not allowed)
-//
-// This used to be called main_loop on main.c
+/// If currently editing a cmdline or text is locked: beep and give an error
+/// message, return true.
+static bool check_text_locked(oparg_T *oap)
+{
+ if (text_locked()) {
+ clearopbeep(oap);
+ text_locked_msg();
+ return true;
+ }
+ return false;
+}
+
+/// Normal state entry point. This is called on:
+///
+/// - Startup, In this case the function never returns.
+/// - The command-line window is opened(`q:`). Returns when `cmdwin_result` != 0.
+/// - The :visual command is called from :global in ex mode, `:global/PAT/visual`
+/// for example. Returns when re-entering ex mode(because ex mode recursion is
+/// not allowed)
+///
+/// This used to be called main_loop on main.c
void normal_enter(bool cmdwin, bool noexmode)
{
NormalState state;
@@ -481,7 +481,7 @@ static void normal_prepare(NormalState *s)
if (finish_op != c) {
ui_cursor_shape(); // may show different cursor shape
}
- trigger_modechanged();
+ may_trigger_modechanged();
// When not finishing an operator and no register name typed, reset the count.
if (!finish_op && !s->oa.regname) {
@@ -500,7 +500,7 @@ static void normal_prepare(NormalState *s)
}
s->mapped_len = typebuf_maplen();
- State = NORMAL_BUSY;
+ State = MODE_NORMAL_BUSY;
// Set v:count here, when called from main() and not a stuffed command, so
// that v:count can be used in an expression mapping when there is no count.
@@ -571,6 +571,14 @@ static bool normal_need_additional_char(NormalState *s)
static bool normal_need_redraw_mode_message(NormalState *s)
{
+ // In Visual mode and with "^O" in Insert mode, a short message will be
+ // overwritten by the mode message. Wait a bit, until a key is hit.
+ // In Visual mode, it's more important to keep the Visual area updated
+ // than keeping a message (e.g. from a /pat search).
+ // Only do this if the command was typed, not from a mapping.
+ // Don't wait when emsg_silent is non-zero.
+ // Also wait a bit after an error message, e.g. for "^O:".
+ // Don't redraw the screen, it would remove the message.
return (
// 'showmode' is set and messages can be printed
((p_smd && msg_silent == 0
@@ -607,7 +615,7 @@ static void normal_redraw_mode_message(NormalState *s)
// Draw the cursor with the right shape here
if (restart_edit != 0) {
- State = INSERT;
+ State = MODE_INSERT;
}
// If need to redraw, and there is a "keep_msg", redraw before the
@@ -651,6 +659,7 @@ static void normal_get_additional_char(NormalState *s)
int lang; // getting a text character
no_mapping++;
+ allow_keys++; // no mapping for nchar, but allow key codes
// Don't generate a CursorHold event here, most commands can't handle
// it, e.g., nv_replace(), nv_csearch().
did_cursorhold = true;
@@ -683,16 +692,17 @@ static void normal_get_additional_char(NormalState *s)
// Get a second or third character.
if (cp != NULL) {
if (repl) {
- State = REPLACE; // pretend Replace mode
+ State = MODE_REPLACE; // pretend Replace mode
ui_cursor_shape(); // show different cursor shape
}
if (lang && curbuf->b_p_iminsert == B_IMODE_LMAP) {
// Allow mappings defined with ":lmap".
no_mapping--;
+ allow_keys--;
if (repl) {
- State = LREPLACE;
+ State = MODE_LREPLACE;
} else {
- State = LANGMAP;
+ State = MODE_LANGMAP;
}
langmap_active = true;
}
@@ -702,8 +712,9 @@ static void normal_get_additional_char(NormalState *s)
if (langmap_active) {
// Undo the decrement done above
no_mapping++;
+ allow_keys++;
}
- State = NORMAL_BUSY;
+ State = MODE_NORMAL_BUSY;
s->need_flushbuf |= add_to_showcmd(*cp);
if (!lit) {
@@ -782,6 +793,7 @@ static void normal_get_additional_char(NormalState *s)
no_mapping++;
}
no_mapping--;
+ allow_keys--;
}
static void normal_invert_horizontal(NormalState *s)
@@ -824,15 +836,12 @@ static bool normal_get_command_count(NormalState *s)
if (s->c == K_DEL || s->c == K_KDEL) {
s->ca.count0 /= 10;
del_from_showcmd(4); // delete the digit and ~@%
+ } else if (s->ca.count0 > 99999999L) {
+ s->ca.count0 = 999999999L;
} else {
s->ca.count0 = s->ca.count0 * 10 + (s->c - '0');
}
- if (s->ca.count0 < 0) {
- // overflow
- s->ca.count0 = 999999999L;
- }
-
// Set v:count here, when called from main() and not a stuffed
// command, so that v:count can be used in an expression mapping
// right after the count. Do set it for redo.
@@ -842,14 +851,16 @@ static bool normal_get_command_count(NormalState *s)
if (s->ctrl_w) {
no_mapping++;
+ allow_keys++; // no mapping for nchar, but keys
}
- ++no_zero_mapping; // don't map zero here
+ no_zero_mapping++; // don't map zero here
s->c = plain_vgetc();
LANGMAP_ADJUST(s->c, true);
- --no_zero_mapping;
+ no_zero_mapping--;
if (s->ctrl_w) {
no_mapping--;
+ allow_keys--;
}
s->need_flushbuf |= add_to_showcmd(s->c);
}
@@ -860,9 +871,11 @@ static bool normal_get_command_count(NormalState *s)
s->ca.opcount = s->ca.count0; // remember first count
s->ca.count0 = 0;
no_mapping++;
+ allow_keys++; // no mapping for nchar, but keys
s->c = plain_vgetc(); // get next character
LANGMAP_ADJUST(s->c, true);
no_mapping--;
+ allow_keys--;
s->need_flushbuf |= add_to_showcmd(s->c);
return true;
}
@@ -899,14 +912,6 @@ static void normal_finish_command(NormalState *s)
// Wait for a moment when a message is displayed that will be overwritten
// by the mode message.
- // In Visual mode and with "^O" in Insert mode, a short message will be
- // overwritten by the mode message. Wait a bit, until a key is hit.
- // In Visual mode, it's more important to keep the Visual area updated
- // than keeping a message (e.g. from a /pat search).
- // Only do this if the command was typed, not from a mapping.
- // Don't wait when emsg_silent is non-zero.
- // Also wait a bit after an error message, e.g. for "^O:".
- // Don't redraw the screen, it would remove the message.
if (normal_need_redraw_mode_message(s)) {
normal_redraw_mode_message(s);
}
@@ -923,7 +928,7 @@ normal_end:
// Reset finish_op, in case it was set
s->c = finish_op;
finish_op = false;
- trigger_modechanged();
+ may_trigger_modechanged();
// Redraw the cursor with another shape, if we were in Operator-pending
// mode or did a replace command.
if (s->c || s->ca.cmdchar == 'r') {
@@ -961,7 +966,8 @@ normal_end:
&& s->oa.regname == 0) {
if (restart_VIsual_select == 1) {
VIsual_select = true;
- trigger_modechanged();
+ VIsual_select_reg = 0;
+ may_trigger_modechanged();
showmode();
restart_VIsual_select = 0;
}
@@ -986,7 +992,7 @@ static int normal_execute(VimState *state, int key)
s->old_col = curwin->w_curswant;
s->c = key;
- LANGMAP_ADJUST(s->c, get_real_state() != SELECTMODE);
+ LANGMAP_ADJUST(s->c, get_real_state() != MODE_SELECT);
// If a mapping was started in Visual or Select mode, remember the length
// of the mapping. This is used below to not return to Insert mode for as
@@ -1005,12 +1011,18 @@ static int normal_execute(VimState *state, int key)
// In Select mode, typed text replaces the selection.
if (VIsual_active && VIsual_select && (vim_isprintc(s->c)
|| s->c == NL || s->c == CAR || s->c == K_KENTER)) {
- // Fake a "c"hange command. When "restart_edit" is set (e.g., because
- // 'insertmode' is set) fake a "d"elete command, Insert mode will
- // restart automatically.
+ // Fake a "c"hange command.
+ // When "restart_edit" is set fake a "d"elete command, Insert mode will restart automatically.
// Insert the typed character in the typeahead buffer, so that it can
// be mapped in Insert mode. Required for ":lmap" to work.
- ins_char_typebuf(s->c);
+ int len = ins_char_typebuf(vgetc_char, vgetc_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 {
@@ -1022,7 +1034,7 @@ static int normal_execute(VimState *state, int key)
s->need_flushbuf = add_to_showcmd(s->c);
- while (normal_get_command_count(s)) { continue; }
+ while (normal_get_command_count(s)) {}
if (s->c == K_EVENT) {
// Save the count values so that ca.opcount and ca.count0 are exactly
@@ -1038,14 +1050,14 @@ static int normal_execute(VimState *state, int key)
// If you give a count before AND after the operator, they are
// multiplied.
if (s->ca.count0) {
- s->ca.count0 = (long)((uint64_t)s->ca.count0 * (uint64_t)s->ca.opcount);
+ if (s->ca.opcount >= 999999999L / s->ca.count0) {
+ s->ca.count0 = 999999999L;
+ } else {
+ s->ca.count0 *= s->ca.opcount;
+ }
} else {
s->ca.count0 = s->ca.opcount;
}
- if (s->ca.count0 < 0) {
- // overflow
- s->ca.count0 = 999999999L;
- }
}
// Always remember the count. It will be set to zero (on the next call,
@@ -1079,15 +1091,9 @@ static int normal_execute(VimState *state, int key)
goto finish;
}
- if (text_locked() && (nv_cmds[s->idx].cmd_flags & NV_NCW)) {
- // This command is not allowed while editing a cmdline: beep.
- clearopbeep(&s->oa);
- text_locked_msg();
- s->command_finished = true;
- goto finish;
- }
-
- if ((nv_cmds[s->idx].cmd_flags & NV_NCW) && curbuf_locked()) {
+ if ((nv_cmds[s->idx].cmd_flags & NV_NCW)
+ && (check_text_locked(&s->oa) || curbuf_locked())) {
+ // this command is not allowed now
s->command_finished = true;
goto finish;
}
@@ -1122,13 +1128,10 @@ static int normal_execute(VimState *state, int key)
did_cursorhold = false;
}
- State = NORMAL;
+ State = MODE_NORMAL;
if (s->ca.nchar == ESC) {
clearop(&s->oa);
- if (restart_edit == 0 && goto_im()) {
- restart_edit = 'a';
- }
s->command_finished = true;
goto finish;
}
@@ -1178,14 +1181,6 @@ static void normal_check_stuff_buffer(NormalState *s)
// if wait_return still needed call it now
wait_return(false);
}
-
- if (need_start_insertmode && goto_im() && !VIsual_active) {
- need_start_insertmode = false;
- stuffReadbuff("i"); // start insert mode next
- // skip the fileinfo message now, because it would be shown
- // after insert mode finishes!
- need_fileinfo = false;
- }
}
}
@@ -1202,7 +1197,7 @@ static void normal_check_interrupt(NormalState *s)
// Typed two CTRL-C in a row: go back to ex mode as if "Q" was
// used and keep "got_int" set, so that it aborts ":g".
exmode_active = true;
- State = NORMAL;
+ State = MODE_NORMAL;
} else if (!global_busy || !exmode_active) {
if (!quit_more) {
// flush all buffers
@@ -1218,22 +1213,18 @@ static void normal_check_interrupt(NormalState *s)
static void normal_check_window_scrolled(NormalState *s)
{
- // Trigger Scroll if the viewport changed.
- if (!finish_op && has_event(EVENT_WINSCROLLED)
- && win_did_scroll(curwin)) {
- do_autocmd_winscrolled(curwin);
+ if (!finish_op) {
+ // Trigger Scroll if the viewport changed.
+ may_trigger_winscrolled();
}
}
static void normal_check_cursor_moved(NormalState *s)
{
// Trigger CursorMoved if the cursor moved.
- if (!finish_op && (has_event(EVENT_CURSORMOVED) || curwin->w_p_cole > 0)
+ if (!finish_op && has_event(EVENT_CURSORMOVED)
&& !equalpos(curwin->w_last_cursormoved, curwin->w_cursor)) {
- if (has_event(EVENT_CURSORMOVED)) {
- apply_autocmds(EVENT_CURSORMOVED, NULL, NULL, false, curbuf);
- }
-
+ apply_autocmds(EVENT_CURSORMOVED, NULL, NULL, false, curbuf);
curwin->w_last_cursormoved = curwin->w_cursor;
}
}
@@ -1283,24 +1274,9 @@ static void normal_redraw(NormalState *s)
update_topline(curwin);
validate_cursor();
- // If the cursor moves horizontally when 'concealcursor' is active, then the
- // current line needs to be redrawn in order to calculate the correct
- // cursor position.
- if (curwin->w_p_cole > 0 && conceal_cursor_line(curwin)) {
- redrawWinline(curwin, curwin->w_cursor.lnum);
- }
-
- // Might need to update for 'cursorline'.
- // When 'cursorlineopt' is "screenline" need to redraw always.
- if (curwin->w_p_cul
- && (curwin->w_last_cursorline != curwin->w_cursor.lnum
- || (curwin->w_p_culopt_flags & CULOPT_SCRLINE))
- && !char_avail()) {
- redraw_later(curwin, VALID);
- }
-
if (VIsual_active) {
- update_curbuf(INVERTED); // update inverted part
+ redraw_curbuf_later(INVERTED); // update inverted part
+ update_screen(INVERTED);
} else if (must_redraw) {
update_screen(0);
} else if (redraw_cmdline || clear_cmdline) {
@@ -1345,11 +1321,12 @@ static void normal_redraw(NormalState *s)
setcursor();
}
-// Function executed before each iteration of normal mode.
-// Return:
-// 1 if the iteration should continue normally
-// -1 if the iteration should be skipped
-// 0 if the main loop must exit
+/// Function executed before each iteration of normal mode.
+///
+/// @return:
+/// 1 if the iteration should continue normally
+/// -1 if the iteration should be skipped
+/// 0 if the main loop must exit
static int normal_check(VimState *state)
{
NormalState *s = (NormalState *)state;
@@ -1367,9 +1344,10 @@ static int normal_check(VimState *state)
if (skip_redraw || exmode_active) {
skip_redraw = false;
} else if (do_redraw || stuff_empty()) {
- // Need to make sure w_topline and w_leftcol are correct before
- // normal_check_window_scrolled() is called.
+ // Ensure curwin->w_topline and curwin->w_leftcol are up to date
+ // before triggering a WinScrolled autocommand.
update_topline(curwin);
+ validate_cursor();
normal_check_cursor_moved(s);
normal_check_text_changed(s);
@@ -1433,10 +1411,8 @@ static int normal_check(VimState *state)
return 1;
}
-/*
- * Set v:count and v:count1 according to "cap".
- * Set v:prevcount only when "set_prevcount" is true.
- */
+/// Set v:count and v:count1 according to "cap".
+/// Set v:prevcount only when "set_prevcount" is true.
static void set_vcount_ca(cmdarg_T *cap, bool *set_prevcount)
{
long count = cap->count0;
@@ -1449,8 +1425,8 @@ static void set_vcount_ca(cmdarg_T *cap, bool *set_prevcount)
*set_prevcount = false; // only set v:prevcount once
}
-// Move the current tab to tab in same column as mouse or to end of the
-// tabline if there is no tab there.
+/// Move the current tab to tab in same column as mouse or to end of the
+/// tabline if there is no tab there.
static void move_tab_to_mouse(void)
{
int tabnr = tab_page_click_defs[mouse_col].tabnr;
@@ -1463,15 +1439,72 @@ static void move_tab_to_mouse(void)
}
}
+/// Call click definition function for column "col" in the "click_defs" array for button
+/// "which_button".
+static void call_click_def_func(StlClickDefinition *click_defs, int col, int which_button)
+{
+ typval_T argv[] = {
+ {
+ .v_lock = VAR_FIXED,
+ .v_type = VAR_NUMBER,
+ .vval = {
+ .v_number = (varnumber_T)click_defs[col].tabnr
+ },
+ },
+ {
+ .v_lock = VAR_FIXED,
+ .v_type = VAR_NUMBER,
+ .vval = {
+ .v_number = ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_4CLICK
+ ? 4
+ : ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_3CLICK
+ ? 3
+ : ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK
+ ? 2
+ : 1)))
+ },
+ },
+ {
+ .v_lock = VAR_FIXED,
+ .v_type = VAR_STRING,
+ .vval = {
+ .v_string = (which_button == MOUSE_LEFT
+ ? "l"
+ : (which_button == MOUSE_RIGHT
+ ? "r"
+ : (which_button == MOUSE_MIDDLE
+ ? "m"
+ : "?")))
+ },
+ },
+ {
+ .v_lock = VAR_FIXED,
+ .v_type = VAR_STRING,
+ .vval = {
+ .v_string = (char[]) {
+ (char)(mod_mask & MOD_MASK_SHIFT ? 's' : ' '),
+ (char)(mod_mask & MOD_MASK_CTRL ? 'c' : ' '),
+ (char)(mod_mask & MOD_MASK_ALT ? 'a' : ' '),
+ (char)(mod_mask & MOD_MASK_META ? 'm' : ' '),
+ NUL
+ }
+ },
+ }
+ };
+ typval_T rettv;
+ (void)call_vim_function(click_defs[col].func, ARRAY_SIZE(argv), argv, &rettv);
+ tv_clear(&rettv);
+}
+
/// Do the appropriate action for the current mouse click in the current mode.
/// Not used for Command-line mode.
///
-/// Normal Mode:
+/// Normal and Visual Mode:
/// event modi- position visual change action
/// fier cursor window
/// left press - yes end yes
/// left press C yes end yes "^]" (2)
-/// left press S yes end yes "*" (2)
+/// left press S yes end (popup: extend) yes "*" (2)
/// left drag - yes start if moved no
/// left relse - yes start if moved no
/// middle press - yes if not active no put register
@@ -1512,6 +1545,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
int jump_flags = 0; // flags for jump_to_mouse()
pos_T start_visual;
bool moved; // Has cursor moved?
+ bool in_winbar; // mouse in window bar
bool in_status_line; // mouse in status line
static bool in_tab_line = false; // mouse clicked in tab line
bool in_sep_line; // mouse in vertical separator line
@@ -1531,16 +1565,18 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
for (;;) {
which_button = get_mouse_button(KEY2TERMCAP1(c), &is_click, &is_drag);
if (is_drag) {
- /* If the next character is the same mouse event then use that
- * one. Speeds up dragging the status line. */
- if (vpeekc() != NUL) {
+ // If the next character is the same mouse event then use that
+ // one. Speeds up dragging the status line.
+ // Note: Since characters added to the stuff buffer in the code
+ // below need to come before the next character, do not do this
+ // when the current character was stuffed.
+ if (!KeyStuffed && vpeekc() != NUL) {
int nc;
int save_mouse_grid = mouse_grid;
int save_mouse_row = mouse_row;
int save_mouse_col = mouse_col;
- /* Need to get the character, peeking doesn't get the actual
- * one. */
+ // Need to get the character, peeking doesn't get the actual one.
nc = safe_vgetc();
if (c == nc) {
continue;
@@ -1559,9 +1595,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
return false;
}
- /*
- * Ignore drag and release events if we didn't get a click.
- */
+ // Ignore drag and release events if we didn't get a click.
if (is_click) {
got_click = true;
} else {
@@ -1577,12 +1611,9 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
}
}
-
- /*
- * CTRL right mouse button does CTRL-T
- */
+ // CTRL right mouse button does CTRL-T
if (is_click && (mod_mask & MOD_MASK_CTRL) && which_button == MOUSE_RIGHT) {
- if (State & INSERT) {
+ if (State & MODE_INSERT) {
stuffcharReadbuff(Ctrl_O);
}
if (count > 1) {
@@ -1593,18 +1624,14 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
return false;
}
- /*
- * CTRL only works with left mouse button
- */
+ // CTRL only works with left mouse button
if ((mod_mask & MOD_MASK_CTRL) && which_button != MOUSE_LEFT) {
return false;
}
- /*
- * When a modifier is down, ignore drag and release events, as well as
- * multiple clicks and the middle mouse button.
- * Accept shift-leftmouse drags when 'mousemodel' is "popup.*".
- */
+ // When a modifier is down, ignore drag and release events, as well as
+ // multiple clicks and the middle mouse button.
+ // Accept shift-leftmouse drags when 'mousemodel' is "popup.*".
if ((mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL | MOD_MASK_ALT
| MOD_MASK_META))
&& (!is_click
@@ -1619,11 +1646,9 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
return false;
}
- /*
- * If the button press was used as the movement command for an operator
- * (eg "d<MOUSE>"), or it is the middle button that is held down, ignore
- * drag/release events.
- */
+ // If the button press was used as the movement command for an operator (eg
+ // "d<MOUSE>"), or it is the middle button that is held down, ignore
+ // drag/release events.
if (!is_click && which_button == MOUSE_MIDDLE) {
return false;
}
@@ -1634,25 +1659,19 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
regname = 0;
}
- /*
- * Middle mouse button does a 'put' of the selected text
- */
+ // Middle mouse button does a 'put' of the selected text
if (which_button == MOUSE_MIDDLE) {
- if (State == NORMAL) {
- /*
- * If an operator was pending, we don't know what the user wanted
- * to do. Go back to normal mode: Clear the operator and beep().
- */
+ if (State == MODE_NORMAL) {
+ // If an operator was pending, we don't know what the user wanted to do.
+ // Go back to normal mode: Clear the operator and beep().
if (oap != NULL && oap->op_type != OP_NOP) {
clearopbeep(oap);
return false;
}
- /*
- * If visual was active, yank the highlighted text and put it
- * before the mouse pointer position.
- * In Select mode replace the highlighted text with the clipboard.
- */
+ // If visual was active, yank the highlighted text and put it
+ // before the mouse pointer position.
+ // In Select mode replace the highlighted text with the clipboard.
if (VIsual_active) {
if (VIsual_select) {
stuffcharReadbuff(Ctrl_G);
@@ -1663,21 +1682,17 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
}
return false;
}
- /*
- * The rest is below jump_to_mouse()
- */
- } else if ((State & INSERT) == 0) {
+ // The rest is below jump_to_mouse()
+ } else if ((State & MODE_INSERT) == 0) {
return false;
}
- /*
- * Middle click in insert mode doesn't move the mouse, just insert the
- * contents of a register. '.' register is special, can't insert that
- * with do_put().
- * Also paste at the cursor if the current mode isn't in 'mouse' (only
- * happens for the GUI).
- */
- if ((State & INSERT)) {
+ // Middle click in insert mode doesn't move the mouse, just insert the
+ // contents of a register. '.' register is special, can't insert that
+ // with do_put().
+ // Also paste at the cursor if the current mode isn't in 'mouse' (only
+ // happens for the GUI).
+ if ((State & MODE_INSERT)) {
if (regname == '.') {
insert_reg(regname, true);
} else {
@@ -1760,67 +1775,10 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
}
}
break;
- case kStlClickFuncRun: {
- typval_T argv[] = {
- {
- .v_lock = VAR_FIXED,
- .v_type = VAR_NUMBER,
- .vval = {
- .v_number = (varnumber_T)tab_page_click_defs[mouse_col].tabnr
- },
- },
- {
- .v_lock = VAR_FIXED,
- .v_type = VAR_NUMBER,
- .vval = {
- .v_number = (((mod_mask & MOD_MASK_MULTI_CLICK)
- == MOD_MASK_4CLICK)
- ? 4
- : ((mod_mask & MOD_MASK_MULTI_CLICK)
- == MOD_MASK_3CLICK)
- ? 3
- : ((mod_mask & MOD_MASK_MULTI_CLICK)
- == MOD_MASK_2CLICK)
- ? 2
- : 1)
- },
- },
- {
- .v_lock = VAR_FIXED,
- .v_type = VAR_STRING,
- .vval = { .v_string = (char_u *)(which_button == MOUSE_LEFT
- ? "l"
- : which_button == MOUSE_RIGHT
- ? "r"
- : which_button == MOUSE_MIDDLE
- ? "m"
- : "?") },
- },
- {
- .v_lock = VAR_FIXED,
- .v_type = VAR_STRING,
- .vval = {
- .v_string = (char_u[]) {
- (char_u)(mod_mask & MOD_MASK_SHIFT ? 's' : ' '),
- (char_u)(mod_mask & MOD_MASK_CTRL ? 'c' : ' '),
- (char_u)(mod_mask & MOD_MASK_ALT ? 'a' : ' '),
- (char_u)(mod_mask & MOD_MASK_META ? 'm' : ' '),
- NUL
- }
- },
- }
- };
- typval_T rettv;
- funcexe_T funcexe = FUNCEXE_INIT;
- funcexe.firstline = curwin->w_cursor.lnum;
- funcexe.lastline = curwin->w_cursor.lnum;
- funcexe.evaluate = true;
- (void)call_func((char_u *)tab_page_click_defs[mouse_col].func, -1,
- &rettv, ARRAY_SIZE(argv), argv, &funcexe);
- tv_clear(&rettv);
+ case kStlClickFuncRun:
+ call_click_def_func(tab_page_click_defs, mouse_col, which_button);
break;
}
- }
}
return true;
} else if (is_drag && in_tab_line) {
@@ -1828,21 +1786,59 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
return false;
}
-
- /*
- * When 'mousemodel' is "popup" or "popup_setpos", translate mouse events:
- * right button up -> pop-up menu
- * shift-left button -> right button
- * alt-left button -> alt-right button
- */
+ // When 'mousemodel' is "popup" or "popup_setpos", translate mouse events:
+ // right button up -> pop-up menu
+ // shift-left button -> right button
+ // alt-left button -> alt-right button
if (mouse_model_popup()) {
if (which_button == MOUSE_RIGHT
&& !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))) {
- /*
- * NOTE: Ignore right button down and drag mouse events.
- * Windows only shows the popup menu on the button up event.
- */
- return false;
+ if (!is_click) {
+ // Ignore right button release events, only shows the popup
+ // menu on the button down event.
+ return false;
+ }
+ jump_flags = 0;
+ if (STRCMP(p_mousem, "popup_setpos") == 0) {
+ // First set the cursor position before showing the popup
+ // menu.
+ if (VIsual_active) {
+ pos_T m_pos;
+ // set MOUSE_MAY_STOP_VIS if we are outside the
+ // selection or the current window (might have false
+ // negative here)
+ if (mouse_row < curwin->w_winrow
+ || mouse_row > (curwin->w_winrow + curwin->w_height)) {
+ jump_flags = MOUSE_MAY_STOP_VIS;
+ } else if (get_fpos_of_mouse(&m_pos) != IN_BUFFER) {
+ jump_flags = MOUSE_MAY_STOP_VIS;
+ } else {
+ if ((lt(curwin->w_cursor, VIsual)
+ && (lt(m_pos, curwin->w_cursor) || lt(VIsual, m_pos)))
+ || (lt(VIsual, curwin->w_cursor)
+ && (lt(m_pos, VIsual) || lt(curwin->w_cursor, m_pos)))) {
+ jump_flags = MOUSE_MAY_STOP_VIS;
+ } else if (VIsual_mode == Ctrl_V) {
+ getvcols(curwin, &curwin->w_cursor, &VIsual, &leftcol, &rightcol);
+ getvcol(curwin, &m_pos, NULL, &m_pos.col, NULL);
+ if (m_pos.col < leftcol || m_pos.col > rightcol) {
+ jump_flags = MOUSE_MAY_STOP_VIS;
+ }
+ }
+ }
+ } else {
+ jump_flags = MOUSE_MAY_STOP_VIS;
+ }
+ }
+ if (jump_flags) {
+ jump_flags = jump_to_mouse(jump_flags, NULL, which_button);
+ update_curbuf(VIsual_active ? INVERTED : VALID);
+ setcursor();
+ ui_flush(); // Update before showing popup menu
+ }
+ show_popupmenu();
+ got_click = false; // ignore release events
+ return (jump_flags & CURSOR_MOVED) != 0;
}
if (which_button == MOUSE_LEFT
&& (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT))) {
@@ -1851,12 +1847,11 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
}
}
- if ((State & (NORMAL | INSERT))
+ if ((State & (MODE_NORMAL | MODE_INSERT))
&& !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))) {
if (which_button == MOUSE_LEFT) {
if (is_click) {
- /* stop Visual mode for a left click in a window, but not when
- * on a status line */
+ // stop Visual mode for a left click in a window, but not when on a status line
if (VIsual_active) {
jump_flags |= MOUSE_MAY_STOP_VIS;
}
@@ -1865,10 +1860,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
}
} else if (which_button == MOUSE_RIGHT) {
if (is_click && VIsual_active) {
- /*
- * Remember the start and end of visual before moving the
- * cursor.
- */
+ // Remember the start and end of visual before moving the cursor.
if (lt(curwin->w_cursor, VIsual)) {
start_visual = curwin->w_cursor;
end_visual = VIsual;
@@ -1882,10 +1874,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
}
}
- /*
- * If an operator is pending, ignore all drags and releases until the
- * next mouse click.
- */
+ // If an operator is pending, ignore all drags and releases until the next mouse click.
if (!is_drag && oap != NULL && oap->op_type != OP_NOP) {
got_click = false;
oap->motion_type = kMTCharWise;
@@ -1896,20 +1885,50 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
jump_flags |= MOUSE_RELEASED;
}
- /*
- * JUMP!
- */
+ // JUMP!
jump_flags = jump_to_mouse(jump_flags,
oap == NULL ? NULL : &(oap->inclusive),
which_button);
moved = (jump_flags & CURSOR_MOVED);
+ in_winbar = (jump_flags & MOUSE_WINBAR);
in_status_line = (jump_flags & IN_STATUS_LINE);
in_sep_line = (jump_flags & IN_SEP_LINE);
+ if ((in_winbar || in_status_line) && is_click) {
+ // Handle click event on window bar or status lin
+ int click_grid = mouse_grid;
+ int click_row = mouse_row;
+ int click_col = mouse_col;
+ win_T *wp = mouse_find_win(&click_grid, &click_row, &click_col);
+ if (wp == NULL) {
+ return false;
+ }
+
+ StlClickDefinition *click_defs = in_status_line ? wp->w_status_click_defs
+ : wp->w_winbar_click_defs;
+
+ if (click_defs != NULL) {
+ switch (click_defs[click_col].type) {
+ case kStlClickDisabled:
+ break;
+ case kStlClickFuncRun:
+ call_click_def_func(click_defs, click_col, which_button);
+ break;
+ default:
+ assert(false && "winbar and statusline only support %@ for clicks");
+ break;
+ }
+ }
+
+ return false;
+ } else if (in_winbar) {
+ // A drag or release event in the window bar has no side effects.
+ return false;
+ }
- /* When jumping to another window, clear a pending operator. That's a bit
- * friendlier than beeping and not jumping to that window. */
+ // When jumping to another window, clear a pending operator. That's a bit
+ // friendlier than beeping and not jumping to that window.
if (curwin != old_curwin && oap != NULL && oap->op_type != OP_NOP) {
clearop(oap);
}
@@ -1930,9 +1949,8 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
}
}
-
- /* Set global flag that we are extending the Visual area with mouse
- * dragging; temporarily minimize 'scrolloff'. */
+ // Set global flag that we are extending the Visual area with mouse dragging;
+ // temporarily minimize 'scrolloff'.
if (VIsual_active && is_drag && get_scrolloff_value(curwin)) {
// In the very first line, allow scrolling one line
if (mouse_row == 0) {
@@ -1954,10 +1972,8 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
VIsual_mode = Ctrl_V;
}
- /*
- * In Visual-block mode, divide the area in four, pick up the corner
- * that is in the quarter that the cursor is in.
- */
+ // In Visual-block mode, divide the area in four, pick up the corner
+ // that is in the quarter that the cursor is in.
if (VIsual_mode == Ctrl_V) {
getvcols(curwin, &start_visual, &end_visual, &leftcol, &rightcol);
if (curwin->w_curswant > (leftcol + rightcol) / 2) {
@@ -1977,11 +1993,9 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
VIsual = curwin->w_cursor;
curwin->w_cursor = start_visual; // restore the cursor
} else {
- /*
- * If the click is before the start of visual, change the start.
- * If the click is after the end of visual, change the end. If
- * the click is inside the visual, change the closest side.
- */
+ // If the click is before the start of visual, change the start.
+ // If the click is after the end of visual, change the end. If
+ // the click is inside the visual, change the closest side.
if (lt(curwin->w_cursor, start_visual)) {
VIsual = end_visual;
} else if (lt(end_visual, curwin->w_cursor)) {
@@ -1995,9 +2009,8 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
} else {
VIsual = end_visual;
}
- }
- // In different lines, compare line number
- else {
+ } else {
+ // In different lines, compare line number
diff = (curwin->w_cursor.lnum - start_visual.lnum) -
(end_visual.lnum - curwin->w_cursor.lnum);
@@ -2016,17 +2029,12 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
}
}
}
- }
- /*
- * If Visual mode started in insert mode, execute "CTRL-O"
- */
- else if ((State & INSERT) && VIsual_active) {
+ } else if ((State & MODE_INSERT) && VIsual_active) {
+ // If Visual mode started in insert mode, execute "CTRL-O"
stuffcharReadbuff(Ctrl_O);
}
- /*
- * Middle mouse click: Put text before cursor.
- */
+ // Middle mouse click: Put text before cursor.
if (which_button == MOUSE_MIDDLE) {
if (regname == 0 && eval_has_provider("clipboard")) {
regname = '*';
@@ -2048,51 +2056,35 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
}
prep_redo(regname, count, NUL, c1, NUL, c2, NUL);
- /*
- * Remember where the paste started, so in edit() Insstart can be set
- * to this position
- */
+ // Remember where the paste started, so in edit() Insstart can be set to this position
if (restart_edit != 0) {
where_paste_started = curwin->w_cursor;
}
do_put(regname, NULL, dir, count,
(fixindent ? PUT_FIXINDENT : 0)| PUT_CURSEND);
- }
- /*
- * Ctrl-Mouse click or double click in a quickfix window jumps to the
- * error under the mouse pointer.
- */
- else if (((mod_mask & MOD_MASK_CTRL)
- || (mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
- && bt_quickfix(curbuf)) {
+ } else if (((mod_mask & MOD_MASK_CTRL) || (mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
+ && bt_quickfix(curbuf)) {
+ // Ctrl-Mouse click or double click in a quickfix window jumps to the
+ // error under the mouse pointer.
if (curwin->w_llist_ref == NULL) { // quickfix window
do_cmdline_cmd(".cc");
} else { // location list window
do_cmdline_cmd(".ll");
}
got_click = false; // ignore drag&release now
- }
- /*
- * Ctrl-Mouse click (or double click in a help window) jumps to the tag
- * under the mouse pointer.
- */
- else if ((mod_mask & MOD_MASK_CTRL) || (curbuf->b_help
- && (mod_mask &
- MOD_MASK_MULTI_CLICK) ==
- MOD_MASK_2CLICK)) {
- if (State & INSERT) {
+ } else if ((mod_mask & MOD_MASK_CTRL)
+ || (curbuf->b_help && (mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)) {
+ // Ctrl-Mouse click (or double click in a help window) jumps to the tag
+ // under the mouse pointer.
+ if (State & MODE_INSERT) {
stuffcharReadbuff(Ctrl_O);
}
stuffcharReadbuff(Ctrl_RSB);
got_click = false; // ignore drag&release now
- }
- /*
- * Shift-Mouse click searches for the next occurrence of the word under
- * the mouse pointer
- */
- else if ((mod_mask & MOD_MASK_SHIFT)) {
- if (State & INSERT
- || (VIsual_active && VIsual_select)) {
+ } else if ((mod_mask & MOD_MASK_SHIFT)) {
+ // Shift-Mouse click searches for the next occurrence of the word under
+ // the mouse pointer
+ if (State & MODE_INSERT || (VIsual_active && VIsual_select)) {
stuffcharReadbuff(Ctrl_O);
}
if (which_button == MOUSE_LEFT) {
@@ -2100,11 +2092,10 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
} else { // MOUSE_RIGHT
stuffcharReadbuff('#');
}
- }
- // Handle double clicks, unless on status line
- else if (in_status_line) {
- } else if (in_sep_line) {
- } else if ((mod_mask & MOD_MASK_MULTI_CLICK) && (State & (NORMAL | INSERT))) {
+ } else if (in_status_line || in_sep_line) {
+ // Do nothing if on status line or vertical separator
+ // Handle double clicks otherwise
+ } else if ((mod_mask & MOD_MASK_MULTI_CLICK) && (State & (MODE_NORMAL | MODE_INSERT))) {
if (is_click || !VIsual_active) {
if (VIsual_active) {
orig_cursor = VIsual;
@@ -2130,17 +2121,15 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
VIsual_mode = Ctrl_V;
}
}
- /*
- * A double click selects a word or a block.
- */
+ // A double click selects a word or a block.
if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK) {
pos_T *pos = NULL;
int gc;
if (is_click) {
- /* If the character under the cursor (skipping white space) is
- * not a word character, try finding a match and select a (),
- * {}, [], #if/#endif, etc. block. */
+ // If the character under the cursor (skipping white space) is
+ // not a word character, try finding a match and select a (),
+ // {}, [], #if/#endif, etc. block.
end_visual = curwin->w_cursor;
while (gc = gchar_pos(&end_visual), ascii_iswhite(gc)) {
inc(&end_visual);
@@ -2167,8 +2156,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
}
if (pos == NULL && (is_click || is_drag)) {
- /* When not found a match or when dragging: extend to include
- * a word. */
+ // When not found a match or when dragging: extend to include a word.
if (lt(curwin->w_cursor, orig_cursor)) {
find_start_of_word(&curwin->w_cursor);
find_end_of_word(&VIsual);
@@ -2176,7 +2164,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
find_start_of_word(&VIsual);
if (*p_sel == 'e' && *get_cursor_pos_ptr() != NUL) {
curwin->w_cursor.col +=
- utfc_ptr2len(get_cursor_pos_ptr());
+ utfc_ptr2len((char *)get_cursor_pos_ptr());
}
find_end_of_word(&curwin->w_cursor);
}
@@ -2204,9 +2192,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
return moved;
}
-/*
- * Move "pos" back to the start of the word it's in.
- */
+/// Move "pos" back to the start of the word it's in.
static void find_start_of_word(pos_T *pos)
{
char_u *line;
@@ -2226,10 +2212,8 @@ static void find_start_of_word(pos_T *pos)
}
}
-/*
- * Move "pos" forward to the end of the word it's in.
- * When 'selection' is "exclusive", the position is just after the word.
- */
+/// Move "pos" forward to the end of the word it's in.
+/// When 'selection' is "exclusive", the position is just after the word.
static void find_end_of_word(pos_T *pos)
{
char_u *line;
@@ -2243,7 +2227,7 @@ static void find_end_of_word(pos_T *pos)
}
cclass = get_mouse_class(line + pos->col);
while (line[pos->col] != NUL) {
- col = pos->col + utfc_ptr2len(line + pos->col);
+ col = pos->col + utfc_ptr2len((char *)line + pos->col);
if (get_mouse_class(line + col) != cclass) {
if (*p_sel == 'e') {
pos->col = col;
@@ -2254,13 +2238,11 @@ static void find_end_of_word(pos_T *pos)
}
}
-/*
- * Get class of a character for selection: same class means same word.
- * 0: blank
- * 1: punctuation groups
- * 2: normal word character
- * >2: multi-byte word character.
- */
+/// Get class of a character for selection: same class means same word.
+/// 0: blank
+/// 1: punctuation groups
+/// 2: normal word character
+/// >2: multi-byte word character.
static int get_mouse_class(char_u *p)
{
if (MB_BYTE2LEN(p[0]) > 1) {
@@ -2275,23 +2257,19 @@ static int get_mouse_class(char_u *p)
return 2;
}
- /*
- * There are a few special cases where we want certain combinations of
- * characters to be considered as a single word. These are things like
- * "->", "/ *", "*=", "+=", "&=", "<=", ">=", "!=" etc. Otherwise, each
- * character is in its own class.
- */
- if (c != NUL && vim_strchr((char_u *)"-+*/%<>&|^!=", c) != NULL) {
+ // There are a few special cases where we want certain combinations of
+ // characters to be considered as a single word. These are things like
+ // "->", "/ *", "*=", "+=", "&=", "<=", ">=", "!=" etc. Otherwise, each
+ // character is in its own class.
+ if (c != NUL && vim_strchr("-+*/%<>&|^!=", c) != NULL) {
return 1;
}
return c;
}
-/*
- * End Visual mode.
- * This function should ALWAYS be called to end Visual mode, except from
- * do_pending_operator().
- */
+/// End Visual mode.
+/// This function should ALWAYS be called to end Visual mode, except from
+/// do_pending_operator().
void end_visual_mode(void)
{
VIsual_active = false;
@@ -2311,12 +2289,10 @@ void end_visual_mode(void)
may_clear_cmdline();
adjust_cursor_eol();
- trigger_modechanged();
+ may_trigger_modechanged();
}
-/*
- * Reset VIsual_active and VIsual_reselect.
- */
+/// Reset VIsual_active and VIsual_reselect.
void reset_VIsual_and_resel(void)
{
if (VIsual_active) {
@@ -2326,9 +2302,7 @@ void reset_VIsual_and_resel(void)
VIsual_reselect = false;
}
-/*
- * Reset VIsual_active and VIsual_reselect if it's set.
- */
+/// Reset VIsual_active and VIsual_reselect if it's set.
void reset_VIsual(void)
{
if (VIsual_active) {
@@ -2346,12 +2320,14 @@ void restore_visual_mode(void)
}
}
-// Check for a balloon-eval special item to include when searching for an
-// identifier. When "dir" is BACKWARD "ptr[-1]" must be valid!
-// Returns true if the character at "*ptr" should be included.
-// "dir" is FORWARD or BACKWARD, the direction of searching.
-// "*colp" is in/decremented if "ptr[-dir]" should also be included.
-// "bnp" points to a counter for square brackets.
+/// Check for a balloon-eval special item to include when searching for an
+/// identifier. When "dir" is BACKWARD "ptr[-1]" must be valid!
+///
+/// @return true if the character at "*ptr" should be included.
+///
+/// @param dir the direction of searching, is either FORWARD or BACKWARD
+/// @param *colp is in/decremented if "ptr[-dir]" should also be included.
+/// @param bnp points to a counter for square brackets.
static bool find_is_eval_item(const char_u *const ptr, int *const colp, int *const bnp,
const int dir)
{
@@ -2380,25 +2356,26 @@ static bool find_is_eval_item(const char_u *const ptr, int *const colp, int *con
return false;
}
-// Find the identifier under or to the right of the cursor.
-// "find_type" can have one of three values:
-// FIND_IDENT: find an identifier (keyword)
-// FIND_STRING: find any non-white text
-// FIND_IDENT + FIND_STRING: find any non-white text, identifier preferred.
-// FIND_EVAL: find text useful for C program debugging
-//
-// There are three steps:
-// 1. Search forward for the start of an identifier/text. Doesn't move if
-// already on one.
-// 2. Search backward for the start of this identifier/text.
-// This doesn't match the real Vi but I like it a little better and it
-// shouldn't bother anyone.
-// 3. Search forward to the end of this identifier/text.
-// When FIND_IDENT isn't defined, we backup until a blank.
-//
-// Returns the length of the text, or zero if no text is found.
-// If text is found, a pointer to the text is put in "*text". This
-// points into the current buffer line and is not always NUL terminated.
+/// Find the identifier under or to the right of the cursor.
+/// "find_type" can have one of three values:
+/// FIND_IDENT: find an identifier (keyword)
+/// FIND_STRING: find any non-white text
+/// FIND_IDENT + FIND_STRING: find any non-white text, identifier preferred.
+/// FIND_EVAL: find text useful for C program debugging
+///
+/// There are three steps:
+/// 1. Search forward for the start of an identifier/text. Doesn't move if
+/// already on one.
+/// 2. Search backward for the start of this identifier/text.
+/// This doesn't match the real Vi but I like it a little better and it
+/// shouldn't bother anyone.
+/// 3. Search forward to the end of this identifier/text.
+/// When FIND_IDENT isn't defined, we backup until a blank.
+///
+/// @return the length of the text, or zero if no text is found.
+///
+/// If text is found, a pointer to the text is put in "*text". This
+/// points into the current buffer line and is not always NUL terminated.
size_t find_ident_under_cursor(char_u **text, int find_type)
FUNC_ATTR_NONNULL_ARG(1)
{
@@ -2436,7 +2413,7 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol, char_u **te
if (this_class != 0 && (i == 1 || this_class != 1)) {
break;
}
- col += utfc_ptr2len(ptr + col);
+ col += utfc_ptr2len((char *)ptr + col);
}
// When starting on a ']' count it, so that we include the '['.
@@ -2504,43 +2481,48 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol, char_u **te
|| ((find_type & FIND_EVAL)
&& col <= (int)startcol
&& find_is_eval_item(ptr + col, &col, &bn, FORWARD)))) {
- col += utfc_ptr2len(ptr + col);
+ col += utfc_ptr2len((char *)ptr + col);
}
assert(col >= 0);
return (size_t)col;
}
-/*
- * Prepare for redo of a normal command.
- */
+/// Prepare for redo of a normal command.
static void prep_redo_cmd(cmdarg_T *cap)
{
prep_redo(cap->oap->regname, cap->count0,
NUL, cap->cmdchar, NUL, NUL, cap->nchar);
}
-/*
- * Prepare for redo of any command.
- * Note that only the last argument can be a multi-byte char.
- */
+/// Prepare for redo of any command.
+/// Note that only the last argument can be a multi-byte char.
void prep_redo(int regname, long num, int cmd1, int cmd2, int cmd3, int cmd4, int cmd5)
{
+ prep_redo_num2(regname, num, cmd1, cmd2, 0L, cmd3, cmd4, cmd5);
+}
+
+/// Prepare for redo of any command with extra count after "cmd2".
+void prep_redo_num2(int regname, long num1, int cmd1, int cmd2, long num2, int cmd3, int cmd4,
+ int cmd5)
+{
ResetRedobuff();
if (regname != 0) { // yank from specified buffer
AppendCharToRedobuff('"');
AppendCharToRedobuff(regname);
}
- if (num) {
- AppendNumberToRedobuff(num);
+ if (num1 != 0) {
+ AppendNumberToRedobuff(num1);
}
-
if (cmd1 != NUL) {
AppendCharToRedobuff(cmd1);
}
if (cmd2 != NUL) {
AppendCharToRedobuff(cmd2);
}
+ if (num2 != 0) {
+ AppendNumberToRedobuff(num2);
+ }
if (cmd3 != NUL) {
AppendCharToRedobuff(cmd3);
}
@@ -2552,11 +2534,9 @@ void prep_redo(int regname, long num, int cmd1, int cmd2, int cmd3, int cmd4, in
}
}
-/*
- * check for operator active and clear it
- *
- * return true if operator was active
- */
+/// check for operator active and clear it
+///
+/// @return true if operator was active
static bool checkclearop(oparg_T *oap)
{
if (oap->op_type == OP_NOP) {
@@ -2566,15 +2546,12 @@ static bool checkclearop(oparg_T *oap)
return true;
}
-/*
- * Check for operator or Visual active. Clear active operator.
- *
- * Return true if operator or Visual was active.
- */
+/// Check for operator or Visual active. Clear active operator.
+///
+/// @return true if operator or Visual was active.
static bool checkclearopq(oparg_T *oap)
{
- if (oap->op_type == OP_NOP
- && !VIsual_active) {
+ if (oap->op_type == OP_NOP && !VIsual_active) {
return false;
}
clearopbeep(oap);
@@ -2596,9 +2573,7 @@ void clearopbeep(oparg_T *oap)
beep_flush();
}
-/*
- * Remove the shift modifier from a special key.
- */
+/// Remove the shift modifier from a special key.
static void unshift_special(cmdarg_T *cap)
{
switch (cap->cmdchar) {
@@ -2631,13 +2606,12 @@ void may_clear_cmdline(void)
}
// Routines for displaying a partly typed command
-#define SHOWCMD_BUFLEN SHOWCMD_COLS + 1 + 30
+#define SHOWCMD_BUFLEN (SHOWCMD_COLS + 1 + 30)
static char_u showcmd_buf[SHOWCMD_BUFLEN];
static char_u old_showcmd_buf[SHOWCMD_BUFLEN]; // For push_showcmd()
static bool showcmd_is_clear = true;
static bool showcmd_visual = false;
-
void clear_showcmd(void)
{
if (!p_sc) {
@@ -2691,14 +2665,14 @@ void clear_showcmd(void)
e = ml_get_pos(&VIsual);
}
while ((*p_sel != 'e') ? s <= e : s < e) {
- l = utfc_ptr2len(s);
+ l = utfc_ptr2len((char *)s);
if (l == 0) {
- ++bytes;
- ++chars;
+ bytes++;
+ chars++;
break; // end of line
}
bytes += l;
- ++chars;
+ chars++;
s += l;
}
if (bytes == chars) {
@@ -2707,7 +2681,7 @@ void clear_showcmd(void)
sprintf((char *)showcmd_buf, "%d-%d", chars, bytes);
}
}
- int limit = ui_has(kUIMessages) ? SHOWCMD_BUFLEN-1 : SHOWCMD_COLS;
+ int limit = ui_has(kUIMessages) ? SHOWCMD_BUFLEN - 1 : SHOWCMD_COLS;
showcmd_buf[limit] = NUL; // truncate
showcmd_visual = true;
} else {
@@ -2723,16 +2697,14 @@ void clear_showcmd(void)
display_showcmd();
}
-/*
- * Add 'c' to string of shown command chars.
- * Return true if output has been written (and setcursor() has been called).
- */
+/// Add 'c' to string of shown command chars.
+///
+/// @return true if output has been written (and setcursor() has been called).
bool add_to_showcmd(int c)
{
char_u *p;
int i;
- static int ignore[] =
- {
+ static int ignore[] = {
K_IGNORE,
K_LEFTMOUSE, K_LEFTDRAG, K_LEFTRELEASE, K_MOUSEMOVE,
K_MIDDLEMOUSE, K_MIDDLEDRAG, K_MIDDLERELEASE,
@@ -2754,7 +2726,7 @@ bool add_to_showcmd(int c)
// Ignore keys that are scrollbar updates and mouse clicks
if (IS_SPECIAL(c)) {
- for (i = 0; ignore[i] != 0; ++i) {
+ for (i = 0; ignore[i] != 0; i++) {
if (ignore[i] == c) {
return false;
}
@@ -2767,7 +2739,7 @@ bool add_to_showcmd(int c)
}
size_t old_len = STRLEN(showcmd_buf);
size_t extra_len = STRLEN(p);
- size_t limit = ui_has(kUIMessages) ? SHOWCMD_BUFLEN-1 : SHOWCMD_COLS;
+ size_t limit = ui_has(kUIMessages) ? SHOWCMD_BUFLEN - 1 : SHOWCMD_COLS;
if (old_len + extra_len > limit) {
size_t overflow = old_len + extra_len - limit;
memmove(showcmd_buf, showcmd_buf + overflow, old_len - overflow + 1);
@@ -2789,9 +2761,7 @@ void add_to_showcmd_c(int c)
setcursor();
}
-/*
- * Delete 'len' characters from the end of the shown command.
- */
+/// Delete 'len' characters from the end of the shown command.
static void del_from_showcmd(int len)
{
int old_len;
@@ -2811,10 +2781,8 @@ static void del_from_showcmd(int len)
}
}
-/*
- * push_showcmd() and pop_showcmd() are used when waiting for the user to type
- * something and there is a partial mapping.
- */
+/// push_showcmd() and pop_showcmd() are used when waiting for the user to type
+/// something and there is a partial mapping.
void push_showcmd(void)
{
if (p_sc) {
@@ -2835,18 +2803,22 @@ void pop_showcmd(void)
static void display_showcmd(void)
{
+ if (p_ch < 1 && !ui_has(kUIMessages)) {
+ return;
+ }
+
int len;
len = (int)STRLEN(showcmd_buf);
showcmd_is_clear = (len == 0);
if (ui_has(kUIMessages)) {
- Array content = ARRAY_DICT_INIT;
+ MAXSIZE_TEMP_ARRAY(content, 1);
+ MAXSIZE_TEMP_ARRAY(chunk, 2);
if (len > 0) {
- Array chunk = ARRAY_DICT_INIT;
// placeholder for future highlight support
- ADD(chunk, INTEGER_OBJ(0));
- ADD(chunk, STRING_OBJ(cstr_to_string((char *)showcmd_buf)));
- ADD(content, ARRAY_OBJ(chunk));
+ ADD_C(chunk, INTEGER_OBJ(0));
+ ADD_C(chunk, STRING_OBJ(cstr_as_string((char *)showcmd_buf)));
+ ADD_C(content, ARRAY_OBJ(chunk));
}
ui_call_msg_showcmd(content);
return;
@@ -2868,11 +2840,9 @@ static void display_showcmd(void)
grid_puts_line_flush(false);
}
-/*
- * When "check" is false, prepare for commands that scroll the window.
- * When "check" is true, take care of scroll-binding after the window has
- * scrolled. Called from normal_cmd() and edit().
- */
+/// When "check" is false, prepare for commands that scroll the window.
+/// When "check" is true, take care of scroll-binding after the window has
+/// scrolled. Called from normal_cmd() and edit().
void do_check_scrollbind(bool check)
{
static win_T *old_curwin = NULL;
@@ -2887,11 +2857,9 @@ void do_check_scrollbind(bool check)
if (did_syncbind) {
did_syncbind = false;
} else if (curwin == old_curwin) {
- /*
- * Synchronize other windows, as necessary according to
- * 'scrollbind'. Don't do this after an ":edit" command, except
- * when 'diff' is set.
- */
+ // Synchronize other windows, as necessary according to
+ // 'scrollbind'. Don't do this after an ":edit" command, except
+ // when 'diff' is set.
if ((curwin->w_buffer == old_buf
|| curwin->w_p_diff
)
@@ -2902,17 +2870,15 @@ void do_check_scrollbind(bool check)
(long)(curwin->w_leftcol - old_leftcol));
}
} else if (vim_strchr(p_sbo, 'j')) { // jump flag set in 'scrollopt'
- /*
- * When switching between windows, make sure that the relative
- * vertical offset is valid for the new window. The relative
- * offset is invalid whenever another 'scrollbind' window has
- * scrolled to a point that would force the current window to
- * scroll past the beginning or end of its buffer. When the
- * resync is performed, some of the other 'scrollbind' windows may
- * need to jump so that the current window's relative position is
- * visible on-screen.
- */
- check_scrollbind(curwin->w_topline - curwin->w_scbind_pos, 0L);
+ // When switching between windows, make sure that the relative
+ // vertical offset is valid for the new window. The relative
+ // offset is invalid whenever another 'scrollbind' window has
+ // scrolled to a point that would force the current window to
+ // scroll past the beginning or end of its buffer. When the
+ // resync is performed, some of the other 'scrollbind' windows may
+ // need to jump so that the current window's relative position is
+ // visible on-screen.
+ check_scrollbind(curwin->w_topline - (linenr_T)curwin->w_scbind_pos, 0L);
}
curwin->w_scbind_pos = curwin->w_topline;
}
@@ -2924,11 +2890,9 @@ void do_check_scrollbind(bool check)
old_leftcol = curwin->w_leftcol;
}
-/*
- * Synchronize any windows that have "scrollbind" set, based on the
- * number of rows by which the current window has changed
- * (1998-11-02 16:21:01 R. Edward Ralston <eralston@computer.org>)
- */
+/// Synchronize any windows that have "scrollbind" set, based on the
+/// number of rows by which the current window has changed
+/// (1998-11-02 16:21:01 R. Edward Ralston <eralston@computer.org>)
void check_scrollbind(linenr_T topline_diff, long leftcol_diff)
{
bool want_ver;
@@ -2941,16 +2905,12 @@ void check_scrollbind(linenr_T topline_diff, long leftcol_diff)
long topline;
long y;
- /*
- * check 'scrollopt' string for vertical and horizontal scroll options
- */
+ // check 'scrollopt' string for vertical and horizontal scroll options
want_ver = (vim_strchr(p_sbo, 'v') && topline_diff != 0);
want_ver |= old_curwin->w_p_diff;
want_hor = (vim_strchr(p_sbo, 'h') && (leftcol_diff || topline_diff != 0));
- /*
- * loop through the scrollbound windows and scroll accordingly
- */
+ // loop through the scrollbound windows and scroll accordingly
VIsual_select = VIsual_active = 0;
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
curwin = wp;
@@ -2959,9 +2919,7 @@ void check_scrollbind(linenr_T topline_diff, long leftcol_diff)
if (curwin == old_curwin || !curwin->w_p_scb) {
continue;
}
- /*
- * do the vertical scroll
- */
+ // do the vertical scroll
if (want_ver) {
if (old_curwin->w_p_diff && curwin->w_p_diff) {
diff_set_topline(old_curwin, curwin);
@@ -2988,53 +2946,40 @@ void check_scrollbind(linenr_T topline_diff, long leftcol_diff)
curwin->w_redr_status = true;
}
- /*
- * do the horizontal scroll
- */
+ // do the horizontal scroll
if (want_hor && curwin->w_leftcol != tgt_leftcol) {
curwin->w_leftcol = tgt_leftcol;
leftcol_changed();
}
}
- /*
- * reset current-window
- */
+ // reset current-window
VIsual_select = old_VIsual_select;
VIsual_active = old_VIsual_active;
curwin = old_curwin;
curbuf = old_curbuf;
}
-/*
- * Command character that's ignored.
- * Used for CTRL-Q and CTRL-S to avoid problems with terminals that use
- * xon/xoff.
- */
+/// Command character that's ignored.
+/// Used for CTRL-Q and CTRL-S to avoid problems with terminals that use
+/// xon/xoff.
static void nv_ignore(cmdarg_T *cap)
{
cap->retval |= CA_COMMAND_BUSY; // don't call edit() now
}
-/*
- * Command character that doesn't do anything, but unlike nv_ignore() does
- * start edit(). Used for "startinsert" executed while starting up.
- */
+/// Command character that doesn't do anything, but unlike nv_ignore() does
+/// start edit(). Used for "startinsert" executed while starting up.
static void nv_nop(cmdarg_T *cap)
-{
-}
+{}
-/*
- * Command character doesn't exist.
- */
+/// Command character doesn't exist.
static void nv_error(cmdarg_T *cap)
{
clearopbeep(cap->oap);
}
-/*
- * <Help> and <F1> commands.
- */
+/// <Help> and <F1> commands.
static void nv_help(cmdarg_T *cap)
{
if (!checkclearopq(cap->oap)) {
@@ -3042,9 +2987,7 @@ static void nv_help(cmdarg_T *cap)
}
}
-/*
- * CTRL-A and CTRL-X: Add or subtract from letter or number under cursor.
- */
+/// CTRL-A and CTRL-X: Add or subtract from letter or number under cursor.
static void nv_addsub(cmdarg_T *cap)
{
if (bt_prompt(curbuf) && !prompt_curpos_editable()) {
@@ -3052,7 +2995,7 @@ static void nv_addsub(cmdarg_T *cap)
} else if (!VIsual_active && cap->oap->op_type == OP_NOP) {
prep_redo_cmd(cap);
cap->oap->op_type = cap->cmdchar == Ctrl_A ? OP_NR_ADD : OP_NR_SUB;
- op_addsub(cap->oap, cap->count1, cap->arg);
+ op_addsub(cap->oap, (linenr_T)cap->count1, cap->arg);
cap->oap->op_type = OP_NOP;
} else if (VIsual_active) {
nv_operator(cap);
@@ -3061,9 +3004,7 @@ static void nv_addsub(cmdarg_T *cap)
}
}
-/*
- * CTRL-F, CTRL-B, etc: Scroll page up or down.
- */
+/// CTRL-F, CTRL-B, etc: Scroll page up or down.
static void nv_page(cmdarg_T *cap)
{
if (!checkclearop(cap->oap)) {
@@ -3090,13 +3031,19 @@ static void nv_gd(oparg_T *oap, int nchar, int thisblock)
if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0
|| !find_decl(ptr, len, nchar == 'd', thisblock, SEARCH_START)) {
clearopbeep(oap);
- } else if ((fdo_flags & FDO_SEARCH) && KeyTyped && oap->op_type == OP_NOP) {
- foldOpenCursor();
+ } else {
+ if ((fdo_flags & FDO_SEARCH) && KeyTyped && oap->op_type == OP_NOP) {
+ foldOpenCursor();
+ }
+ // clear any search statistics
+ if (messaging() && !msg_silent && !shortmess(SHM_SEARCHCOUNT)) {
+ clear_cmdline = true;
+ }
}
}
-// Return true if line[offset] is not inside a C-style comment or string, false
-// otherwise.
+/// @return true if line[offset] is not inside a C-style comment or string,
+/// false otherwise.
static bool is_ident(char_u *line, int offset)
{
bool incomment = false;
@@ -3162,11 +3109,9 @@ bool find_decl(char_u *ptr, size_t len, bool locally, bool thisblock, int flags_
p_ws = false; // don't wrap around end of file now
p_scs = false; // don't switch ignorecase off now
- /*
- * With "gD" go to line 1.
- * With "gd" Search back for the start of the current function, then go
- * back until a blank line. If this fails go to line 1.
- */
+ // With "gD" go to line 1.
+ // With "gd" Search back for the start of the current function, then go
+ // back until a blank line. If this fails go to line 1.
if (!locally || !findpar(&incll, BACKWARD, 1L, '{', false)) {
setpcmark(); // Set in findpar() otherwise
curwin->w_cursor.lnum = 1;
@@ -3174,8 +3119,8 @@ bool find_decl(char_u *ptr, size_t len, bool locally, bool thisblock, int flags_
} else {
par_pos = curwin->w_cursor;
while (curwin->w_cursor.lnum > 1
- && *skipwhite(get_cursor_line_ptr()) != NUL) {
- --curwin->w_cursor.lnum;
+ && *skipwhite((char *)get_cursor_line_ptr()) != NUL) {
+ curwin->w_cursor.lnum--;
}
}
curwin->w_cursor.col = 0;
@@ -3211,9 +3156,9 @@ bool find_decl(char_u *ptr, size_t len, bool locally, bool thisblock, int flags_
}
break;
}
- if (get_leader_len(get_cursor_line_ptr(), NULL, false, true) > 0) {
+ if (get_leader_len((char *)get_cursor_line_ptr(), NULL, false, true) > 0) {
// Ignore this line, continue at start of next line.
- ++curwin->w_cursor.lnum;
+ curwin->w_cursor.lnum++;
curwin->w_cursor.col = 0;
continue;
}
@@ -3265,13 +3210,11 @@ bool find_decl(char_u *ptr, size_t len, bool locally, bool thisblock, int flags_
return retval;
}
-/*
- * Move 'dist' lines in direction 'dir', counting lines by *screen*
- * lines rather than lines in the file.
- * 'dist' must be positive.
- *
- * Return true if able to move cursor, false otherwise.
- */
+/// Move 'dist' lines in direction 'dir', counting lines by *screen*
+/// lines rather than lines in the file.
+/// 'dist' must be positive.
+///
+/// @return true if able to move cursor, false otherwise.
static bool nv_screengo(oparg_T *oap, int dir, long dist)
{
int linelen = linetabsize(get_cursor_line_ptr());
@@ -3281,7 +3224,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);
@@ -3394,15 +3337,20 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist)
}
if (curwin->w_cursor.col > 0 && curwin->w_p_wrap) {
- /*
- * Check for landing on a character that got split at the end of the
- * last line. We want to advance a screenline, not end up in the same
- * screenline or move two screenlines.
- */
+ // Check for landing on a character that got split at the end of the
+ // last line. We want to advance a screenline, not end up in the same
+ // screenline or move two screenlines.
validate_virtcol();
colnr_T virtcol = curwin->w_virtcol;
if (virtcol > (colnr_T)width1 && *get_showbreak_value(curwin) != NUL) {
- virtcol -= vim_strsize(get_showbreak_value(curwin));
+ virtcol -= vim_strsize((char *)get_showbreak_value(curwin));
+ }
+
+ int c = utf_ptr2char((char *)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
@@ -3410,7 +3358,7 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist)
? (curwin->w_curswant > (colnr_T)width1 / 2)
: ((curwin->w_curswant - width1) % width2
> (colnr_T)width2 / 2))) {
- --curwin->w_cursor.col;
+ curwin->w_cursor.col--;
}
}
@@ -3420,12 +3368,10 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist)
return retval;
}
-/*
- * Mouse scroll wheel: Default action is to scroll three lines, or one page
- * when Shift or Ctrl is used.
- * K_MOUSEUP (cap->arg == 1) or K_MOUSEDOWN (cap->arg == 0) or
- * K_MOUSELEFT (cap->arg == -1) or K_MOUSERIGHT (cap->arg == -2)
- */
+/// Mouse scroll wheel: Default action is to scroll three lines, or one page
+/// when Shift or Ctrl is used.
+/// K_MOUSEUP (cap->arg == 1) or K_MOUSEDOWN (cap->arg == 0) or
+/// K_MOUSELEFT (cap->arg == -1) or K_MOUSERIGHT (cap->arg == -2)
static void nv_mousescroll(cmdarg_T *cap)
{
win_T *old_curwin = curwin;
@@ -3450,8 +3396,8 @@ static void nv_mousescroll(cmdarg_T *cap)
if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) {
(void)onepage(cap->arg ? FORWARD : BACKWARD, 1L);
} else {
- cap->count1 = 3;
- cap->count0 = 3;
+ cap->count1 = p_mousescroll_vert;
+ cap->count0 = p_mousescroll_vert;
nv_scroll_line(cap);
}
} else {
@@ -3467,18 +3413,14 @@ static void nv_mousescroll(cmdarg_T *cap)
curbuf = curwin->w_buffer;
}
-/*
- * Mouse clicks and drags.
- */
+/// Mouse clicks and drags.
static void nv_mouse(cmdarg_T *cap)
{
(void)do_mouse(cap->oap, cap->cmdchar, BACKWARD, cap->count1, 0);
}
-/*
- * Handle CTRL-E and CTRL-Y commands: scroll a line up or down.
- * cap->arg must be true for CTRL-E.
- */
+/// Handle CTRL-E and CTRL-Y commands: scroll a line up or down.
+/// cap->arg must be true for CTRL-E.
static void nv_scroll_line(cmdarg_T *cap)
{
if (!checkclearop(cap->oap)) {
@@ -3486,9 +3428,7 @@ static void nv_scroll_line(cmdarg_T *cap)
}
}
-/*
- * Scroll "count" lines up or down, and redraw.
- */
+/// Scroll "count" lines up or down, and redraw.
void scroll_redraw(int up, long count)
{
linenr_T prev_topline = curwin->w_topline;
@@ -3538,9 +3478,109 @@ void scroll_redraw(int up, long count)
redraw_later(curwin, VALID);
}
-/*
- * Commands that start with "z".
- */
+/// Get the count specified after a 'z' command.
+/// @return true to process the 'z' command and false to skip it.
+static bool nv_z_get_count(cmdarg_T *cap, int *nchar_arg)
+{
+ int nchar = *nchar_arg;
+
+ // "z123{nchar}": edit the count before obtaining {nchar}
+ if (checkclearop(cap->oap)) {
+ return false;
+ }
+ long n = nchar - '0';
+
+ for (;;) {
+ no_mapping++;
+ allow_keys++; // no mapping for nchar, but allow key codes
+ nchar = plain_vgetc();
+ LANGMAP_ADJUST(nchar, true);
+ no_mapping--;
+ allow_keys--;
+ (void)add_to_showcmd(nchar);
+ if (nchar == K_DEL || nchar == K_KDEL) {
+ n /= 10;
+ } else if (ascii_isdigit(nchar)) {
+ n = n * 10 + (nchar - '0');
+ } else if (nchar == CAR) {
+ win_setheight((int)n);
+ break;
+ } else if (nchar == 'l'
+ || nchar == 'h'
+ || nchar == K_LEFT
+ || nchar == K_RIGHT) {
+ cap->count1 = n ? n * cap->count1 : cap->count1;
+ *nchar_arg = nchar;
+ return true;
+ } else {
+ clearopbeep(cap->oap);
+ break;
+ }
+ }
+ cap->oap->op_type = OP_NOP;
+ return false;
+}
+
+/// "zug" and "zuw": undo "zg" and "zw"
+/// "zg": add good word to word list
+/// "zw": add wrong word to word list
+/// "zG": add good word to temp word list
+/// "zW": add wrong word to temp word list
+static int nv_zg_zw(cmdarg_T *cap, int nchar)
+{
+ bool undo = false;
+
+ if (nchar == 'u') {
+ no_mapping++;
+ allow_keys++; // no mapping for nchar, but allow key codes
+ nchar = plain_vgetc();
+ LANGMAP_ADJUST(nchar, true);
+ no_mapping--;
+ allow_keys--;
+ (void)add_to_showcmd(nchar);
+ if (vim_strchr("gGwW", nchar) == NULL) {
+ clearopbeep(cap->oap);
+ return OK;
+ }
+ undo = true;
+ }
+
+ if (checkclearop(cap->oap)) {
+ return OK;
+ }
+ char_u *ptr = NULL;
+ size_t len;
+ if (VIsual_active && !get_visual_text(cap, &ptr, &len)) {
+ return FAIL;
+ }
+ if (ptr == NULL) {
+ pos_T pos = curwin->w_cursor;
+
+ // Find bad word under the cursor. When 'spell' is
+ // off this fails and find_ident_under_cursor() is
+ // used below.
+ emsg_off++;
+ len = spell_move_to(curwin, FORWARD, true, true, NULL);
+ emsg_off--;
+ if (len != 0 && curwin->w_cursor.col <= pos.col) {
+ ptr = ml_get_pos(&curwin->w_cursor);
+ }
+ curwin->w_cursor = pos;
+ }
+
+ if (ptr == NULL && (len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0) {
+ return FAIL;
+ }
+ assert(len <= INT_MAX);
+ spell_add_word(ptr, (int)len,
+ nchar == 'w' || nchar == 'W' ? SPELL_ADD_BAD : SPELL_ADD_GOOD,
+ (nchar == 'G' || nchar == 'W') ? 0 : (int)cap->count1,
+ undo);
+
+ return OK;
+}
+
+/// Commands that start with "z".
static void nv_zet(cmdarg_T *cap)
{
int n;
@@ -3548,70 +3588,33 @@ static void nv_zet(cmdarg_T *cap)
int nchar = cap->nchar;
long old_fdl = curwin->w_p_fdl;
int old_fen = curwin->w_p_fen;
- bool undo = false;
int l_p_siso = (int)get_sidescrolloff_value(curwin);
- assert(l_p_siso <= INT_MAX);
- if (ascii_isdigit(nchar)) {
- /*
- * "z123{nchar}": edit the count before obtaining {nchar}
- */
- if (checkclearop(cap->oap)) {
- return;
- }
- n = nchar - '0';
- for (;;) {
- no_mapping++;
- nchar = plain_vgetc();
- LANGMAP_ADJUST(nchar, true);
- no_mapping--;
- (void)add_to_showcmd(nchar);
- if (nchar == K_DEL || nchar == K_KDEL) {
- n /= 10;
- } else if (ascii_isdigit(nchar)) {
- n = n * 10 + (nchar - '0');
- } else if (nchar == CAR) {
- win_setheight(n);
- break;
- } else if (nchar == 'l'
- || nchar == 'h'
- || nchar == K_LEFT
- || nchar == K_RIGHT) {
- cap->count1 = n ? n * cap->count1 : cap->count1;
- goto dozet;
- } else {
- clearopbeep(cap->oap);
- break;
- }
- }
- cap->oap->op_type = OP_NOP;
+ if (ascii_isdigit(nchar) && !nv_z_get_count(cap, &nchar)) {
return;
}
-dozet:
// "zf" and "zF" are always an operator, "zd", "zo", "zO", "zc"
// and "zC" only in Visual mode. "zj" and "zk" are motion
// commands.
if (cap->nchar != 'f' && cap->nchar != 'F'
- && !(VIsual_active && vim_strchr((char_u *)"dcCoO", cap->nchar))
+ && !(VIsual_active && vim_strchr("dcCoO", cap->nchar))
&& cap->nchar != 'j' && cap->nchar != 'k'
&& checkclearop(cap->oap)) {
return;
}
- /*
- * For "z+", "z<CR>", "zt", "z.", "zz", "z^", "z-", "zb":
- * If line number given, set cursor.
- */
- if ((vim_strchr((char_u *)"+\r\nt.z^-b", nchar) != NULL)
+ // For "z+", "z<CR>", "zt", "z.", "zz", "z^", "z-", "zb":
+ // If line number given, set cursor.
+ if ((vim_strchr("+\r\nt.z^-b", nchar) != NULL)
&& cap->count0
&& cap->count0 != curwin->w_cursor.lnum) {
setpcmark();
if (cap->count0 > curbuf->b_ml.ml_line_count) {
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
} else {
- curwin->w_cursor.lnum = cap->count0;
+ curwin->w_cursor.lnum = (linenr_T)cap->count0;
}
check_cursor_col();
}
@@ -3940,60 +3943,15 @@ dozet:
}
break;
-
case 'u': // "zug" and "zuw": undo "zg" and "zw"
- no_mapping++;
- nchar = plain_vgetc();
- LANGMAP_ADJUST(nchar, true);
- no_mapping--;
- (void)add_to_showcmd(nchar);
- if (vim_strchr((char_u *)"gGwW", nchar) == NULL) {
- clearopbeep(cap->oap);
- break;
- }
- undo = true;
- FALLTHROUGH;
-
case 'g': // "zg": add good word to word list
case 'w': // "zw": add wrong word to word list
case 'G': // "zG": add good word to temp word list
case 'W': // "zW": add wrong word to temp word list
- {
- char_u *ptr = NULL;
- size_t len;
-
- if (checkclearop(cap->oap)) {
- break;
- }
- if (VIsual_active && !get_visual_text(cap, &ptr, &len)) {
+ if (nv_zg_zw(cap, nchar) == FAIL) {
return;
}
- if (ptr == NULL) {
- pos_T pos = curwin->w_cursor;
-
- // Find bad word under the cursor. When 'spell' is
- // off this fails and find_ident_under_cursor() is
- // used below.
- emsg_off++;
- len = spell_move_to(curwin, FORWARD, true, true, NULL);
- emsg_off--;
- if (len != 0 && curwin->w_cursor.col <= pos.col) {
- ptr = ml_get_pos(&curwin->w_cursor);
- }
- curwin->w_cursor = pos;
- }
-
- if (ptr == NULL && (len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0) {
- return;
- }
- assert(len <= INT_MAX);
- spell_add_word(ptr, (int)len,
- nchar == 'w' || nchar == 'W'
- ? SPELL_ADD_BAD : SPELL_ADD_GOOD,
- (nchar == 'G' || nchar == 'W') ? 0 : (int)cap->count1,
- undo);
- }
- break;
+ break;
case '=': // "z=": suggestions for a badly spelled word
if (!checkclearop(cap->oap)) {
@@ -4025,10 +3983,7 @@ dozet:
}
}
-
-/*
- * "Q" command.
- */
+/// "Q" command.
static void nv_regreplay(cmdarg_T *cap)
{
if (checkclearop(cap->oap)) {
@@ -4044,10 +3999,9 @@ static void nv_regreplay(cmdarg_T *cap)
}
}
-/// Handle a ":" command and <Cmd> or Lua keymaps.
+/// Handle a ":" command and <Cmd> or Lua mappings.
static void nv_colon(cmdarg_T *cap)
{
- int old_p_im;
bool cmd_result;
bool is_cmdkey = cap->cmdchar == K_COMMAND;
bool is_lua = cap->cmdchar == K_LUA;
@@ -4073,25 +4027,14 @@ static void nv_colon(cmdarg_T *cap)
compute_cmdrow();
}
- old_p_im = p_im;
-
if (is_lua) {
cmd_result = map_execute_lua();
} else {
- // get a command line and execute it
+ // get a command line and execute it
cmd_result = do_cmdline(NULL, is_cmdkey ? getcmdkeycmd : getexline, NULL,
cap->oap->op_type != OP_NOP ? DOCMD_KEEPLINE : 0);
}
- // If 'insertmode' changed, enter or exit Insert mode
- if (p_im != old_p_im) {
- if (p_im) {
- restart_edit = 'i';
- } else {
- restart_edit = 0;
- }
- }
-
if (cmd_result == false) {
// The Ex command failed, do not execute the operator.
clearop(cap->oap);
@@ -4106,14 +4049,12 @@ static void nv_colon(cmdarg_T *cap)
}
}
-/*
- * Handle CTRL-G command.
- */
+/// Handle CTRL-G command.
static void nv_ctrlg(cmdarg_T *cap)
{
if (VIsual_active) { // toggle Selection/Visual mode
VIsual_select = !VIsual_select;
- trigger_modechanged();
+ may_trigger_modechanged();
showmode();
} else if (!checkclearop(cap->oap)) {
// print full name if count given or :cd used
@@ -4121,9 +4062,7 @@ static void nv_ctrlg(cmdarg_T *cap)
}
}
-/*
- * Handle CTRL-H <Backspace> command.
- */
+/// Handle CTRL-H <Backspace> command.
static void nv_ctrlh(cmdarg_T *cap)
{
if (VIsual_active && VIsual_select) {
@@ -4134,9 +4073,7 @@ static void nv_ctrlh(cmdarg_T *cap)
}
}
-/*
- * CTRL-L: clear screen and redraw.
- */
+/// CTRL-L: clear screen and redraw.
static void nv_clear(cmdarg_T *cap)
{
if (!checkclearop(cap->oap)) {
@@ -4149,15 +4086,13 @@ static void nv_clear(cmdarg_T *cap)
}
}
-/*
- * CTRL-O: In Select mode: switch to Visual mode for one command.
- * Otherwise: Go to older pcmark.
- */
+/// CTRL-O: In Select mode: switch to Visual mode for one command.
+/// Otherwise: Go to older pcmark.
static void nv_ctrlo(cmdarg_T *cap)
{
if (VIsual_active && VIsual_select) {
VIsual_select = false;
- trigger_modechanged();
+ may_trigger_modechanged();
showmode();
restart_VIsual_select = 2; // restart Select mode later
} else {
@@ -4166,8 +4101,8 @@ static void nv_ctrlo(cmdarg_T *cap)
}
}
-// CTRL-^ command, short for ":e #". Works even when the alternate buffer is
-// not named.
+/// CTRL-^ command, short for ":e #". Works even when the alternate buffer is
+/// not named.
static void nv_hat(cmdarg_T *cap)
{
if (!checkclearopq(cap->oap)) {
@@ -4176,9 +4111,7 @@ static void nv_hat(cmdarg_T *cap)
}
}
-/*
- * "Z" commands.
- */
+/// "Z" commands.
static void nv_Zet(cmdarg_T *cap)
{
if (!checkclearopq(cap->oap)) {
@@ -4199,9 +4132,7 @@ static void nv_Zet(cmdarg_T *cap)
}
}
-/*
- * Call nv_ident() as if "c1" was used, with "c2" as next character.
- */
+/// Call nv_ident() as if "c1" was used, with "c2" as next character.
void do_nv_ident(int c1, int c2)
{
oparg_T oa;
@@ -4215,14 +4146,75 @@ void do_nv_ident(int c1, int c2)
nv_ident(&ca);
}
-/*
- * Handle the commands that use the word under the cursor.
- * [g] CTRL-] :ta to current identifier
- * [g] 'K' run program for current identifier
- * [g] '*' / to current identifier or string
- * [g] '#' ? to current identifier or string
- * g ']' :tselect for current identifier
- */
+/// 'K' normal-mode command. Get the command to lookup the keyword under the
+/// cursor.
+static size_t nv_K_getcmd(cmdarg_T *cap, char_u *kp, bool kp_help, bool kp_ex, char_u **ptr_arg,
+ size_t n, char *buf, size_t buf_size)
+{
+ if (kp_help) {
+ // in the help buffer
+ STRCPY(buf, "he! ");
+ return n;
+ }
+
+ if (kp_ex) {
+ // 'keywordprg' is an ex command
+ if (cap->count0 != 0) { // Send the count to the ex command.
+ snprintf(buf, buf_size, "%" PRId64, (int64_t)(cap->count0));
+ }
+ STRCAT(buf, kp);
+ STRCAT(buf, " ");
+ return n;
+ }
+
+ char_u *ptr = *ptr_arg;
+
+ // An external command will probably use an argument starting
+ // with "-" as an option. To avoid trouble we skip the "-".
+ while (*ptr == '-' && n > 0) {
+ ptr++;
+ n--;
+ }
+ if (n == 0) {
+ // found dashes only
+ emsg(_(e_noident));
+ xfree(buf);
+ *ptr_arg = ptr;
+ return 0;
+ }
+
+ // When a count is given, turn it into a range. Is this
+ // really what we want?
+ bool isman = (STRCMP(kp, "man") == 0);
+ bool isman_s = (STRCMP(kp, "man -s") == 0);
+ if (cap->count0 != 0 && !(isman || isman_s)) {
+ snprintf(buf, buf_size, ".,.+%" PRId64, (int64_t)(cap->count0 - 1));
+ }
+
+ do_cmdline_cmd("tabnew");
+ STRCAT(buf, "terminal ");
+ if (cap->count0 == 0 && isman_s) {
+ STRCAT(buf, "man");
+ } else {
+ STRCAT(buf, kp);
+ }
+ STRCAT(buf, " ");
+ if (cap->count0 != 0 && (isman || isman_s)) {
+ snprintf(buf + STRLEN(buf), buf_size - STRLEN(buf), "%" PRId64,
+ (int64_t)cap->count0);
+ STRCAT(buf, " ");
+ }
+
+ *ptr_arg = ptr;
+ return n;
+}
+
+/// Handle the commands that use the word under the cursor.
+/// [g] CTRL-] :ta to current identifier
+/// [g] 'K' run program for current identifier
+/// [g] '*' / to current identifier or string
+/// [g] '#' ? to current identifier or string
+/// g ']' :tselect for current identifier
static void nv_ident(cmdarg_T *cap)
{
char_u *ptr = NULL;
@@ -4245,9 +4237,7 @@ static void nv_ident(cmdarg_T *cap)
cmdchar = '#';
}
- /*
- * The "]", "CTRL-]" and "K" commands accept an argument in Visual mode.
- */
+ // The "]", "CTRL-]" and "K" commands accept an argument in Visual mode.
if (cmdchar == ']' || cmdchar == Ctrl_RSB || cmdchar == 'K') {
if (VIsual_active && get_visual_text(cap, &ptr, &n) == false) {
return;
@@ -4273,7 +4263,7 @@ static void nv_ident(cmdarg_T *cap)
assert(*kp != NUL); // option.c:do_set() should default to ":help" if empty.
bool kp_ex = (*kp == ':'); // 'keywordprg' is an ex command
bool kp_help = (STRCMP(kp, ":he") == 0 || STRCMP(kp, ":help") == 0);
- if (kp_help && *skipwhite(ptr) == NUL) {
+ if (kp_help && *skipwhite((char *)ptr) == NUL) {
emsg(_(e_noident)); // found white space only
return;
}
@@ -4284,12 +4274,10 @@ static void nv_ident(cmdarg_T *cap)
switch (cmdchar) {
case '*':
case '#':
- /*
- * Put cursor at start of word, makes search skip the word
- * under the cursor.
- * Call setpcmark() first, so "*``" puts the cursor back where
- * it was.
- */
+ // Put cursor at start of word, makes search skip the word
+ // under the cursor.
+ // Call setpcmark() first, so "*``" puts the cursor back where
+ // it was.
setpcmark();
curwin->w_cursor.col = (colnr_T)(ptr - get_cursor_line_ptr());
@@ -4300,48 +4288,9 @@ static void nv_ident(cmdarg_T *cap)
break;
case 'K':
- if (kp_help) {
- STRCPY(buf, "he! ");
- } else if (kp_ex) {
- if (cap->count0 != 0) { // Send the count to the ex command.
- snprintf(buf, buf_size, "%" PRId64, (int64_t)(cap->count0));
- }
- STRCAT(buf, kp);
- STRCAT(buf, " ");
- } else {
- // An external command will probably use an argument starting
- // with "-" as an option. To avoid trouble we skip the "-".
- while (*ptr == '-' && n > 0) {
- ++ptr;
- --n;
- }
- if (n == 0) {
- emsg(_(e_noident)); // found dashes only
- xfree(buf);
- return;
- }
-
- // When a count is given, turn it into a range. Is this
- // really what we want?
- bool isman = (STRCMP(kp, "man") == 0);
- bool isman_s = (STRCMP(kp, "man -s") == 0);
- if (cap->count0 != 0 && !(isman || isman_s)) {
- snprintf(buf, buf_size, ".,.+%" PRId64, (int64_t)(cap->count0 - 1));
- }
-
- do_cmdline_cmd("tabnew");
- STRCAT(buf, "terminal ");
- if (cap->count0 == 0 && isman_s) {
- STRCAT(buf, "man");
- } else {
- STRCAT(buf, kp);
- }
- STRCAT(buf, " ");
- if (cap->count0 != 0 && (isman || isman_s)) {
- snprintf(buf + STRLEN(buf), buf_size - STRLEN(buf), "%" PRId64,
- (int64_t)cap->count0);
- STRCAT(buf, " ");
- }
+ n = nv_K_getcmd(cap, kp, kp_help, kp_ex, &ptr, n, buf, buf_size);
+ if (n == 0) {
+ return;
}
break;
@@ -4372,7 +4321,7 @@ static void nv_ident(cmdarg_T *cap)
ptr = vim_strnsave(ptr, n);
if (kp_ex) {
// Escape the argument properly for an Ex command
- p = (char_u *)vim_strsave_fnameescape((const char *)ptr, false);
+ p = (char_u *)vim_strsave_fnameescape((const char *)ptr, VSE_NONE);
} else {
// Escape the argument properly for a shell command
p = vim_strsave_shellescape(ptr, true, true);
@@ -4401,12 +4350,12 @@ static void nv_ident(cmdarg_T *cap)
p = (char_u *)buf + STRLEN(buf);
while (n-- > 0) {
// put a backslash before \ and some others
- if (vim_strchr(aux_ptr, *ptr) != NULL) {
+ if (vim_strchr((char *)aux_ptr, *ptr) != NULL) {
*p++ = '\\';
}
// When current byte is a part of multibyte character, copy all
// bytes of that character.
- const size_t len = (size_t)(utfc_ptr2len(ptr) - 1);
+ const size_t len = (size_t)(utfc_ptr2len((char *)ptr) - 1);
for (size_t i = 0; i < len && n > 0; i++, n--) {
*p++ = *ptr++;
}
@@ -4415,9 +4364,7 @@ static void nv_ident(cmdarg_T *cap)
*p = NUL;
}
- /*
- * Execute the command.
- */
+ // Execute the command.
if (cmdchar == '*' || cmdchar == '#') {
if (!g_cmd
&& vim_iswordp(mb_prevptr(get_cursor_line_ptr(), ptr))) {
@@ -4437,11 +4384,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("<esc>", "<Cmd>bdelete!<CR>", MODE_TERMINAL, true);
}
}
@@ -4476,16 +4419,19 @@ 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((char *)(*pp) + (*lenp - 1)) - 1);
+ }
}
reset_VIsual_and_resel();
return true;
}
-/*
- * CTRL-T: backwards in tag stack
- */
+/// CTRL-T: backwards in tag stack
static void nv_tagpop(cmdarg_T *cap)
{
if (!checkclearopq(cap->oap)) {
@@ -4493,9 +4439,7 @@ static void nv_tagpop(cmdarg_T *cap)
}
}
-/*
- * Handle scrolling command 'H', 'L' and 'M'.
- */
+/// Handle scrolling command 'H', 'L' and 'M'.
static void nv_scroll(cmdarg_T *cap)
{
int used = 0;
@@ -4515,13 +4459,13 @@ static void nv_scroll(cmdarg_T *cap)
if (hasAnyFolding(curwin)) {
// Count a fold for one screen line.
for (n = cap->count1 - 1; n > 0
- && curwin->w_cursor.lnum > curwin->w_topline; --n) {
+ && curwin->w_cursor.lnum > curwin->w_topline; n--) {
(void)hasFolding(curwin->w_cursor.lnum,
&curwin->w_cursor.lnum, NULL);
- --curwin->w_cursor.lnum;
+ curwin->w_cursor.lnum--;
}
} else {
- curwin->w_cursor.lnum -= cap->count1 - 1;
+ curwin->w_cursor.lnum -= (linenr_T)cap->count1 - 1;
}
}
} else {
@@ -4532,17 +4476,17 @@ static void nv_scroll(cmdarg_T *cap)
validate_botline(curwin); // make sure w_empty_rows is valid
half = (curwin->w_height_inner - curwin->w_empty_rows + 1) / 2;
for (n = 0; curwin->w_topline + n < curbuf->b_ml.ml_line_count; n++) {
- // Count half he number of filler lines to be "below this
+ // Count half the number of filler lines to be "below this
// line" and half to be "above the next line".
- if (n > 0 && used + win_get_fill(curwin, curwin->w_topline + n) / 2 >= half) {
+ if (n > 0 && used + win_get_fill(curwin, curwin->w_topline + (linenr_T)n) / 2 >= half) {
n--;
break;
}
- used += plines_win(curwin, curwin->w_topline + n, true);
+ used += plines_win(curwin, curwin->w_topline + (linenr_T)n, true);
if (used >= half) {
break;
}
- if (hasFolding(curwin->w_topline + n, NULL, &lnum)) {
+ if (hasFolding(curwin->w_topline + (linenr_T)n, NULL, &lnum)) {
n = lnum - curwin->w_topline;
}
}
@@ -4561,7 +4505,7 @@ static void nv_scroll(cmdarg_T *cap)
n = lnum - curwin->w_topline;
}
}
- curwin->w_cursor.lnum = curwin->w_topline + n;
+ curwin->w_cursor.lnum = curwin->w_topline + (linenr_T)n;
if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) {
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
}
@@ -4574,13 +4518,10 @@ static void nv_scroll(cmdarg_T *cap)
beginline(BL_SOL | BL_FIX);
}
-/*
- * Cursor right commands.
- */
+/// Cursor right commands.
static void nv_right(cmdarg_T *cap)
{
long n;
- int PAST_LINE;
if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) {
// <C-Right> and <S-Right> move a word or WORD right
@@ -4593,26 +4534,23 @@ static void nv_right(cmdarg_T *cap)
cap->oap->motion_type = kMTCharWise;
cap->oap->inclusive = false;
- PAST_LINE = (VIsual_active && *p_sel != 'o');
+ bool past_line = (VIsual_active && *p_sel != 'o');
- /*
- * In virtual mode, there's no such thing as "PAST_LINE", as lines are
- * (theoretically) infinitely long.
- */
+ // In virtual edit mode, there's no such thing as "past_line", as lines
+ // are (theoretically) infinitely long.
if (virtual_active()) {
- PAST_LINE = 0;
+ past_line = false;
}
- for (n = cap->count1; n > 0; --n) {
- if ((!PAST_LINE && oneright() == false)
- || (PAST_LINE
- && *get_cursor_pos_ptr() == NUL)) {
- // <Space> wraps to next line if 'whichwrap' has 's'.
- // 'l' wraps to next line if 'whichwrap' has 'l'.
+ for (n = cap->count1; n > 0; n--) {
+ if ((!past_line && oneright() == false)
+ || (past_line && *get_cursor_pos_ptr() == NUL)) {
+ // <Space> wraps to next line if 'whichwrap' has 's'.
+ // 'l' wraps to next line if 'whichwrap' has 'l'.
// CURS_RIGHT wraps to next line if 'whichwrap' has '>'.
- if (((cap->cmdchar == ' ' && vim_strchr(p_ww, 's') != NULL)
- || (cap->cmdchar == 'l' && vim_strchr(p_ww, 'l') != NULL)
- || (cap->cmdchar == K_RIGHT && vim_strchr(p_ww, '>') != NULL))
+ if (((cap->cmdchar == ' ' && vim_strchr((char *)p_ww, 's') != NULL)
+ || (cap->cmdchar == 'l' && vim_strchr((char *)p_ww, 'l') != NULL)
+ || (cap->cmdchar == K_RIGHT && vim_strchr((char *)p_ww, '>') != NULL))
&& curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
// When deleting we also count the NL as a character.
// Set cap->oap->inclusive when last char in the line is
@@ -4622,7 +4560,7 @@ static void nv_right(cmdarg_T *cap)
&& !LINEEMPTY(curwin->w_cursor.lnum)) {
cap->oap->inclusive = true;
} else {
- ++curwin->w_cursor.lnum;
+ curwin->w_cursor.lnum++;
curwin->w_cursor.col = 0;
curwin->w_cursor.coladd = 0;
curwin->w_set_curswant = true;
@@ -4641,12 +4579,12 @@ static void nv_right(cmdarg_T *cap)
}
}
break;
- } else if (PAST_LINE) {
+ } else if (past_line) {
curwin->w_set_curswant = true;
if (virtual_active()) {
oneright();
} else {
- curwin->w_cursor.col += utfc_ptr2len(get_cursor_pos_ptr());
+ curwin->w_cursor.col += utfc_ptr2len((char *)get_cursor_pos_ptr());
}
}
}
@@ -4656,11 +4594,9 @@ static void nv_right(cmdarg_T *cap)
}
}
-/*
- * Cursor left commands.
- *
- * Returns true when operator end should not be adjusted.
- */
+/// Cursor left commands.
+///
+/// @return true when operator end should not be adjusted.
static void nv_left(cmdarg_T *cap)
{
long n;
@@ -4676,15 +4612,15 @@ static void nv_left(cmdarg_T *cap)
cap->oap->motion_type = kMTCharWise;
cap->oap->inclusive = false;
- for (n = cap->count1; n > 0; --n) {
+ for (n = cap->count1; n > 0; n--) {
if (oneleft() == false) {
// <BS> and <Del> wrap to previous line if 'whichwrap' has 'b'.
// 'h' wraps to previous line if 'whichwrap' has 'h'.
// CURS_LEFT wraps to previous line if 'whichwrap' has '<'.
if ((((cap->cmdchar == K_BS || cap->cmdchar == Ctrl_H)
- && vim_strchr(p_ww, 'b') != NULL)
- || (cap->cmdchar == 'h' && vim_strchr(p_ww, 'h') != NULL)
- || (cap->cmdchar == K_LEFT && vim_strchr(p_ww, '<') != NULL))
+ && vim_strchr((char *)p_ww, 'b') != NULL)
+ || (cap->cmdchar == 'h' && vim_strchr((char *)p_ww, 'h') != NULL)
+ || (cap->cmdchar == K_LEFT && vim_strchr((char *)p_ww, '<') != NULL))
&& curwin->w_cursor.lnum > 1) {
curwin->w_cursor.lnum--;
coladvance(MAXCOL);
@@ -4699,14 +4635,13 @@ static void nv_left(cmdarg_T *cap)
char_u *cp = get_cursor_pos_ptr();
if (*cp != NUL) {
- curwin->w_cursor.col += utfc_ptr2len(cp);
+ curwin->w_cursor.col += utfc_ptr2len((char *)cp);
}
cap->retval |= CA_NO_ADJ_OP_END;
}
continue;
- }
- // Only beep and flush if not moved at all
- else if (cap->oap->op_type == OP_NOP && n == cap->count1) {
+ } else if (cap->oap->op_type == OP_NOP && n == cap->count1) {
+ // Only beep and flush if not moved at all
beep_flush();
}
break;
@@ -4718,10 +4653,8 @@ static void nv_left(cmdarg_T *cap)
}
}
-/*
- * Cursor up commands.
- * cap->arg is true for "-": Move cursor to first non-blank.
- */
+/// Cursor up commands.
+/// cap->arg is true for "-": Move cursor to first non-blank.
static void nv_up(cmdarg_T *cap)
{
if (mod_mask & MOD_MASK_SHIFT) {
@@ -4738,10 +4671,8 @@ static void nv_up(cmdarg_T *cap)
}
}
-/*
- * Cursor down commands.
- * cap->arg is true for CR and "+": Move cursor to first non-blank.
- */
+/// Cursor down commands.
+/// cap->arg is true for CR and "+": Move cursor to first non-blank.
static void nv_down(cmdarg_T *cap)
{
if (mod_mask & MOD_MASK_SHIFT) {
@@ -4773,17 +4704,13 @@ static void nv_down(cmdarg_T *cap)
}
}
-/*
- * Grab the file name under the cursor and edit it.
- */
+/// Grab the file name under the cursor and edit it.
static void nv_gotofile(cmdarg_T *cap)
{
char_u *ptr;
linenr_T lnum = -1;
- if (text_locked()) {
- clearopbeep(cap->oap);
- text_locked_msg();
+ if (check_text_locked(cap->oap)) {
return;
}
if (curbuf_locked()) {
@@ -4799,7 +4726,7 @@ static void nv_gotofile(cmdarg_T *cap)
(void)autowrite(curbuf, false);
}
setpcmark();
- if (do_ecmd(0, ptr, NULL, NULL, ECMD_LAST,
+ if (do_ecmd(0, (char *)ptr, NULL, NULL, ECMD_LAST,
buf_hide(curbuf) ? ECMD_HIDE : 0, curwin) == OK
&& cap->nchar == 'F' && lnum >= 0) {
curwin->w_cursor.lnum = lnum;
@@ -4812,9 +4739,7 @@ static void nv_gotofile(cmdarg_T *cap)
}
}
-/*
- * <End> command: to end of current line or last line.
- */
+/// <End> command: to end of current line or last line.
static void nv_end(cmdarg_T *cap)
{
if (cap->arg || (mod_mask & MOD_MASK_CTRL)) { // CTRL-END = goto last line
@@ -4825,9 +4750,7 @@ static void nv_end(cmdarg_T *cap)
nv_dollar(cap);
}
-/*
- * Handle the "$" command.
- */
+/// Handle the "$" command.
static void nv_dollar(cmdarg_T *cap)
{
cap->oap->motion_type = kMTCharWise;
@@ -4847,10 +4770,8 @@ static void nv_dollar(cmdarg_T *cap)
}
}
-/*
- * Implementation of '?' and '/' commands.
- * If cap->arg is true don't set PC mark.
- */
+/// Implementation of '?' and '/' commands.
+/// If cap->arg is true don't set PC mark.
static void nv_search(cmdarg_T *cap)
{
oparg_T *oap = cap->oap;
@@ -4878,10 +4799,8 @@ static void nv_search(cmdarg_T *cap)
? 0 : SEARCH_MARK, NULL);
}
-/*
- * Handle "N" and "n" commands.
- * cap->arg is SEARCH_REV for "N", 0 for "n".
- */
+/// Handle "N" and "n" commands.
+/// cap->arg is SEARCH_REV for "N", 0 for "n".
static void nv_next(cmdarg_T *cap)
{
pos_T old = curwin->w_cursor;
@@ -4938,12 +4857,10 @@ static int normal_search(cmdarg_T *cap, int dir, char_u *pat, int opt, int *wrap
return i;
}
-/*
- * Character search commands.
- * cap->arg is BACKWARD for 'F' and 'T', FORWARD for 'f' and 't', true for
- * ',' and false for ';'.
- * cap->nchar is NUL for ',' and ';' (repeat the search)
- */
+/// Character search commands.
+/// cap->arg is BACKWARD for 'F' and 'T', FORWARD for 'f' and 't', true for
+/// ',' and false for ';'.
+/// cap->nchar is NUL for ',' and ';' (repeat the search)
static void nv_csearch(cmdarg_T *cap)
{
bool t_cmd;
@@ -4976,50 +4893,156 @@ static void nv_csearch(cmdarg_T *cap)
}
}
-/*
- * "[" and "]" commands.
- * cap->arg is BACKWARD for "[" and FORWARD for "]".
- */
-static void nv_brackets(cmdarg_T *cap)
+/// "[{", "[(", "]}" or "])": go to Nth unclosed '{', '(', '}' or ')'
+/// "[#", "]#": go to start/end of Nth innermost #if..#endif construct.
+/// "[/", "[*", "]/", "]*": go to Nth comment start/end.
+/// "[m" or "]m" search for prev/next start of (Java) method.
+/// "[M" or "]M" search for prev/next end of (Java) method.
+static void nv_bracket_block(cmdarg_T *cap, const pos_T *old_pos)
{
pos_T new_pos = { 0, 0, 0 };
+ pos_T *pos = NULL; // init for GCC
pos_T prev_pos;
- pos_T *pos = NULL; // init for GCC
- pos_T old_pos; // cursor position before command
- int flag;
long n;
int findc;
int c;
+ if (cap->nchar == '*') {
+ cap->nchar = '/';
+ }
+ prev_pos.lnum = 0;
+ if (cap->nchar == 'm' || cap->nchar == 'M') {
+ if (cap->cmdchar == '[') {
+ findc = '{';
+ } else {
+ findc = '}';
+ }
+ n = 9999;
+ } else {
+ findc = cap->nchar;
+ n = cap->count1;
+ }
+ for (; n > 0; n--) {
+ if ((pos = findmatchlimit(cap->oap, findc,
+ (cap->cmdchar == '[') ? FM_BACKWARD : FM_FORWARD, 0)) == NULL) {
+ if (new_pos.lnum == 0) { // nothing found
+ if (cap->nchar != 'm' && cap->nchar != 'M') {
+ clearopbeep(cap->oap);
+ }
+ } else {
+ pos = &new_pos; // use last one found
+ }
+ break;
+ }
+ prev_pos = new_pos;
+ curwin->w_cursor = *pos;
+ new_pos = *pos;
+ }
+ curwin->w_cursor = *old_pos;
+
+ // Handle "[m", "]m", "[M" and "[M". The findmatchlimit() only
+ // brought us to the match for "[m" and "]M" when inside a method.
+ // Try finding the '{' or '}' we want to be at.
+ // Also repeat for the given count.
+ if (cap->nchar == 'm' || cap->nchar == 'M') {
+ // norm is true for "]M" and "[m"
+ int norm = ((findc == '{') == (cap->nchar == 'm'));
+
+ n = cap->count1;
+ // found a match: we were inside a method
+ if (prev_pos.lnum != 0) {
+ pos = &prev_pos;
+ curwin->w_cursor = prev_pos;
+ if (norm) {
+ n--;
+ }
+ } else {
+ pos = NULL;
+ }
+ while (n > 0) {
+ for (;;) {
+ if ((findc == '{' ? dec_cursor() : inc_cursor()) < 0) {
+ // if not found anything, that's an error
+ if (pos == NULL) {
+ clearopbeep(cap->oap);
+ }
+ n = 0;
+ break;
+ }
+ c = gchar_cursor();
+ if (c == '{' || c == '}') {
+ // Must have found end/start of class: use it.
+ // Or found the place to be at.
+ if ((c == findc && norm) || (n == 1 && !norm)) {
+ new_pos = curwin->w_cursor;
+ pos = &new_pos;
+ n = 0;
+ } else if (new_pos.lnum == 0) {
+ // if no match found at all, we started outside of the
+ // class and we're inside now. Just go on.
+ new_pos = curwin->w_cursor;
+ pos = &new_pos;
+ } else if ((pos = findmatchlimit(cap->oap, findc,
+ (cap->cmdchar == '[') ? FM_BACKWARD : FM_FORWARD,
+ 0)) == NULL) {
+ // found start/end of other method: go to match
+ n = 0;
+ } else {
+ curwin->w_cursor = *pos;
+ }
+ break;
+ }
+ }
+ n--;
+ }
+ curwin->w_cursor = *old_pos;
+ if (pos == NULL && new_pos.lnum != 0) {
+ clearopbeep(cap->oap);
+ }
+ }
+ if (pos != NULL) {
+ setpcmark();
+ curwin->w_cursor = *pos;
+ curwin->w_set_curswant = true;
+ if ((fdo_flags & FDO_BLOCK) && KeyTyped
+ && cap->oap->op_type == OP_NOP) {
+ foldOpenCursor();
+ }
+ }
+}
+
+/// "[" and "]" commands.
+/// cap->arg is BACKWARD for "[" and FORWARD for "]".
+static void nv_brackets(cmdarg_T *cap)
+{
+ pos_T old_pos; // cursor position before command
+ int flag;
+ long n;
+
cap->oap->motion_type = kMTCharWise;
cap->oap->inclusive = false;
old_pos = curwin->w_cursor;
- curwin->w_cursor.coladd = 0; // TODO: don't do this for an error.
+ curwin->w_cursor.coladd = 0; // TODO(Unknown): don't do this for an error.
- /*
- * "[f" or "]f" : Edit file under the cursor (same as "gf")
- */
+ // "[f" or "]f" : Edit file under the cursor (same as "gf")
if (cap->nchar == 'f') {
nv_gotofile(cap);
- } else
- /*
- * Find the occurrence(s) of the identifier or define under cursor
- * in current and included files or jump to the first occurrence.
- *
- * search list jump
- * fwd bwd fwd bwd fwd bwd
- * identifier "]i" "[i" "]I" "[I" "]^I" "[^I"
- * define "]d" "[d" "]D" "[D" "]^D" "[^D"
- */
- if (vim_strchr((char_u *)
- "iI\011dD\004",
- cap->nchar) != NULL) {
+ } else if (vim_strchr("iI\011dD\004", cap->nchar) != NULL) {
+ // Find the occurrence(s) of the identifier or define under cursor
+ // in current and included files or jump to the first occurrence.
+ //
+ // search list jump
+ // fwd bwd fwd bwd fwd bwd
+ // identifier "]i" "[i" "]I" "[I" "]^I" "[^I"
+ // define "]d" "[d" "]D" "[D" "]^D" "[^D"
char_u *ptr;
size_t len;
if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0) {
clearop(cap->oap);
} else {
+ // Make a copy, if the line was changed it will be freed.
+ ptr = vim_strnsave(ptr, len);
find_pattern_in_path(ptr, 0, len, true,
cap->count0 == 0 ? !isupper(cap->nchar) : false,
(((cap->nchar & 0xf) == ('d' & 0xf))
@@ -5033,140 +5056,26 @@ static void nv_brackets(cmdarg_T *cap)
? curwin->w_cursor.lnum + 1
: (linenr_T)1),
MAXLNUM);
+ xfree(ptr);
curwin->w_set_curswant = true;
}
- } else
- /*
- * "[{", "[(", "]}" or "])": go to Nth unclosed '{', '(', '}' or ')'
- * "[#", "]#": go to start/end of Nth innermost #if..#endif construct.
- * "[/", "[*", "]/", "]*": go to Nth comment start/end.
- * "[m" or "]m" search for prev/next start of (Java) method.
- * "[M" or "]M" search for prev/next end of (Java) method.
- */
- if ((cap->cmdchar == '['
- && vim_strchr((char_u *)"{(*/#mM", cap->nchar) != NULL)
- || (cap->cmdchar == ']'
- && vim_strchr((char_u *)"})*/#mM", cap->nchar) != NULL)) {
- if (cap->nchar == '*') {
- cap->nchar = '/';
- }
- prev_pos.lnum = 0;
- if (cap->nchar == 'm' || cap->nchar == 'M') {
- if (cap->cmdchar == '[') {
- findc = '{';
- } else {
- findc = '}';
- }
- n = 9999;
- } else {
- findc = cap->nchar;
- n = cap->count1;
- }
- for (; n > 0; --n) {
- if ((pos = findmatchlimit(cap->oap, findc,
- (cap->cmdchar == '[') ? FM_BACKWARD : FM_FORWARD, 0)) == NULL) {
- if (new_pos.lnum == 0) { // nothing found
- if (cap->nchar != 'm' && cap->nchar != 'M') {
- clearopbeep(cap->oap);
- }
- } else {
- pos = &new_pos; // use last one found
- }
- break;
- }
- prev_pos = new_pos;
- curwin->w_cursor = *pos;
- new_pos = *pos;
- }
- curwin->w_cursor = old_pos;
-
- /*
- * Handle "[m", "]m", "[M" and "[M". The findmatchlimit() only
- * brought us to the match for "[m" and "]M" when inside a method.
- * Try finding the '{' or '}' we want to be at.
- * Also repeat for the given count.
- */
- if (cap->nchar == 'm' || cap->nchar == 'M') {
- // norm is true for "]M" and "[m"
- int norm = ((findc == '{') == (cap->nchar == 'm'));
-
- n = cap->count1;
- // found a match: we were inside a method
- if (prev_pos.lnum != 0) {
- pos = &prev_pos;
- curwin->w_cursor = prev_pos;
- if (norm) {
- --n;
- }
- } else {
- pos = NULL;
- }
- while (n > 0) {
- for (;;) {
- if ((findc == '{' ? dec_cursor() : inc_cursor()) < 0) {
- // if not found anything, that's an error
- if (pos == NULL) {
- clearopbeep(cap->oap);
- }
- n = 0;
- break;
- }
- c = gchar_cursor();
- if (c == '{' || c == '}') {
- // Must have found end/start of class: use it.
- // Or found the place to be at.
- if ((c == findc && norm) || (n == 1 && !norm)) {
- new_pos = curwin->w_cursor;
- pos = &new_pos;
- n = 0;
- } else if (new_pos.lnum == 0) {
- // if no match found at all, we started outside of the
- // class and we're inside now. Just go on.
- new_pos = curwin->w_cursor;
- pos = &new_pos;
- }
- // found start/end of other method: go to match
- else if ((pos = findmatchlimit(cap->oap, findc,
- (cap->cmdchar == '[') ? FM_BACKWARD : FM_FORWARD,
- 0)) == NULL) {
- n = 0;
- } else {
- curwin->w_cursor = *pos;
- }
- break;
- }
- }
- --n;
- }
- curwin->w_cursor = old_pos;
- if (pos == NULL && new_pos.lnum != 0) {
- clearopbeep(cap->oap);
- }
- }
- if (pos != NULL) {
- setpcmark();
- curwin->w_cursor = *pos;
- curwin->w_set_curswant = true;
- if ((fdo_flags & FDO_BLOCK) && KeyTyped
- && cap->oap->op_type == OP_NOP) {
- foldOpenCursor();
- }
- }
- }
- /*
- * "[[", "[]", "]]" and "][": move to start or end of function
- */
- else if (cap->nchar == '[' || cap->nchar == ']') {
+ } else if ((cap->cmdchar == '[' && vim_strchr("{(*/#mM", cap->nchar) != NULL)
+ || (cap->cmdchar == ']' && vim_strchr("})*/#mM", cap->nchar) != NULL)) {
+ // "[{", "[(", "]}" or "])": go to Nth unclosed '{', '(', '}' or ')'
+ // "[#", "]#": go to start/end of Nth innermost #if..#endif construct.
+ // "[/", "[*", "]/", "]*": go to Nth comment start/end.
+ // "[m" or "]m" search for prev/next start of (Java) method.
+ // "[M" or "]M" search for prev/next end of (Java) method.
+ nv_bracket_block(cap, &old_pos);
+ } else if (cap->nchar == '[' || cap->nchar == ']') {
+ // "[[", "[]", "]]" and "][": move to start or end of function
if (cap->nchar == cap->cmdchar) { // "]]" or "[["
flag = '{';
} else {
flag = '}'; // "][" or "[]"
}
curwin->w_set_curswant = true;
- /*
- * Imitate strange Vi behaviour: When using "]]" with an operator
- * we also stop at '}'.
- */
+ // Imitate strange Vi behaviour: When using "]]" with an operator we also stop at '}'.
if (!findpar(&cap->oap->inclusive, cap->arg, cap->count1, flag,
(cap->oap->op_type != OP_NOP
&& cap->arg == FORWARD && flag == '{'))) {
@@ -5182,58 +5091,46 @@ static void nv_brackets(cmdarg_T *cap)
} else if (cap->nchar == 'p' || cap->nchar == 'P') {
// "[p", "[P", "]P" and "]p": put with indent adjustment
nv_put_opt(cap, true);
- }
- /*
- * "['", "[`", "]'" and "]`": jump to next mark
- */
- else if (cap->nchar == '\'' || cap->nchar == '`') {
- pos = &curwin->w_cursor;
- for (n = cap->count1; n > 0; --n) {
- prev_pos = *pos;
- pos = getnextmark(pos, cap->cmdchar == '[' ? BACKWARD : FORWARD,
- cap->nchar == '\'');
- if (pos == NULL) {
+ } else if (cap->nchar == '\'' || cap->nchar == '`') {
+ // "['", "[`", "]'" and "]`": jump to next mark
+ fmark_T *fm = pos_to_mark(curbuf, NULL, curwin->w_cursor);
+ fmark_T *prev_fm;
+ for (n = cap->count1; n > 0; n--) {
+ prev_fm = fm;
+ fm = getnextmark(&fm->mark, cap->cmdchar == '[' ? BACKWARD : FORWARD,
+ cap->nchar == '\'');
+ if (fm == NULL) {
break;
}
}
- if (pos == NULL) {
- pos = &prev_pos;
+ if (fm == NULL) {
+ fm = prev_fm;
}
- nv_cursormark(cap, cap->nchar == '\'', pos);
- }
- /*
- * [ or ] followed by a middle mouse click: put selected text with
- * indent adjustment. Any other button just does as usual.
- */
- else if (cap->nchar >= K_RIGHTRELEASE && cap->nchar <= K_LEFTMOUSE) {
+ MarkMove flags = kMarkContext;
+ flags |= cap->nchar == '\'' ? kMarkBeginLine: 0;
+ nv_mark_move_to(cap, flags, fm);
+ } else if (cap->nchar >= K_RIGHTRELEASE && cap->nchar <= K_LEFTMOUSE) {
+ // [ or ] followed by a middle mouse click: put selected text with
+ // indent adjustment. Any other button just does as usual.
(void)do_mouse(cap->oap, cap->nchar,
(cap->cmdchar == ']') ? FORWARD : BACKWARD,
cap->count1, PUT_FIXINDENT);
- }
- /*
- * "[z" and "]z": move to start or end of open fold.
- */
- else if (cap->nchar == 'z') {
+ } else if (cap->nchar == 'z') {
+ // "[z" and "]z": move to start or end of open fold.
if (foldMoveTo(false, cap->cmdchar == ']' ? FORWARD : BACKWARD,
cap->count1) == false) {
clearopbeep(cap->oap);
}
- }
- /*
- * "[c" and "]c": move to next or previous diff-change.
- */
- else if (cap->nchar == 'c') {
+ } else if (cap->nchar == 'c') {
+ // "[c" and "]c": move to next or previous diff-change.
if (diff_move_to(cap->cmdchar == ']' ? FORWARD : BACKWARD,
cap->count1) == false) {
clearopbeep(cap->oap);
}
- }
- /*
- * "[s", "[S", "]s" and "]S": move to next spell error.
- */
- else if (cap->nchar == 's' || cap->nchar == 'S') {
+ } else if (cap->nchar == 's' || cap->nchar == 'S') {
+ // "[s", "[S", "]s" and "]S": move to next spell error.
setpcmark();
- for (n = 0; n < cap->count1; ++n) {
+ for (n = 0; n < cap->count1; n++) {
if (spell_move_to(curwin, cap->cmdchar == ']' ? FORWARD : BACKWARD,
cap->nchar == 's', false, NULL) == 0) {
clearopbeep(cap->oap);
@@ -5245,16 +5142,13 @@ static void nv_brackets(cmdarg_T *cap)
if (cap->oap->op_type == OP_NOP && (fdo_flags & FDO_SEARCH) && KeyTyped) {
foldOpenCursor();
}
- }
- // Not a valid cap->nchar.
- else {
+ } else {
+ // Not a valid cap->nchar.
clearopbeep(cap->oap);
}
}
-/*
- * Handle Normal mode "%" command.
- */
+/// Handle Normal mode "%" command.
static void nv_percent(cmdarg_T *cap)
{
pos_T *pos;
@@ -5272,11 +5166,11 @@ static void nv_percent(cmdarg_T *cap)
// overflow on 32-bits, so use a formula with less accuracy
// to avoid overflows.
if (curbuf->b_ml.ml_line_count >= 21474836) {
- curwin->w_cursor.lnum = (curbuf->b_ml.ml_line_count + 99L)
- / 100L * cap->count0;
+ curwin->w_cursor.lnum = (curbuf->b_ml.ml_line_count + 99)
+ / 100 * (linenr_T)cap->count0;
} else {
curwin->w_cursor.lnum = (curbuf->b_ml.ml_line_count *
- cap->count0 + 99L) / 100L;
+ (linenr_T)cap->count0 + 99) / 100;
}
if (curwin->w_cursor.lnum < 1) {
curwin->w_cursor.lnum = 1;
@@ -5307,10 +5201,8 @@ static void nv_percent(cmdarg_T *cap)
}
}
-/*
- * Handle "(" and ")" commands.
- * cap->arg is BACKWARD for "(" and FORWARD for ")".
- */
+/// Handle "(" and ")" commands.
+/// cap->arg is BACKWARD for "(" and FORWARD for ")".
static void nv_brace(cmdarg_T *cap)
{
cap->oap->motion_type = kMTCharWise;
@@ -5331,9 +5223,7 @@ static void nv_brace(cmdarg_T *cap)
}
}
-/*
- * "m" command: Mark a position.
- */
+/// "m" command: Mark a position.
static void nv_mark(cmdarg_T *cap)
{
if (!checkclearop(cap->oap)) {
@@ -5343,10 +5233,8 @@ static void nv_mark(cmdarg_T *cap)
}
}
-/*
- * "{" and "}" commands.
- * cmd->arg is BACKWARD for "{" and FORWARD for "}".
- */
+/// "{" and "}" commands.
+/// cmd->arg is BACKWARD for "{" and FORWARD for "}".
static void nv_findpar(cmdarg_T *cap)
{
cap->oap->motion_type = kMTCharWise;
@@ -5363,9 +5251,7 @@ static void nv_findpar(cmdarg_T *cap)
}
}
-/*
- * "u" command: Undo or make lower case.
- */
+/// "u" command: Undo or make lower case.
static void nv_undo(cmdarg_T *cap)
{
if (cap->oap->op_type == OP_LOWER
@@ -5379,9 +5265,7 @@ static void nv_undo(cmdarg_T *cap)
}
}
-/*
- * <Undo> command.
- */
+/// <Undo> command.
static void nv_kundo(cmdarg_T *cap)
{
if (!checkclearopq(cap->oap)) {
@@ -5394,9 +5278,7 @@ static void nv_kundo(cmdarg_T *cap)
}
}
-/*
- * Handle the "r" command.
- */
+/// Handle the "r" command.
static void nv_replace(cmdarg_T *cap)
{
char_u *ptr;
@@ -5413,7 +5295,7 @@ static void nv_replace(cmdarg_T *cap)
// get another character
if (cap->nchar == Ctrl_V) {
had_ctrl_v = Ctrl_V;
- cap->nchar = get_literal();
+ cap->nchar = get_literal(false);
// Don't redo a multibyte character with CTRL-V.
if (cap->nchar > DEL) {
had_ctrl_v = NUL;
@@ -5487,14 +5369,12 @@ static void nv_replace(cmdarg_T *cap)
}
if (had_ctrl_v != Ctrl_V && (cap->nchar == '\r' || cap->nchar == '\n')) {
- /*
- * Replace character(s) by a single newline.
- * Strange vi behaviour: Only one newline is inserted.
- * Delete the characters here.
- * Insert the newline with an insert command, takes care of
- * autoindent. The insert command depends on being on the last
- * character of a line or not.
- */
+ // Replace character(s) by a single newline.
+ // Strange vi behaviour: Only one newline is inserted.
+ // Delete the characters here.
+ // Insert the newline with an insert command, takes care of
+ // autoindent. The insert command depends on being on the last
+ // character of a line or not.
(void)del_chars(cap->count1, false); // delete the characters
stuffcharReadbuff('\r');
stuffcharReadbuff(ESC);
@@ -5519,7 +5399,7 @@ static void nv_replace(cmdarg_T *cap)
// multi-byte and the other way around. Also handles adding
// composing characters for utf-8.
for (long n = cap->count1; n > 0; n--) {
- State = REPLACE;
+ State = MODE_REPLACE;
if (cap->nchar == Ctrl_E || cap->nchar == Ctrl_Y) {
int c = ins_copychar(curwin->w_cursor.lnum
+ (cap->nchar == Ctrl_Y ? -1 : 1));
@@ -5552,10 +5432,8 @@ static void nv_replace(cmdarg_T *cap)
foldUpdateAfterInsert();
}
-/*
- * 'o': Exchange start and end of Visual area.
- * 'O': same, but in block mode exchange left and right corners.
- */
+/// 'o': Exchange start and end of Visual area.
+/// 'O': same, but in block mode exchange left and right corners.
static void v_swap_corners(int cmdchar)
{
pos_T old_cursor;
@@ -5573,7 +5451,7 @@ static void v_swap_corners(int cmdchar)
// 'selection "exclusive" and cursor at right-bottom corner: move it
// right one column
if (old_cursor.lnum >= VIsual.lnum && *p_sel == 'e') {
- ++curwin->w_curswant;
+ curwin->w_curswant++;
}
coladvance(curwin->w_curswant);
if (curwin->w_cursor.col == old_cursor.col
@@ -5582,7 +5460,7 @@ static void v_swap_corners(int cmdchar)
old_cursor.coladd)) {
curwin->w_cursor.lnum = VIsual.lnum;
if (old_cursor.lnum <= VIsual.lnum && *p_sel == 'e') {
- ++right;
+ right++;
}
coladvance(right);
VIsual = curwin->w_cursor;
@@ -5599,9 +5477,7 @@ static void v_swap_corners(int cmdchar)
}
}
-/*
- * "R" (cap->arg is false) and "gR" (cap->arg is true).
- */
+/// "R" (cap->arg is false) and "gR" (cap->arg is true).
static void nv_Replace(cmdarg_T *cap)
{
if (VIsual_active) { // "R" is replace lines
@@ -5622,9 +5498,7 @@ static void nv_Replace(cmdarg_T *cap)
}
}
-/*
- * "gr".
- */
+/// "gr".
static void nv_vreplace(cmdarg_T *cap)
{
if (VIsual_active) {
@@ -5636,7 +5510,7 @@ static void nv_vreplace(cmdarg_T *cap)
emsg(_(e_modifiable));
} else {
if (cap->extra_char == Ctrl_V) { // get another character
- cap->extra_char = get_literal();
+ cap->extra_char = get_literal(false);
}
stuffcharReadbuff(cap->extra_char);
stuffcharReadbuff(ESC);
@@ -5648,9 +5522,7 @@ static void nv_vreplace(cmdarg_T *cap)
}
}
-/*
- * Swap case for "~" command, when it does not work like an operator.
- */
+/// Swap case for "~" command, when it does not work like an operator.
static void n_swapchar(cmdarg_T *cap)
{
long n;
@@ -5661,7 +5533,7 @@ static void n_swapchar(cmdarg_T *cap)
return;
}
- if (LINEEMPTY(curwin->w_cursor.lnum) && vim_strchr(p_ww, '~') == NULL) {
+ if (LINEEMPTY(curwin->w_cursor.lnum) && vim_strchr((char *)p_ww, '~') == NULL) {
clearopbeep(cap->oap);
return;
}
@@ -5673,13 +5545,13 @@ static void n_swapchar(cmdarg_T *cap)
}
startpos = curwin->w_cursor;
- for (n = cap->count1; n > 0; --n) {
+ for (n = cap->count1; n > 0; n--) {
did_change |= swapchar(cap->oap->op_type, &curwin->w_cursor);
inc_cursor();
if (gchar_cursor() == NUL) {
- if (vim_strchr(p_ww, '~') != NULL
+ if (vim_strchr((char *)p_ww, '~') != NULL
&& curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
- ++curwin->w_cursor.lnum;
+ curwin->w_cursor.lnum++;
curwin->w_cursor.col = 0;
if (n > 1) {
if (u_savesub(curwin->w_cursor.lnum) == false) {
@@ -5693,7 +5565,6 @@ static void n_swapchar(cmdarg_T *cap)
}
}
-
check_cursor();
curwin->w_set_curswant = true;
if (did_change) {
@@ -5702,43 +5573,36 @@ static void n_swapchar(cmdarg_T *cap)
curbuf->b_op_start = startpos;
curbuf->b_op_end = curwin->w_cursor;
if (curbuf->b_op_end.col > 0) {
- --curbuf->b_op_end.col;
+ curbuf->b_op_end.col--;
}
}
}
-/*
- * Move cursor to mark.
- */
-static void nv_cursormark(cmdarg_T *cap, int flag, pos_T *pos)
-{
- if (check_mark(pos) == false) {
+/// Move the cursor to the mark position
+///
+/// Wrapper to mark_move_to() that also handles normal mode command arguments.
+/// @note It will switch the buffer if neccesarry, move the cursor and set the
+/// view depending on the given flags.
+/// @param cap command line arguments
+/// @param flags for mark_move_to()
+/// @param mark mark
+/// @return The result of calling mark_move_to()
+static MarkMoveRes nv_mark_move_to(cmdarg_T *cap, MarkMove flags, fmark_T *fm)
+{
+ MarkMoveRes res = mark_move_to(fm, flags);
+ if (res & kMarkMoveFailed) {
clearop(cap->oap);
- } else {
- if (cap->cmdchar == '\''
- || cap->cmdchar == '`'
- || cap->cmdchar == '['
- || cap->cmdchar == ']') {
- setpcmark();
- }
- curwin->w_cursor = *pos;
- if (flag) {
- beginline(BL_WHITE | BL_FIX);
- } else {
- check_cursor();
- }
}
- cap->oap->motion_type = flag ? kMTLineWise : kMTCharWise;
+ cap->oap->motion_type = flags & kMarkBeginLine ? kMTLineWise : kMTCharWise;
if (cap->cmdchar == '`') {
cap->oap->use_reg_one = true;
}
cap->oap->inclusive = false; // ignored if not kMTCharWise
curwin->w_set_curswant = true;
+ return res;
}
-/*
- * Handle commands that are operators in Visual mode.
- */
+/// Handle commands that are operators in Visual mode.
static void v_visop(cmdarg_T *cap)
{
static char_u trans[] = "YyDdCcxdXdAAIIrr";
@@ -5753,13 +5617,11 @@ static void v_visop(cmdarg_T *cap)
curwin->w_curswant = MAXCOL;
}
}
- cap->cmdchar = *(vim_strchr(trans, cap->cmdchar) + 1);
+ cap->cmdchar = (uint8_t)(*(vim_strchr((char *)trans, cap->cmdchar) + 1));
nv_operator(cap);
}
-/*
- * "s" and "S" commands.
- */
+/// "s" and "S" commands.
static void nv_subst(cmdarg_T *cap)
{
if (bt_prompt(curbuf) && !prompt_curpos_editable()) {
@@ -5778,9 +5640,7 @@ static void nv_subst(cmdarg_T *cap)
}
}
-/*
- * Abbreviated commands.
- */
+/// Abbreviated commands.
static void nv_abbrev(cmdarg_T *cap)
{
if (cap->cmdchar == K_DEL || cap->cmdchar == K_KDEL) {
@@ -5794,9 +5654,7 @@ static void nv_abbrev(cmdarg_T *cap)
}
}
-/*
- * Translate a command into another command.
- */
+/// Translate a command into another command.
static void nv_optrans(cmdarg_T *cap)
{
static const char *(ar[]) = { "dl", "dh", "d$", "c$", "cl", "cc", "yy",
@@ -5812,70 +5670,70 @@ static void nv_optrans(cmdarg_T *cap)
cap->opcount = 0;
}
-/*
- * "'" and "`" commands. Also for "g'" and "g`".
- * cap->arg is true for "'" and "g'".
- */
+/// "'" and "`" commands. Also for "g'" and "g`".
+/// cap->arg is true for "'" and "g'".
static void nv_gomark(cmdarg_T *cap)
{
- pos_T *pos;
- int c;
- pos_T old_cursor = curwin->w_cursor;
- const bool old_KeyTyped = KeyTyped; // getting file may reset it
+ int name;
+ MarkMove flags = jop_flags & JOP_VIEW ? kMarkSetView : 0; // flags for moving to the mark
+ MarkMoveRes move_res = 0; // Result from moving to the mark
+ const bool old_KeyTyped = KeyTyped; // getting file may reset it
if (cap->cmdchar == 'g') {
- c = cap->extra_char;
+ name = cap->extra_char;
+ flags |= KMarkNoContext;
} else {
- c = cap->nchar;
- }
- pos = getmark(c, (cap->oap->op_type == OP_NOP));
- if (pos == (pos_T *)-1) { // jumped to other file
- if (cap->arg) {
- check_cursor_lnum();
- beginline(BL_WHITE | BL_FIX);
- } else {
- check_cursor();
- }
- } else {
- nv_cursormark(cap, cap->arg, pos);
+ name = cap->nchar;
+ flags |= kMarkContext;
}
+ flags |= cap->arg ? kMarkBeginLine : 0;
+ flags |= cap->count0 ? kMarkSetView : 0;
+
+ fmark_T *fm = mark_get(curbuf, curwin, NULL, kMarkAll, name);
+ move_res = nv_mark_move_to(cap, flags, fm);
// May need to clear the coladd that a mark includes.
if (!virtual_active()) {
curwin->w_cursor.coladd = 0;
}
- check_cursor_col();
+
if (cap->oap->op_type == OP_NOP
- && pos != NULL
- && (pos == (pos_T *)-1 || !equalpos(old_cursor, *pos))
+ && move_res & kMarkMoveSuccess
+ && (move_res & kMarkSwitchedBuf || move_res & kMarkChangedCursor)
&& (fdo_flags & FDO_MARK)
&& old_KeyTyped) {
foldOpenCursor();
}
}
-// Handle CTRL-O, CTRL-I, "g;", "g,", and "CTRL-Tab" commands.
+/// Handle CTRL-O, CTRL-I, "g;", "g,", and "CTRL-Tab" commands.
+/// Movement in the jumplist and changelist.
static void nv_pcmark(cmdarg_T *cap)
{
- pos_T *pos;
- linenr_T lnum = curwin->w_cursor.lnum;
- const bool old_KeyTyped = KeyTyped; // getting file may reset it
+ fmark_T *fm = NULL;
+ MarkMove flags = jop_flags & JOP_VIEW ? kMarkSetView : 0; // flags for moving to the mark
+ MarkMoveRes move_res = 0; // Result from moving to the mark
+ const bool old_KeyTyped = KeyTyped; // getting file may reset it.
if (!checkclearopq(cap->oap)) {
if (cap->cmdchar == TAB && mod_mask == MOD_MASK_CTRL) {
- goto_tabpage_lastused();
+ if (!goto_tabpage_lastused()) {
+ clearopbeep(cap->oap);
+ }
return;
}
+
if (cap->cmdchar == 'g') {
- pos = movechangelist((int)cap->count1);
+ fm = get_changelist(curbuf, curwin, (int)cap->count1);
} else {
- pos = movemark((int)cap->count1);
- }
- if (pos == (pos_T *)-1) { // jump to other file
- curwin->w_set_curswant = true;
- check_cursor();
- } else if (pos != NULL) { // can jump
- nv_cursormark(cap, false, pos);
+ fm = get_jumplist(curwin, (int)cap->count1);
+ flags |= KMarkNoContext | kMarkJumpList;
+ }
+ // Changelist and jumplist have their own error messages. Therefore avoid
+ // calling nv_mark_move_to() when not found to avoid incorrect error
+ // messages.
+ if (fm != NULL) {
+ move_res = nv_mark_move_to(cap, flags, fm);
} else if (cap->cmdchar == 'g') {
if (curbuf->b_changelistlen == 0) {
emsg(_("E664: changelist is empty"));
@@ -5888,7 +5746,7 @@ static void nv_pcmark(cmdarg_T *cap)
clearopbeep(cap->oap);
}
if (cap->oap->op_type == OP_NOP
- && (pos == (pos_T *)-1 || lnum != curwin->w_cursor.lnum)
+ && (move_res & kMarkSwitchedBuf || move_res & kMarkChangedLine)
&& (fdo_flags & FDO_MARK)
&& old_KeyTyped) {
foldOpenCursor();
@@ -5896,9 +5754,7 @@ static void nv_pcmark(cmdarg_T *cap)
}
}
-/*
- * Handle '"' command.
- */
+/// Handle '"' command.
static void nv_regname(cmdarg_T *cap)
{
if (checkclearop(cap->oap)) {
@@ -5916,12 +5772,10 @@ static void nv_regname(cmdarg_T *cap)
}
}
-/*
- * Handle "v", "V" and "CTRL-V" commands.
- * Also for "gh", "gH" and "g^H" commands: Always start Select mode, cap->arg
- * is true.
- * Handle CTRL-Q just like CTRL-V.
- */
+/// Handle "v", "V" and "CTRL-V" commands.
+/// Also for "gh", "gH" and "g^H" commands: Always start Select mode, cap->arg
+/// is true.
+/// Handle CTRL-Q just like CTRL-V.
static void nv_visual(cmdarg_T *cap)
{
if (cap->cmdchar == Ctrl_Q) {
@@ -5944,7 +5798,7 @@ static void nv_visual(cmdarg_T *cap)
// or char/line mode
VIsual_mode = cap->cmdchar;
showmode();
- trigger_modechanged();
+ may_trigger_modechanged();
}
redraw_curbuf_later(INVERTED); // update the inversion
} else { // start Visual mode
@@ -5962,16 +5816,11 @@ static void nv_visual(cmdarg_T *cap)
if (p_smd && msg_silent == 0) {
redraw_cmdline = true; // show visual mode later
}
- /*
- * For V and ^V, we multiply the number of lines even if there
- * was only one -- webb
- */
+ // For V and ^V, we multiply the number of lines even if there
+ // 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 * (linenr_T)cap->count0 - 1;
+ check_cursor();
}
VIsual_mode = resel_VIsual_mode;
if (VIsual_mode == 'v') {
@@ -6005,7 +5854,7 @@ static void nv_visual(cmdarg_T *cap)
}
n_start_visual_mode(cap->cmdchar);
if (VIsual_mode != 'V' && *p_sel == 'e') {
- ++cap->count1; // include one more char
+ cap->count1++; // include one more char
}
if (cap->count0 > 0 && --cap->count1 > 0) {
// With a count select that many characters or lines.
@@ -6019,9 +5868,7 @@ static void nv_visual(cmdarg_T *cap)
}
}
-/*
- * Start selection for Shift-movement keys.
- */
+/// Start selection for Shift-movement keys.
void start_selection(void)
{
// if 'selectmode' contains "key", start Select mode
@@ -6029,19 +5876,16 @@ void start_selection(void)
n_start_visual_mode('v');
}
-/*
- * Start Select mode, if "c" is in 'selectmode' and not in a mapping or menu.
- */
+/// Start Select mode, if "c" is in 'selectmode' and not in a mapping or menu.
+/// When "c" is 'o' (checking for "mouse") then also when mapped.
void may_start_select(int c)
{
- VIsual_select = (stuff_empty() && typebuf_typed()
- && (vim_strchr(p_slm, c) != NULL));
+ VIsual_select = (c == 'o' || (stuff_empty() && typebuf_typed()))
+ && vim_strchr((char *)p_slm, c) != NULL;
}
-/*
- * Start Visual mode "c".
- * Should set VIsual_select before calling this.
- */
+/// Start Visual mode "c".
+/// Should set VIsual_select before calling this.
static void n_start_visual_mode(int c)
{
VIsual_mode = c;
@@ -6050,7 +5894,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);
}
@@ -6058,7 +5902,7 @@ static void n_start_visual_mode(int c)
foldAdjustVisual();
- trigger_modechanged();
+ may_trigger_modechanged();
setmouse();
// Check for redraw after changing the state.
conceal_check_cursor_line();
@@ -6074,10 +5918,7 @@ static void n_start_visual_mode(int c)
}
}
-
-/*
- * CTRL-W: Window commands
- */
+/// CTRL-W: Window commands
static void nv_window(cmdarg_T *cap)
{
if (cap->nchar == ':') {
@@ -6090,9 +5931,7 @@ static void nv_window(cmdarg_T *cap)
}
}
-/*
- * CTRL-Z: Suspend
- */
+/// CTRL-Z: Suspend
static void nv_suspend(cmdarg_T *cap)
{
clearop(cap->oap);
@@ -6102,15 +5941,217 @@ static void nv_suspend(cmdarg_T *cap)
do_cmdline_cmd("st");
}
-/*
- * Commands starting with "g".
- */
+/// "gv": Reselect the previous Visual area. If Visual already active,
+/// exchange previous and current Visual area.
+static void nv_gv_cmd(cmdarg_T *cap)
+{
+ if (checkclearop(cap->oap)) {
+ return;
+ }
+
+ if (curbuf->b_visual.vi_start.lnum == 0
+ || curbuf->b_visual.vi_start.lnum > curbuf->b_ml.ml_line_count
+ || curbuf->b_visual.vi_end.lnum == 0) {
+ beep_flush();
+ return;
+ }
+
+ pos_T tpos;
+ // set w_cursor to the start of the Visual area, tpos to the end
+ if (VIsual_active) {
+ int i = VIsual_mode;
+ VIsual_mode = curbuf->b_visual.vi_mode;
+ curbuf->b_visual.vi_mode = i;
+ curbuf->b_visual_mode_eval = i;
+ i = curwin->w_curswant;
+ curwin->w_curswant = curbuf->b_visual.vi_curswant;
+ curbuf->b_visual.vi_curswant = i;
+
+ tpos = curbuf->b_visual.vi_end;
+ curbuf->b_visual.vi_end = curwin->w_cursor;
+ curwin->w_cursor = curbuf->b_visual.vi_start;
+ curbuf->b_visual.vi_start = VIsual;
+ } else {
+ VIsual_mode = curbuf->b_visual.vi_mode;
+ curwin->w_curswant = curbuf->b_visual.vi_curswant;
+ tpos = curbuf->b_visual.vi_end;
+ curwin->w_cursor = curbuf->b_visual.vi_start;
+ }
+
+ VIsual_active = true;
+ VIsual_reselect = true;
+
+ // Set Visual to the start and w_cursor to the end of the Visual
+ // area. Make sure they are on an existing character.
+ check_cursor();
+ VIsual = curwin->w_cursor;
+ curwin->w_cursor = tpos;
+ check_cursor();
+ update_topline(curwin);
+
+ // When called from normal "g" command: start Select mode when
+ // 'selectmode' contains "cmd". When called for K_SELECT, always
+ // start Select mode.
+ if (cap->arg) {
+ VIsual_select = true;
+ VIsual_select_reg = 0;
+ } else {
+ may_start_select('c');
+ }
+ setmouse();
+ redraw_curbuf_later(INVERTED);
+ showmode();
+}
+
+/// "g0", "g^" : Like "0" and "^" but for screen lines.
+/// "gm": middle of "g0" and "g$".
+static void nv_g_home_m_cmd(cmdarg_T *cap)
+{
+ int i;
+ const bool flag = cap->nchar == '^';
+
+ cap->oap->motion_type = kMTCharWise;
+ cap->oap->inclusive = false;
+ if (curwin->w_p_wrap && curwin->w_width_inner != 0) {
+ int width1 = curwin->w_width_inner - curwin_col_off();
+ int width2 = width1 + curwin_col_off2();
+
+ validate_virtcol();
+ i = 0;
+ if (curwin->w_virtcol >= (colnr_T)width1 && width2 > 0) {
+ i = (curwin->w_virtcol - width1) / width2 * width2 + width1;
+ }
+ } else {
+ i = curwin->w_leftcol;
+ }
+ // Go to the middle of the screen line. When 'number' or
+ // 'relativenumber' is on and lines are wrapping the middle can be more
+ // to the left.
+ if (cap->nchar == 'm') {
+ i += (curwin->w_width_inner - curwin_col_off()
+ + ((curwin->w_p_wrap && i > 0) ? curwin_col_off2() : 0)) / 2;
+ }
+ coladvance((colnr_T)i);
+ if (flag) {
+ do {
+ i = gchar_cursor();
+ } while (ascii_iswhite(i) && oneright());
+ curwin->w_valid &= ~VALID_WCOL;
+ }
+ curwin->w_set_curswant = true;
+}
+
+/// "g_": to the last non-blank character in the line or <count> lines downward.
+static void nv_g_underscore_cmd(cmdarg_T *cap)
+{
+ cap->oap->motion_type = kMTCharWise;
+ cap->oap->inclusive = true;
+ curwin->w_curswant = MAXCOL;
+ if (cursor_down(cap->count1 - 1, cap->oap->op_type == OP_NOP) == false) {
+ clearopbeep(cap->oap);
+ return;
+ }
+
+ char_u *ptr = get_cursor_line_ptr();
+
+ // In Visual mode we may end up after the line.
+ if (curwin->w_cursor.col > 0 && ptr[curwin->w_cursor.col] == NUL) {
+ curwin->w_cursor.col--;
+ }
+
+ // Decrease the cursor column until it's on a non-blank.
+ while (curwin->w_cursor.col > 0 && ascii_iswhite(ptr[curwin->w_cursor.col])) {
+ curwin->w_cursor.col--;
+ }
+ curwin->w_set_curswant = true;
+ adjust_for_sel(cap);
+}
+
+/// "g$" : Like "$" but for screen lines.
+static void nv_g_dollar_cmd(cmdarg_T *cap)
+{
+ oparg_T *oap = cap->oap;
+ int i;
+ int col_off = curwin_col_off();
+
+ oap->motion_type = kMTCharWise;
+ oap->inclusive = true;
+ if (curwin->w_p_wrap && curwin->w_width_inner != 0) {
+ curwin->w_curswant = MAXCOL; // so we stay at the end
+ if (cap->count1 == 1) {
+ int width1 = curwin->w_width_inner - col_off;
+ int width2 = width1 + curwin_col_off2();
+
+ validate_virtcol();
+ i = width1 - 1;
+ if (curwin->w_virtcol >= (colnr_T)width1) {
+ i += ((curwin->w_virtcol - width1) / width2 + 1) * width2;
+ }
+ coladvance((colnr_T)i);
+
+ // Make sure we stick in this column.
+ validate_virtcol();
+ curwin->w_curswant = curwin->w_virtcol;
+ curwin->w_set_curswant = false;
+ if (curwin->w_cursor.col > 0 && curwin->w_p_wrap) {
+ // Check for landing on a character that got split at
+ // the end of the line. We do not want to advance to
+ // the next screen line.
+ if (curwin->w_virtcol > (colnr_T)i) {
+ curwin->w_cursor.col--;
+ }
+ }
+ } else if (nv_screengo(oap, FORWARD, cap->count1 - 1) == false) {
+ clearopbeep(oap);
+ }
+ } else {
+ if (cap->count1 > 1) {
+ // if it fails, let the cursor still move to the last char
+ (void)cursor_down(cap->count1 - 1, false);
+ }
+ i = curwin->w_leftcol + curwin->w_width_inner - col_off - 1;
+ coladvance((colnr_T)i);
+
+ // if the character doesn't fit move one back
+ if (curwin->w_cursor.col > 0 && utf_ptr2cells((const char *)get_cursor_pos_ptr()) > 1) {
+ colnr_T vcol;
+
+ getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &vcol);
+ if (vcol >= curwin->w_leftcol + curwin->w_width - col_off) {
+ curwin->w_cursor.col--;
+ }
+ }
+
+ // Make sure we stick in this column.
+ validate_virtcol();
+ curwin->w_curswant = curwin->w_virtcol;
+ curwin->w_set_curswant = false;
+ }
+}
+
+/// "gi": start Insert at the last position.
+static void nv_gi_cmd(cmdarg_T *cap)
+{
+ if (curbuf->b_last_insert.mark.lnum != 0) {
+ curwin->w_cursor = curbuf->b_last_insert.mark;
+ check_cursor_lnum();
+ int i = (int)STRLEN(get_cursor_line_ptr());
+ if (curwin->w_cursor.col > (colnr_T)i) {
+ if (virtual_active()) {
+ curwin->w_cursor.coladd += curwin->w_cursor.col - i;
+ }
+ curwin->w_cursor.col = i;
+ }
+ }
+ cap->cmdchar = 'i';
+ nv_edit(cap);
+}
+
+/// Commands starting with "g".
static void nv_g_cmd(cmdarg_T *cap)
{
oparg_T *oap = cap->oap;
- pos_T tpos;
int i;
- bool flag = false;
switch (cap->nchar) {
// "g^A/g^X": Sequentially increment visually selected region.
@@ -6140,77 +6181,19 @@ static void nv_g_cmd(cmdarg_T *cap)
do_cmdline_cmd("%s//~/&");
break;
- /*
- * "gv": Reselect the previous Visual area. If Visual already active,
- * exchange previous and current Visual area.
- */
+ // "gv": Reselect the previous Visual area. If Visual already active,
+ // exchange previous and current Visual area.
case 'v':
- if (checkclearop(oap)) {
- break;
- }
-
- if (curbuf->b_visual.vi_start.lnum == 0
- || curbuf->b_visual.vi_start.lnum > curbuf->b_ml.ml_line_count
- || curbuf->b_visual.vi_end.lnum == 0) {
- beep_flush();
- } else {
- // set w_cursor to the start of the Visual area, tpos to the end
- if (VIsual_active) {
- i = VIsual_mode;
- VIsual_mode = curbuf->b_visual.vi_mode;
- curbuf->b_visual.vi_mode = i;
- curbuf->b_visual_mode_eval = i;
- i = curwin->w_curswant;
- curwin->w_curswant = curbuf->b_visual.vi_curswant;
- curbuf->b_visual.vi_curswant = i;
-
- tpos = curbuf->b_visual.vi_end;
- curbuf->b_visual.vi_end = curwin->w_cursor;
- curwin->w_cursor = curbuf->b_visual.vi_start;
- curbuf->b_visual.vi_start = VIsual;
- } else {
- VIsual_mode = curbuf->b_visual.vi_mode;
- curwin->w_curswant = curbuf->b_visual.vi_curswant;
- tpos = curbuf->b_visual.vi_end;
- curwin->w_cursor = curbuf->b_visual.vi_start;
- }
-
- VIsual_active = true;
- VIsual_reselect = true;
-
- // Set Visual to the start and w_cursor to the end of the Visual
- // area. Make sure they are on an existing character.
- check_cursor();
- VIsual = curwin->w_cursor;
- curwin->w_cursor = tpos;
- check_cursor();
- update_topline(curwin);
- // When called from normal "g" command: start Select mode when
- // 'selectmode' contains "cmd". When called for K_SELECT, always
- // start Select mode.
- if (cap->arg) {
- VIsual_select = true;
- } else {
- may_start_select('c');
- }
- setmouse();
- redraw_curbuf_later(INVERTED);
- showmode();
- }
+ nv_gv_cmd(cap);
break;
- /*
- * "gV": Don't reselect the previous Visual area after a Select mode
- * mapping of menu.
- */
+ // "gV": Don't reselect the previous Visual area after a Select mode mapping of menu.
case 'V':
VIsual_reselect = false;
break;
- /*
- * "gh": start Select mode.
- * "gH": start Select line mode.
- * "g^H": start Select block mode.
- */
+ // "gh": start Select mode.
+ // "gH": start Select line mode.
+ // "g^H": start Select block mode.
case K_BS:
cap->nchar = Ctrl_H;
FALLTHROUGH;
@@ -6232,10 +6215,8 @@ static void nv_g_cmd(cmdarg_T *cap)
}
break;
- /*
- * "gj" and "gk" two new funny movement keys -- up and down
- * movement based on *screen* line rather than *file* line.
- */
+ // "gj" and "gk" two new funny movement keys -- up and down
+ // movement based on *screen* line rather than *file* line.
case 'j':
case K_DOWN:
// with 'nowrap' it works just like the normal "j" command.
@@ -6264,158 +6245,46 @@ static void nv_g_cmd(cmdarg_T *cap)
}
break;
- /*
- * "gJ": join two lines without inserting a space.
- */
+ // "gJ": join two lines without inserting a space.
case 'J':
nv_join(cap);
break;
- /*
- * "g0", "g^" and "g$": Like "0", "^" and "$" but for screen lines.
- * "gm": middle of "g0" and "g$".
- */
+ // "g0", "g^" : Like "0" and "^" but for screen lines.
+ // "gm": middle of "g0" and "g$".
case '^':
- flag = true;
- FALLTHROUGH;
-
case '0':
case 'm':
case K_HOME:
case K_KHOME:
- oap->motion_type = kMTCharWise;
- oap->inclusive = false;
- if (curwin->w_p_wrap
- && curwin->w_width_inner != 0) {
- int width1 = curwin->w_width_inner - curwin_col_off();
- int width2 = width1 + curwin_col_off2();
-
- validate_virtcol();
- i = 0;
- if (curwin->w_virtcol >= (colnr_T)width1 && width2 > 0) {
- i = (curwin->w_virtcol - width1) / width2 * width2 + width1;
- }
- } else {
- i = curwin->w_leftcol;
- }
- // Go to the middle of the screen line. When 'number' or
- // 'relativenumber' is on and lines are wrapping the middle can be more
- // to the left.
- if (cap->nchar == 'm') {
- i += (curwin->w_width_inner - curwin_col_off()
- + ((curwin->w_p_wrap && i > 0)
- ? curwin_col_off2() : 0)) / 2;
- }
- coladvance((colnr_T)i);
- if (flag) {
- do {
- i = gchar_cursor();
- } while (ascii_iswhite(i) && oneright());
- curwin->w_valid &= ~VALID_WCOL;
- }
- curwin->w_set_curswant = true;
+ nv_g_home_m_cmd(cap);
break;
- case 'M': {
- const char_u *const ptr = get_cursor_line_ptr();
-
+ case 'M':
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 {
coladvance((colnr_T)(i / 2));
}
curwin->w_set_curswant = true;
- }
- break;
+ break;
+ // "g_": to the last non-blank character in the line or <count> lines downward.
case '_':
- /* "g_": to the last non-blank character in the line or <count> lines
- * downward. */
- cap->oap->motion_type = kMTCharWise;
- cap->oap->inclusive = true;
- curwin->w_curswant = MAXCOL;
- if (cursor_down(cap->count1 - 1,
- cap->oap->op_type == OP_NOP) == false) {
- clearopbeep(cap->oap);
- } else {
- char_u *ptr = get_cursor_line_ptr();
-
- // In Visual mode we may end up after the line.
- if (curwin->w_cursor.col > 0 && ptr[curwin->w_cursor.col] == NUL) {
- --curwin->w_cursor.col;
- }
-
- // Decrease the cursor column until it's on a non-blank.
- while (curwin->w_cursor.col > 0
- && ascii_iswhite(ptr[curwin->w_cursor.col])) {
- --curwin->w_cursor.col;
- }
- curwin->w_set_curswant = true;
- adjust_for_sel(cap);
- }
+ nv_g_underscore_cmd(cap);
break;
+ // "g$" : Like "$" but for screen lines.
case '$':
case K_END:
- case K_KEND: {
- int col_off = curwin_col_off();
-
- oap->motion_type = kMTCharWise;
- oap->inclusive = true;
- if (curwin->w_p_wrap
- && curwin->w_width_inner != 0) {
- curwin->w_curswant = MAXCOL; // so we stay at the end
- if (cap->count1 == 1) {
- int width1 = curwin->w_width_inner - col_off;
- int width2 = width1 + curwin_col_off2();
-
- validate_virtcol();
- i = width1 - 1;
- if (curwin->w_virtcol >= (colnr_T)width1) {
- i += ((curwin->w_virtcol - width1) / width2 + 1)
- * width2;
- }
- coladvance((colnr_T)i);
-
- // Make sure we stick in this column.
- validate_virtcol();
- curwin->w_curswant = curwin->w_virtcol;
- curwin->w_set_curswant = false;
- if (curwin->w_cursor.col > 0 && curwin->w_p_wrap) {
- /*
- * Check for landing on a character that got split at
- * the end of the line. We do not want to advance to
- * the next screen line.
- */
- if (curwin->w_virtcol > (colnr_T)i) {
- --curwin->w_cursor.col;
- }
- }
- } else if (nv_screengo(oap, FORWARD, cap->count1 - 1) == false) {
- clearopbeep(oap);
- }
- } else {
- if (cap->count1 > 1) {
- // if it fails, let the cursor still move to the last char
- (void)cursor_down(cap->count1 - 1, false);
- }
- i = curwin->w_leftcol + curwin->w_width_inner - col_off - 1;
- coladvance((colnr_T)i);
-
- // Make sure we stick in this column.
- validate_virtcol();
- curwin->w_curswant = curwin->w_virtcol;
- curwin->w_set_curswant = false;
- }
- }
- break;
+ case K_KEND:
+ nv_g_dollar_cmd(cap);
+ break;
- /*
- * "g*" and "g#", like "*" and "#" but without using "\<" and "\>"
- */
+ // "g*" and "g#", like "*" and "#" but without using "\<" and "\>"
case '*':
case '#':
#if POUND != '#'
@@ -6426,9 +6295,7 @@ static void nv_g_cmd(cmdarg_T *cap)
nv_ident(cap);
break;
- /*
- * ge and gE: go back to end of word
- */
+ // ge and gE: go back to end of word
case 'e':
case 'E':
oap->motion_type = kMTCharWise;
@@ -6446,24 +6313,10 @@ static void nv_g_cmd(cmdarg_T *cap)
// "gi": start Insert at the last position.
case 'i':
- if (curbuf->b_last_insert.mark.lnum != 0) {
- curwin->w_cursor = curbuf->b_last_insert.mark;
- check_cursor_lnum();
- i = (int)STRLEN(get_cursor_line_ptr());
- if (curwin->w_cursor.col > (colnr_T)i) {
- if (virtual_active()) {
- curwin->w_cursor.coladd += curwin->w_cursor.col - i;
- }
- curwin->w_cursor.col = i;
- }
- }
- cap->cmdchar = 'i';
- nv_edit(cap);
+ nv_gi_cmd(cap);
break;
- /*
- * "gI": Start insert in column 1.
- */
+ // "gI": Start insert in column 1.
case 'I':
beginline(0);
if (!checkclearopq(oap)) {
@@ -6471,10 +6324,8 @@ static void nv_g_cmd(cmdarg_T *cap)
}
break;
- /*
- * "gf": goto file, edit file under cursor
- * "]f" and "[f": can also be used.
- */
+ // "gf": goto file, edit file under cursor
+ // "]f" and "[f": can also be used.
case 'f':
case 'F':
nv_gotofile(cap);
@@ -6488,26 +6339,20 @@ static void nv_g_cmd(cmdarg_T *cap)
nv_gomark(cap);
break;
- /*
- * "gs": Goto sleep.
- */
+ // "gs": Goto sleep.
case 's':
do_sleep(cap->count1 * 1000L);
break;
- /*
- * "ga": Display the ascii value of the character under the
- * cursor. It is displayed in decimal, hex, and octal. -- webb
- */
+ // "ga": Display the ascii value of the character under the
+ // cursor. It is displayed in decimal, hex, and octal. -- webb
case 'a':
do_ascii(NULL);
break;
- /*
- * "g8": Display the bytes used for the UTF-8 character under the
- * cursor. It is displayed in hex.
- * "8g8" finds illegal byte sequence.
- */
+ // "g8": Display the bytes used for the UTF-8 character under the
+ // cursor. It is displayed in hex.
+ // "8g8" finds illegal byte sequence.
case '8':
if (cap->count0 == 8) {
utf_find_illegal();
@@ -6520,25 +6365,21 @@ static void nv_g_cmd(cmdarg_T *cap)
show_sb_text();
break;
- /*
- * "gg": Goto the first line in file. With a count it goes to
- * that line number like for "G". -- webb
- */
+ // "gg": Goto the first line in file. With a count it goes to
+ // that line number like for "G". -- webb
case 'g':
cap->arg = false;
nv_goto(cap);
break;
- /*
- * Two-character operators:
- * "gq" Format text
- * "gw" Format text and keep cursor position
- * "g~" Toggle the case of the text.
- * "gu" Change text to lower case.
- * "gU" Change text to upper case.
- * "g?" rot13 encoding
- * "g@" call 'operatorfunc'
- */
+ // Two-character operators:
+ // "gq" Format text
+ // "gw" Format text and keep cursor position
+ // "g~" Toggle the case of the text.
+ // "gu" Change text to lower case.
+ // "gU" Change text to upper case.
+ // "g?" rot13 encoding
+ // "g@" call 'operatorfunc'
case 'q':
case 'w':
oap->cursor_start = curwin->w_cursor;
@@ -6551,19 +6392,14 @@ static void nv_g_cmd(cmdarg_T *cap)
nv_operator(cap);
break;
- /*
- * "gd": Find first occurrence of pattern under the cursor in the
- * current function
- * "gD": idem, but in the current file.
- */
+ // "gd": Find first occurrence of pattern under the cursor in the current function
+ // "gD": idem, but in the current file.
case 'd':
case 'D':
nv_gd(oap, cap->nchar, (int)cap->count0);
break;
- /*
- * g<*Mouse> : <C-*mouse>
- */
+ // g<*Mouse> : <C-*mouse>
case K_MIDDLEMOUSE:
case K_MIDDLEDRAG:
case K_MIDDLERELEASE:
@@ -6587,9 +6423,7 @@ static void nv_g_cmd(cmdarg_T *cap)
case K_IGNORE:
break;
- /*
- * "gP" and "gp": same as "P" and "p" but leave cursor just after new text
- */
+ // "gP" and "gp": same as "P" and "p" but leave cursor just after new text
case 'p':
case 'P':
nv_put(cap);
@@ -6602,13 +6436,7 @@ static void nv_g_cmd(cmdarg_T *cap)
// "gQ": improved Ex mode
case 'Q':
- if (text_locked()) {
- clearopbeep(cap->oap);
- text_locked_msg();
- break;
- }
-
- if (!checkclearopq(oap)) {
+ if (!check_text_locked(cap->oap) && !checkclearopq(oap)) {
do_exmode();
}
break;
@@ -6632,9 +6460,10 @@ static void nv_g_cmd(cmdarg_T *cap)
goto_tabpage(-(int)cap->count1);
}
break;
+
case TAB:
- if (!checkclearop(oap)) {
- goto_tabpage_lastused();
+ if (!checkclearop(oap) && !goto_tabpage_lastused()) {
+ clearopbeep(oap);
}
break;
@@ -6652,9 +6481,7 @@ static void nv_g_cmd(cmdarg_T *cap)
}
}
-/*
- * Handle "o" and "O" commands.
- */
+/// Handle "o" and "O" commands.
static void n_opencmd(cmdarg_T *cap)
{
if (!checkclearopq(cap->oap)) {
@@ -6673,9 +6500,8 @@ static void n_opencmd(cmdarg_T *cap)
(cap->cmdchar == 'o' ? 1 : 0))
)
&& open_line(cap->cmdchar == 'O' ? BACKWARD : FORWARD,
- has_format_option(FO_OPEN_COMS)
- ? OPENLINE_DO_COM : 0,
- 0)) {
+ has_format_option(FO_OPEN_COMS) ? OPENLINE_DO_COM : 0,
+ 0, NULL)) {
if (win_cursorline_standout(curwin)) {
// force redraw of cursorline
curwin->w_valid &= ~VALID_CROW;
@@ -6685,42 +6511,50 @@ static void n_opencmd(cmdarg_T *cap)
}
}
-/*
- * "." command: redo last change.
- */
+/// "." command: redo last change.
static void nv_dot(cmdarg_T *cap)
{
if (!checkclearopq(cap->oap)) {
- /*
- * If "restart_edit" is true, the last but one command is repeated
- * instead of the last command (inserting text). This is used for
- * CTRL-O <.> in insert mode.
- */
+ // If "restart_edit" is true, the last but one command is repeated
+ // instead of the last command (inserting text). This is used for
+ // CTRL-O <.> in insert mode.
if (start_redo(cap->count0, restart_edit != 0 && !arrow_used) == false) {
clearopbeep(cap->oap);
}
}
}
-/*
- * 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;
}
}
-/*
- * Handle "U" command.
- */
+/// Handle "U" command.
static void nv_Undo(cmdarg_T *cap)
{
// In Visual mode and typing "gUU" triggers an operator
- if (cap->oap->op_type == OP_UPPER
- || VIsual_active) {
+ if (cap->oap->op_type == OP_UPPER || VIsual_active) {
// translate "gUU" to "gUgU"
cap->cmdchar = 'g';
cap->nchar = 'U';
@@ -6731,15 +6565,11 @@ static void nv_Undo(cmdarg_T *cap)
}
}
-/*
- * '~' command: If tilde is not an operator and Visual is off: swap case of a
- * single character.
- */
+/// '~' command: If tilde is not an operator and Visual is off: swap case of a
+/// single character.
static void nv_tilde(cmdarg_T *cap)
{
- if (!p_to
- && !VIsual_active
- && cap->oap->op_type != OP_TILDE) {
+ if (!p_to && !VIsual_active && cap->oap->op_type != OP_TILDE) {
if (bt_prompt(curbuf) && !prompt_curpos_editable()) {
clearopbeep(cap->oap);
return;
@@ -6750,10 +6580,8 @@ static void nv_tilde(cmdarg_T *cap)
}
}
-/*
- * Handle an operator command.
- * The actual work is done by do_pending_operator().
- */
+/// Handle an operator command.
+/// The actual work is done by do_pending_operator().
static void nv_operator(cmdarg_T *cap)
{
int op_type;
@@ -6775,9 +6603,7 @@ static void nv_operator(cmdarg_T *cap)
}
}
-/*
- * Set v:operator to the characters for "optype".
- */
+/// Set v:operator to the characters for "optype".
static void set_op_var(int optype)
{
if (optype == OP_NOP) {
@@ -6797,15 +6623,13 @@ static void set_op_var(int optype)
}
}
-/*
- * Handle linewise operator "dd", "yy", etc.
- *
- * "_" is is a strange motion command that helps make operators more logical.
- * It is actually implemented, but not documented in the real Vi. This motion
- * command actually refers to "the current line". Commands like "dd" and "yy"
- * are really an alternate form of "d_" and "y_". It does accept a count, so
- * "d3_" works to delete 3 lines.
- */
+/// Handle linewise operator "dd", "yy", etc.
+///
+/// "_" is is a strange motion command that helps make operators more logical.
+/// It is actually implemented, but not documented in the real Vi. This motion
+/// command actually refers to "the current line". Commands like "dd" and "yy"
+/// are really an alternate form of "d_" and "y_". It does accept a count, so
+/// "d3_" works to delete 3 lines.
static void nv_lineop(cmdarg_T *cap)
{
cap->oap->motion_type = kMTLineWise;
@@ -6823,9 +6647,7 @@ static void nv_lineop(cmdarg_T *cap)
}
}
-/*
- * <Home> command.
- */
+/// <Home> command.
static void nv_home(cmdarg_T *cap)
{
// CTRL-HOME is like "gg"
@@ -6839,9 +6661,7 @@ static void nv_home(cmdarg_T *cap)
// one-character line).
}
-/*
- * "|" command.
- */
+/// "|" command.
static void nv_pipe(cmdarg_T *cap)
{
cap->oap->motion_type = kMTCharWise;
@@ -6858,10 +6678,8 @@ static void nv_pipe(cmdarg_T *cap)
curwin->w_set_curswant = false;
}
-/*
- * Handle back-word command "b" and "B".
- * cap->arg is 1 for "B"
- */
+/// Handle back-word command "b" and "B".
+/// cap->arg is 1 for "B"
static void nv_bck_word(cmdarg_T *cap)
{
cap->oap->motion_type = kMTCharWise;
@@ -6874,10 +6692,8 @@ static void nv_bck_word(cmdarg_T *cap)
}
}
-/*
- * Handle word motion commands "e", "E", "w" and "W".
- * cap->arg is true for "E" and "W".
- */
+/// Handle word motion commands "e", "E", "w" and "W".
+/// cap->arg is true for "E" and "W".
static void nv_wordcmd(cmdarg_T *cap)
{
int n;
@@ -6885,9 +6701,7 @@ static void nv_wordcmd(cmdarg_T *cap)
bool flag = false;
pos_T startpos = curwin->w_cursor;
- /*
- * Set inclusive for the "E" and "e" command.
- */
+ // Set inclusive for the "E" and "e" command.
if (cap->cmdchar == 'e' || cap->cmdchar == 'E') {
word_end = true;
} else {
@@ -6895,9 +6709,7 @@ static void nv_wordcmd(cmdarg_T *cap)
}
cap->oap->inclusive = word_end;
- /*
- * "cw" and "cW" are a special case.
- */
+ // "cw" and "cW" are a special case.
if (!word_end && cap->oap->op_type == OP_CHANGE) {
n = gchar_cursor();
if (n != NUL && !ascii_iswhite(n)) {
@@ -6941,11 +6753,9 @@ static void nv_wordcmd(cmdarg_T *cap)
}
}
-/*
- * Used after a movement command: If the cursor ends up on the NUL after the
- * end of the line, may move it back to the last character and make the motion
- * inclusive.
- */
+/// Used after a movement command: If the cursor ends up on the NUL after the
+/// end of the line, may move it back to the last character and make the motion
+/// inclusive.
static void adjust_cursor(oparg_T *oap)
{
// The cursor cannot remain on the NUL when:
@@ -6955,7 +6765,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();
@@ -6963,10 +6773,8 @@ static void adjust_cursor(oparg_T *oap)
}
}
-/*
- * "0" and "^" commands.
- * cap->arg is the argument for beginline().
- */
+/// "0" and "^" commands.
+/// cap->arg is the argument for beginline().
static void nv_beginline(cmdarg_T *cap)
{
cap->oap->motion_type = kMTCharWise;
@@ -6979,9 +6787,7 @@ static void nv_beginline(cmdarg_T *cap)
// one-character line).
}
-/*
- * In exclusive Visual mode, may include the last character.
- */
+/// In exclusive Visual mode, may include the last character.
static void adjust_for_sel(cmdarg_T *cap)
{
if (VIsual_active && cap->oap->inclusive && *p_sel == 'e'
@@ -6991,11 +6797,10 @@ static void adjust_for_sel(cmdarg_T *cap)
}
}
-/*
- * Exclude last character at end of Visual area for 'selection' == "exclusive".
- * Should check VIsual_mode before calling this.
- * Returns true when backed up to the previous line.
- */
+/// Exclude last character at end of Visual area for 'selection' == "exclusive".
+/// Should check VIsual_mode before calling this.
+///
+/// @return true when backed up to the previous line.
bool unadjust_for_sel(void)
{
pos_T *pp;
@@ -7012,7 +6817,7 @@ bool unadjust_for_sel(void)
pp->col--;
mark_mb_adjustpos(curbuf, pp);
} else if (pp->lnum > 1) {
- --pp->lnum;
+ pp->lnum--;
pp->col = (colnr_T)STRLEN(ml_get(pp->lnum));
return true;
}
@@ -7020,13 +6825,12 @@ bool unadjust_for_sel(void)
return false;
}
-/*
- * SELECT key in Normal or Visual mode: end of Select mode mapping.
- */
+/// SELECT key in Normal or Visual mode: end of Select mode mapping.
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;
@@ -7034,11 +6838,8 @@ static void nv_select(cmdarg_T *cap)
}
}
-
-/*
- * "G", "gg", CTRL-END, CTRL-HOME.
- * cap->arg is true for "G".
- */
+/// "G", "gg", CTRL-END, CTRL-HOME.
+/// cap->arg is true for "G".
static void nv_goto(cmdarg_T *cap)
{
linenr_T lnum;
@@ -7053,7 +6854,7 @@ static void nv_goto(cmdarg_T *cap)
// When a count is given, use it instead of the default lnum
if (cap->count0 != 0) {
- lnum = cap->count0;
+ lnum = (linenr_T)cap->count0;
}
if (lnum < 1L) {
lnum = 1L;
@@ -7067,9 +6868,7 @@ static void nv_goto(cmdarg_T *cap)
}
}
-/*
- * CTRL-\ in Normal mode.
- */
+/// CTRL-\ in Normal mode.
static void nv_normal(cmdarg_T *cap)
{
if (cap->nchar == Ctrl_N || cap->nchar == Ctrl_G) {
@@ -7085,19 +6884,13 @@ static void nv_normal(cmdarg_T *cap)
end_visual_mode(); // stop Visual
redraw_curbuf_later(INVERTED);
}
- // CTRL-\ CTRL-G restarts Insert mode when 'insertmode' is set.
- if (cap->nchar == Ctrl_G && p_im) {
- restart_edit = 'a';
- }
} else {
clearopbeep(cap->oap);
}
}
-/*
- * ESC in Normal mode: beep, but don't flush buffers.
- * Don't even beep if we are canceling a command.
- */
+/// ESC in Normal mode: beep, but don't flush buffers.
+/// Don't even beep if we are canceling a command.
static void nv_esc(cmdarg_T *cap)
{
int no_reason;
@@ -7105,8 +6898,7 @@ static void nv_esc(cmdarg_T *cap)
no_reason = (cap->oap->op_type == OP_NOP
&& cap->opcount == 0
&& cap->count0 == 0
- && cap->oap->regname == 0
- && !p_im);
+ && cap->oap->regname == 0);
if (cap->arg) { // true for CTRL-C
if (restart_edit == 0
@@ -7121,11 +6913,8 @@ static void nv_esc(cmdarg_T *cap)
}
}
- // Don't reset "restart_edit" when 'insertmode' is set, it won't be
- // set again below when halfway through a mapping.
- if (!p_im) {
- restart_edit = 0;
- }
+ restart_edit = 0;
+
if (cmdwin_type != 0) {
cmdwin_result = K_IGNORE;
got_int = false; // don't stop executing autocommands et al.
@@ -7148,25 +6937,17 @@ static void nv_esc(cmdarg_T *cap)
vim_beep(BO_ESC);
}
clearop(cap->oap);
-
- // A CTRL-C is often used at the start of a menu. When 'insertmode' is
- // set return to Insert mode afterwards.
- if (restart_edit == 0 && goto_im()
- && ex_normal_busy == 0) {
- restart_edit = 'a';
- }
}
-// Move the cursor for the "A" command.
+/// Move the cursor for the "A" command.
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
// character past the end of the line
- State = INSERT;
+ State = MODE_INSERT;
coladvance(MAXCOL);
State = save_State;
} else {
@@ -7189,8 +6970,7 @@ static void nv_edit(cmdarg_T *cap)
} else if ((cap->cmdchar == 'a' || cap->cmdchar == 'i')
&& (cap->oap->op_type != OP_NOP || VIsual_active)) {
nv_object(cap);
- } else if (!curbuf->b_p_ma && !p_im && !curbuf->terminal) {
- // Only give this error when 'insertmode' is off.
+ } else if (!curbuf->b_p_ma && !curbuf->terminal) {
emsg(_(e_modifiable));
clearop(cap->oap);
} else if (!checkclearopq(cap->oap)) {
@@ -7222,7 +7002,7 @@ static void nv_edit(cmdarg_T *cap)
// Pretend Insert mode here to allow the cursor on the
// character past the end of the line
- State = INSERT;
+ State = MODE_INSERT;
coladvance(getviscol());
State = save_State;
}
@@ -7259,9 +7039,7 @@ static void invoke_edit(cmdarg_T *cap, int repl, int cmd, int startln)
}
}
-/*
- * "a" or "i" while an operator is pending or in Visual mode: object motion.
- */
+/// "a" or "i" while an operator is pending or in Visual mode: object motion.
static void nv_object(cmdarg_T *cap)
{
bool flag;
@@ -7337,10 +7115,8 @@ static void nv_object(cmdarg_T *cap)
curwin->w_set_curswant = true;
}
-/*
- * "q" command: Start/stop recording.
- * "q:", "q/", "q?": edit command-line in command-line window.
- */
+/// "q" command: Start/stop recording.
+/// "q:", "q/", "q?": edit command-line in command-line window.
static void nv_record(cmdarg_T *cap)
{
if (cap->oap->op_type == OP_FORMAT) {
@@ -7362,9 +7138,7 @@ static void nv_record(cmdarg_T *cap)
}
}
-/*
- * Handle the "@r" command.
- */
+/// Handle the "@r" command.
static void nv_at(cmdarg_T *cap)
{
if (checkclearop(cap->oap)) {
@@ -7384,9 +7158,7 @@ static void nv_at(cmdarg_T *cap)
}
}
-/*
- * Handle the CTRL-U and CTRL-D commands.
- */
+/// Handle the CTRL-U and CTRL-D commands.
static void nv_halfpage(cmdarg_T *cap)
{
if ((cap->cmdchar == Ctrl_U && curwin->w_cursor.lnum == 1)
@@ -7394,13 +7166,11 @@ static void nv_halfpage(cmdarg_T *cap)
&& curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count)) {
clearopbeep(cap->oap);
} else if (!checkclearop(cap->oap)) {
- halfpage(cap->cmdchar == Ctrl_D, cap->count0);
+ halfpage(cap->cmdchar == Ctrl_D, (linenr_T)cap->count0);
}
}
-/*
- * Handle "J" or "gJ" command.
- */
+/// Handle "J" or "gJ" command.
static void nv_join(cmdarg_T *cap)
{
if (VIsual_active) { // join the visual lines
@@ -7425,16 +7195,15 @@ static void nv_join(cmdarg_T *cap)
}
}
-/*
- * "P", "gP", "p" and "gp" commands.
- */
+/// "P", "gP", "p" and "gp" commands.
static void nv_put(cmdarg_T *cap)
{
nv_put_opt(cap, false);
}
-// "P", "gP", "p" and "gp" commands.
-// "fix_indent" is true for "[p", "[P", "]p" and "]P".
+/// "P", "gP", "p" and "gp" commands.
+///
+/// @param fix_indent true for "[p", "[P", "]p" and "]P".
static void nv_put_opt(cmdarg_T *cap, bool fix_indent)
{
int regname = 0;
@@ -7479,9 +7248,9 @@ static void nv_put_opt(cmdarg_T *cap, bool fix_indent)
// overwrites if the old contents is being put.
was_visual = true;
regname = cap->oap->regname;
+ bool keep_registers = cap->cmdchar == 'P';
// '+' and '*' could be the same selection
- bool clipoverwrite = (regname == '+' || regname == '*')
- && (cb_flags & CB_UNNAMEDMASK);
+ bool clipoverwrite = (regname == '+' || regname == '*') && (cb_flags & CB_UNNAMEDMASK);
if (regname == 0 || regname == '"' || clipoverwrite
|| ascii_isdigit(regname) || regname == '-') {
// The delete might overwrite the register we want to put, save it first
@@ -7496,7 +7265,7 @@ static void nv_put_opt(cmdarg_T *cap, bool fix_indent)
// Now delete the selected text. Avoid messages here.
cap->cmdchar = 'd';
cap->nchar = NUL;
- cap->oap->regname = NUL;
+ cap->oap->regname = keep_registers ? '_' : NUL;
msg_silent++;
nv_operator(cap);
do_pending_operator(cap, 0, false);
@@ -7566,9 +7335,7 @@ static void nv_put_opt(cmdarg_T *cap, bool fix_indent)
}
}
-/*
- * "o" and "O" commands.
- */
+/// "o" and "O" commands.
static void nv_open(cmdarg_T *cap)
{
// "do" is ":diffget"
@@ -7586,7 +7353,7 @@ static void nv_open(cmdarg_T *cap)
}
}
-// Handle an arbitrary event in normal mode
+/// Handle an arbitrary event in normal mode
static void nv_event(cmdarg_T *cap)
{
// Garbage collection should have been executed before blocking for events in
@@ -7609,7 +7376,7 @@ static void nv_event(cmdarg_T *cap)
}
}
-/// @return true when 'mousemodel' is set to "popup" or "popup_setpos".
+/// @return true when 'mousemodel' is set to "popup" or "popup_setpos".
static bool mouse_model_popup(void)
{
return p_mousem[0] == 'p';
diff --git a/src/nvim/normal.h b/src/nvim/normal.h
index 0c2b8b4d8a..9bda70eacd 100644
--- a/src/nvim/normal.h
+++ b/src/nvim/normal.h
@@ -76,7 +76,6 @@ typedef struct cmdarg_S {
#define CA_COMMAND_BUSY 1 // skip restarting edit() once
#define CA_NO_ADJ_OP_END 2 // don't adjust operator end
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "normal.h.generated.h"
#endif
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index ef9fe055ac..3602eb2a3e 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -56,7 +56,6 @@
#include "nvim/undo.h"
#include "nvim/vim.h"
#include "nvim/window.h"
-
#include "nvim/yankmap.h"
struct yank_registers {
@@ -152,10 +151,18 @@ static char opchars[][3] =
{ Ctrl_X, NUL, OPF_CHANGE }, // OP_NR_SUB
};
-/*
- * Translate a command name into an operator type.
- * Must only be called with a valid operator name!
- */
+yankreg_T *get_y_previous(void)
+{
+ return y_previous;
+}
+
+void set_y_previous(yankreg_T *yreg)
+{
+ y_previous = yreg;
+}
+
+/// Translate a command name into an operator type.
+/// Must only be called with a valid operator name!
int get_op_type(int char1, int char2)
{
int i;
@@ -191,40 +198,33 @@ int get_op_type(int char1, int char2)
return i;
}
-/*
- * Return TRUE if operator "op" always works on whole lines.
- */
+/// @return TRUE if operator "op" always works on whole lines.
int op_on_lines(int op)
{
return opchars[op][2] & OPF_LINES;
}
-// Return TRUE if operator "op" changes text.
+/// @return TRUE if operator "op" changes text.
int op_is_change(int op)
{
return opchars[op][2] & OPF_CHANGE;
}
-/*
- * Get first operator command character.
- * Returns 'g' or 'z' if there is another command character.
- */
+/// Get first operator command character.
+///
+/// @return 'g' or 'z' if there is another command character.
int get_op_char(int optype)
{
return opchars[optype][0];
}
-/*
- * Get second operator command character.
- */
+/// Get second operator command character.
int get_extra_op_char(int optype)
{
return opchars[optype][1];
}
-/*
- * op_shift - handle a shift operation
- */
+/// handle a shift operation
void op_shift(oparg_T *oap, int curs_top, int amount)
{
long i;
@@ -284,14 +284,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.cmod_flags & CMOD_LOCKMARKS) == 0) {
+ // 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);
@@ -343,10 +343,8 @@ void shift_line(int left, int round, int amount, int call_changed_bytes)
}
}
-/*
- * Shift one line of the current block one shiftwidth right or left.
- * Leaves cursor on first character in block.
- */
+/// Shift one line of the current block one shiftwidth right or left.
+/// Leaves cursor on first character in block.
static void shift_block(oparg_T *oap, int amount)
{
const bool left = (oap->op_type == OP_LSHIFT);
@@ -363,7 +361,7 @@ static void shift_block(oparg_T *oap, int amount)
p_ri = 0; // don't want revins in indent
- State = INSERT; // don't want REPLACE for State
+ State = MODE_INSERT; // don't want MODE_REPLACE for State
block_prep(oap, &bd, curwin->w_cursor.lnum, true);
if (bd.is_short) {
return;
@@ -390,7 +388,7 @@ static void shift_block(oparg_T *oap, int amount)
colnr_T ws_vcol = bd.start_vcol - bd.pre_whitesp;
char_u *old_textstart = bd.textstart;
if (bd.startspaces) {
- if (utfc_ptr2len(bd.textstart) == 1) {
+ if (utfc_ptr2len((char *)bd.textstart) == 1) {
bd.textstart++;
} else {
ws_vcol = 0;
@@ -398,8 +396,8 @@ static void shift_block(oparg_T *oap, int amount)
}
}
for (; ascii_iswhite(*bd.textstart);) {
- // TODO: is passing bd.textstart for start of the line OK?
- incr = lbr_chartabsize_adv(bd.textstart, &bd.textstart, (bd.start_vcol));
+ // TODO(fmoralesc): is passing bd.textstart for start of the line OK?
+ incr = lbr_chartabsize_adv(bd.textstart, &bd.textstart, bd.start_vcol);
total += incr;
bd.start_vcol += incr;
}
@@ -415,14 +413,14 @@ static void shift_block(oparg_T *oap, int amount)
int col_pre = bd.pre_whitesp_c - (bd.startspaces != 0);
bd.textcol -= col_pre;
const int len = (int)STRLEN(bd.textstart) + 1;
- int col = bd.textcol + i +j + len;
+ int col = bd.textcol + i + j + len;
assert(col >= 0);
newp = (char_u *)xmalloc((size_t)col);
memset(newp, NUL, (size_t)col);
memmove(newp, oldp, (size_t)bd.textcol);
startcol = bd.textcol;
- oldlen = (int)(bd.textstart-old_textstart) + col_pre;
- newlen = i+j;
+ oldlen = (int)(bd.textstart - old_textstart) + col_pre;
+ newlen = i + j;
memset(newp + bd.textcol, TAB, (size_t)i);
memset(newp + bd.textcol + i, ' ', (size_t)j);
// the end
@@ -462,7 +460,6 @@ static void shift_block(oparg_T *oap, int amount)
non_white_col += incr;
}
-
const colnr_T block_space_width = non_white_col - oap->start_vcol;
// We will shift by "total" or "block_space_width", whichever is less.
const colnr_T shift_amount = block_space_width < total
@@ -517,9 +514,9 @@ static void shift_block(oparg_T *oap, int amount)
STRMOVE(newp + verbatim_diff + fill, non_white);
}
// replace the line
- ml_replace(curwin->w_cursor.lnum, newp, false);
+ ml_replace(curwin->w_cursor.lnum, (char *)newp, false);
changed_bytes(curwin->w_cursor.lnum, bd.textcol);
- extmark_splice_cols(curbuf, (int)curwin->w_cursor.lnum-1, startcol,
+ extmark_splice_cols(curbuf, (int)curwin->w_cursor.lnum - 1, startcol,
oldlen, newlen,
kExtmarkUndo);
State = oldstate;
@@ -527,10 +524,8 @@ static void shift_block(oparg_T *oap, int amount)
p_ri = old_p_ri;
}
-/*
- * Insert string "s" (b_insert ? before : after) block :AKelly
- * Caller must prepare for undo.
- */
+/// Insert string "s" (b_insert ? before : after) block :AKelly
+/// Caller must prepare for undo.
static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def *bdp)
{
int p_ts;
@@ -541,7 +536,7 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def
char_u *newp, *oldp; // new, old lines
linenr_T lnum; // loop var
int oldstate = State;
- State = INSERT; // don't want REPLACE for State
+ State = MODE_INSERT; // don't want MODE_REPLACE for State
for (lnum = oap->start.lnum + 1; lnum <= oap->end.lnum; lnum++) {
block_prep(oap, bdp, lnum, true);
@@ -577,21 +572,18 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def
}
if (spaces > 0) {
- int off;
-
- // Avoid starting halfway through a multi-byte character.
- if (b_insert) {
- off = utf_head_off(oldp, oldp + offset + spaces);
- } else {
- off = (*mb_off_next)(oldp, oldp + offset);
- offset += off;
- }
- spaces -= off;
- count -= off;
+ // avoid copying part of a multi-byte character
+ offset -= utf_head_off(oldp, oldp + offset);
+ }
+ if (spaces < 0) { // can happen when the cursor was moved
+ spaces = 0;
}
assert(count >= 0);
- newp = (char_u *)xmalloc(STRLEN(oldp) + s_len + (size_t)count + 1);
+ // Make sure the allocated size matches what is actually copied below.
+ newp = xmalloc(STRLEN(oldp) + (size_t)spaces + s_len
+ + (spaces > 0 && !bdp->is_short ? (size_t)p_ts - (size_t)spaces : 0)
+ + (size_t)count + 1);
// copy up to shifted part
memmove(newp, oldp, (size_t)offset);
@@ -606,14 +598,19 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def
offset += (int)s_len;
int skipped = 0;
- if (spaces && !bdp->is_short) {
- // insert post-padding
- memset(newp + offset + spaces, ' ', (size_t)(p_ts - spaces));
- // We're splitting a TAB, don't copy it.
- oldp++;
- // We allowed for that TAB, remember this now
- count++;
- skipped = 1;
+ if (spaces > 0 && !bdp->is_short) {
+ if (*oldp == TAB) {
+ // insert post-padding
+ memset(newp + offset + spaces, ' ', (size_t)(p_ts - spaces));
+ // We're splitting a TAB, don't copy it.
+ oldp++;
+ // We allowed for that TAB, remember this now
+ count++;
+ skipped = 1;
+ } else {
+ // Not a TAB, no extra spaces
+ count = spaces;
+ }
}
if (spaces > 0) {
@@ -621,9 +618,9 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def
}
STRMOVE(newp + offset, oldp);
- ml_replace(lnum, newp, false);
- extmark_splice_cols(curbuf, (int)lnum-1, startcol,
- skipped, offset-startcol, kExtmarkUndo);
+ ml_replace(lnum, (char *)newp, false);
+ extmark_splice_cols(curbuf, (int)lnum - 1, startcol,
+ skipped, offset - startcol, kExtmarkUndo);
if (lnum == oap->end.lnum) {
// Set "']" mark to the end of the block instead of the end of
@@ -638,12 +635,10 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def
State = oldstate;
}
-/*
- * op_reindent - handle reindenting a block of lines.
- */
+/// Handle reindenting a block of lines.
void op_reindent(oparg_T *oap, Indenter how)
{
- long i;
+ long i = 0;
char_u *l;
int amount;
linenr_T first_changed = 0;
@@ -656,38 +651,41 @@ void op_reindent(oparg_T *oap, Indenter how)
return;
}
- for (i = oap->line_count - 1; i >= 0 && !got_int; i--) {
- /* it's a slow thing to do, so give feedback so there's no worry that
- * the computer's just hung. */
+ // Save for undo. Do this once for all lines, much faster than doing this
+ // for each line separately, especially when undoing.
+ if (u_savecommon(curbuf, start_lnum - 1, start_lnum + (linenr_T)oap->line_count,
+ start_lnum + (linenr_T)oap->line_count, false) == OK) {
+ for (i = oap->line_count - 1; i >= 0 && !got_int; i--) {
+ // it's a slow thing to do, so give feedback so there's no worry
+ // that the computer's just hung.
- if (i > 1
- && (i % 50 == 0 || i == oap->line_count - 1)
- && oap->line_count > p_report) {
- smsg(_("%" PRId64 " lines to indent... "), (int64_t)i);
- }
-
- /*
- * Be vi-compatible: For lisp indenting the first line is not
- * indented, unless there is only one line.
- */
- if (i != oap->line_count - 1 || oap->line_count == 1
- || how != get_lisp_indent) {
- l = skipwhite(get_cursor_line_ptr());
- if (*l == NUL) { // empty or blank line
- amount = 0;
- } else {
- amount = how(); // get the indent for this line
+ if (i > 1
+ && (i % 50 == 0 || i == oap->line_count - 1)
+ && oap->line_count > p_report) {
+ smsg(_("%" PRId64 " lines to indent... "), (int64_t)i);
}
- if (amount >= 0 && set_indent(amount, SIN_UNDO)) {
- // did change the indent, call changed_lines() later
- if (first_changed == 0) {
- first_changed = curwin->w_cursor.lnum;
+
+ // Be vi-compatible: For lisp indenting the first line is not
+ // indented, unless there is only one line.
+ if (i != oap->line_count - 1 || oap->line_count == 1
+ || how != get_lisp_indent) {
+ l = (char_u *)skipwhite((char *)get_cursor_line_ptr());
+ if (*l == NUL) { // empty or blank line
+ amount = 0;
+ } else {
+ amount = how(); // get the indent for this line
+ }
+ if (amount >= 0 && set_indent(amount, 0)) {
+ // did change the indent, call changed_lines() later
+ if (first_changed == 0) {
+ first_changed = curwin->w_cursor.lnum;
+ }
+ last_changed = curwin->w_cursor.lnum;
}
- last_changed = curwin->w_cursor.lnum;
}
+ curwin->w_cursor.lnum++;
+ curwin->w_cursor.col = 0; // make sure it's valid
}
- ++curwin->w_cursor.lnum;
- curwin->w_cursor.col = 0; // make sure it's valid
}
// put cursor on first non-blank of indented line
@@ -699,7 +697,7 @@ void op_reindent(oparg_T *oap, Indenter how)
* there is no change still need to remove the Visual highlighting. */
if (last_changed != 0) {
changed_lines(first_changed, 0,
- oap->is_VIsual ? start_lnum + oap->line_count :
+ oap->is_VIsual ? start_lnum + (linenr_T)oap->line_count :
last_changed + 1, 0L, true);
} else if (oap->is_VIsual) {
redraw_curbuf_later(INVERTED);
@@ -711,9 +709,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.cmod_flags & CMOD_LOCKMARKS) == 0) {
+ // set '[ and '] marks
+ curbuf->b_op_start = oap->start;
+ curbuf->b_op_end = oap->end;
+ }
}
/*
@@ -721,10 +721,9 @@ void op_reindent(oparg_T *oap, Indenter how)
*/
static char_u *expr_line = NULL;
-/*
- * Get an expression for the "\"=expr1" or "CTRL-R =expr1"
- * Returns '=' when OK, NUL otherwise.
- */
+/// Get an expression for the "\"=expr1" or "CTRL-R =expr1"
+///
+/// @return '=' when OK, NUL otherwise.
int get_expr_register(void)
{
char_u *new_line;
@@ -741,20 +740,17 @@ int get_expr_register(void)
return '=';
}
-/*
- * Set the expression for the '=' register.
- * Argument must be an allocated string.
- */
+/// Set the expression for the '=' register.
+/// Argument must be an allocated string.
void set_expr_line(char_u *new_line)
{
xfree(expr_line);
expr_line = new_line;
}
-/*
- * Get the result of the '=' register expression.
- * Returns a pointer to allocated memory, or NULL for failure.
- */
+/// Get the result of the '=' register expression.
+///
+/// @return a pointer to allocated memory, or NULL for failure.
char_u *get_expr_line(void)
{
char_u *expr_copy;
@@ -776,15 +772,13 @@ char_u *get_expr_line(void)
}
nested++;
- rv = eval_to_string(expr_copy, NULL, true);
+ rv = (char_u *)eval_to_string((char *)expr_copy, NULL, true);
nested--;
xfree(expr_copy);
return rv;
}
-/*
- * Get the '=' register expression itself, without evaluating it.
- */
+/// Get the '=' register expression itself, without evaluating it.
char_u *get_expr_line_src(void)
{
if (expr_line == NULL) {
@@ -793,6 +787,7 @@ char_u *get_expr_line_src(void)
return vim_strsave(expr_line);
}
+
int get_userreg(int regname)
{
if ((regname >= 'a' && regname <= 'z')
@@ -810,8 +805,9 @@ int get_userreg(int regname)
return regname + USER_REGISTERS_START;
}
-/// Returns whether `regname` is a valid name of a yank register.
-/// Note: There is no check for 0 (default register), caller should do this.
+/// @return whether `regname` is a valid name of a yank register.
+///
+/// @note: There is no check for 0 (default register), caller should do this.
/// The black hole register '_' is regarded as valid.
///
/// @param regname name of register
@@ -819,7 +815,7 @@ int get_userreg(int regname)
bool valid_yank_reg(int regname, bool writing)
{
if ((regname > 0 && ASCII_ISALNUM(regname))
- || (!writing && vim_strchr((char_u *)"/.%:=", regname) != NULL)
+ || (!writing && vim_strchr("/.%:=", regname) != NULL)
|| regname == '#'
|| regname == '"'
|| regname == '-'
@@ -832,7 +828,7 @@ bool valid_yank_reg(int regname, bool writing)
return false;
}
-/// Return yankreg_T to use, according to the value of `regname`.
+/// @return yankreg_T to use, according to the value of `regname`.
/// Cannot handle the '_' (black hole) register.
/// Must only be called with a valid register name!
///
@@ -892,8 +888,8 @@ static inline bool is_literal_register(int regname)
return regname == '*' || regname == '+';
}
-/// Returns a copy of contents in register `name`
-/// for use in do_put. Should be freed by caller.
+/// @return a copy of contents in register `name` for use in do_put. Should be
+/// freed by caller.
yankreg_T *copy_register(int name)
FUNC_ATTR_NONNULL_RET
{
@@ -904,15 +900,15 @@ yankreg_T *copy_register(int name)
if (copy->y_size == 0) {
copy->y_array = NULL;
} else {
- copy->y_array = xcalloc(copy->y_size, sizeof(char_u *));
+ copy->y_array = xcalloc(copy->y_size, sizeof(char *));
for (size_t i = 0; i < copy->y_size; i++) {
- copy->y_array[i] = vim_strsave(reg->y_array[i]);
+ copy->y_array[i] = xstrdup(reg->y_array[i]);
}
}
return copy;
}
-/// check if the current yank register has kMTLineWise register type
+/// Check if the current yank register has kMTLineWise register type
bool yank_register_mline(int regname)
{
if (regname != 0 && !valid_yank_reg(regname, false)) {
@@ -925,11 +921,9 @@ bool yank_register_mline(int regname)
return reg->y_type == kMTLineWise;
}
-/*
- * Start or stop recording into a yank register.
- *
- * Return FAIL for failure, OK otherwise.
- */
+/// Start or stop recording into a yank register.
+///
+/// @return FAIL for failure, OK otherwise.
int do_record(int c)
{
char_u *p;
@@ -957,18 +951,19 @@ 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);
}
// Name of requested register, or empty string for unnamed operation.
- char buf[NUMBUFLEN+2];
+ char buf[NUMBUFLEN + 2];
buf[0] = (char)regname;
buf[1] = NUL;
(void)tv_dict_add_str(dict, S_LEN("regname"), buf);
+ tv_dict_set_keys_readonly(dict);
- // 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);
@@ -1005,12 +1000,10 @@ static void set_yreg_additional_data(yankreg_T *reg, dict_T *additional_data)
reg->additional_data = additional_data;
}
-/*
- * Stuff string "p" into yank register "regname" as a single line (append if
- * uppercase). "p" must have been allocated.
- *
- * return FAIL for failure, OK otherwise
- */
+/// Stuff string "p" into yank register "regname" as a single line (append if
+/// uppercase). "p" must have been allocated.
+///
+/// @return FAIL for failure, OK otherwise
static int stuff_yank(int regname, char_u *p)
{
// check for read-only register
@@ -1024,7 +1017,7 @@ static int stuff_yank(int regname, char_u *p)
}
yankreg_T *reg = get_yank_register(regname, YREG_YANK);
if (is_append_register(regname) && reg->y_array != NULL) {
- char_u **pp = &(reg->y_array[reg->y_size - 1]);
+ char_u **pp = (char_u **)&(reg->y_array[reg->y_size - 1]);
char_u *lp = xmalloc(STRLEN(*pp) + STRLEN(p) + 1);
STRCPY(lp, *pp);
// TODO(philix): use xstpcpy() in stuff_yank()
@@ -1035,8 +1028,8 @@ static int stuff_yank(int regname, char_u *p)
} else {
free_register(reg);
set_yreg_additional_data(reg, NULL);
- reg->y_array = (char_u **)xmalloc(sizeof(char_u *));
- reg->y_array[0] = p;
+ reg->y_array = xmalloc(sizeof(char_u *));
+ reg->y_array[0] = (char *)p;
reg->y_size = 1;
reg->y_type = kMTCharWise;
}
@@ -1046,6 +1039,60 @@ static int stuff_yank(int regname, char_u *p)
static int execreg_lastc = NUL;
+/// When executing a register as a series of ex-commands, if the
+/// line-continuation character is used for a line, then join it with one or
+/// more previous lines. Note that lines are processed backwards starting from
+/// the last line in the register.
+///
+/// @param lines list of lines in the register
+/// @param idx index of the line starting with \ or "\. Join this line with all the immediate
+/// predecessor lines that start with a \ and the first line that doesn't start
+/// with a \. Lines that start with a comment "\ character are ignored.
+/// @returns the concatenated line. The index of the line that should be
+/// processed next is returned in idx.
+static char_u *execreg_line_continuation(char_u **lines, size_t *idx)
+{
+ size_t i = *idx;
+ assert(i > 0);
+ const size_t cmd_end = i;
+
+ garray_T ga;
+ ga_init(&ga, (int)sizeof(char_u), 400);
+
+ char_u *p;
+
+ // search backwards to find the first line of this command.
+ // Any line not starting with \ or "\ is the start of the
+ // command.
+ while (--i > 0) {
+ p = (char_u *)skipwhite((char *)lines[i]);
+ if (*p != '\\' && (p[0] != '"' || p[1] != '\\' || p[2] != ' ')) {
+ break;
+ }
+ }
+ const size_t cmd_start = i;
+
+ // join all the lines
+ ga_concat(&ga, (char *)lines[cmd_start]);
+ for (size_t j = cmd_start + 1; j <= cmd_end; j++) {
+ p = (char_u *)skipwhite((char *)lines[j]);
+ if (*p == '\\') {
+ // Adjust the growsize to the current length to
+ // speed up concatenating many lines.
+ if (ga.ga_len > 400) {
+ ga_set_growsize(&ga, MIN(ga.ga_len, 8000));
+ }
+ ga_concat(&ga, (char *)(p + 1));
+ }
+ }
+ ga_append(&ga, NUL);
+ char_u *str = vim_strsave(ga.ga_data);
+ ga_clear(&ga);
+
+ *idx = i;
+ return str;
+}
+
/// Execute a yank register: copy it into the stuff buffer
///
/// @param colon insert ':' before each line
@@ -1126,22 +1173,36 @@ int do_execreg(int regname, int colon, int addcr, int silent)
* Insert lines into typeahead buffer, from last one to first one.
*/
put_reedit_in_typebuf(silent);
- char_u *escaped;
+ char *escaped;
for (size_t i = reg->y_size; i-- > 0;) { // from y_size - 1 to 0 included
// insert NL between lines and after last line if type is kMTLineWise
if (reg->y_type == kMTLineWise || i < reg->y_size - 1 || addcr) {
- if (ins_typebuf((char_u *)"\n", remap, 0, true, silent) == FAIL) {
+ if (ins_typebuf("\n", remap, 0, true, silent) == FAIL) {
return FAIL;
}
}
- escaped = vim_strsave_escape_csi(reg->y_array[i]);
+
+ // Handle line-continuation for :@<register>
+ char_u *str = (char_u *)reg->y_array[i];
+ bool free_str = false;
+ if (colon && i > 0) {
+ p = (char_u *)skipwhite((char *)str);
+ if (*p == '\\' || (p[0] == '"' && p[1] == '\\' && p[2] == ' ')) {
+ str = execreg_line_continuation((char_u **)reg->y_array, &i);
+ free_str = true;
+ }
+ }
+ escaped = vim_strsave_escape_ks((char *)str);
+ if (free_str) {
+ xfree(str);
+ }
retval = ins_typebuf(escaped, remap, 0, true, silent);
xfree(escaped);
if (retval == FAIL) {
return FAIL;
}
if (colon
- && ins_typebuf((char_u *)":", remap, 0, true, silent) == FAIL) {
+ && ins_typebuf(":", remap, 0, true, silent) == FAIL) {
return FAIL;
}
}
@@ -1150,10 +1211,8 @@ int do_execreg(int regname, int colon, int addcr, int silent)
return retval;
}
-/*
- * If "restart_edit" is not zero, put it in the typeahead buffer, so that it's
- * used only after other typeahead has been processed.
- */
+/// If "restart_edit" is not zero, put it in the typeahead buffer, so that it's
+/// used only after other typeahead has been processed.
static void put_reedit_in_typebuf(int silent)
{
char_u buf[3];
@@ -1167,7 +1226,7 @@ static void put_reedit_in_typebuf(int silent)
buf[0] = (char_u)(restart_edit == 'I' ? 'i' : restart_edit);
buf[1] = NUL;
}
- if (ins_typebuf(buf, REMAP_NONE, 0, true, silent) == OK) {
+ if (ins_typebuf((char *)buf, REMAP_NONE, 0, true, silent) == OK) {
restart_edit = NUL;
}
}
@@ -1176,7 +1235,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)
@@ -1185,15 +1244,15 @@ static int put_in_typebuf(char_u *s, bool esc, bool colon, int silent)
put_reedit_in_typebuf(silent);
if (colon) {
- retval = ins_typebuf((char_u *)"\n", REMAP_NONE, 0, true, silent);
+ retval = ins_typebuf("\n", REMAP_NONE, 0, true, silent);
}
if (retval == OK) {
- char_u *p;
+ char *p;
if (esc) {
- p = vim_strsave_escape_csi(s);
+ p = vim_strsave_escape_ks((char *)s);
} else {
- p = s;
+ p = (char *)s;
}
if (p == NULL) {
retval = FAIL;
@@ -1205,7 +1264,7 @@ static int put_in_typebuf(char_u *s, bool esc, bool colon, int silent)
}
}
if (colon && retval == OK) {
- retval = ins_typebuf((char_u *)":", REMAP_NONE, 0, true, silent);
+ retval = ins_typebuf(":", REMAP_NONE, 0, true, silent);
}
return retval;
}
@@ -1316,19 +1375,19 @@ bool get_spec_reg(int regname, char_u **argp, bool *allocated, bool errmsg);
*/
static int eval_urf_put(char_u *ufn, int regname, char_u **argp)
{
- char_u regname_str[5];
+ char regname_str[5];
int len;
len = (*utf_char2len)(regname);
regname_str[len] = 0;
- utf_char2bytes(regname, regname_str);
+ utf_char2bytes(regname, (char*) regname_str);
typval_T args[3];
args[0].v_type = VAR_STRING;
args[1].v_type = VAR_STRING;
args[2].v_type = VAR_UNKNOWN;
- args[0].vval.v_string = (char_u *)"put";
+ args[0].vval.v_string = "put";
args[1].vval.v_string = regname_str;
*argp = (char_u *)call_func_retstr((char *)ufn, 3, args);
@@ -1344,11 +1403,11 @@ static int eval_yank_userreg(const char_u *ufn, int regname, yankreg_T *reg)
if (!reg)
return -1;
- char_u *totalbuf;
+ char *totalbuf;
size_t totallen = 0;
size_t i, j, k;
int ret, len;
- char_u regname_str[5];
+ char regname_str[5];
{
// Concat the contents of the register to pass into the yank()
@@ -1379,11 +1438,11 @@ static int eval_yank_userreg(const char_u *ufn, int regname, yankreg_T *reg)
args[2].v_type = VAR_STRING;
args[3].v_type = VAR_UNKNOWN;
- args[0].vval.v_string = (char_u *)"yank";
+ args[0].vval.v_string = "yank";
args[1].vval.v_string = regname_str;
args[2].vval.v_string = totalbuf;
- char_u *dup_ufn = (char_u *)xstrdup((char *)ufn);
+ char *dup_ufn = strdup((char *)ufn);
ret = (int)call_func_retnr(dup_ufn, 3, args);
xfree(dup_ufn);
xfree(totalbuf);
@@ -1408,11 +1467,11 @@ bool get_spec_reg(
if (errmsg) {
check_fname(); // will give emsg if not set
}
- *argp = curbuf->b_fname;
+ *argp = (char_u *)curbuf->b_fname;
return true;
case '#': // alternate file name
- *argp = getaltfname(errmsg); // may give emsg if not set
+ *argp = (char_u *)getaltfname(errmsg); // may give emsg if not set
return true;
case '=': // result of expression
@@ -1510,7 +1569,7 @@ bool cmdline_paste_reg(int regname, bool literally_arg, bool remcr)
}
for (size_t i = 0; i < reg->y_size; i++) {
- cmdline_paste_str(reg->y_array[i], literally);
+ cmdline_paste_str((char_u *)reg->y_array[i], literally);
// Insert ^M between lines, unless `remcr` is true.
if (i < reg->y_size - 1 && !remcr) {
@@ -1527,7 +1586,7 @@ bool cmdline_paste_reg(int regname, bool literally_arg, bool remcr)
return OK;
}
-// Shift the delete registers: "9 is cleared, "8 becomes "9, etc.
+/// Shift the delete registers: "9 is cleared, "8 becomes "9, etc.
static void shift_delete_registers(bool y_append)
{
free_register(get_global_reg(9)); // free register "9
@@ -1540,11 +1599,9 @@ static void shift_delete_registers(bool y_append)
get_global_reg(1)->y_array = NULL; // set register "1 to empty
}
-/*
- * Handle a delete operation.
- *
- * Return FAIL if undo failed, OK otherwise.
- */
+/// Handle a delete operation.
+///
+/// @return FAIL if undo failed, OK otherwise.
int op_delete(oparg_T *oap)
{
int n;
@@ -1568,6 +1625,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);
/*
@@ -1584,7 +1646,7 @@ int op_delete(oparg_T *oap)
if (*ptr != NUL) {
ptr += oap->inclusive;
}
- ptr = skipwhite(ptr);
+ ptr = (char_u *)skipwhite((char *)ptr);
if (*ptr == NUL && inindent(0)) {
oap->motion_type = kMTLineWise;
}
@@ -1620,20 +1682,20 @@ int op_delete(oparg_T *oap)
yankreg_T *reg = NULL;
int did_yank = false;
if (oap->regname != 0) {
- // yank without message
- did_yank = op_yank(oap, false, true);
- if (!did_yank) {
- // op_yank failed, don't do anything
+ // check for read-only register
+ if (!valid_yank_reg(oap->regname, true)) {
+ beep_flush();
return OK;
}
+ reg = get_yank_register(oap->regname, YREG_YANK); // yank into specif'd reg
+ op_yank_reg(oap, false, reg, is_append_register(oap->regname)); // yank without message
+ did_yank = true;
}
- /*
- * Put deleted text into register 1 and shift number registers if the
- * delete contains a line break, or when a regname has been specified.
- */
- if (oap->regname != 0 || oap->motion_type == kMTLineWise
- || oap->line_count > 1 || oap->use_reg_one) {
+ // Put deleted text into register 1 and shift number registers if the
+ // delete contains a line break, or when using a specific operator (Vi
+ // compatible)
+ if (oap->motion_type == kMTLineWise || oap->line_count > 1 || oap->use_reg_one) {
shift_delete_registers(is_append_register(oap->regname));
reg = get_global_reg(1);
op_yank_reg(oap, false, reg, false);
@@ -1694,10 +1756,10 @@ int op_delete(oparg_T *oap)
oldp += bd.textcol + bd.textlen;
STRMOVE(newp + bd.textcol + bd.startspaces + bd.endspaces, oldp);
// replace the line
- ml_replace(lnum, newp, false);
+ ml_replace(lnum, (char *)newp, false);
- extmark_splice_cols(curbuf, (int)lnum-1, bd.textcol,
- bd.textlen, bd.startspaces+bd.endspaces,
+ extmark_splice_cols(curbuf, (int)lnum - 1, bd.textcol,
+ bd.textlen, bd.startspaces + bd.endspaces,
kExtmarkUndo);
}
@@ -1732,7 +1794,7 @@ int op_delete(oparg_T *oap)
truncate_line(false); // delete the rest of the line
extmark_splice_cols(curbuf,
- (int)curwin->w_cursor.lnum-1, curwin->w_cursor.col,
+ (int)curwin->w_cursor.lnum - 1, curwin->w_cursor.col,
old_len - curwin->w_cursor.col, 0, kExtmarkUndo);
// leave cursor past last char in line
@@ -1852,8 +1914,8 @@ int op_delete(oparg_T *oap)
curwin->w_cursor = curpos; // restore curwin->w_cursor
(void)do_join(2, false, false, false, false);
curbuf_splice_pending--;
- extmark_splice(curbuf, (int)startpos.lnum-1, startpos.col,
- (int)oap->line_count-1, n, deleted_bytes,
+ extmark_splice(curbuf, (int)startpos.lnum - 1, startpos.col,
+ (int)oap->line_count - 1, n, deleted_bytes,
0, 0, 0, kExtmarkUndo);
}
if (oap->op_type == OP_DELETE) {
@@ -1864,59 +1926,53 @@ 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.cmod_flags & CMOD_LOCKMARKS) == 0) {
+ 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;
}
-/*
- * Adjust end of operating area for ending on a multi-byte character.
- * Used for deletion.
- */
+/// Adjust end of operating area for ending on a multi-byte character.
+/// Used for deletion.
static void mb_adjust_opend(oparg_T *oap)
{
- char_u *p;
-
if (oap->inclusive) {
- p = ml_get(oap->end.lnum);
+ char *p = (char *)ml_get(oap->end.lnum);
oap->end.col += mb_tail_off(p, p + oap->end.col);
}
}
-/*
- * Put character 'c' at position 'lp'
- */
+/// Put character 'c' at position 'lp'
static inline void pbyte(pos_T lp, int c)
{
assert(c <= UCHAR_MAX);
*(ml_get_buf(curbuf, lp.lnum, true) + lp.col) = (char_u)c;
if (!curbuf_splice_pending) {
- extmark_splice_cols(curbuf, (int)lp.lnum-1, lp.col, 1, 1, kExtmarkUndo);
+ extmark_splice_cols(curbuf, (int)lp.lnum - 1, lp.col, 1, 1, kExtmarkUndo);
}
}
-// Replace the character under the cursor with "c".
-// This takes care of multi-byte characters.
+/// Replace the character under the cursor with "c".
+/// This takes care of multi-byte characters.
static void replace_character(int c)
{
const int n = State;
- State = REPLACE;
+ State = MODE_REPLACE;
ins_char(c);
State = n;
// Backup to the replaced character.
dec_cursor();
}
-/*
- * Replace a whole area with one character.
- */
+/// Replace a whole area with one character.
static int op_replace(oparg_T *oap, int c)
{
int n, numc;
@@ -2025,7 +2081,7 @@ static int op_replace(oparg_T *oap, int c)
// strlen(newp) at this point
int newp_len = bd.textcol + bd.startspaces;
while (--num_chars >= 0) {
- newp_len += utf_char2bytes(c, newp + newp_len);
+ newp_len += utf_char2bytes(c, (char *)newp + newp_len);
}
if (!bd.is_short) {
// insert post-spaces
@@ -2043,19 +2099,19 @@ static int op_replace(oparg_T *oap, int c)
newrows = 1;
}
// replace the line
- ml_replace(curwin->w_cursor.lnum, newp, false);
+ ml_replace(curwin->w_cursor.lnum, (char *)newp, false);
curbuf_splice_pending++;
linenr_T baselnum = curwin->w_cursor.lnum;
if (after_p != NULL) {
- ml_append(curwin->w_cursor.lnum++, after_p, (int)after_p_len, false);
+ ml_append(curwin->w_cursor.lnum++, (char *)after_p, (int)after_p_len, false);
appended_lines_mark(curwin->w_cursor.lnum, 1L);
oap->end.lnum++;
xfree(after_p);
}
curbuf_splice_pending--;
- extmark_splice(curbuf, (int)baselnum-1, bd.textcol,
+ extmark_splice(curbuf, (int)baselnum - 1, bd.textcol,
0, bd.textlen, bd.textlen,
- newrows, newcols, newrows+newcols, kExtmarkUndo);
+ newrows, newcols, newrows + newcols, kExtmarkUndo);
}
} else {
// Characterwise or linewise motion replace.
@@ -2075,11 +2131,14 @@ static int op_replace(oparg_T *oap, int c)
while (ltoreq(curwin->w_cursor, oap->end)) {
n = gchar_cursor();
if (n != NUL) {
- if (utf_char2len(c) > 1 || utf_char2len(n) > 1) {
+ int new_byte_len = utf_char2len(c);
+ int old_byte_len = utfc_ptr2len((char *)get_cursor_pos_ptr());
+
+ if (new_byte_len > 1 || old_byte_len > 1) {
// This is slow, but it handles replacing a single-byte
// with a multi-byte and the other way around.
if (curwin->w_cursor.lnum == oap->end.lnum) {
- oap->end.col += utf_char2len(c) - utf_char2len(n);
+ oap->end.col += new_byte_len - old_byte_len;
}
replace_character(c);
} else {
@@ -2135,17 +2194,16 @@ 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.cmod_flags & CMOD_LOCKMARKS) == 0) {
+ // Set "'[" and "']" marks.
+ curbuf->b_op_start = oap->start;
+ curbuf->b_op_end = oap->end;
+ }
return OK;
}
-
-/*
- * Handle the (non-standard vi) tilde operator. Also for "gu", "gU" and "g?".
- */
+/// Handle the (non-standard vi) tilde operator. Also for "gu", "gU" and "g?".
void op_tilde(oparg_T *oap)
{
pos_T pos;
@@ -2206,11 +2264,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.cmod_flags & CMOD_LOCKMARKS) == 0) {
+ // 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",
@@ -2219,20 +2277,20 @@ void op_tilde(oparg_T *oap)
}
}
-/*
- * Invoke swapchar() on "length" bytes at position "pos".
- * "pos" is advanced to just after the changed characters.
- * "length" is rounded up to include the whole last multi-byte character.
- * Also works correctly when the number of bytes changes.
- * Returns TRUE if some character was changed.
- */
+/// Invoke swapchar() on "length" bytes at position "pos".
+///
+/// @param pos is advanced to just after the changed characters.
+/// @param length is rounded up to include the whole last multi-byte character.
+/// Also works correctly when the number of bytes changes.
+///
+/// @return TRUE if some character was changed.
static int swapchars(int op_type, pos_T *pos, int length)
FUNC_ATTR_NONNULL_ALL
{
int did_change = 0;
for (int todo = length; todo > 0; todo--) {
- const int len = utfc_ptr2len(ml_get_pos(pos));
+ const int len = utfc_ptr2len((char *)ml_get_pos(pos));
// we're counting bytes, not characters
if (len > 0) {
@@ -2246,11 +2304,13 @@ static int swapchars(int op_type, pos_T *pos, int length)
return did_change;
}
-// If op_type == OP_UPPER: make uppercase,
-// if op_type == OP_LOWER: make lowercase,
-// if op_type == OP_ROT13: do rot13 encoding,
-// else swap case of character at 'pos'
-// returns true when something actually changed.
+/// @param op_type
+/// == OP_UPPER: make uppercase,
+/// == OP_LOWER: make lowercase,
+/// == OP_ROT13: do rot13 encoding,
+/// else swap case of character at 'pos'
+///
+/// @return true when something actually changed.
bool swapchar(int op_type, pos_T *pos)
FUNC_ATTR_NONNULL_ARG(2)
{
@@ -2293,7 +2353,7 @@ bool swapchar(int op_type, pos_T *pos)
curwin->w_cursor = *pos;
// don't use del_char(), it also removes composing chars
- del_bytes(utf_ptr2len(get_cursor_pos_ptr()), false, false);
+ del_bytes(utf_ptr2len((char *)get_cursor_pos_ptr()), false, false);
ins_char(nc);
curwin->w_cursor = sp;
} else {
@@ -2304,9 +2364,7 @@ bool swapchar(int op_type, pos_T *pos)
return false;
}
-/*
- * op_insert - Insert and append operators for Visual mode.
- */
+/// Insert and append operators for Visual mode.
void op_insert(oparg_T *oap, long count1)
{
long ins_len, pre_textlen = 0;
@@ -2329,19 +2387,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);
@@ -2389,6 +2450,7 @@ void op_insert(oparg_T *oap, long count1)
}
t1 = oap->start;
+ const pos_T start_insert = curwin->w_cursor;
(void)edit(NUL, false, (linenr_T)count1);
// When a tab was inserted, and the characters in front of the tab
@@ -2423,23 +2485,18 @@ void op_insert(oparg_T *oap, long count1)
// The user may have moved the cursor before inserting something, try
// to adjust the block for that. But only do it, if the difference
// does not come from indent kicking in.
- if (oap->start.lnum == curbuf->b_op_start_orig.lnum
- && !bd.is_MAX
- && !did_indent) {
+ if (oap->start.lnum == curbuf->b_op_start_orig.lnum && !bd.is_MAX && !did_indent) {
+ const int t = getviscol2(curbuf->b_op_start_orig.col, curbuf->b_op_start_orig.coladd);
+
if (oap->op_type == OP_INSERT
&& oap->start.col + oap->start.coladd
!= curbuf->b_op_start_orig.col + curbuf->b_op_start_orig.coladd) {
- int t = getviscol2(curbuf->b_op_start_orig.col,
- curbuf->b_op_start_orig.coladd);
oap->start.col = curbuf->b_op_start_orig.col;
pre_textlen -= t - oap->start_vcol;
oap->start_vcol = t;
} else if (oap->op_type == OP_APPEND
- && oap->end.col + oap->end.coladd
- >= curbuf->b_op_start_orig.col
- + curbuf->b_op_start_orig.coladd) {
- int t = getviscol2(curbuf->b_op_start_orig.col,
- curbuf->b_op_start_orig.coladd);
+ && oap->start.col + oap->start.coladd
+ >= curbuf->b_op_start_orig.col + curbuf->b_op_start_orig.coladd) {
oap->start.col = curbuf->b_op_start_orig.col;
// reset pre_textlen to the value of OP_INSERT
pre_textlen += bd.textlen;
@@ -2487,15 +2544,27 @@ void op_insert(oparg_T *oap, long count1)
firstline = ml_get(oap->start.lnum);
const size_t len = STRLEN(firstline);
colnr_T add = bd.textcol;
+ colnr_T offset = 0; // offset when cursor was moved in insert mode
if (oap->op_type == OP_APPEND) {
add += bd.textlen;
+ // account for pressing cursor in insert mode when '$' was used
+ if (bd.is_MAX && start_insert.lnum == Insstart.lnum && start_insert.col > Insstart.col) {
+ offset = start_insert.col - Insstart.col;
+ add -= offset;
+ if (oap->end_vcol > offset) {
+ oap->end_vcol -= offset + 1;
+ } else {
+ // moved outside of the visual block, what to do?
+ return;
+ }
+ }
}
if ((size_t)add > len) {
firstline += len; // short line, point to the NUL
} else {
firstline += add;
}
- ins_len = (long)STRLEN(firstline) - pre_textlen;
+ ins_len = (long)STRLEN(firstline) - pre_textlen - offset;
if (pre_textlen >= 0 && ins_len > 0) {
ins_text = vim_strnsave(firstline, (size_t)ins_len);
// block handled here
@@ -2510,11 +2579,9 @@ void op_insert(oparg_T *oap, long count1)
}
}
-/*
- * op_change - handle a change operation
- *
- * return TRUE if edit() returns because of a CTRL-O command
- */
+/// handle a change operation
+///
+/// @return TRUE if edit() returns because of a CTRL-O command
int op_change(oparg_T *oap)
{
colnr_T l;
@@ -2533,10 +2600,7 @@ int op_change(oparg_T *oap)
l = oap->start.col;
if (oap->motion_type == kMTLineWise) {
l = 0;
- if (!p_paste && curbuf->b_p_si
- && !curbuf->b_p_cin) {
- can_si = true; // It's like opening a new line, do si
- }
+ can_si = may_do_si(); // Like opening a new line, do smart indent
}
// First delete the text in the region. In an empty buffer only need to
@@ -2623,9 +2687,9 @@ int op_change(oparg_T *oap)
offset += ins_len;
oldp += bd.textcol;
STRMOVE(newp + offset, oldp);
- ml_replace(linenr, newp, false);
- extmark_splice_cols(curbuf, (int)linenr-1, bd.textcol,
- 0, vpos.coladd+(int)ins_len, kExtmarkUndo);
+ ml_replace(linenr, (char *)newp, false);
+ extmark_splice_cols(curbuf, (int)linenr - 1, bd.textcol,
+ 0, vpos.coladd + (int)ins_len, kExtmarkUndo);
}
}
check_cursor();
@@ -2638,6 +2702,7 @@ int op_change(oparg_T *oap)
return retval;
}
+
/*
* set all the yank registers to empty (called from main())
*/
@@ -2658,10 +2723,10 @@ void clear_registers(void)
#endif
-
/// Free contents of yankreg `reg`.
/// Called for normal freeing and in case of error.
-/// `reg` must not be NULL (but `reg->y_array` might be)
+///
+/// @param reg must not be NULL (but `reg->y_array` might be)
void free_register(yankreg_T *reg)
FUNC_ATTR_NONNULL_ALL
{
@@ -2677,12 +2742,12 @@ void free_register(yankreg_T *reg)
/// Yanks the text between "oap->start" and "oap->end" into a yank register.
/// If we are to append (uppercase register), we first yank into a new yank
/// register and then concatenate the old and the new one.
+/// Do not call this from a delete operation. Use op_yank_reg() instead.
///
/// @param oap operator arguments
/// @param message show message when more than `&report` lines are yanked.
-/// @param deleting whether the function was called from a delete operation.
/// @returns whether the operation register was writable.
-bool op_yank(oparg_T *oap, bool message, int deleting)
+bool op_yank(oparg_T *oap, bool message)
FUNC_ATTR_NONNULL_ALL
{
// check for read-only register
@@ -2701,11 +2766,8 @@ bool op_yank(oparg_T *oap, bool message, int deleting)
return eval_yank_userreg(curbuf->b_p_urf, oap->regname, reg) != -1;
}
- // op_delete will set_clipboard and do_autocmd
- if (!deleting) {
- set_clipboard(oap->regname, reg);
- do_autocmd_textyankpost(oap, reg);
- }
+ set_clipboard(oap->regname, reg);
+ do_autocmd_textyankpost(oap, reg);
return true;
}
@@ -2713,7 +2775,7 @@ bool op_yank(oparg_T *oap, bool message, int deleting)
static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
{
yankreg_T newreg; // new yank register when appending
- char_u **new_ptr;
+ char **new_ptr;
linenr_T lnum; // current line number
size_t j;
MotionType yank_type = oap->motion_type;
@@ -2747,7 +2809,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
reg->y_size = yanklines;
reg->y_type = yank_type; // set the yank register type
reg->y_width = 0;
- reg->y_array = xcalloc(yanklines, sizeof(char_u *));
+ reg->y_array = xcalloc(yanklines, sizeof(char *));
reg->additional_data = NULL;
reg->timestamp = os_time();
@@ -2771,7 +2833,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
break;
case kMTLineWise:
- reg->y_array[y_idx] = vim_strsave(ml_get(lnum));
+ reg->y_array[y_idx] = (char *)vim_strsave(ml_get(lnum));
break;
case kMTCharWise: {
@@ -2841,7 +2903,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
if (curr != reg) { // append the new block to the old block
new_ptr = xmalloc(sizeof(char_u *) * (curr->y_size + reg->y_size));
- for (j = 0; j < curr->y_size; ++j) {
+ for (j = 0; j < curr->y_size; j++) {
new_ptr[j] = curr->y_array[j];
}
xfree(curr->y_array);
@@ -2862,7 +2924,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
STRCAT(pnew, reg->y_array[0]);
xfree(curr->y_array[j]);
xfree(reg->y_array[0]);
- curr->y_array[j++] = pnew;
+ curr->y_array[j++] = (char *)pnew;
y_idx = 1;
} else {
y_idx = 0;
@@ -2873,10 +2935,8 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
curr->y_size = j;
xfree(reg->y_array);
}
- if (curwin->w_p_rnu) {
- redraw_later(curwin, SOME_VALID); // cursor moved to start
- }
- if (message) { // Display message about yank?
+
+ if (message && (p_ch > 0 || ui_has(kUIMessages))) { // Display message about yank?
if (yank_type == kMTCharWise && yanklines == 1) {
yanklines = 0;
}
@@ -2904,21 +2964,20 @@ 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.cmod_flags & CMOD_LOCKMARKS) == 0) {
+ // 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;
}
-// Copy a block range into a register.
-// If "exclude_trailing_space" is set, do not copy trailing whitespaces.
+/// Copy a block range into a register.
+///
+/// @param exclude_trailing_space if true, do not copy trailing whitespaces.
static void yank_copy_line(yankreg_T *reg, struct block_def *bd, size_t y_idx,
bool exclude_trailing_space)
FUNC_ATTR_NONNULL_ALL
@@ -2929,7 +2988,7 @@ static void yank_copy_line(yankreg_T *reg, struct block_def *bd, size_t y_idx,
int size = bd->startspaces + bd->endspaces + bd->textlen;
assert(size >= 0);
char_u *pnew = xmallocz((size_t)size);
- reg->y_array[y_idx] = pnew;
+ reg->y_array[y_idx] = (char *)pnew;
memset(pnew, ' ', (size_t)bd->startspaces);
pnew += bd->startspaces;
memmove(pnew, bd->textstart, (size_t)bd->textlen);
@@ -2939,7 +2998,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--;
}
@@ -2976,7 +3035,7 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg)
(void)tv_dict_add_list(dict, S_LEN("regcontents"), list);
// Register type.
- char buf[NUMBUFLEN+2];
+ char buf[NUMBUFLEN + 2];
format_reg_type(reg->y_type, reg->y_width, buf, ARRAY_SIZE(buf));
(void)tv_dict_add_str(dict, S_LEN("regtype"), buf);
@@ -3007,13 +3066,14 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg)
recursive = false;
}
-// Put contents of register "regname" into the text.
-// Caller must check "regname" to be valid!
-// "flags": PUT_FIXINDENT make indent look nice
-// PUT_CURSEND leave cursor after end of new text
-// PUT_LINE force linewise put (":put")
-// PUT_BLOCK_INNER in block mode, do not add trailing spaces
-// dir: BACKWARD for 'P', FORWARD for 'p'
+/// Put contents of register "regname" into the text.
+/// Caller must check "regname" to be valid!
+///
+/// @param flags PUT_FIXINDENT make indent look nice
+/// PUT_CURSEND leave cursor after end of new text
+/// PUT_LINE force linewise put (":put")
+/// PUT_BLOCK_INNER in block mode, do not add trailing spaces
+/// @param dir BACKWARD for 'P', FORWARD for 'p'
void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
{
char_u *ptr;
@@ -3033,7 +3093,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
int incr = 0;
struct block_def bd;
char_u **y_array = NULL;
- long nr_lines = 0;
+ linenr_T nr_lines = 0;
pos_T new_cursor;
int indent;
int orig_indent = 0; // init for gcc
@@ -3044,6 +3104,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();
@@ -3111,10 +3174,10 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
bool one_past_line = (*cursor_pos == NUL);
bool eol = false;
if (!one_past_line) {
- eol = (*(cursor_pos + utfc_ptr2len(cursor_pos)) == NUL);
+ eol = (*(cursor_pos + utfc_ptr2len((char *)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)) {
@@ -3166,8 +3229,8 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
if (y_array != NULL) {
y_array[y_size] = ptr;
}
- ++y_size;
- ptr = vim_strchr(ptr, '\n');
+ y_size++;
+ ptr = (char_u *)vim_strchr((char *)ptr, '\n');
if (ptr != NULL) {
if (y_array != NULL) {
*ptr = NUL;
@@ -3200,7 +3263,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
y_type = reg->y_type;
y_width = reg->y_width;
y_size = reg->y_size;
- y_array = reg->y_array;
+ y_array = (char_u **)reg->y_array;
}
if (curbuf->terminal) {
@@ -3220,7 +3283,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
MB_PTR_ADV(p);
}
ptr = vim_strsave(p);
- ml_append(curwin->w_cursor.lnum, ptr, (colnr_T)0, false);
+ ml_append(curwin->w_cursor.lnum, (char *)ptr, (colnr_T)0, false);
xfree(ptr);
oldp = get_cursor_line_ptr();
@@ -3229,7 +3292,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
MB_PTR_ADV(p);
}
ptr = vim_strnsave(oldp, (size_t)(p - oldp));
- ml_replace(curwin->w_cursor.lnum, ptr, false);
+ ml_replace(curwin->w_cursor.lnum, (char *)ptr, false);
nr_lines++;
dir = FORWARD;
}
@@ -3290,13 +3353,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 {
@@ -3318,23 +3382,22 @@ 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);
}
// move to start of next multi-byte character
- curwin->w_cursor.col += utfc_ptr2len(get_cursor_pos_ptr());
+ curwin->w_cursor.col += utfc_ptr2len((char *)get_cursor_pos_ptr());
col++;
} else {
getvcol(curwin, &curwin->w_cursor, &col, NULL, &endcol2);
}
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++;
}
@@ -3366,7 +3429,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
// add a new line
if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) {
- if (ml_append(curbuf->b_ml.ml_line_count, (char_u *)"",
+ if (ml_append(curbuf->b_ml.ml_line_count, "",
(colnr_T)1, false) == FAIL) {
break;
}
@@ -3416,18 +3479,27 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
}
}
- // insert the new text
+ // Insert the new text.
+ // First check for multiplication overflow.
+ if (yanklen + spaces != 0
+ && count > ((INT_MAX - (bd.startspaces + bd.endspaces)) / (yanklen + spaces))) {
+ emsg(_(e_resulting_text_too_long));
+ break;
+ }
+
totlen = (size_t)(count * (yanklen + spaces)
+ bd.startspaces + bd.endspaces);
- int addcount = (int)totlen + lines_appended;
newp = (char_u *)xmalloc(totlen + oldlen + 1);
+
// copy part up to cursor to new line
ptr = newp;
memmove(ptr, oldp, (size_t)bd.textcol);
ptr += bd.textcol;
+
// may insert some spaces before the new text
memset(ptr, ' ', (size_t)bd.startspaces);
ptr += bd.startspaces;
+
// insert the new text
for (long j = 0; j < count; j++) {
memmove(ptr, y_array[i], (size_t)yanklen);
@@ -3438,19 +3510,21 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
memset(ptr, ' ', (size_t)spaces);
ptr += spaces;
} else {
- addcount -= spaces;
+ totlen -= (size_t)spaces; // didn't use these spaces
}
}
+
// may insert some spaces after the new text
memset(ptr, ' ', (size_t)bd.endspaces);
ptr += bd.endspaces;
+
// move the text after the cursor to the end of the line.
int columns = (int)oldlen - bd.textcol - delcount + 1;
assert(columns >= 0);
memmove(ptr, oldp + bd.textcol + delcount, (size_t)columns);
- ml_replace(curwin->w_cursor.lnum, newp, false);
- extmark_splice_cols(curbuf, (int)curwin->w_cursor.lnum-1, bd.textcol,
- delcount, addcount, kExtmarkUndo);
+ ml_replace(curwin->w_cursor.lnum, (char *)newp, false);
+ extmark_splice_cols(curbuf, (int)curwin->w_cursor.lnum - 1, bd.textcol,
+ delcount, (int)totlen + lines_appended, kExtmarkUndo);
++curwin->w_cursor.lnum;
if (i == 0) {
@@ -3459,7 +3533,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
}
changed_lines(lnum, 0, curbuf->b_op_start.lnum + (linenr_T)y_size
- - (linenr_T)nr_lines, nr_lines, true);
+ - nr_lines, nr_lines, true);
// Set '[ mark.
curbuf->b_op_start = curwin->w_cursor;
@@ -3489,7 +3563,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
// if type is kMTCharWise, FORWARD is the same as BACKWARD on the next
// char
if (dir == FORWARD && gchar_cursor() != NUL) {
- int bytelen = utfc_ptr2len(get_cursor_pos_ptr());
+ int bytelen = utfc_ptr2len((char *)get_cursor_pos_ptr());
// put it on the next of the multi-byte character.
col += bytelen;
@@ -3532,10 +3606,18 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
}
}
- do {
+ if (count == 0 || yanklen == 0) {
+ if (VIsual_active) {
+ lnum = end_lnum;
+ }
+ } else if (count > INT_MAX / yanklen) {
+ // multiplication overflow
+ emsg(_(e_resulting_text_too_long));
+ } else {
totlen = (size_t)(count * yanklen);
- if (totlen > 0) {
+ do {
oldp = ml_get(lnum);
+ oldlen = STRLEN(oldp);
if (lnum > start_lnum) {
pos_T pos = {
.lnum = lnum,
@@ -3546,11 +3628,11 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
col = MAXCOL;
}
}
- if (VIsual_active && col > (int)STRLEN(oldp)) {
+ if (VIsual_active && col > (colnr_T)oldlen) {
lnum++;
continue;
}
- newp = (char_u *)xmalloc((size_t)(STRLEN(oldp) + totlen + 1));
+ newp = (char_u *)xmalloc(totlen + oldlen + 1);
memmove(newp, oldp, (size_t)col);
ptr = newp + col;
for (i = 0; i < (size_t)count; i++) {
@@ -3558,7 +3640,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
ptr += yanklen;
}
STRMOVE(ptr, oldp + col);
- ml_replace(lnum, newp, false);
+ ml_replace(lnum, (char *)newp, false);
// compute the byte offset for the last character
first_byte_off = utf_head_off(newp, ptr - 1);
@@ -3570,16 +3652,16 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
curwin->w_cursor.col += (colnr_T)(totlen - 1);
}
changed_bytes(lnum, col);
- extmark_splice_cols(curbuf, (int)lnum-1, col,
+ extmark_splice_cols(curbuf, (int)lnum - 1, col,
0, (int)totlen, kExtmarkUndo);
- }
- if (VIsual_active) {
- lnum++;
- }
- } while (VIsual_active && lnum <= end_lnum);
+ if (VIsual_active) {
+ lnum++;
+ }
+ } while (VIsual_active && lnum <= end_lnum);
- if (VIsual_active) { // reset lnum to the last visual line
- lnum--;
+ if (VIsual_active) { // reset lnum to the last visual line
+ lnum--;
+ }
}
// put '] at the first byte of the last character
@@ -3593,6 +3675,9 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
curwin->w_cursor.col -= first_byte_off;
}
} else {
+ linenr_T new_lnum = new_cursor.lnum;
+ size_t len;
+
// Insert at least one line. When y_type is kMTCharWise, break the first
// line in two.
for (cnt = 1; cnt <= count; cnt++) {
@@ -3608,7 +3693,8 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
STRCPY(newp, y_array[y_size - 1]);
STRCAT(newp, ptr);
// insert second line
- ml_append(lnum, newp, (colnr_T)0, false);
+ ml_append(lnum, (char *)newp, (colnr_T)0, false);
+ new_lnum++;
xfree(newp);
oldp = ml_get(lnum);
@@ -3617,17 +3703,18 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
memmove(newp, oldp, (size_t)col);
// append to first line
memmove(newp + col, y_array[0], (size_t)yanklen + 1);
- ml_replace(lnum, newp, false);
+ ml_replace(lnum, (char *)newp, false);
curwin->w_cursor.lnum = lnum;
i = 1;
}
for (; i < y_size; i++) {
- if ((y_type != kMTCharWise || i < y_size - 1)
- && ml_append(lnum, y_array[i], (colnr_T)0, false)
- == FAIL) {
- goto error;
+ if ((y_type != kMTCharWise || i < y_size - 1)) {
+ if (ml_append(lnum, (char *)y_array[i], (colnr_T)0, false) == FAIL) {
+ goto error;
+ }
+ new_lnum++;
}
lnum++;
++nr_lines;
@@ -3662,20 +3749,24 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
int lastsize = 0;
if (y_type == kMTCharWise
|| (y_type == kMTLineWise && flags & PUT_LINE_SPLIT)) {
- for (i = 0; i < y_size-1; i++) {
+ for (i = 0; i < y_size - 1; i++) {
totsize += (bcount_t)STRLEN(y_array[i]) + 1;
}
- lastsize = (int)STRLEN(y_array[y_size-1]);
+ lastsize = (int)STRLEN(y_array[y_size - 1]);
totsize += lastsize;
}
if (y_type == kMTCharWise) {
- extmark_splice(curbuf, (int)new_cursor.lnum-1, col, 0, 0, 0,
- (int)y_size-1, lastsize, totsize,
+ extmark_splice(curbuf, (int)new_cursor.lnum - 1, col, 0, 0, 0,
+ (int)y_size - 1, lastsize, totsize,
kExtmarkUndo);
} else if (y_type == kMTLineWise && flags & PUT_LINE_SPLIT) {
// Account for last pasted NL + last NL
- extmark_splice(curbuf, (int)new_cursor.lnum-1, col + 1, 0, 0, 0,
- (int)y_size+1, 0, totsize+2, kExtmarkUndo);
+ extmark_splice(curbuf, (int)new_cursor.lnum - 1, col + 1, 0, 0, 0,
+ (int)y_size + 1, 0, totsize + 2, kExtmarkUndo);
+ }
+
+ if (cnt == 1) {
+ new_lnum = lnum;
}
}
@@ -3704,11 +3795,15 @@ error:
// Put the '] mark on the first byte of the last inserted character.
// Correct the length for change in indent.
- curbuf->b_op_end.lnum = lnum;
- col = (colnr_T)STRLEN(y_array[y_size - 1]) - lendiff;
+ curbuf->b_op_end.lnum = new_lnum;
+ len = STRLEN(y_array[y_size - 1]);
+ col = (colnr_T)len - lendiff;
if (col > 1) {
- curbuf->b_op_end.col = col - 1 - utf_head_off(y_array[y_size - 1],
- y_array[y_size - 1] + col - 1);
+ curbuf->b_op_end.col = col - 1;
+ if (len > 0) {
+ curbuf->b_op_end.col -= utf_head_off(y_array[y_size - 1],
+ y_array[y_size - 1] + len - 1);
+ }
} else {
curbuf->b_op_end.col = 0;
}
@@ -3727,8 +3822,12 @@ error:
}
curwin->w_cursor.col = 0;
} else {
- curwin->w_cursor.lnum = lnum;
+ curwin->w_cursor.lnum = new_lnum;
curwin->w_cursor.col = col;
+ curbuf->b_op_end = curwin->w_cursor;
+ if (col > 1) {
+ curbuf->b_op_end.col = col - 1;
+ }
}
} else if (y_type == kMTLineWise) {
// put cursor on first non-blank in first inserted line
@@ -3747,6 +3846,10 @@ error:
curwin->w_set_curswant = TRUE;
end:
+ if (cmdmod.cmod_flags & CMOD_LOCKMARKS) {
+ curbuf->b_op_start = orig_start;
+ curbuf->b_op_end = orig_end;
+ }
if (allocated) {
xfree(insert_string);
}
@@ -3758,22 +3861,22 @@ end:
// If the cursor is past the end of the line put it at the end.
adjust_cursor_eol();
-} // NOLINT(readability/fn_size)
+}
-/*
- * When the cursor is on the NUL past the end of the line and it should not be
- * there move it left.
- */
+/// When the cursor is on the NUL past the end of the line and it should not be
+/// there move it left.
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
- && !(restart_edit || (State & INSERT))) {
+ && (cur_ve_flags & VE_ONEMORE) == 0
+ && !(restart_edit || (State & MODE_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.
@@ -3783,9 +3886,7 @@ void adjust_cursor_eol(void)
}
}
-/*
- * Return TRUE if lines starting with '#' should be left aligned.
- */
+/// @return TRUE if lines starting with '#' should be left aligned.
int preprocs_left(void)
{
return ((curbuf->b_p_si && !curbuf->b_p_cin)
@@ -3793,7 +3894,7 @@ int preprocs_left(void)
&& curbuf->b_ind_hash_comment == 0));
}
-// Return the character name of the register with the given number
+/// @return the character name of the register with the given number
int get_register_name(int num)
{
if (num == -1) {
@@ -3817,17 +3918,15 @@ int get_unname_register(void)
return yankmap_find(&y_regs.inner, y_previous);
}
-/*
- * ":dis" and ":registers": Display the contents of the yank registers.
- */
+/// ":dis" and ":registers": Display the contents of the yank registers.
void ex_display(exarg_T *eap)
{
char_u *p;
yankreg_T *yb;
int name;
- char_u *arg = eap->arg;
+ char_u *arg = (char_u *)eap->arg;
int clen;
- char_u type[2];
+ int type;
if (arg != NULL && *arg == NUL) {
arg = NULL;
@@ -3840,18 +3939,17 @@ void ex_display(exarg_T *eap)
name = get_register_name(i);
switch (get_reg_type(name, NULL)) {
case kMTLineWise:
- type[0] = 'l'; break;
+ type = 'l'; break;
case kMTCharWise:
- type[0] = 'c'; break;
+ type = 'c'; break;
default:
- type[0] = 'b'; break;
+ type = 'b'; break;
}
- if (arg != NULL && vim_strchr(arg, name) == NULL) {
+ if (arg != NULL && vim_strchr((char *)arg, name) == NULL) {
continue; // did not ask for this register
}
-
if (i == -1) {
if (y_previous != NULL) {
yb = y_previous;
@@ -3871,88 +3969,88 @@ void ex_display(exarg_T *eap)
}
if (yb->y_array != NULL) {
- msg_putchar('\n');
- msg_puts(" ");
- msg_putchar(type[0]);
- msg_puts(" ");
- msg_putchar('"');
- msg_putchar(name);
- msg_puts(" ");
-
- int n = Columns - 11;
- for (size_t j = 0; j < yb->y_size && n > 1; j++) {
- if (j) {
- msg_puts_attr("^J", attr);
- n -= 2;
+ bool do_show = false;
+
+ for (size_t j = 0; !do_show && j < yb->y_size; j++) {
+ do_show = !message_filtered((char_u *)yb->y_array[j]);
+ }
+
+ if (do_show || yb->y_size == 0) {
+ msg_putchar('\n');
+ msg_puts(" ");
+ msg_putchar(type);
+ msg_puts(" ");
+ msg_putchar('"');
+ msg_putchar(name);
+ msg_puts(" ");
+
+ int n = Columns - 11;
+ for (size_t j = 0; j < yb->y_size && n > 1; j++) {
+ if (j) {
+ msg_puts_attr("^J", attr);
+ n -= 2;
+ }
+ for (p = (char_u *)yb->y_array[j];
+ *p != NUL && (n -= ptr2cells((char *)p)) >= 0; p++) { // -V1019
+ clen = utfc_ptr2len((char *)p);
+ msg_outtrans_len(p, clen);
+ p += clen - 1;
+ }
}
- for (p = yb->y_array[j]; *p && (n -= ptr2cells(p)) >= 0; p++) { // -V1019 NOLINT(whitespace/line_length)
- clen = utfc_ptr2len(p);
- msg_outtrans_len(p, clen);
- p += clen - 1;
+ if (n > 1 && yb->y_type == kMTLineWise) {
+ msg_puts_attr("^J", attr);
}
+ ui_flush(); // show one line at a time
}
- if (n > 1 && yb->y_type == kMTLineWise) {
- msg_puts_attr("^J", attr);
- }
- ui_flush(); // show one line at a time
+ os_breakcheck();
}
- os_breakcheck();
}
- /*
- * display last inserted text
- */
+ // display last inserted text
if ((p = get_last_insert()) != NULL
- && (arg == NULL || vim_strchr(arg, '.') != NULL) && !got_int) {
+ && (arg == NULL || vim_strchr((char *)arg, '.') != NULL) && !got_int
+ && !message_filtered(p)) {
msg_puts("\n c \". ");
dis_msg(p, true);
}
- /*
- * display last command line
- */
- if (last_cmdline != NULL && (arg == NULL || vim_strchr(arg, ':') != NULL)
- && !got_int) {
+ // display last command line
+ if (last_cmdline != NULL && (arg == NULL || vim_strchr((char *)arg, ':') != NULL)
+ && !got_int && !message_filtered(last_cmdline)) {
msg_puts("\n c \": ");
dis_msg(last_cmdline, false);
}
- /*
- * display current file name
- */
+ // display current file name
if (curbuf->b_fname != NULL
- && (arg == NULL || vim_strchr(arg, '%') != NULL) && !got_int) {
+ && (arg == NULL || vim_strchr((char *)arg, '%') != NULL) && !got_int
+ && !message_filtered((char_u *)curbuf->b_fname)) {
msg_puts("\n c \"% ");
- dis_msg(curbuf->b_fname, false);
+ dis_msg((char_u *)curbuf->b_fname, false);
}
- /*
- * display alternate file name
- */
- if ((arg == NULL || vim_strchr(arg, '%') != NULL) && !got_int) {
- char_u *fname;
+ // display alternate file name
+ if ((arg == NULL || vim_strchr((char *)arg, '%') != NULL) && !got_int) {
+ char *fname;
linenr_T dummy;
- if (buflist_name_nr(0, &fname, &dummy) != FAIL) {
+ if (buflist_name_nr(0, &fname, &dummy) != FAIL && !message_filtered((char_u *)fname)) {
msg_puts("\n c \"# ");
- dis_msg(fname, false);
+ dis_msg((char_u *)fname, false);
}
}
- /*
- * display last search pattern
- */
+ // display last search pattern
if (last_search_pat() != NULL
- && (arg == NULL || vim_strchr(arg, '/') != NULL) && !got_int) {
+ && (arg == NULL || vim_strchr((char *)arg, '/') != NULL) && !got_int
+ && !message_filtered(last_search_pat())) {
msg_puts("\n c \"/ ");
dis_msg(last_search_pat(), false);
}
- /*
- * display last used expression
- */
- if (expr_line != NULL && (arg == NULL || vim_strchr(arg, '=') != NULL)
- && !got_int) {
+ // display last used expression
+ if (expr_line != NULL && (arg == NULL || vim_strchr((char *)arg, '=') != NULL)
+ && !got_int && !message_filtered(expr_line)) {
msg_puts("\n c \"= ");
dis_msg(expr_line, false);
}
@@ -3971,8 +4069,8 @@ static void dis_msg(const char_u *p, bool skip_esc)
n = Columns - 6;
while (*p != NUL
&& !(*p == ESC && skip_esc && *(p + 1) == NUL)
- && (n -= ptr2cells(p)) >= 0) {
- if ((l = utfc_ptr2len(p)) > 1) {
+ && (n -= ptr2cells((char *)p)) >= 0) {
+ if ((l = utfc_ptr2len((char *)p)) > 1) {
msg_outtrans_len(p, l);
p += l;
} else {
@@ -3997,7 +4095,7 @@ char_u *skip_comment(char_u *line, bool process, bool include_space, bool *is_co
{
char_u *comment_flags = NULL;
int lead_len;
- int leader_offset = get_last_leader_offset(line, &comment_flags);
+ int leader_offset = get_last_leader_offset((char *)line, (char **)&comment_flags);
*is_comment = false;
if (leader_offset != -1) {
@@ -4019,7 +4117,7 @@ char_u *skip_comment(char_u *line, bool process, bool include_space, bool *is_co
return line;
}
- lead_len = get_leader_len(line, &comment_flags, false, include_space);
+ lead_len = get_leader_len((char *)line, (char **)&comment_flags, false, include_space);
if (lead_len == 0) {
return line;
@@ -4047,14 +4145,14 @@ char_u *skip_comment(char_u *line, bool process, bool include_space, bool *is_co
return line;
}
-// Join 'count' lines (minimal 2) at cursor position.
-// When "save_undo" is TRUE save lines for undo first.
-// Set "use_formatoptions" to FALSE when e.g. processing backspace and comment
-// leaders should not be removed.
-// When setmark is true, sets the '[ and '] mark, else, the caller is expected
-// to set those marks.
-//
-// return FAIL for failure, OK otherwise
+/// @param count number of lines (minimal 2) to join at cursor position.
+/// @param save_undo when TRUE, save lines for undo first.
+/// @param use_formatoptions set to FALSE when e.g. processing backspace and comment
+/// leaders should not be removed.
+/// @param setmark when true, sets the '[ and '] mark, else, the caller is expected
+/// to set those marks.
+///
+/// @return FAIL for failure, OK otherwise
int do_join(size_t count, int insert_space, int save_undo, int use_formatoptions, bool setmark)
{
char_u *curr = NULL;
@@ -4091,7 +4189,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.cmod_flags & CMOD_LOCKMARKS) == 0) {
// 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);
@@ -4110,17 +4208,17 @@ int do_join(size_t count, int insert_space, int save_undo, int use_formatoptions
}
if (insert_space && t > 0) {
- curr = skipwhite(curr);
+ curr = (char_u *)skipwhite((char *)curr);
if (*curr != NUL
&& *curr != ')'
&& sumsize != 0
&& endcurr1 != TAB
&& (!has_format_option(FO_MBYTE_JOIN)
- || (utf_ptr2char(curr) < 0x100 && endcurr1 < 0x100))
+ || (utf_ptr2char((char *)curr) < 0x100 && endcurr1 < 0x100))
&& (!has_format_option(FO_MBYTE_JOIN2)
- || (utf_ptr2char(curr) < 0x100 && !utf_eat_space(endcurr1))
+ || (utf_ptr2char((char *)curr) < 0x100 && !utf_eat_space(endcurr1))
|| (endcurr1 < 0x100
- && !utf_eat_space(utf_ptr2char(curr))))) {
+ && !utf_eat_space(utf_ptr2char((char *)curr))))) {
// don't add a space if the line is ending in a space
if (endcurr1 == ' ') {
endcurr1 = endcurr2;
@@ -4135,8 +4233,8 @@ int do_join(size_t count, int insert_space, int save_undo, int use_formatoptions
}
if (t > 0 && curbuf_splice_pending == 0) {
- colnr_T removed = (int)(curr- curr_start);
- extmark_splice(curbuf, (int)curwin->w_cursor.lnum-1, sumsize,
+ colnr_T removed = (int)(curr - curr_start);
+ extmark_splice(curbuf, (int)curwin->w_cursor.lnum - 1, sumsize,
1, removed, removed + 1,
0, spaces[t], spaces[t],
kExtmarkUndo);
@@ -4147,10 +4245,10 @@ int do_join(size_t count, int insert_space, int save_undo, int use_formatoptions
if (insert_space && currsize > 0) {
cend = curr + currsize;
MB_PTR_BACK(curr, cend);
- endcurr1 = utf_ptr2char(cend);
+ endcurr1 = utf_ptr2char((char *)cend);
if (cend > curr) {
MB_PTR_BACK(curr, cend);
- endcurr2 = utf_ptr2char(cend);
+ endcurr2 = utf_ptr2char((char *)cend);
}
}
line_breakcheck();
@@ -4191,7 +4289,7 @@ int do_join(size_t count, int insert_space, int save_undo, int use_formatoptions
const int spaces_removed = (int)((curr - curr_start) - spaces[t]);
linenr_T lnum = curwin->w_cursor.lnum + t;
colnr_T mincol = (colnr_T)0;
- long lnum_amount = -t;
+ linenr_T lnum_amount = -t;
long col_amount = (cend - newp - spaces_removed);
mark_col_adjust(lnum, mincol, lnum_amount, col_amount, spaces_removed);
@@ -4205,14 +4303,14 @@ int do_join(size_t count, int insert_space, int save_undo, int use_formatoptions
curr += comments[t - 1];
}
if (insert_space && t > 1) {
- curr = skipwhite(curr);
+ curr = (char_u *)skipwhite((char *)curr);
}
currsize = (int)STRLEN(curr);
}
- ml_replace(curwin->w_cursor.lnum, newp, false);
+ ml_replace(curwin->w_cursor.lnum, (char *)newp, false);
- if (setmark) {
+ if (setmark && (cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) {
// Set the '] mark.
curwin->w_buffer->b_op_end.lnum = curwin->w_cursor.lnum;
curwin->w_buffer->b_op_end.col = sumsize;
@@ -4255,11 +4353,11 @@ theend:
return ret;
}
-/*
- * Return TRUE if the two comment leaders given are the same. "lnum" is
- * the first line. White-space is ignored. Note that the whole of
- * 'leader1' must match 'leader2_len' characters from 'leader2' -- webb
- */
+/// @return TRUE if the two comment leaders given are the same.
+///
+/// @param lnum The first line. White-space is ignored.
+///
+/// @note the whole of 'leader1' must match 'leader2_len' characters from 'leader2'.
static int same_leader(linenr_T lnum, int leader1_len, char_u *leader1_flags, int leader2_len,
char_u *leader2_flags)
{
@@ -4309,8 +4407,7 @@ static int same_leader(linenr_T lnum, int leader1_len, char_u *leader1_flags, in
* The first line has to be saved, only one line can be locked at a time.
*/
line1 = vim_strsave(ml_get(lnum));
- for (idx1 = 0; ascii_iswhite(line1[idx1]); ++idx1) {
- }
+ for (idx1 = 0; ascii_iswhite(line1[idx1]); idx1++) {}
line2 = ml_get(lnum + 1);
for (idx2 = 0; idx2 < leader2_len; ++idx2) {
if (!ascii_iswhite(line2[idx2])) {
@@ -4333,7 +4430,7 @@ static int same_leader(linenr_T lnum, int leader1_len, char_u *leader1_flags, in
/// @param keep_cursor keep cursor on same text char
static void op_format(oparg_T *oap, int keep_cursor)
{
- long old_line_count = curbuf->b_ml.ml_line_count;
+ linenr_T old_line_count = curbuf->b_ml.ml_line_count;
// Place the cursor where the "gq" or "gw" command was given, so that "u"
// can put it back there.
@@ -4350,8 +4447,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.cmod_flags & CMOD_LOCKMARKS) == 0) {
+ // 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).
@@ -4359,7 +4458,7 @@ static void op_format(oparg_T *oap, int keep_cursor)
saved_cursor = oap->cursor_start;
}
- format_lines(oap->line_count, keep_cursor);
+ format_lines((linenr_T)oap->line_count, keep_cursor);
/*
* Leave the cursor at the first non-blank of the last formatted line.
@@ -4373,8 +4472,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.cmod_flags & CMOD_LOCKMARKS) == 0) {
+ // put '] mark on the end of the formatted area
+ curbuf->b_op_end = curwin->w_cursor;
+ }
if (keep_cursor) {
curwin->w_cursor = saved_cursor;
@@ -4396,9 +4497,7 @@ static void op_format(oparg_T *oap, int keep_cursor)
}
}
-/*
- * Implementation of the format operator 'gq' for when using 'formatexpr'.
- */
+/// Implementation of the format operator 'gq' for when using 'formatexpr'.
static void op_formatexpr(oparg_T *oap)
{
if (oap->is_VIsual) {
@@ -4434,7 +4533,7 @@ int fex_format(linenr_T lnum, long count, int c)
if (use_sandbox) {
sandbox++;
}
- r = (int)eval_to_number(fex);
+ r = (int)eval_to_number((char *)fex);
if (use_sandbox) {
sandbox--;
}
@@ -4445,8 +4544,9 @@ int fex_format(linenr_T lnum, long count, int c)
return r;
}
-/// Format "line_count" lines, starting at the cursor position.
-/// When "line_count" is negative, format until the end of the paragraph.
+/// @param line_count number of lines to format, starting at the cursor position.
+/// when negative, format until the end of the paragraph.
+///
/// Lines after the cursor line are saved for undo, caller must have saved the
/// first line.
///
@@ -4461,13 +4561,14 @@ void format_lines(linenr_T line_count, int avoid_fex)
int leader_len = 0; // leader len of current line
int next_leader_len; // leader len of next line
char_u *leader_flags = NULL; // flags for leader of current line
- char_u *next_leader_flags; // flags for leader of next line
+ char_u *next_leader_flags = NULL; // flags for leader of next line
bool advance = true;
int second_indent = -1; // indent for second line (comment aware)
bool first_par_line = true;
int smd_save;
long count;
bool need_set_indent = true; // set indent of next paragraph
+ linenr_T first_line = curwin->w_cursor.lnum;
bool force_format = false;
const int old_State = State;
@@ -4578,7 +4679,14 @@ void format_lines(linenr_T line_count, int avoid_fex)
leader_len, leader_flags,
next_leader_len,
next_leader_flags)) {
- is_end_par = true;
+ // Special case: If the next line starts with a line comment
+ // and this line has a line comment after some text, the
+ // paragraph doesn't really end.
+ if (next_leader_flags == NULL
+ || STRNCMP(next_leader_flags, "://", 3) != 0
+ || check_linecomment(get_cursor_line_ptr()) == MAXCOL) {
+ is_end_par = true;
+ }
}
/*
@@ -4587,20 +4695,35 @@ void format_lines(linenr_T line_count, int avoid_fex)
*/
if (is_end_par || force_format) {
if (need_set_indent) {
- // replace indent in first line with minimal number of
- // tabs and spaces, according to current options
- (void)set_indent(get_indent(), SIN_CHANGED);
+ int indent = 0; // amount of indent needed
+
+ // Replace indent in first line of a paragraph with minimal
+ // number of tabs and spaces, according to current options.
+ // For the very first formatted line keep the current
+ // indent.
+ if (curwin->w_cursor.lnum == first_line) {
+ indent = get_indent();
+ } else if (curbuf->b_p_lisp) {
+ indent = get_lisp_indent();
+ } else {
+ if (cindent_on()) {
+ indent = *curbuf->b_p_inde != NUL ? get_expr_indent() : get_c_indent();
+ } else {
+ indent = get_indent();
+ }
+ }
+ (void)set_indent(indent, SIN_CHANGED);
}
// put cursor on last non-space
- State = NORMAL; // don't go past end-of-line
+ State = MODE_NORMAL; // don't go past end-of-line
coladvance(MAXCOL);
while (curwin->w_cursor.col && ascii_isspace(gchar_cursor())) {
dec_cursor();
}
// do the formatting, without 'showmode'
- State = INSERT; // for open_line()
+ State = MODE_INSERT; // for open_line()
smd_save = p_smd;
p_smd = FALSE;
insertchar(NUL, INSCHAR_FORMAT
@@ -4666,9 +4789,7 @@ void format_lines(linenr_T line_count, int avoid_fex)
}
}
-/*
- * Return TRUE if line "lnum" ends in a white character.
- */
+/// @return TRUE if line "lnum" ends in a white character.
static int ends_in_white(linenr_T lnum)
{
char_u *s = ml_get(lnum);
@@ -4681,14 +4802,12 @@ static int ends_in_white(linenr_T lnum)
return ascii_iswhite(s[l]);
}
-/*
- * Blank lines, and lines containing only the comment leader, are left
- * untouched by the formatting. The function returns TRUE in this
- * case. It also returns TRUE when a line starts with the end of a comment
- * ('e' in comment flags), so that this line is skipped, and not joined to the
- * previous line. A new paragraph starts after a blank line, or when the
- * comment leader changes -- webb.
- */
+/// Blank lines, and lines containing only the comment leader, are left
+/// untouched by the formatting. The function returns TRUE in this
+/// case. It also returns TRUE when a line starts with the end of a comment
+/// ('e' in comment flags), so that this line is skipped, and not joined to the
+/// previous line. A new paragraph starts after a blank line, or when the
+/// comment leader changes.
static int fmt_check_par(linenr_T lnum, int *leader_len, char_u **leader_flags, int do_comments)
{
char_u *flags = NULL; // init for GCC
@@ -4696,7 +4815,7 @@ static int fmt_check_par(linenr_T lnum, int *leader_len, char_u **leader_flags,
ptr = ml_get(lnum);
if (do_comments) {
- *leader_len = get_leader_len(ptr, leader_flags, false, true);
+ *leader_len = get_leader_len((char *)ptr, (char **)leader_flags, false, true);
} else {
*leader_len = 0;
}
@@ -4711,15 +4830,15 @@ static int fmt_check_par(linenr_T lnum, int *leader_len, char_u **leader_flags,
}
}
- return *skipwhite(ptr + *leader_len) == NUL
+ return *skipwhite((char *)ptr + *leader_len) == NUL
|| (*leader_len > 0 && *flags == COM_END)
|| startPS(lnum, NUL, FALSE);
}
-/*
- * Return TRUE when a paragraph starts in line "lnum". Return FALSE when the
- * previous line is in the same paragraph. Used for auto-formatting.
- */
+/// Used for auto-formatting.
+///
+/// @return TRUE when a paragraph starts in line "lnum".
+/// FALSE when the previous line is in the same paragraph.
int paragraph_start(linenr_T lnum)
{
char_u *p;
@@ -4757,19 +4876,17 @@ int paragraph_start(linenr_T lnum)
return FALSE;
}
-/*
- * prepare a few things for block mode yank/delete/tilde
- *
- * for delete:
- * - textlen includes the first/last char to be (partly) deleted
- * - start/endspaces is the number of columns that are taken by the
- * first/last deleted char minus the number of columns that have to be
- * deleted.
- * for yank and tilde:
- * - textlen includes the first/last char to be wholly yanked
- * - start/endspaces is the number of columns of the first/last yanked char
- * that are to be yanked.
- */
+/// prepare a few things for block mode yank/delete/tilde
+///
+/// for delete:
+/// - textlen includes the first/last char to be (partly) deleted
+/// - start/endspaces is the number of columns that are taken by the
+/// first/last deleted char minus the number of columns that have to be
+/// deleted.
+/// for yank and tilde:
+/// - textlen includes the first/last char to be wholly yanked
+/// - start/endspaces is the number of columns of the first/last yanked char
+/// that are to be yanked.
static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, bool is_del)
{
int incr = 0;
@@ -4899,12 +5016,19 @@ void op_addsub(oparg_T *oap, linenr_T Prenum1, bool g_cmd)
ssize_t change_cnt = 0;
linenr_T amount = Prenum1;
+ // do_addsub() might trigger re-evaluation of 'foldexpr' halfway, when the
+ // buffer is not completly updated yet. Postpone updating folds until before
+ // the call to changed_lines().
+ disable_fold_update++;
+
if (!VIsual_active) {
pos = curwin->w_cursor;
if (u_save_cursor() == FAIL) {
+ disable_fold_update--;
return;
}
change_cnt = do_addsub(oap->op_type, &pos, 0, amount);
+ disable_fold_update--;
if (change_cnt) {
changed_lines(pos.lnum, 0, pos.lnum + 1, 0L, true);
}
@@ -4915,6 +5039,7 @@ void op_addsub(oparg_T *oap, linenr_T Prenum1, bool g_cmd)
if (u_save((linenr_T)(oap->start.lnum - 1),
(linenr_T)(oap->end.lnum + 1)) == FAIL) {
+ disable_fold_update--;
return;
}
@@ -4961,6 +5086,8 @@ void op_addsub(oparg_T *oap, linenr_T Prenum1, bool g_cmd)
amount += Prenum1;
}
}
+
+ disable_fold_update--;
if (change_cnt) {
changed_lines(oap->start.lnum, 0, oap->end.lnum + 1, 0L, true);
}
@@ -4972,7 +5099,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.cmod_flags & CMOD_LOCKMARKS) == 0) {
curbuf->b_op_start = startpos;
}
@@ -5015,12 +5142,12 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
pos_T endpos;
colnr_T save_coladd = 0;
- const bool do_hex = vim_strchr(curbuf->b_p_nf, 'x') != NULL; // "heX"
- const bool do_oct = vim_strchr(curbuf->b_p_nf, 'o') != NULL; // "Octal"
- const bool do_bin = vim_strchr(curbuf->b_p_nf, 'b') != NULL; // "Bin"
- const bool do_alpha = vim_strchr(curbuf->b_p_nf, 'p') != NULL; // "alPha"
+ const bool do_hex = vim_strchr((char *)curbuf->b_p_nf, 'x') != NULL; // "heX"
+ const bool do_oct = vim_strchr((char *)curbuf->b_p_nf, 'o') != NULL; // "Octal"
+ const bool do_bin = vim_strchr((char *)curbuf->b_p_nf, 'b') != NULL; // "Bin"
+ const bool do_alpha = vim_strchr((char *)curbuf->b_p_nf, 'p') != NULL; // "alPha"
// "Unsigned"
- const bool do_unsigned = vim_strchr(curbuf->b_p_nf, 'u') != NULL;
+ const bool do_unsigned = vim_strchr((char *)curbuf->b_p_nf, 'u') != NULL;
if (virtual_active()) {
save_coladd = pos->coladd;
@@ -5103,7 +5230,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
if (visual) {
while (ptr[col] != NUL && length > 0 && !ascii_isdigit(ptr[col])
&& !(do_alpha && ASCII_ISALPHA(ptr[col]))) {
- int mb_len = utfc_ptr2len(ptr + col);
+ int mb_len = utfc_ptr2len((char *)ptr + col);
col += mb_len;
length -= mb_len;
@@ -5131,7 +5258,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
if (do_alpha && ASCII_ISALPHA(firstdigit)) {
// decrement or increment alphabetic character
if (op_type == OP_NR_SUB) {
- if (CharOrd(firstdigit) < Prenum1) {
+ if (CHAR_ORD(firstdigit) < Prenum1) {
if (isupper(firstdigit)) {
firstdigit = 'A';
} else {
@@ -5141,7 +5268,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
firstdigit -= (int)Prenum1;
}
} else {
- if (26 - CharOrd(firstdigit) - 1 < Prenum1) {
+ if (26 - CHAR_ORD(firstdigit) - 1 < Prenum1) {
if (isupper(firstdigit)) {
firstdigit = 'Z';
} else {
@@ -5206,13 +5333,13 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
if (!pre) {
if (subtract) {
if (n > oldn) {
- n = 1 + (n ^ (uvarnumber_T)-1);
+ n = 1 + (n ^ (uvarnumber_T) - 1);
negative ^= true;
}
} else {
// add
if (n < oldn) {
- n = (n ^ (uvarnumber_T)-1);
+ n = (n ^ (uvarnumber_T) - 1);
negative ^= true;
}
}
@@ -5326,11 +5453,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.cmod_flags & CMOD_LOCKMARKS) == 0) {
+ // 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:
@@ -5346,11 +5475,10 @@ theend:
return did_change;
}
-/*
- * Return the type of a register.
- * Used for getregtype()
- * Returns kMTUnknown for error.
- */
+/// Used for getregtype()
+///
+/// @return the type of a register or
+/// kMTUnknown for error.
MotionType get_reg_type(int regname, colnr_T *reg_width)
{
switch (regname) {
@@ -5412,11 +5540,10 @@ void format_reg_type(MotionType reg_type, colnr_T reg_width, char *buf, size_t b
}
}
-
/// When `flags` has `kGRegList` return a list with text `s`.
/// Otherwise just return `s`.
///
-/// Returns a void * for use in get_reg_contents().
+/// @return a void * for use in get_reg_contents().
static void *get_reg_wrap_one_line(char_u *s, int flags)
{
if (!(flags & kGRegList)) {
@@ -5554,7 +5681,7 @@ static void finish_write_reg(int name, yankreg_T *reg, yankreg_T *old_y_previous
}
}
-/// write_reg_contents - store `str` in register `name`
+/// store `str` in register `name`
///
/// @see write_reg_contents_ex
void write_reg_contents(int name, const char_u *str, ssize_t len, int must_append)
@@ -5635,7 +5762,7 @@ void write_reg_contents_ex(int name, const char_u *str, ssize_t len, bool must_a
semsg(_(e_nobufnr), (int64_t)num);
}
} else {
- buf = buflist_findnr(buflist_findpat(str, str + STRLEN(str),
+ buf = buflist_findnr(buflist_findpat((char *)str, (char *)str + STRLEN(str),
true, false, false));
}
if (buf == NULL) {
@@ -5734,7 +5861,7 @@ static void str_to_reg(yankreg_T *y_ptr, MotionType yank_type, const char_u *str
// Grow the register array to hold the pointers to the new lines.
char_u **pp = xrealloc(y_ptr->y_array,
(y_ptr->y_size + newlines) * sizeof(char_u *));
- y_ptr->y_array = pp;
+ y_ptr->y_array = (char **)pp;
size_t lnum = y_ptr->y_size; // The current line number.
@@ -5764,8 +5891,10 @@ static void str_to_reg(yankreg_T *y_ptr, MotionType yank_type, const char_u *str
// When appending, copy the previous line and free it after.
size_t extra = append ? STRLEN(pp[--lnum]) : 0;
- char_u *s = xmallocz(line_len + extra);
- memcpy(s, pp[lnum], extra);
+ char *s = xmallocz(line_len + extra);
+ if (extra > 0) {
+ memcpy(s, pp[lnum], extra);
+ }
memcpy(s + extra, start, line_len);
size_t s_len = extra + line_len;
@@ -5773,7 +5902,7 @@ static void str_to_reg(yankreg_T *y_ptr, MotionType yank_type, const char_u *str
xfree(pp[lnum]);
append = false; // only first line is appended
}
- pp[lnum] = s;
+ pp[lnum] = (char_u *)s;
// Convert NULs to '\n' to prevent truncation.
memchrsub(pp[lnum], NUL, '\n', s_len);
@@ -5795,21 +5924,18 @@ void clear_oparg(oparg_T *oap)
memset(oap, 0, sizeof(oparg_T));
}
-
-/*
- * Count the number of bytes, characters and "words" in a line.
- *
- * "Words" are counted by looking for boundaries between non-space and
- * space characters. (it seems to produce results that match 'wc'.)
- *
- * Return value is byte count; word count for the line is added to "*wc".
- * Char count is added to "*cc".
- *
- * The function will only examine the first "limit" characters in the
- * line, stopping if it encounters an end-of-line (NUL byte). In that
- * case, eol_size will be added to the character count to account for
- * the size of the EOL character.
- */
+/// Count the number of bytes, characters and "words" in a line.
+///
+/// "Words" are counted by looking for boundaries between non-space and
+/// space characters. (it seems to produce results that match 'wc'.)
+///
+/// Return value is byte count; word count for the line is added to "*wc".
+/// Char count is added to "*cc".
+///
+/// The function will only examine the first "limit" characters in the
+/// line, stopping if it encounters an end-of-line (NUL byte). In that
+/// case, eol_size will be added to the character count to account for
+/// the size of the EOL character.
static varnumber_T line_count_info(char_u *line, varnumber_T *wc, varnumber_T *cc,
varnumber_T limit, int eol_size)
{
@@ -5828,7 +5954,7 @@ static varnumber_T line_count_info(char_u *line, varnumber_T *wc, varnumber_T *c
is_word = 1;
}
chars++;
- i += utfc_ptr2len(line + i);
+ i += utfc_ptr2len((char *)line + i);
}
if (is_word) {
@@ -5848,7 +5974,8 @@ static varnumber_T line_count_info(char_u *line, varnumber_T *wc, varnumber_T *c
/// Give some info about the position of the cursor (for "g CTRL-G").
/// In Visual mode, give some info about the selected region. (In this case,
/// the *_count_cursor variables store running totals for the selection.)
-/// When "dict" is not NULL store the info there instead of showing it.
+///
+/// @param dict when not NULL, store the info there instead of showing it.
void cursor_pos_info(dict_T *dict)
{
char_u *p;
@@ -6031,9 +6158,9 @@ void cursor_pos_info(dict_T *dict)
} else {
p = get_cursor_line_ptr();
validate_virtcol();
- col_print(buf1, sizeof(buf1), (int)curwin->w_cursor.col + 1,
+ col_print((char *)buf1, sizeof(buf1), (int)curwin->w_cursor.col + 1,
(int)curwin->w_virtcol + 1);
- col_print(buf2, sizeof(buf2), (int)STRLEN(p), linetabsize(p));
+ col_print((char *)buf2, sizeof(buf2), (int)STRLEN(p), linetabsize(p));
if (char_count_cursor == byte_count_cursor
&& char_count == byte_count) {
@@ -6072,6 +6199,10 @@ void cursor_pos_info(dict_T *dict)
// Don't shorten this message, the user asked for it.
p = p_shm;
p_shm = (char_u *)"";
+ if (p_ch < 1) {
+ msg_start();
+ msg_scroll = true;
+ }
msg((char *)IObuff);
p_shm = p;
}
@@ -6094,7 +6225,7 @@ void cursor_pos_info(dict_T *dict)
}
}
-// Handle indent and format operators and visual mode ":".
+/// Handle indent and format operators and visual mode ":".
static void op_colon(oparg_T *oap)
{
stuffcharReadbuff(':');
@@ -6141,12 +6272,14 @@ static void op_colon(oparg_T *oap)
// do_cmdline() does the rest
}
-// Handle the "g@" operator: call 'operatorfunc'.
+/// Handle the "g@" operator: call 'operatorfunc'.
static void op_function(const oparg_T *oap)
FUNC_ATTR_NONNULL_ALL
{
const TriState save_virtual_op = virtual_op;
const bool save_finish_op = finish_op;
+ 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"));
@@ -6163,7 +6296,7 @@ static void op_function(const oparg_T *oap)
argv[0].v_type = VAR_STRING;
argv[1].v_type = VAR_UNKNOWN;
argv[0].vval.v_string =
- (char_u *)(((const char *const[]) {
+ (char *)(((const char *const[]) {
[kMTBlockWise] = "block",
[kMTLineWise] = "line",
[kMTCharWise] = "char",
@@ -6176,10 +6309,14 @@ static void op_function(const oparg_T *oap)
// Reset finish_op so that mode() returns the right value.
finish_op = false;
- (void)call_func_retnr(p_opfunc, 1, argv);
+ (void)call_func_retnr((char *)p_opfunc, 1, argv);
virtual_op = save_virtual_op;
finish_op = save_finish_op;
+ if (cmdmod.cmod_flags & CMOD_LOCKMARKS) {
+ curbuf->b_op_start = orig_start;
+ curbuf->b_op_end = orig_end;
+ }
}
}
@@ -6248,8 +6385,17 @@ static void get_op_vcol(oparg_T *oap, colnr_T redo_VIsual_vcol, bool initial)
oap->start = curwin->w_cursor;
}
-// Handle an operator after Visual mode or when the movement is finished.
-// "gui_yank" is true when yanking text for the clipboard.
+/// Information for redoing the previous Visual selection.
+typedef struct {
+ int rv_mode; ///< 'v', 'V', or Ctrl-V
+ linenr_T rv_line_count; ///< number of lines
+ colnr_T rv_vcol; ///< number of cols or end column
+ long rv_count; ///< count for Visual operator
+ int rv_arg; ///< extra argument
+} redo_VIsual_T;
+
+/// Handle an operator after Visual mode or when the movement is finished.
+/// "gui_yank" is true when yanking text for the clipboard.
void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
{
oparg_T *oap = cap->oap;
@@ -6258,13 +6404,9 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
int restart_edit_save;
int lbr_saved = curwin->w_p_lbr;
-
// The visual area is remembered for redo
- static int redo_VIsual_mode = NUL; // 'v', 'V', or Ctrl-V
- static linenr_T redo_VIsual_line_count; // number of lines
- static colnr_T redo_VIsual_vcol; // number of cols or end column
- static long redo_VIsual_count; // count for Visual operator
- static int redo_VIsual_arg; // extra argument
+ static redo_VIsual_T redo_VIsual = { NUL, 0, 0, 0, 0 };
+
bool include_line_break = false;
old_cursor = curwin->w_cursor;
@@ -6328,7 +6470,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
// If 'cpoptions' does not contain 'r', insert the search
// pattern to really repeat the same command.
if (vim_strchr(p_cpo, CPO_REDO) == NULL) {
- AppendToRedobuffLit(cap->searchbuf, -1);
+ AppendToRedobuffLit((char *)cap->searchbuf, -1);
}
AppendToRedobuff(NL_STR);
} else if (cap->cmdchar == ':' || cap->cmdchar == K_COMMAND) {
@@ -6338,7 +6480,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
if (repeat_cmdline == NULL) {
ResetRedobuff();
} else {
- AppendToRedobuffLit(repeat_cmdline, -1);
+ AppendToRedobuffLit((char *)repeat_cmdline, -1);
AppendToRedobuff(NL_STR);
XFREE_CLEAR(repeat_cmdline);
}
@@ -6347,28 +6489,27 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
if (redo_VIsual_busy) {
// Redo of an operation on a Visual area. Use the same size from
- // redo_VIsual_line_count and redo_VIsual_vcol.
+ // redo_VIsual.rv_line_count and redo_VIsual.rv_vcol.
oap->start = curwin->w_cursor;
- curwin->w_cursor.lnum += redo_VIsual_line_count - 1;
+ curwin->w_cursor.lnum += redo_VIsual.rv_line_count - 1;
if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) {
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
}
- VIsual_mode = redo_VIsual_mode;
- if (redo_VIsual_vcol == MAXCOL || VIsual_mode == 'v') {
+ VIsual_mode = redo_VIsual.rv_mode;
+ if (redo_VIsual.rv_vcol == MAXCOL || VIsual_mode == 'v') {
if (VIsual_mode == 'v') {
- if (redo_VIsual_line_count <= 1) {
+ if (redo_VIsual.rv_line_count <= 1) {
validate_virtcol();
- curwin->w_curswant =
- curwin->w_virtcol + redo_VIsual_vcol - 1;
+ curwin->w_curswant = curwin->w_virtcol + redo_VIsual.rv_vcol - 1;
} else {
- curwin->w_curswant = redo_VIsual_vcol;
+ curwin->w_curswant = redo_VIsual.rv_vcol;
}
} else {
curwin->w_curswant = MAXCOL;
}
coladvance(curwin->w_curswant);
}
- cap->count0 = redo_VIsual_count;
+ cap->count0 = redo_VIsual.rv_count;
cap->count1 = (cap->count0 == 0 ? 1 : cap->count0);
} else if (VIsual_active) {
if (!gui_yank) {
@@ -6455,7 +6596,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
virtual_op = virtual_active();
if (VIsual_active || redo_VIsual_busy) {
- get_op_vcol(oap, redo_VIsual_vcol, true);
+ get_op_vcol(oap, redo_VIsual.rv_vcol, true);
if (!redo_VIsual_busy && !gui_yank) {
// Prepare to reselect and redo Visual: this is based on the
@@ -6478,7 +6619,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
resel_VIsual_vcol = oap->end_vcol;
}
}
- resel_VIsual_line_count = oap->line_count;
+ resel_VIsual_line_count = (linenr_T)oap->line_count;
}
// can't redo yank (unless 'y' is in 'cpoptions') and ":"
@@ -6500,6 +6641,8 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
get_op_char(oap->op_type), get_extra_op_char(oap->op_type),
oap->motion_force, cap->cmdchar, cap->nchar);
} else if (cap->cmdchar != ':' && cap->cmdchar != K_COMMAND) {
+ int opchar = get_op_char(oap->op_type);
+ int extra_opchar = get_extra_op_char(oap->op_type);
int nchar = oap->op_type == OP_REPLACE ? cap->nchar : NUL;
// reverse what nv_replace() did
@@ -6508,15 +6651,20 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
} else if (nchar == REPLACE_NL_NCHAR) {
nchar = NL;
}
- prep_redo(oap->regname, 0L, NUL, 'v', get_op_char(oap->op_type),
- get_extra_op_char(oap->op_type), nchar);
+
+ if (opchar == 'g' && extra_opchar == '@') {
+ // also repeat the count for 'operatorfunc'
+ prep_redo_num2(oap->regname, 0L, NUL, 'v', cap->count0, opchar, extra_opchar, nchar);
+ } else {
+ prep_redo(oap->regname, 0L, NUL, 'v', opchar, extra_opchar, nchar);
+ }
}
if (!redo_VIsual_busy) {
- redo_VIsual_mode = resel_VIsual_mode;
- redo_VIsual_vcol = resel_VIsual_vcol;
- redo_VIsual_line_count = resel_VIsual_line_count;
- redo_VIsual_count = cap->count0;
- redo_VIsual_arg = cap->arg;
+ redo_VIsual.rv_mode = resel_VIsual_mode;
+ redo_VIsual.rv_vcol = resel_VIsual_vcol;
+ redo_VIsual.rv_line_count = resel_VIsual_line_count;
+ redo_VIsual.rv_count = cap->count0;
+ redo_VIsual.rv_arg = cap->arg;
}
}
@@ -6572,7 +6720,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
// Include the trailing byte of a multi-byte char.
if (oap->inclusive) {
- const int l = utfc_ptr2len(ml_get_pos(&oap->end));
+ const int l = utfc_ptr2len((char *)ml_get_pos(&oap->end));
if (l > 1) {
oap->end.col += l - 1;
}
@@ -6630,9 +6778,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
switch (oap->op_type) {
case OP_LSHIFT:
case OP_RSHIFT:
- op_shift(oap, true,
- oap->is_VIsual ? (int)cap->count1 :
- 1);
+ op_shift(oap, true, oap->is_VIsual ? (int)cap->count1 : 1);
auto_format(false, true);
break;
@@ -6676,7 +6822,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
} else {
curwin->w_p_lbr = lbr_saved;
oap->excl_tr_ws = cap->cmdchar == 'z';
- (void)op_yank(oap, !gui_yank, false);
+ (void)op_yank(oap, !gui_yank);
}
check_cursor_col();
break;
@@ -6688,10 +6834,9 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
CancelRedo();
} else {
// This is a new edit command, not a restart. Need to
- // remember it to make 'insertmode' work with mappings for
- // Visual mode. But do this only once and not when typed and
- // 'insertmode' isn't set.
- if (p_im || !KeyTyped) {
+ // remember it to make i_CTRL-O work with mappings for
+ // Visual mode. But do this only once and not when typed.
+ if (!KeyTyped) {
restart_edit_save = restart_edit;
} else {
restart_edit_save = 0;
@@ -6767,12 +6912,20 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
op_format(oap, true); // use internal function
break;
- case OP_FUNCTION:
+ case OP_FUNCTION: {
+ redo_VIsual_T save_redo_VIsual = redo_VIsual;
+
// Restore linebreak, so that when the user edits it looks as
// before.
curwin->w_p_lbr = lbr_saved;
- op_function(oap); // call 'operatorfunc'
+ // call 'operatorfunc'
+ op_function(oap);
+
+ // Restore the info for redoing Visual mode, the function may
+ // invoke another operator and unintentionally change it.
+ redo_VIsual = save_redo_VIsual;
break;
+ }
case OP_INSERT:
case OP_APPEND:
@@ -6782,7 +6935,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
CancelRedo();
} else {
// This is a new edit command, not a restart. Need to
- // remember it to make 'insertmode' work with mappings for
+ // remember it to make i_CTRL-O work with mappings for
// Visual mode. But do this only once.
restart_edit_save = restart_edit;
restart_edit = 0;
@@ -6853,7 +7006,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
} else {
VIsual_active = true;
curwin->w_p_lbr = lbr_saved;
- op_addsub(oap, cap->count1, redo_VIsual_arg);
+ op_addsub(oap, (linenr_T)cap->count1, redo_VIsual.rv_arg);
VIsual_active = false;
}
check_cursor_col();
@@ -6990,9 +7143,9 @@ bool prepare_yankreg_from_object(yankreg_T *reg, String regtype, size_t lines)
if (!ascii_isdigit(regtype.data[1])) {
return false;
}
- const char *p = regtype.data+1;
- reg->y_width = getdigits_int((char_u **)&p, false, 1) - 1;
- if (regtype.size > (size_t)(p-regtype.data)) {
+ const char *p = regtype.data + 1;
+ reg->y_width = getdigits_int((char **)&p, false, 1) - 1;
+ if (regtype.size > (size_t)(p - regtype.data)) {
return false;
}
}
@@ -7006,12 +7159,12 @@ bool prepare_yankreg_from_object(yankreg_T *reg, String regtype, size_t lines)
void finish_yankreg_from_object(yankreg_T *reg, bool clipboard_adjust)
{
- if (reg->y_size > 0 && STRLEN(reg->y_array[reg->y_size-1]) == 0) {
+ if (reg->y_size > 0 && STRLEN(reg->y_array[reg->y_size - 1]) == 0) {
// a known-to-be charwise yank might have a final linebreak
// but otherwise there is no line after the final newline
if (reg->y_type != kMTCharWise) {
if (reg->y_type == kMTUnknown || clipboard_adjust) {
- xfree(reg->y_array[reg->y_size-1]);
+ xfree(reg->y_array[reg->y_size - 1]);
reg->y_size--;
}
if (reg->y_type == kMTUnknown) {
@@ -7070,7 +7223,7 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet)
if (TV_LIST_ITEM_TV(tv_list_last(res))->v_type != VAR_STRING) {
goto err;
}
- char_u *regtype = TV_LIST_ITEM_TV(tv_list_last(res))->vval.v_string;
+ char_u *regtype = (char_u *)TV_LIST_ITEM_TV(tv_list_last(res))->vval.v_string;
if (regtype == NULL || STRLEN(regtype) > 1) {
goto err;
}
@@ -7099,7 +7252,7 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet)
reg->y_type = kMTUnknown;
}
- reg->y_array = xcalloc((size_t)tv_list_len(lines), sizeof(char_u *));
+ reg->y_array = xcalloc((size_t)tv_list_len(lines), sizeof(char *));
reg->y_size = (size_t)tv_list_len(lines);
reg->additional_data = NULL;
reg->timestamp = 0;
@@ -7111,14 +7264,14 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet)
if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING) {
goto err;
}
- reg->y_array[tv_idx++] = (char_u *)xstrdupnul((const char *)TV_LIST_ITEM_TV(li)->vval.v_string);
+ reg->y_array[tv_idx++] = xstrdupnul((const char *)TV_LIST_ITEM_TV(li)->vval.v_string);
});
- if (reg->y_size > 0 && STRLEN(reg->y_array[reg->y_size-1]) == 0) {
+ if (reg->y_size > 0 && STRLEN(reg->y_array[reg->y_size - 1]) == 0) {
// a known-to-be charwise yank might have a final linebreak
// but otherwise there is no line after the final newline
if (reg->y_type != kMTCharWise) {
- xfree(reg->y_array[reg->y_size-1]);
+ xfree(reg->y_array[reg->y_size - 1]);
reg->y_size--;
if (reg->y_type == kMTUnknown) {
reg->y_type = kMTLineWise;
@@ -7248,7 +7401,6 @@ void restore_batch_count(int save_count)
}
}
-
/// Check whether register is empty
static inline bool reg_empty(const yankreg_T *const reg)
FUNC_ATTR_PURE
@@ -7361,7 +7513,6 @@ const yankreg_T *op_reg_get(const char name)
///
/// @return true on success, false on failure.
bool op_reg_set_previous(const char name)
- FUNC_ATTR_WARN_UNUSED_RESULT
{
int i = op_reg_index(name);
if (i == -1) {
@@ -7388,7 +7539,7 @@ bcount_t get_region_bytecount(buf_T *buf, linenr_T start_lnum, linenr_T end_lnum
const char *first = (const char *)ml_get_buf(buf, start_lnum, false);
bcount_t deleted_bytes = (bcount_t)STRLEN(first) - start_col + 1;
- for (linenr_T i = 1; i <= end_lnum-start_lnum-1; i++) {
+ for (linenr_T i = 1; i <= end_lnum - start_lnum - 1; i++) {
if (start_lnum + i > max_lnum) {
return deleted_bytes;
}
diff --git a/src/nvim/ops.h b/src/nvim/ops.h
index af49e271cb..a456d68003 100644
--- a/src/nvim/ops.h
+++ b/src/nvim/ops.h
@@ -84,11 +84,11 @@ enum GRegFlags {
/// Definition of one register
typedef struct yankreg {
- char_u **y_array; ///< Pointer to an array of line pointers.
- size_t y_size; ///< Number of lines in y_array.
- MotionType y_type; ///< Register type
- colnr_T y_width; ///< Register width (only valid for y_type == kBlockWise).
- Timestamp timestamp; ///< Time when register was last modified.
+ char **y_array; ///< Pointer to an array of line pointers.
+ size_t y_size; ///< Number of lines in y_array.
+ MotionType y_type; ///< Register type
+ colnr_T y_width; ///< Register width (only valid for y_type == kBlockWise).
+ Timestamp timestamp; ///< Time when register was last modified.
dict_T *additional_data; ///< Additional data from ShaDa file.
} yankreg_T;
@@ -112,9 +112,9 @@ static inline int op_reg_index(const int regname)
if (ascii_isdigit(regname)) {
return regname - '0';
} else if (ASCII_ISLOWER(regname)) {
- return CharOrdLow(regname) + 10;
+ return CHAR_ORD_LOW(regname) + 10;
} else if (ASCII_ISUPPER(regname)) {
- return CharOrdUp(regname) + 10;
+ return CHAR_ORD_UP(regname) + 10;
} else if (regname == '-') {
return DELETION_REGISTER;
} else if (regname == '*') {
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 80a6596469..cfd8248eb6 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -47,9 +47,11 @@
#include "nvim/getchar.h"
#include "nvim/hardcopy.h"
#include "nvim/highlight.h"
+#include "nvim/highlight_group.h"
#include "nvim/indent_c.h"
-#include "nvim/keymap.h"
+#include "nvim/keycodes.h"
#include "nvim/macros.h"
+#include "nvim/mapping.h"
#include "nvim/mbyte.h"
#include "nvim/memfile.h"
#include "nvim/memline.h"
@@ -100,7 +102,6 @@
#define OPT_BUF(x) (idopt_T)(PV_BUF + (int)(x))
#define OPT_BOTH(x) (idopt_T)(PV_BOTH + (int)(x))
-
// WV_ and BV_ values get typecasted to this for the "indir" field
typedef enum {
PV_NONE = 0,
@@ -132,6 +133,7 @@ static int p_cin;
static char_u *p_cink;
static char_u *p_cino;
static char_u *p_cinw;
+static char_u *p_cinsd;
static char_u *p_com;
static char_u *p_cms;
static char_u *p_cpt;
@@ -209,7 +211,6 @@ typedef struct vimoption {
LastSet last_set; // script in which the option was last set
} vimoption_T;
-
/*
* Flags
*/
@@ -260,8 +261,8 @@ typedef struct vimoption {
#define HIGHLIGHT_INIT \
"8:SpecialKey,~:EndOfBuffer,z:TermCursor,Z:TermCursorNC,@:NonText,d:Directory,e:ErrorMsg," \
- "i:IncSearch,l:Search,m:MoreMsg,M:ModeMsg,n:LineNr,a:LineNrAbove,b:LineNrBelow,N:CursorLineNr," \
- "G:CursorLineSign,O:CursorLineFold" \
+ "i:IncSearch,l:Search,y:CurSearch,m:MoreMsg,M:ModeMsg,n:LineNr,a:LineNrAbove,b:LineNrBelow," \
+ "N:CursorLineNr,G:CursorLineSign,O:CursorLineFold" \
"r:Question,s:StatusLine,S:StatusLineNC,c:VertSplit,t:Title,v:Visual,V:VisualNOS,w:WarningMsg," \
"W:WildMenu,f:Folded,F:FoldColumn,A:DiffAdd,C:DiffChange,D:DiffDelete,T:DiffText,>:SignColumn," \
"-:Conceal,B:SpellBad,P:SpellCap,R:SpellRare,L:SpellLocal,+:Pmenu,=:PmenuSel,x:PmenuSbar," \
@@ -281,7 +282,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 };
@@ -333,6 +334,9 @@ static char_u SHM_ALL[] = {
0,
};
+static char e_unclosed_expression_sequence[] = N_("E540: Unclosed expression sequence");
+static char e_unbalanced_groups[] = N_("E542: unbalanced groups");
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "option.c.generated.h"
#endif
@@ -358,7 +362,7 @@ void set_init_1(bool clean_arg)
{
const char *shell = os_getenv("SHELL");
if (shell != NULL) {
- if (vim_strchr((const char_u *)shell, ' ') != NULL) {
+ if (vim_strchr(shell, ' ') != NULL) {
const size_t len = strlen(shell) + 3; // two quotes and a trailing NUL
char *const cmd = xmalloc(len);
snprintf(cmd, len, "\"%s\"", shell);
@@ -486,17 +490,17 @@ void set_init_1(bool clean_arg)
#endif
false);
- char *backupdir = stdpaths_user_data_subpath("backup", 2, true);
+ char *backupdir = stdpaths_user_state_subpath("backup", 2, true);
const size_t backupdir_len = strlen(backupdir);
backupdir = xrealloc(backupdir, backupdir_len + 3);
memmove(backupdir + 2, backupdir, backupdir_len + 1);
memmove(backupdir, ".,", 2);
set_string_default("backupdir", backupdir, true);
- set_string_default("viewdir", stdpaths_user_data_subpath("view", 2, true),
+ set_string_default("viewdir", stdpaths_user_state_subpath("view", 2, true),
true);
- set_string_default("directory", stdpaths_user_data_subpath("swap", 2, true),
+ set_string_default("directory", stdpaths_user_state_subpath("swap", 2, true),
true);
- set_string_default("undodir", stdpaths_user_data_subpath("undo", 2, true),
+ set_string_default("undodir", stdpaths_user_state_subpath("undo", 2, true),
true);
// Set default for &runtimepath. All necessary expansions are performed in
// this function.
@@ -514,7 +518,6 @@ void set_init_1(bool clean_arg)
*/
set_options_default(0);
-
curbuf->b_p_initialized = true;
curbuf->b_p_ar = -1; // no local 'autoread' value
curbuf->b_p_ul = NO_LOCAL_UNDOLEVEL;
@@ -618,7 +621,7 @@ static void set_option_default(int opt_idx, int opt_flags)
// freeing and allocating the value.
if (options[opt_idx].indir != PV_NONE) {
set_string_option_direct(NULL, opt_idx,
- options[opt_idx].def_val, opt_flags, 0);
+ (char *)options[opt_idx].def_val, opt_flags, 0);
} else {
if ((opt_flags & OPT_FREE) && (flags & P_ALLOCED)) {
free_string_option(*(char_u **)(varp));
@@ -773,7 +776,6 @@ void free_all_options(void)
}
#endif
-
/// Initialize the options, part two: After getting Rows and Columns.
void set_init_2(bool headless)
{
@@ -827,8 +829,8 @@ void set_init_3(void)
// Default for p_sp is "| tee", for p_srr is ">".
// For known shells it is changed here to include stderr.
//
- if (fnamecmp(p, "csh") == 0
- || fnamecmp(p, "tcsh") == 0) {
+ if (FNAMECMP(p, "csh") == 0
+ || FNAMECMP(p, "tcsh") == 0) {
if (do_sp) {
p_sp = (char_u *)"|& tee";
options[idx_sp].def_val = p_sp;
@@ -837,16 +839,16 @@ void set_init_3(void)
p_srr = (char_u *)">&";
options[idx_srr].def_val = p_srr;
}
- } else if (fnamecmp(p, "sh") == 0
- || fnamecmp(p, "ksh") == 0
- || fnamecmp(p, "mksh") == 0
- || fnamecmp(p, "pdksh") == 0
- || fnamecmp(p, "zsh") == 0
- || fnamecmp(p, "zsh-beta") == 0
- || fnamecmp(p, "bash") == 0
- || fnamecmp(p, "fish") == 0
- || fnamecmp(p, "ash") == 0
- || fnamecmp(p, "dash") == 0) {
+ } else if (FNAMECMP(p, "sh") == 0
+ || FNAMECMP(p, "ksh") == 0
+ || FNAMECMP(p, "mksh") == 0
+ || FNAMECMP(p, "pdksh") == 0
+ || FNAMECMP(p, "zsh") == 0
+ || FNAMECMP(p, "zsh-beta") == 0
+ || FNAMECMP(p, "bash") == 0
+ || FNAMECMP(p, "fish") == 0
+ || FNAMECMP(p, "ash") == 0
+ || FNAMECMP(p, "dash") == 0) {
// Always use POSIX shell style redirection if we reach this
if (do_sp) {
p_sp = (char_u *)"2>&1| tee";
@@ -904,7 +906,6 @@ void set_helplang_default(const char *lang)
}
}
-
/// 'title' and 'icon' only default to true if they have not been set or reset
/// in .vimrc and we can read the old value.
/// When 'title' and 'icon' have been reset in .vimrc, we won't even check if
@@ -931,6 +932,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
@@ -946,7 +962,7 @@ void set_title_defaults(void)
/// @param arg option string (may be written to!)
///
/// @return FAIL if an error is detected, OK otherwise
-int do_set(char_u *arg, int opt_flags)
+int do_set(char *arg, int opt_flags)
{
int opt_idx;
char *errmsg;
@@ -974,7 +990,7 @@ int do_set(char_u *arg, int opt_flags)
while (*arg != NUL) { // loop to process all options
errmsg = NULL;
- startarg = arg; // remember for error message
+ startarg = (char_u *)arg; // remember for error message
if (STRNCMP(arg, "all", 3) == 0 && !isalpha(arg[3])
&& !(opt_flags & OPT_MODELINE)) {
@@ -1027,7 +1043,7 @@ int do_set(char_u *arg, int opt_flags)
}
len++;
if (opt_idx == -1) {
- key = find_key_option(arg + 1, true);
+ key = find_key_option((char_u *)arg + 1, true);
}
} else {
len = 0;
@@ -1041,12 +1057,12 @@ int do_set(char_u *arg, int opt_flags)
}
opt_idx = findoption_len((const char *)arg, (size_t)len);
if (opt_idx == -1) {
- key = find_key_option(arg, false);
+ key = find_key_option((char_u *)arg, false);
}
}
// remember character after option name
- afterchar = arg[len];
+ afterchar = (uint8_t)arg[len];
// skip white space, allow ":set ai ?"
while (ascii_iswhite(arg[len])) {
@@ -1068,7 +1084,7 @@ int do_set(char_u *arg, int opt_flags)
len++;
}
}
- nextchar = arg[len];
+ nextchar = (uint8_t)arg[len];
if (opt_idx == -1 && key == 0) { // found a mismatch: skip
errmsg = N_("E518: Unknown option");
@@ -1079,7 +1095,7 @@ int do_set(char_u *arg, int opt_flags)
if (options[opt_idx].var == NULL) { // hidden option: skip
// Only give an error message when requesting the value of
// a hidden option, ignore setting it.
- if (vim_strchr((char_u *)"=:!&<", nextchar) == NULL
+ if (vim_strchr("=:!&<", nextchar) == NULL
&& (!(options[opt_idx].flags & P_BOOL)
|| nextchar == '?')) {
errmsg = _(e_unsupportedoption);
@@ -1133,7 +1149,7 @@ int do_set(char_u *arg, int opt_flags)
goto skip;
}
- if (vim_strchr((char_u *)"?=:!&<", nextchar) != NULL) {
+ if (vim_strchr("?=:!&<", nextchar) != NULL) {
arg += len;
if (nextchar == '&' && arg[1] == 'v' && arg[2] == 'i') {
if (arg[3] == 'm') { // "opt&vim": set to Vim default
@@ -1142,7 +1158,7 @@ int do_set(char_u *arg, int opt_flags)
arg += 2;
}
}
- if (vim_strchr((char_u *)"?!&<", nextchar) != NULL
+ if (vim_strchr("?!&<", nextchar) != NULL
&& arg[1] != NUL && !ascii_iswhite(arg[1])) {
errmsg = e_trailing;
goto skip;
@@ -1155,7 +1171,7 @@ int do_set(char_u *arg, int opt_flags)
//
if (nextchar == '?'
|| (prefix == 1
- && vim_strchr((char_u *)"=:&<", nextchar) == NULL
+ && vim_strchr("=:&<", nextchar) == NULL
&& !(flags & P_BOOL))) {
/*
* print value
@@ -1235,7 +1251,7 @@ int do_set(char_u *arg, int opt_flags)
errmsg = set_bool_option(opt_idx, varp, (int)value,
opt_flags);
} else { // Numeric or string.
- if (vim_strchr((const char_u *)"=:&<", nextchar) == NULL
+ if (vim_strchr("=:&<", nextchar) == NULL
|| prefix != 1) {
errmsg = e_invarg;
goto skip;
@@ -1266,14 +1282,14 @@ int do_set(char_u *arg, int opt_flags)
|| *arg == '^'
|| (*arg != NUL && (!arg[1] || ascii_iswhite(arg[1]))
&& !ascii_isdigit(*arg)))) {
- value = string_to_key(arg);
+ value = string_to_key((char_u *)arg);
if (value == 0 && (long *)varp != &p_wcm) {
errmsg = e_invarg;
goto skip;
}
} else if (*arg == '-' || ascii_isdigit(*arg)) {
// Allow negative, octal and hex numbers.
- vim_str2nr(arg, NULL, &i, STR2NR_ALL, &value, NULL, 0, true);
+ vim_str2nr((char_u *)arg, NULL, &i, STR2NR_ALL, &value, NULL, 0, true);
if (i == 0 || (arg[i] != NUL && !ascii_iswhite(arg[i]))) {
errmsg = N_("E521: Number required after =");
goto skip;
@@ -1375,8 +1391,8 @@ int do_set(char_u *arg, int opt_flags)
if (varp == (char_u *)&p_kp
&& (*arg == NUL || *arg == ' ')) {
STRCPY(errbuf, ":help");
- save_arg = arg;
- arg = (char_u *)errbuf;
+ save_arg = (char_u *)arg;
+ arg = errbuf;
}
/*
* Convert 'backspace' number to string, for
@@ -1384,7 +1400,7 @@ int do_set(char_u *arg, int opt_flags)
*/
else if (varp == (char_u *)&p_bs
&& ascii_isdigit(**(char_u **)varp)) {
- i = getdigits_int((char_u **)varp, true, 0);
+ i = getdigits_int((char **)varp, true, 0);
switch (i) {
case 0:
*(char_u **)varp = empty_option;
@@ -1435,8 +1451,8 @@ int do_set(char_u *arg, int opt_flags)
if (i & 16) {
STRLCAT(errbuf, "[,],", sizeof(errbuf));
}
- save_arg = arg;
- arg = (char_u *)errbuf;
+ save_arg = (char_u *)arg;
+ arg = errbuf;
}
/*
* Remove '>' before 'dir' and 'bdir', for
@@ -1489,7 +1505,7 @@ int do_set(char_u *arg, int opt_flags)
arg += i;
s += i;
} else {
- *s++ = *arg++;
+ *s++ = (uint8_t)(*arg++);
}
}
*s = NUL;
@@ -1589,14 +1605,14 @@ int do_set(char_u *arg, int opt_flags)
// 'whichwrap'
if (flags & P_ONECOMMA) {
if (*s != ',' && *(s + 1) == ','
- && vim_strchr(s + 2, *s) != NULL) {
+ && vim_strchr((char *)s + 2, *s) != NULL) {
// Remove the duplicated value and the next comma.
STRMOVE(s, s + 2);
continue;
}
} else {
if ((!(flags & P_COMMA) || *s != ',')
- && vim_strchr(s + 1, *s) != NULL) {
+ && vim_strchr((char *)s + 1, *s) != NULL) {
STRMOVE(s, s + 1);
continue;
}
@@ -1606,7 +1622,7 @@ int do_set(char_u *arg, int opt_flags)
}
if (save_arg != NULL) { // number for 'whichwrap'
- arg = save_arg;
+ arg = (char *)save_arg;
}
new_value_alloced = true;
}
@@ -1704,15 +1720,15 @@ skip:
if (errmsg != NULL) {
STRLCPY(IObuff, _(errmsg), IOSIZE);
i = (int)STRLEN(IObuff) + 2;
- if (i + (arg - startarg) < IOSIZE) {
+ if (i + ((char_u *)arg - startarg) < IOSIZE) {
// append the argument with the error
STRCAT(IObuff, ": ");
- assert(arg >= startarg);
- memmove(IObuff + i, startarg, (size_t)(arg - startarg));
- IObuff[i + (arg - startarg)] = NUL;
+ assert((char_u *)arg >= startarg);
+ memmove(IObuff + i, startarg, (size_t)((char_u *)arg - startarg));
+ IObuff[i + ((char_u *)arg - startarg)] = NUL;
}
// make sure all characters are printable
- trans_characters(IObuff, IOSIZE);
+ trans_characters((char *)IObuff, IOSIZE);
no_wait_return++; // wait_return done later
emsg((char *)IObuff); // show error highlighted
@@ -1766,7 +1782,7 @@ static char *illegal_char(char *errbuf, size_t errbuflen, int c)
if (errbuf == NULL) {
return "";
}
- vim_snprintf((char *)errbuf, errbuflen, _("E539: Illegal character <%s>"),
+ vim_snprintf(errbuf, errbuflen, _("E539: Illegal character <%s>"),
(char *)transchar(c));
return errbuf;
}
@@ -1779,7 +1795,7 @@ static int string_to_key(char_u *arg)
return find_key_option(arg + 1, true);
}
if (*arg == '^') {
- return Ctrl_chr(arg[1]);
+ return CTRL_CHR(arg[1]);
}
return *arg;
}
@@ -1897,7 +1913,7 @@ char_u *find_shada_parameter(int type)
if (*p == 'n') { // 'n' is always the last one
break;
}
- p = vim_strchr(p, ','); // skip until next ','
+ p = (char_u *)vim_strchr((char *)p, ','); // skip until next ','
if (p == NULL) { // hit the end without finding parameter
break;
}
@@ -1943,13 +1959,10 @@ static char_u *option_expand(int opt_idx, char_u *val)
return NameBuff;
}
-// After setting various option values: recompute variables that depend on
-// option values.
-static void didset_options(void)
+/// After setting various option values: recompute variables that depend on
+/// option values.
+static void didset_string_options(void)
{
- // initialize the table for 'iskeyword' et.al.
- (void)init_chartab();
-
(void)opt_strings_flags(p_cmp, p_cmp_values, &cmp_flags, true);
(void)opt_strings_flags(p_bkc, p_bkc_values, &bkc_flags, true);
(void)opt_strings_flags(p_bo, p_bo_values, &bo_flags, true);
@@ -1961,18 +1974,29 @@ static void didset_options(void)
(void)opt_strings_flags(p_tc, p_tc_values, &tc_flags, false);
(void)opt_strings_flags(p_tpf, p_tpf_values, &tpf_flags, true);
(void)opt_strings_flags(p_ve, p_ve_values, &ve_flags, true);
+ (void)opt_strings_flags(p_swb, p_swb_values, &swb_flags, true);
(void)opt_strings_flags(p_wop, p_wop_values, &wop_flags, true);
(void)opt_strings_flags(p_jop, p_jop_values, &jop_flags, true);
+}
+
+/// After setting various option values: recompute variables that depend on
+/// option values.
+static void didset_options(void)
+{
+ // initialize the table for 'iskeyword' et.al.
+ (void)init_chartab();
+
+ didset_string_options();
+
(void)spell_check_msm();
(void)spell_check_sps();
(void)compile_cap_prog(curwin->w_s);
(void)did_set_spell_option(true);
// set cedit_key
(void)check_cedit();
- briopt_check(curwin);
// initialize the table for 'breakat'.
fill_breakat_flags();
- fill_culopt_flags(NULL, curwin);
+ didset_window_options(curwin);
}
// More side effects of setting options.
@@ -1993,9 +2017,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).
@@ -2045,6 +2069,7 @@ void check_buf_options(buf_T *buf)
parse_cino(buf);
check_string_option(&buf->b_p_ft);
check_string_option(&buf->b_p_cinw);
+ check_string_option(&buf->b_p_cinsd);
check_string_option(&buf->b_p_cpt);
check_string_option(&buf->b_p_cfu);
check_string_option(&buf->b_p_ofu);
@@ -2120,6 +2145,8 @@ static uint32_t *insecure_flag(win_T *const wp, int opt_idx, int opt_flags)
switch ((int)options[opt_idx].indir) {
case PV_STL:
return &wp->w_p_stl_flags;
+ case PV_WBR:
+ return &wp->w_p_wbr_flags;
case PV_FDE:
return &wp->w_p_fde_flags;
case PV_FDT:
@@ -2137,7 +2164,6 @@ static uint32_t *insecure_flag(win_T *const wp, int opt_idx, int opt_flags)
return &options[opt_idx].flags;
}
-
/// Redraw the window title and/or tab page text later.
static void redraw_titles(void)
{
@@ -2155,11 +2181,11 @@ static int shada_idx = -1;
/// "set_sid".
///
/// @param opt_flags OPT_FREE, OPT_LOCAL and/or OPT_GLOBAL
-void set_string_option_direct(const char *name, int opt_idx, const char_u *val, int opt_flags,
+void set_string_option_direct(const char *name, int opt_idx, const char *val, int opt_flags,
int set_sid)
{
- char_u *s;
- char_u **varp;
+ char *s;
+ char **varp;
int both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0;
int idx = opt_idx;
@@ -2178,18 +2204,17 @@ void set_string_option_direct(const char *name, int opt_idx, const char_u *val,
assert((void *)options[idx].var != (void *)&p_shada);
- s = vim_strsave(val);
+ s = xstrdup(val);
{
- varp = (char_u **)get_varp_scope(&(options[idx]),
- both ? OPT_LOCAL : opt_flags);
+ varp = (char **)get_varp_scope(&(options[idx]), both ? OPT_LOCAL : opt_flags);
if ((opt_flags & OPT_FREE) && (options[idx].flags & P_ALLOCED)) {
- free_string_option(*varp);
+ free_string_option((char_u *)(*varp));
}
*varp = s;
// For buffer/window local option may also set the global value.
if (both) {
- set_string_option_global(idx, varp);
+ set_string_option_global(idx, (char_u **)varp);
}
options[idx].flags |= P_ALLOCED;
@@ -2197,8 +2222,8 @@ void set_string_option_direct(const char *name, int opt_idx, const char_u *val,
/* When setting both values of a global option with a local value,
* make the local value empty, so that the global value is used. */
if (((int)options[idx].indir & PV_BOTH) && both) {
- free_string_option(*varp);
- *varp = empty_option;
+ free_string_option((char_u *)(*varp));
+ *varp = (char *)empty_option;
}
if (set_sid != SID_NONE) {
sctx_T script_ctx;
@@ -2269,12 +2294,12 @@ static char *set_string_option(const int opt_idx, const char *const value, const
*varp = s;
char *const saved_oldval = xstrdup(oldval);
- char *const saved_oldval_l = (oldval_l != NULL) ? xstrdup((char *)oldval_l) : 0;
- char *const saved_oldval_g = (oldval_g != NULL) ? xstrdup((char *)oldval_g) : 0;
+ char *const saved_oldval_l = (oldval_l != NULL) ? xstrdup(oldval_l) : 0;
+ char *const saved_oldval_g = (oldval_g != NULL) ? xstrdup(oldval_g) : 0;
char *const saved_newval = xstrdup(s);
int value_checked = false;
- char *const r = did_set_string_option(opt_idx, (char_u **)varp, (int)true,
+ char *const r = did_set_string_option(opt_idx, (char_u **)varp, true,
(char_u *)oldval,
NULL, 0, opt_flags, &value_checked);
if (r == NULL) {
@@ -2307,7 +2332,7 @@ static bool valid_name(const char_u *val, const char *allowed)
{
for (const char_u *s = val; *s != NUL; s++) {
if (!ASCII_ISALNUM(*s)
- && vim_strchr((const char_u *)allowed, *s) == NULL) {
+ && vim_strchr(allowed, *s) == NULL) {
return false;
}
}
@@ -2341,6 +2366,69 @@ static bool valid_spellfile(const char_u *val)
return true;
}
+/// Handle setting 'mousescroll'.
+/// @return error message, NULL if it's OK.
+static char *check_mousescroll(char *string)
+{
+ long vertical = -1;
+ long horizontal = -1;
+
+ for (;;) {
+ char *end = vim_strchr(string, ',');
+ size_t length = end ? (size_t)(end - string) : STRLEN(string);
+
+ // Both "ver:" and "hor:" are 4 bytes long.
+ // They should be followed by at least one digit.
+ if (length <= 4) {
+ return e_invarg;
+ }
+
+ long *direction;
+
+ if (memcmp(string, "ver:", 4) == 0) {
+ direction = &vertical;
+ } else if (memcmp(string, "hor:", 4) == 0) {
+ direction = &horizontal;
+ } else {
+ return e_invarg;
+ }
+
+ // If the direction has already been set, this is a duplicate.
+ if (*direction != -1) {
+ return e_invarg;
+ }
+
+ // Verify that only digits follow the colon.
+ for (size_t i = 4; i < length; i++) {
+ if (!ascii_isdigit(string[i])) {
+ return N_("E548: digit expected");
+ }
+ }
+
+ string += 4;
+ *direction = getdigits_int(&string, false, -1);
+
+ // Num options are generally kept within the signed int range.
+ // We know this number won't be negative because we've already checked for
+ // a minus sign. We'll allow 0 as a means of disabling mouse scrolling.
+ if (*direction == -1) {
+ return e_invarg;
+ }
+
+ if (!end) {
+ break;
+ }
+
+ string = end + 1;
+ }
+
+ // If a direction wasn't set, fallback to the default value.
+ p_mousescroll_vert = (vertical == -1) ? MOUSESCROLL_VERT_DFLT : vertical;
+ p_mousescroll_hor = (horizontal == -1) ? MOUSESCROLL_HOR_DFLT : horizontal;
+
+ return NULL;
+}
+
/// Handle string options that need some action to perform when changed.
/// Returns NULL for success, or an error message for an error.
///
@@ -2372,10 +2460,10 @@ static char *did_set_string_option(int opt_idx, char_u **varp, bool new_value_al
&& (options[opt_idx].flags & P_SECURE)) {
errmsg = e_secure;
} else if (((options[opt_idx].flags & P_NFNAME)
- && vim_strpbrk(*varp, (char_u *)(secure ? "/\\*?[|;&<>\r\n"
- : "/\\*?[<>\r\n")) != NULL)
+ && strpbrk((char *)(*varp),
+ (secure ? "/\\*?[|;&<>\r\n" : "/\\*?[<>\r\n")) != NULL)
|| ((options[opt_idx].flags & P_NDNAME)
- && vim_strpbrk(*varp, (char_u *)"*?[|;&<>\r\n") != NULL)) {
+ && strpbrk((char *)(*varp), "*?[|;&<>\r\n") != NULL)) {
// Check for a "normal" directory or file name in some options. Disallow a
// path separator (slash and/or backslash), wildcards and characters that
// are often illegal in a file name. Be more permissive if "secure" is off.
@@ -2485,8 +2573,8 @@ static char *did_set_string_option(int opt_idx, char_u **varp, bool new_value_al
if (opt_strings_flags(p_rdb, p_rdb_values, &rdb_flags, true) != OK) {
errmsg = e_invarg;
}
- } else if (varp == &p_sbo) { // 'scrollopt'
- if (check_opt_strings(p_sbo, p_scbopt_values, true) != OK) {
+ } else if (varp == (char_u **)&p_sbo) { // 'scrollopt'
+ if (check_opt_strings((char_u *)p_sbo, p_scbopt_values, true) != OK) {
errmsg = e_invarg;
}
} else if (varp == &p_ambw || (int *)varp == &p_emoji) {
@@ -2549,7 +2637,7 @@ ambw_end:
if (gvarp == &p_fenc) {
if (!MODIFIABLE(curbuf) && opt_flags != OPT_GLOBAL) {
errmsg = e_modifiable;
- } else if (vim_strchr(*varp, ',') != NULL) {
+ } else if (vim_strchr((char *)(*varp), ',') != NULL) {
// No comma allowed in 'fileencoding'; catches confusing it
// with 'fileencodings'.
errmsg = e_invarg;
@@ -2570,6 +2658,8 @@ ambw_end:
// only encoding=utf-8 allowed
if (STRCMP(p_enc, "utf-8") != 0) {
errmsg = e_unsupportedoption;
+ } else {
+ spell_reload();
}
}
}
@@ -2635,8 +2725,8 @@ ambw_end:
redraw_curbuf_later(NOT_VALID);
}
}
- } else if (varp == &p_ffs) { // 'fileformats'
- if (check_opt_strings(p_ffs, p_ff_values, true) != OK) {
+ } else if (varp == (char_u **)&p_ffs) { // 'fileformats'
+ if (check_opt_strings((char_u *)p_ffs, p_ff_values, true) != OK) {
errmsg = e_invarg;
}
} else if (gvarp == &p_mps) { // 'matchpairs'
@@ -2644,15 +2734,13 @@ ambw_end:
int x2 = -1;
int x3 = -1;
- if (*p != NUL) {
- p += utfc_ptr2len(p);
- }
+ p += utfc_ptr2len((char *)p);
if (*p != NUL) {
x2 = *p++;
}
if (*p != NUL) {
- x3 = utf_ptr2char(p);
- p += utfc_ptr2len(p);
+ x3 = utf_ptr2char((char *)p);
+ p += utfc_ptr2len((char *)p);
}
if (x2 != ':' || x3 == -1 || (*p != NUL && *p != ',')) {
errmsg = e_invarg;
@@ -2665,7 +2753,7 @@ ambw_end:
} else if (gvarp == &p_com) { // 'comments'
for (s = *varp; *s;) {
while (*s && *s != ':') {
- if (vim_strchr((char_u *)COM_ALL, *s) == NULL
+ if (vim_strchr(COM_ALL, *s) == NULL
&& !ascii_isdigit(*s) && *s != '-') {
errmsg = illegal_char(errbuf, errbuflen, *s);
break;
@@ -2744,7 +2832,7 @@ ambw_end:
free_oldval = (options[opt_idx].flags & P_ALLOCED);
for (s = p_shada; *s;) {
// Check it's a valid character
- if (vim_strchr((char_u *)"!\"%'/:<@cfhnrs", *s) == NULL) {
+ if (vim_strchr("!\"%'/:<@cfhnrs", *s) == NULL) {
errmsg = illegal_char(errbuf, errbuflen, *s);
break;
}
@@ -2762,7 +2850,7 @@ ambw_end:
if (!ascii_isdigit(*(s - 1))) {
if (errbuf != NULL) {
- vim_snprintf((char *)errbuf, errbuflen,
+ vim_snprintf(errbuf, errbuflen,
_("E526: Missing number after <%s>"),
transchar_byte(*(s - 1)));
errmsg = errbuf;
@@ -2788,7 +2876,7 @@ ambw_end:
}
} else if (gvarp == &p_sbr) { // 'showbreak'
for (s = *varp; *s;) {
- if (ptr2cells(s) != 1) {
+ if (ptr2cells((char *)s) != 1) {
errmsg = N_("E595: 'showbreak' contains unprintable or wide character");
}
MB_PTR_ADV(s);
@@ -2808,7 +2896,7 @@ ambw_end:
int flagval = (varp == &p_titlestring) ? STL_IN_TITLE : STL_IN_ICON;
// NULL => statusline syntax
- if (vim_strchr(*varp, '%') && check_stl_option(*varp) == NULL) {
+ if (vim_strchr((char *)(*varp), '%') && check_stl_option((char *)(*varp)) == NULL) {
stl_syntax |= flagval;
} else {
stl_syntax &= ~flagval;
@@ -2827,13 +2915,15 @@ ambw_end:
if (check_opt_strings(p_km, p_km_values, true) != OK) {
errmsg = e_invarg;
} else {
- km_stopsel = (vim_strchr(p_km, 'o') != NULL);
- km_startsel = (vim_strchr(p_km, 'a') != NULL);
+ km_stopsel = (vim_strchr((char *)p_km, 'o') != NULL);
+ km_startsel = (vim_strchr((char *)p_km, 'a') != NULL);
}
} else if (varp == &p_mousem) { // 'mousemodel'
if (check_opt_strings(p_mousem, p_mousem_values, false) != OK) {
errmsg = e_invarg;
}
+ } else if (varp == &p_mousescroll) { // 'mousescroll'
+ errmsg = check_mousescroll((char *)p_mousescroll);
} else if (varp == &p_swb) { // 'switchbuf'
if (opt_strings_flags(p_swb, p_swb_values, &swb_flags, true) != OK) {
errmsg = e_invarg;
@@ -2896,15 +2986,15 @@ ambw_end:
|| check_opt_strings(curbuf->b_p_bt, p_buftype_values, false) != OK) {
errmsg = e_invarg;
} else {
- if (curwin->w_status_height) {
+ if (curwin->w_status_height || global_stl_height()) {
curwin->w_redr_status = true;
redraw_later(curwin, VALID);
}
curbuf->b_help = (curbuf->b_p_bt[0] == 'h');
redraw_titles();
}
- } else if (gvarp == &p_stl || varp == &p_ruf) {
- // 'statusline' or 'rulerformat'
+ } else if (gvarp == &p_stl || gvarp == (char_u **)&p_wbr || varp == &p_tal || varp == &p_ruf) {
+ // 'statusline', 'winbar', 'tabline' or 'rulerformat'
int wid;
if (varp == &p_ruf) { // reset ru_wid first
@@ -2916,19 +3006,23 @@ ambw_end:
if (*++s == '-') { // ignore a '-'
s++;
}
- wid = getdigits_int(&s, true, 0);
- if (wid && *s == '(' && (errmsg = check_stl_option(p_ruf)) == NULL) {
+ wid = getdigits_int((char **)&s, true, 0);
+ if (wid && *s == '(' && (errmsg = check_stl_option((char *)p_ruf)) == NULL) {
ru_wid = wid;
} else {
- errmsg = check_stl_option(p_ruf);
+ errmsg = check_stl_option((char *)p_ruf);
}
} else if (varp == &p_ruf || s[0] != '%' || s[1] != '!') {
- // check 'statusline' only if it doesn't start with "%!"
- errmsg = check_stl_option(s);
+ // check 'statusline', 'winbar' or 'tabline' only if it doesn't start with "%!"
+ errmsg = check_stl_option((char *)s);
}
if (varp == &p_ruf && errmsg == NULL) {
comp_col();
}
+ // add / remove window bars for 'winbar'
+ if (gvarp == (char_u **)&p_wbr) {
+ set_winbar();
+ }
} else if (gvarp == &p_cpt) {
// check if it is a valid value for 'complete' -- Acevedo
for (s = *varp; *s;) {
@@ -2938,7 +3032,7 @@ ambw_end:
if (!*s) {
break;
}
- if (vim_strchr((char_u *)".wbuksid]tU", *s) == NULL) {
+ if (vim_strchr(".wbuksid]tU", *s) == NULL) {
errmsg = illegal_char(errbuf, errbuflen, *s);
break;
}
@@ -2953,7 +3047,7 @@ ambw_end:
}
} else {
if (errbuf != NULL) {
- vim_snprintf((char *)errbuf, errbuflen,
+ vim_snprintf(errbuf, errbuflen,
_("E535: Illegal character after <%c>"),
*--s);
errmsg = errbuf;
@@ -2997,7 +3091,10 @@ ambw_end:
} else if (varp == &p_pt) {
// 'pastetoggle': translate key codes like in a mapping
if (*p_pt) {
- (void)replace_termcodes(p_pt, STRLEN(p_pt), &p, true, true, true,
+ p = NULL;
+ (void)replace_termcodes((char *)p_pt,
+ STRLEN(p_pt),
+ (char **)&p, REPTERM_FROM_PART | REPTERM_DO_LT, NULL,
CPO_TO_CPO_FLAGS);
if (p != NULL) {
if (new_value_alloced) {
@@ -3060,7 +3157,7 @@ ambw_end:
foldUpdateAll(curwin);
}
} else if (gvarp == &curwin->w_allbuf_opt.wo_fmr) { // 'foldmarker'
- p = vim_strchr(*varp, ',');
+ p = (char_u *)vim_strchr((char *)(*varp), ',');
if (p == NULL) {
errmsg = N_("E536: comma required");
} else if (p == *varp || p[1] == NUL) {
@@ -3084,22 +3181,35 @@ 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) {
p = p_csqf;
while (*p != NUL) {
- if (vim_strchr((char_u *)CSQF_CMDS, *p) == NULL
+ if (vim_strchr(CSQF_CMDS, *p) == NULL
|| p[1] == NUL
- || vim_strchr((char_u *)CSQF_FLAGS, p[1]) == NULL
+ || vim_strchr(CSQF_FLAGS, p[1]) == NULL
|| (p[2] != NUL && p[2] != ',')) {
errmsg = e_invarg;
break;
@@ -3150,10 +3260,7 @@ ambw_end:
char_u *cp;
if (!(*varp)[0] || ((*varp)[0] == '0' && !(*varp)[1])) {
- if (curbuf->b_p_vsts_array) {
- xfree(curbuf->b_p_vsts_array);
- curbuf->b_p_vsts_array = 0;
- }
+ XFREE_CLEAR(curbuf->b_p_vsts_array);
} else {
for (cp = *varp; *cp; cp++) {
if (ascii_isdigit(*cp)) {
@@ -3178,10 +3285,7 @@ ambw_end:
char_u *cp;
if (!(*varp)[0] || ((*varp)[0] == '0' && !(*varp)[1])) {
- if (curbuf->b_p_vts_array) {
- xfree(curbuf->b_p_vts_array);
- curbuf->b_p_vts_array = NULL;
- }
+ XFREE_CLEAR(curbuf->b_p_vts_array);
} else {
for (cp = *varp; *cp; cp++) {
if (ascii_isdigit(*cp)) {
@@ -3217,7 +3321,7 @@ ambw_end:
}
if (varp == &p_shm) { // 'shortmess'
p = (char_u *)SHM_ALL;
- } else if (varp == &(p_cpo)) { // 'cpoptions'
+ } else if (varp == (char_u **)&(p_cpo)) { // 'cpoptions'
p = (char_u *)CPO_VI;
} else if (varp == &(curbuf->b_p_fo)) { // 'formatoptions'
p = (char_u *)FO_ALL;
@@ -3228,7 +3332,7 @@ ambw_end:
}
if (p != NULL) {
for (s = *varp; *s; s++) {
- if (vim_strchr(p, *s) == NULL) {
+ if (vim_strchr((char *)p, *s) == NULL) {
errmsg = illegal_char(errbuf, errbuflen, *s);
break;
}
@@ -3287,7 +3391,7 @@ ambw_end:
syn_recursive++;
// Only pass true for "force" when the value changed or not used
// recursively, to avoid endless recurrence.
- apply_autocmds(EVENT_SYNTAX, curbuf->b_p_syn, curbuf->b_fname,
+ apply_autocmds(EVENT_SYNTAX, (char *)curbuf->b_p_syn, curbuf->b_fname,
value_changed || syn_recursive == 1, curbuf);
curbuf->b_flags |= BF_SYN_SET;
syn_recursive--;
@@ -3307,7 +3411,7 @@ ambw_end:
did_filetype = true;
// Only pass true for "force" when the value changed or not
// used recursively, to avoid endless recurrence.
- apply_autocmds(EVENT_FILETYPE, curbuf->b_p_ft, curbuf->b_fname,
+ apply_autocmds(EVENT_FILETYPE, (char *)curbuf->b_p_ft, curbuf->b_fname,
value_changed || ft_recursive == 1, curbuf);
ft_recursive--;
// Just in case the old "curbuf" is now invalid
@@ -3357,7 +3461,7 @@ ambw_end:
check_redraw(options[opt_idx].flags);
return errmsg;
-} // NOLINT(readability/fn_size)
+}
/// Simple int comparison function for use with qsort()
static int int_cmp(const void *a, const void *b)
@@ -3418,7 +3522,7 @@ char *check_colorcolumn(win_T *wp)
if (!ascii_isdigit(*s)) {
return e_invarg;
}
- col = col * getdigits_int(&s, true, 0);
+ col = col * getdigits_int((char **)&s, true, 0);
if (wp->w_buffer->b_p_tw == 0) {
goto skip; // 'textwidth' not set, skip this item
}
@@ -3433,7 +3537,7 @@ char *check_colorcolumn(win_T *wp)
goto skip;
}
} else if (ascii_isdigit(*s)) {
- col = getdigits_int(&s, true, 0);
+ col = getdigits_int((char **)&s, true, 0);
} else {
return e_invarg;
}
@@ -3501,7 +3605,7 @@ static int get_encoded_char_adv(char_u **p)
}
// TODO(bfredl): use schar_T representation and utfc_ptr2len
- int clen = utf_ptr2len(s);
+ int clen = utf_ptr2len((char *)s);
int c = mb_cptr2char_adv((const char_u **)p);
if (clen == 1 && c > 127) { // Invalid UTF-8 byte
return 0;
@@ -3516,13 +3620,15 @@ static int get_encoded_char_adv(char_u **p)
/// @return error message, NULL if it's OK.
static char *set_chars_option(win_T *wp, char_u **varp, bool set)
{
- int round, i, len, entries;
+ int round, i, len, len2, entries;
char_u *p, *s;
int c1;
int c2 = 0;
int c3 = 0;
- char_u *last_multispace = NULL; // Last occurrence of "multispace:"
- int multispace_len = 0; // Length of lcs-multispace string
+ char_u *last_multispace = NULL; // Last occurrence of "multispace:"
+ char_u *last_lmultispace = NULL; // Last occurrence of "leadmultispace:"
+ int multispace_len = 0; // Length of lcs-multispace string
+ int lead_multispace_len = 0; // Length of lcs-leadmultispace string
struct chars_tab {
int *cp; ///< char value
@@ -3532,17 +3638,24 @@ static char *set_chars_option(win_T *wp, char_u **varp, bool set)
struct chars_tab *tab;
struct chars_tab fcs_tab[] = {
- { &wp->w_p_fcs_chars.stl, "stl", ' ' },
- { &wp->w_p_fcs_chars.stlnc, "stlnc", ' ' },
- { &wp->w_p_fcs_chars.vert, "vert", 9474 }, // │
- { &wp->w_p_fcs_chars.fold, "fold", 183 }, // ·
- { &wp->w_p_fcs_chars.foldopen, "foldopen", '-' },
- { &wp->w_p_fcs_chars.foldclosed, "foldclose", '+' },
- { &wp->w_p_fcs_chars.foldsep, "foldsep", 9474 }, // │
- { &wp->w_p_fcs_chars.diff, "diff", '-' },
- { &wp->w_p_fcs_chars.msgsep, "msgsep", ' ' },
- { &wp->w_p_fcs_chars.eob, "eob", '~' },
- { &wp->w_p_fcs_chars.colorcol, "colorcol", NUL },
+ { &wp->w_p_fcs_chars.colorcol, "colorcol", NUL },
+ { &wp->w_p_fcs_chars.stl, "stl", ' ' },
+ { &wp->w_p_fcs_chars.stlnc, "stlnc", ' ' },
+ { &wp->w_p_fcs_chars.wbr, "wbr", ' ' },
+ { &wp->w_p_fcs_chars.horiz, "horiz", 9472 }, // ─
+ { &wp->w_p_fcs_chars.horizup, "horizup", 9524 }, // ┴
+ { &wp->w_p_fcs_chars.horizdown, "horizdown", 9516 }, // ┬
+ { &wp->w_p_fcs_chars.vert, "vert", 9474 }, // │
+ { &wp->w_p_fcs_chars.vertleft, "vertleft", 9508 }, // ┤
+ { &wp->w_p_fcs_chars.vertright, "vertright", 9500 }, // ├
+ { &wp->w_p_fcs_chars.verthoriz, "verthoriz", 9532 }, // ┼
+ { &wp->w_p_fcs_chars.fold, "fold", 183 }, // ·
+ { &wp->w_p_fcs_chars.foldopen, "foldopen", '-' },
+ { &wp->w_p_fcs_chars.foldclosed, "foldclose", '+' },
+ { &wp->w_p_fcs_chars.foldsep, "foldsep", 9474 }, // │
+ { &wp->w_p_fcs_chars.diff, "diff", '-' },
+ { &wp->w_p_fcs_chars.msgsep, "msgsep", ' ' },
+ { &wp->w_p_fcs_chars.eob, "eob", '~' },
};
struct chars_tab lcs_tab[] = {
{ &wp->w_p_lcs_chars.eol, "eol", NUL },
@@ -3569,15 +3682,17 @@ static char *set_chars_option(win_T *wp, char_u **varp, bool set)
varp = &p_fcs;
}
if (*p_ambw == 'd') {
- // XXX: If ambiwidth=double then "|" and "·" take 2 columns, which is
- // forbidden (TUI limitation?). Set old defaults.
- fcs_tab[2].def = '|';
- fcs_tab[6].def = '|';
- fcs_tab[3].def = '-';
- } else {
- fcs_tab[2].def = 9474; // │
- fcs_tab[6].def = 9474; // │
- fcs_tab[3].def = 183; // ·
+ // XXX: If ambiwidth=double then some characters take 2 columns,
+ // which is forbidden (TUI limitation?). Set old defaults.
+ fcs_tab[3].def = '-';
+ fcs_tab[4].def = '-';
+ fcs_tab[5].def = '-';
+ fcs_tab[6].def = '|';
+ fcs_tab[7].def = '|';
+ fcs_tab[8].def = '|';
+ fcs_tab[9].def = '+';
+ fcs_tab[10].def = '-';
+ fcs_tab[13].def = '|';
}
}
@@ -3593,15 +3708,23 @@ static char *set_chars_option(win_T *wp, char_u **varp, bool set)
if (varp == &p_lcs || varp == &wp->w_p_lcs) {
wp->w_p_lcs_chars.tab1 = NUL;
wp->w_p_lcs_chars.tab3 = NUL;
- if (wp->w_p_lcs_chars.multispace != NULL) {
- xfree(wp->w_p_lcs_chars.multispace);
- }
+
+ xfree(wp->w_p_lcs_chars.multispace);
if (multispace_len > 0) {
wp->w_p_lcs_chars.multispace = xmalloc(((size_t)multispace_len + 1) * sizeof(int));
wp->w_p_lcs_chars.multispace[multispace_len] = NUL;
} else {
wp->w_p_lcs_chars.multispace = NULL;
}
+
+ xfree(wp->w_p_lcs_chars.leadmultispace);
+ if (lead_multispace_len > 0) {
+ wp->w_p_lcs_chars.leadmultispace
+ = xmalloc(((size_t)lead_multispace_len + 1) * sizeof(int));
+ wp->w_p_lcs_chars.leadmultispace[lead_multispace_len] = NUL;
+ } else {
+ wp->w_p_lcs_chars.leadmultispace = NULL;
+ }
}
}
p = *varp;
@@ -3650,6 +3773,7 @@ static char *set_chars_option(win_T *wp, char_u **varp, bool set)
if (i == entries) {
len = (int)STRLEN("multispace");
+ len2 = (int)STRLEN("leadmultispace");
if ((varp == &p_lcs || varp == &wp->w_p_lcs)
&& STRNCMP(p, "multispace", len) == 0
&& p[len] == ':'
@@ -3681,6 +3805,37 @@ static char *set_chars_option(win_T *wp, char_u **varp, bool set)
}
p = s;
}
+ } else if ((varp == &p_lcs || varp == &wp->w_p_lcs)
+ && STRNCMP(p, "leadmultispace", len2) == 0
+ && p[len2] == ':'
+ && p[len2 + 1] != NUL) {
+ s = p + len2 + 1;
+ if (round == 0) {
+ // get length of lcs-leadmultispace string in first round
+ last_lmultispace = p;
+ lead_multispace_len = 0;
+ while (*s != NUL && *s != ',') {
+ c1 = get_encoded_char_adv(&s);
+ if (c1 == 0 || char2cells(c1) > 1) {
+ return e_invarg;
+ }
+ lead_multispace_len++;
+ }
+ if (lead_multispace_len == 0) {
+ // lcs-leadmultispace cannot be an empty string
+ return e_invarg;
+ }
+ p = s;
+ } else {
+ int multispace_pos = 0;
+ while (*s != NUL && *s != ',') {
+ c1 = get_encoded_char_adv(&s);
+ if (p == last_lmultispace) {
+ wp->w_p_lcs_chars.leadmultispace[multispace_pos++] = c1;
+ }
+ }
+ p = s;
+ }
} else {
return e_invarg;
}
@@ -3695,8 +3850,8 @@ static char *set_chars_option(win_T *wp, char_u **varp, bool set)
}
/// Check validity of options with the 'statusline' format.
-/// Return error message or NULL.
-char *check_stl_option(char_u *s)
+/// Return an untranslated error message or NULL.
+char *check_stl_option(char *s)
{
int groupdepth = 0;
static char errbuf[80];
@@ -3744,18 +3899,22 @@ char *check_stl_option(char_u *s)
return illegal_char(errbuf, sizeof(errbuf), *s);
}
if (*s == '{') {
- int reevaluate = (*s == '%');
- s++;
+ bool reevaluate = (*++s == '%');
+
+ if (reevaluate && *++s == '}') {
+ // "}" is not allowed immediately after "%{%"
+ return illegal_char(errbuf, sizeof(errbuf), '}');
+ }
while ((*s != '}' || (reevaluate && s[-1] != '%')) && *s) {
s++;
}
if (*s != '}') {
- return N_("E540: Unclosed expression sequence");
+ return e_unclosed_expression_sequence;
}
}
}
if (groupdepth != 0) {
- return N_("E542: unbalanced groups");
+ return e_unbalanced_groups;
}
return NULL;
}
@@ -3797,7 +3956,7 @@ static char *compile_cap_prog(synblock_T *synblock)
} else {
// Prepend a ^ so that we only match at one column
re = concat_str((char_u *)"^", synblock->b_p_spc);
- synblock->b_cap_prog = vim_regcomp(re, RE_MAGIC);
+ synblock->b_cap_prog = vim_regcomp((char *)re, RE_MAGIC);
xfree(re);
if (synblock->b_cap_prog == NULL) {
synblock->b_cap_prog = rp; // restore the previous program
@@ -3822,16 +3981,16 @@ static bool parse_winhl_opt(win_T *wp)
if (!colon) {
return false;
}
- size_t nlen = (size_t)(colon-p);
- char *hi = colon+1;
+ size_t nlen = (size_t)(colon - p);
+ char *hi = colon + 1;
char *commap = xstrchrnul(hi, ',');
- int len = (int)(commap-hi);
+ size_t len = (size_t)(commap - hi);
int hl_id = len ? syn_check_group(hi, len) : -1;
if (strncmp("Normal", p, nlen) == 0) {
w_hl_id_normal = hl_id;
} else {
- for (hlf = 0; hlf < (int)HLF_COUNT; hlf++) {
+ for (hlf = 0; hlf < HLF_COUNT; hlf++) {
if (strlen(hlf_names[hlf]) == nlen
&& strncmp(hlf_names[hlf], p, nlen) == 0) {
w_hl_ids[hlf] = hl_id;
@@ -3843,7 +4002,7 @@ static bool parse_winhl_opt(win_T *wp)
}
}
- p = *commap ? commap+1 : "";
+ p = *commap ? commap + 1 : "";
}
wp->w_hl_id_normal = w_hl_id_normal;
@@ -3858,6 +4017,7 @@ static void set_option_sctx_idx(int opt_idx, int opt_flags, sctx_T script_ctx)
{
int both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0;
int indir = (int)options[opt_idx].indir;
+ nlua_set_sctx(&script_ctx);
const LastSet last_set = {
.script_ctx = {
script_ctx.sc_sid,
@@ -3912,7 +4072,6 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va
// Remember where the option was set.
set_option_sctx_idx(opt_idx, opt_flags, current_sctx);
-
// May set global value for local option.
if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
*(int *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL) = value;
@@ -3932,11 +4091,8 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va
} else if ((int *)varp == &p_lnr) {
// 'langnoremap' -> !'langremap'
p_lrm = !p_lnr;
- } else if ((int *)varp == &curwin->w_p_cul && !value && old_value) {
- // 'cursorline'
- reset_cursorline();
- // 'undofile'
} else if ((int *)varp == &curbuf->b_p_udf || (int *)varp == &p_udf) {
+ // 'undofile'
// Only take action when the option was set. When reset we do not
// delete the undo file, the option may be set again without making
// any changes in between.
@@ -3952,7 +4108,7 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va
|| (opt_flags & OPT_GLOBAL) || opt_flags == 0)
&& !bufIsChanged(bp) && bp->b_ml.ml_mfp != NULL) {
u_compute_hash(bp, hash);
- u_read_undo(NULL, hash, bp->b_fname);
+ u_read_undo(NULL, hash, (char_u *)bp->b_fname);
}
}
}
@@ -3997,38 +4153,9 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va
// buf->b_p_swf
mf_close_file(curbuf, true); // remove the swap file
}
- } else if ((int *)varp == &p_terse) {
- // when 'terse' is set change 'shortmess'
- char_u *p;
-
- p = vim_strchr(p_shm, SHM_SEARCH);
-
- // insert 's' in p_shm
- if (p_terse && p == NULL) {
- STRCPY(IObuff, p_shm);
- STRCAT(IObuff, "s");
- set_string_option_direct("shm", -1, IObuff, OPT_FREE, 0);
- } else if (!p_terse && p != NULL) { // remove 's' from p_shm
- STRMOVE(p, p + 1);
- }
} else if ((int *)varp == &p_paste) {
// when 'paste' is set or reset also change other options
paste_option_changed();
- } else if ((int *)varp == &p_im) {
- // when 'insertmode' is set from an autocommand need to do work here
- if (p_im) {
- if ((State & INSERT) == 0) {
- need_start_insertmode = true;
- }
- stop_insert_mode = false;
- } else if (old_value) { // only reset if it was set previously
- need_start_insertmode = false;
- stop_insert_mode = true;
- if (restart_edit != 0 && mode_displayed) {
- clear_cmdline = true; // remove "(insert)"
- }
- restart_edit = 0;
- }
} else if ((int *)varp == &p_ic && p_hls) {
// when 'ignorecase' is set or reset and 'hlsearch' is set, redraw
redraw_all_later(SOME_VALID);
@@ -4172,7 +4299,6 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va
}
}
-
/*
* End of handling side effects for bool options.
*/
@@ -4212,13 +4338,13 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va
set_vim_var_string(VV_OPTION_COMMAND, "modeline", -1);
set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1);
}
- apply_autocmds(EVENT_OPTIONSET, (char_u *)options[opt_idx].fullname, NULL, false, NULL);
+ apply_autocmds(EVENT_OPTIONSET, options[opt_idx].fullname, NULL, false, NULL);
reset_v_option_vars();
}
if (options[opt_idx].flags & P_UI_OPTION) {
ui_call_option_set(cstr_as_string(options[opt_idx].fullname),
- BOOLEAN_OBJ(value));
+ BOOLEAN_OBJ(*varp));
}
comp_col(); // in case 'ruler' or 'showcmd' changed
@@ -4257,7 +4383,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf,
}
// Save the global value before changing anything. This is needed as for
- // a global-only option setting the "local value" infact sets the global
+ // a global-only option setting the "local value" in fact sets the global
// value (since there is only one value).
if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
old_global_value = *(long *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL);
@@ -4308,7 +4434,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf,
errmsg = e_positive;
}
} else if (pp == &p_ch) {
- int minval = ui_has(kUIMessages) ? 0 : 1;
+ int minval = 0;
if (value < minval) {
errmsg = e_positive;
}
@@ -4322,6 +4448,12 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf,
} else if (value > 10000) {
errmsg = e_invarg;
}
+ } else if (pp == &p_pyx) {
+ if (value == 0) {
+ value = 3;
+ } else if (value != 3) {
+ errmsg = e_invarg;
+ }
} else if (pp == &p_re) {
if (value < 0 || value > 2) {
errmsg = e_invarg;
@@ -4387,6 +4519,8 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf,
} else if (pp == &curbuf->b_p_ts || pp == &p_ts) {
if (value < 1) {
errmsg = e_positive;
+ } else if (value > TABSTOP_MAX) {
+ errmsg = e_invarg;
}
} else if (pp == &curbuf->b_p_tw || pp == &p_tw) {
if (value < 0) {
@@ -4400,7 +4534,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf,
// Don't change the value and return early if validation failed.
if (errmsg != NULL) {
- return (char *)errmsg;
+ return errmsg;
}
*pp = value;
@@ -4446,10 +4580,24 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf,
// 'winminwidth'
win_setminwidth();
} else if (pp == &p_ls) {
+ // When switching to global statusline, decrease topframe height
+ // Also clear the cmdline to remove the ruler if there is one
+ if (value == 3 && old_value != 3) {
+ frame_new_height(topframe, topframe->fr_height - STATUS_HEIGHT, false, false);
+ (void)win_comp_pos();
+ clear_cmdline = true;
+ }
+ // When switching from global statusline, increase height of topframe by STATUS_HEIGHT
+ // in order to to re-add the space that was previously taken by the global statusline
+ if (old_value == 3 && value != 3) {
+ frame_new_height(topframe, topframe->fr_height + STATUS_HEIGHT, false, false);
+ (void)win_comp_pos();
+ }
+
last_status(false); // (re)set last window status line.
} else if (pp == &p_stal) {
// (re)set tab page line
- shell_new_rows(); // recompute window positions and heights
+ win_new_screen_rows(); // recompute window positions and heights
} else if (pp == &curwin->w_p_fdl) {
newFoldLevel();
} else if (pp == &curwin->w_p_fml) {
@@ -4496,10 +4644,6 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf,
if (pum_drawn()) {
pum_redraw();
}
- } else if (pp == &p_pyx) {
- if (p_pyx != 0 && p_pyx != 2 && p_pyx != 3) {
- errmsg = e_invarg;
- }
} else if (pp == &p_ul || pp == &curbuf->b_p_ul) {
// sync undo before 'undolevels' changes
// use the old value, otherwise u_sync() may not work properly
@@ -4511,24 +4655,23 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf,
check_colorcolumn(wp);
}
} else if (pp == &curbuf->b_p_scbk || pp == &p_scbk) {
- if (curbuf->terminal) {
- // Force the scrollback to take effect.
- terminal_check_size(curbuf->terminal);
+ if (curbuf->terminal && value < old_value) {
+ // Force the scrollback to take immediate effect only when decreasing it.
+ on_scrollback_option_changed(curbuf->terminal);
}
} else if (pp == &curwin->w_p_nuw) {
curwin->w_nrwidth_line_count = 0;
} else if (pp == &curwin->w_p_winbl && value != old_value) {
- // 'floatblend'
+ // 'winblend'
curwin->w_p_winbl = MAX(MIN(curwin->w_p_winbl, 100), 0);
curwin->w_hl_needs_update = true;
check_blending(curwin);
}
-
// Check the (new) bounds for Rows and Columns here.
if (p_lines < min_rows() && full_screen) {
if (errbuf != NULL) {
- vim_snprintf((char *)errbuf, errbuflen,
+ vim_snprintf(errbuf, errbuflen,
_("E593: Need at least %d lines"), min_rows());
errmsg = errbuf;
}
@@ -4536,14 +4679,14 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf,
}
if (p_columns < MIN_COLUMNS && full_screen) {
if (errbuf != NULL) {
- vim_snprintf((char *)errbuf, errbuflen,
+ vim_snprintf(errbuf, errbuflen,
_("E594: Need at least %d columns"), MIN_COLUMNS);
errmsg = errbuf;
}
p_columns = MIN_COLUMNS;
}
- // True max size is defined by check_shellsize()
+ // True max size is defined by check_screensize()
p_lines = MIN(p_lines, INT_MAX);
p_columns = MIN(p_columns, INT_MAX);
@@ -4561,7 +4704,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf,
// messages.
Rows = (int)p_lines;
Columns = (int)p_columns;
- check_shellsize();
+ check_screensize();
if (cmdline_row > Rows - p_ch && Rows > p_ch) {
assert(p_ch >= 0 && Rows - p_ch <= INT_MAX);
cmdline_row = (int)(Rows - p_ch);
@@ -4636,13 +4779,13 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf,
set_vim_var_string(VV_OPTION_COMMAND, "modeline", -1);
set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1);
}
- apply_autocmds(EVENT_OPTIONSET, (char_u *)options[opt_idx].fullname, NULL, false, NULL);
+ apply_autocmds(EVENT_OPTIONSET, options[opt_idx].fullname, NULL, false, NULL);
reset_v_option_vars();
}
if (errmsg == NULL && options[opt_idx].flags & P_UI_OPTION) {
ui_call_option_set(cstr_as_string(options[opt_idx].fullname),
- INTEGER_OBJ(value));
+ INTEGER_OBJ(*pp));
}
comp_col(); // in case 'columns' or 'ls' changed
@@ -4652,7 +4795,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf,
}
check_redraw(options[opt_idx].flags);
- return (char *)errmsg;
+ return errmsg;
}
/// Trigger the OptionSet autocommand.
@@ -4693,7 +4836,7 @@ static void trigger_optionsset_string(int opt_idx, int opt_flags, char *oldval,
set_vim_var_string(VV_OPTION_COMMAND, "modeline", -1);
set_vim_var_string(VV_OPTION_OLDLOCAL, oldval, -1);
}
- apply_autocmds(EVENT_OPTIONSET, (char_u *)options[opt_idx].fullname, NULL, false, NULL);
+ apply_autocmds(EVENT_OPTIONSET, options[opt_idx].fullname, NULL, false, NULL);
reset_v_option_vars();
}
}
@@ -4705,7 +4848,7 @@ static void check_redraw(uint32_t flags)
bool doclear = (flags & P_RCLR) == P_RCLR;
bool all = ((flags & P_RALL) == P_RALL || doclear);
- if ((flags & P_RSTAT) || all) { // mark all status lines dirty
+ if ((flags & P_RSTAT) || all) { // mark all status lines and window bars dirty
status_redraw_all();
}
@@ -4747,7 +4890,7 @@ int findoption_len(const char *const arg, const size_t len)
if (s[0] == 't' && s[1] == '_') {
quick_tab[26] = i;
} else {
- quick_tab[CharOrdLow(s[0])] = i;
+ quick_tab[CHAR_ORD_LOW(s[0])] = i;
}
}
p = s;
@@ -4764,7 +4907,7 @@ int findoption_len(const char *const arg, const size_t len)
if (is_term_opt) {
opt_idx = quick_tab[26];
} else {
- opt_idx = quick_tab[CharOrdLow(arg[0])];
+ opt_idx = quick_tab[CHAR_ORD_LOW(arg[0])];
}
// Match full name
for (; (s = options[opt_idx].fullname) != NULL; opt_idx++) {
@@ -4773,7 +4916,7 @@ int findoption_len(const char *const arg, const size_t len)
}
}
if (s == NULL && !is_term_opt) {
- opt_idx = quick_tab[CharOrdLow(arg[0])];
+ opt_idx = quick_tab[CHAR_ORD_LOW(arg[0])];
// Match short name
for (; options[opt_idx].fullname != NULL; opt_idx++) {
s = options[opt_idx].shortname;
@@ -4869,6 +5012,23 @@ bool set_tty_option(const char *name, char *value)
return false;
}
+void set_tty_background(const char *value)
+{
+ if (option_was_set("bg") || strequal((char *)p_bg, value)) {
+ // background is already set... ignore
+ return;
+ }
+ if (starting) {
+ // Wait until after startup, so OptionSet is triggered.
+ do_cmdline_cmd((value[0] == 'l')
+ ? "autocmd VimEnter * ++once ++nested set bg=light"
+ : "autocmd VimEnter * ++once ++nested set bg=dark");
+ } else {
+ set_option_value("bg", 0L, value, 0);
+ reset_option_was_set("bg");
+ }
+}
+
/// Find index for an option
///
/// @param[in] arg Option name.
@@ -4891,9 +5051,9 @@ static int findoption(const char *const arg)
/// Hidden Number or Toggle option: -1.
/// hidden String option: -2.
/// unknown option: -3.
-int get_option_value(const char *name, long *numval, char_u **stringval, int opt_flags)
+int get_option_value(const char *name, long *numval, char **stringval, int opt_flags)
{
- if (get_tty_option(name, (char **)stringval)) {
+ if (get_tty_option(name, stringval)) {
return 0;
}
@@ -4909,7 +5069,11 @@ int get_option_value(const char *name, long *numval, char_u **stringval, int opt
return -2;
}
if (stringval != NULL) {
- *stringval = vim_strsave(*(char_u **)(varp));
+ if ((char_u **)varp == &p_pt) { // 'pastetoggle'
+ *stringval = str2special_save(*(char **)(varp), false, false);
+ } else {
+ *stringval = xstrdup(*(char **)(varp));
+ }
}
return 0;
}
@@ -5095,43 +5259,43 @@ char *set_option_value(const char *const name, const long number, const char *co
s = "";
}
return set_string_option(opt_idx, s, opt_flags);
- } else {
- varp = get_varp_scope(&(options[opt_idx]), opt_flags);
- if (varp != NULL) { // hidden option is not changed
- if (number == 0 && string != NULL) {
- int idx;
-
- // Either we are given a string or we are setting option
- // to zero.
- for (idx = 0; string[idx] == '0'; idx++) {}
- if (string[idx] != NUL || idx == 0) {
- // There's another character after zeros or the string
- // is empty. In both cases, we are trying to set a
- // num option using a string.
- semsg(_("E521: Number required: &%s = '%s'"),
- name, string);
- return NULL; // do nothing as we hit an error
- }
- }
- long numval = number;
- if (opt_flags & OPT_CLEAR) {
- if ((int *)varp == &curbuf->b_p_ar) {
- numval = -1;
- } else if ((long *)varp == &curbuf->b_p_ul) {
- numval = NO_LOCAL_UNDOLEVEL;
- } else if ((long *)varp == &curwin->w_p_so || (long *)varp == &curwin->w_p_siso) {
- numval = -1;
- } else {
- char *s = NULL;
- (void)get_option_value(name, &numval, (char_u **)&s, OPT_GLOBAL);
- }
+ }
+
+ varp = get_varp_scope(&(options[opt_idx]), opt_flags);
+ if (varp != NULL) { // hidden option is not changed
+ if (number == 0 && string != NULL) {
+ int idx;
+
+ // Either we are given a string or we are setting option
+ // to zero.
+ for (idx = 0; string[idx] == '0'; idx++) {}
+ if (string[idx] != NUL || idx == 0) {
+ // There's another character after zeros or the string
+ // is empty. In both cases, we are trying to set a
+ // num option using a string.
+ semsg(_("E521: Number required: &%s = '%s'"),
+ name, string);
+ return NULL; // do nothing as we hit an error
}
- if (flags & P_NUM) {
- return set_num_option(opt_idx, varp, numval, NULL, 0, opt_flags);
+ }
+ long numval = number;
+ if (opt_flags & OPT_CLEAR) {
+ if ((int *)varp == &curbuf->b_p_ar) {
+ numval = -1;
+ } else if ((long *)varp == &curbuf->b_p_ul) {
+ numval = NO_LOCAL_UNDOLEVEL;
+ } else if ((long *)varp == &curwin->w_p_so || (long *)varp == &curwin->w_p_siso) {
+ numval = -1;
} else {
- return set_bool_option(opt_idx, varp, (int)numval, opt_flags);
+ char *s = NULL;
+ (void)get_option_value(name, &numval, &s, OPT_GLOBAL);
}
}
+ if (flags & P_NUM) {
+ return set_num_option(opt_idx, varp, numval, NULL, 0, opt_flags);
+ } else {
+ return set_bool_option(opt_idx, varp, (int)numval, opt_flags);
+ }
}
}
return NULL;
@@ -5153,7 +5317,8 @@ int find_key_option_len(const char_u *arg_arg, size_t len, bool has_lt)
} else if (has_lt) {
arg--; // put arg at the '<'
modifiers = 0;
- key = find_special_key(&arg, len + 1, &modifiers, true, true, false);
+ key = find_special_key(&arg, len + 1, &modifiers,
+ FSK_KEYCODE | FSK_KEEP_X_KEY | FSK_SIMPLIFY, NULL);
if (modifiers) { // can't handle modifiers here
key = 0;
}
@@ -5185,7 +5350,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) {
@@ -5199,6 +5364,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;
@@ -5209,7 +5375,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);
}
@@ -5218,11 +5384,13 @@ 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;
+ len = (int)STRLEN(p->fullname) + vim_strsize((char *)NameBuff) + 1;
}
if ((len <= INC - GAP && run == 1)
|| (len > INC - GAP && run == 2)) {
@@ -5239,7 +5407,7 @@ static void showoptions(int all, int opt_flags)
&& Columns + GAP >= INT_MIN + 3
&& (Columns + GAP - 3) / INC >= INT_MIN
&& (Columns + GAP - 3) / INC <= INT_MAX);
- cols = (int)((Columns + GAP - 3) / INC);
+ cols = (Columns + GAP - 3) / INC;
if (cols == 0) {
cols = 1;
}
@@ -5335,7 +5503,7 @@ static void showoneopt(vimoption_T *p, int opt_flags)
msg_putchar('=');
// put value string in NameBuff
option_value2string(p, opt_flags);
- msg_outtrans(NameBuff);
+ msg_outtrans((char *)NameBuff);
}
silent_mode = save_silent;
@@ -5528,13 +5696,13 @@ static int put_setstring(FILE *fd, char *cmd, char *name, char_u **valuep, uint6
// replace home directory in the whole option value into "buf"
buf = xmalloc(size);
- home_replace(NULL, *valuep, buf, size, false);
+ home_replace(NULL, (char *)(*valuep), (char *)buf, size, false);
// If the option value is longer than MAXPATHL, we need to append
// each comma separated part of the option separately, so that it
// can be expanded when read back.
if (size >= MAXPATHL && (flags & P_COMMA) != 0
- && vim_strchr(*valuep, ',') != NULL) {
+ && vim_strchr((char *)(*valuep), ',') != NULL) {
part = xmalloc(size);
// write line break to clear the option, e.g. ':set rtp='
@@ -5548,7 +5716,7 @@ static int put_setstring(FILE *fd, char *cmd, char *name, char_u **valuep, uint6
if (fprintf(fd, "%s %s+=", cmd, name) < 0) {
goto fail;
}
- (void)copy_option_part(&p, part, size, ",");
+ (void)copy_option_part((char **)&p, (char *)part, size, ",");
if (put_escstr(fd, part, 2) == FAIL || put_eol(fd) == FAIL) {
goto fail;
}
@@ -5618,7 +5786,7 @@ static int put_setbool(FILE *fd, char *cmd, char *name, int value)
void comp_col(void)
{
- int last_has_status = (p_ls == 2 || (p_ls == 1 && !ONE_WINDOW));
+ int last_has_status = (p_ls > 1 || (p_ls == 1 && !ONE_WINDOW));
sc_col = 0;
ru_col = 0;
@@ -5636,13 +5804,11 @@ void comp_col(void)
}
}
assert(sc_col >= 0
- && INT_MIN + sc_col <= Columns
- && Columns - sc_col <= INT_MAX);
- sc_col = (int)(Columns - sc_col);
+ && INT_MIN + sc_col <= Columns);
+ sc_col = Columns - sc_col;
assert(ru_col >= 0
- && INT_MIN + ru_col <= Columns
- && Columns - ru_col <= INT_MAX);
- ru_col = (int)(Columns - ru_col);
+ && INT_MIN + ru_col <= Columns);
+ ru_col = Columns - ru_col;
if (sc_col <= 0) { // screen too narrow, will become a mess
sc_col = 1;
}
@@ -5730,6 +5896,9 @@ void unset_global_local_option(char *name, void *from)
case PV_STL:
clear_string_option(&((win_T *)from)->w_p_stl);
break;
+ case PV_WBR:
+ clear_string_option((char_u **)&((win_T *)from)->w_p_wbr);
+ break;
case PV_UL:
buf->b_p_ul = NO_LOCAL_UNDOLEVEL;
break;
@@ -5749,6 +5918,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;
}
}
@@ -5803,6 +5976,8 @@ static char_u *get_varp_scope(vimoption_T *p, int opt_flags)
return (char_u *)&(curwin->w_p_sbr);
case PV_STL:
return (char_u *)&(curwin->w_p_stl);
+ case PV_WBR:
+ return (char_u *)&(curwin->w_p_wbr);
case PV_UL:
return (char_u *)&(curbuf->b_p_ul);
case PV_LW:
@@ -5815,6 +5990,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"
}
@@ -5894,6 +6071,9 @@ static char_u *get_varp(vimoption_T *p)
case PV_STL:
return *curwin->w_p_stl != NUL
? (char_u *)&(curwin->w_p_stl) : p->var;
+ case PV_WBR:
+ return *curwin->w_p_wbr != NUL
+ ? (char_u *)&(curwin->w_p_wbr) : p->var;
case PV_UL:
return curbuf->b_p_ul != NO_LOCAL_UNDOLEVEL
? (char_u *)&(curbuf->b_p_ul) : p->var;
@@ -5909,6 +6089,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);
@@ -6002,6 +6185,8 @@ static char_u *get_varp(vimoption_T *p)
return (char_u *)&(curbuf->b_p_cink);
case PV_CINO:
return (char_u *)&(curbuf->b_p_cino);
+ case PV_CINSD:
+ return (char_u *)&(curbuf->b_p_cinsd);
case PV_CINW:
return (char_u *)&(curbuf->b_p_cinw);
case PV_COM:
@@ -6138,6 +6323,7 @@ void win_copy_options(win_T *wp_from, win_T *wp_to)
{
copy_winopt(&wp_from->w_onebuf_opt, &wp_to->w_onebuf_opt);
copy_winopt(&wp_from->w_allbuf_opt, &wp_to->w_allbuf_opt);
+ didset_window_options(wp_to);
}
/// Copy the options from one winopt_T to another.
@@ -6150,11 +6336,14 @@ 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);
to->wo_sbr = vim_strsave(from->wo_sbr);
to->wo_stl = vim_strsave(from->wo_stl);
+ to->wo_wbr = xstrdup(from->wo_wbr);
to->wo_wrap = from->wo_wrap;
to->wo_wrap_save = from->wo_wrap_save;
to->wo_lbr = from->wo_lbr;
@@ -6194,6 +6383,9 @@ void copy_winopt(winopt_T *from, winopt_T *to)
to->wo_fcs = vim_strsave(from->wo_fcs);
to->wo_lcs = vim_strsave(from->wo_lcs);
to->wo_winbl = from->wo_winbl;
+
+ // Copy the script context so that we know were the value was last set.
+ memmove(to->wo_script_ctx, from->wo_script_ctx, sizeof(to->wo_script_ctx));
check_winopt(to); // don't want NULL pointers
}
@@ -6226,6 +6418,8 @@ 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);
+ check_string_option((char_u **)&wop->wo_wbr);
}
/// Free the allocated memory inside a winopt_T.
@@ -6250,6 +6444,8 @@ 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);
+ clear_string_option((char_u **)&wop->wo_wbr);
}
void didset_window_options(win_T *wp)
@@ -6264,11 +6460,30 @@ void didset_window_options(win_T *wp)
wp->w_grid_alloc.blending = wp->w_p_winbl > 0;
}
+/// Index into the options table for a buffer-local option enum.
+static int buf_opt_idx[BV_COUNT];
+#define COPY_OPT_SCTX(buf, bv) buf->b_p_script_ctx[bv] = options[buf_opt_idx[bv]].last_set
+
+/// Initialize buf_opt_idx[] if not done already.
+static void init_buf_opt_idx(void)
+{
+ static int did_init_buf_opt_idx = false;
+
+ if (did_init_buf_opt_idx) {
+ return;
+ }
+ did_init_buf_opt_idx = true;
+ for (int i = 0; options[i].fullname != NULL; i++) {
+ if (options[i].indir & PV_BUF) {
+ buf_opt_idx[options[i].indir & PV_MASK] = i;
+ }
+ }
+}
/// Copy global option values to local options for one buffer.
/// Used when creating a new buffer and sometimes when entering a buffer.
/// flags:
-/// BCO_ENTER We will enter the buf buffer.
+/// BCO_ENTER We will enter the buffer "buf".
/// BCO_ALWAYS Always copy the options, but only set b_p_initialized when
/// appropriate.
/// BCO_NOHELP Don't copy the values to a help buffer.
@@ -6304,11 +6519,12 @@ void buf_copy_options(buf_T *buf, int flags)
}
if (should_copy || (flags & BCO_ALWAYS)) {
- /* Don't copy the options specific to a help buffer when
- * BCO_NOHELP is given or the options were initialized already
- * (jumping back to a help file with CTRL-T or CTRL-O) */
- dont_do_help = ((flags & BCO_NOHELP) && buf->b_help)
- || buf->b_p_initialized;
+ memset(buf->b_p_script_ctx, 0, sizeof(buf->b_p_script_ctx));
+ init_buf_opt_idx();
+ // Don't copy the options specific to a help buffer when
+ // BCO_NOHELP is given or the options were initialized already
+ // (jumping back to a help file with CTRL-T or CTRL-O)
+ dont_do_help = ((flags & BCO_NOHELP) && buf->b_help) || buf->b_p_initialized;
if (dont_do_help) { // don't free b_p_isk
save_p_isk = buf->b_p_isk;
buf->b_p_isk = NULL;
@@ -6340,81 +6556,135 @@ void buf_copy_options(buf_T *buf, int flags)
}
buf->b_p_ai = p_ai;
+ COPY_OPT_SCTX(buf, BV_AI);
buf->b_p_ai_nopaste = p_ai_nopaste;
buf->b_p_sw = p_sw;
+ COPY_OPT_SCTX(buf, BV_SW);
buf->b_p_scbk = p_scbk;
+ COPY_OPT_SCTX(buf, BV_SCBK);
buf->b_p_tw = p_tw;
+ COPY_OPT_SCTX(buf, BV_TW);
buf->b_p_tw_nopaste = p_tw_nopaste;
buf->b_p_tw_nobin = p_tw_nobin;
buf->b_p_wm = p_wm;
+ COPY_OPT_SCTX(buf, BV_WM);
buf->b_p_wm_nopaste = p_wm_nopaste;
buf->b_p_wm_nobin = p_wm_nobin;
buf->b_p_bin = p_bin;
+ COPY_OPT_SCTX(buf, BV_BIN);
buf->b_p_bomb = p_bomb;
+ COPY_OPT_SCTX(buf, BV_BOMB);
buf->b_p_et = p_et;
+ COPY_OPT_SCTX(buf, BV_ET);
buf->b_p_fixeol = p_fixeol;
+ COPY_OPT_SCTX(buf, BV_FIXEOL);
buf->b_p_et_nobin = p_et_nobin;
buf->b_p_et_nopaste = p_et_nopaste;
buf->b_p_ml = p_ml;
+ COPY_OPT_SCTX(buf, BV_ML);
buf->b_p_ml_nobin = p_ml_nobin;
buf->b_p_inf = p_inf;
- buf->b_p_swf = cmdmod.noswapfile ? false : p_swf;
+ COPY_OPT_SCTX(buf, BV_INF);
+ if (cmdmod.cmod_flags & CMOD_NOSWAPFILE) {
+ buf->b_p_swf = false;
+ } else {
+ buf->b_p_swf = p_swf;
+ COPY_OPT_SCTX(buf, BV_SWF);
+ }
buf->b_p_cpt = vim_strsave(p_cpt);
+ COPY_OPT_SCTX(buf, BV_CPT);
#ifdef BACKSLASH_IN_FILENAME
buf->b_p_csl = vim_strsave(p_csl);
+ COPY_OPT_SCTX(buf, BV_CSL);
#endif
buf->b_p_cfu = vim_strsave(p_cfu);
+ COPY_OPT_SCTX(buf, BV_CFU);
buf->b_p_ofu = vim_strsave(p_ofu);
+ COPY_OPT_SCTX(buf, BV_OFU);
buf->b_p_urf = vim_strsave(p_urf);
+ COPY_OPT_SCTX(buf, BV_URF);
buf->b_p_tfu = vim_strsave(p_tfu);
+ COPY_OPT_SCTX(buf, BV_TFU);
buf->b_p_sts = p_sts;
+ COPY_OPT_SCTX(buf, BV_STS);
buf->b_p_sts_nopaste = p_sts_nopaste;
buf->b_p_vsts = vim_strsave(p_vsts);
+ COPY_OPT_SCTX(buf, BV_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;
+ buf->b_p_vsts_array = NULL;
}
buf->b_p_vsts_nopaste = p_vsts_nopaste
? vim_strsave(p_vsts_nopaste)
: NULL;
buf->b_p_com = vim_strsave(p_com);
+ COPY_OPT_SCTX(buf, BV_COM);
buf->b_p_cms = vim_strsave(p_cms);
+ COPY_OPT_SCTX(buf, BV_CMS);
buf->b_p_fo = vim_strsave(p_fo);
+ COPY_OPT_SCTX(buf, BV_FO);
buf->b_p_flp = vim_strsave(p_flp);
+ COPY_OPT_SCTX(buf, BV_FLP);
buf->b_p_nf = vim_strsave(p_nf);
+ COPY_OPT_SCTX(buf, BV_NF);
buf->b_p_mps = vim_strsave(p_mps);
+ COPY_OPT_SCTX(buf, BV_MPS);
buf->b_p_si = p_si;
+ COPY_OPT_SCTX(buf, BV_SI);
buf->b_p_channel = 0;
buf->b_p_ci = p_ci;
+
+ COPY_OPT_SCTX(buf, BV_CI);
buf->b_p_cin = p_cin;
+ COPY_OPT_SCTX(buf, BV_CIN);
buf->b_p_cink = vim_strsave(p_cink);
+ COPY_OPT_SCTX(buf, BV_CINK);
buf->b_p_cino = vim_strsave(p_cino);
+ COPY_OPT_SCTX(buf, BV_CINO);
+ buf->b_p_cinsd = vim_strsave(p_cinsd);
+ COPY_OPT_SCTX(buf, BV_CINSD);
+
// Don't copy 'filetype', it must be detected
buf->b_p_ft = empty_option;
buf->b_p_pi = p_pi;
+ COPY_OPT_SCTX(buf, BV_PI);
buf->b_p_cinw = vim_strsave(p_cinw);
+ COPY_OPT_SCTX(buf, BV_CINW);
buf->b_p_lisp = p_lisp;
+ COPY_OPT_SCTX(buf, BV_LISP);
// Don't copy 'syntax', it must be set
buf->b_p_syn = empty_option;
buf->b_p_smc = p_smc;
+ COPY_OPT_SCTX(buf, BV_SMC);
buf->b_s.b_syn_isk = empty_option;
buf->b_s.b_p_spc = vim_strsave(p_spc);
+ COPY_OPT_SCTX(buf, BV_SPC);
(void)compile_cap_prog(&buf->b_s);
buf->b_s.b_p_spf = vim_strsave(p_spf);
+ COPY_OPT_SCTX(buf, BV_SPF);
buf->b_s.b_p_spl = vim_strsave(p_spl);
+ COPY_OPT_SCTX(buf, BV_SPL);
buf->b_s.b_p_spo = vim_strsave(p_spo);
+ COPY_OPT_SCTX(buf, BV_SPO);
buf->b_p_inde = vim_strsave(p_inde);
+ COPY_OPT_SCTX(buf, BV_INDE);
buf->b_p_indk = vim_strsave(p_indk);
+ COPY_OPT_SCTX(buf, BV_INDK);
buf->b_p_fp = empty_option;
buf->b_p_fex = vim_strsave(p_fex);
+ COPY_OPT_SCTX(buf, BV_FEX);
buf->b_p_sua = vim_strsave(p_sua);
+ COPY_OPT_SCTX(buf, BV_SUA);
buf->b_p_keymap = vim_strsave(p_keymap);
+ COPY_OPT_SCTX(buf, BV_KMAP);
buf->b_kmap_state |= KEYMAP_INIT;
// This isn't really an option, but copying the langmap and IME
// state from the current buffer is better than resetting it.
buf->b_p_iminsert = p_iminsert;
+ COPY_OPT_SCTX(buf, BV_IMI);
buf->b_p_imsearch = p_imsearch;
+ COPY_OPT_SCTX(buf, BV_IMS);
// options that are normally global but also have a local value
// are not copied, start using the global value
@@ -6434,11 +6704,14 @@ void buf_copy_options(buf_T *buf, int flags)
buf->b_p_def = empty_option;
buf->b_p_inc = empty_option;
buf->b_p_inex = vim_strsave(p_inex);
+ COPY_OPT_SCTX(buf, BV_INEX);
buf->b_p_dict = empty_option;
buf->b_p_tsr = empty_option;
buf->b_p_tsrfu = empty_option;
buf->b_p_qe = vim_strsave(p_qe);
+ COPY_OPT_SCTX(buf, BV_QE);
buf->b_p_udf = p_udf;
+ COPY_OPT_SCTX(buf, BV_UDF);
buf->b_p_lw = empty_option;
buf->b_p_menc = empty_option;
@@ -6451,17 +6724,20 @@ 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;
}
} else {
buf->b_p_isk = vim_strsave(p_isk);
+ COPY_OPT_SCTX(buf, BV_ISK);
did_isk = true;
buf->b_p_ts = p_ts;
+ COPY_OPT_SCTX(buf, BV_TS);
buf->b_p_vts = vim_strsave(p_vts);
+ COPY_OPT_SCTX(buf, BV_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;
}
@@ -6470,6 +6746,7 @@ void buf_copy_options(buf_T *buf, int flags)
clear_string_option(&buf->b_p_bt);
}
buf->b_p_ma = p_ma;
+ COPY_OPT_SCTX(buf, BV_MA);
}
}
@@ -6532,12 +6809,12 @@ void set_context_in_set_cmd(expand_T *xp, char_u *arg, int opt_flags)
xp->xp_context = EXPAND_SETTINGS;
if (*arg == NUL) {
- xp->xp_pattern = arg;
+ xp->xp_pattern = (char *)arg;
return;
}
p = arg + STRLEN(arg) - 1;
if (*p == ' ' && *(p - 1) != '\\') {
- xp->xp_pattern = p + 1;
+ xp->xp_pattern = (char *)p + 1;
return;
}
while (p > arg) {
@@ -6563,7 +6840,8 @@ void set_context_in_set_cmd(expand_T *xp, char_u *arg, int opt_flags)
xp->xp_context = EXPAND_BOOL_SETTINGS;
p += 3;
}
- xp->xp_pattern = arg = p;
+ xp->xp_pattern = (char *)p;
+ arg = p;
if (*arg == '<') {
while (*p != '>') {
if (*p++ == NUL) { // expand terminal option name
@@ -6630,7 +6908,7 @@ void set_context_in_set_cmd(expand_T *xp, char_u *arg, int opt_flags)
} else {
expand_option_idx = opt_idx;
}
- xp->xp_pattern = p + 1;
+ xp->xp_pattern = (char *)p + 1;
return;
}
xp->xp_context = EXPAND_NOTHING;
@@ -6638,7 +6916,7 @@ void set_context_in_set_cmd(expand_T *xp, char_u *arg, int opt_flags)
return;
}
- xp->xp_pattern = p + 1;
+ xp->xp_pattern = (char *)p + 1;
if (flags & P_EXPAND) {
p = options[opt_idx].var;
@@ -6671,16 +6949,16 @@ void set_context_in_set_cmd(expand_T *xp, char_u *arg, int opt_flags)
// For an option that is a list of file names, find the start of the
// last file name.
- for (p = arg + STRLEN(arg) - 1; p > xp->xp_pattern; p--) {
+ for (p = arg + STRLEN(arg) - 1; p > (char_u *)xp->xp_pattern; p--) {
// count number of backslashes before ' ' or ','
if (*p == ' ' || *p == ',') {
s = p;
- while (s > xp->xp_pattern && *(s - 1) == '\\') {
+ while (s > (char_u *)xp->xp_pattern && *(s - 1) == '\\') {
s--;
}
if ((*p == ' ' && (xp->xp_backslash == XP_BS_THREE && (p - s) < 3))
|| (*p == ',' && (flags & P_COMMA) && ((p - s) & 1) == 0)) {
- xp->xp_pattern = p + 1;
+ xp->xp_pattern = (char *)p + 1;
break;
}
}
@@ -6688,7 +6966,7 @@ void set_context_in_set_cmd(expand_T *xp, char_u *arg, int opt_flags)
// for 'spellsuggest' start at "file:"
if (options[opt_idx].var == (char_u *)&p_sps
&& STRNCMP(p, "file:", 5) == 0) {
- xp->xp_pattern = p + 5;
+ xp->xp_pattern = (char *)p + 5;
break;
}
}
@@ -6712,7 +6990,7 @@ int ExpandSettings(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u ***
if (xp->xp_context != EXPAND_BOOL_SETTINGS) {
for (match = 0; match < (int)ARRAY_SIZE(names);
match++) {
- if (vim_regexec(regmatch, (char_u *)names[match], (colnr_T)0)) {
+ if (vim_regexec(regmatch, names[match], (colnr_T)0)) {
if (loop == 0) {
num_normal++;
} else {
@@ -6731,10 +7009,10 @@ int ExpandSettings(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u ***
continue;
}
match = false;
- if (vim_regexec(regmatch, str, (colnr_T)0)
+ if (vim_regexec(regmatch, (char *)str, (colnr_T)0)
|| (options[opt_idx].shortname != NULL
&& vim_regexec(regmatch,
- (char_u *)options[opt_idx].shortname,
+ options[opt_idx].shortname,
(colnr_T)0))) {
match = true;
}
@@ -6832,7 +7110,7 @@ static void option_value2string(vimoption_T *opp, int opt_flags)
if (varp == NULL) { // Just in case.
NameBuff[0] = NUL;
} else if (opp->flags & P_EXPAND) {
- home_replace(NULL, varp, NameBuff, MAXPATHL, false);
+ home_replace(NULL, (char *)varp, (char *)NameBuff, MAXPATHL, false);
// Translate 'pastetoggle' into special key names.
} else if ((char_u **)opp->var == &p_pt) {
str2specialbuf((const char *)p_pt, (char *)NameBuff, MAXPATHL);
@@ -6856,175 +7134,6 @@ static int wc_use_keyname(char_u *varp, long *wcp)
return false;
}
-/// Any character has an equivalent 'langmap' character. This is used for
-/// keyboards that have a special language mode that sends characters above
-/// 128 (although other characters can be translated too). The "to" field is a
-/// Vim command character. This avoids having to switch the keyboard back to
-/// ASCII mode when leaving Insert mode.
-///
-/// langmap_mapchar[] maps any of 256 chars to an ASCII char used for Vim
-/// commands.
-/// langmap_mapga.ga_data is a sorted table of langmap_entry_T.
-/// This does the same as langmap_mapchar[] for characters >= 256.
-///
-/// With multi-byte support use growarray for 'langmap' chars >= 256
-typedef struct {
- int from;
- int to;
-} langmap_entry_T;
-
-static garray_T langmap_mapga = GA_EMPTY_INIT_VALUE;
-
-/// Search for an entry in "langmap_mapga" for "from". If found set the "to"
-/// field. If not found insert a new entry at the appropriate location.
-static void langmap_set_entry(int from, int to)
-{
- langmap_entry_T *entries = (langmap_entry_T *)(langmap_mapga.ga_data);
- unsigned int a = 0;
- assert(langmap_mapga.ga_len >= 0);
- unsigned int b = (unsigned int)langmap_mapga.ga_len;
-
- // Do a binary search for an existing entry.
- while (a != b) {
- unsigned int i = (a + b) / 2;
- int d = entries[i].from - from;
-
- if (d == 0) {
- entries[i].to = to;
- return;
- }
- if (d < 0) {
- a = i + 1;
- } else {
- b = i;
- }
- }
-
- ga_grow(&langmap_mapga, 1);
-
- // insert new entry at position "a"
- entries = (langmap_entry_T *)(langmap_mapga.ga_data) + a;
- memmove(entries + 1, entries,
- ((unsigned int)langmap_mapga.ga_len - a) * sizeof(langmap_entry_T));
- langmap_mapga.ga_len++;
- entries[0].from = from;
- entries[0].to = to;
-}
-
-/// Apply 'langmap' to multi-byte character "c" and return the result.
-int langmap_adjust_mb(int c)
-{
- langmap_entry_T *entries = (langmap_entry_T *)(langmap_mapga.ga_data);
- int a = 0;
- int b = langmap_mapga.ga_len;
-
- while (a != b) {
- int i = (a + b) / 2;
- int d = entries[i].from - c;
-
- if (d == 0) {
- return entries[i].to; // found matching entry
- }
- if (d < 0) {
- a = i + 1;
- } else {
- b = i;
- }
- }
- return c; // no entry found, return "c" unmodified
-}
-
-static void langmap_init(void)
-{
- for (int i = 0; i < 256; i++) {
- langmap_mapchar[i] = (char_u)i; // we init with a one-to-one map
- }
- ga_init(&langmap_mapga, sizeof(langmap_entry_T), 8);
-}
-
-/// Called when langmap option is set; the language map can be
-/// changed at any time!
-static void langmap_set(void)
-{
- char_u *p;
- char_u *p2;
- int from, to;
-
- ga_clear(&langmap_mapga); // clear the previous map first
- langmap_init(); // back to one-to-one map
-
- for (p = p_langmap; p[0] != NUL;) {
- for (p2 = p; p2[0] != NUL && p2[0] != ',' && p2[0] != ';';
- MB_PTR_ADV(p2)) {
- if (p2[0] == '\\' && p2[1] != NUL) {
- p2++;
- }
- }
- if (p2[0] == ';') {
- p2++; // abcd;ABCD form, p2 points to A
- } else {
- p2 = NULL; // aAbBcCdD form, p2 is NULL
- }
- while (p[0]) {
- if (p[0] == ',') {
- p++;
- break;
- }
- if (p[0] == '\\' && p[1] != NUL) {
- p++;
- }
- from = utf_ptr2char(p);
- to = NUL;
- if (p2 == NULL) {
- MB_PTR_ADV(p);
- if (p[0] != ',') {
- if (p[0] == '\\') {
- p++;
- }
- to = utf_ptr2char(p);
- }
- } else {
- if (p2[0] != ',') {
- if (p2[0] == '\\') {
- p2++;
- }
- to = utf_ptr2char(p2);
- }
- }
- if (to == NUL) {
- semsg(_("E357: 'langmap': Matching character missing for %s"),
- transchar(from));
- return;
- }
-
- if (from >= 256) {
- langmap_set_entry(from, to);
- } else {
- assert(to <= UCHAR_MAX);
- langmap_mapchar[from & 255] = (char_u)to;
- }
-
- // Advance to next pair
- MB_PTR_ADV(p);
- if (p2 != NULL) {
- MB_PTR_ADV(p2);
- if (*p == ';') {
- p = p2;
- if (p[0] != NUL) {
- if (p[0] != ',') {
- semsg(_("E358: 'langmap': Extra characters after semicolon: %s"),
- p);
- return;
- }
- p++;
- }
- break;
- }
- }
- }
- }
-}
-
/// Return true if format option 'x' is in effect.
/// Take care of no formatting when 'paste' is set.
bool has_format_option(int x)
@@ -7033,7 +7142,7 @@ bool has_format_option(int x)
if (p_paste) {
return false;
}
- return vim_strchr(curbuf->b_p_fo, x) != NULL;
+ return vim_strchr((char *)curbuf->b_p_fo, x) != NULL;
}
/// @returns true if "x" is present in 'shortmess' option, or
@@ -7041,9 +7150,9 @@ bool has_format_option(int x)
bool shortmess(int x)
{
return (p_shm != NULL
- && (vim_strchr(p_shm, x) != NULL
- || (vim_strchr(p_shm, 'a') != NULL
- && vim_strchr((char_u *)SHM_ALL_ABBREVIATIONS, x) != NULL)));
+ && (vim_strchr((char *)p_shm, x) != NULL
+ || (vim_strchr((char *)p_shm, 'a') != NULL
+ && vim_strchr((char *)SHM_ALL_ABBREVIATIONS, x) != NULL)));
}
/// paste_option_changed() - Called after p_paste was set or reset.
@@ -7110,10 +7219,7 @@ static void paste_option_changed(void)
free_string_option(buf->b_p_vsts);
}
buf->b_p_vsts = empty_option;
- if (buf->b_p_vsts_array) {
- xfree(buf->b_p_vsts_array);
- }
- buf->b_p_vsts_array = 0;
+ XFREE_CLEAR(buf->b_p_vsts_array);
}
// set global options
@@ -7150,13 +7256,11 @@ static void paste_option_changed(void)
buf->b_p_vsts = buf->b_p_vsts_nopaste
? vim_strsave(buf->b_p_vsts_nopaste)
: empty_option;
- if (buf->b_p_vsts_array) {
- xfree(buf->b_p_vsts_array);
- }
+ 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;
+ buf->b_p_vsts_array = NULL;
}
}
@@ -7410,7 +7514,7 @@ bool can_bs(int what)
case '0':
return false;
}
- return vim_strchr(p_bs, what) != NULL;
+ return vim_strchr((char *)p_bs, what) != NULL;
}
/// Save the current values of 'fileformat' and 'fileencoding', so that we know
@@ -7425,7 +7529,7 @@ void save_file_ff(buf_T *buf)
if (buf->b_start_fenc == NULL
|| STRCMP(buf->b_start_fenc, buf->b_p_fenc) != 0) {
xfree(buf->b_start_fenc);
- buf->b_start_fenc = vim_strsave(buf->b_p_fenc);
+ buf->b_start_fenc = (char *)vim_strsave(buf->b_p_fenc);
}
}
@@ -7472,6 +7576,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;
@@ -7491,7 +7596,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;
}
@@ -7504,7 +7609,7 @@ bool tabstop_set(char_u *var, long **array)
valcount++;
continue;
}
- emsg(_(e_invarg));
+ semsg(_(e_invarg2), var);
return false;
}
@@ -7513,7 +7618,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 > TABSTOP_MAX) {
+ semsg(_(e_invarg2), cp);
+ XFREE_CLEAR(*array);
+ return false;
+ }
+ (*array)[t++] = n;
while (*cp != NUL && *cp != ',') {
cp++;
}
@@ -7783,10 +7896,10 @@ static bool briopt_check(win_T *wp)
if (STRNCMP(p, "shift:", 6) == 0
&& ((p[6] == '-' && ascii_isdigit(p[7])) || ascii_isdigit(p[6]))) {
p += 6;
- bri_shift = getdigits_int(&p, true, 0);
+ bri_shift = getdigits_int((char **)&p, true, 0);
} else if (STRNCMP(p, "min:", 4) == 0 && ascii_isdigit(p[4])) {
p += 4;
- bri_min = getdigits_int(&p, true, 0);
+ bri_min = getdigits_int((char **)&p, true, 0);
} else if (STRNCMP(p, "sbr", 3) == 0) {
p += 3;
bri_sbr = true;
@@ -7818,6 +7931,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
@@ -7912,11 +8031,7 @@ void set_fileformat(int eol_style, int opt_flags)
// p is NULL if "eol_style" is EOL_UNKNOWN.
if (p != NULL) {
- set_string_option_direct("ff",
- -1,
- (char_u *)p,
- OPT_FREE | opt_flags,
- 0);
+ set_string_option_direct("ff", -1, p, OPT_FREE | opt_flags, 0);
}
// This may cause the buffer to become (un)modified.
@@ -7945,18 +8060,18 @@ char_u *skip_to_option_part(const char_u *p)
/// @param[in] sep_chars chars that separate the option parts
///
/// @return length of `*option`
-size_t copy_option_part(char_u **option, char_u *buf, size_t maxlen, char *sep_chars)
+size_t copy_option_part(char **option, char *buf, size_t maxlen, char *sep_chars)
{
size_t len = 0;
- char_u *p = *option;
+ char *p = *option;
// skip '.' at start of option part, for 'suffixes'
if (*p == '.') {
buf[len++] = *p++;
}
- while (*p != NUL && vim_strchr((char_u *)sep_chars, *p) == NULL) {
+ while (*p != NUL && vim_strchr(sep_chars, *p) == NULL) {
// Skip backslash before a separator character and space.
- if (p[0] == '\\' && vim_strchr((char_u *)sep_chars, p[1]) != NULL) {
+ if (p[0] == '\\' && vim_strchr(sep_chars, p[1]) != NULL) {
p++;
}
if (len < maxlen - 1) {
@@ -7969,7 +8084,7 @@ size_t copy_option_part(char_u **option, char_u *buf, size_t maxlen, char *sep_c
if (*p != NUL && *p != ',') { // skip non-standard separator
p++;
}
- p = skip_to_option_part(p); // p points to next file name
+ p = (char *)skip_to_option_part((char_u *)p); // p points to next file name
*option = p;
return len;
@@ -7978,13 +8093,13 @@ size_t copy_option_part(char_u **option, char_u *buf, size_t maxlen, char *sep_c
/// Return true when 'shell' has "csh" in the tail.
int csh_like_shell(void)
{
- return strstr((char *)path_tail(p_sh), "csh") != NULL;
+ return strstr(path_tail((char *)p_sh), "csh") != NULL;
}
/// Return true when 'shell' has "fish" in the tail.
bool fish_like_shell(void)
{
- return strstr((char *)path_tail(p_sh), "fish") != NULL;
+ return strstr(path_tail((char *)p_sh), "fish") != NULL;
}
/// Return the number of requested sign columns, based on current
@@ -7997,7 +8112,6 @@ int win_signcol_count(win_T *wp)
/// Return the number of requested sign columns, based on user / configuration.
int win_signcol_configured(win_T *wp, int *is_fixed)
{
- int minimum = 0, maximum = 1, needed_signcols;
const char *scl = (const char *)wp->w_p_scl;
if (is_fixed) {
@@ -8010,7 +8124,6 @@ int win_signcol_configured(win_T *wp, int *is_fixed)
&& (wp->w_p_nu || wp->w_p_rnu)))) {
return 0;
}
- needed_signcols = buf_signcols(wp->w_buffer);
// yes or yes
if (!strncmp(scl, "yes:", 4)) {
@@ -8026,6 +8139,8 @@ int win_signcol_configured(win_T *wp, int *is_fixed)
*is_fixed = 0;
}
+ int minimum = 0, maximum = 1;
+
if (!strncmp(scl, "auto:", 5)) {
// Variable depending on a configuration
maximum = scl[5] - '0';
@@ -8036,6 +8151,7 @@ int win_signcol_configured(win_T *wp, int *is_fixed)
}
}
+ int needed_signcols = buf_signcols(wp->w_buffer, maximum);
int ret = MAX(minimum, MIN(maximum, needed_signcols));
assert(ret <= SIGN_SHOW_MAX);
return ret;
@@ -8076,7 +8192,7 @@ dict_T *get_winbuf_options(const int bufopt)
long get_scrolloff_value(win_T *wp)
{
// Disallow scrolloff in terminal-mode. #11915
- if (State & TERM_FOCUS) {
+ if (State & MODE_TERMINAL) {
return 0;
}
return wp->w_p_so < 0 ? p_so : wp->w_p_so;
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 9b7715cbe4..f3f7cf219e 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -49,7 +49,6 @@
# define DFLT_FFS_VI ""
#endif
-
// Possible values for 'encoding'
#define ENC_UCSBOM "ucs-bom" // check for BOM at start of file
@@ -57,7 +56,7 @@
#define ENC_DFLT "utf-8"
// end-of-line style
-#define EOL_UNKNOWN -1 // not defined yet
+#define EOL_UNKNOWN (-1) // not defined yet
#define EOL_UNIX 0 // NL
#define EOL_DOS 1 // CR NL
#define EOL_MAC 2 // CR
@@ -67,6 +66,7 @@
#define FO_WRAP_COMS 'c'
#define FO_RET_COMS 'r'
#define FO_OPEN_COMS 'o'
+#define FO_NO_OPEN_COMS '/'
#define FO_Q_COMS 'q'
#define FO_Q_NUMBER 'n'
#define FO_Q_SECOND '2'
@@ -85,7 +85,7 @@
#define DFLT_FO_VI "vt"
#define DFLT_FO_VIM "tcqj"
-#define FO_ALL "tcroq2vlb1mMBn,aw]jp" // for do_set()
+#define FO_ALL "tcro/q2vlb1mMBn,aw]jp" // for do_set()
// characters for the p_cpo option:
#define CPO_ALTREAD 'a' // ":read" sets alternate file name
@@ -118,7 +118,7 @@
#define CPO_REMMARK 'R' // remove marks when filtering
#define CPO_BUFOPT 's'
#define CPO_BUFOPTGLOB 'S'
-#define CPO_TAGPAT 't'
+#define CPO_TAGPAT 't' // tag pattern is used for "n"
#define CPO_UNDO 'u' // "u" undoes itself
#define CPO_BACKSPACE 'v' // "v" keep deleted text
#define CPO_FWRITE 'W' // "w!" doesn't overwrite readonly files
@@ -153,6 +153,11 @@
#define MOUSE_NONE ' ' // don't use Visual selection
#define MOUSE_NONEF 'x' // forced modeless selection
+// default vertical and horizontal mouse scroll values.
+// Note: This should be in sync with the default mousescroll option.
+#define MOUSESCROLL_VERT_DFLT 3
+#define MOUSESCROLL_HOR_DFLT 6
+
#define COCU_ALL "nvic" // flags for 'concealcursor'
/// characters for p_shm option:
@@ -265,7 +270,7 @@ enum {
STL_CLICK_FUNC = '@', ///< Click region start.
};
/// C string containing all 'statusline' option flags
-#define STL_ALL ((char_u[]) { \
+#define STL_ALL ((char[]) { \
STL_FILEPATH, STL_FULLPATH, STL_FILENAME, STL_COLUMN, STL_VIRTCOL, \
STL_VIRTCOL_ALT, STL_LINE, STL_NUMLINES, STL_BUFNO, STL_KEYMAP, STL_OFFSET, \
STL_OFFSET_X, STL_BYTEVAL, STL_BYTEVAL_X, STL_ROFLAG, STL_ROFLAG_ALT, \
@@ -333,9 +338,9 @@ EXTERN unsigned bo_flags;
#ifdef IN_OPTION_C
static char *(p_bo_values[]) = { "all", "backspace", "cursor", "complete",
"copy", "ctrlg", "error", "esc", "ex",
- "hangul", "insertmode", "lang", "mess",
- "showmatch", "operator", "register", "shell",
- "spell", "wildmode", NULL };
+ "hangul", "lang", "mess", "showmatch",
+ "operator", "register", "shell", "spell",
+ "wildmode", NULL };
#endif
// values for the 'belloff' option
@@ -391,7 +396,7 @@ EXTERN char_u *p_csl; // 'completeslash'
EXTERN long p_pb; // 'pumblend'
EXTERN long p_ph; // 'pumheight'
EXTERN long p_pw; // 'pumwidth'
-EXTERN char_u *p_cpo; // 'cpoptions'
+EXTERN char *p_cpo; // 'cpoptions'
EXTERN char_u *p_csprg; // 'cscopeprg'
EXTERN int p_csre; // 'cscoperelative'
EXTERN char_u *p_csqf; // 'cscopequickfix'
@@ -427,13 +432,13 @@ EXTERN int p_ea; // 'equalalways'
EXTERN char_u *p_ep; // 'equalprg'
EXTERN int p_eb; // 'errorbells'
EXTERN char_u *p_ef; // 'errorfile'
-EXTERN char_u *p_efm; // 'errorformat'
-EXTERN char_u *p_gefm; // 'grepformat'
+EXTERN char *p_efm; // 'errorformat'
+EXTERN char *p_gefm; // 'grepformat'
EXTERN char_u *p_gp; // 'grepprg'
EXTERN char_u *p_ei; // 'eventignore'
EXTERN int p_exrc; // 'exrc'
EXTERN char_u *p_fencs; // 'fileencodings'
-EXTERN char_u *p_ffs; // 'fileformats'
+EXTERN char *p_ffs; // 'fileformats'
EXTERN int p_fic; // 'fileignorecase'
EXTERN char_u *p_fcl; // 'foldclose'
EXTERN long p_fdls; // 'foldlevelstart'
@@ -484,7 +489,6 @@ EXTERN char_u *p_iconstring; // 'iconstring'
EXTERN int p_ic; // 'ignorecase'
EXTERN int p_is; // 'incsearch'
EXTERN char_u *p_icm; // 'inccommand'
-EXTERN int p_im; // 'insertmode'
EXTERN char_u *p_isf; // 'isfname'
EXTERN char_u *p_isi; // 'isident'
EXTERN char_u *p_isp; // 'isprint'
@@ -492,9 +496,10 @@ EXTERN int p_js; // 'joinspaces'
EXTERN char_u *p_jop; // 'jumpooptions'
EXTERN unsigned jop_flags;
#ifdef IN_OPTION_C
-static char *(p_jop_values[]) = { "stack", NULL };
+static char *(p_jop_values[]) = { "stack", "view", NULL };
#endif
#define JOP_STACK 0x01
+#define JOP_VIEW 0x02
EXTERN char_u *p_kp; // 'keywordprg'
EXTERN char_u *p_km; // 'keymodel'
EXTERN char_u *p_langmap; // 'langmap'
@@ -512,7 +517,7 @@ EXTERN int p_lz; // 'lazyredraw'
EXTERN int p_lpl; // 'loadplugins'
EXTERN int p_magic; // 'magic'
EXTERN char_u *p_menc; // 'makeencoding'
-EXTERN char_u *p_mef; // 'makeef'
+EXTERN char *p_mef; // 'makeef'
EXTERN char_u *p_mp; // 'makeprg'
EXTERN char_u *p_cc; // 'colorcolumn'
EXTERN int p_cc_cols[256]; // array for 'colorcolumn' columns
@@ -523,11 +528,14 @@ EXTERN long p_mmd; // 'maxmapdepth'
EXTERN long p_mmp; // 'maxmempattern'
EXTERN long p_mis; // 'menuitems'
EXTERN char_u *p_msm; // 'mkspellmem'
-EXTERN long p_mle; // 'modelineexpr'
+EXTERN int p_mle; // 'modelineexpr'
EXTERN long p_mls; // 'modelines'
EXTERN char_u *p_mouse; // 'mouse'
EXTERN char_u *p_mousem; // 'mousemodel'
-EXTERN long p_mousef; // 'mousefocus'
+EXTERN int p_mousef; // 'mousefocus'
+EXTERN char_u *p_mousescroll; // 'mousescroll'
+EXTERN long p_mousescroll_vert INIT(= MOUSESCROLL_VERT_DFLT);
+EXTERN long p_mousescroll_hor INIT(= MOUSESCROLL_HOR_DFLT);
EXTERN long p_mouset; // 'mousetime'
EXTERN int p_more; // 'more'
EXTERN char_u *p_opfunc; // 'operatorfunc'
@@ -556,7 +564,6 @@ static char *(p_rdb_values[]) = {
#define RDB_NODELTA 0x008
EXTERN long p_rdt; // 'redrawtime'
-EXTERN int p_remap; // 'remap'
EXTERN long p_re; // 'regexpengine'
EXTERN long p_report; // 'report'
EXTERN long p_pvh; // 'previewheight'
@@ -570,7 +577,7 @@ EXTERN char_u *p_rtp; // 'runtimepath'
EXTERN long p_scbk; // 'scrollback'
EXTERN long p_sj; // 'scrolljump'
EXTERN long p_so; // 'scrolloff'
-EXTERN char_u *p_sbo; // 'scrollopt'
+EXTERN char *p_sbo; // 'scrollopt'
EXTERN char_u *p_sections; // 'sections'
EXTERN int p_secure; // 'secure'
EXTERN char_u *p_sel; // 'selection'
@@ -617,6 +624,7 @@ EXTERN int p_stmp; // 'shelltemp'
EXTERN int p_ssl; // 'shellslash'
#endif
EXTERN char_u *p_stl; // 'statusline'
+EXTERN char *p_wbr; // 'winbar'
EXTERN int p_sr; // 'shiftround'
EXTERN char_u *p_shm; // 'shortmess'
EXTERN char_u *p_sbr; // 'showbreak'
@@ -677,7 +685,6 @@ EXTERN int p_tr; ///< 'tagrelative'
EXTERN char_u *p_tags; ///< 'tags'
EXTERN int p_tgst; ///< 'tagstack'
EXTERN int p_tbidi; ///< 'termbidi'
-EXTERN int p_terse; ///< 'terse'
EXTERN int p_to; ///< 'tildeop'
EXTERN int p_timeout; ///< 'timeout'
EXTERN long p_tm; ///< 'timeoutlen'
@@ -705,12 +712,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
@@ -772,6 +781,7 @@ enum {
BV_CINK,
BV_CINO,
BV_CINW,
+ BV_CINSD,
BV_CM,
BV_CMS,
BV_COM,
@@ -871,6 +881,7 @@ enum {
WV_LBR,
WV_NU,
WV_RNU,
+ WV_VE,
WV_NUW,
WV_PVW,
WV_RL,
@@ -894,14 +905,17 @@ enum {
WV_FCS,
WV_LCS,
WV_WINBL,
+ WV_WBR,
WV_COUNT, // must be the last one
};
// Value for b_p_ul indicating the global value must be used.
-#define NO_LOCAL_UNDOLEVEL -123456
+#define NO_LOCAL_UNDOLEVEL (-123456)
#define SB_MAX 100000 // Maximum 'scrollback' value.
+#define TABSTOP_MAX 9999
+
/// Stores an identifier of a script or channel that last set an option.
typedef struct {
sctx_T script_ctx; /// script context where the option was last set
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index 7715a8803f..37f3770506 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -351,6 +351,15 @@ return {
defaults={if_true="if,else,while,do,for,switch"}
},
{
+ full_name='cinscopedecls', abbreviation='cinsd',
+ short_desc=N_("words that are recognized by 'cino-g'"),
+ type='string', list='onecomma', scope={'buffer'},
+ deny_duplicates=true,
+ alloced=true,
+ varname='p_cinsd',
+ defaults={if_true="public,protected,private"}
+ },
+ {
full_name='clipboard', abbreviation='cb',
short_desc=N_("use the clipboard as the unnamed register"),
type='string', list='onecomma', scope={'global'},
@@ -1241,9 +1250,9 @@ return {
},
{
full_name='insertmode', abbreviation='im',
- short_desc=N_("start the edit of a file in Insert mode"),
+ short_desc=N_("No description"),
type='bool', scope={'global'},
- varname='p_im',
+ varname='p_force_off',
defaults={if_true=false}
},
{
@@ -1588,7 +1597,7 @@ return {
short_desc=N_("the use of mouse clicks"),
type='string', list='flags', scope={'global'},
varname='p_mouse',
- defaults={if_true=""}
+ defaults={if_true="nvi"}
},
{
full_name='mousefocus', abbreviation='mousef',
@@ -1610,7 +1619,15 @@ return {
short_desc=N_("changes meaning of mouse buttons"),
type='string', scope={'global'},
varname='p_mousem',
- defaults={if_true="extend"}
+ defaults={if_true="popup_setpos"}
+ },
+ {
+ full_name='mousescroll',
+ short_desc=N_("amount to scroll by when scrolling with a mouse"),
+ type='string', list='comma', scope={'global'},
+ vi_def=true,
+ varname='p_mousescroll',
+ defaults={if_true="ver:3,hor:6"}
},
{
full_name='mouseshape', abbreviation='mouses',
@@ -1846,7 +1863,7 @@ return {
type='number', scope={'global'},
secure=true,
varname='p_pyx',
- defaults={if_true=0}
+ defaults={if_true=3}
},
{
full_name='quickfixtextfunc', abbreviation='qftf',
@@ -1902,9 +1919,9 @@ return {
},
{
full_name='remap',
- short_desc=N_("mappings to work recursively"),
+ short_desc=N_("No description"),
type='bool', scope={'global'},
- varname='p_remap',
+ varname='p_force_on',
defaults={if_true=true}
},
{
@@ -2520,9 +2537,9 @@ return {
},
{
full_name='terse',
- short_desc=N_("hides notification of search wrap"),
+ short_desc=N_("No description"),
type='bool', scope={'global'},
- varname='p_terse',
+ varname='p_force_off',
defaults={if_true=false}
},
{
@@ -2745,7 +2762,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',
@@ -2832,6 +2849,16 @@ return {
defaults={if_true="menu"}
},
{
+ full_name='winbar', abbreviation='wbr',
+ short_desc=N_("custom format for the window bar"),
+ type='string', scope={'global', 'window'},
+ alloced=true,
+ modelineexpr=true,
+ redraw={'statuslines'},
+ varname='p_wbr',
+ defaults={if_true=""}
+ },
+ {
full_name='winblend', abbreviation='winbl',
short_desc=N_("Controls transparency level for floating windows"),
type='number', scope={'window'},
diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c
index e9f44d2775..9c93057fe7 100644
--- a/src/nvim/os/env.c
+++ b/src/nvim/os/env.c
@@ -529,9 +529,9 @@ void free_homedir(void)
/// again soon.
/// @param src String containing environment variables to expand
/// @see {expand_env}
-char_u *expand_env_save(char_u *src)
+char *expand_env_save(char *src)
{
- return expand_env_save_opt(src, false);
+ return (char *)expand_env_save_opt((char_u *)src, false);
}
/// Similar to expand_env_save() but when "one" is `true` handle the string as
@@ -580,14 +580,14 @@ void expand_env_esc(char_u *restrict srcp, char_u *restrict dst, int dstlen, boo
int prefix_len = (prefix == NULL) ? 0 : (int)STRLEN(prefix);
- char_u *src = skipwhite(srcp);
+ char_u *src = (char_u *)skipwhite((char *)srcp);
dstlen--; // leave one char space for "\,"
while (*src && dstlen > 0) {
// Skip over `=expr`.
if (src[0] == '`' && src[1] == '=') {
var = src;
src += 2;
- (void)skip_expr(&src);
+ (void)skip_expr((char **)&src);
if (*src == '`') {
src++;
}
@@ -644,7 +644,7 @@ void expand_env_esc(char_u *restrict srcp, char_u *restrict dst, int dstlen, boo
#endif
} else if (src[1] == NUL // home directory
|| vim_ispathsep(src[1])
- || vim_strchr((char_u *)" ,\t\n", src[1]) != NULL) {
+ || vim_strchr(" ,\t\n", src[1]) != NULL) {
var = (char_u *)homedir;
tail = src + 1;
} else { // user directory
@@ -663,7 +663,7 @@ void expand_env_esc(char_u *restrict srcp, char_u *restrict dst, int dstlen, boo
// Get the user directory. If this fails the shell is used to expand
// ~user, which is slower and may fail on old versions of /bin/sh.
var = (*dst == NUL) ? NULL
- : (char_u *)os_get_user_directory((char *)dst + 1);
+ : (char_u *)os_get_userdir((char *)dst + 1);
mustfree = true;
if (var == NULL) {
expand_T xpc;
@@ -698,7 +698,7 @@ void expand_env_esc(char_u *restrict srcp, char_u *restrict dst, int dstlen, boo
// If "var" contains white space, escape it with a backslash.
// Required for ":e ~/tt" when $HOME includes a space.
- if (esc && var != NULL && vim_strpbrk(var, (char_u *)" \t") != NULL) {
+ if (esc && var != NULL && strpbrk((char *)var, " \t") != NULL) {
char_u *p = vim_strsave_escaped(var, (char_u *)" \t");
if (mustfree) {
@@ -715,12 +715,12 @@ void expand_env_esc(char_u *restrict srcp, char_u *restrict dst, int dstlen, boo
int c = (int)STRLEN(var);
// if var[] ends in a path separator and tail[] starts
// with it, skip a character
- if (*var != NUL && after_pathsep((char *)dst, (char *)dst + c)
+ if (after_pathsep((char *)dst, (char *)dst + c)
#if defined(BACKSLASH_IN_FILENAME)
&& dst[-1] != ':'
#endif
&& vim_ispathsep(*tail)) {
- ++tail;
+ tail++;
}
dst += c;
src = tail;
@@ -738,7 +738,7 @@ void expand_env_esc(char_u *restrict srcp, char_u *restrict dst, int dstlen, boo
at_start = false;
if (src[0] == '\\' && src[1] != NUL) {
*dst++ = *src++;
- --dstlen;
+ dstlen--;
} else if ((src[0] == ' ' || src[0] == ',') && !one) {
at_start = true;
}
@@ -805,7 +805,7 @@ static char *remove_tail(char *path, char *pend, char *dirname)
char *new_tail = pend - len - 1;
if (new_tail >= path
- && fnamencmp((char_u *)new_tail, (char_u *)dirname, len) == 0
+ && FNAMENCMP((char_u *)new_tail, (char_u *)dirname, len) == 0
&& (new_tail == path || after_pathsep(path, new_tail))) {
return new_tail;
}
@@ -878,17 +878,15 @@ const void *vim_env_iter_rev(const char delim, const char *const val, const void
}
}
-
/// @param[out] exe_name should be at least MAXPATHL in size
void vim_get_prefix_from_exepath(char *exe_name)
{
// TODO(bfredl): param could have been written as "char exe_name[MAXPATHL]"
// but c_grammar.lua does not recognize it (yet).
- xstrlcpy(exe_name, (char *)get_vim_var_str(VV_PROGPATH),
- MAXPATHL * sizeof(*exe_name));
- char *path_end = (char *)path_tail_with_sep((char_u *)exe_name);
+ xstrlcpy(exe_name, get_vim_var_str(VV_PROGPATH), MAXPATHL * sizeof(*exe_name));
+ char *path_end = path_tail_with_sep(exe_name);
*path_end = '\0'; // remove the trailing "nvim.exe"
- path_end = (char *)path_tail((char_u *)exe_name);
+ path_end = path_tail(exe_name);
*path_end = '\0'; // remove the trailing "bin/"
}
@@ -940,7 +938,7 @@ char *vim_getenv(const char *name)
// - the directory name from 'helpfile' (unless it contains '$')
// - the executable name from argv[0]
if (vim_path == NULL) {
- if (p_hf != NULL && vim_strchr(p_hf, '$') == NULL) {
+ if (p_hf != NULL && vim_strchr((char *)p_hf, '$') == NULL) {
vim_path = (char *)p_hf;
}
@@ -957,7 +955,7 @@ char *vim_getenv(const char *name)
if (vim_path != NULL) {
// remove the file name
- char *vim_path_end = (char *)path_tail((char_u *)vim_path);
+ char *vim_path_end = path_tail(vim_path);
// remove "doc/" from 'helpfile', if present
if (vim_path == (char *)p_hf) {
@@ -1035,7 +1033,7 @@ char *vim_getenv(const char *name)
/// a list of them.
///
/// @return length of the string put into dst, does not include NUL byte.
-size_t home_replace(const buf_T *const buf, const char_u *src, char_u *const dst, size_t dstlen,
+size_t home_replace(const buf_T *const buf, const char *src, char *const dst, size_t dstlen,
const bool one)
FUNC_ATTR_NONNULL_ARG(3)
{
@@ -1048,7 +1046,7 @@ size_t home_replace(const buf_T *const buf, const char_u *src, char_u *const dst
}
if (buf != NULL && buf->b_help) {
- const size_t dlen = STRLCPY(dst, path_tail(src), dstlen);
+ const size_t dlen = STRLCPY(dst, path_tail((char *)src), dstlen);
return MIN(dlen, dstlen - 1);
}
@@ -1072,8 +1070,8 @@ size_t home_replace(const buf_T *const buf, const char_u *src, char_u *const dst
size_t usedlen = 0;
size_t flen = strlen(homedir_env_mod);
char_u *fbuf = NULL;
- (void)modify_fname((char_u *)":p", false, &usedlen,
- (char_u **)&homedir_env_mod, &fbuf, &flen);
+ (void)modify_fname(":p", false, &usedlen,
+ &homedir_env_mod, (char **)&fbuf, &flen);
flen = strlen(homedir_env_mod);
assert(homedir_env_mod != homedir_env);
if (vim_ispathsep(homedir_env_mod[flen - 1])) {
@@ -1087,9 +1085,9 @@ size_t home_replace(const buf_T *const buf, const char_u *src, char_u *const dst
}
if (!one) {
- src = skipwhite(src);
+ src = skipwhite((char *)src);
}
- char *dst_p = (char *)dst;
+ char *dst_p = dst;
while (*src && dstlen > 0) {
// Here we are at the beginning of a file name.
// First, check to see if the beginning of the file name matches
@@ -1102,7 +1100,7 @@ size_t home_replace(const buf_T *const buf, const char_u *src, char_u *const dst
size_t len = dirlen;
for (;;) {
if (len
- && fnamencmp(src, (char_u *)p, len) == 0
+ && FNAMENCMP(src, (char_u *)p, len) == 0
&& (vim_ispathsep(src[len])
|| (!one && (src[len] == ',' || src[len] == ' '))
|| src[len] == NUL)) {
@@ -1111,10 +1109,9 @@ size_t home_replace(const buf_T *const buf, const char_u *src, char_u *const dst
*dst_p++ = '~';
}
- // If it's just the home directory, add "/".
- if (!vim_ispathsep(src[0]) && --dstlen > 0) {
- *dst_p++ = '/';
- }
+ // Do not add directory separator into dst, because dst is
+ // expected to just return the directory name without the
+ // directory separator '/'.
break;
}
if (p == homedir_env_mod) {
@@ -1126,11 +1123,11 @@ size_t home_replace(const buf_T *const buf, const char_u *src, char_u *const dst
// if (!one) skip to separator: space or comma.
while (*src && (one || (*src != ',' && *src != ' ')) && --dstlen > 0) {
- *dst_p++ = (char)(*src++);
+ *dst_p++ = *src++;
}
// Skip separator.
while ((*src == ' ' || *src == ',') && --dstlen > 0) {
- *dst_p++ = (char)(*src++);
+ *dst_p++ = *src++;
}
}
// If (dstlen == 0) out of space, what to do???
@@ -1140,26 +1137,26 @@ size_t home_replace(const buf_T *const buf, const char_u *src, char_u *const dst
if (must_free) {
xfree(homedir_env_mod);
}
- return (size_t)(dst_p - (char *)dst);
+ return (size_t)(dst_p - dst);
}
/// Like home_replace, store the replaced string in allocated memory.
/// @param buf When not NULL, check for help files
/// @param src Input file name
-char_u *home_replace_save(buf_T *buf, char_u *src) FUNC_ATTR_NONNULL_RET
+char *home_replace_save(buf_T *buf, char *src)
+ FUNC_ATTR_NONNULL_RET
{
size_t len = 3; // space for "~/" and trailing NUL
if (src != NULL) { // just in case
len += STRLEN(src);
}
- char_u *dst = xmalloc(len);
+ char *dst = xmalloc(len);
home_replace(buf, src, dst, len, true);
return dst;
}
-
/// Function given to ExpandGeneric() to obtain an environment variable name.
-char_u *get_env_name(expand_T *xp, int idx)
+char *get_env_name(expand_T *xp, int idx)
{
#define ENVNAMELEN 100
// this static buffer is needed to avoid a memory leak in ExpandGeneric
@@ -1169,7 +1166,7 @@ char_u *get_env_name(expand_T *xp, int idx)
if (envname) {
STRLCPY(name, envname, ENVNAMELEN);
xfree(envname);
- return name;
+ return (char *)name;
}
return NULL;
}
@@ -1193,7 +1190,7 @@ bool os_setenv_append_path(const char *fname)
internal_error("os_setenv_append_path()");
return false;
}
- const char *tail = (char *)path_tail_with_sep((char_u *)fname);
+ const char *tail = path_tail_with_sep((char *)fname);
size_t dirlen = (size_t)(tail - fname);
assert(tail >= fname && dirlen + 1 < sizeof(os_buf));
xstrlcpy(os_buf, fname, dirlen + 1);
@@ -1227,10 +1224,10 @@ bool os_shell_is_cmdexe(const char *sh)
}
if (striequal(sh, "$COMSPEC")) {
const char *comspec = os_getenv("COMSPEC");
- return striequal("cmd.exe", (char *)path_tail((char_u *)comspec));
+ return striequal("cmd.exe", path_tail(comspec));
}
if (striequal(sh, "cmd.exe") || striequal(sh, "cmd")) {
return true;
}
- return striequal("cmd.exe", (char *)path_tail((char_u *)sh));
+ return striequal("cmd.exe", path_tail(sh));
}
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index 24c7678633..901a1bc5a6 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -40,8 +40,10 @@
bool did_try_to_free = false; \
uv_call_start: {} \
uv_fs_t req; \
+ fs_loop_lock(); \
ret = func(&fs_loop, &req, __VA_ARGS__); \
uv_fs_req_cleanup(&req); \
+ fs_loop_unlock(); \
if (ret == UV_ENOMEM && !did_try_to_free) { \
try_to_free_memory(); \
did_try_to_free = true; \
@@ -52,14 +54,27 @@ uv_call_start: {} \
// Many fs functions from libuv return that value on success.
static const int kLibuvSuccess = 0;
static uv_loop_t fs_loop;
-
+static uv_mutex_t fs_loop_mutex;
// Initialize the fs module
void fs_init(void)
{
uv_loop_init(&fs_loop);
+ uv_mutex_init_recursive(&fs_loop_mutex);
+}
+
+/// TODO(bfredl): some of these operations should
+/// be possible to do the private libuv loop of the
+/// thread, instead of contending the global fs loop
+void fs_loop_lock(void)
+{
+ uv_mutex_lock(&fs_loop_mutex);
}
+void fs_loop_unlock(void)
+{
+ uv_mutex_unlock(&fs_loop_mutex);
+}
/// Changes the current directory to `path`.
///
@@ -98,9 +113,12 @@ bool os_isrealdir(const char *name)
FUNC_ATTR_NONNULL_ALL
{
uv_fs_t request;
+ fs_loop_lock();
if (uv_fs_lstat(&fs_loop, &request, name, NULL) != kLibuvSuccess) {
+ fs_loop_unlock();
return false;
}
+ fs_loop_unlock();
if (S_ISLNK(request.statbuf.st_mode)) {
return false;
} else {
@@ -108,7 +126,7 @@ bool os_isrealdir(const char *name)
}
}
-/// Check if the given path is a directory or not.
+/// Check if the given path exists and is a directory.
///
/// @return `true` if `name` is a directory.
bool os_isdir(const char_u *name)
@@ -738,7 +756,9 @@ static int os_stat(const char *name, uv_stat_t *statbuf)
return UV_EINVAL;
}
uv_fs_t request;
+ fs_loop_lock();
int result = uv_fs_stat(&fs_loop, &request, name, NULL);
+ fs_loop_unlock();
if (result == kLibuvSuccess) {
*statbuf = request.statbuf;
}
@@ -771,6 +791,27 @@ int os_setperm(const char *const name, int perm)
return (r == kLibuvSuccess ? OK : FAIL);
}
+#ifdef UNIX
+/// Checks if the current user owns a file.
+///
+/// Uses both uv_fs_stat() and uv_fs_lstat() via os_fileinfo() and
+/// os_fileinfo_link() respectively for extra security.
+bool os_file_owned(const char *fname)
+ FUNC_ATTR_NONNULL_ALL
+{
+ uid_t uid = getuid();
+ FileInfo finfo;
+ bool file_owned = os_fileinfo(fname, &finfo) && finfo.stat.st_uid == uid;
+ bool link_owned = os_fileinfo_link(fname, &finfo) && finfo.stat.st_uid == uid;
+ return file_owned && link_owned;
+}
+#else
+bool os_file_owned(const char *fname)
+{
+ return true; // TODO(justinmk): Windows. #8244
+}
+#endif
+
/// Changes the owner and group of a file, like chown(2).
///
/// @return 0 on success, or libuv error code on failure.
@@ -894,7 +935,7 @@ int os_mkdir_recurse(const char *const dir, int32_t mode, char **const failed_di
const char *const real_end = e;
const char past_head_save = *past_head;
while (!os_isdir((char_u *)curdir)) {
- e = (char *)path_tail_with_sep((char_u *)curdir);
+ e = path_tail_with_sep(curdir);
if (e <= past_head) {
*past_head = NUL;
break;
@@ -935,9 +976,11 @@ int os_mkdtemp(const char *template, char *path)
FUNC_ATTR_NONNULL_ALL
{
uv_fs_t request;
+ fs_loop_lock();
int result = uv_fs_mkdtemp(&fs_loop, &request, template, NULL);
+ fs_loop_unlock();
if (result == kLibuvSuccess) {
- STRNCPY(path, request.path, TEMP_FILE_PATH_MAXLEN);
+ xstrlcpy(path, request.path, TEMP_FILE_PATH_MAXLEN);
}
uv_fs_req_cleanup(&request);
return result;
@@ -962,7 +1005,9 @@ int os_rmdir(const char *path)
bool os_scandir(Directory *dir, const char *path)
FUNC_ATTR_NONNULL_ALL
{
+ fs_loop_lock();
int r = uv_fs_scandir(&fs_loop, &dir->request, path, 0, NULL);
+ fs_loop_unlock();
if (r < 0) {
os_closedir(dir);
}
@@ -1023,7 +1068,9 @@ bool os_fileinfo_link(const char *path, FileInfo *file_info)
return false;
}
uv_fs_t request;
+ fs_loop_lock();
bool ok = uv_fs_lstat(&fs_loop, &request, path, NULL) == kLibuvSuccess;
+ fs_loop_unlock();
if (ok) {
file_info->stat = request.statbuf;
}
@@ -1041,6 +1088,7 @@ bool os_fileinfo_fd(int file_descriptor, FileInfo *file_info)
{
uv_fs_t request;
memset(file_info, 0, sizeof(*file_info));
+ fs_loop_lock();
bool ok = uv_fs_fstat(&fs_loop,
&request,
file_descriptor,
@@ -1049,6 +1097,7 @@ bool os_fileinfo_fd(int file_descriptor, FileInfo *file_info)
file_info->stat = request.statbuf;
}
uv_fs_req_cleanup(&request);
+ fs_loop_unlock();
return ok;
}
@@ -1165,6 +1214,7 @@ char *os_realpath(const char *name, char *buf)
FUNC_ATTR_NONNULL_ARG(1)
{
uv_fs_t request;
+ fs_loop_lock();
int result = uv_fs_realpath(&fs_loop, &request, name, NULL);
if (result == kLibuvSuccess) {
if (buf == NULL) {
@@ -1173,6 +1223,7 @@ char *os_realpath(const char *name, char *buf)
xstrlcpy(buf, request.ptr, MAXPATHL + 1);
}
uv_fs_req_cleanup(&request);
+ fs_loop_unlock();
return result == kLibuvSuccess ? buf : NULL;
}
@@ -1261,7 +1312,7 @@ shortcut_end:
return rfname;
}
-# define is_path_sep(c) ((c) == L'\\' || (c) == L'/')
+# define IS_PATH_SEP(c) ((c) == L'\\' || (c) == L'/')
/// Returns true if the path contains a reparse point (junction or symbolic
/// link). Otherwise false in returned.
bool os_is_reparse_point_include(const char *path)
@@ -1278,9 +1329,9 @@ bool os_is_reparse_point_include(const char *path)
}
p = utf16_path;
- if (isalpha(p[0]) && p[1] == L':' && is_path_sep(p[2])) {
+ if (isalpha(p[0]) && p[1] == L':' && IS_PATH_SEP(p[2])) {
p += 3;
- } else if (is_path_sep(p[0]) && is_path_sep(p[1])) {
+ } else if (IS_PATH_SEP(p[0]) && IS_PATH_SEP(p[1])) {
p += 2;
}
diff --git a/src/nvim/os/fs.h b/src/nvim/os/fs.h
new file mode 100644
index 0000000000..c68081da02
--- /dev/null
+++ b/src/nvim/os/fs.h
@@ -0,0 +1,10 @@
+#ifndef NVIM_OS_FS_H
+#define NVIM_OS_FS_H
+
+#include "nvim/os/fs_defs.h" // for uv_*
+#include "nvim/types.h" // for char_u
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "os/fs.h.generated.h"
+#endif
+#endif // NVIM_OS_FS_H
diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c
index 3790eba212..c47a891c18 100644
--- a/src/nvim/os/input.c
+++ b/src/nvim/os/input.c
@@ -13,12 +13,13 @@
#include "nvim/ex_cmds2.h"
#include "nvim/fileio.h"
#include "nvim/getchar.h"
-#include "nvim/keymap.h"
+#include "nvim/keycodes.h"
#include "nvim/main.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/os/input.h"
+#include "nvim/screen.h"
#include "nvim/state.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
@@ -81,7 +82,7 @@ void input_stop(void)
static void cursorhold_event(void **argv)
{
- event_T event = State & INSERT ? EVENT_CURSORHOLDI : EVENT_CURSORHOLD;
+ event_T event = State & MODE_INSERT ? EVENT_CURSORHOLDI : EVENT_CURSORHOLD;
apply_autocmds(event, NULL, NULL, false, curbuf);
did_cursorhold = true;
}
@@ -98,7 +99,7 @@ static void create_cursorhold_event(bool events_enabled)
/// Low level input function
///
-/// wait until either the input buffer is non-empty or , if `events` is not NULL
+/// wait until either the input buffer is non-empty or, if `events` is not NULL
/// until `events` is non-empty.
int os_inchar(uint8_t *buf, int maxlen, int ms, int tb_change_cnt, MultiQueue *events)
{
@@ -106,6 +107,11 @@ int os_inchar(uint8_t *buf, int maxlen, int ms, int tb_change_cnt, MultiQueue *e
return (int)rbuffer_read(input_buffer, (char *)buf, (size_t)maxlen);
}
+ // No risk of a UI flood, so disable CTRL-C "interrupt" behavior if it's mapped.
+ if ((mapped_ctrl_c | curbuf->b_mapped_ctrl_c) & get_real_state()) {
+ ctrl_c_interrupts = false;
+ }
+
InbufPollResult result;
if (ms >= 0) {
if ((result = inbuf_poll(ms, events)) == kInputNone) {
@@ -127,6 +133,8 @@ int os_inchar(uint8_t *buf, int maxlen, int ms, int tb_change_cnt, MultiQueue *e
}
}
+ ctrl_c_interrupts = true;
+
// If input was put directly in typeahead buffer bail out here.
if (typebuf_changed(tb_change_cnt)) {
return 0;
@@ -171,15 +179,7 @@ void os_breakcheck(void)
return;
}
- int save_us = updating_screen;
- // We do not want screen_resize() to redraw here.
- // TODO(bfredl): we are already special casing redraw events, is this
- // hack still needed?
- updating_screen++;
-
loop_poll_events(&main_loop, 0);
-
- updating_screen = save_us;
}
#define BREAKCHECK_SKIP 1000
@@ -216,7 +216,6 @@ void veryfast_breakcheck(void)
}
}
-
/// Test whether a file descriptor refers to a terminal.
///
/// @param fd File descriptor.
@@ -234,13 +233,13 @@ 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 };
+ // Do not simplify the keys here. Simplification will be done later.
unsigned int new_size
- = trans_special((const uint8_t **)&ptr, (size_t)(end - ptr), buf, true,
- false);
+ = trans_special((const uint8_t **)&ptr, (size_t)(end - ptr), buf, FSK_KEYCODE, true, NULL);
if (new_size) {
new_size = handle_mouse_event(&ptr, buf, new_size);
@@ -263,12 +262,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);
@@ -279,7 +274,7 @@ size_t input_enqueue(String keys)
}
size_t rv = (size_t)(ptr - keys.data);
- process_interrupts();
+ process_ctrl_c();
return rv;
}
@@ -292,8 +287,13 @@ static uint8_t check_multiclick(int code, int grid, int row, int col)
static int orig_mouse_row = 0;
static uint64_t orig_mouse_time = 0; // time of previous mouse click
- if (code == KE_LEFTRELEASE || code == KE_RIGHTRELEASE
- || code == KE_MIDDLERELEASE) {
+ if (code == KE_LEFTRELEASE
+ || code == KE_RIGHTRELEASE
+ || code == KE_MIDDLERELEASE
+ || code == KE_MOUSEDOWN
+ || code == KE_MOUSEUP
+ || code == KE_MOUSELEFT
+ || code == KE_MOUSERIGHT) {
return 0;
}
uint64_t mouse_time = os_hrtime(); // time of current mouse click (ns)
@@ -329,7 +329,6 @@ static uint8_t check_multiclick(int code, int grid, int row, int col)
return modifiers;
}
-
// Mouse event handling code(Extract row/col if available and detect multiple
// clicks)
static unsigned int handle_mouse_event(char **ptr, uint8_t *buf, unsigned int bufsize)
@@ -412,7 +411,7 @@ size_t input_enqueue_mouse(int code, uint8_t modifier, int grid, int row, int co
mouse_row = row;
mouse_col = col;
- size_t written = 3 + (size_t)(p-buf);
+ size_t written = 3 + (size_t)(p - buf);
rbuffer_write(input_buffer, (char *)buf, written);
return written;
}
@@ -479,15 +478,20 @@ static void input_read_cb(Stream *stream, RBuffer *buf, size_t c, void *data, bo
}
}
-static void process_interrupts(void)
+static void process_ctrl_c(void)
{
- if ((mapped_ctrl_c | curbuf->b_mapped_ctrl_c) & get_real_state()) {
+ if (!ctrl_c_interrupts) {
return;
}
size_t consume_count = 0;
RBUFFER_EACH_REVERSE(input_buffer, c, i) {
- if ((uint8_t)c == Ctrl_C) {
+ if ((uint8_t)c == Ctrl_C
+ || ((uint8_t)c == 'C' && i >= 3
+ && (uint8_t)(*rbuffer_get(input_buffer, i - 3)) == K_SPECIAL
+ && (uint8_t)(*rbuffer_get(input_buffer, i - 2)) == KS_MODIFIER
+ && (uint8_t)(*rbuffer_get(input_buffer, i - 1)) == MOD_MASK_CTRL)) {
+ *rbuffer_get(input_buffer, i) = Ctrl_C;
got_int = true;
consume_count = i;
break;
@@ -526,6 +530,7 @@ static bool input_ready(MultiQueue *events)
// Exit because of an input read error.
static void read_error_exit(void)
+ FUNC_ATTR_NORETURN
{
if (silent_mode) { // Normal way to exit for "nvim -es".
getout(0);
diff --git a/src/nvim/os/lang.c b/src/nvim/os/lang.c
index b63faacaae..28f43ff3af 100644
--- a/src/nvim/os/lang.c
+++ b/src/nvim/os/lang.c
@@ -3,9 +3,10 @@
#ifdef __APPLE__
# define Boolean CFBoolean // Avoid conflict with API's Boolean
-# include <CoreFoundation/CFLocale.h>
-# include <CoreFoundation/CFString.h>
+# define FileInfo CSFileInfo // Avoid conflict with API's Fileinfo
+# include <CoreServices/CoreServices.h>
# undef Boolean
+# undef FileInfo
#endif
#include "auto/config.h"
@@ -21,55 +22,24 @@ void lang_init(void)
{
#ifdef __APPLE__
if (os_getenv("LANG") == NULL) {
- const char *lang_region = NULL;
- CFTypeRef cf_lang_region = NULL;
-
- CFLocaleRef cf_locale = CFLocaleCopyCurrent();
- if (cf_locale) {
- cf_lang_region = CFLocaleGetValue(cf_locale, kCFLocaleIdentifier);
- CFRetain(cf_lang_region);
- lang_region = CFStringGetCStringPtr(cf_lang_region,
- kCFStringEncodingUTF8);
- CFRelease(cf_locale);
- } else {
- // Use the primary language defined in Preferences -> Language & Region
- CFArrayRef cf_langs = CFLocaleCopyPreferredLanguages();
- if (cf_langs && CFArrayGetCount(cf_langs) > 0) {
- cf_lang_region = CFArrayGetValueAtIndex(cf_langs, 0);
- CFRetain(cf_lang_region);
- CFRelease(cf_langs);
- lang_region = CFStringGetCStringPtr(cf_lang_region,
- kCFStringEncodingUTF8);
- } else {
- ELOG("$LANG is empty and your primary language cannot be inferred.");
- return;
- }
- }
-
char buf[50] = { 0 };
- bool set_lang;
- if (lang_region) {
- set_lang = true;
- xstrlcpy(buf, lang_region, sizeof(buf));
- } else {
- set_lang = CFStringGetCString(cf_lang_region, buf, 40,
- kCFStringEncodingUTF8);
- }
- if (set_lang) {
+
+ // $LANG is not set, either because it was unset or Nvim was started
+ // from the Dock. Query the system locale.
+ if (LocaleRefGetPartString(NULL,
+ kLocaleLanguageMask | kLocaleLanguageVariantMask |
+ kLocaleRegionMask | kLocaleRegionVariantMask,
+ sizeof(buf) - 10, buf) == noErr && *buf) {
if (strcasestr(buf, "utf-8") == NULL) {
xstrlcat(buf, ".UTF-8", sizeof(buf));
}
os_setenv("LANG", buf, true);
+ setlocale(LC_ALL, "");
+ // Make sure strtod() uses a decimal point, not a comma.
+ setlocale(LC_NUMERIC, "C");
+ } else {
+ ELOG("$LANG is empty and the macOS primary language cannot be inferred.");
}
- CFRelease(cf_lang_region);
-# ifdef HAVE_LOCALE_H
- setlocale(LC_ALL, "");
-
-# ifdef LC_NUMERIC
- // Make sure strtod() uses a decimal point, not a comma.
- setlocale(LC_NUMERIC, "C");
-# endif
-# endif
}
#endif
}
diff --git a/src/nvim/os/os.h b/src/nvim/os/os.h
index bff2936f8e..a7496130cc 100644
--- a/src/nvim/os/os.h
+++ b/src/nvim/os/os.h
@@ -16,4 +16,7 @@
# include "os/users.h.generated.h"
#endif
+#define ENV_LOGFILE "NVIM_LOG_FILE"
+#define ENV_NVIM "NVIM"
+
#endif // NVIM_OS_OS_H
diff --git a/src/nvim/os/os_defs.h b/src/nvim/os/os_defs.h
index dce4b0c187..a4361859ec 100644
--- a/src/nvim/os/os_defs.h
+++ b/src/nvim/os/os_defs.h
@@ -14,7 +14,7 @@
#endif
#if !defined(NAME_MAX) && defined(_XOPEN_NAME_MAX)
-#define NAME_MAX _XOPEN_NAME_MAX
+# define NAME_MAX _XOPEN_NAME_MAX
#endif
#define BASENAMELEN (NAME_MAX - 5)
diff --git a/src/nvim/os/os_win_console.c b/src/nvim/os/os_win_console.c
index 18e2e02b81..20b7f869f1 100644
--- a/src/nvim/os/os_win_console.c
+++ b/src/nvim/os/os_win_console.c
@@ -9,7 +9,6 @@
# include "os/os_win_console.c.generated.h"
#endif
-
int os_get_conin_fd(void)
{
const HANDLE conin_handle = CreateFile("CONIN$",
diff --git a/src/nvim/os/pty_process_unix.c b/src/nvim/os/pty_process_unix.c
index 3459646bad..c5d6af0ff6 100644
--- a/src/nvim/os/pty_process_unix.c
+++ b/src/nvim/os/pty_process_unix.c
@@ -16,11 +16,11 @@
#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
# include <util.h>
#elif defined(__sun)
-# include <sys/stream.h>
-# include <sys/syscall.h>
-# include <fcntl.h>
-# include <unistd.h>
-# include <signal.h>
+# include <fcntl.h>
+# include <signal.h>
+# include <sys/stream.h>
+# include <sys/syscall.h>
+# include <unistd.h>
#else
# include <pty.h>
#endif
@@ -49,10 +49,10 @@
// this header defines STR, just as nvim.h, but it is defined as ('S'<<8),
// to avoid #undef STR, #undef STR, #define STR ('S'<<8) just delay the
// inclusion of the header even though it gets include out of order.
-#include <sys/stropts.h>
+# include <sys/stropts.h>
-static int openpty(int *amaster, int *aslave, char *name,
- struct termios *termp, struct winsize *winp)
+static int openpty(int *amaster, int *aslave, char *name, struct termios *termp,
+ struct winsize *winp)
{
int slave = -1;
int master = open("/dev/ptmx", O_RDWR);
@@ -63,7 +63,7 @@ static int openpty(int *amaster, int *aslave, char *name,
// grantpt will invoke a setuid program to change permissions
// and might fail if SIGCHLD handler is set, temporarily reset
// while running
- void(*sig_saved)(int) = signal(SIGCHLD, SIG_DFL);
+ void (*sig_saved)(int) = signal(SIGCHLD, SIG_DFL);
int res = grantpt(master);
signal(SIGCHLD, sig_saved);
@@ -86,7 +86,7 @@ static int openpty(int *amaster, int *aslave, char *name,
ioctl(slave, I_PUSH, "ptem");
// ldterm provides most of the termio terminal interface
ioctl(slave, I_PUSH, "ldterm");
- // ttcompat compatability with older terminal ioctls
+ // ttcompat compatibility with older terminal ioctls
ioctl(slave, I_PUSH, "ttcompat");
if (termp) {
@@ -129,8 +129,7 @@ static int login_tty(int fd)
return 0;
}
-static pid_t forkpty(int *amaster, char *name,
- struct termios *termp, struct winsize *winp)
+static pid_t forkpty(int *amaster, char *name, struct termios *termp, struct winsize *winp)
{
int master, slave;
if (openpty(&master, &slave, name, termp, winp) == -1) {
@@ -164,10 +163,15 @@ static struct termios termios_default;
/// @param tty_fd TTY file descriptor, or -1 if not in a terminal.
void pty_process_save_termios(int tty_fd)
{
- DLOG("tty_fd=%d", tty_fd);
- if (tty_fd == -1 || tcgetattr(tty_fd, &termios_default) != 0) {
+ if (tty_fd == -1) {
return;
}
+ int rv = tcgetattr(tty_fd, &termios_default);
+ if (rv != 0) {
+ ELOG("tcgetattr failed (tty_fd=%d): %s", tty_fd, strerror(errno));
+ } else {
+ DLOG("tty_fd=%d", tty_fd);
+ }
}
/// @returns zero on success, or negative error code
diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c
index f78f3e66f5..6233a90638 100644
--- a/src/nvim/os/pty_process_win.c
+++ b/src/nvim/os/pty_process_win.c
@@ -4,7 +4,6 @@
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
-#include <winpty_constants.h>
#include "nvim/ascii.h"
#include "nvim/mbyte.h" // for utf8_to_utf16, utf16_to_utf8
@@ -23,11 +22,7 @@ static void CALLBACK pty_process_finish1(void *context, BOOLEAN unused)
PtyProcess *ptyproc = (PtyProcess *)context;
Process *proc = (Process *)ptyproc;
- if (ptyproc->type == kConpty
- && ptyproc->object.conpty != NULL) {
- os_conpty_free(ptyproc->object.conpty);
- ptyproc->object.conpty = NULL;
- }
+ os_conpty_free(ptyproc->conpty);
uv_timer_init(&proc->loop->uv, &ptyproc->wait_eof_timer);
ptyproc->wait_eof_timer.data = (void *)ptyproc;
uv_timer_start(&ptyproc->wait_eof_timer, wait_eof_timer_cb, 200, 200);
@@ -39,10 +34,6 @@ int pty_process_spawn(PtyProcess *ptyproc)
{
Process *proc = (Process *)ptyproc;
int status = 0;
- winpty_error_ptr_t err = NULL;
- winpty_config_t *cfg = NULL;
- winpty_spawn_config_t *spawncfg = NULL;
- winpty_t *winpty_object = NULL;
conpty_t *conpty_object = NULL;
char *in_name = NULL;
char *out_name = NULL;
@@ -56,39 +47,11 @@ int pty_process_spawn(PtyProcess *ptyproc)
assert(proc->err.closed);
- if (os_has_conpty_working()) {
- if ((conpty_object =
- os_conpty_init(&in_name, &out_name,
- ptyproc->width, ptyproc->height)) != NULL) {
- ptyproc->type = kConpty;
- }
- }
-
- if (ptyproc->type == kWinpty) {
- cfg = winpty_config_new(WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION, &err);
- if (cfg == NULL) {
- emsg = "winpty_config_new failed";
- goto cleanup;
- }
-
- winpty_config_set_initial_size(cfg, ptyproc->width, ptyproc->height);
- winpty_object = winpty_open(cfg, &err);
- if (winpty_object == NULL) {
- emsg = "winpty_open failed";
- goto cleanup;
- }
-
- status = utf16_to_utf8(winpty_conin_name(winpty_object), -1, &in_name);
- if (status != 0) {
- emsg = "utf16_to_utf8(winpty_conin_name) failed";
- goto cleanup;
- }
-
- status = utf16_to_utf8(winpty_conout_name(winpty_object), -1, &out_name);
- if (status != 0) {
- emsg = "utf16_to_utf8(winpty_conout_name) failed";
- goto cleanup;
- }
+ if (!os_has_conpty_working() || (conpty_object = os_conpty_init(&in_name,
+ &out_name, ptyproc->width,
+ ptyproc->height)) == NULL) {
+ status = UV_ENOSYS;
+ goto cleanup;
}
if (!proc->in.closed) {
@@ -131,44 +94,15 @@ int pty_process_spawn(PtyProcess *ptyproc)
goto cleanup;
}
- if (ptyproc->type == kConpty) {
- if (!os_conpty_spawn(conpty_object,
- &process_handle,
- NULL,
- cmd_line,
- cwd,
- env)) {
- emsg = "os_conpty_spawn failed";
- status = (int)GetLastError();
- goto cleanup;
- }
- } else {
- spawncfg = winpty_spawn_config_new(WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN,
- NULL, // Optional application name
- cmd_line,
- cwd,
- env,
- &err);
- if (spawncfg == NULL) {
- emsg = "winpty_spawn_config_new failed";
- goto cleanup;
- }
-
- DWORD win_err = 0;
- if (!winpty_spawn(winpty_object,
- spawncfg,
- &process_handle,
- NULL, // Optional thread handle
- &win_err,
- &err)) {
- if (win_err) {
- status = (int)win_err;
- emsg = "failed to spawn process";
- } else {
- emsg = "winpty_spawn failed";
- }
- goto cleanup;
- }
+ if (!os_conpty_spawn(conpty_object,
+ &process_handle,
+ NULL,
+ cmd_line,
+ cwd,
+ env)) {
+ emsg = "os_conpty_spawn failed";
+ status = (int)GetLastError();
+ goto cleanup;
}
proc->pid = (int)GetProcessId(process_handle);
@@ -187,11 +121,8 @@ int pty_process_spawn(PtyProcess *ptyproc)
uv_run(&proc->loop->uv, UV_RUN_ONCE);
}
- (ptyproc->type == kConpty) ?
- (void *)(ptyproc->object.conpty = conpty_object) :
- (void *)(ptyproc->object.winpty = winpty_object);
+ ptyproc->conpty = conpty_object;
ptyproc->process_handle = process_handle;
- winpty_object = NULL;
conpty_object = NULL;
process_handle = NULL;
@@ -201,16 +132,7 @@ cleanup:
ELOG("pty_process_spawn(%s): %s: error code: %d",
proc->argv[0], emsg, status);
status = os_translate_sys_error(status);
- } else if (err != NULL) {
- status = (int)winpty_error_code(err);
- ELOG("pty_process_spawn(%s): %s: error code: %d",
- proc->argv[0], emsg, status);
- status = translate_winpty_error(status);
}
- winpty_error_free(err);
- winpty_config_free(cfg);
- winpty_spawn_config_free(spawncfg);
- winpty_free(winpty_object);
os_conpty_free(conpty_object);
xfree(in_name);
xfree(out_name);
@@ -233,12 +155,7 @@ const char *pty_process_tty_name(PtyProcess *ptyproc)
void pty_process_resize(PtyProcess *ptyproc, uint16_t width, uint16_t height)
FUNC_ATTR_NONNULL_ALL
{
- if (ptyproc->type == kConpty
- && ptyproc->object.conpty != NULL) {
- os_conpty_set_size(ptyproc->object.conpty, width, height);
- } else if (ptyproc->object.winpty != NULL) {
- winpty_set_size(ptyproc->object.winpty, width, height, NULL);
- }
+ os_conpty_set_size(ptyproc->conpty, width, height);
}
void pty_process_close(PtyProcess *ptyproc)
@@ -255,18 +172,11 @@ void pty_process_close(PtyProcess *ptyproc)
void pty_process_close_master(PtyProcess *ptyproc)
FUNC_ATTR_NONNULL_ALL
-{
- if (ptyproc->type == kWinpty
- && ptyproc->object.winpty != NULL) {
- winpty_free(ptyproc->object.winpty);
- ptyproc->object.winpty = NULL;
- }
-}
+{}
void pty_process_teardown(Loop *loop)
FUNC_ATTR_NONNULL_ALL
-{
-}
+{}
static void pty_process_connect_cb(uv_connect_t *req, int status)
FUNC_ATTR_NONNULL_ALL
@@ -281,7 +191,7 @@ static void wait_eof_timer_cb(uv_timer_t *wait_eof_timer)
PtyProcess *ptyproc = wait_eof_timer->data;
Process *proc = (Process *)ptyproc;
- if (proc->out.closed || !uv_is_readable(proc->out.uvstream)) {
+ if (proc->out.closed || proc->out.did_eof || !uv_is_readable(proc->out.uvstream)) {
uv_timer_stop(&ptyproc->wait_eof_timer);
pty_process_finish2(ptyproc);
}
@@ -308,7 +218,7 @@ static void pty_process_finish2(PtyProcess *ptyproc)
/// Build the command line to pass to CreateProcessW.
///
/// @param[in] argv Array with string arguments.
-/// @param[out] cmd_line Location where saved builded cmd line.
+/// @param[out] cmd_line Location where saved built cmd line.
///
/// @returns zero on success, or error code of MultiByteToWideChar function.
///
@@ -435,40 +345,6 @@ static void quote_cmd_arg(char *dest, size_t dest_remaining, const char *src)
}
}
-/// Translate winpty error code to libuv error.
-///
-/// @param[in] winpty_errno Winpty error code returned by winpty_error_code
-/// function.
-///
-/// @returns Error code of libuv error.
-int translate_winpty_error(int winpty_errno)
-{
- if (winpty_errno <= 0) {
- return winpty_errno; // If < 0 then it's already a libuv error.
- }
-
- switch (winpty_errno) {
- case WINPTY_ERROR_OUT_OF_MEMORY:
- return UV_ENOMEM;
- case WINPTY_ERROR_SPAWN_CREATE_PROCESS_FAILED:
- return UV_EAI_FAIL;
- case WINPTY_ERROR_LOST_CONNECTION:
- return UV_ENOTCONN;
- case WINPTY_ERROR_AGENT_EXE_MISSING:
- return UV_ENOENT;
- case WINPTY_ERROR_UNSPECIFIED:
- return UV_UNKNOWN;
- case WINPTY_ERROR_AGENT_DIED:
- return UV_ESRCH;
- case WINPTY_ERROR_AGENT_TIMEOUT:
- return UV_ETIMEDOUT;
- case WINPTY_ERROR_AGENT_CREATION_FAILED:
- return UV_EAI_FAIL;
- default:
- return UV_UNKNOWN;
- }
-}
-
typedef struct EnvNode {
wchar_t *str;
size_t len;
@@ -485,7 +361,7 @@ static int build_env_block(dict_T *denv, wchar_t **env_block)
{
const size_t denv_size = (size_t)tv_dict_len(denv);
size_t env_block_len = 0;
- int rc;
+ int rc = 0;
char **env = tv_dict_to_env(denv);
QUEUE *q;
diff --git a/src/nvim/os/pty_process_win.h b/src/nvim/os/pty_process_win.h
index d1737fd63a..ed7d765ac7 100644
--- a/src/nvim/os/pty_process_win.h
+++ b/src/nvim/os/pty_process_win.h
@@ -2,25 +2,15 @@
#define NVIM_OS_PTY_PROCESS_WIN_H
#include <uv.h>
-#include <winpty.h>
#include "nvim/event/process.h"
#include "nvim/lib/queue.h"
#include "nvim/os/pty_conpty_win.h"
-typedef enum {
- kWinpty,
- kConpty,
-} PtyType;
-
typedef struct pty_process {
Process process;
uint16_t width, height;
- union {
- winpty_t *winpty;
- conpty_t *conpty;
- } object;
- PtyType type;
+ conpty_t *conpty;
HANDLE finish_wait;
HANDLE process_handle;
uv_timer_t wait_eof_timer;
@@ -38,8 +28,7 @@ static inline PtyProcess pty_process_init(Loop *loop, void *data)
rv.process = process_init(loop, kProcessTypePty, data);
rv.width = 80;
rv.height = 24;
- rv.object.winpty = NULL;
- rv.type = kWinpty;
+ rv.conpty = NULL;
rv.finish_wait = NULL;
rv.process_handle = NULL;
return rv;
diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c
index e618b2788b..9283ea2e42 100644
--- a/src/nvim/os/shell.c
+++ b/src/nvim/os/shell.c
@@ -9,10 +9,10 @@
#include "nvim/ascii.h"
#include "nvim/charset.h"
+#include "nvim/eval.h"
#include "nvim/event/libuv_process.h"
#include "nvim/event/loop.h"
#include "nvim/event/rstream.h"
-#include "nvim/eval.h"
#include "nvim/ex_cmds.h"
#include "nvim/fileio.h"
#include "nvim/lib/kvec.h"
@@ -27,8 +27,8 @@
#include "nvim/path.h"
#include "nvim/screen.h"
#include "nvim/strings.h"
-#include "nvim/types.h"
#include "nvim/tag.h"
+#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
@@ -36,7 +36,7 @@
#define NS_1_SECOND 1000000000U // 1 second, in nanoseconds
#define OUT_DATA_THRESHOLD 1024 * 10U // 10KB, "a few screenfuls" of data.
-#define SHELL_SPECIAL (char_u *)"\t \"&'$;<>()\\|"
+#define SHELL_SPECIAL "\t \"&'$;<>()\\|"
typedef struct {
char *data;
@@ -73,7 +73,7 @@ static bool have_wildcard(int num, char_u **file)
static bool have_dollars(int num, char_u **file)
{
for (int i = 0; i < num; i++) {
- if (vim_strchr(file[i], '$') != NULL) {
+ if (vim_strchr((char *)file[i], '$') != NULL) {
return true;
}
}
@@ -151,7 +151,7 @@ int os_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file
// Don't allow the use of backticks in secure.
if (secure) {
for (i = 0; i < num_pat; i++) {
- if (vim_strchr(pat[i], '`') != NULL
+ if (vim_strchr((char *)pat[i], '`') != NULL
&& (check_secure())) {
return FAIL;
}
@@ -188,7 +188,7 @@ int os_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file
}
}
if (shell_style == STYLE_ECHO
- && strstr((char *)path_tail(p_sh), "sh") != NULL) {
+ && strstr(path_tail((char *)p_sh), "sh") != NULL) {
shell_style = STYLE_VIMGLOB;
}
@@ -405,7 +405,7 @@ int os_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file
while (*p != ' ' && *p != '\n') {
p++;
}
- p = skipwhite(p); // skip to next entry
+ p = (char_u *)skipwhite((char *)p); // skip to next entry
}
// file names are separated with NL
} else if (shell_style == STYLE_BT || shell_style == STYLE_VIMGLOB) {
@@ -418,7 +418,7 @@ int os_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file
if (*p != NUL) {
p++;
}
- p = skipwhite(p); // skip leading white space
+ p = (char_u *)skipwhite((char *)p); // skip leading white space
}
// file names are separated with NUL
} else {
@@ -483,7 +483,7 @@ int os_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file
*p = NUL;
} else {
*p++ = NUL;
- p = skipwhite(p); // skip to next entry
+ p = (char_u *)skipwhite((char *)p); // skip to next entry
}
} else { // NUL separates
while (*p && p < buffer + len) { // skip entry
@@ -644,7 +644,7 @@ int os_call_shell(char_u *cmd, ShellOpts opts, char_u *extra_args)
if (opts & (kShellOptHideMess | kShellOptExpand)) {
forward_output = false;
} else {
- State = EXTERNCMD;
+ State = MODE_EXTERNCMD;
if (opts & kShellOptWrite) {
read_input(&input);
@@ -746,7 +746,7 @@ char_u *get_cmd_output(char_u *cmd, char_u *infile, ShellOpts flags, size_t *ret
}
// Add the redirection stuff
- char_u *command = make_filter_cmd(cmd, infile, tempname);
+ char_u *command = (char_u *)make_filter_cmd((char *)cmd, (char *)infile, (char *)tempname);
// Call the shell to execute the command (errors are ignored).
// Don't check timestamps here.
@@ -854,9 +854,9 @@ static int do_os_system(char **argv, const char *input, size_t len, char **outpu
// Failed, probably 'shell' is not executable.
if (!silent) {
msg_puts(_("\nshell failed to start: "));
- msg_outtrans((char_u *)os_strerror(status));
+ msg_outtrans((char *)os_strerror(status));
msg_puts(": ");
- msg_outtrans((char_u *)prog);
+ msg_outtrans(prog);
msg_putchar('\n');
}
multiqueue_free(events);
@@ -1099,8 +1099,8 @@ static void out_data_append_to_screen(char *output, size_t *count, bool eof)
// incomplete UTF-8 sequence that could be composing with the last
// complete sequence.
// This will be corrected when we switch to vterm based implementation
- int i = *p ? utfc_ptr2len_len((char_u *)p, (int)(end-p)) : 1;
- if (!eof && i == 1 && utf8len_tab_zero[*(uint8_t *)p] > (end-p)) {
+ int i = *p ? utfc_ptr2len_len((char_u *)p, (int)(end - p)) : 1;
+ if (!eof && i == 1 && utf8len_tab_zero[*(uint8_t *)p] > (end - p)) {
*count = (size_t)(p - output);
goto end;
}
@@ -1158,7 +1158,7 @@ static size_t tokenize(const char_u *const str, char **const argv)
}
argc++;
- p = (const char *)skipwhite((char_u *)(p + len));
+ p = (const char *)skipwhite((p + len));
}
return argc;
@@ -1213,7 +1213,7 @@ static void read_input(DynamicBuffer *buf)
dynamic_buffer_ensure(buf, buf->len + len);
buf->data[buf->len++] = NUL;
} else {
- char_u *s = vim_strchr(lp + written, NL);
+ char_u *s = (char_u *)vim_strchr((char *)lp + written, NL);
len = s == NULL ? l : (size_t)(s - (lp + written));
dynamic_buffer_ensure(buf, buf->len + len);
memcpy(buf->data + buf->len, lp + written, len);
@@ -1229,7 +1229,7 @@ static void read_input(DynamicBuffer *buf)
dynamic_buffer_ensure(buf, buf->len + 1);
buf->data[buf->len++] = NL;
}
- ++lnum;
+ lnum++;
if (lnum > curbuf->b_op_end.lnum) {
break;
}
@@ -1253,7 +1253,7 @@ static size_t write_output(char *output, size_t remaining, bool eof)
if (output[off] == NL) {
// Insert the line
output[off] = NUL;
- ml_append(curwin->w_cursor.lnum++, (char_u *)output, (int)off + 1,
+ ml_append(curwin->w_cursor.lnum++, output, (int)off + 1,
false);
size_t skip = off + 1;
output += skip;
@@ -1272,7 +1272,7 @@ static size_t write_output(char *output, size_t remaining, bool eof)
if (eof) {
if (remaining) {
// append unfinished line
- ml_append(curwin->w_cursor.lnum++, (char_u *)output, 0, false);
+ ml_append(curwin->w_cursor.lnum++, output, 0, false);
// remember that the NL was missing
curbuf->b_no_eol_lnum = curwin->w_cursor.lnum;
output += remaining;
@@ -1331,4 +1331,3 @@ static char *shell_xescape_xquote(const char *cmd)
return ncmd;
}
-
diff --git a/src/nvim/os/signal.c b/src/nvim/os/signal.c
index a8bf68a1a2..581f025a0f 100644
--- a/src/nvim/os/signal.c
+++ b/src/nvim/os/signal.c
@@ -21,7 +21,7 @@
#include "nvim/os/signal.h"
#include "nvim/vim.h"
-static SignalWatcher spipe, shup, squit, sterm, susr1;
+static SignalWatcher spipe, shup, squit, sterm, susr1, swinch;
#ifdef SIGPWR
static SignalWatcher spwr;
#endif
@@ -54,6 +54,9 @@ void signal_init(void)
#ifdef SIGUSR1
signal_watcher_init(&main_loop, &susr1, NULL);
#endif
+#ifdef SIGWINCH
+ signal_watcher_init(&main_loop, &swinch, NULL);
+#endif
signal_start();
}
@@ -70,6 +73,9 @@ void signal_teardown(void)
#ifdef SIGUSR1
signal_watcher_close(&susr1, NULL);
#endif
+#ifdef SIGWINCH
+ signal_watcher_close(&swinch, NULL);
+#endif
}
void signal_start(void)
@@ -88,6 +94,9 @@ void signal_start(void)
#ifdef SIGUSR1
signal_watcher_start(&susr1, on_signal, SIGUSR1);
#endif
+#ifdef SIGWINCH
+ signal_watcher_start(&swinch, on_signal, SIGWINCH);
+#endif
}
void signal_stop(void)
@@ -106,6 +115,9 @@ void signal_stop(void)
#ifdef SIGUSR1
signal_watcher_stop(&susr1);
#endif
+#ifdef SIGWINCH
+ signal_watcher_stop(&swinch);
+#endif
}
void signal_reject_deadly(void)
@@ -141,6 +153,10 @@ static char *signal_name(int signum)
case SIGUSR1:
return "SIGUSR1";
#endif
+#ifdef SIGWINCH
+ case SIGWINCH:
+ return "SIGWINCH";
+#endif
default:
return "Unknown";
}
@@ -149,15 +165,15 @@ static char *signal_name(int signum)
// This function handles deadly signals.
// It tries to preserve any swap files and exit properly.
// (partly from Elvis).
-// NOTE: Avoid unsafe functions, such as allocating memory, they can result in
-// a deadlock.
+// NOTE: this is scheduled on the event loop, not called directly from a signal handler.
static void deadly_signal(int signum)
+ FUNC_ATTR_NORETURN
{
// Set the v:dying variable.
set_vim_var_nr(VV_DYING, 1);
v_dying = 1;
- WLOG("got signal %d (%s)", signum, signal_name(signum));
+ ILOG("got signal %d (%s)", signum, signal_name(signum));
snprintf((char *)IObuff, sizeof(IObuff), "Vim: Caught deadly signal '%s'\r\n",
signal_name(signum));
@@ -193,8 +209,12 @@ static void on_signal(SignalWatcher *handle, int signum, void *data)
break;
#ifdef SIGUSR1
case SIGUSR1:
- apply_autocmds(EVENT_SIGNAL, (char_u *)"SIGUSR1", curbuf->b_fname, true,
- curbuf);
+ apply_autocmds(EVENT_SIGNAL, "SIGUSR1", curbuf->b_fname, true, curbuf);
+ break;
+#endif
+#ifdef SIGWINCH
+ case SIGWINCH:
+ apply_autocmds(EVENT_SIGNAL, "SIGWINCH", curbuf->b_fname, true, curbuf);
break;
#endif
default:
diff --git a/src/nvim/os/stdpaths.c b/src/nvim/os/stdpaths.c
index 5b824d23f4..59d315d44c 100644
--- a/src/nvim/os/stdpaths.c
+++ b/src/nvim/os/stdpaths.c
@@ -4,6 +4,7 @@
#include <stdbool.h>
#include "nvim/ascii.h"
+#include "nvim/fileio.h"
#include "nvim/memory.h"
#include "nvim/os/os.h"
#include "nvim/os/stdpaths_defs.h"
@@ -14,6 +15,7 @@ static const char *xdg_env_vars[] = {
[kXDGConfigHome] = "XDG_CONFIG_HOME",
[kXDGDataHome] = "XDG_DATA_HOME",
[kXDGCacheHome] = "XDG_CACHE_HOME",
+ [kXDGStateHome] = "XDG_STATE_HOME",
[kXDGRuntimeDir] = "XDG_RUNTIME_DIR",
[kXDGConfigDirs] = "XDG_CONFIG_DIRS",
[kXDGDataDirs] = "XDG_DATA_DIRS",
@@ -24,7 +26,8 @@ static const char *const xdg_defaults_env_vars[] = {
[kXDGConfigHome] = "LOCALAPPDATA",
[kXDGDataHome] = "LOCALAPPDATA",
[kXDGCacheHome] = "TEMP",
- [kXDGRuntimeDir] = NULL,
+ [kXDGStateHome] = "LOCALAPPDATA",
+ [kXDGRuntimeDir] = NULL, // Decided by vim_mktempdir().
[kXDGConfigDirs] = NULL,
[kXDGDataDirs] = NULL,
};
@@ -38,14 +41,16 @@ static const char *const xdg_defaults[] = {
[kXDGConfigHome] = "~\\AppData\\Local",
[kXDGDataHome] = "~\\AppData\\Local",
[kXDGCacheHome] = "~\\AppData\\Local\\Temp",
- [kXDGRuntimeDir] = NULL,
+ [kXDGStateHome] = "~\\AppData\\Local",
+ [kXDGRuntimeDir] = NULL, // Decided by vim_mktempdir().
[kXDGConfigDirs] = NULL,
[kXDGDataDirs] = NULL,
#else
[kXDGConfigHome] = "~/.config",
[kXDGDataHome] = "~/.local/share",
[kXDGCacheHome] = "~/.cache",
- [kXDGRuntimeDir] = NULL,
+ [kXDGStateHome] = "~/.local/state",
+ [kXDGRuntimeDir] = NULL, // Decided by vim_mktempdir().
[kXDGConfigDirs] = "/etc/xdg/",
[kXDGDataDirs] = "/usr/local/share/:/usr/share/",
#endif
@@ -78,7 +83,12 @@ char *stdpaths_get_xdg_var(const XDGVarType idx)
if (env_val != NULL) {
ret = xstrdup(env_val);
} else if (fallback) {
- ret = (char *)expand_env_save((char_u *)fallback);
+ ret = expand_env_save((char *)fallback);
+ } else if (idx == kXDGRuntimeDir) {
+ // Special-case: stdpath('run') is defined at startup.
+ ret = vim_gettempdir();
+ size_t len = strlen(ret);
+ ret = xstrndup(ret, len >= 2 ? len - 1 : 0); // Trim trailing slash.
}
return ret;
@@ -99,11 +109,16 @@ char *get_xdg_home(const XDGVarType idx)
if (dir) {
#if defined(WIN32)
dir = concat_fnames_realloc(dir,
- (idx == kXDGDataHome ? "nvim-data" : "nvim"),
+ ((idx == kXDGDataHome
+ || idx == kXDGStateHome) ? "nvim-data" : "nvim"),
true);
#else
dir = concat_fnames_realloc(dir, "nvim", true);
#endif
+
+#ifdef BACKSLASH_IN_FILENAME
+ slash_adjust((char_u *)dir);
+#endif
}
return dir;
}
@@ -133,15 +148,26 @@ char *stdpaths_user_conf_subpath(const char *fname)
/// Return subpath of $XDG_DATA_HOME
///
/// @param[in] fname New component of the path.
+///
+/// @return [allocated] `$XDG_DATA_HOME/nvim/{fname}`
+char *stdpaths_user_data_subpath(const char *fname)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET
+{
+ return concat_fnames_realloc(get_xdg_home(kXDGDataHome), fname, true);
+}
+
+/// Return subpath of $XDG_STATE_HOME
+///
+/// @param[in] fname New component of the path.
/// @param[in] trailing_pathseps Amount of trailing path separators to add.
/// @param[in] escape_commas If true, all commas will be escaped.
///
-/// @return [allocated] `$XDG_DATA_HOME/nvim/{fname}`.
-char *stdpaths_user_data_subpath(const char *fname, const size_t trailing_pathseps,
- const bool escape_commas)
+/// @return [allocated] `$XDG_STATE_HOME/nvim/{fname}`.
+char *stdpaths_user_state_subpath(const char *fname, const size_t trailing_pathseps,
+ const bool escape_commas)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET
{
- char *ret = concat_fnames_realloc(get_xdg_home(kXDGDataHome), fname, true);
+ char *ret = concat_fnames_realloc(get_xdg_home(kXDGStateHome), fname, true);
const size_t len = strlen(ret);
const size_t numcommas = (escape_commas ? memcnt(ret, ',', len) : 0);
if (numcommas || trailing_pathseps) {
diff --git a/src/nvim/os/stdpaths_defs.h b/src/nvim/os/stdpaths_defs.h
index 44c30df373..f94c511fe7 100644
--- a/src/nvim/os/stdpaths_defs.h
+++ b/src/nvim/os/stdpaths_defs.h
@@ -7,6 +7,7 @@ typedef enum {
kXDGConfigHome, ///< XDG_CONFIG_HOME
kXDGDataHome, ///< XDG_DATA_HOME
kXDGCacheHome, ///< XDG_CACHE_HOME
+ kXDGStateHome, ///< XDG_STATE_HOME
kXDGRuntimeDir, ///< XDG_RUNTIME_DIR
kXDGConfigDirs, ///< XDG_CONFIG_DIRS
kXDGDataDirs, ///< XDG_DATA_DIRS
diff --git a/src/nvim/os/time.c b/src/nvim/os/time.c
index d9f4fe9e37..396bf6986a 100644
--- a/src/nvim/os/time.c
+++ b/src/nvim/os/time.c
@@ -15,7 +15,6 @@
static uv_mutex_t delay_mutex;
static uv_cond_t delay_cond;
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "os/time.c.generated.h"
#endif
diff --git a/src/nvim/os/users.c b/src/nvim/os/users.c
index e0ce3fec31..bd34e917b2 100644
--- a/src/nvim/os/users.c
+++ b/src/nvim/os/users.c
@@ -30,7 +30,7 @@ static void add_user(garray_T *users, char *user, bool need_copy)
if (user_copy == NULL || *user_copy == NUL) {
if (need_copy) {
- xfree(user);
+ xfree(user_copy);
}
return;
}
@@ -112,9 +112,13 @@ int os_get_usernames(garray_T *users)
return OK;
}
-// Insert user name in s[len].
-// Return OK if a name found.
-int os_get_user_name(char *s, size_t len)
+/// Gets the username that owns the current Nvim process.
+///
+/// @param s[out] Username.
+/// @param len Length of `s`.
+///
+/// @return OK if a name found.
+int os_get_username(char *s, size_t len)
{
#ifdef UNIX
return os_get_uname((uv_uid_t)getuid(), s, len);
@@ -124,9 +128,13 @@ int os_get_user_name(char *s, size_t len)
#endif
}
-// Insert user name for "uid" in s[len].
-// Return OK if a name found.
-// If the name is not found, write the uid into s[len] and return FAIL.
+/// Gets the username associated with `uid`.
+///
+/// @param uid User id.
+/// @param s[out] Username, or `uid` on failure.
+/// @param len Length of `s`.
+///
+/// @return OK if a username was found, else FAIL.
int os_get_uname(uv_uid_t uid, char *s, size_t len)
{
#if defined(HAVE_PWD_H) && defined(HAVE_GETPWUID)
@@ -142,10 +150,10 @@ int os_get_uname(uv_uid_t uid, char *s, size_t len)
return FAIL; // a number is not a name
}
-// Returns the user directory for the given username.
-// The caller has to free() the returned string.
-// If the username is not found, NULL is returned.
-char *os_get_user_directory(const char *name)
+/// Gets the user directory for the given username, or NULL on failure.
+///
+/// Caller must free() the returned string.
+char *os_get_userdir(const char *name)
{
#if defined(HAVE_GETPWNAM) && defined(HAVE_PWD_H)
if (name == NULL || *name == NUL) {
@@ -160,7 +168,6 @@ char *os_get_user_directory(const char *name)
return NULL;
}
-
#if defined(EXITFREE)
void free_users(void)
@@ -187,11 +194,11 @@ static void init_users(void)
}
/// Given to ExpandGeneric() to obtain an user names.
-char_u *get_users(expand_T *xp, int idx)
+char *get_users(expand_T *xp, int idx)
{
init_users();
if (idx < ga_users.ga_len) {
- return ((char_u **)ga_users.ga_data)[idx];
+ return ((char **)ga_users.ga_data)[idx];
}
return NULL;
}
diff --git a/src/nvim/os/win_defs.h b/src/nvim/os/win_defs.h
index efef77be7b..1ae86d6bbe 100644
--- a/src/nvim/os/win_defs.h
+++ b/src/nvim/os/win_defs.h
@@ -36,7 +36,7 @@
// Windows defines a RGB macro that produces 0x00bbggrr color values for use
// with GDI. Our macro is different, and we don't use GDI.
// Duplicated from macros.h to avoid include-order sensitivity.
-#define RGB_(r, g, b) ((r << 16) | (g << 8) | b)
+#define RGB_(r, g, b) (((r) << 16) | ((g) << 8) | (b))
#ifdef _MSC_VER
# ifndef inline
diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c
index 1398dba0e4..473bf5072c 100644
--- a/src/nvim/os_unix.c
+++ b/src/nvim/os_unix.c
@@ -48,7 +48,6 @@
# include <sys/access.h>
# endif
-
// Return a pointer to the ACL of file "fname" in allocated memory.
// Return NULL if the ACL is not available for whatever reason.
vim_acl_T mch_get_acl(const char_u *fname)
diff --git a/src/nvim/path.c b/src/nvim/path.c
index 674d67e21a..b22c0a18bd 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -52,28 +52,28 @@
/// @param checkname When both files don't exist, only compare their names.
/// @param expandenv Whether to expand environment variables in file names.
/// @return Enum of type FileComparison. @see FileComparison.
-FileComparison path_full_compare(char_u *const s1, char_u *const s2, const bool checkname,
+FileComparison path_full_compare(char *const s1, char *const s2, const bool checkname,
const bool expandenv)
{
assert(s1 && s2);
- char_u exp1[MAXPATHL];
- char_u full1[MAXPATHL];
- char_u full2[MAXPATHL];
+ char exp1[MAXPATHL];
+ char full1[MAXPATHL];
+ char full2[MAXPATHL];
FileID file_id_1, file_id_2;
if (expandenv) {
- expand_env(s1, exp1, MAXPATHL);
+ expand_env((char_u *)s1, (char_u *)exp1, MAXPATHL);
} else {
STRLCPY(exp1, s1, MAXPATHL);
}
- bool id_ok_1 = os_fileid((char *)exp1, &file_id_1);
- bool id_ok_2 = os_fileid((char *)s2, &file_id_2);
+ bool id_ok_1 = os_fileid(exp1, &file_id_1);
+ bool id_ok_2 = os_fileid(s2, &file_id_2);
if (!id_ok_1 && !id_ok_2) {
// If os_fileid() doesn't work, may compare the names.
if (checkname) {
- vim_FullName((char *)exp1, (char *)full1, MAXPATHL, FALSE);
- vim_FullName((char *)s2, (char *)full2, MAXPATHL, FALSE);
- if (fnamecmp(full1, full2) == 0) {
+ vim_FullName(exp1, full1, MAXPATHL, false);
+ vim_FullName(s2, full2, MAXPATHL, false);
+ if (FNAMECMP(full1, full2) == 0) {
return kEqualFileNames;
}
}
@@ -88,19 +88,24 @@ FileComparison path_full_compare(char_u *const s1, char_u *const s2, const bool
return kDifferentFiles;
}
-/// Gets the tail (i.e., the filename segment) of a path `fname`.
+/// Gets the tail (filename segment) of path `fname`.
+///
+/// Examples:
+/// - "dir/file.txt" => "file.txt"
+/// - "file.txt" => "file.txt"
+/// - "dir/" => ""
///
/// @return pointer just past the last path separator (empty string, if fname
/// ends in a slash), or empty string if fname is NULL.
-char_u *path_tail(const char_u *fname)
+char *path_tail(const char *fname)
FUNC_ATTR_NONNULL_RET
{
if (fname == NULL) {
- return (char_u *)"";
+ return "";
}
- const char_u *tail = get_past_head(fname);
- const char_u *p = tail;
+ const char *tail = (char *)get_past_head((char_u *)fname);
+ const char *p = tail;
// Find last part of path.
while (*p != NUL) {
if (vim_ispathsep_nocolon(*p)) {
@@ -108,7 +113,7 @@ char_u *path_tail(const char_u *fname)
}
MB_PTR_ADV(p);
}
- return (char_u *)tail;
+ return (char *)tail;
}
/// Get pointer to tail of "fname", including path separators.
@@ -120,14 +125,14 @@ char_u *path_tail(const char_u *fname)
/// - Pointer to the last path separator of `fname`, if there is any.
/// - `fname` if it contains no path separator.
/// - Never NULL.
-char_u *path_tail_with_sep(char_u *fname)
+char *path_tail_with_sep(char *fname)
{
assert(fname != NULL);
// Don't remove the '/' from "c:/file".
- char_u *past_head = get_past_head(fname);
- char_u *tail = path_tail(fname);
- while (tail > past_head && after_pathsep((char *)fname, (char *)tail)) {
+ char *past_head = (char *)get_past_head((char_u *)fname);
+ char *tail = path_tail(fname);
+ while (tail > past_head && after_pathsep(fname, tail)) {
tail--;
}
return tail;
@@ -269,16 +274,17 @@ int vim_ispathlistsep(int c)
#endif
}
-/*
- * Shorten the path of a file from "~/foo/../.bar/fname" to "~/f/../.b/fname"
- * It's done in-place.
- */
-char_u *shorten_dir(char_u *str)
+/// Shorten the path of a file from "~/foo/../.bar/fname" to "~/f/../.b/fname"
+/// "trim_len" specifies how many characters to keep for each directory.
+/// Must be 1 or more.
+/// It's done in-place.
+void shorten_dir_len(char_u *str, int trim_len)
{
- char_u *tail = path_tail(str);
+ char_u *tail = (char_u *)path_tail((char *)str);
char_u *d = str;
bool skip = false;
- for (char_u *s = str;; ++s) {
+ int dirchunk_len = 0;
+ for (char_u *s = str;; s++) {
if (s >= tail) { // copy the whole tail
*d++ = *s;
if (*s == NUL) {
@@ -287,18 +293,30 @@ char_u *shorten_dir(char_u *str)
} else if (vim_ispathsep(*s)) { // copy '/' and next char
*d++ = *s;
skip = false;
+ dirchunk_len = 0;
} else if (!skip) {
*d++ = *s; // copy next char
if (*s != '~' && *s != '.') { // and leading "~" and "."
- skip = true;
+ dirchunk_len++; // only count word chars for the size
+ // keep copying chars until we have our preferred length (or
+ // until the above if/else branches move us along)
+ if (dirchunk_len >= trim_len) {
+ skip = true;
+ }
}
- int l = utfc_ptr2len(s);
+ int l = utfc_ptr2len((char *)s);
while (--l > 0) {
*d++ = *++s;
}
}
}
- return str;
+}
+
+/// Shorten the path of a file from "~/foo/../.bar/fname" to "~/f/../.b/fname"
+/// It's done in-place.
+void shorten_dir(char_u *str)
+{
+ shorten_dir_len(str, 1);
}
/*
@@ -308,11 +326,11 @@ char_u *shorten_dir(char_u *str)
*/
bool dir_of_file_exists(char_u *fname)
{
- char_u *p = path_tail_with_sep(fname);
- if (p == fname) {
+ char *p = path_tail_with_sep((char *)fname);
+ if ((char_u *)p == fname) {
return true;
}
- char_u c = *p;
+ char c = *p;
*p = NUL;
bool retval = os_isdir(fname);
*p = c;
@@ -490,7 +508,7 @@ char *save_abs_path(const char *name)
if (!path_is_absolute((char_u *)name)) {
return FullName_save(name, true);
}
- return (char *)vim_strsave((char_u *)name);
+ return xstrdup(name);
}
/// Checks if a path has a wildcard character including '~', unless at the end.
@@ -512,7 +530,7 @@ bool path_has_wildcard(const char_u *p)
// Windows:
const char *wildcards = "?*$[`";
#endif
- if (vim_strchr((char_u *)wildcards, *p) != NULL
+ if (vim_strchr(wildcards, *p) != NULL
|| (p[0] == '~' && p[1] != NUL)) {
return true;
}
@@ -546,7 +564,7 @@ bool path_has_exp_wildcard(const char_u *p)
#else
const char *wildcards = "*?["; // Windows.
#endif
- if (vim_strchr((char_u *)wildcards, *p) != NULL) {
+ if (vim_strchr(wildcards, *p) != NULL) {
return true;
}
}
@@ -627,15 +645,14 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, size_t wildoff,
}
s = p + 1;
} else if (path_end >= path + wildoff
- && (vim_strchr((char_u *)"*?[{~$", *path_end) != NULL
+ && (vim_strchr("*?[{~$", *path_end) != NULL
#ifndef WIN32
- || (!p_fic && (flags & EW_ICASE)
- && isalpha(utf_ptr2char(path_end)))
+ || (!p_fic && (flags & EW_ICASE) && mb_isalpha(utf_ptr2char((char *)path_end)))
#endif
)) {
e = p;
}
- len = (size_t)(utfc_ptr2len(path_end));
+ len = (size_t)(utfc_ptr2len((char *)path_end));
memcpy(p, path_end, len);
p += len;
path_end += len;
@@ -663,7 +680,7 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, size_t wildoff,
// convert the file pattern to a regexp pattern
int starts_with_dot = *s == '.';
- char_u *pat = file_pat_to_reg_pat(s, e, NULL, false);
+ char *pat = file_pat_to_reg_pat((char *)s, (char *)e, NULL, false);
if (pat == NULL) {
xfree(buf);
return 0;
@@ -714,9 +731,9 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, size_t wildoff,
|| ((flags & EW_DODOT)
&& name[1] != NUL
&& (name[1] != '.' || name[2] != NUL))) // -V557
- && ((regmatch.regprog != NULL && vim_regexec(&regmatch, name, 0))
+ && ((regmatch.regprog != NULL && vim_regexec(&regmatch, (char *)name, 0))
|| ((flags & EW_NOTWILD)
- && fnamencmp(path + (s - buf), name, e - s) == 0))) {
+ && FNAMENCMP(path + (s - buf), name, e - s) == 0))) {
STRCPY(s, name);
len = STRLEN(buf);
@@ -805,7 +822,7 @@ static bool is_unique(char_u *maybe_unique, garray_T *gap, int i)
continue; // it's different when it's shorter
}
char_u *rival = other_paths[j] + other_path_len - candidate_len;
- if (fnamecmp(maybe_unique, rival) == 0
+ if (FNAMECMP(maybe_unique, rival) == 0
&& (rival == other_paths[j] || vim_ispathsep(*(rival - 1)))) {
return false; // match
}
@@ -828,7 +845,7 @@ static void expand_path_option(char_u *curdir, garray_T *gap)
char_u *buf = xmalloc(MAXPATHL);
while (*path_option != NUL) {
- copy_option_part(&path_option, buf, MAXPATHL, " ,");
+ copy_option_part((char **)&path_option, (char *)buf, MAXPATHL, " ,");
if (buf[0] == '.' && (buf[1] == NUL || vim_ispathsep(buf[1]))) {
/* Relative to current buffer:
@@ -837,8 +854,8 @@ static void expand_path_option(char_u *curdir, garray_T *gap)
if (curbuf->b_ffname == NULL) {
continue;
}
- char_u *p = path_tail(curbuf->b_ffname);
- size_t len = (size_t)(p - curbuf->b_ffname);
+ char_u *p = (char_u *)path_tail(curbuf->b_ffname);
+ size_t len = (size_t)(p - (char_u *)curbuf->b_ffname);
if (len + STRLEN(buf) >= MAXPATHL) {
continue;
}
@@ -937,13 +954,13 @@ static void uniquefy_paths(garray_T *gap, char_u *pattern)
file_pattern[0] = '*';
file_pattern[1] = NUL;
STRCAT(file_pattern, pattern);
- char_u *pat = file_pat_to_reg_pat(file_pattern, NULL, NULL, true);
+ char *pat = file_pat_to_reg_pat((char *)file_pattern, NULL, NULL, true);
xfree(file_pattern);
if (pat == NULL) {
return;
}
- regmatch.rm_ic = TRUE; // always ignore case
+ regmatch.rm_ic = true; // always ignore case
regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
xfree(pat);
if (regmatch.regprog == NULL) {
@@ -964,7 +981,7 @@ static void uniquefy_paths(garray_T *gap, char_u *pattern)
char_u *path_cutoff;
len = STRLEN(path);
- is_in_curdir = fnamencmp(curdir, path, dir_end - path) == 0
+ is_in_curdir = FNAMENCMP(curdir, path, dir_end - path) == 0
&& curdir[dir_end - path] == NUL;
if (is_in_curdir) {
in_curdir[i] = vim_strsave(path);
@@ -979,7 +996,7 @@ static void uniquefy_paths(garray_T *gap, char_u *pattern)
if (pattern[0] == '*' && pattern[1] == '*'
&& vim_ispathsep_nocolon(pattern[2])
&& path_cutoff != NULL
- && vim_regexec(&regmatch, path_cutoff, (colnr_T)0)
+ && vim_regexec(&regmatch, (char *)path_cutoff, (colnr_T)0)
&& is_unique(path_cutoff, gap, i)) {
sort_again = true;
memmove(path, path_cutoff, STRLEN(path_cutoff) + 1);
@@ -988,7 +1005,7 @@ static void uniquefy_paths(garray_T *gap, char_u *pattern)
// unique path. We start at the end of the path. */
pathsep_p = path + len - 1;
while (find_previous_pathsep(path, &pathsep_p)) {
- if (vim_regexec(&regmatch, pathsep_p + 1, (colnr_T)0)
+ if (vim_regexec(&regmatch, (char *)pathsep_p + 1, (colnr_T)0)
&& is_unique(pathsep_p + 1, gap, i)
&& path_cutoff != NULL && pathsep_p + 1 >= path_cutoff) {
sort_again = true;
@@ -1095,7 +1112,6 @@ const char *gettail_dir(const char *const fname)
return dir_end;
}
-
/// Calls globpath() with 'path' values for the given pattern and stores the
/// result in "gap".
/// Returns the total number of matches.
@@ -1131,7 +1147,6 @@ static int expand_in_path(garray_T *const gap, char_u *const pattern, const int
return gap->ga_len;
}
-
/*
* Return TRUE if "p" contains what looks like an environment variable.
* Allowing for escaping.
@@ -1141,7 +1156,7 @@ static bool has_env_var(char_u *p)
for (; *p; MB_PTR_ADV(p)) {
if (*p == '\\' && p[1] != NUL) {
p++;
- } else if (vim_strchr((char_u *)"$", *p) != NULL) {
+ } else if (vim_strchr("$", *p) != NULL) {
return true;
}
}
@@ -1162,13 +1177,13 @@ static bool has_special_wildchar(char_u *p)
// Allow for escaping.
if (*p == '\\' && p[1] != NUL && p[1] != '\r' && p[1] != '\n') {
p++;
- } else if (vim_strchr((char_u *)SPECIAL_WILDCHAR, *p) != NULL) {
+ } else if (vim_strchr(SPECIAL_WILDCHAR, *p) != NULL) {
// A { must be followed by a matching }.
- if (*p == '{' && vim_strchr(p, '}') == NULL) {
+ if (*p == '{' && vim_strchr((char *)p, '}') == NULL) {
continue;
}
// A quote and backtick must be followed by another one.
- if ((*p == '`' || *p == '\'') && vim_strchr(p, *p) == NULL) {
+ if ((*p == '`' || *p == '\'') && vim_strchr((char *)p, *p) == NULL) {
continue;
}
return true;
@@ -1363,17 +1378,18 @@ static int vim_backtick(char_u *p)
/// @param flags EW_* flags
static int expand_backtick(garray_T *gap, char_u *pat, int flags)
{
- char_u *p;
- char_u *buffer;
+ char *p;
+ char *buffer;
int cnt = 0;
// Create the command: lop off the backticks.
- char_u *cmd = vim_strnsave(pat + 1, STRLEN(pat) - 2);
+ char *cmd = (char *)vim_strnsave(pat + 1, STRLEN(pat) - 2);
if (*cmd == '=') { // `={expr}`: Expand expression
buffer = eval_to_string(cmd + 1, &p, true);
} else {
- buffer = get_cmd_output(cmd, NULL, (flags & EW_SILENT) ? kShellOptSilent : 0, NULL);
+ buffer = (char *)get_cmd_output((char_u *)cmd, NULL, (flags & EW_SILENT) ? kShellOptSilent : 0,
+ NULL);
}
xfree(cmd);
if (buffer == NULL) {
@@ -1389,9 +1405,9 @@ static int expand_backtick(garray_T *gap, char_u *pat, int flags)
}
// add an entry if it is not empty
if (p > cmd) {
- char_u i = *p;
+ char i = *p;
*p = NUL;
- addfile(gap, cmd, flags);
+ addfile(gap, (char_u *)cmd, flags);
*p = i;
++cnt;
}
@@ -1461,7 +1477,7 @@ void addfile(garray_T *gap, char_u *f, int flags)
#ifdef FNAME_ILLEGAL
// if the file/dir contains illegal characters, don't add it
- if (vim_strpbrk(f, (char_u *)FNAME_ILLEGAL) != NULL) {
+ if (strpbrk((char *)f, FNAME_ILLEGAL) != NULL) {
return;
}
#endif
@@ -1508,7 +1524,7 @@ void simplify_filename(char_u *filename)
p = filename;
#ifdef BACKSLASH_IN_FILENAME
- if (p[1] == ':') { // skip "x:"
+ if (p[0] != NUL && p[1] == ':') { // skip "x:"
p += 2;
}
#endif
@@ -1516,9 +1532,8 @@ void simplify_filename(char_u *filename)
if (vim_ispathsep(*p)) {
relative = false;
do {
- ++p;
- }
- while (vim_ispathsep(*p));
+ p++;
+ } while (vim_ispathsep(*p));
}
start = p; // remember start after "c:/" or "/" or "///"
@@ -1667,8 +1682,8 @@ void simplify_filename(char_u *filename)
static char *eval_includeexpr(const char *const ptr, const size_t len)
{
set_vim_var_string(VV_FNAME, ptr, (ptrdiff_t)len);
- char *res = (char *)eval_to_string_safe(curbuf->b_p_inex, NULL,
- was_set_insecurely(curwin, "includeexpr", OPT_LOCAL));
+ char *res = eval_to_string_safe((char *)curbuf->b_p_inex, NULL,
+ was_set_insecurely(curwin, "includeexpr", OPT_LOCAL));
set_vim_var_string(VV_FNAME, NULL, 0);
return res;
}
@@ -1682,6 +1697,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 +1762,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 + 1; (isalpha(*p) || (*p == '-')); p++) {}
+
+ // check last char is not a dash
+ if (p[-1] == '-') {
+ return 0;
+ }
+
+ // "://" or ":\\" must follow
return path_is_url(p);
}
@@ -1840,7 +1877,7 @@ char *fix_fname(const char *fname)
fname = xstrdup(fname);
# ifdef USE_FNAME_CASE
- path_fix_case((char_u *)fname); // set correct case for file name
+ path_fix_case(fname); // set correct case for file name
# endif
return (char *)fname;
@@ -1852,17 +1889,17 @@ char *fix_fname(const char *fname)
/// Only required for file systems where case is ignored and preserved.
// TODO(SplinterOfChaos): Could also be used when mounting case-insensitive
// file systems.
-void path_fix_case(char_u *name)
+void path_fix_case(char *name)
FUNC_ATTR_NONNULL_ALL
{
FileInfo file_info;
- if (!os_fileinfo_link((char *)name, &file_info)) {
+ if (!os_fileinfo_link(name, &file_info)) {
return;
}
// Open the directory where the file is located.
- char_u *slash = STRRCHR(name, '/');
- char_u *tail;
+ char *slash = (char *)STRRCHR(name, '/');
+ char *tail;
Directory dir;
bool ok;
if (slash == NULL) {
@@ -1870,7 +1907,7 @@ void path_fix_case(char_u *name)
tail = name;
} else {
*slash = NUL;
- ok = os_scandir(&dir, (char *)name);
+ ok = os_scandir(&dir, name);
*slash = '/';
tail = slash + 1;
}
@@ -1879,8 +1916,8 @@ void path_fix_case(char_u *name)
return;
}
- char_u *entry;
- while ((entry = (char_u *)os_scandir_next(&dir))) {
+ char *entry;
+ while ((entry = (char *)os_scandir_next(&dir))) {
// Only accept names that differ in case and are the same byte
// length. TODO: accept different length name.
if (STRICMP(tail, entry) == 0 && STRLEN(tail) == STRLEN(entry)) {
@@ -1919,9 +1956,9 @@ int after_pathsep(const char *b, const char *p)
*/
bool same_directory(char_u *f1, char_u *f2)
{
- char_u ffname[MAXPATHL];
- char_u *t1;
- char_u *t2;
+ char ffname[MAXPATHL];
+ char *t1;
+ char *t2;
// safety check
if (f1 == NULL || f2 == NULL) {
@@ -1930,8 +1967,8 @@ bool same_directory(char_u *f1, char_u *f2)
(void)vim_FullName((char *)f1, (char *)ffname, MAXPATHL, FALSE);
t1 = path_tail_with_sep(ffname);
- t2 = path_tail_with_sep(f2);
- return t1 - ffname == t2 - f2
+ t2 = path_tail_with_sep((char *)f2);
+ return t1 - ffname == (char_u *)t2 - f2
&& pathcmp((char *)ffname, (char *)f2, (int)(t1 - ffname)) == 0;
}
@@ -1947,8 +1984,8 @@ int pathcmp(const char *p, const char *q, int maxlen)
const char *s = NULL;
for (i = 0, j = 0; maxlen < 0 || (i < maxlen && j < maxlen);) {
- c1 = utf_ptr2char((char_u *)p + i);
- c2 = utf_ptr2char((char_u *)q + j);
+ c1 = utf_ptr2char(p + i);
+ c2 = utf_ptr2char(q + j);
// End of "p": check if "q" also ends or just has a slash.
if (c1 == NUL) {
@@ -1983,15 +2020,15 @@ int pathcmp(const char *p, const char *q, int maxlen)
: c1 - c2; // no match
}
- i += utfc_ptr2len((char_u *)p + i);
- j += utfc_ptr2len((char_u *)q + j);
+ i += utfc_ptr2len(p + i);
+ j += utfc_ptr2len(q + j);
}
if (s == NULL) { // "i" or "j" ran into "maxlen"
return 0;
}
- c1 = utf_ptr2char((char_u *)s + i);
- c2 = utf_ptr2char((char_u *)s + i + utfc_ptr2len((char_u *)s + i));
+ c1 = utf_ptr2char(s + i);
+ c2 = utf_ptr2char(s + i + utfc_ptr2len(s + i));
// ignore a trailing slash, but not "//" or ":/"
if (c2 == NUL
&& i > 0
@@ -2056,7 +2093,7 @@ char_u *path_shorten_fname(char_u *full_path, char_u *dir_name)
// If full_path and dir_name do not match, it's impossible to make one
// relative to the other.
- if (fnamencmp(dir_name, full_path, len) != 0) {
+ if (FNAMENCMP(dir_name, full_path, len) != 0) {
return NULL;
}
@@ -2209,18 +2246,18 @@ int match_suffix(char_u *fname)
size_t fnamelen = STRLEN(fname);
size_t setsuflen = 0;
for (char_u *setsuf = p_su; *setsuf;) {
- setsuflen = copy_option_part(&setsuf, suf_buf, MAXSUFLEN, ".,");
+ setsuflen = copy_option_part((char **)&setsuf, (char *)suf_buf, MAXSUFLEN, ".,");
if (setsuflen == 0) {
- char_u *tail = path_tail(fname);
+ char_u *tail = (char_u *)path_tail((char *)fname);
// empty entry: match name without a '.'
- if (vim_strchr(tail, '.') == NULL) {
+ if (vim_strchr((char *)tail, '.') == NULL) {
setsuflen = 1;
break;
}
} else {
if (fnamelen >= setsuflen
- && fnamencmp(suf_buf, fname + fnamelen - setsuflen, setsuflen) == 0) {
+ && FNAMENCMP(suf_buf, fname + fnamelen - setsuflen, setsuflen) == 0) {
break;
}
setsuflen = 0;
@@ -2291,7 +2328,7 @@ int append_path(char *path, const char *to_append, size_t max_len)
}
// Combine the path segments, separated by a slash.
- if (current_length > 0 && !vim_ispathsep_nocolon(path[current_length-1])) {
+ if (current_length > 0 && !vim_ispathsep_nocolon(path[current_length - 1])) {
current_length += 1; // Count the trailing slash.
// +1 for the NUL at the end.
@@ -2344,7 +2381,7 @@ static int path_to_absolute(const char_u *fname, char_u *buf, size_t len, int fo
} else {
assert(p >= fname);
memcpy(relative_directory, fname, (size_t)(p - fname));
- relative_directory[p-fname] = NUL;
+ relative_directory[p - fname] = NUL;
}
end_of_path = (char *)(p + 1);
} else {
@@ -2367,9 +2404,11 @@ static int path_to_absolute(const char_u *fname, char_u *buf, size_t len, int fo
int path_is_absolute(const char_u *fname)
{
#ifdef WIN32
+ if (*fname == NUL) {
+ return false;
+ }
// A name like "d:/foo" and "//server/share" is absolute
- return ((isalpha(fname[0]) && fname[1] == ':'
- && vim_ispathsep_nocolon(fname[2]))
+ return ((isalpha(fname[0]) && fname[1] == ':' && vim_ispathsep_nocolon(fname[2]))
|| (vim_ispathsep_nocolon(fname[0]) && fname[0] == fname[1]));
#else
// UNIX: This just checks if the file name starts with '/' or '~'.
diff --git a/src/nvim/plines.c b/src/nvim/plines.c
index a572f747df..70bdbd8b1d 100644
--- a/src/nvim/plines.c
+++ b/src/nvim/plines.c
@@ -45,7 +45,6 @@ int plines_win(win_T *wp, linenr_T lnum, bool winheight)
return plines_win_nofill(wp, lnum, winheight) + win_get_fill(wp, lnum);
}
-
/// Return the number of filler lines above "lnum".
///
/// @param wp
@@ -61,7 +60,7 @@ int win_get_fill(win_T *wp, linenr_T lnum)
int n = diff_check(wp, lnum);
if (n > 0) {
- return virt_lines+n;
+ return virt_lines + n;
}
}
return virt_lines;
@@ -125,7 +124,7 @@ int plines_win_nofold(win_T *wp, linenr_T lnum)
}
col -= (unsigned int)width;
width += win_col_off2(wp);
- assert(col <= INT_MAX && (int)col < INT_MAX - (width -1));
+ assert(col <= INT_MAX && (int)col < INT_MAX - (width - 1));
return ((int)col + (width - 1)) / width + 1;
}
@@ -155,11 +154,11 @@ int plines_win_col(win_T *wp, linenr_T lnum, long column)
}
// If *s is a TAB, and the TAB is not displayed as ^I, and we're not in
- // INSERT mode, then col must be adjusted so that it represents the last
- // screen position of the TAB. This only fixes an error when the TAB wraps
- // from one screen line to the next (when 'columns' is not a multiple of
- // 'ts') -- webb.
- if (*s == TAB && (State & NORMAL)
+ // MODE_INSERT state, then col must be adjusted so that it represents the
+ // last screen position of the TAB. This only fixes an error when the TAB
+ // wraps from one screen line to the next (when 'columns' is not a multiple
+ // of 'ts') -- webb.
+ if (*s == TAB && (State & MODE_NORMAL)
&& (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) {
col += win_lbr_chartabsize(wp, line, s, col, NULL) - 1;
}
@@ -230,7 +229,7 @@ int win_chartabsize(win_T *wp, char_u *p, colnr_T col)
if (*p == TAB && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) {
return tabstop_padding(col, buf->b_p_ts, buf->b_p_vts_array);
} else {
- return ptr2cells(p);
+ return ptr2cells((char *)p);
}
}
@@ -409,7 +408,7 @@ int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *he
// Set *headp to the size of what we add.
added = 0;
- char_u *const sbr = get_showbreak_value(wp);
+ char *const sbr = (char *)get_showbreak_value(wp);
if ((*sbr != NUL || wp->w_p_bri) && wp->w_p_wrap && col != 0) {
colnr_T sbrlen = 0;
int numberwidth = win_col_off(wp);
@@ -424,7 +423,7 @@ int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *he
col %= numberextra;
}
if (*sbr != NUL) {
- sbrlen = (colnr_T)mb_charlen(sbr);
+ sbrlen = (colnr_T)mb_charlen((char_u *)sbr);
if (col >= sbrlen) {
col -= sbrlen;
}
@@ -495,7 +494,7 @@ static int win_nolbr_chartabsize(win_T *wp, char_u *s, colnr_T col, int *headp)
wp->w_buffer->b_p_ts,
wp->w_buffer->b_p_vts_array);
}
- n = ptr2cells(s);
+ n = ptr2cells((char *)s);
// Add one cell for a double-width character in the last column of the
// window, displayed with a ">".
@@ -507,4 +506,3 @@ static int win_nolbr_chartabsize(win_T *wp, char_u *s, colnr_T col, int *headp)
}
return n;
}
-
diff --git a/src/nvim/po/af.po b/src/nvim/po/af.po
index a76dd8eeea..82345f8a46 100644
--- a/src/nvim/po/af.po
+++ b/src/nvim/po/af.po
@@ -34,6 +34,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ISO_8859-1\n"
"Content-Transfer-Encoding: 8-bit\n"
+"Plural-Forms: nplurals=2; plural=n!=1;\n"
#~ msgid "[Location List]"
#~ msgstr ""
@@ -645,11 +646,11 @@ msgstr "&Ok"
#~ msgid "filter() argument"
#~ msgstr ""
-#, fuzzy, c-format
-#~ msgid "+-%s%3ld line: "
-#~ msgid_plural "+-%s%3ld lines: "
-#~ msgstr[0] "+-%s%3ld rels: "
-#~ msgstr[1] "+-%s%3ld rels: "
+#, c-format
+msgid "+-%s%3ld line: "
+msgid_plural "+-%s%3ld lines: "
+msgstr[0] "+-%s%3ld rels: "
+msgstr[1] "+-%s%3ld rels: "
#, fuzzy, c-format
#~ msgid "E700: Unknown function: %s"
diff --git a/src/nvim/po/check.vim b/src/nvim/po/check.vim
index aca878f9d5..7705ba8577 100644
--- a/src/nvim/po/check.vim
+++ b/src/nvim/po/check.vim
@@ -41,7 +41,7 @@ set nowrapscan
" Start at the first "msgid" line.
let wsv = winsaveview()
1
-/^msgid\>
+keeppatterns /^msgid\>
" When an error is detected this is set to the line number.
" Note: this is used in the Makefile.
@@ -104,7 +104,7 @@ while 1
" Find next msgid. Quit when there is no more.
let lnum = line('.')
- silent! /^msgid\>
+ silent! keeppatterns /^msgid\>
if line('.') == lnum
break
endif
@@ -137,7 +137,7 @@ endfunc
" Check that the \n at the end of the msgid line is also present in the msgstr
" line. Skip over the header.
1
-/^"MIME-Version:
+keeppatterns /^"MIME-Version:
while 1
let lnum = search('^msgid\>')
if lnum <= 0
diff --git a/src/nvim/po/cleanup.vim b/src/nvim/po/cleanup.vim
index b27d88092f..8384286b0d 100644
--- a/src/nvim/po/cleanup.vim
+++ b/src/nvim/po/cleanup.vim
@@ -12,6 +12,10 @@ setl nodiff
silent g/^#, c-format\n#/.d
silent g/^#\..*\n#/.d
+" c-format comments have no effect, the check.vim scripts checks it.
+" But they might still be useful?
+" silent g/^#, c-format$/d
+
silent g/^#[:~] /d
silent g/^#, fuzzy\(, .*\)\=\nmsgid ""\@!/.+1,/^$/-1s/^/#\~ /
silent g/^msgstr"/s//msgstr "/
diff --git a/src/nvim/po/de.po b/src/nvim/po/de.po
index 740e9e5f6a..2dde77e9f7 100644
--- a/src/nvim/po/de.po
+++ b/src/nvim/po/de.po
@@ -18,6 +18,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ISO_8859-1\n"
"Content-Transfer-Encoding: 8-bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: ../api/private/helpers.c:197
msgid "Unable to get option value"
@@ -6233,8 +6234,10 @@ msgstr "filter()-Argument"
#: ../eval.c:8717
#, c-format
-msgid "+-%s%3ld lines: "
-msgstr "+-%s%3ld Zeilen: "
+msgid "+-%s%3ld line: "
+msgid_plural "+-%s%3ld lines: "
+msgstr[0] "+-%s%3ld Zeile: "
+msgstr[1] "+-%s%3ld Zeilen: "
#: ../eval.c:8779
#, c-format
diff --git a/src/nvim/po/eo.po b/src/nvim/po/eo.po
index 9b374e91ae..1c503d0a84 100644
--- a/src/nvim/po/eo.po
+++ b/src/nvim/po/eo.po
@@ -25,6 +25,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
msgid "E831: bf_key_init() called with empty password"
msgstr "E831: bf_key_init() alvokita kun malplena pasvorto"
@@ -1932,6 +1933,12 @@ msgstr "E350: Ne eblas krei faldon per la aktuala 'foldmethod'"
msgid "E351: Cannot delete fold with current 'foldmethod'"
msgstr "E351: Ne eblas forviŝi faldon per la aktuala 'foldmethod'"
+#, c-format
+msgid "+-%s%3ld line: "
+msgid_plural "+-%s%3ld lines: "
+msgstr[0] "+-%s%3ld linio: "
+msgstr[1] "+-%s%3ld linioj: "
+
msgid "E222: Add to read buffer"
msgstr "E222: Aldoni al lega bufro"
diff --git a/src/nvim/po/es.po b/src/nvim/po/es.po
index 064484d1a4..adea651b7c 100644
--- a/src/nvim/po/es.po
+++ b/src/nvim/po/es.po
@@ -19,6 +19,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: octect-stream\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: ../api/private/helpers.c:201
#, fuzzy
@@ -786,8 +787,10 @@ msgstr "-c [argumentos]"
#: ../eval.c:9229
#, c-format
-msgid "+-%s%3ld lines: "
-msgstr "+-%s%3ld líneas: "
+msgid "+-%s%3ld line: "
+msgid_plural "+-%s%3ld lines: "
+msgstr[0] "+-%s%3ld línea: "
+msgstr[1] "+-%s%3ld líneas: "
#: ../eval.c:9291
#, c-format
diff --git a/src/nvim/po/fi.po b/src/nvim/po/fi.po
index 77d5f7f826..f10d4ce977 100644
--- a/src/nvim/po/fi.po
+++ b/src/nvim/po/fi.po
@@ -841,9 +841,11 @@ msgstr "map()-argumentti"
msgid "filter() argument"
msgstr "filter()-argumentti"
-#, fuzzy, c-format
-#~ msgid "+-%s%3ld lines: "
-#~ msgstr "+-%s%3ld rivi: "
+#, c-format
+msgid "+-%s%3ld line: "
+msgid_plural "+-%s%3ld lines: "
+msgstr[0] "+--%s%3ld rivi: "
+msgstr[1] "+--%s%3ld riviä: "
#, c-format
msgid "E700: Unknown function: %s"
diff --git a/src/nvim/po/fr.po b/src/nvim/po/fr.po
index 6df7741f1a..614ba013e6 100644
--- a/src/nvim/po/fr.po
+++ b/src/nvim/po/fr.po
@@ -1574,6 +1574,12 @@ msgid_plural "+--%3ld lines folded "
msgstr[0] "+--%3ld ligne dplace "
msgstr[1] "+--%3ld lignes dplaces "
+#, c-format
+msgid "+-%s%3ld line: "
+msgid_plural "+-%s%3ld lines: "
+msgstr[0] "+-%s%3ld ligne : "
+msgstr[1] "+-%s%3ld lignes : "
+
msgid "E222: Add to read buffer"
msgstr "E222: Ajout au tampon de lecture"
diff --git a/src/nvim/po/ga.po b/src/nvim/po/ga.po
index ff16a87dbc..1c25ee481c 100644
--- a/src/nvim/po/ga.po
+++ b/src/nvim/po/ga.po
@@ -7103,13 +7103,14 @@ msgstr ""
"Norbh fhidir an chonair a shocr: n liosta sys.path\n"
"Ba chir duit vim.VIM_SPECIAL_PATH a cheangal le deireadh sys.path"
-#~ msgid "+-%s%3ld line: "
-#~ msgid_plural "+-%s%3ld lines: "
-#~ msgstr[0] "+-%s%3ld lne: "
-#~ msgstr[1] "+-%s%3ld lne: "
-#~ msgstr[2] "+-%s%3ld lne: "
-#~ msgstr[3] "+-%s%3ld lne: "
-#~ msgstr[4] "+-%s%3ld lne: "
+#, c-format
+msgid "+-%s%3ld line: "
+msgid_plural "+-%s%3ld lines: "
+msgstr[0] "+-%s%3ld lne: "
+msgstr[1] "+-%s%3ld lne: "
+msgstr[2] "+-%s%3ld lne: "
+msgstr[3] "+-%s%3ld lne: "
+msgstr[4] "+-%s%3ld lne: "
#~ msgid "+--%3ld line folded "
#~ msgid_plural "+--%3ld lines folded "
diff --git a/src/nvim/po/it.po b/src/nvim/po/it.po
index dfabc4bee0..313280c807 100644
--- a/src/nvim/po/it.po
+++ b/src/nvim/po/it.po
@@ -25,6 +25,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ISO_8859-1\n"
"Content-Transfer-Encoding: 8-bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: ../api/private/helpers.c:201
msgid "Unable to get option value"
diff --git a/src/nvim/po/ja.euc-jp.po b/src/nvim/po/ja.euc-jp.po
index 5dda7c59f5..d7d0faca80 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"
@@ -1846,6 +1837,11 @@ msgid "+--%3ld line folded "
msgid_plural "+--%3ld lines folded "
msgstr[0] "+--%3ld Ԥ޾ޤޤ"
+#, c-format
+msgid "+-%s%3ld line: "
+msgid_plural "+-%s%3ld lines: "
+msgstr[0] "+-%s%3ld : "
+
msgid "E222: Add to read buffer"
msgstr "E222: ɹХåեɲ"
diff --git a/src/nvim/po/ja.po b/src/nvim/po/ja.po
index a169bd3589..b56345e066 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"
@@ -1846,6 +1837,11 @@ msgid "+--%3ld line folded "
msgid_plural "+--%3ld lines folded "
msgstr[0] "+--%3ld 行が折畳まれました"
+#, c-format
+msgid "+-%s%3ld line: "
+msgid_plural "+-%s%3ld lines: "
+msgstr[0] "+-%s%3ld 行: "
+
msgid "E222: Add to read buffer"
msgstr "E222: 読込バッファへ追加"
diff --git a/src/nvim/po/ru.po b/src/nvim/po/ru.po
index 3a96ece2fb..7566036d3e 100644
--- a/src/nvim/po/ru.po
+++ b/src/nvim/po/ru.po
@@ -17,6 +17,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
+"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
#: ../api/private/helpers.c:201
#, fuzzy
@@ -767,8 +769,11 @@ msgstr "параметра filter()"
#: ../eval.c:9229
#, c-format
-msgid "+-%s%3ld lines: "
-msgstr "+-%s%3ld строк: "
+msgid "+-%s%3ld line: "
+msgid_plural "+-%s%3ld lines: "
+msgstr[0] "+-%s%3ld строка: "
+msgstr[1] "+-%s%3ld строки: "
+msgstr[2] "+-%s%3ld строк: "
#: ../eval.c:9291
#, c-format
@@ -959,7 +964,6 @@ msgstr "E129: Требуется имя функции"
#, c-format
msgid "E128: Function name must start with a capital or \"s:\": %s"
msgstr "E128: Имя функции должно начинаться с заглавной буквы или \"s:\": %s"
-"двоеточие: %s"
#: ../eval.c:17833
#, c-format
@@ -1578,7 +1582,7 @@ msgstr "E494: Используйте w или w>>"
#: ../ex_docmd.c:3454
msgid "E319: The command is not available in this version"
-msgstr "E319: Извините, эта команда недоступна в данной версии"
+msgstr "E319: Эта команда недоступна в данной версии"
#: ../ex_docmd.c:3752
msgid "E172: Only one file name allowed"
@@ -2055,7 +2059,7 @@ msgstr "E201: Автокоманды *ReadPre не должны изменять
#: ../fileio.c:672
msgid "Nvim: Reading from stdin...\n"
-msgstr "Vim: Чтение из стандартного потока ввода stdin...\n"
+msgstr "Nvim: Чтение из стандартного потока ввода stdin...\n"
#. Re-opening the original file failed!
#: ../fileio.c:909
@@ -2427,7 +2431,7 @@ msgstr "--Удалено--"
#: ../fileio.c:5732
#, c-format
msgid "auto-removing autocommand: %s <buffer=%d>"
-msgstr "авто-удаление автокоманды: %s <буффер=%d>"
+msgstr "авто-удаление автокоманды: %s <буфер=%d>"
#. the group doesn't exist
#: ../fileio.c:5772
@@ -2666,11 +2670,11 @@ msgstr "E17: \"%s\" является каталогом"
#: ../globals.h:1020
#, fuzzy
msgid "E900: Invalid job id"
-msgstr "E49: Недопустимый размер прокрутки"
+msgstr "E900: Недопустимый идентификатор задания"
#: ../globals.h:1021
msgid "E901: Job table is full"
-msgstr ""
+msgstr "E901: Таблица заданий переполнена"
#: ../globals.h:1024
#, c-format
@@ -2707,9 +2711,7 @@ msgstr "E477: ! не допускается"
#: ../globals.h:1035
msgid "E25: Nvim does not have a built-in GUI"
-msgstr ""
-"E25: Возможность использования графического интерфейса выключена при "
-"компиляции"
+msgstr "E25: У Nvim нет встроенного графического интерфейса"
#: ../globals.h:1036
#, c-format
@@ -3351,10 +3353,10 @@ msgstr "E282: Невозможно выполнить чтение из \"%s\""
#: ../main.c:2149
msgid ""
"\n"
-"More info with: \"vim -h\"\n"
+"More info with: \""
msgstr ""
"\n"
-"Дополнительная информация: \"vim -h\"\n"
+"Дополнительная информация: \""
#: ../main.c:2178
msgid "[file ..] edit specified file(s)"
@@ -4413,7 +4415,7 @@ msgstr "E663: В конце списка изменений"
#: ../normal.c:7053
msgid "Type :quit<Enter> to exit Nvim"
-msgstr "Введите :quit<Enter> для выхода из Vim"
+msgstr "Введите :quit<Enter> для выхода из Nvim"
#: ../ops.c:248
#, c-format
@@ -4521,6 +4523,7 @@ msgid ""
"lines"
msgstr ""
"E883: шаблон поиска и регистр выражения не могут содержать двух или более "
+"строк"
#: ../ops.c:5089
#, c-format
@@ -6137,7 +6140,7 @@ msgstr "Vim: Ошибка чтения ввода, выход...\n"
#: ../undo.c:379
#, fuzzy
msgid "E881: Line count changed unexpectedly"
-msgstr "E834: Неожиданно изменился счётчик строк"
+msgstr "E881: Неожиданно изменился счётчик строк"
#: ../undo.c:627
#, c-format
diff --git a/src/nvim/po/sr.po b/src/nvim/po/sr.po
index d34c1c3100..3c45e1bf80 100644
--- a/src/nvim/po/sr.po
+++ b/src/nvim/po/sr.po
@@ -931,7 +931,8 @@ msgstr "&Ок"
msgid "+-%s%3ld line: "
msgid_plural "+-%s%3ld lines: "
msgstr[0] "+-%s%3ld линија: "
-msgstr[1] "+-%s%3ld линија: "
+msgstr[1] "+-%s%3ld линијe: "
+msgstr[2] "+-%s%3ld линија: "
#, c-format
msgid "E700: Unknown function: %s"
diff --git a/src/nvim/po/uk.po b/src/nvim/po/uk.po
index f0ae154648..da87d50683 100644
--- a/src/nvim/po/uk.po
+++ b/src/nvim/po/uk.po
@@ -14,7 +14,7 @@ msgid ""
msgstr ""
"Project-Id-Version: vim 7.4\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2021-01-18 17:46+0200\n"
+"POT-Creation-Date: 2022-04-13 10:28+0300\n"
"PO-Revision-Date: 2020-08-23 20:19+0300\n"
"Last-Translator: Анатолій Сахнік <sakhnik@gmail.com>\n"
"Language-Team: Ukrainian\n"
@@ -22,6 +22,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n"
+"%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
msgid "--Deleted--"
msgstr "--Знищено--"
@@ -40,18 +42,6 @@ msgstr "E936: Не вдалося знищити цю групу"
msgid "W19: Deleting augroup that is still in use"
msgstr "W19: Знищення автогрупи, яка все ще використовується"
-#, c-format
-msgid "E215: Illegal character after *: %s"
-msgstr "E215: Недозволений символ після *: %s"
-
-#, c-format
-msgid "E216: No such event: %s"
-msgstr "E216: Немає такої події: %s"
-
-#, c-format
-msgid "E216: No such group or event: %s"
-msgstr "E216: Немає такої групи чи події: %s"
-
msgid ""
"\n"
"--- Autocommands ---"
@@ -84,6 +74,18 @@ msgstr "Виконується %s"
msgid "autocommand %s"
msgstr "автокоманда %s"
+#, c-format
+msgid "E215: Illegal character after *: %s"
+msgstr "E215: Недозволений символ після *: %s"
+
+#, c-format
+msgid "E216: No such event: %s"
+msgstr "E216: Немає такої події: %s"
+
+#, c-format
+msgid "E216: No such group or event: %s"
+msgstr "E216: Немає такої групи чи події: %s"
+
msgid "[Location List]"
msgstr "[Список місць]"
@@ -111,27 +113,6 @@ msgstr "E516: Жоден з буферів не знищено"
msgid "E517: No buffers were wiped out"
msgstr "E517: Жоден з буферів не витерто"
-msgid "1 buffer unloaded"
-msgstr "Вивантажено один буфер"
-
-#, c-format
-msgid "%d buffers unloaded"
-msgstr "Вивантажено %d буфери(ів)"
-
-msgid "1 buffer deleted"
-msgstr "Знищено один буфер"
-
-#, c-format
-msgid "%d buffers deleted"
-msgstr "Знищено %d буфери(ів)"
-
-msgid "1 buffer wiped out"
-msgstr "Витерто один буфер"
-
-#, c-format
-msgid "%d buffers wiped out"
-msgstr "Витерто %d буфери(ів)"
-
msgid "E90: Cannot unload last buffer"
msgstr "E90: Не можу вивантажити останній буфер"
@@ -148,14 +129,14 @@ msgid "E88: Cannot go before first buffer"
msgstr "E88: Це вже найперший буфер"
#, c-format
-msgid "E89: %s will be killed (add ! to override)"
-msgstr "E89: «%s» буде вбито (! щоб не зважати)"
-
-#, c-format
msgid ""
"E89: No write since last change for buffer %<PRId64> (add ! to override)"
msgstr "E89: Буфер %<PRId64> має зміни (! щоб не зважати)"
+#, c-format
+msgid "E89: %s will be killed (add ! to override)"
+msgstr "E89: «%s» буде вбито (! щоб не зважати)"
+
msgid "E948: Job still running (add ! to end the job)"
msgstr "E948: Задача все ще виконується (! щоб закінчити)"
@@ -206,14 +187,6 @@ msgid "[readonly]"
msgstr "[лише читати]"
#, c-format
-msgid "1 line --%d%%--"
-msgstr "один рядок --%d%%--"
-
-#, c-format
-msgid "%<PRId64> lines --%d%%--"
-msgstr "%<PRId64> рядки(ів) --%d%%--"
-
-#, c-format
msgid "line %<PRId64> of %<PRId64> --%d%%-- col "
msgstr "рядок %<PRId64> з %<PRId64> --%d%%-- колонка "
@@ -277,6 +250,51 @@ msgstr "E548: Потрібна цифра"
msgid "E549: Illegal percentage"
msgstr "E549: Неправильний відсоток"
+msgid "Entering Debug mode. Type \"cont\" to continue."
+msgstr "Режим налагодження. Щоб продовжити введіть «cont»."
+
+#, c-format
+msgid "Oldval = \"%s\""
+msgstr "Oldval = «%s»"
+
+#, c-format
+msgid "Newval = \"%s\""
+msgstr "Newval = «%s»"
+
+#, c-format
+msgid "line %<PRId64>: %s"
+msgstr "рядок %<PRId64>: %s"
+
+#, c-format
+msgid "cmd: %s"
+msgstr "команда: %s"
+
+msgid "frame is zero"
+msgstr "кадр нульовий"
+
+#, c-format
+msgid "frame at highest level: %d"
+msgstr "кадр на найвищому рівні: %d"
+
+#, c-format
+msgid "Breakpoint in \"%s%s\" line %<PRId64>"
+msgstr "Точка зупинки в «%s%s» рядок %<PRId64>"
+
+#, c-format
+msgid "E161: Breakpoint not found: %s"
+msgstr "E161: Точку зупинки не знайдено: %s"
+
+msgid "No breakpoints defined"
+msgstr "Не визначено жодної точки зупинки"
+
+#, c-format
+msgid "%3d %s %s line %<PRId64>"
+msgstr "%3d %s %s рядок %<PRId64>"
+
+#, c-format
+msgid "%3d expr %s"
+msgstr "%3d вираз %s"
+
#, c-format
msgid "E96: Cannot diff more than %<PRId64> buffers"
msgstr "E96: Не можна порівнювати понад %<PRId64> буфери(ів)"
@@ -325,6 +343,19 @@ msgstr "E103: Буфер «%s» не в режимі порівняння"
msgid "E787: Buffer changed unexpectedly"
msgstr "E787: Буфер несподівано змінився"
+#, c-format
+msgid "E1214: Digraph must be just two characters: %s"
+msgstr "E1214: Диграф має бути з двох символів: %s"
+
+#, c-format
+msgid "E1215: Digraph must be one character: %s"
+msgstr "E1215: Диграф має бути одним символом: %s"
+
+msgid ""
+"E1216: digraph_setlist() argument must be a list of lists with two items"
+msgstr ""
+"E1216: аргумент digraph_setlist() має бути списком списків з двох елементів"
+
msgid "E104: Escape not allowed in digraph"
msgstr "E104: У диграфах не може міститися escape"
@@ -528,14 +559,21 @@ msgstr "E461: Неприпустима назва змінної: %s"
msgid "E995: Cannot modify existing variable"
msgstr "E995: Неможливо змінити наявну змінну"
-msgid "E957: Invalid window number"
-msgstr "E957: Некоректний номер вікна"
+msgid "E274: No white space allowed before parenthesis"
+msgstr "E274: Перед дужками не має бути пробілу"
#, c-format
msgid "E940: Cannot lock or unlock variable %s"
msgstr "E940: Неможливо заблокувати чи розблокувати змінну %s"
#, c-format
+msgid "E80: Error while writing: %s"
+msgstr "E80: Помилка під час запису: %s"
+
+msgid "E1098: String, List or Blob required"
+msgstr "E1098: Потрібен String, List чи Blob"
+
+#, c-format
msgid "E734: Wrong variable type for %s="
msgstr "E734: Неправильний тип змінної для %s="
@@ -564,8 +602,8 @@ msgstr "E687: Цілей менше, ніж елементів списку"
msgid "E688: More targets than List items"
msgstr "E688: Цілей більше, ніж елементів списку"
-msgid "Double ; in list of variables"
-msgstr "Друга ; у списку змінних"
+msgid "E452: Double ; in list of variables"
+msgstr "E452: Друга ; у списку змінних"
#, c-format
msgid "E738: Can't list variables for %s"
@@ -584,8 +622,8 @@ msgstr "E996: Неможливо заблокувати регістр"
msgid "E121: Undefined variable: %.*s"
msgstr "E121: Невизначена змінна: %.*s"
-msgid "E689: Can only index a List or Dictionary"
-msgstr "E689: Індексний доступ може бути тільки до списку чи словника"
+msgid "E689: Can only index a List, Dictionary or Blob"
+msgstr "E689: Індексний доступ може бути тільки до List, Dictionary чи Blob"
msgid "E708: [:] must come last"
msgstr "E708: [:] має бути останньою"
@@ -593,8 +631,11 @@ msgstr "E708: [:] має бути останньою"
msgid "E713: Cannot use empty key after ."
msgstr "E713: Неможливо вжити порожній ключ після ."
-msgid "E709: [:] requires a List value"
-msgstr "E709: [:] вимагає список"
+msgid "E709: [:] requires a List or Blob value"
+msgstr "E709: [:] вимагає List чи Blob"
+
+msgid "E972: Blob value does not have the right number of bytes"
+msgstr "E972: неправильна кількість байтів у значенні Blob"
msgid "E996: Cannot lock a range"
msgstr "E996: Неможливо заблокувати діапазон"
@@ -618,27 +659,18 @@ msgstr "E108: Змінної немає: «%s»"
msgid "E109: Missing ':' after '?'"
msgstr "E109: Бракує ':' після '?'"
-msgid "E691: Can only compare List with List"
-msgstr "E691: Список можна порівняти тільки зі списком"
-
-msgid "E692: Invalid operation for List"
-msgstr "E692: Некоректна операція над списком"
-
-msgid "E735: Can only compare Dictionary with Dictionary"
-msgstr "E735: Словник можна порівняти тільки із словником"
-
-msgid "E736: Invalid operation for Dictionary"
-msgstr "E736: Некоректна операція над словником"
-
-msgid "E694: Invalid operation for Funcrefs"
-msgstr "E694: Некоректна операція над функцією"
-
msgid "E804: Cannot use '%' with Float"
msgstr "E804: Не можна виконати '%' над Float"
+msgid "E973: Blob literal should have an even number of hex characters"
+msgstr "E973: Запис Blob повинен мати парну кількість шістнадцяткових символів"
+
msgid "E110: Missing ')'"
msgstr "E110: Пропущено ')'"
+msgid "E260: Missing name after ->"
+msgstr "E260: Після -> бракує імені"
+
msgid "E695: Cannot index a Funcref"
msgstr "E695: Функція не має індексації"
@@ -716,10 +748,6 @@ msgid "E921: Invalid callback argument"
msgstr "E921: Некоректний аргумент функції зворотнього виклику"
#, c-format
-msgid "E80: Error while writing: %s"
-msgstr "E80: Помилка під час запису: %s"
-
-#, c-format
msgid "E963: setting %s to value with wrong type"
msgstr "E963: встановлення %s до значення з неправильним типом"
@@ -759,6 +787,24 @@ msgstr "E5009: Некоректна $VIMRUNTIME: %s"
msgid "E5009: Invalid 'runtimepath'"
msgstr "E5009: Некоректний 'runtimepath'"
+msgid "E977: Can only compare Blob with Blob"
+msgstr "E977: Блоб можна порівняти тільки із блобом"
+
+msgid "E691: Can only compare List with List"
+msgstr "E691: Список можна порівняти тільки зі списком"
+
+msgid "E692: Invalid operation for List"
+msgstr "E692: Некоректна операція над списком"
+
+msgid "E735: Can only compare Dictionary with Dictionary"
+msgstr "E735: Словник можна порівняти тільки із словником"
+
+msgid "E736: Invalid operation for Dictionary"
+msgstr "E736: Некоректна операція над словником"
+
+msgid "E694: Invalid operation for Funcrefs"
+msgstr "E694: Некоректна операція над функцією"
+
#, c-format
msgid "E474: Expected comma before list item: %s"
msgstr "E474: Очікується кома перед елементом списка: %s"
@@ -808,6 +854,12 @@ msgstr ""
"%.*s"
#, c-format
+msgid "+-%s%3ld line: "
+msgid_plural "+-%s%3ld lines: "
+msgstr[0] "+-%s%3ld рядок: "
+msgstr[1] "+-%s%3ld рядків: "
+
+#, c-format
msgid "E474: Expected string end: %.*s"
msgstr "E474: Очікувався кінець рядка: %.*s"
@@ -1014,8 +1066,15 @@ msgid "E684: list index out of range: %<PRId64>"
msgstr "E684: Індекс списку поза межами: %<PRId64>"
#, c-format
-msgid "E686: Argument of %s must be a List"
-msgstr "E686: Аргумент у %s має бути списком"
+msgid "E899: Argument of %s must be a List or Blob"
+msgstr "E899: Аргумент у %s має бути списком чи блобом"
+
+msgid "E957: Invalid window number"
+msgstr "E957: Некоректний номер вікна"
+
+#, c-format
+msgid "E998: Reduce of an empty %s with no initial value"
+msgstr "E998: Скорочення порожнього %s без початкового значення"
#, c-format
msgid "Error converting the call result: %s"
@@ -1082,14 +1141,6 @@ msgid "E701: Invalid type for len()"
msgstr "E701: Некоректний тип для len()"
#, c-format
-msgid "E798: ID is reserved for \":match\": %<PRId64>"
-msgstr "E798: ID зарезервовано для \":match\": %<PRId64>"
-
-#, c-format
-msgid "E798: ID is reserved for \"match\": %<PRId64>"
-msgstr "E798: ID зарезервовано для \"match\": %<PRId64>"
-
-#, c-format
msgid "msgpackdump() argument, index %i"
msgstr "аргумент msgpackdump(), індекс %i"
@@ -1127,14 +1178,6 @@ msgid "E927: Invalid action: '%s'"
msgstr "E927: Неправильна дія: «%s»"
#, c-format
-msgid "E474: List item %d is either not a dictionary or an empty one"
-msgstr "E474: Елемент списку %d або не словник або порожній"
-
-#, c-format
-msgid "E474: List item %d is missing one of the required keys"
-msgstr "E474: Елемент списку %d немає одного з обов’язкових ключів"
-
-#, c-format
msgid "E962: Invalid action: '%s'"
msgstr "E962: Неправильна дія: «%s»"
@@ -1168,6 +1211,9 @@ msgstr "E935: неправильний номер групи співпадін
msgid "Can only call this function in an unmodified buffer"
msgstr "Цю функцію можна викликати тільки у незміненому буфері"
+msgid "writefile() first argument must be a List or a Blob"
+msgstr "перший аргумент writefile() має бути List або Blob"
+
#, c-format
msgid "E5060: Unknown flag: %s"
msgstr "E5060: Невідомий прапорець: %s"
@@ -1228,6 +1274,9 @@ msgstr "E745: Очікується Number чи String, трапився List"
msgid "E728: Expected a Number or a String, Dictionary found"
msgstr "E728: Очікується Number чи String, трапився Dictionary"
+msgid "E974: Expected a Number or a String, Blob found"
+msgstr "E974: Очікується Number чи String, трапився Blob"
+
msgid "E5299: Expected a Number or a String, Boolean found"
msgstr "E5299: Очікується Number чи String, трапився Boolean"
@@ -1243,6 +1292,9 @@ msgstr "E728: Dictionary вжито як Number"
msgid "E805: Using a Float as a Number"
msgstr "E805: Float вжито як Number"
+msgid "E974: Using a Blob as a Number"
+msgstr "E974: Blob вжито як Number"
+
msgid "E685: using an invalid value as a Number"
msgstr "E685: некоректне значення вжито як Number"
@@ -1252,6 +1304,9 @@ msgstr "E730: List вжито як String"
msgid "E731: using Dictionary as a String"
msgstr "E731: Dictionary вжито як String"
+msgid "E976: using Blob as a String"
+msgstr "E976: Blob вжито як String"
+
msgid "E908: using an invalid value as a String"
msgstr "E908: некоректне значення вжито як String"
@@ -1273,6 +1328,9 @@ msgstr "E362: Використано логічне значення як Float"
msgid "E907: Using a special value as a Float"
msgstr "E907: Використано спеціальне значення як Float"
+msgid "E975: Using a Blob as a Float"
+msgstr "E975: Blob вжито як Float"
+
msgid "E808: Number or Float required"
msgstr "E808: Треба вказати Number чи Float"
@@ -1298,6 +1356,9 @@ msgstr "E125: Недозволений аргумент: %s"
msgid "E853: Duplicate argument name: %s"
msgstr "E853: Назва аргументу повторюється: %s"
+msgid "E989: Non-default argument follows default argument"
+msgstr "E989: Аргумент без домовленого значення після аргументу з домовленим значенням"
+
#, c-format
msgid "E740: Too many arguments for function %s"
msgstr "E740: Забагато аргументів для функції %s"
@@ -1337,6 +1398,10 @@ msgid "E117: Unknown function: %s"
msgstr "E117: Невідома функція: %s"
#, c-format
+msgid "E276: Cannot use function as a method: %s"
+msgstr "E276: Не можна вжити функцію як метод: %s"
+
+#, c-format
msgid "E933: Function was deleted: %s"
msgstr "E933: Функцію було видалено: %s"
@@ -1408,10 +1473,6 @@ msgstr "Не вдалося знищити функцію %s: Вона вико
msgid "E133: :return not inside a function"
msgstr "E133: :return поза межами функції"
-#, c-format
-msgid "E107: Missing parentheses: %s"
-msgstr "E107: Пропущено дужки: %s"
-
msgid "tcp address must be host:port"
msgstr "адреса tcp має бути вузол:порт"
@@ -1448,13 +1509,6 @@ msgstr "> %d, шіст %08x, віс %o"
msgid "E134: Cannot move a range of lines into itself"
msgstr "E134: Неможливо перемістити діапазон рядків сам у себе"
-msgid "1 line moved"
-msgstr "Переміщено один рядок"
-
-#, c-format
-msgid "%<PRId64> lines moved"
-msgstr "Переміщено %<PRId64> рядки(ів)"
-
#, c-format
msgid "E482: Can't create file %s"
msgstr "E482: Не вдалося створити файл %s"
@@ -1533,27 +1587,6 @@ msgstr "Замінити на %s (y/n/a/q/l/^E/^Y)?"
msgid "(Interrupted) "
msgstr "(Перервано) "
-msgid "1 match"
-msgstr "Один збіг"
-
-msgid "1 substitution"
-msgstr "Одна заміна"
-
-#, c-format
-msgid "%<PRId64> matches"
-msgstr "%<PRId64> збіги(ів)"
-
-#, c-format
-msgid "%<PRId64> substitutions"
-msgstr "%<PRId64> замін(и)"
-
-msgid " on 1 line"
-msgstr " в одному рядку"
-
-#, c-format
-msgid " on %<PRId64> lines"
-msgstr " в %<PRId64> рядках"
-
msgid "E147: Cannot do :global recursive with a range"
msgstr "E147: :global не можна рекурсивно з діапазоном"
@@ -1610,39 +1643,6 @@ msgstr "E150: Не є каталогом: %s"
msgid "No old files"
msgstr "Жодного старого файлу"
-msgid "Entering Debug mode. Type \"cont\" to continue."
-msgstr "Режим налагодження. Щоб продовжити введіть «cont»."
-
-#, c-format
-msgid "line %<PRId64>: %s"
-msgstr "рядок %<PRId64>: %s"
-
-#, c-format
-msgid "cmd: %s"
-msgstr "команда: %s"
-
-msgid "frame is zero"
-msgstr "кадр нульовий"
-
-#, c-format
-msgid "frame at highest level: %d"
-msgstr "кадр на найвищому рівні: %d"
-
-#, c-format
-msgid "Breakpoint in \"%s%s\" line %<PRId64>"
-msgstr "Точка зупинки в «%s%s» рядок %<PRId64>"
-
-#, c-format
-msgid "E161: Breakpoint not found: %s"
-msgstr "E161: Точку зупинки не знайдено: %s"
-
-msgid "No breakpoints defined"
-msgstr "Не визначено жодної точки зупинки"
-
-#, c-format
-msgid "%3d %s %s line %<PRId64>"
-msgstr "%3d %s %s рядок %<PRId64>"
-
msgid "E750: First use \":profile start {fname}\""
msgstr "E750: Спочатку зробіть «:profile start {файл}»"
@@ -1683,10 +1683,6 @@ msgid "E666: compiler not supported: %s"
msgstr "E666: Компілятор не підтримується: %s"
#, c-format
-msgid ":source error parsing command %s"
-msgstr ":source помилка розбору команди %s"
-
-#, c-format
msgid "Cannot source a directory: \"%s\""
msgstr "Не вдалося прочитати каталог: «%s»"
@@ -1725,6 +1721,9 @@ msgstr "змінна оточення"
msgid "error handler"
msgstr "обробник помилки"
+msgid "changed window size"
+msgstr "змінено розмір вікна"
+
msgid "Lua"
msgstr "Lua"
@@ -1735,6 +1734,10 @@ msgstr "Клієнт API (канал «%<PRIu64>»)"
msgid "anonymous :source"
msgstr "анонімний :source"
+#, c-format
+msgid "anonymous :source (script id %d)"
+msgstr "анонімний :source (ід. скрипта %d)"
+
msgid "W15: Warning: Wrong line separator, ^M may be missing"
msgstr "W15: Застереження: Неправильний роздільник рядків, можливо, бракує ^M"
@@ -1752,6 +1755,14 @@ msgstr "Мова (%s): «%s»"
msgid "E197: Cannot set language to \"%s\""
msgstr "E197: Не вдалося встановити мову «%s»"
+#, c-format
+msgid "E184: No such user-defined command: %s"
+msgstr "E184: Команду користувача не знайдено: %s"
+
+#, c-format
+msgid "E1237: No such user-defined command in current buffer: %s"
+msgstr "E1237: Немає такої команди користувача у цьому буфері: %s"
+
msgid "Entering Ex mode. Type \"visual\" to go to Normal mode."
msgstr "Режим Ex. Для повернення до нормального режиму виконайте «visual»"
@@ -1796,7 +1807,8 @@ msgstr "E494: Спробуйте w або w>>"
msgid ""
"INTERNAL: Cannot use EX_DFLALL with ADDR_NONE, ADDR_UNSIGNED or ADDR_QUICKFIX"
msgstr ""
-"ВНУТРІШНЄ: Не можна вживати EX_DFLALL з ADDR_NONE, ADDR_UNSIGNED чи ADDR_QUICKFIX"
+"ВНУТРІШНЄ: Не можна вживати EX_DFLALL з ADDR_NONE, ADDR_UNSIGNED чи "
+"ADDR_QUICKFIX"
msgid "E943: Command table needs to be updated, run 'make'"
msgstr "E943: Потрібно поновити таблицю команд, запустіть 'make'"
@@ -1804,20 +1816,6 @@ msgstr "E943: Потрібно поновити таблицю команд, з
msgid "E319: The command is not available in this version"
msgstr "E319: Вибачте, цієї команди немає у цій версії"
-msgid "1 more file to edit. Quit anyway?"
-msgstr "Залишилося відредагувати ще один файл. Все одно вийти?"
-
-#, c-format
-msgid "%d more files to edit. Quit anyway?"
-msgstr "Ще є %d не редагованих файлів. Все одно вийти?"
-
-msgid "E173: 1 more file to edit"
-msgstr "E173: Залишилося відредагувати ще один файл"
-
-#, c-format
-msgid "E173: %<PRId64> more files to edit"
-msgstr "E173: Залишилося %<PRId64> не редагованих файлів"
-
#, c-format
msgid "E174: Command already exists: add ! to replace it: %s"
msgstr "E174: Команда вже існує, ! щоб замінити її: %s"
@@ -1854,6 +1852,9 @@ msgstr "E179: для -addr потрібний аргумент"
msgid "E181: Invalid attribute: %s"
msgstr "E181: Неправильний атрибут: %s"
+msgid "E1208: -complete used without -nargs"
+msgstr "E1208: -complete вжито без without -nargs"
+
msgid "E182: Invalid command name"
msgstr "E182: Неправильна назва команди"
@@ -1865,10 +1866,6 @@ msgstr ""
"E841: Зарезервована назва, не можна використати для користувацької команди"
#, c-format
-msgid "E184: No such user-defined command: %s"
-msgstr "E184: Команду користувача не знайдено: %s"
-
-#, c-format
msgid "E180: Invalid address type value: %s"
msgstr "E180: Неправильне значення типу адреси: %s"
@@ -1949,7 +1946,7 @@ msgstr "E842: немає номера рядка, щоб використати
msgid "E961: no line number to use for \"<sflnum>\""
msgstr "E961: немає номера рядка, щоб використати з «<sflnum>»"
-#, c-format
+#, no-c-format
msgid "E499: Empty file name for '%' or '#', only works with \":p:h\""
msgstr "E499: Назва файлу для '%' чи '#' порожня, працює лише з «:p:h»"
@@ -2106,6 +2103,9 @@ msgstr " тип файлу\n"
msgid "'history' option is zero"
msgstr "Опція 'history' порожня"
+msgid "[Command Line]"
+msgstr "[Рядок Команд]"
+
msgid "E199: Active window or buffer deleted"
msgstr "E199: Активне вікно або буфер було знищено"
@@ -2228,6 +2228,10 @@ msgstr "Не придатний для запису"
msgid "is read-only (add ! to override)"
msgstr "лише для читання (! щоб не зважати)"
+#, c-format
+msgid "E303: Unable to create directory \"%s\" for backup file: %s"
+msgstr "E303: Не вдалося створити каталог «%s» для резервного файлу: %s"
+
msgid "E506: Can't write to backup file (add ! to override)"
msgstr "E506: Не вдалося записати резервний файл (! щоб не зважати)"
@@ -2322,20 +2326,6 @@ msgstr "[формат unix]"
msgid "[unix]"
msgstr "[unix]"
-msgid "1 line, "
-msgstr "один рядок, "
-
-#, c-format
-msgid "%<PRId64> lines, "
-msgstr "%<PRId64> рядків, "
-
-msgid "1 character"
-msgstr "один символ"
-
-#, c-format
-msgid "%<PRId64> characters"
-msgstr "%<PRId64> символів"
-
msgid "[Incomplete last line]"
msgstr "[Неповний останній рядок]"
@@ -2399,10 +2389,12 @@ msgstr "Застереження"
msgid ""
"&OK\n"
-"&Load File"
+"&Load File\n"
+"Load File &and Options"
msgstr ""
-"&O:Гаразд\n"
-"&L:Завантажити"
+"[&O]Гаразд\n"
+"[&L]Завантажити файл\n"
+"[&a]Завантажити файл і опції"
#, c-format
msgid "E462: Could not prepare for reloading \"%s\""
@@ -2550,6 +2542,9 @@ msgstr "E476: Некоректна команда"
msgid "E17: \"%s\" is a directory"
msgstr "E17: «%s» — це каталог"
+msgid "E756: Spell checking is not possible"
+msgstr "E756: Перевірка орфографії неможлива"
+
msgid "E900: Invalid channel id"
msgstr "E900: Некоректний канал"
@@ -2718,7 +2713,14 @@ msgid "E928: String required"
msgstr "E928: Потрібен String"
msgid "E715: Dictionary required"
-msgstr "E715: Потрібен словник"
+msgstr "E715: Потрібен Dictionary"
+
+#, c-format
+msgid "E979: Blob index out of range: %<PRId64>"
+msgstr "E979: Індекс Blob поза межами: %<PRId64>"
+
+msgid "E978: Invalid operation for Blob"
+msgstr "E978: Некоректна операція над Blob"
#, c-format
msgid "E118: Too many arguments for function: %s"
@@ -2731,10 +2733,17 @@ msgstr "E716: Немає такого ключа у словнику: «%s»"
msgid "E714: List required"
msgstr "E714: Потрібен список"
+msgid "E897: List or Blob required"
+msgstr "E897: Потрібен List або Blob"
+
#, c-format
msgid "E712: Argument of %s must be a List or Dictionary"
msgstr "E712: Аргумент у %s має бути списком чи словником"
+#, c-format
+msgid "E896: Argument of %s must be a List, Dictionary or Blob"
+msgstr "E896: Аргумент у %s має бути List, Dictionary чи Blob"
+
msgid "E47: Error while reading errorfile"
msgstr "E47: Помилка читання файлу помилок"
@@ -2802,6 +2811,10 @@ msgstr "E939: Потрібна додана кількість"
msgid "E81: Using <SID> not in a script context"
msgstr "E81: <SID> використовується не в контексті скрипту"
+#, c-format
+msgid "E107: Missing parentheses: %s"
+msgstr "E107: Пропущено дужки: %s"
+
msgid "E363: pattern uses more memory than 'maxmempattern'"
msgstr "E363: Зразок використовує більше, ніж 'maxmempattern', пам'яті"
@@ -2832,6 +2845,13 @@ msgstr "E919: Каталог не знайдено у '%s': «%s»"
msgid "E952: Autocommand caused recursive behavior"
msgstr "E952: Автокоманди призвели до рекурсії"
+msgid "E813: Cannot close autocmd window"
+msgstr "E813: Не вдалося закрити вікно autocmd"
+
+#, c-format
+msgid "E686: Argument of %s must be a List"
+msgstr "E686: Аргумент у %s має бути списком"
+
msgid "E519: Option not supported"
msgstr "E519: Опція не підтримується"
@@ -2869,6 +2889,21 @@ msgstr "E5601: Не вдалося закрити вікно, залишилос
msgid "E5602: Cannot exchange or rotate float"
msgstr "E5602: Не можна обміняти чи покрутити плавуче вікно"
+msgid "E1142: Non-empty string required"
+msgstr "E1142: Потрібен непорожній String"
+
+msgid "E1155: Cannot define autocommands for ALL events"
+msgstr "E1155: Не можу визначити автокоманди для УСІХ подій"
+
+msgid "E1240: Resulting text too long"
+msgstr "E1240: Текст результату задовгий"
+
+msgid "E1247: Line number out of range"
+msgstr "E1247: Номер рядка вийшов поза межами"
+
+msgid "E1249: Highlight group name too long"
+msgstr "E1249: Назва групи підсвічування задовга"
+
msgid "search hit TOP, continuing at BOTTOM"
msgstr "Пошук дійшов до ПОЧАТКУ, продовжується з КІНЦЯ"
@@ -2975,6 +3010,60 @@ msgstr "Завдання друку відіслано."
msgid "E424: Too many different highlighting attributes in use"
msgstr "E424: Використано забагато різних атрибутів кольору"
+#, c-format
+msgid "E411: highlight group not found: %s"
+msgstr "E411: Групу підсвічування не знайдено: %s"
+
+#, c-format
+msgid "E412: Not enough arguments: \":highlight link %s\""
+msgstr "E412: Недостатньо аргументів: «:highlight link %s»"
+
+#, c-format
+msgid "E413: Too many arguments: \":highlight link %s\""
+msgstr "E413: Забагато аргументів: «:highlight link %s»"
+
+msgid "E414: group has settings, highlight link ignored"
+msgstr "E414: Грума має settings, highlight link проігноровано"
+
+#, c-format
+msgid "E415: unexpected equal sign: %s"
+msgstr "E415: Несподіваний знак рівності: %s"
+
+#, c-format
+msgid "E416: missing equal sign: %s"
+msgstr "E416: Пропущено знак рівності: %s"
+
+#, c-format
+msgid "E417: missing argument: %s"
+msgstr "E417: Пропущено аргумент: %s"
+
+#, c-format
+msgid "E418: Illegal value: %s"
+msgstr "E418: Неправильне значення: %s"
+
+msgid "E419: FG color unknown"
+msgstr "E419: Невідомий колір тексту"
+
+msgid "E420: BG color unknown"
+msgstr "E420: Невідомий колір фону"
+
+#, c-format
+msgid "E421: Color name or number not recognized: %s"
+msgstr "E421: Нерозпізнана назва або номер кольору: %s"
+
+#, c-format
+msgid "E423: Illegal argument: %s"
+msgstr "E423: Неправильний аргумент: %s"
+
+msgid "E669: Unprintable character in group name"
+msgstr "E669: Недруковний символ у назві групи"
+
+msgid "W18: Invalid character in group name"
+msgstr "W18: Некоректний символ у назві групи"
+
+msgid "E849: Too many highlight and syntax groups"
+msgstr "E849: Забагато груп підсвічування і синтаксису"
+
msgid "Add a new database"
msgstr "Додати нову базу даних"
@@ -3129,6 +3218,12 @@ msgstr "Жодного з'єднання з cscope\n"
msgid " # pid database name prepend path\n"
msgstr " # pid назва бази даних шлях\n"
+msgid "Type number and <Enter> or click with the mouse (q or empty cancels): "
+msgstr "Наберіть число й <Enter> чи клацніть мишкою (q чи порожнє скасовує): "
+
+msgid "Type number and <Enter> (q or empty cancels): "
+msgstr "Наберіть число й <Enter> (q чи порожнє скасовує): "
+
#, c-format
msgid "E1502: Lua failed to grow stack to %i"
msgstr "E1502: Lua не вдалося збільшити стек до %i"
@@ -3151,20 +3246,11 @@ msgstr "E5102: Lua не вдалося збільшити стек до %i"
msgid "Error executing vim.schedule lua callback: %.*s"
msgstr "Помилка виконання обробника lua vim.schedule: %.*s"
-#, c-format
-msgid "E5106: Error while creating shared module: %.*s"
-msgstr "E5106: Помилка створення розділюваного модуля: %.*s"
+msgid "E970: Failed to initialize lua interpreter\n"
+msgstr "E970: Не вдалося ініціалізувати інтерпретатор lua\n"
-#, c-format
-msgid "E5106: Error while creating inspect module: %.*s"
-msgstr "E5106: Помилка створення модуля inspect: %.*s"
-
-#, c-format
-msgid "E5106: Error while creating vim module: %.*s"
-msgstr "E5106: Помилка створення модуля vim: %.*s"
-
-msgid "E970: Failed to initialize lua interpreter"
-msgstr "E970: Не вдалося ініціалізувати інтерпретатор lua"
+msgid "E970: Failed to initialize builtin lua modules\n"
+msgstr "E970: Не вдалося ініціалізувати інтерпретатор lua\n"
#, c-format
msgid "E5114: Error while converting print argument #%i: %.*s"
@@ -3179,6 +3265,10 @@ msgid "E5116: Error while calling debug string: %.*s"
msgstr "E5116: Помилка виклику налагодження: %.*s"
#, c-format
+msgid "E5108: Error executing Lua function: %.*s"
+msgstr "E5108: Помилка виконання функції lua: %.*s"
+
+#, c-format
msgid "E5107: Error loading lua %.*s"
msgstr "E5107: Помилка завантаження lua %.*s"
@@ -3214,8 +3304,16 @@ msgid "E5113: Error while calling lua chunk: %.*s"
msgstr "E5113: Помилка виклику шматку lua: %.*s"
#, c-format
-msgid "Error executing vim.log_keystroke lua callback: %.*s"
-msgstr "Помилка виконання обробника lua vim.log_keystroke: %.*s"
+msgid "Error executing vim._expand_pat: %.*s"
+msgstr "Помилка виконання vim._expand_pat: %.*s"
+
+#, c-format
+msgid "Error executing vim.on_key Lua callback: %.*s"
+msgstr "Помилка виконання обробника Lua vim.on_key: %.*s"
+
+#, c-format
+msgid "Error executing Lua callback: %.*s"
+msgstr "Помилка виконання обробника Lua: %.*s"
msgid "Argument missing after"
msgstr "Пропущено аргумент після"
@@ -3254,8 +3352,8 @@ msgid "pre-vimrc command line"
msgstr "команди перед vimrc"
#, c-format
-msgid "Conflicting configs: \"%s\" \"%s\""
-msgstr "Суперечливі конфігурації: «%s» «%s»"
+msgid "E5422: Conflicting configs: \"%s\" \"%s\""
+msgstr "E5422: Суперечливі конфігурації: «%s» «%s»"
#, c-format
msgid "E282: Cannot read from \"%s\""
@@ -3387,6 +3485,12 @@ msgstr " --listen <адреса> Обслуговувати RPC API за ц
msgid " --noplugin Don't load plugins\n"
msgstr " --noplugin Не завантажувати доповнення\n"
+msgid " --remote[-subcommand] Execute commands remotely on a server\n"
+msgstr " --remote[-subcommand] Виконати команди на віддаленому сервері\n"
+
+msgid " --server <address> Specify RPC server to send commands to\n"
+msgstr " --server <адреса> Задати сервер RPC, куди слати команди\n"
+
msgid " --startuptime <file> Write startup timing messages to <file>\n"
msgstr " --startuptime <файл> Записати профіль запуску до <файлу>\n"
@@ -3425,6 +3529,46 @@ msgstr ""
"\n"
"змінити ряд. стовп. текст"
+#, c-format
+msgid "E799: Invalid ID: %<PRId64> (must be greater than or equal to 1)"
+msgstr "E799: Неправильний ID: %<PRId64> (має бути не меншим 1)"
+
+#, c-format
+msgid "E801: ID already taken: %<PRId64>"
+msgstr "E801: ID вже зайнято: %<PRId64>"
+
+#, c-format
+msgid "E5030: Empty list at position %d"
+msgstr "E5030: Порожній список у позиції %d"
+
+#, c-format
+msgid "E5031: List or number required at position %d"
+msgstr "E5031: Очікується список чи число у позиції %d"
+
+#, c-format
+msgid "E802: Invalid ID: %<PRId64> (must be greater than or equal to 1)"
+msgstr "E802: Неправильний ID: %<PRId64> (має бути не меншим 1)"
+
+#, c-format
+msgid "E803: ID not found: %<PRId64>"
+msgstr "E803: ID не знайдено: %<PRId64>"
+
+#, c-format
+msgid "E474: List item %d is either not a dictionary or an empty one"
+msgstr "E474: Елемент списку %d або не словник або порожній"
+
+#, c-format
+msgid "E474: List item %d is missing one of the required keys"
+msgstr "E474: Елемент списку %d немає одного з обов’язкових ключів"
+
+#, c-format
+msgid "E798: ID is reserved for \":match\": %<PRId64>"
+msgstr "E798: ID зарезервовано для \":match\": %<PRId64>"
+
+#, c-format
+msgid "E798: ID is reserved for \"match\": %<PRId64>"
+msgstr "E798: ID зарезервовано для \"match\": %<PRId64>"
+
msgid "E293: block was not locked"
msgstr "E293: Блок не було зафіксовано"
@@ -3910,6 +4054,9 @@ msgstr "Перервано: "
msgid "Press ENTER or type command to continue"
msgstr "Натисніть ENTER або введіть команду для продовження"
+msgid " (Interrupted)"
+msgstr " (Перервано)"
+
#, c-format
msgid "%s line %<PRId64>"
msgstr "%s рядок %<PRId64>"
@@ -3952,38 +4099,9 @@ msgstr ""
"&D:Жодного\n"
"&C:Скасувати"
-msgid "Type number and <Enter> or click with mouse (empty cancels): "
-msgstr "Наберіть число й <Enter> чи клацніть мишкою (порожнє скасовує): "
-
-msgid "Type number and <Enter> (empty cancels): "
-msgstr "Наберіть число й <Enter> (порожнє скасовує): "
-
-msgid "1 more line"
-msgstr "додано один рядок"
-
-msgid "1 line less"
-msgstr "знищено один рядок"
-
-#, c-format
-msgid "%<PRId64> more lines"
-msgstr "додано рядків: %<PRId64>"
-
-#, c-format
-msgid "%<PRId64> fewer lines"
-msgstr "знищено рядків: %<PRId64>"
-
-msgid " (Interrupted)"
-msgstr " (Перервано)"
-
-msgid "Beep!"
-msgstr "Дзень!"
-
msgid "E349: No identifier under cursor"
msgstr "E349: Немає ідентифікатора над курсором"
-msgid "E774: 'operatorfunc' is empty"
-msgstr "E774: 'operatorfunc' порожня"
-
msgid "E348: No string under cursor"
msgstr "E348: Немає рядка на курсорі"
@@ -4007,63 +4125,17 @@ msgid "Type :qa and press <Enter> to exit Nvim"
msgstr "Введіть :qa і натисність <Enter> щоб вийти з Nvim"
#, c-format
-msgid "1 line %sed 1 time"
-msgstr "Один рядок %s-но"
-
-#, c-format
-msgid "1 line %sed %d times"
-msgstr "Один рядок %s-но %d разів"
-
-#, c-format
-msgid "%<PRId64> lines %sed 1 time"
-msgstr "%<PRId64> рядків %s-но"
-
-#, c-format
-msgid "%<PRId64> lines %sed %d times"
-msgstr "%<PRId64> рядків %s-но %d разів"
-
-#, c-format
msgid "%<PRId64> lines to indent... "
msgstr "Залишилося вирівняти %<PRId64> рядків..."
-msgid "1 line indented "
-msgstr "Вирівняно один рядок"
-
-#, c-format
-msgid "%<PRId64> lines indented "
-msgstr "Вирівняно рядків: %<PRId64>"
-
msgid "E748: No previously used register"
msgstr "E748: Регістри перед цим не вживались"
-msgid "1 line changed"
-msgstr "Один рядок змінено"
-
-#, c-format
-msgid "%<PRId64> lines changed"
-msgstr "Змінено рядків: %<PRId64>"
-
#, c-format
msgid " into \"%c"
msgstr " у \"%c"
#, c-format
-msgid "block of 1 line yanked%s"
-msgstr "Запам'ятав блок з одного рядка%s"
-
-#, c-format
-msgid "1 line yanked%s"
-msgstr "Запам'ятав один рядок%s"
-
-#, c-format
-msgid "block of %<PRId64> lines yanked%s"
-msgstr "Запам'ятав блок із %<PRId64> рядків%s"
-
-#, c-format
-msgid "%<PRId64> lines yanked%s"
-msgstr "Запам'ятав рядків: %<PRId64>%s"
-
-#, c-format
msgid "E353: Nothing in register %s"
msgstr "E353: У регістрі %s нічого немає"
@@ -4120,6 +4192,9 @@ msgstr ""
msgid "(+%<PRId64> for BOM)"
msgstr "(+%<PRId64> для BOM)"
+msgid "E774: 'operatorfunc' is empty"
+msgstr "E774: 'operatorfunc' порожня"
+
msgid "E518: Unknown option"
msgstr "E518: Невідома опція"
@@ -4168,8 +4243,8 @@ msgstr "E527: Бракує коми"
msgid "E528: Must specify a ' value"
msgstr "E528: Потрібно вказати значення '"
-msgid "E595: contains unprintable or wide character"
-msgstr "E595: Містить недруковні або розширені символи"
+msgid "E595: 'showbreak' contains unprintable or wide character"
+msgstr "E595: 'showbreak' містить недруковні або розширені символи"
#, c-format
msgid "E535: Illegal character after <%c>"
@@ -4399,67 +4474,18 @@ msgstr "E70: %s%%[] порожній"
msgid "E956: Cannot use pattern recursively"
msgstr "E956: Не можна рекурсивно використати шаблон"
-msgid "E65: Illegal back reference"
-msgstr "E65: Некоректне зворотнє посилання"
-
-msgid "E339: Pattern too long"
-msgstr "E339: Зразок занадто довгий"
-
-msgid "E50: Too many \\z("
-msgstr "E50: Забагато \\z("
-
#, c-format
-msgid "E51: Too many %s("
-msgstr "E51: Забагато %s("
-
-msgid "E52: Unmatched \\z("
-msgstr "E52: Немає пари \\z("
+msgid "E1204: No Number allowed after .: '\\%%%c'"
+msgstr "E1204: Number не можна після .: '\\%%%c'"
#, c-format
-msgid "E59: invalid character after %s@"
-msgstr "E59: Недозволений символ після %s@"
-
-#, c-format
-msgid "E60: Too many complex %s{...}s"
-msgstr "E60: Забагато складних %s{...}"
-
-#, c-format
-msgid "E61: Nested %s*"
-msgstr "E61: Вкладені %s*"
-
-#, c-format
-msgid "E62: Nested %s%c"
-msgstr "E62: Вкладені %s%c"
-
-msgid "E63: invalid use of \\_"
-msgstr "E63: Некоректно вжито \\_"
-
-#, c-format
-msgid "E64: %s%c follows nothing"
-msgstr "E64: Після %s%c нічого немає"
-
-msgid "E68: Invalid character after \\z"
-msgstr "E68: Неправильний символ після \\z"
-
-#, c-format
-msgid "E678: Invalid character after %s%%[dxouU]"
-msgstr "E678: Недозволений символ після %s%%[dxouU]"
-
-#, c-format
-msgid "E71: Invalid character after %s%%"
-msgstr "E71: Недозволений символ після %s%%"
+msgid "E554: Syntax error in %s{...}"
+msgstr "E554: Синтаксична помилка в %s{...}"
#, c-format
msgid "E888: (NFA regexp) cannot repeat %s"
msgstr "E888: (NFA regexp) неможливо повторити %s"
-#, c-format
-msgid "E554: Syntax error in %s{...}"
-msgstr "E554: Синтаксична помилка в %s{...}"
-
-msgid "External submatches:\n"
-msgstr "Зовнішні під-збіги:\n"
-
msgid ""
"E864: \\%#= can only be followed by 0, 1, or 2. The automatic engine will be "
"used "
@@ -4482,6 +4508,14 @@ msgstr "Пошук «%s»"
msgid "not found in '%s': \"%s\""
msgstr "не знайдено в '%s': «%s»"
+#, c-format
+msgid "Searching for \"%s\" in runtime path"
+msgstr "Пошук «%s» в шляху виконання"
+
+#, c-format
+msgid "not found in runtime path: \"%s\""
+msgstr "не знайдено в шляху виконання: «%s»"
+
msgid " TERMINAL"
msgstr " ТЕРМІНАЛ"
@@ -4850,9 +4884,6 @@ msgstr " (не підтримується)"
msgid "E759: Format error in spell file"
msgstr "E759: Помилка формату у файлі орфографії"
-msgid "E756: Spell checking is not enabled"
-msgstr "E756: Перевірка орфографії не дозволена"
-
#, c-format
msgid "Warning: Cannot find word list \"%s.%s.spl\" or \"%s.ascii.spl\""
msgstr ""
@@ -5193,6 +5224,9 @@ msgstr "E765: 'spellfile' не містить %<PRId64> елементів"
msgid "Word '%.*s' removed from %s"
msgstr "Слово '%.*s' знищено з %s"
+msgid "Seek error in spellfile"
+msgstr "Помилка зміни позиції у файлі орфографії"
+
#, c-format
msgid "Word '%.*s' added to %s"
msgstr "Слово '%.*s' додано до %s"
@@ -5222,36 +5256,6 @@ msgstr "Для буфера не визначено елементів синт
msgid "'redrawtime' exceeded, syntax highlighting disabled"
msgstr "'redrawtime' перевищено, підсвічування синтаксису вимкнено"
-msgid "syntax conceal on"
-msgstr "синтаксичне приховування увімк"
-
-msgid "syntax conceal off"
-msgstr "синтаксичне приховування вимк"
-
-msgid "syntax case ignore"
-msgstr "синтаксис ігнорувати регістр"
-
-msgid "syntax case match"
-msgstr "синтаксис дотримуватися регістру"
-
-msgid "syntax foldlevel start"
-msgstr "рівень згортки синтаксису початок"
-
-msgid "syntax foldlevel minimum"
-msgstr "рівень згортки синтаксису мінімум"
-
-msgid "syntax spell toplevel"
-msgstr "синтаксис перевіряти всюди"
-
-msgid "syntax spell notoplevel"
-msgstr "синтаксис не перевіряти"
-
-msgid "syntax spell default"
-msgstr "синтаксис початково"
-
-msgid "syntax iskeyword "
-msgstr "синтаксис iskeyword "
-
msgid "syntax iskeyword not set"
msgstr "не встановлено синтаксис iskeyword"
@@ -5400,63 +5404,6 @@ msgid ""
msgstr ""
" ВСЬОГО К-ТЬ СПІВП. НАЙПОВІЛ. СЕРЕДН. НАЗВА ШАБЛОН"
-msgid "E679: recursive loop loading syncolor.vim"
-msgstr "E679: Рекурсивний цикл читання syncolor.vim"
-
-#, c-format
-msgid "E411: highlight group not found: %s"
-msgstr "E411: Групу підсвічування не знайдено: %s"
-
-#, c-format
-msgid "E412: Not enough arguments: \":highlight link %s\""
-msgstr "E412: Недостатньо аргументів: «:highlight link %s»"
-
-#, c-format
-msgid "E413: Too many arguments: \":highlight link %s\""
-msgstr "E413: Забагато аргументів: «:highlight link %s»"
-
-msgid "E414: group has settings, highlight link ignored"
-msgstr "E414: Грума має settings, highlight link проігноровано"
-
-#, c-format
-msgid "E415: unexpected equal sign: %s"
-msgstr "E415: Несподіваний знак рівності: %s"
-
-#, c-format
-msgid "E416: missing equal sign: %s"
-msgstr "E416: Пропущено знак рівності: %s"
-
-#, c-format
-msgid "E417: missing argument: %s"
-msgstr "E417: Пропущено аргумент: %s"
-
-#, c-format
-msgid "E418: Illegal value: %s"
-msgstr "E418: Неправильне значення: %s"
-
-msgid "E419: FG color unknown"
-msgstr "E419: Невідомий колір тексту"
-
-msgid "E420: BG color unknown"
-msgstr "E420: Невідомий колір фону"
-
-#, c-format
-msgid "E421: Color name or number not recognized: %s"
-msgstr "E421: Нерозпізнана назва або номер кольору: %s"
-
-#, c-format
-msgid "E423: Illegal argument: %s"
-msgstr "E423: Неправильний аргумент: %s"
-
-msgid "E669: Unprintable character in group name"
-msgstr "E669: Недруковний символ у назві групи"
-
-msgid "W18: Invalid character in group name"
-msgstr "W18: Некоректний символ у назві групи"
-
-msgid "E849: Too many highlight and syntax groups"
-msgstr "E849: Забагато груп підсвічування і синтаксису"
-
msgid "E555: at bottom of tag stack"
msgstr "E555: Кінець стеку міток"
@@ -5542,6 +5489,9 @@ msgstr "E435: Не вдалося знайти мітку, тільки прип
msgid "Duplicate field name: %s"
msgstr "Назва поля повторюється: %s"
+msgid "Beep!"
+msgstr "Дзень!"
+
msgid "E881: Line count changed unexpectedly"
msgstr "E881: Кількість рядків несподівано змінилася"
@@ -5911,9 +5861,6 @@ msgstr "E443: Не вдалося перемістити вікно, заваж
msgid "E444: Cannot close last window"
msgstr "E444: Не вдалося закрити останнє вікно"
-msgid "E813: Cannot close autocmd window"
-msgstr "E813: Не вдалося закрити вікно autocmd"
-
msgid "E814: Cannot close window, only autocmd window would remain"
msgstr "E814: Не вдалося закрити вікно, залишилося б тільки вікно autocmd"
@@ -5923,26 +5870,4 @@ msgstr "E445: У іншому вікні є зміни"
msgid "E446: No file name under cursor"
msgstr "E446: Немає назви файлу над курсором"
-#, c-format
-msgid "E799: Invalid ID: %<PRId64> (must be greater than or equal to 1)"
-msgstr "E799: Неправильний ID: %<PRId64> (має бути не меншим 1)"
-
-#, c-format
-msgid "E801: ID already taken: %<PRId64>"
-msgstr "E801: ID вже зайнято: %<PRId64>"
-
-#, c-format
-msgid "E5030: Empty list at position %d"
-msgstr "E5030: Порожній список у позиції %d"
-#, c-format
-msgid "E5031: List or number required at position %d"
-msgstr "E5031: Очікується список чи число у позиції %d"
-
-#, c-format
-msgid "E802: Invalid ID: %<PRId64> (must be greater than or equal to 1)"
-msgstr "E802: Неправильний ID: %<PRId64> (має бути не меншим 1)"
-
-#, c-format
-msgid "E803: ID not found: %<PRId64>"
-msgstr "E803: ID не знайдено: %<PRId64>"
diff --git a/src/nvim/po/zh_CN.UTF-8.po b/src/nvim/po/zh_CN.UTF-8.po
index 1e329443ce..9a8cd38f5e 100644
--- a/src/nvim/po/zh_CN.UTF-8.po
+++ b/src/nvim/po/zh_CN.UTF-8.po
@@ -23,6 +23,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8-bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
#: ../api/private/helpers.c:201
#, fuzzy
@@ -781,8 +782,9 @@ msgstr "-c 参数"
#: ../eval.c:9229
#, c-format
-msgid "+-%s%3ld lines: "
-msgstr "+-%s%3ld 行: "
+msgid "+-%s%3ld line: "
+msgid_plural "+-%s%3ld lines: "
+msgstr[0] "+-%s%3ld 行: "
#: ../eval.c:9291
#, c-format
diff --git a/src/nvim/po/zh_TW.UTF-8.po b/src/nvim/po/zh_TW.UTF-8.po
index c97f31ddcf..e2fb2d39d4 100644
--- a/src/nvim/po/zh_TW.UTF-8.po
+++ b/src/nvim/po/zh_TW.UTF-8.po
@@ -827,8 +827,9 @@ msgstr ""
#: ../eval.c:9229
#, c-format
-msgid "+-%s%3ld lines: "
-msgstr "+-%s%3ld 行: "
+msgid "+-%s%3ld line: "
+msgid_plural "+-%s%3ld lines: "
+msgstr[0] "+-%s%3ld 行: "
#: ../eval.c:9291
#, fuzzy, c-format
diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c
index 2bef9dc2d9..625fd15886 100644
--- a/src/nvim/popupmnu.c
+++ b/src/nvim/popupmnu.c
@@ -21,6 +21,7 @@
#include "nvim/highlight.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
+#include "nvim/menu.h"
#include "nvim/move.h"
#include "nvim/option.h"
#include "nvim/popupmnu.h"
@@ -62,8 +63,13 @@ static bool pum_invalid = false; // the screen was just cleared
#define PUM_DEF_HEIGHT 10
#define PUM_DEF_WIDTH 15
-static int str_dispnsize(char_u *s, int len);
-static int str_dispsize(char_u *s);
+static int str_dispnsize(char *s, int len);
+static int str_dispsize(char *s);
+
+// Forward declarations of needed syn functions.
+int syn_id2attr(int hl_id);
+char_u *syn_id2name(int id);
+int syn_name2id(char_u*);
static void pum_compute_size(void)
{
@@ -74,19 +80,19 @@ static void pum_compute_size(void)
for (int i = 0; i < pum_size; i++) {
int w;
if (pum_array[i].pum_text != NULL) {
- w = str_dispsize(pum_array[i].pum_text);
+ w = str_dispsize((char*) pum_array[i].pum_text);
if (pum_base_width < w) {
pum_base_width = w;
}
}
if (pum_array[i].pum_kind != NULL) {
- w = str_dispsize(pum_array[i].pum_kind) + 1;
+ w = str_dispsize((char*) pum_array[i].pum_kind) + 1;
if (pum_kind_width < w) {
pum_kind_width = w;
}
}
if (pum_array[i].pum_extra != NULL) {
- w = str_dispsize(pum_array[i].pum_extra) + 1;
+ w = str_dispsize((char*) pum_array[i].pum_extra) + 1;
if (pum_extra_width < w) {
pum_extra_width = w;
}
@@ -118,10 +124,11 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
if (!pum_is_visible) {
// To keep the code simple, we only allow changing the
// draw mode when the popup menu is not being displayed
- pum_external = ui_has(kUIPopupmenu) || (State == CMDLINE && ui_has(kUIWildmenu));
+ pum_external = ui_has(kUIPopupmenu)
+ || (State == MODE_CMDLINE && ui_has(kUIWildmenu));
}
- pum_rl = (curwin->w_p_rl && State != CMDLINE);
+ pum_rl = (curwin->w_p_rl && State != MODE_CMDLINE);
do {
// Mark the pum as visible already here,
@@ -133,7 +140,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
below_row = cmdline_row;
// wildoptions=pum
- if (State == CMDLINE) {
+ if (State == MODE_CMDLINE) {
pum_win_row = ui_has(kUICmdline) ? 0 : cmdline_row;
cursor_col = cmd_startcol;
pum_anchor_grid = ui_has(kUICmdline) ? -1 : DEFAULT_GRID_HANDLE;
@@ -158,16 +165,20 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
if (pum_external) {
if (array_changed) {
- Array arr = ARRAY_DICT_INIT;
+ Arena arena = ARENA_EMPTY;
+ arena_start(&arena, &ui_ext_fixblk);
+ Array arr = arena_array(&arena, (size_t)size);
for (int i = 0; i < size; i++) {
- Array item = ARRAY_DICT_INIT;
- ADD(item, STRING_OBJ(cstr_to_string((char *)array[i].pum_text)));
- ADD(item, STRING_OBJ(cstr_to_string((char *)array[i].pum_kind)));
- ADD(item, STRING_OBJ(cstr_to_string((char *)array[i].pum_extra)));
- ADD(item, STRING_OBJ(cstr_to_string((char *)array[i].pum_info)));
- ADD(arr, ARRAY_OBJ(item));
+ Array item = arena_array(&arena, 4);
+ ADD_C(item, STRING_OBJ(cstr_as_string((char *)array[i].pum_text)));
+ ADD_C(item, STRING_OBJ(cstr_as_string((char *)array[i].pum_kind)));
+ ADD_C(item, STRING_OBJ(cstr_as_string((char *)array[i].pum_extra)));
+ ADD_C(item, STRING_OBJ(cstr_as_string((char *)array[i].pum_info)));
+ ADD_C(arr, ARRAY_OBJ(item));
}
- ui_call_popupmenu_show(arr, selected, pum_win_row, cursor_col, pum_anchor_grid);
+ ui_call_popupmenu_show(arr, selected, pum_win_row, cursor_col,
+ pum_anchor_grid);
+ arena_mem_free(arena_finish(&arena), &ui_ext_fixblk);
} else {
ui_call_popupmenu_select(selected);
return;
@@ -295,9 +306,8 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
if (pum_rl) {
pum_width = pum_col - pum_scrollbar + 1;
} else {
- assert(Columns - pum_col - pum_scrollbar >= INT_MIN
- && Columns - pum_col - pum_scrollbar <= INT_MAX);
- pum_width = (int)(Columns - pum_col - pum_scrollbar);
+ assert(Columns - pum_col - pum_scrollbar >= 0);
+ pum_width = Columns - pum_col - pum_scrollbar;
}
if ((pum_width > max_width + pum_kind_width + pum_extra_width + 1) && (pum_width > p_pw)) {
@@ -355,12 +365,11 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
// not enough room, will use what we have
if (pum_rl) {
assert(Columns - 1 >= INT_MIN);
- pum_col = (int)(Columns - 1);
+ pum_col = Columns - 1;
} else {
pum_col = 0;
}
- assert(Columns - 1 >= INT_MIN);
- pum_width = (int)(Columns - 1);
+ pum_width = Columns - 1;
} else {
if (max_width > p_pw) {
// truncate
@@ -370,8 +379,8 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
if (pum_rl) {
pum_col = max_width - 1;
} else {
- assert(Columns - max_width >= INT_MIN && Columns - max_width <= INT_MAX);
- pum_col = (int)(Columns - max_width);
+ assert(Columns - max_width >= 0);
+ pum_col = Columns - max_width;
}
pum_width = max_width - pum_scrollbar;
}
@@ -424,25 +433,28 @@ void pum_redraw(void)
grid_assign_handle(&pum_grid);
- pum_grid.zindex = ((State == CMDLINE) ? kZIndexCmdlinePopupMenu : kZIndexPopupMenu);
+ pum_grid.zindex = ((State == MODE_CMDLINE)
+ ? kZIndexCmdlinePopupMenu : kZIndexPopupMenu);
- bool moved = ui_comp_put_grid(&pum_grid, pum_row, pum_col - col_off, pum_height, grid_width,
- false, true);
+ bool moved = ui_comp_put_grid(&pum_grid, pum_row, pum_col - col_off,
+ pum_height, grid_width, false, true);
bool invalid_grid = moved || pum_invalid;
pum_invalid = false;
must_redraw_pum = false;
- if (!pum_grid.chars || pum_grid.Rows != pum_height || pum_grid.Columns != grid_width) {
+ if (!pum_grid.chars
+ || pum_grid.rows != pum_height || pum_grid.cols != grid_width) {
grid_alloc(&pum_grid, pum_height, grid_width, !invalid_grid, false);
- ui_call_grid_resize(pum_grid.handle, pum_grid.Columns, pum_grid.Rows);
+ ui_call_grid_resize(pum_grid.handle, pum_grid.cols, pum_grid.rows);
} else if (invalid_grid) {
grid_invalidate(&pum_grid);
}
if (ui_has(kUIMultigrid)) {
const char *anchor = pum_above ? "SW" : "NW";
int row_off = pum_above ? -pum_height : 0;
- ui_call_win_float_pos(pum_grid.handle, -1, cstr_to_string(anchor), pum_anchor_grid,
- pum_row - row_off, pum_col - col_off, false, pum_grid.zindex);
+ ui_call_win_float_pos(pum_grid.handle, -1, cstr_as_string((char *)anchor),
+ pum_anchor_grid, pum_row - row_off, pum_col - col_off,
+ false, pum_grid.zindex);
}
// Never display more than we have
@@ -503,8 +515,7 @@ void pum_redraw(void)
if (s == NULL) {
s = p;
}
-
- w = ptr2cells(p);
+ w = ptr2cells((char *)p);
if ((*p == NUL) || (*p == TAB) || (*p == '%' && *(p + 1) == '#') || (totwidth + w > pum_width)) {
// Display the text that fits or comes before a Tab.
@@ -512,13 +523,17 @@ void pum_redraw(void)
char_u *st;
char_u saved = *p;
- *p = NUL;
+ if (saved != NUL) {
+ *p = NUL;
+ }
st = (char_u *)transstr((const char *)s, true);
- *p = saved;
+ if (saved != NUL) {
+ *p = saved;
+ }
if (pum_rl) {
- char_u *rt = reverse_text(st);
- char_u *rt_start = rt;
+ char *rt = (char *) reverse_text(st);
+ char *rt_start = rt;
int size = str_dispsize(rt);
if (size > pum_width) {
@@ -535,7 +550,7 @@ void pum_redraw(void)
size++;
}
}
- grid_puts_len(&pum_grid, rt, (int)STRLEN(rt), row,
+ grid_puts_len(&pum_grid, (char_u *)rt, (int)STRLEN(rt), row,
grid_col - size + 1, attr);
xfree(rt_start);
xfree(st);
@@ -707,8 +722,10 @@ static int pum_set_selected(int n, int repeat)
// Skip this when tried twice already.
// Skip this also when there is not much room.
// NOTE: Be very careful not to sync undo!
- if ((pum_array[pum_selected].pum_info != NULL) && (Rows > 10) && (repeat <= 1)
- && (vim_strchr(p_cot, 'p') != NULL)) {
+ if ((pum_array[pum_selected].pum_info != NULL)
+ && (Rows > 10)
+ && (repeat <= 1)
+ && (vim_strchr((char *)p_cot, 'p') != NULL)) {
win_T *curwin_save = curwin;
tabpage_T *curtab_save = curtab;
int res = OK;
@@ -730,8 +747,10 @@ static int pum_set_selected(int n, int repeat)
g_do_tagpreview = 0;
if (curwin->w_p_pvw) {
- if (!resized && (curbuf->b_nwindows == 1) && (curbuf->b_fname == NULL)
- && (curbuf->b_p_bt[0] == 'n') && (curbuf->b_p_bt[2] == 'f')
+ if (!resized
+ && (curbuf->b_nwindows == 1)
+ && (curbuf->b_fname == NULL)
+ && bt_nofile(curbuf)
&& (curbuf->b_p_bh[0] == 'w')) {
// Already a "wipeout" buffer, make it empty.
while (!buf_is_empty(curbuf)) {
@@ -759,13 +778,13 @@ static int pum_set_selected(int n, int repeat)
linenr_T lnum = 0;
for (p = pum_array[pum_selected].pum_info; *p != NUL;) {
- e = vim_strchr(p, '\n');
+ e = (char_u *)vim_strchr((char *)p, '\n');
if (e == NULL) {
- ml_append(lnum++, p, 0, false);
+ ml_append(lnum++, (char *)p, 0, false);
break;
} else {
*e = NUL;
- ml_append(lnum++, p, (int)(e - p + 1), false);
+ ml_append(lnum++, (char *)p, (int)(e - p + 1), false);
*e = '\n';
p = e + 1;
}
@@ -775,7 +794,7 @@ static int pum_set_selected(int n, int repeat)
// text, but no more than 'previewheight' lines.
if (repeat == 0) {
if (lnum > p_pvh) {
- lnum = p_pvh;
+ lnum = (linenr_T)p_pvh;
}
if (curwin->w_height < lnum) {
@@ -941,7 +960,7 @@ void pum_set_event_info(dict_T *dict)
(void)tv_dict_add_bool(dict, S_LEN("scrollbar"), pum_scrollbar ? kBoolVarTrue : kBoolVarFalse);
}
-static int str_dispnsize(char_u *s, int len)
+static int str_dispnsize(char *s, int len)
{
assert(s != NULL);
int size = 0;
@@ -965,7 +984,185 @@ static int str_dispnsize(char_u *s, int len)
return size;
}
-static int str_dispsize(char_u *s)
+static int str_dispsize(char *s)
{
return str_dispnsize(s, MAXCOL);
}
+
+static void pum_position_at_mouse(int min_width)
+{
+ pum_anchor_grid = mouse_grid;
+ if (Rows - mouse_row > pum_size) {
+ // Enough space below the mouse row.
+ pum_above = false;
+ pum_row = mouse_row + 1;
+ if (pum_height > Rows - pum_row) {
+ pum_height = Rows - pum_row;
+ }
+ } else {
+ // Show above the mouse row, reduce height if it does not fit.
+ pum_above = true;
+ pum_row = mouse_row - pum_size;
+ if (pum_row < 0) {
+ pum_height += pum_row;
+ pum_row = 0;
+ }
+ }
+ if (Columns - mouse_col >= pum_base_width || Columns - mouse_col > min_width) {
+ // Enough space to show at mouse column.
+ pum_col = mouse_col;
+ } else {
+ // Not enough space, right align with window.
+ pum_col = Columns - (pum_base_width > min_width ? min_width : pum_base_width);
+ }
+
+ pum_width = Columns - pum_col;
+ if (pum_width > pum_base_width + 1) {
+ pum_width = pum_base_width + 1;
+ }
+}
+
+/// Select the pum entry at the mouse position.
+static void pum_select_mouse_pos(void)
+{
+ if (mouse_grid == pum_grid.handle) {
+ pum_selected = mouse_row;
+ return;
+ } else if (mouse_grid != pum_anchor_grid) {
+ pum_selected = -1;
+ return;
+ }
+
+ int idx = mouse_row - pum_row;
+
+ if (idx < 0 || idx >= pum_size) {
+ pum_selected = -1;
+ } else if (*pum_array[idx].pum_text != NUL) {
+ pum_selected = idx;
+ }
+}
+
+/// Execute the currently selected popup menu item.
+static void pum_execute_menu(vimmenu_T *menu, int mode)
+{
+ int idx = 0;
+ exarg_T ea;
+
+ for (vimmenu_T *mp = menu->children; mp != NULL; mp = mp->next) {
+ if ((mp->modes & mp->enabled & mode) && idx++ == pum_selected) {
+ memset(&ea, 0, sizeof(ea));
+ execute_menu(&ea, mp, -1);
+ break;
+ }
+ }
+}
+
+/// Open the terminal version of the popup menu and don't return until it is closed.
+void pum_show_popupmenu(vimmenu_T *menu)
+{
+ pum_undisplay(true);
+ pum_size = 0;
+ int mode = get_menu_mode_flag();
+
+ for (vimmenu_T *mp = menu->children; mp != NULL; mp = mp->next) {
+ if (menu_is_separator(mp->dname) || (mp->modes & mp->enabled & mode)) {
+ pum_size++;
+ }
+ }
+
+ // When there are only Terminal mode menus, using "popup Edit" results in
+ // pum_size being zero.
+ if (pum_size <= 0) {
+ emsg(e_menuothermode);
+ return;
+ }
+
+ int idx = 0;
+ pumitem_T *array = (pumitem_T *)xcalloc((size_t)pum_size, sizeof(pumitem_T));
+
+ for (vimmenu_T *mp = menu->children; mp != NULL; mp = mp->next) {
+ if (menu_is_separator(mp->dname)) {
+ array[idx++].pum_text = (char_u *)"";
+ } else if (mp->modes & mp->enabled & mode) {
+ array[idx++].pum_text = (char_u *)mp->dname;
+ }
+ }
+
+ pum_array = array;
+ pum_compute_size();
+ pum_scrollbar = 0;
+ pum_height = pum_size;
+ pum_position_at_mouse(20);
+
+ pum_selected = -1;
+ pum_first = 0;
+
+ for (;;) {
+ pum_is_visible = true;
+ pum_is_drawn = true;
+ pum_redraw();
+ setcursor_mayforce(true);
+ ui_flush();
+
+ int c = vgetc();
+ if (c == ESC || c == Ctrl_C) {
+ break;
+ } else if (c == CAR || c == NL) {
+ // enter: select current item, if any, and close
+ pum_execute_menu(menu, mode);
+ break;
+ } else if (c == 'k' || c == K_UP || c == K_MOUSEUP) {
+ // cursor up: select previous item
+ while (pum_selected > 0) {
+ pum_selected--;
+ if (*array[pum_selected].pum_text != NUL) {
+ break;
+ }
+ }
+ } else if (c == 'j' || c == K_DOWN || c == K_MOUSEDOWN) {
+ // cursor down: select next item
+ while (pum_selected < pum_size - 1) {
+ pum_selected++;
+ if (*array[pum_selected].pum_text != NUL) {
+ break;
+ }
+ }
+ } else if (c == K_RIGHTMOUSE) {
+ // Right mouse down: reposition the menu.
+ vungetc(c);
+ break;
+ } else if (c == K_LEFTDRAG || c == K_RIGHTDRAG || c == K_MOUSEMOVE) {
+ // mouse moved: select item in the mouse row
+ pum_select_mouse_pos();
+ } else if (c == K_LEFTMOUSE || c == K_LEFTMOUSE_NM || c == K_RIGHTRELEASE) {
+ // left mouse click: select clicked item, if any, and close;
+ // right mouse release: select clicked item, close if any
+ pum_select_mouse_pos();
+ if (pum_selected >= 0) {
+ pum_execute_menu(menu, mode);
+ break;
+ }
+ if (c == K_LEFTMOUSE || c == K_LEFTMOUSE_NM) {
+ break;
+ }
+ }
+ }
+
+ xfree(array);
+ pum_undisplay(true);
+}
+
+void pum_make_popup(const char *path_name, int use_mouse_pos)
+{
+ if (!use_mouse_pos) {
+ // Hack: set mouse position at the cursor so that the menu pops up
+ // around there.
+ mouse_row = curwin->w_winrow + curwin->w_wrow;
+ mouse_col = curwin->w_wincol + curwin->w_wcol;
+ }
+
+ vimmenu_T *menu = menu_find(path_name);
+ if (menu != NULL) {
+ pum_show_popupmenu(menu);
+ }
+}
diff --git a/src/nvim/pos.h b/src/nvim/pos.h
index d17e27906e..1b7e6273fd 100644
--- a/src/nvim/pos.h
+++ b/src/nvim/pos.h
@@ -1,12 +1,12 @@
#ifndef NVIM_POS_H
#define NVIM_POS_H
-// for INT_MAX, LONG_MAX et al.
-#include <limits.h>
+#include <inttypes.h>
-typedef long linenr_T; // line number type
+/// Line number type
+typedef int32_t linenr_T;
/// Format used to print values which have linenr_T type
-#define PRIdLINENR "ld"
+#define PRIdLINENR PRId32
/// Column number type
typedef int colnr_T;
@@ -15,29 +15,29 @@ typedef int colnr_T;
/// Maximal (invalid) line number
enum { MAXLNUM = 0x7fffffff, };
+
/// Maximal column number
-enum { MAXCOL = INT_MAX, };
-// Minimum line number
+/// MAXCOL used to be INT_MAX, but with 64 bit ints that results in running
+/// out of memory when trying to allocate a very long line.
+enum { MAXCOL = 0x7fffffff, };
+
+/// Minimum line number
enum { MINLNUM = 1, };
-// minimum column number
+
+/// Minimum column number
enum { MINCOL = 1, };
-/*
- * position in file or buffer
- */
+/// position in file or buffer
typedef struct {
- linenr_T lnum; // line number
- colnr_T col; // column number
+ linenr_T lnum; ///< line number
+ colnr_T col; ///< column number
colnr_T coladd;
} pos_T;
-
-/*
- * Same, but without coladd.
- */
+/// position in file or buffer, but without coladd
typedef struct {
- linenr_T lnum; // line number
- colnr_T col; // column number
+ linenr_T lnum; ///< line number
+ colnr_T col; ///< column number
} lpos_T;
#endif // NVIM_POS_H
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index 0196e05455..2138437b29 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -22,6 +22,7 @@
#include "nvim/ex_getln.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
+#include "nvim/highlight_group.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
@@ -39,15 +40,13 @@
#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/strings.h"
-#include "nvim/syntax.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
#include "nvim/window.h"
-
struct dir_stack_T {
struct dir_stack_T *next;
- char_u *dirname;
+ char *dirname;
};
// For each error the next struct is allocated and linked in a list.
@@ -62,23 +61,23 @@ struct qfline_S {
int qf_col; ///< column where the error occurred
int qf_end_col; ///< column when the error has range or zero
int qf_nr; ///< error number
- char_u *qf_module; ///< module name for this error
- char_u *qf_pattern; ///< search pattern for the error
- char_u *qf_text; ///< description of the error
- char_u qf_viscol; ///< set to TRUE if qf_col and qf_end_col is
+ char *qf_module; ///< module name for this error
+ char *qf_pattern; ///< search pattern for the error
+ char *qf_text; ///< description of the error
+ char qf_viscol; ///< set to TRUE if qf_col and qf_end_col is
// screen column
- char_u qf_cleared; ///< set to TRUE if line has been deleted
- char_u qf_type; ///< type of the error (mostly 'E'); 1 for :helpgrep
- char_u qf_valid; ///< valid error message detected
+ char qf_cleared; ///< set to TRUE if line has been deleted
+ char qf_type; ///< type of the error (mostly 'E'); 1 for :helpgrep
+ char qf_valid; ///< valid error message detected
};
// There is a stack of error lists.
#define LISTCOUNT 10
#define INVALID_QFIDX (-1)
+#define INVALID_QFBUFNR (0)
/// Quickfix list type.
-typedef enum
-{
+typedef enum {
QFLT_QUICKFIX, ///< Quickfix list - global list
QFLT_LOCATION, ///< Location list - per window list
QFLT_INTERNAL, ///< Internal - Temporary list used by
@@ -99,15 +98,15 @@ typedef struct qf_list_S {
int qf_count; ///< number of errors (0 means empty list)
int qf_index; ///< current index in the error list
int qf_nonevalid; ///< TRUE if not a single valid entry found
- char_u *qf_title; ///< title derived from the command that created
- ///< the error list or set by setqflist
+ char *qf_title; ///< title derived from the command that created
+ ///< the error list or set by setqflist
typval_T *qf_ctx; ///< context set by setqflist/setloclist
- Callback qftf_cb; ///< 'quickfixtextfunc' callback function
+ Callback qf_qftf_cb; ///< 'quickfixtextfunc' callback function
struct dir_stack_T *qf_dir_stack;
- char_u *qf_directory;
+ char *qf_directory;
struct dir_stack_T *qf_file_stack;
- char_u *qf_currfile;
+ char *qf_currfile;
bool qf_multiline;
bool qf_multiignore;
bool qf_multiscan;
@@ -126,36 +125,37 @@ struct qf_info_S {
int qf_curlist; // current error list
qf_list_T qf_lists[LISTCOUNT];
qfltype_T qfl_type; // type of list
+ int qf_bufnr; // quickfix window buffer number
};
static qf_info_T ql_info; // global quickfix list
static unsigned last_qf_id = 0; // Last Used quickfix list id
-#define FMT_PATTERNS 11 // maximum number of % recognized
+#define FMT_PATTERNS 13 // maximum number of % recognized
// Structure used to hold the info of one part of 'errorformat'
typedef struct efm_S efm_T;
struct efm_S {
regprog_T *prog; // pre-formatted part of 'errorformat'
efm_T *next; // pointer to next (NULL if last)
- char_u addr[FMT_PATTERNS]; // indices of used % patterns
- char_u prefix; // prefix of this format line:
- // 'D' enter directory
- // 'X' leave directory
- // 'A' start of multi-line message
- // 'E' error message
- // 'W' warning message
- // 'I' informational message
- // 'N' note message
- // 'C' continuation line
- // 'Z' end of multi-line message
- // 'G' general, unspecific message
- // 'P' push file (partial) message
- // 'Q' pop/quit file (partial) message
- // 'O' overread (partial) message
- char_u flags; // additional flags given in prefix
- // '-' do not include this line
- // '+' include whole line in message
+ char addr[FMT_PATTERNS]; // indices of used % patterns
+ char prefix; // prefix of this format line:
+ // 'D' enter directory
+ // 'X' leave directory
+ // 'A' start of multi-line message
+ // 'E' error message
+ // 'W' warning message
+ // 'I' informational message
+ // 'N' note message
+ // 'C' continuation line
+ // 'Z' end of multi-line message
+ // 'G' general, unspecific message
+ // 'P' push file (partial) message
+ // 'Q' pop/quit file (partial) message
+ // 'O' overread (partial) message
+ char flags; // additional flags given in prefix
+ // '-' do not include this line
+ // '+' include whole line in message
int conthere; // %> used
};
@@ -178,13 +178,13 @@ enum {
/// State information used to parse lines and add entries to a quickfix/location
/// list.
typedef struct {
- char_u *linebuf;
+ char *linebuf;
size_t linelen;
- char_u *growbuf;
+ char *growbuf;
size_t growbufsiz;
FILE *fd;
typval_T *tv;
- char_u *p_str;
+ char *p_str;
list_T *p_list;
listitem_T *p_li;
buf_T *buf;
@@ -194,55 +194,65 @@ typedef struct {
} qfstate_T;
typedef struct {
- char_u *namebuf;
- char_u *module;
- char_u *errmsg;
+ char *namebuf;
+ char *module;
+ char *errmsg;
size_t errmsglen;
- long lnum;
- long end_lnum;
+ linenr_T lnum;
+ linenr_T end_lnum;
int col;
int end_col;
bool use_viscol;
- char_u *pattern;
+ char *pattern;
int enr;
- char_u type;
+ char type;
bool valid;
} qffields_T;
+/// :vimgrep command arguments
+typedef struct vgr_args_S {
+ long tomatch; ///< maximum number of matches to find
+ char *spat; ///< search pattern
+ int flags; ///< search modifier
+ char **fnames; ///< list of files to search
+ int fcount; ///< number of files
+ regmmatch_T regmatch; ///< compiled search pattern
+ char *qf_title; ///< quickfix list title
+} vgr_args_T;
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "quickfix.c.generated.h"
#endif
-static char_u *e_no_more_items = (char_u *)N_("E553: No more items");
+static char *e_no_more_items = N_("E553: No more items");
// Quickfix window check helper macro
-#define IS_QF_WINDOW(wp) (bt_quickfix(wp->w_buffer) && wp->w_llist_ref == NULL)
+#define IS_QF_WINDOW(wp) (bt_quickfix((wp)->w_buffer) && (wp)->w_llist_ref == NULL)
// Location list window check helper macro
-#define IS_LL_WINDOW(wp) (bt_quickfix(wp->w_buffer) && wp->w_llist_ref != NULL)
+#define IS_LL_WINDOW(wp) (bt_quickfix((wp)->w_buffer) && (wp)->w_llist_ref != NULL)
// Quickfix and location list stack check helper macros
-#define IS_QF_STACK(qi) (qi->qfl_type == QFLT_QUICKFIX)
-#define IS_LL_STACK(qi) (qi->qfl_type == QFLT_LOCATION)
-#define IS_QF_LIST(qfl) (qfl->qfl_type == QFLT_QUICKFIX)
-#define IS_LL_LIST(qfl) (qfl->qfl_type == QFLT_LOCATION)
+#define IS_QF_STACK(qi) ((qi)->qfl_type == QFLT_QUICKFIX)
+#define IS_LL_STACK(qi) ((qi)->qfl_type == QFLT_LOCATION)
+#define IS_QF_LIST(qfl) ((qfl)->qfl_type == QFLT_QUICKFIX)
+#define IS_LL_LIST(qfl) ((qfl)->qfl_type == QFLT_LOCATION)
//
// Return location list for window 'wp'
// For location list window, return the referenced location list
//
-#define GET_LOC_LIST(wp) (IS_LL_WINDOW(wp) ? wp->w_llist_ref : wp->w_llist)
+#define GET_LOC_LIST(wp) (IS_LL_WINDOW(wp) ? (wp)->w_llist_ref : (wp)->w_llist)
// Macro to loop through all the items in a quickfix list
// Quickfix item index starts from 1, so i below starts at 1
#define FOR_ALL_QFL_ITEMS(qfl, qfp, i) \
- for (i = 1, qfp = qfl->qf_start; /* NOLINT(readability/braces) */ \
- !got_int && i <= qfl->qf_count && qfp != NULL; \
- i++, qfp = qfp->qf_next)
-
+ for ((i) = 1, (qfp) = (qfl)->qf_start; /* NOLINT(readability/braces) */ \
+ !got_int && (i) <= (qfl)->qf_count && (qfp) != NULL; \
+ (i)++, (qfp) = (qfp)->qf_next)
// Looking up a buffer can be slow if there are many. Remember the last one
// to make this a lot faster if there are multiple matches in the same file.
-static char_u *qf_last_bufname = NULL;
+static char *qf_last_bufname = NULL;
static bufref_T qf_last_bufref = { NULL, 0, 0 };
static char *e_current_quickfix_list_was_changed =
@@ -279,7 +289,7 @@ static int qf_init_process_nextline(qf_list_T *qfl, efm_T *fmt_first, qfstate_T
(*fields->namebuf || qfl->qf_directory != NULL)
? fields->namebuf
: ((qfl->qf_currfile != NULL && fields->valid)
- ? qfl->qf_currfile : (char_u *)NULL),
+ ? qfl->qf_currfile : NULL),
fields->module,
0,
fields->errmsg,
@@ -305,8 +315,8 @@ static int qf_init_process_nextline(qf_list_T *qfl, efm_T *fmt_first, qfstate_T
/// @params enc If non-NULL, encoding used to parse errors
///
/// @returns -1 for error, number of errors for success.
-int qf_init(win_T *wp, const char_u *restrict efile, char_u *restrict errorformat, int newlist,
- const char_u *restrict qf_title, char_u *restrict enc)
+int qf_init(win_T *wp, const char *restrict efile, char *restrict errorformat, int newlist,
+ const char *restrict qf_title, char *restrict enc)
{
qf_info_T *qi = &ql_info;
@@ -314,37 +324,41 @@ int qf_init(win_T *wp, const char_u *restrict efile, char_u *restrict errorforma
qi = ll_get_or_alloc_list(wp);
}
- return qf_init_ext(qi, qi->qf_curlist, efile, curbuf, NULL, errorformat,
- newlist, (linenr_T)0, (linenr_T)0, qf_title, enc);
+ return qf_init_ext(qi, qi->qf_curlist, (char *)efile, curbuf, NULL, errorformat,
+ newlist, (linenr_T)0, (linenr_T)0, (char *)qf_title, enc);
}
// Maximum number of bytes allowed per line while reading an errorfile.
static const size_t LINE_MAXLEN = 4096;
+/// Patterns used. Keep in sync with qf_parse_fmt[].
static struct fmtpattern {
- char_u convchar;
+ char convchar;
char *pattern;
} fmt_pat[FMT_PATTERNS] =
{
- { 'f', ".\\+" }, // only used when at end
- { 'n', "\\d\\+" },
- { 'l', "\\d\\+" },
- { 'c', "\\d\\+" },
- { 't', "." },
- { 'm', ".\\+" },
- { 'r', ".*" },
- { 'p', "[- .]*"}, // NOLINT(whitespace/tab)
- { 'v', "\\d\\+" },
- { 's', ".\\+" },
- { 'o', ".\\+" }
+ { 'f', ".\\+" }, // only used when at end
+ { 'n', "\\d\\+" }, // 1
+ { 'l', "\\d\\+" }, // 2
+ { 'e', "\\d\\+" }, // 3
+ { 'c', "\\d\\+" }, // 4
+ { 'k', "\\d\\+" }, // 5
+ { 't', "." }, // 6
+#define FMT_PATTERN_M 7
+ { 'm', ".\\+" }, // 7
+#define FMT_PATTERN_R 8
+ { 'r', ".*" }, // 8
+ { 'p', "[- \t.]*" }, // 9
+ { 'v', "\\d\\+" }, // 10
+ { 's', ".\\+" }, // 11
+ { 'o', ".\\+" } // 12
};
/// Convert an errorformat pattern to a regular expression pattern.
/// See fmt_pat definition above for the list of supported patterns. The
/// pattern specifier is supplied in "efmpat". The converted pattern is stored
/// in "regpat". Returns a pointer to the location after the pattern.
-static char_u *efmpat_to_regpat(const char_u *efmpat, char_u *regpat, efm_T *efminfo, int idx,
- int round)
+static char *efmpat_to_regpat(const char *efmpat, char *regpat, efm_T *efminfo, int idx, int round)
FUNC_ATTR_NONNULL_ALL
{
if (efminfo->addr[idx]) {
@@ -352,14 +366,14 @@ static char_u *efmpat_to_regpat(const char_u *efmpat, char_u *regpat, efm_T *efm
semsg(_("E372: Too many %%%c in format string"), *efmpat);
return NULL;
}
- if ((idx && idx < 6
- && vim_strchr((char_u *)"DXOPQ", efminfo->prefix) != NULL)
- || (idx == 6
- && vim_strchr((char_u *)"OPQ", efminfo->prefix) == NULL)) {
+ if ((idx && idx < FMT_PATTERN_R
+ && vim_strchr("DXOPQ", efminfo->prefix) != NULL)
+ || (idx == FMT_PATTERN_R
+ && vim_strchr("OPQ", efminfo->prefix) == NULL)) {
semsg(_("E373: Unexpected %%%c in format string"), *efmpat);
return NULL;
}
- efminfo->addr[idx] = (char_u)++ round;
+ efminfo->addr[idx] = (char)++round;
*regpat++ = '\\';
*regpat++ = '(';
#ifdef BACKSLASH_IN_FILENAME
@@ -387,7 +401,7 @@ static char_u *efmpat_to_regpat(const char_u *efmpat, char_u *regpat, efm_T *efm
regpat += 4;
}
} else {
- char_u *srcptr = (char_u *)fmt_pat[idx].pattern;
+ char *srcptr = fmt_pat[idx].pattern;
while ((*regpat = *srcptr++) != NUL) {
regpat++;
}
@@ -400,10 +414,10 @@ static char_u *efmpat_to_regpat(const char_u *efmpat, char_u *regpat, efm_T *efm
/// Convert a scanf like format in 'errorformat' to a regular expression.
/// Returns a pointer to the location after the pattern.
-static char_u *scanf_fmt_to_regpat(const char_u **pefmp, const char_u *efm, int len, char_u *regpat)
+static char *scanf_fmt_to_regpat(const char **pefmp, const char *efm, int len, char *regpat)
FUNC_ATTR_NONNULL_ALL
{
- const char_u *efmp = *pefmp;
+ const char *efmp = *pefmp;
if (*efmp == '[' || *efmp == '\\') {
if ((*regpat++ = *efmp) == '[') { // %*[^a-z0-9] etc.
@@ -412,8 +426,7 @@ static char_u *scanf_fmt_to_regpat(const char_u **pefmp, const char_u *efm, int
}
if (efmp < efm + len) {
*regpat++ = *++efmp; // could be ']'
- while (efmp < efm + len && (*regpat++ = *++efmp) != ']') {
- }
+ while (efmp < efm + len && (*regpat++ = *++efmp) != ']') {}
if (efmp == efm + len) {
emsg(_("E374: Missing ] in format string"));
return NULL;
@@ -435,13 +448,13 @@ static char_u *scanf_fmt_to_regpat(const char_u **pefmp, const char_u *efm, int
}
/// Analyze/parse an errorformat prefix.
-static const char_u *efm_analyze_prefix(const char_u *efmp, efm_T *efminfo)
+static const char *efm_analyze_prefix(const char *efmp, efm_T *efminfo)
FUNC_ATTR_NONNULL_ALL
{
- if (vim_strchr((char_u *)"+-", *efmp) != NULL) {
+ if (vim_strchr("+-", *efmp) != NULL) {
efminfo->flags = *efmp++;
}
- if (vim_strchr((char_u *)"DXAEWINCZGOPQ", *efmp) != NULL) {
+ if (vim_strchr("DXAEWINCZGOPQ", *efmp) != NULL) {
efminfo->prefix = *efmp;
} else {
semsg(_("E376: Invalid %%%c in format string prefix"), *efmp);
@@ -451,16 +464,15 @@ static const char_u *efm_analyze_prefix(const char_u *efmp, efm_T *efminfo)
return efmp;
}
-
// Converts a 'errorformat' string to regular expression pattern
-static int efm_to_regpat(const char_u *efm, int len, efm_T *fmt_ptr, char_u *regpat)
+static int efm_to_regpat(const char *efm, int len, efm_T *fmt_ptr, char *regpat)
FUNC_ATTR_NONNULL_ALL
{
// Build regexp pattern from current 'errorformat' option
- char_u *ptr = regpat;
+ char *ptr = regpat;
*ptr++ = '^';
int round = 0;
- for (const char_u *efmp = efm; efmp < efm + len; efmp++) {
+ for (const char *efmp = efm; efmp < efm + len; efmp++) {
if (*efmp == '%') {
efmp++;
int idx;
@@ -470,7 +482,7 @@ static int efm_to_regpat(const char_u *efm, int len, efm_T *fmt_ptr, char_u *reg
}
}
if (idx < FMT_PATTERNS) {
- ptr = efmpat_to_regpat(efmp, ptr, fmt_ptr, idx, round);
+ ptr = efmpat_to_regpat((char *)efmp, ptr, fmt_ptr, idx, round);
if (ptr == NULL) {
return FAIL;
}
@@ -481,7 +493,7 @@ static int efm_to_regpat(const char_u *efm, int len, efm_T *fmt_ptr, char_u *reg
if (ptr == NULL) {
return FAIL;
}
- } else if (vim_strchr((char_u *)"%\\.^$~[", *efmp) != NULL) {
+ } else if (vim_strchr("%\\.^$~[", *efmp) != NULL) {
*ptr++ = *efmp; // regexp magic characters
} else if (*efmp == '#') {
*ptr++ = '*';
@@ -501,7 +513,7 @@ static int efm_to_regpat(const char_u *efm, int len, efm_T *fmt_ptr, char_u *reg
} else { // copy normal character
if (*efmp == '\\' && efmp + 1 < efm + len) {
efmp++;
- } else if (vim_strchr((char_u *)".*^$~[", *efmp) != NULL) {
+ } else if (vim_strchr(".*^$~[", *efmp) != NULL) {
*ptr++ = '\\'; // escape regexp atoms
}
if (*efmp) {
@@ -533,7 +545,7 @@ static void free_efm_list(efm_T **efm_first)
/// Compute the size of the buffer used to convert a 'errorformat' pattern into
/// a regular expression pattern.
-static size_t efm_regpat_bufsz(char_u *efm)
+static size_t efm_regpat_bufsz(char *efm)
{
size_t sz;
@@ -551,7 +563,7 @@ static size_t efm_regpat_bufsz(char_u *efm)
}
/// Return the length of a 'errorformat' option part (separated by ",").
-static int efm_option_part_len(char_u *efm)
+static int efm_option_part_len(char *efm)
{
int len;
@@ -567,7 +579,7 @@ static int efm_option_part_len(char_u *efm)
/// Parse the 'errorformat' option. Multiple parts in the 'errorformat' option
/// are parsed and converted to regular expressions. Returns information about
/// the parsed 'errorformat' option.
-static efm_T *parse_efm_option(char_u *efm)
+static efm_T *parse_efm_option(char *efm)
{
efm_T *fmt_ptr = NULL;
efm_T *fmt_first = NULL;
@@ -576,7 +588,7 @@ static efm_T *parse_efm_option(char_u *efm)
// Get some space to modify the format string into.
size_t sz = efm_regpat_bufsz(efm);
- char_u *fmtstr = xmalloc(sz);
+ char *fmtstr = xmalloc(sz);
while (efm[0] != NUL) {
// Allocate a new eformat structure and put it at the end of the list
@@ -598,7 +610,7 @@ static efm_T *parse_efm_option(char_u *efm)
goto parse_efm_error;
}
// Advance to next part
- efm = skip_to_option_part(efm + len); // skip comma and spaces
+ efm = (char *)skip_to_option_part((char_u *)efm + len); // skip comma and spaces
}
if (fmt_first == NULL) { // nothing found
@@ -617,7 +629,7 @@ parse_efm_end:
}
/// Allocate more memory for the line buffer used for parsing lines.
-static char_u *qf_grow_linebuf(qfstate_T *state, size_t newsz)
+static char *qf_grow_linebuf(qfstate_T *state, size_t newsz)
{
// If the line exceeds LINE_MAXLEN exclude the last
// byte since it's not a NL character.
@@ -636,8 +648,8 @@ static char_u *qf_grow_linebuf(qfstate_T *state, size_t newsz)
static int qf_get_next_str_line(qfstate_T *state)
{
// Get the next line from the supplied string
- char_u *p_str = state->p_str;
- char_u *p;
+ char *p_str = state->p_str;
+ char *p;
size_t len;
if (*p_str == NUL) { // Reached the end of the string
@@ -654,7 +666,7 @@ static int qf_get_next_str_line(qfstate_T *state)
if (len > IOSIZE - 2) {
state->linebuf = qf_grow_linebuf(state, len);
} else {
- state->linebuf = IObuff;
+ state->linebuf = (char *)IObuff;
state->linelen = len;
}
memcpy(state->linebuf, p_str, state->linelen);
@@ -690,7 +702,7 @@ static int qf_get_next_list_line(qfstate_T *state)
if (len > IOSIZE - 2) {
state->linebuf = qf_grow_linebuf(state, len);
} else {
- state->linebuf = IObuff;
+ state->linebuf = (char *)IObuff;
state->linelen = len;
}
@@ -704,21 +716,21 @@ static int qf_get_next_list_line(qfstate_T *state)
/// Get the next string from state->buf.
static int qf_get_next_buf_line(qfstate_T *state)
{
- char_u *p_buf = NULL;
+ char *p_buf = NULL;
size_t len;
// Get the next line from the supplied buffer
if (state->buflnum > state->lnumlast) {
return QF_END_OF_INPUT;
}
- p_buf = ml_get_buf(state->buf, state->buflnum, false);
+ p_buf = (char *)ml_get_buf(state->buf, state->buflnum, false);
state->buflnum += 1;
len = STRLEN(p_buf);
if (len > IOSIZE - 2) {
state->linebuf = qf_grow_linebuf(state, len);
} else {
- state->linebuf = IObuff;
+ state->linebuf = (char *)IObuff;
state->linelen = len;
}
STRLCPY(state->linebuf, p_buf, state->linelen + 1);
@@ -757,7 +769,7 @@ retry:
for (;;) {
errno = 0;
- if (fgets((char *)state->growbuf + growbuflen,
+ if (fgets(state->growbuf + growbuflen,
(int)(state->growbufsiz - growbuflen), state->fd) == NULL) {
if (errno == EINTR) {
continue;
@@ -797,19 +809,20 @@ retry:
state->linebuf = state->growbuf;
state->linelen = growbuflen;
} else {
- state->linebuf = IObuff;
+ state->linebuf = (char *)IObuff;
}
// Convert a line if it contains a non-ASCII character
- if (state->vc.vc_type != CONV_NONE && has_non_ascii(state->linebuf)) {
- char_u *line = string_convert(&state->vc, state->linebuf, &state->linelen);
+ if (state->vc.vc_type != CONV_NONE && has_non_ascii((char_u *)state->linebuf)) {
+ char *line = (char *)string_convert(&state->vc, (char_u *)state->linebuf, &state->linelen);
if (line != NULL) {
if (state->linelen < IOSIZE) {
STRLCPY(state->linebuf, line, state->linelen + 1);
xfree(line);
} else {
xfree(state->growbuf);
- state->linebuf = state->growbuf = line;
+ state->linebuf = line;
+ state->growbuf = line;
state->growbufsiz = state->linelen < LINE_MAXLEN
? state->linelen : LINE_MAXLEN;
}
@@ -854,7 +867,7 @@ static int qf_get_nextline(qfstate_T *state)
#endif
}
- remove_bom(state->linebuf);
+ remove_bom((char_u *)state->linebuf);
return QF_OK;
}
@@ -889,12 +902,12 @@ static qf_list_T *qf_get_list(qf_info_T *qi, int idx)
/// Parse a line and get the quickfix fields.
/// Return the QF_ status.
-static int qf_parse_line(qf_list_T *qfl, char_u *linebuf, size_t linelen, efm_T *fmt_first,
+static int qf_parse_line(qf_list_T *qfl, char *linebuf, size_t linelen, efm_T *fmt_first,
qffields_T *fields)
{
efm_T *fmt_ptr;
int idx = 0;
- char_u *tail = NULL;
+ char *tail = NULL;
int status;
restofline:
@@ -911,7 +924,7 @@ restofline:
// match or no match.
fields->valid = true;
for (; fmt_ptr != NULL; fmt_ptr = fmt_ptr->next) {
- idx = fmt_ptr->prefix;
+ idx = (char_u)fmt_ptr->prefix;
status = qf_parse_get_fields(linebuf, linelen, fmt_ptr, fields,
qfl->qf_multiline, qfl->qf_multiscan,
&tail);
@@ -945,16 +958,16 @@ restofline:
fmt_start = fmt_ptr;
}
- if (vim_strchr((char_u *)"AEWIN", idx) != NULL) {
+ if (vim_strchr("AEWIN", idx) != NULL) {
qfl->qf_multiline = true; // start of a multi-line message
qfl->qf_multiignore = false; // reset continuation
- } else if (vim_strchr((char_u *)"CZ", idx) != NULL) {
+ } else if (vim_strchr("CZ", idx) != NULL) {
// continuation of multi-line msg
status = qf_parse_multiline_pfx(idx, qfl, fields);
if (status != QF_OK) {
return status;
}
- } else if (vim_strchr((char_u *)"OPQ", idx) != NULL) {
+ } else if (vim_strchr("OPQ", idx) != NULL) {
// global file names
status = qf_parse_file_pfx(idx, fields, qfl, tail);
if (status == QF_MULTISCAN) {
@@ -996,17 +1009,17 @@ static void qf_free_fields(qffields_T *pfields)
// Setup the state information used for parsing lines and populating a
// quickfix list.
-static int qf_setup_state(qfstate_T *pstate, char_u *restrict enc, const char_u *restrict efile,
+static int qf_setup_state(qfstate_T *pstate, char *restrict enc, const char *restrict efile,
typval_T *tv, buf_T *buf, linenr_T lnumfirst, linenr_T lnumlast)
FUNC_ATTR_NONNULL_ARG(1)
{
pstate->vc.vc_type = CONV_NONE;
if (enc != NULL && *enc != NUL) {
- convert_setup(&pstate->vc, enc, p_enc);
+ convert_setup(&pstate->vc, (char_u *)enc, p_enc);
}
if (efile != NULL
- && (pstate->fd = os_fopen((const char *)efile, "r")) == NULL) {
+ && (pstate->fd = os_fopen(efile, "r")) == NULL) {
semsg(_(e_openerrf), efile);
return FAIL;
}
@@ -1053,9 +1066,9 @@ static void qf_cleanup_state(qfstate_T *pstate)
/// @param lnumlast last line number to use
///
/// @return -1 for error, number of errors for success.
-static int qf_init_ext(qf_info_T *qi, int qf_idx, const char_u *restrict efile, buf_T *buf,
- typval_T *tv, char_u *restrict errorformat, bool newlist, linenr_T lnumfirst,
- linenr_T lnumlast, const char_u *restrict qf_title, char_u *restrict enc)
+static int qf_init_ext(qf_info_T *qi, int qf_idx, const char *restrict efile, buf_T *buf,
+ typval_T *tv, char *restrict errorformat, bool newlist, linenr_T lnumfirst,
+ linenr_T lnumlast, const char *restrict qf_title, char *restrict enc)
FUNC_ATTR_NONNULL_ARG(1)
{
qf_list_T *qfl;
@@ -1064,8 +1077,8 @@ static int qf_init_ext(qf_info_T *qi, int qf_idx, const char_u *restrict efile,
qfline_T *old_last = NULL;
bool adding = false;
static efm_T *fmt_first = NULL;
- char_u *efm;
- static char_u *last_efm = NULL;
+ char *efm;
+ static char *last_efm = NULL;
int retval = -1; // default: return error flag
int status;
@@ -1073,8 +1086,7 @@ static int qf_init_ext(qf_info_T *qi, int qf_idx, const char_u *restrict efile,
XFREE_CLEAR(qf_last_bufname);
qf_alloc_fields(&fields);
- if (qf_setup_state(&state, enc, efile, tv, buf,
- lnumfirst, lnumlast) == FAIL) {
+ if (qf_setup_state(&state, enc, efile, tv, buf, lnumfirst, lnumlast) == FAIL) {
goto qf_init_end;
}
@@ -1092,10 +1104,9 @@ static int qf_init_ext(qf_info_T *qi, int qf_idx, const char_u *restrict efile,
}
}
-
// Use the local value of 'errorformat' if it's set.
if (errorformat == p_efm && tv == NULL && buf && *buf->b_p_efm != NUL) {
- efm = buf->b_p_efm;
+ efm = (char *)buf->b_p_efm;
} else {
efm = errorformat;
}
@@ -1110,7 +1121,7 @@ static int qf_init_ext(qf_info_T *qi, int qf_idx, const char_u *restrict efile,
// parse the current 'efm'
fmt_first = parse_efm_option(efm);
if (fmt_first != NULL) {
- last_efm = vim_strsave(efm);
+ last_efm = xstrdup(efm);
}
}
@@ -1173,14 +1184,14 @@ qf_init_end:
/// Set the title of the specified quickfix list. Frees the previous title.
/// Prepends ':' to the title.
-static void qf_store_title(qf_list_T *qfl, const char_u *title)
+static void qf_store_title(qf_list_T *qfl, const char *title)
FUNC_ATTR_NONNULL_ARG(1)
{
XFREE_CLEAR(qfl->qf_title);
if (title != NULL) {
size_t len = STRLEN(title) + 1;
- char_u *p = xmallocz(len);
+ char *p = xmallocz(len);
qfl->qf_title = p;
STRLCPY(p, title, len + 1);
@@ -1191,11 +1202,11 @@ static void qf_store_title(qf_list_T *qfl, const char_u *title)
/// that created the quickfix list with the ":" prefix.
/// Create a quickfix list title string by prepending ":" to a user command.
/// Returns a pointer to a static buffer with the title.
-static char_u *qf_cmdtitle(char_u *cmd)
+static char *qf_cmdtitle(char *cmd)
{
- static char_u qftitle_str[IOSIZE];
+ static char qftitle_str[IOSIZE];
- snprintf((char *)qftitle_str, IOSIZE, ":%s", (char *)cmd);
+ snprintf((char *)qftitle_str, IOSIZE, ":%s", cmd);
return qftitle_str;
}
@@ -1210,7 +1221,7 @@ static qf_list_T *qf_get_curlist(qf_info_T *qi)
/// Prepare for adding a new quickfix list. If the current list is in the
/// middle of the stack, then all the following lists are freed and then
/// the new list is added.
-static void qf_new_list(qf_info_T *qi, const char_u *qf_title)
+static void qf_new_list(qf_info_T *qi, const char *qf_title)
{
int i;
qf_list_T *qfl;
@@ -1244,22 +1255,22 @@ static void qf_new_list(qf_info_T *qi, const char_u *qf_title)
/// Return the matched value in "fields->namebuf".
static int qf_parse_fmt_f(regmatch_T *rmp, int midx, qffields_T *fields, int prefix)
{
- char_u c;
+ char c;
if (rmp->startp[midx] == NULL || rmp->endp[midx] == NULL) {
return QF_FAIL;
}
// Expand ~/file and $HOME/file to full path.
- c = *rmp->endp[midx];
+ c = (char)(*rmp->endp[midx]);
*rmp->endp[midx] = NUL;
- expand_env(rmp->startp[midx], fields->namebuf, CMDBUFFSIZE);
- *rmp->endp[midx] = c;
+ expand_env(rmp->startp[midx], (char_u *)fields->namebuf, CMDBUFFSIZE);
+ *rmp->endp[midx] = (char_u)c;
// For separate filename patterns (%O, %P and %Q), the specified file
// should exist.
- if (vim_strchr((char_u *)"OPQ", prefix) != NULL
- && !os_path_exists(fields->namebuf)) {
+ if (vim_strchr("OPQ", prefix) != NULL
+ && !os_path_exists((char_u *)fields->namebuf)) {
return QF_FAIL;
}
@@ -1277,14 +1288,25 @@ static int qf_parse_fmt_n(regmatch_T *rmp, int midx, qffields_T *fields)
return QF_OK;
}
-/// Parse the match for line number (%l') pattern in regmatch.
+/// Parse the match for line number ('%l') pattern in regmatch.
/// Return the matched value in "fields->lnum".
static int qf_parse_fmt_l(regmatch_T *rmp, int midx, qffields_T *fields)
{
if (rmp->startp[midx] == NULL) {
return QF_FAIL;
}
- fields->lnum = atol((char *)rmp->startp[midx]);
+ fields->lnum = (linenr_T)atol((char *)rmp->startp[midx]);
+ return QF_OK;
+}
+
+/// Parse the match for end line number ('%e') pattern in regmatch.
+/// Return the matched value in "fields->end_lnum".
+static int qf_parse_fmt_e(regmatch_T *rmp, int midx, qffields_T *fields)
+{
+ if (rmp->startp[midx] == NULL) {
+ return QF_FAIL;
+ }
+ fields->end_lnum = (linenr_T)atol((char *)rmp->startp[midx]);
return QF_OK;
}
@@ -1299,6 +1321,17 @@ static int qf_parse_fmt_c(regmatch_T *rmp, int midx, qffields_T *fields)
return QF_OK;
}
+/// Parse the match for end line number ('%e') pattern in regmatch.
+/// Return the matched value in "fields->end_lnum".
+static int qf_parse_fmt_k(regmatch_T *rmp, int midx, qffields_T *fields)
+{
+ if (rmp->startp[midx] == NULL) {
+ return QF_FAIL;
+ }
+ fields->end_col = (int)atol((char *)rmp->startp[midx]);
+ return QF_OK;
+}
+
/// Parse the match for error type ('%t') pattern in regmatch.
/// Return the matched value in "fields->type".
static int qf_parse_fmt_t(regmatch_T *rmp, int midx, qffields_T *fields)
@@ -1306,13 +1339,13 @@ static int qf_parse_fmt_t(regmatch_T *rmp, int midx, qffields_T *fields)
if (rmp->startp[midx] == NULL) {
return QF_FAIL;
}
- fields->type = *rmp->startp[midx];
+ fields->type = (char)(*rmp->startp[midx]);
return QF_OK;
}
/// Parse the match for '%+' format pattern. The whole matching line is included
/// in the error string. Return the matched line in "fields->errmsg".
-static void qf_parse_fmt_plus(const char_u *linebuf, size_t linelen, qffields_T *fields)
+static void qf_parse_fmt_plus(const char *linebuf, size_t linelen, qffields_T *fields)
FUNC_ATTR_NONNULL_ALL
{
if (linelen >= fields->errmsglen) {
@@ -1344,12 +1377,12 @@ static int qf_parse_fmt_m(regmatch_T *rmp, int midx, qffields_T *fields)
/// Parse the match for rest of a single-line file message ('%r') pattern.
/// Return the matched value in "tail".
-static int qf_parse_fmt_r(regmatch_T *rmp, int midx, char_u **tail)
+static int qf_parse_fmt_r(regmatch_T *rmp, int midx, char **tail)
{
if (rmp->startp[midx] == NULL) {
return QF_FAIL;
}
- *tail = rmp->startp[midx];
+ *tail = (char *)rmp->startp[midx];
return QF_OK;
}
@@ -1357,13 +1390,13 @@ static int qf_parse_fmt_r(regmatch_T *rmp, int midx, char_u **tail)
/// Return the matched value in "fields->col".
static int qf_parse_fmt_p(regmatch_T *rmp, int midx, qffields_T *fields)
{
- char_u *match_ptr;
+ char *match_ptr;
if (rmp->startp[midx] == NULL || rmp->endp[midx] == NULL) {
return QF_FAIL;
}
fields->col = 0;
- for (match_ptr = rmp->startp[midx]; match_ptr != rmp->endp[midx];
+ for (match_ptr = (char *)rmp->startp[midx]; (char_u *)match_ptr != rmp->endp[midx];
match_ptr++) {
fields->col++;
if (*match_ptr == TAB) {
@@ -1431,14 +1464,17 @@ static int qf_parse_fmt_o(regmatch_T *rmp, int midx, qffields_T *fields)
/// 'errorformat' format pattern parser functions.
/// The '%f' and '%r' formats are parsed differently from other formats.
/// See qf_parse_match() for details.
+/// Keep in sync with fmt_pat[].
static int (*qf_parse_fmt[FMT_PATTERNS])(regmatch_T *, int, qffields_T *) = {
- NULL,
+ NULL, // %f
qf_parse_fmt_n,
qf_parse_fmt_l,
+ qf_parse_fmt_e,
qf_parse_fmt_c,
+ qf_parse_fmt_k,
qf_parse_fmt_t,
qf_parse_fmt_m,
- NULL,
+ NULL, // %r
qf_parse_fmt_p,
qf_parse_fmt_v,
qf_parse_fmt_s,
@@ -1449,10 +1485,10 @@ static int (*qf_parse_fmt[FMT_PATTERNS])(regmatch_T *, int, qffields_T *) = {
/// fmt_ptr contains the 'efm' format specifiers/prefixes that have a match.
/// Returns QF_OK if all the matches are successfully parsed. On failure,
/// returns QF_FAIL or QF_NOMEM.
-static int qf_parse_match(char_u *linebuf, size_t linelen, efm_T *fmt_ptr, regmatch_T *regmatch,
- qffields_T *fields, int qf_multiline, int qf_multiscan, char_u **tail)
+static int qf_parse_match(char *linebuf, size_t linelen, efm_T *fmt_ptr, regmatch_T *regmatch,
+ qffields_T *fields, int qf_multiline, int qf_multiscan, char **tail)
{
- char_u idx = fmt_ptr->prefix;
+ char idx = fmt_ptr->prefix;
int i;
int midx;
int status;
@@ -1460,7 +1496,7 @@ static int qf_parse_match(char_u *linebuf, size_t linelen, efm_T *fmt_ptr, regma
if ((idx == 'C' || idx == 'Z') && !qf_multiline) {
return QF_FAIL;
}
- if (vim_strchr((char_u *)"EWIN", idx) != NULL) {
+ if (vim_strchr("EWIN", idx) != NULL) {
fields->type = idx;
} else {
fields->type = 0;
@@ -1474,13 +1510,13 @@ static int qf_parse_match(char_u *linebuf, size_t linelen, efm_T *fmt_ptr, regma
midx = (int)fmt_ptr->addr[i];
if (i == 0 && midx > 0) { // %f
status = qf_parse_fmt_f(regmatch, midx, fields, idx);
- } else if (i == 5) {
+ } else if (i == FMT_PATTERN_M) {
if (fmt_ptr->flags == '+' && !qf_multiscan) { // %+
qf_parse_fmt_plus(linebuf, linelen, fields);
} else if (midx > 0) { // %m
status = qf_parse_fmt_m(regmatch, midx, fields);
}
- } else if (i == 6 && midx > 0) { // %r
+ } else if (i == FMT_PATTERN_R && midx > 0) { // %r
status = qf_parse_fmt_r(regmatch, midx, tail);
} else if (midx > 0) { // others
status = (qf_parse_fmt[i])(regmatch, midx, fields);
@@ -1498,14 +1534,14 @@ static int qf_parse_match(char_u *linebuf, size_t linelen, efm_T *fmt_ptr, regma
/// 'fmt_ptr->prog' and return the matching values in 'fields'.
/// Returns QF_OK if the efm format matches completely and the fields are
/// successfully copied. Otherwise returns QF_FAIL or QF_NOMEM.
-static int qf_parse_get_fields(char_u *linebuf, size_t linelen, efm_T *fmt_ptr, qffields_T *fields,
- int qf_multiline, int qf_multiscan, char_u **tail)
+static int qf_parse_get_fields(char *linebuf, size_t linelen, efm_T *fmt_ptr, qffields_T *fields,
+ int qf_multiline, int qf_multiscan, char **tail)
{
regmatch_T regmatch;
int status = QF_FAIL;
int r;
- if (qf_multiscan && vim_strchr((char_u *)"OPQ", fmt_ptr->prefix) == NULL) {
+ if (qf_multiscan && vim_strchr("OPQ", fmt_ptr->prefix) == NULL) {
return QF_FAIL;
}
@@ -1559,13 +1595,12 @@ static int qf_parse_dir_pfx(int idx, qffields_T *fields, qf_list_T *qfl)
}
/// Parse global file name error format prefixes (%O, %P and %Q).
-static int qf_parse_file_pfx(int idx, qffields_T *fields, qf_list_T *qfl, char_u *tail)
+static int qf_parse_file_pfx(int idx, qffields_T *fields, qf_list_T *qfl, char *tail)
{
fields->valid = false;
- if (*fields->namebuf == NUL || os_path_exists(fields->namebuf)) {
+ if (*fields->namebuf == NUL || os_path_exists((char_u *)fields->namebuf)) {
if (*fields->namebuf && idx == 'P') {
- qfl->qf_currfile = qf_push_dir(fields->namebuf, &qfl->qf_file_stack,
- true);
+ qfl->qf_currfile = qf_push_dir(fields->namebuf, &qfl->qf_file_stack, true);
} else if (idx == 'Q') {
qfl->qf_currfile = qf_pop_dir(&qfl->qf_file_stack);
}
@@ -1582,7 +1617,7 @@ static int qf_parse_file_pfx(int idx, qffields_T *fields, qf_list_T *qfl, char_u
/// Parse a non-error line (a line which doesn't match any of the error
/// format in 'efm').
-static int qf_parse_line_nomatch(char_u *linebuf, size_t linelen, qffields_T *fields)
+static int qf_parse_line_nomatch(char *linebuf, size_t linelen, qffields_T *fields)
{
fields->namebuf[0] = NUL; // no match found, remove file name
fields->lnum = 0; // don't jump to this line
@@ -1625,10 +1660,16 @@ static int qf_parse_multiline_pfx(int idx, qf_list_T *qfl, qffields_T *fields)
if (!qfprev->qf_lnum) {
qfprev->qf_lnum = fields->lnum;
}
+ if (!qfprev->qf_end_lnum) {
+ qfprev->qf_end_lnum = fields->end_lnum;
+ }
if (!qfprev->qf_col) {
qfprev->qf_col = fields->col;
qfprev->qf_viscol = fields->use_viscol;
}
+ if (!qfprev->qf_end_col) {
+ qfprev->qf_end_col = fields->end_col;
+ }
if (!qfprev->qf_fnum) {
qfprev->qf_fnum = qf_get_fnum(qfl, qfl->qf_directory,
*fields->namebuf || qfl->qf_directory
@@ -1656,6 +1697,28 @@ static void locstack_queue_delreq(qf_info_T *qi)
qf_delq_head = q;
}
+/// Return the global quickfix stack window buffer number.
+int qf_stack_get_bufnr(void)
+{
+ return ql_info.qf_bufnr;
+}
+
+/// Wipe the quickfix window buffer (if present) for the specified
+/// quickfix/location list.
+static void wipe_qf_buffer(qf_info_T *qi)
+ FUNC_ATTR_NONNULL_ALL
+{
+ if (qi->qf_bufnr != INVALID_QFBUFNR) {
+ buf_T *const qfbuf = buflist_findnr(qi->qf_bufnr);
+ if (qfbuf != NULL && qfbuf->b_nwindows == 0) {
+ // If the quickfix buffer is not loaded in any window, then
+ // wipe the buffer.
+ close_buffer(NULL, qfbuf, DOBUF_WIPE, false, false);
+ qi->qf_bufnr = INVALID_QFBUFNR;
+ }
+ }
+}
+
/// Free a location list stack
static void ll_free_all(qf_info_T **pqi)
{
@@ -1668,19 +1731,23 @@ static void ll_free_all(qf_info_T **pqi)
}
*pqi = NULL; // Remove reference to this list
+ // If the location list is still in use, then queue the delete request
+ // to be processed later.
+ if (quickfix_busy > 0) {
+ locstack_queue_delreq(qi);
+ return;
+ }
+
qi->qf_refcount--;
if (qi->qf_refcount < 1) {
// No references to this location list.
- // If the location list is still in use, then queue the delete request
- // to be processed later.
- if (quickfix_busy > 0) {
- locstack_queue_delreq(qi);
- } else {
- for (i = 0; i < qi->qf_listcount; i++) {
- qf_free(qf_get_list(qi, i));
- }
- xfree(qi);
+ // If the quickfix window buffer is loaded, then wipe it
+ wipe_qf_buffer(qi);
+
+ for (i = 0; i < qi->qf_listcount; i++) {
+ qf_free(qf_get_list(qi, i));
}
+ xfree(qi);
}
}
@@ -1765,9 +1832,9 @@ void check_quickfix_busy(void)
/// @param valid valid entry
///
/// @returns QF_OK or QF_FAIL.
-static int qf_add_entry(qf_list_T *qfl, char_u *dir, char_u *fname, char_u *module, int bufnum,
- char_u *mesg, long lnum, long end_lnum, int col, int end_col,
- char_u vis_col, char_u *pattern, int nr, char_u type, char_u valid)
+static int qf_add_entry(qf_list_T *qfl, char *dir, char *fname, char *module, int bufnum,
+ char *mesg, linenr_T lnum, linenr_T end_lnum, int col, int end_col,
+ char vis_col, char *pattern, int nr, char type, char valid)
{
qfline_T *qfp = xmalloc(sizeof(qfline_T));
qfline_T **lastp; // pointer to qf_last or NULL
@@ -1783,7 +1850,7 @@ static int qf_add_entry(qf_list_T *qfl, char_u *dir, char_u *fname, char_u *modu
} else {
qfp->qf_fnum = qf_get_fnum(qfl, dir, fname);
}
- qfp->qf_text = vim_strsave(mesg);
+ qfp->qf_text = xstrdup(mesg);
qfp->qf_lnum = lnum;
qfp->qf_end_lnum = end_lnum;
qfp->qf_col = col;
@@ -1792,12 +1859,12 @@ static int qf_add_entry(qf_list_T *qfl, char_u *dir, char_u *fname, char_u *modu
if (pattern == NULL || *pattern == NUL) {
qfp->qf_pattern = NULL;
} else {
- qfp->qf_pattern = vim_strsave(pattern);
+ qfp->qf_pattern = xstrdup(pattern);
}
if (module == NULL || *module == NUL) {
qfp->qf_module = NULL;
} else {
- qfp->qf_module = vim_strsave(module);
+ qfp->qf_module = xstrdup(module);
}
qfp->qf_nr = nr;
if (type != 1 && !vim_isprintc(type)) { // only printable chars allowed
@@ -1838,6 +1905,7 @@ static qf_info_T *qf_alloc_stack(qfltype_T qfltype)
qf_info_T *qi = xcalloc(1, sizeof(qf_info_T));
qi->qf_refcount++;
qi->qfl_type = qfltype;
+ qi->qf_bufnr = INVALID_QFBUFNR;
return qi;
}
@@ -1954,7 +2022,7 @@ static int copy_loclist(qf_list_T *from_qfl, qf_list_T *to_qfl)
to_qfl->qf_last = NULL;
to_qfl->qf_ptr = NULL;
if (from_qfl->qf_title != NULL) {
- to_qfl->qf_title = vim_strsave(from_qfl->qf_title);
+ to_qfl->qf_title = xstrdup(from_qfl->qf_title);
} else {
to_qfl->qf_title = NULL;
}
@@ -1964,7 +2032,7 @@ static int copy_loclist(qf_list_T *from_qfl, qf_list_T *to_qfl)
} else {
to_qfl->qf_ctx = NULL;
}
- callback_copy(&to_qfl->qftf_cb, &from_qfl->qftf_cb);
+ callback_copy(&to_qfl->qf_qftf_cb, &from_qfl->qf_qftf_cb);
if (from_qfl->qf_count) {
if (copy_loclist_entries(from_qfl, to_qfl) == FAIL) {
@@ -2028,10 +2096,10 @@ void copy_loclist_stack(win_T *from, win_T *to)
/// Get buffer number for file "directory/fname".
/// Also sets the b_has_qf_entry flag.
-static int qf_get_fnum(qf_list_T *qfl, char_u *directory, char_u *fname)
+static int qf_get_fnum(qf_list_T *qfl, char *directory, char *fname)
{
- char_u *ptr = NULL;
- char_u *bufname;
+ char *ptr = NULL;
+ char *bufname;
buf_T *buf;
if (fname == NULL || *fname == NUL) { // no file name
return 0;
@@ -2043,19 +2111,19 @@ static int qf_get_fnum(qf_list_T *qfl, char_u *directory, char_u *fname)
}
slash_adjust(fname);
#endif
- if (directory != NULL && !vim_isAbsName(fname)) {
- ptr = (char_u *)concat_fnames((char *)directory, (char *)fname, true);
+ if (directory != NULL && !vim_isAbsName((char_u *)fname)) {
+ ptr = concat_fnames(directory, fname, true);
// Here we check if the file really exists.
// This should normally be true, but if make works without
// "leaving directory"-messages we might have missed a
// directory change.
- if (!os_path_exists(ptr)) {
+ if (!os_path_exists((char_u *)ptr)) {
xfree(ptr);
directory = qf_guess_filepath(qfl, fname);
if (directory) {
- ptr = (char_u *)concat_fnames((char *)directory, (char *)fname, true);
+ ptr = concat_fnames(directory, fname, true);
} else {
- ptr = vim_strsave(fname);
+ ptr = xstrdup(fname);
}
}
// Use concatenated directory name and file name.
@@ -2072,7 +2140,7 @@ static int qf_get_fnum(qf_list_T *qfl, char_u *directory, char_u *fname)
} else {
xfree(qf_last_bufname);
buf = buflist_new(bufname, NULL, (linenr_T)0, BLN_NOOPT);
- qf_last_bufname = (bufname == ptr) ? bufname : vim_strsave(bufname);
+ qf_last_bufname = (bufname == ptr) ? bufname : xstrdup(bufname);
set_bufref(&qf_last_bufref, buf);
}
if (buf == NULL) {
@@ -2085,7 +2153,7 @@ static int qf_get_fnum(qf_list_T *qfl, char_u *directory, char_u *fname)
// Push dirbuf onto the directory stack and return pointer to actual dir or
// NULL on error.
-static char_u *qf_push_dir(char_u *dirbuf, struct dir_stack_T **stackptr, bool is_file_stack)
+static char *qf_push_dir(char *dirbuf, struct dir_stack_T **stackptr, bool is_file_stack)
{
struct dir_stack_T *ds_ptr;
@@ -2096,10 +2164,10 @@ static char_u *qf_push_dir(char_u *dirbuf, struct dir_stack_T **stackptr, bool i
*stackptr = ds_new;
// store directory on the stack
- if (vim_isAbsName(dirbuf)
+ if (vim_isAbsName((char_u *)dirbuf)
|| (*stackptr)->next == NULL
- || (*stackptr && is_file_stack)) {
- (*stackptr)->dirname = vim_strsave(dirbuf);
+ || is_file_stack) {
+ (*stackptr)->dirname = xstrdup(dirbuf);
} else {
// Okay we don't have an absolute path.
// dirbuf must be a subdir of one of the directories on the stack.
@@ -2108,9 +2176,8 @@ static char_u *qf_push_dir(char_u *dirbuf, struct dir_stack_T **stackptr, bool i
(*stackptr)->dirname = NULL;
while (ds_new) {
xfree((*stackptr)->dirname);
- (*stackptr)->dirname = (char_u *)concat_fnames((char *)ds_new->dirname,
- (char *)dirbuf, TRUE);
- if (os_isdir((*stackptr)->dirname)) {
+ (*stackptr)->dirname = concat_fnames(ds_new->dirname, dirbuf, true);
+ if (os_isdir((char_u *)(*stackptr)->dirname)) {
break;
}
@@ -2128,7 +2195,7 @@ static char_u *qf_push_dir(char_u *dirbuf, struct dir_stack_T **stackptr, bool i
// Nothing found -> it must be on top level
if (ds_new == NULL) {
xfree((*stackptr)->dirname);
- (*stackptr)->dirname = vim_strsave(dirbuf);
+ (*stackptr)->dirname = xstrdup(dirbuf);
}
}
@@ -2142,10 +2209,9 @@ static char_u *qf_push_dir(char_u *dirbuf, struct dir_stack_T **stackptr, bool i
}
}
-
// pop dirbuf from the directory stack and return previous directory or NULL if
// stack is empty
-static char_u *qf_pop_dir(struct dir_stack_T **stackptr)
+static char *qf_pop_dir(struct dir_stack_T **stackptr)
{
struct dir_stack_T *ds_ptr;
@@ -2194,11 +2260,11 @@ static void qf_clean_dir_stack(struct dir_stack_T **stackptr)
/// x.c:9: Error
/// Then qf_push_dir thinks we are in ./aa/bb, but we are in ./bb.
/// qf_guess_filepath will return NULL.
-static char_u *qf_guess_filepath(qf_list_T *qfl, char_u *filename)
+static char *qf_guess_filepath(qf_list_T *qfl, char *filename)
{
struct dir_stack_T *ds_ptr;
struct dir_stack_T *ds_tmp;
- char_u *fullname;
+ char *fullname;
// no dirs on the stack - there's nothing we can do
if (qfl->qf_dir_stack == NULL) {
@@ -2209,9 +2275,9 @@ static char_u *qf_guess_filepath(qf_list_T *qfl, char_u *filename)
fullname = NULL;
while (ds_ptr) {
xfree(fullname);
- fullname = (char_u *)concat_fnames((char *)ds_ptr->dirname, (char *)filename, TRUE);
+ fullname = concat_fnames(ds_ptr->dirname, filename, true);
- if (os_path_exists(fullname)) {
+ if (os_path_exists((char_u *)fullname)) {
break;
}
@@ -2237,7 +2303,10 @@ static bool qflist_valid(win_T *wp, unsigned int qf_id)
qf_info_T *qi = &ql_info;
if (wp) {
- qi = GET_LOC_LIST(wp);
+ if (!win_valid(wp)) {
+ return false;
+ }
+ qi = GET_LOC_LIST(wp); // Location list
if (!qi) {
return false;
}
@@ -2325,7 +2394,7 @@ static qfline_T *get_nth_valid_entry(qf_list_T *qfl, int errornr, int dir, int *
int qf_idx = qfl->qf_index;
qfline_T *prev_qf_ptr;
int prev_index;
- char_u *err = e_no_more_items;
+ char *err = e_no_more_items;
while (errornr--) {
prev_qf_ptr = qf_ptr;
@@ -2399,7 +2468,7 @@ static qfline_T *qf_get_entry(qf_list_T *qfl, int errornr, int dir, int *new_qfi
return qf_ptr;
}
-// Find a window displaying a Vim help file.
+// Find a window displaying a Vim help file in the current tab page.
static win_T *qf_find_help_win(void)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
@@ -2424,7 +2493,7 @@ static int jump_to_help_window(qf_info_T *qi, bool newwin, int *opened_window)
{
win_T *wp = NULL;
- if (cmdmod.tab != 0 || newwin) {
+ if (cmdmod.cmod_tab != 0 || newwin) {
wp = NULL;
} else {
wp = qf_find_help_win();
@@ -2436,7 +2505,7 @@ static int jump_to_help_window(qf_info_T *qi, bool newwin, int *opened_window)
// Split off help window; put it at far top if no position
// specified, the current window is vertically split and narrow.
int flags = WSP_HELP;
- if (cmdmod.split == 0
+ if (cmdmod.cmod_split == 0
&& curwin->w_width != Columns
&& curwin->w_width < 80) {
flags |= WSP_TOP;
@@ -2466,15 +2535,14 @@ static int jump_to_help_window(qf_info_T *qi, bool newwin, int *opened_window)
}
}
- if (!p_im) {
- restart_edit = 0; // don't want insert mode in help file
- }
+ restart_edit = 0; // don't want insert mode in help file
return OK;
}
-// Find a non-quickfix window using the given location list.
-// Returns NULL if a matching window is not found.
+/// Find a non-quickfix window using the given location list stack in the
+/// current tabpage.
+/// Returns NULL if a matching window is not found.
static win_T *qf_find_win_with_loclist(const qf_info_T *ll)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
@@ -2486,7 +2554,7 @@ static win_T *qf_find_win_with_loclist(const qf_info_T *ll)
return NULL;
}
-// Find a window containing a normal buffer
+/// Find a window containing a normal buffer in the current tab page.
static win_T *qf_find_win_with_normal_buf(void)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
@@ -2542,7 +2610,7 @@ static void qf_goto_win_with_ll_file(win_T *use_win, int qf_fnum, qf_info_T *ll_
win_T *win = use_win;
if (win == NULL) {
- // Find the window showing the selected file
+ // Find the window showing the selected file in the current tab page.
FOR_ALL_WINDOWS_IN_TAB(win2, curtab) {
if (win2->w_buffer->b_fnum == qf_fnum) {
win = win2;
@@ -2674,7 +2742,7 @@ static int qf_jump_to_usable_window(int qf_fnum, bool newwin, int *opened_window
}
/// Edit the selected file or help file.
-static int qf_jump_edit_buffer(qf_info_T *qi, qfline_T *qf_ptr, int forceit, win_T *oldwin,
+static int qf_jump_edit_buffer(qf_info_T *qi, qfline_T *qf_ptr, int forceit, int prev_winid,
int *opened_window)
{
qf_list_T *qfl = qf_get_curlist(qi);
@@ -2693,7 +2761,7 @@ static int qf_jump_edit_buffer(qf_info_T *qi, qfline_T *qf_ptr, int forceit, win
} else {
retval = do_ecmd(qf_ptr->qf_fnum, NULL, NULL, NULL, (linenr_T)1,
ECMD_HIDE + ECMD_SET_HELP,
- oldwin == curwin ? curwin : NULL);
+ prev_winid == curwin->handle ? curwin : NULL);
}
} else {
retval = buflist_getfile(qf_ptr->qf_fnum, (linenr_T)1,
@@ -2701,10 +2769,14 @@ static int qf_jump_edit_buffer(qf_info_T *qi, qfline_T *qf_ptr, int forceit, win
}
// If a location list, check whether the associated window is still
// present.
- if (qfl_type == QFLT_LOCATION && !win_valid_any_tab(oldwin)) {
- emsg(_("E924: Current window was closed"));
- *opened_window = false;
- return NOTDONE;
+ if (qfl_type == QFLT_LOCATION) {
+ win_T *wp = win_id2wp(prev_winid);
+
+ if (wp == NULL && curwin->w_llist != qi) {
+ emsg(_("E924: Current window was closed"));
+ *opened_window = false;
+ return NOTDONE;
+ }
}
if (qfl_type == QFLT_QUICKFIX && !qflist_valid(NULL, save_qfid)) {
@@ -2712,8 +2784,8 @@ static int qf_jump_edit_buffer(qf_info_T *qi, qfline_T *qf_ptr, int forceit, win
return NOTDONE;
}
- if (old_qf_curlist != qi->qf_curlist
- || old_changetick != qfl->qf_changedtick
+ if (old_qf_curlist != qi->qf_curlist // -V560
+ || old_changetick != qfl->qf_changedtick // -V560
|| !is_qf_entry_present(qfl, qf_ptr)) {
if (qfl_type == QFLT_QUICKFIX) {
emsg(_(e_current_quickfix_list_was_changed));
@@ -2728,7 +2800,7 @@ static int qf_jump_edit_buffer(qf_info_T *qi, qfline_T *qf_ptr, int forceit, win
/// Go to the error line in the current file using either line/column number or
/// a search pattern.
-static void qf_jump_goto_line(linenr_T qf_lnum, int qf_col, char_u qf_viscol, char_u *qf_pattern)
+static void qf_jump_goto_line(linenr_T qf_lnum, int qf_col, char qf_viscol, char *qf_pattern)
{
linenr_T i;
@@ -2757,7 +2829,7 @@ static void qf_jump_goto_line(linenr_T qf_lnum, int qf_col, char_u qf_viscol, ch
// Move the cursor to the first line in the buffer
pos_T save_cursor = curwin->w_cursor;
curwin->w_cursor.lnum = 0;
- if (!do_search(NULL, '/', '/', qf_pattern, (long)1, SEARCH_KEEP, NULL)) {
+ if (!do_search(NULL, '/', '/', (char_u *)qf_pattern, (long)1, SEARCH_KEEP, NULL)) {
curwin->w_cursor = save_cursor;
}
}
@@ -2775,10 +2847,10 @@ static void qf_jump_print_msg(qf_info_T *qi, int qf_index, qfline_T *qf_ptr, buf
snprintf((char *)IObuff, IOSIZE, _("(%d of %d)%s%s: "), qf_index,
qf_get_curlist(qi)->qf_count,
qf_ptr->qf_cleared ? _(" (line deleted)") : "",
- (char *)qf_types(qf_ptr->qf_type, qf_ptr->qf_nr));
+ qf_types(qf_ptr->qf_type, qf_ptr->qf_nr));
// Add the message, skipping leading whitespace and newlines.
int len = (int)STRLEN(IObuff);
- qf_fmt_text(skipwhite(qf_ptr->qf_text), IObuff + len, IOSIZE - len);
+ qf_fmt_text(skipwhite(qf_ptr->qf_text), (char *)IObuff + len, IOSIZE - len);
// Output the message. Overwrite to avoid scrolling when the 'O'
// flag is present in 'shortmess'; But when not jumping, print the
@@ -2808,13 +2880,13 @@ static int qf_jump_open_window(qf_info_T *qi, qfline_T *qf_ptr, bool newwin, int
qfltype_T qfl_type = qfl->qfl_type;
// For ":helpgrep" find a help window or open one.
- if (qf_ptr->qf_type == 1 && (!bt_help(curwin->w_buffer) || cmdmod.tab != 0)) {
+ if (qf_ptr->qf_type == 1 && (!bt_help(curwin->w_buffer) || cmdmod.cmod_tab != 0)) {
if (jump_to_help_window(qi, newwin, opened_window) == FAIL) {
return FAIL;
}
}
if (old_qf_curlist != qi->qf_curlist
- || old_changetick != qfl->qf_changedtick
+ || old_changetick != qfl->qf_changedtick // -V560
|| !is_qf_entry_present(qfl, qf_ptr)) {
if (qfl_type == QFLT_QUICKFIX) {
emsg(_(e_current_quickfix_list_was_changed));
@@ -2859,7 +2931,7 @@ static int qf_jump_open_window(qf_info_T *qi, qfline_T *qf_ptr, bool newwin, int
/// NOTDONE if the quickfix/location list is freed by an autocmd when opening
/// the file.
static int qf_jump_to_buffer(qf_info_T *qi, int qf_index, qfline_T *qf_ptr, int forceit,
- win_T *oldwin, int *opened_window, int openfold, int print_message)
+ int prev_winid, int *opened_window, int openfold, int print_message)
{
buf_T *old_curbuf;
linenr_T old_lnum;
@@ -2871,7 +2943,7 @@ static int qf_jump_to_buffer(qf_info_T *qi, int qf_index, qfline_T *qf_ptr, int
old_lnum = curwin->w_cursor.lnum;
if (qf_ptr->qf_fnum != 0) {
- retval = qf_jump_edit_buffer(qi, qf_ptr, forceit, oldwin,
+ retval = qf_jump_edit_buffer(qi, qf_ptr, forceit, prev_winid,
opened_window);
if (retval != OK) {
return retval;
@@ -2883,8 +2955,7 @@ static int qf_jump_to_buffer(qf_info_T *qi, int qf_index, qfline_T *qf_ptr, int
setpcmark();
}
- qf_jump_goto_line(qf_ptr->qf_lnum, qf_ptr->qf_col, qf_ptr->qf_viscol,
- qf_ptr->qf_pattern);
+ qf_jump_goto_line(qf_ptr->qf_lnum, qf_ptr->qf_col, qf_ptr->qf_viscol, qf_ptr->qf_pattern);
if ((fdo_flags & FDO_QUICKFIX) && openfold) {
foldOpenCursor();
@@ -2918,10 +2989,10 @@ static void qf_jump_newwin(qf_info_T *qi, int dir, int errornr, int forceit, boo
qfline_T *old_qf_ptr;
int qf_index;
int old_qf_index;
- char_u *old_swb = p_swb;
+ char *old_swb = (char *)p_swb;
unsigned old_swb_flags = swb_flags;
+ int prev_winid;
int opened_window = false;
- win_T *oldwin = curwin;
int print_message = true;
const bool old_KeyTyped = KeyTyped; // getting file may reset it
int retval = OK;
@@ -2935,6 +3006,8 @@ static void qf_jump_newwin(qf_info_T *qi, int dir, int errornr, int forceit, boo
return;
}
+ incr_quickfix_busy();
+
qfl = qf_get_curlist(qi);
qf_ptr = qfl->qf_ptr;
@@ -2957,6 +3030,8 @@ static void qf_jump_newwin(qf_info_T *qi, int dir, int errornr, int forceit, boo
print_message = false;
}
+ prev_winid = curwin->handle;
+
retval = qf_jump_open_window(qi, qf_ptr, newwin, &opened_window);
if (retval == FAIL) {
goto failed;
@@ -2965,7 +3040,7 @@ static void qf_jump_newwin(qf_info_T *qi, int dir, int errornr, int forceit, boo
goto theend;
}
- retval = qf_jump_to_buffer(qi, qf_index, qf_ptr, forceit, oldwin,
+ retval = qf_jump_to_buffer(qi, qf_index, qf_ptr, forceit, prev_winid,
&opened_window, old_KeyTyped, print_message);
if (retval == NOTDONE) {
// Quickfix/location list is freed by an autocmd
@@ -2975,7 +3050,7 @@ static void qf_jump_newwin(qf_info_T *qi, int dir, int errornr, int forceit, boo
if (retval != OK) {
if (opened_window) {
- win_close(curwin, true); // Close opened window
+ win_close(curwin, true, false); // Close opened window
}
if (qf_ptr != NULL && qf_ptr->qf_fnum != 0) {
// Couldn't open file, so put index back where it was. This could
@@ -2990,15 +3065,15 @@ theend:
qfl->qf_ptr = qf_ptr;
qfl->qf_index = qf_index;
}
- if (p_swb != old_swb && p_swb == empty_option && opened_window) {
+ if (p_swb != (char_u *)old_swb && p_swb == empty_option) {
// Restore old 'switchbuf' value, but not when an autocommand or
// modeline has changed the value.
- p_swb = old_swb;
+ p_swb = (char_u *)old_swb;
swb_flags = old_swb_flags;
}
+ decr_quickfix_busy();
}
-
// Highlight attributes used for displaying entries from the quickfix list.
static int qfFileAttr;
static int qfSepAttr;
@@ -3010,13 +3085,12 @@ static int qfLineAttr;
/// quickfix list.
static void qf_list_entry(qfline_T *qfp, int qf_idx, bool cursel)
{
- char_u *fname;
+ char *fname;
buf_T *buf;
fname = NULL;
if (qfp->qf_module != NULL && *qfp->qf_module != NUL) {
- vim_snprintf((char *)IObuff, IOSIZE, "%2d %s", qf_idx,
- (char *)qfp->qf_module);
+ vim_snprintf((char *)IObuff, IOSIZE, "%2d %s", qf_idx, qfp->qf_module);
} else {
if (qfp->qf_fnum != 0
&& (buf = buflist_findnr(qfp->qf_fnum)) != NULL) {
@@ -3028,8 +3102,7 @@ static void qf_list_entry(qfline_T *qfp, int qf_idx, bool cursel)
if (fname == NULL) {
snprintf((char *)IObuff, IOSIZE, "%2d", qf_idx);
} else {
- vim_snprintf((char *)IObuff, IOSIZE, "%2d %s",
- qf_idx, (char *)fname);
+ vim_snprintf((char *)IObuff, IOSIZE, "%2d %s", qf_idx, fname);
}
}
@@ -3038,16 +3111,16 @@ static void qf_list_entry(qfline_T *qfp, int qf_idx, bool cursel)
// text of the entry.
bool filter_entry = true;
if (qfp->qf_module != NULL && *qfp->qf_module != NUL) {
- filter_entry &= message_filtered(qfp->qf_module);
+ filter_entry &= message_filtered((char_u *)qfp->qf_module);
}
if (filter_entry && fname != NULL) {
- filter_entry &= message_filtered(fname);
+ filter_entry &= message_filtered((char_u *)fname);
}
if (filter_entry && qfp->qf_pattern != NULL) {
- filter_entry &= message_filtered(qfp->qf_pattern);
+ filter_entry &= message_filtered((char_u *)qfp->qf_pattern);
}
if (filter_entry) {
- filter_entry &= message_filtered(qfp->qf_text);
+ filter_entry &= message_filtered((char_u *)qfp->qf_text);
}
if (filter_entry) {
return;
@@ -3062,14 +3135,13 @@ static void qf_list_entry(qfline_T *qfp, int qf_idx, bool cursel)
if (qfp->qf_lnum == 0) {
IObuff[0] = NUL;
} else {
- qf_range_text(qfp, IObuff, IOSIZE);
+ qf_range_text(qfp, (char *)IObuff, IOSIZE);
}
- vim_snprintf((char *)IObuff + STRLEN(IObuff), IOSIZE, "%s",
- (char *)qf_types(qfp->qf_type, qfp->qf_nr));
+ vim_snprintf((char *)IObuff + STRLEN(IObuff), IOSIZE, "%s", qf_types(qfp->qf_type, qfp->qf_nr));
msg_puts_attr((const char *)IObuff, qfLineAttr);
msg_puts_attr(":", qfSepAttr);
if (qfp->qf_pattern != NULL) {
- qf_fmt_text(qfp->qf_pattern, IObuff, IOSIZE);
+ qf_fmt_text(qfp->qf_pattern, (char *)IObuff, IOSIZE);
msg_puts((const char *)IObuff);
msg_puts_attr(":", qfSepAttr);
}
@@ -3080,7 +3152,7 @@ static void qf_list_entry(qfline_T *qfp, int qf_idx, bool cursel)
// with ^^^^. */
qf_fmt_text((fname != NULL || qfp->qf_lnum != 0)
? skipwhite(qfp->qf_text) : qfp->qf_text,
- IObuff, IOSIZE);
+ (char *)IObuff, IOSIZE);
msg_prt_line(IObuff, false);
ui_flush(); // show one line at a time
}
@@ -3094,7 +3166,7 @@ void qf_list(exarg_T *eap)
int i;
int idx1 = 1;
int idx2 = -1;
- char_u *arg = eap->arg;
+ char *arg = eap->arg;
int all = eap->forceit; // if not :cl!, only show
// recognised errors
qf_info_T *qi;
@@ -3113,7 +3185,7 @@ void qf_list(exarg_T *eap)
arg++;
plus = true;
}
- if (!get_list_range(&arg, &idx1, &idx2) || *arg != NUL) {
+ if (!get_list_range((char_u **)&arg, &idx1, &idx2) || *arg != NUL) {
emsg(_(e_trailing));
return;
}
@@ -3163,11 +3235,11 @@ void qf_list(exarg_T *eap)
// Remove newlines and leading whitespace from an error message.
// Put the result in "buf[bufsize]".
-static void qf_fmt_text(const char_u *restrict text, char_u *restrict buf, int bufsize)
+static void qf_fmt_text(const char *restrict text, char *restrict buf, int bufsize)
FUNC_ATTR_NONNULL_ALL
{
int i;
- const char_u *p = text;
+ const char *p = (char *)text;
for (i = 0; *p != NUL && i < bufsize - 1; ++i) {
if (*p == '\n') {
@@ -3186,37 +3258,33 @@ static void qf_fmt_text(const char_u *restrict text, char_u *restrict buf, int b
// Range information from lnum, col, end_lnum, and end_col.
// Put the result in "buf[bufsize]".
-static void qf_range_text(const qfline_T *qfp, char_u *buf, int bufsize)
+static void qf_range_text(const qfline_T *qfp, char *buf, int bufsize)
{
- vim_snprintf((char *)buf, (size_t)bufsize, "%" PRIdLINENR, qfp->qf_lnum);
+ vim_snprintf(buf, (size_t)bufsize, "%" PRIdLINENR, qfp->qf_lnum);
int len = (int)STRLEN(buf);
if (qfp->qf_end_lnum > 0 && qfp->qf_lnum != qfp->qf_end_lnum) {
- vim_snprintf((char *)buf + len, (size_t)(bufsize - len),
- "-%" PRIdLINENR, qfp->qf_end_lnum);
+ vim_snprintf(buf + len, (size_t)(bufsize - len), "-%" PRIdLINENR, qfp->qf_end_lnum);
len += (int)STRLEN(buf + len);
}
if (qfp->qf_col > 0) {
- vim_snprintf((char *)buf + len, (size_t)(bufsize - len),
- " col %d", qfp->qf_col);
+ vim_snprintf(buf + len, (size_t)(bufsize - len), " col %d", qfp->qf_col);
len += (int)STRLEN(buf + len);
if (qfp->qf_end_col > 0 && qfp->qf_col != qfp->qf_end_col) {
- vim_snprintf((char *)buf + len, (size_t)(bufsize - len),
- "-%d", qfp->qf_end_col);
+ vim_snprintf(buf + len, (size_t)(bufsize - len), "-%d", qfp->qf_end_col);
len += (int)STRLEN(buf + len);
}
}
buf[len] = NUL;
}
-
/// Display information (list number, list size and the title) about a
/// quickfix/location list.
static void qf_msg(qf_info_T *qi, int which, char *lead)
{
- char *title = (char *)qi->qf_lists[which].qf_title;
+ char *title = qi->qf_lists[which].qf_title;
int count = qi->qf_lists[which].qf_count;
- char_u buf[IOSIZE];
+ char buf[IOSIZE];
vim_snprintf((char *)buf, IOSIZE, _("%serror list %d of %d; %d errors "),
lead,
@@ -3234,7 +3302,7 @@ static void qf_msg(qf_info_T *qi, int which, char *lead)
STRLCAT(buf, title, IOSIZE);
}
trunc_string(buf, buf, Columns - 1, IOSIZE);
- msg((char *)buf);
+ msg(buf);
}
/// ":colder [count]": Up in the quickfix stack.
@@ -3251,7 +3319,6 @@ void qf_age(exarg_T *eap)
}
if (eap->addr_count != 0) {
- assert(eap->line2 <= INT_MAX);
count = (int)eap->line2;
} else {
count = 1;
@@ -3289,7 +3356,7 @@ void qf_history(exarg_T *eap)
// Jump to the specified quickfix list
if (eap->line2 > 0 && eap->line2 <= qi->qf_listcount) {
- qi->qf_curlist = (int)(eap->line2 - 1);
+ qi->qf_curlist = eap->line2 - 1;
qf_msg(qi, qi->qf_curlist, "");
qf_update_buffer(qi, NULL);
} else {
@@ -3362,13 +3429,14 @@ static void qf_free(qf_list_T *qfl)
XFREE_CLEAR(qfl->qf_title);
tv_free(qfl->qf_ctx);
qfl->qf_ctx = NULL;
- callback_free(&qfl->qftf_cb);
+ callback_free(&qfl->qf_qftf_cb);
qfl->qf_id = 0;
qfl->qf_changedtick = 0L;
}
// qf_mark_adjust: adjust marks
-bool qf_mark_adjust(win_T *wp, linenr_T line1, linenr_T line2, long amount, long amount_after)
+bool qf_mark_adjust(win_T *wp, linenr_T line1, linenr_T line2, linenr_T amount,
+ linenr_T amount_after)
{
int i;
qfline_T *qfp;
@@ -3425,25 +3493,25 @@ bool qf_mark_adjust(win_T *wp, linenr_T line1, linenr_T line2, long amount, long
// 0 n " error n"
// other n " c n"
// 1 x "" :helpgrep
-static char_u *qf_types(int c, int nr)
+static char *qf_types(int c, int nr)
{
- static char_u buf[20];
- static char_u cc[3];
- char_u *p;
+ static char buf[20];
+ static char cc[3];
+ char *p;
if (c == 'W' || c == 'w') {
- p = (char_u *)" warning";
+ p = " warning";
} else if (c == 'I' || c == 'i') {
- p = (char_u *)" info";
+ p = " info";
} else if (c == 'N' || c == 'n') {
- p = (char_u *)" note";
+ p = " note";
} else if (c == 'E' || c == 'e' || (c == 0 && nr > 0)) {
- p = (char_u *)" error";
+ p = " error";
} else if (c == 0 || c == 1) {
- p = (char_u *)"";
+ p = "";
} else {
cc[0] = ' ';
- cc[1] = (char_u)c;
+ cc[1] = (char)c;
cc[2] = NUL;
p = cc;
}
@@ -3452,7 +3520,7 @@ static char_u *qf_types(int c, int nr)
return p;
}
- sprintf((char *)buf, "%s %3d", (char *)p, nr);
+ snprintf((char *)buf, sizeof(buf), "%s %3d", p, nr);
return buf;
}
@@ -3530,7 +3598,7 @@ void ex_cclose(exarg_T *eap)
// Find existing quickfix window and close it.
win = qf_find_win(qi);
if (win != NULL) {
- win_close(win, false);
+ win_close(win, false, false);
}
}
@@ -3550,7 +3618,7 @@ static int qf_goto_cwindow(const qf_info_T *qi, bool resize, int sz, bool vertsp
win_setwidth(sz);
}
} else if (sz != win->w_height
- && (win->w_height + win->w_status_height + tabline_height()
+ && (win->w_height + win->w_hsep_height + win->w_status_height + tabline_height()
< cmdline_row)) {
win_setheight(sz);
}
@@ -3565,7 +3633,7 @@ static void qf_set_cwindow_options(void)
// switch off 'swapfile'
set_option_value("swf", 0L, NULL, OPT_LOCAL);
set_option_value("bt", 0L, "quickfix", OPT_LOCAL);
- set_option_value("bh", 0L, "wipe", OPT_LOCAL);
+ set_option_value("bh", 0L, "hide", OPT_LOCAL);
RESET_BINDING(curwin);
curwin->w_p_diff = false;
set_option_value("fdm", 0L, "manual", OPT_LOCAL);
@@ -3586,35 +3654,26 @@ static int qf_open_new_cwindow(qf_info_T *qi, int height)
// The current window becomes the previous window afterwards.
win_T *const win = curwin;
- if (IS_QF_STACK(qi) && cmdmod.split == 0) {
+ if (IS_QF_STACK(qi) && cmdmod.cmod_split == 0) {
// Create the new quickfix window at the very bottom, except when
// :belowright or :aboveleft is used.
win_goto(lastwin);
}
// Default is to open the window below the current window
- if (cmdmod.split == 0) {
+ if (cmdmod.cmod_split == 0) {
flags = WSP_BELOW;
}
flags |= WSP_NEWLOC;
if (win_split(height, flags) == FAIL) {
return FAIL; // not enough room for window
}
-
- // User autocommands may have invalidated the previous window after calling
- // win_split, so add a check to ensure that the win is still here
- if (IS_LL_STACK(qi) && !win_valid(win)) {
- // close the window that was supposed to be for the loclist
- win_close(curwin, false);
- return FAIL;
- }
-
RESET_BINDING(curwin);
if (IS_LL_STACK(qi)) {
// For the location list window, create a reference to the
- // location list from the window 'win'.
- curwin->w_llist_ref = win->w_llist;
- win->w_llist->qf_refcount++;
+ // location list stack from the window 'win'.
+ curwin->w_llist_ref = qi;
+ qi->qf_refcount++;
}
if (oldwin != curwin) {
@@ -3631,6 +3690,8 @@ static int qf_open_new_cwindow(qf_info_T *qi, int height)
if (do_ecmd(0, NULL, NULL, NULL, ECMD_ONE, ECMD_HIDE + ECMD_NOWINENTER, oldwin) == FAIL) {
return FAIL;
}
+ // save the number of the new buffer
+ qi->qf_bufnr = curbuf->b_fnum;
}
// Set the options for the quickfix buffer/window (if not already done)
@@ -3677,7 +3738,6 @@ void ex_copen(exarg_T *eap)
incr_quickfix_busy();
if (eap->addr_count != 0) {
- assert(eap->line2 <= INT_MAX);
height = (int)eap->line2;
} else {
height = QF_WINHEIGHT;
@@ -3685,9 +3745,9 @@ void ex_copen(exarg_T *eap)
reset_VIsual_and_resel(); // stop Visual mode
// Find an existing quickfix window, or open a new one.
- if (cmdmod.tab == 0) {
+ if (cmdmod.cmod_tab == 0) {
status = qf_goto_cwindow(qi, eap->addr_count != 0, height,
- cmdmod.split & WSP_VERT);
+ cmdmod.cmod_split & WSP_VERT);
}
if (status == FAIL) {
if (qf_open_new_cwindow(qi, height) == FAIL) {
@@ -3809,8 +3869,8 @@ static int is_qf_win(const win_T *win, const qf_info_T *qi)
return false;
}
-/// Find a window displaying the quickfix/location stack 'qi'
-/// Only searches in the current tabpage.
+/// Find a window displaying the quickfix/location stack 'qi' in the current tab
+/// page.
static win_T *qf_find_win(const qf_info_T *qi)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
@@ -3823,11 +3883,20 @@ static win_T *qf_find_win(const qf_info_T *qi)
return NULL;
}
-// Find a quickfix buffer.
-// Searches in windows opened in all the tabs.
+/// Find a quickfix buffer.
+/// Searches in windows opened in all the tab pages.
static buf_T *qf_find_buf(qf_info_T *qi)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
+ if (qi->qf_bufnr != INVALID_QFBUFNR) {
+ buf_T *const qfbuf = buflist_findnr(qi->qf_bufnr);
+ if (qfbuf != NULL) {
+ return qfbuf;
+ }
+ // buffer is no longer present
+ qi->qf_bufnr = INVALID_QFBUFNR;
+ }
+
FOR_ALL_TAB_WINDOWS(tp, win) {
if (is_qf_win(win, qi)) {
return win->w_buffer;
@@ -3850,7 +3919,7 @@ bool qf_process_qftf_option(void)
if (*p_qftf == '{') {
// Lambda expression
- tv = eval_expr(p_qftf);
+ tv = eval_expr((char *)p_qftf);
if (tv == NULL) {
return false;
}
@@ -3858,7 +3927,7 @@ bool qf_process_qftf_option(void)
// treat everything else as a function name string
tv = xcalloc(1, sizeof(*tv));
tv->v_type = VAR_STRING;
- tv->vval.v_string = vim_strsave(p_qftf);
+ tv->vval.v_string = (char *)vim_strsave(p_qftf);
}
if (!callback_from_typval(&cb, tv)) {
@@ -3941,7 +4010,7 @@ static void qf_update_buffer(qf_info_T *qi, qfline_T *old_last)
// Add an error line to the quickfix buffer.
static int qf_buf_add_line(qf_list_T *qfl, buf_T *buf, linenr_T lnum, const qfline_T *qfp,
- char_u *dirname, char_u *qftf_str, bool first_bufline)
+ char *dirname, char *qftf_str, bool first_bufline)
FUNC_ATTR_NONNULL_ARG(1, 2, 4, 5)
{
int len;
@@ -3966,11 +4035,11 @@ static int qf_buf_add_line(qf_list_T *qfl, buf_T *buf, linenr_T lnum, const qfli
// buffer.
if (first_bufline
&& (errbuf->b_sfname == NULL
- || path_is_absolute(errbuf->b_sfname))) {
+ || path_is_absolute((char_u *)errbuf->b_sfname))) {
if (*dirname == NUL) {
- os_dirname(dirname, MAXPATHL);
+ os_dirname((char_u *)dirname, MAXPATHL);
}
- shorten_buf_fname(errbuf, dirname, false);
+ shorten_buf_fname(errbuf, (char_u *)dirname, false);
}
STRLCPY(IObuff, errbuf->b_fname, IOSIZE);
}
@@ -3982,14 +4051,14 @@ static int qf_buf_add_line(qf_list_T *qfl, buf_T *buf, linenr_T lnum, const qfli
IObuff[len++] = '|';
}
if (qfp->qf_lnum > 0) {
- qf_range_text(qfp, IObuff + len, IOSIZE - len);
+ qf_range_text(qfp, (char *)IObuff + len, IOSIZE - len);
len += (int)STRLEN(IObuff + len);
- snprintf((char *)IObuff + len, (size_t)(IOSIZE - len), "%s",
- (char *)qf_types(qfp->qf_type, qfp->qf_nr));
+ snprintf((char *)IObuff + len, (size_t)(IOSIZE - len), "%s", qf_types(qfp->qf_type,
+ qfp->qf_nr));
len += (int)STRLEN(IObuff + len);
} else if (qfp->qf_pattern != NULL) {
- qf_fmt_text(qfp->qf_pattern, IObuff + len, IOSIZE - len);
+ qf_fmt_text(qfp->qf_pattern, (char *)IObuff + len, IOSIZE - len);
len += (int)STRLEN(IObuff + len);
}
if (len < IOSIZE - 2) {
@@ -4001,7 +4070,7 @@ static int qf_buf_add_line(qf_list_T *qfl, buf_T *buf, linenr_T lnum, const qfli
// For an unrecognized line keep the indent, the compiler may
// mark a word with ^^^^.
qf_fmt_text(len > 3 ? skipwhite(qfp->qf_text) : qfp->qf_text,
- IObuff + len, IOSIZE - len);
+ (char *)IObuff + len, IOSIZE - len);
}
if (ml_append_buf(buf, lnum, IObuff,
@@ -4021,10 +4090,10 @@ static list_T *call_qftf_func(qf_list_T *qfl, int qf_winid, long start_idx, long
// If 'quickfixtextfunc' is set, then use the user-supplied function to get
// the text to display. Use the local value of 'quickfixtextfunc' if it is
// set.
- if (qfl->qftf_cb.type != kCallbackNone) {
- cb = &qfl->qftf_cb;
+ if (qfl->qf_qftf_cb.type != kCallbackNone) {
+ cb = &qfl->qf_qftf_cb;
}
- if (cb != NULL && cb->type != kCallbackNone) {
+ if (cb->type != kCallbackNone) {
typval_T args[1];
typval_T rettv;
@@ -4083,7 +4152,7 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, int q
// Check if there is anything to display
if (qfl != NULL) {
- char_u dirname[MAXPATHL];
+ char dirname[MAXPATHL];
int prev_bufnr = -1;
bool invalid_val = false;
@@ -4106,13 +4175,13 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, int q
qftf_li = tv_list_first(qftf_list);
while (lnum < qfl->qf_count) {
- char_u *qftf_str = NULL;
+ char *qftf_str = NULL;
// Use the text supplied by the user defined function (if any).
// If the returned value is not string, then ignore the rest
// of the returned values and use the default.
if (qftf_li != NULL && !invalid_val) {
- qftf_str = (char_u *)tv_get_string_chk(TV_LIST_ITEM_TV(qftf_li));
+ qftf_str = (char *)tv_get_string_chk(TV_LIST_ITEM_TV(qftf_li));
if (qftf_str == NULL) {
invalid_val = true;
}
@@ -4151,10 +4220,8 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, int q
curbuf->b_p_ma = false;
keep_filetype = true; // don't detect 'filetype'
- apply_autocmds(EVENT_BUFREADPOST, (char_u *)"quickfix", NULL,
- false, curbuf);
- apply_autocmds(EVENT_BUFWINENTER, (char_u *)"quickfix", NULL,
- false, curbuf);
+ apply_autocmds(EVENT_BUFREADPOST, "quickfix", NULL, false, curbuf);
+ apply_autocmds(EVENT_BUFWINENTER, "quickfix", NULL, false, curbuf);
keep_filetype = false;
curbuf->b_ro_locked--;
@@ -4228,22 +4295,22 @@ int grep_internal(cmdidx_T cmdidx)
}
// Return the make/grep autocmd name.
-static char_u *make_get_auname(cmdidx_T cmdidx)
+static char *make_get_auname(cmdidx_T cmdidx)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
switch (cmdidx) {
case CMD_make:
- return (char_u *)"make";
+ return "make";
case CMD_lmake:
- return (char_u *)"lmake";
+ return "lmake";
case CMD_grep:
- return (char_u *)"grep";
+ return "grep";
case CMD_lgrep:
- return (char_u *)"lgrep";
+ return "lgrep";
case CMD_grepadd:
- return (char_u *)"grepadd";
+ return "grepadd";
case CMD_lgrepadd:
- return (char_u *)"lgrepadd";
+ return "lgrepadd";
default:
return NULL;
}
@@ -4251,7 +4318,7 @@ static char_u *make_get_auname(cmdidx_T cmdidx)
// Form the complete command line to invoke 'make'/'grep'. Quote the command
// using 'shellquote' and append 'shellpipe'. Echo the fully formed command.
-static char *make_get_fullcmd(const char_u *makecmd, const char_u *fname)
+static char *make_get_fullcmd(const char *makecmd, const char *fname)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET
{
size_t len = STRLEN(p_shq) * 2 + STRLEN(makecmd) + 1;
@@ -4274,7 +4341,7 @@ static char *make_get_fullcmd(const char_u *makecmd, const char_u *fname)
}
msg_start();
msg_puts(":!");
- msg_outtrans((char_u *)cmd); // show what we are doing
+ msg_outtrans(cmd); // show what we are doing
return cmd;
}
@@ -4282,11 +4349,11 @@ static char *make_get_fullcmd(const char_u *makecmd, const char_u *fname)
// Used for ":make", ":lmake", ":grep", ":lgrep", ":grepadd", and ":lgrepadd"
void ex_make(exarg_T *eap)
{
- char_u *fname;
+ char *fname;
win_T *wp = NULL;
qf_info_T *qi = &ql_info;
int res;
- char_u *enc = (*curbuf->b_p_menc != NUL) ? curbuf->b_p_menc : p_menc;
+ char *enc = (*curbuf->b_p_menc != NUL) ? (char *)curbuf->b_p_menc : (char *)p_menc;
// Redirect ":grep" to ":vimgrep" if 'grepprg' is "internal".
if (grep_internal(eap->cmdidx)) {
@@ -4294,7 +4361,7 @@ void ex_make(exarg_T *eap)
return;
}
- char_u *const au_name = make_get_auname(eap->cmdidx);
+ char *const au_name = make_get_auname(eap->cmdidx);
if (au_name != NULL && apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name,
curbuf->b_fname, true, curbuf)) {
if (aborting()) {
@@ -4311,11 +4378,11 @@ void ex_make(exarg_T *eap)
if (fname == NULL) {
return;
}
- os_remove((char *)fname); // in case it's not unique
+ os_remove(fname); // in case it's not unique
char *const cmd = make_get_fullcmd(eap->arg, fname);
- do_shell((char_u *)cmd, 0);
+ do_shell(cmd, 0);
incr_quickfix_busy();
@@ -4346,7 +4413,7 @@ void ex_make(exarg_T *eap)
cleanup:
decr_quickfix_busy();
- os_remove((char *)fname);
+ os_remove(fname);
xfree(fname);
xfree(cmd);
}
@@ -4354,15 +4421,15 @@ cleanup:
// Return the name for the errorfile, in allocated memory.
// Find a new unique name when 'makeef' contains "##".
// Returns NULL for error.
-static char_u *get_mef_name(void)
+static char *get_mef_name(void)
{
- char_u *p;
- char_u *name;
+ char *p;
+ char *name;
static int start = -1;
static int off = 0;
if (*p_mef == NUL) {
- name = vim_tempname();
+ name = (char *)vim_tempname();
if (name == NULL) {
emsg(_(e_notmp));
}
@@ -4376,7 +4443,7 @@ static char_u *get_mef_name(void)
}
if (*p == NUL) {
- return vim_strsave(p_mef);
+ return xstrdup(p_mef);
}
// Keep trying until the name doesn't exist yet.
@@ -4388,11 +4455,11 @@ static char_u *get_mef_name(void)
}
name = xmalloc(STRLEN(p_mef) + 30);
STRCPY(name, p_mef);
- sprintf((char *)name + (p - p_mef), "%d%d", start, off);
+ snprintf(name + (p - p_mef), STRLEN(name), "%d%d", start, off);
STRCAT(name, p + 2);
// Don't accept a symbolic link, it's a security risk.
FileInfo file_info;
- bool file_or_link_found = os_fileinfo_link((char *)name, &file_info);
+ bool file_or_link_found = os_fileinfo_link(name, &file_info);
if (!file_or_link_found) {
break;
}
@@ -4849,11 +4916,12 @@ static qfline_T *qf_find_closest_entry(qf_list_T *qfl, int bnr, const pos_T *pos
/// Get the nth quickfix entry below the specified entry. Searches forward in
/// the list. If linewise is true, then treat multiple entries on a single line
/// as one.
-static void qf_get_nth_below_entry(qfline_T *entry, linenr_T n, bool linewise, int *errornr)
+static void qf_get_nth_below_entry(qfline_T *entry_arg, linenr_T n, bool linewise, int *errornr)
FUNC_ATTR_NONNULL_ALL
{
+ qfline_T *entry = entry_arg;
+
while (n-- > 0 && !got_int) {
- // qfline_T *first_entry = entry;
int first_errornr = *errornr;
if (linewise) {
@@ -4864,9 +4932,6 @@ static void qf_get_nth_below_entry(qfline_T *entry, linenr_T n, bool linewise, i
if (entry->qf_next == NULL
|| entry->qf_next->qf_fnum != entry->qf_fnum) {
if (linewise) {
- // If multiple entries are on the same line, then use the first
- // entry
- // entry = first_entry;
*errornr = first_errornr;
}
break;
@@ -4996,36 +5061,34 @@ void ex_cbelow(exarg_T *eap)
}
}
-
/// Return the autocmd name for the :cfile Ex commands
-static char_u *cfile_get_auname(cmdidx_T cmdidx)
+static char *cfile_get_auname(cmdidx_T cmdidx)
{
switch (cmdidx) {
case CMD_cfile:
- return (char_u *)"cfile";
+ return "cfile";
case CMD_cgetfile:
- return (char_u *)"cgetfile";
+ return "cgetfile";
case CMD_caddfile:
- return (char_u *)"caddfile";
+ return "caddfile";
case CMD_lfile:
- return (char_u *)"lfile";
+ return "lfile";
case CMD_lgetfile:
- return (char_u *)"lgetfile";
+ return "lgetfile";
case CMD_laddfile:
- return (char_u *)"laddfile";
+ return "laddfile";
default:
return NULL;
}
}
-
// ":cfile"/":cgetfile"/":caddfile" commands.
// ":lfile"/":lgetfile"/":laddfile" commands.
void ex_cfile(exarg_T *eap)
{
win_T *wp = NULL;
qf_info_T *qi = &ql_info;
- char_u *au_name = NULL;
+ char *au_name = NULL;
au_name = cfile_get_auname(eap->cmdidx);
if (au_name != NULL
@@ -5038,7 +5101,7 @@ void ex_cfile(exarg_T *eap)
set_string_option_direct("ef", -1, eap->arg, OPT_FREE, 0);
}
- char_u *enc = (*curbuf->b_p_menc != NUL) ? curbuf->b_p_menc : p_menc;
+ char *enc = (*curbuf->b_p_menc != NUL) ? (char *)curbuf->b_p_menc : (char *)p_menc;
if (is_loclist_cmd(eap->cmdidx)) {
wp = curwin;
@@ -5054,8 +5117,8 @@ void ex_cfile(exarg_T *eap)
// first error.
// :caddfile adds to an existing quickfix list. If there is no
// quickfix list then a new list is created.
- int res = qf_init(wp, p_ef, p_efm, (eap->cmdidx != CMD_caddfile
- && eap->cmdidx != CMD_laddfile),
+ int res = qf_init(wp, (char *)p_ef, p_efm, (eap->cmdidx != CMD_caddfile
+ && eap->cmdidx != CMD_laddfile),
qf_cmdtitle(*eap->cmdlinep), enc);
if (wp != NULL) {
qi = GET_LOC_LIST(wp);
@@ -5083,32 +5146,32 @@ void ex_cfile(exarg_T *eap)
}
/// Return the vimgrep autocmd name.
-static char_u *vgr_get_auname(cmdidx_T cmdidx)
+static char *vgr_get_auname(cmdidx_T cmdidx)
{
switch (cmdidx) {
case CMD_vimgrep:
- return (char_u *)"vimgrep";
+ return "vimgrep";
case CMD_lvimgrep:
- return (char_u *)"lvimgrep";
+ return "lvimgrep";
case CMD_vimgrepadd:
- return (char_u *)"vimgrepadd";
+ return "vimgrepadd";
case CMD_lvimgrepadd:
- return (char_u *)"lvimgrepadd";
+ return "lvimgrepadd";
case CMD_grep:
- return (char_u *)"grep";
+ return "grep";
case CMD_lgrep:
- return (char_u *)"lgrep";
+ return "lgrep";
case CMD_grepadd:
- return (char_u *)"grepadd";
+ return "grepadd";
case CMD_lgrepadd:
- return (char_u *)"lgrepadd";
+ return "lgrepadd";
default:
return NULL;
}
}
/// Initialize the regmatch used by vimgrep for pattern "s".
-static void vgr_init_regmatch(regmmatch_T *regmatch, char_u *s)
+static void vgr_init_regmatch(regmmatch_T *regmatch, char *s)
{
// Get the search pattern: either white-separated or enclosed in //.
regmatch->regprog = NULL;
@@ -5119,7 +5182,7 @@ static void vgr_init_regmatch(regmmatch_T *regmatch, char_u *s)
emsg(_(e_noprevre));
return;
}
- regmatch->regprog = vim_regcomp(last_search_pat(), RE_MAGIC);
+ regmatch->regprog = vim_regcomp((char *)last_search_pat(), RE_MAGIC);
} else {
regmatch->regprog = vim_regcomp(s, RE_MAGIC);
}
@@ -5128,12 +5191,11 @@ static void vgr_init_regmatch(regmmatch_T *regmatch, char_u *s)
regmatch->rmm_maxcol = 0;
}
-
/// Display a file name when vimgrep is running.
-static void vgr_display_fname(char_u *fname)
+static void vgr_display_fname(char *fname)
{
msg_start();
- char_u *p = msg_strtrunc(fname, true);
+ char *p = (char *)msg_strtrunc((char_u *)fname, true);
if (p == NULL) {
msg_outtrans(fname);
} else {
@@ -5148,11 +5210,11 @@ static void vgr_display_fname(char_u *fname)
}
/// Load a dummy buffer to search for a pattern using vimgrep.
-static buf_T *vgr_load_dummy_buf(char_u *fname, char_u *dirname_start, char_u *dirname_now)
+static buf_T *vgr_load_dummy_buf(char *fname, char *dirname_start, char *dirname_now)
{
// Don't do Filetype autocommands to avoid loading syntax and
// indent scripts, a great speed improvement.
- char_u *save_ei = au_event_disable(",Filetype");
+ char *save_ei = au_event_disable(",Filetype");
long save_mls = p_mls;
p_mls = 0;
@@ -5170,7 +5232,7 @@ static buf_T *vgr_load_dummy_buf(char_u *fname, char_u *dirname_start, char_u *d
/// Check whether a quickfix/location list is valid. Autocmds may remove or
/// change a quickfix list when vimgrep is running. If the list is not found,
/// create a new list.
-static bool vgr_qflist_valid(win_T *wp, qf_info_T *qi, unsigned qfid, char_u *title)
+static bool vgr_qflist_valid(win_T *wp, qf_info_T *qi, unsigned qfid, char *title)
{
// Verify that the quickfix/location list was not freed by an autocmd
if (!qflist_valid(wp, qfid)) {
@@ -5191,52 +5253,96 @@ static bool vgr_qflist_valid(win_T *wp, qf_info_T *qi, unsigned qfid, char_u *ti
return true;
}
-
/// Search for a pattern in all the lines in a buffer and add the matching lines
/// to a quickfix list.
-static bool vgr_match_buflines(qf_list_T *qfl, char_u *fname, buf_T *buf, regmmatch_T *regmatch,
- long *tomatch, int duplicate_name, int flags)
- FUNC_ATTR_NONNULL_ARG(1, 3, 4, 5)
+static bool vgr_match_buflines(qf_list_T *qfl, char *fname, buf_T *buf, char *spat,
+ regmmatch_T *regmatch, long *tomatch, int duplicate_name, int flags)
+ FUNC_ATTR_NONNULL_ARG(1, 3, 4, 5, 6)
{
bool found_match = false;
- for (long lnum = 1; lnum <= buf->b_ml.ml_line_count && *tomatch > 0; lnum++) {
+ for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count && *tomatch > 0; lnum++) {
colnr_T col = 0;
- while (vim_regexec_multi(regmatch, curwin, buf, lnum, col, NULL,
- NULL) > 0) {
- // Pass the buffer number so that it gets used even for a
- // dummy buffer, unless duplicate_name is set, then the
- // buffer will be wiped out below.
- if (qf_add_entry(qfl,
- NULL, // dir
- fname,
- NULL,
- duplicate_name ? 0 : buf->b_fnum,
- ml_get_buf(buf, regmatch->startpos[0].lnum + lnum,
- false),
- regmatch->startpos[0].lnum + lnum,
- regmatch->endpos[0].lnum + lnum,
- regmatch->startpos[0].col + 1,
- regmatch->endpos[0].col + 1,
- false, // vis_col
- NULL, // search pattern
- 0, // nr
- 0, // type
- true) // valid
- == QF_FAIL) {
- got_int = true;
- break;
- }
- found_match = true;
- if (--*tomatch == 0) {
- break;
- }
- if ((flags & VGR_GLOBAL) == 0 || regmatch->endpos[0].lnum > 0) {
- break;
+ if (!(flags & VGR_FUZZY)) {
+ // Regular expression match
+ while (vim_regexec_multi(regmatch, curwin, buf, lnum, col, NULL, NULL) > 0) {
+ // Pass the buffer number so that it gets used even for a
+ // dummy buffer, unless duplicate_name is set, then the
+ // buffer will be wiped out below.
+ if (qf_add_entry(qfl,
+ NULL, // dir
+ fname,
+ NULL,
+ duplicate_name ? 0 : buf->b_fnum,
+ (char *)ml_get_buf(buf, regmatch->startpos[0].lnum + lnum, false),
+ regmatch->startpos[0].lnum + lnum,
+ regmatch->endpos[0].lnum + lnum,
+ regmatch->startpos[0].col + 1,
+ regmatch->endpos[0].col + 1,
+ false, // vis_col
+ NULL, // search pattern
+ 0, // nr
+ 0, // type
+ true) // valid
+ == QF_FAIL) {
+ got_int = true;
+ break;
+ }
+ found_match = true;
+ if (--*tomatch == 0) {
+ break;
+ }
+ if ((flags & VGR_GLOBAL) == 0 || regmatch->endpos[0].lnum > 0) {
+ break;
+ }
+ col = regmatch->endpos[0].col + (col == regmatch->endpos[0].col);
+ if (col > (colnr_T)STRLEN(ml_get_buf(buf, lnum, false))) {
+ break;
+ }
}
- col = regmatch->endpos[0].col + (col == regmatch->endpos[0].col);
- if (col > (colnr_T)STRLEN(ml_get_buf(buf, lnum, false))) {
- break;
+ } else {
+ const size_t pat_len = STRLEN(spat);
+ char *const str = (char *)ml_get_buf(buf, lnum, false);
+ int score;
+ uint32_t matches[MAX_FUZZY_MATCHES];
+ const size_t sz = sizeof(matches) / sizeof(matches[0]);
+
+ // Fuzzy string match
+ while (fuzzy_match((char_u *)str + col, (char_u *)spat, false, &score, matches,
+ (int)sz) > 0) {
+ // Pass the buffer number so that it gets used even for a
+ // dummy buffer, unless duplicate_name is set, then the
+ // buffer will be wiped out below.
+ if (qf_add_entry(qfl,
+ NULL, // dir
+ fname,
+ NULL,
+ duplicate_name ? 0 : buf->b_fnum,
+ str,
+ lnum,
+ 0,
+ (colnr_T)matches[0] + col + 1,
+ 0,
+ false, // vis_col
+ NULL, // search pattern
+ 0, // nr
+ 0, // type
+ true) // valid
+ == QF_FAIL) {
+ got_int = true;
+ break;
+ }
+ found_match = true;
+ if (--*tomatch == 0) {
+ break;
+ }
+ if ((flags & VGR_GLOBAL) == 0) {
+ break;
+ }
+ col = (colnr_T)matches[pat_len - 1] + col + 1;
+ if (col > (colnr_T)STRLEN(str)) {
+ break;
+ }
}
}
line_breakcheck();
@@ -5249,8 +5355,8 @@ static bool vgr_match_buflines(qf_list_T *qfl, char_u *fname, buf_T *buf, regmma
}
/// Jump to the first match and update the directory.
-static void vgr_jump_to_match(qf_info_T *qi, int forceit, int *redraw_for_dummy,
- buf_T *first_match_buf, char_u *target_dir)
+static void vgr_jump_to_match(qf_info_T *qi, int forceit, bool *redraw_for_dummy,
+ buf_T *first_match_buf, char *target_dir)
{
buf_T *buf = curbuf;
qf_jump(qi, 0, 0, forceit);
@@ -5276,7 +5382,7 @@ static bool existing_swapfile(const buf_T *buf)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
if (buf->b_ml.ml_mfp != NULL && buf->b_ml.ml_mfp->mf_fname != NULL) {
- const char_u *const fname = buf->b_ml.ml_mfp->mf_fname;
+ const char *const fname = (char *)buf->b_ml.ml_mfp->mf_fname;
const size_t len = STRLEN(fname);
return fname[len - 1] != 'p' || fname[len - 2] != 'w';
@@ -5284,104 +5390,71 @@ static bool existing_swapfile(const buf_T *buf)
return false;
}
-// ":vimgrep {pattern} file(s)"
-// ":vimgrepadd {pattern} file(s)"
-// ":lvimgrep {pattern} file(s)"
-// ":lvimgrepadd {pattern} file(s)"
-void ex_vimgrep(exarg_T *eap)
+/// Process :vimgrep command arguments. The command syntax is:
+///
+/// :{count}vimgrep /{pattern}/[g][j] {file} ...
+static int vgr_process_args(exarg_T *eap, vgr_args_T *args)
{
- regmmatch_T regmatch;
- int fcount;
- char_u **fnames;
- char_u *fname;
- char_u *s;
- char_u *p;
- int fi;
- qf_list_T *qfl;
- win_T *wp = NULL;
- buf_T *buf;
- int duplicate_name = FALSE;
- int using_dummy;
- int redraw_for_dummy = FALSE;
- int found_match;
- buf_T *first_match_buf = NULL;
- time_t seconds = 0;
- aco_save_T aco;
- int flags = 0;
- long tomatch;
- char_u *dirname_start = NULL;
- char_u *dirname_now = NULL;
- char_u *target_dir = NULL;
- char_u *au_name = NULL;
-
- au_name = vgr_get_auname(eap->cmdidx);
- if (au_name != NULL && apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name,
- curbuf->b_fname, true, curbuf)) {
- if (aborting()) {
- return;
- }
- }
+ memset(args, 0, sizeof(*args));
- qf_info_T *qi = qf_cmd_get_or_alloc_stack(eap, &wp);
+ args->regmatch.regprog = NULL;
+ args->qf_title = xstrdup(qf_cmdtitle(*eap->cmdlinep));
if (eap->addr_count > 0) {
- tomatch = eap->line2;
+ args->tomatch = eap->line2;
} else {
- tomatch = MAXLNUM;
+ args->tomatch = MAXLNUM;
}
// Get the search pattern: either white-separated or enclosed in //
- regmatch.regprog = NULL;
- char_u *title = vim_strsave(qf_cmdtitle(*eap->cmdlinep));
- p = skip_vimgrep_pat(eap->arg, &s, &flags);
+ char *p = skip_vimgrep_pat(eap->arg, &args->spat, &args->flags);
if (p == NULL) {
emsg(_(e_invalpat));
- goto theend;
+ return FAIL;
}
- vgr_init_regmatch(&regmatch, s);
- if (regmatch.regprog == NULL) {
- goto theend;
+ vgr_init_regmatch(&args->regmatch, args->spat);
+ if (args->regmatch.regprog == NULL) {
+ return FAIL;
}
p = skipwhite(p);
if (*p == NUL) {
emsg(_("E683: File name missing or invalid pattern"));
- goto theend;
- }
-
- if ((eap->cmdidx != CMD_grepadd && eap->cmdidx != CMD_lgrepadd
- && eap->cmdidx != CMD_vimgrepadd && eap->cmdidx != CMD_lvimgrepadd)
- || qf_stack_empty(qi)) {
- // make place for a new list
- qf_new_list(qi, title);
+ return FAIL;
}
// Parse the list of arguments, wildcards have already been expanded.
- if (get_arglist_exp(p, &fcount, &fnames, true) == FAIL) {
- goto theend;
+ if (get_arglist_exp((char_u *)p, &args->fcount, (char_u ***)&args->fnames, true) == FAIL) {
+ return FAIL;
}
- if (fcount == 0) {
+ if (args->fcount == 0) {
emsg(_(e_nomatch));
- goto theend;
+ return FAIL;
}
- dirname_start = xmalloc(MAXPATHL);
- dirname_now = xmalloc(MAXPATHL);
+ return OK;
+}
- // Remember the current directory, because a BufRead autocommand that does
- // ":lcd %:p:h" changes the meaning of short path names.
- os_dirname(dirname_start, MAXPATHL);
+/// Search for a pattern in a list of files and populate the quickfix list with
+/// the matches.
+static int vgr_process_files(win_T *wp, qf_info_T *qi, vgr_args_T *cmd_args, bool *redraw_for_dummy,
+ buf_T **first_match_buf, char **target_dir)
+{
+ int status = FAIL;
+ unsigned save_qfid = qf_get_curlist(qi)->qf_id;
+ bool duplicate_name = false;
- incr_quickfix_busy();
+ char *dirname_start = xmalloc(MAXPATHL);
+ char *dirname_now = xmalloc(MAXPATHL);
- // Remember the current quickfix list identifier, so that we can check for
- // autocommands changing the current quickfix list.
- unsigned save_qfid = qf_get_curlist(qi)->qf_id;
+ // Remember the current directory, because a BufRead autocommand that does
+ // ":lcd %:p:h" changes the meaning of short path names.
+ os_dirname((char_u *)dirname_start, MAXPATHL);
- seconds = (time_t)0;
- for (fi = 0; fi < fcount && !got_int && tomatch > 0; fi++) {
- fname = path_try_shorten_fname(fnames[fi]);
+ time_t seconds = (time_t)0;
+ for (int fi = 0; fi < cmd_args->fcount && !got_int && cmd_args->tomatch > 0; fi++) {
+ char *fname = (char *)path_try_shorten_fname((char_u *)cmd_args->fnames[fi]);
if (time(NULL) > seconds) {
// Display the file name every second or so, show the user we are
// working on it.
@@ -5389,13 +5462,13 @@ void ex_vimgrep(exarg_T *eap)
vgr_display_fname(fname);
}
- buf = buflist_findname_exp(fnames[fi]);
+ buf_T *buf = buflist_findname_exp(cmd_args->fnames[fi]);
+ bool using_dummy;
if (buf == NULL || buf->b_ml.ml_mfp == NULL) {
// Remember that a buffer with this name already exists.
duplicate_name = (buf != NULL);
- using_dummy = TRUE;
- redraw_for_dummy = TRUE;
-
+ using_dummy = true;
+ *redraw_for_dummy = true;
buf = vgr_load_dummy_buf(fname, dirname_start, dirname_now);
} else {
// Use existing, loaded buffer.
@@ -5404,11 +5477,10 @@ void ex_vimgrep(exarg_T *eap)
// Check whether the quickfix list is still valid. When loading a
// buffer above, autocommands might have changed the quickfix list.
- if (!vgr_qflist_valid(wp, qi, save_qfid, *eap->cmdlinep)) {
- FreeWild(fcount, fnames);
- decr_quickfix_busy();
+ if (!vgr_qflist_valid(wp, qi, save_qfid, cmd_args->qf_title)) {
goto theend;
}
+
save_qfid = qf_get_curlist(qi)->qf_id;
if (buf == NULL) {
@@ -5418,20 +5490,25 @@ void ex_vimgrep(exarg_T *eap)
} else {
// Try for a match in all lines of the buffer.
// For ":1vimgrep" look for first match only.
- found_match = vgr_match_buflines(qf_get_curlist(qi),
- fname, buf, &regmatch, &tomatch,
- duplicate_name, flags);
+ bool found_match = vgr_match_buflines(qf_get_curlist(qi),
+ fname,
+ buf,
+ cmd_args->spat,
+ &cmd_args->regmatch,
+ &cmd_args->tomatch,
+ duplicate_name,
+ cmd_args->flags);
if (using_dummy) {
- if (found_match && first_match_buf == NULL) {
- first_match_buf = buf;
+ if (found_match && *first_match_buf == NULL) {
+ *first_match_buf = buf;
}
if (duplicate_name) {
// Never keep a dummy buffer if there is another buffer
// with the same name.
wipe_dummy_buffer(buf, dirname_start);
buf = NULL;
- } else if (!cmdmod.hide
+ } else if ((cmdmod.cmod_flags & CMOD_HIDE) == 0
|| buf->b_p_bh[0] == 'u' // "unload"
|| buf->b_p_bh[0] == 'w' // "wipe"
|| buf->b_p_bh[0] == 'd') { // "delete"
@@ -5444,8 +5521,8 @@ void ex_vimgrep(exarg_T *eap)
if (!found_match) {
wipe_dummy_buffer(buf, dirname_start);
buf = NULL;
- } else if (buf != first_match_buf
- || (flags & VGR_NOJUMP)
+ } else if (buf != *first_match_buf
+ || (cmd_args->flags & VGR_NOJUMP)
|| existing_swapfile(buf)) {
unload_dummy_buffer(buf, dirname_start);
// Keeping the buffer, remove the dummy flag.
@@ -5460,18 +5537,19 @@ void ex_vimgrep(exarg_T *eap)
// If the buffer is still loaded we need to use the
// directory we jumped to below.
- if (buf == first_match_buf
- && target_dir == NULL
+ if (buf == *first_match_buf
+ && *target_dir == NULL
&& STRCMP(dirname_start, dirname_now) != 0) {
- target_dir = vim_strsave(dirname_now);
+ *target_dir = xstrdup(dirname_now);
}
// The buffer is still loaded, the Filetype autocommands
// need to be done now, in that buffer. And the modelines
// need to be done (again). But not the window-local
// options!
+ aco_save_T aco;
aucmd_prepbuf(&aco, buf);
- apply_autocmds(EVENT_FILETYPE, buf->b_p_ft, buf->b_fname, true, buf);
+ apply_autocmds(EVENT_FILETYPE, (char *)buf->b_p_ft, buf->b_fname, true, buf);
do_modelines(OPT_NOWIN);
aucmd_restbuf(&aco);
}
@@ -5479,9 +5557,58 @@ void ex_vimgrep(exarg_T *eap)
}
}
- FreeWild(fcount, fnames);
+ status = OK;
- qfl = qf_get_curlist(qi);
+theend:
+ xfree(dirname_now);
+ xfree(dirname_start);
+ return status;
+}
+
+/// ":vimgrep {pattern} file(s)"
+/// ":vimgrepadd {pattern} file(s)"
+/// ":lvimgrep {pattern} file(s)"
+/// ":lvimgrepadd {pattern} file(s)"
+void ex_vimgrep(exarg_T *eap)
+{
+ char *au_name = vgr_get_auname(eap->cmdidx);
+ if (au_name != NULL && apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name,
+ curbuf->b_fname, true, curbuf)) {
+ if (aborting()) {
+ return;
+ }
+ }
+
+ win_T *wp = NULL;
+ qf_info_T *qi = qf_cmd_get_or_alloc_stack(eap, &wp);
+ char *target_dir = NULL;
+ vgr_args_T args;
+ if (vgr_process_args(eap, &args) == FAIL) {
+ goto theend;
+ }
+
+ if ((eap->cmdidx != CMD_grepadd && eap->cmdidx != CMD_lgrepadd
+ && eap->cmdidx != CMD_vimgrepadd && eap->cmdidx != CMD_lvimgrepadd)
+ || qf_stack_empty(qi)) {
+ // make place for a new list
+ qf_new_list(qi, args.qf_title);
+ }
+
+ incr_quickfix_busy();
+
+ bool redraw_for_dummy = false;
+ buf_T *first_match_buf = NULL;
+ int status = vgr_process_files(wp, qi, &args, &redraw_for_dummy, &first_match_buf, &target_dir);
+
+ if (status != OK) {
+ FreeWild(args.fcount, (char_u **)args.fnames);
+ decr_quickfix_busy();
+ goto theend;
+ }
+
+ FreeWild(args.fcount, (char_u **)args.fnames);
+
+ qf_list_T *qfl = qf_get_curlist(qi);
qfl->qf_nonevalid = false;
qfl->qf_ptr = qfl->qf_start;
qfl->qf_index = 1;
@@ -5489,26 +5616,28 @@ void ex_vimgrep(exarg_T *eap)
qf_update_buffer(qi, NULL);
+ // Remember the current quickfix list identifier, so that we can check for
+ // autocommands changing the current quickfix list.
+ unsigned save_qfid = qf_get_curlist(qi)->qf_id;
+
if (au_name != NULL) {
apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, curbuf->b_fname, true, curbuf);
}
// The QuickFixCmdPost autocmd may free the quickfix list. Check the list
// is still valid.
- if (!qflist_valid(wp, save_qfid)
- || qf_restore_list(qi, save_qfid) == FAIL) {
+ if (!qflist_valid(wp, save_qfid) || qf_restore_list(qi, save_qfid) == FAIL) {
decr_quickfix_busy();
goto theend;
}
// Jump to first match.
if (!qf_list_empty(qf_get_curlist(qi))) {
- if ((flags & VGR_NOJUMP) == 0) {
- vgr_jump_to_match(qi, eap->forceit, &redraw_for_dummy, first_match_buf,
- target_dir);
+ if ((args.flags & VGR_NOJUMP) == 0) {
+ vgr_jump_to_match(qi, eap->forceit, &redraw_for_dummy, first_match_buf, target_dir);
}
} else {
- semsg(_(e_nomatch2), s);
+ semsg(_(e_nomatch2), args.spat);
}
decr_quickfix_busy();
@@ -5520,21 +5649,19 @@ void ex_vimgrep(exarg_T *eap)
}
theend:
- xfree(title);
- xfree(dirname_now);
- xfree(dirname_start);
+ xfree(args.qf_title);
xfree(target_dir);
- vim_regfree(regmatch.regprog);
+ vim_regfree(args.regmatch.regprog);
}
// Restore current working directory to "dirname_start" if they differ, taking
// into account whether it is set locally or globally.
-static void restore_start_dir(char_u *dirname_start)
+static void restore_start_dir(char *dirname_start)
FUNC_ATTR_NONNULL_ALL
{
- char_u *dirname_now = xmalloc(MAXPATHL);
+ char *dirname_now = xmalloc(MAXPATHL);
- os_dirname(dirname_now, MAXPATHL);
+ os_dirname((char_u *)dirname_now, MAXPATHL);
if (STRCMP(dirname_start, dirname_now) != 0) {
// If the directory has changed, change it back by building up an
// appropriate ex command and executing it.
@@ -5560,7 +5687,7 @@ static void restore_start_dir(char_u *dirname_start)
/// @param resulting_dir out: new directory
///
/// @return NULL if it fails.
-static buf_T *load_dummy_buffer(char_u *fname, char_u *dirname_start, char_u *resulting_dir)
+static buf_T *load_dummy_buffer(char *fname, char *dirname_start, char *resulting_dir)
{
buf_T *newbuf;
bufref_T newbufref;
@@ -5599,7 +5726,7 @@ static buf_T *load_dummy_buffer(char_u *fname, char_u *dirname_start, char_u *re
newbuf_to_wipe.br_buf = NULL;
readfile_result = readfile(fname, NULL, (linenr_T)0, (linenr_T)0,
(linenr_T)MAXLNUM, NULL,
- READ_NEW | READ_DUMMY);
+ READ_NEW | READ_DUMMY, false);
newbuf->b_locked--;
if (readfile_result == OK
&& !got_int
@@ -5629,7 +5756,7 @@ static buf_T *load_dummy_buffer(char_u *fname, char_u *dirname_start, char_u *re
// When autocommands/'autochdir' option changed directory: go back.
// Let the caller know what the resulting dir was first, in case it is
// important.
- os_dirname(resulting_dir, MAXPATHL);
+ os_dirname((char_u *)resulting_dir, MAXPATHL);
restore_start_dir(dirname_start);
if (!bufref_valid(&newbufref)) {
@@ -5645,7 +5772,7 @@ static buf_T *load_dummy_buffer(char_u *fname, char_u *dirname_start, char_u *re
// Wipe out the dummy buffer that load_dummy_buffer() created. Restores
// directory to "dirname_start" prior to returning, if autocmds or the
// 'autochdir' option have changed it.
-static void wipe_dummy_buffer(buf_T *buf, char_u *dirname_start)
+static void wipe_dummy_buffer(buf_T *buf, char *dirname_start)
FUNC_ATTR_NONNULL_ALL
{
// If any autocommand opened a window on the dummy buffer, close that
@@ -5656,7 +5783,7 @@ static void wipe_dummy_buffer(buf_T *buf, char_u *dirname_start)
if (firstwin->w_next != NULL) {
for (win_T *wp = firstwin; wp != NULL; wp = wp->w_next) {
if (wp->w_buffer == buf) {
- if (win_close(wp, false) == OK) {
+ if (win_close(wp, false, false) == OK) {
did_one = true;
}
break;
@@ -5689,10 +5816,10 @@ static void wipe_dummy_buffer(buf_T *buf, char_u *dirname_start)
// Unload the dummy buffer that load_dummy_buffer() created. Restores
// directory to "dirname_start" prior to returning, if autocmds or the
// 'autochdir' option have changed it.
-static void unload_dummy_buffer(buf_T *buf, char_u *dirname_start)
+static void unload_dummy_buffer(buf_T *buf, char *dirname_start)
{
if (curbuf != buf) { // safety check
- close_buffer(NULL, buf, DOBUF_UNLOAD, false);
+ close_buffer(NULL, buf, DOBUF_UNLOAD, false, true);
// When autocommands/'autochdir' option changed directory: go back.
restore_start_dir(dirname_start);
@@ -5703,7 +5830,7 @@ static void unload_dummy_buffer(buf_T *buf, char_u *dirname_start)
/// to 'list'. Returns OK on success.
static int get_qfline_items(qfline_T *qfp, list_T *list)
{
- char_u buf[2];
+ char buf[2];
int bufnum;
// Handle entries with a non-existing buffer number.
@@ -5822,7 +5949,7 @@ enum {
static int qf_get_list_from_lines(dict_T *what, dictitem_T *di, dict_T *retdict)
{
int status = FAIL;
- char_u *errorformat = p_efm;
+ char *errorformat = p_efm;
dictitem_T *efm_di;
// Only a List value is supported
@@ -5869,6 +5996,21 @@ static int qf_winid(qf_info_T *qi)
return 0;
}
+/// Returns the number of the buffer displayed in the quickfix/location list
+/// window. If there is no buffer associated with the list or the buffer is
+/// wiped out, then returns 0.
+static int qf_getprop_qfbufnr(const qf_info_T *qi, dict_T *retdict)
+ FUNC_ATTR_NONNULL_ARG(2)
+{
+ int bufnum = 0;
+
+ if (qi != NULL && buflist_findnr(qi->qf_bufnr) != NULL) {
+ bufnum = qi->qf_bufnr;
+ }
+
+ return tv_dict_add_nr(retdict, S_LEN("qfbufnr"), bufnum);
+}
+
/// Convert the keys in 'what' to quickfix list property flags.
static int qf_getprop_keys2flags(const dict_T *what, bool loclist)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
@@ -5912,6 +6054,9 @@ static int qf_getprop_keys2flags(const dict_T *what, bool loclist)
if (loclist && tv_dict_find(what, S_LEN("filewinid")) != NULL) {
flags |= QF_GETLIST_FILEWINID;
}
+ if (tv_dict_find(what, S_LEN("qfbufnr")) != NULL) {
+ flags |= QF_GETLIST_QFBUFNR;
+ }
if (tv_dict_find(what, S_LEN("quickfixtextfunc")) != NULL) {
flags |= QF_GETLIST_QFTF;
}
@@ -6003,6 +6148,9 @@ static int qf_getprop_defaults(qf_info_T *qi, int flags, int locstack, dict_T *r
if ((status == OK) && locstack && (flags & QF_GETLIST_FILEWINID)) {
status = tv_dict_add_nr(retdict, S_LEN("filewinid"), 0);
}
+ if ((status == OK) && (flags & QF_GETLIST_QFBUFNR)) {
+ status = qf_getprop_qfbufnr(qi, retdict);
+ }
if ((status == OK) && (flags & QF_GETLIST_QFTF)) {
status = tv_dict_add_str(retdict, S_LEN("quickfixtextfunc"), "");
}
@@ -6086,10 +6234,10 @@ static int qf_getprop_qftf(qf_list_T *qfl, dict_T *retdict)
{
int status;
- if (qfl->qftf_cb.type != kCallbackNone) {
+ if (qfl->qf_qftf_cb.type != kCallbackNone) {
typval_T tv;
- callback_put(&qfl->qftf_cb, &tv);
+ callback_put(&qfl->qf_qftf_cb, &tv);
status = tv_dict_add_tv(retdict, S_LEN("quickfixtextfunc"), &tv);
tv_clear(&tv);
} else {
@@ -6172,6 +6320,9 @@ int qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict)
if ((status == OK) && (wp != NULL) && (flags & QF_GETLIST_FILEWINID)) {
status = qf_getprop_filewinid(wp, qi, retdict);
}
+ if ((status == OK) && (flags & QF_GETLIST_QFBUFNR)) {
+ status = qf_getprop_qfbufnr(qi, retdict);
+ }
if ((status == OK) && (flags & QF_GETLIST_QFTF)) {
status = qf_getprop_qftf(qfl, retdict);
}
@@ -6186,9 +6337,9 @@ static int qf_setprop_qftf(qf_list_T *qfl, dictitem_T *di)
{
Callback cb;
- callback_free(&qfl->qftf_cb);
+ callback_free(&qfl->qf_qftf_cb);
if (callback_from_typval(&cb, &di->di_tv)) {
- qfl->qftf_cb = cb;
+ qfl->qf_qftf_cb = cb;
}
return OK;
}
@@ -6209,11 +6360,11 @@ static int qf_add_entry_from_dict(qf_list_T *qfl, const dict_T *d, bool first_en
char *const filename = tv_dict_get_string(d, "filename", true);
char *const module = tv_dict_get_string(d, "module", true);
int bufnum = (int)tv_dict_get_number(d, "bufnr");
- const long lnum = (long)tv_dict_get_number(d, "lnum");
- const long end_lnum = (long)tv_dict_get_number(d, "end_lnum");
+ const linenr_T lnum = (linenr_T)tv_dict_get_number(d, "lnum");
+ const linenr_T end_lnum = (linenr_T)tv_dict_get_number(d, "end_lnum");
const int col = (int)tv_dict_get_number(d, "col");
const int end_col = (int)tv_dict_get_number(d, "end_col");
- const char_u vcol = (char_u)tv_dict_get_number(d, "vcol");
+ const char vcol = (char)tv_dict_get_number(d, "vcol");
const int nr = (int)tv_dict_get_number(d, "nr");
const char *const type = tv_dict_get_string(d, "type", false);
char *const pattern = tv_dict_get_string(d, "pattern", true);
@@ -6245,18 +6396,18 @@ static int qf_add_entry_from_dict(qf_list_T *qfl, const dict_T *d, bool first_en
const int status = qf_add_entry(qfl,
NULL, // dir
- (char_u *)filename,
- (char_u *)module,
+ filename,
+ module,
bufnum,
- (char_u *)text,
+ text,
lnum,
end_lnum,
col,
end_col,
vcol, // vis_col
- (char_u *)pattern, // search pattern
+ pattern, // search pattern
nr,
- (char_u)(type == NULL ? NUL : *type),
+ type == NULL ? NUL : *type,
valid);
xfree(filename);
@@ -6273,7 +6424,7 @@ static int qf_add_entry_from_dict(qf_list_T *qfl, const dict_T *d, bool first_en
/// Add list of entries to quickfix/location list. Each list entry is
/// a dictionary with item information.
-static int qf_add_entries(qf_info_T *qi, int qf_idx, list_T *list, char_u *title, int action)
+static int qf_add_entries(qf_info_T *qi, int qf_idx, list_T *list, char *title, int action)
{
qf_list_T *qfl = qf_get_list(qi, qf_idx);
qfline_T *old_last = NULL;
@@ -6396,7 +6547,7 @@ static int qf_setprop_title(qf_info_T *qi, int qf_idx, const dict_T *what, const
}
xfree(qfl->qf_title);
- qfl->qf_title = (char_u *)tv_dict_get_string(what, "title", true);
+ qfl->qf_title = tv_dict_get_string(what, "title", true);
if (qf_idx == qi->qf_curlist) {
qf_update_win_titlevar(qi);
}
@@ -6412,9 +6563,8 @@ static int qf_setprop_items(qf_info_T *qi, int qf_idx, dictitem_T *di, int actio
return FAIL;
}
- char_u *title_save = vim_strsave(qi->qf_lists[qf_idx].qf_title);
- const int retval = qf_add_entries(qi, qf_idx, di->di_tv.vval.v_list,
- title_save,
+ char *title_save = xstrdup(qi->qf_lists[qf_idx].qf_title);
+ const int retval = qf_add_entries(qi, qf_idx, di->di_tv.vval.v_list, title_save,
action == ' ' ? 'a' : action);
xfree(title_save);
@@ -6426,7 +6576,7 @@ static int qf_setprop_items_from_lines(qf_info_T *qi, int qf_idx, const dict_T *
dictitem_T *di, int action)
FUNC_ATTR_NONNULL_ALL
{
- char_u *errorformat = p_efm;
+ char *errorformat = p_efm;
dictitem_T *efm_di;
int retval = FAIL;
@@ -6512,7 +6662,7 @@ static int qf_setprop_curidx(qf_info_T *qi, qf_list_T *qfl, const dictitem_T *di
/// Set quickfix/location list properties (title, items, context).
/// Also used to add items from parsing a list of lines.
/// Used by the setqflist() and setloclist() Vim script functions.
-static int qf_set_properties(qf_info_T *qi, const dict_T *what, int action, char_u *title)
+static int qf_set_properties(qf_info_T *qi, const dict_T *what, int action, char *title)
FUNC_ATTR_NONNULL_ALL
{
qf_list_T *qfl;
@@ -6560,20 +6710,6 @@ static int qf_set_properties(qf_info_T *qi, const dict_T *what, int action, char
return retval;
}
-/// Find the non-location list window with the specified location list stack in
-/// the current tabpage.
-static win_T *find_win_with_ll(const qf_info_T *qi)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
-{
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if ((wp->w_llist == qi) && !bt_quickfix(wp->w_buffer)) {
- return wp;
- }
- }
-
- return NULL;
-}
-
// Free the entire quickfix/location list stack.
// If the quickfix/location list window is open, then clear it.
static void qf_free_stack(win_T *wp, qf_info_T *qi)
@@ -6588,12 +6724,10 @@ static void qf_free_stack(win_T *wp, qf_info_T *qi)
qf_update_buffer(qi, NULL);
}
- win_T *llwin = NULL;
- win_T *orig_wp = wp;
if (wp != NULL && IS_LL_WINDOW(wp)) {
// If in the location list window, then use the non-location list
// window with this location list (if present)
- llwin = find_win_with_ll(qi);
+ win_T *const llwin = qf_find_win_with_loclist(qi);
if (llwin != NULL) {
wp = llwin;
}
@@ -6604,16 +6738,17 @@ static void qf_free_stack(win_T *wp, qf_info_T *qi)
// quickfix list
qi->qf_curlist = 0;
qi->qf_listcount = 0;
- } else if (IS_LL_WINDOW(orig_wp)) {
+ } else if (qfwin != NULL) {
// If the location list window is open, then create a new empty location
// list
qf_info_T *new_ll = qf_alloc_stack(QFLT_LOCATION);
+ new_ll->qf_bufnr = qfwin->w_buffer->b_fnum;
// first free the list reference in the location list window
- ll_free_all(&orig_wp->w_llist_ref);
+ ll_free_all(&qfwin->w_llist_ref);
- orig_wp->w_llist_ref = new_ll;
- if (llwin != NULL) {
+ qfwin->w_llist_ref = new_ll;
+ if (wp != qfwin) {
win_set_loclist(wp, new_ll);
}
}
@@ -6623,7 +6758,7 @@ static void qf_free_stack(win_T *wp, qf_info_T *qi)
// of dictionaries. "title" will be copied to w:quickfix_title
// "action" is 'a' for add, 'r' for replace. Otherwise create a new list.
// When "what" is not NULL then only set some properties.
-int set_errorlist(win_T *wp, list_T *list, int action, char_u *title, dict_T *what)
+int set_errorlist(win_T *wp, list_T *list, int action, char *title, dict_T *what)
{
qf_info_T *qi = &ql_info;
int retval = OK;
@@ -6708,21 +6843,21 @@ bool set_ref_in_quickfix(int copyID)
}
/// Return the autocmd name for the :cbuffer Ex commands
-static char_u *cbuffer_get_auname(cmdidx_T cmdidx)
+static char *cbuffer_get_auname(cmdidx_T cmdidx)
{
switch (cmdidx) {
case CMD_cbuffer:
- return (char_u *)"cbuffer";
+ return "cbuffer";
case CMD_cgetbuffer:
- return (char_u *)"cgetbuffer";
+ return "cgetbuffer";
case CMD_caddbuffer:
- return (char_u *)"caddbuffer";
+ return "caddbuffer";
case CMD_lbuffer:
- return (char_u *)"lbuffer";
+ return "lbuffer";
case CMD_lgetbuffer:
- return (char_u *)"lgetbuffer";
+ return "lgetbuffer";
case CMD_laddbuffer:
- return (char_u *)"laddbuffer";
+ return "laddbuffer";
default:
return NULL;
}
@@ -6737,7 +6872,7 @@ static int cbuffer_process_args(exarg_T *eap, buf_T **bufp, linenr_T *line1, lin
if (*eap->arg == NUL) {
buf = curbuf;
} else if (*skipwhite(skipdigits(eap->arg)) == NUL) {
- buf = buflist_findnr(atoi((char *)eap->arg));
+ buf = buflist_findnr(atoi(eap->arg));
}
if (buf == NULL) {
@@ -6777,9 +6912,9 @@ static int cbuffer_process_args(exarg_T *eap, buf_T **bufp, linenr_T *line1, lin
void ex_cbuffer(exarg_T *eap)
{
buf_T *buf = NULL;
- char_u *au_name = NULL;
+ char *au_name = NULL;
win_T *wp = NULL;
- char_u *qf_title;
+ char *qf_title;
linenr_T line1;
linenr_T line2;
@@ -6801,9 +6936,8 @@ void ex_cbuffer(exarg_T *eap)
qf_title = qf_cmdtitle(*eap->cmdlinep);
if (buf->b_sfname) {
- vim_snprintf((char *)IObuff, IOSIZE, "%s (%s)",
- (char *)qf_title, (char *)buf->b_sfname);
- qf_title = IObuff;
+ vim_snprintf((char *)IObuff, IOSIZE, "%s (%s)", qf_title, buf->b_sfname);
+ qf_title = (char *)IObuff;
}
incr_quickfix_busy();
@@ -6824,8 +6958,7 @@ void ex_cbuffer(exarg_T *eap)
unsigned save_qfid = qf_get_curlist(qi)->qf_id;
if (au_name != NULL) {
const buf_T *const curbuf_old = curbuf;
- apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name,
- curbuf->b_fname, true, curbuf);
+ apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, curbuf->b_fname, true, curbuf);
if (curbuf != curbuf_old) {
// Autocommands changed buffer, don't jump now, "qi" may
// be invalid.
@@ -6844,21 +6977,21 @@ void ex_cbuffer(exarg_T *eap)
}
/// Return the autocmd name for the :cexpr Ex commands.
-static char_u *cexpr_get_auname(cmdidx_T cmdidx)
+static char *cexpr_get_auname(cmdidx_T cmdidx)
{
switch (cmdidx) {
case CMD_cexpr:
- return (char_u *)"cexpr";
+ return "cexpr";
case CMD_cgetexpr:
- return (char_u *)"cgetexpr";
+ return "cgetexpr";
case CMD_caddexpr:
- return (char_u *)"caddexpr";
+ return "caddexpr";
case CMD_lexpr:
- return (char_u *)"lexpr";
+ return "lexpr";
case CMD_lgetexpr:
- return (char_u *)"lgetexpr";
+ return "lgetexpr";
case CMD_laddexpr:
- return (char_u *)"laddexpr";
+ return "laddexpr";
default:
return NULL;
}
@@ -6868,7 +7001,7 @@ static char_u *cexpr_get_auname(cmdidx_T cmdidx)
/// ":lexpr {expr}", ":lgetexpr {expr}", ":laddexpr {expr}" command.
void ex_cexpr(exarg_T *eap)
{
- char_u *au_name = NULL;
+ char *au_name = NULL;
win_T *wp = NULL;
au_name = cexpr_get_auname(eap->cmdidx);
@@ -6904,8 +7037,7 @@ void ex_cexpr(exarg_T *eap)
// check for autocommands changing the current quickfix list.
unsigned save_qfid = qf_get_curlist(qi)->qf_id;
if (au_name != NULL) {
- apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name,
- curbuf->b_fname, true, curbuf);
+ apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, curbuf->b_fname, true, curbuf);
}
// Jump to the first error for a new list and if autocmds didn't
// free the list.
@@ -6954,17 +7086,17 @@ static qf_info_T *hgr_get_ll(bool *new_ll)
}
// Search for a pattern in a help file.
-static void hgr_search_file(qf_list_T *qfl, char_u *fname, regmatch_T *p_regmatch)
+static void hgr_search_file(qf_list_T *qfl, char *fname, regmatch_T *p_regmatch)
FUNC_ATTR_NONNULL_ARG(1, 3)
{
- FILE *const fd = os_fopen((char *)fname, "r");
+ FILE *const fd = os_fopen(fname, "r");
if (fd == NULL) {
return;
}
- long lnum = 1;
+ linenr_T lnum = 1;
while (!vim_fgets(IObuff, IOSIZE, fd) && !got_int) {
- char_u *line = IObuff;
+ char *line = (char *)IObuff;
if (vim_regexec(p_regmatch, line, (colnr_T)0)) {
int l = (int)STRLEN(line);
@@ -6982,8 +7114,8 @@ static void hgr_search_file(qf_list_T *qfl, char_u *fname, regmatch_T *p_regmatc
line,
lnum,
0,
- (int)(p_regmatch->startp[0] - line) + 1, // col
- (int)(p_regmatch->endp[0] - line)
+ (int)(p_regmatch->startp[0] - (char_u *)line) + 1, // col
+ (int)(p_regmatch->endp[0] - (char_u *)line)
+ 1, // end_col
false, // vis_col
NULL, // search pattern
@@ -6992,13 +7124,13 @@ static void hgr_search_file(qf_list_T *qfl, char_u *fname, regmatch_T *p_regmatc
true) // valid
== QF_FAIL) {
got_int = true;
- if (line != IObuff) {
+ if ((char_u *)line != IObuff) {
xfree(line);
}
break;
}
}
- if (line != IObuff) {
+ if ((char_u *)line != IObuff) {
xfree(line);
}
lnum++;
@@ -7009,18 +7141,18 @@ static void hgr_search_file(qf_list_T *qfl, char_u *fname, regmatch_T *p_regmatc
// Search for a pattern in all the help files in the doc directory under
// the given directory.
-static void hgr_search_files_in_dir(qf_list_T *qfl, char_u *dirname, regmatch_T *p_regmatch,
- const char_u *lang)
+static void hgr_search_files_in_dir(qf_list_T *qfl, char *dirname, regmatch_T *p_regmatch,
+ const char *lang)
FUNC_ATTR_NONNULL_ARG(1, 2, 3)
{
int fcount;
- char_u **fnames;
+ char **fnames;
// Find all "*.txt" and "*.??x" files in the "doc" directory.
- add_pathsep((char *)dirname);
+ add_pathsep(dirname);
STRCAT(dirname, "doc/*.\\(txt\\|??x\\)"); // NOLINT
- if (gen_expand_wildcards(1, &dirname, &fcount,
- &fnames, EW_FILE|EW_SILENT) == OK
+ if (gen_expand_wildcards(1, (char_u **)&dirname, &fcount,
+ (char_u ***)&fnames, EW_FILE|EW_SILENT) == OK
&& fcount > 0) {
for (int fi = 0; fi < fcount && !got_int; fi++) {
// Skip files for a different language.
@@ -7034,7 +7166,7 @@ static void hgr_search_files_in_dir(qf_list_T *qfl, char_u *dirname, regmatch_T
hgr_search_file(qfl, fnames[fi], p_regmatch);
}
- FreeWild(fcount, fnames);
+ FreeWild(fcount, (char_u **)fnames);
}
}
@@ -7042,15 +7174,15 @@ static void hgr_search_files_in_dir(qf_list_T *qfl, char_u *dirname, regmatch_T
// and add the matches to a quickfix list.
// 'lang' is the language specifier. If supplied, then only matches in the
// specified language are found.
-static void hgr_search_in_rtp(qf_list_T *qfl, regmatch_T *p_regmatch, const char_u *lang)
+static void hgr_search_in_rtp(qf_list_T *qfl, regmatch_T *p_regmatch, const char *lang)
FUNC_ATTR_NONNULL_ARG(1, 2)
{
// Go through all directories in 'runtimepath'
- char_u *p = p_rtp;
+ char *p = (char *)p_rtp;
while (*p != NUL && !got_int) {
- copy_option_part(&p, NameBuff, MAXPATHL, ",");
+ copy_option_part(&p, (char *)NameBuff, MAXPATHL, ",");
- hgr_search_files_in_dir(qfl, NameBuff, p_regmatch, lang);
+ hgr_search_files_in_dir(qfl, (char *)NameBuff, p_regmatch, (char *)lang);
}
}
@@ -7059,13 +7191,13 @@ void ex_helpgrep(exarg_T *eap)
{
qf_info_T *qi = &ql_info;
bool new_qi = false;
- char_u *au_name = NULL;
+ char *au_name = NULL;
switch (eap->cmdidx) {
case CMD_helpgrep:
- au_name = (char_u *)"helpgrep"; break;
+ au_name = "helpgrep"; break;
case CMD_lhelpgrep:
- au_name = (char_u *)"lhelpgrep"; break;
+ au_name = "lhelpgrep"; break;
default:
break;
}
@@ -7077,8 +7209,8 @@ void ex_helpgrep(exarg_T *eap)
}
// Make 'cpoptions' empty, the 'l' flag should not be used here.
- char_u *const save_cpo = p_cpo;
- p_cpo = empty_option;
+ char *const save_cpo = p_cpo;
+ p_cpo = (char *)empty_option;
if (is_loclist_cmd(eap->cmdidx)) {
qi = hgr_get_ll(&new_qi);
@@ -7087,7 +7219,7 @@ void ex_helpgrep(exarg_T *eap)
incr_quickfix_busy();
// Check for a specified language
- char_u *const lang = check_help_lang(eap->arg);
+ char *const lang = check_help_lang(eap->arg);
regmatch_T regmatch = {
.regprog = vim_regcomp(eap->arg, RE_MAGIC + RE_STRING),
.rm_ic = false,
@@ -7108,16 +7240,15 @@ void ex_helpgrep(exarg_T *eap)
qf_update_buffer(qi, NULL);
}
- if (p_cpo == empty_option) {
+ if ((char_u *)p_cpo == empty_option) {
p_cpo = save_cpo;
} else {
// Darn, some plugin changed the value.
- free_string_option(save_cpo);
+ free_string_option((char_u *)save_cpo);
}
if (au_name != NULL) {
- apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name,
- curbuf->b_fname, true, curbuf);
+ apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, curbuf->b_fname, true, curbuf);
// When adding a location list to an existing location list stack,
// if the autocmd made the stack invalid, then just return.
if (!new_qi && IS_LL_STACK(qi) && qf_find_win_with_loclist(qi) == NULL) {
@@ -7147,4 +7278,3 @@ void ex_helpgrep(exarg_T *eap)
}
}
}
-
diff --git a/src/nvim/quickfix.h b/src/nvim/quickfix.h
index f5178e332a..0da43e436c 100644
--- a/src/nvim/quickfix.h
+++ b/src/nvim/quickfix.h
@@ -7,6 +7,7 @@
// flags for skip_vimgrep_pat()
#define VGR_GLOBAL 1
#define VGR_NOJUMP 2
+#define VGR_FUZZY 4
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "quickfix.h.generated.h"
diff --git a/src/nvim/rbuffer.c b/src/nvim/rbuffer.c
index 4ac50095b3..6407ac172e 100644
--- a/src/nvim/rbuffer.c
+++ b/src/nvim/rbuffer.c
@@ -154,6 +154,23 @@ void rbuffer_consumed(RBuffer *buf, size_t count)
}
}
+/// Use instead of rbuffer_consumed to use rbuffer in a linear, non-cyclic fashion.
+///
+/// This is generally usefull if we can guarantee to parse all input
+/// except some small incomplete token, like when parsing msgpack.
+void rbuffer_consumed_compact(RBuffer *buf, size_t count)
+ FUNC_ATTR_NONNULL_ALL
+{
+ assert(buf->read_ptr <= buf->write_ptr);
+ rbuffer_consumed(buf, count);
+ if (buf->read_ptr > buf->start_ptr) {
+ assert((size_t)(buf->read_ptr - buf->write_ptr) == buf->size);
+ memmove(buf->start_ptr, buf->read_ptr, buf->size);
+ buf->read_ptr = buf->start_ptr;
+ buf->write_ptr = buf->read_ptr + buf->size;
+ }
+}
+
// Higher level functions for copying from/to RBuffer instances and data
// pointers
size_t rbuffer_write(RBuffer *buf, const char *src, size_t src_size)
@@ -224,4 +241,3 @@ int rbuffer_cmp(RBuffer *buf, const char *str, size_t count)
return memcmp(str + n, buf->start_ptr, count);
}
-
diff --git a/src/nvim/rbuffer.h b/src/nvim/rbuffer.h
index cc690050ab..3ebbc9d82c 100644
--- a/src/nvim/rbuffer.h
+++ b/src/nvim/rbuffer.h
@@ -36,20 +36,19 @@
//
// Note that the rbuffer_{produced,consumed} calls are necessary or these macros
// create infinite loops
+//
+// -V:RBUFFER_UNTIL_EMPTY:1044
#define RBUFFER_UNTIL_EMPTY(buf, rptr, rcnt) \
for (size_t rcnt = 0, _r = 1; _r; _r = 0) /* NOLINT(readability/braces) */ \
- for ( /* NOLINT(readability/braces) */ \
- char *rptr = rbuffer_read_ptr(buf, &rcnt); \
- buf->size; \
- rptr = rbuffer_read_ptr(buf, &rcnt))
+ for (char *rptr = rbuffer_read_ptr(buf, &rcnt); /* NOLINT(readability/braces) */ \
+ buf->size; \
+ rptr = rbuffer_read_ptr(buf, &rcnt))
#define RBUFFER_UNTIL_FULL(buf, wptr, wcnt) \
for (size_t wcnt = 0, _r = 1; _r; _r = 0) /* NOLINT(readability/braces) */ \
- for ( /* NOLINT(readability/braces) */ \
- char *wptr = rbuffer_write_ptr(buf, &wcnt); \
- rbuffer_space(buf); \
- wptr = rbuffer_write_ptr(buf, &wcnt))
-
+ for (char *wptr = rbuffer_write_ptr(buf, &wcnt); /* NOLINT(readability/braces) */ \
+ rbuffer_space(buf); \
+ wptr = rbuffer_write_ptr(buf, &wcnt))
// Iteration
#define RBUFFER_EACH(buf, c, i) \
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index 45e580dbee..4c49d30819 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -1,45 +1,8 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
-// uncrustify:off
-
/*
* Handling of regular expressions: vim_regcomp(), vim_regexec(), vim_regsub()
- *
- * NOTICE:
- *
- * This is NOT the original regular expression code as written by Henry
- * Spencer. This code has been modified specifically for use with the VIM
- * editor, and should not be used separately from Vim. If you want a good
- * regular expression library, get the original code. The copyright notice
- * that follows is from the original.
- *
- * END NOTICE
- *
- * Copyright (c) 1986 by University of Toronto.
- * Written by Henry Spencer. Not derived from licensed software.
- *
- * Permission is granted to anyone to use this software for any
- * purpose on any computer system, and to redistribute it freely,
- * subject to the following restrictions:
- *
- * 1. The author is not responsible for the consequences of use of
- * this software, no matter how awful, even if they arise
- * from defects in it.
- *
- * 2. The origin of this software must not be misrepresented, either
- * by explicit claim or by omission.
- *
- * 3. Altered versions must be plainly marked as such, and must not
- * be misrepresented as being the original software.
- *
- * Beware that some of this code is subtly aware of the way operator
- * precedence is structured in regular expressions. Serious changes in
- * regular-expression syntax might require a total rethink.
- *
- * Changes have been made by Tony Andrews, Olaf 'Rhialto' Seibert, Robert
- * Webb, Ciaran McCreesh and Bram Moolenaar.
- * Named character class support added by Walter Briscoe (1998 Jul 01)
*/
// By default: do not create debugging logs or files related to regular
@@ -53,222 +16,32 @@
#include <stdbool.h>
#include <string.h>
-#include "nvim/vim.h"
#include "nvim/ascii.h"
-#include "nvim/regexp.h"
#include "nvim/charset.h"
#include "nvim/eval.h"
#include "nvim/eval/userfunc.h"
#include "nvim/ex_cmds2.h"
+#include "nvim/garray.h"
#include "nvim/mark.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/os/input.h"
#include "nvim/plines.h"
-#include "nvim/garray.h"
+#include "nvim/regexp.h"
#include "nvim/strings.h"
+#include "nvim/vim.h"
#ifdef REGEXP_DEBUG
-/* show/save debugging data when BT engine is used */
+// show/save debugging data when BT engine is used
# define BT_REGEXP_DUMP
-/* save the debugging data to a file instead of displaying it */
+// save the debugging data to a file instead of displaying it
# define BT_REGEXP_LOG
# define BT_REGEXP_DEBUG_LOG
# define BT_REGEXP_DEBUG_LOG_NAME "bt_regexp_debug.log"
#endif
/*
- * The "internal use only" fields in regexp_defs.h are present to pass info from
- * compile to execute that permits the execute phase to run lots faster on
- * simple cases. They are:
- *
- * regstart char that must begin a match; NUL if none obvious; Can be a
- * multi-byte character.
- * reganch is the match anchored (at beginning-of-line only)?
- * regmust string (pointer into program) that match must include, or NULL
- * regmlen length of regmust string
- * regflags RF_ values or'ed together
- *
- * Regstart and reganch permit very fast decisions on suitable starting points
- * for a match, cutting down the work a lot. Regmust permits fast rejection
- * of lines that cannot possibly match. The regmust tests are costly enough
- * that vim_regcomp() supplies a regmust only if the r.e. contains something
- * potentially expensive (at present, the only such thing detected is * or +
- * at the start of the r.e., which can involve a lot of backup). Regmlen is
- * supplied because the test in vim_regexec() needs it and vim_regcomp() is
- * computing it anyway.
- */
-
-/*
- * Structure for regexp "program". This is essentially a linear encoding
- * of a nondeterministic finite-state machine (aka syntax charts or
- * "railroad normal form" in parsing technology). Each node is an opcode
- * plus a "next" pointer, possibly plus an operand. "Next" pointers of
- * all nodes except BRANCH and BRACES_COMPLEX implement concatenation; a "next"
- * pointer with a BRANCH on both ends of it is connecting two alternatives.
- * (Here we have one of the subtle syntax dependencies: an individual BRANCH
- * (as opposed to a collection of them) is never concatenated with anything
- * because of operator precedence). The "next" pointer of a BRACES_COMPLEX
- * node points to the node after the stuff to be repeated.
- * The operand of some types of node is a literal string; for others, it is a
- * node leading into a sub-FSM. In particular, the operand of a BRANCH node
- * is the first node of the branch.
- * (NB this is *not* a tree structure: the tail of the branch connects to the
- * thing following the set of BRANCHes.)
- *
- * pattern is coded like:
- *
- * +-----------------+
- * | V
- * <aa>\|<bb> BRANCH <aa> BRANCH <bb> --> END
- * | ^ | ^
- * +------+ +----------+
- *
- *
- * +------------------+
- * V |
- * <aa>* BRANCH BRANCH <aa> --> BACK BRANCH --> NOTHING --> END
- * | | ^ ^
- * | +---------------+ |
- * +---------------------------------------------+
- *
- *
- * +----------------------+
- * V |
- * <aa>\+ BRANCH <aa> --> BRANCH --> BACK BRANCH --> NOTHING --> END
- * | | ^ ^
- * | +-----------+ |
- * +--------------------------------------------------+
- *
- *
- * +-------------------------+
- * V |
- * <aa>\{} BRANCH BRACE_LIMITS --> BRACE_COMPLEX <aa> --> BACK END
- * | | ^
- * | +----------------+
- * +-----------------------------------------------+
- *
- *
- * <aa>\@!<bb> BRANCH NOMATCH <aa> --> END <bb> --> END
- * | | ^ ^
- * | +----------------+ |
- * +--------------------------------+
- *
- * +---------+
- * | V
- * \z[abc] BRANCH BRANCH a BRANCH b BRANCH c BRANCH NOTHING --> END
- * | | | | ^ ^
- * | | | +-----+ |
- * | | +----------------+ |
- * | +---------------------------+ |
- * +------------------------------------------------------+
- *
- * They all start with a BRANCH for "\|" alternatives, even when there is only
- * one alternative.
- */
-
-/*
- * The opcodes are:
- */
-
-/* definition number opnd? meaning */
-#define END 0 /* End of program or NOMATCH operand. */
-#define BOL 1 /* Match "" at beginning of line. */
-#define EOL 2 /* Match "" at end of line. */
-#define BRANCH 3 /* node Match this alternative, or the
- * next... */
-#define BACK 4 /* Match "", "next" ptr points backward. */
-#define EXACTLY 5 /* str Match this string. */
-#define NOTHING 6 /* Match empty string. */
-#define STAR 7 /* node Match this (simple) thing 0 or more
- * times. */
-#define PLUS 8 /* node Match this (simple) thing 1 or more
- * times. */
-#define MATCH 9 /* node match the operand zero-width */
-#define NOMATCH 10 /* node check for no match with operand */
-#define BEHIND 11 /* node look behind for a match with operand */
-#define NOBEHIND 12 /* node look behind for no match with operand */
-#define SUBPAT 13 /* node match the operand here */
-#define BRACE_SIMPLE 14 /* node Match this (simple) thing between m and
- * n times (\{m,n\}). */
-#define BOW 15 /* Match "" after [^a-zA-Z0-9_] */
-#define EOW 16 /* Match "" at [^a-zA-Z0-9_] */
-#define BRACE_LIMITS 17 /* nr nr define the min & max for BRACE_SIMPLE
- * and BRACE_COMPLEX. */
-#define NEWL 18 /* Match line-break */
-#define BHPOS 19 /* End position for BEHIND or NOBEHIND */
-
-
-/* character classes: 20-48 normal, 50-78 include a line-break */
-#define ADD_NL 30
-#define FIRST_NL ANY + ADD_NL
-#define ANY 20 /* Match any one character. */
-#define ANYOF 21 /* str Match any character in this string. */
-#define ANYBUT 22 /* str Match any character not in this
- * string. */
-#define IDENT 23 /* Match identifier char */
-#define SIDENT 24 /* Match identifier char but no digit */
-#define KWORD 25 /* Match keyword char */
-#define SKWORD 26 /* Match word char but no digit */
-#define FNAME 27 /* Match file name char */
-#define SFNAME 28 /* Match file name char but no digit */
-#define PRINT 29 /* Match printable char */
-#define SPRINT 30 /* Match printable char but no digit */
-#define WHITE 31 /* Match whitespace char */
-#define NWHITE 32 /* Match non-whitespace char */
-#define DIGIT 33 /* Match digit char */
-#define NDIGIT 34 /* Match non-digit char */
-#define HEX 35 /* Match hex char */
-#define NHEX 36 /* Match non-hex char */
-#define OCTAL 37 /* Match octal char */
-#define NOCTAL 38 /* Match non-octal char */
-#define WORD 39 /* Match word char */
-#define NWORD 40 /* Match non-word char */
-#define HEAD 41 /* Match head char */
-#define NHEAD 42 /* Match non-head char */
-#define ALPHA 43 /* Match alpha char */
-#define NALPHA 44 /* Match non-alpha char */
-#define LOWER 45 /* Match lowercase char */
-#define NLOWER 46 /* Match non-lowercase char */
-#define UPPER 47 /* Match uppercase char */
-#define NUPPER 48 /* Match non-uppercase char */
-#define LAST_NL NUPPER + ADD_NL
-// -V:WITH_NL:560
-#define WITH_NL(op) ((op) >= FIRST_NL && (op) <= LAST_NL)
-
-#define MOPEN 80 // -89 Mark this point in input as start of
- // \( … \) subexpr. MOPEN + 0 marks start of
- // match.
-#define MCLOSE 90 // -99 Analogous to MOPEN. MCLOSE + 0 marks
- // end of match.
-#define BACKREF 100 // -109 node Match same string again \1-\9.
-
-# define ZOPEN 110 // -119 Mark this point in input as start of
- // \z( … \) subexpr.
-# define ZCLOSE 120 // -129 Analogous to ZOPEN.
-# define ZREF 130 // -139 node Match external submatch \z1-\z9
-
-#define BRACE_COMPLEX 140 /* -149 node Match nodes between m & n times */
-
-#define NOPEN 150 // Mark this point in input as start of
- // \%( subexpr.
-#define NCLOSE 151 // Analogous to NOPEN.
-
-#define MULTIBYTECODE 200 /* mbc Match one multi-byte character */
-#define RE_BOF 201 /* Match "" at beginning of file. */
-#define RE_EOF 202 /* Match "" at end of file. */
-#define CURSOR 203 /* Match location of cursor. */
-
-#define RE_LNUM 204 /* nr cmp Match line number */
-#define RE_COL 205 /* nr cmp Match column number */
-#define RE_VCOL 206 /* nr cmp Match virtual column number */
-
-#define RE_MARK 207 /* mark cmp Match mark position */
-#define RE_VISUAL 208 /* Match Visual area */
-#define RE_COMPOSING 209 // any composing characters
-
-/*
* Magic characters have a special meaning, they don't match literally.
* Magic characters are negative. This separates them from literal characters
* (possibly multi-byte). Only ASCII characters can be Magic.
@@ -285,181 +58,29 @@
*/
typedef void (*(*fptr_T)(int *, int))(void);
-typedef struct {
- char_u *regparse;
- int prevchr_len;
- int curchr;
- int prevchr;
- int prevprevchr;
- int nextchr;
- int at_start;
- int prev_at_start;
- int regnpar;
-} parse_state_T;
-
-/*
- * Structure used to save the current input state, when it needs to be
- * restored after trying a match. Used by reg_save() and reg_restore().
- * Also stores the length of "backpos".
- */
-typedef struct {
- union {
- char_u *ptr; ///< rex.input pointer, for single-line regexp
- lpos_T pos; ///< rex.input pos, for multi-line regexp
- } rs_u;
- int rs_len;
-} regsave_T;
-
-/* struct to save start/end pointer/position in for \(\) */
-typedef struct {
- union {
- char_u *ptr;
- lpos_T pos;
- } se_u;
-} save_se_T;
-
-/* used for BEHIND and NOBEHIND matching */
-typedef struct regbehind_S {
- regsave_T save_after;
- regsave_T save_behind;
- int save_need_clear_subexpr;
- save_se_T save_start[NSUBEXP];
- save_se_T save_end[NSUBEXP];
-} regbehind_T;
-
-/* Values for rs_state in regitem_T. */
-typedef enum regstate_E {
- RS_NOPEN = 0 /* NOPEN and NCLOSE */
- , RS_MOPEN /* MOPEN + [0-9] */
- , RS_MCLOSE /* MCLOSE + [0-9] */
- , RS_ZOPEN /* ZOPEN + [0-9] */
- , RS_ZCLOSE /* ZCLOSE + [0-9] */
- , RS_BRANCH /* BRANCH */
- , RS_BRCPLX_MORE /* BRACE_COMPLEX and trying one more match */
- , RS_BRCPLX_LONG /* BRACE_COMPLEX and trying longest match */
- , RS_BRCPLX_SHORT /* BRACE_COMPLEX and trying shortest match */
- , RS_NOMATCH /* NOMATCH */
- , RS_BEHIND1 /* BEHIND / NOBEHIND matching rest */
- , RS_BEHIND2 /* BEHIND / NOBEHIND matching behind part */
- , RS_STAR_LONG /* STAR/PLUS/BRACE_SIMPLE longest match */
- , RS_STAR_SHORT /* STAR/PLUS/BRACE_SIMPLE shortest match */
-} regstate_T;
-
-/*
- * When there are alternatives a regstate_T is put on the regstack to remember
- * what we are doing.
- * Before it may be another type of item, depending on rs_state, to remember
- * more things.
- */
-typedef struct regitem_S {
- regstate_T rs_state; // what we are doing, one of RS_ above
- uint16_t rs_no; // submatch nr or BEHIND/NOBEHIND
- char_u *rs_scan; // current node in program
- union {
- save_se_T sesave;
- regsave_T regsave;
- } rs_un; ///< room for saving rex.input
-} regitem_T;
-
-
-/* used for STAR, PLUS and BRACE_SIMPLE matching */
-typedef struct regstar_S {
- int nextb; /* next byte */
- int nextb_ic; /* next byte reverse case */
- long count;
- long minval;
- long maxval;
-} regstar_T;
-
-/* used to store input position when a BACK was encountered, so that we now if
- * we made any progress since the last time. */
-typedef struct backpos_S {
- char_u *bp_scan; /* "scan" where BACK was encountered */
- regsave_T bp_pos; /* last input position */
-} backpos_T;
-
-typedef struct {
- int a, b, c;
-} decomp_T;
-
-
-#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "regexp.c.generated.h"
-#endif
static int no_Magic(int x)
{
- if (is_Magic(x))
+ if (is_Magic(x)) {
return un_Magic(x);
+ }
return x;
}
static int toggle_Magic(int x)
{
- if (is_Magic(x))
+ if (is_Magic(x)) {
return un_Magic(x);
+ }
return Magic(x);
}
-/*
- * The first byte of the regexp internal "program" is actually this magic
- * number; the start node begins in the second byte. It's used to catch the
- * most severe mutilation of the program by the caller.
- */
-
+// The first byte of the BT regexp internal "program" is actually this magic
+// number; the start node begins in the second byte. It's used to catch the
+// most severe mutilation of the program by the caller.
#define REGMAGIC 0234
-/*
- * Opcode notes:
- *
- * BRANCH The set of branches constituting a single choice are hooked
- * together with their "next" pointers, since precedence prevents
- * anything being concatenated to any individual branch. The
- * "next" pointer of the last BRANCH in a choice points to the
- * thing following the whole choice. This is also where the
- * final "next" pointer of each individual branch points; each
- * branch starts with the operand node of a BRANCH node.
- *
- * BACK Normal "next" pointers all implicitly point forward; BACK
- * exists to make loop structures possible.
- *
- * STAR,PLUS '=', and complex '*' and '+', are implemented as circular
- * BRANCH structures using BACK. Simple cases (one character
- * per match) are implemented with STAR and PLUS for speed
- * and to minimize recursive plunges.
- *
- * BRACE_LIMITS This is always followed by a BRACE_SIMPLE or BRACE_COMPLEX
- * node, and defines the min and max limits to be used for that
- * node.
- *
- * MOPEN,MCLOSE ...are numbered at compile time.
- * ZOPEN,ZCLOSE ...ditto
- */
-
-/*
- * A node is one char of opcode followed by two chars of "next" pointer.
- * "Next" pointers are stored as two 8-bit bytes, high order first. The
- * value is a positive offset from the opcode of the node containing it.
- * An operand, if any, simply follows the node. (Note that much of the
- * code generation knows about this implicit relationship.)
- *
- * Using two bytes for the "next" pointer is vast overkill for most things,
- * but allows patterns to get big without disasters.
- */
-#define OP(p) ((int)*(p))
-#define NEXT(p) (((*((p) + 1) & 0377) << 8) + (*((p) + 2) & 0377))
-#define OPERAND(p) ((p) + 3)
-/* Obtain an operand that was stored as four bytes, MSB first. */
-#define OPERAND_MIN(p) (((long)(p)[3] << 24) + ((long)(p)[4] << 16) \
- + ((long)(p)[5] << 8) + (long)(p)[6])
-/* Obtain a second operand stored as four bytes. */
-#define OPERAND_MAX(p) OPERAND_MIN((p) + 4)
-/* Obtain a second single-byte operand stored after a four bytes operand. */
-#define OPERAND_CMP(p) (p)[7]
-
-/*
- * Utility definitions.
- */
-#define UCHARAT(p) ((int)*(char_u *)(p))
+// Utility definitions.
+#define UCHARAT(p) ((int)(*(char_u *)(p)))
// Used for an error (down from) vim_regcomp(): give the error message, set
// rc_did_emsg and return NULL
@@ -467,22 +88,15 @@ static int toggle_Magic(int x)
#define IEMSG_RET_NULL(m) return (iemsg(m), rc_did_emsg = true, (void *)NULL)
#define EMSG_RET_FAIL(m) return (emsg(m), rc_did_emsg = true, FAIL)
#define EMSG2_RET_NULL(m, c) \
- return (semsg((m), (c) ? "" : "\\"), rc_did_emsg = true, (void *)NULL)
+ return (semsg((m), (c) ? "" : "\\"), rc_did_emsg = true, (void *)NULL)
+#define EMSG3_RET_NULL(m, c, a) \
+ return (semsg((const char *)(m), (c) ? "" : "\\", (a)), rc_did_emsg = true, (void *)NULL)
#define EMSG2_RET_FAIL(m, c) \
- return (semsg((m), (c) ? "" : "\\"), rc_did_emsg = true, FAIL)
-#define EMSG_ONE_RET_NULL EMSG2_RET_NULL(_( \
- "E369: invalid item in %s%%[]"), reg_magic == MAGIC_ALL)
+ return (semsg((m), (c) ? "" : "\\"), rc_did_emsg = true, FAIL)
+#define EMSG_ONE_RET_NULL EMSG2_RET_NULL(_("E369: invalid item in %s%%[]"), reg_magic == MAGIC_ALL)
#define MAX_LIMIT (32767L << 16L)
-
-#ifdef BT_REGEXP_DUMP
-static void regdump(char_u *, bt_regprog_T *);
-#endif
-#ifdef REGEXP_DEBUG
-static char_u *regprop(char_u *);
-#endif
-
static char_u e_missingbracket[] = N_("E769: Missing ] after %s[");
static char_u e_reverse_range[] = N_("E944: Reverse range in character class");
static char_u e_large_class[] = N_("E945: Range too large in character class");
@@ -492,63 +106,57 @@ static char_u e_unmatchedpar[] = N_("E55: Unmatched %s)");
static char_u e_z_not_allowed[] = N_("E66: \\z( not allowed here");
static char_u e_z1_not_allowed[] = N_("E67: \\z1 - \\z9 not allowed here");
static char_u e_missing_sb[] = N_("E69: Missing ] after %s%%[");
-static char_u e_empty_sb[] = N_("E70: Empty %s%%[]");
-static char_u e_recursive[] = N_("E956: Cannot use pattern recursively");
+static char_u e_empty_sb[] = N_("E70: Empty %s%%[]");
+static char_u e_recursive[] = N_("E956: Cannot use pattern recursively");
+static char_u e_regexp_number_after_dot_pos_search[]
+ = N_("E1204: No Number allowed after .: '\\%%%c'");
+static char_u e_substitute_nesting_too_deep[] = N_("E1290: substitute nesting too deep");
#define NOT_MULTI 0
#define MULTI_ONE 1
#define MULTI_MULT 2
-/*
- * Return NOT_MULTI if c is not a "multi" operator.
- * Return MULTI_ONE if c is a single "multi" operator.
- * Return MULTI_MULT if c is a multi "multi" operator.
- */
+
+// return values for regmatch()
+#define RA_FAIL 1 // something failed, abort
+#define RA_CONT 2 // continue in inner loop
+#define RA_BREAK 3 // break inner loop
+#define RA_MATCH 4 // successful match
+#define RA_NOMATCH 5 // didn't match
+
+/// Return NOT_MULTI if c is not a "multi" operator.
+/// Return MULTI_ONE if c is a single "multi" operator.
+/// Return MULTI_MULT if c is a multi "multi" operator.
static int re_multi_type(int c)
{
- if (c == Magic('@') || c == Magic('=') || c == Magic('?'))
+ if (c == Magic('@') || c == Magic('=') || c == Magic('?')) {
return MULTI_ONE;
- if (c == Magic('*') || c == Magic('+') || c == Magic('{'))
+ }
+ if (c == Magic('*') || c == Magic('+') || c == Magic('{')) {
return MULTI_MULT;
+ }
return NOT_MULTI;
}
-/*
- * Flags to be passed up and down.
- */
-#define HASWIDTH 0x1 /* Known never to match null string. */
-#define SIMPLE 0x2 /* Simple enough to be STAR/PLUS operand. */
-#define SPSTART 0x4 /* Starts with * or +. */
-#define HASNL 0x8 /* Contains some \n. */
-#define HASLOOKBH 0x10 /* Contains "\@<=" or "\@<!". */
-#define WORST 0 /* Worst case. */
-
-/*
- * When regcode is set to this value, code is not emitted and size is computed
- * instead.
- */
-#define JUST_CALC_SIZE ((char_u *) -1)
-
-static char_u *reg_prev_sub = NULL;
+static char_u *reg_prev_sub = NULL;
/*
* REGEXP_INRANGE contains all characters which are always special in a []
* range after '\'.
* REGEXP_ABBR contains all characters which act as abbreviations after '\'.
* These are:
- * \n - New line (NL).
- * \r - Carriage Return (CR).
- * \t - Tab (TAB).
- * \e - Escape (ESC).
- * \b - Backspace (Ctrl_H).
+ * \n - New line (NL).
+ * \r - Carriage Return (CR).
+ * \t - Tab (TAB).
+ * \e - Escape (ESC).
+ * \b - Backspace (Ctrl_H).
* \d - Character code in decimal, eg \d123
- * \o - Character code in octal, eg \o80
- * \x - Character code in hex, eg \x4a
- * \u - Multibyte character code, eg \u20ac
- * \U - Long multibyte character code, eg \U12345678
+ * \o - Character code in octal, eg \o80
+ * \x - Character code in hex, eg \x4a
+ * \u - Multibyte character code, eg \u20ac
+ * \U - Long multibyte character code, eg \U12345678
*/
-static char_u REGEXP_INRANGE[] = "]^-n\\";
-static char_u REGEXP_ABBR[] = "nrtebdoxuU";
-
+static char REGEXP_INRANGE[] = "]^-n\\";
+static char REGEXP_ABBR[] = "nrtebdoxuU";
/*
* Translate '\x' to its control character, except "\n", which is Magic.
@@ -556,10 +164,14 @@ static char_u REGEXP_ABBR[] = "nrtebdoxuU";
static int backslash_trans(int c)
{
switch (c) {
- case 'r': return CAR;
- case 't': return TAB;
- case 'e': return ESC;
- case 'b': return BS;
+ case 'r':
+ return CAR;
+ case 't':
+ return TAB;
+ case 'e':
+ return ESC;
+ case 'b':
+ return BS;
}
return c;
}
@@ -616,11 +228,12 @@ static int get_char_class(char_u **pp)
int i;
if ((*pp)[1] == ':') {
- for (i = 0; i < (int)ARRAY_SIZE(class_names); ++i)
+ for (i = 0; i < (int)ARRAY_SIZE(class_names); i++) {
if (STRNCMP(*pp + 2, class_names[i], STRLEN(class_names[i])) == 0) {
*pp += STRLEN(class_names[i]) + 2;
return i;
}
+ }
}
return CLASS_NONE;
}
@@ -646,74 +259,66 @@ static void init_class_tab(void)
int i;
static int done = false;
- if (done)
+ if (done) {
return;
+ }
- for (i = 0; i < 256; ++i) {
- if (i >= '0' && i <= '7')
+ for (i = 0; i < 256; i++) {
+ if (i >= '0' && i <= '7') {
class_tab[i] = RI_DIGIT + RI_HEX + RI_OCTAL + RI_WORD;
- else if (i >= '8' && i <= '9')
+ } else if (i >= '8' && i <= '9') {
class_tab[i] = RI_DIGIT + RI_HEX + RI_WORD;
- else if (i >= 'a' && i <= 'f')
+ } else if (i >= 'a' && i <= 'f') {
class_tab[i] = RI_HEX + RI_WORD + RI_HEAD + RI_ALPHA + RI_LOWER;
- else if (i >= 'g' && i <= 'z')
+ } else if (i >= 'g' && i <= 'z') {
class_tab[i] = RI_WORD + RI_HEAD + RI_ALPHA + RI_LOWER;
- else if (i >= 'A' && i <= 'F')
+ } else if (i >= 'A' && i <= 'F') {
class_tab[i] = RI_HEX + RI_WORD + RI_HEAD + RI_ALPHA + RI_UPPER;
- else if (i >= 'G' && i <= 'Z')
+ } else if (i >= 'G' && i <= 'Z') {
class_tab[i] = RI_WORD + RI_HEAD + RI_ALPHA + RI_UPPER;
- else if (i == '_')
+ } else if (i == '_') {
class_tab[i] = RI_WORD + RI_HEAD;
- else
+ } else {
class_tab[i] = 0;
+ }
}
class_tab[' '] |= RI_WHITE;
class_tab['\t'] |= RI_WHITE;
done = true;
}
-# define ri_digit(c) (c < 0x100 && (class_tab[c] & RI_DIGIT))
-# define ri_hex(c) (c < 0x100 && (class_tab[c] & RI_HEX))
-# define ri_octal(c) (c < 0x100 && (class_tab[c] & RI_OCTAL))
-# define ri_word(c) (c < 0x100 && (class_tab[c] & RI_WORD))
-# define ri_head(c) (c < 0x100 && (class_tab[c] & RI_HEAD))
-# define ri_alpha(c) (c < 0x100 && (class_tab[c] & RI_ALPHA))
-# define ri_lower(c) (c < 0x100 && (class_tab[c] & RI_LOWER))
-# define ri_upper(c) (c < 0x100 && (class_tab[c] & RI_UPPER))
-# define ri_white(c) (c < 0x100 && (class_tab[c] & RI_WHITE))
-
-/* flags for regflags */
-#define RF_ICASE 1 /* ignore case */
-#define RF_NOICASE 2 /* don't ignore case */
-#define RF_HASNL 4 /* can match a NL */
-#define RF_ICOMBINE 8 /* ignore combining characters */
-#define RF_LOOKBH 16 /* uses "\@<=" or "\@<!" */
+#define ri_digit(c) ((c) < 0x100 && (class_tab[c] & RI_DIGIT))
+#define ri_hex(c) ((c) < 0x100 && (class_tab[c] & RI_HEX))
+#define ri_octal(c) ((c) < 0x100 && (class_tab[c] & RI_OCTAL))
+#define ri_word(c) ((c) < 0x100 && (class_tab[c] & RI_WORD))
+#define ri_head(c) ((c) < 0x100 && (class_tab[c] & RI_HEAD))
+#define ri_alpha(c) ((c) < 0x100 && (class_tab[c] & RI_ALPHA))
+#define ri_lower(c) ((c) < 0x100 && (class_tab[c] & RI_LOWER))
+#define ri_upper(c) ((c) < 0x100 && (class_tab[c] & RI_UPPER))
+#define ri_white(c) ((c) < 0x100 && (class_tab[c] & RI_WHITE))
+
+// flags for regflags
+#define RF_ICASE 1 // ignore case
+#define RF_NOICASE 2 // don't ignore case
+#define RF_HASNL 4 // can match a NL
+#define RF_ICOMBINE 8 // ignore combining characters
+#define RF_LOOKBH 16 // uses "\@<=" or "\@<!"
// Global work variables for vim_regcomp().
static char_u *regparse; ///< Input-scan pointer.
-static int prevchr_len; ///< byte length of previous char
-static int num_complex_braces; ///< Complex \{...} count
static int regnpar; ///< () count.
static bool wants_nfa; ///< regex should use NFA engine
static int regnzpar; ///< \z() count.
static int re_has_z; ///< \z item detected
-static char_u *regcode; ///< Code-emit pointer, or JUST_CALC_SIZE
-static long regsize; ///< Code size.
-static int reg_toolong; ///< true when offset out of range
-static char_u had_endbrace[NSUBEXP]; ///< flags, true if end of () found
-static unsigned regflags; ///< RF_ flags for prog
-static long brace_min[10]; ///< Minimums for complex brace repeats
-static long brace_max[10]; ///< Maximums for complex brace repeats
-static int brace_count[10]; ///< Current counts for complex brace repeats
-static int had_eol; ///< true when EOL found by vim_regcomp()
-static int one_exactly = false; ///< only do one char for EXACTLY
-
-static int reg_magic; /* magicness of the pattern: */
-#define MAGIC_NONE 1 /* "\V" very unmagic */
-#define MAGIC_OFF 2 /* "\M" or 'magic' off */
-#define MAGIC_ON 3 /* "\m" or 'magic' */
-#define MAGIC_ALL 4 /* "\v" very magic */
+static unsigned regflags; ///< RF_ flags for prog
+static int had_eol; ///< true when EOL found by vim_regcomp()
+
+static int reg_magic; // magicness of the pattern:
+#define MAGIC_NONE 1 // "\V" very unmagic
+#define MAGIC_OFF 2 // "\M" or 'magic' off
+#define MAGIC_ON 3 // "\m" or 'magic'
+#define MAGIC_ALL 4 // "\v" very magic
static int reg_string; // matching with a string instead of a buffer
// line
@@ -723,47 +328,61 @@ static int reg_strict; // "[abc" is illegal
* META contains all characters that may be magic, except '^' and '$'.
*/
-/* META[] is used often enough to justify turning it into a table. */
+// uncrustify:off
+
+// META[] is used often enough to justify turning it into a table.
static char_u META_flags[] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- /* % & ( ) * + . */
- 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0,
- /* 1 2 3 4 5 6 7 8 9 < = > ? */
- 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1,
- /* @ A C D F H I K L M O */
- 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1,
- /* P S U V W X Z [ _ */
- 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1,
- /* a c d f h i k l m n o */
- 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1,
- /* p s u v w x z { | ~ */
- 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// % & ( ) * + .
+ 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0,
+// 1 2 3 4 5 6 7 8 9 < = > ?
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1,
+// @ A C D F H I K L M O
+ 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1,
+// P S U V W X Z [ _
+ 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1,
+// a c d f h i k l m n o
+ 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1,
+// p s u v w x z { | ~
+ 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1
};
+// uncrustify:on
+
static int curchr; // currently parsed character
// Previous character. Note: prevchr is sometimes -1 when we are not at the
// start, eg in /[ ^I]^ the pattern was never found even if it existed,
// because ^ was taken to be magic -- webb
static int prevchr;
-static int prevprevchr; /* previous-previous character */
-static int nextchr; /* used for ungetchr() */
+static int prevprevchr; // previous-previous character
+static int nextchr; // used for ungetchr()
-/* arguments for reg() */
-#define REG_NOPAREN 0 /* toplevel reg() */
-#define REG_PAREN 1 /* \(\) */
-#define REG_ZPAREN 2 /* \z(\) */
-#define REG_NPAREN 3 /* \%(\) */
+// arguments for reg()
+#define REG_NOPAREN 0 // toplevel reg()
+#define REG_PAREN 1 // \(\)
+#define REG_ZPAREN 2 // \z(\)
+#define REG_NPAREN 3 // \%(\)
-/*
- * Forward declarations for vim_regcomp()'s friends.
- */
-# define REGMBC(x) regmbc(x);
-# define CASEMBC(x) case x:
+typedef struct {
+ char_u *regparse;
+ int prevchr_len;
+ int curchr;
+ int prevchr;
+ int prevprevchr;
+ int nextchr;
+ int at_start;
+ int prev_at_start;
+ int regnpar;
+} parse_state_T;
static regengine_T bt_regengine;
static regengine_T nfa_regengine;
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "regexp.c.generated.h"
+#endif
+
// Return true if compiled regular expression "prog" can match a line break.
int re_multiline(const regprog_T *prog)
FUNC_ATTR_NONNULL_ALL
@@ -780,12 +399,12 @@ static int get_equi_class(char_u **pp)
{
int c;
int l = 1;
- char_u *p = *pp;
+ char_u *p = *pp;
if (p[1] == '=' && p[2] != NUL) {
- l = utfc_ptr2len(p + 2);
+ l = utfc_ptr2len((char *)p + 2);
if (p[l + 2] == '=' && p[l + 3] == ']') {
- c = utf_ptr2char(p + 2);
+ c = utf_ptr2char((char *)p + 2);
*pp += l + 4;
return c;
}
@@ -793,313 +412,6 @@ static int get_equi_class(char_u **pp)
return 0;
}
-
-/*
- * Produce the bytes for equivalence class "c".
- * Currently only handles latin1, latin9 and utf-8.
- * NOTE: When changing this function, also change nfa_emit_equi_class()
- */
-static void reg_equi_class(int c)
-{
- {
- switch (c) {
- // Do not use '\300' style, it results in a negative number.
- case 'A': case 0xc0: case 0xc1: case 0xc2:
- case 0xc3: case 0xc4: case 0xc5:
- CASEMBC(0x100) CASEMBC(0x102) CASEMBC(0x104) CASEMBC(0x1cd)
- CASEMBC(0x1de) CASEMBC(0x1e0) CASEMBC(0x1ea2)
- regmbc('A'); regmbc(0xc0); regmbc(0xc1);
- regmbc(0xc2); regmbc(0xc3); regmbc(0xc4);
- regmbc(0xc5);
- REGMBC(0x100) REGMBC(0x102) REGMBC(0x104)
- REGMBC(0x1cd) REGMBC(0x1de) REGMBC(0x1e0)
- REGMBC(0x1ea2)
- return;
- case 'B': CASEMBC(0x1e02) CASEMBC(0x1e06)
- regmbc('B'); REGMBC(0x1e02) REGMBC(0x1e06)
- return;
- case 'C': case 0xc7:
- CASEMBC(0x106) CASEMBC(0x108) CASEMBC(0x10a) CASEMBC(0x10c)
- regmbc('C'); regmbc(0xc7);
- REGMBC(0x106) REGMBC(0x108) REGMBC(0x10a)
- REGMBC(0x10c)
- return;
- case 'D': CASEMBC(0x10e) CASEMBC(0x110) CASEMBC(0x1e0a)
- CASEMBC(0x1e0e) CASEMBC(0x1e10)
- regmbc('D'); REGMBC(0x10e) REGMBC(0x110)
- REGMBC(0x1e0a) REGMBC(0x1e0e) REGMBC(0x1e10)
- return;
- case 'E': case 0xc8: case 0xc9: case 0xca: case 0xcb:
- CASEMBC(0x112) CASEMBC(0x114) CASEMBC(0x116) CASEMBC(0x118)
- CASEMBC(0x11a) CASEMBC(0x1eba) CASEMBC(0x1ebc)
- regmbc('E'); regmbc(0xc8); regmbc(0xc9);
- regmbc(0xca); regmbc(0xcb);
- REGMBC(0x112) REGMBC(0x114) REGMBC(0x116)
- REGMBC(0x118) REGMBC(0x11a) REGMBC(0x1eba)
- REGMBC(0x1ebc)
- return;
- case 'F': CASEMBC(0x1e1e)
- regmbc('F'); REGMBC(0x1e1e)
- return;
- case 'G': CASEMBC(0x11c) CASEMBC(0x11e) CASEMBC(0x120)
- CASEMBC(0x122) CASEMBC(0x1e4) CASEMBC(0x1e6) CASEMBC(0x1f4)
- CASEMBC(0x1e20)
- regmbc('G'); REGMBC(0x11c) REGMBC(0x11e)
- REGMBC(0x120) REGMBC(0x122) REGMBC(0x1e4)
- REGMBC(0x1e6) REGMBC(0x1f4) REGMBC(0x1e20)
- return;
- case 'H': CASEMBC(0x124) CASEMBC(0x126) CASEMBC(0x1e22)
- CASEMBC(0x1e26) CASEMBC(0x1e28)
- regmbc('H'); REGMBC(0x124) REGMBC(0x126)
- REGMBC(0x1e22) REGMBC(0x1e26) REGMBC(0x1e28)
- return;
- case 'I': case 0xcc: case 0xcd: case 0xce: case 0xcf:
- CASEMBC(0x128) CASEMBC(0x12a) CASEMBC(0x12c) CASEMBC(0x12e)
- CASEMBC(0x130) CASEMBC(0x1cf) CASEMBC(0x1ec8)
- regmbc('I'); regmbc(0xcc); regmbc(0xcd);
- regmbc(0xce); regmbc(0xcf);
- REGMBC(0x128) REGMBC(0x12a) REGMBC(0x12c)
- REGMBC(0x12e) REGMBC(0x130) REGMBC(0x1cf)
- REGMBC(0x1ec8)
- return;
- case 'J': CASEMBC(0x134)
- regmbc('J'); REGMBC(0x134)
- return;
- case 'K': CASEMBC(0x136) CASEMBC(0x1e8) CASEMBC(0x1e30)
- CASEMBC(0x1e34)
- regmbc('K'); REGMBC(0x136) REGMBC(0x1e8)
- REGMBC(0x1e30) REGMBC(0x1e34)
- return;
- case 'L': CASEMBC(0x139) CASEMBC(0x13b) CASEMBC(0x13d)
- CASEMBC(0x13f) CASEMBC(0x141) CASEMBC(0x1e3a)
- regmbc('L'); REGMBC(0x139) REGMBC(0x13b)
- REGMBC(0x13d) REGMBC(0x13f) REGMBC(0x141)
- REGMBC(0x1e3a)
- return;
- case 'M': CASEMBC(0x1e3e) CASEMBC(0x1e40)
- regmbc('M'); REGMBC(0x1e3e) REGMBC(0x1e40)
- return;
- case 'N': case 0xd1:
- CASEMBC(0x143) CASEMBC(0x145) CASEMBC(0x147) CASEMBC(0x1e44)
- CASEMBC(0x1e48)
- regmbc('N'); regmbc(0xd1);
- REGMBC(0x143) REGMBC(0x145) REGMBC(0x147)
- REGMBC(0x1e44) REGMBC(0x1e48)
- return;
- case 'O': case 0xd2: case 0xd3: case 0xd4: case 0xd5:
- case 0xd6: case 0xd8:
- CASEMBC(0x14c) CASEMBC(0x14e) CASEMBC(0x150) CASEMBC(0x1a0)
- CASEMBC(0x1d1) CASEMBC(0x1ea) CASEMBC(0x1ec) CASEMBC(0x1ece)
- regmbc('O'); regmbc(0xd2); regmbc(0xd3);
- regmbc(0xd4); regmbc(0xd5); regmbc(0xd6);
- regmbc(0xd8);
- REGMBC(0x14c) REGMBC(0x14e) REGMBC(0x150)
- REGMBC(0x1a0) REGMBC(0x1d1) REGMBC(0x1ea)
- REGMBC(0x1ec) REGMBC(0x1ece)
- return;
- case 'P': case 0x1e54: case 0x1e56:
- regmbc('P'); REGMBC(0x1e54) REGMBC(0x1e56)
- return;
- case 'R': CASEMBC(0x154) CASEMBC(0x156) CASEMBC(0x158)
- CASEMBC(0x1e58) CASEMBC(0x1e5e)
- regmbc('R'); REGMBC(0x154) REGMBC(0x156) REGMBC(0x158)
- REGMBC(0x1e58) REGMBC(0x1e5e)
- return;
- case 'S': CASEMBC(0x15a) CASEMBC(0x15c) CASEMBC(0x15e)
- CASEMBC(0x160) CASEMBC(0x1e60)
- regmbc('S'); REGMBC(0x15a) REGMBC(0x15c)
- REGMBC(0x15e) REGMBC(0x160) REGMBC(0x1e60)
- return;
- case 'T': CASEMBC(0x162) CASEMBC(0x164) CASEMBC(0x166)
- CASEMBC(0x1e6a) CASEMBC(0x1e6e)
- regmbc('T'); REGMBC(0x162) REGMBC(0x164)
- REGMBC(0x166) REGMBC(0x1e6a) REGMBC(0x1e6e)
- return;
- case 'U': case 0xd9: case 0xda: case 0xdb: case 0xdc:
- CASEMBC(0x168) CASEMBC(0x16a) CASEMBC(0x16c) CASEMBC(0x16e)
- CASEMBC(0x170) CASEMBC(0x172) CASEMBC(0x1af) CASEMBC(0x1d3)
- CASEMBC(0x1ee6)
- regmbc('U'); regmbc(0xd9); regmbc(0xda);
- regmbc(0xdb); regmbc(0xdc);
- REGMBC(0x168) REGMBC(0x16a) REGMBC(0x16c)
- REGMBC(0x16e) REGMBC(0x170) REGMBC(0x172)
- REGMBC(0x1af) REGMBC(0x1d3) REGMBC(0x1ee6)
- return;
- case 'V': CASEMBC(0x1e7c)
- regmbc('V'); REGMBC(0x1e7c)
- return;
- case 'W': CASEMBC(0x174) CASEMBC(0x1e80) CASEMBC(0x1e82)
- CASEMBC(0x1e84) CASEMBC(0x1e86)
- regmbc('W'); REGMBC(0x174) REGMBC(0x1e80)
- REGMBC(0x1e82) REGMBC(0x1e84) REGMBC(0x1e86)
- return;
- case 'X': CASEMBC(0x1e8a) CASEMBC(0x1e8c)
- regmbc('X'); REGMBC(0x1e8a) REGMBC(0x1e8c)
- return;
- case 'Y': case 0xdd:
- CASEMBC(0x176) CASEMBC(0x178) CASEMBC(0x1e8e) CASEMBC(0x1ef2)
- CASEMBC(0x1ef6) CASEMBC(0x1ef8)
- regmbc('Y'); regmbc(0xdd);
- REGMBC(0x176) REGMBC(0x178) REGMBC(0x1e8e)
- REGMBC(0x1ef2) REGMBC(0x1ef6) REGMBC(0x1ef8)
- return;
- case 'Z': CASEMBC(0x179) CASEMBC(0x17b) CASEMBC(0x17d)
- CASEMBC(0x1b5) CASEMBC(0x1e90) CASEMBC(0x1e94)
- regmbc('Z'); REGMBC(0x179) REGMBC(0x17b)
- REGMBC(0x17d) REGMBC(0x1b5) REGMBC(0x1e90)
- REGMBC(0x1e94)
- return;
- case 'a': case 0xe0: case 0xe1: case 0xe2:
- case 0xe3: case 0xe4: case 0xe5:
- CASEMBC(0x101) CASEMBC(0x103) CASEMBC(0x105) CASEMBC(0x1ce)
- CASEMBC(0x1df) CASEMBC(0x1e1) CASEMBC(0x1ea3)
- regmbc('a'); regmbc(0xe0); regmbc(0xe1);
- regmbc(0xe2); regmbc(0xe3); regmbc(0xe4);
- regmbc(0xe5);
- REGMBC(0x101) REGMBC(0x103) REGMBC(0x105)
- REGMBC(0x1ce) REGMBC(0x1df) REGMBC(0x1e1)
- REGMBC(0x1ea3)
- return;
- case 'b': CASEMBC(0x1e03) CASEMBC(0x1e07)
- regmbc('b'); REGMBC(0x1e03) REGMBC(0x1e07)
- return;
- case 'c': case 0xe7:
- CASEMBC(0x107) CASEMBC(0x109) CASEMBC(0x10b) CASEMBC(0x10d)
- regmbc('c'); regmbc(0xe7);
- REGMBC(0x107) REGMBC(0x109) REGMBC(0x10b)
- REGMBC(0x10d)
- return;
- case 'd': CASEMBC(0x10f) CASEMBC(0x111) CASEMBC(0x1e0b)
- CASEMBC(0x1e0f) CASEMBC(0x1e11)
- regmbc('d'); REGMBC(0x10f) REGMBC(0x111)
- REGMBC(0x1e0b) REGMBC(0x1e0f) REGMBC(0x1e11)
- return;
- case 'e': case 0xe8: case 0xe9: case 0xea: case 0xeb:
- CASEMBC(0x113) CASEMBC(0x115) CASEMBC(0x117) CASEMBC(0x119)
- CASEMBC(0x11b) CASEMBC(0x1ebb) CASEMBC(0x1ebd)
- regmbc('e'); regmbc(0xe8); regmbc(0xe9);
- regmbc(0xea); regmbc(0xeb);
- REGMBC(0x113) REGMBC(0x115) REGMBC(0x117)
- REGMBC(0x119) REGMBC(0x11b) REGMBC(0x1ebb)
- REGMBC(0x1ebd)
- return;
- case 'f': CASEMBC(0x1e1f)
- regmbc('f'); REGMBC(0x1e1f)
- return;
- case 'g': CASEMBC(0x11d) CASEMBC(0x11f) CASEMBC(0x121)
- CASEMBC(0x123) CASEMBC(0x1e5) CASEMBC(0x1e7) CASEMBC(0x1f5)
- CASEMBC(0x1e21)
- regmbc('g'); REGMBC(0x11d) REGMBC(0x11f)
- REGMBC(0x121) REGMBC(0x123) REGMBC(0x1e5)
- REGMBC(0x1e7) REGMBC(0x1f5) REGMBC(0x1e21)
- return;
- case 'h': CASEMBC(0x125) CASEMBC(0x127) CASEMBC(0x1e23)
- CASEMBC(0x1e27) CASEMBC(0x1e29) CASEMBC(0x1e96)
- regmbc('h'); REGMBC(0x125) REGMBC(0x127)
- REGMBC(0x1e23) REGMBC(0x1e27) REGMBC(0x1e29)
- REGMBC(0x1e96)
- return;
- case 'i': case 0xec: case 0xed: case 0xee: case 0xef:
- CASEMBC(0x129) CASEMBC(0x12b) CASEMBC(0x12d) CASEMBC(0x12f)
- CASEMBC(0x1d0) CASEMBC(0x1ec9)
- regmbc('i'); regmbc(0xec); regmbc(0xed);
- regmbc(0xee); regmbc(0xef);
- REGMBC(0x129) REGMBC(0x12b) REGMBC(0x12d)
- REGMBC(0x12f) REGMBC(0x1d0) REGMBC(0x1ec9)
- return;
- case 'j': CASEMBC(0x135) CASEMBC(0x1f0)
- regmbc('j'); REGMBC(0x135) REGMBC(0x1f0)
- return;
- case 'k': CASEMBC(0x137) CASEMBC(0x1e9) CASEMBC(0x1e31)
- CASEMBC(0x1e35)
- regmbc('k'); REGMBC(0x137) REGMBC(0x1e9)
- REGMBC(0x1e31) REGMBC(0x1e35)
- return;
- case 'l': CASEMBC(0x13a) CASEMBC(0x13c) CASEMBC(0x13e)
- CASEMBC(0x140) CASEMBC(0x142) CASEMBC(0x1e3b)
- regmbc('l'); REGMBC(0x13a) REGMBC(0x13c)
- REGMBC(0x13e) REGMBC(0x140) REGMBC(0x142)
- REGMBC(0x1e3b)
- return;
- case 'm': CASEMBC(0x1e3f) CASEMBC(0x1e41)
- regmbc('m'); REGMBC(0x1e3f) REGMBC(0x1e41)
- return;
- case 'n': case 0xf1:
- CASEMBC(0x144) CASEMBC(0x146) CASEMBC(0x148) CASEMBC(0x149)
- CASEMBC(0x1e45) CASEMBC(0x1e49)
- regmbc('n'); regmbc(0xf1);
- REGMBC(0x144) REGMBC(0x146) REGMBC(0x148)
- REGMBC(0x149) REGMBC(0x1e45) REGMBC(0x1e49)
- return;
- case 'o': case 0xf2: case 0xf3: case 0xf4: case 0xf5:
- case 0xf6: case 0xf8:
- CASEMBC(0x14d) CASEMBC(0x14f) CASEMBC(0x151) CASEMBC(0x1a1)
- CASEMBC(0x1d2) CASEMBC(0x1eb) CASEMBC(0x1ed) CASEMBC(0x1ecf)
- regmbc('o'); regmbc(0xf2); regmbc(0xf3);
- regmbc(0xf4); regmbc(0xf5); regmbc(0xf6);
- regmbc(0xf8);
- REGMBC(0x14d) REGMBC(0x14f) REGMBC(0x151)
- REGMBC(0x1a1) REGMBC(0x1d2) REGMBC(0x1eb)
- REGMBC(0x1ed) REGMBC(0x1ecf)
- return;
- case 'p': CASEMBC(0x1e55) CASEMBC(0x1e57)
- regmbc('p'); REGMBC(0x1e55) REGMBC(0x1e57)
- return;
- case 'r': CASEMBC(0x155) CASEMBC(0x157) CASEMBC(0x159)
- CASEMBC(0x1e59) CASEMBC(0x1e5f)
- regmbc('r'); REGMBC(0x155) REGMBC(0x157) REGMBC(0x159)
- REGMBC(0x1e59) REGMBC(0x1e5f)
- return;
- case 's': CASEMBC(0x15b) CASEMBC(0x15d) CASEMBC(0x15f)
- CASEMBC(0x161) CASEMBC(0x1e61)
- regmbc('s'); REGMBC(0x15b) REGMBC(0x15d)
- REGMBC(0x15f) REGMBC(0x161) REGMBC(0x1e61)
- return;
- case 't': CASEMBC(0x163) CASEMBC(0x165) CASEMBC(0x167)
- CASEMBC(0x1e6b) CASEMBC(0x1e6f) CASEMBC(0x1e97)
- regmbc('t'); REGMBC(0x163) REGMBC(0x165) REGMBC(0x167)
- REGMBC(0x1e6b) REGMBC(0x1e6f) REGMBC(0x1e97)
- return;
- case 'u': case 0xf9: case 0xfa: case 0xfb: case 0xfc:
- CASEMBC(0x169) CASEMBC(0x16b) CASEMBC(0x16d) CASEMBC(0x16f)
- CASEMBC(0x171) CASEMBC(0x173) CASEMBC(0x1b0) CASEMBC(0x1d4)
- CASEMBC(0x1ee7)
- regmbc('u'); regmbc(0xf9); regmbc(0xfa);
- regmbc(0xfb); regmbc(0xfc);
- REGMBC(0x169) REGMBC(0x16b) REGMBC(0x16d)
- REGMBC(0x16f) REGMBC(0x171) REGMBC(0x173)
- REGMBC(0x1b0) REGMBC(0x1d4) REGMBC(0x1ee7)
- return;
- case 'v': CASEMBC(0x1e7d)
- regmbc('v'); REGMBC(0x1e7d)
- return;
- case 'w': CASEMBC(0x175) CASEMBC(0x1e81) CASEMBC(0x1e83)
- CASEMBC(0x1e85) CASEMBC(0x1e87) CASEMBC(0x1e98)
- regmbc('w'); REGMBC(0x175) REGMBC(0x1e81)
- REGMBC(0x1e83) REGMBC(0x1e85) REGMBC(0x1e87)
- REGMBC(0x1e98)
- return;
- case 'x': CASEMBC(0x1e8b) CASEMBC(0x1e8d)
- regmbc('x'); REGMBC(0x1e8b) REGMBC(0x1e8d)
- return;
- case 'y': case 0xfd: case 0xff:
- CASEMBC(0x177) CASEMBC(0x1e8f) CASEMBC(0x1e99)
- CASEMBC(0x1ef3) CASEMBC(0x1ef7) CASEMBC(0x1ef9)
- regmbc('y'); regmbc(0xfd); regmbc(0xff);
- REGMBC(0x177) REGMBC(0x1e8f) REGMBC(0x1e99)
- REGMBC(0x1ef3) REGMBC(0x1ef7) REGMBC(0x1ef9)
- return;
- case 'z': CASEMBC(0x17a) CASEMBC(0x17c) CASEMBC(0x17e)
- CASEMBC(0x1b6) CASEMBC(0x1e91) CASEMBC(0x1e95)
- regmbc('z'); REGMBC(0x17a) REGMBC(0x17c)
- REGMBC(0x17e) REGMBC(0x1b6) REGMBC(0x1e91)
- REGMBC(0x1e95)
- return;
- }
- }
- regmbc(c);
-}
-
/*
* Check for a collating element "[.a.]". "pp" points to the '['.
* Returns a character. Zero means that no item was recognized. Otherwise
@@ -1110,12 +422,12 @@ static int get_coll_element(char_u **pp)
{
int c;
int l = 1;
- char_u *p = *pp;
+ char_u *p = *pp;
if (p[0] != NUL && p[1] == '.' && p[2] != NUL) {
- l = utfc_ptr2len(p + 2);
+ l = utfc_ptr2len((char *)p + 2);
if (p[l + 2] == '.' && p[l + 3] == ']') {
- c = utf_ptr2char(p + 2);
+ c = utf_ptr2char((char *)p + 2);
*pp += l + 4;
return c;
}
@@ -1123,7 +435,7 @@ static int get_coll_element(char_u **pp)
return 0;
}
-static int reg_cpo_lit; /* 'cpoptions' contains 'l' flag */
+static int reg_cpo_lit; // 'cpoptions' contains 'l' flag
static void get_cpo_flags(void)
{
@@ -1139,14 +451,16 @@ static char_u *skip_anyof(char_u *p)
{
int l;
- if (*p == '^') /* Complement of range. */
- ++p;
- if (*p == ']' || *p == '-')
- ++p;
+ if (*p == '^') { // Complement of range.
+ p++;
+ }
+ if (*p == ']' || *p == '-') {
+ p++;
+ }
while (*p != NUL && *p != ']') {
- if ((l = utfc_ptr2len(p)) > 1) {
+ if ((l = utfc_ptr2len((char *)p)) > 1) {
p += l;
- } else if (*p == '-') {
+ } else if (*p == '-') {
p++;
if (*p != ']' && *p != NUL) {
MB_PTR_ADV(p);
@@ -1183,12 +497,13 @@ static char_u *skip_anyof(char_u *p)
char_u *skip_regexp(char_u *startp, int dirc, int magic, char_u **newp)
{
int mymagic;
- char_u *p = startp;
+ char_u *p = startp;
- if (magic)
+ if (magic) {
mymagic = MAGIC_ON;
- else
+ } else {
mymagic = MAGIC_OFF;
+ }
get_cpo_flags();
for (; p[0] != NUL; MB_PTR_ADV(p)) {
@@ -1198,1554 +513,36 @@ char_u *skip_regexp(char_u *startp, int dirc, int magic, char_u **newp)
if ((p[0] == '[' && mymagic >= MAGIC_ON)
|| (p[0] == '\\' && p[1] == '[' && mymagic <= MAGIC_OFF)) {
p = skip_anyof(p + 1);
- if (p[0] == NUL)
+ if (p[0] == NUL) {
break;
- } else if (p[0] == '\\' && p[1] != NUL) {
+ }
+ } else if (p[0] == '\\' && p[1] != NUL) {
if (dirc == '?' && newp != NULL && p[1] == '?') {
- /* change "\?" to "?", make a copy first. */
+ // change "\?" to "?", make a copy first.
if (*newp == NULL) {
*newp = vim_strsave(startp);
p = *newp + (p - startp);
}
STRMOVE(p, p + 1);
- } else
- ++p; /* skip next character */
- if (*p == 'v')
+ } else {
+ p++; // skip next character
+ }
+ if (*p == 'v') {
mymagic = MAGIC_ALL;
- else if (*p == 'V')
+ } else if (*p == 'V') {
mymagic = MAGIC_NONE;
- }
- }
- return p;
-}
-
-/// Return true if the back reference is legal. We must have seen the close
-/// brace.
-/// TODO(vim): Should also check that we don't refer to something repeated
-/// (+*=): what instance of the repetition should we match?
-static int seen_endbrace(int refnum)
-{
- if (!had_endbrace[refnum]) {
- char_u *p;
-
- // Trick: check if "@<=" or "@<!" follows, in which case
- // the \1 can appear before the referenced match.
- for (p = regparse; *p != NUL; p++) {
- if (p[0] == '@' && p[1] == '<' && (p[2] == '!' || p[2] == '=')) {
- break;
- }
}
-
- if (*p == NUL) {
- emsg(_("E65: Illegal back reference"));
- rc_did_emsg = true;
- return false;
- }
- }
- return true;
-}
-
-/*
- * bt_regcomp() - compile a regular expression into internal code for the
- * traditional back track matcher.
- * Returns the program in allocated space. Returns NULL for an error.
- *
- * We can't allocate space until we know how big the compiled form will be,
- * but we can't compile it (and thus know how big it is) until we've got a
- * place to put the code. So we cheat: we compile it twice, once with code
- * generation turned off and size counting turned on, and once "for real".
- * This also means that we don't allocate space until we are sure that the
- * thing really will compile successfully, and we never have to move the
- * code and thus invalidate pointers into it. (Note that it has to be in
- * one piece because free() must be able to free it all.)
- *
- * Whether upper/lower case is to be ignored is decided when executing the
- * program, it does not matter here.
- *
- * Beware that the optimization-preparation code in here knows about some
- * of the structure of the compiled regexp.
- * "re_flags": RE_MAGIC and/or RE_STRING.
- */
-static regprog_T *bt_regcomp(char_u *expr, int re_flags)
-{
- char_u *scan;
- char_u *longest;
- int len;
- int flags;
-
- if (expr == NULL) {
- IEMSG_RET_NULL(_(e_null));
- }
-
- init_class_tab();
-
- /*
- * First pass: determine size, legality.
- */
- regcomp_start(expr, re_flags);
- regcode = JUST_CALC_SIZE;
- regc(REGMAGIC);
- if (reg(REG_NOPAREN, &flags) == NULL)
- return NULL;
-
- /* Allocate space. */
- bt_regprog_T *r = xmalloc(sizeof(bt_regprog_T) + regsize);
- r->re_in_use = false;
-
- /*
- * Second pass: emit code.
- */
- regcomp_start(expr, re_flags);
- regcode = r->program;
- regc(REGMAGIC);
- if (reg(REG_NOPAREN, &flags) == NULL || reg_toolong) {
- xfree(r);
- if (reg_toolong)
- EMSG_RET_NULL(_("E339: Pattern too long"));
- return NULL;
- }
-
- /* Dig out information for optimizations. */
- r->regstart = NUL; /* Worst-case defaults. */
- r->reganch = 0;
- r->regmust = NULL;
- r->regmlen = 0;
- r->regflags = regflags;
- if (flags & HASNL)
- r->regflags |= RF_HASNL;
- if (flags & HASLOOKBH)
- r->regflags |= RF_LOOKBH;
- /* Remember whether this pattern has any \z specials in it. */
- r->reghasz = re_has_z;
- scan = r->program + 1; /* First BRANCH. */
- if (OP(regnext(scan)) == END) { /* Only one top-level choice. */
- scan = OPERAND(scan);
-
- /* Starting-point info. */
- if (OP(scan) == BOL || OP(scan) == RE_BOF) {
- r->reganch++;
- scan = regnext(scan);
- }
-
- if (OP(scan) == EXACTLY) {
- r->regstart = utf_ptr2char(OPERAND(scan));
- } else if (OP(scan) == BOW
- || OP(scan) == EOW
- || OP(scan) == NOTHING
- || OP(scan) == MOPEN + 0 || OP(scan) == NOPEN
- || OP(scan) == MCLOSE + 0 || OP(scan) == NCLOSE) {
- char_u *regnext_scan = regnext(scan);
- if (OP(regnext_scan) == EXACTLY) {
- r->regstart = utf_ptr2char(OPERAND(regnext_scan));
- }
- }
-
- /*
- * If there's something expensive in the r.e., find the longest
- * literal string that must appear and make it the regmust. Resolve
- * ties in favor of later strings, since the regstart check works
- * with the beginning of the r.e. and avoiding duplication
- * strengthens checking. Not a strong reason, but sufficient in the
- * absence of others.
- */
- /*
- * When the r.e. starts with BOW, it is faster to look for a regmust
- * first. Used a lot for "#" and "*" commands. (Added by mool).
- */
- if ((flags & SPSTART || OP(scan) == BOW || OP(scan) == EOW)
- && !(flags & HASNL)) {
- longest = NULL;
- len = 0;
- for (; scan != NULL; scan = regnext(scan))
- if (OP(scan) == EXACTLY && STRLEN(OPERAND(scan)) >= (size_t)len) {
- longest = OPERAND(scan);
- len = (int)STRLEN(OPERAND(scan));
- }
- r->regmust = longest;
- r->regmlen = len;
}
}
-#ifdef BT_REGEXP_DUMP
- regdump(expr, r);
-#endif
- r->engine = &bt_regengine;
- return (regprog_T *)r;
-}
-
-/*
- * Free a compiled regexp program, returned by bt_regcomp().
- */
-static void bt_regfree(regprog_T *prog)
-{
- xfree(prog);
-}
-
-/*
- * Setup to parse the regexp. Used once to get the length and once to do it.
- */
-static void
-regcomp_start (
- char_u *expr,
- int re_flags /* see vim_regcomp() */
-)
-{
- initchr(expr);
- if (re_flags & RE_MAGIC)
- reg_magic = MAGIC_ON;
- else
- reg_magic = MAGIC_OFF;
- reg_string = (re_flags & RE_STRING);
- reg_strict = (re_flags & RE_STRICT);
- get_cpo_flags();
-
- num_complex_braces = 0;
- regnpar = 1;
- memset(had_endbrace, 0, sizeof(had_endbrace));
- regnzpar = 1;
- re_has_z = 0;
- regsize = 0L;
- reg_toolong = false;
- regflags = 0;
- had_eol = false;
-}
-
-/*
- * Check if during the previous call to vim_regcomp the EOL item "$" has been
- * found. This is messy, but it works fine.
- */
-int vim_regcomp_had_eol(void)
-{
- return had_eol;
+ return p;
}
// variables used for parsing
+static int prevchr_len; // byte length of previous char
static int at_start; // True when on the first character
static int prev_at_start; // True when on the second character
/*
- * Parse regular expression, i.e. main body or parenthesized thing.
- *
- * Caller must absorb opening parenthesis.
- *
- * Combining parenthesis handling with the base level of regular expression
- * is a trifle forced, but the need to tie the tails of the branches to what
- * follows makes it hard to avoid.
- */
-static char_u *
-reg (
- int paren, /* REG_NOPAREN, REG_PAREN, REG_NPAREN or REG_ZPAREN */
- int *flagp
-)
-{
- char_u *ret;
- char_u *br;
- char_u *ender;
- int parno = 0;
- int flags;
-
- *flagp = HASWIDTH; /* Tentatively. */
-
- if (paren == REG_ZPAREN) {
- /* Make a ZOPEN node. */
- if (regnzpar >= NSUBEXP)
- EMSG_RET_NULL(_("E50: Too many \\z("));
- parno = regnzpar;
- regnzpar++;
- ret = regnode(ZOPEN + parno);
- } else if (paren == REG_PAREN) {
- /* Make a MOPEN node. */
- if (regnpar >= NSUBEXP)
- EMSG2_RET_NULL(_("E51: Too many %s("), reg_magic == MAGIC_ALL);
- parno = regnpar;
- ++regnpar;
- ret = regnode(MOPEN + parno);
- } else if (paren == REG_NPAREN) {
- /* Make a NOPEN node. */
- ret = regnode(NOPEN);
- } else
- ret = NULL;
-
- /* Pick up the branches, linking them together. */
- br = regbranch(&flags);
- if (br == NULL)
- return NULL;
- if (ret != NULL)
- regtail(ret, br); /* [MZ]OPEN -> first. */
- else
- ret = br;
- /* If one of the branches can be zero-width, the whole thing can.
- * If one of the branches has * at start or matches a line-break, the
- * whole thing can. */
- if (!(flags & HASWIDTH))
- *flagp &= ~HASWIDTH;
- *flagp |= flags & (SPSTART | HASNL | HASLOOKBH);
- while (peekchr() == Magic('|')) {
- skipchr();
- br = regbranch(&flags);
- if (br == NULL || reg_toolong)
- return NULL;
- regtail(ret, br); /* BRANCH -> BRANCH. */
- if (!(flags & HASWIDTH))
- *flagp &= ~HASWIDTH;
- *flagp |= flags & (SPSTART | HASNL | HASLOOKBH);
- }
-
- /* Make a closing node, and hook it on the end. */
- ender = regnode(
- paren == REG_ZPAREN ? ZCLOSE + parno :
- paren == REG_PAREN ? MCLOSE + parno :
- paren == REG_NPAREN ? NCLOSE : END);
- regtail(ret, ender);
-
- /* Hook the tails of the branches to the closing node. */
- for (br = ret; br != NULL; br = regnext(br))
- regoptail(br, ender);
-
- /* Check for proper termination. */
- if (paren != REG_NOPAREN && getchr() != Magic(')')) {
- if (paren == REG_ZPAREN)
- EMSG_RET_NULL(_("E52: Unmatched \\z("));
- else if (paren == REG_NPAREN)
- EMSG2_RET_NULL(_(e_unmatchedpp), reg_magic == MAGIC_ALL);
- else
- EMSG2_RET_NULL(_(e_unmatchedp), reg_magic == MAGIC_ALL);
- } else if (paren == REG_NOPAREN && peekchr() != NUL) {
- if (curchr == Magic(')'))
- EMSG2_RET_NULL(_(e_unmatchedpar), reg_magic == MAGIC_ALL);
- else
- EMSG_RET_NULL(_(e_trailing)); /* "Can't happen". */
- /* NOTREACHED */
- }
- // Here we set the flag allowing back references to this set of
- // parentheses.
- if (paren == REG_PAREN) {
- had_endbrace[parno] = true; // have seen the close paren
- }
- return ret;
-}
-
-/*
- * Parse one alternative of an | operator.
- * Implements the & operator.
- */
-static char_u *regbranch(int *flagp)
-{
- char_u *ret;
- char_u *chain = NULL;
- char_u *latest;
- int flags;
-
- *flagp = WORST | HASNL; /* Tentatively. */
-
- ret = regnode(BRANCH);
- for (;; ) {
- latest = regconcat(&flags);
- if (latest == NULL)
- return NULL;
- /* If one of the branches has width, the whole thing has. If one of
- * the branches anchors at start-of-line, the whole thing does.
- * If one of the branches uses look-behind, the whole thing does. */
- *flagp |= flags & (HASWIDTH | SPSTART | HASLOOKBH);
- /* If one of the branches doesn't match a line-break, the whole thing
- * doesn't. */
- *flagp &= ~HASNL | (flags & HASNL);
- if (chain != NULL)
- regtail(chain, latest);
- if (peekchr() != Magic('&'))
- break;
- skipchr();
- regtail(latest, regnode(END)); /* operand ends */
- if (reg_toolong)
- break;
- reginsert(MATCH, latest);
- chain = latest;
- }
-
- return ret;
-}
-
-/*
- * Parse one alternative of an | or & operator.
- * Implements the concatenation operator.
- */
-static char_u *regconcat(int *flagp)
-{
- char_u *first = NULL;
- char_u *chain = NULL;
- char_u *latest;
- int flags;
- int cont = true;
-
- *flagp = WORST; /* Tentatively. */
-
- while (cont) {
- switch (peekchr()) {
- case NUL:
- case Magic('|'):
- case Magic('&'):
- case Magic(')'):
- cont = false;
- break;
- case Magic('Z'):
- regflags |= RF_ICOMBINE;
- skipchr_keepstart();
- break;
- case Magic('c'):
- regflags |= RF_ICASE;
- skipchr_keepstart();
- break;
- case Magic('C'):
- regflags |= RF_NOICASE;
- skipchr_keepstart();
- break;
- case Magic('v'):
- reg_magic = MAGIC_ALL;
- skipchr_keepstart();
- curchr = -1;
- break;
- case Magic('m'):
- reg_magic = MAGIC_ON;
- skipchr_keepstart();
- curchr = -1;
- break;
- case Magic('M'):
- reg_magic = MAGIC_OFF;
- skipchr_keepstart();
- curchr = -1;
- break;
- case Magic('V'):
- reg_magic = MAGIC_NONE;
- skipchr_keepstart();
- curchr = -1;
- break;
- default:
- latest = regpiece(&flags);
- if (latest == NULL || reg_toolong)
- return NULL;
- *flagp |= flags & (HASWIDTH | HASNL | HASLOOKBH);
- if (chain == NULL) /* First piece. */
- *flagp |= flags & SPSTART;
- else
- regtail(chain, latest);
- chain = latest;
- if (first == NULL)
- first = latest;
- break;
- }
- }
- if (first == NULL) /* Loop ran zero times. */
- first = regnode(NOTHING);
- return first;
-}
-
-/*
- * Parse something followed by possible [*+=].
- *
- * Note that the branching code sequences used for = and the general cases
- * of * and + are somewhat optimized: they use the same NOTHING node as
- * both the endmarker for their branch list and the body of the last branch.
- * It might seem that this node could be dispensed with entirely, but the
- * endmarker role is not redundant.
- */
-static char_u *regpiece(int *flagp)
-{
- char_u *ret;
- int op;
- char_u *next;
- int flags;
- long minval;
- long maxval;
-
- ret = regatom(&flags);
- if (ret == NULL)
- return NULL;
-
- op = peekchr();
- if (re_multi_type(op) == NOT_MULTI) {
- *flagp = flags;
- return ret;
- }
- /* default flags */
- *flagp = (WORST | SPSTART | (flags & (HASNL | HASLOOKBH)));
-
- skipchr();
- switch (op) {
- case Magic('*'):
- if (flags & SIMPLE)
- reginsert(STAR, ret);
- else {
- /* Emit x* as (x&|), where & means "self". */
- reginsert(BRANCH, ret); /* Either x */
- regoptail(ret, regnode(BACK)); /* and loop */
- regoptail(ret, ret); /* back */
- regtail(ret, regnode(BRANCH)); /* or */
- regtail(ret, regnode(NOTHING)); /* null. */
- }
- break;
-
- case Magic('+'):
- if (flags & SIMPLE)
- reginsert(PLUS, ret);
- else {
- /* Emit x+ as x(&|), where & means "self". */
- next = regnode(BRANCH); /* Either */
- regtail(ret, next);
- regtail(regnode(BACK), ret); /* loop back */
- regtail(next, regnode(BRANCH)); /* or */
- regtail(ret, regnode(NOTHING)); /* null. */
- }
- *flagp = (WORST | HASWIDTH | (flags & (HASNL | HASLOOKBH)));
- break;
-
- case Magic('@'):
- {
- int lop = END;
- int64_t nr = getdecchrs();
-
- switch (no_Magic(getchr())) {
- case '=': lop = MATCH; break; /* \@= */
- case '!': lop = NOMATCH; break; /* \@! */
- case '>': lop = SUBPAT; break; /* \@> */
- case '<': switch (no_Magic(getchr())) {
- case '=': lop = BEHIND; break; /* \@<= */
- case '!': lop = NOBEHIND; break; /* \@<! */
- }
- }
- if (lop == END)
- EMSG2_RET_NULL(_("E59: invalid character after %s@"),
- reg_magic == MAGIC_ALL);
- /* Look behind must match with behind_pos. */
- if (lop == BEHIND || lop == NOBEHIND) {
- regtail(ret, regnode(BHPOS));
- *flagp |= HASLOOKBH;
- }
- regtail(ret, regnode(END)); /* operand ends */
- if (lop == BEHIND || lop == NOBEHIND) {
- if (nr < 0)
- nr = 0; /* no limit is same as zero limit */
- reginsert_nr(lop, (uint32_t)nr, ret);
- } else
- reginsert(lop, ret);
- break;
- }
-
- case Magic('?'):
- case Magic('='):
- /* Emit x= as (x|) */
- reginsert(BRANCH, ret); /* Either x */
- regtail(ret, regnode(BRANCH)); /* or */
- next = regnode(NOTHING); /* null. */
- regtail(ret, next);
- regoptail(ret, next);
- break;
-
- case Magic('{'):
- if (!read_limits(&minval, &maxval))
- return NULL;
- if (flags & SIMPLE) {
- reginsert(BRACE_SIMPLE, ret);
- reginsert_limits(BRACE_LIMITS, minval, maxval, ret);
- } else {
- if (num_complex_braces >= 10)
- EMSG2_RET_NULL(_("E60: Too many complex %s{...}s"),
- reg_magic == MAGIC_ALL);
- reginsert(BRACE_COMPLEX + num_complex_braces, ret);
- regoptail(ret, regnode(BACK));
- regoptail(ret, ret);
- reginsert_limits(BRACE_LIMITS, minval, maxval, ret);
- ++num_complex_braces;
- }
- if (minval > 0 && maxval > 0)
- *flagp = (HASWIDTH | (flags & (HASNL | HASLOOKBH)));
- break;
- }
- if (re_multi_type(peekchr()) != NOT_MULTI) {
- // Can't have a multi follow a multi.
- if (peekchr() == Magic('*')) {
- snprintf((char *)IObuff, IOSIZE, _("E61: Nested %s*"),
- reg_magic >= MAGIC_ON ? "" : "\\");
- } else {
- snprintf((char *)IObuff, IOSIZE, _("E62: Nested %s%c"),
- reg_magic == MAGIC_ALL ? "" : "\\", no_Magic(peekchr()));
- }
- EMSG_RET_NULL((char *)IObuff);
- }
-
- return ret;
-}
-
-/* When making changes to classchars also change nfa_classcodes. */
-static char_u *classchars = (char_u *)".iIkKfFpPsSdDxXoOwWhHaAlLuU";
-static int classcodes[] = {
- ANY, IDENT, SIDENT, KWORD, SKWORD,
- FNAME, SFNAME, PRINT, SPRINT,
- WHITE, NWHITE, DIGIT, NDIGIT,
- HEX, NHEX, OCTAL, NOCTAL,
- WORD, NWORD, HEAD, NHEAD,
- ALPHA, NALPHA, LOWER, NLOWER,
- UPPER, NUPPER
-};
-
-/*
- * Parse the lowest level.
- *
- * Optimization: gobbles an entire sequence of ordinary characters so that
- * it can turn them into a single node, which is smaller to store and
- * faster to run. Don't do this when one_exactly is set.
- */
-static char_u *regatom(int *flagp)
-{
- char_u *ret;
- int flags;
- int c;
- char_u *p;
- int extra = 0;
- int save_prev_at_start = prev_at_start;
-
- *flagp = WORST; /* Tentatively. */
-
- c = getchr();
- switch (c) {
- case Magic('^'):
- ret = regnode(BOL);
- break;
-
- case Magic('$'):
- ret = regnode(EOL);
- had_eol = true;
- break;
-
- case Magic('<'):
- ret = regnode(BOW);
- break;
-
- case Magic('>'):
- ret = regnode(EOW);
- break;
-
- case Magic('_'):
- c = no_Magic(getchr());
- if (c == '^') { /* "\_^" is start-of-line */
- ret = regnode(BOL);
- break;
- }
- if (c == '$') { /* "\_$" is end-of-line */
- ret = regnode(EOL);
- had_eol = true;
- break;
- }
-
- extra = ADD_NL;
- *flagp |= HASNL;
-
- /* "\_[" is character range plus newline */
- if (c == '[')
- goto collection;
-
- // "\_x" is character class plus newline
- FALLTHROUGH;
-
- /*
- * Character classes.
- */
- case Magic('.'):
- case Magic('i'):
- case Magic('I'):
- case Magic('k'):
- case Magic('K'):
- case Magic('f'):
- case Magic('F'):
- case Magic('p'):
- case Magic('P'):
- case Magic('s'):
- case Magic('S'):
- case Magic('d'):
- case Magic('D'):
- case Magic('x'):
- case Magic('X'):
- case Magic('o'):
- case Magic('O'):
- case Magic('w'):
- case Magic('W'):
- case Magic('h'):
- case Magic('H'):
- case Magic('a'):
- case Magic('A'):
- case Magic('l'):
- case Magic('L'):
- case Magic('u'):
- case Magic('U'):
- p = vim_strchr(classchars, no_Magic(c));
- if (p == NULL)
- EMSG_RET_NULL(_("E63: invalid use of \\_"));
- /* When '.' is followed by a composing char ignore the dot, so that
- * the composing char is matched here. */
- if (c == Magic('.') && utf_iscomposing(peekchr())) {
- c = getchr();
- goto do_multibyte;
- }
- ret = regnode(classcodes[p - classchars] + extra);
- *flagp |= HASWIDTH | SIMPLE;
- break;
-
- case Magic('n'):
- if (reg_string) {
- /* In a string "\n" matches a newline character. */
- ret = regnode(EXACTLY);
- regc(NL);
- regc(NUL);
- *flagp |= HASWIDTH | SIMPLE;
- } else {
- /* In buffer text "\n" matches the end of a line. */
- ret = regnode(NEWL);
- *flagp |= HASWIDTH | HASNL;
- }
- break;
-
- case Magic('('):
- if (one_exactly)
- EMSG_ONE_RET_NULL;
- ret = reg(REG_PAREN, &flags);
- if (ret == NULL)
- return NULL;
- *flagp |= flags & (HASWIDTH | SPSTART | HASNL | HASLOOKBH);
- break;
-
- case NUL:
- case Magic('|'):
- case Magic('&'):
- case Magic(')'):
- if (one_exactly)
- EMSG_ONE_RET_NULL;
- IEMSG_RET_NULL(_(e_internal)); // Supposed to be caught earlier.
- // NOTREACHED
-
- case Magic('='):
- case Magic('?'):
- case Magic('+'):
- case Magic('@'):
- case Magic('{'):
- case Magic('*'):
- c = no_Magic(c);
- snprintf((char *)IObuff, IOSIZE, _("E64: %s%c follows nothing"),
- (c == '*' ? reg_magic >= MAGIC_ON : reg_magic == MAGIC_ALL)
- ? "" : "\\", c);
- EMSG_RET_NULL((char *)IObuff);
- // NOTREACHED
-
- case Magic('~'): /* previous substitute pattern */
- if (reg_prev_sub != NULL) {
- char_u *lp;
-
- ret = regnode(EXACTLY);
- lp = reg_prev_sub;
- while (*lp != NUL)
- regc(*lp++);
- regc(NUL);
- if (*reg_prev_sub != NUL) {
- *flagp |= HASWIDTH;
- if ((lp - reg_prev_sub) == 1)
- *flagp |= SIMPLE;
- }
- } else
- EMSG_RET_NULL(_(e_nopresub));
- break;
-
- case Magic('1'):
- case Magic('2'):
- case Magic('3'):
- case Magic('4'):
- case Magic('5'):
- case Magic('6'):
- case Magic('7'):
- case Magic('8'):
- case Magic('9'):
- {
- int refnum;
-
- refnum = c - Magic('0');
- if (!seen_endbrace(refnum)) {
- return NULL;
- }
- ret = regnode(BACKREF + refnum);
- }
- break;
-
- case Magic('z'):
- {
- c = no_Magic(getchr());
- switch (c) {
- case '(': if ((reg_do_extmatch & REX_SET) == 0)
- EMSG_RET_NULL(_(e_z_not_allowed));
- if (one_exactly)
- EMSG_ONE_RET_NULL;
- ret = reg(REG_ZPAREN, &flags);
- if (ret == NULL)
- return NULL;
- *flagp |= flags & (HASWIDTH|SPSTART|HASNL|HASLOOKBH);
- re_has_z = REX_SET;
- break;
-
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9': if ((reg_do_extmatch & REX_USE) == 0)
- EMSG_RET_NULL(_(e_z1_not_allowed));
- ret = regnode(ZREF + c - '0');
- re_has_z = REX_USE;
- break;
-
- case 's': ret = regnode(MOPEN + 0);
- if (!re_mult_next("\\zs")) {
- return NULL;
- }
- break;
-
- case 'e': ret = regnode(MCLOSE + 0);
- if (!re_mult_next("\\ze")) {
- return NULL;
- }
- break;
-
- default: EMSG_RET_NULL(_("E68: Invalid character after \\z"));
- }
- }
- break;
-
- case Magic('%'):
- {
- c = no_Magic(getchr());
- switch (c) {
- /* () without a back reference */
- case '(':
- if (one_exactly)
- EMSG_ONE_RET_NULL;
- ret = reg(REG_NPAREN, &flags);
- if (ret == NULL)
- return NULL;
- *flagp |= flags & (HASWIDTH | SPSTART | HASNL | HASLOOKBH);
- break;
-
- /* Catch \%^ and \%$ regardless of where they appear in the
- * pattern -- regardless of whether or not it makes sense. */
- case '^':
- ret = regnode(RE_BOF);
- break;
-
- case '$':
- ret = regnode(RE_EOF);
- break;
-
- case '#':
- ret = regnode(CURSOR);
- break;
-
- case 'V':
- ret = regnode(RE_VISUAL);
- break;
-
- case 'C':
- ret = regnode(RE_COMPOSING);
- break;
-
- /* \%[abc]: Emit as a list of branches, all ending at the last
- * branch which matches nothing. */
- case '[':
- if (one_exactly) /* doesn't nest */
- EMSG_ONE_RET_NULL;
- {
- char_u *lastbranch;
- char_u *lastnode = NULL;
- char_u *br;
-
- ret = NULL;
- while ((c = getchr()) != ']') {
- if (c == NUL)
- EMSG2_RET_NULL(_(e_missing_sb),
- reg_magic == MAGIC_ALL);
- br = regnode(BRANCH);
- if (ret == NULL) {
- ret = br;
- } else {
- regtail(lastnode, br);
- if (reg_toolong) {
- return NULL;
- }
- }
-
- ungetchr();
- one_exactly = true;
- lastnode = regatom(flagp);
- one_exactly = false;
- if (lastnode == NULL) {
- return NULL;
- }
- }
- if (ret == NULL)
- EMSG2_RET_NULL(_(e_empty_sb),
- reg_magic == MAGIC_ALL);
- lastbranch = regnode(BRANCH);
- br = regnode(NOTHING);
- if (ret != JUST_CALC_SIZE) {
- regtail(lastnode, br);
- regtail(lastbranch, br);
- /* connect all branches to the NOTHING
- * branch at the end */
- for (br = ret; br != lastnode; ) {
- if (OP(br) == BRANCH) {
- regtail(br, lastbranch);
- if (reg_toolong) {
- return NULL;
- }
- br = OPERAND(br);
- } else
- br = regnext(br);
- }
- }
- *flagp &= ~(HASWIDTH | SIMPLE);
- break;
- }
-
- case 'd': /* %d123 decimal */
- case 'o': /* %o123 octal */
- case 'x': /* %xab hex 2 */
- case 'u': /* %uabcd hex 4 */
- case 'U': /* %U1234abcd hex 8 */
- {
- int64_t i;
-
- switch (c) {
- case 'd': i = getdecchrs(); break;
- case 'o': i = getoctchrs(); break;
- case 'x': i = gethexchrs(2); break;
- case 'u': i = gethexchrs(4); break;
- case 'U': i = gethexchrs(8); break;
- default: i = -1; break;
- }
-
- if (i < 0 || i > INT_MAX) {
- EMSG2_RET_NULL(_("E678: Invalid character after %s%%[dxouU]"),
- reg_magic == MAGIC_ALL);
- }
- if (use_multibytecode(i)) {
- ret = regnode(MULTIBYTECODE);
- } else {
- ret = regnode(EXACTLY);
- }
- if (i == 0) {
- regc(0x0a);
- } else {
- regmbc(i);
- }
- regc(NUL);
- *flagp |= HASWIDTH;
- break;
- }
-
- default:
- if (ascii_isdigit(c) || c == '<' || c == '>'
- || c == '\'') {
- uint32_t n = 0;
- int cmp;
-
- cmp = c;
- if (cmp == '<' || cmp == '>')
- c = getchr();
- while (ascii_isdigit(c)) {
- n = n * 10 + (uint32_t)(c - '0');
- c = getchr();
- }
- if (c == '\'' && n == 0) {
- /* "\%'m", "\%<'m" and "\%>'m": Mark */
- c = getchr();
- ret = regnode(RE_MARK);
- if (ret == JUST_CALC_SIZE)
- regsize += 2;
- else {
- *regcode++ = c;
- *regcode++ = cmp;
- }
- break;
- } else if (c == 'l' || c == 'c' || c == 'v') {
- if (c == 'l') {
- ret = regnode(RE_LNUM);
- if (save_prev_at_start) {
- at_start = true;
- }
- } else if (c == 'c') {
- ret = regnode(RE_COL);
- } else {
- ret = regnode(RE_VCOL);
- }
- if (ret == JUST_CALC_SIZE) {
- regsize += 5;
- } else {
- // put the number and the optional
- // comparator after the opcode
- regcode = re_put_uint32(regcode, n);
- *regcode++ = cmp;
- }
- break;
- }
- }
-
- EMSG2_RET_NULL(_("E71: Invalid character after %s%%"),
- reg_magic == MAGIC_ALL);
- }
- }
- break;
-
- case Magic('['):
-collection:
- {
- char_u *lp;
-
- /*
- * If there is no matching ']', we assume the '[' is a normal
- * character. This makes 'incsearch' and ":help [" work.
- */
- lp = skip_anyof(regparse);
- if (*lp == ']') { /* there is a matching ']' */
- int startc = -1; /* > 0 when next '-' is a range */
- int endc;
-
- /*
- * In a character class, different parsing rules apply.
- * Not even \ is special anymore, nothing is.
- */
- if (*regparse == '^') { /* Complement of range. */
- ret = regnode(ANYBUT + extra);
- regparse++;
- } else
- ret = regnode(ANYOF + extra);
-
- /* At the start ']' and '-' mean the literal character. */
- if (*regparse == ']' || *regparse == '-') {
- startc = *regparse;
- regc(*regparse++);
- }
-
- while (*regparse != NUL && *regparse != ']') {
- if (*regparse == '-') {
- ++regparse;
- /* The '-' is not used for a range at the end and
- * after or before a '\n'. */
- if (*regparse == ']' || *regparse == NUL
- || startc == -1
- || (regparse[0] == '\\' && regparse[1] == 'n')) {
- regc('-');
- startc = '-'; /* [--x] is a range */
- } else {
- /* Also accept "a-[.z.]" */
- endc = 0;
- if (*regparse == '[')
- endc = get_coll_element(&regparse);
- if (endc == 0) {
- endc = mb_ptr2char_adv((const char_u **)&regparse);
- }
-
- /* Handle \o40, \x20 and \u20AC style sequences */
- if (endc == '\\' && !reg_cpo_lit)
- endc = coll_get_char();
-
- if (startc > endc) {
- EMSG_RET_NULL(_(e_reverse_range));
- }
- if (utf_char2len(startc) > 1
- || utf_char2len(endc) > 1) {
- // Limit to a range of 256 chars
- if (endc > startc + 256) {
- EMSG_RET_NULL(_(e_large_class));
- }
- while (++startc <= endc) {
- regmbc(startc);
- }
- } else {
- while (++startc <= endc)
- regc(startc);
- }
- startc = -1;
- }
- }
- /*
- * Only "\]", "\^", "\]" and "\\" are special in Vi. Vim
- * accepts "\t", "\e", etc., but only when the 'l' flag in
- * 'cpoptions' is not included.
- */
- else if (*regparse == '\\'
- && (vim_strchr(REGEXP_INRANGE, regparse[1]) != NULL
- || (!reg_cpo_lit
- && vim_strchr(REGEXP_ABBR,
- regparse[1]) != NULL))) {
- regparse++;
- if (*regparse == 'n') {
- /* '\n' in range: also match NL */
- if (ret != JUST_CALC_SIZE) {
- /* Using \n inside [^] does not change what
- * matches. "[^\n]" is the same as ".". */
- if (*ret == ANYOF) {
- *ret = ANYOF + ADD_NL;
- *flagp |= HASNL;
- }
- /* else: must have had a \n already */
- }
- regparse++;
- startc = -1;
- } else if (*regparse == 'd'
- || *regparse == 'o'
- || *regparse == 'x'
- || *regparse == 'u'
- || *regparse == 'U') {
- startc = coll_get_char();
- if (startc == 0)
- regc(0x0a);
- else
- regmbc(startc);
- } else {
- startc = backslash_trans(*regparse++);
- regc(startc);
- }
- } else if (*regparse == '[') {
- int c_class;
- int cu;
-
- c_class = get_char_class(&regparse);
- startc = -1;
- /* Characters assumed to be 8 bits! */
- switch (c_class) {
- case CLASS_NONE:
- c_class = get_equi_class(&regparse);
- if (c_class != 0) {
- /* produce equivalence class */
- reg_equi_class(c_class);
- } else if ((c_class =
- get_coll_element(&regparse)) != 0) {
- /* produce a collating element */
- regmbc(c_class);
- } else {
- /* literal '[', allow [[-x] as a range */
- startc = *regparse++;
- regc(startc);
- }
- break;
- case CLASS_ALNUM:
- for (cu = 1; cu < 128; cu++) {
- if (isalnum(cu)) {
- regmbc(cu);
- }
- }
- break;
- case CLASS_ALPHA:
- for (cu = 1; cu < 128; cu++) {
- if (isalpha(cu)) {
- regmbc(cu);
- }
- }
- break;
- case CLASS_BLANK:
- regc(' ');
- regc('\t');
- break;
- case CLASS_CNTRL:
- for (cu = 1; cu <= 127; cu++) {
- if (iscntrl(cu)) {
- regmbc(cu);
- }
- }
- break;
- case CLASS_DIGIT:
- for (cu = 1; cu <= 127; cu++) {
- if (ascii_isdigit(cu)) {
- regmbc(cu);
- }
- }
- break;
- case CLASS_GRAPH:
- for (cu = 1; cu <= 127; cu++) {
- if (isgraph(cu)) {
- regmbc(cu);
- }
- }
- break;
- case CLASS_LOWER:
- for (cu = 1; cu <= 255; cu++) {
- if (mb_islower(cu) && cu != 170 && cu != 186) {
- regmbc(cu);
- }
- }
- break;
- case CLASS_PRINT:
- for (cu = 1; cu <= 255; cu++) {
- if (vim_isprintc(cu)) {
- regmbc(cu);
- }
- }
- break;
- case CLASS_PUNCT:
- for (cu = 1; cu < 128; cu++) {
- if (ispunct(cu)) {
- regmbc(cu);
- }
- }
- break;
- case CLASS_SPACE:
- for (cu = 9; cu <= 13; cu++)
- regc(cu);
- regc(' ');
- break;
- case CLASS_UPPER:
- for (cu = 1; cu <= 255; cu++) {
- if (mb_isupper(cu)) {
- regmbc(cu);
- }
- }
- break;
- case CLASS_XDIGIT:
- for (cu = 1; cu <= 255; cu++) {
- if (ascii_isxdigit(cu)) {
- regmbc(cu);
- }
- }
- break;
- case CLASS_TAB:
- regc('\t');
- break;
- case CLASS_RETURN:
- regc('\r');
- break;
- case CLASS_BACKSPACE:
- regc('\b');
- break;
- case CLASS_ESCAPE:
- regc(ESC);
- break;
- case CLASS_IDENT:
- for (cu = 1; cu <= 255; cu++) {
- if (vim_isIDc(cu)) {
- regmbc(cu);
- }
- }
- break;
- case CLASS_KEYWORD:
- for (cu = 1; cu <= 255; cu++) {
- if (reg_iswordc(cu)) {
- regmbc(cu);
- }
- }
- break;
- case CLASS_FNAME:
- for (cu = 1; cu <= 255; cu++) {
- if (vim_isfilec(cu)) {
- regmbc(cu);
- }
- }
- break;
- }
- } else {
- // produce a multibyte character, including any
- // following composing characters.
- startc = utf_ptr2char(regparse);
- int len = utfc_ptr2len(regparse);
- if (utf_char2len(startc) != len) {
- // composing chars
- startc = -1;
- }
- while (--len >= 0) {
- regc(*regparse++);
- }
- }
- }
- regc(NUL);
- prevchr_len = 1; /* last char was the ']' */
- if (*regparse != ']')
- EMSG_RET_NULL(_(e_toomsbra)); /* Cannot happen? */
- skipchr(); /* let's be friends with the lexer again */
- *flagp |= HASWIDTH | SIMPLE;
- break;
- } else if (reg_strict)
- EMSG2_RET_NULL(_(e_missingbracket), reg_magic > MAGIC_OFF);
- }
- FALLTHROUGH;
-
- default:
- {
- int len;
-
- /* A multi-byte character is handled as a separate atom if it's
- * before a multi and when it's a composing char. */
- if (use_multibytecode(c)) {
-do_multibyte:
- ret = regnode(MULTIBYTECODE);
- regmbc(c);
- *flagp |= HASWIDTH | SIMPLE;
- break;
- }
-
- ret = regnode(EXACTLY);
-
- /*
- * Append characters as long as:
- * - there is no following multi, we then need the character in
- * front of it as a single character operand
- * - not running into a Magic character
- * - "one_exactly" is not set
- * But always emit at least one character. Might be a Multi,
- * e.g., a "[" without matching "]".
- */
- for (len = 0; c != NUL && (len == 0
- || (re_multi_type(peekchr()) == NOT_MULTI
- && !one_exactly
- && !is_Magic(c))); ++len) {
- c = no_Magic(c);
- {
- regmbc(c);
- {
- int l;
-
- /* Need to get composing character too. */
- for (;; ) {
- l = utf_ptr2len(regparse);
- if (!utf_composinglike(regparse, regparse + l)) {
- break;
- }
- regmbc(utf_ptr2char(regparse));
- skipchr();
- }
- }
- }
- c = getchr();
- }
- ungetchr();
-
- regc(NUL);
- *flagp |= HASWIDTH;
- if (len == 1)
- *flagp |= SIMPLE;
- }
- break;
- }
-
- return ret;
-}
-
-/// Used in a place where no * or \+ can follow.
-static bool re_mult_next(char *what)
-{
- if (re_multi_type(peekchr()) == MULTI_MULT) {
- EMSG2_RET_FAIL(_("E888: (NFA regexp) cannot repeat %s"), what);
- }
- return true;
-}
-
-// Return true if MULTIBYTECODE should be used instead of EXACTLY for
-// character "c".
-static bool use_multibytecode(int c)
-{
- return utf_char2len(c) > 1
- && (re_multi_type(peekchr()) != NOT_MULTI
- || utf_iscomposing(c));
-}
-
-/*
- * Emit a node.
- * Return pointer to generated code.
- */
-static char_u *regnode(int op)
-{
- char_u *ret;
-
- ret = regcode;
- if (ret == JUST_CALC_SIZE)
- regsize += 3;
- else {
- *regcode++ = op;
- *regcode++ = NUL; /* Null "next" pointer. */
- *regcode++ = NUL;
- }
- return ret;
-}
-
-/*
- * Emit (if appropriate) a byte of code
- */
-static void regc(int b)
-{
- if (regcode == JUST_CALC_SIZE)
- regsize++;
- else
- *regcode++ = b;
-}
-
-/*
- * Emit (if appropriate) a multi-byte character of code
- */
-static void regmbc(int c)
-{
- if (regcode == JUST_CALC_SIZE) {
- regsize += utf_char2len(c);
- } else {
- regcode += utf_char2bytes(c, regcode);
- }
-}
-
-/*
- * Insert an operator in front of already-emitted operand
- *
- * Means relocating the operand.
- */
-static void reginsert(int op, char_u *opnd)
-{
- char_u *src;
- char_u *dst;
- char_u *place;
-
- if (regcode == JUST_CALC_SIZE) {
- regsize += 3;
- return;
- }
- src = regcode;
- regcode += 3;
- dst = regcode;
- while (src > opnd)
- *--dst = *--src;
-
- place = opnd; /* Op node, where operand used to be. */
- *place++ = op;
- *place++ = NUL;
- *place = NUL;
-}
-
-/*
- * Insert an operator in front of already-emitted operand.
- * Add a number to the operator.
- */
-static void reginsert_nr(int op, long val, char_u *opnd)
-{
- char_u *src;
- char_u *dst;
- char_u *place;
-
- if (regcode == JUST_CALC_SIZE) {
- regsize += 7;
- return;
- }
- src = regcode;
- regcode += 7;
- dst = regcode;
- while (src > opnd)
- *--dst = *--src;
-
- place = opnd; /* Op node, where operand used to be. */
- *place++ = op;
- *place++ = NUL;
- *place++ = NUL;
- assert(val >= 0 && (uintmax_t)val <= UINT32_MAX);
- re_put_uint32(place, (uint32_t)val);
-}
-
-/*
- * Insert an operator in front of already-emitted operand.
- * The operator has the given limit values as operands. Also set next pointer.
- *
- * Means relocating the operand.
- */
-static void reginsert_limits(int op, long minval, long maxval, char_u *opnd)
-{
- char_u *src;
- char_u *dst;
- char_u *place;
-
- if (regcode == JUST_CALC_SIZE) {
- regsize += 11;
- return;
- }
- src = regcode;
- regcode += 11;
- dst = regcode;
- while (src > opnd)
- *--dst = *--src;
-
- place = opnd; /* Op node, where operand used to be. */
- *place++ = op;
- *place++ = NUL;
- *place++ = NUL;
- assert(minval >= 0 && (uintmax_t)minval <= UINT32_MAX);
- place = re_put_uint32(place, (uint32_t)minval);
- assert(maxval >= 0 && (uintmax_t)maxval <= UINT32_MAX);
- place = re_put_uint32(place, (uint32_t)maxval);
- regtail(opnd, place);
-}
-
-/*
- * Write a four bytes number at "p" and return pointer to the next char.
- */
-static char_u *re_put_uint32(char_u *p, uint32_t val)
-{
- *p++ = (char_u) ((val >> 24) & 0377);
- *p++ = (char_u) ((val >> 16) & 0377);
- *p++ = (char_u) ((val >> 8) & 0377);
- *p++ = (char_u) (val & 0377);
- return p;
-}
-
-// Set the next-pointer at the end of a node chain.
-static void regtail(char_u *p, char_u *val)
-{
- int offset;
-
- if (p == JUST_CALC_SIZE) {
- return;
- }
-
- // Find last node.
- char_u *scan = p;
- for (;; ) {
- char_u *temp = regnext(scan);
- if (temp == NULL) {
- break;
- }
- scan = temp;
- }
-
- if (OP(scan) == BACK) {
- offset = (int)(scan - val);
- } else {
- offset = (int)(val - scan);
- }
- // When the offset uses more than 16 bits it can no longer fit in the two
- // bytes available. Use a global flag to avoid having to check return
- // values in too many places.
- if (offset > 0xffff) {
- reg_toolong = true;
- } else {
- *(scan + 1) = (char_u)(((unsigned)offset >> 8) & 0377);
- *(scan + 2) = (char_u)(offset & 0377);
- }
-}
-
-/*
- * Like regtail, on item after a BRANCH; nop if none.
- */
-static void regoptail(char_u *p, char_u *val)
-{
- /* When op is neither BRANCH nor BRACE_COMPLEX0-9, it is "operandless" */
- if (p == NULL || p == JUST_CALC_SIZE
- || (OP(p) != BRANCH
- && (OP(p) < BRACE_COMPLEX || OP(p) > BRACE_COMPLEX + 9)))
- return;
- regtail(OPERAND(p), val);
-}
-
-/*
- * Functions for getting characters from the regexp input.
- */
-
-/*
* Start parsing at "str".
*/
static void initchr(char_u *str)
@@ -2790,7 +587,6 @@ static void restore_parse_state(parse_state_T *ps)
regnpar = ps->regnpar;
}
-
/*
* Get the next character without advancing.
*/
@@ -2806,9 +602,10 @@ static int peekchr(void)
case '.':
case '[':
case '~':
- /* magic when 'magic' is on */
- if (reg_magic >= MAGIC_ON)
+ // magic when 'magic' is on
+ if (reg_magic >= MAGIC_ON) {
curchr = Magic(curchr);
+ }
break;
case '(':
case ')':
@@ -2823,18 +620,19 @@ static int peekchr(void)
case '|':
case '<':
case '>':
- case '#': /* future ext. */
- case '"': /* future ext. */
- case '\'': /* future ext. */
- case ',': /* future ext. */
- case '-': /* future ext. */
- case ':': /* future ext. */
- case ';': /* future ext. */
- case '`': /* future ext. */
- case '/': /* Can't be used in / command */
- /* magic only after "\v" */
- if (reg_magic == MAGIC_ALL)
+ case '#': // future ext.
+ case '"': // future ext.
+ case '\'': // future ext.
+ case ',': // future ext.
+ case '-': // future ext.
+ case ':': // future ext.
+ case ';': // future ext.
+ case '`': // future ext.
+ case '/': // Can't be used in / command
+ // magic only after "\v"
+ if (reg_magic == MAGIC_ALL) {
curchr = Magic(curchr);
+ }
break;
case '*':
// * is not magic as the very first character, eg "?*ptr", when
@@ -2897,22 +695,17 @@ static int peekchr(void)
}
}
break;
- case '\\':
- {
+ case '\\': {
int c = regparse[1];
- if (c == NUL)
- curchr = '\\'; /* trailing '\' */
- else if (
- c <= '~' && META_flags[c]
- ) {
- /*
- * META contains everything that may be magic sometimes,
- * except ^ and $ ("\^" and "\$" are only magic after
- * "\V"). We now fetch the next character and toggle its
- * magicness. Therefore, \ is so meta-magic that it is
- * not in META.
- */
+ if (c == NUL) {
+ curchr = '\\'; // trailing '\'
+ } else if (c <= '~' && META_flags[c]) {
+ // META contains everything that may be magic sometimes,
+ // except ^ and $ ("\^" and "\$" are only magic after
+ // "\V"). We now fetch the next character and toggle its
+ // magicness. Therefore, \ is so meta-magic that it is
+ // not in META.
curchr = -1;
prev_at_start = at_start;
at_start = false; // be able to say "/\*ptr"
@@ -2927,20 +720,18 @@ static int peekchr(void)
* Handle abbreviations, like "\t" for TAB -- webb
*/
curchr = backslash_trans(c);
- } else if (reg_magic == MAGIC_NONE && (c == '$' || c == '^'))
+ } else if (reg_magic == MAGIC_NONE && (c == '$' || c == '^')) {
curchr = toggle_Magic(c);
- else {
- /*
- * Next character can never be (made) magic?
- * Then backslashing it won't do anything.
- */
- curchr = utf_ptr2char(regparse + 1);
+ } else {
+ // Next character can never be (made) magic?
+ // Then backslashing it won't do anything.
+ curchr = utf_ptr2char((char *)regparse + 1);
}
break;
}
default:
- curchr = utf_ptr2char(regparse);
+ curchr = utf_ptr2char((char *)regparse);
}
return curchr;
@@ -2951,21 +742,22 @@ static int peekchr(void)
*/
static void skipchr(void)
{
- /* peekchr() eats a backslash, do the same here */
- if (*regparse == '\\')
+ // peekchr() eats a backslash, do the same here
+ if (*regparse == '\\') {
prevchr_len = 1;
- else
+ } else {
prevchr_len = 0;
+ }
if (regparse[prevchr_len] != NUL) {
// Exclude composing chars that utfc_ptr2len does include.
- prevchr_len += utf_ptr2len(regparse + prevchr_len);
+ prevchr_len += utf_ptr2len((char *)regparse + prevchr_len);
}
regparse += prevchr_len;
prev_at_start = at_start;
at_start = false;
prevprevchr = prevchr;
prevchr = curchr;
- curchr = nextchr; /* use previously unget char, or -1 */
+ curchr = nextchr; // use previously unget char, or -1
nextchr = -1;
}
@@ -3018,7 +810,7 @@ static void ungetchr(void)
* Return -1 if there is no valid hex number.
* The position is updated:
* blahblah\%x20asdf
- * before-^ ^-after
+ * before-^ ^-after
* The parameter controls the maximum number of input characters. This will be
* 2 when reading a \%x20 sequence and 4 when reading a \%u20AC sequence.
*/
@@ -3030,15 +822,17 @@ static int64_t gethexchrs(int maxinputlen)
for (i = 0; i < maxinputlen; ++i) {
c = regparse[0];
- if (!ascii_isxdigit(c))
+ if (!ascii_isxdigit(c)) {
break;
+ }
nr <<= 4;
nr |= hex2nr(c);
++regparse;
}
- if (i == 0)
+ if (i == 0) {
return -1;
+ }
return nr;
}
@@ -3054,16 +848,18 @@ static int64_t getdecchrs(void)
for (i = 0;; ++i) {
c = regparse[0];
- if (c < '0' || c > '9')
+ if (c < '0' || c > '9') {
break;
+ }
nr *= 10;
nr += c - '0';
- ++regparse;
- curchr = -1; /* no longer valid */
+ regparse++;
+ curchr = -1; // no longer valid
}
- if (i == 0)
+ if (i == 0) {
return -1;
+ }
return nr;
}
@@ -3073,7 +869,7 @@ static int64_t getdecchrs(void)
* numbers > 377 correctly (for example, 400 is treated as 40) and doesn't
* treat 8 or 9 as recognised characters. Position is updated:
* blahblah\%o210asdf
- * before-^ ^-after
+ * before-^ ^-after
*/
static int64_t getoctchrs(void)
{
@@ -3083,38 +879,16 @@ static int64_t getoctchrs(void)
for (i = 0; i < 3 && nr < 040; i++) { // -V536
c = regparse[0];
- if (c < '0' || c > '7')
+ if (c < '0' || c > '7') {
break;
+ }
nr <<= 3;
nr |= hex2nr(c);
++regparse;
}
- if (i == 0)
+ if (i == 0) {
return -1;
- return nr;
-}
-
-/*
- * Get a number after a backslash that is inside [].
- * When nothing is recognized return a backslash.
- */
-static int coll_get_char(void)
-{
- int64_t nr = -1;
-
- switch (*regparse++) {
- case 'd': nr = getdecchrs(); break;
- case 'o': nr = getoctchrs(); break;
- case 'x': nr = gethexchrs(2); break;
- case 'u': nr = gethexchrs(4); break;
- case 'U': nr = gethexchrs(8); break;
- }
- if (nr < 0 || nr > INT_MAX) {
- // If getting the number fails be backwards compatible: the character
- // is a backslash.
- regparse--;
- nr = '\\';
}
return nr;
}
@@ -3128,7 +902,7 @@ static int coll_get_char(void)
static int read_limits(long *minval, long *maxval)
{
int reverse = false;
- char_u *first_char;
+ char_u *first_char;
long tmp;
if (*regparse == '-') {
@@ -3153,9 +927,7 @@ static int read_limits(long *minval, long *maxval)
regparse++; // Allow either \{...} or \{...\}
}
if (*regparse != '}') {
- snprintf((char *)IObuff, IOSIZE, _("E554: Syntax error in %s{...}"),
- reg_magic == MAGIC_ALL ? "" : "\\");
- EMSG_RET_FAIL((char *)IObuff);
+ EMSG2_RET_FAIL(_("E554: Syntax error in %s{...}"), reg_magic == MAGIC_ALL);
}
/*
@@ -3167,7 +939,7 @@ static int read_limits(long *minval, long *maxval)
*minval = *maxval;
*maxval = tmp;
}
- skipchr(); /* let's be friends with the lexer again */
+ skipchr(); // let's be friends with the lexer again
return OK;
}
@@ -3179,26 +951,10 @@ static int read_limits(long *minval, long *maxval)
* Global work variables for vim_regexec().
*/
-/* Save the sub-expressions before attempting a match. */
-#define save_se(savep, posp, pp) \
- REG_MULTI ? save_se_multi((savep), (posp)) : save_se_one((savep), (pp))
-
-/* After a failed match restore the sub-expressions. */
-#define restore_se(savep, posp, pp) { \
- if (REG_MULTI) \
- *(posp) = (savep)->se_u.pos; \
- else \
- *(pp) = (savep)->se_u.ptr; }
-
-
-#ifdef REGEXP_DEBUG
-int regnarrate = 0;
-#endif
-
// Sometimes need to save a copy of a line. Since alloc()/free() is very
// slow, we keep one allocated piece of memory and only re-allocate it when
// it's too small. It's freed in bt_regexec_both() when finished.
-static char_u *reg_tofree = NULL;
+static char_u *reg_tofree = NULL;
static unsigned reg_tofreelen;
// Structure used to store the execution state of the regex engine.
@@ -3232,13 +988,12 @@ 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
///< cleared
-
// Internal copy of 'ignorecase'. It is set at each call to vim_regexec().
// Normally it gets the value of "rm_ic" or "rmm_ic", but when the pattern
// contains '\c' or '\C' the value is overruled.
@@ -3270,41 +1025,6 @@ typedef struct {
static regexec_T rex;
static bool rex_in_use = false;
-/*
- * "regstack" and "backpos" are used by regmatch(). They are kept over calls
- * to avoid invoking malloc() and free() often.
- * "regstack" is a stack with regitem_T items, sometimes preceded by regstar_T
- * or regbehind_T.
- * "backpos_T" is a table with backpos_T for BACK
- */
-static garray_T regstack = GA_EMPTY_INIT_VALUE;
-static garray_T backpos = GA_EMPTY_INIT_VALUE;
-
-/*
- * Both for regstack and backpos tables we use the following strategy of
- * allocation (to reduce malloc/free calls):
- * - Initial size is fairly small.
- * - When needed, the tables are grown bigger (8 times at first, double after
- * that).
- * - After executing the match we free the memory only if the array has grown.
- * Thus the memory is kept allocated when it's at the initial size.
- * This makes it fast while not keeping a lot of memory allocated.
- * A three times speed increase was observed when using many simple patterns.
- */
-#define REGSTACK_INITIAL 2048
-#define BACKPOS_INITIAL 64
-
-#if defined(EXITFREE)
-void free_regexp_stuff(void)
-{
- ga_clear(&regstack);
- ga_clear(&backpos);
- xfree(reg_tofree);
- xfree(reg_prev_sub);
-}
-
-#endif
-
// Return true if character 'c' is included in 'iskeyword' option for
// "reg_buf" buffer.
static bool reg_iswordc(int c)
@@ -3329,312 +1049,15 @@ static char_u *reg_getline(linenr_T lnum)
return ml_get_buf(rex.reg_buf, rex.reg_firstlnum + lnum, false);
}
-static regsave_T behind_pos;
-
-static char_u *reg_startzp[NSUBEXP]; /* Workspace to mark beginning */
-static char_u *reg_endzp[NSUBEXP]; /* and end of \z(...\) matches */
-static lpos_T reg_startzpos[NSUBEXP]; /* idem, beginning pos */
-static lpos_T reg_endzpos[NSUBEXP]; /* idem, end pos */
+static char_u *reg_startzp[NSUBEXP]; // Workspace to mark beginning
+static char_u *reg_endzp[NSUBEXP]; // and end of \z(...\) matches
+static lpos_T reg_startzpos[NSUBEXP]; // idem, beginning pos
+static lpos_T reg_endzpos[NSUBEXP]; // idem, end pos
// true if using multi-line regexp.
#define REG_MULTI (rex.reg_match == NULL)
/*
- * Match a regexp against a string.
- * "rmp->regprog" is a compiled regexp as returned by vim_regcomp().
- * Uses curbuf for line count and 'iskeyword'.
- * If "line_lbr" is true, consider a "\n" in "line" to be a line break.
- *
- * Returns 0 for failure, number of lines contained in the match otherwise.
- */
-static int
-bt_regexec_nl (
- regmatch_T *rmp,
- char_u *line, /* string to match against */
- colnr_T col, /* column to start looking for match */
- bool line_lbr
-)
-{
- rex.reg_match = rmp;
- rex.reg_mmatch = NULL;
- rex.reg_maxline = 0;
- rex.reg_line_lbr = line_lbr;
- rex.reg_buf = curbuf;
- rex.reg_win = NULL;
- rex.reg_ic = rmp->rm_ic;
- rex.reg_icombine = false;
- rex.reg_maxcol = 0;
-
- long r = bt_regexec_both(line, col, NULL, NULL);
- assert(r <= INT_MAX);
- return (int)r;
-}
-
-/// Wrapper around strchr which accounts for case-insensitive searches and
-/// non-ASCII characters.
-///
-/// This function is used a lot for simple searches, keep it fast!
-///
-/// @param s string to search
-/// @param c character to find in @a s
-///
-/// @return NULL if no match, otherwise pointer to the position in @a s
-static inline char_u *cstrchr(const char_u *const s, const int c)
- FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
- FUNC_ATTR_ALWAYS_INLINE
-{
- if (!rex.reg_ic) {
- return vim_strchr(s, c);
- }
-
- // Use folded case for UTF-8, slow! For ASCII use libc strpbrk which is
- // expected to be highly optimized.
- if (c > 0x80) {
- const int folded_c = utf_fold(c);
- for (const char_u *p = s; *p != NUL; p += utfc_ptr2len(p)) {
- if (utf_fold(utf_ptr2char(p)) == folded_c) {
- return (char_u *)p;
- }
- }
- return NULL;
- }
-
- int cc;
- if (ASCII_ISUPPER(c)) {
- cc = TOLOWER_ASC(c);
- } else if (ASCII_ISLOWER(c)) {
- cc = TOUPPER_ASC(c);
- } else {
- return vim_strchr(s, c);
- }
-
- char tofind[] = { (char)c, (char)cc, NUL };
- return (char_u *)strpbrk((const char *)s, tofind);
-}
-
-/// Matches a regexp against multiple lines.
-/// "rmp->regprog" is a compiled regexp as returned by vim_regcomp().
-/// Uses curbuf for line count and 'iskeyword'.
-///
-/// @param win Window in which to search or NULL
-/// @param buf Buffer in which to search
-/// @param lnum Number of line to start looking for match
-/// @param col Column to start looking for match
-/// @param tm Timeout limit or NULL
-///
-/// @return zero if there is no match and number of lines contained in the match
-/// otherwise.
-static long bt_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf,
- linenr_T lnum, colnr_T col,
- proftime_T *tm, int *timed_out)
-{
- rex.reg_match = NULL;
- rex.reg_mmatch = rmp;
- rex.reg_buf = buf;
- rex.reg_win = win;
- rex.reg_firstlnum = lnum;
- rex.reg_maxline = rex.reg_buf->b_ml.ml_line_count - lnum;
- rex.reg_line_lbr = false;
- rex.reg_ic = rmp->rmm_ic;
- rex.reg_icombine = false;
- rex.reg_maxcol = rmp->rmm_maxcol;
-
- return bt_regexec_both(NULL, col, tm, timed_out);
-}
-
-/// Match a regexp against a string ("line" points to the string) or multiple
-/// lines (if "line" is NULL, use reg_getline()).
-/// @return 0 for failure, or number of lines contained in the match.
-static long bt_regexec_both(char_u *line,
- colnr_T col, // column to start search
- proftime_T *tm, // timeout limit or NULL
- int *timed_out) // flag set on timeout or NULL
-{
- bt_regprog_T *prog;
- char_u *s;
- long retval = 0L;
-
- /* Create "regstack" and "backpos" if they are not allocated yet.
- * We allocate *_INITIAL amount of bytes first and then set the grow size
- * to much bigger value to avoid many malloc calls in case of deep regular
- * expressions. */
- if (regstack.ga_data == NULL) {
- /* Use an item size of 1 byte, since we push different things
- * onto the regstack. */
- ga_init(&regstack, 1, REGSTACK_INITIAL);
- ga_grow(&regstack, REGSTACK_INITIAL);
- ga_set_growsize(&regstack, REGSTACK_INITIAL * 8);
- }
-
- if (backpos.ga_data == NULL) {
- ga_init(&backpos, sizeof(backpos_T), BACKPOS_INITIAL);
- ga_grow(&backpos, BACKPOS_INITIAL);
- ga_set_growsize(&backpos, BACKPOS_INITIAL * 8);
- }
-
- if (REG_MULTI) {
- prog = (bt_regprog_T *)rex.reg_mmatch->regprog;
- line = reg_getline((linenr_T)0);
- rex.reg_startpos = rex.reg_mmatch->startpos;
- rex.reg_endpos = rex.reg_mmatch->endpos;
- } else {
- prog = (bt_regprog_T *)rex.reg_match->regprog;
- rex.reg_startp = rex.reg_match->startp;
- rex.reg_endp = rex.reg_match->endp;
- }
-
- /* Be paranoid... */
- if (prog == NULL || line == NULL) {
- iemsg(_(e_null));
- goto theend;
- }
-
- /* Check validity of program. */
- if (prog_magic_wrong())
- goto theend;
-
- // If the start column is past the maximum column: no need to try.
- if (rex.reg_maxcol > 0 && col >= rex.reg_maxcol) {
- goto theend;
- }
-
- // If pattern contains "\c" or "\C": overrule value of rex.reg_ic
- if (prog->regflags & RF_ICASE) {
- rex.reg_ic = true;
- } else if (prog->regflags & RF_NOICASE) {
- rex.reg_ic = false;
- }
-
- // If pattern contains "\Z" overrule value of rex.reg_icombine
- if (prog->regflags & RF_ICOMBINE) {
- rex.reg_icombine = true;
- }
-
- /* If there is a "must appear" string, look for it. */
- if (prog->regmust != NULL) {
- int c = utf_ptr2char(prog->regmust);
- s = line + col;
-
- // This is used very often, esp. for ":global". Use two versions of
- // the loop to avoid overhead of conditions.
- if (!rex.reg_ic) {
- while ((s = vim_strchr(s, c)) != NULL) {
- if (cstrncmp(s, prog->regmust, &prog->regmlen) == 0) {
- break; // Found it.
- }
- MB_PTR_ADV(s);
- }
- } else {
- while ((s = cstrchr(s, c)) != NULL) {
- if (cstrncmp(s, prog->regmust, &prog->regmlen) == 0) {
- break; // Found it.
- }
- MB_PTR_ADV(s);
- }
- }
- if (s == NULL) { // Not present.
- goto theend;
- }
- }
-
- rex.line = line;
- rex.lnum = 0;
- reg_toolong = false;
-
- /* Simplest case: Anchored match need be tried only once. */
- if (prog->reganch) {
- int c = utf_ptr2char(rex.line + col);
- if (prog->regstart == NUL
- || prog->regstart == c
- || (rex.reg_ic
- && (utf_fold(prog->regstart) == utf_fold(c)
- || (c < 255 && prog->regstart < 255
- && mb_tolower(prog->regstart) == mb_tolower(c))))) {
- retval = regtry(prog, col, tm, timed_out);
- } else {
- retval = 0;
- }
- } else {
- int tm_count = 0;
- /* Messy cases: unanchored match. */
- while (!got_int) {
- if (prog->regstart != NUL) {
- // Skip until the char we know it must start with.
- s = cstrchr(rex.line + col, prog->regstart);
- if (s == NULL) {
- retval = 0;
- break;
- }
- col = (int)(s - rex.line);
- }
-
- // Check for maximum column to try.
- if (rex.reg_maxcol > 0 && col >= rex.reg_maxcol) {
- retval = 0;
- break;
- }
-
- retval = regtry(prog, col, tm, timed_out);
- if (retval > 0) {
- break;
- }
-
- // if not currently on the first line, get it again
- if (rex.lnum != 0) {
- rex.lnum = 0;
- rex.line = reg_getline((linenr_T)0);
- }
- if (rex.line[col] == NUL) {
- break;
- }
- col += utfc_ptr2len(rex.line + col);
- // Check for timeout once in a twenty times to avoid overhead.
- if (tm != NULL && ++tm_count == 20) {
- tm_count = 0;
- if (profile_passed_limit(*tm)) {
- if (timed_out != NULL) {
- *timed_out = true;
- }
- break;
- }
- }
- }
- }
-
-theend:
- /* Free "reg_tofree" when it's a bit big.
- * Free regstack and backpos if they are bigger than their initial size. */
- if (reg_tofreelen > 400) {
- XFREE_CLEAR(reg_tofree);
- }
- if (regstack.ga_maxlen > REGSTACK_INITIAL)
- ga_clear(&regstack);
- if (backpos.ga_maxlen > BACKPOS_INITIAL)
- ga_clear(&backpos);
-
- if (retval > 0) {
- // Make sure the end is never before the start. Can happen when \zs
- // and \ze are used.
- if (REG_MULTI) {
- const lpos_T *const start = &rex.reg_mmatch->startpos[0];
- const lpos_T *const end = &rex.reg_mmatch->endpos[0];
-
- if (end->lnum < start->lnum
- || (end->lnum == start->lnum && end->col < start->col)) {
- rex.reg_mmatch->endpos[0] = rex.reg_mmatch->startpos[0];
- }
- } else {
- if (rex.reg_match->endp[0] < rex.reg_match->startp[0]) {
- rex.reg_match->endp[0] = rex.reg_match->startp[0];
- }
- }
- }
-
- return retval;
-}
-
-
-/*
* Create a new extmatch and mark it as referenced once.
*/
static reg_extmatch_T *make_extmatch(void)
@@ -3650,8 +1073,9 @@ static reg_extmatch_T *make_extmatch(void)
*/
reg_extmatch_T *ref_extmatch(reg_extmatch_T *em)
{
- if (em != NULL)
+ if (em != NULL) {
em->refcnt++;
+ }
return em;
}
@@ -3664,93 +1088,23 @@ void unref_extmatch(reg_extmatch_T *em)
int i;
if (em != NULL && --em->refcnt <= 0) {
- for (i = 0; i < NSUBEXP; ++i)
- xfree(em->matches[i]);
- xfree(em);
- }
-}
-
-/// Try match of "prog" with at rex.line["col"].
-/// @returns 0 for failure, or number of lines contained in the match.
-static long regtry(bt_regprog_T *prog,
- colnr_T col,
- proftime_T *tm, // timeout limit or NULL
- int *timed_out) // flag set on timeout or NULL
-{
- rex.input = rex.line + col;
- rex.need_clear_subexpr = true;
- // Clear the external match subpointers if necessaey.
- rex.need_clear_zsubexpr = (prog->reghasz == REX_SET);
-
- if (regmatch(prog->program + 1, tm, timed_out) == 0) {
- return 0;
- }
-
- cleanup_subexpr();
- if (REG_MULTI) {
- if (rex.reg_startpos[0].lnum < 0) {
- rex.reg_startpos[0].lnum = 0;
- rex.reg_startpos[0].col = col;
- }
- if (rex.reg_endpos[0].lnum < 0) {
- rex.reg_endpos[0].lnum = rex.lnum;
- rex.reg_endpos[0].col = (int)(rex.input - rex.line);
- } else {
- // Use line number of "\ze".
- rex.lnum = rex.reg_endpos[0].lnum;
- }
- } else {
- if (rex.reg_startp[0] == NULL) {
- rex.reg_startp[0] = rex.line + col;
- }
- if (rex.reg_endp[0] == NULL) {
- rex.reg_endp[0] = rex.input;
- }
- }
- /* Package any found \z(...\) matches for export. Default is none. */
- unref_extmatch(re_extmatch_out);
- re_extmatch_out = NULL;
-
- if (prog->reghasz == REX_SET) {
- int i;
-
- cleanup_zsubexpr();
- re_extmatch_out = make_extmatch();
for (i = 0; i < NSUBEXP; i++) {
- if (REG_MULTI) {
- /* Only accept single line matches. */
- if (reg_startzpos[i].lnum >= 0
- && reg_endzpos[i].lnum == reg_startzpos[i].lnum
- && reg_endzpos[i].col >= reg_startzpos[i].col) {
- re_extmatch_out->matches[i] =
- vim_strnsave(reg_getline(reg_startzpos[i].lnum)
- + reg_startzpos[i].col,
- reg_endzpos[i].col
- - reg_startzpos[i].col);
- }
- } else {
- if (reg_startzp[i] != NULL && reg_endzp[i] != NULL)
- re_extmatch_out->matches[i] =
- vim_strnsave(reg_startzp[i], reg_endzp[i] - reg_startzp[i]);
- }
+ xfree(em->matches[i]);
}
+ xfree(em);
}
- return 1 + rex.lnum;
}
-
// Get class of previous character.
static int reg_prev_class(void)
{
if (rex.input > rex.line) {
- return mb_get_class_tab(
- rex.input - 1 - utf_head_off(rex.line, rex.input - 1),
- rex.reg_buf->b_chartab);
+ return mb_get_class_tab(rex.input - 1 - utf_head_off(rex.line, rex.input - 1),
+ rex.reg_buf->b_chartab);
}
return -1;
}
-
// Return true if the current rex.input position matches the Visual area.
static bool reg_match_visual(void)
{
@@ -3794,8 +1148,8 @@ static bool reg_match_visual(void)
return false;
}
+ col = (colnr_T)(rex.input - rex.line);
if (mode == 'v') {
- col = (colnr_T)(rex.input - rex.line);
if ((lnum == top.lnum && col < top.col)
|| (lnum == bot.lnum && col >= bot.col + (*p_sel != 'e'))) {
return false;
@@ -3803,15 +1157,21 @@ static bool reg_match_visual(void)
} else if (mode == Ctrl_V) {
getvvcol(wp, &top, &start, NULL, &end);
getvvcol(wp, &bot, &start2, NULL, &end2);
- if (start2 < start)
+ if (start2 < start) {
start = start2;
- if (end2 > end)
+ }
+ if (end2 > end) {
end = end2;
+ }
if (top.col == MAXCOL || bot.col == MAXCOL || curswant == MAXCOL) {
end = MAXCOL;
}
- unsigned int cols_u = win_linetabsize(wp, rex.line,
- (colnr_T)(rex.input - rex.line));
+
+ // getvvcol() flushes rex.line, need to get it again
+ rex.line = reg_getline(rex.lnum);
+ rex.input = rex.line + col;
+
+ unsigned int cols_u = win_linetabsize(wp, rex.line, col);
assert(cols_u <= MAXCOL);
colnr_T cols = (colnr_T)cols_u;
if (cols < start || cols > end - (*p_sel == 'e')) {
@@ -3821,1810 +1181,13 @@ static bool reg_match_visual(void)
return true;
}
-#define ADVANCE_REGINPUT() MB_PTR_ADV(rex.input)
-
-/*
- * The arguments from BRACE_LIMITS are stored here. They are actually local
- * to regmatch(), but they are here to reduce the amount of stack space used
- * (it can be called recursively many times).
- */
-static long bl_minval;
-static long bl_maxval;
-
-/// Main matching routine
-///
-/// Conceptually the strategy is simple: Check to see whether the current node
-/// matches, push an item onto the regstack and loop to see whether the rest
-/// matches, and then act accordingly. In practice we make some effort to
-/// avoid using the regstack, in particular by going through "ordinary" nodes
-/// (that don't need to know whether the rest of the match failed) by a nested
-/// loop.
-///
-/// Returns true when there is a match. Leaves rex.input and rex.lnum
-/// just after the last matched character.
-/// Returns false when there is no match. Leaves rex.input and rex.lnum in an
-/// undefined state!
-static bool regmatch(
- char_u *scan, // Current node.
- proftime_T *tm, // timeout limit or NULL
- int *timed_out // flag set on timeout or NULL
-)
-{
- char_u *next; /* Next node. */
- int op;
- int c;
- regitem_T *rp;
- int no;
- int status; // one of the RA_ values:
- int tm_count = 0;
-#define RA_FAIL 1 // something failed, abort
-#define RA_CONT 2 // continue in inner loop
-#define RA_BREAK 3 // break inner loop
-#define RA_MATCH 4 // successful match
-#define RA_NOMATCH 5 // didn't match
-
- // Make "regstack" and "backpos" empty. They are allocated and freed in
- // bt_regexec_both() to reduce malloc()/free() calls.
- regstack.ga_len = 0;
- backpos.ga_len = 0;
-
- /*
- * Repeat until "regstack" is empty.
- */
- for (;; ) {
- /* Some patterns may take a long time to match, e.g., "\([a-z]\+\)\+Q".
- * Allow interrupting them with CTRL-C. */
- fast_breakcheck();
-
-#ifdef REGEXP_DEBUG
- if (scan != NULL && regnarrate) {
- mch_errmsg((char *)regprop(scan));
- mch_errmsg("(\n");
- }
-#endif
-
- /*
- * Repeat for items that can be matched sequentially, without using the
- * regstack.
- */
- for (;; ) {
- if (got_int || scan == NULL) {
- status = RA_FAIL;
- break;
- }
- // Check for timeout once in a 100 times to avoid overhead.
- if (tm != NULL && ++tm_count == 100) {
- tm_count = 0;
- if (profile_passed_limit(*tm)) {
- if (timed_out != NULL) {
- *timed_out = true;
- }
- status = RA_FAIL;
- break;
- }
- }
- status = RA_CONT;
-
-#ifdef REGEXP_DEBUG
- if (regnarrate) {
- mch_errmsg((char *)regprop(scan));
- mch_errmsg("...\n");
- if (re_extmatch_in != NULL) {
- int i;
-
- mch_errmsg(_("External submatches:\n"));
- for (i = 0; i < NSUBEXP; i++) {
- mch_errmsg(" \"");
- if (re_extmatch_in->matches[i] != NULL)
- mch_errmsg((char *)re_extmatch_in->matches[i]);
- mch_errmsg("\"\n");
- }
- }
- }
-#endif
- next = regnext(scan);
-
- op = OP(scan);
- // Check for character class with NL added.
- if (!rex.reg_line_lbr && WITH_NL(op) && REG_MULTI
- && *rex.input == NUL && rex.lnum <= rex.reg_maxline) {
- reg_nextline();
- } else if (rex.reg_line_lbr && WITH_NL(op) && *rex.input == '\n') {
- ADVANCE_REGINPUT();
- } else {
- if (WITH_NL(op)) {
- op -= ADD_NL;
- }
- c = utf_ptr2char(rex.input);
- switch (op) {
- case BOL:
- if (rex.input != rex.line) {
- status = RA_NOMATCH;
- }
- break;
-
- case EOL:
- if (c != NUL) {
- status = RA_NOMATCH;
- }
- break;
-
- case RE_BOF:
- // We're not at the beginning of the file when below the first
- // line where we started, not at the start of the line or we
- // didn't start at the first line of the buffer.
- if (rex.lnum != 0 || rex.input != rex.line
- || (REG_MULTI && rex.reg_firstlnum > 1)) {
- status = RA_NOMATCH;
- }
- break;
-
- case RE_EOF:
- if (rex.lnum != rex.reg_maxline || c != NUL) {
- status = RA_NOMATCH;
- }
- break;
-
- case CURSOR:
- // Check if the buffer is in a window and compare the
- // rex.reg_win->w_cursor position to the match position.
- if (rex.reg_win == NULL
- || (rex.lnum + rex.reg_firstlnum != rex.reg_win->w_cursor.lnum)
- || ((colnr_T)(rex.input - rex.line) !=
- rex.reg_win->w_cursor.col)) {
- status = RA_NOMATCH;
- }
- break;
-
- case RE_MARK:
- /* Compare the mark position to the match position. */
- {
- int mark = OPERAND(scan)[0];
- int cmp = OPERAND(scan)[1];
- pos_T *pos;
-
- pos = getmark_buf(rex.reg_buf, mark, false);
- if (pos == NULL // mark doesn't exist
- || pos->lnum <= 0) { // mark isn't set in reg_buf
- status = RA_NOMATCH;
- } else {
- const colnr_T pos_col = pos->lnum == rex.lnum + rex.reg_firstlnum
- && pos->col == MAXCOL
- ? (colnr_T)STRLEN(reg_getline(pos->lnum - rex.reg_firstlnum))
- : pos->col;
-
- if (pos->lnum == rex.lnum + rex.reg_firstlnum
- ? (pos_col == (colnr_T)(rex.input - rex.line)
- ? (cmp == '<' || cmp == '>')
- : (pos_col < (colnr_T)(rex.input - rex.line)
- ? cmp != '>'
- : cmp != '<'))
- : (pos->lnum < rex.lnum + rex.reg_firstlnum
- ? cmp != '>'
- : cmp != '<')) {
- status = RA_NOMATCH;
- }
- }
- }
- break;
-
- case RE_VISUAL:
- if (!reg_match_visual())
- status = RA_NOMATCH;
- break;
-
- case RE_LNUM:
- assert(rex.lnum + rex.reg_firstlnum >= 0
- && (uintmax_t)(rex.lnum + rex.reg_firstlnum) <= UINT32_MAX);
- if (!REG_MULTI
- || !re_num_cmp((uint32_t)(rex.lnum + rex.reg_firstlnum), scan)) {
- status = RA_NOMATCH;
- }
- break;
-
- case RE_COL:
- assert(rex.input - rex.line + 1 >= 0
- && (uintmax_t)(rex.input - rex.line + 1) <= UINT32_MAX);
- if (!re_num_cmp((uint32_t)(rex.input - rex.line + 1), scan)) {
- status = RA_NOMATCH;
- }
- break;
-
- case RE_VCOL:
- if (!re_num_cmp(win_linetabsize(rex.reg_win == NULL
- ? curwin : rex.reg_win,
- rex.line,
- (colnr_T)(rex.input - rex.line)) + 1,
- scan)) {
- status = RA_NOMATCH;
- }
- break;
-
- case BOW: // \<word; rex.input points to w
- if (c == NUL) { // Can't match at end of line
- status = RA_NOMATCH;
- } else {
- // Get class of current and previous char (if it exists).
- const int this_class =
- mb_get_class_tab(rex.input, rex.reg_buf->b_chartab);
- if (this_class <= 1) {
- status = RA_NOMATCH; // Not on a word at all.
- } else if (reg_prev_class() == this_class) {
- status = RA_NOMATCH; // Previous char is in same word.
- }
- }
- break;
-
- case EOW: // word\>; rex.input points after d
- if (rex.input == rex.line) { // Can't match at start of line
- status = RA_NOMATCH;
- } else {
- int this_class, prev_class;
-
- // Get class of current and previous char (if it exists).
- this_class = mb_get_class_tab(rex.input, rex.reg_buf->b_chartab);
- prev_class = reg_prev_class();
- if (this_class == prev_class
- || prev_class == 0 || prev_class == 1) {
- status = RA_NOMATCH;
- }
- }
- break; // Matched with EOW
-
- case ANY:
- // ANY does not match new lines.
- if (c == NUL) {
- status = RA_NOMATCH;
- } else {
- ADVANCE_REGINPUT();
- }
- break;
-
- case IDENT:
- if (!vim_isIDc(c))
- status = RA_NOMATCH;
- else
- ADVANCE_REGINPUT();
- break;
-
- case SIDENT:
- if (ascii_isdigit(*rex.input) || !vim_isIDc(c)) {
- status = RA_NOMATCH;
- } else {
- ADVANCE_REGINPUT();
- }
- break;
-
- case KWORD:
- if (!vim_iswordp_buf(rex.input, rex.reg_buf)) {
- status = RA_NOMATCH;
- } else {
- ADVANCE_REGINPUT();
- }
- break;
-
- case SKWORD:
- if (ascii_isdigit(*rex.input)
- || !vim_iswordp_buf(rex.input, rex.reg_buf)) {
- status = RA_NOMATCH;
- } else {
- ADVANCE_REGINPUT();
- }
- break;
-
- case FNAME:
- if (!vim_isfilec(c)) {
- status = RA_NOMATCH;
- } else {
- ADVANCE_REGINPUT();
- }
- break;
-
- case SFNAME:
- if (ascii_isdigit(*rex.input) || !vim_isfilec(c)) {
- status = RA_NOMATCH;
- } else {
- ADVANCE_REGINPUT();
- }
- break;
-
- case PRINT:
- if (!vim_isprintc(utf_ptr2char(rex.input))) {
- status = RA_NOMATCH;
- } else {
- ADVANCE_REGINPUT();
- }
- break;
-
- case SPRINT:
- if (ascii_isdigit(*rex.input) || !vim_isprintc(utf_ptr2char(rex.input))) {
- status = RA_NOMATCH;
- } else {
- ADVANCE_REGINPUT();
- }
- break;
-
- case WHITE:
- if (!ascii_iswhite(c))
- status = RA_NOMATCH;
- else
- ADVANCE_REGINPUT();
- break;
-
- case NWHITE:
- if (c == NUL || ascii_iswhite(c))
- status = RA_NOMATCH;
- else
- ADVANCE_REGINPUT();
- break;
-
- case DIGIT:
- if (!ri_digit(c))
- status = RA_NOMATCH;
- else
- ADVANCE_REGINPUT();
- break;
-
- case NDIGIT:
- if (c == NUL || ri_digit(c))
- status = RA_NOMATCH;
- else
- ADVANCE_REGINPUT();
- break;
-
- case HEX:
- if (!ri_hex(c))
- status = RA_NOMATCH;
- else
- ADVANCE_REGINPUT();
- break;
-
- case NHEX:
- if (c == NUL || ri_hex(c))
- status = RA_NOMATCH;
- else
- ADVANCE_REGINPUT();
- break;
-
- case OCTAL:
- if (!ri_octal(c))
- status = RA_NOMATCH;
- else
- ADVANCE_REGINPUT();
- break;
-
- case NOCTAL:
- if (c == NUL || ri_octal(c))
- status = RA_NOMATCH;
- else
- ADVANCE_REGINPUT();
- break;
-
- case WORD:
- if (!ri_word(c))
- status = RA_NOMATCH;
- else
- ADVANCE_REGINPUT();
- break;
-
- case NWORD:
- if (c == NUL || ri_word(c))
- status = RA_NOMATCH;
- else
- ADVANCE_REGINPUT();
- break;
-
- case HEAD:
- if (!ri_head(c))
- status = RA_NOMATCH;
- else
- ADVANCE_REGINPUT();
- break;
-
- case NHEAD:
- if (c == NUL || ri_head(c))
- status = RA_NOMATCH;
- else
- ADVANCE_REGINPUT();
- break;
-
- case ALPHA:
- if (!ri_alpha(c))
- status = RA_NOMATCH;
- else
- ADVANCE_REGINPUT();
- break;
-
- case NALPHA:
- if (c == NUL || ri_alpha(c))
- status = RA_NOMATCH;
- else
- ADVANCE_REGINPUT();
- break;
-
- case LOWER:
- if (!ri_lower(c))
- status = RA_NOMATCH;
- else
- ADVANCE_REGINPUT();
- break;
-
- case NLOWER:
- if (c == NUL || ri_lower(c))
- status = RA_NOMATCH;
- else
- ADVANCE_REGINPUT();
- break;
-
- case UPPER:
- if (!ri_upper(c))
- status = RA_NOMATCH;
- else
- ADVANCE_REGINPUT();
- break;
-
- case NUPPER:
- if (c == NUL || ri_upper(c))
- status = RA_NOMATCH;
- else
- ADVANCE_REGINPUT();
- break;
-
- case EXACTLY:
- {
- int len;
- char_u *opnd;
-
- opnd = OPERAND(scan);
- // Inline the first byte, for speed.
- if (*opnd != *rex.input
- && (!rex.reg_ic)) {
- status = RA_NOMATCH;
- } else if (*opnd == NUL) {
- // match empty string always works; happens when "~" is
- // empty.
- } else {
- if (opnd[1] == NUL && !rex.reg_ic) {
- len = 1; // matched a single byte above
- } else {
- // Need to match first byte again for multi-byte.
- len = (int)STRLEN(opnd);
- if (cstrncmp(opnd, rex.input, &len) != 0) {
- status = RA_NOMATCH;
- }
- }
- // Check for following composing character, unless %C
- // follows (skips over all composing chars).
- if (status != RA_NOMATCH
- && utf_composinglike(rex.input, rex.input + len)
- && !rex.reg_icombine
- && OP(next) != RE_COMPOSING) {
- // raaron: This code makes a composing character get
- // ignored, which is the correct behavior (sometimes)
- // for voweled Hebrew texts.
- status = RA_NOMATCH;
- }
- if (status != RA_NOMATCH) {
- rex.input += len;
- }
- }
- }
- break;
-
- case ANYOF:
- case ANYBUT:
- if (c == NUL)
- status = RA_NOMATCH;
- else if ((cstrchr(OPERAND(scan), c) == NULL) == (op == ANYOF))
- status = RA_NOMATCH;
- else
- ADVANCE_REGINPUT();
- break;
-
- case MULTIBYTECODE:
- {
- int i, len;
-
- const char_u *opnd = OPERAND(scan);
- // Safety check (just in case 'encoding' was changed since
- // compiling the program).
- if ((len = utfc_ptr2len(opnd)) < 2) {
- status = RA_NOMATCH;
- break;
- }
- const int opndc = utf_ptr2char(opnd);
- if (utf_iscomposing(opndc)) {
- // When only a composing char is given match at any
- // position where that composing char appears.
- status = RA_NOMATCH;
- for (i = 0; rex.input[i] != NUL;
- i += utf_ptr2len(rex.input + i)) {
- const int inpc = utf_ptr2char(rex.input + i);
- if (!utf_iscomposing(inpc)) {
- if (i > 0) {
- break;
- }
- } else if (opndc == inpc) {
- // Include all following composing chars.
- len = i + utfc_ptr2len(rex.input + i);
- status = RA_MATCH;
- break;
- }
- }
- } else {
- for (i = 0; i < len; i++) {
- if (opnd[i] != rex.input[i]) {
- status = RA_NOMATCH;
- break;
- }
- }
- }
- rex.input += len;
- }
- break;
-
- case RE_COMPOSING:
- {
- // Skip composing characters.
- while (utf_iscomposing(utf_ptr2char(rex.input))) {
- MB_CPTR_ADV(rex.input);
- }
- }
- break;
-
- case NOTHING:
- break;
-
- case BACK:
- {
- int i;
-
- /*
- * When we run into BACK we need to check if we don't keep
- * looping without matching any input. The second and later
- * times a BACK is encountered it fails if the input is still
- * at the same position as the previous time.
- * The positions are stored in "backpos" and found by the
- * current value of "scan", the position in the RE program.
- */
- backpos_T *bp = (backpos_T *)backpos.ga_data;
- for (i = 0; i < backpos.ga_len; ++i)
- if (bp[i].bp_scan == scan)
- break;
- if (i == backpos.ga_len) {
- backpos_T *p = GA_APPEND_VIA_PTR(backpos_T, &backpos);
- p->bp_scan = scan;
- } else if (reg_save_equal(&bp[i].bp_pos))
- /* Still at same position as last time, fail. */
- status = RA_NOMATCH;
-
- assert(status != RA_FAIL);
- if (status != RA_NOMATCH) {
- reg_save(&bp[i].bp_pos, &backpos);
- }
- }
- break;
-
- case MOPEN + 0: /* Match start: \zs */
- case MOPEN + 1: /* \( */
- case MOPEN + 2:
- case MOPEN + 3:
- case MOPEN + 4:
- case MOPEN + 5:
- case MOPEN + 6:
- case MOPEN + 7:
- case MOPEN + 8:
- case MOPEN + 9:
- {
- no = op - MOPEN;
- cleanup_subexpr();
- rp = regstack_push(RS_MOPEN, scan);
- if (rp == NULL)
- status = RA_FAIL;
- else {
- rp->rs_no = no;
- save_se(&rp->rs_un.sesave, &rex.reg_startpos[no],
- &rex.reg_startp[no]);
- // We simply continue and handle the result when done.
- }
- }
- break;
-
- case NOPEN: /* \%( */
- case NCLOSE: /* \) after \%( */
- if (regstack_push(RS_NOPEN, scan) == NULL)
- status = RA_FAIL;
- /* We simply continue and handle the result when done. */
- break;
-
- case ZOPEN + 1:
- case ZOPEN + 2:
- case ZOPEN + 3:
- case ZOPEN + 4:
- case ZOPEN + 5:
- case ZOPEN + 6:
- case ZOPEN + 7:
- case ZOPEN + 8:
- case ZOPEN + 9:
- {
- no = op - ZOPEN;
- cleanup_zsubexpr();
- rp = regstack_push(RS_ZOPEN, scan);
- if (rp == NULL)
- status = RA_FAIL;
- else {
- rp->rs_no = no;
- save_se(&rp->rs_un.sesave, &reg_startzpos[no],
- &reg_startzp[no]);
- /* We simply continue and handle the result when done. */
- }
- }
- break;
-
- case MCLOSE + 0: /* Match end: \ze */
- case MCLOSE + 1: /* \) */
- case MCLOSE + 2:
- case MCLOSE + 3:
- case MCLOSE + 4:
- case MCLOSE + 5:
- case MCLOSE + 6:
- case MCLOSE + 7:
- case MCLOSE + 8:
- case MCLOSE + 9:
- {
- no = op - MCLOSE;
- cleanup_subexpr();
- rp = regstack_push(RS_MCLOSE, scan);
- if (rp == NULL) {
- status = RA_FAIL;
- } else {
- rp->rs_no = no;
- save_se(&rp->rs_un.sesave, &rex.reg_endpos[no], &rex.reg_endp[no]);
- // We simply continue and handle the result when done.
- }
- }
- break;
-
- case ZCLOSE + 1: /* \) after \z( */
- case ZCLOSE + 2:
- case ZCLOSE + 3:
- case ZCLOSE + 4:
- case ZCLOSE + 5:
- case ZCLOSE + 6:
- case ZCLOSE + 7:
- case ZCLOSE + 8:
- case ZCLOSE + 9:
- {
- no = op - ZCLOSE;
- cleanup_zsubexpr();
- rp = regstack_push(RS_ZCLOSE, scan);
- if (rp == NULL)
- status = RA_FAIL;
- else {
- rp->rs_no = no;
- save_se(&rp->rs_un.sesave, &reg_endzpos[no],
- &reg_endzp[no]);
- /* We simply continue and handle the result when done. */
- }
- }
- break;
-
- case BACKREF + 1:
- case BACKREF + 2:
- case BACKREF + 3:
- case BACKREF + 4:
- case BACKREF + 5:
- case BACKREF + 6:
- case BACKREF + 7:
- case BACKREF + 8:
- case BACKREF + 9:
- {
- int len;
-
- no = op - BACKREF;
- cleanup_subexpr();
- if (!REG_MULTI) { // Single-line regexp
- if (rex.reg_startp[no] == NULL || rex.reg_endp[no] == NULL) {
- // Backref was not set: Match an empty string.
- len = 0;
- } else {
- // Compare current input with back-ref in the same line.
- len = (int)(rex.reg_endp[no] - rex.reg_startp[no]);
- if (cstrncmp(rex.reg_startp[no], rex.input, &len) != 0) {
- status = RA_NOMATCH;
- }
- }
- } else { // Multi-line regexp
- if (rex.reg_startpos[no].lnum < 0 || rex.reg_endpos[no].lnum < 0) {
- // Backref was not set: Match an empty string.
- len = 0;
- } else {
- if (rex.reg_startpos[no].lnum == rex.lnum
- && rex.reg_endpos[no].lnum == rex.lnum) {
- // Compare back-ref within the current line.
- len = rex.reg_endpos[no].col - rex.reg_startpos[no].col;
- if (cstrncmp(rex.line + rex.reg_startpos[no].col,
- rex.input, &len) != 0) {
- status = RA_NOMATCH;
- }
- } else {
- // Messy situation: Need to compare between two lines.
- int r = match_with_backref(rex.reg_startpos[no].lnum,
- rex.reg_startpos[no].col,
- rex.reg_endpos[no].lnum,
- rex.reg_endpos[no].col,
- &len);
- if (r != RA_MATCH) {
- status = r;
- }
- }
- }
- }
-
- // Matched the backref, skip over it.
- rex.input += len;
- }
- break;
-
- case ZREF + 1:
- case ZREF + 2:
- case ZREF + 3:
- case ZREF + 4:
- case ZREF + 5:
- case ZREF + 6:
- case ZREF + 7:
- case ZREF + 8:
- case ZREF + 9:
- {
- cleanup_zsubexpr();
- no = op - ZREF;
- if (re_extmatch_in != NULL
- && re_extmatch_in->matches[no] != NULL) {
- int len = (int)STRLEN(re_extmatch_in->matches[no]);
- if (cstrncmp(re_extmatch_in->matches[no], rex.input, &len) != 0) {
- status = RA_NOMATCH;
- } else {
- rex.input += len;
- }
- } else {
- // Backref was not set: Match an empty string.
- }
- }
- break;
-
- case BRANCH:
- {
- if (OP(next) != BRANCH) /* No choice. */
- next = OPERAND(scan); /* Avoid recursion. */
- else {
- rp = regstack_push(RS_BRANCH, scan);
- if (rp == NULL)
- status = RA_FAIL;
- else
- status = RA_BREAK; /* rest is below */
- }
- }
- break;
-
- case BRACE_LIMITS:
- {
- if (OP(next) == BRACE_SIMPLE) {
- bl_minval = OPERAND_MIN(scan);
- bl_maxval = OPERAND_MAX(scan);
- } else if (OP(next) >= BRACE_COMPLEX
- && OP(next) < BRACE_COMPLEX + 10) {
- no = OP(next) - BRACE_COMPLEX;
- brace_min[no] = OPERAND_MIN(scan);
- brace_max[no] = OPERAND_MAX(scan);
- brace_count[no] = 0;
- } else {
- internal_error("BRACE_LIMITS");
- status = RA_FAIL;
- }
- }
- break;
-
- case BRACE_COMPLEX + 0:
- case BRACE_COMPLEX + 1:
- case BRACE_COMPLEX + 2:
- case BRACE_COMPLEX + 3:
- case BRACE_COMPLEX + 4:
- case BRACE_COMPLEX + 5:
- case BRACE_COMPLEX + 6:
- case BRACE_COMPLEX + 7:
- case BRACE_COMPLEX + 8:
- case BRACE_COMPLEX + 9:
- {
- no = op - BRACE_COMPLEX;
- ++brace_count[no];
-
- /* If not matched enough times yet, try one more */
- if (brace_count[no] <= (brace_min[no] <= brace_max[no]
- ? brace_min[no] : brace_max[no])) {
- rp = regstack_push(RS_BRCPLX_MORE, scan);
- if (rp == NULL)
- status = RA_FAIL;
- else {
- rp->rs_no = no;
- reg_save(&rp->rs_un.regsave, &backpos);
- next = OPERAND(scan);
- /* We continue and handle the result when done. */
- }
- break;
- }
-
- /* If matched enough times, may try matching some more */
- if (brace_min[no] <= brace_max[no]) {
- /* Range is the normal way around, use longest match */
- if (brace_count[no] <= brace_max[no]) {
- rp = regstack_push(RS_BRCPLX_LONG, scan);
- if (rp == NULL)
- status = RA_FAIL;
- else {
- rp->rs_no = no;
- reg_save(&rp->rs_un.regsave, &backpos);
- next = OPERAND(scan);
- /* We continue and handle the result when done. */
- }
- }
- } else {
- /* Range is backwards, use shortest match first */
- if (brace_count[no] <= brace_min[no]) {
- rp = regstack_push(RS_BRCPLX_SHORT, scan);
- if (rp == NULL)
- status = RA_FAIL;
- else {
- reg_save(&rp->rs_un.regsave, &backpos);
- /* We continue and handle the result when done. */
- }
- }
- }
- }
- break;
-
- case BRACE_SIMPLE:
- case STAR:
- case PLUS:
- {
- regstar_T rst;
-
- /*
- * Lookahead to avoid useless match attempts when we know
- * what character comes next.
- */
- if (OP(next) == EXACTLY) {
- rst.nextb = *OPERAND(next);
- if (rex.reg_ic) {
- if (mb_isupper(rst.nextb)) {
- rst.nextb_ic = mb_tolower(rst.nextb);
- } else {
- rst.nextb_ic = mb_toupper(rst.nextb);
- }
- } else {
- rst.nextb_ic = rst.nextb;
- }
- } else {
- rst.nextb = NUL;
- rst.nextb_ic = NUL;
- }
- if (op != BRACE_SIMPLE) {
- rst.minval = (op == STAR) ? 0 : 1;
- rst.maxval = MAX_LIMIT;
- } else {
- rst.minval = bl_minval;
- rst.maxval = bl_maxval;
- }
-
- /*
- * When maxval > minval, try matching as much as possible, up
- * to maxval. When maxval < minval, try matching at least the
- * minimal number (since the range is backwards, that's also
- * maxval!).
- */
- rst.count = regrepeat(OPERAND(scan), rst.maxval);
- if (got_int) {
- status = RA_FAIL;
- break;
- }
- if (rst.minval <= rst.maxval
- ? rst.count >= rst.minval : rst.count >= rst.maxval) {
- /* It could match. Prepare for trying to match what
- * follows. The code is below. Parameters are stored in
- * a regstar_T on the regstack. */
- if ((long)((unsigned)regstack.ga_len >> 10) >= p_mmp) {
- emsg(_(e_maxmempat));
- status = RA_FAIL;
- } else {
- ga_grow(&regstack, sizeof(regstar_T));
- regstack.ga_len += sizeof(regstar_T);
- rp = regstack_push(rst.minval <= rst.maxval
- ? RS_STAR_LONG : RS_STAR_SHORT, scan);
- if (rp == NULL)
- status = RA_FAIL;
- else {
- *(((regstar_T *)rp) - 1) = rst;
- status = RA_BREAK; /* skip the restore bits */
- }
- }
- } else
- status = RA_NOMATCH;
-
- }
- break;
-
- case NOMATCH:
- case MATCH:
- case SUBPAT:
- rp = regstack_push(RS_NOMATCH, scan);
- if (rp == NULL)
- status = RA_FAIL;
- else {
- rp->rs_no = op;
- reg_save(&rp->rs_un.regsave, &backpos);
- next = OPERAND(scan);
- /* We continue and handle the result when done. */
- }
- break;
-
- case BEHIND:
- case NOBEHIND:
- /* Need a bit of room to store extra positions. */
- if ((long)((unsigned)regstack.ga_len >> 10) >= p_mmp) {
- emsg(_(e_maxmempat));
- status = RA_FAIL;
- } else {
- ga_grow(&regstack, sizeof(regbehind_T));
- regstack.ga_len += sizeof(regbehind_T);
- rp = regstack_push(RS_BEHIND1, scan);
- if (rp == NULL)
- status = RA_FAIL;
- else {
- /* Need to save the subexpr to be able to restore them
- * when there is a match but we don't use it. */
- save_subexpr(((regbehind_T *)rp) - 1);
-
- rp->rs_no = op;
- reg_save(&rp->rs_un.regsave, &backpos);
- /* First try if what follows matches. If it does then we
- * check the behind match by looping. */
- }
- }
- break;
-
- case BHPOS:
- if (REG_MULTI) {
- if (behind_pos.rs_u.pos.col != (colnr_T)(rex.input - rex.line)
- || behind_pos.rs_u.pos.lnum != rex.lnum) {
- status = RA_NOMATCH;
- }
- } else if (behind_pos.rs_u.ptr != rex.input) {
- status = RA_NOMATCH;
- }
- break;
-
- case NEWL:
- if ((c != NUL || !REG_MULTI || rex.lnum > rex.reg_maxline
- || rex.reg_line_lbr) && (c != '\n' || !rex.reg_line_lbr)) {
- status = RA_NOMATCH;
- } else if (rex.reg_line_lbr) {
- ADVANCE_REGINPUT();
- } else {
- reg_nextline();
- }
- break;
-
- case END:
- status = RA_MATCH; /* Success! */
- break;
-
- default:
- iemsg(_(e_re_corr));
-#ifdef REGEXP_DEBUG
- printf("Illegal op code %d\n", op);
-#endif
- status = RA_FAIL;
- break;
- }
- }
-
- /* If we can't continue sequentially, break the inner loop. */
- if (status != RA_CONT)
- break;
-
- /* Continue in inner loop, advance to next item. */
- scan = next;
-
- } /* end of inner loop */
-
- /*
- * If there is something on the regstack execute the code for the state.
- * If the state is popped then loop and use the older state.
- */
- while (!GA_EMPTY(&regstack) && status != RA_FAIL) {
- rp = (regitem_T *)((char *)regstack.ga_data + regstack.ga_len) - 1;
- switch (rp->rs_state) {
- case RS_NOPEN:
- /* Result is passed on as-is, simply pop the state. */
- regstack_pop(&scan);
- break;
-
- case RS_MOPEN:
- // Pop the state. Restore pointers when there is no match.
- if (status == RA_NOMATCH) {
- restore_se(&rp->rs_un.sesave, &rex.reg_startpos[rp->rs_no],
- &rex.reg_startp[rp->rs_no]);
- }
- regstack_pop(&scan);
- break;
-
- case RS_ZOPEN:
- /* Pop the state. Restore pointers when there is no match. */
- if (status == RA_NOMATCH)
- restore_se(&rp->rs_un.sesave, &reg_startzpos[rp->rs_no],
- &reg_startzp[rp->rs_no]);
- regstack_pop(&scan);
- break;
-
- case RS_MCLOSE:
- // Pop the state. Restore pointers when there is no match.
- if (status == RA_NOMATCH) {
- restore_se(&rp->rs_un.sesave, &rex.reg_endpos[rp->rs_no],
- &rex.reg_endp[rp->rs_no]);
- }
- regstack_pop(&scan);
- break;
-
- case RS_ZCLOSE:
- /* Pop the state. Restore pointers when there is no match. */
- if (status == RA_NOMATCH)
- restore_se(&rp->rs_un.sesave, &reg_endzpos[rp->rs_no],
- &reg_endzp[rp->rs_no]);
- regstack_pop(&scan);
- break;
-
- case RS_BRANCH:
- if (status == RA_MATCH)
- /* this branch matched, use it */
- regstack_pop(&scan);
- else {
- if (status != RA_BREAK) {
- /* After a non-matching branch: try next one. */
- reg_restore(&rp->rs_un.regsave, &backpos);
- scan = rp->rs_scan;
- }
- if (scan == NULL || OP(scan) != BRANCH) {
- /* no more branches, didn't find a match */
- status = RA_NOMATCH;
- regstack_pop(&scan);
- } else {
- /* Prepare to try a branch. */
- rp->rs_scan = regnext(scan);
- reg_save(&rp->rs_un.regsave, &backpos);
- scan = OPERAND(scan);
- }
- }
- break;
-
- case RS_BRCPLX_MORE:
- /* Pop the state. Restore pointers when there is no match. */
- if (status == RA_NOMATCH) {
- reg_restore(&rp->rs_un.regsave, &backpos);
- --brace_count[rp->rs_no]; /* decrement match count */
- }
- regstack_pop(&scan);
- break;
-
- case RS_BRCPLX_LONG:
- /* Pop the state. Restore pointers when there is no match. */
- if (status == RA_NOMATCH) {
- /* There was no match, but we did find enough matches. */
- reg_restore(&rp->rs_un.regsave, &backpos);
- --brace_count[rp->rs_no];
- /* continue with the items after "\{}" */
- status = RA_CONT;
- }
- regstack_pop(&scan);
- if (status == RA_CONT)
- scan = regnext(scan);
- break;
-
- case RS_BRCPLX_SHORT:
- /* Pop the state. Restore pointers when there is no match. */
- if (status == RA_NOMATCH)
- /* There was no match, try to match one more item. */
- reg_restore(&rp->rs_un.regsave, &backpos);
- regstack_pop(&scan);
- if (status == RA_NOMATCH) {
- scan = OPERAND(scan);
- status = RA_CONT;
- }
- break;
-
- case RS_NOMATCH:
- /* Pop the state. If the operand matches for NOMATCH or
- * doesn't match for MATCH/SUBPAT, we fail. Otherwise backup,
- * except for SUBPAT, and continue with the next item. */
- if (status == (rp->rs_no == NOMATCH ? RA_MATCH : RA_NOMATCH))
- status = RA_NOMATCH;
- else {
- status = RA_CONT;
- if (rp->rs_no != SUBPAT) /* zero-width */
- reg_restore(&rp->rs_un.regsave, &backpos);
- }
- regstack_pop(&scan);
- if (status == RA_CONT)
- scan = regnext(scan);
- break;
-
- case RS_BEHIND1:
- if (status == RA_NOMATCH) {
- regstack_pop(&scan);
- regstack.ga_len -= sizeof(regbehind_T);
- } else {
- /* The stuff after BEHIND/NOBEHIND matches. Now try if
- * the behind part does (not) match before the current
- * position in the input. This must be done at every
- * position in the input and checking if the match ends at
- * the current position. */
-
- /* save the position after the found match for next */
- reg_save(&(((regbehind_T *)rp) - 1)->save_after, &backpos);
-
- /* Start looking for a match with operand at the current
- * position. Go back one character until we find the
- * result, hitting the start of the line or the previous
- * line (for multi-line matching).
- * Set behind_pos to where the match should end, BHPOS
- * will match it. Save the current value. */
- (((regbehind_T *)rp) - 1)->save_behind = behind_pos;
- behind_pos = rp->rs_un.regsave;
-
- rp->rs_state = RS_BEHIND2;
-
- reg_restore(&rp->rs_un.regsave, &backpos);
- scan = OPERAND(rp->rs_scan) + 4;
- }
- break;
-
- case RS_BEHIND2:
- /*
- * Looping for BEHIND / NOBEHIND match.
- */
- if (status == RA_MATCH && reg_save_equal(&behind_pos)) {
- /* found a match that ends where "next" started */
- behind_pos = (((regbehind_T *)rp) - 1)->save_behind;
- if (rp->rs_no == BEHIND)
- reg_restore(&(((regbehind_T *)rp) - 1)->save_after,
- &backpos);
- else {
- /* But we didn't want a match. Need to restore the
- * subexpr, because what follows matched, so they have
- * been set. */
- status = RA_NOMATCH;
- restore_subexpr(((regbehind_T *)rp) - 1);
- }
- regstack_pop(&scan);
- regstack.ga_len -= sizeof(regbehind_T);
- } else {
- long limit;
-
- /* No match or a match that doesn't end where we want it: Go
- * back one character. May go to previous line once. */
- no = OK;
- limit = OPERAND_MIN(rp->rs_scan);
- if (REG_MULTI) {
- if (limit > 0
- && ((rp->rs_un.regsave.rs_u.pos.lnum
- < behind_pos.rs_u.pos.lnum
- ? (colnr_T)STRLEN(rex.line)
- : behind_pos.rs_u.pos.col)
- - rp->rs_un.regsave.rs_u.pos.col >= limit))
- no = FAIL;
- else if (rp->rs_un.regsave.rs_u.pos.col == 0) {
- if (rp->rs_un.regsave.rs_u.pos.lnum
- < behind_pos.rs_u.pos.lnum
- || reg_getline(
- --rp->rs_un.regsave.rs_u.pos.lnum)
- == NULL)
- no = FAIL;
- else {
- reg_restore(&rp->rs_un.regsave, &backpos);
- rp->rs_un.regsave.rs_u.pos.col =
- (colnr_T)STRLEN(rex.line);
- }
- } else {
- const char_u *const line =
- reg_getline(rp->rs_un.regsave.rs_u.pos.lnum);
-
- rp->rs_un.regsave.rs_u.pos.col -=
- utf_head_off(line,
- line + rp->rs_un.regsave.rs_u.pos.col - 1)
- + 1;
- }
- } else {
- if (rp->rs_un.regsave.rs_u.ptr == rex.line) {
- no = FAIL;
- } else {
- MB_PTR_BACK(rex.line, rp->rs_un.regsave.rs_u.ptr);
- if (limit > 0
- && (long)(behind_pos.rs_u.ptr
- - rp->rs_un.regsave.rs_u.ptr) > limit) {
- no = FAIL;
- }
- }
- }
- if (no == OK) {
- /* Advanced, prepare for finding match again. */
- reg_restore(&rp->rs_un.regsave, &backpos);
- scan = OPERAND(rp->rs_scan) + 4;
- if (status == RA_MATCH) {
- /* We did match, so subexpr may have been changed,
- * need to restore them for the next try. */
- status = RA_NOMATCH;
- restore_subexpr(((regbehind_T *)rp) - 1);
- }
- } else {
- /* Can't advance. For NOBEHIND that's a match. */
- behind_pos = (((regbehind_T *)rp) - 1)->save_behind;
- if (rp->rs_no == NOBEHIND) {
- reg_restore(&(((regbehind_T *)rp) - 1)->save_after,
- &backpos);
- status = RA_MATCH;
- } else {
- /* We do want a proper match. Need to restore the
- * subexpr if we had a match, because they may have
- * been set. */
- if (status == RA_MATCH) {
- status = RA_NOMATCH;
- restore_subexpr(((regbehind_T *)rp) - 1);
- }
- }
- regstack_pop(&scan);
- regstack.ga_len -= sizeof(regbehind_T);
- }
- }
- break;
-
- case RS_STAR_LONG:
- case RS_STAR_SHORT:
- {
- regstar_T *rst = ((regstar_T *)rp) - 1;
-
- if (status == RA_MATCH) {
- regstack_pop(&scan);
- regstack.ga_len -= sizeof(regstar_T);
- break;
- }
-
- /* Tried once already, restore input pointers. */
- if (status != RA_BREAK)
- reg_restore(&rp->rs_un.regsave, &backpos);
-
- /* Repeat until we found a position where it could match. */
- for (;; ) {
- if (status != RA_BREAK) {
- /* Tried first position already, advance. */
- if (rp->rs_state == RS_STAR_LONG) {
- /* Trying for longest match, but couldn't or
- * didn't match -- back up one char. */
- if (--rst->count < rst->minval)
- break;
- if (rex.input == rex.line) {
- // backup to last char of previous line
- rex.lnum--;
- rex.line = reg_getline(rex.lnum);
- // Just in case regrepeat() didn't count right.
- if (rex.line == NULL) {
- break;
- }
- rex.input = rex.line + STRLEN(rex.line);
- fast_breakcheck();
- } else {
- MB_PTR_BACK(rex.line, rex.input);
- }
- } else {
- /* Range is backwards, use shortest match first.
- * Careful: maxval and minval are exchanged!
- * Couldn't or didn't match: try advancing one
- * char. */
- if (rst->count == rst->minval
- || regrepeat(OPERAND(rp->rs_scan), 1L) == 0)
- break;
- ++rst->count;
- }
- if (got_int)
- break;
- } else
- status = RA_NOMATCH;
-
- // If it could match, try it.
- if (rst->nextb == NUL || *rex.input == rst->nextb
- || *rex.input == rst->nextb_ic) {
- reg_save(&rp->rs_un.regsave, &backpos);
- scan = regnext(rp->rs_scan);
- status = RA_CONT;
- break;
- }
- }
- if (status != RA_CONT) {
- /* Failed. */
- regstack_pop(&scan);
- regstack.ga_len -= sizeof(regstar_T);
- status = RA_NOMATCH;
- }
- }
- break;
- }
-
- /* If we want to continue the inner loop or didn't pop a state
- * continue matching loop */
- if (status == RA_CONT || rp == (regitem_T *)
- ((char *)regstack.ga_data + regstack.ga_len) - 1)
- break;
- }
-
- /* May need to continue with the inner loop, starting at "scan". */
- if (status == RA_CONT)
- continue;
-
- /*
- * If the regstack is empty or something failed we are done.
- */
- if (GA_EMPTY(&regstack) || status == RA_FAIL) {
- if (scan == NULL) {
- /*
- * We get here only if there's trouble -- normally "case END" is
- * the terminating point.
- */
- iemsg(_(e_re_corr));
-#ifdef REGEXP_DEBUG
- printf("Premature EOL\n");
-#endif
- }
- return status == RA_MATCH;
- }
-
- } /* End of loop until the regstack is empty. */
-
- /* NOTREACHED */
-}
-
-/*
- * Push an item onto the regstack.
- * Returns pointer to new item. Returns NULL when out of memory.
- */
-static regitem_T *regstack_push(regstate_T state, char_u *scan)
-{
- regitem_T *rp;
-
- if ((long)((unsigned)regstack.ga_len >> 10) >= p_mmp) {
- emsg(_(e_maxmempat));
- return NULL;
- }
- ga_grow(&regstack, sizeof(regitem_T));
-
- rp = (regitem_T *)((char *)regstack.ga_data + regstack.ga_len);
- rp->rs_state = state;
- rp->rs_scan = scan;
-
- regstack.ga_len += sizeof(regitem_T);
- return rp;
-}
-
-/*
- * Pop an item from the regstack.
- */
-static void regstack_pop(char_u **scan)
-{
- regitem_T *rp;
-
- rp = (regitem_T *)((char *)regstack.ga_data + regstack.ga_len) - 1;
- *scan = rp->rs_scan;
-
- regstack.ga_len -= sizeof(regitem_T);
-}
-
-/*
- * regrepeat - repeatedly match something simple, return how many.
- * Advances rex.input (and rex.lnum) to just after the matched chars.
- */
-static int
-regrepeat (
- char_u *p,
- long maxcount /* maximum number of matches allowed */
-)
-{
- long count = 0;
- char_u *opnd;
- int mask;
- int testval = 0;
-
- char_u *scan = rex.input; // Make local copy of rex.input for speed.
- opnd = OPERAND(p);
- switch (OP(p)) {
- case ANY:
- case ANY + ADD_NL:
- while (count < maxcount) {
- /* Matching anything means we continue until end-of-line (or
- * end-of-file for ANY + ADD_NL), only limited by maxcount. */
- while (*scan != NUL && count < maxcount) {
- count++;
- MB_PTR_ADV(scan);
- }
- if (!REG_MULTI || !WITH_NL(OP(p)) || rex.lnum > rex.reg_maxline
- || rex.reg_line_lbr || count == maxcount) {
- break;
- }
- count++; // count the line-break
- reg_nextline();
- scan = rex.input;
- if (got_int) {
- break;
- }
- }
- break;
-
- case IDENT:
- case IDENT + ADD_NL:
- testval = 1;
- FALLTHROUGH;
- case SIDENT:
- case SIDENT + ADD_NL:
- while (count < maxcount) {
- if (vim_isIDc(utf_ptr2char(scan)) && (testval || !ascii_isdigit(*scan))) {
- MB_PTR_ADV(scan);
- } else if (*scan == NUL) {
- if (!REG_MULTI || !WITH_NL(OP(p)) || rex.lnum > rex.reg_maxline
- || rex.reg_line_lbr) {
- break;
- }
- reg_nextline();
- scan = rex.input;
- if (got_int) {
- break;
- }
- } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) {
- scan++;
- } else {
- break;
- }
- ++count;
- }
- break;
-
- case KWORD:
- case KWORD + ADD_NL:
- testval = 1;
- FALLTHROUGH;
- case SKWORD:
- case SKWORD + ADD_NL:
- while (count < maxcount) {
- if (vim_iswordp_buf(scan, rex.reg_buf)
- && (testval || !ascii_isdigit(*scan))) {
- MB_PTR_ADV(scan);
- } else if (*scan == NUL) {
- if (!REG_MULTI || !WITH_NL(OP(p)) || rex.lnum > rex.reg_maxline
- || rex.reg_line_lbr) {
- break;
- }
- reg_nextline();
- scan = rex.input;
- if (got_int) {
- break;
- }
- } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) {
- scan++;
- } else {
- break;
- }
- count++;
- }
- break;
-
- case FNAME:
- case FNAME + ADD_NL:
- testval = 1;
- FALLTHROUGH;
- case SFNAME:
- case SFNAME + ADD_NL:
- while (count < maxcount) {
- if (vim_isfilec(utf_ptr2char(scan)) && (testval || !ascii_isdigit(*scan))) {
- MB_PTR_ADV(scan);
- } else if (*scan == NUL) {
- if (!REG_MULTI || !WITH_NL(OP(p)) || rex.lnum > rex.reg_maxline
- || rex.reg_line_lbr) {
- break;
- }
- reg_nextline();
- scan = rex.input;
- if (got_int) {
- break;
- }
- } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) {
- scan++;
- } else {
- break;
- }
- count++;
- }
- break;
-
- case PRINT:
- case PRINT + ADD_NL:
- testval = 1;
- FALLTHROUGH;
- case SPRINT:
- case SPRINT + ADD_NL:
- while (count < maxcount) {
- if (*scan == NUL) {
- if (!REG_MULTI || !WITH_NL(OP(p)) || rex.lnum > rex.reg_maxline
- || rex.reg_line_lbr) {
- break;
- }
- reg_nextline();
- scan = rex.input;
- if (got_int) {
- break;
- }
- } else if (vim_isprintc(utf_ptr2char(scan)) == 1
- && (testval || !ascii_isdigit(*scan))) {
- MB_PTR_ADV(scan);
- } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) {
- scan++;
- } else {
- break;
- }
- count++;
- }
- break;
-
- case WHITE:
- case WHITE + ADD_NL:
- testval = mask = RI_WHITE;
-do_class:
- while (count < maxcount) {
- int l;
- if (*scan == NUL) {
- if (!REG_MULTI || !WITH_NL(OP(p)) || rex.lnum > rex.reg_maxline
- || rex.reg_line_lbr) {
- break;
- }
- reg_nextline();
- scan = rex.input;
- if (got_int) {
- break;
- }
- } else if ((l = utfc_ptr2len(scan)) > 1) {
- if (testval != 0) {
- break;
- }
- scan += l;
- } else if ((class_tab[*scan] & mask) == testval) {
- scan++;
- } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) {
- scan++;
- } else {
- break;
- }
- ++count;
- }
- break;
-
- case NWHITE:
- case NWHITE + ADD_NL:
- mask = RI_WHITE;
- goto do_class;
- case DIGIT:
- case DIGIT + ADD_NL:
- testval = mask = RI_DIGIT;
- goto do_class;
- case NDIGIT:
- case NDIGIT + ADD_NL:
- mask = RI_DIGIT;
- goto do_class;
- case HEX:
- case HEX + ADD_NL:
- testval = mask = RI_HEX;
- goto do_class;
- case NHEX:
- case NHEX + ADD_NL:
- mask = RI_HEX;
- goto do_class;
- case OCTAL:
- case OCTAL + ADD_NL:
- testval = mask = RI_OCTAL;
- goto do_class;
- case NOCTAL:
- case NOCTAL + ADD_NL:
- mask = RI_OCTAL;
- goto do_class;
- case WORD:
- case WORD + ADD_NL:
- testval = mask = RI_WORD;
- goto do_class;
- case NWORD:
- case NWORD + ADD_NL:
- mask = RI_WORD;
- goto do_class;
- case HEAD:
- case HEAD + ADD_NL:
- testval = mask = RI_HEAD;
- goto do_class;
- case NHEAD:
- case NHEAD + ADD_NL:
- mask = RI_HEAD;
- goto do_class;
- case ALPHA:
- case ALPHA + ADD_NL:
- testval = mask = RI_ALPHA;
- goto do_class;
- case NALPHA:
- case NALPHA + ADD_NL:
- mask = RI_ALPHA;
- goto do_class;
- case LOWER:
- case LOWER + ADD_NL:
- testval = mask = RI_LOWER;
- goto do_class;
- case NLOWER:
- case NLOWER + ADD_NL:
- mask = RI_LOWER;
- goto do_class;
- case UPPER:
- case UPPER + ADD_NL:
- testval = mask = RI_UPPER;
- goto do_class;
- case NUPPER:
- case NUPPER + ADD_NL:
- mask = RI_UPPER;
- goto do_class;
-
- case EXACTLY:
- {
- int cu, cl;
-
- // This doesn't do a multi-byte character, because a MULTIBYTECODE
- // would have been used for it. It does handle single-byte
- // characters, such as latin1.
- if (rex.reg_ic) {
- cu = mb_toupper(*opnd);
- cl = mb_tolower(*opnd);
- while (count < maxcount && (*scan == cu || *scan == cl)) {
- count++;
- scan++;
- }
- } else {
- cu = *opnd;
- while (count < maxcount && *scan == cu) {
- count++;
- scan++;
- }
- }
- break;
- }
-
- case MULTIBYTECODE:
- {
- int i, len, cf = 0;
-
- /* Safety check (just in case 'encoding' was changed since
- * compiling the program). */
- if ((len = utfc_ptr2len(opnd)) > 1) {
- if (rex.reg_ic) {
- cf = utf_fold(utf_ptr2char(opnd));
- }
- while (count < maxcount && utfc_ptr2len(scan) >= len) {
- for (i = 0; i < len; i++) {
- if (opnd[i] != scan[i]) {
- break;
- }
- }
- if (i < len && (!rex.reg_ic
- || utf_fold(utf_ptr2char(scan)) != cf)) {
- break;
- }
- scan += len;
- ++count;
- }
- }
- }
- break;
-
- case ANYOF:
- case ANYOF + ADD_NL:
- testval = 1;
- FALLTHROUGH;
-
- case ANYBUT:
- case ANYBUT + ADD_NL:
- while (count < maxcount) {
- int len;
- if (*scan == NUL) {
- if (!REG_MULTI || !WITH_NL(OP(p)) || rex.lnum > rex.reg_maxline
- || rex.reg_line_lbr) {
- break;
- }
- reg_nextline();
- scan = rex.input;
- if (got_int) {
- break;
- }
- } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) {
- scan++;
- } else if ((len = utfc_ptr2len(scan)) > 1) {
- if ((cstrchr(opnd, utf_ptr2char(scan)) == NULL) == testval) {
- break;
- }
- scan += len;
- } else {
- if ((cstrchr(opnd, *scan) == NULL) == testval)
- break;
- ++scan;
- }
- ++count;
- }
- break;
-
- case NEWL:
- while (count < maxcount
- && ((*scan == NUL && rex.lnum <= rex.reg_maxline && !rex.reg_line_lbr
- && REG_MULTI) || (*scan == '\n' && rex.reg_line_lbr))) {
- count++;
- if (rex.reg_line_lbr) {
- ADVANCE_REGINPUT();
- } else {
- reg_nextline();
- }
- scan = rex.input;
- if (got_int) {
- break;
- }
- }
- break;
-
- default: // Oh dear. Called inappropriately.
- iemsg(_(e_re_corr));
-#ifdef REGEXP_DEBUG
- printf("Called regrepeat with op code %d\n", OP(p));
-#endif
- break;
- }
-
- rex.input = scan;
-
- return (int)count;
-}
-
-/*
- * regnext - dig the "next" pointer out of a node
- * Returns NULL when calculating size, when there is no next item and when
- * there is an error.
- */
-static char_u *regnext(char_u *p)
- FUNC_ATTR_NONNULL_ALL
-{
- int offset;
-
- if (p == JUST_CALC_SIZE || reg_toolong)
- return NULL;
-
- offset = NEXT(p);
- if (offset == 0)
- return NULL;
-
- if (OP(p) == BACK)
- return p - offset;
- else
- return p + offset;
-}
-
/*
* Check the regexp program for its magic number.
* Return true if it's wrong.
*/
static int prog_magic_wrong(void)
{
- regprog_T *prog;
+ regprog_T *prog;
prog = REG_MULTI ? rex.reg_mmatch->regprog : rex.reg_match->regprog;
if (prog->engine == &nfa_regengine) {
@@ -5663,7 +1226,7 @@ static void cleanup_zsubexpr(void)
{
if (rex.need_clear_zsubexpr) {
if (REG_MULTI) {
- /* Use 0xff to set lnum to -1 */
+ // Use 0xff to set lnum to -1
memset(reg_startzpos, 0xff, sizeof(lpos_T) * NSUBEXP);
memset(reg_endzpos, 0xff, sizeof(lpos_T) * NSUBEXP);
} else {
@@ -5674,46 +1237,6 @@ static void cleanup_zsubexpr(void)
}
}
-// Save the current subexpr to "bp", so that they can be restored
-// later by restore_subexpr().
-static void save_subexpr(regbehind_T *bp)
- FUNC_ATTR_NONNULL_ALL
-{
- // When "rex.need_clear_subexpr" is set we don't need to save the values, only
- // remember that this flag needs to be set again when restoring.
- bp->save_need_clear_subexpr = rex.need_clear_subexpr;
- if (!rex.need_clear_subexpr) {
- for (int i = 0; i < NSUBEXP; i++) {
- if (REG_MULTI) {
- bp->save_start[i].se_u.pos = rex.reg_startpos[i];
- bp->save_end[i].se_u.pos = rex.reg_endpos[i];
- } else {
- bp->save_start[i].se_u.ptr = rex.reg_startp[i];
- bp->save_end[i].se_u.ptr = rex.reg_endp[i];
- }
- }
- }
-}
-
-// Restore the subexpr from "bp".
-static void restore_subexpr(regbehind_T *bp)
- FUNC_ATTR_NONNULL_ALL
-{
- // Only need to restore saved values when they are not to be cleared.
- rex.need_clear_subexpr = bp->save_need_clear_subexpr;
- if (!rex.need_clear_subexpr) {
- for (int i = 0; i < NSUBEXP; i++) {
- if (REG_MULTI) {
- rex.reg_startpos[i] = bp->save_start[i].se_u.pos;
- rex.reg_endpos[i] = bp->save_end[i].se_u.pos;
- } else {
- rex.reg_startp[i] = bp->save_start[i].se_u.ptr;
- rex.reg_endp[i] = bp->save_end[i].se_u.ptr;
- }
- }
- }
-}
-
// Advance rex.lnum, rex.line and rex.input to the next line.
static void reg_nextline(void)
{
@@ -5722,104 +1245,30 @@ static void reg_nextline(void)
fast_breakcheck();
}
-// Save the input line and position in a regsave_T.
-static void reg_save(regsave_T *save, garray_T *gap)
- FUNC_ATTR_NONNULL_ALL
-{
- if (REG_MULTI) {
- save->rs_u.pos.col = (colnr_T)(rex.input - rex.line);
- save->rs_u.pos.lnum = rex.lnum;
- } else {
- save->rs_u.ptr = rex.input;
- }
- save->rs_len = gap->ga_len;
-}
-
-// Restore the input line and position from a regsave_T.
-static void reg_restore(regsave_T *save, garray_T *gap)
- FUNC_ATTR_NONNULL_ALL
-{
- if (REG_MULTI) {
- if (rex.lnum != save->rs_u.pos.lnum) {
- // only call reg_getline() when the line number changed to save
- // a bit of time
- rex.lnum = save->rs_u.pos.lnum;
- rex.line = reg_getline(rex.lnum);
- }
- rex.input = rex.line + save->rs_u.pos.col;
- } else {
- rex.input = save->rs_u.ptr;
- }
- gap->ga_len = save->rs_len;
-}
-
-// Return true if current position is equal to saved position.
-static bool reg_save_equal(const regsave_T *save)
- FUNC_ATTR_NONNULL_ALL
-{
- if (REG_MULTI) {
- return rex.lnum == save->rs_u.pos.lnum
- && rex.input == rex.line + save->rs_u.pos.col;
- }
- return rex.input == save->rs_u.ptr;
-}
-
-/*
- * Tentatively set the sub-expression start to the current position (after
- * calling regmatch() they will have changed). Need to save the existing
- * values for when there is no match.
- * Use se_save() to use pointer (save_se_multi()) or position (save_se_one()),
- * depending on REG_MULTI.
- */
-static void save_se_multi(save_se_T *savep, lpos_T *posp)
-{
- savep->se_u.pos = *posp;
- posp->lnum = rex.lnum;
- posp->col = (colnr_T)(rex.input - rex.line);
-}
-
-static void save_se_one(save_se_T *savep, char_u **pp)
-{
- savep->se_u.ptr = *pp;
- *pp = rex.input;
-}
-
-/*
- * Compare a number with the operand of RE_LNUM, RE_COL or RE_VCOL.
- */
-static int re_num_cmp(uint32_t val, char_u *scan)
-{
- uint32_t n = (uint32_t)OPERAND_MIN(scan);
-
- if (OPERAND_CMP(scan) == '>')
- return val > n;
- if (OPERAND_CMP(scan) == '<')
- return val < n;
- return val == n;
-}
-
/*
* Check whether a backreference matches.
* Returns RA_FAIL, RA_NOMATCH or RA_MATCH.
* If "bytelen" is not NULL, it is set to the byte length of the match in the
* last line.
*/
-static int match_with_backref(linenr_T start_lnum, colnr_T start_col, linenr_T end_lnum, colnr_T end_col, int *bytelen)
+static int match_with_backref(linenr_T start_lnum, colnr_T start_col, linenr_T end_lnum,
+ colnr_T end_col, int *bytelen)
{
linenr_T clnum = start_lnum;
colnr_T ccol = start_col;
int len;
- char_u *p;
+ char_u *p;
- if (bytelen != NULL)
+ if (bytelen != NULL) {
*bytelen = 0;
- for (;; ) {
+ }
+ for (;;) {
// Since getting one line may invalidate the other, need to make copy.
// Slow!
if (rex.line != reg_tofree) {
len = (int)STRLEN(rex.line);
if (reg_tofree == NULL || len >= (int)reg_tofreelen) {
- len += 50; /* get some extra */
+ len += 50; // get some extra
xfree(reg_tofree);
reg_tofree = xmalloc(len);
reg_tofreelen = len;
@@ -5829,14 +1278,15 @@ static int match_with_backref(linenr_T start_lnum, colnr_T start_col, linenr_T e
rex.line = reg_tofree;
}
- /* Get the line to compare with. */
+ // Get the line to compare with.
p = reg_getline(clnum);
assert(p);
- if (clnum == end_lnum)
+ if (clnum == end_lnum) {
len = end_col - ccol;
- else
+ } else {
len = (int)STRLEN(p + ccol);
+ }
if (cstrncmp(p + ccol, rex.input, &len) != 0) {
return RA_NOMATCH; // doesn't match
@@ -5851,14 +1301,16 @@ static int match_with_backref(linenr_T start_lnum, colnr_T start_col, linenr_T e
return RA_NOMATCH; // text too short
}
- /* Advance to next line. */
+ // Advance to next line.
reg_nextline();
- if (bytelen != NULL)
+ if (bytelen != NULL) {
*bytelen = 0;
- ++clnum;
+ }
+ clnum++;
ccol = 0;
- if (got_int)
+ if (got_int) {
return RA_FAIL;
+ }
}
// found a match! Note that rex.line may now point to a copy of the line,
@@ -5866,520 +1318,72 @@ static int match_with_backref(linenr_T start_lnum, colnr_T start_col, linenr_T e
return RA_MATCH;
}
-#ifdef BT_REGEXP_DUMP
-
-/*
- * regdump - dump a regexp onto stdout in vaguely comprehensible form
- */
-static void regdump(char_u *pattern, bt_regprog_T *r)
-{
- char_u *s;
- int op = EXACTLY; /* Arbitrary non-END op. */
- char_u *next;
- char_u *end = NULL;
- FILE *f;
-
-#ifdef BT_REGEXP_LOG
- f = fopen("bt_regexp_log.log", "a");
-#else
- f = stdout;
-#endif
- if (f == NULL)
- return;
- fprintf(f, "-------------------------------------\n\r\nregcomp(%s):\r\n",
- pattern);
-
- s = r->program + 1;
- /*
- * Loop until we find the END that isn't before a referred next (an END
- * can also appear in a NOMATCH operand).
- */
- while (op != END || s <= end) {
- op = OP(s);
- fprintf(f, "%2d%s", (int)(s - r->program), regprop(s)); /* Where, what. */
- next = regnext(s);
- if (next == NULL) /* Next ptr. */
- fprintf(f, "(0)");
- else
- fprintf(f, "(%d)", (int)((s - r->program) + (next - s)));
- if (end < next)
- end = next;
- if (op == BRACE_LIMITS) {
- /* Two ints */
- fprintf(f, " minval %" PRId64 ", maxval %" PRId64,
- (int64_t)OPERAND_MIN(s), (int64_t)OPERAND_MAX(s));
- s += 8;
- } else if (op == BEHIND || op == NOBEHIND) {
- /* one int */
- fprintf(f, " count %" PRId64, (int64_t)OPERAND_MIN(s));
- s += 4;
- } else if (op == RE_LNUM || op == RE_COL || op == RE_VCOL) {
- // one int plus comparator
- fprintf(f, " count %" PRId64, (int64_t)OPERAND_MIN(s));
- s += 5;
- }
- s += 3;
- if (op == ANYOF || op == ANYOF + ADD_NL
- || op == ANYBUT || op == ANYBUT + ADD_NL
- || op == EXACTLY) {
- /* Literal string, where present. */
- fprintf(f, "\nxxxxxxxxx\n");
- while (*s != NUL)
- fprintf(f, "%c", *s++);
- fprintf(f, "\nxxxxxxxxx\n");
- s++;
- }
- fprintf(f, "\r\n");
- }
-
- /* Header fields of interest. */
- if (r->regstart != NUL)
- fprintf(f, "start `%s' 0x%x; ", r->regstart < 256
- ? (char *)transchar(r->regstart)
- : "multibyte", r->regstart);
- if (r->reganch)
- fprintf(f, "anchored; ");
- if (r->regmust != NULL)
- fprintf(f, "must have \"%s\"", r->regmust);
- fprintf(f, "\r\n");
-
-#ifdef BT_REGEXP_LOG
- fclose(f);
-#endif
-}
-#endif /* BT_REGEXP_DUMP */
-
-#ifdef REGEXP_DEBUG
-/*
- * regprop - printable representation of opcode
- */
-static char_u *regprop(char_u *op)
+/// Used in a place where no * or \+ can follow.
+static bool re_mult_next(char *what)
{
- char *p;
- static char buf[50];
-
- STRCPY(buf, ":");
-
- switch ((int) OP(op)) {
- case BOL:
- p = "BOL";
- break;
- case EOL:
- p = "EOL";
- break;
- case RE_BOF:
- p = "BOF";
- break;
- case RE_EOF:
- p = "EOF";
- break;
- case CURSOR:
- p = "CURSOR";
- break;
- case RE_VISUAL:
- p = "RE_VISUAL";
- break;
- case RE_LNUM:
- p = "RE_LNUM";
- break;
- case RE_MARK:
- p = "RE_MARK";
- break;
- case RE_COL:
- p = "RE_COL";
- break;
- case RE_VCOL:
- p = "RE_VCOL";
- break;
- case BOW:
- p = "BOW";
- break;
- case EOW:
- p = "EOW";
- break;
- case ANY:
- p = "ANY";
- break;
- case ANY + ADD_NL:
- p = "ANY+NL";
- break;
- case ANYOF:
- p = "ANYOF";
- break;
- case ANYOF + ADD_NL:
- p = "ANYOF+NL";
- break;
- case ANYBUT:
- p = "ANYBUT";
- break;
- case ANYBUT + ADD_NL:
- p = "ANYBUT+NL";
- break;
- case IDENT:
- p = "IDENT";
- break;
- case IDENT + ADD_NL:
- p = "IDENT+NL";
- break;
- case SIDENT:
- p = "SIDENT";
- break;
- case SIDENT + ADD_NL:
- p = "SIDENT+NL";
- break;
- case KWORD:
- p = "KWORD";
- break;
- case KWORD + ADD_NL:
- p = "KWORD+NL";
- break;
- case SKWORD:
- p = "SKWORD";
- break;
- case SKWORD + ADD_NL:
- p = "SKWORD+NL";
- break;
- case FNAME:
- p = "FNAME";
- break;
- case FNAME + ADD_NL:
- p = "FNAME+NL";
- break;
- case SFNAME:
- p = "SFNAME";
- break;
- case SFNAME + ADD_NL:
- p = "SFNAME+NL";
- break;
- case PRINT:
- p = "PRINT";
- break;
- case PRINT + ADD_NL:
- p = "PRINT+NL";
- break;
- case SPRINT:
- p = "SPRINT";
- break;
- case SPRINT + ADD_NL:
- p = "SPRINT+NL";
- break;
- case WHITE:
- p = "WHITE";
- break;
- case WHITE + ADD_NL:
- p = "WHITE+NL";
- break;
- case NWHITE:
- p = "NWHITE";
- break;
- case NWHITE + ADD_NL:
- p = "NWHITE+NL";
- break;
- case DIGIT:
- p = "DIGIT";
- break;
- case DIGIT + ADD_NL:
- p = "DIGIT+NL";
- break;
- case NDIGIT:
- p = "NDIGIT";
- break;
- case NDIGIT + ADD_NL:
- p = "NDIGIT+NL";
- break;
- case HEX:
- p = "HEX";
- break;
- case HEX + ADD_NL:
- p = "HEX+NL";
- break;
- case NHEX:
- p = "NHEX";
- break;
- case NHEX + ADD_NL:
- p = "NHEX+NL";
- break;
- case OCTAL:
- p = "OCTAL";
- break;
- case OCTAL + ADD_NL:
- p = "OCTAL+NL";
- break;
- case NOCTAL:
- p = "NOCTAL";
- break;
- case NOCTAL + ADD_NL:
- p = "NOCTAL+NL";
- break;
- case WORD:
- p = "WORD";
- break;
- case WORD + ADD_NL:
- p = "WORD+NL";
- break;
- case NWORD:
- p = "NWORD";
- break;
- case NWORD + ADD_NL:
- p = "NWORD+NL";
- break;
- case HEAD:
- p = "HEAD";
- break;
- case HEAD + ADD_NL:
- p = "HEAD+NL";
- break;
- case NHEAD:
- p = "NHEAD";
- break;
- case NHEAD + ADD_NL:
- p = "NHEAD+NL";
- break;
- case ALPHA:
- p = "ALPHA";
- break;
- case ALPHA + ADD_NL:
- p = "ALPHA+NL";
- break;
- case NALPHA:
- p = "NALPHA";
- break;
- case NALPHA + ADD_NL:
- p = "NALPHA+NL";
- break;
- case LOWER:
- p = "LOWER";
- break;
- case LOWER + ADD_NL:
- p = "LOWER+NL";
- break;
- case NLOWER:
- p = "NLOWER";
- break;
- case NLOWER + ADD_NL:
- p = "NLOWER+NL";
- break;
- case UPPER:
- p = "UPPER";
- break;
- case UPPER + ADD_NL:
- p = "UPPER+NL";
- break;
- case NUPPER:
- p = "NUPPER";
- break;
- case NUPPER + ADD_NL:
- p = "NUPPER+NL";
- break;
- case BRANCH:
- p = "BRANCH";
- break;
- case EXACTLY:
- p = "EXACTLY";
- break;
- case NOTHING:
- p = "NOTHING";
- break;
- case BACK:
- p = "BACK";
- break;
- case END:
- p = "END";
- break;
- case MOPEN + 0:
- p = "MATCH START";
- break;
- case MOPEN + 1:
- case MOPEN + 2:
- case MOPEN + 3:
- case MOPEN + 4:
- case MOPEN + 5:
- case MOPEN + 6:
- case MOPEN + 7:
- case MOPEN + 8:
- case MOPEN + 9:
- sprintf(buf + STRLEN(buf), "MOPEN%d", OP(op) - MOPEN);
- p = NULL;
- break;
- case MCLOSE + 0:
- p = "MATCH END";
- break;
- case MCLOSE + 1:
- case MCLOSE + 2:
- case MCLOSE + 3:
- case MCLOSE + 4:
- case MCLOSE + 5:
- case MCLOSE + 6:
- case MCLOSE + 7:
- case MCLOSE + 8:
- case MCLOSE + 9:
- sprintf(buf + STRLEN(buf), "MCLOSE%d", OP(op) - MCLOSE);
- p = NULL;
- break;
- case BACKREF + 1:
- case BACKREF + 2:
- case BACKREF + 3:
- case BACKREF + 4:
- case BACKREF + 5:
- case BACKREF + 6:
- case BACKREF + 7:
- case BACKREF + 8:
- case BACKREF + 9:
- sprintf(buf + STRLEN(buf), "BACKREF%d", OP(op) - BACKREF);
- p = NULL;
- break;
- case NOPEN:
- p = "NOPEN";
- break;
- case NCLOSE:
- p = "NCLOSE";
- break;
- case ZOPEN + 1:
- case ZOPEN + 2:
- case ZOPEN + 3:
- case ZOPEN + 4:
- case ZOPEN + 5:
- case ZOPEN + 6:
- case ZOPEN + 7:
- case ZOPEN + 8:
- case ZOPEN + 9:
- sprintf(buf + STRLEN(buf), "ZOPEN%d", OP(op) - ZOPEN);
- p = NULL;
- break;
- case ZCLOSE + 1:
- case ZCLOSE + 2:
- case ZCLOSE + 3:
- case ZCLOSE + 4:
- case ZCLOSE + 5:
- case ZCLOSE + 6:
- case ZCLOSE + 7:
- case ZCLOSE + 8:
- case ZCLOSE + 9:
- sprintf(buf + STRLEN(buf), "ZCLOSE%d", OP(op) - ZCLOSE);
- p = NULL;
- break;
- case ZREF + 1:
- case ZREF + 2:
- case ZREF + 3:
- case ZREF + 4:
- case ZREF + 5:
- case ZREF + 6:
- case ZREF + 7:
- case ZREF + 8:
- case ZREF + 9:
- sprintf(buf + STRLEN(buf), "ZREF%d", OP(op) - ZREF);
- p = NULL;
- break;
- case STAR:
- p = "STAR";
- break;
- case PLUS:
- p = "PLUS";
- break;
- case NOMATCH:
- p = "NOMATCH";
- break;
- case MATCH:
- p = "MATCH";
- break;
- case BEHIND:
- p = "BEHIND";
- break;
- case NOBEHIND:
- p = "NOBEHIND";
- break;
- case SUBPAT:
- p = "SUBPAT";
- break;
- case BRACE_LIMITS:
- p = "BRACE_LIMITS";
- break;
- case BRACE_SIMPLE:
- p = "BRACE_SIMPLE";
- break;
- case BRACE_COMPLEX + 0:
- case BRACE_COMPLEX + 1:
- case BRACE_COMPLEX + 2:
- case BRACE_COMPLEX + 3:
- case BRACE_COMPLEX + 4:
- case BRACE_COMPLEX + 5:
- case BRACE_COMPLEX + 6:
- case BRACE_COMPLEX + 7:
- case BRACE_COMPLEX + 8:
- case BRACE_COMPLEX + 9:
- sprintf(buf + STRLEN(buf), "BRACE_COMPLEX%d", OP(op) - BRACE_COMPLEX);
- p = NULL;
- break;
- case MULTIBYTECODE:
- p = "MULTIBYTECODE";
- break;
- case NEWL:
- p = "NEWL";
- break;
- default:
- sprintf(buf + STRLEN(buf), "corrupt %d", OP(op));
- p = NULL;
- break;
+ if (re_multi_type(peekchr()) == MULTI_MULT) {
+ semsg(_("E888: (NFA regexp) cannot repeat %s"), what);
+ rc_did_emsg = true;
+ return false;
}
- if (p != NULL)
- STRCAT(buf, p);
- return (char_u *)buf;
+ return true;
}
-#endif /* REGEXP_DEBUG */
-
+typedef struct {
+ int a, b, c;
+} decomp_T;
-/* 0xfb20 - 0xfb4f */
-static decomp_T decomp_table[0xfb4f-0xfb20+1] =
-{
- {0x5e2,0,0}, /* 0xfb20 alt ayin */
- {0x5d0,0,0}, /* 0xfb21 alt alef */
- {0x5d3,0,0}, /* 0xfb22 alt dalet */
- {0x5d4,0,0}, /* 0xfb23 alt he */
- {0x5db,0,0}, /* 0xfb24 alt kaf */
- {0x5dc,0,0}, /* 0xfb25 alt lamed */
- {0x5dd,0,0}, /* 0xfb26 alt mem-sofit */
- {0x5e8,0,0}, /* 0xfb27 alt resh */
- {0x5ea,0,0}, /* 0xfb28 alt tav */
- {'+', 0, 0}, /* 0xfb29 alt plus */
- {0x5e9, 0x5c1, 0}, /* 0xfb2a shin+shin-dot */
- {0x5e9, 0x5c2, 0}, /* 0xfb2b shin+sin-dot */
- {0x5e9, 0x5c1, 0x5bc}, /* 0xfb2c shin+shin-dot+dagesh */
- {0x5e9, 0x5c2, 0x5bc}, /* 0xfb2d shin+sin-dot+dagesh */
- {0x5d0, 0x5b7, 0}, /* 0xfb2e alef+patah */
- {0x5d0, 0x5b8, 0}, /* 0xfb2f alef+qamats */
- {0x5d0, 0x5b4, 0}, /* 0xfb30 alef+hiriq */
- {0x5d1, 0x5bc, 0}, /* 0xfb31 bet+dagesh */
- {0x5d2, 0x5bc, 0}, /* 0xfb32 gimel+dagesh */
- {0x5d3, 0x5bc, 0}, /* 0xfb33 dalet+dagesh */
- {0x5d4, 0x5bc, 0}, /* 0xfb34 he+dagesh */
- {0x5d5, 0x5bc, 0}, /* 0xfb35 vav+dagesh */
- {0x5d6, 0x5bc, 0}, /* 0xfb36 zayin+dagesh */
- {0xfb37, 0, 0}, /* 0xfb37 -- */
- {0x5d8, 0x5bc, 0}, /* 0xfb38 tet+dagesh */
- {0x5d9, 0x5bc, 0}, /* 0xfb39 yud+dagesh */
- {0x5da, 0x5bc, 0}, /* 0xfb3a kaf sofit+dagesh */
- {0x5db, 0x5bc, 0}, /* 0xfb3b kaf+dagesh */
- {0x5dc, 0x5bc, 0}, /* 0xfb3c lamed+dagesh */
- {0xfb3d, 0, 0}, /* 0xfb3d -- */
- {0x5de, 0x5bc, 0}, /* 0xfb3e mem+dagesh */
- {0xfb3f, 0, 0}, /* 0xfb3f -- */
- {0x5e0, 0x5bc, 0}, /* 0xfb40 nun+dagesh */
- {0x5e1, 0x5bc, 0}, /* 0xfb41 samech+dagesh */
- {0xfb42, 0, 0}, /* 0xfb42 -- */
- {0x5e3, 0x5bc, 0}, /* 0xfb43 pe sofit+dagesh */
- {0x5e4, 0x5bc,0}, /* 0xfb44 pe+dagesh */
- {0xfb45, 0, 0}, /* 0xfb45 -- */
- {0x5e6, 0x5bc, 0}, /* 0xfb46 tsadi+dagesh */
- {0x5e7, 0x5bc, 0}, /* 0xfb47 qof+dagesh */
- {0x5e8, 0x5bc, 0}, /* 0xfb48 resh+dagesh */
- {0x5e9, 0x5bc, 0}, /* 0xfb49 shin+dagesh */
- {0x5ea, 0x5bc, 0}, /* 0xfb4a tav+dagesh */
- {0x5d5, 0x5b9, 0}, /* 0xfb4b vav+holam */
- {0x5d1, 0x5bf, 0}, /* 0xfb4c bet+rafe */
- {0x5db, 0x5bf, 0}, /* 0xfb4d kaf+rafe */
- {0x5e4, 0x5bf, 0}, /* 0xfb4e pe+rafe */
- {0x5d0, 0x5dc, 0} /* 0xfb4f alef-lamed */
+// 0xfb20 - 0xfb4f
+static decomp_T decomp_table[0xfb4f - 0xfb20 + 1] =
+{
+ { 0x5e2, 0, 0 }, // 0xfb20 alt ayin
+ { 0x5d0, 0, 0 }, // 0xfb21 alt alef
+ { 0x5d3, 0, 0 }, // 0xfb22 alt dalet
+ { 0x5d4, 0, 0 }, // 0xfb23 alt he
+ { 0x5db, 0, 0 }, // 0xfb24 alt kaf
+ { 0x5dc, 0, 0 }, // 0xfb25 alt lamed
+ { 0x5dd, 0, 0 }, // 0xfb26 alt mem-sofit
+ { 0x5e8, 0, 0 }, // 0xfb27 alt resh
+ { 0x5ea, 0, 0 }, // 0xfb28 alt tav
+ { '+', 0, 0 }, // 0xfb29 alt plus
+ { 0x5e9, 0x5c1, 0 }, // 0xfb2a shin+shin-dot
+ { 0x5e9, 0x5c2, 0 }, // 0xfb2b shin+sin-dot
+ { 0x5e9, 0x5c1, 0x5bc }, // 0xfb2c shin+shin-dot+dagesh
+ { 0x5e9, 0x5c2, 0x5bc }, // 0xfb2d shin+sin-dot+dagesh
+ { 0x5d0, 0x5b7, 0 }, // 0xfb2e alef+patah
+ { 0x5d0, 0x5b8, 0 }, // 0xfb2f alef+qamats
+ { 0x5d0, 0x5b4, 0 }, // 0xfb30 alef+hiriq
+ { 0x5d1, 0x5bc, 0 }, // 0xfb31 bet+dagesh
+ { 0x5d2, 0x5bc, 0 }, // 0xfb32 gimel+dagesh
+ { 0x5d3, 0x5bc, 0 }, // 0xfb33 dalet+dagesh
+ { 0x5d4, 0x5bc, 0 }, // 0xfb34 he+dagesh
+ { 0x5d5, 0x5bc, 0 }, // 0xfb35 vav+dagesh
+ { 0x5d6, 0x5bc, 0 }, // 0xfb36 zayin+dagesh
+ { 0xfb37, 0, 0 }, // 0xfb37 -- UNUSED
+ { 0x5d8, 0x5bc, 0 }, // 0xfb38 tet+dagesh
+ { 0x5d9, 0x5bc, 0 }, // 0xfb39 yud+dagesh
+ { 0x5da, 0x5bc, 0 }, // 0xfb3a kaf sofit+dagesh
+ { 0x5db, 0x5bc, 0 }, // 0xfb3b kaf+dagesh
+ { 0x5dc, 0x5bc, 0 }, // 0xfb3c lamed+dagesh
+ { 0xfb3d, 0, 0 }, // 0xfb3d -- UNUSED
+ { 0x5de, 0x5bc, 0 }, // 0xfb3e mem+dagesh
+ { 0xfb3f, 0, 0 }, // 0xfb3f -- UNUSED
+ { 0x5e0, 0x5bc, 0 }, // 0xfb40 nun+dagesh
+ { 0x5e1, 0x5bc, 0 }, // 0xfb41 samech+dagesh
+ { 0xfb42, 0, 0 }, // 0xfb42 -- UNUSED
+ { 0x5e3, 0x5bc, 0 }, // 0xfb43 pe sofit+dagesh
+ { 0x5e4, 0x5bc, 0 }, // 0xfb44 pe+dagesh
+ { 0xfb45, 0, 0 }, // 0xfb45 -- UNUSED
+ { 0x5e6, 0x5bc, 0 }, // 0xfb46 tsadi+dagesh
+ { 0x5e7, 0x5bc, 0 }, // 0xfb47 qof+dagesh
+ { 0x5e8, 0x5bc, 0 }, // 0xfb48 resh+dagesh
+ { 0x5e9, 0x5bc, 0 }, // 0xfb49 shin+dagesh
+ { 0x5ea, 0x5bc, 0 }, // 0xfb4a tav+dagesh
+ { 0x5d5, 0x5b9, 0 }, // 0xfb4b vav+holam
+ { 0x5d1, 0x5bf, 0 }, // 0xfb4c bet+rafe
+ { 0x5db, 0x5bf, 0 }, // 0xfb4d kaf+rafe
+ { 0x5e4, 0x5bf, 0 }, // 0xfb4e pe+rafe
+ { 0x5d0, 0x5dc, 0 } // 0xfb4f alef-lamed
};
static void mb_decompose(int c, int *c1, int *c2, int *c3)
@@ -6413,7 +1417,7 @@ static int cstrncmp(char_u *s1, char_u *s2, int *n)
// if it failed and it's utf8 and we want to combineignore:
if (result != 0 && rex.reg_icombine) {
- char_u *str1, *str2;
+ char_u *str1, *str2;
int c1, c2, c11, c12;
int junk;
@@ -6441,20 +1445,61 @@ static int cstrncmp(char_u *s1, char_u *s2, int *n)
}
}
result = c2 - c1;
- if (result == 0)
+ if (result == 0) {
*n = (int)(str2 - s2);
+ }
}
return result;
}
+/// Wrapper around strchr which accounts for case-insensitive searches and
+/// non-ASCII characters.
+///
+/// This function is used a lot for simple searches, keep it fast!
+///
+/// @param s string to search
+/// @param c character to find in @a s
+///
+/// @return NULL if no match, otherwise pointer to the position in @a s
+static inline char_u *cstrchr(const char_u *const s, const int c)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
+ FUNC_ATTR_ALWAYS_INLINE
+{
+ if (!rex.reg_ic) {
+ return (char_u *)vim_strchr((char *)s, c);
+ }
+
+ // Use folded case for UTF-8, slow! For ASCII use libc strpbrk which is
+ // expected to be highly optimized.
+ if (c > 0x80) {
+ const int folded_c = utf_fold(c);
+ for (const char_u *p = s; *p != NUL; p += utfc_ptr2len((char *)p)) {
+ if (utf_fold(utf_ptr2char((char *)p)) == folded_c) {
+ return (char_u *)p;
+ }
+ }
+ return NULL;
+ }
+
+ int cc;
+ if (ASCII_ISUPPER(c)) {
+ cc = TOLOWER_ASC(c);
+ } else if (ASCII_ISLOWER(c)) {
+ cc = TOUPPER_ASC(c);
+ } else {
+ return (char_u *)vim_strchr((char *)s, c);
+ }
+
+ char tofind[] = { (char)c, (char)cc, NUL };
+ return (char_u *)strpbrk((const char *)s, tofind);
+}
+
////////////////////////////////////////////////////////////////
// regsub stuff //
////////////////////////////////////////////////////////////////
-/* This stuff below really confuses cc on an SGI -- webb */
-
-
+// This stuff below really confuses cc on an SGI -- webb
static fptr_T do_upper(int *d, int c)
{
@@ -6496,24 +1541,24 @@ static fptr_T do_Lower(int *d, int c)
*
* The tildes are parsed once before the first call to vim_regsub().
*/
-char_u *regtilde(char_u *source, int magic)
+char_u *regtilde(char_u *source, int magic, bool preview)
{
- char_u *newsub = source;
- char_u *tmpsub;
- char_u *p;
+ char_u *newsub = source;
+ char_u *tmpsub;
+ char_u *p;
int len;
int prevlen;
for (p = newsub; *p; ++p) {
if ((*p == '~' && magic) || (*p == '\\' && *(p + 1) == '~' && !magic)) {
if (reg_prev_sub != NULL) {
- /* length = len(newsub) - 1 + len(prev_sub) + 1 */
+ // length = len(newsub) - 1 + len(prev_sub) + 1
prevlen = (int)STRLEN(reg_prev_sub);
tmpsub = xmalloc(STRLEN(newsub) + prevlen);
- /* copy prefix */
- len = (int)(p - newsub); /* not including ~ */
+ // copy prefix
+ len = (int)(p - newsub); // not including ~
memmove(tmpsub, newsub, (size_t)len);
- /* interpret tilde */
+ // interpret tilde
memmove(tmpsub + len, reg_prev_sub, (size_t)prevlen);
// copy postfix
if (!magic) {
@@ -6521,28 +1566,35 @@ char_u *regtilde(char_u *source, int magic)
}
STRCPY(tmpsub + len + prevlen, p + 1);
- if (newsub != source) /* already allocated newsub */
+ if (newsub != source) { // already allocated newsub
xfree(newsub);
+ }
newsub = tmpsub;
p = newsub + len + prevlen;
- } else if (magic)
- STRMOVE(p, p + 1); /* remove '~' */
- else
- STRMOVE(p, p + 2); /* remove '\~' */
- --p;
+ } else if (magic) {
+ STRMOVE(p, p + 1); // remove '~'
+ } else {
+ STRMOVE(p, p + 2); // remove '\~'
+ }
+ p--;
} else {
if (*p == '\\' && p[1]) { // skip escaped characters
p++;
}
- p += utfc_ptr2len(p) - 1;
+ p += utfc_ptr2len((char *)p) - 1;
+ }
+ }
+
+ // Only change reg_prev_sub when not previewing.
+ if (!preview) {
+ xfree(reg_prev_sub);
+ if (newsub != source) { // newsub was allocated, just keep it
+ reg_prev_sub = newsub;
+ } else { // no ~ found, need to save newsub
+ reg_prev_sub = vim_strsave(newsub);
}
}
- xfree(reg_prev_sub);
- if (newsub != source) /* newsub was allocated, just keep it */
- reg_prev_sub = newsub;
- else /* no ~ found, need to save newsub */
- reg_prev_sub = vim_strsave(newsub);
return newsub;
}
@@ -6563,8 +1615,7 @@ static regsubmatch_T rsm; // can only be used when can_f_submatch is true
/// Put the submatches in "argv[argskip]" which is a list passed into
/// call_func() by vim_regsub_both().
-static int fill_submatch_list(int argc FUNC_ATTR_UNUSED, typval_T *argv,
- int argskip, int argcount)
+static int fill_submatch_list(int argc FUNC_ATTR_UNUSED, typval_T *argv, int argskip, int argcount)
FUNC_ATTR_NONNULL_ALL
{
typval_T *listarg = argv + argskip;
@@ -6587,7 +1638,7 @@ static int fill_submatch_list(int argc FUNC_ATTR_UNUSED, typval_T *argv,
s = vim_strnsave(s, rsm.sm_match->endp[i] - s);
}
TV_LIST_ITEM_TV(li)->v_type = VAR_STRING;
- TV_LIST_ITEM_TV(li)->vval.v_string = s;
+ TV_LIST_ITEM_TV(li)->vval.v_string = (char *)s;
li = TV_LIST_ITEM_NEXT(argv->vval.v_list, li);
}
return argskip + 1;
@@ -6603,21 +1654,22 @@ static void clear_submatch_list(staticList10_T *sl)
/// vim_regsub() - perform substitutions after a vim_regexec() or
/// vim_regexec_multi() match.
///
-/// If "copy" is true really copy into "dest".
-/// If "copy" is false nothing is copied, this is just to find out the length
-/// of the result.
+/// If "flags" has REGSUB_COPY really copy into "dest[destlen]".
+/// Oterwise nothing is copied, only compue the length of the result.
///
-/// If "backslash" is true, a backslash will be removed later, need to double
-/// them to keep them, and insert a backslash before a CR to avoid it being
-/// replaced with a line break later.
+/// If "flags" has REGSUB_MAGIC then behave like 'magic' is set.
+///
+/// If "flags" has REGSUB_BACKSLASH a backslash will be removed later, need to
+/// double them to keep them, and insert a backslash before a CR to avoid it
+/// being replaced with a line break later.
///
/// Note: The matched text must not change between the call of
/// vim_regexec()/vim_regexec_multi() and vim_regsub()! It would make the back
/// references invalid!
///
/// Returns the size of the replacement, including terminating NUL.
-int vim_regsub(regmatch_T *rmp, char_u *source, typval_T *expr, char_u *dest,
- int copy, int magic, int backslash)
+int vim_regsub(regmatch_T *rmp, char_u *source, typval_T *expr, char_u *dest, int destlen,
+ int flags)
{
regexec_T rex_save;
bool rex_in_use_save = rex_in_use;
@@ -6633,7 +1685,7 @@ int vim_regsub(regmatch_T *rmp, char_u *source, typval_T *expr, char_u *dest,
rex.reg_maxline = 0;
rex.reg_buf = curbuf;
rex.reg_line_lbr = true;
- int result = vim_regsub_both(source, expr, dest, copy, magic, backslash);
+ int result = vim_regsub_both(source, expr, dest, destlen, flags);
rex_in_use = rex_in_use_save;
if (rex_in_use) {
@@ -6643,7 +1695,8 @@ int vim_regsub(regmatch_T *rmp, char_u *source, typval_T *expr, char_u *dest,
return result;
}
-int vim_regsub_multi(regmmatch_T *rmp, linenr_T lnum, char_u *source, char_u *dest, int copy, int magic, int backslash)
+int vim_regsub_multi(regmmatch_T *rmp, linenr_T lnum, char_u *source, char_u *dest, int destlen,
+ int flags)
{
regexec_T rex_save;
bool rex_in_use_save = rex_in_use;
@@ -6660,7 +1713,7 @@ int vim_regsub_multi(regmmatch_T *rmp, linenr_T lnum, char_u *source, char_u *de
rex.reg_firstlnum = lnum;
rex.reg_maxline = curbuf->b_ml.ml_line_count - lnum;
rex.reg_line_lbr = false;
- int result = vim_regsub_both(source, NULL, dest, copy, magic, backslash);
+ int result = vim_regsub_both(source, NULL, dest, destlen, flags);
rex_in_use = rex_in_use_save;
if (rex_in_use) {
@@ -6670,32 +1723,47 @@ int vim_regsub_multi(regmmatch_T *rmp, linenr_T lnum, char_u *source, char_u *de
return result;
}
-static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
- int copy, int magic, int backslash)
+// When nesting more than a couple levels it's probably a mistake.
+#define MAX_REGSUB_NESTING 4
+static char_u *eval_result[MAX_REGSUB_NESTING] = { NULL, NULL, NULL, NULL };
+
+#if defined(EXITFREE)
+void free_resub_eval_result(void)
+{
+ for (int i = 0; i < MAX_REGSUB_NESTING; i++) {
+ XFREE_CLEAR(eval_result[i]);
+ }
+}
+#endif
+
+static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int destlen, int flags)
{
- char_u *src;
- char_u *dst;
- char_u *s;
+ char_u *src;
+ char_u *dst;
+ char_u *s;
int c;
int cc;
int no = -1;
fptr_T func_all = (fptr_T)NULL;
fptr_T func_one = (fptr_T)NULL;
- linenr_T clnum = 0; /* init for GCC */
- int len = 0; /* init for GCC */
- static char_u *eval_result = NULL;
-
- // We need to keep track of how many backslashes we escape, so that the byte
- // counts for `extmark_splice` are correct.
- int num_escaped = 0;
+ linenr_T clnum = 0; // init for GCC
+ int len = 0; // init for GCC
+ static int nesting = 0;
+ bool copy = flags & REGSUB_COPY;
// Be paranoid...
if ((source == NULL && expr == NULL) || dest == NULL) {
emsg(_(e_null));
return 0;
}
- if (prog_magic_wrong())
+ if (prog_magic_wrong()) {
+ return 0;
+ }
+ if (nesting == MAX_REGSUB_NESTING) {
+ emsg(_(e_substitute_nesting_too_deep));
return 0;
+ }
+ int nested = nesting;
src = source;
dst = dest;
@@ -6703,19 +1771,20 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
if (expr != NULL || (source[0] == '\\' && source[1] == '=')) {
// To make sure that the length doesn't change between checking the
// length and copying the string, and to speed up things, the
- // resulting string is saved from the call with "copy" == false to the
- // call with "copy" == true.
+ // resulting string is saved from the call with
+ // "flags & REGSUB_COPY" == 0 to the call with
+ // "flags & REGSUB_COPY" != 0.
if (copy) {
- if (eval_result != NULL) {
- STRCPY(dest, eval_result);
- dst += STRLEN(eval_result);
- XFREE_CLEAR(eval_result);
+ if (eval_result[nested] != NULL) {
+ STRCPY(dest, eval_result[nested]);
+ dst += STRLEN(eval_result[nested]);
+ XFREE_CLEAR(eval_result[nested]);
}
} else {
const bool prev_can_f_submatch = can_f_submatch;
regsubmatch_T rsm_save;
- xfree(eval_result);
+ XFREE_CLEAR(eval_result[nested]);
// The expression may contain substitute(), which calls us
// recursively. Make sure submatch() gets the text from the first
@@ -6730,6 +1799,11 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
rsm.sm_maxline = rex.reg_maxline;
rsm.sm_line_lbr = rex.reg_line_lbr;
+ // Although unlikely, it is possible that the expression invokes a
+ // substitute command (it might fail, but still). Therefore keep
+ // an array of eval results.
+ nesting++;
+
if (expr != NULL) {
typval_T argv[2];
typval_T rettv;
@@ -6742,14 +1816,14 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
funcexe.argv_func = fill_submatch_list;
funcexe.evaluate = true;
if (expr->v_type == VAR_FUNC) {
- s = expr->vval.v_string;
- call_func(s, -1, &rettv, 1, argv, &funcexe);
+ s = (char_u *)expr->vval.v_string;
+ call_func((char *)s, -1, &rettv, 1, argv, &funcexe);
} else if (expr->v_type == VAR_PARTIAL) {
partial_T *partial = expr->vval.v_partial;
- s = partial_name(partial);
+ s = (char_u *)partial_name(partial);
funcexe.partial = partial;
- call_func(s, -1, &rettv, 1, argv, &funcexe);
+ call_func((char *)s, -1, &rettv, 1, argv, &funcexe);
}
if (tv_list_len(&matchList.sl_list) > 0) {
// fill_submatch_list() was called.
@@ -6757,23 +1831,24 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
}
if (rettv.v_type == VAR_UNKNOWN) {
// something failed, no need to report another error
- eval_result = NULL;
+ eval_result[nested] = NULL;
} else {
char buf[NUMBUFLEN];
- eval_result = (char_u *)tv_get_string_buf_chk(&rettv, buf);
- if (eval_result != NULL) {
- eval_result = vim_strsave(eval_result);
+ eval_result[nested] = (char_u *)tv_get_string_buf_chk(&rettv, buf);
+ if (eval_result[nested] != NULL) {
+ eval_result[nested] = vim_strsave(eval_result[nested]);
}
}
tv_clear(&rettv);
} else {
- eval_result = eval_to_string(source + 2, NULL, true);
+ eval_result[nested] = (char_u *)eval_to_string((char *)source + 2, NULL, true);
}
+ nesting--;
- if (eval_result != NULL) {
+ if (eval_result[nested] != NULL) {
int had_backslash = false;
- for (s = eval_result; *s != NUL; MB_PTR_ADV(s)) {
+ for (s = eval_result[nested]; *s != NUL; MB_PTR_ADV(s)) {
// Change NL to CR, so that it becomes a line break,
// unless called from vim_regexec_nl().
// Skip over a backslashed character.
@@ -6793,14 +1868,14 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
had_backslash = true;
}
}
- if (had_backslash && backslash) {
- /* Backslashes will be consumed, need to double them. */
- s = vim_strsave_escaped(eval_result, (char_u *)"\\");
- xfree(eval_result);
- eval_result = s;
+ if (had_backslash && (flags & REGSUB_BACKSLASH)) {
+ // Backslashes will be consumed, need to double them.
+ s = vim_strsave_escaped(eval_result[nested], (char_u *)"\\");
+ xfree(eval_result[nested]);
+ eval_result[nested] = s;
}
- dst += STRLEN(eval_result);
+ dst += STRLEN(eval_result[nested]);
}
can_f_submatch = prev_can_f_submatch;
@@ -6808,36 +1883,45 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
rsm = rsm_save;
}
}
- } else
+ } else {
while ((c = *src++) != NUL) {
- if (c == '&' && magic)
+ if (c == '&' && (flags & REGSUB_MAGIC)) {
no = 0;
- else if (c == '\\' && *src != NUL) {
- if (*src == '&' && !magic) {
- ++src;
+ } else if (c == '\\' && *src != NUL) {
+ if (*src == '&' && !(flags & REGSUB_MAGIC)) {
+ src++;
no = 0;
} else if ('0' <= *src && *src <= '9') {
no = *src++ - '0';
- } else if (vim_strchr((char_u *)"uUlLeE", *src)) {
+ } else if (vim_strchr("uUlLeE", *src)) {
switch (*src++) {
- case 'u': func_one = (fptr_T)do_upper;
+ case 'u':
+ func_one = (fptr_T)do_upper;
continue;
- case 'U': func_all = (fptr_T)do_Upper;
+ case 'U':
+ func_all = (fptr_T)do_Upper;
continue;
- case 'l': func_one = (fptr_T)do_lower;
+ case 'l':
+ func_one = (fptr_T)do_lower;
continue;
- case 'L': func_all = (fptr_T)do_Lower;
+ case 'L':
+ func_all = (fptr_T)do_Lower;
continue;
case 'e':
- case 'E': func_one = func_all = (fptr_T)NULL;
+ case 'E':
+ func_one = func_all = (fptr_T)NULL;
continue;
}
}
}
- if (no < 0) { /* Ordinary character. */
+ if (no < 0) { // Ordinary character.
if (c == K_SPECIAL && src[0] != NUL && src[1] != NUL) {
- /* Copy a special key as-is. */
+ // Copy a special key as-is.
if (copy) {
+ if (dst + 3 > dest + destlen) {
+ iemsg("vim_regsub_both(): not enough space");
+ return 0;
+ }
*dst++ = c;
*dst++ = *src++;
*dst++ = *src++;
@@ -6851,19 +1935,26 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
if (c == '\\' && *src != NUL) {
// Check for abbreviations -- webb
switch (*src) {
- case 'r': c = CAR; ++src; break;
- case 'n': c = NL; ++src; break;
- case 't': c = TAB; ++src; break;
+ case 'r':
+ c = CAR; ++src; break;
+ case 'n':
+ c = NL; ++src; break;
+ case 't':
+ c = TAB; ++src; break;
// Oh no! \e already has meaning in subst pat :-(
// case 'e': c = ESC; ++src; break;
- case 'b': c = Ctrl_H; ++src; break;
+ case 'b':
+ c = Ctrl_H; ++src; break;
// If "backslash" is true the backslash will be removed
// later. Used to insert a literal CR.
default:
- if (backslash) {
- num_escaped += 1;
+ if (flags & REGSUB_BACKSLASH) {
if (copy) {
+ if (dst + 1 > dest + destlen) {
+ iemsg("vim_regsub_both(): not enough space");
+ return 0;
+ }
*dst = '\\';
}
dst++;
@@ -6871,7 +1962,7 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
c = *src++;
}
} else {
- c = utf_ptr2char(src - 1);
+ c = utf_ptr2char((char *)src - 1);
}
// Write to buffer, if copy is set.
if (func_one != NULL) {
@@ -6883,18 +1974,27 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
cc = c;
}
- int totlen = utfc_ptr2len(src - 1);
+ int totlen = utfc_ptr2len((char *)src - 1);
+ int charlen = utf_char2len(cc);
if (copy) {
- utf_char2bytes(cc, dst);
+ if (dst + charlen > dest + destlen) {
+ iemsg("vim_regsub_both(): not enough space");
+ return 0;
+ }
+ utf_char2bytes(cc, (char *)dst);
}
- dst += utf_char2len(cc) - 1;
- int clen = utf_ptr2len(src - 1);
+ dst += charlen - 1;
+ int clen = utf_ptr2len((char *)src - 1);
// If the character length is shorter than "totlen", there
// are composing characters; copy them as-is.
if (clen < totlen) {
if (copy) {
+ if (dst + totlen - clen > dest + destlen) {
+ iemsg("vim_regsub_both(): not enough space");
+ return 0;
+ }
memmove(dst + 1, src - 1 + clen, (size_t)(totlen - clen));
}
dst += totlen - clen;
@@ -6924,13 +2024,17 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
}
}
if (s != NULL) {
- for (;; ) {
+ for (;;) {
if (len == 0) {
if (REG_MULTI) {
if (rex.reg_mmatch->endpos[no].lnum == clnum) {
break;
}
if (copy) {
+ if (dst + 1 > dest + destlen) {
+ iemsg("vim_regsub_both(): not enough space");
+ return 0;
+ }
*dst = CAR;
}
dst++;
@@ -6949,43 +2053,52 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
}
goto exit;
} else {
- if (backslash && (*s == CAR || *s == '\\')) {
- /*
- * Insert a backslash in front of a CR, otherwise
- * it will be replaced by a line break.
- * Number of backslashes will be halved later,
- * double them here.
- */
+ if ((flags & REGSUB_BACKSLASH) && (*s == CAR || *s == '\\')) {
+ // Insert a backslash in front of a CR, otherwise
+ // it will be replaced by a line break.
+ // Number of backslashes will be halved later,
+ // double them here.
if (copy) {
+ if (dst + 2 > dest + destlen) {
+ iemsg("vim_regsub_both(): not enough space");
+ return 0;
+ }
dst[0] = '\\';
dst[1] = *s;
}
dst += 2;
} else {
- c = utf_ptr2char(s);
+ c = utf_ptr2char((char *)s);
- if (func_one != (fptr_T)NULL)
- /* Turbo C complains without the typecast */
+ if (func_one != (fptr_T)NULL) {
+ // Turbo C complains without the typecast
func_one = (fptr_T)(func_one(&cc, c));
- else if (func_all != (fptr_T)NULL)
- /* Turbo C complains without the typecast */
+ } else if (func_all != (fptr_T)NULL) {
+ // Turbo C complains without the typecast
func_all = (fptr_T)(func_all(&cc, c));
- else /* just copy */
+ } else { // just copy
cc = c;
+ }
{
int l;
+ int charlen;
// Copy composing characters separately, one
// at a time.
- l = utf_ptr2len(s) - 1;
+ l = utf_ptr2len((char *)s) - 1;
s += l;
len -= l;
+ charlen = utf_char2len(cc);
if (copy) {
- utf_char2bytes(cc, dst);
+ if (dst + charlen > dest + destlen) {
+ iemsg("vim_regsub_both(): not enough space");
+ return 0;
+ }
+ utf_char2bytes(cc, (char *)dst);
}
- dst += utf_char2len(cc) - 1;
+ dst += charlen - 1;
}
dst++;
}
@@ -6998,14 +2111,15 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
no = -1;
}
}
- if (copy)
+ }
+ if (copy) {
*dst = NUL;
+ }
exit:
- return (int)((dst - dest) + 1 - num_escaped);
+ return (int)((dst - dest) + 1);
}
-
/*
* Call reg_getline() with the line numbers from the submatch. If a
* substitute() was used the reg_maxline and other values have been
@@ -7034,13 +2148,14 @@ static char_u *reg_getline_submatch(linenr_T lnum)
*/
char_u *reg_submatch(int no)
{
- char_u *retval = NULL;
- char_u *s;
+ char_u *retval = NULL;
+ char_u *s;
int round;
linenr_T lnum;
- if (!can_f_submatch || no < 0)
+ if (!can_f_submatch || no < 0) {
return NULL;
+ }
if (rsm.sm_match == NULL) {
ssize_t len;
@@ -7079,12 +2194,14 @@ char_u *reg_submatch(int no)
lnum++;
while (lnum < rsm.sm_mmatch->endpos[no].lnum) {
s = reg_getline_submatch(lnum++);
- if (round == 2)
+ if (round == 2) {
STRCPY(retval + len, s);
+ }
len += STRLEN(s);
- if (round == 2)
+ if (round == 2) {
retval[len] = '\n';
- ++len;
+ }
+ len++;
}
if (round == 2) {
STRNCPY(retval + len, reg_getline_submatch(lnum),
@@ -7166,28 +2283,26 @@ list_T *reg_submatch_list(int no)
return list;
}
+// XXX Do not allow headers generator to catch definitions from regexp_nfa.c
+#ifndef DO_NOT_DEFINE_EMPTY_ATTRIBUTES
+# include "nvim/regexp_bt.c"
+# include "nvim/regexp_nfa.c"
+#endif
+
static regengine_T bt_regengine =
{
bt_regcomp,
bt_regfree,
bt_regexec_nl,
bt_regexec_multi,
- (char_u *)""
};
-
-// XXX Do not allow headers generator to catch definitions from regexp_nfa.c
-#ifndef DO_NOT_DEFINE_EMPTY_ATTRIBUTES
-# include "nvim/regexp_nfa.c"
-#endif
-
static regengine_T nfa_regengine =
{
nfa_regcomp,
nfa_regfree,
nfa_regexec_nl,
nfa_regexec_multi,
- (char_u *)""
};
// Which regexp engine to use? Needed for vim_regcomp().
@@ -7208,15 +2323,14 @@ static char_u regname[][30] = {
* Use vim_regfree() to free the memory.
* Returns NULL for an error.
*/
-regprog_T *vim_regcomp(char_u *expr_arg, int re_flags)
+regprog_T *vim_regcomp(char *expr_arg, int re_flags)
{
- regprog_T *prog = NULL;
- char_u *expr = expr_arg;
- int save_called_emsg;
+ regprog_T *prog = NULL;
+ char_u *expr = (char_u *)expr_arg;
regexp_engine = p_re;
- /* Check for prefix "\%#=", that sets the regexp engine */
+ // Check for prefix "\%#=", that sets the regexp engine
if (STRNCMP(expr, "\\%#=", 4) == 0) {
int newengine = expr[4] - '0';
@@ -7231,8 +2345,7 @@ regprog_T *vim_regcomp(char_u *expr_arg, int re_flags)
regname[newengine]);
#endif
} else {
- emsg(_(
- "E864: \\%#= can only be followed by 0, 1, or 2. The automatic engine will be used "));
+ emsg(_("E864: \\%#= can only be followed by 0, 1, or 2. The automatic engine will be used "));
regexp_engine = AUTOMATIC_ENGINE;
}
}
@@ -7246,11 +2359,10 @@ regprog_T *vim_regcomp(char_u *expr_arg, int re_flags)
//
// First try the NFA engine, unless backtracking was requested.
//
- save_called_emsg = called_emsg;
- called_emsg = false;
+ const int called_emsg_before = called_emsg;
if (regexp_engine != BACKTRACKING_ENGINE) {
prog = nfa_regengine.regcomp(expr,
- re_flags + (regexp_engine == AUTOMATIC_ENGINE ? RE_AUTO : 0));
+ re_flags + (regexp_engine == AUTOMATIC_ENGINE ? RE_AUTO : 0));
} else {
prog = bt_regengine.regcomp(expr, re_flags);
}
@@ -7274,13 +2386,12 @@ regprog_T *vim_regcomp(char_u *expr_arg, int re_flags)
// also fails for patterns that it can't handle well but are still valid
// patterns, thus a retry should work.
// But don't try if an error message was given.
- if (regexp_engine == AUTOMATIC_ENGINE && !called_emsg) {
+ if (regexp_engine == AUTOMATIC_ENGINE && called_emsg == called_emsg_before) {
regexp_engine = BACKTRACKING_ENGINE;
report_re_switch(expr);
prog = bt_regengine.regcomp(expr, re_flags);
}
}
- called_emsg |= save_called_emsg;
if (prog != NULL) {
// Store the info needed to call regcomp() again when the engine turns out
@@ -7297,10 +2408,22 @@ regprog_T *vim_regcomp(char_u *expr_arg, int re_flags)
*/
void vim_regfree(regprog_T *prog)
{
- if (prog != NULL)
+ if (prog != NULL) {
prog->engine->regfree(prog);
+ }
+}
+
+#if defined(EXITFREE)
+void free_regexp_stuff(void)
+{
+ ga_clear(&regstack);
+ ga_clear(&backpos);
+ xfree(reg_tofree);
+ xfree(reg_prev_sub);
}
+#endif
+
static void report_re_switch(char_u *pat)
{
if (p_verbose > 0) {
@@ -7311,8 +2434,8 @@ static void report_re_switch(char_u *pat)
}
}
-/// Matches a regexp against a string.
-/// "rmp->regprog" is a compiled regexp as returned by vim_regcomp().
+/// Match a regexp against a string.
+/// "rmp->regprog" must be a compiled regexp as returned by vim_regcomp().
/// Note: "rmp->regprog" may be freed and changed.
/// Uses curbuf for line count and 'iskeyword'.
/// When "nl" is true consider a "\n" in "line" to be a line break.
@@ -7323,8 +2446,7 @@ static void report_re_switch(char_u *pat)
/// @param nl
///
/// @return true if there is a match, false if not.
-static bool vim_regexec_string(regmatch_T *rmp, char_u *line, colnr_T col,
- bool nl)
+static bool vim_regexec_string(regmatch_T *rmp, char_u *line, colnr_T col, bool nl)
{
regexec_T rex_save;
bool rex_in_use_save = rex_in_use;
@@ -7360,7 +2482,7 @@ static bool vim_regexec_string(regmatch_T *rmp, char_u *line, colnr_T col,
p_re = BACKTRACKING_ENGINE;
vim_regfree(rmp->regprog);
report_re_switch(pat);
- rmp->regprog = vim_regcomp(pat, re_flags);
+ rmp->regprog = vim_regcomp((char *)pat, re_flags);
if (rmp->regprog != NULL) {
rmp->regprog->re_in_use = true;
result = rmp->regprog->engine->regexec_nl(rmp, line, col, nl);
@@ -7381,8 +2503,7 @@ static bool vim_regexec_string(regmatch_T *rmp, char_u *line, colnr_T col,
// Note: "*prog" may be freed and changed.
// Return true if there is a match, false if not.
-bool vim_regexec_prog(regprog_T **prog, bool ignore_case, char_u *line,
- colnr_T col)
+bool vim_regexec_prog(regprog_T **prog, bool ignore_case, char_u *line, colnr_T col)
{
regmatch_T regmatch = { .regprog = *prog, .rm_ic = ignore_case };
bool r = vim_regexec_string(&regmatch, line, col, false);
@@ -7392,9 +2513,9 @@ bool vim_regexec_prog(regprog_T **prog, bool ignore_case, char_u *line,
// Note: "rmp->regprog" may be freed and changed.
// Return true if there is a match, false if not.
-bool vim_regexec(regmatch_T *rmp, char_u *line, colnr_T col)
+bool vim_regexec(regmatch_T *rmp, char *line, colnr_T col)
{
- return vim_regexec_string(rmp, line, col, false);
+ return vim_regexec_string(rmp, (char_u *)line, col, false);
}
// Like vim_regexec(), but consider a "\n" in "line" to be a line break.
@@ -7410,17 +2531,17 @@ bool vim_regexec_nl(regmatch_T *rmp, char_u *line, colnr_T col)
/// Note: "rmp->regprog" may be freed and changed, even set to NULL.
/// Uses curbuf for line count and 'iskeyword'.
///
-/// Return zero if there is no match. Return number of lines contained in the
-/// match otherwise.
-long vim_regexec_multi(
- regmmatch_T *rmp,
- win_T *win, // window in which to search or NULL
- buf_T *buf, // buffer in which to search
- linenr_T lnum, // nr of line to start looking for match
- colnr_T col, // column to start looking for match
- proftime_T *tm, // timeout limit or NULL
- int *timed_out // flag is set when timeout limit reached
-)
+/// @param win window in which to search or NULL
+/// @param buf buffer in which to search
+/// @param lnum nr of line to start looking for match
+/// @param col column to start looking for match
+/// @param tm timeout limit or NULL
+/// @param timed_out flag is set when timeout limit reached
+///
+/// @return zero if there is no match. Return number of lines contained in the
+/// match otherwise.
+long vim_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf, linenr_T lnum, colnr_T col,
+ proftime_T *tm, int *timed_out)
FUNC_ATTR_NONNULL_ARG(1)
{
regexec_T rex_save;
@@ -7451,15 +2572,22 @@ long vim_regexec_multi(
char_u *pat = vim_strsave(((nfa_regprog_T *)rmp->regprog)->pattern);
p_re = BACKTRACKING_ENGINE;
- vim_regfree(rmp->regprog);
+ regprog_T *prev_prog = rmp->regprog;
+
report_re_switch(pat);
// checking for \z misuse was already done when compiling for NFA,
// allow all here
reg_do_extmatch = REX_ALL;
- rmp->regprog = vim_regcomp(pat, re_flags);
+ rmp->regprog = vim_regcomp((char *)pat, re_flags);
reg_do_extmatch = 0;
- if (rmp->regprog != NULL) {
+ if (rmp->regprog == NULL) {
+ // Somehow compiling the pattern failed now, put back the
+ // previous one to avoid "regprog" becoming NULL.
+ rmp->regprog = prev_prog;
+ } else {
+ vim_regfree(prev_prog);
+
rmp->regprog->re_in_use = true;
result = rmp->regprog->engine->regexec_multi(rmp, win, buf, lnum, col,
tm, timed_out);
diff --git a/src/nvim/regexp.h b/src/nvim/regexp.h
index 9527afed58..085f78af54 100644
--- a/src/nvim/regexp.h
+++ b/src/nvim/regexp.h
@@ -19,6 +19,8 @@
// regexp.c
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "regexp.h.generated.h"
+
+# include "regexp_bt.h.generated.h"
#endif
#endif // NVIM_REGEXP_H
diff --git a/src/nvim/regexp_bt.c b/src/nvim/regexp_bt.c
new file mode 100644
index 0000000000..272429bb91
--- /dev/null
+++ b/src/nvim/regexp_bt.c
@@ -0,0 +1,5728 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+/*
+ *
+ * Backtracking regular expression implementation.
+ *
+ * This file is included in "regexp.c".
+ *
+ * NOTICE:
+ *
+ * This is NOT the original regular expression code as written by Henry
+ * Spencer. This code has been modified specifically for use with the VIM
+ * editor, and should not be used separately from Vim. If you want a good
+ * regular expression library, get the original code. The copyright notice
+ * that follows is from the original.
+ *
+ * END NOTICE
+ *
+ * Copyright (c) 1986 by University of Toronto.
+ * Written by Henry Spencer. Not derived from licensed software.
+ *
+ * Permission is granted to anyone to use this software for any
+ * purpose on any computer system, and to redistribute it freely,
+ * subject to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of
+ * this software, no matter how awful, even if they arise
+ * from defects in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either
+ * by explicit claim or by omission.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+ *
+ * Beware that some of this code is subtly aware of the way operator
+ * precedence is structured in regular expressions. Serious changes in
+ * regular-expression syntax might require a total rethink.
+ *
+ * Changes have been made by Tony Andrews, Olaf 'Rhialto' Seibert, Robert
+ * Webb, Ciaran McCreesh and Bram Moolenaar.
+ * Named character class support added by Walter Briscoe (1998 Jul 01)
+ */
+
+/*
+ * The "internal use only" fields in regexp_defs.h are present to pass info from
+ * compile to execute that permits the execute phase to run lots faster on
+ * simple cases. They are:
+ *
+ * regstart char that must begin a match; NUL if none obvious; Can be a
+ * multi-byte character.
+ * reganch is the match anchored (at beginning-of-line only)?
+ * regmust string (pointer into program) that match must include, or NULL
+ * regmlen length of regmust string
+ * regflags RF_ values or'ed together
+ *
+ * Regstart and reganch permit very fast decisions on suitable starting points
+ * for a match, cutting down the work a lot. Regmust permits fast rejection
+ * of lines that cannot possibly match. The regmust tests are costly enough
+ * that vim_regcomp() supplies a regmust only if the r.e. contains something
+ * potentially expensive (at present, the only such thing detected is * or +
+ * at the start of the r.e., which can involve a lot of backup). Regmlen is
+ * supplied because the test in vim_regexec() needs it and vim_regcomp() is
+ * computing it anyway.
+ */
+
+/*
+ * Structure for regexp "program". This is essentially a linear encoding
+ * of a nondeterministic finite-state machine (aka syntax charts or
+ * "railroad normal form" in parsing technology). Each node is an opcode
+ * plus a "next" pointer, possibly plus an operand. "Next" pointers of
+ * all nodes except BRANCH and BRACES_COMPLEX implement concatenation; a "next"
+ * pointer with a BRANCH on both ends of it is connecting two alternatives.
+ * (Here we have one of the subtle syntax dependencies: an individual BRANCH
+ * (as opposed to a collection of them) is never concatenated with anything
+ * because of operator precedence). The "next" pointer of a BRACES_COMPLEX
+ * node points to the node after the stuff to be repeated.
+ * The operand of some types of node is a literal string; for others, it is a
+ * node leading into a sub-FSM. In particular, the operand of a BRANCH node
+ * is the first node of the branch.
+ * (NB this is *not* a tree structure: the tail of the branch connects to the
+ * thing following the set of BRANCHes.)
+ *
+ * pattern is coded like:
+ *
+ * +-----------------+
+ * | V
+ * <aa>\|<bb> BRANCH <aa> BRANCH <bb> --> END
+ * | ^ | ^
+ * +------+ +----------+
+ *
+ *
+ * +------------------+
+ * V |
+ * <aa>* BRANCH BRANCH <aa> --> BACK BRANCH --> NOTHING --> END
+ * | | ^ ^
+ * | +---------------+ |
+ * +---------------------------------------------+
+ *
+ *
+ * +----------------------+
+ * V |
+ * <aa>\+ BRANCH <aa> --> BRANCH --> BACK BRANCH --> NOTHING --> END
+ * | | ^ ^
+ * | +-----------+ |
+ * +--------------------------------------------------+
+ *
+ *
+ * +-------------------------+
+ * V |
+ * <aa>\{} BRANCH BRACE_LIMITS --> BRACE_COMPLEX <aa> --> BACK END
+ * | | ^
+ * | +----------------+
+ * +-----------------------------------------------+
+ *
+ *
+ * <aa>\@!<bb> BRANCH NOMATCH <aa> --> END <bb> --> END
+ * | | ^ ^
+ * | +----------------+ |
+ * +--------------------------------+
+ *
+ * +---------+
+ * | V
+ * \z[abc] BRANCH BRANCH a BRANCH b BRANCH c BRANCH NOTHING --> END
+ * | | | | ^ ^
+ * | | | +-----+ |
+ * | | +----------------+ |
+ * | +---------------------------+ |
+ * +------------------------------------------------------+
+ *
+ * They all start with a BRANCH for "\|" alternatives, even when there is only
+ * one alternative.
+ */
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "nvim/garray.h"
+#include "nvim/regexp.h"
+
+/*
+ * The opcodes are:
+ */
+
+// definition number opnd? meaning
+#define END 0 // End of program or NOMATCH operand.
+#define BOL 1 // Match "" at beginning of line.
+#define EOL 2 // Match "" at end of line.
+#define BRANCH 3 // node Match this alternative, or the
+ // next...
+#define BACK 4 // Match "", "next" ptr points backward.
+#define EXACTLY 5 // str Match this string.
+#define NOTHING 6 // Match empty string.
+#define STAR 7 // node Match this (simple) thing 0 or more
+ // times.
+#define PLUS 8 // node Match this (simple) thing 1 or more
+ // times.
+#define MATCH 9 // node match the operand zero-width
+#define NOMATCH 10 // node check for no match with operand
+#define BEHIND 11 // node look behind for a match with operand
+#define NOBEHIND 12 // node look behind for no match with operand
+#define SUBPAT 13 // node match the operand here
+#define BRACE_SIMPLE 14 // node Match this (simple) thing between m and
+ // n times (\{m,n\}).
+#define BOW 15 // Match "" after [^a-zA-Z0-9_]
+#define EOW 16 // Match "" at [^a-zA-Z0-9_]
+#define BRACE_LIMITS 17 // nr nr define the min & max for BRACE_SIMPLE
+ // and BRACE_COMPLEX.
+#define NEWL 18 // Match line-break
+#define BHPOS 19 // End position for BEHIND or NOBEHIND
+
+// character classes: 20-48 normal, 50-78 include a line-break
+#define ADD_NL 30
+#define FIRST_NL ANY + ADD_NL
+#define ANY 20 // Match any one character.
+#define ANYOF 21 // str Match any character in this string.
+#define ANYBUT 22 // str Match any character not in this
+ // string.
+#define IDENT 23 // Match identifier char
+#define SIDENT 24 // Match identifier char but no digit
+#define KWORD 25 // Match keyword char
+#define SKWORD 26 // Match word char but no digit
+#define FNAME 27 // Match file name char
+#define SFNAME 28 // Match file name char but no digit
+#define PRINT 29 // Match printable char
+#define SPRINT 30 // Match printable char but no digit
+#define WHITE 31 // Match whitespace char
+#define NWHITE 32 // Match non-whitespace char
+#define DIGIT 33 // Match digit char
+#define NDIGIT 34 // Match non-digit char
+#define HEX 35 // Match hex char
+#define NHEX 36 // Match non-hex char
+#define OCTAL 37 // Match octal char
+#define NOCTAL 38 // Match non-octal char
+#define WORD 39 // Match word char
+#define NWORD 40 // Match non-word char
+#define HEAD 41 // Match head char
+#define NHEAD 42 // Match non-head char
+#define ALPHA 43 // Match alpha char
+#define NALPHA 44 // Match non-alpha char
+#define LOWER 45 // Match lowercase char
+#define NLOWER 46 // Match non-lowercase char
+#define UPPER 47 // Match uppercase char
+#define NUPPER 48 // Match non-uppercase char
+#define LAST_NL NUPPER + ADD_NL
+// -V:WITH_NL:560
+#define WITH_NL(op) ((op) >= FIRST_NL && (op) <= LAST_NL)
+
+#define MOPEN 80 // -89 Mark this point in input as start of
+ // \( … \) subexpr. MOPEN + 0 marks start of
+ // match.
+#define MCLOSE 90 // -99 Analogous to MOPEN. MCLOSE + 0 marks
+ // end of match.
+#define BACKREF 100 // -109 node Match same string again \1-\9.
+
+#define ZOPEN 110 // -119 Mark this point in input as start of
+ // \z( … \) subexpr.
+#define ZCLOSE 120 // -129 Analogous to ZOPEN.
+#define ZREF 130 // -139 node Match external submatch \z1-\z9
+
+#define BRACE_COMPLEX 140 // -149 node Match nodes between m & n times
+
+#define NOPEN 150 // Mark this point in input as start of
+ // \%( subexpr.
+#define NCLOSE 151 // Analogous to NOPEN.
+
+#define MULTIBYTECODE 200 // mbc Match one multi-byte character
+#define RE_BOF 201 // Match "" at beginning of file.
+#define RE_EOF 202 // Match "" at end of file.
+#define CURSOR 203 // Match location of cursor.
+
+#define RE_LNUM 204 // nr cmp Match line number
+#define RE_COL 205 // nr cmp Match column number
+#define RE_VCOL 206 // nr cmp Match virtual column number
+
+#define RE_MARK 207 // mark cmp Match mark position
+#define RE_VISUAL 208 // Match Visual area
+#define RE_COMPOSING 209 // any composing characters
+
+/*
+ * Flags to be passed up and down.
+ */
+#define HASWIDTH 0x1 // Known never to match null string.
+#define SIMPLE 0x2 // Simple enough to be STAR/PLUS operand.
+#define SPSTART 0x4 // Starts with * or +.
+#define HASNL 0x8 // Contains some \n.
+#define HASLOOKBH 0x10 // Contains "\@<=" or "\@<!".
+#define WORST 0 // Worst case.
+
+static int prevchr_len; ///< byte length of previous char
+static int num_complex_braces; ///< Complex \{...} count
+static char_u *regcode; ///< Code-emit pointer, or JUST_CALC_SIZE
+static long regsize; ///< Code size.
+static int reg_toolong; ///< true when offset out of range
+static char_u had_endbrace[NSUBEXP]; ///< flags, true if end of () found
+static long brace_min[10]; ///< Minimums for complex brace repeats
+static long brace_max[10]; ///< Maximums for complex brace repeats
+static int brace_count[10]; ///< Current counts for complex brace repeats
+static int one_exactly = false; ///< only do one char for EXACTLY
+
+// When making changes to classchars also change nfa_classcodes.
+static char_u *classchars = (char_u *)".iIkKfFpPsSdDxXoOwWhHaAlLuU";
+static int classcodes[] = {
+ ANY, IDENT, SIDENT, KWORD, SKWORD,
+ FNAME, SFNAME, PRINT, SPRINT,
+ WHITE, NWHITE, DIGIT, NDIGIT,
+ HEX, NHEX, OCTAL, NOCTAL,
+ WORD, NWORD, HEAD, NHEAD,
+ ALPHA, NALPHA, LOWER, NLOWER,
+ UPPER, NUPPER
+};
+
+/*
+ * When regcode is set to this value, code is not emitted and size is computed
+ * instead.
+ */
+#define JUST_CALC_SIZE ((char_u *)-1)
+
+// Values for rs_state in regitem_T.
+typedef enum regstate_E {
+ RS_NOPEN = 0, // NOPEN and NCLOSE
+ RS_MOPEN, // MOPEN + [0-9]
+ RS_MCLOSE, // MCLOSE + [0-9]
+ RS_ZOPEN, // ZOPEN + [0-9]
+ RS_ZCLOSE, // ZCLOSE + [0-9]
+ RS_BRANCH, // BRANCH
+ RS_BRCPLX_MORE, // BRACE_COMPLEX and trying one more match
+ RS_BRCPLX_LONG, // BRACE_COMPLEX and trying longest match
+ RS_BRCPLX_SHORT, // BRACE_COMPLEX and trying shortest match
+ RS_NOMATCH, // NOMATCH
+ RS_BEHIND1, // BEHIND / NOBEHIND matching rest
+ RS_BEHIND2, // BEHIND / NOBEHIND matching behind part
+ RS_STAR_LONG, // STAR/PLUS/BRACE_SIMPLE longest match
+ RS_STAR_SHORT, // STAR/PLUS/BRACE_SIMPLE shortest match
+} regstate_T;
+
+/*
+ * Structure used to save the current input state, when it needs to be
+ * restored after trying a match. Used by reg_save() and reg_restore().
+ * Also stores the length of "backpos".
+ */
+typedef struct {
+ union {
+ char_u *ptr; // rex.input pointer, for single-line regexp
+ lpos_T pos; // rex.input pos, for multi-line regexp
+ } rs_u;
+ int rs_len;
+} regsave_T;
+
+// struct to save start/end pointer/position in for \(\)
+typedef struct {
+ union {
+ char_u *ptr;
+ lpos_T pos;
+ } se_u;
+} save_se_T;
+
+// used for BEHIND and NOBEHIND matching
+typedef struct regbehind_S {
+ regsave_T save_after;
+ regsave_T save_behind;
+ int save_need_clear_subexpr;
+ save_se_T save_start[NSUBEXP];
+ save_se_T save_end[NSUBEXP];
+} regbehind_T;
+
+/*
+ * When there are alternatives a regstate_T is put on the regstack to remember
+ * what we are doing.
+ * Before it may be another type of item, depending on rs_state, to remember
+ * more things.
+ */
+typedef struct regitem_S {
+ regstate_T rs_state; // what we are doing, one of RS_ above
+ int16_t rs_no; // submatch nr or BEHIND/NOBEHIND
+ char_u *rs_scan; // current node in program
+ union {
+ save_se_T sesave;
+ regsave_T regsave;
+ } rs_un; // room for saving rex.input
+} regitem_T;
+
+// used for STAR, PLUS and BRACE_SIMPLE matching
+typedef struct regstar_S {
+ int nextb; // next byte
+ int nextb_ic; // next byte reverse case
+ long count;
+ long minval;
+ long maxval;
+} regstar_T;
+
+// used to store input position when a BACK was encountered, so that we now if
+// we made any progress since the last time.
+typedef struct backpos_S {
+ char_u *bp_scan; // "scan" where BACK was encountered
+ regsave_T bp_pos; // last input position
+} backpos_T;
+
+/*
+ * "regstack" and "backpos" are used by regmatch(). They are kept over calls
+ * to avoid invoking malloc() and free() often.
+ * "regstack" is a stack with regitem_T items, sometimes preceded by regstar_T
+ * or regbehind_T.
+ * "backpos_T" is a table with backpos_T for BACK
+ */
+static garray_T regstack = GA_EMPTY_INIT_VALUE;
+static garray_T backpos = GA_EMPTY_INIT_VALUE;
+
+static regsave_T behind_pos;
+
+/*
+ * Both for regstack and backpos tables we use the following strategy of
+ * allocation (to reduce malloc/free calls):
+ * - Initial size is fairly small.
+ * - When needed, the tables are grown bigger (8 times at first, double after
+ * that).
+ * - After executing the match we free the memory only if the array has grown.
+ * Thus the memory is kept allocated when it's at the initial size.
+ * This makes it fast while not keeping a lot of memory allocated.
+ * A three times speed increase was observed when using many simple patterns.
+ */
+#define REGSTACK_INITIAL 2048
+#define BACKPOS_INITIAL 64
+
+/*
+ * Opcode notes:
+ *
+ * BRANCH The set of branches constituting a single choice are hooked
+ * together with their "next" pointers, since precedence prevents
+ * anything being concatenated to any individual branch. The
+ * "next" pointer of the last BRANCH in a choice points to the
+ * thing following the whole choice. This is also where the
+ * final "next" pointer of each individual branch points; each
+ * branch starts with the operand node of a BRANCH node.
+ *
+ * BACK Normal "next" pointers all implicitly point forward; BACK
+ * exists to make loop structures possible.
+ *
+ * STAR,PLUS '=', and complex '*' and '+', are implemented as circular
+ * BRANCH structures using BACK. Simple cases (one character
+ * per match) are implemented with STAR and PLUS for speed
+ * and to minimize recursive plunges.
+ *
+ * BRACE_LIMITS This is always followed by a BRACE_SIMPLE or BRACE_COMPLEX
+ * node, and defines the min and max limits to be used for that
+ * node.
+ *
+ * MOPEN,MCLOSE ...are numbered at compile time.
+ * ZOPEN,ZCLOSE ...ditto
+ */
+
+/*
+ * A node is one char of opcode followed by two chars of "next" pointer.
+ * "Next" pointers are stored as two 8-bit bytes, high order first. The
+ * value is a positive offset from the opcode of the node containing it.
+ * An operand, if any, simply follows the node. (Note that much of the
+ * code generation knows about this implicit relationship.)
+ *
+ * Using two bytes for the "next" pointer is vast overkill for most things,
+ * but allows patterns to get big without disasters.
+ */
+#define OP(p) ((int)(*(p)))
+#define NEXT(p) (((*((p) + 1) & 0377) << 8) + (*((p) + 2) & 0377))
+#define OPERAND(p) ((p) + 3)
+// Obtain an operand that was stored as four bytes, MSB first.
+#define OPERAND_MIN(p) (((long)(p)[3] << 24) + ((long)(p)[4] << 16) \
+ + ((long)(p)[5] << 8) + (long)(p)[6])
+// Obtain a second operand stored as four bytes.
+#define OPERAND_MAX(p) OPERAND_MIN((p) + 4)
+// Obtain a second single-byte operand stored after a four bytes operand.
+#define OPERAND_CMP(p) (p)[7]
+
+static char_u *reg(int paren, int *flagp);
+
+#ifdef BT_REGEXP_DUMP
+static void regdump(char_u *, bt_regprog_T *);
+#endif
+
+#ifdef REGEXP_DEBUG
+static char_u *regprop(char_u *);
+
+static int regnarrate = 0;
+#endif
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "regexp_bt.c.generated.h"
+#endif
+
+/*
+ * Setup to parse the regexp. Used once to get the length and once to do it.
+ */
+static void regcomp_start(char_u *expr, int re_flags) // see vim_regcomp()
+{
+ initchr(expr);
+ if (re_flags & RE_MAGIC) {
+ reg_magic = MAGIC_ON;
+ } else {
+ reg_magic = MAGIC_OFF;
+ }
+ reg_string = (re_flags & RE_STRING);
+ reg_strict = (re_flags & RE_STRICT);
+ get_cpo_flags();
+
+ num_complex_braces = 0;
+ regnpar = 1;
+ memset(had_endbrace, 0, sizeof(had_endbrace));
+ regnzpar = 1;
+ re_has_z = 0;
+ regsize = 0L;
+ reg_toolong = false;
+ regflags = 0;
+ had_eol = false;
+}
+
+// Return true if MULTIBYTECODE should be used instead of EXACTLY for
+// character "c".
+static bool use_multibytecode(int c)
+{
+ return utf_char2len(c) > 1
+ && (re_multi_type(peekchr()) != NOT_MULTI
+ || utf_iscomposing(c));
+}
+
+/*
+ * Emit (if appropriate) a byte of code
+ */
+static void regc(int b)
+{
+ if (regcode == JUST_CALC_SIZE) {
+ regsize++;
+ } else {
+ *regcode++ = b;
+ }
+}
+
+/*
+ * Emit (if appropriate) a multi-byte character of code
+ */
+static void regmbc(int c)
+{
+ if (regcode == JUST_CALC_SIZE) {
+ regsize += utf_char2len(c);
+ } else {
+ regcode += utf_char2bytes(c, (char *)regcode);
+ }
+}
+
+/*
+ * Produce the bytes for equivalence class "c".
+ * Currently only handles latin1, latin9 and utf-8.
+ * NOTE: When changing this function, also change nfa_emit_equi_class()
+ */
+static void reg_equi_class(int c)
+{
+ {
+ switch (c) {
+ // Do not use '\300' style, it results in a negative number.
+ case 'A':
+ case 0xc0:
+ case 0xc1:
+ case 0xc2:
+ case 0xc3:
+ case 0xc4:
+ case 0xc5:
+ case 0x100:
+ case 0x102:
+ case 0x104:
+ case 0x1cd:
+ case 0x1de:
+ case 0x1e0:
+ case 0x1fa:
+ case 0x202:
+ case 0x226:
+ case 0x23a:
+ case 0x1e00:
+ case 0x1ea0:
+ case 0x1ea2:
+ case 0x1ea4:
+ case 0x1ea6:
+ case 0x1ea8:
+ case 0x1eaa:
+ case 0x1eac:
+ case 0x1eae:
+ case 0x1eb0:
+ case 0x1eb2:
+ case 0x1eb4:
+ case 0x1eb6:
+ regmbc('A'); regmbc(0xc0); regmbc(0xc1); regmbc(0xc2);
+ regmbc(0xc3); regmbc(0xc4); regmbc(0xc5);
+ regmbc(0x100); regmbc(0x102); regmbc(0x104);
+ regmbc(0x1cd); regmbc(0x1de); regmbc(0x1e0);
+ regmbc(0x1fa); regmbc(0x202); regmbc(0x226);
+ regmbc(0x23a); regmbc(0x1e00); regmbc(0x1ea0);
+ regmbc(0x1ea2); regmbc(0x1ea4); regmbc(0x1ea6);
+ regmbc(0x1ea8); regmbc(0x1eaa); regmbc(0x1eac);
+ regmbc(0x1eae); regmbc(0x1eb0); regmbc(0x1eb2);
+ regmbc(0x1eb4); regmbc(0x1eb6);
+ return;
+ case 'B':
+ case 0x181:
+ case 0x243:
+ case 0x1e02:
+ case 0x1e04:
+ case 0x1e06:
+ regmbc('B');
+ regmbc(0x181); regmbc(0x243); regmbc(0x1e02);
+ regmbc(0x1e04); regmbc(0x1e06);
+ return;
+ case 'C':
+ case 0xc7:
+ case 0x106:
+ case 0x108:
+ case 0x10a:
+ case 0x10c:
+ case 0x187:
+ case 0x23b:
+ case 0x1e08:
+ case 0xa792:
+ regmbc('C'); regmbc(0xc7);
+ regmbc(0x106); regmbc(0x108); regmbc(0x10a);
+ regmbc(0x10c); regmbc(0x187); regmbc(0x23b);
+ regmbc(0x1e08); regmbc(0xa792);
+ return;
+ case 'D':
+ case 0x10e:
+ case 0x110:
+ case 0x18a:
+ case 0x1e0a:
+ case 0x1e0c:
+ case 0x1e0e:
+ case 0x1e10:
+ case 0x1e12:
+ regmbc('D'); regmbc(0x10e); regmbc(0x110);
+ regmbc(0x18a); regmbc(0x1e0a); regmbc(0x1e0c);
+ regmbc(0x1e0e); regmbc(0x1e10); regmbc(0x1e12);
+ return;
+ case 'E':
+ case 0xc8:
+ case 0xc9:
+ case 0xca:
+ case 0xcb:
+ case 0x112:
+ case 0x114:
+ case 0x116:
+ case 0x118:
+ case 0x11a:
+ case 0x204:
+ case 0x206:
+ case 0x228:
+ case 0x246:
+ case 0x1e14:
+ case 0x1e16:
+ case 0x1e18:
+ case 0x1e1a:
+ case 0x1e1c:
+ case 0x1eb8:
+ case 0x1eba:
+ case 0x1ebc:
+ case 0x1ebe:
+ case 0x1ec0:
+ case 0x1ec2:
+ case 0x1ec4:
+ case 0x1ec6:
+ regmbc('E'); regmbc(0xc8); regmbc(0xc9);
+ regmbc(0xca); regmbc(0xcb); regmbc(0x112);
+ regmbc(0x114); regmbc(0x116); regmbc(0x118);
+ regmbc(0x11a); regmbc(0x204); regmbc(0x206);
+ regmbc(0x228); regmbc(0x246); regmbc(0x1e14);
+ regmbc(0x1e16); regmbc(0x1e18); regmbc(0x1e1a);
+ regmbc(0x1e1c); regmbc(0x1eb8); regmbc(0x1eba);
+ regmbc(0x1ebc); regmbc(0x1ebe); regmbc(0x1ec0);
+ regmbc(0x1ec2); regmbc(0x1ec4); regmbc(0x1ec6);
+ return;
+ case 'F':
+ case 0x191:
+ case 0x1e1e:
+ case 0xa798:
+ regmbc('F'); regmbc(0x191); regmbc(0x1e1e);
+ regmbc(0xa798);
+ return;
+ case 'G':
+ case 0x11c:
+ case 0x11e:
+ case 0x120:
+ case 0x122:
+ case 0x193:
+ case 0x1e4:
+ case 0x1e6:
+ case 0x1f4:
+ case 0x1e20:
+ case 0xa7a0:
+ regmbc('G'); regmbc(0x11c); regmbc(0x11e);
+ regmbc(0x120); regmbc(0x122); regmbc(0x193);
+ regmbc(0x1e4); regmbc(0x1e6); regmbc(0x1f4);
+ regmbc(0x1e20); regmbc(0xa7a0);
+ return;
+ case 'H':
+ case 0x124:
+ case 0x126:
+ case 0x21e:
+ case 0x1e22:
+ case 0x1e24:
+ case 0x1e26:
+ case 0x1e28:
+ case 0x1e2a:
+ case 0x2c67:
+ regmbc('H'); regmbc(0x124); regmbc(0x126);
+ regmbc(0x21e); regmbc(0x1e22); regmbc(0x1e24);
+ regmbc(0x1e26); regmbc(0x1e28); regmbc(0x1e2a);
+ regmbc(0x2c67);
+ return;
+ case 'I':
+ case 0xcc:
+ case 0xcd:
+ case 0xce:
+ case 0xcf:
+ case 0x128:
+ case 0x12a:
+ case 0x12c:
+ case 0x12e:
+ case 0x130:
+ case 0x197:
+ case 0x1cf:
+ case 0x208:
+ case 0x20a:
+ case 0x1e2c:
+ case 0x1e2e:
+ case 0x1ec8:
+ case 0x1eca:
+ regmbc('I'); regmbc(0xcc); regmbc(0xcd);
+ regmbc(0xce); regmbc(0xcf); regmbc(0x128);
+ regmbc(0x12a); regmbc(0x12c); regmbc(0x12e);
+ regmbc(0x130); regmbc(0x197); regmbc(0x1cf);
+ regmbc(0x208); regmbc(0x20a); regmbc(0x1e2c);
+ regmbc(0x1e2e); regmbc(0x1ec8); regmbc(0x1eca);
+ return;
+ case 'J':
+ case 0x134:
+ case 0x248:
+ regmbc('J'); regmbc(0x134); regmbc(0x248);
+ return;
+ case 'K':
+ case 0x136:
+ case 0x198:
+ case 0x1e8:
+ case 0x1e30:
+ case 0x1e32:
+ case 0x1e34:
+ case 0x2c69:
+ case 0xa740:
+ regmbc('K'); regmbc(0x136); regmbc(0x198);
+ regmbc(0x1e8); regmbc(0x1e30); regmbc(0x1e32);
+ regmbc(0x1e34); regmbc(0x2c69); regmbc(0xa740);
+ return;
+ case 'L':
+ case 0x139:
+ case 0x13b:
+ case 0x13d:
+ case 0x13f:
+ case 0x141:
+ case 0x23d:
+ case 0x1e36:
+ case 0x1e38:
+ case 0x1e3a:
+ case 0x1e3c:
+ case 0x2c60:
+ regmbc('L'); regmbc(0x139); regmbc(0x13b);
+ regmbc(0x13d); regmbc(0x13f); regmbc(0x141);
+ regmbc(0x23d); regmbc(0x1e36); regmbc(0x1e38);
+ regmbc(0x1e3a); regmbc(0x1e3c); regmbc(0x2c60);
+ return;
+ case 'M':
+ case 0x1e3e:
+ case 0x1e40:
+ case 0x1e42:
+ regmbc('M'); regmbc(0x1e3e); regmbc(0x1e40);
+ regmbc(0x1e42);
+ return;
+ case 'N':
+ case 0xd1:
+ case 0x143:
+ case 0x145:
+ case 0x147:
+ case 0x1f8:
+ case 0x1e44:
+ case 0x1e46:
+ case 0x1e48:
+ case 0x1e4a:
+ case 0xa7a4:
+ regmbc('N'); regmbc(0xd1);
+ regmbc(0x143); regmbc(0x145); regmbc(0x147);
+ regmbc(0x1f8); regmbc(0x1e44); regmbc(0x1e46);
+ regmbc(0x1e48); regmbc(0x1e4a); regmbc(0xa7a4);
+ return;
+ case 'O':
+ case 0xd2:
+ case 0xd3:
+ case 0xd4:
+ case 0xd5:
+ case 0xd6:
+ case 0xd8:
+ case 0x14c:
+ case 0x14e:
+ case 0x150:
+ case 0x19f:
+ case 0x1a0:
+ case 0x1d1:
+ case 0x1ea:
+ case 0x1ec:
+ case 0x1fe:
+ case 0x20c:
+ case 0x20e:
+ case 0x22a:
+ case 0x22c:
+ case 0x22e:
+ case 0x230:
+ case 0x1e4c:
+ case 0x1e4e:
+ case 0x1e50:
+ case 0x1e52:
+ case 0x1ecc:
+ case 0x1ece:
+ case 0x1ed0:
+ case 0x1ed2:
+ case 0x1ed4:
+ case 0x1ed6:
+ case 0x1ed8:
+ case 0x1eda:
+ case 0x1edc:
+ case 0x1ede:
+ case 0x1ee0:
+ case 0x1ee2:
+ regmbc('O'); regmbc(0xd2); regmbc(0xd3); regmbc(0xd4);
+ regmbc(0xd5); regmbc(0xd6); regmbc(0xd8);
+ regmbc(0x14c); regmbc(0x14e); regmbc(0x150);
+ regmbc(0x19f); regmbc(0x1a0); regmbc(0x1d1);
+ regmbc(0x1ea); regmbc(0x1ec); regmbc(0x1fe);
+ regmbc(0x20c); regmbc(0x20e); regmbc(0x22a);
+ regmbc(0x22c); regmbc(0x22e); regmbc(0x230);
+ regmbc(0x1e4c); regmbc(0x1e4e); regmbc(0x1e50);
+ regmbc(0x1e52); regmbc(0x1ecc); regmbc(0x1ece);
+ regmbc(0x1ed0); regmbc(0x1ed2); regmbc(0x1ed4);
+ regmbc(0x1ed6); regmbc(0x1ed8); regmbc(0x1eda);
+ regmbc(0x1edc); regmbc(0x1ede); regmbc(0x1ee0);
+ regmbc(0x1ee2);
+ return;
+ case 'P':
+ case 0x1a4:
+ case 0x1e54:
+ case 0x1e56:
+ case 0x2c63:
+ regmbc('P'); regmbc(0x1a4); regmbc(0x1e54);
+ regmbc(0x1e56); regmbc(0x2c63);
+ return;
+ case 'Q':
+ case 0x24a:
+ regmbc('Q'); regmbc(0x24a);
+ return;
+ case 'R':
+ case 0x154:
+ case 0x156:
+ case 0x158:
+ case 0x210:
+ case 0x212:
+ case 0x24c:
+ case 0x1e58:
+ case 0x1e5a:
+ case 0x1e5c:
+ case 0x1e5e:
+ case 0x2c64:
+ case 0xa7a6:
+ regmbc('R'); regmbc(0x154); regmbc(0x156);
+ regmbc(0x210); regmbc(0x212); regmbc(0x158);
+ regmbc(0x24c); regmbc(0x1e58); regmbc(0x1e5a);
+ regmbc(0x1e5c); regmbc(0x1e5e); regmbc(0x2c64);
+ regmbc(0xa7a6);
+ return;
+ case 'S':
+ case 0x15a:
+ case 0x15c:
+ case 0x15e:
+ case 0x160:
+ case 0x218:
+ case 0x1e60:
+ case 0x1e62:
+ case 0x1e64:
+ case 0x1e66:
+ case 0x1e68:
+ case 0x2c7e:
+ case 0xa7a8:
+ regmbc('S'); regmbc(0x15a); regmbc(0x15c);
+ regmbc(0x15e); regmbc(0x160); regmbc(0x218);
+ regmbc(0x1e60); regmbc(0x1e62); regmbc(0x1e64);
+ regmbc(0x1e66); regmbc(0x1e68); regmbc(0x2c7e);
+ regmbc(0xa7a8);
+ return;
+ case 'T':
+ case 0x162:
+ case 0x164:
+ case 0x166:
+ case 0x1ac:
+ case 0x1ae:
+ case 0x21a:
+ case 0x23e:
+ case 0x1e6a:
+ case 0x1e6c:
+ case 0x1e6e:
+ case 0x1e70:
+ regmbc('T'); regmbc(0x162); regmbc(0x164);
+ regmbc(0x166); regmbc(0x1ac); regmbc(0x23e);
+ regmbc(0x1ae); regmbc(0x21a); regmbc(0x1e6a);
+ regmbc(0x1e6c); regmbc(0x1e6e); regmbc(0x1e70);
+ return;
+ case 'U':
+ case 0xd9:
+ case 0xda:
+ case 0xdb:
+ case 0xdc:
+ case 0x168:
+ case 0x16a:
+ case 0x16c:
+ case 0x16e:
+ case 0x170:
+ case 0x172:
+ case 0x1af:
+ case 0x1d3:
+ case 0x1d5:
+ case 0x1d7:
+ case 0x1d9:
+ case 0x1db:
+ case 0x214:
+ case 0x216:
+ case 0x244:
+ case 0x1e72:
+ case 0x1e74:
+ case 0x1e76:
+ case 0x1e78:
+ case 0x1e7a:
+ case 0x1ee4:
+ case 0x1ee6:
+ case 0x1ee8:
+ case 0x1eea:
+ case 0x1eec:
+ case 0x1eee:
+ case 0x1ef0:
+ regmbc('U'); regmbc(0xd9); regmbc(0xda);
+ regmbc(0xdb); regmbc(0xdc); regmbc(0x168);
+ regmbc(0x16a); regmbc(0x16c); regmbc(0x16e);
+ regmbc(0x170); regmbc(0x172); regmbc(0x1af);
+ regmbc(0x1d3); regmbc(0x1d5); regmbc(0x1d7);
+ regmbc(0x1d9); regmbc(0x1db); regmbc(0x214);
+ regmbc(0x216); regmbc(0x244); regmbc(0x1e72);
+ regmbc(0x1e74); regmbc(0x1e76); regmbc(0x1e78);
+ regmbc(0x1e7a); regmbc(0x1ee4); regmbc(0x1ee6);
+ regmbc(0x1ee8); regmbc(0x1eea); regmbc(0x1eec);
+ regmbc(0x1eee); regmbc(0x1ef0);
+ return;
+ case 'V':
+ case 0x1b2:
+ case 0x1e7c:
+ case 0x1e7e:
+ regmbc('V'); regmbc(0x1b2); regmbc(0x1e7c);
+ regmbc(0x1e7e);
+ return;
+ case 'W':
+ case 0x174:
+ case 0x1e80:
+ case 0x1e82:
+ case 0x1e84:
+ case 0x1e86:
+ case 0x1e88:
+ regmbc('W'); regmbc(0x174); regmbc(0x1e80);
+ regmbc(0x1e82); regmbc(0x1e84); regmbc(0x1e86);
+ regmbc(0x1e88);
+ return;
+ case 'X':
+ case 0x1e8a:
+ case 0x1e8c:
+ regmbc('X'); regmbc(0x1e8a); regmbc(0x1e8c);
+ return;
+ case 'Y':
+ case 0xdd:
+ case 0x176:
+ case 0x178:
+ case 0x1b3:
+ case 0x232:
+ case 0x24e:
+ case 0x1e8e:
+ case 0x1ef2:
+ case 0x1ef6:
+ case 0x1ef4:
+ case 0x1ef8:
+ regmbc('Y'); regmbc(0xdd); regmbc(0x176);
+ regmbc(0x178); regmbc(0x1b3); regmbc(0x232);
+ regmbc(0x24e); regmbc(0x1e8e); regmbc(0x1ef2);
+ regmbc(0x1ef4); regmbc(0x1ef6); regmbc(0x1ef8);
+ return;
+ case 'Z':
+ case 0x179:
+ case 0x17b:
+ case 0x17d:
+ case 0x1b5:
+ case 0x1e90:
+ case 0x1e92:
+ case 0x1e94:
+ case 0x2c6b:
+ regmbc('Z'); regmbc(0x179); regmbc(0x17b);
+ regmbc(0x17d); regmbc(0x1b5); regmbc(0x1e90);
+ regmbc(0x1e92); regmbc(0x1e94); regmbc(0x2c6b);
+ return;
+ case 'a':
+ case 0xe0:
+ case 0xe1:
+ case 0xe2:
+ case 0xe3:
+ case 0xe4:
+ case 0xe5:
+ case 0x101:
+ case 0x103:
+ case 0x105:
+ case 0x1ce:
+ case 0x1df:
+ case 0x1e1:
+ case 0x1fb:
+ case 0x201:
+ case 0x203:
+ case 0x227:
+ case 0x1d8f:
+ case 0x1e01:
+ case 0x1e9a:
+ case 0x1ea1:
+ case 0x1ea3:
+ case 0x1ea5:
+ case 0x1ea7:
+ case 0x1ea9:
+ case 0x1eab:
+ case 0x1ead:
+ case 0x1eaf:
+ case 0x1eb1:
+ case 0x1eb3:
+ case 0x1eb5:
+ case 0x1eb7:
+ case 0x2c65:
+ regmbc('a'); regmbc(0xe0); regmbc(0xe1);
+ regmbc(0xe2); regmbc(0xe3); regmbc(0xe4);
+ regmbc(0xe5); regmbc(0x101); regmbc(0x103);
+ regmbc(0x105); regmbc(0x1ce); regmbc(0x1df);
+ regmbc(0x1e1); regmbc(0x1fb); regmbc(0x201);
+ regmbc(0x203); regmbc(0x227); regmbc(0x1d8f);
+ regmbc(0x1e01); regmbc(0x1e9a); regmbc(0x1ea1);
+ regmbc(0x1ea3); regmbc(0x1ea5); regmbc(0x1ea7);
+ regmbc(0x1ea9); regmbc(0x1eab); regmbc(0x1ead);
+ regmbc(0x1eaf); regmbc(0x1eb1); regmbc(0x1eb3);
+ regmbc(0x1eb5); regmbc(0x1eb7); regmbc(0x2c65);
+ return;
+ case 'b':
+ case 0x180:
+ case 0x253:
+ case 0x1d6c:
+ case 0x1d80:
+ case 0x1e03:
+ case 0x1e05:
+ case 0x1e07:
+ regmbc('b');
+ regmbc(0x180); regmbc(0x253); regmbc(0x1d6c);
+ regmbc(0x1d80); regmbc(0x1e03); regmbc(0x1e05);
+ regmbc(0x1e07);
+ return;
+ case 'c':
+ case 0xe7:
+ case 0x107:
+ case 0x109:
+ case 0x10b:
+ case 0x10d:
+ case 0x188:
+ case 0x23c:
+ case 0x1e09:
+ case 0xa793:
+ case 0xa794:
+ regmbc('c'); regmbc(0xe7); regmbc(0x107);
+ regmbc(0x109); regmbc(0x10b); regmbc(0x10d);
+ regmbc(0x188); regmbc(0x23c); regmbc(0x1e09);
+ regmbc(0xa793); regmbc(0xa794);
+ return;
+ case 'd':
+ case 0x10f:
+ case 0x111:
+ case 0x257:
+ case 0x1d6d:
+ case 0x1d81:
+ case 0x1d91:
+ case 0x1e0b:
+ case 0x1e0d:
+ case 0x1e0f:
+ case 0x1e11:
+ case 0x1e13:
+ regmbc('d'); regmbc(0x10f); regmbc(0x111);
+ regmbc(0x257); regmbc(0x1d6d); regmbc(0x1d81);
+ regmbc(0x1d91); regmbc(0x1e0b); regmbc(0x1e0d);
+ regmbc(0x1e0f); regmbc(0x1e11); regmbc(0x1e13);
+ return;
+ case 'e':
+ case 0xe8:
+ case 0xe9:
+ case 0xea:
+ case 0xeb:
+ case 0x113:
+ case 0x115:
+ case 0x117:
+ case 0x119:
+ case 0x11b:
+ case 0x205:
+ case 0x207:
+ case 0x229:
+ case 0x247:
+ case 0x1d92:
+ case 0x1e15:
+ case 0x1e17:
+ case 0x1e19:
+ case 0x1e1b:
+ case 0x1eb9:
+ case 0x1ebb:
+ case 0x1e1d:
+ case 0x1ebd:
+ case 0x1ebf:
+ case 0x1ec1:
+ case 0x1ec3:
+ case 0x1ec5:
+ case 0x1ec7:
+ regmbc('e'); regmbc(0xe8); regmbc(0xe9);
+ regmbc(0xea); regmbc(0xeb); regmbc(0x113);
+ regmbc(0x115); regmbc(0x117); regmbc(0x119);
+ regmbc(0x11b); regmbc(0x205); regmbc(0x207);
+ regmbc(0x229); regmbc(0x247); regmbc(0x1d92);
+ regmbc(0x1e15); regmbc(0x1e17); regmbc(0x1e19);
+ regmbc(0x1e1b); regmbc(0x1e1d); regmbc(0x1eb9);
+ regmbc(0x1ebb); regmbc(0x1ebd); regmbc(0x1ebf);
+ regmbc(0x1ec1); regmbc(0x1ec3); regmbc(0x1ec5);
+ regmbc(0x1ec7);
+ return;
+ case 'f':
+ case 0x192:
+ case 0x1d6e:
+ case 0x1d82:
+ case 0x1e1f:
+ case 0xa799:
+ regmbc('f'); regmbc(0x192); regmbc(0x1d6e);
+ regmbc(0x1d82); regmbc(0x1e1f); regmbc(0xa799);
+ return;
+ case 'g':
+ case 0x11d:
+ case 0x11f:
+ case 0x121:
+ case 0x123:
+ case 0x1e5:
+ case 0x1e7:
+ case 0x260:
+ case 0x1f5:
+ case 0x1d83:
+ case 0x1e21:
+ case 0xa7a1:
+ regmbc('g'); regmbc(0x11d); regmbc(0x11f);
+ regmbc(0x121); regmbc(0x123); regmbc(0x1e5);
+ regmbc(0x1e7); regmbc(0x1f5); regmbc(0x260);
+ regmbc(0x1d83); regmbc(0x1e21); regmbc(0xa7a1);
+ return;
+ case 'h':
+ case 0x125:
+ case 0x127:
+ case 0x21f:
+ case 0x1e23:
+ case 0x1e25:
+ case 0x1e27:
+ case 0x1e29:
+ case 0x1e2b:
+ case 0x1e96:
+ case 0x2c68:
+ case 0xa795:
+ regmbc('h'); regmbc(0x125); regmbc(0x127);
+ regmbc(0x21f); regmbc(0x1e23); regmbc(0x1e25);
+ regmbc(0x1e27); regmbc(0x1e29); regmbc(0x1e2b);
+ regmbc(0x1e96); regmbc(0x2c68); regmbc(0xa795);
+ return;
+ case 'i':
+ case 0xec:
+ case 0xed:
+ case 0xee:
+ case 0xef:
+ case 0x129:
+ case 0x12b:
+ case 0x12d:
+ case 0x12f:
+ case 0x1d0:
+ case 0x209:
+ case 0x20b:
+ case 0x268:
+ case 0x1d96:
+ case 0x1e2d:
+ case 0x1e2f:
+ case 0x1ec9:
+ case 0x1ecb:
+ regmbc('i'); regmbc(0xec); regmbc(0xed);
+ regmbc(0xee); regmbc(0xef); regmbc(0x129);
+ regmbc(0x12b); regmbc(0x12d); regmbc(0x12f);
+ regmbc(0x1d0); regmbc(0x209); regmbc(0x20b);
+ regmbc(0x268); regmbc(0x1d96); regmbc(0x1e2d);
+ regmbc(0x1e2f); regmbc(0x1ec9); regmbc(0x1ecb);
+ return;
+ case 'j':
+ case 0x135:
+ case 0x1f0:
+ case 0x249:
+ regmbc('j'); regmbc(0x135); regmbc(0x1f0);
+ regmbc(0x249);
+ return;
+ case 'k':
+ case 0x137:
+ case 0x199:
+ case 0x1e9:
+ case 0x1d84:
+ case 0x1e31:
+ case 0x1e33:
+ case 0x1e35:
+ case 0x2c6a:
+ case 0xa741:
+ regmbc('k'); regmbc(0x137); regmbc(0x199);
+ regmbc(0x1e9); regmbc(0x1d84); regmbc(0x1e31);
+ regmbc(0x1e33); regmbc(0x1e35); regmbc(0x2c6a);
+ regmbc(0xa741);
+ return;
+ case 'l':
+ case 0x13a:
+ case 0x13c:
+ case 0x13e:
+ case 0x140:
+ case 0x142:
+ case 0x19a:
+ case 0x1e37:
+ case 0x1e39:
+ case 0x1e3b:
+ case 0x1e3d:
+ case 0x2c61:
+ regmbc('l'); regmbc(0x13a); regmbc(0x13c);
+ regmbc(0x13e); regmbc(0x140); regmbc(0x142);
+ regmbc(0x19a); regmbc(0x1e37); regmbc(0x1e39);
+ regmbc(0x1e3b); regmbc(0x1e3d); regmbc(0x2c61);
+ return;
+ case 'm':
+ case 0x1d6f:
+ case 0x1e3f:
+ case 0x1e41:
+ case 0x1e43:
+ regmbc('m'); regmbc(0x1d6f); regmbc(0x1e3f);
+ regmbc(0x1e41); regmbc(0x1e43);
+ return;
+ case 'n':
+ case 0xf1:
+ case 0x144:
+ case 0x146:
+ case 0x148:
+ case 0x149:
+ case 0x1f9:
+ case 0x1d70:
+ case 0x1d87:
+ case 0x1e45:
+ case 0x1e47:
+ case 0x1e49:
+ case 0x1e4b:
+ case 0xa7a5:
+ regmbc('n'); regmbc(0xf1); regmbc(0x144);
+ regmbc(0x146); regmbc(0x148); regmbc(0x149);
+ regmbc(0x1f9); regmbc(0x1d70); regmbc(0x1d87);
+ regmbc(0x1e45); regmbc(0x1e47); regmbc(0x1e49);
+ regmbc(0x1e4b); regmbc(0xa7a5);
+ return;
+ case 'o':
+ case 0xf2:
+ case 0xf3:
+ case 0xf4:
+ case 0xf5:
+ case 0xf6:
+ case 0xf8:
+ case 0x14d:
+ case 0x14f:
+ case 0x151:
+ case 0x1a1:
+ case 0x1d2:
+ case 0x1eb:
+ case 0x1ed:
+ case 0x1ff:
+ case 0x20d:
+ case 0x20f:
+ case 0x22b:
+ case 0x22d:
+ case 0x22f:
+ case 0x231:
+ case 0x275:
+ case 0x1e4d:
+ case 0x1e4f:
+ case 0x1e51:
+ case 0x1e53:
+ case 0x1ecd:
+ case 0x1ecf:
+ case 0x1ed1:
+ case 0x1ed3:
+ case 0x1ed5:
+ case 0x1ed7:
+ case 0x1ed9:
+ case 0x1edb:
+ case 0x1edd:
+ case 0x1edf:
+ case 0x1ee1:
+ case 0x1ee3:
+ regmbc('o'); regmbc(0xf2); regmbc(0xf3);
+ regmbc(0xf4); regmbc(0xf5); regmbc(0xf6);
+ regmbc(0xf8); regmbc(0x14d); regmbc(0x14f);
+ regmbc(0x151); regmbc(0x1a1); regmbc(0x1d2);
+ regmbc(0x1eb); regmbc(0x1ed); regmbc(0x1ff);
+ regmbc(0x20d); regmbc(0x20f); regmbc(0x22b);
+ regmbc(0x22d); regmbc(0x22f); regmbc(0x231);
+ regmbc(0x275); regmbc(0x1e4d); regmbc(0x1e4f);
+ regmbc(0x1e51); regmbc(0x1e53); regmbc(0x1ecd);
+ regmbc(0x1ecf); regmbc(0x1ed1); regmbc(0x1ed3);
+ regmbc(0x1ed5); regmbc(0x1ed7); regmbc(0x1ed9);
+ regmbc(0x1edb); regmbc(0x1edd); regmbc(0x1edf);
+ regmbc(0x1ee1); regmbc(0x1ee3);
+ return;
+ case 'p':
+ case 0x1a5:
+ case 0x1d71:
+ case 0x1d88:
+ case 0x1d7d:
+ case 0x1e55:
+ case 0x1e57:
+ regmbc('p'); regmbc(0x1a5); regmbc(0x1d71);
+ regmbc(0x1d7d); regmbc(0x1d88); regmbc(0x1e55);
+ regmbc(0x1e57);
+ return;
+ case 'q':
+ case 0x24b:
+ case 0x2a0:
+ regmbc('q'); regmbc(0x24b); regmbc(0x2a0);
+ return;
+ case 'r':
+ case 0x155:
+ case 0x157:
+ case 0x159:
+ case 0x211:
+ case 0x213:
+ case 0x24d:
+ case 0x27d:
+ case 0x1d72:
+ case 0x1d73:
+ case 0x1d89:
+ case 0x1e59:
+ case 0x1e5b:
+ case 0x1e5d:
+ case 0x1e5f:
+ case 0xa7a7:
+ regmbc('r'); regmbc(0x155); regmbc(0x157);
+ regmbc(0x159); regmbc(0x211); regmbc(0x213);
+ regmbc(0x24d); regmbc(0x1d72); regmbc(0x1d73);
+ regmbc(0x1d89); regmbc(0x1e59); regmbc(0x27d);
+ regmbc(0x1e5b); regmbc(0x1e5d); regmbc(0x1e5f);
+ regmbc(0xa7a7);
+ return;
+ case 's':
+ case 0x15b:
+ case 0x15d:
+ case 0x15f:
+ case 0x161:
+ case 0x1e61:
+ case 0x219:
+ case 0x23f:
+ case 0x1d74:
+ case 0x1d8a:
+ case 0x1e63:
+ case 0x1e65:
+ case 0x1e67:
+ case 0x1e69:
+ case 0xa7a9:
+ regmbc('s'); regmbc(0x15b); regmbc(0x15d);
+ regmbc(0x15f); regmbc(0x161); regmbc(0x23f);
+ regmbc(0x219); regmbc(0x1d74); regmbc(0x1d8a);
+ regmbc(0x1e61); regmbc(0x1e63); regmbc(0x1e65);
+ regmbc(0x1e67); regmbc(0x1e69); regmbc(0xa7a9);
+ return;
+ case 't':
+ case 0x163:
+ case 0x165:
+ case 0x167:
+ case 0x1ab:
+ case 0x1ad:
+ case 0x21b:
+ case 0x288:
+ case 0x1d75:
+ case 0x1e6b:
+ case 0x1e6d:
+ case 0x1e6f:
+ case 0x1e71:
+ case 0x1e97:
+ case 0x2c66:
+ regmbc('t'); regmbc(0x163); regmbc(0x165);
+ regmbc(0x167); regmbc(0x1ab); regmbc(0x21b);
+ regmbc(0x1ad); regmbc(0x288); regmbc(0x1d75);
+ regmbc(0x1e6b); regmbc(0x1e6d); regmbc(0x1e6f);
+ regmbc(0x1e71); regmbc(0x1e97); regmbc(0x2c66);
+ return;
+ case 'u':
+ case 0xf9:
+ case 0xfa:
+ case 0xfb:
+ case 0xfc:
+ case 0x169:
+ case 0x16b:
+ case 0x16d:
+ case 0x16f:
+ case 0x171:
+ case 0x173:
+ case 0x1b0:
+ case 0x1d4:
+ case 0x1d6:
+ case 0x1d8:
+ case 0x1da:
+ case 0x1dc:
+ case 0x215:
+ case 0x217:
+ case 0x289:
+ case 0x1e73:
+ case 0x1d7e:
+ case 0x1d99:
+ case 0x1e75:
+ case 0x1e77:
+ case 0x1e79:
+ case 0x1e7b:
+ case 0x1ee5:
+ case 0x1ee7:
+ case 0x1ee9:
+ case 0x1eeb:
+ case 0x1eed:
+ case 0x1eef:
+ case 0x1ef1:
+ regmbc('u'); regmbc(0xf9); regmbc(0xfa);
+ regmbc(0xfb); regmbc(0xfc); regmbc(0x169);
+ regmbc(0x16b); regmbc(0x16d); regmbc(0x16f);
+ regmbc(0x171); regmbc(0x173); regmbc(0x1d6);
+ regmbc(0x1d8); regmbc(0x1da); regmbc(0x1dc);
+ regmbc(0x215); regmbc(0x217); regmbc(0x1b0);
+ regmbc(0x1d4); regmbc(0x289); regmbc(0x1d7e);
+ regmbc(0x1d99); regmbc(0x1e73); regmbc(0x1e75);
+ regmbc(0x1e77); regmbc(0x1e79); regmbc(0x1e7b);
+ regmbc(0x1ee5); regmbc(0x1ee7); regmbc(0x1ee9);
+ regmbc(0x1eeb); regmbc(0x1eed); regmbc(0x1eef);
+ regmbc(0x1ef1);
+ return;
+ case 'v':
+ case 0x28b:
+ case 0x1d8c:
+ case 0x1e7d:
+ case 0x1e7f:
+ regmbc('v'); regmbc(0x28b); regmbc(0x1d8c);
+ regmbc(0x1e7d); regmbc(0x1e7f);
+ return;
+ case 'w':
+ case 0x175:
+ case 0x1e81:
+ case 0x1e83:
+ case 0x1e85:
+ case 0x1e87:
+ case 0x1e89:
+ case 0x1e98:
+ regmbc('w'); regmbc(0x175); regmbc(0x1e81);
+ regmbc(0x1e83); regmbc(0x1e85); regmbc(0x1e87);
+ regmbc(0x1e89); regmbc(0x1e98);
+ return;
+ case 'x':
+ case 0x1e8b:
+ case 0x1e8d:
+ regmbc('x'); regmbc(0x1e8b); regmbc(0x1e8d);
+ return;
+ case 'y':
+ case 0xfd:
+ case 0xff:
+ case 0x177:
+ case 0x1b4:
+ case 0x233:
+ case 0x24f:
+ case 0x1e8f:
+ case 0x1e99:
+ case 0x1ef3:
+ case 0x1ef5:
+ case 0x1ef7:
+ case 0x1ef9:
+ regmbc('y'); regmbc(0xfd); regmbc(0xff);
+ regmbc(0x177); regmbc(0x1b4); regmbc(0x233);
+ regmbc(0x24f); regmbc(0x1e8f); regmbc(0x1e99);
+ regmbc(0x1ef3); regmbc(0x1ef5); regmbc(0x1ef7);
+ regmbc(0x1ef9);
+ return;
+ case 'z':
+ case 0x17a:
+ case 0x17c:
+ case 0x17e:
+ case 0x1b6:
+ case 0x1d76:
+ case 0x1d8e:
+ case 0x1e91:
+ case 0x1e93:
+ case 0x1e95:
+ case 0x2c6c:
+ regmbc('z'); regmbc(0x17a); regmbc(0x17c);
+ regmbc(0x17e); regmbc(0x1b6); regmbc(0x1d76);
+ regmbc(0x1d8e); regmbc(0x1e91); regmbc(0x1e93);
+ regmbc(0x1e95); regmbc(0x2c6c);
+ return;
+ }
+ }
+ regmbc(c);
+}
+
+/*
+ * Emit a node.
+ * Return pointer to generated code.
+ */
+static char_u *regnode(int op)
+{
+ char_u *ret;
+
+ ret = regcode;
+ if (ret == JUST_CALC_SIZE) {
+ regsize += 3;
+ } else {
+ *regcode++ = op;
+ *regcode++ = NUL; // Null "next" pointer.
+ *regcode++ = NUL;
+ }
+ return ret;
+}
+
+/*
+ * Write a four bytes number at "p" and return pointer to the next char.
+ */
+static char_u *re_put_uint32(char_u *p, uint32_t val)
+{
+ *p++ = (char_u)((val >> 24) & 0377);
+ *p++ = (char_u)((val >> 16) & 0377);
+ *p++ = (char_u)((val >> 8) & 0377);
+ *p++ = (char_u)(val & 0377);
+ return p;
+}
+
+/*
+ * regnext - dig the "next" pointer out of a node
+ * Returns NULL when calculating size, when there is no next item and when
+ * there is an error.
+ */
+static char_u *regnext(char_u *p)
+ FUNC_ATTR_NONNULL_ALL
+{
+ int offset;
+
+ if (p == JUST_CALC_SIZE || reg_toolong) {
+ return NULL;
+ }
+
+ offset = NEXT(p);
+ if (offset == 0) {
+ return NULL;
+ }
+
+ if (OP(p) == BACK) {
+ return p - offset;
+ } else {
+ return p + offset;
+ }
+}
+
+// Set the next-pointer at the end of a node chain.
+static void regtail(char_u *p, char_u *val)
+{
+ int offset;
+
+ if (p == JUST_CALC_SIZE) {
+ return;
+ }
+
+ // Find last node.
+ char_u *scan = p;
+ for (;;) {
+ char_u *temp = regnext(scan);
+ if (temp == NULL) {
+ break;
+ }
+ scan = temp;
+ }
+
+ if (OP(scan) == BACK) {
+ offset = (int)(scan - val);
+ } else {
+ offset = (int)(val - scan);
+ }
+ // When the offset uses more than 16 bits it can no longer fit in the two
+ // bytes available. Use a global flag to avoid having to check return
+ // values in too many places.
+ if (offset > 0xffff) {
+ reg_toolong = true;
+ } else {
+ *(scan + 1) = (char_u)(((unsigned)offset >> 8) & 0377);
+ *(scan + 2) = (char_u)(offset & 0377);
+ }
+}
+
+/*
+ * Like regtail, on item after a BRANCH; nop if none.
+ */
+static void regoptail(char_u *p, char_u *val)
+{
+ // When op is neither BRANCH nor BRACE_COMPLEX0-9, it is "operandless"
+ if (p == NULL || p == JUST_CALC_SIZE
+ || (OP(p) != BRANCH
+ && (OP(p) < BRACE_COMPLEX || OP(p) > BRACE_COMPLEX + 9))) {
+ return;
+ }
+ regtail(OPERAND(p), val);
+}
+
+/*
+ * Insert an operator in front of already-emitted operand
+ *
+ * Means relocating the operand.
+ */
+static void reginsert(int op, char_u *opnd)
+{
+ char_u *src;
+ char_u *dst;
+ char_u *place;
+
+ if (regcode == JUST_CALC_SIZE) {
+ regsize += 3;
+ return;
+ }
+ src = regcode;
+ regcode += 3;
+ dst = regcode;
+ while (src > opnd) {
+ *--dst = *--src;
+ }
+
+ place = opnd; // Op node, where operand used to be.
+ *place++ = op;
+ *place++ = NUL;
+ *place = NUL;
+}
+
+/*
+ * Insert an operator in front of already-emitted operand.
+ * Add a number to the operator.
+ */
+static void reginsert_nr(int op, long val, char_u *opnd)
+{
+ char_u *src;
+ char_u *dst;
+ char_u *place;
+
+ if (regcode == JUST_CALC_SIZE) {
+ regsize += 7;
+ return;
+ }
+ src = regcode;
+ regcode += 7;
+ dst = regcode;
+ while (src > opnd) {
+ *--dst = *--src;
+ }
+
+ place = opnd; // Op node, where operand used to be.
+ *place++ = op;
+ *place++ = NUL;
+ *place++ = NUL;
+ assert(val >= 0 && (uintmax_t)val <= UINT32_MAX);
+ re_put_uint32(place, (uint32_t)val);
+}
+
+/*
+ * Insert an operator in front of already-emitted operand.
+ * The operator has the given limit values as operands. Also set next pointer.
+ *
+ * Means relocating the operand.
+ */
+static void reginsert_limits(int op, long minval, long maxval, char_u *opnd)
+{
+ char_u *src;
+ char_u *dst;
+ char_u *place;
+
+ if (regcode == JUST_CALC_SIZE) {
+ regsize += 11;
+ return;
+ }
+ src = regcode;
+ regcode += 11;
+ dst = regcode;
+ while (src > opnd) {
+ *--dst = *--src;
+ }
+
+ place = opnd; // Op node, where operand used to be.
+ *place++ = op;
+ *place++ = NUL;
+ *place++ = NUL;
+ assert(minval >= 0 && (uintmax_t)minval <= UINT32_MAX);
+ place = re_put_uint32(place, (uint32_t)minval);
+ assert(maxval >= 0 && (uintmax_t)maxval <= UINT32_MAX);
+ place = re_put_uint32(place, (uint32_t)maxval);
+ regtail(opnd, place);
+}
+
+/// Return true if the back reference is legal. We must have seen the close
+/// brace.
+/// TODO(vim): Should also check that we don't refer to something repeated
+/// (+*=): what instance of the repetition should we match?
+static int seen_endbrace(int refnum)
+{
+ if (!had_endbrace[refnum]) {
+ char_u *p;
+
+ // Trick: check if "@<=" or "@<!" follows, in which case
+ // the \1 can appear before the referenced match.
+ for (p = regparse; *p != NUL; p++) {
+ if (p[0] == '@' && p[1] == '<' && (p[2] == '!' || p[2] == '=')) {
+ break;
+ }
+ }
+
+ if (*p == NUL) {
+ emsg(_("E65: Illegal back reference"));
+ rc_did_emsg = true;
+ return false;
+ }
+ }
+ return true;
+}
+
+/*
+ * Parse the lowest level.
+ *
+ * Optimization: gobbles an entire sequence of ordinary characters so that
+ * it can turn them into a single node, which is smaller to store and
+ * faster to run. Don't do this when one_exactly is set.
+ */
+static char_u *regatom(int *flagp)
+{
+ char_u *ret;
+ int flags;
+ int c;
+ char_u *p;
+ int extra = 0;
+ int save_prev_at_start = prev_at_start;
+
+ *flagp = WORST; // Tentatively.
+
+ c = getchr();
+ switch (c) {
+ case Magic('^'):
+ ret = regnode(BOL);
+ break;
+
+ case Magic('$'):
+ ret = regnode(EOL);
+ had_eol = true;
+ break;
+
+ case Magic('<'):
+ ret = regnode(BOW);
+ break;
+
+ case Magic('>'):
+ ret = regnode(EOW);
+ break;
+
+ case Magic('_'):
+ c = no_Magic(getchr());
+ if (c == '^') { // "\_^" is start-of-line
+ ret = regnode(BOL);
+ break;
+ }
+ if (c == '$') { // "\_$" is end-of-line
+ ret = regnode(EOL);
+ had_eol = true;
+ break;
+ }
+
+ extra = ADD_NL;
+ *flagp |= HASNL;
+
+ // "\_[" is character range plus newline
+ if (c == '[') {
+ goto collection;
+ }
+
+ // "\_x" is character class plus newline
+ FALLTHROUGH;
+
+ // Character classes.
+ case Magic('.'):
+ case Magic('i'):
+ case Magic('I'):
+ case Magic('k'):
+ case Magic('K'):
+ case Magic('f'):
+ case Magic('F'):
+ case Magic('p'):
+ case Magic('P'):
+ case Magic('s'):
+ case Magic('S'):
+ case Magic('d'):
+ case Magic('D'):
+ case Magic('x'):
+ case Magic('X'):
+ case Magic('o'):
+ case Magic('O'):
+ case Magic('w'):
+ case Magic('W'):
+ case Magic('h'):
+ case Magic('H'):
+ case Magic('a'):
+ case Magic('A'):
+ case Magic('l'):
+ case Magic('L'):
+ case Magic('u'):
+ case Magic('U'):
+ p = (char_u *)vim_strchr((char *)classchars, no_Magic(c));
+ if (p == NULL) {
+ EMSG_RET_NULL(_("E63: invalid use of \\_"));
+ }
+ // When '.' is followed by a composing char ignore the dot, so that
+ // the composing char is matched here.
+ if (c == Magic('.') && utf_iscomposing(peekchr())) {
+ c = getchr();
+ goto do_multibyte;
+ }
+ ret = regnode(classcodes[p - classchars] + extra);
+ *flagp |= HASWIDTH | SIMPLE;
+ break;
+
+ case Magic('n'):
+ if (reg_string) {
+ // In a string "\n" matches a newline character.
+ ret = regnode(EXACTLY);
+ regc(NL);
+ regc(NUL);
+ *flagp |= HASWIDTH | SIMPLE;
+ } else {
+ // In buffer text "\n" matches the end of a line.
+ ret = regnode(NEWL);
+ *flagp |= HASWIDTH | HASNL;
+ }
+ break;
+
+ case Magic('('):
+ if (one_exactly) {
+ EMSG_ONE_RET_NULL;
+ }
+ ret = reg(REG_PAREN, &flags);
+ if (ret == NULL) {
+ return NULL;
+ }
+ *flagp |= flags & (HASWIDTH | SPSTART | HASNL | HASLOOKBH);
+ break;
+
+ case NUL:
+ case Magic('|'):
+ case Magic('&'):
+ case Magic(')'):
+ if (one_exactly) {
+ EMSG_ONE_RET_NULL;
+ }
+ IEMSG_RET_NULL(_(e_internal)); // Supposed to be caught earlier.
+ // NOTREACHED
+
+ case Magic('='):
+ case Magic('?'):
+ case Magic('+'):
+ case Magic('@'):
+ case Magic('{'):
+ case Magic('*'):
+ c = no_Magic(c);
+ EMSG3_RET_NULL(_("E64: %s%c follows nothing"),
+ (c == '*' ? reg_magic >= MAGIC_ON : reg_magic == MAGIC_ALL), c);
+ // NOTREACHED
+
+ case Magic('~'): // previous substitute pattern
+ if (reg_prev_sub != NULL) {
+ char_u *lp;
+
+ ret = regnode(EXACTLY);
+ lp = reg_prev_sub;
+ while (*lp != NUL) {
+ regc(*lp++);
+ }
+ regc(NUL);
+ if (*reg_prev_sub != NUL) {
+ *flagp |= HASWIDTH;
+ if ((lp - reg_prev_sub) == 1) {
+ *flagp |= SIMPLE;
+ }
+ }
+ } else {
+ EMSG_RET_NULL(_(e_nopresub));
+ }
+ break;
+
+ case Magic('1'):
+ case Magic('2'):
+ case Magic('3'):
+ case Magic('4'):
+ case Magic('5'):
+ case Magic('6'):
+ case Magic('7'):
+ case Magic('8'):
+ case Magic('9'): {
+ int refnum;
+
+ refnum = c - Magic('0');
+ if (!seen_endbrace(refnum)) {
+ return NULL;
+ }
+ ret = regnode(BACKREF + refnum);
+ }
+ break;
+
+ case Magic('z'):
+ c = no_Magic(getchr());
+ switch (c) {
+ case '(':
+ if ((reg_do_extmatch & REX_SET) == 0) {
+ EMSG_RET_NULL(_(e_z_not_allowed));
+ }
+ if (one_exactly) {
+ EMSG_ONE_RET_NULL;
+ }
+ ret = reg(REG_ZPAREN, &flags);
+ if (ret == NULL) {
+ return NULL;
+ }
+ *flagp |= flags & (HASWIDTH|SPSTART|HASNL|HASLOOKBH);
+ re_has_z = REX_SET;
+ break;
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if ((reg_do_extmatch & REX_USE) == 0) {
+ EMSG_RET_NULL(_(e_z1_not_allowed));
+ }
+ ret = regnode(ZREF + c - '0');
+ re_has_z = REX_USE;
+ break;
+
+ case 's':
+ ret = regnode(MOPEN + 0);
+ if (!re_mult_next("\\zs")) {
+ return NULL;
+ }
+ break;
+
+ case 'e':
+ ret = regnode(MCLOSE + 0);
+ if (!re_mult_next("\\ze")) {
+ return NULL;
+ }
+ break;
+
+ default:
+ EMSG_RET_NULL(_("E68: Invalid character after \\z"));
+ }
+ break;
+
+ case Magic('%'):
+ c = no_Magic(getchr());
+ switch (c) {
+ // () without a back reference
+ case '(':
+ if (one_exactly) {
+ EMSG_ONE_RET_NULL;
+ }
+ ret = reg(REG_NPAREN, &flags);
+ if (ret == NULL) {
+ return NULL;
+ }
+ *flagp |= flags & (HASWIDTH | SPSTART | HASNL | HASLOOKBH);
+ break;
+
+ // Catch \%^ and \%$ regardless of where they appear in the
+ // pattern -- regardless of whether or not it makes sense.
+ case '^':
+ ret = regnode(RE_BOF);
+ break;
+
+ case '$':
+ ret = regnode(RE_EOF);
+ break;
+
+ case '#':
+ ret = regnode(CURSOR);
+ break;
+
+ case 'V':
+ ret = regnode(RE_VISUAL);
+ break;
+
+ case 'C':
+ ret = regnode(RE_COMPOSING);
+ break;
+
+ // \%[abc]: Emit as a list of branches, all ending at the last
+ // branch which matches nothing.
+ case '[':
+ if (one_exactly) { // doesn't nest
+ EMSG_ONE_RET_NULL;
+ }
+ {
+ char_u *lastbranch;
+ char_u *lastnode = NULL;
+ char_u *br;
+
+ ret = NULL;
+ while ((c = getchr()) != ']') {
+ if (c == NUL) {
+ EMSG2_RET_NULL(_(e_missing_sb),
+ reg_magic == MAGIC_ALL);
+ }
+ br = regnode(BRANCH);
+ if (ret == NULL) {
+ ret = br;
+ } else {
+ regtail(lastnode, br);
+ if (reg_toolong) {
+ return NULL;
+ }
+ }
+
+ ungetchr();
+ one_exactly = true;
+ lastnode = regatom(flagp);
+ one_exactly = false;
+ if (lastnode == NULL) {
+ return NULL;
+ }
+ }
+ if (ret == NULL) {
+ EMSG2_RET_NULL(_(e_empty_sb),
+ reg_magic == MAGIC_ALL);
+ }
+ lastbranch = regnode(BRANCH);
+ br = regnode(NOTHING);
+ if (ret != JUST_CALC_SIZE) {
+ regtail(lastnode, br);
+ regtail(lastbranch, br);
+ // connect all branches to the NOTHING
+ // branch at the end
+ for (br = ret; br != lastnode;) {
+ if (OP(br) == BRANCH) {
+ regtail(br, lastbranch);
+ if (reg_toolong) {
+ return NULL;
+ }
+ br = OPERAND(br);
+ } else {
+ br = regnext(br);
+ }
+ }
+ }
+ *flagp &= ~(HASWIDTH | SIMPLE);
+ break;
+ }
+
+ case 'd': // %d123 decimal
+ case 'o': // %o123 octal
+ case 'x': // %xab hex 2
+ case 'u': // %uabcd hex 4
+ case 'U': // %U1234abcd hex 8
+ {
+ int64_t i;
+
+ switch (c) {
+ case 'd':
+ i = getdecchrs(); break;
+ case 'o':
+ i = getoctchrs(); break;
+ case 'x':
+ i = gethexchrs(2); break;
+ case 'u':
+ i = gethexchrs(4); break;
+ case 'U':
+ i = gethexchrs(8); break;
+ default:
+ i = -1; break;
+ }
+
+ if (i < 0 || i > INT_MAX) {
+ EMSG2_RET_NULL(_("E678: Invalid character after %s%%[dxouU]"),
+ reg_magic == MAGIC_ALL);
+ }
+ if (use_multibytecode(i)) {
+ ret = regnode(MULTIBYTECODE);
+ } else {
+ ret = regnode(EXACTLY);
+ }
+ if (i == 0) {
+ regc(0x0a);
+ } else {
+ regmbc(i);
+ }
+ regc(NUL);
+ *flagp |= HASWIDTH;
+ break;
+ }
+
+ default:
+ if (ascii_isdigit(c) || c == '<' || c == '>' || c == '\'' || c == '.') {
+ uint32_t n = 0;
+ int cmp;
+ bool cur = false;
+
+ cmp = c;
+ if (cmp == '<' || cmp == '>') {
+ c = getchr();
+ }
+ if (no_Magic(c) == '.') {
+ cur = true;
+ c = getchr();
+ }
+ while (ascii_isdigit(c)) {
+ n = n * 10 + (uint32_t)(c - '0');
+ c = getchr();
+ }
+ if (c == '\'' && n == 0) {
+ // "\%'m", "\%<'m" and "\%>'m": Mark
+ c = getchr();
+ ret = regnode(RE_MARK);
+ if (ret == JUST_CALC_SIZE) {
+ regsize += 2;
+ } else {
+ *regcode++ = c;
+ *regcode++ = cmp;
+ }
+ break;
+ } else if (c == 'l' || c == 'c' || c == 'v') {
+ if (cur && n) {
+ semsg(_(e_regexp_number_after_dot_pos_search), no_Magic(c));
+ rc_did_emsg = true;
+ return NULL;
+ }
+ if (c == 'l') {
+ if (cur) {
+ n = curwin->w_cursor.lnum;
+ }
+ ret = regnode(RE_LNUM);
+ if (save_prev_at_start) {
+ at_start = true;
+ }
+ } else if (c == 'c') {
+ if (cur) {
+ n = curwin->w_cursor.col;
+ n++;
+ }
+ ret = regnode(RE_COL);
+ } else {
+ if (cur) {
+ colnr_T vcol = 0;
+ getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &vcol);
+ n = ++vcol;
+ }
+ ret = regnode(RE_VCOL);
+ }
+ if (ret == JUST_CALC_SIZE) {
+ regsize += 5;
+ } else {
+ // put the number and the optional
+ // comparator after the opcode
+ regcode = re_put_uint32(regcode, n);
+ *regcode++ = cmp;
+ }
+ break;
+ }
+ }
+
+ EMSG2_RET_NULL(_("E71: Invalid character after %s%%"),
+ reg_magic == MAGIC_ALL);
+ }
+ break;
+
+ case Magic('['):
+collection:
+ {
+ char_u *lp;
+
+ // If there is no matching ']', we assume the '[' is a normal
+ // character. This makes 'incsearch' and ":help [" work.
+ lp = skip_anyof(regparse);
+ if (*lp == ']') { // there is a matching ']'
+ int startc = -1; // > 0 when next '-' is a range
+ int endc;
+
+ // In a character class, different parsing rules apply.
+ // Not even \ is special anymore, nothing is.
+ if (*regparse == '^') { // Complement of range.
+ ret = regnode(ANYBUT + extra);
+ regparse++;
+ } else {
+ ret = regnode(ANYOF + extra);
+ }
+
+ // At the start ']' and '-' mean the literal character.
+ if (*regparse == ']' || *regparse == '-') {
+ startc = *regparse;
+ regc(*regparse++);
+ }
+
+ while (*regparse != NUL && *regparse != ']') {
+ if (*regparse == '-') {
+ ++regparse;
+ // The '-' is not used for a range at the end and
+ // after or before a '\n'.
+ if (*regparse == ']' || *regparse == NUL
+ || startc == -1
+ || (regparse[0] == '\\' && regparse[1] == 'n')) {
+ regc('-');
+ startc = '-'; // [--x] is a range
+ } else {
+ // Also accept "a-[.z.]"
+ endc = 0;
+ if (*regparse == '[') {
+ endc = get_coll_element(&regparse);
+ }
+ if (endc == 0) {
+ endc = mb_ptr2char_adv((const char_u **)&regparse);
+ }
+
+ // Handle \o40, \x20 and \u20AC style sequences
+ if (endc == '\\' && !reg_cpo_lit) {
+ endc = coll_get_char();
+ }
+
+ if (startc > endc) {
+ EMSG_RET_NULL(_(e_reverse_range));
+ }
+ if (utf_char2len(startc) > 1
+ || utf_char2len(endc) > 1) {
+ // Limit to a range of 256 chars
+ if (endc > startc + 256) {
+ EMSG_RET_NULL(_(e_large_class));
+ }
+ while (++startc <= endc) {
+ regmbc(startc);
+ }
+ } else {
+ while (++startc <= endc) {
+ regc(startc);
+ }
+ }
+ startc = -1;
+ }
+ }
+ // Only "\]", "\^", "\]" and "\\" are special in Vi. Vim
+ // accepts "\t", "\e", etc., but only when the 'l' flag in
+ // 'cpoptions' is not included.
+ else if (*regparse == '\\'
+ && (vim_strchr(REGEXP_INRANGE, regparse[1]) != NULL
+ || (!reg_cpo_lit
+ && vim_strchr(REGEXP_ABBR,
+ regparse[1]) != NULL))) {
+ regparse++;
+ if (*regparse == 'n') {
+ // '\n' in range: also match NL
+ if (ret != JUST_CALC_SIZE) {
+ // Using \n inside [^] does not change what
+ // matches. "[^\n]" is the same as ".".
+ if (*ret == ANYOF) {
+ *ret = ANYOF + ADD_NL;
+ *flagp |= HASNL;
+ }
+ // else: must have had a \n already
+ }
+ regparse++;
+ startc = -1;
+ } else if (*regparse == 'd'
+ || *regparse == 'o'
+ || *regparse == 'x'
+ || *regparse == 'u'
+ || *regparse == 'U') {
+ startc = coll_get_char();
+ if (startc == 0) {
+ regc(0x0a);
+ } else {
+ regmbc(startc);
+ }
+ } else {
+ startc = backslash_trans(*regparse++);
+ regc(startc);
+ }
+ } else if (*regparse == '[') {
+ int c_class;
+ int cu;
+
+ c_class = get_char_class(&regparse);
+ startc = -1;
+ // Characters assumed to be 8 bits!
+ switch (c_class) {
+ case CLASS_NONE:
+ c_class = get_equi_class(&regparse);
+ if (c_class != 0) {
+ // produce equivalence class
+ reg_equi_class(c_class);
+ } else if ((c_class =
+ get_coll_element(&regparse)) != 0) {
+ // produce a collating element
+ regmbc(c_class);
+ } else {
+ // literal '[', allow [[-x] as a range
+ startc = *regparse++;
+ regc(startc);
+ }
+ break;
+ case CLASS_ALNUM:
+ for (cu = 1; cu < 128; cu++) {
+ if (isalnum(cu)) {
+ regmbc(cu);
+ }
+ }
+ break;
+ case CLASS_ALPHA:
+ for (cu = 1; cu < 128; cu++) {
+ if (isalpha(cu)) {
+ regmbc(cu);
+ }
+ }
+ break;
+ case CLASS_BLANK:
+ regc(' ');
+ regc('\t');
+ break;
+ case CLASS_CNTRL:
+ for (cu = 1; cu <= 127; cu++) {
+ if (iscntrl(cu)) {
+ regmbc(cu);
+ }
+ }
+ break;
+ case CLASS_DIGIT:
+ for (cu = 1; cu <= 127; cu++) {
+ if (ascii_isdigit(cu)) {
+ regmbc(cu);
+ }
+ }
+ break;
+ case CLASS_GRAPH:
+ for (cu = 1; cu <= 127; cu++) {
+ if (isgraph(cu)) {
+ regmbc(cu);
+ }
+ }
+ break;
+ case CLASS_LOWER:
+ for (cu = 1; cu <= 255; cu++) {
+ if (mb_islower(cu) && cu != 170 && cu != 186) {
+ regmbc(cu);
+ }
+ }
+ break;
+ case CLASS_PRINT:
+ for (cu = 1; cu <= 255; cu++) {
+ if (vim_isprintc(cu)) {
+ regmbc(cu);
+ }
+ }
+ break;
+ case CLASS_PUNCT:
+ for (cu = 1; cu < 128; cu++) {
+ if (ispunct(cu)) {
+ regmbc(cu);
+ }
+ }
+ break;
+ case CLASS_SPACE:
+ for (cu = 9; cu <= 13; cu++) {
+ regc(cu);
+ }
+ regc(' ');
+ break;
+ case CLASS_UPPER:
+ for (cu = 1; cu <= 255; cu++) {
+ if (mb_isupper(cu)) {
+ regmbc(cu);
+ }
+ }
+ break;
+ case CLASS_XDIGIT:
+ for (cu = 1; cu <= 255; cu++) {
+ if (ascii_isxdigit(cu)) {
+ regmbc(cu);
+ }
+ }
+ break;
+ case CLASS_TAB:
+ regc('\t');
+ break;
+ case CLASS_RETURN:
+ regc('\r');
+ break;
+ case CLASS_BACKSPACE:
+ regc('\b');
+ break;
+ case CLASS_ESCAPE:
+ regc(ESC);
+ break;
+ case CLASS_IDENT:
+ for (cu = 1; cu <= 255; cu++) {
+ if (vim_isIDc(cu)) {
+ regmbc(cu);
+ }
+ }
+ break;
+ case CLASS_KEYWORD:
+ for (cu = 1; cu <= 255; cu++) {
+ if (reg_iswordc(cu)) {
+ regmbc(cu);
+ }
+ }
+ break;
+ case CLASS_FNAME:
+ for (cu = 1; cu <= 255; cu++) {
+ if (vim_isfilec(cu)) {
+ regmbc(cu);
+ }
+ }
+ break;
+ }
+ } else {
+ // produce a multibyte character, including any
+ // following composing characters.
+ startc = utf_ptr2char((char *)regparse);
+ int len = utfc_ptr2len((char *)regparse);
+ if (utf_char2len(startc) != len) {
+ // composing chars
+ startc = -1;
+ }
+ while (--len >= 0) {
+ regc(*regparse++);
+ }
+ }
+ }
+ regc(NUL);
+ prevchr_len = 1; // last char was the ']'
+ if (*regparse != ']') {
+ EMSG_RET_NULL(_(e_toomsbra)); // Cannot happen?
+ }
+ skipchr(); // let's be friends with the lexer again
+ *flagp |= HASWIDTH | SIMPLE;
+ break;
+ } else if (reg_strict) {
+ EMSG2_RET_NULL(_(e_missingbracket), reg_magic > MAGIC_OFF);
+ }
+ }
+ FALLTHROUGH;
+
+ default: {
+ int len;
+
+ // A multi-byte character is handled as a separate atom if it's
+ // before a multi and when it's a composing char.
+ if (use_multibytecode(c)) {
+do_multibyte:
+ ret = regnode(MULTIBYTECODE);
+ regmbc(c);
+ *flagp |= HASWIDTH | SIMPLE;
+ break;
+ }
+
+ ret = regnode(EXACTLY);
+
+ // Append characters as long as:
+ // - there is no following multi, we then need the character in
+ // front of it as a single character operand
+ // - not running into a Magic character
+ // - "one_exactly" is not set
+ // But always emit at least one character. Might be a Multi,
+ // e.g., a "[" without matching "]".
+ for (len = 0; c != NUL && (len == 0
+ || (re_multi_type(peekchr()) == NOT_MULTI
+ && !one_exactly
+ && !is_Magic(c))); ++len) {
+ c = no_Magic(c);
+ {
+ regmbc(c);
+ {
+ int l;
+
+ // Need to get composing character too.
+ for (;;) {
+ l = utf_ptr2len((char *)regparse);
+ if (!utf_composinglike(regparse, regparse + l)) {
+ break;
+ }
+ regmbc(utf_ptr2char((char *)regparse));
+ skipchr();
+ }
+ }
+ }
+ c = getchr();
+ }
+ ungetchr();
+
+ regc(NUL);
+ *flagp |= HASWIDTH;
+ if (len == 1) {
+ *flagp |= SIMPLE;
+ }
+ }
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * Parse something followed by possible [*+=].
+ *
+ * Note that the branching code sequences used for = and the general cases
+ * of * and + are somewhat optimized: they use the same NOTHING node as
+ * both the endmarker for their branch list and the body of the last branch.
+ * It might seem that this node could be dispensed with entirely, but the
+ * endmarker role is not redundant.
+ */
+static char_u *regpiece(int *flagp)
+{
+ char_u *ret;
+ int op;
+ char_u *next;
+ int flags;
+ long minval;
+ long maxval;
+
+ ret = regatom(&flags);
+ if (ret == NULL) {
+ return NULL;
+ }
+
+ op = peekchr();
+ if (re_multi_type(op) == NOT_MULTI) {
+ *flagp = flags;
+ return ret;
+ }
+ // default flags
+ *flagp = (WORST | SPSTART | (flags & (HASNL | HASLOOKBH)));
+
+ skipchr();
+ switch (op) {
+ case Magic('*'):
+ if (flags & SIMPLE) {
+ reginsert(STAR, ret);
+ } else {
+ // Emit x* as (x&|), where & means "self".
+ reginsert(BRANCH, ret); // Either x
+ regoptail(ret, regnode(BACK)); // and loop
+ regoptail(ret, ret); // back
+ regtail(ret, regnode(BRANCH)); // or
+ regtail(ret, regnode(NOTHING)); // null.
+ }
+ break;
+
+ case Magic('+'):
+ if (flags & SIMPLE) {
+ reginsert(PLUS, ret);
+ } else {
+ // Emit x+ as x(&|), where & means "self".
+ next = regnode(BRANCH); // Either
+ regtail(ret, next);
+ regtail(regnode(BACK), ret); // loop back
+ regtail(next, regnode(BRANCH)); // or
+ regtail(ret, regnode(NOTHING)); // null.
+ }
+ *flagp = (WORST | HASWIDTH | (flags & (HASNL | HASLOOKBH)));
+ break;
+
+ case Magic('@'): {
+ int lop = END;
+ int64_t nr = getdecchrs();
+
+ switch (no_Magic(getchr())) {
+ case '=':
+ lop = MATCH; break; // \@=
+ case '!':
+ lop = NOMATCH; break; // \@!
+ case '>':
+ lop = SUBPAT; break; // \@>
+ case '<':
+ switch (no_Magic(getchr())) {
+ case '=':
+ lop = BEHIND; break; // \@<=
+ case '!':
+ lop = NOBEHIND; break; // \@<!
+ }
+ }
+ if (lop == END) {
+ EMSG2_RET_NULL(_("E59: invalid character after %s@"),
+ reg_magic == MAGIC_ALL);
+ }
+ // Look behind must match with behind_pos.
+ if (lop == BEHIND || lop == NOBEHIND) {
+ regtail(ret, regnode(BHPOS));
+ *flagp |= HASLOOKBH;
+ }
+ regtail(ret, regnode(END)); // operand ends
+ if (lop == BEHIND || lop == NOBEHIND) {
+ if (nr < 0) {
+ nr = 0; // no limit is same as zero limit
+ }
+ reginsert_nr(lop, (uint32_t)nr, ret);
+ } else {
+ reginsert(lop, ret);
+ }
+ break;
+ }
+
+ case Magic('?'):
+ case Magic('='):
+ // Emit x= as (x|)
+ reginsert(BRANCH, ret); // Either x
+ regtail(ret, regnode(BRANCH)); // or
+ next = regnode(NOTHING); // null.
+ regtail(ret, next);
+ regoptail(ret, next);
+ break;
+
+ case Magic('{'):
+ if (!read_limits(&minval, &maxval)) {
+ return NULL;
+ }
+ if (flags & SIMPLE) {
+ reginsert(BRACE_SIMPLE, ret);
+ reginsert_limits(BRACE_LIMITS, minval, maxval, ret);
+ } else {
+ if (num_complex_braces >= 10) {
+ EMSG2_RET_NULL(_("E60: Too many complex %s{...}s"),
+ reg_magic == MAGIC_ALL);
+ }
+ reginsert(BRACE_COMPLEX + num_complex_braces, ret);
+ regoptail(ret, regnode(BACK));
+ regoptail(ret, ret);
+ reginsert_limits(BRACE_LIMITS, minval, maxval, ret);
+ ++num_complex_braces;
+ }
+ if (minval > 0 && maxval > 0) {
+ *flagp = (HASWIDTH | (flags & (HASNL | HASLOOKBH)));
+ }
+ break;
+ }
+ if (re_multi_type(peekchr()) != NOT_MULTI) {
+ // Can't have a multi follow a multi.
+ if (peekchr() == Magic('*')) {
+ EMSG2_RET_NULL(_("E61: Nested %s*"), reg_magic >= MAGIC_ON);
+ }
+ EMSG3_RET_NULL(_("E62: Nested %s%c"), reg_magic == MAGIC_ALL, no_Magic(peekchr()));
+ }
+
+ return ret;
+}
+
+/*
+ * Parse one alternative of an | or & operator.
+ * Implements the concatenation operator.
+ */
+static char_u *regconcat(int *flagp)
+{
+ char_u *first = NULL;
+ char_u *chain = NULL;
+ char_u *latest;
+ int flags;
+ int cont = true;
+
+ *flagp = WORST; // Tentatively.
+
+ while (cont) {
+ switch (peekchr()) {
+ case NUL:
+ case Magic('|'):
+ case Magic('&'):
+ case Magic(')'):
+ cont = false;
+ break;
+ case Magic('Z'):
+ regflags |= RF_ICOMBINE;
+ skipchr_keepstart();
+ break;
+ case Magic('c'):
+ regflags |= RF_ICASE;
+ skipchr_keepstart();
+ break;
+ case Magic('C'):
+ regflags |= RF_NOICASE;
+ skipchr_keepstart();
+ break;
+ case Magic('v'):
+ reg_magic = MAGIC_ALL;
+ skipchr_keepstart();
+ curchr = -1;
+ break;
+ case Magic('m'):
+ reg_magic = MAGIC_ON;
+ skipchr_keepstart();
+ curchr = -1;
+ break;
+ case Magic('M'):
+ reg_magic = MAGIC_OFF;
+ skipchr_keepstart();
+ curchr = -1;
+ break;
+ case Magic('V'):
+ reg_magic = MAGIC_NONE;
+ skipchr_keepstart();
+ curchr = -1;
+ break;
+ default:
+ latest = regpiece(&flags);
+ if (latest == NULL || reg_toolong) {
+ return NULL;
+ }
+ *flagp |= flags & (HASWIDTH | HASNL | HASLOOKBH);
+ if (chain == NULL) { // First piece.
+ *flagp |= flags & SPSTART;
+ } else {
+ regtail(chain, latest);
+ }
+ chain = latest;
+ if (first == NULL) {
+ first = latest;
+ }
+ break;
+ }
+ }
+ if (first == NULL) { // Loop ran zero times.
+ first = regnode(NOTHING);
+ }
+ return first;
+}
+
+/*
+ * Parse one alternative of an | operator.
+ * Implements the & operator.
+ */
+static char_u *regbranch(int *flagp)
+{
+ char_u *ret;
+ char_u *chain = NULL;
+ char_u *latest;
+ int flags;
+
+ *flagp = WORST | HASNL; // Tentatively.
+
+ ret = regnode(BRANCH);
+ for (;;) {
+ latest = regconcat(&flags);
+ if (latest == NULL) {
+ return NULL;
+ }
+ // If one of the branches has width, the whole thing has. If one of
+ // the branches anchors at start-of-line, the whole thing does.
+ // If one of the branches uses look-behind, the whole thing does.
+ *flagp |= flags & (HASWIDTH | SPSTART | HASLOOKBH);
+ // If one of the branches doesn't match a line-break, the whole thing
+ // doesn't.
+ *flagp &= ~HASNL | (flags & HASNL);
+ if (chain != NULL) {
+ regtail(chain, latest);
+ }
+ if (peekchr() != Magic('&')) {
+ break;
+ }
+ skipchr();
+ regtail(latest, regnode(END)); // operand ends
+ if (reg_toolong) {
+ break;
+ }
+ reginsert(MATCH, latest);
+ chain = latest;
+ }
+
+ return ret;
+}
+
+/// Parse regular expression, i.e. main body or parenthesized thing.
+///
+/// Caller must absorb opening parenthesis.
+///
+/// Combining parenthesis handling with the base level of regular expression
+/// is a trifle forced, but the need to tie the tails of the branches to what
+/// follows makes it hard to avoid.
+///
+/// @param paren REG_NOPAREN, REG_PAREN, REG_NPAREN or REG_ZPAREN
+static char_u *reg(int paren, int *flagp)
+{
+ char_u *ret;
+ char_u *br;
+ char_u *ender;
+ int parno = 0;
+ int flags;
+
+ *flagp = HASWIDTH; // Tentatively.
+
+ if (paren == REG_ZPAREN) {
+ // Make a ZOPEN node.
+ if (regnzpar >= NSUBEXP) {
+ EMSG_RET_NULL(_("E50: Too many \\z("));
+ }
+ parno = regnzpar;
+ regnzpar++;
+ ret = regnode(ZOPEN + parno);
+ } else if (paren == REG_PAREN) {
+ // Make a MOPEN node.
+ if (regnpar >= NSUBEXP) {
+ EMSG2_RET_NULL(_("E51: Too many %s("), reg_magic == MAGIC_ALL);
+ }
+ parno = regnpar;
+ ++regnpar;
+ ret = regnode(MOPEN + parno);
+ } else if (paren == REG_NPAREN) {
+ // Make a NOPEN node.
+ ret = regnode(NOPEN);
+ } else {
+ ret = NULL;
+ }
+
+ // Pick up the branches, linking them together.
+ br = regbranch(&flags);
+ if (br == NULL) {
+ return NULL;
+ }
+ if (ret != NULL) {
+ regtail(ret, br); // [MZ]OPEN -> first.
+ } else {
+ ret = br;
+ }
+ // If one of the branches can be zero-width, the whole thing can.
+ // If one of the branches has * at start or matches a line-break, the
+ // whole thing can.
+ if (!(flags & HASWIDTH)) {
+ *flagp &= ~HASWIDTH;
+ }
+ *flagp |= flags & (SPSTART | HASNL | HASLOOKBH);
+ while (peekchr() == Magic('|')) {
+ skipchr();
+ br = regbranch(&flags);
+ if (br == NULL || reg_toolong) {
+ return NULL;
+ }
+ regtail(ret, br); // BRANCH -> BRANCH.
+ if (!(flags & HASWIDTH)) {
+ *flagp &= ~HASWIDTH;
+ }
+ *flagp |= flags & (SPSTART | HASNL | HASLOOKBH);
+ }
+
+ // Make a closing node, and hook it on the end.
+ ender = regnode(paren == REG_ZPAREN ? ZCLOSE + parno :
+ paren == REG_PAREN ? MCLOSE + parno :
+ paren == REG_NPAREN ? NCLOSE : END);
+ regtail(ret, ender);
+
+ // Hook the tails of the branches to the closing node.
+ for (br = ret; br != NULL; br = regnext(br)) {
+ regoptail(br, ender);
+ }
+
+ // Check for proper termination.
+ if (paren != REG_NOPAREN && getchr() != Magic(')')) {
+ if (paren == REG_ZPAREN) {
+ EMSG_RET_NULL(_("E52: Unmatched \\z("));
+ } else if (paren == REG_NPAREN) {
+ EMSG2_RET_NULL(_(e_unmatchedpp), reg_magic == MAGIC_ALL);
+ } else {
+ EMSG2_RET_NULL(_(e_unmatchedp), reg_magic == MAGIC_ALL);
+ }
+ } else if (paren == REG_NOPAREN && peekchr() != NUL) {
+ if (curchr == Magic(')')) {
+ EMSG2_RET_NULL(_(e_unmatchedpar), reg_magic == MAGIC_ALL);
+ } else {
+ EMSG_RET_NULL(_(e_trailing)); // "Can't happen".
+ }
+ // NOTREACHED
+ }
+ // Here we set the flag allowing back references to this set of
+ // parentheses.
+ if (paren == REG_PAREN) {
+ had_endbrace[parno] = true; // have seen the close paren
+ }
+ return ret;
+}
+
+/*
+ * bt_regcomp() - compile a regular expression into internal code for the
+ * traditional back track matcher.
+ * Returns the program in allocated space. Returns NULL for an error.
+ *
+ * We can't allocate space until we know how big the compiled form will be,
+ * but we can't compile it (and thus know how big it is) until we've got a
+ * place to put the code. So we cheat: we compile it twice, once with code
+ * generation turned off and size counting turned on, and once "for real".
+ * This also means that we don't allocate space until we are sure that the
+ * thing really will compile successfully, and we never have to move the
+ * code and thus invalidate pointers into it. (Note that it has to be in
+ * one piece because free() must be able to free it all.)
+ *
+ * Whether upper/lower case is to be ignored is decided when executing the
+ * program, it does not matter here.
+ *
+ * Beware that the optimization-preparation code in here knows about some
+ * of the structure of the compiled regexp.
+ * "re_flags": RE_MAGIC and/or RE_STRING.
+ */
+static regprog_T *bt_regcomp(char_u *expr, int re_flags)
+{
+ char_u *scan;
+ char_u *longest;
+ int len;
+ int flags;
+
+ if (expr == NULL) {
+ IEMSG_RET_NULL(_(e_null));
+ }
+
+ init_class_tab();
+
+ // First pass: determine size, legality.
+ regcomp_start(expr, re_flags);
+ regcode = JUST_CALC_SIZE;
+ regc(REGMAGIC);
+ if (reg(REG_NOPAREN, &flags) == NULL) {
+ return NULL;
+ }
+
+ // Allocate space.
+ bt_regprog_T *r = xmalloc(sizeof(bt_regprog_T) + regsize);
+ r->re_in_use = false;
+
+ // Second pass: emit code.
+ regcomp_start(expr, re_flags);
+ regcode = r->program;
+ regc(REGMAGIC);
+ if (reg(REG_NOPAREN, &flags) == NULL || reg_toolong) {
+ xfree(r);
+ if (reg_toolong) {
+ EMSG_RET_NULL(_("E339: Pattern too long"));
+ }
+ return NULL;
+ }
+
+ // Dig out information for optimizations.
+ r->regstart = NUL; // Worst-case defaults.
+ r->reganch = 0;
+ r->regmust = NULL;
+ r->regmlen = 0;
+ r->regflags = regflags;
+ if (flags & HASNL) {
+ r->regflags |= RF_HASNL;
+ }
+ if (flags & HASLOOKBH) {
+ r->regflags |= RF_LOOKBH;
+ }
+ // Remember whether this pattern has any \z specials in it.
+ r->reghasz = re_has_z;
+ scan = r->program + 1; // First BRANCH.
+ if (OP(regnext(scan)) == END) { // Only one top-level choice.
+ scan = OPERAND(scan);
+
+ // Starting-point info.
+ if (OP(scan) == BOL || OP(scan) == RE_BOF) {
+ r->reganch++;
+ scan = regnext(scan);
+ }
+
+ if (OP(scan) == EXACTLY) {
+ r->regstart = utf_ptr2char((char *)OPERAND(scan));
+ } else if (OP(scan) == BOW
+ || OP(scan) == EOW
+ || OP(scan) == NOTHING
+ || OP(scan) == MOPEN + 0 || OP(scan) == NOPEN
+ || OP(scan) == MCLOSE + 0 || OP(scan) == NCLOSE) {
+ char_u *regnext_scan = regnext(scan);
+ if (OP(regnext_scan) == EXACTLY) {
+ r->regstart = utf_ptr2char((char *)OPERAND(regnext_scan));
+ }
+ }
+
+ // If there's something expensive in the r.e., find the longest
+ // literal string that must appear and make it the regmust. Resolve
+ // ties in favor of later strings, since the regstart check works
+ // with the beginning of the r.e. and avoiding duplication
+ // strengthens checking. Not a strong reason, but sufficient in the
+ // absence of others.
+
+ // When the r.e. starts with BOW, it is faster to look for a regmust
+ // first. Used a lot for "#" and "*" commands. (Added by mool).
+ if ((flags & SPSTART || OP(scan) == BOW || OP(scan) == EOW)
+ && !(flags & HASNL)) {
+ longest = NULL;
+ len = 0;
+ for (; scan != NULL; scan = regnext(scan)) {
+ if (OP(scan) == EXACTLY && STRLEN(OPERAND(scan)) >= (size_t)len) {
+ longest = OPERAND(scan);
+ len = (int)STRLEN(OPERAND(scan));
+ }
+ }
+ r->regmust = longest;
+ r->regmlen = len;
+ }
+ }
+#ifdef BT_REGEXP_DUMP
+ regdump(expr, r);
+#endif
+ r->engine = &bt_regengine;
+ return (regprog_T *)r;
+}
+
+/*
+ * Check if during the previous call to vim_regcomp the EOL item "$" has been
+ * found. This is messy, but it works fine.
+ */
+int vim_regcomp_had_eol(void)
+{
+ return had_eol;
+}
+
+/*
+ * Get a number after a backslash that is inside [].
+ * When nothing is recognized return a backslash.
+ */
+static int coll_get_char(void)
+{
+ int64_t nr = -1;
+
+ switch (*regparse++) {
+ case 'd':
+ nr = getdecchrs(); break;
+ case 'o':
+ nr = getoctchrs(); break;
+ case 'x':
+ nr = gethexchrs(2); break;
+ case 'u':
+ nr = gethexchrs(4); break;
+ case 'U':
+ nr = gethexchrs(8); break;
+ }
+ if (nr < 0 || nr > INT_MAX) {
+ // If getting the number fails be backwards compatible: the character
+ // is a backslash.
+ regparse--;
+ nr = '\\';
+ }
+ return nr;
+}
+
+/*
+ * Free a compiled regexp program, returned by bt_regcomp().
+ */
+static void bt_regfree(regprog_T *prog)
+{
+ xfree(prog);
+}
+
+#define ADVANCE_REGINPUT() MB_PTR_ADV(rex.input)
+
+/*
+ * The arguments from BRACE_LIMITS are stored here. They are actually local
+ * to regmatch(), but they are here to reduce the amount of stack space used
+ * (it can be called recursively many times).
+ */
+static long bl_minval;
+static long bl_maxval;
+
+// Save the input line and position in a regsave_T.
+static void reg_save(regsave_T *save, garray_T *gap)
+ FUNC_ATTR_NONNULL_ALL
+{
+ if (REG_MULTI) {
+ save->rs_u.pos.col = (colnr_T)(rex.input - rex.line);
+ save->rs_u.pos.lnum = rex.lnum;
+ } else {
+ save->rs_u.ptr = rex.input;
+ }
+ save->rs_len = gap->ga_len;
+}
+
+// Restore the input line and position from a regsave_T.
+static void reg_restore(regsave_T *save, garray_T *gap)
+ FUNC_ATTR_NONNULL_ALL
+{
+ if (REG_MULTI) {
+ if (rex.lnum != save->rs_u.pos.lnum) {
+ // only call reg_getline() when the line number changed to save
+ // a bit of time
+ rex.lnum = save->rs_u.pos.lnum;
+ rex.line = reg_getline(rex.lnum);
+ }
+ rex.input = rex.line + save->rs_u.pos.col;
+ } else {
+ rex.input = save->rs_u.ptr;
+ }
+ gap->ga_len = save->rs_len;
+}
+
+// Return true if current position is equal to saved position.
+static bool reg_save_equal(const regsave_T *save)
+ FUNC_ATTR_NONNULL_ALL
+{
+ if (REG_MULTI) {
+ return rex.lnum == save->rs_u.pos.lnum
+ && rex.input == rex.line + save->rs_u.pos.col;
+ }
+ return rex.input == save->rs_u.ptr;
+}
+
+// Save the sub-expressions before attempting a match.
+#define save_se(savep, posp, pp) \
+ REG_MULTI ? save_se_multi((savep), (posp)) : save_se_one((savep), (pp))
+
+// After a failed match restore the sub-expressions.
+#define restore_se(savep, posp, pp) { \
+ if (REG_MULTI) /* NOLINT(readability/braces) */ \
+ *(posp) = (savep)->se_u.pos; \
+ else /* NOLINT */ \
+ *(pp) = (savep)->se_u.ptr; }
+
+/*
+ * Tentatively set the sub-expression start to the current position (after
+ * calling regmatch() they will have changed). Need to save the existing
+ * values for when there is no match.
+ * Use se_save() to use pointer (save_se_multi()) or position (save_se_one()),
+ * depending on REG_MULTI.
+ */
+static void save_se_multi(save_se_T *savep, lpos_T *posp)
+{
+ savep->se_u.pos = *posp;
+ posp->lnum = rex.lnum;
+ posp->col = (colnr_T)(rex.input - rex.line);
+}
+
+static void save_se_one(save_se_T *savep, char_u **pp)
+{
+ savep->se_u.ptr = *pp;
+ *pp = rex.input;
+}
+
+/// regrepeat - repeatedly match something simple, return how many.
+/// Advances rex.input (and rex.lnum) to just after the matched chars.
+///
+/// @param maxcount maximum number of matches allowed
+static int regrepeat(char_u *p, long maxcount)
+{
+ long count = 0;
+ char_u *opnd;
+ int mask;
+ int testval = 0;
+
+ char_u *scan = rex.input; // Make local copy of rex.input for speed.
+ opnd = OPERAND(p);
+ switch (OP(p)) {
+ case ANY:
+ case ANY + ADD_NL:
+ while (count < maxcount) {
+ // Matching anything means we continue until end-of-line (or
+ // end-of-file for ANY + ADD_NL), only limited by maxcount.
+ while (*scan != NUL && count < maxcount) {
+ count++;
+ MB_PTR_ADV(scan);
+ }
+ if (!REG_MULTI || !WITH_NL(OP(p)) || rex.lnum > rex.reg_maxline
+ || rex.reg_line_lbr || count == maxcount) {
+ break;
+ }
+ count++; // count the line-break
+ reg_nextline();
+ scan = rex.input;
+ if (got_int) {
+ break;
+ }
+ }
+ break;
+
+ case IDENT:
+ case IDENT + ADD_NL:
+ testval = 1;
+ FALLTHROUGH;
+ case SIDENT:
+ case SIDENT + ADD_NL:
+ while (count < maxcount) {
+ if (vim_isIDc(utf_ptr2char((char *)scan)) && (testval || !ascii_isdigit(*scan))) {
+ MB_PTR_ADV(scan);
+ } else if (*scan == NUL) {
+ if (!REG_MULTI || !WITH_NL(OP(p)) || rex.lnum > rex.reg_maxline
+ || rex.reg_line_lbr) {
+ break;
+ }
+ reg_nextline();
+ scan = rex.input;
+ if (got_int) {
+ break;
+ }
+ } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) {
+ scan++;
+ } else {
+ break;
+ }
+ ++count;
+ }
+ break;
+
+ case KWORD:
+ case KWORD + ADD_NL:
+ testval = 1;
+ FALLTHROUGH;
+ case SKWORD:
+ case SKWORD + ADD_NL:
+ while (count < maxcount) {
+ if (vim_iswordp_buf(scan, rex.reg_buf)
+ && (testval || !ascii_isdigit(*scan))) {
+ MB_PTR_ADV(scan);
+ } else if (*scan == NUL) {
+ if (!REG_MULTI || !WITH_NL(OP(p)) || rex.lnum > rex.reg_maxline
+ || rex.reg_line_lbr) {
+ break;
+ }
+ reg_nextline();
+ scan = rex.input;
+ if (got_int) {
+ break;
+ }
+ } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) {
+ scan++;
+ } else {
+ break;
+ }
+ count++;
+ }
+ break;
+
+ case FNAME:
+ case FNAME + ADD_NL:
+ testval = 1;
+ FALLTHROUGH;
+ case SFNAME:
+ case SFNAME + ADD_NL:
+ while (count < maxcount) {
+ if (vim_isfilec(utf_ptr2char((char *)scan)) && (testval || !ascii_isdigit(*scan))) {
+ MB_PTR_ADV(scan);
+ } else if (*scan == NUL) {
+ if (!REG_MULTI || !WITH_NL(OP(p)) || rex.lnum > rex.reg_maxline
+ || rex.reg_line_lbr) {
+ break;
+ }
+ reg_nextline();
+ scan = rex.input;
+ if (got_int) {
+ break;
+ }
+ } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) {
+ scan++;
+ } else {
+ break;
+ }
+ count++;
+ }
+ break;
+
+ case PRINT:
+ case PRINT + ADD_NL:
+ testval = 1;
+ FALLTHROUGH;
+ case SPRINT:
+ case SPRINT + ADD_NL:
+ while (count < maxcount) {
+ if (*scan == NUL) {
+ if (!REG_MULTI || !WITH_NL(OP(p)) || rex.lnum > rex.reg_maxline
+ || rex.reg_line_lbr) {
+ break;
+ }
+ reg_nextline();
+ scan = rex.input;
+ if (got_int) {
+ break;
+ }
+ } else if (vim_isprintc(utf_ptr2char((char *)scan)) == 1
+ && (testval || !ascii_isdigit(*scan))) {
+ MB_PTR_ADV(scan);
+ } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) {
+ scan++;
+ } else {
+ break;
+ }
+ count++;
+ }
+ break;
+
+ case WHITE:
+ case WHITE + ADD_NL:
+ testval = mask = RI_WHITE;
+do_class:
+ while (count < maxcount) {
+ int l;
+ if (*scan == NUL) {
+ if (!REG_MULTI || !WITH_NL(OP(p)) || rex.lnum > rex.reg_maxline
+ || rex.reg_line_lbr) {
+ break;
+ }
+ reg_nextline();
+ scan = rex.input;
+ if (got_int) {
+ break;
+ }
+ } else if ((l = utfc_ptr2len((char *)scan)) > 1) {
+ if (testval != 0) {
+ break;
+ }
+ scan += l;
+ } else if ((class_tab[*scan] & mask) == testval) {
+ scan++;
+ } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) {
+ scan++;
+ } else {
+ break;
+ }
+ ++count;
+ }
+ break;
+
+ case NWHITE:
+ case NWHITE + ADD_NL:
+ mask = RI_WHITE;
+ goto do_class;
+ case DIGIT:
+ case DIGIT + ADD_NL:
+ testval = mask = RI_DIGIT;
+ goto do_class;
+ case NDIGIT:
+ case NDIGIT + ADD_NL:
+ mask = RI_DIGIT;
+ goto do_class;
+ case HEX:
+ case HEX + ADD_NL:
+ testval = mask = RI_HEX;
+ goto do_class;
+ case NHEX:
+ case NHEX + ADD_NL:
+ mask = RI_HEX;
+ goto do_class;
+ case OCTAL:
+ case OCTAL + ADD_NL:
+ testval = mask = RI_OCTAL;
+ goto do_class;
+ case NOCTAL:
+ case NOCTAL + ADD_NL:
+ mask = RI_OCTAL;
+ goto do_class;
+ case WORD:
+ case WORD + ADD_NL:
+ testval = mask = RI_WORD;
+ goto do_class;
+ case NWORD:
+ case NWORD + ADD_NL:
+ mask = RI_WORD;
+ goto do_class;
+ case HEAD:
+ case HEAD + ADD_NL:
+ testval = mask = RI_HEAD;
+ goto do_class;
+ case NHEAD:
+ case NHEAD + ADD_NL:
+ mask = RI_HEAD;
+ goto do_class;
+ case ALPHA:
+ case ALPHA + ADD_NL:
+ testval = mask = RI_ALPHA;
+ goto do_class;
+ case NALPHA:
+ case NALPHA + ADD_NL:
+ mask = RI_ALPHA;
+ goto do_class;
+ case LOWER:
+ case LOWER + ADD_NL:
+ testval = mask = RI_LOWER;
+ goto do_class;
+ case NLOWER:
+ case NLOWER + ADD_NL:
+ mask = RI_LOWER;
+ goto do_class;
+ case UPPER:
+ case UPPER + ADD_NL:
+ testval = mask = RI_UPPER;
+ goto do_class;
+ case NUPPER:
+ case NUPPER + ADD_NL:
+ mask = RI_UPPER;
+ goto do_class;
+
+ case EXACTLY: {
+ int cu, cl;
+
+ // This doesn't do a multi-byte character, because a MULTIBYTECODE
+ // would have been used for it. It does handle single-byte
+ // characters, such as latin1.
+ if (rex.reg_ic) {
+ cu = mb_toupper(*opnd);
+ cl = mb_tolower(*opnd);
+ while (count < maxcount && (*scan == cu || *scan == cl)) {
+ count++;
+ scan++;
+ }
+ } else {
+ cu = *opnd;
+ while (count < maxcount && *scan == cu) {
+ count++;
+ scan++;
+ }
+ }
+ break;
+ }
+
+ case MULTIBYTECODE: {
+ int i, len, cf = 0;
+
+ // Safety check (just in case 'encoding' was changed since
+ // compiling the program).
+ if ((len = utfc_ptr2len((char *)opnd)) > 1) {
+ if (rex.reg_ic) {
+ cf = utf_fold(utf_ptr2char((char *)opnd));
+ }
+ while (count < maxcount && utfc_ptr2len((char *)scan) >= len) {
+ for (i = 0; i < len; i++) {
+ if (opnd[i] != scan[i]) {
+ break;
+ }
+ }
+ if (i < len && (!rex.reg_ic
+ || utf_fold(utf_ptr2char((char *)scan)) != cf)) {
+ break;
+ }
+ scan += len;
+ ++count;
+ }
+ }
+ }
+ break;
+
+ case ANYOF:
+ case ANYOF + ADD_NL:
+ testval = 1;
+ FALLTHROUGH;
+
+ case ANYBUT:
+ case ANYBUT + ADD_NL:
+ while (count < maxcount) {
+ int len;
+ if (*scan == NUL) {
+ if (!REG_MULTI || !WITH_NL(OP(p)) || rex.lnum > rex.reg_maxline
+ || rex.reg_line_lbr) {
+ break;
+ }
+ reg_nextline();
+ scan = rex.input;
+ if (got_int) {
+ break;
+ }
+ } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) {
+ scan++;
+ } else if ((len = utfc_ptr2len((char *)scan)) > 1) {
+ if ((cstrchr(opnd, utf_ptr2char((char *)scan)) == NULL) == testval) {
+ break;
+ }
+ scan += len;
+ } else {
+ if ((cstrchr(opnd, *scan) == NULL) == testval) {
+ break;
+ }
+ scan++;
+ }
+ ++count;
+ }
+ break;
+
+ case NEWL:
+ while (count < maxcount
+ && ((*scan == NUL && rex.lnum <= rex.reg_maxline && !rex.reg_line_lbr
+ && REG_MULTI) || (*scan == '\n' && rex.reg_line_lbr))) {
+ count++;
+ if (rex.reg_line_lbr) {
+ ADVANCE_REGINPUT();
+ } else {
+ reg_nextline();
+ }
+ scan = rex.input;
+ if (got_int) {
+ break;
+ }
+ }
+ break;
+
+ default: // Oh dear. Called inappropriately.
+ iemsg(_(e_re_corr));
+#ifdef REGEXP_DEBUG
+ printf("Called regrepeat with op code %d\n", OP(p));
+#endif
+ break;
+ }
+
+ rex.input = scan;
+
+ return (int)count;
+}
+
+/*
+ * Push an item onto the regstack.
+ * Returns pointer to new item. Returns NULL when out of memory.
+ */
+static regitem_T *regstack_push(regstate_T state, char_u *scan)
+{
+ regitem_T *rp;
+
+ if ((long)((unsigned)regstack.ga_len >> 10) >= p_mmp) {
+ emsg(_(e_maxmempat));
+ return NULL;
+ }
+ ga_grow(&regstack, sizeof(regitem_T));
+
+ rp = (regitem_T *)((char *)regstack.ga_data + regstack.ga_len);
+ rp->rs_state = state;
+ rp->rs_scan = scan;
+
+ regstack.ga_len += sizeof(regitem_T);
+ return rp;
+}
+
+/*
+ * Pop an item from the regstack.
+ */
+static void regstack_pop(char_u **scan)
+{
+ regitem_T *rp;
+
+ rp = (regitem_T *)((char *)regstack.ga_data + regstack.ga_len) - 1;
+ *scan = rp->rs_scan;
+
+ regstack.ga_len -= sizeof(regitem_T);
+}
+
+// Save the current subexpr to "bp", so that they can be restored
+// later by restore_subexpr().
+static void save_subexpr(regbehind_T *bp)
+ FUNC_ATTR_NONNULL_ALL
+{
+ // When "rex.need_clear_subexpr" is set we don't need to save the values, only
+ // remember that this flag needs to be set again when restoring.
+ bp->save_need_clear_subexpr = rex.need_clear_subexpr;
+ if (!rex.need_clear_subexpr) {
+ for (int i = 0; i < NSUBEXP; i++) {
+ if (REG_MULTI) {
+ bp->save_start[i].se_u.pos = rex.reg_startpos[i];
+ bp->save_end[i].se_u.pos = rex.reg_endpos[i];
+ } else {
+ bp->save_start[i].se_u.ptr = rex.reg_startp[i];
+ bp->save_end[i].se_u.ptr = rex.reg_endp[i];
+ }
+ }
+ }
+}
+
+// Restore the subexpr from "bp".
+static void restore_subexpr(regbehind_T *bp)
+ FUNC_ATTR_NONNULL_ALL
+{
+ // Only need to restore saved values when they are not to be cleared.
+ rex.need_clear_subexpr = bp->save_need_clear_subexpr;
+ if (!rex.need_clear_subexpr) {
+ for (int i = 0; i < NSUBEXP; i++) {
+ if (REG_MULTI) {
+ rex.reg_startpos[i] = bp->save_start[i].se_u.pos;
+ rex.reg_endpos[i] = bp->save_end[i].se_u.pos;
+ } else {
+ rex.reg_startp[i] = bp->save_start[i].se_u.ptr;
+ rex.reg_endp[i] = bp->save_end[i].se_u.ptr;
+ }
+ }
+ }
+}
+/// Main matching routine
+///
+/// Conceptually the strategy is simple: Check to see whether the current node
+/// matches, push an item onto the regstack and loop to see whether the rest
+/// matches, and then act accordingly. In practice we make some effort to
+/// avoid using the regstack, in particular by going through "ordinary" nodes
+/// (that don't need to know whether the rest of the match failed) by a nested
+/// loop.
+///
+/// @param scan Current node.
+/// @param tm timeout limit or NULL
+/// @param timed_out flag set on timeout or NULL
+///
+/// @return - true when there is a match. Leaves rex.input and rex.lnum
+/// just after the last matched character.
+/// - false when there is no match. Leaves rex.input and rex.lnum in an
+/// undefined state!
+static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out)
+{
+ char_u *next; // Next node.
+ int op;
+ int c;
+ regitem_T *rp;
+ int no;
+ int status; // one of the RA_ values:
+ int tm_count = 0;
+
+ // Make "regstack" and "backpos" empty. They are allocated and freed in
+ // bt_regexec_both() to reduce malloc()/free() calls.
+ regstack.ga_len = 0;
+ backpos.ga_len = 0;
+
+ // Repeat until "regstack" is empty.
+ for (;;) {
+ // Some patterns may take a long time to match, e.g., "\([a-z]\+\)\+Q".
+ // Allow interrupting them with CTRL-C.
+ fast_breakcheck();
+
+#ifdef REGEXP_DEBUG
+ if (scan != NULL && regnarrate) {
+ mch_errmsg((char *)regprop(scan));
+ mch_errmsg("(\n");
+ }
+#endif
+
+ // Repeat for items that can be matched sequentially, without using the
+ // regstack.
+ for (;;) {
+ if (got_int || scan == NULL) {
+ status = RA_FAIL;
+ break;
+ }
+ // Check for timeout once in a 100 times to avoid overhead.
+ if (tm != NULL && ++tm_count == 100) {
+ tm_count = 0;
+ if (profile_passed_limit(*tm)) {
+ if (timed_out != NULL) {
+ *timed_out = true;
+ }
+ status = RA_FAIL;
+ break;
+ }
+ }
+ status = RA_CONT;
+
+#ifdef REGEXP_DEBUG
+ if (regnarrate) {
+ mch_errmsg((char *)regprop(scan));
+ mch_errmsg("...\n");
+ if (re_extmatch_in != NULL) {
+ int i;
+
+ mch_errmsg(_("External submatches:\n"));
+ for (i = 0; i < NSUBEXP; i++) {
+ mch_errmsg(" \"");
+ if (re_extmatch_in->matches[i] != NULL) {
+ mch_errmsg((char *)re_extmatch_in->matches[i]);
+ }
+ mch_errmsg("\"\n");
+ }
+ }
+ }
+#endif
+ next = regnext(scan);
+
+ op = OP(scan);
+ // Check for character class with NL added.
+ if (!rex.reg_line_lbr && WITH_NL(op) && REG_MULTI
+ && *rex.input == NUL && rex.lnum <= rex.reg_maxline) {
+ reg_nextline();
+ } else if (rex.reg_line_lbr && WITH_NL(op) && *rex.input == '\n') {
+ ADVANCE_REGINPUT();
+ } else {
+ if (WITH_NL(op)) {
+ op -= ADD_NL;
+ }
+ c = utf_ptr2char((char *)rex.input);
+ switch (op) {
+ case BOL:
+ if (rex.input != rex.line) {
+ status = RA_NOMATCH;
+ }
+ break;
+
+ case EOL:
+ if (c != NUL) {
+ status = RA_NOMATCH;
+ }
+ break;
+
+ case RE_BOF:
+ // We're not at the beginning of the file when below the first
+ // line where we started, not at the start of the line or we
+ // didn't start at the first line of the buffer.
+ if (rex.lnum != 0 || rex.input != rex.line
+ || (REG_MULTI && rex.reg_firstlnum > 1)) {
+ status = RA_NOMATCH;
+ }
+ break;
+
+ case RE_EOF:
+ if (rex.lnum != rex.reg_maxline || c != NUL) {
+ status = RA_NOMATCH;
+ }
+ break;
+
+ case CURSOR:
+ // Check if the buffer is in a window and compare the
+ // rex.reg_win->w_cursor position to the match position.
+ if (rex.reg_win == NULL
+ || (rex.lnum + rex.reg_firstlnum != rex.reg_win->w_cursor.lnum)
+ || ((colnr_T)(rex.input - rex.line) !=
+ rex.reg_win->w_cursor.col)) {
+ status = RA_NOMATCH;
+ }
+ break;
+
+ case RE_MARK:
+ // Compare the mark position to the match position.
+ {
+ int mark = OPERAND(scan)[0];
+ int cmp = OPERAND(scan)[1];
+ pos_T *pos;
+ size_t col = REG_MULTI ? rex.input - rex.line : 0;
+
+ // fm will be NULL if the mark is not set in reg_buf
+ fmark_T *fm = mark_get(rex.reg_buf, curwin, NULL, kMarkBufLocal, mark);
+
+ // Line may have been freed, get it again.
+ if (REG_MULTI) {
+ rex.line = reg_getline(rex.lnum);
+ rex.input = rex.line + col;
+ }
+
+ if (fm == NULL // mark doesn't exist
+ || fm->mark.lnum <= 0) { // mark isn't set in reg_buf
+ status = RA_NOMATCH;
+ } else {
+ pos = &fm->mark;
+ const colnr_T pos_col = pos->lnum == rex.lnum + rex.reg_firstlnum
+ && pos->col == MAXCOL
+ ? (colnr_T)STRLEN(reg_getline(pos->lnum - rex.reg_firstlnum))
+ : pos->col;
+
+ if (pos->lnum == rex.lnum + rex.reg_firstlnum
+ ? (pos_col == (colnr_T)(rex.input - rex.line)
+ ? (cmp == '<' || cmp == '>')
+ : (pos_col < (colnr_T)(rex.input - rex.line)
+ ? cmp != '>'
+ : cmp != '<'))
+ : (pos->lnum < rex.lnum + rex.reg_firstlnum
+ ? cmp != '>'
+ : cmp != '<')) {
+ status = RA_NOMATCH;
+ }
+ }
+ }
+ break;
+
+ case RE_VISUAL:
+ if (!reg_match_visual()) {
+ status = RA_NOMATCH;
+ }
+ break;
+
+ case RE_LNUM:
+ assert(rex.lnum + rex.reg_firstlnum >= 0
+ && (uintmax_t)(rex.lnum + rex.reg_firstlnum) <= UINT32_MAX);
+ if (!REG_MULTI
+ || !re_num_cmp((uint32_t)(rex.lnum + rex.reg_firstlnum), scan)) {
+ status = RA_NOMATCH;
+ }
+ break;
+
+ case RE_COL:
+ assert(rex.input - rex.line + 1 >= 0
+ && (uintmax_t)(rex.input - rex.line + 1) <= UINT32_MAX);
+ if (!re_num_cmp((uint32_t)(rex.input - rex.line + 1), scan)) {
+ status = RA_NOMATCH;
+ }
+ break;
+
+ case RE_VCOL:
+ if (!re_num_cmp(win_linetabsize(rex.reg_win == NULL
+ ? curwin : rex.reg_win,
+ rex.line,
+ (colnr_T)(rex.input - rex.line)) + 1,
+ scan)) {
+ status = RA_NOMATCH;
+ }
+ break;
+
+ case BOW: // \<word; rex.input points to w
+ if (c == NUL) { // Can't match at end of line
+ status = RA_NOMATCH;
+ } else {
+ // Get class of current and previous char (if it exists).
+ const int this_class =
+ mb_get_class_tab(rex.input, rex.reg_buf->b_chartab);
+ if (this_class <= 1) {
+ status = RA_NOMATCH; // Not on a word at all.
+ } else if (reg_prev_class() == this_class) {
+ status = RA_NOMATCH; // Previous char is in same word.
+ }
+ }
+ break;
+
+ case EOW: // word\>; rex.input points after d
+ if (rex.input == rex.line) { // Can't match at start of line
+ status = RA_NOMATCH;
+ } else {
+ int this_class, prev_class;
+
+ // Get class of current and previous char (if it exists).
+ this_class = mb_get_class_tab(rex.input, rex.reg_buf->b_chartab);
+ prev_class = reg_prev_class();
+ if (this_class == prev_class
+ || prev_class == 0 || prev_class == 1) {
+ status = RA_NOMATCH;
+ }
+ }
+ break; // Matched with EOW
+
+ case ANY:
+ // ANY does not match new lines.
+ if (c == NUL) {
+ status = RA_NOMATCH;
+ } else {
+ ADVANCE_REGINPUT();
+ }
+ break;
+
+ case IDENT:
+ if (!vim_isIDc(c)) {
+ status = RA_NOMATCH;
+ } else {
+ ADVANCE_REGINPUT();
+ }
+ break;
+
+ case SIDENT:
+ if (ascii_isdigit(*rex.input) || !vim_isIDc(c)) {
+ status = RA_NOMATCH;
+ } else {
+ ADVANCE_REGINPUT();
+ }
+ break;
+
+ case KWORD:
+ if (!vim_iswordp_buf(rex.input, rex.reg_buf)) {
+ status = RA_NOMATCH;
+ } else {
+ ADVANCE_REGINPUT();
+ }
+ break;
+
+ case SKWORD:
+ if (ascii_isdigit(*rex.input)
+ || !vim_iswordp_buf(rex.input, rex.reg_buf)) {
+ status = RA_NOMATCH;
+ } else {
+ ADVANCE_REGINPUT();
+ }
+ break;
+
+ case FNAME:
+ if (!vim_isfilec(c)) {
+ status = RA_NOMATCH;
+ } else {
+ ADVANCE_REGINPUT();
+ }
+ break;
+
+ case SFNAME:
+ if (ascii_isdigit(*rex.input) || !vim_isfilec(c)) {
+ status = RA_NOMATCH;
+ } else {
+ ADVANCE_REGINPUT();
+ }
+ break;
+
+ case PRINT:
+ if (!vim_isprintc(utf_ptr2char((char *)rex.input))) {
+ status = RA_NOMATCH;
+ } else {
+ ADVANCE_REGINPUT();
+ }
+ break;
+
+ case SPRINT:
+ if (ascii_isdigit(*rex.input) || !vim_isprintc(utf_ptr2char((char *)rex.input))) {
+ status = RA_NOMATCH;
+ } else {
+ ADVANCE_REGINPUT();
+ }
+ break;
+
+ case WHITE:
+ if (!ascii_iswhite(c)) {
+ status = RA_NOMATCH;
+ } else {
+ ADVANCE_REGINPUT();
+ }
+ break;
+
+ case NWHITE:
+ if (c == NUL || ascii_iswhite(c)) {
+ status = RA_NOMATCH;
+ } else {
+ ADVANCE_REGINPUT();
+ }
+ break;
+
+ case DIGIT:
+ if (!ri_digit(c)) {
+ status = RA_NOMATCH;
+ } else {
+ ADVANCE_REGINPUT();
+ }
+ break;
+
+ case NDIGIT:
+ if (c == NUL || ri_digit(c)) {
+ status = RA_NOMATCH;
+ } else {
+ ADVANCE_REGINPUT();
+ }
+ break;
+
+ case HEX:
+ if (!ri_hex(c)) {
+ status = RA_NOMATCH;
+ } else {
+ ADVANCE_REGINPUT();
+ }
+ break;
+
+ case NHEX:
+ if (c == NUL || ri_hex(c)) {
+ status = RA_NOMATCH;
+ } else {
+ ADVANCE_REGINPUT();
+ }
+ break;
+
+ case OCTAL:
+ if (!ri_octal(c)) {
+ status = RA_NOMATCH;
+ } else {
+ ADVANCE_REGINPUT();
+ }
+ break;
+
+ case NOCTAL:
+ if (c == NUL || ri_octal(c)) {
+ status = RA_NOMATCH;
+ } else {
+ ADVANCE_REGINPUT();
+ }
+ break;
+
+ case WORD:
+ if (!ri_word(c)) {
+ status = RA_NOMATCH;
+ } else {
+ ADVANCE_REGINPUT();
+ }
+ break;
+
+ case NWORD:
+ if (c == NUL || ri_word(c)) {
+ status = RA_NOMATCH;
+ } else {
+ ADVANCE_REGINPUT();
+ }
+ break;
+
+ case HEAD:
+ if (!ri_head(c)) {
+ status = RA_NOMATCH;
+ } else {
+ ADVANCE_REGINPUT();
+ }
+ break;
+
+ case NHEAD:
+ if (c == NUL || ri_head(c)) {
+ status = RA_NOMATCH;
+ } else {
+ ADVANCE_REGINPUT();
+ }
+ break;
+
+ case ALPHA:
+ if (!ri_alpha(c)) {
+ status = RA_NOMATCH;
+ } else {
+ ADVANCE_REGINPUT();
+ }
+ break;
+
+ case NALPHA:
+ if (c == NUL || ri_alpha(c)) {
+ status = RA_NOMATCH;
+ } else {
+ ADVANCE_REGINPUT();
+ }
+ break;
+
+ case LOWER:
+ if (!ri_lower(c)) {
+ status = RA_NOMATCH;
+ } else {
+ ADVANCE_REGINPUT();
+ }
+ break;
+
+ case NLOWER:
+ if (c == NUL || ri_lower(c)) {
+ status = RA_NOMATCH;
+ } else {
+ ADVANCE_REGINPUT();
+ }
+ break;
+
+ case UPPER:
+ if (!ri_upper(c)) {
+ status = RA_NOMATCH;
+ } else {
+ ADVANCE_REGINPUT();
+ }
+ break;
+
+ case NUPPER:
+ if (c == NUL || ri_upper(c)) {
+ status = RA_NOMATCH;
+ } else {
+ ADVANCE_REGINPUT();
+ }
+ break;
+
+ case EXACTLY: {
+ int len;
+ char_u *opnd;
+
+ opnd = OPERAND(scan);
+ // Inline the first byte, for speed.
+ if (*opnd != *rex.input
+ && (!rex.reg_ic)) {
+ status = RA_NOMATCH;
+ } else if (*opnd == NUL) {
+ // match empty string always works; happens when "~" is
+ // empty.
+ } else {
+ if (opnd[1] == NUL && !rex.reg_ic) {
+ len = 1; // matched a single byte above
+ } else {
+ // Need to match first byte again for multi-byte.
+ len = (int)STRLEN(opnd);
+ if (cstrncmp(opnd, rex.input, &len) != 0) {
+ status = RA_NOMATCH;
+ }
+ }
+ // Check for following composing character, unless %C
+ // follows (skips over all composing chars).
+ if (status != RA_NOMATCH
+ && utf_composinglike(rex.input, rex.input + len)
+ && !rex.reg_icombine
+ && OP(next) != RE_COMPOSING) {
+ // raaron: This code makes a composing character get
+ // ignored, which is the correct behavior (sometimes)
+ // for voweled Hebrew texts.
+ status = RA_NOMATCH;
+ }
+ if (status != RA_NOMATCH) {
+ rex.input += len;
+ }
+ }
+ }
+ break;
+
+ case ANYOF:
+ case ANYBUT:
+ if (c == NUL) {
+ status = RA_NOMATCH;
+ } else if ((cstrchr(OPERAND(scan), c) == NULL) == (op == ANYOF)) {
+ status = RA_NOMATCH;
+ } else {
+ ADVANCE_REGINPUT();
+ }
+ break;
+
+ case MULTIBYTECODE: {
+ int i, len;
+
+ const char_u *opnd = OPERAND(scan);
+ // Safety check (just in case 'encoding' was changed since
+ // compiling the program).
+ if ((len = utfc_ptr2len((char *)opnd)) < 2) {
+ status = RA_NOMATCH;
+ break;
+ }
+ const int opndc = utf_ptr2char((char *)opnd);
+ if (utf_iscomposing(opndc)) {
+ // When only a composing char is given match at any
+ // position where that composing char appears.
+ status = RA_NOMATCH;
+ for (i = 0; rex.input[i] != NUL;
+ i += utf_ptr2len((char *)rex.input + i)) {
+ const int inpc = utf_ptr2char((char *)rex.input + i);
+ if (!utf_iscomposing(inpc)) {
+ if (i > 0) {
+ break;
+ }
+ } else if (opndc == inpc) {
+ // Include all following composing chars.
+ len = i + utfc_ptr2len((char *)rex.input + i);
+ status = RA_MATCH;
+ break;
+ }
+ }
+ } else {
+ for (i = 0; i < len; i++) {
+ if (opnd[i] != rex.input[i]) {
+ status = RA_NOMATCH;
+ break;
+ }
+ }
+ }
+ rex.input += len;
+ }
+ break;
+
+ case RE_COMPOSING:
+ // Skip composing characters.
+ while (utf_iscomposing(utf_ptr2char((char *)rex.input))) {
+ MB_CPTR_ADV(rex.input);
+ }
+ break;
+
+ case NOTHING:
+ break;
+
+ case BACK: {
+ int i;
+
+ // When we run into BACK we need to check if we don't keep
+ // looping without matching any input. The second and later
+ // times a BACK is encountered it fails if the input is still
+ // at the same position as the previous time.
+ // The positions are stored in "backpos" and found by the
+ // current value of "scan", the position in the RE program.
+ backpos_T *bp = (backpos_T *)backpos.ga_data;
+ for (i = 0; i < backpos.ga_len; i++) {
+ if (bp[i].bp_scan == scan) {
+ break;
+ }
+ }
+ if (i == backpos.ga_len) {
+ backpos_T *p = GA_APPEND_VIA_PTR(backpos_T, &backpos);
+ p->bp_scan = scan;
+ } else if (reg_save_equal(&bp[i].bp_pos)) {
+ // Still at same position as last time, fail.
+ status = RA_NOMATCH;
+ }
+
+ assert(status != RA_FAIL);
+ if (status != RA_NOMATCH) {
+ reg_save(&bp[i].bp_pos, &backpos);
+ }
+ }
+ break;
+
+ case MOPEN + 0: // Match start: \zs
+ case MOPEN + 1: // \(
+ case MOPEN + 2:
+ case MOPEN + 3:
+ case MOPEN + 4:
+ case MOPEN + 5:
+ case MOPEN + 6:
+ case MOPEN + 7:
+ case MOPEN + 8:
+ case MOPEN + 9:
+ no = op - MOPEN;
+ cleanup_subexpr();
+ rp = regstack_push(RS_MOPEN, scan);
+ if (rp == NULL) {
+ status = RA_FAIL;
+ } else {
+ rp->rs_no = no;
+ save_se(&rp->rs_un.sesave, &rex.reg_startpos[no],
+ &rex.reg_startp[no]);
+ // We simply continue and handle the result when done.
+ }
+ break;
+
+ case NOPEN: // \%(
+ case NCLOSE: // \) after \%(
+ if (regstack_push(RS_NOPEN, scan) == NULL) {
+ status = RA_FAIL;
+ }
+ // We simply continue and handle the result when done.
+ break;
+
+ case ZOPEN + 1:
+ case ZOPEN + 2:
+ case ZOPEN + 3:
+ case ZOPEN + 4:
+ case ZOPEN + 5:
+ case ZOPEN + 6:
+ case ZOPEN + 7:
+ case ZOPEN + 8:
+ case ZOPEN + 9:
+ no = op - ZOPEN;
+ cleanup_zsubexpr();
+ rp = regstack_push(RS_ZOPEN, scan);
+ if (rp == NULL) {
+ status = RA_FAIL;
+ } else {
+ rp->rs_no = no;
+ save_se(&rp->rs_un.sesave, &reg_startzpos[no],
+ &reg_startzp[no]);
+ // We simply continue and handle the result when done.
+ }
+ break;
+
+ case MCLOSE + 0: // Match end: \ze
+ case MCLOSE + 1: // \)
+ case MCLOSE + 2:
+ case MCLOSE + 3:
+ case MCLOSE + 4:
+ case MCLOSE + 5:
+ case MCLOSE + 6:
+ case MCLOSE + 7:
+ case MCLOSE + 8:
+ case MCLOSE + 9:
+ no = op - MCLOSE;
+ cleanup_subexpr();
+ rp = regstack_push(RS_MCLOSE, scan);
+ if (rp == NULL) {
+ status = RA_FAIL;
+ } else {
+ rp->rs_no = no;
+ save_se(&rp->rs_un.sesave, &rex.reg_endpos[no], &rex.reg_endp[no]);
+ // We simply continue and handle the result when done.
+ }
+ break;
+
+ case ZCLOSE + 1: // \) after \z(
+ case ZCLOSE + 2:
+ case ZCLOSE + 3:
+ case ZCLOSE + 4:
+ case ZCLOSE + 5:
+ case ZCLOSE + 6:
+ case ZCLOSE + 7:
+ case ZCLOSE + 8:
+ case ZCLOSE + 9:
+ no = op - ZCLOSE;
+ cleanup_zsubexpr();
+ rp = regstack_push(RS_ZCLOSE, scan);
+ if (rp == NULL) {
+ status = RA_FAIL;
+ } else {
+ rp->rs_no = no;
+ save_se(&rp->rs_un.sesave, &reg_endzpos[no],
+ &reg_endzp[no]);
+ // We simply continue and handle the result when done.
+ }
+ break;
+
+ case BACKREF + 1:
+ case BACKREF + 2:
+ case BACKREF + 3:
+ case BACKREF + 4:
+ case BACKREF + 5:
+ case BACKREF + 6:
+ case BACKREF + 7:
+ case BACKREF + 8:
+ case BACKREF + 9: {
+ int len;
+
+ no = op - BACKREF;
+ cleanup_subexpr();
+ if (!REG_MULTI) { // Single-line regexp
+ if (rex.reg_startp[no] == NULL || rex.reg_endp[no] == NULL) {
+ // Backref was not set: Match an empty string.
+ len = 0;
+ } else {
+ // Compare current input with back-ref in the same line.
+ len = (int)(rex.reg_endp[no] - rex.reg_startp[no]);
+ if (cstrncmp(rex.reg_startp[no], rex.input, &len) != 0) {
+ status = RA_NOMATCH;
+ }
+ }
+ } else { // Multi-line regexp
+ if (rex.reg_startpos[no].lnum < 0 || rex.reg_endpos[no].lnum < 0) {
+ // Backref was not set: Match an empty string.
+ len = 0;
+ } else {
+ if (rex.reg_startpos[no].lnum == rex.lnum
+ && rex.reg_endpos[no].lnum == rex.lnum) {
+ // Compare back-ref within the current line.
+ len = rex.reg_endpos[no].col - rex.reg_startpos[no].col;
+ if (cstrncmp(rex.line + rex.reg_startpos[no].col,
+ rex.input, &len) != 0) {
+ status = RA_NOMATCH;
+ }
+ } else {
+ // Messy situation: Need to compare between two lines.
+ int r = match_with_backref(rex.reg_startpos[no].lnum,
+ rex.reg_startpos[no].col,
+ rex.reg_endpos[no].lnum,
+ rex.reg_endpos[no].col,
+ &len);
+ if (r != RA_MATCH) {
+ status = r;
+ }
+ }
+ }
+ }
+
+ // Matched the backref, skip over it.
+ rex.input += len;
+ }
+ break;
+
+ case ZREF + 1:
+ case ZREF + 2:
+ case ZREF + 3:
+ case ZREF + 4:
+ case ZREF + 5:
+ case ZREF + 6:
+ case ZREF + 7:
+ case ZREF + 8:
+ case ZREF + 9:
+ cleanup_zsubexpr();
+ no = op - ZREF;
+ if (re_extmatch_in != NULL
+ && re_extmatch_in->matches[no] != NULL) {
+ int len = (int)STRLEN(re_extmatch_in->matches[no]);
+ if (cstrncmp(re_extmatch_in->matches[no], rex.input, &len) != 0) {
+ status = RA_NOMATCH;
+ } else {
+ rex.input += len;
+ }
+ } else {
+ // Backref was not set: Match an empty string.
+ }
+ break;
+
+ case BRANCH:
+ if (OP(next) != BRANCH) { // No choice.
+ next = OPERAND(scan); // Avoid recursion.
+ } else {
+ rp = regstack_push(RS_BRANCH, scan);
+ if (rp == NULL) {
+ status = RA_FAIL;
+ } else {
+ status = RA_BREAK; // rest is below
+ }
+ }
+ break;
+
+ case BRACE_LIMITS:
+ if (OP(next) == BRACE_SIMPLE) {
+ bl_minval = OPERAND_MIN(scan);
+ bl_maxval = OPERAND_MAX(scan);
+ } else if (OP(next) >= BRACE_COMPLEX
+ && OP(next) < BRACE_COMPLEX + 10) {
+ no = OP(next) - BRACE_COMPLEX;
+ brace_min[no] = OPERAND_MIN(scan);
+ brace_max[no] = OPERAND_MAX(scan);
+ brace_count[no] = 0;
+ } else {
+ internal_error("BRACE_LIMITS");
+ status = RA_FAIL;
+ }
+ break;
+
+ case BRACE_COMPLEX + 0:
+ case BRACE_COMPLEX + 1:
+ case BRACE_COMPLEX + 2:
+ case BRACE_COMPLEX + 3:
+ case BRACE_COMPLEX + 4:
+ case BRACE_COMPLEX + 5:
+ case BRACE_COMPLEX + 6:
+ case BRACE_COMPLEX + 7:
+ case BRACE_COMPLEX + 8:
+ case BRACE_COMPLEX + 9:
+ no = op - BRACE_COMPLEX;
+ ++brace_count[no];
+
+ // If not matched enough times yet, try one more
+ if (brace_count[no] <= (brace_min[no] <= brace_max[no]
+ ? brace_min[no] : brace_max[no])) {
+ rp = regstack_push(RS_BRCPLX_MORE, scan);
+ if (rp == NULL) {
+ status = RA_FAIL;
+ } else {
+ rp->rs_no = no;
+ reg_save(&rp->rs_un.regsave, &backpos);
+ next = OPERAND(scan);
+ // We continue and handle the result when done.
+ }
+ break;
+ }
+
+ // If matched enough times, may try matching some more
+ if (brace_min[no] <= brace_max[no]) {
+ // Range is the normal way around, use longest match
+ if (brace_count[no] <= brace_max[no]) {
+ rp = regstack_push(RS_BRCPLX_LONG, scan);
+ if (rp == NULL) {
+ status = RA_FAIL;
+ } else {
+ rp->rs_no = no;
+ reg_save(&rp->rs_un.regsave, &backpos);
+ next = OPERAND(scan);
+ // We continue and handle the result when done.
+ }
+ }
+ } else {
+ // Range is backwards, use shortest match first
+ if (brace_count[no] <= brace_min[no]) {
+ rp = regstack_push(RS_BRCPLX_SHORT, scan);
+ if (rp == NULL) {
+ status = RA_FAIL;
+ } else {
+ reg_save(&rp->rs_un.regsave, &backpos);
+ // We continue and handle the result when done.
+ }
+ }
+ }
+ break;
+
+ case BRACE_SIMPLE:
+ case STAR:
+ case PLUS: {
+ regstar_T rst;
+
+ // Lookahead to avoid useless match attempts when we know
+ // what character comes next.
+ if (OP(next) == EXACTLY) {
+ rst.nextb = *OPERAND(next);
+ if (rex.reg_ic) {
+ if (mb_isupper(rst.nextb)) {
+ rst.nextb_ic = mb_tolower(rst.nextb);
+ } else {
+ rst.nextb_ic = mb_toupper(rst.nextb);
+ }
+ } else {
+ rst.nextb_ic = rst.nextb;
+ }
+ } else {
+ rst.nextb = NUL;
+ rst.nextb_ic = NUL;
+ }
+ if (op != BRACE_SIMPLE) {
+ rst.minval = (op == STAR) ? 0 : 1;
+ rst.maxval = MAX_LIMIT;
+ } else {
+ rst.minval = bl_minval;
+ rst.maxval = bl_maxval;
+ }
+
+ // When maxval > minval, try matching as much as possible, up
+ // to maxval. When maxval < minval, try matching at least the
+ // minimal number (since the range is backwards, that's also
+ // maxval!).
+ rst.count = regrepeat(OPERAND(scan), rst.maxval);
+ if (got_int) {
+ status = RA_FAIL;
+ break;
+ }
+ if (rst.minval <= rst.maxval
+ ? rst.count >= rst.minval : rst.count >= rst.maxval) {
+ // It could match. Prepare for trying to match what
+ // follows. The code is below. Parameters are stored in
+ // a regstar_T on the regstack.
+ if ((long)((unsigned)regstack.ga_len >> 10) >= p_mmp) {
+ emsg(_(e_maxmempat));
+ status = RA_FAIL;
+ } else {
+ ga_grow(&regstack, sizeof(regstar_T));
+ regstack.ga_len += sizeof(regstar_T);
+ rp = regstack_push(rst.minval <= rst.maxval ? RS_STAR_LONG : RS_STAR_SHORT, scan);
+ if (rp == NULL) {
+ status = RA_FAIL;
+ } else {
+ *(((regstar_T *)rp) - 1) = rst;
+ status = RA_BREAK; // skip the restore bits
+ }
+ }
+ } else {
+ status = RA_NOMATCH;
+ }
+ }
+ break;
+
+ case NOMATCH:
+ case MATCH:
+ case SUBPAT:
+ rp = regstack_push(RS_NOMATCH, scan);
+ if (rp == NULL) {
+ status = RA_FAIL;
+ } else {
+ rp->rs_no = op;
+ reg_save(&rp->rs_un.regsave, &backpos);
+ next = OPERAND(scan);
+ // We continue and handle the result when done.
+ }
+ break;
+
+ case BEHIND:
+ case NOBEHIND:
+ // Need a bit of room to store extra positions.
+ if ((long)((unsigned)regstack.ga_len >> 10) >= p_mmp) {
+ emsg(_(e_maxmempat));
+ status = RA_FAIL;
+ } else {
+ ga_grow(&regstack, sizeof(regbehind_T));
+ regstack.ga_len += sizeof(regbehind_T);
+ rp = regstack_push(RS_BEHIND1, scan);
+ if (rp == NULL) {
+ status = RA_FAIL;
+ } else {
+ // Need to save the subexpr to be able to restore them
+ // when there is a match but we don't use it.
+ save_subexpr(((regbehind_T *)rp) - 1);
+
+ rp->rs_no = op;
+ reg_save(&rp->rs_un.regsave, &backpos);
+ // First try if what follows matches. If it does then we
+ // check the behind match by looping.
+ }
+ }
+ break;
+
+ case BHPOS:
+ if (REG_MULTI) {
+ if (behind_pos.rs_u.pos.col != (colnr_T)(rex.input - rex.line)
+ || behind_pos.rs_u.pos.lnum != rex.lnum) {
+ status = RA_NOMATCH;
+ }
+ } else if (behind_pos.rs_u.ptr != rex.input) {
+ status = RA_NOMATCH;
+ }
+ break;
+
+ case NEWL:
+ if ((c != NUL || !REG_MULTI || rex.lnum > rex.reg_maxline
+ || rex.reg_line_lbr) && (c != '\n' || !rex.reg_line_lbr)) {
+ status = RA_NOMATCH;
+ } else if (rex.reg_line_lbr) {
+ ADVANCE_REGINPUT();
+ } else {
+ reg_nextline();
+ }
+ break;
+
+ case END:
+ status = RA_MATCH; // Success!
+ break;
+
+ default:
+ iemsg(_(e_re_corr));
+#ifdef REGEXP_DEBUG
+ printf("Illegal op code %d\n", op);
+#endif
+ status = RA_FAIL;
+ break;
+ }
+ }
+
+ // If we can't continue sequentially, break the inner loop.
+ if (status != RA_CONT) {
+ break;
+ }
+
+ // Continue in inner loop, advance to next item.
+ scan = next;
+ } // end of inner loop
+
+ // If there is something on the regstack execute the code for the state.
+ // If the state is popped then loop and use the older state.
+ while (!GA_EMPTY(&regstack) && status != RA_FAIL) {
+ rp = (regitem_T *)((char *)regstack.ga_data + regstack.ga_len) - 1;
+ switch (rp->rs_state) {
+ case RS_NOPEN:
+ // Result is passed on as-is, simply pop the state.
+ regstack_pop(&scan);
+ break;
+
+ case RS_MOPEN:
+ // Pop the state. Restore pointers when there is no match.
+ if (status == RA_NOMATCH) {
+ restore_se(&rp->rs_un.sesave, &rex.reg_startpos[rp->rs_no],
+ &rex.reg_startp[rp->rs_no]);
+ }
+ regstack_pop(&scan);
+ break;
+
+ case RS_ZOPEN:
+ // Pop the state. Restore pointers when there is no match.
+ if (status == RA_NOMATCH) {
+ restore_se(&rp->rs_un.sesave, &reg_startzpos[rp->rs_no],
+ &reg_startzp[rp->rs_no]);
+ }
+ regstack_pop(&scan);
+ break;
+
+ case RS_MCLOSE:
+ // Pop the state. Restore pointers when there is no match.
+ if (status == RA_NOMATCH) {
+ restore_se(&rp->rs_un.sesave, &rex.reg_endpos[rp->rs_no],
+ &rex.reg_endp[rp->rs_no]);
+ }
+ regstack_pop(&scan);
+ break;
+
+ case RS_ZCLOSE:
+ // Pop the state. Restore pointers when there is no match.
+ if (status == RA_NOMATCH) {
+ restore_se(&rp->rs_un.sesave, &reg_endzpos[rp->rs_no],
+ &reg_endzp[rp->rs_no]);
+ }
+ regstack_pop(&scan);
+ break;
+
+ case RS_BRANCH:
+ if (status == RA_MATCH) {
+ // this branch matched, use it
+ regstack_pop(&scan);
+ } else {
+ if (status != RA_BREAK) {
+ // After a non-matching branch: try next one.
+ reg_restore(&rp->rs_un.regsave, &backpos);
+ scan = rp->rs_scan;
+ }
+ if (scan == NULL || OP(scan) != BRANCH) {
+ // no more branches, didn't find a match
+ status = RA_NOMATCH;
+ regstack_pop(&scan);
+ } else {
+ // Prepare to try a branch.
+ rp->rs_scan = regnext(scan);
+ reg_save(&rp->rs_un.regsave, &backpos);
+ scan = OPERAND(scan);
+ }
+ }
+ break;
+
+ case RS_BRCPLX_MORE:
+ // Pop the state. Restore pointers when there is no match.
+ if (status == RA_NOMATCH) {
+ reg_restore(&rp->rs_un.regsave, &backpos);
+ --brace_count[rp->rs_no]; // decrement match count
+ }
+ regstack_pop(&scan);
+ break;
+
+ case RS_BRCPLX_LONG:
+ // Pop the state. Restore pointers when there is no match.
+ if (status == RA_NOMATCH) {
+ // There was no match, but we did find enough matches.
+ reg_restore(&rp->rs_un.regsave, &backpos);
+ --brace_count[rp->rs_no];
+ // continue with the items after "\{}"
+ status = RA_CONT;
+ }
+ regstack_pop(&scan);
+ if (status == RA_CONT) {
+ scan = regnext(scan);
+ }
+ break;
+
+ case RS_BRCPLX_SHORT:
+ // Pop the state. Restore pointers when there is no match.
+ if (status == RA_NOMATCH) {
+ // There was no match, try to match one more item.
+ reg_restore(&rp->rs_un.regsave, &backpos);
+ }
+ regstack_pop(&scan);
+ if (status == RA_NOMATCH) {
+ scan = OPERAND(scan);
+ status = RA_CONT;
+ }
+ break;
+
+ case RS_NOMATCH:
+ // Pop the state. If the operand matches for NOMATCH or
+ // doesn't match for MATCH/SUBPAT, we fail. Otherwise backup,
+ // except for SUBPAT, and continue with the next item.
+ if (status == (rp->rs_no == NOMATCH ? RA_MATCH : RA_NOMATCH)) {
+ status = RA_NOMATCH;
+ } else {
+ status = RA_CONT;
+ if (rp->rs_no != SUBPAT) { // zero-width
+ reg_restore(&rp->rs_un.regsave, &backpos);
+ }
+ }
+ regstack_pop(&scan);
+ if (status == RA_CONT) {
+ scan = regnext(scan);
+ }
+ break;
+
+ case RS_BEHIND1:
+ if (status == RA_NOMATCH) {
+ regstack_pop(&scan);
+ regstack.ga_len -= sizeof(regbehind_T);
+ } else {
+ // The stuff after BEHIND/NOBEHIND matches. Now try if
+ // the behind part does (not) match before the current
+ // position in the input. This must be done at every
+ // position in the input and checking if the match ends at
+ // the current position.
+
+ // save the position after the found match for next
+ reg_save(&(((regbehind_T *)rp) - 1)->save_after, &backpos);
+
+ // Start looking for a match with operand at the current
+ // position. Go back one character until we find the
+ // result, hitting the start of the line or the previous
+ // line (for multi-line matching).
+ // Set behind_pos to where the match should end, BHPOS
+ // will match it. Save the current value.
+ (((regbehind_T *)rp) - 1)->save_behind = behind_pos;
+ behind_pos = rp->rs_un.regsave;
+
+ rp->rs_state = RS_BEHIND2;
+
+ reg_restore(&rp->rs_un.regsave, &backpos);
+ scan = OPERAND(rp->rs_scan) + 4;
+ }
+ break;
+
+ case RS_BEHIND2:
+ // Looping for BEHIND / NOBEHIND match.
+ if (status == RA_MATCH && reg_save_equal(&behind_pos)) {
+ // found a match that ends where "next" started
+ behind_pos = (((regbehind_T *)rp) - 1)->save_behind;
+ if (rp->rs_no == BEHIND) {
+ reg_restore(&(((regbehind_T *)rp) - 1)->save_after,
+ &backpos);
+ } else {
+ // But we didn't want a match. Need to restore the
+ // subexpr, because what follows matched, so they have
+ // been set.
+ status = RA_NOMATCH;
+ restore_subexpr(((regbehind_T *)rp) - 1);
+ }
+ regstack_pop(&scan);
+ regstack.ga_len -= sizeof(regbehind_T);
+ } else {
+ long limit;
+
+ // No match or a match that doesn't end where we want it: Go
+ // back one character. May go to previous line once.
+ no = OK;
+ limit = OPERAND_MIN(rp->rs_scan);
+ if (REG_MULTI) {
+ if (limit > 0
+ && ((rp->rs_un.regsave.rs_u.pos.lnum
+ < behind_pos.rs_u.pos.lnum
+ ? (colnr_T)STRLEN(rex.line)
+ : behind_pos.rs_u.pos.col)
+ - rp->rs_un.regsave.rs_u.pos.col >= limit)) {
+ no = FAIL;
+ } else if (rp->rs_un.regsave.rs_u.pos.col == 0) {
+ if (rp->rs_un.regsave.rs_u.pos.lnum
+ < behind_pos.rs_u.pos.lnum
+ || reg_getline(--rp->rs_un.regsave.rs_u.pos.lnum)
+ == NULL) {
+ no = FAIL;
+ } else {
+ reg_restore(&rp->rs_un.regsave, &backpos);
+ rp->rs_un.regsave.rs_u.pos.col =
+ (colnr_T)STRLEN(rex.line);
+ }
+ } else {
+ const char_u *const line =
+ reg_getline(rp->rs_un.regsave.rs_u.pos.lnum);
+
+ rp->rs_un.regsave.rs_u.pos.col -=
+ utf_head_off(line,
+ line + rp->rs_un.regsave.rs_u.pos.col - 1)
+ + 1;
+ }
+ } else {
+ if (rp->rs_un.regsave.rs_u.ptr == rex.line) {
+ no = FAIL;
+ } else {
+ MB_PTR_BACK(rex.line, rp->rs_un.regsave.rs_u.ptr);
+ if (limit > 0
+ && (behind_pos.rs_u.ptr - rp->rs_un.regsave.rs_u.ptr) > (ptrdiff_t)limit) {
+ no = FAIL;
+ }
+ }
+ }
+ if (no == OK) {
+ // Advanced, prepare for finding match again.
+ reg_restore(&rp->rs_un.regsave, &backpos);
+ scan = OPERAND(rp->rs_scan) + 4;
+ if (status == RA_MATCH) {
+ // We did match, so subexpr may have been changed,
+ // need to restore them for the next try.
+ status = RA_NOMATCH;
+ restore_subexpr(((regbehind_T *)rp) - 1);
+ }
+ } else {
+ // Can't advance. For NOBEHIND that's a match.
+ behind_pos = (((regbehind_T *)rp) - 1)->save_behind;
+ if (rp->rs_no == NOBEHIND) {
+ reg_restore(&(((regbehind_T *)rp) - 1)->save_after,
+ &backpos);
+ status = RA_MATCH;
+ } else {
+ // We do want a proper match. Need to restore the
+ // subexpr if we had a match, because they may have
+ // been set.
+ if (status == RA_MATCH) {
+ status = RA_NOMATCH;
+ restore_subexpr(((regbehind_T *)rp) - 1);
+ }
+ }
+ regstack_pop(&scan);
+ regstack.ga_len -= sizeof(regbehind_T);
+ }
+ }
+ break;
+
+ case RS_STAR_LONG:
+ case RS_STAR_SHORT: {
+ regstar_T *rst = ((regstar_T *)rp) - 1;
+
+ if (status == RA_MATCH) {
+ regstack_pop(&scan);
+ regstack.ga_len -= sizeof(regstar_T);
+ break;
+ }
+
+ // Tried once already, restore input pointers.
+ if (status != RA_BREAK) {
+ reg_restore(&rp->rs_un.regsave, &backpos);
+ }
+
+ // Repeat until we found a position where it could match.
+ for (;;) {
+ if (status != RA_BREAK) {
+ // Tried first position already, advance.
+ if (rp->rs_state == RS_STAR_LONG) {
+ // Trying for longest match, but couldn't or
+ // didn't match -- back up one char.
+ if (--rst->count < rst->minval) {
+ break;
+ }
+ if (rex.input == rex.line) {
+ // backup to last char of previous line
+ if (rex.lnum == 0) {
+ status = RA_NOMATCH;
+ break;
+ }
+ rex.lnum--;
+ rex.line = reg_getline(rex.lnum);
+ // Just in case regrepeat() didn't count right.
+ if (rex.line == NULL) {
+ break;
+ }
+ rex.input = rex.line + STRLEN(rex.line);
+ fast_breakcheck();
+ } else {
+ MB_PTR_BACK(rex.line, rex.input);
+ }
+ } else {
+ // Range is backwards, use shortest match first.
+ // Careful: maxval and minval are exchanged!
+ // Couldn't or didn't match: try advancing one
+ // char.
+ if (rst->count == rst->minval
+ || regrepeat(OPERAND(rp->rs_scan), 1L) == 0) {
+ break;
+ }
+ rst->count++;
+ }
+ if (got_int) {
+ break;
+ }
+ } else {
+ status = RA_NOMATCH;
+ }
+
+ // If it could match, try it.
+ if (rst->nextb == NUL || *rex.input == rst->nextb
+ || *rex.input == rst->nextb_ic) {
+ reg_save(&rp->rs_un.regsave, &backpos);
+ scan = regnext(rp->rs_scan);
+ status = RA_CONT;
+ break;
+ }
+ }
+ if (status != RA_CONT) {
+ // Failed.
+ regstack_pop(&scan);
+ regstack.ga_len -= sizeof(regstar_T);
+ status = RA_NOMATCH;
+ }
+ }
+ break;
+ }
+
+ // If we want to continue the inner loop or didn't pop a state
+ // continue matching loop
+ if (status == RA_CONT || rp == (regitem_T *)
+ ((char *)regstack.ga_data + regstack.ga_len) - 1) {
+ break;
+ }
+ }
+
+ // May need to continue with the inner loop, starting at "scan".
+ if (status == RA_CONT) {
+ continue;
+ }
+
+ // If the regstack is empty or something failed we are done.
+ if (GA_EMPTY(&regstack) || status == RA_FAIL) {
+ if (scan == NULL) {
+ // We get here only if there's trouble -- normally "case END" is
+ // the terminating point.
+ iemsg(_(e_re_corr));
+#ifdef REGEXP_DEBUG
+ printf("Premature EOL\n");
+#endif
+ }
+ return status == RA_MATCH;
+ }
+ } // End of loop until the regstack is empty.
+
+ // NOTREACHED
+}
+
+/// Try match of "prog" with at rex.line["col"].
+///
+/// @param tm timeout limit or NULL
+/// @param timed_out flag set on timeout or NULL
+///
+/// @return 0 for failure, or number of lines contained in the match.
+static long regtry(bt_regprog_T *prog, colnr_T col, proftime_T *tm, int *timed_out)
+{
+ rex.input = rex.line + col;
+ rex.need_clear_subexpr = true;
+ // Clear the external match subpointers if necessaey.
+ rex.need_clear_zsubexpr = (prog->reghasz == REX_SET);
+
+ if (regmatch(prog->program + 1, tm, timed_out) == 0) {
+ return 0;
+ }
+
+ cleanup_subexpr();
+ if (REG_MULTI) {
+ if (rex.reg_startpos[0].lnum < 0) {
+ rex.reg_startpos[0].lnum = 0;
+ rex.reg_startpos[0].col = col;
+ }
+ if (rex.reg_endpos[0].lnum < 0) {
+ rex.reg_endpos[0].lnum = rex.lnum;
+ rex.reg_endpos[0].col = (int)(rex.input - rex.line);
+ } else {
+ // Use line number of "\ze".
+ rex.lnum = rex.reg_endpos[0].lnum;
+ }
+ } else {
+ if (rex.reg_startp[0] == NULL) {
+ rex.reg_startp[0] = rex.line + col;
+ }
+ if (rex.reg_endp[0] == NULL) {
+ rex.reg_endp[0] = rex.input;
+ }
+ }
+ // Package any found \z(...\) matches for export. Default is none.
+ unref_extmatch(re_extmatch_out);
+ re_extmatch_out = NULL;
+
+ if (prog->reghasz == REX_SET) {
+ int i;
+
+ cleanup_zsubexpr();
+ re_extmatch_out = make_extmatch();
+ for (i = 0; i < NSUBEXP; i++) {
+ if (REG_MULTI) {
+ // Only accept single line matches.
+ if (reg_startzpos[i].lnum >= 0
+ && reg_endzpos[i].lnum == reg_startzpos[i].lnum
+ && reg_endzpos[i].col >= reg_startzpos[i].col) {
+ re_extmatch_out->matches[i] =
+ vim_strnsave(reg_getline(reg_startzpos[i].lnum)
+ + reg_startzpos[i].col,
+ reg_endzpos[i].col
+ - reg_startzpos[i].col);
+ }
+ } else {
+ if (reg_startzp[i] != NULL && reg_endzp[i] != NULL) {
+ re_extmatch_out->matches[i] =
+ vim_strnsave(reg_startzp[i], reg_endzp[i] - reg_startzp[i]);
+ }
+ }
+ }
+ }
+ return 1 + rex.lnum;
+}
+
+/// Match a regexp against a string ("line" points to the string) or multiple
+/// lines (if "line" is NULL, use reg_getline()).
+///
+/// @param col column to start search
+/// @param tm timeout limit or NULL
+/// @param timed_out flag set on timeout or NULL
+///
+/// @return 0 for failure, or number of lines contained in the match.
+static long bt_regexec_both(char_u *line, colnr_T col, proftime_T *tm, int *timed_out)
+{
+ bt_regprog_T *prog;
+ char_u *s;
+ long retval = 0L;
+
+ // Create "regstack" and "backpos" if they are not allocated yet.
+ // We allocate *_INITIAL amount of bytes first and then set the grow size
+ // to much bigger value to avoid many malloc calls in case of deep regular
+ // expressions.
+ if (regstack.ga_data == NULL) {
+ // Use an item size of 1 byte, since we push different things
+ // onto the regstack.
+ ga_init(&regstack, 1, REGSTACK_INITIAL);
+ ga_grow(&regstack, REGSTACK_INITIAL);
+ ga_set_growsize(&regstack, REGSTACK_INITIAL * 8);
+ }
+
+ if (backpos.ga_data == NULL) {
+ ga_init(&backpos, sizeof(backpos_T), BACKPOS_INITIAL);
+ ga_grow(&backpos, BACKPOS_INITIAL);
+ ga_set_growsize(&backpos, BACKPOS_INITIAL * 8);
+ }
+
+ if (REG_MULTI) {
+ prog = (bt_regprog_T *)rex.reg_mmatch->regprog;
+ line = reg_getline((linenr_T)0);
+ rex.reg_startpos = rex.reg_mmatch->startpos;
+ rex.reg_endpos = rex.reg_mmatch->endpos;
+ } else {
+ prog = (bt_regprog_T *)rex.reg_match->regprog;
+ rex.reg_startp = rex.reg_match->startp;
+ rex.reg_endp = rex.reg_match->endp;
+ }
+
+ // Be paranoid...
+ if (prog == NULL || line == NULL) {
+ iemsg(_(e_null));
+ goto theend;
+ }
+
+ // Check validity of program.
+ if (prog_magic_wrong()) {
+ goto theend;
+ }
+
+ // If the start column is past the maximum column: no need to try.
+ if (rex.reg_maxcol > 0 && col >= rex.reg_maxcol) {
+ goto theend;
+ }
+
+ // If pattern contains "\c" or "\C": overrule value of rex.reg_ic
+ if (prog->regflags & RF_ICASE) {
+ rex.reg_ic = true;
+ } else if (prog->regflags & RF_NOICASE) {
+ rex.reg_ic = false;
+ }
+
+ // If pattern contains "\Z" overrule value of rex.reg_icombine
+ if (prog->regflags & RF_ICOMBINE) {
+ rex.reg_icombine = true;
+ }
+
+ // If there is a "must appear" string, look for it.
+ if (prog->regmust != NULL) {
+ int c = utf_ptr2char((char *)prog->regmust);
+ s = line + col;
+
+ // This is used very often, esp. for ":global". Use two versions of
+ // the loop to avoid overhead of conditions.
+ if (!rex.reg_ic) {
+ while ((s = (char_u *)vim_strchr((char *)s, c)) != NULL) {
+ if (cstrncmp(s, prog->regmust, &prog->regmlen) == 0) {
+ break; // Found it.
+ }
+ MB_PTR_ADV(s);
+ }
+ } else {
+ while ((s = cstrchr(s, c)) != NULL) {
+ if (cstrncmp(s, prog->regmust, &prog->regmlen) == 0) {
+ break; // Found it.
+ }
+ MB_PTR_ADV(s);
+ }
+ }
+ if (s == NULL) { // Not present.
+ goto theend;
+ }
+ }
+
+ rex.line = line;
+ rex.lnum = 0;
+ reg_toolong = false;
+
+ // Simplest case: Anchored match need be tried only once.
+ if (prog->reganch) {
+ int c = utf_ptr2char((char *)rex.line + col);
+ if (prog->regstart == NUL
+ || prog->regstart == c
+ || (rex.reg_ic
+ && (utf_fold(prog->regstart) == utf_fold(c)
+ || (c < 255 && prog->regstart < 255
+ && mb_tolower(prog->regstart) == mb_tolower(c))))) {
+ retval = regtry(prog, col, tm, timed_out);
+ } else {
+ retval = 0;
+ }
+ } else {
+ int tm_count = 0;
+ // Messy cases: unanchored match.
+ while (!got_int) {
+ if (prog->regstart != NUL) {
+ // Skip until the char we know it must start with.
+ s = cstrchr(rex.line + col, prog->regstart);
+ if (s == NULL) {
+ retval = 0;
+ break;
+ }
+ col = (int)(s - rex.line);
+ }
+
+ // Check for maximum column to try.
+ if (rex.reg_maxcol > 0 && col >= rex.reg_maxcol) {
+ retval = 0;
+ break;
+ }
+
+ retval = regtry(prog, col, tm, timed_out);
+ if (retval > 0) {
+ break;
+ }
+
+ // if not currently on the first line, get it again
+ if (rex.lnum != 0) {
+ rex.lnum = 0;
+ rex.line = reg_getline((linenr_T)0);
+ }
+ if (rex.line[col] == NUL) {
+ break;
+ }
+ col += utfc_ptr2len((char *)rex.line + col);
+ // Check for timeout once in a twenty times to avoid overhead.
+ if (tm != NULL && ++tm_count == 20) {
+ tm_count = 0;
+ if (profile_passed_limit(*tm)) {
+ if (timed_out != NULL) {
+ *timed_out = true;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+theend:
+ // Free "reg_tofree" when it's a bit big.
+ // Free regstack and backpos if they are bigger than their initial size.
+ if (reg_tofreelen > 400) {
+ XFREE_CLEAR(reg_tofree);
+ }
+ if (regstack.ga_maxlen > REGSTACK_INITIAL) {
+ ga_clear(&regstack);
+ }
+ if (backpos.ga_maxlen > BACKPOS_INITIAL) {
+ ga_clear(&backpos);
+ }
+
+ if (retval > 0) {
+ // Make sure the end is never before the start. Can happen when \zs
+ // and \ze are used.
+ if (REG_MULTI) {
+ const lpos_T *const start = &rex.reg_mmatch->startpos[0];
+ const lpos_T *const end = &rex.reg_mmatch->endpos[0];
+
+ if (end->lnum < start->lnum
+ || (end->lnum == start->lnum && end->col < start->col)) {
+ rex.reg_mmatch->endpos[0] = rex.reg_mmatch->startpos[0];
+ }
+ } else {
+ if (rex.reg_match->endp[0] < rex.reg_match->startp[0]) {
+ rex.reg_match->endp[0] = rex.reg_match->startp[0];
+ }
+ }
+ }
+
+ return retval;
+}
+
+/// Match a regexp against a string.
+/// "rmp->regprog" is a compiled regexp as returned by vim_regcomp().
+/// Uses curbuf for line count and 'iskeyword'.
+/// If "line_lbr" is true, consider a "\n" in "line" to be a line break.
+///
+/// @param line string to match against
+/// @param col column to start looking for match
+///
+/// @return 0 for failure, number of lines contained in the match otherwise.
+static int bt_regexec_nl(regmatch_T *rmp, char_u *line, colnr_T col, bool line_lbr)
+{
+ rex.reg_match = rmp;
+ rex.reg_mmatch = NULL;
+ rex.reg_maxline = 0;
+ rex.reg_line_lbr = line_lbr;
+ rex.reg_buf = curbuf;
+ rex.reg_win = NULL;
+ rex.reg_ic = rmp->rm_ic;
+ rex.reg_icombine = false;
+ rex.reg_maxcol = 0;
+
+ long r = bt_regexec_both(line, col, NULL, NULL);
+ assert(r <= INT_MAX);
+ return (int)r;
+}
+
+/// Matches a regexp against multiple lines.
+/// "rmp->regprog" is a compiled regexp as returned by vim_regcomp().
+/// Uses curbuf for line count and 'iskeyword'.
+///
+/// @param win Window in which to search or NULL
+/// @param buf Buffer in which to search
+/// @param lnum Number of line to start looking for match
+/// @param col Column to start looking for match
+/// @param tm Timeout limit or NULL
+///
+/// @return zero if there is no match and number of lines contained in the match
+/// otherwise.
+static long bt_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf, linenr_T lnum, colnr_T col,
+ proftime_T *tm, int *timed_out)
+{
+ rex.reg_match = NULL;
+ rex.reg_mmatch = rmp;
+ rex.reg_buf = buf;
+ rex.reg_win = win;
+ rex.reg_firstlnum = lnum;
+ rex.reg_maxline = rex.reg_buf->b_ml.ml_line_count - lnum;
+ rex.reg_line_lbr = false;
+ rex.reg_ic = rmp->rmm_ic;
+ rex.reg_icombine = false;
+ rex.reg_maxcol = rmp->rmm_maxcol;
+
+ return bt_regexec_both(NULL, col, tm, timed_out);
+}
+
+/*
+ * Compare a number with the operand of RE_LNUM, RE_COL or RE_VCOL.
+ */
+static int re_num_cmp(uint32_t val, char_u *scan)
+{
+ uint32_t n = (uint32_t)OPERAND_MIN(scan);
+
+ if (OPERAND_CMP(scan) == '>') {
+ return val > n;
+ }
+ if (OPERAND_CMP(scan) == '<') {
+ return val < n;
+ }
+ return val == n;
+}
+
+#ifdef BT_REGEXP_DUMP
+
+/*
+ * regdump - dump a regexp onto stdout in vaguely comprehensible form
+ */
+static void regdump(char_u *pattern, bt_regprog_T *r)
+{
+ char_u *s;
+ int op = EXACTLY; // Arbitrary non-END op.
+ char_u *next;
+ char_u *end = NULL;
+ FILE *f;
+
+# ifdef BT_REGEXP_LOG
+ f = fopen("bt_regexp_log.log", "a");
+# else
+ f = stdout;
+# endif
+ if (f == NULL) {
+ return;
+ }
+ fprintf(f, "-------------------------------------\n\r\nregcomp(%s):\r\n",
+ pattern);
+
+ s = r->program + 1;
+ // Loop until we find the END that isn't before a referred next (an END
+ // can also appear in a NOMATCH operand).
+ while (op != END || s <= end) {
+ op = OP(s);
+ fprintf(f, "%2d%s", (int)(s - r->program), regprop(s)); // Where, what.
+ next = regnext(s);
+ if (next == NULL) { // Next ptr.
+ fprintf(f, "(0)");
+ } else {
+ fprintf(f, "(%d)", (int)((s - r->program) + (next - s)));
+ }
+ if (end < next) {
+ end = next;
+ }
+ if (op == BRACE_LIMITS) {
+ // Two ints
+ fprintf(f, " minval %" PRId64 ", maxval %" PRId64,
+ (int64_t)OPERAND_MIN(s), (int64_t)OPERAND_MAX(s));
+ s += 8;
+ } else if (op == BEHIND || op == NOBEHIND) {
+ // one int
+ fprintf(f, " count %" PRId64, (int64_t)OPERAND_MIN(s));
+ s += 4;
+ } else if (op == RE_LNUM || op == RE_COL || op == RE_VCOL) {
+ // one int plus comparator
+ fprintf(f, " count %" PRId64, (int64_t)OPERAND_MIN(s));
+ s += 5;
+ }
+ s += 3;
+ if (op == ANYOF || op == ANYOF + ADD_NL
+ || op == ANYBUT || op == ANYBUT + ADD_NL
+ || op == EXACTLY) {
+ // Literal string, where present.
+ fprintf(f, "\nxxxxxxxxx\n");
+ while (*s != NUL) {
+ fprintf(f, "%c", *s++);
+ }
+ fprintf(f, "\nxxxxxxxxx\n");
+ s++;
+ }
+ fprintf(f, "\r\n");
+ }
+
+ // Header fields of interest.
+ if (r->regstart != NUL) {
+ fprintf(f, "start `%s' 0x%x; ", r->regstart < 256
+ ? (char *)transchar(r->regstart)
+ : "multibyte", r->regstart);
+ }
+ if (r->reganch) {
+ fprintf(f, "anchored; ");
+ }
+ if (r->regmust != NULL) {
+ fprintf(f, "must have \"%s\"", r->regmust);
+ }
+ fprintf(f, "\r\n");
+
+# ifdef BT_REGEXP_LOG
+ fclose(f);
+# endif
+}
+#endif // BT_REGEXP_DUMP
+
+#ifdef REGEXP_DEBUG
+
+/*
+ * regprop - printable representation of opcode
+ */
+static char_u *regprop(char_u *op)
+{
+ char *p;
+ static char buf[50];
+
+ STRCPY(buf, ":");
+
+ switch ((int)OP(op)) {
+ case BOL:
+ p = "BOL";
+ break;
+ case EOL:
+ p = "EOL";
+ break;
+ case RE_BOF:
+ p = "BOF";
+ break;
+ case RE_EOF:
+ p = "EOF";
+ break;
+ case CURSOR:
+ p = "CURSOR";
+ break;
+ case RE_VISUAL:
+ p = "RE_VISUAL";
+ break;
+ case RE_LNUM:
+ p = "RE_LNUM";
+ break;
+ case RE_MARK:
+ p = "RE_MARK";
+ break;
+ case RE_COL:
+ p = "RE_COL";
+ break;
+ case RE_VCOL:
+ p = "RE_VCOL";
+ break;
+ case BOW:
+ p = "BOW";
+ break;
+ case EOW:
+ p = "EOW";
+ break;
+ case ANY:
+ p = "ANY";
+ break;
+ case ANY + ADD_NL:
+ p = "ANY+NL";
+ break;
+ case ANYOF:
+ p = "ANYOF";
+ break;
+ case ANYOF + ADD_NL:
+ p = "ANYOF+NL";
+ break;
+ case ANYBUT:
+ p = "ANYBUT";
+ break;
+ case ANYBUT + ADD_NL:
+ p = "ANYBUT+NL";
+ break;
+ case IDENT:
+ p = "IDENT";
+ break;
+ case IDENT + ADD_NL:
+ p = "IDENT+NL";
+ break;
+ case SIDENT:
+ p = "SIDENT";
+ break;
+ case SIDENT + ADD_NL:
+ p = "SIDENT+NL";
+ break;
+ case KWORD:
+ p = "KWORD";
+ break;
+ case KWORD + ADD_NL:
+ p = "KWORD+NL";
+ break;
+ case SKWORD:
+ p = "SKWORD";
+ break;
+ case SKWORD + ADD_NL:
+ p = "SKWORD+NL";
+ break;
+ case FNAME:
+ p = "FNAME";
+ break;
+ case FNAME + ADD_NL:
+ p = "FNAME+NL";
+ break;
+ case SFNAME:
+ p = "SFNAME";
+ break;
+ case SFNAME + ADD_NL:
+ p = "SFNAME+NL";
+ break;
+ case PRINT:
+ p = "PRINT";
+ break;
+ case PRINT + ADD_NL:
+ p = "PRINT+NL";
+ break;
+ case SPRINT:
+ p = "SPRINT";
+ break;
+ case SPRINT + ADD_NL:
+ p = "SPRINT+NL";
+ break;
+ case WHITE:
+ p = "WHITE";
+ break;
+ case WHITE + ADD_NL:
+ p = "WHITE+NL";
+ break;
+ case NWHITE:
+ p = "NWHITE";
+ break;
+ case NWHITE + ADD_NL:
+ p = "NWHITE+NL";
+ break;
+ case DIGIT:
+ p = "DIGIT";
+ break;
+ case DIGIT + ADD_NL:
+ p = "DIGIT+NL";
+ break;
+ case NDIGIT:
+ p = "NDIGIT";
+ break;
+ case NDIGIT + ADD_NL:
+ p = "NDIGIT+NL";
+ break;
+ case HEX:
+ p = "HEX";
+ break;
+ case HEX + ADD_NL:
+ p = "HEX+NL";
+ break;
+ case NHEX:
+ p = "NHEX";
+ break;
+ case NHEX + ADD_NL:
+ p = "NHEX+NL";
+ break;
+ case OCTAL:
+ p = "OCTAL";
+ break;
+ case OCTAL + ADD_NL:
+ p = "OCTAL+NL";
+ break;
+ case NOCTAL:
+ p = "NOCTAL";
+ break;
+ case NOCTAL + ADD_NL:
+ p = "NOCTAL+NL";
+ break;
+ case WORD:
+ p = "WORD";
+ break;
+ case WORD + ADD_NL:
+ p = "WORD+NL";
+ break;
+ case NWORD:
+ p = "NWORD";
+ break;
+ case NWORD + ADD_NL:
+ p = "NWORD+NL";
+ break;
+ case HEAD:
+ p = "HEAD";
+ break;
+ case HEAD + ADD_NL:
+ p = "HEAD+NL";
+ break;
+ case NHEAD:
+ p = "NHEAD";
+ break;
+ case NHEAD + ADD_NL:
+ p = "NHEAD+NL";
+ break;
+ case ALPHA:
+ p = "ALPHA";
+ break;
+ case ALPHA + ADD_NL:
+ p = "ALPHA+NL";
+ break;
+ case NALPHA:
+ p = "NALPHA";
+ break;
+ case NALPHA + ADD_NL:
+ p = "NALPHA+NL";
+ break;
+ case LOWER:
+ p = "LOWER";
+ break;
+ case LOWER + ADD_NL:
+ p = "LOWER+NL";
+ break;
+ case NLOWER:
+ p = "NLOWER";
+ break;
+ case NLOWER + ADD_NL:
+ p = "NLOWER+NL";
+ break;
+ case UPPER:
+ p = "UPPER";
+ break;
+ case UPPER + ADD_NL:
+ p = "UPPER+NL";
+ break;
+ case NUPPER:
+ p = "NUPPER";
+ break;
+ case NUPPER + ADD_NL:
+ p = "NUPPER+NL";
+ break;
+ case BRANCH:
+ p = "BRANCH";
+ break;
+ case EXACTLY:
+ p = "EXACTLY";
+ break;
+ case NOTHING:
+ p = "NOTHING";
+ break;
+ case BACK:
+ p = "BACK";
+ break;
+ case END:
+ p = "END";
+ break;
+ case MOPEN + 0:
+ p = "MATCH START";
+ break;
+ case MOPEN + 1:
+ case MOPEN + 2:
+ case MOPEN + 3:
+ case MOPEN + 4:
+ case MOPEN + 5:
+ case MOPEN + 6:
+ case MOPEN + 7:
+ case MOPEN + 8:
+ case MOPEN + 9:
+ sprintf(buf + STRLEN(buf), "MOPEN%d", OP(op) - MOPEN);
+ p = NULL;
+ break;
+ case MCLOSE + 0:
+ p = "MATCH END";
+ break;
+ case MCLOSE + 1:
+ case MCLOSE + 2:
+ case MCLOSE + 3:
+ case MCLOSE + 4:
+ case MCLOSE + 5:
+ case MCLOSE + 6:
+ case MCLOSE + 7:
+ case MCLOSE + 8:
+ case MCLOSE + 9:
+ sprintf(buf + STRLEN(buf), "MCLOSE%d", OP(op) - MCLOSE);
+ p = NULL;
+ break;
+ case BACKREF + 1:
+ case BACKREF + 2:
+ case BACKREF + 3:
+ case BACKREF + 4:
+ case BACKREF + 5:
+ case BACKREF + 6:
+ case BACKREF + 7:
+ case BACKREF + 8:
+ case BACKREF + 9:
+ sprintf(buf + STRLEN(buf), "BACKREF%d", OP(op) - BACKREF);
+ p = NULL;
+ break;
+ case NOPEN:
+ p = "NOPEN";
+ break;
+ case NCLOSE:
+ p = "NCLOSE";
+ break;
+ case ZOPEN + 1:
+ case ZOPEN + 2:
+ case ZOPEN + 3:
+ case ZOPEN + 4:
+ case ZOPEN + 5:
+ case ZOPEN + 6:
+ case ZOPEN + 7:
+ case ZOPEN + 8:
+ case ZOPEN + 9:
+ sprintf(buf + STRLEN(buf), "ZOPEN%d", OP(op) - ZOPEN);
+ p = NULL;
+ break;
+ case ZCLOSE + 1:
+ case ZCLOSE + 2:
+ case ZCLOSE + 3:
+ case ZCLOSE + 4:
+ case ZCLOSE + 5:
+ case ZCLOSE + 6:
+ case ZCLOSE + 7:
+ case ZCLOSE + 8:
+ case ZCLOSE + 9:
+ sprintf(buf + STRLEN(buf), "ZCLOSE%d", OP(op) - ZCLOSE);
+ p = NULL;
+ break;
+ case ZREF + 1:
+ case ZREF + 2:
+ case ZREF + 3:
+ case ZREF + 4:
+ case ZREF + 5:
+ case ZREF + 6:
+ case ZREF + 7:
+ case ZREF + 8:
+ case ZREF + 9:
+ sprintf(buf + STRLEN(buf), "ZREF%d", OP(op) - ZREF);
+ p = NULL;
+ break;
+ case STAR:
+ p = "STAR";
+ break;
+ case PLUS:
+ p = "PLUS";
+ break;
+ case NOMATCH:
+ p = "NOMATCH";
+ break;
+ case MATCH:
+ p = "MATCH";
+ break;
+ case BEHIND:
+ p = "BEHIND";
+ break;
+ case NOBEHIND:
+ p = "NOBEHIND";
+ break;
+ case SUBPAT:
+ p = "SUBPAT";
+ break;
+ case BRACE_LIMITS:
+ p = "BRACE_LIMITS";
+ break;
+ case BRACE_SIMPLE:
+ p = "BRACE_SIMPLE";
+ break;
+ case BRACE_COMPLEX + 0:
+ case BRACE_COMPLEX + 1:
+ case BRACE_COMPLEX + 2:
+ case BRACE_COMPLEX + 3:
+ case BRACE_COMPLEX + 4:
+ case BRACE_COMPLEX + 5:
+ case BRACE_COMPLEX + 6:
+ case BRACE_COMPLEX + 7:
+ case BRACE_COMPLEX + 8:
+ case BRACE_COMPLEX + 9:
+ sprintf(buf + STRLEN(buf), "BRACE_COMPLEX%d", OP(op) - BRACE_COMPLEX);
+ p = NULL;
+ break;
+ case MULTIBYTECODE:
+ p = "MULTIBYTECODE";
+ break;
+ case NEWL:
+ p = "NEWL";
+ break;
+ default:
+ sprintf(buf + STRLEN(buf), "corrupt %d", OP(op));
+ p = NULL;
+ break;
+ }
+ if (p != NULL) {
+ STRCAT(buf, p);
+ }
+ return (char_u *)buf;
+}
+#endif // REGEXP_DEBUG
diff --git a/src/nvim/regexp_defs.h b/src/nvim/regexp_defs.h
index 1d112bd64a..09f244c2f6 100644
--- a/src/nvim/regexp_defs.h
+++ b/src/nvim/regexp_defs.h
@@ -34,7 +34,7 @@
// In the NFA engine: how many states are allowed.
#define NFA_MAX_STATES 100000
-#define NFA_TOO_EXPENSIVE -1
+#define NFA_TOO_EXPENSIVE (-1)
// Which regexp engine to use? Needed for vim_regcomp().
// Must match with 'regexpengine'.
@@ -157,12 +157,20 @@ struct reg_extmatch {
};
struct regengine {
+ /// bt_regcomp or nfa_regcomp
regprog_T *(*regcomp)(char_u *, int);
+ /// bt_regfree or nfa_regfree
void (*regfree)(regprog_T *);
+ /// bt_regexec_nl or nfa_regexec_nl
int (*regexec_nl)(regmatch_T *, char_u *, colnr_T, bool);
- long (*regexec_multi)(regmmatch_T *, win_T *, buf_T *, linenr_T, colnr_T,
- proftime_T *, int *);
- char_u *expr;
+ /// bt_regexec_mult or nfa_regexec_mult
+ long (*regexec_multi)(regmmatch_T *, win_T *, buf_T *, linenr_T, colnr_T, proftime_T *, int *);
+ // char_u *expr;
};
+// Flags used by vim_regsub() and vim_regsub_both()
+#define REGSUB_COPY 1
+#define REGSUB_MAGIC 2
+#define REGSUB_BACKSLASH 4
+
#endif // NVIM_REGEXP_DEFS_H
diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c
index cafffc0319..870af3eafc 100644
--- a/src/nvim/regexp_nfa.c
+++ b/src/nvim/regexp_nfa.c
@@ -1,8 +1,6 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
-// uncrustify:off
-
/*
* NFA regular expression implementation.
*
@@ -11,8 +9,8 @@
#include <assert.h>
#include <inttypes.h>
-#include <stdbool.h>
#include <limits.h>
+#include <stdbool.h>
#include "nvim/ascii.h"
#include "nvim/garray.h"
@@ -39,13 +37,13 @@
# define NFA_REGEXP_DEBUG_LOG "nfa_regexp_debug.log"
#endif
-/* Added to NFA_ANY - NFA_NUPPER_IC to include a NL. */
+// Added to NFA_ANY - NFA_NUPPER_IC to include a NL.
#define NFA_ADD_NL 31
enum {
NFA_SPLIT = -1024,
NFA_MATCH,
- NFA_EMPTY, /* matches 0-length */
+ NFA_EMPTY, // matches 0-length
NFA_START_COLL, // [abc] start
NFA_END_COLL, // [abc] end
@@ -64,17 +62,17 @@ enum {
NFA_QUEST, // greedy \? (postfix only)
NFA_QUEST_NONGREEDY, // non-greedy \? (postfix only)
- NFA_BOL, /* ^ Begin line */
- NFA_EOL, /* $ End line */
- NFA_BOW, /* \< Begin word */
- NFA_EOW, /* \> End word */
- NFA_BOF, /* \%^ Begin file */
- NFA_EOF, /* \%$ End file */
+ NFA_BOL, // ^ Begin line
+ NFA_EOL, // $ End line
+ NFA_BOW, // \< Begin word
+ NFA_EOW, // \> End word
+ NFA_BOF, // \%^ Begin file
+ NFA_EOF, // \%$ End file
NFA_NEWL,
- NFA_ZSTART, /* Used for \zs */
- NFA_ZEND, /* Used for \ze */
- NFA_NOPEN, /* Start of subexpression marked with \%( */
- NFA_NCLOSE, /* End of subexpr. marked with \%( ... \) */
+ NFA_ZSTART, // Used for \zs
+ NFA_ZEND, // Used for \ze
+ NFA_NOPEN, // Start of subexpression marked with \%(
+ NFA_NCLOSE, // End of subexpr. marked with \%( ... \)
NFA_START_INVISIBLE,
NFA_START_INVISIBLE_FIRST,
NFA_START_INVISIBLE_NEG,
@@ -91,34 +89,34 @@ enum {
// composing multibyte char
NFA_END_COMPOSING, // End of a composing char in the NFA
NFA_ANY_COMPOSING, // \%C: Any composing characters.
- NFA_OPT_CHARS, /* \%[abc] */
-
- /* The following are used only in the postfix form, not in the NFA */
- NFA_PREV_ATOM_NO_WIDTH, /* Used for \@= */
- NFA_PREV_ATOM_NO_WIDTH_NEG, /* Used for \@! */
- NFA_PREV_ATOM_JUST_BEFORE, /* Used for \@<= */
- NFA_PREV_ATOM_JUST_BEFORE_NEG, /* Used for \@<! */
- NFA_PREV_ATOM_LIKE_PATTERN, /* Used for \@> */
-
- NFA_BACKREF1, /* \1 */
- NFA_BACKREF2, /* \2 */
- NFA_BACKREF3, /* \3 */
- NFA_BACKREF4, /* \4 */
- NFA_BACKREF5, /* \5 */
- NFA_BACKREF6, /* \6 */
- NFA_BACKREF7, /* \7 */
- NFA_BACKREF8, /* \8 */
- NFA_BACKREF9, /* \9 */
- NFA_ZREF1, /* \z1 */
- NFA_ZREF2, /* \z2 */
- NFA_ZREF3, /* \z3 */
- NFA_ZREF4, /* \z4 */
- NFA_ZREF5, /* \z5 */
- NFA_ZREF6, /* \z6 */
- NFA_ZREF7, /* \z7 */
- NFA_ZREF8, /* \z8 */
- NFA_ZREF9, /* \z9 */
- NFA_SKIP, /* Skip characters */
+ NFA_OPT_CHARS, // \%[abc]
+
+ // The following are used only in the postfix form, not in the NFA
+ NFA_PREV_ATOM_NO_WIDTH, // Used for \@=
+ NFA_PREV_ATOM_NO_WIDTH_NEG, // Used for \@!
+ NFA_PREV_ATOM_JUST_BEFORE, // Used for \@<=
+ NFA_PREV_ATOM_JUST_BEFORE_NEG, // Used for \@<!
+ NFA_PREV_ATOM_LIKE_PATTERN, // Used for \@>
+
+ NFA_BACKREF1, // \1
+ NFA_BACKREF2, // \2
+ NFA_BACKREF3, // \3
+ NFA_BACKREF4, // \4
+ NFA_BACKREF5, // \5
+ NFA_BACKREF6, // \6
+ NFA_BACKREF7, // \7
+ NFA_BACKREF8, // \8
+ NFA_BACKREF9, // \9
+ NFA_ZREF1, // \z1
+ NFA_ZREF2, // \z2
+ NFA_ZREF3, // \z3
+ NFA_ZREF4, // \z4
+ NFA_ZREF5, // \z5
+ NFA_ZREF6, // \z6
+ NFA_ZREF7, // \z7
+ NFA_ZREF8, // \z8
+ NFA_ZREF9, // \z9
+ NFA_SKIP, // Skip characters
NFA_MOPEN,
NFA_MOPEN1,
@@ -164,58 +162,58 @@ enum {
NFA_ZCLOSE8,
NFA_ZCLOSE9,
- /* NFA_FIRST_NL */
- NFA_ANY, /* Match any one character. */
- NFA_IDENT, /* Match identifier char */
- NFA_SIDENT, /* Match identifier char but no digit */
- NFA_KWORD, /* Match keyword char */
- NFA_SKWORD, /* Match word char but no digit */
- NFA_FNAME, /* Match file name char */
- NFA_SFNAME, /* Match file name char but no digit */
- NFA_PRINT, /* Match printable char */
- NFA_SPRINT, /* Match printable char but no digit */
- NFA_WHITE, /* Match whitespace char */
- NFA_NWHITE, /* Match non-whitespace char */
- NFA_DIGIT, /* Match digit char */
- NFA_NDIGIT, /* Match non-digit char */
- NFA_HEX, /* Match hex char */
- NFA_NHEX, /* Match non-hex char */
- NFA_OCTAL, /* Match octal char */
- NFA_NOCTAL, /* Match non-octal char */
- NFA_WORD, /* Match word char */
- NFA_NWORD, /* Match non-word char */
- NFA_HEAD, /* Match head char */
- NFA_NHEAD, /* Match non-head char */
- NFA_ALPHA, /* Match alpha char */
- NFA_NALPHA, /* Match non-alpha char */
- NFA_LOWER, /* Match lowercase char */
- NFA_NLOWER, /* Match non-lowercase char */
- NFA_UPPER, /* Match uppercase char */
- NFA_NUPPER, /* Match non-uppercase char */
- NFA_LOWER_IC, /* Match [a-z] */
- NFA_NLOWER_IC, /* Match [^a-z] */
- NFA_UPPER_IC, /* Match [A-Z] */
- NFA_NUPPER_IC, /* Match [^A-Z] */
+ // NFA_FIRST_NL
+ NFA_ANY, // Match any one character.
+ NFA_IDENT, // Match identifier char
+ NFA_SIDENT, // Match identifier char but no digit
+ NFA_KWORD, // Match keyword char
+ NFA_SKWORD, // Match word char but no digit
+ NFA_FNAME, // Match file name char
+ NFA_SFNAME, // Match file name char but no digit
+ NFA_PRINT, // Match printable char
+ NFA_SPRINT, // Match printable char but no digit
+ NFA_WHITE, // Match whitespace char
+ NFA_NWHITE, // Match non-whitespace char
+ NFA_DIGIT, // Match digit char
+ NFA_NDIGIT, // Match non-digit char
+ NFA_HEX, // Match hex char
+ NFA_NHEX, // Match non-hex char
+ NFA_OCTAL, // Match octal char
+ NFA_NOCTAL, // Match non-octal char
+ NFA_WORD, // Match word char
+ NFA_NWORD, // Match non-word char
+ NFA_HEAD, // Match head char
+ NFA_NHEAD, // Match non-head char
+ NFA_ALPHA, // Match alpha char
+ NFA_NALPHA, // Match non-alpha char
+ NFA_LOWER, // Match lowercase char
+ NFA_NLOWER, // Match non-lowercase char
+ NFA_UPPER, // Match uppercase char
+ NFA_NUPPER, // Match non-uppercase char
+ NFA_LOWER_IC, // Match [a-z]
+ NFA_NLOWER_IC, // Match [^a-z]
+ NFA_UPPER_IC, // Match [A-Z]
+ NFA_NUPPER_IC, // Match [^A-Z]
NFA_FIRST_NL = NFA_ANY + NFA_ADD_NL,
NFA_LAST_NL = NFA_NUPPER_IC + NFA_ADD_NL,
- NFA_CURSOR, /* Match cursor pos */
- NFA_LNUM, /* Match line number */
- NFA_LNUM_GT, /* Match > line number */
- NFA_LNUM_LT, /* Match < line number */
- NFA_COL, /* Match cursor column */
- NFA_COL_GT, /* Match > cursor column */
- NFA_COL_LT, /* Match < cursor column */
- NFA_VCOL, /* Match cursor virtual column */
- NFA_VCOL_GT, /* Match > cursor virtual column */
- NFA_VCOL_LT, /* Match < cursor virtual column */
- NFA_MARK, /* Match mark */
- NFA_MARK_GT, /* Match > mark */
- NFA_MARK_LT, /* Match < mark */
- NFA_VISUAL, /* Match Visual area */
-
- /* Character classes [:alnum:] etc */
+ NFA_CURSOR, // Match cursor pos
+ NFA_LNUM, // Match line number
+ NFA_LNUM_GT, // Match > line number
+ NFA_LNUM_LT, // Match < line number
+ NFA_COL, // Match cursor column
+ NFA_COL_GT, // Match > cursor column
+ NFA_COL_LT, // Match < cursor column
+ NFA_VCOL, // Match cursor virtual column
+ NFA_VCOL_GT, // Match > cursor virtual column
+ NFA_VCOL_LT, // Match < cursor virtual column
+ NFA_MARK, // Match mark
+ NFA_MARK_GT, // Match > mark
+ NFA_MARK_LT, // Match < mark
+ NFA_VISUAL, // Match Visual area
+
+ // Character classes [:alnum:] etc
NFA_CLASS_ALNUM,
NFA_CLASS_ALPHA,
NFA_CLASS_BLANK,
@@ -237,9 +235,9 @@ enum {
NFA_CLASS_FNAME,
};
-/* Keep in sync with classchars. */
+// Keep in sync with classchars.
static int nfa_classcodes[] = {
- NFA_ANY, NFA_IDENT, NFA_SIDENT, NFA_KWORD,NFA_SKWORD,
+ NFA_ANY, NFA_IDENT, NFA_SIDENT, NFA_KWORD, NFA_SKWORD,
NFA_FNAME, NFA_SFNAME, NFA_PRINT, NFA_SPRINT,
NFA_WHITE, NFA_NWHITE, NFA_DIGIT, NFA_NDIGIT,
NFA_HEX, NFA_NHEX, NFA_OCTAL, NFA_NOCTAL,
@@ -248,11 +246,9 @@ static int nfa_classcodes[] = {
NFA_UPPER, NFA_NUPPER
};
-static char_u e_nul_found[] = N_(
- "E865: (NFA) Regexp end encountered prematurely");
+static char_u e_nul_found[] = N_("E865: (NFA) Regexp end encountered prematurely");
static char_u e_misplaced[] = N_("E866: (NFA regexp) Misplaced %c");
-static char_u e_ill_char_class[] = N_(
- "E877: (NFA regexp) Invalid character class: %" PRId64);
+static char_u e_ill_char_class[] = N_("E877: (NFA regexp) Invalid character class: %" PRId64);
static char_u e_value_too_large[] = N_("E951: \\% value too large");
// Since the out pointers in the list are always
@@ -260,13 +256,13 @@ static char_u e_value_too_large[] = N_("E951: \\% value too large");
// as storage for the Ptrlists.
typedef union Ptrlist Ptrlist;
union Ptrlist {
- Ptrlist *next;
+ Ptrlist *next;
nfa_state_T *s;
};
struct Frag {
nfa_state_T *start;
- Ptrlist *out;
+ Ptrlist *out;
};
typedef struct Frag Frag_T;
@@ -276,36 +272,36 @@ typedef struct {
// When REG_MULTI is true list.multi is used, otherwise list.line.
union {
struct multipos {
- linenr_T start_lnum;
- linenr_T end_lnum;
+ linenr_T start_lnum;
+ linenr_T end_lnum;
colnr_T start_col;
colnr_T end_col;
} multi[NSUBEXP];
struct linepos {
- char_u *start;
- char_u *end;
+ char_u *start;
+ char_u *end;
} line[NSUBEXP];
} list;
} regsub_T;
typedef struct {
- regsub_T norm; /* \( .. \) matches */
- regsub_T synt; /* \z( .. \) matches */
+ regsub_T norm; // \( .. \) matches
+ regsub_T synt; // \z( .. \) matches
} regsubs_T;
-/* nfa_pim_T stores a Postponed Invisible Match. */
+// nfa_pim_T stores a Postponed Invisible Match.
typedef struct nfa_pim_S nfa_pim_T;
struct nfa_pim_S {
- int result; /* NFA_PIM_*, see below */
- nfa_state_T *state; /* the invisible match start state */
- regsubs_T subs; /* submatch info, only party used */
+ int result; // NFA_PIM_*, see below
+ nfa_state_T *state; // the invisible match start state
+ regsubs_T subs; // submatch info, only party used
union {
lpos_T pos;
- char_u *ptr;
- } end; /* where the match must end */
+ char_u *ptr;
+ } end; // where the match must end
};
-/* nfa_thread_T contains execution information of a NFA state */
+// nfa_thread_T contains execution information of a NFA state
typedef struct {
nfa_state_T *state;
int count;
@@ -316,7 +312,7 @@ typedef struct {
// nfa_list_T contains the alternative NFA execution states.
typedef struct {
- nfa_thread_T *t; ///< allocated array of states
+ nfa_thread_T *t; ///< allocated array of states
int n; ///< nr of states currently in "t"
int len; ///< max nr of states in "t"
int id; ///< ID of the list
@@ -337,10 +333,10 @@ static bool wants_nfa;
static int nstate; ///< Number of states in the NFA. Also used when executing.
static int istate; ///< Index in the state vector, used in alloc_state()
-/* If not NULL match must end at this position */
+// If not NULL match must end at this position
static save_se_T *nfa_endp = NULL;
-/* 0 for first call to nfa_regmatch(), 1 for recursive call. */
+// 0 for first call to nfa_regmatch(), 1 for recursive call.
static int nfa_ll_index = 0;
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -349,35 +345,31 @@ static int nfa_ll_index = 0;
// Helper functions used when doing re2post() ... regatom() parsing
#define EMIT(c) \
- do { \
- if (post_ptr >= post_end) { \
- realloc_post_list(); \
- } \
- *post_ptr++ = c; \
- } while (0)
-
-/*
- * Initialize internal variables before NFA compilation.
- */
-static void
-nfa_regcomp_start (
- char_u *expr,
- int re_flags /* see vim_regcomp() */
-)
+ do { \
+ if (post_ptr >= post_end) { \
+ realloc_post_list(); \
+ } \
+ *post_ptr++ = c; \
+ } while (0)
+
+/// Initialize internal variables before NFA compilation.
+///
+/// @param re_flags @see vim_regcomp()
+static void nfa_regcomp_start(char_u *expr, int re_flags)
{
size_t postfix_size;
size_t nstate_max;
nstate = 0;
istate = 0;
- /* A reasonable estimation for maximum size */
+ // A reasonable estimation for maximum size
nstate_max = (STRLEN(expr) + 1) * 25;
// Some items blow up in size, such as [A-z]. Add more space for that.
// When it is still not enough realloc_post_list() will be used.
nstate_max += 1000;
- /* Size for postfix representation of expr. */
+ // Size for postfix representation of expr.
postfix_size = sizeof(int) * nstate_max;
post_start = (int *)xmalloc(postfix_size);
@@ -387,7 +379,7 @@ nfa_regcomp_start (
rex.nfa_has_zend = false;
rex.nfa_has_backref = false;
- /* shared with BT engine */
+ // shared with BT engine
regcomp_start(expr, re_flags);
}
@@ -399,14 +391,15 @@ static int nfa_get_reganch(nfa_state_T *start, int depth)
{
nfa_state_T *p = start;
- if (depth > 4)
+ if (depth > 4) {
return 0;
+ }
while (p != NULL) {
switch (p->c) {
case NFA_BOL:
case NFA_BOF:
- return 1; /* yes! */
+ return 1; // yes!
case NFA_ZSTART:
case NFA_ZEND:
@@ -442,7 +435,7 @@ static int nfa_get_reganch(nfa_state_T *start, int depth)
&& nfa_get_reganch(p->out1, depth + 1);
default:
- return 0; /* noooo */
+ return 0; // noooo
}
}
return 0;
@@ -456,12 +449,13 @@ static int nfa_get_regstart(nfa_state_T *start, int depth)
{
nfa_state_T *p = start;
- if (depth > 4)
+ if (depth > 4) {
return 0;
+ }
while (p != NULL) {
switch (p->c) {
- /* all kinds of zero-width matches */
+ // all kinds of zero-width matches
case NFA_BOL:
case NFA_BOF:
case NFA_BOW:
@@ -507,19 +501,20 @@ static int nfa_get_regstart(nfa_state_T *start, int depth)
p = p->out;
break;
- case NFA_SPLIT:
- {
+ case NFA_SPLIT: {
int c1 = nfa_get_regstart(p->out, depth + 1);
int c2 = nfa_get_regstart(p->out1, depth + 1);
- if (c1 == c2)
- return c1; /* yes! */
+ if (c1 == c2) {
+ return c1; // yes!
+ }
return 0;
}
default:
- if (p->c > 0)
- return p->c; /* yes! */
+ if (p->c > 0) {
+ return p->c; // yes!
+ }
return 0;
}
}
@@ -535,24 +530,26 @@ static char_u *nfa_get_match_text(nfa_state_T *start)
{
nfa_state_T *p = start;
int len = 0;
- char_u *ret;
- char_u *s;
+ char_u *ret;
+ char_u *s;
- if (p->c != NFA_MOPEN)
- return NULL; /* just in case */
+ if (p->c != NFA_MOPEN) {
+ return NULL; // just in case
+ }
p = p->out;
while (p->c > 0) {
len += utf_char2len(p->c);
p = p->out;
}
- if (p->c != NFA_MCLOSE || p->out->c != NFA_MATCH)
+ if (p->c != NFA_MCLOSE || p->out->c != NFA_MATCH) {
return NULL;
+ }
ret = xmalloc(len);
- p = start->out->out; /* skip first char, it goes into regstart */
+ p = start->out->out; // skip first char, it goes into regstart
s = ret;
while (p->c > 0) {
- s += utf_char2bytes(p->c, s);
+ s += utf_char2bytes(p->c, (char *)s);
p = p->out;
}
*s = NUL;
@@ -587,22 +584,23 @@ static void realloc_post_list(void)
*/
static int nfa_recognize_char_class(char_u *start, char_u *end, int extra_newl)
{
-# define CLASS_not 0x80
-# define CLASS_af 0x40
-# define CLASS_AF 0x20
-# define CLASS_az 0x10
-# define CLASS_AZ 0x08
-# define CLASS_o7 0x04
-# define CLASS_o9 0x02
-# define CLASS_underscore 0x01
-
- char_u *p;
+#define CLASS_not 0x80
+#define CLASS_af 0x40
+#define CLASS_AF 0x20
+#define CLASS_az 0x10
+#define CLASS_AZ 0x08
+#define CLASS_o7 0x04
+#define CLASS_o9 0x02
+#define CLASS_underscore 0x01
+
+ char_u *p;
int config = 0;
bool newl = extra_newl == true;
- if (*end != ']')
+ if (*end != ']') {
return FAIL;
+ }
p = start;
if (*p == '^') {
config |= CLASS_not;
@@ -652,12 +650,14 @@ static int nfa_recognize_char_class(char_u *start, char_u *end, int extra_newl)
} else if (*p == '\n') {
newl = true;
p++;
- } else
+ } else {
return FAIL;
- } /* while (p < end) */
+ }
+ } // while (p < end)
- if (p != end)
+ if (p != end) {
return FAIL;
+ }
if (newl == true) {
extra_newl = NFA_ADD_NL;
@@ -711,7 +711,6 @@ static int nfa_recognize_char_class(char_u *start, char_u *end, int extra_newl)
static void nfa_emit_equi_class(int c)
{
#define EMIT2(c) EMIT(c); EMIT(NFA_CONCAT);
-#define EMITMBC(c) EMIT(c); EMIT(NFA_CONCAT);
{
#define A_grave 0xc0
@@ -770,362 +769,1013 @@ static void nfa_emit_equi_class(int c)
#define y_acute 0xfd
#define y_diaeresis 0xff
switch (c) {
- case 'A': case A_grave: case A_acute: case A_circumflex:
- case A_virguilla: case A_diaeresis: case A_ring:
- CASEMBC(0x100) CASEMBC(0x102) CASEMBC(0x104)
- CASEMBC(0x1cd) CASEMBC(0x1de) CASEMBC(0x1e0)
- CASEMBC(0x1ea2)
- EMIT2('A'); EMIT2(A_grave); EMIT2(A_acute);
- EMIT2(A_circumflex); EMIT2(A_virguilla);
- EMIT2(A_diaeresis); EMIT2(A_ring);
- EMITMBC(0x100) EMITMBC(0x102) EMITMBC(0x104)
- EMITMBC(0x1cd) EMITMBC(0x1de) EMITMBC(0x1e0)
- EMITMBC(0x1ea2)
+ case 'A':
+ case A_grave:
+ case A_acute:
+ case A_circumflex:
+ case A_virguilla:
+ case A_diaeresis:
+ case A_ring:
+ case 0x100:
+ case 0x102:
+ case 0x104:
+ case 0x1cd:
+ case 0x1de:
+ case 0x1e0:
+ case 0x1fa:
+ case 0x200:
+ case 0x202:
+ case 0x226:
+ case 0x23a:
+ case 0x1e00:
+ case 0x1ea0:
+ case 0x1ea2:
+ case 0x1ea4:
+ case 0x1ea6:
+ case 0x1ea8:
+ case 0x1eaa:
+ case 0x1eac:
+ case 0x1eae:
+ case 0x1eb0:
+ case 0x1eb2:
+ case 0x1eb4:
+ case 0x1eb6:
+ EMIT2('A') EMIT2(A_grave) EMIT2(A_acute) // NOLINT(whitespace/cast)
+ EMIT2(A_circumflex) EMIT2(A_virguilla) // NOLINT(whitespace/cast)
+ EMIT2(A_diaeresis) EMIT2(A_ring) // NOLINT(whitespace/cast)
+ EMIT2(0x100) EMIT2(0x102) EMIT2(0x104)
+ EMIT2(0x1cd) EMIT2(0x1de) EMIT2(0x1e0)
+ EMIT2(0x1fa) EMIT2(0x200) EMIT2(0x202)
+ EMIT2(0x226) EMIT2(0x23a) EMIT2(0x1e00)
+ EMIT2(0x1ea0) EMIT2(0x1ea2) EMIT2(0x1ea4)
+ EMIT2(0x1ea6) EMIT2(0x1ea8) EMIT2(0x1eaa)
+ EMIT2(0x1eac) EMIT2(0x1eae) EMIT2(0x1eb0)
+ EMIT2(0x1eb2) EMIT2(0x1eb6) EMIT2(0x1eb4)
+ return;
+
+ case 'B':
+ case 0x181:
+ case 0x243:
+ case 0x1e02:
+ case 0x1e04:
+ case 0x1e06:
+ EMIT2('B')
+ EMIT2(0x181) EMIT2(0x243) EMIT2(0x1e02)
+ EMIT2(0x1e04) EMIT2(0x1e06)
+ return;
+
+ case 'C':
+ case C_cedilla:
+ case 0x106:
+ case 0x108:
+ case 0x10a:
+ case 0x10c:
+ case 0x187:
+ case 0x23b:
+ case 0x1e08:
+ case 0xa792:
+ EMIT2('C') EMIT2(C_cedilla)
+ EMIT2(0x106) EMIT2(0x108) EMIT2(0x10a)
+ EMIT2(0x10c) EMIT2(0x187) EMIT2(0x23b)
+ EMIT2(0x1e08) EMIT2(0xa792)
return;
- case 'B': CASEMBC(0x1e02) CASEMBC(0x1e06)
- EMIT2('B'); EMITMBC(0x1e02) EMITMBC(0x1e06)
+ case 'D':
+ case 0x10e:
+ case 0x110:
+ case 0x18a:
+ case 0x1e0a:
+ case 0x1e0c:
+ case 0x1e0e:
+ case 0x1e10:
+ case 0x1e12:
+ EMIT2('D') EMIT2(0x10e) EMIT2(0x110) EMIT2(0x18a)
+ EMIT2(0x1e0a) EMIT2(0x1e0c) EMIT2(0x1e0e)
+ EMIT2(0x1e10) EMIT2(0x1e12)
return;
- case 'C': case C_cedilla: CASEMBC(0x106) CASEMBC(0x108) CASEMBC(0x10a)
- CASEMBC(0x10c)
- EMIT2('C'); EMIT2(C_cedilla); EMITMBC(0x106) EMITMBC(0x108)
- EMITMBC(0x10a) EMITMBC(0x10c)
+ case 'E':
+ case E_grave:
+ case E_acute:
+ case E_circumflex:
+ case E_diaeresis:
+ case 0x112:
+ case 0x114:
+ case 0x116:
+ case 0x118:
+ case 0x11a:
+ case 0x204:
+ case 0x206:
+ case 0x228:
+ case 0x246:
+ case 0x1e14:
+ case 0x1e16:
+ case 0x1e18:
+ case 0x1e1a:
+ case 0x1e1c:
+ case 0x1eb8:
+ case 0x1eba:
+ case 0x1ebc:
+ case 0x1ebe:
+ case 0x1ec0:
+ case 0x1ec2:
+ case 0x1ec4:
+ case 0x1ec6:
+ EMIT2('E') EMIT2(E_grave) EMIT2(E_acute) // NOLINT(whitespace/cast)
+ EMIT2(E_circumflex) EMIT2(E_diaeresis) // NOLINT(whitespace/cast)
+ EMIT2(0x112) EMIT2(0x114) EMIT2(0x116)
+ EMIT2(0x118) EMIT2(0x11a) EMIT2(0x204)
+ EMIT2(0x206) EMIT2(0x228) EMIT2(0x246)
+ EMIT2(0x1e14) EMIT2(0x1e16) EMIT2(0x1e18)
+ EMIT2(0x1e1a) EMIT2(0x1e1c) EMIT2(0x1eb8)
+ EMIT2(0x1eba) EMIT2(0x1ebc) EMIT2(0x1ebe)
+ EMIT2(0x1ec0) EMIT2(0x1ec2) EMIT2(0x1ec4)
+ EMIT2(0x1ec6)
return;
- case 'D': CASEMBC(0x10e) CASEMBC(0x110) CASEMBC(0x1e0a)
- CASEMBC(0x1e0e) CASEMBC(0x1e10)
- EMIT2('D'); EMITMBC(0x10e) EMITMBC(0x110) EMITMBC(0x1e0a)
- EMITMBC(0x1e0e) EMITMBC(0x1e10)
+ case 'F':
+ case 0x191:
+ case 0x1e1e:
+ case 0xa798:
+ EMIT2('F') EMIT2(0x191) EMIT2(0x1e1e) EMIT2(0xa798)
return;
- case 'E': case E_grave: case E_acute: case E_circumflex:
- case E_diaeresis: CASEMBC(0x112) CASEMBC(0x114)
- CASEMBC(0x116) CASEMBC(0x118) CASEMBC(0x11a)
- CASEMBC(0x1eba) CASEMBC(0x1ebc)
- EMIT2('E'); EMIT2(E_grave); EMIT2(E_acute);
- EMIT2(E_circumflex); EMIT2(E_diaeresis);
- EMITMBC(0x112) EMITMBC(0x114) EMITMBC(0x116)
- EMITMBC(0x118) EMITMBC(0x11a) EMITMBC(0x1eba)
- EMITMBC(0x1ebc)
+ case 'G':
+ case 0x11c:
+ case 0x11e:
+ case 0x120:
+ case 0x122:
+ case 0x193:
+ case 0x1e4:
+ case 0x1e6:
+ case 0x1f4:
+ case 0x1e20:
+ case 0xa7a0:
+ EMIT2('G') EMIT2(0x11c) EMIT2(0x11e) EMIT2(0x120)
+ EMIT2(0x122) EMIT2(0x193) EMIT2(0x1e4)
+ EMIT2(0x1e6) EMIT2(0x1f4) EMIT2(0x1e20)
+ EMIT2(0xa7a0)
return;
- case 'F': CASEMBC(0x1e1e)
- EMIT2('F'); EMITMBC(0x1e1e)
+ case 'H':
+ case 0x124:
+ case 0x126:
+ case 0x21e:
+ case 0x1e22:
+ case 0x1e24:
+ case 0x1e26:
+ case 0x1e28:
+ case 0x1e2a:
+ case 0x2c67:
+ EMIT2('H') EMIT2(0x124) EMIT2(0x126) EMIT2(0x21e)
+ EMIT2(0x1e22) EMIT2(0x1e24) EMIT2(0x1e26)
+ EMIT2(0x1e28) EMIT2(0x1e2a) EMIT2(0x2c67)
return;
- case 'G': CASEMBC(0x11c) CASEMBC(0x11e) CASEMBC(0x120)
- CASEMBC(0x122) CASEMBC(0x1e4) CASEMBC(0x1e6)
- CASEMBC(0x1f4) CASEMBC(0x1e20)
- EMIT2('G'); EMITMBC(0x11c) EMITMBC(0x11e) EMITMBC(0x120)
- EMITMBC(0x122) EMITMBC(0x1e4) EMITMBC(0x1e6)
- EMITMBC(0x1f4) EMITMBC(0x1e20)
+ case 'I':
+ case I_grave:
+ case I_acute:
+ case I_circumflex:
+ case I_diaeresis:
+ case 0x128:
+ case 0x12a:
+ case 0x12c:
+ case 0x12e:
+ case 0x130:
+ case 0x197:
+ case 0x1cf:
+ case 0x208:
+ case 0x20a:
+ case 0x1e2c:
+ case 0x1e2e:
+ case 0x1ec8:
+ case 0x1eca:
+ EMIT2('I') EMIT2(I_grave) EMIT2(I_acute) // NOLINT(whitespace/cast)
+ EMIT2(I_circumflex) EMIT2(I_diaeresis) // NOLINT(whitespace/cast)
+ EMIT2(0x128) EMIT2(0x12a) EMIT2(0x12c)
+ EMIT2(0x12e) EMIT2(0x130) EMIT2(0x197)
+ EMIT2(0x1cf) EMIT2(0x208) EMIT2(0x20a)
+ EMIT2(0x1e2c) EMIT2(0x1e2e) EMIT2(0x1ec8)
+ EMIT2(0x1eca)
return;
- case 'H': CASEMBC(0x124) CASEMBC(0x126) CASEMBC(0x1e22)
- CASEMBC(0x1e26) CASEMBC(0x1e28)
- EMIT2('H'); EMITMBC(0x124) EMITMBC(0x126) EMITMBC(0x1e22)
- EMITMBC(0x1e26) EMITMBC(0x1e28)
+ case 'J':
+ case 0x134:
+ case 0x248:
+ EMIT2('J') EMIT2(0x134) EMIT2(0x248)
return;
- case 'I': case I_grave: case I_acute: case I_circumflex:
- case I_diaeresis: CASEMBC(0x128) CASEMBC(0x12a)
- CASEMBC(0x12c) CASEMBC(0x12e) CASEMBC(0x130)
- CASEMBC(0x1cf) CASEMBC(0x1ec8)
- EMIT2('I'); EMIT2(I_grave); EMIT2(I_acute);
- EMIT2(I_circumflex); EMIT2(I_diaeresis);
- EMITMBC(0x128) EMITMBC(0x12a)
- EMITMBC(0x12c) EMITMBC(0x12e) EMITMBC(0x130)
- EMITMBC(0x1cf) EMITMBC(0x1ec8)
+ case 'K':
+ case 0x136:
+ case 0x198:
+ case 0x1e8:
+ case 0x1e30:
+ case 0x1e32:
+ case 0x1e34:
+ case 0x2c69:
+ case 0xa740:
+ EMIT2('K') EMIT2(0x136) EMIT2(0x198) EMIT2(0x1e8)
+ EMIT2(0x1e30) EMIT2(0x1e32) EMIT2(0x1e34)
+ EMIT2(0x2c69) EMIT2(0xa740)
return;
- case 'J': CASEMBC(0x134)
- EMIT2('J'); EMITMBC(0x134)
+ case 'L':
+ case 0x139:
+ case 0x13b:
+ case 0x13d:
+ case 0x13f:
+ case 0x141:
+ case 0x23d:
+ case 0x1e36:
+ case 0x1e38:
+ case 0x1e3a:
+ case 0x1e3c:
+ case 0x2c60:
+ EMIT2('L') EMIT2(0x139) EMIT2(0x13b)
+ EMIT2(0x13d) EMIT2(0x13f) EMIT2(0x141)
+ EMIT2(0x23d) EMIT2(0x1e36) EMIT2(0x1e38)
+ EMIT2(0x1e3a) EMIT2(0x1e3c) EMIT2(0x2c60)
return;
- case 'K': CASEMBC(0x136) CASEMBC(0x1e8) CASEMBC(0x1e30)
- CASEMBC(0x1e34)
- EMIT2('K'); EMITMBC(0x136) EMITMBC(0x1e8) EMITMBC(0x1e30)
- EMITMBC(0x1e34)
+ case 'M':
+ case 0x1e3e:
+ case 0x1e40:
+ case 0x1e42:
+ EMIT2('M') EMIT2(0x1e3e) EMIT2(0x1e40)
+ EMIT2(0x1e42)
return;
- case 'L': CASEMBC(0x139) CASEMBC(0x13b) CASEMBC(0x13d)
- CASEMBC(0x13f) CASEMBC(0x141) CASEMBC(0x1e3a)
- EMIT2('L'); EMITMBC(0x139) EMITMBC(0x13b) EMITMBC(0x13d)
- EMITMBC(0x13f) EMITMBC(0x141) EMITMBC(0x1e3a)
+ case 'N':
+ case N_virguilla:
+ case 0x143:
+ case 0x145:
+ case 0x147:
+ case 0x1f8:
+ case 0x1e44:
+ case 0x1e46:
+ case 0x1e48:
+ case 0x1e4a:
+ case 0xa7a4:
+ EMIT2('N') EMIT2(N_virguilla)
+ EMIT2(0x143) EMIT2(0x145) EMIT2(0x147)
+ EMIT2(0x1f8) EMIT2(0x1e44) EMIT2(0x1e46)
+ EMIT2(0x1e48) EMIT2(0x1e4a) EMIT2(0xa7a4)
return;
- case 'M': CASEMBC(0x1e3e) CASEMBC(0x1e40)
- EMIT2('M'); EMITMBC(0x1e3e) EMITMBC(0x1e40)
+ case 'O':
+ case O_grave:
+ case O_acute:
+ case O_circumflex:
+ case O_virguilla:
+ case O_diaeresis:
+ case O_slash:
+ case 0x14c:
+ case 0x14e:
+ case 0x150:
+ case 0x19f:
+ case 0x1a0:
+ case 0x1d1:
+ case 0x1ea:
+ case 0x1ec:
+ case 0x1fe:
+ case 0x20c:
+ case 0x20e:
+ case 0x22a:
+ case 0x22c:
+ case 0x22e:
+ case 0x230:
+ case 0x1e4c:
+ case 0x1e4e:
+ case 0x1e50:
+ case 0x1e52:
+ case 0x1ecc:
+ case 0x1ece:
+ case 0x1ed0:
+ case 0x1ed2:
+ case 0x1ed4:
+ case 0x1ed6:
+ case 0x1ed8:
+ case 0x1eda:
+ case 0x1edc:
+ case 0x1ede:
+ case 0x1ee0:
+ case 0x1ee2:
+ EMIT2('O') EMIT2(O_grave) EMIT2(O_acute) // NOLINT(whitespace/cast)
+ EMIT2(O_circumflex) EMIT2(O_virguilla) // NOLINT(whitespace/cast)
+ EMIT2(O_diaeresis) EMIT2(O_slash) // NOLINT(whitespace/cast)
+ EMIT2(0x14c) EMIT2(0x14e) EMIT2(0x150)
+ EMIT2(0x19f) EMIT2(0x1a0) EMIT2(0x1d1)
+ EMIT2(0x1ea) EMIT2(0x1ec) EMIT2(0x1fe)
+ EMIT2(0x20c) EMIT2(0x20e) EMIT2(0x22a)
+ EMIT2(0x22c) EMIT2(0x22e) EMIT2(0x230)
+ EMIT2(0x1e4c) EMIT2(0x1e4e) EMIT2(0x1e50)
+ EMIT2(0x1e52) EMIT2(0x1ecc) EMIT2(0x1ece)
+ EMIT2(0x1ed0) EMIT2(0x1ed2) EMIT2(0x1ed4)
+ EMIT2(0x1ed6) EMIT2(0x1ed8) EMIT2(0x1eda)
+ EMIT2(0x1edc) EMIT2(0x1ede) EMIT2(0x1ee0)
+ EMIT2(0x1ee2)
return;
- case 'N': case N_virguilla: CASEMBC(0x143) CASEMBC(0x145)
- CASEMBC(0x147) CASEMBC(0x1e44) CASEMBC(0x1e48)
- EMIT2('N'); EMIT2(N_virguilla);
- EMITMBC(0x143) EMITMBC(0x145)
- EMITMBC(0x147) EMITMBC(0x1e44) EMITMBC(0x1e48)
+ case 'P':
+ case 0x1a4:
+ case 0x1e54:
+ case 0x1e56:
+ case 0x2c63:
+ EMIT2('P') EMIT2(0x1a4) EMIT2(0x1e54) EMIT2(0x1e56)
+ EMIT2(0x2c63)
return;
- case 'O': case O_grave: case O_acute: case O_circumflex:
- case O_virguilla: case O_diaeresis: case O_slash:
- CASEMBC(0x14c) CASEMBC(0x14e) CASEMBC(0x150)
- CASEMBC(0x1a0) CASEMBC(0x1d1) CASEMBC(0x1ea)
- CASEMBC(0x1ec) CASEMBC(0x1ece)
- EMIT2('O'); EMIT2(O_grave); EMIT2(O_acute);
- EMIT2(O_circumflex); EMIT2(O_virguilla);
- EMIT2(O_diaeresis); EMIT2(O_slash);
- EMITMBC(0x14c) EMITMBC(0x14e) EMITMBC(0x150)
- EMITMBC(0x1a0) EMITMBC(0x1d1) EMITMBC(0x1ea)
- EMITMBC(0x1ec) EMITMBC(0x1ece)
+ case 'Q':
+ case 0x24a:
+ EMIT2('Q') EMIT2(0x24a)
return;
- case 'P': case 0x1e54: case 0x1e56:
- EMIT2('P'); EMITMBC(0x1e54) EMITMBC(0x1e56)
+ case 'R':
+ case 0x154:
+ case 0x156:
+ case 0x158:
+ case 0x210:
+ case 0x212:
+ case 0x24c:
+ case 0x1e58:
+ case 0x1e5a:
+ case 0x1e5c:
+ case 0x1e5e:
+ case 0x2c64:
+ case 0xa7a6:
+ EMIT2('R') EMIT2(0x154) EMIT2(0x156) EMIT2(0x158)
+ EMIT2(0x210) EMIT2(0x212) EMIT2(0x24c) EMIT2(0x1e58)
+ EMIT2(0x1e5a) EMIT2(0x1e5c) EMIT2(0x1e5e) EMIT2(0x2c64)
+ EMIT2(0xa7a6)
return;
- case 'R': CASEMBC(0x154) CASEMBC(0x156) CASEMBC(0x158)
- CASEMBC(0x1e58) CASEMBC(0x1e5e)
- EMIT2('R'); EMITMBC(0x154) EMITMBC(0x156) EMITMBC(0x158)
- EMITMBC(0x1e58) EMITMBC(0x1e5e)
+ case 'S':
+ case 0x15a:
+ case 0x15c:
+ case 0x15e:
+ case 0x160:
+ case 0x218:
+ case 0x1e60:
+ case 0x1e62:
+ case 0x1e64:
+ case 0x1e66:
+ case 0x1e68:
+ case 0x2c7e:
+ case 0xa7a8:
+ EMIT2('S') EMIT2(0x15a) EMIT2(0x15c) EMIT2(0x15e)
+ EMIT2(0x160) EMIT2(0x218) EMIT2(0x1e60) EMIT2(0x1e62)
+ EMIT2(0x1e64) EMIT2(0x1e66) EMIT2(0x1e68) EMIT2(0x2c7e)
+ EMIT2(0xa7a8)
return;
- case 'S': CASEMBC(0x15a) CASEMBC(0x15c) CASEMBC(0x15e)
- CASEMBC(0x160) CASEMBC(0x1e60)
- EMIT2('S'); EMITMBC(0x15a) EMITMBC(0x15c) EMITMBC(0x15e)
- EMITMBC(0x160) EMITMBC(0x1e60)
+ case 'T':
+ case 0x162:
+ case 0x164:
+ case 0x166:
+ case 0x1ac:
+ case 0x1ae:
+ case 0x21a:
+ case 0x23e:
+ case 0x1e6a:
+ case 0x1e6c:
+ case 0x1e6e:
+ case 0x1e70:
+ EMIT2('T') EMIT2(0x162) EMIT2(0x164) EMIT2(0x166)
+ EMIT2(0x1ac) EMIT2(0x1ae) EMIT2(0x23e) EMIT2(0x21a)
+ EMIT2(0x1e6a) EMIT2(0x1e6c) EMIT2(0x1e6e) EMIT2(0x1e70)
return;
- case 'T': CASEMBC(0x162) CASEMBC(0x164) CASEMBC(0x166)
- CASEMBC(0x1e6a) CASEMBC(0x1e6e)
- EMIT2('T'); EMITMBC(0x162) EMITMBC(0x164) EMITMBC(0x166)
- EMITMBC(0x1e6a) EMITMBC(0x1e6e)
+ case 'U':
+ case U_grave:
+ case U_acute:
+ case U_diaeresis:
+ case U_circumflex:
+ case 0x168:
+ case 0x16a:
+ case 0x16c:
+ case 0x16e:
+ case 0x170:
+ case 0x172:
+ case 0x1af:
+ case 0x1d3:
+ case 0x1d5:
+ case 0x1d7:
+ case 0x1d9:
+ case 0x1db:
+ case 0x214:
+ case 0x216:
+ case 0x244:
+ case 0x1e72:
+ case 0x1e74:
+ case 0x1e76:
+ case 0x1e78:
+ case 0x1e7a:
+ case 0x1ee4:
+ case 0x1ee6:
+ case 0x1ee8:
+ case 0x1eea:
+ case 0x1eec:
+ case 0x1eee:
+ case 0x1ef0:
+ EMIT2('U') EMIT2(U_grave) EMIT2(U_acute) // NOLINT(whitespace/cast)
+ EMIT2(U_diaeresis) EMIT2(U_circumflex) // NOLINT(whitespace/cast)
+ EMIT2(0x168) EMIT2(0x16a)
+ EMIT2(0x16c) EMIT2(0x16e) EMIT2(0x170)
+ EMIT2(0x172) EMIT2(0x1af) EMIT2(0x1d3)
+ EMIT2(0x1d5) EMIT2(0x1d7) EMIT2(0x1d9)
+ EMIT2(0x1db) EMIT2(0x214) EMIT2(0x216)
+ EMIT2(0x244) EMIT2(0x1e72) EMIT2(0x1e74)
+ EMIT2(0x1e76) EMIT2(0x1e78) EMIT2(0x1e7a)
+ EMIT2(0x1ee4) EMIT2(0x1ee6) EMIT2(0x1ee8)
+ EMIT2(0x1eea) EMIT2(0x1eec) EMIT2(0x1eee)
+ EMIT2(0x1ef0)
return;
- case 'U': case U_grave: case U_acute: case U_diaeresis:
- case U_circumflex: CASEMBC(0x168) CASEMBC(0x16a)
- CASEMBC(0x16c) CASEMBC(0x16e) CASEMBC(0x170)
- CASEMBC(0x172) CASEMBC(0x1af) CASEMBC(0x1d3)
- CASEMBC(0x1ee6)
- EMIT2('U'); EMIT2(U_grave); EMIT2(U_acute);
- EMIT2(U_diaeresis); EMIT2(U_circumflex);
- EMITMBC(0x168) EMITMBC(0x16a)
- EMITMBC(0x16c) EMITMBC(0x16e) EMITMBC(0x170)
- EMITMBC(0x172) EMITMBC(0x1af) EMITMBC(0x1d3)
- EMITMBC(0x1ee6)
+ case 'V':
+ case 0x1b2:
+ case 0x1e7c:
+ case 0x1e7e:
+ EMIT2('V') EMIT2(0x1b2) EMIT2(0x1e7c) EMIT2(0x1e7e)
return;
- case 'V': CASEMBC(0x1e7c)
- EMIT2('V'); EMITMBC(0x1e7c)
+ case 'W':
+ case 0x174:
+ case 0x1e80:
+ case 0x1e82:
+ case 0x1e84:
+ case 0x1e86:
+ case 0x1e88:
+ EMIT2('W') EMIT2(0x174) EMIT2(0x1e80) EMIT2(0x1e82)
+ EMIT2(0x1e84) EMIT2(0x1e86) EMIT2(0x1e88)
return;
- case 'W': CASEMBC(0x174) CASEMBC(0x1e80) CASEMBC(0x1e82)
- CASEMBC(0x1e84) CASEMBC(0x1e86)
- EMIT2('W'); EMITMBC(0x174) EMITMBC(0x1e80) EMITMBC(0x1e82)
- EMITMBC(0x1e84) EMITMBC(0x1e86)
+ case 'X':
+ case 0x1e8a:
+ case 0x1e8c:
+ EMIT2('X') EMIT2(0x1e8a) EMIT2(0x1e8c)
return;
- case 'X': CASEMBC(0x1e8a) CASEMBC(0x1e8c)
- EMIT2('X'); EMITMBC(0x1e8a) EMITMBC(0x1e8c)
+ case 'Y':
+ case Y_acute:
+ case 0x176:
+ case 0x178:
+ case 0x1b3:
+ case 0x232:
+ case 0x24e:
+ case 0x1e8e:
+ case 0x1ef2:
+ case 0x1ef4:
+ case 0x1ef6:
+ case 0x1ef8:
+ EMIT2('Y') EMIT2(Y_acute)
+ EMIT2(0x176) EMIT2(0x178) EMIT2(0x1b3)
+ EMIT2(0x232) EMIT2(0x24e) EMIT2(0x1e8e)
+ EMIT2(0x1ef2) EMIT2(0x1ef4) EMIT2(0x1ef6)
+ EMIT2(0x1ef8)
return;
- case 'Y': case Y_acute: CASEMBC(0x176) CASEMBC(0x178)
- CASEMBC(0x1e8e) CASEMBC(0x1ef2) CASEMBC(0x1ef6)
- CASEMBC(0x1ef8)
- EMIT2('Y'); EMIT2(Y_acute);
- EMITMBC(0x176) EMITMBC(0x178)
- EMITMBC(0x1e8e) EMITMBC(0x1ef2) EMITMBC(0x1ef6)
- EMITMBC(0x1ef8)
+ case 'Z':
+ case 0x179:
+ case 0x17b:
+ case 0x17d:
+ case 0x1b5:
+ case 0x1e90:
+ case 0x1e92:
+ case 0x1e94:
+ case 0x2c6b:
+ EMIT2('Z') EMIT2(0x179) EMIT2(0x17b) EMIT2(0x17d)
+ EMIT2(0x1b5) EMIT2(0x1e90) EMIT2(0x1e92)
+ EMIT2(0x1e94) EMIT2(0x2c6b)
return;
- case 'Z': CASEMBC(0x179) CASEMBC(0x17b) CASEMBC(0x17d)
- CASEMBC(0x1b5) CASEMBC(0x1e90) CASEMBC(0x1e94)
- EMIT2('Z'); EMITMBC(0x179) EMITMBC(0x17b) EMITMBC(0x17d)
- EMITMBC(0x1b5) EMITMBC(0x1e90) EMITMBC(0x1e94)
+ case 'a':
+ case a_grave:
+ case a_acute:
+ case a_circumflex:
+ case a_virguilla:
+ case a_diaeresis:
+ case a_ring:
+ case 0x101:
+ case 0x103:
+ case 0x105:
+ case 0x1ce:
+ case 0x1df:
+ case 0x1e1:
+ case 0x1fb:
+ case 0x201:
+ case 0x203:
+ case 0x227:
+ case 0x1d8f:
+ case 0x1e01:
+ case 0x1e9a:
+ case 0x1ea1:
+ case 0x1ea3:
+ case 0x1ea5:
+ case 0x1ea7:
+ case 0x1ea9:
+ case 0x1eab:
+ case 0x1ead:
+ case 0x1eaf:
+ case 0x1eb1:
+ case 0x1eb3:
+ case 0x1eb5:
+ case 0x1eb7:
+ case 0x2c65:
+ EMIT2('a') EMIT2(a_grave) EMIT2(a_acute) // NOLINT(whitespace/cast)
+ EMIT2(a_circumflex) EMIT2(a_virguilla) // NOLINT(whitespace/cast)
+ EMIT2(a_diaeresis) EMIT2(a_ring) // NOLINT(whitespace/cast)
+ EMIT2(0x101) EMIT2(0x103) EMIT2(0x105)
+ EMIT2(0x1ce) EMIT2(0x1df) EMIT2(0x1e1)
+ EMIT2(0x1fb) EMIT2(0x201) EMIT2(0x203)
+ EMIT2(0x227) EMIT2(0x1d8f) EMIT2(0x1e01)
+ EMIT2(0x1e9a) EMIT2(0x1ea1) EMIT2(0x1ea3)
+ EMIT2(0x1ea5) EMIT2(0x1ea7) EMIT2(0x1ea9)
+ EMIT2(0x1eab) EMIT2(0x1ead) EMIT2(0x1eaf)
+ EMIT2(0x1eb1) EMIT2(0x1eb3) EMIT2(0x1eb5)
+ EMIT2(0x1eb7) EMIT2(0x2c65)
return;
- case 'a': case a_grave: case a_acute: case a_circumflex:
- case a_virguilla: case a_diaeresis: case a_ring:
- CASEMBC(0x101) CASEMBC(0x103) CASEMBC(0x105)
- CASEMBC(0x1ce) CASEMBC(0x1df) CASEMBC(0x1e1)
- CASEMBC(0x1ea3)
- EMIT2('a'); EMIT2(a_grave); EMIT2(a_acute);
- EMIT2(a_circumflex); EMIT2(a_virguilla);
- EMIT2(a_diaeresis); EMIT2(a_ring);
- EMITMBC(0x101) EMITMBC(0x103) EMITMBC(0x105)
- EMITMBC(0x1ce) EMITMBC(0x1df) EMITMBC(0x1e1)
- EMITMBC(0x1ea3)
+ case 'b':
+ case 0x180:
+ case 0x253:
+ case 0x1d6c:
+ case 0x1d80:
+ case 0x1e03:
+ case 0x1e05:
+ case 0x1e07:
+ EMIT2('b') EMIT2(0x180) EMIT2(0x253) EMIT2(0x1d6c)
+ EMIT2(0x1d80) EMIT2(0x1e03) EMIT2(0x1e05) EMIT2(0x1e07)
return;
- case 'b': CASEMBC(0x1e03) CASEMBC(0x1e07)
- EMIT2('b'); EMITMBC(0x1e03) EMITMBC(0x1e07)
+ case 'c':
+ case c_cedilla:
+ case 0x107:
+ case 0x109:
+ case 0x10b:
+ case 0x10d:
+ case 0x188:
+ case 0x23c:
+ case 0x1e09:
+ case 0xa793:
+ case 0xa794:
+ EMIT2('c') EMIT2(c_cedilla)
+ EMIT2(0x107) EMIT2(0x109) EMIT2(0x10b)
+ EMIT2(0x10d) EMIT2(0x188) EMIT2(0x23c)
+ EMIT2(0x1e09) EMIT2(0xa793) EMIT2(0xa794)
return;
- case 'c': case c_cedilla: CASEMBC(0x107) CASEMBC(0x109)
- CASEMBC(0x10b) CASEMBC(0x10d)
- EMIT2('c'); EMIT2(c_cedilla);
- EMITMBC(0x107) EMITMBC(0x109)
- EMITMBC(0x10b) EMITMBC(0x10d)
+ case 'd':
+ case 0x10f:
+ case 0x111:
+ case 0x257:
+ case 0x1d6d:
+ case 0x1d81:
+ case 0x1d91:
+ case 0x1e0b:
+ case 0x1e0d:
+ case 0x1e0f:
+ case 0x1e11:
+ case 0x1e13:
+ EMIT2('d') EMIT2(0x10f) EMIT2(0x111)
+ EMIT2(0x257) EMIT2(0x1d6d) EMIT2(0x1d81)
+ EMIT2(0x1d91) EMIT2(0x1e0b) EMIT2(0x1e0d)
+ EMIT2(0x1e0f) EMIT2(0x1e11) EMIT2(0x1e13)
return;
- case 'd': CASEMBC(0x10f) CASEMBC(0x111) CASEMBC(0x1e0b)
- CASEMBC(0x1e0f) CASEMBC(0x1e11)
- EMIT2('d'); EMITMBC(0x10f) EMITMBC(0x111) EMITMBC(0x1e0b)
- EMITMBC(0x1e0f) EMITMBC(0x1e11)
+ case 'e':
+ case e_grave:
+ case e_acute:
+ case e_circumflex:
+ case e_diaeresis:
+ case 0x113:
+ case 0x115:
+ case 0x117:
+ case 0x119:
+ case 0x11b:
+ case 0x205:
+ case 0x207:
+ case 0x229:
+ case 0x247:
+ case 0x1d92:
+ case 0x1e15:
+ case 0x1e17:
+ case 0x1e19:
+ case 0x1e1b:
+ case 0x1e1d:
+ case 0x1eb9:
+ case 0x1ebb:
+ case 0x1ebd:
+ case 0x1ebf:
+ case 0x1ec1:
+ case 0x1ec3:
+ case 0x1ec5:
+ case 0x1ec7:
+ EMIT2('e') EMIT2(e_grave) EMIT2(e_acute) // NOLINT(whitespace/cast)
+ EMIT2(e_circumflex) EMIT2(e_diaeresis) // NOLINT(whitespace/cast)
+ EMIT2(0x113) EMIT2(0x115)
+ EMIT2(0x117) EMIT2(0x119) EMIT2(0x11b)
+ EMIT2(0x205) EMIT2(0x207) EMIT2(0x229)
+ EMIT2(0x247) EMIT2(0x1d92) EMIT2(0x1e15)
+ EMIT2(0x1e17) EMIT2(0x1e19) EMIT2(0x1e1b)
+ EMIT2(0x1e1d) EMIT2(0x1eb9) EMIT2(0x1ebb)
+ EMIT2(0x1ebd) EMIT2(0x1ebf) EMIT2(0x1ec1)
+ EMIT2(0x1ec3) EMIT2(0x1ec5) EMIT2(0x1ec7)
return;
- case 'e': case e_grave: case e_acute: case e_circumflex:
- case e_diaeresis: CASEMBC(0x113) CASEMBC(0x115)
- CASEMBC(0x117) CASEMBC(0x119) CASEMBC(0x11b)
- CASEMBC(0x1ebb) CASEMBC(0x1ebd)
- EMIT2('e'); EMIT2(e_grave); EMIT2(e_acute);
- EMIT2(e_circumflex); EMIT2(e_diaeresis);
- EMITMBC(0x113) EMITMBC(0x115)
- EMITMBC(0x117) EMITMBC(0x119) EMITMBC(0x11b)
- EMITMBC(0x1ebb) EMITMBC(0x1ebd)
+ case 'f':
+ case 0x192:
+ case 0x1d6e:
+ case 0x1d82:
+ case 0x1e1f:
+ case 0xa799:
+ EMIT2('f') EMIT2(0x192) EMIT2(0x1d6e) EMIT2(0x1d82)
+ EMIT2(0x1e1f) EMIT2(0xa799)
return;
- case 'f': CASEMBC(0x1e1f)
- EMIT2('f'); EMITMBC(0x1e1f)
+ case 'g':
+ case 0x11d:
+ case 0x11f:
+ case 0x121:
+ case 0x123:
+ case 0x1e5:
+ case 0x1e7:
+ case 0x1f5:
+ case 0x260:
+ case 0x1d83:
+ case 0x1e21:
+ case 0xa7a1:
+ EMIT2('g') EMIT2(0x11d) EMIT2(0x11f) EMIT2(0x121)
+ EMIT2(0x123) EMIT2(0x1e5) EMIT2(0x1e7)
+ EMIT2(0x1f5) EMIT2(0x260) EMIT2(0x1d83)
+ EMIT2(0x1e21) EMIT2(0xa7a1)
return;
- case 'g': CASEMBC(0x11d) CASEMBC(0x11f) CASEMBC(0x121)
- CASEMBC(0x123) CASEMBC(0x1e5) CASEMBC(0x1e7)
- CASEMBC(0x1f5) CASEMBC(0x1e21)
- EMIT2('g'); EMITMBC(0x11d) EMITMBC(0x11f) EMITMBC(0x121)
- EMITMBC(0x123) EMITMBC(0x1e5) EMITMBC(0x1e7)
- EMITMBC(0x1f5) EMITMBC(0x1e21)
+ case 'h':
+ case 0x125:
+ case 0x127:
+ case 0x21f:
+ case 0x1e23:
+ case 0x1e25:
+ case 0x1e27:
+ case 0x1e29:
+ case 0x1e2b:
+ case 0x1e96:
+ case 0x2c68:
+ case 0xa795:
+ EMIT2('h') EMIT2(0x125) EMIT2(0x127) EMIT2(0x21f)
+ EMIT2(0x1e23) EMIT2(0x1e25) EMIT2(0x1e27)
+ EMIT2(0x1e29) EMIT2(0x1e2b) EMIT2(0x1e96)
+ EMIT2(0x2c68) EMIT2(0xa795)
return;
- case 'h': CASEMBC(0x125) CASEMBC(0x127) CASEMBC(0x1e23)
- CASEMBC(0x1e27) CASEMBC(0x1e29) CASEMBC(0x1e96)
- EMIT2('h'); EMITMBC(0x125) EMITMBC(0x127) EMITMBC(0x1e23)
- EMITMBC(0x1e27) EMITMBC(0x1e29) EMITMBC(0x1e96)
+ case 'i':
+ case i_grave:
+ case i_acute:
+ case i_circumflex:
+ case i_diaeresis:
+ case 0x129:
+ case 0x12b:
+ case 0x12d:
+ case 0x12f:
+ case 0x1d0:
+ case 0x209:
+ case 0x20b:
+ case 0x268:
+ case 0x1d96:
+ case 0x1e2d:
+ case 0x1e2f:
+ case 0x1ec9:
+ case 0x1ecb:
+ EMIT2('i') EMIT2(i_grave) EMIT2(i_acute) // NOLINT(whitespace/cast)
+ EMIT2(i_circumflex) EMIT2(i_diaeresis) // NOLINT(whitespace/cast)
+ EMIT2(0x129) EMIT2(0x12b) EMIT2(0x12d)
+ EMIT2(0x12f) EMIT2(0x1d0) EMIT2(0x209)
+ EMIT2(0x20b) EMIT2(0x268) EMIT2(0x1d96)
+ EMIT2(0x1e2d) EMIT2(0x1e2f) EMIT2(0x1ec9)
+ EMIT2(0x1ecb) EMIT2(0x1ecb)
return;
- case 'i': case i_grave: case i_acute: case i_circumflex:
- case i_diaeresis: CASEMBC(0x129) CASEMBC(0x12b)
- CASEMBC(0x12d) CASEMBC(0x12f) CASEMBC(0x1d0)
- CASEMBC(0x1ec9)
- EMIT2('i'); EMIT2(i_grave); EMIT2(i_acute);
- EMIT2(i_circumflex); EMIT2(i_diaeresis);
- EMITMBC(0x129) EMITMBC(0x12b)
- EMITMBC(0x12d) EMITMBC(0x12f) EMITMBC(0x1d0)
- EMITMBC(0x1ec9)
+ case 'j':
+ case 0x135:
+ case 0x1f0:
+ case 0x249:
+ EMIT2('j') EMIT2(0x135) EMIT2(0x1f0) EMIT2(0x249)
return;
- case 'j': CASEMBC(0x135) CASEMBC(0x1f0)
- EMIT2('j'); EMITMBC(0x135) EMITMBC(0x1f0)
+ case 'k':
+ case 0x137:
+ case 0x199:
+ case 0x1e9:
+ case 0x1d84:
+ case 0x1e31:
+ case 0x1e33:
+ case 0x1e35:
+ case 0x2c6a:
+ case 0xa741:
+ EMIT2('k') EMIT2(0x137) EMIT2(0x199) EMIT2(0x1e9)
+ EMIT2(0x1d84) EMIT2(0x1e31) EMIT2(0x1e33)
+ EMIT2(0x1e35) EMIT2(0x2c6a) EMIT2(0xa741)
return;
- case 'k': CASEMBC(0x137) CASEMBC(0x1e9) CASEMBC(0x1e31)
- CASEMBC(0x1e35)
- EMIT2('k'); EMITMBC(0x137) EMITMBC(0x1e9) EMITMBC(0x1e31)
- EMITMBC(0x1e35)
+ case 'l':
+ case 0x13a:
+ case 0x13c:
+ case 0x13e:
+ case 0x140:
+ case 0x142:
+ case 0x19a:
+ case 0x1e37:
+ case 0x1e39:
+ case 0x1e3b:
+ case 0x1e3d:
+ case 0x2c61:
+ EMIT2('l') EMIT2(0x13a) EMIT2(0x13c)
+ EMIT2(0x13e) EMIT2(0x140) EMIT2(0x142)
+ EMIT2(0x19a) EMIT2(0x1e37) EMIT2(0x1e39)
+ EMIT2(0x1e3b) EMIT2(0x1e3d) EMIT2(0x2c61)
return;
- case 'l': CASEMBC(0x13a) CASEMBC(0x13c) CASEMBC(0x13e)
- CASEMBC(0x140) CASEMBC(0x142) CASEMBC(0x1e3b)
- EMIT2('l'); EMITMBC(0x13a) EMITMBC(0x13c) EMITMBC(0x13e)
- EMITMBC(0x140) EMITMBC(0x142) EMITMBC(0x1e3b)
+ case 'm':
+ case 0x1d6f:
+ case 0x1e3f:
+ case 0x1e41:
+ case 0x1e43:
+ EMIT2('m') EMIT2(0x1d6f) EMIT2(0x1e3f)
+ EMIT2(0x1e41) EMIT2(0x1e43)
return;
- case 'm': CASEMBC(0x1e3f) CASEMBC(0x1e41)
- EMIT2('m'); EMITMBC(0x1e3f) EMITMBC(0x1e41)
+ case 'n':
+ case n_virguilla:
+ case 0x144:
+ case 0x146:
+ case 0x148:
+ case 0x149:
+ case 0x1f9:
+ case 0x1d70:
+ case 0x1d87:
+ case 0x1e45:
+ case 0x1e47:
+ case 0x1e49:
+ case 0x1e4b:
+ case 0xa7a5:
+ EMIT2('n') EMIT2(n_virguilla)
+ EMIT2(0x144) EMIT2(0x146) EMIT2(0x148)
+ EMIT2(0x149) EMIT2(0x1f9) EMIT2(0x1d70)
+ EMIT2(0x1d87) EMIT2(0x1e45) EMIT2(0x1e47)
+ EMIT2(0x1e49) EMIT2(0x1e4b) EMIT2(0xa7a5)
return;
- case 'n': case n_virguilla: CASEMBC(0x144) CASEMBC(0x146)
- CASEMBC(0x148) CASEMBC(0x149) CASEMBC(0x1e45)
- CASEMBC(0x1e49)
- EMIT2('n'); EMIT2(n_virguilla);
- EMITMBC(0x144) EMITMBC(0x146)
- EMITMBC(0x148) EMITMBC(0x149) EMITMBC(0x1e45)
- EMITMBC(0x1e49)
+ case 'o':
+ case o_grave:
+ case o_acute:
+ case o_circumflex:
+ case o_virguilla:
+ case o_diaeresis:
+ case o_slash:
+ case 0x14d:
+ case 0x14f:
+ case 0x151:
+ case 0x1a1:
+ case 0x1d2:
+ case 0x1eb:
+ case 0x1ed:
+ case 0x1ff:
+ case 0x20d:
+ case 0x20f:
+ case 0x22b:
+ case 0x22d:
+ case 0x22f:
+ case 0x231:
+ case 0x275:
+ case 0x1e4d:
+ case 0x1e4f:
+ case 0x1e51:
+ case 0x1e53:
+ case 0x1ecd:
+ case 0x1ecf:
+ case 0x1ed1:
+ case 0x1ed3:
+ case 0x1ed5:
+ case 0x1ed7:
+ case 0x1ed9:
+ case 0x1edb:
+ case 0x1edd:
+ case 0x1edf:
+ case 0x1ee1:
+ case 0x1ee3:
+ EMIT2('o') EMIT2(o_grave) EMIT2(o_acute) // NOLINT(whitespace/cast)
+ EMIT2(o_circumflex) EMIT2(o_virguilla) // NOLINT(whitespace/cast)
+ EMIT2(o_diaeresis) EMIT2(o_slash) // NOLINT(whitespace/cast)
+ EMIT2(0x14d) EMIT2(0x14f) EMIT2(0x151)
+ EMIT2(0x1a1) EMIT2(0x1d2) EMIT2(0x1eb)
+ EMIT2(0x1ed) EMIT2(0x1ff) EMIT2(0x20d)
+ EMIT2(0x20f) EMIT2(0x22b) EMIT2(0x22d)
+ EMIT2(0x22f) EMIT2(0x231) EMIT2(0x275)
+ EMIT2(0x1e4d) EMIT2(0x1e4f) EMIT2(0x1e51)
+ EMIT2(0x1e53) EMIT2(0x1ecd) EMIT2(0x1ecf)
+ EMIT2(0x1ed1) EMIT2(0x1ed3) EMIT2(0x1ed5)
+ EMIT2(0x1ed7) EMIT2(0x1ed9) EMIT2(0x1edb)
+ EMIT2(0x1edd) EMIT2(0x1edf) EMIT2(0x1ee1)
+ EMIT2(0x1ee3)
return;
- case 'o': case o_grave: case o_acute: case o_circumflex:
- case o_virguilla: case o_diaeresis: case o_slash:
- CASEMBC(0x14d) CASEMBC(0x14f) CASEMBC(0x151)
- CASEMBC(0x1a1) CASEMBC(0x1d2) CASEMBC(0x1eb)
- CASEMBC(0x1ed) CASEMBC(0x1ecf)
- EMIT2('o'); EMIT2(o_grave); EMIT2(o_acute);
- EMIT2(o_circumflex); EMIT2(o_virguilla);
- EMIT2(o_diaeresis); EMIT2(o_slash);
- EMITMBC(0x14d) EMITMBC(0x14f) EMITMBC(0x151)
- EMITMBC(0x1a1) EMITMBC(0x1d2) EMITMBC(0x1eb)
- EMITMBC(0x1ed) EMITMBC(0x1ecf)
+ case 'p':
+ case 0x1a5:
+ case 0x1d71:
+ case 0x1d7d:
+ case 0x1d88:
+ case 0x1e55:
+ case 0x1e57:
+ EMIT2('p') EMIT2(0x1a5) EMIT2(0x1d71) EMIT2(0x1d7d)
+ EMIT2(0x1d88) EMIT2(0x1e55) EMIT2(0x1e57)
return;
- case 'p': CASEMBC(0x1e55) CASEMBC(0x1e57)
- EMIT2('p'); EMITMBC(0x1e55) EMITMBC(0x1e57)
+ case 'q':
+ case 0x24b:
+ case 0x2a0:
+ EMIT2('q') EMIT2(0x24b) EMIT2(0x2a0)
return;
- case 'r': CASEMBC(0x155) CASEMBC(0x157) CASEMBC(0x159)
- CASEMBC(0x1e59) CASEMBC(0x1e5f)
- EMIT2('r'); EMITMBC(0x155) EMITMBC(0x157) EMITMBC(0x159)
- EMITMBC(0x1e59) EMITMBC(0x1e5f)
+ case 'r':
+ case 0x155:
+ case 0x157:
+ case 0x159:
+ case 0x211:
+ case 0x213:
+ case 0x24d:
+ case 0x27d:
+ case 0x1d72:
+ case 0x1d73:
+ case 0x1d89:
+ case 0x1e59:
+ case 0x1e5b:
+ case 0x1e5d:
+ case 0x1e5f:
+ case 0xa7a7:
+ EMIT2('r') EMIT2(0x155) EMIT2(0x157) EMIT2(0x159)
+ EMIT2(0x211) EMIT2(0x213) EMIT2(0x24d) EMIT2(0x27d)
+ EMIT2(0x1d72) EMIT2(0x1d73) EMIT2(0x1d89) EMIT2(0x1e59)
+ EMIT2(0x1e5b) EMIT2(0x1e5d) EMIT2(0x1e5f) EMIT2(0xa7a7)
return;
- case 's': CASEMBC(0x15b) CASEMBC(0x15d) CASEMBC(0x15f)
- CASEMBC(0x161) CASEMBC(0x1e61)
- EMIT2('s'); EMITMBC(0x15b) EMITMBC(0x15d) EMITMBC(0x15f)
- EMITMBC(0x161) EMITMBC(0x1e61)
+ case 's':
+ case 0x15b:
+ case 0x15d:
+ case 0x15f:
+ case 0x161:
+ case 0x219:
+ case 0x23f:
+ case 0x1d74:
+ case 0x1d8a:
+ case 0x1e61:
+ case 0x1e63:
+ case 0x1e65:
+ case 0x1e67:
+ case 0x1e69:
+ case 0xa7a9:
+ EMIT2('s') EMIT2(0x15b) EMIT2(0x15d) EMIT2(0x15f)
+ EMIT2(0x161) EMIT2(0x219) EMIT2(0x23f) EMIT2(0x1d74)
+ EMIT2(0x1d8a) EMIT2(0x1e61) EMIT2(0x1e63) EMIT2(0x1e65)
+ EMIT2(0x1e67) EMIT2(0x1e69) EMIT2(0xa7a9)
return;
- case 't': CASEMBC(0x163) CASEMBC(0x165) CASEMBC(0x167)
- CASEMBC(0x1e6b) CASEMBC(0x1e6f) CASEMBC(0x1e97)
- EMIT2('t'); EMITMBC(0x163) EMITMBC(0x165) EMITMBC(0x167)
- EMITMBC(0x1e6b) EMITMBC(0x1e6f) EMITMBC(0x1e97)
+ case 't':
+ case 0x163:
+ case 0x165:
+ case 0x167:
+ case 0x1ab:
+ case 0x1ad:
+ case 0x21b:
+ case 0x288:
+ case 0x1d75:
+ case 0x1e6b:
+ case 0x1e6d:
+ case 0x1e6f:
+ case 0x1e71:
+ case 0x1e97:
+ case 0x2c66:
+ EMIT2('t') EMIT2(0x163) EMIT2(0x165) EMIT2(0x167)
+ EMIT2(0x1ab) EMIT2(0x1ad) EMIT2(0x21b) EMIT2(0x288)
+ EMIT2(0x1d75) EMIT2(0x1e6b) EMIT2(0x1e6d) EMIT2(0x1e6f)
+ EMIT2(0x1e71) EMIT2(0x1e97) EMIT2(0x2c66)
return;
- case 'u': case u_grave: case u_acute: case u_circumflex:
- case u_diaeresis: CASEMBC(0x169) CASEMBC(0x16b)
- CASEMBC(0x16d) CASEMBC(0x16f) CASEMBC(0x171)
- CASEMBC(0x173) CASEMBC(0x1b0) CASEMBC(0x1d4)
- CASEMBC(0x1ee7)
- EMIT2('u'); EMIT2(u_grave); EMIT2(u_acute);
- EMIT2(u_circumflex); EMIT2(u_diaeresis);
- EMITMBC(0x169) EMITMBC(0x16b)
- EMITMBC(0x16d) EMITMBC(0x16f) EMITMBC(0x171)
- EMITMBC(0x173) EMITMBC(0x1b0) EMITMBC(0x1d4)
- EMITMBC(0x1ee7)
+ case 'u':
+ case u_grave:
+ case u_acute:
+ case u_circumflex:
+ case u_diaeresis:
+ case 0x169:
+ case 0x16b:
+ case 0x16d:
+ case 0x16f:
+ case 0x171:
+ case 0x173:
+ case 0x1b0:
+ case 0x1d4:
+ case 0x1d6:
+ case 0x1d8:
+ case 0x1da:
+ case 0x1dc:
+ case 0x215:
+ case 0x217:
+ case 0x289:
+ case 0x1d7e:
+ case 0x1d99:
+ case 0x1e73:
+ case 0x1e75:
+ case 0x1e77:
+ case 0x1e79:
+ case 0x1e7b:
+ case 0x1ee5:
+ case 0x1ee7:
+ case 0x1ee9:
+ case 0x1eeb:
+ case 0x1eed:
+ case 0x1eef:
+ case 0x1ef1:
+ EMIT2('u') EMIT2(u_grave) EMIT2(u_acute) // NOLINT(whitespace/cast)
+ EMIT2(u_circumflex) EMIT2(u_diaeresis) // NOLINT(whitespace/cast)
+ EMIT2(0x169) EMIT2(0x16b)
+ EMIT2(0x16d) EMIT2(0x16f) EMIT2(0x171)
+ EMIT2(0x173) EMIT2(0x1d6) EMIT2(0x1d8)
+ EMIT2(0x215) EMIT2(0x217) EMIT2(0x1b0)
+ EMIT2(0x1d4) EMIT2(0x1da) EMIT2(0x1dc)
+ EMIT2(0x289) EMIT2(0x1e73) EMIT2(0x1d7e)
+ EMIT2(0x1d99) EMIT2(0x1e75) EMIT2(0x1e77)
+ EMIT2(0x1e79) EMIT2(0x1e7b) EMIT2(0x1ee5)
+ EMIT2(0x1ee7) EMIT2(0x1ee9) EMIT2(0x1eeb)
+ EMIT2(0x1eed) EMIT2(0x1eef) EMIT2(0x1ef1)
return;
- case 'v': CASEMBC(0x1e7d)
- EMIT2('v'); EMITMBC(0x1e7d)
+ case 'v':
+ case 0x28b:
+ case 0x1d8c:
+ case 0x1e7d:
+ case 0x1e7f:
+ EMIT2('v') EMIT2(0x28b) EMIT2(0x1d8c) EMIT2(0x1e7d)
+ EMIT2(0x1e7f)
return;
- case 'w': CASEMBC(0x175) CASEMBC(0x1e81) CASEMBC(0x1e83)
- CASEMBC(0x1e85) CASEMBC(0x1e87) CASEMBC(0x1e98)
- EMIT2('w'); EMITMBC(0x175) EMITMBC(0x1e81) EMITMBC(0x1e83)
- EMITMBC(0x1e85) EMITMBC(0x1e87) EMITMBC(0x1e98)
+ case 'w':
+ case 0x175:
+ case 0x1e81:
+ case 0x1e83:
+ case 0x1e85:
+ case 0x1e87:
+ case 0x1e89:
+ case 0x1e98:
+ EMIT2('w') EMIT2(0x175) EMIT2(0x1e81) EMIT2(0x1e83)
+ EMIT2(0x1e85) EMIT2(0x1e87) EMIT2(0x1e89) EMIT2(0x1e98)
return;
- case 'x': CASEMBC(0x1e8b) CASEMBC(0x1e8d)
- EMIT2('x'); EMITMBC(0x1e8b) EMITMBC(0x1e8d)
+ case 'x':
+ case 0x1e8b:
+ case 0x1e8d:
+ EMIT2('x') EMIT2(0x1e8b) EMIT2(0x1e8d)
return;
- case 'y': case y_acute: case y_diaeresis: CASEMBC(0x177)
- CASEMBC(0x1e8f) CASEMBC(0x1e99) CASEMBC(0x1ef3)
- CASEMBC(0x1ef7) CASEMBC(0x1ef9)
- EMIT2('y'); EMIT2(y_acute); EMIT2(y_diaeresis);
- EMITMBC(0x177)
- EMITMBC(0x1e8f) EMITMBC(0x1e99) EMITMBC(0x1ef3)
- EMITMBC(0x1ef7) EMITMBC(0x1ef9)
+ case 'y':
+ case y_acute:
+ case y_diaeresis:
+ case 0x177:
+ case 0x1b4:
+ case 0x233:
+ case 0x24f:
+ case 0x1e8f:
+ case 0x1e99:
+ case 0x1ef3:
+ case 0x1ef5:
+ case 0x1ef7:
+ case 0x1ef9:
+ EMIT2('y') EMIT2(y_acute) EMIT2(y_diaeresis)
+ EMIT2(0x177) EMIT2(0x1b4) EMIT2(0x233) EMIT2(0x24f)
+ EMIT2(0x1e8f) EMIT2(0x1e99) EMIT2(0x1ef3)
+ EMIT2(0x1ef5) EMIT2(0x1ef7) EMIT2(0x1ef9)
return;
- case 'z': CASEMBC(0x17a) CASEMBC(0x17c) CASEMBC(0x17e)
- CASEMBC(0x1b6) CASEMBC(0x1e91) CASEMBC(0x1e95)
- EMIT2('z'); EMITMBC(0x17a) EMITMBC(0x17c) EMITMBC(0x17e)
- EMITMBC(0x1b6) EMITMBC(0x1e91) EMITMBC(0x1e95)
+ case 'z':
+ case 0x17a:
+ case 0x17c:
+ case 0x17e:
+ case 0x1b6:
+ case 0x1d76:
+ case 0x1d8e:
+ case 0x1e91:
+ case 0x1e93:
+ case 0x1e95:
+ case 0x2c6c:
+ EMIT2('z') EMIT2(0x17a) EMIT2(0x17c) EMIT2(0x17e)
+ EMIT2(0x1b6) EMIT2(0x1d76) EMIT2(0x1d8e) EMIT2(0x1e91)
+ EMIT2(0x1e93) EMIT2(0x1e95) EMIT2(0x2c6c)
return;
- /* default: character itself */
+ // default: character itself
}
}
EMIT2(c);
#undef EMIT2
-#undef EMITMBC
}
/*
@@ -1155,9 +1805,9 @@ static int nfa_regatom(void)
int equiclass;
int collclass;
int got_coll_char;
- char_u *p;
- char_u *endp;
- char_u *old_regparse = regparse;
+ char_u *p;
+ char_u *endp;
+ char_u *old_regparse = regparse;
int extra = 0;
int emit_range;
int negated;
@@ -1188,14 +1838,15 @@ static int nfa_regatom(void)
case Magic('_'):
c = no_Magic(getchr());
- if (c == NUL)
+ if (c == NUL) {
EMSG_RET_FAIL(_(e_nul_found));
+ }
- if (c == '^') { /* "\_^" is start-of-line */
+ if (c == '^') { // "\_^" is start-of-line
EMIT(NFA_BOL);
break;
}
- if (c == '$') { /* "\_$" is end-of-line */
+ if (c == '$') { // "\_$" is end-of-line
EMIT(NFA_EOL);
had_eol = true;
break;
@@ -1203,12 +1854,13 @@ static int nfa_regatom(void)
extra = NFA_ADD_NL;
- /* "\_[" is collection plus newline */
- if (c == '[')
+ // "\_[" is collection plus newline
+ if (c == '[') {
goto collection;
+ }
- // "\_x" is character class plus newline
- FALLTHROUGH;
+ // "\_x" is character class plus newline
+ FALLTHROUGH;
/*
* Character classes.
@@ -1240,7 +1892,7 @@ static int nfa_regatom(void)
case Magic('L'):
case Magic('u'):
case Magic('U'):
- p = vim_strchr(classchars, no_Magic(c));
+ p = (char_u *)vim_strchr((char *)classchars, no_Magic(c));
if (p == NULL) {
if (extra == NFA_ADD_NL) {
semsg(_(e_ill_char_class), (int64_t)c);
@@ -1298,9 +1950,8 @@ static int nfa_regatom(void)
semsg(_(e_misplaced), (int64_t)no_Magic(c));
return FAIL;
- case Magic('~'):
- {
- char_u *lp;
+ case Magic('~'): {
+ char_u *lp;
// Previous substitute pattern.
// Generated as "\%(pattern\)".
@@ -1309,7 +1960,7 @@ static int nfa_regatom(void)
return FAIL;
}
for (lp = reg_prev_sub; *lp != NUL; MB_CPTR_ADV(lp)) {
- EMIT(utf_ptr2char(lp));
+ EMIT(utf_ptr2char((char *)lp));
if (lp != reg_prev_sub) {
EMIT(NFA_CONCAT);
}
@@ -1326,17 +1977,16 @@ static int nfa_regatom(void)
case Magic('6'):
case Magic('7'):
case Magic('8'):
- case Magic('9'):
- {
- int refnum = no_Magic(c) - '1';
+ case Magic('9'): {
+ int refnum = no_Magic(c) - '1';
- if (!seen_endbrace(refnum + 1)) {
- return FAIL;
- }
- EMIT(NFA_BACKREF1 + refnum);
- rex.nfa_has_backref = true;
+ if (!seen_endbrace(refnum + 1)) {
+ return FAIL;
}
- break;
+ EMIT(NFA_BACKREF1 + refnum);
+ rex.nfa_has_backref = true;
+ }
+ break;
case Magic('z'):
c = no_Magic(getchr());
@@ -1392,28 +2042,35 @@ static int nfa_regatom(void)
case Magic('%'):
c = no_Magic(getchr());
switch (c) {
- /* () without a back reference */
+ // () without a back reference
case '(':
- if (nfa_reg(REG_NPAREN) == FAIL)
+ if (nfa_reg(REG_NPAREN) == FAIL) {
return FAIL;
+ }
EMIT(NFA_NOPEN);
break;
- case 'd': /* %d123 decimal */
- case 'o': /* %o123 octal */
- case 'x': /* %xab hex 2 */
- case 'u': /* %uabcd hex 4 */
- case 'U': /* %U1234abcd hex 8 */
+ case 'd': // %d123 decimal
+ case 'o': // %o123 octal
+ case 'x': // %xab hex 2
+ case 'u': // %uabcd hex 4
+ case 'U': // %U1234abcd hex 8
{
int64_t nr;
switch (c) {
- case 'd': nr = getdecchrs(); break;
- case 'o': nr = getoctchrs(); break;
- case 'x': nr = gethexchrs(2); break;
- case 'u': nr = gethexchrs(4); break;
- case 'U': nr = gethexchrs(8); break;
- default: nr = -1; break;
+ case 'd':
+ nr = getdecchrs(); break;
+ case 'o':
+ nr = getoctchrs(); break;
+ case 'x':
+ nr = gethexchrs(2); break;
+ case 'u':
+ nr = gethexchrs(4); break;
+ case 'U':
+ nr = gethexchrs(8); break;
+ default:
+ nr = -1; break;
}
if (nr < 0 || nr > INT_MAX) {
@@ -1448,18 +2105,19 @@ static int nfa_regatom(void)
EMIT(NFA_ANY_COMPOSING);
break;
- case '[':
- {
+ case '[': {
int n;
- /* \%[abc] */
- for (n = 0; (c = peekchr()) != ']'; ++n) {
- if (c == NUL)
+ // \%[abc]
+ for (n = 0; (c = peekchr()) != ']'; n++) {
+ if (c == NUL) {
EMSG2_RET_FAIL(_(e_missing_sb),
- reg_magic == MAGIC_ALL);
- /* recursive call! */
- if (nfa_regatom() == FAIL)
+ reg_magic == MAGIC_ALL);
+ }
+ // recursive call!
+ if (nfa_regatom() == FAIL) {
return FAIL;
+ }
}
(void)getchr(); // get the ]
if (n == 0) {
@@ -1479,14 +2137,23 @@ static int nfa_regatom(void)
break;
}
- default:
- {
+ default: {
int64_t n = 0;
const int cmp = c;
+ bool cur = false;
- if (c == '<' || c == '>')
+ if (c == '<' || c == '>') {
+ c = getchr();
+ }
+ if (no_Magic(c) == '.') {
+ cur = true;
c = getchr();
+ }
while (ascii_isdigit(c)) {
+ if (cur) {
+ semsg(_(e_regexp_number_after_dot_pos_search), no_Magic(c));
+ return FAIL;
+ }
if (n > (INT32_MAX - (c - '0')) / 10) {
// overflow.
emsg(_(e_value_too_large));
@@ -1499,6 +2166,9 @@ static int nfa_regatom(void)
int32_t limit = INT32_MAX;
if (c == 'l') {
+ if (cur) {
+ n = curwin->w_cursor.lnum;
+ }
// \%{n}l \%{n}<l \%{n}>l
EMIT(cmp == '<' ? NFA_LNUM_LT :
cmp == '>' ? NFA_LNUM_GT : NFA_LNUM);
@@ -1506,10 +2176,19 @@ static int nfa_regatom(void)
at_start = true;
}
} else if (c == 'c') {
+ if (cur) {
+ n = curwin->w_cursor.col;
+ n++;
+ }
// \%{n}c \%{n}<c \%{n}>c
EMIT(cmp == '<' ? NFA_COL_LT :
cmp == '>' ? NFA_COL_GT : NFA_COL);
} else {
+ if (cur) {
+ colnr_T vcol = 0;
+ getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &vcol);
+ n = ++vcol;
+ }
// \%{n}v \%{n}<v \%{n}>v
EMIT(cmp == '<' ? NFA_VCOL_LT :
cmp == '>' ? NFA_VCOL_GT : NFA_VCOL);
@@ -1522,9 +2201,9 @@ static int nfa_regatom(void)
EMIT((int)n);
break;
} else if (c == '\'' && n == 0) {
- /* \%'m \%<'m \%>'m */
+ // \%'m \%<'m \%>'m
EMIT(cmp == '<' ? NFA_MARK_LT :
- cmp == '>' ? NFA_MARK_GT : NFA_MARK);
+ cmp == '>' ? NFA_MARK_GT : NFA_MARK);
EMIT(getchr());
break;
}
@@ -1562,8 +2241,9 @@ collection:
EMIT(result - NFA_ADD_NL);
EMIT(NFA_NEWL);
EMIT(NFA_OR);
- } else
+ } else {
EMIT(result);
+ }
regparse = endp;
MB_PTR_ADV(regparse);
return OK;
@@ -1578,8 +2258,9 @@ collection:
negated = true;
MB_PTR_ADV(regparse);
EMIT(NFA_START_NEG_COLL);
- } else
+ } else {
EMIT(NFA_START_COLL);
+ }
if (*regparse == '-') {
startc = '-';
EMIT(startc);
@@ -1593,16 +2274,17 @@ collection:
startc = -1;
got_coll_char = false;
if (*regparse == '[') {
- /* Check for [: :], [= =], [. .] */
+ // Check for [: :], [= =], [. .]
equiclass = collclass = 0;
charclass = get_char_class(&regparse);
if (charclass == CLASS_NONE) {
equiclass = get_equi_class(&regparse);
- if (equiclass == 0)
+ if (equiclass == 0) {
collclass = get_coll_element(&regparse);
+ }
}
- /* Character class like [:alpha:] */
+ // Character class like [:alpha:]
if (charclass != CLASS_NONE) {
switch (charclass) {
case CLASS_ALNUM:
@@ -1668,12 +2350,12 @@ collection:
EMIT(NFA_CONCAT);
continue;
}
- /* Try equivalence class [=a=] and the like */
+ // Try equivalence class [=a=] and the like
if (equiclass != 0) {
nfa_emit_equi_class(equiclass);
continue;
}
- /* Try collating class like [. .] */
+ // Try collating class like [. .]
if (collclass != 0) {
startc = collclass; // allow [.a.]-x as a range
// Will emit the proper atom at the end of the
@@ -1698,36 +2380,33 @@ collection:
&& (vim_strchr(REGEXP_INRANGE, regparse[1]) != NULL
|| (!reg_cpo_lit
&& vim_strchr(REGEXP_ABBR, regparse[1])
- != NULL)
- )
- ) {
+ != NULL))) {
MB_PTR_ADV(regparse);
if (*regparse == 'n') {
startc = (reg_string || emit_range || regparse[1] == '-')
? NL : NFA_NEWL;
- } else if (*regparse == 'd'
- || *regparse == 'o'
- || *regparse == 'x'
- || *regparse == 'u'
- || *regparse == 'U'
- ) {
+ } else if (*regparse == 'd'
+ || *regparse == 'o'
+ || *regparse == 'x'
+ || *regparse == 'u'
+ || *regparse == 'U') {
// TODO(RE): This needs more testing
startc = coll_get_char();
got_coll_char = true;
MB_PTR_BACK(old_regparse, regparse);
} else {
- /* \r,\t,\e,\b */
+ // \r,\t,\e,\b
startc = backslash_trans(*regparse);
}
}
// Normal printable char
if (startc == -1) {
- startc = utf_ptr2char(regparse);
+ startc = utf_ptr2char((char *)regparse);
}
- /* Previous char was '-', so this char is end of range. */
+ // Previous char was '-', so this char is end of range.
if (emit_range) {
int endc = startc;
startc = oldstartc;
@@ -1799,7 +2478,7 @@ collection:
EMIT(NFA_CONCAT);
}
- /* skip the trailing ] */
+ // skip the trailing ]
regparse = endp;
MB_PTR_ADV(regparse);
@@ -1817,19 +2496,19 @@ collection:
}
return OK;
- } /* if exists closing ] */
+ } // if exists closing ]
- if (reg_strict)
+ if (reg_strict) {
EMSG_RET_FAIL(_(e_missingbracket));
+ }
FALLTHROUGH;
- default:
- {
+ default: {
int plen;
nfa_do_multibyte:
// plen is length of current char with composing chars
- if (utf_char2len(c) != (plen = utfc_ptr2len(old_regparse))
+ if (utf_char2len(c) != (plen = utfc_ptr2len((char *)old_regparse))
|| utf_iscomposing(c)) {
int i = 0;
@@ -1841,13 +2520,15 @@ nfa_do_multibyte:
// building the postfix form, not the NFA itself;
// a composing char could be: a, b, c, NFA_COMPOSING
// where 'b' and 'c' are chars with codes > 256. */
- for (;; ) {
+ for (;;) {
EMIT(c);
- if (i > 0)
+ if (i > 0) {
EMIT(NFA_CONCAT);
- if ((i += utf_char2len(c)) >= plen)
+ }
+ if ((i += utf_char2len(c)) >= plen) {
break;
- c = utf_ptr2char(old_regparse + i);
+ }
+ c = utf_ptr2char((char *)old_regparse + i);
}
EMIT(NFA_COMPOSING);
regparse = old_regparse + plen;
@@ -1869,8 +2550,8 @@ nfa_do_multibyte:
* times the atom can be matched. Example: "a*" matches any sequence of "a"
* characters: "", "a", "aa", etc.
*
- * piece ::= atom
- * or atom multi
+ * piece ::= atom
+ * or atom multi
*/
static int nfa_regpiece(void)
{
@@ -1890,16 +2571,17 @@ static int nfa_regpiece(void)
// next.
save_parse_state(&old_state);
- /* store current pos in the postfix form, for \{m,n} involving 0s */
+ // store current pos in the postfix form, for \{m,n} involving 0s
my_post_start = (int)(post_ptr - post_start);
ret = nfa_regatom();
- if (ret == FAIL)
- return FAIL; /* cascaded error */
-
+ if (ret == FAIL) {
+ return FAIL; // cascaded error
+ }
op = peekchr();
- if (re_multi_type(op) == NOT_MULTI)
+ if (re_multi_type(op) == NOT_MULTI) {
return OK;
+ }
skipchr();
switch (op) {
@@ -1921,37 +2603,39 @@ static int nfa_regpiece(void)
*/
restore_parse_state(&old_state);
curchr = -1;
- if (nfa_regatom() == FAIL)
+ if (nfa_regatom() == FAIL) {
return FAIL;
+ }
EMIT(NFA_STAR);
EMIT(NFA_CONCAT);
- skipchr(); /* skip the \+ */
+ skipchr(); // skip the \+
break;
case Magic('@'):
c2 = getdecchrs();
op = no_Magic(getchr());
i = 0;
- switch(op) {
+ switch (op) {
case '=':
- /* \@= */
+ // \@=
i = NFA_PREV_ATOM_NO_WIDTH;
break;
case '!':
- /* \@! */
+ // \@!
i = NFA_PREV_ATOM_NO_WIDTH_NEG;
break;
case '<':
op = no_Magic(getchr());
- if (op == '=')
- /* \@<= */
+ if (op == '=') {
+ // \@<=
i = NFA_PREV_ATOM_JUST_BEFORE;
- else if (op == '!')
- /* \@<! */
+ } else if (op == '!') {
+ // \@<!
i = NFA_PREV_ATOM_JUST_BEFORE_NEG;
+ }
break;
case '>':
- /* \@> */
+ // \@>
i = NFA_PREV_ATOM_LIKE_PATTERN;
break;
}
@@ -1961,8 +2645,9 @@ static int nfa_regpiece(void)
}
EMIT(i);
if (i == NFA_PREV_ATOM_JUST_BEFORE
- || i == NFA_PREV_ATOM_JUST_BEFORE_NEG)
+ || i == NFA_PREV_ATOM_JUST_BEFORE_NEG) {
EMIT(c2);
+ }
break;
case Magic('?'):
@@ -1983,26 +2668,28 @@ static int nfa_regpiece(void)
skipchr();
greedy = false;
}
- if (!read_limits(&minval, &maxval))
+ if (!read_limits(&minval, &maxval)) {
EMSG_RET_FAIL(_("E870: (NFA regexp) Error reading repetition limits"));
+ }
// <atom>{0,inf}, <atom>{0,} and <atom>{} are equivalent to
// <atom>*
if (minval == 0 && maxval == MAX_LIMIT) {
- if (greedy)
- /* \{}, \{0,} */
+ if (greedy) {
+ // \{}, \{0,}
EMIT(NFA_STAR);
- else
- /* \{-}, \{-0,} */
+ } else {
+ // \{-}, \{-0,}
EMIT(NFA_STAR_NONGREEDY);
+ }
break;
}
- /* Special case: x{0} or x{-0} */
+ // Special case: x{0} or x{-0}
if (maxval == 0) {
- /* Ignore result of previous call to nfa_regatom() */
+ // Ignore result of previous call to nfa_regatom()
post_ptr = post_start + my_post_start;
- /* NFA_EMPTY is 0-length and works everywhere */
+ // NFA_EMPTY is 0-length and works everywhere
EMIT(NFA_EMPTY);
return OK;
}
@@ -2021,44 +2708,48 @@ static int nfa_regpiece(void)
return FAIL;
}
- /* Ignore previous call to nfa_regatom() */
+ // Ignore previous call to nfa_regatom()
post_ptr = post_start + my_post_start;
- /* Save parse state after the repeated atom and the \{} */
+ // Save parse state after the repeated atom and the \{}
save_parse_state(&new_state);
quest = (greedy == true ? NFA_QUEST : NFA_QUEST_NONGREEDY);
for (i = 0; i < maxval; i++) {
- /* Goto beginning of the repeated atom */
+ // Goto beginning of the repeated atom
restore_parse_state(&old_state);
old_post_pos = (int)(post_ptr - post_start);
- if (nfa_regatom() == FAIL)
+ if (nfa_regatom() == FAIL) {
return FAIL;
- /* after "minval" times, atoms are optional */
+ }
+ // after "minval" times, atoms are optional
if (i + 1 > minval) {
if (maxval == MAX_LIMIT) {
- if (greedy)
+ if (greedy) {
EMIT(NFA_STAR);
- else
+ } else {
EMIT(NFA_STAR_NONGREEDY);
- } else
+ }
+ } else {
EMIT(quest);
+ }
}
- if (old_post_pos != my_post_start)
+ if (old_post_pos != my_post_start) {
EMIT(NFA_CONCAT);
- if (i + 1 > minval && maxval == MAX_LIMIT)
+ }
+ if (i + 1 > minval && maxval == MAX_LIMIT) {
break;
+ }
}
- /* Go to just after the repeated atom and the \{} */
+ // Go to just after the repeated atom and the \{}
restore_parse_state(&new_state);
curchr = -1;
break;
-
default:
break;
- } /* end switch */
+ } // end switch
if (re_multi_type(peekchr()) != NOT_MULTI) {
// Can't have a multi follow a multi.
@@ -2073,10 +2764,10 @@ static int nfa_regpiece(void)
* first piece, followed by a match for the second piece, etc. Example:
* "f[0-9]b", first matches "f", then a digit and then "b".
*
- * concat ::= piece
- * or piece piece
- * or piece piece piece
- * etc.
+ * concat ::= piece
+ * or piece piece
+ * or piece piece piece
+ * etc.
*/
static int nfa_regconcat(void)
{
@@ -2148,10 +2839,10 @@ static int nfa_regconcat(void)
* "foobeep\&..." matches "foo" in "foobeep".
* ".*Peter\&.*Bob" matches in a line containing both "Peter" and "Bob"
*
- * branch ::= concat
- * or concat \& concat
- * or concat \& concat \& concat
- * etc.
+ * branch ::= concat
+ * or concat \& concat
+ * or concat \& concat \& concat
+ * etc.
*/
static int nfa_regbranch(void)
{
@@ -2159,9 +2850,10 @@ static int nfa_regbranch(void)
old_post_pos = (int)(post_ptr - post_start);
- /* First branch, possibly the only one */
- if (nfa_regconcat() == FAIL)
+ // First branch, possibly the only one
+ if (nfa_regconcat() == FAIL) {
return FAIL;
+ }
// Try next concats
while (peekchr() == Magic('&')) {
@@ -2173,71 +2865,76 @@ static int nfa_regbranch(void)
EMIT(NFA_NOPEN);
EMIT(NFA_PREV_ATOM_NO_WIDTH);
old_post_pos = (int)(post_ptr - post_start);
- if (nfa_regconcat() == FAIL)
+ if (nfa_regconcat() == FAIL) {
return FAIL;
- /* if concat is empty do emit a node */
- if (old_post_pos == (int)(post_ptr - post_start))
+ }
+ // if concat is empty do emit a node
+ if (old_post_pos == (int)(post_ptr - post_start)) {
EMIT(NFA_EMPTY);
+ }
EMIT(NFA_CONCAT);
}
- /* if a branch is empty, emit one node for it */
- if (old_post_pos == (int)(post_ptr - post_start))
+ // if a branch is empty, emit one node for it
+ if (old_post_pos == (int)(post_ptr - post_start)) {
EMIT(NFA_EMPTY);
+ }
return OK;
}
-/*
- * Parse a pattern, one or more branches, separated by "\|". It matches
- * anything that matches one of the branches. Example: "foo\|beep" matches
- * "foo" and matches "beep". If more than one branch matches, the first one
- * is used.
- *
- * pattern ::= branch
- * or branch \| branch
- * or branch \| branch \| branch
- * etc.
- */
-static int
-nfa_reg (
- int paren /* REG_NOPAREN, REG_PAREN, REG_NPAREN or REG_ZPAREN */
-)
+/// Parse a pattern, one or more branches, separated by "\|". It matches
+/// anything that matches one of the branches. Example: "foo\|beep" matches
+/// "foo" and matches "beep". If more than one branch matches, the first one
+/// is used.
+///
+/// pattern ::= branch
+/// or branch \| branch
+/// or branch \| branch \| branch
+/// etc.
+///
+/// @param paren REG_NOPAREN, REG_PAREN, REG_NPAREN or REG_ZPAREN
+static int nfa_reg(int paren)
{
int parno = 0;
if (paren == REG_PAREN) {
- if (regnpar >= NSUBEXP) /* Too many `(' */
+ if (regnpar >= NSUBEXP) { // Too many `('
EMSG_RET_FAIL(_("E872: (NFA regexp) Too many '('"));
+ }
parno = regnpar++;
} else if (paren == REG_ZPAREN) {
- /* Make a ZOPEN node. */
- if (regnzpar >= NSUBEXP)
+ // Make a ZOPEN node.
+ if (regnzpar >= NSUBEXP) {
EMSG_RET_FAIL(_("E879: (NFA regexp) Too many \\z("));
+ }
parno = regnzpar++;
}
- if (nfa_regbranch() == FAIL)
- return FAIL; /* cascaded error */
-
+ if (nfa_regbranch() == FAIL) {
+ return FAIL; // cascaded error
+ }
while (peekchr() == Magic('|')) {
skipchr();
- if (nfa_regbranch() == FAIL)
- return FAIL; /* cascaded error */
+ if (nfa_regbranch() == FAIL) {
+ return FAIL; // cascaded error
+ }
EMIT(NFA_OR);
}
- /* Check for proper termination. */
+ // Check for proper termination.
if (paren != REG_NOPAREN && getchr() != Magic(')')) {
- if (paren == REG_NPAREN)
+ if (paren == REG_NPAREN) {
EMSG2_RET_FAIL(_(e_unmatchedpp), reg_magic == MAGIC_ALL);
- else
+ } else {
EMSG2_RET_FAIL(_(e_unmatchedp), reg_magic == MAGIC_ALL);
+ }
} else if (paren == REG_NOPAREN && peekchr() != NUL) {
- if (peekchr() == Magic(')'))
+ if (peekchr() == Magic(')')) {
EMSG2_RET_FAIL(_(e_unmatchedpar), reg_magic == MAGIC_ALL);
- else
+ } else {
EMSG_RET_FAIL(_("E873: (NFA regexp) proper termination error"));
+ }
}
// Here we set the flag allowing back references to this set of
// parentheses.
@@ -2265,32 +2962,57 @@ static void nfa_set_code(int c)
STRCPY(code, "");
switch (c) {
- case NFA_MATCH: STRCPY(code, "NFA_MATCH "); break;
- case NFA_SPLIT: STRCPY(code, "NFA_SPLIT "); break;
- case NFA_CONCAT: STRCPY(code, "NFA_CONCAT "); break;
- case NFA_NEWL: STRCPY(code, "NFA_NEWL "); break;
- case NFA_ZSTART: STRCPY(code, "NFA_ZSTART"); break;
- case NFA_ZEND: STRCPY(code, "NFA_ZEND"); break;
-
- case NFA_BACKREF1: STRCPY(code, "NFA_BACKREF1"); break;
- case NFA_BACKREF2: STRCPY(code, "NFA_BACKREF2"); break;
- case NFA_BACKREF3: STRCPY(code, "NFA_BACKREF3"); break;
- case NFA_BACKREF4: STRCPY(code, "NFA_BACKREF4"); break;
- case NFA_BACKREF5: STRCPY(code, "NFA_BACKREF5"); break;
- case NFA_BACKREF6: STRCPY(code, "NFA_BACKREF6"); break;
- case NFA_BACKREF7: STRCPY(code, "NFA_BACKREF7"); break;
- case NFA_BACKREF8: STRCPY(code, "NFA_BACKREF8"); break;
- case NFA_BACKREF9: STRCPY(code, "NFA_BACKREF9"); break;
- case NFA_ZREF1: STRCPY(code, "NFA_ZREF1"); break;
- case NFA_ZREF2: STRCPY(code, "NFA_ZREF2"); break;
- case NFA_ZREF3: STRCPY(code, "NFA_ZREF3"); break;
- case NFA_ZREF4: STRCPY(code, "NFA_ZREF4"); break;
- case NFA_ZREF5: STRCPY(code, "NFA_ZREF5"); break;
- case NFA_ZREF6: STRCPY(code, "NFA_ZREF6"); break;
- case NFA_ZREF7: STRCPY(code, "NFA_ZREF7"); break;
- case NFA_ZREF8: STRCPY(code, "NFA_ZREF8"); break;
- case NFA_ZREF9: STRCPY(code, "NFA_ZREF9"); break;
- case NFA_SKIP: STRCPY(code, "NFA_SKIP"); break;
+ case NFA_MATCH:
+ STRCPY(code, "NFA_MATCH "); break;
+ case NFA_SPLIT:
+ STRCPY(code, "NFA_SPLIT "); break;
+ case NFA_CONCAT:
+ STRCPY(code, "NFA_CONCAT "); break;
+ case NFA_NEWL:
+ STRCPY(code, "NFA_NEWL "); break;
+ case NFA_ZSTART:
+ STRCPY(code, "NFA_ZSTART"); break;
+ case NFA_ZEND:
+ STRCPY(code, "NFA_ZEND"); break;
+
+ case NFA_BACKREF1:
+ STRCPY(code, "NFA_BACKREF1"); break;
+ case NFA_BACKREF2:
+ STRCPY(code, "NFA_BACKREF2"); break;
+ case NFA_BACKREF3:
+ STRCPY(code, "NFA_BACKREF3"); break;
+ case NFA_BACKREF4:
+ STRCPY(code, "NFA_BACKREF4"); break;
+ case NFA_BACKREF5:
+ STRCPY(code, "NFA_BACKREF5"); break;
+ case NFA_BACKREF6:
+ STRCPY(code, "NFA_BACKREF6"); break;
+ case NFA_BACKREF7:
+ STRCPY(code, "NFA_BACKREF7"); break;
+ case NFA_BACKREF8:
+ STRCPY(code, "NFA_BACKREF8"); break;
+ case NFA_BACKREF9:
+ STRCPY(code, "NFA_BACKREF9"); break;
+ case NFA_ZREF1:
+ STRCPY(code, "NFA_ZREF1"); break;
+ case NFA_ZREF2:
+ STRCPY(code, "NFA_ZREF2"); break;
+ case NFA_ZREF3:
+ STRCPY(code, "NFA_ZREF3"); break;
+ case NFA_ZREF4:
+ STRCPY(code, "NFA_ZREF4"); break;
+ case NFA_ZREF5:
+ STRCPY(code, "NFA_ZREF5"); break;
+ case NFA_ZREF6:
+ STRCPY(code, "NFA_ZREF6"); break;
+ case NFA_ZREF7:
+ STRCPY(code, "NFA_ZREF7"); break;
+ case NFA_ZREF8:
+ STRCPY(code, "NFA_ZREF8"); break;
+ case NFA_ZREF9:
+ STRCPY(code, "NFA_ZREF9"); break;
+ case NFA_SKIP:
+ STRCPY(code, "NFA_SKIP"); break;
case NFA_PREV_ATOM_NO_WIDTH:
STRCPY(code, "NFA_PREV_ATOM_NO_WIDTH"); break;
@@ -2303,9 +3025,12 @@ static void nfa_set_code(int c)
case NFA_PREV_ATOM_LIKE_PATTERN:
STRCPY(code, "NFA_PREV_ATOM_LIKE_PATTERN"); break;
- case NFA_NOPEN: STRCPY(code, "NFA_NOPEN"); break;
- case NFA_NCLOSE: STRCPY(code, "NFA_NCLOSE"); break;
- case NFA_START_INVISIBLE: STRCPY(code, "NFA_START_INVISIBLE"); break;
+ case NFA_NOPEN:
+ STRCPY(code, "NFA_NOPEN"); break;
+ case NFA_NCLOSE:
+ STRCPY(code, "NFA_NCLOSE"); break;
+ case NFA_START_INVISIBLE:
+ STRCPY(code, "NFA_START_INVISIBLE"); break;
case NFA_START_INVISIBLE_FIRST:
STRCPY(code, "NFA_START_INVISIBLE_FIRST"); break;
case NFA_START_INVISIBLE_NEG:
@@ -2320,14 +3045,21 @@ static void nfa_set_code(int c)
STRCPY(code, "NFA_START_INVISIBLE_BEFORE_NEG"); break;
case NFA_START_INVISIBLE_BEFORE_NEG_FIRST:
STRCPY(code, "NFA_START_INVISIBLE_BEFORE_NEG_FIRST"); break;
- case NFA_START_PATTERN: STRCPY(code, "NFA_START_PATTERN"); break;
- case NFA_END_INVISIBLE: STRCPY(code, "NFA_END_INVISIBLE"); break;
- case NFA_END_INVISIBLE_NEG: STRCPY(code, "NFA_END_INVISIBLE_NEG"); break;
- case NFA_END_PATTERN: STRCPY(code, "NFA_END_PATTERN"); break;
+ case NFA_START_PATTERN:
+ STRCPY(code, "NFA_START_PATTERN"); break;
+ case NFA_END_INVISIBLE:
+ STRCPY(code, "NFA_END_INVISIBLE"); break;
+ case NFA_END_INVISIBLE_NEG:
+ STRCPY(code, "NFA_END_INVISIBLE_NEG"); break;
+ case NFA_END_PATTERN:
+ STRCPY(code, "NFA_END_PATTERN"); break;
- case NFA_COMPOSING: STRCPY(code, "NFA_COMPOSING"); break;
- case NFA_END_COMPOSING: STRCPY(code, "NFA_END_COMPOSING"); break;
- case NFA_OPT_CHARS: STRCPY(code, "NFA_OPT_CHARS"); break;
+ case NFA_COMPOSING:
+ STRCPY(code, "NFA_COMPOSING"); break;
+ case NFA_END_COMPOSING:
+ STRCPY(code, "NFA_END_COMPOSING"); break;
+ case NFA_OPT_CHARS:
+ STRCPY(code, "NFA_OPT_CHARS"); break;
case NFA_MOPEN:
case NFA_MOPEN1:
@@ -2381,94 +3113,178 @@ static void nfa_set_code(int c)
STRCPY(code, "NFA_ZCLOSE(x)");
code[11] = c - NFA_ZCLOSE + '0';
break;
- case NFA_EOL: STRCPY(code, "NFA_EOL "); break;
- case NFA_BOL: STRCPY(code, "NFA_BOL "); break;
- case NFA_EOW: STRCPY(code, "NFA_EOW "); break;
- case NFA_BOW: STRCPY(code, "NFA_BOW "); break;
- case NFA_EOF: STRCPY(code, "NFA_EOF "); break;
- case NFA_BOF: STRCPY(code, "NFA_BOF "); break;
- case NFA_LNUM: STRCPY(code, "NFA_LNUM "); break;
- case NFA_LNUM_GT: STRCPY(code, "NFA_LNUM_GT "); break;
- case NFA_LNUM_LT: STRCPY(code, "NFA_LNUM_LT "); break;
- case NFA_COL: STRCPY(code, "NFA_COL "); break;
- case NFA_COL_GT: STRCPY(code, "NFA_COL_GT "); break;
- case NFA_COL_LT: STRCPY(code, "NFA_COL_LT "); break;
- case NFA_VCOL: STRCPY(code, "NFA_VCOL "); break;
- case NFA_VCOL_GT: STRCPY(code, "NFA_VCOL_GT "); break;
- case NFA_VCOL_LT: STRCPY(code, "NFA_VCOL_LT "); break;
- case NFA_MARK: STRCPY(code, "NFA_MARK "); break;
- case NFA_MARK_GT: STRCPY(code, "NFA_MARK_GT "); break;
- case NFA_MARK_LT: STRCPY(code, "NFA_MARK_LT "); break;
- case NFA_CURSOR: STRCPY(code, "NFA_CURSOR "); break;
- case NFA_VISUAL: STRCPY(code, "NFA_VISUAL "); break;
- case NFA_ANY_COMPOSING: STRCPY(code, "NFA_ANY_COMPOSING "); break;
-
- case NFA_STAR: STRCPY(code, "NFA_STAR "); break;
- case NFA_STAR_NONGREEDY: STRCPY(code, "NFA_STAR_NONGREEDY "); break;
- case NFA_QUEST: STRCPY(code, "NFA_QUEST"); break;
- case NFA_QUEST_NONGREEDY: STRCPY(code, "NFA_QUEST_NON_GREEDY"); break;
- case NFA_EMPTY: STRCPY(code, "NFA_EMPTY"); break;
- case NFA_OR: STRCPY(code, "NFA_OR"); break;
-
- case NFA_START_COLL: STRCPY(code, "NFA_START_COLL"); break;
- case NFA_END_COLL: STRCPY(code, "NFA_END_COLL"); break;
- case NFA_START_NEG_COLL: STRCPY(code, "NFA_START_NEG_COLL"); break;
- case NFA_END_NEG_COLL: STRCPY(code, "NFA_END_NEG_COLL"); break;
- case NFA_RANGE: STRCPY(code, "NFA_RANGE"); break;
- case NFA_RANGE_MIN: STRCPY(code, "NFA_RANGE_MIN"); break;
- case NFA_RANGE_MAX: STRCPY(code, "NFA_RANGE_MAX"); break;
-
- case NFA_CLASS_ALNUM: STRCPY(code, "NFA_CLASS_ALNUM"); break;
- case NFA_CLASS_ALPHA: STRCPY(code, "NFA_CLASS_ALPHA"); break;
- case NFA_CLASS_BLANK: STRCPY(code, "NFA_CLASS_BLANK"); break;
- case NFA_CLASS_CNTRL: STRCPY(code, "NFA_CLASS_CNTRL"); break;
- case NFA_CLASS_DIGIT: STRCPY(code, "NFA_CLASS_DIGIT"); break;
- case NFA_CLASS_GRAPH: STRCPY(code, "NFA_CLASS_GRAPH"); break;
- case NFA_CLASS_LOWER: STRCPY(code, "NFA_CLASS_LOWER"); break;
- case NFA_CLASS_PRINT: STRCPY(code, "NFA_CLASS_PRINT"); break;
- case NFA_CLASS_PUNCT: STRCPY(code, "NFA_CLASS_PUNCT"); break;
- case NFA_CLASS_SPACE: STRCPY(code, "NFA_CLASS_SPACE"); break;
- case NFA_CLASS_UPPER: STRCPY(code, "NFA_CLASS_UPPER"); break;
- case NFA_CLASS_XDIGIT: STRCPY(code, "NFA_CLASS_XDIGIT"); break;
- case NFA_CLASS_TAB: STRCPY(code, "NFA_CLASS_TAB"); break;
- case NFA_CLASS_RETURN: STRCPY(code, "NFA_CLASS_RETURN"); break;
- case NFA_CLASS_BACKSPACE: STRCPY(code, "NFA_CLASS_BACKSPACE"); break;
- case NFA_CLASS_ESCAPE: STRCPY(code, "NFA_CLASS_ESCAPE"); break;
- case NFA_CLASS_IDENT: STRCPY(code, "NFA_CLASS_IDENT"); break;
- case NFA_CLASS_KEYWORD: STRCPY(code, "NFA_CLASS_KEYWORD"); break;
- case NFA_CLASS_FNAME: STRCPY(code, "NFA_CLASS_FNAME"); break;
-
- case NFA_ANY: STRCPY(code, "NFA_ANY"); break;
- case NFA_IDENT: STRCPY(code, "NFA_IDENT"); break;
- case NFA_SIDENT: STRCPY(code, "NFA_SIDENT"); break;
- case NFA_KWORD: STRCPY(code, "NFA_KWORD"); break;
- case NFA_SKWORD: STRCPY(code, "NFA_SKWORD"); break;
- case NFA_FNAME: STRCPY(code, "NFA_FNAME"); break;
- case NFA_SFNAME: STRCPY(code, "NFA_SFNAME"); break;
- case NFA_PRINT: STRCPY(code, "NFA_PRINT"); break;
- case NFA_SPRINT: STRCPY(code, "NFA_SPRINT"); break;
- case NFA_WHITE: STRCPY(code, "NFA_WHITE"); break;
- case NFA_NWHITE: STRCPY(code, "NFA_NWHITE"); break;
- case NFA_DIGIT: STRCPY(code, "NFA_DIGIT"); break;
- case NFA_NDIGIT: STRCPY(code, "NFA_NDIGIT"); break;
- case NFA_HEX: STRCPY(code, "NFA_HEX"); break;
- case NFA_NHEX: STRCPY(code, "NFA_NHEX"); break;
- case NFA_OCTAL: STRCPY(code, "NFA_OCTAL"); break;
- case NFA_NOCTAL: STRCPY(code, "NFA_NOCTAL"); break;
- case NFA_WORD: STRCPY(code, "NFA_WORD"); break;
- case NFA_NWORD: STRCPY(code, "NFA_NWORD"); break;
- case NFA_HEAD: STRCPY(code, "NFA_HEAD"); break;
- case NFA_NHEAD: STRCPY(code, "NFA_NHEAD"); break;
- case NFA_ALPHA: STRCPY(code, "NFA_ALPHA"); break;
- case NFA_NALPHA: STRCPY(code, "NFA_NALPHA"); break;
- case NFA_LOWER: STRCPY(code, "NFA_LOWER"); break;
- case NFA_NLOWER: STRCPY(code, "NFA_NLOWER"); break;
- case NFA_UPPER: STRCPY(code, "NFA_UPPER"); break;
- case NFA_NUPPER: STRCPY(code, "NFA_NUPPER"); break;
- case NFA_LOWER_IC: STRCPY(code, "NFA_LOWER_IC"); break;
- case NFA_NLOWER_IC: STRCPY(code, "NFA_NLOWER_IC"); break;
- case NFA_UPPER_IC: STRCPY(code, "NFA_UPPER_IC"); break;
- case NFA_NUPPER_IC: STRCPY(code, "NFA_NUPPER_IC"); break;
+ case NFA_EOL:
+ STRCPY(code, "NFA_EOL "); break;
+ case NFA_BOL:
+ STRCPY(code, "NFA_BOL "); break;
+ case NFA_EOW:
+ STRCPY(code, "NFA_EOW "); break;
+ case NFA_BOW:
+ STRCPY(code, "NFA_BOW "); break;
+ case NFA_EOF:
+ STRCPY(code, "NFA_EOF "); break;
+ case NFA_BOF:
+ STRCPY(code, "NFA_BOF "); break;
+ case NFA_LNUM:
+ STRCPY(code, "NFA_LNUM "); break;
+ case NFA_LNUM_GT:
+ STRCPY(code, "NFA_LNUM_GT "); break;
+ case NFA_LNUM_LT:
+ STRCPY(code, "NFA_LNUM_LT "); break;
+ case NFA_COL:
+ STRCPY(code, "NFA_COL "); break;
+ case NFA_COL_GT:
+ STRCPY(code, "NFA_COL_GT "); break;
+ case NFA_COL_LT:
+ STRCPY(code, "NFA_COL_LT "); break;
+ case NFA_VCOL:
+ STRCPY(code, "NFA_VCOL "); break;
+ case NFA_VCOL_GT:
+ STRCPY(code, "NFA_VCOL_GT "); break;
+ case NFA_VCOL_LT:
+ STRCPY(code, "NFA_VCOL_LT "); break;
+ case NFA_MARK:
+ STRCPY(code, "NFA_MARK "); break;
+ case NFA_MARK_GT:
+ STRCPY(code, "NFA_MARK_GT "); break;
+ case NFA_MARK_LT:
+ STRCPY(code, "NFA_MARK_LT "); break;
+ case NFA_CURSOR:
+ STRCPY(code, "NFA_CURSOR "); break;
+ case NFA_VISUAL:
+ STRCPY(code, "NFA_VISUAL "); break;
+ case NFA_ANY_COMPOSING:
+ STRCPY(code, "NFA_ANY_COMPOSING "); break;
+
+ case NFA_STAR:
+ STRCPY(code, "NFA_STAR "); break;
+ case NFA_STAR_NONGREEDY:
+ STRCPY(code, "NFA_STAR_NONGREEDY "); break;
+ case NFA_QUEST:
+ STRCPY(code, "NFA_QUEST"); break;
+ case NFA_QUEST_NONGREEDY:
+ STRCPY(code, "NFA_QUEST_NON_GREEDY"); break;
+ case NFA_EMPTY:
+ STRCPY(code, "NFA_EMPTY"); break;
+ case NFA_OR:
+ STRCPY(code, "NFA_OR"); break;
+
+ case NFA_START_COLL:
+ STRCPY(code, "NFA_START_COLL"); break;
+ case NFA_END_COLL:
+ STRCPY(code, "NFA_END_COLL"); break;
+ case NFA_START_NEG_COLL:
+ STRCPY(code, "NFA_START_NEG_COLL"); break;
+ case NFA_END_NEG_COLL:
+ STRCPY(code, "NFA_END_NEG_COLL"); break;
+ case NFA_RANGE:
+ STRCPY(code, "NFA_RANGE"); break;
+ case NFA_RANGE_MIN:
+ STRCPY(code, "NFA_RANGE_MIN"); break;
+ case NFA_RANGE_MAX:
+ STRCPY(code, "NFA_RANGE_MAX"); break;
+
+ case NFA_CLASS_ALNUM:
+ STRCPY(code, "NFA_CLASS_ALNUM"); break;
+ case NFA_CLASS_ALPHA:
+ STRCPY(code, "NFA_CLASS_ALPHA"); break;
+ case NFA_CLASS_BLANK:
+ STRCPY(code, "NFA_CLASS_BLANK"); break;
+ case NFA_CLASS_CNTRL:
+ STRCPY(code, "NFA_CLASS_CNTRL"); break;
+ case NFA_CLASS_DIGIT:
+ STRCPY(code, "NFA_CLASS_DIGIT"); break;
+ case NFA_CLASS_GRAPH:
+ STRCPY(code, "NFA_CLASS_GRAPH"); break;
+ case NFA_CLASS_LOWER:
+ STRCPY(code, "NFA_CLASS_LOWER"); break;
+ case NFA_CLASS_PRINT:
+ STRCPY(code, "NFA_CLASS_PRINT"); break;
+ case NFA_CLASS_PUNCT:
+ STRCPY(code, "NFA_CLASS_PUNCT"); break;
+ case NFA_CLASS_SPACE:
+ STRCPY(code, "NFA_CLASS_SPACE"); break;
+ case NFA_CLASS_UPPER:
+ STRCPY(code, "NFA_CLASS_UPPER"); break;
+ case NFA_CLASS_XDIGIT:
+ STRCPY(code, "NFA_CLASS_XDIGIT"); break;
+ case NFA_CLASS_TAB:
+ STRCPY(code, "NFA_CLASS_TAB"); break;
+ case NFA_CLASS_RETURN:
+ STRCPY(code, "NFA_CLASS_RETURN"); break;
+ case NFA_CLASS_BACKSPACE:
+ STRCPY(code, "NFA_CLASS_BACKSPACE"); break;
+ case NFA_CLASS_ESCAPE:
+ STRCPY(code, "NFA_CLASS_ESCAPE"); break;
+ case NFA_CLASS_IDENT:
+ STRCPY(code, "NFA_CLASS_IDENT"); break;
+ case NFA_CLASS_KEYWORD:
+ STRCPY(code, "NFA_CLASS_KEYWORD"); break;
+ case NFA_CLASS_FNAME:
+ STRCPY(code, "NFA_CLASS_FNAME"); break;
+
+ case NFA_ANY:
+ STRCPY(code, "NFA_ANY"); break;
+ case NFA_IDENT:
+ STRCPY(code, "NFA_IDENT"); break;
+ case NFA_SIDENT:
+ STRCPY(code, "NFA_SIDENT"); break;
+ case NFA_KWORD:
+ STRCPY(code, "NFA_KWORD"); break;
+ case NFA_SKWORD:
+ STRCPY(code, "NFA_SKWORD"); break;
+ case NFA_FNAME:
+ STRCPY(code, "NFA_FNAME"); break;
+ case NFA_SFNAME:
+ STRCPY(code, "NFA_SFNAME"); break;
+ case NFA_PRINT:
+ STRCPY(code, "NFA_PRINT"); break;
+ case NFA_SPRINT:
+ STRCPY(code, "NFA_SPRINT"); break;
+ case NFA_WHITE:
+ STRCPY(code, "NFA_WHITE"); break;
+ case NFA_NWHITE:
+ STRCPY(code, "NFA_NWHITE"); break;
+ case NFA_DIGIT:
+ STRCPY(code, "NFA_DIGIT"); break;
+ case NFA_NDIGIT:
+ STRCPY(code, "NFA_NDIGIT"); break;
+ case NFA_HEX:
+ STRCPY(code, "NFA_HEX"); break;
+ case NFA_NHEX:
+ STRCPY(code, "NFA_NHEX"); break;
+ case NFA_OCTAL:
+ STRCPY(code, "NFA_OCTAL"); break;
+ case NFA_NOCTAL:
+ STRCPY(code, "NFA_NOCTAL"); break;
+ case NFA_WORD:
+ STRCPY(code, "NFA_WORD"); break;
+ case NFA_NWORD:
+ STRCPY(code, "NFA_NWORD"); break;
+ case NFA_HEAD:
+ STRCPY(code, "NFA_HEAD"); break;
+ case NFA_NHEAD:
+ STRCPY(code, "NFA_NHEAD"); break;
+ case NFA_ALPHA:
+ STRCPY(code, "NFA_ALPHA"); break;
+ case NFA_NALPHA:
+ STRCPY(code, "NFA_NALPHA"); break;
+ case NFA_LOWER:
+ STRCPY(code, "NFA_LOWER"); break;
+ case NFA_NLOWER:
+ STRCPY(code, "NFA_NLOWER"); break;
+ case NFA_UPPER:
+ STRCPY(code, "NFA_UPPER"); break;
+ case NFA_NUPPER:
+ STRCPY(code, "NFA_NUPPER"); break;
+ case NFA_LOWER_IC:
+ STRCPY(code, "NFA_LOWER_IC"); break;
+ case NFA_NLOWER_IC:
+ STRCPY(code, "NFA_NLOWER_IC"); break;
+ case NFA_UPPER_IC:
+ STRCPY(code, "NFA_UPPER_IC"); break;
+ case NFA_NUPPER_IC:
+ STRCPY(code, "NFA_NUPPER_IC"); break;
default:
STRCPY(code, "CHAR(x)");
@@ -2481,8 +3297,8 @@ static void nfa_set_code(int c)
}
static FILE *log_fd;
-static char_u e_log_open_failed[] = N_(
- "Could not open temporary log file for writing, displaying on stderr... ");
+static char_u e_log_open_failed[] =
+ N_("Could not open temporary log file for writing, displaying on stderr... ");
/*
* Print the postfix notation of the current regexp.
@@ -2506,8 +3322,9 @@ static void nfa_postfix_dump(char_u *expr, int retval)
fprintf(f, "%s, ", code);
}
fprintf(f, "\"\nPostfix notation (int): ");
- for (p = post_start; *p && p < post_ptr; p++)
+ for (p = post_start; *p && p < post_ptr; p++) {
fprintf(f, "%d ", *p);
+ }
fprintf(f, "\n\n");
fclose(f);
}
@@ -2528,14 +3345,15 @@ static void nfa_print_state(FILE *debugf, nfa_state_T *state)
static void nfa_print_state2(FILE *debugf, nfa_state_T *state, garray_T *indent)
{
- char_u *p;
+ char_u *p;
- if (state == NULL)
+ if (state == NULL) {
return;
+ }
fprintf(debugf, "(%2d)", abs(state->id));
- /* Output indent */
+ // Output indent
p = (char_u *)indent->ga_data;
if (indent->ga_len >= 3) {
int last = indent->ga_len - 3;
@@ -2544,39 +3362,42 @@ static void nfa_print_state2(FILE *debugf, nfa_state_T *state, garray_T *indent)
STRNCPY(save, &p[last], 2);
STRNCPY(&p[last], "+-", 2);
fprintf(debugf, " %s", p);
- STRNCPY(&p[last], save, 2);
- } else
+ STRNCPY(&p[last], save, 2); // NOLINT(runtime/printf)
+ } else {
fprintf(debugf, " %s", p);
+ }
nfa_set_code(state->c);
fprintf(debugf, "%s (%d) (id=%d) val=%d\n",
- code,
- state->c,
- abs(state->id),
- state->val);
- if (state->id < 0)
+ code,
+ state->c,
+ abs(state->id),
+ state->val);
+ if (state->id < 0) {
return;
+ }
state->id = abs(state->id) * -1;
- /* grow indent for state->out */
+ // grow indent for state->out
indent->ga_len -= 1;
- if (state->out1)
+ if (state->out1) {
ga_concat(indent, (char_u *)"| ");
- else
+ } else {
ga_concat(indent, (char_u *)" ");
+ }
ga_append(indent, NUL);
nfa_print_state2(debugf, state->out, indent);
- /* replace last part of indent for state->out1 */
+ // replace last part of indent for state->out1
indent->ga_len -= 3;
ga_concat(indent, (char_u *)" ");
ga_append(indent, NUL);
nfa_print_state2(debugf, state->out1, indent);
- /* shrink indent */
+ // shrink indent
indent->ga_len -= 3;
ga_append(indent, NUL);
}
@@ -2591,13 +3412,16 @@ static void nfa_dump(nfa_regprog_T *prog)
if (debugf != NULL) {
nfa_print_state(debugf, prog->start);
- if (prog->reganch)
+ if (prog->reganch) {
fprintf(debugf, "reganch: %d\n", prog->reganch);
- if (prog->regstart != NUL)
+ }
+ if (prog->regstart != NUL) {
fprintf(debugf, "regstart: %c (decimal: %d)\n",
- prog->regstart, prog->regstart);
- if (prog->match_text != NULL)
+ prog->regstart, prog->regstart);
+ }
+ if (prog->match_text != NULL) {
fprintf(debugf, "match_text: \"%s\"\n", prog->match_text);
+ }
fclose(debugf);
}
@@ -2610,13 +3434,14 @@ static void nfa_dump(nfa_regprog_T *prog)
*/
static int *re2post(void)
{
- if (nfa_reg(REG_NOPAREN) == FAIL)
+ if (nfa_reg(REG_NOPAREN) == FAIL) {
return NULL;
+ }
EMIT(NFA_MOPEN);
return post_start;
}
-/* NB. Some of the code below is inspired by Russ's. */
+// NB. Some of the code below is inspired by Russ's.
/*
* Represents an NFA state plus zero or one or two arrows exiting.
@@ -2625,7 +3450,7 @@ static int *re2post(void)
* If c < 256, labeled arrow with character c to out.
*/
-static nfa_state_T *state_ptr; /* points to nfa_prog->state */
+static nfa_state_T *state_ptr; // points to nfa_prog->state
/*
* Allocate and initialize nfa_state_T.
@@ -2634,8 +3459,9 @@ static nfa_state_T *alloc_state(int c, nfa_state_T *out, nfa_state_T *out1)
{
nfa_state_T *s;
- if (istate >= nstate)
+ if (istate >= nstate) {
return NULL;
+ }
s = &state_ptr[istate++];
@@ -2658,7 +3484,6 @@ static nfa_state_T *alloc_state(int c, nfa_state_T *out, nfa_state_T *out1)
* next state for this fragment.
*/
-
/*
* Initialize a Frag_T struct and return it.
*/
@@ -2696,7 +3521,6 @@ static void patch(Ptrlist *l, nfa_state_T *s)
}
}
-
/*
* Join the two lists l1 and l2, returning the combination.
*/
@@ -2705,8 +3529,9 @@ static Ptrlist *append(Ptrlist *l1, Ptrlist *l2)
Ptrlist *oldl1;
oldl1 = l1;
- while (l1->next)
+ while (l1->next) {
l1 = l1->next;
+ }
l1->next = l2;
return oldl1;
}
@@ -2725,11 +3550,11 @@ static void st_error(int *postfix, int *end, int *p)
df = fopen(NFA_REGEXP_ERROR_LOG, "a");
if (df) {
fprintf(df, "Error popping the stack!\n");
-#ifdef REGEXP_DEBUG
+# ifdef REGEXP_DEBUG
fprintf(df, "Current regexp is \"%s\"\n", nfa_regengine.expr);
-#endif
+# endif
fprintf(df, "Postfix form is: ");
-#ifdef REGEXP_DEBUG
+# ifdef REGEXP_DEBUG
for (p2 = postfix; p2 < end; p2++) {
nfa_set_code(*p2);
fprintf(df, "%s, ", code);
@@ -2740,7 +3565,7 @@ static void st_error(int *postfix, int *end, int *p)
nfa_set_code(*p2);
fprintf(df, "%s, ", code);
}
-#else
+# else
for (p2 = postfix; p2 < end; p2++) {
fprintf(df, "%d, ", *p2);
}
@@ -2748,7 +3573,7 @@ static void st_error(int *postfix, int *end, int *p)
for (p2 = postfix; p2 <= p; p2++) {
fprintf(df, "%d, ", *p2);
}
-#endif
+# endif
fprintf(df, "\n--------------------------\n");
fclose(df);
}
@@ -2763,8 +3588,9 @@ static void st_push(Frag_T s, Frag_T **p, Frag_T *stack_end)
{
Frag_T *stackp = *p;
- if (stackp >= stack_end)
+ if (stackp >= stack_end) {
return;
+ }
*stackp = s;
*p = *p + 1;
}
@@ -2778,8 +3604,9 @@ static Frag_T st_pop(Frag_T **p, Frag_T *stack)
*p = *p - 1;
stackp = *p;
- if (stackp < stack)
+ if (stackp < stack) {
return empty;
+ }
return **p;
}
@@ -2790,26 +3617,28 @@ static Frag_T st_pop(Frag_T **p, Frag_T *stack)
static int nfa_max_width(nfa_state_T *startstate, int depth)
{
int l, r;
- nfa_state_T *state = startstate;
+ nfa_state_T *state = startstate;
int len = 0;
- /* detect looping in a NFA_SPLIT */
- if (depth > 4)
+ // detect looping in a NFA_SPLIT
+ if (depth > 4) {
return -1;
+ }
while (state != NULL) {
switch (state->c) {
case NFA_END_INVISIBLE:
case NFA_END_INVISIBLE_NEG:
- /* the end, return what we have */
+ // the end, return what we have
return len;
case NFA_SPLIT:
- /* two alternatives, use the maximum */
+ // two alternatives, use the maximum
l = nfa_max_width(state->out, depth + 1);
r = nfa_max_width(state->out1, depth + 1);
- if (l < 0 || r < 0)
+ if (l < 0 || r < 0) {
return -1;
+ }
return len + (l > r ? l : r);
case NFA_ANY:
@@ -2828,8 +3657,8 @@ static int nfa_max_width(nfa_state_T *startstate, int depth)
case NFA_WHITE:
case NFA_HEX:
case NFA_OCTAL:
- /* ascii */
- ++len;
+ // ascii
+ len++;
break;
case NFA_IDENT:
@@ -2867,7 +3696,7 @@ static int nfa_max_width(nfa_state_T *startstate, int depth)
case NFA_START_INVISIBLE_NEG:
case NFA_START_INVISIBLE_BEFORE:
case NFA_START_INVISIBLE_BEFORE_NEG:
- /* zero-width, out1 points to the END state */
+ // zero-width, out1 points to the END state
state = state->out1->out;
continue;
@@ -2891,7 +3720,7 @@ static int nfa_max_width(nfa_state_T *startstate, int depth)
case NFA_ZREF9:
case NFA_NEWL:
case NFA_SKIP:
- /* unknown width */
+ // unknown width
return -1;
case NFA_BOL:
@@ -2966,23 +3795,24 @@ static int nfa_max_width(nfa_state_T *startstate, int depth)
case NFA_END_PATTERN:
case NFA_COMPOSING:
case NFA_END_COMPOSING:
- /* zero-width */
+ // zero-width
break;
default:
- if (state->c < 0)
- /* don't know what this is */
+ if (state->c < 0) {
+ // don't know what this is
return -1;
+ }
// normal character
len += utf_char2len(state->c);
break;
}
- /* normal way to continue */
+ // normal way to continue
state = state->out;
}
- /* unrecognized, "cannot happen" */
+ // unrecognized, "cannot happen"
return -1;
}
@@ -2992,12 +3822,12 @@ static int nfa_max_width(nfa_state_T *startstate, int depth)
*/
static nfa_state_T *post2nfa(int *postfix, int *end, int nfa_calc_size)
{
- int *p;
+ int *p;
int mopen;
int mclose;
- Frag_T *stack = NULL;
- Frag_T *stackp = NULL;
- Frag_T *stack_end = NULL;
+ Frag_T *stack = NULL;
+ Frag_T *stackp = NULL;
+ Frag_T *stack_end = NULL;
Frag_T e1;
Frag_T e2;
Frag_T e;
@@ -3006,8 +3836,9 @@ static nfa_state_T *post2nfa(int *postfix, int *end, int nfa_calc_size)
nfa_state_T *matchstate;
nfa_state_T *ret = NULL;
- if (postfix == NULL)
+ if (postfix == NULL) {
return NULL;
+ }
#define PUSH(s) st_push((s), &stackp, stack_end)
#define POP() st_pop(&stackp, stack); \
@@ -3050,8 +3881,9 @@ static nfa_state_T *post2nfa(int *postfix, int *end, int nfa_calc_size)
e2 = POP();
e1 = POP();
s = alloc_state(NFA_SPLIT, e1.start, e2.start);
- if (s == NULL)
+ if (s == NULL) {
goto theend;
+ }
PUSH(frag(s, append(e1.out, e2.out)));
break;
@@ -3063,8 +3895,9 @@ static nfa_state_T *post2nfa(int *postfix, int *end, int nfa_calc_size)
}
e = POP();
s = alloc_state(NFA_SPLIT, e.start, NULL);
- if (s == NULL)
+ if (s == NULL) {
goto theend;
+ }
patch(e.out, s);
PUSH(frag(s, list1(&s->out1)));
break;
@@ -3077,8 +3910,9 @@ static nfa_state_T *post2nfa(int *postfix, int *end, int nfa_calc_size)
}
e = POP();
s = alloc_state(NFA_SPLIT, NULL, e.start);
- if (s == NULL)
+ if (s == NULL) {
goto theend;
+ }
patch(e.out, s);
PUSH(frag(s, list1(&s->out)));
break;
@@ -3091,8 +3925,9 @@ static nfa_state_T *post2nfa(int *postfix, int *end, int nfa_calc_size)
}
e = POP();
s = alloc_state(NFA_SPLIT, e.start, NULL);
- if (s == NULL)
+ if (s == NULL) {
goto theend;
+ }
PUSH(frag(s, append(e.out, list1(&s->out1))));
break;
@@ -3104,8 +3939,9 @@ static nfa_state_T *post2nfa(int *postfix, int *end, int nfa_calc_size)
}
e = POP();
s = alloc_state(NFA_SPLIT, NULL, e.start);
- if (s == NULL)
+ if (s == NULL) {
goto theend;
+ }
PUSH(frag(s, append(e.out, list1(&s->out))));
break;
@@ -3120,8 +3956,9 @@ static nfa_state_T *post2nfa(int *postfix, int *end, int nfa_calc_size)
}
e = POP();
s = alloc_state(NFA_END_COLL, NULL, NULL);
- if (s == NULL)
+ if (s == NULL) {
goto theend;
+ }
patch(e.out, s);
e.start->out1 = s;
PUSH(frag(e.start, list1(&s->out)));
@@ -3151,13 +3988,13 @@ static nfa_state_T *post2nfa(int *postfix, int *end, int nfa_calc_size)
break;
}
s = alloc_state(NFA_EMPTY, NULL, NULL);
- if (s == NULL)
+ if (s == NULL) {
goto theend;
+ }
PUSH(frag(s, list1(&s->out)));
break;
- case NFA_OPT_CHARS:
- {
+ case NFA_OPT_CHARS: {
int n;
// \%[abc] implemented as:
@@ -3176,16 +4013,18 @@ static nfa_state_T *post2nfa(int *postfix, int *end, int nfa_calc_size)
nstate += n;
break;
}
- s = NULL; /* avoid compiler warning */
- e1.out = NULL; /* stores list with out1's */
- s1 = NULL; /* previous NFA_SPLIT to connect to */
+ s = NULL; // avoid compiler warning
+ e1.out = NULL; // stores list with out1's
+ s1 = NULL; // previous NFA_SPLIT to connect to
while (n-- > 0) {
- e = POP(); /* get character */
+ e = POP(); // get character
s = alloc_state(NFA_SPLIT, e.start, NULL);
- if (s == NULL)
+ if (s == NULL) {
goto theend;
- if (e1.out == NULL)
+ }
+ if (e1.out == NULL) {
e1 = e;
+ }
patch(e.out, s1);
append(e1.out, list1(&s->out1));
s1 = s;
@@ -3198,8 +4037,7 @@ static nfa_state_T *post2nfa(int *postfix, int *end, int nfa_calc_size)
case NFA_PREV_ATOM_NO_WIDTH_NEG:
case NFA_PREV_ATOM_JUST_BEFORE:
case NFA_PREV_ATOM_JUST_BEFORE_NEG:
- case NFA_PREV_ATOM_LIKE_PATTERN:
- {
+ case NFA_PREV_ATOM_LIKE_PATTERN: {
int before = (*p == NFA_PREV_ATOM_JUST_BEFORE
|| *p == NFA_PREV_ATOM_JUST_BEFORE_NEG);
int pattern = (*p == NFA_PREV_ATOM_LIKE_PATTERN);
@@ -3226,15 +4064,15 @@ static nfa_state_T *post2nfa(int *postfix, int *end, int nfa_calc_size)
start_state = NFA_START_INVISIBLE_BEFORE_NEG;
end_state = NFA_END_INVISIBLE_NEG;
break;
- default: /* NFA_PREV_ATOM_LIKE_PATTERN: */
+ default: // NFA_PREV_ATOM_LIKE_PATTERN:
start_state = NFA_START_PATTERN;
end_state = NFA_END_PATTERN;
break;
}
- if (before)
- n = *++p; /* get the count */
-
+ if (before) {
+ n = *++p; // get the count
+ }
// The \@= operator: match the preceding atom with zero width.
// The \@! operator: no match for the preceding atom.
// The \@<= operator: match for the preceding atom.
@@ -3248,14 +4086,16 @@ static nfa_state_T *post2nfa(int *postfix, int *end, int nfa_calc_size)
}
e = POP();
s1 = alloc_state(end_state, NULL, NULL);
- if (s1 == NULL)
+ if (s1 == NULL) {
goto theend;
+ }
s = alloc_state(start_state, e.start, s1);
- if (s == NULL)
+ if (s == NULL) {
goto theend;
+ }
if (pattern) {
- /* NFA_ZEND -> NFA_END_PATTERN -> NFA_SKIP -> what follows. */
+ // NFA_ZEND -> NFA_END_PATTERN -> NFA_SKIP -> what follows.
skip = alloc_state(NFA_SKIP, NULL, NULL);
if (skip == NULL) {
goto theend;
@@ -3285,7 +4125,7 @@ static nfa_state_T *post2nfa(int *postfix, int *end, int nfa_calc_size)
case NFA_COMPOSING: // char with composing char
FALLTHROUGH;
- case NFA_MOPEN: /* \( \) Submatch */
+ case NFA_MOPEN: // \( \) Submatch
case NFA_MOPEN1:
case NFA_MOPEN2:
case NFA_MOPEN3:
@@ -3295,7 +4135,7 @@ static nfa_state_T *post2nfa(int *postfix, int *end, int nfa_calc_size)
case NFA_MOPEN7:
case NFA_MOPEN8:
case NFA_MOPEN9:
- case NFA_ZOPEN: /* \z( \) Submatch */
+ case NFA_ZOPEN: // \z( \) Submatch
case NFA_ZOPEN1:
case NFA_ZOPEN2:
case NFA_ZOPEN3:
@@ -3313,20 +4153,32 @@ static nfa_state_T *post2nfa(int *postfix, int *end, int nfa_calc_size)
mopen = *p;
switch (*p) {
- case NFA_NOPEN: mclose = NFA_NCLOSE; break;
- case NFA_ZOPEN: mclose = NFA_ZCLOSE; break;
- case NFA_ZOPEN1: mclose = NFA_ZCLOSE1; break;
- case NFA_ZOPEN2: mclose = NFA_ZCLOSE2; break;
- case NFA_ZOPEN3: mclose = NFA_ZCLOSE3; break;
- case NFA_ZOPEN4: mclose = NFA_ZCLOSE4; break;
- case NFA_ZOPEN5: mclose = NFA_ZCLOSE5; break;
- case NFA_ZOPEN6: mclose = NFA_ZCLOSE6; break;
- case NFA_ZOPEN7: mclose = NFA_ZCLOSE7; break;
- case NFA_ZOPEN8: mclose = NFA_ZCLOSE8; break;
- case NFA_ZOPEN9: mclose = NFA_ZCLOSE9; break;
- case NFA_COMPOSING: mclose = NFA_END_COMPOSING; break;
+ case NFA_NOPEN:
+ mclose = NFA_NCLOSE; break;
+ case NFA_ZOPEN:
+ mclose = NFA_ZCLOSE; break;
+ case NFA_ZOPEN1:
+ mclose = NFA_ZCLOSE1; break;
+ case NFA_ZOPEN2:
+ mclose = NFA_ZCLOSE2; break;
+ case NFA_ZOPEN3:
+ mclose = NFA_ZCLOSE3; break;
+ case NFA_ZOPEN4:
+ mclose = NFA_ZCLOSE4; break;
+ case NFA_ZOPEN5:
+ mclose = NFA_ZCLOSE5; break;
+ case NFA_ZOPEN6:
+ mclose = NFA_ZCLOSE6; break;
+ case NFA_ZOPEN7:
+ mclose = NFA_ZCLOSE7; break;
+ case NFA_ZOPEN8:
+ mclose = NFA_ZCLOSE8; break;
+ case NFA_ZOPEN9:
+ mclose = NFA_ZCLOSE9; break;
+ case NFA_COMPOSING:
+ mclose = NFA_END_COMPOSING; break;
default:
- /* NFA_MOPEN, NFA_MOPEN1 .. NFA_MOPEN9 */
+ // NFA_MOPEN, NFA_MOPEN1 .. NFA_MOPEN9
mclose = *p + NSUBEXP;
break;
}
@@ -3337,11 +4189,13 @@ static nfa_state_T *post2nfa(int *postfix, int *end, int nfa_calc_size)
// empty groups of parenthesis, and empty mbyte chars
if (stackp == stack) {
s = alloc_state(mopen, NULL, NULL);
- if (s == NULL)
+ if (s == NULL) {
goto theend;
+ }
s1 = alloc_state(mclose, NULL, NULL);
- if (s1 == NULL)
+ if (s1 == NULL) {
goto theend;
+ }
patch(list1(&s->out), s1);
PUSH(frag(s, list1(&s1->out)));
break;
@@ -3350,18 +4204,21 @@ static nfa_state_T *post2nfa(int *postfix, int *end, int nfa_calc_size)
// At least one node was emitted before NFA_MOPEN, so
// at least one node will be between NFA_MOPEN and NFA_MCLOSE
e = POP();
- s = alloc_state(mopen, e.start, NULL); /* `(' */
- if (s == NULL)
+ s = alloc_state(mopen, e.start, NULL); // `('
+ if (s == NULL) {
goto theend;
+ }
- s1 = alloc_state(mclose, NULL, NULL); /* `)' */
- if (s1 == NULL)
+ s1 = alloc_state(mclose, NULL, NULL); // `)'
+ if (s1 == NULL) {
goto theend;
+ }
patch(e.out, s1);
- if (mopen == NFA_COMPOSING)
- /* COMPOSING->out1 = END_COMPOSING */
+ if (mopen == NFA_COMPOSING) {
+ // COMPOSING->out1 = END_COMPOSING
patch(list1(&s->out1), s1);
+ }
PUSH(frag(s, list1(&s1->out)));
break;
@@ -3389,11 +4246,13 @@ static nfa_state_T *post2nfa(int *postfix, int *end, int nfa_calc_size)
break;
}
s = alloc_state(*p, NULL, NULL);
- if (s == NULL)
+ if (s == NULL) {
goto theend;
+ }
s1 = alloc_state(NFA_SKIP, NULL, NULL);
- if (s1 == NULL)
+ if (s1 == NULL) {
goto theend;
+ }
patch(list1(&s->out), s1);
PUSH(frag(s, list1(&s1->out)));
break;
@@ -3409,17 +4268,17 @@ static nfa_state_T *post2nfa(int *postfix, int *end, int nfa_calc_size)
case NFA_COL_LT:
case NFA_MARK:
case NFA_MARK_GT:
- case NFA_MARK_LT:
- {
- int n = *++p; /* lnum, col or mark name */
+ case NFA_MARK_LT: {
+ int n = *++p; // lnum, col or mark name
if (nfa_calc_size == true) {
nstate += 1;
break;
}
s = alloc_state(p[-1], NULL, NULL);
- if (s == NULL)
+ if (s == NULL) {
goto theend;
+ }
s->val = n;
PUSH(frag(s, list1(&s->out)));
break;
@@ -3434,18 +4293,17 @@ static nfa_state_T *post2nfa(int *postfix, int *end, int nfa_calc_size)
break;
}
s = alloc_state(*p, NULL, NULL);
- if (s == NULL)
+ if (s == NULL) {
goto theend;
+ }
PUSH(frag(s, list1(&s->out)));
break;
-
- } /* switch(*p) */
-
- } /* for(p = postfix; *p; ++p) */
+ } // switch(*p)
+ } // for(p = postfix; *p; ++p)
if (nfa_calc_size == true) {
nstate++;
- goto theend; /* Return value when counting size is ignored anyway */
+ goto theend; // Return value when counting size is ignored anyway
}
e = POP();
@@ -3461,7 +4319,7 @@ static nfa_state_T *post2nfa(int *postfix, int *end, int nfa_calc_size)
"Not enough space to store the whole NFA "));
}
- matchstate = &state_ptr[istate++]; /* the match state */
+ matchstate = &state_ptr[istate++]; // the match state
matchstate->c = NFA_MATCH;
matchstate->out = matchstate->out1 = NULL;
matchstate->id = 0;
@@ -3524,9 +4382,10 @@ static void nfa_postprocess(nfa_regprog_T *prog)
directly = ch_follows < ch_invisible;
}
}
- if (directly)
- /* switch to the _FIRST state */
- ++prog->state[i].c;
+ if (directly) {
+ // switch to the _FIRST state
+ prog->state[i].c++;
+ }
}
}
}
@@ -3535,12 +4394,11 @@ static void nfa_postprocess(nfa_regprog_T *prog)
// NFA execution code.
/////////////////////////////////////////////////////////////////
-/* Values for done in nfa_pim_T. */
-#define NFA_PIM_UNUSED 0 /* pim not used */
-#define NFA_PIM_TODO 1 /* pim not done yet */
-#define NFA_PIM_MATCH 2 /* pim executed, matches */
-#define NFA_PIM_NOMATCH 3 /* pim executed, no match */
-
+// Values for done in nfa_pim_T.
+#define NFA_PIM_UNUSED 0 // pim not used
+#define NFA_PIM_TODO 1 // pim not done yet
+#define NFA_PIM_MATCH 2 // pim executed, matches
+#define NFA_PIM_NOMATCH 3 // pim executed, no match
#ifdef REGEXP_DEBUG
static void log_subsexpr(regsubs_T *subs)
@@ -3555,23 +4413,24 @@ static void log_subexpr(regsub_T *sub)
{
int j;
- for (j = 0; j < sub->in_use; j++)
- if (REG_MULTI)
+ for (j = 0; j < sub->in_use; j++) {
+ if (REG_MULTI) {
fprintf(log_fd, "*** group %d, start: c=%d, l=%d, end: c=%d, l=%d\n",
- j,
- sub->list.multi[j].start_col,
- (int)sub->list.multi[j].start_lnum,
- sub->list.multi[j].end_col,
- (int)sub->list.multi[j].end_lnum);
- else {
+ j,
+ sub->list.multi[j].start_col,
+ (int)sub->list.multi[j].start_lnum,
+ sub->list.multi[j].end_col,
+ (int)sub->list.multi[j].end_lnum);
+ } else {
char *s = (char *)sub->list.line[j].start;
char *e = (char *)sub->list.line[j].end;
fprintf(log_fd, "*** group %d, start: \"%s\", end: \"%s\"\n",
- j,
- s == NULL ? "NULL" : s,
- e == NULL ? "NULL" : e);
+ j,
+ s == NULL ? "NULL" : s,
+ e == NULL ? "NULL" : e);
}
+ }
}
static char *pim_info(const nfa_pim_T *pim)
@@ -3628,15 +4487,16 @@ static void copy_sub(regsub_T *to, regsub_T *from)
{
to->in_use = from->in_use;
if (from->in_use > 0) {
- /* Copy the match start and end positions. */
- if (REG_MULTI)
+ // Copy the match start and end positions.
+ if (REG_MULTI) {
memmove(&to->list.multi[0],
- &from->list.multi[0],
- sizeof(struct multipos) * from->in_use);
- else
+ &from->list.multi[0],
+ sizeof(struct multipos) * from->in_use);
+ } else {
memmove(&to->list.line[0],
- &from->list.line[0],
- sizeof(struct linepos) * from->in_use);
+ &from->list.line[0],
+ sizeof(struct linepos) * from->in_use);
+ }
}
}
@@ -3645,18 +4505,20 @@ static void copy_sub(regsub_T *to, regsub_T *from)
*/
static void copy_sub_off(regsub_T *to, regsub_T *from)
{
- if (to->in_use < from->in_use)
+ if (to->in_use < from->in_use) {
to->in_use = from->in_use;
+ }
if (from->in_use > 1) {
- /* Copy the match start and end positions. */
- if (REG_MULTI)
+ // Copy the match start and end positions.
+ if (REG_MULTI) {
memmove(&to->list.multi[1],
- &from->list.multi[1],
- sizeof(struct multipos) * (from->in_use - 1));
- else
+ &from->list.multi[1],
+ sizeof(struct multipos) * (from->in_use - 1));
+ } else {
memmove(&to->list.line[1],
- &from->list.line[1],
- sizeof(struct linepos) * (from->in_use - 1));
+ &from->list.line[1],
+ sizeof(struct linepos) * (from->in_use - 1));
+ }
}
}
@@ -3667,13 +4529,14 @@ static void copy_ze_off(regsub_T *to, regsub_T *from)
{
if (rex.nfa_has_zend) {
if (REG_MULTI) {
- if (from->list.multi[0].end_lnum >= 0){
+ if (from->list.multi[0].end_lnum >= 0) {
to->list.multi[0].end_lnum = from->list.multi[0].end_lnum;
to->list.multi[0].end_col = from->list.multi[0].end_col;
}
} else {
- if (from->list.line[0].end != NULL)
+ if (from->list.line[0].end != NULL) {
to->list.line[0].end = from->list.line[0].end;
+ }
}
}
}
@@ -3686,8 +4549,8 @@ static bool sub_equal(regsub_T *sub1, regsub_T *sub2)
int todo;
linenr_T s1;
linenr_T s2;
- char_u *sp1;
- char_u *sp2;
+ char_u *sp1;
+ char_u *sp2;
todo = sub1->in_use > sub2->in_use ? sub1->in_use : sub2->in_use;
if (REG_MULTI) {
@@ -3766,11 +4629,8 @@ static bool sub_equal(regsub_T *sub1, regsub_T *sub2)
}
#ifdef REGEXP_DEBUG
-static void report_state(char *action,
- regsub_T *sub,
- nfa_state_T *state,
- int lid,
- nfa_pim_T *pim) {
+static void report_state(char *action, regsub_T *sub, nfa_state_T *state, int lid, nfa_pim_T *pim)
+{
int col;
if (sub->in_use <= 0) {
@@ -3788,14 +4648,14 @@ static void report_state(char *action,
#endif
-// Return true if the same state is already in list "l" with the same
-// positions as "subs".
-static bool has_state_with_pos(
- nfa_list_T *l, // runtime state list
- nfa_state_T *state, // state to update
- regsubs_T *subs, // pointers to subexpressions
- nfa_pim_T *pim // postponed match or NULL
-)
+/// @param l runtime state list
+/// @param state state to update
+/// @param subs pointers to subexpressions
+/// @param pim postponed match or NULL
+///
+/// @return true if the same state is already in list "l" with the same
+/// positions as "subs".
+static bool has_state_with_pos(nfa_list_T *l, nfa_state_T *state, regsubs_T *subs, nfa_pim_T *pim)
FUNC_ATTR_NONNULL_ARG(1, 2, 3)
{
for (int i = 0; i < l->n; i++) {
@@ -3870,7 +4730,7 @@ static bool match_follows(const nfa_state_T *startstate, int depth)
case NFA_START_INVISIBLE_BEFORE_NEG:
case NFA_START_INVISIBLE_BEFORE_NEG_FIRST:
case NFA_COMPOSING:
- /* skip ahead to next state */
+ // skip ahead to next state
state = state->out1->out;
continue;
@@ -3926,13 +4786,12 @@ static bool match_follows(const nfa_state_T *startstate, int depth)
return false;
}
-
-// Return true if "state" is already in list "l".
-static bool state_in_list(
- nfa_list_T *l, // runtime state list
- nfa_state_T *state, // state to update
- regsubs_T *subs // pointers to subexpressions
-)
+/// @param l runtime state list
+/// @param state state to update
+/// @param subs pointers to subexpressions
+///
+/// @return true if "state" is already in list "l".
+static bool state_in_list(nfa_list_T *l, nfa_state_T *state, regsubs_T *subs)
FUNC_ATTR_NONNULL_ALL
{
if (state->lastlist[nfa_ll_index] == l->id) {
@@ -3946,15 +4805,18 @@ static bool state_in_list(
// Offset used for "off" by addstate_here().
#define ADDSTATE_HERE_OFFSET 10
-// Add "state" and possibly what follows to state list ".".
-// Returns "subs_arg", possibly copied into temp_subs.
-// Returns NULL when recursiveness is too deep.
-static regsubs_T *addstate(
- nfa_list_T *l, // runtime state list
- nfa_state_T *state, // state to update
- regsubs_T *subs_arg, // pointers to subexpressions
- nfa_pim_T *pim, // postponed look-behind match
- int off_arg) // byte offset, when -1 go to next line
+/// Add "state" and possibly what follows to state list ".".
+///
+/// @param l runtime state list
+/// @param state state to update
+/// @param subs_arg pointers to subexpressions
+/// @param pim postponed look-behind match
+/// @param off_arg byte offset, when -1 go to next line
+///
+/// @return "subs_arg", possibly copied into temp_subs.
+/// NULL when recursiveness is too deep.
+static regsubs_T *addstate(nfa_list_T *l, nfa_state_T *state, regsubs_T *subs_arg, nfa_pim_T *pim,
+ int off_arg)
FUNC_ATTR_NONNULL_ARG(1, 2) FUNC_ATTR_WARN_UNUSED_RESULT
{
int subidx;
@@ -3963,13 +4825,13 @@ static regsubs_T *addstate(
int listindex = 0;
int k;
int found = false;
- nfa_thread_T *thread;
- struct multipos save_multipos;
+ nfa_thread_T *thread;
+ struct multipos save_multipos;
int save_in_use;
- char_u *save_ptr;
+ char_u *save_ptr;
int i;
- regsub_T *sub;
- regsubs_T *subs = subs_arg;
+ regsub_T *sub;
+ regsubs_T *subs = subs_arg;
static regsubs_T temp_subs;
#ifdef REGEXP_DEBUG
int did_print = false;
@@ -4081,12 +4943,13 @@ static regsubs_T *addstate(
skip_add:
#ifdef REGEXP_DEBUG
nfa_set_code(state->c);
- fprintf(log_fd, "> Not adding state %d to list %d. char %d: %s pim: %s has_pim: %d found: %d\n",
+ fprintf(log_fd,
+ "> Not adding state %d to list %d. char %d: %s pim: %s has_pim: %d found: %d\n",
abs(state->id), l->id, state->c, code,
pim == NULL ? "NULL" : "yes", l->has_pim, found);
#endif
- depth--;
- return subs;
+ depth--;
+ return subs;
}
}
@@ -4123,13 +4986,13 @@ skip_add:
l->len = newlen;
}
- /* add the state to the list */
+ // add the state to the list
state->lastlist[nfa_ll_index] = l->id;
thread = &l->t[l->n++];
thread->state = state;
- if (pim == NULL)
+ if (pim == NULL) {
thread->pim.result = NFA_PIM_UNUSED;
- else {
+ } else {
copy_pim(&thread->pim, pim);
l->has_pim = true;
}
@@ -4144,15 +5007,16 @@ skip_add:
}
#ifdef REGEXP_DEBUG
- if (!did_print)
+ if (!did_print) {
report_state("Processing", &subs->norm, state, l->id, pim);
+ }
#endif
switch (state->c) {
case NFA_MATCH:
break;
case NFA_SPLIT:
- /* order matters here */
+ // order matters here
subs = addstate(l, state->out, subs, pim, off_arg);
subs = addstate(l, state->out1, subs, pim, off_arg);
break;
@@ -4195,7 +5059,7 @@ skip_add:
sub = &subs->norm;
}
- /* avoid compiler warnings */
+ // avoid compiler warnings
save_ptr = NULL;
memset(&save_multipos, 0, sizeof(save_multipos));
@@ -4251,11 +5115,12 @@ skip_add:
if (save_in_use == -1) {
if (REG_MULTI) {
sub->list.multi[subidx] = save_multipos;
- }
- else
+ } else {
sub->list.line[subidx].start = save_ptr;
- } else
+ }
+ } else {
sub->in_use = save_in_use;
+ }
break;
case NFA_MCLOSE:
@@ -4302,8 +5167,9 @@ skip_add:
// We don't fill in gaps here, there must have been an MOPEN that
// has done that.
save_in_use = sub->in_use;
- if (sub->in_use <= subidx)
+ if (sub->in_use <= subidx) {
sub->in_use = subidx + 1;
+ }
if (REG_MULTI) {
save_multipos = sub->list.multi[subidx];
if (off == -1) {
@@ -4314,7 +5180,7 @@ skip_add:
sub->list.multi[subidx].end_col =
(colnr_T)(rex.input - rex.line + off);
}
- /* avoid compiler warnings */
+ // avoid compiler warnings
save_ptr = NULL;
} else {
save_ptr = sub->list.line[subidx].end;
@@ -4336,9 +5202,9 @@ skip_add:
if (REG_MULTI) {
sub->list.multi[subidx] = save_multipos;
- }
- else
+ } else {
sub->list.line[subidx].end = save_ptr;
+ }
sub->in_use = save_in_use;
break;
}
@@ -4346,19 +5212,17 @@ skip_add:
return subs;
}
-/*
- * Like addstate(), but the new state(s) are put at position "*ip".
- * Used for zero-width matches, next state to use is the added one.
- * This makes sure the order of states to be tried does not change, which
- * matters for alternatives.
- */
-static regsubs_T *addstate_here(
- nfa_list_T *l, // runtime state list
- nfa_state_T *state, // state to update
- regsubs_T *subs, // pointers to subexpressions
- nfa_pim_T *pim, // postponed look-behind match
- int *ip
-)
+/// Like addstate(), but the new state(s) are put at position "*ip".
+/// Used for zero-width matches, next state to use is the added one.
+/// This makes sure the order of states to be tried does not change, which
+/// matters for alternatives.
+///
+/// @param l runtime state list
+/// @param state state to update
+/// @param subs pointers to subexpressions
+/// @param pim postponed look-behind match
+static regsubs_T *addstate_here(nfa_list_T *l, nfa_state_T *state, regsubs_T *subs, nfa_pim_T *pim,
+ int *ip)
FUNC_ATTR_NONNULL_ARG(1, 2, 5) FUNC_ATTR_WARN_UNUSED_RESULT
{
int tlen = l->n;
@@ -4400,25 +5264,25 @@ static regsubs_T *addstate_here(
nfa_thread_T *const newl = xmalloc(newsize);
l->len = newlen;
memmove(&(newl[0]),
- &(l->t[0]),
- sizeof(nfa_thread_T) * listidx);
+ &(l->t[0]),
+ sizeof(nfa_thread_T) * listidx);
memmove(&(newl[listidx]),
- &(l->t[l->n - count]),
- sizeof(nfa_thread_T) * count);
+ &(l->t[l->n - count]),
+ sizeof(nfa_thread_T) * count);
memmove(&(newl[listidx + count]),
- &(l->t[listidx + 1]),
- sizeof(nfa_thread_T) * (l->n - count - listidx - 1));
+ &(l->t[listidx + 1]),
+ sizeof(nfa_thread_T) * (l->n - count - listidx - 1));
xfree(l->t);
l->t = newl;
} else {
// make space for new states, then move them from the
// end to the current position
memmove(&(l->t[listidx + count]),
- &(l->t[listidx + 1]),
- sizeof(nfa_thread_T) * (l->n - listidx - 1));
+ &(l->t[listidx + 1]),
+ sizeof(nfa_thread_T) * (l->n - listidx - 1));
memmove(&(l->t[listidx]),
- &(l->t[l->n - 1]),
- sizeof(nfa_thread_T) * count);
+ &(l->t[l->n - 1]),
+ sizeof(nfa_thread_T) * count);
}
}
--l->n;
@@ -4444,8 +5308,9 @@ static int check_char_class(int class, int c)
}
break;
case NFA_CLASS_BLANK:
- if (c == ' ' || c == '\t')
+ if (c == ' ' || c == '\t') {
return OK;
+ }
break;
case NFA_CLASS_CNTRL:
if (c >= 1 && c <= 127 && iscntrl(c)) {
@@ -4453,8 +5318,9 @@ static int check_char_class(int class, int c)
}
break;
case NFA_CLASS_DIGIT:
- if (ascii_isdigit(c))
+ if (ascii_isdigit(c)) {
return OK;
+ }
break;
case NFA_CLASS_GRAPH:
if (c >= 1 && c <= 127 && isgraph(c)) {
@@ -4467,8 +5333,9 @@ static int check_char_class(int class, int c)
}
break;
case NFA_CLASS_PRINT:
- if (vim_isprintc(c))
+ if (vim_isprintc(c)) {
return OK;
+ }
break;
case NFA_CLASS_PUNCT:
if (c >= 1 && c < 128 && ispunct(c)) {
@@ -4476,8 +5343,9 @@ static int check_char_class(int class, int c)
}
break;
case NFA_CLASS_SPACE:
- if ((c >= 9 && c <= 13) || (c == ' '))
+ if ((c >= 9 && c <= 13) || (c == ' ')) {
return OK;
+ }
break;
case NFA_CLASS_UPPER:
if (mb_isupper(c)) {
@@ -4485,20 +5353,24 @@ static int check_char_class(int class, int c)
}
break;
case NFA_CLASS_XDIGIT:
- if (ascii_isxdigit(c))
+ if (ascii_isxdigit(c)) {
return OK;
+ }
break;
case NFA_CLASS_TAB:
- if (c == '\t')
+ if (c == '\t') {
return OK;
+ }
break;
case NFA_CLASS_RETURN:
- if (c == '\r')
+ if (c == '\r') {
return OK;
+ }
break;
case NFA_CLASS_BACKSPACE:
- if (c == '\b')
+ if (c == '\b') {
return OK;
+ }
break;
case NFA_CLASS_ESCAPE:
if (c == ESC) {
@@ -4529,30 +5401,28 @@ static int check_char_class(int class, int c)
return FAIL;
}
-/*
- * Check for a match with subexpression "subidx".
- * Return true if it matches.
- */
-static int
-match_backref (
- regsub_T *sub, /* pointers to subexpressions */
- int subidx,
- int *bytelen /* out: length of match in bytes */
-)
+/// Check for a match with subexpression "subidx".
+///
+/// @param sub pointers to subexpressions
+/// @param bytelen out: length of match in bytes
+///
+/// @return true if it matches.
+static int match_backref(regsub_T *sub, int subidx, int *bytelen)
{
int len;
if (sub->in_use <= subidx) {
retempty:
- /* backref was not set, match an empty string */
+ // backref was not set, match an empty string
*bytelen = 0;
return true;
}
if (REG_MULTI) {
if (sub->list.multi[subidx].start_lnum < 0
- || sub->list.multi[subidx].end_lnum < 0)
+ || sub->list.multi[subidx].end_lnum < 0) {
goto retempty;
+ }
if (sub->list.multi[subidx].start_lnum == rex.lnum
&& sub->list.multi[subidx].end_lnum == rex.lnum) {
len = sub->list.multi[subidx].end_col
@@ -4573,8 +5443,9 @@ retempty:
}
} else {
if (sub->list.line[subidx].start == NULL
- || sub->list.line[subidx].end == NULL)
+ || sub->list.line[subidx].end == NULL) {
goto retempty;
+ }
len = (int)(sub->list.line[subidx].end - sub->list.line[subidx].start);
if (cstrncmp(sub->list.line[subidx].start, rex.input, &len) == 0) {
*bytelen = len;
@@ -4584,23 +5455,18 @@ retempty:
return false;
}
-
-
-/*
- * Check for a match with \z subexpression "subidx".
- * Return true if it matches.
- */
-static int
-match_zref (
- int subidx,
- int *bytelen /* out: length of match in bytes */
-)
+/// Check for a match with \z subexpression "subidx".
+///
+/// @param bytelen out: length of match in bytes
+///
+/// @return true if it matches.
+static int match_zref(int subidx, int *bytelen)
{
int len;
cleanup_zsubexpr();
if (re_extmatch_in == NULL || re_extmatch_in->matches[subidx] == NULL) {
- /* backref was not set, match an empty string */
+ // backref was not set, match an empty string
*bytelen = 0;
return true;
}
@@ -4621,11 +5487,11 @@ match_zref (
static void nfa_save_listids(nfa_regprog_T *prog, int *list)
{
int i;
- nfa_state_T *p;
+ nfa_state_T *p;
- /* Order in the list is reverse, it's a bit faster that way. */
+ // Order in the list is reverse, it's a bit faster that way.
p = &prog->state[0];
- for (i = prog->nstate; --i >= 0; ) {
+ for (i = prog->nstate; --i >= 0;) {
list[i] = p->lastlist[1];
p->lastlist[1] = 0;
++p;
@@ -4638,10 +5504,10 @@ static void nfa_save_listids(nfa_regprog_T *prog, int *list)
static void nfa_restore_listids(nfa_regprog_T *prog, int *list)
{
int i;
- nfa_state_T *p;
+ nfa_state_T *p;
p = &prog->state[0];
- for (i = prog->nstate; --i >= 0; ) {
+ for (i = prog->nstate; --i >= 0;) {
p->lastlist[1] = list[i];
++p;
}
@@ -4649,20 +5515,22 @@ static void nfa_restore_listids(nfa_regprog_T *prog, int *list)
static bool nfa_re_num_cmp(uintmax_t val, int op, uintmax_t pos)
{
- if (op == 1) return pos > val;
- if (op == 2) return pos < val;
+ if (op == 1) {
+ return pos > val;
+ }
+ if (op == 2) {
+ return pos < val;
+ }
return val == pos;
}
-
/*
* Recursively call nfa_regmatch()
* "pim" is NULL or contains info about a Postponed Invisible Match (start
* position).
*/
-static int recursive_regmatch(
- nfa_state_T *state, nfa_pim_T *pim, nfa_regprog_T *prog,
- regsubs_T *submatch, regsubs_T *m, int **listids, int *listids_len)
+static int recursive_regmatch(nfa_state_T *state, nfa_pim_T *pim, nfa_regprog_T *prog,
+ regsubs_T *submatch, regsubs_T *m, int **listids, int *listids_len)
FUNC_ATTR_NONNULL_ARG(1, 3, 5, 6, 7)
{
const int save_reginput_col = (int)(rex.input - rex.line);
@@ -4671,7 +5539,7 @@ static int recursive_regmatch(
const int save_nfa_listid = rex.nfa_listid;
save_se_T *const save_nfa_endp = nfa_endp;
save_se_T endpos;
- save_se_T *endposp = NULL;
+ save_se_T *endposp = NULL;
int need_restore = false;
if (pim != NULL) {
@@ -4741,8 +5609,9 @@ static int recursive_regmatch(
}
#ifdef REGEXP_DEBUG
- if (log_fd != stderr)
+ if (log_fd != stderr) {
fclose(log_fd);
+ }
log_fd = NULL;
#endif
// Have to clear the lastlist field of the NFA nodes, so that
@@ -4808,7 +5677,6 @@ static int recursive_regmatch(
return result;
}
-
/*
* Estimate the chance of a match with "state" failing.
* empty match: 0
@@ -4820,28 +5688,30 @@ static int failure_chance(nfa_state_T *state, int depth)
int c = state->c;
int l, r;
- /* detect looping */
- if (depth > 4)
+ // detect looping
+ if (depth > 4) {
return 1;
+ }
switch (c) {
case NFA_SPLIT:
- if (state->out->c == NFA_SPLIT || state->out1->c == NFA_SPLIT)
- /* avoid recursive stuff */
+ if (state->out->c == NFA_SPLIT || state->out1->c == NFA_SPLIT) {
+ // avoid recursive stuff
return 1;
- /* two alternatives, use the lowest failure chance */
+ }
+ // two alternatives, use the lowest failure chance
l = failure_chance(state->out, depth + 1);
r = failure_chance(state->out1, depth + 1);
return l < r ? l : r;
case NFA_ANY:
- /* matches anything, unlikely to fail */
+ // matches anything, unlikely to fail
return 1;
case NFA_MATCH:
case NFA_MCLOSE:
case NFA_ANY_COMPOSING:
- /* empty match works always */
+ // empty match works always
return 0;
case NFA_START_INVISIBLE:
@@ -4853,7 +5723,7 @@ static int failure_chance(nfa_state_T *state, int depth)
case NFA_START_INVISIBLE_BEFORE_NEG:
case NFA_START_INVISIBLE_BEFORE_NEG_FIRST:
case NFA_START_PATTERN:
- /* recursive regmatch is expensive, use low failure chance */
+ // recursive regmatch is expensive, use low failure chance
return 5;
case NFA_BOL:
@@ -4928,7 +5798,7 @@ static int failure_chance(nfa_state_T *state, int depth)
case NFA_ZREF7:
case NFA_ZREF8:
case NFA_ZREF9:
- /* backreferences don't match in many places */
+ // backreferences don't match in many places
return 94;
case NFA_LNUM_GT:
@@ -4940,7 +5810,7 @@ static int failure_chance(nfa_state_T *state, int depth)
case NFA_MARK_GT:
case NFA_MARK_LT:
case NFA_VISUAL:
- /* before/after positions don't match very often */
+ // before/after positions don't match very often
return 85;
case NFA_LNUM:
@@ -4950,19 +5820,20 @@ static int failure_chance(nfa_state_T *state, int depth)
case NFA_COL:
case NFA_VCOL:
case NFA_MARK:
- /* specific positions rarely match */
+ // specific positions rarely match
return 98;
case NFA_COMPOSING:
return 95;
default:
- if (c > 0)
- /* character match fails often */
+ if (c > 0) {
+ // character match fails often
return 95;
+ }
}
- /* something else, includes character classes */
+ // something else, includes character classes
return 50;
}
@@ -4989,17 +5860,17 @@ static long find_match_text(colnr_T startcol, int regstart, char_u *match_text)
#define PTR2LEN(x) utf_ptr2len(x)
colnr_T col = startcol;
- int regstart_len = PTR2LEN(rex.line + startcol);
+ int regstart_len = PTR2LEN((char *)rex.line + startcol);
for (;;) {
bool match = true;
char_u *s1 = match_text;
char_u *s2 = rex.line + col + regstart_len; // skip regstart
while (*s1) {
- int c1_len = PTR2LEN(s1);
- int c1 = utf_ptr2char(s1);
- int c2_len = PTR2LEN(s2);
- int c2 = utf_ptr2char(s2);
+ int c1_len = PTR2LEN((char *)s1);
+ int c1 = utf_ptr2char((char *)s1);
+ int c2_len = PTR2LEN((char *)s2);
+ int c2 = utf_ptr2char((char *)s2);
if ((c1 != c2 && (!rex.reg_ic || utf_fold(c1) != utf_fold(c2)))
|| c1_len != c2_len) {
@@ -5011,7 +5882,7 @@ static long find_match_text(colnr_T startcol, int regstart, char_u *match_text)
}
if (match
// check that no composing char follows
- && !utf_iscomposing(utf_ptr2char(s2))) {
+ && !utf_iscomposing(utf_ptr2char((char *)s2))) {
cleanup_subexpr();
if (REG_MULTI) {
rex.reg_startpos[0].lnum = rex.lnum;
@@ -5058,8 +5929,7 @@ static int nfa_did_time_out(void)
/// When there is a match "submatch" contains the positions.
///
/// Note: Caller must ensure that: start != NULL.
-static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
- regsubs_T *submatch, regsubs_T *m)
+static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *submatch, regsubs_T *m)
FUNC_ATTR_NONNULL_ARG(1, 2, 4)
{
int result = false;
@@ -5068,9 +5938,9 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
nfa_thread_T *t;
nfa_list_T list[2];
int listidx;
- nfa_list_T *thislist;
- nfa_list_T *nextlist;
- int *listids = NULL;
+ nfa_list_T *thislist;
+ nfa_list_T *nextlist;
+ int *listids = NULL;
int listids_len = 0;
nfa_state_T *add_state;
bool add_here;
@@ -5078,30 +5948,24 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
int add_off = 0;
int toplevel = start->c == NFA_MOPEN;
regsubs_T *r;
-#ifdef NFA_REGEXP_DEBUG_LOG
- FILE *debug = fopen(NFA_REGEXP_DEBUG_LOG, "a");
-
- if (debug == NULL) {
- semsg("(NFA) COULD NOT OPEN %s!", NFA_REGEXP_DEBUG_LOG);
- return false;
- }
-#endif
// Some patterns may take a long time to match, especially when using
// recursive_regmatch(). Allow interrupting them with CTRL-C.
fast_breakcheck();
if (got_int) {
-#ifdef NFA_REGEXP_DEBUG_LOG
- fclose(debug);
-#endif
return false;
}
if (nfa_did_time_out()) {
-#ifdef NFA_REGEXP_DEBUG_LOG
- fclose(debug);
-#endif
return false;
}
+#ifdef NFA_REGEXP_DEBUG_LOG
+ FILE *debug = fopen(NFA_REGEXP_DEBUG_LOG, "a");
+
+ if (debug == NULL) {
+ semsg("(NFA) COULD NOT OPEN %s!", NFA_REGEXP_DEBUG_LOG);
+ return false;
+ }
+#endif
nfa_match = false;
// Allocate memory for the lists of nodes.
@@ -5117,7 +5981,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
fprintf(log_fd, "**********************************\n");
nfa_set_code(start->c);
fprintf(log_fd, " RUNNING nfa_regmatch() starting with state %d, code %s\n",
- abs(start->id), code);
+ abs(start->id), code);
fprintf(log_fd, "**********************************\n");
} else {
emsg(_(e_log_open_failed));
@@ -5157,22 +6021,22 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
#define ADD_STATE_IF_MATCH(state) \
if (result) { \
- add_state = state->out; \
+ add_state = (state)->out; \
add_off = clen; \
}
/*
* Run for each character.
*/
- for (;; ) {
- int curc = utf_ptr2char(rex.input);
- int clen = utfc_ptr2len(rex.input);
+ for (;;) {
+ int curc = utf_ptr2char((char *)rex.input);
+ int clen = utfc_ptr2len((char *)rex.input);
if (curc == NUL) {
clen = 0;
go_to_nextline = false;
}
- /* swap lists */
+ // swap lists
thislist = &list[flag];
nextlist = &list[flag ^= 1];
nextlist->n = 0; // clear nextlist
@@ -5199,8 +6063,9 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
{
int i;
- for (i = 0; i < thislist->n; i++)
+ for (i = 0; i < thislist->n; i++) {
fprintf(log_fd, "%d ", abs(thislist->t[i].state->id));
+ }
}
fprintf(log_fd, "\n");
#endif
@@ -5211,8 +6076,9 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
/*
* If the state lists are empty we can stop.
*/
- if (thislist->n == 0)
+ if (thislist->n == 0) {
break;
+ }
// compute nextlist
for (listidx = 0; listidx < thislist->n; listidx++) {
@@ -5261,7 +6127,6 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
add_count = 0;
switch (t->state->c) {
case NFA_MATCH:
- {
// If the match is not at the start of the line, ends before a
// composing characters and rex.reg_icombine is not set, that
// is not really a match.
@@ -5286,7 +6151,6 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
clen = 0;
}
goto nextchar;
- }
case NFA_END_INVISIBLE:
case NFA_END_INVISIBLE_NEG:
@@ -5352,11 +6216,10 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
case NFA_START_INVISIBLE_BEFORE_FIRST:
case NFA_START_INVISIBLE_BEFORE_NEG:
case NFA_START_INVISIBLE_BEFORE_NEG_FIRST:
- {
#ifdef REGEXP_DEBUG
fprintf(log_fd, "Failure chance invisible: %d, what follows: %d\n",
- failure_chance(t->state->out, 0),
- failure_chance(t->state->out1->out, 0));
+ failure_chance(t->state->out, 0),
+ failure_chance(t->state->out1->out, 0));
#endif
// Do it directly if there already is a PIM or when
// nfa_postprocess() detected it will work better.
@@ -5432,11 +6295,9 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
goto theend;
}
}
- }
- break;
+ break;
- case NFA_START_PATTERN:
- {
+ case NFA_START_PATTERN: {
nfa_state_T *skip = NULL;
#ifdef REGEXP_DEBUG
int skip_lid = 0;
@@ -5450,13 +6311,13 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
skip_lid = nextlist->id;
#endif
} else if (state_in_list(nextlist,
- t->state->out1->out->out, &t->subs)) {
+ t->state->out1->out->out, &t->subs)) {
skip = t->state->out1->out->out;
#ifdef REGEXP_DEBUG
skip_lid = nextlist->id;
#endif
} else if (state_in_list(thislist,
- t->state->out1->out->out, &t->subs)) {
+ t->state->out1->out->out, &t->subs)) {
skip = t->state->out1->out->out;
#ifdef REGEXP_DEBUG
skip_lid = thislist->id;
@@ -5465,10 +6326,9 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
if (skip != NULL) {
#ifdef REGEXP_DEBUG
nfa_set_code(skip->c);
- fprintf(
- log_fd,
- "> Not trying to match pattern, output state %d is already in list %d. char %d: %s\n",
- abs(skip->id), skip_lid, skip->c, code);
+ fprintf(log_fd,
+ "> Not trying to match pattern, output state %d is already in list %d. char %d: %s\n", // NOLINT(whitespace/line_length)
+ abs(skip->id), skip_lid, skip->c, code);
#endif
break;
}
@@ -5605,8 +6465,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
}
break;
- case NFA_COMPOSING:
- {
+ case NFA_COMPOSING: {
int mc = curc;
int len = 0;
nfa_state_T *end;
@@ -5644,7 +6503,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
// We don't care about the order of composing characters.
// Get them into cchars[] first.
while (len < clen) {
- mc = utf_ptr2char(rex.input + len);
+ mc = utf_ptr2char((char *)rex.input + len);
cchars[ccount++] = mc;
len += utf_char2len(mc);
if (ccount == MAX_MCO) {
@@ -5657,17 +6516,20 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
// composing chars are matched.
result = OK;
while (sta->c != NFA_END_COMPOSING) {
- for (j = 0; j < ccount; ++j)
- if (cchars[j] == sta->c)
+ for (j = 0; j < ccount; j++) {
+ if (cchars[j] == sta->c) {
break;
+ }
+ }
if (j == ccount) {
result = FAIL;
break;
}
sta = sta->out;
}
- } else
+ } else {
result = FAIL;
+ }
end = t->state->out1; // NFA_END_COMPOSING
ADD_STATE_IF_MATCH(end);
@@ -5690,11 +6552,10 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
break;
case NFA_START_COLL:
- case NFA_START_NEG_COLL:
- {
+ case NFA_START_NEG_COLL: {
// What follows is a list of characters, until NFA_END_COLL.
// One of them must match or none of them must match.
- nfa_state_T *state;
+ nfa_state_T *state;
int result_if_matched;
int c1, c2;
@@ -5706,7 +6567,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
state = t->state->out;
result_if_matched = (t->state->c == NFA_START_COLL);
- for (;; ) {
+ for (;;) {
if (state->c == NFA_END_COLL) {
result = !result_if_matched;
break;
@@ -5717,7 +6578,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
c2 = state->val;
#ifdef REGEXP_DEBUG
fprintf(log_fd, "NFA_RANGE_MIN curc=%d c1=%d c2=%d\n",
- curc, c1, c2);
+ curc, c1, c2);
#endif
if (curc >= c1 && curc <= c2) {
result = result_if_matched;
@@ -5809,12 +6670,12 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
break;
case NFA_PRINT: // \p
- result = vim_isprintc(utf_ptr2char(rex.input));
+ result = vim_isprintc(utf_ptr2char((char *)rex.input));
ADD_STATE_IF_MATCH(t->state);
break;
case NFA_SPRINT: // \P
- result = !ascii_isdigit(curc) && vim_isprintc(utf_ptr2char(rex.input));
+ result = !ascii_isdigit(curc) && vim_isprintc(utf_ptr2char((char *)rex.input));
ADD_STATE_IF_MATCH(t->state);
break;
@@ -6032,52 +6893,60 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
case NFA_VCOL:
case NFA_VCOL_GT:
- case NFA_VCOL_LT:
- {
- int op = t->state->c - NFA_VCOL;
- colnr_T col = (colnr_T)(rex.input - rex.line);
-
- // Bail out quickly when there can't be a match, avoid the overhead of
- // win_linetabsize() on long lines.
- if (op != 1 && col > t->state->val * MB_MAXBYTES) {
- break;
- }
+ case NFA_VCOL_LT: {
+ int op = t->state->c - NFA_VCOL;
+ colnr_T col = (colnr_T)(rex.input - rex.line);
- result = false;
- win_T *wp = rex.reg_win == NULL ? curwin : rex.reg_win;
- if (op == 1 && col - 1 > t->state->val && col > 100) {
- long ts = wp->w_buffer->b_p_ts;
-
- // Guess that a character won't use more columns than 'tabstop',
- // with a minimum of 4.
- if (ts < 4) {
- ts = 4;
- }
- result = col > t->state->val * ts;
- }
- if (!result) {
- uintmax_t lts = win_linetabsize(wp, rex.line, col);
- assert(t->state->val >= 0);
- result = nfa_re_num_cmp((uintmax_t)t->state->val, op, lts + 1);
- }
- if (result) {
- add_here = true;
- add_state = t->state->out;
+ // Bail out quickly when there can't be a match, avoid the overhead of
+ // win_linetabsize() on long lines.
+ if (op != 1 && col > t->state->val * MB_MAXBYTES) {
+ break;
+ }
+
+ result = false;
+ win_T *wp = rex.reg_win == NULL ? curwin : rex.reg_win;
+ if (op == 1 && col - 1 > t->state->val && col > 100) {
+ long ts = wp->w_buffer->b_p_ts;
+
+ // Guess that a character won't use more columns than 'tabstop',
+ // with a minimum of 4.
+ if (ts < 4) {
+ ts = 4;
}
+ result = col > t->state->val * ts;
}
- break;
+ if (!result) {
+ uintmax_t lts = win_linetabsize(wp, rex.line, col);
+ assert(t->state->val >= 0);
+ result = nfa_re_num_cmp((uintmax_t)t->state->val, op, lts + 1);
+ }
+ if (result) {
+ add_here = true;
+ add_state = t->state->out;
+ }
+ }
+ break;
case NFA_MARK:
case NFA_MARK_GT:
- case NFA_MARK_LT:
- {
- pos_T *pos = getmark_buf(rex.reg_buf, t->state->val, false);
+ case NFA_MARK_LT: {
+ fmark_T *fm;
+ size_t col = REG_MULTI ? rex.input - rex.line : 0;
+ // fm will be NULL if the mark is not set, doesn't belong to reg_buf
+ fm = mark_get(rex.reg_buf, curwin, NULL, kMarkBufLocal, t->state->val);
+
+ // 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) {
+ if (fm != NULL && fm->mark.lnum > 0) {
+ pos_T *pos = &fm->mark;
const colnr_T pos_col = pos->lnum == rex.lnum + rex.reg_firstlnum
- && pos->col == MAXCOL
+ && pos->col == MAXCOL
? (colnr_T)STRLEN(reg_getline(pos->lnum - rex.reg_firstlnum))
: pos->col;
@@ -6100,8 +6969,8 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
case NFA_CURSOR:
result = rex.reg_win != NULL
- && (rex.lnum + rex.reg_firstlnum == rex.reg_win->w_cursor.lnum)
- && ((colnr_T)(rex.input - rex.line) == rex.reg_win->w_cursor.col);
+ && (rex.lnum + rex.reg_firstlnum == rex.reg_win->w_cursor.lnum)
+ && ((colnr_T)(rex.input - rex.line) == rex.reg_win->w_cursor.col);
if (result) {
add_here = true;
add_state = t->state->out;
@@ -6159,7 +7028,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
// If rex.reg_icombine is not set only skip over the character
// itself. When it is set skip over composing characters.
if (result && !rex.reg_icombine) {
- clen = utf_ptr2len(rex.input);
+ clen = utf_ptr2len((char *)rex.input);
}
ADD_STATE_IF_MATCH(t->state);
@@ -6171,10 +7040,11 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
nfa_pim_T *pim;
nfa_pim_T pim_copy;
- if (t->pim.result == NFA_PIM_UNUSED)
+ if (t->pim.result == NFA_PIM_UNUSED) {
pim = NULL;
- else
+ } else {
pim = &t->pim;
+ }
// Handle the postponed invisible match if the match might end
// without advancing and before the end of the line.
@@ -6207,10 +7077,9 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
result = (pim->result == NFA_PIM_MATCH);
#ifdef REGEXP_DEBUG
fprintf(log_fd, "\n");
- fprintf(
- log_fd,
- "Using previous recursive nfa_regmatch() result, result == %d\n",
- pim->result);
+ fprintf(log_fd,
+ "Using previous recursive nfa_regmatch() result, result == %d\n",
+ pim->result);
fprintf(log_fd, "MATCH = %s\n", result ? "OK" : "false");
fprintf(log_fd, "\n");
#endif
@@ -6307,13 +7176,13 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
} else {
// Checking if the required start character matches is
// cheaper than adding a state that won't match.
- const int c = utf_ptr2char(rex.input + clen);
+ const int c = utf_ptr2char((char *)rex.input + clen);
if (c != prog->regstart
&& (!rex.reg_ic
|| utf_fold(c) != utf_fold(prog->regstart))) {
#ifdef REGEXP_DEBUG
fprintf(log_fd,
- " Skipping start state, regstart does not match\n");
+ " Skipping start state, regstart does not match\n");
#endif
add = false;
}
@@ -6345,8 +7214,9 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
{
int i;
- for (i = 0; i < thislist->n; i++)
+ for (i = 0; i < thislist->n; i++) {
fprintf(log_fd, "%d ", abs(thislist->t[i].state->id));
+ }
}
fprintf(log_fd, "\n");
#endif
@@ -6378,8 +7248,9 @@ nextchar:
}
#ifdef REGEXP_DEBUG
- if (log_fd != stderr)
+ if (log_fd != stderr) {
fclose(log_fd);
+ }
log_fd = NULL;
#endif
@@ -6396,18 +7267,19 @@ theend:
return nfa_match;
}
-// Try match of "prog" with at rex.line["col"].
-// Returns <= 0 for failure, number of lines contained in the match otherwise.
-static long nfa_regtry(nfa_regprog_T *prog,
- colnr_T col,
- proftime_T *tm, // timeout limit or NULL
- int *timed_out) // flag set on timeout or NULL
+/// Try match of "prog" with at rex.line["col"].
+///
+/// @param tm timeout limit or NULL
+/// @param timed_out flag set on timeout or NULL
+///
+/// @return <= 0 for failure, number of lines contained in the match otherwise.
+static long nfa_regtry(nfa_regprog_T *prog, colnr_T col, proftime_T *tm, int *timed_out)
{
int i;
regsubs_T subs, m;
nfa_state_T *start = prog->start;
#ifdef REGEXP_DEBUG
- FILE *f;
+ FILE *f;
#endif
rex.input = rex.line + col;
@@ -6419,10 +7291,10 @@ static long nfa_regtry(nfa_regprog_T *prog,
f = fopen(NFA_REGEXP_RUN_LOG, "a");
if (f != NULL) {
fprintf(f,
- "\n\n\t=======================================================\n");
-#ifdef REGEXP_DEBUG
+ "\n\n\t=======================================================\n");
+# ifdef REGEXP_DEBUG
fprintf(f, "\tRegexp is \"%s\"\n", nfa_regengine.expr);
-#endif
+# endif
fprintf(f, "\tInput text is \"%s\" \n", rex.input);
fprintf(f, "\t=======================================================\n\n");
nfa_print_state(f, start);
@@ -6481,7 +7353,7 @@ static long nfa_regtry(nfa_regprog_T *prog,
}
}
- /* Package any found \z(...\) matches for export. Default is none. */
+ // Package any found \z(...\) matches for export. Default is none.
unref_extmatch(re_extmatch_out);
re_extmatch_out = NULL;
@@ -6504,9 +7376,10 @@ static long nfa_regtry(nfa_regprog_T *prog,
} else {
struct linepos *lpos = &subs.synt.list.line[i];
- if (lpos->start != NULL && lpos->end != NULL)
+ if (lpos->start != NULL && lpos->end != NULL) {
re_extmatch_out->matches[i] =
vim_strnsave(lpos->start, lpos->end - lpos->start);
+ }
}
}
}
@@ -6524,10 +7397,9 @@ static long nfa_regtry(nfa_regprog_T *prog,
///
/// @return <= 0 if there is no match and number of lines contained in the
/// match otherwise.
-static long nfa_regexec_both(char_u *line, colnr_T startcol,
- proftime_T *tm, int *timed_out)
+static long nfa_regexec_both(char_u *line, colnr_T startcol, proftime_T *tm, int *timed_out)
{
- nfa_regprog_T *prog;
+ nfa_regprog_T *prog;
long retval = 0L;
colnr_T col = startcol;
@@ -6542,7 +7414,7 @@ static long nfa_regexec_both(char_u *line, colnr_T startcol,
rex.reg_endp = rex.reg_match->endp;
}
- /* Be paranoid... */
+ // Be paranoid...
if (prog == NULL || line == NULL) {
iemsg(_(e_null));
goto theend;
@@ -6572,8 +7444,9 @@ static long nfa_regexec_both(char_u *line, colnr_T startcol,
nfa_regengine.expr = prog->pattern;
#endif
- if (prog->reganch && col > 0)
+ if (prog->reganch && col > 0) {
return 0L;
+ }
rex.need_clear_subexpr = true;
// Clear the external match subpointers if necessary.
@@ -6647,11 +7520,12 @@ theend:
*/
static regprog_T *nfa_regcomp(char_u *expr, int re_flags)
{
- nfa_regprog_T *prog = NULL;
- int *postfix;
+ nfa_regprog_T *prog = NULL;
+ int *postfix;
- if (expr == NULL)
+ if (expr == NULL) {
return NULL;
+ }
#ifdef REGEXP_DEBUG
nfa_regengine.expr = expr;
@@ -6694,7 +7568,7 @@ static regprog_T *nfa_regcomp(char_u *expr, int re_flags)
*/
post2nfa(postfix, post_ptr, true);
- /* allocate the regprog with space for the compiled regexp */
+ // allocate the regprog with space for the compiled regexp
size_t prog_size = sizeof(nfa_regprog_T) + sizeof(nfa_state_T) * (nstate - 1);
prog = xmalloc(prog_size);
state_ptr = prog->state;
@@ -6725,7 +7599,7 @@ static regprog_T *nfa_regcomp(char_u *expr, int re_flags)
nfa_postfix_dump(expr, OK);
nfa_dump(prog);
#endif
- /* Remember whether this pattern has any \z specials in it. */
+ // Remember whether this pattern has any \z specials in it.
prog->reghasz = re_has_z;
prog->pattern = vim_strsave(expr);
#ifdef REGEXP_DEBUG
@@ -6759,21 +7633,16 @@ static void nfa_regfree(regprog_T *prog)
}
}
-/*
- * Match a regexp against a string.
- * "rmp->regprog" is a compiled regexp as returned by nfa_regcomp().
- * Uses curbuf for line count and 'iskeyword'.
- * If "line_lbr" is true, consider a "\n" in "line" to be a line break.
- *
- * Returns <= 0 for failure, number of lines contained in the match otherwise.
- */
-static int
-nfa_regexec_nl (
- regmatch_T *rmp,
- char_u *line, /* string to match against */
- colnr_T col, /* column to start looking for match */
- bool line_lbr
-)
+/// Match a regexp against a string.
+/// "rmp->regprog" is a compiled regexp as returned by nfa_regcomp().
+/// Uses curbuf for line count and 'iskeyword'.
+/// If "line_lbr" is true, consider a "\n" in "line" to be a line break.
+///
+/// @param line string to match against
+/// @param col column to start looking for match
+///
+/// @return <= 0 for failure, number of lines contained in the match otherwise.
+static int nfa_regexec_nl(regmatch_T *rmp, char_u *line, colnr_T col, bool line_lbr)
{
rex.reg_match = rmp;
rex.reg_mmatch = NULL;
@@ -6822,8 +7691,7 @@ nfa_regexec_nl (
///
/// @par
/// FIXME if this behavior is not compatible.
-static long nfa_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf,
- linenr_T lnum, colnr_T col,
+static long nfa_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf, linenr_T lnum, colnr_T col,
proftime_T *tm, int *timed_out)
{
rex.reg_match = NULL;
diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c
index 1c04cb16b3..045cee2439 100644
--- a/src/nvim/runtime.c
+++ b/src/nvim/runtime.c
@@ -24,12 +24,19 @@
static bool runtime_search_path_valid = false;
static int *runtime_search_path_ref = NULL;
static RuntimeSearchPath runtime_search_path;
+static RuntimeSearchPath runtime_search_path_thread;
+static uv_mutex_t runtime_search_path_mutex;
+
+void runtime_init(void)
+{
+ uv_mutex_init(&runtime_search_path_mutex);
+}
/// ":runtime [what] {name}"
void ex_runtime(exarg_T *eap)
{
- char_u *arg = eap->arg;
- char_u *p = skiptowhite(arg);
+ char *arg = eap->arg;
+ char *p = (char *)skiptowhite((char_u *)arg);
ptrdiff_t len = p - arg;
int flags = eap->forceit ? DIP_ALL : 0;
@@ -47,13 +54,12 @@ void ex_runtime(exarg_T *eap)
arg = skipwhite(arg + len);
}
- source_runtime((char *)arg, flags);
+ source_runtime(arg, flags);
}
-
-static void source_callback(char_u *fname, void *cookie)
+static void source_callback(char *fname, void *cookie)
{
- (void)do_source((char *)fname, false, DOSO_NONE);
+ (void)do_source(fname, false, DOSO_NONE);
}
/// Find the file "name" in all directories in "path" and invoke
@@ -87,7 +93,7 @@ int do_in_path(char_u *path, char *name, int flags, DoInRuntimepathCB callback,
char_u *rtp = rtp_copy;
while (*rtp != NUL && ((flags & DIP_ALL) || !did_one)) {
// Copy the path from 'runtimepath' to buf[].
- copy_option_part(&rtp, buf, MAXPATHL, ",");
+ copy_option_part((char **)&rtp, (char *)buf, MAXPATHL, ",");
size_t buflen = STRLEN(buf);
// Skip after or non-after directories.
@@ -101,7 +107,7 @@ int do_in_path(char_u *path, char *name, int flags, DoInRuntimepathCB callback,
}
if (name == NULL) {
- (*callback)(buf, cookie);
+ (*callback)((char *)buf, cookie);
did_one = true;
} else if (buflen + STRLEN(name) + 2 < MAXPATHL) {
add_pathsep((char *)buf);
@@ -112,8 +118,7 @@ int do_in_path(char_u *path, char *name, int flags, DoInRuntimepathCB callback,
while (*np != NUL && ((flags & DIP_ALL) || !did_one)) {
// Append the pattern from "name" to buf[].
assert(MAXPATHL >= (tail - buf));
- copy_option_part(&np, tail, (size_t)(MAXPATHL - (tail - buf)),
- "\t ");
+ copy_option_part((char **)&np, (char *)tail, (size_t)(MAXPATHL - (tail - buf)), "\t ");
if (p_verbose > 10) {
verbose_enter();
@@ -127,7 +132,7 @@ int do_in_path(char_u *path, char *name, int flags, DoInRuntimepathCB callback,
// Expand wildcards, invoke the callback for each match.
if (gen_expand_wildcards(1, &buf, &num_files, &files, ew_flags) == OK) {
for (i = 0; i < num_files; i++) {
- (*callback)(files[i], cookie);
+ (*callback)((char *)files[i], cookie);
did_one = true;
if (!(flags & DIP_ALL)) {
break;
@@ -146,14 +151,13 @@ int do_in_path(char_u *path, char *name, int flags, DoInRuntimepathCB callback,
if (flags & DIP_ERR) {
semsg(_(e_dirnotf), basepath, name);
- } else if (p_verbose > 0) {
+ } else if (p_verbose > 1) {
verbose_enter();
smsg(_("not found in '%s': \"%s\""), basepath, name);
verbose_leave();
}
}
-
return did_one ? OK : FAIL;
}
@@ -172,6 +176,17 @@ RuntimeSearchPath runtime_search_path_get_cached(int *ref)
return runtime_search_path;
}
+RuntimeSearchPath copy_runtime_search_path(const RuntimeSearchPath src)
+{
+ RuntimeSearchPath dst = KV_INITIAL_VALUE;
+ for (size_t j = 0; j < kv_size(src); j++) {
+ SearchPathItem src_item = kv_A(src, j);
+ kv_push(dst, ((SearchPathItem){ xstrdup(src_item.path), src_item.after, src_item.has_lua }));
+ }
+
+ return dst;
+}
+
void runtime_search_path_unref(RuntimeSearchPath path, int *ref)
FUNC_ATTR_NONNULL_ALL
{
@@ -184,7 +199,6 @@ void runtime_search_path_unref(RuntimeSearchPath path, int *ref)
}
}
-
/// Find the file "name" in all directories in "path" and invoke
/// "callback(fname, cookie)".
/// "name" can contain wildcards.
@@ -226,7 +240,7 @@ int do_in_cached_path(char_u *name, int flags, DoInRuntimepathCB callback, void
}
if (name == NULL) {
- (*callback)((char_u *)item.path, cookie);
+ (*callback)(item.path, cookie);
} else if (buflen + STRLEN(name) + 2 < MAXPATHL) {
STRCPY(buf, item.path);
add_pathsep((char *)buf);
@@ -237,8 +251,7 @@ int do_in_cached_path(char_u *name, int flags, DoInRuntimepathCB callback, void
while (*np != NUL && ((flags & DIP_ALL) || !did_one)) {
// Append the pattern from "name" to buf[].
assert(MAXPATHL >= (tail - buf));
- copy_option_part(&np, tail, (size_t)(MAXPATHL - (tail - buf)),
- "\t ");
+ copy_option_part((char **)&np, (char *)tail, (size_t)(MAXPATHL - (tail - buf)), "\t ");
if (p_verbose > 10) {
verbose_enter();
@@ -253,7 +266,7 @@ int do_in_cached_path(char_u *name, int flags, DoInRuntimepathCB callback, void
char_u *(pat[]) = { buf };
if (gen_expand_wildcards(1, pat, &num_files, &files, ew_flags) == OK) {
for (i = 0; i < num_files; i++) {
- (*callback)(files[i], cookie);
+ (*callback)((char *)files[i], cookie);
did_one = true;
if (!(flags & DIP_ALL)) {
break;
@@ -268,7 +281,7 @@ int do_in_cached_path(char_u *name, int flags, DoInRuntimepathCB callback, void
if (!did_one && name != NULL) {
if (flags & DIP_ERR) {
semsg(_(e_dirnotf), "runtime path", name);
- } else if (p_verbose > 0) {
+ } else if (p_verbose > 1) {
verbose_enter();
smsg(_("not found in runtime path: \"%s\""), name);
verbose_leave();
@@ -302,15 +315,35 @@ ArrayOf(String) runtime_get_named(bool lua, Array pat, bool all)
{
int ref;
RuntimeSearchPath path = runtime_search_path_get_cached(&ref);
- ArrayOf(String) rv = ARRAY_DICT_INIT;
static char buf[MAXPATHL];
+ ArrayOf(String) rv = runtime_get_named_common(lua, pat, all, path, buf, sizeof buf);
+
+ runtime_search_path_unref(path, &ref);
+ return rv;
+}
+
+ArrayOf(String) runtime_get_named_thread(bool lua, Array pat, bool all)
+{
+ // TODO(bfredl): avoid contention between multiple worker threads?
+ uv_mutex_lock(&runtime_search_path_mutex);
+ static char buf[MAXPATHL];
+ ArrayOf(String) rv = runtime_get_named_common(lua, pat, all, runtime_search_path_thread,
+ buf, sizeof buf);
+ uv_mutex_unlock(&runtime_search_path_mutex);
+ return rv;
+}
+
+ArrayOf(String) runtime_get_named_common(bool lua, Array pat, bool all,
+ RuntimeSearchPath path, char *buf, size_t buf_len)
+{
+ ArrayOf(String) rv = ARRAY_DICT_INIT;
for (size_t i = 0; i < kv_size(path); i++) {
SearchPathItem *item = &kv_A(path, i);
if (lua) {
if (item->has_lua == kNone) {
- size_t size = (size_t)snprintf(buf, sizeof buf, "%s/lua/", item->path);
- item->has_lua = (size < sizeof buf && os_isdir((char_u *)buf)) ? kTrue : kFalse;
+ size_t size = (size_t)snprintf(buf, buf_len, "%s/lua/", item->path);
+ item->has_lua = (size < buf_len && os_isdir((char_u *)buf));
}
if (item->has_lua == kFalse) {
continue;
@@ -320,9 +353,9 @@ ArrayOf(String) runtime_get_named(bool lua, Array pat, bool all)
for (size_t j = 0; j < pat.size; j++) {
Object pat_item = pat.items[j];
if (pat_item.type == kObjectTypeString) {
- size_t size = (size_t)snprintf(buf, sizeof buf, "%s/%s",
+ size_t size = (size_t)snprintf(buf, buf_len, "%s/%s",
item->path, pat_item.data.string.data);
- if (size < sizeof buf) {
+ if (size < buf_len) {
if (os_file_is_readable(buf)) {
ADD(rv, STRING_OBJ(cstr_to_string(buf)));
if (!all) {
@@ -333,9 +366,7 @@ ArrayOf(String) runtime_get_named(bool lua, Array pat, bool all)
}
}
}
-
done:
- runtime_search_path_unref(path, &ref);
return rv;
}
@@ -449,7 +480,7 @@ static void expand_pack_entry(RuntimeSearchPath *search_path, Map(String, handle
STRLCPY(buf, pack_entry, sizeof buf);
STRLCPY(buf + pack_entry_len, start_pat[i], sizeof buf - pack_entry_len);
expand_rtp_entry(search_path, rtp_used, buf, false);
- size_t after_size = STRLEN(buf)+7;
+ size_t after_size = STRLEN(buf) + 7;
char *after = xmallocz(after_size);
xstrlcpy(after, buf, after_size);
xstrlcat(after, "/after", after_size);
@@ -463,7 +494,7 @@ static bool path_is_after(char_u *buf, size_t buflen)
// vim8 considers all dirs like "foo/bar_after", "Xafter" etc, as an
// "after" dir in SOME codepaths not not in ALL codepaths.
return buflen >= 5
- && (!(buflen >= 6) || vim_ispathsep(buf[buflen-6]))
+ && (!(buflen >= 6) || vim_ispathsep(buf[buflen - 6]))
&& STRCMP(buf + buflen - 5, "after") == 0;
}
@@ -480,7 +511,7 @@ RuntimeSearchPath runtime_search_path_build(void)
static char_u buf[MAXPATHL];
for (char *entry = (char *)p_pp; *entry != NUL;) {
char *cur_entry = entry;
- copy_option_part((char_u **)&entry, buf, MAXPATHL, ",");
+ copy_option_part(&entry, (char *)buf, MAXPATHL, ",");
String the_entry = { .data = cur_entry, .size = STRLEN(buf) };
@@ -488,11 +519,10 @@ RuntimeSearchPath runtime_search_path_build(void)
map_put(String, handle_T)(&pack_used, the_entry, 0);
}
-
char *rtp_entry;
for (rtp_entry = (char *)p_rtp; *rtp_entry != NUL;) {
char *cur_entry = rtp_entry;
- copy_option_part((char_u **)&rtp_entry, buf, MAXPATHL, ",");
+ copy_option_part(&rtp_entry, (char *)buf, MAXPATHL, ",");
size_t buflen = STRLEN(buf);
if (path_is_after(buf, buflen)) {
@@ -526,7 +556,7 @@ RuntimeSearchPath runtime_search_path_build(void)
// "after" dirs in rtp
for (; *rtp_entry != NUL;) {
- copy_option_part((char_u **)&rtp_entry, buf, MAXPATHL, ",");
+ copy_option_part(&rtp_entry, (char *)buf, MAXPATHL, ",");
expand_rtp_entry(&search_path, &rtp_used, (char *)buf, path_is_after(buf, STRLEN(buf)));
}
@@ -569,22 +599,25 @@ void runtime_search_path_validate(void)
runtime_search_path = runtime_search_path_build();
runtime_search_path_valid = true;
runtime_search_path_ref = NULL; // initially unowned
+ uv_mutex_lock(&runtime_search_path_mutex);
+ runtime_search_path_free(runtime_search_path_thread);
+ runtime_search_path_thread = copy_runtime_search_path(runtime_search_path);
+ uv_mutex_unlock(&runtime_search_path_mutex);
}
}
-
/// Just like do_in_path_and_pp(), using 'runtimepath' for "path".
-int do_in_runtimepath(char_u *name, int flags, DoInRuntimepathCB callback, void *cookie)
+int do_in_runtimepath(char *name, int flags, DoInRuntimepathCB callback, void *cookie)
{
int success = FAIL;
if (!(flags & DIP_NORTP)) {
- success |= do_in_cached_path((name && !*name) ? NULL : name, flags, callback, cookie);
+ success |= do_in_cached_path((name && !*name) ? NULL : (char_u *)name, flags, callback, cookie);
flags = (flags & ~DIP_START) | DIP_NORTP;
}
// TODO(bfredl): we could integrate disabled OPT dirs into the cached path
// which would effectivize ":packadd myoptpack" as well
if ((flags & (DIP_START|DIP_OPT)) && (success == FAIL || (flags & DIP_ALL))) {
- success |= do_in_path_and_pp(p_rtp, name, flags, callback, cookie);
+ success |= do_in_path_and_pp(p_rtp, (char_u *)name, flags, callback, cookie);
}
return success;
}
@@ -596,7 +629,7 @@ int do_in_runtimepath(char_u *name, int flags, DoInRuntimepathCB callback, void
/// return FAIL when no file could be sourced, OK otherwise.
int source_runtime(char *name, int flags)
{
- return do_in_runtimepath((char_u *)name, flags, source_callback, NULL);
+ return do_in_runtimepath(name, flags, source_callback, NULL);
}
/// Just like source_runtime(), but use "path" instead of 'runtimepath'.
@@ -665,7 +698,7 @@ static int add_pack_dir_to_rtp(char_u *fname, bool is_pack)
for (const char *entry = (const char *)p_rtp; *entry != NUL;) {
const char *cur_entry = entry;
- copy_option_part((char_u **)&entry, buf, MAXPATHL, ",");
+ copy_option_part((char **)&entry, (char *)buf, MAXPATHL, ",");
if (insp == NULL) {
add_pathsep((char *)buf);
char *const rtp_ffname = fix_fname((char *)buf);
@@ -786,7 +819,7 @@ static int load_pack_plugin(bool opt, char_u *fname)
// If runtime/filetype.vim wasn't loaded yet, the scripts will be
// found when it loads.
- if (opt && eval_to_number(cmd) > 0) {
+ if (opt && eval_to_number((char *)cmd) > 0) {
do_cmdline_cmd("augroup filetypedetect");
vim_snprintf((char *)pat, len, ftpat, ffname);
source_all_matches(pat);
@@ -814,7 +847,7 @@ static void add_pack_plugin(bool opt, char_u *fname, void *cookie)
const char *p = (const char *)p_rtp;
while (*p != NUL) {
- copy_option_part((char_u **)&p, (char_u *)buf, MAXPATHL, ",");
+ copy_option_part((char **)&p, buf, MAXPATHL, ",");
if (path_fnamecmp(buf, (char *)fname) == 0) {
found = true;
break;
@@ -834,17 +867,16 @@ static void add_pack_plugin(bool opt, char_u *fname, void *cookie)
}
}
-static void add_start_pack_plugin(char_u *fname, void *cookie)
+static void add_start_pack_plugin(char *fname, void *cookie)
{
- add_pack_plugin(false, fname, cookie);
+ add_pack_plugin(false, (char_u *)fname, cookie);
}
-static void add_opt_pack_plugin(char_u *fname, void *cookie)
+static void add_opt_pack_plugin(char *fname, void *cookie)
{
- add_pack_plugin(true, fname, cookie);
+ add_pack_plugin(true, (char_u *)fname, cookie);
}
-
/// Add all packages in the "start" directory to 'runtimepath'.
void add_pack_start_dirs(void)
{
@@ -862,7 +894,7 @@ static bool pack_has_entries(char_u *buf)
return num_files > 0;
}
-static void add_pack_start_dir(char_u *fname, void *cookie)
+static void add_pack_start_dir(char *fname, void *cookie)
{
static char_u buf[MAXPATHL];
char *(start_pat[]) = { "/start/*", "/pack/*/start/*" }; // NOLINT
@@ -878,7 +910,6 @@ static void add_pack_start_dir(char_u *fname, void *cookie)
}
}
-
/// Load plugins from all packages in the "start" directory.
void load_start_packages(void)
{
@@ -1009,7 +1040,6 @@ static inline size_t compute_double_env_sep_len(const char *const val, const siz
return ret;
}
-
#define NVIM_SIZE (sizeof("nvim") - 1)
/// Add directories to a ENV_SEPCHAR-separated array from a colon-separated one
///
@@ -1211,10 +1241,11 @@ char *runtimepath_default(bool clean_arg)
AFTER_SIZE + 1);
rtp_size += compute_double_env_sep_len(config_dirs, NVIM_SIZE + 1,
AFTER_SIZE + 1);
+ char *rtp = NULL;
if (rtp_size == 0) {
- return NULL;
+ goto freeall;
}
- char *const rtp = xmalloc(rtp_size);
+ rtp = xmalloc(rtp_size);
char *rtp_cur = rtp;
rtp_cur = add_dir(rtp_cur, config_home, config_len, kXDGConfigHome,
NULL, 0, NULL, 0);
@@ -1239,6 +1270,7 @@ char *runtimepath_default(bool clean_arg)
assert((size_t)(rtp_cur - rtp) == rtp_size);
#undef SITE_SIZE
#undef AFTER_SIZE
+freeall:
xfree(data_dirs);
xfree(config_dirs);
xfree(data_home);
diff --git a/src/nvim/runtime.h b/src/nvim/runtime.h
index 4337a0b3cd..d83ec00185 100644
--- a/src/nvim/runtime.h
+++ b/src/nvim/runtime.h
@@ -5,7 +5,7 @@
#include "nvim/ex_docmd.h"
-typedef void (*DoInRuntimepathCB)(char_u *, void *);
+typedef void (*DoInRuntimepathCB)(char *, void *);
typedef struct {
char *path;
@@ -32,7 +32,6 @@ typedef kvec_t(char *) CharVec;
#define DIP_LUA 0x100 // also use ".lua" files
#define DIP_DIRFILE 0x200 // find both files and directories
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "runtime.h.generated.h"
#endif
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 3c91d764bf..e99f9b9153 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -67,6 +67,7 @@
#include "nvim/api/extmark.h"
#include "nvim/api/private/helpers.h"
+#include "nvim/api/ui.h"
#include "nvim/api/vim.h"
#include "nvim/arabic.h"
#include "nvim/ascii.h"
@@ -75,6 +76,7 @@
#include "nvim/cursor.h"
#include "nvim/cursor_shape.h"
#include "nvim/decoration.h"
+#include "nvim/decoration_provider.h"
#include "nvim/diff.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
@@ -86,13 +88,16 @@
#include "nvim/fold.h"
#include "nvim/garray.h"
#include "nvim/getchar.h"
+#include "nvim/grid_defs.h"
#include "nvim/highlight.h"
+#include "nvim/highlight_group.h"
#include "nvim/indent.h"
#include "nvim/lib/kvec.h"
#include "nvim/log.h"
#include "nvim/lua/executor.h"
#include "nvim/main.h"
#include "nvim/mark.h"
+#include "nvim/match.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
@@ -126,21 +131,8 @@
#define MB_FILLER_CHAR '<' /* character used when a double-width character
* doesn't fit. */
-typedef kvec_withinit_t(DecorProvider *, 4) Providers;
-
-// temporary buffer for rendering a single screenline, so it can be
-// compared with previous contents to calculate smallest delta.
-// Per-cell attributes
-static size_t linebuf_size = 0;
-static schar_T *linebuf_char = NULL;
-static sattr_T *linebuf_attr = NULL;
-
static match_T search_hl; // used for 'hlsearch' highlight matching
-StlClickDefinition *tab_page_click_defs = NULL;
-
-long tab_page_click_defs_size = 0;
-
// for line_putchar. Contains the state that needs to be remembered from
// putting one character to the next.
typedef struct {
@@ -160,44 +152,20 @@ static bool msg_grid_invalid = false;
static bool resizing = false;
+typedef struct {
+ NS ns_id;
+ uint64_t mark_id;
+ int win_row;
+ int win_col;
+} WinExtmark;
+static kvec_t(WinExtmark) win_extmark_arr INIT(= KV_INITIAL_VALUE);
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "screen.c.generated.h"
#endif
-#define SEARCH_HL_PRIORITY 0
static char *provider_err = NULL;
-static bool provider_invoke(NS ns_id, const char *name, LuaRef ref, Array args, bool default_true)
-{
- Error err = ERROR_INIT;
-
- textlock++;
- provider_active = true;
- Object ret = nlua_call_ref(ref, name, args, true, &err);
- provider_active = false;
- textlock--;
-
- if (!ERROR_SET(&err)
- && api_object_to_bool(ret, "provider %s retval", default_true, &err)) {
- return true;
- }
-
- if (ERROR_SET(&err)) {
- const char *ns_name = describe_ns(ns_id);
- ELOG("error in provider %s:%s: %s", ns_name, name, err.msg);
- bool verbose_errs = true; // TODO(bfredl):
- if (verbose_errs && provider_err == NULL) {
- static char errbuf[IOSIZE];
- snprintf(errbuf, sizeof errbuf, "%s: %s", ns_name, err.msg);
- provider_err = xstrdup(errbuf);
- }
- }
-
- api_free_object(ret);
- return false;
-}
-
/// Redraw a window later, with update_screen(type).
///
/// Set must_redraw only if not already set to a higher value.
@@ -304,20 +272,12 @@ void redrawWinline(win_T *wp, linenr_T lnum)
}
}
-/*
- * update all windows that are editing the current buffer
- */
-void update_curbuf(int type)
-{
- redraw_curbuf_later(type);
- update_screen(type);
-}
-
/// called when the status bars for the buffer 'buf' need to be updated
void redraw_buf_status_later(buf_T *buf)
{
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_buffer == buf && wp->w_status_height) {
+ if (wp->w_buffer == buf && (wp->w_status_height || (wp == curwin && global_stl_height())
+ || wp->w_winbar_height)) {
wp->w_redr_status = true;
if (must_redraw < VALID) {
must_redraw = VALID;
@@ -326,6 +286,25 @@ void redraw_buf_status_later(buf_T *buf)
}
}
+void redraw_win_signcol(win_T *wp)
+{
+ // If we can compute a change in the automatic sizing of the sign column
+ // under 'signcolumn=auto:X' and signs currently placed in the buffer, better
+ // figuring it out here so we can redraw the entire screen for it.
+ int scwidth = wp->w_scwidth;
+ wp->w_scwidth = win_signcol_count(wp);
+ if (wp->w_scwidth != scwidth) {
+ changed_line_abv_curs_win(wp);
+ }
+}
+
+/// Update all windows that are editing the current buffer.
+void update_curbuf(int type)
+{
+ redraw_curbuf_later(type);
+ update_screen(type);
+}
+
/// Redraw the parts of the screen that is marked for redraw.
///
/// Most code shouldn't call this directly, rather use redraw_later() and
@@ -335,6 +314,7 @@ void redraw_buf_status_later(buf_T *buf)
int update_screen(int type)
{
static bool did_intro = false;
+ bool is_stl_global = global_stl_height() > 0;
// Don't do anything if the screen structures are (not yet) valid.
// A VimResized autocmd can invoke redrawing in the middle of a resize,
@@ -353,10 +333,11 @@ int update_screen(int type)
type = must_redraw;
}
- /* must_redraw is reset here, so that when we run into some weird
- * reason to redraw while busy redrawing (e.g., asynchronous
- * scrolling), or update_topline() in win_update() will cause a
- * scroll, the screen will be redrawn later or in win_update(). */
+ // must_redraw is reset here, so that when we run into some weird
+ // reason to redraw while busy redrawing (e.g., asynchronous
+ // scrolling), or update_topline() in win_update() will cause a
+ // scroll, or a decoration provider requires a redraw, the screen
+ // will be redrawn later or in win_update().
must_redraw = 0;
}
@@ -396,9 +377,9 @@ int update_screen(int type)
int valid = MAX(Rows - msg_scrollsize(), 0);
if (msg_grid.chars) {
// non-displayed part of msg_grid is considered invalid.
- for (int i = 0; i < MIN(msg_scrollsize(), msg_grid.Rows); i++) {
+ for (int i = 0; i < MIN(msg_scrollsize(), msg_grid.rows); i++) {
grid_clear_line(&msg_grid, msg_grid.line_offset[i],
- msg_grid.Columns, false);
+ msg_grid.cols, false);
}
}
if (msg_use_msgsep()) {
@@ -406,7 +387,7 @@ int update_screen(int type)
// CLEAR is already handled
if (type == NOT_VALID && !ui_has(kUIMultigrid) && msg_scrolled) {
ui_comp_set_screen_valid(false);
- for (int i = valid; i < Rows-p_ch; i++) {
+ for (int i = valid; i < Rows - p_ch; i++) {
grid_clear_line(&default_grid, default_grid.line_offset[i],
Columns, false);
}
@@ -417,12 +398,15 @@ int update_screen(int type)
if (W_ENDROW(wp) > valid) {
wp->w_redr_type = MAX(wp->w_redr_type, NOT_VALID);
}
- if (W_ENDROW(wp) + wp->w_status_height > valid) {
+ if (!is_stl_global && W_ENDROW(wp) + wp->w_status_height > valid) {
wp->w_redr_status = true;
}
}
+ if (is_stl_global && Rows - p_ch - 1 > valid) {
+ curwin->w_redr_status = true;
+ }
}
- msg_grid_set_pos(Rows-p_ch, false);
+ msg_grid_set_pos(Rows - p_ch, false);
msg_grid_invalid = false;
} else if (msg_scrolled > Rows - 5) { // clearing is faster
type = CLEAR;
@@ -442,13 +426,15 @@ int update_screen(int type)
wp->w_redr_type = REDRAW_TOP;
} else {
wp->w_redr_type = NOT_VALID;
- if (W_ENDROW(wp) + wp->w_status_height
- <= msg_scrolled) {
- wp->w_redr_status = TRUE;
+ if (wp->w_winrow + wp->w_winbar_height <= msg_scrolled) {
+ wp->w_redr_status = true;
}
}
}
}
+ if (is_stl_global && Rows - p_ch - 1 <= msg_scrolled) {
+ curwin->w_redr_status = true;
+ }
redraw_cmdline = true;
redraw_tabline = true;
}
@@ -484,33 +470,13 @@ int update_screen(int type)
// After disabling msgsep the grid might not have been deallocated yet,
// hence we also need to check msg_grid.chars
if (type == NOT_VALID && (msg_use_grid() || msg_grid.chars)) {
- grid_fill(&default_grid, Rows-p_ch, Rows, 0, Columns, ' ', ' ', 0);
+ grid_fill(&default_grid, Rows - p_ch, Rows, 0, Columns, ' ', ' ', 0);
}
ui_comp_set_screen_valid(true);
- Providers providers;
- kvi_init(providers);
- for (size_t i = 0; i < kv_size(decor_providers); i++) {
- DecorProvider *p = &kv_A(decor_providers, i);
- if (!p->active) {
- continue;
- }
-
- bool active;
- if (p->redraw_start != LUA_NOREF) {
- FIXED_TEMP_ARRAY(args, 2);
- args.items[0] = INTEGER_OBJ(display_tick);
- args.items[1] = INTEGER_OBJ(type);
- active = provider_invoke(p->ns_id, "start", p->redraw_start, args, true);
- } else {
- active = true;
- }
-
- if (active) {
- kvi_push(providers, p);
- }
- }
+ DecorProviders providers;
+ decor_providers_start(&providers, type, &provider_err);
// "start" callback could have changed highlights for global elements
if (win_check_ns_hl(NULL)) {
@@ -579,14 +545,7 @@ int update_screen(int type)
}
if (buf->b_mod_tick_decor < display_tick) {
- for (size_t i = 0; i < kv_size(providers); i++) {
- DecorProvider *p = kv_A(providers, i);
- if (p && p->redraw_buf != LUA_NOREF) {
- FIXED_TEMP_ARRAY(args, 1);
- args.items[0] = BUFFER_OBJ(buf->handle);
- provider_invoke(p->ns_id, "buf", p->redraw_buf, args, true);
- }
- }
+ decor_providers_invoke_buf(buf, &providers, &provider_err);
buf->b_mod_tick_decor = display_tick;
}
}
@@ -599,7 +558,6 @@ int update_screen(int type)
bool did_one = false;
search_hl.rm.regprog = NULL;
-
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_redr_type == CLEAR && wp->w_floating && wp->w_grid_alloc.chars) {
grid_invalidate(&wp->w_grid_alloc);
@@ -621,8 +579,9 @@ int update_screen(int type)
win_update(wp, &providers);
}
- // redraw status line after the window to minimize cursor movement
+ // redraw status line and window bar after the window to minimize cursor movement
if (wp->w_redr_status) {
+ win_redr_winbar(wp);
win_redr_status(wp);
}
}
@@ -634,8 +593,6 @@ int update_screen(int type)
pum_redraw();
}
- send_grid_resize = false;
-
/* Reset b_mod_set flags. Going through all windows is probably faster
* than going through all buffers (there could be many buffers). */
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
@@ -656,21 +613,9 @@ int update_screen(int type)
}
did_intro = true;
- for (size_t i = 0; i < kv_size(providers); i++) {
- DecorProvider *p = kv_A(providers, i);
- if (!p->active) {
- continue;
- }
-
- if (p->redraw_end != LUA_NOREF) {
- FIXED_TEMP_ARRAY(args, 1);
- args.items[0] = INTEGER_OBJ(display_tick);
- provider_invoke(p->ns_id, "end", p->redraw_end, args, true);
- }
- }
+ decor_providers_invoke_end(&providers, &provider_err);
kvi_destroy(providers);
-
// either cmdline is cleared, not drawn or mode is last drawn
cmdline_was_last_drawn = false;
return OK;
@@ -686,18 +631,18 @@ bool conceal_cursor_line(const win_T *wp)
if (*wp->w_p_cocu == NUL) {
return false;
}
- if (get_real_state() & VISUAL) {
+ if (get_real_state() & MODE_VISUAL) {
c = 'v';
- } else if (State & INSERT) {
+ } else if (State & MODE_INSERT) {
c = 'i';
- } else if (State & NORMAL) {
+ } else if (State & MODE_NORMAL) {
c = 'n';
- } else if (State & CMDLINE) {
+ } else if (State & MODE_CMDLINE) {
c = 'c';
} else {
return false;
}
- return vim_strchr(wp->w_p_cocu, c) != NULL;
+ return vim_strchr((char *)wp->w_p_cocu, c) != NULL;
}
// Check if the cursor line needs to be redrawn because of 'concealcursor'.
@@ -716,15 +661,11 @@ void conceal_check_cursor_line(void)
/// Whether cursorline is drawn in a special way
///
-/// If true, both old and new cursorline will need
-/// to be redrawn when moving cursor within windows.
-/// TODO(bfredl): VIsual_active shouldn't be needed, but is used to fix a glitch
-/// caused by scrolling.
+/// If true, both old and new cursorline will need to be redrawn when moving cursor within windows.
bool win_cursorline_standout(const win_T *wp)
FUNC_ATTR_NONNULL_ALL
{
- return wp->w_p_cul
- || (wp->w_p_cole > 0 && (VIsual_active || !conceal_cursor_line(wp)));
+ return wp->w_p_cul || (wp->w_p_cole > 0 && !conceal_cursor_line(wp));
}
/*
@@ -754,8 +695,11 @@ bool win_cursorline_standout(const win_T *wp)
* mid: from mid_start to mid_end (update inversion or changed text)
* bot: from bot_start to last row (when scrolled up)
*/
-static void win_update(win_T *wp, Providers *providers)
+static void win_update(win_T *wp, DecorProviders *providers)
{
+ bool called_decor_providers = false;
+win_update_start:
+ ;
buf_T *buf = wp->w_buffer;
int type;
int top_end = 0; /* Below last row of the top area that needs
@@ -790,12 +734,6 @@ static void win_update(win_T *wp, Providers *providers)
linenr_T mod_bot = 0;
int save_got_int;
-
- // If we can compute a change in the automatic sizing of the sign column
- // under 'signcolumn=auto:X' and signs currently placed in the buffer, better
- // figuring it out here so we can redraw the entire screen for it.
- buf_signcols(buf);
-
type = wp->w_redr_type;
if (type >= NOT_VALID) {
@@ -803,21 +741,27 @@ static void win_update(win_T *wp, Providers *providers)
wp->w_lines_valid = 0;
}
- // Window is zero-height: nothing to draw.
- if (wp->w_grid.Rows == 0) {
+ // Window is zero-height: Only need to draw the separator
+ if (wp->w_grid.rows == 0) {
+ // draw the horizontal separator below this window
+ draw_hsep_win(wp);
+ draw_sep_connectors_win(wp);
wp->w_redr_type = 0;
return;
}
// Window is zero-width: Only need to draw the separator.
- if (wp->w_grid.Columns == 0) {
+ if (wp->w_grid.cols == 0) {
// draw the vertical separator right of this window
- draw_vsep_win(wp, 0);
+ draw_vsep_win(wp);
+ draw_sep_connectors_win(wp);
wp->w_redr_type = 0;
return;
}
- init_search_hl(wp);
+ redraw_win_signcol(wp);
+
+ init_search_hl(wp, &search_hl);
/* Force redraw when width of 'number' or 'relativenumber' column
* changes. */
@@ -987,7 +931,7 @@ static void win_update(win_T *wp, Providers *providers)
if (mod_top != 0
&& wp->w_topline == mod_top
&& (!wp->w_lines[0].wl_valid
- || wp->w_topline <= wp->w_lines[0].wl_lnum)) {
+ || wp->w_topline == wp->w_lines[0].wl_lnum)) {
// w_topline is the first changed line and window is not scrolled,
// the scrolling from changed lines will be done further down.
} else if (wp->w_lines[0].wl_valid
@@ -1006,7 +950,7 @@ static void win_update(win_T *wp, Providers *providers)
j = 0;
for (ln = wp->w_topline; ln < wp->w_lines[0].wl_lnum; ln++) {
j++;
- if (j >= wp->w_grid.Rows - 2) {
+ if (j >= wp->w_grid.rows - 2) {
break;
}
(void)hasFoldingWin(wp, ln, NULL, &ln, true, NULL);
@@ -1014,13 +958,13 @@ static void win_update(win_T *wp, Providers *providers)
} else {
j = wp->w_lines[0].wl_lnum - wp->w_topline;
}
- if (j < wp->w_grid.Rows - 2) { // not too far off
+ if (j < wp->w_grid.rows - 2) { // not too far off
i = plines_m_win(wp, wp->w_topline, wp->w_lines[0].wl_lnum - 1);
// insert extra lines for previously invisible filler lines
if (wp->w_lines[0].wl_lnum != wp->w_topline) {
i += win_get_fill(wp, wp->w_lines[0].wl_lnum) - wp->w_old_topfill;
}
- if (i != 0 && i < wp->w_grid.Rows - 2) { // less than a screen off
+ if (i != 0 && i < wp->w_grid.rows - 2) { // less than a screen off
// Try to insert the correct number of lines.
// If not the last window, delete the lines at the bottom.
// win_ins_lines may fail when the terminal can't do it.
@@ -1033,8 +977,8 @@ static void win_update(win_T *wp, Providers *providers)
// Move the entries that were scrolled, disable
// the entries for the lines to be redrawn.
- if ((wp->w_lines_valid += j) > wp->w_grid.Rows) {
- wp->w_lines_valid = wp->w_grid.Rows;
+ if ((wp->w_lines_valid += j) > wp->w_grid.rows) {
+ wp->w_lines_valid = wp->w_grid.rows;
}
for (idx = wp->w_lines_valid; idx - j >= 0; idx--) {
wp->w_lines[idx] = wp->w_lines[idx - j];
@@ -1087,7 +1031,7 @@ static void win_update(win_T *wp, Providers *providers)
row -= wp->w_topfill;
if (row > 0) {
win_scroll_lines(wp, 0, -row);
- bot_start = wp->w_grid.Rows - row;
+ bot_start = wp->w_grid.rows - row;
}
if ((row == 0 || bot_start < 999) && wp->w_lines_valid != 0) {
/*
@@ -1103,7 +1047,7 @@ static void win_update(win_T *wp, Providers *providers)
/* stop at line that didn't fit, unless it is still
* valid (no lines deleted) */
if (row > 0 && bot_start + row
- + (int)wp->w_lines[j].wl_size > wp->w_grid.Rows) {
+ + (int)wp->w_lines[j].wl_size > wp->w_grid.rows) {
wp->w_lines_valid = idx + 1;
break;
}
@@ -1128,18 +1072,18 @@ static void win_update(win_T *wp, Providers *providers)
// When starting redraw in the first line, redraw all lines.
if (mid_start == 0) {
- mid_end = wp->w_grid.Rows;
+ mid_end = wp->w_grid.rows;
}
} else {
// Not VALID or INVERTED: redraw all lines.
mid_start = 0;
- mid_end = wp->w_grid.Rows;
+ mid_end = wp->w_grid.rows;
}
if (type == SOME_VALID) {
// SOME_VALID: redraw all lines.
mid_start = 0;
- mid_end = wp->w_grid.Rows;
+ mid_end = wp->w_grid.rows;
type = NOT_VALID;
}
@@ -1215,19 +1159,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
@@ -1305,7 +1270,7 @@ static void win_update(win_T *wp, Providers *providers)
}
}
srow += mid_start;
- mid_end = wp->w_grid.Rows;
+ mid_end = wp->w_grid.rows;
for (; idx < wp->w_lines_valid; idx++) { // find end
if (wp->w_lines[idx].wl_valid
&& wp->w_lines[idx].wl_lnum >= to + 1) {
@@ -1346,37 +1311,27 @@ static void win_update(win_T *wp, Providers *providers)
srow = 0;
lnum = wp->w_topline; // first line shown in window
- decor_redraw_reset(buf, &decor_state);
-
- Providers line_providers;
- kvi_init(line_providers);
+ win_extmark_arr.size = 0;
- linenr_T knownmax = ((wp->w_valid & VALID_BOTLINE)
- ? wp->w_botline
- : (wp->w_topline + wp->w_height_inner));
+ decor_redraw_reset(buf, &decor_state);
- for (size_t k = 0; k < kv_size(*providers); k++) {
- DecorProvider *p = kv_A(*providers, k);
- if (p && p->redraw_win != LUA_NOREF) {
- FIXED_TEMP_ARRAY(args, 4);
- args.items[0] = WINDOW_OBJ(wp->handle);
- args.items[1] = BUFFER_OBJ(buf->handle);
- // TODO(bfredl): we are not using this, but should be first drawn line?
- args.items[2] = INTEGER_OBJ(wp->w_topline-1);
- args.items[3] = INTEGER_OBJ(knownmax);
- if (provider_invoke(p->ns_id, "win", p->redraw_win, args, true)) {
- kvi_push(line_providers, p);
- }
+ DecorProviders line_providers;
+ decor_providers_invoke_win(wp, providers, &line_providers, &provider_err);
+ (void)win_signcol_count(wp); // check if provider changed signcol width
+ if (must_redraw != 0) {
+ must_redraw = 0;
+ if (!called_decor_providers) {
+ called_decor_providers = true;
+ goto win_update_start;
}
}
- win_check_ns_hl(wp);
-
+ bool cursorline_standout = win_cursorline_standout(wp);
for (;;) {
/* stop updating when reached the end of the window (check for _past_
* the end of the window is at the end of the loop) */
- if (row == wp->w_grid.Rows) {
+ if (row == wp->w_grid.rows) {
didline = true;
break;
}
@@ -1417,8 +1372,8 @@ static void win_update(win_T *wp, Providers *providers)
// if lines were inserted or deleted
|| (wp->w_match_head != NULL
&& buf->b_mod_xlines != 0)))))
- || (wp->w_p_cul && (lnum == wp->w_cursor.lnum
- || lnum == wp->w_last_cursorline))) {
+ || (cursorline_standout && lnum == wp->w_cursor.lnum)
+ || lnum == wp->w_last_cursorline) {
if (lnum == mod_top) {
top_to_mod = false;
}
@@ -1481,7 +1436,7 @@ static void win_update(win_T *wp, Providers *providers)
new_rows += plines_win(wp, l, true);
}
j++;
- if (new_rows > wp->w_grid.Rows - row - 2) {
+ if (new_rows > wp->w_grid.rows - row - 2) {
// it's getting too much, must redraw the rest
new_rows = 9999;
break;
@@ -1493,17 +1448,17 @@ static void win_update(win_T *wp, Providers *providers)
* remaining text or scrolling fails, must redraw the
* rest. If scrolling works, must redraw the text
* below the scrolled text. */
- if (row - xtra_rows >= wp->w_grid.Rows - 2) {
+ if (row - xtra_rows >= wp->w_grid.rows - 2) {
mod_bot = MAXLNUM;
} else {
win_scroll_lines(wp, row, xtra_rows);
- bot_start = wp->w_grid.Rows + xtra_rows;
+ bot_start = wp->w_grid.rows + xtra_rows;
}
} else if (xtra_rows > 0) {
/* May scroll text down. If there is not enough
* remaining text of scrolling fails, must redraw the
* rest. */
- if (row + xtra_rows >= wp->w_grid.Rows - 2) {
+ if (row + xtra_rows >= wp->w_grid.rows - 2) {
mod_bot = MAXLNUM;
} else {
win_scroll_lines(wp, row + old_rows, xtra_rows);
@@ -1531,7 +1486,7 @@ static void win_update(win_T *wp, Providers *providers)
wp->w_lines[j] = wp->w_lines[i];
// stop at a line that won't fit
if (x + (int)wp->w_lines[j].wl_size
- > wp->w_grid.Rows) {
+ > wp->w_grid.rows) {
wp->w_lines_valid = j + 1;
break;
}
@@ -1545,8 +1500,8 @@ static void win_update(win_T *wp, Providers *providers)
// move entries in w_lines[] downwards
j -= i;
wp->w_lines_valid += j;
- if (wp->w_lines_valid > wp->w_grid.Rows) {
- wp->w_lines_valid = wp->w_grid.Rows;
+ if (wp->w_lines_valid > wp->w_grid.rows) {
+ wp->w_lines_valid = wp->w_grid.rows;
}
for (i = wp->w_lines_valid; i - j >= idx; i--) {
wp->w_lines[i] = wp->w_lines[i - j];
@@ -1577,13 +1532,13 @@ static void win_update(win_T *wp, Providers *providers)
&& wp->w_lines[idx].wl_lnum == lnum
&& lnum > wp->w_topline
&& !(dy_flags & (DY_LASTLINE | DY_TRUNCATE))
- && srow + wp->w_lines[idx].wl_size > wp->w_grid.Rows
+ && srow + wp->w_lines[idx].wl_size > wp->w_grid.rows
&& win_get_fill(wp, lnum) == 0) {
// This line is not going to fit. Don't draw anything here,
// will draw "@ " lines below.
- row = wp->w_grid.Rows + 1;
+ row = wp->w_grid.rows + 1;
} else {
- prepare_search_hl(wp, lnum);
+ prepare_search_hl(wp, &search_hl, lnum);
// Let the syntax stuff know we skipped a few lines.
if (syntax_last_parsed != 0 && syntax_last_parsed + 1 < lnum
&& syntax_present(wp)) {
@@ -1592,26 +1547,26 @@ static void win_update(win_T *wp, Providers *providers)
// Display one line
row = win_line(wp, lnum, srow,
- foldinfo.fi_lines ? srow : wp->w_grid.Rows,
+ foldinfo.fi_lines ? srow : wp->w_grid.rows,
mod_top == 0, false, foldinfo, &line_providers);
- wp->w_lines[idx].wl_folded = foldinfo.fi_lines != 0;
- wp->w_lines[idx].wl_lastlnum = lnum;
- did_update = DID_LINE;
-
- if (foldinfo.fi_lines > 0) {
- did_update = DID_FOLD;
+ if (foldinfo.fi_lines == 0) {
+ wp->w_lines[idx].wl_folded = false;
+ wp->w_lines[idx].wl_lastlnum = lnum;
+ did_update = DID_LINE;
+ syntax_last_parsed = lnum;
+ } else {
foldinfo.fi_lines--;
+ wp->w_lines[idx].wl_folded = true;
wp->w_lines[idx].wl_lastlnum = lnum + foldinfo.fi_lines;
+ did_update = DID_FOLD;
}
-
- syntax_last_parsed = lnum;
}
wp->w_lines[idx].wl_lnum = lnum;
wp->w_lines[idx].wl_valid = true;
- if (row > wp->w_grid.Rows) { // past end of grid
+ if (row > wp->w_grid.rows) { // past end of grid
// we may need the size of that too long line later on
if (dollar_vcol == -1) {
wp->w_lines[idx].wl_size = plines_win(wp, lnum, true);
@@ -1625,17 +1580,17 @@ static void win_update(win_T *wp, Providers *providers)
idx++;
lnum += foldinfo.fi_lines + 1;
} else {
- if (wp->w_p_rnu) {
- // 'relativenumber' set: The text doesn't need to be drawn, but
- // the number column nearly always does.
+ if (wp->w_p_rnu && wp->w_last_cursor_lnum_rnu != wp->w_cursor.lnum) {
+ // 'relativenumber' set and cursor moved vertically: The
+ // text doesn't need to be drawn, but the number column does.
foldinfo_T info = fold_info(wp, lnum);
- (void)win_line(wp, lnum, srow, wp->w_grid.Rows, true, true,
+ (void)win_line(wp, lnum, srow, wp->w_grid.rows, true, true,
info, &line_providers);
}
// This line does not need to be drawn, advance to the next one.
row += wp->w_lines[idx++].wl_size;
- if (row > wp->w_grid.Rows) { // past end of screen
+ if (row > wp->w_grid.rows) { // past end of screen
break;
}
lnum = wp->w_lines[idx - 1].wl_lastlnum + 1;
@@ -1651,6 +1606,11 @@ static void win_update(win_T *wp, Providers *providers)
* End of loop over all window lines.
*/
+ // Now that the window has been redrawn with the old and new cursor line,
+ // update w_last_cursorline.
+ wp->w_last_cursorline = cursorline_standout ? wp->w_cursor.lnum : 0;
+
+ wp->w_last_cursor_lnum_rnu = wp->w_p_rnu ? wp->w_cursor.lnum : 0;
if (idx > wp->w_lines_valid) {
wp->w_lines_valid = idx;
@@ -1678,39 +1638,41 @@ static void win_update(win_T *wp, Providers *providers)
* Don't overwrite it, it can be edited.
*/
wp->w_botline = lnum + 1;
- } else if (win_get_fill(wp, lnum) >= wp->w_grid.Rows - srow) {
+ } else if (win_get_fill(wp, lnum) >= wp->w_grid.rows - srow) {
// Window ends in filler lines.
wp->w_botline = lnum;
- wp->w_filler_rows = wp->w_grid.Rows - srow;
+ wp->w_filler_rows = wp->w_grid.rows - srow;
} else if (dy_flags & DY_TRUNCATE) { // 'display' has "truncate"
- int scr_row = wp->w_grid.Rows - 1;
+ int scr_row = wp->w_grid.rows - 1;
// Last line isn't finished: Display "@@@" in the last screen line.
- grid_puts_len(&wp->w_grid, (char_u *)"@@", 2, scr_row, 0, at_attr);
+ grid_puts_len(&wp->w_grid, (char_u *)"@@", MIN(wp->w_grid.cols, 2), scr_row, 0, at_attr);
- grid_fill(&wp->w_grid, scr_row, scr_row + 1, 2, wp->w_grid.Columns,
+ grid_fill(&wp->w_grid, scr_row, scr_row + 1, 2, wp->w_grid.cols,
'@', ' ', at_attr);
set_empty_rows(wp, srow);
wp->w_botline = lnum;
} else if (dy_flags & DY_LASTLINE) { // 'display' has "lastline"
+ int start_col = wp->w_grid.cols - 3;
+
// Last line isn't finished: Display "@@@" at the end.
- grid_fill(&wp->w_grid, wp->w_grid.Rows - 1, wp->w_grid.Rows,
- wp->w_grid.Columns - 3, wp->w_grid.Columns, '@', '@', at_attr);
+ grid_fill(&wp->w_grid, wp->w_grid.rows - 1, wp->w_grid.rows,
+ MAX(start_col, 0), wp->w_grid.cols, '@', '@', at_attr);
set_empty_rows(wp, srow);
wp->w_botline = lnum;
} else {
- win_draw_end(wp, '@', ' ', true, srow, wp->w_grid.Rows, HLF_AT);
+ win_draw_end(wp, '@', ' ', true, srow, wp->w_grid.rows, HLF_AT);
wp->w_botline = lnum;
}
} else {
if (eof) { // we hit the end of the file
wp->w_botline = buf->b_ml.ml_line_count + 1;
j = win_get_fill(wp, wp->w_botline);
- if (j > 0 && !wp->w_botfill && row < wp->w_grid.Rows) {
+ if (j > 0 && !wp->w_botfill && row < wp->w_grid.rows) {
// Display filler text below last line. win_line() will check
// for ml_line_count+1 and only draw filler lines
foldinfo_T info = FOLDINFO_INIT;
- row = win_line(wp, wp->w_botline, row, wp->w_grid.Rows,
+ row = win_line(wp, wp->w_botline, row, wp->w_grid.rows,
false, false, info, &line_providers);
}
} else if (dollar_vcol == -1) {
@@ -1719,14 +1681,16 @@ static void win_update(win_T *wp, Providers *providers)
// make sure the rest of the screen is blank
// write the 'eob' character to rows that aren't part of the file.
- win_draw_end(wp, wp->w_p_fcs_chars.eob, ' ', false, row, wp->w_grid.Rows,
+ win_draw_end(wp, wp->w_p_fcs_chars.eob, ' ', false, row, wp->w_grid.rows,
HLF_EOB);
}
kvi_destroy(line_providers);
if (wp->w_redr_type >= REDRAW_TOP) {
- draw_vsep_win(wp, 0);
+ draw_vsep_win(wp);
+ draw_hsep_win(wp);
+ draw_sep_connectors_win(wp);
}
syn_set_timeout(NULL);
@@ -1735,6 +1699,13 @@ static void win_update(win_T *wp, Providers *providers)
wp->w_old_topfill = wp->w_topfill;
wp->w_old_botfill = wp->w_botfill;
+ // Send win_extmarks if needed
+ for (size_t n = 0; n < kv_size(win_extmark_arr); n++) {
+ ui_call_win_extmark(wp->w_grid_alloc.handle, wp->handle,
+ kv_A(win_extmark_arr, n).ns_id, kv_A(win_extmark_arr, n).mark_id,
+ kv_A(win_extmark_arr, n).win_row, kv_A(win_extmark_arr, n).win_col);
+ }
+
if (dollar_vcol == -1) {
/*
* There is a trick with w_botline. If we invalidate it on each
@@ -1766,12 +1737,11 @@ static void win_update(win_T *wp, Providers *providers)
}
}
-
// restore got_int, unless CTRL-C was hit while redrawing
if (!got_int) {
got_int = save_got_int;
}
-} // NOLINT(readability/fn_size)
+}
/// Returns width of the signcolumn that should be used for the whole window
///
@@ -1795,8 +1765,8 @@ static int win_fill_end(win_T *wp, int c1, int c2, int off, int width, int row,
{
int nn = off + width;
- if (nn > wp->w_grid.Columns) {
- nn = wp->w_grid.Columns;
+ if (nn > wp->w_grid.cols) {
+ nn = wp->w_grid.cols;
}
if (wp->w_p_rl) {
@@ -1825,7 +1795,7 @@ static void win_draw_end(win_T *wp, int c1, int c2, bool draw_margin, int row, i
win_hl_attr(wp, HLF_FC));
}
// draw the sign column
- int count = win_signcol_count(wp);
+ int count = wp->w_scwidth;
if (count > 0) {
n = win_fill_end(wp, ' ', ' ', n, win_signcol_width(wp) * count, row,
endrow, win_hl_attr(wp, HLF_SC));
@@ -1845,13 +1815,12 @@ static void win_draw_end(win_T *wp, int c1, int c2, bool draw_margin, int row, i
grid_fill(&wp->w_grid, row, endrow, W_ENDCOL(wp) - 1 - n, W_ENDCOL(wp) - n,
c1, c2, attr);
} else {
- grid_fill(&wp->w_grid, row, endrow, n, wp->w_grid.Columns, c1, c2, attr);
+ grid_fill(&wp->w_grid, row, endrow, n, wp->w_grid.cols, c1, c2, attr);
}
set_empty_rows(wp, row);
}
-
/// Advance **color_cols
///
/// @return true when there are columns to draw.
@@ -1869,7 +1838,7 @@ static int compute_foldcolumn(win_T *wp, int col)
{
int fdc = win_fdccol_count(wp);
int wmw = wp == curwin && p_wmw == 0 ? 1 : p_wmw;
- int wwidth = wp->w_grid.Columns;
+ int wwidth = wp->w_grid.cols;
if (fdc > wwidth - (col + wmw)) {
fdc = wwidth - (col + wmw);
@@ -1883,8 +1852,8 @@ static int compute_foldcolumn(win_T *wp, int col)
static int line_putchar(buf_T *buf, LineState *s, schar_T *dest, int maxcells, bool rl, int vcol)
{
const char_u *p = (char_u *)s->p;
- int cells = utf_ptr2cells(p);
- int c_len = utfc_ptr2len(p);
+ int cells = utf_ptr2cells((char *)p);
+ int c_len = utfc_ptr2len((char *)p);
int u8c, u8cc[MAX_MCO];
if (cells > maxcells) {
return -1;
@@ -1900,7 +1869,7 @@ static int line_putchar(buf_T *buf, LineState *s, schar_T *dest, int maxcells, b
schar_from_ascii(dest[0], *p);
s->prev_c = u8c;
} else {
- if (p_arshape && !p_tbidi && arabic_char(u8c)) {
+ if (p_arshape && !p_tbidi && ARABIC_CHAR(u8c)) {
// Do Arabic shaping.
int pc, pc1, nc;
int pcc[MAX_MCO];
@@ -1911,7 +1880,7 @@ static int line_putchar(buf_T *buf, LineState *s, schar_T *dest, int maxcells, b
if (rl) {
pc = s->prev_c;
pc1 = s->prev_c1;
- nc = utf_ptr2char(p + c_len);
+ nc = utf_ptr2char((char *)p + c_len);
s->prev_c1 = u8cc[0];
} else {
pc = utfc_ptr2char(p + c_len, pcc);
@@ -1934,7 +1903,6 @@ done:
return cells;
}
-
/// Fills the foldcolumn at "p" for window "wp".
/// Only to be called when 'foldcolumn' > 0.
///
@@ -1960,7 +1928,7 @@ static size_t fill_foldcolumn(char_u *p, win_T *wp, foldinfo_T foldinfo, linenr_
level = foldinfo.fi_level;
// If the column is too narrow, we start at the lowest level that
- // fits and use numbers to indicated the depth.
+ // fits and use numbers to indicate the depth.
first_level = level - fdc - closed + 1;
if (first_level < 1) {
first_level = 1;
@@ -1978,7 +1946,7 @@ static size_t fill_foldcolumn(char_u *p, win_T *wp, foldinfo_T foldinfo, linenr_
symbol = '>';
}
- len = utf_char2bytes(symbol, &p[char_counter]);
+ len = utf_char2bytes(symbol, (char *)&p[char_counter]);
char_counter += len;
if (first_level + i >= level) {
i++;
@@ -1992,11 +1960,43 @@ static size_t fill_foldcolumn(char_u *p, win_T *wp, foldinfo_T foldinfo, linenr_
char_counter -= len;
memset(&p[char_counter], ' ', len);
}
- len = utf_char2bytes(wp->w_p_fcs_chars.foldclosed, &p[char_counter]);
+ len = utf_char2bytes(wp->w_p_fcs_chars.foldclosed, (char *)&p[char_counter]);
char_counter += len;
}
- return MAX(char_counter + (fdc-i), (size_t)fdc);
+ return MAX(char_counter + (fdc - i), (size_t)fdc);
+}
+
+static inline void provider_err_virt_text(linenr_T lnum, char *err)
+{
+ Decoration err_decor = DECORATION_INIT;
+ int hl_err = syn_check_group(S_LEN("ErrorMsg"));
+ kv_push(err_decor.virt_text,
+ ((VirtTextChunk){ .text = provider_err,
+ .hl_id = hl_err }));
+ err_decor.virt_text_width = mb_string2cells(err);
+ decor_add_ephemeral(lnum - 1, 0, lnum - 1, 0, &err_decor, 0, 0);
+}
+
+static inline void get_line_number_str(win_T *wp, linenr_T lnum, char_u *buf, size_t buf_len)
+{
+ long num;
+ char *fmt = "%*ld ";
+
+ if (wp->w_p_nu && !wp->w_p_rnu) {
+ // 'number' + 'norelativenumber'
+ num = (long)lnum;
+ } else {
+ // 'relativenumber', don't use negative numbers
+ num = labs((long)get_cursor_rel_lnum(wp, lnum));
+ if (num == 0 && wp->w_p_nu && wp->w_p_rnu) {
+ // 'number' + 'relativenumber'
+ num = lnum;
+ fmt = "%-*ld ";
+ }
+ }
+
+ snprintf((char *)buf, buf_len, fmt, number_width(wp), num);
}
/// Display line "lnum" of window 'wp' on the screen.
@@ -2014,7 +2014,7 @@ static size_t fill_foldcolumn(char_u *p, win_T *wp, foldinfo_T foldinfo, linenr_
///
/// @return the number of last row the line occupies.
static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
- bool number_only, foldinfo_T foldinfo, Providers *providers)
+ bool number_only, foldinfo_T foldinfo, DecorProviders *providers)
{
int c = 0; // init for GCC
long vcol = 0; // virtual column (for tabs)
@@ -2111,13 +2111,6 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
int line_attr_save;
int line_attr_lowprio = 0; // low-priority attribute for the line
int line_attr_lowprio_save;
- matchitem_T *cur; // points to the match list
- match_T *shl; // points to search_hl or a match
- bool shl_flag; // flag to indicate whether search_hl
- // has been processed or not
- bool prevcol_hl_flag; // flag to indicate whether prevcol
- // equals startcol of search_hl or one
- // of the matches
int prev_c = 0; // previous Arabic character
int prev_c1 = 0; // first composing char for prev_c
@@ -2139,13 +2132,13 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
// draw_state: items that are drawn in sequence:
#define WL_START 0 // nothing done yet
-#define WL_CMDLINE WL_START + 1 // cmdline window column
-#define WL_FOLD WL_CMDLINE + 1 // 'foldcolumn'
-#define WL_SIGN WL_FOLD + 1 // column for signs
-#define WL_NR WL_SIGN + 1 // line number
-#define WL_BRI WL_NR + 1 // 'breakindent'
-#define WL_SBR WL_BRI + 1 // 'showbreak' or 'diff'
-#define WL_LINE WL_SBR + 1 // text in the line
+#define WL_CMDLINE (WL_START + 1) // cmdline window column
+#define WL_FOLD (WL_CMDLINE + 1) // 'foldcolumn'
+#define WL_SIGN (WL_FOLD + 1) // column for signs
+#define WL_NR (WL_SIGN + 1) // line number
+#define WL_BRI (WL_NR + 1) // 'breakindent'
+#define WL_SBR (WL_BRI + 1) // 'showbreak' or 'diff'
+#define WL_LINE (WL_SBR + 1) // text in the line
int draw_state = WL_START; // what to draw next
int syntax_flags = 0;
@@ -2177,13 +2170,14 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
row = startrow;
buf_T *buf = wp->w_buffer;
- bool end_fill = (lnum == buf->b_ml.ml_line_count+1);
+ bool end_fill = (lnum == buf->b_ml.ml_line_count + 1);
if (!number_only) {
// To speed up the loop below, set extra_check when there is linebreak,
// trailing white space and/or syntax processing to be done.
extra_check = wp->w_p_lbr;
- if (syntax_present(wp) && !wp->w_s->b_syn_error && !wp->w_s->b_syn_slow) {
+ if (syntax_present(wp) && !wp->w_s->b_syn_error && !wp->w_s->b_syn_slow
+ && !has_fold && !end_fill) {
// Prepare for syntax highlighting in this line. When there is an
// error, stop syntax highlighting.
save_did_emsg = did_emsg;
@@ -2200,37 +2194,14 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
}
}
- has_decor = decor_redraw_line(wp->w_buffer, lnum-1,
- &decor_state);
+ has_decor = decor_redraw_line(buf, lnum - 1, &decor_state);
- for (size_t k = 0; k < kv_size(*providers); k++) {
- DecorProvider *p = kv_A(*providers, k);
- if (p && p->redraw_line != LUA_NOREF) {
- FIXED_TEMP_ARRAY(args, 3);
- args.items[0] = WINDOW_OBJ(wp->handle);
- args.items[1] = BUFFER_OBJ(buf->handle);
- args.items[2] = INTEGER_OBJ(lnum-1);
- if (provider_invoke(p->ns_id, "line", p->redraw_line, args, true)) {
- has_decor = true;
- } else {
- // return 'false' or error: skip rest of this window
- kv_A(*providers, k) = NULL;
- }
-
- win_check_ns_hl(wp);
- }
- }
+ providers_invoke_line(wp, providers, lnum - 1, &has_decor, &provider_err);
if (provider_err) {
- Decoration err_decor = DECORATION_INIT;
- int hl_err = syn_check_group(S_LEN("ErrorMsg"));
- kv_push(err_decor.virt_text,
- ((VirtTextChunk){ .text = provider_err,
- .hl_id = hl_err }));
- err_decor.virt_text_width = mb_string2cells((char_u *)provider_err);
- decor_add_ephemeral(lnum-1, 0, lnum-1, 0, &err_decor);
- provider_err = NULL;
+ provider_err_virt_text(lnum, provider_err);
has_decor = true;
+ provider_err = NULL;
}
if (has_decor) {
@@ -2413,7 +2384,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
if (ae.rgb_fg_color == -1 && ae.cterm_fg_color == 0) {
line_attr_lowprio = cul_attr;
} else {
- if (!(State & INSERT) && bt_quickfix(wp->w_buffer)
+ if (!(State & MODE_INSERT) && bt_quickfix(wp->w_buffer)
&& qf_current_entry(wp) == lnum) {
line_attr = hl_combine_attr(cul_attr, line_attr);
} else {
@@ -2425,12 +2396,11 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
}
area_highlighting = true;
}
- // Update w_last_cursorline even if Visual mode is active.
- wp->w_last_cursorline = wp->w_cursor.lnum;
}
memset(sattrs, 0, sizeof(sattrs));
num_signs = buf_get_signattrs(wp->w_buffer, lnum, sattrs);
+ decor_redraw_signs(buf, lnum - 1, &num_signs, sattrs);
// If this line has a sign with line highlighting set line_attr.
// TODO(bfredl, vigoux): this should not take priority over decoration!
@@ -2490,6 +2460,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
if (wp->w_p_list && !has_fold && !end_fill) {
if (wp->w_p_lcs_chars.space
|| wp->w_p_lcs_chars.multispace != NULL
+ || wp->w_p_lcs_chars.leadmultispace != NULL
|| wp->w_p_lcs_chars.trail
|| wp->w_p_lcs_chars.lead
|| wp->w_p_lcs_chars.nbsp) {
@@ -2504,7 +2475,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
trailcol += (colnr_T)(ptr - line);
}
// find end of leading whitespace
- if (wp->w_p_lcs_chars.lead) {
+ if (wp->w_p_lcs_chars.lead || wp->w_p_lcs_chars.leadmultispace != NULL) {
leadcol = 0;
while (ascii_iswhite(ptr[leadcol])) {
leadcol++;
@@ -2557,7 +2528,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
ptr = prev_ptr;
// If the character fits on the screen, don't need to skip it.
// Except for a TAB.
- if (utf_ptr2cells(ptr) >= c || *ptr == TAB) {
+ if (utf_ptr2cells((char *)ptr) >= c || *ptr == TAB) {
n_skip = v - vcol;
}
}
@@ -2637,69 +2608,12 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
}
}
- /*
- * Handle highlighting the last used search pattern and matches.
- * Do this for both search_hl and the match list.
- */
- cur = wp->w_match_head;
- shl_flag = false;
- while ((cur != NULL || !shl_flag) && !number_only
- && !has_fold && !end_fill) {
- if (!shl_flag) {
- shl = &search_hl;
- shl_flag = true;
- } else {
- shl = &cur->hl; // -V595
- }
- shl->startcol = MAXCOL;
- shl->endcol = MAXCOL;
- shl->attr_cur = 0;
- shl->is_addpos = false;
- v = (ptr - line);
- if (cur != NULL) {
- cur->pos.cur = 0;
- }
- next_search_hl(wp, shl, lnum, (colnr_T)v,
- shl == &search_hl ? NULL : cur);
- if (wp->w_s->b_syn_slow) {
- has_syntax = false;
- }
-
- // Need to get the line again, a multi-line regexp may have made it
- // invalid.
- line = ml_get_buf(wp->w_buffer, lnum, false);
- ptr = line + v;
-
- if (shl->lnum != 0 && shl->lnum <= lnum) {
- if (shl->lnum == lnum) {
- shl->startcol = shl->rm.startpos[0].col;
- } else {
- shl->startcol = 0;
- }
- if (lnum == shl->lnum + shl->rm.endpos[0].lnum
- - shl->rm.startpos[0].lnum) {
- shl->endcol = shl->rm.endpos[0].col;
- } else {
- shl->endcol = MAXCOL;
- }
- // Highlight one character for an empty match.
- if (shl->startcol == shl->endcol) {
- if (line[shl->endcol] != NUL) {
- shl->endcol += utfc_ptr2len(line + shl->endcol);
- } else {
- ++shl->endcol;
- }
- }
- if ((long)shl->startcol < v) { // match at leftcol
- shl->attr_cur = shl->attr;
- search_attr = shl->attr;
- search_attr_from_match = shl != &search_hl;
- }
- area_highlighting = true;
- }
- if (shl != &search_hl && cur != NULL) {
- cur = cur->next;
- }
+ if (!number_only && !has_fold && !end_fill) {
+ v = ptr - line;
+ area_highlighting |= prepare_search_hl_line(wp, lnum, (colnr_T)v,
+ &line, &search_hl, &search_attr,
+ &search_attr_from_match);
+ ptr = line + v; // "line" may have been updated
}
unsigned off = 0; // Offset relative start of line
@@ -2708,7 +2622,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
// Rightleft window: process the text in the normal direction, but put
// it in linebuf_char[off] from right to left. Start at the
// rightmost column of the window.
- col = grid->Columns - 1;
+ col = grid->cols - 1;
off += col;
}
@@ -2723,6 +2637,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
// Repeat for the whole displayed line.
for (;;) {
int has_match_conc = 0; ///< match wants to conceal
+ int decor_conceal = 0;
+
bool did_decrement_ptr = false;
// Skip this quickly when working on the text.
@@ -2771,13 +2687,17 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
draw_state = WL_SIGN;
/* Show the sign column when there are any signs in this
* buffer or when using Netbeans. */
- int count = win_signcol_count(wp);
- if (count > 0) {
+ if (wp->w_scwidth > 0) {
get_sign_display_info(false, wp, lnum, sattrs, row,
- startrow, filler_lines, filler_todo, count,
+ startrow, filler_lines, filler_todo,
&c_extra, &c_final, extra, sizeof(extra),
- &p_extra, &n_extra,
- &char_attr, &draw_state, &sign_idx);
+ &p_extra, &n_extra, &char_attr, sign_idx);
+ sign_idx++;
+ if (sign_idx < wp->w_scwidth) {
+ draw_state = WL_SIGN - 1;
+ } else {
+ sign_idx = 0;
+ }
}
}
@@ -2792,34 +2712,15 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
// in 'lnum', then display the sign instead of the line
// number.
if (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u'
- && num_signs > 0) {
- int count = win_signcol_count(wp);
+ && num_signs > 0 && sign_get_attr(SIGN_TEXT, sattrs, 0, 1)) {
get_sign_display_info(true, wp, lnum, sattrs, row,
- startrow, filler_lines, filler_todo, count,
+ startrow, filler_lines, filler_todo,
&c_extra, &c_final, extra, sizeof(extra),
- &p_extra, &n_extra,
- &char_attr, &draw_state, &sign_idx);
+ &p_extra, &n_extra, &char_attr, sign_idx);
} else {
+ // Draw the line number (empty space after wrapping).
if (row == startrow + filler_lines) {
- // Draw the line number (empty space after wrapping). */
- long num;
- char *fmt = "%*ld ";
-
- if (wp->w_p_nu && !wp->w_p_rnu) {
- // 'number' + 'norelativenumber'
- num = (long)lnum;
- } else {
- // 'relativenumber', don't use negative numbers
- num = labs((long)get_cursor_rel_lnum(wp, lnum));
- if (num == 0 && wp->w_p_nu && wp->w_p_rnu) {
- // 'number' + 'relativenumber'
- num = lnum;
- fmt = "%-*ld ";
- }
- }
-
- snprintf((char *)extra, sizeof(extra),
- fmt, number_width(wp), num);
+ get_line_number_str(wp, lnum, (char_u *)extra, sizeof(extra));
if (wp->w_skipcol > 0) {
for (p_extra = extra; *p_extra == ' '; p_extra++) {
*p_extra = '-';
@@ -2827,9 +2728,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
}
if (wp->w_p_rl) { // reverse line numbers
// like rl_mirror(), but keep the space at the end
- char_u *p2 = skipwhite(extra);
+ char_u *p2 = (char_u *)skipwhite((char *)extra);
p2 = skiptowhite(p2) - 1;
- for (char_u *p1 = skipwhite(extra); p1 < p2; p1++, p2--) {
+ for (char_u *p1 = (char_u *)skipwhite((char *)extra); p1 < p2; p1++, p2--) {
const int t = *p1;
*p1 = *p2;
*p2 = t;
@@ -2837,41 +2738,12 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
}
p_extra = extra;
c_extra = NUL;
- c_final = NUL;
} else {
c_extra = ' ';
- c_final = NUL;
}
+ c_final = NUL;
n_extra = number_width(wp) + 1;
- char_attr = win_hl_attr(wp, HLF_N);
-
- if (wp->w_p_rnu && lnum < wp->w_cursor.lnum) {
- // Use LineNrAbove
- char_attr = win_hl_attr(wp, HLF_LNA);
- }
- if (wp->w_p_rnu && lnum > wp->w_cursor.lnum) {
- // Use LineNrBelow
- char_attr = win_hl_attr(wp, HLF_LNB);
- }
-
- sign_attrs_T *num_sattr = sign_get_attr(SIGN_NUMHL, sattrs, 0, 1);
- if (num_sattr != NULL) {
- // :sign defined with "numhl" highlight.
- char_attr = num_sattr->sat_numhl;
- } else if (wp->w_p_cul
- && lnum == wp->w_cursor.lnum
- && (wp->w_p_culopt_flags & CULOPT_NBR)
- && (row == startrow
- || wp->w_p_culopt_flags & CULOPT_LINE)
- && filler_todo == 0) {
- // When 'cursorline' is set highlight the line number of
- // the current line differently.
- // When 'cursorlineopt' has "screenline" only highlight
- // the line number itself.
- // TODO(vim): Can we use CursorLine instead of CursorLineNr
- // when CursorLineNr isn't set?
- char_attr = win_hl_attr(wp, HLF_CLN);
- }
+ char_attr = get_line_number_attr(wp, lnum, row, startrow, filler_lines, sattrs);
}
}
}
@@ -2932,7 +2804,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
if (wp->w_p_rl) {
n_extra = col + 1;
} else {
- n_extra = grid->Columns - col;
+ n_extra = grid->cols - col;
}
char_attr = 0;
} else if (filler_todo > 0) {
@@ -2947,7 +2819,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
if (wp->w_p_rl) {
n_extra = col + 1;
} else {
- n_extra = grid->Columns - col;
+ n_extra = grid->cols - col;
}
char_attr = win_hl_attr(wp, HLF_DED);
}
@@ -3005,7 +2877,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
if (ae.rgb_fg_color == -1 && ae.cterm_fg_color == 0) {
line_attr_lowprio = cul_attr;
} else {
- if (!(State & INSERT) && bt_quickfix(wp->w_buffer)
+ if (!(State & MODE_INSERT) && bt_quickfix(wp->w_buffer)
&& qf_current_entry(wp) == lnum) {
line_attr = hl_combine_attr(cul_attr, line_attr);
} else {
@@ -3021,15 +2893,15 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
&& vcol >= (long)wp->w_virtcol)
|| (number_only && draw_state > WL_NR))
&& filler_todo <= 0) {
- draw_virt_text(buf, win_col_offset, &col, grid->Columns);
- grid_put_linebuf(grid, row, 0, col, -grid->Columns, wp->w_p_rl, wp,
+ draw_virt_text(wp, buf, win_col_offset, &col, grid->cols, row);
+ grid_put_linebuf(grid, row, 0, col, -grid->cols, wp->w_p_rl, wp,
wp->w_hl_attr_normal, false);
// Pretend we have finished updating the window. Except when
// 'cursorcolumn' is set.
if (wp->w_p_cuc) {
row = wp->w_cline_row + wp->w_cline_height;
} else {
- row = grid->Rows;
+ row = grid->rows;
}
break;
}
@@ -3057,19 +2929,19 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
if (draw_state == WL_LINE
&& has_fold
- && col < grid->Columns
+ && col < grid->cols
&& n_extra == 0
&& row == startrow) {
// fill rest of line with 'fold'
c_extra = wp->w_p_fcs_chars.fold;
c_final = NUL;
- n_extra = wp->w_p_rl ? (col + 1) : (grid->Columns - col);
+ n_extra = wp->w_p_rl ? (col + 1) : (grid->cols - col);
}
if (draw_state == WL_LINE
&& has_fold
- && col >= grid->Columns
+ && col >= grid->cols
&& n_extra != 0
&& row == startrow) {
// Truncate the folding.
@@ -3080,7 +2952,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
// handle Visual or match highlighting in this line
if (vcol == fromcol
|| (vcol + 1 == fromcol && n_extra == 0
- && utf_ptr2cells(ptr) > 1)
+ && utf_ptr2cells((char *)ptr) > 1)
|| ((int)vcol_prev == fromcol_prev
&& vcol_prev < vcol // not at margin
&& vcol < tocol)) {
@@ -3096,115 +2968,13 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
}
if (!n_extra) {
- /*
- * Check for start/end of search pattern match.
- * After end, check for start/end of next match.
- * When another match, have to check for start again.
- * Watch out for matching an empty string!
- * Do this for 'search_hl' and the match list (ordered by
- * priority).
- */
+ // Check for start/end of 'hlsearch' and other matches.
+ // After end, check for start/end of next match.
+ // When another match, have to check for start again.
v = (ptr - line);
- cur = wp->w_match_head;
- shl_flag = false;
- while (cur != NULL || !shl_flag) {
- if (!shl_flag
- && (cur == NULL || cur->priority > SEARCH_HL_PRIORITY)) {
- shl = &search_hl;
- shl_flag = true;
- } else {
- shl = &cur->hl;
- }
- if (cur != NULL) {
- cur->pos.cur = 0;
- }
- bool pos_inprogress = true; // mark that a position match search is
- // in progress
- while (shl->rm.regprog != NULL
- || (cur != NULL && pos_inprogress)) {
- if (shl->startcol != MAXCOL
- && v >= (long)shl->startcol
- && v < (long)shl->endcol) {
- int tmp_col = v + utfc_ptr2len(ptr);
-
- if (shl->endcol < tmp_col) {
- shl->endcol = tmp_col;
- }
- shl->attr_cur = shl->attr;
- // Match with the "Conceal" group results in hiding
- // the match.
- if (cur != NULL
- && shl != &search_hl
- && syn_name2id("Conceal") == cur->hlg_id) {
- has_match_conc = v == (long)shl->startcol ? 2 : 1;
- match_conc = cur->conceal_char;
- } else {
- has_match_conc = 0;
- }
- } else if (v == (long)shl->endcol) {
- shl->attr_cur = 0;
-
- next_search_hl(wp, shl, lnum, (colnr_T)v,
- shl == &search_hl ? NULL : cur);
- pos_inprogress = !(cur == NULL || cur->pos.cur == 0);
-
- // Need to get the line again, a multi-line regexp
- // may have made it invalid.
- line = ml_get_buf(wp->w_buffer, lnum, false);
- ptr = line + v;
-
- if (shl->lnum == lnum) {
- shl->startcol = shl->rm.startpos[0].col;
- if (shl->rm.endpos[0].lnum == 0) {
- shl->endcol = shl->rm.endpos[0].col;
- } else {
- shl->endcol = MAXCOL;
- }
-
- if (shl->startcol == shl->endcol) {
- // highlight empty match, try again after it
- shl->endcol += utfc_ptr2len(line + shl->endcol);
- }
-
- // Loop to check if the match starts at the
- // current position
- continue;
- }
- }
- break;
- }
- if (shl != &search_hl && cur != NULL) {
- cur = cur->next;
- }
- }
-
- /* Use attributes from match with highest priority among
- * 'search_hl' and the match list. */
- search_attr_from_match = false;
- search_attr = search_hl.attr_cur;
- cur = wp->w_match_head;
- shl_flag = false;
- while (cur != NULL || !shl_flag) {
- if (!shl_flag
- && (cur == NULL || cur->priority > SEARCH_HL_PRIORITY)) {
- shl = &search_hl;
- shl_flag = true;
- } else {
- shl = &cur->hl;
- }
- if (shl->attr_cur != 0) {
- search_attr = shl->attr_cur;
- search_attr_from_match = shl != &search_hl;
- }
- if (shl != &search_hl && cur != NULL) {
- cur = cur->next;
- }
- }
- // Only highlight one character after the last column.
- if (*ptr == NUL
- && (wp->w_p_list && lcs_eol_one == -1)) {
- search_attr = 0;
- }
+ search_attr = update_search_hl(wp, lnum, (colnr_T)v, &line, &search_hl, &has_match_conc,
+ &match_conc, lcs_eol_one, &search_attr_from_match);
+ ptr = line + v; // "line" may have been changed
// Do not allow a conceal over EOL otherwise EOL will be missed
// and bad things happen.
@@ -3237,6 +3007,10 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
if (area_attr != 0) {
char_attr = hl_combine_attr(line_attr, area_attr);
+ if (!highlight_match) {
+ // let search highlight show in Visual area if possible
+ char_attr = hl_combine_attr(search_attr, char_attr);
+ }
} else if (search_attr != 0) {
char_attr = hl_combine_attr(line_attr, search_attr);
}
@@ -3282,7 +3056,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
mb_c = c;
// If the UTF-8 character is more than one byte:
// Decode it into "mb_c".
- mb_l = utfc_ptr2len(p_extra);
+ mb_l = utfc_ptr2len((char *)p_extra);
mb_utf8 = false;
if (mb_l > n_extra) {
mb_l = 1;
@@ -3296,7 +3070,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
}
// If a double-width char doesn't fit display a '>' in the last column.
- if ((wp->w_p_rl ? (col <= 0) : (col >= grid->Columns - 1))
+ if ((wp->w_p_rl ? (col <= 0) : (col >= grid->cols - 1))
&& utf_char2cells(mb_c) == 2) {
c = '>';
mb_c = c;
@@ -3335,7 +3109,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
mb_c = c;
// If the UTF-8 character is more than one byte: Decode it
// into "mb_c".
- mb_l = utfc_ptr2len(ptr);
+ mb_l = utfc_ptr2len((char *)ptr);
mb_utf8 = false;
if (mb_l > 1) {
mb_c = utfc_ptr2char(ptr, u8cc);
@@ -3383,7 +3157,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
}
} else if (mb_l == 0) { // at the NUL at end-of-line
mb_l = 1;
- } else if (p_arshape && !p_tbidi && arabic_char(mb_c)) {
+ } else if (p_arshape && !p_tbidi && ARABIC_CHAR(mb_c)) {
// Do Arabic shaping.
int pc, pc1, nc;
int pcc[MAX_MCO];
@@ -3393,7 +3167,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
if (wp->w_p_rl) {
pc = prev_c;
pc1 = prev_c1;
- nc = utf_ptr2char(ptr + mb_l);
+ nc = utf_ptr2char((char *)ptr + mb_l);
prev_c1 = u8cc[0];
} else {
pc = utfc_ptr2char(ptr + mb_l, pcc);
@@ -3410,7 +3184,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
// last column; the character is displayed at the start of the
// next line.
if ((wp->w_p_rl ? (col <= 0) :
- (col >= grid->Columns - 1))
+ (col >= grid->cols - 1))
&& utf_char2cells(mb_c) == 2) {
c = '>';
mb_c = c;
@@ -3465,6 +3239,10 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
did_emsg = save_did_emsg;
}
+ if (wp->w_s->b_syn_slow) {
+ has_syntax = false;
+ }
+
// Need to get the line again, a multi-line regexp may
// have made it invalid.
line = ml_get_buf(wp->w_buffer, lnum, false);
@@ -3526,7 +3304,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
/* In Insert mode only highlight a word that
* doesn't touch the cursor. */
if (spell_hlf != HLF_COUNT
- && (State & INSERT) != 0
+ && (State & MODE_INSERT)
&& wp->w_cursor.lnum == lnum
&& wp->w_cursor.col >=
(colnr_T)(prev_ptr - line)
@@ -3578,7 +3356,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
if (has_decor && v > 0) {
bool selected = (area_active || (area_highlighting && noinvcur
&& (colnr_T)vcol == wp->w_virtcol));
- int extmark_attr = decor_redraw_col(wp->w_buffer, (colnr_T)v-1, off,
+ int extmark_attr = decor_redraw_col(wp->w_buffer, (colnr_T)v - 1, off,
selected, &decor_state);
if (extmark_attr != 0) {
if (!attr_pri) {
@@ -3587,6 +3365,11 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
char_attr = hl_combine_attr(extmark_attr, char_attr);
}
}
+
+ decor_conceal = decor_state.conceal;
+ if (decor_conceal && decor_state.conceal_char) {
+ decor_conceal = 2; // really??
+ }
}
// Found last space before word: check for line break.
@@ -3606,7 +3389,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
}
}
- if (c == TAB && n_extra + col > grid->Columns) {
+ if (c == TAB && n_extra + col > grid->cols) {
n_extra = tabstop_padding(vcol, wp->w_buffer->b_p_ts,
wp->w_buffer->b_p_vts_array) - 1;
}
@@ -3664,10 +3447,22 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
}
}
- if ((trailcol != MAXCOL && ptr > line + trailcol && c == ' ')
- || (leadcol != 0 && ptr < line + leadcol && c == ' ')) {
- c = (ptr > line + trailcol) ? wp->w_p_lcs_chars.trail
- : wp->w_p_lcs_chars.lead;
+ if (c == ' ' && ((trailcol != MAXCOL && ptr > line + trailcol)
+ || (leadcol != 0 && ptr < line + leadcol))) {
+ if (leadcol != 0 && in_multispace && ptr < line + leadcol
+ && wp->w_p_lcs_chars.leadmultispace != NULL) {
+ c = wp->w_p_lcs_chars.leadmultispace[multispace_pos++];
+ if (wp->w_p_lcs_chars.leadmultispace[multispace_pos] == NUL) {
+ multispace_pos = 0;
+ }
+ } else if (ptr > line + trailcol && wp->w_p_lcs_chars.trail) {
+ c = wp->w_p_lcs_chars.trail;
+ } else if (ptr < line + leadcol && wp->w_p_lcs_chars.lead) {
+ c = wp->w_p_lcs_chars.lead;
+ } else if (leadcol != 0 && wp->w_p_lcs_chars.space) {
+ c = wp->w_p_lcs_chars.space;
+ }
+
n_attr = 1;
extra_attr = win_hl_attr(wp, HLF_0);
saved_attr2 = char_attr; // save current attr
@@ -3720,10 +3515,13 @@ 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;
}
@@ -3740,13 +3538,11 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
}
int lcs = wp->w_p_lcs_chars.tab2;
- // if tab3 is given, need to change the char
- // for tab
+ // 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;
}
- utf_char2bytes(lcs, p);
- p += utf_char2len(lcs);
+ p += utf_char2bytes(lcs, (char *)p);
n_extra += utf_char2len(lcs) - (saved_nextra > 0 ? 1 : 0);
}
p_extra = p_extra_free;
@@ -3808,7 +3604,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
|| ((fromcol >= 0 || fromcol_prev >= 0)
&& tocol > vcol
&& VIsual_mode != Ctrl_V
- && (wp->w_p_rl ? (col >= 0) : (col < grid->Columns))
+ && (wp->w_p_rl ? (col >= 0) : (col < grid->cols))
&& !(noinvcur
&& lnum == wp->w_cursor.lnum
&& (colnr_T)vcol == wp->w_virtcol)))
@@ -3880,28 +3676,33 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
&& virtual_active()
&& tocol != MAXCOL
&& vcol < tocol
- && (wp->w_p_rl ? (col >= 0) : (col < grid->Columns))) {
+ && (wp->w_p_rl ? (col >= 0) : (col < grid->cols))) {
c = ' ';
ptr--; // put it back at the NUL
}
}
if (wp->w_p_cole > 0
- && (wp != curwin || lnum != wp->w_cursor.lnum
- || conceal_cursor_line(wp))
- && ((syntax_flags & HL_CONCEAL) != 0 || has_match_conc > 0)
- && !(lnum_in_visual_area
- && vim_strchr(wp->w_p_cocu, 'v') == NULL)) {
+ && (wp != curwin || lnum != wp->w_cursor.lnum || conceal_cursor_line(wp))
+ && ((syntax_flags & HL_CONCEAL) != 0 || has_match_conc > 0 || decor_conceal > 0)
+ && !(lnum_in_visual_area && vim_strchr((char *)wp->w_p_cocu, 'v') == NULL)) {
char_attr = conceal_attr;
- if ((prev_syntax_id != syntax_seqnr || has_match_conc > 1)
+ if (((prev_syntax_id != syntax_seqnr && (syntax_flags & HL_CONCEAL) != 0)
+ || has_match_conc > 1 || decor_conceal > 1)
&& (syn_get_sub_char() != NUL
|| (has_match_conc && match_conc)
+ || (decor_conceal && decor_state.conceal_char)
|| wp->w_p_cole == 1)
&& wp->w_p_cole != 3) {
// First time at this concealed item: display one
// character.
if (has_match_conc && match_conc) {
c = match_conc;
+ } else if (decor_conceal && decor_state.conceal_char) {
+ c = decor_state.conceal_char;
+ if (decor_state.conceal_attr) {
+ char_attr = decor_state.conceal_attr;
+ }
} else if (syn_get_sub_char() != NUL) {
c = syn_get_sub_char();
} else if (wp->w_p_lcs_chars.conceal != NUL) {
@@ -3957,7 +3758,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
&& conceal_cursor_line(wp)
&& (int)wp->w_virtcol <= vcol + n_skip) {
if (wp->w_p_rl) {
- wp->w_wcol = grid->Columns - col + boguscols - 1;
+ wp->w_wcol = grid->cols - col + boguscols - 1;
} else {
wp->w_wcol = col - boguscols;
}
@@ -4006,30 +3807,15 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
// At end of the text line or just after the last character.
if (c == NUL && eol_hl_off == 0) {
- long prevcol = (ptr - line) - 1;
-
- // we're not really at that column when skipping some text
- if ((long)(wp->w_p_wrap ? wp->w_skipcol : wp->w_leftcol) > prevcol) {
- prevcol++;
- }
+ // flag to indicate whether prevcol equals startcol of search_hl or
+ // one of the matches
+ bool prevcol_hl_flag = get_prevcol_hl_flag(wp, &search_hl,
+ (long)(ptr - line) - 1);
// Invert at least one char, used for Visual and empty line or
// highlight match at end of line. If it's beyond the last
// char on the screen, just overwrite that one (tricky!) Not
// needed when a '$' was displayed for 'list'.
- prevcol_hl_flag = false;
- if (!search_hl.is_addpos && prevcol == (long)search_hl.startcol) {
- prevcol_hl_flag = true;
- } else {
- cur = wp->w_match_head;
- while (cur != NULL) {
- if (!cur->hl.is_addpos && prevcol == (long)cur->hl.startcol) {
- prevcol_hl_flag = true;
- break;
- }
- cur = cur->next;
- }
- }
if (wp->w_p_lcs_chars.eol == lcs_eol_one
&& ((area_attr != 0 && vcol == fromcol
&& (VIsual_mode != Ctrl_V
@@ -4044,7 +3830,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
n = 1;
}
} else {
- if (col >= grid->Columns) {
+ if (col >= grid->cols) {
n = -1;
}
}
@@ -4060,25 +3846,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
if (area_attr == 0 && !has_fold) {
// Use attributes from match with highest priority among
// 'search_hl' and the match list.
- char_attr = search_hl.attr;
- cur = wp->w_match_head;
- shl_flag = false;
- while (cur != NULL || !shl_flag) {
- if (!shl_flag
- && (cur == NULL || cur->priority > SEARCH_HL_PRIORITY)) {
- shl = &search_hl;
- shl_flag = true;
- } else {
- shl = &cur->hl;
- }
- if ((ptr - line) - 1 == (long)shl->startcol
- && (shl == &search_hl || !shl->is_addpos)) {
- char_attr = shl->attr;
- }
- if (shl != &search_hl && cur != NULL) {
- cur = cur->next;
- }
- }
+ get_search_match_hl(wp, &search_hl, (long)(ptr - line), &char_attr);
}
int eol_attr = char_attr;
@@ -4130,7 +3898,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 <
- (long)grid->Columns * (row - startrow + 1) + v
+ (long)grid->cols * (row - startrow + 1) + v
&& lnum != wp->w_cursor.lnum)
|| draw_color_col || line_attr_lowprio || line_attr
|| diff_hlf != (hlf_T)0 || has_virttext)) {
@@ -4168,7 +3936,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
int col_stride = wp->w_p_rl ? -1 : 1;
- while (wp->w_p_rl ? col >= 0 : col < grid->Columns) {
+ while (wp->w_p_rl ? col >= 0 : col < grid->cols) {
schar_from_ascii(linebuf_char[off], ' ');
col += col_stride;
if (draw_color_col) {
@@ -4204,7 +3972,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
// terminal buffers may need to highlight beyond the end of the
// logical line
int n = wp->w_p_rl ? -1 : 1;
- while (col >= 0 && col < grid->Columns) {
+ while (col >= 0 && col < grid->cols) {
schar_from_ascii(linebuf_char[off], ' ');
linebuf_attr[off] = vcol >= TERM_ATTRS_MAX ? 0 : term_attrs[vcol];
off += n;
@@ -4213,8 +3981,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
}
}
- draw_virt_text(buf, win_col_offset, &col, grid->Columns);
- grid_put_linebuf(grid, row, 0, col, grid->Columns, wp->w_p_rl, wp,
+ draw_virt_text(wp, buf, win_col_offset, &col, grid->cols, row);
+ grid_put_linebuf(grid, row, 0, col, grid->cols, wp->w_p_rl, wp,
wp->w_hl_attr_normal, false);
row++;
@@ -4235,10 +4003,11 @@ 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
- && (wp->w_p_rl ? col == 0 : col == grid->Columns - 1)
+ && (wp->w_p_rl ? col == 0 : col == grid->cols - 1)
&& !has_fold
&& (*ptr != NUL
|| lcs_eol_one > 0
@@ -4373,7 +4142,6 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
n_attr = 0;
}
-
if (utf_char2cells(mb_c) > 1) {
// Need to fill two screen columns.
if (wp->w_p_rl) {
@@ -4428,9 +4196,10 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
* At end of screen line and there is more to come: Display the line
* so far. If there is no more to display it is caught above.
*/
- if ((wp->w_p_rl ? (col < 0) : (col >= grid->Columns))
+ if ((wp->w_p_rl ? (col < 0) : (col >= grid->cols))
&& 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)
@@ -4440,7 +4209,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
&& filler_todo <= 0 // Not drawing diff filler lines.
&& lcs_eol_one != -1 // Haven't printed the lcs_eol character.
&& row != endrow - 1 // Not the last line being displayed.
- && (grid->Columns == Columns // Window spans the width of the screen,
+ && (grid->cols == Columns // Window spans the width of the screen,
|| ui_has(kUIMultigrid)) // or has dedicated grid.
&& !wp->w_p_rl; // Not right-to-left.
@@ -4452,21 +4221,21 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
assert(i >= 0);
int offset = kv_A(virt_lines, i).left_col ? 0 : win_col_offset;
draw_virt_text_item(buf, offset, kv_A(virt_lines, i).line,
- kHlModeReplace, grid->Columns, offset);
+ kHlModeReplace, grid->cols, offset);
}
} else {
- draw_virt_text(buf, win_col_offset, &draw_col, grid->Columns);
+ draw_virt_text(wp, buf, win_col_offset, &draw_col, grid->cols, row);
}
- grid_put_linebuf(grid, row, 0, draw_col, grid->Columns, wp->w_p_rl,
+ grid_put_linebuf(grid, row, 0, draw_col, grid->cols, wp->w_p_rl,
wp, wp->w_hl_attr_normal, wrap);
if (wrap) {
ScreenGrid *current_grid = grid;
int current_row = row, dummy_col = 0; // dummy_col unused
- screen_adjust_grid(&current_grid, &current_row, &dummy_col);
+ grid_adjust(&current_grid, &current_row, &dummy_col);
// Force a redraw of the first column of the next line.
- current_grid->attrs[current_grid->line_offset[current_row+1]] = -1;
+ current_grid->attrs[current_grid->line_offset[current_row + 1]] = -1;
// Remember that the line wraps, used for modeless copy.
current_grid->line_wraps[current_row] = true;
@@ -4485,7 +4254,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
// When the window is too narrow draw all "@" lines.
if (draw_state != WL_LINE && filler_todo <= 0) {
- win_draw_end(wp, '@', ' ', true, row, wp->w_grid.Rows, HLF_AT);
+ win_draw_end(wp, '@', ' ', true, row, wp->w_grid.rows, HLF_AT);
row = endrow;
}
@@ -4498,7 +4267,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
col = 0;
off = 0;
if (wp->w_p_rl) {
- col = grid->Columns - 1; // col is not used if breaking!
+ col = grid->cols - 1; // col is not used if breaking!
off += col;
}
@@ -4524,7 +4293,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
} // for every character in the line
// After an empty line check first word for capital.
- if (*skipwhite(line) == NUL) {
+ if (*skipwhite((char *)line) == NUL) {
capcol_lnum = lnum + 1;
cap_col = 0;
}
@@ -4534,14 +4303,15 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
return row;
}
-void draw_virt_text(buf_T *buf, int col_off, int *end_col, int max_col)
+void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int max_col, int win_row)
{
DecorState *state = &decor_state;
int right_pos = max_col;
bool do_eol = state->eol_col > -1;
for (size_t i = 0; i < kv_size(state->active); i++) {
DecorRange *item = &kv_A(state->active, i);
- if (!(item->start_row == state->row && kv_size(item->decor.virt_text))) {
+ if (!(item->start_row == state->row
+ && (kv_size(item->decor.virt_text) || item->decor.ui_watched))) {
continue;
}
if (item->win_col == -1) {
@@ -4551,18 +4321,26 @@ void draw_virt_text(buf_T *buf, int col_off, int *end_col, int max_col)
} else if (item->decor.virt_text_pos == kVTEndOfLine && do_eol) {
item->win_col = state->eol_col;
} else if (item->decor.virt_text_pos == kVTWinCol) {
- item->win_col = MAX(item->decor.col+col_off, 0);
+ item->win_col = MAX(item->decor.col + col_off, 0);
}
}
if (item->win_col < 0) {
continue;
}
-
- int col = draw_virt_text_item(buf, item->win_col, item->decor.virt_text,
- item->decor.hl_mode, max_col, item->win_col-col_off);
+ int col;
+ if (item->decor.ui_watched) {
+ // send mark position to UI
+ col = item->win_col;
+ WinExtmark m = { item->ns_id, item->mark_id, win_row, col };
+ kv_push(win_extmark_arr, m);
+ }
+ if (kv_size(item->decor.virt_text)) {
+ col = draw_virt_text_item(buf, item->win_col, item->decor.virt_text,
+ item->decor.hl_mode, max_col, item->win_col - col_off);
+ }
item->win_col = -2; // deactivate
if (item->decor.virt_text_pos == kVTEndOfLine && do_eol) {
- state->eol_col = col+1;
+ state->eol_col = col + 1;
}
*end_col = MAX(*end_col, col);
@@ -4593,6 +4371,9 @@ static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode,
break;
}
}
+ if (!*s.p) {
+ continue;
+ }
int attr;
bool through = false;
if (hl_mode == kHlModeCombine) {
@@ -4605,7 +4386,7 @@ static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode,
}
schar_T dummy[2];
int cells = line_putchar(buf, &s, through ? dummy : &linebuf_char[col],
- max_col-col, false, vcol);
+ max_col - col, false, vcol);
// if we failed to emit a char, we still need to advance
cells = MAX(cells, 1);
@@ -4617,25 +4398,6 @@ static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode,
return col;
}
-/// Determine if dedicated window grid should be used or the default_grid
-///
-/// If UI did not request multigrid support, draw all windows on the
-/// default_grid.
-///
-/// NB: this function can only been used with window grids in a context where
-/// win_grid_alloc already has been called!
-///
-/// If the default_grid is used, adjust window relative positions to global
-/// screen positions.
-void screen_adjust_grid(ScreenGrid **grid, int *row_off, int *col_off)
-{
- if ((*grid)->target) {
- *row_off += (*grid)->row_offset;
- *col_off += (*grid)->col_offset;
- *grid = (*grid)->target;
- }
-}
-
// Return true if CursorLineSign highlight is to be used.
static bool use_cursor_line_sign(win_T *wp, linenr_T lnum)
{
@@ -4644,18 +4406,65 @@ static bool use_cursor_line_sign(win_T *wp, linenr_T lnum)
&& (wp->w_p_culopt_flags & CULOPT_NBR);
}
+/// Return true if CursorLineNr highlight is to be used for the number column.
+///
+/// - 'cursorline' must be set
+/// - lnum must be the cursor line
+/// - 'cursorlineopt' has "number"
+/// - don't highlight filler lines (when in diff mode)
+/// - When line is wrapped and 'cursorlineopt' does not have "line", only highlight the line number
+/// itself on the first screenline of the wrapped line, otherwise highlight the number column of
+/// all screenlines of the wrapped line.
+static bool use_cursor_line_nr(win_T *wp, linenr_T lnum, int row, int startrow, int filler_lines)
+{
+ return wp->w_p_cul
+ && lnum == wp->w_cursor.lnum
+ && (wp->w_p_culopt_flags & CULOPT_NBR)
+ && (row == startrow + filler_lines
+ || (row > startrow + filler_lines
+ && (wp->w_p_culopt_flags & CULOPT_LINE)));
+}
+
+static int get_line_number_attr(win_T *wp, linenr_T lnum, int row, int startrow, int filler_lines,
+ sign_attrs_T *sattrs)
+{
+ sign_attrs_T *num_sattr = sign_get_attr(SIGN_NUMHL, sattrs, 0, 1);
+ if (num_sattr != NULL) {
+ // :sign defined with "numhl" highlight.
+ return num_sattr->sat_numhl;
+ }
+
+ if (wp->w_p_rnu) {
+ if (lnum < wp->w_cursor.lnum) {
+ // Use LineNrAbove
+ return win_hl_attr(wp, HLF_LNA);
+ }
+ if (lnum > wp->w_cursor.lnum) {
+ // Use LineNrBelow
+ return win_hl_attr(wp, HLF_LNB);
+ }
+ }
+
+ if (use_cursor_line_nr(wp, lnum, row, startrow, filler_lines)) {
+ // TODO(vim): Can we use CursorLine instead of CursorLineNr
+ // when CursorLineNr isn't set?
+ return win_hl_attr(wp, HLF_CLN);
+ }
+
+ return win_hl_attr(wp, HLF_N);
+}
+
// Get information needed to display the sign in line 'lnum' in window 'wp'.
// If 'nrcol' is TRUE, the sign is going to be displayed in the number column.
// Otherwise the sign is going to be displayed in the sign column.
//
// @param count max number of signs
// @param[out] n_extrap number of characters from pp_extra to display
-// @param[in, out] sign_idxp Index of the displayed sign
+// @param sign_idxp Index of the displayed sign
static void get_sign_display_info(bool nrcol, win_T *wp, linenr_T lnum, sign_attrs_T sattrs[],
int row, int startrow, int filler_lines, int filler_todo,
- int count, int *c_extrap, int *c_finalp, char_u *extra,
- size_t extra_size, char_u **pp_extra, int *n_extrap,
- int *char_attrp, int *draw_statep, int *sign_idxp)
+ int *c_extrap, int *c_finalp, char_u *extra, size_t extra_size,
+ char_u **pp_extra, int *n_extrap, int *char_attrp, int sign_idx)
{
// Draw cells with the sign value or blank.
*c_extrap = ' ';
@@ -4672,7 +4481,7 @@ static void get_sign_display_info(bool nrcol, win_T *wp, linenr_T lnum, sign_att
}
if (row == startrow + filler_lines && filler_todo <= 0) {
- sign_attrs_T *sattr = sign_get_attr(SIGN_TEXT, sattrs, *sign_idxp, count);
+ sign_attrs_T *sattr = sign_get_attr(SIGN_TEXT, sattrs, sign_idx, wp->w_scwidth);
if (sattr != NULL) {
*pp_extra = sattr->sat_text;
if (*pp_extra != NULL) {
@@ -4694,10 +4503,10 @@ static void get_sign_display_info(bool nrcol, win_T *wp, linenr_T lnum, sign_att
// TODO(oni-link): Is sign text already extended to
// full cell width?
- assert((size_t)win_signcol_width(wp) >= mb_string2cells(*pp_extra));
+ assert((size_t)win_signcol_width(wp) >= mb_string2cells((char *)(*pp_extra)));
// symbol(s) bytes + (filling spaces) (one byte each)
*n_extrap = symbol_blen +
- (win_signcol_width(wp) - mb_string2cells(*pp_extra));
+ (win_signcol_width(wp) - mb_string2cells((char *)(*pp_extra)));
assert(extra_size > (size_t)symbol_blen);
memset(extra, ' ', extra_size);
@@ -4715,205 +4524,6 @@ static void get_sign_display_info(bool nrcol, win_T *wp, linenr_T lnum, sign_att
}
}
}
-
- (*sign_idxp)++;
- if (*sign_idxp < count) {
- *draw_statep = WL_SIGN - 1;
- } else {
- *sign_idxp = 0;
- }
-}
-
-
-/*
- * Check whether the given character needs redrawing:
- * - the (first byte of the) character is different
- * - the attributes are different
- * - the character is multi-byte and the next byte is different
- * - the character is two cells wide and the second cell differs.
- */
-static int grid_char_needs_redraw(ScreenGrid *grid, int off_from, int off_to, int cols)
-{
- return (cols > 0
- && ((schar_cmp(linebuf_char[off_from], grid->chars[off_to])
- || linebuf_attr[off_from] != grid->attrs[off_to]
- || (line_off2cells(linebuf_char, off_from, off_from + cols) > 1
- && schar_cmp(linebuf_char[off_from + 1],
- grid->chars[off_to + 1])))
- || rdb_flags & RDB_NODELTA));
-}
-
-/// Move one buffered line to the window grid, but only the characters that
-/// have actually changed. Handle insert/delete character.
-/// "coloff" gives the first column on the grid for this line.
-/// "endcol" gives the columns where valid characters are.
-/// "clear_width" is the width of the window. It's > 0 if the rest of the line
-/// needs to be cleared, negative otherwise.
-/// "rlflag" is TRUE in a rightleft window:
-/// When TRUE and "clear_width" > 0, clear columns 0 to "endcol"
-/// When FALSE and "clear_width" > 0, clear columns "endcol" to "clear_width"
-/// If "wrap" is true, then hint to the UI that "row" contains a line
-/// which has wrapped into the next row.
-static void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int clear_width,
- int rlflag, win_T *wp, int bg_attr, bool wrap)
-{
- unsigned off_from;
- unsigned off_to;
- unsigned max_off_from;
- unsigned max_off_to;
- int col = 0;
- bool redraw_this; // Does character need redraw?
- bool redraw_next; // redraw_this for next character
- bool clear_next = false;
- int char_cells; // 1: normal char
- // 2: occupies two display cells
- int start_dirty = -1, end_dirty = 0;
-
- // TODO(bfredl): check all callsites and eliminate
- // Check for illegal row and col, just in case
- if (row >= grid->Rows) {
- row = grid->Rows - 1;
- }
- if (endcol > grid->Columns) {
- endcol = grid->Columns;
- }
-
- screen_adjust_grid(&grid, &row, &coloff);
-
- // Safety check. Avoids clang warnings down the call stack.
- if (grid->chars == NULL || row >= grid->Rows || coloff >= grid->Columns) {
- DLOG("invalid state, skipped");
- return;
- }
-
- off_from = 0;
- off_to = grid->line_offset[row] + coloff;
- max_off_from = linebuf_size;
- max_off_to = grid->line_offset[row] + grid->Columns;
-
- if (rlflag) {
- // Clear rest first, because it's left of the text.
- if (clear_width > 0) {
- while (col <= endcol && grid->chars[off_to][0] == ' '
- && grid->chars[off_to][1] == NUL
- && grid->attrs[off_to] == bg_attr) {
- ++off_to;
- ++col;
- }
- if (col <= endcol) {
- grid_fill(grid, row, row + 1, col + coloff, endcol + coloff + 1,
- ' ', ' ', bg_attr);
- }
- }
- col = endcol + 1;
- off_to = grid->line_offset[row] + col + coloff;
- off_from += col;
- endcol = (clear_width > 0 ? clear_width : -clear_width);
- }
-
- if (bg_attr) {
- for (int c = col; c < endcol; c++) {
- linebuf_attr[off_from+c] =
- hl_combine_attr(bg_attr, linebuf_attr[off_from+c]);
- }
- }
-
- redraw_next = grid_char_needs_redraw(grid, off_from, off_to, endcol - col);
-
- while (col < endcol) {
- char_cells = 1;
- if (col + 1 < endcol) {
- char_cells = line_off2cells(linebuf_char, off_from, max_off_from);
- }
- redraw_this = redraw_next;
- redraw_next = grid_char_needs_redraw(grid, off_from + char_cells,
- off_to + char_cells,
- endcol - col - char_cells);
-
- if (redraw_this) {
- if (start_dirty == -1) {
- start_dirty = col;
- }
- end_dirty = col + char_cells;
- // When writing a single-width character over a double-width
- // character and at the end of the redrawn text, need to clear out
- // the right half of the old character.
- // Also required when writing the right half of a double-width
- // char over the left half of an existing one
- if (col + char_cells == endcol
- && ((char_cells == 1
- && grid_off2cells(grid, off_to, max_off_to) > 1)
- || (char_cells == 2
- && grid_off2cells(grid, off_to, max_off_to) == 1
- && grid_off2cells(grid, off_to + 1, max_off_to) > 1))) {
- clear_next = true;
- }
-
- schar_copy(grid->chars[off_to], linebuf_char[off_from]);
- if (char_cells == 2) {
- schar_copy(grid->chars[off_to+1], linebuf_char[off_from+1]);
- }
-
- grid->attrs[off_to] = linebuf_attr[off_from];
- // For simplicity set the attributes of second half of a
- // double-wide character equal to the first half.
- if (char_cells == 2) {
- grid->attrs[off_to + 1] = linebuf_attr[off_from];
- }
- }
-
- off_to += char_cells;
- off_from += char_cells;
- col += char_cells;
- }
-
- if (clear_next) {
- // Clear the second half of a double-wide character of which the left
- // half was overwritten with a single-wide character.
- schar_from_ascii(grid->chars[off_to], ' ');
- end_dirty++;
- }
-
- int clear_end = -1;
- if (clear_width > 0 && !rlflag) {
- // blank out the rest of the line
- // TODO(bfredl): we could cache winline widths
- while (col < clear_width) {
- if (grid->chars[off_to][0] != ' '
- || grid->chars[off_to][1] != NUL
- || grid->attrs[off_to] != bg_attr) {
- grid->chars[off_to][0] = ' ';
- grid->chars[off_to][1] = NUL;
- grid->attrs[off_to] = bg_attr;
- if (start_dirty == -1) {
- start_dirty = col;
- end_dirty = col;
- } else if (clear_end == -1) {
- end_dirty = endcol;
- }
- clear_end = col+1;
- }
- col++;
- off_to++;
- }
- }
-
- if (clear_width > 0 || wp->w_width != grid->Columns) {
- // If we cleared after the end of the line, it did not wrap.
- // For vsplit, line wrapping is not possible.
- grid->line_wraps[row] = false;
- }
-
- if (clear_end < end_dirty) {
- clear_end = end_dirty;
- }
- if (start_dirty == -1) {
- start_dirty = end_dirty;
- }
- if (clear_end > start_dirty) {
- ui_line(grid, row, coloff+start_dirty, coloff+end_dirty, coloff+clear_end,
- bg_attr, wrap);
- }
}
/*
@@ -4932,30 +4542,34 @@ void rl_mirror(char_u *str)
}
}
-/*
- * mark all status lines for redraw; used after first :cd
- */
+/// Mark all status lines and window bars for redraw; used after first :cd
void status_redraw_all(void)
{
+ bool is_stl_global = global_stl_height() != 0;
+
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_status_height) {
+ if ((!is_stl_global && wp->w_status_height) || (is_stl_global && wp == curwin)
+ || wp->w_winbar_height) {
wp->w_redr_status = true;
redraw_later(wp, VALID);
}
}
}
-/// Marks all status lines of the current buffer for redraw.
+/// Marks all status lines and window bars of the current buffer for redraw.
void status_redraw_curbuf(void)
{
status_redraw_buf(curbuf);
}
-/// Marks all status lines of the specified buffer for redraw.
+/// Marks all status lines and window bars of the given buffer for redraw.
void status_redraw_buf(buf_T *buf)
{
+ bool is_stl_global = global_stl_height() != 0;
+
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_status_height != 0 && wp->w_buffer == buf) {
+ if (wp->w_buffer == buf && ((!is_stl_global && wp->w_status_height)
+ || (is_stl_global && wp == curwin) || wp->w_winbar_height)) {
wp->w_redr_status = true;
redraw_later(wp, VALID);
}
@@ -4969,6 +4583,7 @@ void redraw_statuslines(void)
{
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_redr_status) {
+ win_redr_winbar(wp);
win_redr_status(wp);
}
}
@@ -4999,10 +4614,8 @@ void win_redraw_last_status(const frame_T *frp)
}
}
-/*
- * Draw the verticap separator right of window "wp" starting with line "row".
- */
-static void draw_vsep_win(win_T *wp, int row)
+/// Draw the vertical separator right of window "wp"
+static void draw_vsep_win(win_T *wp)
{
int hl;
int c;
@@ -5010,15 +4623,97 @@ static void draw_vsep_win(win_T *wp, int row)
if (wp->w_vsep_width) {
// draw the vertical separator right of this window
c = fillchar_vsep(wp, &hl);
- grid_fill(&default_grid, wp->w_winrow + row, W_ENDROW(wp),
+ grid_fill(&default_grid, wp->w_winrow, W_ENDROW(wp),
W_ENDCOL(wp), W_ENDCOL(wp) + 1, c, ' ', hl);
}
}
+/// Draw the horizontal separator below window "wp"
+static void draw_hsep_win(win_T *wp)
+{
+ int hl;
+ int c;
+
+ if (wp->w_hsep_height) {
+ // draw the horizontal separator below this window
+ c = fillchar_hsep(wp, &hl);
+ grid_fill(&default_grid, W_ENDROW(wp), W_ENDROW(wp) + 1,
+ wp->w_wincol, W_ENDCOL(wp), c, c, hl);
+ }
+}
+
+/// Get the separator connector for specified window corner of window "wp"
+static int get_corner_sep_connector(win_T *wp, WindowCorner corner)
+{
+ // It's impossible for windows to be connected neither vertically nor horizontally
+ // So if they're not vertically connected, assume they're horizontally connected
+ if (vsep_connected(wp, corner)) {
+ if (hsep_connected(wp, corner)) {
+ return wp->w_p_fcs_chars.verthoriz;
+ } else if (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT) {
+ return wp->w_p_fcs_chars.vertright;
+ } else {
+ return wp->w_p_fcs_chars.vertleft;
+ }
+ } else if (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT) {
+ return wp->w_p_fcs_chars.horizdown;
+ } else {
+ return wp->w_p_fcs_chars.horizup;
+ }
+}
-/*
- * Get the length of an item as it will be shown in the status line.
- */
+/// Draw separator connecting characters on the corners of window "wp"
+static void draw_sep_connectors_win(win_T *wp)
+{
+ // Don't draw separator connectors unless global statusline is enabled and the window has
+ // either a horizontal or vertical separator
+ if (global_stl_height() == 0 || !(wp->w_hsep_height == 1 || wp->w_vsep_width == 1)) {
+ return;
+ }
+
+ int hl = win_hl_attr(wp, HLF_C);
+
+ // Determine which edges of the screen the window is located on so we can avoid drawing separators
+ // on corners contained in those edges
+ bool win_at_top;
+ bool win_at_bottom = wp->w_hsep_height == 0;
+ bool win_at_left;
+ bool win_at_right = wp->w_vsep_width == 0;
+ frame_T *frp;
+
+ for (frp = wp->w_frame; frp->fr_parent != NULL; frp = frp->fr_parent) {
+ if (frp->fr_parent->fr_layout == FR_COL && frp->fr_prev != NULL) {
+ break;
+ }
+ }
+ win_at_top = frp->fr_parent == NULL;
+ for (frp = wp->w_frame; frp->fr_parent != NULL; frp = frp->fr_parent) {
+ if (frp->fr_parent->fr_layout == FR_ROW && frp->fr_prev != NULL) {
+ break;
+ }
+ }
+ win_at_left = frp->fr_parent == NULL;
+
+ // Draw the appropriate separator connector in every corner where drawing them is necessary
+ if (!(win_at_top || win_at_left)) {
+ grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_TOP_LEFT),
+ wp->w_winrow - 1, wp->w_wincol - 1, hl);
+ }
+ if (!(win_at_top || win_at_right)) {
+ grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_TOP_RIGHT),
+ wp->w_winrow - 1, W_ENDCOL(wp), hl);
+ }
+ if (!(win_at_bottom || win_at_left)) {
+ grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_BOTTOM_LEFT),
+ W_ENDROW(wp), wp->w_wincol - 1, hl);
+ }
+ if (!(win_at_bottom || win_at_right)) {
+ grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_BOTTOM_RIGHT),
+ W_ENDROW(wp), W_ENDCOL(wp), hl);
+ }
+}
+
+/// Get the length of an item as it will be shown in the status line.
static int status_match_len(expand_T *xp, char_u *s)
{
int len = 0;
@@ -5027,13 +4722,13 @@ static int status_match_len(expand_T *xp, char_u *s)
|| xp->xp_context == EXPAND_MENUNAMES);
// Check for menu separators - replace with '|'.
- if (emenu && menu_is_separator(s)) {
+ if (emenu && menu_is_separator((char *)s)) {
return 1;
}
while (*s != NUL) {
s += skip_status_match_char(xp, s);
- len += ptr2cells(s);
+ len += ptr2cells((char *)s);
MB_PTR_ADV(s);
}
@@ -5153,7 +4848,7 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char_u **matches, in
clen = len;
i = first_match;
- while ((long)(clen + status_match_len(xp, L_MATCH(i)) + 2) < Columns) {
+ while (clen + status_match_len(xp, L_MATCH(i)) + 2 < Columns) {
if (i == match) {
selstart = buf + len;
selstart_col = clen;
@@ -5163,7 +4858,7 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char_u **matches, in
// Check for menu separators - replace with '|'
emenu = (xp->xp_context == EXPAND_MENUS
|| xp->xp_context == EXPAND_MENUNAMES);
- if (emenu && menu_is_separator(s)) {
+ if (emenu && menu_is_separator((char *)s)) {
STRCPY(buf + len, transchar('|'));
l = (int)STRLEN(buf + len);
len += l;
@@ -5171,8 +4866,8 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char_u **matches, in
} else {
for (; *s != NUL; ++s) {
s += skip_status_match_char(xp, s);
- clen += ptr2cells(s);
- if ((l = utfc_ptr2len(s)) > 1) {
+ clen += ptr2cells((char *)s);
+ if ((l = utfc_ptr2len((char *)s)) > 1) {
STRNCPY(buf + len, s, l); // NOLINT(runtime/printf)
s += l - 1;
len += l;
@@ -5219,7 +4914,7 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char_u **matches, in
// Create status line if needed by setting 'laststatus' to 2.
// Set 'winminheight' to zero to avoid that the window is
// resized.
- if (lastwin->w_status_height == 0) {
+ if (lastwin->w_status_height == 0 && global_stl_height() == 0) {
save_p_ls = p_ls;
save_p_wmh = p_wmh;
p_ls = 2;
@@ -5255,12 +4950,15 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char_u **matches, in
static void win_redr_status(win_T *wp)
{
int row;
+ int col;
char_u *p;
int len;
int fillchar;
int attr;
+ int width;
int this_ru_col;
- static int busy = FALSE;
+ bool is_stl_global = global_stl_height() > 0;
+ static int busy = false;
// May get here recursively when 'statusline' (indirectly)
// invokes ":redrawstatus". Simply ignore the call then.
@@ -5271,9 +4969,9 @@ static void win_redr_status(win_T *wp)
}
busy = true;
- wp->w_redr_status = FALSE;
- if (wp->w_status_height == 0) {
- // no status line, can only be last window
+ wp->w_redr_status = false;
+ if (wp->w_status_height == 0 && !(is_stl_global && wp == curwin)) {
+ // no status line, either global statusline is enabled or the window is a last window
redraw_cmdline = true;
} else if (!redrawing()) {
// Don't redraw right now, do it later. Don't update status line when
@@ -5284,6 +4982,7 @@ static void win_redr_status(win_T *wp)
redraw_custom_statusline(wp);
} else {
fillchar = fillchar_status(&attr, wp);
+ width = is_stl_global ? Columns : wp->w_width;
get_trans_bufname(wp->w_buffer);
p = NameBuff;
@@ -5296,25 +4995,25 @@ static void win_redr_status(win_T *wp)
*(p + len++) = ' ';
}
if (bt_help(wp->w_buffer)) {
- STRCPY(p + len, _("[Help]"));
+ snprintf((char *)p + len, MAXPATHL - len, "%s", _("[Help]"));
len += (int)STRLEN(p + len);
}
if (wp->w_p_pvw) {
- STRCPY(p + len, _("[Preview]"));
+ snprintf((char *)p + len, MAXPATHL - len, "%s", _("[Preview]"));
len += (int)STRLEN(p + len);
}
if (bufIsChanged(wp->w_buffer)) {
- STRCPY(p + len, "[+]");
- len += 3;
+ snprintf((char *)p + len, MAXPATHL - len, "%s", "[+]");
+ len += (int)STRLEN(p + len);
}
if (wp->w_buffer->b_p_ro) {
- STRCPY(p + len, _("[RO]"));
+ snprintf((char *)p + len, MAXPATHL - len, "%s", _("[RO]"));
// len += (int)STRLEN(p + len); // dead assignment
}
- this_ru_col = ru_col - (Columns - wp->w_width);
- if (this_ru_col < (wp->w_width + 1) / 2) {
- this_ru_col = (wp->w_width + 1) / 2;
+ this_ru_col = ru_col - (Columns - width);
+ if (this_ru_col < (width + 1) / 2) {
+ this_ru_col = (width + 1) / 2;
}
if (this_ru_col <= 1) {
p = (char_u *)"<"; // No room for file name!
@@ -5323,13 +5022,13 @@ static void win_redr_status(win_T *wp)
int clen = 0, i;
// Count total number of display cells.
- clen = (int)mb_string2cells(p);
+ clen = (int)mb_string2cells((char *)p);
// Find first character that will fit.
// Going from start to end is much faster for DBCS.
for (i = 0; p[i] != NUL && clen >= this_ru_col - 1;
- i += utfc_ptr2len(p + i)) {
- clen -= utf_ptr2cells(p + i);
+ i += utfc_ptr2len((char *)p + i)) {
+ clen -= utf_ptr2cells((char *)p + i);
}
len = clen;
if (i > 0) {
@@ -5339,12 +5038,13 @@ static void win_redr_status(win_T *wp)
}
}
- row = W_ENDROW(wp);
- grid_puts(&default_grid, p, row, wp->w_wincol, attr);
- grid_fill(&default_grid, row, row + 1, len + wp->w_wincol,
- this_ru_col + wp->w_wincol, fillchar, fillchar, attr);
+ row = is_stl_global ? (Rows - p_ch - 1) : W_ENDROW(wp);
+ col = is_stl_global ? 0 : wp->w_wincol;
+ grid_puts(&default_grid, p, row, col, attr);
+ grid_fill(&default_grid, row, row + 1, len + col,
+ this_ru_col + col, fillchar, fillchar, attr);
- if (get_keymap_str(wp, (char_u *)"<%s>", NameBuff, MAXPATHL)
+ if (get_keymap_str(wp, "<%s>", (char *)NameBuff, MAXPATHL)
&& this_ru_col - len > (int)(STRLEN(NameBuff) + 1)) {
grid_puts(&default_grid, NameBuff, row,
(int)(this_ru_col - STRLEN(NameBuff) - 1), attr);
@@ -5384,12 +5084,12 @@ static void redraw_custom_statusline(win_T *wp)
entered = true;
did_emsg = false;
- win_redr_custom(wp, false);
+ win_redr_custom(wp, false, false);
if (did_emsg) {
// When there is an error disable the statusline, otherwise the
// display is messed up with errors and a redraw triggers the problem
// again and again.
- set_string_option_direct("statusline", -1, (char_u *)"",
+ set_string_option_direct("statusline", -1, "",
OPT_FREE | (*wp->w_p_stl != NUL
? OPT_LOCAL : OPT_GLOBAL), SID_ERROR);
}
@@ -5397,6 +5097,37 @@ static void redraw_custom_statusline(win_T *wp)
entered = false;
}
+static void win_redr_winbar(win_T *wp)
+{
+ static bool entered = false;
+
+ // Return when called recursively. This can happen when the winbar contains an expression
+ // that triggers a redraw.
+ if (entered) {
+ return;
+ }
+ entered = true;
+
+ if (wp->w_winbar_height == 0 || !redrawing()) {
+ // Do nothing.
+ } else if (*p_wbr != NUL || *wp->w_p_wbr != NUL) {
+ int saved_did_emsg = did_emsg;
+
+ did_emsg = false;
+ win_redr_custom(wp, true, false);
+ if (did_emsg) {
+ // When there is an error disable the winbar, otherwise the
+ // display is messed up with errors and a redraw triggers the problem
+ // again and again.
+ set_string_option_direct("winbar", -1, "",
+ OPT_FREE | (*wp->w_p_stl != NUL
+ ? OPT_LOCAL : OPT_GLOBAL), SID_ERROR);
+ }
+ did_emsg |= saved_did_emsg;
+ }
+ entered = false;
+}
+
/// Only call if (wp->w_vsep_width != 0).
///
/// @return true if the status line of window "wp" is connected to the status
@@ -5421,15 +5152,85 @@ bool stl_connected(win_T *wp)
return false;
}
+/// Check if horizontal separator of window "wp" at specified window corner is connected to the
+/// horizontal separator of another window
+/// Assumes global statusline is enabled
+static bool hsep_connected(win_T *wp, WindowCorner corner)
+{
+ bool before = (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT);
+ int sep_row = (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT)
+ ? wp->w_winrow - 1 : W_ENDROW(wp);
+ frame_T *fr = wp->w_frame;
+
+ while (fr->fr_parent != NULL) {
+ if (fr->fr_parent->fr_layout == FR_ROW && (before ? fr->fr_prev : fr->fr_next) != NULL) {
+ fr = before ? fr->fr_prev : fr->fr_next;
+ break;
+ }
+ fr = fr->fr_parent;
+ }
+ if (fr->fr_parent == NULL) {
+ return false;
+ }
+ while (fr->fr_layout != FR_LEAF) {
+ fr = fr->fr_child;
+ if (fr->fr_parent->fr_layout == FR_ROW && before) {
+ while (fr->fr_next != NULL) {
+ fr = fr->fr_next;
+ }
+ } else {
+ while (fr->fr_next != NULL && frame2win(fr)->w_winrow + fr->fr_height < sep_row) {
+ fr = fr->fr_next;
+ }
+ }
+ }
+
+ return (sep_row == fr->fr_win->w_winrow - 1 || sep_row == W_ENDROW(fr->fr_win));
+}
+
+/// Check if vertical separator of window "wp" at specified window corner is connected to the
+/// vertical separator of another window
+static bool vsep_connected(win_T *wp, WindowCorner corner)
+{
+ bool before = (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT);
+ int sep_col = (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT)
+ ? wp->w_wincol - 1 : W_ENDCOL(wp);
+ frame_T *fr = wp->w_frame;
+
+ while (fr->fr_parent != NULL) {
+ if (fr->fr_parent->fr_layout == FR_COL && (before ? fr->fr_prev : fr->fr_next) != NULL) {
+ fr = before ? fr->fr_prev : fr->fr_next;
+ break;
+ }
+ fr = fr->fr_parent;
+ }
+ if (fr->fr_parent == NULL) {
+ return false;
+ }
+ while (fr->fr_layout != FR_LEAF) {
+ fr = fr->fr_child;
+ if (fr->fr_parent->fr_layout == FR_COL && before) {
+ while (fr->fr_next != NULL) {
+ fr = fr->fr_next;
+ }
+ } else {
+ while (fr->fr_next != NULL && frame2win(fr)->w_wincol + fr->fr_width < sep_col) {
+ fr = fr->fr_next;
+ }
+ }
+ }
+
+ return (sep_col == fr->fr_win->w_wincol - 1 || sep_col == W_ENDCOL(fr->fr_win));
+}
/// Get the value to show for the language mappings, active 'keymap'.
///
/// @param fmt format string containing one %s item
/// @param buf buffer for the result
/// @param len length of buffer
-bool get_keymap_str(win_T *wp, char_u *fmt, char_u *buf, int len)
+bool get_keymap_str(win_T *wp, char *fmt, char *buf, int len)
{
- char_u *p;
+ char *p;
if (wp->w_buffer->b_p_iminsert != B_IMODE_LMAP) {
return false;
@@ -5438,7 +5239,7 @@ bool get_keymap_str(win_T *wp, char_u *fmt, char_u *buf, int len)
{
buf_T *old_curbuf = curbuf;
win_T *old_curwin = curwin;
- char_u *s;
+ char *s;
curbuf = wp->w_buffer;
curwin = wp;
@@ -5450,12 +5251,12 @@ bool get_keymap_str(win_T *wp, char_u *fmt, char_u *buf, int len)
curwin = old_curwin;
if (p == NULL || *p == NUL) {
if (wp->w_buffer->b_kmap_state & KEYMAP_LOADED) {
- p = wp->w_buffer->b_p_keymap;
+ p = (char *)wp->w_buffer->b_p_keymap;
} else {
- p = (char_u *)"lang";
+ p = "lang";
}
}
- if (vim_snprintf((char *)buf, len, (char *)fmt, p) > len - 1) {
+ if (vim_snprintf(buf, len, fmt, p) > len - 1) {
buf[0] = NUL;
}
xfree(s);
@@ -5463,11 +5264,9 @@ bool get_keymap_str(win_T *wp, char_u *fmt, char_u *buf, int len)
return buf[0] != NUL;
}
-/*
- * Redraw the status line or ruler of window "wp".
- * When "wp" is NULL redraw the tab pages line from 'tabline'.
- */
-static void win_redr_custom(win_T *wp, bool draw_ruler)
+/// Redraw the status line, window bar or ruler of window "wp".
+/// When "wp" is NULL redraw the tab pages line from 'tabline'.
+static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler)
{
static bool entered = false;
int attr;
@@ -5479,14 +5278,15 @@ static void win_redr_custom(win_T *wp, bool draw_ruler)
int n;
int len;
int fillchar;
- char_u buf[MAXPATHL];
+ char buf[MAXPATHL];
char_u *stl;
- char_u *p;
+ char *p;
stl_hlrec_t *hltab;
StlClickRecord *tabtab;
int use_sandbox = false;
win_T *ewp;
int p_crb_save;
+ bool is_stl_global = global_stl_height() > 0;
ScreenGrid *grid = &default_grid;
@@ -5507,10 +5307,41 @@ static void win_redr_custom(win_T *wp, bool draw_ruler)
attr = HL_ATTR(HLF_TPF);
maxwidth = Columns;
use_sandbox = was_set_insecurely(wp, "tabline", 0);
+ } else if (draw_winbar) {
+ stl = (char_u *)((*wp->w_p_wbr != NUL) ? wp->w_p_wbr : p_wbr);
+ row = -1; // row zero is first row of text
+ col = 0;
+ grid = &wp->w_grid;
+ grid_adjust(&grid, &row, &col);
+
+ if (row < 0) {
+ return;
+ }
+
+ fillchar = wp->w_p_fcs_chars.wbr;
+ attr = (wp == curwin) ? win_hl_attr(wp, HLF_WBR) : win_hl_attr(wp, HLF_WBRNC);
+ maxwidth = wp->w_width_inner;
+ use_sandbox = was_set_insecurely(wp, "winbar", 0);
+
+ stl_clear_click_defs(wp->w_winbar_click_defs, wp->w_winbar_click_defs_size);
+ // Allocate / resize the click definitions array for winbar if needed.
+ if (wp->w_winbar_height && wp->w_winbar_click_defs_size < (size_t)maxwidth) {
+ xfree(wp->w_winbar_click_defs);
+ wp->w_winbar_click_defs_size = (size_t)maxwidth;
+ wp->w_winbar_click_defs = xcalloc(wp->w_winbar_click_defs_size, sizeof(StlClickRecord));
+ }
} else {
- row = W_ENDROW(wp);
+ row = is_stl_global ? (Rows - p_ch - 1) : W_ENDROW(wp);
fillchar = fillchar_status(&attr, wp);
- maxwidth = wp->w_width;
+ maxwidth = is_stl_global ? Columns : wp->w_width;
+
+ stl_clear_click_defs(wp->w_status_click_defs, wp->w_status_click_defs_size);
+ // Allocate / resize the click definitions array for statusline if needed.
+ if (wp->w_status_click_defs_size < (size_t)maxwidth) {
+ xfree(wp->w_status_click_defs);
+ wp->w_status_click_defs_size = maxwidth;
+ wp->w_status_click_defs = xcalloc(wp->w_status_click_defs_size, sizeof(StlClickRecord));
+ }
if (draw_ruler) {
stl = p_ruf;
@@ -5528,12 +5359,12 @@ static void win_redr_custom(win_T *wp, bool draw_ruler)
stl = p_ruf;
}
}
- col = ru_col - (Columns - wp->w_width);
- if (col < (wp->w_width + 1) / 2) {
- col = (wp->w_width + 1) / 2;
+ col = ru_col - (Columns - maxwidth);
+ if (col < (maxwidth + 1) / 2) {
+ col = (maxwidth + 1) / 2;
}
- maxwidth = wp->w_width - col;
- if (!wp->w_status_height) {
+ maxwidth = maxwidth - col;
+ if (!wp->w_status_height && !is_stl_global) {
grid = &msg_grid_adj;
row = Rows - 1;
maxwidth--; // writing in last column may cause scrolling
@@ -5551,7 +5382,7 @@ static void win_redr_custom(win_T *wp, bool draw_ruler)
use_sandbox = was_set_insecurely(wp, "statusline", *wp->w_p_stl == NUL ? 0 : OPT_LOCAL);
}
- col += wp->w_wincol;
+ col += is_stl_global ? 0 : wp->w_wincol;
}
if (maxwidth <= 0) {
@@ -5568,13 +5399,13 @@ static void win_redr_custom(win_T *wp, bool draw_ruler)
* might change the option value and free the memory. */
stl = vim_strsave(stl);
width =
- build_stl_str_hl(ewp, buf, sizeof(buf), stl, use_sandbox,
+ build_stl_str_hl(ewp, buf, sizeof(buf), (char *)stl, use_sandbox,
fillchar, maxwidth, &hltab, &tabtab);
xfree(stl);
ewp->w_p_crb = p_crb_save;
// Make all characters printable.
- p = (char_u *)transstr((const char *)buf, true);
+ p = transstr(buf, true);
len = STRLCPY(buf, p, sizeof(buf));
len = (size_t)len < sizeof(buf) ? len : (int)sizeof(buf) - 1;
xfree(p);
@@ -5595,8 +5426,8 @@ static void win_redr_custom(win_T *wp, bool draw_ruler)
p = buf;
for (n = 0; hltab[n].start != NULL; n++) {
int textlen = (int)(hltab[n].start - p);
- grid_puts_len(grid, p, textlen, row, col, curattr);
- col += vim_strnsize(p, textlen);
+ grid_puts_len(grid, (char_u *)p, textlen, row, col, curattr);
+ col += vim_strnsize((char_u *)p, textlen);
p = hltab[n].start;
if (hltab[n].userhl == 0) {
@@ -5610,31 +5441,43 @@ static void win_redr_custom(win_T *wp, bool draw_ruler)
}
}
// Make sure to use an empty string instead of p, if p is beyond buf + len.
- grid_puts(grid, p >= buf + len ? (char_u *)"" : p, row, col,
+ grid_puts(grid, p >= buf + len ? (char_u *)"" : (char_u *)p, row, col,
curattr);
grid_puts_line_flush(false);
- if (wp == NULL) {
- // Fill the tab_page_click_defs array for clicking in the tab pages line.
- col = 0;
- len = 0;
- p = buf;
- StlClickDefinition cur_click_def = {
- .type = kStlClickDisabled,
- };
- for (n = 0; tabtab[n].start != NULL; n++) {
- len += vim_strnsize(p, (int)(tabtab[n].start - (char *)p));
- while (col < len) {
- tab_page_click_defs[col++] = cur_click_def;
- }
- p = (char_u *)tabtab[n].start;
- cur_click_def = tabtab[n].def;
+ // Fill the tab_page_click_defs, w_status_click_defs or w_winbar_click_defs array for clicking
+ // in the tab page line, status line or window bar
+ StlClickDefinition *click_defs = (wp == NULL) ? tab_page_click_defs
+ : draw_winbar ? wp->w_winbar_click_defs
+ : wp->w_status_click_defs;
+
+ if (click_defs == NULL) {
+ goto theend;
+ }
+
+ col = 0;
+ len = 0;
+ p = buf;
+ StlClickDefinition cur_click_def = {
+ .type = kStlClickDisabled,
+ };
+ for (n = 0; tabtab[n].start != NULL; n++) {
+ len += vim_strnsize((char_u *)p, (int)(tabtab[n].start - p));
+ while (col < len) {
+ click_defs[col++] = cur_click_def;
}
- while (col < Columns) {
- tab_page_click_defs[col++] = cur_click_def;
+ p = (char *)tabtab[n].start;
+ cur_click_def = tabtab[n].def;
+ if ((wp != NULL) && !(cur_click_def.type == kStlClickDisabled
+ || cur_click_def.type == kStlClickFuncRun)) {
+ // window bar and status line only support click functions
+ cur_click_def.type = kStlClickDisabled;
}
}
+ while (col < maxwidth) {
+ click_defs[col++] = cur_click_def;
+ }
theend:
entered = false;
@@ -5653,9 +5496,9 @@ static void win_redr_border(win_T *wp)
int *attrs = wp->w_float_config.border_attr;
int *adj = wp->w_border_adj;
- int irow = wp->w_height_inner, icol = wp->w_width_inner;
+ int irow = wp->w_height_inner + wp->w_winbar_height, icol = wp->w_width_inner;
- char_u* title = wp->w_float_config.title;
+ char* title = wp->w_float_config.title;
size_t n_title = wp->w_float_config.n_title;
stl_hlrec_t* title_hl = wp->w_float_config.title_hl;
@@ -5691,7 +5534,7 @@ static void win_redr_border(win_T *wp)
int attr;
// Draw the title if in the correct position.
if (i > title_pos && n_title > 0 && i < icol - 2) {
- cc = utfc_ptr2char(title, m8);
+ cc = utfc_ptr2char((char_u*) title, m8);
len = utfc_ptr2len(title);
n_title -= len;
title += len;
@@ -5709,373 +5552,44 @@ static void win_redr_border(win_T *wp)
memcpy(ch, chars[1], sizeof(schar_T));
attr = attrs[1];
}
- grid_put_schar(grid, 0, i+adj[3], ch, attr);
+ grid_put_schar(grid, 0, i + adj[3], ch, attr);
}
if (adj[1]) {
- grid_put_schar(grid, 0, icol+adj[3], chars[2], attrs[2]);
+ grid_put_schar(grid, 0, icol + adj[3], chars[2], attrs[2]);
}
grid_puts_line_flush(false);
}
for (int i = 0; i < irow; i++) {
if (adj[3]) {
- grid_puts_line_start(grid, i+adj[0]);
- grid_put_schar(grid, i+adj[0], 0, chars[7], attrs[7]);
+ grid_puts_line_start(grid, i + adj[0]);
+ grid_put_schar(grid, i + adj[0], 0, chars[7], attrs[7]);
grid_puts_line_flush(false);
}
if (adj[1]) {
int ic = (i == 0 && !adj[0] && chars[2][0]) ? 2 : 3;
- grid_puts_line_start(grid, i+adj[0]);
- grid_put_schar(grid, i+adj[0], icol+adj[3], chars[ic], attrs[ic]);
+ grid_puts_line_start(grid, i + adj[0]);
+ grid_put_schar(grid, i + adj[0], icol + adj[3], chars[ic], attrs[ic]);
grid_puts_line_flush(false);
}
}
if (adj[2]) {
- grid_puts_line_start(grid, irow+adj[0]);
+ grid_puts_line_start(grid, irow + adj[0]);
if (adj[3]) {
- grid_put_schar(grid, irow+adj[0], 0, chars[6], attrs[6]);
+ grid_put_schar(grid, irow + adj[0], 0, chars[6], attrs[6]);
}
for (int i = 0; i < icol; i++) {
int ic = (i == 0 && !adj[3] && chars[6][0]) ? 6 : 5;
- grid_put_schar(grid, irow+adj[0], i+adj[3], chars[ic], attrs[ic]);
+ grid_put_schar(grid, irow + adj[0], i + adj[3], chars[ic], attrs[ic]);
}
if (adj[1]) {
- grid_put_schar(grid, irow+adj[0], icol+adj[3], chars[4], attrs[4]);
+ grid_put_schar(grid, irow + adj[0], icol + adj[3], chars[4], attrs[4]);
}
grid_puts_line_flush(false);
}
}
-// Low-level functions to manipulate individual character cells on the
-// screen grid.
-
-/// Put a ASCII character in a screen cell.
-static void schar_from_ascii(char_u *p, const char c)
-{
- p[0] = c;
- p[1] = 0;
-}
-
-/// Put a unicode character in a screen cell.
-static int schar_from_char(char_u *p, int c)
-{
- int len = utf_char2bytes(c, p);
- p[len] = NUL;
- return len;
-}
-
-/// Put a unicode char, and up to MAX_MCO composing chars, in a screen cell.
-static int schar_from_cc(char_u *p, int c, int u8cc[MAX_MCO])
-{
- int len = utf_char2bytes(c, p);
- for (int i = 0; i < MAX_MCO; i++) {
- if (u8cc[i] == 0) {
- break;
- }
- len += utf_char2bytes(u8cc[i], p + len);
- }
- p[len] = 0;
- return len;
-}
-
-/// compare the contents of two screen cells.
-static int schar_cmp(char_u *sc1, char_u *sc2)
-{
- return STRNCMP(sc1, sc2, sizeof(schar_T));
-}
-
-/// copy the contents of screen cell `sc2` into cell `sc1`
-static void schar_copy(char_u *sc1, char_u *sc2)
-{
- STRLCPY(sc1, sc2, sizeof(schar_T));
-}
-
-static int line_off2cells(schar_T *line, size_t off, size_t max_off)
-{
- return (off + 1 < max_off && line[off + 1][0] == 0) ? 2 : 1;
-}
-
-/// Return number of display cells for char at grid->chars[off].
-/// We make sure that the offset used is less than "max_off".
-static int grid_off2cells(ScreenGrid *grid, size_t off, size_t max_off)
-{
- return line_off2cells(grid->chars, off, max_off);
-}
-
-/// Return true if the character at "row"/"col" on the screen is the left side
-/// of a double-width character.
-///
-/// Caller must make sure "row" and "col" are not invalid!
-bool grid_lefthalve(ScreenGrid *grid, int row, int col)
-{
- screen_adjust_grid(&grid, &row, &col);
-
- return grid_off2cells(grid, grid->line_offset[row] + col,
- grid->line_offset[row] + grid->Columns) > 1;
-}
-
-/// Correct a position on the screen, if it's the right half of a double-wide
-/// char move it to the left half. Returns the corrected column.
-int grid_fix_col(ScreenGrid *grid, int col, int row)
-{
- int coloff = 0;
- screen_adjust_grid(&grid, &row, &coloff);
-
- col += coloff;
- if (grid->chars != NULL && col > 0
- && grid->chars[grid->line_offset[row] + col][0] == 0) {
- return col - 1 - coloff;
- }
- return col - coloff;
-}
-
-/// output a single character directly to the grid
-void grid_putchar(ScreenGrid *grid, int c, int row, int col, int attr)
-{
- char_u buf[MB_MAXBYTES + 1];
-
- buf[utf_char2bytes(c, buf)] = NUL;
- grid_puts(grid, buf, row, col, attr);
-}
-
-/// get a single character directly from grid.chars into "bytes[]".
-/// Also return its attribute in *attrp;
-void grid_getbytes(ScreenGrid *grid, int row, int col, char_u *bytes, int *attrp)
-{
- unsigned off;
-
- screen_adjust_grid(&grid, &row, &col);
-
- // safety check
- if (grid->chars != NULL && row < grid->Rows && col < grid->Columns) {
- off = grid->line_offset[row] + col;
- *attrp = grid->attrs[off];
- schar_copy(bytes, grid->chars[off]);
- }
-}
-
-
-/// put string '*text' on the window grid at position 'row' and 'col', with
-/// attributes 'attr', and update chars[] and attrs[].
-/// Note: only outputs within one row, message is truncated at grid boundary!
-/// Note: if grid, row and/or col is invalid, nothing is done.
-void grid_puts(ScreenGrid *grid, char_u *text, int row, int col, int attr)
-{
- grid_puts_len(grid, text, -1, row, col, attr);
-}
-
-static ScreenGrid *put_dirty_grid = NULL;
-static int put_dirty_row = -1;
-static int put_dirty_first = INT_MAX;
-static int put_dirty_last = 0;
-
-/// Start a group of grid_puts_len calls that builds a single grid line.
-///
-/// Must be matched with a grid_puts_line_flush call before moving to
-/// another line.
-void grid_puts_line_start(ScreenGrid *grid, int row)
-{
- int col = 0; // unused
- screen_adjust_grid(&grid, &row, &col);
- assert(put_dirty_row == -1);
- put_dirty_row = row;
- put_dirty_grid = grid;
-}
-
-void grid_put_schar(ScreenGrid *grid, int row, int col, char_u *schar, int attr)
-{
- assert(put_dirty_row == row);
- unsigned int off = grid->line_offset[row] + col;
- if (grid->attrs[off] != attr || schar_cmp(grid->chars[off], schar)) {
- schar_copy(grid->chars[off], schar);
- grid->attrs[off] = attr;
-
- put_dirty_first = MIN(put_dirty_first, col);
- // TODO(bfredl): Y U NO DOUBLEWIDTH?
- put_dirty_last = MAX(put_dirty_last, col+1);
- }
-}
-
-/// like grid_puts(), but output "text[len]". When "len" is -1 output up to
-/// a NUL.
-void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, int col, int attr)
-{
- unsigned off;
- char_u *ptr = text;
- int len = textlen;
- int c;
- unsigned max_off;
- int mbyte_blen = 1;
- int mbyte_cells = 1;
- int u8c = 0;
- int u8cc[MAX_MCO];
- int clear_next_cell = FALSE;
- int prev_c = 0; // previous Arabic character
- int pc, nc, nc1;
- int pcc[MAX_MCO];
- int need_redraw;
- bool do_flush = false;
-
- screen_adjust_grid(&grid, &row, &col);
-
- // Safety check. The check for negative row and column is to fix issue
- // vim/vim#4102. TODO(neovim): find out why row/col could be negative.
- if (grid->chars == NULL
- || row >= grid->Rows || row < 0
- || col >= grid->Columns || col < 0) {
- return;
- }
-
- if (put_dirty_row == -1) {
- grid_puts_line_start(grid, row);
- do_flush = true;
- } else {
- if (grid != put_dirty_grid || row != put_dirty_row) {
- abort();
- }
- }
- off = grid->line_offset[row] + col;
-
- // When drawing over the right half of a double-wide char clear out the
- // left half. Only needed in a terminal.
- if (grid != &default_grid && col == 0 && grid_invalid_row(grid, row)) {
- // redraw the previous cell, make it empty
- put_dirty_first = -1;
- put_dirty_last = MAX(put_dirty_last, 1);
- }
-
- max_off = grid->line_offset[row] + grid->Columns;
- while (col < grid->Columns
- && (len < 0 || (int)(ptr - text) < len)
- && *ptr != NUL) {
- c = *ptr;
- // check if this is the first byte of a multibyte
- if (len > 0) {
- mbyte_blen = utfc_ptr2len_len(ptr, (int)((text + len) - ptr));
- } else {
- mbyte_blen = utfc_ptr2len(ptr);
- }
- if (len >= 0) {
- u8c = utfc_ptr2char_len(ptr, u8cc, (int)((text + len) - ptr));
- } else {
- u8c = utfc_ptr2char(ptr, u8cc);
- }
- mbyte_cells = utf_char2cells(u8c);
- if (p_arshape && !p_tbidi && arabic_char(u8c)) {
- // Do Arabic shaping.
- if (len >= 0 && (int)(ptr - text) + mbyte_blen >= len) {
- // Past end of string to be displayed.
- nc = NUL;
- nc1 = NUL;
- } else {
- nc = utfc_ptr2char_len(ptr + mbyte_blen, pcc,
- (int)((text + len) - ptr - mbyte_blen));
- nc1 = pcc[0];
- }
- pc = prev_c;
- prev_c = u8c;
- u8c = arabic_shape(u8c, &c, &u8cc[0], nc, nc1, pc);
- } else {
- prev_c = u8c;
- }
- if (col + mbyte_cells > grid->Columns) {
- // Only 1 cell left, but character requires 2 cells:
- // display a '>' in the last column to avoid wrapping. */
- c = '>';
- u8c = '>';
- u8cc[0] = 0;
- mbyte_cells = 1;
- }
-
- schar_T buf;
- schar_from_cc(buf, u8c, u8cc);
-
-
- need_redraw = schar_cmp(grid->chars[off], buf)
- || (mbyte_cells == 2 && grid->chars[off + 1][0] != 0)
- || grid->attrs[off] != attr
- || exmode_active;
-
- if (need_redraw) {
- // When at the end of the text and overwriting a two-cell
- // character with a one-cell character, need to clear the next
- // cell. Also when overwriting the left half of a two-cell char
- // with the right half of a two-cell char. Do this only once
- // (utf8_off2cells() may return 2 on the right half).
- if (clear_next_cell) {
- clear_next_cell = false;
- } else if ((len < 0 ? ptr[mbyte_blen] == NUL
- : ptr + mbyte_blen >= text + len)
- && ((mbyte_cells == 1
- && grid_off2cells(grid, off, max_off) > 1)
- || (mbyte_cells == 2
- && grid_off2cells(grid, off, max_off) == 1
- && grid_off2cells(grid, off + 1, max_off) > 1))) {
- clear_next_cell = true;
- }
-
- // When at the start of the text and overwriting the right half of a
- // two-cell character in the same grid, truncate that into a '>'.
- if (ptr == text && col > 0 && grid->chars[off][0] == 0) {
- grid->chars[off - 1][0] = '>';
- grid->chars[off - 1][1] = 0;
- }
-
- schar_copy(grid->chars[off], buf);
- grid->attrs[off] = attr;
- if (mbyte_cells == 2) {
- grid->chars[off + 1][0] = 0;
- grid->attrs[off + 1] = attr;
- }
- put_dirty_first = MIN(put_dirty_first, col);
- put_dirty_last = MAX(put_dirty_last, col+mbyte_cells);
- }
-
- off += mbyte_cells;
- col += mbyte_cells;
- ptr += mbyte_blen;
- if (clear_next_cell) {
- // This only happens at the end, display one space next.
- ptr = (char_u *)" ";
- len = -1;
- }
- }
-
- if (do_flush) {
- grid_puts_line_flush(true);
- }
-}
-
-/// End a group of grid_puts_len calls and send the screen buffer to the UI
-/// layer.
-///
-/// @param set_cursor Move the visible cursor to the end of the changed region.
-/// This is a workaround for not yet refactored code paths
-/// and shouldn't be used in new code.
-void grid_puts_line_flush(bool set_cursor)
-{
- assert(put_dirty_row != -1);
- if (put_dirty_first < put_dirty_last) {
- if (set_cursor) {
- ui_grid_cursor_goto(put_dirty_grid->handle, put_dirty_row,
- MIN(put_dirty_last, put_dirty_grid->Columns-1));
- }
- if (!put_dirty_grid->throttled) {
- ui_line(put_dirty_grid, put_dirty_row, put_dirty_first, put_dirty_last,
- put_dirty_last, 0, false);
- } else if (put_dirty_grid->dirty_col) {
- if (put_dirty_last > put_dirty_grid->dirty_col[put_dirty_row]) {
- put_dirty_grid->dirty_col[put_dirty_row] = put_dirty_last;
- }
- }
- put_dirty_first = INT_MAX;
- put_dirty_last = 0;
- }
- put_dirty_row = -1;
- put_dirty_grid = NULL;
-}
-
/*
* Prepare for 'hlsearch' highlighting.
*/
@@ -6100,372 +5614,6 @@ static void end_search_hl(void)
}
}
-
-/*
- * Init for calling prepare_search_hl().
- */
-static void init_search_hl(win_T *wp)
- FUNC_ATTR_NONNULL_ALL
-{
- // Setup for match and 'hlsearch' highlighting. Disable any previous
- // match
- matchitem_T *cur = wp->w_match_head;
- while (cur != NULL) {
- cur->hl.rm = cur->match;
- if (cur->hlg_id == 0) {
- cur->hl.attr = 0;
- } else {
- cur->hl.attr = syn_id2attr(cur->hlg_id);
- }
- cur->hl.buf = wp->w_buffer;
- cur->hl.lnum = 0;
- cur->hl.first_lnum = 0;
- // Set the time limit to 'redrawtime'.
- cur->hl.tm = profile_setlimit(p_rdt);
- cur = cur->next;
- }
- search_hl.buf = wp->w_buffer;
- search_hl.lnum = 0;
- search_hl.first_lnum = 0;
- search_hl.attr = win_hl_attr(wp, HLF_L);
-
- // time limit is set at the toplevel, for all windows
-}
-
-/*
- * Advance to the match in window "wp" line "lnum" or past it.
- */
-static void prepare_search_hl(win_T *wp, linenr_T lnum)
- FUNC_ATTR_NONNULL_ALL
-{
- matchitem_T *cur; // points to the match list
- match_T *shl; // points to search_hl or a match
- bool shl_flag; // flag to indicate whether search_hl
- // has been processed or not
-
- // When using a multi-line pattern, start searching at the top
- // of the window or just after a closed fold.
- // Do this both for search_hl and the match list.
- cur = wp->w_match_head;
- shl_flag = false;
- while (cur != NULL || shl_flag == false) {
- if (shl_flag == false) {
- shl = &search_hl;
- shl_flag = true;
- } else {
- shl = &cur->hl; // -V595
- }
- if (shl->rm.regprog != NULL
- && shl->lnum == 0
- && re_multiline(shl->rm.regprog)) {
- if (shl->first_lnum == 0) {
- for (shl->first_lnum = lnum;
- shl->first_lnum > wp->w_topline;
- shl->first_lnum--) {
- if (hasFoldingWin(wp, shl->first_lnum - 1, NULL, NULL, true, NULL)) {
- break;
- }
- }
- }
- if (cur != NULL) {
- cur->pos.cur = 0;
- }
- bool pos_inprogress = true; // mark that a position match search is
- // in progress
- int n = 0;
- while (shl->first_lnum < lnum && (shl->rm.regprog != NULL
- || (cur != NULL && pos_inprogress))) {
- next_search_hl(wp, shl, shl->first_lnum, (colnr_T)n,
- shl == &search_hl ? NULL : cur);
- pos_inprogress = !(cur == NULL || cur->pos.cur == 0);
- if (shl->lnum != 0) {
- shl->first_lnum = shl->lnum
- + shl->rm.endpos[0].lnum
- - shl->rm.startpos[0].lnum;
- n = shl->rm.endpos[0].col;
- } else {
- ++shl->first_lnum;
- n = 0;
- }
- }
- }
- if (shl != &search_hl && cur != NULL) {
- cur = cur->next;
- }
- }
-}
-
-/// Search for a next 'hlsearch' or match.
-/// Uses shl->buf.
-/// Sets shl->lnum and shl->rm contents.
-/// Note: Assumes a previous match is always before "lnum", unless
-/// shl->lnum is zero.
-/// Careful: Any pointers for buffer lines will become invalid.
-///
-/// @param shl points to search_hl or a match
-/// @param mincol minimal column for a match
-/// @param cur to retrieve match positions if any
-static void next_search_hl(win_T *win, match_T *shl, linenr_T lnum, colnr_T mincol,
- matchitem_T *cur)
- FUNC_ATTR_NONNULL_ARG(2)
-{
- linenr_T l;
- colnr_T matchcol;
- long nmatched = 0;
- int save_called_emsg = called_emsg;
-
- // for :{range}s/pat only highlight inside the range
- if (lnum < search_first_line || lnum > search_last_line) {
- shl->lnum = 0;
- return;
- }
-
- if (shl->lnum != 0) {
- // Check for three situations:
- // 1. If the "lnum" is below a previous match, start a new search.
- // 2. If the previous match includes "mincol", use it.
- // 3. Continue after the previous match.
- l = shl->lnum + shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum;
- if (lnum > l) {
- shl->lnum = 0;
- } else if (lnum < l || shl->rm.endpos[0].col > mincol) {
- return;
- }
- }
-
- /*
- * Repeat searching for a match until one is found that includes "mincol"
- * or none is found in this line.
- */
- called_emsg = FALSE;
- for (;;) {
- // Stop searching after passing the time limit.
- if (profile_passed_limit(shl->tm)) {
- shl->lnum = 0; // no match found in time
- break;
- }
- // Three situations:
- // 1. No useful previous match: search from start of line.
- // 2. Not Vi compatible or empty match: continue at next character.
- // Break the loop if this is beyond the end of the line.
- // 3. Vi compatible searching: continue at end of previous match.
- if (shl->lnum == 0) {
- matchcol = 0;
- } else if (vim_strchr(p_cpo, CPO_SEARCH) == NULL
- || (shl->rm.endpos[0].lnum == 0
- && shl->rm.endpos[0].col <= shl->rm.startpos[0].col)) {
- char_u *ml;
-
- matchcol = shl->rm.startpos[0].col;
- ml = ml_get_buf(shl->buf, lnum, false) + matchcol;
- if (*ml == NUL) {
- ++matchcol;
- shl->lnum = 0;
- break;
- }
- matchcol += utfc_ptr2len(ml);
- } else {
- matchcol = shl->rm.endpos[0].col;
- }
-
- shl->lnum = lnum;
- if (shl->rm.regprog != NULL) {
- // Remember whether shl->rm is using a copy of the regprog in
- // cur->match.
- bool regprog_is_copy = (shl != &search_hl
- && cur != NULL
- && shl == &cur->hl
- && cur->match.regprog == cur->hl.rm.regprog);
- int timed_out = false;
-
- nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum, matchcol,
- &(shl->tm), &timed_out);
- // Copy the regprog, in case it got freed and recompiled.
- if (regprog_is_copy) {
- cur->match.regprog = cur->hl.rm.regprog;
- }
- if (called_emsg || got_int || timed_out) {
- // Error while handling regexp: stop using this regexp.
- if (shl == &search_hl) {
- // don't free regprog in the match list, it's a copy
- vim_regfree(shl->rm.regprog);
- set_no_hlsearch(true);
- }
- shl->rm.regprog = NULL;
- shl->lnum = 0;
- got_int = FALSE; // avoid the "Type :quit to exit Vim" message
- break;
- }
- } else if (cur != NULL) {
- nmatched = next_search_hl_pos(shl, lnum, &(cur->pos), matchcol);
- }
- if (nmatched == 0) {
- shl->lnum = 0; // no match found
- break;
- }
- if (shl->rm.startpos[0].lnum > 0
- || shl->rm.startpos[0].col >= mincol
- || nmatched > 1
- || shl->rm.endpos[0].col > mincol) {
- shl->lnum += shl->rm.startpos[0].lnum;
- break; // useful match found
- }
-
- // Restore called_emsg for assert_fails().
- called_emsg = save_called_emsg;
- }
-}
-
-/// @param shl points to a match. Fill on match.
-/// @param posmatch match positions
-/// @param mincol minimal column for a match
-///
-/// @return one on match, otherwise return zero.
-static int next_search_hl_pos(match_T *shl, linenr_T lnum, posmatch_T *posmatch, colnr_T mincol)
- FUNC_ATTR_NONNULL_ALL
-{
- int i;
- int found = -1;
-
- shl->lnum = 0;
- for (i = posmatch->cur; i < MAXPOSMATCH; i++) {
- llpos_T *pos = &posmatch->pos[i];
-
- if (pos->lnum == 0) {
- break;
- }
- if (pos->len == 0 && pos->col < mincol) {
- continue;
- }
- if (pos->lnum == lnum) {
- if (found >= 0) {
- // if this match comes before the one at "found" then swap
- // them
- if (pos->col < posmatch->pos[found].col) {
- llpos_T tmp = *pos;
-
- *pos = posmatch->pos[found];
- posmatch->pos[found] = tmp;
- }
- } else {
- found = i;
- }
- }
- }
- posmatch->cur = 0;
- if (found >= 0) {
- colnr_T start = posmatch->pos[found].col == 0
- ? 0: posmatch->pos[found].col - 1;
- colnr_T end = posmatch->pos[found].col == 0
- ? MAXCOL : start + posmatch->pos[found].len;
-
- shl->lnum = lnum;
- shl->rm.startpos[0].lnum = 0;
- shl->rm.startpos[0].col = start;
- shl->rm.endpos[0].lnum = 0;
- shl->rm.endpos[0].col = end;
- shl->is_addpos = true;
- posmatch->cur = found + 1;
- return 1;
- }
- return 0;
-}
-
-
-/// Fill the grid from 'start_row' to 'end_row', from 'start_col' to 'end_col'
-/// with character 'c1' in first column followed by 'c2' in the other columns.
-/// Use attributes 'attr'.
-void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col, int end_col, int c1,
- int c2, int attr)
-{
- schar_T sc;
-
- int row_off = 0, col_off = 0;
- screen_adjust_grid(&grid, &row_off, &col_off);
- start_row += row_off;
- end_row += row_off;
- start_col += col_off;
- end_col += col_off;
-
- // safety check
- if (end_row > grid->Rows) {
- end_row = grid->Rows;
- }
- if (end_col > grid->Columns) {
- end_col = grid->Columns;
- }
-
- // nothing to do
- if (start_row >= end_row || start_col >= end_col) {
- return;
- }
-
- for (int row = start_row; row < end_row; row++) {
- // When drawing over the right half of a double-wide char clear
- // out the left half. When drawing over the left half of a
- // double wide-char clear out the right half. Only needed in a
- // terminal.
- if (start_col > 0 && grid_fix_col(grid, start_col, row) != start_col) {
- grid_puts_len(grid, (char_u *)" ", 1, row, start_col - 1, 0);
- }
- if (end_col < grid->Columns
- && grid_fix_col(grid, end_col, row) != end_col) {
- grid_puts_len(grid, (char_u *)" ", 1, row, end_col, 0);
- }
-
- // if grid was resized (in ext_multigrid mode), the UI has no redraw updates
- // for the newly resized grid. It is better mark everything as dirty and
- // send all the updates.
- int dirty_first = INT_MAX;
- int dirty_last = 0;
-
- int col = start_col;
- schar_from_char(sc, c1);
- int lineoff = grid->line_offset[row];
- for (col = start_col; col < end_col; col++) {
- int off = lineoff + col;
- if (schar_cmp(grid->chars[off], sc)
- || grid->attrs[off] != attr) {
- schar_copy(grid->chars[off], sc);
- grid->attrs[off] = attr;
- if (dirty_first == INT_MAX) {
- dirty_first = col;
- }
- dirty_last = col+1;
- }
- if (col == start_col) {
- schar_from_char(sc, c2);
- }
- }
- if (dirty_last > dirty_first) {
- // TODO(bfredl): support a cleared suffix even with a batched line?
- if (put_dirty_row == row) {
- put_dirty_first = MIN(put_dirty_first, dirty_first);
- put_dirty_last = MAX(put_dirty_last, dirty_last);
- } else if (grid->throttled) {
- // Note: assumes msg_grid is the only throttled grid
- assert(grid == &msg_grid);
- int dirty = 0;
- if (attr != HL_ATTR(HLF_MSG) || c2 != ' ') {
- dirty = dirty_last;
- } else if (c1 != ' ') {
- dirty = dirty_first + 1;
- }
- if (grid->dirty_col && dirty > grid->dirty_col[row]) {
- grid->dirty_col[row] = dirty;
- }
- } else {
- int last = c2 != ' ' ? dirty_last : dirty_first + (c1 != ' ');
- ui_line(grid, row, dirty_first, last, dirty_last, attr, false);
- }
- }
-
- if (end_col == grid->Columns) {
- grid->line_wraps[row] = false;
- }
- }
-}
-
/// Check if there should be a delay. Used before clearing or redrawing the
/// screen or the command line.
void check_for_delay(bool check_msg_scroll)
@@ -6482,95 +5630,15 @@ void check_for_delay(bool check_msg_scroll)
}
}
-/// (Re)allocates a window grid if size changed while in ext_multigrid mode.
-/// Updates size, offsets and handle for the grid regardless.
-///
-/// If "doclear" is true, don't try to copy from the old grid rather clear the
-/// resized grid.
-void win_grid_alloc(win_T *wp)
-{
- ScreenGrid *grid = &wp->w_grid;
- ScreenGrid *grid_allocated = &wp->w_grid_alloc;
-
- int rows = wp->w_height_inner;
- int cols = wp->w_width_inner;
- int total_rows = wp->w_height_outer;
- int total_cols = wp->w_width_outer;
-
- bool want_allocation = ui_has(kUIMultigrid) || wp->w_floating;
- bool has_allocation = (grid_allocated->chars != NULL);
-
- if (grid->Rows != rows) {
- wp->w_lines_valid = 0;
- xfree(wp->w_lines);
- wp->w_lines = xcalloc(rows+1, sizeof(wline_T));
- }
-
- int was_resized = false;
- if (want_allocation && (!has_allocation
- || grid_allocated->Rows != total_rows
- || grid_allocated->Columns != total_cols)) {
- grid_alloc(grid_allocated, total_rows, total_cols,
- wp->w_grid_alloc.valid, false);
- grid_allocated->valid = true;
- if (wp->w_floating && wp->w_float_config.border) {
- wp->w_redr_border = true;
- }
- was_resized = true;
- } else if (!want_allocation && has_allocation) {
- // Single grid mode, all rendering will be redirected to default_grid.
- // Only keep track of the size and offset of the window.
- grid_free(grid_allocated);
- grid_allocated->valid = false;
- was_resized = true;
- } else if (want_allocation && has_allocation && !wp->w_grid_alloc.valid) {
- grid_invalidate(grid_allocated);
- grid_allocated->valid = true;
- }
-
- grid->Rows = rows;
- grid->Columns = cols;
-
- if (want_allocation) {
- grid->target = grid_allocated;
- grid->row_offset = wp->w_border_adj[0];
- grid->col_offset = wp->w_border_adj[3];
- } else {
- grid->target = &default_grid;
- grid->row_offset = wp->w_winrow;
- grid->col_offset = wp->w_wincol;
- }
-
- // send grid resize event if:
- // - a grid was just resized
- // - screen_resize was called and all grid sizes must be sent
- // - the UI wants multigrid event (necessary)
- if ((send_grid_resize || was_resized) && want_allocation) {
- ui_call_grid_resize(grid_allocated->handle,
- grid_allocated->Columns, grid_allocated->Rows);
- }
-}
-
-/// assign a handle to the grid. The grid need not be allocated.
-void grid_assign_handle(ScreenGrid *grid)
-{
- static int last_grid_handle = DEFAULT_GRID_HANDLE;
-
- // only assign a grid handle if not already
- if (grid->handle == 0) {
- grid->handle = ++last_grid_handle;
- }
-}
-
/// Resize the screen to Rows and Columns.
///
/// Allocate default_grid.chars[] and other grid arrays.
///
/// There may be some time between setting Rows and Columns and (re)allocating
/// default_grid arrays. This happens when starting up and when
-/// (manually) changing the shell size. Always use default_grid.Rows and
+/// (manually) changing the screen size. Always use default_grid.rows and
/// default_grid.Columns to access items in default_grid.chars[]. Use Rows
-/// and Columns for positioning text etc. where the final size of the shell is
+/// and Columns for positioning text etc. where the final size of the screen is
/// needed.
void screenalloc(void)
{
@@ -6589,8 +5657,8 @@ retry:
// when Rows and Columns have been set and we have started doing full
// screen stuff.
if ((default_grid.chars != NULL
- && Rows == default_grid.Rows
- && Columns == default_grid.Columns
+ && Rows == default_grid.rows
+ && Columns == default_grid.cols
)
|| Rows == 0
|| Columns == 0
@@ -6605,14 +5673,14 @@ retry:
*/
++RedrawingDisabled;
- // win_new_shellsize will recompute floats position, but tell the
+ // win_new_screensize will recompute floats position, but tell the
// compositor to not redraw them yet
ui_comp_set_screen_valid(false);
if (msg_grid.chars) {
msg_grid_invalid = true;
}
- win_new_shellsize(); // fit the windows in the new sized shell
+ win_new_screensize(); // fit the windows in the new sized screen
comp_col(); // recompute columns for shown command and ruler
@@ -6630,7 +5698,7 @@ retry:
StlClickDefinition *new_tab_page_click_defs =
xcalloc((size_t)Columns, sizeof(*new_tab_page_click_defs));
- clear_tab_page_click_defs(tab_page_click_defs, tab_page_click_defs_size);
+ stl_clear_click_defs(tab_page_click_defs, tab_page_click_defs_size);
xfree(tab_page_click_defs);
tab_page_click_defs = new_tab_page_click_defs;
@@ -6661,90 +5729,19 @@ retry:
resizing = false;
}
-void grid_alloc(ScreenGrid *grid, int rows, int columns, bool copy, bool valid)
-{
- int new_row;
- ScreenGrid new = *grid;
- assert(rows >= 0 && columns >= 0);
- size_t ncells = (size_t)rows * columns;
- new.chars = xmalloc(ncells * sizeof(schar_T));
- new.attrs = xmalloc(ncells * sizeof(sattr_T));
- new.line_offset = xmalloc((size_t)(rows * sizeof(unsigned)));
- new.line_wraps = xmalloc((size_t)(rows * sizeof(char_u)));
-
- new.Rows = rows;
- new.Columns = columns;
-
- for (new_row = 0; new_row < new.Rows; new_row++) {
- new.line_offset[new_row] = new_row * new.Columns;
- new.line_wraps[new_row] = false;
-
- grid_clear_line(&new, new.line_offset[new_row], columns, valid);
-
- if (copy) {
- // If the screen is not going to be cleared, copy as much as
- // possible from the old screen to the new one and clear the rest
- // (used when resizing the window at the "--more--" prompt or when
- // executing an external command, for the GUI).
- if (new_row < grid->Rows && grid->chars != NULL) {
- int len = MIN(grid->Columns, new.Columns);
- memmove(new.chars + new.line_offset[new_row],
- grid->chars + grid->line_offset[new_row],
- (size_t)len * sizeof(schar_T));
- memmove(new.attrs + new.line_offset[new_row],
- grid->attrs + grid->line_offset[new_row],
- (size_t)len * sizeof(sattr_T));
- }
- }
- }
- grid_free(grid);
- *grid = new;
-
- // Share a single scratch buffer for all grids, by
- // ensuring it is as wide as the widest grid.
- if (linebuf_size < (size_t)columns) {
- xfree(linebuf_char);
- xfree(linebuf_attr);
- linebuf_char = xmalloc(columns * sizeof(schar_T));
- linebuf_attr = xmalloc(columns * sizeof(sattr_T));
- linebuf_size = columns;
- }
-}
-
-void grid_free(ScreenGrid *grid)
-{
- xfree(grid->chars);
- xfree(grid->attrs);
- xfree(grid->line_offset);
- xfree(grid->line_wraps);
-
- grid->chars = NULL;
- grid->attrs = NULL;
- grid->line_offset = NULL;
- grid->line_wraps = NULL;
-}
-
-/// Doesn't allow reinit, so must only be called by free_all_mem!
-void screen_free_all_mem(void)
-{
- grid_free(&default_grid);
- xfree(linebuf_char);
- xfree(linebuf_attr);
-}
-
-/// Clear tab_page_click_defs table
+/// Clear status line, window bar or tab page line click definition table
///
/// @param[out] tpcd Table to clear.
/// @param[in] tpcd_size Size of the table.
-void clear_tab_page_click_defs(StlClickDefinition *const tpcd, const long tpcd_size)
+void stl_clear_click_defs(StlClickDefinition *const click_defs, const long click_defs_size)
{
- if (tpcd != NULL) {
- for (long i = 0; i < tpcd_size; i++) {
- if (i == 0 || tpcd[i].func != tpcd[i - 1].func) {
- xfree(tpcd[i].func);
+ if (click_defs != NULL) {
+ for (long i = 0; i < click_defs_size; i++) {
+ if (i == 0 || click_defs[i].func != click_defs[i - 1].func) {
+ xfree(click_defs[i].func);
}
}
- memset(tpcd, 0, (size_t)tpcd_size * sizeof(tpcd[0]));
+ memset(click_defs, 0, (size_t)click_defs_size * sizeof(click_defs[0]));
}
}
@@ -6760,9 +5757,9 @@ void screenclear(void)
}
// blank out the default grid
- for (i = 0; i < default_grid.Rows; i++) {
+ for (i = 0; i < default_grid.rows; i++) {
grid_clear_line(&default_grid, default_grid.line_offset[i],
- default_grid.Columns, true);
+ default_grid.cols, true);
default_grid.line_wraps[i] = false;
}
@@ -6799,28 +5796,6 @@ void screenclear(void)
}
}
-/// clear a line in the grid starting at "off" until "width" characters
-/// are cleared.
-void grid_clear_line(ScreenGrid *grid, unsigned off, int width, bool valid)
-{
- for (int col = 0; col < width; col++) {
- schar_from_ascii(grid->chars[off + col], ' ');
- }
- int fill = valid ? 0 : -1;
- (void)memset(grid->attrs + off, fill, (size_t)width * sizeof(sattr_T));
-}
-
-void grid_invalidate(ScreenGrid *grid)
-{
- (void)memset(grid->attrs, -1, sizeof(sattr_T) * grid->Rows * grid->Columns);
-}
-
-bool grid_invalid_row(ScreenGrid *grid, int row)
-{
- return grid->attrs[grid->line_offset[row]] < 0;
-}
-
-
/// Copy part of a grid line for vertically split window.
static void linecopy(ScreenGrid *grid, int to, int from, int col, int width)
{
@@ -6833,12 +5808,17 @@ static void linecopy(ScreenGrid *grid, int to, int from, int col, int width)
width * sizeof(sattr_T));
}
-/*
- * Set cursor to its position in the current window.
- */
+/// Set cursor to its position in the current window.
void setcursor(void)
{
- if (redrawing()) {
+ setcursor_mayforce(false);
+}
+
+/// Set cursor to its position in the current window.
+/// @param force when true, also when not redrawing.
+void setcursor_mayforce(bool force)
+{
+ if (force || redrawing()) {
validate_cursor();
ScreenGrid *grid = &curwin->w_grid;
@@ -6848,11 +5828,11 @@ void setcursor(void)
// With 'rightleft' set and the cursor on a double-wide character,
// position it on the leftmost column.
col = curwin->w_width_inner - curwin->w_wcol
- - ((utf_ptr2cells(get_cursor_pos_ptr()) == 2
+ - ((utf_ptr2cells((char *)get_cursor_pos_ptr()) == 2
&& vim_isprintc(gchar_cursor())) ? 2 : 1);
}
- screen_adjust_grid(&grid, &row, &col);
+ grid_adjust(&grid, &row, &col);
ui_grid_cursor_goto(grid->handle, row, col);
}
}
@@ -6868,16 +5848,16 @@ void win_scroll_lines(win_T *wp, int row, int line_count)
}
// No lines are being moved, just draw over the entire area
- if (row + abs(line_count) >= wp->w_grid.Rows) {
+ if (row + abs(line_count) >= wp->w_grid.rows) {
return;
}
if (line_count < 0) {
grid_del_lines(&wp->w_grid, row, -line_count,
- wp->w_grid.Rows, 0, wp->w_grid.Columns);
+ wp->w_grid.rows, 0, wp->w_grid.cols);
} else {
grid_ins_lines(&wp->w_grid, row, line_count,
- wp->w_grid.Rows, 0, wp->w_grid.Columns);
+ wp->w_grid.rows, 0, wp->w_grid.cols);
}
}
@@ -6891,7 +5871,6 @@ void win_scroll_lines(win_T *wp, int row, int line_count)
* screen changes, and in the meantime, everything still works.
*/
-
/// insert lines on the screen and move the existing lines down
/// 'line_count' is the number of lines to be inserted.
/// 'end' is the line after the scrolled part. Normally it is Rows.
@@ -6905,7 +5884,7 @@ void grid_ins_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
unsigned temp;
int row_off = 0;
- screen_adjust_grid(&grid, &row_off, &col);
+ grid_adjust(&grid, &row_off, &col);
row += row_off;
end += row_off;
@@ -6916,7 +5895,7 @@ void grid_ins_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
// Shift line_offset[] line_count down to reflect the inserted lines.
// Clear the inserted lines.
for (i = 0; i < line_count; i++) {
- if (width != grid->Columns) {
+ if (width != grid->cols) {
// need to copy part of a line
j = end - 1 - i;
while ((j -= line_count) >= row) {
@@ -6934,15 +5913,13 @@ void grid_ins_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
}
grid->line_offset[j + line_count] = temp;
grid->line_wraps[j + line_count] = false;
- grid_clear_line(grid, temp, grid->Columns, false);
+ grid_clear_line(grid, temp, grid->cols, false);
}
}
if (!grid->throttled) {
- ui_call_grid_scroll(grid->handle, row, end, col, col+width, -line_count, 0);
+ ui_call_grid_scroll(grid->handle, row, end, col, col + width, -line_count, 0);
}
-
- return;
}
/// delete lines on the screen and move lines up.
@@ -6956,7 +5933,7 @@ void grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
unsigned temp;
int row_off = 0;
- screen_adjust_grid(&grid, &row_off, &col);
+ grid_adjust(&grid, &row_off, &col);
row += row_off;
end += row_off;
@@ -6967,7 +5944,7 @@ void grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
// Now shift line_offset[] line_count up to reflect the deleted lines.
// Clear the inserted lines.
for (i = 0; i < line_count; i++) {
- if (width != grid->Columns) {
+ if (width != grid->cols) {
// need to copy part of a line
j = row + i;
while ((j += line_count) <= end - 1) {
@@ -6986,18 +5963,15 @@ void grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
}
grid->line_offset[j - line_count] = temp;
grid->line_wraps[j - line_count] = false;
- grid_clear_line(grid, temp, grid->Columns, false);
+ grid_clear_line(grid, temp, grid->cols, false);
}
}
if (!grid->throttled) {
- ui_call_grid_scroll(grid->handle, row, end, col, col+width, line_count, 0);
+ ui_call_grid_scroll(grid->handle, row, end, col, col + width, line_count, 0);
}
-
- return;
}
-
// Show the current mode and ruler.
//
// If clear_cmdline is true, clear the rest of the cmdline.
@@ -7022,8 +5996,8 @@ int showmode(void)
msg_grid_validate();
do_mode = ((p_smd && msg_silent == 0)
- && ((State & TERM_FOCUS)
- || (State & INSERT)
+ && ((State & MODE_TERMINAL)
+ || (State & MODE_INSERT)
|| restart_edit != NUL
|| VIsual_active));
if (do_mode || reg_recording != 0) {
@@ -7069,13 +6043,13 @@ int showmode(void)
length = (Rows - msg_row) * Columns - 3;
}
if (edit_submode_extra != NULL) {
- length -= vim_strsize(edit_submode_extra);
+ length -= vim_strsize((char *)edit_submode_extra);
}
if (length > 0) {
if (edit_submode_pre != NULL) {
- length -= vim_strsize(edit_submode_pre);
+ length -= vim_strsize((char *)edit_submode_pre);
}
- if (length - vim_strsize(edit_submode) > 0) {
+ if (length - vim_strsize((char *)edit_submode) > 0) {
if (edit_submode_pre != NULL) {
msg_puts_attr((const char *)edit_submode_pre, attr);
}
@@ -7092,13 +6066,13 @@ int showmode(void)
}
}
} else {
- if (State & TERM_FOCUS) {
+ if (State & MODE_TERMINAL) {
msg_puts_attr(_(" TERMINAL"), attr);
} else if (State & VREPLACE_FLAG) {
msg_puts_attr(_(" VREPLACE"), attr);
} else if (State & REPLACE_FLAG) {
msg_puts_attr(_(" REPLACE"), attr);
- } else if (State & INSERT) {
+ } else if (State & MODE_INSERT) {
if (p_ri) {
msg_puts_attr(_(" REVERSE"), attr);
}
@@ -7114,15 +6088,15 @@ int showmode(void)
if (p_hkmap) {
msg_puts_attr(_(" Hebrew"), attr);
}
- if (State & LANGMAP) {
+ if (State & MODE_LANGMAP) {
if (curwin->w_p_arab) {
msg_puts_attr(_(" Arabic"), attr);
- } else if (get_keymap_str(curwin, (char_u *)" (%s)",
- NameBuff, MAXPATHL)) {
+ } else if (get_keymap_str(curwin, " (%s)",
+ (char *)NameBuff, MAXPATHL)) {
msg_puts_attr((char *)NameBuff, attr);
}
}
- if ((State & INSERT) && p_paste) {
+ if ((State & MODE_INSERT) && p_paste) {
msg_puts_attr(_(" (paste)"), attr);
}
@@ -7184,10 +6158,10 @@ int showmode(void)
clear_showcmd();
}
- // If the last window has no status line, the ruler is after the mode
- // message and must be redrawn
+ // If the last window has no status line and global statusline is disabled,
+ // the ruler is after the mode message and must be redrawn
win_T *last = lastwin_nofloating();
- if (redrawing() && last->w_status_height == 0) {
+ if (redrawing() && last->w_status_height == 0 && global_stl_height() == 0) {
win_redr_ruler(last, true);
}
redraw_cmdline = false;
@@ -7221,6 +6195,10 @@ void unshowmode(bool force)
// Clear the mode message.
void clearmode(void)
{
+ if (p_ch <= 0 && !ui_has(kUIMessages)) {
+ return;
+ }
+
const int save_msg_row = msg_row;
const int save_msg_col = msg_col;
@@ -7238,6 +6216,10 @@ void clearmode(void)
static void recording_mode(int attr)
{
+ if (p_ch <= 0 && !ui_has(kUIMessages)) {
+ return;
+ }
+
msg_puts_attr(_("recording"), attr);
if (!shortmess(SHM_RECORDING)) {
char s[4];
@@ -7283,10 +6265,9 @@ void draw_tabline(void)
return;
}
-
// Init TabPageIdxs[] to zero: Clicking outside of tabs has no effect.
assert(Columns == tab_page_click_defs_size);
- clear_tab_page_click_defs(tab_page_click_defs, tab_page_click_defs_size);
+ stl_clear_click_defs(tab_page_click_defs, tab_page_click_defs_size);
// Use the 'tabline' option if it's set.
if (*p_tal != NUL) {
@@ -7295,10 +6276,9 @@ void draw_tabline(void)
// Check for an error. If there is one we would loop in redrawing the
// screen. Avoid that by making 'tabline' empty.
did_emsg = false;
- win_redr_custom(NULL, false);
+ win_redr_custom(NULL, false, false);
if (did_emsg) {
- set_string_option_direct("tabline", -1,
- (char_u *)"", OPT_FREE, SID_ERROR);
+ set_string_option_direct("tabline", -1, "", OPT_FREE, SID_ERROR);
}
did_emsg |= saved_did_emsg;
} else {
@@ -7332,7 +6312,6 @@ void draw_tabline(void)
wp = tp->tp_firstwin;
}
-
if (tp->tp_topframe == topframe) {
attr = win_hl_attr(cwp, HLF_TPS);
}
@@ -7354,7 +6333,6 @@ void draw_tabline(void)
}
}
-
if (modified || wincount > 1) {
if (wincount > 1) {
vim_snprintf((char *)NameBuff, MAXPATHL, "%d", wincount);
@@ -7376,11 +6354,11 @@ void draw_tabline(void)
if (room > 0) {
// Get buffer name in NameBuff[]
get_trans_bufname(cwp->w_buffer);
- (void)shorten_dir(NameBuff);
- len = vim_strsize(NameBuff);
+ shorten_dir(NameBuff);
+ len = vim_strsize((char *)NameBuff);
p = NameBuff;
while (len > room) {
- len -= ptr2cells(p);
+ len -= ptr2cells((char *)p);
MB_PTR_ADV(p);
}
if (len > Columns - col - 1) {
@@ -7429,35 +6407,49 @@ void draw_tabline(void)
void ui_ext_tabline_update(void)
{
- Array tabs = ARRAY_DICT_INIT;
+ Arena arena = ARENA_EMPTY;
+ arena_start(&arena, &ui_ext_fixblk);
+
+ size_t n_tabs = 0;
FOR_ALL_TABS(tp) {
- Dictionary tab_info = ARRAY_DICT_INIT;
- PUT(tab_info, "tab", TABPAGE_OBJ(tp->handle));
+ n_tabs++;
+ }
+
+ Array tabs = arena_array(&arena, n_tabs);
+ FOR_ALL_TABS(tp) {
+ Dictionary tab_info = arena_dict(&arena, 2);
+ PUT_C(tab_info, "tab", TABPAGE_OBJ(tp->handle));
win_T *cwp = (tp == curtab) ? curwin : tp->tp_curwin;
get_trans_bufname(cwp->w_buffer);
- PUT(tab_info, "name", STRING_OBJ(cstr_to_string((char *)NameBuff)));
+ PUT_C(tab_info, "name", STRING_OBJ(arena_string(&arena, cstr_as_string((char *)NameBuff))));
- ADD(tabs, DICTIONARY_OBJ(tab_info));
+ ADD_C(tabs, DICTIONARY_OBJ(tab_info));
+ }
+
+ size_t n_buffers = 0;
+ FOR_ALL_BUFFERS(buf) {
+ n_buffers += buf->b_p_bl ? 1 : 0;
}
- Array buffers = ARRAY_DICT_INIT;
+ Array buffers = arena_array(&arena, n_buffers);
FOR_ALL_BUFFERS(buf) {
// Do not include unlisted buffers
if (!buf->b_p_bl) {
continue;
}
- Dictionary buffer_info = ARRAY_DICT_INIT;
- PUT(buffer_info, "buffer", BUFFER_OBJ(buf->handle));
+ Dictionary buffer_info = arena_dict(&arena, 2);
+ PUT_C(buffer_info, "buffer", BUFFER_OBJ(buf->handle));
get_trans_bufname(buf);
- PUT(buffer_info, "name", STRING_OBJ(cstr_to_string((char *)NameBuff)));
+ PUT_C(buffer_info, "name", STRING_OBJ(arena_string(&arena, cstr_as_string((char *)NameBuff))));
- ADD(buffers, DICTIONARY_OBJ(buffer_info));
+ ADD_C(buffers, DICTIONARY_OBJ(buffer_info));
}
ui_call_tabline_update(curtab->handle, tabs, curbuf->handle, buffers);
+ arena_mem_free(arena_finish(&arena), &ui_ext_fixblk);
}
/*
@@ -7469,9 +6461,9 @@ void get_trans_bufname(buf_T *buf)
if (buf_spname(buf) != NULL) {
STRLCPY(NameBuff, buf_spname(buf), MAXPATHL);
} else {
- home_replace(buf, buf->b_fname, NameBuff, MAXPATHL, TRUE);
+ home_replace(buf, buf->b_fname, (char *)NameBuff, MAXPATHL, true);
}
- trans_characters(NameBuff, MAXPATHL);
+ trans_characters((char *)NameBuff, MAXPATHL);
}
/*
@@ -7502,16 +6494,22 @@ int fillchar_status(int *attr, win_T *wp)
return '=';
}
-/*
- * Get the character to use in a separator between vertically split windows.
- * Get its attributes in "*attr".
- */
+/// Get the character to use in a separator between vertically split windows.
+/// Get its attributes in "*attr".
static int fillchar_vsep(win_T *wp, int *attr)
{
*attr = win_hl_attr(wp, HLF_C);
return wp->w_p_fcs_chars.vert;
}
+/// Get the character to use in a separator between horizontally split windows.
+/// Get its attributes in "*attr".
+static int fillchar_hsep(win_T *wp, int *attr)
+{
+ *attr = win_hl_attr(wp, HLF_C);
+ return wp->w_p_fcs_chars.horiz;
+}
+
/*
* Return TRUE if redrawing should currently be done.
*/
@@ -7526,7 +6524,8 @@ int redrawing(void)
*/
int messaging(void)
{
- return !(p_lz && char_avail() && !KeyTyped);
+ return !(p_lz && char_avail() && !KeyTyped)
+ && (p_ch > 0 || ui_has(kUIMessages));
}
/// Show current status info in ruler and various other places
@@ -7537,11 +6536,15 @@ void showruler(bool always)
if (!always && !redrawing()) {
return;
}
- if ((*p_stl != NUL || *curwin->w_p_stl != NUL) && curwin->w_status_height) {
+ if ((*p_stl != NUL || *curwin->w_p_stl != NUL)
+ && (curwin->w_status_height || global_stl_height())) {
redraw_custom_statusline(curwin);
} else {
win_redr_ruler(curwin, always);
}
+ if (*p_wbr != NUL || *curwin->w_p_wbr != NUL) {
+ win_redr_winbar(curwin);
+ }
if (need_maketitle
|| (p_icon && (stl_syntax & STL_IN_ICON))
@@ -7556,6 +6559,7 @@ void showruler(bool always)
static void win_redr_ruler(win_T *wp, bool always)
{
+ bool is_stl_global = global_stl_height() > 0;
static bool did_show_ext_ruler = false;
// If 'ruler' off or redrawing disabled, don't do anything
@@ -7573,32 +6577,25 @@ static void win_redr_ruler(win_T *wp, bool always)
// Don't draw the ruler while doing insert-completion, it might overwrite
// the (long) mode message.
- if (wp == lastwin && lastwin->w_status_height == 0) {
+ if (wp == lastwin && lastwin->w_status_height == 0 && !is_stl_global) {
if (edit_submode != NULL) {
return;
}
}
- if (*p_ruf) {
- int save_called_emsg = called_emsg;
-
- called_emsg = false;
- win_redr_custom(wp, true);
- if (called_emsg) {
- set_string_option_direct("rulerformat", -1, (char_u *)"",
- OPT_FREE, SID_ERROR);
+ if (*p_ruf && p_ch > 0 && !ui_has(kUIMessages)) {
+ const int called_emsg_before = called_emsg;
+ win_redr_custom(wp, false, true);
+ if (called_emsg > called_emsg_before) {
+ set_string_option_direct("rulerformat", -1, "", OPT_FREE, SID_ERROR);
}
- called_emsg |= save_called_emsg;
return;
}
- /*
- * Check if not in Insert mode and the line is empty (will show "0-1").
- */
- int empty_line = FALSE;
- if (!(State & INSERT)
- && *ml_get_buf(wp->w_buffer, wp->w_cursor.lnum, FALSE) == NUL) {
- empty_line = TRUE;
+ // Check if not in Insert mode and the line is empty (will show "0-1").
+ int empty_line = false;
+ if ((State & MODE_INSERT) == 0 && *ml_get_buf(wp->w_buffer, wp->w_cursor.lnum, false) == NUL) {
+ empty_line = true;
}
/*
@@ -7628,6 +6625,12 @@ static void win_redr_ruler(win_T *wp, bool always)
off = wp->w_wincol;
width = wp->w_width;
part_of_status = true;
+ } else if (is_stl_global) {
+ row = Rows - p_ch - 1;
+ fillchar = fillchar_status(&attr, wp);
+ off = 0;
+ width = Columns;
+ part_of_status = true;
} else {
row = Rows - 1;
fillchar = ' ';
@@ -7636,6 +6639,10 @@ static void win_redr_ruler(win_T *wp, bool always)
off = 0;
}
+ if (!part_of_status && p_ch < 1 && !ui_has(kUIMessages)) {
+ return;
+ }
+
// 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) {
@@ -7645,13 +6652,13 @@ static void win_redr_ruler(win_T *wp, bool always)
}
#define RULER_BUF_LEN 70
- char_u buffer[RULER_BUF_LEN];
+ char buffer[RULER_BUF_LEN];
/*
* Some sprintfs return the length, some return a pointer.
* To avoid portability problems we use strlen() here.
*/
- vim_snprintf((char *)buffer, RULER_BUF_LEN, "%" PRId64 ",",
+ vim_snprintf(buffer, RULER_BUF_LEN, "%" PRId64 ",",
(wp->w_buffer->b_ml.ml_flags & ML_EMPTY) ? (int64_t)0L
: (int64_t)wp->w_cursor.lnum);
size_t len = STRLEN(buffer);
@@ -7667,7 +6674,7 @@ static void win_redr_ruler(win_T *wp, bool always)
int i = (int)STRLEN(buffer);
get_rel_pos(wp, buffer + i + 1, RULER_BUF_LEN - i - 1);
int o = i + vim_strsize(buffer + i + 1);
- if (wp->w_status_height == 0) { // can't use last char of screen
+ if (wp->w_status_height == 0 && !is_stl_global) { // can't use last char of screen
o++;
}
int this_ru_col = ru_col - (Columns - width);
@@ -7689,11 +6696,11 @@ static void win_redr_ruler(win_T *wp, bool always)
}
if (ui_has(kUIMessages) && !part_of_status) {
- Array content = ARRAY_DICT_INIT;
- Array chunk = ARRAY_DICT_INIT;
- ADD(chunk, INTEGER_OBJ(attr));
- ADD(chunk, STRING_OBJ(cstr_to_string((char *)buffer)));
- ADD(content, ARRAY_OBJ(chunk));
+ MAXSIZE_TEMP_ARRAY(content, 1);
+ MAXSIZE_TEMP_ARRAY(chunk, 2);
+ ADD_C(chunk, INTEGER_OBJ(attr));
+ ADD_C(chunk, STRING_OBJ(cstr_as_string((char *)buffer)));
+ ADD_C(content, ARRAY_OBJ(chunk));
ui_call_msg_ruler(content);
did_show_ext_ruler = true;
} else {
@@ -7712,7 +6719,7 @@ static void win_redr_ruler(win_T *wp, bool always)
}
ScreenGrid *grid = part_of_status ? &default_grid : &msg_grid_adj;
- grid_puts(grid, buffer, row, this_ru_col + off, attr);
+ grid_puts(grid, (char_u *)buffer, row, this_ru_col + off, attr);
grid_fill(grid, row, row + 1,
this_ru_col + off + (int)STRLEN(buffer), off + width, fillchar,
fillchar, attr);
@@ -7815,14 +6822,12 @@ static void margin_columns_win(win_T *wp, int *left_col, int *right_col)
prev_col_off = cur_col_off;
}
-/// Set dimensions of the Nvim application "shell".
+/// Set dimensions of the Nvim application "screen".
void screen_resize(int width, int height)
{
- static bool recursive = false;
-
// Avoid recursiveness, can happen when setting the window size causes
// another window-changed signal.
- if (updating_screen || recursive) {
+ if (updating_screen || resizing_screen) {
return;
}
@@ -7830,9 +6835,9 @@ void screen_resize(int width, int height)
return;
}
- if (State == HITRETURN || State == SETWSIZE) {
+ if (State == MODE_HITRETURN || State == MODE_SETWSIZE) {
// postpone the resizing
- State = SETWSIZE;
+ State = MODE_SETWSIZE;
return;
}
@@ -7844,13 +6849,13 @@ void screen_resize(int width, int height)
return;
}
- recursive = true;
+ resizing_screen = true;
Rows = height;
Columns = width;
- check_shellsize();
+ check_screensize();
int max_p_ch = Rows - min_rows() + 1;
- if (!ui_has(kUIMessages) && p_ch > max_p_ch) {
+ if (!ui_has(kUIMessages) && p_ch > 0 && p_ch > max_p_ch) {
p_ch = max_p_ch ? max_p_ch : 1;
}
height = Rows;
@@ -7861,11 +6866,11 @@ void screen_resize(int width, int height)
send_grid_resize = true;
- /* The window layout used to be adjusted here, but it now happens in
- * screenalloc() (also invoked from screenclear()). That is because the
- * "recursive" check above may skip this, but not screenalloc(). */
+ /// The window layout used to be adjusted here, but it now happens in
+ /// screenalloc() (also invoked from screenclear()). That is because the
+ /// recursize "resizing_screen" check above may skip this, but not screenalloc().
- if (State != ASKMORE && State != EXTERNCMD && State != CONFIRM) {
+ if (State != MODE_ASKMORE && State != MODE_EXTERNCMD && State != MODE_CONFIRM) {
screenclear();
}
@@ -7884,7 +6889,7 @@ void screen_resize(int width, int height)
* Always need to call update_screen() or screenalloc(), to make
* sure Rows/Columns and the size of the screen is correct!
*/
- if (State == ASKMORE || State == EXTERNCMD || State == CONFIRM
+ if (State == MODE_ASKMORE || State == MODE_EXTERNCMD || State == MODE_CONFIRM
|| exmode_active) {
screenalloc();
if (msg_grid.chars) {
@@ -7898,7 +6903,7 @@ void screen_resize(int width, int height)
if (curwin->w_p_scb) {
do_check_scrollbind(true);
}
- if (State & CMDLINE) {
+ if (State & MODE_CMDLINE) {
redraw_popupmenu = false;
update_screen(NOT_VALID);
redrawcmdline();
@@ -7922,52 +6927,26 @@ void screen_resize(int width, int height)
}
ui_flush();
}
- recursive = false;
+ resizing_screen = false;
}
-/// Check if the new Nvim application "shell" dimensions are valid.
+/// Check if the new Nvim application "screen" dimensions are valid.
/// Correct it if it's too small or way too big.
-void check_shellsize(void)
+void check_screensize(void)
{
+ // Limit Rows and Columns to avoid an overflow in Rows * Columns.
if (Rows < min_rows()) {
// need room for one window and command line
Rows = min_rows();
+ } else if (Rows > 1000) {
+ Rows = 1000;
}
- limit_screen_size();
-}
-// Limit Rows and Columns to avoid an overflow in Rows * Columns.
-void limit_screen_size(void)
-{
if (Columns < MIN_COLUMNS) {
Columns = MIN_COLUMNS;
} else if (Columns > 10000) {
Columns = 10000;
}
-
- if (Rows > 1000) {
- Rows = 1000;
- }
-}
-
-void win_new_shellsize(void)
-{
- static long old_Rows = 0;
- static long old_Columns = 0;
-
- if (old_Rows != Rows) {
- // If 'window' uses the whole screen, keep it using that.
- // Don't change it when set with "-w size" on the command line.
- if (p_window == old_Rows - 1 || (old_Rows == 0 && p_window == 0)) {
- p_window = Rows - 1;
- }
- old_Rows = Rows;
- shell_new_rows(); // update window sizes
- }
- if (old_Columns != Columns) {
- old_Columns = Columns;
- shell_new_columns(); // update window sizes
- }
}
win_T *get_win_by_grid_handle(handle_T handle)
@@ -7979,4 +6958,3 @@ win_T *get_win_by_grid_handle(handle_T handle)
}
return NULL;
}
-
diff --git a/src/nvim/screen.h b/src/nvim/screen.h
index d704a6eb8a..9eda5223f1 100644
--- a/src/nvim/screen.h
+++ b/src/nvim/screen.h
@@ -4,16 +4,14 @@
#include <stdbool.h>
#include "nvim/buffer_defs.h"
-#include "nvim/grid_defs.h"
+#include "nvim/grid.h"
#include "nvim/pos.h"
#include "nvim/types.h"
-/*
- * flags for update_screen()
- * The higher the value, the higher the priority
- */
-#define VALID 10 /* buffer not changed, or changes marked
- with b_mod_* */
+// flags for update_screen()
+// The higher the value, the higher the priority
+#define VALID 10 // buffer not changed, or changes marked
+ // with b_mod_*
#define INVERTED 20 // redisplay inverted part that changed
#define INVERTED_ALL 25 // redisplay whole inverted part
#define REDRAW_TOP 30 // display first w_upd_rows screen lines
@@ -21,46 +19,32 @@
#define NOT_VALID 40 // buffer needs complete redraw
#define CLEAR 50 // screen messed up, clear it
-/// By default, all widows are draw on a single rectangular grid, represented by
-/// this ScreenGrid instance. In multigrid mode each window will have its own
-/// grid, then this is only used for global screen elements that hasn't been
-/// externalized.
-///
-/// Note: before the screen is initialized and when out of memory these can be
-/// NULL.
-EXTERN ScreenGrid default_grid INIT(= SCREEN_GRID_INIT);
-
-#define DEFAULT_GRID_HANDLE 1 // handle for the default_grid
+/// corner value flags for hsep_connected and vsep_connected
+typedef enum {
+ WC_TOP_LEFT = 0,
+ WC_TOP_RIGHT,
+ WC_BOTTOM_LEFT,
+ WC_BOTTOM_RIGHT,
+} WindowCorner;
// Maximum columns for terminal highlight attributes
#define TERM_ATTRS_MAX 1024
-/// Status line click definition
-typedef struct {
- enum {
- kStlClickDisabled = 0, ///< Clicks to this area are ignored.
- kStlClickTabSwitch, ///< Switch to the given tab.
- kStlClickTabClose, ///< Close given tab.
- kStlClickFuncRun, ///< Run user function.
- } type; ///< Type of the click.
- int tabnr; ///< Tab page number.
- char *func; ///< Function to run.
-} StlClickDefinition;
-
-/// Used for tabline clicks
-typedef struct {
- StlClickDefinition def; ///< Click definition.
- const char *start; ///< Location where region starts.
-} StlClickRecord;
-
/// Array defining what should be done when tabline is clicked
-extern StlClickDefinition *tab_page_click_defs;
+EXTERN StlClickDefinition *tab_page_click_defs INIT(= NULL);
/// Size of the tab_page_click_defs array
-extern long tab_page_click_defs_size;
+EXTERN long tab_page_click_defs_size INIT(= 0);
+
+#define W_ENDCOL(wp) ((wp)->w_wincol + (wp)->w_width)
+#define W_ENDROW(wp) ((wp)->w_winrow + (wp)->w_height)
+
+// While redrawing the screen this flag is set. It means the screen size
+// ('lines' and 'rows') must not be changed.
+EXTERN bool updating_screen INIT(= 0);
-#define W_ENDCOL(wp) (wp->w_wincol + wp->w_width)
-#define W_ENDROW(wp) (wp->w_winrow + wp->w_height)
+// While resizing the screen this flag is set.
+EXTERN bool resizing_screen INIT(= 0);
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "screen.h.generated.h"
diff --git a/src/nvim/search.c b/src/nvim/search.c
index 906c9a6f47..f3061b4dc4 100644
--- a/src/nvim/search.c
+++ b/src/nvim/search.c
@@ -13,8 +13,8 @@
#include "nvim/ascii.h"
#include "nvim/buffer.h"
-#include "nvim/charset.h"
#include "nvim/change.h"
+#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
@@ -26,6 +26,7 @@
#include "nvim/func_attr.h"
#include "nvim/getchar.h"
#include "nvim/indent.h"
+#include "nvim/indent_c.h"
#include "nvim/main.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
@@ -175,7 +176,7 @@ int search_regcomp(char_u *pat, int pat_save, int pat_use, int options, regmmatc
* Save the currently used pattern in the appropriate place,
* unless the pattern should not be remembered.
*/
- if (!(options & SEARCH_KEEP) && !cmdmod.keeppatterns) {
+ if (!(options & SEARCH_KEEP) && (cmdmod.cmod_flags & CMOD_KEEPPATTERNS) == 0) {
// search or global command
if (pat_save == RE_SEARCH || pat_save == RE_BOTH) {
save_re_pat(RE_SEARCH, pat, magic);
@@ -188,7 +189,7 @@ int search_regcomp(char_u *pat, int pat_save, int pat_use, int options, regmmatc
regmatch->rmm_ic = ignorecase(pat);
regmatch->rmm_maxcol = 0;
- regmatch->regprog = vim_regcomp(pat, magic ? RE_MAGIC : 0);
+ regmatch->regprog = vim_regcomp((char *)pat, magic ? RE_MAGIC : 0);
if (regmatch->regprog == NULL) {
return FAIL;
}
@@ -203,31 +204,6 @@ char_u *get_search_pat(void)
return mr_pattern;
}
-/*
- * Reverse text into allocated memory.
- * Returns the allocated string.
- *
- * TODO(philix): move reverse_text() to strings.c
- */
-char_u *reverse_text(char_u *s) FUNC_ATTR_NONNULL_RET
-{
- /*
- * Reverse the pattern.
- */
- size_t len = STRLEN(s);
- char_u *rev = xmalloc(len + 1);
- size_t rev_i = len;
- for (size_t s_i = 0; s_i < len; s_i++) {
- const int mb_len = utfc_ptr2len(s + s_i);
- rev_i -= mb_len;
- memmove(rev + rev_i, s + s_i, mb_len);
- s_i += mb_len - 1;
- }
- rev[len] = NUL;
-
- return rev;
-}
-
void save_re_pat(int idx, char_u *pat, int magic)
{
if (spats[idx].pat != pat) {
@@ -310,6 +286,8 @@ static struct spat saved_last_search_spat;
static int did_save_last_search_spat = 0;
static int saved_last_idx = 0;
static bool saved_no_hlsearch = false;
+static colnr_T saved_search_match_endcol;
+static linenr_T saved_search_match_lines;
/// Save and restore the search pattern for incremental highlight search
/// feature.
@@ -352,6 +330,21 @@ void restore_last_search_pattern(void)
set_no_hlsearch(saved_no_hlsearch);
}
+/// Save and restore the incsearch highlighting variables.
+/// This is required so that calling searchcount() at does not invalidate the
+/// incsearch highlighting.
+static void save_incsearch_state(void)
+{
+ saved_search_match_endcol = search_match_endcol;
+ saved_search_match_lines = search_match_lines;
+}
+
+static void restore_incsearch_state(void)
+{
+ search_match_endcol = saved_search_match_endcol;
+ search_match_lines = saved_search_match_lines;
+}
+
char_u *last_search_pattern(void)
{
return spats[RE_SEARCH].pat;
@@ -387,10 +380,10 @@ bool pat_has_uppercase(char_u *pat)
char_u *p = pat;
while (*p != NUL) {
- const int l = utfc_ptr2len(p);
+ const int l = utfc_ptr2len((char *)p);
if (l > 1) {
- if (mb_isupper(utf_ptr2char(p))) {
+ if (mb_isupper(utf_ptr2char((char *)p))) {
return true;
}
p += l;
@@ -521,7 +514,7 @@ void last_pat_prog(regmmatch_T *regmatch)
--emsg_off;
}
-/// lowest level search function.
+/// Lowest level search function.
/// Search for 'count'th occurrence of pattern "pat" in direction "dir".
/// Start at position "pos" and return the found position in "pos".
///
@@ -563,7 +556,7 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,
long nmatched;
int submatch = 0;
bool first_match = true;
- int save_called_emsg = called_emsg;
+ const int called_emsg_before = called_emsg;
bool break_loop = false;
linenr_T stop_lnum = 0; // stop after this line number when != 0
proftime_T *tm = NULL; // timeout limit or NULL
@@ -586,7 +579,6 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,
/*
* find the string
*/
- called_emsg = FALSE;
do { // loop for count
// When not accepting a match at the start position set "extra_col" to a
// non-zero value. Don't do that when starting at MAXCOL, since MAXCOL + 1
@@ -601,7 +593,7 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,
if ((int)STRLEN(ptr) <= pos->col) {
start_char_len = 1;
} else {
- start_char_len = utfc_ptr2len(ptr + pos->col);
+ start_char_len = utfc_ptr2len((char *)ptr + pos->col);
}
} else {
start_char_len = 1;
@@ -658,7 +650,7 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,
break;
}
// Abort searching on an error (e.g., out of stack).
- if (called_emsg || (timed_out != NULL && *timed_out)) {
+ if (called_emsg > called_emsg_before || (timed_out != NULL && *timed_out)) {
break;
}
if (nmatched > 0) {
@@ -713,7 +705,7 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,
}
if (matchcol == matchpos.col && ptr[matchcol] != NUL) {
- matchcol += utfc_ptr2len(ptr + matchcol);
+ matchcol += utfc_ptr2len((char *)ptr + matchcol);
}
if (matchcol == 0 && (options & SEARCH_START)) {
@@ -798,7 +790,7 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,
// for empty match: advance one char
if (matchcol == matchpos.col
&& ptr[matchcol] != NUL) {
- matchcol += utfc_ptr2len(ptr + matchcol);
+ matchcol += utfc_ptr2len((char *)ptr + matchcol);
}
} else {
// Stop when the match is in a next line.
@@ -807,7 +799,7 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,
}
matchcol = matchpos.col;
if (ptr[matchcol] != NUL) {
- matchcol += utfc_ptr2len(ptr + matchcol);
+ matchcol += utfc_ptr2len((char *)ptr + matchcol);
}
}
if (ptr[matchcol] == NUL
@@ -915,7 +907,8 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,
// Stop the search if wrapscan isn't set, "stop_lnum" is
// specified, after an interrupt, after a match and after looping
// twice.
- if (!p_ws || stop_lnum != 0 || got_int || called_emsg
+ if (!p_ws || stop_lnum != 0 || got_int
+ || called_emsg > called_emsg_before
|| (timed_out != NULL && *timed_out)
|| break_loop
|| found || loop) {
@@ -934,14 +927,13 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,
lnum = 1;
}
if (!shortmess(SHM_SEARCH) && (options & SEARCH_MSG)) {
- give_warning((char_u *)_(dir == BACKWARD
- ? top_bot_msg : bot_top_msg), true);
+ give_warning(_(dir == BACKWARD ? top_bot_msg : bot_top_msg), true);
}
if (extra_arg != NULL) {
extra_arg->sa_wrapped = true;
}
}
- if (got_int || called_emsg
+ if (got_int || called_emsg > called_emsg_before
|| (timed_out != NULL && *timed_out)
|| break_loop) {
break;
@@ -950,8 +942,6 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,
vim_regfree(regmatch.regprog);
- called_emsg |= save_called_emsg;
-
if (!found) { // did not find it
if (got_int) {
emsg(_(e_interr));
@@ -1054,7 +1044,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count,
* A line offset is not remembered, this is vi compatible.
*/
if (spats[0].off.line && vim_strchr(p_cpo, CPO_LINEOFF) != NULL) {
- spats[0].off.line = FALSE;
+ spats[0].off.line = false;
spats[0].off.off = 0;
}
@@ -1070,7 +1060,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count,
* Find out the direction of the search.
*/
if (dirc == 0) {
- dirc = spats[0].off.dir;
+ dirc = (char_u)spats[0].off.dir;
} else {
spats[0].off.dir = dirc;
set_vv_searchforward();
@@ -1247,7 +1237,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count,
// empty for the search_stat feature.
if (!cmd_silent) {
msgbuf[0] = dirc;
- if (utf_iscomposing(utf_ptr2char(p))) {
+ if (utf_iscomposing(utf_ptr2char((char *)p))) {
// Use a space to draw the composing char on.
msgbuf[1] = ' ';
memmove(msgbuf + 2, p, STRLEN(p));
@@ -1285,7 +1275,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count,
memset(msgbuf + pat_len, ' ', r - msgbuf);
}
}
- msg_outtrans(msgbuf);
+ msg_outtrans((char *)msgbuf);
msg_clr_eos();
msg_check();
@@ -1444,7 +1434,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count,
curwin->w_set_curswant = TRUE;
end_do_search:
- if ((options & SEARCH_KEEP) || cmdmod.keeppatterns) {
+ if ((options & SEARCH_KEEP) || (cmdmod.cmod_flags & CMOD_KEEPPATTERNS)) {
spats[0].off = old_off;
}
xfree(msgbuf);
@@ -1476,7 +1466,7 @@ int search_for_exact_line(buf_T *buf, pos_T *pos, Direction dir, char_u *pat)
if (p_ws) {
pos->lnum = buf->b_ml.ml_line_count;
if (!shortmess(SHM_SEARCH)) {
- give_warning((char_u *)_(top_bot_msg), true);
+ give_warning(_(top_bot_msg), true);
}
} else {
pos->lnum = 1;
@@ -1486,7 +1476,7 @@ int search_for_exact_line(buf_T *buf, pos_T *pos, Direction dir, char_u *pat)
if (p_ws) {
pos->lnum = 1;
if (!shortmess(SHM_SEARCH)) {
- give_warning((char_u *)_(bot_top_msg), true);
+ give_warning(_(bot_top_msg), true);
}
} else {
pos->lnum = 1;
@@ -1500,7 +1490,7 @@ int search_for_exact_line(buf_T *buf, pos_T *pos, Direction dir, char_u *pat)
start = pos->lnum;
}
ptr = ml_get_buf(buf, pos->lnum, false);
- p = skipwhite(ptr);
+ p = (char_u *)skipwhite((char *)ptr);
pos->col = (colnr_T)(p - ptr);
// when adding lines the matching line may be empty but it is not
@@ -1548,13 +1538,13 @@ int searchc(cmdarg_T *cap, int t_cmd)
*lastc = c;
set_csearch_direction(dir);
set_csearch_until(t_cmd);
- lastc_bytelen = utf_char2bytes(c, lastc_bytes);
+ lastc_bytelen = utf_char2bytes(c, (char *)lastc_bytes);
if (cap->ncharC1 != 0) {
lastc_bytelen += utf_char2bytes(cap->ncharC1,
- lastc_bytes + lastc_bytelen);
+ (char *)lastc_bytes + lastc_bytelen);
if (cap->ncharC2 != 0) {
lastc_bytelen += utf_char2bytes(cap->ncharC2,
- lastc_bytes + lastc_bytelen);
+ (char *)lastc_bytes + lastc_bytelen);
}
}
}
@@ -1592,7 +1582,7 @@ int searchc(cmdarg_T *cap, int t_cmd)
while (count--) {
for (;;) {
if (dir > 0) {
- col += utfc_ptr2len(p + col);
+ col += utfc_ptr2len((char *)p + col);
if (col >= len) {
return FAIL;
}
@@ -1708,31 +1698,31 @@ static void find_mps_values(int *initc, int *findc, bool *backwards, bool switch
char_u *ptr = curbuf->b_p_mps;
while (*ptr != NUL) {
- if (utf_ptr2char(ptr) == *initc) {
+ if (utf_ptr2char((char *)ptr) == *initc) {
if (switchit) {
*findc = *initc;
- *initc = utf_ptr2char(ptr + utfc_ptr2len(ptr) + 1);
+ *initc = utf_ptr2char((char *)ptr + utfc_ptr2len((char *)ptr) + 1);
*backwards = true;
} else {
- *findc = utf_ptr2char(ptr + utfc_ptr2len(ptr) + 1);
+ *findc = utf_ptr2char((char *)ptr + utfc_ptr2len((char *)ptr) + 1);
*backwards = false;
}
return;
}
char_u *prev = ptr;
- ptr += utfc_ptr2len(ptr) + 1;
- if (utf_ptr2char(ptr) == *initc) {
+ ptr += utfc_ptr2len((char *)ptr) + 1;
+ if (utf_ptr2char((char *)ptr) == *initc) {
if (switchit) {
*findc = *initc;
- *initc = utf_ptr2char(prev);
+ *initc = utf_ptr2char((char *)prev);
*backwards = false;
} else {
- *findc = utf_ptr2char(prev);
+ *findc = utf_ptr2char((char *)prev);
*backwards = true;
}
return;
}
- ptr += utfc_ptr2len(ptr);
+ ptr += utfc_ptr2len((char *)ptr);
if (*ptr == ',') {
ptr++;
}
@@ -1834,9 +1824,9 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
*/
if (!cpo_match) {
// Are we before or at #if, #else etc.?
- ptr = skipwhite(linep);
+ ptr = (char_u *)skipwhite((char *)linep);
if (*ptr == '#' && pos.col <= (colnr_T)(ptr - linep)) {
- ptr = skipwhite(ptr + 1);
+ ptr = (char_u *)skipwhite((char *)ptr + 1);
if (STRNCMP(ptr, "if", 2) == 0
|| STRNCMP(ptr, "endif", 5) == 0
|| STRNCMP(ptr, "el", 2) == 0) {
@@ -1879,7 +1869,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
--pos.col;
}
for (;;) {
- initc = utf_ptr2char(linep + pos.col);
+ initc = utf_ptr2char((char *)linep + pos.col);
if (initc == NUL) {
break;
}
@@ -1888,11 +1878,11 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
if (findc) {
break;
}
- pos.col += utfc_ptr2len(linep + pos.col);
+ pos.col += utfc_ptr2len((char *)linep + pos.col);
}
if (!findc) {
// no brace in the line, maybe use " #if" then
- if (!cpo_match && *skipwhite(linep) == '#') {
+ if (!cpo_match && *skipwhite((char *)linep) == '#') {
hash_dir = 1;
} else {
return NULL;
@@ -1917,7 +1907,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
oap->motion_type = kMTLineWise; // Linewise for this case only
}
if (initc != '#') {
- ptr = skipwhite(skipwhite(linep) + 1);
+ ptr = (char_u *)skipwhite(skipwhite((char *)linep) + 1);
if (STRNCMP(ptr, "if", 2) == 0 || STRNCMP(ptr, "el", 2) == 0) {
hash_dir = 1;
} else if (STRNCMP(ptr, "endif", 5) == 0) {
@@ -1938,12 +1928,12 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
pos.lnum += hash_dir;
linep = ml_get(pos.lnum);
line_breakcheck(); // check for CTRL-C typed
- ptr = skipwhite(linep);
+ ptr = (char_u *)skipwhite((char *)linep);
if (*ptr != '#') {
continue;
}
pos.col = (colnr_T)(ptr - linep);
- ptr = skipwhite(ptr + 1);
+ ptr = (char_u *)skipwhite((char *)ptr + 1);
if (hash_dir > 0) {
if (STRNCMP(ptr, "if", 2) == 0) {
count++;
@@ -1978,7 +1968,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
// This is just guessing: when 'rightleft' is set, search for a matching
// paren/brace in the other direction.
- if (curwin->w_p_rl && vim_strchr((char_u *)"()[]{}<>", initc) != NULL) {
+ if (curwin->w_p_rl && vim_strchr("()[]{}<>", initc) != NULL) {
backwards = !backwards;
}
@@ -1989,13 +1979,13 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
clearpos(&match_pos);
// backward search: Check if this line contains a single-line comment
- if ((backwards && comment_dir)
- || lisp) {
+ if ((backwards && comment_dir) || lisp) {
comment_col = check_linecomment(linep);
}
if (lisp && comment_col != MAXCOL && pos.col > (colnr_T)comment_col) {
lispcomm = true; // find match inside this comment
}
+
while (!got_int) {
/*
* Go to the next position, forward or backward. We could use
@@ -2022,8 +2012,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
line_breakcheck();
// Check if this line contains a single-line comment
- if (comment_dir
- || lisp) {
+ if (comment_dir || lisp) {
comment_col = check_linecomment(linep);
}
// skip comment
@@ -2037,7 +2026,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
} else { // forward search
if (linep[pos.col] == NUL
// at end of line, go to next one
- // don't search for match in comment
+ // For lisp don't search for match in comment
|| (lisp && comment_col != MAXCOL
&& pos.col == (colnr_T)comment_col)) {
if (pos.lnum == curbuf->b_ml.ml_line_count // end of file
@@ -2060,7 +2049,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
comment_col = check_linecomment(linep);
}
} else {
- pos.col += utfc_ptr2len(linep + pos.col);
+ pos.col += utfc_ptr2len((char *)linep + pos.col);
}
}
@@ -2091,7 +2080,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
} else if (raw_string) {
if (linep[pos.col - 1] == 'R'
&& linep[pos.col] == '"'
- && vim_strchr(linep + pos.col + 1, '(') != NULL) {
+ && vim_strchr((char *)linep + pos.col + 1, '(') != NULL) {
// Possible start of raw string. Now that we have the
// delimiter we can check if it ends before where we
// started searching, or before the previously found
@@ -2205,7 +2194,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
* inquote if the number of quotes in a line is even, unless this
* line or the previous one ends in a '\'. Complicated, isn't it?
*/
- const int c = utf_ptr2char(linep + pos.col);
+ const int c = utf_ptr2char((char *)linep + pos.col);
switch (c) {
case NUL:
// at end of line without trailing backslash, reset inquote
@@ -2272,7 +2261,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
* (actually, we skip #\( et al)
*/
if (curbuf->b_p_lisp
- && vim_strchr((char_u *)"(){}[]", c) != NULL
+ && vim_strchr("(){}[]", c) != NULL
&& pos.col > 1
&& check_prevcol(linep, pos.col, '\\', NULL)
&& check_prevcol(linep, pos.col - 1, '#', NULL)) {
@@ -2313,20 +2302,17 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
return (pos_T *)NULL; // never found it
}
-/*
- * Check if line[] contains a / / comment.
- * Return MAXCOL if not, otherwise return the column.
- * TODO: skip strings.
- */
-static int check_linecomment(const char_u *line)
+/// Check if line[] contains a / / comment.
+/// @returns MAXCOL if not, otherwise return the column.
+int check_linecomment(const char_u *line)
{
const char_u *p = line; // scan from start
// skip Lispish one-line comments
if (curbuf->b_p_lisp) {
- if (vim_strchr(p, ';') != NULL) { // there may be comments
+ if (vim_strchr((char *)p, ';') != NULL) { // there may be comments
bool in_str = false; // inside of string
- while ((p = vim_strpbrk(p, (char_u *)"\";")) != NULL) {
+ while ((p = (char_u *)strpbrk((char *)p, "\";")) != NULL) {
if (*p == '"') {
if (in_str) {
if (*(p - 1) != '\\') { // skip escaped quote
@@ -2338,7 +2324,8 @@ static int check_linecomment(const char_u *line)
in_str = true;
}
} else if (!in_str && ((p - line) < 2
- || (*(p - 1) != '\\' && *(p - 2) != '#'))) {
+ || (*(p - 1) != '\\' && *(p - 2) != '#'))
+ && !is_pos_in_string(line, (colnr_T)(p - line))) {
break; // found!
}
p++;
@@ -2347,10 +2334,12 @@ static int check_linecomment(const char_u *line)
p = NULL;
}
} else {
- while ((p = vim_strchr(p, '/')) != NULL) {
- // accept a double /, unless it's preceded with * and followed by *,
- // because * / / * is an end and start of a C comment
- if (p[1] == '/' && (p == line || p[-1] != '*' || p[2] != '*')) {
+ while ((p = (char_u *)vim_strchr((char *)p, '/')) != NULL) {
+ // Accept a double /, unless it's preceded with * and followed by *,
+ // because * / / * is an end and start of a C comment. Only
+ // accept the position if it is not inside a string.
+ if (p[1] == '/' && (p == line || p[-1] != '*' || p[2] != '*')
+ && !is_pos_in_string(line, (colnr_T)(p - line))) {
break;
}
++p;
@@ -2387,14 +2376,14 @@ void showmatch(int c)
*/
// 'matchpairs' is "x:y,x:y"
for (p = curbuf->b_p_mps; *p != NUL; p++) {
- if (utf_ptr2char(p) == c && (curwin->w_p_rl ^ p_ri)) {
+ if (utf_ptr2char((char *)p) == c && (curwin->w_p_rl ^ p_ri)) {
break;
}
- p += utfc_ptr2len(p) + 1;
- if (utf_ptr2char(p) == c && !(curwin->w_p_rl ^ p_ri)) {
+ p += utfc_ptr2len((char *)p) + 1;
+ if (utf_ptr2char((char *)p) == c && !(curwin->w_p_rl ^ p_ri)) {
break;
}
- p += utfc_ptr2len(p);
+ p += utfc_ptr2len((char *)p);
if (*p == NUL) {
return;
}
@@ -2427,7 +2416,7 @@ void showmatch(int c)
save_dollar_vcol = dollar_vcol;
save_state = State;
- State = SHOWMATCH;
+ State = MODE_SHOWMATCH;
ui_cursor_shape(); // may show different cursor shape
curwin->w_cursor = mpos; // move to matching char
*so = 0; // don't use 'scrolloff' here
@@ -2506,7 +2495,7 @@ int findsent(Direction dir, long count)
// go back to the previous non-white non-punctuation character
bool found_dot = false;
while (c = gchar_pos(&pos), ascii_iswhite(c)
- || vim_strchr((char_u *)".!?)]\"'", c) != NULL) {
+ || vim_strchr(".!?)]\"'", c) != NULL) {
tpos = pos;
if (decl(&tpos) == -1 || (LINEEMPTY(tpos.lnum) && dir == FORWARD)) {
break;
@@ -2514,11 +2503,11 @@ int findsent(Direction dir, long count)
if (found_dot) {
break;
}
- if (vim_strchr((char_u *)".!?", c) != NULL) {
+ if (vim_strchr(".!?", c) != NULL) {
found_dot = true;
}
- if (vim_strchr((char_u *)")]\"'", c) != NULL
- && vim_strchr((char_u *)".!?)]\"'", gchar_pos(&tpos)) == NULL) {
+ if (vim_strchr(")]\"'", c) != NULL
+ && vim_strchr(".!?)]\"'", gchar_pos(&tpos)) == NULL) {
break;
}
decl(&pos);
@@ -2542,9 +2531,8 @@ int findsent(Direction dir, long count)
if ((c = inc(&tpos)) == -1) {
break;
}
- }
- while (vim_strchr((char_u *)")]\"'", c = gchar_pos(&tpos))
- != NULL);
+ } while (vim_strchr(")]\"'", c = gchar_pos(&tpos))
+ != NULL);
if (c == -1 || (!cpo_J && (c == ' ' || c == '\t')) || c == NUL
|| (cpo_J && (c == ' ' && inc(&tpos) >= 0
&& gchar_pos(&tpos) == ' '))) {
@@ -3399,7 +3387,7 @@ int current_block(oparg_T *oap, long count, int include, int what, int other)
pos_T start_pos;
pos_T *end_pos;
pos_T old_start, old_end;
- char_u *save_cpo;
+ char *save_cpo;
bool sol = false; // '{' at start of line
old_pos = curwin->w_cursor;
@@ -3434,7 +3422,7 @@ int current_block(oparg_T *oap, long count, int include, int what, int other)
// Ignore quotes here. Keep the "M" flag in 'cpo', as that is what the
// user wants.
save_cpo = p_cpo;
- p_cpo = (char_u *)(vim_strchr(p_cpo, CPO_MATCHBSL) != NULL ? "%M" : "%");
+ p_cpo = vim_strchr(p_cpo, CPO_MATCHBSL) != NULL ? "%M" : "%";
if ((pos = findmatch(NULL, what)) != NULL) {
while (count-- > 0) {
if ((pos = findmatch(NULL, what)) == NULL) {
@@ -3479,11 +3467,11 @@ int current_block(oparg_T *oap, long count, int include, int what, int other)
}
}
- /*
- * In Visual mode, when the resulting area is not bigger than what we
- * started with, extend it to the next block, and then exclude again.
- */
+ // In Visual mode, when the resulting area is not bigger than what we
+ // started with, extend it to the next block, and then exclude again.
+ // Don't try to expand the area if the area is empty.
if (!lt(start_pos, old_start) && !lt(old_end, curwin->w_cursor)
+ && !equalpos(start_pos, curwin->w_cursor)
&& VIsual_active) {
curwin->w_cursor = old_start;
decl(&curwin->w_cursor);
@@ -3533,7 +3521,6 @@ int current_block(oparg_T *oap, long count, int include, int what, int other)
return OK;
}
-
/// @param end_tag when true, return true if the cursor is on "</aaa>".
///
/// @return true if the cursor is on a "<aaa>" tag. Ignore "<aaa/>".
@@ -3672,8 +3659,7 @@ again:
p = get_cursor_pos_ptr();
for (cp = p;
*cp != NUL && *cp != '>' && !ascii_iswhite(*cp);
- MB_PTR_ADV(cp)) {
- }
+ MB_PTR_ADV(cp)) {}
len = (int)(cp - p);
if (len == 0) {
curwin->w_cursor = old_pos;
@@ -3952,7 +3938,6 @@ extend:
return OK;
}
-
/// Search quote char from string line[col].
/// Quote character escaped by one of the characters in "escape" is not counted
/// as a quote.
@@ -3968,12 +3953,15 @@ static int find_next_quote(char_u *line, int col, int quotechar, char_u *escape)
c = line[col];
if (c == NUL) {
return -1;
- } else if (escape != NULL && vim_strchr(escape, c)) {
+ } else if (escape != NULL && vim_strchr((char *)escape, c)) {
col++;
+ if (line[col] == NUL) {
+ return -1;
+ }
} else if (c == quotechar) {
break;
}
- col += utfc_ptr2len(line + col);
+ col += utfc_ptr2len((char *)line + col);
}
return col;
}
@@ -3994,15 +3982,14 @@ static int find_prev_quote(char_u *line, int col_start, int quotechar, char_u *e
col_start -= utf_head_off(line, line + col_start);
n = 0;
if (escape != NULL) {
- while (col_start - n > 0 && vim_strchr(escape,
+ while (col_start - n > 0 && vim_strchr((char *)escape,
line[col_start - n - 1]) != NULL) {
++n;
}
}
if (n & 1) {
col_start -= n; // uneven number of escape chars, skip it
- } else if (line[col_start] ==
- quotechar) {
+ } else if (line[col_start] == quotechar) {
break;
}
}
@@ -4083,6 +4070,11 @@ bool current_quote(oparg_T *oap, long count, bool include, int quotechar)
// Find out if we have a quote in the selection.
while (i <= col_end) {
+ // check for going over the end of the line, which can happen if
+ // the line was changed after the Visual area was selected.
+ if (line[i] == NUL) {
+ break;
+ }
if (line[i++] == quotechar) {
selected_quote = true;
break;
@@ -4120,8 +4112,7 @@ bool current_quote(oparg_T *oap, long count, bool include, int quotechar)
col_end = curwin->w_cursor.col;
}
}
- } else if (line[col_start] == quotechar
- || !vis_empty) {
+ } else if (line[col_start] == quotechar || !vis_empty) {
int first_col = col_start;
if (!vis_empty) {
@@ -4190,9 +4181,8 @@ bool current_quote(oparg_T *oap, long count, bool include, int quotechar)
// Set start position. After vi" another i" must include the ".
// For v2i" include the quotes.
- if (!include && count < 2
- && (vis_empty || !inside_quotes)) {
- ++col_start;
+ if (!include && count < 2 && (vis_empty || !inside_quotes)) {
+ col_start++;
}
curwin->w_cursor.col = col_start;
if (VIsual_active) {
@@ -4268,7 +4258,6 @@ abort_search:
return false;
}
-
/// Find next search match under cursor, cursor at end.
/// Used while an operator is pending, and in Visual mode.
///
@@ -4418,7 +4407,7 @@ static int is_zero_width(char_u *pattern, int move, pos_T *cur, Direction direct
int nmatched = 0;
int result = -1;
pos_T pos;
- int save_called_emsg = called_emsg;
+ const int called_emsg_before = called_emsg;
int flag = 0;
if (pattern == NULL) {
@@ -4444,7 +4433,6 @@ static int is_zero_width(char_u *pattern, int move, pos_T *cur, Direction direct
SEARCH_KEEP + flag, RE_SEARCH, NULL) != FAIL) {
// Zero-width pattern should match somewhere, then we can check if
// start and end are in the same position.
- called_emsg = false;
do {
regmatch.startpos[0].col++;
nmatched = vim_regexec_multi(&regmatch, curwin, curbuf,
@@ -4458,14 +4446,13 @@ static int is_zero_width(char_u *pattern, int move, pos_T *cur, Direction direct
? regmatch.startpos[0].col < pos.col
: regmatch.startpos[0].col > pos.col);
- if (!called_emsg) {
+ if (called_emsg == called_emsg_before) {
result = (nmatched != 0
&& regmatch.startpos[0].lnum == regmatch.endpos[0].lnum
&& regmatch.startpos[0].col == regmatch.endpos[0].col);
}
}
- called_emsg |= save_called_emsg;
vim_regfree(regmatch.regprog);
return result;
}
@@ -4477,7 +4464,7 @@ int linewhite(linenr_T lnum)
{
char_u *p;
- p = skipwhite(ml_get(lnum));
+ p = (char_u *)skipwhite((char *)ml_get(lnum));
return *p == NUL;
}
@@ -4537,7 +4524,7 @@ static void cmdline_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, bool sh
// keep the message even after redraw, but don't put in history
msg_hist_off = true;
msg_ext_set_kind("search_count");
- give_warning(msgbuf, false);
+ give_warning((char *)msgbuf, false);
msg_hist_off = false;
}
}
@@ -4740,6 +4727,7 @@ void f_searchcount(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
save_last_search_pattern();
+ save_incsearch_state();
if (pattern != NULL) {
if (*pattern == NUL) {
goto the_end;
@@ -4761,6 +4749,564 @@ void f_searchcount(typval_T *argvars, typval_T *rettv, FunPtr fptr)
the_end:
restore_last_search_pattern();
+ restore_incsearch_state();
+}
+
+/// Fuzzy string matching
+///
+/// Ported from the lib_fts library authored by Forrest Smith.
+/// https://github.com/forrestthewoods/lib_fts/tree/master/code
+///
+/// The following blog describes the fuzzy matching algorithm:
+/// https://www.forrestthewoods.com/blog/reverse_engineering_sublime_texts_fuzzy_match/
+///
+/// Each matching string is assigned a score. The following factors are checked:
+/// - Matched letter
+/// - Unmatched letter
+/// - Consecutively matched letters
+/// - Proximity to start
+/// - Letter following a separator (space, underscore)
+/// - Uppercase letter following lowercase (aka CamelCase)
+///
+/// Matched letters are good. Unmatched letters are bad. Matching near the start
+/// is good. Matching the first letter in the middle of a phrase is good.
+/// Matching the uppercase letters in camel case entries is good.
+///
+/// The score assigned for each factor is explained below.
+/// File paths are different from file names. File extensions may be ignorable.
+/// Single words care about consecutive matches but not separators or camel
+/// case.
+/// Score starts at 100
+/// Matched letter: +0 points
+/// Unmatched letter: -1 point
+/// Consecutive match bonus: +15 points
+/// First letter bonus: +15 points
+/// Separator bonus: +30 points
+/// Camel case bonus: +30 points
+/// Unmatched leading letter: -5 points (max: -15)
+///
+/// There is some nuance to this. Scores don’t have an intrinsic meaning. The
+/// score range isn’t 0 to 100. It’s roughly [50, 150]. Longer words have a
+/// lower minimum score due to unmatched letter penalty. Longer search patterns
+/// have a higher maximum score due to match bonuses.
+///
+/// Separator and camel case bonus is worth a LOT. Consecutive matches are worth
+/// quite a bit.
+///
+/// There is a penalty if you DON’T match the first three letters. Which
+/// effectively rewards matching near the start. However there’s no difference
+/// in matching between the middle and end.
+///
+/// There is not an explicit bonus for an exact match. Unmatched letters receive
+/// a penalty. So shorter strings and closer matches are worth more.
+typedef struct {
+ int idx; ///< used for stable sort
+ listitem_T *item;
+ int score;
+ list_T *lmatchpos;
+} fuzzyItem_T;
+
+/// bonus for adjacent matches; this is higher than SEPARATOR_BONUS so that
+/// matching a whole word is preferred.
+#define SEQUENTIAL_BONUS 40
+/// bonus if match occurs after a path separator
+#define PATH_SEPARATOR_BONUS 30
+/// bonus if match occurs after a word separator
+#define WORD_SEPARATOR_BONUS 25
+/// bonus if match is uppercase and prev is lower
+#define CAMEL_BONUS 30
+/// bonus if the first letter is matched
+#define FIRST_LETTER_BONUS 15
+/// penalty applied for every letter in str before the first match
+#define LEADING_LETTER_PENALTY (-5)
+/// maximum penalty for leading letters
+#define MAX_LEADING_LETTER_PENALTY (-15)
+/// penalty for every letter that doesn't match
+#define UNMATCHED_LETTER_PENALTY (-1)
+/// penalty for gap in matching positions (-2 * k)
+#define GAP_PENALTY (-2)
+/// Score for a string that doesn't fuzzy match the pattern
+#define SCORE_NONE (-9999)
+
+#define FUZZY_MATCH_RECURSION_LIMIT 10
+
+/// Compute a score for a fuzzy matched string. The matching character locations
+/// are in 'matches'.
+static int fuzzy_match_compute_score(const char_u *const str, const int strSz,
+ const uint32_t *const matches, const int numMatches)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
+{
+ assert(numMatches > 0); // suppress clang "result of operation is garbage"
+ // Initialize score
+ int score = 100;
+
+ // Apply leading letter penalty
+ int penalty = LEADING_LETTER_PENALTY * matches[0];
+ if (penalty < MAX_LEADING_LETTER_PENALTY) {
+ penalty = MAX_LEADING_LETTER_PENALTY;
+ }
+ score += penalty;
+
+ // Apply unmatched penalty
+ const int unmatched = strSz - numMatches;
+ score += UNMATCHED_LETTER_PENALTY * unmatched;
+
+ // Apply ordering bonuses
+ for (int i = 0; i < numMatches; i++) {
+ const uint32_t currIdx = matches[i];
+
+ if (i > 0) {
+ const uint32_t prevIdx = matches[i - 1];
+
+ // Sequential
+ if (currIdx == prevIdx + 1) {
+ score += SEQUENTIAL_BONUS;
+ } else {
+ score += GAP_PENALTY * (currIdx - prevIdx);
+ }
+ }
+
+ // Check for bonuses based on neighbor character value
+ if (currIdx > 0) {
+ // Camel case
+ const char_u *p = str;
+ int neighbor;
+
+ for (uint32_t sidx = 0; sidx < currIdx; sidx++) {
+ neighbor = utf_ptr2char((char *)p);
+ MB_PTR_ADV(p);
+ }
+ const int curr = utf_ptr2char((char *)p);
+
+ if (mb_islower(neighbor) && mb_isupper(curr)) {
+ score += CAMEL_BONUS;
+ }
+
+ // Bonus if the match follows a separator character
+ if (neighbor == '/' || neighbor == '\\') {
+ score += PATH_SEPARATOR_BONUS;
+ } else if (neighbor == ' ' || neighbor == '_') {
+ score += WORD_SEPARATOR_BONUS;
+ }
+ } else {
+ // First letter
+ score += FIRST_LETTER_BONUS;
+ }
+ }
+ return score;
+}
+
+/// Perform a recursive search for fuzzy matching 'fuzpat' in 'str'.
+/// @return the number of matching characters.
+static int fuzzy_match_recursive(const char_u *fuzpat, const char_u *str, uint32_t strIdx,
+ int *const outScore, const char_u *const strBegin,
+ const int strLen, const uint32_t *const srcMatches,
+ uint32_t *const matches, const int maxMatches, int nextMatch,
+ int *const recursionCount)
+ FUNC_ATTR_NONNULL_ARG(1, 2, 4, 5, 8, 11) FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ // Recursion params
+ bool recursiveMatch = false;
+ uint32_t bestRecursiveMatches[MAX_FUZZY_MATCHES];
+ int bestRecursiveScore = 0;
+
+ // Count recursions
+ (*recursionCount)++;
+ if (*recursionCount >= FUZZY_MATCH_RECURSION_LIMIT) {
+ return 0;
+ }
+
+ // Detect end of strings
+ if (*fuzpat == NUL || *str == NUL) {
+ return 0;
+ }
+
+ // Loop through fuzpat and str looking for a match
+ bool first_match = true;
+ while (*fuzpat != NUL && *str != NUL) {
+ const int c1 = utf_ptr2char((char *)fuzpat);
+ const int c2 = utf_ptr2char((char *)str);
+
+ // Found match
+ if (mb_tolower(c1) == mb_tolower(c2)) {
+ // Supplied matches buffer was too short
+ if (nextMatch >= maxMatches) {
+ return 0;
+ }
+
+ // "Copy-on-Write" srcMatches into matches
+ if (first_match && srcMatches != NULL) {
+ memcpy(matches, srcMatches, nextMatch * sizeof(srcMatches[0]));
+ first_match = false;
+ }
+
+ // Recursive call that "skips" this match
+ uint32_t recursiveMatches[MAX_FUZZY_MATCHES];
+ int recursiveScore = 0;
+ const char_u *const next_char = str + utfc_ptr2len((char *)str);
+ if (fuzzy_match_recursive(fuzpat, next_char, strIdx + 1, &recursiveScore, strBegin, strLen,
+ matches, recursiveMatches,
+ sizeof(recursiveMatches) / sizeof(recursiveMatches[0]), nextMatch,
+ recursionCount)) {
+ // Pick best recursive score
+ if (!recursiveMatch || recursiveScore > bestRecursiveScore) {
+ memcpy(bestRecursiveMatches, recursiveMatches,
+ MAX_FUZZY_MATCHES * sizeof(recursiveMatches[0]));
+ bestRecursiveScore = recursiveScore;
+ }
+ recursiveMatch = true;
+ }
+
+ // Advance
+ matches[nextMatch++] = strIdx;
+ MB_PTR_ADV(fuzpat);
+ }
+ MB_PTR_ADV(str);
+ strIdx++;
+ }
+
+ // Determine if full fuzpat was matched
+ const bool matched = *fuzpat == NUL;
+
+ // Calculate score
+ if (matched) {
+ *outScore = fuzzy_match_compute_score(strBegin, strLen, matches, nextMatch);
+ }
+
+ // Return best result
+ if (recursiveMatch && (!matched || bestRecursiveScore > *outScore)) {
+ // Recursive score is better than "this"
+ memcpy(matches, bestRecursiveMatches, maxMatches * sizeof(matches[0]));
+ *outScore = bestRecursiveScore;
+ return nextMatch;
+ } else if (matched) {
+ return nextMatch; // "this" score is better than recursive
+ }
+
+ return 0; // no match
+}
+
+/// fuzzy_match()
+///
+/// Performs exhaustive search via recursion to find all possible matches and
+/// match with highest score.
+/// Scores values have no intrinsic meaning. Possible score range is not
+/// normalized and varies with pattern.
+/// Recursion is limited internally (default=10) to prevent degenerate cases
+/// (pat_arg="aaaaaa" str="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").
+/// Uses char_u for match indices. Therefore patterns are limited to
+/// MAX_FUZZY_MATCHES characters.
+///
+/// @return true if 'pat_arg' matches 'str'. Also returns the match score in
+/// 'outScore' and the matching character positions in 'matches'.
+bool fuzzy_match(char_u *const str, const char_u *const pat_arg, const bool matchseq,
+ int *const outScore, uint32_t *const matches, const int maxMatches)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ const int len = mb_charlen(str);
+ bool complete = false;
+ int numMatches = 0;
+
+ *outScore = 0;
+
+ char_u *const save_pat = vim_strsave(pat_arg);
+ char_u *pat = save_pat;
+ char_u *p = pat;
+
+ // Try matching each word in 'pat_arg' in 'str'
+ while (true) {
+ if (matchseq) {
+ complete = true;
+ } else {
+ // Extract one word from the pattern (separated by space)
+ p = (char_u *)skipwhite((char *)p);
+ if (*p == NUL) {
+ break;
+ }
+ pat = p;
+ while (*p != NUL && !ascii_iswhite(utf_ptr2char((char *)p))) {
+ MB_PTR_ADV(p);
+ }
+ if (*p == NUL) { // processed all the words
+ complete = true;
+ }
+ *p = NUL;
+ }
+
+ int score = 0;
+ int recursionCount = 0;
+ const int matchCount
+ = fuzzy_match_recursive(pat, str, 0, &score, str, len, NULL, matches + numMatches,
+ maxMatches - numMatches, 0, &recursionCount);
+ if (matchCount == 0) {
+ numMatches = 0;
+ break;
+ }
+
+ // Accumulate the match score and the number of matches
+ *outScore += score;
+ numMatches += matchCount;
+
+ if (complete) {
+ break;
+ }
+
+ // try matching the next word
+ p++;
+ }
+
+ xfree(save_pat);
+ return numMatches != 0;
+}
+
+/// Sort the fuzzy matches in the descending order of the match score.
+/// For items with same score, retain the order using the index (stable sort)
+static int fuzzy_match_item_compare(const void *const s1, const void *const s2)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
+{
+ const int v1 = ((const fuzzyItem_T *)s1)->score;
+ const int v2 = ((const fuzzyItem_T *)s2)->score;
+ const int idx1 = ((const fuzzyItem_T *)s1)->idx;
+ const int idx2 = ((const fuzzyItem_T *)s2)->idx;
+
+ return v1 == v2 ? (idx1 - idx2) : v1 > v2 ? -1 : 1;
+}
+
+/// Fuzzy search the string 'str' in a list of 'items' and return the matching
+/// strings in 'fmatchlist'.
+/// If 'matchseq' is true, then for multi-word search strings, match all the
+/// words in sequence.
+/// If 'items' is a list of strings, then search for 'str' in the list.
+/// If 'items' is a list of dicts, then either use 'key' to lookup the string
+/// for each item or use 'item_cb' Funcref function to get the string.
+/// If 'retmatchpos' is true, then return a list of positions where 'str'
+/// matches for each item.
+static void fuzzy_match_in_list(list_T *const l, char_u *const str, const bool matchseq,
+ const char_u *const key, Callback *const item_cb,
+ const bool retmatchpos, list_T *const fmatchlist,
+ const long max_matches)
+ FUNC_ATTR_NONNULL_ARG(2, 5, 7)
+{
+ long len = tv_list_len(l);
+ if (len == 0) {
+ return;
+ }
+ if (max_matches > 0 && len > max_matches) {
+ len = max_matches;
+ }
+
+ fuzzyItem_T *const items = xcalloc(len, sizeof(fuzzyItem_T));
+ long match_count = 0;
+ uint32_t matches[MAX_FUZZY_MATCHES];
+
+ // For all the string items in items, get the fuzzy matching score
+ TV_LIST_ITER(l, li, {
+ if (max_matches > 0 && match_count >= max_matches) {
+ break;
+ }
+
+ char_u *itemstr = NULL;
+ typval_T rettv;
+ rettv.v_type = VAR_UNKNOWN;
+ const typval_T *const tv = TV_LIST_ITEM_TV(li);
+ if (tv->v_type == VAR_STRING) { // list of strings
+ itemstr = (char_u *)tv->vval.v_string;
+ } else if (tv->v_type == VAR_DICT && (key != NULL || item_cb->type != kCallbackNone)) {
+ // For a dict, either use the specified key to lookup the string or
+ // use the specified callback function to get the string.
+ if (key != NULL) {
+ itemstr = (char_u *)tv_dict_get_string(tv->vval.v_dict, (const char *)key, false);
+ } else {
+ typval_T argv[2];
+
+ // Invoke the supplied callback (if any) to get the dict item
+ tv->vval.v_dict->dv_refcount++;
+ argv[0].v_type = VAR_DICT;
+ argv[0].vval.v_dict = tv->vval.v_dict;
+ argv[1].v_type = VAR_UNKNOWN;
+ if (callback_call(item_cb, 1, argv, &rettv)) {
+ if (rettv.v_type == VAR_STRING) {
+ itemstr = (char_u *)rettv.vval.v_string;
+ }
+ }
+ tv_dict_unref(tv->vval.v_dict);
+ }
+ }
+
+ int score;
+ if (itemstr != NULL && fuzzy_match(itemstr, str, matchseq, &score, matches,
+ MAX_FUZZY_MATCHES)) {
+ items[match_count].idx = match_count;
+ items[match_count].item = li;
+ items[match_count].score = score;
+
+ // Copy the list of matching positions in itemstr to a list, if
+ // 'retmatchpos' is set.
+ if (retmatchpos) {
+ items[match_count].lmatchpos = tv_list_alloc(kListLenMayKnow);
+ int j = 0;
+ const char_u *p = str;
+ while (*p != NUL) {
+ if (!ascii_iswhite(utf_ptr2char((char *)p)) || matchseq) {
+ tv_list_append_number(items[match_count].lmatchpos, matches[j]);
+ j++;
+ }
+ MB_PTR_ADV(p);
+ }
+ }
+ match_count++;
+ }
+ tv_clear(&rettv);
+ });
+
+ if (match_count > 0) {
+ // Sort the list by the descending order of the match score
+ qsort(items, match_count, sizeof(fuzzyItem_T), fuzzy_match_item_compare);
+
+ // For matchfuzzy(), return a list of matched strings.
+ // ['str1', 'str2', 'str3']
+ // For matchfuzzypos(), return a list with three items.
+ // The first item is a list of matched strings. The second item
+ // is a list of lists where each list item is a list of matched
+ // character positions. The third item is a list of matching scores.
+ // [['str1', 'str2', 'str3'], [[1, 3], [1, 3], [1, 3]]]
+ list_T *retlist;
+ if (retmatchpos) {
+ const listitem_T *const li = tv_list_find(fmatchlist, 0);
+ assert(li != NULL && TV_LIST_ITEM_TV(li)->vval.v_list != NULL);
+ retlist = TV_LIST_ITEM_TV(li)->vval.v_list;
+ } else {
+ retlist = fmatchlist;
+ }
+
+ // Copy the matching strings with a valid score to the return list
+ for (long i = 0; i < match_count; i++) {
+ if (items[i].score == SCORE_NONE) {
+ break;
+ }
+ tv_list_append_tv(retlist, TV_LIST_ITEM_TV(items[i].item));
+ }
+
+ // next copy the list of matching positions
+ if (retmatchpos) {
+ const listitem_T *li = tv_list_find(fmatchlist, -2);
+ assert(li != NULL && TV_LIST_ITEM_TV(li)->vval.v_list != NULL);
+ retlist = TV_LIST_ITEM_TV(li)->vval.v_list;
+
+ for (long i = 0; i < match_count; i++) {
+ if (items[i].score == SCORE_NONE) {
+ break;
+ }
+ tv_list_append_list(retlist, items[i].lmatchpos);
+ }
+
+ // copy the matching scores
+ li = tv_list_find(fmatchlist, -1);
+ assert(li != NULL && TV_LIST_ITEM_TV(li)->vval.v_list != NULL);
+ retlist = TV_LIST_ITEM_TV(li)->vval.v_list;
+ for (long i = 0; i < match_count; i++) {
+ if (items[i].score == SCORE_NONE) {
+ break;
+ }
+ tv_list_append_number(retlist, items[i].score);
+ }
+ }
+ }
+ xfree(items);
+}
+
+/// Do fuzzy matching. Returns the list of matched strings in 'rettv'.
+/// If 'retmatchpos' is true, also returns the matching character positions.
+static void do_fuzzymatch(const typval_T *const argvars, typval_T *const rettv,
+ const bool retmatchpos)
+ FUNC_ATTR_NONNULL_ALL
+{
+ // validate and get the arguments
+ if (argvars[0].v_type != VAR_LIST || argvars[0].vval.v_list == NULL) {
+ semsg(_(e_listarg), retmatchpos ? "matchfuzzypos()" : "matchfuzzy()");
+ return;
+ }
+ if (argvars[1].v_type != VAR_STRING || argvars[1].vval.v_string == NULL) {
+ semsg(_(e_invarg2), tv_get_string(&argvars[1]));
+ return;
+ }
+
+ Callback cb = CALLBACK_NONE;
+ const char_u *key = NULL;
+ bool matchseq = false;
+ long max_matches = 0;
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ if (argvars[2].v_type != VAR_DICT || argvars[2].vval.v_dict == NULL) {
+ emsg(_(e_dictreq));
+ return;
+ }
+
+ // To search a dict, either a callback function or a key can be
+ // specified.
+ dict_T *const d = argvars[2].vval.v_dict;
+ const dictitem_T *di;
+ if ((di = tv_dict_find(d, "key", -1)) != NULL) {
+ if (di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string == NULL
+ || *di->di_tv.vval.v_string == NUL) {
+ semsg(_(e_invarg2), tv_get_string(&di->di_tv));
+ return;
+ }
+ key = (const char_u *)tv_get_string(&di->di_tv);
+ } else if (!tv_dict_get_callback(d, "text_cb", -1, &cb)) {
+ semsg(_(e_invargval), "text_cb");
+ return;
+ }
+
+ if ((di = tv_dict_find(d, "limit", -1)) != NULL) {
+ if (di->di_tv.v_type != VAR_NUMBER) {
+ semsg(_(e_invarg2), tv_get_string(&di->di_tv));
+ return;
+ }
+ max_matches = (long)tv_get_number_chk(&di->di_tv, NULL);
+ }
+
+ if (tv_dict_find(d, "matchseq", -1) != NULL) {
+ matchseq = true;
+ }
+ }
+
+ // get the fuzzy matches
+ tv_list_alloc_ret(rettv, retmatchpos ? 3 : kListLenUnknown);
+ if (retmatchpos) {
+ // For matchfuzzypos(), a list with three items are returned. First
+ // item is a list of matching strings, the second item is a list of
+ // lists with matching positions within each string and the third item
+ // is the list of scores of the matches.
+ tv_list_append_list(rettv->vval.v_list, tv_list_alloc(kListLenUnknown));
+ tv_list_append_list(rettv->vval.v_list, tv_list_alloc(kListLenUnknown));
+ tv_list_append_list(rettv->vval.v_list, tv_list_alloc(kListLenUnknown));
+ }
+
+ fuzzy_match_in_list(argvars[0].vval.v_list, (char_u *)tv_get_string(&argvars[1]), matchseq, key,
+ &cb, retmatchpos, rettv->vval.v_list, max_matches);
+ callback_free(&cb);
+}
+
+/// "matchfuzzy()" function
+void f_matchfuzzy(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ do_fuzzymatch(argvars, rettv, false);
+}
+
+/// "matchfuzzypos()" function
+void f_matchfuzzypos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ do_fuzzymatch(argvars, rettv, true);
+}
+
+/// Get line "lnum" and copy it into "buf[LSIZE]".
+/// The copy is made because the regexp may make the line invalid when using a
+/// mark.
+static char_u *get_line_and_copy(linenr_T lnum, char_u *buf)
+{
+ char_u *line = ml_get(lnum);
+ STRLCPY(buf, line, LSIZE);
+ return buf;
}
/// Find identifiers or defines in included files.
@@ -4785,7 +5331,7 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
char_u *pat;
char_u *new_fname;
- char_u *curr_fname = curbuf->b_fname;
+ char_u *curr_fname = (char_u *)curbuf->b_fname;
char_u *prev_fname = NULL;
linenr_T lnum;
int depth;
@@ -4825,7 +5371,7 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
sprintf((char *)pat, whole ? "\\<%.*s\\>" : "%.*s", (int)len, ptr);
// ignore case according to p_ic, p_scs and pat
regmatch.rm_ic = ignorecase(pat);
- regmatch.regprog = vim_regcomp(pat, p_magic ? RE_MAGIC : 0);
+ regmatch.regprog = vim_regcomp((char *)pat, p_magic ? RE_MAGIC : 0);
xfree(pat);
if (regmatch.regprog == NULL) {
goto fpip_end;
@@ -4833,7 +5379,7 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
}
inc_opt = (*curbuf->b_p_inc == NUL) ? p_inc : curbuf->b_p_inc;
if (*inc_opt != NUL) {
- incl_regmatch.regprog = vim_regcomp(inc_opt, p_magic ? RE_MAGIC : 0);
+ incl_regmatch.regprog = vim_regcomp((char *)inc_opt, p_magic ? RE_MAGIC : 0);
if (incl_regmatch.regprog == NULL) {
goto fpip_end;
}
@@ -4841,7 +5387,8 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
}
if (type == FIND_DEFINE && (*curbuf->b_p_def != NUL || *p_def != NUL)) {
def_regmatch.regprog = vim_regcomp(*curbuf->b_p_def == NUL
- ? p_def : curbuf->b_p_def, p_magic ? RE_MAGIC : 0);
+ ? (char *)p_def : (char *)curbuf->b_p_def,
+ p_magic ? RE_MAGIC : 0);
if (def_regmatch.regprog == NULL) {
goto fpip_end;
}
@@ -4858,13 +5405,13 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
if (lnum > end_lnum) { // do at least one line
lnum = end_lnum;
}
- line = ml_get(lnum);
+ line = get_line_and_copy(lnum, file_line);
for (;;) {
if (incl_regmatch.regprog != NULL
- && vim_regexec(&incl_regmatch, line, (colnr_T)0)) {
- char_u *p_fname = (curr_fname == curbuf->b_fname)
- ? curbuf->b_ffname : curr_fname;
+ && vim_regexec(&incl_regmatch, (char *)line, (colnr_T)0)) {
+ char_u *p_fname = (curr_fname == (char_u *)curbuf->b_fname)
+ ? (char_u *)curbuf->b_ffname : curr_fname;
if (inc_opt != NULL && strstr((char *)inc_opt, "\\zs") != NULL) {
// Use text from '\zs' to '\ze' (or end) of 'include'.
@@ -4888,8 +5435,8 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
if (i == max_path_depth) {
break;
}
- if (path_full_compare(new_fname, files[i].name,
- true, true) & kEqualFiles) {
+ if (path_full_compare((char *)new_fname, (char *)files[i].name, true,
+ true) & kEqualFiles) {
if (type != CHECK_PATH
&& action == ACTION_SHOW_ALL && files[i].matched) {
msg_putchar('\n'); // cursor below last one */
@@ -4951,10 +5498,8 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
} else {
// find the file name after the end of the match
for (p = incl_regmatch.endp[0];
- *p && !vim_isfilec(*p); p++) {
- }
- for (i = 0; vim_isfilec(p[i]); i++) {
- }
+ *p && !vim_isfilec(*p); p++) {}
+ for (i = 0; vim_isfilec(p[i]); i++) {}
}
if (i == 0) {
@@ -5044,12 +5589,10 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
search_line:
define_matched = false;
if (def_regmatch.regprog != NULL
- && vim_regexec(&def_regmatch, line, (colnr_T)0)) {
- /*
- * Pattern must be first identifier after 'define', so skip
- * to that position before checking for match of pattern. Also
- * don't let it match beyond the end of this identifier.
- */
+ && vim_regexec(&def_regmatch, (char *)line, (colnr_T)0)) {
+ // Pattern must be first identifier after 'define', so skip
+ // to that position before checking for match of pattern. Also
+ // don't let it match beyond the end of this identifier.
p = def_regmatch.endp[0];
while (*p && !vim_iswordc(*p)) {
p++;
@@ -5065,7 +5608,7 @@ search_line:
if (define_matched
|| (compl_cont_status & CONT_SOL)) {
// compare the first "len" chars from "ptr"
- startp = skipwhite(p);
+ startp = (char_u *)skipwhite((char *)p);
if (p_ic) {
matched = !mb_strnicmp(startp, ptr, len);
} else {
@@ -5076,7 +5619,7 @@ search_line:
matched = false;
}
} else if (regmatch.regprog != NULL
- && vim_regexec(&regmatch, line, (colnr_T)(p - line))) {
+ && vim_regexec(&regmatch, (char *)line, (colnr_T)(p - line))) {
matched = true;
startp = regmatch.startp[0];
// Check if the line is not a comment line (unless we are
@@ -5084,8 +5627,8 @@ search_line:
// is not considered to be a comment line.
if (skip_comments) {
if ((*line != '#'
- || STRNCMP(skipwhite(line + 1), "define", 6) != 0)
- && get_leader_len(line, NULL, false, true)) {
+ || STRNCMP(skipwhite((char *)line + 1), "define", 6) != 0)
+ && get_leader_len((char *)line, NULL, false, true)) {
matched = false;
}
@@ -5095,7 +5638,7 @@ search_line:
* * /" when looking for "normal".
* Note: Doesn't skip "/ *" in comments.
*/
- p = skipwhite(line);
+ p = (char_u *)skipwhite((char *)line);
if (matched
|| (p[0] == '/' && p[1] == '*') || p[0] == '*') {
for (p = line; *p && p < startp; ++p) {
@@ -5150,7 +5693,7 @@ search_line:
if (lnum >= end_lnum) {
goto exit_matched;
}
- line = ml_get(++lnum);
+ line = get_line_and_copy(++lnum, file_line);
} else if (vim_fgets(line = file_line,
LSIZE, files[depth].fp)) {
goto exit_matched;
@@ -5158,20 +5701,20 @@ search_line:
// we read a line, set "already" to check this "line" later
// if depth >= 0 we'll increase files[depth].lnum far
- // bellow -- Acevedo
- already = aux = p = skipwhite(line);
+ // below -- Acevedo
+ already = aux = p = (char_u *)skipwhite((char *)line);
p = find_word_start(p);
p = find_word_end(p);
if (p > aux) {
- if (*aux != ')' && IObuff[i-1] != TAB) {
- if (IObuff[i-1] != ' ') {
+ if (*aux != ')' && IObuff[i - 1] != TAB) {
+ if (IObuff[i - 1] != ' ') {
IObuff[i++] = ' ';
}
// IObuf =~ "\(\k\|\i\).* ", thus i >= 2
if (p_js
- && (IObuff[i-2] == '.'
- || IObuff[i-2] == '?'
- || IObuff[i-2] == '!')) {
+ && (IObuff[i - 2] == '.'
+ || IObuff[i - 2] == '?'
+ || IObuff[i - 2] == '!')) {
IObuff[i++] = ' ';
}
}
@@ -5192,7 +5735,8 @@ search_line:
}
const int add_r = ins_compl_add_infercase(aux, i, p_ic,
- curr_fname == curbuf->b_fname ? NULL : curr_fname,
+ curr_fname == (char_u *)curbuf->b_fname
+ ? NULL : curr_fname,
dir, cont_s_ipos);
if (add_r == OK) {
// if dir was BACKWARD then honor it just once
@@ -5266,7 +5810,7 @@ search_line:
curwin->w_cursor.lnum = lnum;
check_cursor();
} else {
- if (!GETFILE_SUCCESS(getfile(0, files[depth].name, NULL, true,
+ if (!GETFILE_SUCCESS(getfile(0, (char *)files[depth].name, NULL, true,
files[depth].lnum, false))) {
break; // failed to jump to file
}
@@ -5297,7 +5841,7 @@ exit_matched:
&& action == ACTION_EXPAND
&& !(compl_cont_status & CONT_SOL)
&& *startp != NUL
- && *(p = startp + utfc_ptr2len(startp)) != NUL) {
+ && *(p = startp + utfc_ptr2len((char *)startp)) != NUL) {
goto search_line;
}
}
@@ -5320,8 +5864,8 @@ exit_matched:
--old_files;
files[old_files].name = files[depth].name;
files[old_files].matched = files[depth].matched;
- --depth;
- curr_fname = (depth == -1) ? curbuf->b_fname
+ depth--;
+ curr_fname = (depth == -1) ? (char_u *)curbuf->b_fname
: files[depth].name;
if (depth < depth_displayed) {
depth_displayed = depth;
@@ -5341,7 +5885,7 @@ exit_matched:
if (++lnum > end_lnum) {
break;
}
- line = ml_get(lnum);
+ line = get_line_and_copy(lnum, file_line);
}
already = NULL;
}
@@ -5415,7 +5959,7 @@ static void show_pat_in_path(char_u *line, int type, bool did_show, int action,
if (action == ACTION_SHOW_ALL) {
snprintf((char *)IObuff, IOSIZE, "%3ld: ", count); // Show match nr.
msg_puts((const char *)IObuff);
- snprintf((char *)IObuff, IOSIZE, "%4ld", *lnum); // Show line nr.
+ snprintf((char *)IObuff, IOSIZE, "%4" PRIdLINENR, *lnum); // Show line nr.
// Highlight line numbers.
msg_puts_attr((const char *)IObuff, HL_ATTR(HLF_N));
msg_puts(" ");
@@ -5486,3 +6030,9 @@ bool search_was_last_used(void)
{
return last_idx == 0;
}
+
+/// @return true if 'hlsearch' highlight is currently in use.
+bool using_hlsearch(void)
+{
+ return spats[last_idx].pat != NULL && p_hls && !no_hlsearch;
+}
diff --git a/src/nvim/search.h b/src/nvim/search.h
index 15b8d41f39..53059cc1ea 100644
--- a/src/nvim/search.h
+++ b/src/nvim/search.h
@@ -55,6 +55,9 @@
#define SEARCH_STAT_DEF_MAX_COUNT 99
#define SEARCH_STAT_BUF_LEN 12
+/// Maximum number of characters that can be fuzzy matched
+#define MAX_FUZZY_MATCHES 256
+
/// Structure containing offset definition for the last search pattern
///
/// @note Only offset for the last search pattern is used, not for the last
diff --git a/src/nvim/sha256.c b/src/nvim/sha256.c
index 9d6a2f2c41..53c9cb7c81 100644
--- a/src/nvim/sha256.c
+++ b/src/nvim/sha256.c
@@ -73,8 +73,8 @@ static void sha256_process(context_sha256_T *ctx, const char_u data[SHA256_BUFFE
GET_UINT32(W[14], data, 56);
GET_UINT32(W[15], data, 60);
-#define SHR(x, n) ((x & 0xFFFFFFFF) >> n)
-#define ROTR(x, n) (SHR(x, n) | (x << (32 - n)))
+#define SHR(x, n) (((x) & 0xFFFFFFFF) >> (n))
+#define ROTR(x, n) (SHR(x, n) | ((x) << (32 - (n))))
#define S0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHR(x, 3))
#define S1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHR(x, 10))
@@ -82,17 +82,16 @@ static void sha256_process(context_sha256_T *ctx, const char_u data[SHA256_BUFFE
#define S2(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22))
#define S3(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25))
-#define F0(x, y, z) ((x & y) | (z & (x | y)))
-#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F0(x, y, z) (((x) & (y)) | ((z) & ((x) | (y))))
+#define F1(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
#define R(t) \
- (W[t] = S1(W[t - 2]) + W[t - 7] + \
- S0(W[t - 15]) + W[t - 16])
+ (W[t] = S1(W[(t) - 2]) + W[(t) - 7] + S0(W[(t) - 15]) + W[(t) - 16])
#define P(a, b, c, d, e, f, g, h, x, K) { \
- temp1 = h + S3(e) + F1(e, f, g) + K + x; \
+ temp1 = (h) + S3(e) + F1(e, f, g) + (K) + (x); \
temp2 = S2(a) + F0(a, b, c); \
- d += temp1; h = temp1 + temp2; \
+ (d) += temp1; (h) = temp1 + temp2; \
}
A = ctx->state[0];
@@ -185,7 +184,7 @@ void sha256_update(context_sha256_T *ctx, const char_u *input, size_t length)
return;
}
- uint32_t left = ctx->total[0] & (SHA256_BUFFER_SIZE-1); // left < buf size
+ uint32_t left = ctx->total[0] & (SHA256_BUFFER_SIZE - 1); // left < buf size
ctx->total[0] += (uint32_t)length;
ctx->total[0] &= 0xFFFFFFFF;
@@ -336,7 +335,7 @@ bool sha256_self_test(void)
sha256_finish(&ctx, sha256sum);
for (size_t j = 0; j < SHA256_SUM_SIZE; j++) {
- snprintf(output + j * SHA_STEP, SHA_STEP+1, "%02x", sha256sum[j]);
+ snprintf(output + j * SHA_STEP, SHA_STEP + 1, "%02x", sha256sum[j]);
}
}
diff --git a/src/nvim/shada.c b/src/nvim/shada.c
index d7bda03116..cd3b967a9f 100644
--- a/src/nvim/shada.c
+++ b/src/nvim/shada.c
@@ -151,8 +151,8 @@ typedef enum {
kSDItemBufferList = 9, ///< Buffer list.
kSDItemLocalMark = 10, ///< Buffer-local mark.
kSDItemChange = 11, ///< Item from buffer change list.
-#define SHADA_LAST_ENTRY ((uint64_t)kSDItemChange)
} ShadaEntryType;
+#define SHADA_LAST_ENTRY ((uint64_t)kSDItemChange)
/// Possible results when reading ShaDa file
typedef enum {
@@ -512,8 +512,8 @@ static inline void hmll_init(HMLList *const hmll, const size_t size)
///
/// @return `for` cycle header (use `HMLL_FORALL(hmll, cur_entry) {body}`).
#define HMLL_FORALL(hmll, cur_entry, code) \
- for (HMLListEntry *cur_entry = (hmll)->first; cur_entry != NULL; \
- cur_entry = cur_entry->next) { \
+ for (HMLListEntry *(cur_entry) = (hmll)->first; (cur_entry) != NULL; \
+ (cur_entry) = (cur_entry)->next) { \
code \
} \
@@ -550,7 +550,6 @@ static inline void hmll_remove(HMLList *const hmll, HMLListEntry *const hmll_ent
}
}
-
/// Insert entry to the linked list
///
/// @param[out] hmll List to insert to.
@@ -831,7 +830,7 @@ static int shada_read_file(const char *const file, const int flags)
ShaDaReadDef sd_reader;
const int of_ret = open_shada_file_for_reading(fname, &sd_reader);
- if (p_verbose > 0) {
+ if (p_verbose > 1) {
verbose_enter();
smsg(_("Reading ShaDa file \"%s\"%s%s%s%s"),
fname,
@@ -1053,7 +1052,7 @@ static buf_T *find_buffer(khash_t(fnamebufs) *const fname_bufs, const char *cons
kh_key(fname_bufs, k) = xstrdup(fname);
FOR_ALL_BUFFERS(buf) {
if (buf->b_ffname != NULL) {
- if (fnamecmp(fname, buf->b_ffname) == 0) {
+ if (FNAMECMP(fname, buf->b_ffname) == 0) {
kh_val(fname_bufs, k) = buf;
return buf;
}
@@ -1073,13 +1072,13 @@ static inline bool marks_equal(const pos_T a, const pos_T b)
entry, fname_cond, free_func, fin_func, \
idxadj_func, afterfree_func) \
do { \
- const int jl_len = (int)jumps_size; \
+ const int jl_len = (int)(jumps_size); \
int i; \
for (i = jl_len; i > 0; i--) { \
- const jumps_type jl_entry = jumps[i - 1]; \
- if (jl_entry.timestamp_attr <= entry.timestamp) { \
- if (marks_equal(jl_entry.mark_attr, entry.data.filemark.mark) \
- && fname_cond) { \
+ const jumps_type jl_entry = (jumps)[i - 1]; \
+ if (jl_entry.timestamp_attr <= (entry).timestamp) { \
+ if (marks_equal(jl_entry.mark_attr, (entry).data.filemark.mark) \
+ && (fname_cond)) { \
i = -1; \
} \
break; \
@@ -1087,30 +1086,30 @@ static inline bool marks_equal(const pos_T a, const pos_T b)
} \
if (i > 0) { \
if (jl_len == JUMPLISTSIZE) { \
- free_func(jumps[0]); \
+ free_func((jumps)[0]); \
i--; \
if (i > 0) { \
- memmove(&jumps[0], &jumps[1], sizeof(jumps[1]) * (size_t)i); \
+ memmove(&(jumps)[0], &(jumps)[1], sizeof((jumps)[1]) * (size_t)i); \
} \
} else if (i != jl_len) { \
- memmove(&jumps[i + 1], &jumps[i], \
- sizeof(jumps[0]) * (size_t)(jl_len - i)); \
+ memmove(&(jumps)[i + 1], &(jumps)[i], \
+ sizeof((jumps)[0]) * (size_t)(jl_len - i)); \
} \
} else if (i == 0) { \
if (jl_len == JUMPLISTSIZE) { \
i = -1; \
} else if (jl_len > 0) { \
- memmove(&jumps[1], &jumps[0], sizeof(jumps[0]) * (size_t)jl_len); \
+ memmove(&(jumps)[1], &(jumps)[0], sizeof((jumps)[0]) * (size_t)jl_len); \
} \
} \
if (i != -1) { \
- jumps[i] = fin_func(entry); \
+ (jumps)[i] = fin_func(entry); \
if (jl_len < JUMPLISTSIZE) { \
- jumps_size++; \
+ (jumps_size)++; \
} \
idxadj_func(i); \
} else { \
- shada_free_shada_entry(&entry); \
+ shada_free_shada_entry(&(entry)); \
afterfree_func(entry); \
} \
} while (0)
@@ -1238,7 +1237,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags)
// string is close to useless: you can only use it with :& or :~ and
// that’s all because s//~ is not available until the first call to
// regtilde. Vim was not calling this for some reason.
- (void)(char *)regtilde((char_u *)cur_entry.data.sub_string.sub, p_magic);
+ (void)(char *)regtilde((char_u *)cur_entry.data.sub_string.sub, p_magic, false);
// Do not free shada entry: its allocated memory was saved above.
break;
case kSDItemHistoryEntry:
@@ -1265,7 +1264,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags)
}
}
if (!op_reg_set(cur_entry.data.reg.name, (yankreg_T) {
- .y_array = (char_u **)cur_entry.data.reg.contents,
+ .y_array = cur_entry.data.reg.contents,
.y_size = cur_entry.data.reg.contents_size,
.y_type = cur_entry.data.reg.type,
.y_width = (colnr_T)cur_entry.data.reg.width,
@@ -1289,9 +1288,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags)
XFREE_CLEAR(cur_entry.data.filemark.fname);
}
xfmark_T fm = (xfmark_T) {
- .fname = (char_u *)(buf == NULL
- ? cur_entry.data.filemark.fname
- : NULL),
+ .fname = buf == NULL ? cur_entry.data.filemark.fname : NULL,
.fmark = {
.mark = cur_entry.data.filemark.mark,
.fnum = (buf == NULL ? 0 : buf->b_fnum),
@@ -1307,7 +1304,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags)
} else {
#define SDE_TO_XFMARK(entry) fm
#define ADJUST_IDX(i) \
- if (curwin->w_jumplistidx >= i \
+ if (curwin->w_jumplistidx >= (i) \
&& curwin->w_jumplistidx + 1 <= curwin->w_jumplistlen) { \
curwin->w_jumplistidx++; \
}
@@ -1331,11 +1328,11 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags)
char *const sfname =
(char *)path_try_shorten_fname((char_u *)cur_entry.data.buffer_list.buffers[i].fname);
buf_T *const buf =
- buflist_new((char_u *)cur_entry.data.buffer_list.buffers[i].fname, (char_u *)sfname, 0,
- BLN_LISTED);
+ buflist_new(cur_entry.data.buffer_list.buffers[i].fname, sfname, 0, BLN_LISTED);
if (buf != NULL) {
+ fmarkv_T view = INIT_FMARKV;
RESET_FMARK(&buf->b_last_cursor,
- cur_entry.data.buffer_list.buffers[i].pos, 0);
+ cur_entry.data.buffer_list.buffers[i].pos, 0, view);
buflist_setfpos(buf, curwin, buf->b_last_cursor.mark.lnum,
buf->b_last_cursor.mark.col, false);
buf->additional_data =
@@ -1449,7 +1446,7 @@ static const char *shada_get_default_file(void)
FUNC_ATTR_WARN_UNUSED_RESULT
{
if (default_shada_file == NULL) {
- char *shada_dir = stdpaths_user_data_subpath("shada", 0, false);
+ char *shada_dir = stdpaths_user_state_subpath("shada", 0, false);
default_shada_file = concat_fnames_realloc(shada_dir, "main.shada", true);
}
return default_shada_file;
@@ -1550,7 +1547,7 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, ShadaEntr
} \
} while (0)
#define CHECK_DEFAULT(entry, attr) \
- (sd_default_values[entry.type].data.attr == entry.data.attr)
+ (sd_default_values[(entry).type].data.attr == (entry).data.attr)
#define ONE_IF_NOT_DEFAULT(entry, attr) \
((size_t)(!CHECK_DEFAULT(entry, attr)))
switch (entry.type) {
@@ -1640,7 +1637,7 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, ShadaEntr
do { \
if (!CHECK_DEFAULT(entry, search_pattern.attr)) { \
PACK_STATIC_STR(name); \
- if (sd_default_values[entry.type].data.search_pattern.attr) { \
+ if (sd_default_values[(entry).type].data.search_pattern.attr) { \
msgpack_pack_false(spacker); \
} else { \
msgpack_pack_true(spacker); \
@@ -2224,12 +2221,12 @@ static inline ShaDaWriteResult shada_read_when_writing(ShaDaReadDef *const sd_re
} else {
#define FREE_POSSIBLY_FREED_SHADA_ENTRY(entry) \
do { \
- if (entry.can_free_entry) { \
- shada_free_shada_entry(&entry.data); \
+ if ((entry).can_free_entry) { \
+ shada_free_shada_entry(&(entry).data); \
} \
} while (0)
#define SDE_TO_PFSDE(entry) \
- ((PossiblyFreedShadaEntry) { .can_free_entry = true, .data = entry })
+ ((PossiblyFreedShadaEntry) { .can_free_entry = true, .data = (entry) })
#define AFTERFREE_DUMMY(entry)
#define DUMMY_IDX_ADJ(i)
MERGE_JUMPS(filemarks->changes_size, filemarks->changes,
@@ -2309,7 +2306,7 @@ static inline ShadaEntry shada_get_buflist(khash_t(bufset) *const removable_bufs
}
buflist_entry.data.buffer_list.buffers[i] = (struct buffer_list_buffer) {
.pos = buf->b_last_cursor.mark,
- .fname = (char *)buf->b_ffname,
+ .fname = buf->b_ffname,
.additional_data = buf->additional_data,
};
i++;
@@ -2400,7 +2397,7 @@ static inline void shada_initialize_registers(WriteMergerState *const wms, int m
.timestamp = reg.timestamp,
.data = {
.reg = {
- .contents = (char **)reg.y_array,
+ .contents = reg.y_array,
.contents_size = reg.y_size,
.type = reg.y_type,
.width = (size_t)(reg.y_type == kMTBlockWise ? reg.y_width : 0),
@@ -2447,7 +2444,7 @@ static inline void replace_numbered_mark(WriteMergerState *const wms, const size
static inline void find_removable_bufs(khash_t(bufset) *removable_bufs)
{
FOR_ALL_BUFFERS(buf) {
- if (buf->b_ffname != NULL && shada_removable((char *)buf->b_ffname)) {
+ if (buf->b_ffname != NULL && shada_removable(buf->b_ffname)) {
int kh_ret;
(void)kh_put(bufset, removable_bufs, (uintptr_t)buf, &kh_ret);
}
@@ -2806,7 +2803,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, ShaDaReadDef
.mark = curwin->w_cursor,
.name = '0',
.additional_data = NULL,
- .fname = (char *)curbuf->b_ffname,
+ .fname = curbuf->b_ffname,
}
}
},
@@ -2817,8 +2814,8 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, ShaDaReadDef
#define PACK_WMS_ARRAY(wms_array) \
do { \
for (size_t i_ = 0; i_ < ARRAY_SIZE(wms_array); i_++) { \
- if (wms_array[i_].data.type != kSDItemMissing) { \
- if (shada_pack_pfreed_entry(packer, wms_array[i_], max_kbyte) \
+ if ((wms_array)[i_].data.type != kSDItemMissing) { \
+ if (shada_pack_pfreed_entry(packer, (wms_array)[i_], max_kbyte) \
== kSDWriteFailed) { \
ret = kSDWriteFailed; \
goto shada_write_exit; \
@@ -2838,7 +2835,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, ShaDaReadDef
}
#define PACK_WMS_ENTRY(wms_entry) \
do { \
- if (wms_entry.data.type != kSDItemMissing) { \
+ if ((wms_entry).data.type != kSDItemMissing) { \
if (shada_pack_pfreed_entry(packer, wms_entry, max_kbyte) \
== kSDWriteFailed) { \
ret = kSDWriteFailed; \
@@ -3000,7 +2997,7 @@ shada_write_file_open: {}
}
if (nomerge) {
shada_write_file_nomerge: {}
- char *const tail = (char *)path_tail_with_sep((char_u *)fname);
+ char *const tail = path_tail_with_sep(fname);
if (tail != fname) {
const char tail_save = *tail;
*tail = NUL;
@@ -3036,7 +3033,7 @@ shada_write_file_nomerge: {}
return FAIL;
}
- if (p_verbose > 0) {
+ if (p_verbose > 1) {
verbose_enter();
smsg(_("Writing ShaDa file \"%s\""), fname);
verbose_leave();
@@ -3302,7 +3299,7 @@ static ShaDaReadResult msgpack_read_uint64(ShaDaReadDef *const sd_reader, const
uint64_t buf = 0;
char *buf_u8 = (char *)&buf;
ShaDaReadResult fl_ret;
- if ((fl_ret = fread_len(sd_reader, &(buf_u8[sizeof(buf)-length]), length))
+ if ((fl_ret = fread_len(sd_reader, &(buf_u8[sizeof(buf) - length]), length))
!= kSDReadStatusSuccess) {
return fl_ret;
}
@@ -3315,16 +3312,16 @@ static ShaDaReadResult msgpack_read_uint64(ShaDaReadDef *const sd_reader, const
RERR "Error while reading ShaDa file: " \
entry_name " entry at position %" PRIu64 " " \
error_desc
-#define CHECK_KEY(key, expected) ( \
- key.via.str.size == sizeof(expected) - 1 \
- && STRNCMP(key.via.str.ptr, expected, sizeof(expected) - 1) == 0)
+#define CHECK_KEY(key, \
+ expected) ((key).via.str.size == (sizeof(expected) - 1) \
+ && STRNCMP((key).via.str.ptr, expected, (sizeof(expected) - 1)) == 0)
#define CLEAR_GA_AND_ERROR_OUT(ga) \
do { \
- ga_clear(&ga); \
+ ga_clear(&(ga)); \
goto shada_read_next_item_error; \
} while (0)
#define ID(s) s
-#define BINDUP(b) xmemdupz(b.ptr, b.size)
+#define BINDUP(b) xmemdupz((b).ptr, (b).size)
#define TOINT(s) ((int)(s))
#define TOLONG(s) ((long)(s))
#define TOCHAR(s) ((char)(s))
@@ -3337,28 +3334,31 @@ static ShaDaReadResult msgpack_read_uint64(ShaDaReadDef *const sd_reader, const
semsg(_(READERR(entry_name, error_desc)), initial_fpos); \
CLEAR_GA_AND_ERROR_OUT(ad_ga); \
} \
- tgt = proc(obj.via.attr); \
+ (tgt) = proc((obj).via.attr); \
} while (0)
#define CHECK_KEY_IS_STR(un, entry_name) \
- if (un.data.via.map.ptr[i].key.type != MSGPACK_OBJECT_STR) { \
+ if ((un).data.via.map.ptr[i].key.type != MSGPACK_OBJECT_STR) { \
semsg(_(READERR(entry_name, "has key which is not a string")), \
initial_fpos); \
CLEAR_GA_AND_ERROR_OUT(ad_ga); \
- } else if (un.data.via.map.ptr[i].key.via.str.size == 0) { \
+ } else if ((un).data.via.map.ptr[i].key.via.str.size == 0) { \
semsg(_(READERR(entry_name, "has empty key")), initial_fpos); \
CLEAR_GA_AND_ERROR_OUT(ad_ga); \
}
-#define CHECKED_KEY(un, entry_name, name, error_desc, tgt, condition, attr, \
- proc) \
- else if (CHECK_KEY( /* NOLINT(readability/braces) */ \
- un.data.via.map.ptr[i].key, name)) { \
- CHECKED_ENTRY(condition, "has " name " key value " error_desc, \
- entry_name, un.data.via.map.ptr[i].val, \
- tgt, attr, proc); \
+#define CHECKED_KEY(un, entry_name, name, error_desc, tgt, condition, attr, proc) \
+ else if (CHECK_KEY((un).data.via.map.ptr[i].key, name)) /* NOLINT(readability/braces) */ \
+ { \
+ CHECKED_ENTRY(condition, \
+ "has " name " key value " error_desc, \
+ entry_name, \
+ (un).data.via.map.ptr[i].val, \
+ tgt, \
+ attr, \
+ proc); \
}
#define TYPED_KEY(un, entry_name, name, type_name, tgt, objtype, attr, proc) \
CHECKED_KEY(un, entry_name, name, "which is not " type_name, tgt, \
- un.data.via.map.ptr[i].val.type == MSGPACK_OBJECT_##objtype, \
+ (un).data.via.map.ptr[i].val.type == MSGPACK_OBJECT_##objtype, \
attr, proc)
#define BOOLEAN_KEY(un, entry_name, name, tgt) \
TYPED_KEY(un, entry_name, name, "a boolean", tgt, BOOLEAN, boolean, ID)
@@ -3369,25 +3369,23 @@ static ShaDaReadResult msgpack_read_uint64(ShaDaReadDef *const sd_reader, const
BIN_CONVERTED)
#define INT_KEY(un, entry_name, name, tgt, proc) \
CHECKED_KEY(un, entry_name, name, "which is not an integer", tgt, \
- ((un.data.via.map.ptr[i].val.type \
+ (((un).data.via.map.ptr[i].val.type \
== MSGPACK_OBJECT_POSITIVE_INTEGER) \
- || (un.data.via.map.ptr[i].val.type \
+ || ((un).data.via.map.ptr[i].val.type \
== MSGPACK_OBJECT_NEGATIVE_INTEGER)), \
i64, proc)
#define INTEGER_KEY(un, entry_name, name, tgt) \
INT_KEY(un, entry_name, name, tgt, TOINT)
-#define LONG_KEY(un, entry_name, name, tgt) \
- INT_KEY(un, entry_name, name, tgt, TOLONG)
#define ADDITIONAL_KEY(un) \
else { /* NOLINT(readability/braces) */ \
ga_grow(&ad_ga, 1); \
memcpy(((char *)ad_ga.ga_data) + ((size_t)ad_ga.ga_len \
- * sizeof(*un.data.via.map.ptr)), \
- un.data.via.map.ptr + i, \
- sizeof(*un.data.via.map.ptr)); \
+ * sizeof(*(un).data.via.map.ptr)), \
+ (un).data.via.map.ptr + i, \
+ sizeof(*(un).data.via.map.ptr)); \
ad_ga.ga_len++; \
}
-#define BIN_CONVERTED(b) (xmemdupz((b.ptr), (b.size)))
+#define BIN_CONVERTED(b) (xmemdupz(((b).ptr), ((b).size)))
#define SET_ADDITIONAL_DATA(tgt, name) \
do { \
if (ad_ga.ga_len) { \
@@ -3410,7 +3408,7 @@ static ShaDaReadResult msgpack_read_uint64(ShaDaReadDef *const sd_reader, const
tv_clear(&adtv); \
goto shada_read_next_item_error; \
} \
- tgt = adtv.vval.v_dict; \
+ (tgt) = adtv.vval.v_dict; \
} \
ga_clear(&ad_ga); \
} while (0)
@@ -3641,7 +3639,7 @@ shada_read_next_item_start:
"mark", unpacked.data.via.map.ptr[i].val,
entry->data.filemark.name, u64, TOCHAR);
}
- LONG_KEY(unpacked, "mark", KEY_LNUM, entry->data.filemark.mark.lnum)
+ INTEGER_KEY(unpacked, "mark", KEY_LNUM, entry->data.filemark.mark.lnum)
INTEGER_KEY(unpacked, "mark", KEY_COL, entry->data.filemark.mark.col)
STRING_KEY(unpacked, "mark", KEY_FILE, entry->data.filemark.fname)
ADDITIONAL_KEY(unpacked)
@@ -3888,8 +3886,8 @@ shada_read_next_item_start:
{
for (i = 0; i < unpacked_2.data.via.map.size; i++) { // -V535
CHECK_KEY_IS_STR(unpacked_2, "buffer list entry")
- LONG_KEY(unpacked_2, "buffer list entry", KEY_LNUM,
- entry->data.buffer_list.buffers[j].pos.lnum)
+ INTEGER_KEY(unpacked_2, "buffer list entry", KEY_LNUM,
+ entry->data.buffer_list.buffers[j].pos.lnum)
INTEGER_KEY(unpacked_2, "buffer list entry", KEY_COL,
entry->data.buffer_list.buffers[j].pos.col)
STRING_KEY(unpacked_2, "buffer list entry", KEY_FILE,
@@ -3957,7 +3955,6 @@ shada_read_next_item_error:
#undef TYPED_KEY
#undef INT_KEY
#undef INTEGER_KEY
-#undef LONG_KEY
#undef TOU8
#undef TOSIZE
#undef SET_ADDITIONAL_DATA
@@ -3976,11 +3973,11 @@ static bool shada_removable(const char *name)
char part[MAXPATHL + 1];
bool retval = false;
- char *new_name = (char *)home_replace_save(NULL, (char_u *)name);
+ char *new_name = home_replace_save(NULL, (char *)name);
for (p = (char *)p_shada; *p;) {
- (void)copy_option_part((char_u **)&p, (char_u *)part, ARRAY_SIZE(part), ", ");
+ (void)copy_option_part(&p, part, ARRAY_SIZE(part), ", ");
if (part[0] == 'r') {
- home_replace(NULL, (char_u *)(part + 1), (char_u *)NameBuff, MAXPATHL, true);
+ home_replace(NULL, part + 1, (char *)NameBuff, MAXPATHL, true);
size_t n = STRLEN(NameBuff);
if (mb_strnicmp((char_u *)NameBuff, (char_u *)new_name, n) == 0) {
retval = true;
@@ -4027,9 +4024,8 @@ static inline size_t shada_init_jumps(PossiblyFreedShadaEntry *jumps,
: fm.fmark.fnum != 0) {
continue;
}
- const char *const fname = (char *)(fm.fmark.fnum == 0
- ? (fm.fname == NULL ? NULL : fm.fname)
- : buf ? buf->b_ffname : NULL);
+ const char *const fname =
+ (fm.fmark.fnum == 0 ? (fm.fname == NULL ? NULL : fm.fname) : buf ? buf->b_ffname : NULL);
if (fname == NULL) {
continue;
}
diff --git a/src/nvim/sign.c b/src/nvim/sign.c
index a308df07d1..9a4b304d6c 100644
--- a/src/nvim/sign.c
+++ b/src/nvim/sign.c
@@ -5,7 +5,6 @@
// sign.c: functions for managing with signs
//
-
#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
@@ -13,12 +12,14 @@
#include "nvim/edit.h"
#include "nvim/ex_docmd.h"
#include "nvim/fold.h"
+#include "nvim/highlight_group.h"
#include "nvim/move.h"
#include "nvim/option.h"
#include "nvim/screen.h"
#include "nvim/sign.h"
#include "nvim/syntax.h"
#include "nvim/vim.h"
+#include "nvim/window.h"
/// Struct to hold the sign properties.
typedef struct sign sign_T;
@@ -58,7 +59,6 @@ static char *cmds[] = {
#define SIGNCMD_LAST 6
};
-
static hashtab_T sg_table; // sign group (signgroup_T) hashtable
static int next_sign_id = 1; // next sign id in the global group
@@ -100,10 +100,9 @@ static signgroup_T *sign_group_ref(const char_u *groupname)
/// removed, then remove the group.
static void sign_group_unref(char_u *groupname)
{
- hashitem_T *hi;
signgroup_T *group;
- hi = hash_find(&sg_table, groupname);
+ hashitem_T *hi = hash_find(&sg_table, (char *)groupname);
if (!HASHITEM_EMPTY(hi)) {
group = HI2SG(hi);
group->sg_refcount--;
@@ -118,7 +117,7 @@ static void sign_group_unref(char_u *groupname)
/// @return true if 'sign' is in 'group'.
/// A sign can either be in the global group (sign->group == NULL)
/// or in a named group. If 'group' is '*', then the sign is part of the group.
-bool sign_in_group(sign_entry_T *sign, const char_u *group)
+static bool sign_in_group(sign_entry_T *sign, const char_u *group)
{
return ((group != NULL && STRCMP(group, "*") == 0)
|| (group == NULL && sign->se_group == NULL)
@@ -127,7 +126,7 @@ bool sign_in_group(sign_entry_T *sign, const char_u *group)
}
/// Get the next free sign identifier in the specified group
-int sign_group_get_next_signid(buf_T *buf, const char_u *groupname)
+static int sign_group_get_next_signid(buf_T *buf, const char_u *groupname)
{
int id = 1;
signgroup_T *group = NULL;
@@ -136,7 +135,7 @@ int sign_group_get_next_signid(buf_T *buf, const char_u *groupname)
int found = false;
if (groupname != NULL) {
- hi = hash_find(&sg_table, groupname);
+ hi = hash_find(&sg_table, (char *)groupname);
if (HASHITEM_EMPTY(hi)) {
return id;
}
@@ -196,7 +195,8 @@ static void insert_sign(buf_T *buf, sign_entry_T *prev, sign_entry_T *next, int
if (next != NULL) {
next->se_prev = newsign;
}
- buf->b_signcols_valid = false;
+
+ buf_signcols_add_check(buf, newsign);
if (prev == NULL) {
// When adding first sign need to redraw the windows to create the
@@ -244,8 +244,21 @@ static void insert_sign_by_lnum_prio(buf_T *buf, sign_entry_T *prev, int id, con
insert_sign(buf, prev, sign, id, group, prio, lnum, typenr, has_text_or_icon);
}
+/// Lookup a sign by typenr. Returns NULL if sign is not found.
+static sign_T *find_sign_by_typenr(int typenr)
+{
+ sign_T *sp;
+
+ for (sp = first_sign; sp != NULL; sp = sp->sn_next) {
+ if (sp->sn_typenr == typenr) {
+ return sp;
+ }
+ }
+ return NULL;
+}
+
/// Get the name of a sign by its typenr.
-char_u *sign_typenr2name(int typenr)
+static char_u *sign_typenr2name(int typenr)
{
sign_T *sp;
@@ -258,7 +271,7 @@ char_u *sign_typenr2name(int typenr)
}
/// Return information about a sign in a Dict
-dict_T *sign_get_info(sign_entry_T *sign)
+static dict_T *sign_get_info(sign_entry_T *sign)
{
dict_T *d = tv_dict_alloc();
tv_dict_add_nr(d, S_LEN("id"), sign->se_id);
@@ -346,7 +359,6 @@ static void sign_sort_by_prio_on_line(buf_T *buf, sign_entry_T *sign)
}
}
-
/// Add the sign into the signlist. Find the right spot to do it though.
///
/// @param buf buffer to store sign in
@@ -356,8 +368,8 @@ static void sign_sort_by_prio_on_line(buf_T *buf, sign_entry_T *sign)
/// @param lnum line number which gets the mark
/// @param typenr typenr of sign we are adding
/// @param has_text_or_icon sign has text or icon
-void buf_addsign(buf_T *buf, int id, const char_u *groupname, int prio, linenr_T lnum, int typenr,
- bool has_text_or_icon)
+static void buf_addsign(buf_T *buf, int id, const char_u *groupname, int prio, linenr_T lnum,
+ int typenr, bool has_text_or_icon)
{
sign_entry_T *sign; // a sign in the signlist
sign_entry_T *prev; // the previous sign
@@ -403,7 +415,8 @@ void buf_addsign(buf_T *buf, int id, const char_u *groupname, int prio, linenr_T
/// @param group sign group
/// @param typenr typenr of sign we are adding
/// @param prio sign priority
-linenr_T buf_change_sign_type(buf_T *buf, int markId, const char_u *group, int typenr, int prio)
+static linenr_T buf_change_sign_type(buf_T *buf, int markId, const char_u *group, int typenr,
+ int prio)
{
sign_entry_T *sign; // a sign in the signlist
@@ -454,19 +467,6 @@ sign_attrs_T *sign_get_attr(SignType type, sign_attrs_T sattrs[], int idx, int m
return NULL;
}
-/// Lookup a sign by typenr. Returns NULL if sign is not found.
-static sign_T *find_sign_by_typenr(int typenr)
-{
- sign_T *sp;
-
- for (sp = first_sign; sp != NULL; sp = sp->sn_next) {
- if (sp->sn_typenr == typenr) {
- return sp;
- }
- }
- return NULL;
-}
-
/// Return the attributes of all the signs placed on line 'lnum' in buffer
/// 'buf'. Used when refreshing the screen. Returns the number of signs.
/// @param buf Buffer in which to search
@@ -506,6 +506,8 @@ int buf_get_signattrs(buf_T *buf, linenr_T lnum, sign_attrs_T sattrs[])
if (sp->sn_num_hl != 0) {
sattr.sat_numhl = syn_id2attr(sp->sn_num_hl);
}
+ // Store the priority so we can mesh in extmark signs later
+ sattr.sat_prio = sign->se_priority;
}
sattrs[nr_matches] = sattr;
@@ -532,14 +534,13 @@ int buf_get_signattrs(buf_T *buf, linenr_T lnum, sign_attrs_T sattrs[])
///
/// @return the line number of the deleted sign. If multiple signs are deleted,
/// then returns the line number of the last sign deleted.
-linenr_T buf_delsign(buf_T *buf, linenr_T atlnum, int id, char_u *group)
+static linenr_T buf_delsign(buf_T *buf, linenr_T atlnum, int id, char_u *group)
{
sign_entry_T **lastp; // pointer to pointer to current sign
sign_entry_T *sign; // a sign in a b_signlist
sign_entry_T *next; // the next sign in a b_signlist
linenr_T lnum; // line number whose sign was deleted
- buf->b_signcols_valid = false;
lastp = &buf->b_signlist;
lnum = 0;
for (sign = buf->b_signlist; sign != NULL; sign = next) {
@@ -552,6 +553,7 @@ linenr_T buf_delsign(buf_T *buf, linenr_T atlnum, int id, char_u *group)
next->se_prev = sign->se_prev;
}
lnum = sign->se_lnum;
+ buf_signcols_del_check(buf, lnum, lnum);
if (sign->se_group != NULL) {
sign_group_unref(sign->se_group->sg_name);
}
@@ -581,7 +583,6 @@ linenr_T buf_delsign(buf_T *buf, linenr_T atlnum, int id, char_u *group)
return lnum;
}
-
/// Find the line number of the sign with the requested id in group 'group'. If
/// the sign does not exist, return 0 as the line number. This will still let
/// the correct file get loaded.
@@ -589,7 +590,7 @@ linenr_T buf_delsign(buf_T *buf, linenr_T atlnum, int id, char_u *group)
/// @param buf buffer to store sign in
/// @param id sign ID
/// @param group sign group
-int buf_findsign(buf_T *buf, int id, char_u *group)
+static int buf_findsign(buf_T *buf, int id, char_u *group)
{
sign_entry_T *sign; // a sign in the signlist
@@ -632,7 +633,7 @@ static sign_entry_T *buf_getsign_at_line(buf_T *buf, linenr_T lnum, char_u *grou
/// @param buf buffer whose sign we are searching for
/// @param lnum line number of sign
/// @param groupname sign group name
-int buf_findsign_id(buf_T *buf, linenr_T lnum, char_u *groupname)
+static int buf_findsign_id(buf_T *buf, linenr_T lnum, char_u *groupname)
{
sign_entry_T *sign; // a sign in the signlist
@@ -645,7 +646,7 @@ int buf_findsign_id(buf_T *buf, linenr_T lnum, char_u *groupname)
}
/// Delete signs in buffer "buf".
-void buf_delete_signs(buf_T *buf, char_u *group)
+void buf_delete_signs(buf_T *buf, char *group)
{
sign_entry_T *sign;
sign_entry_T **lastp; // pointer to pointer to current sign
@@ -660,7 +661,7 @@ void buf_delete_signs(buf_T *buf, char_u *group)
lastp = &buf->b_signlist;
for (sign = buf->b_signlist; sign != NULL; sign = next) {
next = sign->se_next;
- if (sign_in_group(sign, group)) {
+ if (sign_in_group(sign, (char_u *)group)) {
*lastp = next;
if (next != NULL) {
next->se_prev = sign->se_prev;
@@ -673,11 +674,11 @@ void buf_delete_signs(buf_T *buf, char_u *group)
lastp = &sign->se_next;
}
}
- buf->b_signcols_valid = false;
+ buf_signcols_del_check(buf, 1, MAXLNUM);
}
/// List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers.
-void sign_list_placed(buf_T *rbuf, char_u *sign_group)
+static void sign_list_placed(buf_T *rbuf, char_u *sign_group)
{
buf_T *buf;
sign_entry_T *sign;
@@ -725,7 +726,7 @@ void sign_list_placed(buf_T *rbuf, char_u *sign_group)
}
/// Adjust a placed sign for inserted/deleted lines.
-void sign_mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after)
+void sign_mark_adjust(linenr_T line1, linenr_T line2, linenr_T amount, linenr_T amount_after)
{
sign_entry_T *sign; // a sign in a b_signlist
sign_entry_T *next; // the next sign in a b_signlist
@@ -735,14 +736,19 @@ void sign_mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_a
int is_fixed = 0;
int signcol = win_signcol_configured(curwin, &is_fixed);
- curbuf->b_signcols_valid = false;
+ bool delete = amount == MAXLNUM;
+
+ if (delete) {
+ buf_signcols_del_check(curbuf, line1, line2);
+ }
+
lastp = &curbuf->b_signlist;
for (sign = curbuf->b_signlist; sign != NULL; sign = next) {
next = sign->se_next;
new_lnum = sign->se_lnum;
if (sign->se_lnum >= line1 && sign->se_lnum <= line2) {
- if (amount != MAXLNUM) {
+ if (!delete) {
new_lnum += amount;
} else if (!is_fixed || signcol >= 2) {
*lastp = next;
@@ -874,11 +880,11 @@ static int sign_define_init_text(sign_T *sp, char_u *text)
}
// Count cells and check for non-printable chars
cells = 0;
- for (s = text; s < endp; s += utfc_ptr2len(s)) {
- if (!vim_isprintc(utf_ptr2char(s))) {
+ for (s = text; s < endp; s += utfc_ptr2len((char *)s)) {
+ if (!vim_isprintc(utf_ptr2char((char *)s))) {
break;
}
- cells += utf_ptr2cells(s);
+ cells += utf_ptr2cells((char *)s);
}
// Currently must be empty, one or two display cells
if (s != endp || cells > 2) {
@@ -904,8 +910,8 @@ static int sign_define_init_text(sign_T *sp, char_u *text)
}
/// Define a new sign or update an existing sign
-int sign_define_by_name(char_u *name, char_u *icon, char_u *linehl, char_u *text, char_u *texthl,
- char_u *culhl, char *numhl)
+static int sign_define_by_name(char_u *name, char_u *icon, char_u *linehl, char_u *text,
+ char_u *texthl, char_u *culhl, char *numhl)
{
sign_T *sp_prev;
sign_T *sp;
@@ -946,7 +952,7 @@ int sign_define_by_name(char_u *name, char_u *icon, char_u *linehl, char_u *text
if (*linehl == NUL) {
sp->sn_line_hl = 0;
} else {
- sp->sn_line_hl = syn_check_group((char *)linehl, (int)STRLEN(linehl));
+ sp->sn_line_hl = syn_check_group((char *)linehl, STRLEN(linehl));
}
}
@@ -954,7 +960,7 @@ int sign_define_by_name(char_u *name, char_u *icon, char_u *linehl, char_u *text
if (*texthl == NUL) {
sp->sn_text_hl = 0;
} else {
- sp->sn_text_hl = syn_check_group((char *)texthl, (int)STRLEN(texthl));
+ sp->sn_text_hl = syn_check_group((char *)texthl, STRLEN(texthl));
}
}
@@ -962,7 +968,7 @@ int sign_define_by_name(char_u *name, char_u *icon, char_u *linehl, char_u *text
if (*culhl == NUL) {
sp->sn_cul_hl = 0;
} else {
- sp->sn_cul_hl = syn_check_group((char *)culhl, (int)STRLEN(culhl));
+ sp->sn_cul_hl = syn_check_group((char *)culhl, STRLEN(culhl));
}
}
@@ -970,7 +976,7 @@ int sign_define_by_name(char_u *name, char_u *icon, char_u *linehl, char_u *text
if (*numhl == NUL) {
sp->sn_num_hl = 0;
} else {
- sp->sn_num_hl = syn_check_group(numhl, (int)STRLEN(numhl));
+ sp->sn_num_hl = syn_check_group(numhl, STRLEN(numhl));
}
}
@@ -978,7 +984,7 @@ int sign_define_by_name(char_u *name, char_u *icon, char_u *linehl, char_u *text
}
/// Free the sign specified by 'name'.
-int sign_undefine_by_name(const char_u *name)
+static int sign_undefine_by_name(const char_u *name)
{
sign_T *sp_prev;
sign_T *sp;
@@ -993,17 +999,6 @@ int sign_undefine_by_name(const char_u *name)
return OK;
}
-static void may_force_numberwidth_recompute(buf_T *buf, int unplace)
-{
- FOR_ALL_TAB_WINDOWS(tp, wp)
- if (wp->w_buffer == buf
- && (wp->w_p_nu || wp->w_p_rnu)
- && (unplace || wp->w_nrwidth_width < 2)
- && (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u')) {
- wp->w_nrwidth_line_count = 0;
- }
-}
-
/// List the signs matching 'name'
static void sign_list_by_name(char_u *name)
{
@@ -1017,10 +1012,20 @@ static void sign_list_by_name(char_u *name)
}
}
+static void may_force_numberwidth_recompute(buf_T *buf, int unplace)
+{
+ FOR_ALL_TAB_WINDOWS(tp, wp)
+ if (wp->w_buffer == buf
+ && (wp->w_p_nu || wp->w_p_rnu)
+ && (unplace || wp->w_nrwidth_width < 2)
+ && (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u')) {
+ wp->w_nrwidth_line_count = 0;
+ }
+}
/// Place a sign at the specified file location or update a sign.
-int sign_place(int *sign_id, const char_u *sign_group, const char_u *sign_name, buf_T *buf,
- linenr_T lnum, int prio)
+static int sign_place(int *sign_id, const char_u *sign_group, const char_u *sign_name, buf_T *buf,
+ linenr_T lnum, int prio)
{
sign_T *sp;
@@ -1072,7 +1077,7 @@ int sign_place(int *sign_id, const char_u *sign_group, const char_u *sign_name,
}
/// Unplace the specified sign
-int sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum)
+static int sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum)
{
if (buf->b_signlist == NULL) { // No signs in the buffer
return OK;
@@ -1080,7 +1085,7 @@ int sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum)
if (sign_id == 0) {
// Delete all the signs in the specified buffer
redraw_buf_later(buf, NOT_VALID);
- buf_delete_signs(buf, sign_group);
+ buf_delete_signs(buf, (char *)sign_group);
} else {
linenr_T lnum;
@@ -1116,7 +1121,7 @@ static void sign_unplace_at_cursor(char_u *groupname)
}
/// Jump to a sign.
-linenr_T sign_jump(int sign_id, char_u *sign_group, buf_T *buf)
+static linenr_T sign_jump(int sign_id, char_u *sign_group, buf_T *buf)
{
linenr_T lnum;
@@ -1163,28 +1168,34 @@ static void sign_define_cmd(char_u *sign_name, char_u *cmdline)
// set values for a defined sign.
for (;;) {
- arg = skipwhite(p);
+ arg = (char_u *)skipwhite((char *)p);
if (*arg == NUL) {
break;
}
- p = skiptowhite_esc(arg);
+ p = (char_u *)skiptowhite_esc((char *)arg);
if (STRNCMP(arg, "icon=", 5) == 0) {
arg += 5;
+ XFREE_CLEAR(icon);
icon = vim_strnsave(arg, (size_t)(p - arg));
} else if (STRNCMP(arg, "text=", 5) == 0) {
arg += 5;
+ XFREE_CLEAR(text);
text = vim_strnsave(arg, (size_t)(p - arg));
} else if (STRNCMP(arg, "linehl=", 7) == 0) {
arg += 7;
+ XFREE_CLEAR(linehl);
linehl = vim_strnsave(arg, (size_t)(p - arg));
} else if (STRNCMP(arg, "texthl=", 7) == 0) {
arg += 7;
+ XFREE_CLEAR(texthl);
texthl = vim_strnsave(arg, (size_t)(p - arg));
} else if (STRNCMP(arg, "culhl=", 6) == 0) {
arg += 6;
+ XFREE_CLEAR(culhl);
culhl = vim_strnsave(arg, (size_t)(p - arg));
} else if (STRNCMP(arg, "numhl=", 6) == 0) {
arg += 6;
+ XFREE_CLEAR(numhl);
numhl = vim_strnsave(arg, (size_t)(p - arg));
} else {
semsg(_(e_invarg2), arg);
@@ -1261,7 +1272,7 @@ static void sign_unplace_cmd(buf_T *buf, linenr_T lnum, char_u *sign_name, int i
// :sign unplace * group=*
FOR_ALL_BUFFERS(cbuf) {
if (cbuf->b_signlist != NULL) {
- buf_delete_signs(cbuf, group);
+ buf_delete_signs(cbuf, (char *)group);
}
}
}
@@ -1329,12 +1340,12 @@ static int parse_sign_cmd_args(int cmd, char_u *arg, char_u **sign_name, int *si
// first arg could be placed sign id
arg1 = arg;
if (ascii_isdigit(*arg)) {
- *signid = getdigits_int(&arg, true, 0);
+ *signid = getdigits_int((char **)&arg, true, 0);
if (!ascii_iswhite(*arg) && *arg != NUL) {
*signid = -1;
arg = arg1;
} else {
- arg = skipwhite(arg);
+ arg = (char_u *)skipwhite((char *)arg);
}
}
@@ -1376,13 +1387,13 @@ static int parse_sign_cmd_args(int cmd, char_u *arg, char_u **sign_name, int *si
} else if (STRNCMP(arg, "file=", 5) == 0) {
arg += 5;
filename = arg;
- *buf = buflist_findname_exp(arg);
+ *buf = buflist_findname_exp((char *)arg);
break;
} else if (STRNCMP(arg, "buffer=", 7) == 0) {
arg += 7;
filename = arg;
- *buf = buflist_findnr(getdigits_int(&arg, true, 0));
- if (*skipwhite(arg) != NUL) {
+ *buf = buflist_findnr(getdigits_int((char **)&arg, true, 0));
+ if (*skipwhite((char *)arg) != NUL) {
emsg(_(e_trailing));
}
break;
@@ -1390,7 +1401,7 @@ static int parse_sign_cmd_args(int cmd, char_u *arg, char_u **sign_name, int *si
emsg(_(e_invarg));
return FAIL;
}
- arg = skipwhite(arg);
+ arg = (char_u *)skipwhite((char *)arg);
}
if (filename != NULL && *buf == NULL) {
@@ -1410,7 +1421,7 @@ static int parse_sign_cmd_args(int cmd, char_u *arg, char_u **sign_name, int *si
/// ":sign" command
void ex_sign(exarg_T *eap)
{
- char_u *arg = eap->arg;
+ char_u *arg = (char_u *)eap->arg;
char_u *p;
int idx;
sign_T *sp;
@@ -1422,7 +1433,7 @@ void ex_sign(exarg_T *eap)
semsg(_("E160: Unknown sign command: %s"), arg);
return;
}
- arg = skipwhite(p);
+ arg = (char_u *)skipwhite((char *)p);
if (idx <= SIGNCMD_LIST) {
// Define, undefine or list signs.
@@ -1501,34 +1512,34 @@ static void sign_getinfo(sign_T *sp, dict_T *retdict)
if (p == NULL) {
p = "NONE";
}
- tv_dict_add_str(retdict, S_LEN("linehl"), (char *)p);
+ tv_dict_add_str(retdict, S_LEN("linehl"), p);
}
if (sp->sn_text_hl > 0) {
p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, false);
if (p == NULL) {
p = "NONE";
}
- tv_dict_add_str(retdict, S_LEN("texthl"), (char *)p);
+ tv_dict_add_str(retdict, S_LEN("texthl"), p);
}
if (sp->sn_cul_hl > 0) {
p = get_highlight_name_ext(NULL, sp->sn_cul_hl - 1, false);
if (p == NULL) {
p = "NONE";
}
- tv_dict_add_str(retdict, S_LEN("culhl"), (char *)p);
+ tv_dict_add_str(retdict, S_LEN("culhl"), p);
}
if (sp->sn_num_hl > 0) {
p = get_highlight_name_ext(NULL, sp->sn_num_hl - 1, false);
if (p == NULL) {
p = "NONE";
}
- tv_dict_add_str(retdict, S_LEN("numhl"), (char *)p);
+ tv_dict_add_str(retdict, S_LEN("numhl"), p);
}
}
/// If 'name' is NULL, return a list of all the defined signs.
/// Otherwise, return information about the specified sign.
-void sign_getlist(const char_u *name, list_T *retlist)
+static void sign_getlist(const char_u *name, list_T *retlist)
{
sign_T *sp = first_sign;
dict_T *dict;
@@ -1598,8 +1609,8 @@ static void sign_get_placed_in_buf(buf_T *buf, linenr_T lnum, int sign_id, const
/// Get a list of signs placed in buffer 'buf'. If 'num' is non-zero, return the
/// sign placed at the line number. If 'lnum' is zero, return all the signs
/// placed in 'buf'. If 'buf' is NULL, return signs placed in all the buffers.
-void sign_get_placed(buf_T *buf, linenr_T lnum, int sign_id, const char_u *sign_group,
- list_T *retlist)
+static void sign_get_placed(buf_T *buf, linenr_T lnum, int sign_id, const char_u *sign_group,
+ list_T *retlist)
{
if (buf != NULL) {
sign_get_placed_in_buf(buf, lnum, sign_id, sign_group, retlist);
@@ -1618,12 +1629,12 @@ static void sign_list_defined(sign_T *sp)
smsg("sign %s", sp->sn_name);
if (sp->sn_icon != NULL) {
msg_puts(" icon=");
- msg_outtrans(sp->sn_icon);
+ msg_outtrans((char *)sp->sn_icon);
msg_puts(_(" (not supported)"));
}
if (sp->sn_text != NULL) {
msg_puts(" text=");
- msg_outtrans(sp->sn_text);
+ msg_outtrans((char *)sp->sn_text);
}
if (sp->sn_line_hl > 0) {
msg_puts(" linehl=");
@@ -1689,8 +1700,7 @@ void free_signs(void)
}
}
-static enum
-{
+static enum {
EXP_SUBCMD, // expand :sign sub-commands
EXP_DEFINE, // expand :sign define {name} args
EXP_PLACE, // expand :sign place {id} args
@@ -1734,33 +1744,33 @@ static char_u *get_nth_sign_group_name(int idx)
/// Function given to ExpandGeneric() to obtain the sign command
/// expansion.
-char_u *get_sign_name(expand_T *xp, int idx)
+char *get_sign_name(expand_T *xp, int idx)
{
switch (expand_what) {
case EXP_SUBCMD:
- return (char_u *)cmds[idx];
+ return cmds[idx];
case EXP_DEFINE: {
char *define_arg[] = { "culhl=", "icon=", "linehl=", "numhl=", "text=", "texthl=",
NULL };
- return (char_u *)define_arg[idx];
+ return define_arg[idx];
}
case EXP_PLACE: {
char *place_arg[] = { "line=", "name=", "group=", "priority=", "file=",
"buffer=", NULL };
- return (char_u *)place_arg[idx];
+ return place_arg[idx];
}
case EXP_LIST: {
char *list_arg[] = { "group=", "file=", "buffer=", NULL };
- return (char_u *)list_arg[idx];
+ return list_arg[idx];
}
case EXP_UNPLACE: {
char *unplace_arg[] = { "group=", "file=", "buffer=", NULL };
- return (char_u *)unplace_arg[idx];
+ return unplace_arg[idx];
}
case EXP_SIGN_NAMES:
- return get_nth_sign_name(idx);
+ return (char *)get_nth_sign_name(idx);
case EXP_SIGN_GROUPS:
- return get_nth_sign_group_name(idx);
+ return (char *)get_nth_sign_group_name(idx);
default:
return NULL;
}
@@ -1777,7 +1787,7 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg)
// Default: expand subcommands.
xp->xp_context = EXPAND_SIGN;
expand_what = EXP_SUBCMD;
- xp->xp_pattern = arg;
+ xp->xp_pattern = (char *)arg;
end_subcmd = skiptowhite(arg);
if (*end_subcmd == NUL) {
@@ -1791,7 +1801,7 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg)
// :sign {subcmd} {subcmd_args}
// |
// begin_subcmd_args
- begin_subcmd_args = skipwhite(end_subcmd);
+ begin_subcmd_args = (char_u *)skipwhite((char *)end_subcmd);
// Expand last argument of subcmd.
//
@@ -1802,19 +1812,19 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg)
// Loop until reaching last argument.
char_u *p = begin_subcmd_args;
do {
- p = skipwhite(p);
+ p = (char_u *)skipwhite((char *)p);
last = p;
p = skiptowhite(p);
} while (*p != NUL);
- p = vim_strchr(last, '=');
+ p = (char_u *)vim_strchr((char *)last, '=');
// :sign define {name} {args}... {last}=
// | |
// last p
if (p == NULL) {
// Expand last argument name (before equal sign).
- xp->xp_pattern = last;
+ xp->xp_pattern = (char *)last;
switch (cmd_idx) {
case SIGNCMD_DEFINE:
expand_what = EXP_DEFINE;
@@ -1844,7 +1854,7 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg)
}
} else {
// Expand last argument value (after equal sign).
- xp->xp_pattern = p + 1;
+ xp->xp_pattern = (char *)p + 1;
switch (cmd_idx) {
case SIGNCMD_DEFINE:
if (STRNCMP(last, "texthl", 6) == 0
@@ -1887,7 +1897,7 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg)
/// Define a sign using the attributes in 'dict'. Returns 0 on success and -1 on
/// failure.
-int sign_define_from_dict(const char *name_arg, dict_T *dict)
+static int sign_define_from_dict(const char *name_arg, dict_T *dict)
{
char *name = NULL;
char *icon = NULL;
@@ -1938,7 +1948,7 @@ cleanup:
/// Define multiple signs using attributes from list 'l' and store the return
/// values in 'retlist'.
-void sign_define_multiple(list_T *l, list_T *retlist)
+static void sign_define_multiple(list_T *l, list_T *retlist)
{
int retval;
@@ -1953,10 +1963,156 @@ void sign_define_multiple(list_T *l, list_T *retlist)
});
}
+/// "sign_define()" function
+void f_sign_define(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ const char *name;
+
+ if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN) {
+ // Define multiple signs
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
+
+ sign_define_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
+ return;
+ }
+
+ // Define a single sign
+ rettv->vval.v_number = -1;
+
+ name = tv_get_string_chk(&argvars[0]);
+ if (name == NULL) {
+ return;
+ }
+
+ if (argvars[1].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_DICT) {
+ emsg(_(e_dictreq));
+ return;
+ }
+
+ rettv->vval.v_number = sign_define_from_dict(name,
+ argvars[1].v_type ==
+ VAR_DICT ? argvars[1].vval.v_dict : NULL);
+}
+
+/// "sign_getdefined()" function
+void f_sign_getdefined(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ const char *name = NULL;
+
+ tv_list_alloc_ret(rettv, 0);
+
+ if (argvars[0].v_type != VAR_UNKNOWN) {
+ name = tv_get_string(&argvars[0]);
+ }
+
+ sign_getlist((const char_u *)name, rettv->vval.v_list);
+}
+
+/// "sign_getplaced()" function
+void f_sign_getplaced(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ buf_T *buf = NULL;
+ dict_T *dict;
+ dictitem_T *di;
+ linenr_T lnum = 0;
+ int sign_id = 0;
+ const char *group = NULL;
+ bool notanum = false;
+
+ tv_list_alloc_ret(rettv, 0);
+
+ if (argvars[0].v_type != VAR_UNKNOWN) {
+ // get signs placed in the specified buffer
+ buf = get_buf_arg(&argvars[0]);
+ if (buf == NULL) {
+ return;
+ }
+
+ if (argvars[1].v_type != VAR_UNKNOWN) {
+ if (argvars[1].v_type != VAR_DICT
+ || ((dict = argvars[1].vval.v_dict) == NULL)) {
+ emsg(_(e_dictreq));
+ return;
+ }
+ if ((di = tv_dict_find(dict, "lnum", -1)) != NULL) {
+ // get signs placed at this line
+ lnum = (linenr_T)tv_get_number_chk(&di->di_tv, &notanum);
+ if (notanum) {
+ return;
+ }
+ (void)lnum;
+ lnum = tv_get_lnum(&di->di_tv);
+ }
+ if ((di = tv_dict_find(dict, "id", -1)) != NULL) {
+ // get sign placed with this identifier
+ sign_id = (int)tv_get_number_chk(&di->di_tv, &notanum);
+ if (notanum) {
+ return;
+ }
+ }
+ if ((di = tv_dict_find(dict, "group", -1)) != NULL) {
+ group = tv_get_string_chk(&di->di_tv);
+ if (group == NULL) {
+ return;
+ }
+ if (*group == '\0') { // empty string means global group
+ group = NULL;
+ }
+ }
+ }
+ }
+
+ sign_get_placed(buf, lnum, sign_id, (const char_u *)group,
+ rettv->vval.v_list);
+}
+
+/// "sign_jump()" function
+void f_sign_jump(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ int sign_id;
+ char *sign_group = NULL;
+ buf_T *buf;
+ bool notanum = false;
+
+ rettv->vval.v_number = -1;
+
+ // Sign identifier
+ sign_id = (int)tv_get_number_chk(&argvars[0], &notanum);
+ if (notanum) {
+ return;
+ }
+ if (sign_id <= 0) {
+ emsg(_(e_invarg));
+ return;
+ }
+
+ // Sign group
+ const char *sign_group_chk = tv_get_string_chk(&argvars[1]);
+ if (sign_group_chk == NULL) {
+ return;
+ }
+ if (sign_group_chk[0] == '\0') {
+ sign_group = NULL; // global sign group
+ } else {
+ sign_group = xstrdup(sign_group_chk);
+ }
+
+ // Buffer to place the sign
+ buf = get_buf_arg(&argvars[2]);
+ if (buf == NULL) {
+ goto cleanup;
+ }
+
+ rettv->vval.v_number = sign_jump(sign_id, (char_u *)sign_group, buf);
+
+cleanup:
+ xfree(sign_group);
+}
+
/// Place a new sign using the values specified in dict 'dict'. Returns the sign
/// identifier if successfully placed, otherwise returns 0.
-int sign_place_from_dict(typval_T *id_tv, typval_T *group_tv, typval_T *name_tv, typval_T *buf_tv,
- dict_T *dict)
+static int sign_place_from_dict(typval_T *id_tv, typval_T *group_tv, typval_T *name_tv,
+ typval_T *buf_tv, dict_T *dict)
{
int sign_id = 0;
char_u *group = NULL;
@@ -2068,8 +2224,50 @@ cleanup:
return ret_sign_id;
}
+/// "sign_place()" function
+void f_sign_place(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ dict_T *dict = NULL;
+
+ rettv->vval.v_number = -1;
+
+ if (argvars[4].v_type != VAR_UNKNOWN
+ && (argvars[4].v_type != VAR_DICT
+ || ((dict = argvars[4].vval.v_dict) == NULL))) {
+ emsg(_(e_dictreq));
+ return;
+ }
+
+ rettv->vval.v_number = sign_place_from_dict(&argvars[0], &argvars[1], &argvars[2], &argvars[3],
+ dict);
+}
+
+/// "sign_placelist()" function. Place multiple signs.
+void f_sign_placelist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ int sign_id;
+
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
+
+ if (argvars[0].v_type != VAR_LIST) {
+ emsg(_(e_listreq));
+ return;
+ }
+
+ // Process the List of sign attributes
+ TV_LIST_ITER_CONST(argvars[0].vval.v_list, li, {
+ sign_id = -1;
+ if (TV_LIST_ITEM_TV(li)->v_type == VAR_DICT) {
+ sign_id = sign_place_from_dict(NULL, NULL, NULL, NULL, TV_LIST_ITEM_TV(li)->vval.v_dict);
+ } else {
+ emsg(_(e_dictreq));
+ }
+ tv_list_append_number(rettv->vval.v_list, sign_id);
+ });
+}
+
/// Undefine multiple signs
-void sign_undefine_multiple(list_T *l, list_T *retlist)
+static void sign_undefine_multiple(list_T *l, list_T *retlist)
{
char_u *name;
int retval;
@@ -2084,9 +2282,41 @@ void sign_undefine_multiple(list_T *l, list_T *retlist)
});
}
+/// "sign_undefine()" function
+void f_sign_undefine(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ const char *name;
+
+ if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN) {
+ // Undefine multiple signs
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
+
+ sign_undefine_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
+ return;
+ }
+
+ rettv->vval.v_number = -1;
+
+ if (argvars[0].v_type == VAR_UNKNOWN) {
+ // Free all the signs
+ free_signs();
+ rettv->vval.v_number = 0;
+ } else {
+ // Free only the specified sign
+ name = tv_get_string_chk(&argvars[0]);
+ if (name == NULL) {
+ return;
+ }
+
+ if (sign_undefine_by_name((const char_u *)name) == OK) {
+ rettv->vval.v_number = 0;
+ }
+ }
+}
+
/// Unplace the sign with attributes specified in 'dict'. Returns 0 on success
/// and -1 on failure.
-int sign_unplace_from_dict(typval_T *group_tv, dict_T *dict)
+static int sign_unplace_from_dict(typval_T *group_tv, dict_T *dict)
{
dictitem_T *di;
int sign_id = 0;
@@ -2142,3 +2372,48 @@ cleanup:
return retval;
}
+/// "sign_unplace()" function
+void f_sign_unplace(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ dict_T *dict = NULL;
+
+ rettv->vval.v_number = -1;
+
+ if (argvars[0].v_type != VAR_STRING) {
+ emsg(_(e_invarg));
+ return;
+ }
+
+ if (argvars[1].v_type != VAR_UNKNOWN) {
+ if (argvars[1].v_type != VAR_DICT) {
+ emsg(_(e_dictreq));
+ return;
+ }
+ dict = argvars[1].vval.v_dict;
+ }
+
+ rettv->vval.v_number = sign_unplace_from_dict(&argvars[0], dict);
+}
+
+/// "sign_unplacelist()" function
+void f_sign_unplacelist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ int retval;
+
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
+
+ if (argvars[0].v_type != VAR_LIST) {
+ emsg(_(e_listreq));
+ return;
+ }
+
+ TV_LIST_ITER_CONST(argvars[0].vval.v_list, li, {
+ retval = -1;
+ if (TV_LIST_ITEM_TV(li)->v_type == VAR_DICT) {
+ retval = sign_unplace_from_dict(NULL, TV_LIST_ITEM_TV(li)->vval.v_dict);
+ } else {
+ emsg(_(e_dictreq));
+ }
+ tv_list_append_number(rettv->vval.v_list, retval);
+ });
+}
diff --git a/src/nvim/sign.h b/src/nvim/sign.h
index 9044c2d0bb..c61e5d20ef 100644
--- a/src/nvim/sign.h
+++ b/src/nvim/sign.h
@@ -4,10 +4,10 @@
#include <stdbool.h>
#include "nvim/buffer_defs.h"
+#include "nvim/eval/funcs.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/sign_defs.h"
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "sign.h.generated.h"
#endif
diff --git a/src/nvim/sign_defs.h b/src/nvim/sign_defs.h
index c734502878..e4ece71846 100644
--- a/src/nvim/sign_defs.h
+++ b/src/nvim/sign_defs.h
@@ -40,6 +40,7 @@ typedef struct sign_attrs_S {
int sat_linehl;
int sat_culhl;
int sat_numhl;
+ int sat_prio; // Used for inserting extmark signs
} sign_attrs_T;
#define SIGN_SHOW_MAX 9
@@ -54,5 +55,4 @@ typedef enum {
SIGN_TEXT,
} SignType;
-
#endif // NVIM_SIGN_DEFS_H
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index 1296d410f6..2aadc2258e 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -62,11 +62,11 @@
// Disadvantage: When "the" is typed as "hte" it sounds quite different ("@"
// vs "ht") and goes down in the list.
// Used when 'spellsuggest' is set to "best".
-#define RESCORE(word_score, sound_score) ((3 * word_score + sound_score) / 4)
+#define RESCORE(word_score, sound_score) ((3 * (word_score) + (sound_score)) / 4)
// Do the opposite: based on a maximum end score and a known sound score,
// compute the maximum word score that can be used.
-#define MAXSCORE(word_score, sound_score) ((4 * word_score - sound_score) / 3)
+#define MAXSCORE(word_score, sound_score) ((4 * (word_score) - (sound_score)) / 3)
#include <assert.h>
#include <inttypes.h>
@@ -122,7 +122,7 @@
#define WF_CAPMASK (WF_ONECAP | WF_ALLCAP | WF_KEEPCAP | WF_FIXCAP)
// Result values. Lower number is accepted over higher one.
-#define SP_BANNED -1
+#define SP_BANNED (-1)
#define SP_RARE 0
#define SP_OK 1
#define SP_LOCAL 2
@@ -176,7 +176,7 @@ typedef struct {
#define SUG(ga, i) (((suggest_T *)(ga).ga_data)[i])
// True if a word appears in the list of banned words.
-#define WAS_BANNED(su, word) (!HASHITEM_EMPTY(hash_find(&su->su_banned, word)))
+#define WAS_BANNED(su, word) (!HASHITEM_EMPTY(hash_find(&(su)->su_banned, word)))
// Number of suggestions kept when cleaning up. We need to keep more than
// what is displayed, because when rescore_suggestions() is called the score
@@ -225,7 +225,7 @@ typedef struct {
#define SCORE_SFMAX2 300 // maximum score for second try
#define SCORE_SFMAX3 400 // maximum score for third try
-#define SCORE_BIG SCORE_INS * 3 // big difference
+#define SCORE_BIG (SCORE_INS * 3) // big difference
#define SCORE_MAXMAX 999999 // accept any score
#define SCORE_LIMITMAX 350 // for spell_edit_score_limit()
@@ -301,7 +301,6 @@ typedef struct {
int score;
} limitscore_T;
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "spell.c.generated.h"
#endif
@@ -385,7 +384,7 @@ size_t spell_check(win_T *wp, char_u *ptr, hlf_T *attrp, int *capcol, bool docou
} else if (*ptr == '0' && (ptr[1] == 'x' || ptr[1] == 'X')) {
mi.mi_end = skiphex(ptr + 2);
} else {
- mi.mi_end = skipdigits(ptr);
+ mi.mi_end = (char_u *)skipdigits((char *)ptr);
}
nrlen = (size_t)(mi.mi_end - ptr);
}
@@ -397,7 +396,7 @@ size_t spell_check(win_T *wp, char_u *ptr, hlf_T *attrp, int *capcol, bool docou
bool this_upper = false; // init for gcc
if (use_camel_case) {
- c = utf_ptr2char(mi.mi_fend);
+ c = utf_ptr2char((char *)mi.mi_fend);
this_upper = SPELL_ISUPPER(c);
}
@@ -405,7 +404,7 @@ size_t spell_check(win_T *wp, char_u *ptr, hlf_T *attrp, int *capcol, bool docou
MB_PTR_ADV(mi.mi_fend);
if (use_camel_case) {
const bool prev_upper = this_upper;
- c = utf_ptr2char(mi.mi_fend);
+ c = utf_ptr2char((char *)mi.mi_fend);
this_upper = SPELL_ISUPPER(c);
camel_case = !prev_upper && this_upper;
}
@@ -414,7 +413,7 @@ size_t spell_check(win_T *wp, char_u *ptr, hlf_T *attrp, int *capcol, bool docou
if (capcol != NULL && *capcol == 0 && wp->w_s->b_cap_prog != NULL) {
// Check word starting with capital letter.
- c = utf_ptr2char(ptr);
+ c = utf_ptr2char((char *)ptr);
if (!SPELL_ISUPPER(c)) {
wrongcaplen = (size_t)(mi.mi_fend - ptr);
}
@@ -443,7 +442,7 @@ size_t spell_check(win_T *wp, char_u *ptr, hlf_T *attrp, int *capcol, bool docou
MAXWLEN + 1);
mi.mi_fwordlen = (int)STRLEN(mi.mi_fword);
- if (camel_case) {
+ if (camel_case && mi.mi_fwordlen > 0) {
// introduce a fake word end space into the folded word.
mi.mi_fword[mi.mi_fwordlen - 1] = ' ';
}
@@ -505,14 +504,14 @@ size_t spell_check(win_T *wp, char_u *ptr, hlf_T *attrp, int *capcol, bool docou
// Check for end of sentence.
regmatch.regprog = wp->w_s->b_cap_prog;
regmatch.rm_ic = false;
- int r = vim_regexec(&regmatch, ptr, 0);
+ int r = vim_regexec(&regmatch, (char *)ptr, 0);
wp->w_s->b_cap_prog = regmatch.regprog;
if (r) {
*capcol = (int)(regmatch.endp[0] - ptr);
}
}
- return (size_t)(utfc_ptr2len(ptr));
+ return (size_t)(utfc_ptr2len((char *)ptr));
} else if (mi.mi_end == ptr) {
// Always include at least one character. Required for when there
// is a mixup in "midword".
@@ -1086,7 +1085,7 @@ static bool can_compound(slang_T *slang, const char_u *word, const char_u *flags
// Need to convert the single byte flags to utf8 characters.
char_u *p = uflags;
for (int i = 0; flags[i] != NUL; i++) {
- p += utf_char2bytes(flags[i], p);
+ p += utf_char2bytes(flags[i], (char *)p);
}
*p = NUL;
p = uflags;
@@ -1173,7 +1172,7 @@ static bool match_compoundrule(slang_T *slang, char_u *compflags)
}
// Skip to the next "/", where the next pattern starts.
- p = vim_strchr(p, '/');
+ p = (char_u *)vim_strchr((char *)p, '/');
if (p == NULL) {
break;
}
@@ -1303,7 +1302,6 @@ static void find_prefix(matchinf_T *mip, int mode)
mip->mi_word);
find_word(mip, FIND_PREFIX);
-
if (len == 0) {
break; // no children, word must end here
}
@@ -1471,7 +1469,9 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att
}
// Copy the line into "buf" and append the start of the next line if
- // possible.
+ // possible. Note: this ml_get_buf() may make "line" invalid, check
+ // for empty line first.
+ bool empty_line = *skipwhite((const char *)line) == NUL;
STRCPY(buf, line);
if (lnum < wp->w_buffer->b_ml.ml_line_count) {
spell_cat_line(buf + STRLEN(buf),
@@ -1578,7 +1578,7 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att
lnum = wp->w_buffer->b_ml.ml_line_count;
wrapped = true;
if (!shortmess(SHM_SEARCH)) {
- give_warning((char_u *)_(top_bot_msg), true);
+ give_warning(_(top_bot_msg), true);
}
}
capcol = -1;
@@ -1593,7 +1593,7 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att
lnum = 1;
wrapped = true;
if (!shortmess(SHM_SEARCH)) {
- give_warning((char_u *)_(bot_top_msg), true);
+ give_warning(_(bot_top_msg), true);
}
}
@@ -1615,7 +1615,7 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att
--capcol;
// But after empty line check first word in next line
- if (*skipwhite(line) == NUL) {
+ if (empty_line) {
capcol = 0;
}
}
@@ -1636,9 +1636,9 @@ void spell_cat_line(char_u *buf, char_u *line, int maxlen)
char_u *p;
int n;
- p = skipwhite(line);
- while (vim_strchr((char_u *)"*#/\"\t", *p) != NULL) {
- p = skipwhite(p + 1);
+ p = (char_u *)skipwhite((char *)line);
+ while (vim_strchr("*#/\"\t", *p) != NULL) {
+ p = (char_u *)skipwhite((char *)p + 1);
}
if (*p != NUL) {
@@ -1647,7 +1647,7 @@ void spell_cat_line(char_u *buf, char_u *line, int maxlen)
n = (int)(p - line) + 1;
if (n < maxlen - 1) {
memset(buf, ' ', n);
- STRLCPY(buf + n, p, maxlen - n);
+ STRLCPY(buf + n, p, maxlen - n);
}
}
}
@@ -1656,7 +1656,7 @@ void spell_cat_line(char_u *buf, char_u *line, int maxlen)
// "lang" must be the language without the region: e.g., "en".
static void spell_load_lang(char_u *lang)
{
- char_u fname_enc[85];
+ char fname_enc[85];
int r;
spelload_T sl;
int round;
@@ -1673,17 +1673,17 @@ static void spell_load_lang(char_u *lang)
// Find the first spell file for "lang" in 'runtimepath' and load it.
vim_snprintf((char *)fname_enc, sizeof(fname_enc) - 5,
"spell/%s.%s.spl", lang, spell_enc());
- r = do_in_runtimepath(fname_enc, 0, spell_load_cb, &sl);
+ r = do_in_runtimepath((char *)fname_enc, 0, spell_load_cb, &sl);
if (r == FAIL && *sl.sl_lang != NUL) {
// Try loading the ASCII version.
vim_snprintf((char *)fname_enc, sizeof(fname_enc) - 5,
"spell/%s.ascii.spl", lang);
- r = do_in_runtimepath(fname_enc, 0, spell_load_cb, &sl);
+ r = do_in_runtimepath((char *)fname_enc, 0, spell_load_cb, &sl);
if (r == FAIL && *sl.sl_lang != NUL && round == 1
- && apply_autocmds(EVENT_SPELLFILEMISSING, lang,
- curbuf->b_fname, FALSE, curbuf)) {
+ && apply_autocmds(EVENT_SPELLFILEMISSING, (char *)lang,
+ curbuf->b_fname, false, curbuf)) {
continue;
}
break;
@@ -1707,7 +1707,7 @@ static void spell_load_lang(char_u *lang)
} else if (sl.sl_slang != NULL) {
// At least one file was loaded, now load ALL the additions.
STRCPY(fname_enc + STRLEN(fname_enc) - 3, "add.spl");
- do_in_runtimepath(fname_enc, DIP_ALL, spell_load_cb, &sl);
+ do_in_runtimepath((char *)fname_enc, DIP_ALL, spell_load_cb, &sl);
}
}
@@ -1846,12 +1846,12 @@ void slang_clear_sug(slang_T *lp)
// Load one spell file and store the info into a slang_T.
// Invoked through do_in_runtimepath().
-static void spell_load_cb(char_u *fname, void *cookie)
+static void spell_load_cb(char *fname, void *cookie)
{
spelload_T *slp = (spelload_T *)cookie;
slang_T *slang;
- slang = spell_load_file(fname, slp->sl_lang, NULL, false);
+ slang = spell_load_file((char_u *)fname, slp->sl_lang, NULL, false);
if (slang != NULL) {
// When a previously loaded file has NOBREAK also use it for the
// ".add" files.
@@ -1910,12 +1910,11 @@ void count_common_word(slang_T *lp, char_u *word, int len, int count)
/// @param split word was split, less bonus
static int score_wordcount_adj(slang_T *slang, int score, char_u *word, bool split)
{
- hashitem_T *hi;
wordcount_T *wc;
int bonus;
int newscore;
- hi = hash_find(&slang->sl_wordcount, word);
+ hashitem_T *hi = hash_find(&slang->sl_wordcount, (char *)word);
if (!HASHITEM_EMPTY(hi)) {
wc = HI2WC(hi);
if (wc->wc_count < SCORE_THRES2) {
@@ -1961,14 +1960,14 @@ int init_syl_tab(slang_T *slang)
int l;
ga_init(&slang->sl_syl_items, sizeof(syl_item_T), 4);
- p = vim_strchr(slang->sl_syllable, '/');
+ p = (char_u *)vim_strchr((char *)slang->sl_syllable, '/');
while (p != NULL) {
*p++ = NUL;
if (*p == NUL) { // trailing slash
break;
}
s = p;
- p = vim_strchr(p, '/');
+ p = (char_u *)vim_strchr((char *)p, '/');
if (p == NULL) {
l = (int)STRLEN(s);
} else {
@@ -2023,9 +2022,9 @@ static int count_syllables(slang_T *slang, const char_u *word)
skip = false;
} else {
// No recognized syllable item, at least a syllable char then?
- c = utf_ptr2char(p);
- len = utfc_ptr2len(p);
- if (vim_strchr(slang->sl_syllable, c) == NULL) {
+ c = utf_ptr2char((char *)p);
+ len = utfc_ptr2len((char *)p);
+ if (vim_strchr((char *)slang->sl_syllable, c) == NULL) {
skip = false; // No, search for next syllable
} else if (!skip) {
++cnt; // Yes, count it
@@ -2085,7 +2084,7 @@ char *did_set_spelllang(win_T *wp)
// Loop over comma separated language names.
for (splp = spl_copy; *splp != NUL;) {
// Get one language name.
- copy_option_part(&splp, lang, MAXWLEN, ",");
+ copy_option_part((char **)&splp, (char *)lang, MAXWLEN, ",");
region = NULL;
len = (int)STRLEN(lang);
@@ -2101,11 +2100,11 @@ char *did_set_spelllang(win_T *wp)
// If the name ends in ".spl" use it as the name of the spell file.
// If there is a region name let "region" point to it and remove it
// from the name.
- if (len > 4 && fnamecmp(lang + len - 4, ".spl") == 0) {
+ if (len > 4 && FNAMECMP(lang + len - 4, ".spl") == 0) {
filename = true;
// Locate a region and remove it from the file name.
- p = vim_strchr(path_tail(lang), '_');
+ p = (char_u *)vim_strchr(path_tail((char *)lang), '_');
if (p != NULL && ASCII_ISALPHA(p[1]) && ASCII_ISALPHA(p[2])
&& !ASCII_ISALPHA(p[3])) {
STRLCPY(region_cp, p + 1, 3);
@@ -2117,7 +2116,7 @@ char *did_set_spelllang(win_T *wp)
// Check if we loaded this language before.
for (slang = first_lang; slang != NULL; slang = slang->sl_next) {
- if (path_full_compare(lang, slang->sl_fname, false, true)
+ if (path_full_compare((char *)lang, (char *)slang->sl_fname, false, true)
== kEqualFiles) {
break;
}
@@ -2166,7 +2165,7 @@ char *did_set_spelllang(win_T *wp)
// Loop over the languages, there can be several files for "lang".
for (slang = first_lang; slang != NULL; slang = slang->sl_next) {
if (filename
- ? path_full_compare(lang, slang->sl_fname, false, true) == kEqualFiles
+ ? path_full_compare((char *)lang, (char *)slang->sl_fname, false, true) == kEqualFiles
: STRICMP(lang, slang->sl_name) == 0) {
region_mask = REGION_ALL;
if (!filename && region != NULL) {
@@ -2217,14 +2216,14 @@ char *did_set_spelllang(win_T *wp)
int_wordlist_spl(spf_name);
} else {
// One entry in 'spellfile'.
- copy_option_part(&spf, spf_name, MAXPATHL - 5, ",");
+ copy_option_part((char **)&spf, (char *)spf_name, MAXPATHL - 5, ",");
STRCAT(spf_name, ".spl");
// If it was already found above then skip it.
for (c = 0; c < ga.ga_len; ++c) {
p = LANGP_ENTRY(ga, c)->lp_slang->sl_fname;
if (p != NULL
- && path_full_compare(spf_name, p, false, true) == kEqualFiles) {
+ && path_full_compare((char *)spf_name, (char *)p, false, true) == kEqualFiles) {
break;
}
}
@@ -2235,7 +2234,7 @@ char *did_set_spelllang(win_T *wp)
// Check if it was loaded already.
for (slang = first_lang; slang != NULL; slang = slang->sl_next) {
- if (path_full_compare(spf_name, slang->sl_fname, false, true)
+ if (path_full_compare((char *)spf_name, (char *)slang->sl_fname, false, true)
== kEqualFiles) {
break;
}
@@ -2247,8 +2246,8 @@ char *did_set_spelllang(win_T *wp)
if (round == 0) {
STRCPY(lang, "internal wordlist");
} else {
- STRLCPY(lang, path_tail(spf_name), MAXWLEN + 1);
- p = vim_strchr(lang, '.');
+ STRLCPY(lang, path_tail((char *)spf_name), MAXWLEN + 1);
+ p = (char_u *)vim_strchr((char *)lang, '.');
if (p != NULL) {
*p = NUL; // truncate at ".encoding.add"
}
@@ -2330,11 +2329,11 @@ char *did_set_spelllang(win_T *wp)
}
}
}
+ redraw_later(wp, NOT_VALID);
theend:
xfree(spl_copy);
recursive = false;
- redraw_later(wp, NOT_VALID);
return ret_msg;
}
@@ -2355,8 +2354,8 @@ static void use_midword(slang_T *lp, win_T *wp)
}
for (char_u *p = lp->sl_midword; *p != NUL;) {
- const int c = utf_ptr2char(p);
- const int l = utfc_ptr2len(p);
+ const int c = utf_ptr2char((char *)p);
+ const int l = utfc_ptr2len((char *)p);
if (c < 256 && l <= 2) {
wp->w_s->b_spell_ismw[c] = true;
} else if (wp->w_s->b_spell_ismw_mb == NULL) {
@@ -2423,7 +2422,7 @@ int captype(char_u *word, char_u *end)
// But a word with an upper char only at start is a ONECAP.
for (; end == NULL ? *p != NUL : p < end; MB_PTR_ADV(p)) {
if (spell_iswordp_nmw(p, curwin)) {
- c = utf_ptr2char(p);
+ c = utf_ptr2char((char *)p);
if (!SPELL_ISUPPER(c)) {
// UUl -> KEEPCAP
if (past_second && allcap) {
@@ -2464,7 +2463,7 @@ static int badword_captype(char_u *word, char_u *end)
l = u = 0;
first = false;
for (p = word; p < end; MB_PTR_ADV(p)) {
- c = utf_ptr2char(p);
+ c = utf_ptr2char((char *)p);
if (SPELL_ISUPPER(c)) {
++u;
if (p == word) {
@@ -2550,7 +2549,6 @@ void spell_reload(void)
}
}
-
// Opposite of offset2bytes().
// "pp" points to the bytes and is advanced over it.
// Returns the offset.
@@ -2674,7 +2672,7 @@ static bool spell_iswordp(const char_u *p, const win_T *wp)
{
int c;
- const int l = utfc_ptr2len(p);
+ const int l = utfc_ptr2len((char *)p);
const char_u *s = p;
if (l == 1) {
// be quick for ASCII
@@ -2682,16 +2680,16 @@ static bool spell_iswordp(const char_u *p, const win_T *wp)
s = p + 1; // skip a mid-word character
}
} else {
- c = utf_ptr2char(p);
+ c = utf_ptr2char((char *)p);
if (c < 256
? wp->w_s->b_spell_ismw[c]
: (wp->w_s->b_spell_ismw_mb != NULL
- && vim_strchr(wp->w_s->b_spell_ismw_mb, c) != NULL)) {
+ && vim_strchr((char *)wp->w_s->b_spell_ismw_mb, c) != NULL)) {
s = p + l;
}
}
- c = utf_ptr2char(s);
+ c = utf_ptr2char((char *)s);
if (c > 255) {
return spell_mb_isword_class(mb_get_class(s), wp);
}
@@ -2702,7 +2700,7 @@ static bool spell_iswordp(const char_u *p, const win_T *wp)
// Unlike spell_iswordp() this doesn't check for "midword" characters.
bool spell_iswordp_nmw(const char_u *p, win_T *wp)
{
- int c = utf_ptr2char(p);
+ int c = utf_ptr2char((char *)p);
if (c > 255) {
return spell_mb_isword_class(mb_get_class(p), wp);
}
@@ -2730,9 +2728,10 @@ static bool spell_iswordp_w(const int *p, const win_T *wp)
{
const int *s;
- if (*p < 256 ? wp->w_s->b_spell_ismw[*p]
- : (wp->w_s->b_spell_ismw_mb != NULL
- && vim_strchr(wp->w_s->b_spell_ismw_mb, *p) != NULL)) {
+ if (*p <
+ 256 ? wp->w_s->b_spell_ismw[*p] : (wp->w_s->b_spell_ismw_mb != NULL
+ && vim_strchr((char *)wp->w_s->b_spell_ismw_mb,
+ *p) != NULL)) {
s = p + 1;
} else {
s = p;
@@ -2779,7 +2778,7 @@ int spell_casefold(const win_T *wp, char_u *str, int len, char_u *buf, int bufle
c = SPELL_TOFOLD(c);
}
- outi += utf_char2bytes(c, buf + outi);
+ outi += utf_char2bytes(c, (char *)buf + outi);
}
buf[outi] = NUL;
@@ -2807,12 +2806,12 @@ int spell_check_sps(void)
sps_limit = 9999;
for (p = p_sps; *p != NUL;) {
- copy_option_part(&p, buf, MAXPATHL, ",");
+ copy_option_part((char **)&p, (char *)buf, MAXPATHL, ",");
f = 0;
if (ascii_isdigit(*buf)) {
s = buf;
- sps_limit = getdigits_int(&s, true, 0);
+ sps_limit = getdigits_int((char **)&s, true, 0);
if (*s != NUL && !ascii_isdigit(*s)) {
f = -1;
}
@@ -3058,15 +3057,15 @@ void spell_suggest(int count)
// For redo we use a change-word command.
ResetRedobuff();
AppendToRedobuff("ciw");
- AppendToRedobuffLit(p + c,
+ AppendToRedobuffLit((char *)p + c,
stp->st_wordlen + sug.su_badlen - stp->st_orglen);
AppendCharToRedobuff(ESC);
// "p" may be freed here
- ml_replace(curwin->w_cursor.lnum, p, false);
+ ml_replace(curwin->w_cursor.lnum, (char *)p, false);
curwin->w_cursor.col = c;
- changed_bytes(curwin->w_cursor.lnum, c);
+ inserted_bytes(curwin->w_cursor.lnum, c, stp->st_orglen, stp->st_wordlen);
} else {
curwin->w_cursor = prev_cursor;
}
@@ -3100,7 +3099,7 @@ static bool check_need_cap(linenr_T lnum, colnr_T col)
need_cap = true;
} else {
line = ml_get(lnum - 1);
- if (*skipwhite(line) == NUL) {
+ if (*skipwhite((char *)line) == NUL) {
need_cap = true;
} else {
// Append a space in place of the line break.
@@ -3123,7 +3122,7 @@ static bool check_need_cap(linenr_T lnum, colnr_T col)
if (p == line || spell_iswordp_nmw(p, curwin)) {
break;
}
- if (vim_regexec(&regmatch, p, 0)
+ if (vim_regexec(&regmatch, (char *)p, 0)
&& regmatch.endp[0] == line + endcol) {
need_cap = true;
break;
@@ -3137,7 +3136,6 @@ static bool check_need_cap(linenr_T lnum, colnr_T col)
return need_cap;
}
-
// ":spellrepall"
void ex_spellrepall(exarg_T *eap)
{
@@ -3177,7 +3175,7 @@ void ex_spellrepall(exarg_T *eap)
memmove(p, line, curwin->w_cursor.col);
STRCPY(p + curwin->w_cursor.col, repl_to);
STRCAT(p, line + curwin->w_cursor.col + STRLEN(repl_from));
- ml_replace(curwin->w_cursor.lnum, p, false);
+ ml_replace(curwin->w_cursor.lnum, (char *)p, false);
changed_bytes(curwin->w_cursor.lnum, curwin->w_cursor.col);
if (curwin->w_cursor.lnum != prev_lnum) {
@@ -3314,7 +3312,7 @@ static void spell_find_suggest(char_u *badptr, int badlen, suginfo_T *su, int ma
// If the word is not capitalised and spell_check() doesn't consider the
// word to be bad then it might need to be capitalised. Add a suggestion
// for that.
- c = utf_ptr2char(su->su_badptr);
+ c = utf_ptr2char((char *)su->su_badptr);
if (!SPELL_ISUPPER(c) && attr == HLF_COUNT) {
make_case_word(su->su_badword, buf, WF_ONECAP);
add_suggestion(su, &su->su_ga, buf, su->su_badlen, SCORE_ICASE,
@@ -3331,7 +3329,7 @@ static void spell_find_suggest(char_u *badptr, int badlen, suginfo_T *su, int ma
// Loop over the items in 'spellsuggest'.
for (p = sps_copy; *p != NUL;) {
- copy_option_part(&p, buf, MAXPATHL, ",");
+ copy_option_part((char **)&p, (char *)buf, MAXPATHL, ",");
if (STRNCMP(buf, "expr:", 5) == 0) {
// Evaluate an expression. Skip this when called recursively,
@@ -3372,7 +3370,7 @@ static void spell_suggest_expr(suginfo_T *su, char_u *expr)
// The work is split up in a few parts to avoid having to export
// suginfo_T.
// First evaluate the expression and get the resulting list.
- list_T *const list = eval_spell_expr(su->su_badword, expr);
+ list_T *const list = eval_spell_expr((char *)su->su_badword, (char *)expr);
if (list != NULL) {
// Loop over the items in the list.
TV_LIST_ITER(list, li, {
@@ -3413,15 +3411,14 @@ static void spell_suggest_file(suginfo_T *su, char_u *fname)
while (!vim_fgets(line, MAXWLEN * 2, fd) && !got_int) {
line_breakcheck();
- p = vim_strchr(line, '/');
+ p = (char_u *)vim_strchr((char *)line, '/');
if (p == NULL) {
continue; // No Tab found, just skip the line.
}
*p++ = NUL;
if (STRICMP(su->su_badword, line) == 0) {
// Match! Isolate the good word, until CR or NL.
- for (len = 0; p[len] >= ' '; ++len) {
- }
+ for (len = 0; p[len] >= ' '; len++) {}
p[len] = NUL;
// If the suggestion doesn't have specific case duplicate the case
@@ -3523,7 +3520,7 @@ static void spell_suggest_intern(suginfo_T *su, bool interactive)
// Free the info put in "*su" by spell_find_suggest().
static void spell_find_cleanup(suginfo_T *su)
{
-#define FREE_SUG_WORD(sug) xfree(sug->st_word)
+#define FREE_SUG_WORD(sug) xfree((sug)->st_word)
// Free the suggestions.
GA_DEEP_CLEAR(&su->su_ga, suggest_T, FREE_SUG_WORD);
GA_DEEP_CLEAR(&su->su_sga, suggest_T, FREE_SUG_WORD);
@@ -3548,7 +3545,7 @@ void onecap_copy(char_u *word, char_u *wcopy, bool upper)
} else {
c = SPELL_TOFOLD(c);
}
- int l = utf_char2bytes(c, wcopy);
+ int l = utf_char2bytes(c, (char *)wcopy);
STRLCPY(wcopy + l, p, MAXWLEN - l);
}
@@ -3573,7 +3570,7 @@ static void allcap_copy(char_u *word, char_u *wcopy)
if (d - wcopy >= MAXWLEN - MB_MAXBYTES) {
break;
}
- d += utf_char2bytes(c, d);
+ d += utf_char2bytes(c, (char *)d);
}
*d = NUL;
}
@@ -3589,7 +3586,7 @@ static void suggest_try_special(suginfo_T *su)
// Recognize a word that is repeated: "the the".
p = skiptowhite(su->su_fbadword);
len = p - su->su_fbadword;
- p = skipwhite(p);
+ p = (char_u *)skipwhite((char *)p);
if (STRLEN(p) == len && STRNCMP(su->su_fbadword, p, len) == 0) {
// Include badflags: if the badword is onecap or allcap
// use that for the goodword too: "The the" -> "The".
@@ -3667,6 +3664,12 @@ static void suggest_try_change(suginfo_T *su)
p = su->su_badptr + su->su_badlen;
(void)spell_casefold(curwin, p, (int)STRLEN(p), fword + n, MAXWLEN - n);
+ // Make sure the resulting text is not longer than the original text.
+ n = (int)STRLEN(su->su_badptr);
+ if (n < MAXWLEN) {
+ fword[n] = NUL;
+ }
+
for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; ++lpi) {
lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi);
@@ -3690,7 +3693,7 @@ static void suggest_try_change(suginfo_T *su)
// Check the maximum score, if we go over it we won't try this change.
#define TRY_DEEPER(su, stack, depth, add) \
- (stack[depth].ts_score + (add) < su->su_maxscore)
+ ((depth) < MAXWLEN - 1 && (stack)[depth].ts_score + (add) < (su)->su_maxscore)
// Try finding suggestions by adding/removing/swapping letters.
//
@@ -3794,6 +3797,10 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
}
}
+ // The loop may take an indefinite amount of time. Break out after five
+ // sectonds. TODO(vim): add an option for the time limit.
+ proftime_T time_limit = profile_setlimit(5000);
+
// Loop to find all suggestions. At each round we either:
// - For the current state try one operation, advance "ts_curi",
// increase "depth".
@@ -3812,8 +3819,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
if (sp->ts_prefixdepth == PFD_PREFIXTREE) {
// Skip over the NUL bytes, we use them later.
- for (n = 0; n < len && byts[arridx + n] == 0; ++n) {
- }
+ for (n = 0; n < len && byts[arridx + n] == 0; n++) {}
sp->ts_curi += n;
// Always past NUL bytes now.
@@ -3824,7 +3830,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
// At end of a prefix or at start of prefixtree: check for
// following word.
- if (byts[arridx] == 0 || n == (int)STATE_NOPREFIX) {
+ if (depth < MAXWLEN - 1 && (byts[arridx] == 0 || n == STATE_NOPREFIX)) {
// Set su->su_badflags to the caps type at this position.
// Use the caps type until here for the prefix itself.
n = nofold_len(fword, sp->ts_fidx, su->su_badptr);
@@ -3886,8 +3892,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
// none this must be the first try without a prefix.
n = stack[sp->ts_prefixdepth].ts_arridx;
len = pbyts[n++];
- for (c = 0; c < len && pbyts[n + c] == 0; ++c) {
- }
+ for (c = 0; c < len && pbyts[n + c] == 0; c++) {}
if (c > 0) {
c = valid_word_prefix(c, n, flags,
tword + sp->ts_splitoff, slang, false);
@@ -3976,7 +3981,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
if (compound_ok) {
p = preword;
while (*skiptowhite(p) != NUL) {
- p = skipwhite(skiptowhite(p));
+ p = (char_u *)skipwhite((char *)skiptowhite(p));
}
if (fword_ends && !can_compound(slang, p,
compflags + sp->ts_compsplit)) {
@@ -4008,7 +4013,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
c = su->su_badflags;
if ((c & WF_ALLCAP)
&& su->su_badlen ==
- utfc_ptr2len(su->su_badptr)) {
+ utfc_ptr2len((char *)su->su_badptr)) {
c = WF_ONECAP;
}
c |= flags;
@@ -4030,8 +4035,8 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
break;
}
if ((sp->ts_complen == sp->ts_compsplit
- && WAS_BANNED(su, preword + sp->ts_prewordlen))
- || WAS_BANNED(su, preword)) {
+ && WAS_BANNED(su, (char *)preword + sp->ts_prewordlen))
+ || WAS_BANNED(su, (char *)preword)) {
if (slang->sl_compprog == NULL) {
break;
}
@@ -4198,7 +4203,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
}
p = preword;
while (*skiptowhite(p) != NUL) {
- p = skipwhite(skiptowhite(p));
+ p = (char_u *)skipwhite((char *)skiptowhite(p));
}
if (sp->ts_complen > sp->ts_compsplit
&& !can_compound(slang, p,
@@ -4257,7 +4262,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
&& goodword_ends) {
int l;
- l = utfc_ptr2len(fword + sp->ts_fidx);
+ l = utfc_ptr2len((char *)fword + sp->ts_fidx);
if (fword_ends) {
// Copy the skipped character to preword.
memmove(preword + sp->ts_prewordlen,
@@ -4377,7 +4382,9 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
#endif
++depth;
sp = &stack[depth];
- ++sp->ts_fidx;
+ if (fword[sp->ts_fidx] != NUL) {
+ sp->ts_fidx++;
+ }
tword[sp->ts_twordlen++] = c;
sp->ts_arridx = idxs[arridx];
if (newscore == SCORE_SUBST) {
@@ -4393,7 +4400,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
sp->ts_fcharstart = sp->ts_fidx - 1;
sp->ts_isdiff = (newscore != 0)
? DIFF_YES : DIFF_NONE;
- } else if (sp->ts_isdiff == DIFF_INSERT) {
+ } else if (sp->ts_isdiff == DIFF_INSERT && sp->ts_fidx > 0) {
// When inserting trail bytes don't advance in the
// bad word.
sp->ts_fidx--;
@@ -4404,22 +4411,22 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
// Correct ts_fidx for the byte length of the
// character (we didn't check that before).
sp->ts_fidx = sp->ts_fcharstart
- + utfc_ptr2len(fword + sp->ts_fcharstart);
+ + utfc_ptr2len((char *)fword + sp->ts_fcharstart);
// For changing a composing character adjust
// the score from SCORE_SUBST to
// SCORE_SUBCOMP.
- if (utf_iscomposing(utf_ptr2char(tword + sp->ts_twordlen
+ if (utf_iscomposing(utf_ptr2char((char *)tword + sp->ts_twordlen
- sp->ts_tcharlen))
- && utf_iscomposing(utf_ptr2char(fword
+ && utf_iscomposing(utf_ptr2char((char *)fword
+ sp->ts_fcharstart))) {
sp->ts_score -= SCORE_SUBST - SCORE_SUBCOMP;
- } else if (
- !soundfold
+ } else if (!soundfold
&& slang->sl_has_map
&& similar_chars(slang,
- utf_ptr2char(tword + sp->ts_twordlen - sp->ts_tcharlen),
- utf_ptr2char(fword + sp->ts_fcharstart))) {
+ utf_ptr2char((char *)tword + sp->ts_twordlen -
+ sp->ts_tcharlen),
+ utf_ptr2char((char *)fword + sp->ts_fcharstart))) {
// For a similar character adjust score from
// SCORE_SUBST to SCORE_SIMILAR.
sp->ts_score -= SCORE_SUBST - SCORE_SIMILAR;
@@ -4427,7 +4434,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
} else if (sp->ts_isdiff == DIFF_INSERT
&& sp->ts_twordlen > sp->ts_tcharlen) {
p = tword + sp->ts_twordlen - sp->ts_tcharlen;
- c = utf_ptr2char(p);
+ c = utf_ptr2char((char *)p);
if (utf_iscomposing(c)) {
// Inserting a composing char doesn't
// count that much.
@@ -4439,7 +4446,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
// tree (might seem illogical but does
// give better scores).
MB_PTR_BACK(tword, p);
- if (c == utf_ptr2char(p)) {
+ if (c == utf_ptr2char((char *)p)) {
sp->ts_score -= SCORE_INS - SCORE_INSDUP;
}
}
@@ -4490,11 +4497,11 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
// score if the same character is following "nn" -> "n". It's
// a bit illogical for soundfold tree but it does give better
// results.
- c = utf_ptr2char(fword + sp->ts_fidx);
- stack[depth].ts_fidx += utfc_ptr2len(fword + sp->ts_fidx);
+ c = utf_ptr2char((char *)fword + sp->ts_fidx);
+ stack[depth].ts_fidx += utfc_ptr2len((char *)fword + sp->ts_fidx);
if (utf_iscomposing(c)) {
stack[depth].ts_score -= SCORE_DEL - SCORE_DELCOMP;
- } else if (c == utf_ptr2char(fword + stack[depth].ts_fidx)) {
+ } else if (c == utf_ptr2char((char *)fword + stack[depth].ts_fidx)) {
stack[depth].ts_score -= SCORE_DEL - SCORE_DELDUP;
}
@@ -4609,14 +4616,14 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
break;
}
- n = utf_ptr2len(p);
- c = utf_ptr2char(p);
+ n = utf_ptr2len((char *)p);
+ c = utf_ptr2char((char *)p);
if (p[n] == NUL) {
c2 = NUL;
} else if (!soundfold && !spell_iswordp(p + n, curwin)) {
c2 = c; // don't swap non-word char
} else {
- c2 = utf_ptr2char(p + n);
+ c2 = utf_ptr2char((char *)p + n);
}
// When the second character is NUL we can't swap.
@@ -4646,7 +4653,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
depth++;
fl = utf_char2len(c2);
memmove(p, p + n, fl);
- utf_char2bytes(c, p + fl);
+ utf_char2bytes(c, (char *)p + fl);
stack[depth].ts_fidxtry = sp->ts_fidx + n + fl;
} else {
// If this swap doesn't work then SWAP3 won't either.
@@ -4658,10 +4665,10 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
case STATE_UNSWAP:
// Undo the STATE_SWAP swap: "21" -> "12".
p = fword + sp->ts_fidx;
- n = utfc_ptr2len(p);
- c = utf_ptr2char(p + n);
- memmove(p + utfc_ptr2len(p + n), p, n);
- utf_char2bytes(c, p);
+ n = utfc_ptr2len((char *)p);
+ c = utf_ptr2char((char *)p + n);
+ memmove(p + utfc_ptr2len((char *)p + n), p, n);
+ utf_char2bytes(c, (char *)p);
FALLTHROUGH;
@@ -4669,14 +4676,14 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
// Swap two bytes, skipping one: "123" -> "321". We change
// "fword" here, it's changed back afterwards at STATE_UNSWAP3.
p = fword + sp->ts_fidx;
- n = utf_ptr2len(p);
- c = utf_ptr2char(p);
- fl = utf_ptr2len(p + n);
- c2 = utf_ptr2char(p + n);
+ n = utf_ptr2len((char *)p);
+ c = utf_ptr2char((char *)p);
+ fl = utf_ptr2len((char *)p + n);
+ c2 = utf_ptr2char((char *)p + n);
if (!soundfold && !spell_iswordp(p + n + fl, curwin)) {
c3 = c; // don't swap non-word char
} else {
- c3 = utf_ptr2char(p + n + fl);
+ c3 = utf_ptr2char((char *)p + n + fl);
}
// When characters are identical: "121" then SWAP3 result is
@@ -4702,8 +4709,8 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
depth++;
tl = utf_char2len(c3);
memmove(p, p + n + fl, tl);
- utf_char2bytes(c2, p + tl);
- utf_char2bytes(c, p + fl + tl);
+ utf_char2bytes(c2, (char *)p + tl);
+ utf_char2bytes(c, (char *)p + fl + tl);
stack[depth].ts_fidxtry = sp->ts_fidx + n + fl + tl;
} else {
PROF_STORE(sp->ts_state)
@@ -4714,14 +4721,14 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
case STATE_UNSWAP3:
// Undo STATE_SWAP3: "321" -> "123"
p = fword + sp->ts_fidx;
- n = utfc_ptr2len(p);
- c2 = utf_ptr2char(p + n);
- fl = utfc_ptr2len(p + n);
- c = utf_ptr2char(p + n + fl);
- tl = utfc_ptr2len(p + n + fl);
+ n = utfc_ptr2len((char *)p);
+ c2 = utf_ptr2char((char *)p + n);
+ fl = utfc_ptr2len((char *)p + n);
+ c = utf_ptr2char((char *)p + n + fl);
+ tl = utfc_ptr2len((char *)p + n + fl);
memmove(p + fl + tl, p, n);
- utf_char2bytes(c, p);
- utf_char2bytes(c2, p + tl);
+ utf_char2bytes(c, (char *)p);
+ utf_char2bytes(c2, (char *)p + tl);
p = p + tl;
if (!soundfold && !spell_iswordp(p, curwin)) {
@@ -4746,12 +4753,12 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
sp->ts_state = STATE_UNROT3L;
++depth;
p = fword + sp->ts_fidx;
- n = utf_ptr2len(p);
- c = utf_ptr2char(p);
- fl = utf_ptr2len(p + n);
- fl += utf_ptr2len(p + n + fl);
+ n = utf_ptr2len((char *)p);
+ c = utf_ptr2char((char *)p);
+ fl = utf_ptr2len((char *)p + n);
+ fl += utf_ptr2len((char *)p + n + fl);
memmove(p, p + n, fl);
- utf_char2bytes(c, p + fl);
+ utf_char2bytes(c, (char *)p + fl);
stack[depth].ts_fidxtry = sp->ts_fidx + n + fl;
} else {
PROF_STORE(sp->ts_state)
@@ -4762,12 +4769,12 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
case STATE_UNROT3L:
// Undo ROT3L: "231" -> "123"
p = fword + sp->ts_fidx;
- n = utfc_ptr2len(p);
- n += utfc_ptr2len(p + n);
- c = utf_ptr2char(p + n);
- tl = utfc_ptr2len(p + n);
+ n = utfc_ptr2len((char *)p);
+ n += utfc_ptr2len((char *)p + n);
+ c = utf_ptr2char((char *)p + n);
+ tl = utfc_ptr2len((char *)p + n);
memmove(p + tl, p, n);
- utf_char2bytes(c, p);
+ utf_char2bytes(c, (char *)p);
// Rotate three bytes right: "123" -> "312". We change "fword"
// here, it's changed back afterwards at STATE_UNROT3R.
@@ -4783,12 +4790,12 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
sp->ts_state = STATE_UNROT3R;
++depth;
p = fword + sp->ts_fidx;
- n = utf_ptr2len(p);
- n += utf_ptr2len(p + n);
- c = utf_ptr2char(p + n);
- tl = utf_ptr2len(p + n);
+ n = utf_ptr2len((char *)p);
+ n += utf_ptr2len((char *)p + n);
+ c = utf_ptr2char((char *)p + n);
+ tl = utf_ptr2len((char *)p + n);
memmove(p + tl, p, n);
- utf_char2bytes(c, p);
+ utf_char2bytes(c, (char *)p);
stack[depth].ts_fidxtry = sp->ts_fidx + n + tl;
} else {
PROF_STORE(sp->ts_state)
@@ -4799,12 +4806,12 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
case STATE_UNROT3R:
// Undo ROT3R: "312" -> "123"
p = fword + sp->ts_fidx;
- c = utf_ptr2char(p);
- tl = utfc_ptr2len(p);
- n = utfc_ptr2len(p + tl);
- n += utfc_ptr2len(p + tl + n);
+ c = utf_ptr2char((char *)p);
+ tl = utfc_ptr2len((char *)p);
+ n = utfc_ptr2len((char *)p + tl);
+ n += utfc_ptr2len((char *)p + tl + n);
memmove(p, p + tl, n);
- utf_char2bytes(c, p + n);
+ utf_char2bytes(c, (char *)p + n);
FALLTHROUGH;
@@ -4927,12 +4934,14 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
if (--breakcheckcount == 0) {
os_breakcheck();
breakcheckcount = 1000;
+ if (profile_passed_limit(time_limit)) {
+ got_int = true;
+ }
}
}
}
}
-
// Go one level deeper in the tree.
static void go_deeper(trystate_T *stack, int depth, int score_add)
{
@@ -5021,8 +5030,8 @@ static void find_keepcap_word(slang_T *slang, char_u *fword, char_u *kword)
} else {
// round[depth] == 1: Try using the folded-case character.
// round[depth] == 2: Try using the upper-case character.
- flen = utf_ptr2len(fword + fwordidx[depth]);
- ulen = utf_ptr2len(uword + uwordidx[depth]);
+ flen = utf_ptr2len((char *)fword + fwordidx[depth]);
+ ulen = utf_ptr2len((char *)uword + uwordidx[depth]);
if (round[depth] == 1) {
p = fword + fwordidx[depth];
l = flen;
@@ -5284,7 +5293,7 @@ static int stp_sal_score(suggest_T *stp, suginfo_T *su, slang_T *slang, char_u *
}
static sftword_T dumsft;
-#define HIKEY2SFT(p) ((sftword_T *)(p - (dumsft.sft_word - (char_u *)&dumsft)))
+#define HIKEY2SFT(p) ((sftword_T *)((p) - (dumsft.sft_word - (char_u *)&dumsft)))
#define HI2SFT(hi) HIKEY2SFT((hi)->hi_key)
// Prepare for calling suggest_try_soundalike().
@@ -5512,9 +5521,9 @@ badword:
// lower to upper case. Helps for "tath" -> "Kath", which is
// less common than "tath" -> "path". Don't do it when the
// letter is the same, that has already been counted.
- gc = utf_ptr2char(p);
+ gc = utf_ptr2char((char *)p);
if (SPELL_ISUPPER(gc)) {
- bc = utf_ptr2char(su->su_badword);
+ bc = utf_ptr2char((char *)su->su_badword);
if (!SPELL_ISUPPER(bc)
&& SPELL_TOFOLD(bc) != SPELL_TOFOLD(gc)) {
goodscore += SCORE_ICASE / 2;
@@ -5648,16 +5657,16 @@ static void make_case_word(char_u *fword, char_u *cword, int flags)
static bool similar_chars(slang_T *slang, int c1, int c2)
{
int m1, m2;
- char_u buf[MB_MAXBYTES + 1];
+ char buf[MB_MAXBYTES + 1];
hashitem_T *hi;
if (c1 >= 256) {
- buf[utf_char2bytes(c1, buf)] = 0;
+ buf[utf_char2bytes(c1, (char *)buf)] = 0;
hi = hash_find(&slang->sl_map_hash, buf);
if (HASHITEM_EMPTY(hi)) {
m1 = 0;
} else {
- m1 = utf_ptr2char(hi->hi_key + STRLEN(hi->hi_key) + 1);
+ m1 = utf_ptr2char((char *)hi->hi_key + STRLEN(hi->hi_key) + 1);
}
} else {
m1 = slang->sl_map_array[c1];
@@ -5667,12 +5676,12 @@ static bool similar_chars(slang_T *slang, int c1, int c2)
}
if (c2 >= 256) {
- buf[utf_char2bytes(c2, buf)] = 0;
+ buf[utf_char2bytes(c2, (char *)buf)] = 0;
hi = hash_find(&slang->sl_map_hash, buf);
if (HASHITEM_EMPTY(hi)) {
m2 = 0;
} else {
- m2 = utf_ptr2char(hi->hi_key + STRLEN(hi->hi_key) + 1);
+ m2 = utf_ptr2char((char *)hi->hi_key + STRLEN(hi->hi_key) + 1);
}
} else {
m2 = slang->sl_map_array[c2];
@@ -5709,7 +5718,7 @@ static void add_suggestion(suginfo_T *su, garray_T *gap, const char_u *goodword,
}
MB_PTR_BACK(goodword, pgood);
MB_PTR_BACK(su->su_badptr, pbad);
- if (utf_ptr2char(pgood) != utf_ptr2char(pbad)) {
+ if (utf_ptr2char((char *)pgood) != utf_ptr2char((char *)pbad)) {
break;
}
}
@@ -5829,7 +5838,6 @@ static void check_suggestions(suginfo_T *su, garray_T *gap)
}
}
-
// Add a word to be banned.
static void add_banned(suginfo_T *su, char_u *word)
{
@@ -5883,7 +5891,6 @@ static void rescore_one(suginfo_T *su, suggest_T *stp)
}
}
-
// Function given to qsort() to sort the suggestions on st_score.
// First on "st_score", then "st_altscore" then alphabetically.
static int sug_compare(const void *s1, const void *s2)
@@ -6030,7 +6037,7 @@ static void spell_soundfold_sofo(slang_T *slang, char_u *inword, char_u *res)
}
if (c != NUL && c != prevc) {
- ri += utf_char2bytes(c, res + ri);
+ ri += utf_char2bytes(c, (char *)res + ri);
if (ri + MB_MAXBYTES > MAXWLEN) {
break;
}
@@ -6064,7 +6071,6 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res)
bool did_white = false;
int wordlen;
-
// Convert the multi-byte string to a wide-character string.
// Remove accents, if wanted. We actually remove all non-word characters.
// But keep white space.
@@ -6256,7 +6262,7 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res)
// replace string
ws = smp[n].sm_to_w;
s = smp[n].sm_rules;
- p0 = (vim_strchr(s, '<') != NULL) ? 1 : 0;
+ p0 = (vim_strchr((char *)s, '<') != NULL) ? 1 : 0;
if (p0 == 1 && z == 0) {
// rule with '<' is used
if (reslen > 0 && ws != NULL && *ws != NUL
@@ -6335,7 +6341,7 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res)
// Convert wide characters in "wres" to a multi-byte string in "res".
l = 0;
for (n = 0; n < reslen; n++) {
- l += utf_char2bytes(wres[n], res + l);
+ l += utf_char2bytes(wres[n], (char *)res + l);
if (l + MB_MAXBYTES > MAXWLEN) {
break;
}
@@ -6867,7 +6873,7 @@ void ex_spellinfo(exarg_T *eap)
// ":spelldump"
void ex_spelldump(exarg_T *eap)
{
- char_u *spl;
+ char *spl;
long dummy;
if (no_spell_checking(curwin)) {
@@ -6880,7 +6886,7 @@ void ex_spelldump(exarg_T *eap)
// enable spelling locally in the new window
set_option_value("spell", true, "", OPT_LOCAL);
- set_option_value("spl", dummy, (char *)spl, OPT_LOCAL);
+ set_option_value("spl", dummy, spl, OPT_LOCAL);
xfree(spl);
if (!buf_is_empty(curbuf)) {
@@ -6936,7 +6942,7 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg)
if (n == WF_ONECAP) {
dumpflags |= DUMPFLAG_ONECAP;
} else if (n == WF_ALLCAP
- && (int)STRLEN(pat) > utfc_ptr2len(pat)) {
+ && (int)STRLEN(pat) > utfc_ptr2len((char *)pat)) {
dumpflags |= DUMPFLAG_ALLCAP;
}
}
@@ -6960,7 +6966,7 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg)
if (do_region && region_names != NULL) {
if (pat == NULL) {
vim_snprintf((char *)IObuff, IOSIZE, "/regions=%s", region_names);
- ml_append(lnum++, IObuff, (colnr_T)0, false);
+ ml_append(lnum++, (char *)IObuff, (colnr_T)0, false);
}
} else {
do_region = false;
@@ -6976,7 +6982,7 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg)
if (pat == NULL) {
vim_snprintf((char *)IObuff, IOSIZE, "# file: %s", slang->sl_fname);
- ml_append(lnum++, IObuff, (colnr_T)0, false);
+ ml_append(lnum++, (char *)IObuff, (colnr_T)0, false);
}
// When matching with a pattern and there are no prefixes only use
@@ -7017,8 +7023,9 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg)
n = arridx[depth] + curi[depth];
++curi[depth];
c = byts[n];
- if (c == 0) {
- // End of word, deal with the word.
+ if (c == 0 || depth >= MAXWLEN - 1) {
+ // End of word or reached maximum length, deal with the
+ // word.
// Don't use keep-case words in the fold-case tree,
// they will appear in the keep-case tree.
// Only use the word when the region matches.
@@ -7139,7 +7146,7 @@ static void dump_word(slang_T *slang, char_u *word, char_u *pat, Direction *dir,
hashitem_T *hi;
// Include the word count for ":spelldump!".
- hi = hash_find(&slang->sl_wordcount, tw);
+ hi = hash_find(&slang->sl_wordcount, (char *)tw);
if (!HASHITEM_EMPTY(hi)) {
vim_snprintf((char *)IObuff, IOSIZE, "%s\t%d",
tw, HI2WC(hi)->wc_count);
@@ -7147,7 +7154,7 @@ static void dump_word(slang_T *slang, char_u *word, char_u *pat, Direction *dir,
}
}
- ml_append(lnum, p, (colnr_T)0, false);
+ ml_append(lnum, (char *)p, (colnr_T)0, false);
} else if (((dumpflags & DUMPFLAG_ICASE)
? mb_strnicmp(p, pat, STRLEN(pat)) == 0
: STRNCMP(p, pat, STRLEN(pat)) == 0)
@@ -7185,7 +7192,7 @@ static linenr_T dump_prefixes(slang_T *slang, char_u *word, char_u *pat, Directi
// If the word starts with a lower-case letter make the word with an
// upper-case letter in word_up[].
- c = utf_ptr2char(word);
+ c = utf_ptr2char((char *)word);
if (SPELL_TOUPPER(c) != c) {
onecap_copy(word, word_up, true);
has_word_up = true;
diff --git a/src/nvim/spell_defs.h b/src/nvim/spell_defs.h
index 220f51cd2f..7e85b5bf03 100644
--- a/src/nvim/spell_defs.h
+++ b/src/nvim/spell_defs.h
@@ -60,7 +60,6 @@ typedef int idx_T;
#define WF_PFX_COMPFORBID (WFP_COMPFORBID << 24) // postponed prefix with
// COMPOUNDFORBIDFLAG
-
// flags for <compoptions>
#define COMP_CHECKDUP 1 // CHECKCOMPOUNDDUP
#define COMP_CHECKREP 2 // CHECKCOMPOUNDREP
@@ -93,9 +92,9 @@ typedef int salfirst_T;
// Values for SP_*ERROR are negative, positive values are used by
// read_cnt_string().
-#define SP_TRUNCERROR -1 // spell file truncated error
-#define SP_FORMERROR -2 // format error in spell file
-#define SP_OTHERERROR -3 // other error while reading spell file
+#define SP_TRUNCERROR (-1) // spell file truncated error
+#define SP_FORMERROR (-2) // format error in spell file
+#define SP_OTHERERROR (-3) // other error while reading spell file
// Structure used to store words and other info for one language, loaded from
// a .spl file.
diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c
index f6b95f37b1..9d2fd2637d 100644
--- a/src/nvim/spellfile.c
+++ b/src/nvim/spellfile.c
@@ -302,6 +302,7 @@
#define CF_UPPER 0x02
static char *e_spell_trunc = N_("E758: Truncated spell file");
+static char *e_illegal_character_in_word = N_("E1280: Illegal character in word");
static char *e_afftrailing = N_("Trailing text in %s line %d: %s");
static char *e_affname = N_("Affix name too long in %s line %d: %s");
static char *msg_compressing = N_("Compressing word tree...");
@@ -575,7 +576,7 @@ slang_T *spell_load_file(char_u *fname, char_u *lang, slang_T *old_lp, bool sile
char_u *p;
int n;
int len;
- char_u *save_sourcing_name = sourcing_name;
+ char_u *save_sourcing_name = (char_u *)sourcing_name;
linenr_T save_sourcing_lnum = sourcing_lnum;
slang_T *lp = NULL;
int c = 0;
@@ -605,13 +606,13 @@ slang_T *spell_load_file(char_u *fname, char_u *lang, slang_T *old_lp, bool sile
lp->sl_fname = vim_strsave(fname);
// Check for .add.spl.
- lp->sl_add = strstr((char *)path_tail(fname), SPL_FNAME_ADD) != NULL;
+ lp->sl_add = strstr(path_tail((char *)fname), SPL_FNAME_ADD) != NULL;
} else {
lp = old_lp;
}
// Set sourcing_name, so that error messages mention the file name.
- sourcing_name = fname;
+ sourcing_name = (char *)fname;
sourcing_lnum = 0;
// <HEADER>: <fileID>
@@ -637,7 +638,6 @@ slang_T *spell_load_file(char_u *fname, char_u *lang, slang_T *old_lp, bool sile
goto endFAIL;
}
-
// <SECTIONS>: <section> ... <sectionend>
// <section>: <sectionID> <sectionflags> <sectionlen> (section contents)
for (;;) {
@@ -733,7 +733,7 @@ slang_T *spell_load_file(char_u *fname, char_u *lang, slang_T *old_lp, bool sile
if (lp->sl_syllable == NULL) {
goto endFAIL;
}
- if (init_syl_tab(lp) == FAIL) {
+ if (init_syl_tab(lp) != OK) {
goto endFAIL;
}
break;
@@ -809,7 +809,7 @@ endOK:
if (fd != NULL) {
fclose(fd);
}
- sourcing_name = save_sourcing_name;
+ sourcing_name = (char *)save_sourcing_name;
sourcing_lnum = save_sourcing_lnum;
return lp;
@@ -893,7 +893,7 @@ void suggest_load_files(void)
slang->sl_sugloaded = true;
dotp = STRRCHR(slang->sl_fname, '.');
- if (dotp == NULL || fnamecmp(dotp, ".spl") != 0) {
+ if (dotp == NULL || FNAMECMP(dotp, ".spl") != 0) {
continue;
}
STRCPY(dotp, ".sug");
@@ -991,7 +991,6 @@ nextone:
}
}
-
// Read a length field from "fd" in "cnt_bytes" bytes.
// Allocate memory, read the string into it and add a NUL at the end.
// Returns NULL when the count is zero.
@@ -1099,7 +1098,7 @@ static int read_prefcond_section(FILE *fd, slang_T *lp)
buf[0] = '^'; // always match at one position only
SPELL_READ_NONNUL_BYTES(buf + 1, (size_t)n, fd,; );
buf[n + 1] = NUL;
- lp->sl_prefprog[i] = vim_regcomp((char_u *)buf, RE_MAGIC | RE_STRING);
+ lp->sl_prefprog[i] = vim_regcomp(buf, RE_MAGIC | RE_STRING);
}
}
return 0;
@@ -1201,7 +1200,7 @@ static int read_sal_section(FILE *fd, slang_T *slang)
int i = 0;
for (; i < ccnt; ++i) {
c = getc(fd); // <salfrom>
- if (vim_strchr((char_u *)"0123456789(-<^$", c) != NULL) {
+ if (vim_strchr("0123456789(-<^$", c) != NULL) {
break;
}
*p++ = c;
@@ -1465,7 +1464,7 @@ static int read_compound(FILE *fd, slang_T *slang, int len)
}
// Add all flags to "sl_compallflags".
- if (vim_strchr((char_u *)"?*+[]/", c) == NULL
+ if (vim_strchr("?*+[]/", c) == NULL
&& !byte_in_str(slang->sl_compallflags, c)) {
*ap++ = c;
*ap = NUL;
@@ -1507,7 +1506,7 @@ static int read_compound(FILE *fd, slang_T *slang, int len)
if (c == '?' || c == '+' || c == '~') {
*pp++ = '\\'; // "a?" becomes "a\?", "a+" becomes "a\+"
}
- pp += utf_char2bytes(c, pp);
+ pp += utf_char2bytes(c, (char *)pp);
}
}
@@ -1520,7 +1519,7 @@ static int read_compound(FILE *fd, slang_T *slang, int len)
*crp = NUL;
}
- slang->sl_compprog = vim_regcomp(pat, RE_MAGIC + RE_STRING + RE_STRICT);
+ slang->sl_compprog = vim_regcomp((char *)pat, RE_MAGIC + RE_STRING + RE_STRICT);
xfree(pat);
if (slang->sl_compprog == NULL) {
return SP_FORMERROR;
@@ -1827,7 +1826,7 @@ static void spell_reload_one(char_u *fname, bool added_word)
bool didit = false;
for (slang = first_lang; slang != NULL; slang = slang->sl_next) {
- if (path_full_compare(fname, slang->sl_fname, false, true) == kEqualFiles) {
+ if (path_full_compare((char *)fname, (char *)slang->sl_fname, false, true) == kEqualFiles) {
slang_clear(slang);
if (spell_load_file(fname, NULL, slang, false) == NULL) {
// reloading failed, clear the language
@@ -1850,7 +1849,7 @@ static void spell_reload_one(char_u *fname, bool added_word)
// In the postponed prefixes tree wn_flags is used to store the WFP_ flags,
// but it must be negative to indicate the prefix tree to tree_add_word().
// Use a negative number with the lower 8 bits zero.
-#define PFX_FLAGS -256
+#define PFX_FLAGS (-256)
// flags for "condit" argument of store_aff_word()
#define CONDIT_COMB 1 // affix must combine
@@ -2251,7 +2250,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
}
} else if (is_aff_rule(items, itemcnt, "COMPOUNDRULE", 2)) {
// Don't use the first rule if it is a number.
- if (compflags != NULL || *skipdigits(items[1]) != NUL) {
+ if (compflags != NULL || *skipdigits((char *)items[1]) != NUL) {
// Concatenate this string to previously defined ones,
// using a slash to separate them.
l = (int)STRLEN(items[1]) + 1;
@@ -2352,7 +2351,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
// "S" flag on all but the last block, thus we check for that
// and store it in ah_follows.
STRLCPY(key, items[1], AH_KEY_LEN);
- hi = hash_find(tp, key);
+ hi = hash_find(tp, (char *)key);
if (!HASHITEM_EMPTY(hi)) {
cur_aff = HI2AH(hi);
if (cur_aff->ah_combine != (*items[2] == 'Y')) {
@@ -2380,7 +2379,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
|| cur_aff->ah_flag == aff->af_needcomp
|| cur_aff->ah_flag == aff->af_comproot) {
smsg(_("Affix also used for "
- "BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGEST"
+ "BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGEST "
"in %s line %d: %s"),
fname, lnum, items[1]);
}
@@ -2460,7 +2459,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
aff_entry->ae_add = getroom_save(spin, items[3]);
// Recognize flags on the affix: abcd/XYZ
- aff_entry->ae_flags = vim_strchr(aff_entry->ae_add, '/');
+ aff_entry->ae_flags = (char_u *)vim_strchr((char *)aff_entry->ae_add, '/');
if (aff_entry->ae_flags != NULL) {
*aff_entry->ae_flags++ = NUL;
aff_process_flags(aff, aff_entry);
@@ -2483,8 +2482,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
} else {
sprintf((char *)buf, "%s$", items[4]);
}
- aff_entry->ae_prog = vim_regcomp(buf,
- RE_MAGIC + RE_STRING + RE_STRICT);
+ aff_entry->ae_prog = vim_regcomp((char *)buf, RE_MAGIC + RE_STRING + RE_STRICT);
if (aff_entry->ae_prog == NULL) {
smsg(_("Broken condition in %s line %d: %s"),
fname, lnum, items[4]);
@@ -2504,19 +2502,19 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
// be empty or start with the same letter.
if (aff_entry->ae_chop != NULL
&& aff_entry->ae_add != NULL
- && aff_entry->ae_chop[utfc_ptr2len(aff_entry->ae_chop)] ==
+ && aff_entry->ae_chop[utfc_ptr2len((char *)aff_entry->ae_chop)] ==
NUL) {
int c, c_up;
- c = utf_ptr2char(aff_entry->ae_chop);
+ c = utf_ptr2char((char *)aff_entry->ae_chop);
c_up = SPELL_TOUPPER(c);
if (c_up != c
&& (aff_entry->ae_cond == NULL
- || utf_ptr2char(aff_entry->ae_cond) == c)) {
+ || utf_ptr2char((char *)aff_entry->ae_cond) == c)) {
p = aff_entry->ae_add
+ STRLEN(aff_entry->ae_add);
MB_PTR_BACK(aff_entry->ae_add, p);
- if (utf_ptr2char(p) == c_up) {
+ if (utf_ptr2char((char *)p) == c_up) {
upper = true;
aff_entry->ae_chop = NULL;
*p = NUL;
@@ -2532,7 +2530,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
sprintf((char *)buf, "^%s",
aff_entry->ae_cond);
vim_regfree(aff_entry->ae_prog);
- aff_entry->ae_prog = vim_regcomp(buf, RE_MAGIC + RE_STRING);
+ aff_entry->ae_prog = vim_regcomp((char *)buf, RE_MAGIC + RE_STRING);
}
}
}
@@ -2651,7 +2649,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
if ((!GA_EMPTY(&spin->si_map)
&& vim_strchr(spin->si_map.ga_data, c)
!= NULL)
- || vim_strchr(p, c) != NULL) {
+ || vim_strchr((char *)p, c) != NULL) {
smsg(_("Duplicate character in MAP in %s line %d"),
fname, lnum);
}
@@ -2690,9 +2688,8 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
} else if (STRCMP(items[0], "COMMON") == 0) {
int i;
- for (i = 1; i < itemcnt; ++i) {
- if (HASHITEM_EMPTY(hash_find(&spin->si_commonwords,
- items[i]))) {
+ for (i = 1; i < itemcnt; i++) {
+ if (HASHITEM_EMPTY(hash_find(&spin->si_commonwords, (char *)items[i]))) {
p = vim_strsave(items[i]);
hash_add(&spin->si_commonwords, p);
}
@@ -2874,7 +2871,7 @@ static unsigned get_affitem(int flagtype, char_u **pp)
++*pp; // always advance, avoid getting stuck
return 0;
}
- res = getdigits_int(pp, true, 0);
+ res = getdigits_int((char **)pp, true, 0);
if (res == 0) {
res = ZERO_FLAG;
}
@@ -2923,7 +2920,7 @@ static void process_compflags(spellinfo_T *spin, afffile_T *aff, char_u *compfla
tp = p + STRLEN(p);
for (p = compflags; *p != NUL;) {
- if (vim_strchr((char_u *)"/?*+[]", *p) != NULL) {
+ if (vim_strchr("/?*+[]", *p) != NULL) {
// Copy non-flag characters directly.
*tp++ = *p++;
} else {
@@ -2934,7 +2931,7 @@ static void process_compflags(spellinfo_T *spin, afffile_T *aff, char_u *compfla
// Find the flag in the hashtable. If it was used before, use
// the existing ID. Otherwise add a new entry.
STRLCPY(key, prevp, p - prevp + 1);
- hi = hash_find(&aff->af_comp, key);
+ hi = hash_find(&aff->af_comp, (char *)key);
if (!HASHITEM_EMPTY(hi)) {
id = HI2CI(hi)->ci_newID;
} else {
@@ -2946,7 +2943,7 @@ static void process_compflags(spellinfo_T *spin, afffile_T *aff, char_u *compfla
do {
check_renumber(spin);
id = spin->si_newcompID--;
- } while (vim_strchr((char_u *)"/?*+[]\\-^", id) != NULL);
+ } while (vim_strchr("/?*+[]\\-^", id) != NULL);
ci->ci_newID = id;
hash_add(&aff->af_comp, ci->ci_key);
}
@@ -2981,7 +2978,7 @@ static bool flag_in_afflist(int flagtype, char_u *afflist, unsigned flag)
switch (flagtype) {
case AFT_CHAR:
- return vim_strchr(afflist, flag) != NULL;
+ return vim_strchr((char *)afflist, flag) != NULL;
case AFT_CAPLONG:
case AFT_LONG:
@@ -2999,7 +2996,7 @@ static bool flag_in_afflist(int flagtype, char_u *afflist, unsigned flag)
case AFT_NUM:
for (p = afflist; *p != NUL;) {
- int digits = getdigits_int(&p, true, 0);
+ int digits = getdigits_int((char **)&p, true, 0);
assert(digits >= 0);
n = (unsigned int)digits;
if (n == 0) {
@@ -3141,7 +3138,7 @@ static int spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile)
spin->si_msg_count = 999999;
// Read and ignore the first line: word count.
- if (vim_fgets(line, MAXLINELEN, fd) || !ascii_isdigit(*skipwhite(line))) {
+ if (vim_fgets(line, MAXLINELEN, fd) || !ascii_isdigit(*skipwhite((char *)line))) {
semsg(_("E760: No word count in %s"), fname);
}
@@ -3361,7 +3358,7 @@ static int get_pfxlist(afffile_T *affile, char_u *afflist, char_u *store_afflist
// A flag is a postponed prefix flag if it appears in "af_pref"
// and its ID is not zero.
STRLCPY(key, prevp, p - prevp + 1);
- hi = hash_find(&affile->af_pref, key);
+ hi = hash_find(&affile->af_pref, (char *)key);
if (!HASHITEM_EMPTY(hi)) {
id = HI2AH(hi)->ah_newID;
if (id != 0) {
@@ -3394,7 +3391,7 @@ static void get_compflags(afffile_T *affile, char_u *afflist, char_u *store_affl
if (get_affitem(affile->af_flagtype, &p) != 0) {
// A flag is a compound flag if it appears in "af_comp".
STRLCPY(key, prevp, p - prevp + 1);
- hi = hash_find(&affile->af_comp, key);
+ hi = hash_find(&affile->af_comp, (char *)key);
if (!HASHITEM_EMPTY(hi)) {
store_afflist[cnt++] = HI2CI(hi)->ci_newID;
}
@@ -3782,7 +3779,7 @@ static int spell_read_wordfile(spellinfo_T *spin, char_u *fname)
regionmask = spin->si_region;
// Check for flags and region after a slash.
- p = vim_strchr(line, '/');
+ p = (char_u *)vim_strchr((char *)line, '/');
if (p != NULL) {
*p++ = NUL;
while (*p != NUL) {
@@ -3886,7 +3883,6 @@ static char_u *getroom_save(spellinfo_T *spin, char_u *s)
return memcpy(getroom(spin, s_size, false), s, s_size);
}
-
// Free the list of allocated sblock_T.
static void free_blocks(sblock_T *bl)
{
@@ -3907,6 +3903,21 @@ static wordnode_T *wordtree_alloc(spellinfo_T *spin)
return (wordnode_T *)getroom(spin, sizeof(wordnode_T), true);
}
+/// Return true if "word" contains valid word characters.
+/// Control characters and trailing '/' are invalid. Space is OK.
+static bool valid_spell_word(const char_u *word, const char_u *end)
+{
+ if (!utf_valid_string(word, end)) {
+ return false;
+ }
+ for (const char_u *p = word; *p != NUL && p < end; p += utfc_ptr2len((const char *)p)) {
+ if (*p < ' ' || (p[0] == '/' && p[1] == NUL)) {
+ return false;
+ }
+ }
+ return true;
+}
+
/// Store a word in the tree(s).
/// Always store it in the case-folded tree. For a keep-case word this is
/// useful when the word can also be used with all caps (no WF_FIXCAP flag) and
@@ -3927,6 +3938,11 @@ static int store_word(spellinfo_T *spin, char_u *word, int flags, int region, co
char_u foldword[MAXWLEN];
int res = OK;
+ // Avoid adding illegal bytes to the word tree.
+ if (!valid_spell_word(word, word + len)) {
+ return FAIL;
+ }
+
(void)spell_casefold(curwin, word, len, foldword, MAXWLEN);
for (const char_u *p = pfxlist; res == OK; p++) {
if (!need_affix || (p != NULL && *p != NUL)) {
@@ -4333,7 +4349,6 @@ static bool node_equal(wordnode_T *n1, wordnode_T *n2)
return p1 == NULL && p2 == NULL;
}
-
// Function given to qsort() to sort the REP items on "from" string.
static int rep_compare(const void *s1, const void *s2)
{
@@ -4401,7 +4416,7 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname)
// Also skip this for an .add.spl file, the main spell file must contain
// the table (avoids that it conflicts). File is shorter too.
if (!spin->si_ascii && !spin->si_add) {
- char_u folchars[128 * 8];
+ char folchars[128 * 8];
int flags;
putc(SN_CHARFLAGS, fd); // <sectionID>
@@ -4410,7 +4425,7 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname)
// Form the <folchars> string first, we need to know its length.
size_t l = 0;
for (size_t i = 128; i < 256; i++) {
- l += (size_t)utf_char2bytes(spelltab.st_fold[i], folchars + l);
+ l += (size_t)utf_char2bytes(spelltab.st_fold[i], (char *)folchars + l);
}
put_bytes(fd, 1 + 128 + 2 + l, 4); // <sectionlen>
@@ -4680,7 +4695,6 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname)
// end of <SECTIONS>
putc(SN_END, fd); // <sectionend>
-
// <LWORDTREE> <KWORDTREE> <PREFIXTREE>
spin->si_memtot = 0;
for (unsigned int round = 1; round <= 3; ++round) {
@@ -4748,7 +4762,6 @@ static void clear_node(wordnode_T *node)
}
}
-
/// Dump a word tree at node "node".
///
/// This first writes the list of possible bytes (siblings). Then for each
@@ -4867,19 +4880,18 @@ static int put_node(FILE *fd, wordnode_T *node, int idx, int regionmask, bool pr
return newindex;
}
-
// ":mkspell [-ascii] outfile infile ..."
// ":mkspell [-ascii] addfile"
void ex_mkspell(exarg_T *eap)
{
int fcount;
char_u **fnames;
- char_u *arg = eap->arg;
+ char_u *arg = (char_u *)eap->arg;
bool ascii = false;
if (STRNCMP(arg, "-ascii", 6) == 0) {
ascii = true;
- arg = skipwhite(arg + 6);
+ arg = (char_u *)skipwhite((char *)arg + 6);
}
// Expand all the remaining arguments (e.g., $VIMRUNTIME).
@@ -4905,7 +4917,7 @@ static void spell_make_sugfile(spellinfo_T *spin, char_u *wfname)
// of the code for the soundfolding stuff.
// It might have been done already by spell_reload_one().
for (slang = first_lang; slang != NULL; slang = slang->sl_next) {
- if (path_full_compare(wfname, slang->sl_fname, false, true)
+ if (path_full_compare((char *)wfname, (char *)slang->sl_fname, false, true)
== kEqualFiles) {
break;
}
@@ -5255,7 +5267,6 @@ theend:
fclose(fd);
}
-
/// Create a Vim spell file from one or more word lists.
/// "fnames[0]" is the output file name.
/// "fnames[fcount - 1]" is the last input file name.
@@ -5320,19 +5331,19 @@ static void mkspell(int fcount, char_u **fnames, bool ascii, bool over_write, bo
}
// Check for .ascii.spl.
- if (strstr((char *)path_tail(wfname), SPL_FNAME_ASCII) != NULL) {
+ if (strstr(path_tail((char *)wfname), SPL_FNAME_ASCII) != NULL) {
spin.si_ascii = true;
}
// Check for .add.spl.
- if (strstr((char *)path_tail(wfname), SPL_FNAME_ADD) != NULL) {
+ if (strstr(path_tail((char *)wfname), SPL_FNAME_ADD) != NULL) {
spin.si_add = true;
}
}
if (incount <= 0) {
emsg(_(e_invarg)); // need at least output and input names
- } else if (vim_strchr(path_tail(wfname), '_') != NULL) {
+ } else if (vim_strchr(path_tail((char *)wfname), '_') != NULL) {
emsg(_("E751: Output file name must not have region name"));
} else if (incount > MAXREGIONS) {
semsg(_("E754: Only up to %d regions supported"), MAXREGIONS);
@@ -5357,7 +5368,7 @@ static void mkspell(int fcount, char_u **fnames, bool ascii, bool over_write, bo
if (incount > 1) {
len = (int)STRLEN(innames[i]);
- if (STRLEN(path_tail(innames[i])) < 5
+ if (STRLEN(path_tail((char *)innames[i])) < 5
|| innames[i][len - 3] != '_') {
semsg(_("E755: Invalid region in %s"), innames[i]);
goto theend;
@@ -5501,7 +5512,7 @@ static void spell_message(const spellinfo_T *spin, char_u *str)
// ":[count]spellrare {word}"
void ex_spell(exarg_T *eap)
{
- spell_add_word(eap->arg, (int)STRLEN(eap->arg),
+ spell_add_word((char_u *)eap->arg, (int)STRLEN(eap->arg),
eap->cmdidx == CMD_spellwrong ? SPELL_ADD_BAD :
eap->cmdidx == CMD_spellrare ? SPELL_ADD_RARE : SPELL_ADD_GOOD,
eap->forceit ? 0 : (int)eap->line2,
@@ -5525,6 +5536,11 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo
int i;
char_u *spf;
+ if (!valid_spell_word(word, word + len)) {
+ emsg(_(e_illegal_character_in_word));
+ return;
+ }
+
if (idx == 0) { // use internal wordlist
if (int_wordlist == NULL) {
int_wordlist = vim_tempname();
@@ -5546,8 +5562,8 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo
}
fnamebuf = xmalloc(MAXPATHL);
- for (spf = curwin->w_s->b_p_spf, i = 1; *spf != NUL; ++i) {
- copy_option_part(&spf, fnamebuf, MAXPATHL, ",");
+ for (spf = curwin->w_s->b_p_spf, i = 1; *spf != NUL; i++) {
+ copy_option_part((char **)&spf, (char *)fnamebuf, MAXPATHL, ",");
if (i == idx) {
break;
}
@@ -5559,7 +5575,7 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo
}
// Check that the user isn't editing the .add file somewhere.
- buf = buflist_findname_exp(fnamebuf);
+ buf = buflist_findname_exp((char *)fnamebuf);
if (buf != NULL && buf->b_ml.ml_mfp == NULL) {
buf = NULL;
}
@@ -5580,6 +5596,9 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo
while (!vim_fgets(line, MAXWLEN * 2, fd)) {
fpos = fpos_next;
fpos_next = ftell(fd);
+ if (fpos_next < 0) {
+ break; // should never happen
+ }
if (STRNCMP(word, line, len) == 0
&& (line[len] == '/' || line[len] < ' ')) {
// Found duplicate word. Remove it by writing a '#' at
@@ -5593,9 +5612,8 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo
if (fseek(fd, fpos, SEEK_SET) == 0) {
fputc('#', fd);
if (undo) {
- home_replace(NULL, fname, NameBuff, MAXPATHL, TRUE);
- smsg(_("Word '%.*s' removed from %s"),
- len, word, NameBuff);
+ home_replace(NULL, (char *)fname, (char *)NameBuff, MAXPATHL, true);
+ smsg(_("Word '%.*s' removed from %s"), len, word, NameBuff);
}
}
if (fseek(fd, fpos_next, SEEK_SET) != 0) {
@@ -5619,7 +5637,8 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo
// file. We may need to create the "spell" directory first. We
// already checked the runtime directory is writable in
// init_spellfile().
- if (!dir_of_file_exists(fname) && (p = path_tail_with_sep(fname)) != fname) {
+ if (!dir_of_file_exists(fname)
+ && (p = (char_u *)path_tail_with_sep((char *)fname)) != fname) {
int c = *p;
// The directory doesn't exist. Try creating it and opening
@@ -5643,7 +5662,7 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo
}
fclose(fd);
- home_replace(NULL, fname, NameBuff, MAXPATHL, TRUE);
+ home_replace(NULL, (char *)fname, (char *)NameBuff, MAXPATHL, true);
smsg(_("Word '%.*s' added to %s"), len, word, NameBuff);
}
}
@@ -5654,7 +5673,7 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo
// If the .add file is edited somewhere, reload it.
if (buf != NULL) {
- buf_reload(buf, buf->b_orig_mode);
+ buf_reload(buf, buf->b_orig_mode, false);
}
redraw_all_later(SOME_VALID);
@@ -5679,7 +5698,7 @@ static void init_spellfile(void)
// Find the end of the language name. Exclude the region. If there
// is a path separator remember the start of the tail.
for (lend = curwin->w_s->b_p_spl; *lend != NUL
- && vim_strchr((char_u *)",._", *lend) == NULL; ++lend) {
+ && vim_strchr(",._", *lend) == NULL; lend++) {
if (vim_ispathsep(*lend)) {
aspath = true;
lstart = lend + 1;
@@ -5697,7 +5716,7 @@ static void init_spellfile(void)
lstart - curbuf->b_s.b_p_spl);
} else {
// Copy the path from 'runtimepath' to buf[].
- copy_option_part(&rtp, buf, MAXPATHL, ",");
+ copy_option_part((char **)&rtp, (char *)buf, MAXPATHL, ",");
}
if (os_file_is_writable((char *)buf) == 2) {
// Use the first language name from 'spelllang' and the
@@ -5722,7 +5741,7 @@ static void init_spellfile(void)
->lp_slang->sl_fname;
vim_snprintf((char *)buf + l, MAXPATHL - l, ".%s.add",
((fname != NULL
- && strstr((char *)path_tail(fname), ".ascii.") != NULL)
+ && strstr(path_tail((char *)fname), ".ascii.") != NULL)
? "ascii"
: (const char *)spell_enc()));
set_option_value("spellfile", 0L, (const char *)buf, OPT_LOCAL);
@@ -5859,7 +5878,7 @@ static void set_map_str(slang_T *lp, char_u *map)
if (c >= 256) {
int cl = utf_char2len(c);
int headcl = utf_char2len(headc);
- char_u *b;
+ char *b;
hash_T hash;
hashitem_T *hi;
@@ -5868,10 +5887,10 @@ static void set_map_str(slang_T *lp, char_u *map)
b[cl] = NUL;
utf_char2bytes(headc, b + cl + 1);
b[cl + 1 + headcl] = NUL;
- hash = hash_hash(b);
+ hash = hash_hash((char_u *)b);
hi = hash_lookup(&lp->sl_map_hash, (const char *)b, STRLEN(b), hash);
if (HASHITEM_EMPTY(hi)) {
- hash_add_item(&lp->sl_map_hash, hi, b, hash);
+ hash_add_item(&lp->sl_map_hash, hi, (char_u *)b, hash);
} else {
// This should have been checked when generating the .spl
// file.
@@ -5884,4 +5903,3 @@ static void set_map_str(slang_T *lp, char_u *map)
}
}
}
-
diff --git a/src/nvim/state.c b/src/nvim/state.c
index 1fe8bb671d..6475105192 100644
--- a/src/nvim/state.c
+++ b/src/nvim/state.c
@@ -12,8 +12,10 @@
#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/screen.h"
#include "nvim/state.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
@@ -22,7 +24,6 @@
# include "state.c.generated.h"
#endif
-
void state_enter(VimState *s)
{
for (;;) {
@@ -38,15 +39,27 @@ void state_enter(VimState *s)
int key;
getkey:
- if (char_avail() || using_script() || input_available()) {
- // Don't block for events if there's a character already available for
- // processing. Characters can come from mappings, scripts and other
- // sources, so this scenario is very common.
+ // Apply mappings first by calling vpeekc() directly.
+ // - If vpeekc() returns non-NUL, there is a character already available for processing, so
+ // don't block for events. vgetc() may still block, in case of an incomplete UTF-8 sequence.
+ // - If vpeekc() returns NUL, vgetc() will block, and there are three cases:
+ // - There is no input available.
+ // - All of available input maps to an empty string.
+ // - There is an incomplete mapping.
+ // A blocking wait for a character should only be done in the third case, which is the only
+ // case of the three where typebuf.tb_len > 0 after vpeekc() returns NUL.
+ if (vpeekc() != NUL || typebuf.tb_len > 0) {
key = safe_vgetc();
} else if (!multiqueue_empty(main_loop.events)) {
// Event was made available after the last multiqueue_process_events call
key = K_EVENT;
} else {
+ // Duplicate display updating logic in vgetorpeek()
+ if (((State & MODE_INSERT) != 0 || p_lz) && (State & MODE_CMDLINE) == 0
+ && must_redraw != 0 && !need_wait_return) {
+ update_screen(0);
+ setcursor(); // put cursor back where it belongs
+ }
// Flush screen updates before blocking
ui_flush();
// Call `os_inchar` directly to block for events or user input without
@@ -54,17 +67,22 @@ getkey:
// mapping engine.
(void)os_inchar(NULL, 0, -1, 0, main_loop.events);
// If an event was put into the queue, we send K_EVENT directly.
- key = !multiqueue_empty(main_loop.events)
- ? K_EVENT
- : safe_vgetc();
+ if (!multiqueue_empty(main_loop.events)) {
+ key = K_EVENT;
+ } else {
+ goto getkey;
+ }
}
if (key == K_EVENT) {
+ // An event handler may use the value of reg_executing.
+ // Clear it if it should be cleared when getting the next character.
+ check_end_reg_executing(true);
may_sync_undo();
}
-#if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL
- log_key(DEBUG_LOG_LEVEL, key);
+#if MIN_LOG_LEVEL <= LOGLVL_DBG
+ log_key(LOGLVL_DBG, key);
#endif
int execute_result = s->execute(s, key);
@@ -103,134 +121,134 @@ 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 & MODE_INSERT));
}
-/// VISUAL, SELECTMODE and OP_PENDING State are never set, they are equal to
-/// NORMAL State with a condition. This function returns the real State.
+/// MODE_VISUAL, MODE_SELECT and MODE_OP_PENDING State are never set, they are
+/// equal to MODE_NORMAL State with a condition. This function returns the real
+/// State.
int get_real_state(void)
{
- if (State & NORMAL) {
+ if (State & MODE_NORMAL) {
if (VIsual_active) {
if (VIsual_select) {
- return SELECTMODE;
+ return MODE_SELECT;
}
- return VISUAL;
+ return MODE_VISUAL;
} else if (finish_op) {
- return OP_PENDING;
+ return MODE_OP_PENDING;
}
}
return State;
}
-/// @returns[allocated] mode string
-char *get_mode(void)
+/// Returns the current mode as a string in "buf[MODE_MAX_LENGTH]", NUL
+/// terminated.
+/// The first character represents the major mode, the following ones the minor
+/// ones.
+void get_mode(char *buf)
{
- char *buf = xcalloc(MODE_MAX_LENGTH, sizeof(char));
+ int i = 0;
if (VIsual_active) {
if (VIsual_select) {
- buf[0] = (char)(VIsual_mode + 's' - 'v');
+ buf[i++] = (char)(VIsual_mode + 's' - 'v');
} else {
- buf[0] = (char)VIsual_mode;
+ buf[i++] = (char)VIsual_mode;
if (restart_VIsual_select) {
- buf[1] = 's';
+ buf[i++] = 's';
}
}
- } else if (State == HITRETURN || State == ASKMORE || State == SETWSIZE
- || State == CONFIRM) {
- buf[0] = 'r';
- if (State == ASKMORE) {
- buf[1] = 'm';
- } else if (State == CONFIRM) {
- buf[1] = '?';
+ } else if (State == MODE_HITRETURN || State == MODE_ASKMORE || State == MODE_SETWSIZE
+ || State == MODE_CONFIRM) {
+ buf[i++] = 'r';
+ if (State == MODE_ASKMORE) {
+ buf[i++] = 'm';
+ } else if (State == MODE_CONFIRM) {
+ buf[i++] = '?';
}
- } else if (State == EXTERNCMD) {
- buf[0] = '!';
- } else if (State & INSERT) {
+ } else if (State == MODE_EXTERNCMD) {
+ buf[i++] = '!';
+ } else if (State & MODE_INSERT) {
if (State & VREPLACE_FLAG) {
- buf[0] = 'R';
- buf[1] = 'v';
- if (ins_compl_active()) {
- buf[2] = 'c';
- } else if (ctrl_x_mode_not_defined_yet()) {
- buf[2] = 'x';
- }
+ buf[i++] = 'R';
+ buf[i++] = 'v';
} else {
if (State & REPLACE_FLAG) {
- buf[0] = 'R';
+ buf[i++] = 'R';
} else {
- buf[0] = 'i';
- }
- if (ins_compl_active()) {
- buf[1] = 'c';
- } else if (ctrl_x_mode_not_defined_yet()) {
- buf[1] = 'x';
+ buf[i++] = 'i';
}
}
- } else if ((State & CMDLINE) || exmode_active) {
- buf[0] = 'c';
+ if (ins_compl_active()) {
+ buf[i++] = 'c';
+ } else if (ctrl_x_mode_not_defined_yet()) {
+ buf[i++] = 'x';
+ }
+ } else if ((State & MODE_CMDLINE) || exmode_active) {
+ buf[i++] = 'c';
if (exmode_active) {
- buf[1] = 'v';
+ buf[i++] = 'v';
}
- } else if (State & TERM_FOCUS) {
- buf[0] = 't';
+ } else if (State & MODE_TERMINAL) {
+ buf[i++] = 't';
} else {
- buf[0] = 'n';
+ buf[i++] = 'n';
if (finish_op) {
- buf[1] = 'o';
+ buf[i++] = 'o';
// to be able to detect force-linewise/blockwise/charwise operations
- buf[2] = (char)motion_force;
+ buf[i++] = (char)motion_force;
} else if (restart_edit == 'I' || restart_edit == 'R'
|| restart_edit == 'V') {
- buf[1] = 'i';
- buf[2] = (char)restart_edit;
+ buf[i++] = 'i';
+ buf[i++] = (char)restart_edit;
} else if (curbuf->terminal) {
- buf[1] = 't';
+ buf[i++] = 't';
}
}
- return buf;
+ buf[i] = NUL;
}
-/// Fires a ModeChanged autocmd.
-void trigger_modechanged(void)
+/// Fires a ModeChanged autocmd if appropriate.
+void may_trigger_modechanged(void)
{
if (!has_event(EVENT_MODECHANGED)) {
return;
}
- char *mode = get_mode();
- if (STRCMP(mode, last_mode) == 0) {
- xfree(mode);
+ char curr_mode[MODE_MAX_LENGTH];
+ char pattern_buf[2 * MODE_MAX_LENGTH];
+
+ get_mode(curr_mode);
+ if (STRCMP(curr_mode, last_mode) == 0) {
return;
}
save_v_event_T save_v_event;
dict_T *v_event = get_v_event(&save_v_event);
- tv_dict_add_str(v_event, S_LEN("new_mode"), mode);
+ tv_dict_add_str(v_event, S_LEN("new_mode"), curr_mode);
tv_dict_add_str(v_event, S_LEN("old_mode"), last_mode);
+ tv_dict_set_keys_readonly(v_event);
- char_u *pat_pre = concat_str((char_u *)last_mode, (char_u *)":");
- char_u *pat = concat_str(pat_pre, (char_u *)mode);
- xfree(pat_pre);
+ // concatenate modes in format "old_mode:new_mode"
+ vim_snprintf(pattern_buf, sizeof(pattern_buf), "%s:%s", last_mode, curr_mode);
- apply_autocmds(EVENT_MODECHANGED, pat, NULL, false, curbuf);
- xfree(last_mode);
- last_mode = mode;
+ apply_autocmds(EVENT_MODECHANGED, pattern_buf, NULL, false, curbuf);
+ STRCPY(last_mode, curr_mode);
- xfree(pat);
restore_v_event(v_event, &save_v_event);
}
diff --git a/src/nvim/strings.c b/src/nvim/strings.c
index e2a8108c45..5c2721536d 100644
--- a/src/nvim/strings.c
+++ b/src/nvim/strings.c
@@ -67,6 +67,13 @@ char_u *vim_strnsave(const char_u *string, size_t len)
return (char_u *)strncpy(xmallocz(len), (char *)string, len);
}
+/// A clone of vim_strnsave() that uses char* instead of char_u*
+char *xstrnsave(const char *string, size_t len)
+ FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL
+{
+ return strncpy(xmallocz(len), string, len); // NOLINT(runtime/printf)
+}
+
/*
* Same as vim_strsave(), but any characters found in esc_chars are preceded
* by a backslash.
@@ -91,14 +98,14 @@ char_u *vim_strsave_escaped_ext(const char_u *string, const char_u *esc_chars, c
*/
size_t length = 1; // count the trailing NUL
for (const char_u *p = string; *p; p++) {
- const size_t l = (size_t)(utfc_ptr2len(p));
+ const size_t l = (size_t)(utfc_ptr2len((char *)p));
if (l > 1) {
length += l; // count a multibyte char
p += l - 1;
continue;
}
- if (vim_strchr(esc_chars, *p) != NULL || (bsl && rem_backslash(p))) {
- ++length; // count a backslash
+ if (vim_strchr((char *)esc_chars, *p) != NULL || (bsl && rem_backslash(p))) {
+ length++; // count a backslash
}
++length; // count an ordinary char
}
@@ -106,14 +113,14 @@ char_u *vim_strsave_escaped_ext(const char_u *string, const char_u *esc_chars, c
char_u *escaped_string = xmalloc(length);
char_u *p2 = escaped_string;
for (const char_u *p = string; *p; p++) {
- const size_t l = (size_t)(utfc_ptr2len(p));
+ const size_t l = (size_t)(utfc_ptr2len((char *)p));
if (l > 1) {
memcpy(p2, p, l);
p2 += l;
p += l - 1; // skip multibyte char
continue;
}
- if (vim_strchr(esc_chars, *p) != NULL || (bsl && rem_backslash(p))) {
+ if (vim_strchr((char *)esc_chars, *p) != NULL || (bsl && rem_backslash(p))) {
*p2++ = cc;
}
*p2++ = *p;
@@ -139,7 +146,7 @@ char *vim_strnsave_unquoted(const char *const string, const size_t length)
FUNC_ATTR_NONNULL_RET
{
#define ESCAPE_COND(p, inquote, string_end) \
- (*p == '\\' && inquote && p + 1 < string_end && (p[1] == '\\' || p[1] == '"'))
+ (*(p) == '\\' && (inquote) && (p) + 1 < (string_end) && ((p)[1] == '\\' || (p)[1] == '"'))
size_t ret_length = 0;
bool inquote = false;
const char *const string_end = string + length;
@@ -350,11 +357,11 @@ char *strcase_save(const char *const orig, bool upper)
char *p = res;
while (*p != NUL) {
- int c = utf_ptr2char((const char_u *)p);
- int l = utf_ptr2len((const char_u *)p);
+ int c = utf_ptr2char(p);
+ int l = utf_ptr2len(p);
if (c == 0) {
// overlong sequence, use only the first byte
- c = *p;
+ c = (char_u)(*p);
l = 1;
}
int uc = upper ? mb_toupper(c) : mb_tolower(c);
@@ -372,7 +379,7 @@ char *strcase_save(const char *const orig, bool upper)
res = s;
}
- utf_char2bytes(uc, (char_u *)p);
+ utf_char2bytes(uc, p);
p += newl;
}
@@ -401,7 +408,7 @@ size_t xstrnlen(const char *s, size_t n)
if (end == NULL) {
return n;
}
- return end - s;
+ return (size_t)(end - s);
}
#endif
@@ -466,18 +473,18 @@ int vim_strnicmp(const char *s1, const char *s2, size_t len)
/// @return Pointer to the first byte of the found character in string or NULL
/// if it was not found or character is invalid. NUL character is never
/// found, use `strlen()` instead.
-char_u *vim_strchr(const char_u *const string, const int c)
+char *vim_strchr(const char *const string, const int c)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
if (c <= 0) {
return NULL;
} else if (c < 0x80) {
- return (char_u *)strchr((const char *)string, c);
+ return strchr(string, c);
} else {
char u8char[MB_MAXBYTES + 1];
- const int len = utf_char2bytes(c, (char_u *)u8char);
+ const int len = utf_char2bytes(c, u8char);
u8char[len] = NUL;
- return (char_u *)strstr((const char *)string, u8char);
+ return strstr(string, u8char);
}
}
@@ -546,7 +553,6 @@ char_u *concat_str(const char_u *restrict str1, const char_u *restrict str2)
return dest;
}
-
static const char *const e_printf =
N_("E766: Insufficient arguments for printf()");
@@ -627,7 +633,7 @@ static const void *tv_ptr(const typval_T *const tvs, int *const idxp)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
#define OFF(attr) offsetof(union typval_vval_union, attr)
- STATIC_ASSERT(OFF(v_string) == OFF(v_list)
+ STATIC_ASSERT(OFF(v_string) == OFF(v_list) // -V568
&& OFF(v_string) == OFF(v_dict)
&& OFF(v_string) == OFF(v_partial)
&& sizeof(tvs[0].vval.v_string) == sizeof(tvs[0].vval.v_list)
@@ -1001,22 +1007,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((char *)p1)) {
+ size_t cell = (size_t)utf_ptr2cells((char *)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;
@@ -1308,8 +1312,7 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
if (fmt_spec == 'f' || fmt_spec == 'F') {
tp = tmp + str_arg_l - 1;
} else {
- tp = (char *)vim_strchr((char_u *)tmp,
- fmt_spec == 'e' ? 'e' : 'E');
+ tp = vim_strchr(tmp, fmt_spec == 'e' ? 'e' : 'E');
if (tp) {
// remove superfluous '+' and leading zeroes from exponent
if (tp[1] == '+') {
@@ -1338,8 +1341,7 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
} else {
// Be consistent: some printf("%e") use 1.0e+12 and some
// 1.0e+012; remove one zero in the last case.
- char *tp = (char *)vim_strchr((char_u *)tmp,
- fmt_spec == 'e' ? 'e' : 'E');
+ char *tp = vim_strchr(tmp, fmt_spec == 'e' ? 'e' : 'E');
if (tp && (tp[1] == '+' || tp[1] == '-') && tp[2] == '0'
&& ascii_isdigit(tp[3]) && ascii_isdigit(tp[4])) {
STRMOVE(tp + 2, tp + 3);
@@ -1475,3 +1477,54 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
// written to the buffer if it were large enough.
return (int)str_l;
}
+
+int kv_do_printf(StringBuilder *str, const char *fmt, ...)
+ FUNC_ATTR_PRINTF(2, 3)
+{
+ size_t remaining = str->capacity - str->size;
+
+ va_list ap;
+ va_start(ap, fmt);
+ int printed = vsnprintf(str->items ? str->items + str->size : NULL, remaining, fmt, ap);
+ va_end(ap);
+
+ if (printed < 0) {
+ return -1;
+ }
+
+ // printed string didn't fit, resize and try again
+ if ((size_t)printed >= remaining) {
+ kv_ensure_space(*str, (size_t)printed + 1); // include space for NUL terminator at the end
+ assert(str->items != NULL);
+ va_start(ap, fmt);
+ printed = vsnprintf(str->items + str->size, str->capacity - str->size, fmt, ap);
+ va_end(ap);
+ if (printed < 0) {
+ return -1;
+ }
+ }
+
+ str->size += (size_t)printed;
+ return printed;
+}
+
+/// Reverse text into allocated memory.
+///
+/// @return the allocated string.
+char_u *reverse_text(char_u *s)
+ FUNC_ATTR_NONNULL_RET
+{
+ // Reverse the pattern.
+ size_t len = STRLEN(s);
+ char_u *rev = xmalloc(len + 1);
+ size_t rev_i = len;
+ for (size_t s_i = 0; s_i < len; s_i++) {
+ const int mb_len = utfc_ptr2len((char *)s + s_i);
+ rev_i -= (size_t)mb_len;
+ memmove(rev + rev_i, s + s_i, (size_t)mb_len);
+ s_i += (size_t)mb_len - 1;
+ }
+ rev[len] = NUL;
+
+ return rev;
+}
diff --git a/src/nvim/strings.h b/src/nvim/strings.h
index 893b0ea269..9ef1eb5816 100644
--- a/src/nvim/strings.h
+++ b/src/nvim/strings.h
@@ -6,6 +6,7 @@
#include <string.h>
#include "nvim/eval/typval.h"
+#include "nvim/lib/kvec.h"
#include "nvim/types.h"
/// Append string to string and return pointer to the next byte
@@ -25,6 +26,8 @@ static inline char *strappend(char *const dst, const char *const src)
return (char *)memmove(dst, src, src_len) + src_len;
}
+typedef kvec_t(char) StringBuilder;
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "strings.h.generated.h"
#endif
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index a9447165c2..43dbeccf01 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -25,8 +25,10 @@
#include "nvim/garray.h"
#include "nvim/hashtab.h"
#include "nvim/highlight.h"
+#include "nvim/highlight_group.h"
#include "nvim/indent_c.h"
-#include "nvim/keymap.h"
+#include "nvim/keycodes.h"
+#include "nvim/lua/executor.h"
#include "nvim/macros.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
@@ -50,56 +52,6 @@
static bool did_syntax_onoff = false;
-/// Structure that stores information about a highlight group.
-/// The ID of a highlight group is also called group ID. It is the index in
-/// the highlight_ga array PLUS ONE.
-typedef struct hl_group {
- char_u *sg_name; ///< highlight group name
- char *sg_name_u; ///< uppercase of sg_name
- bool sg_cleared; ///< "hi clear" was used
- int sg_attr; ///< Screen attr @see ATTR_ENTRY
- int sg_link; ///< link to this highlight group ID
- int sg_deflink; ///< default link; restored in highlight_clear()
- int sg_set; ///< combination of flags in \ref SG_SET
- sctx_T sg_deflink_sctx; ///< script where the default link was set
- sctx_T sg_script_ctx; ///< script in which the group was last set
- // for terminal UIs
- int sg_cterm; ///< "cterm=" highlighting attr
- ///< (combination of \ref HlAttrFlags)
- int sg_cterm_fg; ///< terminal fg color number + 1
- int sg_cterm_bg; ///< terminal bg color number + 1
- bool sg_cterm_bold; ///< bold attr was set for light color
- // for RGB UIs
- int sg_gui; ///< "gui=" highlighting attributes
- ///< (combination of \ref HlAttrFlags)
- RgbValue sg_rgb_fg; ///< RGB foreground color
- RgbValue sg_rgb_bg; ///< RGB background color
- RgbValue sg_rgb_sp; ///< RGB special color
- char *sg_rgb_fg_name; ///< RGB foreground color name
- char *sg_rgb_bg_name; ///< RGB background color name
- char *sg_rgb_sp_name; ///< RGB special color name
-
- int sg_blend; ///< blend level (0-100 inclusive), -1 if unset
-} HlGroup;
-
-/// \addtogroup SG_SET
-/// @{
-#define SG_CTERM 2 // cterm has been set
-#define SG_GUI 4 // gui has been set
-#define SG_LINK 8 // link has been set
-/// @}
-
-// builtin |highlight-groups|
-static garray_T highlight_ga = GA_EMPTY_INIT_VALUE;
-Map(cstr_t, int) highlight_unames = MAP_INIT;
-
-static inline struct hl_group *HL_TABLE(void)
-{
- return ((struct hl_group *)((highlight_ga.ga_data)));
-}
-
-#define MAX_HL_ID 20000 // maximum value for a highlight ID.
-
// different types of offsets that are possible
#define SPO_MS_OFF 0 // match start offset
#define SPO_ME_OFF 1 // match end offset
@@ -110,22 +62,6 @@ static inline struct hl_group *HL_TABLE(void)
#define SPO_LC_OFF 6 // leading context offset
#define SPO_COUNT 7
-// Flags to indicate an additional string for highlight name completion.
-static int include_none = 0; // when 1 include "nvim/None"
-static int include_default = 0; // when 1 include "nvim/default"
-static int include_link = 0; // when 2 include "nvim/link" and "clear"
-
-#define MAX_SYN_NAME 200
-
-/// The "term", "cterm" and "gui" arguments can be any combination of the
-/// following names, separated by commas (but no spaces!).
-static char *(hl_name_table[]) =
-{ "bold", "standout", "underline", "undercurl",
- "italic", "reverse", "inverse", "strikethrough", "nocombine", "NONE" };
-static int hl_attr_table[] =
-{ HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE,
- HL_INVERSE, HL_STRIKETHROUGH, HL_NOCOMBINE, 0 };
-
static char e_illegal_arg[] = N_("E390: Illegal argument: %s");
// The patterns that are being searched for are stored in a syn_pattern.
@@ -159,7 +95,6 @@ typedef struct syn_pattern {
syn_time_T sp_time;
} synpat_T;
-
typedef struct syn_cluster_S {
char_u *scl_name; // syntax cluster name
char_u *scl_name_u; // uppercase of scl_name
@@ -242,10 +177,9 @@ static char *(spo_name_tab[SPO_COUNT]) =
#define SPTYPE_END 3 // match a regexp, end of item
#define SPTYPE_SKIP 4 // match a regexp, skip within item
-
#define SYN_ITEMS(buf) ((synpat_T *)((buf)->b_syn_patterns.ga_data))
-#define NONE_IDX -2 // value of sp_sync_idx for "NONE"
+#define NONE_IDX (-2) // value of sp_sync_idx for "NONE"
/*
* Flags for b_syn_sync_flags:
@@ -331,9 +265,9 @@ static int keepend_level = -1;
static char msg_no_items[] = N_("No Syntax items defined for this buffer");
// value of si_idx for keywords
-#define KEYWORD_IDX -1
+#define KEYWORD_IDX (-1)
// valid of si_cont_list for containing all but contained groups
-#define ID_LIST_ALL (int16_t *)-1
+#define ID_LIST_ALL ((int16_t *)-1)
static int next_seqnr = 1; // value to use for si_seqnr
@@ -1083,8 +1017,7 @@ static void syn_stack_alloc(void)
// When shrinking the array, cleanup the existing stack.
// Make sure that all valid entries fit in the new array.
while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
- && syn_stack_cleanup()) {
- }
+ && syn_stack_cleanup()) {}
if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2) {
len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
}
@@ -2159,7 +2092,6 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con
}
}
-
/*
* Check for end of current state (and the states before it) at the
* next column. Don't do this for syncing, because we would miss a
@@ -2205,7 +2137,6 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con
return current_attr;
}
-
/// @return true if we already matched pattern "idx" at the current column.
static bool did_match_already(int idx, garray_T *gap)
{
@@ -2353,55 +2284,52 @@ static void check_state_ends(void)
next_match_idx = 0;
next_match_col = MAXCOL;
break;
- } else {
- // handle next_list, unless at end of line and no "skipnl" or
- // "skipempty"
- current_next_list = cur_si->si_next_list;
- current_next_flags = cur_si->si_flags;
- if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
- && syn_getcurline()[current_col] == NUL) {
- current_next_list = NULL;
- }
+ }
+
+ // handle next_list, unless at end of line and no "skipnl" or
+ // "skipempty"
+ current_next_list = cur_si->si_next_list;
+ current_next_flags = cur_si->si_flags;
+ if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
+ && syn_getcurline()[current_col] == NUL) {
+ current_next_list = NULL;
+ }
- // When the ended item has "extend", another item with
- // "keepend" now needs to check for its end.
- had_extend = (cur_si->si_flags & HL_EXTEND);
+ // When the ended item has "extend", another item with
+ // "keepend" now needs to check for its end.
+ had_extend = (cur_si->si_flags & HL_EXTEND);
- pop_current_state();
+ pop_current_state();
+ if (GA_EMPTY(&current_state)) {
+ break;
+ }
+
+ if (had_extend && keepend_level >= 0) {
+ syn_update_ends(false);
if (GA_EMPTY(&current_state)) {
break;
}
+ }
- if (had_extend && keepend_level >= 0) {
- syn_update_ends(false);
- if (GA_EMPTY(&current_state)) {
- break;
- }
- }
-
- cur_si = &CUR_STATE(current_state.ga_len - 1);
+ cur_si = &CUR_STATE(current_state.ga_len - 1);
- /*
- * Only for a region the search for the end continues after
- * the end of the contained item. If the contained match
- * included the end-of-line, break here, the region continues.
- * Don't do this when:
- * - "keepend" is used for the contained item
- * - not at the end of the line (could be end="x$"me=e-1).
- * - "excludenl" is used (HL_HAS_EOL won't be set)
- */
- if (cur_si->si_idx >= 0
- && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
- == SPTYPE_START
- && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND))) {
- update_si_end(cur_si, (int)current_col, true);
- check_keepend();
- if ((current_next_flags & HL_HAS_EOL)
- && keepend_level < 0
- && syn_getcurline()[current_col] == NUL) {
- break;
- }
+ // Only for a region the search for the end continues after
+ // the end of the contained item. If the contained match
+ // included the end-of-line, break here, the region continues.
+ // Don't do this when:
+ // - "keepend" is used for the contained item
+ // - not at the end of the line (could be end="x$"me=e-1).
+ // - "excludenl" is used (HL_HAS_EOL won't be set)
+ if (cur_si->si_idx >= 0
+ && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type == SPTYPE_START
+ && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND))) {
+ update_si_end(cur_si, (int)current_col, true);
+ check_keepend();
+ if ((current_next_flags & HL_HAS_EOL)
+ && keepend_level < 0
+ && syn_getcurline()[current_col] == NUL) {
+ break;
}
}
} else {
@@ -2453,8 +2381,6 @@ static void update_si_attr(int idx)
} else {
sip->si_attr = CUR_STATE(idx - 1).si_attr;
sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
- sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
- sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
if (sip->si_cont_list == NULL) {
sip->si_flags |= HL_TRANS_CONT;
sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
@@ -2766,8 +2692,7 @@ static void find_endpos(int idx, lpos_T *startpos, lpos_T *m_endpos, lpos_T *hl_
// Be careful not to jump over the NUL at the end-of-line
for (matchcol = regmatch.endpos[0].col;
matchcol < line_len && matchcol < pos.col;
- matchcol++) {
- }
+ matchcol++) {}
}
// if the skip pattern includes end-of-line, break here
@@ -3037,7 +2962,7 @@ static int check_keyword_id(char_u *const line, const int startcol, int *const e
char_u *const kwp = line + startcol;
int kwlen = 0;
do {
- kwlen += utfc_ptr2len(kwp + kwlen);
+ kwlen += utfc_ptr2len((char *)kwp + kwlen);
} while (vim_iswordp_buf(kwp + kwlen, syn_buf));
if (kwlen > MAXKEYWLEN) {
@@ -3080,7 +3005,7 @@ static int check_keyword_id(char_u *const line, const int startcol, int *const e
/// Accept a keyword at other levels only if it is in the contains list.
static keyentry_T *match_keyword(char_u *keyword, hashtab_T *ht, stateitem_T *cur_si)
{
- hashitem_T *hi = hash_find(ht, keyword);
+ hashitem_T *hi = hash_find(ht, (char *)keyword);
if (!HASHITEM_EMPTY(hi)) {
for (keyentry_T *kp = HI2KE(hi); kp != NULL; kp = kp->ke_next) {
if (current_next_list != 0
@@ -3101,10 +3026,10 @@ static keyentry_T *match_keyword(char_u *keyword, hashtab_T *ht, stateitem_T *cu
*/
static void syn_cmd_conceal(exarg_T *eap, int syncing)
{
- char_u *arg = eap->arg;
+ char_u *arg = (char_u *)eap->arg;
char_u *next;
- eap->nextcmd = find_nextcmd(arg);
+ eap->nextcmd = (char *)find_nextcmd(arg);
if (eap->skip) {
return;
}
@@ -3112,9 +3037,9 @@ static void syn_cmd_conceal(exarg_T *eap, int syncing)
next = skiptowhite(arg);
if (*arg == NUL) {
if (curwin->w_s->b_syn_conceal) {
- msg(_("syntax conceal on"));
+ msg("syntax conceal on");
} else {
- msg(_("syntax conceal off"));
+ msg("syntax conceal off");
}
} else if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2) {
curwin->w_s->b_syn_conceal = true;
@@ -3130,10 +3055,10 @@ static void syn_cmd_conceal(exarg_T *eap, int syncing)
*/
static void syn_cmd_case(exarg_T *eap, int syncing)
{
- char_u *arg = eap->arg;
+ char_u *arg = (char_u *)eap->arg;
char_u *next;
- eap->nextcmd = find_nextcmd(arg);
+ eap->nextcmd = (char *)find_nextcmd(arg);
if (eap->skip) {
return;
}
@@ -3141,9 +3066,9 @@ static void syn_cmd_case(exarg_T *eap, int syncing)
next = skiptowhite(arg);
if (*arg == NUL) {
if (curwin->w_s->b_syn_ic) {
- msg(_("syntax case ignore"));
+ msg("syntax case ignore");
} else {
- msg(_("syntax case match"));
+ msg("syntax case match");
}
} else if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5) {
curwin->w_s->b_syn_ic = false;
@@ -3157,10 +3082,10 @@ static void syn_cmd_case(exarg_T *eap, int syncing)
/// Handle ":syntax foldlevel" command.
static void syn_cmd_foldlevel(exarg_T *eap, int syncing)
{
- char_u *arg = eap->arg;
+ char_u *arg = (char_u *)eap->arg;
char_u *arg_end;
- eap->nextcmd = find_nextcmd(arg);
+ eap->nextcmd = (char *)find_nextcmd(arg);
if (eap->skip) {
return;
}
@@ -3168,9 +3093,9 @@ static void syn_cmd_foldlevel(exarg_T *eap, int syncing)
if (*arg == NUL) {
switch (curwin->w_s->b_syn_foldlevel) {
case SYNFLD_START:
- msg(_("syntax foldlevel start")); break;
+ msg("syntax foldlevel start"); break;
case SYNFLD_MINIMUM:
- msg(_("syntax foldlevel minimum")); break;
+ msg("syntax foldlevel minimum"); break;
default:
break;
}
@@ -3187,7 +3112,7 @@ static void syn_cmd_foldlevel(exarg_T *eap, int syncing)
return;
}
- arg = skipwhite(arg_end);
+ arg = (char_u *)skipwhite((char *)arg_end);
if (*arg != NUL) {
semsg(_(e_illegal_arg), arg);
}
@@ -3198,10 +3123,10 @@ static void syn_cmd_foldlevel(exarg_T *eap, int syncing)
*/
static void syn_cmd_spell(exarg_T *eap, int syncing)
{
- char_u *arg = eap->arg;
+ char_u *arg = (char_u *)eap->arg;
char_u *next;
- eap->nextcmd = find_nextcmd(arg);
+ eap->nextcmd = (char *)find_nextcmd(arg);
if (eap->skip) {
return;
}
@@ -3209,11 +3134,11 @@ static void syn_cmd_spell(exarg_T *eap, int syncing)
next = skiptowhite(arg);
if (*arg == NUL) {
if (curwin->w_s->b_syn_spell == SYNSPL_TOP) {
- msg(_("syntax spell toplevel"));
+ msg("syntax spell toplevel");
} else if (curwin->w_s->b_syn_spell == SYNSPL_NOTOP) {
- msg(_("syntax spell notoplevel"));
+ msg("syntax spell notoplevel");
} else {
- msg(_("syntax spell default"));
+ msg("syntax spell default");
}
} else if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8) {
curwin->w_s->b_syn_spell = SYNSPL_TOP;
@@ -3233,7 +3158,7 @@ static void syn_cmd_spell(exarg_T *eap, int syncing)
/// Handle ":syntax iskeyword" command.
static void syn_cmd_iskeyword(exarg_T *eap, int syncing)
{
- char_u *arg = eap->arg;
+ char_u *arg = (char_u *)eap->arg;
char_u save_chartab[32];
char_u *save_isk;
@@ -3241,14 +3166,14 @@ static void syn_cmd_iskeyword(exarg_T *eap, int syncing)
return;
}
- arg = skipwhite(arg);
+ arg = (char_u *)skipwhite((char *)arg);
if (*arg == NUL) {
msg_puts("\n");
if (curwin->w_s->b_syn_isk != empty_option) {
- msg_puts(_("syntax iskeyword "));
- msg_outtrans(curwin->w_s->b_syn_isk);
+ msg_puts("syntax iskeyword ");
+ msg_outtrans((char *)curwin->w_s->b_syn_isk);
} else {
- msg_outtrans((char_u *)_("syntax iskeyword not set"));
+ msg_outtrans(_("syntax iskeyword not set"));
}
} else {
if (STRNICMP(arg, "clear", 5) == 0) {
@@ -3405,11 +3330,11 @@ static void syn_clear_cluster(synblock_T *block, int i)
*/
static void syn_cmd_clear(exarg_T *eap, int syncing)
{
- char_u *arg = eap->arg;
+ char_u *arg = (char_u *)eap->arg;
char_u *arg_end;
int id;
- eap->nextcmd = find_nextcmd(arg);
+ eap->nextcmd = (char *)find_nextcmd(arg);
if (eap->skip) {
return;
}
@@ -3457,7 +3382,7 @@ static void syn_cmd_clear(exarg_T *eap, int syncing)
XFREE_CLEAR(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
}
} else {
- id = syn_name2id_len(arg, (int)(arg_end - arg));
+ id = syn_name2id_len((char *)arg, (int)(arg_end - arg));
if (id == 0) {
semsg(_(e_nogroup), arg);
break;
@@ -3465,7 +3390,7 @@ static void syn_cmd_clear(exarg_T *eap, int syncing)
syn_clear_one(id, syncing);
}
}
- arg = skipwhite(arg_end);
+ arg = (char_u *)skipwhite((char *)arg_end);
}
}
redraw_curbuf_later(SOME_VALID);
@@ -3509,7 +3434,7 @@ static void syn_cmd_on(exarg_T *eap, int syncing)
*/
static void syn_cmd_reset(exarg_T *eap, int syncing)
{
- eap->nextcmd = check_nextcmd(eap->arg);
+ eap->nextcmd = (char *)check_nextcmd((char_u *)eap->arg);
if (!eap->skip) {
init_highlight(true, true);
}
@@ -3534,7 +3459,7 @@ static void syn_cmd_off(exarg_T *eap, int syncing)
static void syn_cmd_onoff(exarg_T *eap, char *name)
FUNC_ATTR_NONNULL_ALL
{
- eap->nextcmd = check_nextcmd(eap->arg);
+ eap->nextcmd = (char *)check_nextcmd((char_u *)eap->arg);
if (!eap->skip) {
did_syntax_onoff = true;
char buf[100];
@@ -3548,7 +3473,7 @@ void syn_maybe_enable(void)
{
if (!did_syntax_onoff) {
exarg_T ea;
- ea.arg = (char_u *)"";
+ ea.arg = "";
ea.skip = false;
syn_cmd_on(&ea, false);
}
@@ -3559,10 +3484,10 @@ void syn_maybe_enable(void)
/// @param syncing when TRUE: list syncing items
static void syn_cmd_list(exarg_T *eap, int syncing)
{
- char_u *arg = eap->arg;
+ char_u *arg = (char_u *)eap->arg;
char_u *arg_end;
- eap->nextcmd = find_nextcmd(arg);
+ eap->nextcmd = (char *)find_nextcmd(arg);
if (eap->skip) {
return;
}
@@ -3608,7 +3533,7 @@ static void syn_cmd_list(exarg_T *eap, int syncing)
/*
* No argument: List all group IDs and all syntax clusters.
*/
- for (int id = 1; id <= highlight_ga.ga_len && !got_int; id++) {
+ for (int id = 1; id <= highlight_num_groups() && !got_int; id++) {
syn_list_one(id, syncing, false);
}
for (int id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id) {
@@ -3628,17 +3553,17 @@ static void syn_cmd_list(exarg_T *eap, int syncing)
syn_list_cluster(id - SYNID_CLUSTER);
}
} else {
- int id = syn_name2id_len(arg, (int)(arg_end - arg));
+ int id = syn_name2id_len((char *)arg, (int)(arg_end - arg));
if (id == 0) {
semsg(_(e_nogroup), arg);
} else {
syn_list_one(id, syncing, true);
}
}
- arg = skipwhite(arg_end);
+ arg = (char_u *)skipwhite((char *)arg_end);
}
}
- eap->nextcmd = check_nextcmd(arg);
+ eap->nextcmd = (char *)check_nextcmd(arg);
}
static void syn_lines_msg(void)
@@ -3676,7 +3601,6 @@ static void syn_match_msg(void)
static int last_matchgroup;
-
/// List one syntax item, for ":syntax" or "syntax list syntax_name".
///
/// @param syncing when true: list syncing items
@@ -3766,8 +3690,8 @@ static void syn_list_one(const int id, const bool syncing, const bool link_only)
}
msg_putchar(' ');
if (spp->sp_sync_idx >= 0) {
- msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
- [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
+ msg_outtrans((char *)highlight_group_name(SYN_ITEMS(curwin->w_s)
+ [spp->sp_sync_idx].sp_syn.id - 1));
} else {
msg_puts("NONE");
}
@@ -3776,11 +3700,11 @@ static void syn_list_one(const int id, const bool syncing, const bool link_only)
}
// list the link, if there is one
- if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int) {
+ if (highlight_link_id(id - 1) && (did_header || link_only) && !got_int) {
(void)syn_list_header(did_header, 0, id, true);
msg_puts_attr("links to", attr);
msg_putchar(' ');
- msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
+ msg_outtrans((char *)highlight_group_name(highlight_link_id(id - 1) - 1));
}
}
@@ -3805,7 +3729,7 @@ static void syn_list_cluster(int id)
// slight hack: roughly duplicate the guts of syn_list_header()
msg_putchar('\n');
- msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
+ msg_outtrans((char *)SYN_CLSTR(curwin->w_s)[id].scl_name);
if (msg_col >= endcol) { // output at least one space
endcol = msg_col + 1;
@@ -3842,9 +3766,9 @@ static void put_id_list(const char *const name, const int16_t *const list, const
int scl_id = *p - SYNID_CLUSTER;
msg_putchar('@');
- msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
+ msg_outtrans((char *)SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
} else {
- msg_outtrans(HL_TABLE()[*p - 1].sg_name);
+ msg_outtrans((char *)highlight_group_name(*p - 1));
}
if (p[1]) {
msg_putchar(',');
@@ -3864,9 +3788,9 @@ static void put_pattern(const char *const s, const int c, const synpat_T *const
msg_puts_attr("matchgroup", attr);
msg_putchar('=');
if (last_matchgroup == 0) {
- msg_outtrans((char_u *)"NONE");
+ msg_outtrans("NONE");
} else {
- msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
+ msg_outtrans((char *)highlight_group_name(last_matchgroup - 1));
}
msg_putchar(' ');
}
@@ -3876,14 +3800,14 @@ static void put_pattern(const char *const s, const int c, const synpat_T *const
msg_putchar(c);
// output the pattern, in between a char that is not in the pattern
- for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL;) {
+ for (i = 0; vim_strchr((char *)spp->sp_pattern, sepchars[i]) != NULL;) {
if (sepchars[++i] == NUL) {
i = 0; // no good char found, just use the first one
break;
}
}
msg_putchar(sepchars[i]);
- msg_outtrans(spp->sp_pattern);
+ msg_outtrans((char *)spp->sp_pattern);
msg_putchar(sepchars[i]);
// output any pattern options
@@ -3993,7 +3917,7 @@ static bool syn_list_keywords(const int id, const hashtab_T *const ht, bool did_
prev_skipempty = (kp->flags & HL_SKIPEMPTY);
}
}
- msg_outtrans(kp->keyword);
+ msg_outtrans((char *)kp->keyword);
}
}
}
@@ -4130,7 +4054,7 @@ static char_u *get_group_name(char_u *arg, char_u **name_end)
char_u *rest;
*name_end = skiptowhite(arg);
- rest = skipwhite(*name_end);
+ rest = (char_u *)skipwhite((char *)(*name_end));
/*
* Check if there are enough arguments. The first argument may be a
@@ -4207,7 +4131,7 @@ static char_u *get_syn_options(char_u *arg, syn_opt_arg_T *opt, int *conceal_cha
p = flagtab[fidx].name;
int i;
for (i = 0, len = 0; p[i] != NUL; i += 2, ++len) {
- if (arg[len] != p[i] && arg[len] != p[i + 1]) {
+ if (arg[len] != (char_u)p[i] && arg[len] != (char_u)p[i + 1]) {
break;
}
}
@@ -4247,16 +4171,16 @@ static char_u *get_syn_options(char_u *arg, syn_opt_arg_T *opt, int *conceal_cha
}
} else if (flagtab[fidx].argtype == 11 && arg[5] == '=') {
// cchar=?
- *conceal_char = utf_ptr2char(arg + 6);
- arg += utfc_ptr2len(arg + 6) - 1;
+ *conceal_char = utf_ptr2char((char *)arg + 6);
+ arg += utfc_ptr2len((char *)arg + 6) - 1;
if (!vim_isprintc_strict(*conceal_char)) {
emsg(_("E844: invalid cchar value"));
return NULL;
}
- arg = skipwhite(arg + 7);
+ arg = (char_u *)skipwhite((char *)arg + 7);
} else {
opt->flags |= flagtab[fidx].flags;
- arg = skipwhite(arg + len);
+ arg = (char_u *)skipwhite((char *)arg + len);
if (flagtab[fidx].flags == HL_SYNC_HERE
|| flagtab[fidx].flags == HL_SYNC_THERE) {
@@ -4290,7 +4214,7 @@ static char_u *get_syn_options(char_u *arg, syn_opt_arg_T *opt, int *conceal_cha
}
xfree(gname);
- arg = skipwhite(arg);
+ arg = (char_u *)skipwhite((char *)arg);
} else if (flagtab[fidx].flags == HL_FOLD
&& foldmethodIsSyntax(curwin)) {
// Need to update folds later.
@@ -4330,7 +4254,7 @@ static void syn_incl_toplevel(int id, int *flagsp)
*/
static void syn_cmd_include(exarg_T *eap, int syncing)
{
- char_u *arg = eap->arg;
+ char_u *arg = (char_u *)eap->arg;
int sgl_id = 1;
char_u *group_name_end;
char_u *rest;
@@ -4339,7 +4263,7 @@ static void syn_cmd_include(exarg_T *eap, int syncing)
int prev_syn_inc_tag;
bool source = false;
- eap->nextcmd = find_nextcmd(arg);
+ eap->nextcmd = (char *)find_nextcmd(arg);
if (eap->skip) {
return;
}
@@ -4356,7 +4280,7 @@ static void syn_cmd_include(exarg_T *eap, int syncing)
return;
}
// separate_nextcmd() and expand_filename() depend on this
- eap->arg = rest;
+ eap->arg = (char *)rest;
}
/*
@@ -4365,7 +4289,7 @@ static void syn_cmd_include(exarg_T *eap, int syncing)
*/
eap->argt |= (EX_XFILE | EX_NOSPC);
separate_nextcmd(eap);
- if (*eap->arg == '<' || *eap->arg == '$' || path_is_absolute(eap->arg)) {
+ if (*eap->arg == '<' || *eap->arg == '$' || path_is_absolute((char_u *)eap->arg)) {
// For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
// file. Need to expand the file name first. In other cases
// ":runtime!" is used.
@@ -4391,8 +4315,8 @@ static void syn_cmd_include(exarg_T *eap, int syncing)
prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
curwin->w_s->b_syn_topgrp = sgl_id;
if (source
- ? do_source((char *)eap->arg, false, DOSO_NONE) == FAIL
- : source_runtime((char *)eap->arg, DIP_ALL) == FAIL) {
+ ? do_source(eap->arg, false, DOSO_NONE) == FAIL
+ : source_runtime(eap->arg, DIP_ALL) == FAIL) {
semsg(_(e_notopen), eap->arg);
}
curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
@@ -4404,7 +4328,7 @@ static void syn_cmd_include(exarg_T *eap, int syncing)
*/
static void syn_cmd_keyword(exarg_T *eap, int syncing)
{
- char_u *arg = eap->arg;
+ char_u *arg = (char_u *)eap->arg;
char_u *group_name_end;
int syn_id;
char_u *rest;
@@ -4440,7 +4364,7 @@ static void syn_cmd_keyword(exarg_T *eap, int syncing)
// 1: collect the options and copy the keywords to keyword_copy.
cnt = 0;
p = keyword_copy;
- for (; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest)) {
+ for (; rest != NULL && !ends_excmd(*rest); rest = (char_u *)skipwhite((char *)rest)) {
rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
if (rest == NULL || ends_excmd(*rest)) {
break;
@@ -4462,7 +4386,7 @@ static void syn_cmd_keyword(exarg_T *eap, int syncing)
// 2: Add an entry for each keyword.
for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1) {
- for (p = vim_strchr(kw, '[');;) {
+ for (p = (char_u *)vim_strchr((char *)kw, '[');;) {
if (p != NULL) {
*p = NUL;
}
@@ -4485,7 +4409,7 @@ static void syn_cmd_keyword(exarg_T *eap, int syncing)
kw = p + 1;
break; // skip over the "]"
}
- const int l = utfc_ptr2len(p + 1);
+ const int l = utfc_ptr2len((char *)p + 1);
memmove(p, p + 1, l);
p += l;
@@ -4501,7 +4425,7 @@ error:
}
if (rest != NULL) {
- eap->nextcmd = check_nextcmd(rest);
+ eap->nextcmd = (char *)check_nextcmd(rest);
} else {
semsg(_(e_invarg2), arg);
}
@@ -4517,7 +4441,7 @@ error:
/// @param syncing TRUE for ":syntax sync match .. "
static void syn_cmd_match(exarg_T *eap, int syncing)
{
- char_u *arg = eap->arg;
+ char_u *arg = (char_u *)eap->arg;
char_u *group_name_end;
char_u *rest;
synpat_T item; // the item found in the line
@@ -4554,7 +4478,7 @@ static void syn_cmd_match(exarg_T *eap, int syncing)
/*
* Check for trailing command and illegal trailing arguments.
*/
- eap->nextcmd = check_nextcmd(rest);
+ eap->nextcmd = (char *)check_nextcmd(rest);
if (!ends_excmd(*rest) || eap->skip) {
rest = NULL;
} else {
@@ -4615,7 +4539,7 @@ static void syn_cmd_match(exarg_T *eap, int syncing)
/// @param syncing TRUE for ":syntax sync region .."
static void syn_cmd_region(exarg_T *eap, int syncing)
{
- char_u *arg = eap->arg;
+ char_u *arg = (char_u *)eap->arg;
char_u *group_name_end;
char_u *rest; // next arg, NULL on error
char_u *key_end;
@@ -4690,13 +4614,13 @@ static void syn_cmd_region(exarg_T *eap, int syncing)
} else {
break;
}
- rest = skipwhite(key_end);
+ rest = (char_u *)skipwhite((char *)key_end);
if (*rest != '=') {
rest = NULL;
semsg(_("E398: Missing '=': %s"), arg);
break;
}
- rest = skipwhite(rest + 1);
+ rest = (char_u *)skipwhite((char *)rest + 1);
if (*rest == NUL) {
not_enough = true;
break;
@@ -4713,7 +4637,7 @@ static void syn_cmd_region(exarg_T *eap, int syncing)
break;
}
}
- rest = skipwhite(p);
+ rest = (char_u *)skipwhite((char *)p);
} else {
/*
* Allocate room for a syn_pattern, and link it in the list of
@@ -4761,7 +4685,7 @@ static void syn_cmd_region(exarg_T *eap, int syncing)
* Check for trailing garbage or command.
* If OK, add the item.
*/
- eap->nextcmd = check_nextcmd(rest);
+ eap->nextcmd = (char *)check_nextcmd(rest);
if (!ends_excmd(*rest) || eap->skip) {
rest = NULL;
} else {
@@ -5059,14 +4983,14 @@ static int syn_add_cluster(char_u *name)
*/
static void syn_cmd_cluster(exarg_T *eap, int syncing)
{
- char_u *arg = eap->arg;
+ char_u *arg = (char_u *)eap->arg;
char_u *group_name_end;
char_u *rest;
bool got_clstr = false;
int opt_len;
int list_op;
- eap->nextcmd = find_nextcmd(arg);
+ eap->nextcmd = (char *)find_nextcmd(arg);
if (eap->skip) {
return;
}
@@ -5144,7 +5068,7 @@ static char_u *get_syn_pattern(char_u *arg, synpat_T *ci)
char_u *end;
int *p;
int idx;
- char_u *cpo_save;
+ char *cpo_save;
// need at least three chars
if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL) {
@@ -5161,8 +5085,8 @@ static char_u *get_syn_pattern(char_u *arg, synpat_T *ci)
// Make 'cpoptions' empty, to avoid the 'l' flag
cpo_save = p_cpo;
- p_cpo = (char_u *)"";
- ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
+ p_cpo = "";
+ ci->sp_prog = vim_regcomp((char *)ci->sp_pattern, RE_MAGIC);
p_cpo = cpo_save;
if (ci->sp_prog == NULL) {
@@ -5199,7 +5123,7 @@ static char_u *get_syn_pattern(char_u *arg, synpat_T *ci)
ci->sp_off_flags |= (1 << idx);
if (idx == SPO_LC_OFF) { // lc=99
end += 3;
- *p = getdigits_int(&end, true, 0);
+ *p = getdigits_int((char **)&end, true, 0);
// "lc=" offset automatically sets "ms=" offset
if (!(ci->sp_off_flags & (1 << SPO_MS_OFF))) {
@@ -5210,10 +5134,10 @@ static char_u *get_syn_pattern(char_u *arg, synpat_T *ci)
end += 4;
if (*end == '+') {
end++;
- *p = getdigits_int(&end, true, 0); // positive offset
+ *p = getdigits_int((char **)&end, true, 0); // positive offset
} else if (*end == '-') {
end++;
- *p = -getdigits_int(&end, true, 0); // negative offset
+ *p = -getdigits_int((char **)&end, true, 0); // negative offset
}
}
if (*end != ',') {
@@ -5228,7 +5152,7 @@ static char_u *get_syn_pattern(char_u *arg, synpat_T *ci)
semsg(_("E402: Garbage after pattern: %s"), arg);
return NULL;
}
- return skipwhite(end);
+ return (char_u *)skipwhite((char *)end);
}
/*
@@ -5236,14 +5160,14 @@ static char_u *get_syn_pattern(char_u *arg, synpat_T *ci)
*/
static void syn_cmd_sync(exarg_T *eap, int syncing)
{
- char_u *arg_start = eap->arg;
+ char_u *arg_start = (char_u *)eap->arg;
char_u *arg_end;
char_u *key = NULL;
char_u *next_arg;
int illegal = FALSE;
int finished = FALSE;
long n;
- char_u *cpo_save;
+ char *cpo_save;
if (ends_excmd(*arg_start)) {
syn_cmd_list(eap, TRUE);
@@ -5252,7 +5176,7 @@ static void syn_cmd_sync(exarg_T *eap, int syncing)
while (!ends_excmd(*arg_start)) {
arg_end = skiptowhite(arg_start);
- next_arg = skipwhite(arg_end);
+ next_arg = (char_u *)skipwhite((char *)arg_end);
xfree(key);
key = vim_strnsave_up(arg_start, arg_end - arg_start);
if (STRCMP(key, "CCOMMENT") == 0) {
@@ -5264,7 +5188,7 @@ static void syn_cmd_sync(exarg_T *eap, int syncing)
if (!eap->skip) {
curwin->w_s->b_syn_sync_id = syn_check_group((char *)next_arg, (int)(arg_end - next_arg));
}
- next_arg = skipwhite(arg_end);
+ next_arg = (char_u *)skipwhite((char *)arg_end);
} else if (!eap->skip) {
curwin->w_s->b_syn_sync_id = syn_name2id("Comment");
}
@@ -5322,9 +5246,9 @@ static void syn_cmd_sync(exarg_T *eap, int syncing)
// Make 'cpoptions' empty, to avoid the 'l' flag
cpo_save = p_cpo;
- p_cpo = (char_u *)"";
+ p_cpo = "";
curwin->w_s->b_syn_linecont_prog =
- vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
+ vim_regcomp((char *)curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
p_cpo = cpo_save;
syn_clear_time(&curwin->w_s->b_syn_linecont_time);
@@ -5334,9 +5258,9 @@ static void syn_cmd_sync(exarg_T *eap, int syncing)
break;
}
}
- next_arg = skipwhite(arg_end + 1);
+ next_arg = (char_u *)skipwhite((char *)arg_end + 1);
} else {
- eap->arg = next_arg;
+ eap->arg = (char *)next_arg;
if (STRCMP(key, "MATCH") == 0) {
syn_cmd_match(eap, TRUE);
} else if (STRCMP(key, "REGION") == 0) {
@@ -5355,7 +5279,7 @@ static void syn_cmd_sync(exarg_T *eap, int syncing)
if (illegal) {
semsg(_("E404: Illegal arguments: %s"), arg_start);
} else if (!finished) {
- eap->nextcmd = check_nextcmd(arg_start);
+ eap->nextcmd = (char *)check_nextcmd(arg_start);
redraw_curbuf_later(SOME_VALID);
syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
}
@@ -5372,8 +5296,8 @@ static void syn_cmd_sync(exarg_T *eap, int syncing)
/// @return FAIL for some error, OK for success.
static int get_id_list(char_u **const arg, const int keylen, int16_t **const list, const bool skip)
{
- char_u *p = NULL;
- char_u *end;
+ char *p = NULL;
+ char *end;
int total_count = 0;
int16_t *retval = NULL;
regmatch_T regmatch;
@@ -5387,7 +5311,7 @@ static int get_id_list(char_u **const arg, const int keylen, int16_t **const lis
// grow when a regexp is used. In that case round 1 is done once again.
for (int round = 1; round <= 2; round++) {
// skip "contains"
- p = skipwhite(*arg + keylen);
+ p = skipwhite((char *)(*arg) + keylen);
if (*p != '=') {
semsg(_("E405: Missing equal sign: %s"), *arg);
break;
@@ -5401,9 +5325,8 @@ static int get_id_list(char_u **const arg, const int keylen, int16_t **const lis
// parse the arguments after "contains"
int count = 0;
do {
- for (end = p; *end && !ascii_iswhite(*end) && *end != ','; end++) {
- }
- char_u *const name = xmalloc(end - p + 3); // leave room for "^$"
+ for (end = p; *end && !ascii_iswhite(*end) && *end != ','; end++) {}
+ char *const name = xmalloc(end - p + 3); // leave room for "^$"
STRLCPY(name + 1, p, end - p + 1);
if (STRCMP(name + 1, "ALLBUT") == 0
|| STRCMP(name + 1, "ALL") == 0
@@ -5437,14 +5360,14 @@ static int get_id_list(char_u **const arg, const int keylen, int16_t **const lis
if (skip) {
id = -1;
} else {
- id = syn_check_cluster(name + 2, (int)(end - p - 1));
+ id = syn_check_cluster((char_u *)name + 2, (int)(end - p - 1));
}
} else {
/*
* Handle full group name.
*/
- if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL) {
- id = syn_check_group((char *)(name + 1), (int)(end - p));
+ if (strpbrk(name + 1, "\\.*^$~[") == NULL) {
+ id = syn_check_group((name + 1), (int)(end - p));
} else {
// Handle match of regexp with group names.
*name = '^';
@@ -5458,8 +5381,8 @@ static int get_id_list(char_u **const arg, const int keylen, int16_t **const lis
regmatch.rm_ic = TRUE;
id = 0;
- for (int i = highlight_ga.ga_len; --i >= 0;) {
- if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name, (colnr_T)0)) {
+ for (int i = highlight_num_groups(); --i >= 0;) {
+ if (vim_regexec(&regmatch, (char *)highlight_group_name(i), (colnr_T)0)) {
if (round == 2) {
// Got more items than expected; can happen
// when adding items that match:
@@ -5513,7 +5436,7 @@ static int get_id_list(char_u **const arg, const int keylen, int16_t **const lis
}
}
- *arg = p;
+ *arg = (char_u *)p;
if (failed || retval == NULL) {
xfree(retval);
return FAIL;
@@ -5537,8 +5460,7 @@ static int16_t *copy_id_list(const int16_t *const list)
}
int count;
- for (count = 0; list[count]; count++) {
- }
+ for (count = 0; list[count]; count++) {}
const size_t len = (count + 1) * sizeof(int16_t);
int16_t *const retval = xmalloc(len);
memmove(retval, list, len);
@@ -5685,14 +5607,13 @@ static struct subcommand subcommands[] =
*/
void ex_syntax(exarg_T *eap)
{
- char_u *arg = eap->arg;
+ char_u *arg = (char_u *)eap->arg;
char_u *subcmd_end;
- syn_cmdlinep = eap->cmdlinep;
+ syn_cmdlinep = (char_u **)eap->cmdlinep;
// isolate subcommand name
- for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); subcmd_end++) {
- }
+ for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); subcmd_end++) {}
char_u *const subcmd_name = vim_strnsave(arg, subcmd_end - arg);
if (eap->skip) { // skip error messages for all subcommands
emsg_skip++;
@@ -5703,8 +5624,8 @@ void ex_syntax(exarg_T *eap)
break;
}
if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0) {
- eap->arg = skipwhite(subcmd_end);
- (subcommands[i].func)(eap, FALSE);
+ eap->arg = skipwhite((char *)subcmd_end);
+ (subcommands[i].func)(eap, false);
break;
}
}
@@ -5746,14 +5667,14 @@ void ex_ownsyntax(exarg_T *eap)
// Move value of b:current_syntax to w:current_syntax.
new_value = get_var_value("b:current_syntax");
if (new_value != NULL) {
- set_internal_string_var("w:current_syntax", new_value);
+ set_internal_string_var("w:current_syntax", (char *)new_value);
}
// Restore value of b:current_syntax.
if (old_value == NULL) {
do_unlet(S_LEN("b:current_syntax"), true);
} else {
- set_internal_string_var("b:current_syntax", old_value);
+ set_internal_string_var("b:current_syntax", (char *)old_value);
xfree(old_value);
}
}
@@ -5766,7 +5687,6 @@ bool syntax_present(win_T *win)
|| win->w_s->b_keywtab_ic.ht_used > 0;
}
-
static enum {
EXP_SUBCMD, // expand ":syn" sub-commands
EXP_CASE, // expand ":syn case" arguments
@@ -5790,7 +5710,7 @@ void reset_expand_highlight(void)
void set_context_in_echohl_cmd(expand_T *xp, const char *arg)
{
xp->xp_context = EXPAND_HIGHLIGHT;
- xp->xp_pattern = (char_u *)arg;
+ xp->xp_pattern = (char *)arg;
include_none = 1;
}
@@ -5802,7 +5722,7 @@ void set_context_in_syntax_cmd(expand_T *xp, const char *arg)
// Default: expand subcommands.
xp->xp_context = EXPAND_SYNTAX;
expand_what = EXP_SUBCMD;
- xp->xp_pattern = (char_u *)arg;
+ xp->xp_pattern = (char *)arg;
include_link = 0;
include_default = 0;
@@ -5810,8 +5730,8 @@ void set_context_in_syntax_cmd(expand_T *xp, const char *arg)
if (*arg != NUL) {
const char *p = (const char *)skiptowhite((const char_u *)arg);
if (*p != NUL) { // Past first word.
- xp->xp_pattern = skipwhite((const char_u *)p);
- if (*skiptowhite(xp->xp_pattern) != NUL) {
+ xp->xp_pattern = skipwhite(p);
+ if (*skiptowhite((char_u *)xp->xp_pattern) != NUL) {
xp->xp_context = EXPAND_NOTHING;
} else if (STRNICMP(arg, "case", p - arg) == 0) {
expand_what = EXP_CASE;
@@ -5835,32 +5755,31 @@ void set_context_in_syntax_cmd(expand_T *xp, const char *arg)
* Function given to ExpandGeneric() to obtain the list syntax names for
* expansion.
*/
-char_u *get_syntax_name(expand_T *xp, int idx)
+char *get_syntax_name(expand_T *xp, int idx)
{
switch (expand_what) {
case EXP_SUBCMD:
- return (char_u *)subcommands[idx].name;
+ return subcommands[idx].name;
case EXP_CASE: {
static char *case_args[] = { "match", "ignore", NULL };
- return (char_u *)case_args[idx];
+ return case_args[idx];
}
case EXP_SPELL: {
static char *spell_args[] =
{ "toplevel", "notoplevel", "default", NULL };
- return (char_u *)spell_args[idx];
+ return spell_args[idx];
}
case EXP_SYNC: {
static char *sync_args[] =
{ "ccomment", "clear", "fromstart",
"linebreaks=", "linecont", "lines=", "match",
"maxlines=", "minlines=", "region", NULL };
- return (char_u *)sync_args[idx];
+ return sync_args[idx];
}
}
return NULL;
}
-
/// Function called for expression evaluation: get syntax ID at file position.
///
/// @param trans remove transparency
@@ -5869,8 +5788,8 @@ char_u *get_syntax_name(expand_T *xp, int idx)
int syn_get_id(win_T *wp, long lnum, colnr_T col, int trans, bool *spellp, int keep_state)
{
// When the position is not after the current position and in the same
- // line of the same buffer, need to restart parsing.
- if (wp->w_buffer != syn_buf || lnum != current_lnum || col < current_col) {
+ // line of the same window with the same buffer, need to restart parsing.
+ if (wp != syn_win || wp->w_buffer != syn_buf || lnum != current_lnum || col < current_col) {
syntax_start(wp, lnum);
} else if (col > current_col) {
// next_match may not be correct when moving around, e.g. with the
@@ -5895,7 +5814,6 @@ int get_syntax_info(int *seqnrp)
return current_flags;
}
-
/// Get the sequence number of the concealed file position.
///
/// @return seqnr if the file position is concealed, 0 otherwise.
@@ -6037,17 +5955,17 @@ static void syntime_clear(void)
* Function given to ExpandGeneric() to obtain the possible arguments of the
* ":syntime {on,off,clear,report}" command.
*/
-char_u *get_syntime_arg(expand_T *xp, int idx)
+char *get_syntime_arg(expand_T *xp, int idx)
{
switch (idx) {
case 0:
- return (char_u *)"on";
+ return "on";
case 1:
- return (char_u *)"off";
+ return "off";
case 2:
- return (char_u *)"clear";
+ return "clear";
case 3:
- return (char_u *)"report";
+ return "report";
}
return NULL;
}
@@ -6120,7 +6038,7 @@ static void syntime_report(void)
msg_puts(profile_msg(p->average));
msg_puts(" ");
msg_advance(50);
- msg_outtrans(HL_TABLE()[p->id - 1].sg_name);
+ msg_outtrans((char *)highlight_group_name(p->id - 1));
msg_puts(" ");
msg_advance(69);
@@ -6145,2627 +6063,3 @@ static void syntime_report(void)
msg_puts("\n");
}
}
-
-/**************************************
-* Highlighting stuff *
-**************************************/
-
-// The default highlight groups. These are compiled-in for fast startup and
-// they still work when the runtime files can't be found.
-//
-// When making changes here, also change runtime/colors/default.vim!
-
-static const char *highlight_init_both[] = {
- "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey",
- "Cursor guibg=fg guifg=bg",
- "lCursor guibg=fg guifg=bg",
- "DiffText cterm=bold ctermbg=Red gui=bold guibg=Red",
- "ErrorMsg ctermbg=DarkRed ctermfg=White guibg=Red guifg=White",
- "IncSearch cterm=reverse gui=reverse",
- "ModeMsg cterm=bold gui=bold",
- "NonText ctermfg=Blue gui=bold guifg=Blue",
- "Normal cterm=NONE gui=NONE",
- "PmenuSbar ctermbg=Grey guibg=Grey",
- "StatusLine cterm=reverse,bold gui=reverse,bold",
- "StatusLineNC cterm=reverse gui=reverse",
- "TabLineFill cterm=reverse gui=reverse",
- "TabLineSel cterm=bold gui=bold",
- "TermCursor cterm=reverse gui=reverse",
- "VertSplit cterm=reverse gui=reverse",
- "WildMenu ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black",
- "default link EndOfBuffer NonText",
- "default link LineNrAbove LineNr",
- "default link LineNrBelow LineNr",
- "default link QuickFixLine Search",
- "default link CursorLineSign SignColumn",
- "default link CursorLineFold FoldColumn",
- "default link Substitute Search",
- "default link Whitespace NonText",
- "default link MsgSeparator StatusLine",
- "default link NormalFloat Pmenu",
- "default link FloatBorder VertSplit",
- "default FloatShadow blend=80 guibg=Black",
- "default FloatShadowThrough blend=100 guibg=Black",
- "RedrawDebugNormal cterm=reverse gui=reverse",
- "RedrawDebugClear ctermbg=Yellow guibg=Yellow",
- "RedrawDebugComposed ctermbg=Green guibg=Green",
- "RedrawDebugRecompose ctermbg=Red guibg=Red",
- "Error term=reverse cterm=NONE ctermfg=White ctermbg=Red gui=NONE guifg=White guibg=Red",
- "Todo term=standout cterm=NONE ctermfg=Black ctermbg=Yellow gui=NONE guifg=Blue guibg=Yellow",
- "default link String Constant",
- "default link Character Constant",
- "default link Number Constant",
- "default link Boolean Constant",
- "default link Float Number",
- "default link Function Identifier",
- "default link Conditional Statement",
- "default link Repeat Statement",
- "default link Label Statement",
- "default link Operator Statement",
- "default link Keyword Statement",
- "default link Exception Statement",
- "default link Include PreProc",
- "default link Define PreProc",
- "default link Macro PreProc",
- "default link PreCondit PreProc",
- "default link StorageClass Type",
- "default link Structure Type",
- "default link Typedef Type",
- "default link Tag Special",
- "default link SpecialChar Special",
- "default link Delimiter Special",
- "default link SpecialComment Special",
- "default link Debug Special",
- "default DiagnosticError ctermfg=1 guifg=Red",
- "default DiagnosticWarn ctermfg=3 guifg=Orange",
- "default DiagnosticInfo ctermfg=4 guifg=LightBlue",
- "default DiagnosticHint ctermfg=7 guifg=LightGrey",
- "default DiagnosticUnderlineError cterm=underline gui=underline guisp=Red",
- "default DiagnosticUnderlineWarn cterm=underline gui=underline guisp=Orange",
- "default DiagnosticUnderlineInfo cterm=underline gui=underline guisp=LightBlue",
- "default DiagnosticUnderlineHint cterm=underline gui=underline guisp=LightGrey",
- "default link DiagnosticVirtualTextError DiagnosticError",
- "default link DiagnosticVirtualTextWarn DiagnosticWarn",
- "default link DiagnosticVirtualTextInfo DiagnosticInfo",
- "default link DiagnosticVirtualTextHint DiagnosticHint",
- "default link DiagnosticFloatingError DiagnosticError",
- "default link DiagnosticFloatingWarn DiagnosticWarn",
- "default link DiagnosticFloatingInfo DiagnosticInfo",
- "default link DiagnosticFloatingHint DiagnosticHint",
- "default link DiagnosticSignError DiagnosticError",
- "default link DiagnosticSignWarn DiagnosticWarn",
- "default link DiagnosticSignInfo DiagnosticInfo",
- "default link DiagnosticSignHint DiagnosticHint",
- NULL
-};
-
-// Default colors only used with a light background.
-static const char *highlight_init_light[] = {
- "ColorColumn ctermbg=LightRed guibg=LightRed",
- "CursorColumn ctermbg=LightGrey guibg=Grey90",
- "CursorLine cterm=underline guibg=Grey90",
- "CursorLineNr cterm=underline ctermfg=Brown gui=bold guifg=Brown",
- "DiffAdd ctermbg=LightBlue guibg=LightBlue",
- "DiffChange ctermbg=LightMagenta guibg=LightMagenta",
- "DiffDelete ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan",
- "Directory ctermfg=DarkBlue guifg=Blue",
- "FoldColumn ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue",
- "Folded ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue",
- "LineNr ctermfg=Brown guifg=Brown",
- "MatchParen ctermbg=Cyan guibg=Cyan",
- "MoreMsg ctermfg=DarkGreen gui=bold guifg=SeaGreen",
- "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta",
- "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey",
- "PmenuThumb ctermbg=Black guibg=Black",
- "Question ctermfg=DarkGreen gui=bold guifg=SeaGreen",
- "Search ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE",
- "SignColumn ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue",
- "SpecialKey ctermfg=DarkBlue guifg=Blue",
- "SpellBad ctermbg=LightRed guisp=Red gui=undercurl",
- "SpellCap ctermbg=LightBlue guisp=Blue gui=undercurl",
- "SpellLocal ctermbg=Cyan guisp=DarkCyan gui=undercurl",
- "SpellRare ctermbg=LightMagenta guisp=Magenta gui=undercurl",
- "TabLine cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey",
- "Title ctermfg=DarkMagenta gui=bold guifg=Magenta",
- "Visual guibg=LightGrey",
- "WarningMsg ctermfg=DarkRed guifg=Red",
- "Comment term=bold cterm=NONE ctermfg=DarkBlue ctermbg=NONE gui=NONE guifg=Blue guibg=NONE",
- "Constant term=underline cterm=NONE ctermfg=DarkRed ctermbg=NONE gui=NONE guifg=Magenta guibg=NONE",
- "Special term=bold cterm=NONE ctermfg=DarkMagenta ctermbg=NONE gui=NONE guifg=#6a5acd guibg=NONE",
- "Identifier term=underline cterm=NONE ctermfg=DarkCyan ctermbg=NONE gui=NONE guifg=DarkCyan guibg=NONE",
- "Statement term=bold cterm=NONE ctermfg=Brown ctermbg=NONE gui=bold guifg=Brown guibg=NONE",
- "PreProc term=underline cterm=NONE ctermfg=DarkMagenta ctermbg=NONE gui=NONE guifg=#6a0dad guibg=NONE",
- "Type term=underline cterm=NONE ctermfg=DarkGreen ctermbg=NONE gui=bold guifg=SeaGreen guibg=NONE",
- "Underlined term=underline cterm=underline ctermfg=DarkMagenta gui=underline guifg=SlateBlue",
- "Ignore term=NONE cterm=NONE ctermfg=white ctermbg=NONE gui=NONE guifg=bg guibg=NONE",
- NULL
-};
-
-// Default colors only used with a dark background.
-static const char *highlight_init_dark[] = {
- "ColorColumn ctermbg=DarkRed guibg=DarkRed",
- "CursorColumn ctermbg=DarkGrey guibg=Grey40",
- "CursorLine cterm=underline guibg=Grey40",
- "CursorLineNr cterm=underline ctermfg=Yellow gui=bold guifg=Yellow",
- "DiffAdd ctermbg=DarkBlue guibg=DarkBlue",
- "DiffChange ctermbg=DarkMagenta guibg=DarkMagenta",
- "DiffDelete ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan",
- "Directory ctermfg=LightCyan guifg=Cyan",
- "FoldColumn ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan",
- "Folded ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan",
- "LineNr ctermfg=Yellow guifg=Yellow",
- "MatchParen ctermbg=DarkCyan guibg=DarkCyan",
- "MoreMsg ctermfg=LightGreen gui=bold guifg=SeaGreen",
- "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta",
- "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey",
- "PmenuThumb ctermbg=White guibg=White",
- "Question ctermfg=LightGreen gui=bold guifg=Green",
- "Search ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black",
- "SignColumn ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan",
- "SpecialKey ctermfg=LightBlue guifg=Cyan",
- "SpellBad ctermbg=Red guisp=Red gui=undercurl",
- "SpellCap ctermbg=Blue guisp=Blue gui=undercurl",
- "SpellLocal ctermbg=Cyan guisp=Cyan gui=undercurl",
- "SpellRare ctermbg=Magenta guisp=Magenta gui=undercurl",
- "TabLine cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey",
- "Title ctermfg=LightMagenta gui=bold guifg=Magenta",
- "Visual guibg=DarkGrey",
- "WarningMsg ctermfg=LightRed guifg=Red",
- "Comment term=bold cterm=NONE ctermfg=Cyan ctermbg=NONE gui=NONE guifg=#80a0ff guibg=NONE",
- "Constant term=underline cterm=NONE ctermfg=Magenta ctermbg=NONE gui=NONE guifg=#ffa0a0 guibg=NONE",
- "Special term=bold cterm=NONE ctermfg=LightRed ctermbg=NONE gui=NONE guifg=Orange guibg=NONE",
- "Identifier term=underline cterm=bold ctermfg=Cyan ctermbg=NONE gui=NONE guifg=#40ffff guibg=NONE",
- "Statement term=bold cterm=NONE ctermfg=Yellow ctermbg=NONE gui=bold guifg=#ffff60 guibg=NONE",
- "PreProc term=underline cterm=NONE ctermfg=LightBlue ctermbg=NONE gui=NONE guifg=#ff80ff guibg=NONE",
- "Type term=underline cterm=NONE ctermfg=LightGreen ctermbg=NONE gui=bold guifg=#60ff60 guibg=NONE",
- "Underlined term=underline cterm=underline ctermfg=LightBlue gui=underline guifg=#80a0ff",
- "Ignore term=NONE cterm=NONE ctermfg=black ctermbg=NONE gui=NONE guifg=bg guibg=NONE",
- NULL
-};
-
-const char *const highlight_init_cmdline[] = {
- // XXX When modifying a list modify it in both valid and invalid halves.
- // TODO(ZyX-I): merge valid and invalid groups via a macros.
-
- // NvimInternalError should appear only when highlighter has a bug.
- "NvimInternalError ctermfg=Red ctermbg=Red guifg=Red guibg=Red",
-
- // Highlight groups (links) used by parser:
-
- "default link NvimAssignment Operator",
- "default link NvimPlainAssignment NvimAssignment",
- "default link NvimAugmentedAssignment NvimAssignment",
- "default link NvimAssignmentWithAddition NvimAugmentedAssignment",
- "default link NvimAssignmentWithSubtraction NvimAugmentedAssignment",
- "default link NvimAssignmentWithConcatenation NvimAugmentedAssignment",
-
- "default link NvimOperator Operator",
-
- "default link NvimUnaryOperator NvimOperator",
- "default link NvimUnaryPlus NvimUnaryOperator",
- "default link NvimUnaryMinus NvimUnaryOperator",
- "default link NvimNot NvimUnaryOperator",
-
- "default link NvimBinaryOperator NvimOperator",
- "default link NvimComparison NvimBinaryOperator",
- "default link NvimComparisonModifier NvimComparison",
- "default link NvimBinaryPlus NvimBinaryOperator",
- "default link NvimBinaryMinus NvimBinaryOperator",
- "default link NvimConcat NvimBinaryOperator",
- "default link NvimConcatOrSubscript NvimConcat",
- "default link NvimOr NvimBinaryOperator",
- "default link NvimAnd NvimBinaryOperator",
- "default link NvimMultiplication NvimBinaryOperator",
- "default link NvimDivision NvimBinaryOperator",
- "default link NvimMod NvimBinaryOperator",
-
- "default link NvimTernary NvimOperator",
- "default link NvimTernaryColon NvimTernary",
-
- "default link NvimParenthesis Delimiter",
- "default link NvimLambda NvimParenthesis",
- "default link NvimNestingParenthesis NvimParenthesis",
- "default link NvimCallingParenthesis NvimParenthesis",
-
- "default link NvimSubscript NvimParenthesis",
- "default link NvimSubscriptBracket NvimSubscript",
- "default link NvimSubscriptColon NvimSubscript",
- "default link NvimCurly NvimSubscript",
-
- "default link NvimContainer NvimParenthesis",
- "default link NvimDict NvimContainer",
- "default link NvimList NvimContainer",
-
- "default link NvimIdentifier Identifier",
- "default link NvimIdentifierScope NvimIdentifier",
- "default link NvimIdentifierScopeDelimiter NvimIdentifier",
- "default link NvimIdentifierName NvimIdentifier",
- "default link NvimIdentifierKey NvimIdentifier",
-
- "default link NvimColon Delimiter",
- "default link NvimComma Delimiter",
- "default link NvimArrow Delimiter",
-
- "default link NvimRegister SpecialChar",
- "default link NvimNumber Number",
- "default link NvimFloat NvimNumber",
- "default link NvimNumberPrefix Type",
-
- "default link NvimOptionSigil Type",
- "default link NvimOptionName NvimIdentifier",
- "default link NvimOptionScope NvimIdentifierScope",
- "default link NvimOptionScopeDelimiter NvimIdentifierScopeDelimiter",
-
- "default link NvimEnvironmentSigil NvimOptionSigil",
- "default link NvimEnvironmentName NvimIdentifier",
-
- "default link NvimString String",
- "default link NvimStringBody NvimString",
- "default link NvimStringQuote NvimString",
- "default link NvimStringSpecial SpecialChar",
-
- "default link NvimSingleQuote NvimStringQuote",
- "default link NvimSingleQuotedBody NvimStringBody",
- "default link NvimSingleQuotedQuote NvimStringSpecial",
-
- "default link NvimDoubleQuote NvimStringQuote",
- "default link NvimDoubleQuotedBody NvimStringBody",
- "default link NvimDoubleQuotedEscape NvimStringSpecial",
-
- "default link NvimFigureBrace NvimInternalError",
- "default link NvimSingleQuotedUnknownEscape NvimInternalError",
-
- "default link NvimSpacing Normal",
-
- // NvimInvalid groups:
-
- "default link NvimInvalidSingleQuotedUnknownEscape NvimInternalError",
-
- "default link NvimInvalid Error",
-
- "default link NvimInvalidAssignment NvimInvalid",
- "default link NvimInvalidPlainAssignment NvimInvalidAssignment",
- "default link NvimInvalidAugmentedAssignment NvimInvalidAssignment",
- "default link NvimInvalidAssignmentWithAddition NvimInvalidAugmentedAssignment",
- "default link NvimInvalidAssignmentWithSubtraction NvimInvalidAugmentedAssignment",
- "default link NvimInvalidAssignmentWithConcatenation NvimInvalidAugmentedAssignment",
-
- "default link NvimInvalidOperator NvimInvalid",
-
- "default link NvimInvalidUnaryOperator NvimInvalidOperator",
- "default link NvimInvalidUnaryPlus NvimInvalidUnaryOperator",
- "default link NvimInvalidUnaryMinus NvimInvalidUnaryOperator",
- "default link NvimInvalidNot NvimInvalidUnaryOperator",
-
- "default link NvimInvalidBinaryOperator NvimInvalidOperator",
- "default link NvimInvalidComparison NvimInvalidBinaryOperator",
- "default link NvimInvalidComparisonModifier NvimInvalidComparison",
- "default link NvimInvalidBinaryPlus NvimInvalidBinaryOperator",
- "default link NvimInvalidBinaryMinus NvimInvalidBinaryOperator",
- "default link NvimInvalidConcat NvimInvalidBinaryOperator",
- "default link NvimInvalidConcatOrSubscript NvimInvalidConcat",
- "default link NvimInvalidOr NvimInvalidBinaryOperator",
- "default link NvimInvalidAnd NvimInvalidBinaryOperator",
- "default link NvimInvalidMultiplication NvimInvalidBinaryOperator",
- "default link NvimInvalidDivision NvimInvalidBinaryOperator",
- "default link NvimInvalidMod NvimInvalidBinaryOperator",
-
- "default link NvimInvalidTernary NvimInvalidOperator",
- "default link NvimInvalidTernaryColon NvimInvalidTernary",
-
- "default link NvimInvalidDelimiter NvimInvalid",
-
- "default link NvimInvalidParenthesis NvimInvalidDelimiter",
- "default link NvimInvalidLambda NvimInvalidParenthesis",
- "default link NvimInvalidNestingParenthesis NvimInvalidParenthesis",
- "default link NvimInvalidCallingParenthesis NvimInvalidParenthesis",
-
- "default link NvimInvalidSubscript NvimInvalidParenthesis",
- "default link NvimInvalidSubscriptBracket NvimInvalidSubscript",
- "default link NvimInvalidSubscriptColon NvimInvalidSubscript",
- "default link NvimInvalidCurly NvimInvalidSubscript",
-
- "default link NvimInvalidContainer NvimInvalidParenthesis",
- "default link NvimInvalidDict NvimInvalidContainer",
- "default link NvimInvalidList NvimInvalidContainer",
-
- "default link NvimInvalidValue NvimInvalid",
-
- "default link NvimInvalidIdentifier NvimInvalidValue",
- "default link NvimInvalidIdentifierScope NvimInvalidIdentifier",
- "default link NvimInvalidIdentifierScopeDelimiter NvimInvalidIdentifier",
- "default link NvimInvalidIdentifierName NvimInvalidIdentifier",
- "default link NvimInvalidIdentifierKey NvimInvalidIdentifier",
-
- "default link NvimInvalidColon NvimInvalidDelimiter",
- "default link NvimInvalidComma NvimInvalidDelimiter",
- "default link NvimInvalidArrow NvimInvalidDelimiter",
-
- "default link NvimInvalidRegister NvimInvalidValue",
- "default link NvimInvalidNumber NvimInvalidValue",
- "default link NvimInvalidFloat NvimInvalidNumber",
- "default link NvimInvalidNumberPrefix NvimInvalidNumber",
-
- "default link NvimInvalidOptionSigil NvimInvalidIdentifier",
- "default link NvimInvalidOptionName NvimInvalidIdentifier",
- "default link NvimInvalidOptionScope NvimInvalidIdentifierScope",
- "default link NvimInvalidOptionScopeDelimiter "
- "NvimInvalidIdentifierScopeDelimiter",
-
- "default link NvimInvalidEnvironmentSigil NvimInvalidOptionSigil",
- "default link NvimInvalidEnvironmentName NvimInvalidIdentifier",
-
- // Invalid string bodies and specials are still highlighted as valid ones to
- // minimize the red area.
- "default link NvimInvalidString NvimInvalidValue",
- "default link NvimInvalidStringBody NvimStringBody",
- "default link NvimInvalidStringQuote NvimInvalidString",
- "default link NvimInvalidStringSpecial NvimStringSpecial",
-
- "default link NvimInvalidSingleQuote NvimInvalidStringQuote",
- "default link NvimInvalidSingleQuotedBody NvimInvalidStringBody",
- "default link NvimInvalidSingleQuotedQuote NvimInvalidStringSpecial",
-
- "default link NvimInvalidDoubleQuote NvimInvalidStringQuote",
- "default link NvimInvalidDoubleQuotedBody NvimInvalidStringBody",
- "default link NvimInvalidDoubleQuotedEscape NvimInvalidStringSpecial",
- "default link NvimInvalidDoubleQuotedUnknownEscape NvimInvalidValue",
-
- "default link NvimInvalidFigureBrace NvimInvalidDelimiter",
-
- "default link NvimInvalidSpacing ErrorMsg",
-
- // Not actually invalid, but we highlight user that he is doing something
- // wrong.
- "default link NvimDoubleQuotedUnknownEscape NvimInvalidValue",
- NULL,
-};
-
-/// Create default links for Nvim* highlight groups used for cmdline coloring
-void syn_init_cmdline_highlight(bool reset, bool init)
-{
- for (size_t i = 0; highlight_init_cmdline[i] != NULL; i++) {
- do_highlight(highlight_init_cmdline[i], reset, init);
- }
-}
-
-/// Load colors from a file if "g:colors_name" is set, otherwise load builtin
-/// colors
-///
-/// @param both include groups where 'bg' doesn't matter
-/// @param reset clear groups first
-void init_highlight(bool both, bool reset)
-{
- static int had_both = false;
-
- // Try finding the color scheme file. Used when a color file was loaded
- // and 'background' or 't_Co' is changed.
- char_u *p = get_var_value("g:colors_name");
- if (p != NULL) {
- // Value of g:colors_name could be freed in load_colors() and make
- // p invalid, so copy it.
- char_u *copy_p = vim_strsave(p);
- bool okay = load_colors(copy_p);
- xfree(copy_p);
- if (okay) {
- return;
- }
- }
-
- /*
- * Didn't use a color file, use the compiled-in colors.
- */
- if (both) {
- had_both = true;
- const char *const *const pp = highlight_init_both;
- for (size_t i = 0; pp[i] != NULL; i++) {
- do_highlight(pp[i], reset, true);
- }
- } else if (!had_both) {
- // Don't do anything before the call with both == true from main().
- // Not everything has been setup then, and that call will overrule
- // everything anyway.
- return;
- }
-
- const char *const *const pp = ((*p_bg == 'l')
- ? highlight_init_light
- : highlight_init_dark);
- for (size_t i = 0; pp[i] != NULL; i++) {
- do_highlight(pp[i], reset, true);
- }
-
- /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
- * depend on the number of colors available.
- * With 8 colors brown is equal to yellow, need to use black for Search fg
- * to avoid Statement highlighted text disappears.
- * Clear the attributes, needed when changing the t_Co value. */
- if (t_colors > 8) {
- do_highlight((*p_bg == 'l'
- ? "Visual cterm=NONE ctermbg=LightGrey"
- : "Visual cterm=NONE ctermbg=DarkGrey"), false, true);
- } else {
- do_highlight("Visual cterm=reverse ctermbg=NONE", false, true);
- if (*p_bg == 'l') {
- do_highlight("Search ctermfg=black", false, true);
- }
- }
-
- syn_init_cmdline_highlight(false, false);
-}
-
-/*
- * Load color file "name".
- * Return OK for success, FAIL for failure.
- */
-int load_colors(char_u *name)
-{
- char_u *buf;
- int retval = FAIL;
- static bool recursive = false;
-
- // When being called recursively, this is probably because setting
- // 'background' caused the highlighting to be reloaded. This means it is
- // working, thus we should return OK.
- if (recursive) {
- return OK;
- }
-
- recursive = true;
- size_t buflen = STRLEN(name) + 12;
- buf = xmalloc(buflen);
- apply_autocmds(EVENT_COLORSCHEMEPRE, name, curbuf->b_fname, false, curbuf);
- snprintf((char *)buf, buflen, "colors/%s.vim", name);
- retval = source_runtime((char *)buf, DIP_START + DIP_OPT);
- if (retval == FAIL) {
- snprintf((char *)buf, buflen, "colors/%s.lua", name);
- retval = source_runtime((char *)buf, DIP_START + DIP_OPT);
- }
- xfree(buf);
- apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, false, curbuf);
-
- recursive = false;
-
- return retval;
-}
-
-static char *(color_names[28]) = {
- "Black", "DarkBlue", "DarkGreen", "DarkCyan",
- "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
- "Gray", "Grey", "LightGray", "LightGrey",
- "DarkGray", "DarkGrey",
- "Blue", "LightBlue", "Green", "LightGreen",
- "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
- "LightMagenta", "Yellow", "LightYellow", "White", "NONE"
-};
-// indices:
-// 0, 1, 2, 3,
-// 4, 5, 6, 7,
-// 8, 9, 10, 11,
-// 12, 13,
-// 14, 15, 16, 17,
-// 18, 19, 20, 21, 22,
-// 23, 24, 25, 26, 27
-static int color_numbers_16[28] = { 0, 1, 2, 3,
- 4, 5, 6, 6,
- 7, 7, 7, 7,
- 8, 8,
- 9, 9, 10, 10,
- 11, 11, 12, 12, 13,
- 13, 14, 14, 15, -1 };
-// for xterm with 88 colors...
-static int color_numbers_88[28] = { 0, 4, 2, 6,
- 1, 5, 32, 72,
- 84, 84, 7, 7,
- 82, 82,
- 12, 43, 10, 61,
- 14, 63, 9, 74, 13,
- 75, 11, 78, 15, -1 };
-// for xterm with 256 colors...
-static int color_numbers_256[28] = { 0, 4, 2, 6,
- 1, 5, 130, 3,
- 248, 248, 7, 7,
- 242, 242,
- 12, 81, 10, 121,
- 14, 159, 9, 224, 13,
- 225, 11, 229, 15, -1 };
-// for terminals with less than 16 colors...
-static int color_numbers_8[28] = { 0, 4, 2, 6,
- 1, 5, 3, 3,
- 7, 7, 7, 7,
- 0+8, 0+8,
- 4+8, 4+8, 2+8, 2+8,
- 6+8, 6+8, 1+8, 1+8, 5+8,
- 5+8, 3+8, 3+8, 7+8, -1 };
-
-// Lookup the "cterm" value to be used for color with index "idx" in
-// color_names[].
-// "boldp" will be set to TRUE or FALSE for a foreground color when using 8
-// colors, otherwise it will be unchanged.
-int lookup_color(const int idx, const bool foreground, TriState *const boldp)
-{
- int color = color_numbers_16[idx];
-
- // Use the _16 table to check if it's a valid color name.
- if (color < 0) {
- return -1;
- }
-
- if (t_colors == 8) {
- // t_Co is 8: use the 8 colors table
- color = color_numbers_8[idx];
- if (foreground) {
- // set/reset bold attribute to get light foreground
- // colors (on some terminals, e.g. "linux")
- if (color & 8) {
- *boldp = kTrue;
- } else {
- *boldp = kFalse;
- }
- }
- color &= 7; // truncate to 8 colors
- } else if (t_colors == 16) {
- color = color_numbers_8[idx];
- } else if (t_colors == 88) {
- color = color_numbers_88[idx];
- } else if (t_colors >= 256) {
- color = color_numbers_256[idx];
- }
- return color;
-}
-
-
-/// Handle ":highlight" command
-///
-/// When using ":highlight clear" this is called recursively for each group with
-/// forceit and init being both true.
-///
-/// @param[in] line Command arguments.
-/// @param[in] forceit True when bang is given, allows to link group even if
-/// it has its own settings.
-/// @param[in] init True when initializing.
-void do_highlight(const char *line, const bool forceit, const bool init)
- FUNC_ATTR_NONNULL_ALL
-{
- const char *name_end;
- const char *linep;
- const char *key_start;
- const char *arg_start;
- long i;
- int off;
- int len;
- int attr;
- int id;
- int idx;
- struct hl_group item_before;
- bool did_change = false;
- bool dodefault = false;
- bool doclear = false;
- bool dolink = false;
- bool error = false;
- int color;
- bool is_normal_group = false; // "Normal" group
- bool did_highlight_changed = false;
-
- // If no argument, list current highlighting.
- if (ends_excmd((uint8_t)(*line))) {
- for (i = 1; i <= highlight_ga.ga_len && !got_int; i++) {
- // TODO(brammool): only call when the group has attributes set
- highlight_list_one(i);
- }
- return;
- }
-
- // Isolate the name.
- name_end = (const char *)skiptowhite((const char_u *)line);
- linep = (const char *)skipwhite((const char_u *)name_end);
-
- // Check for "default" argument.
- if (strncmp(line, "default", name_end - line) == 0) {
- dodefault = true;
- line = linep;
- name_end = (const char *)skiptowhite((const char_u *)line);
- linep = (const char *)skipwhite((const char_u *)name_end);
- }
-
- // Check for "clear" or "link" argument.
- if (strncmp(line, "clear", name_end - line) == 0) {
- doclear = true;
- } else if (strncmp(line, "link", name_end - line) == 0) {
- dolink = true;
- }
-
- // ":highlight {group-name}": list highlighting for one group.
- if (!doclear && !dolink && ends_excmd((uint8_t)(*linep))) {
- id = syn_name2id_len((const char_u *)line, (int)(name_end - line));
- if (id == 0) {
- semsg(_("E411: highlight group not found: %s"), line);
- } else {
- highlight_list_one(id);
- }
- return;
- }
-
- // Handle ":highlight link {from} {to}" command.
- if (dolink) {
- const char *from_start = linep;
- const char *from_end;
- const char *to_start;
- const char *to_end;
- int from_id;
- int to_id;
- struct hl_group *hlgroup = NULL;
-
- from_end = (const char *)skiptowhite((const char_u *)from_start);
- to_start = (const char *)skipwhite((const char_u *)from_end);
- to_end = (const char *)skiptowhite((const char_u *)to_start);
-
- if (ends_excmd((uint8_t)(*from_start))
- || ends_excmd((uint8_t)(*to_start))) {
- semsg(_("E412: Not enough arguments: \":highlight link %s\""),
- from_start);
- return;
- }
-
- if (!ends_excmd(*skipwhite((const char_u *)to_end))) {
- semsg(_("E413: Too many arguments: \":highlight link %s\""), from_start);
- return;
- }
-
- from_id = syn_check_group(from_start, (int)(from_end - from_start));
- if (strncmp(to_start, "NONE", 4) == 0) {
- to_id = 0;
- } else {
- to_id = syn_check_group(to_start, (int)(to_end - to_start));
- }
-
- if (from_id > 0) {
- hlgroup = &HL_TABLE()[from_id - 1];
- if (dodefault && (forceit || hlgroup->sg_deflink == 0)) {
- hlgroup->sg_deflink = to_id;
- hlgroup->sg_deflink_sctx = current_sctx;
- hlgroup->sg_deflink_sctx.sc_lnum += sourcing_lnum;
- }
- }
-
- if (from_id > 0 && (!init || hlgroup->sg_set == 0)) {
- // Don't allow a link when there already is some highlighting
- // for the group, unless '!' is used
- if (to_id > 0 && !forceit && !init
- && hl_has_settings(from_id - 1, dodefault)) {
- if (sourcing_name == NULL && !dodefault) {
- emsg(_("E414: group has settings, highlight link ignored"));
- }
- } else if (hlgroup->sg_link != to_id
- || hlgroup->sg_script_ctx.sc_sid != current_sctx.sc_sid
- || hlgroup->sg_cleared) {
- if (!init) {
- hlgroup->sg_set |= SG_LINK;
- }
- hlgroup->sg_link = to_id;
- hlgroup->sg_script_ctx = current_sctx;
- hlgroup->sg_script_ctx.sc_lnum += sourcing_lnum;
- hlgroup->sg_cleared = false;
- redraw_all_later(SOME_VALID);
-
- // Only call highlight changed() once after multiple changes
- need_highlight_changed = true;
- }
- }
-
- return;
- }
-
- if (doclear) {
- // ":highlight clear [group]" command.
- line = linep;
- if (ends_excmd((uint8_t)(*line))) {
- do_unlet(S_LEN("colors_name"), true);
- restore_cterm_colors();
-
- // Clear all default highlight groups and load the defaults.
- for (int j = 0; j < highlight_ga.ga_len; j++) {
- highlight_clear(j);
- }
- init_highlight(true, true);
- highlight_changed();
- redraw_all_later(NOT_VALID);
- return;
- }
- name_end = (const char *)skiptowhite((const char_u *)line);
- linep = (const char *)skipwhite((const char_u *)name_end);
- }
-
- // Find the group name in the table. If it does not exist yet, add it.
- id = syn_check_group(line, (int)(name_end - line));
- if (id == 0) { // Failed (out of memory).
- return;
- }
- idx = id - 1; // Index is ID minus one.
-
- // Return if "default" was used and the group already has settings
- if (dodefault && hl_has_settings(idx, true)) {
- return;
- }
-
- // Make a copy so we can check if any attribute actually changed
- item_before = HL_TABLE()[idx];
- is_normal_group = (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0);
-
- // Clear the highlighting for ":hi clear {group}" and ":hi clear".
- if (doclear || (forceit && init)) {
- highlight_clear(idx);
- if (!doclear) {
- HL_TABLE()[idx].sg_set = 0;
- }
- }
-
- char *key = NULL;
- char *arg = NULL;
- if (!doclear) {
- while (!ends_excmd((uint8_t)(*linep))) {
- key_start = linep;
- if (*linep == '=') {
- semsg(_("E415: unexpected equal sign: %s"), key_start);
- error = true;
- break;
- }
-
- // Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg",
- // "guibg" or "guisp").
- while (*linep && !ascii_iswhite(*linep) && *linep != '=') {
- linep++;
- }
- xfree(key);
- key = (char *)vim_strnsave_up((const char_u *)key_start,
- linep - key_start);
- linep = (const char *)skipwhite((const char_u *)linep);
-
- if (strcmp(key, "NONE") == 0) {
- if (!init || HL_TABLE()[idx].sg_set == 0) {
- if (!init) {
- HL_TABLE()[idx].sg_set |= SG_CTERM+SG_GUI;
- }
- highlight_clear(idx);
- }
- continue;
- }
-
- // Check for the equal sign.
- if (*linep != '=') {
- semsg(_("E416: missing equal sign: %s"), key_start);
- error = true;
- break;
- }
- linep++;
-
- // Isolate the argument.
- linep = (const char *)skipwhite((const char_u *)linep);
- if (*linep == '\'') { // guifg='color name'
- arg_start = ++linep;
- linep = strchr(linep, '\'');
- if (linep == NULL) {
- semsg(_(e_invarg2), key_start);
- error = true;
- break;
- }
- } else {
- arg_start = linep;
- linep = (const char *)skiptowhite((const char_u *)linep);
- }
- if (linep == arg_start) {
- semsg(_("E417: missing argument: %s"), key_start);
- error = true;
- break;
- }
- xfree(arg);
- arg = xstrndup(arg_start, (size_t)(linep - arg_start));
-
- if (*linep == '\'') {
- linep++;
- }
-
- // Store the argument.
- if (strcmp(key, "TERM") == 0
- || strcmp(key, "CTERM") == 0
- || strcmp(key, "GUI") == 0) {
- attr = 0;
- off = 0;
- while (arg[off] != NUL) {
- for (i = ARRAY_SIZE(hl_attr_table); --i >= 0;) {
- len = (int)STRLEN(hl_name_table[i]);
- if (STRNICMP(arg + off, hl_name_table[i], len) == 0) {
- attr |= hl_attr_table[i];
- off += len;
- break;
- }
- }
- if (i < 0) {
- semsg(_("E418: Illegal value: %s"), arg);
- error = true;
- break;
- }
- if (arg[off] == ',') { // Another one follows.
- off++;
- }
- }
- if (error) {
- break;
- }
- if (*key == 'C') {
- if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM)) {
- if (!init) {
- HL_TABLE()[idx].sg_set |= SG_CTERM;
- }
- HL_TABLE()[idx].sg_cterm = attr;
- HL_TABLE()[idx].sg_cterm_bold = false;
- }
- } else if (*key == 'G') {
- if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) {
- if (!init) {
- HL_TABLE()[idx].sg_set |= SG_GUI;
- }
- HL_TABLE()[idx].sg_gui = attr;
- }
- }
- } else if (STRCMP(key, "FONT") == 0) {
- // in non-GUI fonts are simply ignored
- } else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0) {
- if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM)) {
- if (!init) {
- HL_TABLE()[idx].sg_set |= SG_CTERM;
- }
-
- /* When setting the foreground color, and previously the "bold"
- * flag was set for a light color, reset it now */
- if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold) {
- HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
- HL_TABLE()[idx].sg_cterm_bold = false;
- }
-
- if (ascii_isdigit(*arg)) {
- color = atoi(arg);
- } else if (STRICMP(arg, "fg") == 0) {
- if (cterm_normal_fg_color) {
- color = cterm_normal_fg_color - 1;
- } else {
- emsg(_("E419: FG color unknown"));
- error = true;
- break;
- }
- } else if (STRICMP(arg, "bg") == 0) {
- if (cterm_normal_bg_color > 0) {
- color = cterm_normal_bg_color - 1;
- } else {
- emsg(_("E420: BG color unknown"));
- error = true;
- break;
- }
- } else {
- // Reduce calls to STRICMP a bit, it can be slow.
- off = TOUPPER_ASC(*arg);
- for (i = ARRAY_SIZE(color_names); --i >= 0;) {
- if (off == color_names[i][0]
- && STRICMP(arg + 1, color_names[i] + 1) == 0) {
- break;
- }
- }
- if (i < 0) {
- semsg(_("E421: Color name or number not recognized: %s"),
- key_start);
- error = true;
- break;
- }
-
- TriState bold = kNone;
- color = lookup_color(i, key[5] == 'F', &bold);
-
- // set/reset bold attribute to get light foreground
- // colors (on some terminals, e.g. "linux")
- if (bold == kTrue) {
- HL_TABLE()[idx].sg_cterm |= HL_BOLD;
- HL_TABLE()[idx].sg_cterm_bold = true;
- } else if (bold == kFalse) {
- HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
- }
- }
- // Add one to the argument, to avoid zero. Zero is used for
- // "NONE", then "color" is -1.
- if (key[5] == 'F') {
- HL_TABLE()[idx].sg_cterm_fg = color + 1;
- if (is_normal_group) {
- cterm_normal_fg_color = color + 1;
- }
- } else {
- HL_TABLE()[idx].sg_cterm_bg = color + 1;
- if (is_normal_group) {
- cterm_normal_bg_color = color + 1;
- if (!ui_rgb_attached()) {
- if (color >= 0) {
- int dark = -1;
-
- if (t_colors < 16) {
- dark = (color == 0 || color == 4);
- } else if (color < 16) {
- // Limit the heuristic to the standard 16 colors
- dark = (color < 7 || color == 8);
- }
- // Set the 'background' option if the value is
- // wrong.
- if (dark != -1
- && dark != (*p_bg == 'd')
- && !option_was_set("bg")) {
- set_option_value("bg", 0L, (dark ? "dark" : "light"), 0);
- reset_option_was_set("bg");
- }
- }
- }
- }
- }
- }
- } else if (strcmp(key, "GUIFG") == 0) {
- char **namep = &HL_TABLE()[idx].sg_rgb_fg_name;
-
- if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) {
- if (!init) {
- HL_TABLE()[idx].sg_set |= SG_GUI;
- }
-
- if (*namep == NULL || STRCMP(*namep, arg) != 0) {
- xfree(*namep);
- if (strcmp(arg, "NONE") != 0) {
- *namep = xstrdup(arg);
- HL_TABLE()[idx].sg_rgb_fg = name_to_color(arg);
- } else {
- *namep = NULL;
- HL_TABLE()[idx].sg_rgb_fg = -1;
- }
- did_change = true;
- }
- }
-
- if (is_normal_group) {
- normal_fg = HL_TABLE()[idx].sg_rgb_fg;
- }
- } else if (STRCMP(key, "GUIBG") == 0) {
- char **const namep = &HL_TABLE()[idx].sg_rgb_bg_name;
-
- if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) {
- if (!init) {
- HL_TABLE()[idx].sg_set |= SG_GUI;
- }
-
- if (*namep == NULL || STRCMP(*namep, arg) != 0) {
- xfree(*namep);
- if (STRCMP(arg, "NONE") != 0) {
- *namep = xstrdup(arg);
- HL_TABLE()[idx].sg_rgb_bg = name_to_color(arg);
- } else {
- *namep = NULL;
- HL_TABLE()[idx].sg_rgb_bg = -1;
- }
- did_change = true;
- }
- }
-
- if (is_normal_group) {
- normal_bg = HL_TABLE()[idx].sg_rgb_bg;
- }
- } else if (strcmp(key, "GUISP") == 0) {
- char **const namep = &HL_TABLE()[idx].sg_rgb_sp_name;
-
- if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) {
- if (!init) {
- HL_TABLE()[idx].sg_set |= SG_GUI;
- }
-
- if (*namep == NULL || STRCMP(*namep, arg) != 0) {
- xfree(*namep);
- if (strcmp(arg, "NONE") != 0) {
- *namep = xstrdup(arg);
- HL_TABLE()[idx].sg_rgb_sp = name_to_color(arg);
- } else {
- *namep = NULL;
- HL_TABLE()[idx].sg_rgb_sp = -1;
- }
- did_change = true;
- }
- }
-
- if (is_normal_group) {
- normal_sp = HL_TABLE()[idx].sg_rgb_sp;
- }
- } else if (strcmp(key, "START") == 0 || strcmp(key, "STOP") == 0) {
- // Ignored for now
- } else if (strcmp(key, "BLEND") == 0) {
- if (strcmp(arg, "NONE") != 0) {
- HL_TABLE()[idx].sg_blend = strtol(arg, NULL, 10);
- } else {
- HL_TABLE()[idx].sg_blend = -1;
- }
- } else {
- semsg(_("E423: Illegal argument: %s"), key_start);
- error = true;
- break;
- }
- HL_TABLE()[idx].sg_cleared = false;
-
- // When highlighting has been given for a group, don't link it.
- if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK)) {
- HL_TABLE()[idx].sg_link = 0;
- }
-
- // Continue with next argument.
- linep = (const char *)skipwhite((const char_u *)linep);
- }
- }
-
- // If there is an error, and it's a new entry, remove it from the table.
- if (error && idx == highlight_ga.ga_len) {
- syn_unadd_group();
- } else {
- if (!error && is_normal_group) {
- // Need to update all groups, because they might be using "bg" and/or
- // "fg", which have been changed now.
- highlight_attr_set_all();
-
- if (!ui_has(kUILinegrid) && starting == 0) {
- // Older UIs assume that we clear the screen after normal group is
- // changed
- ui_refresh();
- } else {
- // TUI and newer UIs will repaint the screen themselves. NOT_VALID
- // redraw below will still handle usages of guibg=fg etc.
- ui_default_colors_set();
- }
- did_highlight_changed = true;
- redraw_all_later(NOT_VALID);
- } else {
- set_hl_attr(idx);
- }
- HL_TABLE()[idx].sg_script_ctx = current_sctx;
- HL_TABLE()[idx].sg_script_ctx.sc_lnum += sourcing_lnum;
- }
- xfree(key);
- xfree(arg);
-
- // Only call highlight_changed() once, after a sequence of highlight
- // commands, and only if an attribute actually changed
- if ((did_change
- || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
- && !did_highlight_changed) {
- // Do not trigger a redraw when highlighting is changed while
- // redrawing. This may happen when evaluating 'statusline' changes the
- // StatusLine group.
- if (!updating_screen) {
- redraw_all_later(NOT_VALID);
- }
- need_highlight_changed = true;
- }
-}
-
-#if defined(EXITFREE)
-void free_highlight(void)
-{
- for (int i = 0; i < highlight_ga.ga_len; ++i) {
- highlight_clear(i);
- xfree(HL_TABLE()[i].sg_name);
- xfree(HL_TABLE()[i].sg_name_u);
- }
- ga_clear(&highlight_ga);
- map_destroy(cstr_t, int)(&highlight_unames);
-}
-
-#endif
-
-/*
- * Reset the cterm colors to what they were before Vim was started, if
- * possible. Otherwise reset them to zero.
- */
-void restore_cterm_colors(void)
-{
- normal_fg = -1;
- normal_bg = -1;
- normal_sp = -1;
- cterm_normal_fg_color = 0;
- cterm_normal_bg_color = 0;
-}
-
-/// @param check_link if true also check for an existing link.
-///
-/// @return TRUE if highlight group "idx" has any settings.
-static int hl_has_settings(int idx, bool check_link)
-{
- return HL_TABLE()[idx].sg_cleared == 0
- && (HL_TABLE()[idx].sg_attr != 0
- || HL_TABLE()[idx].sg_cterm_fg != 0
- || HL_TABLE()[idx].sg_cterm_bg != 0
- || HL_TABLE()[idx].sg_rgb_fg_name != NULL
- || HL_TABLE()[idx].sg_rgb_bg_name != NULL
- || HL_TABLE()[idx].sg_rgb_sp_name != NULL
- || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
-}
-
-/*
- * Clear highlighting for one group.
- */
-static void highlight_clear(int idx)
-{
- HL_TABLE()[idx].sg_cleared = true;
-
- HL_TABLE()[idx].sg_attr = 0;
- HL_TABLE()[idx].sg_cterm = 0;
- HL_TABLE()[idx].sg_cterm_bold = false;
- HL_TABLE()[idx].sg_cterm_fg = 0;
- HL_TABLE()[idx].sg_cterm_bg = 0;
- HL_TABLE()[idx].sg_gui = 0;
- HL_TABLE()[idx].sg_rgb_fg = -1;
- HL_TABLE()[idx].sg_rgb_bg = -1;
- HL_TABLE()[idx].sg_rgb_sp = -1;
- XFREE_CLEAR(HL_TABLE()[idx].sg_rgb_fg_name);
- XFREE_CLEAR(HL_TABLE()[idx].sg_rgb_bg_name);
- XFREE_CLEAR(HL_TABLE()[idx].sg_rgb_sp_name);
- HL_TABLE()[idx].sg_blend = -1;
- // Restore default link and context if they exist. Otherwise clears.
- HL_TABLE()[idx].sg_link = HL_TABLE()[idx].sg_deflink;
- // Since we set the default link, set the location to where the default
- // link was set.
- HL_TABLE()[idx].sg_script_ctx = HL_TABLE()[idx].sg_deflink_sctx;
-}
-
-
-/// \addtogroup LIST_XXX
-/// @{
-#define LIST_ATTR 1
-#define LIST_STRING 2
-#define LIST_INT 3
-/// @}
-
-static void highlight_list_one(const int id)
-{
- struct hl_group *const sgp = &HL_TABLE()[id - 1]; // index is ID minus one
- bool didh = false;
-
- if (message_filtered(sgp->sg_name)) {
- return;
- }
-
- didh = highlight_list_arg(id, didh, LIST_ATTR,
- sgp->sg_cterm, NULL, "cterm");
- didh = highlight_list_arg(id, didh, LIST_INT,
- sgp->sg_cterm_fg, NULL, "ctermfg");
- didh = highlight_list_arg(id, didh, LIST_INT,
- sgp->sg_cterm_bg, NULL, "ctermbg");
-
- didh = highlight_list_arg(id, didh, LIST_ATTR,
- sgp->sg_gui, NULL, "gui");
- didh = highlight_list_arg(id, didh, LIST_STRING,
- 0, sgp->sg_rgb_fg_name, "guifg");
- didh = highlight_list_arg(id, didh, LIST_STRING,
- 0, sgp->sg_rgb_bg_name, "guibg");
- didh = highlight_list_arg(id, didh, LIST_STRING,
- 0, sgp->sg_rgb_sp_name, "guisp");
-
- didh = highlight_list_arg(id, didh, LIST_INT,
- sgp->sg_blend+1, NULL, "blend");
-
- if (sgp->sg_link && !got_int) {
- (void)syn_list_header(didh, 0, id, true);
- didh = true;
- msg_puts_attr("links to", HL_ATTR(HLF_D));
- msg_putchar(' ');
- msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
- }
-
- if (!didh) {
- highlight_list_arg(id, didh, LIST_STRING, 0, "cleared", "");
- }
- if (p_verbose > 0) {
- last_set_msg(sgp->sg_script_ctx);
- }
-}
-
-Dictionary get_global_hl_defs(void)
-{
- Dictionary rv = ARRAY_DICT_INIT;
- for (int i = 1; i <= highlight_ga.ga_len && !got_int; i++) {
- Dictionary attrs = ARRAY_DICT_INIT;
- struct hl_group *h = &HL_TABLE()[i - 1];
- if (h->sg_attr > 0) {
- attrs = hlattrs2dict(syn_attr2entry(h->sg_attr), true);
- } else if (h->sg_link > 0) {
- const char *link = (const char *)HL_TABLE()[h->sg_link - 1].sg_name;
- PUT(attrs, "link", STRING_OBJ(cstr_to_string(link)));
- }
- PUT(rv, (const char *)h->sg_name, DICTIONARY_OBJ(attrs));
- }
-
- return rv;
-}
-
-/// Outputs a highlight when doing ":hi MyHighlight"
-///
-/// @param type one of \ref LIST_XXX
-/// @param iarg integer argument used if \p type == LIST_INT
-/// @param sarg string used if \p type == LIST_STRING
-static bool highlight_list_arg(const int id, bool didh, const int type, int iarg, char *const sarg,
- const char *const name)
-{
- char buf[100];
-
- if (got_int) {
- return false;
- }
- if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0)) {
- char *ts = buf;
- if (type == LIST_INT) {
- snprintf((char *)buf, sizeof(buf), "%d", iarg - 1);
- } else if (type == LIST_STRING) {
- ts = sarg;
- } else { // type == LIST_ATTR
- buf[0] = NUL;
- for (int i = 0; hl_attr_table[i] != 0; i++) {
- if (iarg & hl_attr_table[i]) {
- if (buf[0] != NUL) {
- xstrlcat(buf, ",", 100);
- }
- xstrlcat(buf, hl_name_table[i], 100);
- iarg &= ~hl_attr_table[i]; // don't want "inverse"
- }
- }
- }
-
- (void)syn_list_header(didh, (int)(vim_strsize((char_u *)ts) + STRLEN(name)
- + 1), id, false);
- didh = true;
- if (!got_int) {
- if (*name != NUL) {
- msg_puts_attr(name, HL_ATTR(HLF_D));
- msg_puts_attr("=", HL_ATTR(HLF_D));
- }
- msg_outtrans((char_u *)ts);
- }
- }
- return didh;
-}
-
-/// Check whether highlight group has attribute
-///
-/// @param[in] id Highlight group to check.
-/// @param[in] flag Attribute to check.
-/// @param[in] modec 'g' for GUI, 'c' for term.
-///
-/// @return "1" if highlight group has attribute, NULL otherwise.
-const char *highlight_has_attr(const int id, const int flag, const int modec)
- FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
-{
- int attr;
-
- if (id <= 0 || id > highlight_ga.ga_len) {
- return NULL;
- }
-
- if (modec == 'g') {
- attr = HL_TABLE()[id - 1].sg_gui;
- } else {
- attr = HL_TABLE()[id - 1].sg_cterm;
- }
-
- return (attr & flag) ? "1" : NULL;
-}
-
-/// Return color name of the given highlight group
-///
-/// @param[in] id Highlight group to work with.
-/// @param[in] what What to return: one of "font", "fg", "bg", "sp", "fg#",
-/// "bg#" or "sp#".
-/// @param[in] modec 'g' for GUI, 'c' for cterm and 't' for term.
-///
-/// @return color name, possibly in a static buffer. Buffer will be overwritten
-/// on next highlight_color() call. May return NULL.
-const char *highlight_color(const int id, const char *const what, const int modec)
- FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
-{
- static char name[20];
- int n;
- bool fg = false;
- bool sp = false;
- bool font = false;
-
- if (id <= 0 || id > highlight_ga.ga_len) {
- return NULL;
- }
-
- if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g') {
- fg = true;
- } else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
- && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't') {
- font = true;
- } else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p') {
- sp = true;
- } else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g')) {
- return NULL;
- }
- if (modec == 'g') {
- if (what[2] == '#' && ui_rgb_attached()) {
- if (fg) {
- n = HL_TABLE()[id - 1].sg_rgb_fg;
- } else if (sp) {
- n = HL_TABLE()[id - 1].sg_rgb_sp;
- } else {
- n = HL_TABLE()[id - 1].sg_rgb_bg;
- }
- if (n < 0 || n > 0xffffff) {
- return NULL;
- }
- snprintf(name, sizeof(name), "#%06x", n);
- return name;
- }
- if (fg) {
- return (const char *)HL_TABLE()[id - 1].sg_rgb_fg_name;
- }
- if (sp) {
- return (const char *)HL_TABLE()[id - 1].sg_rgb_sp_name;
- }
- return (const char *)HL_TABLE()[id - 1].sg_rgb_bg_name;
- }
- if (font || sp) {
- return NULL;
- }
- if (modec == 'c') {
- if (fg) {
- n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
- } else {
- n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
- }
- if (n < 0) {
- return NULL;
- }
- snprintf(name, sizeof(name), "%d", n);
- return name;
- }
- // term doesn't have color.
- return NULL;
-}
-
-/// Output the syntax list header.
-///
-/// @param did_header did header already
-/// @param outlen length of string that comes
-/// @param id highlight group id
-/// @param force_newline always start a new line
-/// @return true when started a new line.
-static bool syn_list_header(const bool did_header, const int outlen, const int id,
- bool force_newline)
-{
- int endcol = 19;
- bool newline = true;
- int name_col = 0;
- bool adjust = true;
-
- if (!did_header) {
- msg_putchar('\n');
- if (got_int) {
- return true;
- }
- msg_outtrans(HL_TABLE()[id - 1].sg_name);
- name_col = msg_col;
- endcol = 15;
- } else if ((ui_has(kUIMessages) || msg_silent) && !force_newline) {
- msg_putchar(' ');
- adjust = false;
- } else if (msg_col + outlen + 1 >= Columns || force_newline) {
- msg_putchar('\n');
- if (got_int) {
- return true;
- }
- } else {
- if (msg_col >= endcol) { // wrap around is like starting a new line
- newline = false;
- }
- }
-
- if (adjust) {
- if (msg_col >= endcol) {
- // output at least one space
- endcol = msg_col + 1;
- }
-
- msg_advance(endcol);
- }
-
- // Show "xxx" with the attributes.
- if (!did_header) {
- if (endcol == Columns - 1 && endcol <= name_col) {
- msg_putchar(' ');
- }
- msg_puts_attr("xxx", syn_id2attr(id));
- msg_putchar(' ');
- }
-
- return newline;
-}
-
-/// Set the attribute numbers for a highlight group.
-/// Called after one of the attributes has changed.
-/// @param idx corrected highlight index
-static void set_hl_attr(int idx)
-{
- HlAttrs at_en = HLATTRS_INIT;
- struct hl_group *sgp = HL_TABLE() + idx;
-
- at_en.cterm_ae_attr = sgp->sg_cterm;
- at_en.cterm_fg_color = sgp->sg_cterm_fg;
- at_en.cterm_bg_color = sgp->sg_cterm_bg;
- at_en.rgb_ae_attr = sgp->sg_gui;
- // FIXME(tarruda): The "unset value" for rgb is -1, but since hlgroup is
- // initialized with 0(by garray functions), check for sg_rgb_{f,b}g_name
- // before setting attr_entry->{f,g}g_color to a other than -1
- at_en.rgb_fg_color = sgp->sg_rgb_fg_name ? sgp->sg_rgb_fg : -1;
- at_en.rgb_bg_color = sgp->sg_rgb_bg_name ? sgp->sg_rgb_bg : -1;
- at_en.rgb_sp_color = sgp->sg_rgb_sp_name ? sgp->sg_rgb_sp : -1;
- at_en.hl_blend = sgp->sg_blend;
-
- sgp->sg_attr = hl_get_syn_attr(0, idx+1, at_en);
-
- // a cursor style uses this syn_id, make sure its attribute is updated.
- if (cursor_mode_uses_syn_id(idx+1)) {
- ui_mode_info_set();
- }
-}
-
-int syn_name2id(const char *name)
- FUNC_ATTR_NONNULL_ALL
-{
- return syn_name2id_len((char_u *)name, STRLEN(name));
-}
-
-/// Lookup a highlight group name and return its ID.
-///
-/// @param highlight name e.g. 'Cursor', 'Normal'
-/// @return the highlight id, else 0 if \p name does not exist
-int syn_name2id_len(const char_u *name, size_t len)
- FUNC_ATTR_NONNULL_ALL
-{
- char name_u[MAX_SYN_NAME + 1];
-
- if (len == 0 || len > MAX_SYN_NAME) {
- return 0;
- }
-
- // Avoid using stricmp() too much, it's slow on some systems */
- // Avoid alloc()/free(), these are slow too.
- memcpy(name_u, name, len);
- name_u[len] = '\0';
- vim_strup((char_u *)name_u);
-
- // map_get(..., int) returns 0 when no key is present, which is
- // the expected value for missing highlight group.
- return map_get(cstr_t, int)(&highlight_unames, name_u);
-}
-
-/// Lookup a highlight group name and return its attributes.
-/// Return zero if not found.
-int syn_name2attr(const char_u *name)
- FUNC_ATTR_NONNULL_ALL
-{
- int id = syn_name2id((char *)name);
-
- if (id != 0) {
- return syn_id2attr(id);
- }
- return 0;
-}
-
-/*
- * Return TRUE if highlight group "name" exists.
- */
-int highlight_exists(const char *name)
-{
- return syn_name2id(name) > 0;
-}
-
-/*
- * Return the name of highlight group "id".
- * When not a valid ID return an empty string.
- */
-char_u *syn_id2name(int id)
-{
- if (id <= 0 || id > highlight_ga.ga_len) {
- return (char_u *)"";
- }
- return HL_TABLE()[id - 1].sg_name;
-}
-
-
-/// Find highlight group name in the table and return its ID.
-/// If it doesn't exist yet, a new entry is created.
-///
-/// @param pp Highlight group name
-/// @param len length of \p pp
-///
-/// @return 0 for failure else the id of the group
-int syn_check_group(const char *name, int len)
-{
- if (len > MAX_SYN_NAME) {
- emsg(_(e_highlight_group_name_too_long));
- return 0;
- }
- int id = syn_name2id_len((char_u *)name, len);
- if (id == 0) { // doesn't exist yet
- return syn_add_group(vim_strnsave((char_u *)name, len));
- }
- return id;
-}
-
-/// Add new highlight group and return its ID.
-///
-/// @param name must be an allocated string, it will be consumed.
-/// @return 0 for failure, else the allocated group id
-/// @see syn_check_group syn_unadd_group
-static int syn_add_group(char_u *name)
-{
- char_u *p;
-
- // Check that the name is ASCII letters, digits and underscore.
- for (p = name; *p != NUL; ++p) {
- if (!vim_isprintc(*p)) {
- emsg(_("E669: Unprintable character in group name"));
- xfree(name);
- return 0;
- } else if (!ASCII_ISALNUM(*p) && *p != '_') {
- /* This is an error, but since there previously was no check only
- * give a warning. */
- msg_source(HL_ATTR(HLF_W));
- msg(_("W18: Invalid character in group name"));
- break;
- }
- }
-
- /*
- * First call for this growarray: init growing array.
- */
- if (highlight_ga.ga_data == NULL) {
- highlight_ga.ga_itemsize = sizeof(struct hl_group);
- ga_set_growsize(&highlight_ga, 10);
- }
-
- if (highlight_ga.ga_len >= MAX_HL_ID) {
- emsg(_("E849: Too many highlight and syntax groups"));
- xfree(name);
- return 0;
- }
-
- char *const name_up = (char *)vim_strsave_up(name);
-
- // Append another syntax_highlight entry.
- struct hl_group *hlgp = GA_APPEND_VIA_PTR(struct hl_group, &highlight_ga);
- memset(hlgp, 0, sizeof(*hlgp));
- hlgp->sg_name = name;
- hlgp->sg_rgb_bg = -1;
- hlgp->sg_rgb_fg = -1;
- hlgp->sg_rgb_sp = -1;
- hlgp->sg_blend = -1;
- hlgp->sg_name_u = name_up;
-
- int id = highlight_ga.ga_len; // ID is index plus one
-
- map_put(cstr_t, int)(&highlight_unames, name_up, id);
-
- return id;
-}
-
-/// When, just after calling syn_add_group(), an error is discovered, this
-/// function deletes the new name.
-static void syn_unadd_group(void)
-{
- highlight_ga.ga_len--;
- HlGroup *item = &HL_TABLE()[highlight_ga.ga_len];
- map_del(cstr_t, int)(&highlight_unames, item->sg_name_u);
- xfree(item->sg_name);
- xfree(item->sg_name_u);
-}
-
-
-/// Translate a group ID to highlight attributes.
-/// @see syn_attr2entry
-int syn_id2attr(int hl_id)
-{
- hl_id = syn_get_final_id(hl_id);
- struct hl_group *sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
-
- int attr = ns_get_hl(-1, hl_id, false, sgp->sg_set);
- if (attr >= 0) {
- return attr;
- }
- return sgp->sg_attr;
-}
-
-
-/*
- * Translate a group ID to the final group ID (following links).
- */
-int syn_get_final_id(int hl_id)
-{
- int count;
-
- if (hl_id > highlight_ga.ga_len || hl_id < 1) {
- return 0; // Can be called from eval!!
- }
- /*
- * Follow links until there is no more.
- * Look out for loops! Break after 100 links.
- */
- for (count = 100; --count >= 0;) {
- struct hl_group *sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
-
- // ACHTUNG: when using "tmp" attribute (no link) the function might be
- // called twice. it needs be smart enough to remember attr only to
- // syn_id2attr time
- int check = ns_get_hl(-1, hl_id, true, sgp->sg_set);
- if (check == 0) {
- return hl_id; // how dare! it broke the link!
- } else if (check > 0) {
- hl_id = check;
- continue;
- }
-
-
- if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len) {
- break;
- }
- hl_id = sgp->sg_link;
- }
-
- return hl_id;
-}
-
-/// Refresh the color attributes of all highlight groups.
-void highlight_attr_set_all(void)
-{
- for (int idx = 0; idx < highlight_ga.ga_len; idx++) {
- struct hl_group *sgp = &HL_TABLE()[idx];
- if (sgp->sg_rgb_bg_name != NULL) {
- sgp->sg_rgb_bg = name_to_color(sgp->sg_rgb_bg_name);
- }
- if (sgp->sg_rgb_fg_name != NULL) {
- sgp->sg_rgb_fg = name_to_color(sgp->sg_rgb_fg_name);
- }
- if (sgp->sg_rgb_sp_name != NULL) {
- sgp->sg_rgb_sp = name_to_color(sgp->sg_rgb_sp_name);
- }
- set_hl_attr(idx);
- }
-}
-
-// Apply difference between User[1-9] and HLF_S to HLF_SNC.
-static void combine_stl_hlt(int id, int id_S, int id_alt, int hlcnt, int i, int hlf, int *table)
- FUNC_ATTR_NONNULL_ALL
-{
- struct hl_group *const hlt = HL_TABLE();
-
- if (id_alt == 0) {
- memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
- hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
- hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
- } else {
- memmove(&hlt[hlcnt + i], &hlt[id_alt - 1], sizeof(struct hl_group));
- }
- hlt[hlcnt + i].sg_link = 0;
-
- hlt[hlcnt + i].sg_cterm ^= hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
- if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg) {
- hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
- }
- if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg) {
- hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
- }
- hlt[hlcnt + i].sg_gui ^= hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
- if (hlt[id - 1].sg_rgb_fg != hlt[id_S - 1].sg_rgb_fg) {
- hlt[hlcnt + i].sg_rgb_fg = hlt[id - 1].sg_rgb_fg;
- }
- if (hlt[id - 1].sg_rgb_bg != hlt[id_S - 1].sg_rgb_bg) {
- hlt[hlcnt + i].sg_rgb_bg = hlt[id - 1].sg_rgb_bg;
- }
- if (hlt[id - 1].sg_rgb_sp != hlt[id_S - 1].sg_rgb_sp) {
- hlt[hlcnt + i].sg_rgb_sp = hlt[id - 1].sg_rgb_sp;
- }
- highlight_ga.ga_len = hlcnt + i + 1;
- set_hl_attr(hlcnt + i); // At long last we can apply
- table[i] = syn_id2attr(hlcnt + i + 1);
-}
-
-/// Translate highlight groups into attributes in highlight_attr[] and set up
-/// the user highlights User1..9. A set of corresponding highlights to use on
-/// top of HLF_SNC is computed. Called only when nvim starts and upon first
-/// screen redraw after any :highlight command.
-void highlight_changed(void)
-{
- int id;
- char userhl[30]; // use 30 to avoid compiler warning
- int id_S = -1;
- int id_SNC = 0;
- int hlcnt;
-
- need_highlight_changed = false;
-
- /// Translate builtin highlight groups into attributes for quick lookup.
- for (int hlf = 0; hlf < HLF_COUNT; hlf++) {
- id = syn_check_group(hlf_names[hlf], STRLEN(hlf_names[hlf]));
- if (id == 0) {
- abort();
- }
- int final_id = syn_get_final_id(id);
- if (hlf == HLF_SNC) {
- id_SNC = final_id;
- } else if (hlf == HLF_S) {
- id_S = final_id;
- }
-
- highlight_attr[hlf] = hl_get_ui_attr(hlf, final_id,
- hlf == HLF_INACTIVE);
-
- if (highlight_attr[hlf] != highlight_attr_last[hlf]) {
- if (hlf == HLF_MSG) {
- clear_cmdline = true;
- }
- ui_call_hl_group_set(cstr_as_string((char *)hlf_names[hlf]),
- highlight_attr[hlf]);
- highlight_attr_last[hlf] = highlight_attr[hlf];
- }
- }
-
- //
- // Setup the user highlights
- //
- // Temporarily utilize 10 more hl entries:
- // 9 for User1-User9 combined with StatusLineNC
- // 1 for StatusLine default
- // Must to be in there simultaneously in case of table overflows in
- // get_attr_entry()
- ga_grow(&highlight_ga, 10);
- hlcnt = highlight_ga.ga_len;
- if (id_S == -1) {
- // Make sure id_S is always valid to simplify code below. Use the last entry
- memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
- id_S = hlcnt + 10;
- }
- for (int i = 0; i < 9; i++) {
- snprintf(userhl, sizeof(userhl), "User%d", i + 1);
- id = syn_name2id(userhl);
- if (id == 0) {
- highlight_user[i] = 0;
- highlight_stlnc[i] = 0;
- } else {
- highlight_user[i] = syn_id2attr(id);
- combine_stl_hlt(id, id_S, id_SNC, hlcnt, i, HLF_SNC, highlight_stlnc);
- }
- }
- highlight_ga.ga_len = hlcnt;
-}
-
-
-/*
- * Handle command line completion for :highlight command.
- */
-void set_context_in_highlight_cmd(expand_T *xp, const char *arg)
-{
- // Default: expand group names.
- xp->xp_context = EXPAND_HIGHLIGHT;
- xp->xp_pattern = (char_u *)arg;
- include_link = 2;
- include_default = 1;
-
- // (part of) subcommand already typed
- if (*arg != NUL) {
- const char *p = (const char *)skiptowhite((const char_u *)arg);
- if (*p != NUL) { // Past "default" or group name.
- include_default = 0;
- if (strncmp("default", arg, p - arg) == 0) {
- arg = (const char *)skipwhite((const char_u *)p);
- xp->xp_pattern = (char_u *)arg;
- p = (const char *)skiptowhite((const char_u *)arg);
- }
- if (*p != NUL) { // past group name
- include_link = 0;
- if (arg[1] == 'i' && arg[0] == 'N') {
- highlight_list();
- }
- if (strncmp("link", arg, p - arg) == 0
- || strncmp("clear", arg, p - arg) == 0) {
- xp->xp_pattern = skipwhite((const char_u *)p);
- p = (const char *)skiptowhite(xp->xp_pattern);
- if (*p != NUL) { // Past first group name.
- xp->xp_pattern = skipwhite((const char_u *)p);
- p = (const char *)skiptowhite(xp->xp_pattern);
- }
- }
- if (*p != NUL) { // Past group name(s).
- xp->xp_context = EXPAND_NOTHING;
- }
- }
- }
- }
-}
-
-/*
- * List highlighting matches in a nice way.
- */
-static void highlight_list(void)
-{
- int i;
-
- for (i = 10; --i >= 0;) {
- highlight_list_two(i, HL_ATTR(HLF_D));
- }
- for (i = 40; --i >= 0;) {
- highlight_list_two(99, 0);
- }
-}
-
-static void highlight_list_two(int cnt, int attr)
-{
- msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
- msg_clr_eos();
- ui_flush();
- os_delay(cnt == 99 ? 40L : (long)cnt * 50L, false);
-}
-
-
-/// Function given to ExpandGeneric() to obtain the list of group names.
-const char *get_highlight_name(expand_T *const xp, int idx)
- FUNC_ATTR_WARN_UNUSED_RESULT
-{
- return get_highlight_name_ext(xp, idx, true);
-}
-
-
-/// Obtain a highlight group name.
-///
-/// @param skip_cleared if true don't return a cleared entry.
-const char *get_highlight_name_ext(expand_T *xp, int idx, bool skip_cleared)
- FUNC_ATTR_WARN_UNUSED_RESULT
-{
- if (idx < 0) {
- return NULL;
- }
-
- // Items are never removed from the table, skip the ones that were cleared.
- if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared) {
- return "";
- }
-
- if (idx == highlight_ga.ga_len && include_none != 0) {
- return "none";
- } else if (idx == highlight_ga.ga_len + include_none
- && include_default != 0) {
- return "default";
- } else if (idx == highlight_ga.ga_len + include_none + include_default
- && include_link != 0) {
- return "link";
- } else if (idx == highlight_ga.ga_len + include_none + include_default + 1
- && include_link != 0) {
- return "clear";
- } else if (idx >= highlight_ga.ga_len) {
- return NULL;
- }
- return (const char *)HL_TABLE()[idx].sg_name;
-}
-
-color_name_table_T color_name_table[] = {
- // Colors from rgb.txt
- { "AliceBlue", RGB_(0xf0, 0xf8, 0xff) },
- { "AntiqueWhite", RGB_(0xfa, 0xeb, 0xd7) },
- { "AntiqueWhite1", RGB_(0xff, 0xef, 0xdb) },
- { "AntiqueWhite2", RGB_(0xee, 0xdf, 0xcc) },
- { "AntiqueWhite3", RGB_(0xcd, 0xc0, 0xb0) },
- { "AntiqueWhite4", RGB_(0x8b, 0x83, 0x78) },
- { "Aqua", RGB_(0x00, 0xff, 0xff) },
- { "Aquamarine", RGB_(0x7f, 0xff, 0xd4) },
- { "Aquamarine1", RGB_(0x7f, 0xff, 0xd4) },
- { "Aquamarine2", RGB_(0x76, 0xee, 0xc6) },
- { "Aquamarine3", RGB_(0x66, 0xcd, 0xaa) },
- { "Aquamarine4", RGB_(0x45, 0x8b, 0x74) },
- { "Azure", RGB_(0xf0, 0xff, 0xff) },
- { "Azure1", RGB_(0xf0, 0xff, 0xff) },
- { "Azure2", RGB_(0xe0, 0xee, 0xee) },
- { "Azure3", RGB_(0xc1, 0xcd, 0xcd) },
- { "Azure4", RGB_(0x83, 0x8b, 0x8b) },
- { "Beige", RGB_(0xf5, 0xf5, 0xdc) },
- { "Bisque", RGB_(0xff, 0xe4, 0xc4) },
- { "Bisque1", RGB_(0xff, 0xe4, 0xc4) },
- { "Bisque2", RGB_(0xee, 0xd5, 0xb7) },
- { "Bisque3", RGB_(0xcd, 0xb7, 0x9e) },
- { "Bisque4", RGB_(0x8b, 0x7d, 0x6b) },
- { "Black", RGB_(0x00, 0x00, 0x00) },
- { "BlanchedAlmond", RGB_(0xff, 0xeb, 0xcd) },
- { "Blue", RGB_(0x00, 0x00, 0xff) },
- { "Blue1", RGB_(0x0, 0x0, 0xff) },
- { "Blue2", RGB_(0x0, 0x0, 0xee) },
- { "Blue3", RGB_(0x0, 0x0, 0xcd) },
- { "Blue4", RGB_(0x0, 0x0, 0x8b) },
- { "BlueViolet", RGB_(0x8a, 0x2b, 0xe2) },
- { "Brown", RGB_(0xa5, 0x2a, 0x2a) },
- { "Brown1", RGB_(0xff, 0x40, 0x40) },
- { "Brown2", RGB_(0xee, 0x3b, 0x3b) },
- { "Brown3", RGB_(0xcd, 0x33, 0x33) },
- { "Brown4", RGB_(0x8b, 0x23, 0x23) },
- { "BurlyWood", RGB_(0xde, 0xb8, 0x87) },
- { "Burlywood1", RGB_(0xff, 0xd3, 0x9b) },
- { "Burlywood2", RGB_(0xee, 0xc5, 0x91) },
- { "Burlywood3", RGB_(0xcd, 0xaa, 0x7d) },
- { "Burlywood4", RGB_(0x8b, 0x73, 0x55) },
- { "CadetBlue", RGB_(0x5f, 0x9e, 0xa0) },
- { "CadetBlue1", RGB_(0x98, 0xf5, 0xff) },
- { "CadetBlue2", RGB_(0x8e, 0xe5, 0xee) },
- { "CadetBlue3", RGB_(0x7a, 0xc5, 0xcd) },
- { "CadetBlue4", RGB_(0x53, 0x86, 0x8b) },
- { "ChartReuse", RGB_(0x7f, 0xff, 0x00) },
- { "Chartreuse1", RGB_(0x7f, 0xff, 0x0) },
- { "Chartreuse2", RGB_(0x76, 0xee, 0x0) },
- { "Chartreuse3", RGB_(0x66, 0xcd, 0x0) },
- { "Chartreuse4", RGB_(0x45, 0x8b, 0x0) },
- { "Chocolate", RGB_(0xd2, 0x69, 0x1e) },
- { "Chocolate1", RGB_(0xff, 0x7f, 0x24) },
- { "Chocolate2", RGB_(0xee, 0x76, 0x21) },
- { "Chocolate3", RGB_(0xcd, 0x66, 0x1d) },
- { "Chocolate4", RGB_(0x8b, 0x45, 0x13) },
- { "Coral", RGB_(0xff, 0x7f, 0x50) },
- { "Coral1", RGB_(0xff, 0x72, 0x56) },
- { "Coral2", RGB_(0xee, 0x6a, 0x50) },
- { "Coral3", RGB_(0xcd, 0x5b, 0x45) },
- { "Coral4", RGB_(0x8b, 0x3e, 0x2f) },
- { "CornFlowerBlue", RGB_(0x64, 0x95, 0xed) },
- { "Cornsilk", RGB_(0xff, 0xf8, 0xdc) },
- { "Cornsilk1", RGB_(0xff, 0xf8, 0xdc) },
- { "Cornsilk2", RGB_(0xee, 0xe8, 0xcd) },
- { "Cornsilk3", RGB_(0xcd, 0xc8, 0xb1) },
- { "Cornsilk4", RGB_(0x8b, 0x88, 0x78) },
- { "Crimson", RGB_(0xdc, 0x14, 0x3c) },
- { "Cyan", RGB_(0x00, 0xff, 0xff) },
- { "Cyan1", RGB_(0x0, 0xff, 0xff) },
- { "Cyan2", RGB_(0x0, 0xee, 0xee) },
- { "Cyan3", RGB_(0x0, 0xcd, 0xcd) },
- { "Cyan4", RGB_(0x0, 0x8b, 0x8b) },
- { "DarkBlue", RGB_(0x00, 0x00, 0x8b) },
- { "DarkCyan", RGB_(0x00, 0x8b, 0x8b) },
- { "DarkGoldenRod", RGB_(0xb8, 0x86, 0x0b) },
- { "DarkGoldenrod1", RGB_(0xff, 0xb9, 0xf) },
- { "DarkGoldenrod2", RGB_(0xee, 0xad, 0xe) },
- { "DarkGoldenrod3", RGB_(0xcd, 0x95, 0xc) },
- { "DarkGoldenrod4", RGB_(0x8b, 0x65, 0x8) },
- { "DarkGray", RGB_(0xa9, 0xa9, 0xa9) },
- { "DarkGreen", RGB_(0x00, 0x64, 0x00) },
- { "DarkGrey", RGB_(0xa9, 0xa9, 0xa9) },
- { "DarkKhaki", RGB_(0xbd, 0xb7, 0x6b) },
- { "DarkMagenta", RGB_(0x8b, 0x00, 0x8b) },
- { "DarkOliveGreen", RGB_(0x55, 0x6b, 0x2f) },
- { "DarkOliveGreen1", RGB_(0xca, 0xff, 0x70) },
- { "DarkOliveGreen2", RGB_(0xbc, 0xee, 0x68) },
- { "DarkOliveGreen3", RGB_(0xa2, 0xcd, 0x5a) },
- { "DarkOliveGreen4", RGB_(0x6e, 0x8b, 0x3d) },
- { "DarkOrange", RGB_(0xff, 0x8c, 0x00) },
- { "DarkOrange1", RGB_(0xff, 0x7f, 0x0) },
- { "DarkOrange2", RGB_(0xee, 0x76, 0x0) },
- { "DarkOrange3", RGB_(0xcd, 0x66, 0x0) },
- { "DarkOrange4", RGB_(0x8b, 0x45, 0x0) },
- { "DarkOrchid", RGB_(0x99, 0x32, 0xcc) },
- { "DarkOrchid1", RGB_(0xbf, 0x3e, 0xff) },
- { "DarkOrchid2", RGB_(0xb2, 0x3a, 0xee) },
- { "DarkOrchid3", RGB_(0x9a, 0x32, 0xcd) },
- { "DarkOrchid4", RGB_(0x68, 0x22, 0x8b) },
- { "DarkRed", RGB_(0x8b, 0x00, 0x00) },
- { "DarkSalmon", RGB_(0xe9, 0x96, 0x7a) },
- { "DarkSeaGreen", RGB_(0x8f, 0xbc, 0x8f) },
- { "DarkSeaGreen1", RGB_(0xc1, 0xff, 0xc1) },
- { "DarkSeaGreen2", RGB_(0xb4, 0xee, 0xb4) },
- { "DarkSeaGreen3", RGB_(0x9b, 0xcd, 0x9b) },
- { "DarkSeaGreen4", RGB_(0x69, 0x8b, 0x69) },
- { "DarkSlateBlue", RGB_(0x48, 0x3d, 0x8b) },
- { "DarkSlateGray", RGB_(0x2f, 0x4f, 0x4f) },
- { "DarkSlateGray1", RGB_(0x97, 0xff, 0xff) },
- { "DarkSlateGray2", RGB_(0x8d, 0xee, 0xee) },
- { "DarkSlateGray3", RGB_(0x79, 0xcd, 0xcd) },
- { "DarkSlateGray4", RGB_(0x52, 0x8b, 0x8b) },
- { "DarkSlateGrey", RGB_(0x2f, 0x4f, 0x4f) },
- { "DarkTurquoise", RGB_(0x00, 0xce, 0xd1) },
- { "DarkViolet", RGB_(0x94, 0x00, 0xd3) },
- { "DarkYellow", RGB_(0xbb, 0xbb, 0x00) },
- { "DeepPink", RGB_(0xff, 0x14, 0x93) },
- { "DeepPink1", RGB_(0xff, 0x14, 0x93) },
- { "DeepPink2", RGB_(0xee, 0x12, 0x89) },
- { "DeepPink3", RGB_(0xcd, 0x10, 0x76) },
- { "DeepPink4", RGB_(0x8b, 0xa, 0x50) },
- { "DeepSkyBlue", RGB_(0x00, 0xbf, 0xff) },
- { "DeepSkyBlue1", RGB_(0x0, 0xbf, 0xff) },
- { "DeepSkyBlue2", RGB_(0x0, 0xb2, 0xee) },
- { "DeepSkyBlue3", RGB_(0x0, 0x9a, 0xcd) },
- { "DeepSkyBlue4", RGB_(0x0, 0x68, 0x8b) },
- { "DimGray", RGB_(0x69, 0x69, 0x69) },
- { "DimGrey", RGB_(0x69, 0x69, 0x69) },
- { "DodgerBlue", RGB_(0x1e, 0x90, 0xff) },
- { "DodgerBlue1", RGB_(0x1e, 0x90, 0xff) },
- { "DodgerBlue2", RGB_(0x1c, 0x86, 0xee) },
- { "DodgerBlue3", RGB_(0x18, 0x74, 0xcd) },
- { "DodgerBlue4", RGB_(0x10, 0x4e, 0x8b) },
- { "Firebrick", RGB_(0xb2, 0x22, 0x22) },
- { "Firebrick1", RGB_(0xff, 0x30, 0x30) },
- { "Firebrick2", RGB_(0xee, 0x2c, 0x2c) },
- { "Firebrick3", RGB_(0xcd, 0x26, 0x26) },
- { "Firebrick4", RGB_(0x8b, 0x1a, 0x1a) },
- { "FloralWhite", RGB_(0xff, 0xfa, 0xf0) },
- { "ForestGreen", RGB_(0x22, 0x8b, 0x22) },
- { "Fuchsia", RGB_(0xff, 0x00, 0xff) },
- { "Gainsboro", RGB_(0xdc, 0xdc, 0xdc) },
- { "GhostWhite", RGB_(0xf8, 0xf8, 0xff) },
- { "Gold", RGB_(0xff, 0xd7, 0x00) },
- { "Gold1", RGB_(0xff, 0xd7, 0x0) },
- { "Gold2", RGB_(0xee, 0xc9, 0x0) },
- { "Gold3", RGB_(0xcd, 0xad, 0x0) },
- { "Gold4", RGB_(0x8b, 0x75, 0x0) },
- { "GoldenRod", RGB_(0xda, 0xa5, 0x20) },
- { "Goldenrod1", RGB_(0xff, 0xc1, 0x25) },
- { "Goldenrod2", RGB_(0xee, 0xb4, 0x22) },
- { "Goldenrod3", RGB_(0xcd, 0x9b, 0x1d) },
- { "Goldenrod4", RGB_(0x8b, 0x69, 0x14) },
- { "Gray", RGB_(0x80, 0x80, 0x80) },
- { "Gray0", RGB_(0x0, 0x0, 0x0) },
- { "Gray1", RGB_(0x3, 0x3, 0x3) },
- { "Gray10", RGB_(0x1a, 0x1a, 0x1a) },
- { "Gray100", RGB_(0xff, 0xff, 0xff) },
- { "Gray11", RGB_(0x1c, 0x1c, 0x1c) },
- { "Gray12", RGB_(0x1f, 0x1f, 0x1f) },
- { "Gray13", RGB_(0x21, 0x21, 0x21) },
- { "Gray14", RGB_(0x24, 0x24, 0x24) },
- { "Gray15", RGB_(0x26, 0x26, 0x26) },
- { "Gray16", RGB_(0x29, 0x29, 0x29) },
- { "Gray17", RGB_(0x2b, 0x2b, 0x2b) },
- { "Gray18", RGB_(0x2e, 0x2e, 0x2e) },
- { "Gray19", RGB_(0x30, 0x30, 0x30) },
- { "Gray2", RGB_(0x5, 0x5, 0x5) },
- { "Gray20", RGB_(0x33, 0x33, 0x33) },
- { "Gray21", RGB_(0x36, 0x36, 0x36) },
- { "Gray22", RGB_(0x38, 0x38, 0x38) },
- { "Gray23", RGB_(0x3b, 0x3b, 0x3b) },
- { "Gray24", RGB_(0x3d, 0x3d, 0x3d) },
- { "Gray25", RGB_(0x40, 0x40, 0x40) },
- { "Gray26", RGB_(0x42, 0x42, 0x42) },
- { "Gray27", RGB_(0x45, 0x45, 0x45) },
- { "Gray28", RGB_(0x47, 0x47, 0x47) },
- { "Gray29", RGB_(0x4a, 0x4a, 0x4a) },
- { "Gray3", RGB_(0x8, 0x8, 0x8) },
- { "Gray30", RGB_(0x4d, 0x4d, 0x4d) },
- { "Gray31", RGB_(0x4f, 0x4f, 0x4f) },
- { "Gray32", RGB_(0x52, 0x52, 0x52) },
- { "Gray33", RGB_(0x54, 0x54, 0x54) },
- { "Gray34", RGB_(0x57, 0x57, 0x57) },
- { "Gray35", RGB_(0x59, 0x59, 0x59) },
- { "Gray36", RGB_(0x5c, 0x5c, 0x5c) },
- { "Gray37", RGB_(0x5e, 0x5e, 0x5e) },
- { "Gray38", RGB_(0x61, 0x61, 0x61) },
- { "Gray39", RGB_(0x63, 0x63, 0x63) },
- { "Gray4", RGB_(0xa, 0xa, 0xa) },
- { "Gray40", RGB_(0x66, 0x66, 0x66) },
- { "Gray41", RGB_(0x69, 0x69, 0x69) },
- { "Gray42", RGB_(0x6b, 0x6b, 0x6b) },
- { "Gray43", RGB_(0x6e, 0x6e, 0x6e) },
- { "Gray44", RGB_(0x70, 0x70, 0x70) },
- { "Gray45", RGB_(0x73, 0x73, 0x73) },
- { "Gray46", RGB_(0x75, 0x75, 0x75) },
- { "Gray47", RGB_(0x78, 0x78, 0x78) },
- { "Gray48", RGB_(0x7a, 0x7a, 0x7a) },
- { "Gray49", RGB_(0x7d, 0x7d, 0x7d) },
- { "Gray5", RGB_(0xd, 0xd, 0xd) },
- { "Gray50", RGB_(0x7f, 0x7f, 0x7f) },
- { "Gray51", RGB_(0x82, 0x82, 0x82) },
- { "Gray52", RGB_(0x85, 0x85, 0x85) },
- { "Gray53", RGB_(0x87, 0x87, 0x87) },
- { "Gray54", RGB_(0x8a, 0x8a, 0x8a) },
- { "Gray55", RGB_(0x8c, 0x8c, 0x8c) },
- { "Gray56", RGB_(0x8f, 0x8f, 0x8f) },
- { "Gray57", RGB_(0x91, 0x91, 0x91) },
- { "Gray58", RGB_(0x94, 0x94, 0x94) },
- { "Gray59", RGB_(0x96, 0x96, 0x96) },
- { "Gray6", RGB_(0xf, 0xf, 0xf) },
- { "Gray60", RGB_(0x99, 0x99, 0x99) },
- { "Gray61", RGB_(0x9c, 0x9c, 0x9c) },
- { "Gray62", RGB_(0x9e, 0x9e, 0x9e) },
- { "Gray63", RGB_(0xa1, 0xa1, 0xa1) },
- { "Gray64", RGB_(0xa3, 0xa3, 0xa3) },
- { "Gray65", RGB_(0xa6, 0xa6, 0xa6) },
- { "Gray66", RGB_(0xa8, 0xa8, 0xa8) },
- { "Gray67", RGB_(0xab, 0xab, 0xab) },
- { "Gray68", RGB_(0xad, 0xad, 0xad) },
- { "Gray69", RGB_(0xb0, 0xb0, 0xb0) },
- { "Gray7", RGB_(0x12, 0x12, 0x12) },
- { "Gray70", RGB_(0xb3, 0xb3, 0xb3) },
- { "Gray71", RGB_(0xb5, 0xb5, 0xb5) },
- { "Gray72", RGB_(0xb8, 0xb8, 0xb8) },
- { "Gray73", RGB_(0xba, 0xba, 0xba) },
- { "Gray74", RGB_(0xbd, 0xbd, 0xbd) },
- { "Gray75", RGB_(0xbf, 0xbf, 0xbf) },
- { "Gray76", RGB_(0xc2, 0xc2, 0xc2) },
- { "Gray77", RGB_(0xc4, 0xc4, 0xc4) },
- { "Gray78", RGB_(0xc7, 0xc7, 0xc7) },
- { "Gray79", RGB_(0xc9, 0xc9, 0xc9) },
- { "Gray8", RGB_(0x14, 0x14, 0x14) },
- { "Gray80", RGB_(0xcc, 0xcc, 0xcc) },
- { "Gray81", RGB_(0xcf, 0xcf, 0xcf) },
- { "Gray82", RGB_(0xd1, 0xd1, 0xd1) },
- { "Gray83", RGB_(0xd4, 0xd4, 0xd4) },
- { "Gray84", RGB_(0xd6, 0xd6, 0xd6) },
- { "Gray85", RGB_(0xd9, 0xd9, 0xd9) },
- { "Gray86", RGB_(0xdb, 0xdb, 0xdb) },
- { "Gray87", RGB_(0xde, 0xde, 0xde) },
- { "Gray88", RGB_(0xe0, 0xe0, 0xe0) },
- { "Gray89", RGB_(0xe3, 0xe3, 0xe3) },
- { "Gray9", RGB_(0x17, 0x17, 0x17) },
- { "Gray90", RGB_(0xe5, 0xe5, 0xe5) },
- { "Gray91", RGB_(0xe8, 0xe8, 0xe8) },
- { "Gray92", RGB_(0xeb, 0xeb, 0xeb) },
- { "Gray93", RGB_(0xed, 0xed, 0xed) },
- { "Gray94", RGB_(0xf0, 0xf0, 0xf0) },
- { "Gray95", RGB_(0xf2, 0xf2, 0xf2) },
- { "Gray96", RGB_(0xf5, 0xf5, 0xf5) },
- { "Gray97", RGB_(0xf7, 0xf7, 0xf7) },
- { "Gray98", RGB_(0xfa, 0xfa, 0xfa) },
- { "Gray99", RGB_(0xfc, 0xfc, 0xfc) },
- { "Green", RGB_(0x00, 0x80, 0x00) },
- { "Green1", RGB_(0x0, 0xff, 0x0) },
- { "Green2", RGB_(0x0, 0xee, 0x0) },
- { "Green3", RGB_(0x0, 0xcd, 0x0) },
- { "Green4", RGB_(0x0, 0x8b, 0x0) },
- { "GreenYellow", RGB_(0xad, 0xff, 0x2f) },
- { "Grey", RGB_(0x80, 0x80, 0x80) },
- { "Grey0", RGB_(0x0, 0x0, 0x0) },
- { "Grey1", RGB_(0x3, 0x3, 0x3) },
- { "Grey10", RGB_(0x1a, 0x1a, 0x1a) },
- { "Grey100", RGB_(0xff, 0xff, 0xff) },
- { "Grey11", RGB_(0x1c, 0x1c, 0x1c) },
- { "Grey12", RGB_(0x1f, 0x1f, 0x1f) },
- { "Grey13", RGB_(0x21, 0x21, 0x21) },
- { "Grey14", RGB_(0x24, 0x24, 0x24) },
- { "Grey15", RGB_(0x26, 0x26, 0x26) },
- { "Grey16", RGB_(0x29, 0x29, 0x29) },
- { "Grey17", RGB_(0x2b, 0x2b, 0x2b) },
- { "Grey18", RGB_(0x2e, 0x2e, 0x2e) },
- { "Grey19", RGB_(0x30, 0x30, 0x30) },
- { "Grey2", RGB_(0x5, 0x5, 0x5) },
- { "Grey20", RGB_(0x33, 0x33, 0x33) },
- { "Grey21", RGB_(0x36, 0x36, 0x36) },
- { "Grey22", RGB_(0x38, 0x38, 0x38) },
- { "Grey23", RGB_(0x3b, 0x3b, 0x3b) },
- { "Grey24", RGB_(0x3d, 0x3d, 0x3d) },
- { "Grey25", RGB_(0x40, 0x40, 0x40) },
- { "Grey26", RGB_(0x42, 0x42, 0x42) },
- { "Grey27", RGB_(0x45, 0x45, 0x45) },
- { "Grey28", RGB_(0x47, 0x47, 0x47) },
- { "Grey29", RGB_(0x4a, 0x4a, 0x4a) },
- { "Grey3", RGB_(0x8, 0x8, 0x8) },
- { "Grey30", RGB_(0x4d, 0x4d, 0x4d) },
- { "Grey31", RGB_(0x4f, 0x4f, 0x4f) },
- { "Grey32", RGB_(0x52, 0x52, 0x52) },
- { "Grey33", RGB_(0x54, 0x54, 0x54) },
- { "Grey34", RGB_(0x57, 0x57, 0x57) },
- { "Grey35", RGB_(0x59, 0x59, 0x59) },
- { "Grey36", RGB_(0x5c, 0x5c, 0x5c) },
- { "Grey37", RGB_(0x5e, 0x5e, 0x5e) },
- { "Grey38", RGB_(0x61, 0x61, 0x61) },
- { "Grey39", RGB_(0x63, 0x63, 0x63) },
- { "Grey4", RGB_(0xa, 0xa, 0xa) },
- { "Grey40", RGB_(0x66, 0x66, 0x66) },
- { "Grey41", RGB_(0x69, 0x69, 0x69) },
- { "Grey42", RGB_(0x6b, 0x6b, 0x6b) },
- { "Grey43", RGB_(0x6e, 0x6e, 0x6e) },
- { "Grey44", RGB_(0x70, 0x70, 0x70) },
- { "Grey45", RGB_(0x73, 0x73, 0x73) },
- { "Grey46", RGB_(0x75, 0x75, 0x75) },
- { "Grey47", RGB_(0x78, 0x78, 0x78) },
- { "Grey48", RGB_(0x7a, 0x7a, 0x7a) },
- { "Grey49", RGB_(0x7d, 0x7d, 0x7d) },
- { "Grey5", RGB_(0xd, 0xd, 0xd) },
- { "Grey50", RGB_(0x7f, 0x7f, 0x7f) },
- { "Grey51", RGB_(0x82, 0x82, 0x82) },
- { "Grey52", RGB_(0x85, 0x85, 0x85) },
- { "Grey53", RGB_(0x87, 0x87, 0x87) },
- { "Grey54", RGB_(0x8a, 0x8a, 0x8a) },
- { "Grey55", RGB_(0x8c, 0x8c, 0x8c) },
- { "Grey56", RGB_(0x8f, 0x8f, 0x8f) },
- { "Grey57", RGB_(0x91, 0x91, 0x91) },
- { "Grey58", RGB_(0x94, 0x94, 0x94) },
- { "Grey59", RGB_(0x96, 0x96, 0x96) },
- { "Grey6", RGB_(0xf, 0xf, 0xf) },
- { "Grey60", RGB_(0x99, 0x99, 0x99) },
- { "Grey61", RGB_(0x9c, 0x9c, 0x9c) },
- { "Grey62", RGB_(0x9e, 0x9e, 0x9e) },
- { "Grey63", RGB_(0xa1, 0xa1, 0xa1) },
- { "Grey64", RGB_(0xa3, 0xa3, 0xa3) },
- { "Grey65", RGB_(0xa6, 0xa6, 0xa6) },
- { "Grey66", RGB_(0xa8, 0xa8, 0xa8) },
- { "Grey67", RGB_(0xab, 0xab, 0xab) },
- { "Grey68", RGB_(0xad, 0xad, 0xad) },
- { "Grey69", RGB_(0xb0, 0xb0, 0xb0) },
- { "Grey7", RGB_(0x12, 0x12, 0x12) },
- { "Grey70", RGB_(0xb3, 0xb3, 0xb3) },
- { "Grey71", RGB_(0xb5, 0xb5, 0xb5) },
- { "Grey72", RGB_(0xb8, 0xb8, 0xb8) },
- { "Grey73", RGB_(0xba, 0xba, 0xba) },
- { "Grey74", RGB_(0xbd, 0xbd, 0xbd) },
- { "Grey75", RGB_(0xbf, 0xbf, 0xbf) },
- { "Grey76", RGB_(0xc2, 0xc2, 0xc2) },
- { "Grey77", RGB_(0xc4, 0xc4, 0xc4) },
- { "Grey78", RGB_(0xc7, 0xc7, 0xc7) },
- { "Grey79", RGB_(0xc9, 0xc9, 0xc9) },
- { "Grey8", RGB_(0x14, 0x14, 0x14) },
- { "Grey80", RGB_(0xcc, 0xcc, 0xcc) },
- { "Grey81", RGB_(0xcf, 0xcf, 0xcf) },
- { "Grey82", RGB_(0xd1, 0xd1, 0xd1) },
- { "Grey83", RGB_(0xd4, 0xd4, 0xd4) },
- { "Grey84", RGB_(0xd6, 0xd6, 0xd6) },
- { "Grey85", RGB_(0xd9, 0xd9, 0xd9) },
- { "Grey86", RGB_(0xdb, 0xdb, 0xdb) },
- { "Grey87", RGB_(0xde, 0xde, 0xde) },
- { "Grey88", RGB_(0xe0, 0xe0, 0xe0) },
- { "Grey89", RGB_(0xe3, 0xe3, 0xe3) },
- { "Grey9", RGB_(0x17, 0x17, 0x17) },
- { "Grey90", RGB_(0xe5, 0xe5, 0xe5) },
- { "Grey91", RGB_(0xe8, 0xe8, 0xe8) },
- { "Grey92", RGB_(0xeb, 0xeb, 0xeb) },
- { "Grey93", RGB_(0xed, 0xed, 0xed) },
- { "Grey94", RGB_(0xf0, 0xf0, 0xf0) },
- { "Grey95", RGB_(0xf2, 0xf2, 0xf2) },
- { "Grey96", RGB_(0xf5, 0xf5, 0xf5) },
- { "Grey97", RGB_(0xf7, 0xf7, 0xf7) },
- { "Grey98", RGB_(0xfa, 0xfa, 0xfa) },
- { "Grey99", RGB_(0xfc, 0xfc, 0xfc) },
- { "Honeydew", RGB_(0xf0, 0xff, 0xf0) },
- { "Honeydew1", RGB_(0xf0, 0xff, 0xf0) },
- { "Honeydew2", RGB_(0xe0, 0xee, 0xe0) },
- { "Honeydew3", RGB_(0xc1, 0xcd, 0xc1) },
- { "Honeydew4", RGB_(0x83, 0x8b, 0x83) },
- { "HotPink", RGB_(0xff, 0x69, 0xb4) },
- { "HotPink1", RGB_(0xff, 0x6e, 0xb4) },
- { "HotPink2", RGB_(0xee, 0x6a, 0xa7) },
- { "HotPink3", RGB_(0xcd, 0x60, 0x90) },
- { "HotPink4", RGB_(0x8b, 0x3a, 0x62) },
- { "IndianRed", RGB_(0xcd, 0x5c, 0x5c) },
- { "IndianRed1", RGB_(0xff, 0x6a, 0x6a) },
- { "IndianRed2", RGB_(0xee, 0x63, 0x63) },
- { "IndianRed3", RGB_(0xcd, 0x55, 0x55) },
- { "IndianRed4", RGB_(0x8b, 0x3a, 0x3a) },
- { "Indigo", RGB_(0x4b, 0x00, 0x82) },
- { "Ivory", RGB_(0xff, 0xff, 0xf0) },
- { "Ivory1", RGB_(0xff, 0xff, 0xf0) },
- { "Ivory2", RGB_(0xee, 0xee, 0xe0) },
- { "Ivory3", RGB_(0xcd, 0xcd, 0xc1) },
- { "Ivory4", RGB_(0x8b, 0x8b, 0x83) },
- { "Khaki", RGB_(0xf0, 0xe6, 0x8c) },
- { "Khaki1", RGB_(0xff, 0xf6, 0x8f) },
- { "Khaki2", RGB_(0xee, 0xe6, 0x85) },
- { "Khaki3", RGB_(0xcd, 0xc6, 0x73) },
- { "Khaki4", RGB_(0x8b, 0x86, 0x4e) },
- { "Lavender", RGB_(0xe6, 0xe6, 0xfa) },
- { "LavenderBlush", RGB_(0xff, 0xf0, 0xf5) },
- { "LavenderBlush1", RGB_(0xff, 0xf0, 0xf5) },
- { "LavenderBlush2", RGB_(0xee, 0xe0, 0xe5) },
- { "LavenderBlush3", RGB_(0xcd, 0xc1, 0xc5) },
- { "LavenderBlush4", RGB_(0x8b, 0x83, 0x86) },
- { "LawnGreen", RGB_(0x7c, 0xfc, 0x00) },
- { "LemonChiffon", RGB_(0xff, 0xfa, 0xcd) },
- { "LemonChiffon1", RGB_(0xff, 0xfa, 0xcd) },
- { "LemonChiffon2", RGB_(0xee, 0xe9, 0xbf) },
- { "LemonChiffon3", RGB_(0xcd, 0xc9, 0xa5) },
- { "LemonChiffon4", RGB_(0x8b, 0x89, 0x70) },
- { "LightBlue", RGB_(0xad, 0xd8, 0xe6) },
- { "LightBlue1", RGB_(0xbf, 0xef, 0xff) },
- { "LightBlue2", RGB_(0xb2, 0xdf, 0xee) },
- { "LightBlue3", RGB_(0x9a, 0xc0, 0xcd) },
- { "LightBlue4", RGB_(0x68, 0x83, 0x8b) },
- { "LightCoral", RGB_(0xf0, 0x80, 0x80) },
- { "LightCyan", RGB_(0xe0, 0xff, 0xff) },
- { "LightCyan1", RGB_(0xe0, 0xff, 0xff) },
- { "LightCyan2", RGB_(0xd1, 0xee, 0xee) },
- { "LightCyan3", RGB_(0xb4, 0xcd, 0xcd) },
- { "LightCyan4", RGB_(0x7a, 0x8b, 0x8b) },
- { "LightGoldenrod", RGB_(0xee, 0xdd, 0x82) },
- { "LightGoldenrod1", RGB_(0xff, 0xec, 0x8b) },
- { "LightGoldenrod2", RGB_(0xee, 0xdc, 0x82) },
- { "LightGoldenrod3", RGB_(0xcd, 0xbe, 0x70) },
- { "LightGoldenrod4", RGB_(0x8b, 0x81, 0x4c) },
- { "LightGoldenRodYellow", RGB_(0xfa, 0xfa, 0xd2) },
- { "LightGray", RGB_(0xd3, 0xd3, 0xd3) },
- { "LightGreen", RGB_(0x90, 0xee, 0x90) },
- { "LightGrey", RGB_(0xd3, 0xd3, 0xd3) },
- { "LightMagenta", RGB_(0xff, 0xbb, 0xff) },
- { "LightPink", RGB_(0xff, 0xb6, 0xc1) },
- { "LightPink1", RGB_(0xff, 0xae, 0xb9) },
- { "LightPink2", RGB_(0xee, 0xa2, 0xad) },
- { "LightPink3", RGB_(0xcd, 0x8c, 0x95) },
- { "LightPink4", RGB_(0x8b, 0x5f, 0x65) },
- { "LightRed", RGB_(0xff, 0xbb, 0xbb) },
- { "LightSalmon", RGB_(0xff, 0xa0, 0x7a) },
- { "LightSalmon1", RGB_(0xff, 0xa0, 0x7a) },
- { "LightSalmon2", RGB_(0xee, 0x95, 0x72) },
- { "LightSalmon3", RGB_(0xcd, 0x81, 0x62) },
- { "LightSalmon4", RGB_(0x8b, 0x57, 0x42) },
- { "LightSeaGreen", RGB_(0x20, 0xb2, 0xaa) },
- { "LightSkyBlue", RGB_(0x87, 0xce, 0xfa) },
- { "LightSkyBlue1", RGB_(0xb0, 0xe2, 0xff) },
- { "LightSkyBlue2", RGB_(0xa4, 0xd3, 0xee) },
- { "LightSkyBlue3", RGB_(0x8d, 0xb6, 0xcd) },
- { "LightSkyBlue4", RGB_(0x60, 0x7b, 0x8b) },
- { "LightSlateBlue", RGB_(0x84, 0x70, 0xff) },
- { "LightSlateGray", RGB_(0x77, 0x88, 0x99) },
- { "LightSlateGrey", RGB_(0x77, 0x88, 0x99) },
- { "LightSteelBlue", RGB_(0xb0, 0xc4, 0xde) },
- { "LightSteelBlue1", RGB_(0xca, 0xe1, 0xff) },
- { "LightSteelBlue2", RGB_(0xbc, 0xd2, 0xee) },
- { "LightSteelBlue3", RGB_(0xa2, 0xb5, 0xcd) },
- { "LightSteelBlue4", RGB_(0x6e, 0x7b, 0x8b) },
- { "LightYellow", RGB_(0xff, 0xff, 0xe0) },
- { "LightYellow1", RGB_(0xff, 0xff, 0xe0) },
- { "LightYellow2", RGB_(0xee, 0xee, 0xd1) },
- { "LightYellow3", RGB_(0xcd, 0xcd, 0xb4) },
- { "LightYellow4", RGB_(0x8b, 0x8b, 0x7a) },
- { "Lime", RGB_(0x00, 0xff, 0x00) },
- { "LimeGreen", RGB_(0x32, 0xcd, 0x32) },
- { "Linen", RGB_(0xfa, 0xf0, 0xe6) },
- { "Magenta", RGB_(0xff, 0x00, 0xff) },
- { "Magenta1", RGB_(0xff, 0x0, 0xff) },
- { "Magenta2", RGB_(0xee, 0x0, 0xee) },
- { "Magenta3", RGB_(0xcd, 0x0, 0xcd) },
- { "Magenta4", RGB_(0x8b, 0x0, 0x8b) },
- { "Maroon", RGB_(0x80, 0x00, 0x00) },
- { "Maroon1", RGB_(0xff, 0x34, 0xb3) },
- { "Maroon2", RGB_(0xee, 0x30, 0xa7) },
- { "Maroon3", RGB_(0xcd, 0x29, 0x90) },
- { "Maroon4", RGB_(0x8b, 0x1c, 0x62) },
- { "MediumAquamarine", RGB_(0x66, 0xcd, 0xaa) },
- { "MediumBlue", RGB_(0x00, 0x00, 0xcd) },
- { "MediumOrchid", RGB_(0xba, 0x55, 0xd3) },
- { "MediumOrchid1", RGB_(0xe0, 0x66, 0xff) },
- { "MediumOrchid2", RGB_(0xd1, 0x5f, 0xee) },
- { "MediumOrchid3", RGB_(0xb4, 0x52, 0xcd) },
- { "MediumOrchid4", RGB_(0x7a, 0x37, 0x8b) },
- { "MediumPurple", RGB_(0x93, 0x70, 0xdb) },
- { "MediumPurple1", RGB_(0xab, 0x82, 0xff) },
- { "MediumPurple2", RGB_(0x9f, 0x79, 0xee) },
- { "MediumPurple3", RGB_(0x89, 0x68, 0xcd) },
- { "MediumPurple4", RGB_(0x5d, 0x47, 0x8b) },
- { "MediumSeaGreen", RGB_(0x3c, 0xb3, 0x71) },
- { "MediumSlateBlue", RGB_(0x7b, 0x68, 0xee) },
- { "MediumSpringGreen", RGB_(0x00, 0xfa, 0x9a) },
- { "MediumTurquoise", RGB_(0x48, 0xd1, 0xcc) },
- { "MediumVioletRed", RGB_(0xc7, 0x15, 0x85) },
- { "MidnightBlue", RGB_(0x19, 0x19, 0x70) },
- { "MintCream", RGB_(0xf5, 0xff, 0xfa) },
- { "MistyRose", RGB_(0xff, 0xe4, 0xe1) },
- { "MistyRose1", RGB_(0xff, 0xe4, 0xe1) },
- { "MistyRose2", RGB_(0xee, 0xd5, 0xd2) },
- { "MistyRose3", RGB_(0xcd, 0xb7, 0xb5) },
- { "MistyRose4", RGB_(0x8b, 0x7d, 0x7b) },
- { "Moccasin", RGB_(0xff, 0xe4, 0xb5) },
- { "NavajoWhite", RGB_(0xff, 0xde, 0xad) },
- { "NavajoWhite1", RGB_(0xff, 0xde, 0xad) },
- { "NavajoWhite2", RGB_(0xee, 0xcf, 0xa1) },
- { "NavajoWhite3", RGB_(0xcd, 0xb3, 0x8b) },
- { "NavajoWhite4", RGB_(0x8b, 0x79, 0x5e) },
- { "Navy", RGB_(0x00, 0x00, 0x80) },
- { "NavyBlue", RGB_(0x0, 0x0, 0x80) },
- { "OldLace", RGB_(0xfd, 0xf5, 0xe6) },
- { "Olive", RGB_(0x80, 0x80, 0x00) },
- { "OliveDrab", RGB_(0x6b, 0x8e, 0x23) },
- { "OliveDrab1", RGB_(0xc0, 0xff, 0x3e) },
- { "OliveDrab2", RGB_(0xb3, 0xee, 0x3a) },
- { "OliveDrab3", RGB_(0x9a, 0xcd, 0x32) },
- { "OliveDrab4", RGB_(0x69, 0x8b, 0x22) },
- { "Orange", RGB_(0xff, 0xa5, 0x00) },
- { "Orange1", RGB_(0xff, 0xa5, 0x0) },
- { "Orange2", RGB_(0xee, 0x9a, 0x0) },
- { "Orange3", RGB_(0xcd, 0x85, 0x0) },
- { "Orange4", RGB_(0x8b, 0x5a, 0x0) },
- { "OrangeRed", RGB_(0xff, 0x45, 0x00) },
- { "OrangeRed1", RGB_(0xff, 0x45, 0x0) },
- { "OrangeRed2", RGB_(0xee, 0x40, 0x0) },
- { "OrangeRed3", RGB_(0xcd, 0x37, 0x0) },
- { "OrangeRed4", RGB_(0x8b, 0x25, 0x0) },
- { "Orchid", RGB_(0xda, 0x70, 0xd6) },
- { "Orchid1", RGB_(0xff, 0x83, 0xfa) },
- { "Orchid2", RGB_(0xee, 0x7a, 0xe9) },
- { "Orchid3", RGB_(0xcd, 0x69, 0xc9) },
- { "Orchid4", RGB_(0x8b, 0x47, 0x89) },
- { "PaleGoldenRod", RGB_(0xee, 0xe8, 0xaa) },
- { "PaleGreen", RGB_(0x98, 0xfb, 0x98) },
- { "PaleGreen1", RGB_(0x9a, 0xff, 0x9a) },
- { "PaleGreen2", RGB_(0x90, 0xee, 0x90) },
- { "PaleGreen3", RGB_(0x7c, 0xcd, 0x7c) },
- { "PaleGreen4", RGB_(0x54, 0x8b, 0x54) },
- { "PaleTurquoise", RGB_(0xaf, 0xee, 0xee) },
- { "PaleTurquoise1", RGB_(0xbb, 0xff, 0xff) },
- { "PaleTurquoise2", RGB_(0xae, 0xee, 0xee) },
- { "PaleTurquoise3", RGB_(0x96, 0xcd, 0xcd) },
- { "PaleTurquoise4", RGB_(0x66, 0x8b, 0x8b) },
- { "PaleVioletRed", RGB_(0xdb, 0x70, 0x93) },
- { "PaleVioletRed1", RGB_(0xff, 0x82, 0xab) },
- { "PaleVioletRed2", RGB_(0xee, 0x79, 0x9f) },
- { "PaleVioletRed3", RGB_(0xcd, 0x68, 0x89) },
- { "PaleVioletRed4", RGB_(0x8b, 0x47, 0x5d) },
- { "PapayaWhip", RGB_(0xff, 0xef, 0xd5) },
- { "PeachPuff", RGB_(0xff, 0xda, 0xb9) },
- { "PeachPuff1", RGB_(0xff, 0xda, 0xb9) },
- { "PeachPuff2", RGB_(0xee, 0xcb, 0xad) },
- { "PeachPuff3", RGB_(0xcd, 0xaf, 0x95) },
- { "PeachPuff4", RGB_(0x8b, 0x77, 0x65) },
- { "Peru", RGB_(0xcd, 0x85, 0x3f) },
- { "Pink", RGB_(0xff, 0xc0, 0xcb) },
- { "Pink1", RGB_(0xff, 0xb5, 0xc5) },
- { "Pink2", RGB_(0xee, 0xa9, 0xb8) },
- { "Pink3", RGB_(0xcd, 0x91, 0x9e) },
- { "Pink4", RGB_(0x8b, 0x63, 0x6c) },
- { "Plum", RGB_(0xdd, 0xa0, 0xdd) },
- { "Plum1", RGB_(0xff, 0xbb, 0xff) },
- { "Plum2", RGB_(0xee, 0xae, 0xee) },
- { "Plum3", RGB_(0xcd, 0x96, 0xcd) },
- { "Plum4", RGB_(0x8b, 0x66, 0x8b) },
- { "PowderBlue", RGB_(0xb0, 0xe0, 0xe6) },
- { "Purple", RGB_(0x80, 0x00, 0x80) },
- { "Purple1", RGB_(0x9b, 0x30, 0xff) },
- { "Purple2", RGB_(0x91, 0x2c, 0xee) },
- { "Purple3", RGB_(0x7d, 0x26, 0xcd) },
- { "Purple4", RGB_(0x55, 0x1a, 0x8b) },
- { "RebeccaPurple", RGB_(0x66, 0x33, 0x99) },
- { "Red", RGB_(0xff, 0x00, 0x00) },
- { "Red1", RGB_(0xff, 0x0, 0x0) },
- { "Red2", RGB_(0xee, 0x0, 0x0) },
- { "Red3", RGB_(0xcd, 0x0, 0x0) },
- { "Red4", RGB_(0x8b, 0x0, 0x0) },
- { "RosyBrown", RGB_(0xbc, 0x8f, 0x8f) },
- { "RosyBrown1", RGB_(0xff, 0xc1, 0xc1) },
- { "RosyBrown2", RGB_(0xee, 0xb4, 0xb4) },
- { "RosyBrown3", RGB_(0xcd, 0x9b, 0x9b) },
- { "RosyBrown4", RGB_(0x8b, 0x69, 0x69) },
- { "RoyalBlue", RGB_(0x41, 0x69, 0xe1) },
- { "RoyalBlue1", RGB_(0x48, 0x76, 0xff) },
- { "RoyalBlue2", RGB_(0x43, 0x6e, 0xee) },
- { "RoyalBlue3", RGB_(0x3a, 0x5f, 0xcd) },
- { "RoyalBlue4", RGB_(0x27, 0x40, 0x8b) },
- { "SaddleBrown", RGB_(0x8b, 0x45, 0x13) },
- { "Salmon", RGB_(0xfa, 0x80, 0x72) },
- { "Salmon1", RGB_(0xff, 0x8c, 0x69) },
- { "Salmon2", RGB_(0xee, 0x82, 0x62) },
- { "Salmon3", RGB_(0xcd, 0x70, 0x54) },
- { "Salmon4", RGB_(0x8b, 0x4c, 0x39) },
- { "SandyBrown", RGB_(0xf4, 0xa4, 0x60) },
- { "SeaGreen", RGB_(0x2e, 0x8b, 0x57) },
- { "SeaGreen1", RGB_(0x54, 0xff, 0x9f) },
- { "SeaGreen2", RGB_(0x4e, 0xee, 0x94) },
- { "SeaGreen3", RGB_(0x43, 0xcd, 0x80) },
- { "SeaGreen4", RGB_(0x2e, 0x8b, 0x57) },
- { "SeaShell", RGB_(0xff, 0xf5, 0xee) },
- { "Seashell1", RGB_(0xff, 0xf5, 0xee) },
- { "Seashell2", RGB_(0xee, 0xe5, 0xde) },
- { "Seashell3", RGB_(0xcd, 0xc5, 0xbf) },
- { "Seashell4", RGB_(0x8b, 0x86, 0x82) },
- { "Sienna", RGB_(0xa0, 0x52, 0x2d) },
- { "Sienna1", RGB_(0xff, 0x82, 0x47) },
- { "Sienna2", RGB_(0xee, 0x79, 0x42) },
- { "Sienna3", RGB_(0xcd, 0x68, 0x39) },
- { "Sienna4", RGB_(0x8b, 0x47, 0x26) },
- { "Silver", RGB_(0xc0, 0xc0, 0xc0) },
- { "SkyBlue", RGB_(0x87, 0xce, 0xeb) },
- { "SkyBlue1", RGB_(0x87, 0xce, 0xff) },
- { "SkyBlue2", RGB_(0x7e, 0xc0, 0xee) },
- { "SkyBlue3", RGB_(0x6c, 0xa6, 0xcd) },
- { "SkyBlue4", RGB_(0x4a, 0x70, 0x8b) },
- { "SlateBlue", RGB_(0x6a, 0x5a, 0xcd) },
- { "SlateBlue1", RGB_(0x83, 0x6f, 0xff) },
- { "SlateBlue2", RGB_(0x7a, 0x67, 0xee) },
- { "SlateBlue3", RGB_(0x69, 0x59, 0xcd) },
- { "SlateBlue4", RGB_(0x47, 0x3c, 0x8b) },
- { "SlateGray", RGB_(0x70, 0x80, 0x90) },
- { "SlateGray1", RGB_(0xc6, 0xe2, 0xff) },
- { "SlateGray2", RGB_(0xb9, 0xd3, 0xee) },
- { "SlateGray3", RGB_(0x9f, 0xb6, 0xcd) },
- { "SlateGray4", RGB_(0x6c, 0x7b, 0x8b) },
- { "SlateGrey", RGB_(0x70, 0x80, 0x90) },
- { "Snow", RGB_(0xff, 0xfa, 0xfa) },
- { "Snow1", RGB_(0xff, 0xfa, 0xfa) },
- { "Snow2", RGB_(0xee, 0xe9, 0xe9) },
- { "Snow3", RGB_(0xcd, 0xc9, 0xc9) },
- { "Snow4", RGB_(0x8b, 0x89, 0x89) },
- { "SpringGreen", RGB_(0x00, 0xff, 0x7f) },
- { "SpringGreen1", RGB_(0x0, 0xff, 0x7f) },
- { "SpringGreen2", RGB_(0x0, 0xee, 0x76) },
- { "SpringGreen3", RGB_(0x0, 0xcd, 0x66) },
- { "SpringGreen4", RGB_(0x0, 0x8b, 0x45) },
- { "SteelBlue", RGB_(0x46, 0x82, 0xb4) },
- { "SteelBlue1", RGB_(0x63, 0xb8, 0xff) },
- { "SteelBlue2", RGB_(0x5c, 0xac, 0xee) },
- { "SteelBlue3", RGB_(0x4f, 0x94, 0xcd) },
- { "SteelBlue4", RGB_(0x36, 0x64, 0x8b) },
- { "Tan", RGB_(0xd2, 0xb4, 0x8c) },
- { "Tan1", RGB_(0xff, 0xa5, 0x4f) },
- { "Tan2", RGB_(0xee, 0x9a, 0x49) },
- { "Tan3", RGB_(0xcd, 0x85, 0x3f) },
- { "Tan4", RGB_(0x8b, 0x5a, 0x2b) },
- { "Teal", RGB_(0x00, 0x80, 0x80) },
- { "Thistle", RGB_(0xd8, 0xbf, 0xd8) },
- { "Thistle1", RGB_(0xff, 0xe1, 0xff) },
- { "Thistle2", RGB_(0xee, 0xd2, 0xee) },
- { "Thistle3", RGB_(0xcd, 0xb5, 0xcd) },
- { "Thistle4", RGB_(0x8b, 0x7b, 0x8b) },
- { "Tomato", RGB_(0xff, 0x63, 0x47) },
- { "Tomato1", RGB_(0xff, 0x63, 0x47) },
- { "Tomato2", RGB_(0xee, 0x5c, 0x42) },
- { "Tomato3", RGB_(0xcd, 0x4f, 0x39) },
- { "Tomato4", RGB_(0x8b, 0x36, 0x26) },
- { "Turquoise", RGB_(0x40, 0xe0, 0xd0) },
- { "Turquoise1", RGB_(0x0, 0xf5, 0xff) },
- { "Turquoise2", RGB_(0x0, 0xe5, 0xee) },
- { "Turquoise3", RGB_(0x0, 0xc5, 0xcd) },
- { "Turquoise4", RGB_(0x0, 0x86, 0x8b) },
- { "Violet", RGB_(0xee, 0x82, 0xee) },
- { "VioletRed", RGB_(0xd0, 0x20, 0x90) },
- { "VioletRed1", RGB_(0xff, 0x3e, 0x96) },
- { "VioletRed2", RGB_(0xee, 0x3a, 0x8c) },
- { "VioletRed3", RGB_(0xcd, 0x32, 0x78) },
- { "VioletRed4", RGB_(0x8b, 0x22, 0x52) },
- { "WebGray", RGB_(0x80, 0x80, 0x80) },
- { "WebGreen", RGB_(0x0, 0x80, 0x0) },
- { "WebGrey", RGB_(0x80, 0x80, 0x80) },
- { "WebMaroon", RGB_(0x80, 0x0, 0x0) },
- { "WebPurple", RGB_(0x80, 0x0, 0x80) },
- { "Wheat", RGB_(0xf5, 0xde, 0xb3) },
- { "Wheat1", RGB_(0xff, 0xe7, 0xba) },
- { "Wheat2", RGB_(0xee, 0xd8, 0xae) },
- { "Wheat3", RGB_(0xcd, 0xba, 0x96) },
- { "Wheat4", RGB_(0x8b, 0x7e, 0x66) },
- { "White", RGB_(0xff, 0xff, 0xff) },
- { "WhiteSmoke", RGB_(0xf5, 0xf5, 0xf5) },
- { "X11Gray", RGB_(0xbe, 0xbe, 0xbe) },
- { "X11Green", RGB_(0x0, 0xff, 0x0) },
- { "X11Grey", RGB_(0xbe, 0xbe, 0xbe) },
- { "X11Maroon", RGB_(0xb0, 0x30, 0x60) },
- { "X11Purple", RGB_(0xa0, 0x20, 0xf0) },
- { "Yellow", RGB_(0xff, 0xff, 0x00) },
- { "Yellow1", RGB_(0xff, 0xff, 0x0) },
- { "Yellow2", RGB_(0xee, 0xee, 0x0) },
- { "Yellow3", RGB_(0xcd, 0xcd, 0x0) },
- { "Yellow4", RGB_(0x8b, 0x8b, 0x0) },
- { "YellowGreen", RGB_(0x9a, 0xcd, 0x32) },
- { NULL, 0 },
-};
-
-
-/// Translate to RgbValue if \p name is an hex value (e.g. #XXXXXX),
-/// else look into color_name_table to translate a color name to its
-/// hex value
-///
-/// @param[in] name string value to convert to RGB
-/// return the hex value or -1 if could not find a correct value
-RgbValue name_to_color(const char *name)
-{
- if (name[0] == '#' && isxdigit(name[1]) && isxdigit(name[2])
- && isxdigit(name[3]) && isxdigit(name[4]) && isxdigit(name[5])
- && isxdigit(name[6]) && name[7] == NUL) {
- // rgb hex string
- return strtol((char *)(name + 1), NULL, 16);
- } else if (!STRICMP(name, "bg") || !STRICMP(name, "background")) {
- return normal_bg;
- } else if (!STRICMP(name, "fg") || !STRICMP(name, "foreground")) {
- return normal_fg;
- }
-
- for (int i = 0; color_name_table[i].name != NULL; i++) {
- if (!STRICMP(name, color_name_table[i].name)) {
- return color_name_table[i].color;
- }
- }
-
- return -1;
-}
-
-
-/**************************************
-* End of Highlighting stuff *
-**************************************/
diff --git a/src/nvim/syntax.h b/src/nvim/syntax.h
index 15fc084a0a..0d890314c5 100644
--- a/src/nvim/syntax.h
+++ b/src/nvim/syntax.h
@@ -29,12 +29,6 @@
#define SYN_GROUP_STATIC(s) syn_check_group(S_LEN(s))
-typedef struct {
- char *name;
- RgbValue color;
-} color_name_table_T;
-extern color_name_table_T color_name_table[];
-
/// Array of highlight definitions, used for unit testing
extern const char *const highlight_init_cmdline[];
diff --git a/src/nvim/syntax_defs.h b/src/nvim/syntax_defs.h
index 526be905e9..c656f21181 100644
--- a/src/nvim/syntax_defs.h
+++ b/src/nvim/syntax_defs.h
@@ -7,7 +7,7 @@
#define SST_MAX_ENTRIES 1000 // maximal size for state stack array
#define SST_FIX_STATES 7 // size of sst_stack[].
#define SST_DIST 16 // normal distance between entries
-#define SST_INVALID (synstate_T *)-1 // invalid syn_state pointer
+#define SST_INVALID ((synstate_T *)-1) // invalid syn_state pointer
typedef struct syn_state synstate_T;
@@ -21,9 +21,7 @@ struct sp_syn {
int16_t *cont_in_list; // cont.in group IDs, if non-zero
};
-/*
- * Each keyword has one keyentry, which is linked in a hash list.
- */
+// Each keyword has one keyentry, which is linked in a hash list.
typedef struct keyentry keyentry_T;
struct keyentry {
@@ -35,9 +33,7 @@ struct keyentry {
char_u keyword[1]; // actually longer
};
-/*
- * Struct used to store one state of the state stack.
- */
+// Struct used to store one state of the state stack.
typedef struct buf_state {
int bs_idx; // index of pattern
int bs_flags; // flags for pattern
@@ -46,10 +42,8 @@ typedef struct buf_state {
reg_extmatch_T *bs_extmatch; // external matches from start pattern
} bufstate_T;
-/*
- * syn_state contains the syntax state stack for the start of one line.
- * Used by b_sst_array[].
- */
+// syn_state contains the syntax state stack for the start of one line.
+// Used by b_sst_array[].
struct syn_state {
synstate_T *sst_next; // next entry in used or free list
linenr_T sst_lnum; // line number for this state
@@ -66,4 +60,4 @@ struct syn_state {
// may have made the state invalid
};
-#endif // NVIM_SYNTAX_DEFS_H
+#endif // NVIM_SYNTAX_DEFS_H
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index a10a2a0c32..28b3b6c1ef 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -99,7 +99,6 @@ static char *mt_names[MT_COUNT/2] =
#define NOTAGFILE 99 // return value for jumpto_tag
static char_u *nofile_fname = NULL; // fname for NOTAGFILE error
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "tag.c.generated.h"
#endif
@@ -117,7 +116,7 @@ static char_u *tagmatchname = NULL; // name of last used tag
* Tag for preview window is remembered separately, to avoid messing up the
* normal tagstack.
*/
-static taggy_T ptag_entry = { NULL, { { 0, 0, 0 }, 0, 0, NULL }, 0, 0, NULL };
+static taggy_T ptag_entry = { NULL, INIT_FMARK, 0, 0, NULL };
static int tfu_in_use = false; // disallow recursive call of tagfunc
@@ -168,7 +167,7 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
char_u **new_matches;
int use_tagstack;
int skip_msg = false;
- char_u *buf_ffname = curbuf->b_ffname; // name for priority computation
+ char_u *buf_ffname = (char_u *)curbuf->b_ffname; // name for priority computation
int use_tfu = 1;
// remember the matches for the last used tag
@@ -424,7 +423,7 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
buf_T *buf = buflist_findnr(cur_fnum);
if (buf != NULL) {
- buf_ffname = buf->b_ffname;
+ buf_ffname = (char_u *)buf->b_ffname;
}
}
@@ -585,7 +584,8 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
&& tagp2.user_data) {
XFREE_CLEAR(tagstack[tagstackidx].user_data);
tagstack[tagstackidx].user_data = vim_strnsave(tagp2.user_data,
- tagp2.user_data_end - tagp2.user_data);
+ (size_t)(tagp2.user_data_end -
+ tagp2.user_data));
}
tagstackidx++;
@@ -602,7 +602,6 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
smsg(_("File \"%s\" does not exist"), nofile_fname);
}
-
ic = (matches[cur_match][0] & MT_IC_OFF);
if (type != DT_TAG && type != DT_SELECT && type != DT_JUMP
&& type != DT_CSCOPE
@@ -625,7 +624,7 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
}
msg_scroll = true; // Don't overwrite this message.
} else {
- give_warning(IObuff, ic);
+ give_warning((char *)IObuff, ic);
}
if (ic && !msg_scrolled && msg_silent == 0) {
ui_flush();
@@ -785,7 +784,7 @@ static void print_tag_list(int new_tag, int use_tagstack, int num_matches, char_
// print all other extra fields
attr = HL_ATTR(HLF_CM);
while (*p && *p != '\r' && *p != '\n') {
- if (msg_col + ptr2cells(p) >= Columns) {
+ if (msg_col + ptr2cells((char *)p) >= Columns) {
msg_putchar('\n');
if (got_int) {
break;
@@ -812,8 +811,7 @@ static void print_tag_list(int new_tag, int use_tagstack, int num_matches, char_
} else {
for (p = tagp.command;
*p && *p != '\r' && *p != '\n';
- p++) {
- }
+ p++) {}
command_end = p;
}
@@ -832,7 +830,7 @@ static void print_tag_list(int new_tag, int use_tagstack, int num_matches, char_
}
while (p != command_end) {
- if (msg_col + (*p == TAB ? 1 : ptr2cells(p)) > Columns) {
+ if (msg_col + (*p == TAB ? 1 : ptr2cells((char *)p)) > Columns) {
msg_putchar('\n');
}
if (got_int) {
@@ -932,8 +930,7 @@ static int add_llist_tags(char_u *tag, int num_matches, char_u **matches)
cmd_end = tagp.command_end;
if (cmd_end == NULL) {
for (p = tagp.command;
- *p && *p != '\r' && *p != '\n'; p++) {
- }
+ *p && *p != '\r' && *p != '\n'; p++) {}
cmd_end = p;
}
@@ -972,7 +969,7 @@ static int add_llist_tags(char_u *tag, int num_matches, char_u **matches)
if (cmd_len > (CMDBUFFSIZE - 5)) {
cmd_len = CMDBUFFSIZE - 5;
}
- snprintf((char *)cmd + len, CMDBUFFSIZE + 1 - len,
+ snprintf((char *)cmd + len, (size_t)(CMDBUFFSIZE + 1 - len),
"%.*s", cmd_len, cmd_start);
len += cmd_len;
@@ -999,7 +996,7 @@ static int add_llist_tags(char_u *tag, int num_matches, char_u **matches)
}
vim_snprintf((char *)IObuff, IOSIZE, "ltag %s", tag);
- set_errorlist(curwin, list, ' ', IObuff, NULL);
+ set_errorlist(curwin, list, ' ', (char *)IObuff, NULL);
tv_list_free(list);
XFREE_CLEAR(fname);
@@ -1047,13 +1044,13 @@ void do_tags(exarg_T *eap)
}
msg_putchar('\n');
- vim_snprintf((char *)IObuff, IOSIZE, "%c%2d %2d %-15s %5ld ",
+ vim_snprintf((char *)IObuff, IOSIZE, "%c%2d %2d %-15s %5" PRIdLINENR " ",
i == tagstackidx ? '>' : ' ',
i + 1,
tagstack[i].cur_match + 1,
tagstack[i].tagname,
tagstack[i].fmark.mark.lnum);
- msg_outtrans(IObuff);
+ msg_outtrans((char *)IObuff);
msg_outtrans_attr(name, tagstack[i].fmark.fnum == curbuf->b_fnum
? HL_ATTR(HLF_D) : 0);
xfree(name);
@@ -1065,7 +1062,6 @@ void do_tags(exarg_T *eap)
}
}
-
/*
* Compare two strings, for length "len", ignoring case the ASCII way.
* return 0 for match, < 0 for smaller, > 0 for bigger
@@ -1090,7 +1086,6 @@ static int tag_strnicmp(char_u *s1, char_u *s2, size_t len)
return 0; // strings match
}
-
/*
* Extract info from the tag search pattern "pats->pat".
*/
@@ -1109,21 +1104,19 @@ static void prepare_pats(pat_T *pats, int has_re)
if (pats->head == pats->pat) {
pats->headlen = 0;
} else {
- for (pats->headlen = 0; pats->head[pats->headlen] != NUL;
- ++pats->headlen) {
- if (vim_strchr((char_u *)(p_magic ? ".[~*\\$" : "\\$"),
- pats->head[pats->headlen]) != NULL) {
+ for (pats->headlen = 0; pats->head[pats->headlen] != NUL; pats->headlen++) {
+ if (vim_strchr((p_magic ? ".[~*\\$" : "\\$"), pats->head[pats->headlen]) != NULL) {
break;
}
}
}
if (p_tl != 0 && pats->headlen > p_tl) { // adjust for 'taglength'
- pats->headlen = p_tl;
+ pats->headlen = (int)p_tl;
}
}
if (has_re) {
- pats->regmatch.regprog = vim_regcomp(pats->pat, p_magic ? RE_MAGIC : 0);
+ pats->regmatch.regprog = vim_regcomp((char *)pats->pat, p_magic ? RE_MAGIC : 0);
} else {
pats->regmatch.regprog = NULL;
}
@@ -1150,20 +1143,28 @@ static int find_tagfunc_tags(char_u *pat, garray_T *ga, int *match_count, int fl
typval_T args[4];
typval_T rettv;
char_u flagString[4];
- taggy_T *tag = &curwin->w_tagstack[curwin->w_tagstackidx];
+ taggy_T *tag = NULL;
+
+ if (curwin->w_tagstacklen > 0) {
+ if (curwin->w_tagstackidx == curwin->w_tagstacklen) {
+ tag = &curwin->w_tagstack[curwin->w_tagstackidx - 1];
+ } else {
+ tag = &curwin->w_tagstack[curwin->w_tagstackidx];
+ }
+ }
if (*curbuf->b_p_tfu == NUL) {
return FAIL;
}
args[0].v_type = VAR_STRING;
- args[0].vval.v_string = pat;
+ args[0].vval.v_string = (char *)pat;
args[1].v_type = VAR_STRING;
- args[1].vval.v_string = flagString;
+ args[1].vval.v_string = (char *)flagString;
// create 'info' dict argument
dict_T *const d = tv_dict_alloc_lock(VAR_FIXED);
- if (tag->user_data != NULL) {
+ if (tag != NULL && tag->user_data != NULL) {
tv_dict_add_str(d, S_LEN("user_data"), (const char *)tag->user_data);
}
if (buf_ffname != NULL) {
@@ -1183,7 +1184,7 @@ static int find_tagfunc_tags(char_u *pat, garray_T *ga, int *match_count, int fl
flags & TAG_REGEXP ? "r": "");
save_pos = curwin->w_cursor;
- result = call_vim_function(curbuf->b_p_tfu, 3, args, &rettv);
+ result = call_vim_function((char *)curbuf->b_p_tfu, 3, args, &rettv);
curwin->w_cursor = save_pos; // restore the cursor position
d->dv_refcount--;
@@ -1230,20 +1231,20 @@ static int find_tagfunc_tags(char_u *pat, garray_T *ga, int *match_count, int fl
len += STRLEN(tv->vval.v_string) + 1; // Space for "\tVALUE"
if (!STRCMP(dict_key, "name")) {
- res_name = tv->vval.v_string;
+ res_name = (char_u *)tv->vval.v_string;
continue;
}
if (!STRCMP(dict_key, "filename")) {
- res_fname = tv->vval.v_string;
+ res_fname = (char_u *)tv->vval.v_string;
continue;
}
if (!STRCMP(dict_key, "cmd")) {
- res_cmd = tv->vval.v_string;
+ res_cmd = (char_u *)tv->vval.v_string;
continue;
}
has_extra = 1;
if (!STRCMP(dict_key, "kind")) {
- res_kind = tv->vval.v_string;
+ res_kind = (char_u *)tv->vval.v_string;
continue;
}
// Other elements will be stored as "\tKEY:VALUE"
@@ -1413,7 +1414,6 @@ int find_tags(char_u *pat, int *num_matches, char_u ***matchesp, int flags, int
int matchoff = 0;
int save_emsg_off;
-
char_u *mfp;
garray_T ga_match[MT_COUNT]; // stores matches in sequence
hashtab_T ht_match[MT_COUNT]; // stores matches by key
@@ -1476,7 +1476,7 @@ int find_tags(char_u *pat, int *num_matches, char_u ***matchesp, int flags, int
/*
* Allocate memory for the buffers that are used
*/
- lbuf = xmalloc(lbuf_size);
+ lbuf = xmalloc((size_t)lbuf_size);
tag_fname = xmalloc(MAXPATHL + 1);
for (mtt = 0; mtt < MT_COUNT; mtt++) {
ga_init(&ga_match[mtt], sizeof(char_u *), 100);
@@ -1503,14 +1503,14 @@ int find_tags(char_u *pat, int *num_matches, char_u ***matchesp, int flags, int
if (orgpat.len > 3 && pat[orgpat.len - 3] == '@'
&& ASCII_ISALPHA(pat[orgpat.len - 2])
&& ASCII_ISALPHA(pat[orgpat.len - 1])) {
- saved_pat = vim_strnsave(pat, orgpat.len - 3);
+ saved_pat = vim_strnsave(pat, (size_t)orgpat.len - 3);
help_lang_find = &pat[orgpat.len - 2];
orgpat.pat = saved_pat;
orgpat.len -= 3;
}
}
if (p_tl != 0 && orgpat.len > p_tl) { // adjust for 'taglength'
- orgpat.len = p_tl;
+ orgpat.len = (int)p_tl;
}
save_emsg_off = emsg_off;
@@ -1605,8 +1605,8 @@ int find_tags(char_u *pat, int *num_matches, char_u ***matchesp, int flags, int
if (STRNICMP(s, help_lang, 2) == 0) {
break;
}
- ++help_pri;
- if ((s = vim_strchr(s, ',')) == NULL) {
+ help_pri++;
+ if ((s = (char_u *)vim_strchr((char *)s, ',')) == NULL) {
break;
}
}
@@ -1755,7 +1755,6 @@ line_read_in:
}
}
-
/*
* When still at the start of the file, check for Emacs tags file
* format, and for "not sorted" flag.
@@ -1779,8 +1778,7 @@ line_read_in:
if (STRNCMP(lbuf, "!_TAG_FILE_ENCODING\t", 20) == 0) {
// Prepare to convert every line from the specified
// encoding to 'encoding'.
- for (p = lbuf + 20; *p > ' ' && *p < 127; p++) {
- }
+ for (p = lbuf + 20; *p > ' ' && *p < 127; p++) {}
*p = NUL;
convert_setup(&vimconv, lbuf + 20, p_enc);
}
@@ -1854,7 +1852,7 @@ parse_line:
if (lbuf[lbuf_size - 2] != NUL && !use_cscope) {
lbuf_size *= 2;
xfree(lbuf);
- lbuf = xmalloc(lbuf_size);
+ lbuf = xmalloc((size_t)lbuf_size);
// this will try the same thing again, make sure the offset is
// different
search_info.curr_offset = 0;
@@ -1865,8 +1863,9 @@ parse_line:
// For "normal" tags: Do a quick check if the tag matches.
// This speeds up tag searching a lot!
if (orgpat.headlen) {
+ memset(&tagp, 0, sizeof(tagp));
tagp.tagname = lbuf;
- tagp.tagname_end = vim_strchr(lbuf, TAB);
+ tagp.tagname_end = (char_u *)vim_strchr((char *)lbuf, TAB);
if (tagp.tagname_end == NULL) {
// Corrupted tag line.
line_error = true;
@@ -1879,7 +1878,7 @@ parse_line:
*/
cmplen = (int)(tagp.tagname_end - tagp.tagname);
if (p_tl != 0 && cmplen > p_tl) { // adjust for 'taglength'
- cmplen = p_tl;
+ cmplen = (int)p_tl;
}
if (has_re && orgpat.headlen < cmplen) {
cmplen = orgpat.headlen;
@@ -1984,7 +1983,7 @@ parse_line:
// Can be a matching tag, isolate the file name and command.
tagp.fname = tagp.tagname_end + 1;
- tagp.fname_end = vim_strchr(tagp.fname, TAB);
+ tagp.fname_end = (char_u *)vim_strchr((char *)tagp.fname, TAB);
tagp.command = tagp.fname_end + 1;
if (tagp.fname_end == NULL) {
i = FAIL;
@@ -2006,7 +2005,7 @@ parse_line:
*/
cmplen = (int)(tagp.tagname_end - tagp.tagname);
if (p_tl != 0 && cmplen > p_tl) { // adjust for 'taglength'
- cmplen = p_tl;
+ cmplen = (int)p_tl;
}
// if tag length does not match, don't try comparing
if (orgpat.len != cmplen) {
@@ -2033,23 +2032,22 @@ parse_line:
cc = *tagp.tagname_end;
*tagp.tagname_end = NUL;
- match = vim_regexec(&orgpat.regmatch, tagp.tagname, (colnr_T)0);
+ match = vim_regexec(&orgpat.regmatch, (char *)tagp.tagname, (colnr_T)0);
if (match) {
matchoff = (int)(orgpat.regmatch.startp[0] - tagp.tagname);
if (orgpat.regmatch.rm_ic) {
- orgpat.regmatch.rm_ic = FALSE;
- match_no_ic = vim_regexec(&orgpat.regmatch, tagp.tagname,
- (colnr_T)0);
- orgpat.regmatch.rm_ic = TRUE;
+ orgpat.regmatch.rm_ic = false;
+ match_no_ic = vim_regexec(&orgpat.regmatch, (char *)tagp.tagname, (colnr_T)0);
+ orgpat.regmatch.rm_ic = true;
}
}
- *tagp.tagname_end = cc;
- match_re = TRUE;
+ *tagp.tagname_end = (char_u)cc;
+ match_re = true;
}
// If a match is found, add it to ht_match[] and ga_match[].
if (match) {
- int len = 0;
+ size_t len = 0;
if (use_cscope) {
// Don't change the ordering, always use the same table.
@@ -2092,15 +2090,15 @@ parse_line:
// detecting duplicates.
// The format is {tagname}@{lang}NUL{heuristic}NUL
*tagp.tagname_end = NUL;
- len = (int)(tagp.tagname_end - tagp.tagname);
+ len = (size_t)(tagp.tagname_end - tagp.tagname);
mfp = xmalloc(sizeof(char_u) + len + 10 + ML_EXTRA + 1);
p = mfp;
STRCPY(p, tagp.tagname);
p[len] = '@';
STRCPY(p + len + 1, help_lang);
- snprintf((char *)p + len + 1 + ML_EXTRA, 10, "%06d",
- help_heuristic(tagp.tagname,
+ snprintf((char *)p + len + 1 + ML_EXTRA, STRLEN(p) + len + 1 + ML_EXTRA, "%06d",
+ help_heuristic((char *)tagp.tagname,
match_re ? matchoff : 0, !match_no_ic)
+ help_pri);
@@ -2118,7 +2116,7 @@ parse_line:
}
if (tagp.command + 2 < temp_end) {
- len = (int)(temp_end - tagp.command - 2);
+ len = (size_t)(temp_end - tagp.command - 2);
mfp = xmalloc(len + 2);
STRLCPY(mfp, tagp.command + 2, len + 1);
} else {
@@ -2126,12 +2124,12 @@ parse_line:
}
get_it_again = false;
} else {
- len = (int)(tagp.tagname_end - tagp.tagname);
+ len = (size_t)(tagp.tagname_end - tagp.tagname);
mfp = xmalloc(sizeof(char_u) + len + 1);
STRLCPY(mfp, tagp.tagname, len + 1);
// if wanted, re-read line to get long form too
- if (State & INSERT) {
+ if (State & MODE_INSERT) {
get_it_again = p_sft;
}
}
@@ -2144,10 +2142,10 @@ parse_line:
// other tag: <mtt><tag_fname><0x02><0x02><lbuf><NUL>
// without Emacs tags: <mtt><tag_fname><0x02><lbuf><NUL>
// Here <mtt> is the "mtt" value plus 1 to avoid NUL.
- len = (int)tag_fname_len + (int)STRLEN(lbuf) + 3;
+ len = tag_fname_len + STRLEN(lbuf) + 3;
mfp = xmalloc(sizeof(char_u) + len + 1);
p = mfp;
- p[0] = mtt + 1;
+ p[0] = (char_u)(mtt + 1);
STRCPY(p + 1, tag_fname);
#ifdef BACKSLASH_IN_FILENAME
// Ignore differences in slashes, avoid adding
@@ -2263,7 +2261,7 @@ findtag_end:
}
if (match_count > 0) {
- matches = xmalloc(match_count * sizeof(char_u *));
+ matches = xmalloc((size_t)match_count * sizeof(char_u *));
} else {
matches = NULL;
}
@@ -2276,7 +2274,7 @@ findtag_end:
} else {
if (!name_only) {
// Change mtt back to zero-based.
- *mfp = *mfp - 1;
+ *mfp = (char_u)(*mfp - 1);
// change the TAG_SEP back to NUL
for (p = mfp + 1; *p != NUL; p++) {
@@ -2310,9 +2308,9 @@ static garray_T tag_fnames = GA_EMPTY_INIT_VALUE;
* Callback function for finding all "tags" and "tags-??" files in
* 'runtimepath' doc directories.
*/
-static void found_tagfile_cb(char_u *fname, void *cookie)
+static void found_tagfile_cb(char *fname, void *cookie)
{
- char_u *const tag_fname = vim_strsave(fname);
+ char_u *const tag_fname = vim_strsave((char_u *)fname);
#ifdef BACKSLASH_IN_FILENAME
slash_adjust(tag_fname);
@@ -2359,7 +2357,7 @@ int get_tagfname(tagname_T *tnp, int first, char_u *buf)
if (first) {
ga_clear_strings(&tag_fnames);
ga_init(&tag_fnames, (int)sizeof(char_u *), 10);
- do_in_runtimepath((char_u *)"doc/tags doc/tags-??", DIP_ALL,
+ do_in_runtimepath("doc/tags doc/tags-??", DIP_ALL,
found_tagfile_cb, NULL);
}
@@ -2371,7 +2369,7 @@ int get_tagfname(tagname_T *tnp, int first, char_u *buf)
}
++tnp->tn_hf_idx;
STRCPY(buf, p_hf);
- STRCPY(path_tail(buf), "tags");
+ STRCPY(path_tail((char *)buf), "tags");
#ifdef BACKSLASH_IN_FILENAME
slash_adjust(buf);
#endif
@@ -2425,12 +2423,12 @@ int get_tagfname(tagname_T *tnp, int first, char_u *buf)
* Copy next file name into buf.
*/
buf[0] = NUL;
- (void)copy_option_part(&tnp->tn_np, buf, MAXPATHL - 1, " ,");
+ (void)copy_option_part((char **)&tnp->tn_np, (char *)buf, MAXPATHL - 1, " ,");
r_ptr = vim_findfile_stopdir(buf);
// move the filename one char forward and truncate the
// filepath with a NUL
- filename = path_tail(buf);
+ filename = (char_u *)path_tail((char *)buf);
STRMOVE(filename + 1, filename);
*filename++ = NUL;
@@ -2438,7 +2436,7 @@ int get_tagfname(tagname_T *tnp, int first, char_u *buf)
r_ptr, 100,
FALSE, // don't free visited list
FINDFILE_FILE, // we search for a file
- tnp->tn_search_ctx, TRUE, curbuf->b_ffname);
+ tnp->tn_search_ctx, true, (char_u *)curbuf->b_ffname);
if (tnp->tn_search_ctx != NULL) {
tnp->tn_did_filefind_init = TRUE;
}
@@ -2475,7 +2473,7 @@ static int parse_tag_line(char_u *lbuf, tagptrs_T *tagp)
// Isolate the tagname, from lbuf up to the first white
tagp->tagname = lbuf;
- p = vim_strchr(lbuf, TAB);
+ p = (char_u *)vim_strchr((char *)lbuf, TAB);
if (p == NULL) {
return FAIL;
}
@@ -2486,7 +2484,7 @@ static int parse_tag_line(char_u *lbuf, tagptrs_T *tagp)
++p;
}
tagp->fname = p;
- p = vim_strchr(p, TAB);
+ p = (char_u *)vim_strchr((char *)p, TAB);
if (p == NULL) {
return FAIL;
}
@@ -2524,8 +2522,8 @@ static bool test_for_static(tagptrs_T *tagp)
// Check for new style static tag ":...<Tab>file:[<Tab>...]"
p = tagp->command;
- while ((p = vim_strchr(p, '\t')) != NULL) {
- ++p;
+ while ((p = (char_u *)vim_strchr((char *)p, '\t')) != NULL) {
+ p++;
if (STRNCMP(p, "file:", 5) == 0) {
return TRUE;
}
@@ -2541,7 +2539,7 @@ static size_t matching_line_len(const char_u *const lbuf)
// does the same thing as parse_match()
p += STRLEN(p) + 1;
- return (p - lbuf) + STRLEN(p);
+ return (size_t)(p - lbuf) + STRLEN(p);
}
/// Parse a line from a matching tag. Does not change the line itself.
@@ -2585,7 +2583,7 @@ static int parse_match(char_u *lbuf, tagptrs_T *tagp)
if (*p++ == TAB) {
// Accept ASCII alphabetic kind characters and any multi-byte
// character.
- while (ASCII_ISALPHA(*p) || utfc_ptr2len(p) > 1) {
+ while (ASCII_ISALPHA(*p) || utfc_ptr2len((char *)p) > 1) {
if (STRNCMP(p, "kind:", 5) == 0) {
tagp->tagkind = p + 5;
} else if (STRNCMP(p, "user_data:", 10) == 0) {
@@ -2597,8 +2595,8 @@ static int parse_match(char_u *lbuf, tagptrs_T *tagp)
break;
}
- pc = vim_strchr(p, ':');
- pt = vim_strchr(p, '\t');
+ pc = (char_u *)vim_strchr((char *)p, ':');
+ pt = (char_u *)vim_strchr((char *)p, '\t');
if (pc == NULL || (pt != NULL && pc > pt)) {
tagp->tagkind = p;
}
@@ -2613,15 +2611,13 @@ static int parse_match(char_u *lbuf, tagptrs_T *tagp)
if (tagp->tagkind != NULL) {
for (p = tagp->tagkind;
*p && *p != '\t' && *p != '\r' && *p != '\n';
- MB_PTR_ADV(p)) {
- }
+ MB_PTR_ADV(p)) {}
tagp->tagkind_end = p;
}
if (tagp->user_data != NULL) {
for (p = tagp->user_data;
*p && *p != '\t' && *p != '\r' && *p != '\n';
- MB_PTR_ADV(p)) {
- }
+ MB_PTR_ADV(p)) {}
tagp->user_data_end = p;
}
}
@@ -2638,7 +2634,7 @@ static char_u *tag_full_fname(tagptrs_T *tagp)
int c = *tagp->fname_end;
*tagp->fname_end = NUL;
char_u *fullname = expand_tag_fname(tagp->fname, tagp->tag_fname, false);
- *tagp->fname_end = c;
+ *tagp->fname_end = (char_u)c;
return fullname;
}
@@ -2719,7 +2715,7 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
* autocommand event (e.g., http://sys/file).
*/
if (!os_path_exists(fname)
- && !has_autocmd(EVENT_BUFREADCMD, fname,
+ && !has_autocmd(EVENT_BUFREADCMD, (char *)fname,
NULL)) {
retval = NOTAGFILE;
xfree(nofile_fname);
@@ -2729,7 +2725,6 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
++RedrawingDisabled;
-
if (l_g_do_tagpreview != 0) {
postponed_split = 0; // don't split again below
curwin_save = curwin; // Save current window
@@ -2754,7 +2749,7 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
// If it was a CTRL-W CTRL-] command split window now. For ":tab tag"
// open a new tab page.
if (postponed_split && (swb_flags & (SWB_USEOPEN | SWB_USETAB))) {
- buf_T *const existing_buf = buflist_findname_exp(fname);
+ buf_T *const existing_buf = buflist_findname_exp((char *)fname);
if (existing_buf != NULL) {
const win_T *wp = NULL;
@@ -2777,7 +2772,7 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
}
}
if (getfile_result == GETFILE_UNUSED
- && (postponed_split || cmdmod.tab != 0)) {
+ && (postponed_split || cmdmod.cmod_tab != 0)) {
if (win_split(postponed_split > 0 ? postponed_split : 0,
postponed_split_flags) == FAIL) {
RedrawingDisabled--;
@@ -2790,7 +2785,7 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
// A :ta from a help file will keep the b_help flag set. For ":ptag"
// we need to use the flag from the window where we came from.
if (l_g_do_tagpreview != 0) {
- keep_help_flag = curwin_save->w_buffer->b_help;
+ keep_help_flag = bt_help(curwin_save->w_buffer);
} else {
keep_help_flag = curbuf->b_help;
}
@@ -2799,7 +2794,7 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
if (getfile_result == GETFILE_UNUSED) {
// Careful: getfile() may trigger autocommands and call jumpto_tag()
// recursively.
- getfile_result = getfile(0, fname, NULL, true, (linenr_T)0, forceit);
+ getfile_result = getfile(0, (char *)fname, NULL, true, (linenr_T)0, forceit);
}
keep_help_flag = false;
@@ -2878,7 +2873,7 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
found = 0;
}
}
- *tagp.tagname_end = cc;
+ *tagp.tagname_end = (char_u)cc;
}
if (found == 0) {
emsg(_("E434: Can't find tag pattern"));
@@ -2960,7 +2955,7 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
} else {
RedrawingDisabled--;
if (postponed_split) { // close the window
- win_close(curwin, false);
+ win_close(curwin, false, false);
postponed_split = 0;
}
}
@@ -3001,7 +2996,7 @@ static char_u *expand_tag_fname(char_u *fname, char_u *const tag_fname, const bo
char_u *retval;
if ((p_tr || curbuf->b_help)
&& !vim_isAbsName(fname)
- && (p = path_tail(tag_fname)) != tag_fname) {
+ && (p = (char_u *)path_tail((char *)tag_fname)) != tag_fname) {
retval = xmalloc(MAXPATHL);
STRCPY(retval, tag_fname);
STRLCPY(retval + (p - tag_fname), fname,
@@ -3037,10 +3032,9 @@ static int test_for_current(char_u *fname, char_u *fname_end, char_u *tag_fname,
*fname_end = NUL;
}
fullname = expand_tag_fname(fname, tag_fname, true);
- retval = (path_full_compare(fullname, buf_ffname, true, true)
- & kEqualFiles);
+ retval = (path_full_compare((char *)fullname, (char *)buf_ffname, true, true) & kEqualFiles);
xfree(fullname);
- *fname_end = c;
+ *fname_end = (char_u)c;
}
return retval;
@@ -3058,7 +3052,7 @@ static int find_extra(char_u **pp)
// Repeat for addresses separated with ';'
for (;;) {
if (ascii_isdigit(*str)) {
- str = skipdigits(str + 1);
+ str = (char_u *)skipdigits((char *)str + 1);
} else if (*str == '/' || *str == '?') {
str = skip_regexp(str + 1, *str, false, NULL);
if (*str != first_char) {
@@ -3118,11 +3112,11 @@ int expand_tags(int tagnames, char_u *pat, int *num_file, char_u ***file)
if (pat[0] == '/') {
ret = find_tags(pat + 1, num_file, file,
TAG_REGEXP | extra_flag | TAG_VERBOSE | TAG_NO_TAGFUNC,
- TAG_MANY, curbuf->b_ffname);
+ TAG_MANY, (char_u *)curbuf->b_ffname);
} else {
ret = find_tags(pat, num_file, file,
TAG_REGEXP | extra_flag | TAG_VERBOSE | TAG_NO_TAGFUNC | TAG_NOIC,
- TAG_MANY, curbuf->b_ffname);
+ TAG_MANY, (char_u *)curbuf->b_ffname);
}
if (ret == OK && !tagnames) {
// Reorganize the tags for display and matching as strings of:
@@ -3131,7 +3125,7 @@ int expand_tags(int tagnames, char_u *pat, int *num_file, char_u ***file)
size_t len;
parse_match((*file)[i], &t_p);
- len = t_p.tagname_end - t_p.tagname;
+ len = (size_t)(t_p.tagname_end - t_p.tagname);
if (len > name_buf_size - 3) {
char_u *buf;
@@ -3145,8 +3139,8 @@ int expand_tags(int tagnames, char_u *pat, int *num_file, char_u ***file)
name_buf[len++] = (t_p.tagkind != NULL && *t_p.tagkind)
? *t_p.tagkind : 'f';
name_buf[len++] = 0;
- memmove((*file)[i] + len, t_p.fname, t_p.fname_end - t_p.fname);
- (*file)[i][len + (t_p.fname_end - t_p.fname)] = 0;
+ memmove((*file)[i] + len, t_p.fname, (size_t)(t_p.fname_end - t_p.fname));
+ (*file)[i][len + (size_t)(t_p.fname_end - t_p.fname)] = 0;
memmove((*file)[i], name_buf, len);
}
}
@@ -3154,7 +3148,6 @@ int expand_tags(int tagnames, char_u *pat, int *num_file, char_u ***file)
return ret;
}
-
/// Add a tag field to the dictionary "dict".
/// Return OK or FAIL.
///
@@ -3408,7 +3401,7 @@ static void tagstack_push_items(win_T *wp, list_T *l)
if ((di = tv_dict_find(itemdict, "from", -1)) == NULL) {
continue;
}
- if (list2fpos(&di->di_tv, &mark, &fnum, NULL) != OK) {
+ if (list2fpos(&di->di_tv, &mark, &fnum, NULL, false) != OK) {
continue;
}
if ((tagname = (char_u *)tv_dict_get_string(itemdict, "tagname", true))
diff --git a/src/nvim/tag.h b/src/nvim/tag.h
index 64bacceb1b..c8051e1dcc 100644
--- a/src/nvim/tag.h
+++ b/src/nvim/tag.h
@@ -4,9 +4,7 @@
#include "nvim/ex_cmds_defs.h"
#include "nvim/types.h"
-/*
- * Values for do_tag().
- */
+// Values for do_tag().
#define DT_TAG 1 // jump to newer position or same tag again
#define DT_POP 2 // jump to older position
#define DT_NEXT 3 // jump to next match of same tag
@@ -20,9 +18,7 @@
#define DT_LTAG 11 // tag using location list
#define DT_FREE 99 // free cached matches
-//
// flags for find_tags().
-//
#define TAG_HELP 1 // only search for help tags
#define TAG_NAMES 2 // only return name of tag
#define TAG_REGEXP 4 // use tag pattern as regexp
@@ -36,9 +32,7 @@
#define TAG_MANY 300 // When finding many tags (for completion),
// find up to this many tags
-/*
- * Structure used for get_tagfname().
- */
+// Structure used for get_tagfname().
typedef struct {
char_u *tn_tags; // value of 'tags' when starting
char_u *tn_np; // current position in tn_tags
@@ -47,7 +41,6 @@ typedef struct {
void *tn_search_ctx;
} tagname_T;
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "tag.h.generated.h"
#endif
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index 2c242f4a75..be49048aec 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -55,7 +55,8 @@
#include "nvim/fileio.h"
#include "nvim/getchar.h"
#include "nvim/highlight.h"
-#include "nvim/keymap.h"
+#include "nvim/highlight_group.h"
+#include "nvim/keycodes.h"
#include "nvim/log.h"
#include "nvim/macros.h"
#include "nvim/main.h"
@@ -70,7 +71,6 @@
#include "nvim/os/input.h"
#include "nvim/screen.h"
#include "nvim/state.h"
-#include "nvim/syntax.h"
#include "nvim/terminal.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
@@ -172,8 +172,20 @@ void terminal_teardown(void)
pmap_init(ptr_t, &invalidated_terminals);
}
+static void term_output_callback(const char *s, size_t len, void *user_data)
+{
+ terminal_send((Terminal *)user_data, (char *)s, len);
+}
+
// public API {{{
+/// Initializes terminal properties, and triggers TermOpen.
+///
+/// The PTY process (TerminalOptions.data) was already started by termopen(),
+/// via ex_terminal() or the term:// BufReadCmd.
+///
+/// @param buf Buffer used for presentation of the terminal.
+/// @param opts PTY process channel, various terminal properties and callbacks.
Terminal *terminal_open(buf_T *buf, TerminalOptions opts)
{
// Create a new terminal instance and configure it
@@ -195,6 +207,7 @@ Terminal *terminal_open(buf_T *buf, TerminalOptions opts)
vterm_screen_set_callbacks(rv->vts, &vterm_screen_callbacks, rv);
vterm_screen_set_damage_merge(rv->vts, VTERM_DAMAGE_SCROLL);
vterm_screen_reset(rv->vts, 1);
+ vterm_output_set_callback(rv->vt, term_output_callback, rv);
// force a initial refresh of the screen to ensure the buffer will always
// have as many lines as screen rows when refresh_scrollback is called
rv->invalid_start = 0;
@@ -215,11 +228,13 @@ Terminal *terminal_open(buf_T *buf, TerminalOptions opts)
set_option_value("wrap", false, NULL, OPT_LOCAL);
set_option_value("list", false, NULL, OPT_LOCAL);
if (buf->b_ffname != NULL) {
- buf_set_term_title(buf, (char *)buf->b_ffname);
+ buf_set_term_title(buf, buf->b_ffname);
}
RESET_BINDING(curwin);
// Reset cursor in current window.
curwin->w_cursor = (pos_T){ .lnum = 1, .col = 0, .coladd = 0 };
+ // Initialize to check if the scrollback buffer has been allocated inside a TermOpen autocmd
+ rv->sb_buffer = NULL;
// Apply TermOpen autocmds _before_ configuring the scrollback buffer.
apply_autocmds(EVENT_TERMOPEN, NULL, NULL, false, buf);
// Local 'scrollback' _after_ autocmds.
@@ -242,7 +257,8 @@ Terminal *terminal_open(buf_T *buf, TerminalOptions opts)
snprintf(var, sizeof(var), "terminal_color_%d", i);
char *name = get_config_string(var);
if (name) {
- color_val = name_to_color(name);
+ int dummy;
+ color_val = name_to_color(name, &dummy);
xfree(name);
if (color_val != -1) {
@@ -260,8 +276,12 @@ Terminal *terminal_open(buf_T *buf, TerminalOptions opts)
return rv;
}
-void terminal_close(Terminal *term, int status)
+/// Closes the Terminal buffer.
+///
+/// May call terminal_destroy, which sets caller storage to NULL.
+void terminal_close(Terminal **termpp, int status)
{
+ Terminal *term = *termpp;
if (term->destroy) {
return;
}
@@ -270,7 +290,7 @@ void terminal_close(Terminal *term, int status)
if (entered_free_all_mem) {
// If called from close_buffer() inside free_all_mem(), the main loop has
// already been freed, so it is not safe to call the close callback here.
- terminal_destroy(term);
+ terminal_destroy(termpp);
return;
}
#endif
@@ -311,10 +331,14 @@ void terminal_close(Terminal *term, int status)
term->opts.close_cb(term->opts.data);
}
} else if (!only_destroy) {
- // This was called by channel_process_exit_cb() not in process_teardown().
+ // Associated channel has been closed and the editor is not exiting.
// Do not call the close callback now. Wait for the user to press a key.
char msg[sizeof("\r\n[Process exited ]") + NUMBUFLEN];
- snprintf(msg, sizeof msg, "\r\n[Process exited %d]", status);
+ if (((Channel *)term->opts.data)->streamtype == kChannelStreamInternal) {
+ snprintf(msg, sizeof msg, "\r\n[Terminal closed]");
+ } else {
+ snprintf(msg, sizeof msg, "\r\n[Process exited %d]", status);
+ }
terminal_receive(term, msg, strlen(msg));
}
@@ -326,6 +350,7 @@ void terminal_close(Terminal *term, int status)
save_v_event_T save_v_event;
dict_T *dict = get_v_event(&save_v_event);
tv_dict_add_nr(dict, S_LEN("status"), status);
+ tv_dict_set_keys_readonly(dict);
apply_autocmds(EVENT_TERMCLOSE, NULL, NULL, false, buf);
restore_v_event(dict, &save_v_event);
}
@@ -341,7 +366,6 @@ void terminal_check_size(Terminal *term)
vterm_get_size(term->vt, &curheight, &curwidth);
uint16_t width = 0, height = 0;
-
FOR_ALL_TAB_WINDOWS(tp, wp) {
if (wp->w_buffer && wp->w_buffer->terminal == term) {
const uint16_t win_width =
@@ -363,6 +387,7 @@ void terminal_check_size(Terminal *term)
invalidate_terminal(term, -1, -1);
}
+/// Implements MODE_TERMINAL state. :help Terminal-mode
void terminal_enter(void)
{
buf_T *buf = curbuf;
@@ -379,13 +404,13 @@ void terminal_enter(void)
int save_state = State;
s->save_rd = RedrawingDisabled;
- State = TERM_FOCUS;
- mapped_ctrl_c |= TERM_FOCUS; // Always map CTRL-C to avoid interrupt.
+ State = MODE_TERMINAL;
+ mapped_ctrl_c |= MODE_TERMINAL; // Always map CTRL-C to avoid interrupt.
RedrawingDisabled = false;
// Disable these options in terminal-mode. They are nonsense because cursor is
// placed at end of buffer to "follow" output. #11072
- win_T *save_curwin = curwin;
+ handle_T save_curwin = curwin->handle;
bool save_w_p_cul = curwin->w_p_cul;
char_u *save_w_p_culopt = NULL;
char_u save_w_p_culopt_flags = curwin->w_p_culopt_flags;
@@ -412,7 +437,7 @@ void terminal_enter(void)
curwin->w_redr_status = true; // For mode() in statusline. #8323
ui_busy_start();
apply_autocmds(EVENT_TERMENTER, NULL, NULL, false, curbuf);
- trigger_modechanged();
+ may_trigger_modechanged();
s->state.execute = terminal_execute;
s->state.check = terminal_check;
@@ -423,7 +448,7 @@ void terminal_enter(void)
RedrawingDisabled = s->save_rd;
apply_autocmds(EVENT_TERMLEAVE, NULL, NULL, false, curbuf);
- if (save_curwin == curwin) { // save_curwin may be invalid (window closed)!
+ if (save_curwin == curwin->handle) { // Else: window was closed.
curwin->w_p_cul = save_w_p_cul;
if (save_w_p_culopt) {
xfree(curwin->w_p_culopt);
@@ -491,6 +516,7 @@ static int terminal_check(VimState *state)
return 1;
}
+/// Processes one char of terminal-mode input.
static int terminal_execute(VimState *state, int key)
{
TerminalState *s = (TerminalState *)state;
@@ -565,8 +591,11 @@ static int terminal_execute(VimState *state, int key)
return 1;
}
-void terminal_destroy(Terminal *term)
+/// Frees the given Terminal structure and sets the caller storage to NULL (in the spirit of
+/// XFREE_CLEAR).
+void terminal_destroy(Terminal **termpp)
{
+ Terminal *term = *termpp;
buf_T *buf = handle_get_buffer(term->buf_handle);
if (buf) {
term->buf_handle = 0;
@@ -587,6 +616,7 @@ void terminal_destroy(Terminal *term)
xfree(term->sb_buffer);
vterm_free(term->vt);
xfree(term);
+ *termpp = NULL; // coverity[dead-store]
}
}
@@ -636,7 +666,6 @@ void terminal_paste(long count, char_u **y_array, size_t y_size)
return;
}
vterm_keyboard_start_paste(curbuf->terminal->vt);
- terminal_flush_output(curbuf->terminal);
size_t buff_len = STRLEN(y_array[0]);
char_u *buff = xmalloc(buff_len);
for (int i = 0; i < count; i++) { // -V756
@@ -654,8 +683,8 @@ void terminal_paste(long count, char_u **y_array, size_t y_size)
char_u *dst = buff;
char_u *src = y_array[j];
while (*src != '\0') {
- len = (size_t)utf_ptr2len(src);
- int c = utf_ptr2char(src);
+ len = (size_t)utf_ptr2len((char *)src);
+ int c = utf_ptr2char((char *)src);
if (!is_filter_char(c)) {
memcpy(dst, src, len);
dst += len;
@@ -667,14 +696,6 @@ void terminal_paste(long count, char_u **y_array, size_t y_size)
}
xfree(buff);
vterm_keyboard_end_paste(curbuf->terminal->vt);
- terminal_flush_output(curbuf->terminal);
-}
-
-void terminal_flush_output(Terminal *term)
-{
- size_t len = vterm_output_read(term->vt, term->textbuf,
- sizeof(term->textbuf));
- terminal_send(term, term->textbuf, len);
}
void terminal_send_key(Terminal *term, int c)
@@ -693,8 +714,6 @@ void terminal_send_key(Terminal *term, int c)
} else {
vterm_keyboard_unichar(term->vt, (uint32_t)c, mod);
}
-
- terminal_flush_output(term);
}
void terminal_receive(Terminal *term, char *data, size_t len)
@@ -713,7 +732,6 @@ static int get_rgb(VTermState *state, VTermColor color)
return RGB_(color.rgb.red, color.rgb.green, color.rgb.blue);
}
-
void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr, int *term_attrs)
{
int height, width;
@@ -744,8 +762,8 @@ void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr, int *te
int vt_fg_idx = ((!fg_default && fg_indexed) ? cell.fg.indexed.idx + 1 : 0);
int vt_bg_idx = ((!bg_default && bg_indexed) ? cell.bg.indexed.idx + 1 : 0);
- bool fg_set = vt_fg_idx && vt_fg_idx <= 16 && term->color_set[vt_fg_idx-1];
- bool bg_set = vt_bg_idx && vt_bg_idx <= 16 && term->color_set[vt_bg_idx-1];
+ bool fg_set = vt_fg_idx && vt_fg_idx <= 16 && term->color_set[vt_fg_idx - 1];
+ bool bg_set = vt_bg_idx && vt_bg_idx <= 16 && term->color_set[vt_bg_idx - 1];
int hl_attrs = (cell.attrs.bold ? HL_BOLD : 0)
| (cell.attrs.italic ? HL_ITALIC : 0)
@@ -1317,9 +1335,6 @@ static bool send_mouse_event(Terminal *term, int c)
}
mouse_action(term, button, row, col - offset, pressed, 0);
- size_t len = vterm_output_read(term->vt, term->textbuf,
- sizeof(term->textbuf));
- terminal_send(term, term->textbuf, len);
return false;
}
@@ -1344,21 +1359,20 @@ static bool send_mouse_event(Terminal *term, int c)
return mouse_win == curwin;
}
- // ignore left release action if it was not proccessed above
+ // ignore left release action if it was not processed above
// to prevent leaving Terminal mode after entering to it using a mouse
if (c == K_LEFTRELEASE && mouse_win->w_buffer->terminal == term) {
return false;
}
end:
- ins_char_typebuf(c);
+ ins_char_typebuf(vgetc_char, vgetc_mod_mask);
return true;
}
// }}}
// terminal buffer refresh & misc {{{
-
static void fetch_row(Terminal *term, int row, int end_col)
{
int col = 0;
@@ -1368,27 +1382,21 @@ static void fetch_row(Terminal *term, int row, int end_col)
while (col < end_col) {
VTermScreenCell cell;
fetch_cell(term, row, col, &cell);
- int cell_len = 0;
if (cell.chars[0]) {
+ int cell_len = 0;
for (int i = 0; cell.chars[i]; i++) {
- cell_len += utf_char2bytes((int)cell.chars[i],
- (uint8_t *)ptr + cell_len);
+ cell_len += utf_char2bytes((int)cell.chars[i], ptr + cell_len);
}
- } else {
- *ptr = ' ';
- cell_len = 1;
- }
- char c = *ptr;
- ptr += cell_len;
- if (c != ' ') {
- // only increase the line length if the last character is not whitespace
+ ptr += cell_len;
line_len = (size_t)(ptr - term->textbuf);
+ } else {
+ *ptr++ = ' ';
}
col += cell.width;
}
- // trim trailing whitespace
- term->textbuf[line_len] = 0;
+ // end of line
+ term->textbuf[line_len] = NUL;
}
static bool fetch_cell(Terminal *term, int row, int col, VTermScreenCell *cell)
@@ -1451,7 +1459,8 @@ static void refresh_terminal(Terminal *term)
long ml_added = buf->b_ml.ml_line_count - ml_before;
adjust_topline(term, buf, ml_added);
}
-// Calls refresh_terminal() on all invalidated_terminals.
+
+/// Calls refresh_terminal() on all invalidated_terminals.
static void refresh_timer_cb(TimeWatcher *watcher, void *data)
{
refresh_pending = false;
@@ -1483,8 +1492,16 @@ static void refresh_size(Terminal *term, buf_T *buf)
term->opts.resize_cb((uint16_t)width, (uint16_t)height, term->opts.data);
}
-/// Adjusts scrollback storage after 'scrollback' option changed.
-static void on_scrollback_option_changed(Terminal *term, buf_T *buf)
+void on_scrollback_option_changed(Terminal *term)
+{
+ // Scrollback buffer may not exist yet, e.g. if 'scrollback' is set in a TermOpen autocmd.
+ if (term->sb_buffer != NULL) {
+ refresh_terminal(term);
+ }
+}
+
+/// Adjusts scrollback storage and the terminal buffer scrollback lines
+static void adjust_scrollback(Terminal *term, buf_T *buf)
{
if (buf->b_p_scbk < 1) { // Local 'scrollback' was set to -1.
buf->b_p_scbk = SB_MAX;
@@ -1503,7 +1520,7 @@ static void on_scrollback_option_changed(Terminal *term, buf_T *buf)
term->sb_current--;
xfree(term->sb_buffer[term->sb_current]);
}
- deleted_lines(1, (long)diff);
+ deleted_lines(1, (linenr_T)diff);
}
// Resize the scrollback storage.
@@ -1526,7 +1543,7 @@ static void refresh_scrollback(Terminal *term, buf_T *buf)
int row_offset = term->sb_pending;
while (term->sb_pending > 0 && buf->b_ml.ml_line_count < height) {
fetch_row(term, term->sb_pending - row_offset - 1, width);
- ml_append(0, (uint8_t *)term->textbuf, 0, false);
+ ml_append(0, term->textbuf, 0, false);
appended_lines(0, 1);
term->sb_pending--;
}
@@ -1544,7 +1561,7 @@ static void refresh_scrollback(Terminal *term, buf_T *buf)
}
fetch_row(term, -term->sb_pending - row_offset, width);
int buf_index = (int)buf->b_ml.ml_line_count - height;
- ml_append(buf_index, (uint8_t *)term->textbuf, 0, false);
+ ml_append(buf_index, term->textbuf, 0, false);
appended_lines(buf_index, 1);
term->sb_pending--;
}
@@ -1556,7 +1573,7 @@ static void refresh_scrollback(Terminal *term, buf_T *buf)
deleted_lines(buf->b_ml.ml_line_count, 1);
}
- on_scrollback_option_changed(term, buf);
+ adjust_scrollback(term, buf);
}
// Refresh the screen (visible part of the buffer when the terminal is
@@ -1584,10 +1601,10 @@ static void refresh_screen(Terminal *term, buf_T *buf)
fetch_row(term, r, width);
if (linenr <= buf->b_ml.ml_line_count) {
- ml_replace(linenr, (uint8_t *)term->textbuf, true);
+ ml_replace(linenr, term->textbuf, true);
changed++;
} else {
- ml_append(linenr - 1, (uint8_t *)term->textbuf, 0, false);
+ ml_append(linenr - 1, term->textbuf, 0, false);
added++;
}
}
@@ -1631,7 +1648,7 @@ static int linenr_to_row(Terminal *term, int linenr)
static bool is_focused(Terminal *term)
{
- return State & TERM_FOCUS && curbuf->terminal == term;
+ return State & MODE_TERMINAL && curbuf->terminal == term;
}
static char *get_config_string(char *key)
diff --git a/src/nvim/terminal.h b/src/nvim/terminal.h
index 001adbadc3..a83929e224 100644
--- a/src/nvim/terminal.h
+++ b/src/nvim/terminal.h
@@ -13,7 +13,7 @@ typedef void (*terminal_close_cb)(void *data);
#include "nvim/buffer_defs.h"
typedef struct {
- void *data;
+ void *data; // PTY process channel
uint16_t width, height;
terminal_write_cb write_cb;
terminal_resize_cb resize_cb;
diff --git a/src/nvim/testdir/check.vim b/src/nvim/testdir/check.vim
index 14bab33a2f..4107df99d6 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
@@ -55,6 +55,14 @@ func CheckMSWindows()
endif
endfunc
+" Command to check for NOT running on MS-Windows
+command CheckNotMSWindows call CheckNotMSWindows()
+func CheckNotMSWindows()
+ if has('win32')
+ throw 'Skipped: does not work on MS-Windows'
+ endif
+endfunc
+
" Command to check for running on Unix
command CheckUnix call CheckUnix()
func CheckUnix()
@@ -63,6 +71,15 @@ func CheckUnix()
endif
endfunc
+" Command to check for not running on a BSD system.
+" TODO: using this checks should not be needed
+command CheckNotBSD call CheckNotBSD()
+func CheckNotBSD()
+ if has('bsd')
+ throw 'Skipped: does not work on BSD'
+ endif
+endfunc
+
" Command to check that making screendumps is supported.
" Caller must source screendump.vim
command CheckScreendump call CheckScreendump()
@@ -104,6 +121,14 @@ func CheckNotGui()
endif
endfunc
+" Command to check that test is not running as root
+command CheckNotRoot call CheckNotRoot()
+func CheckNotRoot()
+ if IsRoot()
+ throw 'Skipped: cannot run test as root'
+ endif
+endfunc
+
" Command to check that the current language is English
command CheckEnglish call CheckEnglish()
func CheckEnglish()
@@ -112,11 +137,11 @@ func CheckEnglish()
endif
endfunc
-" Command to check for NOT running on MS-Windows
-command CheckNotMSWindows call CheckNotMSWindows()
-func CheckNotMSWindows()
- if has('win32')
- throw 'Skipped: does not work on MS-Windows'
+" Command to check for not running under ASAN
+command CheckNotAsan call CheckNotAsan()
+func CheckNotAsan()
+ if execute('version') =~# '-fsanitize=[a-z,]*\<address\>'
+ throw 'Skipped: does not work with ASAN'
endif
endfunc
diff --git a/src/nvim/testdir/runnvim.sh b/src/nvim/testdir/runnvim.sh
index fdd3f3144b..322265737a 100755
--- a/src/nvim/testdir/runnvim.sh
+++ b/src/nvim/testdir/runnvim.sh
@@ -38,7 +38,7 @@ main() {(
-S runnvim.vim \
"$tlog" > "out-$tlog" 2> "err-$tlog"
then
- fail "$test_name" F "Nvim exited with non-zero code"
+ fail "$test_name" "Nvim exited with non-zero code"
fi
{
echo "Stdout of :terminal runner"
@@ -53,7 +53,7 @@ main() {(
if test "$oldesttest" = 1 ; then
if ! diff -q test.out "$test_name.ok" > /dev/null 2>&1 ; then
if test -f test.out ; then
- fail "$test_name" F "Oldest test .out file differs from .ok file"
+ fail "$test_name" "Oldest test .out file differs from .ok file"
{
echo "Diff between test.out and $test_name.ok"
echo "$separator"
@@ -65,9 +65,6 @@ main() {(
fi
fi
fi
- if test "$FAILED" = 1 ; then
- ci_fold start "$test_name"
- fi
valgrind_check .
if test -n "$LOG_DIR" ; then
check_sanitizer "$LOG_DIR"
@@ -78,9 +75,6 @@ main() {(
fi
rm -f "$tlog"
if test "$FAILED" = 1 ; then
- ci_fold end ""
- fi
- if test "$FAILED" = 1 ; then
echo "Test $test_name failed, see output above and summary for more details" >> test.log
# When Neovim crashed/aborted it might not have created messages.
# test.log itself is used as an indicator to exit non-zero in the Makefile.
diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim
index b0d872e392..6b16e888a9 100644
--- a/src/nvim/testdir/runtest.vim
+++ b/src/nvim/testdir/runtest.vim
@@ -153,6 +153,9 @@ func RunTheTest(test)
" directory after executing the test.
let save_cwd = getcwd()
+ " Align Nvim defaults to Vim.
+ source setup.vim
+
if exists("*SetUp")
try
call SetUp()
@@ -361,24 +364,25 @@ let s:flaky_tests = [
\ 'Test_cursorhold_insert()',
\ 'Test_exit_callback_interval()',
\ 'Test_map_timeout_with_timer_interrupt()',
- \ 'Test_oneshot()',
\ 'Test_out_cb()',
- \ 'Test_paused()',
\ 'Test_popup_and_window_resize()',
\ 'Test_quoteplus()',
\ 'Test_quotestar()',
\ 'Test_reltime()',
- \ 'Test_repeat_many()',
- \ 'Test_repeat_three()',
\ 'Test_state()',
- \ 'Test_stop_all_in_callback()',
\ 'Test_term_mouse_double_click_to_create_tab()',
\ 'Test_term_mouse_multiple_clicks_to_visually_select()',
\ 'Test_terminal_composing_unicode()',
\ 'Test_terminal_redir_file()',
\ 'Test_terminal_tmap()',
+ \ 'Test_timer_oneshot()',
+ \ 'Test_timer_paused()',
+ \ 'Test_timer_repeat_many()',
+ \ 'Test_timer_repeat_three()',
+ \ 'Test_timer_stop_all_in_callback()',
+ \ 'Test_timer_stop_in_callback()',
+ \ 'Test_timer_with_partial_callback()',
\ 'Test_termwinscroll()',
- \ 'Test_with_partial_callback()',
\ ]
" Locate Test_ functions and execute them.
diff --git a/src/nvim/testdir/setup.vim b/src/nvim/testdir/setup.vim
index fdae0697c3..e6c0762729 100644
--- a/src/nvim/testdir/setup.vim
+++ b/src/nvim/testdir/setup.vim
@@ -1,3 +1,35 @@
+if exists('s:did_load')
+ " Align Nvim defaults to Vim.
+ set backspace=
+ set complete=.,w,b,u,t,i
+ set directory&
+ set directory^=.
+ set fillchars=vert:\|,fold:-
+ set formatoptions=tcq
+ set fsync
+ set laststatus=1
+ set listchars=eol:$
+ set joinspaces
+ set nohidden nosmarttab noautoindent noautoread noruler noshowcmd
+ set nohlsearch noincsearch
+ set nrformats=bin,octal,hex
+ set shortmess=filnxtToOS
+ set sidescroll=0
+ set tags=./tags,tags
+ set undodir&
+ set undodir^=.
+ set wildoptions=
+ set startofline
+ set sessionoptions&
+ set sessionoptions+=options
+ set viewoptions&
+ set viewoptions+=options
+ set switchbuf=
+ " Make "Q" switch to Ex mode.
+ " This does not work for all tests.
+ nnoremap Q gQ
+endif
+
" Common preparations for running tests.
" Only load this once.
@@ -6,30 +38,9 @@ if exists('s:did_load')
endif
let s:did_load = 1
-" Align Nvim defaults to Vim.
-set backspace=
-set directory^=.
-set fillchars=vert:\|,fold:-
-set laststatus=1
-set listchars=eol:$
-set joinspaces
-set nohidden nosmarttab noautoindent noautoread complete-=i noruler noshowcmd
-set nrformats+=octal
-set shortmess-=F
-set sidescroll=0
-set tags=./tags,tags
-set undodir^=.
-set wildoptions=
-set startofline
-set sessionoptions+=options
-set viewoptions+=options
-set switchbuf=
-
-" Unmap Nvim default mappings.
-unmap Y
-unmap <C-L>
-iunmap <C-U>
-iunmap <C-W>
+" Clear Nvim default mappings.
+mapclear
+mapclear!
" Prevent Nvim log from writing to stderr.
let $NVIM_LOG_FILE = exists($NVIM_LOG_FILE) ? $NVIM_LOG_FILE : 'Xnvim.log'
diff --git a/src/nvim/testdir/shared.vim b/src/nvim/testdir/shared.vim
index f456ff4250..c2809844ac 100644
--- a/src/nvim/testdir/shared.vim
+++ b/src/nvim/testdir/shared.vim
@@ -343,6 +343,15 @@ func RunVimPiped(before, after, arguments, pipecmd)
return 1
endfunc
+func IsRoot()
+ if !has('unix')
+ return v:false
+ elseif $USER == 'root' || system('id -un') =~ '\<root\>'
+ return v:true
+ endif
+ return v:false
+endfunc
+
" Get all messages but drop the maintainer entry.
func GetMessages()
redir => result
diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim
index c0ac4393c4..43a519bc84 100644
--- a/src/nvim/testdir/test_alot.vim
+++ b/src/nvim/testdir/test_alot.vim
@@ -3,55 +3,32 @@
source test_backup.vim
source test_behave.vim
-source test_cd.vim
-source test_changedtick.vim
source test_compiler.vim
-source test_cursor_func.vim
-source test_cursorline.vim
source test_ex_equal.vim
source test_ex_undo.vim
source test_ex_z.vim
source test_ex_mode.vim
-source test_execute_func.vim
+source test_expand.vim
source test_expand_func.vim
source test_feedkeys.vim
-source test_filter_cmd.vim
-source test_filter_map.vim
-source test_findfile.vim
-source test_float_func.vim
-source test_functions.vim
+source test_file_perm.vim
+source test_fnamemodify.vim
source test_ga.vim
+source test_glob2regpat.vim
source test_global.vim
-source test_goto.vim
-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_lispwords.vim
source test_menu.vim
-source test_messages.vim
-source test_modeline.vim
source test_move.vim
-source test_partial.vim
-source test_popup.vim
source test_put.vim
-source test_rename.vim
+source test_reltime.vim
source test_scroll_opt.vim
+source test_searchpos.vim
+source test_set.vim
source test_shift.vim
-source test_sort.vim
source test_sha256.vim
-source test_suspend.vim
-source test_syn_attr.vim
source test_tabline.vim
-source test_tabpage.vim
source test_tagcase.vim
source test_tagfunc.vim
-source test_tagjump.vim
-source test_taglist.vim
-source test_true_false.vim
source test_unlet.vim
source test_version.vim
-source test_virtualedit.vim
-source test_window_cmd.vim
source test_wnext.vim
diff --git a/src/nvim/testdir/test_alot_utf8.vim b/src/nvim/testdir/test_alot_utf8.vim
index 70f14320a6..77f5ede4c8 100644
--- a/src/nvim/testdir/test_alot_utf8.vim
+++ b/src/nvim/testdir/test_alot_utf8.vim
@@ -6,7 +6,6 @@
source test_charsearch_utf8.vim
source test_expr_utf8.vim
-source test_matchadd_conceal_utf8.vim
source test_mksession_utf8.vim
source test_regexp_utf8.vim
source test_source_utf8.vim
diff --git a/src/nvim/testdir/test_arabic.vim b/src/nvim/testdir/test_arabic.vim
index 450c6f98f5..272937387d 100644
--- a/src/nvim/testdir/test_arabic.vim
+++ b/src/nvim/testdir/test_arabic.vim
@@ -2,9 +2,8 @@
" NOTE: This just checks if the code works. If you know Arabic please add
" functional tests that check the shaping works with real text.
-if !has('arabic')
- throw 'Skipped: arabic feature missing'
-endif
+source check.vim
+CheckFeature arabic
source view_util.vim
@@ -563,3 +562,26 @@ func Test_shape_combination_isolated()
set arabicshape&
bwipe!
endfunc
+
+" Test for entering arabic character in a search command
+func Test_arabic_chars_in_search_cmd()
+ new
+ set arabic
+ call feedkeys("i\nsghl!\<C-^>vim\<C-^>", 'tx')
+ call cursor(1, 1)
+ call feedkeys("/^sghl!\<C-^>vim$\<C-^>\<CR>", 'tx')
+ call assert_equal([2, 1], [line('.'), col('.')])
+
+ " Try searching in left-to-right mode
+ set rightleftcmd=
+ call cursor(1, 1)
+ call feedkeys("/^sghl!\<C-^>vim$\<CR>", 'tx')
+ call assert_equal([2, 1], [line('.'), col('.')])
+
+ set rightleftcmd&
+ set rightleft&
+ set arabic&
+ bwipe!
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_arglist.vim b/src/nvim/testdir/test_arglist.vim
index 164149476f..ca7c8574cb 100644
--- a/src/nvim/testdir/test_arglist.vim
+++ b/src/nvim/testdir/test_arglist.vim
@@ -1,5 +1,8 @@
" Test argument list commands
+source shared.vim
+source term_util.vim
+
func Reset_arglist()
args a | %argd
endfunc
@@ -510,3 +513,42 @@ func Test_argdo()
call assert_equal(['Xa.c', 'Xb.c', 'Xc.c'], l)
bwipe Xa.c Xb.c Xc.c
endfunc
+
+" Test for quiting Vim with unedited files in the argument list
+func Test_quit_with_arglist()
+ if !CanRunVimInTerminal()
+ throw 'Skipped: cannot run vim in terminal'
+ endif
+ let buf = RunVimInTerminal('', {'rows': 6})
+ call term_sendkeys(buf, ":set nomore\n")
+ call term_sendkeys(buf, ":args a b c\n")
+ call term_sendkeys(buf, ":quit\n")
+ call term_wait(buf)
+ call WaitForAssert({-> assert_match('^E173:', term_getline(buf, 6))})
+ call StopVimInTerminal(buf)
+
+ " Try :confirm quit with unedited files in arglist
+ let buf = RunVimInTerminal('', {'rows': 6})
+ call term_sendkeys(buf, ":set nomore\n")
+ call term_sendkeys(buf, ":args a b c\n")
+ call term_sendkeys(buf, ":confirm quit\n")
+ call term_wait(buf)
+ call WaitForAssert({-> assert_match('^\[Y\]es, (N)o: *$',
+ \ term_getline(buf, 6))})
+ call term_sendkeys(buf, "N")
+ call term_wait(buf)
+ call term_sendkeys(buf, ":confirm quit\n")
+ call WaitForAssert({-> assert_match('^\[Y\]es, (N)o: *$',
+ \ term_getline(buf, 6))})
+ call term_sendkeys(buf, "Y")
+ call term_wait(buf)
+ call WaitForAssert({-> assert_equal("finished", term_getstatus(buf))})
+ only!
+ " When this test fails, swap files are left behind which breaks subsequent
+ " tests
+ call delete('.a.swp')
+ call delete('.b.swp')
+ call delete('.c.swp')
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_assert.vim b/src/nvim/testdir/test_assert.vim
index 52f243aaea..fdd8b0bef6 100644
--- a/src/nvim/testdir/test_assert.vim
+++ b/src/nvim/testdir/test_assert.vim
@@ -1,5 +1,75 @@
" Test that the methods used for testing work.
+func Test_assert_false()
+ call assert_equal(0, assert_false(0))
+ call assert_equal(0, assert_false(v:false))
+ call assert_equal(0, v:false->assert_false())
+
+ call assert_equal(1, assert_false(123))
+ call assert_match("Expected False but got 123", v:errors[0])
+ call remove(v:errors, 0)
+
+ call assert_equal(1, 123->assert_false())
+ call assert_match("Expected False but got 123", v:errors[0])
+ call remove(v:errors, 0)
+endfunc
+
+func Test_assert_true()
+ call assert_equal(0, assert_true(1))
+ call assert_equal(0, assert_true(123))
+ call assert_equal(0, assert_true(v:true))
+ call assert_equal(0, v:true->assert_true())
+
+ call assert_equal(1, assert_true(0))
+ call assert_match("Expected True but got 0", v:errors[0])
+ call remove(v:errors, 0)
+
+ call assert_equal(1, 0->assert_true())
+ call assert_match("Expected True but got 0", v:errors[0])
+ call remove(v:errors, 0)
+endfunc
+
+func Test_assert_equal()
+ let s = 'foo'
+ call assert_equal(0, assert_equal('foo', s))
+ let n = 4
+ call assert_equal(0, assert_equal(4, n))
+ let l = [1, 2, 3]
+ call assert_equal(0, assert_equal([1, 2, 3], l))
+ call assert_equal(v:_null_list, v:_null_list)
+ call assert_equal(v:_null_list, [])
+ call assert_equal([], v:_null_list)
+
+ let s = 'foo'
+ call assert_equal(1, assert_equal('bar', s))
+ call assert_match("Expected 'bar' but got 'foo'", v:errors[0])
+ call remove(v:errors, 0)
+
+ call assert_equal('XxxxxxxxxxxxxxxxxxxxxxX', 'XyyyyyyyyyyyyyyyyyyyyyyyyyX')
+ call assert_match("Expected 'X\\\\\\[x occurs 21 times]X' but got 'X\\\\\\[y occurs 25 times]X'", v:errors[0])
+ call remove(v:errors, 0)
+endfunc
+
+func Test_assert_equal_dict()
+ call assert_equal(0, assert_equal(#{one: 1, two: 2}, #{two: 2, one: 1}))
+
+ call assert_equal(1, assert_equal(#{one: 1, two: 2}, #{two: 2, one: 3}))
+ call assert_match("Expected {'one': 1} but got {'one': 3} - 1 equal item omitted", v:errors[0])
+ call remove(v:errors, 0)
+
+ call assert_equal(1, assert_equal(#{one: 1, two: 2}, #{two: 22, one: 11}))
+ call assert_match("Expected {'one': 1, 'two': 2} but got {'one': 11, 'two': 22}", v:errors[0])
+ call remove(v:errors, 0)
+
+ call assert_equal(1, assert_equal(#{}, #{two: 2, one: 1}))
+ call assert_match("Expected {} but got {'one': 1, 'two': 2}", v:errors[0])
+ call remove(v:errors, 0)
+
+ call assert_equal(1, assert_equal(#{two: 2, one: 1}, #{}))
+ call assert_match("Expected {'one': 1, 'two': 2} but got {}", v:errors[0])
+ call remove(v:errors, 0)
+endfunc
+
func Test_assert_equalfile()
call assert_equal(1, assert_equalfile('abcabc', 'xyzxyz'))
call assert_match("E485: Can't read file abcabc", v:errors[0])
@@ -46,17 +116,129 @@ func Test_assert_equalfile()
call delete('Xtwo')
endfunc
+func Test_assert_notequal()
+ let n = 4
+ call assert_equal(0, assert_notequal('foo', n))
+ let s = 'foo'
+ call assert_equal(0, assert_notequal([1, 2, 3], s))
+
+ call assert_equal(1, assert_notequal('foo', s))
+ call assert_match("Expected not equal to 'foo'", v:errors[0])
+ call remove(v:errors, 0)
+endfunc
+
+func Test_assert_report()
+ call assert_equal(1, assert_report('something is wrong'))
+ call assert_match('something is wrong', v:errors[0])
+ call remove(v:errors, 0)
+ call assert_equal(1, 'also wrong'->assert_report())
+ call assert_match('also wrong', v:errors[0])
+ call remove(v:errors, 0)
+endfunc
+
+func Test_assert_exception()
+ try
+ nocommand
+ catch
+ call assert_equal(0, assert_exception('E492:'))
+ endtry
+
+ try
+ nocommand
+ catch
+ try
+ " illegal argument, get NULL for error
+ call assert_equal(1, assert_exception([]))
+ catch
+ call assert_equal(0, assert_exception('E730:'))
+ endtry
+ endtry
+endfunc
+
+func Test_wrong_error_type()
+ let save_verrors = v:errors
+ let v:['errors'] = {'foo': 3}
+ call assert_equal('yes', 'no')
+ let verrors = v:errors
+ let v:errors = save_verrors
+ call assert_equal(type([]), type(verrors))
+endfunc
+
+func Test_match()
+ call assert_equal(0, assert_match('^f.*b.*r$', 'foobar'))
+
+ call assert_equal(1, assert_match('bar.*foo', 'foobar'))
+ call assert_match("Pattern 'bar.*foo' does not match 'foobar'", v:errors[0])
+ call remove(v:errors, 0)
+
+ call assert_equal(1, assert_match('bar.*foo', 'foobar', 'wrong'))
+ call assert_match('wrong', v:errors[0])
+ call remove(v:errors, 0)
+
+ call assert_equal(1, 'foobar'->assert_match('bar.*foo', 'wrong'))
+ call assert_match('wrong', v:errors[0])
+ call remove(v:errors, 0)
+endfunc
+
+func Test_notmatch()
+ call assert_equal(0, assert_notmatch('foo', 'bar'))
+ call assert_equal(0, assert_notmatch('^foobar$', 'foobars'))
+
+ call assert_equal(1, assert_notmatch('foo', 'foobar'))
+ call assert_match("Pattern 'foo' does match 'foobar'", v:errors[0])
+ call remove(v:errors, 0)
+
+ call assert_equal(1, 'foobar'->assert_notmatch('foo'))
+ call assert_match("Pattern 'foo' does match 'foobar'", v:errors[0])
+ call remove(v:errors, 0)
+endfunc
+
+func Test_assert_fail_fails()
+ call assert_equal(1, assert_fails('xxx', 'E12345'))
+ call assert_match("Expected 'E12345' but got 'E492:", v:errors[0])
+ call remove(v:errors, 0)
+
+ call assert_equal(1, assert_fails('xxx', 'E9876', 'stupid'))
+ call assert_match("stupid: Expected 'E9876' but got 'E492:", v:errors[0])
+ call remove(v:errors, 0)
+
+ call assert_equal(1, assert_fails('echo', '', 'echo command'))
+ call assert_match("command did not fail: echo command", v:errors[0])
+ call remove(v:errors, 0)
+
+ call assert_equal(1, 'echo'->assert_fails('', 'echo command'))
+ call assert_match("command did not fail: echo command", v:errors[0])
+ call remove(v:errors, 0)
+endfunc
+
func Test_assert_fails_in_try_block()
try
call assert_equal(0, assert_fails('throw "error"'))
endtry
endfunc
+func Test_assert_beeps()
+ new
+ call assert_equal(0, assert_beeps('normal h'))
+
+ call assert_equal(1, assert_beeps('normal 0'))
+ call assert_match("command did not beep: normal 0", v:errors[0])
+ call remove(v:errors, 0)
+
+ call assert_equal(0, 'normal h'->assert_beeps())
+ call assert_equal(1, 'normal 0'->assert_beeps())
+ call assert_match("command did not beep: normal 0", v:errors[0])
+ call remove(v:errors, 0)
+
+ bwipe
+endfunc
+
func Test_assert_inrange()
call assert_equal(0, assert_inrange(7, 7, 7))
call assert_equal(0, assert_inrange(5, 7, 5))
call assert_equal(0, assert_inrange(5, 7, 6))
call assert_equal(0, assert_inrange(5, 7, 7))
+
call assert_equal(1, assert_inrange(5, 7, 4))
call assert_match("Expected range 5 - 7, but got 4", v:errors[0])
call remove(v:errors, 0)
@@ -64,6 +246,12 @@ func Test_assert_inrange()
call assert_match("Expected range 5 - 7, but got 8", v:errors[0])
call remove(v:errors, 0)
+ call assert_equal(0, 5->assert_inrange(5, 7))
+ call assert_equal(0, 7->assert_inrange(5, 7))
+ call assert_equal(1, 8->assert_inrange(5, 7))
+ call assert_match("Expected range 5 - 7, but got 8", v:errors[0])
+ call remove(v:errors, 0)
+
call assert_fails('call assert_inrange(1, 1)', 'E119:')
if has('float')
@@ -83,6 +271,32 @@ func Test_assert_inrange()
endif
endfunc
+func Test_assert_with_msg()
+ call assert_equal('foo', 'bar', 'testing')
+ call assert_match("testing: Expected 'foo' but got 'bar'", v:errors[0])
+ call remove(v:errors, 0)
+endfunc
+
+func Test_mouse_position()
+ throw 'Skipped: Nvim does not have test_setmouse()'
+ let save_mouse = &mouse
+ set mouse=a
+ new
+ call setline(1, ['line one', 'line two'])
+ call assert_equal([0, 1, 1, 0], getpos('.'))
+ call test_setmouse(1, 5)
+ call feedkeys("\<LeftMouse>", "xt")
+ call assert_equal([0, 1, 5, 0], getpos('.'))
+ call test_setmouse(2, 20)
+ call feedkeys("\<LeftMouse>", "xt")
+ call assert_equal([0, 2, 8, 0], getpos('.'))
+ call test_setmouse(5, 1)
+ call feedkeys("\<LeftMouse>", "xt")
+ call assert_equal([0, 2, 1, 0], getpos('.'))
+ bwipe!
+ let &mouse = save_mouse
+endfunc
+
" Must be last.
func Test_zz_quit_detected()
" Verify that if a test function ends Vim the test script detects this.
diff --git a/src/nvim/testdir/test_autochdir.vim b/src/nvim/testdir/test_autochdir.vim
index 9ad727241e..4229095f9f 100644
--- a/src/nvim/testdir/test_autochdir.vim
+++ b/src/nvim/testdir/test_autochdir.vim
@@ -26,6 +26,54 @@ func Test_set_filename()
call delete('samples/Xtest')
endfunc
+func Test_set_filename_other_window()
+ CheckFunction test_autochdir
+ let cwd = getcwd()
+ call test_autochdir()
+ call mkdir('Xa')
+ call mkdir('Xb')
+ call mkdir('Xc')
+ try
+ args Xa/aaa.txt Xb/bbb.txt
+ set acd
+ let winid = win_getid()
+ snext
+ call assert_equal('Xb', substitute(getcwd(), '.*/\([^/]*\)$', '\1', ''))
+ call win_execute(winid, 'file ' .. cwd .. '/Xc/ccc.txt')
+ call assert_equal('Xb', substitute(getcwd(), '.*/\([^/]*\)$', '\1', ''))
+ finally
+ set noacd
+ call chdir(cwd)
+ call delete('Xa', 'rf')
+ call delete('Xb', 'rf')
+ call delete('Xc', 'rf')
+ bwipe! aaa.txt
+ bwipe! bbb.txt
+ bwipe! ccc.txt
+ endtry
+endfunc
+
+func Test_acd_win_execute()
+ CheckFunction test_autochdir
+ let cwd = getcwd()
+ set acd
+ call test_autochdir()
+
+ call mkdir('Xfile')
+ let winid = win_getid()
+ new Xfile/file
+ call assert_match('testdir.Xfile$', getcwd())
+ cd ..
+ call assert_match('testdir$', getcwd())
+ call win_execute(winid, 'echo')
+ call assert_match('testdir$', getcwd())
+
+ bwipe!
+ set noacd
+ call chdir(cwd)
+ call delete('Xfile', 'rf')
+endfunc
+
func Test_verbose_pwd()
CheckFunction test_autochdir
let cwd = getcwd()
@@ -42,20 +90,27 @@ func Test_verbose_pwd()
set acd
wincmd w
call assert_match('\[autochdir\].*testdir$', execute('verbose pwd'))
- execute 'lcd' cwd
- call assert_match('\[window\].*testdir$', execute('verbose pwd'))
execute 'tcd' cwd
call assert_match('\[tabpage\].*testdir$', execute('verbose pwd'))
execute 'cd' cwd
call assert_match('\[global\].*testdir$', execute('verbose pwd'))
+ execute 'lcd' cwd
+ call assert_match('\[window\].*testdir$', execute('verbose pwd'))
edit
call assert_match('\[autochdir\].*testdir$', execute('verbose pwd'))
+ enew
+ wincmd w
+ call assert_match('\[autochdir\].*testdir[/\\]Xautodir', execute('verbose pwd'))
+ wincmd w
+ call assert_match('\[window\].*testdir$', execute('verbose pwd'))
wincmd w
call assert_match('\[autochdir\].*testdir[/\\]Xautodir', execute('verbose pwd'))
set noacd
call assert_match('\[autochdir\].*testdir[/\\]Xautodir', execute('verbose pwd'))
wincmd w
- call assert_match('\[global\].*testdir', execute('verbose pwd'))
+ call assert_match('\[window\].*testdir$', execute('verbose pwd'))
+ execute 'cd' cwd
+ call assert_match('\[global\].*testdir$', execute('verbose pwd'))
wincmd w
call assert_match('\[window\].*testdir[/\\]Xautodir', execute('verbose pwd'))
@@ -64,4 +119,14 @@ func Test_verbose_pwd()
call delete('Xautodir', 'rf')
endfunc
+func Test_multibyte()
+ " using an invalid character should not cause a crash
+ set wic
+ " Except on Windows, E472 is also thrown last, but v8.1.1183 isn't ported yet
+ " call assert_fails('tc *', has('win32') ? 'E480:' : 'E344:')
+ call assert_fails('tc *', has('win32') ? 'E480:' : 'E472:')
+ set nowic
+endfunc
+
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index 45285b69a1..438851a0ad 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -3,8 +3,9 @@
source shared.vim
source check.vim
source term_util.vim
+source screendump.vim
-func! s:cleanup_buffers() abort
+func s:cleanup_buffers() abort
for bnr in range(1, bufnr('$'))
if bufloaded(bnr) && bufnr('%') != bnr
execute 'bd! ' . bnr
@@ -168,7 +169,9 @@ func Test_autocmd_bufunload_avoiding_SEGV_01()
exe 'autocmd BufUnload <buffer> ' . (lastbuf + 1) . 'bwipeout!'
augroup END
- call assert_fails('edit bb.txt', 'E937:')
+ " Todo: check for E937 generated first
+ " call assert_fails('edit bb.txt', 'E937:')
+ call assert_fails('edit bb.txt', 'E517:')
autocmd! test_autocmd_bufunload
augroup! test_autocmd_bufunload
@@ -260,6 +263,84 @@ func Test_win_tab_autocmd()
unlet g:record
endfunc
+func Test_WinScrolled()
+ CheckRunVimInTerminal
+
+ let lines =<< trim END
+ set nowrap scrolloff=0
+ for ii in range(1, 18)
+ call setline(ii, repeat(nr2char(96 + ii), ii * 2))
+ endfor
+ let win_id = win_getid()
+ let g:matched = v:false
+ execute 'au WinScrolled' win_id 'let g:matched = v:true'
+ let g:scrolled = 0
+ au WinScrolled * let g:scrolled += 1
+ au WinScrolled * let g:amatch = str2nr(expand('<amatch>'))
+ au WinScrolled * let g:afile = str2nr(expand('<afile>'))
+ END
+ call writefile(lines, 'Xtest_winscrolled')
+ let buf = RunVimInTerminal('-S Xtest_winscrolled', {'rows': 6})
+
+ call term_sendkeys(buf, ":echo g:scrolled\<CR>")
+ call WaitForAssert({-> assert_match('^0 ', term_getline(buf, 6))}, 1000)
+
+ " Scroll left/right in Normal mode.
+ call term_sendkeys(buf, "zlzh:echo g:scrolled\<CR>")
+ call WaitForAssert({-> assert_match('^2 ', term_getline(buf, 6))}, 1000)
+
+ " Scroll up/down in Normal mode.
+ call term_sendkeys(buf, "\<c-e>\<c-y>:echo g:scrolled\<CR>")
+ call WaitForAssert({-> assert_match('^4 ', term_getline(buf, 6))}, 1000)
+
+ " Scroll up/down in Insert mode.
+ call term_sendkeys(buf, "Mi\<c-x>\<c-e>\<Esc>i\<c-x>\<c-y>\<Esc>")
+ call term_sendkeys(buf, ":echo g:scrolled\<CR>")
+ call WaitForAssert({-> assert_match('^6 ', term_getline(buf, 6))}, 1000)
+
+ " Scroll the window horizontally to focus the last letter of the third line
+ " containing only six characters. Moving to the previous and shorter lines
+ " should trigger another autocommand as Vim has to make them visible.
+ call term_sendkeys(buf, "5zl2k")
+ call term_sendkeys(buf, ":echo g:scrolled\<CR>")
+ call WaitForAssert({-> assert_match('^8 ', term_getline(buf, 6))}, 1000)
+
+ " Ensure the command was triggered for the specified window ID.
+ call term_sendkeys(buf, ":echo g:matched\<CR>")
+ call WaitForAssert({-> assert_match('^v:true ', term_getline(buf, 6))}, 1000)
+
+ " Ensure the expansion of <amatch> and <afile> matches the window ID.
+ call term_sendkeys(buf, ":echo g:amatch == win_id && g:afile == win_id\<CR>")
+ call WaitForAssert({-> assert_match('^v:true ', term_getline(buf, 6))}, 1000)
+
+ call StopVimInTerminal(buf)
+ call delete('Xtest_winscrolled')
+endfunc
+
+func Test_WinScrolled_close_curwin()
+ CheckRunVimInTerminal
+
+ let lines =<< trim END
+ set nowrap scrolloff=0
+ call setline(1, ['aaa', 'bbb'])
+ vsplit
+ au WinScrolled * close
+ au VimLeave * call writefile(['123456'], 'Xtestout')
+ END
+ call writefile(lines, 'Xtest_winscrolled_close_curwin')
+ let buf = RunVimInTerminal('-S Xtest_winscrolled_close_curwin', {'rows': 6})
+
+ " This was using freed memory
+ call term_sendkeys(buf, "\<C-E>")
+ call TermWait(buf)
+ call StopVimInTerminal(buf)
+
+ call assert_equal(['123456'], readfile('Xtestout'))
+
+ call delete('Xtest_winscrolled_close_curwin')
+ call delete('Xtestout')
+endfunc
+
func Test_WinClosed()
" Test that the pattern is matched against the closed window's ID, and both
" <amatch> and <afile> are set to it.
@@ -299,6 +380,40 @@ func Test_WinClosed()
unlet g:triggered
endfunc
+func Test_WinClosed_throws()
+ vnew
+ let bnr = bufnr()
+ call assert_equal(1, bufloaded(bnr))
+ augroup test-WinClosed
+ autocmd WinClosed * throw 'foo'
+ augroup END
+ try
+ close
+ catch /.*/
+ endtry
+ call assert_equal(0, bufloaded(bnr))
+
+ autocmd! test-WinClosed
+ augroup! test-WinClosed
+endfunc
+
+func Test_WinClosed_throws_with_tabs()
+ tabnew
+ let bnr = bufnr()
+ call assert_equal(1, bufloaded(bnr))
+ augroup test-WinClosed
+ autocmd WinClosed * throw 'foo'
+ augroup END
+ try
+ close
+ catch /.*/
+ endtry
+ call assert_equal(0, bufloaded(bnr))
+
+ autocmd! test-WinClosed
+ augroup! test-WinClosed
+endfunc
+
func s:AddAnAutocmd()
augroup vimBarTest
au BufReadCmd * echo 'hello'
@@ -427,7 +542,7 @@ func Test_three_windows()
e Xtestje2
sp Xtestje1
call assert_fails('e', 'E937:')
- call assert_equal('Xtestje2', expand('%'))
+ call assert_equal('Xtestje1', expand('%'))
" Test changing buffers in a BufWipeout autocommand. If this goes wrong
" there are ml_line errors and/or a Crash.
@@ -450,7 +565,6 @@ func Test_three_windows()
au!
enew
- bwipe! Xtestje1
call delete('Xtestje1')
call delete('Xtestje2')
call delete('Xtestje3')
@@ -502,7 +616,7 @@ func Test_autocmd_bufwipe_in_SessLoadPost()
[CODE]
call writefile(content, 'Xvimrc')
- call system(v:progpath. ' --headless -i NONE -u Xvimrc --noplugins -S Session.vim -c cq')
+ call system(GetVimCommand('Xvimrc') .. ' --headless --noplugins -S Session.vim -c cq')
let errors = join(readfile('Xerrors'))
call assert_match('E814', errors)
@@ -562,7 +676,7 @@ func Test_autocmd_bufwipe_in_SessLoadPost2()
[CODE]
call writefile(content, 'Xvimrc')
- call system(v:progpath. ' --headless -i NONE -u Xvimrc --noplugins -S Session.vim -c cq')
+ call system(GetVimCommand('Xvimrc') .. ' --headless --noplugins -S Session.vim -c cq')
let errors = join(readfile('Xerrors'))
" This probably only ever matches on unix.
call assert_notmatch('Caught deadly signal SEGV', errors)
@@ -1506,7 +1620,7 @@ func Test_bufunload_all()
call writefile(content, 'Xtest')
call delete('Xout')
- call system(v:progpath. ' -u NORC -i NONE -N -S Xtest')
+ call system(GetVimCommandClean() .. ' -N --headless -S Xtest')
call assert_true(filereadable('Xout'))
call delete('Xxx1')
@@ -1606,7 +1720,7 @@ func Test_Cmd_Autocmds()
au BufWriteCmd XtestE call extend(g:lines, getline(0, '$'))
wall " will write other window to 'lines'
call assert_equal(4, len(g:lines), g:lines)
- call assert_equal("\tasdf", g:lines[2])
+ call assert_equal("asdf", g:lines[2])
au! BufReadCmd
au! BufWriteCmd
@@ -1827,6 +1941,14 @@ func Test_autocommand_all_events()
call assert_fails('au * x bwipe', 'E1155:')
endfunc
+func Test_autocmd_user()
+ au User MyEvent let s:res = [expand("<afile>"), expand("<amatch>")]
+ doautocmd User MyEvent
+ call assert_equal(['MyEvent', 'MyEvent'], s:res)
+ au! User
+ unlet s:res
+endfunc
+
function s:Before_test_dirchanged()
augroup test_dirchanged
autocmd!
@@ -1850,14 +1972,23 @@ endfunc
function Test_dirchanged_global()
call s:Before_test_dirchanged()
+ autocmd test_dirchanged DirChangedPre global call add(s:li, expand("<amatch>") .. " pre cd " .. v:event.directory)
autocmd test_dirchanged DirChanged global call add(s:li, "cd:")
autocmd test_dirchanged DirChanged global call add(s:li, expand("<afile>"))
call chdir(s:dir_foo)
- call assert_equal(["cd:", s:dir_foo], s:li)
+ let expected = ["global pre cd " .. s:dir_foo, "cd:", s:dir_foo]
+ call assert_equal(expected, s:li)
call chdir(s:dir_foo)
- call assert_equal(["cd:", s:dir_foo], s:li)
+ call assert_equal(expected, s:li)
exe 'lcd ' .. fnameescape(s:dir_bar)
- call assert_equal(["cd:", s:dir_foo], s:li)
+ call assert_equal(expected, s:li)
+
+ exe 'cd ' .. s:dir_foo
+ exe 'cd ' .. s:dir_bar
+ autocmd! test_dirchanged DirChanged global let g:result = expand("<afile>")
+ cd -
+ call assert_equal(s:dir_foo, substitute(g:result, '\\', '/', 'g'))
+
call s:After_test_dirchanged()
endfunc
@@ -1879,6 +2010,7 @@ function Test_dirchanged_auto()
CheckOption autochdir
call s:Before_test_dirchanged()
call test_autochdir()
+ autocmd test_dirchanged DirChangedPre auto call add(s:li, "pre cd " .. v:event.directory)
autocmd test_dirchanged DirChanged auto call add(s:li, "auto:")
autocmd test_dirchanged DirChanged auto call add(s:li, expand("<afile>"))
set acd
@@ -1886,7 +2018,8 @@ function Test_dirchanged_auto()
call assert_equal([], s:li)
exe 'edit ' . s:dir_foo . '/Xfile'
call assert_equal(s:dir_foo, getcwd())
- call assert_equal(["auto:", s:dir_foo], s:li)
+ let expected = ["pre cd " .. s:dir_foo, "auto:", s:dir_foo]
+ call assert_equal(expected, s:li)
set noacd
bwipe!
call s:After_test_dirchanged()
@@ -2093,7 +2226,7 @@ func Test_autocmd_bufreadpre()
" (even though the position will be invalid, this should make Vim reset the
" cursor position in the other window.
wincmd p
- 1
+ 1 " set cpo+=g
" won't do anything, but try to set the cursor on an invalid lnum
autocmd BufReadPre <buffer> :norm! 70gg
" triggers BufReadPre, should not move the cursor in either window
@@ -2108,8 +2241,11 @@ func Test_autocmd_bufreadpre()
close
close
call delete('XAutocmdBufReadPre.txt')
+ " set cpo-=g
endfunc
+" FileChangedShell tested in test_filechanged.vim
+
" Tests for the following autocommands:
" - FileWritePre writing a compressed file
" - FileReadPost reading a compressed file
@@ -2331,6 +2467,19 @@ func Test_throw_in_BufWritePre()
au! throwing
endfunc
+func Test_autocmd_in_try_block()
+ call mkdir('Xdir')
+ au BufEnter * let g:fname = expand('%')
+ try
+ edit Xdir/
+ endtry
+ call assert_match('Xdir', g:fname)
+
+ unlet g:fname
+ au! BufEnter
+ call delete('Xdir', 'rf')
+endfunc
+
func Test_autocmd_CmdWinEnter()
CheckRunVimInTerminal
" There is not cmdwin switch, so
@@ -2380,7 +2529,63 @@ func Test_autocmd_was_using_freed_memory()
pclose
endfunc
-" FileChangedShell tested in test_filechanged.vim
+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
+
+" Test closing a window or editing another buffer from a FileChangedRO handler
+" in a readonly buffer
+func Test_FileChangedRO_winclose()
+ augroup FileChangedROTest
+ au!
+ autocmd FileChangedRO * quit
+ augroup END
+ new
+ set readonly
+ call assert_fails('normal i', 'E788:')
+ close
+ augroup! FileChangedROTest
+
+ augroup FileChangedROTest
+ au!
+ autocmd FileChangedRO * edit Xfile
+ augroup END
+ new
+ set readonly
+ call assert_fails('normal i', 'E788:')
+ close
+ augroup! FileChangedROTest
+endfunc
func LogACmd()
call add(g:logged, line('$'))
@@ -2489,6 +2694,27 @@ func Test_autocmd_window()
%bw!
endfunc
+" Test for trying to close the temporary window used for executing an autocmd
+func Test_close_autocmd_window()
+ %bw!
+ edit one.txt
+ tabnew two.txt
+ augroup aucmd_win_test2
+ au!
+ " Nvim makes aucmd_win the last window
+ " au BufEnter * if expand('<afile>') == 'one.txt' | 1close | endif
+ au BufEnter * if expand('<afile>') == 'one.txt' | close | endif
+ augroup END
+
+ call assert_fails('doautoall BufEnter', 'E813:')
+
+ augroup aucmd_win_test2
+ au!
+ augroup END
+ augroup! aucmd_win_test2
+ %bw!
+endfunc
+
" Test for trying to close the tab that has the temporary window for exeucing
" an autocmd.
func Test_close_autocmd_tab()
@@ -2509,13 +2735,23 @@ func Test_close_autocmd_tab()
%bwipe!
endfunc
+func Test_Visual_doautoall_redraw()
+ call setline(1, ['a', 'b'])
+ new
+ wincmd p
+ call feedkeys("G\<C-V>", 'txn')
+ autocmd User Explode ++once redraw
+ doautoall User Explode
+ %bwipe!
+endfunc
+
func Test_autocmd_closes_window()
au BufNew,BufWinLeave * e %e
file yyy
au BufNew,BufWinLeave * ball
- call assert_fails('n xxx', 'E143:')
+ n xxx
- bwipe %
+ %bwipe
au! BufNew
au! BufWinLeave
endfunc
@@ -2531,9 +2767,34 @@ func Test_autocmd_quit_psearch()
augroup aucmd_win_test
au!
augroup END
+ new
+ pclose
+endfunc
+
+" Fuzzer found some strange combination that caused a crash.
+func Test_autocmd_normal_mess()
+ " For unknown reason this hangs on MS-Windows
+ CheckNotMSWindows
+
+ augroup aucmd_normal_test
+ au BufLeave,BufWinLeave,BufHidden,BufUnload,BufDelete,BufWipeout * norm 7q/qc
+ augroup END
+ " Nvim has removed :open
+ " call assert_fails('o4', 'E1159')
+ call assert_fails('e4', 'E1159')
+ silent! H
+ call assert_fails('e xx', 'E1159')
+ normal G
+
+ augroup aucmd_normal_test
+ au!
+ augroup END
endfunc
func Test_autocmd_closing_cmdwin()
+ " For unknown reason this hangs on MS-Windows
+ CheckNotMSWindows
+
au BufWinLeave * nested q
call assert_fails("norm 7q?\n", 'E855:')
@@ -2542,4 +2803,71 @@ func Test_autocmd_closing_cmdwin()
only
endfunc
+func Test_autocmd_vimgrep()
+ augroup aucmd_vimgrep
+ au QuickfixCmdPre,BufNew,BufReadCmd * sb
+ " Nvim makes aucmd_win the last window
+ " au QuickfixCmdPre,BufNew,BufReadCmd * q9
+ au QuickfixCmdPre,BufNew,BufReadCmd * exe 'q' .. (winnr('$') - (win_gettype(winnr('$')) == 'autocmd'))
+ augroup END
+ call assert_fails('lv ?a? foo', 'E926:')
+
+ augroup aucmd_vimgrep
+ au!
+ augroup END
+endfunc
+
+func Test_bufwipeout_changes_window()
+ " This should not crash, but we don't have any expectations about what
+ " happens, changing window in BufWipeout has unpredictable results.
+ tabedit
+ let g:window_id = win_getid()
+ topleft new
+ setlocal bufhidden=wipe
+ autocmd BufWipeout <buffer> call win_gotoid(g:window_id)
+ tabprevious
+ +tabclose
+
+ unlet g:window_id
+ au! BufWipeout
+ %bwipe!
+endfunc
+
+func Test_v_event_readonly()
+ autocmd CompleteChanged * let v:event.width = 0
+ call assert_fails("normal! i\<C-X>\<C-V>", 'E46:')
+ au! CompleteChanged
+
+ autocmd DirChangedPre * let v:event.directory = ''
+ call assert_fails('cd .', 'E46:')
+ au! DirChangedPre
+
+ autocmd ModeChanged * let v:event.new_mode = ''
+ call assert_fails('normal! cc', 'E46:')
+ au! ModeChanged
+
+ autocmd TextYankPost * let v:event.operator = ''
+ call assert_fails('normal! yy', 'E46:')
+ au! TextYankPost
+endfunc
+
+
+func Test_noname_autocmd()
+ augroup test_noname_autocmd_group
+ autocmd!
+ autocmd BufEnter * call add(s:li, ["BufEnter", expand("<afile>")])
+ autocmd BufDelete * call add(s:li, ["BufDelete", expand("<afile>")])
+ autocmd BufLeave * call add(s:li, ["BufLeave", expand("<afile>")])
+ autocmd BufUnload * call add(s:li, ["BufUnload", expand("<afile>")])
+ autocmd BufWipeout * call add(s:li, ["BufWipeout", expand("<afile>")])
+ augroup END
+
+ let s:li = []
+ edit foo
+ call assert_equal([['BufUnload', ''], ['BufDelete', ''], ['BufWipeout', ''], ['BufEnter', 'foo']], s:li)
+
+ au! test_noname_autocmd_group
+ augroup! test_noname_autocmd_group
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_backspace_opt.vim b/src/nvim/testdir/test_backspace_opt.vim
index 11459991ea..59e94d2898 100644
--- a/src/nvim/testdir/test_backspace_opt.vim
+++ b/src/nvim/testdir/test_backspace_opt.vim
@@ -76,7 +76,7 @@ func Test_backspace_ctrl_u()
set cpo-=<
inoremap <c-u> <left><c-u>
- exe "normal Avim3\<C-U>\<Esc>\<CR>"
+ exe "normal Avim3\<*C-U>\<Esc>\<CR>"
iunmap <c-u>
exe "normal Avim4\<C-U>\<C-U>\<Esc>\<CR>"
@@ -86,7 +86,7 @@ func Test_backspace_ctrl_u()
exe "normal A vim6\<Esc>Azwei\<C-G>u\<C-U>\<Esc>\<CR>"
inoremap <c-u> <left><c-u>
- exe "normal A vim7\<C-U>\<C-U>\<Esc>\<CR>"
+ exe "normal A vim7\<*C-U>\<*C-U>\<Esc>\<CR>"
call assert_equal([
\ "1 this shouldn't be deleted",
diff --git a/src/nvim/testdir/test_blockedit.vim b/src/nvim/testdir/test_blockedit.vim
index 38978ef689..7b56b1554f 100644
--- a/src/nvim/testdir/test_blockedit.vim
+++ b/src/nvim/testdir/test_blockedit.vim
@@ -81,4 +81,52 @@ func Test_blockinsert_delete()
bwipe!
endfunc
+func Test_blockappend_eol_cursor()
+ new
+ " Test 1 Move 1 char left
+ call setline(1, ['aaa', 'bbb', 'ccc'])
+ exe "norm! gg$\<c-v>2jA\<left>x\<esc>"
+ call assert_equal(['aaxa', 'bbxb', 'ccxc'], getline(1, '$'))
+ " Test 2 Move 2 chars left
+ sil %d
+ call setline(1, ['aaa', 'bbb', 'ccc'])
+ exe "norm! gg$\<c-v>2jA\<left>\<left>x\<esc>"
+ call assert_equal(['axaa', 'bxbb', 'cxcc'], getline(1, '$'))
+ " Test 3 Move 3 chars left (outside of the visual selection)
+ sil %d
+ call setline(1, ['aaa', 'bbb', 'ccc'])
+ exe "norm! ggl$\<c-v>2jA\<left>\<left>\<left>x\<esc>"
+ call assert_equal(['xaaa', 'bbb', 'ccc'], getline(1, '$'))
+ bw!
+endfunc
+
+func Test_blockappend_eol_cursor2()
+ new
+ " Test 1 Move 1 char left
+ call setline(1, ['aaaaa', 'bbb', 'ccccc'])
+ exe "norm! gg\<c-v>$2jA\<left>x\<esc>"
+ call assert_equal(['aaaaxa', 'bbbx', 'ccccxc'], getline(1, '$'))
+ " Test 2 Move 2 chars left
+ sil %d
+ call setline(1, ['aaaaa', 'bbb', 'ccccc'])
+ exe "norm! gg\<c-v>$2jA\<left>\<left>x\<esc>"
+ call assert_equal(['aaaxaa', 'bbbx', 'cccxcc'], getline(1, '$'))
+ " Test 3 Move 3 chars left (to the beginning of the visual selection)
+ sil %d
+ call setline(1, ['aaaaa', 'bbb', 'ccccc'])
+ exe "norm! gg\<c-v>$2jA\<left>\<left>\<left>x\<esc>"
+ call assert_equal(['aaxaaa', 'bbxb', 'ccxccc'], getline(1, '$'))
+ " Test 4 Move 3 chars left (outside of the visual selection)
+ sil %d
+ call setline(1, ['aaaaa', 'bbb', 'ccccc'])
+ exe "norm! ggl\<c-v>$2jA\<left>\<left>\<left>x\<esc>"
+ call assert_equal(['aaxaaa', 'bbxb', 'ccxccc'], getline(1, '$'))
+ " Test 5 Move 4 chars left (outside of the visual selection)
+ sil %d
+ call setline(1, ['aaaaa', 'bbb', 'ccccc'])
+ exe "norm! ggl\<c-v>$2jA\<left>\<left>\<left>\<left>x\<esc>"
+ call assert_equal(['axaaaa', 'bxbb', 'cxcccc'], getline(1, '$'))
+ bw!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_breakindent.vim b/src/nvim/testdir/test_breakindent.vim
index 8d592f21ea..a37751e748 100644
--- a/src/nvim/testdir/test_breakindent.vim
+++ b/src/nvim/testdir/test_breakindent.vim
@@ -1,7 +1,7 @@
" Test for breakindent
"
" Note: if you get strange failures when adding new tests, it might be that
-" while the test is run, the breakindent cacheing gets in its way.
+" while the test is run, the breakindent caching gets in its way.
" It helps to change the tabstop setting and force a redraw (e.g. see
" Test_breakindent08())
if !exists('+breakindent')
@@ -20,7 +20,7 @@ func s:screen_lines2(lnums, lnume, width) abort
return ScreenLines([a:lnums, a:lnume], a:width)
endfunc
-func! s:compare_lines(expect, actual)
+func s:compare_lines(expect, actual)
call assert_equal(join(a:expect, "\n"), join(a:actual, "\n"))
endfunc
@@ -770,7 +770,7 @@ func Test_breakindent20_list()
\ "shall make no law ",
\ ]
call s:compare_lines(expect, lines)
- " set mininum indent
+ " set minimum indent
setl briopt=min:5
redraw!
let lines = s:screen_lines2(1, 6, 20)
diff --git a/src/nvim/testdir/test_buffer.vim b/src/nvim/testdir/test_buffer.vim
index 40111fdf06..67be3e6747 100644
--- a/src/nvim/testdir/test_buffer.vim
+++ b/src/nvim/testdir/test_buffer.vim
@@ -1,5 +1,162 @@
" Tests for Vim buffer
+source check.vim
+
+" Test for the :bunload command with an offset
+func Test_bunload_with_offset()
+ %bwipe!
+ call writefile(['B1'], 'b1')
+ call writefile(['B2'], 'b2')
+ call writefile(['B3'], 'b3')
+ call writefile(['B4'], 'b4')
+
+ " Load four buffers. Unload the second and third buffers and then
+ " execute .+3bunload to unload the last buffer.
+ edit b1
+ new b2
+ new b3
+ new b4
+
+ bunload b2
+ bunload b3
+ exe bufwinnr('b1') . 'wincmd w'
+ .+3bunload
+ call assert_equal(0, getbufinfo('b4')[0].loaded)
+ call assert_equal('b1',
+ \ fnamemodify(getbufinfo({'bufloaded' : 1})[0].name, ':t'))
+
+ " Load four buffers. Unload the third and fourth buffers. Execute .+3bunload
+ " and check whether the second buffer is unloaded.
+ ball
+ bunload b3
+ bunload b4
+ exe bufwinnr('b1') . 'wincmd w'
+ .+3bunload
+ call assert_equal(0, getbufinfo('b2')[0].loaded)
+ call assert_equal('b1',
+ \ fnamemodify(getbufinfo({'bufloaded' : 1})[0].name, ':t'))
+
+ " Load four buffers. Unload the second and third buffers and from the last
+ " buffer execute .-3bunload to unload the first buffer.
+ ball
+ bunload b2
+ bunload b3
+ exe bufwinnr('b4') . 'wincmd w'
+ .-3bunload
+ call assert_equal(0, getbufinfo('b1')[0].loaded)
+ call assert_equal('b4',
+ \ fnamemodify(getbufinfo({'bufloaded' : 1})[0].name, ':t'))
+
+ " Load four buffers. Unload the first and second buffers. Execute .-3bunload
+ " from the last buffer and check whether the third buffer is unloaded.
+ ball
+ bunload b1
+ bunload b2
+ exe bufwinnr('b4') . 'wincmd w'
+ .-3bunload
+ call assert_equal(0, getbufinfo('b3')[0].loaded)
+ call assert_equal('b4',
+ \ fnamemodify(getbufinfo({'bufloaded' : 1})[0].name, ':t'))
+
+ %bwipe!
+ call delete('b1')
+ call delete('b2')
+ call delete('b3')
+ call delete('b4')
+
+ call assert_fails('1,4bunload', 'E16:')
+ call assert_fails(',100bunload', 'E16:')
+
+ " Use a try-catch for this test. When assert_fails() is used for this
+ " test, the command fails with E515: instead of E90:
+ let caught_E90 = 0
+ try
+ $bunload
+ catch /E90:/
+ let caught_E90 = 1
+ endtry
+ call assert_equal(1, caught_E90)
+ call assert_fails('$bunload', 'E515:')
+endfunc
+
+" Test for :buffer, :bnext, :bprevious, :brewind, :blast and :bmodified
+" commands
+func Test_buflist_browse()
+ %bwipe!
+ call assert_fails('buffer 1000', 'E86:')
+
+ call writefile(['foo1', 'foo2', 'foo3', 'foo4'], 'Xfile1')
+ call writefile(['bar1', 'bar2', 'bar3', 'bar4'], 'Xfile2')
+ call writefile(['baz1', 'baz2', 'baz3', 'baz4'], 'Xfile3')
+ edit Xfile1
+ let b1 = bufnr()
+ edit Xfile2
+ let b2 = bufnr()
+ edit +/baz4 Xfile3
+ let b3 = bufnr()
+
+ call assert_fails('buffer ' .. b1 .. ' abc', 'E488:')
+ call assert_equal(b3, bufnr())
+ call assert_equal(4, line('.'))
+ exe 'buffer +/bar2 ' .. b2
+ call assert_equal(b2, bufnr())
+ call assert_equal(2, line('.'))
+ exe 'buffer +/bar1'
+ call assert_equal(b2, bufnr())
+ call assert_equal(1, line('.'))
+
+ brewind +
+ call assert_equal(b1, bufnr())
+ call assert_equal(4, line('.'))
+
+ blast +/baz2
+ call assert_equal(b3, bufnr())
+ call assert_equal(2, line('.'))
+
+ bprevious +/bar4
+ call assert_equal(b2, bufnr())
+ call assert_equal(4, line('.'))
+
+ bnext +/baz3
+ call assert_equal(b3, bufnr())
+ call assert_equal(3, line('.'))
+
+ call assert_fails('bmodified', 'E84:')
+ call setbufvar(b2, '&modified', 1)
+ exe 'bmodified +/bar3'
+ call assert_equal(b2, bufnr())
+ call assert_equal(3, line('.'))
+
+ " With no listed buffers in the list, :bnext and :bprev should fail
+ %bwipe!
+ set nobuflisted
+ call assert_fails('bnext', 'E85:')
+ call assert_fails('bprev', 'E85:')
+ set buflisted
+
+ call assert_fails('sandbox bnext', 'E48:')
+
+ call delete('Xfile1')
+ call delete('Xfile2')
+ call delete('Xfile3')
+ %bwipe!
+endfunc
+
+" Test for :bdelete
+func Test_bdelete_cmd()
+ %bwipe!
+ call assert_fails('bdelete 5', 'E516:')
+ call assert_fails('1,1bdelete 1 2', 'E488:')
+
+ " Deleting a unlisted and unloaded buffer
+ edit Xfile1
+ let bnr = bufnr()
+ set nobuflisted
+ enew
+ call assert_fails('bdelete ' .. bnr, 'E516:')
+ %bwipe!
+endfunc
+
func Test_buffer_error()
new foo1
new foo2
@@ -30,4 +187,44 @@ 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
+
+" this was using a NULL pointer after failing to use the pattern
+func Test_buf_pattern_invalid()
+ vsplit 0000000
+ silent! buf [0--]\&\zs*\zs*e
+ bwipe!
+
+ vsplit 00000000000000000000000000
+ silent! buf [0--]\&\zs*\zs*e
+ bwipe!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_bufline.vim b/src/nvim/testdir/test_bufline.vim
index 765ae17736..ffb8e3facd 100644
--- a/src/nvim/testdir/test_bufline.vim
+++ b/src/nvim/testdir/test_bufline.vim
@@ -153,3 +153,38 @@ func Test_appendbufline_redraw()
call StopVimInTerminal(buf)
call delete('XscriptMatchCommon')
endfunc
+
+func Test_setbufline_select_mode()
+ new
+ call setline(1, ['foo', 'bar'])
+ call feedkeys("j^v2l\<C-G>", 'nx')
+
+ let bufnr = bufadd('Xdummy')
+ call bufload(bufnr)
+ call setbufline(bufnr, 1, ['abc'])
+
+ call feedkeys("x", 'nx')
+ call assert_equal(['foo', 'x'], getline(1, 2))
+
+ exe "bwipe! " .. bufnr
+ bwipe!
+endfunc
+
+func Test_deletebufline_select_mode()
+ new
+ call setline(1, ['foo', 'bar'])
+ call feedkeys("j^v2l\<C-G>", 'nx')
+
+ let bufnr = bufadd('Xdummy')
+ call bufload(bufnr)
+ call setbufline(bufnr, 1, ['abc', 'def'])
+ call deletebufline(bufnr, 1)
+
+ call feedkeys("x", 'nx')
+ call assert_equal(['foo', 'x'], getline(1, 2))
+
+ exe "bwipe! " .. bufnr
+ bwipe!
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_bufwintabinfo.vim b/src/nvim/testdir/test_bufwintabinfo.vim
index a6eb93b4be..326aefb731 100644
--- a/src/nvim/testdir/test_bufwintabinfo.vim
+++ b/src/nvim/testdir/test_bufwintabinfo.vim
@@ -145,6 +145,13 @@ function Test_get_win_options()
endif
endfunc
+function Test_getbufinfo_lastused()
+ new Xfoo
+ let info = getbufinfo('Xfoo')[0]
+ call assert_equal(has_key(info, 'lastused'), 1)
+ call assert_equal(type(info.lastused), type(0))
+endfunc
+
func Test_getbufinfo_lines()
new Xfoo
call setline(1, ['a', 'bc', 'd'])
@@ -155,9 +162,26 @@ func Test_getbufinfo_lines()
bw!
endfunc
-function Test_getbufinfo_lastused()
- new Xfoo
- let info = getbufinfo('Xfoo')[0]
- call assert_equal(has_key(info, 'lastused'), 1)
- call assert_equal(type(info.lastused), type(0))
+func Test_getwininfo_au()
+ enew
+ call setline(1, range(1, 16))
+
+ let g:info = #{}
+ augroup T1
+ au!
+ au WinEnter * let g:info = getwininfo(win_getid())[0]
+ augroup END
+
+ 4split
+ " Check that calling getwininfo() from WinEnter returns fresh values for
+ " topline and botline.
+ call assert_equal(1, g:info.topline)
+ call assert_equal(4, g:info.botline)
+ close
+
+ unlet g:info
+ augroup! T1
+ bwipe!
endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_cd.vim b/src/nvim/testdir/test_cd.vim
index 76a2620be0..a1e53df774 100644
--- a/src/nvim/testdir/test_cd.vim
+++ b/src/nvim/testdir/test_cd.vim
@@ -44,6 +44,15 @@ func Test_cd_minus()
cd -
call assert_equal(path, getcwd())
+ " Test for :cd - after a failed :cd
+ " v8.2.1183 is not ported yet
+ " call assert_fails('cd /nonexistent', 'E344:')
+ call assert_fails('cd /nonexistent', 'E472:')
+ call assert_equal(path, getcwd())
+ cd -
+ call assert_equal(path_dotdot, getcwd())
+ cd -
+
" Test for :cd - without a previous directory
let lines =<< trim [SCRIPT]
call assert_fails('cd -', 'E186:')
@@ -216,6 +225,21 @@ func Test_cd_from_non_existing_dir()
call assert_equal(saveddir, getcwd())
endfunc
+func Test_cd_completion()
+ call mkdir('XComplDir1', 'p')
+ call mkdir('XComplDir2', 'p')
+ call writefile([], 'XComplFile')
+
+ for cmd in ['cd', 'chdir', 'lcd', 'lchdir', 'tcd', 'tchdir']
+ call feedkeys(':' .. cmd .. " XCompl\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"' .. cmd .. ' XComplDir1/ XComplDir2/', @:)
+ endfor
+
+ call delete('XComplDir1', 'd')
+ call delete('XComplDir2', 'd')
+ call delete('XComplFile')
+endfunc
+
func Test_cd_unknown_dir()
call mkdir('Xa')
cd Xa
diff --git a/src/nvim/testdir/test_cdo.vim b/src/nvim/testdir/test_cdo.vim
new file mode 100644
index 0000000000..dbed7df4ac
--- /dev/null
+++ b/src/nvim/testdir/test_cdo.vim
@@ -0,0 +1,216 @@
+" Tests for the :cdo, :cfdo, :ldo and :lfdo commands
+
+source check.vim
+CheckFeature quickfix
+
+" Create the files used by the tests
+func SetUp()
+ call writefile(["Line1", "Line2", "Line3"], 'Xtestfile1')
+ call writefile(["Line1", "Line2", "Line3"], 'Xtestfile2')
+ call writefile(["Line1", "Line2", "Line3"], 'Xtestfile3')
+endfunc
+
+" Remove the files used by the tests
+func TearDown()
+ call delete('Xtestfile1')
+ call delete('Xtestfile2')
+ call delete('Xtestfile3')
+endfunc
+
+" Returns the current line in '<filename> <linenum>L <column>C' format
+func GetRuler()
+ return expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C'
+endfunc
+
+" Tests for the :cdo and :ldo commands
+func XdoTests(cchar)
+ enew
+
+ " Shortcuts for calling the cdo and ldo commands
+ let Xdo = a:cchar . 'do'
+ let Xgetexpr = a:cchar . 'getexpr'
+ let Xprev = a:cchar. 'prev'
+ let XdoCmd = Xdo . ' call add(l, GetRuler())'
+
+ " Try with an empty list
+ let l = []
+ exe XdoCmd
+ call assert_equal([], l)
+
+ " Populate the list and then try
+ exe Xgetexpr . " ['non-error 1', 'Xtestfile1:1:3:Line1', 'non-error 2', 'Xtestfile2:2:2:Line2', 'non-error 3', 'Xtestfile3:3:1:Line3']"
+
+ let l = []
+ exe XdoCmd
+ call assert_equal(['Xtestfile1 1L 3C', 'Xtestfile2 2L 2C', 'Xtestfile3 3L 1C'], l)
+
+ " Run command only on selected error lines
+ let l = []
+ enew
+ exe "2,3" . XdoCmd
+ call assert_equal(['Xtestfile2 2L 2C', 'Xtestfile3 3L 1C'], l)
+
+ " Boundary condition tests
+ let l = []
+ enew
+ exe "1,1" . XdoCmd
+ call assert_equal(['Xtestfile1 1L 3C'], l)
+
+ let l = []
+ enew
+ exe "3" . XdoCmd
+ call assert_equal(['Xtestfile3 3L 1C'], l)
+
+ " Range test commands
+ let l = []
+ enew
+ exe "%" . XdoCmd
+ call assert_equal(['Xtestfile1 1L 3C', 'Xtestfile2 2L 2C', 'Xtestfile3 3L 1C'], l)
+
+ let l = []
+ enew
+ exe "1,$" . XdoCmd
+ call assert_equal(['Xtestfile1 1L 3C', 'Xtestfile2 2L 2C', 'Xtestfile3 3L 1C'], l)
+
+ let l = []
+ enew
+ exe Xprev
+ exe "." . XdoCmd
+ call assert_equal(['Xtestfile2 2L 2C'], l)
+
+ let l = []
+ enew
+ exe "+" . XdoCmd
+ call assert_equal(['Xtestfile3 3L 1C'], l)
+
+ " Invalid error lines test
+ let l = []
+ enew
+ exe "silent! 27" . XdoCmd
+ exe "silent! 4,5" . XdoCmd
+ call assert_equal([], l)
+
+ " Run commands from an unsaved buffer
+ let v:errmsg=''
+ let l = []
+ enew
+ setlocal modified
+ exe "silent! 2,2" . XdoCmd
+ if v:errmsg !~# 'No write since last change'
+ call add(v:errors, 'Unsaved file change test failed')
+ endif
+
+ " If the executed command fails, then the operation should be aborted
+ enew!
+ let subst_count = 0
+ exe "silent!" . Xdo . " s/Line/xLine/ | let subst_count += 1"
+ if subst_count != 1 || getline('.') != 'xLine1'
+ call add(v:errors, 'Abort command on error test failed')
+ endif
+
+ let l = []
+ exe "2,2" . Xdo . "! call add(l, GetRuler())"
+ call assert_equal(['Xtestfile2 2L 2C'], l)
+
+ " List with no valid error entries
+ let l = []
+ edit! +2 Xtestfile1
+ exe Xgetexpr . " ['non-error 1', 'non-error 2', 'non-error 3']"
+ exe XdoCmd
+ call assert_equal([], l)
+ exe "silent! 2" . XdoCmd
+ call assert_equal([], l)
+ let v:errmsg=''
+ exe "%" . XdoCmd
+ exe "1,$" . XdoCmd
+ exe "." . XdoCmd
+ call assert_equal('', v:errmsg)
+
+ " List with only one valid entry
+ let l = []
+ exe Xgetexpr . " ['Xtestfile3:3:1:Line3']"
+ exe XdoCmd
+ call assert_equal(['Xtestfile3 3L 1C'], l)
+
+endfunc
+
+" Tests for the :cfdo and :lfdo commands
+func XfdoTests(cchar)
+ enew
+
+ " Shortcuts for calling the cfdo and lfdo commands
+ let Xfdo = a:cchar . 'fdo'
+ let Xgetexpr = a:cchar . 'getexpr'
+ let XfdoCmd = Xfdo . ' call add(l, GetRuler())'
+ let Xpfile = a:cchar. 'pfile'
+
+ " Clear the quickfix/location list
+ exe Xgetexpr . " []"
+
+ " Try with an empty list
+ let l = []
+ exe XfdoCmd
+ call assert_equal([], l)
+
+ " Populate the list and then try
+ exe Xgetexpr . " ['non-error 1', 'Xtestfile1:1:3:Line1', 'Xtestfile1:2:1:Line2', 'non-error 2', 'Xtestfile2:2:2:Line2', 'non-error 3', 'Xtestfile3:2:3:Line2', 'Xtestfile3:3:1:Line3']"
+
+ let l = []
+ exe XfdoCmd
+ call assert_equal(['Xtestfile1 1L 3C', 'Xtestfile2 2L 2C', 'Xtestfile3 2L 3C'], l)
+
+ " Run command only on selected error lines
+ let l = []
+ exe "2,3" . XfdoCmd
+ call assert_equal(['Xtestfile2 2L 2C', 'Xtestfile3 2L 3C'], l)
+
+ " Boundary condition tests
+ let l = []
+ exe "3" . XfdoCmd
+ call assert_equal(['Xtestfile3 2L 3C'], l)
+
+ " Range test commands
+ let l = []
+ exe "%" . XfdoCmd
+ call assert_equal(['Xtestfile1 1L 3C', 'Xtestfile2 2L 2C', 'Xtestfile3 2L 3C'], l)
+
+ let l = []
+ exe "1,$" . XfdoCmd
+ call assert_equal(['Xtestfile1 1L 3C', 'Xtestfile2 2L 2C', 'Xtestfile3 2L 3C'], l)
+
+ let l = []
+ exe Xpfile
+ exe "." . XfdoCmd
+ call assert_equal(['Xtestfile2 2L 2C'], l)
+
+ " List with only one valid entry
+ let l = []
+ exe Xgetexpr . " ['Xtestfile2:2:5:Line2']"
+ exe XfdoCmd
+ call assert_equal(['Xtestfile2 2L 5C'], l)
+
+endfunc
+
+" Tests for cdo and cfdo
+func Test_cdo()
+ call XdoTests('c')
+ call XfdoTests('c')
+endfunc
+
+" Tests for ldo and lfdo
+func Test_ldo()
+ call XdoTests('l')
+ call XfdoTests('l')
+endfunc
+
+" Test for making 'shm' doesn't interfere with the output.
+func Test_cdo_print()
+ enew | only!
+ cgetexpr ["Xtestfile1:1:Line1", "Xtestfile2:1:Line1", "Xtestfile3:1:Line1"]
+ cdo print
+ call assert_equal('Line1', Screenline(&lines))
+ call assert_equal('Line1', Screenline(&lines - 3))
+ call assert_equal('Line1', Screenline(&lines - 6))
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_changelist.vim b/src/nvim/testdir/test_changelist.vim
index ce77c1f3c7..3741f32e69 100644
--- a/src/nvim/testdir/test_changelist.vim
+++ b/src/nvim/testdir/test_changelist.vim
@@ -46,3 +46,5 @@ func Test_getchangelist()
call delete('Xfile1.txt')
call delete('Xfile2.txt')
endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_charsearch.vim b/src/nvim/testdir/test_charsearch.vim
index 6f09e85a42..d386d74f8d 100644
--- a/src/nvim/testdir/test_charsearch.vim
+++ b/src/nvim/testdir/test_charsearch.vim
@@ -1,3 +1,4 @@
+" Test for character search commands - t, T, f, F, ; and ,
func Test_charsearch()
enew!
@@ -28,6 +29,17 @@ func Test_charsearch()
set cpo-=;
normal! ;;p
call assert_equal('ZabcdeZfghijkZZemnokqretkZvwxyz', getline(3))
+
+ " check that repeating a search before and after a line fails
+ normal 3Gfv
+ call assert_beeps('normal ;')
+ call assert_beeps('normal ,')
+
+ " clear the character search
+ call setcharsearch({'char' : ''})
+ call assert_equal('', getcharsearch().char)
+
+ call assert_fails("call setcharsearch([])", 'E715:')
enew!
endfunc
@@ -60,3 +72,30 @@ func Test_search_cmds()
call assert_equal('ddd yee y', getline(6))
enew!
endfunc
+
+" Test for character search in virtual edit mode with <Tab>
+func Test_csearch_virtualedit()
+ new
+ set virtualedit=all
+ call setline(1, "a\tb")
+ normal! tb
+ call assert_equal([0, 1, 2, 6], getpos('.'))
+ set virtualedit&
+ close!
+endfunc
+
+" Test for character search failure in latin1 encoding
+func Test_charsearch_latin1()
+ new
+ let save_enc = &encoding
+ " set encoding=latin1
+ call setline(1, 'abcdefghijk')
+ call assert_beeps('normal fz')
+ call assert_beeps('normal tx')
+ call assert_beeps('normal $Fz')
+ call assert_beeps('normal $Tx')
+ let &encoding = save_enc
+ close!
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_charsearch_utf8.vim b/src/nvim/testdir/test_charsearch_utf8.vim
index 09341a90b0..82a807ac5b 100644
--- a/src/nvim/testdir/test_charsearch_utf8.vim
+++ b/src/nvim/testdir/test_charsearch_utf8.vim
@@ -1,7 +1,7 @@
" Tests for related f{char} and t{char} using utf-8.
" Test for t,f,F,T movement commands
-function! Test_search_cmds()
+func Test_search_cmds()
new!
call setline(1, "・最初から最後まで最強のVimは最高")
1
diff --git a/src/nvim/testdir/test_cindent.vim b/src/nvim/testdir/test_cindent.vim
index 562867f548..ccc8168c09 100644
--- a/src/nvim/testdir/test_cindent.vim
+++ b/src/nvim/testdir/test_cindent.vim
@@ -1,6 +1,5 @@
" Test for cinoptions and cindent
"
-" TODO: rewrite test3.in into this new style test
func Test_cino_hash()
" Test that curbuf->b_ind_hash_comment is correctly reset
@@ -128,15 +127,5249 @@ func Test_cindent_func()
bwipe!
endfunc
+func Test_cindent_1()
+ new
+ setl cindent ts=4 sw=4
+ setl cino& sts&
+
+ let code =<< trim [CODE]
+ /* start of AUTO matically checked vim: set ts=4 : */
+ {
+ if (test)
+ cmd1;
+ cmd2;
+ }
+
+ {
+ if (test)
+ cmd1;
+ else
+ cmd2;
+ }
+
+ {
+ if (test)
+ {
+ cmd1;
+ cmd2;
+ }
+ }
+
+ {
+ if (test)
+ {
+ cmd1;
+ else
+ }
+ }
+
+ {
+ while (this)
+ if (test)
+ cmd1;
+ cmd2;
+ }
+
+ {
+ while (this)
+ if (test)
+ cmd1;
+ else
+ cmd2;
+ }
+
+ {
+ if (test)
+ {
+ cmd;
+ }
+
+ if (test)
+ cmd;
+ }
+
+ {
+ if (test) {
+ cmd;
+ }
+
+ if (test) cmd;
+ }
+
+ {
+ cmd1;
+ for (blah)
+ while (this)
+ if (test)
+ cmd2;
+ cmd3;
+ }
+
+ {
+ cmd1;
+ for (blah)
+ while (this)
+ if (test)
+ cmd2;
+ cmd3;
+
+ if (test)
+ {
+ cmd1;
+ cmd2;
+ cmd3;
+ }
+ }
+
+
+ /* Test for 'cindent' do/while mixed with if/else: */
+
+ {
+ do
+ if (asdf)
+ asdfasd;
+ while (cond);
+
+ do
+ if (asdf)
+ while (asdf)
+ asdf;
+ while (asdf);
+ }
+
+ /* Test for 'cindent' with two ) on a continuation line */
+ {
+ if (asdfasdf;asldkfj asdlkfj as;ldkfj sal;d
+ aal;sdkjf ( ;asldfkja;sldfk
+ al;sdjfka ;slkdf ) sa;ldkjfsa dlk;)
+ line up here;
+ }
+
+
+ /* C++ tests: */
+
+ // foo() these three lines should remain in column 0
+ // {
+ // }
+
+ /* Test for continuation and unterminated lines: */
+ {
+ i = 99 + 14325 +
+ 21345 +
+ 21345 +
+ 21345 + ( 21345 +
+ 21345) +
+ 2345 +
+ 1234;
+ c = 1;
+ }
+
+ /*
+ testje for indent with empty line
+
+ here */
+
+ {
+ if (testing &&
+ not a joke ||
+ line up here)
+ hay;
+ if (testing &&
+ (not a joke || testing
+ )line up here)
+ hay;
+ if (testing &&
+ (not a joke || testing
+ line up here))
+ hay;
+ }
+
+
+ {
+ switch (c)
+ {
+ case xx:
+ do
+ if (asdf)
+ do
+ asdfasdf;
+ while (asdf);
+ else
+ asdfasdf;
+ while (cond);
+ case yy:
+ case xx:
+ case zz:
+ testing;
+ }
+ }
+
+ {
+ if (cond) {
+ foo;
+ }
+ else
+ {
+ bar;
+ }
+ }
+
+ {
+ if (alskdfj ;alsdkfjal;skdjf (;sadlkfsa ;dlkf j;alksdfj ;alskdjf
+ alsdkfj (asldk;fj
+ awith cino=(0 ;lf this one goes to below the paren with ==
+ ;laksjfd ;lsakdjf ;alskdf asd)
+ asdfasdf;)))
+ asdfasdf;
+ }
+
+ int
+ func(a, b)
+ int a;
+ int c;
+ {
+ if (c1 && (c2 ||
+ c3))
+ foo;
+ if (c1 &&
+ (c2 || c3)
+ )
+ }
+
+ {
+ while (asd)
+ {
+ if (asdf)
+ if (test)
+ if (that)
+ {
+ if (asdf)
+ do
+ cdasd;
+ while (as
+ df);
+ }
+ else
+ if (asdf)
+ asdf;
+ else
+ asdf;
+ asdf;
+ }
+ }
+
+ {
+ s = "/*"; b = ';'
+ s = "/*"; b = ';';
+ a = b;
+ }
+
+ {
+ switch (a)
+ {
+ case a:
+ switch (t)
+ {
+ case 1:
+ cmd;
+ break;
+ case 2:
+ cmd;
+ break;
+ }
+ cmd;
+ break;
+ case b:
+ {
+ int i;
+ cmd;
+ }
+ break;
+ case c: {
+ int i;
+ cmd;
+ }
+ case d: if (cond &&
+ test) { /* this line doesn't work right */
+ int i;
+ cmd;
+ }
+ break;
+ }
+ }
+
+ {
+ if (!(vim_strchr(p_cpo, CPO_BUFOPTGLOB) != NULL && entering) &&
+ (bp_to->b_p_initialized ||
+ (!entering && vim_strchr(p_cpo, CPO_BUFOPT) != NULL)))
+ return;
+ label :
+ asdf = asdf ?
+ asdf : asdf;
+ asdf = asdf ?
+ asdf: asdf;
+ }
+
+ /* Special Comments : This function has the added complexity (compared */
+ /* : to addtolist) of having to check for a detail */
+ /* : texture and add that to the list first. */
+
+ char *(array[100]) = {
+ "testje",
+ "foo",
+ "bar",
+ }
+
+ enum soppie
+ {
+ yes = 0,
+ no,
+ maybe
+ };
+
+ typedef enum soppie
+ {
+ yes = 0,
+ no,
+ maybe
+ };
+
+ static enum
+ {
+ yes = 0,
+ no,
+ maybe
+ } soppie;
+
+ public static enum
+ {
+ yes = 0,
+ no,
+ maybe
+ } soppie;
+
+ static private enum
+ {
+ yes = 0,
+ no,
+ maybe
+ } soppie;
+
+ {
+ int a,
+ b;
+ }
+
+ {
+ struct Type
+ {
+ int i;
+ char *str;
+ } var[] =
+ {
+ 0, "zero",
+ 1, "one",
+ 2, "two",
+ 3, "three"
+ };
+
+ float matrix[3][3] =
+ {
+ {
+ 0,
+ 1,
+ 2
+ },
+ {
+ 3,
+ 4,
+ 5
+ },
+ {
+ 6,
+ 7,
+ 8
+ }
+ };
+ }
+
+ {
+ /* blah ( blah */
+ /* where does this go? */
+
+ /* blah ( blah */
+ cmd;
+
+ func(arg1,
+ /* comment */
+ arg2);
+ a;
+ {
+ b;
+ {
+ c; /* Hey, NOW it indents?! */
+ }
+ }
+
+ {
+ func(arg1,
+ arg2,
+ arg3);
+ /* Hey, what am I doing here? Is this coz of the ","? */
+ }
+ }
+
+ main ()
+ {
+ if (cond)
+ {
+ a = b;
+ }
+ if (cond) {
+ a = c;
+ }
+ if (cond)
+ a = d;
+ return;
+ }
+
+ {
+ case 2: if (asdf &&
+ asdfasdf)
+ aasdf;
+ a = 9;
+ case 3: if (asdf)
+ aasdf;
+ a = 9;
+ case 4: x = 1;
+ y = 2;
+
+ label: if (asdf)
+ here;
+
+ label: if (asdf &&
+ asdfasdf)
+ {
+ }
+
+ label: if (asdf &&
+ asdfasdf) {
+ there;
+ }
+
+ label: if (asdf &&
+ asdfasdf)
+ there;
+ }
+
+ {
+ /*
+ hello with ":set comments= cino=c5"
+ */
+
+ /*
+ hello with ":set comments= cino="
+ */
+ }
+
+
+ {
+ if (a < b) {
+ a = a + 1;
+ } else
+ a = a + 2;
+
+ if (a)
+ do {
+ testing;
+ } while (asdfasdf);
+ a = b + 1;
+ asdfasdf
+ }
+
+ {
+ for ( int i = 0;
+ i < 10; i++ )
+ {
+ }
+ i = 0;
+ }
+
+ class bob
+ {
+ int foo() {return 1;}
+ int bar;
+ }
+
+ main()
+ {
+ while(1)
+ if (foo)
+ {
+ bar;
+ }
+ else {
+ asdf;
+ }
+ misplacedline;
+ }
+
+ {
+ if (clipboard.state == SELECT_DONE
+ && ((row == clipboard.start.lnum
+ && col >= clipboard.start.col)
+ || row > clipboard.start.lnum))
+ }
+
+ {
+ if (1) {i += 4;}
+ where_am_i;
+ return 0;
+ }
+
+ {
+ {
+ } // sdf(asdf
+ if (asdf)
+ asd;
+ }
+
+ {
+ label1:
+ label2:
+ }
+
+ {
+ int fooRet = foo(pBar1, false /*fKB*/,
+ true /*fPTB*/, 3 /*nT*/, false /*fDF*/);
+ f() {
+ for ( i = 0;
+ i < m;
+ /* c */ i++ ) {
+ a = b;
+ }
+ }
+ }
+
+ {
+ f1(/*comment*/);
+ f2();
+ }
+
+ {
+ do {
+ if (foo) {
+ } else
+ ;
+ } while (foo);
+ foo(); // was wrong
+ }
+
+ int x; // no extra indent because of the ;
+ void func()
+ {
+ }
+
+ char *tab[] = {"aaa",
+ "};", /* }; */ NULL}
+ int indented;
+ {}
+
+ char *a[] = {"aaa", "bbb",
+ "ccc", NULL};
+ // here
+
+ char *tab[] = {"aaa",
+ "xx", /* xx */}; /* asdf */
+ int not_indented;
+
+ {
+ do {
+ switch (bla)
+ {
+ case 1: if (foo)
+ bar;
+ }
+ } while (boo);
+ wrong;
+ }
+
+ int foo,
+ bar;
+ int foo;
+
+ #if defined(foo) \
+ && defined(bar)
+ char * xx = "asdf\
+ foo\
+ bor";
+ int x;
+
+ char *foo = "asdf\
+ asdf\
+ asdf",
+ *bar;
+
+ void f()
+ {
+ #if defined(foo) \
+ && defined(bar)
+ char *foo = "asdf\
+ asdf\
+ asdf",
+ *bar;
+ {
+ int i;
+ char *foo = "asdf\
+ asdf\
+ asdf",
+ *bar;
+ }
+ #endif
+ }
+ #endif
+
+ int y; // comment
+ // comment
+
+ // comment
+
+ {
+ Constructor(int a,
+ int b ) : BaseClass(a)
+ {
+ }
+ }
+
+ void foo()
+ {
+ char one,
+ two;
+ struct bla piet,
+ jan;
+ enum foo kees,
+ jannie;
+ static unsigned sdf,
+ krap;
+ unsigned int piet,
+ jan;
+ int
+ kees,
+ jan;
+ }
+
+ {
+ t(int f,
+ int d); // )
+ d();
+ }
+
+ Constructor::Constructor(int a,
+ int b
+ ) :
+ BaseClass(a,
+ b,
+ c),
+ mMember(b),
+ {
+ }
+
+ Constructor::Constructor(int a,
+ int b ) :
+ BaseClass(a)
+ {
+ }
+
+ Constructor::Constructor(int a,
+ int b ) /*x*/ : /*x*/ BaseClass(a),
+ member(b)
+ {
+ }
+
+ A::A(int a, int b)
+ : aa(a),
+ bb(b),
+ cc(c)
+ {
+ }
+
+ class CAbc :
+ public BaseClass1,
+ protected BaseClass2
+ {
+ int Test() { return FALSE; }
+ int Test1() { return TRUE; }
+
+ CAbc(int a, int b ) :
+ BaseClass(a)
+ {
+ switch(xxx)
+ {
+ case abc:
+ asdf();
+ break;
+
+ case 999:
+ baer();
+ break;
+ }
+ }
+
+ public: // <-- this was incorrectly indented before!!
+ void testfall();
+ protected:
+ void testfall();
+ };
+
+ class CAbc : public BaseClass1,
+ protected BaseClass2
+ {
+ };
+
+ static struct
+ {
+ int a;
+ int b;
+ } variable[COUNT] =
+ {
+ {
+ 123,
+ 456
+ },
+ {
+ 123,
+ 456
+ }
+ };
+
+ static struct
+ {
+ int a;
+ int b;
+ } variable[COUNT] =
+ {
+ { 123, 456 },
+ { 123, 456 }
+ };
+
+ void asdf() /* ind_maxparen may cause trouble here */
+ {
+ if ((0
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1)) break;
+ }
+
+ foo()
+ {
+ a = cond ? foo() : asdf
+ + asdf;
+
+ a = cond ?
+ foo() : asdf
+ + asdf;
+ }
+
+ int main(void)
+ {
+ if (a)
+ if (b)
+ 2;
+ else 3;
+ next_line_of_code();
+ }
+
+ barry()
+ {
+ Foo::Foo (int one,
+ int two)
+ : something(4)
+ {}
+ }
+
+ barry()
+ {
+ Foo::Foo (int one, int two)
+ : something(4)
+ {}
+ }
+
+ Constructor::Constructor(int a,
+ int b
+ ) :
+ BaseClass(a,
+ b,
+ c),
+ mMember(b)
+ {
+ }
+ int main ()
+ {
+ if (lala)
+ do
+ ++(*lolo);
+ while (lili
+ && lele);
+ lulu;
+ }
+
+ int main ()
+ {
+ switch (c)
+ {
+ case 'c': if (cond)
+ {
+ }
+ }
+ }
+
+ main()
+ {
+ (void) MyFancyFuasdfadsfnction(
+ argument);
+ }
+
+ main()
+ {
+ char foo[] = "/*";
+ /* as
+ df */
+ hello
+ }
+
+ /* valid namespaces with normal indent */
+ namespace
+ {
+ {
+ 111111111111;
+ }
+ }
+ namespace /* test */
+ {
+ 11111111111111111;
+ }
+ namespace // test
+ {
+ 111111111111111111;
+ }
+ namespace
+ {
+ 111111111111111111;
+ }
+ namespace test
+ {
+ 111111111111111111;
+ }
+ namespace{
+ 111111111111111111;
+ }
+ namespace test{
+ 111111111111111111;
+ }
+ namespace {
+ 111111111111111111;
+ }
+ namespace test {
+ 111111111111111111;
+ namespace test2 {
+ 22222222222222222;
+ }
+ }
+ inline namespace {
+ 111111111111111111;
+ }
+ inline /* test */ namespace {
+ 111111111111111111;
+ }
+ inline/* test */namespace {
+ 111111111111111111;
+ }
+
+ /* invalid namespaces use block indent */
+ namespace test test2 {
+ 111111111111111111111;
+ }
+ namespace11111111111 {
+ 111111111111;
+ }
+ namespace() {
+ 1111111111111;
+ }
+ namespace()
+ {
+ 111111111111111111;
+ }
+ namespace test test2
+ {
+ 1111111111111111111;
+ }
+ namespace111111111
+ {
+ 111111111111111111;
+ }
+ inlinenamespace {
+ 111111111111111111;
+ }
+
+ void getstring() {
+ /* Raw strings */
+ const char* s = R"(
+ test {
+ # comment
+ field: 123
+ }
+ )";
+ }
+
+ void getstring() {
+ const char* s = R"foo(
+ test {
+ # comment
+ field: 123
+ }
+ )foo";
+ }
+
+ {
+ int a[4] = {
+ [0] = 0,
+ [1] = 1,
+ [2] = 2,
+ [3] = 3,
+ };
+ }
+
+ {
+ a = b[2]
+ + 3;
+ }
+
+ {
+ if (1)
+ /* aaaaa
+ * bbbbb
+ */
+ a = 1;
+ }
+
+ void func()
+ {
+ switch (foo)
+ {
+ case (bar):
+ if (baz())
+ quux();
+ break;
+ case (shmoo):
+ if (!bar)
+ {
+ }
+ case (foo1):
+ switch (bar)
+ {
+ case baz:
+ baz_f();
+ break;
+ }
+ break;
+ default:
+ baz();
+ baz();
+ break;
+ }
+ }
+
+ /* end of AUTO */
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ call search('start of AUTO')
+ exe "normal =/end of AUTO\<CR>"
+
+ let expected =<< trim [CODE]
+ /* start of AUTO matically checked vim: set ts=4 : */
+ {
+ if (test)
+ cmd1;
+ cmd2;
+ }
+
+ {
+ if (test)
+ cmd1;
+ else
+ cmd2;
+ }
+
+ {
+ if (test)
+ {
+ cmd1;
+ cmd2;
+ }
+ }
+
+ {
+ if (test)
+ {
+ cmd1;
+ else
+ }
+ }
+
+ {
+ while (this)
+ if (test)
+ cmd1;
+ cmd2;
+ }
+
+ {
+ while (this)
+ if (test)
+ cmd1;
+ else
+ cmd2;
+ }
+
+ {
+ if (test)
+ {
+ cmd;
+ }
+
+ if (test)
+ cmd;
+ }
+
+ {
+ if (test) {
+ cmd;
+ }
+
+ if (test) cmd;
+ }
+
+ {
+ cmd1;
+ for (blah)
+ while (this)
+ if (test)
+ cmd2;
+ cmd3;
+ }
+
+ {
+ cmd1;
+ for (blah)
+ while (this)
+ if (test)
+ cmd2;
+ cmd3;
+
+ if (test)
+ {
+ cmd1;
+ cmd2;
+ cmd3;
+ }
+ }
+
+
+ /* Test for 'cindent' do/while mixed with if/else: */
+
+ {
+ do
+ if (asdf)
+ asdfasd;
+ while (cond);
+
+ do
+ if (asdf)
+ while (asdf)
+ asdf;
+ while (asdf);
+ }
+
+ /* Test for 'cindent' with two ) on a continuation line */
+ {
+ if (asdfasdf;asldkfj asdlkfj as;ldkfj sal;d
+ aal;sdkjf ( ;asldfkja;sldfk
+ al;sdjfka ;slkdf ) sa;ldkjfsa dlk;)
+ line up here;
+ }
+
+
+ /* C++ tests: */
+
+ // foo() these three lines should remain in column 0
+ // {
+ // }
+
+ /* Test for continuation and unterminated lines: */
+ {
+ i = 99 + 14325 +
+ 21345 +
+ 21345 +
+ 21345 + ( 21345 +
+ 21345) +
+ 2345 +
+ 1234;
+ c = 1;
+ }
+
+ /*
+ testje for indent with empty line
+
+ here */
+
+ {
+ if (testing &&
+ not a joke ||
+ line up here)
+ hay;
+ if (testing &&
+ (not a joke || testing
+ )line up here)
+ hay;
+ if (testing &&
+ (not a joke || testing
+ line up here))
+ hay;
+ }
+
+
+ {
+ switch (c)
+ {
+ case xx:
+ do
+ if (asdf)
+ do
+ asdfasdf;
+ while (asdf);
+ else
+ asdfasdf;
+ while (cond);
+ case yy:
+ case xx:
+ case zz:
+ testing;
+ }
+ }
+
+ {
+ if (cond) {
+ foo;
+ }
+ else
+ {
+ bar;
+ }
+ }
+
+ {
+ if (alskdfj ;alsdkfjal;skdjf (;sadlkfsa ;dlkf j;alksdfj ;alskdjf
+ alsdkfj (asldk;fj
+ awith cino=(0 ;lf this one goes to below the paren with ==
+ ;laksjfd ;lsakdjf ;alskdf asd)
+ asdfasdf;)))
+ asdfasdf;
+ }
+
+ int
+ func(a, b)
+ int a;
+ int c;
+ {
+ if (c1 && (c2 ||
+ c3))
+ foo;
+ if (c1 &&
+ (c2 || c3)
+ )
+ }
+
+ {
+ while (asd)
+ {
+ if (asdf)
+ if (test)
+ if (that)
+ {
+ if (asdf)
+ do
+ cdasd;
+ while (as
+ df);
+ }
+ else
+ if (asdf)
+ asdf;
+ else
+ asdf;
+ asdf;
+ }
+ }
+
+ {
+ s = "/*"; b = ';'
+ s = "/*"; b = ';';
+ a = b;
+ }
+
+ {
+ switch (a)
+ {
+ case a:
+ switch (t)
+ {
+ case 1:
+ cmd;
+ break;
+ case 2:
+ cmd;
+ break;
+ }
+ cmd;
+ break;
+ case b:
+ {
+ int i;
+ cmd;
+ }
+ break;
+ case c: {
+ int i;
+ cmd;
+ }
+ case d: if (cond &&
+ test) { /* this line doesn't work right */
+ int i;
+ cmd;
+ }
+ break;
+ }
+ }
+
+ {
+ if (!(vim_strchr(p_cpo, CPO_BUFOPTGLOB) != NULL && entering) &&
+ (bp_to->b_p_initialized ||
+ (!entering && vim_strchr(p_cpo, CPO_BUFOPT) != NULL)))
+ return;
+ label :
+ asdf = asdf ?
+ asdf : asdf;
+ asdf = asdf ?
+ asdf: asdf;
+ }
+
+ /* Special Comments : This function has the added complexity (compared */
+ /* : to addtolist) of having to check for a detail */
+ /* : texture and add that to the list first. */
+
+ char *(array[100]) = {
+ "testje",
+ "foo",
+ "bar",
+ }
+
+ enum soppie
+ {
+ yes = 0,
+ no,
+ maybe
+ };
+
+ typedef enum soppie
+ {
+ yes = 0,
+ no,
+ maybe
+ };
+
+ static enum
+ {
+ yes = 0,
+ no,
+ maybe
+ } soppie;
+
+ public static enum
+ {
+ yes = 0,
+ no,
+ maybe
+ } soppie;
+
+ static private enum
+ {
+ yes = 0,
+ no,
+ maybe
+ } soppie;
+
+ {
+ int a,
+ b;
+ }
+
+ {
+ struct Type
+ {
+ int i;
+ char *str;
+ } var[] =
+ {
+ 0, "zero",
+ 1, "one",
+ 2, "two",
+ 3, "three"
+ };
+
+ float matrix[3][3] =
+ {
+ {
+ 0,
+ 1,
+ 2
+ },
+ {
+ 3,
+ 4,
+ 5
+ },
+ {
+ 6,
+ 7,
+ 8
+ }
+ };
+ }
+
+ {
+ /* blah ( blah */
+ /* where does this go? */
+
+ /* blah ( blah */
+ cmd;
+
+ func(arg1,
+ /* comment */
+ arg2);
+ a;
+ {
+ b;
+ {
+ c; /* Hey, NOW it indents?! */
+ }
+ }
+
+ {
+ func(arg1,
+ arg2,
+ arg3);
+ /* Hey, what am I doing here? Is this coz of the ","? */
+ }
+ }
+
+ main ()
+ {
+ if (cond)
+ {
+ a = b;
+ }
+ if (cond) {
+ a = c;
+ }
+ if (cond)
+ a = d;
+ return;
+ }
+
+ {
+ case 2: if (asdf &&
+ asdfasdf)
+ aasdf;
+ a = 9;
+ case 3: if (asdf)
+ aasdf;
+ a = 9;
+ case 4: x = 1;
+ y = 2;
+
+ label: if (asdf)
+ here;
+
+ label: if (asdf &&
+ asdfasdf)
+ {
+ }
+
+ label: if (asdf &&
+ asdfasdf) {
+ there;
+ }
+
+ label: if (asdf &&
+ asdfasdf)
+ there;
+ }
+
+ {
+ /*
+ hello with ":set comments= cino=c5"
+ */
+
+ /*
+ hello with ":set comments= cino="
+ */
+ }
+
+
+ {
+ if (a < b) {
+ a = a + 1;
+ } else
+ a = a + 2;
+
+ if (a)
+ do {
+ testing;
+ } while (asdfasdf);
+ a = b + 1;
+ asdfasdf
+ }
+
+ {
+ for ( int i = 0;
+ i < 10; i++ )
+ {
+ }
+ i = 0;
+ }
+
+ class bob
+ {
+ int foo() {return 1;}
+ int bar;
+ }
+
+ main()
+ {
+ while(1)
+ if (foo)
+ {
+ bar;
+ }
+ else {
+ asdf;
+ }
+ misplacedline;
+ }
+
+ {
+ if (clipboard.state == SELECT_DONE
+ && ((row == clipboard.start.lnum
+ && col >= clipboard.start.col)
+ || row > clipboard.start.lnum))
+ }
+
+ {
+ if (1) {i += 4;}
+ where_am_i;
+ return 0;
+ }
+
+ {
+ {
+ } // sdf(asdf
+ if (asdf)
+ asd;
+ }
+
+ {
+ label1:
+ label2:
+ }
+
+ {
+ int fooRet = foo(pBar1, false /*fKB*/,
+ true /*fPTB*/, 3 /*nT*/, false /*fDF*/);
+ f() {
+ for ( i = 0;
+ i < m;
+ /* c */ i++ ) {
+ a = b;
+ }
+ }
+ }
+
+ {
+ f1(/*comment*/);
+ f2();
+ }
+
+ {
+ do {
+ if (foo) {
+ } else
+ ;
+ } while (foo);
+ foo(); // was wrong
+ }
+
+ int x; // no extra indent because of the ;
+ void func()
+ {
+ }
+
+ char *tab[] = {"aaa",
+ "};", /* }; */ NULL}
+ int indented;
+ {}
+
+ char *a[] = {"aaa", "bbb",
+ "ccc", NULL};
+ // here
+
+ char *tab[] = {"aaa",
+ "xx", /* xx */}; /* asdf */
+ int not_indented;
+
+ {
+ do {
+ switch (bla)
+ {
+ case 1: if (foo)
+ bar;
+ }
+ } while (boo);
+ wrong;
+ }
+
+ int foo,
+ bar;
+ int foo;
+
+ #if defined(foo) \
+ && defined(bar)
+ char * xx = "asdf\
+ foo\
+ bor";
+ int x;
+
+ char *foo = "asdf\
+ asdf\
+ asdf",
+ *bar;
+
+ void f()
+ {
+ #if defined(foo) \
+ && defined(bar)
+ char *foo = "asdf\
+ asdf\
+ asdf",
+ *bar;
+ {
+ int i;
+ char *foo = "asdf\
+ asdf\
+ asdf",
+ *bar;
+ }
+ #endif
+ }
+ #endif
+
+ int y; // comment
+ // comment
+
+ // comment
+
+ {
+ Constructor(int a,
+ int b ) : BaseClass(a)
+ {
+ }
+ }
+
+ void foo()
+ {
+ char one,
+ two;
+ struct bla piet,
+ jan;
+ enum foo kees,
+ jannie;
+ static unsigned sdf,
+ krap;
+ unsigned int piet,
+ jan;
+ int
+ kees,
+ jan;
+ }
+
+ {
+ t(int f,
+ int d); // )
+ d();
+ }
+
+ Constructor::Constructor(int a,
+ int b
+ ) :
+ BaseClass(a,
+ b,
+ c),
+ mMember(b),
+ {
+ }
+
+ Constructor::Constructor(int a,
+ int b ) :
+ BaseClass(a)
+ {
+ }
+
+ Constructor::Constructor(int a,
+ int b ) /*x*/ : /*x*/ BaseClass(a),
+ member(b)
+ {
+ }
+
+ A::A(int a, int b)
+ : aa(a),
+ bb(b),
+ cc(c)
+ {
+ }
+
+ class CAbc :
+ public BaseClass1,
+ protected BaseClass2
+ {
+ int Test() { return FALSE; }
+ int Test1() { return TRUE; }
+
+ CAbc(int a, int b ) :
+ BaseClass(a)
+ {
+ switch(xxx)
+ {
+ case abc:
+ asdf();
+ break;
+
+ case 999:
+ baer();
+ break;
+ }
+ }
+
+ public: // <-- this was incorrectly indented before!!
+ void testfall();
+ protected:
+ void testfall();
+ };
+
+ class CAbc : public BaseClass1,
+ protected BaseClass2
+ {
+ };
+
+ static struct
+ {
+ int a;
+ int b;
+ } variable[COUNT] =
+ {
+ {
+ 123,
+ 456
+ },
+ {
+ 123,
+ 456
+ }
+ };
+
+ static struct
+ {
+ int a;
+ int b;
+ } variable[COUNT] =
+ {
+ { 123, 456 },
+ { 123, 456 }
+ };
+
+ void asdf() /* ind_maxparen may cause trouble here */
+ {
+ if ((0
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1
+ && 1)) break;
+ }
+
+ foo()
+ {
+ a = cond ? foo() : asdf
+ + asdf;
+
+ a = cond ?
+ foo() : asdf
+ + asdf;
+ }
+
+ int main(void)
+ {
+ if (a)
+ if (b)
+ 2;
+ else 3;
+ next_line_of_code();
+ }
+
+ barry()
+ {
+ Foo::Foo (int one,
+ int two)
+ : something(4)
+ {}
+ }
+
+ barry()
+ {
+ Foo::Foo (int one, int two)
+ : something(4)
+ {}
+ }
+
+ Constructor::Constructor(int a,
+ int b
+ ) :
+ BaseClass(a,
+ b,
+ c),
+ mMember(b)
+ {
+ }
+ int main ()
+ {
+ if (lala)
+ do
+ ++(*lolo);
+ while (lili
+ && lele);
+ lulu;
+ }
+
+ int main ()
+ {
+ switch (c)
+ {
+ case 'c': if (cond)
+ {
+ }
+ }
+ }
+
+ main()
+ {
+ (void) MyFancyFuasdfadsfnction(
+ argument);
+ }
+
+ main()
+ {
+ char foo[] = "/*";
+ /* as
+ df */
+ hello
+ }
+
+ /* valid namespaces with normal indent */
+ namespace
+ {
+ {
+ 111111111111;
+ }
+ }
+ namespace /* test */
+ {
+ 11111111111111111;
+ }
+ namespace // test
+ {
+ 111111111111111111;
+ }
+ namespace
+ {
+ 111111111111111111;
+ }
+ namespace test
+ {
+ 111111111111111111;
+ }
+ namespace{
+ 111111111111111111;
+ }
+ namespace test{
+ 111111111111111111;
+ }
+ namespace {
+ 111111111111111111;
+ }
+ namespace test {
+ 111111111111111111;
+ namespace test2 {
+ 22222222222222222;
+ }
+ }
+ inline namespace {
+ 111111111111111111;
+ }
+ inline /* test */ namespace {
+ 111111111111111111;
+ }
+ inline/* test */namespace {
+ 111111111111111111;
+ }
+
+ /* invalid namespaces use block indent */
+ namespace test test2 {
+ 111111111111111111111;
+ }
+ namespace11111111111 {
+ 111111111111;
+ }
+ namespace() {
+ 1111111111111;
+ }
+ namespace()
+ {
+ 111111111111111111;
+ }
+ namespace test test2
+ {
+ 1111111111111111111;
+ }
+ namespace111111111
+ {
+ 111111111111111111;
+ }
+ inlinenamespace {
+ 111111111111111111;
+ }
+
+ void getstring() {
+ /* Raw strings */
+ const char* s = R"(
+ test {
+ # comment
+ field: 123
+ }
+ )";
+ }
+
+ void getstring() {
+ const char* s = R"foo(
+ test {
+ # comment
+ field: 123
+ }
+ )foo";
+ }
+
+ {
+ int a[4] = {
+ [0] = 0,
+ [1] = 1,
+ [2] = 2,
+ [3] = 3,
+ };
+ }
+
+ {
+ a = b[2]
+ + 3;
+ }
+
+ {
+ if (1)
+ /* aaaaa
+ * bbbbb
+ */
+ a = 1;
+ }
+
+ void func()
+ {
+ switch (foo)
+ {
+ case (bar):
+ if (baz())
+ quux();
+ break;
+ case (shmoo):
+ if (!bar)
+ {
+ }
+ case (foo1):
+ switch (bar)
+ {
+ case baz:
+ baz_f();
+ break;
+ }
+ break;
+ default:
+ baz();
+ baz();
+ break;
+ }
+ }
+
+ /* end of AUTO */
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_2()
+ new
+ setl cindent ts=4 sw=4
+ setl tw=0 noai fo=croq
+ let &wm = &columns - 20
+
+ let code =<< trim [CODE]
+ {
+
+ /* this is
+ * a real serious important big
+ * comment
+ */
+ /* insert " about life, the universe, and the rest" after "serious" */
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ call search('serious', 'e')
+ normal a about life, the universe, and the rest
+
+ let expected =<< trim [CODE]
+ {
+
+ /* this is
+ * a real serious
+ * about life, the
+ * universe, and the
+ * rest important big
+ * comment
+ */
+ /* insert " about life, the universe, and the rest" after "serious" */
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ set wm&
+ enew! | close
+endfunc
+
+func Test_cindent_3()
+ new
+ setl nocindent ts=4 sw=4
+
+ let code =<< trim [CODE]
+ {
+ /*
+ * Testing for comments, without 'cin' set
+ */
+
+ /*
+ * what happens here?
+ */
+
+ /*
+ the end of the comment, try inserting a line below */
+
+ /* how about
+ this one */
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ call search('comments')
+ normal joabout life
+ call search('happens')
+ normal jothere
+ call search('below')
+ normal oline
+ call search('this')
+ normal Ohello
+
+ let expected =<< trim [CODE]
+ {
+ /*
+ * Testing for comments, without 'cin' set
+ */
+ about life
+
+ /*
+ * what happens here?
+ */
+ there
+
+ /*
+ the end of the comment, try inserting a line below */
+ line
+
+ /* how about
+ hello
+ this one */
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_4()
+ new
+ setl cindent ts=4 sw=4
+
+ let code =<< trim [CODE]
+ {
+ var = this + that + vec[0] * vec[0]
+ + vec[1] * vec[1]
+ + vec2[2] * vec[2];
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ call search('vec2')
+ normal ==
+
+ let expected =<< trim [CODE]
+ {
+ var = this + that + vec[0] * vec[0]
+ + vec[1] * vec[1]
+ + vec2[2] * vec[2];
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_5()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=}4
+
+ let code =<< trim [CODE]
+ {
+ asdf asdflkajds f;
+ if (tes & ting) {
+ asdf asdf asdf ;
+ asdfa sdf asdf;
+ }
+ testing1;
+ if (tes & ting)
+ {
+ asdf asdf asdf ;
+ asdfa sdf asdf;
+ }
+ testing2;
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ call search('testing1')
+ exe "normal k2==/testing2\<CR>"
+ normal k2==
+
+ let expected =<< trim [CODE]
+ {
+ asdf asdflkajds f;
+ if (tes & ting) {
+ asdf asdf asdf ;
+ asdfa sdf asdf;
+ }
+ testing1;
+ if (tes & ting)
+ {
+ asdf asdf asdf ;
+ asdfa sdf asdf;
+ }
+ testing2;
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_6()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=(0,)20
+
+ let code =<< trim [CODE]
+ main ( int first_par, /*
+ * Comment for
+ * first par
+ */
+ int second_par /*
+ * Comment for
+ * second par
+ */
+ )
+ {
+ func( first_par, /*
+ * Comment for
+ * first par
+ */
+ second_par /*
+ * Comment for
+ * second par
+ */
+ );
+
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ call search('main')
+ normal =][
+
+ let expected =<< trim [CODE]
+ main ( int first_par, /*
+ * Comment for
+ * first par
+ */
+ int second_par /*
+ * Comment for
+ * second par
+ */
+ )
+ {
+ func( first_par, /*
+ * Comment for
+ * first par
+ */
+ second_par /*
+ * Comment for
+ * second par
+ */
+ );
+
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_7()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=es,n0s
+
+ let code =<< trim [CODE]
+ main(void)
+ {
+ /* Make sure that cino=X0s is not parsed like cino=Xs. */
+ if (cond)
+ foo();
+ else
+ {
+ bar();
+ }
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ call search('main')
+ normal =][
+
+ let expected =<< trim [CODE]
+ main(void)
+ {
+ /* Make sure that cino=X0s is not parsed like cino=Xs. */
+ if (cond)
+ foo();
+ else
+ {
+ bar();
+ }
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_8()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=
+
+ let code =<< trim [CODE]
+
+ {
+ do
+ {
+ if ()
+ {
+ if ()
+ asdf;
+ else
+ asdf;
+ }
+ } while ();
+ cmd; /* this should go under the } */
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ normal ]]=][
+
+ let expected =<< trim [CODE]
+
+ {
+ do
+ {
+ if ()
+ {
+ if ()
+ asdf;
+ else
+ asdf;
+ }
+ } while ();
+ cmd; /* this should go under the } */
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_9()
+ new
+ setl cindent ts=4 sw=4
+
+ let code =<< trim [CODE]
+
+ void f()
+ {
+ if ( k() ) {
+ l();
+
+ } else { /* Start (two words) end */
+ m();
+ }
+
+ n();
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ normal ]]=][
+
+ let expected =<< trim [CODE]
+
+ void f()
+ {
+ if ( k() ) {
+ l();
+
+ } else { /* Start (two words) end */
+ m();
+ }
+
+ n();
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_10()
+ new
+ setl cindent ts=4 sw=4
+ setl cino={s,e-s
+
+ let code =<< trim [CODE]
+
+ void f()
+ {
+ if ( k() )
+ {
+ l();
+ } else { /* Start (two words) end */
+ m();
+ }
+ n(); /* should be under the if () */
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ normal ]]=][
+
+ let expected =<< trim [CODE]
+
+ void f()
+ {
+ if ( k() )
+ {
+ l();
+ } else { /* Start (two words) end */
+ m();
+ }
+ n(); /* should be under the if () */
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_11()
+ new
+ setl cindent ts=4 sw=4
+ setl cino={s,fs
+
+ let code =<< trim [CODE]
+ void bar(void)
+ {
+ static array[2][2] =
+ {
+ { 1, 2 },
+ { 3, 4 },
+ }
+
+ while (a)
+ {
+ foo(&a);
+ }
+
+ {
+ int a;
+ {
+ a = a + 1;
+ }
+ }
+ b = a;
+ }
+
+ void func(void)
+ {
+ a = 1;
+ {
+ b = 2;
+ }
+ c = 3;
+ d = 4;
+ }
+ /* foo */
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ exe "normal ]]=/ foo\<CR>"
+
+ let expected =<< trim [CODE]
+ void bar(void)
+ {
+ static array[2][2] =
+ {
+ { 1, 2 },
+ { 3, 4 },
+ }
+
+ while (a)
+ {
+ foo(&a);
+ }
+
+ {
+ int a;
+ {
+ a = a + 1;
+ }
+ }
+ b = a;
+ }
+
+ void func(void)
+ {
+ a = 1;
+ {
+ b = 2;
+ }
+ c = 3;
+ d = 4;
+ }
+ /* foo */
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_12()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=
+
+ let code =<< trim [CODE]
+ a()
+ {
+ do {
+ a = a +
+ a;
+ } while ( a ); /* add text under this line */
+ if ( a )
+ a;
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ call search('while')
+ normal ohere
+
+ let expected =<< trim [CODE]
+ a()
+ {
+ do {
+ a = a +
+ a;
+ } while ( a ); /* add text under this line */
+ here
+ if ( a )
+ a;
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_13()
+ new
+ setl cindent ts=4 sw=4
+ setl cino= com=
+
+ let code =<< trim [CODE]
+ a()
+ {
+ label1:
+ /* hmm */
+ // comment
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ call search('comment')
+ exe "normal olabel2: b();\rlabel3 /* post */:\r/* pre */ label4:\r" .
+ \ "f(/*com*/);\rif (/*com*/)\rcmd();"
+
+ let expected =<< trim [CODE]
+ a()
+ {
+ label1:
+ /* hmm */
+ // comment
+ label2: b();
+ label3 /* post */:
+ /* pre */ label4:
+ f(/*com*/);
+ if (/*com*/)
+ cmd();
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_14()
+ new
+ setl cindent ts=4 sw=4
+ setl comments& comments^=s:/*,m:**,ex:*/
+
+ let code =<< trim [CODE]
+ /*
+ * A simple comment
+ */
+
+ /*
+ ** A different comment
+ */
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ call search('simple')
+ normal =5j
+
+ let expected =<< trim [CODE]
+ /*
+ * A simple comment
+ */
+
+ /*
+ ** A different comment
+ */
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_15()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=c0
+ setl comments& comments-=s1:/* comments^=s0:/*
+
+ let code =<< trim [CODE]
+ void f()
+ {
+
+ /*********
+ A comment.
+ *********/
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ normal ]]=][
+
+ let expected =<< trim [CODE]
+ void f()
+ {
+
+ /*********
+ A comment.
+ *********/
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_16()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=c0,C1
+ setl comments& comments-=s1:/* comments^=s0:/*
+
+ let code =<< trim [CODE]
+ void f()
+ {
+
+ /*********
+ A comment.
+ *********/
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ normal ]]=][
+
+ let expected =<< trim [CODE]
+ void f()
+ {
+
+ /*********
+ A comment.
+ *********/
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_17()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=
+
+ let code =<< trim [CODE]
+ void f()
+ {
+ c = c1 &&
+ (
+ c2 ||
+ c3
+ ) && c4;
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ normal ]]=][
+
+ let expected =<< trim [CODE]
+ void f()
+ {
+ c = c1 &&
+ (
+ c2 ||
+ c3
+ ) && c4;
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_18()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=(s
+
+ let code =<< trim [CODE]
+ void f()
+ {
+ c = c1 &&
+ (
+ c2 ||
+ c3
+ ) && c4;
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ normal ]]=][
+
+ let expected =<< trim [CODE]
+ void f()
+ {
+ c = c1 &&
+ (
+ c2 ||
+ c3
+ ) && c4;
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_19()
+ new
+ setl cindent ts=4 sw=4
+ set cino=(s,U1
+
+ let code =<< trim [CODE]
+ void f()
+ {
+ c = c1 &&
+ (
+ c2 ||
+ c3
+ ) && c4;
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ normal ]]=][
+
+ let expected =<< trim [CODE]
+ void f()
+ {
+ c = c1 &&
+ (
+ c2 ||
+ c3
+ ) && c4;
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_20()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=(0
+
+ let code =<< trim [CODE]
+ void f()
+ {
+ if ( c1
+ && ( c2
+ || c3))
+ foo;
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ normal ]]=][
+
+ let expected =<< trim [CODE]
+ void f()
+ {
+ if ( c1
+ && ( c2
+ || c3))
+ foo;
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_21()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=(0,w1
+
+ let code =<< trim [CODE]
+ void f()
+ {
+ if ( c1
+ && ( c2
+ || c3))
+ foo;
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ normal ]]=][
+
+ let expected =<< trim [CODE]
+ void f()
+ {
+ if ( c1
+ && ( c2
+ || c3))
+ foo;
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_22()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=(s
+
+ let code =<< trim [CODE]
+ void f()
+ {
+ c = c1 && (
+ c2 ||
+ c3
+ ) && c4;
+ if (
+ c1 && c2
+ )
+ foo;
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ normal ]]=][
+
+ let expected =<< trim [CODE]
+ void f()
+ {
+ c = c1 && (
+ c2 ||
+ c3
+ ) && c4;
+ if (
+ c1 && c2
+ )
+ foo;
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_23()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=(s,m1
+
+ let code =<< trim [CODE]
+ void f()
+ {
+ c = c1 && (
+ c2 ||
+ c3
+ ) && c4;
+ if (
+ c1 && c2
+ )
+ foo;
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ normal ]]=][
+
+ let expected =<< trim [CODE]
+ void f()
+ {
+ c = c1 && (
+ c2 ||
+ c3
+ ) && c4;
+ if (
+ c1 && c2
+ )
+ foo;
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_24()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=b1
+
+ let code =<< trim [CODE]
+ void f()
+ {
+ switch (x)
+ {
+ case 1:
+ a = b;
+ break;
+ default:
+ a = 0;
+ break;
+ }
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ normal ]]=][
+
+ let expected =<< trim [CODE]
+ void f()
+ {
+ switch (x)
+ {
+ case 1:
+ a = b;
+ break;
+ default:
+ a = 0;
+ break;
+ }
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_25()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=(0,W5
+
+ let code =<< trim [CODE]
+ void f()
+ {
+ invokeme(
+ argu,
+ ment);
+ invokeme(
+ argu,
+ ment
+ );
+ invokeme(argu,
+ ment
+ );
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ normal ]]=][
+
+ let expected =<< trim [CODE]
+ void f()
+ {
+ invokeme(
+ argu,
+ ment);
+ invokeme(
+ argu,
+ ment
+ );
+ invokeme(argu,
+ ment
+ );
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_26()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=/6
+
+ let code =<< trim [CODE]
+ void f()
+ {
+ statement;
+ // comment 1
+ // comment 2
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ normal ]]=][
+
+ let expected =<< trim [CODE]
+ void f()
+ {
+ statement;
+ // comment 1
+ // comment 2
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_27()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=
+
+ let code =<< trim [CODE]
+ void f()
+ {
+ statement;
+ // comment 1
+ // comment 2
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ exe "normal ]]/comment 1/+1\<CR>=="
+
+ let expected =<< trim [CODE]
+ void f()
+ {
+ statement;
+ // comment 1
+ // comment 2
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_28()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=g0
+
+ let code =<< trim [CODE]
+ class CAbc
+ {
+ int Test() { return FALSE; }
+
+ public: // comment
+ void testfall();
+ protected:
+ void testfall();
+ };
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ normal ]]=][
+
+ let expected =<< trim [CODE]
+ class CAbc
+ {
+ int Test() { return FALSE; }
+
+ public: // comment
+ void testfall();
+ protected:
+ void testfall();
+ };
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_29()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=(0,gs,hs
+
+ let code =<< trim [CODE]
+ class Foo : public Bar
+ {
+ public:
+ virtual void method1(void) = 0;
+ virtual void method2(int arg1,
+ int arg2,
+ int arg3) = 0;
+ };
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ normal ]]=][
+
+ let expected =<< trim [CODE]
+ class Foo : public Bar
+ {
+ public:
+ virtual void method1(void) = 0;
+ virtual void method2(int arg1,
+ int arg2,
+ int arg3) = 0;
+ };
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_30()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=+20
+
+ let code =<< [CODE]
+ void
+foo()
+{
+ if (a)
+ {
+ } else
+ asdf;
+}
+[CODE]
+
+ call append(0, code)
+ normal gg
+ normal ]]=][
+
+ let expected =<< [CODE]
+ void
+foo()
+{
+ if (a)
+ {
+ } else
+ asdf;
+}
+
+[CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_31()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=(0,W2s
+
+ let code =<< trim [CODE]
+
+ {
+ averylongfunctionnamelongfunctionnameaverylongfunctionname()->asd(
+ asdasdf,
+ func(asdf,
+ asdfadsf),
+ asdfasdf
+ );
+
+ /* those are ugly, but consequent */
+
+ func()->asd(asdasdf,
+ averylongfunctionname(
+ abc,
+ dec)->averylongfunctionname(
+ asdfadsf,
+ asdfasdf,
+ asdfasdf,
+ ),
+ func(asdfadf,
+ asdfasdf
+ ),
+ asdasdf
+ );
+
+ averylongfunctionnameaverylongfunctionnameavery()->asd(fasdf(
+ abc,
+ dec)->asdfasdfasdf(
+ asdfadsf,
+ asdfasdf,
+ asdfasdf,
+ ),
+ func(asdfadf,
+ asdfasdf),
+ asdasdf
+ );
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ normal ]]=][
+
+ let expected =<< trim [CODE]
+
+ {
+ averylongfunctionnamelongfunctionnameaverylongfunctionname()->asd(
+ asdasdf,
+ func(asdf,
+ asdfadsf),
+ asdfasdf
+ );
+
+ /* those are ugly, but consequent */
+
+ func()->asd(asdasdf,
+ averylongfunctionname(
+ abc,
+ dec)->averylongfunctionname(
+ asdfadsf,
+ asdfasdf,
+ asdfasdf,
+ ),
+ func(asdfadf,
+ asdfasdf
+ ),
+ asdasdf
+ );
+
+ averylongfunctionnameaverylongfunctionnameavery()->asd(fasdf(
+ abc,
+ dec)->asdfasdfasdf(
+ asdfadsf,
+ asdfasdf,
+ asdfasdf,
+ ),
+ func(asdfadf,
+ asdfasdf),
+ asdasdf
+ );
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_32()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=M1
+
+ let code =<< trim [CODE]
+ int main ()
+ {
+ if (cond1 &&
+ cond2
+ )
+ foo;
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ normal ]]=][
+
+ let expected =<< trim [CODE]
+ int main ()
+ {
+ if (cond1 &&
+ cond2
+ )
+ foo;
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_33()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=(0,ts
+
+ let code =<< trim [CODE]
+ void func(int a
+ #if defined(FOO)
+ , int b
+ , int c
+ #endif
+ )
+ {
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ normal 2j=][
+
+ let expected =<< trim [CODE]
+ void func(int a
+ #if defined(FOO)
+ , int b
+ , int c
+ #endif
+ )
+ {
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_34()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=(0
+
+ let code =<< trim [CODE]
+
+ void
+ func(int a
+ #if defined(FOO)
+ , int b
+ , int c
+ #endif
+ )
+ {
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ normal =][
+
+ let expected =<< trim [CODE]
+
+ void
+ func(int a
+ #if defined(FOO)
+ , int b
+ , int c
+ #endif
+ )
+ {
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_35()
+ new
+ setl cindent ts=4 sw=4
+ setl cino&
+
+ let code =<< trim [CODE]
+ void func(void)
+ {
+ if(x==y)
+ if(y==z)
+ foo=1;
+ else { bar=1;
+ baz=2;
+ }
+ printf("Foo!\n");
+ }
+
+ void func1(void)
+ {
+ char* tab[] = {"foo", "bar",
+ "baz", "quux",
+ "this line used", "to be indented incorrectly"};
+ foo();
+ }
+
+ void func2(void)
+ {
+ int tab[] =
+ {1, 2,
+ 3, 4,
+ 5, 6};
+
+ printf("This line used to be indented incorrectly.\n");
+ }
+
+ int foo[]
+ #ifdef BAR
+
+ = { 1, 2, 3,
+ 4, 5, 6 }
+
+ #endif
+ ;
+ int baz;
+
+ void func3(void)
+ {
+ int tab[] = {
+ 1, 2,
+ 3, 4,
+ 5, 6};
+
+ printf("Don't you dare indent this line incorrectly!\n");
+ }
+
+ void
+ func4(a, b,
+ c)
+ int a;
+ int b;
+ int c;
+ {
+ }
+
+ void
+ func5(
+ int a,
+ int b)
+ {
+ }
+
+ void
+ func6(
+ int a)
+ {
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ normal ]]=7][
+
+ let expected =<< trim [CODE]
+ void func(void)
+ {
+ if(x==y)
+ if(y==z)
+ foo=1;
+ else { bar=1;
+ baz=2;
+ }
+ printf("Foo!\n");
+ }
+
+ void func1(void)
+ {
+ char* tab[] = {"foo", "bar",
+ "baz", "quux",
+ "this line used", "to be indented incorrectly"};
+ foo();
+ }
+
+ void func2(void)
+ {
+ int tab[] =
+ {1, 2,
+ 3, 4,
+ 5, 6};
+
+ printf("This line used to be indented incorrectly.\n");
+ }
+
+ int foo[]
+ #ifdef BAR
+
+ = { 1, 2, 3,
+ 4, 5, 6 }
+
+ #endif
+ ;
+ int baz;
+
+ void func3(void)
+ {
+ int tab[] = {
+ 1, 2,
+ 3, 4,
+ 5, 6};
+
+ printf("Don't you dare indent this line incorrectly!\n");
+ }
+
+ void
+ func4(a, b,
+ c)
+ int a;
+ int b;
+ int c;
+ {
+ }
+
+ void
+ func5(
+ int a,
+ int b)
+ {
+ }
+
+ void
+ func6(
+ int a)
+ {
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_36()
+ new
+ setl cindent ts=4 sw=4
+ setl cino&
+ setl cino+=l1
+
+ let code =<< trim [CODE]
+ void func(void)
+ {
+ int tab[] =
+ {
+ 1, 2, 3,
+ 4, 5, 6};
+
+ printf("Indent this line correctly!\n");
+
+ switch (foo)
+ {
+ case bar:
+ printf("bar");
+ break;
+ case baz: {
+ printf("baz");
+ break;
+ }
+ case quux:
+ printf("But don't break the indentation of this instruction\n");
+ break;
+ }
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ normal ]]=][
+
+ let expected =<< trim [CODE]
+ void func(void)
+ {
+ int tab[] =
+ {
+ 1, 2, 3,
+ 4, 5, 6};
+
+ printf("Indent this line correctly!\n");
+
+ switch (foo)
+ {
+ case bar:
+ printf("bar");
+ break;
+ case baz: {
+ printf("baz");
+ break;
+ }
+ case quux:
+ printf("But don't break the indentation of this instruction\n");
+ break;
+ }
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_37()
+ new
+ setl cindent ts=4 sw=4
+ setl cino&
+
+ let code =<< trim [CODE]
+ void func(void)
+ {
+ cout << "a"
+ << "b"
+ << ") :"
+ << "c";
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ normal ]]=][
+
+ let expected =<< trim [CODE]
+ void func(void)
+ {
+ cout << "a"
+ << "b"
+ << ") :"
+ << "c";
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_38()
+ new
+ setl cindent ts=4 sw=4
+ setl com=s1:/*,m:*,ex:*/
+
+ let code =<< trim [CODE]
+ void func(void)
+ {
+ /*
+ * This is a comment.
+ */
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ normal ]]3jofoo();
+
+ let expected =<< trim [CODE]
+ void func(void)
+ {
+ /*
+ * This is a comment.
+ */
+ foo();
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_39()
+ new
+ setl cindent ts=4 sw=4
+ setl cino&
+
+ let code =<< trim [CODE]
+ void func(void)
+ {
+ for (int i = 0; i < 10; ++i)
+ if (i & 1) {
+ foo(1);
+ } else
+ foo(0);
+ baz();
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ normal ]]=][
+
+ let expected =<< trim [CODE]
+ void func(void)
+ {
+ for (int i = 0; i < 10; ++i)
+ if (i & 1) {
+ foo(1);
+ } else
+ foo(0);
+ baz();
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_40()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=k2s,(0
+
+ let code =<< trim [CODE]
+ void func(void)
+ {
+ if (condition1
+ && condition2)
+ action();
+ function(argument1
+ && argument2);
+
+ if (c1 && (c2 ||
+ c3))
+ foo;
+ if (c1 &&
+ (c2 || c3))
+ {
+ }
+
+ if ( c1
+ && ( c2
+ || c3))
+ foo;
+ func( c1
+ && ( c2
+ || c3))
+ foo;
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ normal ]]=][
+
+ let expected =<< trim [CODE]
+ void func(void)
+ {
+ if (condition1
+ && condition2)
+ action();
+ function(argument1
+ && argument2);
+
+ if (c1 && (c2 ||
+ c3))
+ foo;
+ if (c1 &&
+ (c2 || c3))
+ {
+ }
+
+ if ( c1
+ && ( c2
+ || c3))
+ foo;
+ func( c1
+ && ( c2
+ || c3))
+ foo;
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_41()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=k2s,(s
+
+ let code =<< trim [CODE]
+ void func(void)
+ {
+ if (condition1
+ && condition2)
+ action();
+ function(argument1
+ && argument2);
+
+ if (c1 && (c2 ||
+ c3))
+ foo;
+ if (c1 &&
+ (c2 || c3))
+ {
+ }
+
+ if ( c1
+ && ( c2
+ || c3))
+ foo;
+ func( c1
+ && ( c2
+ || c3))
+ foo;
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ normal ]]=][
+
+ let expected =<< trim [CODE]
+ void func(void)
+ {
+ if (condition1
+ && condition2)
+ action();
+ function(argument1
+ && argument2);
+
+ if (c1 && (c2 ||
+ c3))
+ foo;
+ if (c1 &&
+ (c2 || c3))
+ {
+ }
+
+ if ( c1
+ && ( c2
+ || c3))
+ foo;
+ func( c1
+ && ( c2
+ || c3))
+ foo;
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_42()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=k2s,(s,U1
+
+ let code =<< trim [CODE]
+ void func(void)
+ {
+ if (condition1
+ && condition2)
+ action();
+ function(argument1
+ && argument2);
+
+ if (c1 && (c2 ||
+ c3))
+ foo;
+ if (c1 &&
+ (c2 || c3))
+ {
+ }
+ if (c123456789
+ && (c22345
+ || c3))
+ printf("foo\n");
+
+ c = c1 &&
+ (
+ c2 ||
+ c3
+ ) && c4;
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ normal ]]=][
+
+ let expected =<< trim [CODE]
+ void func(void)
+ {
+ if (condition1
+ && condition2)
+ action();
+ function(argument1
+ && argument2);
+
+ if (c1 && (c2 ||
+ c3))
+ foo;
+ if (c1 &&
+ (c2 || c3))
+ {
+ }
+ if (c123456789
+ && (c22345
+ || c3))
+ printf("foo\n");
+
+ c = c1 &&
+ (
+ c2 ||
+ c3
+ ) && c4;
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_43()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=k2s,(0,W4
+
+ let code =<< trim [CODE]
+ void func(void)
+ {
+ if (condition1
+ && condition2)
+ action();
+ function(argument1
+ && argument2);
+
+ if (c1 && (c2 ||
+ c3))
+ foo;
+ if (c1 &&
+ (c2 || c3))
+ {
+ }
+ if (c123456789
+ && (c22345
+ || c3))
+ printf("foo\n");
+
+ if ( c1
+ && ( c2
+ || c3))
+ foo;
+
+ a_long_line(
+ argument,
+ argument);
+ a_short_line(argument,
+ argument);
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ normal ]]=][
+
+ let expected =<< trim [CODE]
+ void func(void)
+ {
+ if (condition1
+ && condition2)
+ action();
+ function(argument1
+ && argument2);
+
+ if (c1 && (c2 ||
+ c3))
+ foo;
+ if (c1 &&
+ (c2 || c3))
+ {
+ }
+ if (c123456789
+ && (c22345
+ || c3))
+ printf("foo\n");
+
+ if ( c1
+ && ( c2
+ || c3))
+ foo;
+
+ a_long_line(
+ argument,
+ argument);
+ a_short_line(argument,
+ argument);
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_44()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=k2s,u2
+
+ let code =<< trim [CODE]
+ void func(void)
+ {
+ if (condition1
+ && condition2)
+ action();
+ function(argument1
+ && argument2);
+
+ if (c1 && (c2 ||
+ c3))
+ foo;
+ if (c1 &&
+ (c2 || c3))
+ {
+ }
+ if (c123456789
+ && (c22345
+ || c3))
+ printf("foo\n");
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ normal ]]=][
+
+ let expected =<< trim [CODE]
+ void func(void)
+ {
+ if (condition1
+ && condition2)
+ action();
+ function(argument1
+ && argument2);
+
+ if (c1 && (c2 ||
+ c3))
+ foo;
+ if (c1 &&
+ (c2 || c3))
+ {
+ }
+ if (c123456789
+ && (c22345
+ || c3))
+ printf("foo\n");
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_45()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=k2s,(0,w1
+
+ let code =<< trim [CODE]
+ void func(void)
+ {
+ if (condition1
+ && condition2)
+ action();
+ function(argument1
+ && argument2);
+
+ if (c1 && (c2 ||
+ c3))
+ foo;
+ if (c1 &&
+ (c2 || c3))
+ {
+ }
+ if (c123456789
+ && (c22345
+ || c3))
+ printf("foo\n");
+
+ if ( c1
+ && ( c2
+ || c3))
+ foo;
+ func( c1
+ && ( c2
+ || c3))
+ foo;
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ normal ]]=][
+
+ let expected =<< trim [CODE]
+ void func(void)
+ {
+ if (condition1
+ && condition2)
+ action();
+ function(argument1
+ && argument2);
+
+ if (c1 && (c2 ||
+ c3))
+ foo;
+ if (c1 &&
+ (c2 || c3))
+ {
+ }
+ if (c123456789
+ && (c22345
+ || c3))
+ printf("foo\n");
+
+ if ( c1
+ && ( c2
+ || c3))
+ foo;
+ func( c1
+ && ( c2
+ || c3))
+ foo;
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_46()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=k2,(s
+
+ let code =<< trim [CODE]
+ void func(void)
+ {
+ if (condition1
+ && condition2)
+ action();
+ function(argument1
+ && argument2);
+
+ if (c1 && (c2 ||
+ c3))
+ foo;
+ if (c1 &&
+ (c2 || c3))
+ {
+ }
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ normal ]]=][
+
+ let expected =<< trim [CODE]
+ void func(void)
+ {
+ if (condition1
+ && condition2)
+ action();
+ function(argument1
+ && argument2);
+
+ if (c1 && (c2 ||
+ c3))
+ foo;
+ if (c1 &&
+ (c2 || c3))
+ {
+ }
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_47()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=N-s
+
+ let code =<< trim [CODE]
+ NAMESPACESTART
+ /* valid namespaces with normal indent */
+ namespace
+ {
+ {
+ 111111111111;
+ }
+ }
+ namespace /* test */
+ {
+ 11111111111111111;
+ }
+ namespace // test
+ {
+ 111111111111111111;
+ }
+ namespace
+ {
+ 111111111111111111;
+ }
+ namespace test
+ {
+ 111111111111111111;
+ }
+ namespace test::cpp17
+ {
+ 111111111111111111;
+ }
+ namespace ::incorrectcpp17
+ {
+ 111111111111111111;
+ }
+ namespace test::incorrectcpp17::
+ {
+ 111111111111111111;
+ }
+ namespace test:incorrectcpp17
+ {
+ 111111111111111111;
+ }
+ namespace test:::incorrectcpp17
+ {
+ 111111111111111111;
+ }
+ namespace{
+ 111111111111111111;
+ }
+ namespace test{
+ 111111111111111111;
+ }
+ namespace {
+ 111111111111111111;
+ }
+ namespace test {
+ 111111111111111111;
+ namespace test2 {
+ 22222222222222222;
+ }
+ }
+ inline namespace {
+ 111111111111111111;
+ }
+ inline /* test */ namespace {
+ 111111111111111111;
+ }
+ inline/* test */namespace {
+ 111111111111111111;
+ }
+
+ /* invalid namespaces use block indent */
+ namespace test test2 {
+ 111111111111111111111;
+ }
+ namespace11111111111 {
+ 111111111111;
+ }
+ namespace() {
+ 1111111111111;
+ }
+ namespace()
+ {
+ 111111111111111111;
+ }
+ namespace test test2
+ {
+ 1111111111111111111;
+ }
+ namespace111111111
+ {
+ 111111111111111111;
+ }
+ inlinenamespace {
+ 111111111111111111;
+ }
+ NAMESPACEEND
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ call search('^NAMESPACESTART')
+ exe "normal =/^NAMESPACEEND\n"
+
+ let expected =<< trim [CODE]
+ NAMESPACESTART
+ /* valid namespaces with normal indent */
+ namespace
+ {
+ {
+ 111111111111;
+ }
+ }
+ namespace /* test */
+ {
+ 11111111111111111;
+ }
+ namespace // test
+ {
+ 111111111111111111;
+ }
+ namespace
+ {
+ 111111111111111111;
+ }
+ namespace test
+ {
+ 111111111111111111;
+ }
+ namespace test::cpp17
+ {
+ 111111111111111111;
+ }
+ namespace ::incorrectcpp17
+ {
+ 111111111111111111;
+ }
+ namespace test::incorrectcpp17::
+ {
+ 111111111111111111;
+ }
+ namespace test:incorrectcpp17
+ {
+ 111111111111111111;
+ }
+ namespace test:::incorrectcpp17
+ {
+ 111111111111111111;
+ }
+ namespace{
+ 111111111111111111;
+ }
+ namespace test{
+ 111111111111111111;
+ }
+ namespace {
+ 111111111111111111;
+ }
+ namespace test {
+ 111111111111111111;
+ namespace test2 {
+ 22222222222222222;
+ }
+ }
+ inline namespace {
+ 111111111111111111;
+ }
+ inline /* test */ namespace {
+ 111111111111111111;
+ }
+ inline/* test */namespace {
+ 111111111111111111;
+ }
+
+ /* invalid namespaces use block indent */
+ namespace test test2 {
+ 111111111111111111111;
+ }
+ namespace11111111111 {
+ 111111111111;
+ }
+ namespace() {
+ 1111111111111;
+ }
+ namespace()
+ {
+ 111111111111111111;
+ }
+ namespace test test2
+ {
+ 1111111111111111111;
+ }
+ namespace111111111
+ {
+ 111111111111111111;
+ }
+ inlinenamespace {
+ 111111111111111111;
+ }
+ NAMESPACEEND
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_48()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=j1,J1
+
+ let code =<< trim [CODE]
+ JSSTART
+ var bar = {
+ foo: {
+ that: this,
+ some: ok,
+ },
+ "bar":{
+ a : 2,
+ b: "123abc",
+ x: 4,
+ "y": 5
+ }
+ }
+ JSEND
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ call search('^JSSTART')
+ exe "normal =/^JSEND\n"
+
+ let expected =<< trim [CODE]
+ JSSTART
+ var bar = {
+ foo: {
+ that: this,
+ some: ok,
+ },
+ "bar":{
+ a : 2,
+ b: "123abc",
+ x: 4,
+ "y": 5
+ }
+ }
+ JSEND
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_49()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=j1,J1
+
+ let code =<< trim [CODE]
+ JSSTART
+ var foo = [
+ 1,
+ 2,
+ 3
+ ];
+ JSEND
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ call search('^JSSTART')
+ exe "normal =/^JSEND\n"
+
+ let expected =<< trim [CODE]
+ JSSTART
+ var foo = [
+ 1,
+ 2,
+ 3
+ ];
+ JSEND
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_50()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=j1,J1
+
+ let code =<< trim [CODE]
+ JSSTART
+ function bar() {
+ var foo = [
+ 1,
+ 2,
+ 3
+ ];
+ }
+ JSEND
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ call search('^JSSTART')
+ exe "normal =/^JSEND\n"
+
+ let expected =<< trim [CODE]
+ JSSTART
+ function bar() {
+ var foo = [
+ 1,
+ 2,
+ 3
+ ];
+ }
+ JSEND
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_51()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=j1,J1
+
+ let code =<< trim [CODE]
+ JSSTART
+ (function($){
+
+ if (cond &&
+ cond) {
+ stmt;
+ }
+ window.something.left =
+ (width - 50 + offset) + "px";
+ var class_name='myclass';
+
+ function private_method() {
+ }
+
+ var public_method={
+ method: function(options,args){
+ private_method();
+ }
+ }
+
+ function init(options) {
+
+ $(this).data(class_name+'_public',$.extend({},{
+ foo: 'bar',
+ bar: 2,
+ foobar: [
+ 1,
+ 2,
+ 3
+ ],
+ callback: function(){
+ return true;
+ }
+ }, options||{}));
+ }
+
+ $.fn[class_name]=function() {
+
+ var _arguments=arguments;
+ return this.each(function(){
+
+ var options=$(this).data(class_name+'_public');
+ if (!options) {
+ init.apply(this,_arguments);
+
+ } else {
+ var method=public_method[_arguments[0]];
+
+ if (typeof(method)!='function') {
+ console.log(class_name+' has no method "'+_arguments[0]+'"');
+ return false;
+ }
+ _arguments[0]=options;
+ method.apply(this,_arguments);
+ }
+ });
+ }
+
+ })(jQuery);
+ JSEND
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ call search('^JSSTART')
+ exe "normal =/^JSEND\n"
+
+ let expected =<< trim [CODE]
+ JSSTART
+ (function($){
+
+ if (cond &&
+ cond) {
+ stmt;
+ }
+ window.something.left =
+ (width - 50 + offset) + "px";
+ var class_name='myclass';
+
+ function private_method() {
+ }
+
+ var public_method={
+ method: function(options,args){
+ private_method();
+ }
+ }
+
+ function init(options) {
+
+ $(this).data(class_name+'_public',$.extend({},{
+ foo: 'bar',
+ bar: 2,
+ foobar: [
+ 1,
+ 2,
+ 3
+ ],
+ callback: function(){
+ return true;
+ }
+ }, options||{}));
+ }
+
+ $.fn[class_name]=function() {
+
+ var _arguments=arguments;
+ return this.each(function(){
+
+ var options=$(this).data(class_name+'_public');
+ if (!options) {
+ init.apply(this,_arguments);
+
+ } else {
+ var method=public_method[_arguments[0]];
+
+ if (typeof(method)!='function') {
+ console.log(class_name+' has no method "'+_arguments[0]+'"');
+ return false;
+ }
+ _arguments[0]=options;
+ method.apply(this,_arguments);
+ }
+ });
+ }
+
+ })(jQuery);
+ JSEND
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_52()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=j1,J1
+
+ let code =<< trim [CODE]
+ JSSTART
+ function init(options) {
+ $(this).data(class_name+'_public',$.extend({},{
+ foo: 'bar',
+ bar: 2,
+ foobar: [
+ 1,
+ 2,
+ 3
+ ],
+ callback: function(){
+ return true;
+ }
+ }, options||{}));
+ }
+ JSEND
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ call search('^JSSTART')
+ exe "normal =/^JSEND\n"
+
+ let expected =<< trim [CODE]
+ JSSTART
+ function init(options) {
+ $(this).data(class_name+'_public',$.extend({},{
+ foo: 'bar',
+ bar: 2,
+ foobar: [
+ 1,
+ 2,
+ 3
+ ],
+ callback: function(){
+ return true;
+ }
+ }, options||{}));
+ }
+ JSEND
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_53()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=j1,J1
+
+ let code =<< trim [CODE]
+ JSSTART
+ (function($){
+ function init(options) {
+ $(this).data(class_name+'_public',$.extend({},{
+ foo: 'bar',
+ bar: 2,
+ foobar: [
+ 1,
+ 2,
+ 3
+ ],
+ callback: function(){
+ return true;
+ }
+ }, options||{}));
+ }
+ })(jQuery);
+ JSEND
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ call search('^JSSTART')
+ exe "normal =/^JSEND\n"
+
+ let expected =<< trim [CODE]
+ JSSTART
+ (function($){
+ function init(options) {
+ $(this).data(class_name+'_public',$.extend({},{
+ foo: 'bar',
+ bar: 2,
+ foobar: [
+ 1,
+ 2,
+ 3
+ ],
+ callback: function(){
+ return true;
+ }
+ }, options||{}));
+ }
+ })(jQuery);
+ JSEND
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_54()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=j1,J1,+2
+
+ let code =<< trim [CODE]
+ JSSTART
+ // Results of JavaScript indent
+ // 1
+ (function(){
+ var a = [
+ 'a',
+ 'b',
+ 'c',
+ 'd',
+ 'e',
+ 'f',
+ 'g',
+ 'h',
+ 'i'
+ ];
+ }())
+
+ // 2
+ (function(){
+ var a = [
+ 0 +
+ 5 *
+ 9 *
+ 'a',
+ 'b',
+ 0 +
+ 5 *
+ 9 *
+ 'c',
+ 'd',
+ 'e',
+ 'f',
+ 'g',
+ 'h',
+ 'i'
+ ];
+ }())
+
+ // 3
+ (function(){
+ var a = [
+ 0 +
+ // comment 1
+ 5 *
+ /* comment 2 */
+ 9 *
+ 'a',
+ 'b',
+ 0 +
+ 5 *
+ 9 *
+ 'c',
+ 'd',
+ 'e',
+ 'f',
+ 'g',
+ 'h',
+ 'i'
+ ];
+ }())
+
+ // 4
+ {
+ var a = [
+ 0,
+ 1
+ ];
+ var b;
+ var c;
+ }
+
+ // 5
+ {
+ var a = [
+ [
+ 0
+ ],
+ 2,
+ 3
+ ];
+ }
+
+ // 6
+ {
+ var a = [
+ [
+ 0,
+ 1
+ ],
+ 2,
+ 3
+ ];
+ }
+
+ // 7
+ {
+ var a = [
+ // [
+ 0,
+ // 1
+ // ],
+ 2,
+ 3
+ ];
+ }
+
+ // 8
+ var x = [
+ (function(){
+ var a,
+ b,
+ c,
+ d,
+ e,
+ f,
+ g,
+ h,
+ i;
+ })
+ ];
+
+ // 9
+ var a = [
+ 0 +
+ 5 *
+ 9 *
+ 'a',
+ 'b',
+ 0 +
+ 5 *
+ 9 *
+ 'c',
+ 'd',
+ 'e',
+ 'f',
+ 'g',
+ 'h',
+ 'i'
+ ];
+
+ // 10
+ var a,
+ b,
+ c,
+ d,
+ e,
+ f,
+ g,
+ h,
+ i;
+ JSEND
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ call search('^JSSTART')
+ exe "normal =/^JSEND\n"
+
+ let expected =<< trim [CODE]
+ JSSTART
+ // Results of JavaScript indent
+ // 1
+ (function(){
+ var a = [
+ 'a',
+ 'b',
+ 'c',
+ 'd',
+ 'e',
+ 'f',
+ 'g',
+ 'h',
+ 'i'
+ ];
+ }())
+
+ // 2
+ (function(){
+ var a = [
+ 0 +
+ 5 *
+ 9 *
+ 'a',
+ 'b',
+ 0 +
+ 5 *
+ 9 *
+ 'c',
+ 'd',
+ 'e',
+ 'f',
+ 'g',
+ 'h',
+ 'i'
+ ];
+ }())
+
+ // 3
+ (function(){
+ var a = [
+ 0 +
+ // comment 1
+ 5 *
+ /* comment 2 */
+ 9 *
+ 'a',
+ 'b',
+ 0 +
+ 5 *
+ 9 *
+ 'c',
+ 'd',
+ 'e',
+ 'f',
+ 'g',
+ 'h',
+ 'i'
+ ];
+ }())
+
+ // 4
+ {
+ var a = [
+ 0,
+ 1
+ ];
+ var b;
+ var c;
+ }
+
+ // 5
+ {
+ var a = [
+ [
+ 0
+ ],
+ 2,
+ 3
+ ];
+ }
+
+ // 6
+ {
+ var a = [
+ [
+ 0,
+ 1
+ ],
+ 2,
+ 3
+ ];
+ }
+
+ // 7
+ {
+ var a = [
+ // [
+ 0,
+ // 1
+ // ],
+ 2,
+ 3
+ ];
+ }
+
+ // 8
+ var x = [
+ (function(){
+ var a,
+ b,
+ c,
+ d,
+ e,
+ f,
+ g,
+ h,
+ i;
+ })
+ ];
+
+ // 9
+ var a = [
+ 0 +
+ 5 *
+ 9 *
+ 'a',
+ 'b',
+ 0 +
+ 5 *
+ 9 *
+ 'c',
+ 'd',
+ 'e',
+ 'f',
+ 'g',
+ 'h',
+ 'i'
+ ];
+
+ // 10
+ var a,
+ b,
+ c,
+ d,
+ e,
+ f,
+ g,
+ h,
+ i;
+ JSEND
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_55()
+ new
+ setl cindent ts=4 sw=4
+ setl cino&
+
+ let code =<< trim [CODE]
+ /* start of define */
+ {
+ }
+ #define AAA \
+ BBB\
+ CCC
+
+ #define CNT \
+ 1 + \
+ 2 + \
+ 4
+ /* end of define */
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ call search('start of define')
+ exe "normal =/end of define\n"
+
+ let expected =<< trim [CODE]
+ /* start of define */
+ {
+ }
+ #define AAA \
+ BBB\
+ CCC
+
+ #define CNT \
+ 1 + \
+ 2 + \
+ 4
+ /* end of define */
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
+func Test_cindent_56()
+ new
+ setl cindent ts=4 sw=4
+ setl cino&
+
+ let code =<< trim [CODE]
+ {
+ a = second/*bug*/*line;
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ call search('a = second')
+ normal ox
+
+ let expected =<< trim [CODE]
+ {
+ a = second/*bug*/*line;
+ x
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
" this was going beyond the end of the line.
func Test_cindent_case()
new
- call setline(1, "case x: // x")
+ call setline(1, 'case x: // x')
set cindent
norm! f:a:
+ call assert_equal('case x:: // x', getline(1))
+ set cindent&
+ bwipe!
+endfunc
+
+" Test for changing multiple lines (using c) with cindent
+func Test_cindent_change_multline()
+ new
+ setlocal cindent
+ call setline(1, ['if (a)', '{', ' i = 1;', '}'])
+ normal! jc3jm = 2;
+ call assert_equal("\tm = 2;", getline(2))
+ close!
+endfunc
+
+" This was reading past the end of the line
+func Test_cindent_check_funcdecl()
+ new
+ sil norm o0('\0=L
bwipe!
endfunc
+func Test_cindent_scopedecls()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=g0
+ setl cinsd+=public\ slots,signals
+
+ let code =<< trim [CODE]
+ class Foo
+ {
+ public:
+ virtual void foo() = 0;
+ public slots:
+ void onBar();
+ signals:
+ void baz();
+ private:
+ int x;
+ };
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ normal ]]=][
+
+ let expected =<< trim [CODE]
+ class Foo
+ {
+ public:
+ virtual void foo() = 0;
+ public slots:
+ void onBar();
+ signals:
+ void baz();
+ private:
+ int x;
+ };
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
func Test_cindent_pragma()
new
setl cindent ts=4 sw=4
@@ -173,4 +5406,23 @@ func Test_cindent_pragma()
enew! | close
endfunc
+func Test_backslash_at_end_of_line()
+ new
+ exe "norm v>O'\\\<C-m>-"
+ exe "norm \<C-q>="
+ bwipe!
+endfunc
+
+func Test_find_brace_backwards()
+ " this was looking beyond the end of the line
+ new
+ norm R/*
+ norm o0{
+ norm o//
+ norm V{=
+ call assert_equal(['/*', ' 0{', '//'], getline(1, 3))
+ bwipe!
+endfunc
+
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_clientserver.vim b/src/nvim/testdir/test_clientserver.vim
index 922803438f..db62fe5fa6 100644
--- a/src/nvim/testdir/test_clientserver.vim
+++ b/src/nvim/testdir/test_clientserver.vim
@@ -39,6 +39,8 @@ func Test_client_server()
call remote_send(name, ":let testvar = 'yes'\<CR>")
call WaitFor('remote_expr("' . name . '", "exists(\"testvar\") ? testvar : \"\"", "", 1) == "yes"')
call assert_equal('yes', remote_expr(name, "testvar", "", 2))
+ call assert_fails("let x=remote_expr(name, '2+x')", 'E449:')
+ call assert_fails("let x=remote_expr('[], '2+2')", 'E116:')
if has('unix') && has('gui') && !has('gui_running')
" Running in a terminal and the GUI is available: Tell the server to open
@@ -75,6 +77,7 @@ func Test_client_server()
eval 'MYSELF'->remote_startserver()
" May get MYSELF1 when running the test again.
call assert_match('MYSELF', v:servername)
+ call assert_fails("call remote_startserver('MYSELF')", 'E941:')
endif
let g:testvar = 'myself'
call assert_equal('myself', remote_expr(v:servername, 'testvar'))
@@ -107,7 +110,12 @@ func Test_client_server()
call job_stop(job, 'kill')
endif
endtry
+
+ call assert_fails("let x=remote_peek([])", 'E730:')
+ call assert_fails("let x=remote_read('vim10')", 'E277:')
endfunc
" Uncomment this line to get a debugging log
" call ch_logfile('channellog', 'w')
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim
index 1672b0e840..276bb7fb71 100644
--- a/src/nvim/testdir/test_cmdline.vim
+++ b/src/nvim/testdir/test_cmdline.vim
@@ -2,6 +2,7 @@
source check.vim
source screendump.vim
+source view_util.vim
func Test_complete_tab()
call writefile(['testfile'], 'Xtestfile')
@@ -18,6 +19,11 @@ func Test_complete_list()
" We can't see the output, but at least we check the code runs properly.
call feedkeys(":e test\<C-D>\r", "tx")
call assert_equal('test', expand('%:t'))
+
+ " If a command doesn't support completion, then CTRL-D should be literally
+ " used.
+ call feedkeys(":chistory \<C-D>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"chistory \<C-D>", @:)
endfunc
func Test_complete_wildmenu()
@@ -71,6 +77,17 @@ func Test_complete_wildmenu()
cunmap <C-K>
endif
+ " Test for canceling the wild menu by adding a character
+ redrawstatus
+ call feedkeys(":e Xdir1/\<Tab>x\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e Xdir1/Xdir2/x', @:)
+
+ " Completion using a relative path
+ cd Xdir1/Xdir2
+ call feedkeys(":e ../\<Tab>\<Right>\<Down>\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"e Xtestfile3 Xtestfile4', @:)
+ cd -
+
" cleanup
%bwipe
call delete('Xdir1/Xdir2/Xtestfile4')
@@ -305,7 +322,7 @@ func Test_getcompletion()
call assert_equal([], l)
let l = getcompletion('', 'dir')
- call assert_true(index(l, expand('sautest/')) >= 0)
+ call assert_true(index(l, 'sautest/') >= 0)
let l = getcompletion('NoMatch', 'dir')
call assert_equal([], l)
@@ -415,7 +432,7 @@ func Test_getcompletion()
" Command line completion tests
let l = getcompletion('cd ', 'cmdline')
- call assert_true(index(l, expand('sautest/')) >= 0)
+ call assert_true(index(l, 'sautest/') >= 0)
let l = getcompletion('cd NoMatch', 'cmdline')
call assert_equal([], l)
let l = getcompletion('let v:n', 'cmdline')
@@ -500,8 +517,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()
@@ -531,7 +556,7 @@ func Test_expand_star_star()
call mkdir('a/b', 'p')
call writefile(['asdfasdf'], 'a/b/fileXname')
call feedkeys(":find **/fileXname\<Tab>\<CR>", 'xt')
- call assert_equal('find '.expand('a/b/fileXname'), getreg(':'))
+ call assert_equal('find a/b/fileXname', getreg(':'))
bwipe!
call delete('a', 'rf')
endfunc
@@ -573,6 +598,29 @@ func Test_cmdline_paste()
" ignore error E32
endtry
call assert_equal("Xtestfile", bufname("%"))
+
+ " Try to paste an invalid register using <C-R>
+ call feedkeys(":\"one\<C-R>\<C-X>two\<CR>", 'xt')
+ call assert_equal('"onetwo', @:)
+
+ " Test for pasting register containing CTRL-H using CTRL-R and CTRL-R CTRL-R
+ let @a = "xy\<C-H>z"
+ call feedkeys(":\"\<C-R>a\<CR>", 'xt')
+ call assert_equal('"xz', @:)
+ call feedkeys(":\"\<C-R>\<C-R>a\<CR>", 'xt')
+ call assert_equal("\"xy\<C-H>z", @:)
+ call feedkeys(":\"\<C-R>\<C-O>a\<CR>", 'xt')
+ call assert_equal("\"xy\<C-H>z", @:)
+
+ " Test for pasting register containing CTRL-V using CTRL-R and CTRL-R CTRL-R
+ let @a = "xy\<C-V>z"
+ call feedkeys(":\"\<C-R>=@a\<CR>\<cr>", 'xt')
+ call assert_equal('"xyz', @:)
+ call feedkeys(":\"\<C-R>\<C-R>=@a\<CR>\<cr>", 'xt')
+ call assert_equal("\"xy\<C-V>z", @:)
+
+ call assert_beeps('call feedkeys(":\<C-R>=\<C-R>=\<Esc>", "xt")')
+
bwipe!
endfunc
@@ -628,6 +676,14 @@ func Test_illegal_address2()
call delete('Xtest.vim')
endfunc
+func Test_mark_from_line_zero()
+ " this was reading past the end of the first (empty) line
+ new
+ norm oxxxx
+ call assert_fails("0;'(", 'E20:')
+ bwipe!
+endfunc
+
func Test_cmdline_complete_wildoptions()
help
call feedkeys(":tag /\<c-a>\<c-b>\"\<cr>", 'tx')
@@ -741,6 +797,15 @@ funct Test_cmdline_complete_languages()
endif
endfunc
+func Test_cmdline_complete_env_variable()
+ let $X_VIM_TEST_COMPLETE_ENV = 'foo'
+
+ call feedkeys(":edit $X_VIM_TEST_COMPLETE_E\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_match('"edit $X_VIM_TEST_COMPLETE_ENV', @:)
+
+ unlet $X_VIM_TEST_COMPLETE_ENV
+endfunc
+
func Test_cmdline_complete_expression()
let g:SomeVar = 'blah'
for cmd in ['exe', 'echo', 'echon', 'echomsg']
@@ -752,6 +817,158 @@ func Test_cmdline_complete_expression()
unlet g:SomeVar
endfunc
+" Test for various command-line completion
+func Test_cmdline_complete_various()
+ " completion for a command starting with a comment
+ call feedkeys(": :|\"\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\" :|\"\<C-A>", @:)
+
+ " completion for a range followed by a comment
+ call feedkeys(":1,2\"\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"1,2\"\<C-A>", @:)
+
+ " completion for :k command
+ call feedkeys(":ka\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"ka\<C-A>", @:)
+
+ " completion for short version of the :s command
+ call feedkeys(":sI \<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"sI \<C-A>", @:)
+
+ " completion for :write command
+ call mkdir('Xdir')
+ call writefile(['one'], 'Xdir/Xfile1')
+ let save_cwd = getcwd()
+ cd Xdir
+ call feedkeys(":w >> \<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"w >> Xfile1", @:)
+ call chdir(save_cwd)
+ call delete('Xdir', 'rf')
+
+ " completion for :w ! and :r ! commands
+ call feedkeys(":w !invalid_xyz_cmd\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"w !invalid_xyz_cmd", @:)
+ call feedkeys(":r !invalid_xyz_cmd\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"r !invalid_xyz_cmd", @:)
+
+ " completion for :>> and :<< commands
+ call feedkeys(":>>>\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\">>>\<C-A>", @:)
+ call feedkeys(":<<<\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"<<<\<C-A>", @:)
+
+ " completion for command with +cmd argument
+ call feedkeys(":buffer +/pat Xabc\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"buffer +/pat Xabc", @:)
+ call feedkeys(":buffer +/pat\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"buffer +/pat\<C-A>", @:)
+
+ " completion for a command with a trailing comment
+ call feedkeys(":ls \" comment\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"ls \" comment\<C-A>", @:)
+
+ " completion for a command with a trailing command
+ call feedkeys(":ls | ls\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"ls | ls", @:)
+
+ " completion for a command with an CTRL-V escaped argument
+ call feedkeys(":ls \<C-V>\<C-V>a\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"ls \<C-V>a\<C-A>", @:)
+
+ " completion for a command that doesn't take additional arguments
+ call feedkeys(":all abc\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"all abc\<C-A>", @:)
+
+ " completion for a command with a command modifier
+ call feedkeys(":topleft new\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"topleft new", @:)
+
+ " completion for the :match command
+ call feedkeys(":match Search /pat/\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"match Search /pat/\<C-A>", @:)
+
+ " completion for the :s command
+ call feedkeys(":s/from/to/g\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"s/from/to/g\<C-A>", @:)
+
+ " completion for the :dlist command
+ call feedkeys(":dlist 10 /pat/ a\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"dlist 10 /pat/ a\<C-A>", @:)
+
+ " completion for the :doautocmd command
+ call feedkeys(":doautocmd User MyCmd a.c\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"doautocmd User MyCmd a.c\<C-A>", @:)
+
+ " completion for the :augroup command
+ augroup XTest
+ augroup END
+ call feedkeys(":augroup X\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"augroup XTest", @:)
+ augroup! XTest
+
+ " completion for the :unlet command
+ call feedkeys(":unlet one two\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"unlet one two", @:)
+
+ " completion for the :buffer command with curlies
+ " FIXME: what should happen on MS-Windows?
+ if !has('win32')
+ edit \{someFile}
+ call feedkeys(":buf someFile\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"buf {someFile}", @:)
+ bwipe {someFile}
+ endif
+
+ " completion for the :bdelete command
+ call feedkeys(":bdel a b c\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"bdel a b c", @:)
+
+ " completion for the :mapclear command
+ call feedkeys(":mapclear \<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"mapclear <buffer>", @:)
+
+ " completion for user defined commands with menu names
+ menu Test.foo :ls<CR>
+ com -nargs=* -complete=menu MyCmd
+ call feedkeys(":MyCmd Te\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"MyCmd Test.', @:)
+ delcom MyCmd
+ unmenu Test
+
+ " completion for user defined commands with mappings
+ mapclear
+ map <F3> :ls<CR>
+ com -nargs=* -complete=mapping MyCmd
+ call feedkeys(":MyCmd <F\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"MyCmd <F3>', @:)
+ mapclear
+ delcom MyCmd
+
+ " completion for :set path= with multiple backslashes
+ call feedkeys(":set path=a\\\\\\ b\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"set path=a\\\ b', @:)
+
+ " completion for :set dir= with a backslash
+ call feedkeys(":set dir=a\\ b\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"set dir=a\ b', @:)
+
+ " completion for the :py3 commands
+ call feedkeys(":py3\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"py3 py3do py3file', @:)
+
+ " redir @" is not the start of a comment. So complete after that
+ call feedkeys(":redir @\" | cwin\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"redir @" | cwindow', @:)
+
+ " completion after a backtick
+ call feedkeys(":e `a1b2c\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e `a1b2c', @:)
+
+ " completion for the expression register
+ call feedkeys(":\"\<C-R>=float2\t\"\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"float2nr("', @=)
+endfunc
+
func Test_cmdline_write_alternatefile()
new
call setline('.', ['one', 'two'])
@@ -793,9 +1010,32 @@ func Test_cmdline_search_range()
1,\&s/b/B/
call assert_equal('B', getline(2))
+ let @/ = 'apple'
+ call assert_fails('\/print', 'E486:')
+
bwipe!
endfunc
+" Test for the tick mark (') in an excmd range
+func Test_tick_mark_in_range()
+ " If only the tick is passed as a range and no command is specified, there
+ " should not be an error
+ call feedkeys(":'\<CR>", 'xt')
+ call assert_equal("'", getreg(':'))
+ call assert_fails("',print", 'E78:')
+endfunc
+
+" Test for using a line number followed by a search pattern as range
+func Test_lnum_and_pattern_as_range()
+ new
+ call setline(1, ['foo 1', 'foo 2', 'foo 3'])
+ let @" = ''
+ 2/foo/yank
+ call assert_equal("foo 3\n", @")
+ call assert_equal(1, line('.'))
+ close!
+endfunc
+
" Tests for getcmdline(), getcmdpos() and getcmdtype()
func Check_cmdline(cmdtype)
call assert_equal('MyCmd a', getcmdline())
@@ -828,6 +1068,8 @@ func Test_getcmdtype()
cnoremap <expr> <F6> Check_cmdline('=')
call feedkeys("a\<C-R>=MyCmd a\<F6>\<Esc>\<Esc>", "xt")
cunmap <F6>
+
+ call assert_equal('', getcmdline())
endfunc
func Test_getcmdwintype()
@@ -877,22 +1119,6 @@ func Test_getcmdwin_autocmd()
augroup END
endfunc
-" Test error: "E135: *Filter* Autocommands must not change current buffer"
-func Test_cmd_bang_E135()
- new
- call setline(1, ['a', 'b', 'c', 'd'])
- augroup test_cmd_filter_E135
- au!
- autocmd FilterReadPost * help
- augroup END
- call assert_fails('2,3!echo "x"', 'E135:')
-
- augroup test_cmd_filter_E135
- au!
- augroup END
- %bwipe!
-endfunc
-
func Test_verbosefile()
set verbosefile=Xlog
echomsg 'foo'
@@ -973,34 +1199,6 @@ func Test_cmdline_overstrike()
let &encoding = encoding_save
endfunc
-func Test_cmdwin_feedkeys()
- " This should not generate E488
- call feedkeys("q:\<CR>", 'x')
-endfunc
-
-" Tests for the issues fixed in 7.4.441.
-" When 'cedit' is set to Ctrl-C, opening the command window hangs Vim
-func Test_cmdwin_cedit()
- exe "set cedit=\<C-c>"
- normal! :
- call assert_equal(1, winnr('$'))
-
- let g:cmd_wintype = ''
- func CmdWinType()
- let g:cmd_wintype = getcmdwintype()
- let g:wintype = win_gettype()
- return ''
- endfunc
-
- call feedkeys("\<C-c>a\<C-R>=CmdWinType()\<CR>\<CR>")
- echo input('')
- call assert_equal('@', g:cmd_wintype)
- call assert_equal('command', g:wintype)
-
- set cedit&vim
- delfunc CmdWinType
-endfunc
-
func Test_cmdwin_restore()
CheckScreendump
@@ -1077,6 +1275,38 @@ func Test_buffers_lastused()
bwipeout bufc
endfunc
+func Test_cmdwin_feedkeys()
+ " This should not generate E488
+ call feedkeys("q:\<CR>", 'x')
+ " Using feedkeys with q: only should automatically close the cmd window
+ call feedkeys('q:', 'xt')
+ call assert_equal(1, winnr('$'))
+ call assert_equal('', getcmdwintype())
+endfunc
+
+" Tests for the issues fixed in 7.4.441.
+" When 'cedit' is set to Ctrl-C, opening the command window hangs Vim
+func Test_cmdwin_cedit()
+ exe "set cedit=\<C-c>"
+ normal! :
+ call assert_equal(1, winnr('$'))
+
+ let g:cmd_wintype = ''
+ func CmdWinType()
+ let g:cmd_wintype = getcmdwintype()
+ let g:wintype = win_gettype()
+ return ''
+ endfunc
+
+ call feedkeys("\<C-c>a\<C-R>=CmdWinType()\<CR>\<CR>")
+ echo input('')
+ call assert_equal('@', g:cmd_wintype)
+ call assert_equal('command', g:wintype)
+
+ set cedit&vim
+ delfunc CmdWinType
+endfunc
+
" Test for CmdwinEnter autocmd
func Test_cmdwin_autocmd()
CheckFeature cmdwin
@@ -1115,6 +1345,377 @@ func Test_cmdlineclear_tabenter()
call delete('XtestCmdlineClearTabenter')
endfunc
+" Test for expanding special keywords in cmdline
+func Test_cmdline_expand_special()
+ new
+ %bwipe!
+ call assert_fails('e #', 'E194:')
+ call assert_fails('e <afile>', 'E495:')
+ call assert_fails('e <abuf>', 'E496:')
+ call assert_fails('e <amatch>', 'E497:')
+ call writefile([], 'Xfile.cpp')
+ call writefile([], 'Xfile.java')
+ new Xfile.cpp
+ call feedkeys(":e %:r\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e Xfile.cpp Xfile.java', @:)
+ close
+ call delete('Xfile.cpp')
+ call delete('Xfile.java')
+endfunc
+
+func Test_cmdwin_jump_to_win()
+ call assert_fails('call feedkeys("q:\<C-W>\<C-W>\<CR>", "xt")', 'E11:')
+ new
+ set modified
+ call assert_fails('call feedkeys("q/:qall\<CR>", "xt")', 'E162:')
+ close!
+ call feedkeys("q/:close\<CR>", "xt")
+ call assert_equal(1, winnr('$'))
+ call feedkeys("q/:exit\<CR>", "xt")
+ call assert_equal(1, winnr('$'))
+
+ " opening command window twice should fail
+ call assert_beeps('call feedkeys("q:q:\<CR>\<CR>", "xt")')
+ call assert_equal(1, winnr('$'))
+endfunc
+
+" Test for backtick expression in the command line
+func Test_cmd_backtick()
+ %argd
+ argadd `=['a', 'b', 'c']`
+ call assert_equal(['a', 'b', 'c'], argv())
+ %argd
+endfunc
+
+func Test_cmdwin_tabpage()
+ tabedit
+ " v8.2.1919 isn't ported yet, so E492 is thrown after E11 here.
+ " v8.2.1183 also isn't ported yet, so we also can't assert E11 directly.
+ " For now, assert E11 and E492 separately. When v8.2.1183 is ported, the
+ " assert for E492 will fail and this workaround should be removed.
+ " call assert_fails("silent norm q/g :I\<Esc>", 'E11:')
+ call assert_fails("silent norm q/g ", 'E11:')
+ call assert_fails("silent norm q/g :I\<Esc>", 'E492:')
+ tabclose!
+endfunc
+
+" Test for the :! command
+func Test_cmd_bang()
+ if !has('unix')
+ return
+ endif
+
+ let lines =<< trim [SCRIPT]
+ " Test for no previous command
+ call assert_fails('!!', 'E34:')
+ set nomore
+ " Test for cmdline expansion with :!
+ call setline(1, 'foo!')
+ silent !echo <cWORD> > Xfile.out
+ call assert_equal(['foo!'], readfile('Xfile.out'))
+ " Test for using previous command
+ silent !echo \! !
+ call assert_equal(['! echo foo!'], readfile('Xfile.out'))
+ call writefile(v:errors, 'Xresult')
+ call delete('Xfile.out')
+ qall!
+ [SCRIPT]
+ call writefile(lines, 'Xscript')
+ if RunVim([], [], '--clean -S Xscript')
+ call assert_equal([], readfile('Xresult'))
+ endif
+ call delete('Xscript')
+ call delete('Xresult')
+endfunc
+
+" Test error: "E135: *Filter* Autocommands must not change current buffer"
+func Test_cmd_bang_E135()
+ new
+ call setline(1, ['a', 'b', 'c', 'd'])
+ augroup test_cmd_filter_E135
+ au!
+ autocmd FilterReadPost * help
+ augroup END
+ call assert_fails('2,3!echo "x"', 'E135:')
+
+ augroup test_cmd_filter_E135
+ au!
+ augroup END
+ %bwipe!
+endfunc
+
+" Test for using ~ for home directory in cmdline completion matches
+func Test_cmdline_expand_home()
+ call mkdir('Xdir')
+ call writefile([], 'Xdir/Xfile1')
+ call writefile([], 'Xdir/Xfile2')
+ cd Xdir
+ let save_HOME = $HOME
+ let $HOME = getcwd()
+ call feedkeys(":e ~/\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e ~/Xfile1 ~/Xfile2', @:)
+ let $HOME = save_HOME
+ cd ..
+ call delete('Xdir', 'rf')
+endfunc
+
+" Test for using CTRL-\ CTRL-G in the command line to go back to normal mode
+" or insert mode (when 'insertmode' is set)
+func Test_cmdline_ctrl_g()
+ new
+ call setline(1, 'abc')
+ call cursor(1, 3)
+ " If command line is entered from insert mode, using C-\ C-G should back to
+ " insert mode
+ call feedkeys("i\<C-O>:\<C-\>\<C-G>xy", 'xt')
+ call assert_equal('abxyc', getline(1))
+ call assert_equal(4, col('.'))
+
+ " If command line is entered in 'insertmode', using C-\ C-G should back to
+ " 'insertmode'
+ " call feedkeys(":set im\<cr>\<C-L>:\<C-\>\<C-G>12\<C-L>:set noim\<cr>", 'xt')
+ " call assert_equal('ab12xyc', getline(1))
+ close!
+endfunc
+
+" Test for 'wildmode'
+func Test_wildmode()
+ func T(a, c, p)
+ return "oneA\noneB\noneC"
+ endfunc
+ command -nargs=1 -complete=custom,T MyCmd
+
+ func SaveScreenLine()
+ let g:Sline = Screenline(&lines - 1)
+ return ''
+ endfunc
+ cnoremap <expr> <F2> SaveScreenLine()
+
+ set nowildmenu
+ set wildmode=full,list
+ let g:Sline = ''
+ call feedkeys(":MyCmd \t\t\<F2>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('oneA oneB oneC', g:Sline)
+ call assert_equal('"MyCmd oneA', @:)
+
+ set wildmode=longest,full
+ call feedkeys(":MyCmd o\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"MyCmd one', @:)
+ call feedkeys(":MyCmd o\t\t\t\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"MyCmd oneC', @:)
+
+ set wildmode=longest
+ call feedkeys(":MyCmd one\t\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"MyCmd one', @:)
+
+ set wildmode=list:longest
+ let g:Sline = ''
+ call feedkeys(":MyCmd \t\<F2>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('oneA oneB oneC', g:Sline)
+ call assert_equal('"MyCmd one', @:)
+
+ set wildmode=""
+ call feedkeys(":MyCmd \t\t\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"MyCmd oneA', @:)
+
+ " Test for wildmode=longest with 'fileignorecase' set
+ set wildmode=longest
+ set fileignorecase
+ argadd AA AAA AAAA
+ call feedkeys(":buffer \t\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"buffer AA', @:)
+ set fileignorecase&
+
+ " Test for listing files with wildmode=list
+ set wildmode=list
+ let g:Sline = ''
+ call feedkeys(":b A\t\t\<F2>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('AA AAA AAAA', g:Sline)
+ call assert_equal('"b A', @:)
+
+ %argdelete
+ delcommand MyCmd
+ delfunc T
+ delfunc SaveScreenLine
+ cunmap <F2>
+ set wildmode&
+ %bwipe!
+endfunc
+
+" Test for interrupting the command-line completion
+func Test_interrupt_compl()
+ func F(lead, cmdl, p)
+ if a:lead =~ 'tw'
+ call interrupt()
+ return
+ endif
+ return "one\ntwo\nthree"
+ endfunc
+ command -nargs=1 -complete=custom,F Tcmd
+
+ set nowildmenu
+ set wildmode=full
+ let interrupted = 0
+ try
+ call feedkeys(":Tcmd tw\<Tab>\<C-B>\"\<CR>", 'xt')
+ catch /^Vim:Interrupt$/
+ let interrupted = 1
+ endtry
+ call assert_equal(1, interrupted)
+
+ delcommand Tcmd
+ delfunc F
+ set wildmode&
+endfunc
+
+" Test for moving the cursor on the : command line
+func Test_cmdline_edit()
+ let str = ":one two\<C-U>"
+ let str ..= "one two\<C-W>\<C-W>"
+ let str ..= "four\<BS>\<C-H>\<Del>\<kDel>"
+ let str ..= "\<Left>five\<Right>"
+ let str ..= "\<Home>two "
+ let str ..= "\<C-Left>one "
+ let str ..= "\<C-Right> three"
+ let str ..= "\<End>\<S-Left>four "
+ let str ..= "\<S-Right> six"
+ let str ..= "\<C-B>\"\<C-E> seven\<CR>"
+ call feedkeys(str, 'xt')
+ call assert_equal("\"one two three four five six seven", @:)
+endfunc
+
+" Test for moving the cursor on the / command line in 'rightleft' mode
+func Test_cmdline_edit_rightleft()
+ CheckFeature rightleft
+ set rightleft
+ set rightleftcmd=search
+ let str = "/one two\<C-U>"
+ let str ..= "one two\<C-W>\<C-W>"
+ let str ..= "four\<BS>\<C-H>\<Del>\<kDel>"
+ let str ..= "\<Right>five\<Left>"
+ let str ..= "\<Home>two "
+ let str ..= "\<C-Right>one "
+ let str ..= "\<C-Left> three"
+ let str ..= "\<End>\<S-Right>four "
+ let str ..= "\<S-Left> six"
+ let str ..= "\<C-B>\"\<C-E> seven\<CR>"
+ call assert_fails("call feedkeys(str, 'xt')", 'E486:')
+ call assert_equal("\"one two three four five six seven", @/)
+ set rightleftcmd&
+ set rightleft&
+endfunc
+
+" Test for using <C-\>e in the command line to evaluate an expression
+func Test_cmdline_expr()
+ " Evaluate an expression from the beginning of a command line
+ call feedkeys(":abc\<C-B>\<C-\>e\"\\\"hello\"\<CR>\<CR>", 'xt')
+ call assert_equal('"hello', @:)
+
+ " Use an invalid expression for <C-\>e
+ call assert_beeps('call feedkeys(":\<C-\>einvalid\<CR>", "tx")')
+
+ " Insert literal <CTRL-\> in the command line
+ call feedkeys(":\"e \<C-\>\<C-Y>\<CR>", 'xt')
+ call assert_equal("\"e \<C-\>\<C-Y>", @:)
+endfunc
+
+" Test for 'imcmdline' and 'imsearch'
+" This test doesn't actually test the input method functionality.
+func Test_cmdline_inputmethod()
+ new
+ call setline(1, ['', 'abc', ''])
+ set imcmdline
+
+ call feedkeys(":\"abc\<CR>", 'xt')
+ call assert_equal("\"abc", @:)
+ call feedkeys(":\"\<C-^>abc\<C-^>\<CR>", 'xt')
+ call assert_equal("\"abc", @:)
+ call feedkeys("/abc\<CR>", 'xt')
+ call assert_equal([2, 1], [line('.'), col('.')])
+ call feedkeys("/\<C-^>abc\<C-^>\<CR>", 'xt')
+ call assert_equal([2, 1], [line('.'), col('.')])
+
+ " set imsearch=2
+ call cursor(1, 1)
+ call feedkeys("/abc\<CR>", 'xt')
+ call assert_equal([2, 1], [line('.'), col('.')])
+ call cursor(1, 1)
+ call feedkeys("/\<C-^>abc\<C-^>\<CR>", 'xt')
+ call assert_equal([2, 1], [line('.'), col('.')])
+ set imdisable
+ call feedkeys("/\<C-^>abc\<C-^>\<CR>", 'xt')
+ call assert_equal([2, 1], [line('.'), col('.')])
+ set imdisable&
+ set imsearch&
+
+ set imcmdline&
+ %bwipe!
+endfunc
+
+" Test for recursively getting multiple command line inputs
+func Test_cmdwin_multi_input()
+ call feedkeys(":\<C-R>=input('P: ')\<CR>\"cyan\<CR>\<CR>", 'xt')
+ call assert_equal('"cyan', @:)
+endfunc
+
+" Test for using CTRL-_ in the command line with 'allowrevins'
+func Test_cmdline_revins()
+ CheckNotMSWindows
+ CheckFeature rightleft
+ call feedkeys(":\"abc\<c-_>\<cr>", 'xt')
+ call assert_equal("\"abc\<c-_>", @:)
+ set allowrevins
+ call feedkeys(":\"abc\<c-_>xyz\<c-_>\<CR>", 'xt')
+ call assert_equal('"abcñèæ', @:)
+ set allowrevins&
+endfunc
+
+" Test for typing UTF-8 composing characters in the command line
+func Test_cmdline_composing_chars()
+ call feedkeys(":\"\<C-V>u3046\<C-V>u3099\<CR>", 'xt')
+ call assert_equal('"ゔ', @:)
+endfunc
+
+" Test for normal mode commands not supported in the cmd window
+func Test_cmdwin_blocked_commands()
+ call assert_fails('call feedkeys("q:\<C-T>\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-]>\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-^>\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:Q\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:Z\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<F1>\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>s\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>v\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>^\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>n\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>z\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>o\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>w\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>j\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>k\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>h\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>l\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>T\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>x\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>r\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>R\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>K\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>}\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>]\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>f\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>d\<CR>", "xt")', 'E11:')
+ call assert_fails('call feedkeys("q:\<C-W>g\<CR>", "xt")', 'E11:')
+endfunc
+
+" Close the Cmd-line window in insert mode using CTRL-C
+func Test_cmdwin_insert_mode_close()
+ %bw!
+ let s = ''
+ exe "normal q:a\<C-C>let s='Hello'\<CR>"
+ call assert_equal('Hello', s)
+ call assert_equal(1, winnr('$'))
+endfunc
+
" test that ";" works to find a match at the start of the first line
func Test_zero_line_search()
new
@@ -1192,4 +1793,57 @@ func Test_recalling_cmdline()
cunmap <Plug>(save-cmdline)
endfunc
+" this was going over the end of IObuff
+func Test_report_error_with_composing()
+ let caught = 'no'
+ try
+ exe repeat('0', 987) .. "0\xdd\x80\xdd\x80\xdd\x80\xdd\x80"
+ catch /E492:/
+ let caught = 'yes'
+ endtry
+ call assert_equal('yes', caught)
+endfunc
+
+" Test for expanding 2-letter and 3-letter :substitute command arguments.
+" These commands don't accept an argument.
+func Test_cmdline_complete_substitute_short()
+ for cmd in ['sc', 'sce', 'scg', 'sci', 'scI', 'scn', 'scp', 'scl',
+ \ 'sgc', 'sge', 'sg', 'sgi', 'sgI', 'sgn', 'sgp', 'sgl', 'sgr',
+ \ 'sic', 'sie', 'si', 'siI', 'sin', 'sip', 'sir',
+ \ 'sIc', 'sIe', 'sIg', 'sIi', 'sI', 'sIn', 'sIp', 'sIl', 'sIr',
+ \ 'src', 'srg', 'sri', 'srI', 'srn', 'srp', 'srl', 'sr']
+ call feedkeys(':' .. cmd .. " \<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"' .. cmd .. " \<Tab>", @:)
+ endfor
+endfunc
+
+func Check_completion()
+ call assert_equal('let a', getcmdline())
+ call assert_equal(6, getcmdpos())
+ call assert_equal(7, getcmdscreenpos())
+ call assert_equal('var', getcmdcompltype())
+ return ''
+endfunc
+
+func Test_screenpos_and_completion()
+ call feedkeys(":let a\<C-R>=Check_completion()\<CR>\<Esc>", "xt")
+endfunc
+
+func Test_recursive_register()
+ let @= = ''
+ silent! ?e/
+ let caught = 'no'
+ try
+ normal //
+ catch /E169:/
+ let caught = 'yes'
+ endtry
+ call assert_equal('yes', caught)
+endfunc
+
+func Test_long_error_message()
+ " the error should be truncated, not overrun IObuff
+ silent! norm Q00000000000000     000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000                                                                                                                                                                                                                        
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_command_count.vim b/src/nvim/testdir/test_command_count.vim
index c7dddf4164..55b230373f 100644
--- a/src/nvim/testdir/test_command_count.vim
+++ b/src/nvim/testdir/test_command_count.vim
@@ -33,7 +33,7 @@ func Test_command_count_0()
delcommand RangeBuffers
delcommand RangeBuffersAll
- set nohidden
+ set hidden&
set swapfile&
endfunc
diff --git a/src/nvim/testdir/test_compiler.vim b/src/nvim/testdir/test_compiler.vim
index aaa2301bca..3dc8710d63 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')
@@ -65,5 +68,9 @@ func Test_compiler_completion()
endfunc
func Test_compiler_error()
+ let g:current_compiler = 'abc'
call assert_fails('compiler doesnotexist', 'E666:')
+ call assert_equal('abc', g:current_compiler)
+ call assert_fails('compiler! doesnotexist', 'E666:')
+ unlet! g:current_compiler
endfunc
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_cscope.vim b/src/nvim/testdir/test_cscope.vim
index cc6154af69..76ea35fa10 100644
--- a/src/nvim/testdir/test_cscope.vim
+++ b/src/nvim/testdir/test_cscope.vim
@@ -1,7 +1,11 @@
" Test for cscope commands.
-if !has('cscope') || !executable('cscope') || !has('quickfix')
- finish
+source check.vim
+CheckFeature cscope
+CheckFeature quickfix
+
+if !executable('cscope')
+ throw 'Skipped: cscope program missing'
endif
func CscopeSetupOrClean(setup)
@@ -102,7 +106,7 @@ func Test_cscopeWithCscopeConnections()
for cmd in ['cs find f Xmemfile_test.c', 'cs find 7 Xmemfile_test.c']
enew
let a = execute(cmd)
- call assert_true(a =~ '"Xmemfile_test.c" \d\+L, \d\+C')
+ call assert_true(a =~ '"Xmemfile_test.c" \d\+L, \d\+B')
call assert_equal('Xmemfile_test.c', @%)
endfor
@@ -112,7 +116,7 @@ func Test_cscopeWithCscopeConnections()
let a = execute(cmd)
let alines = split(a, '\n', 1)
call assert_equal('', alines[0])
- call assert_true(alines[1] =~ '"Xmemfile_test.c" \d\+L, \d\+C')
+ call assert_true(alines[1] =~ '"Xmemfile_test.c" \d\+L, \d\+B')
call assert_equal('(1 of 1): <<global>> #include <assert.h>', alines[2])
call assert_equal('#include <assert.h>', getline('.'))
endfor
diff --git a/src/nvim/testdir/test_cursor_func.vim b/src/nvim/testdir/test_cursor_func.vim
index e8c4a952ee..3b8a5f27ad 100644
--- a/src/nvim/testdir/test_cursor_func.vim
+++ b/src/nvim/testdir/test_cursor_func.vim
@@ -1,4 +1,4 @@
-" Tests for cursor().
+" Tests for cursor() and other functions that get/set the cursor position
func Test_wrong_arguments()
call assert_fails('call cursor(1. 3)', 'E474:')
@@ -24,6 +24,9 @@ func Test_move_cursor()
" below last line goes to last line
call cursor(9, 1)
call assert_equal([4, 1, 0, 1], getcurpos()[1:])
+ " pass string arguments
+ call cursor('3', '3')
+ call assert_equal([3, 3, 0, 3], getcurpos()[1:])
call setline(1, ["\<TAB>"])
call cursor(1, 1, 1)
@@ -72,7 +75,6 @@ func Test_curswant_with_cursorline()
endfunc
func Test_screenpos()
- throw 'skipped: TODO: '
rightbelow new
rightbelow 20vsplit
call setline(1, ["\tsome text", "long wrapping line here", "next line"])
@@ -97,12 +99,15 @@ func Test_screenpos()
\ 'curscol': wincol + 9,
\ 'endcol': wincol + 9}, screenpos(winid, 2, 22))
close
+ call assert_equal({}, screenpos(999, 1, 1))
bwipe!
call assert_equal({'col': 1, 'row': 1, 'endcol': 1, 'curscol': 1}, screenpos(win_getid(), 1, 1))
- nmenu WinBar.TEST :
+ " nmenu WinBar.TEST :
+ setlocal winbar=TEST
call assert_equal({'col': 1, 'row': 2, 'endcol': 1, 'curscol': 1}, screenpos(win_getid(), 1, 1))
- nunmenu WinBar.TEST
+ " nunmenu WinBar.TEST
+ setlocal winbar&
endfunc
func Test_screenpos_number()
@@ -119,3 +124,253 @@ func Test_screenpos_number()
close
bwipe!
endfunc
+
+" Save the visual start character position
+func SaveVisualStartCharPos()
+ call add(g:VisualStartPos, getcharpos('v'))
+ return ''
+endfunc
+
+" Save the current cursor character position in insert mode
+func SaveInsertCurrentCharPos()
+ call add(g:InsertCurrentPos, getcharpos('.'))
+ return ''
+endfunc
+
+" Test for the getcharpos() function
+func Test_getcharpos()
+ call assert_fails('call getcharpos({})', 'E731:')
+ call assert_equal([0, 0, 0, 0], getcharpos(0))
+ new
+ call setline(1, ['', "01\tà4è678", 'Ⅵ', '012345678', ' │ x'])
+
+ " Test for '.' and '$'
+ normal 1G
+ call assert_equal([0, 1, 1, 0], getcharpos('.'))
+ call assert_equal([0, 5, 1, 0], getcharpos('$'))
+ normal 2G6l
+ call assert_equal([0, 2, 7, 0], getcharpos('.'))
+ normal 3G$
+ call assert_equal([0, 3, 1, 0], getcharpos('.'))
+ normal 4G$
+ call assert_equal([0, 4, 9, 0], getcharpos('.'))
+
+ " Test for a mark
+ normal 2G7lmmgg
+ call assert_equal([0, 2, 8, 0], getcharpos("'m"))
+ delmarks m
+ call assert_equal([0, 0, 0, 0], getcharpos("'m"))
+
+ " Check mark does not move
+ normal 5Gfxma
+ call assert_equal([0, 5, 5, 0], getcharpos("'a"))
+ call assert_equal([0, 5, 5, 0], getcharpos("'a"))
+ call assert_equal([0, 5, 5, 0], getcharpos("'a"))
+
+ " Test for the visual start column
+ vnoremap <expr> <F3> SaveVisualStartCharPos()
+ let g:VisualStartPos = []
+ exe "normal 2G6lv$\<F3>ohh\<F3>o\<F3>"
+ call assert_equal([[0, 2, 7, 0], [0, 2, 10, 0], [0, 2, 5, 0]], g:VisualStartPos)
+ call assert_equal([0, 2, 9, 0], getcharpos('v'))
+ let g:VisualStartPos = []
+ exe "normal 3Gv$\<F3>o\<F3>"
+ call assert_equal([[0, 3, 1, 0], [0, 3, 2, 0]], g:VisualStartPos)
+ let g:VisualStartPos = []
+ exe "normal 1Gv$\<F3>o\<F3>"
+ call assert_equal([[0, 1, 1, 0], [0, 1, 1, 0]], g:VisualStartPos)
+ vunmap <F3>
+
+ " Test for getting the position in insert mode with the cursor after the
+ " last character in a line
+ inoremap <expr> <F3> SaveInsertCurrentCharPos()
+ let g:InsertCurrentPos = []
+ exe "normal 1GA\<F3>"
+ exe "normal 2GA\<F3>"
+ exe "normal 3GA\<F3>"
+ exe "normal 4GA\<F3>"
+ exe "normal 2G6li\<F3>"
+ call assert_equal([[0, 1, 1, 0], [0, 2, 10, 0], [0, 3, 2, 0], [0, 4, 10, 0],
+ \ [0, 2, 7, 0]], g:InsertCurrentPos)
+ iunmap <F3>
+
+ %bw!
+endfunc
+
+" Test for the setcharpos() function
+func Test_setcharpos()
+ call assert_equal(-1, setcharpos('.', v:_null_list))
+ new
+ call setline(1, ['', "01\tà4è678", 'Ⅵ', '012345678'])
+ call setcharpos('.', [0, 1, 1, 0])
+ call assert_equal([1, 1], [line('.'), col('.')])
+ call setcharpos('.', [0, 2, 7, 0])
+ call assert_equal([2, 9], [line('.'), col('.')])
+ call setcharpos('.', [0, 3, 4, 0])
+ call assert_equal([3, 1], [line('.'), col('.')])
+ call setcharpos('.', [0, 3, 1, 0])
+ call assert_equal([3, 1], [line('.'), col('.')])
+ call setcharpos('.', [0, 4, 0, 0])
+ call assert_equal([4, 1], [line('.'), col('.')])
+ call setcharpos('.', [0, 4, 20, 0])
+ call assert_equal([4, 9], [line('.'), col('.')])
+
+ " Test for mark
+ delmarks m
+ call setcharpos("'m", [0, 2, 9, 0])
+ normal `m
+ call assert_equal([2, 11], [line('.'), col('.')])
+ " unload the buffer and try to set the mark
+ let bnr = bufnr()
+ enew!
+ call assert_equal(-1, setcharpos("'m", [bnr, 2, 2, 0]))
+
+ %bw!
+ call assert_equal(-1, setcharpos('.', [10, 3, 1, 0]))
+endfunc
+
+func SaveVisualStartCharCol()
+ call add(g:VisualStartCol, charcol('v'))
+ return ''
+endfunc
+
+func SaveInsertCurrentCharCol()
+ call add(g:InsertCurrentCol, charcol('.'))
+ return ''
+endfunc
+
+" Test for the charcol() function
+func Test_charcol()
+ call assert_fails('call charcol({})', 'E731:')
+ call assert_equal(0, charcol(0))
+ new
+ call setline(1, ['', "01\tà4è678", 'Ⅵ', '012345678'])
+
+ " Test for '.' and '$'
+ normal 1G
+ call assert_equal(1, charcol('.'))
+ call assert_equal(1, charcol('$'))
+ normal 2G6l
+ call assert_equal(7, charcol('.'))
+ call assert_equal(10, charcol('$'))
+ normal 3G$
+ call assert_equal(1, charcol('.'))
+ call assert_equal(2, charcol('$'))
+ normal 4G$
+ call assert_equal(9, charcol('.'))
+ call assert_equal(10, charcol('$'))
+
+ " Test for [lnum, '$']
+ call assert_equal(1, charcol([1, '$']))
+ call assert_equal(10, charcol([2, '$']))
+ call assert_equal(2, charcol([3, '$']))
+ call assert_equal(0, charcol([5, '$']))
+
+ " Test for a mark
+ normal 2G7lmmgg
+ call assert_equal(8, charcol("'m"))
+ delmarks m
+ call assert_equal(0, charcol("'m"))
+
+ " Test for the visual start column
+ vnoremap <expr> <F3> SaveVisualStartCharCol()
+ let g:VisualStartCol = []
+ exe "normal 2G6lv$\<F3>ohh\<F3>o\<F3>"
+ call assert_equal([7, 10, 5], g:VisualStartCol)
+ call assert_equal(9, charcol('v'))
+ let g:VisualStartCol = []
+ exe "normal 3Gv$\<F3>o\<F3>"
+ call assert_equal([1, 2], g:VisualStartCol)
+ let g:VisualStartCol = []
+ exe "normal 1Gv$\<F3>o\<F3>"
+ call assert_equal([1, 1], g:VisualStartCol)
+ vunmap <F3>
+
+ " Test for getting the column number in insert mode with the cursor after
+ " the last character in a line
+ inoremap <expr> <F3> SaveInsertCurrentCharCol()
+ let g:InsertCurrentCol = []
+ exe "normal 1GA\<F3>"
+ exe "normal 2GA\<F3>"
+ exe "normal 3GA\<F3>"
+ exe "normal 4GA\<F3>"
+ exe "normal 2G6li\<F3>"
+ call assert_equal([1, 10, 2, 10, 7], g:InsertCurrentCol)
+ iunmap <F3>
+
+ %bw!
+endfunc
+
+func SaveInsertCursorCharPos()
+ call add(g:InsertCursorPos, getcursorcharpos('.'))
+ return ''
+endfunc
+
+" Test for getcursorcharpos()
+func Test_getcursorcharpos()
+ call assert_equal(getcursorcharpos(), getcursorcharpos(0))
+ call assert_equal([0, 0, 0, 0, 0], getcursorcharpos(-1))
+ call assert_equal([0, 0, 0, 0, 0], getcursorcharpos(1999))
+
+ new
+ call setline(1, ['', "01\tà4è678", 'Ⅵ', '012345678'])
+ normal 1G9l
+ call assert_equal([0, 1, 1, 0, 1], getcursorcharpos())
+ normal 2G9l
+ call assert_equal([0, 2, 9, 0, 14], getcursorcharpos())
+ normal 3G9l
+ call assert_equal([0, 3, 1, 0, 1], getcursorcharpos())
+ normal 4G9l
+ call assert_equal([0, 4, 9, 0, 9], getcursorcharpos())
+
+ " Test for getting the cursor position in insert mode with the cursor after
+ " the last character in a line
+ inoremap <expr> <F3> SaveInsertCursorCharPos()
+ let g:InsertCursorPos = []
+ exe "normal 1GA\<F3>"
+ exe "normal 2GA\<F3>"
+ exe "normal 3GA\<F3>"
+ exe "normal 4GA\<F3>"
+ exe "normal 2G6li\<F3>"
+ call assert_equal([[0, 1, 1, 0, 1], [0, 2, 10, 0, 15], [0, 3, 2, 0, 2],
+ \ [0, 4, 10, 0, 10], [0, 2, 7, 0, 12]], g:InsertCursorPos)
+ iunmap <F3>
+
+ let winid = win_getid()
+ normal 2G5l
+ wincmd w
+ call assert_equal([0, 2, 6, 0, 11], getcursorcharpos(winid))
+ %bw!
+endfunc
+
+" Test for setcursorcharpos()
+func Test_setcursorcharpos()
+ call assert_fails('call setcursorcharpos(v:_null_list)', 'E474:')
+ call assert_fails('call setcursorcharpos([1])', 'E474:')
+ call assert_fails('call setcursorcharpos([1, 1, 1, 1, 1])', 'E474:')
+ new
+ call setline(1, ['', "01\tà4è678", 'Ⅵ', '012345678'])
+ normal G
+ call setcursorcharpos([1, 1])
+ call assert_equal([1, 1], [line('.'), col('.')])
+ call setcursorcharpos([2, 7, 0])
+ call assert_equal([2, 9], [line('.'), col('.')])
+ call setcursorcharpos(3, 4)
+ call assert_equal([3, 1], [line('.'), col('.')])
+ call setcursorcharpos([3, 1])
+ call assert_equal([3, 1], [line('.'), col('.')])
+ call setcursorcharpos([4, 0, 0, 0])
+ call assert_equal([4, 1], [line('.'), col('.')])
+ call setcursorcharpos([4, 20])
+ call assert_equal([4, 9], [line('.'), col('.')])
+ normal 1G
+ call setcursorcharpos([100, 100, 100, 100])
+ call assert_equal([4, 9], [line('.'), col('.')])
+ normal 1G
+ call setcursorcharpos('$', 1)
+ call assert_equal([4, 1], [line('.'), col('.')])
+
+ %bw!
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_cursorline.vim b/src/nvim/testdir/test_cursorline.vim
index 39d8b901ed..e85e9304a3 100644
--- a/src/nvim/testdir/test_cursorline.vim
+++ b/src/nvim/testdir/test_cursorline.vim
@@ -268,4 +268,87 @@ END
call delete('Xtextfile')
endfunc
+func Test_cursorline_callback()
+ CheckScreendump
+ CheckFeature timers
+
+ let lines =<< trim END
+ call setline(1, ['aaaaa', 'bbbbb', 'ccccc', 'ddddd'])
+ set cursorline
+ call cursor(4, 1)
+
+ func Func(timer)
+ call cursor(2, 1)
+ endfunc
+
+ call timer_start(300, 'Func')
+ END
+ call writefile(lines, 'Xcul_timer')
+
+ let buf = RunVimInTerminal('-S Xcul_timer', #{rows: 8})
+ call TermWait(buf, 310)
+ call VerifyScreenDump(buf, 'Test_cursorline_callback_1', {})
+
+ call StopVimInTerminal(buf)
+ call delete('Xcul_timer')
+endfunc
+
+func Test_cursorline_screenline_update()
+ CheckScreendump
+
+ let lines =<< trim END
+ call setline(1, repeat('xyz ', 30))
+ set cursorline cursorlineopt=screenline
+ inoremap <F2> <Cmd>call cursor(1, 1)<CR>
+ END
+ call writefile(lines, 'Xcul_screenline')
+
+ let buf = RunVimInTerminal('-S Xcul_screenline', #{rows: 8})
+ call term_sendkeys(buf, "A")
+ call VerifyScreenDump(buf, 'Test_cursorline_screenline_1', {})
+ call term_sendkeys(buf, "\<F2>")
+ call VerifyScreenDump(buf, 'Test_cursorline_screenline_2', {})
+ call term_sendkeys(buf, "\<Esc>")
+
+ call StopVimInTerminal(buf)
+ call delete('Xcul_screenline')
+endfunc
+
+func Test_cursorline_cursorbind_horizontal_scroll()
+ CheckScreendump
+
+ let lines =<< trim END
+ call setline(1, 'aa bb cc dd ee ff gg hh ii jj kk ll mm' ..
+ \ ' nn oo pp qq rr ss tt uu vv ww xx yy zz')
+ set nowrap
+ " The following makes the cursor apparent on the screen dump
+ set sidescroll=1 cursorcolumn
+ " add empty lines, required for cursorcolumn
+ call append(1, ['','','',''])
+ 20vsp
+ windo :set cursorbind
+ END
+ call writefile(lines, 'Xhor_scroll')
+
+ let buf = RunVimInTerminal('-S Xhor_scroll', #{rows: 8})
+ call term_sendkeys(buf, "20l")
+ call VerifyScreenDump(buf, 'Test_hor_scroll_1', {})
+ call term_sendkeys(buf, "10l")
+ call VerifyScreenDump(buf, 'Test_hor_scroll_2', {})
+ call term_sendkeys(buf, ":windo :set cursorline\<cr>")
+ call term_sendkeys(buf, "0")
+ call term_sendkeys(buf, "20l")
+ call VerifyScreenDump(buf, 'Test_hor_scroll_3', {})
+ call term_sendkeys(buf, "10l")
+ call VerifyScreenDump(buf, 'Test_hor_scroll_4', {})
+ call term_sendkeys(buf, ":windo :set nocursorline nocursorcolumn\<cr>")
+ call term_sendkeys(buf, "0")
+ call term_sendkeys(buf, "40l")
+ call VerifyScreenDump(buf, 'Test_hor_scroll_5', {})
+
+ call StopVimInTerminal(buf)
+ call delete('Xhor_scroll')
+endfunc
+
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_debugger.vim b/src/nvim/testdir/test_debugger.vim
index a396efc09e..e038c0096a 100644
--- a/src/nvim/testdir/test_debugger.vim
+++ b/src/nvim/testdir/test_debugger.vim
@@ -976,7 +976,7 @@ func Test_debug_backtrace_level()
\ 'line 1: let s:file1_var = ''file1'''
\ ])
- " step throught the initial declarations
+ " step through the initial declarations
call RunDbgCmd(buf, 'step', [ 'line 2: let g:global_var = ''global''' ] )
call RunDbgCmd(buf, 'step', [ 'line 4: func s:File1Func( arg )' ] )
call RunDbgCmd(buf, 'echo s:file1_var', [ 'file1' ] )
diff --git a/src/nvim/testdir/test_delete.vim b/src/nvim/testdir/test_delete.vim
new file mode 100644
index 0000000000..b23a3bd025
--- /dev/null
+++ b/src/nvim/testdir/test_delete.vim
@@ -0,0 +1,114 @@
+" Test for delete().
+
+func Test_file_delete()
+ split Xfile
+ call setline(1, ['a', 'b'])
+ wq
+ call assert_equal(['a', 'b'], readfile('Xfile'))
+ call assert_equal(0, delete('Xfile'))
+ call assert_fails('call readfile("Xfile")', 'E484:')
+ call assert_equal(-1, delete('Xfile'))
+ bwipe Xfile
+endfunc
+
+func Test_dir_delete()
+ call mkdir('Xdir1')
+ call assert_true(isdirectory('Xdir1'))
+ call assert_equal(0, delete('Xdir1', 'd'))
+ call assert_false(isdirectory('Xdir1'))
+ call assert_equal(-1, delete('Xdir1', 'd'))
+endfunc
+
+func Test_recursive_delete()
+ call mkdir('Xdir1')
+ call mkdir('Xdir1/subdir')
+ call mkdir('Xdir1/empty')
+ split Xdir1/Xfile
+ call setline(1, ['a', 'b'])
+ w
+ w Xdir1/subdir/Xfile
+ close
+ call assert_true(isdirectory('Xdir1'))
+ call assert_equal(['a', 'b'], readfile('Xdir1/Xfile'))
+ call assert_true(isdirectory('Xdir1/subdir'))
+ call assert_equal(['a', 'b'], readfile('Xdir1/subdir/Xfile'))
+ call assert_true('Xdir1/empty'->isdirectory())
+ call assert_equal(0, delete('Xdir1', 'rf'))
+ call assert_false(isdirectory('Xdir1'))
+ call assert_equal(-1, delete('Xdir1', 'd'))
+ bwipe Xdir1/Xfile
+ bwipe Xdir1/subdir/Xfile
+endfunc
+
+func Test_symlink_delete()
+ if !has('unix')
+ return
+ endif
+ split Xfile
+ call setline(1, ['a', 'b'])
+ wq
+ silent !ln -s Xfile Xlink
+ " Delete the link, not the file
+ call assert_equal(0, delete('Xlink'))
+ call assert_equal(-1, delete('Xlink'))
+ call assert_equal(0, delete('Xfile'))
+ bwipe Xfile
+endfunc
+
+func Test_symlink_dir_delete()
+ if !has('unix')
+ return
+ endif
+ call mkdir('Xdir1')
+ silent !ln -s Xdir1 Xlink
+ call assert_true(isdirectory('Xdir1'))
+ call assert_true(isdirectory('Xlink'))
+ " Delete the link, not the directory
+ call assert_equal(0, delete('Xlink'))
+ call assert_equal(-1, delete('Xlink'))
+ call assert_equal(0, delete('Xdir1', 'd'))
+endfunc
+
+func Test_symlink_recursive_delete()
+ if !has('unix')
+ return
+ endif
+ call mkdir('Xdir3')
+ call mkdir('Xdir3/subdir')
+ call mkdir('Xdir4')
+ split Xdir3/Xfile
+ call setline(1, ['a', 'b'])
+ w
+ w Xdir3/subdir/Xfile
+ w Xdir4/Xfile
+ close
+ silent !ln -s ../Xdir4 Xdir3/Xlink
+
+ call assert_true(isdirectory('Xdir3'))
+ call assert_equal(['a', 'b'], readfile('Xdir3/Xfile'))
+ call assert_true(isdirectory('Xdir3/subdir'))
+ call assert_equal(['a', 'b'], readfile('Xdir3/subdir/Xfile'))
+ call assert_true(isdirectory('Xdir4'))
+ call assert_true(isdirectory('Xdir3/Xlink'))
+ call assert_equal(['a', 'b'], readfile('Xdir4/Xfile'))
+
+ call assert_equal(0, delete('Xdir3', 'rf'))
+ call assert_false(isdirectory('Xdir3'))
+ call assert_equal(-1, delete('Xdir3', 'd'))
+ " symlink is deleted, not the directory it points to
+ call assert_true(isdirectory('Xdir4'))
+ call assert_equal(['a', 'b'], readfile('Xdir4/Xfile'))
+ call assert_equal(0, delete('Xdir4/Xfile'))
+ call assert_equal(0, delete('Xdir4', 'd'))
+
+ bwipe Xdir3/Xfile
+ bwipe Xdir3/subdir/Xfile
+ bwipe Xdir4/Xfile
+endfunc
+
+func Test_delete_errors()
+ call assert_fails('call delete('''')', 'E474:')
+ call assert_fails('call delete(''foo'', 0)', 'E15:')
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim
index 3a0c615cf6..8c20c647f1 100644
--- a/src/nvim/testdir/test_diffmode.vim
+++ b/src/nvim/testdir/test_diffmode.vim
@@ -540,7 +540,7 @@ func Test_diffopt_hiddenoff()
bwipe!
bwipe!
- set nohidden diffopt&
+ set hidden& diffopt&
endfunc
func Test_diffoff_hidden()
@@ -577,7 +577,7 @@ func Test_diffoff_hidden()
bwipe!
bwipe!
- set nohidden diffopt&
+ set hidden& diffopt&
endfunc
func Test_setting_cursor()
@@ -1017,6 +1017,32 @@ func Test_diff_with_cursorline()
call delete('Xtest_diff_cursorline')
endfunc
+func Test_diff_with_cursorline_number()
+ CheckScreendump
+
+ let lines =<< trim END
+ hi CursorLine ctermbg=red ctermfg=white
+ hi CursorLineNr ctermbg=white ctermfg=black cterm=underline
+ set cursorline number
+ call setline(1, ["baz", "foo", "foo", "bar"])
+ 2
+ vnew
+ call setline(1, ["foo", "foo", "bar"])
+ windo diffthis
+ 1wincmd w
+ END
+ call writefile(lines, 'Xtest_diff_cursorline_number')
+ let buf = RunVimInTerminal('-S Xtest_diff_cursorline_number', {})
+
+ call VerifyScreenDump(buf, 'Test_diff_with_cursorline_number_01', {})
+ call term_sendkeys(buf, ":set cursorlineopt=number\r")
+ call VerifyScreenDump(buf, 'Test_diff_with_cursorline_number_02', {})
+
+ " clean up
+ call StopVimInTerminal(buf)
+ call delete('Xtest_diff_cursorline_number')
+endfunc
+
func Test_diff_with_cursorline_breakindent()
CheckScreendump
@@ -1146,6 +1172,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
@@ -1240,4 +1295,41 @@ func Test_diff_filler_cursorcolumn()
endfunc
+func Test_diff_binary()
+ CheckScreendump
+
+ let content =<< trim END
+ call setline(1, ['a', 'b', "c\n", 'd', 'e', 'f', 'g'])
+ vnew
+ call setline(1, ['A', 'b', 'c', 'd', 'E', 'f', 'g'])
+ windo diffthis
+ wincmd p
+ norm! gg0
+ redraw!
+ END
+ call writefile(content, 'Xtest_diff_bin')
+ let buf = RunVimInTerminal('-S Xtest_diff_bin', {})
+
+ " Test using internal diff
+ call VerifyScreenDump(buf, 'Test_diff_bin_01', {})
+
+ " Test using internal diff and case folding
+ call term_sendkeys(buf, ":set diffopt+=icase\<cr>")
+ call term_sendkeys(buf, "\<C-l>")
+ call VerifyScreenDump(buf, 'Test_diff_bin_02', {})
+ " Test using external diff
+ call term_sendkeys(buf, ":set diffopt=filler\<cr>")
+ call term_sendkeys(buf, "\<C-l>")
+ call VerifyScreenDump(buf, 'Test_diff_bin_03', {})
+ " Test using external diff and case folding
+ call term_sendkeys(buf, ":set diffopt=filler,icase\<cr>")
+ call term_sendkeys(buf, "\<C-l>")
+ call VerifyScreenDump(buf, 'Test_diff_bin_04', {})
+
+ " clean up
+ call StopVimInTerminal(buf)
+ call delete('Xtest_diff_bin')
+ set diffopt&vim
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_digraph.vim b/src/nvim/testdir/test_digraph.vim
index d23748a3e3..acc34e5e7c 100644
--- a/src/nvim/testdir/test_digraph.vim
+++ b/src/nvim/testdir/test_digraph.vim
@@ -81,7 +81,7 @@ func Test_digraphs()
call Put_Dig(".e")
call Put_Dig("a.") " not defined
call assert_equal(['ḃ', 'ė', '.'], getline(line('.')-2,line('.')))
- " Diaresis
+ " Diaeresis
call Put_Dig("a:")
call Put_Dig(":u")
call Put_Dig("b:") " not defined
@@ -212,7 +212,7 @@ func Test_digraphs()
call Put_Dig("el")
call assert_equal(['␀', 'ü', '∞', 'l'], getline(line('.')-3,line('.')))
call assert_fails('digraph xy z', 'E39:')
- call assert_fails('digraph x', 'E474:')
+ call assert_fails('digraph x', 'E1214:')
bw!
endfunc
@@ -288,7 +288,7 @@ func Test_digraphs_option()
call Put_Dig_BS(".","e")
call Put_Dig_BS("a",".") " not defined
call assert_equal(['ḃ', 'ė', '.'], getline(line('.')-2,line('.')))
- " Diaresis
+ " Diaeresis
call Put_Dig_BS("a",":")
call Put_Dig_BS(":","u")
call Put_Dig_BS("b",":") " not defined
@@ -466,10 +466,12 @@ endfunc
func Test_digraph_cmndline()
" Create digraph on commandline
- " This is a hack, to let Vim create the digraph in commandline mode
- let s = ''
- exe "sil! norm! :let s.='\<c-k>Eu'\<cr>"
- call assert_equal("€", s)
+ call feedkeys(":\"\<c-k>Eu\<cr>", 'xt')
+ call assert_equal('"€', @:)
+
+ " Canceling a CTRL-K on the cmdline
+ call feedkeys(":\"a\<c-k>\<esc>b\<cr>", 'xt')
+ call assert_equal('"ab', @:)
endfunc
func Test_show_digraph()
@@ -505,4 +507,85 @@ func Test_entering_digraph()
call StopVimInTerminal(buf)
endfunc
+func Test_digraph_set_function()
+ new
+ call digraph_set('aa', 'あ')
+ call Put_Dig('aa')
+ call assert_equal('あ', getline('$'))
+ call digraph_set(' i', 'い')
+ call Put_Dig(' i')
+ call assert_equal('い', getline('$'))
+ call digraph_set(' ', 'う')
+ call Put_Dig(' ')
+ call assert_equal('う', getline('$'))
+
+ eval 'aa'->digraph_set('え')
+ call Put_Dig('aa')
+ call assert_equal('え', getline('$'))
+
+ call assert_fails('call digraph_set("aaa", "あ")', 'E1214: Digraph must be just two characters: aaa')
+ call assert_fails('call digraph_set("b", "あ")', 'E1214: Digraph must be just two characters: b')
+ call assert_fails('call digraph_set("あ", "あ")', 'E1214: Digraph must be just two characters: あ')
+ call assert_fails('call digraph_set("aa", "ああ")', 'E1215: Digraph must be one character: ああ')
+ call assert_fails('call digraph_set("aa", "か" .. nr2char(0x3099))', 'E1215: Digraph must be one character: か' .. nr2char(0x3099))
+ bwipe!
+endfunc
+
+func Test_digraph_get_function()
+ " Built-in digraphs
+ call assert_equal('∞', digraph_get('00'))
+
+ " User-defined digraphs
+ call digraph_set('aa', 'あ')
+ call digraph_set(' i', 'い')
+ call digraph_set(' ', 'う')
+ call assert_equal('あ', digraph_get('aa'))
+ call assert_equal('あ', 'aa'->digraph_get())
+ call assert_equal('い', digraph_get(' i'))
+ call assert_equal('う', digraph_get(' '))
+ call assert_fails('call digraph_get("aaa")', 'E1214: Digraph must be just two characters: aaa')
+ call assert_fails('call digraph_get("b")', 'E1214: Digraph must be just two characters: b')
+endfunc
+
+func Test_digraph_get_function_encode()
+ throw 'Skipped: Nvim does not support setting encoding=japan'
+ CheckFeature iconv
+
+ let testcases = {
+ \'00': '∞',
+ \'aa': 'あ',
+ \}
+ for [key, ch] in items(testcases)
+ call digraph_set(key, ch)
+ set encoding=japan
+ call assert_equal(iconv(ch, 'utf-8', 'japan'), digraph_get(key))
+ set encoding=utf-8
+ endfor
+endfunc
+
+func Test_digraph_setlist_function()
+ call digraph_setlist([['aa', 'き'], ['bb', 'く']])
+ call assert_equal('き', digraph_get('aa'))
+ call assert_equal('く', digraph_get('bb'))
+
+ call assert_fails('call digraph_setlist([[]])', 'E1216:')
+ call assert_fails('call digraph_setlist([["aa", "b", "cc"]])', '1216:')
+ call assert_fails('call digraph_setlist([["あ", "あ"]])', 'E1214: Digraph must be just two characters: あ')
+endfunc
+
+func Test_digraph_getlist_function()
+ " Make sure user-defined digraphs are defined
+ call digraph_setlist([['aa', 'き'], ['bb', 'く']])
+
+ for pair in digraph_getlist(1)
+ call assert_equal(digraph_get(pair[0]), pair[1])
+ endfor
+
+ " We don't know how many digraphs are registered before, so check the number
+ " of digraphs returned.
+ call assert_equal(digraph_getlist()->len(), digraph_getlist(0)->len())
+ call assert_notequal((digraph_getlist()->len()), digraph_getlist(1)->len())
+endfunc
+
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_display.vim b/src/nvim/testdir/test_display.vim
index c2a9683f7c..6938abbc28 100644
--- a/src/nvim/testdir/test_display.vim
+++ b/src/nvim/testdir/test_display.vim
@@ -41,7 +41,7 @@ func Test_display_foldcolumn()
quit!
endfunc
-func! Test_display_foldtext_mbyte()
+func Test_display_foldtext_mbyte()
CheckFeature folding
call NewWindow(10, 40)
@@ -263,6 +263,27 @@ func Test_display_scroll_at_topline()
call StopVimInTerminal(buf)
endfunc
+func Test_display_scroll_update_visual()
+ CheckScreendump
+
+ let lines =<< trim END
+ set scrolloff=0
+ call setline(1, repeat(['foo'], 10))
+ call sign_define('foo', { 'text': '>' })
+ call sign_place(1, 'bar', 'foo', bufnr(), { 'lnum': 2 })
+ call sign_place(2, 'bar', 'foo', bufnr(), { 'lnum': 1 })
+ autocmd CursorMoved * if getcurpos()[1] == 2 | call sign_unplace('bar', { 'id': 1 }) | endif
+ END
+ call writefile(lines, 'XupdateVisual.vim')
+
+ let buf = RunVimInTerminal('-S XupdateVisual.vim', #{rows: 8, cols: 60})
+ call term_sendkeys(buf, "VG7kk")
+ call VerifyScreenDump(buf, 'Test_display_scroll_update_visual', {})
+
+ call StopVimInTerminal(buf)
+ call delete('XupdateVisual.vim')
+endfunc
+
" Test for 'eob' (EndOfBuffer) item in 'fillchars'
func Test_eob_fillchars()
" default value (skipped)
@@ -304,4 +325,31 @@ func Test_display_linebreak_breakat()
let &breakat=_breakat
endfunc
+func Test_display_lastline()
+ CheckScreendump
+
+ let lines =<< trim END
+ call setline(1, ['aaa', 'b'->repeat(100)])
+ set display=truncate
+ vsplit
+ 100wincmd <
+ END
+ call writefile(lines, 'XdispLastline')
+ let buf = RunVimInTerminal('-S XdispLastline', #{rows: 10})
+ call VerifyScreenDump(buf, 'Test_display_lastline_1', {})
+
+ call term_sendkeys(buf, ":set display=lastline\<CR>")
+ call VerifyScreenDump(buf, 'Test_display_lastline_2', {})
+
+ call term_sendkeys(buf, ":100wincmd >\<CR>")
+ call VerifyScreenDump(buf, 'Test_display_lastline_3', {})
+
+ call term_sendkeys(buf, ":set display=truncate\<CR>")
+ call VerifyScreenDump(buf, 'Test_display_lastline_4', {})
+
+ call StopVimInTerminal(buf)
+ call delete('XdispLastline')
+endfunc
+
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim
index fc4e80f0d6..42c77518e4 100644
--- a/src/nvim/testdir/test_edit.vim
+++ b/src/nvim/testdir/test_edit.vim
@@ -16,8 +16,9 @@ func Test_edit_00b()
call setline(1, ['abc '])
inoreabbr <buffer> h here some more
call cursor(1, 4)
- " <c-l> expands the abbreviation and ends insertmode
- call feedkeys(":set im\<cr> h\<c-l>:set noim\<cr>", 'tix')
+ " <esc> expands the abbreviation and ends insert mode
+ " call feedkeys(":set im\<cr> h\<c-l>:set noim\<cr>", 'tix')
+ call feedkeys("i h\<esc>", 'tix')
call assert_equal(['abc here some more '], getline(1,'$'))
iunabbr <buffer> h
bw!
@@ -213,7 +214,7 @@ func Test_edit_07()
bw!
endfunc
-func! Test_edit_08()
+func Test_edit_08()
throw 'skipped: moved to test/functional/legacy/edit_spec.lua'
" reset insertmode from i_ctrl-r_=
let g:bufnr = bufnr('%')
@@ -234,15 +235,18 @@ func Test_edit_09()
call setline(1, ['abc', 'def', 'ghi'])
call cursor(1, 1)
" 1) CTRL-\ CTLR-N
- call feedkeys(":set im\<cr>\<c-\>\<c-n>ccABC\<c-l>", 'txin')
+ " call feedkeys(":set im\<cr>\<c-\>\<c-n>ccABC\<c-l>", 'txin')
+ call feedkeys("i\<c-\>\<c-n>ccABC\<esc>", 'txin')
call assert_equal(['ABC', 'def', 'ghi'], getline(1,'$'))
call setline(1, ['ABC', 'def', 'ghi'])
" 2) CTRL-\ CTLR-G
- call feedkeys("j0\<c-\>\<c-g>ZZZ\<cr>\<c-l>", 'txin')
- call assert_equal(['ABC', 'ZZZ', 'def', 'ghi'], getline(1,'$'))
- call feedkeys("I\<c-\>\<c-g>YYY\<c-l>", 'txin')
- call assert_equal(['ABC', 'ZZZ', 'YYYdef', 'ghi'], getline(1,'$'))
- set noinsertmode
+ " CTRL-\ CTRL-G goes to Insert mode when 'insertmode' is set, but
+ " 'insertmode' is now removed so skip this test
+ " call feedkeys("j0\<c-\>\<c-g>ZZZ\<cr>\<esc>", 'txin')
+ " call assert_equal(['ABC', 'ZZZ', 'def', 'ghi'], getline(1,'$'))
+ " call feedkeys("I\<c-\>\<c-g>YYY\<c-l>", 'txin')
+ " call assert_equal(['ABC', 'ZZZ', 'YYYdef', 'ghi'], getline(1,'$'))
+ " set noinsertmode
" 3) CTRL-\ CTRL-O
call setline(1, ['ABC', 'ZZZ', 'def', 'ghi'])
call cursor(1, 1)
@@ -266,6 +270,10 @@ func Test_edit_10()
call cursor(1, 4)
call feedkeys("A\<s-home>start\<esc>", 'txin')
call assert_equal(['startdef', 'ghi'], getline(1, '$'))
+ " start select mode again with gv
+ set selectmode=cmd
+ call feedkeys('gvabc', 'xt')
+ call assert_equal('abctdef', getline(1))
set selectmode= keymodel=
bw!
endfunc
@@ -278,7 +286,7 @@ func Test_edit_11()
call cursor(2, 1)
call feedkeys("i\<c-f>int c;\<esc>", 'tnix')
call cursor(3, 1)
- call feedkeys("i/* comment */", 'tnix')
+ call feedkeys("\<Insert>/* comment */", 'tnix')
call assert_equal(['{', "\<tab>int c;", "/* comment */"], getline(1, '$'))
" added changed cindentkeys slightly
set cindent cinkeys+=*/
@@ -341,8 +349,8 @@ func Test_edit_11_indentexpr()
bw!
endfunc
+" Test changing indent in replace mode
func Test_edit_12()
- " Test changing indent in replace mode
new
call setline(1, ["\tabc", "\tdef"])
call cursor(2, 4)
@@ -381,29 +389,27 @@ func Test_edit_12()
call feedkeys("R\<c-t>\<c-t>", 'tnix')
call assert_equal(["\tabc", "\t\t\tdef"], getline(1, '$'))
call assert_equal([0, 2, 2, 0], getpos('.'))
- set et
- set sw& et&
+ set sw&
+
+ " In replace mode, after hitting enter in a line with tab characters,
+ " pressing backspace should restore the tab characters.
%d
- call setline(1, ["\t/*"])
- set formatoptions=croql
- call cursor(1, 3)
- call feedkeys("A\<cr>\<cr>/", 'tnix')
- call assert_equal(["\t/*", " *", " */"], getline(1, '$'))
- set formatoptions&
+ setlocal autoindent backspace=2
+ call setline(1, "\tone\t\ttwo")
+ exe "normal ggRred\<CR>six" .. repeat("\<BS>", 8)
+ call assert_equal(["\tone\t\ttwo"], getline(1, '$'))
bw!
endfunc
func Test_edit_13()
" Test smartindenting
- if exists("+smartindent")
- new
- set smartindent autoindent
- call setline(1, ["\tabc"])
- call feedkeys("A {\<cr>more\<cr>}\<esc>", 'tnix')
- call assert_equal(["\tabc {", "\t\tmore", "\t}"], getline(1, '$'))
- set smartindent& autoindent&
- bwipe!
- endif
+ new
+ set smartindent autoindent
+ call setline(1, ["\tabc"])
+ call feedkeys("A {\<cr>more\<cr>}\<esc>", 'tnix')
+ call assert_equal(["\tabc {", "\t\tmore", "\t}"], getline(1, '$'))
+ set smartindent& autoindent&
+ bwipe!
" Test autoindent removing indent of blank line.
new
@@ -414,16 +420,49 @@ func Test_edit_13()
call assert_equal("", getline(2))
call assert_equal(" baz", getline(3))
set autoindent&
+
+ " pressing <C-U> to erase line should keep the indent with 'autoindent'
+ set backspace=2 autoindent
+ %d
+ exe "normal i\tone\<CR>three\<C-U>two"
+ call assert_equal(["\tone", "\ttwo"], getline(1, '$'))
+ set backspace& autoindent&
+
bwipe!
endfunc
-func! Test_edit_CR()
+" Test for autoindent removing indent when insert mode is stopped. Some parts
+" of the code is exercised only when interactive mode is used. So use Vim in a
+" terminal.
+func Test_autoindent_remove_indent()
+ CheckRunVimInTerminal
+ let buf = RunVimInTerminal('-N Xfile', {'rows': 6, 'cols' : 20})
+ call TermWait(buf)
+ call term_sendkeys(buf, ":set autoindent\n")
+ " leaving insert mode in a new line with indent added by autoindent, should
+ " remove the indent.
+ call term_sendkeys(buf, "i\<Tab>foo\<CR>\<Esc>")
+ " Need to delay for sometime, otherwise the code in getchar.c will not be
+ " exercised.
+ call TermWait(buf, 50)
+ " when a line is wrapped and the cursor is at the start of the second line,
+ " leaving insert mode, should move the cursor back to the first line.
+ call term_sendkeys(buf, "o" .. repeat('x', 20) .. "\<Esc>")
+ " Need to delay for sometime, otherwise the code in getchar.c will not be
+ " exercised.
+ call TermWait(buf, 50)
+ call term_sendkeys(buf, ":w\n")
+ call TermWait(buf)
+ call StopVimInTerminal(buf)
+ call assert_equal(["\tfoo", '', repeat('x', 20)], readfile('Xfile'))
+ call delete('Xfile')
+endfunc
+
+func Test_edit_CR()
" Test for <CR> in insert mode
" basically only in quickfix mode ist tested, the rest
" has been taken care of by other tests
- if !has("quickfix")
- return
- endif
+ CheckFeature quickfix
botright new
call writefile(range(1, 10), 'Xqflist.txt')
call setqflist([{'filename': 'Xqflist.txt', 'lnum': 2}])
@@ -450,7 +489,7 @@ func! Test_edit_CR()
call delete('Xqflist.txt')
endfunc
-func! Test_edit_CTRL_()
+func Test_edit_CTRL_()
" disabled for Windows builds, why?
if !has("rightleft") || has("win32")
return
@@ -590,7 +629,7 @@ func Test_edit_CTRL_K()
call feedkeys("A\<c-x>\<c-k>\<down>\<down>\<down>\<down>\<cr>\<esc>", 'tnix')
call assert_equal(['AA'], getline(1, '$'))
- " press an unexecpted key after dictionary completion
+ " press an unexpected key after dictionary completion
%d
call setline(1, 'A')
call cursor(1, 1)
@@ -734,7 +773,7 @@ func Test_edit_CTRL_O()
bw!
endfunc
-func! Test_edit_CTRL_R()
+func Test_edit_CTRL_R()
" Insert Register
new
" call test_override("ALL", 1)
@@ -979,6 +1018,22 @@ func Test_edit_CTRL_U()
bw!
endfunc
+func Test_edit_completefunc_delete()
+ func CompleteFunc(findstart, base)
+ if a:findstart == 1
+ return col('.') - 1
+ endif
+ normal dd
+ return ['a', 'b']
+ endfunc
+ new
+ set completefunc=CompleteFunc
+ call setline(1, ['', 'abcd', ''])
+ 2d
+ call assert_fails("normal 2G$a\<C-X>\<C-U>", 'E565:')
+ bwipe!
+endfunc
+
func Test_edit_CTRL_Z()
" Ctrl-Z when insertmode is not set inserts it literally
new
@@ -1006,16 +1061,14 @@ func Test_edit_DROP()
endfunc
func Test_edit_CTRL_V()
- if has("ebcdic")
- return
- endif
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,15 +1081,27 @@ 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
func Test_edit_F1()
" Pressing <f1>
new
- call feedkeys(":set im\<cr>\<f1>\<c-l>", 'tnix')
+ " call feedkeys(":set im\<cr>\<f1>\<c-l>", 'tnix')
+ call feedkeys("i\<f1>\<esc>", 'tnix')
set noinsertmode
call assert_equal('help', &buftype)
bw
@@ -1132,26 +1197,38 @@ endfunc
func Test_edit_MOUSE()
" This is a simple test, since we not really using the mouse here
- if !has("mouse")
- return
- endif
+ CheckFeature mouse
10new
call setline(1, range(1, 100))
call cursor(1, 1)
+ call assert_equal(1, line('w0'))
+ call assert_equal(10, line('w$'))
set mouse=a
+ " One scroll event moves three lines.
call feedkeys("A\<ScrollWheelDown>\<esc>", 'tnix')
- call assert_equal([0, 4, 1, 0], getpos('.'))
- " This should move by one pageDown, but only moves
- " by one line when the test is run...
+ call assert_equal(4, line('w0'))
+ call assert_equal(13, line('w$'))
+ " This should move by one page down.
call feedkeys("A\<S-ScrollWheelDown>\<esc>", 'tnix')
- call assert_equal([0, 5, 1, 0], getpos('.'))
+ call assert_equal(14, line('w0'))
set nostartofline
+ " Another page down.
call feedkeys("A\<C-ScrollWheelDown>\<esc>", 'tnix')
- call assert_equal([0, 6, 1, 0], getpos('.'))
+ call assert_equal(24, line('w0'))
+
+ call assert_equal([0, 24, 2, 0], getpos('.'))
+ " call test_setmouse(4, 3)
+ call nvim_input_mouse('left', 'press', '', 0, 3, 2) " set mouse position
+ call getchar() " discard mouse event but keep mouse position
call feedkeys("A\<LeftMouse>\<esc>", 'tnix')
- call assert_equal([0, 6, 1, 0], getpos('.'))
- call feedkeys("A\<RightMouse>\<esc>", 'tnix')
- call assert_equal([0, 6, 1, 0], getpos('.'))
+ call assert_equal([0, 27, 2, 0], getpos('.'))
+ set mousemodel=extend
+ " call test_setmouse(5, 3)
+ call nvim_input_mouse('right', 'press', '', 0, 4, 2) " set mouse position
+ call getchar() " discard mouse event but keep mouse position
+ call feedkeys("A\<RightMouse>\<esc>\<esc>", 'tnix')
+ call assert_equal([0, 28, 2, 0], getpos('.'))
+ set mousemodel&
call cursor(1, 100)
norm! zt
" this should move by a screen up, but when the test
@@ -1303,7 +1380,7 @@ func Test_edit_forbidden()
try
call feedkeys("ix\<esc>", 'tnix')
call assert_fails(1, 'textlock')
- catch /^Vim\%((\a\+)\)\=:E523/ " catch E523: not allowed here
+ catch /^Vim\%((\a\+)\)\=:E565/ " catch E565: not allowed here
endtry
" TODO: Might be a bug: should x really be inserted here
call assert_equal(['xa'], getline(1, '$'))
@@ -1328,7 +1405,7 @@ func Test_edit_forbidden()
try
call feedkeys("i\<c-x>\<c-u>\<esc>", 'tnix')
call assert_fails(1, 'change in complete function')
- catch /^Vim\%((\a\+)\)\=:E523/ " catch E523
+ catch /^Vim\%((\a\+)\)\=:E565/ " catch E565
endtry
delfu Complete
set completefunc=
@@ -1550,11 +1627,7 @@ endfunc
func Test_edit_special_chars()
new
- if has("ebcdic")
- let t = "o\<C-V>193\<C-V>xc2\<C-V>o303 \<C-V>90a\<C-V>xfg\<C-V>o578\<Esc>"
- else
- let t = "o\<C-V>65\<C-V>x42\<C-V>o103 \<C-V>33a\<C-V>xfg\<C-V>o78\<Esc>"
- endif
+ let t = "o\<C-V>65\<C-V>x42\<C-V>o103 \<C-V>33a\<C-V>xfg\<C-V>o78\<Esc>"
exe "normal " . t
call assert_equal("ABC !a\<C-O>g\<C-G>8", getline(2))
@@ -1577,6 +1650,22 @@ func Test_edit_startinsert()
bwipe!
endfunc
+" Test for :startreplace and :startgreplace
+func Test_edit_startreplace()
+ new
+ call setline(1, 'abc')
+ call feedkeys("l:startreplace\<CR>xyz\e", 'xt')
+ call assert_equal('axyz', getline(1))
+ call feedkeys("0:startreplace!\<CR>abc\e", 'xt')
+ call assert_equal('axyzabc', getline(1))
+ call setline(1, "a\tb")
+ call feedkeys("0l:startgreplace\<CR>xyz\e", 'xt')
+ call assert_equal("axyz\tb", getline(1))
+ call feedkeys("0i\<C-R>=execute('startreplace')\<CR>12\e", 'xt')
+ call assert_equal("12axyz\tb", getline(1))
+ close!
+endfunc
+
func Test_edit_noesckeys()
CheckNotGui
new
@@ -1597,6 +1686,58 @@ func Test_edit_noesckeys()
" set esckeys
endfunc
+" Test for running an invalid ex command in insert mode using CTRL-O
+" Note that vim has a hard-coded sleep of 3 seconds. So this test will take
+" more than 3 seconds to complete.
+func Test_edit_ctrl_o_invalid_cmd()
+ new
+ set showmode showcmd
+ let caught_e492 = 0
+ try
+ call feedkeys("i\<C-O>:invalid\<CR>abc\<Esc>", "xt")
+ catch /E492:/
+ let caught_e492 = 1
+ endtry
+ call assert_equal(1, caught_e492)
+ call assert_equal('abc', getline(1))
+ set showmode& showcmd&
+ close!
+endfunc
+
+" Test for inserting text in a line with only spaces ('H' flag in 'cpoptions')
+func Test_edit_cpo_H()
+ throw 'Skipped: Nvim does not support cpoptions flag "H"'
+ new
+ call setline(1, ' ')
+ normal! Ia
+ call assert_equal(' a', getline(1))
+ set cpo+=H
+ call setline(1, ' ')
+ normal! Ia
+ call assert_equal(' a ', getline(1))
+ set cpo-=H
+ close!
+endfunc
+
+" Test for inserting tab in virtual replace mode ('L' flag in 'cpoptions')
+func Test_edit_cpo_L()
+ new
+ call setline(1, 'abcdefghijklmnopqr')
+ exe "normal 0gR\<Tab>"
+ call assert_equal("\<Tab>ijklmnopqr", getline(1))
+ set cpo+=L
+ set list
+ call setline(1, 'abcdefghijklmnopqr')
+ exe "normal 0gR\<Tab>"
+ call assert_equal("\<Tab>cdefghijklmnopqr", getline(1))
+ set nolist
+ call setline(1, 'abcdefghijklmnopqr')
+ exe "normal 0gR\<Tab>"
+ call assert_equal("\<Tab>ijklmnopqr", getline(1))
+ set cpo-=L
+ %bw!
+endfunc
+
" Test for editing a directory
func Test_edit_is_a_directory()
CheckEnglish
@@ -1622,6 +1763,29 @@ func Test_edit_is_a_directory()
call delete(dirname, 'rf')
endfunc
+" Using :edit without leaving 'insertmode' should not cause Insert mode to be
+" re-entered immediately after <C-L>
+func Test_edit_insertmode_ex_edit()
+ CheckRunVimInTerminal
+
+ let lines =<< trim END
+ set insertmode noruler
+ inoremap <C-B> <Cmd>edit Xfoo<CR>
+ END
+ call writefile(lines, 'Xtest_edit_insertmode_ex_edit')
+
+ let buf = RunVimInTerminal('-S Xtest_edit_insertmode_ex_edit', #{rows: 6})
+ call TermWait(buf, 50)
+ call assert_match('^-- INSERT --\s*$', term_getline(buf, 6))
+ call term_sendkeys(buf, "\<C-B>\<C-L>")
+ call TermWait(buf, 50)
+ call assert_notmatch('^-- INSERT --\s*$', term_getline(buf, 6))
+
+ " clean up
+ call StopVimInTerminal(buf)
+ call delete('Xtest_edit_insertmode_ex_edit')
+endfunc
+
func Test_edit_browse()
" in the GUI this opens a file picker, we only test the terminal behavior
CheckNotGui
diff --git a/src/nvim/testdir/test_eval_stuff.vim b/src/nvim/testdir/test_eval_stuff.vim
index 883ba5de3d..811c6c946d 100644
--- a/src/nvim/testdir/test_eval_stuff.vim
+++ b/src/nvim/testdir/test_eval_stuff.vim
@@ -65,11 +65,9 @@ func Test_E963()
endfunc
func Test_for_invalid()
- " Vim gives incorrect emsg here until v8.2.3284, but the exact emsg from that
- " patch cannot be used until v8.2.2658 is ported (for loop over Strings)
- call assert_fails("for x in 99", 'E897:')
- call assert_fails("for x in function('winnr')", 'E897:')
- call assert_fails("for x in {'a': 9}", 'E897:')
+ call assert_fails("for x in 99", 'E1098:')
+ call assert_fails("for x in function('winnr')", 'E1098:')
+ call assert_fails("for x in {'a': 9}", 'E1098:')
if 0
/1/5/2/s/\n
@@ -322,4 +320,26 @@ func Test_curly_assignment()
unlet g:gvar
endfunc
+" K_SPECIAL in the modified character used be escaped, which causes
+" double-escaping with feedkeys() or as the return value of an <expr> mapping,
+" and doesn't match what getchar() returns,
+func Test_modified_char_no_escape_special()
+ nnoremap <M-…> <Cmd>let g:got_m_ellipsis += 1<CR>
+ call feedkeys("\<M-…>", 't')
+ call assert_equal("\<M-…>", getchar())
+ let g:got_m_ellipsis = 0
+ call feedkeys("\<M-…>", 'xt')
+ call assert_equal(1, g:got_m_ellipsis)
+ func Func()
+ return "\<M-…>"
+ endfunc
+ nmap <expr> <F2> Func()
+ call feedkeys("\<F2>", 'xt')
+ call assert_equal(2, g:got_m_ellipsis)
+ delfunc Func
+ nunmap <F2>
+ unlet g:got_m_ellipsis
+ nunmap <M-…>
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_ex_mode.vim b/src/nvim/testdir/test_ex_mode.vim
index 92e0559618..122572f32a 100644
--- a/src/nvim/testdir/test_ex_mode.vim
+++ b/src/nvim/testdir/test_ex_mode.vim
@@ -29,12 +29,11 @@ endfunc
" Test editing line in Ex mode (both Q and gQ)
func Test_ex_mode()
- throw 'skipped: TODO: '
+ throw 'Skipped: Nvim only supports Vim Ex mode'
let encoding_save = &encoding
set sw=2
- " for e in ['utf8', 'latin1']
- for e in ['utf8']
+ for e in ['utf8', 'latin1']
exe 'set encoding=' . e
call assert_equal(['bar', 'bar'], Ex("foo bar\<C-u>bar"), e)
@@ -52,22 +51,131 @@ func Test_ex_mode()
call assert_equal([' foo', ' foo'], Ex(" foo\<C-d>"), e)
call assert_equal(['foo', ' foo0'], Ex(" foo0\<C-d>"), e)
call assert_equal(['foo', ' foo^'], Ex(" foo^\<C-d>"), e)
+ call assert_equal(['foo', 'foo'],
+ \ Ex("\<BS>\<C-H>\<Del>\<kDel>foo"), e)
+ " default wildchar <Tab> interferes with this test
+ set wildchar=<c-e>
+ call assert_equal(["a\tb", "a\tb"], Ex("a\t\t\<C-H>b"), e)
+ call assert_equal(["\t mn", "\tm\<C-T>n"], Ex("\tm\<C-T>n"), e)
+ set wildchar&
endfor
set sw&
let &encoding = encoding_save
endfunc
+" Test substitute confirmation prompt :%s/pat/str/c in Ex mode
+func Test_Ex_substitute()
+ CheckRunVimInTerminal
+ let buf = RunVimInTerminal('', {'rows': 6})
+
+ call term_sendkeys(buf, ":call setline(1, ['foo foo', 'foo foo', 'foo foo'])\<CR>")
+ call term_sendkeys(buf, ":set number\<CR>")
+ call term_sendkeys(buf, "gQ")
+ call WaitForAssert({-> assert_match(':', term_getline(buf, 6))}, 1000)
+
+ call term_sendkeys(buf, "%s/foo/bar/gc\<CR>")
+ call WaitForAssert({-> assert_match(' 1 foo foo', term_getline(buf, 5))},
+ \ 1000)
+ call WaitForAssert({-> assert_match(' ^^^', term_getline(buf, 6))}, 1000)
+ call term_sendkeys(buf, "N\<CR>")
+ call term_wait(buf)
+ call WaitForAssert({-> assert_match(' ^^^', term_getline(buf, 6))}, 1000)
+ call term_sendkeys(buf, "n\<CR>")
+ call WaitForAssert({-> assert_match(' ^^^', term_getline(buf, 6))},
+ \ 1000)
+ call term_sendkeys(buf, "y\<CR>")
+
+ call term_sendkeys(buf, "q\<CR>")
+ call WaitForAssert({-> assert_match(':', term_getline(buf, 6))}, 1000)
+
+ " Pressing enter in ex mode should print the current line
+ call term_sendkeys(buf, "\<CR>")
+ call WaitForAssert({-> assert_match(' 3 foo foo',
+ \ term_getline(buf, 5))}, 1000)
+
+ call term_sendkeys(buf, ":vi\<CR>")
+ call WaitForAssert({-> assert_match('foo bar', term_getline(buf, 1))}, 1000)
+
+ call term_sendkeys(buf, ":q!\n")
+ call StopVimInTerminal(buf)
+endfunc
+
+" Test for displaying lines from an empty buffer in Ex mode
+func Test_Ex_emptybuf()
+ new
+ call assert_fails('call feedkeys("Q\<CR>", "xt")', 'E749:')
+ call setline(1, "abc")
+ call assert_fails('call feedkeys("Q\<CR>", "xt")', 'E501:')
+ call assert_fails('call feedkeys("Q%d\<CR>", "xt")', 'E749:')
+ close!
+endfunc
+
+" Test for the :open command
+func Test_open_command()
+ throw 'Skipped: Nvim does not have :open'
+ new
+ call setline(1, ['foo foo', 'foo bar', 'foo baz'])
+ call feedkeys("Qopen\<CR>j", 'xt')
+ call assert_equal('foo bar', getline('.'))
+ call feedkeys("Qopen /bar/\<CR>", 'xt')
+ call assert_equal(5, col('.'))
+ call assert_fails('call feedkeys("Qopen /baz/\<CR>", "xt")', 'E479:')
+ close!
+endfunc
+
+" Test for :g/pat/visual to run vi commands in Ex mode
+" This used to hang Vim before 8.2.0274.
+func Test_Ex_global()
+ new
+ call setline(1, ['', 'foo', 'bar', 'foo', 'bar', 'foo'])
+ call feedkeys("Q\<bs>g/bar/visual\<CR>$rxQ$ryQvisual\<CR>j", "xt")
+ call assert_equal('bax', getline(3))
+ call assert_equal('bay', getline(5))
+ bwipe!
+endfunc
+
+" In Ex-mode, a backslash escapes a newline
+func Test_Ex_escape_enter()
+ call feedkeys("gQlet l = \"a\\\<kEnter>b\"\<cr>vi\<cr>", 'xt')
+ call assert_equal("a\rb", l)
+endfunc
+
+" Test for :append! command in Ex mode
+func Test_Ex_append()
+ throw 'Skipped: Nvim only supports Vim Ex mode'
+ new
+ call setline(1, "\t abc")
+ call feedkeys("Qappend!\npqr\nxyz\n.\nvisual\n", 'xt')
+ call assert_equal(["\t abc", "\t pqr", "\t xyz"], getline(1, '$'))
+ close!
+endfunc
+
+" In Ex-mode, backslashes at the end of a command should be halved.
+func Test_Ex_echo_backslash()
+ throw 'Skipped: Nvim only supports Vim Ex mode'
+ " This test works only when the language is English
+ if v:lang != "C" && v:lang !~ '^[Ee]n'
+ return
+ endif
+ let bsl = '\\\\'
+ let bsl2 = '\\\'
+ call assert_fails('call feedkeys("Qecho " .. bsl .. "\nvisual\n", "xt")',
+ \ "E15: Invalid expression: \\\\")
+ call assert_fails('call feedkeys("Qecho " .. bsl2 .. "\nm\nvisual\n", "xt")',
+ \ "E15: Invalid expression: \\\nm")
+endfunc
+
func Test_ex_mode_errors()
" Not allowed to enter ex mode when text is locked
au InsertCharPre <buffer> normal! gQ<CR>
- let caught_e523 = 0
+ let caught_e565 = 0
try
call feedkeys("ix\<esc>", 'xt')
- catch /^Vim\%((\a\+)\)\=:E523/ " catch E523
- let caught_e523 = 1
+ catch /^Vim\%((\a\+)\)\=:E565/ " catch E565
+ let caught_e565 = 1
endtry
- call assert_equal(1, caught_e523)
+ call assert_equal(1, caught_e565)
au! InsertCharPre
new
@@ -83,6 +191,9 @@ func Test_ex_mode_errors()
endfunc
func Test_ex_mode_count_overflow()
+ " The multiplication causes an integer overflow
+ CheckNotAsan
+
" this used to cause a crash
let lines =<< trim END
call feedkeys("\<Esc>gQ\<CR>")
@@ -98,4 +209,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_excmd.vim b/src/nvim/testdir/test_excmd.vim
index 1c053c824f..7d581d5efd 100644
--- a/src/nvim/testdir/test_excmd.vim
+++ b/src/nvim/testdir/test_excmd.vim
@@ -22,6 +22,7 @@ func Test_range_error()
call assert_fails(':\/echo 1', 'E481:')
normal vv
call assert_fails(":'<,'>echo 1", 'E481:')
+ call assert_fails(":\\xcenter", 'E10:')
endfunc
func Test_buffers_lastused()
@@ -65,6 +66,10 @@ func Test_copy()
1,3copy 2
call assert_equal(['L1', 'L2', 'L1', 'L2', 'L3', 'L3', 'L4'], getline(1, 7))
+ " Specifying a count before using : to run an ex-command
+ exe "normal! gg4:yank\<CR>"
+ call assert_equal("L1\nL2\nL1\nL2\n", @")
+
close!
endfunc
@@ -147,7 +152,6 @@ endfunc
" Test for the :insert command
func Test_insert_cmd()
- set noautoindent " test assumes noautoindent, but it's on by default in Nvim
new
call setline(1, [' L1'])
call feedkeys(":insert\<CR> L2\<CR> L3\<CR>.\<CR>", 'xt')
@@ -197,7 +201,6 @@ endfunc
" Test for the :change command
func Test_change_cmd()
- set noautoindent " test assumes noautoindent, but it's on by default in Nvim
new
call setline(1, [' L1', 'L2', 'L3'])
call feedkeys(":change\<CR> L4\<CR> L5\<CR>.\<CR>", 'xt')
@@ -224,12 +227,24 @@ func Test_change_cmd()
close!
endfunc
+" Test for the :language command
+func Test_language_cmd()
+ CheckNotMSWindows " FIXME: why does this fail on Windows CI?
+ CheckNotBSD " FIXME: why does this fail on OpenBSD CI?
+ CheckFeature multi_lang
+
+ call assert_fails('language ctype non_existing_lang', 'E197:')
+ call assert_fails('language time non_existing_lang', 'E197:')
+endfunc
+
" Test for the :confirm command dialog
func Test_confirm_cmd()
CheckNotGui
CheckRunVimInTerminal
+
call writefile(['foo1'], 'foo')
call writefile(['bar1'], 'bar')
+
" Test for saving all the modified buffers
let buf = RunVimInTerminal('', {'rows': 20})
call term_sendkeys(buf, ":set nomore\n")
@@ -242,8 +257,10 @@ func Test_confirm_cmd()
call WaitForAssert({-> assert_match('\[Y\]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: ', term_getline(buf, 20))}, 1000)
call term_sendkeys(buf, "A")
call StopVimInTerminal(buf)
+
call assert_equal(['foo2'], readfile('foo'))
call assert_equal(['bar2'], readfile('bar'))
+
" Test for discarding all the changes to modified buffers
let buf = RunVimInTerminal('', {'rows': 20})
call term_sendkeys(buf, ":set nomore\n")
@@ -256,8 +273,10 @@ func Test_confirm_cmd()
call WaitForAssert({-> assert_match('\[Y\]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: ', term_getline(buf, 20))}, 1000)
call term_sendkeys(buf, "D")
call StopVimInTerminal(buf)
+
call assert_equal(['foo2'], readfile('foo'))
call assert_equal(['bar2'], readfile('bar'))
+
" Test for saving and discarding changes to some buffers
let buf = RunVimInTerminal('', {'rows': 20})
call term_sendkeys(buf, ":set nomore\n")
@@ -272,6 +291,7 @@ func Test_confirm_cmd()
call WaitForAssert({-> assert_match('\[Y\]es, (N)o, (C)ancel: ', term_getline(buf, 20))}, 1000)
call term_sendkeys(buf, "Y")
call StopVimInTerminal(buf)
+
call assert_equal(['foo4'], readfile('foo'))
call assert_equal(['bar2'], readfile('bar'))
@@ -393,6 +413,11 @@ func Test_confirm_write_partial_file()
call delete('Xscript')
endfunc
+" Test for the :print command
+func Test_print_cmd()
+ call assert_fails('print', 'E749:')
+endfunc
+
" Test for the :winsize command
func Test_winsize_cmd()
call assert_fails('winsize 1', 'E465:')
@@ -400,3 +425,226 @@ func Test_winsize_cmd()
call assert_fails('win_getid(1)', 'E475: Invalid argument: _getid(1)')
" Actually changing the window size would be flaky.
endfunc
+
+" Test for the :redir command
+func Test_redir_cmd()
+ call assert_fails('redir @@', 'E475:')
+ call assert_fails('redir abc', 'E475:')
+ if has('unix')
+ call mkdir('Xdir')
+ call assert_fails('redir > Xdir', 'E17:')
+ call delete('Xdir', 'd')
+ endif
+ if !has('bsd')
+ call writefile([], 'Xfile')
+ call setfperm('Xfile', 'r--r--r--')
+ call assert_fails('redir! > Xfile', 'E190:')
+ call delete('Xfile')
+ endif
+
+ " Test for redirecting to a register
+ redir @q> | echon 'clean ' | redir END
+ redir @q>> | echon 'water' | redir END
+ call assert_equal('clean water', @q)
+
+ " Test for redirecting to a variable
+ redir => color | echon 'blue ' | redir END
+ redir =>> color | echon 'sky' | redir END
+ call assert_equal('blue sky', color)
+endfunc
+
+" Test for the :filetype command
+func Test_filetype_cmd()
+ call assert_fails('filetype abc', 'E475:')
+endfunc
+
+" Test for the :mode command
+func Test_mode_cmd()
+ call assert_fails('mode abc', 'E359:')
+endfunc
+
+" Test for the :sleep command
+func Test_sleep_cmd()
+ call assert_fails('sleep x', 'E475:')
+endfunc
+
+" Test for the :read command
+func Test_read_cmd()
+ call writefile(['one'], 'Xfile')
+ new
+ call assert_fails('read', 'E32:')
+ edit Xfile
+ read
+ call assert_equal(['one', 'one'], getline(1, '$'))
+ close!
+ new
+ read Xfile
+ call assert_equal(['', 'one'], getline(1, '$'))
+ call deletebufline('', 1, '$')
+ call feedkeys("Qr Xfile\<CR>visual\<CR>", 'xt')
+ call assert_equal(['one'], getline(1, '$'))
+ close!
+ call delete('Xfile')
+endfunc
+
+" Test for running Ex commands when text is locked.
+" <C-\>e in the command line is used to lock the text
+func Test_run_excmd_with_text_locked()
+ " :quit
+ let cmd = ":\<C-\>eexecute('quit')\<CR>\<C-C>"
+ call assert_fails("call feedkeys(cmd, 'xt')", 'E565:')
+
+ " :qall
+ let cmd = ":\<C-\>eexecute('qall')\<CR>\<C-C>"
+ call assert_fails("call feedkeys(cmd, 'xt')", 'E565:')
+
+ " :exit
+ let cmd = ":\<C-\>eexecute('exit')\<CR>\<C-C>"
+ call assert_fails("call feedkeys(cmd, 'xt')", 'E565:')
+
+ " :close - should be ignored
+ new
+ let cmd = ":\<C-\>eexecute('close')\<CR>\<C-C>"
+ call assert_equal(2, winnr('$'))
+ close
+
+ call assert_fails("call feedkeys(\":\<C-R>=execute('bnext')\<CR>\", 'xt')", 'E565:')
+
+ " :tabfirst
+ tabnew
+ call assert_fails("call feedkeys(\":\<C-R>=execute('tabfirst')\<CR>\", 'xt')", 'E565:')
+ tabclose
+endfunc
+
+" Test for the :verbose command
+func Test_verbose_cmd()
+ call assert_equal([' verbose=1'], split(execute('verbose set vbs'), "\n"))
+ call assert_equal([' verbose=0'], split(execute('0verbose set vbs'), "\n"))
+ let l = execute("4verbose set verbose | set verbose")
+ call assert_equal([' verbose=4', ' verbose=0'], split(l, "\n"))
+endfunc
+
+" Test for the :delete command and the related abbreviated commands
+func Test_excmd_delete()
+ new
+ call setline(1, ['foo', "\tbar"])
+ call assert_equal(['^Ibar$'], split(execute('dl'), "\n"))
+ call setline(1, ['foo', "\tbar"])
+ call assert_equal(['^Ibar$'], split(execute('dell'), "\n"))
+ call setline(1, ['foo', "\tbar"])
+ call assert_equal(['^Ibar$'], split(execute('delel'), "\n"))
+ call setline(1, ['foo', "\tbar"])
+ call assert_equal(['^Ibar$'], split(execute('deletl'), "\n"))
+ call setline(1, ['foo', "\tbar"])
+ call assert_equal(['^Ibar$'], split(execute('deletel'), "\n"))
+ call setline(1, ['foo', "\tbar"])
+ call assert_equal([' bar'], split(execute('dp'), "\n"))
+ call setline(1, ['foo', "\tbar"])
+ call assert_equal([' bar'], split(execute('dep'), "\n"))
+ call setline(1, ['foo', "\tbar"])
+ call assert_equal([' bar'], split(execute('delp'), "\n"))
+ call setline(1, ['foo', "\tbar"])
+ call assert_equal([' bar'], split(execute('delep'), "\n"))
+ call setline(1, ['foo', "\tbar"])
+ call assert_equal([' bar'], split(execute('deletp'), "\n"))
+ call setline(1, ['foo', "\tbar"])
+ call assert_equal([' bar'], split(execute('deletep'), "\n"))
+ close!
+endfunc
+
+" Test for commands that are blocked in a sandbox
+func Sandbox_tests()
+ call assert_fails("call histadd(':', 'ls')", 'E48:')
+ call assert_fails("call mkdir('Xdir')", 'E48:')
+ call assert_fails("call rename('a', 'b')", 'E48:')
+ call assert_fails("call setbufvar(1, 'myvar', 1)", 'E48:')
+ call assert_fails("call settabvar(1, 'myvar', 1)", 'E48:')
+ call assert_fails("call settabwinvar(1, 1, 'myvar', 1)", 'E48:')
+ call assert_fails("call setwinvar(1, 'myvar', 1)", 'E48:')
+ call assert_fails("call timer_start(100, '')", 'E48:')
+ if has('channel')
+ call assert_fails("call prompt_setcallback(1, '')", 'E48:')
+ call assert_fails("call prompt_setinterrupt(1, '')", 'E48:')
+ call assert_fails("call prompt_setprompt(1, '')", 'E48:')
+ endif
+ call assert_fails("let $TESTVAR=1", 'E48:')
+ call assert_fails("call feedkeys('ivim')", 'E48:')
+ call assert_fails("source! Xfile", 'E48:')
+ call assert_fails("call delete('Xfile')", 'E48:')
+ call assert_fails("call writefile([], 'Xfile')", 'E48:')
+ call assert_fails('!ls', 'E48:')
+ " call assert_fails('shell', 'E48:')
+ call assert_fails('stop', 'E48:')
+ call assert_fails('exe "normal \<C-Z>"', 'E48:')
+ " set insertmode
+ " call assert_fails('call feedkeys("\<C-Z>", "xt")', 'E48:')
+ " set insertmode&
+ call assert_fails('suspend', 'E48:')
+ call assert_fails('call system("ls")', 'E48:')
+ call assert_fails('call systemlist("ls")', 'E48:')
+ if has('clientserver')
+ call assert_fails('let s=remote_expr("gvim", "2+2")', 'E48:')
+ if !has('win32')
+ " remote_foreground() doesn't thrown an error message on MS-Windows
+ call assert_fails('call remote_foreground("gvim")', 'E48:')
+ endif
+ call assert_fails('let s=remote_peek("gvim")', 'E48:')
+ call assert_fails('let s=remote_read("gvim")', 'E48:')
+ call assert_fails('let s=remote_send("gvim", "abc")', 'E48:')
+ call assert_fails('let s=server2client("gvim", "abc")', 'E48:')
+ endif
+ if has('terminal')
+ call assert_fails('terminal', 'E48:')
+ call assert_fails('call term_start("vim")', 'E48:')
+ call assert_fails('call term_dumpwrite(1, "Xfile")', 'E48:')
+ endif
+ if has('channel')
+ call assert_fails("call ch_logfile('chlog')", 'E48:')
+ call assert_fails("call ch_open('localhost:8765')", 'E48:')
+ endif
+ if has('job')
+ call assert_fails("call job_start('vim')", 'E48:')
+ endif
+ if has('unix') && has('libcall')
+ call assert_fails("echo libcall('libc.so', 'getenv', 'HOME')", 'E48:')
+ endif
+ if has('unix')
+ call assert_fails('cd `pwd`', 'E48:')
+ endif
+endfunc
+
+func Test_sandbox()
+ sandbox call Sandbox_tests()
+endfunc
+
+func Test_not_break_expression_register()
+ call setreg('=', '1+1')
+ if 0
+ put =1
+ endif
+ call assert_equal('1+1', getreg('=', 1))
+endfunc
+
+func Test_address_line_overflow()
+ throw 'Skipped: v:sizeoflong is N/A' " use legacy/excmd_spec.lua instead
+
+ if v:sizeoflong < 8
+ throw 'Skipped: only works with 64 bit long ints'
+ endif
+ new
+ call setline(1, 'text')
+ call assert_fails('|.44444444444444444444444', 'E1247:')
+ call assert_fails('|.9223372036854775806', 'E1247:')
+ bwipe!
+endfunc
+
+" This was leaving the cursor in line zero
+func Test_using_zero_in_range()
+ new
+ norm o00
+ silent! 0;s/\%')
+ bwipe!
+endfunc
+
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_exec_while_if.vim b/src/nvim/testdir/test_exec_while_if.vim
index 3da2784d77..3f13b09945 100644
--- a/src/nvim/testdir/test_exec_while_if.vim
+++ b/src/nvim/testdir/test_exec_while_if.vim
@@ -6,11 +6,7 @@ func Test_exec_while_if()
let i = 0
while i < 12
let i = i + 1
- if has("ebcdic")
- execute "normal o" . i . "\047"
- else
- execute "normal o" . i . "\033"
- endif
+ execute "normal o" . i . "\033"
if i % 2
normal Ax
if i == 9
@@ -21,21 +17,13 @@ func Test_exec_while_if()
else
let j = 9
while j > 0
- if has("ebcdic")
- execute "normal" j . "a" . j . "\x27"
- else
- execute "normal" j . "a" . j . "\x1b"
- endif
+ execute "normal" j . "a" . j . "\x1b"
let j = j - 1
endwhile
endif
endif
if i == 9
- if has("ebcdic")
- execute "normal Az\047"
- else
- execute "normal Az\033"
- endif
+ execute "normal Az\033"
endif
endwhile
unlet i j
diff --git a/src/nvim/testdir/test_execute_func.vim b/src/nvim/testdir/test_execute_func.vim
index 2cb6d73407..16cc20e9a7 100644
--- a/src/nvim/testdir/test_execute_func.vim
+++ b/src/nvim/testdir/test_execute_func.vim
@@ -147,3 +147,30 @@ func Test_win_execute_other_tab()
tabclose
unlet xyz
endfunc
+
+func Test_win_execute_visual_redraw()
+ call setline(1, ['a', 'b', 'c'])
+ new
+ wincmd p
+ " start Visual in current window, redraw in other window with fewer lines
+ call feedkeys("G\<C-V>", 'txn')
+ call win_execute(winnr('#')->win_getid(), 'redraw')
+ call feedkeys("\<Esc>", 'txn')
+ bwipe!
+ bwipe!
+
+ enew
+ new
+ call setline(1, ['a', 'b', 'c'])
+ let winid = win_getid()
+ wincmd p
+ " start Visual in current window, extend it in other window with more lines
+ call feedkeys("\<C-V>", 'txn')
+ call win_execute(winid, 'call feedkeys("G\<C-V>", ''txn'')')
+ redraw
+
+ bwipe!
+ bwipe!
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_exists.vim b/src/nvim/testdir/test_exists.vim
index fd34be83b0..471c77853d 100644
--- a/src/nvim/testdir/test_exists.vim
+++ b/src/nvim/testdir/test_exists.vim
@@ -94,8 +94,12 @@ func Test_exists()
call assert_equal(0, exists(':edit/a'))
" Valid internal command (partial match)
call assert_equal(1, exists(':q'))
+ " Valid internal command with a digit
+ call assert_equal(2, exists(':2match'))
" Non-existing internal command
call assert_equal(0, exists(':invalidcmd'))
+ " Internal command with a count
+ call assert_equal(0, exists(':3buffer'))
" User defined command (full match)
command! MyCmd :echo 'My command'
diff --git a/src/nvim/testdir/test_exit.vim b/src/nvim/testdir/test_exit.vim
index bd3e9eb4d4..befcaec2b2 100644
--- a/src/nvim/testdir/test_exit.vim
+++ b/src/nvim/testdir/test_exit.vim
@@ -1,6 +1,7 @@
" Tests for exiting Vim.
source shared.vim
+source check.vim
func Test_exiting()
let after =<< trim [CODE]
@@ -109,4 +110,25 @@ func Test_exit_code()
call delete('Xtestout')
endfunc
+func Test_exit_error_reading_input()
+ throw 'Skipped: Nvim does not exit after stdin is read'
+
+ CheckNotGui
+ CheckNotMSWindows
+ " The early exit causes memory not to be freed somehow
+ CheckNotAsan
+
+ call writefile([":au VimLeave * call writefile(['l = ' .. v:exiting], 'Xtestout')", ":tabnew", "q:"], 'Xscript', 'b')
+
+ " Nvim requires "-s -" to read stdin as Normal mode input
+ " if RunVim([], [], '<Xscript')
+ if RunVim([], [], '-s - <Xscript')
+ call assert_equal(1, v:shell_error)
+ call assert_equal(['l = 1'], readfile('Xtestout'))
+ endif
+ call delete('Xscript')
+ call delete('Xtestout')
+endfun
+
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_expand.vim b/src/nvim/testdir/test_expand.vim
new file mode 100644
index 0000000000..d86fea4f45
--- /dev/null
+++ b/src/nvim/testdir/test_expand.vim
@@ -0,0 +1,128 @@
+" Test for expanding file names
+
+source shared.vim
+
+func Test_with_directories()
+ call mkdir('Xdir1')
+ call mkdir('Xdir2')
+ call mkdir('Xdir3')
+ cd Xdir3
+ call mkdir('Xdir4')
+ cd ..
+
+ split Xdir1/file
+ call setline(1, ['a', 'b'])
+ w
+ w Xdir3/Xdir4/file
+ close
+
+ next Xdir?/*/file
+ call assert_equal('Xdir3/Xdir4/file', expand('%'))
+ if has('unix')
+ next! Xdir?/*/nofile
+ call assert_equal('Xdir?/*/nofile', expand('%'))
+ endif
+ " Edit another file, on MS-Windows the swap file would be in use and can't
+ " be deleted.
+ edit foo
+
+ call assert_equal(0, delete('Xdir1', 'rf'))
+ call assert_equal(0, delete('Xdir2', 'rf'))
+ call assert_equal(0, delete('Xdir3', 'rf'))
+endfunc
+
+func Test_with_tilde()
+ let dir = getcwd()
+ call mkdir('Xdir ~ dir')
+ call assert_true(isdirectory('Xdir ~ dir'))
+ cd Xdir\ ~\ dir
+ call assert_true(getcwd() =~ 'Xdir \~ dir')
+ call chdir(dir)
+ call delete('Xdir ~ dir', 'd')
+ call assert_false(isdirectory('Xdir ~ dir'))
+endfunc
+
+func Test_expand_tilde_filename()
+ split ~
+ call assert_equal('~', expand('%'))
+ call assert_notequal(expand('%:p'), expand('~/'))
+ call assert_match('\~', expand('%:p'))
+ bwipe!
+endfunc
+
+func Test_expandcmd()
+ let $FOO = 'Test'
+ call assert_equal('e x/Test/y', expandcmd('e x/$FOO/y'))
+ unlet $FOO
+
+ new
+ edit Xfile1
+ call assert_equal('e Xfile1', expandcmd('e %'))
+ edit Xfile2
+ edit Xfile1
+ call assert_equal('e Xfile2', 'e #'->expandcmd())
+ edit Xfile2
+ edit Xfile3
+ edit Xfile4
+ let bnum = bufnr('Xfile2')
+ call assert_equal('e Xfile2', expandcmd('e #' . bnum))
+ call setline('.', 'Vim!@#')
+ call assert_equal('e Vim', expandcmd('e <cword>'))
+ call assert_equal('e Vim!@#', expandcmd('e <cWORD>'))
+ enew!
+ edit Xfile.java
+ call assert_equal('e Xfile.py', expandcmd('e %:r.py'))
+ call assert_equal('make abc.java', expandcmd('make abc.%:e'))
+ call assert_equal('make Xabc.java', expandcmd('make %:s?file?abc?'))
+ edit a1a2a3.rb
+ call assert_equal('make b1b2b3.rb a1a2a3 Xfile.o', expandcmd('make %:gs?a?b? %< #<.o'))
+
+ call assert_fails('call expandcmd("make <afile>")', 'E495:')
+ call assert_fails('call expandcmd("make <afile>")', 'E495:')
+ enew
+ call assert_fails('call expandcmd("make %")', 'E499:')
+ let $FOO="blue\tsky"
+ call setline(1, "$FOO")
+ call assert_equal("grep pat blue\tsky", expandcmd('grep pat <cfile>'))
+ unlet $FOO
+ close!
+endfunc
+
+" Test for expanding <sfile>, <slnum> and <sflnum> outside of sourcing a script
+func Test_source_sfile()
+ let lines =<< trim [SCRIPT]
+ :call assert_fails('echo expandcmd("<sfile>")', 'E498:')
+ :call assert_fails('echo expandcmd("<slnum>")', 'E842:')
+ :call assert_fails('echo expandcmd("<sflnum>")', 'E961:')
+ :call assert_fails('call expandcmd("edit <cfile>")', 'E446:')
+ :call assert_fails('call expandcmd("edit #")', 'E194:')
+ :call assert_fails('call expandcmd("edit #<2")', 'E684:')
+ :call assert_fails('call expandcmd("edit <cword>")', 'E348:')
+ :call assert_fails('call expandcmd("edit <cexpr>")', 'E348:')
+ :call assert_fails('autocmd User MyCmd echo "<sfile>"', 'E498:')
+ :call writefile(v:errors, 'Xresult')
+ :qall!
+
+ [SCRIPT]
+ call writefile(lines, 'Xscript')
+ if RunVim([], [], '--clean -s Xscript')
+ call assert_equal([], readfile('Xresult'))
+ endif
+ call delete('Xscript')
+ call delete('Xresult')
+endfunc
+
+" Test for expanding filenames multiple times in a command line
+func Test_expand_filename_multicmd()
+ edit foo
+ call setline(1, 'foo!')
+ new
+ call setline(1, 'foo!')
+ new <cword> | new <cWORD>
+ call assert_equal(4, winnr('$'))
+ call assert_equal('foo!', bufname(winbufnr(1)))
+ call assert_equal('foo', bufname(winbufnr(2)))
+ %bwipe!
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_expand_func.vim b/src/nvim/testdir/test_expand_func.vim
index 44d2c156d5..b48c2e8a19 100644
--- a/src/nvim/testdir/test_expand_func.vim
+++ b/src/nvim/testdir/test_expand_func.vim
@@ -37,15 +37,6 @@ func Test_expand_sflnum()
delcommand Flnum
endfunc
-func Test_expand()
- new
- call assert_equal("", expand('%:S'))
- call assert_equal('3', '<slnum>'->expand())
- call assert_equal(['4'], expand('<slnum>', v:false, v:true))
- " Don't add any line above this, otherwise <slnum> will change.
- quit
-endfunc
-
func Test_expand_sfile()
call assert_match('test_expand_func\.vim$', s:sfile)
call assert_match('^function .*\.\.Test_expand_sfile$', expand('<sfile>'))
@@ -77,6 +68,15 @@ func Test_expand_slnum()
delcommand Slnum
endfunc
+func Test_expand()
+ new
+ call assert_equal("", expand('%:S'))
+ call assert_equal('3', '<slnum>'->expand())
+ call assert_equal(['4'], expand('<slnum>', v:false, v:true))
+ " Don't add any line above this, otherwise <slnum> will change.
+ quit
+endfunc
+
func s:sid_test()
return 'works'
endfunc
@@ -87,4 +87,17 @@ func Test_expand_SID()
call assert_equal('works', g:sid_result)
endfunc
+
+" Test for 'wildignore' with expand()
+func Test_expand_wildignore()
+ set wildignore=*.vim
+ call assert_equal('', expand('test_expand_func.vim'))
+ call assert_equal('', expand('test_expand_func.vim', 0))
+ call assert_equal([], expand('test_expand_func.vim', 0, 1))
+ call assert_equal('test_expand_func.vim', expand('test_expand_func.vim', 1))
+ call assert_equal(['test_expand_func.vim'],
+ \ expand('test_expand_func.vim', 1, 1))
+ set wildignore&
+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_feedkeys.vim b/src/nvim/testdir/test_feedkeys.vim
index 70500f2bb5..fb64711863 100644
--- a/src/nvim/testdir/test_feedkeys.vim
+++ b/src/nvim/testdir/test_feedkeys.vim
@@ -12,3 +12,26 @@ func Test_feedkeys_x_with_empty_string()
call assert_equal('foo', getline('.'))
quit!
endfunc
+
+func Test_feedkeys_with_abbreviation()
+ new
+ inoreabbrev trigger value
+ call feedkeys("atrigger ", 'x')
+ call feedkeys("atrigger ", 'x')
+ call assert_equal('value value ', getline(1))
+ bwipe!
+ iunabbrev trigger
+endfunc
+
+func Test_feedkeys_escape_special()
+ nnoremap … <Cmd>let g:got_ellipsis += 1<CR>
+ call feedkeys('…', 't')
+ call assert_equal('…', getcharstr())
+ let g:got_ellipsis = 0
+ call feedkeys('…', 'xt')
+ call assert_equal(1, g:got_ellipsis)
+ unlet g:got_ellipsis
+ nunmap …
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_file_perm.vim b/src/nvim/testdir/test_file_perm.vim
new file mode 100644
index 0000000000..1cb09e8647
--- /dev/null
+++ b/src/nvim/testdir/test_file_perm.vim
@@ -0,0 +1,30 @@
+" Test getting and setting file permissions.
+
+func Test_file_perm()
+ call assert_equal('', getfperm('Xtest'))
+ call assert_equal(0, 'Xtest'->setfperm('r--------'))
+
+ call writefile(['one'], 'Xtest')
+ call assert_true(len('Xtest'->getfperm()) == 9)
+
+ call assert_equal(1, setfperm('Xtest', 'rwx------'))
+ if has('win32')
+ call assert_equal('rw-rw-rw-', getfperm('Xtest'))
+ else
+ call assert_equal('rwx------', getfperm('Xtest'))
+ endif
+
+ call assert_equal(1, setfperm('Xtest', 'r--r--r--'))
+ call assert_equal('r--r--r--', getfperm('Xtest'))
+
+ call assert_fails("setfperm('Xtest', '---')")
+
+ call assert_equal(1, setfperm('Xtest', 'rwx------'))
+ call delete('Xtest')
+
+ call assert_fails("call setfperm(['Xfile'], 'rw-rw-rw-')", 'E730:')
+ call assert_fails("call setfperm('Xfile', [])", 'E730:')
+ call assert_fails("call setfperm('Xfile', 'rwxrwxrwxrw')", 'E475:')
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_filechanged.vim b/src/nvim/testdir/test_filechanged.vim
index b95cd5faf8..c6e781a1ef 100644
--- a/src/nvim/testdir/test_filechanged.vim
+++ b/src/nvim/testdir/test_filechanged.vim
@@ -1,9 +1,10 @@
" Tests for when a file was changed outside of Vim.
+source check.vim
+
func Test_FileChangedShell_reload()
- if !has('unix')
- return
- endif
+ CheckUnix
+
augroup testreload
au FileChangedShell Xchanged_r let g:reason = v:fcs_reason | let v:fcs_choice = 'reload'
augroup END
@@ -90,11 +91,108 @@ func Test_FileChangedShell_reload()
call delete('Xchanged_r')
endfunc
+func Test_FileChangedShell_edit()
+ CheckUnix
+
+ new Xchanged_r
+ call setline(1, 'reload this')
+ set fileformat=unix
+ write
+
+ " File format changed, reload (content only, no 'ff' etc)
+ augroup testreload
+ au!
+ au FileChangedShell Xchanged_r let g:reason = v:fcs_reason | let v:fcs_choice = 'reload'
+ augroup END
+ call assert_equal(&fileformat, 'unix')
+ sleep 10m " make the test less flaky in Nvim
+ call writefile(["line1\r", "line2\r"], 'Xchanged_r')
+ let g:reason = ''
+ checktime
+ call assert_equal('changed', g:reason)
+ call assert_equal(&fileformat, 'unix')
+ call assert_equal("line1\r", getline(1))
+ call assert_equal("line2\r", getline(2))
+ %s/\r
+ write
+
+ " File format changed, reload with 'ff', etc
+ augroup testreload
+ au!
+ au FileChangedShell Xchanged_r let g:reason = v:fcs_reason | let v:fcs_choice = 'edit'
+ augroup END
+ call assert_equal(&fileformat, 'unix')
+ sleep 10m " make the test less flaky in Nvim
+ call writefile(["line1\r", "line2\r"], 'Xchanged_r')
+ let g:reason = ''
+ checktime
+ call assert_equal('changed', g:reason)
+ call assert_equal(&fileformat, 'dos')
+ call assert_equal('line1', getline(1))
+ call assert_equal('line2', getline(2))
+ set fileformat=unix
+ write
+
+ au! testreload
+ bwipe!
+ call delete(undofile('Xchanged_r'))
+ call delete('Xchanged_r')
+endfunc
+
+func Test_FileChangedShell_edit_dialog()
+ throw 'Skipped: requires a UI to be active'
+ CheckNotGui
+ CheckUnix " Using low level feedkeys() does not work on MS-Windows.
+
+ new Xchanged_r
+ call setline(1, 'reload this')
+ set fileformat=unix
+ write
+
+ " File format changed, reload (content only) via prompt
+ augroup testreload
+ au!
+ au FileChangedShell Xchanged_r let g:reason = v:fcs_reason | let v:fcs_choice = 'ask'
+ augroup END
+ call assert_equal(&fileformat, 'unix')
+ call writefile(["line1\r", "line2\r"], 'Xchanged_r')
+ let g:reason = ''
+ call feedkeys('L', 'L') " load file content only
+ checktime
+ call assert_equal('changed', g:reason)
+ call assert_equal(&fileformat, 'unix')
+ call assert_equal("line1\r", getline(1))
+ call assert_equal("line2\r", getline(2))
+ %s/\r
+ write
+
+ " File format changed, reload (file and options) via prompt
+ augroup testreload
+ au!
+ au FileChangedShell Xchanged_r let g:reason = v:fcs_reason | let v:fcs_choice = 'ask'
+ augroup END
+ call assert_equal(&fileformat, 'unix')
+ call writefile(["line1\r", "line2\r"], 'Xchanged_r')
+ let g:reason = ''
+ call feedkeys('a', 'L') " load file content and options
+ checktime
+ call assert_equal('changed', g:reason)
+ call assert_equal(&fileformat, 'dos')
+ call assert_equal("line1", getline(1))
+ call assert_equal("line2", getline(2))
+ set fileformat=unix
+ write
+
+ au! testreload
+ bwipe!
+ call delete(undofile('Xchanged_r'))
+ call delete('Xchanged_r')
+endfunc
+
func Test_file_changed_dialog()
- throw 'skipped: TODO: '
- if !has('unix') || has('gui_running')
- return
- endif
+ throw 'Skipped: requires a UI to be active'
+ CheckUnix
+ CheckNotGui
au! FileChangedShell
new Xchanged_d
@@ -139,7 +237,7 @@ func Test_file_changed_dialog()
sleep 2
silent !touch Xchanged_d
let v:warningmsg = ''
- checktime
+ checktime Xchanged_d
call assert_equal('', v:warningmsg)
call assert_equal(1, line('$'))
call assert_equal('new line', getline(1))
@@ -147,3 +245,19 @@ func Test_file_changed_dialog()
bwipe!
call delete('Xchanged_d')
endfunc
+
+" Test for editing a new buffer from a FileChangedShell autocmd
+func Test_FileChangedShell_newbuf()
+ call writefile(['one', 'two'], 'Xfile')
+ new Xfile
+ augroup testnewbuf
+ autocmd FileChangedShell * enew
+ augroup END
+ sleep 10m " make the test less flaky in Nvim
+ call writefile(['red'], 'Xfile')
+ call assert_fails('checktime', 'E811:')
+ au! testnewbuf
+ call delete('Xfile')
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim
index 6e92baa939..59b272d563 100644
--- a/src/nvim/testdir/test_filetype.vim
+++ b/src/nvim/testdir/test_filetype.vim
@@ -76,15 +76,18 @@ let s:filename_checks = {
\ 'ave': ['file.ave'],
\ 'awk': ['file.awk', 'file.gawk'],
\ 'b': ['file.mch', 'file.ref', 'file.imp'],
- \ 'bzl': ['file.bazel', 'file.bzl', 'WORKSPACE'],
+ \ 'basic': ['file.bas', 'file.bi', 'file.bm'],
\ 'bc': ['file.bc'],
\ 'bdf': ['file.bdf'],
- \ 'bib': ['file.bib'],
\ 'beancount': ['file.beancount'],
+ \ 'bib': ['file.bib'],
+ \ 'bicep': ['file.bicep'],
\ 'bindzone': ['named.root', '/bind/db.file', '/named/db.file', 'any/bind/db.file', 'any/named/db.file'],
+ \ 'bitbake': ['file.bb', 'file.bbappend', 'file.bbclass', 'build/conf/local.conf', 'meta/conf/layer.conf', 'build/conf/bbappend.conf', 'meta-layer/conf/distro/foo.conf'],
\ 'blank': ['file.bl'],
\ 'bsdl': ['file.bsd', 'file.bsdl', 'bsd', 'some-bsd'],
\ 'bst': ['file.bst'],
+ \ 'bzl': ['file.bazel', 'file.bzl', 'WORKSPACE'],
\ 'bzr': ['bzr_log.any', 'bzr_log.file'],
\ 'c': ['enlightenment/file.cfg', 'file.qc', 'file.c', 'some-enlightenment/file.cfg'],
\ 'cabal': ['file.cabal'],
@@ -97,7 +100,7 @@ let s:filename_checks = {
\ 'cdrtoc': ['file.toc'],
\ 'cf': ['file.cfm', 'file.cfi', 'file.cfc'],
\ 'cfengine': ['cfengine.conf'],
- \ 'cfg': ['file.cfg', 'file.hgrc', 'filehgrc', 'hgrc', 'some-hgrc'],
+ \ 'cfg': ['file.hgrc', 'filehgrc', 'hgrc', 'some-hgrc'],
\ 'ch': ['file.chf'],
\ 'chaiscript': ['file.chai'],
\ 'chaskell': ['file.chs'],
@@ -107,13 +110,16 @@ let s:filename_checks = {
\ 'clean': ['file.dcl', 'file.icl'],
\ 'clojure': ['file.clj', 'file.cljs', 'file.cljx', 'file.cljc'],
\ 'cmake': ['CMakeLists.txt', 'file.cmake', 'file.cmake.in'],
+ \ 'cmod': ['file.cmod'],
\ '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'],
+ \ 'config': ['configure.in', 'configure.ac', '/etc/hostname.file', 'any/etc/hostname.file'],
+ \ 'confini': ['/etc/pacman.conf', 'any/etc/pacman.conf', 'mpv.conf'],
\ 'context': ['tex/context/any/file.tex', 'file.mkii', 'file.mkiv', 'file.mkvi', 'file.mkxl', 'file.mklx'],
+ \ 'cook': ['file.cook'],
\ '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'],
@@ -123,6 +129,7 @@ let s:filename_checks = {
\ 'csp': ['file.csp', 'file.fdr'],
\ 'css': ['file.css'],
\ 'cterm': ['file.con'],
+ \ 'csv': ['file.csv'],
\ 'cucumber': ['file.feature'],
\ 'cuda': ['file.cu', 'file.cuh'],
\ 'cupl': ['file.pld'],
@@ -130,6 +137,7 @@ let s:filename_checks = {
\ 'cvs': ['cvs123'],
\ 'cvsrc': ['.cvsrc'],
\ 'cynpp': ['file.cyn'],
+ \ 'd': ['file.d'],
\ 'dart': ['file.dart', 'file.drt'],
\ 'datascript': ['file.ds'],
\ 'dcd': ['file.dcd'],
@@ -146,12 +154,13 @@ let s:filename_checks = {
\ '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'],
+ \ 'dockerfile': ['Containerfile', 'Dockerfile', 'dockerfile', 'file.Dockerfile', 'file.dockerfile', 'Dockerfile.debian', 'Containerfile.something'],
+ \ 'dosbatch': ['file.bat'],
+ \ 'dosini': ['.editorconfig', '/etc/yum.conf', 'file.ini', 'npmrc', '.npmrc', 'php.ini', 'php.ini-5', 'php.ini-file', '/etc/yum.repos.d/file', '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'],
+ \ 'dtrace': ['/usr/lib/dtrace/io.d'],
\ 'dts': ['file.dts', 'file.dtsi'],
\ 'dune': ['jbuild', 'dune', 'dune-project', 'dune-workspace'],
\ 'dylan': ['file.dylan'],
@@ -159,11 +168,12 @@ let s:filename_checks = {
\ 'dylanlid': ['file.lid'],
\ 'ecd': ['file.ecd'],
\ 'edif': ['file.edf', 'file.edif', 'file.edo'],
+ \ 'eelixir': ['file.eex', 'file.leex'],
\ 'elinks': ['elinks.conf'],
\ 'elixir': ['file.ex', 'file.exs', 'mix.lock'],
- \ 'eelixir': ['file.eex', 'file.leex'],
\ 'elm': ['file.elm'],
\ 'elmfilt': ['filter-rules'],
+ \ 'elvish': ['file.elv'],
\ 'epuppet': ['file.epp'],
\ 'erlang': ['file.erl', 'file.hrl', 'file.yaws'],
\ 'eruby': ['file.erb', 'file.rhtml'],
@@ -182,66 +192,70 @@ let s:filename_checks = {
\ 'fgl': ['file.4gl', 'file.4gh', 'file.m4gl'],
\ 'fish': ['file.fish'],
\ 'focexec': ['file.fex', 'file.focexec'],
+ \ 'form': ['file.frm'],
\ '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'],
+ \ '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'],
+ \ 'gdb': ['.gdbinit', 'gdbinit', 'file.gdb', '.config/gdbearlyinit', '.gdbearlyinit'],
\ 'gdmo': ['file.mo', 'file.gdmo'],
+ \ 'gdresource': ['file.tscn', 'file.tres'],
+ \ 'gdscript': ['file.gd'],
\ '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', 'NOTES_EDITMSG', 'EDIT_DESCRIPTION'],
- \ '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'],
+ \ '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', 'any/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'],
+ \ 'gleam': ['file.gleam'],
+ \ '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'],
+ \ 'hare': ['file.ha'],
\ 'haskell': ['file.hs', 'file.hsc', 'file.hs-boot', 'file.hsig'],
\ 'haste': ['file.ht'],
\ 'hastepreproc': ['file.htpp'],
\ 'hb': ['file.hb'],
+ \ 'hcl': ['file.hcl'],
+ \ 'heex': ['file.heex'],
\ 'hercules': ['file.vc', 'file.ev', 'file.sum', 'file.errsum'],
\ 'hex': ['file.hex', 'file.h32'],
\ 'hgcommit': ['hg-editor-file.txt'],
+ \ 'hjson': ['file.hjson'],
\ 'hog': ['file.hog', 'snort.conf', 'vision.conf'],
\ 'hollywood': ['file.hws'],
+ \ 'hoon': ['file.hoon'],
\ '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'],
+ \ 'html': ['file.html', 'file.htm', 'file.cshtml'],
\ 'htmlm4': ['file.html.m4'],
\ 'httest': ['file.htt', 'file.htb'],
+ \ 'i3config': ['/home/user/.i3/config', '/home/user/.config/i3/config', '/etc/i3/config', '/etc/xdg/i3/config'],
\ 'ibasic': ['file.iba', 'file.ibi'],
\ 'icemenu': ['/.icewm/menu', 'any/.icewm/menu'],
\ 'icon': ['file.icn'],
@@ -258,12 +272,14 @@ let s:filename_checks = {
\ 'java': ['file.java', 'file.jav'],
\ 'javacc': ['file.jj', 'file.jjt'],
\ 'javascript': ['file.js', 'file.javascript', 'file.es', 'file.mjs', 'file.cjs'],
+ \ 'javascript.glimmer': ['file.gjs'],
\ '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'],
+ \ 'json5': ['file.json5'],
\ 'jsonc': ['file.jsonc'],
\ 'jsp': ['file.jsp'],
\ 'julia': ['file.jl'],
@@ -271,12 +287,14 @@ let s:filename_checks = {
\ 'kivy': ['file.kv'],
\ 'kix': ['file.kix'],
\ 'kotlin': ['file.kt', 'file.ktm', 'file.kts'],
+ \ 'krl': ['file.sub', 'file.Sub', 'file.SUB'],
\ 'kscript': ['file.ks'],
\ 'kwt': ['file.k'],
\ 'lace': ['file.ace', 'file.ACE'],
\ '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'],
@@ -284,22 +302,23 @@ let s:filename_checks = {
\ 'libao': ['/etc/libao.conf', '/.libao', 'any/.libao', 'any/etc/libao.conf'],
\ 'lifelines': ['file.ll'],
\ 'lilo': ['lilo.conf', 'lilo.conf-file'],
+ \ 'lilypond': ['file.ly', 'file.ily'],
\ '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'],
+ \ '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'],
\ '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'],
+ \ 'lpc': ['file.lpc', 'file.ulpc'],
\ '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'],
@@ -314,6 +333,9 @@ let s:filename_checks = {
\ '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'],
+ \ 'matlab': ['file.m'],
+ \ 'maxima': ['file.demo', 'file.dmt', 'file.dm1', 'file.dm2', 'file.dm3',
+ \ 'file.wxm', 'maxima-init.mac'],
\ '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',
@@ -332,8 +354,10 @@ let s:filename_checks = {
\ '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'],
+ \ 'modula3': ['file.m3', 'file.mg', 'file.i3', 'file.ig'],
\ 'monk': ['file.isc', 'file.monk', 'file.ssc', 'file.tsc'],
\ 'moo': ['file.moo'],
+ \ 'moonscript': ['file.moon'],
\ 'mp': ['file.mp'],
\ 'mplayerconf': ['mplayer.conf', '/.mplayer/config', 'any/.mplayer/config'],
\ 'mrxvtrc': ['mrxvtrc', '.mrxvtrc'],
@@ -346,10 +370,13 @@ let s:filename_checks = {
\ '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'],
+ \ 'natural': ['file.NSA', 'file.NSC', 'file.NSG', 'file.NSL', 'file.NSM', 'file.NSN', 'file.NSP', 'file.NSS'],
\ 'ncf': ['file.ncf'],
+ \ 'neomuttrc': ['Neomuttrc', '.neomuttrc', '.neomuttrc-file', '/.neomutt/neomuttrc', '/.neomutt/neomuttrc-file', 'Neomuttrc', 'Neomuttrc-file', 'any/.neomutt/neomuttrc', 'any/.neomutt/neomuttrc-file', 'neomuttrc', 'neomuttrc-file'],
\ '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'],
@@ -360,7 +387,10 @@ let s:filename_checks = {
\ 'omnimark': ['file.xom', 'file.xin'],
\ 'opam': ['opam', 'file.opam', 'file.opam.template'],
\ 'openroad': ['file.or'],
+ \ 'openscad': ['file.scad'],
+ \ 'opl': ['file.OPL', 'file.OPl', 'file.OpL', 'file.Opl', 'file.oPL', 'file.oPl', 'file.opL', 'file.opl'],
\ 'ora': ['file.ora'],
+ \ 'org': ['file.org', 'file.org_archive'],
\ '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'],
@@ -368,14 +398,13 @@ let s:filename_checks = {
\ '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'],
+ \ 'pcmk': ['file.pcmk'],
\ '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'],
+ \ 'php': ['file.php', 'file.php9', 'file.phtml', 'file.ctp', 'file.phpt'],
\ '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'],
@@ -391,6 +420,7 @@ let s:filename_checks = {
\ 'povini': ['.povrayrc'],
\ 'ppd': ['file.ppd'],
\ 'ppwiz': ['file.it', 'file.ih'],
+ \ 'prisma': ['file.prisma'],
\ 'privoxy': ['file.action'],
\ 'proc': ['file.pc'],
\ 'procmail': ['.procmail', '.procmailrc'],
@@ -402,74 +432,84 @@ 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'],
+ \ 'r': ['file.r'],
\ 'radiance': ['file.rad', 'file.mat'],
\ 'raku': ['file.pm6', 'file.p6', 'file.t6', 'file.pod6', 'file.raku', 'file.rakumod', 'file.rakudoc', 'file.rakutest'],
+ \ 'raml': ['file.raml'],
\ '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'],
+ \ 'remind': ['.reminders', 'file.remind', 'file.rem', '.reminders-file'],
+ \ 'rescript': ['file.res', 'file.resi'],
\ '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'],
+ \ 'rhelp': ['file.rd'],
\ 'rib': ['file.rib'],
+ \ 'rmd': ['file.rmd', 'file.smd'],
\ 'rnc': ['file.rnc'],
\ 'rng': ['file.rng'],
+ \ 'rnoweb': ['file.rnw', 'file.snw'],
+ \ 'robot': ['file.robot', 'file.resource'],
\ 'robots': ['robots.txt'],
\ 'routeros': ['file.rsc'],
\ 'rpcgen': ['file.x'],
\ 'rpl': ['file.rpl'],
+ \ 'rrst': ['file.rrst', 'file.srst'],
\ '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'],
+ \ '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', 'Vagrantfile'],
\ 'rust': ['file.rs'],
\ 'samba': ['smb.conf'],
\ 'sas': ['file.sas'],
\ 'sass': ['file.sass'],
\ 'sather': ['file.sa'],
\ 'sbt': ['file.sbt'],
- \ 'scala': ['file.scala', 'file.sc'],
+ \ 'scala': ['file.scala'],
\ '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'],
+ \ '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'],
+ \ 'sexplib': ['file.sexp'],
\ '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'],
+ \ 'sil': ['file.sil'],
\ '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'],
+ \ 'solidity': ['file.sol'],
+ \ 'solution': ['file.sln'],
\ 'sparql': ['file.rq', 'file.sparql'],
\ 'spec': ['file.spec'],
\ 'spice': ['file.sp', 'file.spice'],
@@ -487,11 +527,13 @@ 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'],
+ \ 'supercollider': ['file.quark'],
+ \ 'surface': ['file.sface'],
+ \ 'svelte': ['file.svelte'],
\ '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'],
@@ -500,8 +542,11 @@ 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'],
+ \ 'template': ['file.tmpl'],
\ '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 +554,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'],
@@ -519,7 +565,9 @@ let s:filename_checks = {
\ 'tsscl': ['file.tsscl'],
\ 'tssgm': ['file.tssgm'],
\ 'tssop': ['file.tssop'],
+ \ 'tsv': ['file.tsv'],
\ 'twig': ['file.twig'],
+ \ 'typescript.glimmer': ['file.gts'],
\ 'typescriptreact': ['file.tsx'],
\ 'uc': ['file.uc'],
\ 'udevconf': ['/etc/udev/udev.conf', 'any/etc/udev/udev.conf'],
@@ -533,6 +581,7 @@ let s:filename_checks = {
\ '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'],
+ \ 'vala': ['file.vala'],
\ 'vb': ['file.sba', 'file.vb', 'file.vbs', 'file.dsm', 'file.ctl'],
\ 'vera': ['file.vr', 'file.vri', 'file.vrh'],
\ 'verilog': ['file.v'],
@@ -549,18 +598,19 @@ let s:filename_checks = {
\ 'wast': ['file.wast', 'file.wat'],
\ 'webmacro': ['file.wm'],
\ 'wget': ['.wgetrc', 'wgetrc'],
+ \ 'wget2': ['.wget2rc', 'wget2rc'],
\ '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'],
+ \ 'xf86conf': ['xorg.conf', 'xorg.conf-4'],
\ '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'],
@@ -569,7 +619,7 @@ let s:filename_checks = {
\ 'xslt': ['file.xsl', 'file.xslt'],
\ 'yacc': ['file.yy', 'file.yxx', 'file.y++'],
\ 'yaml': ['file.yaml', 'file.yml'],
- \ 'raml': ['file.raml'],
+ \ 'yang': ['file.yang'],
\ 'z8a': ['file.z8a'],
\ 'zig': ['file.zig'],
\ 'zimbu': ['file.zu'],
@@ -580,7 +630,7 @@ let s:filename_checks = {
\ }
let s:filename_case_checks = {
- \ 'modula2': ['file.DEF', 'file.MOD'],
+ \ 'modula2': ['file.DEF'],
\ 'bzl': ['file.BUILD', 'BUILD'],
\ }
@@ -653,12 +703,13 @@ 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']],
\ 'wml': [['#!/path/wml']],
- \ 'scheme': [['#!/path/scheme']],
+ \ 'scheme': [['#!/path/scheme'],
+ \ ['#!/path/guile']],
\ 'cfengine': [['#!/path/cfengine']],
\ 'erlang': [['#!/path/escript']],
\ 'haskell': [['#!/path/haskell']],
@@ -670,6 +721,7 @@ let s:script_checks = {
\ 'routeros': [['#!/path/rsc']],
\ 'fish': [['#!/path/fish']],
\ 'forth': [['#!/path/gforth']],
+ \ 'icon': [['#!/path/icon']],
\ }
" Various forms of "env" optional arguments.
@@ -706,83 +758,252 @@ func Test_setfiletype_completion()
call assert_equal('"setfiletype java javacc javascript javascriptreact', @:)
endfunc
-func Test_hook_file()
+" Test for ':filetype detect' command for a buffer without a file
+func Test_emptybuf_ftdetect()
+ new
+ call setline(1, '#!/bin/sh')
+ call assert_equal('', &filetype)
+ filetype detect
+ call assert_equal('sh', &filetype)
+ close!
+endfunc
+
+" Test for ':filetype indent on' and ':filetype indent off' commands
+func Test_filetype_indent_off()
+ new Xtest.vim
+ filetype indent on
+ call assert_equal(1, g:did_indent_on)
+ call assert_equal(['filetype detection:ON plugin:OFF indent:ON'],
+ \ execute('filetype')->split("\n"))
+ filetype indent off
+ call assert_equal(0, exists('g:did_indent_on'))
+ call assert_equal(['filetype detection:ON plugin:OFF indent:OFF'],
+ \ execute('filetype')->split("\n"))
+ close
+endfunc
+
+"""""""""""""""""""""""""""""""""""""""""""""""""
+" Tests for specific extensions and filetypes.
+" Keep sorted.
+"""""""""""""""""""""""""""""""""""""""""""""""""
+
+func Test_bas_file()
filetype on
- call writefile(['[Trigger]', 'this is pacman config'], 'Xfile.hook')
- split Xfile.hook
- call assert_equal('dosini', &filetype)
+ call writefile(['looks like BASIC'], 'Xfile.bas')
+ split Xfile.bas
+ call assert_equal('basic', &filetype)
bwipe!
- call writefile(['not pacman'], 'Xfile.hook')
- split Xfile.hook
- call assert_notequal('dosini', &filetype)
+ " Test dist#ft#FTbas()
+
+ let g:filetype_bas = 'freebasic'
+ split Xfile.bas
+ call assert_equal('freebasic', &filetype)
bwipe!
+ unlet g:filetype_bas
- call delete('Xfile.hook')
+ " 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"', 'Enum Foo', 'End Enum'], 'Xfile.bas')
+ split Xfile.bas
+ call assert_equal('vb', &filetype)
+ bwipe!
+
+ call delete('Xfile.bas')
filetype off
endfunc
-func Test_ts_file()
+" Test dist#ft#FTcfg()
+func Test_cfg_file()
filetype on
- call writefile(['<?xml version="1.0" encoding="utf-8"?>'], 'Xfile.ts')
- split Xfile.ts
- call assert_equal('xml', &filetype)
+ " *.cfg defaults to cfg
+ call writefile(['looks like cfg'], 'cfgfile.cfg')
+ split cfgfile.cfg
+ call assert_equal('cfg', &filetype)
+
+ let g:filetype_cfg = 'other'
+ edit
+ call assert_equal('other', &filetype)
+ bwipe!
+ unlet g:filetype_cfg
+
+ " RAPID cfg
+ let ext = 'cfg'
+ for i in ['EIO', 'MMC', 'MOC', 'PROC', 'SIO', 'SYS']
+ call writefile([i .. ':CFG'], 'cfgfile.' .. ext)
+ execute "split cfgfile." .. ext
+ call assert_equal('rapid', &filetype)
+ bwipe!
+ call delete('cfgfile.' .. ext)
+ " check different case of file extension
+ let ext = substitute(ext, '\(\l\)', '\u\1', '')
+ endfor
+
+ " clean up
+ filetype off
+endfunc
+
+func Test_d_file()
+ filetype on
+
+ call writefile(['looks like D'], 'Xfile.d')
+ split Xfile.d
+ call assert_equal('d', &filetype)
bwipe!
- call writefile(['// looks like Typescript'], 'Xfile.ts')
- split Xfile.ts
- call assert_equal('typescript', &filetype)
+ call writefile(['#!/some/bin/dtrace'], 'Xfile.d')
+ split Xfile.d
+ call assert_equal('dtrace', &filetype)
+ bwipe!
+
+ call writefile(['#pragma D option'], 'Xfile.d')
+ split Xfile.d
+ call assert_equal('dtrace', &filetype)
+ bwipe!
+
+ call writefile([':some:thing:'], 'Xfile.d')
+ split Xfile.d
+ call assert_equal('dtrace', &filetype)
+ bwipe!
+
+ call writefile(['module this', '#pragma D option'], 'Xfile.d')
+ split Xfile.d
+ call assert_equal('d', &filetype)
+ bwipe!
+
+ call writefile(['import that', '#pragma D option'], 'Xfile.d')
+ split Xfile.d
+ call assert_equal('d', &filetype)
bwipe!
- call delete('Xfile.hook')
filetype off
endfunc
-func Test_ttl_file()
+func Test_dat_file()
filetype on
- call writefile(['@base <http://example.org/> .'], 'Xfile.ttl')
- split Xfile.ttl
- call assert_equal('turtle', &filetype)
+ " KRL header start with "&WORD", but is not always present.
+ call writefile(['&ACCESS'], 'datfile.dat')
+ split datfile.dat
+ call assert_equal('krl', &filetype)
bwipe!
+ call delete('datfile.dat')
- call writefile(['looks like Tera Term Language'], 'Xfile.ttl')
- split Xfile.ttl
- call assert_equal('teraterm', &filetype)
+ " KRL defdat with leading spaces, for KRL file extension is not case
+ " sensitive.
+ call writefile([' DEFDAT datfile'], 'datfile.Dat')
+ split datfile.Dat
+ call assert_equal('krl', &filetype)
bwipe!
+ call delete('datfile.Dat')
+
+ " KRL defdat with embedded spaces, file starts with empty line(s).
+ call writefile(['', 'defdat datfile public'], 'datfile.DAT')
+ split datfile.DAT
+ call assert_equal('krl', &filetype)
+ bwipe!
+
+ " User may overrule file inspection
+ let g:filetype_dat = 'dat'
+ split datfile.DAT
+ call assert_equal('dat', &filetype)
+ bwipe!
+ call delete('datfile.DAT')
+ unlet g:filetype_dat
- call delete('Xfile.ttl')
filetype off
endfunc
-func Test_pp_file()
+func Test_dep3patch_file()
filetype on
- call writefile(['looks like puppet'], 'Xfile.pp')
- split Xfile.pp
- call assert_equal('puppet', &filetype)
+ call assert_true(mkdir('debian/patches', 'p'))
+
+ " series files are not patches
+ call writefile(['Description: some awesome patch'], 'debian/patches/series')
+ split debian/patches/series
+ call assert_notequal('dep3patch', &filetype)
bwipe!
- let g:filetype_pp = 'pascal'
- split Xfile.pp
- call assert_equal('pascal', &filetype)
+ " diff/patch files without the right headers should still show up as ft=diff
+ call writefile([], 'debian/patches/foo.diff')
+ split debian/patches/foo.diff
+ call assert_equal('diff', &filetype)
bwipe!
- unlet g:filetype_pp
- " Test dist#ft#FTpp()
- call writefile(['{ pascal comment'], 'Xfile.pp')
- split Xfile.pp
- call assert_equal('pascal', &filetype)
+ " Files with the right headers are detected as dep3patch, even if they don't
+ " have a diff/patch extension
+ call writefile(['Subject: dep3patches'], 'debian/patches/bar')
+ split debian/patches/bar
+ call assert_equal('dep3patch', &filetype)
bwipe!
- call writefile(['procedure pascal'], 'Xfile.pp')
- split Xfile.pp
- call assert_equal('pascal', &filetype)
+ " Files in sub-directories are detected
+ call assert_true(mkdir('debian/patches/s390x', 'p'))
+ call writefile(['Subject: dep3patches'], 'debian/patches/s390x/bar')
+ split debian/patches/s390x/bar
+ call assert_equal('dep3patch', &filetype)
bwipe!
- call delete('Xfile.pp')
+ " The detection stops when seeing the "header end" marker
+ call writefile(['---', 'Origin: the cloud'], 'debian/patches/baz')
+ split debian/patches/baz
+ call assert_notequal('dep3patch', &filetype)
+ bwipe!
+
+ call delete('debian', 'rf')
+endfunc
+
+func Test_dsl_file()
+ filetype on
+
+ call writefile([' <!doctype dsssl-spec ['], 'dslfile.dsl')
+ split dslfile.dsl
+ call assert_equal('dsl', &filetype)
+ bwipe!
+
+ call writefile(['workspace {'], 'dslfile.dsl')
+ split dslfile.dsl
+ call assert_equal('structurizr', &filetype)
+ bwipe!
+
+ call delete('dslfile.dsl')
filetype off
endfunc
@@ -823,20 +1044,183 @@ func Test_ex_file()
filetype off
endfunc
-func Test_dsl_file()
+func Test_foam_file()
filetype on
+ call assert_true(mkdir('0', 'p'))
+ call assert_true(mkdir('0.orig', 'p'))
- call writefile([' <!doctype dsssl-spec ['], 'dslfile.dsl')
- split dslfile.dsl
- call assert_equal('dsl', &filetype)
+ call writefile(['FoamFile {', ' object something;'], 'Xfile1Dict')
+ split Xfile1Dict
+ call assert_equal('foam', &filetype)
bwipe!
- call writefile(['workspace {'], 'dslfile.dsl')
- split dslfile.dsl
- call assert_equal('structurizr', &filetype)
+ call writefile(['FoamFile {', ' object something;'], 'Xfile1Dict.something')
+ split Xfile1Dict.something
+ call assert_equal('foam', &filetype)
bwipe!
- call delete('dslfile.dsl')
+ 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')
+ call delete('Xfile1Dict')
+ call delete('Xfile1Dict.something')
+ call delete('XfileProperties')
+ call delete('XfileProperties.something')
+ filetype off
+endfunc
+
+func Test_frm_file()
+ filetype on
+
+ call writefile(['looks like FORM'], 'Xfile.frm')
+ split Xfile.frm
+ call assert_equal('form', &filetype)
+ bwipe!
+
+ " Test dist#ft#FTfrm()
+
+ let g:filetype_frm = 'form'
+ split Xfile.frm
+ call assert_equal('form', &filetype)
+ bwipe!
+ unlet g:filetype_frm
+
+ " Visual Basic
+
+ call writefile(['Begin VB.Form Form1'], 'Xfile.frm')
+ split Xfile.frm
+ call assert_equal('vb', &filetype)
+ bwipe!
+
+ call delete('Xfile.frm')
+ filetype off
+endfunc
+
+func Test_fs_file()
+ filetype on
+
+ call writefile(['looks like F#'], 'Xfile.fs')
+ split Xfile.fs
+ call assert_equal('fsharp', &filetype)
+ bwipe!
+
+ let g:filetype_fs = 'forth'
+ split Xfile.fs
+ call assert_equal('forth', &filetype)
+ bwipe!
+ unlet g:filetype_fs
+
+ " Test dist#ft#FTfs()
+
+ " Forth (Gforth)
+
+ call writefile(['( Forth inline comment )'], 'Xfile.fs')
+ split Xfile.fs
+ call assert_equal('forth', &filetype)
+ bwipe!
+
+ call writefile(['.( Forth displayed inline comment )'], 'Xfile.fs')
+ split Xfile.fs
+ call assert_equal('forth', &filetype)
+ bwipe!
+
+ call writefile(['\ Forth line comment'], 'Xfile.fs')
+ split Xfile.fs
+ call assert_equal('forth', &filetype)
+ bwipe!
+
+ " empty line comment - no space required
+ call writefile(['\'], 'Xfile.fs')
+ split Xfile.fs
+ call assert_equal('forth', &filetype)
+ bwipe!
+
+ call writefile(['\G Forth documentation comment '], 'Xfile.fs')
+ split Xfile.fs
+ call assert_equal('forth', &filetype)
+ bwipe!
+
+ call writefile([': squared ( n -- n^2 )', 'dup * ;'], 'Xfile.fs')
+ split Xfile.fs
+ call assert_equal('forth', &filetype)
+ bwipe!
+
+ call delete('Xfile.fs')
+ filetype off
+endfunc
+
+func Test_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_hook_file()
+ filetype on
+
+ call writefile(['[Trigger]', 'this is pacman config'], 'Xfile.hook')
+ split Xfile.hook
+ call assert_equal('conf', &filetype)
+ bwipe!
+
+ call writefile(['not pacman'], 'Xfile.hook')
+ split Xfile.hook
+ call assert_notequal('conf', &filetype)
+ bwipe!
+
+ call delete('Xfile.hook')
filetype off
endfunc
@@ -940,209 +1324,547 @@ func Test_m_file()
filetype off
endfunc
-func Test_xpm_file()
+func Test_mod_file()
filetype on
- call writefile(['this is XPM2'], 'file.xpm')
- split file.xpm
- call assert_equal('xpm2', &filetype)
+ " *.mod defaults to Modsim III
+ call writefile(['locks like Modsim III'], 'modfile.mod')
+ split modfile.mod
+ call assert_equal('modsim3', &filetype)
bwipe!
- call delete('file.xpm')
+ " Users preference set by g:filetype_mod
+ let g:filetype_mod = 'lprolog'
+ split modfile.mod
+ call assert_equal('lprolog', &filetype)
+ unlet g:filetype_mod
+ bwipe!
+
+ " RAPID header start with a line containing only "%%%",
+ " but is not always present.
+ call writefile(['%%%'], 'modfile.mod')
+ split modfile.mod
+ call assert_equal('rapid', &filetype)
+ bwipe!
+ call delete('modfile.mod')
+
+ " RAPID supports umlauts in module names, leading spaces,
+ " the .mod extension is not case sensitive.
+ call writefile([' module ÜmlautModule'], 'modfile.Mod')
+ split modfile.Mod
+ call assert_equal('rapid', &filetype)
+ bwipe!
+ call delete('modfile.Mod')
+
+ " RAPID is not case sensitive, embedded spaces, sysmodule,
+ " file starts with empty line(s).
+ call writefile(['', 'MODULE rapidmödüle (SYSMODULE,NOSTEPIN)'], 'modfile.MOD')
+ split modfile.MOD
+ call assert_equal('rapid', &filetype)
+ bwipe!
+
+ " Modula-2 MODULE not start of line
+ call writefile(['IMPLEMENTATION MODULE Module2Mod;'], 'modfile.MOD')
+ split modfile.MOD
+ call assert_equal('modula2', &filetype)
+ bwipe!
+
+ " Modula-2 with comment and empty lines prior MODULE
+ call writefile(['', '(* with', ' comment *)', '', 'MODULE Module2Mod;'], 'modfile.MOD')
+ split modfile.MOD
+ call assert_equal('modula2', &filetype)
+ bwipe!
+ call delete('modfile.MOD')
+
+ " LambdaProlog module
+ call writefile(['module lpromod.'], 'modfile.mod')
+ split modfile.mod
+ call assert_equal('lprolog', &filetype)
+ bwipe!
+
+ " LambdaProlog with comment and empty lines prior module
+ call writefile(['', '% with', '% comment', '', 'module lpromod.'], 'modfile.mod')
+ split modfile.mod
+ call assert_equal('lprolog', &filetype)
+ bwipe!
+ call delete('modfile.mod')
+
+ " go.mod
+ call writefile(['module example.com/M'], 'go.mod')
+ split go.mod
+ call assert_equal('gomod', &filetype)
+ bwipe!
+ call delete('go.mod')
+
filetype off
endfunc
-func Test_fs_file()
+func Test_patch_file()
filetype on
- call writefile(['looks like F#'], 'Xfile.fs')
- split Xfile.fs
- call assert_equal('fsharp', &filetype)
+ call writefile([], 'Xfile.patch')
+ split Xfile.patch
+ call assert_equal('diff', &filetype)
bwipe!
- let g:filetype_fs = 'forth'
- split Xfile.fs
- call assert_equal('forth', &filetype)
+ call writefile(['From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001'], 'Xfile.patch')
+ split Xfile.patch
+ call assert_equal('gitsendemail', &filetype)
bwipe!
- unlet g:filetype_fs
- " Test dist#ft#FTfs()
+ call writefile(['From 0000000000000000000000000000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001'], 'Xfile.patch')
+ split Xfile.patch
+ call assert_equal('gitsendemail', &filetype)
+ bwipe!
- " Forth (Gforth)
+ call delete('Xfile.patch')
+ filetype off
+endfunc
- call writefile(['( Forth inline comment )'], 'Xfile.fs')
- split Xfile.fs
- call assert_equal('forth', &filetype)
+func Test_perl_file()
+ filetype on
+
+ " only tests one case, should do more
+ let lines =<< trim END
+
+ use a
+ END
+ call writefile(lines, "Xfile.t")
+ split Xfile.t
+ call assert_equal('perl', &filetype)
+ bwipe
+
+ call delete('Xfile.t')
+ filetype off
+endfunc
+
+func Test_pp_file()
+ filetype on
+
+ call writefile(['looks like puppet'], 'Xfile.pp')
+ split Xfile.pp
+ call assert_equal('puppet', &filetype)
bwipe!
- call writefile(['.( Forth displayed inline comment )'], 'Xfile.fs')
- split Xfile.fs
- call assert_equal('forth', &filetype)
+ let g:filetype_pp = 'pascal'
+ split Xfile.pp
+ call assert_equal('pascal', &filetype)
bwipe!
+ unlet g:filetype_pp
- call writefile(['\ Forth line comment'], 'Xfile.fs')
- split Xfile.fs
- call assert_equal('forth', &filetype)
+ " Test dist#ft#FTpp()
+ call writefile(['{ pascal comment'], 'Xfile.pp')
+ split Xfile.pp
+ call assert_equal('pascal', &filetype)
bwipe!
- " empty line comment - no space required
- call writefile(['\'], 'Xfile.fs')
- split Xfile.fs
- call assert_equal('forth', &filetype)
+ call writefile(['procedure pascal'], 'Xfile.pp')
+ split Xfile.pp
+ call assert_equal('pascal', &filetype)
bwipe!
- call writefile(['\G Forth documentation comment '], 'Xfile.fs')
- split Xfile.fs
- call assert_equal('forth', &filetype)
+ call delete('Xfile.pp')
+ filetype off
+endfunc
+
+" Test dist#ft#FTprg()
+func Test_prg_file()
+ filetype on
+
+ " *.prg defaults to clipper
+ call writefile(['looks like clipper'], 'prgfile.prg')
+ split prgfile.prg
+ call assert_equal('clipper', &filetype)
bwipe!
- call writefile([': squared ( n -- n^2 )', 'dup * ;'], 'Xfile.fs')
- split Xfile.fs
- call assert_equal('forth', &filetype)
+ " Users preference set by g:filetype_prg
+ let g:filetype_prg = 'eviews'
+ split prgfile.prg
+ call assert_equal('eviews', &filetype)
+ unlet g:filetype_prg
bwipe!
- call delete('Xfile.fs')
+ " RAPID header start with a line containing only "%%%",
+ " but is not always present.
+ call writefile(['%%%'], 'prgfile.prg')
+ split prgfile.prg
+ call assert_equal('rapid', &filetype)
+ bwipe!
+ call delete('prgfile.prg')
+
+ " RAPID supports umlauts in module names, leading spaces,
+ " the .prg extension is not case sensitive.
+ call writefile([' module ÜmlautModule'], 'prgfile.Prg')
+ split prgfile.Prg
+ call assert_equal('rapid', &filetype)
+ bwipe!
+ call delete('prgfile.Prg')
+
+ " RAPID is not case sensitive, embedded spaces, sysmodule,
+ " file starts with empty line(s).
+ call writefile(['', 'MODULE rapidmödüle (SYSMODULE,NOSTEPIN)'], 'prgfile.PRG')
+ split prgfile.PRG
+ call assert_equal('rapid', &filetype)
+ bwipe!
+ call delete('prgfile.PRG')
+
filetype off
endfunc
-func Test_dep3patch_file()
+" Test dist#ft#FTsc()
+func Test_sc_file()
filetype on
- call assert_true(mkdir('debian/patches', 'p'))
+ " SC file methods are defined 'Class : Method'
+ call writefile(['SCNvimDocRenderer : SCDocHTMLRenderer {'], 'srcfile.sc')
+ split srcfile.sc
+ call assert_equal('supercollider', &filetype)
+ bwipe!
+ call delete('srcfile.sc')
- " series files are not patches
- call writefile(['Description: some awesome patch'], 'debian/patches/series')
- split debian/patches/series
- call assert_notequal('dep3patch', &filetype)
+ " SC classes are defined with '+ Class {}'
+ call writefile(['+ SCNvim {', '*methodArgs {|method|'], 'srcfile.sc')
+ split srcfile.sc
+ call assert_equal('supercollider', &filetype)
bwipe!
+ call delete('srcfile.sc')
- " diff/patch files without the right headers should still show up as ft=diff
- call writefile([], 'debian/patches/foo.diff')
- split debian/patches/foo.diff
- call assert_equal('diff', &filetype)
+ " Some SC class files start with comment and define methods many lines later
+ call writefile(['// Query', '//Method','^this {'], 'srcfile.sc')
+ split srcfile.sc
+ call assert_equal('supercollider', &filetype)
bwipe!
+ call delete('srcfile.sc')
- " Files with the right headers are detected as dep3patch, even if they don't
- " have a diff/patch extension
- call writefile(['Subject: dep3patches'], 'debian/patches/bar')
- split debian/patches/bar
- call assert_equal('dep3patch', &filetype)
+ " Some SC class files put comments between method declaration after class
+ call writefile(['PingPong {', '//comment','*ar { arg'], 'srcfile.sc')
+ split srcfile.sc
+ call assert_equal('supercollider', &filetype)
bwipe!
+ call delete('srcfile.sc')
- " Files in sub-directories are detected
- call assert_true(mkdir('debian/patches/s390x', 'p'))
- call writefile(['Subject: dep3patches'], 'debian/patches/s390x/bar')
- split debian/patches/s390x/bar
- call assert_equal('dep3patch', &filetype)
+ filetype off
+endfunc
+
+" Test dist#ft#FTscd()
+func Test_scd_file()
+ filetype on
+
+ call writefile(['ijq(1)'], 'srcfile.scd')
+ split srcfile.scd
+ call assert_equal('scdoc', &filetype)
bwipe!
+ call delete('srcfile.scd')
- " The detection stops when seeing the "header end" marker
- call writefile(['---', 'Origin: the cloud'], 'debian/patches/baz')
- split debian/patches/baz
- call assert_notequal('dep3patch', &filetype)
+ filetype off
+endfunc
+
+func Test_src_file()
+ filetype on
+
+ " KRL header start with "&WORD", but is not always present.
+ call writefile(['&ACCESS'], 'srcfile.src')
+ split srcfile.src
+ call assert_equal('krl', &filetype)
bwipe!
+ call delete('srcfile.src')
- call delete('debian', 'rf')
+ " KRL def with leading spaces, for KRL file extension is not case sensitive.
+ call writefile([' DEF srcfile()'], 'srcfile.Src')
+ split srcfile.Src
+ call assert_equal('krl', &filetype)
+ bwipe!
+ call delete('srcfile.Src')
+
+ " KRL global deffct with embedded spaces, file starts with empty line(s).
+ for text in ['global def srcfile()', 'global deffct srcfile()']
+ call writefile(['', text], 'srcfile.SRC')
+ split srcfile.SRC
+ call assert_equal('krl', &filetype, text)
+ bwipe!
+ endfor
+
+ " User may overrule file inspection
+ let g:filetype_src = 'src'
+ split srcfile.SRC
+ call assert_equal('src', &filetype)
+ bwipe!
+ call delete('srcfile.SRC')
+ unlet g:filetype_src
+
+ filetype off
endfunc
-func Test_patch_file()
+func Test_sys_file()
filetype on
- call writefile([], 'Xfile.patch')
- split Xfile.patch
- call assert_equal('diff', &filetype)
+ " *.sys defaults to Batch file for MSDOS
+ call writefile(['looks like dos batch'], 'sysfile.sys')
+ split sysfile.sys
+ call assert_equal('bat', &filetype)
bwipe!
- call writefile(['From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001'], 'Xfile.patch')
- split Xfile.patch
- call assert_equal('gitsendemail', &filetype)
+ " Users preference set by g:filetype_sys
+ let g:filetype_sys = 'sys'
+ split sysfile.sys
+ call assert_equal('sys', &filetype)
+ unlet g:filetype_sys
bwipe!
- call writefile(['From 0000000000000000000000000000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001'], 'Xfile.patch')
- split Xfile.patch
- call assert_equal('gitsendemail', &filetype)
+ " RAPID header start with a line containing only "%%%",
+ " but is not always present.
+ call writefile(['%%%'], 'sysfile.sys')
+ split sysfile.sys
+ call assert_equal('rapid', &filetype)
bwipe!
+ call delete('sysfile.sys')
+
+ " RAPID supports umlauts in module names, leading spaces,
+ " the .sys extension is not case sensitive.
+ call writefile([' module ÜmlautModule'], 'sysfile.Sys')
+ split sysfile.Sys
+ call assert_equal('rapid', &filetype)
+ bwipe!
+ call delete('sysfile.Sys')
+
+ " RAPID is not case sensitive, embedded spaces, sysmodule,
+ " file starts with empty line(s).
+ call writefile(['', 'MODULE rapidmödüle (SYSMODULE,NOSTEPIN)'], 'sysfile.SYS')
+ split sysfile.SYS
+ call assert_equal('rapid', &filetype)
+ bwipe!
+ call delete('sysfile.SYS')
- call delete('Xfile.patch')
filetype off
endfunc
-func Test_git_file()
+func Test_tex_file()
filetype on
- call assert_true(mkdir('Xrepo.git', 'p'))
+ " only tests one case, should do more
+ let lines =<< trim END
+ % This is a sentence.
- call writefile([], 'Xrepo.git/HEAD')
- split Xrepo.git/HEAD
+ This is a sentence.
+ END
+ call writefile(lines, "Xfile.tex")
+ split Xfile.tex
+ call assert_equal('plaintex', &filetype)
+ bwipe
+
+ call delete('Xfile.tex')
+ 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
+
+ call writefile(['<?xml version="1.0" encoding="utf-8"?>'], 'Xfile.ts')
+ split Xfile.ts
+ call assert_equal('xml', &filetype)
+ bwipe!
+
+ call writefile(['// looks like Typescript'], 'Xfile.ts')
+ split Xfile.ts
+ call assert_equal('typescript', &filetype)
+ bwipe!
+
+ call delete('Xfile.ts')
+ filetype off
+endfunc
+
+func Test_ttl_file()
+ filetype on
+
+ call writefile(['@base <http://example.org/> .'], 'Xfile.ttl')
+ split Xfile.ttl
+ call assert_equal('turtle', &filetype)
+ bwipe!
+
+ call writefile(['looks like Tera Term Language'], 'Xfile.ttl')
+ split Xfile.ttl
+ call assert_equal('teraterm', &filetype)
+ bwipe!
+
+ call delete('Xfile.ttl')
+ filetype off
+endfunc
+
+func Test_xpm_file()
+ filetype on
+
+ call writefile(['this is XPM2'], 'file.xpm')
+ split file.xpm
+ call assert_equal('xpm2', &filetype)
+ bwipe!
+
+ call delete('file.xpm')
+ filetype off
+endfunc
+
+func Test_cls_file()
+ filetype on
+
+ call writefile(['looks like Smalltalk'], 'Xfile.cls')
+ split Xfile.cls
+ call assert_equal('st', &filetype)
+ bwipe!
+
+ " Test dist#ft#FTcls()
+
+ let g:filetype_cls = 'vb'
+ split Xfile.cls
+ call assert_equal('vb', &filetype)
+ bwipe!
+ unlet g:filetype_cls
+
+ " TeX
+
+ call writefile(['%'], 'Xfile.cls')
+ split Xfile.cls
+ call assert_equal('tex', &filetype)
+ bwipe!
+
+ " Rexx
+
+ call writefile(['# rexx'], 'Xfile.cls')
+ split Xfile.cls
+ call assert_equal('rexx', &filetype)
+ bwipe!
+
+ " Visual Basic
+
+ call writefile(['VERSION 1.0 CLASS'], 'Xfile.cls')
+ split Xfile.cls
+ call assert_equal('vb', &filetype)
+ bwipe!
+
+ call delete('Xfile.cls')
+ filetype off
+endfunc
+
+func Test_sig_file()
+ filetype on
+
+ call writefile(['this is neither Lambda Prolog nor SML'], 'Xfile.sig')
+ split Xfile.sig
call assert_equal('', &filetype)
bwipe!
- call writefile(['0000000000000000000000000000000000000000'], 'Xrepo.git/HEAD')
- split Xrepo.git/HEAD
- call assert_equal('git', &filetype)
+ " Test dist#ft#FTsig()
+
+ let g:filetype_sig = 'sml'
+ split Xfile.sig
+ call assert_equal('sml', &filetype)
bwipe!
+ unlet g:filetype_sig
- call writefile(['0000000000000000000000000000000000000000000000000000000000000000'], 'Xrepo.git/HEAD')
- split Xrepo.git/HEAD
- call assert_equal('git', &filetype)
+ " Lambda Prolog
+
+ call writefile(['sig foo.'], 'Xfile.sig')
+ split Xfile.sig
+ call assert_equal('lprolog', &filetype)
bwipe!
- call writefile(['ref: refs/heads/master'], 'Xrepo.git/HEAD')
- split Xrepo.git/HEAD
- call assert_equal('git', &filetype)
+ call writefile(['/* ... */'], 'Xfile.sig')
+ split Xfile.sig
+ call assert_equal('lprolog', &filetype)
bwipe!
- call delete('Xrepo.git', 'rf')
+ call writefile(['% ...'], 'Xfile.sig')
+ split Xfile.sig
+ call assert_equal('lprolog', &filetype)
+ bwipe!
+
+ " SML signature file
+
+ call writefile(['signature FOO ='], 'Xfile.sig')
+ split Xfile.sig
+ call assert_equal('sml', &filetype)
+ bwipe!
+
+ call writefile(['structure FOO ='], 'Xfile.sig')
+ split Xfile.sig
+ call assert_equal('sml', &filetype)
+ bwipe!
+
+ call writefile(['(* ... *)'], 'Xfile.sig')
+ split Xfile.sig
+ call assert_equal('sml', &filetype)
+ bwipe!
+
+ call delete('Xfile.sig')
filetype off
endfunc
-func Test_foam_file()
+func Test_inc_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)
+ call writefile(['this is the fallback'], 'Xfile.inc')
+ split Xfile.inc
+ call assert_equal('pov', &filetype)
bwipe!
- call writefile(['FoamFile {', ' object something;'], 'Xfile1Dict.something')
- split Xfile1Dict.something
- call assert_equal('foam', &filetype)
+ let g:filetype_inc = 'foo'
+ split Xfile.inc
+ call assert_equal('foo', &filetype)
bwipe!
+ unlet g:filetype_inc
- call writefile(['FoamFile {', ' object something;'], 'XfileProperties')
- split XfileProperties
- call assert_equal('foam', &filetype)
+ " aspperl
+ call writefile(['perlscript'], 'Xfile.inc')
+ split Xfile.inc
+ call assert_equal('aspperl', &filetype)
bwipe!
- call writefile(['FoamFile {', ' object something;'], 'XfileProperties.something')
- split XfileProperties.something
- call assert_equal('foam', &filetype)
+ " aspvbs
+ call writefile(['<% something'], 'Xfile.inc')
+ split Xfile.inc
+ call assert_equal('aspvbs', &filetype)
bwipe!
- call writefile(['FoamFile {', ' object something;'], 'XfileProperties')
- split XfileProperties
- call assert_equal('foam', &filetype)
+ " php
+ call writefile(['<?php'], 'Xfile.inc')
+ split Xfile.inc
+ call assert_equal('php', &filetype)
bwipe!
- call writefile(['FoamFile {', ' object something;'], 'XfileProperties.something')
- split XfileProperties.something
- call assert_equal('foam', &filetype)
+ " pascal
+ call writefile(['program'], 'Xfile.inc')
+ split Xfile.inc
+ call assert_equal('pascal', &filetype)
bwipe!
- call writefile(['FoamFile {', ' object something;'], '0/Xfile')
- split 0/Xfile
- call assert_equal('foam', &filetype)
+ " bitbake
+ call writefile(['require foo'], 'Xfile.inc')
+ split Xfile.inc
+ call assert_equal('bitbake', &filetype)
bwipe!
- call writefile(['FoamFile {', ' object something;'], '0.orig/Xfile')
- split 0.orig/Xfile
- call assert_equal('foam', &filetype)
+ " asm
+ call writefile(['asmsyntax=bar'], 'Xfile.inc')
+ split Xfile.inc
+ call assert_equal('bar', &filetype)
bwipe!
- call delete('0', 'rf')
- call delete('0.orig', 'rf')
+ call delete('Xfile.inc')
filetype off
endfunc
diff --git a/src/nvim/testdir/test_filetype_lua.vim b/src/nvim/testdir/test_filetype_lua.vim
deleted file mode 100644
index f73e4ca33f..0000000000
--- a/src/nvim/testdir/test_filetype_lua.vim
+++ /dev/null
@@ -1,2 +0,0 @@
-let g:do_filetype_lua = 1
-source test_filetype.vim
diff --git a/src/nvim/testdir/test_filter_cmd.vim b/src/nvim/testdir/test_filter_cmd.vim
index 0c45db049b..dae164b11c 100644
--- a/src/nvim/testdir/test_filter_cmd.vim
+++ b/src/nvim/testdir/test_filter_cmd.vim
@@ -45,6 +45,14 @@ func Test_filter_fails()
call assert_fails('filter /pat', 'E476:')
call assert_fails('filter /pat/', 'E476:')
call assert_fails('filter /pat/ asdf', 'E492:')
+ " Using assert_fails() causes E476 instead of E866. So use a try-catch.
+ let caught_e866 = 0
+ try
+ filter /\@>b/ ls
+ catch /E866:/
+ let caught_e866 = 1
+ endtry
+ call assert_equal(1, caught_e866)
call assert_fails('filter!', 'E471:')
call assert_fails('filter! pat', 'E476:')
@@ -145,3 +153,38 @@ func Test_filter_commands()
bwipe! file.h
bwipe! file.hs
endfunc
+
+func Test_filter_display()
+ edit Xdoesnotmatch
+ let @a = '!!willmatch'
+ let @b = '!!doesnotmatch'
+ let @c = "oneline\ntwoline\nwillmatch\n"
+ let @/ = '!!doesnotmatch'
+ call feedkeys(":echo '!!doesnotmatch:'\<CR>", 'ntx')
+ let lines = map(split(execute('filter /willmatch/ display'), "\n"), 'v:val[5:6]')
+
+ call assert_true(index(lines, '"a') >= 0)
+ call assert_false(index(lines, '"b') >= 0)
+ call assert_true(index(lines, '"c') >= 0)
+ call assert_false(index(lines, '"/') >= 0)
+ call assert_false(index(lines, '":') >= 0)
+ call assert_false(index(lines, '"%') >= 0)
+
+ let lines = map(split(execute('filter /doesnotmatch/ display'), "\n"), 'v:val[5:6]')
+ call assert_true(index(lines, '"a') < 0)
+ call assert_false(index(lines, '"b') < 0)
+ call assert_true(index(lines, '"c') < 0)
+ call assert_false(index(lines, '"/') < 0)
+ call assert_false(index(lines, '":') < 0)
+ call assert_false(index(lines, '"%') < 0)
+
+ bwipe!
+endfunc
+
+func Test_filter_scriptnames()
+ let lines = split(execute('filter /test_filter_cmd/ scriptnames'), "\n")
+ call assert_equal(1, len(lines))
+ call assert_match('filter_cmd', lines[0])
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
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_findfile.vim b/src/nvim/testdir/test_findfile.vim
index 5a20475d3d..0f4b30aec2 100644
--- a/src/nvim/testdir/test_findfile.vim
+++ b/src/nvim/testdir/test_findfile.vim
@@ -193,12 +193,14 @@ func Test_find_cmd()
set path=.,./**/*
call CreateFiles()
cd Xdir1
+
" Test for :find
find foo
call assert_equal('foo', expand('%:.'))
2find foo
call assert_equal('Xdir2/foo', expand('%:.'))
call assert_fails('3find foo', 'E347:')
+
" Test for :sfind
enew
sfind barfoo
@@ -207,6 +209,7 @@ func Test_find_cmd()
close
call assert_fails('sfind baz', 'E345:')
call assert_equal(2, winnr('$'))
+
" Test for :tabfind
enew
tabfind foobar
@@ -215,7 +218,8 @@ func Test_find_cmd()
tabclose
call assert_fails('tabfind baz', 'E345:')
call assert_equal(1, tabpagenr('$'))
- " call chdir(save_dir)
+
+ call chdir(save_dir)
exe 'cd ' . save_dir
call CleanFiles()
let &path = save_path
@@ -226,4 +230,26 @@ func Test_find_cmd()
call assert_fails('tabfind', 'E471:')
endfunc
+func Test_find_non_existing_path()
+ new
+ let save_path = &path
+ let save_dir = getcwd()
+ call mkdir('dir1/dir2', 'p')
+ call writefile([], 'dir1/file.txt')
+ call writefile([], 'dir1/dir2/base.txt')
+ call chdir('dir1/dir2')
+ e base.txt
+ set path=../include
+
+ call assert_fails(':find file.txt', 'E345:')
+
+ call chdir(save_dir)
+ bw!
+ call delete('dir1/dir2/base.txt', 'rf')
+ call delete('dir1/dir2', 'rf')
+ call delete('dir1/file.txt', 'rf')
+ call delete('dir1', 'rf')
+ let &path = save_path
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_float_func.vim b/src/nvim/testdir/test_float_func.vim
index 1e0c75c49d..902a011a9d 100644
--- a/src/nvim/testdir/test_float_func.vim
+++ b/src/nvim/testdir/test_float_func.vim
@@ -1,8 +1,7 @@
" test float functions
-if !has('float')
- finish
-end
+source check.vim
+CheckFeature float
func Test_abs()
call assert_equal('1.23', string(abs(1.23)))
diff --git a/src/nvim/testdir/test_fnamemodify.vim b/src/nvim/testdir/test_fnamemodify.vim
index 411f7ebbb3..5ae2a5ee17 100644
--- a/src/nvim/testdir/test_fnamemodify.vim
+++ b/src/nvim/testdir/test_fnamemodify.vim
@@ -3,8 +3,10 @@
func Test_fnamemodify()
let save_home = $HOME
let save_shell = &shell
+ let save_shellslash = &shellslash
let $HOME = fnamemodify('.', ':p:h:h')
set shell=sh
+ set shellslash
call assert_equal('/', fnamemodify('.', ':p')[-1:])
call assert_equal('r', fnamemodify('.', ':p:h')[-1:])
@@ -27,6 +29,21 @@ func Test_fnamemodify()
call assert_equal('fb2.tar.gz', fnamemodify('abc.fb2.tar.gz', ':e:e:e'))
call assert_equal('fb2.tar.gz', fnamemodify('abc.fb2.tar.gz', ':e:e:e:e'))
call assert_equal('tar', fnamemodify('abc.fb2.tar.gz', ':e:e:r'))
+ call assert_equal(getcwd(), fnamemodify('', ':p:h'))
+
+ let cwd = getcwd()
+ call chdir($HOME)
+ call assert_equal('foobar', fnamemodify('~/foobar', ':~:.'))
+ call chdir(cwd)
+ call mkdir($HOME . '/XXXXXXXX/a', 'p')
+ call mkdir($HOME . '/XXXXXXXX/b', 'p')
+ call chdir($HOME . '/XXXXXXXX/a/')
+ call assert_equal('foo', fnamemodify($HOME . '/XXXXXXXX/a/foo', ':p:~:.'))
+ call assert_equal('~/XXXXXXXX/b/foo', fnamemodify($HOME . '/XXXXXXXX/b/foo', ':p:~:.'))
+ call mkdir($HOME . '/XXXXXXXX/a.ext', 'p')
+ call assert_equal('~/XXXXXXXX/a.ext/foo', fnamemodify($HOME . '/XXXXXXXX/a.ext/foo', ':p:~:.'))
+ call chdir(cwd)
+ call delete($HOME . '/XXXXXXXX', 'rf')
call assert_equal('''abc def''', fnamemodify('abc def', ':S'))
call assert_equal('''abc" "def''', fnamemodify('abc" "def', ':S'))
@@ -44,6 +61,7 @@ func Test_fnamemodify()
let $HOME = save_home
let &shell = save_shell
+ let &shellslash = save_shellslash
endfunc
func Test_fnamemodify_er()
@@ -73,6 +91,7 @@ func Test_fnamemodify_er()
call assert_equal('b.c', fnamemodify('a.b.c.d.e', ':r:r:e:e:e'))
call assert_equal('b.c', fnamemodify('a.b.c.d.e', ':r:r:e:e:e:e'))
+ call assert_equal('', fnamemodify('', ':p:t'))
call assert_equal('', fnamemodify(v:_null_string, v:_null_string))
endfunc
diff --git a/src/nvim/testdir/test_fold.vim b/src/nvim/testdir/test_fold.vim
index 6da1b3d4a0..327f0f73f2 100644
--- a/src/nvim/testdir/test_fold.vim
+++ b/src/nvim/testdir/test_fold.vim
@@ -217,6 +217,26 @@ func Test_update_folds_expr_read()
set foldmethod& foldexpr&
endfunc
+" Test for what patch 8.1.0535 fixes.
+func Test_foldexpr_no_interrupt_addsub()
+ new
+ func! FoldFunc()
+ call setpos('.', getcurpos())
+ return '='
+ endfunc
+
+ set foldmethod=expr
+ set foldexpr=FoldFunc()
+ call setline(1, '1.2')
+
+ exe "norm! $\<C-A>"
+ call assert_equal('1.3', getline(1))
+
+ bwipe!
+ delfunc FoldFunc
+ set foldmethod& foldexpr&
+endfunc
+
func Check_foldlevels(expected)
call assert_equal(a:expected, map(range(1, line('$')), 'foldlevel(v:val)'))
endfunc
@@ -881,4 +901,92 @@ func Test_fold_relative_move()
set fdm& sw& wrap& tw&
endfunc
+" Make sure a fold containing a nested fold is split correctly when using
+" foldmethod=indent
+func Test_fold_split()
+ new
+ let lines =<< trim END
+ line 1
+ line 2
+ line 3
+ line 4
+ line 5
+ END
+ call setline(1, lines)
+ setlocal sw=2
+ setlocal foldmethod=indent foldenable
+ call assert_equal([0, 1, 1, 2, 2], range(1, 5)->map('foldlevel(v:val)'))
+ call append(2, 'line 2.5')
+ call assert_equal([0, 1, 0, 1, 2, 2], range(1, 6)->map('foldlevel(v:val)'))
+ bw!
+endfunc
+
+" Make sure that when you append under a blank line that is under a fold with
+" the same indent level as your appended line, the fold expands across the
+" blank line
+func Test_indent_append_under_blank_line()
+ new
+ let lines =<< trim END
+ line 1
+ line 2
+ line 3
+ END
+ call setline(1, lines)
+ setlocal sw=2
+ setlocal foldmethod=indent foldenable
+ call assert_equal([0, 1, 1], range(1, 3)->map('foldlevel(v:val)'))
+ call append(3, '')
+ call append(4, ' line 5')
+ call assert_equal([0, 1, 1, 1, 1], range(1, 5)->map('foldlevel(v:val)'))
+ bw!
+endfunc
+
+" Make sure that when you delete 1 line of a fold whose length is 2 lines, the
+" fold can't be closed since its length (1) is now less than foldminlines.
+func Test_indent_one_line_fold_close()
+ let lines =<< trim END
+ line 1
+ line 2
+ line 3
+ END
+
+ new
+ setlocal sw=2 foldmethod=indent
+ call setline(1, lines)
+ " open all folds, delete line, then close all folds
+ normal zR
+ 3delete
+ normal zM
+ call assert_equal(-1, foldclosed(2)) " the fold should not be closed
+
+ " Now do the same, but delete line 2 this time; this covers different code.
+ " (Combining this code with the above code doesn't expose both bugs.)
+ 1,$delete
+ call setline(1, lines)
+ normal zR
+ 2delete
+ normal zM
+ call assert_equal(-1, foldclosed(2))
+ bw!
+endfunc
+
+" Make sure that when appending [an indented line then a blank line] right
+" before a single indented line, the resulting extended fold can be closed
+func Test_indent_append_blank_small_fold_close()
+ new
+ setlocal sw=2 foldmethod=indent
+ " at first, the fold at the second line can't be closed since it's smaller
+ " than foldminlines
+ let lines =<< trim END
+ line 1
+ line 4
+ END
+ call setline(1, lines)
+ call append(1, [' line 2', ''])
+ " close all folds
+ normal zM
+ call assert_notequal(-1, foldclosed(2)) " the fold should be closed now
+ bw!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index 0edbeb420a..9b8d740efb 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -185,7 +185,9 @@ func Test_str2nr()
call assert_fails('call str2nr([])', 'E730:')
call assert_fails('call str2nr({->2})', 'E729:')
- call assert_fails('call str2nr(1.2)', 'E806:')
+ if has('float')
+ call assert_fails('call str2nr(1.2)', 'E806:')
+ endif
call assert_fails('call str2nr(10, [])', 'E474:')
endfunc
@@ -325,42 +327,18 @@ func Test_simplify()
call assert_equal('./file', simplify('./dir/../file'))
call assert_equal('../dir/file', simplify('dir/../../dir/file'))
call assert_equal('./file', simplify('dir/.././file'))
+ call assert_equal('../dir', simplify('./../dir'))
+ call assert_equal('..', simplify('../testdir/..'))
+ call mkdir('Xdir')
+ call assert_equal('.', simplify('Xdir/../.'))
+ call delete('Xdir', 'd')
call assert_fails('call simplify({->0})', 'E729:')
call assert_fails('call simplify([])', 'E730:')
call assert_fails('call simplify({})', 'E731:')
- call assert_fails('call simplify(1.2)', 'E806:')
-endfunc
-
-func Test_setbufvar_options()
- " This tests that aucmd_prepbuf() and aucmd_restbuf() properly restore the
- " window layout.
- call assert_equal(1, winnr('$'))
- split dummy_preview
- resize 2
- set winfixheight winfixwidth
- let prev_id = win_getid()
-
- wincmd j
- let wh = winheight(0)
- let dummy_buf = bufnr('dummy_buf1', v:true)
- call setbufvar(dummy_buf, '&buftype', 'nofile')
- execute 'belowright vertical split #' . dummy_buf
- call assert_equal(wh, winheight(0))
- let dum1_id = win_getid()
-
- wincmd h
- let wh = winheight(0)
- let dummy_buf = bufnr('dummy_buf2', v:true)
- eval 'nofile'->setbufvar(dummy_buf, '&buftype')
- execute 'belowright vertical split #' . dummy_buf
- call assert_equal(wh, winheight(0))
-
- bwipe!
- call win_gotoid(prev_id)
- bwipe!
- call win_gotoid(dum1_id)
- bwipe!
+ if has('float')
+ call assert_fails('call simplify(1.2)', 'E806:')
+ endif
endfunc
func Test_pathshorten()
@@ -376,6 +354,25 @@ func Test_pathshorten()
call assert_equal('~.f/bar', pathshorten('~.foo/bar'))
call assert_equal('.~f/bar', pathshorten('.~foo/bar'))
call assert_equal('~/f/bar', pathshorten('~/foo/bar'))
+ call assert_fails('call pathshorten([])', 'E730:')
+
+ " test pathshorten with optional variable to set preferred size of shortening
+ call assert_equal('', pathshorten('', 2))
+ call assert_equal('foo', pathshorten('foo', 2))
+ call assert_equal('/foo', pathshorten('/foo', 2))
+ call assert_equal('fo/', pathshorten('foo/', 2))
+ call assert_equal('fo/bar', pathshorten('foo/bar', 2))
+ call assert_equal('fo/ba/foobar', pathshorten('foo/bar/foobar', 2))
+ call assert_equal('/fo/ba/foobar', pathshorten('/foo/bar/foobar', 2))
+ call assert_equal('.fo/bar', pathshorten('.foo/bar', 2))
+ call assert_equal('~fo/bar', pathshorten('~foo/bar', 2))
+ call assert_equal('~.fo/bar', pathshorten('~.foo/bar', 2))
+ call assert_equal('.~fo/bar', pathshorten('.~foo/bar', 2))
+ call assert_equal('~/fo/bar', pathshorten('~/foo/bar', 2))
+ call assert_fails('call pathshorten([],2)', 'E730:')
+ call assert_notequal('~/fo/bar', pathshorten('~/foo/bar', 3))
+ call assert_equal('~/foo/bar', pathshorten('~/foo/bar', 3))
+ call assert_equal('~/f/bar', pathshorten('~/foo/bar', 0))
endfunc
func Test_strpart()
@@ -1192,6 +1189,55 @@ func Test_col()
bw!
endfunc
+" Test for input()
+func Test_input_func()
+ " Test for prompt with multiple lines
+ redir => v
+ call feedkeys(":let c = input(\"A\\nB\\nC\\n? \")\<CR>B\<CR>", 'xt')
+ redir END
+ call assert_equal("B", c)
+ call assert_equal(['A', 'B', 'C'], split(v, "\n"))
+
+ " Test for default value
+ call feedkeys(":let c = input('color? ', 'red')\<CR>\<CR>", 'xt')
+ call assert_equal('red', c)
+
+ " Test for completion at the input prompt
+ func! Tcomplete(arglead, cmdline, pos)
+ return "item1\nitem2\nitem3"
+ endfunc
+ call feedkeys(":let c = input('Q? ', '', 'custom,Tcomplete')\<CR>"
+ \ .. "\<C-A>\<CR>", 'xt')
+ delfunc Tcomplete
+ call assert_equal('item1 item2 item3', c)
+
+ " Test for using special characters as default input
+ call feedkeys(":let c = input('name? ', \"x\\<BS>y\")\<CR>\<CR>", 'xt')
+ call assert_equal('y', c)
+
+ " Test for using text with composing characters as default input
+ call feedkeys(":let c = input('name? ', \"ã̳\")\<CR>\<CR>", 'xt')
+ call assert_equal('ã̳', c)
+
+ " Test for using <CR> as default input
+ call feedkeys(":let c = input('name? ', \"\\<CR>\")\<CR>x\<CR>", 'xt')
+ call assert_equal(' x', c)
+
+ call assert_fails("call input('F:', '', 'invalid')", 'E180:')
+ call assert_fails("call input('F:', '', [])", 'E730:')
+endfunc
+
+" Test for the inputdialog() function
+func Test_inputdialog()
+ CheckNotGui
+
+ call feedkeys(":let v=inputdialog('Q:', 'xx', 'yy')\<CR>\<CR>", 'xt')
+ call assert_equal('xx', v)
+ call feedkeys(":let v=inputdialog('Q:', 'xx', 'yy')\<CR>\<Esc>", 'xt')
+ call assert_equal('yy', v)
+endfunc
+
+" Test for inputlist()
func Test_inputlist()
call feedkeys(":let c = inputlist(['Select color:', '1. red', '2. green', '3. blue'])\<cr>1\<cr>", 'tx')
call assert_equal(1, c)
@@ -1274,6 +1320,37 @@ func Test_shellescape()
let &shell = save_shell
endfunc
+func Test_setbufvar_options()
+ " This tests that aucmd_prepbuf() and aucmd_restbuf() properly restore the
+ " window layout.
+ call assert_equal(1, winnr('$'))
+ split dummy_preview
+ resize 2
+ set winfixheight winfixwidth
+ let prev_id = win_getid()
+
+ wincmd j
+ let wh = winheight(0)
+ let dummy_buf = bufnr('dummy_buf1', v:true)
+ call setbufvar(dummy_buf, '&buftype', 'nofile')
+ execute 'belowright vertical split #' . dummy_buf
+ call assert_equal(wh, winheight(0))
+ let dum1_id = win_getid()
+
+ wincmd h
+ let wh = winheight(0)
+ let dummy_buf = bufnr('dummy_buf2', v:true)
+ eval 'nofile'->setbufvar(dummy_buf, '&buftype')
+ execute 'belowright vertical split #' . dummy_buf
+ call assert_equal(wh, winheight(0))
+
+ bwipe!
+ call win_gotoid(prev_id)
+ bwipe!
+ call win_gotoid(dum1_id)
+ bwipe!
+endfunc
+
func Test_redo_in_nested_functions()
nnoremap g. :set opfunc=Operator<CR>g@
function Operator( type, ... )
@@ -1331,68 +1408,6 @@ func Test_trim()
call assert_equal("x", trim(chars . "x" . chars))
endfunc
-func EditAnotherFile()
- let word = expand('<cword>')
- edit Xfuncrange2
-endfunc
-
-func Test_func_range_with_edit()
- " Define a function that edits another buffer, then call it with a range that
- " is invalid in that buffer.
- call writefile(['just one line'], 'Xfuncrange2')
- new
- eval 10->range()->setline(1)
- write Xfuncrange1
- call assert_fails('5,8call EditAnotherFile()', 'E16:')
-
- call delete('Xfuncrange1')
- call delete('Xfuncrange2')
- bwipe!
-endfunc
-
-func Test_func_exists_on_reload()
- call writefile(['func ExistingFunction()', 'echo "yes"', 'endfunc'], 'Xfuncexists')
- call assert_equal(0, exists('*ExistingFunction'))
- source Xfuncexists
- call assert_equal(1, '*ExistingFunction'->exists())
- " Redefining a function when reloading a script is OK.
- source Xfuncexists
- call assert_equal(1, exists('*ExistingFunction'))
-
- " But redefining in another script is not OK.
- call writefile(['func ExistingFunction()', 'echo "yes"', 'endfunc'], 'Xfuncexists2')
- call assert_fails('source Xfuncexists2', 'E122:')
-
- delfunc ExistingFunction
- call assert_equal(0, exists('*ExistingFunction'))
- call writefile([
- \ 'func ExistingFunction()', 'echo "yes"', 'endfunc',
- \ 'func ExistingFunction()', 'echo "no"', 'endfunc',
- \ ], 'Xfuncexists')
- call assert_fails('source Xfuncexists', 'E122:')
- call assert_equal(1, exists('*ExistingFunction'))
-
- call delete('Xfuncexists2')
- call delete('Xfuncexists')
- delfunc ExistingFunction
-endfunc
-
-sandbox function Fsandbox()
- normal ix
-endfunc
-
-func Test_func_sandbox()
- sandbox let F = {-> 'hello'}
- call assert_equal('hello', F())
-
- sandbox let F = {-> "normal ix\<Esc>"->execute()}
- call assert_fails('call F()', 'E48:')
- unlet F
-
- call assert_fails('call Fsandbox()', 'E48:')
- delfunc Fsandbox
-endfunc
-
" Test for reg_recording() and reg_executing()
func Test_reg_executing_and_recording()
let s:reg_stat = ''
@@ -1494,6 +1509,10 @@ func Test_getchar()
call assert_equal('', getcharstr(0))
call assert_equal('', getcharstr(1))
+ call feedkeys("\<M-F2>", '')
+ call assert_equal("\<M-F2>", getchar(0))
+ call assert_equal(0, getchar(0))
+
call setline(1, 'xxxx')
" call test_setmouse(1, 3)
" let v:mouse_win = 9
@@ -1519,24 +1538,31 @@ func Test_libcall_libcallnr()
let libc = 'msvcrt.dll'
elseif has('mac')
let libc = 'libSystem.B.dylib'
- elseif system('uname -s') =~ 'SunOS'
- " Set the path to libc.so according to the architecture.
- let test_bits = system('file ' . GetVimProg())
- let test_arch = system('uname -p')
- if test_bits =~ '64-bit' && test_arch =~ 'sparc'
- let libc = '/usr/lib/sparcv9/libc.so'
- elseif test_bits =~ '64-bit' && test_arch =~ 'i386'
- let libc = '/usr/lib/amd64/libc.so'
+ elseif executable('ldd')
+ let libc = matchstr(split(system('ldd ' . GetVimProg())), '/libc\.so\>')
+ endif
+ if get(l:, 'libc', '') ==# ''
+ " On Unix, libc.so can be in various places.
+ if has('linux')
+ " There is not documented but regarding the 1st argument of glibc's
+ " dlopen an empty string and nullptr are equivalent, so using an empty
+ " string for the 1st argument of libcall allows to call functions.
+ let libc = ''
+ elseif has('sun')
+ " Set the path to libc.so according to the architecture.
+ let test_bits = system('file ' . GetVimProg())
+ let test_arch = system('uname -p')
+ if test_bits =~ '64-bit' && test_arch =~ 'sparc'
+ let libc = '/usr/lib/sparcv9/libc.so'
+ elseif test_bits =~ '64-bit' && test_arch =~ 'i386'
+ let libc = '/usr/lib/amd64/libc.so'
+ else
+ let libc = '/usr/lib/libc.so'
+ endif
else
- let libc = '/usr/lib/libc.so'
+ " Unfortunately skip this test until a good way is found.
+ return
endif
- elseif system('uname -s') =~ 'OpenBSD'
- let libc = 'libc.so'
- else
- " On Unix, libc.so can be in various places.
- " Interestingly, using an empty string for the 1st argument of libcall
- " allows to call functions from libc which is not documented.
- let libc = ''
endif
if has('win32')
@@ -1559,54 +1585,99 @@ func Test_libcall_libcallnr()
call assert_fails("call libcallnr('Xdoesnotexist_', 'strlen', 'abcd')", 'E364:')
endfunc
-func Test_bufadd_bufload()
- call assert_equal(0, bufexists('someName'))
- let buf = bufadd('someName')
- call assert_notequal(0, buf)
- call assert_equal(1, bufexists('someName'))
- call assert_equal(0, getbufvar(buf, '&buflisted'))
- call assert_equal(0, bufloaded(buf))
- call bufload(buf)
- call assert_equal(1, bufloaded(buf))
- call assert_equal([''], getbufline(buf, 1, '$'))
+sandbox function Fsandbox()
+ normal ix
+endfunc
- let curbuf = bufnr('')
- eval ['some', 'text']->writefile('XotherName')
- let buf = 'XotherName'->bufadd()
- call assert_notequal(0, buf)
- eval 'XotherName'->bufexists()->assert_equal(1)
- call assert_equal(0, getbufvar(buf, '&buflisted'))
- call assert_equal(0, bufloaded(buf))
- eval buf->bufload()
- call assert_equal(1, bufloaded(buf))
- call assert_equal(['some', 'text'], getbufline(buf, 1, '$'))
- call assert_equal(curbuf, bufnr(''))
+func Test_func_sandbox()
+ sandbox let F = {-> 'hello'}
+ call assert_equal('hello', F())
- let buf1 = bufadd('')
- let buf2 = bufadd('')
- call assert_notequal(0, buf1)
- call assert_notequal(0, buf2)
- call assert_notequal(buf1, buf2)
- call assert_equal(1, bufexists(buf1))
- call assert_equal(1, bufexists(buf2))
- call assert_equal(0, bufloaded(buf1))
- exe 'bwipe ' .. buf1
- call assert_equal(0, bufexists(buf1))
- call assert_equal(1, bufexists(buf2))
- exe 'bwipe ' .. buf2
- call assert_equal(0, bufexists(buf2))
+ sandbox let F = {-> "normal ix\<Esc>"->execute()}
+ call assert_fails('call F()', 'E48:')
+ unlet F
- bwipe someName
- bwipe XotherName
- call assert_equal(0, bufexists('someName'))
- call delete('XotherName')
+ call assert_fails('call Fsandbox()', 'E48:')
+ delfunc Fsandbox
endfunc
-func Test_readdir()
- if isdirectory('Xdir')
- call delete('Xdir', 'rf')
+func EditAnotherFile()
+ let word = expand('<cword>')
+ edit Xfuncrange2
+endfunc
+
+func Test_func_range_with_edit()
+ " Define a function that edits another buffer, then call it with a range that
+ " is invalid in that buffer.
+ call writefile(['just one line'], 'Xfuncrange2')
+ new
+ eval 10->range()->setline(1)
+ write Xfuncrange1
+ call assert_fails('5,8call EditAnotherFile()', 'E16:')
+
+ call delete('Xfuncrange1')
+ call delete('Xfuncrange2')
+ bwipe!
+endfunc
+
+func Test_func_exists_on_reload()
+ call writefile(['func ExistingFunction()', 'echo "yes"', 'endfunc'], 'Xfuncexists')
+ call assert_equal(0, exists('*ExistingFunction'))
+ source Xfuncexists
+ call assert_equal(1, '*ExistingFunction'->exists())
+ " Redefining a function when reloading a script is OK.
+ source Xfuncexists
+ call assert_equal(1, exists('*ExistingFunction'))
+
+ " But redefining in another script is not OK.
+ call writefile(['func ExistingFunction()', 'echo "yes"', 'endfunc'], 'Xfuncexists2')
+ call assert_fails('source Xfuncexists2', 'E122:')
+
+ delfunc ExistingFunction
+ call assert_equal(0, exists('*ExistingFunction'))
+ call writefile([
+ \ 'func ExistingFunction()', 'echo "yes"', 'endfunc',
+ \ 'func ExistingFunction()', 'echo "no"', 'endfunc',
+ \ ], 'Xfuncexists')
+ call assert_fails('source Xfuncexists', 'E122:')
+ call assert_equal(1, exists('*ExistingFunction'))
+
+ call delete('Xfuncexists2')
+ call delete('Xfuncexists')
+ delfunc ExistingFunction
+endfunc
+
+func Test_platform_name()
+ " The system matches at most only one name.
+ let names = ['amiga', 'beos', 'bsd', 'hpux', 'linux', 'mac', 'qnx', 'sun', 'vms', 'win32', 'win32unix']
+ call assert_inrange(0, 1, len(filter(copy(names), 'has(v:val)')))
+
+ " Is Unix?
+ call assert_equal(has('beos'), has('beos') && has('unix'))
+ call assert_equal(has('bsd'), has('bsd') && has('unix'))
+ call assert_equal(has('hpux'), has('hpux') && has('unix'))
+ call assert_equal(has('linux'), has('linux') && has('unix'))
+ call assert_equal(has('mac'), has('mac') && has('unix'))
+ call assert_equal(has('qnx'), has('qnx') && has('unix'))
+ call assert_equal(has('sun'), has('sun') && has('unix'))
+ call assert_equal(has('win32'), has('win32') && !has('unix'))
+ call assert_equal(has('win32unix'), has('win32unix') && has('unix'))
+
+ if has('unix') && executable('uname')
+ let uname = system('uname')
+ call assert_equal(uname =~? 'BeOS', has('beos'))
+ " GNU userland on BSD kernels (e.g., GNU/kFreeBSD) don't have BSD defined
+ call assert_equal(uname =~? '\%(GNU/k\w\+\)\@<!BSD\|DragonFly', has('bsd'))
+ call assert_equal(uname =~? 'HP-UX', has('hpux'))
+ call assert_equal(uname =~? 'Linux', has('linux'))
+ call assert_equal(uname =~? 'Darwin', has('mac'))
+ call assert_equal(uname =~? 'QNX', has('qnx'))
+ call assert_equal(uname =~? 'SunOS', has('sun'))
+ call assert_equal(uname =~? 'CYGWIN\|MSYS', has('win32unix'))
endif
+endfunc
+func Test_readdir()
call mkdir('Xdir')
call writefile([], 'Xdir/foo.txt')
call writefile([], 'Xdir/bar.txt')
@@ -1636,6 +1707,30 @@ func Test_readdir()
call delete('Xdir', 'rf')
endfunc
+func Test_delete_rf()
+ call mkdir('Xdir')
+ call writefile([], 'Xdir/foo.txt')
+ call writefile([], 'Xdir/bar.txt')
+ call mkdir('Xdir/[a-1]') " issue #696
+ call writefile([], 'Xdir/[a-1]/foo.txt')
+ call writefile([], 'Xdir/[a-1]/bar.txt')
+ call assert_true(filereadable('Xdir/foo.txt'))
+ call assert_true('Xdir/[a-1]/foo.txt'->filereadable())
+
+ call assert_equal(0, delete('Xdir', 'rf'))
+ call assert_false(filereadable('Xdir/foo.txt'))
+ call assert_false(filereadable('Xdir/[a-1]/foo.txt'))
+
+ if has('unix')
+ call mkdir('Xdir/Xdir2', 'p')
+ silent !chmod 555 Xdir
+ call assert_equal(-1, delete('Xdir/Xdir2', 'rf'))
+ call assert_equal(-1, delete('Xdir', 'rf'))
+ silent !chmod 755 Xdir
+ call assert_equal(0, delete('Xdir', 'rf'))
+ endif
+endfunc
+
func Test_call()
call assert_equal(3, call('len', [123]))
call assert_equal(3, 'len'->call([123]))
@@ -1658,6 +1753,49 @@ func Test_eventhandler()
call assert_equal(0, eventhandler())
endfunc
+func Test_bufadd_bufload()
+ call assert_equal(0, bufexists('someName'))
+ let buf = bufadd('someName')
+ call assert_notequal(0, buf)
+ call assert_equal(1, bufexists('someName'))
+ call assert_equal(0, getbufvar(buf, '&buflisted'))
+ call assert_equal(0, bufloaded(buf))
+ call bufload(buf)
+ call assert_equal(1, bufloaded(buf))
+ call assert_equal([''], getbufline(buf, 1, '$'))
+
+ let curbuf = bufnr('')
+ eval ['some', 'text']->writefile('XotherName')
+ let buf = 'XotherName'->bufadd()
+ call assert_notequal(0, buf)
+ eval 'XotherName'->bufexists()->assert_equal(1)
+ call assert_equal(0, getbufvar(buf, '&buflisted'))
+ call assert_equal(0, bufloaded(buf))
+ eval buf->bufload()
+ call assert_equal(1, bufloaded(buf))
+ call assert_equal(['some', 'text'], getbufline(buf, 1, '$'))
+ call assert_equal(curbuf, bufnr(''))
+
+ let buf1 = bufadd('')
+ let buf2 = bufadd('')
+ call assert_notequal(0, buf1)
+ call assert_notequal(0, buf2)
+ call assert_notequal(buf1, buf2)
+ call assert_equal(1, bufexists(buf1))
+ call assert_equal(1, bufexists(buf2))
+ call assert_equal(0, bufloaded(buf1))
+ exe 'bwipe ' .. buf1
+ call assert_equal(0, bufexists(buf1))
+ call assert_equal(1, bufexists(buf2))
+ exe 'bwipe ' .. buf2
+ call assert_equal(0, bufexists(buf2))
+
+ bwipe someName
+ bwipe XotherName
+ call assert_equal(0, bufexists('someName'))
+ call delete('XotherName')
+endfunc
+
" Test for the eval() function
func Test_eval()
call assert_fails("call eval('5 a')", 'E488:')
@@ -1671,8 +1809,104 @@ func Test_nr2char()
call assert_equal('a', nr2char(97, 1))
call assert_equal('a', nr2char(97, 0))
- call assert_equal("\x80\xfc\b\xf4\x80\xfeX\x80\xfeX\x80\xfeX", eval('"\<M-' .. nr2char(0x100000) .. '>"'))
- call assert_equal("\x80\xfc\b\xfd\x80\xfeX\x80\xfeX\x80\xfeX\x80\xfeX\x80\xfeX", eval('"\<M-' .. nr2char(0x40000000) .. '>"'))
+ call assert_equal("\x80\xfc\b" .. nr2char(0x100000), eval('"\<M-' .. nr2char(0x100000) .. '>"'))
+ call assert_equal("\x80\xfc\b" .. nr2char(0x40000000), eval('"\<M-' .. nr2char(0x40000000) .. '>"'))
+endfunc
+
+" Test for getcurpos() and setpos()
+func Test_getcurpos_setpos()
+ new
+ call setline(1, ['012345678', '012345678'])
+ normal gg6l
+ let sp = getcurpos()
+ normal 0
+ call setpos('.', sp)
+ normal jyl
+ call assert_equal('6', @")
+ call assert_equal(-1, setpos('.', v:_null_list))
+ call assert_equal(-1, setpos('.', {}))
+
+ let winid = win_getid()
+ normal G$
+ let pos = getcurpos()
+ wincmd w
+ call assert_equal(pos, getcurpos(winid))
+
+ wincmd w
+ close!
+
+ call assert_equal(getcurpos(), getcurpos(0))
+ call assert_equal([0, 0, 0, 0, 0], getcurpos(-1))
+ call assert_equal([0, 0, 0, 0, 0], getcurpos(1999))
+endfunc
+
+func Test_getmousepos()
+ enew!
+ call setline(1, "\t\t\t1234")
+ " call test_setmouse(1, 1)
+ call nvim_input_mouse('left', 'press', '', 0, 0, 0)
+ call getchar() " wait for and consume the mouse press
+ call assert_equal(#{
+ \ screenrow: 1,
+ \ screencol: 1,
+ \ winid: win_getid(),
+ \ winrow: 1,
+ \ wincol: 1,
+ \ line: 1,
+ \ column: 1,
+ \ }, getmousepos())
+ " call test_setmouse(1, 25)
+ call nvim_input_mouse('left', 'press', '', 0, 0, 24)
+ call getchar() " wait for and consume the mouse press
+ call assert_equal(#{
+ \ screenrow: 1,
+ \ screencol: 25,
+ \ winid: win_getid(),
+ \ winrow: 1,
+ \ wincol: 25,
+ \ line: 1,
+ \ column: 4,
+ \ }, getmousepos())
+ " call test_setmouse(1, 50)
+ call nvim_input_mouse('left', 'press', '', 0, 0, 49)
+ call getchar() " wait for and consume the mouse press
+ call assert_equal(#{
+ \ screenrow: 1,
+ \ screencol: 50,
+ \ winid: win_getid(),
+ \ winrow: 1,
+ \ wincol: 50,
+ \ line: 1,
+ \ column: 8,
+ \ }, getmousepos())
+
+ " If the mouse is positioned past the last buffer line, "line" and "column"
+ " should act like it's positioned on the last buffer line.
+ " call test_setmouse(2, 25)
+ call nvim_input_mouse('left', 'press', '', 0, 1, 24)
+ call getchar() " wait for and consume the mouse press
+ call assert_equal(#{
+ \ screenrow: 2,
+ \ screencol: 25,
+ \ winid: win_getid(),
+ \ winrow: 2,
+ \ wincol: 25,
+ \ line: 1,
+ \ column: 4,
+ \ }, getmousepos())
+ " call test_setmouse(2, 50)
+ call nvim_input_mouse('left', 'press', '', 0, 1, 49)
+ call getchar() " wait for and consume the mouse press
+ call assert_equal(#{
+ \ screenrow: 2,
+ \ screencol: 50,
+ \ winid: win_getid(),
+ \ winrow: 2,
+ \ wincol: 50,
+ \ line: 1,
+ \ column: 8,
+ \ }, getmousepos())
+ bwipe!
endfunc
func HasDefault(msg = 'msg')
diff --git a/src/nvim/testdir/test_gf.vim b/src/nvim/testdir/test_gf.vim
index 43efd6248e..1569177d66 100644
--- a/src/nvim/testdir/test_gf.vim
+++ b/src/nvim/testdir/test_gf.vim
@@ -19,11 +19,7 @@ func Test_gf_url()
call search("^second")
call search("URL")
call assert_equal("URL://machine.name/tmp/vimtest2b", expand("<cfile>"))
- if has("ebcdic")
- set isf=@,240-249,/,.,-,_,+,,,$,:,~,\
- else
- set isf=@,48-57,/,.,-,_,+,,,$,~,\
- endif
+ set isf=@,48-57,/,.,-,_,+,,,$,~,\
call search("^third")
call search("name")
call assert_equal("URL:\\\\machine.name\\vimtest2c", expand("<cfile>"))
@@ -39,6 +35,13 @@ func Test_gf_url()
call search("URL")
call assert_equal("URL://machine.name:1234?q=vim", expand("<cfile>"))
+ %d
+ call setline(1, "demo://remote_file")
+ wincmd f
+ call assert_equal('demo://remote_file', @%)
+ call assert_equal(2, winnr('$'))
+ close!
+
set isf&vim
enew!
endfunc
@@ -67,20 +70,23 @@ func Test_gF()
call assert_equal('Xfile', bufname('%'))
call assert_equal(2, getcurpos()[1])
+ " jumping to the file/line with CTRL-W_F
+ %bw!
+ edit Xfile1
+ call setline(1, ['one', 'Xfile:4', 'three'])
+ exe "normal 2G\<C-W>F"
+ call assert_equal('Xfile', bufname('%'))
+ call assert_equal(4, getcurpos()[1])
+
set isfname&
call delete('Xfile')
call delete('Xfile2')
- bwipe Xfile
- bwipe Xfile2
+ %bw!
endfunc
" Test for invoking 'gf' on a ${VAR} variable
func Test_gf()
- if has("ebcdic")
- set isfname=@,240-249,/,.,-,_,+,,,$,:,~,{,}
- else
- set isfname=@,48-57,/,.,-,_,+,,,$,:,~,{,}
- endif
+ set isfname=@,48-57,/,.,-,_,+,,,$,:,~,{,}
call writefile(["Test for gf command"], "Xtest1")
if has("unix")
@@ -145,7 +151,7 @@ func Test_gf_visual()
bwipe!
call delete('Xtest_gf_visual')
- set nohidden
+ set hidden&
endfunc
func Test_gf_error()
@@ -155,5 +161,37 @@ func Test_gf_error()
call setline(1, '/doesnotexist')
call assert_fails('normal gf', 'E447:')
call assert_fails('normal gF', 'E447:')
+ call assert_fails('normal [f', 'E447:')
+
+ " gf is not allowed when text is locked
+ au InsertCharPre <buffer> normal! gF<CR>
+ let caught_e565 = 0
+ try
+ call feedkeys("ix\<esc>", 'xt')
+ catch /^Vim\%((\a\+)\)\=:E565/ " catch E565
+ let caught_e565 = 1
+ endtry
+ call assert_equal(1, caught_e565)
+ au! InsertCharPre
+
bwipe!
endfunc
+
+" If a file is not found by 'gf', then 'includeexpr' should be used to locate
+" the file.
+func Test_gf_includeexpr()
+ new
+ let g:Inc_fname = ''
+ func IncFunc()
+ let g:Inc_fname = v:fname
+ return v:fname
+ endfunc
+ setlocal includeexpr=IncFunc()
+ call setline(1, 'somefile.java')
+ call assert_fails('normal gf', 'E447:')
+ call assert_equal('somefile.java', g:Inc_fname)
+ close!
+ delfunc IncFunc
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_global.vim b/src/nvim/testdir/test_global.vim
index 8edc9c2608..feddf85346 100644
--- a/src/nvim/testdir/test_global.vim
+++ b/src/nvim/testdir/test_global.vim
@@ -36,6 +36,48 @@ func Test_global_error()
call assert_fails('g/\(/y', 'E476:')
endfunc
+" Test for printing lines using :g with different search patterns
+func Test_global_print()
+ new
+ call setline(1, ['foo', 'bar', 'foo', 'foo'])
+ let @/ = 'foo'
+ let t = execute("g/")->trim()->split("\n")
+ call assert_equal(['foo', 'foo', 'foo'], t)
+
+ " Test for Vi compatible patterns
+ let @/ = 'bar'
+ let t = execute('g\/')->trim()->split("\n")
+ call assert_equal(['bar'], t)
+
+ normal gg
+ s/foo/foo/
+ let t = execute('g\&')->trim()->split("\n")
+ call assert_equal(['foo', 'foo', 'foo'], t)
+
+ let @/ = 'bar'
+ let t = execute('g?')->trim()->split("\n")
+ call assert_equal(['bar'], t)
+
+ " Test for the 'Pattern found in every line' message
+ let v:statusmsg = ''
+ v/foo\|bar/p
+ call assert_notequal('', v:statusmsg)
+
+ close!
+endfunc
+
+" Test for global command with newline character
+func Test_global_newline()
+ new
+ call setline(1, ['foo'])
+ exe "g/foo/s/f/h/\<NL>s/o$/w/"
+ call assert_equal('how', getline(1))
+ call setline(1, ["foo\<NL>bar"])
+ exe "g/foo/s/foo\\\<NL>bar/xyz/"
+ call assert_equal('xyz', getline(1))
+ close!
+endfunc
+
func Test_wrong_delimiter()
call assert_fails('g x^bxd', 'E146:')
endfunc
diff --git a/src/nvim/testdir/test_gn.vim b/src/nvim/testdir/test_gn.vim
index d09b25b0e7..c4a41a6742 100644
--- a/src/nvim/testdir/test_gn.vim
+++ b/src/nvim/testdir/test_gn.vim
@@ -154,8 +154,24 @@ func Test_gn_command()
norm! gg0f2vf7gNd
call assert_equal(['1678'], getline(1,'$'))
sil! %d _
-
set wrapscan&vim
+
+ " Without 'wrapscan', in visual mode, running gn without a match should fail
+ " but the visual mode should be kept.
+ set nowrapscan
+ call setline('.', 'one two')
+ let @/ = 'one'
+ call assert_beeps('normal 0wvlgn')
+ exe "normal y"
+ call assert_equal('tw', @")
+
+ " with exclusive selection, run gn and gN
+ set selection=exclusive
+ normal 0gny
+ call assert_equal('one', @")
+ normal 0wgNy
+ call assert_equal('one', @")
+ set selection&
endfunc
func Test_gN_repeat()
diff --git a/src/nvim/testdir/test_goto.vim b/src/nvim/testdir/test_goto.vim
index 19513b315a..49095400ef 100644
--- a/src/nvim/testdir/test_goto.vim
+++ b/src/nvim/testdir/test_goto.vim
@@ -334,21 +334,24 @@ endfunc
func Test_motion_if_elif_else_endif()
new
- a
-/* Test pressing % on #if, #else #elsif and #endif,
- * with nested #if
- */
-#if FOO
-/* ... */
-# if BAR
-/* ... */
-# endif
-#elif BAR
-/* ... */
-#else
-/* ... */
-#endif
-.
+ let lines =<< trim END
+ /* Test pressing % on #if, #else #elsif and #endif,
+ * with nested #if
+ */
+ #if FOO
+ /* ... */
+ # if BAR
+ /* ... */
+ # endif
+ #elif BAR
+ /* ... */
+ #else
+ /* ... */
+ #endif
+
+ #define FOO 1
+ END
+ call setline(1, lines)
/#if FOO
norm %
call assert_equal([9, 1], getpos('.')[1:2])
@@ -364,6 +367,30 @@ func Test_motion_if_elif_else_endif()
norm $%
call assert_equal([6, 1], getpos('.')[1:2])
+ " Test for [# and ]# command
+ call cursor(5, 1)
+ normal [#
+ call assert_equal([4, 1], getpos('.')[1:2])
+ call cursor(5, 1)
+ normal ]#
+ call assert_equal([9, 1], getpos('.')[1:2])
+ call cursor(10, 1)
+ normal [#
+ call assert_equal([9, 1], getpos('.')[1:2])
+ call cursor(10, 1)
+ normal ]#
+ call assert_equal([11, 1], getpos('.')[1:2])
+
+ " Finding a match before the first line or after the last line should fail
+ normal gg
+ call assert_beeps('normal [#')
+ normal G
+ call assert_beeps('normal ]#')
+
+ " Finding a match for a macro definition (#define) should fail
+ normal G
+ call assert_beeps('normal %')
+
bw!
endfunc
@@ -393,3 +420,5 @@ func Test_motion_c_comment()
bw!
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..dbb36facee 100644
--- a/src/nvim/testdir/test_help.vim
+++ b/src/nvim/testdir/test_help.vim
@@ -1,4 +1,3 @@
-
" Tests for :help
func Test_help_restore_snapshot()
@@ -10,9 +9,45 @@ func Test_help_restore_snapshot()
helpclose
endfunc
+func Test_help_restore_snapshot_split()
+ " Squeeze the unnamed buffer, Xfoo and the help one side-by-side and focus
+ " the first one before calling :help.
+ let bnr = bufnr()
+ botright vsp Xfoo
+ wincmd h
+ help
+ wincmd L
+ let g:did_bufenter = v:false
+ augroup T
+ au!
+ au BufEnter Xfoo let g:did_bufenter = v:true
+ augroup END
+ helpclose
+ augroup! T
+ " We're back to the unnamed buffer.
+ call assert_equal(bnr, bufnr())
+ " No BufEnter was triggered for Xfoo.
+ call assert_equal(v:false, g:did_bufenter)
+
+ close!
+ bwipe!
+endfunc
+
func Test_help_errors()
call assert_fails('help doesnotexist', 'E149:')
call assert_fails('help!', 'E478:')
+ if has('multi_lang')
+ call assert_fails('help help@xy', 'E661:')
+ endif
+
+ let save_hf = &helpfile
+ set helpfile=help_missing
+ help
+ call assert_equal(1, winnr('$'))
+ call assert_notequal('help', &buftype)
+ let &helpfile = save_hf
+
+ call assert_fails('help ' . repeat('a', 1048), 'E149:')
new
set keywordprg=:help
@@ -57,6 +92,11 @@ func Test_help_local_additions()
let &rtp = rtp_save
endfunc
+func Test_help_completion()
+ call feedkeys(":help :undo\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"help :undo :undoj :undol :undojoin :undolist', @:)
+endfunc
+
" Test for the :helptags command
func Test_helptag_cmd()
call mkdir('Xdir/a/doc', 'p')
@@ -101,4 +141,13 @@ 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_help_tagjump.vim b/src/nvim/testdir/test_help_tagjump.vim
index a6494c531c..e84726bbfc 100644
--- a/src/nvim/testdir/test_help_tagjump.vim
+++ b/src/nvim/testdir/test_help_tagjump.vim
@@ -23,11 +23,30 @@ func Test_help_tagjump()
call assert_true(getline('.') =~ '\*bar\*')
helpclose
+ help "
+ call assert_equal("help", &filetype)
+ call assert_true(getline('.') =~ '\*quote\*')
+ helpclose
+
+ help *
+ call assert_equal("help", &filetype)
+ call assert_true(getline('.') =~ '\*star\*')
+ helpclose
+
help "*
call assert_equal("help", &filetype)
call assert_true(getline('.') =~ '\*quotestar\*')
helpclose
+ " The test result is different in vim. There ":help ??" will jump to the
+ " falsy operator ??, which hasn't been ported to neovim yet. Instead, neovim
+ " jumps to the tag "g??". This test result needs to be changed if neovim
+ " ports the falsy operator.
+ help ??
+ call assert_equal("help", &filetype)
+ call assert_true(getline('.') =~ '\*g??\*')
+ helpclose
+
help ch?ckhealth
call assert_equal("help", &filetype)
call assert_true(getline('.') =~ '\*:checkhealth\*')
@@ -86,11 +105,45 @@ func Test_help_tagjump()
call assert_true(getline('.') =~ '\*i_^_CTRL-D\*')
helpclose
+ help i^x^y
+ call assert_equal("help", &filetype)
+ call assert_true(getline('.') =~ '\*i_CTRL-X_CTRL-Y\*')
+ helpclose
+
+ exe "help i\<C-\>\<C-G>"
+ call assert_equal("help", &filetype)
+ call assert_true(getline('.') =~ '\*i_CTRL-\\_CTRL-G\*')
+ helpclose
+
exec "help \<C-V>"
call assert_equal("help", &filetype)
call assert_true(getline('.') =~ '\*CTRL-V\*')
helpclose
+ help /\|
+ call assert_equal("help", &filetype)
+ call assert_true(getline('.') =~ '\*/\\bar\*')
+ helpclose
+
+ help \_$
+ call assert_equal("help", &filetype)
+ call assert_true(getline('.') =~ '\*/\\_$\*')
+ helpclose
+
+ help CTRL-\_CTRL-N
+ call assert_equal("help", &filetype)
+ call assert_true(getline('.') =~ '\*CTRL-\\_CTRL-N\*')
+ helpclose
+
+ help `:pwd`,
+ call assert_equal("help", &filetype)
+ call assert_true(getline('.') =~ '\*:pwd\*')
+ helpclose
+
+ help `:ls`.
+ call assert_equal("help", &filetype)
+ call assert_true(getline('.') =~ '\*:ls\*')
+ helpclose
exec "help! ('textwidth'"
call assert_equal("help", &filetype)
@@ -122,6 +175,15 @@ func Test_help_tagjump()
call assert_true(getline('.') =~ '\*{address}\*')
helpclose
+ " Use special patterns in the help tag
+ for h in ['/\w', '/\%^', '/\%(', '/\zs', '/\@<=', '/\_$', '[++opt]', '/\{']
+ exec "help! " . h
+ call assert_equal("help", &filetype)
+ let pat = '\*' . escape(h, '\$[') . '\*'
+ call assert_true(getline('.') =~ pat, pat)
+ helpclose
+ endfor
+
exusage
call assert_equal("help", &filetype)
call assert_true(getline('.') =~ '\*:index\*')
diff --git a/src/nvim/testdir/test_highlight.vim b/src/nvim/testdir/test_highlight.vim
index 899eb530ec..efdf44a0d6 100644
--- a/src/nvim/testdir/test_highlight.vim
+++ b/src/nvim/testdir/test_highlight.vim
@@ -146,7 +146,7 @@ func Test_highlight_eol_with_cursorline_vertsplit()
" 'abcd |abcd '
" ^^^^ ^^^^^^^^^ no highlight
" ^ 'Search' highlight
- " ^ 'VertSplit' highlight
+ " ^ 'WinSeparator' highlight
let attrs0 = ScreenAttrs(1, 15)[0]
call assert_equal(repeat([attrs0[0]], 4), attrs0[0:3])
call assert_equal(repeat([attrs0[0]], 9), attrs0[6:14])
@@ -160,7 +160,7 @@ func Test_highlight_eol_with_cursorline_vertsplit()
" 'abcd |abcd '
" ^^^^ underline
" ^ 'Search' highlight with underline
- " ^ 'VertSplit' highlight
+ " ^ 'WinSeparator' highlight
" ^^^^^^^^^ no highlight
" underline
@@ -597,6 +597,86 @@ func Test_cursorline_with_visualmode()
call delete('Xtest_cursorline_with_visualmode')
endfunc
+func Test_cursorcolumn_insert_on_tab()
+ CheckScreendump
+
+ let lines =<< trim END
+ call setline(1, ['123456789', "a\tb"])
+ set cursorcolumn
+ call cursor(2, 2)
+ END
+ call writefile(lines, 'Xcuc_insert_on_tab')
+
+ let buf = RunVimInTerminal('-S Xcuc_insert_on_tab', #{rows: 8})
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_cursorcolumn_insert_on_tab_1', {})
+
+ call term_sendkeys(buf, 'i')
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_cursorcolumn_insert_on_tab_2', {})
+
+ call term_sendkeys(buf, "\<C-O>")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_cursorcolumn_insert_on_tab_3', {})
+
+ call term_sendkeys(buf, 'i')
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_cursorcolumn_insert_on_tab_2', {})
+
+ call StopVimInTerminal(buf)
+ call delete('Xcuc_insert_on_tab')
+endfunc
+
+func Test_cursorcolumn_callback()
+ CheckScreendump
+ CheckFeature timers
+
+ let lines =<< trim END
+ call setline(1, ['aaaaa', 'bbbbb', 'ccccc', 'ddddd'])
+ set cursorcolumn
+ call cursor(4, 5)
+
+ func Func(timer)
+ call cursor(1, 1)
+ endfunc
+
+ call timer_start(300, 'Func')
+ END
+ call writefile(lines, 'Xcuc_timer')
+
+ let buf = RunVimInTerminal('-S Xcuc_timer', #{rows: 8})
+ call TermWait(buf, 310)
+ call VerifyScreenDump(buf, 'Test_cursorcolumn_callback_1', {})
+
+ call StopVimInTerminal(buf)
+ call delete('Xcuc_timer')
+endfunc
+
+func Test_colorcolumn()
+ CheckScreendump
+
+ " check that setting 'colorcolumn' when entering a buffer works
+ let lines =<< trim END
+ split
+ edit X
+ call setline(1, ["1111111111","22222222222","3333333333"])
+ set nomodified
+ set colorcolumn=3,9
+ set number cursorline cursorlineopt=number
+ wincmd w
+ buf X
+ END
+ call writefile(lines, 'Xtest_colorcolumn')
+ let buf = RunVimInTerminal('-S Xtest_colorcolumn', {'rows': 10})
+ call term_sendkeys(buf, ":\<CR>")
+ call term_wait(buf)
+ call VerifyScreenDump(buf, 'Test_colorcolumn_1', {})
+
+ " clean up
+ call StopVimInTerminal(buf)
+ call delete('Xtest_colorcolumn')
+endfunc
+
func Test_colorcolumn_bri()
CheckScreendump
diff --git a/src/nvim/testdir/test_history.vim b/src/nvim/testdir/test_history.vim
index 2f0dc2dae1..feb521e232 100644
--- a/src/nvim/testdir/test_history.vim
+++ b/src/nvim/testdir/test_history.vim
@@ -1,8 +1,7 @@
" Tests for the history functions
-if !has('cmdline_hist')
- finish
-endif
+source check.vim
+CheckFeature cmdline_hist
set history=7
@@ -71,6 +70,14 @@ function History_Tests(hist)
call assert_equal('', histget(a:hist, i))
call assert_equal('', histget(a:hist, i - 7 - 1))
endfor
+
+ " Test for freeing an entry at the beginning of the history list
+ for i in range(1, 4)
+ call histadd(a:hist, 'text_' . i)
+ endfor
+ call histdel(a:hist, 1)
+ call assert_equal('', histget(a:hist, 1))
+ call assert_equal('text_4', histget(a:hist, 4))
endfunction
function Test_History()
@@ -86,6 +93,8 @@ function Test_History()
call assert_fails('call histget([])', 'E730:')
call assert_equal(-1, histnr('abc'))
call assert_fails('call histnr([])', 'E730:')
+ call assert_fails('history xyz', 'E488:')
+ call assert_fails('history ,abc', 'E488:')
endfunction
function Test_Search_history_window()
@@ -105,7 +114,122 @@ function Test_Search_history_window()
bwipe!
endfunc
+" Test for :history command option completion
function Test_history_completion()
call feedkeys(":history \<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"history / : = > ? @ all cmd debug expr input search', @:)
endfunc
+
+" Test for increasing the 'history' option value
+func Test_history_size()
+ let save_histsz = &history
+ set history=10
+ call histadd(':', 'ls')
+ call histdel(':')
+ for i in range(1, 5)
+ call histadd(':', 'cmd' .. i)
+ endfor
+ call assert_equal(5, histnr(':'))
+ call assert_equal('cmd5', histget(':', -1))
+
+ set history=15
+ for i in range(6, 10)
+ call histadd(':', 'cmd' .. i)
+ endfor
+ call assert_equal(10, histnr(':'))
+ call assert_equal('cmd1', histget(':', 1))
+ call assert_equal('cmd10', histget(':', -1))
+
+ set history=5
+ call histadd(':', 'abc')
+ call assert_equal('', histget(':', 6))
+ call assert_equal('', histget(':', 12))
+ call assert_equal('cmd7', histget(':', 7))
+ call assert_equal('abc', histget(':', -1))
+
+ " This test works only when the language is English
+ if v:lang == "C" || v:lang =~ '^[Ee]n'
+ set history=0
+ redir => v
+ call feedkeys(":history\<CR>", 'xt')
+ redir END
+ call assert_equal(["'history' option is zero"], split(v, "\n"))
+ endif
+
+ let &history=save_histsz
+endfunc
+
+" Test for recalling old search patterns in /
+func Test_history_search()
+ call histdel('/')
+ let g:pat = []
+ func SavePat()
+ call add(g:pat, getcmdline())
+ return ''
+ endfunc
+ cnoremap <F2> <C-\>eSavePat()<CR>
+ call histadd('/', 'pat1')
+ call histadd('/', 'pat2')
+ let @/ = ''
+ call feedkeys("/\<Up>\<F2>\<Up>\<F2>\<Down>\<Down>\<F2>\<Esc>", 'xt')
+ call assert_equal(['pat2', 'pat1', ''], g:pat)
+ cunmap <F2>
+ delfunc SavePat
+
+ " Search for a pattern that is not present in the history
+ call assert_beeps('call feedkeys("/a1b2\<Up>\<CR>", "xt")')
+
+ " Recall patterns with 'history' set to 0
+ set history=0
+ let @/ = 'abc'
+ let cmd = 'call feedkeys("/\<Up>\<Down>\<S-Up>\<S-Down>\<CR>", "xt")'
+ call assert_fails(cmd, 'E486:')
+ set history&
+
+ " Recall patterns till the end of history
+ set history=4
+ call histadd('/', 'pat')
+ call histdel('/')
+ call histadd('/', 'pat1')
+ call histadd('/', 'pat2')
+ call assert_beeps('call feedkeys("/\<Up>\<Up>\<Up>\<C-U>\<cr>", "xt")')
+ call assert_beeps('call feedkeys("/\<Down><cr>", "xt")')
+
+ " Test for wrapping around the history list
+ for i in range(3, 7)
+ call histadd('/', 'pat' .. i)
+ endfor
+ let upcmd = "\<up>\<up>\<up>\<up>\<up>"
+ let downcmd = "\<down>\<down>\<down>\<down>\<down>"
+ try
+ call feedkeys("/" .. upcmd .. "\<cr>", 'xt')
+ catch /E486:/
+ endtry
+ call assert_equal('pat4', @/)
+ try
+ call feedkeys("/" .. upcmd .. downcmd .. "\<cr>", 'xt')
+ catch /E486:/
+ endtry
+ call assert_equal('pat4', @/)
+
+ " Test for changing the search command separator in the history
+ call assert_fails('call feedkeys("/def/\<cr>", "xt")', 'E486:')
+ call assert_fails('call feedkeys("?\<up>\<cr>", "xt")', 'E486:')
+ call assert_equal('def?', histget('/', -1))
+
+ call assert_fails('call feedkeys("/ghi?\<cr>", "xt")', 'E486:')
+ call assert_fails('call feedkeys("?\<up>\<cr>", "xt")', 'E486:')
+ call assert_equal('ghi\?', histget('/', -1))
+
+ set history&
+endfunc
+
+" Test for making sure the key value is not stored in history
+func Test_history_crypt_key()
+ CheckFeature cryptv
+ call feedkeys(":set bs=2 key=abc ts=8\<CR>", 'xt')
+ call assert_equal('set bs=2 key= ts=8', histget(':'))
+ set key& bs& ts&
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_increment.vim b/src/nvim/testdir/test_increment.vim
index 6d08cd40a8..2559654f25 100644
--- a/src/nvim/testdir/test_increment.vim
+++ b/src/nvim/testdir/test_increment.vim
@@ -285,7 +285,7 @@ endfunc
" 1
" 1
" 1
-" Expexted:
+" Expected:
" 1) g Ctrl-A on block selected indented lines
" 2
" 1
@@ -776,6 +776,14 @@ func Test_increment_empty_line()
call setline(1, ['0', '0', '0', '0', '0', '0', ''])
exe "normal Gvgg\<C-A>"
call assert_equal(['1', '1', '1', '1', '1', '1', ''], getline(1, 7))
+
+ " Ctrl-A/Ctrl-X should do nothing in operator pending mode
+ %d
+ call setline(1, 'one two')
+ exe "normal! c\<C-A>l"
+ exe "normal! c\<C-X>l"
+ call assert_equal('one two', getline(1))
+
bwipe!
endfunc
diff --git a/src/nvim/testdir/test_indent.vim b/src/nvim/testdir/test_indent.vim
new file mode 100644
index 0000000000..3b5b643177
--- /dev/null
+++ b/src/nvim/testdir/test_indent.vim
@@ -0,0 +1,279 @@
+" Test for various indent options
+
+func Test_preserveindent()
+ new
+ " Test for autoindent copying indent from the previous line
+ setlocal autoindent
+ call setline(1, [repeat(' ', 16) .. 'line1'])
+ call feedkeys("A\nline2", 'xt')
+ call assert_equal("\t\tline2", getline(2))
+ setlocal autoindent&
+
+ " Test for using CTRL-T with and without 'preserveindent'
+ set shiftwidth=4
+ call cursor(1, 1)
+ call setline(1, " \t ")
+ call feedkeys("Al\<C-T>", 'xt')
+ call assert_equal("\t\tl", getline(1))
+ set preserveindent
+ call setline(1, " \t ")
+ call feedkeys("Al\<C-T>", 'xt')
+ call assert_equal(" \t \tl", getline(1))
+ set pi& sw&
+
+ " Test for using CTRL-T with 'expandtab' and 'preserveindent'
+ call cursor(1, 1)
+ call setline(1, "\t \t")
+ set shiftwidth=4 expandtab preserveindent
+ call feedkeys("Al\<C-T>", 'xt')
+ call assert_equal("\t \t l", getline(1))
+ set sw& et& pi&
+
+ close!
+endfunc
+
+" Test for indent()
+func Test_indent_func()
+ call assert_equal(-1, indent(-1))
+ new
+ call setline(1, "\tabc")
+ call assert_equal(8, indent(1))
+ call setline(1, " abc")
+ call assert_equal(4, indent(1))
+ call setline(1, " \t abc")
+ call assert_equal(12, indent(1))
+ close!
+endfunc
+
+" Test for reindenting a line using the '=' operator
+func Test_reindent()
+ new
+ call setline(1, 'abc')
+ set nomodifiable
+ call assert_fails('normal ==', 'E21:')
+ set modifiable
+
+ call setline(1, ['foo', 'bar'])
+ call feedkeys('ggVG=', 'xt')
+ call assert_equal(['foo', 'bar'], getline(1, 2))
+ close!
+endfunc
+
+" Test indent operator creating one undo entry
+func Test_indent_operator_undo()
+ enew
+ call setline(1, range(12)->map('"\t" .. v:val'))
+ func FoldExpr()
+ let g:foldcount += 1
+ return '='
+ endfunc
+ set foldmethod=expr foldexpr=FoldExpr()
+ let g:foldcount = 0
+ redraw
+ call assert_equal(12, g:foldcount)
+ normal gg=G
+ call assert_equal(24, g:foldcount)
+ undo
+ call assert_equal(38, g:foldcount)
+
+ bwipe!
+ set foldmethod& foldexpr=
+ delfunc FoldExpr
+ unlet g:foldcount
+endfunc
+
+" Test for shifting a line with a preprocessor directive ('#')
+func Test_preproc_indent()
+ new
+ set sw=4
+ call setline(1, '#define FOO 1')
+ normal >>
+ call assert_equal(' #define FOO 1', getline(1))
+
+ " with 'smartindent'
+ call setline(1, '#define FOO 1')
+ set smartindent
+ normal >>
+ call assert_equal('#define FOO 1', getline(1))
+ set smartindent&
+
+ " with 'cindent'
+ set cindent
+ normal >>
+ call assert_equal('#define FOO 1', getline(1))
+ set cindent&
+
+ close!
+endfunc
+
+" Test for 'copyindent'
+func Test_copyindent()
+ new
+ set shiftwidth=4 autoindent expandtab copyindent
+ call setline(1, " \t abc")
+ call feedkeys("ol", 'xt')
+ call assert_equal(" \t l", getline(2))
+ set noexpandtab
+ call setline(1, " \t abc")
+ call feedkeys("ol", 'xt')
+ call assert_equal(" \t l", getline(2))
+ set sw& ai& et& ci&
+ close!
+endfunc
+
+" Test for changing multiple lines with lisp indent
+func Test_lisp_indent_change_multiline()
+ new
+ setlocal lisp autoindent
+ call setline(1, ['(if a', ' (if b', ' (return 5)))'])
+ normal! jc2j(return 4))
+ call assert_equal(' (return 4))', getline(2))
+ close!
+endfunc
+
+func Test_lisp_indent()
+ new
+ setlocal lisp autoindent
+ call setline(1, ['(if a', ' ;; comment', ' \ abc', '', ' " str1\', ' " st\b', ' (return 5)'])
+ normal! jo;; comment
+ normal! jo\ abc
+ normal! jo;; ret
+ normal! jostr1"
+ normal! jostr2"
+ call assert_equal([' ;; comment', ' ;; comment', ' \ abc', ' \ abc', '', ' ;; ret', ' " str1\', ' str1"', ' " st\b', ' str2"'], getline(2, 11))
+ close!
+endfunc
+
+func Test_lisp_indent_quoted()
+ " This was going past the end of the line
+ new
+ setlocal lisp autoindent
+ call setline(1, ['"[', '='])
+ normal Gvk=
+
+ bwipe!
+endfunc
+
+" Test for setting the 'indentexpr' from a modeline
+func Test_modeline_indent_expr()
+ let modeline = &modeline
+ set modeline
+ func GetIndent()
+ return line('.') * 2
+ endfunc
+ call writefile(['# vim: indentexpr=GetIndent()'], 'Xfile.txt')
+ set modelineexpr
+ new Xfile.txt
+ call assert_equal('GetIndent()', &indentexpr)
+ exe "normal Oa\nb\n"
+ call assert_equal([' a', ' b'], getline(1, 2))
+
+ set modelineexpr&
+ delfunc GetIndent
+ let &modeline = modeline
+ close!
+ call delete('Xfile.txt')
+endfunc
+
+func Test_indent_func_with_gq()
+
+ function GetTeXIndent()
+ " Sample indent expression for TeX files
+ let lnum = prevnonblank(v:lnum - 1)
+ " At the start of the file use zero indent.
+ if lnum == 0
+ return 0
+ endif
+ let line = getline(lnum)
+ let ind = indent(lnum)
+ " Add a 'shiftwidth' after beginning of environments.
+ if line =~ '\\begin{center}'
+ let ind = ind + shiftwidth()
+ endif
+ return ind
+ endfunction
+
+ new
+ setl et sw=2 sts=2 ts=2 tw=50 indentexpr=GetTeXIndent()
+ put =[ '\documentclass{article}', '', '\begin{document}', '',
+ \ 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce ut enim non',
+ \ 'libero efficitur aliquet. Maecenas metus justo, facilisis convallis blandit',
+ \ 'non, semper eu urna. Suspendisse diam diam, iaculis faucibus lorem eu,',
+ \ 'fringilla condimentum lectus. Quisque euismod diam at convallis vulputate.',
+ \ 'Pellentesque laoreet tortor sit amet mauris euismod ornare. Sed varius',
+ \ 'bibendum orci vel vehicula. Pellentesque tempor, ipsum et auctor accumsan,',
+ \ 'metus lectus ultrices odio, sed elementum mi ante at arcu.', '', '\begin{center}', '',
+ \ 'Proin nec risus consequat nunc dapibus consectetur. Mauris lacinia est a augue',
+ \ 'tristique accumsan. Morbi pretium, felis molestie eleifend condimentum, arcu',
+ \ 'ipsum congue nisl, quis euismod purus libero in ante.', '',
+ \ 'Donec id semper purus.',
+ \ 'Suspendisse eget aliquam nunc. Maecenas fringilla mauris vitae maximus',
+ \ 'condimentum. Cras a quam in mi dictum eleifend at a lorem. Sed convallis',
+ \ 'ante a commodo facilisis. Nam suscipit vulputate odio, vel dapibus nisl',
+ \ 'dignissim facilisis. Vestibulum ante ipsum primis in faucibus orci luctus et',
+ \ 'ultrices posuere cubilia curae;', '', '']
+ 1d_
+ call cursor(5, 1)
+ ka
+ call cursor(14, 1)
+ kb
+ norm! 'agqap
+ norm! 'bgqG
+ let expected = [ '\documentclass{article}', '', '\begin{document}', '',
+ \ 'Lorem ipsum dolor sit amet, consectetur adipiscing',
+ \ 'elit. Fusce ut enim non libero efficitur aliquet.',
+ \ 'Maecenas metus justo, facilisis convallis blandit',
+ \ 'non, semper eu urna. Suspendisse diam diam,',
+ \ 'iaculis faucibus lorem eu, fringilla condimentum',
+ \ 'lectus. Quisque euismod diam at convallis',
+ \ 'vulputate. Pellentesque laoreet tortor sit amet',
+ \ 'mauris euismod ornare. Sed varius bibendum orci',
+ \ 'vel vehicula. Pellentesque tempor, ipsum et auctor',
+ \ 'accumsan, metus lectus ultrices odio, sed',
+ \ 'elementum mi ante at arcu.', '', '\begin{center}', '',
+ \ ' Proin nec risus consequat nunc dapibus',
+ \ ' consectetur. Mauris lacinia est a augue',
+ \ ' tristique accumsan. Morbi pretium, felis',
+ \ ' molestie eleifend condimentum, arcu ipsum congue',
+ \ ' nisl, quis euismod purus libero in ante.',
+ \ '',
+ \ ' Donec id semper purus. Suspendisse eget aliquam',
+ \ ' nunc. Maecenas fringilla mauris vitae maximus',
+ \ ' condimentum. Cras a quam in mi dictum eleifend',
+ \ ' at a lorem. Sed convallis ante a commodo',
+ \ ' facilisis. Nam suscipit vulputate odio, vel',
+ \ ' dapibus nisl dignissim facilisis. Vestibulum',
+ \ ' ante ipsum primis in faucibus orci luctus et',
+ \ ' ultrices posuere cubilia curae;', '', '']
+ call assert_equal(expected, getline(1, '$'))
+
+ bwipe!
+ delmark ab
+ delfunction GetTeXIndent
+endfu
+
+func Test_formatting_keeps_first_line_indent()
+ let lines =<< trim END
+ foo()
+ {
+ int x; // manually positioned
+ // more text that will be formatted
+ // but not reindented
+ END
+ new
+ call setline(1, lines)
+ setlocal sw=4 cindent tw=45 et
+ normal! 4Ggqj
+ let expected =<< trim END
+ foo()
+ {
+ int x; // manually positioned
+ // more text that will be
+ // formatted but not
+ // reindented
+ END
+ call assert_equal(expected, getline(1, '$'))
+ bwipe!
+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..93ab17955d 100644
--- a/src/nvim/testdir/test_ins_complete.vim
+++ b/src/nvim/testdir/test_ins_complete.vim
@@ -95,7 +95,7 @@ func Test_ins_complete()
call delete('Xtest11.one')
call delete('Xtest11.two')
call delete('Xtestdata')
- set cpt& cot& def& tags& tagbsearch& nohidden
+ set cpt& cot& def& tags& tagbsearch& hidden&
cd ..
call delete('Xdir', 'rf')
endfunc
@@ -109,7 +109,7 @@ func s:CompleteDone_CompleteFuncNone( findstart, base )
return v:none
endfunc
-function! s:CompleteDone_CompleteFuncDict( findstart, base )
+func s:CompleteDone_CompleteFuncDict( findstart, base )
if a:findstart
return 0
endif
@@ -126,7 +126,7 @@ function! s:CompleteDone_CompleteFuncDict( findstart, base )
\ }
\ ]
\ }
-endfunction
+endfunc
func s:CompleteDone_CheckCompletedItemNone()
let s:called_completedone = 1
@@ -287,6 +287,30 @@ func Test_omni_dash()
set omnifunc=
endfunc
+func Test_omni_throw()
+ let g:CallCount = 0
+ func Omni(findstart, base)
+ let g:CallCount += 1
+ if a:findstart
+ throw "he he he"
+ endif
+ endfunc
+ set omnifunc=Omni
+ new
+ try
+ exe "normal ifoo\<C-x>\<C-o>"
+ call assert_false(v:true, 'command should have failed')
+ catch
+ call assert_exception('he he he')
+ call assert_equal(1, g:CallCount)
+ endtry
+
+ bwipe!
+ delfunc Omni
+ unlet g:CallCount
+ set omnifunc=
+endfunc
+
func Test_completefunc_args()
let s:args = []
func! CompleteFunc(findstart, base)
@@ -341,6 +365,14 @@ func Test_compl_feedkeys()
set completeopt&
endfunc
+func s:ComplInCmdwin_GlobalCompletion(a, l, p)
+ return 'global'
+endfunc
+
+func s:ComplInCmdwin_LocalCompletion(a, l, p)
+ return 'local'
+endfunc
+
func Test_compl_in_cmdwin()
set wildmenu wildchar=<Tab>
com! -nargs=1 -complete=command GetInput let input = <q-args>
@@ -376,6 +408,47 @@ func Test_compl_in_cmdwin()
call feedkeys("q::GetInput b:test_\<Tab>\<CR>:q\<CR>", 'tx!')
call assert_equal('b:test_', input)
+
+ " Argument completion of buffer-local command
+ func s:ComplInCmdwin_GlobalCompletionList(a, l, p)
+ return ['global']
+ endfunc
+
+ func s:ComplInCmdwin_LocalCompletionList(a, l, p)
+ return ['local']
+ endfunc
+
+ func s:ComplInCmdwin_CheckCompletion(arg)
+ call assert_equal('local', a:arg)
+ endfunc
+
+ com! -nargs=1 -complete=custom,<SID>ComplInCmdwin_GlobalCompletion
+ \ TestCommand call s:ComplInCmdwin_CheckCompletion(<q-args>)
+ com! -buffer -nargs=1 -complete=custom,<SID>ComplInCmdwin_LocalCompletion
+ \ TestCommand call s:ComplInCmdwin_CheckCompletion(<q-args>)
+ call feedkeys("q:iTestCommand \<Tab>\<CR>", 'tx!')
+
+ com! -nargs=1 -complete=customlist,<SID>ComplInCmdwin_GlobalCompletionList
+ \ TestCommand call s:ComplInCmdwin_CheckCompletion(<q-args>)
+ com! -buffer -nargs=1 -complete=customlist,<SID>ComplInCmdwin_LocalCompletionList
+ \ TestCommand call s:ComplInCmdwin_CheckCompletion(<q-args>)
+
+ call feedkeys("q:iTestCommand \<Tab>\<CR>", 'tx!')
+
+ func! s:ComplInCmdwin_CheckCompletion(arg)
+ call assert_equal('global', a:arg)
+ endfunc
+ new
+ call feedkeys("q:iTestCommand \<Tab>\<CR>", 'tx!')
+ quit
+
+ delfunc s:ComplInCmdwin_GlobalCompletion
+ delfunc s:ComplInCmdwin_LocalCompletion
+ delfunc s:ComplInCmdwin_GlobalCompletionList
+ delfunc s:ComplInCmdwin_LocalCompletionList
+ delfunc s:ComplInCmdwin_CheckCompletion
+
+ delcom -buffer TestCommand
delcom TestCommand
delcom GetInput
unlet w:test_winvar
@@ -445,6 +518,37 @@ 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_complete_stopinsert_startinsert()
+ nnoremap <F2> <Cmd>startinsert<CR>
+ inoremap <F2> <Cmd>stopinsert<CR>
+ " This just checks if this causes an error
+ call feedkeys("i\<C-X>\<C-N>\<F2>\<F2>", 'x')
+ nunmap <F2>
+ iunmap <F2>
+endfunc
+
func Test_pum_with_folds_two_tabs()
CheckScreendump
@@ -497,6 +601,107 @@ func Test_ins_compl_tag_sft()
%bwipe!
endfunc
+" Test for 'completefunc' deleting text
+func Test_completefunc_error()
+ new
+ " delete text when called for the first time
+ func CompleteFunc(findstart, base)
+ if a:findstart == 1
+ normal dd
+ return col('.') - 1
+ endif
+ return ['a', 'b']
+ endfunc
+ set completefunc=CompleteFunc
+ call setline(1, ['', 'abcd', ''])
+ call assert_fails('exe "normal 2G$a\<C-X>\<C-U>"', 'E565:')
+
+ " delete text when called for the second time
+ func CompleteFunc2(findstart, base)
+ if a:findstart == 1
+ return col('.') - 1
+ endif
+ normal dd
+ return ['a', 'b']
+ endfunc
+ set completefunc=CompleteFunc2
+ call setline(1, ['', 'abcd', ''])
+ call assert_fails('exe "normal 2G$a\<C-X>\<C-U>"', 'E565:')
+
+ " Jump to a different window from the complete function
+ func CompleteFunc3(findstart, base)
+ if a:findstart == 1
+ return col('.') - 1
+ endif
+ wincmd p
+ return ['a', 'b']
+ endfunc
+ set completefunc=CompleteFunc3
+ new
+ call assert_fails('exe "normal a\<C-X>\<C-U>"', 'E565:')
+ close!
+
+ set completefunc&
+ delfunc CompleteFunc
+ delfunc CompleteFunc2
+ delfunc CompleteFunc3
+ close!
+endfunc
+
+" Test for returning non-string values from 'completefunc'
+func Test_completefunc_invalid_data()
+ new
+ func! CompleteFunc(findstart, base)
+ if a:findstart == 1
+ return col('.') - 1
+ endif
+ return [{}, '', 'moon']
+ endfunc
+ set completefunc=CompleteFunc
+ exe "normal i\<C-X>\<C-U>"
+ call assert_equal('moon', getline(1))
+ set completefunc&
+ close!
+endfunc
+
+" Test for errors in using complete() function
+func Test_complete_func_error()
+ call assert_fails('call complete(1, ["a"])', 'E785:')
+ func ListColors()
+ call complete(col('.'), "blue")
+ endfunc
+ call assert_fails('exe "normal i\<C-R>=ListColors()\<CR>"', 'E474:')
+ func ListMonths()
+ call complete(col('.'), test_null_list())
+ endfunc
+ " Nvim allows a NULL list
+ " call assert_fails('exe "normal i\<C-R>=ListMonths()\<CR>"', 'E474:')
+ delfunc ListColors
+ delfunc ListMonths
+ call assert_fails('call complete_info({})', 'E714:')
+ call assert_equal([], complete_info(['items']).items)
+endfunc
+
+" Test for completing words following a completed word in a line
+func Test_complete_wrapscan()
+ " complete words from another buffer
+ new
+ call setline(1, ['one two', 'three four'])
+ new
+ setlocal complete=w
+ call feedkeys("itw\<C-N>\<C-X>\<C-N>\<C-X>\<C-N>\<C-X>\<C-N>", 'xt')
+ call assert_equal('two three four', getline(1))
+ close!
+ " complete words from the current buffer
+ setlocal complete=.
+ %d
+ call setline(1, ['one two', ''])
+ call cursor(2, 1)
+ call feedkeys("ion\<C-N>\<C-X>\<C-N>\<C-X>\<C-N>\<C-X>\<C-N>", 'xt')
+ call assert_equal('one two one two', getline(2))
+ close!
+endfunc
+
" Test for completing special characters
func Test_complete_special_chars()
new
@@ -636,4 +841,23 @@ func Test_z1_complete_no_history()
close!
endfunc
+func FooBarComplete(findstart, base)
+ if a:findstart
+ return col('.') - 1
+ else
+ return ["Foo", "Bar", "}"]
+ endif
+endfunc
+
+func Test_complete_smartindent()
+ new
+ setlocal smartindent completefunc=FooBarComplete
+
+ exe "norm! o{\<cr>\<c-x>\<c-u>\<c-p>}\<cr>\<esc>"
+ let result = getline(1,'$')
+ call assert_equal(['', '{','}',''], result)
+ bw!
+ delfunction! FooBarComplete
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_interrupt.vim b/src/nvim/testdir/test_interrupt.vim
index 111752d16a..aa7f634302 100644
--- a/src/nvim/testdir/test_interrupt.vim
+++ b/src/nvim/testdir/test_interrupt.vim
@@ -13,15 +13,20 @@ func s:bufwritepost()
endfunction
func Test_interrupt()
- new Xfile
+ new Xinterrupt
let n = 0
try
- au BufWritePre Xfile call s:bufwritepre()
- au BufWritePost Xfile call s:bufwritepost()
+ au BufWritePre Xinterrupt call s:bufwritepre()
+ au BufWritePost Xinterrupt call s:bufwritepost()
w!
catch /^Vim:Interrupt$/
endtry
call assert_equal(1, s:bufwritepre_called)
call assert_equal(0, s:bufwritepost_called)
- call assert_equal(0, filereadable('Xfile'))
+ call assert_equal(0, filereadable('Xinterrupt'))
+
+ au! BufWritePre
+ au! BufWritePost
endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_join.vim b/src/nvim/testdir/test_join.vim
index ecb969d10a..1f7a0825a5 100644
--- a/src/nvim/testdir/test_join.vim
+++ b/src/nvim/testdir/test_join.vim
@@ -51,7 +51,7 @@ func Test_join_marks()
/^This line/;'}-join
call assert_equal([0, 4, 11, 0], getpos("'["))
- call assert_equal([0, 4, 66, 0], getpos("']"))
+ call assert_equal([0, 4, 67, 0], getpos("']"))
enew!
endfunc
@@ -437,5 +437,11 @@ func Test_join_lines()
call setline(1, ['a', 'b', '', 'c', 'd'])
normal 5J
call assert_equal('a b c d', getline(1))
+ call setline(1, ['a', 'b', 'c'])
+ 2,2join
+ call assert_equal(['a', 'b', 'c'], getline(1, '$'))
+ call assert_equal(2, line('.'))
+ 2join
+ call assert_equal(['a', 'b c'], getline(1, '$'))
bwipe!
endfunc
diff --git a/src/nvim/testdir/test_jumplist.vim b/src/nvim/testdir/test_jumplist.vim
index 9cfbbe2029..91ad940e18 100644
--- a/src/nvim/testdir/test_jumplist.vim
+++ b/src/nvim/testdir/test_jumplist.vim
@@ -64,3 +64,44 @@ func Test_getjumplist()
call delete("Xtest")
endfunc
+
+func Test_jumplist_invalid()
+ new
+ clearjumps
+ " put some randome text
+ put ='a'
+ let prev = bufnr('%')
+ setl nomodified bufhidden=wipe
+ e XXJumpListBuffer
+ let bnr = bufnr('%')
+ " 1) empty jumplist
+ let expected = [[
+ \ {'lnum': 2, 'bufnr': prev, 'col': 0, 'coladd': 0}], 1]
+ call assert_equal(expected, getjumplist())
+ let jumps = execute(':jumps')
+ call assert_equal('>', jumps[-1:])
+ " now jump back
+ exe ":norm! \<c-o>"
+ let expected = [[
+ \ {'lnum': 2, 'bufnr': prev, 'col': 0, 'coladd': 0},
+ \ {'lnum': 1, 'bufnr': bnr, 'col': 0, 'coladd': 0}], 0]
+ call assert_equal(expected, getjumplist())
+ let jumps = execute(':jumps')
+ call assert_match('> 0 2 0 -invalid-', jumps)
+endfunc
+
+" Test for '' mark in an empty buffer
+
+func Test_empty_buffer()
+ new
+ insert
+a
+b
+c
+d
+.
+ call assert_equal(1, line("''"))
+ bwipe!
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_jumps.vim b/src/nvim/testdir/test_jumps.vim
deleted file mode 100644
index 5a3717d165..0000000000
--- a/src/nvim/testdir/test_jumps.vim
+++ /dev/null
@@ -1,11 +0,0 @@
-func Test_empty_buffer()
- new
- insert
-a
-b
-c
-d
-.
- call assert_equal(1, line("''"))
- bwipe!
-endfunc
diff --git a/src/nvim/testdir/test_lambda.vim b/src/nvim/testdir/test_lambda.vim
index 63bb4ae1ef..c1fe47d1c9 100644
--- a/src/nvim/testdir/test_lambda.vim
+++ b/src/nvim/testdir/test_lambda.vim
@@ -1,24 +1,24 @@
" Test for lambda and closure
-function! Test_lambda_feature()
+func Test_lambda_feature()
call assert_equal(1, has('lambda'))
-endfunction
+endfunc
-function! Test_lambda_with_filter()
+func Test_lambda_with_filter()
let s:x = 2
call assert_equal([2, 3], filter([1, 2, 3], {i, v -> v >= s:x}))
-endfunction
+endfunc
-function! Test_lambda_with_map()
+func Test_lambda_with_map()
let s:x = 1
call assert_equal([2, 3, 4], map([1, 2, 3], {i, v -> v + s:x}))
-endfunction
+endfunc
-function! Test_lambda_with_sort()
+func Test_lambda_with_sort()
call assert_equal([1, 2, 3, 4, 7], sort([3,7,2,1,4], {a, b -> a - b}))
-endfunction
+endfunc
-function! Test_lambda_with_timer()
+func Test_lambda_with_timer()
if !has('timers')
return
endif
@@ -54,10 +54,10 @@ function! Test_lambda_with_timer()
call assert_true(s:n > m)
endfunc
-function! Test_lambda_with_partial()
+func Test_lambda_with_partial()
let l:Cb = function({... -> ['zero', a:1, a:2, a:3]}, ['one', 'two'])
call assert_equal(['zero', 'one', 'two', 'three'], l:Cb('three'))
-endfunction
+endfunc
function Test_lambda_fails()
call assert_equal(3, {a, b -> a + b}(1, 2))
@@ -70,59 +70,59 @@ func Test_not_lambda()
call assert_equal('foo', x['>'])
endfunc
-function! Test_lambda_capture_by_reference()
+func Test_lambda_capture_by_reference()
let v = 1
let l:F = {x -> x + v}
let v = 2
call assert_equal(12, l:F(10))
-endfunction
+endfunc
-function! Test_lambda_side_effect()
- function! s:update_and_return(arr)
+func Test_lambda_side_effect()
+ func! s:update_and_return(arr)
let a:arr[1] = 5
return a:arr
- endfunction
+ endfunc
- function! s:foo(arr)
+ func! s:foo(arr)
return {-> s:update_and_return(a:arr)}
- endfunction
+ endfunc
let arr = [3,2,1]
call assert_equal([3, 5, 1], s:foo(arr)())
-endfunction
+endfunc
-function! Test_lambda_refer_local_variable_from_other_scope()
- function! s:foo(X)
+func Test_lambda_refer_local_variable_from_other_scope()
+ func! s:foo(X)
return a:X() " refer l:x in s:bar()
- endfunction
+ endfunc
- function! s:bar()
+ func! s:bar()
let x = 123
return s:foo({-> x})
- endfunction
+ endfunc
call assert_equal(123, s:bar())
-endfunction
+endfunc
-function! Test_lambda_do_not_share_local_variable()
- function! s:define_funcs()
+func Test_lambda_do_not_share_local_variable()
+ func! s:define_funcs()
let l:One = {-> split(execute("let a = 'abc' | echo a"))[0]}
let l:Two = {-> exists("a") ? a : "no"}
return [l:One, l:Two]
- endfunction
+ endfunc
let l:F = s:define_funcs()
call assert_equal('no', l:F[1]())
call assert_equal('abc', l:F[0]())
call assert_equal('no', l:F[1]())
-endfunction
+endfunc
-function! Test_lambda_closure_counter()
- function! s:foo()
+func Test_lambda_closure_counter()
+ func! s:foo()
let x = 0
return {-> [execute("let x += 1"), x][-1]}
- endfunction
+ endfunc
let l:F = s:foo()
call garbagecollect()
@@ -130,52 +130,52 @@ function! Test_lambda_closure_counter()
call assert_equal(2, l:F())
call assert_equal(3, l:F())
call assert_equal(4, l:F())
-endfunction
+endfunc
-function! Test_lambda_with_a_var()
- function! s:foo()
+func Test_lambda_with_a_var()
+ func! s:foo()
let x = 2
return {... -> a:000 + [x]}
- endfunction
- function! s:bar()
+ endfunc
+ func! s:bar()
return s:foo()(1)
- endfunction
+ endfunc
call assert_equal([1, 2], s:bar())
-endfunction
+endfunc
-function! Test_lambda_call_lambda_from_lambda()
- function! s:foo(x)
+func Test_lambda_call_lambda_from_lambda()
+ func! s:foo(x)
let l:F1 = {-> {-> a:x}}
return {-> l:F1()}
- endfunction
+ endfunc
let l:F = s:foo(1)
call assert_equal(1, l:F()())
-endfunction
+endfunc
-function! Test_lambda_delfunc()
- function! s:gen()
+func Test_lambda_delfunc()
+ func! s:gen()
let pl = l:
let l:Foo = {-> get(pl, "Foo", get(pl, "Bar", {-> 0}))}
let l:Bar = l:Foo
delfunction l:Foo
return l:Bar
- endfunction
+ endfunc
let l:F = s:gen()
call assert_fails(':call l:F()', 'E933:')
-endfunction
+endfunc
-function! Test_lambda_scope()
- function! s:NewCounter()
+func Test_lambda_scope()
+ func! s:NewCounter()
let c = 0
return {-> [execute('let c += 1'), c][-1]}
- endfunction
+ endfunc
- function! s:NewCounter2()
+ func! s:NewCounter2()
return {-> [execute('let c += 100'), c][-1]}
- endfunction
+ endfunc
let l:C = s:NewCounter()
let l:D = s:NewCounter2()
@@ -183,37 +183,37 @@ function! Test_lambda_scope()
call assert_equal(1, l:C())
call assert_fails(':call l:D()', 'E121:')
call assert_equal(2, l:C())
-endfunction
+endfunc
-function! Test_lambda_share_scope()
- function! s:New()
+func Test_lambda_share_scope()
+ func! s:New()
let c = 0
let l:Inc0 = {-> [execute('let c += 1'), c][-1]}
let l:Dec0 = {-> [execute('let c -= 1'), c][-1]}
return [l:Inc0, l:Dec0]
- endfunction
+ endfunc
let [l:Inc, l:Dec] = s:New()
call assert_equal(1, l:Inc())
call assert_equal(2, l:Inc())
call assert_equal(1, l:Dec())
-endfunction
+endfunc
-function! Test_lambda_circular_reference()
- function! s:Foo()
+func Test_lambda_circular_reference()
+ func! s:Foo()
let d = {}
let d.f = {-> d}
return d.f
- endfunction
+ endfunc
call s:Foo()
call garbagecollect()
let i = 0 | while i < 10000 | call s:Foo() | let i+= 1 | endwhile
call garbagecollect()
-endfunction
+endfunc
-function! Test_lambda_combination()
+func Test_lambda_combination()
call assert_equal(2, {x -> {x -> x}}(1)(2))
call assert_equal(10, {y -> {x -> x(y)(10)}({y -> y})}({z -> z}))
call assert_equal(5.0, {x -> {y -> x / y}}(10)(2.0))
@@ -226,17 +226,17 @@ function! Test_lambda_combination()
let Z = {f -> {x -> f({y -> x(x)(y)})}({x -> f({y -> x(x)(y)})})}
let Fact = {f -> {x -> x == 0 ? 1 : x * f(x - 1)}}
call assert_equal(120, Z(Fact)(5))
-endfunction
+endfunc
-function! Test_closure_counter()
- function! s:foo()
+func Test_closure_counter()
+ func! s:foo()
let x = 0
- function! s:bar() closure
+ func! s:bar() closure
let x += 1
return x
- endfunction
+ endfunc
return function('s:bar')
- endfunction
+ endfunc
let l:F = s:foo()
call garbagecollect()
@@ -244,30 +244,30 @@ function! Test_closure_counter()
call assert_equal(2, l:F())
call assert_equal(3, l:F())
call assert_equal(4, l:F())
-endfunction
+endfunc
-function! Test_closure_unlet()
- function! s:foo()
+func Test_closure_unlet()
+ func! s:foo()
let x = 1
- function! s:bar() closure
+ func! s:bar() closure
unlet x
- endfunction
+ endfunc
call s:bar()
return l:
- endfunction
+ endfunc
call assert_false(has_key(s:foo(), 'x'))
call garbagecollect()
-endfunction
+endfunc
-function! LambdaFoo()
+func LambdaFoo()
let x = 0
- function! LambdaBar() closure
+ func! LambdaBar() closure
let x += 1
return x
- endfunction
+ endfunc
return function('LambdaBar')
-endfunction
+endfunc
func Test_closure_refcount()
let g:Count = LambdaFoo()
@@ -303,3 +303,8 @@ func Test_lambda_with_index()
let Extract = {-> function(List, ['foobar'])()[0]}
call assert_equal('foobar', Extract())
endfunc
+
+func Test_lambda_error()
+ " This was causing a crash
+ call assert_fails('ec{@{->{d->()()', 'E15')
+endfunc
diff --git a/src/nvim/testdir/test_langmap.vim b/src/nvim/testdir/test_langmap.vim
index 066c3bf2bd..4f831aa40b 100644
--- a/src/nvim/testdir/test_langmap.vim
+++ b/src/nvim/testdir/test_langmap.vim
@@ -1,5 +1,8 @@
" tests for 'langmap'
+source check.vim
+CheckFeature langmap
+
func Test_langmap()
new
set langmap=}l,^x,%v
@@ -20,5 +23,32 @@ func Test_langmap()
silent! call feedkeys("gg0}%}\<C-G>}^\<Esc>00", 'tx')
call assert_equal('a}^de', getline(1))
+ " Error cases
+ call assert_fails('set langmap=aA,b', 'E357:')
+ call assert_fails('set langmap=z;y;y;z', 'E358:')
+
+ " Map character > 256
+ enew!
+ set langmap=āx,ăl,āx
+ call setline(1, ['abcde'])
+ call feedkeys('gg2lā', 'tx')
+ call assert_equal('abde', getline(1))
+
+ " special characters in langmap
+ enew!
+ call setline(1, ['Hello World'])
+ set langmap=\\;\\,,\\,\\;
+ call feedkeys('ggfo,', 'tx')
+ call assert_equal(8, col('.'))
+ call feedkeys(';', 'tx')
+ call assert_equal(5, col('.'))
+ set langmap&
+ set langmap=\\;\\,;\\,\\;
+ call feedkeys('ggfo,', 'tx')
+ call assert_equal(8, col('.'))
+ call feedkeys(';', 'tx')
+ call assert_equal(5, col('.'))
+
+ set langmap&
quit!
endfunc
diff --git a/src/nvim/testdir/test_legacy_filetype.vim b/src/nvim/testdir/test_legacy_filetype.vim
new file mode 100644
index 0000000000..772faaadb0
--- /dev/null
+++ b/src/nvim/testdir/test_legacy_filetype.vim
@@ -0,0 +1,4 @@
+let g:do_legacy_filetype = 1
+filetype on
+
+source test_filetype.vim
diff --git a/src/nvim/testdir/test_lispwords.vim b/src/nvim/testdir/test_lispwords.vim
index aa5a738bdf..4144fb0521 100644
--- a/src/nvim/testdir/test_lispwords.vim
+++ b/src/nvim/testdir/test_lispwords.vim
@@ -1,4 +1,5 @@
-" Tests for 'lispwords' settings being global-local
+" Tests for 'lispwords' settings being global-local.
+" And other lisp indent stuff.
set nocompatible viminfo+=nviminfo
@@ -45,6 +46,7 @@ func Test_lisp_indent()
\ ])
call assert_equal(7, lispindent(2))
call assert_equal(5, 6->lispindent())
+ call assert_equal(-1, lispindent(-1))
set lisp
set lispwords&
@@ -83,3 +85,14 @@ func Test_lisp_indent()
let &cpoptions=save_copt
set nolisp
endfunc
+
+func Test_lisp_indent_works()
+ " This was reading beyond the end of the line
+ new
+ exe "norm a\tü(\<CR>="
+ set lisp
+ norm ==
+ bwipe!
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_listchars.vim b/src/nvim/testdir/test_listchars.vim
index 0bcbd9c4a5..eed4d7e30c 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!
@@ -130,7 +132,7 @@ func Test_listchars()
\ 'h<<<<<<<<<<<$',
\ '<<<<<<<<<<<<$',
\ '>>>>0xx0<<<<$',
- \ '$'
+ \ '$'
\ ]
redraw!
for i in range(1, 5)
@@ -160,7 +162,7 @@ func Test_listchars()
\ ' hyYzZyYzZyY$',
\ 'yYzZyYzZyYj $',
\ 'yYzZ0yY0yYzZ$',
- \ '$'
+ \ '$'
\ ]
redraw!
for i in range(1, 5)
@@ -170,7 +172,133 @@ func Test_listchars()
call assert_equal(expected, split(execute("%list"), "\n"))
+ " Test leadmultispace + multispace
+ normal ggdG
+ set listchars=eol:$,multispace:yYzZ,nbsp:S
+ set listchars+=leadmultispace:.-+*
+ set list
+
+ call append(0, [
+ \ ' ffff ',
+ \ ' i i  gg',
+ \ ' h ',
+ \ ' j ',
+ \ ' 0 0 ',
+ \ ])
+
+ let expected = [
+ \ '.-+*ffffyYzZ$',
+ \ '.-i iSyYzZgg$',
+ \ ' hyYzZyYzZyY$',
+ \ '.-+*.-+*.-j $',
+ \ '.-+*0yY0yYzZ$',
+ \ '$'
+ \ ]
+ redraw!
+ call assert_equal('eol:$,multispace:yYzZ,nbsp:S,leadmultispace:.-+*', &listchars)
+ for i in range(1, 5)
+ call cursor(i, 1)
+ call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$')))
+ endfor
+
+ call assert_equal(expected, split(execute("%list"), "\n"))
+
+ " Test leadmultispace without multispace
+ normal ggdG
+ set listchars-=multispace:yYzZ
+ set listchars+=space:+,trail:>,eol:$
+ set list
+
+ call append(0, [
+ \ ' ffff ',
+ \ ' i i gg',
+ \ ' h ',
+ \ ' j ',
+ \ ' 0 0 ',
+ \ ])
+
+ let expected = [
+ \ '.-+*ffff>>>>$',
+ \ '.-i+i+++++gg$',
+ \ '+h>>>>>>>>>>$',
+ \ '.-+*.-+*.-j>$',
+ \ '.-+*0++0>>>>$',
+ \ '$',
+ \ ]
+
+ redraw!
+ call assert_equal('eol:$,nbsp:S,leadmultispace:.-+*,space:+,trail:>,eol:$', &listchars)
+ for i in range(1, 5)
+ call cursor(i, 1)
+ call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$')))
+ endfor
+
+ call assert_equal(expected, split(execute("%list"), "\n"))
+
+ " Test leadmultispace only
+ normal ggdG
+ set listchars=eol:$ " Accommodate Nvim default
+ set listchars=leadmultispace:.-+*
+ set list
+
+ call append(0, [
+ \ ' ffff ',
+ \ ' i i gg',
+ \ ' h ',
+ \ ' j ',
+ \ ' 0 0 ',
+ \ ])
+
+ let expected = [
+ \ '.-+*ffff ',
+ \ '.-i i gg',
+ \ ' h ',
+ \ '.-+*.-+*.-j ',
+ \ '.-+*0 0 ',
+ \ ' ',
+ \ ]
+ redraw!
+ call assert_equal('leadmultispace:.-+*', &listchars)
+ for i in range(1, 5)
+ call cursor(i, 1)
+ call assert_equal([expected[i - 1]], ScreenLines(i, 12))
+ endfor
+ call assert_equal(expected, split(execute("%list"), "\n"))
+
+ " Test leadmultispace and lead and space
+ normal ggdG
+ set listchars=eol:$ " Accommodate Nvim default
+ set listchars+=lead:<,space:-
+ set listchars+=leadmultispace:.-+*
+ set list
+
+ call append(0, [
+ \ ' ffff ',
+ \ ' i i gg',
+ \ ' h ',
+ \ ' j ',
+ \ ' 0 0 ',
+ \ ])
+
+ let expected = [
+ \ '.-+*ffff----$',
+ \ '.-i-i-----gg$',
+ \ '<h----------$',
+ \ '.-+*.-+*.-j-$',
+ \ '.-+*0--0----$',
+ \ '$',
+ \ ]
+ redraw!
+ call assert_equal('eol:$,lead:<,space:-,leadmultispace:.-+*', &listchars)
+ for i in range(1, 5)
+ call cursor(i, 1)
+ call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$')))
+ endfor
+ call assert_equal(expected, split(execute("%list"), "\n"))
+
" the last occurrence of 'multispace:' is used
+ set listchars=eol:$ " Accommodate Nvim default
+ set listchars+=multispace:yYzZ
set listchars+=space:x,multispace:XyY
let expected = [
@@ -179,9 +307,10 @@ func Test_listchars()
\ 'xhXyYXyYXyYX$',
\ 'XyYXyYXyYXjx$',
\ 'XyYX0Xy0XyYX$',
- \ '$'
+ \ '$'
\ ]
redraw!
+ call assert_equal('eol:$,multispace:yYzZ,space:x,multispace:XyY', &listchars)
for i in range(1, 5)
call cursor(i, 1)
call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$')))
@@ -197,7 +326,7 @@ func Test_listchars()
\ '>h<<<<<<<<<<$',
\ '>>>>>>>>>>j<$',
\ '>>>>0Xy0<<<<$',
- \ '$'
+ \ '$'
\ ]
redraw!
for i in range(1, 5)
@@ -217,7 +346,7 @@ func Test_listchars()
\ '>h<<<<<<<<<<$',
\ '>>>>>>>>>>j<$',
\ '>>>>0xx0<<<<$',
- \ '$'
+ \ '$'
\ ]
redraw!
for i in range(1, 5)
@@ -313,11 +442,13 @@ func Test_listchars_invalid()
call assert_fails('set listchars=x', 'E474:')
call assert_fails('set listchars=x', 'E474:')
call assert_fails('set listchars=multispace', 'E474:')
+ call assert_fails('set listchars=leadmultispace', 'E474:')
" Too short
call assert_fails('set listchars=space:', 'E474:')
call assert_fails('set listchars=tab:x', 'E474:')
call assert_fails('set listchars=multispace:', 'E474:')
+ call assert_fails('set listchars=leadmultispace:', 'E474:')
" One occurrence too short
call assert_fails('set listchars=space:,space:x', 'E474:')
@@ -326,6 +457,8 @@ func Test_listchars_invalid()
call assert_fails('set listchars=tab:xx,tab:x', 'E474:')
call assert_fails('set listchars=multispace:,multispace:x', 'E474:')
call assert_fails('set listchars=multispace:x,multispace:', 'E474:')
+ call assert_fails('set listchars=leadmultispace:,leadmultispace:x', 'E474:')
+ call assert_fails('set listchars=leadmultispace:x,leadmultispace:', 'E474:')
" Too long
call assert_fails('set listchars=space:xx', 'E474:')
@@ -338,6 +471,8 @@ func Test_listchars_invalid()
call assert_fails('set listchars=tab:xx·', 'E474:')
call assert_fails('set listchars=multispace:·', 'E474:')
call assert_fails('set listchars=multispace:xxx·', 'E474:')
+ call assert_fails('set listchars=leadmultispace:·', 'E474:')
+ call assert_fails('set listchars=leadmultispace:xxx·', 'E474:')
" Has control character
call assert_fails("set listchars=space:\x01", 'E474:')
@@ -352,6 +487,10 @@ func Test_listchars_invalid()
call assert_fails('set listchars=tab:xx\\x01', 'E474:')
call assert_fails('set listchars=multispace:\\x01', 'E474:')
call assert_fails('set listchars=multispace:xxx\\x01', 'E474:')
+ call assert_fails("set listchars=leadmultispace:\x01", 'E474:')
+ call assert_fails('set listchars=leadmultispace:\\x01', 'E474:')
+ call assert_fails("set listchars=leadmultispace:xxx\x01", 'E474:')
+ call assert_fails('set listchars=leadmultispace:xxx\\x01', 'E474:')
enew!
set ambiwidth& listchars& ff&
@@ -517,4 +656,40 @@ 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', {})
+ call term_sendkeys(buf, "\<C-W>h")
+ call term_sendkeys(buf, ":set nowrap foldcolumn=4\<CR>")
+ call term_sendkeys(buf, "15\<C-W><")
+ call VerifyScreenDump(buf, 'Test_listchars_06', {})
+ call term_sendkeys(buf, "4\<C-W><")
+ call VerifyScreenDump(buf, 'Test_listchars_07', {})
+
+ " clean up
+ call StopVimInTerminal(buf)
+ call delete('XTest_listchars')
+endfunc
+
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_listdict.vim b/src/nvim/testdir/test_listdict.vim
index f6c404d390..aa66d86af1 100644
--- a/src/nvim/testdir/test_listdict.vim
+++ b/src/nvim/testdir/test_listdict.vim
@@ -516,22 +516,22 @@ func Test_dict_lock_operator()
endfunc
" No remove() of write-protected scope-level variable
-func! Tfunc(this_is_a_long_parameter_name)
+func Tfunc1(this_is_a_long_parameter_name)
call assert_fails("call remove(a:, 'this_is_a_long_parameter_name')", 'E742')
-endfun
+endfunc
func Test_dict_scope_var_remove()
- call Tfunc('testval')
+ call Tfunc1('testval')
endfunc
" No extend() of write-protected scope-level variable
func Test_dict_scope_var_extend()
call assert_fails("call extend(a:, {'this_is_a_long_parameter_name': 1234})", 'E742')
endfunc
-func! Tfunc(this_is_a_long_parameter_name)
+func Tfunc2(this_is_a_long_parameter_name)
call assert_fails("call extend(a:, {'this_is_a_long_parameter_name': 1234})", 'E742')
endfunc
func Test_dict_scope_var_extend_overwrite()
- call Tfunc('testval')
+ call Tfunc2('testval')
endfunc
" No :unlet of variable in locked scope
@@ -620,6 +620,49 @@ func Test_reverse_sort_uniq()
call assert_fails('call reverse("")', 'E899:')
endfunc
+" reduce a list or a blob
+func Test_reduce()
+ call assert_equal(1, reduce([], { acc, val -> acc + val }, 1))
+ call assert_equal(10, reduce([1, 3, 5], { acc, val -> acc + val }, 1))
+ call assert_equal(2 * (2 * ((2 * 1) + 2) + 3) + 4, reduce([2, 3, 4], { acc, val -> 2 * acc + val }, 1))
+ call assert_equal('a x y z', ['x', 'y', 'z']->reduce({ acc, val -> acc .. ' ' .. val}, 'a'))
+ call assert_equal(#{ x: 1, y: 1, z: 1 }, ['x', 'y', 'z']->reduce({ acc, val -> extend(acc, { val: 1 }) }, {}))
+ call assert_equal([0, 1, 2, 3], reduce([1, 2, 3], function('add'), [0]))
+
+ let l = ['x', 'y', 'z']
+ call assert_equal(42, reduce(l, function('get'), #{ x: #{ y: #{ z: 42 } } }))
+ call assert_equal(['x', 'y', 'z'], l)
+
+ call assert_equal(1, reduce([1], { acc, val -> acc + val }))
+ call assert_equal('x y z', reduce(['x', 'y', 'z'], { acc, val -> acc .. ' ' .. val }))
+ call assert_equal(120, range(1, 5)->reduce({ acc, val -> acc * val }))
+ call assert_fails("call reduce([], { acc, val -> acc + val })", 'E998: Reduce of an empty List with no initial value')
+
+ call assert_equal(1, reduce(0z, { acc, val -> acc + val }, 1))
+ call assert_equal(1 + 0xaf + 0xbf + 0xcf, reduce(0zAFBFCF, { acc, val -> acc + val }, 1))
+ call assert_equal(2 * (2 * 1 + 0xaf) + 0xbf, 0zAFBF->reduce({ acc, val -> 2 * acc + val }, 1))
+
+ call assert_equal(0xff, reduce(0zff, { acc, val -> acc + val }))
+ call assert_equal(2 * (2 * 0xaf + 0xbf) + 0xcf, reduce(0zAFBFCF, { acc, val -> 2 * acc + val }))
+ call assert_fails("call reduce(0z, { acc, val -> acc + val })", 'E998: Reduce of an empty Blob with no initial value')
+
+ call assert_fails("call reduce({}, { acc, val -> acc + val }, 1)", 'E897:')
+ call assert_fails("call reduce(0, { acc, val -> acc + val }, 1)", 'E897:')
+ call assert_fails("call reduce('', { acc, val -> acc + val }, 1)", 'E897:')
+
+ let g:lut = [1, 2, 3, 4]
+ func EvilRemove()
+ call remove(g:lut, 1)
+ return 1
+ endfunc
+ call assert_fails("call reduce(g:lut, { acc, val -> EvilRemove() }, 1)", 'E742:')
+ unlet g:lut
+ delfunc EvilRemove
+
+ call assert_equal(42, reduce(v:_null_list, function('add'), 42))
+ call assert_equal(42, reduce(v:_null_blob, function('add'), 42))
+endfunc
+
" splitting a string to a List
func Test_str_split()
call assert_equal(['aa', 'bb'], split(' aa bb '))
diff --git a/src/nvim/testdir/test_listlbr.vim b/src/nvim/testdir/test_listlbr.vim
index 2fda12d8b4..affa0f96fa 100644
--- a/src/nvim/testdir/test_listlbr.vim
+++ b/src/nvim/testdir/test_listlbr.vim
@@ -2,9 +2,9 @@
scriptencoding latin1
-if !exists("+linebreak") || !has("conceal")
- finish
-endif
+source check.vim
+CheckOption linebreak
+CheckFeature conceal
source view_util.vim
diff --git a/src/nvim/testdir/test_listlbr_utf8.vim b/src/nvim/testdir/test_listlbr_utf8.vim
index c38e0c5f3c..df1ed78119 100644
--- a/src/nvim/testdir/test_listlbr_utf8.vim
+++ b/src/nvim/testdir/test_listlbr_utf8.vim
@@ -3,9 +3,10 @@
set encoding=utf-8
scriptencoding utf-8
-if !exists("+linebreak") || !has("conceal") || !has("signs")
- finish
-endif
+source check.vim
+CheckOption linebreak
+CheckFeature conceal
+CheckFeature signs
source view_util.vim
@@ -69,6 +70,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_makeencoding.vim b/src/nvim/testdir/test_makeencoding.vim
index 2b346e0720..c53c07d991 100644
--- a/src/nvim/testdir/test_makeencoding.vim
+++ b/src/nvim/testdir/test_makeencoding.vim
@@ -4,8 +4,7 @@ source shared.vim
let s:python = PythonProg()
if s:python == ''
- " Can't run this test.
- finish
+ throw 'Skipped: python program missing'
endif
let s:script = 'test_makeencoding.py'
diff --git a/src/nvim/testdir/test_maparg.vim b/src/nvim/testdir/test_maparg.vim
index 5b082198cf..f9429a8020 100644
--- a/src/nvim/testdir/test_maparg.vim
+++ b/src/nvim/testdir/test_maparg.vim
@@ -42,14 +42,53 @@ function Test_maparg()
map abc y<S-char-114>y
call assert_equal("yRy", maparg('abc'))
+ " character with K_SPECIAL byte
+ nmap abc …
+ call assert_equal('…', maparg('abc'))
+
+ " modified character with K_SPECIAL byte
+ nmap abc <M-…>
+ call assert_equal('<M-…>', maparg('abc'))
+
+ " illegal bytes
+ let str = ":\x7f:\x80:\x90:\xd0:"
+ exe 'nmap abc ' .. str
+ call assert_equal(str, maparg('abc'))
+ unlet str
+
omap { w
let d = maparg('{', 'o', 0, 1)
call assert_equal(['{', 'w', 'o'], [d.lhs, d.rhs, d.mode])
ounmap {
+ lmap { w
+ let d = maparg('{', 'l', 0, 1)
+ call assert_equal(['{', 'w', 'l'], [d.lhs, d.rhs, d.mode])
+ lunmap {
+
+ nmap { w
+ let d = maparg('{', 'n', 0, 1)
+ call assert_equal(['{', 'w', 'n'], [d.lhs, d.rhs, d.mode])
+ nunmap {
+
+ xmap { w
+ let d = maparg('{', 'x', 0, 1)
+ call assert_equal(['{', 'w', 'x'], [d.lhs, d.rhs, d.mode])
+ xunmap {
+
+ smap { w
+ let d = maparg('{', 's', 0, 1)
+ call assert_equal(['{', 'w', 's'], [d.lhs, d.rhs, d.mode])
+ sunmap {
+
map abc <Nop>
call assert_equal("<Nop>", maparg('abc'))
unmap abc
+
+ call feedkeys(":abbr esc \<C-V>\<C-V>\<C-V>\<C-V>\<C-V>\<Esc>\<CR>", "xt")
+ let d = maparg('esc', 'i', 1, 1)
+ call assert_equal(['esc', "\<C-V>\<C-V>\<Esc>", '!'], [d.lhs, d.rhs, d.mode])
+ abclear
endfunction
func Test_mapcheck()
diff --git a/src/nvim/testdir/test_mapping.vim b/src/nvim/testdir/test_mapping.vim
index f88e8cf843..e1d0b9a9ba 100644
--- a/src/nvim/testdir/test_mapping.vim
+++ b/src/nvim/testdir/test_mapping.vim
@@ -1,6 +1,8 @@
" Tests for mappings and abbreviations
source shared.vim
+source check.vim
+source screendump.vim
func Test_abbreviation()
" abbreviation with 0x80 should work
@@ -29,6 +31,7 @@ func Test_abclear()
abclear
call assert_equal("\n\nNo abbreviation found", execute('abbrev'))
+ call assert_fails('%abclear', 'E481:')
endfunc
func Test_abclear_buffer()
@@ -60,7 +63,7 @@ func Test_map_ctrl_c_insert()
inoremap <c-c> <ctrl-c>
cnoremap <c-c> dummy
cunmap <c-c>
- call feedkeys("GoTEST2: CTRL-C |\<C-C>A|\<Esc>", "xt")
+ call feedkeys("GoTEST2: CTRL-C |\<*C-C>A|\<Esc>", "xt")
call assert_equal('TEST2: CTRL-C |<ctrl-c>A|', getline('$'))
unmap! <c-c>
set nomodified
@@ -69,7 +72,7 @@ endfunc
func Test_map_ctrl_c_visual()
" mapping of ctrl-c in Visual mode
vnoremap <c-c> :<C-u>$put ='vmap works'
- call feedkeys("GV\<C-C>\<CR>", "xt")
+ call feedkeys("GV\<*C-C>\<CR>", "xt")
call assert_equal('vmap works', getline('$'))
vunmap <c-c>
set nomodified
@@ -219,7 +222,7 @@ endfunc
func Test_map_meta_quotes()
imap <M-"> foo
- call feedkeys("Go-\<M-\">-\<Esc>", "xt")
+ call feedkeys("Go-\<*M-\">-\<Esc>", "xt")
call assert_equal("-foo-", getline('$'))
set nomodified
iunmap <M-">
@@ -427,6 +430,85 @@ func Test_error_in_map_expr()
exe buf .. 'bwipe!'
endfunc
+func Test_list_mappings()
+ " Remove default mappings
+ imapclear
+
+ " reset 'isident' to check it isn't used
+ set isident=
+ inoremap <C-m> CtrlM
+ inoremap <A-S> AltS
+ inoremap <S-/> ShiftSlash
+ set isident&
+ call assert_equal([
+ \ 'i <S-/> * ShiftSlash',
+ \ 'i <M-S> * AltS',
+ \ 'i <C-M> * CtrlM',
+ \], execute('imap')->trim()->split("\n"))
+ iunmap <C-M>
+ iunmap <A-S>
+ call assert_equal(['i <S-/> * ShiftSlash'], execute('imap')->trim()->split("\n"))
+ iunmap <S-/>
+ call assert_equal(['No mapping found'], execute('imap')->trim()->split("\n"))
+
+ " List global, buffer local and script local mappings
+ nmap ,f /^\k\+ (<CR>
+ nmap <buffer> ,f /^\k\+ (<CR>
+ nmap <script> ,fs /^\k\+ (<CR>
+ call assert_equal(['n ,f @/^\k\+ (<CR>',
+ \ 'n ,fs & /^\k\+ (<CR>',
+ \ 'n ,f /^\k\+ (<CR>'],
+ \ execute('nmap ,f')->trim()->split("\n"))
+
+ " List <Nop> mapping
+ nmap ,n <Nop>
+ call assert_equal(['n ,n <Nop>'],
+ \ execute('nmap ,n')->trim()->split("\n"))
+
+ " verbose map
+ call assert_match("\tLast set from .*/test_mapping.vim line \\d\\+$",
+ \ execute('verbose map ,n')->trim()->split("\n")[1])
+
+ " character with K_SPECIAL byte in rhs
+ nmap foo …
+ call assert_equal(['n foo …'],
+ \ execute('nmap foo')->trim()->split("\n"))
+
+ " modified character with K_SPECIAL byte in rhs
+ nmap foo <M-…>
+ call assert_equal(['n foo <M-…>'],
+ \ execute('nmap foo')->trim()->split("\n"))
+
+ " character with K_SPECIAL byte in lhs
+ nmap … foo
+ call assert_equal(['n … foo'],
+ \ execute('nmap …')->trim()->split("\n"))
+
+ " modified character with K_SPECIAL byte in lhs
+ nmap <M-…> foo
+ call assert_equal(['n <M-…> foo'],
+ \ execute('nmap <M-…>')->trim()->split("\n"))
+
+ " illegal bytes
+ let str = ":\x7f:\x80:\x90:\xd0:"
+ exe 'nmap foo ' .. str
+ call assert_equal(['n foo ' .. strtrans(str)],
+ \ execute('nmap foo')->trim()->split("\n"))
+ unlet str
+
+ " map to CTRL-V
+ exe "nmap ,k \<C-V>"
+ call assert_equal(['n ,k <Nop>'],
+ \ execute('nmap ,k')->trim()->split("\n"))
+
+ " map with space at the beginning
+ exe "nmap \<C-V> w <Nop>"
+ call assert_equal(['n <Space>w <Nop>'],
+ \ execute("nmap \<C-V> w")->trim()->split("\n"))
+
+ nmapclear
+endfunc
+
func Test_expr_map_gets_cursor()
new
call setline(1, ['one', 'some w!rd'])
@@ -451,6 +533,82 @@ func Test_expr_map_gets_cursor()
nunmap !
endfunc
+func Test_expr_map_restore_cursor()
+ CheckScreendump
+
+ let lines =<< trim END
+ call setline(1, ['one', 'two', 'three'])
+ 2
+ set ls=2
+ hi! link StatusLine ErrorMsg
+ noremap <expr> <C-B> Func()
+ func Func()
+ let g:on = !get(g:, 'on', 0)
+ redraws
+ return ''
+ endfunc
+ func Status()
+ return get(g:, 'on', 0) ? '[on]' : ''
+ endfunc
+ set stl=%{Status()}
+ END
+ call writefile(lines, 'XtestExprMap')
+ let buf = RunVimInTerminal('-S XtestExprMap', #{rows: 10})
+ call term_sendkeys(buf, "\<C-B>")
+ call VerifyScreenDump(buf, 'Test_map_expr_1', {})
+
+ " clean up
+ call StopVimInTerminal(buf)
+ call delete('XtestExprMap')
+endfunc
+
+func Test_map_listing()
+ CheckScreendump
+
+ let lines =<< trim END
+ nmap a b
+ END
+ call writefile(lines, 'XtestMapList')
+ let buf = RunVimInTerminal('-S XtestMapList', #{rows: 6})
+ call term_sendkeys(buf, ": nmap a\<CR>")
+ call VerifyScreenDump(buf, 'Test_map_list_1', {})
+
+ " clean up
+ call StopVimInTerminal(buf)
+ call delete('XtestMapList')
+endfunc
+
+func Test_expr_map_error()
+ CheckScreendump
+
+ let lines =<< trim END
+ func Func()
+ throw 'test'
+ return ''
+ endfunc
+
+ nnoremap <expr> <F2> Func()
+ cnoremap <expr> <F2> Func()
+
+ call test_override('ui_delay', 10)
+ END
+ call writefile(lines, 'XtestExprMap')
+ let buf = RunVimInTerminal('-S XtestExprMap', #{rows: 10})
+ call term_sendkeys(buf, "\<F2>")
+ call TermWait(buf)
+ call term_sendkeys(buf, "\<CR>")
+ call VerifyScreenDump(buf, 'Test_map_expr_2', {})
+
+ call term_sendkeys(buf, ":abc\<F2>")
+ call VerifyScreenDump(buf, 'Test_map_expr_3', {})
+ call term_sendkeys(buf, "\<Esc>0")
+ call VerifyScreenDump(buf, 'Test_map_expr_4', {})
+
+ " clean up
+ call StopVimInTerminal(buf)
+ call delete('XtestExprMap')
+endfunc
+
" Test for mapping errors
func Test_map_error()
call assert_fails('unmap', 'E474:')
@@ -476,6 +634,22 @@ func Test_map_error()
call assert_fails('mapclear abc', 'E474:')
call assert_fails('abclear abc', 'E474:')
+ call assert_fails('abbr $xyz abc', 'E474:')
+
+ " space character in an abbreviation
+ call assert_fails('abbr ab<space> ABC', 'E474:')
+
+ " invalid <expr> map
+ map <expr> ,f abc
+ call assert_fails('normal ,f', 'E121:')
+ unmap <expr> ,f
+
+ " Recursive use of :normal in a map
+ set maxmapdepth=100
+ map gq :normal gq<CR>
+ call assert_fails('normal gq', 'E192:')
+ unmap gq
+ set maxmapdepth&
endfunc
" Test for <special> key mapping
@@ -503,11 +677,66 @@ endfunc
" Test for hasmapto()
func Test_hasmapto()
call assert_equal(0, hasmapto('/^\k\+ ('))
+ map ,f /^\k\+ (<CR>
+ call assert_equal(1, hasmapto('/^\k\+ ('))
+ unmap ,f
+
+ " Insert mode mapping
+ call assert_equal(0, hasmapto('/^\k\+ (', 'i'))
+ imap ,f /^\k\+ (<CR>
+ call assert_equal(1, hasmapto('/^\k\+ (', 'i'))
+ iunmap ,f
+
+ " Normal mode mapping
call assert_equal(0, hasmapto('/^\k\+ (', 'n'))
nmap ,f /^\k\+ (<CR>
call assert_equal(1, hasmapto('/^\k\+ ('))
call assert_equal(1, hasmapto('/^\k\+ (', 'n'))
+ nunmap ,f
+
+ " Visual and Select mode mapping
call assert_equal(0, hasmapto('/^\k\+ (', 'v'))
+ call assert_equal(0, hasmapto('/^\k\+ (', 'x'))
+ call assert_equal(0, hasmapto('/^\k\+ (', 's'))
+ vmap ,f /^\k\+ (<CR>
+ call assert_equal(1, hasmapto('/^\k\+ (', 'v'))
+ call assert_equal(1, hasmapto('/^\k\+ (', 'x'))
+ call assert_equal(1, hasmapto('/^\k\+ (', 's'))
+ vunmap ,f
+
+ " Visual mode mapping
+ call assert_equal(0, hasmapto('/^\k\+ (', 'x'))
+ xmap ,f /^\k\+ (<CR>
+ call assert_equal(1, hasmapto('/^\k\+ (', 'v'))
+ call assert_equal(1, hasmapto('/^\k\+ (', 'x'))
+ call assert_equal(0, hasmapto('/^\k\+ (', 's'))
+ xunmap ,f
+
+ " Select mode mapping
+ call assert_equal(0, hasmapto('/^\k\+ (', 's'))
+ smap ,f /^\k\+ (<CR>
+ call assert_equal(1, hasmapto('/^\k\+ (', 'v'))
+ call assert_equal(0, hasmapto('/^\k\+ (', 'x'))
+ call assert_equal(1, hasmapto('/^\k\+ (', 's'))
+ sunmap ,f
+
+ " Operator-pending mode mapping
+ call assert_equal(0, hasmapto('/^\k\+ (', 'o'))
+ omap ,f /^\k\+ (<CR>
+ call assert_equal(1, hasmapto('/^\k\+ (', 'o'))
+ ounmap ,f
+
+ " Language mapping
+ call assert_equal(0, hasmapto('/^\k\+ (', 'l'))
+ lmap ,f /^\k\+ (<CR>
+ call assert_equal(1, hasmapto('/^\k\+ (', 'l'))
+ lunmap ,f
+
+ " Cmdline mode mapping
+ call assert_equal(0, hasmapto('/^\k\+ (', 'c'))
+ cmap ,f /^\k\+ (<CR>
+ call assert_equal(1, hasmapto('/^\k\+ (', 'c'))
+ cunmap ,f
call assert_equal(0, hasmapto('/^\k\+ (', 'n', 1))
endfunc
@@ -519,8 +748,176 @@ func Test_mapcomplete()
\ getcompletion('', 'mapping'))
call assert_equal([], getcompletion(',d', 'mapping'))
+ call feedkeys(":unmap <buf\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"unmap <buffer>', @:)
+
+ call feedkeys(":unabbr <buf\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"unabbr <buffer>', @:)
+
call feedkeys(":abbr! \<C-A>\<C-B>\"\<CR>", 'tx')
- call assert_match("abbr! \x01", @:)
+ call assert_equal("\"abbr! \x01", @:)
+
+ " Multiple matches for a map
+ nmap ,f /H<CR>
+ omap ,f /H<CR>
+ call feedkeys(":map ,\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"map ,f', @:)
+ mapclear
+endfunc
+
+" Test for <expr> in abbreviation
+func Test_expr_abbr()
+ new
+ iabbr <expr> teh "the"
+ call feedkeys("iteh ", "tx")
+ call assert_equal('the ', getline(1))
+ iabclear
+ call setline(1, '')
+
+ " invalid <expr> abbreviation
+ abbr <expr> hte GetAbbr()
+ call assert_fails('normal ihte ', 'E117:')
+ call assert_equal(' ', getline(1))
+ unabbr <expr> hte
+
+ close!
+endfunc
+
+" Test for storing mappings in different modes in a vimrc file
+func Test_mkvimrc_mapmodes()
+ map a1 /a1
+ nmap a2 /a2
+ vmap a3 /a3
+ smap a4 /a4
+ xmap a5 /a5
+ omap a6 /a6
+ map! a7 /a7
+ imap a8 /a8
+ lmap a9 /a9
+ cmap a10 /a10
+ tmap a11 /a11
+ " Normal + Visual map
+ map a12 /a12
+ sunmap a12
+ ounmap a12
+ " Normal + Selectmode map
+ map a13 /a13
+ xunmap a13
+ ounmap a13
+ " Normal + OpPending map
+ map a14 /a14
+ vunmap a14
+ " Visual + Selectmode map
+ map a15 /a15
+ nunmap a15
+ ounmap a15
+ " Visual + OpPending map
+ map a16 /a16
+ nunmap a16
+ sunmap a16
+ " Selectmode + OpPending map
+ map a17 /a17
+ nunmap a17
+ xunmap a17
+ " Normal + Visual + Selectmode map
+ map a18 /a18
+ ounmap a18
+ " Normal + Visual + OpPending map
+ map a19 /a19
+ sunmap a19
+ " Normal + Selectmode + OpPending map
+ map a20 /a20
+ xunmap a20
+ " Visual + Selectmode + OpPending map
+ map a21 /a21
+ nunmap a21
+ " Mapping to Nop
+ map a22 <Nop>
+ " Script local mapping
+ map <script> a23 /a23
+
+ " Newline in {lhs} and {rhs} of a map
+ exe "map a24\<C-V>\<C-J> ia24\<C-V>\<C-J><Esc>"
+
+ " Abbreviation
+ abbr a25 A25
+ cabbr a26 A26
+ iabbr a27 A27
+
+ mkvimrc! Xvimrc
+ let l = readfile('Xvimrc')
+ call assert_equal(['map a1 /a1'], filter(copy(l), 'v:val =~ " a1 "'))
+ call assert_equal(['nmap a2 /a2'], filter(copy(l), 'v:val =~ " a2 "'))
+ call assert_equal(['vmap a3 /a3'], filter(copy(l), 'v:val =~ " a3 "'))
+ call assert_equal(['smap a4 /a4'], filter(copy(l), 'v:val =~ " a4 "'))
+ call assert_equal(['xmap a5 /a5'], filter(copy(l), 'v:val =~ " a5 "'))
+ call assert_equal(['omap a6 /a6'], filter(copy(l), 'v:val =~ " a6 "'))
+ call assert_equal(['map! a7 /a7'], filter(copy(l), 'v:val =~ " a7 "'))
+ call assert_equal(['imap a8 /a8'], filter(copy(l), 'v:val =~ " a8 "'))
+ call assert_equal(['lmap a9 /a9'], filter(copy(l), 'v:val =~ " a9 "'))
+ call assert_equal(['cmap a10 /a10'], filter(copy(l), 'v:val =~ " a10 "'))
+ call assert_equal(['tmap a11 /a11'], filter(copy(l), 'v:val =~ " a11 "'))
+ call assert_equal(['nmap a12 /a12', 'xmap a12 /a12'],
+ \ filter(copy(l), 'v:val =~ " a12 "'))
+ call assert_equal(['nmap a13 /a13', 'smap a13 /a13'],
+ \ filter(copy(l), 'v:val =~ " a13 "'))
+ call assert_equal(['nmap a14 /a14', 'omap a14 /a14'],
+ \ filter(copy(l), 'v:val =~ " a14 "'))
+ call assert_equal(['vmap a15 /a15'], filter(copy(l), 'v:val =~ " a15 "'))
+ call assert_equal(['xmap a16 /a16', 'omap a16 /a16'],
+ \ filter(copy(l), 'v:val =~ " a16 "'))
+ call assert_equal(['smap a17 /a17', 'omap a17 /a17'],
+ \ filter(copy(l), 'v:val =~ " a17 "'))
+ call assert_equal(['nmap a18 /a18', 'vmap a18 /a18'],
+ \ filter(copy(l), 'v:val =~ " a18 "'))
+ call assert_equal(['nmap a19 /a19', 'xmap a19 /a19', 'omap a19 /a19'],
+ \ filter(copy(l), 'v:val =~ " a19 "'))
+ call assert_equal(['nmap a20 /a20', 'smap a20 /a20', 'omap a20 /a20'],
+ \ filter(copy(l), 'v:val =~ " a20 "'))
+ call assert_equal(['vmap a21 /a21', 'omap a21 /a21'],
+ \ filter(copy(l), 'v:val =~ " a21 "'))
+ call assert_equal(['map a22 <Nop>'], filter(copy(l), 'v:val =~ " a22 "'))
+ call assert_equal([], filter(copy(l), 'v:val =~ " a23 "'))
+ call assert_equal(["map a24<NL> ia24<NL>\x16\e"],
+ \ filter(copy(l), 'v:val =~ " a24"'))
+
+ call assert_equal(['abbr a25 A25'], filter(copy(l), 'v:val =~ " a25 "'))
+ call assert_equal(['cabbr a26 A26'], filter(copy(l), 'v:val =~ " a26 "'))
+ call assert_equal(['iabbr a27 A27'], filter(copy(l), 'v:val =~ " a27 "'))
+ call delete('Xvimrc')
+
+ mapclear
+ nmapclear
+ vmapclear
+ xmapclear
+ smapclear
+ omapclear
+ imapclear
+ lmapclear
+ cmapclear
+ tmapclear
+endfunc
+
+" Test for recursive mapping ('maxmapdepth')
+func Test_map_recursive()
+ map x y
+ map y x
+ call assert_fails('normal x', 'E223:')
+ unmap x
+ unmap y
+endfunc
+
+" Test for removing an abbreviation using {rhs} and with space after {lhs}
+func Test_abbr_remove()
+ abbr foo bar
+ let d = maparg('foo', 'i', 1, 1)
+ call assert_equal(['foo', 'bar', '!'], [d.lhs, d.rhs, d.mode])
+ unabbr bar
+ call assert_equal({}, maparg('foo', 'i', 1, 1))
+
+ abbr foo bar
+ unabbr foo<space><tab>
+ call assert_equal({}, maparg('foo', 'i', 1, 1))
endfunc
func Test_map_cmdkey_redo()
@@ -559,6 +956,16 @@ func Test_map_cmdkey_redo()
ounmap i-
endfunc
+" Test for using <script> with a map to remap characters in rhs
+func Test_script_local_remap()
+ new
+ inoremap <buffer> <SID>xyz mno
+ inoremap <buffer> <script> abc st<SID>xyzre
+ normal iabc
+ call assert_equal('stmnore', getline(1))
+ bwipe!
+endfunc
+
func Test_abbreviate_multi_byte()
new
iabbrev foo bar
@@ -568,4 +975,158 @@ func Test_abbreviate_multi_byte()
bwipe!
endfunc
+" Test for <Plug> always being mapped, even when used with "noremap".
+func Test_plug_remap()
+ let g:foo = 0
+ nnoremap <Plug>(Increase_x) <Cmd>let g:foo += 1<CR>
+ nmap <F2> <Plug>(Increase_x)
+ nnoremap <F3> <Plug>(Increase_x)
+ call feedkeys("\<F2>", 'xt')
+ call assert_equal(1, g:foo)
+ call feedkeys("\<F3>", 'xt')
+ call assert_equal(2, g:foo)
+ nnoremap x <Nop>
+ nmap <F4> x<Plug>(Increase_x)x
+ nnoremap <F5> x<Plug>(Increase_x)x
+ call setline(1, 'Some text')
+ normal! gg$
+ call feedkeys("\<F4>", 'xt')
+ call assert_equal(3, g:foo)
+ call assert_equal('Some text', getline(1))
+ call feedkeys("\<F5>", 'xt')
+ call assert_equal(4, g:foo)
+ call assert_equal('Some te', getline(1))
+ nunmap <Plug>(Increase_x)
+ nunmap <F2>
+ nunmap <F3>
+ nunmap <F4>
+ nunmap <F5>
+ unlet g:foo
+ %bw!
+endfunc
+
+func Test_mouse_drag_mapped_start_select()
+ CheckFunction test_setmouse
+ set mouse=a
+ set selectmode=key,mouse
+ func ClickExpr()
+ call test_setmouse(1, 1)
+ return "\<LeftMouse>"
+ endfunc
+ func DragExpr()
+ call test_setmouse(1, 2)
+ return "\<LeftDrag>"
+ endfunc
+ nnoremap <expr> <F2> ClickExpr()
+ nmap <expr> <F3> DragExpr()
+
+ nnoremap <LeftDrag> <LeftDrag><Cmd><CR>
+ exe "normal \<F2>\<F3>"
+ call assert_equal('s', mode())
+ exe "normal! \<C-\>\<C-N>"
+
+ nunmap <LeftDrag>
+ nunmap <F2>
+ nunmap <F3>
+ delfunc ClickExpr
+ delfunc DragExpr
+ set selectmode&
+ set mouse&
+endfunc
+
+" Test for mapping <LeftDrag> in Insert mode
+func Test_mouse_drag_insert_map()
+ CheckFunction test_setmouse
+ set mouse=a
+ func ClickExpr()
+ call test_setmouse(1, 1)
+ return "\<LeftMouse>"
+ endfunc
+ func DragExpr()
+ call test_setmouse(1, 2)
+ return "\<LeftDrag>"
+ endfunc
+ inoremap <expr> <F2> ClickExpr()
+ imap <expr> <F3> DragExpr()
+
+ inoremap <LeftDrag> <LeftDrag><Cmd>let g:dragged = 1<CR>
+ exe "normal i\<F2>\<F3>"
+ call assert_equal(1, g:dragged)
+ call assert_equal('v', mode())
+ exe "normal! \<C-\>\<C-N>"
+ unlet g:dragged
+
+ inoremap <LeftDrag> <LeftDrag><C-\><C-N>
+ exe "normal i\<F2>\<F3>"
+ call assert_equal('n', mode())
+
+ iunmap <LeftDrag>
+ iunmap <F2>
+ iunmap <F3>
+ delfunc ClickExpr
+ delfunc DragExpr
+ set mouse&
+endfunc
+
+func Test_unmap_simplifiable()
+ map <C-I> foo
+ map <Tab> bar
+ call assert_equal('foo', maparg('<C-I>'))
+ call assert_equal('bar', maparg('<Tab>'))
+ unmap <C-I>
+ call assert_equal('', maparg('<C-I>'))
+ call assert_equal('bar', maparg('<Tab>'))
+ unmap <Tab>
+
+ map <C-I> foo
+ unmap <Tab>
+ " This should not error
+ unmap <C-I>
+endfunc
+
+func Test_expr_map_escape_special()
+ nnoremap … <Cmd>let g:got_ellipsis += 1<CR>
+ func Func()
+ return '…'
+ endfunc
+ nmap <expr> <F2> Func()
+ let g:got_ellipsis = 0
+ call feedkeys("\<F2>", 'xt')
+ call assert_equal(1, g:got_ellipsis)
+ delfunc Func
+ nunmap <F2>
+ unlet g:got_ellipsis
+ nunmap …
+endfunc
+
+" Testing for mapping after an <Nop> mapping is triggered on timeout.
+" Test for what patch 8.1.0052 fixes.
+func Test_map_after_timed_out_nop()
+ CheckRunVimInTerminal
+
+ let lines =<< trim END
+ set timeout timeoutlen=400
+ inoremap ab TEST
+ inoremap a <Nop>
+ END
+ call writefile(lines, 'Xtest_map_after_timed_out_nop')
+ let buf = RunVimInTerminal('-S Xtest_map_after_timed_out_nop', #{rows: 6})
+
+ " Enter Insert mode
+ call term_sendkeys(buf, 'i')
+ " Wait for the "a" mapping to timeout
+ call term_sendkeys(buf, 'a')
+ call term_wait(buf, 500)
+ " Send "a" and wait for a period shorter than 'timeoutlen'
+ call term_sendkeys(buf, 'a')
+ call term_wait(buf, 100)
+ " Send "b", should trigger the "ab" mapping
+ call term_sendkeys(buf, 'b')
+ call WaitForAssert({-> assert_equal("TEST", term_getline(buf, 1))})
+
+ " clean up
+ call StopVimInTerminal(buf)
+ call delete('Xtest_map_after_timed_out_nop')
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_marks.vim b/src/nvim/testdir/test_marks.vim
index b3035d73ce..74e63d9d69 100644
--- a/src/nvim/testdir/test_marks.vim
+++ b/src/nvim/testdir/test_marks.vim
@@ -1,6 +1,6 @@
" Test that a deleted mark is restored after delete-undo-redo-undo.
-function! Test_Restore_DelMark()
+func Test_Restore_DelMark()
enew!
call append(0, [" textline A", " textline B", " textline C"])
normal! 2gg
@@ -11,10 +11,10 @@ function! Test_Restore_DelMark()
call assert_equal(2, pos[1])
call assert_equal(1, pos[2])
enew!
-endfunction
+endfunc
" Test that CTRL-A and CTRL-X updates last changed mark '[, '].
-function! Test_Incr_Marks()
+func Test_Incr_Marks()
enew!
call append(0, ["123 123 123", "123 123 123", "123 123 123"])
normal! gg
@@ -23,7 +23,17 @@ function! Test_Incr_Marks()
call assert_equal("123 XXXXXXX", getline(2))
call assert_equal("XXX 123 123", getline(3))
enew!
-endfunction
+endfunc
+
+func Test_previous_jump_mark()
+ new
+ call setline(1, ['']->repeat(6))
+ normal Ggg
+ call assert_equal(6, getpos("''")[1])
+ normal jjjjj
+ call assert_equal(6, getpos("''")[1])
+ bwipe!
+endfunc
func Test_setpos()
new Xone
@@ -205,6 +215,57 @@ func Test_mark_error()
call assert_fails('mark', 'E471:')
call assert_fails('mark xx', 'E488:')
call assert_fails('mark _', 'E191:')
+ call assert_beeps('normal! m~')
+
+ call setpos("'k", [0, 100, 1, 0])
+ call assert_fails("normal 'k", 'E19:')
+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 :k command to set a mark
+func Test_marks_k_cmd()
+ new
+ call setline(1, ['foo', 'bar', 'baz', 'qux'])
+ 1,3kr
+ call assert_equal([0, 3, 1, 0], getpos("'r"))
+ close!
+endfunc
+
+" Test for file marks (A-Z)
+func Test_file_mark()
+ new Xone
+ call setline(1, ['aaa', 'bbb'])
+ norm! G$mB
+ w!
+ new Xtwo
+ call setline(1, ['ccc', 'ddd'])
+ norm! GmD
+ w!
+
+ enew
+ normal! `B
+ call assert_equal('Xone', bufname())
+ call assert_equal([2, 3], [line('.'), col('.')])
+ normal! 'D
+ call assert_equal('Xtwo', bufname())
+ call assert_equal([2, 1], [line('.'), col('.')])
+
+ call delete('Xone')
+ call delete('Xtwo')
endfunc
" Test for the getmarklist() function
@@ -231,3 +292,5 @@ func Test_getmarklist()
call assert_equal([], {}->getmarklist())
close!
endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_matchadd_conceal.vim b/src/nvim/testdir/test_matchadd_conceal.vim
index 2cbaf5cb76..1b5fc8313f 100644
--- a/src/nvim/testdir/test_matchadd_conceal.vim
+++ b/src/nvim/testdir/test_matchadd_conceal.vim
@@ -7,7 +7,7 @@ source shared.vim
source term_util.vim
source view_util.vim
-function! Test_simple_matchadd()
+func Test_simple_matchadd()
new
1put='# This is a Test'
@@ -333,7 +333,26 @@ func Test_matchadd_and_syn_conceal()
call assert_notequal(screenattr(1, 10) , screenattr(1, 11))
call assert_notequal(screenattr(1, 11) , screenattr(1, 12))
call assert_equal(screenattr(1, 11) , screenattr(1, 32))
-endfunction
+endfunc
+
+func Test_interaction_matchadd_syntax()
+ new
+ " Test for issue #7268 fix.
+ " When redrawing the second column, win_line() was comparing the sequence
+ " number of the syntax-concealed region with a bogus zero value that was
+ " returned for the matchadd-concealed region. Before 8.0.0672 the sequence
+ " number was never reset, thus masking the problem.
+ call setline(1, 'aaa|bbb|ccc')
+ call matchadd('Conceal', '^..', 10, -1, #{conceal: 'X'})
+ syn match foobar '^.'
+ setl concealcursor=n conceallevel=1
+ redraw!
+
+ call assert_equal('Xa|bbb|ccc', Screenline(1))
+ call assert_notequal(screenattr(1, 1), screenattr(1, 2))
+
+ bwipe!
+endfunc
func Test_cursor_column_in_concealed_line_after_window_scroll()
CheckRunVimInTerminal
diff --git a/src/nvim/testdir/test_matchadd_conceal_utf8.vim b/src/nvim/testdir/test_matchadd_conceal_utf8.vim
index 7bfac13ad8..f33c7f694c 100644
--- a/src/nvim/testdir/test_matchadd_conceal_utf8.vim
+++ b/src/nvim/testdir/test_matchadd_conceal_utf8.vim
@@ -1,21 +1,21 @@
" Test for matchadd() and conceal feature using utf-8.
-if !has('conceal')
- finish
-endif
-function! s:screenline(lnum) abort
+source check.vim
+CheckFeature conceal
+
+func s:screenline(lnum) abort
let line = []
for c in range(1, winwidth(0))
call add(line, nr2char(a:lnum->screenchar(c)))
endfor
return s:trim(join(line, ''))
-endfunction
+endfunc
-function! s:trim(str) abort
+func s:trim(str) abort
return matchstr(a:str,'^\s*\zs.\{-}\ze\s*$')
-endfunction
+endfunc
-function! Test_match_using_multibyte_conceal_char()
+func Test_match_using_multibyte_conceal_char()
new
setlocal concealcursor=n conceallevel=1
diff --git a/src/nvim/testdir/test_matchfuzzy.vim b/src/nvim/testdir/test_matchfuzzy.vim
new file mode 100644
index 0000000000..c836bc87aa
--- /dev/null
+++ b/src/nvim/testdir/test_matchfuzzy.vim
@@ -0,0 +1,271 @@
+" Tests for fuzzy matching
+
+source shared.vim
+source check.vim
+
+" Test for matchfuzzy()
+func Test_matchfuzzy()
+ call assert_fails('call matchfuzzy(10, "abc")', 'E686:')
+ " Needs v8.2.1183; match the final error that's thrown for now
+ " call assert_fails('call matchfuzzy(["abc"], [])', 'E730:')
+ call assert_fails('call matchfuzzy(["abc"], [])', 'E475:')
+ call assert_fails("let x = matchfuzzy(v:_null_list, 'foo')", 'E686:')
+ call assert_fails('call matchfuzzy(["abc"], v:_null_string)', 'E475:')
+ call assert_equal([], matchfuzzy([], 'abc'))
+ call assert_equal([], matchfuzzy(['abc'], ''))
+ call assert_equal(['abc'], matchfuzzy(['abc', 10], 'ac'))
+ call assert_equal([], matchfuzzy([10, 20], 'ac'))
+ call assert_equal(['abc'], matchfuzzy(['abc'], 'abc'))
+ call assert_equal(['crayon', 'camera'], matchfuzzy(['camera', 'crayon'], 'cra'))
+ call assert_equal(['aabbaa', 'aaabbbaaa', 'aaaabbbbaaaa', 'aba'], matchfuzzy(['aba', 'aabbaa', 'aaabbbaaa', 'aaaabbbbaaaa'], 'aa'))
+ call assert_equal(['one'], matchfuzzy(['one', 'two'], 'one'))
+ call assert_equal(['oneTwo', 'onetwo'], matchfuzzy(['onetwo', 'oneTwo'], 'oneTwo'))
+ call assert_equal(['onetwo', 'one_two'], matchfuzzy(['onetwo', 'one_two'], 'oneTwo'))
+ call assert_equal(['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], matchfuzzy(['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], 'aa'))
+ call assert_equal(256, matchfuzzy([repeat('a', 256)], repeat('a', 256))[0]->len())
+ call assert_equal([], matchfuzzy([repeat('a', 300)], repeat('a', 257)))
+ " matches with same score should not be reordered
+ let l = ['abc1', 'abc2', 'abc3']
+ call assert_equal(l, l->matchfuzzy('abc'))
+
+ " Tests for match preferences
+ " preference for camel case match
+ call assert_equal(['oneTwo', 'onetwo'], ['onetwo', 'oneTwo']->matchfuzzy('onetwo'))
+ " preference for match after a separator (_ or space)
+ call assert_equal(['onetwo', 'one_two', 'one two'], ['onetwo', 'one_two', 'one two']->matchfuzzy('onetwo'))
+ " preference for leading letter match
+ call assert_equal(['onetwo', 'xonetwo'], ['xonetwo', 'onetwo']->matchfuzzy('onetwo'))
+ " preference for sequential match
+ call assert_equal(['onetwo', 'oanbectdweo'], ['oanbectdweo', 'onetwo']->matchfuzzy('onetwo'))
+ " non-matching leading letter(s) penalty
+ call assert_equal(['xonetwo', 'xxonetwo'], ['xxonetwo', 'xonetwo']->matchfuzzy('onetwo'))
+ " total non-matching letter(s) penalty
+ call assert_equal(['one', 'onex', 'onexx'], ['onexx', 'one', 'onex']->matchfuzzy('one'))
+ " prefer complete matches over separator matches
+ call assert_equal(['.vim/vimrc', '.vim/vimrc_colors', '.vim/v_i_m_r_c'], ['.vim/vimrc', '.vim/vimrc_colors', '.vim/v_i_m_r_c']->matchfuzzy('vimrc'))
+ " gap penalty
+ call assert_equal(['xxayybxxxx', 'xxayyybxxx', 'xxayyyybxx'], ['xxayyyybxx', 'xxayyybxxx', 'xxayybxxxx']->matchfuzzy('ab'))
+ " path separator vs word separator
+ call assert_equal(['color/setup.vim', 'color\\setup.vim', 'color setup.vim', 'color_setup.vim', 'colorsetup.vim'], matchfuzzy(['colorsetup.vim', 'color setup.vim', 'color/setup.vim', 'color_setup.vim', 'color\\setup.vim'], 'setup.vim'))
+
+ " match multiple words (separated by space)
+ call assert_equal(['foo bar baz'], ['foo bar baz', 'foo', 'foo bar', 'baz bar']->matchfuzzy('baz foo'))
+ call assert_equal([], ['foo bar baz', 'foo', 'foo bar', 'baz bar']->matchfuzzy('one two'))
+ call assert_equal([], ['foo bar']->matchfuzzy(" \t "))
+
+ " test for matching a sequence of words
+ call assert_equal(['bar foo'], ['foo bar', 'bar foo', 'foobar', 'barfoo']->matchfuzzy('bar foo', {'matchseq' : 1}))
+ call assert_equal([#{text: 'two one'}], [#{text: 'one two'}, #{text: 'two one'}]->matchfuzzy('two one', #{key: 'text', matchseq: v:true}))
+
+ %bw!
+ eval ['somebuf', 'anotherone', 'needle', 'yetanotherone']->map({_, v -> bufadd(v) + bufload(v)})
+ let l = getbufinfo()->map({_, v -> fnamemodify(v.name, ':t')})->matchfuzzy('ndl')
+ call assert_equal(1, len(l))
+ call assert_match('needle', l[0])
+
+ " Test for fuzzy matching dicts
+ let l = [{'id' : 5, 'val' : 'crayon'}, {'id' : 6, 'val' : 'camera'}]
+ call assert_equal([{'id' : 6, 'val' : 'camera'}], matchfuzzy(l, 'cam', {'text_cb' : {v -> v.val}}))
+ call assert_equal([{'id' : 6, 'val' : 'camera'}], matchfuzzy(l, 'cam', {'key' : 'val'}))
+ call assert_equal([], matchfuzzy(l, 'day', {'text_cb' : {v -> v.val}}))
+ call assert_equal([], matchfuzzy(l, 'day', {'key' : 'val'}))
+ call assert_fails("let x = matchfuzzy(l, 'cam', 'random')", 'E715:')
+ call assert_equal([], matchfuzzy(l, 'day', {'text_cb' : {v -> []}}))
+ call assert_equal([], matchfuzzy(l, 'day', {'text_cb' : {v -> 1}}))
+ call assert_fails("let x = matchfuzzy(l, 'day', {'text_cb' : {a, b -> 1}})", 'E119:')
+ call assert_equal([], matchfuzzy(l, 'cam'))
+ " Nvim's callback implementation is different, so E6000 is expected instead,
+ " but we need v8.2.1183 to assert it
+ " call assert_fails("let x = matchfuzzy(l, 'cam', {'text_cb' : []})", 'E921:')
+ " call assert_fails("let x = matchfuzzy(l, 'cam', {'text_cb' : []})", 'E6000:')
+ call assert_fails("let x = matchfuzzy(l, 'cam', {'text_cb' : []})", 'E475:')
+ " call assert_fails("let x = matchfuzzy(l, 'foo', {'key' : []})", 'E730:')
+ call assert_fails("let x = matchfuzzy(l, 'foo', {'key' : []})", 'E475:')
+ call assert_fails("let x = matchfuzzy(l, 'cam', v:_null_dict)", 'E715:')
+ call assert_fails("let x = matchfuzzy(l, 'foo', {'key' : v:_null_string})", 'E475:')
+ " Nvim doesn't have null functions
+ " call assert_fails("let x = matchfuzzy(l, 'foo', {'text_cb' : test_null_function()})", 'E475:')
+ " matches with same score should not be reordered
+ let l = [#{text: 'abc', id: 1}, #{text: 'abc', id: 2}, #{text: 'abc', id: 3}]
+ call assert_equal(l, l->matchfuzzy('abc', #{key: 'text'}))
+
+ let l = [{'id' : 5, 'name' : 'foo'}, {'id' : 6, 'name' : []}, {'id' : 7}]
+ call assert_fails("let x = matchfuzzy(l, 'foo', {'key' : 'name'})", 'E730:')
+
+ " Test in latin1 encoding
+ let save_enc = &encoding
+ " Nvim supports utf-8 encoding only
+ " set encoding=latin1
+ call assert_equal(['abc'], matchfuzzy(['abc'], 'abc'))
+ let &encoding = save_enc
+endfunc
+
+" Test for the matchfuzzypos() function
+func Test_matchfuzzypos()
+ call assert_equal([['curl', 'world'], [[2,3], [2,3]], [128, 127]], matchfuzzypos(['world', 'curl'], 'rl'))
+ call assert_equal([['curl', 'world'], [[2,3], [2,3]], [128, 127]], matchfuzzypos(['world', 'one', 'curl'], 'rl'))
+ call assert_equal([['hello', 'hello world hello world'],
+ \ [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [275, 257]],
+ \ matchfuzzypos(['hello world hello world', 'hello', 'world'], 'hello'))
+ call assert_equal([['aaaaaaa'], [[0, 1, 2]], [191]], matchfuzzypos(['aaaaaaa'], 'aaa'))
+ call assert_equal([['a b'], [[0, 3]], [219]], matchfuzzypos(['a b'], 'a b'))
+ call assert_equal([['a b'], [[0, 3]], [219]], matchfuzzypos(['a b'], 'a b'))
+ call assert_equal([['a b'], [[0]], [112]], matchfuzzypos(['a b'], ' a '))
+ call assert_equal([[], [], []], matchfuzzypos(['a b'], ' '))
+ call assert_equal([[], [], []], matchfuzzypos(['world', 'curl'], 'ab'))
+ let x = matchfuzzypos([repeat('a', 256)], repeat('a', 256))
+ call assert_equal(range(256), x[1][0])
+ call assert_equal([[], [], []], matchfuzzypos([repeat('a', 300)], repeat('a', 257)))
+ call assert_equal([[], [], []], matchfuzzypos([], 'abc'))
+
+ " match in a long string
+ call assert_equal([[repeat('x', 300) .. 'abc'], [[300, 301, 302]], [-135]],
+ \ matchfuzzypos([repeat('x', 300) .. 'abc'], 'abc'))
+
+ " preference for camel case match
+ call assert_equal([['xabcxxaBc'], [[6, 7, 8]], [189]], matchfuzzypos(['xabcxxaBc'], 'abc'))
+ " preference for match after a separator (_ or space)
+ call assert_equal([['xabx_ab'], [[5, 6]], [145]], matchfuzzypos(['xabx_ab'], 'ab'))
+ " preference for leading letter match
+ call assert_equal([['abcxabc'], [[0, 1]], [150]], matchfuzzypos(['abcxabc'], 'ab'))
+ " preference for sequential match
+ call assert_equal([['aobncedone'], [[7, 8, 9]], [158]], matchfuzzypos(['aobncedone'], 'one'))
+ " best recursive match
+ call assert_equal([['xoone'], [[2, 3, 4]], [168]], matchfuzzypos(['xoone'], 'one'))
+
+ " match multiple words (separated by space)
+ call assert_equal([['foo bar baz'], [[8, 9, 10, 0, 1, 2]], [369]], ['foo bar baz', 'foo', 'foo bar', 'baz bar']->matchfuzzypos('baz foo'))
+ call assert_equal([[], [], []], ['foo bar baz', 'foo', 'foo bar', 'baz bar']->matchfuzzypos('baz foo', {'matchseq': 1}))
+ call assert_equal([['foo bar baz'], [[0, 1, 2, 8, 9, 10]], [369]], ['foo bar baz', 'foo', 'foo bar', 'baz bar']->matchfuzzypos('foo baz'))
+ call assert_equal([['foo bar baz'], [[0, 1, 2, 3, 4, 5, 10]], [326]], ['foo bar baz', 'foo', 'foo bar', 'baz bar']->matchfuzzypos('foo baz', {'matchseq': 1}))
+ call assert_equal([[], [], []], ['foo bar baz', 'foo', 'foo bar', 'baz bar']->matchfuzzypos('one two'))
+ call assert_equal([[], [], []], ['foo bar']->matchfuzzypos(" \t "))
+ call assert_equal([['grace'], [[1, 2, 3, 4, 2, 3, 4, 0, 1, 2, 3, 4]], [657]], ['grace']->matchfuzzypos('race ace grace'))
+
+ let l = [{'id' : 5, 'val' : 'crayon'}, {'id' : 6, 'val' : 'camera'}]
+ call assert_equal([[{'id' : 6, 'val' : 'camera'}], [[0, 1, 2]], [192]],
+ \ matchfuzzypos(l, 'cam', {'text_cb' : {v -> v.val}}))
+ call assert_equal([[{'id' : 6, 'val' : 'camera'}], [[0, 1, 2]], [192]],
+ \ matchfuzzypos(l, 'cam', {'key' : 'val'}))
+ call assert_equal([[], [], []], matchfuzzypos(l, 'day', {'text_cb' : {v -> v.val}}))
+ call assert_equal([[], [], []], matchfuzzypos(l, 'day', {'key' : 'val'}))
+ call assert_fails("let x = matchfuzzypos(l, 'cam', 'random')", 'E715:')
+ call assert_equal([[], [], []], matchfuzzypos(l, 'day', {'text_cb' : {v -> []}}))
+ call assert_equal([[], [], []], matchfuzzypos(l, 'day', {'text_cb' : {v -> 1}}))
+ call assert_fails("let x = matchfuzzypos(l, 'day', {'text_cb' : {a, b -> 1}})", 'E119:')
+ call assert_equal([[], [], []], matchfuzzypos(l, 'cam'))
+ " Nvim's callback implementation is different, so E6000 is expected instead,
+ " but we need v8.2.1183 to assert it
+ " call assert_fails("let x = matchfuzzypos(l, 'cam', {'text_cb' : []})", 'E921:')
+ " call assert_fails("let x = matchfuzzypos(l, 'cam', {'text_cb' : []})", 'E6000:')
+ call assert_fails("let x = matchfuzzypos(l, 'cam', {'text_cb' : []})", 'E475:')
+ " call assert_fails("let x = matchfuzzypos(l, 'foo', {'key' : []})", 'E730:')
+ call assert_fails("let x = matchfuzzypos(l, 'foo', {'key' : []})", 'E475:')
+ call assert_fails("let x = matchfuzzypos(l, 'cam', v:_null_dict)", 'E715:')
+ call assert_fails("let x = matchfuzzypos(l, 'foo', {'key' : v:_null_string})", 'E475:')
+ " Nvim doesn't have null functions
+ " call assert_fails("let x = matchfuzzypos(l, 'foo', {'text_cb' : test_null_function()})", 'E475:')
+
+ let l = [{'id' : 5, 'name' : 'foo'}, {'id' : 6, 'name' : []}, {'id' : 7}]
+ call assert_fails("let x = matchfuzzypos(l, 'foo', {'key' : 'name'})", 'E730:')
+endfunc
+
+" Test for matchfuzzy() with multibyte characters
+func Test_matchfuzzy_mbyte()
+ CheckFeature multi_lang
+ call assert_equal(['ンヹㄇヺヴ'], matchfuzzy(['ンヹㄇヺヴ'], 'ヹヺ'))
+ " reverse the order of characters
+ call assert_equal([], matchfuzzy(['ンヹㄇヺヴ'], 'ヺヹ'))
+ call assert_equal(['αβΩxxx', 'xαxβxΩx'],
+ \ matchfuzzy(['αβΩxxx', 'xαxβxΩx'], 'αβΩ'))
+ call assert_equal(['ππbbππ', 'πππbbbπππ', 'ππππbbbbππππ', 'πbπ'],
+ \ matchfuzzy(['πbπ', 'ππbbππ', 'πππbbbπππ', 'ππππbbbbππππ'], 'ππ'))
+
+ " match multiple words (separated by space)
+ call assert_equal(['세 마리의 작은 돼지'], ['세 마리의 작은 돼지', '마리의', '마리의 작은', '작은 돼지']->matchfuzzy('돼지 마리의'))
+ call assert_equal([], ['세 마리의 작은 돼지', '마리의', '마리의 작은', '작은 돼지']->matchfuzzy('파란 하늘'))
+
+ " preference for camel case match
+ call assert_equal(['oneĄwo', 'oneąwo'],
+ \ ['oneąwo', 'oneĄwo']->matchfuzzy('oneąwo'))
+ " preference for complete match then match after separator (_ or space)
+ call assert_equal(['ⅠⅡabㄟㄠ'] + sort(['ⅠⅡa_bㄟㄠ', 'ⅠⅡa bㄟㄠ']),
+ \ ['ⅠⅡabㄟㄠ', 'ⅠⅡa bㄟㄠ', 'ⅠⅡa_bㄟㄠ']->matchfuzzy('ⅠⅡabㄟㄠ'))
+ " preference for match after a separator (_ or space)
+ call assert_equal(['ㄓㄔabㄟㄠ', 'ㄓㄔa_bㄟㄠ', 'ㄓㄔa bㄟㄠ'],
+ \ ['ㄓㄔa_bㄟㄠ', 'ㄓㄔa bㄟㄠ', 'ㄓㄔabㄟㄠ']->matchfuzzy('ㄓㄔabㄟㄠ'))
+ " preference for leading letter match
+ call assert_equal(['ŗŝţũŵż', 'xŗŝţũŵż'],
+ \ ['xŗŝţũŵż', 'ŗŝţũŵż']->matchfuzzy('ŗŝţũŵż'))
+ " preference for sequential match
+ call assert_equal(['ㄞㄡㄤfffifl', 'ㄞaㄡbㄤcffdfiefl'],
+ \ ['ㄞaㄡbㄤcffdfiefl', 'ㄞㄡㄤfffifl']->matchfuzzy('ㄞㄡㄤfffifl'))
+ " non-matching leading letter(s) penalty
+ call assert_equal(['xㄞㄡㄤfffifl', 'xxㄞㄡㄤfffifl'],
+ \ ['xxㄞㄡㄤfffifl', 'xㄞㄡㄤfffifl']->matchfuzzy('ㄞㄡㄤfffifl'))
+ " total non-matching letter(s) penalty
+ call assert_equal(['ŗŝţ', 'ŗŝţx', 'ŗŝţxx'],
+ \ ['ŗŝţxx', 'ŗŝţ', 'ŗŝţx']->matchfuzzy('ŗŝţ'))
+endfunc
+
+" Test for matchfuzzypos() with multibyte characters
+func Test_matchfuzzypos_mbyte()
+ CheckFeature multi_lang
+ call assert_equal([['こんにちは世界'], [[0, 1, 2, 3, 4]], [273]],
+ \ matchfuzzypos(['こんにちは世界'], 'こんにちは'))
+ call assert_equal([['ンヹㄇヺヴ'], [[1, 3]], [88]], matchfuzzypos(['ンヹㄇヺヴ'], 'ヹヺ'))
+ " reverse the order of characters
+ call assert_equal([[], [], []], matchfuzzypos(['ンヹㄇヺヴ'], 'ヺヹ'))
+ call assert_equal([['αβΩxxx', 'xαxβxΩx'], [[0, 1, 2], [1, 3, 5]], [222, 113]],
+ \ matchfuzzypos(['αβΩxxx', 'xαxβxΩx'], 'αβΩ'))
+ call assert_equal([['ππbbππ', 'πππbbbπππ', 'ππππbbbbππππ', 'πbπ'],
+ \ [[0, 1], [0, 1], [0, 1], [0, 2]], [151, 148, 145, 110]],
+ \ matchfuzzypos(['πbπ', 'ππbbππ', 'πππbbbπππ', 'ππππbbbbππππ'], 'ππ'))
+ call assert_equal([['ααααααα'], [[0, 1, 2]], [191]],
+ \ matchfuzzypos(['ααααααα'], 'ααα'))
+
+ call assert_equal([[], [], []], matchfuzzypos(['ンヹㄇ', 'ŗŝţ'], 'fffifl'))
+ let x = matchfuzzypos([repeat('Ψ', 256)], repeat('Ψ', 256))
+ call assert_equal(range(256), x[1][0])
+ call assert_equal([[], [], []], matchfuzzypos([repeat('✓', 300)], repeat('✓', 257)))
+
+ " match multiple words (separated by space)
+ call assert_equal([['세 마리의 작은 돼지'], [[9, 10, 2, 3, 4]], [328]], ['세 마리의 작은 돼지', '마리의', '마리의 작은', '작은 돼지']->matchfuzzypos('돼지 마리의'))
+ call assert_equal([[], [], []], ['세 마리의 작은 돼지', '마리의', '마리의 작은', '작은 돼지']->matchfuzzypos('파란 하늘'))
+
+ " match in a long string
+ call assert_equal([[repeat('ぶ', 300) .. 'ẼẼẼ'], [[300, 301, 302]], [-135]],
+ \ matchfuzzypos([repeat('ぶ', 300) .. 'ẼẼẼ'], 'ẼẼẼ'))
+ " preference for camel case match
+ call assert_equal([['xѳѵҁxxѳѴҁ'], [[6, 7, 8]], [189]], matchfuzzypos(['xѳѵҁxxѳѴҁ'], 'ѳѵҁ'))
+ " preference for match after a separator (_ or space)
+ call assert_equal([['xちだx_ちだ'], [[5, 6]], [145]], matchfuzzypos(['xちだx_ちだ'], 'ちだ'))
+ " preference for leading letter match
+ call assert_equal([['ѳѵҁxѳѵҁ'], [[0, 1]], [150]], matchfuzzypos(['ѳѵҁxѳѵҁ'], 'ѳѵ'))
+ " preference for sequential match
+ call assert_equal([['aンbヹcㄇdンヹㄇ'], [[7, 8, 9]], [158]], matchfuzzypos(['aンbヹcㄇdンヹㄇ'], 'ンヹㄇ'))
+ " best recursive match
+ call assert_equal([['xффйд'], [[2, 3, 4]], [168]], matchfuzzypos(['xффйд'], 'фйд'))
+endfunc
+
+" Test for matchfuzzy() with limit
+func Test_matchfuzzy_limit()
+ let x = ['1', '2', '3', '2']
+ call assert_equal(['2', '2'], x->matchfuzzy('2'))
+ call assert_equal(['2', '2'], x->matchfuzzy('2', #{}))
+ call assert_equal(['2', '2'], x->matchfuzzy('2', #{limit: 0}))
+ call assert_equal(['2'], x->matchfuzzy('2', #{limit: 1}))
+ call assert_equal(['2', '2'], x->matchfuzzy('2', #{limit: 2}))
+ call assert_equal(['2', '2'], x->matchfuzzy('2', #{limit: 3}))
+ call assert_fails("call matchfuzzy(x, '2', #{limit: '2'})", 'E475:')
+
+ let l = [{'id': 5, 'val': 'crayon'}, {'id': 6, 'val': 'camera'}]
+ call assert_equal([{'id': 5, 'val': 'crayon'}, {'id': 6, 'val': 'camera'}], l->matchfuzzy('c', #{text_cb: {v -> v.val}}))
+ call assert_equal([{'id': 5, 'val': 'crayon'}, {'id': 6, 'val': 'camera'}], l->matchfuzzy('c', #{key: 'val'}))
+ call assert_equal([{'id': 5, 'val': 'crayon'}, {'id': 6, 'val': 'camera'}], l->matchfuzzy('c', #{text_cb: {v -> v.val}, limit: 0}))
+ call assert_equal([{'id': 5, 'val': 'crayon'}, {'id': 6, 'val': 'camera'}], l->matchfuzzy('c', #{key: 'val', limit: 0}))
+ call assert_equal([{'id': 5, 'val': 'crayon'}], l->matchfuzzy('c', #{text_cb: {v -> v.val}, limit: 1}))
+ call assert_equal([{'id': 5, 'val': 'crayon'}], l->matchfuzzy('c', #{key: 'val', limit: 1}))
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_menu.vim b/src/nvim/testdir/test_menu.vim
index de6d4aa359..4af75be514 100644
--- a/src/nvim/testdir/test_menu.vim
+++ b/src/nvim/testdir/test_menu.vim
@@ -1,8 +1,7 @@
" Test that the system menu can be loaded.
-if !has('menu')
- finish
-endif
+source check.vim
+CheckFeature menu
func Test_load_menu()
try
@@ -36,3 +35,92 @@ func Test_translate_menu()
source $VIMRUNTIME/delmenu.vim
endfunc
+
+func Test_menu_commands()
+ nmenu 2 Test.FooBar :let g:did_menu = 'normal'<CR>
+ vmenu 2 Test.FooBar :let g:did_menu = 'visual'<CR>
+ smenu 2 Test.FooBar :let g:did_menu = 'select'<CR>
+ omenu 2 Test.FooBar :let g:did_menu = 'op-pending'<CR>
+ tlmenu 2 Test.FooBar :let g:did_menu = 'terminal'<CR>
+ imenu 2 Test.FooBar :let g:did_menu = 'insert'<CR>
+ cmenu 2 Test.FooBar :let g:did_menu = 'cmdline'<CR>
+ emenu n Test.FooBar
+
+ call feedkeys(":menu Test.FooB\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"menu Test.FooBar', @:)
+
+ call assert_equal('normal', g:did_menu)
+ emenu v Test.FooBar
+ call assert_equal('visual', g:did_menu)
+ emenu s Test.FooBar
+ call assert_equal('select', g:did_menu)
+ emenu o Test.FooBar
+ call assert_equal('op-pending', g:did_menu)
+ emenu t Test.FooBar
+ call assert_equal('terminal', g:did_menu)
+ emenu i Test.FooBar
+ call assert_equal('insert', g:did_menu)
+ emenu c Test.FooBar
+ call assert_equal('cmdline', g:did_menu)
+
+ nunmenu Test.FooBar
+ call assert_fails('emenu n Test.FooBar', 'E335: Menu not defined for Normal mode')
+ vunmenu Test.FooBar
+ call assert_fails('emenu v Test.FooBar', 'E335: Menu not defined for Visual mode')
+ vmenu 2 Test.FooBar :let g:did_menu = 'visual'<CR>
+ sunmenu Test.FooBar
+ call assert_fails('emenu s Test.FooBar', 'E335: Menu not defined for Select mode')
+ ounmenu Test.FooBar
+ call assert_fails('emenu o Test.FooBar', 'E335: Menu not defined for Op-pending mode')
+ iunmenu Test.FooBar
+ call assert_fails('emenu i Test.FooBar', 'E335: Menu not defined for Insert mode')
+ cunmenu Test.FooBar
+ call assert_fails('emenu c Test.FooBar', 'E335: Menu not defined for Cmdline mode')
+ tlunmenu Test.FooBar
+ call assert_fails('emenu t Test.FooBar', 'E335: Menu not defined for Terminal mode')
+
+ aunmenu Test.FooBar
+ call assert_fails('emenu n Test.FooBar', 'E334:')
+
+ nmenu 2 Test.FooBar.Child :let g:did_menu = 'foobar'<CR>
+ call assert_fails('emenu n Test.FooBar', 'E333:')
+ nunmenu Test.FooBar.Child
+
+ unlet g:did_menu
+endfun
+
+" Test for menu item completion in command line
+func Test_menu_expand()
+ " Create the menu itmes for test
+ for i in range(1, 4)
+ let m = 'menu Xmenu.A' .. i .. '.A' .. i
+ for j in range(1, 4)
+ exe m .. 'B' .. j .. ' :echo "A' .. i .. 'B' .. j .. '"' .. "<CR>"
+ endfor
+ endfor
+ set wildmenu
+
+ " Test for <CR> selecting a submenu
+ call feedkeys(":emenu Xmenu.A\<Tab>\<CR>\<Right>x\<BS>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"emenu Xmenu.A1.A1B2', @:)
+
+ " Test for <Down> selecting a submenu
+ call feedkeys(":emenu Xmenu.A\<Tab>\<Right>\<Right>\<Down>" ..
+ \ "\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"emenu Xmenu.A3.A3B1 A3B2 A3B3 A3B4', @:)
+
+ " Test for <Up> to go up a submenu
+ call feedkeys(":emenu Xmenu.A\<Tab>\<Down>\<Up>\<Right>\<Right>" ..
+ \ "\<Left>\<Down>\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"emenu Xmenu.A2.A2B1 A2B2 A2B3 A2B4', @:)
+
+ " Test for <Up> to go up a menu
+ call feedkeys(":emenu Xmenu.A\<Tab>\<Down>\<Up>\<Up>\<Up>" ..
+ \ "\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"emenu Buffers. Xmenu.', @:)
+
+ set wildmenu&
+ unmenu Xmenu
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_messages.vim b/src/nvim/testdir/test_messages.vim
index e0286548d9..5670368936 100644
--- a/src/nvim/testdir/test_messages.vim
+++ b/src/nvim/testdir/test_messages.vim
@@ -2,6 +2,9 @@
source check.vim
source shared.vim
+source term_util.vim
+source view_util.vim
+source screendump.vim
func Test_messages()
let oldmore = &more
@@ -40,7 +43,7 @@ endfunc
" indicator (e.g., "-- INSERT --") when ":stopinsert" is invoked. Message
" output could then be disturbed when 'cmdheight' was greater than one.
" This test ensures that the bugfix for this issue remains in place.
-function! Test_stopinsert_does_not_break_message_output()
+func Test_stopinsert_does_not_break_message_output()
set cmdheight=2
redraw!
@@ -55,7 +58,7 @@ function! Test_stopinsert_does_not_break_message_output()
redraw!
set cmdheight&
-endfunction
+endfunc
func Test_message_completion()
call feedkeys(":message \<C-A>\<C-B>\"\<CR>", 'tx')
@@ -109,6 +112,160 @@ func Test_echospace()
set ruler& showcmd&
endfunc
+" Test more-prompt (see :help more-prompt).
+func Test_message_more()
+ CheckRunVimInTerminal
+ let buf = RunVimInTerminal('', {'rows': 6})
+ call term_sendkeys(buf, ":call setline(1, range(1, 100))\n")
+
+ call term_sendkeys(buf, ":%p#\n")
+ call WaitForAssert({-> assert_equal(' 5 5', term_getline(buf, 5))})
+ call WaitForAssert({-> assert_equal('-- More --', term_getline(buf, 6))})
+
+ call term_sendkeys(buf, '?')
+ call WaitForAssert({-> assert_equal(' 5 5', term_getline(buf, 5))})
+ call WaitForAssert({-> assert_equal('-- More -- SPACE/d/j: screen/page/line down, b/u/k: up, q: quit ', term_getline(buf, 6))})
+
+ " Down a line with j, <CR>, <NL> or <Down>.
+ call term_sendkeys(buf, "j")
+ call WaitForAssert({-> assert_equal(' 6 6', term_getline(buf, 5))})
+ call WaitForAssert({-> assert_equal('-- More --', term_getline(buf, 6))})
+ call term_sendkeys(buf, "\<NL>")
+ call WaitForAssert({-> assert_equal(' 7 7', term_getline(buf, 5))})
+ call term_sendkeys(buf, "\<CR>")
+ call WaitForAssert({-> assert_equal(' 8 8', term_getline(buf, 5))})
+ call term_sendkeys(buf, "\<Down>")
+ call WaitForAssert({-> assert_equal(' 9 9', term_getline(buf, 5))})
+
+ " Down a screen with <Space>, f, or <PageDown>.
+ call term_sendkeys(buf, 'f')
+ call WaitForAssert({-> assert_equal(' 14 14', term_getline(buf, 5))})
+ call WaitForAssert({-> assert_equal('-- More --', term_getline(buf, 6))})
+ call term_sendkeys(buf, ' ')
+ call WaitForAssert({-> assert_equal(' 19 19', term_getline(buf, 5))})
+ call term_sendkeys(buf, "\<PageDown>")
+ call WaitForAssert({-> assert_equal(' 24 24', term_getline(buf, 5))})
+
+ " Down a page (half a screen) with d.
+ call term_sendkeys(buf, 'd')
+ call WaitForAssert({-> assert_equal(' 27 27', term_getline(buf, 5))})
+
+ " Down all the way with 'G'.
+ call term_sendkeys(buf, 'G')
+ call WaitForAssert({-> assert_equal('100 100', term_getline(buf, 5))})
+ call WaitForAssert({-> assert_equal('Press ENTER or type command to continue', term_getline(buf, 6))})
+
+ " Up a line k, <BS> or <Up>.
+ call term_sendkeys(buf, 'k')
+ call WaitForAssert({-> assert_equal(' 99 99', term_getline(buf, 5))})
+ call term_sendkeys(buf, "\<BS>")
+ call WaitForAssert({-> assert_equal(' 98 98', term_getline(buf, 5))})
+ call term_sendkeys(buf, "\<Up>")
+ call WaitForAssert({-> assert_equal(' 97 97', term_getline(buf, 5))})
+
+ " Up a screen with b or <PageUp>.
+ call term_sendkeys(buf, 'b')
+ call WaitForAssert({-> assert_equal(' 92 92', term_getline(buf, 5))})
+ call term_sendkeys(buf, "\<PageUp>")
+ call WaitForAssert({-> assert_equal(' 87 87', term_getline(buf, 5))})
+
+ " Up a page (half a screen) with u.
+ call term_sendkeys(buf, 'u')
+ call WaitForAssert({-> assert_equal(' 84 84', term_getline(buf, 5))})
+
+ " Up all the way with 'g'.
+ call term_sendkeys(buf, 'g')
+ call WaitForAssert({-> assert_equal(' 5 5', term_getline(buf, 5))})
+ call WaitForAssert({-> assert_equal('-- More --', term_getline(buf, 6))})
+
+ " All the way down. Pressing f should do nothing but pressing
+ " space should end the more prompt.
+ call term_sendkeys(buf, 'G')
+ call WaitForAssert({-> assert_equal('100 100', term_getline(buf, 5))})
+ call WaitForAssert({-> assert_equal('Press ENTER or type command to continue', term_getline(buf, 6))})
+ call term_sendkeys(buf, 'f')
+ call WaitForAssert({-> assert_equal('100 100', term_getline(buf, 5))})
+ call term_sendkeys(buf, ' ')
+ call WaitForAssert({-> assert_equal('100', term_getline(buf, 5))})
+
+ " Pressing g< shows the previous command output.
+ call term_sendkeys(buf, 'g<')
+ call WaitForAssert({-> assert_equal('100 100', term_getline(buf, 5))})
+ call WaitForAssert({-> assert_equal('Press ENTER or type command to continue', term_getline(buf, 6))})
+
+ call term_sendkeys(buf, ":%p#\n")
+ call WaitForAssert({-> assert_equal(' 5 5', term_getline(buf, 5))})
+ call WaitForAssert({-> assert_equal('-- More --', term_getline(buf, 6))})
+
+ " Stop command output with q, <Esc> or CTRL-C.
+ call term_sendkeys(buf, 'q')
+ call WaitForAssert({-> assert_equal('100', term_getline(buf, 5))})
+
+ " Execute a : command from the more prompt
+ call term_sendkeys(buf, ":%p#\n")
+ call term_wait(buf)
+ call WaitForAssert({-> assert_equal('-- More --', term_getline(buf, 6))})
+ call term_sendkeys(buf, ":")
+ call term_wait(buf)
+ call WaitForAssert({-> assert_equal(':', term_getline(buf, 6))})
+ call term_sendkeys(buf, "echo 'Hello'\n")
+ call term_wait(buf)
+ call WaitForAssert({-> assert_equal('Hello ', term_getline(buf, 5))})
+
+ call StopVimInTerminal(buf)
+endfunc
+
+func Test_ask_yesno()
+ CheckRunVimInTerminal
+ let buf = RunVimInTerminal('', {'rows': 6})
+ call term_sendkeys(buf, ":call setline(1, range(1, 2))\n")
+
+ call term_sendkeys(buf, ":2,1s/^/n/\n")
+ call WaitForAssert({-> assert_equal('Backwards range given, OK to swap (y/n)?', term_getline(buf, 6))})
+ call term_sendkeys(buf, "n")
+ call WaitForAssert({-> assert_match('^Backwards range given, OK to swap (y/n)?n *1,1 *All$', term_getline(buf, 6))})
+ call WaitForAssert({-> assert_equal('1', term_getline(buf, 1))})
+
+ call term_sendkeys(buf, ":2,1s/^/Esc/\n")
+ call WaitForAssert({-> assert_equal('Backwards range given, OK to swap (y/n)?', term_getline(buf, 6))})
+ call term_sendkeys(buf, "\<Esc>")
+ call WaitForAssert({-> assert_match('^Backwards range given, OK to swap (y/n)?n *1,1 *All$', term_getline(buf, 6))})
+ call WaitForAssert({-> assert_equal('1', term_getline(buf, 1))})
+
+ call term_sendkeys(buf, ":2,1s/^/y/\n")
+ call WaitForAssert({-> assert_equal('Backwards range given, OK to swap (y/n)?', term_getline(buf, 6))})
+ call term_sendkeys(buf, "y")
+ call WaitForAssert({-> assert_match('^Backwards range given, OK to swap (y/n)?y *2,1 *All$', term_getline(buf, 6))})
+ call WaitForAssert({-> assert_equal('y1', term_getline(buf, 1))})
+ call WaitForAssert({-> assert_equal('y2', term_getline(buf, 2))})
+
+ call StopVimInTerminal(buf)
+endfunc
+
+func Test_mapping_at_hit_return_prompt()
+ nnoremap <C-B> :echo "hit ctrl-b"<CR>
+ call feedkeys(":ls\<CR>", "xt")
+ call feedkeys("\<*C-B>", "xt")
+ call assert_match('hit ctrl-b', Screenline(&lines - 1))
+ nunmap <C-B>
+endfunc
+
+func Test_quit_long_message()
+ CheckScreendump
+
+ let content =<< trim END
+ echom range(9999)->join("\x01")
+ END
+ call writefile(content, 'Xtest_quit_message')
+ let buf = RunVimInTerminal('-S Xtest_quit_message', #{rows: 6})
+ call term_sendkeys(buf, "q")
+ call VerifyScreenDump(buf, 'Test_quit_long_message', {})
+
+ " clean up
+ call StopVimInTerminal(buf)
+ call delete('Xtest_quit_message')
+endfunc
+
" this was missing a terminating NUL
func Test_echo_string_partial()
function CountSpaces()
@@ -116,3 +273,37 @@ func Test_echo_string_partial()
call assert_equal("function('CountSpaces', [{'ccccccccccc': ['ab', 'cd'], 'aaaaaaaaaaa': v:false, 'bbbbbbbbbbbb': ''}])", string(function('CountSpaces', [#{aaaaaaaaaaa: v:false, bbbbbbbbbbbb: '', ccccccccccc: ['ab', 'cd']}])))
endfunc
+" Message output was previously overwritten by the fileinfo display, shown
+" when switching buffers. If a buffer is switched to, then a message if
+" echoed, we should show the message, rather than overwriting it with
+" fileinfo.
+func Test_fileinfo_after_echo()
+ CheckScreendump
+
+ let content =<< trim END
+ file a.txt
+
+ hide edit b.txt
+ call setline(1, "hi")
+ setlocal modified
+
+ hide buffer a.txt
+
+ autocmd CursorHold * buf b.txt | w | echo "'b' written"
+ END
+
+ call writefile(content, 'Xtest_fileinfo_after_echo')
+ let buf = RunVimInTerminal('-S Xtest_fileinfo_after_echo', #{rows: 6})
+ call term_sendkeys(buf, ":set updatetime=50\<CR>")
+ call term_sendkeys(buf, "0$")
+ call VerifyScreenDump(buf, 'Test_fileinfo_after_echo', {})
+
+ call term_sendkeys(buf, ":q\<CR>")
+
+ " clean up
+ call StopVimInTerminal(buf)
+ call delete('Xtest_fileinfo_after_echo')
+ call delete('b.txt')
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim
index 057895047d..8ec408e62e 100644
--- a/src/nvim/testdir/test_mksession.vim
+++ b/src/nvim/testdir/test_mksession.vim
@@ -2,9 +2,8 @@
scriptencoding latin1
-if !has('mksession')
- finish
-endif
+source check.vim
+CheckFeature mksession
source shared.vim
source term_util.vim
@@ -43,9 +42,9 @@ func Test_mksession()
\ ' four leadinG spaces',
\ 'two consecutive tabs',
\ 'two tabs in one line',
- \ 'one ä multibyteCharacter',
- \ 'aä Ä two multiByte characters',
- \ 'Aäöü three mulTibyte characters',
+ \ 'one multibyteCharacter',
+ \ 'a two multiByte characters',
+ \ 'A three mulTibyte characters',
\ 'short line',
\ ])
let tmpfile = 'Xtemp'
@@ -219,6 +218,7 @@ func Test_mksession_one_buffer_two_windows()
let count1 = 0
let count2 = 0
let count2buf = 0
+ let bufexists = 0
for line in lines
if line =~ 'edit \f*Xtest1$'
let count1 += 1
@@ -229,10 +229,14 @@ func Test_mksession_one_buffer_two_windows()
if line =~ 'buffer \f\{-}Xtest2'
let count2buf += 1
endif
+ if line =~ 'bufexists(fnamemodify(.*, ":p")'
+ let bufexists += 1
+ endif
endfor
call assert_equal(1, count1, 'Xtest1 count')
call assert_equal(2, count2, 'Xtest2 count')
call assert_equal(2, count2buf, 'Xtest2 buffer count')
+ call assert_equal(2, bufexists)
close
bwipe!
@@ -309,6 +313,31 @@ func Test_mksession_buffer_count()
set nohidden
endfunc
+func Test_mksession_buffer_order()
+ %bwipe!
+ e Xfoo | e Xbar | e Xbaz | e Xqux
+ bufdo write
+ mksession! Xtest_mks.out
+
+ " Verify that loading the session preserves order of buffers
+ %bwipe!
+ source Xtest_mks.out
+
+ let s:buf_info = getbufinfo()
+ call assert_true(s:buf_info[0]['name'] =~# 'Xfoo$')
+ call assert_true(s:buf_info[1]['name'] =~# 'Xbar$')
+ call assert_true(s:buf_info[2]['name'] =~# 'Xbaz$')
+ call assert_true(s:buf_info[3]['name'] =~# 'Xqux$')
+
+ " Clean up.
+ call delete('Xfoo')
+ call delete('Xbar')
+ call delete('Xbaz')
+ call delete('Xqux')
+ call delete('Xtest_mks.out')
+ %bwipe!
+endfunc
+
if has('extra_search')
func Test_mksession_hlsearch()
@@ -333,21 +362,29 @@ func Test_mkview_open_folds()
call append(0, ['a', 'b', 'c'])
1,3fold
+ write! Xtestfile
+
+ call assert_notequal(-1, foldclosed(1))
+ call assert_notequal(-1, foldclosed(2))
+ call assert_notequal(-1, foldclosed(3))
+
+ " Save the view with folds closed
+ mkview! Xtestview
+
" zR affects 'foldlevel', make sure the option is applied after the folds
" have been recreated.
+ " Open folds to ensure they get closed when restoring the view
normal zR
- write! Xtestfile
call assert_equal(-1, foldclosed(1))
call assert_equal(-1, foldclosed(2))
call assert_equal(-1, foldclosed(3))
- mkview! Xtestview
source Xtestview
- call assert_equal(-1, foldclosed(1))
- call assert_equal(-1, foldclosed(2))
- call assert_equal(-1, foldclosed(3))
+ call assert_notequal(-1, foldclosed(1))
+ call assert_notequal(-1, foldclosed(2))
+ call assert_notequal(-1, foldclosed(3))
call delete('Xtestview')
call delete('Xtestfile')
@@ -696,6 +733,36 @@ func Test_mksession_foldopt()
set sessionoptions&
endfunc
+" Test for mksession with "help" but not "options" in 'sessionoptions'
+func Test_mksession_help_noopt()
+ set sessionoptions-=options
+ set sessionoptions+=help
+ help
+ let fname = expand('%')
+ mksession! Xtest_mks.out
+ bwipe
+
+ source Xtest_mks.out
+ call assert_equal('help', &buftype)
+ call assert_equal('help', &filetype)
+ call assert_equal(fname, expand('%'))
+ call assert_false(&modifiable)
+ call assert_true(&readonly)
+
+ helpclose
+ help index
+ let fname = expand('%')
+ mksession! Xtest_mks.out
+ bwipe
+
+ source Xtest_mks.out
+ call assert_equal('help', &buftype)
+ call assert_equal(fname, expand('%'))
+
+ call delete('Xtest_mks.out')
+ set sessionoptions&
+endfunc
+
" Test for mksession with window position
func Test_mksession_winpos()
if !has('gui_running')
@@ -735,6 +802,73 @@ func Test_mksession_winminheight()
set sessionoptions&
endfunc
+" Test for mksession with and without options restores shortmess
+func Test_mksession_shortmess()
+ " Without options
+ set sessionoptions-=options
+ split
+ mksession! Xtest_mks.out
+ let found_save = 0
+ let found_restore = 0
+ let lines = readfile('Xtest_mks.out')
+ for line in lines
+ let line = trim(line)
+
+ if line ==# 'let s:shortmess_save = &shortmess'
+ let found_save += 1
+ endif
+
+ if found_save !=# 0 && line ==# 'let &shortmess = s:shortmess_save'
+ let found_restore += 1
+ endif
+ endfor
+ call assert_equal(1, found_save)
+ call assert_equal(1, found_restore)
+ call delete('Xtest_mks.out')
+ close
+ set sessionoptions&
+
+ " With options
+ set sessionoptions+=options
+ split
+ mksession! Xtest_mks.out
+ let found_restore = 0
+ let lines = readfile('Xtest_mks.out')
+ for line in lines
+ if line =~# 's:shortmess_save'
+ let found_restore += 1
+ endif
+ endfor
+ call assert_equal(0, found_restore)
+ call delete('Xtest_mks.out')
+ close
+ set sessionoptions&
+endfunc
+
+" Test that when Vim loading session has 'A' in 'shortmess' it does not
+" complain about an existing swapfile.
+func Test_mksession_shortmess_with_A()
+ edit Xtestfile
+ write
+ let fname = swapname('%')
+ " readblob() needs patch 8.2.2343
+ " let cont = readblob(fname)
+ let cont = readfile(fname, 'B')
+ set sessionoptions-=options
+ mksession Xtestsession
+ bwipe!
+
+ " Recreate the swap file to pretend the file is being edited
+ call writefile(cont, fname)
+ set shortmess+=A
+ source Xtestsession
+
+ set shortmess&
+ set sessionoptions&
+ call delete('Xtestsession')
+ call delete(fname)
+endfunc
+
" Test for mksession with 'compatible' option
func Test_mksession_compatible()
throw 'skipped: Nvim does not support "compatible" option'
diff --git a/src/nvim/testdir/test_mksession_utf8.vim b/src/nvim/testdir/test_mksession_utf8.vim
index 722fd28beb..4e593cc21a 100644
--- a/src/nvim/testdir/test_mksession_utf8.vim
+++ b/src/nvim/testdir/test_mksession_utf8.vim
@@ -3,9 +3,8 @@
set encoding=utf-8
scriptencoding utf-8
-if !has('mksession')
- finish
-endif
+source check.vim
+CheckFeature mksession
func Test_mksession_utf8()
tabnew
diff --git a/src/nvim/testdir/test_move.vim b/src/nvim/testdir/test_move.vim
index f666a904b0..8c40369dbd 100644
--- a/src/nvim/testdir/test_move.vim
+++ b/src/nvim/testdir/test_move.vim
@@ -38,6 +38,7 @@ func Test_move()
call assert_fails("move -100", 'E16:')
call assert_fails("move +100", 'E16:')
call assert_fails('move', 'E16:')
+ call assert_fails("move 'r", 'E20:')
%bwipeout!
endfunc
diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim
index aff22f5d01..9fbd1f774a 100644
--- a/src/nvim/testdir/test_normal.vim
+++ b/src/nvim/testdir/test_normal.vim
@@ -1,6 +1,8 @@
" Test for various Normal mode commands
source shared.vim
+source check.vim
+source view_util.vim
func Setup_NewWindow()
10new
@@ -53,7 +55,7 @@ func OpfuncDummy(type, ...)
let g:bufnr=bufnr('%')
endfunc
-fun! Test_normal00_optrans()
+func Test_normal00_optrans()
new
call append(0, ['1 This is a simple test: abcd', '2 This is the second line', '3 this is the third line'])
1
@@ -95,6 +97,12 @@ func Test_normal01_keymodel()
50
call feedkeys("\<S-Up>y", 'tx')
call assert_equal(['49', '5'], getreg(0, 0, 1))
+ " Use the different Shift special keys
+ 50
+ call feedkeys("\<S-Right>\<S-Left>\<S-Up>\<S-Down>\<S-Home>\<S-End>y", 'tx')
+ call assert_equal(['50'], getline("'<", "'>"))
+ call assert_equal(['50', ''], getreg(0, 0, 1))
+
" Do not start visual mode when keymodel=
set keymodel=
50
@@ -115,8 +123,8 @@ func Test_normal01_keymodel()
bw!
endfunc
+" Test for select mode
func Test_normal02_selectmode()
- " some basic select mode tests
call Setup_NewWindow()
50
norm! gHy
@@ -133,7 +141,8 @@ func Test_normal02_selectmode2()
" some basic select mode tests
call Setup_NewWindow()
50
- call feedkeys(":set im\n\<c-o>gHc\<c-o>:set noim\n", 'tx')
+ " call feedkeys(":set im\n\<c-o>gHc\<c-o>:set noim\n", 'tx')
+ call feedkeys("i\<c-o>gHc\<esc>", 'tx')
call assert_equal('c51', getline('.'))
" clean up
bw!
@@ -344,7 +353,7 @@ func Test_normal08_fold()
bw!
endfunc
-func Test_normal09_operatorfunc()
+func Test_normal09a_operatorfunc()
" Test operatorfunc
call Setup_NewWindow()
" Add some spaces for counting
@@ -374,7 +383,7 @@ func Test_normal09_operatorfunc()
bw!
endfunc
-func Test_normal09a_operatorfunc()
+func Test_normal09b_operatorfunc()
" Test operatorfunc
call Setup_NewWindow()
" Add some spaces for counting
@@ -396,10 +405,45 @@ func Test_normal09a_operatorfunc()
" clean up
unmap <buffer> ,,
set opfunc=
+ call assert_fails('normal Vg@', 'E774:')
bw!
unlet! g:opt
endfunc
+func OperatorfuncRedo(_)
+ let g:opfunc_count = v:count
+endfunc
+
+func Underscorize(_)
+ normal! '[V']r_
+endfunc
+
+func Test_normal09c_operatorfunc()
+ " Test redoing operatorfunc
+ new
+ call setline(1, 'some text')
+ set operatorfunc=OperatorfuncRedo
+ normal v3g@
+ call assert_equal(3, g:opfunc_count)
+ let g:opfunc_count = 0
+ normal .
+ call assert_equal(3, g:opfunc_count)
+
+ bw!
+ unlet g:opfunc_count
+
+ " Test redoing Visual mode
+ set operatorfunc=Underscorize
+ new
+ call setline(1, ['first', 'first', 'third', 'third', 'second'])
+ normal! 1GVjg@
+ normal! 5G.
+ normal! 3G.
+ call assert_equal(['_____', '_____', '_____', '_____', '______'], getline(1, '$'))
+ bwipe!
+ set operatorfunc=
+endfunc
+
func Test_normal10_expand()
" Test for expand()
10new
@@ -450,13 +494,33 @@ func Test_normal11_showcmd()
bw!
endfunc
+" Test for nv_error and normal command errors
func Test_normal12_nv_error()
- " Test for nv_error
10new
call setline(1, range(1,5))
" should not do anything, just beep
- exe "norm! <c-k>"
+ call assert_beeps('exe "norm! <c-k>"')
call assert_equal(map(range(1,5), 'string(v:val)'), getline(1,'$'))
+ call assert_beeps('normal! G2dd')
+ call assert_beeps("normal! g\<C-A>")
+ call assert_beeps("normal! g\<C-X>")
+ call assert_beeps("normal! g\<C-B>")
+ " call assert_beeps("normal! vQ\<Esc>")
+ call assert_beeps("normal! 2[[")
+ call assert_beeps("normal! 2]]")
+ call assert_beeps("normal! 2[]")
+ call assert_beeps("normal! 2][")
+ call assert_beeps("normal! 4[z")
+ call assert_beeps("normal! 4]z")
+ call assert_beeps("normal! 4[c")
+ call assert_beeps("normal! 4]c")
+ call assert_beeps("normal! 200%")
+ call assert_beeps("normal! %")
+ call assert_beeps("normal! 2{")
+ call assert_beeps("normal! 2}")
+ call assert_beeps("normal! r\<Right>")
+ call assert_beeps("normal! 8ry")
+ call assert_beeps('normal! "@')
bw!
endfunc
@@ -618,6 +682,13 @@ func Test_normal16_z_scroll_hor()
$put =lineB
1d
+ " Test for zl and zh with a count
+ norm! 0z10l
+ call assert_equal([11, 1], [col('.'), wincol()])
+ norm! z4h
+ call assert_equal([11, 5], [col('.'), wincol()])
+ normal! 2gg
+
" Test for zl
1
norm! 5zl
@@ -740,15 +811,134 @@ func Test_normal17_z_scroll_hor2()
bw!
endfunc
+" Test for commands that scroll the window horizontally. Test with folds.
+" H, M, L, CTRL-E, CTRL-Y, CTRL-U, CTRL-D, PageUp, PageDown commands
+func Test_vert_scroll_cmds()
+ 15new
+ call setline(1, range(1, 100))
+ exe "normal! 30ggz\<CR>"
+ set foldenable
+ 33,36fold
+ 40,43fold
+ 46,49fold
+ let h = winheight(0)
+
+ " Test for H, M and L commands
+ " Top of the screen = 30
+ " Folded lines = 9
+ " Bottom of the screen = 30 + h + 9 - 1
+ normal! 4L
+ call assert_equal(35 + h, line('.'))
+ normal! 4H
+ call assert_equal(33, line('.'))
+
+ " Test for the CTRL-E and CTRL-Y commands with folds
+ %d
+ call setline(1, range(1, 10))
+ 3,5fold
+ exe "normal 6G3\<C-E>"
+ call assert_equal(6, line('w0'))
+ exe "normal 2\<C-Y>"
+ call assert_equal(2, line('w0'))
+
+ " Test for CTRL-Y on a folded line
+ %d
+ call setline(1, range(1, 100))
+ exe (h + 2) .. "," .. (h + 4) .. "fold"
+ exe h + 5
+ normal z-
+ exe "normal \<C-Y>\<C-Y>"
+ call assert_equal(h + 1, line('w$'))
+
+ " Using <PageUp> and <PageDown> in an empty buffer should beep
+ %d
+ call assert_beeps('exe "normal \<PageUp>"')
+ call assert_beeps('exe "normal \<C-B>"')
+ call assert_beeps('exe "normal \<PageDown>"')
+ call assert_beeps('exe "normal \<C-F>"')
+
+ " Test for <C-U> and <C-D> with fold
+ %d
+ call setline(1, range(1, 100))
+ 10,35fold
+ set scroll=10
+ exe "normal \<C-D>"
+ call assert_equal(36, line('.'))
+ exe "normal \<C-D>"
+ call assert_equal(46, line('.'))
+ exe "normal \<C-U>"
+ call assert_equal(36, line('.'))
+ exe "normal \<C-U>"
+ call assert_equal(10, line('.'))
+ exe "normal \<C-U>"
+ call assert_equal(1, line('.'))
+ set scroll&
+
+ " Test for scrolling to the top of the file with <C-U> and a fold
+ 10
+ normal ztL
+ exe "normal \<C-U>\<C-U>"
+ call assert_equal(1, line('w0'))
+
+ " Test for CTRL-D on a folded line
+ %d
+ call setline(1, range(1, 100))
+ 50,100fold
+ 75
+ normal z-
+ exe "normal \<C-D>"
+ call assert_equal(50, line('.'))
+ call assert_equal(100, line('w$'))
+ normal z.
+ let lnum = winline()
+ exe "normal \<C-D>"
+ call assert_equal(lnum, winline())
+ call assert_equal(50, line('.'))
+ normal zt
+ exe "normal \<C-D>"
+ call assert_equal(50, line('w0'))
+
+ set foldenable&
+ close!
+endfunc
+
+" Test for the 'sidescroll' option
+func Test_sidescroll_opt()
+ new
+ 20vnew
+
+ " scroll by 2 characters horizontally
+ set sidescroll=2 nowrap
+ call setline(1, repeat('a', 40))
+ normal g$l
+ call assert_equal(19, screenpos(0, 1, 21).col)
+ normal l
+ call assert_equal(20, screenpos(0, 1, 22).col)
+ normal g0h
+ call assert_equal(2, screenpos(0, 1, 2).col)
+ call assert_equal(20, screenpos(0, 1, 20).col)
+
+ " when 'sidescroll' is 0, cursor positioned at the center
+ set sidescroll=0
+ normal g$l
+ call assert_equal(11, screenpos(0, 1, 21).col)
+ normal g0h
+ call assert_equal(10, screenpos(0, 1, 10).col)
+
+ %bw!
+ set wrap& sidescroll&
+endfunc
+
+" basic tests for foldopen/folddelete
func Test_normal18_z_fold()
- " basic tests for foldopen/folddelete
- if !has("folding")
- return
- endif
+ CheckFeature folding
call Setup_NewWindow()
50
setl foldenable fdm=marker foldlevel=5
+ call assert_beeps('normal! zj')
+ call assert_beeps('normal! zk')
+
" Test for zF
" First fold
norm! 4zF
@@ -1118,7 +1308,7 @@ func Test_normal20_exmode()
endif
call writefile(['1a', 'foo', 'bar', '.', 'w! Xfile2', 'q!'], 'Xscript')
call writefile(['1', '2'], 'Xfile')
- call system(v:progpath .' -e -s < Xscript Xfile')
+ call system(GetVimCommand() .. ' -e -s < Xscript Xfile')
let a=readfile('Xfile2')
call assert_equal(['1', 'foo', 'bar', '2'], a)
@@ -1171,16 +1361,19 @@ func Test_normal22_zet()
endfor
call writefile(['1', '2'], 'Xfile_Test_normal22_zet')
- let args = ' --headless -u NONE -N -U NONE -i NONE --noplugins'
- call system(v:progpath . args . ' -c "%d" -c ":norm! ZZ" Xfile_Test_normal22_zet')
+ let args = ' -N -i NONE --noplugins -X --headless'
+ call system(GetVimCommand() .. args .. ' -c "%d" -c ":norm! ZZ" Xfile_Test_normal22_zet')
let a = readfile('Xfile_Test_normal22_zet')
call assert_equal([], a)
" Test for ZQ
call writefile(['1', '2'], 'Xfile_Test_normal22_zet')
- call system(v:progpath . args . ' -c "%d" -c ":norm! ZQ" Xfile_Test_normal22_zet')
+ call system(GetVimCommand() . args . ' -c "%d" -c ":norm! ZQ" Xfile_Test_normal22_zet')
let a = readfile('Xfile_Test_normal22_zet')
call assert_equal(['1', '2'], a)
+ " Unsupported Z command
+ call assert_beeps('normal! ZW')
+
" Nvim: This sometimes hangs the TSAN build.
" for file in ['Xfile_Test_normal22_zet']
" call delete(file)
@@ -1249,6 +1442,15 @@ func Test_normal23_K()
call assert_match("man --pager=cat 'man'", a)
endif
+ " Error cases
+ call setline(1, '#$#')
+ call assert_fails('normal! ggK', 'E349:')
+ call setline(1, '---')
+ call assert_fails('normal! ggv2lK', 'E349:')
+ call setline(1, ['abc', 'xyz'])
+ call assert_fails("normal! gg2lv2h\<C-]>", 'E426:')
+ call assert_beeps("normal! ggVjK")
+
" clean up
let &keywordprg = k
bw!
@@ -1382,9 +1584,16 @@ func Test_normal27_bracket()
call assert_equal(5, line('.'))
call assert_equal(3, col('.'))
- " No mark after line 21, cursor moves to first non blank on current line
+ " No mark before line 1, cursor moves to first non-blank on current line
+ 1
+ norm! 5|['
+ call assert_equal(' 1 b', getline('.'))
+ call assert_equal(1, line('.'))
+ call assert_equal(3, col('.'))
+
+ " No mark after line 21, cursor moves to first non-blank on current line
21
- norm! $]'
+ norm! 5|]'
call assert_equal(' 21 b', getline('.'))
call assert_equal(21, line('.'))
call assert_equal(3, col('.'))
@@ -1401,12 +1610,40 @@ func Test_normal27_bracket()
call assert_equal(20, line('.'))
call assert_equal(8, col('.'))
+ " No mark before line 1, cursor does not move
+ 1
+ norm! 5|[`
+ call assert_equal(' 1 b', getline('.'))
+ call assert_equal(1, line('.'))
+ call assert_equal(5, col('.'))
+
+ " No mark after line 21, cursor does not move
+ 21
+ norm! 5|]`
+ call assert_equal(' 21 b', getline('.'))
+ call assert_equal(21, line('.'))
+ call assert_equal(5, col('.'))
+
+ " Count too large for [`
+ " cursor moves to first lowercase mark
+ norm! 99[`
+ call assert_equal(' 1 b', getline('.'))
+ call assert_equal(1, line('.'))
+ call assert_equal(7, col('.'))
+
+ " Count too large for ]`
+ " cursor moves to last lowercase mark
+ norm! 99]`
+ call assert_equal(' 20 b', getline('.'))
+ call assert_equal(20, line('.'))
+ call assert_equal(8, col('.'))
+
" clean up
bw!
endfunc
+" Test for ( and ) sentence movements
func Test_normal28_parenthesis()
- " basic testing for ( and )
new
call append(0, ['This is a test. With some sentences!', '', 'Even with a question? And one more. And no sentence here'])
@@ -1424,12 +1661,43 @@ func Test_normal28_parenthesis()
norm! $d(
call assert_equal(['With some sentences!', '', ' ', '', 'This is a long sentence', ''], getline(1, '$'))
+ " Move to the next sentence from a paragraph macro
+ %d
+ call setline(1, ['.LP', 'blue sky!. blue sky.', 'blue sky. blue sky.'])
+ call cursor(1, 1)
+ normal )
+ call assert_equal([2, 1], [line('.'), col('.')])
+ normal )
+ call assert_equal([2, 12], [line('.'), col('.')])
+ normal ((
+ call assert_equal([1, 1], [line('.'), col('.')])
+
+ " It is an error if a next sentence is not found
+ %d
+ call setline(1, '.SH')
+ call assert_beeps('normal )')
+
+ " If only dot is present, don't treat that as a sentence
+ call setline(1, '. This is a sentence.')
+ normal $((
+ call assert_equal(3, col('.'))
+
+ " Jumping to a fold should open the fold
+ call setline(1, ['', '', 'one', 'two', 'three'])
+ set foldenable
+ 2,$fold
+ call feedkeys(')', 'xt')
+ call assert_equal(3, line('.'))
+ call assert_equal(1, foldlevel('.'))
+ call assert_equal(-1, foldclosed('.'))
+ set foldenable&
+
" clean up
bw!
endfunc
-fun! Test_normal29_brace()
- " basic test for { and } movements
+" Test for { and } paragraph movements
+func Test_normal29_brace()
let text =<< trim [DATA]
A paragraph begins after each empty line, and also at each of a set of
paragraph macros, specified by the pairs of characters in the 'paragraphs'
@@ -1582,12 +1850,24 @@ fun! Test_normal29_brace()
" [DATA]
" call assert_equal(expected, getline(1, '$'))
+ " Jumping to a fold should open the fold
+ " %d
+ " call setline(1, ['', 'one', 'two', ''])
+ " set foldenable
+ " 2,$fold
+ " call feedkeys('}', 'xt')
+ " call assert_equal(4, line('.'))
+ " call assert_equal(1, foldlevel('.'))
+ " call assert_equal(-1, foldclosed('.'))
+ " set foldenable&
+
" clean up
set cpo-={
bw!
endfunc
-fun! Test_normal30_changecase()
+" Test for ~ command
+func Test_normal30_changecase()
new
call append(0, 'This is a simple test: äüöß')
norm! 1ggVu
@@ -1607,8 +1887,23 @@ fun! Test_normal30_changecase()
norm! V~
call assert_equal('THIS IS A simple test: äüöss', getline('.'))
- " Turkish ASCII turns to multi-byte. On some systems Turkish locale
- " is available but toupper()/tolower() don't do the right thing.
+ " Test for changing case across lines using 'whichwrap'
+ call setline(1, ['aaaaaa', 'aaaaaa'])
+ normal! gg10~
+ call assert_equal(['AAAAAA', 'aaaaaa'], getline(1, 2))
+ set whichwrap+=~
+ normal! gg10~
+ call assert_equal(['aaaaaa', 'AAAAaa'], getline(1, 2))
+ set whichwrap&
+
+ " clean up
+ bw!
+endfunc
+
+" Turkish ASCII turns to multi-byte. On some systems Turkish locale
+" is available but toupper()/tolower() don't do the right thing.
+func Test_normal_changecase_turkish()
+ new
try
lang tr_TR.UTF-8
set casemap=
@@ -1652,13 +1947,11 @@ fun! Test_normal30_changecase()
" can't use Turkish locale
throw 'Skipped: Turkish locale not available'
endtry
-
- " clean up
- bw!
+ close!
endfunc
-fun! Test_normal31_r_cmd()
- " Test for r command
+" Test for r (replace) command
+func Test_normal31_r_cmd()
new
call append(0, 'This is a simple test: abcd')
exe "norm! 1gg$r\<cr>"
@@ -1677,13 +1970,29 @@ fun! Test_normal31_r_cmd()
exe "norm! 1gg05rf"
call assert_equal('fffffis a', getline(1))
+ " When replacing characters, copy characters from above and below lines
+ " using CTRL-Y and CTRL-E.
+ " Different code paths are used for utf-8 and latin1 encodings
+ set showmatch
+ for enc in ['latin1', 'utf-8']
+ enew!
+ let &encoding = enc
+ call setline(1, [' {a}', 'xxxxxxxxxx', ' [b]'])
+ exe "norm! 2gg5r\<C-Y>l5r\<C-E>"
+ call assert_equal(' {a}x [b]x', getline(2))
+ endfor
+ set showmatch&
+
+ " r command should fail in operator pending mode
+ call assert_beeps('normal! cr')
+
" clean up
set noautoindent
bw!
endfunc
+" Test for g*, g#
func Test_normal32_g_cmd1()
- " Test for g*, g#
new
call append(0, ['abc.x_foo', 'x_foobar.abc'])
1
@@ -1698,11 +2007,12 @@ func Test_normal32_g_cmd1()
bw!
endfunc
-fun! Test_normal33_g_cmd2()
+" Test for g`, g;, g,, g&, gv, gk, gj, gJ, g0, g^, g_, gm, g$, gM, g CTRL-G,
+" gi and gI commands
+func Test_normal33_g_cmd2()
if !has("jumplist")
return
endif
- " Tests for g cmds
call Setup_NewWindow()
" Test for g`
clearjumps
@@ -1714,6 +2024,10 @@ fun! Test_normal33_g_cmd2()
call assert_equal('>', a[-1:])
call assert_equal(1, line('.'))
call assert_equal('1', getline('.'))
+ call cursor(10, 1)
+ norm! g'a
+ call assert_equal('>', a[-1:])
+ call assert_equal(1, line('.'))
" Test for g; and g,
norm! g;
@@ -1744,6 +2058,16 @@ fun! Test_normal33_g_cmd2()
norm! g&
call assert_equal(['11', '22', '33', '44', '55', '66', '77', '88', '9', '110', 'a', 'b', 'c', 'dd'], getline(1, '$'))
+ " Jumping to a fold using gg should open the fold
+ set foldenable
+ set foldopen+=jump
+ 5,8fold
+ call feedkeys('6gg', 'xt')
+ call assert_equal(1, foldlevel('.'))
+ call assert_equal(-1, foldclosed('.'))
+ set foldopen-=jump
+ set foldenable&
+
" Test for gv
%d
call append('$', repeat(['abcdefgh'], 8))
@@ -1755,14 +2079,20 @@ fun! Test_normal33_g_cmd2()
exe "norm! G0\<c-v>4k4ly"
exe "norm! gvood"
call assert_equal(['', 'abfgh', 'abfgh', 'abfgh', 'fgh', 'fgh', 'fgh', 'fgh', 'fgh'], getline(1,'$'))
+ " gv cannot be used in operator pending mode
+ call assert_beeps('normal! cgv')
+ " gv should beep without a previously selected visual area
+ new
+ call assert_beeps('normal! gv')
+ close
" Test for gk/gj
%d
15vsp
set wrap listchars= sbr=
- let lineA='abcdefghijklmnopqrstuvwxyz'
- let lineB='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
- let lineC='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
+ let lineA = 'abcdefghijklmnopqrstuvwxyz'
+ let lineB = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ let lineC = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
$put =lineA
$put =lineB
@@ -1795,8 +2125,39 @@ fun! Test_normal33_g_cmd2()
norm! g^yl
call assert_equal(15, col('.'))
call assert_equal('l', getreg(0))
+ call assert_beeps('normal 5g$')
+
+ " Test for g$ with double-width character half displayed
+ vsplit
+ 9wincmd |
+ setlocal nowrap nonumber
+ call setline(2, 'asdfasdfヨ')
+ 2
+ normal 0g$
+ call assert_equal(8, col('.'))
+ 10wincmd |
+ normal 0g$
+ call assert_equal(9, col('.'))
+
+ setlocal signcolumn=yes
+ 11wincmd |
+ normal 0g$
+ call assert_equal(8, col('.'))
+ 12wincmd |
+ normal 0g$
+ call assert_equal(9, col('.'))
+
+ close
+
+ " Test for g_
+ call assert_beeps('normal! 100g_')
+ call setline(2, [' foo ', ' foobar '])
+ normal! 2ggg_
+ call assert_equal(5, col('.'))
+ normal! 2g_
+ call assert_equal(8, col('.'))
- norm! 2ggdd
+ norm! 2ggdG
$put =lineC
" Test for gM
@@ -1812,7 +2173,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)
@@ -1830,17 +2199,46 @@ fun! Test_normal33_g_cmd2()
$put ='third line'
norm! gi another word
call assert_equal(['foobar next word another word', 'new line', 'third line'], getline(1,'$'))
+ call setline(1, 'foobar')
+ normal! Ggifirst line
+ call assert_equal('foobarfirst line', getline(1))
+ " Test gi in 'virtualedit' mode with cursor after the end of the line
+ set virtualedit=all
+ call setline(1, 'foo')
+ exe "normal! Abar\<Right>\<Right>\<Right>\<Right>"
+ call setline(1, 'foo')
+ normal! Ggifirst line
+ call assert_equal('foo first line', getline(1))
+ set virtualedit&
+
+ " Test for aboring a g command using CTRL-\ CTRL-G
+ exe "normal! g\<C-\>\<C-G>"
+ call assert_equal('foo first line', getline('.'))
" clean up
bw!
endfunc
+func Test_normal_ex_substitute()
+ " This was hanging on the substitute prompt.
+ new
+ call setline(1, 'a')
+ exe "normal! gggQs/a/b/c\<CR>"
+ call assert_equal('a', getline(1))
+ bwipe!
+endfunc
+
+" Test for g CTRL-G
func Test_g_ctrl_g()
new
let a = execute(":norm! g\<c-g>")
call assert_equal("\n--No lines in buffer--", a)
+ " Test for CTRL-G (same as :file)
+ let a = execute(":norm! \<c-g>")
+ call assert_equal("\n\n\"[No Name]\" --No lines in buffer--", a)
+
call setline(1, ['first line', 'second line'])
" Test g CTRL-g with dos, mac and unix file type.
@@ -1908,8 +2306,8 @@ func Test_g_ctrl_g()
bwipe!
endfunc
-fun! Test_normal34_g_cmd3()
- " Test for g8
+" Test for g8
+func Test_normal34_g_cmd3()
new
let a=execute(':norm! 1G0g8')
call assert_equal("\nNUL", a)
@@ -1926,11 +2324,10 @@ fun! Test_normal34_g_cmd3()
bw!
endfunc
+" Test 8g8 which finds invalid utf8 at or after the cursor.
func Test_normal_8g8()
new
- " Test 8g8 which finds invalid utf8 at or after the cursor.
-
" With invalid byte.
call setline(1, "___\xff___")
norm! 1G08g8g
@@ -1959,8 +2356,8 @@ func Test_normal_8g8()
bw!
endfunc
-fun! Test_normal35_g_cmd4()
- " Test for g<
+" Test for g<
+func Test_normal35_g_cmd4()
" Cannot capture its output,
" probably a bug, therefore, test disabled:
throw "Skipped: output of g< can't be tested currently"
@@ -1969,7 +2366,8 @@ fun! Test_normal35_g_cmd4()
call assert_true(!empty(b), 'failed `execute(g<)`')
endfunc
-fun! Test_normal36_g_cmd5()
+" Test for gp gP go
+func Test_normal36_g_cmd5()
new
call append(0, 'abcdefghijklmnopqrstuvwxyz')
set ff=unix
@@ -2007,8 +2405,8 @@ fun! Test_normal36_g_cmd5()
bw!
endfunc
-fun! Test_normal37_g_cmd6()
- " basic test for gt and gT
+" Test for gt and gT
+func Test_normal37_g_cmd6()
tabnew 1.txt
tabnew 2.txt
tabnew 3.txt
@@ -2031,11 +2429,11 @@ fun! Test_normal37_g_cmd6()
tabclose
endfor
" clean up
- call assert_fails(':tabclose', 'E784')
+ call assert_fails(':tabclose', 'E784:')
endfunc
-fun! Test_normal38_nvhome()
- " Test for <Home> and <C-Home> key
+" Test for <Home> and <C-Home> key
+func Test_normal38_nvhome()
new
call setline(1, range(10))
$
@@ -2050,12 +2448,28 @@ fun! Test_normal38_nvhome()
call assert_equal([0, 5, 1, 0, 1], getcurpos())
exe "norm! \<c-home>"
call assert_equal([0, 1, 1, 0, 1], getcurpos())
+ exe "norm! G\<c-kHome>"
+ call assert_equal([0, 1, 1, 0, 1], getcurpos())
" clean up
bw!
endfunc
-fun! Test_normal39_cw()
+" Test for <End> and <C-End> keys
+func Test_normal_nvend()
+ new
+ call setline(1, map(range(1, 10), '"line" .. v:val'))
+ exe "normal! \<End>"
+ call assert_equal(5, col('.'))
+ exe "normal! 4\<End>"
+ call assert_equal([4, 5], [line('.'), col('.')])
+ exe "normal! \<C-End>"
+ call assert_equal([10, 6], [line('.'), col('.')])
+ close!
+endfunc
+
+" Test for cw cW ce
+func Test_normal39_cw()
" Test for cw and cW on whitespace
" and cpo+=w setting
new
@@ -2076,12 +2490,27 @@ fun! Test_normal39_cw()
norm! 2gg0cwfoo
call assert_equal('foo', getline('.'))
+ call setline(1, 'one; two')
+ call cursor(1, 1)
+ call feedkeys('cwvim', 'xt')
+ call assert_equal('vim; two', getline(1))
+ call feedkeys('0cWone', 'xt')
+ call assert_equal('one two', getline(1))
+ "When cursor is at the end of a word 'ce' will change until the end of the
+ "next word, but 'cw' will change only one character
+ call setline(1, 'one two')
+ call feedkeys('0ecwce', 'xt')
+ call assert_equal('once two', getline(1))
+ call setline(1, 'one two')
+ call feedkeys('0ecely', 'xt')
+ call assert_equal('only', getline(1))
+
" clean up
bw!
endfunc
-fun! Test_normal40_ctrl_bsl()
- " Basic test for CTRL-\ commands
+" Test for CTRL-\ commands
+func Test_normal40_ctrl_bsl()
new
call append(0, 'here are some words')
exe "norm! 1gg0a\<C-\>\<C-N>"
@@ -2095,19 +2524,23 @@ fun! Test_normal40_ctrl_bsl()
call assert_equal('n', mode())
call assert_equal(1, col('.'))
"imap <buffer> , <c-\><c-n>
- set im
+ " set im
exe ":norm! \<c-\>\<c-n>dw"
- set noim
+ " set noim
call assert_equal('are some words', getline(1))
call assert_false(&insertmode)
+ call assert_beeps("normal! \<C-\>\<C-A>")
+
+ " Using CTRL-\ CTRL-N in cmd window should close the window
+ call feedkeys("q:\<C-\>\<C-N>", 'xt')
+ call assert_equal('', getcmdwintype())
" clean up
bw!
endfunc
-fun! Test_normal41_insert_reg()
- " Test for <c-r>=, <c-r><c-r>= and <c-r><c-o>=
- " in insert mode
+" Test for <c-r>=, <c-r><c-r>= and <c-r><c-o>= in insert mode
+func Test_normal41_insert_reg()
new
set sts=2 sw=2 ts=8 tw=0
call append(0, ["aaa\tbbb\tccc", '', '', ''])
@@ -2125,8 +2558,8 @@ fun! Test_normal41_insert_reg()
bw!
endfunc
+" Test for Ctrl-D and Ctrl-U
func Test_normal42_halfpage()
- " basic test for Ctrl-D and Ctrl-U
call Setup_NewWindow()
call assert_equal(5, &scroll)
exe "norm! \<c-d>"
@@ -2162,92 +2595,6 @@ func Test_normal42_halfpage()
bw!
endfunc
-fun! Test_normal43_textobject1()
- " basic tests for text object aw
- new
- call append(0, ['foobar,eins,foobar', 'foo,zwei,foo '])
- " diw
- norm! 1gg0diw
- call assert_equal([',eins,foobar', 'foo,zwei,foo ', ''], getline(1,'$'))
- " daw
- norm! 2ggEdaw
- call assert_equal([',eins,foobar', 'foo,zwei,', ''], getline(1, '$'))
- %d
- call append(0, ["foo\teins\tfoobar", "foo\tzwei\tfoo "])
- " diW
- norm! 2ggwd2iW
- call assert_equal(['foo eins foobar', 'foo foo ', ''], getline(1,'$'))
- " daW
- norm! 1ggd2aW
- call assert_equal(['foobar', 'foo foo ', ''], getline(1,'$'))
-
- %d
- call append(0, ["foo\teins\tfoobar", "foo\tzwei\tfoo "])
- " aw in visual line mode switches to characterwise mode
- norm! 2gg$Vawd
- call assert_equal(['foo eins foobar', 'foo zwei foo'], getline(1,'$'))
- norm! 1gg$Viwd
- call assert_equal(['foo eins ', 'foo zwei foo'], getline(1,'$'))
-
- " clean up
- bw!
-endfunc
-
-func Test_normal44_textobjects2()
- " basic testing for is and as text objects
- new
- call append(0, ['This is a test. With some sentences!', '', 'Even with a question? And one more. And no sentence here'])
- " Test for dis - does not remove trailing whitespace
- norm! 1gg0dis
- call assert_equal([' With some sentences!', '', 'Even with a question? And one more. And no sentence here', ''], getline(1,'$'))
- " Test for das - removes leading whitespace
- norm! 3ggf?ldas
- call assert_equal([' With some sentences!', '', 'Even with a question? And no sentence here', ''], getline(1,'$'))
- " when used in visual mode, is made characterwise
- norm! 3gg$Visy
- call assert_equal('v', visualmode())
- " reset visualmode()
- norm! 3ggVy
- norm! 3gg$Vasy
- call assert_equal('v', visualmode())
- " basic testing for textobjects a< and at
- %d
- call setline(1, ['<div> ','<a href="foobar" class="foo">xyz</a>',' </div>', ' '])
- " a<
- norm! 1gg0da<
- call assert_equal([' ', '<a href="foobar" class="foo">xyz</a>', ' </div>', ' '], getline(1,'$'))
- norm! 1pj
- call assert_equal([' <div>', '<a href="foobar" class="foo">xyz</a>', ' </div>', ' '], getline(1,'$'))
- " at
- norm! d2at
- call assert_equal([' '], getline(1,'$'))
- %d
- call setline(1, ['<div> ','<a href="foobar" class="foo">xyz</a>',' </div>', ' '])
- " i<
- norm! 1gg0di<
- call assert_equal(['<> ', '<a href="foobar" class="foo">xyz</a>', ' </div>', ' '], getline(1,'$'))
- norm! 1Pj
- call assert_equal(['<div> ', '<a href="foobar" class="foo">xyz</a>', ' </div>', ' '], getline(1,'$'))
- norm! d2it
- call assert_equal(['<div></div>',' '], getline(1,'$'))
- " basic testing for a[ and i[ text object
- %d
- call setline(1, [' ', '[', 'one [two]', 'thre', ']'])
- norm! 3gg0di[
- call assert_equal([' ', '[', ']'], getline(1,'$'))
- call setline(1, [' ', '[', 'one [two]', 'thre', ']'])
- norm! 3gg0ftd2a[
- call assert_equal([' '], getline(1,'$'))
- %d
- " Test for i" when cursor is in front of a quoted object
- call append(0, 'foo "bar"')
- norm! 1gg0di"
- call assert_equal(['foo ""', ''], getline(1,'$'))
-
- " clean up
- bw!
-endfunc
-
func Test_normal45_drop()
if !has('dnd')
" The ~ register does not exist
@@ -2334,7 +2681,7 @@ func Test_normal49_counts()
endfunc
func Test_normal50_commandline()
- if !has("timers") || !has("cmdline_hist") || !has("vertsplit")
+ if !has("timers") || !has("cmdline_hist")
return
endif
func! DoTimerWork(id)
@@ -2396,6 +2743,8 @@ func Test_normal52_rl()
call assert_equal(19, col('.'))
call feedkeys("\<right>", 'tx')
call assert_equal(18, col('.'))
+ call feedkeys("\<left>", 'tx')
+ call assert_equal(19, col('.'))
call feedkeys("\<s-right>", 'tx')
call assert_equal(13, col('.'))
call feedkeys("\<c-right>", 'tx')
@@ -2485,6 +2834,18 @@ func Test_gr_command()
normal 4gro
call assert_equal('ooooecond line', getline(2))
let &cpo = save_cpo
+ normal! ggvegrx
+ call assert_equal('xxxxx line', getline(1))
+ exe "normal! gggr\<C-V>122"
+ call assert_equal('zxxxx line', getline(1))
+ set virtualedit=all
+ normal! 15|grl
+ call assert_equal('zxxxx line l', getline(1))
+ set virtualedit&
+ set nomodifiable
+ call assert_fails('normal! grx', 'E21:')
+ call assert_fails('normal! gRx', 'E21:')
+ set modifiable&
enew!
endfunc
@@ -2507,6 +2868,8 @@ func Test_changelist()
normal g;
call assert_equal([2, 2], [line('.'), col('.')])
call assert_fails('normal g;', 'E662:')
+ new
+ call assert_fails('normal g;', 'E664:')
%bwipe!
let &ul = save_ul
endfunc
@@ -2553,6 +2916,10 @@ endfunc
" Jumping to beginning and end of methods in Java-like languages
func Test_java_motion()
new
+ call assert_beeps('normal! [m')
+ call assert_beeps('normal! ]m')
+ call assert_beeps('normal! [M')
+ call assert_beeps('normal! ]M')
a
Piece of Java
{
@@ -2627,7 +2994,7 @@ Piece of Java
close!
endfunc
-fun! Test_normal_gdollar_cmd()
+func Test_normal_gdollar_cmd()
if !has("jumplist")
return
endif
@@ -2682,10 +3049,11 @@ fun! Test_normal_gdollar_cmd()
bw!
endfunc
-func Test_normal_gk()
+func Test_normal_gk_gj()
" needs 80 column new window
new
vert 80new
+ call assert_beeps('normal gk')
put =[repeat('x',90)..' {{{1', 'x {{{1']
norm! gk
" In a 80 column wide terminal the window will be only 78 char
@@ -2700,12 +3068,12 @@ func Test_normal_gk()
norm! gk
call assert_equal(95, col('.'))
call assert_equal(95, virtcol('.'))
- bw!
- bw!
+ %bw!
" needs 80 column new window
new
vert 80new
+ call assert_beeps('normal gj')
set number
set numberwidth=10
set cpoptions+=n
@@ -2724,9 +3092,191 @@ func Test_normal_gk()
call assert_equal(1, col('.'))
norm! gj
call assert_equal(76, col('.'))
- bw!
- bw!
- set cpoptions& number& numberwidth&
+ " When 'nowrap' is set, gk and gj behave like k and j
+ set nowrap
+ normal! gk
+ call assert_equal([2, 76], [line('.'), col('.')])
+ normal! gj
+ call assert_equal([3, 76], [line('.'), col('.')])
+ %bw!
+ set cpoptions& number& numberwidth& wrap&
+endfunc
+
+" Test for cursor movement with '-' in 'cpoptions'
+func Test_normal_cpo_minus()
+ throw 'Skipped: Nvim does not support cpoptions flag "-"'
+ new
+ call setline(1, ['foo', 'bar', 'baz'])
+ let save_cpo = &cpo
+ set cpo+=-
+ call assert_beeps('normal 10j')
+ call assert_equal(1, line('.'))
+ normal G
+ call assert_beeps('normal 10k')
+ call assert_equal(3, line('.'))
+ call assert_fails(10, 'E16:')
+ let &cpo = save_cpo
+ close!
+endfunc
+
+" Test for displaying dollar when changing text ('$' flag in 'cpoptions')
+func Test_normal_cpo_dollar()
+ throw 'Skipped: use test/functional/legacy/cpoptions_spec.lua'
+ new
+ let g:Line = ''
+ func SaveFirstLine()
+ let g:Line = Screenline(1)
+ return ''
+ endfunc
+ inoremap <expr> <buffer> <F2> SaveFirstLine()
+ call test_override('redraw_flag', 1)
+ set cpo+=$
+ call setline(1, 'one two three')
+ redraw!
+ exe "normal c2w\<F2>vim"
+ call assert_equal('one tw$ three', g:Line)
+ call assert_equal('vim three', getline(1))
+ set cpo-=$
+ call test_override('ALL', 0)
+ delfunc SaveFirstLine
+ %bw!
+endfunc
+
+" Test for using : to run a multi-line Ex command in operator pending mode
+func Test_normal_yank_with_excmd()
+ new
+ call setline(1, ['foo', 'bar', 'baz'])
+ let @a = ''
+ call feedkeys("\"ay:if v:true\<CR>normal l\<CR>endif\<CR>", 'xt')
+ call assert_equal('f', @a)
+ close!
+endfunc
+
+" Test for supplying a count to a normal-mode command across a cursorhold call
+func Test_normal_cursorhold_with_count()
+ throw 'Skipped: Nvim removed <CursorHold> key'
+ func s:cHold()
+ let g:cHold_Called += 1
+ endfunc
+ new
+ augroup normalcHoldTest
+ au!
+ au CursorHold <buffer> call s:cHold()
+ augroup END
+ let g:cHold_Called = 0
+ call feedkeys("3\<CursorHold>2ix", 'xt')
+ call assert_equal(1, g:cHold_Called)
+ call assert_equal(repeat('x', 32), getline(1))
+ augroup normalcHoldTest
+ au!
+ augroup END
+ au! normalcHoldTest
+ close!
+ delfunc s:cHold
+endfunc
+
+" Test for using a count and a command with CTRL-W
+func Test_wincmd_with_count()
+ call feedkeys("\<C-W>12n", 'xt')
+ call assert_equal(12, winheight(0))
+endfunc
+
+" Test for 'b', 'B' 'ge' and 'gE' commands
+func Test_horiz_motion()
+ new
+ normal! gg
+ call assert_beeps('normal! b')
+ call assert_beeps('normal! B')
+ call assert_beeps('normal! gE')
+ call assert_beeps('normal! ge')
+ " <S-Backspace> moves one word left and <C-Backspace> moves one WORD left
+ call setline(1, 'one ,two ,three')
+ exe "normal! $\<S-BS>"
+ call assert_equal(11, col('.'))
+ exe "normal! $\<C-BS>"
+ call assert_equal(10, col('.'))
+ close!
+endfunc
+
+" Test for using a : command in operator pending mode
+func Test_normal_colon_op()
+ new
+ call setline(1, ['one', 'two'])
+ call assert_beeps("normal! Gc:d\<CR>")
+ close!
+endfunc
+
+" Test for 'w' and 'b' commands
+func Test_normal_word_move()
+ new
+ call setline(1, ['foo bar a', '', 'foo bar b'])
+ " copy a single character word at the end of a line
+ normal 1G$yw
+ call assert_equal('a', @")
+ " copy a single character word at the end of a file
+ normal G$yw
+ call assert_equal('b', @")
+ " check for a word movement handling an empty line properly
+ normal 1G$vwy
+ call assert_equal("a\n\n", @")
+
+ " copy using 'b' command
+ %d
+ " non-empty blank line at the start of file
+ call setline(1, [' ', 'foo bar'])
+ normal 2Gyb
+ call assert_equal(" \n", @")
+ " try to copy backwards from the start of the file
+ call setline(1, ['one two', 'foo bar'])
+ call assert_beeps('normal ggyb')
+ " 'b' command should stop at an empty line
+ call setline(1, ['one two', '', 'foo bar'])
+ normal 3Gyb
+ call assert_equal("\n", @")
+ normal 3Gy2b
+ call assert_equal("two\n", @")
+ " 'b' command should not stop at a non-empty blank line
+ call setline(1, ['one two', ' ', 'foo bar'])
+ normal 3Gyb
+ call assert_equal("two\n ", @")
+
+ close!
+endfunc
+
+" Test for 'scrolloff' with a long line that doesn't fit in the screen
+func Test_normal_scroloff()
+ 10new
+ 80vnew
+ call setline(1, repeat('a', 1000))
+ set scrolloff=10
+ normal gg10gj
+ call assert_equal(8, winline())
+ normal 10gj
+ call assert_equal(10, winline())
+ normal 10gk
+ call assert_equal(3, winline())
+ set scrolloff&
+ close!
+endfunc
+
+" Test for vertical scrolling with CTRL-F and CTRL-B with a long line
+func Test_normal_vert_scroll_longline()
+ 10new
+ 80vnew
+ call setline(1, range(1, 10))
+ call append(5, repeat('a', 1000))
+ exe "normal gg\<C-F>"
+ call assert_equal(6, line('.'))
+ exe "normal \<C-F>\<C-F>"
+ call assert_equal(11, line('.'))
+ call assert_equal(1, winline())
+ exe "normal \<C-B>"
+ call assert_equal(10, line('.'))
+ call assert_equal(3, winline())
+ exe "normal \<C-B>\<C-B>"
+ call assert_equal(5, line('.'))
+ call assert_equal(5, winline())
+ close!
endfunc
" Some commands like yy, cc, dd, >>, << and !! accept a count after
@@ -2759,4 +3309,37 @@ 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
+
+func Test_normal_count_out_of_range()
+ new
+ call setline(1, 'text')
+ normal 44444444444|
+ call assert_equal(999999999, v:count)
+ normal 444444444444|
+ call assert_equal(999999999, v:count)
+ normal 4444444444444|
+ call assert_equal(999999999, v:count)
+ normal 4444444444444444444|
+ call assert_equal(999999999, v:count)
+
+ normal 9y99999999|
+ call assert_equal(899999991, v:count)
+ normal 10y99999999|
+ call assert_equal(999999999, v:count)
+ normal 44444444444y44444444444|
+ call assert_equal(999999999, v:count)
+ bwipe!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_number.vim b/src/nvim/testdir/test_number.vim
index d737ebe9f0..521b0cf706 100644
--- a/src/nvim/testdir/test_number.vim
+++ b/src/nvim/testdir/test_number.vim
@@ -284,10 +284,10 @@ func Test_relativenumber_colors()
" Default colors
call VerifyScreenDump(buf, 'Test_relnr_colors_1', {})
- call term_sendkeys(buf, ":hi LineNrAbove ctermfg=blue\<CR>")
+ call term_sendkeys(buf, ":hi LineNrAbove ctermfg=blue\<CR>:\<CR>")
call VerifyScreenDump(buf, 'Test_relnr_colors_2', {})
- call term_sendkeys(buf, ":hi LineNrBelow ctermfg=green\<CR>")
+ call term_sendkeys(buf, ":hi LineNrBelow ctermfg=green\<CR>:\<CR>")
call VerifyScreenDump(buf, 'Test_relnr_colors_3', {})
call term_sendkeys(buf, ":hi clear LineNrAbove\<CR>")
@@ -298,6 +298,31 @@ func Test_relativenumber_colors()
call delete('XTest_relnr')
endfunc
+func Test_relativenumber_callback()
+ CheckScreendump
+ CheckFeature timers
+
+ let lines =<< trim END
+ call setline(1, ['aaaaa', 'bbbbb', 'ccccc', 'ddddd'])
+ set relativenumber
+ call cursor(4, 1)
+
+ func Func(timer)
+ call cursor(1, 1)
+ endfunc
+
+ call timer_start(300, 'Func')
+ END
+ call writefile(lines, 'Xrnu_timer')
+
+ let buf = RunVimInTerminal('-S Xrnu_timer', #{rows: 8})
+ call TermWait(buf, 310)
+ call VerifyScreenDump(buf, 'Test_relativenumber_callback_1', {})
+
+ call StopVimInTerminal(buf)
+ call delete('Xrnu_timer')
+endfunc
+
" Test for displaying line numbers with 'rightleft'
func Test_number_rightleft()
CheckFeature rightleft
diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim
index 5946732937..1f003041e6 100644
--- a/src/nvim/testdir/test_options.vim
+++ b/src/nvim/testdir/test_options.vim
@@ -22,16 +22,36 @@ func Test_whichwrap()
call assert_equal('h', &whichwrap)
set whichwrap&
-endfunction
+endfunc
-function! Test_isfname()
+func Test_isfname()
" This used to cause Vim to access uninitialized memory.
set isfname=
call assert_equal("~X", expand("~X"))
set isfname&
-endfunction
+endfunc
+
+" Test for getting the value of 'pastetoggle'
+func Test_pastetoggle()
+ " character with K_SPECIAL byte
+ let &pastetoggle = '…'
+ call assert_equal('…', &pastetoggle)
+ call assert_equal("\n pastetoggle=…", execute('set pastetoggle?'))
+
+ " modified character with K_SPECIAL byte
+ let &pastetoggle = '<M-…>'
+ call assert_equal('<M-…>', &pastetoggle)
+ call assert_equal("\n pastetoggle=<M-…>", execute('set pastetoggle?'))
+
+ " illegal bytes
+ let str = ":\x7f:\x80:\x90:\xd0:"
+ let &pastetoggle = str
+ call assert_equal(str, &pastetoggle)
+ call assert_equal("\n pastetoggle=" .. strtrans(str), execute('set pastetoggle?'))
+ unlet str
+endfunc
-function Test_wildchar()
+func Test_wildchar()
" Empty 'wildchar' used to access invalid memory.
call assert_fails('set wildchar=', 'E521:')
call assert_fails('set wildchar=abc', 'E521:')
@@ -42,7 +62,7 @@ function Test_wildchar()
let a=execute('set wildchar?')
call assert_equal("\n wildchar=<Esc>", a)
set wildchar&
-endfunction
+endfunc
func Test_wildoptions()
set wildoptions=
@@ -51,7 +71,7 @@ func Test_wildoptions()
call assert_equal('tagfile', &wildoptions)
endfunc
-function! Test_options()
+func Test_options_command()
let caught = 'ok'
try
options
@@ -78,6 +98,19 @@ function! Test_options()
" close option-window
close
+ " Open the option-window at the top.
+ set splitbelow
+ topleft options
+ call assert_equal(1, winnr())
+ close
+
+ " Open the option-window at the bottom.
+ set nosplitbelow
+ botright options
+ call assert_equal(winnr('$'), winnr())
+ close
+ set splitbelow&
+
" Open the option-window in a new tab.
tab options
" Check if the option-window is opened in a tab.
@@ -85,12 +118,18 @@ function! Test_options()
call assert_notequal('option-window', bufname(''))
normal gt
call assert_equal('option-window', bufname(''))
-
" close option-window
close
-endfunction
-function! Test_path_keep_commas()
+ " Open the options window browse
+ if has('browse')
+ browse set
+ call assert_equal('option-window', bufname(''))
+ close
+ endif
+endfunc
+
+func Test_path_keep_commas()
" Test that changing 'path' keeps two commas.
set path=foo,,bar
set path-=bar
@@ -98,7 +137,7 @@ function! Test_path_keep_commas()
call assert_equal('foo,,bar', &path)
set path&
-endfunction
+endfunc
func Test_filetype_valid()
set ft=valid_name
@@ -199,6 +238,12 @@ func Test_set_completion()
call feedkeys(":set di\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"set dictionary diff diffexpr diffopt digraph directory display', @:)
+ call feedkeys(":setlocal di\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"setlocal dictionary diff diffexpr diffopt digraph directory display', @:)
+
+ call feedkeys(":setglobal di\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"setglobal dictionary diff diffexpr diffopt digraph directory display', @:)
+
" Expand boolan options. When doing :set no<Tab>
" vim displays the options names without "no" but completion uses "no...".
call feedkeys(":set nodi\<C-A>\<C-B>\"\<CR>", 'tx')
@@ -229,6 +274,7 @@ func Test_set_completion()
call feedkeys(":set tags=./\\\\ dif\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"set tags=./\\ diff diffexpr diffopt', @:)
+
set tags&
" Expand values for 'filetype'
@@ -244,9 +290,10 @@ func Test_set_errors()
call assert_fails('set regexpengine=3', 'E474:')
call assert_fails('set history=10001', 'E474:')
call assert_fails('set numberwidth=21', 'E474:')
- call assert_fails('set colorcolumn=-a')
- call assert_fails('set colorcolumn=a')
- call assert_fails('set colorcolumn=1,')
+ call assert_fails('set colorcolumn=-a', 'E474:')
+ call assert_fails('set colorcolumn=a', 'E474:')
+ call assert_fails('set colorcolumn=1,', 'E474:')
+ call assert_fails('set colorcolumn=1;', 'E474:')
call assert_fails('set cmdheight=-1', 'E487:')
call assert_fails('set cmdwinheight=-1', 'E487:')
if has('conceal')
@@ -259,6 +306,8 @@ func Test_set_errors()
call assert_fails('set shiftwidth=-1', 'E487:')
call assert_fails('set sidescroll=-1', 'E487:')
call assert_fails('set tabstop=-1', 'E487:')
+ call assert_fails('set tabstop=10000', 'E474:')
+ call assert_fails('set tabstop=5500000000', 'E474:')
call assert_fails('set textwidth=-1', 'E487:')
call assert_fails('set timeoutlen=-1', 'E487:')
call assert_fails('set updatecount=-1', 'E487:')
@@ -279,17 +328,29 @@ func Test_set_errors()
call assert_fails('set rulerformat=%15(%%', 'E542:')
call assert_fails('set statusline=%$', 'E539:')
call assert_fails('set statusline=%{', 'E540:')
+ call assert_fails('set statusline=%{%', 'E540:')
+ call assert_fails('set statusline=%{%}', 'E539:')
call assert_fails('set statusline=%(', 'E542:')
call assert_fails('set statusline=%)', 'E542:')
+ call assert_fails('set tabline=%$', 'E539:')
+ call assert_fails('set tabline=%{', 'E540:')
+ call assert_fails('set tabline=%{%', 'E540:')
+ call assert_fails('set tabline=%{%}', 'E539:')
+ call assert_fails('set tabline=%(', 'E542:')
+ call assert_fails('set tabline=%)', 'E542:')
if has('cursorshape')
" This invalid value for 'guicursor' used to cause Vim to crash.
call assert_fails('set guicursor=i-ci,r-cr:h', 'E545:')
call assert_fails('set guicursor=i-ci', 'E545:')
call assert_fails('set guicursor=x', 'E545:')
+ call assert_fails('set guicursor=x:', 'E546:')
call assert_fails('set guicursor=r-cr:horx', 'E548:')
call assert_fails('set guicursor=r-cr:hor0', 'E549:')
endif
+ if has('mouseshape')
+ call assert_fails('se mouseshape=i-r:x', 'E547:')
+ endif
call assert_fails('set backupext=~ patchmode=~', 'E589:')
call assert_fails('set winminheight=10 winheight=9', 'E591:')
call assert_fails('set winminwidth=10 winwidth=9', 'E592:')
@@ -312,21 +373,50 @@ func Test_set_errors()
set modifiable&
endfunc
+func CheckWasSet(name)
+ let verb_cm = execute('verbose set ' .. a:name .. '?')
+ call assert_match('Last set from.*test_options.vim', verb_cm)
+endfunc
+func CheckWasNotSet(name)
+ let verb_cm = execute('verbose set ' .. a:name .. '?')
+ call assert_notmatch('Last set from', verb_cm)
+endfunc
+
" Must be executed before other tests that set 'term'.
func Test_000_term_option_verbose()
if has('nvim') || has('gui_running')
return
endif
- let verb_cm = execute('verbose set t_cm')
- call assert_notmatch('Last set from', verb_cm)
+
+ call CheckWasNotSet('t_cm')
let term_save = &term
set term=ansi
- let verb_cm = execute('verbose set t_cm')
- call assert_match('Last set from.*test_options.vim', verb_cm)
+ call CheckWasSet('t_cm')
let &term = term_save
endfunc
+func Test_copy_context()
+ setlocal list
+ call CheckWasSet('list')
+ split
+ call CheckWasSet('list')
+ quit
+ setlocal nolist
+
+ set ai
+ call CheckWasSet('ai')
+ set filetype=perl
+ call CheckWasSet('filetype')
+ set fo=tcroq
+ call CheckWasSet('fo')
+
+ split Xsomebuf
+ call CheckWasSet('ai')
+ call CheckWasNotSet('filetype')
+ call CheckWasSet('fo')
+endfunc
+
func Test_set_ttytype()
" Nvim does not support 'ttytype'.
if !has('nvim') && !has('gui_running') && has('unix')
@@ -368,10 +458,19 @@ 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.
+ " opt_test.vim is generated from ../optiondefs.h using gen_opt_test.vim
if filereadable('opt_test.vim')
source opt_test.vim
+ else
+ throw 'Skipped: opt_test.vim does not exist'
endif
endfunc
@@ -538,7 +637,7 @@ func Test_copy_winopt()
call assert_equal(4,&numberwidth)
bw!
- set nohidden
+ set hidden&
endfunc
func Test_shortmess_F()
@@ -644,7 +743,62 @@ func Test_buftype()
call setline(1, ['L1'])
set buftype=nowrite
call assert_fails('write', 'E382:')
- close!
+
+ " for val in ['', 'nofile', 'nowrite', 'acwrite', 'quickfix', 'help', 'terminal', 'prompt', 'popup']
+ for val in ['', 'nofile', 'nowrite', 'acwrite', 'quickfix', 'help', 'prompt']
+ exe 'set buftype=' .. val
+ call writefile(['something'], 'XBuftype')
+ call assert_fails('write XBuftype', 'E13:', 'with buftype=' .. val)
+ endfor
+
+ call delete('XBuftype')
+ bwipe!
+endfunc
+
+" Test for the 'shell' option
+func Test_shell()
+ throw 'Skipped: Nvim does not have :shell'
+ CheckUnix
+ let save_shell = &shell
+ set shell=
+ call assert_fails('shell', 'E91:')
+ let &shell = save_shell
+endfunc
+
+" Test for the 'shellquote' option
+func Test_shellquote()
+ CheckUnix
+ set shellquote=#
+ set verbose=20
+ redir => v
+ silent! !echo Hello
+ redir END
+ set verbose&
+ set shellquote&
+ call assert_match(': "#echo Hello#"', v)
+endfunc
+
+" Test for the 'rightleftcmd' option
+func Test_rightleftcmd()
+ CheckFeature rightleft
+ set rightleft
+ set rightleftcmd
+
+ let g:l = []
+ func AddPos()
+ call add(g:l, screencol())
+ return ''
+ endfunc
+ cmap <expr> <F2> AddPos()
+
+ call feedkeys("/\<F2>abc\<Left>\<F2>\<Right>\<Right>\<F2>" ..
+ \ "\<Left>\<F2>\<Esc>", 'xt')
+ call assert_equal([&co - 1, &co - 4, &co - 2, &co - 3], g:l)
+
+ cunmap <F2>
+ unlet g:l
+ set rightleftcmd&
+ set rightleft&
endfunc
" Test for setting option values using v:false and v:true
@@ -661,6 +815,47 @@ func Test_opt_boolean()
set number&
endfunc
+" Test for the 'window' option
+func Test_window_opt()
+ " Needs only one open widow
+ %bw!
+ call setline(1, range(1, 8))
+ set window=5
+ exe "normal \<C-F>"
+ call assert_equal(4, line('w0'))
+ exe "normal \<C-F>"
+ call assert_equal(7, line('w0'))
+ exe "normal \<C-F>"
+ call assert_equal(8, line('w0'))
+ exe "normal \<C-B>"
+ call assert_equal(5, line('w0'))
+ exe "normal \<C-B>"
+ call assert_equal(2, line('w0'))
+ exe "normal \<C-B>"
+ call assert_equal(1, line('w0'))
+ set window=1
+ exe "normal gg\<C-F>"
+ call assert_equal(2, line('w0'))
+ exe "normal \<C-F>"
+ call assert_equal(3, line('w0'))
+ exe "normal \<C-B>"
+ call assert_equal(2, line('w0'))
+ exe "normal \<C-B>"
+ call assert_equal(1, line('w0'))
+ enew!
+ set window&
+endfunc
+
+" Test for the 'winminheight' option
+func Test_opt_winminheight()
+ only!
+ let &winheight = &lines + 4
+ call assert_fails('let &winminheight = &lines + 2', 'E36:')
+ call assert_true(&winminheight <= &lines)
+ set winminheight&
+ set winheight&
+endfunc
+
func Test_opt_winminheight_term()
" See test/functional/legacy/options_spec.lua
CheckRunVimInTerminal
@@ -704,6 +899,16 @@ func Test_opt_winminheight_term_tabs()
call delete('Xwinminheight')
endfunc
+" Test for the 'winminwidth' option
+func Test_opt_winminwidth()
+ only!
+ let &winwidth = &columns + 4
+ call assert_fails('let &winminwidth = &columns + 2', 'E36:')
+ call assert_true(&winminwidth <= &columns)
+ set winminwidth&
+ set winwidth&
+endfunc
+
" Test for setting option value containing spaces with isfname+=32
func Test_isfname_with_options()
set isfname+=32
@@ -753,4 +958,17 @@ func Test_opt_cdhome()
set cdhome&
endfunc
+func Test_switchbuf_reset()
+ set switchbuf=useopen
+ sblast
+ call assert_equal(1, winnr('$'))
+ set all&
+ " Nvim has a different default for 'switchbuf'
+ " call assert_equal('', &switchbuf)
+ call assert_equal('uselast', &switchbuf)
+ sblast
+ call assert_equal(2, winnr('$'))
+ only!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_packadd.vim b/src/nvim/testdir/test_packadd.vim
new file mode 100644
index 0000000000..fcb8b8033b
--- /dev/null
+++ b/src/nvim/testdir/test_packadd.vim
@@ -0,0 +1,361 @@
+" Tests for 'packpath' and :packadd
+
+
+func SetUp()
+ let s:topdir = getcwd() . '/Xdir'
+ exe 'set packpath=' . s:topdir
+ let s:plugdir = s:topdir . '/pack/mine/opt/mytest'
+endfunc
+
+func TearDown()
+ call delete(s:topdir, 'rf')
+endfunc
+
+func Test_packadd()
+ if !exists('s:plugdir')
+ echomsg 'when running this test manually, call SetUp() first'
+ return
+ endif
+
+ call mkdir(s:plugdir . '/plugin/also', 'p')
+ call mkdir(s:plugdir . '/ftdetect', 'p')
+ call mkdir(s:plugdir . '/after', 'p')
+ set rtp&
+ let rtp = &rtp
+ filetype on
+
+ let rtp_entries = split(rtp, ',')
+ for entry in rtp_entries
+ if entry =~? '\<after\>'
+ let first_after_entry = entry
+ break
+ endif
+ endfor
+
+ exe 'split ' . s:plugdir . '/plugin/test.vim'
+ call setline(1, 'let g:plugin_works = 42')
+ wq
+
+ exe 'split ' . s:plugdir . '/plugin/also/loaded.vim'
+ call setline(1, 'let g:plugin_also_works = 77')
+ wq
+
+ exe 'split ' . s:plugdir . '/ftdetect/test.vim'
+ call setline(1, 'let g:ftdetect_works = 17')
+ wq
+
+ packadd mytest
+
+ call assert_equal(42, g:plugin_works)
+ call assert_equal(77, g:plugin_also_works)
+ call assert_equal(17, g:ftdetect_works)
+ call assert_true(len(&rtp) > len(rtp))
+ call assert_match('/testdir/Xdir/pack/mine/opt/mytest\($\|,\)', &rtp)
+
+ let new_after = match(&rtp, '/testdir/Xdir/pack/mine/opt/mytest/after,')
+ let forwarded = substitute(first_after_entry, '\\', '[/\\\\]', 'g')
+ let old_after = match(&rtp, ',' . forwarded . '\>')
+ call assert_true(new_after > 0, 'rtp is ' . &rtp)
+ call assert_true(old_after > 0, 'match ' . forwarded . ' in ' . &rtp)
+ call assert_true(new_after < old_after, 'rtp is ' . &rtp)
+
+ " NOTE: '/.../opt/myte' forwardly matches with '/.../opt/mytest'
+ call mkdir(fnamemodify(s:plugdir, ':h') . '/myte', 'p')
+ let rtp = &rtp
+ packadd myte
+
+ " Check the path of 'myte' is added
+ call assert_true(len(&rtp) > len(rtp))
+ call assert_match('/testdir/Xdir/pack/mine/opt/myte\($\|,\)', &rtp)
+
+ " Check exception
+ call assert_fails("packadd directorynotfound", 'E919:')
+ call assert_fails("packadd", 'E471:')
+endfunc
+
+func Test_packadd_start()
+ let plugdir = s:topdir . '/pack/mine/start/other'
+ call mkdir(plugdir . '/plugin', 'p')
+ set rtp&
+ let rtp = &rtp
+ filetype on
+
+ exe 'split ' . plugdir . '/plugin/test.vim'
+ call setline(1, 'let g:plugin_works = 24')
+ wq
+
+ packadd other
+
+ call assert_equal(24, g:plugin_works)
+ call assert_true(len(&rtp) > len(rtp))
+ call assert_match('/testdir/Xdir/pack/mine/start/other\($\|,\)', &rtp)
+endfunc
+
+func Test_packadd_noload()
+ call mkdir(s:plugdir . '/plugin', 'p')
+ call mkdir(s:plugdir . '/syntax', 'p')
+ set rtp&
+ let rtp = &rtp
+
+ exe 'split ' . s:plugdir . '/plugin/test.vim'
+ call setline(1, 'let g:plugin_works = 42')
+ wq
+ let g:plugin_works = 0
+
+ packadd! mytest
+
+ call assert_true(len(&rtp) > len(rtp))
+ call assert_match('testdir/Xdir/pack/mine/opt/mytest\($\|,\)', &rtp)
+ call assert_equal(0, g:plugin_works)
+
+ " check the path is not added twice
+ let new_rtp = &rtp
+ packadd! mytest
+ call assert_equal(new_rtp, &rtp)
+endfunc
+
+func Test_packadd_symlink_dir()
+ if !has('unix')
+ return
+ endif
+ let top2_dir = s:topdir . '/Xdir2'
+ let real_dir = s:topdir . '/Xsym'
+ call mkdir(real_dir, 'p')
+ exec "silent !ln -s Xsym" top2_dir
+ let &rtp = top2_dir . ',' . top2_dir . '/after'
+ let &packpath = &rtp
+
+ let s:plugdir = top2_dir . '/pack/mine/opt/mytest'
+ call mkdir(s:plugdir . '/plugin', 'p')
+
+ exe 'split ' . s:plugdir . '/plugin/test.vim'
+ call setline(1, 'let g:plugin_works = 44')
+ wq
+ let g:plugin_works = 0
+
+ packadd mytest
+
+ " Must have been inserted in the middle, not at the end
+ call assert_match('/pack/mine/opt/mytest,', &rtp)
+ call assert_equal(44, g:plugin_works)
+
+ " No change when doing it again.
+ let rtp_before = &rtp
+ packadd mytest
+ call assert_equal(rtp_before, &rtp)
+
+ set rtp&
+ let rtp = &rtp
+ exec "silent !rm" top2_dir
+endfunc
+
+func Test_packadd_symlink_dir2()
+ if !has('unix')
+ return
+ endif
+ let top2_dir = s:topdir . '/Xdir2'
+ let real_dir = s:topdir . '/Xsym/pack'
+ call mkdir(top2_dir, 'p')
+ call mkdir(real_dir, 'p')
+ let &rtp = top2_dir . ',' . top2_dir . '/after'
+ let &packpath = &rtp
+
+ exec "silent !ln -s ../Xsym/pack" top2_dir . '/pack'
+ let s:plugdir = top2_dir . '/pack/mine/opt/mytest'
+ call mkdir(s:plugdir . '/plugin', 'p')
+
+ exe 'split ' . s:plugdir . '/plugin/test.vim'
+ call setline(1, 'let g:plugin_works = 48')
+ wq
+ let g:plugin_works = 0
+
+ packadd mytest
+
+ " Must have been inserted in the middle, not at the end
+ call assert_match('/Xdir2/pack/mine/opt/mytest,', &rtp)
+ call assert_equal(48, g:plugin_works)
+
+ " No change when doing it again.
+ let rtp_before = &rtp
+ packadd mytest
+ call assert_equal(rtp_before, &rtp)
+
+ set rtp&
+ let rtp = &rtp
+ exec "silent !rm" top2_dir . '/pack'
+ exec "silent !rmdir" top2_dir
+endfunc
+
+" Check command-line completion for 'packadd'
+func Test_packadd_completion()
+ let optdir1 = &packpath . '/pack/mine/opt'
+ let optdir2 = &packpath . '/pack/candidate/opt'
+
+ call mkdir(optdir1 . '/pluginA', 'p')
+ call mkdir(optdir1 . '/pluginC', 'p')
+ call mkdir(optdir2 . '/pluginB', 'p')
+ call mkdir(optdir2 . '/pluginC', 'p')
+
+ let li = []
+ call feedkeys(":packadd \<Tab>')\<C-B>call add(li, '\<CR>", 't')
+ call feedkeys(":packadd " . repeat("\<Tab>", 2) . "')\<C-B>call add(li, '\<CR>", 't')
+ call feedkeys(":packadd " . repeat("\<Tab>", 3) . "')\<C-B>call add(li, '\<CR>", 't')
+ call feedkeys(":packadd " . repeat("\<Tab>", 4) . "')\<C-B>call add(li, '\<CR>", 'tx')
+ call assert_equal("packadd pluginA", li[0])
+ call assert_equal("packadd pluginB", li[1])
+ call assert_equal("packadd pluginC", li[2])
+ call assert_equal("packadd ", li[3])
+endfunc
+
+func Test_packloadall()
+ " plugin foo with an autoload directory
+ let fooplugindir = &packpath . '/pack/mine/start/foo/plugin'
+ call mkdir(fooplugindir, 'p')
+ call writefile(['let g:plugin_foo_number = 1234',
+ \ 'let g:plugin_foo_auto = bbb#value',
+ \ 'let g:plugin_extra_auto = extra#value'], fooplugindir . '/bar.vim')
+ let fooautodir = &packpath . '/pack/mine/start/foo/autoload'
+ call mkdir(fooautodir, 'p')
+ call writefile(['let bar#value = 77'], fooautodir . '/bar.vim')
+
+ " plugin aaa with an autoload directory
+ let aaaplugindir = &packpath . '/pack/mine/start/aaa/plugin'
+ call mkdir(aaaplugindir, 'p')
+ call writefile(['let g:plugin_aaa_number = 333',
+ \ 'let g:plugin_aaa_auto = bar#value'], aaaplugindir . '/bbb.vim')
+ let aaaautodir = &packpath . '/pack/mine/start/aaa/autoload'
+ call mkdir(aaaautodir, 'p')
+ call writefile(['let bbb#value = 55'], aaaautodir . '/bbb.vim')
+
+ " plugin extra with only an autoload directory
+ let extraautodir = &packpath . '/pack/mine/start/extra/autoload'
+ call mkdir(extraautodir, 'p')
+ call writefile(['let extra#value = 99'], extraautodir . '/extra.vim')
+
+ packloadall
+ call assert_equal(1234, g:plugin_foo_number)
+ call assert_equal(55, g:plugin_foo_auto)
+ call assert_equal(99, g:plugin_extra_auto)
+ call assert_equal(333, g:plugin_aaa_number)
+ call assert_equal(77, g:plugin_aaa_auto)
+
+ " only works once
+ call writefile(['let g:plugin_bar_number = 4321'], fooplugindir . '/bar2.vim')
+ packloadall
+ call assert_false(exists('g:plugin_bar_number'))
+
+ " works when ! used
+ packloadall!
+ call assert_equal(4321, g:plugin_bar_number)
+endfunc
+
+func Test_helptags()
+ let docdir1 = &packpath . '/pack/mine/start/foo/doc'
+ let docdir2 = &packpath . '/pack/mine/start/bar/doc'
+ call mkdir(docdir1, 'p')
+ call mkdir(docdir2, 'p')
+ call writefile(['look here: *look-here*'], docdir1 . '/bar.txt')
+ call writefile(['look away: *look-away*'], docdir2 . '/foo.txt')
+ exe 'set rtp=' . &packpath . '/pack/mine/start/foo,' . &packpath . '/pack/mine/start/bar'
+
+ helptags ALL
+
+ let tags1 = readfile(docdir1 . '/tags')
+ call assert_match('look-here', tags1[0])
+ let tags2 = readfile(docdir2 . '/tags')
+ call assert_match('look-away', tags2[0])
+
+ call assert_fails('helptags abcxyz', 'E150:')
+endfunc
+
+func Test_colorscheme()
+ let colordirrun = &packpath . '/runtime/colors'
+ let colordirstart = &packpath . '/pack/mine/start/foo/colors'
+ let colordiropt = &packpath . '/pack/mine/opt/bar/colors'
+ call mkdir(colordirrun, 'p')
+ call mkdir(colordirstart, 'p')
+ call mkdir(colordiropt, 'p')
+ call writefile(['let g:found_one = 1'], colordirrun . '/one.vim')
+ call writefile(['let g:found_two = 1'], colordirstart . '/two.vim')
+ call writefile(['let g:found_three = 1'], colordiropt . '/three.vim')
+ exe 'set rtp=' . &packpath . '/runtime'
+
+ colorscheme one
+ call assert_equal(1, g:found_one)
+ colorscheme two
+ call assert_equal(1, g:found_two)
+ colorscheme three
+ call assert_equal(1, g:found_three)
+endfunc
+
+func Test_colorscheme_completion()
+ let colordirrun = &packpath . '/runtime/colors'
+ let colordirstart = &packpath . '/pack/mine/start/foo/colors'
+ let colordiropt = &packpath . '/pack/mine/opt/bar/colors'
+ call mkdir(colordirrun, 'p')
+ call mkdir(colordirstart, 'p')
+ call mkdir(colordiropt, 'p')
+ call writefile(['let g:found_one = 1'], colordirrun . '/one.vim')
+ call writefile(['let g:found_two = 1'], colordirstart . '/two.vim')
+ call writefile(['let g:found_three = 1'], colordiropt . '/three.vim')
+ exe 'set rtp=' . &packpath . '/runtime'
+
+ let li=[]
+ call feedkeys(":colorscheme " . repeat("\<Tab>", 1) . "')\<C-B>call add(li, '\<CR>", 't')
+ call feedkeys(":colorscheme " . repeat("\<Tab>", 2) . "')\<C-B>call add(li, '\<CR>", 't')
+ call feedkeys(":colorscheme " . repeat("\<Tab>", 3) . "')\<C-B>call add(li, '\<CR>", 't')
+ call feedkeys(":colorscheme " . repeat("\<Tab>", 4) . "')\<C-B>call add(li, '\<CR>", 'tx')
+ call assert_equal("colorscheme one", li[0])
+ call assert_equal("colorscheme three", li[1])
+ call assert_equal("colorscheme two", li[2])
+ call assert_equal("colorscheme ", li[3])
+endfunc
+
+func Test_runtime()
+ let rundir = &packpath . '/runtime/extra'
+ let startdir = &packpath . '/pack/mine/start/foo/extra'
+ let optdir = &packpath . '/pack/mine/opt/bar/extra'
+ call mkdir(rundir, 'p')
+ call mkdir(startdir, 'p')
+ call mkdir(optdir, 'p')
+ call writefile(['let g:sequence .= "run"'], rundir . '/bar.vim')
+ call writefile(['let g:sequence .= "start"'], startdir . '/bar.vim')
+ call writefile(['let g:sequence .= "foostart"'], startdir . '/foo.vim')
+ call writefile(['let g:sequence .= "opt"'], optdir . '/bar.vim')
+ call writefile(['let g:sequence .= "xxxopt"'], optdir . '/xxx.vim')
+ exe 'set rtp=' . &packpath . '/runtime'
+
+ let g:sequence = ''
+ runtime extra/bar.vim
+ call assert_equal('run', g:sequence)
+ let g:sequence = ''
+ runtime START extra/bar.vim
+ call assert_equal('start', g:sequence)
+ let g:sequence = ''
+ runtime OPT extra/bar.vim
+ call assert_equal('opt', g:sequence)
+ let g:sequence = ''
+ runtime PACK extra/bar.vim
+ call assert_equal('start', g:sequence)
+ let g:sequence = ''
+ runtime! PACK extra/bar.vim
+ call assert_equal('startopt', g:sequence)
+ let g:sequence = ''
+ runtime PACK extra/xxx.vim
+ call assert_equal('xxxopt', g:sequence)
+
+ let g:sequence = ''
+ runtime ALL extra/bar.vim
+ call assert_equal('run', g:sequence)
+ let g:sequence = ''
+ runtime ALL extra/foo.vim
+ call assert_equal('foostart', g:sequence)
+ let g:sequence = ''
+ runtime! ALL extra/xxx.vim
+ call assert_equal('xxxopt', g:sequence)
+ let g:sequence = ''
+ runtime! ALL extra/bar.vim
+ call assert_equal('runstartopt', g:sequence)
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_perl.vim b/src/nvim/testdir/test_perl.vim
index b911a982f9..558d0a5d6b 100644
--- a/src/nvim/testdir/test_perl.vim
+++ b/src/nvim/testdir/test_perl.vim
@@ -1,8 +1,8 @@
" Tests for Perl interface
-if !has('perl') || has('win32')
- finish
-endif
+source check.vim
+CheckFeature perl
+CheckNotMSWindows
" FIXME: RunTest don't see any error when Perl abort...
perl $SIG{__WARN__} = sub { die "Unexpected warnings from perl: @_" };
diff --git a/src/nvim/testdir/test_plus_arg_edit.vim b/src/nvim/testdir/test_plus_arg_edit.vim
index e31680e7b6..c52044d064 100644
--- a/src/nvim/testdir/test_plus_arg_edit.vim
+++ b/src/nvim/testdir/test_plus_arg_edit.vim
@@ -18,7 +18,7 @@ func Test_edit_bad()
e! ++enc=utf8 Xfile
call assert_equal('[?][?][???][??]', getline(1))
- e! ++enc=utf8 ++bad=_ Xfile
+ e! ++encoding=utf8 ++bad=_ Xfile
call assert_equal('[_][_][___][__]', getline(1))
e! ++enc=utf8 ++bad=drop Xfile
@@ -32,3 +32,16 @@ func Test_edit_bad()
bw!
call delete('Xfile')
endfunc
+
+" Test for ++bin and ++nobin arguments
+func Test_binary_arg()
+ new
+ edit ++bin Xfile1
+ call assert_equal(1, &binary)
+ edit ++nobin Xfile2
+ call assert_equal(0, &binary)
+ call assert_fails('edit ++binabc Xfile3', 'E474:')
+ close!
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_popup.vim b/src/nvim/testdir/test_popup.vim
index eb367cfe5c..a5e4be49f4 100644
--- a/src/nvim/testdir/test_popup.vim
+++ b/src/nvim/testdir/test_popup.vim
@@ -325,6 +325,21 @@ func Test_compl_vim_cmds_after_register_expr()
bwipe!
endfunc
+func Test_compl_ignore_mappings()
+ call setline(1, ['foo', 'bar', 'baz', 'foobar'])
+ inoremap <C-P> (C-P)
+ inoremap <C-N> (C-N)
+ normal! G
+ call feedkeys("o\<C-X>\<C-N>\<C-N>\<C-N>\<C-P>\<C-N>\<C-Y>", 'tx')
+ call assert_equal('baz', getline('.'))
+ " Also test with unsimplified keys
+ call feedkeys("o\<C-X>\<*C-N>\<*C-N>\<*C-N>\<*C-P>\<*C-N>\<C-Y>", 'tx')
+ call assert_equal('baz', getline('.'))
+ iunmap <C-P>
+ iunmap <C-N>
+ bwipe!
+endfunc
+
func DummyCompleteOne(findstart, base)
if a:findstart
return 0
@@ -334,19 +349,17 @@ func DummyCompleteOne(findstart, base)
endif
endfunc
-" Test that nothing happens if the 'completefunc' opens
-" a new window (no completion, no crash)
+" Test that nothing happens if the 'completefunc' tries to open
+" a new window (fails to open window, continues)
func Test_completefunc_opens_new_window_one()
new
let winid = win_getid()
setlocal completefunc=DummyCompleteOne
call setline(1, 'one')
/^one
- call assert_fails('call feedkeys("A\<C-X>\<C-U>\<C-N>\<Esc>", "x")', 'E839:')
- call assert_notequal(winid, win_getid())
- q!
+ call assert_fails('call feedkeys("A\<C-X>\<C-U>\<C-N>\<Esc>", "x")', 'E565:')
call assert_equal(winid, win_getid())
- call assert_equal('', getline(1))
+ call assert_equal('oneDEF', getline(1))
q!
endfunc
@@ -369,11 +382,11 @@ func Test_completefunc_opens_new_window_two()
setlocal completefunc=DummyCompleteTwo
call setline(1, 'two')
/^two
- call assert_fails('call feedkeys("A\<C-X>\<C-U>\<C-N>\<Esc>", "x")', 'E764:')
- call assert_notequal(winid, win_getid())
- q!
+ call assert_fails('call feedkeys("A\<C-X>\<C-U>\<C-N>\<Esc>", "x")', 'E565:')
call assert_equal(winid, win_getid())
- call assert_equal('two', getline(1))
+ " v8.2.1919 hasn't been ported yet
+ " call assert_equal('twodef', getline(1))
+ call assert_equal('twoDEF', getline(1))
q!
endfunc
@@ -642,8 +655,8 @@ func Test_complete_func_mess()
set completefunc=MessComplete
new
call setline(1, 'Ju')
- call feedkeys("A\<c-x>\<c-u>/\<esc>", 'tx')
- call assert_equal('Oct/Oct', getline(1))
+ call assert_fails('call feedkeys("A\<c-x>\<c-u>/\<esc>", "tx")', 'E565:')
+ call assert_equal('Jan/', getline(1))
bwipe!
set completefunc=
endfunc
@@ -898,7 +911,7 @@ func Test_popup_complete_backwards_ctrl_p()
bwipe!
endfunc
-fun! Test_complete_o_tab()
+func Test_complete_o_tab()
CheckFunction test_override
let s:o_char_pressed = 0
@@ -907,7 +920,7 @@ fun! Test_complete_o_tab()
let s:o_char_pressed = 0
call feedkeys("\<c-x>\<c-n>", 'i')
endif
- endf
+ endfunc
set completeopt=menu,noselect
new
@@ -926,7 +939,21 @@ fun! Test_complete_o_tab()
bwipe!
set completeopt&
delfunc s:act_on_text_changed
-endf
+endfunc
+
+func Test_menu_only_exists_in_terminal()
+ if !exists(':tlmenu') || has('gui_running')
+ return
+ endif
+ tlnoremenu &Edit.&Paste<Tab>"+gP <C-W>"+
+ aunmenu *
+ try
+ popup Edit
+ call assert_false(1, 'command should have failed')
+ catch
+ call assert_exception('E328:')
+ endtry
+endfunc
func Test_popup_complete_info_01()
new
diff --git a/src/nvim/testdir/test_profile.vim b/src/nvim/testdir/test_profile.vim
index 4b0097617e..fdb6f13e2b 100644
--- a/src/nvim/testdir/test_profile.vim
+++ b/src/nvim/testdir/test_profile.vim
@@ -1,8 +1,9 @@
" Test Vim profiler
-if !has('profile')
- finish
-endif
+source check.vim
+CheckFeature profile
+
+source shared.vim
source screendump.vim
func Test_profile_func()
@@ -37,7 +38,7 @@ func Test_profile_func()
[CODE]
call writefile(lines, 'Xprofile_func.vim')
- call system(v:progpath
+ call system(GetVimCommand()
\ . ' -es --clean'
\ . ' -c "so Xprofile_func.vim"'
\ . ' -c "qall!"')
@@ -124,8 +125,8 @@ func Test_profile_func_with_ifelse()
[CODE]
call writefile(lines, 'Xprofile_func.vim')
- call system(v:progpath
- \ . ' -es -u NONE -U NONE -i NONE --noplugin'
+ call system(GetVimCommand()
+ \ . ' -es -i NONE --noplugin'
\ . ' -c "profile start Xprofile_func.log"'
\ . ' -c "profile func Foo*"'
\ . ' -c "so Xprofile_func.vim"'
@@ -237,8 +238,8 @@ func Test_profile_func_with_trycatch()
[CODE]
call writefile(lines, 'Xprofile_func.vim')
- call system(v:progpath
- \ . ' -es -u NONE -U NONE -i NONE --noplugin'
+ call system(GetVimCommand()
+ \ . ' -es -i NONE --noplugin'
\ . ' -c "profile start Xprofile_func.log"'
\ . ' -c "profile func Foo*"'
\ . ' -c "so Xprofile_func.vim"'
@@ -324,8 +325,8 @@ func Test_profile_file()
[CODE]
call writefile(lines, 'Xprofile_file.vim')
- call system(v:progpath
- \ . ' -es --clean'
+ call system(GetVimCommandClean()
+ \ . ' -es'
\ . ' -c "profile start Xprofile_file.log"'
\ . ' -c "profile file Xprofile_file.vim"'
\ . ' -c "so Xprofile_file.vim"'
@@ -369,8 +370,8 @@ func Test_profile_file_with_cont()
\ ]
call writefile(lines, 'Xprofile_file.vim')
- call system(v:progpath
- \ . ' -es -u NONE -U NONE -i NONE --noplugin'
+ call system(GetVimCommandClean()
+ \ . ' -es'
\ . ' -c "profile start Xprofile_file.log"'
\ . ' -c "profile file Xprofile_file.vim"'
\ . ' -c "so Xprofile_file.vim"'
@@ -427,7 +428,7 @@ func Test_profile_truncate_mbyte()
\ ]
call writefile(lines, 'Xprofile_file.vim')
- call system(v:progpath
+ call system(GetVimCommandClean()
\ . ' -es --cmd "set enc=utf-8"'
\ . ' -c "profile start Xprofile_file.log"'
\ . ' -c "profile file Xprofile_file.vim"'
@@ -474,7 +475,7 @@ func Test_profdel_func()
call Foo3()
[CODE]
call writefile(lines, 'Xprofile_file.vim')
- call system(v:progpath . ' -es --clean -c "so Xprofile_file.vim" -c q')
+ call system(GetVimCommandClean() . ' -es -c "so Xprofile_file.vim" -c q')
call assert_equal(0, v:shell_error)
let lines = readfile('Xprofile_file.log')
@@ -509,7 +510,7 @@ func Test_profdel_star()
call Foo()
[CODE]
call writefile(lines, 'Xprofile_file.vim')
- call system(v:progpath . ' -es --clean -c "so Xprofile_file.vim" -c q')
+ call system(GetVimCommandClean() . ' -es -c "so Xprofile_file.vim" -c q')
call assert_equal(0, v:shell_error)
let lines = readfile('Xprofile_file.log')
diff --git a/src/nvim/testdir/test_put.vim b/src/nvim/testdir/test_put.vim
index 440717eaa8..97af3699a8 100644
--- a/src/nvim/testdir/test_put.vim
+++ b/src/nvim/testdir/test_put.vim
@@ -1,5 +1,7 @@
" Tests for put commands, e.g. ":put", "p", "gp", "P", "gP", etc.
+source check.vim
+
func Test_put_block()
new
call feedkeys("i\<C-V>u2500\<CR>x\<ESC>", 'x')
@@ -112,6 +114,93 @@ func Test_put_p_indent_visual()
bwipe!
endfunc
+" Test for deleting all the contents of a buffer with a put
+func Test_put_visual_delete_all_lines()
+ new
+ call setline(1, ['one', 'two', 'three'])
+ let @r = ''
+ normal! VG"rgp
+ call assert_equal(1, line('$'))
+ close!
+endfunc
+
+func Test_gp_with_count_leaves_cursor_at_end()
+ new
+ call setline(1, '<---->')
+ call setreg('@', "foo\nbar", 'c')
+ normal 1G3|3gp
+ call assert_equal([0, 4, 4, 0], getpos("."))
+ call assert_equal(['<--foo', 'barfoo', 'barfoo', 'bar-->'], getline(1, '$'))
+ call assert_equal([0, 4, 3, 0], getpos("']"))
+
+ bwipe!
+endfunc
+
+func Test_p_with_count_leaves_mark_at_end()
+ new
+ call setline(1, '<---->')
+ call setreg('@', "start\nend", 'c')
+ normal 1G3|3p
+ call assert_equal([0, 1, 4, 0], getpos("."))
+ call assert_equal(['<--start', 'endstart', 'endstart', 'end-->'], getline(1, '$'))
+ call assert_equal([0, 4, 3, 0], getpos("']"))
+
+ bwipe!
+endfunc
+
+func Test_very_large_count()
+ new
+ " total put-length (21474837 * 100) brings 32 bit int overflow
+ let @" = repeat('x', 100)
+ call assert_fails('norm 21474837p', 'E1240:')
+ bwipe!
+endfunc
+
+func Test_very_large_count_64bit()
+ throw 'Skipped: v:sizeoflong is N/A' " use legacy/put_spec.lua instead
+
+ if v:sizeoflong < 8
+ throw 'Skipped: only works with 64 bit long ints'
+ endif
+
+ new
+ let @" = repeat('x', 100)
+ call assert_fails('norm 999999999p', 'E1240:')
+ bwipe!
+endfunc
+
+func Test_very_large_count_block()
+ new
+ " total put-length (21474837 * 100) brings 32 bit int overflow
+ call setline(1, repeat('x', 100))
+ exe "norm \<C-V>99ly"
+ call assert_fails('norm 21474837p', 'E1240:')
+ bwipe!
+endfunc
+
+func Test_very_large_count_block_64bit()
+ throw 'Skipped: v:sizeoflong is N/A' " use legacy/put_spec.lua instead
+
+ if v:sizeoflong < 8
+ throw 'Skipped: only works with 64 bit long ints'
+ endif
+
+ new
+ call setline(1, repeat('x', 100))
+ exe "norm \<C-V>$y"
+ call assert_fails('norm 999999999p', 'E1240:')
+ bwipe!
+endfunc
+
+func Test_put_above_first_line()
+ new
+ let @" = 'text'
+ silent! normal 0o00
+ 0put
+ call assert_equal('text', getline(1))
+ bwipe!
+endfunc
+
func Test_multibyte_op_end_mark()
new
call setline(1, 'тест')
@@ -124,3 +213,27 @@ func Test_multibyte_op_end_mark()
call assert_equal([0, 2, 7, 0], getpos("']"))
bwipe!
endfunc
+
+" this was putting a mark before the start of a line
+func Test_put_empty_register()
+ new
+ norm yy
+ norm [Pi00ggv)s0
+ sil! norm [P
+ bwipe!
+endfunc
+
+" this was putting the end mark after the end of the line
+func Test_put_visual_mode()
+ edit! SomeNewBuffer
+ set selection=exclusive
+ exe "norm o\t"
+ m0
+ sil! norm  p p
+
+ bwipe!
+ set selection&
+endfunc
+
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_python2.vim b/src/nvim/testdir/test_python2.vim
index ae8bc57c7f..745b7da086 100644
--- a/src/nvim/testdir/test_python2.vim
+++ b/src/nvim/testdir/test_python2.vim
@@ -1,9 +1,8 @@
" Test for python 2 commands.
" TODO: move tests from test86.in here.
-if !has('python')
- finish
-endif
+source check.vim
+CheckFeature python
func Test_pydo()
" Check deleting lines does not trigger ml_get error.
diff --git a/src/nvim/testdir/test_python3.vim b/src/nvim/testdir/test_python3.vim
index 54da3d2eba..69f5f6dcc0 100644
--- a/src/nvim/testdir/test_python3.vim
+++ b/src/nvim/testdir/test_python3.vim
@@ -1,9 +1,8 @@
" Test for python 3 commands.
" TODO: move tests from test87.in here.
-if !has('python3')
- finish
-endif
+source check.vim
+CheckFeature python3
func Test_py3do()
" Check deleting lines does not trigger an ml_get error.
@@ -69,6 +68,7 @@ func Test_vim_function()
endfunc
func Test_skipped_python3_command_does_not_affect_pyxversion()
+ throw 'skipped: Nvim hardcodes pyxversion=3'
set pyxversion=0
if 0
python3 import vim
diff --git a/src/nvim/testdir/test_pyx2.vim b/src/nvim/testdir/test_pyx2.vim
index b6ed80f842..eee825fa9b 100644
--- a/src/nvim/testdir/test_pyx2.vim
+++ b/src/nvim/testdir/test_pyx2.vim
@@ -1,9 +1,8 @@
" Test for pyx* commands and functions with Python 2.
+source check.vim
+CheckFeature python
set pyx=2
-if !has('python')
- finish
-endif
let s:py2pattern = '^2\.[0-7]\.\d\+'
let s:py3pattern = '^3\.\d\+\.\d\+'
diff --git a/src/nvim/testdir/test_pyx3.vim b/src/nvim/testdir/test_pyx3.vim
index 2044af3abe..db39f5134a 100644
--- a/src/nvim/testdir/test_pyx3.vim
+++ b/src/nvim/testdir/test_pyx3.vim
@@ -1,9 +1,8 @@
" Test for pyx* commands and functions with Python 3.
set pyx=3
-if !has('python3')
- finish
-endif
+source check.vim
+CheckFeature python3
let s:py2pattern = '^2\.[0-7]\.\d\+'
let s:py3pattern = '^3\.\d\+\.\d\+'
diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim
index 6db679c5f9..ddd4229f17 100644
--- a/src/nvim/testdir/test_quickfix.vim
+++ b/src/nvim/testdir/test_quickfix.vim
@@ -1,4 +1,4 @@
-" Test for the quickfix commands.
+" Test for the quickfix feature.
source check.vim
CheckFeature quickfix
@@ -32,7 +32,7 @@ func s:setup_commands(cchar)
command! -count -nargs=* -bang Xnfile <mods><count>cnfile<bang> <args>
command! -nargs=* -bang Xpfile <mods>cpfile<bang> <args>
command! -nargs=* Xexpr <mods>cexpr <args>
- command! -count -nargs=* Xvimgrep <mods> <count>vimgrep <args>
+ command! -count=999 -nargs=* Xvimgrep <mods> <count>vimgrep <args>
command! -nargs=* Xvimgrepadd <mods> vimgrepadd <args>
command! -nargs=* Xgrep <mods> grep <args>
command! -nargs=* Xgrepadd <mods> grepadd <args>
@@ -69,7 +69,7 @@ func s:setup_commands(cchar)
command! -count -nargs=* -bang Xnfile <mods><count>lnfile<bang> <args>
command! -nargs=* -bang Xpfile <mods>lpfile<bang> <args>
command! -nargs=* Xexpr <mods>lexpr <args>
- command! -count -nargs=* Xvimgrep <mods> <count>lvimgrep <args>
+ command! -count=999 -nargs=* Xvimgrep <mods> <count>lvimgrep <args>
command! -nargs=* Xvimgrepadd <mods> lvimgrepadd <args>
command! -nargs=* Xgrep <mods> lgrep <args>
command! -nargs=* Xgrepadd <mods> lgrepadd <args>
@@ -169,8 +169,8 @@ func XlistTests(cchar)
\ {'lnum':30,'col':15,'type':'W','filename':'Data/Text.hs','text':'FileWarning','nr':33,'valid':v:true}])
let l = split(execute('Xlist', ""), "\n")
call assert_equal([' 1 Data.Text:10 col 5 warning 11: ModuleWarning',
- \ ' 2 Data.Text:20 col 10 warning 22: ModuleWarning',
- \ ' 3 Data/Text.hs:30 col 15 warning 33: FileWarning'], l)
+ \ ' 2 Data.Text:20 col 10 warning 22: ModuleWarning',
+ \ ' 3 Data/Text.hs:30 col 15 warning 33: FileWarning'], l)
" For help entries in the quickfix list, only the filename without directory
" should be displayed
@@ -301,6 +301,23 @@ func XwindowTests(cchar)
call assert_equal(12, winwidth(0))
Xclose
+ " Horizontally or vertically splitting the quickfix window should create a
+ " normal window/buffer
+ Xopen
+ wincmd s
+ call assert_equal(0, getwininfo(win_getid())[0].quickfix)
+ call assert_equal(0, getwininfo(win_getid())[0].loclist)
+ call assert_notequal('quickfix', &buftype)
+ close
+ Xopen
+ wincmd v
+ call assert_equal(0, getwininfo(win_getid())[0].quickfix)
+ call assert_equal(0, getwininfo(win_getid())[0].loclist)
+ call assert_notequal('quickfix', &buftype)
+ close
+ Xopen
+ Xclose
+
if a:cchar == 'c'
" Opening the quickfix window in multiple tab pages should reuse the
" quickfix buffer
@@ -499,13 +516,14 @@ func Xtest_browse(cchar)
\ 'RegularLine2']
Xfirst
+ call assert_fails('-5Xcc', 'E16:')
call assert_fails('Xprev', 'E553')
call assert_fails('Xpfile', 'E553')
Xnfile
- call assert_equal('Xqftestfile2', bufname('%'))
+ call assert_equal('Xqftestfile2', @%)
call assert_equal(10, line('.'))
Xpfile
- call assert_equal('Xqftestfile1', bufname('%'))
+ call assert_equal('Xqftestfile1', @%)
call assert_equal(6, line('.'))
5Xcc
call assert_equal(5, g:Xgetlist({'idx':0}).idx)
@@ -521,7 +539,7 @@ func Xtest_browse(cchar)
call assert_equal(6, g:Xgetlist({'idx':0}).idx)
Xlast
Xprev
- call assert_equal('Xqftestfile2', bufname('%'))
+ call assert_equal('Xqftestfile2', @%)
call assert_equal(11, line('.'))
call assert_fails('Xnext', 'E553')
call assert_fails('Xnfile', 'E553')
@@ -534,14 +552,14 @@ func Xtest_browse(cchar)
endif
call assert_equal(6, g:Xgetlist({'idx':0}).idx)
Xrewind
- call assert_equal('Xqftestfile1', bufname('%'))
+ call assert_equal('Xqftestfile1', @%)
call assert_equal(5, line('.'))
10Xnext
- call assert_equal('Xqftestfile2', bufname('%'))
+ call assert_equal('Xqftestfile2', @%)
call assert_equal(11, line('.'))
10Xprev
- call assert_equal('Xqftestfile1', bufname('%'))
+ call assert_equal('Xqftestfile1', @%)
call assert_equal(5, line('.'))
" Jumping to an error from the error window using cc command
@@ -552,14 +570,23 @@ func Xtest_browse(cchar)
Xopen
10Xcc
call assert_equal(11, line('.'))
- call assert_equal('Xqftestfile2', bufname('%'))
+ call assert_equal('Xqftestfile2', @%)
+ Xopen
+ call cursor(2, 1)
+ if a:cchar == 'c'
+ .cc
+ else
+ .ll
+ endif
+ call assert_equal(6, line('.'))
+ call assert_equal('Xqftestfile1', @%)
" Jumping to an error from the error window (when only the error window is
" present)
Xopen | only
Xlast 1
call assert_equal(5, line('.'))
- call assert_equal('Xqftestfile1', bufname('%'))
+ call assert_equal('Xqftestfile1', @%)
Xexpr ""
call assert_fails('Xnext', 'E42:')
@@ -796,101 +823,102 @@ func ReadTestProtocol(name)
endfunc
func Test_locationlist()
- enew
+ enew
- augroup testgroup
- au!
- autocmd BufReadCmd test://* call ReadTestProtocol(expand("<amatch>"))
- augroup END
+ augroup testgroup
+ au!
+ autocmd BufReadCmd test://* call ReadTestProtocol(expand("<amatch>"))
+ augroup END
- let words = [ "foo", "bar", "baz", "quux", "shmoo", "spam", "eggs" ]
+ let words = [ "foo", "bar", "baz", "quux", "shmoo", "spam", "eggs" ]
- let qflist = []
- for word in words
- call add(qflist, {'filename': 'test://' . word . '.txt', 'text': 'file ' . word . '.txt', })
- " NOTE: problem 1:
- " intentionally not setting 'lnum' so that the quickfix entries are not
- " valid
- eval qflist->setloclist(0, ' ')
- endfor
+ let qflist = []
+ for word in words
+ call add(qflist, {'filename': 'test://' . word . '.txt', 'text': 'file ' . word . '.txt', })
+ " NOTE: problem 1:
+ " intentionally not setting 'lnum' so that the quickfix entries are not
+ " valid
+ eval qflist->setloclist(0, ' ')
+ endfor
- " Test A
- lrewind
- enew
- lopen
- 4lnext
- vert split
- wincmd L
- lopen
- wincmd p
- lnext
- let fileName = expand("%")
- wincmd p
- let locationListFileName = substitute(getline(line('.')), '\([^|]*\)|.*', '\1', '')
- let fileName = substitute(fileName, '\\', '/', 'g')
- let locationListFileName = substitute(locationListFileName, '\\', '/', 'g')
- call assert_equal("test://bar.txt", fileName)
- call assert_equal("test://bar.txt", locationListFileName)
+ " Test A
+ lrewind
+ enew
+ lopen
+ 4lnext
+ vert split
+ wincmd L
+ lopen
+ wincmd p
+ lnext
+ let fileName = expand("%")
+ wincmd p
+ let locationListFileName = substitute(getline(line('.')), '\([^|]*\)|.*', '\1', '')
+ let fileName = substitute(fileName, '\\', '/', 'g')
+ let locationListFileName = substitute(locationListFileName, '\\', '/', 'g')
+ call assert_equal("test://bar.txt", fileName)
+ call assert_equal("test://bar.txt", locationListFileName)
- wincmd n | only
+ wincmd n | only
- " Test B:
- lrewind
- lopen
- 2
- exe "normal \<CR>"
- wincmd p
- 3
- exe "normal \<CR>"
- wincmd p
- 4
- exe "normal \<CR>"
- call assert_equal(2, winnr('$'))
- wincmd n | only
+ " Test B:
+ lrewind
+ lopen
+ 2
+ exe "normal \<CR>"
+ wincmd p
+ 3
+ exe "normal \<CR>"
+ wincmd p
+ 4
+ exe "normal \<CR>"
+ call assert_equal(2, winnr('$'))
+ wincmd n | only
- " Test C:
- lrewind
- lopen
- " Let's move the location list window to the top to check whether it (the
- " first window found) will be reused when we try to open new windows:
- wincmd K
- 2
- exe "normal \<CR>"
- wincmd p
- 3
- exe "normal \<CR>"
- wincmd p
- 4
- exe "normal \<CR>"
- 1wincmd w
- call assert_equal('quickfix', &buftype)
- 2wincmd w
- let bufferName = expand("%")
- let bufferName = substitute(bufferName, '\\', '/', 'g')
- call assert_equal('test://quux.txt', bufferName)
+ " Test C:
+ lrewind
+ lopen
+ " Let's move the location list window to the top to check whether it (the
+ " first window found) will be reused when we try to open new windows:
+ wincmd K
+ 2
+ exe "normal \<CR>"
+ wincmd p
+ 3
+ exe "normal \<CR>"
+ wincmd p
+ 4
+ exe "normal \<CR>"
+ 1wincmd w
+ call assert_equal('quickfix', &buftype)
+ 2wincmd w
+ let bufferName = expand("%")
+ let bufferName = substitute(bufferName, '\\', '/', 'g')
+ call assert_equal('test://quux.txt', bufferName)
- wincmd n | only
+ wincmd n | only
- augroup! testgroup
+ augroup! testgroup
endfunc
func Test_locationlist_curwin_was_closed()
- augroup testgroup
- au!
- autocmd BufReadCmd test_curwin.txt call R(expand("<amatch>"))
- augroup END
+ augroup testgroup
+ au!
+ autocmd BufReadCmd test_curwin.txt call R(expand("<amatch>"))
+ augroup END
- func! R(n)
- quit
- endfunc
+ func! R(n)
+ quit
+ endfunc
- new
- let q = []
- call add(q, {'filename': 'test_curwin.txt' })
- call setloclist(0, q)
- call assert_fails('lrewind', 'E924:')
+ new
+ let q = []
+ call add(q, {'filename': 'test_curwin.txt' })
+ call setloclist(0, q)
+ call assert_fails('lrewind', 'E924:')
- augroup! testgroup
+ augroup! testgroup
+ delfunc R
endfunc
func Test_locationlist_cross_tab_jump()
@@ -1037,21 +1065,20 @@ func s:dir_stack_tests(cchar)
let save_efm=&efm
set efm=%DEntering\ dir\ '%f',%f:%l:%m,%XLeaving\ dir\ '%f'
- let lines =<< trim [DATA]
- Entering dir 'dir1/a'
- habits2.txt:1:Nine Healthy Habits
- Entering dir 'b'
- habits3.txt:2:0 Hours of television
- habits2.txt:7:5 Small meals
- Entering dir 'dir1/c'
- habits4.txt:3:1 Hour of exercise
- Leaving dir 'dir1/c'
- Leaving dir 'dir1/a'
- habits1.txt:4:2 Liters of water
- Entering dir 'dir2'
- habits5.txt:5:3 Cups of hot green tea
- Leaving dir 'dir2
- [DATA]
+ let lines = ["Entering dir 'dir1/a'",
+ \ 'habits2.txt:1:Nine Healthy Habits',
+ \ "Entering dir 'b'",
+ \ 'habits3.txt:2:0 Hours of television',
+ \ 'habits2.txt:7:5 Small meals',
+ \ "Entering dir 'dir1/c'",
+ \ 'habits4.txt:3:1 Hour of exercise',
+ \ "Leaving dir 'dir1/c'",
+ \ "Leaving dir 'dir1/a'",
+ \ 'habits1.txt:4:2 Liters of water',
+ \ "Entering dir 'dir2'",
+ \ 'habits5.txt:5:3 Cups of hot green tea',
+ \ "Leaving dir 'dir2'"
+ \]
Xexpr ""
for l in lines
@@ -1060,17 +1087,17 @@ func s:dir_stack_tests(cchar)
let qf = g:Xgetlist()
- call assert_equal(expand('dir1/a/habits2.txt'), bufname(qf[1].bufnr))
+ call assert_equal('dir1/a/habits2.txt', bufname(qf[1].bufnr))
call assert_equal(1, qf[1].lnum)
- call assert_equal(expand('dir1/a/b/habits3.txt'), bufname(qf[3].bufnr))
+ call assert_equal('dir1/a/b/habits3.txt', bufname(qf[3].bufnr))
call assert_equal(2, qf[3].lnum)
- call assert_equal(expand('dir1/a/habits2.txt'), bufname(qf[4].bufnr))
+ call assert_equal('dir1/a/habits2.txt', bufname(qf[4].bufnr))
call assert_equal(7, qf[4].lnum)
- call assert_equal(expand('dir1/c/habits4.txt'), bufname(qf[6].bufnr))
+ call assert_equal('dir1/c/habits4.txt', bufname(qf[6].bufnr))
call assert_equal(3, qf[6].lnum)
call assert_equal('habits1.txt', bufname(qf[9].bufnr))
call assert_equal(4, qf[9].lnum)
- call assert_equal(expand('dir2/habits5.txt'), bufname(qf[11].bufnr))
+ call assert_equal('dir2/habits5.txt', bufname(qf[11].bufnr))
call assert_equal(5, qf[11].lnum)
let &efm=save_efm
@@ -1085,19 +1112,18 @@ func Test_efm_dirstack()
call mkdir('dir1/c')
call mkdir('dir2')
- let lines =<< trim [DATA]
- Nine Healthy Habits,
- 0 Hours of television,
- 1 Hour of exercise,
- 2 Liters of water,
- 3 Cups of hot green tea,
- 4 Short mental breaks,
- 5 Small meals,
- 6 AM wake up time,
- 7 Minutes of laughter,
- 8 Hours of sleep (at least),
- 9 PM end of the day and off to bed
- [DATA]
+ let lines = ["Nine Healthy Habits",
+ \ "0 Hours of television",
+ \ "1 Hour of exercise",
+ \ "2 Liters of water",
+ \ "3 Cups of hot green tea",
+ \ "4 Short mental breaks",
+ \ "5 Small meals",
+ \ "6 AM wake up time",
+ \ "7 Minutes of laughter",
+ \ "8 Hours of sleep (at least)",
+ \ "9 PM end of the day and off to bed"
+ \ ]
call writefile(lines, 'habits1.txt')
call writefile(lines, 'dir1/a/habits2.txt')
@@ -1219,6 +1245,7 @@ func Test_efm2()
(67,3) warning: 's' already defined
--
[DATA]
+
set efm=%+P[%f]%r,(%l\\,%c)%*[\ ]%t%*[^:]:\ %m,%+Q--%r
" To exercise the push/pop file functionality in quickfix, the test files
" need to be created.
@@ -1279,27 +1306,28 @@ func Test_efm2()
" Test for %A, %C and other formats
let lines =<< trim [DATA]
- ==============================================================
- FAIL: testGetTypeIdCachesResult (dbfacadeTest.DjsDBFacadeTest)
- --------------------------------------------------------------
- Traceback (most recent call last):
- File "unittests/dbfacadeTest.py", line 89, in testFoo
- self.assertEquals(34, dtid)
- File "/usr/lib/python2.2/unittest.py", line 286, in
- failUnlessEqual
- raise self.failureException, \\
- W:AssertionError: 34 != 33
-
- --------------------------------------------------------------
- Ran 27 tests in 0.063s
+ ==============================================================
+ FAIL: testGetTypeIdCachesResult (dbfacadeTest.DjsDBFacadeTest)
+ --------------------------------------------------------------
+ Traceback (most recent call last):
+ File "unittests/dbfacadeTest.py", line 89, in testFoo
+ self.assertEquals(34, dtid)
+ File "/usr/lib/python2.2/unittest.py", line 286, in
+ failUnlessEqual
+ raise self.failureException, \\
+ W:AssertionError: 34 != 33
+
+ --------------------------------------------------------------
+ Ran 27 tests in 0.063s
[DATA]
+
set efm=%C\ %.%#,%A\ \ File\ \"%f\"\\,\ line\ %l%.%#,%Z%[%^\ ]%\\@=%t:%m
cgetexpr lines
let l = getqflist()
call assert_equal(8, len(l))
call assert_equal(89, l[4].lnum)
call assert_equal(1, l[4].valid)
- call assert_equal(expand('unittests/dbfacadeTest.py'), bufname(l[4].bufnr))
+ call assert_equal('unittests/dbfacadeTest.py', bufname(l[4].bufnr))
call assert_equal('W', l[4].type)
" Test for %o
@@ -1384,6 +1412,29 @@ func Test_efm_error_type()
let &efm = save_efm
endfunc
+" Test for end_lnum ('%e') and end_col ('%k') fields in 'efm'
+func Test_efm_end_lnum_col()
+ let save_efm = &efm
+
+ " single line
+ set efm=%f:%l-%e:%c-%k:%t:%m
+ cexpr ["Xfile1:10-20:1-2:E:msg1", "Xfile1:20-30:2-3:W:msg2",]
+ let output = split(execute('clist'), "\n")
+ call assert_equal([
+ \ ' 1 Xfile1:10-20 col 1-2 error: msg1',
+ \ ' 2 Xfile1:20-30 col 2-3 warning: msg2'], output)
+
+ " multiple lines
+ set efm=%A%n)%m,%Z%f:%l-%e:%c-%k
+ cexpr ["1)msg1", "Xfile1:14-24:1-2",
+ \ "2)msg2", "Xfile1:24-34:3-4"]
+ let output = split(execute('clist'), "\n")
+ call assert_equal([
+ \ ' 1 Xfile1:14-24 col 1-2 error 1: msg1',
+ \ ' 2 Xfile1:24-34 col 3-4 error 2: msg2'], output)
+ let &efm = save_efm
+endfunc
+
func XquickfixChangedByAutocmd(cchar)
call s:setup_commands(a:cchar)
if a:cchar == 'c'
@@ -1632,7 +1683,7 @@ func XquickfixSetListWithAct(cchar)
\ {'filename': 'fnameD', 'text': 'D'},
\ {'filename': 'fnameE', 'text': 'E'}]
- " {action} is unspecified. Same as specifing ' '.
+ " {action} is unspecified. Same as specifying ' '.
new | only
silent! Xnewer 99
call g:Xsetlist(list1)
@@ -1910,10 +1961,11 @@ func Test_switchbuf()
copen | only
cfirst
call assert_equal(1, tabpagenr())
- call assert_equal('Xqftestfile1', bufname(''))
+ call assert_equal('Xqftestfile1', @%)
" If opening a file changes 'switchbuf', then the new value should be
" retained.
+ set modeline&vim
call writefile(["vim: switchbuf=split"], 'Xqftestfile1')
enew | only
set switchbuf&vim
@@ -1996,6 +2048,7 @@ func s:test_xgrep(cchar)
enew
set makeef=Temp_File_##
silent Xgrepadd GrepAdd_Test_Text: test_quickfix.vim
+ call assert_true(len(g:Xgetlist()) == 9)
" Try with 'grepprg' set to 'internal'
set grepprg=internal
@@ -2004,12 +2057,12 @@ func s:test_xgrep(cchar)
call assert_true(len(g:Xgetlist()) == 9)
set grepprg&vim
- call writefile(['Vim'], 'XtestTempFile')
- set makeef=XtestTempFile
- silent Xgrep Grep_Test_Text: test_quickfix.vim
- call assert_equal(5, len(g:Xgetlist()))
- call assert_false(filereadable('XtestTempFile'))
- set makeef&vim
+ call writefile(['Vim'], 'XtestTempFile')
+ set makeef=XtestTempFile
+ silent Xgrep Grep_Test_Text: test_quickfix.vim
+ call assert_equal(5, len(g:Xgetlist()))
+ call assert_false(filereadable('XtestTempFile'))
+ set makeef&vim
endfunc
func Test_grep()
@@ -2048,11 +2101,11 @@ func Test_two_windows()
laddexpr 'one.txt:3:one one one'
let loc_one = getloclist(one_id)
- call assert_equal(expand('Xone/a/one.txt'), bufname(loc_one[1].bufnr))
+ call assert_equal('Xone/a/one.txt', bufname(loc_one[1].bufnr))
call assert_equal(3, loc_one[1].lnum)
let loc_two = getloclist(two_id)
- call assert_equal(expand('Xtwo/a/two.txt'), bufname(loc_two[1].bufnr))
+ call assert_equal('Xtwo/a/two.txt', bufname(loc_two[1].bufnr))
call assert_equal(5, loc_two[1].lnum)
call win_gotoid(one_id)
@@ -2682,7 +2735,7 @@ func Test_cwindow_jump()
" Open a new window and create a location list
" Open the location list window and close the other window
" Jump to an entry.
- " Should create a new window and jump to the entry. The scrtach buffer
+ " Should create a new window and jump to the entry. The scratch buffer
" should not be used.
enew | only
set buftype=nofile
@@ -2715,7 +2768,26 @@ func Test_cwindow_jump()
call assert_true(winnr('$') == 2)
call assert_true(winnr() == 1)
- " Jumping to a file from the location list window should find a usuable
+ " open the quickfix buffer in two windows and jump to an entry. Should open
+ " the file in the first quickfix window.
+ enew | only
+ copen
+ let bnum = bufnr('')
+ exe 'sbuffer ' . bnum
+ wincmd b
+ cfirst
+ call assert_equal(2, winnr())
+ call assert_equal('F1', @%)
+ enew | only
+ exe 'sb' bnum
+ exe 'botright sb' bnum
+ wincmd t
+ clast
+ call assert_equal(2, winnr())
+ call assert_equal('quickfix', getwinvar(1, '&buftype'))
+ call assert_equal('quickfix', getwinvar(3, '&buftype'))
+
+ " Jumping to a file from the location list window should find a usable
" window by wrapping around the window list.
enew | only
call setloclist(0, [], 'f')
@@ -2795,7 +2867,7 @@ func XvimgrepTests(cchar)
edit +3 Xtestfile2
Xvimgrep +\cemacs+j Xtestfile1
let l = g:Xgetlist()
- call assert_equal('Xtestfile2', bufname(''))
+ call assert_equal('Xtestfile2', @%)
call assert_equal('Editor:Emacs EmAcS', l[0].text)
" Test for unloading a buffer after vimgrep searched the buffer
@@ -2837,6 +2909,21 @@ func Test_vimgrep_incsearch()
set noincsearch
endfunc
+" Test vimgrep with the last search pattern not set
+func Test_vimgrep_with_no_last_search_pat()
+ let lines =<< trim [SCRIPT]
+ call assert_fails('vimgrep // *', 'E35:')
+ call writefile(v:errors, 'Xresult')
+ qall!
+ [SCRIPT]
+ call writefile(lines, 'Xscript')
+ if RunVim([], [], '--clean -S Xscript')
+ call assert_equal([], readfile('Xresult'))
+ endif
+ call delete('Xscript')
+ call delete('Xresult')
+endfunc
+
" Test vimgrep without swap file
func Test_vimgrep_without_swap_file()
let lines =<< trim [SCRIPT]
@@ -3037,20 +3124,80 @@ func Test_file_from_copen()
endfunc
func Test_resize_from_copen()
+ augroup QF_Test
+ au!
+ au FileType qf resize 5
+ augroup END
+ try
+ " This should succeed without any exception. No other buffers are
+ " involved in the autocmd.
+ copen
+ finally
augroup QF_Test
- au!
- au FileType qf resize 5
+ au!
augroup END
- try
- " This should succeed without any exception. No other buffers are
- " involved in the autocmd.
- copen
- finally
- augroup QF_Test
- au!
- augroup END
- augroup! QF_Test
- endtry
+ augroup! QF_Test
+ endtry
+endfunc
+
+func Test_vimgrep_with_textlock()
+ new
+
+ " Simple way to execute something with "textlock" set.
+ " Check that vimgrep without jumping can be executed.
+ au InsertCharPre * vimgrep /RunTheTest/j runtest.vim
+ normal ax
+ let qflist = getqflist()
+ call assert_true(len(qflist) > 0)
+ call assert_match('RunTheTest', qflist[0].text)
+ call setqflist([], 'r')
+ au! InsertCharPre
+
+ " Check that vimgrepadd without jumping can be executed.
+ au InsertCharPre * vimgrepadd /RunTheTest/j runtest.vim
+ normal ax
+ let qflist = getqflist()
+ call assert_true(len(qflist) > 0)
+ call assert_match('RunTheTest', qflist[0].text)
+ call setqflist([], 'r')
+ au! InsertCharPre
+
+ " Check that lvimgrep without jumping can be executed.
+ au InsertCharPre * lvimgrep /RunTheTest/j runtest.vim
+ normal ax
+ let qflist = getloclist(0)
+ call assert_true(len(qflist) > 0)
+ call assert_match('RunTheTest', qflist[0].text)
+ call setloclist(0, [], 'r')
+ au! InsertCharPre
+
+ " Check that lvimgrepadd without jumping can be executed.
+ au InsertCharPre * lvimgrepadd /RunTheTest/j runtest.vim
+ normal ax
+ let qflist = getloclist(0)
+ call assert_true(len(qflist) > 0)
+ call assert_match('RunTheTest', qflist[0].text)
+ call setloclist(0, [], 'r')
+ au! InsertCharPre
+
+ " trying to jump will give an error
+ au InsertCharPre * vimgrep /RunTheTest/ runtest.vim
+ call assert_fails('normal ax', 'E565:')
+ au! InsertCharPre
+
+ au InsertCharPre * vimgrepadd /RunTheTest/ runtest.vim
+ call assert_fails('normal ax', 'E565:')
+ au! InsertCharPre
+
+ au InsertCharPre * lvimgrep /RunTheTest/ runtest.vim
+ call assert_fails('normal ax', 'E565:')
+ au! InsertCharPre
+
+ au InsertCharPre * lvimgrepadd /RunTheTest/ runtest.vim
+ call assert_fails('normal ax', 'E565:')
+ au! InsertCharPre
+
+ bwipe!
endfunc
" Tests for the quickfix buffer b:changedtick variable
@@ -3449,7 +3596,7 @@ func Xqfjump_tests(cchar)
Xopen | only
2Xnext
call assert_equal(3, g:Xgetlist({'idx' : 0}).idx)
- call assert_equal('F3', bufname('%'))
+ call assert_equal('F3', @%)
Xnext
call assert_equal(7, col('.'))
Xnext
@@ -3524,20 +3671,21 @@ func Xgetlist_empty_tests(cchar)
call assert_equal(0, g:Xgetlist({'changedtick' : 0}).changedtick)
if a:cchar == 'c'
call assert_equal({'context' : '', 'id' : 0, 'idx' : 0,
- \ 'items' : [], 'nr' : 0, 'size' : 0,
+ \ 'items' : [], 'nr' : 0, 'size' : 0, 'qfbufnr' : 0,
\ 'title' : '', 'winid' : 0, 'changedtick': 0,
\ 'quickfixtextfunc' : ''}, g:Xgetlist({'all' : 0}))
else
call assert_equal({'context' : '', 'id' : 0, 'idx' : 0,
\ 'items' : [], 'nr' : 0, 'size' : 0, 'title' : '',
\ 'winid' : 0, 'changedtick': 0, 'filewinid' : 0,
- \ 'quickfixtextfunc' : ''},
+ \ 'qfbufnr' : 0, 'quickfixtextfunc' : ''},
\ g:Xgetlist({'all' : 0}))
endif
" Quickfix window with empty stack
silent! Xopen
let qfwinid = (a:cchar == 'c') ? win_getid() : 0
+ let qfbufnr = (a:cchar == 'c') ? bufnr('') : 0
call assert_equal(qfwinid, g:Xgetlist({'winid' : 0}).winid)
Xclose
@@ -3569,12 +3717,12 @@ func Xgetlist_empty_tests(cchar)
if a:cchar == 'c'
call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [],
\ 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0,
- \ 'quickfixtextfunc' : '',
+ \ 'qfbufnr' : qfbufnr, 'quickfixtextfunc' : '',
\ 'changedtick' : 0}, g:Xgetlist({'id' : qfid, 'all' : 0}))
else
call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [],
\ 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0,
- \ 'changedtick' : 0, 'filewinid' : 0,
+ \ 'changedtick' : 0, 'filewinid' : 0, 'qfbufnr' : 0,
\ 'quickfixtextfunc' : ''},
\ g:Xgetlist({'id' : qfid, 'all' : 0}))
endif
@@ -3592,12 +3740,12 @@ func Xgetlist_empty_tests(cchar)
if a:cchar == 'c'
call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [],
\ 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0,
- \ 'changedtick' : 0,
+ \ 'changedtick' : 0, 'qfbufnr' : qfbufnr,
\ 'quickfixtextfunc' : ''}, g:Xgetlist({'nr' : 5, 'all' : 0}))
else
call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [],
\ 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0,
- \ 'changedtick' : 0, 'filewinid' : 0,
+ \ 'changedtick' : 0, 'filewinid' : 0, 'qfbufnr' : 0,
\ 'quickfixtextfunc' : ''}, g:Xgetlist({'nr' : 5, 'all' : 0}))
endif
endfunc
@@ -4142,20 +4290,20 @@ func Xjumpto_first_error_test(cchar)
" Test for cexpr/lexpr
enew
Xexpr l
- call assert_equal('Xtestfile1', bufname(''))
+ call assert_equal('Xtestfile1', @%)
call assert_equal(2, line('.'))
" Test for cfile/lfile
enew
call writefile(l, 'Xerr')
Xfile Xerr
- call assert_equal('Xtestfile1', bufname(''))
+ call assert_equal('Xtestfile1', @%)
call assert_equal(2, line('.'))
" Test for cbuffer/lbuffer
edit Xerr
Xbuffer
- call assert_equal('Xtestfile1', bufname(''))
+ call assert_equal('Xtestfile1', @%)
call assert_equal(2, line('.'))
call delete('Xerr')
@@ -4180,7 +4328,7 @@ func Xautocmd_changelist(cchar)
autocmd QuickFixCmdPost * Xolder
call writefile(['Xtestfile2:4:Line4'], 'Xerr')
Xfile Xerr
- call assert_equal('Xtestfile2', bufname(''))
+ call assert_equal('Xtestfile2', @%)
call assert_equal(4, line('.'))
autocmd! QuickFixCmdPost
@@ -4191,7 +4339,7 @@ func Xautocmd_changelist(cchar)
call writefile(['Xtestfile2:4:Line4'], 'Xerr')
edit Xerr
Xbuffer
- call assert_equal('Xtestfile2', bufname(''))
+ call assert_equal('Xtestfile2', @%)
call assert_equal(4, line('.'))
autocmd! QuickFixCmdPost
@@ -4200,7 +4348,7 @@ func Xautocmd_changelist(cchar)
Xexpr 'Xtestfile1:2:Line2'
autocmd QuickFixCmdPost * Xolder
Xexpr 'Xtestfile2:4:Line4'
- call assert_equal('Xtestfile2', bufname(''))
+ call assert_equal('Xtestfile2', @%)
call assert_equal(4, line('.'))
autocmd! QuickFixCmdPost
@@ -4211,7 +4359,7 @@ func Xautocmd_changelist(cchar)
Xexpr 'Xtestfile1:2:Line2'
autocmd QuickFixCmdPost * Xolder
silent Xgrep Line5 Xtestfile2
- call assert_equal('Xtestfile2', bufname(''))
+ call assert_equal('Xtestfile2', @%)
call assert_equal(5, line('.'))
autocmd! QuickFixCmdPost
endif
@@ -4221,7 +4369,7 @@ func Xautocmd_changelist(cchar)
Xexpr 'Xtestfile1:2:Line2'
autocmd QuickFixCmdPost * Xolder
silent Xvimgrep Line5 Xtestfile2
- call assert_equal('Xtestfile2', bufname(''))
+ call assert_equal('Xtestfile2', @%)
call assert_equal(5, line('.'))
autocmd! QuickFixCmdPost
@@ -4336,7 +4484,7 @@ func Test_splitview()
new | only
" When split opening files from a helpgrep location list window, a new help
- " window should be opend with a copy of the location list.
+ " window should be opened with a copy of the location list.
lhelpgrep window
let locid = getloclist(0, {'id' : 0}).id
lwindow
@@ -4345,6 +4493,20 @@ func Test_splitview()
call assert_equal(0, getloclist(0, {'winid' : 0}).winid)
new | only
+ " Using :split or :vsplit from a quickfix window should behave like a :new
+ " or a :vnew command
+ copen
+ split
+ call assert_equal(3, winnr('$'))
+ let l = getwininfo()
+ call assert_equal([0, 0, 1], [l[0].quickfix, l[1].quickfix, l[2].quickfix])
+ close
+ copen
+ vsplit
+ let l = getwininfo()
+ call assert_equal([0, 0, 1], [l[0].quickfix, l[1].quickfix, l[2].quickfix])
+ new | only
+
call delete('Xtestfile1')
call delete('Xtestfile2')
endfunc
@@ -4432,11 +4594,19 @@ func Xqfbuf_test(cchar)
Xclose
" Even after the quickfix window is closed, the buffer should be loaded
call assert_true(bufloaded(qfbnum))
+ call assert_true(qfbnum, g:Xgetlist({'qfbufnr' : 0}).qfbufnr)
Xopen
" Buffer should be reused when opening the window again
call assert_equal(qfbnum, bufnr(''))
Xclose
+ " When quickfix buffer is wiped out, getqflist() should return 0
+ %bw!
+ Xexpr ""
+ Xopen
+ bw!
+ call assert_equal(0, g:Xgetlist({'qfbufnr': 0}).qfbufnr)
+
if a:cchar == 'l'
%bwipe
" For a location list, when both the file window and the location list
@@ -4450,7 +4620,7 @@ func Xqfbuf_test(cchar)
close
" When the location list window is closed, the buffer name should not
" change to 'Quickfix List'
- call assert_match(qfbnum . ' h- "\[Location List]"', execute('ls'))
+ call assert_match(qfbnum . 'u h- "\[Location List]"', execute('ls!'))
call assert_true(bufloaded(qfbnum))
" After deleting a location list buffer using ":bdelete", opening the
@@ -4467,6 +4637,7 @@ func Xqfbuf_test(cchar)
" removed
call setloclist(0, [], 'f')
call assert_false(bufexists(qfbnum))
+ call assert_equal(0, getloclist(0, {'qfbufnr' : 0}).qfbufnr)
" When the location list is freed with the location list window open, the
" location list buffer should not be lost. It should be reused when the
@@ -4491,11 +4662,36 @@ func Xqfbuf_test(cchar)
endfunc
func Test_qfbuf()
- throw 'skipped: enable after porting patch 8.1.0877'
call Xqfbuf_test('c')
call Xqfbuf_test('l')
endfunc
+" If there is an autocmd to use only one window, then opening the location
+" list window used to crash Vim.
+func Test_winonly_autocmd()
+ call s:create_test_file('Xtest1')
+ " Autocmd to show only one Vim window at a time
+ autocmd WinEnter * only
+ new
+ " Load the location list
+ lexpr "Xtest1:5:Line5\nXtest1:10:Line10\nXtest1:15:Line15"
+ let loclistid = getloclist(0, {'id' : 0}).id
+ " Open the location list window. Only this window will be shown and the file
+ " window is closed.
+ lopen
+ call assert_equal(loclistid, getloclist(0, {'id' : 0}).id)
+ " Jump to an entry in the location list and make sure that the cursor is
+ " positioned correctly.
+ ll 3
+ call assert_equal(loclistid, getloclist(0, {'id' : 0}).id)
+ call assert_equal('Xtest1', @%)
+ call assert_equal(15, line('.'))
+ " Cleanup
+ autocmd! WinEnter
+ new | only
+ call delete('Xtest1')
+endfunc
+
" Test to make sure that an empty quickfix buffer is not reused for loading
" a normal buffer.
func Test_empty_qfbuf()
@@ -4549,51 +4745,51 @@ func Xtest_below(cchar)
Xexpr ["X1:5:3:L5", "X2:5:2:L5", "X2:10:3:L10", "X2:15:4:L15", "X3:3:5:L3"]
edit +7 X2
Xabove
- call assert_equal(['X2', 5], [bufname(''), line('.')])
+ call assert_equal(['X2', 5], [@%, line('.')])
call assert_fails('Xabove', 'E553:')
normal 7G
Xbefore
- call assert_equal(['X2', 5, 2], [bufname(''), line('.'), col('.')])
+ call assert_equal(['X2', 5, 2], [@%, line('.'), col('.')])
call assert_fails('Xbefore', 'E553:')
normal 2j
Xbelow
- call assert_equal(['X2', 10], [bufname(''), line('.')])
+ call assert_equal(['X2', 10], [@%, line('.')])
normal 7G
Xafter
- call assert_equal(['X2', 10, 3], [bufname(''), line('.'), col('.')])
+ call assert_equal(['X2', 10, 3], [@%, line('.'), col('.')])
" Last error in this file
Xbelow 99
- call assert_equal(['X2', 15], [bufname(''), line('.')])
+ call assert_equal(['X2', 15], [@%, line('.')])
call assert_fails('Xbelow', 'E553:')
normal gg
Xafter 99
- call assert_equal(['X2', 15, 4], [bufname(''), line('.'), col('.')])
+ call assert_equal(['X2', 15, 4], [@%, line('.'), col('.')])
call assert_fails('Xafter', 'E553:')
" First error in this file
Xabove 99
- call assert_equal(['X2', 5], [bufname(''), line('.')])
+ call assert_equal(['X2', 5], [@%, line('.')])
call assert_fails('Xabove', 'E553:')
normal G
Xbefore 99
- call assert_equal(['X2', 5, 2], [bufname(''), line('.'), col('.')])
+ call assert_equal(['X2', 5, 2], [@%, line('.'), col('.')])
call assert_fails('Xbefore', 'E553:')
normal gg
Xbelow 2
- call assert_equal(['X2', 10], [bufname(''), line('.')])
+ call assert_equal(['X2', 10], [@%, line('.')])
normal gg
Xafter 2
- call assert_equal(['X2', 10, 3], [bufname(''), line('.'), col('.')])
+ call assert_equal(['X2', 10, 3], [@%, line('.'), col('.')])
normal G
Xabove 2
- call assert_equal(['X2', 10], [bufname(''), line('.')])
+ call assert_equal(['X2', 10], [@%, line('.')])
normal G
Xbefore 2
- call assert_equal(['X2', 10, 3], [bufname(''), line('.'), col('.')])
+ call assert_equal(['X2', 10, 3], [@%, line('.'), col('.')])
edit X4
call assert_fails('Xabove', 'E42:')
@@ -4617,45 +4813,45 @@ func Xtest_below(cchar)
\ "X2:15:1:L15_1", "X2:15:2:L15_2", "X2:15:3:L15_3", "X3:3:L3"]
edit +1 X2
Xbelow 2
- call assert_equal(['X2', 10, 1], [bufname(''), line('.'), col('.')])
+ call assert_equal(['X2', 10, 1], [@%, line('.'), col('.')])
normal 1G
Xafter 2
- call assert_equal(['X2', 5, 2], [bufname(''), line('.'), col('.')])
+ call assert_equal(['X2', 5, 2], [@%, line('.'), col('.')])
normal gg
Xbelow 99
- call assert_equal(['X2', 15, 1], [bufname(''), line('.'), col('.')])
+ call assert_equal(['X2', 15, 1], [@%, line('.'), col('.')])
normal gg
Xafter 99
- call assert_equal(['X2', 15, 3], [bufname(''), line('.'), col('.')])
+ call assert_equal(['X2', 15, 3], [@%, line('.'), col('.')])
normal G
Xabove 2
- call assert_equal(['X2', 10, 1], [bufname(''), line('.'), col('.')])
+ call assert_equal(['X2', 10, 1], [@%, line('.'), col('.')])
normal G
Xbefore 2
- call assert_equal(['X2', 15, 2], [bufname(''), line('.'), col('.')])
+ call assert_equal(['X2', 15, 2], [@%, line('.'), col('.')])
normal G
Xabove 99
- call assert_equal(['X2', 5, 1], [bufname(''), line('.'), col('.')])
+ call assert_equal(['X2', 5, 1], [@%, line('.'), col('.')])
normal G
Xbefore 99
- call assert_equal(['X2', 5, 1], [bufname(''), line('.'), col('.')])
+ call assert_equal(['X2', 5, 1], [@%, line('.'), col('.')])
normal 10G
Xabove
- call assert_equal(['X2', 5, 1], [bufname(''), line('.'), col('.')])
+ call assert_equal(['X2', 5, 1], [@%, line('.'), col('.')])
normal 10G$
2Xbefore
- call assert_equal(['X2', 10, 2], [bufname(''), line('.'), col('.')])
+ call assert_equal(['X2', 10, 2], [@%, line('.'), col('.')])
normal 10G
Xbelow
- call assert_equal(['X2', 15, 1], [bufname(''), line('.'), col('.')])
+ call assert_equal(['X2', 15, 1], [@%, line('.'), col('.')])
normal 9G
5Xafter
- call assert_equal(['X2', 15, 2], [bufname(''), line('.'), col('.')])
+ call assert_equal(['X2', 15, 2], [@%, line('.'), col('.')])
" Invalid range
if a:cchar == 'c'
@@ -4969,7 +5165,7 @@ func Test_quickfix_window_fails_to_open()
call delete('XquickfixFails')
endfunc
-" Test for updating the quickfix buffer whenever the assocaited quickfix list
+" Test for updating the quickfix buffer whenever the associated quickfix list
" is changed.
func Xqfbuf_update(cchar)
call s:setup_commands(a:cchar)
@@ -5027,6 +5223,52 @@ func Test_qfbuf_update()
call Xqfbuf_update('l')
endfunc
+" Test for the :vimgrep 'f' flag (fuzzy match)
+func Xvimgrep_fuzzy_match(cchar)
+ call s:setup_commands(a:cchar)
+
+ Xvimgrep /three one/f Xfile*
+ let l = g:Xgetlist()
+ call assert_equal(2, len(l))
+ call assert_equal(['Xfile1', 1, 9, 'one two three'],
+ \ [bufname(l[0].bufnr), l[0].lnum, l[0].col, l[0].text])
+ call assert_equal(['Xfile2', 2, 1, 'three one two'],
+ \ [bufname(l[1].bufnr), l[1].lnum, l[1].col, l[1].text])
+
+ Xvimgrep /the/f Xfile*
+ let l = g:Xgetlist()
+ call assert_equal(3, len(l))
+ call assert_equal(['Xfile1', 1, 9, 'one two three'],
+ \ [bufname(l[0].bufnr), l[0].lnum, l[0].col, l[0].text])
+ call assert_equal(['Xfile2', 2, 1, 'three one two'],
+ \ [bufname(l[1].bufnr), l[1].lnum, l[1].col, l[1].text])
+ call assert_equal(['Xfile2', 4, 4, 'aaathreeaaa'],
+ \ [bufname(l[2].bufnr), l[2].lnum, l[2].col, l[2].text])
+
+ Xvimgrep /aaa/fg Xfile*
+ let l = g:Xgetlist()
+ call assert_equal(4, len(l))
+ call assert_equal(['Xfile1', 2, 1, 'aaaaaa'],
+ \ [bufname(l[0].bufnr), l[0].lnum, l[0].col, l[0].text])
+ call assert_equal(['Xfile1', 2, 4, 'aaaaaa'],
+ \ [bufname(l[1].bufnr), l[1].lnum, l[1].col, l[1].text])
+ call assert_equal(['Xfile2', 4, 1, 'aaathreeaaa'],
+ \ [bufname(l[2].bufnr), l[2].lnum, l[2].col, l[2].text])
+ call assert_equal(['Xfile2', 4, 9, 'aaathreeaaa'],
+ \ [bufname(l[3].bufnr), l[3].lnum, l[3].col, l[3].text])
+
+ call assert_fails('Xvimgrep /xyz/fg Xfile*', 'E480:')
+endfunc
+
+func Test_vimgrep_fuzzy_match()
+ call writefile(['one two three', 'aaaaaa'], 'Xfile1')
+ call writefile(['one', 'three one two', 'two', 'aaathreeaaa'], 'Xfile2')
+ call Xvimgrep_fuzzy_match('c')
+ call Xvimgrep_fuzzy_match('l')
+ call delete('Xfile1')
+ call delete('Xfile2')
+endfunc
+
" Test for getting a specific item from a quickfix list
func Xtest_getqflist_by_idx(cchar)
call s:setup_commands(a:cchar)
@@ -5137,16 +5379,14 @@ func Xtest_qftextfunc(cchar)
" Non-existing function
set quickfixtextfunc=Tabc
- " call assert_fails("Xexpr ['F1:10:2:green', 'F1:20:4:blue']", 'E117:')
- Xexpr ['F1:10:2:green', 'F1:20:4:blue']"
+ call assert_fails("Xexpr ['F1:10:2:green', 'F1:20:4:blue']", 'E117:')
call assert_fails("Xwindow", 'E117:')
Xclose
set quickfixtextfunc&
" set option to a non-function
set quickfixtextfunc=[10,\ 20]
- " call assert_fails("Xexpr ['F1:10:2:green', 'F1:20:4:blue']", 'E117:')
- Xexpr ['F1:10:2:green', 'F1:20:4:blue']"
+ call assert_fails("Xexpr ['F1:10:2:green', 'F1:20:4:blue']", 'E117:')
call assert_fails("Xwindow", 'E117:')
Xclose
set quickfixtextfunc&
@@ -5156,8 +5396,7 @@ func Xtest_qftextfunc(cchar)
return a:a .. a:b .. a:c
endfunc
set quickfixtextfunc=Xqftext
- " call assert_fails("Xexpr ['F1:10:2:green', 'F1:20:4:blue']", 'E119:')
- Xexpr ['F1:10:2:green', 'F1:20:4:blue']"
+ call assert_fails("Xexpr ['F1:10:2:green', 'F1:20:4:blue']", 'E119:')
call assert_fails("Xwindow", 'E119:')
Xclose
@@ -5166,9 +5405,8 @@ func Xtest_qftextfunc(cchar)
return ['one', [], 'two']
endfunc
set quickfixtextfunc=Xqftext2
- " call assert_fails("Xexpr ['F1:10:2:green', 'F1:20:4:blue', 'F1:30:6:red']",
- " \ 'E730:')
- Xexpr ['F1:10:2:green', 'F1:20:4:blue', 'F1:30:6:red']
+ call assert_fails("Xexpr ['F1:10:2:green', 'F1:20:4:blue', 'F1:30:6:red']",
+ \ 'E730:')
call assert_fails('Xwindow', 'E730:')
call assert_equal(['one', 'F1|20 col 4| blue', 'F1|30 col 6| red'],
\ getline(1, '$'))
@@ -5332,4 +5570,81 @@ func Test_win_gettype()
lclose
endfunc
+" Test for opening the quickfix window in two tab pages and then closing one
+" of the quickfix windows. This should not make the quickfix buffer unlisted.
+" (github issue #9300).
+func Test_two_qf_windows()
+ cexpr "F1:1:line1"
+ copen
+ tabnew
+ copen
+ call assert_true(&buflisted)
+ cclose
+ tabfirst
+ call assert_true(&buflisted)
+ let bnum = bufnr()
+ cclose
+ " if all the quickfix windows are closed, then buffer should be unlisted.
+ call assert_false(buflisted(bnum))
+ %bw!
+
+ " Repeat the test for a location list
+ lexpr "F2:2:line2"
+ lopen
+ let bnum = bufnr()
+ tabnew
+ exe "buffer" bnum
+ tabfirst
+ lclose
+ tablast
+ call assert_true(buflisted(bnum))
+ tabclose
+ lopen
+ call assert_true(buflisted(bnum))
+ lclose
+ call assert_false(buflisted(bnum))
+ %bw!
+endfunc
+
+" Weird sequence of commands that caused entering a wiped-out buffer
+func Test_lopen_bwipe()
+ func R()
+ silent! tab lopen
+ e x
+ silent! lfile
+ endfunc
+
+ cal R()
+ cal R()
+ cal R()
+ bw!
+ delfunc R
+endfunc
+
+" Another sequence of commands that caused all buffers to be wiped out
+func Test_lopen_bwipe_all()
+ let lines =<< trim END
+ func R()
+ silent! tab lopen
+ e foo
+ silent! lfile
+ endfunc
+ cal R()
+ exe "norm \<C-W>\<C-V>0"
+ cal R()
+ bwipe
+
+ call writefile(['done'], 'Xresult')
+ qall!
+ END
+ call writefile(lines, 'Xscript')
+ if RunVim([], [], '--clean -n -S Xscript')
+ call assert_equal(['done'], readfile('Xresult'))
+ endif
+
+ call delete('Xscript')
+ call delete('Xresult')
+endfunc
+
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_quotestar.vim b/src/nvim/testdir/test_quotestar.vim
index 6e6f91362b..93865869fa 100644
--- a/src/nvim/testdir/test_quotestar.vim
+++ b/src/nvim/testdir/test_quotestar.vim
@@ -1,10 +1,9 @@
" *-register (quotestar) tests
-if !has('clipboard')
- finish
-endif
-
source shared.vim
+source check.vim
+
+CheckFeature clipboard_working
func Do_test_quotestar_for_macunix()
if empty(exepath('pbcopy')) || empty(exepath('pbpaste'))
diff --git a/src/nvim/testdir/test_random.vim b/src/nvim/testdir/test_random.vim
new file mode 100644
index 0000000000..6d3f7dcfd9
--- /dev/null
+++ b/src/nvim/testdir/test_random.vim
@@ -0,0 +1,51 @@
+" Tests for srand() and rand()
+
+func Test_Rand()
+ let r = srand(123456789)
+ call assert_equal([1573771921, 319883699, 2742014374, 1324369493], r)
+ call assert_equal(4284103975, rand(r))
+ call assert_equal(1001954530, rand(r))
+ call assert_equal(2701803082, rand(r))
+ call assert_equal(2658065534, rand(r))
+ call assert_equal(3104308804, rand(r))
+
+ " Nvim does not support test_settime
+ " call test_settime(12341234)
+ let s = srand()
+ if !has('win32') && filereadable('/dev/urandom')
+ " using /dev/urandom
+ call assert_notequal(s, srand())
+ " else
+ " " using time()
+ " call assert_equal(s, srand())
+ " call test_settime(12341235)
+ " call assert_notequal(s, srand())
+ endif
+
+ " Nvim does not support test_srand_seed
+ " call test_srand_seed(123456789)
+ " call assert_equal(4284103975, rand())
+ " call assert_equal(1001954530, rand())
+ " call test_srand_seed()
+
+ if has('float')
+ call assert_fails('echo srand(1.2)', 'E805:')
+ endif
+ call assert_fails('echo srand([1])', 'E745:')
+ call assert_fails('echo rand("burp")', 'E475:')
+ call assert_fails('echo rand([1, 2, 3])', 'E475:')
+ call assert_fails('echo rand([[1], 2, 3, 4])', 'E475:')
+ call assert_fails('echo rand([1, [2], 3, 4])', 'E475:')
+ call assert_fails('echo rand([1, 2, [3], 4])', 'E475:')
+ call assert_fails('echo rand([1, 2, 3, [4]])', 'E475:')
+
+ " call test_settime(0)
+endfunc
+
+func Test_issue_5587()
+ call rand()
+ call garbagecollect()
+ call rand()
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_regex_char_classes.vim b/src/nvim/testdir/test_regex_char_classes.vim
index c1a4202c2b..db16f057c8 100644
--- a/src/nvim/testdir/test_regex_char_classes.vim
+++ b/src/nvim/testdir/test_regex_char_classes.vim
@@ -293,3 +293,5 @@ func Test_regex_char_classes()
enew!
close
endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_regexp_latin.vim b/src/nvim/testdir/test_regexp_latin.vim
index 712f1e6025..82d250e8b3 100644
--- a/src/nvim/testdir/test_regexp_latin.vim
+++ b/src/nvim/testdir/test_regexp_latin.vim
@@ -6,7 +6,10 @@ scriptencoding latin1
source check.vim
func s:equivalence_test()
- let str = "AÀÁÂÃÄÅ B C D EÈÉÊË F G H IÌÍÎÏ J K L M NÑ OÒÓÔÕÖØ P Q R S T UÙÚÛÜ V W X YÝ Z aàáâãäå b c d eèéêë f g h iìíîï j k l m nñ oòóôõöø p q r s t uùúûü v w x yýÿ z"
+ let str = 'A B C D E F G H I J K L M N O P Q R S T U V W X Y Z '
+ \ .. 'a b c d e f g h i j k l m n o p q r s t u v w x y z '
+ \ .. "0 1 2 3 4 5 6 7 8 9 "
+ \ .. "` ~ ! ? ; : . , / \\ ' \" | < > [ ] { } ( ) @ # $ % ^ & * _ - + \b \e \f \n \r \t"
let groups = split(str)
for group1 in groups
for c in split(group1, '\zs')
@@ -119,7 +122,10 @@ endfunc
" Tests for regexp patterns without multi-byte support.
func Test_regexp_single_line_pat()
" tl is a List of Lists with:
- " regexp engine
+ " regexp engines to test
+ " 0 - test with 'regexpengine' values 0 and 1
+ " 1 - test with 'regexpengine' values 0 and 2
+ " 2 - test with 'regexpengine' values 0, 1 and 2
" regexp pattern
" text to test the pattern on
" expected match (optional)
@@ -140,6 +146,8 @@ func Test_regexp_single_line_pat()
call add(tl, [2, 'c*', 'abdef', ''])
call add(tl, [2, 'bc\+', 'abccccdef', 'bcccc'])
call add(tl, [2, 'bc\+', 'abdef']) " no match
+ " match newline character in a string
+ call add(tl, [2, 'o\nb', "foo\nbar", "o\nb"])
" operator \|
call add(tl, [2, 'a\|ab', 'cabd', 'a']) " alternation is ordered
@@ -337,7 +345,7 @@ func Test_regexp_single_line_pat()
call add(tl, [2, '\v((ab)|c*)+', 'abcccaba', 'abcccab', '', 'ab'])
call add(tl, [2, '\v(a(c*)+b)+', 'acbababaaa', 'acbabab', 'ab', ''])
call add(tl, [2, '\v(a|b*)+', 'aaaa', 'aaaa', ''])
- call add(tl, [2, '\p*', 'aá ', 'aá '])
+ call add(tl, [2, '\p*', 'a ', 'a '])
" Test greedy-ness and lazy-ness
call add(tl, [2, 'a\{-2,7}','aaaaaaaaaaaaa', 'aa'])
@@ -563,6 +571,9 @@ func Test_regexp_single_line_pat()
" Test \%V atom
call add(tl, [2, '\%>70vGesamt', 'Jean-Michel Charlier & Victor Hubinon\Gesamtausgabe [Salleck] Buck Danny {Jean-Michel Charlier & Victor Hubinon}\Gesamtausgabe', 'Gesamt'])
+ " Test for ignoring case and matching repeated characters
+ call add(tl, [2, '\cb\+', 'aAbBbBcC', 'bBbB'])
+
" Run the tests
for t in tl
let re = t[0]
@@ -622,6 +633,14 @@ endfunc
" Tests for multi-line regexp patterns without multi-byte support.
func Test_regexp_multiline_pat()
+ " tl is a List of Lists with:
+ " regexp engines to test
+ " 0 - test with 'regexpengine' values 0 and 1
+ " 1 - test with 'regexpengine' values 0 and 2
+ " 2 - test with 'regexpengine' values 0, 1 and 2
+ " regexp pattern
+ " List with text to test the pattern on
+ " List with the expected match
let tl = []
" back references
@@ -631,6 +650,70 @@ func Test_regexp_multiline_pat()
" line breaks
call add(tl, [2, '\S.*\nx', ['abc', 'def', 'ghi', 'xjk', 'lmn'], ['abc', 'def', 'XXjk', 'lmn']])
+ " Any single character or end-of-line
+ call add(tl, [2, '\_.\+', ['a', 'b', 'c'], ['XX']])
+ " Any identifier or end-of-line
+ call add(tl, [2, '\_i\+', ['a', 'b', ';', '2'], ['XX;XX']])
+ " Any identifier but excluding digits or end-of-line
+ call add(tl, [2, '\_I\+', ['a', 'b', ';', '2'], ['XX;XX2XX']])
+ " Any keyword or end-of-line
+ call add(tl, [2, '\_k\+', ['a', 'b', '=', '2'], ['XX=XX']])
+ " Any keyword but excluding digits or end-of-line
+ call add(tl, [2, '\_K\+', ['a', 'b', '=', '2'], ['XX=XX2XX']])
+ " Any filename character or end-of-line
+ call add(tl, [2, '\_f\+', ['a', 'b', '.', '5'], ['XX']])
+ " Any filename character but excluding digits or end-of-line
+ call add(tl, [2, '\_F\+', ['a', 'b', '.', '5'], ['XX5XX']])
+ " Any printable character or end-of-line
+ call add(tl, [2, '\_p\+', ['a', 'b', '=', '4'], ['XX']])
+ " Any printable character excluding digits or end-of-line
+ call add(tl, [2, '\_P\+', ['a', 'b', '=', '4'], ['XX4XX']])
+ " Any whitespace character or end-of-line
+ call add(tl, [2, '\_s\+', [' ', ' ', 'a', 'b'], ['XXaXXbXX']])
+ " Any non-whitespace character or end-of-line
+ call add(tl, [2, '\_S\+', [' ', ' ', 'a', 'b'], [' XX XX']])
+ " Any decimal digit or end-of-line
+ call add(tl, [2, '\_d\+', ['1', 'a', '2', 'b', '3'], ['XXaXXbXX']])
+ " Any non-decimal digit or end-of-line
+ call add(tl, [2, '\_D\+', ['1', 'a', '2', 'b', '3'], ['1XX2XX3XX']])
+ " Any hexadecimal digit or end-of-line
+ call add(tl, [2, '\_x\+', ['1', 'a', 'g', '9', '8'], ['XXgXX']])
+ " Any non-hexadecimal digit or end-of-line
+ call add(tl, [2, '\_X\+', ['1', 'a', 'g', '9', '8'], ['1XXaXX9XX8XX']])
+ " Any octal digit or end-of-line
+ call add(tl, [2, '\_o\+', ['0', '7', '8', '9', '0'], ['XX8XX9XX']])
+ " Any non-octal digit or end-of-line
+ call add(tl, [2, '\_O\+', ['0', '7', '8', '9', '0'], ['0XX7XX0XX']])
+ " Any word character or end-of-line
+ call add(tl, [2, '\_w\+', ['A', 'B', '=', 'C', 'D'], ['XX=XX']])
+ " Any non-word character or end-of-line
+ call add(tl, [2, '\_W\+', ['A', 'B', '=', 'C', 'D'], ['AXXBXXCXXDXX']])
+ " Any head-of-word character or end-of-line
+ call add(tl, [2, '\_h\+', ['a', '1', 'b', '2', 'c'], ['XX1XX2XX']])
+ " Any non-head-of-word character or end-of-line
+ call add(tl, [2, '\_H\+', ['a', '1', 'b', '2', 'c'], ['aXXbXXcXX']])
+ " Any alphabetic character or end-of-line
+ call add(tl, [2, '\_a\+', ['a', '1', 'b', '2', 'c'], ['XX1XX2XX']])
+ " Any non-alphabetic character or end-of-line
+ call add(tl, [2, '\_A\+', ['a', '1', 'b', '2', 'c'], ['aXXbXXcXX']])
+ " Any lowercase character or end-of-line
+ call add(tl, [2, '\_l\+', ['a', 'A', 'b', 'B'], ['XXAXXBXX']])
+ " Any non-lowercase character or end-of-line
+ call add(tl, [2, '\_L\+', ['a', 'A', 'b', 'B'], ['aXXbXX']])
+ " Any uppercase character or end-of-line
+ call add(tl, [2, '\_u\+', ['a', 'A', 'b', 'B'], ['aXXbXX']])
+ " Any non-uppercase character or end-of-line
+ call add(tl, [2, '\_U\+', ['a', 'A', 'b', 'B'], ['XXAXXBXX']])
+ " Collection or end-of-line
+ call add(tl, [2, '\_[a-z]\+', ['a', 'A', 'b', 'B'], ['XXAXXBXX']])
+ " start of line anywhere in the text
+ call add(tl, [2, 'one\zs\_s*\_^\zetwo',
+ \ ['', 'one', ' two', 'one', '', 'two'],
+ \ ['', 'one', ' two', 'oneXXtwo']])
+ " end of line anywhere in the text
+ call add(tl, [2, 'one\zs\_$\_s*two',
+ \ ['', 'one', ' two', 'one', '', 'two'], ['', 'oneXX', 'oneXX']])
+
" Check that \_[0-9] matching EOL does not break a following \>
call add(tl, [2, '\<\(\(25\_[0-5]\|2\_[0-4]\_[0-9]\|\_[01]\?\_[0-9]\_[0-9]\?\)\.\)\{3\}\(25\_[0-5]\|2\_[0-4]\_[0-9]\|\_[01]\?\_[0-9]\_[0-9]\?\)\>', ['', 'localnet/192.168.0.1', ''], ['', 'localnet/XX', '']])
@@ -646,7 +729,7 @@ func Test_regexp_multiline_pat()
let before = t[2]
let after = t[3]
for engine in [0, 1, 2]
- if engine == 2 && re == 0 || engine == 1 && re ==1
+ if engine == 2 && re == 0 || engine == 1 && re == 1
continue
endif
let &regexpengine = engine
@@ -683,7 +766,7 @@ func Test_matchstr_with_ze()
bwipe!
endfunc
-" Check a pattern with a look beind crossing a line boundary
+" Check a pattern with a look behind crossing a line boundary
func Test_lookbehind_across_line()
new
call append(0, ['Behind:', 'asdfasd<yyy', 'xxstart1', 'asdfasd<yy',
@@ -694,9 +777,8 @@ func Test_lookbehind_across_line()
bwipe!
endfunc
-" Check matching Visual area
-func Test_matching_visual_area()
- new
+" Test for the \%V atom (match inside the visual area)
+func Regex_Match_Visual_Area()
call append(0, ['Visual:', 'thexe the thexethe', 'andaxand andaxand',
\ 'oooxofor foroxooo', 'oooxofor foroxooo'])
call cursor(1, 1)
@@ -705,12 +787,22 @@ func Test_matching_visual_area()
exe "normal jfx\<C-V>fxj:s/\\%Vo/O/g\<CR>"
call assert_equal(['Visual:', 'thexE thE thExethe', 'AndAxAnd AndAxAnd',
\ 'oooxOfOr fOrOxooo', 'oooxOfOr fOrOxooo', ''], getline(1, '$'))
+ %d
+endfunc
+
+" Check matching Visual area
+func Test_matching_visual_area()
+ new
+ set regexpengine=1
+ call Regex_Match_Visual_Area()
+ set regexpengine=2
+ call Regex_Match_Visual_Area()
+ set regexpengine&
bwipe!
endfunc
" Check matching marks
-func Test_matching_marks()
- new
+func Regex_Mark()
call append(0, ['', '', '', 'Marks:', 'asdfSasdfsadfEasdf', 'asdfSas',
\ 'dfsadfEasdf', '', '', '', '', ''])
call cursor(4, 1)
@@ -718,6 +810,15 @@ func Test_matching_marks()
exe "normal jfSmsj0fEme:.-4,.+6s/.\\%>'s\\_.*\\%<'e../again/\<CR>"
call assert_equal(['', '', '', 'Marks:', 'asdfhereasdf', 'asdfagainasdf',
\ '', '', '', '', '', ''], getline(1, '$'))
+ %d
+endfunc
+
+func Test_matching_marks()
+ new
+ set regexpengine=1
+ call Regex_Mark()
+ set regexpengine=2
+ call Regex_Mark()
bwipe!
endfunc
@@ -758,8 +859,7 @@ func Test_matching_curpos()
endfunc
" Test for matching the start and end of a buffer
-func Test_start_end_of_buffer_match()
- new
+func Regex_start_end_buffer()
call setline(1, repeat(['vim edit'], 20))
/\%^
call assert_equal([0, 1, 1, 0], getpos('.'))
@@ -769,6 +869,15 @@ func Test_start_end_of_buffer_match()
call assert_equal([0, 20, 8, 0], getpos('.'))
exe "normal 6gg/..\\%$\<CR>"
call assert_equal([0, 20, 7, 0], getpos('.'))
+ %d
+endfunc
+
+func Test_start_end_of_buffer_match()
+ new
+ set regexpengine=1
+ call Regex_start_end_buffer()
+ set regexpengine=2
+ call Regex_start_end_buffer()
bwipe!
endfunc
@@ -781,10 +890,141 @@ endfunc
" Check for detecting error
func Test_regexp_error()
- set regexpengine=2
- call assert_fails("call matchlist('x x', ' \\ze*')", 'E888:')
- call assert_fails("call matchlist('x x', ' \\zs*')", 'E888:')
+ call assert_fails("call matchlist('x x', '\\%#=1 \\zs*')", 'E888:')
+ call assert_fails("call matchlist('x x', '\\%#=1 \\ze*')", 'E888:')
+ call assert_fails("call matchlist('x x', '\\%#=2 \\zs*')", 'E888:')
+ call assert_fails("call matchlist('x x', '\\%#=2 \\ze*')", 'E888:')
+ call assert_fails('exe "normal /\\%#=1\\%[x\\%[x]]\<CR>"', 'E369:')
+endfunc
+
+" Test for using the last substitute string pattern (~)
+func Test_regexp_last_subst_string()
+ new
+ s/bar/baz/e
+ call assert_equal(matchstr("foo\nbaz\nbar", "\\%#=1\~"), "baz")
+ call assert_equal(matchstr("foo\nbaz\nbar", "\\%#=2\~"), "baz")
+ close!
+endfunc
+
+" Check patterns matching cursor position.
+func s:curpos_test2()
+ new
+ call setline(1, ['1', '2 foobar eins zwei drei vier fnf sechse',
+ \ '3 foobar eins zwei drei vier fnf sechse',
+ \ '4 foobar eins zwei drei vier fnf sechse',
+ \ '5 foobar eins zwei drei vier fnf sechse',
+ \ '6 foobar eins zwei drei vier fnf sechse',
+ \ '7 foobar eins zwei drei vier fnf sechse'])
+ call setpos('.', [0, 2, 10, 0])
+ s/\%.c.*//g
+ call setpos('.', [0, 3, 15, 0])
+ s/\%.l.*//g
+ call setpos('.', [0, 5, 3, 0])
+ s/\%.v.*/_/g
+ call assert_equal(['1',
+ \ '2 foobar ',
+ \ '',
+ \ '4 foobar eins zwei drei vier fnf sechse',
+ \ '5 _',
+ \ '6 foobar eins zwei drei vier fnf sechse',
+ \ '7 foobar eins zwei drei vier fnf sechse'],
+ \ getline(1, '$'))
+ call assert_fails('call search("\\%.1l")', 'E1204:')
+ call assert_fails('call search("\\%.1c")', 'E1204:')
+ call assert_fails('call search("\\%.1v")', 'E1204:')
+ bwipe!
+endfunc
+
+" Check patterns matching before or after cursor position.
+func s:curpos_test3()
+ new
+ call setline(1, ['1', '2 foobar eins zwei drei vier fnf sechse',
+ \ '3 foobar eins zwei drei vier fnf sechse',
+ \ '4 foobar eins zwei drei vier fnf sechse',
+ \ '5 foobar eins zwei drei vier fnf sechse',
+ \ '6 foobar eins zwei drei vier fnf sechse',
+ \ '7 foobar eins zwei drei vier fnf sechse'])
+ call setpos('.', [0, 2, 10, 0])
+ " Note: This removes all columns, except for the column directly in front of
+ " the cursor. Bug????
+ :s/^.*\%<.c//
+ call setpos('.', [0, 3, 10, 0])
+ :s/\%>.c.*$//
+ call setpos('.', [0, 5, 4, 0])
+ " Note: This removes all columns, except for the column directly in front of
+ " the cursor. Bug????
+ :s/^.*\%<.v/_/
+ call setpos('.', [0, 6, 4, 0])
+ :s/\%>.v.*$/_/
+ call assert_equal(['1',
+ \ ' eins zwei drei vier fnf sechse',
+ \ '3 foobar e',
+ \ '4 foobar eins zwei drei vier fnf sechse',
+ \ '_foobar eins zwei drei vier fnf sechse',
+ \ '6 fo_',
+ \ '7 foobar eins zwei drei vier fnf sechse'],
+ \ getline(1, '$'))
+ sil %d
+ call setline(1, ['1', '2 foobar eins zwei drei vier fnf sechse',
+ \ '3 foobar eins zwei drei vier fnf sechse',
+ \ '4 foobar eins zwei drei vier fnf sechse',
+ \ '5 foobar eins zwei drei vier fnf sechse',
+ \ '6 foobar eins zwei drei vier fnf sechse',
+ \ '7 foobar eins zwei drei vier fnf sechse'])
+ call setpos('.', [0, 4, 4, 0])
+ %s/\%<.l.*//
+ call setpos('.', [0, 5, 4, 0])
+ %s/\%>.l.*//
+ call assert_equal(['', '', '',
+ \ '4 foobar eins zwei drei vier fnf sechse',
+ \ '5 foobar eins zwei drei vier fnf sechse',
+ \ '', ''],
+ \ getline(1, '$'))
+ bwipe!
+endfunc
+
+" Test that matching below, at or after the
+" cursor position work
+func Test_matching_pos()
+ for val in range(3)
+ exe "set re=" .. val
+ " Match at cursor position
+ call s:curpos_test2()
+ " Match before or after cursor position
+ call s:curpos_test3()
+ endfor
set re&
endfunc
+func Test_using_mark_position()
+ " this was using freed memory
+ " new engine
+ new
+ norm O0
+ call assert_fails("s/\\%')", 'E486:')
+ bwipe!
+
+ " old engine
+ new
+ norm O0
+ call assert_fails("s/\\%#=1\\%')", 'E486:')
+ bwipe!
+endfunc
+
+func Test_using_visual_position()
+ " this was using freed memory
+ new
+ exe "norm 0o\<Esc>\<C-V>k\<C-X>o0"
+ /\%V
+ bwipe!
+endfunc
+
+func Test_using_invalid_visual_position()
+ " this was going beyond the end of the line
+ new
+ exe "norm 0o000\<Esc>0\<C-V>$s0"
+ /\%V
+ 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 c568805f87..191cd948ac 100644
--- a/src/nvim/testdir/test_regexp_utf8.vim
+++ b/src/nvim/testdir/test_regexp_utf8.vim
@@ -1,21 +1,21 @@
" Tests for regexp in utf8 encoding
func s:equivalence_test()
- let str = "AÀÁÂÃÄÅĀĂĄǍǞǠẢ BḂḆ CÇĆĈĊČ DĎĐḊḎḐ EÈÉÊËĒĔĖĘĚẺẼ FḞ GĜĞĠĢǤǦǴḠ HĤĦḢḦḨ IÌÍÎÏĨĪĬĮİǏỈ JĴ KĶǨḰḴ LĹĻĽĿŁḺ MḾṀ NÑŃŅŇṄṈ OÒÓÔÕÖØŌŎŐƠǑǪǬỎ PṔṖ Q RŔŖŘṘṞ SŚŜŞŠṠ TŢŤŦṪṮ UÙÚÛÜŨŪŬŮŰŲƯǓỦ VṼ WŴẀẂẄẆ XẊẌ YÝŶŸẎỲỶỸ ZŹŻŽƵẐẔ aàáâãäåāăąǎǟǡả bḃḇ cçćĉċč dďđḋḏḑ eèéêëēĕėęěẻẽ fḟ gĝğġģǥǧǵḡ hĥħḣḧḩẖ iìíîïĩīĭįǐỉ jĵǰ kķǩḱḵ lĺļľŀłḻ mḿṁ nñńņňʼnṅṉ oòóôõöøōŏőơǒǫǭỏ pṕṗ q rŕŗřṙṟ sśŝşšṡ tţťŧṫṯẗ uùúûüũūŭůűųưǔủ vṽ wŵẁẃẅẇẘ xẋẍ yýÿŷẏẙỳỷỹ zźżžƶẑẕ"
+ let str = "AÀÁÂÃÄÅĀĂĄǍǞǠǺȂȦȺḀẠẢẤẦẨẪẬẮẰẲẴẶ BƁɃḂḄḆ CÇĆĈĊČƇȻḈꞒ DĎĐƊḊḌḎḐḒ EÈÉÊËĒĔĖĘĚȄȆȨɆḔḖḘḚḜẸẺẼẾỀỂỄỆ FƑḞꞘ GĜĞĠĢƓǤǦǴḠꞠ HĤĦȞḢḤḦḨḪⱧ IÌÍÎÏĨĪĬĮİƗǏȈȊḬḮỈỊ JĴɈ KĶƘǨḰḲḴⱩꝀ LĹĻĽĿŁȽḶḸḺḼⱠ MḾṀṂ NÑŃŅŇǸṄṆṈṊꞤ OÒÓÔÕÖØŌŎŐƟƠǑǪǬǾȌȎȪȬȮȰṌṎṐṒỌỎỐỒỔỖỘỚỜỞỠỢ PƤṔṖⱣ QɊ RŔŖŘȐȒɌṘṚṜṞⱤꞦ SŚŜŞŠȘṠṢṤṦṨⱾꞨ TŢŤŦƬƮȚȾṪṬṮṰ UÙÚÛÜŨŪŬŮŰƯǕǙǛǓǗȔȖɄṲṴṶṸṺỤỦỨỪỬỮỰ VƲṼṾ WŴẀẂẄẆẈ XẊẌ YÝŶŸƳȲɎẎỲỴỶỸ ZŹŻŽƵẐẒẔⱫ aàáâãäåāăąǎǟǡǻȃȧᶏḁẚạảấầẩẫậắằẳẵặⱥ bƀɓᵬᶀḃḅḇ cçćĉċčƈȼḉꞓꞔ dďđɗᵭᶁᶑḋḍḏḑḓ eèéêëēĕėęěȅȇȩɇᶒḕḗḙḛḝẹẻẽếềểễệ fƒᵮᶂḟꞙ gĝğġģǥǧǵɠᶃḡꞡ hĥħȟḣḥḧḩḫẖⱨꞕ iìíîïĩīĭįǐȉȋɨᶖḭḯỉị jĵǰɉ kķƙǩᶄḱḳḵⱪꝁ lĺļľŀłƚḷḹḻḽⱡ mᵯḿṁṃ nñńņňʼnǹᵰᶇṅṇṉṋꞥ oòóôõöøōŏőơǒǫǭǿȍȏȫȭȯȱɵṍṏṑṓọỏốồổỗộớờởỡợ pƥᵱᵽᶈṕṗ qɋʠ rŕŗřȑȓɍɽᵲᵳᶉṛṝṟꞧ sśŝşšșȿᵴᶊṡṣṥṧṩꞩ tţťŧƫƭțʈᵵṫṭṯṱẗⱦ uùúûüũūŭůűųǚǖưǔǘǜȕȗʉᵾᶙṳṵṷṹṻụủứừửữự vʋᶌṽṿ wŵẁẃẅẇẉẘ xẋẍ yýÿŷƴȳɏẏẙỳỵỷỹ zźżžƶᵶᶎẑẓẕⱬ"
let groups = split(str)
for group1 in groups
- for c in split(group1, '\zs')
- " next statement confirms that equivalence class matches every
- " character in group
- call assert_match('^[[=' . c . '=]]*$', group1)
- for group2 in groups
- if group2 != group1
- " next statement converts that equivalence class doesn't match
- " character in any other group
- call assert_equal(-1, match(group2, '[[=' . c . '=]]'))
- endif
+ for c in split(group1, '\zs')
+ " next statement confirms that equivalence class matches every
+ " character in group
+ call assert_match('^[[=' .. c .. '=]]*$', group1)
+ for group2 in groups
+ if group2 != group1
+ " next statement converts that equivalence class doesn't match
+ " character in any other group
+ call assert_equal(-1, match(group2, '[[=' .. c .. '=]]'), c)
+ endif
+ endfor
endfor
- endfor
endfor
endfunc
@@ -152,9 +152,6 @@ func s:classes_test()
if has('win32')
let identchars_ok = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§µÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ'
let kwordchars_ok = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyzµÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ'
- elseif has('ebcdic')
- let identchars_ok = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz€ŒŽœž¬®µº¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ'
- let kwordchars_ok = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz€ŒŽœž¬®µº¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ'
else
let identchars_ok = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyzµÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ'
let kwordchars_ok = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyzµÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ'
@@ -166,8 +163,6 @@ func s:classes_test()
let fnamechars_ok = '$+,-./0123456789:ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ'
elseif has('vms')
let fnamechars_ok = '#$%+,-./0123456789:;<>ABCDEFGHIJKLMNOPQRSTUVWXYZ[]_abcdefghijklmnopqrstuvwxyz~ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ'
- elseif has('ebcdic')
- let fnamechars_ok = '#$%+,-./=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ'
else
let fnamechars_ok = '#$%+,-./0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ'
endif
@@ -228,7 +223,7 @@ endfunc
func Test_reversed_range()
for re in range(0, 2)
exe 'set re=' . re
- call assert_fails('call match("abc def", "[c-a]")', 'E944:')
+ call assert_fails('call match("abc def", "[c-a]")', 'E944:', re)
endfor
set re=0
endfunc
@@ -545,7 +540,6 @@ endfunc
" Check that [[:upper:]] matches for automatic engine
func Test_match_char_class_upper()
new
- let _engine=&regexpengine
" Test 1: [[:upper:]]\{2,\}
set regexpengine=0
@@ -586,7 +580,7 @@ func Test_match_char_class_upper()
call assert_equal(4, searchcount().total, 'TEST 3 lower')
" clean up
- let &regexpengine=_engine
+ set regexpengine=0
bwipe!
endfunc
@@ -598,4 +592,13 @@ func Test_match_invalid_byte()
call delete('Xinvalid')
endfunc
+func Test_match_too_complicated()
+ set regexpengine=1
+ exe "noswapfile vsplit \xeb\xdb\x99"
+ silent! buf \&\zs*\zs*0
+ bwipe!
+ set regexpengine=0
+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..52e745438d 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()
@@ -62,7 +62,6 @@ func Test_display_registers()
call assert_match('^\nType Name Content\n'
\ . ' c "" a\n'
\ . ' c "0 ba\n'
- \ . ' c "1 b\n'
\ . ' c "a b\n'
\ . '.*'
\ . ' c "- a\n'
@@ -85,6 +84,90 @@ func Test_display_registers()
let g:clipboard = save_clipboard
endfunc
+func Test_register_one()
+ " delete a line goes into register one
+ new
+ call setline(1, "one")
+ normal dd
+ call assert_equal("one\n", @1)
+
+ " delete a word does not change register one, does change "-
+ call setline(1, "two")
+ normal de
+ call assert_equal("one\n", @1)
+ call assert_equal("two", @-)
+
+ " delete a word with a register does not change register one
+ call setline(1, "three")
+ normal "ade
+ call assert_equal("three", @a)
+ call assert_equal("one\n", @1)
+
+ " delete a word with register DOES change register one with one of a list of
+ " operators
+ " %
+ call setline(1, ["(12)3"])
+ normal "ad%
+ call assert_equal("(12)", @a)
+ call assert_equal("(12)", @1)
+
+ " (
+ call setline(1, ["first second"])
+ normal $"ad(
+ call assert_equal("first secon", @a)
+ call assert_equal("first secon", @1)
+
+ " )
+ call setline(1, ["First Second."])
+ normal gg0"ad)
+ call assert_equal("First Second.", @a)
+ call assert_equal("First Second.", @1)
+
+ " `
+ call setline(1, ["start here."])
+ normal gg0fhmx0"ad`x
+ call assert_equal("start ", @a)
+ call assert_equal("start ", @1)
+
+ " /
+ call setline(1, ["searchX"])
+ exe "normal gg0\"ad/X\<CR>"
+ call assert_equal("search", @a)
+ call assert_equal("search", @1)
+
+ " ?
+ call setline(1, ["Ysearch"])
+ exe "normal gg$\"ad?Y\<CR>"
+ call assert_equal("Ysearc", @a)
+ call assert_equal("Ysearc", @1)
+
+ " n
+ call setline(1, ["Ynext"])
+ normal gg$"adn
+ call assert_equal("Ynex", @a)
+ call assert_equal("Ynex", @1)
+
+ " N
+ call setline(1, ["prevY"])
+ normal gg0"adN
+ call assert_equal("prev", @a)
+ call assert_equal("prev", @1)
+
+ " }
+ call setline(1, ["one", ""])
+ normal gg0"ad}
+ call assert_equal("one\n", @a)
+ call assert_equal("one\n", @1)
+
+ " {
+ call setline(1, ["", "two"])
+ normal 2G$"ad{
+ call assert_equal("\ntw", @a)
+ call assert_equal("\ntw", @1)
+
+ bwipe!
+endfunc
+
func Test_recording_status_in_ex_line()
norm qx
redraw!
@@ -119,6 +202,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 +235,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 +266,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 +302,214 @@ func Test_set_register()
call setreg('=', 'b', 'a')
call assert_equal('regwrite', getreg('='))
+ " Test for setting 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 executing a register using :@ command
+func Test_execute_register()
+ call setreg('r', [])
+ call assert_beeps('@r')
+ let i = 1
+ let @q = 'let i+= 1'
+ @q
+ @
+ call assert_equal(3, i)
+
+ " cannot execute a register in operator pending mode
+ call assert_beeps('normal! c@r')
+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')
@@ -259,6 +579,82 @@ func Test_v_register()
bwipe!
endfunc
+" Test for executing the contents of a register as an Ex command with line
+" continuation.
+func Test_execute_reg_as_ex_cmd()
+ " Line continuation with just two lines
+ let code =<< trim END
+ let l = [
+ \ 1]
+ END
+ let @r = code->join("\n")
+ let l = []
+ @r
+ call assert_equal([1], l)
+
+ " Line continuation with more than two lines
+ let code =<< trim END
+ let l = [
+ \ 1,
+ \ 2,
+ \ 3]
+ END
+ let @r = code->join("\n")
+ let l = []
+ @r
+ call assert_equal([1, 2, 3], l)
+
+ " use comments interspersed with code
+ let code =<< trim END
+ let l = [
+ "\ one
+ \ 1,
+ "\ two
+ \ 2,
+ "\ three
+ \ 3]
+ END
+ let @r = code->join("\n")
+ let l = []
+ @r
+ call assert_equal([1, 2, 3], l)
+
+ " use line continuation in the middle
+ let code =<< trim END
+ let a = "one"
+ let l = [
+ \ 1,
+ \ 2]
+ let b = "two"
+ END
+ let @r = code->join("\n")
+ let l = []
+ @r
+ call assert_equal([1, 2], l)
+ call assert_equal("one", a)
+ call assert_equal("two", b)
+
+ " only one line with a \
+ let @r = "\\let l = 1"
+ call assert_fails('@r', 'E10:')
+
+ " only one line with a "\
+ let @r = ' "\ let i = 1'
+ @r
+ call assert_false(exists('i'))
+
+ " first line also begins with a \
+ let @r = "\\let l = [\n\\ 1]"
+ call assert_fails('@r', 'E10:')
+
+ " Test with a large number of lines
+ let @r = "let str = \n"
+ let @r ..= repeat(" \\ 'abcdefghijklmnopqrstuvwxyz' ..\n", 312)
+ let @r ..= ' \ ""'
+ @r
+ call assert_equal(repeat('abcdefghijklmnopqrstuvwxyz', 312), str)
+endfunc
+
func Test_ve_blockpaste()
new
set ve=all
@@ -288,84 +684,74 @@ 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)
-
+" Record in insert mode using CTRL-O
+func Test_record_in_insert_mode()
+ new
+ let @r = ''
+ call setline(1, ['foo'])
+ call feedkeys("i\<C-O>qrbaz\<C-O>q", 'xt')
+ call assert_equal('baz', @r)
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)
+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>
- 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)
+ bwipe!
+endfunc
- 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)
+" mapping that ends macro recording should be removed from recorded macro
+func Test_end_record_using_mapping()
+ call setline(1, 'aaa')
+ nnoremap s q
+ call feedkeys('safas', 'tx')
+ call assert_equal('fa', @a)
+ nunmap s
- 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)
+ nnoremap xx q
+ call feedkeys('0xxafaxx', 'tx')
+ call assert_equal('fa', @a)
+ nunmap xx
- let @x = 'one'
- call setreg('x', {})
- call assert_equal(1, len(split(execute('reg x'), '\n')))
+ nnoremap xsx q
+ call feedkeys('0qafaxsx', 'tx')
+ call assert_equal('fa', @a)
+ nunmap xsx
- 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_end_reg_executing()
+ nnoremap s <Nop>
+ let @a = 's'
+ call feedkeys("@aqaq\<Esc>", 'tx')
+ call assert_equal('', @a)
+ call assert_equal('', getline(1))
+
+ call setline(1, 'aaa')
+ nnoremap s qa
+ let @a = 'fa'
+ call feedkeys("@asq\<Esc>", 'tx')
+ call assert_equal('', @a)
+ call assert_equal('aaa', getline(1))
+
+ nunmap s
bwipe!
endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_reltime.vim b/src/nvim/testdir/test_reltime.vim
index 37b9e783c6..b381f1ddbb 100644
--- a/src/nvim/testdir/test_reltime.vim
+++ b/src/nvim/testdir/test_reltime.vim
@@ -1,8 +1,8 @@
" Tests for reltime()
-if !has('reltime') || !has('float')
- finish
-endif
+source check.vim
+CheckFeature reltime
+CheckFeature float
func Test_reltime()
let now = reltime()
diff --git a/src/nvim/testdir/test_rename.vim b/src/nvim/testdir/test_rename.vim
index 3887fcfabf..5359b84923 100644
--- a/src/nvim/testdir/test_rename.vim
+++ b/src/nvim/testdir/test_rename.vim
@@ -1,5 +1,7 @@
" Test rename()
+source shared.vim
+
func Test_rename_file_to_file()
call writefile(['foo'], 'Xrename1')
@@ -81,7 +83,7 @@ func Test_rename_copy()
call assert_equal(0, rename('Xrenamedir/Xrenamefile', 'Xrenamefile'))
- if !has('win32')
+ if !has('win32') && !IsRoot()
" On Windows, the source file is removed despite
" its directory being made not writable.
call assert_equal(['foo'], readfile('Xrenamedir/Xrenamefile'))
diff --git a/src/nvim/testdir/test_retab.vim b/src/nvim/testdir/test_retab.vim
index f11a32bade..1650a03876 100644
--- a/src/nvim/testdir/test_retab.vim
+++ b/src/nvim/testdir/test_retab.vim
@@ -69,9 +69,33 @@ func Test_retab()
call assert_equal(" a b c ", Retab('!', 3))
call assert_equal(" a b c ", Retab('', 5))
call assert_equal(" a b c ", Retab('!', 5))
+
+ set tabstop& expandtab&
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
+
+func Test_retab_endless()
+ new
+ call setline(1, "\t0\t")
+ let caught = 'no'
+ try
+ while 1
+ set ts=4000
+ retab 4
+ endwhile
+ catch /E1240/
+ let caught = 'yes'
+ endtry
+ bwipe!
+ set tabstop&
+endfunc
+
+
+" vim: shiftwidth=2 sts=2 expandtab
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_search.vim b/src/nvim/testdir/test_search.vim
index c796f1f676..3d1bbfb726 100644
--- a/src/nvim/testdir/test_search.vim
+++ b/src/nvim/testdir/test_search.vim
@@ -19,9 +19,9 @@ func Test_search_cmdline()
set noincsearch
:1
call feedkeys("/foobar\<cr>", 'tx')
- call feedkeys("/the\<cr>",'tx')
+ call feedkeys("/the\<cr>", 'tx')
call assert_equal('the', @/)
- call feedkeys("/thes\<C-P>\<C-P>\<cr>",'tx')
+ call feedkeys("/thes\<C-P>\<C-P>\<cr>", 'tx')
call assert_equal('foobar', @/)
" Test 2
@@ -292,15 +292,84 @@ endfunc
func Test_searchpair()
new
- call setline(1, ['other code here', '', '[', '" cursor here', ']'])
+ call setline(1, ['other code', 'here [', ' [', ' " cursor here', ' ]]'])
+
+ 4
+ call assert_equal(3, searchpair('\[', '', ']', 'bW'))
+ call assert_equal([0, 3, 2, 0], getpos('.'))
+ 4
+ call assert_equal(2, searchpair('\[', '', ']', 'bWr'))
+ call assert_equal([0, 2, 6, 0], getpos('.'))
+ 4
+ call assert_equal(1, searchpair('\[', '', ']', 'bWm'))
+ call assert_equal([0, 3, 2, 0], getpos('.'))
+ 4|norm ^
+ call assert_equal(5, searchpair('\[', '', ']', 'Wn'))
+ call assert_equal([0, 4, 2, 0], getpos('.'))
+ 4
+ call assert_equal(2, searchpair('\[', '', ']', 'bW',
+ \ 'getline(".") =~ "^\\s*\["'))
+ call assert_equal([0, 2, 6, 0], getpos('.'))
+ set nomagic
+ 4
+ call assert_equal(3, searchpair('\[', '', ']', 'bW'))
+ call assert_equal([0, 3, 2, 0], getpos('.'))
+ set magic
+ 4|norm ^
+ call assert_equal(0, searchpair('{', '', '}', 'bW'))
+ call assert_equal([0, 4, 2, 0], getpos('.'))
+
+ %d
+ call setline(1, ['if 1', ' if 2', ' else', ' endif 2', 'endif 1'])
+
+ /\<if 1
+ call assert_equal(5, searchpair('\<if\>', '\<else\>', '\<endif\>', 'W'))
+ call assert_equal([0, 5, 1, 0], getpos('.'))
+ /\<if 2
+ call assert_equal(3, searchpair('\<if\>', '\<else\>', '\<endif\>', 'W'))
+ call assert_equal([0, 3, 3, 0], getpos('.'))
+
+ q!
+endfunc
+
+func Test_searchpairpos()
+ new
+ call setline(1, ['other code', 'here [', ' [', ' " cursor here', ' ]]'])
+
4
- let a = searchpair('\[','',']','bW')
- call assert_equal(3, a)
+ call assert_equal([3, 2], searchpairpos('\[', '', ']', 'bW'))
+ call assert_equal([0, 3, 2, 0], getpos('.'))
+ 4
+ call assert_equal([2, 6], searchpairpos('\[', '', ']', 'bWr'))
+ call assert_equal([0, 2, 6, 0], getpos('.'))
+ 4|norm ^
+ call assert_equal([5, 2], searchpairpos('\[', '', ']', 'Wn'))
+ call assert_equal([0, 4, 2, 0], getpos('.'))
+ 4
+ call assert_equal([2, 6], searchpairpos('\[', '', ']', 'bW',
+ \ 'getline(".") =~ "^\\s*\["'))
+ call assert_equal([0, 2, 6, 0], getpos('.'))
+ 4
+ call assert_equal([2, 6], searchpairpos('\[', '', ']', 'bWr'))
+ call assert_equal([0, 2, 6, 0], getpos('.'))
set nomagic
4
- let a = searchpair('\[','',']','bW')
- call assert_equal(3, a)
+ call assert_equal([3, 2], searchpairpos('\[', '', ']', 'bW'))
+ call assert_equal([0, 3, 2, 0], getpos('.'))
set magic
+ 4|norm ^
+ call assert_equal([0, 0], searchpairpos('{', '', '}', 'bW'))
+ call assert_equal([0, 4, 2, 0], getpos('.'))
+
+ %d
+ call setline(1, ['if 1', ' if 2', ' else', ' endif 2', 'endif 1'])
+ /\<if 1
+ call assert_equal([5, 1], searchpairpos('\<if\>', '\<else\>', '\<endif\>', 'W'))
+ call assert_equal([0, 5, 1, 0], getpos('.'))
+ /\<if 2
+ call assert_equal([3, 3], searchpairpos('\<if\>', '\<else\>', '\<endif\>', 'W'))
+ call assert_equal([0, 3, 3, 0], getpos('.'))
+
q!
endfunc
@@ -309,17 +378,29 @@ func Test_searchpair_errors()
call assert_fails("call searchpair('start', {-> 0}, 'end', 'bW', 'skip', 99, 100)", 'E729: using Funcref as a String')
call assert_fails("call searchpair('start', 'middle', {'one': 1}, 'bW', 'skip', 99, 100)", 'E731: using Dictionary as a String')
call assert_fails("call searchpair('start', 'middle', 'end', 'flags', 'skip', 99, 100)", 'E475: Invalid argument: flags')
- call assert_fails("call searchpair('start', 'middle', 'end', 'bW', 0, 99, 100)", 'E475: Invalid argument: 0')
call assert_fails("call searchpair('start', 'middle', 'end', 'bW', 'func', -99, 100)", 'E475: Invalid argument: -99')
call assert_fails("call searchpair('start', 'middle', 'end', 'bW', 'func', 99, -100)", 'E475: Invalid argument: -100')
+ call assert_fails("call searchpair('start', 'middle', 'end', 'e')", 'E475: Invalid argument: e')
+ call assert_fails("call searchpair('start', 'middle', 'end', 'sn')", 'E475: Invalid argument: sn')
+endfunc
+
+func Test_searchpairpos_errors()
+ call assert_fails("call searchpairpos([0], 'middle', 'end', 'bW', 'skip', 99, 100)", 'E730: using List as a String')
+ call assert_fails("call searchpairpos('start', {-> 0}, 'end', 'bW', 'skip', 99, 100)", 'E729: using Funcref as a String')
+ call assert_fails("call searchpairpos('start', 'middle', {'one': 1}, 'bW', 'skip', 99, 100)", 'E731: using Dictionary as a String')
+ call assert_fails("call searchpairpos('start', 'middle', 'end', 'flags', 'skip', 99, 100)", 'E475: Invalid argument: flags')
+ call assert_fails("call searchpairpos('start', 'middle', 'end', 'bW', 'func', -99, 100)", 'E475: Invalid argument: -99')
+ call assert_fails("call searchpairpos('start', 'middle', 'end', 'bW', 'func', 99, -100)", 'E475: Invalid argument: -100')
+ call assert_fails("call searchpairpos('start', 'middle', 'end', 'e')", 'E475: Invalid argument: e')
+ call assert_fails("call searchpairpos('start', 'middle', 'end', 'sn')", 'E475: Invalid argument: sn')
endfunc
func Test_searchpair_skip()
func Zero()
- return 0
+ return 0
endfunc
func Partial(x)
- return a:x
+ return a:x
endfunc
new
call setline(1, ['{', 'foo', 'foo', 'foo', '}'])
@@ -574,10 +655,49 @@ func Test_search_cmdline7()
bw!
endfunc
-" Tests for regexp with various magic settings
-func Test_search_regexp()
- enew!
+func Test_search_cmdline8()
+ " Highlighting is cleared in all windows
+ " since hls applies to all windows
+ CheckOption incsearch
+ CheckFeature terminal
+ CheckNotGui
+ if has("win32")
+ throw "Skipped: Bug with sending <ESC> to terminal window not fixed yet"
+ endif
+ let h = winheight(0)
+ if h < 3
+ return
+ endif
+ " Prepare buffer text
+ let lines = ['abb vim vim vi', 'vimvivim']
+ call writefile(lines, 'Xsearch.txt')
+ let buf = term_start([GetVimProg(), '--clean', '-c', 'set noswapfile', 'Xsearch.txt'], {'term_rows': 3})
+
+ call WaitForAssert({-> assert_equal(lines, [term_getline(buf, 1), term_getline(buf, 2)])})
+
+ call term_sendkeys(buf, ":set incsearch hlsearch\<cr>")
+ call term_sendkeys(buf, ":14vsp\<cr>")
+ call term_sendkeys(buf, "/vim\<cr>")
+ call term_sendkeys(buf, "/b\<esc>")
+ call term_sendkeys(buf, "gg0")
+ call TermWait(buf, 250)
+ let screen_line = term_scrape(buf, 1)
+ let [a0,a1,a2,a3] = [screen_line[3].attr, screen_line[4].attr,
+ \ screen_line[18].attr, screen_line[19].attr]
+ call assert_notequal(a0, a1)
+ call assert_notequal(a0, a3)
+ call assert_notequal(a1, a2)
+ call assert_equal(a0, a2)
+ call assert_equal(a1, a3)
+ " clean up
+ call delete('Xsearch.txt')
+
+ bwipe!
+endfunc
+
+" Tests for regexp with various magic settings
+func Run_search_regexp_magic_opt()
put ='1 a aa abb abbccc'
exe 'normal! /a*b\{2}c\+/e' . "\<CR>"
call assert_equal([0, 2, 17, 0], getpos('.'))
@@ -612,6 +732,18 @@ func Test_search_regexp()
exe 'normal! /\V[ab]\(\[xy]\)\1' . "\<CR>"
call assert_equal([0, 9, 7, 0], getpos('.'))
+ %d
+endfunc
+
+func Test_search_regexp()
+ enew!
+
+ set regexpengine=1
+ call Run_search_regexp_magic_opt()
+ set regexpengine=2
+ call Run_search_regexp_magic_opt()
+ set regexpengine&
+
set undolevels=100
put ='9 foobar'
put =''
@@ -619,12 +751,12 @@ func Test_search_regexp()
normal G
exe 'normal! dv?bar?' . "\<CR>"
call assert_equal('9 foo', getline('.'))
- call assert_equal([0, 10, 5, 0], getpos('.'))
- call assert_equal(10, line('$'))
+ call assert_equal([0, 2, 5, 0], getpos('.'))
+ call assert_equal(2, line('$'))
normal u
call assert_equal('9 foobar', getline('.'))
- call assert_equal([0, 10, 6, 0], getpos('.'))
- call assert_equal(11, line('$'))
+ call assert_equal([0, 2, 6, 0], getpos('.'))
+ call assert_equal(3, line('$'))
set undolevels&
enew!
@@ -844,6 +976,26 @@ func Test_hlsearch_block_visual_match()
call delete('Xhlsearch_block')
endfunc
+func Test_hlsearch_and_visual()
+ CheckOption hlsearch
+ CheckScreendump
+
+ call writefile([
+ \ 'set hlsearch',
+ \ 'call setline(1, repeat(["xxx yyy zzz"], 3))',
+ \ 'hi Search cterm=bold',
+ \ '/yyy',
+ \ 'call cursor(1, 6)',
+ \ ], 'Xhlvisual_script')
+ let buf = RunVimInTerminal('-S Xhlvisual_script', {'rows': 6, 'cols': 40})
+ call term_sendkeys(buf, "vjj")
+ call VerifyScreenDump(buf, 'Test_hlsearch_visual_1', {})
+ call term_sendkeys(buf, "\<Esc>")
+
+ call StopVimInTerminal(buf)
+ call delete('Xhlvisual_script')
+endfunc
+
func Test_incsearch_substitute()
CheckFunction test_override
CheckOption incsearch
@@ -865,6 +1017,51 @@ func Test_incsearch_substitute()
call Incsearch_cleanup()
endfunc
+func Test_hlsearch_cursearch()
+ CheckScreendump
+
+ let lines =<< trim END
+ set hlsearch scrolloff=0
+ call setline(1, ['one', 'foo', 'bar', 'baz', 'foo the foo and foo', 'bar'])
+ hi Search ctermbg=yellow
+ hi CurSearch ctermbg=blue
+ END
+ call writefile(lines, 'Xhlsearch_cursearch')
+ let buf = RunVimInTerminal('-S Xhlsearch_cursearch', {'rows': 9, 'cols': 60})
+
+ call term_sendkeys(buf, "gg/foo\<CR>")
+ call VerifyScreenDump(buf, 'Test_hlsearch_cursearch_single_line_1', {})
+
+ call term_sendkeys(buf, "n")
+ call VerifyScreenDump(buf, 'Test_hlsearch_cursearch_single_line_2', {})
+
+ call term_sendkeys(buf, "n")
+ call VerifyScreenDump(buf, 'Test_hlsearch_cursearch_single_line_2a', {})
+
+ call term_sendkeys(buf, "n")
+ call VerifyScreenDump(buf, 'Test_hlsearch_cursearch_single_line_2b', {})
+
+ call term_sendkeys(buf, ":call setline(5, 'foo')\<CR>")
+ call term_sendkeys(buf, "0?\<CR>")
+ call VerifyScreenDump(buf, 'Test_hlsearch_cursearch_single_line_3', {})
+
+ call term_sendkeys(buf, "gg/foo\\nbar\<CR>")
+ call VerifyScreenDump(buf, 'Test_hlsearch_cursearch_multiple_line_1', {})
+
+ call term_sendkeys(buf, ":call setline(1, ['---', 'abcdefg', 'hijkl', '---', 'abcdefg', 'hijkl'])\<CR>")
+ call term_sendkeys(buf, "gg/efg\\nhij\<CR>")
+ call VerifyScreenDump(buf, 'Test_hlsearch_cursearch_multiple_line_2', {})
+ call term_sendkeys(buf, "h\<C-L>")
+ call VerifyScreenDump(buf, 'Test_hlsearch_cursearch_multiple_line_3', {})
+ call term_sendkeys(buf, "j\<C-L>")
+ call VerifyScreenDump(buf, 'Test_hlsearch_cursearch_multiple_line_4', {})
+ call term_sendkeys(buf, "h\<C-L>")
+ call VerifyScreenDump(buf, 'Test_hlsearch_cursearch_multiple_line_5', {})
+
+ call StopVimInTerminal(buf)
+ call delete('Xhlsearch_cursearch')
+endfunc
+
" Similar to Test_incsearch_substitute() but with a screendump halfway.
func Test_incsearch_substitute_dump()
CheckOption incsearch
@@ -1192,7 +1389,7 @@ endfunc
" This was causing E874. Also causes an invalid read?
func Test_look_behind()
new
- call setline(1, '0\|\&\n\@<=')
+ call setline(1, '0\|\&\n\@<=')
call search(getline("."))
bwipe!
endfunc
@@ -1236,11 +1433,11 @@ endfunc
func Test_search_Ctrl_L_combining()
" Make sure, that Ctrl-L works correctly with combining characters.
" It uses an artificial example of an 'a' with 4 combining chars:
- " 'a' U+0061 Dec:97 LATIN SMALL LETTER A &#x61; /\%u61\Z "\u0061"
+ " 'a' U+0061 Dec:97 LATIN SMALL LETTER A &#x61; /\%u61\Z "\u0061"
" ' ̀' U+0300 Dec:768 COMBINING GRAVE ACCENT &#x300; /\%u300\Z "\u0300"
" ' ́' U+0301 Dec:769 COMBINING ACUTE ACCENT &#x301; /\%u301\Z "\u0301"
" ' ̇' U+0307 Dec:775 COMBINING DOT ABOVE &#x307; /\%u307\Z "\u0307"
- " ' ̣' U+0323 Dec:803 COMBINING DOT BELOW &#x323; /\%u323 "\u0323"
+ " ' ̣' U+0323 Dec:803 COMBINING DOT BELOW &#x323; /\%u323 "\u0323"
" Those should also appear on the commandline
CheckOption incsearch
@@ -1287,7 +1484,7 @@ func Test_large_hex_chars2()
endfunc
func Test_one_error_msg()
- " This was also giving an internal error
+ " This was also giving an internal error
call assert_fails('call search(" \\((\\v[[=P=]]){185}+ ")', 'E871:')
endfunc
@@ -1332,6 +1529,20 @@ func Test_search_match_at_curpos()
close!
endfunc
+" Test for error cases with the search() function
+func Test_search_errors()
+ call assert_fails("call search('pat', [])", 'E730:')
+ call assert_fails("call search('pat', 'b', {})", 'E728:')
+ call assert_fails("call search('pat', 'b', 1, [])", 'E745:')
+ call assert_fails("call search('pat', 'ns')", 'E475:')
+ call assert_fails("call search('pat', 'mr')", 'E475:')
+
+ new
+ call setline(1, ['foo', 'bar'])
+ call assert_fails('call feedkeys("/foo/;/bar/;\<CR>", "tx")', 'E386:')
+ bwipe!
+endfunc
+
func Test_search_display_pattern()
new
call setline(1, ['foo', 'bar', 'foobar'])
@@ -1395,6 +1606,160 @@ func Test_search_special()
exe "norm /\x80PS"
endfunc
+" Test for command failures when the last search pattern is not set.
+" Need to run this in a new vim instance where last search pattern is not set.
+func Test_search_with_no_last_pat()
+ let lines =<< trim [SCRIPT]
+ call assert_fails("normal i\<C-R>/\e", 'E35:')
+ call assert_fails("exe '/'", 'E35:')
+ call assert_fails("exe '?'", 'E35:')
+ call assert_fails("/", 'E35:')
+ call assert_fails("?", 'E35:')
+ call assert_fails("normal n", 'E35:')
+ call assert_fails("normal N", 'E35:')
+ call assert_fails("normal gn", 'E35:')
+ call assert_fails("normal gN", 'E35:')
+ call assert_fails("normal cgn", 'E35:')
+ call assert_fails("normal cgN", 'E35:')
+ let p = []
+ let p = @/
+ call assert_equal('', p)
+ call assert_fails("normal :\<C-R>/", 'E35:')
+ call assert_fails("//p", 'E35:')
+ call assert_fails(";//p", 'E35:')
+ call assert_fails("??p", 'E35:')
+ call assert_fails(";??p", 'E35:')
+ call assert_fails('g//p', 'E476:')
+ call assert_fails('v//p', 'E476:')
+ call writefile(v:errors, 'Xresult')
+ qall!
+ [SCRIPT]
+ call writefile(lines, 'Xscript')
+
+ if RunVim([], [], '--clean -S Xscript')
+ call assert_equal([], readfile('Xresult'))
+ endif
+ call delete('Xscript')
+ call delete('Xresult')
+endfunc
+
+" Test for using tilde (~) atom in search. This should use the last used
+" substitute pattern
+func Test_search_tilde_pat()
+ let lines =<< trim [SCRIPT]
+ set regexpengine=1
+ call assert_fails('exe "normal /~\<CR>"', 'E33:')
+ call assert_fails('exe "normal ?~\<CR>"', 'E33:')
+ set regexpengine=2
+ call assert_fails('exe "normal /~\<CR>"', 'E383:')
+ call assert_fails('exe "normal ?~\<CR>"', 'E383:')
+ set regexpengine&
+ call writefile(v:errors, 'Xresult')
+ qall!
+ [SCRIPT]
+ call writefile(lines, 'Xscript')
+ if RunVim([], [], '--clean -S Xscript')
+ call assert_equal([], readfile('Xresult'))
+ endif
+ call delete('Xscript')
+ call delete('Xresult')
+endfunc
+
+" Test for searching a pattern that is not present with 'nowrapscan'
+func Test_search_pat_not_found()
+ new
+ set nowrapscan
+ let @/ = '1abcxyz2'
+ call assert_fails('normal n', 'E385:')
+ call assert_fails('normal N', 'E384:')
+ set wrapscan&
+ close
+endfunc
+
+" Test for v:searchforward variable
+func Test_searchforward_var()
+ new
+ call setline(1, ['foo', '', 'foo'])
+ call cursor(2, 1)
+ let @/ = 'foo'
+ let v:searchforward = 0
+ normal N
+ call assert_equal(3, line('.'))
+ call cursor(2, 1)
+ let v:searchforward = 1
+ normal N
+ call assert_equal(1, line('.'))
+ close!
+endfunc
+
+" Test for invalid regular expressions
+func Test_invalid_regexp()
+ set regexpengine=1
+ call assert_fails("call search(repeat('\\(.\\)', 10))", 'E51:')
+ call assert_fails("call search('\\%(')", 'E53:')
+ call assert_fails("call search('\\(')", 'E54:')
+ call assert_fails("call search('\\)')", 'E55:')
+ call assert_fails("call search('x\\@#')", 'E59:')
+ call assert_fails('call search(''\v%(%(%(%(%(%(%(%(%(%(%(a){1}){1}){1}){1}){1}){1}){1}){1}){1}){1}){1}'')', 'E60:')
+ call assert_fails("call search('a\\+*')", 'E61:')
+ call assert_fails("call search('\\_m')", 'E63:')
+ call assert_fails("call search('\\+')", 'E64:')
+ call assert_fails("call search('\\1')", 'E65:')
+ call assert_fails("call search('\\z\\(\\)')", 'E66:')
+ call assert_fails("call search('\\z2')", 'E67:')
+ call assert_fails("call search('\\zx')", 'E68:')
+ call assert_fails("call search('\\%[ab')", 'E69:')
+ call assert_fails("call search('\\%[]')", 'E70:')
+ call assert_fails("call search('\\%a')", 'E71:')
+ call assert_fails("call search('ab\\%[\\(cd\\)]')", 'E369:')
+ call assert_fails("call search('ab\\%[\\%(cd\\)]')", 'E369:')
+ set regexpengine=2
+ call assert_fails("call search('\\_')", 'E865:')
+ call assert_fails("call search('\\+')", 'E866:')
+ call assert_fails("call search('\\zx')", 'E867:')
+ call assert_fails("call search('\\%a')", 'E867:')
+ call assert_fails("call search('x\\@#')", 'E869:')
+ call assert_fails("call search(repeat('\\(.\\)', 10))", 'E872:')
+ call assert_fails("call search('\\_m')", 'E877:')
+ call assert_fails("call search('\\%(')", 'E53:')
+ call assert_fails("call search('\\(')", 'E54:')
+ call assert_fails("call search('\\)')", 'E55:')
+ call assert_fails("call search('\\z\\(\\)')", 'E66:')
+ call assert_fails("call search('\\%[ab')", 'E69:')
+ call assert_fails("call search('\\%[]')", 'E70:')
+ call assert_fails("call search('\\%9999999999999999999999999999v')", 'E951:')
+ set regexpengine&
+ call assert_fails("call search('\\%#=3ab')", 'E864:')
+endfunc
+
+" Test for searching with 'smartcase' and 'ignorecase'
+func Test_search_smartcase()
+ new
+ call setline(1, ['', 'Hello'])
+ set noignorecase nosmartcase
+ call assert_fails('exe "normal /\\a\\_.\\(.*\\)O\<CR>"', 'E486:')
+
+ set ignorecase nosmartcase
+ exe "normal /\\a\\_.\\(.*\\)O\<CR>"
+ call assert_equal([2, 1], [line('.'), col('.')])
+
+ call cursor(1, 1)
+ set ignorecase smartcase
+ call assert_fails('exe "normal /\\a\\_.\\(.*\\)O\<CR>"', 'E486:')
+
+ exe "normal /\\a\\_.\\(.*\\)o\<CR>"
+ call assert_equal([2, 1], [line('.'), col('.')])
+
+ " Test for using special atoms with 'smartcase'
+ call setline(1, ['', ' Hello\ '])
+ call cursor(1, 1)
+ call feedkeys('/\_.\%(\uello\)\' .. "\<CR>", 'xt')
+ call assert_equal([2, 4], [line('.'), col('.')])
+
+ set ignorecase& smartcase&
+ close!
+endfun
+
" Test 'smartcase' with utf-8.
func Test_search_smartcase_utf8()
new
@@ -1414,6 +1779,102 @@ func Test_search_smartcase_utf8()
close!
endfunc
+" Test searching past the end of a file
+func Test_search_past_eof()
+ new
+ call setline(1, ['Line'])
+ exe "normal /\\n\\zs\<CR>"
+ call assert_equal([1, 4], [line('.'), col('.')])
+ close!
+endfunc
+
+" Test for various search offsets
+func Test_search_offset()
+ " With /e, for a match in the first column of a line, the cursor should be
+ " placed at the end of the previous line.
+ new
+ call setline(1, ['one two', 'three four'])
+ call search('two\_.', 'e')
+ call assert_equal([1, 7], [line('.'), col('.')])
+
+ " with cursor at the beginning of the file, use /s+1
+ call cursor(1, 1)
+ exe "normal /two/s+1\<CR>"
+ call assert_equal([1, 6], [line('.'), col('.')])
+
+ " with cursor at the end of the file, use /e-1
+ call cursor(2, 10)
+ exe "normal ?three?e-1\<CR>"
+ call assert_equal([2, 4], [line('.'), col('.')])
+
+ " line offset - after the last line
+ call cursor(1, 1)
+ exe "normal /three/+1\<CR>"
+ call assert_equal([2, 1], [line('.'), col('.')])
+
+ " line offset - before the first line
+ call cursor(2, 1)
+ exe "normal ?one?-1\<CR>"
+ call assert_equal([1, 1], [line('.'), col('.')])
+
+ " character offset - before the first character in the file
+ call cursor(2, 1)
+ exe "normal ?one?s-1\<CR>"
+ call assert_equal([1, 1], [line('.'), col('.')])
+ call cursor(2, 1)
+ exe "normal ?one?e-3\<CR>"
+ call assert_equal([1, 1], [line('.'), col('.')])
+
+ " character offset - after the last character in the file
+ call cursor(1, 1)
+ exe "normal /four/s+4\<CR>"
+ call assert_equal([2, 10], [line('.'), col('.')])
+ call cursor(1, 1)
+ exe "normal /four/e+1\<CR>"
+ call assert_equal([2, 10], [line('.'), col('.')])
+
+ close!
+endfunc
+
+" Test for searching for matching parenthesis using %
+func Test_search_match_paren()
+ new
+ call setline(1, "abc(def')'ghi'('jk'\\t'lm)no")
+ " searching for a matching parenthesis should skip single quoted characters
+ call cursor(1, 4)
+ normal %
+ call assert_equal([1, 25], [line('.'), col('.')])
+ normal %
+ call assert_equal([1, 4], [line('.'), col('.')])
+ call cursor(1, 5)
+ normal ])
+ call assert_equal([1, 25], [line('.'), col('.')])
+ call cursor(1, 24)
+ normal [(
+ call assert_equal([1, 4], [line('.'), col('.')])
+
+ " matching parenthesis in 'virtualedit' mode with cursor after the eol
+ call setline(1, 'abc(defgh)')
+ set virtualedit=all
+ normal 20|%
+ call assert_equal(4, col('.'))
+ set virtualedit&
+ close!
+endfunc
+
+" Test for searching a pattern and stopping before a specified line
+func Test_search_stopline()
+ new
+ call setline(1, ['', '', '', 'vim'])
+ call assert_equal(0, search('vim', 'n', 3))
+ call assert_equal(4, search('vim', 'n', 4))
+ call setline(1, ['vim', '', '', ''])
+ call cursor(4, 1)
+ call assert_equal(0, search('vim', 'bn', 2))
+ call assert_equal(1, search('vim', 'bn', 1))
+ close!
+endfunc
+
func Test_incsearch_highlighting_newline()
CheckRunVimInTerminal
CheckOption incsearch
@@ -1446,4 +1907,31 @@ func Test_incsearch_highlighting_newline()
bw
endfunc
+func Test_no_last_search_pattern()
+ CheckOption incsearch
+
+ let @/ = ""
+ set incsearch
+ " these were causing a crash
+ call feedkeys("//\<C-G>", 'xt')
+ call feedkeys("//\<C-T>", 'xt')
+ call feedkeys("??\<C-G>", 'xt')
+ call feedkeys("??\<C-T>", 'xt')
+endfunc
+
+func Test_search_with_invalid_range()
+ new
+ let lines =<< trim END
+ /\%.v
+ 5/
+ c
+ END
+ call writefile(lines, 'Xrangesearch')
+ source Xrangesearch
+
+ bwipe!
+ call delete('Xrangesearch')
+endfunc
+
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_search_stat.vim b/src/nvim/testdir/test_search_stat.vim
index f7f7467e91..89e09cf85b 100644
--- a/src/nvim/testdir/test_search_stat.vim
+++ b/src/nvim/testdir/test_search_stat.vim
@@ -1,14 +1,15 @@
" Tests for search_stats, when "S" is not in 'shortmess'
-source screendump.vim
source check.vim
+source screendump.vim
func Test_search_stat()
new
set shortmess-=S
" Append 50 lines with text to search for, "foobar" appears 20 times
call append(0, repeat(['foobar', 'foo', 'fooooobar', 'foba', 'foobar'], 10))
- call nvim_win_set_cursor(0, [1, 0])
+
+ call cursor(1, 1)
" searchcount() returns an empty dictionary when previous pattern was not set
call assert_equal({}, searchcount(#{pattern: ''}))
@@ -45,7 +46,6 @@ func Test_search_stat()
\ searchcount(#{pattern: 'fooooobar', maxcount: 1}))
" match at second line
- call cursor(1, 1)
let messages_before = execute('messages')
let @/ = 'fo*\(bar\?\)\?'
let g:a = execute(':unsilent :norm! n')
@@ -262,6 +262,34 @@ func Test_searchcount_fails()
call assert_fails('echo searchcount("boo!")', 'E715:')
endfunc
+func Test_searchcount_in_statusline()
+ CheckScreendump
+
+ let lines =<< trim END
+ set shortmess-=S
+ call append(0, 'this is something')
+ function TestSearchCount() abort
+ let search_count = searchcount()
+ if !empty(search_count)
+ return '[' . search_count.current . '/' . search_count.total . ']'
+ else
+ return ''
+ endif
+ endfunction
+ set hlsearch
+ set laststatus=2 statusline+=%{TestSearchCount()}
+ END
+ call writefile(lines, 'Xsearchstatusline')
+ let buf = RunVimInTerminal('-S Xsearchstatusline', #{rows: 10})
+ call TermWait(buf)
+ call term_sendkeys(buf, "/something")
+ call VerifyScreenDump(buf, 'Test_searchstat_4', {})
+
+ call term_sendkeys(buf, "\<Esc>")
+ call StopVimInTerminal(buf)
+ call delete('Xsearchstatusline')
+endfunc
+
func Test_search_stat_foldopen()
CheckScreendump
@@ -319,30 +347,71 @@ func! Test_search_stat_screendump()
call delete('Xsearchstat')
endfunc
-func Test_searchcount_in_statusline()
+func Test_search_stat_then_gd()
CheckScreendump
let lines =<< trim END
+ call setline(1, ['int cat;', 'int dog;', 'cat = dog;'])
set shortmess-=S
- call append(0, 'this is something')
- function TestSearchCount() abort
- let search_count = searchcount()
- if !empty(search_count)
- return '[' . search_count.current . '/' . search_count.total . ']'
- else
- return ''
- endif
- endfunction
set hlsearch
- set laststatus=2 statusline+=%{TestSearchCount()}
END
- call writefile(lines, 'Xsearchstatusline')
- let buf = RunVimInTerminal('-S Xsearchstatusline', #{rows: 10})
+ call writefile(lines, 'Xsearchstatgd')
+
+ let buf = RunVimInTerminal('-S Xsearchstatgd', #{rows: 10})
+ call term_sendkeys(buf, "/dog\<CR>")
call TermWait(buf)
- call term_sendkeys(buf, "/something")
- call VerifyScreenDump(buf, 'Test_searchstat_4', {})
+ call VerifyScreenDump(buf, 'Test_searchstatgd_1', {})
+
+ call term_sendkeys(buf, "G0gD")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_searchstatgd_2', {})
- call term_sendkeys(buf, "\<Esc>")
call StopVimInTerminal(buf)
- call delete('Xsearchstatusline')
+ call delete('Xsearchstatgd')
endfunc
+
+func Test_search_stat_and_incsearch()
+ CheckScreendump
+
+ let lines =<< trim END
+ call setline(1, ['abc--c', '--------abc', '--abc'])
+ set hlsearch
+ set incsearch
+ set bg=dark
+ set showtabline=2
+
+ function MyTabLine()
+ try
+ let a=searchcount(#{recompute: 1, maxcount: -1})
+ return a.current .. '/' .. a.total
+ catch
+ return ''
+ endtry
+ endfunction
+
+ set tabline=%!MyTabLine()
+ END
+ call writefile(lines, 'Xsearchstat_inc')
+
+ let buf = RunVimInTerminal('-S Xsearchstat_inc', #{rows: 10})
+ call term_sendkeys(buf, "/abc")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_searchstat_inc_1', {})
+
+ call term_sendkeys(buf, "\<c-g>")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_searchstat_inc_2', {})
+
+ call term_sendkeys(buf, "\<c-g>")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_searchstat_inc_3', {})
+
+ call term_sendkeys(buf, "\<esc>:qa\<cr>")
+ call TermWait(buf)
+
+ call StopVimInTerminal(buf)
+ call delete('Xsearchstat_inc')
+endfunc
+
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_searchpos.vim b/src/nvim/testdir/test_searchpos.vim
new file mode 100644
index 0000000000..dd13c305c5
--- /dev/null
+++ b/src/nvim/testdir/test_searchpos.vim
@@ -0,0 +1,30 @@
+" Tests for searchpos()
+
+func Test_searchpos()
+ new one
+ 0put ='1a3'
+ 1put ='123xyz'
+ call cursor(1, 1)
+ call assert_equal([1, 1, 2], searchpos('\%(\([a-z]\)\|\_.\)\{-}xyz', 'pcW'))
+ call cursor(1, 2)
+ call assert_equal([2, 1, 1], '\%(\([a-z]\)\|\_.\)\{-}xyz'->searchpos('pcW'))
+ set cpo-=c
+ call cursor(1, 2)
+ call assert_equal([1, 2, 2], searchpos('\%(\([a-z]\)\|\_.\)\{-}xyz', 'pcW'))
+ call cursor(1, 3)
+ call assert_equal([1, 3, 1], searchpos('\%(\([a-z]\)\|\_.\)\{-}xyz', 'pcW'))
+
+ " Now with \zs, first match is in column 0, "a" is matched.
+ call cursor(1, 3)
+ call assert_equal([2, 4, 2], searchpos('\%(\([a-z]\)\|\_.\)\{-}\zsxyz', 'pcW'))
+ " With z flag start at cursor column, don't see the "a".
+ call cursor(1, 3)
+ call assert_equal([2, 4, 1], searchpos('\%(\([a-z]\)\|\_.\)\{-}\zsxyz', 'pcWz'))
+
+ set cpo+=c
+ " close the window
+ q!
+
+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_set.vim b/src/nvim/testdir/test_set.vim
new file mode 100644
index 0000000000..2b1e9eeee0
--- /dev/null
+++ b/src/nvim/testdir/test_set.vim
@@ -0,0 +1,29 @@
+" Tests for the :set command
+
+function Test_set_backslash()
+ let isk_save = &isk
+
+ set isk=a,b,c
+ set isk+=d
+ call assert_equal('a,b,c,d', &isk)
+ set isk+=\\,e
+ call assert_equal('a,b,c,d,\,e', &isk)
+ set isk-=e
+ call assert_equal('a,b,c,d,\', &isk)
+ set isk-=\\
+ call assert_equal('a,b,c,d', &isk)
+
+ let &isk = isk_save
+endfunction
+
+function Test_set_add()
+ let wig_save = &wig
+
+ set wildignore=*.png,
+ set wildignore+=*.jpg
+ call assert_equal('*.png,*.jpg', &wig)
+
+ let &wig = wig_save
+endfunction
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_sha256.vim b/src/nvim/testdir/test_sha256.vim
index 76d1306836..f6f430b04e 100644
--- a/src/nvim/testdir/test_sha256.vim
+++ b/src/nvim/testdir/test_sha256.vim
@@ -1,8 +1,8 @@
" Tests for the sha256() function.
-if !has('cryptv') || !exists('*sha256')
- finish
-endif
+source check.vim
+CheckFeature cryptv
+CheckFunction sha256
function Test_sha256()
" test for empty string:
diff --git a/src/nvim/testdir/test_smartindent.vim b/src/nvim/testdir/test_smartindent.vim
index e89ad19d34..e2d028e828 100644
--- a/src/nvim/testdir/test_smartindent.vim
+++ b/src/nvim/testdir/test_smartindent.vim
@@ -21,9 +21,7 @@ endfunc
func Test_smartindent_has_no_effect()
new
exe "normal! i\<Tab>one\<Esc>"
- set noautoindent
- set smartindent
- set indentexpr=
+ setlocal noautoindent smartindent indentexpr=
exe "normal! Gotwo\<Esc>"
call assert_equal("\ttwo", getline("$"))
@@ -32,9 +30,106 @@ func Test_smartindent_has_no_effect()
call assert_equal("three", getline("$"))
delfunction! MyIndent
- set autoindent&
- set smartindent&
- set indentexpr&
+ bwipe!
+endfunc
+
+" Test for inserting '{' and '} with smartindent
+func Test_smartindent_braces()
+ new
+ setlocal smartindent shiftwidth=4
+ call setline(1, [' if (a)', "\tif (b)", "\t return 1"])
+ normal 2ggO{
+ normal 3ggA {
+ normal 4ggo}
+ normal o}
+ normal 4ggO#define FOO 1
+ call assert_equal([
+ \ ' if (a)',
+ \ ' {',
+ \ "\tif (b) {",
+ \ '#define FOO 1',
+ \ "\t return 1",
+ \ "\t}",
+ \ ' }'
+ \ ], getline(1, '$'))
+ close!
+endfunc
+
+" Test for adding a new line before and after comments with smartindent
+func Test_si_add_line_around_comment()
+ new
+ setlocal smartindent shiftwidth=4
+ call setline(1, [' A', '# comment1', '# comment2'])
+ exe "normal GoC\<Esc>2GOB"
+ call assert_equal([' A', ' B', '# comment1', '# comment2', ' C'],
+ \ getline(1, '$'))
+ close!
+endfunc
+
+" After a C style comment, indent for a following line should line up with the
+" line containing the start of the comment.
+func Test_si_indent_after_c_comment()
+ new
+ setlocal smartindent shiftwidth=4 fo+=ro
+ exe "normal i\<C-t>/*\ncomment\n/\n#define FOOBAR\n75\<Esc>ggOabc"
+ normal 3jOcont
+ call assert_equal([' abc', ' /*', ' * comment', ' * cont',
+ \ ' */', '#define FOOBAR', ' 75'], getline(1, '$'))
+ close!
+endfunc
+
+" Test for indenting a statement after a if condition split across lines
+func Test_si_if_cond_split_across_lines()
+ new
+ setlocal smartindent shiftwidth=4
+ exe "normal i\<C-t>if (cond1 &&\n\<C-t>cond2) {\ni = 10;\n}"
+ call assert_equal([' if (cond1 &&', "\t cond2) {", "\ti = 10;",
+ \ ' }'], getline(1, '$'))
+ close!
+endfunc
+
+" Test for inserting lines before and after a one line comment
+func Test_si_one_line_comment()
+ new
+ setlocal smartindent shiftwidth=4
+ exe "normal i\<C-t>abc;\n\<C-t>/* comment */"
+ normal oi = 10;
+ normal kOj = 1;
+ call assert_equal([' abc;', "\tj = 1;", "\t/* comment */", "\ti = 10;"],
+ \ getline(1, '$'))
+ close!
+endfunc
+
+" Test for smartindent with a comment continued across multiple lines
+func Test_si_comment_line_continuation()
+ new
+ setlocal smartindent shiftwidth=4
+ call setline(1, ['# com1', '# com2 \', ' contd', '# com3', ' xyz'])
+ normal ggOabc
+ call assert_equal([' abc', '# com1', '# com2 \', ' contd', '# com3',
+ \ ' xyz'], getline(1, '$'))
+ close!
+endfunc
+
+func Test_si_after_completion()
+ new
+ setlocal ai smartindent indentexpr=
+ call setline(1, 'foo foot')
+ call feedkeys("o f\<C-X>\<C-N>#", 'tx')
+ call assert_equal(' foo#', getline(2))
+
+ call setline(2, '')
+ call feedkeys("1Go f\<C-X>\<C-N>}", 'tx')
+ call assert_equal(' foo}', getline(2))
+
+ bwipe!
+endfunc
+
+func Test_no_si_after_completion()
+ new
+ call setline(1, 'foo foot')
+ call feedkeys("o f\<C-X>\<C-N>#", 'tx')
+ call assert_equal(' foo#', getline(2))
bwipe!
endfunc
diff --git a/src/nvim/testdir/test_sort.vim b/src/nvim/testdir/test_sort.vim
index 570c4415c6..9895ad754c 100644
--- a/src/nvim/testdir/test_sort.vim
+++ b/src/nvim/testdir/test_sort.vim
@@ -12,6 +12,7 @@ func Compare2(a, b) abort
endfunc
func Test_sort_strings()
+ CheckNotMSWindows " FIXME: Why does this fail with MSVC?
" numbers compared as strings
call assert_equal([1, 2, 3], sort([3, 2, 1]))
call assert_equal([13, 28, 3], sort([3, 28, 13]))
@@ -58,6 +59,7 @@ endfunc
func Test_sort_numbers()
call assert_equal([3, 13, 28], sort([13, 28, 3], 'N'))
call assert_equal(['3', '13', '28'], sort(['13', '28', '3'], 'N'))
+ call assert_equal([3997, 4996], sort([4996, 3997], 'Compare1'))
endfunc
func Test_sort_float()
@@ -1354,7 +1356,7 @@ func Test_sort_cmd()
endif
endfor
- " Needs atleast two lines for this test
+ " Needs at least two lines for this test
call setline(1, ['line1', 'line2'])
call assert_fails('sort no', 'E474:')
call assert_fails('sort c', 'E475:')
@@ -1487,6 +1489,22 @@ func Test_sort_last_search_pat()
close!
endfunc
+" Test for :sort with no last search pattern
+func Test_sort_with_no_last_search_pat()
+ let lines =<< trim [SCRIPT]
+ call setline(1, ['3b', '1c', '2a'])
+ call assert_fails('sort //', 'E35:')
+ call writefile(v:errors, 'Xresult')
+ qall!
+ [SCRIPT]
+ call writefile(lines, 'Xscript')
+ if RunVim([], [], '--clean -S Xscript')
+ call assert_equal([], readfile('Xresult'))
+ endif
+ call delete('Xscript')
+ call delete('Xresult')
+endfunc
+
" Test for retaining marks across a :sort
func Test_sort_with_marks()
new
diff --git a/src/nvim/testdir/test_source.vim b/src/nvim/testdir/test_source.vim
index 09baec0b7d..ba6fd5ad95 100644
--- a/src/nvim/testdir/test_source.vim
+++ b/src/nvim/testdir/test_source.vim
@@ -46,3 +46,45 @@ func Test_source_sandbox()
bwipe!
call delete('Xsourcehello')
endfunc
+
+" When deleting a file and immediately creating a new one the inode may be
+" recycled. Vim should not recognize it as the same script.
+func Test_different_script()
+ call writefile(['let s:var = "asdf"'], 'XoneScript')
+ source XoneScript
+ call delete('XoneScript')
+ call writefile(['let g:var = s:var'], 'XtwoScript')
+ call assert_fails('source XtwoScript', 'E121:')
+ call delete('XtwoScript')
+endfunc
+
+" When sourcing a vim script, shebang should be ignored.
+func Test_source_ignore_shebang()
+ call writefile(['#!./xyzabc', 'let g:val=369'], 'Xfile.vim')
+ source Xfile.vim
+ call assert_equal(g:val, 369)
+ call delete('Xfile.vim')
+endfunc
+
+" Test for expanding <sfile> in a autocmd and for <slnum> and <sflnum>
+func Test_source_autocmd_sfile()
+ let code =<< trim [CODE]
+ let g:SfileName = ''
+ augroup sfiletest
+ au!
+ autocmd User UserAutoCmd let g:Sfile = '<sfile>:t'
+ augroup END
+ doautocmd User UserAutoCmd
+ let g:Slnum = expand('<slnum>')
+ let g:Sflnum = expand('<sflnum>')
+ augroup! sfiletest
+ [CODE]
+ call writefile(code, 'Xscript.vim')
+ source Xscript.vim
+ call assert_equal('Xscript.vim', g:Sfile)
+ call assert_equal('7', g:Slnum)
+ call assert_equal('8', g:Sflnum)
+ call delete('Xscript.vim')
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_source_utf8.vim b/src/nvim/testdir/test_source_utf8.vim
index e93ea29dff..66fabe0442 100644
--- a/src/nvim/testdir/test_source_utf8.vim
+++ b/src/nvim/testdir/test_source_utf8.vim
@@ -1,4 +1,5 @@
" Test the :source! command
+source check.vim
func Test_source_utf8()
" check that sourcing a script with 0x80 as second byte works
@@ -31,24 +32,24 @@ endfunc
" Test for sourcing a file with CTRL-V's at the end of the line
func Test_source_ctrl_v()
- call writefile(['map __1 afirst',
- \ 'map __2 asecond',
- \ 'map __3 athird',
- \ 'map __4 afourth',
- \ 'map __5 afifth',
- \ "map __1 asd\<C-V>",
- \ "map __2 asd\<C-V>\<C-V>",
- \ "map __3 asd\<C-V>\<C-V>",
- \ "map __4 asd\<C-V>\<C-V>\<C-V>",
- \ "map __5 asd\<C-V>\<C-V>\<C-V>",
- \ ], 'Xtestfile')
+ call writefile(['map __1 afirst',
+ \ 'map __2 asecond',
+ \ 'map __3 athird',
+ \ 'map __4 afourth',
+ \ 'map __5 afifth',
+ \ "map __1 asd\<C-V>",
+ \ "map __2 asd\<C-V>\<C-V>",
+ \ "map __3 asd\<C-V>\<C-V>",
+ \ "map __4 asd\<C-V>\<C-V>\<C-V>",
+ \ "map __5 asd\<C-V>\<C-V>\<C-V>",
+ \ ], 'Xtestfile')
source Xtestfile
enew!
exe "normal __1\<Esc>\<Esc>__2\<Esc>__3\<Esc>\<Esc>__4\<Esc>__5\<Esc>"
exe "%s/\<C-J>/0/g"
call assert_equal(['sd',
- \ "map __2 asd\<Esc>secondsd\<Esc>sd0map __5 asd0fifth"],
- \ getline(1, 2))
+ \ "map __2 asd\<Esc>secondsd\<Esc>sd0map __5 asd0fifth"],
+ \ getline(1, 2))
enew!
call delete('Xtestfile')
diff --git a/src/nvim/testdir/test_spell.vim b/src/nvim/testdir/test_spell.vim
index 1ecb5c8070..7744c5bcca 100644
--- a/src/nvim/testdir/test_spell.vim
+++ b/src/nvim/testdir/test_spell.vim
@@ -2,9 +2,7 @@
" Note: this file uses latin1 encoding, but is used with utf-8 encoding.
source check.vim
-if !has('spell')
- finish
-endif
+CheckFeature spell
source screendump.vim
@@ -16,6 +14,8 @@ func TearDown()
call delete('Xtest.latin1.add.spl')
call delete('Xtest.latin1.spl')
call delete('Xtest.latin1.sug')
+ " set 'encoding' to clear the word list
+ set encoding=utf-8
endfunc
func Test_wrap_search()
@@ -72,6 +72,16 @@ func Test_z_equal_on_invalid_utf8_word()
bwipe!
endfunc
+func Test_z_equal_on_single_character()
+ " this was decrementing the index below zero
+ new
+ norm a0\
+ norm zW
+ norm z=
+
+ bwipe!
+endfunc
+
" Test spellbadword() with argument
func Test_spellbadword()
set spell
@@ -121,6 +131,106 @@ foobar/?
set spell&
endfunc
+func Test_spell_file_missing()
+ let s:spell_file_missing = 0
+ augroup TestSpellFileMissing
+ autocmd! SpellFileMissing * let s:spell_file_missing += 1
+ augroup END
+
+ set spell spelllang=ab_cd
+ let messages = GetMessages()
+ " This message is not shown in Nvim because of #3027
+ " call assert_equal('Warning: Cannot find word list "ab.utf-8.spl" or "ab.ascii.spl"', messages[-1])
+ call assert_equal(1, s:spell_file_missing)
+
+ new XTestSpellFileMissing
+ augroup TestSpellFileMissing
+ autocmd! SpellFileMissing * bwipe
+ augroup END
+ call assert_fails('set spell spelllang=ab_cd', 'E797:')
+
+ " clean up
+ augroup TestSpellFileMissing
+ autocmd! SpellFileMissing
+ augroup END
+ augroup! TestSpellFileMissing
+ unlet s:spell_file_missing
+ set spell& spelllang&
+ %bwipe!
+endfunc
+
+func Test_spelldump()
+ " In case the spell file is not found avoid getting the download dialog, we
+ " would get stuck at the prompt.
+ let g:en_not_found = 0
+ augroup TestSpellFileMissing
+ au! SpellFileMissing * let g:en_not_found = 1
+ augroup END
+ set spell spelllang=en
+ spellrare! emacs
+ if g:en_not_found
+ call assert_report("Could not find English spell file")
+ else
+ spelldump
+
+ " Check assumption about region: 1: us, 2: au, 3: ca, 4: gb, 5: nz.
+ call assert_equal('/regions=usaucagbnz', getline(1))
+ call assert_notequal(0, search('^theater/1$')) " US English only.
+ call assert_notequal(0, search('^theatre/2345$')) " AU, CA, GB or NZ English.
+
+ call assert_notequal(0, search('^emacs/?$')) " ? for a rare word.
+ call assert_notequal(0, search('^the the/!$')) " ! for a wrong word.
+ endif
+
+ " clean up
+ unlet g:en_not_found
+ augroup TestSpellFileMissing
+ autocmd! SpellFileMissing
+ augroup END
+ augroup! TestSpellFileMissing
+ bwipe
+ set spell&
+endfunc
+
+func Test_spelldump_bang()
+ new
+ call setline(1, 'This is a sample sentence.')
+ redraw
+
+ " In case the spell file is not found avoid getting the download dialog, we
+ " would get stuck at the prompt.
+ let g:en_not_found = 0
+ augroup TestSpellFileMissing
+ au! SpellFileMissing * let g:en_not_found = 1
+ augroup END
+
+ set spell
+
+ if g:en_not_found
+ call assert_report("Could not find English spell file")
+ else
+ redraw
+ spelldump!
+
+ " :spelldump! includes the number of times a word was found while updating
+ " the screen.
+ " Common word count starts at 10, regular word count starts at 0.
+ call assert_notequal(0, search("^is\t11$")) " common word found once.
+ call assert_notequal(0, search("^the\t10$")) " common word never found.
+ call assert_notequal(0, search("^sample\t1$")) " regular word found once.
+ call assert_equal(0, search("^screen\t")) " regular word never found.
+ endif
+
+ " clean up
+ unlet g:en_not_found
+ augroup TestSpellFileMissing
+ autocmd! SpellFileMissing
+ augroup END
+ augroup! TestSpellFileMissing
+ %bwipe!
+ set spell&
+endfunc
+
func Test_spelllang_inv_region()
set spell spelllang=en_xx
let messages = GetMessages()
@@ -175,6 +285,18 @@ func Test_spellreall()
bwipe!
endfunc
+func Test_spell_dump_word_length()
+ " this was running over MAXWLEN
+ new
+ noremap 0 0a0zW0000000
+ sil! norm 0z=0
+ sil norm 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+ sil! norm 0z=0
+
+ bwipe!
+ nunmap 0
+endfunc
+
" Test spellsuggest({word} [, {max} [, {capital}]])
func Test_spellsuggest()
" Verify suggestions are given even when spell checking is not enabled.
@@ -407,7 +529,7 @@ func Test_zz_basic()
\ )
call assert_equal("gebletegek", soundfold('goobledygoook'))
- call assert_equal("kepereneven", 'kóopërÿnôven'->soundfold())
+ call assert_equal("kepereneven", 'koprnven'->soundfold())
call assert_equal("everles gesvets etele", soundfold('oeverloos gezwets edale'))
endfunc
@@ -588,7 +710,7 @@ func Test_zz_sal_and_addition()
mkspell! Xtest Xtest
set spl=Xtest.latin1.spl spell
call assert_equal('kbltykk', soundfold('goobledygoook'))
- call assert_equal('kprnfn', soundfold('kóopërÿnôven'))
+ call assert_equal('kprnfn', soundfold('koprnven'))
call assert_equal('*fls kswts tl', soundfold('oeverloos gezwets edale'))
"also use an addition file
@@ -616,6 +738,10 @@ func Test_zz_sal_and_addition()
set spl=Xtest_ca.latin1.spl
call assert_equal("elequint", FirstSpellWord())
call assert_equal("elekwint", SecondSpellWord())
+
+ bwipe!
+ set spellfile=
+ set spl&
endfunc
func Test_spellfile_value()
@@ -681,6 +807,36 @@ func Test_spell_long_word()
set nospell
endfunc
+func Test_spellsuggest_too_deep()
+ " This was incrementing "depth" over MAXWLEN.
+ new
+ norm s000G00000000000000
+ sil norm ..vzG................vvzG0 v z=
+ bwipe!
+endfunc
+
+func Test_spell_good_word_invalid()
+ " This was adding a word with a 0x02 byte, which causes havoc.
+ enew
+ norm o0
+ sil! norm rzzWs00/
+ 2
+ sil! norm VzGprzzW
+ sil! norm z=
+
+ bwipe!
+endfunc
+
+func Test_spell_good_word_slash()
+ " This caused E1280.
+ new
+ norm afoo /
+ 1
+ norm zG
+
+ bwipe!
+endfunc
+
func LoadAffAndDic(aff_contents, dic_contents)
throw 'skipped: Nvim does not support enc=latin1'
set enc=latin1
@@ -768,14 +924,6 @@ 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_spell_utf8.vim b/src/nvim/testdir/test_spell_utf8.vim
index 3d159f3352..b7e3da37cb 100644
--- a/src/nvim/testdir/test_spell_utf8.vim
+++ b/src/nvim/testdir/test_spell_utf8.vim
@@ -13,6 +13,8 @@ func TearDown()
call delete('Xtest.utf-8.add.spl')
call delete('Xtest.utf-8.spl')
call delete('Xtest.utf-8.sug')
+ " set 'encoding' to clear the word list
+ set encoding=utf-8
endfunc
let g:test_data_aff1 = [
@@ -484,7 +486,6 @@ let g:test_data_aff_sal = [
\ ]
func LoadAffAndDic(aff_contents, dic_contents)
- set enc=utf-8
set spellfile=
call writefile(a:aff_contents, "Xtest.aff")
call writefile(a:dic_contents, "Xtest.dic")
@@ -576,7 +577,6 @@ endfunc
"Compound words
func Test_spell_compound()
- throw 'skipped: TODO: '
call LoadAffAndDic(g:test_data_aff3, g:test_data_dic3)
call RunGoodBad("foo m\u00EF foobar foofoobar barfoo barbarfoo",
\ "bad: bar la foom\u00EF barm\u00EF m\u00EFfoo m\u00EFbar m\u00EFm\u00EF lala m\u00EFla lam\u00EF foola labar",
@@ -624,14 +624,14 @@ endfunc
" Test affix flags with two characters
func Test_spell_affix()
- throw 'skipped: TODO: '
+ CheckNotMSWindows " FIXME: Why does this fail with MSVC?
call LoadAffAndDic(g:test_data_aff5, g:test_data_dic5)
call RunGoodBad("fooa1 fooa\u00E9 bar prebar barbork prebarbork startprebar start end startend startmiddleend nouend",
\ "bad: foo fooa2 prabar probarbirk middle startmiddle middleend endstart startprobar startnouend",
\ ["bar", "barbork", "end", "fooa1", "fooa\u00E9", "nouend", "prebar", "prebarbork", "start"],
\ [
\ ["bad", ["bar", "end", "fooa1"]],
- \ ["foo", ["fooa1", "fooa\u00E9", "bar"]],
+ \ ["foo", ["fooa1", "bar", "end"]],
\ ["fooa2", ["fooa1", "fooa\u00E9", "bar"]],
\ ["prabar", ["prebar", "bar", "bar bar"]],
\ ["probarbirk", ["prebarbork"]],
@@ -649,7 +649,7 @@ func Test_spell_affix()
\ ["bar", "barbork", "end", "lead", "meea1", "meea\u00E9", "prebar", "prebarbork"],
\ [
\ ["bad", ["bar", "end", "lead"]],
- \ ["mee", ["meea1", "meea\u00E9", "bar"]],
+ \ ["mee", ["meea1", "bar", "end"]],
\ ["meea2", ["meea1", "meea\u00E9", "lead"]],
\ ["prabar", ["prebar", "bar", "leadbar"]],
\ ["probarbirk", ["prebarbork"]],
@@ -666,7 +666,7 @@ func Test_spell_affix()
\ ["bar", "barmeat", "lead", "meea1", "meea\u00E9", "meezero", "prebar", "prebarmeat", "tail"],
\ [
\ ["bad", ["bar", "lead", "tail"]],
- \ ["mee", ["meea1", "meea\u00E9", "bar"]],
+ \ ["mee", ["meea1", "bar", "lead"]],
\ ["meea2", ["meea1", "meea\u00E9", "lead"]],
\ ["prabar", ["prebar", "bar", "leadbar"]],
\ ["probarmaat", ["prebarmeat"]],
@@ -700,7 +700,6 @@ endfunc
" Affix flags
func Test_spell_affix_flags()
- throw 'skipped: TODO: '
call LoadAffAndDic(g:test_data_aff10, g:test_data_dic10)
call RunGoodBad("drink drinkable drinkables drinktable drinkabletable",
\ "bad: drinks drinkstable drinkablestable",
@@ -761,11 +760,65 @@ func Test_spell_sal_and_addition()
set spl=Xtest_ca.utf-8.spl
call assert_equal("elequint", FirstSpellWord())
call assert_equal("elekwint", SecondSpellWord())
+
+ bwipe!
+ set spellfile=
+ set spl&
endfunc
func Test_spellfile_value()
set spellfile=Xdir/Xtest.utf-8.add
set spellfile=Xdir/Xtest.utf-8.add,Xtest_other.add
+ set spellfile=
endfunc
+func Test_no_crash_with_weird_text()
+ new
+ let lines =<< trim END
+ r<sfile>
+ €
+
+
+ €
+ END
+ call setline(1, lines)
+ try
+ exe "%norm \<C-v>ez=>\<C-v>wzG"
+ catch /E1280:/
+ let caught = 'yes'
+ endtry
+ call assert_equal('yes', caught)
+
+ bwipe!
+endfunc
+
+" Invalid bytes may cause trouble when creating the word list.
+func Test_check_for_valid_word()
+ call assert_fails("spellgood! 0\xac", 'E1280:')
+endfunc
+
+" This was going over the end of the word
+func Test_word_index()
+ new
+ norm R0
+ spellgood! fl0
+ sil norm z=
+
+ bwipe!
+ call delete('Xtmpfile')
+endfunc
+
+func Test_check_empty_line()
+ " This was using freed memory
+ enew
+ spellgood! fl
+ norm z=
+ norm yy
+ sil! norm P]svc
+ norm P]s
+
+ bwipe!
+endfunc
+
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_spellfile.vim b/src/nvim/testdir/test_spellfile.vim
index 0f48ab8f6f..b028e9d969 100644
--- a/src/nvim/testdir/test_spellfile.vim
+++ b/src/nvim/testdir/test_spellfile.vim
@@ -167,8 +167,632 @@ func Test_spell_normal()
call assert_equal([], glob('Xspellfile.add',0,1))
call assert_equal([], glob('Xspellfile2.add',0,1))
+ set spellfile= spell& spelllang&
+ bw!
+endfunc
+
+" Spell file content test. Write 'content' to the spell file prefixed by the
+" spell file header and then enable spell checking. If 'emsg' is not empty,
+" then check for error.
+func Spellfile_Test(content, emsg)
+ let splfile = './Xtest/spell/Xtest.utf-8.spl'
+ " Add the spell file header and version (VIMspell2)
+ let v = 0z56494D7370656C6C32 + a:content
+ call writefile(v, splfile, 'b')
+ set runtimepath=./Xtest
+ set spelllang=Xtest
+ if a:emsg != ''
+ call assert_fails('set spell', a:emsg)
+ else
+ " FIXME: With some invalid spellfile contents, there are no error
+ " messages. So don't know how to check for the test result.
+ set spell
+ endif
+ set nospell spelllang& rtp&
+endfunc
+
+" Test for spell file format errors.
+" The spell file format is described in spellfile.c
+func Test_spellfile_format_error()
+ let save_rtp = &rtp
+ call mkdir('Xtest/spell', 'p')
+ let splfile = './Xtest/spell/Xtest.utf-8.spl'
+
+ " empty spell file
+ call writefile([], splfile)
+ set runtimepath=./Xtest
+ set spelllang=Xtest
+ call assert_fails('set spell', 'E757:')
+ set nospell spelllang&
+
+ " invalid file ID
+ call writefile(0z56494D, splfile, 'b')
+ set runtimepath=./Xtest
+ set spelllang=Xtest
+ call assert_fails('set spell', 'E757:')
+ set nospell spelllang&
+
+ " missing version number
+ call writefile(0z56494D7370656C6C, splfile, 'b')
+ set runtimepath=./Xtest
+ set spelllang=Xtest
+ call assert_fails('set spell', 'E771:')
+ set nospell spelllang&
+
+ " invalid version number
+ call writefile(0z56494D7370656C6C7A, splfile, 'b')
+ set runtimepath=./Xtest
+ set spelllang=Xtest
+ call assert_fails('set spell', 'E772:')
+ set nospell spelllang&
+
+ " no sections
+ call Spellfile_Test(0z, 'E758:')
+
+ " missing section length
+ call Spellfile_Test(0z00, 'E758:')
+
+ " unsupported required section
+ call Spellfile_Test(0z7A0100000004, 'E770:')
+
+ " unsupported not-required section
+ call Spellfile_Test(0z7A0000000004, 'E758:')
+
+ " SN_REGION: invalid number of region names
+ call Spellfile_Test(0z0000000000FF, 'E759:')
+
+ " SN_CHARFLAGS: missing <charflagslen> length
+ call Spellfile_Test(0z010000000004, 'E758:')
+
+ " SN_CHARFLAGS: invalid <charflagslen> length
+ call Spellfile_Test(0z0100000000010201, '')
+
+ " SN_CHARFLAGS: charflagslen == 0 and folcharslen != 0
+ call Spellfile_Test(0z01000000000400000101, 'E759:')
+
+ " SN_CHARFLAGS: missing <folcharslen> length
+ call Spellfile_Test(0z01000000000100, 'E758:')
+
+ " SN_PREFCOND: invalid prefcondcnt
+ call Spellfile_Test(0z03000000000100, 'E759:')
+
+ " SN_PREFCOND: invalid condlen
+ call Spellfile_Test(0z0300000000020001, 'E759:')
+
+ " SN_REP: invalid repcount
+ call Spellfile_Test(0z04000000000100, 'E758:')
+
+ " SN_REP: missing rep
+ call Spellfile_Test(0z0400000000020004, 'E758:')
+
+ " SN_REP: zero repfromlen
+ call Spellfile_Test(0z040000000003000100, 'E759:')
+
+ " SN_REP: invalid reptolen
+ call Spellfile_Test(0z0400000000050001014101, '')
+
+ " SN_REP: zero reptolen
+ call Spellfile_Test(0z0400000000050001014100, 'E759:')
+
+ " SN_SAL: missing salcount
+ call Spellfile_Test(0z05000000000102, 'E758:')
+
+ " SN_SAL: missing salfromlen
+ call Spellfile_Test(0z050000000003080001, 'E758:')
+
+ " SN_SAL: missing saltolen
+ call Spellfile_Test(0z0500000000050400010161, 'E758:')
+
+ " SN_WORDS: non-NUL terminated word
+ call Spellfile_Test(0z0D000000000376696D, 'E758:')
+
+ " SN_WORDS: very long word
+ let v = eval('0z0D000000012C' .. repeat('41', 300))
+ call Spellfile_Test(v, 'E759:')
+
+ " SN_SOFO: missing sofofromlen
+ call Spellfile_Test(0z06000000000100, 'E758:')
+
+ " SN_SOFO: missing sofotolen
+ call Spellfile_Test(0z06000000000400016100, 'E758:')
+
+ " SN_SOFO: missing sofoto
+ call Spellfile_Test(0z0600000000050001610000, 'E759:')
+
+ " SN_COMPOUND: compmax is less than 2
+ call Spellfile_Test(0z08000000000101, 'E759:')
+
+ " SN_COMPOUND: missing compsylmax and other options
+ call Spellfile_Test(0z0800000000020401, 'E759:')
+
+ " SN_COMPOUND: missing compoptions
+ call Spellfile_Test(0z080000000005040101, 'E758:')
+
+ " SN_INFO: missing info
+ call Spellfile_Test(0z0F0000000005040101, '')
+
+ " SN_MIDWORD: missing midword
+ call Spellfile_Test(0z0200000000040102, '')
+
+ " SN_MAP: missing midword
+ call Spellfile_Test(0z0700000000040102, '')
+
+ " SN_SYLLABLE: missing SYLLABLE item
+ call Spellfile_Test(0z0900000000040102, '')
+
+ " SN_SYLLABLE: More than SY_MAXLEN size
+ let v = eval('0z090000000022612F' .. repeat('62', 32))
+ call Spellfile_Test(v, '')
+
+ " LWORDTREE: missing
+ call Spellfile_Test(0zFF, 'E758:')
+
+ " LWORDTREE: missing tree node
+ call Spellfile_Test(0zFF00000004, 'E758:')
+
+ " LWORDTREE: missing tree node value
+ call Spellfile_Test(0zFF0000000402, 'E758:')
+
+ " KWORDTREE: missing tree node
+ call Spellfile_Test(0zFF0000000000000004, 'E758:')
+
+ " PREFIXTREE: missing tree node
+ call Spellfile_Test(0zFF000000000000000000000004, 'E758:')
+
+ let &rtp = save_rtp
+ call delete('Xtest', 'rf')
+endfunc
+
+" Test for format errors in suggest file
+func Test_sugfile_format_error()
+ let save_rtp = &rtp
+ call mkdir('Xtest/spell', 'p')
+ let splfile = './Xtest/spell/Xtest.utf-8.spl'
+ let sugfile = './Xtest/spell/Xtest.utf-8.sug'
+
+ " create an empty spell file with a suggest timestamp
+ call writefile(0z56494D7370656C6C320B00000000080000000000000044FF000000000000000000000000, splfile, 'b')
+
+ " 'encoding' is set before each test to clear the previously loaded suggest
+ " file from memory.
+
+ " empty suggest file
+ set encoding=utf-8
+ call writefile([], sugfile)
+ set runtimepath=./Xtest
+ set spelllang=Xtest
+ set spell
+ call assert_fails("let s = spellsuggest('abc')", 'E778:')
+ set nospell spelllang&
+
+ " zero suggest version
+ set encoding=utf-8
+ call writefile(0z56494D73756700, sugfile)
+ set runtimepath=./Xtest
+ set spelllang=Xtest
+ set spell
+ call assert_fails("let s = spellsuggest('abc')", 'E779:')
+ set nospell spelllang&
+
+ " unsupported suggest version
+ set encoding=utf-8
+ call writefile(0z56494D7375671F, sugfile)
+ set runtimepath=./Xtest
+ set spelllang=Xtest
+ set spell
+ call assert_fails("let s = spellsuggest('abc')", 'E780:')
+ set nospell spelllang&
+
+ " missing suggest timestamp
+ set encoding=utf-8
+ call writefile(0z56494D73756701, sugfile)
+ set runtimepath=./Xtest
+ set spelllang=Xtest
+ set spell
+ call assert_fails("let s = spellsuggest('abc')", 'E781:')
+ set nospell spelllang&
+
+ " incorrect suggest timestamp
+ set encoding=utf-8
+ call writefile(0z56494D7375670100000000000000FF, sugfile)
+ set runtimepath=./Xtest
+ set spelllang=Xtest
+ set spell
+ call assert_fails("let s = spellsuggest('abc')", 'E781:')
+ set nospell spelllang&
+
+ " missing suggest wordtree
+ set encoding=utf-8
+ call writefile(0z56494D737567010000000000000044, sugfile)
+ set runtimepath=./Xtest
+ set spelllang=Xtest
+ set spell
+ call assert_fails("let s = spellsuggest('abc')", 'E782:')
+ set nospell spelllang&
+
+ " invalid suggest word count in SUGTABLE
+ set encoding=utf-8
+ call writefile(0z56494D7375670100000000000000440000000022, sugfile)
+ set runtimepath=./Xtest
+ set spelllang=Xtest
+ set spell
+ call assert_fails("let s = spellsuggest('abc')", 'E782:')
+ set nospell spelllang&
+
+ " missing sugline in SUGTABLE
+ set encoding=utf-8
+ call writefile(0z56494D7375670100000000000000440000000000000005, sugfile)
+ set runtimepath=./Xtest
+ set spelllang=Xtest
+ set spell
+ call assert_fails("let s = spellsuggest('abc')", 'E782:')
+ set nospell spelllang&
+
+ let &rtp = save_rtp
+ call delete('Xtest', 'rf')
+endfunc
+
+" Test for using :mkspell to create a spell file from a list of words
+func Test_wordlist_dic()
+ " duplicate encoding
+ let lines =<< trim [END]
+ # This is an example word list
+
+ /encoding=latin1
+ /encoding=latin1
+ example
+ [END]
+ call writefile(lines, 'Xwordlist.dic')
+ let output = execute('mkspell Xwordlist.spl Xwordlist.dic')
+ call assert_match('Duplicate /encoding= line ignored in Xwordlist.dic line 4: /encoding=latin1', output)
+
+ " multiple encoding for a word
+ let lines =<< trim [END]
+ example
+ /encoding=latin1
+ example
+ [END]
+ call writefile(lines, 'Xwordlist.dic')
+ let output = execute('mkspell! Xwordlist.spl Xwordlist.dic')
+ call assert_match('/encoding= line after word ignored in Xwordlist.dic line 2: /encoding=latin1', output)
+
+ " unsupported encoding for a word
+ let lines =<< trim [END]
+ /encoding=Xtest
+ example
+ [END]
+ call writefile(lines, 'Xwordlist.dic')
+ let output = execute('mkspell! Xwordlist.spl Xwordlist.dic')
+ call assert_match('Conversion in Xwordlist.dic not supported: from Xtest to utf-8', output)
+
+ " duplicate region
+ let lines =<< trim [END]
+ /regions=usca
+ /regions=usca
+ example
+ [END]
+ call writefile(lines, 'Xwordlist.dic')
+ let output = execute('mkspell! Xwordlist.spl Xwordlist.dic')
+ call assert_match('Duplicate /regions= line ignored in Xwordlist.dic line 2: regions=usca', output)
+
+ " maximum regions
+ let lines =<< trim [END]
+ /regions=uscauscauscauscausca
+ example
+ [END]
+ call writefile(lines, 'Xwordlist.dic')
+ let output = execute('mkspell! Xwordlist.spl Xwordlist.dic')
+ call assert_match('Too many regions in Xwordlist.dic line 1: uscauscauscauscausca', output)
+
+ " unsupported '/' value
+ let lines =<< trim [END]
+ /test=abc
+ example
+ [END]
+ call writefile(lines, 'Xwordlist.dic')
+ let output = execute('mkspell! Xwordlist.spl Xwordlist.dic')
+ call assert_match('/ line ignored in Xwordlist.dic line 1: /test=abc', output)
+
+ " unsupported flag
+ let lines =<< trim [END]
+ example/+
+ [END]
+ call writefile(lines, 'Xwordlist.dic')
+ let output = execute('mkspell! Xwordlist.spl Xwordlist.dic')
+ call assert_match('Unrecognized flags in Xwordlist.dic line 1: +', output)
+
+ " non-ascii word
+ call writefile(["ʀʀ"], 'Xwordlist.dic')
+ let output = execute('mkspell! -ascii Xwordlist.spl Xwordlist.dic')
+ call assert_match('Ignored 1 words with non-ASCII characters', output)
+
+ call delete('Xwordlist.spl')
+ call delete('Xwordlist.dic')
+endfunc
+
+" Test for the :mkspell command
+func Test_mkspell()
+ call assert_fails('mkspell Xtest_us.spl', 'E751:')
+ call assert_fails('mkspell a b c d e f g h i j k', 'E754:')
+
+ call writefile([], 'Xtest.spl')
+ call writefile([], 'Xtest.dic')
+ call assert_fails('mkspell Xtest.spl Xtest.dic', 'E13:')
+ call delete('Xtest.spl')
+ call delete('Xtest.dic')
+
+ call mkdir('Xtest.spl')
+ call assert_fails('mkspell! Xtest.spl Xtest.dic', 'E17:')
+ call delete('Xtest.spl', 'rf')
+
+ " can't write the .spl file as its directory does not exist
+ call writefile([], 'Xtest.aff')
+ call writefile([], 'Xtest.dic')
+ call assert_fails('mkspell DOES_NOT_EXIT/Xtest.spl Xtest.dic', 'E484:')
+ call delete('Xtest.aff')
+ call delete('Xtest.dic')
+
+ call assert_fails('mkspell en en_US abc_xyz', 'E755:')
+endfunc
+
+" Tests for :mkspell with a .dic and .aff file
+func Test_aff_file_format_error()
+ " FIXME: For some reason, the :mkspell command below doesn't fail on the
+ " MS-Windows CI build. Disable this test on MS-Windows for now.
+ CheckNotMSWindows
+
+ " No word count in .dic file
+ call writefile([], 'Xtest.dic')
+ call writefile([], 'Xtest.aff')
+ call assert_fails('mkspell! Xtest.spl Xtest', 'E760:')
+
+ " create a .dic file for the tests below
+ call writefile(['1', 'work'], 'Xtest.dic')
+
+ " Invalid encoding in .aff file
+ call writefile(['# comment', 'SET Xinvalidencoding'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Conversion in Xtest.aff not supported: from xinvalidencoding', output)
+
+ " Invalid flag in .aff file
+ call writefile(['FLAG xxx'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Invalid value for FLAG in Xtest.aff line 1: xxx', output)
+
+ " set FLAGS after using flag for an affix
+ call writefile(['SFX L Y 1', 'SFX L 0 re [^x]', 'FLAG long'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('FLAG after using flags in Xtest.aff line 3: long', output)
+
+ " INFO in affix file
+ let save_encoding = &encoding
+ call mkdir('Xrtp/spell', 'p')
+ call writefile(['1', 'work'], 'Xrtp/spell/Xtest.dic')
+ call writefile(['NAME klingon', 'VERSION 1.4', 'AUTHOR Spock'],
+ \ 'Xrtp/spell/Xtest.aff')
+ silent mkspell! Xrtp/spell/Xtest.utf-8.spl Xrtp/spell/Xtest
+ let save_rtp = &rtp
+ set runtimepath=./Xrtp
+ set spelllang=Xtest
+ set spell
+ let output = split(execute('spellinfo'), "\n")
+ call assert_equal("NAME klingon", output[1])
+ call assert_equal("VERSION 1.4", output[2])
+ call assert_equal("AUTHOR Spock", output[3])
+ let &rtp = save_rtp
+ call delete('Xrtp', 'rf')
+ set spell& spelllang& spellfile&
+ %bw!
+ " 'encoding' must be set again to clear the spell file in memory
+ let &encoding = save_encoding
+
+ " COMPOUNDFORBIDFLAG flag after PFX in an affix file
+ call writefile(['PFX L Y 1', 'PFX L 0 re x', 'COMPOUNDFLAG c', 'COMPOUNDFORBIDFLAG x'],
+ \ 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Defining COMPOUNDFORBIDFLAG after PFX item may give wrong results in Xtest.aff line 4', output)
+
+ " COMPOUNDPERMITFLAG flag after PFX in an affix file
+ call writefile(['PFX L Y 1', 'PFX L 0 re x', 'COMPOUNDPERMITFLAG c'],
+ \ 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Defining COMPOUNDPERMITFLAG after PFX item may give wrong results in Xtest.aff line 3', output)
+
+ " Wrong COMPOUNDRULES flag value in an affix file
+ call writefile(['COMPOUNDRULES a'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Wrong COMPOUNDRULES value in Xtest.aff line 1: a', output)
+
+ " Wrong COMPOUNDWORDMAX flag value in an affix file
+ call writefile(['COMPOUNDWORDMAX 0'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Wrong COMPOUNDWORDMAX value in Xtest.aff line 1: 0', output)
+
+ " Wrong COMPOUNDMIN flag value in an affix file
+ call writefile(['COMPOUNDMIN 0'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Wrong COMPOUNDMIN value in Xtest.aff line 1: 0', output)
+
+ " Wrong COMPOUNDSYLMAX flag value in an affix file
+ call writefile(['COMPOUNDSYLMAX 0'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Wrong COMPOUNDSYLMAX value in Xtest.aff line 1: 0', output)
+
+ " Wrong CHECKCOMPOUNDPATTERN flag value in an affix file
+ call writefile(['CHECKCOMPOUNDPATTERN 0'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Wrong CHECKCOMPOUNDPATTERN value in Xtest.aff line 1: 0', output)
+
+ " Both compounding and NOBREAK specified
+ call writefile(['COMPOUNDFLAG c', 'NOBREAK'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Warning: both compounding and NOBREAK specified', output)
+
+ " Duplicate affix entry in an affix file
+ call writefile(['PFX L Y 1', 'PFX L 0 re x', 'PFX L Y 1', 'PFX L 0 re x'],
+ \ 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Duplicate affix in Xtest.aff line 3: L', output)
+
+ " Duplicate affix entry in an affix file
+ call writefile(['PFX L Y 1', 'PFX L Y 1'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Unrecognized or duplicate item in Xtest.aff line 2: PFX', output)
+
+ " Different combining flags in an affix file
+ call writefile(['PFX L Y 1', 'PFX L 0 re x', 'PFX L N 1'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Different combining flag in continued affix block in Xtest.aff line 3', output)
+
+ " Try to reuse a affix used for BAD flag
+ call writefile(['BAD x', 'PFX x Y 1', 'PFX x 0 re x'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Affix also used for BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGEST in Xtest.aff line 2: x', output)
+
+ " Trailing characters in an affix entry
+ call writefile(['PFX L Y 1 Test', 'PFX L 0 re x'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Trailing text in Xtest.aff line 1: Test', output)
+
+ " Trailing characters in an affix entry
+ call writefile(['PFX L Y 1', 'PFX L 0 re x Test'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Trailing text in Xtest.aff line 2: Test', output)
+
+ " Incorrect combine flag in an affix entry
+ call writefile(['PFX L X 1', 'PFX L 0 re x'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Expected Y or N in Xtest.aff line 1: X', output)
+
+ " Invalid count for REP item
+ call writefile(['REP a'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Expected REP(SAL) count in Xtest.aff line 1', output)
+
+ " Trailing characters in REP item
+ call writefile(['REP 1', 'REP f ph test'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Trailing text in Xtest.aff line 2: test', output)
+
+ " Invalid count for MAP item
+ call writefile(['MAP a'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Expected MAP count in Xtest.aff line 1', output)
+
+ " Duplicate character in a MAP item
+ call writefile(['MAP 2', 'MAP xx', 'MAP yy'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Duplicate character in MAP in Xtest.aff line 2', output)
+
+ " Use COMPOUNDSYLMAX without SYLLABLE
+ call writefile(['COMPOUNDSYLMAX 2'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('COMPOUNDSYLMAX used without SYLLABLE', output)
+
+ " Missing SOFOTO
+ call writefile(['SOFOFROM abcdef'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Missing SOFOTO line in Xtest.aff', output)
+
+ " Length of SOFOFROM and SOFOTO differ
+ call writefile(['SOFOFROM abcde', 'SOFOTO ABCD'], 'Xtest.aff')
+ call assert_fails('mkspell! Xtest.spl Xtest', 'E759:')
+
+ " Both SAL and SOFOFROM/SOFOTO items
+ call writefile(['SOFOFROM abcd', 'SOFOTO ABCD', 'SAL CIA X'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Both SAL and SOFO lines in Xtest.aff', output)
+
+ " use an alphabet flag when FLAG is num
+ call writefile(['FLAG num', 'SFX L Y 1', 'SFX L 0 re [^x]'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Flag is not a number in Xtest.aff line 2: L', output)
+
+ " use number and alphabet flag when FLAG is num
+ call writefile(['FLAG num', 'SFX 4f Y 1', 'SFX 4f 0 re [^x]'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Affix name too long in Xtest.aff line 2: 4f', output)
+
+ " use a single character flag when FLAG is long
+ call writefile(['FLAG long', 'SFX L Y 1', 'SFX L 0 re [^x]'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('Illegal flag in Xtest.aff line 2: L', output)
+
+ " duplicate word in the .dic file
+ call writefile(['2', 'good', 'good', 'good'], 'Xtest.dic')
+ call writefile(['NAME vim'], 'Xtest.aff')
+ let output = execute('mkspell! Xtest.spl Xtest')
+ call assert_match('First duplicate word in Xtest.dic line 3: good', output)
+ call assert_match('2 duplicate word(s) in Xtest.dic', output)
+
+ call delete('Xtest.dic')
+ call delete('Xtest.aff')
+ call delete('Xtest.spl')
+ call delete('Xtest.sug')
+endfunc
+
+func Test_spell_add_word()
set spellfile=
+ call assert_fails('spellgood abc', 'E764:')
+
+ set spellfile=Xtest.utf-8.add
+ call assert_fails('2spellgood abc', 'E765:')
+
+ edit Xtest.utf-8.add
+ call setline(1, 'sample')
+ call assert_fails('spellgood abc', 'E139:')
+ set spellfile&
+ %bw!
+endfunc
+
+func Test_spellfile_verbose()
+ call writefile(['1', 'one'], 'XtestVerbose.dic')
+ call writefile([], 'XtestVerbose.aff')
+ mkspell! XtestVerbose-utf8.spl XtestVerbose
+ set spell
+
+ " First time: the spl file should be read.
+ let a = execute('3verbose set spelllang=XtestVerbose-utf8.spl')
+ call assert_match('Reading spell file "XtestVerbose-utf8.spl"', a)
+
+ " Second time time: the spl file should not be read (already read).
+ let a = execute('3verbose set spelllang=XtestVerbose-utf8.spl')
+ call assert_notmatch('Reading spell file "XtestVerbose-utf8.spl"', a)
+
+ set spell& spelllang&
+ call delete('XtestVerbose.dic')
+ call delete('XtestVerbose.aff')
+ call delete('XtestVerbose-utf8.spl')
+endfunc
+
+" Test NOBREAK (see :help spell-NOBREAK)
+func Test_NOBREAK()
+ call writefile(['3', 'one', 'two', 'three' ], 'XtestNOBREAK.dic')
+ call writefile(['NOBREAK' ], 'XtestNOBREAK.aff')
+
+ mkspell! XtestNOBREAK-utf8.spl XtestNOBREAK
+ set spell spelllang=XtestNOBREAK-utf8.spl
+
+ call assert_equal(['', ''], spellbadword('One two three onetwo onetwothree threetwoone'))
+
+ call assert_equal(['x', 'bad'], spellbadword('x'))
+ call assert_equal(['y', 'bad'], spellbadword('yone'))
+ call assert_equal(['z', 'bad'], spellbadword('onez'))
+ call assert_equal(['zero', 'bad'], spellbadword('Onetwozerothree'))
+
+ new
+ call setline(1, 'Onetwwothree')
+ norm! fw1z=
+ call assert_equal('Onetwothree', getline(1))
+ call setline(1, 'Onetwothre')
+ norm! fh1z=
+ call assert_equal('Onetwothree', getline(1))
+
bw!
+ set spell& spelllang&
+ call delete('XtestNOBREAK.dic')
+ call delete('XtestNOBREAK.aff')
+ call delete('XtestNOBREAK-utf8.spl')
endfunc
" Test CHECKCOMPOUNDPATTERN (see :help spell-CHECKCOMPOUNDPATTERN)
@@ -183,7 +807,7 @@ func Test_spellfile_CHECKCOMPOUNDPATTERN()
\ 'CHECKCOMPOUNDPATTERN wo on',
\ 'COMPOUNDFLAG c'], 'XtestCHECKCOMPOUNDPATTERN.aff')
- let output = execute('mkspell! XtestCHECKCOMPOUNDPATTERN-utf8.spl XtestCHECKCOMPOUNDPATTERN')
+ mkspell! XtestCHECKCOMPOUNDPATTERN-utf8.spl XtestCHECKCOMPOUNDPATTERN
set spell spelllang=XtestCHECKCOMPOUNDPATTERN-utf8.spl
" Check valid words with and without valid compounds.
@@ -268,7 +892,7 @@ func Test_spellfile_COMMON()
\ 'ted'], 'XtestCOMMON.dic')
call writefile(['COMMON the and'], 'XtestCOMMON.aff')
- let output = execute('mkspell! XtestCOMMON-utf8.spl XtestCOMMON')
+ mkspell! XtestCOMMON-utf8.spl XtestCOMMON
set spell spelllang=XtestCOMMON-utf8.spl
" COMMON words 'and' and 'the' should be the top suggestions.
@@ -283,4 +907,121 @@ func Test_spellfile_COMMON()
call delete('XtestCOMMON-utf8.spl')
endfunc
+" Test CIRCUMFIX (see: :help spell-CIRCUMFIX)
+func Test_spellfile_CIRCUMFIX()
+ " Example taken verbatim from https://github.com/hunspell/hunspell/tree/master/tests
+ call writefile(['1',
+ \ 'nagy/C po:adj'], 'XtestCIRCUMFIX.dic')
+ call writefile(['# circumfixes: ~ obligate prefix/suffix combinations',
+ \ '# superlative in Hungarian: leg- (prefix) AND -bb (suffix)',
+ \ '',
+ \ 'CIRCUMFIX X',
+ \ '',
+ \ 'PFX A Y 1',
+ \ 'PFX A 0 leg/X .',
+ \ '',
+ \ 'PFX B Y 1',
+ \ 'PFX B 0 legesleg/X .',
+ \ '',
+ \ 'SFX C Y 3',
+ \ 'SFX C 0 obb . is:COMPARATIVE',
+ \ 'SFX C 0 obb/AX . is:SUPERLATIVE',
+ \ 'SFX C 0 obb/BX . is:SUPERSUPERLATIVE'], 'XtestCIRCUMFIX.aff')
+
+ mkspell! XtestCIRCUMFIX-utf8.spl XtestCIRCUMFIX
+ set spell spelllang=XtestCIRCUMFIX-utf8.spl
+
+ " From https://catalog.ldc.upenn.edu/docs/LDC2008T01/acta04.pdf:
+ " Hungarian English
+ " --------- -------
+ " nagy great
+ " nagyobb greater
+ " legnagyobb greatest
+ " legeslegnagyob most greatest
+ call assert_equal(['', ''], spellbadword('nagy nagyobb legnagyobb legeslegnagyobb'))
+
+ for badword in ['legnagy', 'legeslegnagy', 'legobb', 'legeslegobb']
+ call assert_equal([badword, 'bad'], spellbadword(badword))
+ endfor
+
+ set spell& spelllang&
+ call delete('XtestCIRCUMFIX.dic')
+ call delete('XtestCIRCUMFIX.aff')
+ call delete('XtestCIRCUMFIX-utf8.spl')
+endfunc
+
+" Test SFX that strips/chops characters
+func Test_spellfile_SFX_strip()
+ " Simplified conjugation of Italian verbs ending in -are (first conjugation).
+ call writefile(['SFX A Y 4',
+ \ 'SFX A are iamo [^icg]are',
+ \ 'SFX A are hiamo [cg]are',
+ \ 'SFX A re mo iare',
+ \ 'SFX A re vamo are'],
+ \ 'XtestSFX.aff')
+ " Examples of Italian verbs:
+ " - cantare = to sing
+ " - cercare = to search
+ " - odiare = to hate
+ call writefile(['3', 'cantare/A', 'cercare/A', 'odiare/A'], 'XtestSFX.dic')
+
+ mkspell! XtestSFX-utf8.spl XtestSFX
+ set spell spelllang=XtestSFX-utf8.spl
+
+ " To sing, we're singing, we were singing.
+ call assert_equal(['', ''], spellbadword('cantare cantiamo cantavamo'))
+
+ " To search, we're searching, we were searching.
+ call assert_equal(['', ''], spellbadword('cercare cerchiamo cercavamo'))
+
+ " To hate, we hate, we were hating.
+ call assert_equal(['', ''], spellbadword('odiare odiamo odiavamo'))
+
+ for badword in ['canthiamo', 'cerciamo', 'cantarevamo', 'odiiamo']
+ call assert_equal([badword, 'bad'], spellbadword(badword))
+ endfor
+
+ call assert_equal(['cantiamo'], spellsuggest('canthiamo', 1))
+ call assert_equal(['cerchiamo'], spellsuggest('cerciamo', 1))
+ call assert_equal(['cantavamo'], spellsuggest('cantarevamo', 1))
+ call assert_equal(['odiamo'], spellsuggest('odiiamo', 1))
+
+ set spell& spelllang&
+ call delete('XtestSFX.dic')
+ call delete('XtestSFX.aff')
+ call delete('XtestSFX-utf8.spl')
+endfunc
+
+" When 'spellfile' is not set, adding a new good word will automatically set
+" the 'spellfile'
+func Test_init_spellfile()
+ let save_rtp = &rtp
+ let save_encoding = &encoding
+ call mkdir('Xrtp/spell', 'p')
+ call writefile(['vim'], 'Xrtp/spell/Xtest.dic')
+ silent mkspell Xrtp/spell/Xtest.utf-8.spl Xrtp/spell/Xtest.dic
+ set runtimepath=./Xrtp
+ set spelllang=Xtest
+ set spell
+ silent spellgood abc
+ call assert_equal('./Xrtp/spell/Xtest.utf-8.add', &spellfile)
+ call assert_equal(['abc'], readfile('Xrtp/spell/Xtest.utf-8.add'))
+ call assert_true(filereadable('Xrtp/spell/Xtest.utf-8.spl'))
+ set spell& spelllang& spellfile&
+ call delete('Xrtp', 'rf')
+ let &encoding = save_encoding
+ let &rtp = save_rtp
+ %bw!
+endfunc
+
+" Test for the 'mkspellmem' option
+func Test_mkspellmem_opt()
+ call assert_fails('set mkspellmem=1000', 'E474:')
+ call assert_fails('set mkspellmem=1000,', 'E474:')
+ call assert_fails('set mkspellmem=1000,50', 'E474:')
+ call assert_fails('set mkspellmem=1000,50,', 'E474:')
+ call assert_fails('set mkspellmem=1000,50,10,', 'E474:')
+ call assert_fails('set mkspellmem=1000,50,0', 'E474:')
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_stat.vim b/src/nvim/testdir/test_stat.vim
index 170358e023..d3059664e9 100644
--- a/src/nvim/testdir/test_stat.vim
+++ b/src/nvim/testdir/test_stat.vim
@@ -1,11 +1,13 @@
" Tests for stat functions and checktime
+source check.vim
+
func CheckFileTime(doSleep)
let fnames = ['Xtest1.tmp', 'Xtest2.tmp', 'Xtest3.tmp']
let times = []
let result = 0
- " Use three files istead of localtim(), with a network filesystem the file
+ " Use three files instead of localtim(), with a network filesystem the file
" times may differ at bit
let fl = ['Hello World!']
for fname in fnames
@@ -74,6 +76,44 @@ func Test_checktime()
call delete(fname)
endfunc
+func Test_checktime_fast()
+ CheckFeature nanotime
+
+ let fname = 'Xtest.tmp'
+
+ let fl = ['Hello World!']
+ call writefile(fl, fname)
+ set autoread
+ exec 'e' fname
+ let fl = readfile(fname)
+ let fl[0] .= ' - checktime'
+ sleep 10m " make test less flaky in Nvim
+ call writefile(fl, fname)
+ checktime
+ call assert_equal(fl[0], getline(1))
+
+ call delete(fname)
+endfunc
+
+func Test_autoread_fast()
+ CheckFeature nanotime
+
+ " this is timing sensitive
+ let g:test_is_flaky = 1
+
+ new Xautoread
+ setlocal autoread
+ call setline(1, 'foo')
+ w!
+ sleep 10m
+ call writefile(['bar'], 'Xautoread')
+ sleep 10m
+ checktime
+ call assert_equal('bar', trim(getline(1)))
+
+ call delete('Xautoread')
+endfunc
+
func Test_autoread_file_deleted()
new Xautoread
set autoread
diff --git a/src/nvim/testdir/test_statusline.vim b/src/nvim/testdir/test_statusline.vim
index a3e4dcdd25..ec35fac964 100644
--- a/src/nvim/testdir/test_statusline.vim
+++ b/src/nvim/testdir/test_statusline.vim
@@ -2,13 +2,15 @@
"
" Not tested yet:
" %N
-" %T
-" %X
source view_util.vim
source check.vim
source term_util.vim
+func SetUp()
+ set laststatus=2
+endfunc
+
func s:get_statusline()
return ScreenLines(&lines - 1, &columns)[0]
endfunc
@@ -104,6 +106,18 @@ func Test_statusline()
set statusline=%F
call assert_match('/testdir/Xstatusline\s*$', s:get_statusline())
+ " Test for min and max width with %(. For some reason, if this test is moved
+ " after the below test for the help buffer flag, then the code to truncate
+ " the string is not executed.
+ set statusline=%015(%f%)
+ call assert_match('^ Xstatusline\s*$', s:get_statusline())
+ set statusline=%.6(%f%)
+ call assert_match('^<sline\s*$', s:get_statusline())
+ set statusline=%14f
+ call assert_match('^ Xstatusline\s*$', s:get_statusline())
+ set statusline=%.4L
+ call assert_match('^10>3\s*$', s:get_statusline())
+
" %h: Help buffer flag, text is "[help]".
" %H: Help buffer flag, text is ",HLP".
set statusline=%h,%H
@@ -186,7 +200,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".
@@ -452,7 +475,6 @@ func Test_statusline_removed_group()
call writefile(lines, 'XTest_statusline')
let buf = RunVimInTerminal('-S XTest_statusline', {'rows': 10, 'cols': 50})
- call term_wait(buf, 100)
call VerifyScreenDump(buf, 'Test_statusline_1', {})
" clean up
@@ -498,5 +520,50 @@ func Test_statusline_after_split_vsplit()
set ls& stl&
endfunc
+" Test using a multibyte character for 'stl' and 'stlnc' items in 'fillchars'
+" with a custom 'statusline'
+func Test_statusline_mbyte_fillchar()
+ only
+ set laststatus=2
+ set fillchars=vert:\|,fold:-,stl:━,stlnc:═
+ set statusline=a%=b
+ call assert_match('^a\+━\+b$', s:get_statusline())
+ vnew
+ call assert_match('^a\+━\+b━a\+═\+b$', s:get_statusline())
+ wincmd w
+ call assert_match('^a\+═\+b═a\+━\+b$', s:get_statusline())
+ set statusline& fillchars& laststatus&
+ %bw!
+endfunc
+
+" Used to write beyond allocated memory. This assumes MAXPATHL is 4096 bytes.
+func Test_statusline_verylong_filename()
+ let fname = repeat('x', 4090)
+ " Nvim's swap file creation fails on Windows (E303) due to fname's length
+ " exe "new " .. fname
+ exe "noswapfile new " .. fname
+ set buftype=help
+ set previewwindow
+ redraw
+ bwipe!
+endfunc
+
+func Test_statusline_highlight_truncate()
+ CheckScreendump
+
+ let lines =<< trim END
+ set laststatus=2
+ hi! link User1 Directory
+ hi! link User2 ErrorMsg
+ set statusline=%.5(%1*ABC%2*DEF%1*GHI%)
+ END
+ call writefile(lines, 'XTest_statusline')
+
+ let buf = RunVimInTerminal('-S XTest_statusline', {'rows': 6})
+ call VerifyScreenDump(buf, 'Test_statusline_hl', {})
+
+ call StopVimInTerminal(buf)
+ call delete('XTest_statusline')
+endfunc
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_substitute.vim b/src/nvim/testdir/test_substitute.vim
index 20b760ac15..f795d1c0cf 100644
--- a/src/nvim/testdir/test_substitute.vim
+++ b/src/nvim/testdir/test_substitute.vim
@@ -1,4 +1,6 @@
-" Tests for multi-line regexps with ":s".
+" Tests for the substitute (:s) command
+
+source shared.vim
func Test_multiline_subst()
enew!
@@ -51,10 +53,12 @@ func Test_substitute_variants()
\ { 'cmd': ':s/t/r/cg', 'exp': 'Tesring srring', 'prompt': 'a' },
\ { 'cmd': ':s/t/r/ci', 'exp': 'resting string', 'prompt': 'y' },
\ { 'cmd': ':s/t/r/cI', 'exp': 'Tesring string', 'prompt': 'y' },
+ \ { 'cmd': ':s/t/r/c', 'exp': 'Testing string', 'prompt': 'n' },
\ { 'cmd': ':s/t/r/cn', 'exp': ln },
\ { 'cmd': ':s/t/r/cp', 'exp': 'Tesring string', 'prompt': 'y' },
\ { 'cmd': ':s/t/r/cl', 'exp': 'Tesring string', 'prompt': 'y' },
\ { 'cmd': ':s/t/r/gc', 'exp': 'Tesring srring', 'prompt': 'a' },
+ \ { 'cmd': ':s/i/I/gc', 'exp': 'TestIng string', 'prompt': 'l' },
\ { 'cmd': ':s/foo/bar/ge', 'exp': ln },
\ { 'cmd': ':s/t/r/g', 'exp': 'Tesring srring' },
\ { 'cmd': ':s/t/r/gi', 'exp': 'resring srring' },
@@ -86,6 +90,7 @@ func Test_substitute_variants()
\ { 'cmd': ':s//r/rp', 'exp': 'Testr string' },
\ { 'cmd': ':s//r/rl', 'exp': 'Testr string' },
\ { 'cmd': ':s//r/r', 'exp': 'Testr string' },
+ \ { 'cmd': ':s/i/I/gc', 'exp': 'Testing string', 'prompt': 'q' },
\]
for var in variants
@@ -105,7 +110,7 @@ func Test_substitute_variants()
call assert_equal(var.exp, getline('.'), msg)
endfor
endfor
-endfunction
+endfunc
" Test the l, p, # flags.
func Test_substitute_flags_lp()
@@ -137,7 +142,7 @@ func Test_substitute_repeat()
" This caused an invalid memory access.
split Xfile
s/^/x
- call feedkeys("gQsc\<CR>y", 'tx')
+ call feedkeys("Qsc\<CR>y", 'tx')
bwipe!
endfunc
@@ -149,7 +154,6 @@ func Run_SubCmd_Tests(tests)
for t in a:tests
let start = line('.') + 1
let end = start + len(t[2]) - 1
- " TODO: why is there a one second delay the first time we get here?
exe "normal o" . t[0]
call cursor(start, 1)
exe t[1]
@@ -184,7 +188,8 @@ func Test_sub_cmd_1()
\ ['sSs', 's/S/\c/', ['scs']],
\ ['tTt', "s/T/\<C-V>\<C-J>/", ["t\<C-V>\<C-J>t"]],
\ ['U', 's/U/\L\uuUu\l\EU/', ['UuuU']],
- \ ['V', 's/V/\U\lVvV\u\Ev/', ['vVVv']]
+ \ ['V', 's/V/\U\lVvV\u\Ev/', ['vVVv']],
+ \ ['\', 's/\\/\\\\/', ['\\']]
\ ]
call Run_SubCmd_Tests(tests)
endfunc
@@ -215,7 +220,8 @@ func Test_sub_cmd_2()
\ ['sSs', 's/S/\c/', ['scs']],
\ ['tTt', "s/T/\<C-V>\<C-J>/", ["t\<C-V>\<C-J>t"]],
\ ['U', 's/U/\L\uuUu\l\EU/', ['UuuU']],
- \ ['V', 's/V/\U\lVvV\u\Ev/', ['vVVv']]
+ \ ['V', 's/V/\U\lVvV\u\Ev/', ['vVVv']],
+ \ ['\', 's/\\/\\\\/', ['\\']]
\ ]
call Run_SubCmd_Tests(tests)
endfunc
@@ -288,7 +294,7 @@ endfunc
" Test for *:s%* on :substitute.
func Test_sub_cmd_6()
- throw "skipped: Nvim removed POSIX-related 'cpoptions' flags"
+ throw 'Skipped: Nvim does not support cpoptions flag "/"'
set magic&
set cpo+=/
@@ -384,6 +390,10 @@ func Test_substitute_join()
call assert_equal(["foo\tbarbar\<C-H>foo"], getline(1, '$'))
call assert_equal('\n', histget("search", -1))
+ call setline(1, ['foo', 'bar', 'baz', 'qux'])
+ call execute('1,2s/\n//')
+ call assert_equal(['foobarbaz', 'qux'], getline(1, '$'))
+
bwipe!
endfunc
@@ -398,6 +408,11 @@ func Test_substitute_count()
call assert_fails('s/foo/bar/0', 'E939:')
+ call setline(1, ['foo foo', 'foo foo', 'foo foo', 'foo foo', 'foo foo'])
+ 2,4s/foo/bar/ 10
+ call assert_equal(['foo foo', 'foo foo', 'foo foo', 'bar foo', 'bar foo'],
+ \ getline(1, '$'))
+
bwipe!
endfunc
@@ -416,6 +431,10 @@ func Test_substitute_flag_n()
" No substitution should have been done.
call assert_equal(lines, getline(1, '$'))
+ %delete _
+ call setline(1, ['A', 'Bar', 'Baz'])
+ call assert_equal("\n1 match on 1 line", execute('s/\nB\@=//gn'))
+
bwipe!
endfunc
@@ -656,10 +675,11 @@ func Test_nocatch_sub_failure_handling()
endfunc
new
call setline(1, ['1 aaa', '2 aaa', '3 aaa'])
- %s/aaa/\=Foo()/g
+ " need silent! to avoid a delay when entering Insert mode
+ silent! %s/aaa/\=Foo()/g
call assert_equal(['1 0', '2 0', '3 0'], getline(1, 3))
- " Trow without try-catch causes abort after the first line.
+ " Throw without try-catch causes abort after the first line.
" We cannot test this, since it would stop executing the test script.
" try/catch does not result in any changes
@@ -749,6 +769,80 @@ func Test_sub_beyond_end()
bwipe!
endfunc
+" Test for repeating last substitution using :~ and :&r
+func Test_repeat_last_sub()
+ new
+ call setline(1, ['blue green yellow orange white'])
+ s/blue/red/
+ let @/ = 'yellow'
+ ~
+ let @/ = 'white'
+ :&r
+ let @/ = 'green'
+ s//gray
+ call assert_equal('red gray red orange red', getline(1))
+ close!
+endfunc
+
+" Test for Vi compatible substitution:
+" \/{string}/, \?{string}? and \&{string}&
+func Test_sub_vi_compatibility()
+ new
+ call setline(1, ['blue green yellow orange blue'])
+ let @/ = 'orange'
+ s\/white/
+ let @/ = 'blue'
+ s\?amber?
+ let @/ = 'white'
+ s\&green&
+ call assert_equal('amber green yellow white green', getline(1))
+ close!
+endfunc
+
+" Test for substitute with the new text longer than the original text
+func Test_sub_expand_text()
+ new
+ call setline(1, 'abcabcabcabcabcabcabcabc')
+ s/b/\=repeat('B', 10)/g
+ call assert_equal(repeat('aBBBBBBBBBBc', 8), getline(1))
+ close!
+endfunc
+
+" Test for command failures when the last substitute pattern is not set.
+func Test_sub_with_no_last_pat()
+ let lines =<< trim [SCRIPT]
+ call assert_fails('~', 'E33:')
+ call assert_fails('s//abc/g', 'E476:')
+ call assert_fails('s\/bar', 'E476:')
+ call assert_fails('s\&bar&', 'E476:')
+ call writefile(v:errors, 'Xresult')
+ qall!
+ [SCRIPT]
+ call writefile(lines, 'Xscript')
+ if RunVim([], [], '--clean -S Xscript')
+ call assert_equal([], readfile('Xresult'))
+ endif
+
+ " Nvim does not support cpoptions flag "/"'
+ " let lines =<< trim [SCRIPT]
+ " set cpo+=/
+ " call assert_fails('s/abc/%/', 'E33:')
+ " call writefile(v:errors, 'Xresult')
+ " qall!
+ " [SCRIPT]
+ " call writefile(lines, 'Xscript')
+ " if RunVim([], [], '--clean -S Xscript')
+ " call assert_equal([], readfile('Xresult'))
+ " endif
+
+ call delete('Xscript')
+ call delete('Xresult')
+endfunc
+
+func Test_substitute()
+ call assert_equal('a1a2a3a', substitute('123', '\zs', 'a', 'g'))
+endfunc
+
func Test_submatch_list_concatenate()
let pat = 'A\(.\)'
let Rep = {-> string([submatch(0, 1)] + [[submatch(1)]])}
@@ -764,4 +858,375 @@ func Test_substitute_skipped_range()
bwipe!
endfunc
+" This was using "old_sub" after it was freed.
+func Test_using_old_sub()
+ " set compatible maxfuncdepth=10
+ set maxfuncdepth=10
+ new
+ call setline(1, 'some text.')
+ func Repl()
+ ~
+ s/
+ endfunc
+ silent! s/\%')/\=Repl()
+
+ delfunc Repl
+ bwipe!
+ set nocompatible
+endfunc
+
+" This was switching windows in between computing the length and using it.
+func Test_sub_change_window()
+ silent! lfile
+ sil! norm o0000000000000000000000000000000000000000000000000000
+ func Repl()
+ lopen
+ endfunc
+ silent! s/\%')/\=Repl()
+ bwipe!
+ bwipe!
+ delfunc Repl
+endfunc
+
+" This was undoign a change in between computing the length and using it.
+func Do_Test_sub_undo_change()
+ new
+ norm o0000000000000000000000000000000000000000000000000000
+ silent! s/\%')/\=Repl()
+ bwipe!
+endfunc
+
+func Test_sub_undo_change()
+ func Repl()
+ silent! norm g-
+ endfunc
+ call Do_Test_sub_undo_change()
+
+ func! Repl()
+ silent earlier
+ endfunc
+ call Do_Test_sub_undo_change()
+
+ delfunc Repl
+endfunc
+
+" This was opening a command line window from the expression
+func Test_sub_open_cmdline_win()
+ " the error only happens in a very specific setup, run a new Vim instance to
+ " get a clean starting point.
+ let lines =<< trim [SCRIPT]
+ set vb t_vb=
+ norm o0000000000000000000000000000000000000000000000000000
+ func Replace()
+ norm q/
+ endfunc
+ s/\%')/\=Replace()
+ redir >Xresult
+ messages
+ redir END
+ qall!
+ [SCRIPT]
+ call writefile(lines, 'Xscript')
+ if RunVim([], [], '-u NONE -S Xscript')
+ call assert_match('E565: Not allowed to change text or change window',
+ \ readfile('Xresult')->join('XX'))
+ endif
+
+ call delete('Xscript')
+ call delete('Xresult')
+endfunc
+
+" Test for the 2-letter and 3-letter :substitute commands
+func Test_substitute_short_cmd()
+ new
+ call setline(1, ['one', 'one one one'])
+ s/one/two
+ call cursor(2, 1)
+
+ " :sc
+ call feedkeys(":sc\<CR>y", 'xt')
+ call assert_equal('two one one', getline(2))
+
+ " :scg
+ call setline(2, 'one one one')
+ call feedkeys(":scg\<CR>nyq", 'xt')
+ call assert_equal('one two one', getline(2))
+
+ " :sci
+ call setline(2, 'ONE One onE')
+ call feedkeys(":sci\<CR>y", 'xt')
+ call assert_equal('two One onE', getline(2))
+
+ " :scI
+ set ignorecase
+ call setline(2, 'ONE One one')
+ call feedkeys(":scI\<CR>y", 'xt')
+ call assert_equal('ONE One two', getline(2))
+ set ignorecase&
+
+ " :scn
+ call setline(2, 'one one one')
+ let t = execute('scn')->split("\n")
+ call assert_equal(['1 match on 1 line'], t)
+ call assert_equal('one one one', getline(2))
+
+ " :scp
+ call setline(2, "\tone one one")
+ redir => output
+ call feedkeys(":scp\<CR>y", 'xt')
+ redir END
+ call assert_equal(' two one one', output->split("\n")[-1])
+ call assert_equal("\ttwo one one", getline(2))
+
+ " :scl
+ call setline(2, "\tone one one")
+ redir => output
+ call feedkeys(":scl\<CR>y", 'xt')
+ redir END
+ call assert_equal("^Itwo one one$", output->split("\n")[-1])
+ call assert_equal("\ttwo one one", getline(2))
+
+ " :sgc
+ call setline(2, 'one one one one one')
+ call feedkeys(":sgc\<CR>nyyq", 'xt')
+ call assert_equal('one two two one one', getline(2))
+
+ " :sg
+ call setline(2, 'one one one')
+ sg
+ call assert_equal('two two two', getline(2))
+
+ " :sgi
+ call setline(2, 'ONE One onE')
+ sgi
+ call assert_equal('two two two', getline(2))
+
+ " :sgI
+ set ignorecase
+ call setline(2, 'ONE One one')
+ sgI
+ call assert_equal('ONE One two', getline(2))
+ set ignorecase&
+
+ " :sgn
+ call setline(2, 'one one one')
+ let t = execute('sgn')->split("\n")
+ call assert_equal(['3 matches on 1 line'], t)
+ call assert_equal('one one one', getline(2))
+
+ " :sgp
+ call setline(2, "\tone one one")
+ redir => output
+ sgp
+ redir END
+ call assert_equal(' two two two', output->split("\n")[-1])
+ call assert_equal("\ttwo two two", getline(2))
+
+ " :sgl
+ call setline(2, "\tone one one")
+ redir => output
+ sgl
+ redir END
+ call assert_equal("^Itwo two two$", output->split("\n")[-1])
+ call assert_equal("\ttwo two two", getline(2))
+
+ " :sgr
+ call setline(2, "one one one")
+ call cursor(2, 1)
+ s/abc/xyz/e
+ let @/ = 'one'
+ sgr
+ call assert_equal('xyz xyz xyz', getline(2))
+
+ " :sic
+ call cursor(1, 1)
+ s/one/two/e
+ call setline(2, "ONE One one")
+ call cursor(2, 1)
+ call feedkeys(":sic\<CR>y", 'xt')
+ call assert_equal('two One one', getline(2))
+
+ " :si
+ call setline(2, "ONE One one")
+ si
+ call assert_equal('two One one', getline(2))
+
+ " :siI
+ call setline(2, "ONE One one")
+ siI
+ call assert_equal('ONE One two', getline(2))
+
+ " :sin
+ call setline(2, 'ONE One onE')
+ let t = execute('sin')->split("\n")
+ call assert_equal(['1 match on 1 line'], t)
+ call assert_equal('ONE One onE', getline(2))
+
+ " :sip
+ call setline(2, "\tONE One onE")
+ redir => output
+ sip
+ redir END
+ call assert_equal(' two One onE', output->split("\n")[-1])
+ call assert_equal("\ttwo One onE", getline(2))
+
+ " :sir
+ call setline(2, "ONE One onE")
+ call cursor(2, 1)
+ s/abc/xyz/e
+ let @/ = 'one'
+ sir
+ call assert_equal('xyz One onE', getline(2))
+
+ " :sIc
+ call cursor(1, 1)
+ s/one/two/e
+ call setline(2, "ONE One one")
+ call cursor(2, 1)
+ call feedkeys(":sIc\<CR>y", 'xt')
+ call assert_equal('ONE One two', getline(2))
+
+ " :sIg
+ call setline(2, "ONE one onE one")
+ sIg
+ call assert_equal('ONE two onE two', getline(2))
+
+ " :sIi
+ call setline(2, "ONE One one")
+ sIi
+ call assert_equal('two One one', getline(2))
+
+ " :sI
+ call setline(2, "ONE One one")
+ sI
+ call assert_equal('ONE One two', getline(2))
+
+ " :sIn
+ call setline(2, 'ONE One one')
+ let t = execute('sIn')->split("\n")
+ call assert_equal(['1 match on 1 line'], t)
+ call assert_equal('ONE One one', getline(2))
+
+ " :sIp
+ call setline(2, "\tONE One one")
+ redir => output
+ sIp
+ redir END
+ call assert_equal(' ONE One two', output->split("\n")[-1])
+ call assert_equal("\tONE One two", getline(2))
+
+ " :sIl
+ call setline(2, "\tONE onE one")
+ redir => output
+ sIl
+ redir END
+ call assert_equal("^IONE onE two$", output->split("\n")[-1])
+ call assert_equal("\tONE onE two", getline(2))
+
+ " :sIr
+ call setline(2, "ONE one onE")
+ call cursor(2, 1)
+ s/abc/xyz/e
+ let @/ = 'one'
+ sIr
+ call assert_equal('ONE xyz onE', getline(2))
+
+ " :src
+ call setline(2, "ONE one one")
+ call cursor(2, 1)
+ s/abc/xyz/e
+ let @/ = 'one'
+ call feedkeys(":src\<CR>y", 'xt')
+ call assert_equal('ONE xyz one', getline(2))
+
+ " :srg
+ call setline(2, "one one one")
+ call cursor(2, 1)
+ s/abc/xyz/e
+ let @/ = 'one'
+ srg
+ call assert_equal('xyz xyz xyz', getline(2))
+
+ " :sri
+ call setline(2, "ONE one onE")
+ call cursor(2, 1)
+ s/abc/xyz/e
+ let @/ = 'one'
+ sri
+ call assert_equal('xyz one onE', getline(2))
+
+ " :srI
+ call setline(2, "ONE one onE")
+ call cursor(2, 1)
+ s/abc/xyz/e
+ let @/ = 'one'
+ srI
+ call assert_equal('ONE xyz onE', getline(2))
+
+ " :srn
+ call setline(2, "ONE one onE")
+ call cursor(2, 1)
+ s/abc/xyz/e
+ let @/ = 'one'
+ let t = execute('srn')->split("\n")
+ call assert_equal(['1 match on 1 line'], t)
+ call assert_equal('ONE one onE', getline(2))
+
+ " :srp
+ call setline(2, "\tONE one onE")
+ call cursor(2, 1)
+ s/abc/xyz/e
+ let @/ = 'one'
+ redir => output
+ srp
+ redir END
+ call assert_equal(' ONE xyz onE', output->split("\n")[-1])
+ call assert_equal("\tONE xyz onE", getline(2))
+
+ " :srl
+ call setline(2, "\tONE one onE")
+ call cursor(2, 1)
+ s/abc/xyz/e
+ let @/ = 'one'
+ redir => output
+ srl
+ redir END
+ call assert_equal("^IONE xyz onE$", output->split("\n")[-1])
+ call assert_equal("\tONE xyz onE", getline(2))
+
+ " :sr
+ call setline(2, "ONE one onE")
+ call cursor(2, 1)
+ s/abc/xyz/e
+ let @/ = 'one'
+ sr
+ call assert_equal('ONE xyz onE', getline(2))
+
+ " :sce
+ s/abc/xyz/e
+ call assert_fails("sc", 'E486:')
+ sce
+ " :sge
+ call assert_fails("sg", 'E486:')
+ sge
+ " :sie
+ call assert_fails("si", 'E486:')
+ sie
+ " :sIe
+ call assert_fails("sI", 'E486:')
+ sIe
+
+ bw!
+endfunc
+
+" This should be done last to reveal a memory leak when vim_regsub_both() is
+" called to evaluate an expression but it is not used in a second call.
+func Test_z_substitute_expr_leak()
+ func SubExpr()
+ ~n
+ endfunc
+ silent! s/\%')/\=SubExpr()
+ delfunc SubExpr
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_suspend.vim b/src/nvim/testdir/test_suspend.vim
index 4b3bd5eadf..bf88bd4453 100644
--- a/src/nvim/testdir/test_suspend.vim
+++ b/src/nvim/testdir/test_suspend.vim
@@ -26,8 +26,8 @@ func Test_suspend()
" Wait for shell prompt.
call WaitForAssert({-> assert_match('[$#] $', term_getline(buf, '.'))})
- call term_sendkeys(buf, v:progpath
- \ . " --clean -X"
+ call term_sendkeys(buf, GetVimCommandClean()
+ \ . " -X"
\ . " -c 'set nu'"
\ . " -c 'call setline(1, \"foo\")'"
\ . " Xfoo\<CR>")
diff --git a/src/nvim/testdir/test_swap.vim b/src/nvim/testdir/test_swap.vim
index b3018b2b0d..923e1cbf50 100644
--- a/src/nvim/testdir/test_swap.vim
+++ b/src/nvim/testdir/test_swap.vim
@@ -1,6 +1,7 @@
" Tests for the swap feature
source check.vim
+source shared.vim
func s:swapname()
return trim(execute('swapname'))
@@ -198,14 +199,17 @@ func Test_swapfile_delete()
quit
call assert_equal(fnamemodify(swapfile_name, ':t'), fnamemodify(s:swapname, ':t'))
- " Write the swapfile with a modified PID, now it will be automatically
- " deleted. Process one should never be Vim.
- let swapfile_bytes[24:27] = 0z01000000
- call writefile(swapfile_bytes, swapfile_name)
- let s:swapname = ''
- split XswapfileText
- quit
- call assert_equal('', s:swapname)
+ " This test won't work as root because root can successfully run kill(1, 0)
+ if !IsRoot()
+ " Write the swapfile with a modified PID, now it will be automatically
+ " deleted. Process one should never be Vim.
+ let swapfile_bytes[24:27] = 0z01000000
+ call writefile(swapfile_bytes, swapfile_name)
+ let s:swapname = ''
+ split XswapfileText
+ quit
+ call assert_equal('', s:swapname)
+ endif
" Now set the modified flag, the swap file will not be deleted
let swapfile_bytes[28 + 80 + 899] = 0x55
@@ -274,7 +278,6 @@ func Test_swap_recover_ext()
autocmd SwapExists * let v:swapchoice = 'r'
augroup END
-
" Create a valid swapfile by editing a file with a special extension.
split Xtest.scr
call setline(1, ['one', 'two', 'three'])
@@ -307,6 +310,46 @@ func Test_swap_recover_ext()
augroup! test_swap_recover_ext
endfunc
+" Test for closing a split window automatically when a swap file is detected
+" and 'Q' is selected in the confirmation prompt.
+func Test_swap_split_win()
+ autocmd! SwapExists
+ augroup test_swap_splitwin
+ autocmd!
+ autocmd SwapExists * let v:swapchoice = 'q'
+ augroup END
+
+ " Create a valid swapfile by editing a file with a special extension.
+ split Xtest.scr
+ call setline(1, ['one', 'two', 'three'])
+ write " file is written, not modified
+ write " write again to make sure the swapfile is created
+ " read the swapfile as a Blob
+ let swapfile_name = swapname('%')
+ let swapfile_bytes = readfile(swapfile_name, 'B')
+
+ " Close and delete the file and recreate the swap file.
+ quit
+ call delete('Xtest.scr')
+ call writefile(swapfile_bytes, swapfile_name)
+ " Split edit the file again. This should fail to open the window
+ try
+ split Xtest.scr
+ catch
+ " E308 should be caught, not E306.
+ call assert_exception('E308:') " Original file may have been changed
+ endtry
+ call assert_equal(1, winnr('$'))
+
+ call delete('Xtest.scr')
+ call delete(swapfile_name)
+
+ augroup test_swap_splitwin
+ autocmd!
+ augroup END
+ augroup! test_swap_splitwin
+endfunc
+
" Test for selecting 'q' in the attention prompt
func Test_swap_prompt_splitwin()
CheckRunVimInTerminal
diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim
index 757866f5dc..7ba0149971 100644
--- a/src/nvim/testdir/test_syntax.vim
+++ b/src/nvim/testdir/test_syntax.vim
@@ -1,5 +1,8 @@
" Test for syntax and syntax iskeyword option
+source check.vim
+CheckFeature syntax
+
source view_util.vim
source screendump.vim
@@ -197,6 +200,12 @@ func Test_syntax_completion()
call assert_match('^"syn match Boolean Character ', @:)
endfunc
+func Test_echohl_completion()
+ call feedkeys(":echohl no\<C-A>\<C-B>\"\<CR>", 'tx')
+ " call assert_equal('"echohl NonText Normal none', @:)
+ call assert_equal('"echohl NonText Normal NormalFloat NormalNC none', @:)
+endfunc
+
func Test_syntax_arg_skipped()
syn clear
syntax case ignore
@@ -728,6 +737,134 @@ func Test_syntax_foldlevel()
quit!
endfunc
+func Test_search_syntax_skip()
+ new
+ let lines =<< trim END
+
+ /* This is VIM */
+ Another Text for VIM
+ let a = "VIM"
+ END
+ call setline(1, lines)
+ syntax on
+ syntax match Comment "^/\*.*\*/"
+ syntax match String '".*"'
+
+ " Skip argument using string evaluation.
+ 1
+ call search('VIM', 'w', '', 0, 'synIDattr(synID(line("."), col("."), 1), "name") =~? "comment"')
+ call assert_equal('Another Text for VIM', getline('.'))
+
+ 1
+ call search('VIM', 'cw', '', 0, 'synIDattr(synID(line("."), col("."), 1), "name") !~? "string"')
+ call assert_equal(' let a = "VIM"', getline('.'))
+
+ " Skip argument using Lambda.
+ 1
+ call search('VIM', 'w', '', 0, { -> synIDattr(synID(line("."), col("."), 1), "name") =~? "comment"})
+ call assert_equal('Another Text for VIM', getline('.'))
+
+ 1
+ call search('VIM', 'cw', '', 0, { -> synIDattr(synID(line("."), col("."), 1), "name") !~? "string"})
+ call assert_equal(' let a = "VIM"', getline('.'))
+
+ " Skip argument using funcref.
+ func InComment()
+ return synIDattr(synID(line("."), col("."), 1), "name") =~? "comment"
+ endfunc
+ func NotInString()
+ return synIDattr(synID(line("."), col("."), 1), "name") !~? "string"
+ endfunc
+
+ 1
+ call search('VIM', 'w', '', 0, function('InComment'))
+ call assert_equal('Another Text for VIM', getline('.'))
+
+ 1
+ call search('VIM', 'cw', '', 0, function('NotInString'))
+ call assert_equal(' let a = "VIM"', getline('.'))
+
+ delfunc InComment
+ delfunc NotInString
+ bwipe!
+endfunc
+
+func Test_syn_contained_transparent()
+ " Comments starting with "Regression:" show the result when the highlighting
+ " span of the containing item is assigned to the contained region.
+ syntax on
+
+ let l:case = "Transparent region contained in region"
+ new
+ syntax region X start=/\[/ end=/\]/ contained transparent
+ syntax region Y start=/(/ end=/)/ contains=X
+
+ call setline(1, "==(--[~~]--)==")
+ let l:expected = " YYYYYYYYYY "
+ eval AssertHighlightGroups(1, 1, l:expected, 1, l:case)
+ syntax clear Y X
+ bw!
+
+ let l:case = "Transparent region extends region"
+ new
+ syntax region X start=/\[/ end=/\]/ contained transparent
+ syntax region Y start=/(/ end=/)/ end=/e/ contains=X
+
+ call setline(1, "==(--[~~e~~]--)==")
+ let l:expected = " YYYYYYYYYYYYY "
+ " Regression: " YYYYYYY YYY "
+ eval AssertHighlightGroups(1, 1, l:expected, 1, l:case)
+ syntax clear Y X
+ bw!
+
+ let l:case = "Nested transparent regions extend region"
+ new
+ syntax region X start=/\[/ end=/\]/ contained transparent
+ syntax region Y start=/(/ end=/)/ end=/e/ contains=X
+
+ call setline(1, "==(--[~~e~~[~~e~~]~~e~~]--)==")
+ let l:expected = " YYYYYYYYYYYYYYYYYYYYYYYYY "
+ " Regression: " YYYYYYY YYYYYYYYY "
+ eval AssertHighlightGroups(1, 1, l:expected, 1, l:case)
+ syntax clear Y X
+ bw!
+
+ let l:case = "Transparent region contained in match"
+ new
+ syntax region X start=/\[/ end=/\]/ contained transparent
+ syntax match Y /(.\{-})/ contains=X
+
+ call setline(1, "==(--[~~]--)==")
+ let l:expected = " YYYYYYYYYY "
+ eval AssertHighlightGroups(1, 1, l:expected, 1, l:case)
+ syntax clear Y X
+ bw!
+
+ let l:case = "Transparent region extends match"
+ new
+ syntax region X start=/\[/ end=/\]/ contained transparent
+ syntax match Y /(.\{-}[e)]/ contains=X
+
+ call setline(1, "==(--[~~e~~]--)==")
+ let l:expected = " YYYYYYYYYY "
+ " Regression: " YYYYYYY "
+ eval AssertHighlightGroups(1, 1, l:expected, 1, l:case)
+ syntax clear Y X
+ bw!
+
+ let l:case = "Nested transparent regions extend match"
+ new
+ syntax region X start=/\[/ end=/\]/ contained transparent
+ syntax match Y /(.\{-}[e)]/ contains=X
+
+ call setline(1, "==(--[~~e~~[~~e~~]~~e~~]--)==")
+ let l:expected = " YYYYYYYYYYYYYYYYYYYYYY "
+ " Regression: " YYYYYYY YYYYYY "
+ eval AssertHighlightGroups(1, 1, l:expected, 1, l:case)
+ syntax clear Y X
+ bw!
+endfunc
+
func Test_syn_include_contains_TOP()
let l:case = "TOP in included syntax means its group list name"
new
@@ -744,5 +881,18 @@ func Test_syn_include_contains_TOP()
bw!
endfunc
+" This was using freed memory
+func Test_WinEnter_synstack_synID()
+ autocmd WinEnter * call synstack(line("."), col("."))
+ autocmd WinEnter * call synID(line('.'), col('.') - 1, 1)
+ call setline(1, 'aaaaa')
+ normal! $
+ new
+ close
+
+ au! WinEnter
+ bw!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_system.vim b/src/nvim/testdir/test_system.vim
index 1858b48807..18692f42c9 100644
--- a/src/nvim/testdir/test_system.vim
+++ b/src/nvim/testdir/test_system.vim
@@ -46,15 +46,15 @@ func Test_System()
bwipe!
call assert_fails('call system("wc -l", 99999)', 'E86:')
-endfunction
+endfunc
-function! Test_system_exmode()
+func Test_system_exmode()
if has('unix') " echo $? only works on Unix
- let cmd = ' -es --headless -u NONE -c "source Xscript" +q; echo "result=$?"'
+ let cmd = ' -es -c "source Xscript" +q; echo "result=$?"'
" Need to put this in a script, "catch" isn't found after an unknown
" function.
call writefile(['try', 'call doesnotexist()', 'catch', 'endtry'], 'Xscript')
- let a = system(v:progpath . cmd)
+ let a = system(GetVimCommand() . cmd)
call assert_match('result=0', a)
call assert_equal(0, v:shell_error)
endif
@@ -62,33 +62,33 @@ function! Test_system_exmode()
" Error before try does set error flag.
call writefile(['call nosuchfunction()', 'try', 'call doesnotexist()', 'catch', 'endtry'], 'Xscript')
if has('unix') " echo $? only works on Unix
- let a = system(v:progpath . cmd)
+ let a = system(GetVimCommand() . cmd)
call assert_notequal('0', a[0])
endif
- let cmd = ' -es --headless -u NONE -c "source Xscript" +q'
- let a = system(v:progpath . cmd)
+ let cmd = ' -es -c "source Xscript" +q'
+ let a = system(GetVimCommand() . cmd)
call assert_notequal(0, v:shell_error)
call delete('Xscript')
if has('unix') " echo $? only works on Unix
- let cmd = ' -es --headless -u NONE -c "call doesnotexist()" +q; echo $?'
- let a = system(v:progpath. cmd)
+ let cmd = ' -es -c "call doesnotexist()" +q; echo $?'
+ let a = system(GetVimCommand() . cmd)
call assert_notequal(0, a[0])
endif
- let cmd = ' -es --headless -u NONE -c "call doesnotexist()" +q'
- let a = system(v:progpath. cmd)
+ let cmd = ' -es -c "call doesnotexist()" +q'
+ let a = system(GetVimCommand(). cmd)
call assert_notequal(0, v:shell_error)
if has('unix') " echo $? only works on Unix
- let cmd = ' -es --headless -u NONE -c "call doesnotexist()|let a=1" +q; echo $?'
- let a = system(v:progpath. cmd)
+ let cmd = ' -es -c "call doesnotexist()|let a=1" +q; echo $?'
+ let a = system(GetVimCommand() . cmd)
call assert_notequal(0, a[0])
endif
- let cmd = ' -es --headless -u NONE -c "call doesnotexist()|let a=1" +q'
- let a = system(v:progpath. cmd)
+ let cmd = ' -es -c "call doesnotexist()|let a=1" +q'
+ let a = system(GetVimCommand() . cmd)
call assert_notequal(0, v:shell_error)
endfunc
diff --git a/src/nvim/testdir/test_tabline.vim b/src/nvim/testdir/test_tabline.vim
index 117d962d08..e58a412c5a 100644
--- a/src/nvim/testdir/test_tabline.vim
+++ b/src/nvim/testdir/test_tabline.vim
@@ -1,3 +1,4 @@
+" Test for tabline
source shared.vim
@@ -17,6 +18,9 @@ func TablineWithError()
endfunc
func Test_caught_error_in_tabline()
+ if has('gui')
+ set guioptions-=e
+ endif
let showtabline_save = &showtabline
set showtabline=2
let s:func_in_tabline_called = 0
@@ -30,6 +34,9 @@ func Test_caught_error_in_tabline()
endfunc
func Test_tabline_will_be_disabled_with_error()
+ if has('gui')
+ set guioptions-=e
+ endif
let showtabline_save = &showtabline
set showtabline=2
let s:func_in_tabline_called = 0
@@ -65,6 +72,47 @@ func Test_redrawtabline()
au! Bufadd
endfunc
+" Test for the "%T" and "%X" flags in the 'tabline' option
+func MyTabLine()
+ let s = ''
+ for i in range(tabpagenr('$'))
+ " set the tab page number (for mouse clicks)
+ let s .= '%' . (i + 1) . 'T'
+
+ " the label is made by MyTabLabel()
+ let s .= ' %{MyTabLabel(' . (i + 1) . ')} '
+ endfor
+
+ " after the last tab fill with TabLineFill and reset tab page nr
+ let s .= '%T'
+
+ " right-align the label to close the current tab page
+ if tabpagenr('$') > 1
+ let s .= '%=%Xclose'
+ endif
+
+ return s
+endfunc
+
+func MyTabLabel(n)
+ let buflist = tabpagebuflist(a:n)
+ let winnr = tabpagewinnr(a:n)
+ return bufname(buflist[winnr - 1])
+endfunc
+
+func Test_tabline_flags()
+ if has('gui')
+ set guioptions-=e
+ endif
+ set tabline=%!MyTabLine()
+ edit Xtabline1
+ tabnew Xtabline2
+ redrawtabline
+ call assert_match('^ Xtabline1 Xtabline2\s\+close$', Screenline(1))
+ set tabline=
+ %bw!
+endfunc
+
function EmptyTabname()
return ""
endfunction
@@ -86,6 +134,17 @@ func Test_tabline_empty_group()
set tabline=
endfunc
+" When there are exactly 20 tabline format items (the exact size of the
+" initial tabline items array), test that we don't write beyond the size
+" of the array.
+func Test_tabline_20_format_items_no_overrun()
+ set showtabline=2
+
+ let tabline = repeat('%#StatColorHi2#', 20)
+ let &tabline = tabline
+ redrawtabline
+ set showtabline& tabline&
+endfunc
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_tabpage.vim b/src/nvim/testdir/test_tabpage.vim
index 9869dc7590..d891684ecb 100644
--- a/src/nvim/testdir/test_tabpage.vim
+++ b/src/nvim/testdir/test_tabpage.vim
@@ -128,6 +128,8 @@ function Test_tabpage()
1tabmove
call assert_equal(2, tabpagenr())
+ call assert_fails('let t = tabpagenr("@")', 'E15:')
+ call assert_equal(0, tabpagewinnr(-1))
call assert_fails("99tabmove", 'E16:')
call assert_fails("+99tabmove", 'E16:')
call assert_fails("-99tabmove", 'E16:')
@@ -137,7 +139,13 @@ function Test_tabpage()
call assert_fails("tabmove -99", 'E474:')
call assert_fails("tabmove -3+", 'E474:')
call assert_fails("tabmove $3", 'E474:')
+ call assert_fails("%tabonly", 'E16:')
1tabonly!
+ tabmove 1
+ call assert_equal(1, tabpagenr())
+ tabnew
+ call assert_fails("-2tabmove", 'E474:')
+ tabonly!
endfunc
" Test autocommands
@@ -605,6 +613,16 @@ func Test_tabpage_cmdheight()
call delete('XTest_tabpage_cmdheight')
endfunc
+" Test for closing the tab page from a command window
+func Test_tabpage_close_cmdwin()
+ tabnew
+ call feedkeys("q/:tabclose\<CR>\<Esc>", 'xt')
+ call assert_equal(2, tabpagenr('$'))
+ call feedkeys("q/:tabonly\<CR>\<Esc>", 'xt')
+ call assert_equal(2, tabpagenr('$'))
+ tabonly
+endfunc
+
" Return the terminal key code for selecting a tab page from the tabline. This
" sequence contains the following codes: a CSI (0x9b), KS_TABLINE (0xf0),
" KS_FILLER (0x58) and then the tab page number.
@@ -683,4 +701,136 @@ func Test_tabline_tabmenu()
%bw!
endfunc
+" Test for changing the current tab page from an autocmd when closing a tab
+" page.
+func Test_tabpage_switchtab_on_close()
+ only
+ tabnew
+ tabnew
+ " Test for BufLeave
+ augroup T1
+ au!
+ au BufLeave * tabfirst
+ augroup END
+ tabclose
+ call assert_equal(1, tabpagenr())
+ augroup T1
+ au!
+ augroup END
+
+ " Test for WinLeave
+ $tabnew
+ augroup T1
+ au!
+ au WinLeave * tabfirst
+ augroup END
+ tabclose
+ call assert_equal(1, tabpagenr())
+ augroup T1
+ au!
+ augroup END
+
+ " Test for TabLeave
+ $tabnew
+ augroup T1
+ au!
+ au TabLeave * tabfirst
+ augroup END
+ tabclose
+ call assert_equal(1, tabpagenr())
+ augroup T1
+ au!
+ augroup END
+ augroup! T1
+ tabonly
+endfunc
+
+" Test for closing the destination tabpage when jumping from one to another.
+func Test_tabpage_close_on_switch()
+ tabnew
+ tabnew
+ edit Xfile
+ augroup T2
+ au!
+ au BufLeave Xfile 1tabclose
+ augroup END
+ tabfirst
+ call assert_equal(2, tabpagenr())
+ call assert_equal('Xfile', @%)
+ augroup T2
+ au!
+ augroup END
+ augroup! T2
+ %bw!
+endfunc
+
+" Test for jumping to last accessed tabpage
+func Test_lastused_tabpage()
+ tabonly!
+ call assert_equal(0, tabpagenr('#'))
+ call assert_beeps('call feedkeys("g\<Tab>", "xt")')
+ call assert_beeps('call feedkeys("\<C-Tab>", "xt")')
+ call assert_beeps('call feedkeys("\<C-W>g\<Tab>", "xt")')
+ call assert_fails('tabnext #', 'E475:')
+
+ " open four tab pages
+ tabnew
+ tabnew
+ tabnew
+
+ 2tabnext
+
+ " Test for g<Tab>
+ call assert_equal(4, tabpagenr('#'))
+ call feedkeys("g\<Tab>", "xt")
+ call assert_equal(4, tabpagenr())
+ call assert_equal(2, tabpagenr('#'))
+
+ " Test for <C-Tab>
+ call feedkeys("\<C-Tab>", "xt")
+ call assert_equal(2, tabpagenr())
+ call assert_equal(4, tabpagenr('#'))
+
+ " Test for <C-W>g<Tab>
+ call feedkeys("\<C-W>g\<Tab>", "xt")
+ call assert_equal(4, tabpagenr())
+ call assert_equal(2, tabpagenr('#'))
+
+ " Test for :tabnext #
+ tabnext #
+ call assert_equal(2, tabpagenr())
+ call assert_equal(4, tabpagenr('#'))
+
+ " Try to jump to a closed tab page
+ tabclose #
+ call assert_equal(0, tabpagenr('#'))
+ call feedkeys("g\<Tab>", "xt")
+ call assert_equal(2, tabpagenr())
+ call feedkeys("\<C-Tab>", "xt")
+ call assert_equal(2, tabpagenr())
+ call feedkeys("\<C-W>g\<Tab>", "xt")
+ call assert_equal(2, tabpagenr())
+ call assert_fails('tabnext #', 'E475:')
+ call assert_equal(2, tabpagenr())
+
+ " Test for :tabonly #
+ let wnum = win_getid()
+ $tabnew
+ tabonly #
+ call assert_equal(wnum, win_getid())
+ call assert_equal(1, tabpagenr('$'))
+
+ " Test for :tabmove #
+ tabnew
+ let wnum = win_getid()
+ tabnew
+ tabnew
+ tabnext 2
+ tabmove #
+ call assert_equal(4, tabpagenr())
+ call assert_equal(wnum, win_getid())
+
+ tabonly!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_tagjump.vim b/src/nvim/testdir/test_tagjump.vim
index 2aa04df42a..1dd656ece5 100644
--- a/src/nvim/testdir/test_tagjump.vim
+++ b/src/nvim/testdir/test_tagjump.vim
@@ -5,12 +5,57 @@ source screendump.vim
" SEGV occurs in older versions. (At least 7.4.1748 or older)
func Test_ptag_with_notagstack()
+ CheckFeature quickfix
+
set notagstack
call assert_fails('ptag does_not_exist_tag_name', 'E426')
set tagstack&vim
endfunc
+func Test_ptjump()
+ CheckFeature quickfix
+
+ set tags=Xtags
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "one\tXfile\t1",
+ \ "three\tXfile\t3",
+ \ "two\tXfile\t2"],
+ \ 'Xtags')
+ call writefile(['one', 'two', 'three'], 'Xfile')
+
+ %bw!
+ ptjump two
+ call assert_equal(2, winnr())
+ wincmd p
+ call assert_equal(1, &previewwindow)
+ call assert_equal('Xfile', expand("%:p:t"))
+ call assert_equal(2, line('.'))
+ call assert_equal(2, winnr('$'))
+ call assert_equal(1, winnr())
+ close
+ call setline(1, ['one', 'two', 'three'])
+ exe "normal 3G\<C-W>g}"
+ call assert_equal(2, winnr())
+ wincmd p
+ call assert_equal(1, &previewwindow)
+ call assert_equal('Xfile', expand("%:p:t"))
+ call assert_equal(3, line('.'))
+ call assert_equal(2, winnr('$'))
+ call assert_equal(1, winnr())
+ close
+ exe "normal 3G5\<C-W>\<C-G>}"
+ wincmd p
+ call assert_equal(5, winheight(0))
+ close
+
+ call delete('Xtags')
+ call delete('Xfile')
+ set tags&
+endfunc
+
func Test_cancel_ptjump()
+ CheckFeature quickfix
+
set tags=Xtags
call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
\ "word\tfile1\tcmd1",
@@ -70,6 +115,8 @@ func Test_duplicate_tagjump()
endfunc
func Test_tagjump_switchbuf()
+ CheckFeature quickfix
+
set tags=Xtags
call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
\ "second\tXfile1\t2",
@@ -641,7 +688,7 @@ func Test_tag_envvar()
endfunc
" Test for :ptag
-func Test_ptag()
+func Test_tag_preview()
call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
\ "second\tXfile1\t2",
\ "third\tXfile1\t3",],
@@ -655,11 +702,19 @@ func Test_ptag()
call assert_equal(2, winnr('$'))
call assert_equal(1, getwinvar(1, '&previewwindow'))
call assert_equal(0, getwinvar(2, '&previewwindow'))
- wincmd w
+ wincmd P
call assert_equal(3, line('.'))
" jump to the tag again
+ wincmd w
ptag third
+ wincmd P
+ call assert_equal(3, line('.'))
+
+ " jump to the newer tag
+ wincmd w
+ ptag
+ wincmd P
call assert_equal(3, line('.'))
" close the preview window
@@ -829,8 +884,8 @@ func Test_tag_last_search_pat()
%bwipe
endfunc
-" Test for jumping to a tag when the tag stack is full
-func Test_tag_stack_full()
+" Tag stack tests
+func Test_tag_stack()
let l = []
for i in range(10, 31)
let l += ["var" .. i .. "\tXfoo\t/^int var" .. i .. ";$/"]
@@ -844,6 +899,7 @@ func Test_tag_stack_full()
endfor
call writefile(l, 'Xfoo')
+ " Jump to a tag when the tag stack is full. Oldest entry should be removed.
enew
for i in range(10, 30)
exe "tag var" .. i
@@ -856,9 +912,15 @@ func Test_tag_stack_full()
call assert_equal('var12', l.items[0].tagname)
call assert_equal('var31', l.items[19].tagname)
- " Jump from the top of the stack
+ " Use tnext with a single match
+ call assert_fails('tnext', 'E427:')
+
+ " Jump to newest entry from the top of the stack
call assert_fails('tag', 'E556:')
+ " Pop with zero count from the top of the stack
+ call assert_fails('0pop', 'E556:')
+
" Pop from an unsaved buffer
enew!
call append(1, "sample text")
@@ -869,6 +931,13 @@ func Test_tag_stack_full()
" Pop all the entries in the tag stack
call assert_fails('30pop', 'E555:')
+ " Pop with a count when already at the bottom of the stack
+ call assert_fails('exe "normal 4\<C-T>"', 'E555:')
+ call assert_equal(1, gettagstack().curidx)
+
+ " Jump to newest entry from the bottom of the stack with zero count
+ call assert_fails('0tag', 'E555:')
+
" Pop the tag stack when it is empty
call settagstack(1, {'items' : []})
call assert_fails('pop', 'E73:')
@@ -895,6 +964,7 @@ func Test_tag_multimatch()
[CODE]
call writefile(code, 'Xfoo')
+ call settagstack(1, {'items' : []})
tag first
tlast
call assert_equal(3, line('.'))
@@ -903,12 +973,419 @@ func Test_tag_multimatch()
call assert_equal(1, line('.'))
call assert_fails('tprev', 'E425:')
+ tlast
+ call feedkeys("5\<CR>", 't')
+ tselect first
+ call assert_equal(2, gettagstack().curidx)
+
+ set ignorecase
+ tag FIRST
+ tnext
+ call assert_equal(2, line('.'))
+ tlast
+ tprev
+ call assert_equal(2, line('.'))
+ tNext
+ call assert_equal(1, line('.'))
+ set ignorecase&
+
+ call delete('Xtags')
+ call delete('Xfoo')
+ set tags&
+ %bwipe
+endfunc
+
+" Test for previewing multiple matching tags
+func Test_preview_tag_multimatch()
+ call writefile([
+ \ "!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "first\tXfoo\t1",
+ \ "first\tXfoo\t2",
+ \ "first\tXfoo\t3"],
+ \ 'Xtags')
+ set tags=Xtags
+ let code =<< trim [CODE]
+ int first() {}
+ int first() {}
+ int first() {}
+ [CODE]
+ call writefile(code, 'Xfoo')
+
+ enew | only
+ ptag first
+ ptlast
+ wincmd P
+ call assert_equal(3, line('.'))
+ wincmd w
+ call assert_fails('ptnext', 'E428:')
+ ptprev
+ wincmd P
+ call assert_equal(2, line('.'))
+ wincmd w
+ ptfirst
+ wincmd P
+ call assert_equal(1, line('.'))
+ wincmd w
+ call assert_fails('ptprev', 'E425:')
+ ptnext
+ wincmd P
+ call assert_equal(2, line('.'))
+ wincmd w
+ ptlast
+ call feedkeys("5\<CR>", 't')
+ ptselect first
+ wincmd P
+ call assert_equal(3, line('.'))
+
+ pclose
+
+ call delete('Xtags')
+ call delete('Xfoo')
+ set tags&
+ %bwipe
+endfunc
+
+" Test for jumping to multiple matching tags across multiple :tags commands
+func Test_tnext_multimatch()
+ call writefile([
+ \ "!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "first\tXfoo1\t1",
+ \ "first\tXfoo2\t1",
+ \ "first\tXfoo3\t1"],
+ \ 'Xtags')
+ set tags=Xtags
+ let code =<< trim [CODE]
+ int first() {}
+ [CODE]
+ call writefile(code, 'Xfoo1')
+ call writefile(code, 'Xfoo2')
+ call writefile(code, 'Xfoo3')
+
+ tag first
+ tag first
+ pop
+ tnext
+ tnext
+ call assert_fails('tnext', 'E428:')
+
+ call delete('Xtags')
+ call delete('Xfoo1')
+ call delete('Xfoo2')
+ call delete('Xfoo3')
+ set tags&
+ %bwipe
+endfunc
+
+" Test for jumping to multiple matching tags in non-existing files
+func Test_multimatch_non_existing_files()
+ call writefile([
+ \ "!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "first\tXfoo1\t1",
+ \ "first\tXfoo2\t1",
+ \ "first\tXfoo3\t1"],
+ \ 'Xtags')
+ set tags=Xtags
+
+ call settagstack(1, {'items' : []})
+ call assert_fails('tag first', 'E429:')
+ call assert_equal(3, gettagstack().items[0].matchnr)
+
+ call delete('Xtags')
+ set tags&
+ %bwipe
+endfunc
+
+func Test_tselect_listing()
+ call writefile([
+ \ "!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "first\tXfoo\t1" .. ';"' .. "\tv\ttyperef:typename:int\tfile:",
+ \ "first\tXfoo\t2" .. ';"' .. "\tv\ttyperef:typename:char\tfile:"],
+ \ 'Xtags')
+ set tags=Xtags
+
+ let code =<< trim [CODE]
+ static int first;
+ static char first;
+ [CODE]
+ call writefile(code, 'Xfoo')
+
+ call feedkeys("\<CR>", "t")
+ let l = split(execute("tselect first"), "\n")
+ let expected =<< [DATA]
+ # pri kind tag file
+ 1 FS v first Xfoo
+ typeref:typename:int
+ 1
+ 2 FS v first Xfoo
+ typeref:typename:char
+ 2
+Type number and <Enter> (q or empty cancels):
+[DATA]
+ call assert_equal(expected, l)
+
call delete('Xtags')
call delete('Xfoo')
set tags&
%bwipe
endfunc
+" Test for :isearch, :ilist, :ijump and :isplit commands
+" Test for [i, ]i, [I, ]I, [ CTRL-I, ] CTRL-I and CTRL-W i commands
+func Test_inc_search()
+ new
+ call setline(1, ['1:foo', '2:foo', 'foo', '3:foo', '4:foo'])
+ call cursor(3, 1)
+
+ " Test for [i and ]i
+ call assert_equal('1:foo', execute('normal [i'))
+ call assert_equal('2:foo', execute('normal 2[i'))
+ call assert_fails('normal 3[i', 'E387:')
+ call assert_equal('3:foo', execute('normal ]i'))
+ call assert_equal('4:foo', execute('normal 2]i'))
+ call assert_fails('normal 3]i', 'E389:')
+
+ " Test for :isearch
+ call assert_equal('1:foo', execute('isearch foo'))
+ call assert_equal('3:foo', execute('isearch 4 /foo/'))
+ call assert_fails('isearch 3 foo', 'E387:')
+ call assert_equal('3:foo', execute('+1,$isearch foo'))
+ call assert_fails('1,.-1isearch 3 foo', 'E389:')
+ call assert_fails('isearch bar', 'E389:')
+ call assert_fails('isearch /foo/3', 'E488:')
+
+ " Test for [I and ]I
+ call assert_equal([
+ \ ' 1: 1 1:foo',
+ \ ' 2: 2 2:foo',
+ \ ' 3: 3 foo',
+ \ ' 4: 4 3:foo',
+ \ ' 5: 5 4:foo'], split(execute('normal [I'), "\n"))
+ call assert_equal([
+ \ ' 1: 4 3:foo',
+ \ ' 2: 5 4:foo'], split(execute('normal ]I'), "\n"))
+
+ " Test for :ilist
+ call assert_equal([
+ \ ' 1: 1 1:foo',
+ \ ' 2: 2 2:foo',
+ \ ' 3: 3 foo',
+ \ ' 4: 4 3:foo',
+ \ ' 5: 5 4:foo'], split(execute('ilist foo'), "\n"))
+ call assert_equal([
+ \ ' 1: 4 3:foo',
+ \ ' 2: 5 4:foo'], split(execute('+1,$ilist /foo/'), "\n"))
+ call assert_fails('ilist bar', 'E389:')
+
+ " Test for [ CTRL-I and ] CTRL-I
+ exe "normal [\t"
+ call assert_equal([1, 3], [line('.'), col('.')])
+ exe "normal 2j4[\t"
+ call assert_equal([4, 3], [line('.'), col('.')])
+ call assert_fails("normal k3[\t", 'E387:')
+ call assert_fails("normal 6[\t", 'E389:')
+ exe "normal ]\t"
+ call assert_equal([4, 3], [line('.'), col('.')])
+ exe "normal k2]\t"
+ call assert_equal([5, 3], [line('.'), col('.')])
+ call assert_fails("normal 2k3]\t", 'E389:')
+
+ " Test for :ijump
+ call cursor(3, 1)
+ ijump foo
+ call assert_equal([1, 3], [line('.'), col('.')])
+ call cursor(3, 1)
+ ijump 4 /foo/
+ call assert_equal([4, 3], [line('.'), col('.')])
+ call cursor(3, 1)
+ call assert_fails('ijump 3 foo', 'E387:')
+ +,$ijump 2 foo
+ call assert_equal([5, 3], [line('.'), col('.')])
+ call assert_fails('ijump bar', 'E389:')
+
+ " Test for CTRL-W i
+ call cursor(3, 1)
+ wincmd i
+ call assert_equal([1, 3, 3], [line('.'), col('.'), winnr('$')])
+ close
+ 5wincmd i
+ call assert_equal([5, 3, 3], [line('.'), col('.'), winnr('$')])
+ close
+ call assert_fails('3wincmd i', 'E387:')
+ call assert_fails('6wincmd i', 'E389:')
+
+ " Test for :isplit
+ isplit foo
+ call assert_equal([1, 3, 3], [line('.'), col('.'), winnr('$')])
+ close
+ isplit 5 /foo/
+ call assert_equal([5, 3, 3], [line('.'), col('.'), winnr('$')])
+ close
+ call assert_fails('isplit 3 foo', 'E387:')
+ call assert_fails('isplit 6 foo', 'E389:')
+ call assert_fails('isplit bar', 'E389:')
+
+ close!
+endfunc
+
+" this was using a line from ml_get() freed by the regexp
+func Test_isearch_copy_line()
+ new
+ norm o
+ norm 0
+ 0norm o
+ sil! norm bc0
+ sil! isearch \%')
+ bwipe!
+endfunc
+
+" Test for :dsearch, :dlist, :djump and :dsplit commands
+" Test for [d, ]d, [D, ]D, [ CTRL-D, ] CTRL-D and CTRL-W d commands
+func Test_macro_search()
+ new
+ call setline(1, ['#define FOO 1', '#define FOO 2', '#define FOO 3',
+ \ '#define FOO 4', '#define FOO 5'])
+ call cursor(3, 9)
+
+ " Test for [d and ]d
+ call assert_equal('#define FOO 1', execute('normal [d'))
+ call assert_equal('#define FOO 2', execute('normal 2[d'))
+ call assert_fails('normal 3[d', 'E387:')
+ call assert_equal('#define FOO 4', execute('normal ]d'))
+ call assert_equal('#define FOO 5', execute('normal 2]d'))
+ call assert_fails('normal 3]d', 'E388:')
+
+ " Test for :dsearch
+ call assert_equal('#define FOO 1', execute('dsearch FOO'))
+ call assert_equal('#define FOO 5', execute('dsearch 5 /FOO/'))
+ call assert_fails('dsearch 3 FOO', 'E387:')
+ call assert_equal('#define FOO 4', execute('+1,$dsearch FOO'))
+ call assert_fails('1,.-1dsearch 3 FOO', 'E388:')
+ call assert_fails('dsearch BAR', 'E388:')
+
+ " Test for [D and ]D
+ call assert_equal([
+ \ ' 1: 1 #define FOO 1',
+ \ ' 2: 2 #define FOO 2',
+ \ ' 3: 3 #define FOO 3',
+ \ ' 4: 4 #define FOO 4',
+ \ ' 5: 5 #define FOO 5'], split(execute('normal [D'), "\n"))
+ call assert_equal([
+ \ ' 1: 4 #define FOO 4',
+ \ ' 2: 5 #define FOO 5'], split(execute('normal ]D'), "\n"))
+
+ " Test for :dlist
+ call assert_equal([
+ \ ' 1: 1 #define FOO 1',
+ \ ' 2: 2 #define FOO 2',
+ \ ' 3: 3 #define FOO 3',
+ \ ' 4: 4 #define FOO 4',
+ \ ' 5: 5 #define FOO 5'], split(execute('dlist FOO'), "\n"))
+ call assert_equal([
+ \ ' 1: 4 #define FOO 4',
+ \ ' 2: 5 #define FOO 5'], split(execute('+1,$dlist /FOO/'), "\n"))
+ call assert_fails('dlist BAR', 'E388:')
+
+ " Test for [ CTRL-D and ] CTRL-D
+ exe "normal [\<C-D>"
+ call assert_equal([1, 9], [line('.'), col('.')])
+ exe "normal 2j4[\<C-D>"
+ call assert_equal([4, 9], [line('.'), col('.')])
+ call assert_fails("normal k3[\<C-D>", 'E387:')
+ call assert_fails("normal 6[\<C-D>", 'E388:')
+ exe "normal ]\<C-D>"
+ call assert_equal([4, 9], [line('.'), col('.')])
+ exe "normal k2]\<C-D>"
+ call assert_equal([5, 9], [line('.'), col('.')])
+ call assert_fails("normal 2k3]\<C-D>", 'E388:')
+
+ " Test for :djump
+ call cursor(3, 9)
+ djump FOO
+ call assert_equal([1, 9], [line('.'), col('.')])
+ call cursor(3, 9)
+ djump 4 /FOO/
+ call assert_equal([4, 9], [line('.'), col('.')])
+ call cursor(3, 9)
+ call assert_fails('djump 3 FOO', 'E387:')
+ +,$djump 2 FOO
+ call assert_equal([5, 9], [line('.'), col('.')])
+ call assert_fails('djump BAR', 'E388:')
+
+ " Test for CTRL-W d
+ call cursor(3, 9)
+ wincmd d
+ call assert_equal([1, 9, 3], [line('.'), col('.'), winnr('$')])
+ close
+ 5wincmd d
+ call assert_equal([5, 9, 3], [line('.'), col('.'), winnr('$')])
+ close
+ call assert_fails('3wincmd d', 'E387:')
+ call assert_fails('6wincmd d', 'E388:')
+ new
+ call assert_fails("normal \<C-W>d", 'E349:')
+ call assert_fails("normal \<C-W>\<C-D>", 'E349:')
+ close
+
+ " Test for :dsplit
+ dsplit FOO
+ call assert_equal([1, 9, 3], [line('.'), col('.'), winnr('$')])
+ close
+ dsplit 5 /FOO/
+ call assert_equal([5, 9, 3], [line('.'), col('.'), winnr('$')])
+ close
+ call assert_fails('dsplit 3 FOO', 'E387:')
+ call assert_fails('dsplit 6 FOO', 'E388:')
+ call assert_fails('dsplit BAR', 'E388:')
+
+ close!
+endfunc
+
+func Test_define_search()
+ " this was accessing freed memory
+ new
+ call setline(1, ['first line', '', '#define something 0'])
+ sil norm o0
+ sil! norm 
+ bwipe!
+
+ new somefile
+ call setline(1, ['first line', '', '#define something 0'])
+ sil norm 0o0
+ sil! norm ]d
+ bwipe!
+endfunc
+
+" Test for [*, [/, ]* and ]/
+func Test_comment_search()
+ new
+ call setline(1, ['', '/*', ' *', ' *', ' */'])
+ normal! 4gg[/
+ call assert_equal([2, 1], [line('.'), col('.')])
+ normal! 3gg[*
+ call assert_equal([2, 1], [line('.'), col('.')])
+ normal! 3gg]/
+ call assert_equal([5, 3], [line('.'), col('.')])
+ normal! 3gg]*
+ call assert_equal([5, 3], [line('.'), col('.')])
+ %d
+ call setline(1, ['', '/*', ' *', ' *'])
+ call assert_beeps('normal! 3gg]/')
+ %d
+ call setline(1, ['', ' *', ' *', ' */'])
+ call assert_beeps('normal! 4gg[/')
+ %d
+ call setline(1, ' /* comment */')
+ normal! 15|[/
+ call assert_equal(9, col('.'))
+ normal! 15|]/
+ call assert_equal(21, col('.'))
+ call setline(1, ' comment */')
+ call assert_beeps('normal! 15|[/')
+ call setline(1, ' /* comment')
+ call assert_beeps('normal! 15|]/')
+ close!
+endfunc
+
" Test for the 'taglength' option
func Test_tag_length()
set tags=Xtags
diff --git a/src/nvim/testdir/test_taglist.vim b/src/nvim/testdir/test_taglist.vim
index e11815ff33..be46773256 100644
--- a/src/nvim/testdir/test_taglist.vim
+++ b/src/nvim/testdir/test_taglist.vim
@@ -1,5 +1,7 @@
" test taglist(), tagfiles() functions and :tags command
+source view_util.vim
+
func Test_taglist()
call writefile([
\ "FFoo\tXfoo\t1",
@@ -222,3 +224,21 @@ func Test_format_error()
set tags&
call delete('Xtags')
endfunc
+
+" Test for :tag command completion with 'wildoptions' set to 'tagfile'
+func Test_tag_complete_wildoptions()
+ call writefile(["foo\ta.c\t10;\"\tf", "bar\tb.c\t20;\"\td"], 'Xtags')
+ set tags=Xtags
+ set wildoptions=tagfile
+
+ call feedkeys(":tag \<C-D>\<C-R>=Screenline(&lines - 1)\<CR> : "
+ \ .. "\<C-R>=Screenline(&lines - 2)\<CR>\<C-B>\"\<CR>", 'xt')
+
+ call assert_equal('"tag bar d b.c : foo f a.c', @:)
+
+ call delete('Xtags')
+ set wildoptions&
+ set tags&
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_termcodes.vim b/src/nvim/testdir/test_termcodes.vim
new file mode 100644
index 0000000000..c0712aa892
--- /dev/null
+++ b/src/nvim/testdir/test_termcodes.vim
@@ -0,0 +1,60 @@
+
+" Test for terminal keycodes that doesn't have termcap entries
+func Test_special_term_keycodes()
+ new
+ " Test for <xHome>, <S-xHome> and <C-xHome>
+ " send <K_SPECIAL> <KS_EXTRA> keycode
+ call feedkeys("i\<C-K>\x80\xfd\x3f\n", 'xt')
+ " send <K_SPECIAL> <KS_MODIFIER> bitmap <K_SPECIAL> <KS_EXTRA> keycode
+ call feedkeys("i\<C-K>\x80\xfc\x2\x80\xfd\x3f\n", 'xt')
+ call feedkeys("i\<C-K>\x80\xfc\x4\x80\xfd\x3f\n", 'xt')
+ " Test for <xEnd>, <S-xEnd> and <C-xEnd>
+ call feedkeys("i\<C-K>\x80\xfd\x3d\n", 'xt')
+ call feedkeys("i\<C-K>\x80\xfc\x2\x80\xfd\x3d\n", 'xt')
+ call feedkeys("i\<C-K>\x80\xfc\x4\x80\xfd\x3d\n", 'xt')
+ " Test for <zHome>, <S-zHome> and <C-zHome>
+ call feedkeys("i\<C-K>\x80\xfd\x40\n", 'xt')
+ call feedkeys("i\<C-K>\x80\xfc\x2\x80\xfd\x40\n", 'xt')
+ call feedkeys("i\<C-K>\x80\xfc\x4\x80\xfd\x40\n", 'xt')
+ " Test for <zEnd>, <S-zEnd> and <C-zEnd>
+ call feedkeys("i\<C-K>\x80\xfd\x3e\n", 'xt')
+ call feedkeys("i\<C-K>\x80\xfc\x2\x80\xfd\x3e\n", 'xt')
+ call feedkeys("i\<C-K>\x80\xfc\x4\x80\xfd\x3e\n", 'xt')
+ " Test for <xUp>, <xDown>, <xLeft> and <xRight>
+ call feedkeys("i\<C-K>\x80\xfd\x41\n", 'xt')
+ call feedkeys("i\<C-K>\x80\xfd\x42\n", 'xt')
+ call feedkeys("i\<C-K>\x80\xfd\x43\n", 'xt')
+ call feedkeys("i\<C-K>\x80\xfd\x44\n", 'xt')
+ call assert_equal(['<Home>', '<S-Home>', '<C-Home>',
+ \ '<End>', '<S-End>', '<C-End>',
+ \ '<Home>', '<S-Home>', '<C-Home>',
+ \ '<End>', '<S-End>', '<C-End>',
+ \ '<Up>', '<Down>', '<Left>', '<Right>', ''], getline(1, '$'))
+ bw!
+endfunc
+
+func Test_simplify_ctrl_at()
+ " feeding unsimplified CTRL-@ should still trigger i_CTRL-@
+ call feedkeys("ifoo\<Esc>A\<*C-@>x", 'xt')
+ call assert_equal('foofo', getline(1))
+ bw!
+endfunc
+
+func Test_simplify_noremap()
+ call feedkeys("i\<*C-M>", 'nx')
+ call assert_equal('', getline(1))
+ call assert_equal([0, 2, 1, 0, 1], getcurpos())
+ bw!
+endfunc
+
+func Test_simplify_timedout()
+ inoremap <C-M>a b
+ call feedkeys("i\<*C-M>", 'xt')
+ call assert_equal('', getline(1))
+ call assert_equal([0, 2, 1, 0, 1], getcurpos())
+ iunmap <C-M>a
+ bw!
+endfunc
+
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_textformat.vim b/src/nvim/testdir/test_textformat.vim
index bf0706a0c2..970f5ae0d0 100644
--- a/src/nvim/testdir/test_textformat.vim
+++ b/src/nvim/testdir/test_textformat.vim
@@ -196,6 +196,184 @@ func Test_text_format()
enew!
endfunc
+func Test_format_c_comment()
+ new
+ setl ai cindent tw=40 et fo=croql
+ let text =<< trim END
+ var = 2345; // asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf
+ END
+ call setline(1, text)
+ normal gql
+ let expected =<< trim END
+ var = 2345; // asdf asdf asdf asdf asdf
+ // asdf asdf asdf asdf asdf
+ END
+ call assert_equal(expected, getline(1, '$'))
+
+ %del
+ let text =<< trim END
+ var = 2345; // asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf
+ END
+ call setline(1, text)
+ normal gql
+ let expected =<< trim END
+ var = 2345; // asdf asdf asdf asdf asdf
+ // asdf asdf asdf asdf asdf
+ // asdf asdf
+ END
+ call assert_equal(expected, getline(1, '$'))
+
+ %del
+ let text =<< trim END
+ #if 0 // This is another long end of
+ // line comment that
+ // wraps.
+ END
+ call setline(1, text)
+ normal gq2j
+ let expected =<< trim END
+ #if 0 // This is another long
+ // end of line comment
+ // that wraps.
+ END
+ call assert_equal(expected, getline(1, '$'))
+
+ " Using either "o" or "O" repeats a line comment occupying a whole line.
+ %del
+ let text =<< trim END
+ nop;
+ // This is a comment
+ val = val;
+ END
+ call setline(1, text)
+ normal 2Go
+ let expected =<< trim END
+ nop;
+ // This is a comment
+ //
+ val = val;
+ END
+ call assert_equal(expected, getline(1, '$'))
+ normal 2GO
+ let expected =<< trim END
+ nop;
+ //
+ // This is a comment
+ //
+ val = val;
+ END
+ call assert_equal(expected, getline(1, '$'))
+
+ " Using "o" repeats a line comment after a statement, "O" does not.
+ %del
+ let text =<< trim END
+ nop;
+ val = val; // This is a comment
+ END
+ call setline(1, text)
+ normal 2Go
+ let expected =<< trim END
+ nop;
+ val = val; // This is a comment
+ //
+ END
+ call assert_equal(expected, getline(1, '$'))
+ 3delete
+
+ " No comment repeated with a slash in 'formatoptions'
+ set fo+=/
+ normal 2Gox
+ let expected =<< trim END
+ nop;
+ val = val; // This is a comment
+ x
+ END
+ call assert_equal(expected, getline(1, '$'))
+
+ " Comment is formatted when it wraps
+ normal 2GA with some more text added
+ let expected =<< trim END
+ nop;
+ val = val; // This is a comment
+ // with some more text
+ // added
+ x
+ END
+ call assert_equal(expected, getline(1, '$'))
+
+ set fo-=/
+
+ " using 'indentexpr' instead of 'cindent' does not repeat a comment
+ setl nocindent indentexpr=2
+ %del
+ let text =<< trim END
+ nop;
+ val = val; // This is a comment
+ END
+ call setline(1, text)
+ normal 2Gox
+ let expected =<< trim END
+ nop;
+ val = val; // This is a comment
+ x
+ END
+ call assert_equal(expected, getline(1, '$'))
+ setl cindent indentexpr=
+ 3delete
+
+ normal 2GO
+ let expected =<< trim END
+ nop;
+
+ val = val; // This is a comment
+ END
+ call assert_equal(expected, getline(1, '$'))
+
+ " Using "o" does not repeat a comment in a string
+ %del
+ let text =<< trim END
+ nop;
+ val = " // This is not a comment";
+ END
+ call setline(1, text)
+ normal 2Gox
+ let expected =<< trim END
+ nop;
+ val = " // This is not a comment";
+ x
+ END
+ call assert_equal(expected, getline(1, '$'))
+
+ " Using CTRL-U after "o" fixes the indent
+ %del
+ let text =<< trim END
+ {
+ val = val; // This is a comment
+ END
+ call setline(1, text)
+ exe "normal! 2Go\<C-U>x\<Esc>"
+ let expected =<< trim END
+ {
+ val = val; // This is a comment
+ x
+ END
+ call assert_equal(expected, getline(1, '$'))
+
+ " typing comment text auto-wraps
+ %del
+ call setline(1, text)
+ exe "normal! 2GA blah more text blah.\<Esc>"
+ let expected =<< trim END
+ {
+ val = val; // This is a comment
+ // blah more text
+ // blah.
+ END
+ call assert_equal(expected, getline(1, '$'))
+
+ bwipe!
+endfunc
+
" Tests for :right, :center and :left on text with embedded TAB.
func Test_format_align()
enew!
@@ -433,6 +611,21 @@ func Test_format_align()
call assert_equal("\t\t Vim", getline(1))
q!
+ " align text with 'rightleft'
+ if has('rightleft')
+ new
+ call setline(1, 'Vim')
+ setlocal rightleft
+ left 20
+ setlocal norightleft
+ call assert_equal("\t\t Vim", getline(1))
+ setlocal rightleft
+ right
+ setlocal norightleft
+ call assert_equal("Vim", getline(1))
+ close!
+ endif
+
set tw&
endfunc
@@ -867,7 +1060,7 @@ func Test_tw_2_fo_tm_replace()
endfunc
" Test for 'matchpairs' with multibyte chars
-func Test_mps()
+func Test_mps_multibyte()
new
let t =<< trim END
{
@@ -891,6 +1084,30 @@ func Test_mps()
bwipe!
endfunc
+" Test for 'matchpairs' in latin1 encoding
+func Test_mps_latin1()
+ new
+ let save_enc = &encoding
+ " set encoding=latin1
+ call setline(1, 'abc(def)ghi')
+ normal %
+ call assert_equal(8, col('.'))
+ normal %
+ call assert_equal(4, col('.'))
+ call cursor(1, 6)
+ normal [(
+ call assert_equal(4, col('.'))
+ normal %
+ call assert_equal(8, col('.'))
+ call cursor(1, 6)
+ normal ])
+ call assert_equal(8, col('.'))
+ normal %
+ call assert_equal(4, col('.'))
+ let &encoding = save_enc
+ close!
+endfunc
+
func Test_empty_matchpairs()
split
set matchpairs= showmatch
@@ -944,8 +1161,103 @@ func Test_whichwrap_multi_byte()
bwipe!
endfunc
-func Test_substitute()
- call assert_equal('a1a2a3a', substitute('123', '\zs', 'a', 'g'))
+" Test for automatically adding comment leaders in insert mode
+func Test_threepiece_comment()
+ new
+ setlocal expandtab
+ call setline(1, ["\t/*"])
+ setlocal formatoptions=croql
+ call cursor(1, 3)
+ call feedkeys("A\<cr>\<cr>/", 'tnix')
+ call assert_equal(["\t/*", " *", " */"], getline(1, '$'))
+
+ " If a comment ends in a single line, then don't add it in the next line
+ %d
+ call setline(1, '/* line1 */')
+ call feedkeys("A\<CR>next line", 'xt')
+ call assert_equal(['/* line1 */', 'next line'], getline(1, '$'))
+
+ %d
+ " Copy the trailing indentation from the leader comment to a new line
+ setlocal autoindent noexpandtab
+ call feedkeys("a\t/*\tone\ntwo\n/", 'xt')
+ call assert_equal(["\t/*\tone", "\t *\ttwo", "\t */"], getline(1, '$'))
+ close!
+endfunc
+
+" Test for the 'f' flag in 'comments' (only the first line has the comment
+" string)
+func Test_firstline_comment()
+ new
+ setlocal comments=f:- fo+=ro
+ exe "normal i- B\nD\<C-C>ggoC\<C-C>ggOA\<C-C>"
+ call assert_equal(['A', '- B', ' C', ' D'], getline(1, '$'))
+ %d
+ setlocal comments=:-
+ exe "normal i- B\nD\<C-C>ggoC\<C-C>ggOA\<C-C>"
+ call assert_equal(['- A', '- B', '- C', '- D'], getline(1, '$'))
+ %bw!
+endfunc
+
+" Test for the 'r' flag in 'comments' (right align comment)
+func Test_comment_rightalign()
+ new
+ setlocal comments=sr:/***,m:**,ex-2:******/ fo+=ro
+ exe "normal i=\<C-C>o\t /***\nD\n/"
+ exe "normal 2GOA\<C-C>joB\<C-C>jOC\<C-C>joE\<C-C>GOF\<C-C>joG"
+ let expected =<< trim END
+ =
+ A
+ /***
+ ** B
+ ** C
+ ** D
+ ** E
+ ** F
+ ******/
+ G
+ END
+ call assert_equal(expected, getline(1, '$'))
+ %bw!
+endfunc
+
+" Test for the 'b' flag in 'comments'
+func Test_comment_blank()
+ new
+ setlocal comments=b:* fo+=ro
+ exe "normal i* E\nF\n\<BS>G\nH\<C-C>ggOC\<C-C>O\<BS>B\<C-C>OA\<C-C>2joD"
+ let expected =<< trim END
+ A
+ *B
+ * C
+ * D
+ * E
+ * F
+ *G
+ H
+ END
+ call assert_equal(expected, getline(1, '$'))
+ %bw!
+endfunc
+
+" Test for the 'n' flag in comments
+func Test_comment_nested()
+ new
+ setlocal comments=n:> fo+=ro
+ exe "normal i> B\nD\<C-C>ggOA\<C-C>joC\<C-C>Go\<BS>>>> F\nH"
+ exe "normal 5GOE\<C-C>6GoG"
+ let expected =<< trim END
+ > A
+ > B
+ > C
+ > D
+ >>>> E
+ >>>> F
+ >>>> G
+ >>>> H
+ END
+ call assert_equal(expected, getline(1, '$'))
+ %bw!
endfunc
" Test for 'a' and 'w' flags in 'formatoptions'
diff --git a/src/nvim/testdir/test_textobjects.vim b/src/nvim/testdir/test_textobjects.vim
index 2b6bb8b302..eeb2946a8b 100644
--- a/src/nvim/testdir/test_textobjects.vim
+++ b/src/nvim/testdir/test_textobjects.vim
@@ -1,8 +1,7 @@
" Test for textobjects
-if !has('textobjects')
- finish
-endif
+source check.vim
+CheckFeature textobjects
func CpoM(line, useM, expected)
new
@@ -42,6 +41,24 @@ func Test_inner_block_with_cpo_M_right_backslash()
call CpoM('(red (blue\) green)', 1, ['red (blue\) green', 'blue\', 'red (blue\) green'])
endfunc
+func Test_inner_block_single_char()
+ new
+ call setline(1, "(a)")
+
+ set selection=inclusive
+ let @" = ''
+ call assert_nobeep('norm! 0faviby')
+ call assert_equal('a', @")
+
+ set selection=exclusive
+ let @" = ''
+ call assert_nobeep('norm! 0faviby')
+ call assert_equal('a', @")
+
+ set selection&
+ bwipe!
+endfunc
+
func Test_quote_selection_selection_exclusive()
new
call setline(1, "a 'bcde' f")
@@ -50,11 +67,11 @@ func Test_quote_selection_selection_exclusive()
exe "norm! fdvhi'y"
call assert_equal('bcde', @")
- let @"='dummy'
+ let @" = 'dummy'
exe "norm! $gevi'y"
call assert_equal('bcde', @")
- let @"='dummy'
+ let @" = 'dummy'
exe "norm! 0fbhvi'y"
call assert_equal('bcde', @")
@@ -188,10 +205,18 @@ func Test_string_html_objects()
call assert_equal("<div><div\nattr=\"attr\"\n></div></div>", @", e)
set quoteescape&
+
+ " this was going beyond the end of the line
+ %del
+ sil! norm i"\
+ sil! norm i"\
+ sil! norm i"\
+ call assert_equal('"\', getline(1))
+
+ bwipe!
endfor
set enc=utf-8
- bwipe!
endfunc
func Test_empty_html_tag()
@@ -208,6 +233,10 @@ func Test_empty_html_tag()
normal 0f<vitsaaa
call assert_equal('aaa', getline(1))
+ " selecting a tag block in an non-empty blank line should fail
+ call setline(1, ' ')
+ call assert_beeps('normal $vaty')
+
bwipe!
endfunc
@@ -342,6 +371,168 @@ func Test_sentence_with_cursor_on_delimiter()
%delete _
endfunc
+" Test for the paragraph (ap) text object
+func Test_paragraph()
+ new
+ call setline(1, ['First line.', 'Second line.', 'Third line.'])
+ call cursor(2, 1)
+ normal vapy
+ call assert_equal("First line.\nSecond line.\nThird line.\n", @")
+
+ call cursor(2, 1)
+ call assert_beeps('normal vapapy')
+
+ call setline(1, ['First line.', 'Second line.', ' ', ''])
+ call cursor(1, 1)
+ normal vapy
+ call assert_equal("First line.\nSecond line.\n \n\n", @")
+
+ call setline(1, ['', '', '', 'First line.', 'Second line.'])
+ call cursor(2, 1)
+ normal yap
+ call assert_equal("\n\n\nFirst line.\nSecond line.\n", @")
+ call assert_beeps('normal 3yap')
+ exe "normal \<C-C>"
+
+ %d
+ call setline(1, [' ', ' ', ' '])
+ call cursor(2, 1)
+ normal Vipy
+ call assert_equal(" \n \n \n", @")
+ call cursor(2, 1)
+ call assert_beeps("normal Vipip")
+ exe "normal \<C-C>"
+
+ close!
+endfunc
+
+" Tests for text object aw
+func Test_textobj_a_word()
+ new
+ call append(0, ['foobar,eins,foobar', 'foo,zwei,foo '])
+ " diw
+ norm! 1gg0diw
+ call assert_equal([',eins,foobar', 'foo,zwei,foo ', ''], getline(1,'$'))
+ " daw
+ norm! 2ggEdaw
+ call assert_equal([',eins,foobar', 'foo,zwei,', ''], getline(1, '$'))
+ " daw the last word in a line
+ call setline(1, ['foo bar', 'foo bar', ''])
+ call cursor(1, 5)
+ normal daw
+ call assert_equal('foo', getline(1))
+ " aw in visual mode
+ call cursor(2, 5)
+ normal! vawx
+ call assert_equal('foo', getline(2))
+ %d
+ call append(0, ["foo\teins\tfoobar", "foo\tzwei\tfoo "])
+ " diW
+ norm! 2ggwd2iW
+ call assert_equal(['foo eins foobar', 'foo foo ', ''], getline(1,'$'))
+ " daW
+ norm! 1ggd2aW
+ call assert_equal(['foobar', 'foo foo ', ''], getline(1,'$'))
+
+ %d
+ call append(0, ["foo\teins\tfoobar", "foo\tzwei\tfoo "])
+ " aw in visual line mode switches to characterwise mode
+ norm! 2gg$Vawd
+ call assert_equal(['foo eins foobar', 'foo zwei foo'], getline(1,'$'))
+ norm! 1gg$Viwd
+ call assert_equal(['foo eins ', 'foo zwei foo'], getline(1,'$'))
+
+ " visually selecting a tab before a word with 'selection' set to 'exclusive'
+ set selection=exclusive
+ normal gg3lvlawy
+ call assert_equal("\teins", @")
+ " visually selecting a tab before a word with 'selection' set to 'inclusive'
+ set selection=inclusive
+ normal gg3lvlawy
+ call assert_equal("\teins\t", @")
+ set selection&
+
+ " selecting a word with no non-space characters in a buffer fails
+ %d
+ call setline(1, ' ')
+ call assert_beeps('normal 3lyaw')
+
+ " visually selecting words backwards with no more words to select
+ call setline(1, 'one two')
+ call assert_beeps('normal 2lvh2aw')
+ exe "normal \<C-C>"
+ call assert_beeps('normal $vh3aw')
+ exe "normal \<C-C>"
+ call setline(1, ['', 'one two'])
+ call assert_beeps('normal 2G2lvh3aw')
+ exe "normal \<C-C>"
+
+ " selecting words forward with no more words to select
+ %d
+ call setline(1, 'one a')
+ call assert_beeps('normal 0y3aw')
+ call setline(1, 'one two ')
+ call assert_beeps('normal 0y3aw')
+ call assert_beeps('normal 03ly2aw')
+
+ " clean up
+ bw!
+endfunc
+
+" Test for is and as text objects
+func Test_textobj_sentence()
+ new
+ call append(0, ['This is a test. With some sentences!', '',
+ \ 'Even with a question? And one more. And no sentence here'])
+ " Test for dis - does not remove trailing whitespace
+ norm! 1gg0dis
+ call assert_equal([' With some sentences!', '',
+ \ 'Even with a question? And one more. And no sentence here', ''],
+ \ getline(1,'$'))
+ " Test for das - removes leading whitespace
+ norm! 3ggf?ldas
+ call assert_equal([' With some sentences!', '',
+ \ 'Even with a question? And no sentence here', ''], getline(1,'$'))
+ " when used in visual mode, is made characterwise
+ norm! 3gg$Visy
+ call assert_equal('v', visualmode())
+ " reset visualmode()
+ norm! 3ggVy
+ norm! 3gg$Vasy
+ call assert_equal('v', visualmode())
+ " basic testing for textobjects a< and at
+ %d
+ call setline(1, ['<div> ','<a href="foobar" class="foo">xyz</a>',' </div>', ' '])
+ " a<
+ norm! 1gg0da<
+ call assert_equal([' ', '<a href="foobar" class="foo">xyz</a>', ' </div>', ' '], getline(1,'$'))
+ norm! 1pj
+ call assert_equal([' <div>', '<a href="foobar" class="foo">xyz</a>', ' </div>', ' '], getline(1,'$'))
+ " at
+ norm! d2at
+ call assert_equal([' '], getline(1,'$'))
+ %d
+ call setline(1, ['<div> ','<a href="foobar" class="foo">xyz</a>',' </div>', ' '])
+ " i<
+ norm! 1gg0di<
+ call assert_equal(['<> ', '<a href="foobar" class="foo">xyz</a>', ' </div>', ' '], getline(1,'$'))
+ norm! 1Pj
+ call assert_equal(['<div> ', '<a href="foobar" class="foo">xyz</a>', ' </div>', ' '], getline(1,'$'))
+ norm! d2it
+ call assert_equal(['<div></div>',' '], getline(1,'$'))
+ " basic testing for a[ and i[ text object
+ %d
+ call setline(1, [' ', '[', 'one [two]', 'thre', ']'])
+ norm! 3gg0di[
+ call assert_equal([' ', '[', ']'], getline(1,'$'))
+ call setline(1, [' ', '[', 'one [two]', 'thre', ']'])
+ norm! 3gg0ftd2a[
+ call assert_equal([' '], getline(1,'$'))
+
+ " clean up
+ bw!
+endfunc
+
" Test for quote (', " and `) textobjects
func Test_textobj_quote()
new
diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim
index aae315b2c5..56a5ec96af 100644
--- a/src/nvim/testdir/test_timers.vim
+++ b/src/nvim/testdir/test_timers.vim
@@ -1,13 +1,16 @@
" Test for timers
-if !has('timers')
- finish
-endif
+source check.vim
+CheckFeature timers
source shared.vim
source term_util.vim
source load.vim
+func SetUp()
+ call timer_stopall()
+endfunc
+
func MyHandler(timer)
let g:val += 1
endfunc
@@ -16,7 +19,7 @@ func MyHandlerWithLists(lists, timer)
let x = string(a:lists)
endfunc
-func Test_oneshot()
+func Test_timer_oneshot()
let g:val = 0
let timer = timer_start(50, 'MyHandler')
let slept = WaitFor('g:val == 1')
@@ -28,7 +31,7 @@ func Test_oneshot()
endif
endfunc
-func Test_repeat_three()
+func Test_timer_repeat_three()
let g:val = 0
let timer = timer_start(50, 'MyHandler', {'repeat': 3})
let slept = WaitFor('g:val == 3')
@@ -40,8 +43,7 @@ func Test_repeat_three()
endif
endfunc
-func Test_repeat_many()
- call timer_stopall()
+func Test_timer_repeat_many()
let g:val = 0
let timer = timer_start(50, 'MyHandler', {'repeat': -1})
if has('mac')
@@ -52,7 +54,7 @@ func Test_repeat_many()
call assert_inrange((has('mac') ? 1 : 2), LoadAdjust(5), g:val)
endfunc
-func Test_with_partial_callback()
+func Test_timer_with_partial_callback()
let g:val = 0
let meow = {'one': 1}
function meow.bite(...)
@@ -69,13 +71,13 @@ func Test_with_partial_callback()
endif
endfunc
-func Test_retain_partial()
+func Test_timer_retain_partial()
call timer_start(50, function('MyHandlerWithLists', [['a']]))
- call garbagecollect()
+ call test_garbagecollect_now()
sleep 100m
endfunc
-func Test_info()
+func Test_timer_info()
let id = timer_start(1000, 'MyHandler')
let info = id->timer_info()
call assert_equal(id, info[0]['id'])
@@ -92,10 +94,11 @@ func Test_info()
call timer_stop(id)
call assert_equal([], timer_info(id))
+
+ call assert_fails('call timer_info("abc")', 'E39:')
endfunc
-func Test_stopall()
- call timer_stopall()
+func Test_timer_stopall()
let id1 = timer_start(1000, 'MyHandler')
let id2 = timer_start(2000, 'MyHandler')
let info = timer_info()
@@ -106,7 +109,7 @@ func Test_stopall()
call assert_equal(0, len(info))
endfunc
-func Test_paused()
+func Test_timer_paused()
let g:val = 0
let id = timer_start(50, 'MyHandler')
@@ -130,6 +133,8 @@ func Test_paused()
else
call assert_inrange(0, 10, slept)
endif
+
+ call assert_fails('call timer_pause("abc", 1)', 'E39:')
endfunc
func StopMyself(timer)
@@ -139,7 +144,7 @@ func StopMyself(timer)
endif
endfunc
-func Test_delete_myself()
+func Test_timer_delete_myself()
let g:called = 0
let t = timer_start(10, 'StopMyself', {'repeat': -1})
call WaitForAssert({-> assert_equal(2, g:called)})
@@ -151,33 +156,45 @@ func StopTimer1(timer)
let g:timer2 = 10->timer_start('StopTimer2')
" avoid maxfuncdepth error
call timer_pause(g:timer1, 1)
- sleep 40m
+ sleep 20m
endfunc
func StopTimer2(timer)
call timer_stop(g:timer1)
endfunc
-func Test_stop_in_callback()
+func Test_timer_stop_in_callback()
+ call assert_equal(0, len(timer_info()))
let g:timer1 = timer_start(10, 'StopTimer1')
- sleep 40m
+ let slept = 0
+ for i in range(10)
+ if len(timer_info()) == 0
+ break
+ endif
+ sleep 10m
+ let slept += 10
+ endfor
+ " This should take only 30 msec, but on Mac it's often longer
+ call assert_inrange(0, 50, slept)
endfunc
func StopTimerAll(timer)
call timer_stopall()
endfunc
-func Test_stop_all_in_callback()
- call timer_stopall()
- let g:timer1 = timer_start(10, 'StopTimerAll')
- let info = timer_info()
- call assert_equal(1, len(info))
- if has('mac')
- sleep 100m
- endif
- sleep 40m
- let info = timer_info()
- call assert_equal(0, len(info))
+func Test_timer_stop_all_in_callback()
+ call assert_equal(0, len(timer_info()))
+ call timer_start(10, 'StopTimerAll')
+ call assert_equal(1, len(timer_info()))
+ let slept = 0
+ for i in range(10)
+ if len(timer_info()) == 0
+ break
+ endif
+ sleep 10m
+ let slept += 10
+ endfor
+ call assert_inrange(0, 30, slept)
endfunc
func FeedkeysCb(timer)
@@ -190,7 +207,7 @@ func InputCb(timer)
call Resume()
endfunc
-func Test_input_in_timer()
+func Test_timer_input_in_timer()
let g:val = ''
call timer_start(10, 'InputCb')
call Standby(1000)
@@ -212,6 +229,10 @@ func Test_timer_errors()
call WaitForAssert({-> assert_equal(3, g:call_count)})
sleep 50m
call assert_equal(3, g:call_count)
+
+ call assert_fails('call timer_start(100, "MyHandler", "abc")', 'E475:')
+ call assert_fails('call timer_start(100, [])', 'E921:')
+ call assert_fails('call timer_stop("abc")', 'E39:')
endfunc
func FuncWithCaughtError(timer)
@@ -243,7 +264,7 @@ func Interrupt(timer)
call nvim_input("\<C-C>")
endfunc
-func Test_peek_and_get_char()
+func Test_timer_peek_and_get_char()
if !has('unix') && !has('gui_running')
return
endif
@@ -254,7 +275,7 @@ func Test_peek_and_get_char()
eval intr->timer_stop()
endfunc
-func Test_getchar_zero()
+func Test_timer_getchar_zero()
if has('win32') && !has('gui_running')
" Console: no low-level input
return
@@ -272,20 +293,20 @@ func Test_getchar_zero()
call timer_stop(id)
endfunc
-func Test_ex_mode()
+func Test_timer_ex_mode()
" Function with an empty line.
func Foo(...)
endfunc
let timer = timer_start(40, function('g:Foo'), {'repeat':-1})
" This used to throw error E749.
- exe "normal gQsleep 100m\rvi\r"
+ exe "normal Qsleep 100m\rvi\r"
call timer_stop(timer)
endfunc
-func Test_restore_count()
+func Test_timer_restore_count()
if !CanRunVimInTerminal()
- return
+ throw 'Skipped: cannot run Vim in a terminal window'
endif
" Check that v:count is saved and restored, not changed by a timer.
call writefile([
@@ -316,9 +337,9 @@ endfunc
" Test that the garbage collector isn't triggered if a timer callback invokes
" vgetc().
-func Test_nocatch_garbage_collect()
- CheckFunction test_garbagecollect_soon
- CheckFunction test_override
+func Test_timer_nocatch_garbage_collect()
+ " skipped: Nvim does not support test_garbagecollect_soon(), test_override()
+ return
" 'uptimetime. must be bigger than the timer timeout
set ut=200
call test_garbagecollect_soon()
@@ -340,7 +361,7 @@ func Test_nocatch_garbage_collect()
delfunc FeedChar
endfunc
-func Test_error_in_timer_callback()
+func Test_timer_error_in_timer_callback()
if !has('terminal') || (has('win32') && has('gui_running'))
throw 'Skipped: cannot run Vim in a terminal window'
endif
@@ -375,8 +396,40 @@ func Test_error_in_timer_callback()
exe buf .. 'bwipe!'
endfunc
+" Test for garbage collection when a timer is still running
+func Test_timer_garbage_collect()
+ let timer = timer_start(1000, function('MyHandler'), {'repeat' : 10})
+ call test_garbagecollect_now()
+ let l = timer_info(timer)
+ call assert_equal(function('MyHandler'), l[0].callback)
+ call timer_stop(timer)
+endfunc
+
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_trycatch.vim b/src/nvim/testdir/test_trycatch.vim
index 7e513180a3..598402fafe 100644
--- a/src/nvim/testdir/test_trycatch.vim
+++ b/src/nvim/testdir/test_trycatch.vim
@@ -1972,6 +1972,162 @@ func Test_builtin_func_error()
call assert_equal('jlmnpqrtueghivyzACD', g:Xpath)
endfunc
-" Modelines {{{1
+func Test_reload_in_try_catch()
+ call writefile(['x'], 'Xreload')
+ set autoread
+ edit Xreload
+ tabnew
+ call writefile(['xx'], 'Xreload')
+ augroup ReLoad
+ au FileReadPost Xreload let x = doesnotexist
+ au BufReadPost Xreload let x = doesnotexist
+ augroup END
+ try
+ edit Xreload
+ catch
+ endtry
+ tabnew
+
+ tabclose
+ tabclose
+ autocmd! ReLoad
+ set noautoread
+ bwipe! Xreload
+ call delete('Xreload')
+endfunc
+
+" Test for errors with :catch, :throw, :finally {{{1
+func Test_try_catch_errors()
+ call assert_fails('throw |', 'E471:')
+ call assert_fails("throw \n ", 'E471:')
+ call assert_fails('catch abc', 'E603:')
+ call assert_fails('try | let i = 1| finally | catch | endtry', 'E604:')
+ call assert_fails('finally', 'E606:')
+ call assert_fails('try | finally | finally | endtry', 'E607:')
+ " v8.2.3486 has been ported, but v8.2.1183 hasn't, so E170 appears here.
+ " call assert_fails('try | for i in range(5) | endif | endtry', 'E580:')
+ call assert_fails('try | for i in range(5) | endif | endtry', 'E170:')
+ call assert_fails('try | while v:true | endtry', 'E170:')
+ call assert_fails('try | if v:true | endtry', 'E171:')
+endfunc
+
+" Test for verbose messages with :try :catch, and :finally {{{1
+func Test_try_catch_verbose()
+ " This test works only when the language is English
+ if v:lang != "C" && v:lang !~ '^[Ee]n'
+ return
+ endif
+
+ set verbose=14
+ redir => msg
+ try
+ echo i
+ catch /E121:/
+ finally
+ endtry
+ redir END
+ let expected = [
+ \ 'Exception thrown: Vim(echo):E121: Undefined variable: i',
+ \ '',
+ \ 'Exception caught: Vim(echo):E121: Undefined variable: i',
+ \ '',
+ \ 'Exception finished: Vim(echo):E121: Undefined variable: i'
+ \ ]
+ call assert_equal(expected, split(msg, "\n"))
+ set verbose&
+endfunc
+
+" Test for using throw in a called function with following error {{{1
+func Test_user_command_throw_in_function_call()
+ let lines =<< trim END
+ function s:get_dict() abort
+ throw 'my_error'
+ endfunction
+
+ try
+ call s:get_dict().foo()
+ catch /my_error/
+ let caught = 'yes'
+ catch
+ let caught = v:exception
+ endtry
+ call assert_equal('yes', caught)
+ END
+ call writefile(lines, 'XtestThrow')
+ source XtestThrow
+
+ call delete('XtestThrow')
+ unlet g:caught
+endfunc
+
+" Test for using throw in a called function with following endtry {{{1
+func Test_user_command_function_call_with_endtry()
+ let lines =<< trim END
+ funct s:throw(msg) abort
+ throw a:msg
+ endfunc
+ func s:main() abort
+ try
+ try
+ throw 'err1'
+ catch
+ call s:throw('err2') | endtry
+ catch
+ let s:caught = 'yes'
+ endtry
+ endfunc
+
+ call s:main()
+ call assert_equal('yes', s:caught)
+ END
+ call writefile(lines, 'XtestThrow')
+ source XtestThrow
+
+ call delete('XtestThrow')
+endfunc
+
+func ThisWillFail()
+
+endfunc
+
+" This was crashing prior to the fix in 8.2.3478.
+func Test_error_in_catch_and_finally()
+ let lines =<< trim END
+ try
+ echo x
+ catch
+ for l in []
+ finally
+ END
+ call writefile(lines, 'XtestCatchAndFinally')
+ try
+ source XtestCatchAndFinally
+ catch /E600:/
+ endtry
+
+ call delete('XtestCatchAndFinally')
+endfunc
+
+" This was causing an illegal memory access
+func Test_leave_block_in_endtry_not_called()
+ let lines =<< trim END
+ " vim9script
+ " try #
+ try "
+ for x in []
+ if
+ endwhile
+ if
+ endtry
+ END
+ call writefile(lines, 'XtestEndtry')
+ try
+ source XtestEndtry
+ catch /E171:/
+ endtry
+
+ call delete('XtestEndtry')
+endfunc
+
+" Modeline {{{1
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
-"-------------------------------------------------------------------------------
diff --git a/src/nvim/testdir/test_undo.vim b/src/nvim/testdir/test_undo.vim
index 30e00e7ad4..a9ec405aa4 100644
--- a/src/nvim/testdir/test_undo.vim
+++ b/src/nvim/testdir/test_undo.vim
@@ -3,8 +3,6 @@
" undo-able pieces. Do that by setting 'undolevels'.
" Also tests :earlier and :later.
-source check.vim
-
func Test_undotree()
new
@@ -137,8 +135,7 @@ func BackOne(expected)
endfunc
func Test_undo_del_chars()
- CheckFunction test_settime
-
+ throw 'Skipped: Nvim does not support test_settime()'
" Setup a buffer without creating undo entries
new
set ul=-1
@@ -299,6 +296,8 @@ func Test_undo_write()
close!
call delete('Xtest')
bwipe! Xtest
+
+ call assert_fails('earlier xyz', 'E475:')
endfunc
func Test_insert_expr()
@@ -332,8 +331,9 @@ func Test_insert_expr()
endfunc
func Test_undofile_earlier()
- CheckFunction test_settime
-
+ throw 'Skipped: Nvim does not support test_settime()'
+ " Issue #1254
+ " create undofile with timestamps older than Vim startup time.
let t0 = localtime() - 43200
call test_settime(t0)
new Xfile
@@ -366,7 +366,7 @@ func Test_wundo_errors()
bwipe!
endfunc
-" Check that reading a truncted undo file doesn't hang.
+" Check that reading a truncated undo file doesn't hang.
func Test_undofile_truncated()
new
call setline(1, 'hello')
@@ -429,6 +429,59 @@ func Test_cmd_in_reg_undo()
let @a = ''
endfunc
+" This used to cause an illegal memory access
+func Test_undo_append()
+ new
+ call feedkeys("axx\<Esc>v", 'xt')
+ undo
+ norm o
+ quit
+endfunc
+
+func Test_undo_0()
+ new
+ set ul=100
+ normal i1
+ undo
+ normal i2
+ undo
+ normal i3
+
+ undo 0
+ let d = undotree()
+ call assert_equal('', getline(1))
+ call assert_equal(0, d.seq_cur)
+
+ redo
+ let d = undotree()
+ call assert_equal('3', getline(1))
+ call assert_equal(3, d.seq_cur)
+
+ undo 2
+ undo 0
+ let d = undotree()
+ call assert_equal('', getline(1))
+ call assert_equal(0, d.seq_cur)
+
+ redo
+ let d = undotree()
+ call assert_equal('2', getline(1))
+ call assert_equal(2, d.seq_cur)
+
+ undo 1
+ undo 0
+ let d = undotree()
+ call assert_equal('', getline(1))
+ call assert_equal(0, d.seq_cur)
+
+ redo
+ let d = undotree()
+ call assert_equal('1', getline(1))
+ call assert_equal(1, d.seq_cur)
+
+ bwipe!
+endfunc
+
" undo or redo are noop if there is nothing to undo or redo
func Test_undo_redo_noop()
new
@@ -454,15 +507,6 @@ func Test_redo_empty_line()
bwipe!
endfunc
-" This used to cause an illegal memory access
-func Test_undo_append()
- new
- call feedkeys("axx\<Esc>v", 'xt')
- undo
- norm o
- quit
-endfunc
-
funct Test_undofile()
" Test undofile() without setting 'undodir'.
if has('persistent_undo')
@@ -504,50 +548,6 @@ funct Test_undofile()
set undodir&
endfunc
-func Test_undo_0()
- new
- set ul=100
- normal i1
- undo
- normal i2
- undo
- normal i3
-
- undo 0
- let d = undotree()
- call assert_equal('', getline(1))
- call assert_equal(0, d.seq_cur)
-
- redo
- let d = undotree()
- call assert_equal('3', getline(1))
- call assert_equal(3, d.seq_cur)
-
- undo 2
- undo 0
- let d = undotree()
- call assert_equal('', getline(1))
- call assert_equal(0, d.seq_cur)
-
- redo
- let d = undotree()
- call assert_equal('2', getline(1))
- call assert_equal(2, d.seq_cur)
-
- undo 1
- undo 0
- let d = undotree()
- call assert_equal('', getline(1))
- call assert_equal(0, d.seq_cur)
-
- redo
- let d = undotree()
- call assert_equal('1', getline(1))
- call assert_equal(1, d.seq_cur)
-
- bwipe!
-endfunc
-
" Tests for the undo file
" Explicitly break changes up in undo-able pieces by setting 'undolevels'.
func Test_undofile_2()
@@ -579,7 +579,7 @@ func Test_undofile_2()
" add 10 lines, delete 6 lines, undo 3
set undofile
- call setbufline(0, 1, ['one', 'two', 'three', 'four', 'five', 'six',
+ call setbufline('%', 1, ['one', 'two', 'three', 'four', 'five', 'six',
\ 'seven', 'eight', 'nine', 'ten'])
set undolevels=100
normal 3Gdd
@@ -733,4 +733,44 @@ func Test_undofile_cryptmethod_blowfish2()
set undofile& undolevels& cryptmethod&
endfunc
+" Test for redoing with incrementing numbered registers
+func Test_redo_repeat_numbered_register()
+ new
+ for [i, v] in [[1, 'one'], [2, 'two'], [3, 'three'],
+ \ [4, 'four'], [5, 'five'], [6, 'six'],
+ \ [7, 'seven'], [8, 'eight'], [9, 'nine']]
+ exe 'let @' .. i .. '="' .. v .. '\n"'
+ endfor
+ call feedkeys('"1p.........', 'xt')
+ call assert_equal(['', 'one', 'two', 'three', 'four', 'five', 'six',
+ \ 'seven', 'eight', 'nine', 'nine'], getline(1, '$'))
+ bwipe!
+endfunc
+
+" Test for redo in insert mode using CTRL-O with multibyte characters
+func Test_redo_multibyte_in_insert_mode()
+ new
+ call feedkeys("a\<C-K>ft", 'xt')
+ call feedkeys("uiHe\<C-O>.llo", 'xt')
+ call assert_equal("He\ufb05llo", getline(1))
+ bwipe!
+endfunc
+
+func Test_undo_mark()
+ new
+ " The undo is applied to the only line.
+ call setline(1, 'hello')
+ call feedkeys("ggyiw$p", 'xt')
+ undo
+ call assert_equal([0, 1, 1, 0], getpos("'["))
+ call assert_equal([0, 1, 1, 0], getpos("']"))
+ " The undo removes the last line.
+ call feedkeys("Goaaaa\<Esc>", 'xt')
+ call feedkeys("obbbb\<Esc>", 'xt')
+ undo
+ call assert_equal([0, 2, 1, 0], getpos("'["))
+ call assert_equal([0, 2, 1, 0], getpos("']"))
+ bwipe!
+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..e37fe43b22 100644
--- a/src/nvim/testdir/test_usercommands.vim
+++ b/src/nvim/testdir/test_usercommands.vim
@@ -4,63 +4,112 @@
function Test_cmdmods()
let g:mods = ''
- command! -nargs=* MyCmd let g:mods .= '<mods> '
+ command! -nargs=* MyCmd let g:mods = '<mods>'
MyCmd
+ call assert_equal('', g:mods)
aboveleft MyCmd
+ call assert_equal('aboveleft', g:mods)
abo MyCmd
+ call assert_equal('aboveleft', g:mods)
belowright MyCmd
+ call assert_equal('belowright', g:mods)
bel MyCmd
+ call assert_equal('belowright', g:mods)
botright MyCmd
+ call assert_equal('botright', g:mods)
bo MyCmd
+ call assert_equal('botright', g:mods)
browse MyCmd
+ call assert_equal('browse', g:mods)
bro MyCmd
+ call assert_equal('browse', g:mods)
confirm MyCmd
+ call assert_equal('confirm', g:mods)
conf MyCmd
+ call assert_equal('confirm', g:mods)
hide MyCmd
+ call assert_equal('hide', g:mods)
hid MyCmd
+ call assert_equal('hide', g:mods)
keepalt MyCmd
+ call assert_equal('keepalt', g:mods)
keepa MyCmd
+ call assert_equal('keepalt', g:mods)
keepjumps MyCmd
+ call assert_equal('keepjumps', g:mods)
keepj MyCmd
+ call assert_equal('keepjumps', g:mods)
keepmarks MyCmd
+ call assert_equal('keepmarks', g:mods)
kee MyCmd
+ call assert_equal('keepmarks', g:mods)
keeppatterns MyCmd
+ call assert_equal('keeppatterns', g:mods)
keepp MyCmd
+ call assert_equal('keeppatterns', g:mods)
leftabove MyCmd " results in :aboveleft
+ call assert_equal('aboveleft', g:mods)
lefta MyCmd
+ call assert_equal('aboveleft', g:mods)
lockmarks MyCmd
+ call assert_equal('lockmarks', g:mods)
loc MyCmd
- " noautocmd MyCmd
+ call assert_equal('lockmarks', g:mods)
+ noautocmd MyCmd
+ call assert_equal('noautocmd', g:mods)
+ noa MyCmd
+ call assert_equal('noautocmd', g:mods)
noswapfile MyCmd
+ call assert_equal('noswapfile', g:mods)
nos MyCmd
+ call assert_equal('noswapfile', g:mods)
rightbelow MyCmd " results in :belowright
+ call assert_equal('belowright', g:mods)
rightb MyCmd
+ call assert_equal('belowright', g:mods)
" sandbox MyCmd
silent MyCmd
+ call assert_equal('silent', g:mods)
sil MyCmd
+ call assert_equal('silent', g:mods)
+ silent! MyCmd
+ call assert_equal('silent!', g:mods)
+ sil! MyCmd
+ call assert_equal('silent!', g:mods)
tab MyCmd
+ call assert_equal('tab', g:mods)
topleft MyCmd
+ call assert_equal('topleft', g:mods)
to MyCmd
- " unsilent MyCmd
+ call assert_equal('topleft', g:mods)
+ unsilent MyCmd
+ call assert_equal('unsilent', g:mods)
+ uns MyCmd
+ call assert_equal('unsilent', g:mods)
verbose MyCmd
+ call assert_equal('verbose', g:mods)
verb MyCmd
+ call assert_equal('verbose', g:mods)
+ 0verbose MyCmd
+ call assert_equal('0verbose', g:mods)
+ 3verbose MyCmd
+ call assert_equal('3verbose', g:mods)
+ 999verbose MyCmd
+ call assert_equal('999verbose', g:mods)
vertical MyCmd
+ call assert_equal('vertical', g:mods)
vert MyCmd
+ call assert_equal('vertical', g:mods)
aboveleft belowright botright browse confirm hide keepalt keepjumps
- \ keepmarks keeppatterns lockmarks noswapfile silent tab
- \ topleft verbose vertical MyCmd
-
- call assert_equal(' aboveleft aboveleft belowright belowright botright ' .
- \ 'botright browse browse confirm confirm hide hide ' .
- \ 'keepalt keepalt keepjumps keepjumps keepmarks keepmarks ' .
- \ 'keeppatterns keeppatterns aboveleft aboveleft lockmarks lockmarks noswapfile ' .
- \ 'noswapfile belowright belowright silent silent tab topleft topleft verbose verbose ' .
- \ 'vertical vertical ' .
- \ 'aboveleft belowright botright browse confirm hide keepalt keepjumps ' .
- \ 'keepmarks keeppatterns lockmarks noswapfile silent tab topleft ' .
- \ 'verbose vertical ', g:mods)
+ \ keepmarks keeppatterns lockmarks noautocmd noswapfile silent
+ \ tab topleft unsilent verbose vertical MyCmd
+
+ call assert_equal('browse confirm hide keepalt keepjumps ' .
+ \ 'keepmarks keeppatterns lockmarks noswapfile unsilent noautocmd ' .
+ \ 'silent verbose aboveleft belowright botright tab topleft vertical',
+ \ g:mods)
let g:mods = ''
command! -nargs=* MyQCmd let g:mods .= '<q-mods> '
@@ -264,15 +313,15 @@ func CustomComplete(A, L, P)
endfunc
func CustomCompleteList(A, L, P)
- return [ "Monday", "Tuesday", "Wednesday" ]
+ return [ "Monday", "Tuesday", "Wednesday", {}]
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 ?', @:)
@@ -350,7 +399,6 @@ func Test_use_execute_in_completion()
endfunc
func Test_addr_all()
- throw 'skipped: requires patch v8.1.0341 to pass'
command! -addr=lines DoSomething let g:a1 = <line1> | let g:a2 = <line2>
%DoSomething
call assert_equal(1, g:a1)
@@ -551,3 +599,46 @@ func Test_command_list()
call assert_equal("\nNo user-defined commands found", execute(':command Xxx'))
call assert_equal("\nNo user-defined commands found", execute('command'))
endfunc
+
+func Test_delcommand_buffer()
+ command Global echo 'global'
+ command -buffer OneBuffer echo 'one'
+ new
+ command -buffer TwoBuffer echo 'two'
+ call assert_equal(0, exists(':OneBuffer'))
+ call assert_equal(2, exists(':Global'))
+ call assert_equal(2, exists(':TwoBuffer'))
+ delcommand -buffer TwoBuffer
+ call assert_equal(0, exists(':TwoBuffer'))
+ call assert_fails('delcommand -buffer Global', 'E1237:')
+ call assert_fails('delcommand -buffer OneBuffer', 'E1237:')
+ bwipe!
+ call assert_equal(2, exists(':OneBuffer'))
+ delcommand -buffer OneBuffer
+ call assert_equal(0, exists(':OneBuffer'))
+ call assert_fails('delcommand -buffer Global', 'E1237:')
+ delcommand Global
+ call assert_equal(0, exists(':Global'))
+endfunc
+
+func DefCmd(name)
+ if len(a:name) > 30
+ return
+ endif
+ exe 'command ' .. a:name .. ' call DefCmd("' .. a:name .. 'x")'
+ echo a:name
+ exe a:name
+endfunc
+
+func Test_recursive_define()
+ call DefCmd('Command')
+
+ let name = 'Command'
+ while len(name) < 30
+ exe 'delcommand ' .. name
+ let name ..= 'x'
+ endwhile
+endfunc
+
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_utf8.vim b/src/nvim/testdir/test_utf8.vim
index 0818c2e4b0..9b010a5dbc 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
@@ -7,7 +8,7 @@ func Test_visual_block_insert()
new
call setline(1, ["aaa", "あああ", "bbb"])
exe ":norm! gg0l\<C-V>jjIx\<Esc>"
- call assert_equal(['axaa', 'xあああ', 'bxbb'], getline(1, '$'))
+ call assert_equal(['axaa', ' xあああ', 'bxbb'], getline(1, '$'))
bwipeout!
endfunc
@@ -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_utf8_comparisons.vim b/src/nvim/testdir/test_utf8_comparisons.vim
index fdf9d80802..f3c86b44fb 100644
--- a/src/nvim/testdir/test_utf8_comparisons.vim
+++ b/src/nvim/testdir/test_utf8_comparisons.vim
@@ -1,12 +1,12 @@
" Tests for case-insensitive UTF-8 comparisons (utf_strnicmp() in mbyte.c)
" Also test "g~ap".
-function! Ch(a, op, b, expected)
+func Ch(a, op, b, expected)
call assert_equal(eval(printf('"%s" %s "%s"', a:a, a:op, a:b)), a:expected,
\ printf('"%s" %s "%s" should return %d', a:a, a:op, a:b, a:expected))
-endfunction
+endfunc
-function! Chk(a, b, result)
+func Chk(a, b, result)
if a:result == 0
call Ch(a:a, '==?', a:b, 1)
call Ch(a:a, '!=?', a:b, 0)
@@ -86,6 +86,9 @@ endfunc
" test that g~ap changes one paragraph only.
func Test_gap()
new
- call feedkeys("iabcd\n\ndefggg0g~ap", "tx")
+ " setup text
+ call feedkeys("iabcd\<cr>\<cr>defg", "tx")
+ " modify only first line
+ call feedkeys("gg0g~ap", "tx")
call assert_equal(["ABCD", "", "defg"], getline(1,3))
endfunc
diff --git a/src/nvim/testdir/test_vartabs.vim b/src/nvim/testdir/test_vartabs.vim
index 46e0d62313..68fe15ff93 100644
--- a/src/nvim/testdir/test_vartabs.vim
+++ b/src/nvim/testdir/test_vartabs.vim
@@ -1,8 +1,7 @@
" Test for variable tabstops
-if !has("vartabs")
- finish
-endif
+source check.vim
+CheckFeature vartabs
source view_util.vim
@@ -92,6 +91,18 @@ func Test_vartabs()
let expect = "l\<tab> l\<tab>l l\<tab> l\<tab> l"
call assert_equal(expect, getline(1))
+ " Test for 'retab' with vts
+ set ts=8 sts=0 vts=5,3,6,2 vsts=
+ exe "norm! S l"
+ .retab!
+ call assert_equal("\t\t\t\tl", getline(1))
+
+ " Test for 'retab' with same vlaues as vts
+ set ts=8 sts=0 vts=5,3,6,2 vsts=
+ exe "norm! S l"
+ .retab! 5,3,6,2
+ call assert_equal("\t\t\t\tl", getline(1))
+
" Check that global and local values are set.
set ts=4 vts=6 sts=8 vsts=10
call assert_equal(&ts, 4)
@@ -135,7 +146,17 @@ func Test_vartabs()
bwipeout!
endfunc
-func! Test_vartabs_breakindent()
+func Test_retab_invalid_arg()
+ new
+ call setline(1, "\ttext")
+ retab 0
+ call assert_fails("retab -8", 'E487: Argument must be positive')
+ call assert_fails("retab 10000", 'E475:')
+ call assert_fails("retab 720575940379279360", 'E475:')
+ bwipe!
+endfunc
+
+func Test_vartabs_breakindent()
if !exists("+breakindent")
return
endif
@@ -379,3 +400,33 @@ func Test_vartabs_reset()
set all&
call assert_equal('', &vts)
endfunc
+
+func s:SaveCol(l)
+ call add(a:l, [col('.'), virtcol('.')])
+ return ''
+endfunc
+
+" Test for 'varsofttabstop'
+func Test_varsofttabstop()
+ new
+ inoremap <expr> <F2> s:SaveCol(g:cols)
+
+ set backspace=indent,eol,start
+ set varsofttabstop=6,2,5,3
+ let g:cols = []
+ call feedkeys("a\t\<F2>\t\<F2>\t\<F2>\t\<F2> ", 'xt')
+ call assert_equal("\t\t ", getline(1))
+ call assert_equal([[7, 7], [2, 9], [7, 14], [3, 17]], g:cols)
+
+ let g:cols = []
+ call feedkeys("a\<bs>\<F2>\<bs>\<F2>\<bs>\<F2>\<bs>\<F2>\<bs>\<F2>", 'xt')
+ call assert_equal('', getline(1))
+ call assert_equal([[3, 17], [7, 14], [2, 9], [7, 7], [1, 1]], g:cols)
+
+ set varsofttabstop&
+ set backspace&
+ iunmap <F2>
+ close!
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim
index 34733f127f..1323288676 100644
--- a/src/nvim/testdir/test_vimscript.vim
+++ b/src/nvim/testdir/test_vimscript.vim
@@ -1,6 +1,9 @@
" Test various aspects of the Vim script language.
" Most of this was formerly in test49.
+source check.vim
+source shared.vim
+
"-------------------------------------------------------------------------------
" Test environment {{{1
"-------------------------------------------------------------------------------
@@ -25,7 +28,7 @@ com! -nargs=1 Xout call Xout(<args>)
" in the variable argument list. This function is useful if similar tests are
" to be made for a ":return" from a function call or a ":finish" in a script
" file.
-function! MakeScript(funcname, ...)
+func MakeScript(funcname, ...)
let script = tempname()
execute "redir! >" . script
execute "function" a:funcname
@@ -1156,6 +1159,82 @@ func Test_type()
call assert_equal(v:t_list, type(v:_null_list))
call assert_equal(v:t_dict, type(v:_null_dict))
call assert_equal(v:t_blob, type(v:_null_blob))
+
+ call assert_equal(0, 0 + v:false)
+ call assert_equal(1, 0 + v:true)
+ " call assert_equal(0, 0 + v:none)
+ call assert_equal(0, 0 + v:null)
+
+ call assert_equal('false', '' . v:false)
+ call assert_equal('true', '' . v:true)
+ " call assert_equal('none', '' . v:none)
+ call assert_equal('null', '' . v:null)
+
+ call assert_true(v:false == 0)
+ call assert_false(v:false != 0)
+ call assert_true(v:true == 1)
+ call assert_false(v:true != 1)
+ call assert_false(v:true == v:false)
+ call assert_true(v:true != v:false)
+
+ call assert_true(v:null == 0)
+ call assert_false(v:null != 0)
+ " call assert_true(v:none == 0)
+ " call assert_false(v:none != 0)
+
+ call assert_true(v:false is v:false)
+ call assert_true(v:true is v:true)
+ " call assert_true(v:none is v:none)
+ call assert_true(v:null is v:null)
+
+ call assert_false(v:false isnot v:false)
+ call assert_false(v:true isnot v:true)
+ " call assert_false(v:none isnot v:none)
+ call assert_false(v:null isnot v:null)
+
+ call assert_false(v:false is 0)
+ call assert_false(v:true is 1)
+ call assert_false(v:true is v:false)
+ " call assert_false(v:none is 0)
+ call assert_false(v:null is 0)
+ " call assert_false(v:null is v:none)
+
+ call assert_true(v:false isnot 0)
+ call assert_true(v:true isnot 1)
+ call assert_true(v:true isnot v:false)
+ " call assert_true(v:none isnot 0)
+ call assert_true(v:null isnot 0)
+ " call assert_true(v:null isnot v:none)
+
+ call assert_equal(v:false, eval(string(v:false)))
+ call assert_equal(v:true, eval(string(v:true)))
+ " call assert_equal(v:none, eval(string(v:none)))
+ call assert_equal(v:null, eval(string(v:null)))
+
+ call assert_equal(v:false, copy(v:false))
+ call assert_equal(v:true, copy(v:true))
+ " call assert_equal(v:none, copy(v:none))
+ call assert_equal(v:null, copy(v:null))
+
+ call assert_equal([v:false], deepcopy([v:false]))
+ call assert_equal([v:true], deepcopy([v:true]))
+ " call assert_equal([v:none], deepcopy([v:none]))
+ call assert_equal([v:null], deepcopy([v:null]))
+
+ call assert_true(empty(v:false))
+ call assert_false(empty(v:true))
+ call assert_true(empty(v:null))
+ " call assert_true(empty(v:none))
+
+ func ChangeYourMind()
+ try
+ return v:true
+ finally
+ return 'something else'
+ endtry
+ endfunc
+
+ call ChangeYourMind()
endfunc
"-------------------------------------------------------------------------------
@@ -1668,7 +1747,7 @@ func Test_function_defined_line()
[CODE]
call writefile(lines, 'Xtest.vim')
- let res = system(v:progpath .. ' --clean -es -X -S Xtest.vim')
+ let res = system(GetVimCommandClean() .. ' -es -X -S Xtest.vim')
call assert_equal(0, v:shell_error)
let m = matchstr(res, 'function F1()[^[:print:]]*[[:print:]]*')
@@ -1692,6 +1771,145 @@ func Test_function_defined_line()
call delete('Xtest.vim')
endfunc
+" Test for missing :endif, :endfor, :endwhile and :endtry {{{1
+func Test_missing_end()
+ call writefile(['if 2 > 1', 'echo ">"'], 'Xscript')
+ call assert_fails('source Xscript', 'E171:')
+ call writefile(['for i in range(5)', 'echo i'], 'Xscript')
+ call assert_fails('source Xscript', 'E170:')
+ call writefile(['while v:true', 'echo "."'], 'Xscript')
+ call assert_fails('source Xscript', 'E170:')
+ call writefile(['try', 'echo "."'], 'Xscript')
+ call assert_fails('source Xscript', 'E600:')
+ call delete('Xscript')
+
+ " Using endfor with :while
+ let caught_e732 = 0
+ try
+ while v:true
+ endfor
+ catch /E732:/
+ let caught_e732 = 1
+ endtry
+ call assert_equal(1, caught_e732)
+
+ " Using endwhile with :for
+ let caught_e733 = 0
+ try
+ for i in range(1)
+ endwhile
+ catch /E733:/
+ let caught_e733 = 1
+ endtry
+ call assert_equal(1, caught_e733)
+
+ " Missing 'in' in a :for statement
+ call assert_fails('for i range(1) | endfor', 'E690:')
+endfunc
+
+" Test for deep nesting of if/for/while/try statements {{{1
+func Test_deep_nest()
+ if !CanRunVimInTerminal()
+ throw 'Skipped: cannot run vim in terminal'
+ endif
+
+ let lines =<< trim [SCRIPT]
+ " Deep nesting of if ... endif
+ func Test1()
+ let @a = join(repeat(['if v:true'], 51), "\n")
+ let @a ..= "\n"
+ let @a ..= join(repeat(['endif'], 51), "\n")
+ @a
+ let @a = ''
+ endfunc
+
+ " Deep nesting of for ... endfor
+ func Test2()
+ let @a = join(repeat(['for i in [1]'], 51), "\n")
+ let @a ..= "\n"
+ let @a ..= join(repeat(['endfor'], 51), "\n")
+ @a
+ let @a = ''
+ endfunc
+
+ " Deep nesting of while ... endwhile
+ func Test3()
+ let @a = join(repeat(['while v:true'], 51), "\n")
+ let @a ..= "\n"
+ let @a ..= join(repeat(['endwhile'], 51), "\n")
+ @a
+ let @a = ''
+ endfunc
+
+ " Deep nesting of try ... endtry
+ func Test4()
+ let @a = join(repeat(['try'], 51), "\n")
+ let @a ..= "\necho v:true\n"
+ let @a ..= join(repeat(['endtry'], 51), "\n")
+ @a
+ let @a = ''
+ endfunc
+ [SCRIPT]
+ call writefile(lines, 'Xscript')
+
+ let buf = RunVimInTerminal('-S Xscript', {'rows': 6})
+
+ " Deep nesting of if ... endif
+ call term_sendkeys(buf, ":call Test1()\n")
+ call WaitForAssert({-> assert_match('^E579:', term_getline(buf, 5))})
+
+ " Deep nesting of for ... endfor
+ call term_sendkeys(buf, ":call Test2()\n")
+ call WaitForAssert({-> assert_match('^E585:', term_getline(buf, 5))})
+
+ " Deep nesting of while ... endwhile
+ call term_sendkeys(buf, ":call Test3()\n")
+ call WaitForAssert({-> assert_match('^E585:', term_getline(buf, 5))})
+
+ " Deep nesting of try ... endtry
+ call term_sendkeys(buf, ":call Test4()\n")
+ call WaitForAssert({-> assert_match('^E601:', term_getline(buf, 5))})
+
+ "let l = ''
+ "for i in range(1, 6)
+ " let l ..= term_getline(buf, i) . "\n"
+ "endfor
+ "call assert_report(l)
+
+ call StopVimInTerminal(buf)
+ call delete('Xscript')
+endfunc
+
+" Test for <sfile>, <slnum> in a function {{{1
+func Test_sfile_in_function()
+ func Xfunc()
+ call assert_match('..Test_sfile_in_function\[5]..Xfunc', expand('<sfile>'))
+ call assert_equal('2', expand('<slnum>'))
+ endfunc
+ call Xfunc()
+ delfunc Xfunc
+endfunc
+
+func Test_for_over_string()
+ let res = ''
+ for c in 'aéc̀d'
+ let res ..= c .. '-'
+ endfor
+ call assert_equal('a-é-c̀-d-', res)
+
+ let res = ''
+ for c in ''
+ let res ..= c .. '-'
+ endfor
+ call assert_equal('', res)
+
+ let res = ''
+ for c in v:_null_string
+ let res ..= c .. '-'
+ endfor
+ call assert_equal('', res)
+endfunc
+
"-------------------------------------------------------------------------------
" Modelines {{{1
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
diff --git a/src/nvim/testdir/test_virtualedit.vim b/src/nvim/testdir/test_virtualedit.vim
index 8f992f7501..522ca17675 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".
@@ -174,6 +301,9 @@ func Test_replace_on_tab()
call append(0, "'r'\t")
normal gg^5lrxAy
call assert_equal("'r' x y", getline(1))
+ call setline(1, 'aaaaaaaaaaaa')
+ exe "normal! gg2lgR\<Tab>"
+ call assert_equal("aa\taaaa", getline(1))
bwipe!
set virtualedit=
endfunc
@@ -216,4 +346,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..f9ac0e0884 100644
--- a/src/nvim/testdir/test_visual.vim
+++ b/src/nvim/testdir/test_visual.vim
@@ -22,6 +22,14 @@ func Test_block_shift_overflow()
q!
endfunc
+func Test_dotregister_paste()
+ new
+ exe "norm! ihello world\<esc>"
+ norm! 0ve".p
+ call assert_equal('hello world world', getline(1))
+ q!
+endfunc
+
func Test_Visual_ctrl_o()
new
call setline(1, ['one', 'two', 'three'])
@@ -42,14 +50,6 @@ func Test_Visual_vapo()
bwipe!
endfunc
-func Test_dotregister_paste()
- new
- exe "norm! ihello world\<esc>"
- norm! 0ve".p
- call assert_equal('hello world world', getline(1))
- q!
-endfunc
-
func Test_Visual_inner_quote()
new
normal oxX
@@ -57,9 +57,37 @@ func Test_Visual_inner_quote()
bwipe!
endfunc
+" Test for Visual mode not being reset causing E315 error.
+func TriggerTheProblem()
+ " At this point there is no visual selection because :call reset it.
+ " Let's restore the selection:
+ normal gv
+ '<,'>del _
+ try
+ exe "normal \<Esc>"
+ catch /^Vim\%((\a\+)\)\=:E315/
+ echom 'Snap! E315 error!'
+ let g:msg = 'Snap! E315 error!'
+ endtry
+endfunc
+
+func Test_visual_mode_reset()
+ enew
+ let g:msg = "Everything's fine."
+ enew
+ setl buftype=nofile
+ call append(line('$'), 'Delete this line.')
+
+ " NOTE: this has to be done by a call to a function because executing :del
+ " the ex-way will require the colon operator which resets the visual mode
+ " thus preventing the problem:
+ exe "normal! GV:call TriggerTheProblem()\<CR>"
+ call assert_equal("Everything's fine.", g:msg)
+endfunc
+
" Test for visual block shift and tab characters.
func Test_block_shift_tab()
- enew!
+ new
call append(0, repeat(['one two three'], 5))
call cursor(1,1)
exe "normal i\<C-G>u"
@@ -68,7 +96,7 @@ func Test_block_shift_tab()
call assert_equal('on1 two three', getline(2))
call assert_equal('on1 two three', getline(5))
- enew!
+ %d _
call append(0, repeat(['abcdefghijklmnopqrstuvwxyz'], 5))
call cursor(1,1)
exe "normal \<C-V>4jI \<Esc>j<<11|D"
@@ -93,12 +121,26 @@ func Test_block_shift_tab()
call assert_equal(" abc\<Tab>\<Tab>defghijklmnopqrstuvwxyz", getline(4))
call assert_equal(" abc\<Tab> defghijklmnopqrstuvwxyz", getline(5))
- enew!
+ " Test for block shift with space characters at the beginning and with
+ " 'noexpandtab' and 'expandtab'
+ %d _
+ call setline(1, [" 1", " 2", " 3"])
+ setlocal shiftwidth=2 noexpandtab
+ exe "normal gg\<C-V>3j>"
+ call assert_equal(["\t1", "\t2", "\t3"], getline(1, '$'))
+ %d _
+ call setline(1, [" 1", " 2", " 3"])
+ setlocal shiftwidth=2 expandtab
+ exe "normal gg\<C-V>3j>"
+ call assert_equal([" 1", " 2", " 3"], getline(1, '$'))
+ setlocal shiftwidth&
+
+ bw!
endfunc
" Tests Blockwise Visual when there are TABs before the text.
func Test_blockwise_visual()
- enew!
+ new
call append(0, ['123456',
\ '234567',
\ '345678',
@@ -120,26 +162,31 @@ func Test_blockwise_visual()
\ "\t\tsomext",
\ "\t\ttesext"], getline(1, 7))
- enew!
+ bw!
endfunc
" Test swapping corners in blockwise visual mode with o and O
func Test_blockwise_visual_o_O()
- enew!
+ new
exe "norm! 10i.\<Esc>Y4P3lj\<C-V>4l2jr "
exe "norm! gvO\<Esc>ra"
exe "norm! gvO\<Esc>rb"
exe "norm! gvo\<C-c>rc"
exe "norm! gvO\<C-c>rd"
+ set selection=exclusive
+ exe "norm! gvOo\<C-c>re"
+ call assert_equal('...a be.', getline(4))
+ exe "norm! gvOO\<C-c>rf"
+ set selection&
call assert_equal(['..........',
\ '...c d..',
\ '... ..',
- \ '...a b..',
+ \ '...a bf.',
\ '..........'], getline(1, '$'))
- enew!
+ bw!
endfun
" Test Virtual replace mode.
@@ -237,40 +284,20 @@ func Test_virtual_replace2()
call assert_equal(['abcd',
\ 'efgh',
\ 'ijkl'], getline(1, '$'))
+
+ " Test for truncating spaces in a newly added line using 'autoindent' if
+ " characters are not added to that line.
+ %d_
+ call setline(1, [' app', ' bee', ' cat'])
+ setlocal autoindent
+ exe "normal gg$gRt\n\nr"
+ call assert_equal([' apt', '', ' rat'], getline(1, '$'))
+
" clean up
%d_
set bs&vim
endfunc
-" Test for Visual mode not being reset causing E315 error.
-func TriggerTheProblem()
- " At this point there is no visual selection because :call reset it.
- " Let's restore the selection:
- normal gv
- '<,'>del _
- try
- exe "normal \<Esc>"
- catch /^Vim\%((\a\+)\)\=:E315/
- echom 'Snap! E315 error!'
- let g:msg = 'Snap! E315 error!'
- endtry
-endfunc
-
-func Test_visual_mode_reset()
- enew
- let g:msg = "Everything's fine."
- enew
- setl buftype=nofile
- call append(line('$'), 'Delete this line.')
-
- " NOTE: this has to be done by a call to a function because executing :del
- " the ex-way will require the colon operator which resets the visual mode
- " thus preventing the problem:
- exe "normal! GV:call TriggerTheProblem()\<CR>"
- call assert_equal("Everything's fine.", g:msg)
-
-endfunc
-
func Test_Visual_word_textobject()
new
call setline(1, ['First sentence. Second sentence.'])
@@ -349,17 +376,6 @@ func Test_Visual_sentence_textobject()
bwipe!
endfunc
-func Test_curswant_not_changed()
- new
- call setline(1, ['one', 'two'])
- au InsertLeave * call getcurpos()
- call feedkeys("gg0\<C-V>jI123 \<Esc>j", 'xt')
- call assert_equal([0, 2, 1, 0, 1], getcurpos())
-
- bwipe!
- au! InsertLeave
-endfunc
-
func Test_Visual_paragraph_textobject()
new
call setline(1, ['First line.',
@@ -409,6 +425,17 @@ func Test_Visual_paragraph_textobject()
bwipe!
endfunc
+func Test_curswant_not_changed()
+ new
+ call setline(1, ['one', 'two'])
+ au InsertLeave * call getcurpos()
+ call feedkeys("gg0\<C-V>jI123 \<Esc>j", 'xt')
+ call assert_equal([0, 2, 1, 0, 1], getcurpos())
+
+ bwipe!
+ au! InsertLeave
+endfunc
+
" Tests for "vaBiB", end could be wrong.
func Test_Visual_Block()
new
@@ -433,13 +460,15 @@ func Test_Visual_Block()
close!
endfunc
-func Test_visual_put_in_block()
+" Test for 'p'ut in visual block mode
+func Test_visual_block_put()
new
- call setline(1, ['xxxx', 'y∞yy', 'zzzz'])
- normal 1G2yl
- exe "normal 1G2l\<C-V>jjlp"
- call assert_equal(['xxxx', 'y∞xx', 'zzxx'], getline(1, 3))
- bwipe!
+ 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, '$'))
+ bw!
endfunc
" Visual modes (v V CTRL-V) followed by an operator; count; repeating
@@ -610,6 +639,17 @@ func Test_characterwise_visual_mode()
normal Gkvj$d
call assert_equal(['', 'a', ''], getline(1, '$'))
+ " characterwise visual mode: replace a single character line and the eol
+ %d _
+ call setline(1, "a")
+ normal v$rx
+ call assert_equal(['x'], getline(1, '$'))
+
+ " replace a character with composing characters
+ call setline(1, "xã̳x")
+ normal gg0lvrb
+ call assert_equal("xbx", getline(1))
+
bwipe!
endfunc
@@ -645,6 +685,16 @@ func Test_characterwise_select_mode()
exe "normal Gkgh\<Down>\<End>\<Del>"
call assert_equal(['', 'a', ''], getline(1, '$'))
+ " CTRL-H in select mode behaves like 'x'
+ call setline(1, 'abcdef')
+ exe "normal! gggh\<Right>\<Right>\<Right>\<C-H>"
+ call assert_equal('ef', getline(1))
+
+ " CTRL-O in select mode switches to visual mode for one command
+ call setline(1, 'abcdef')
+ exe "normal! gggh\<C-O>3lm"
+ call assert_equal('mef', getline(1))
+
sunmap <lt>End>
sunmap <lt>Down>
sunmap <lt>Del>
@@ -659,7 +709,6 @@ func Test_linewise_select_mode()
exe "normal GkkgH\<Del>"
call assert_equal(['', 'b', 'c'], getline(1, '$'))
-
" linewise select mode: delete middle two lines
call deletebufline('', 1, '$')
call append('$', ['a', 'b', 'c'])
@@ -681,6 +730,15 @@ func Test_linewise_select_mode()
bwipe!
endfunc
+" Test for blockwise select mode (g CTRL-H)
+func Test_blockwise_select_mode()
+ new
+ call setline(1, ['foo', 'bar'])
+ call feedkeys("g\<BS>\<Right>\<Down>mm", 'xt')
+ call assert_equal(['mmo', 'mmr'], getline(1, '$'))
+ close!
+endfunc
+
func Test_visual_mode_put()
new
@@ -744,8 +802,7 @@ endfunc
func Test_visual_block_mode()
new
call append(0, '')
- call setline(1, ['abcdefghijklm', 'abcdefghijklm', 'abcdefghijklm',
- \ 'abcdefghijklm', 'abcdefghijklm'])
+ call setline(1, repeat(['abcdefghijklm'], 5))
call cursor(1, 1)
" Test shift-right of a block
@@ -764,6 +821,76 @@ func Test_visual_block_mode()
\ 'axyzqqqqefgmnoklm',
\ 'abcdqqqqijklm'], getline(1, 5))
+ " Test 'C' to change till the end of the line
+ call cursor(3, 4)
+ exe "normal! \<C-V>j3lCooo"
+ call assert_equal(['axyooo', 'axyooo'], getline(3, 4))
+
+ " Test 'D' to delete till the end of the line
+ call cursor(3, 3)
+ exe "normal! \<C-V>j2lD"
+ call assert_equal(['ax', 'ax'], getline(3, 4))
+
+ " Test block insert with a short line that ends before the block
+ %d _
+ call setline(1, [" one", "a", " two"])
+ exe "normal gg\<C-V>2jIx"
+ call assert_equal([" xone", "a", " xtwo"], getline(1, '$'))
+
+ " Test block append at EOL with '$' and without '$'
+ %d _
+ call setline(1, ["one", "a", "two"])
+ exe "normal gg$\<C-V>2jAx"
+ call assert_equal(["onex", "ax", "twox"], getline(1, '$'))
+ %d _
+ call setline(1, ["one", "a", "two"])
+ exe "normal gg3l\<C-V>2jAx"
+ call assert_equal(["onex", "a x", "twox"], getline(1, '$'))
+
+ " Test block replace with an empty line in the middle and use $ to jump to
+ " the end of the line.
+ %d _
+ call setline(1, ['one', '', 'two'])
+ exe "normal gg$\<C-V>2jrx"
+ call assert_equal(["onx", "", "twx"], getline(1, '$'))
+
+ " Test block replace with an empty line in the middle and move cursor to the
+ " end of the line
+ %d _
+ call setline(1, ['one', '', 'two'])
+ exe "normal gg2l\<C-V>2jrx"
+ call assert_equal(["onx", "", "twx"], getline(1, '$'))
+
+ " Replace odd number of characters with a multibyte character
+ %d _
+ call setline(1, ['abcd', 'efgh'])
+ exe "normal ggl\<C-V>2ljr\u1100"
+ call assert_equal(["a\u1100 ", "e\u1100 "], getline(1, '$'))
+
+ " During visual block append, if the cursor moved outside of the selected
+ " range, then the edit should not be applied to the block.
+ %d _
+ call setline(1, ['aaa', 'bbb', 'ccc'])
+ exe "normal 2G\<C-V>jAx\<Up>"
+ call assert_equal(['aaa', 'bxbb', 'ccc'], getline(1, '$'))
+
+ " During visual block append, if the cursor is moved before the start of the
+ " block, then the new text should be appended there.
+ %d _
+ call setline(1, ['aaa', 'bbb', 'ccc'])
+ exe "normal $\<C-V>2jA\<Left>x"
+ call assert_equal(['aaxa', 'bbxb', 'ccxc'], getline(1, '$'))
+ " Repeat the previous test but use 'l' to move the cursor instead of '$'
+ call setline(1, ['aaa', 'bbb', 'ccc'])
+ exe "normal! gg2l\<C-V>2jA\<Left>x"
+ call assert_equal(['aaxa', 'bbxb', 'ccxc'], getline(1, '$'))
+
+ " Change a characterwise motion to a blockwise motion using CTRL-V
+ %d _
+ call setline(1, ['123', '456', '789'])
+ exe "normal ld\<C-V>j"
+ call assert_equal(['13', '46', '789'], getline(1, '$'))
+
" Test from ':help v_b_I_example'
%d _
setlocal tabstop=8 shiftwidth=4
@@ -995,6 +1122,105 @@ func Test_block_insert_replace_tabs()
bwipe!
endfunc
+" Test for * register in :
+func Test_star_register()
+ call assert_fails('*bfirst', 'E16:')
+ new
+ call setline(1, ['foo', 'bar', 'baz', 'qux'])
+ exe "normal jVj\<ESC>"
+ *yank r
+ call assert_equal("bar\nbaz\n", @r)
+
+ delmarks < >
+ call assert_fails('*yank', 'E20:')
+ close!
+endfunc
+
+" Test for using visual mode maps in select mode
+func Test_select_mode_map()
+ new
+ vmap <buffer> <F2> 3l
+ call setline(1, 'Test line')
+ call feedkeys("gh\<F2>map", 'xt')
+ call assert_equal('map line', getline(1))
+
+ vmap <buffer> <F2> ygV
+ call feedkeys("0gh\<Right>\<Right>\<F2>cwabc", 'xt')
+ call assert_equal('abc line', getline(1))
+
+ vmap <buffer> <F2> :<C-U>let v=100<CR>
+ call feedkeys("gggh\<Right>\<Right>\<F2>foo", 'xt')
+ call assert_equal('foo line', getline(1))
+
+ " reselect the select mode using gv from a visual mode map
+ vmap <buffer> <F2> gv
+ set selectmode=cmd
+ call feedkeys("0gh\<F2>map", 'xt')
+ call assert_equal('map line', getline(1))
+ set selectmode&
+
+ close!
+endfunc
+
+" Test for changing text in visual mode with 'exclusive' selection
+func Test_exclusive_selection()
+ new
+ call setline(1, ['one', 'two'])
+ set selection=exclusive
+ call feedkeys("vwcabc", 'xt')
+ call assert_equal('abctwo', getline(1))
+ call setline(1, ["\tone"])
+ set virtualedit=all
+ call feedkeys('0v2lcl', 'xt')
+ call assert_equal('l one', getline(1))
+ set virtualedit&
+ set selection&
+ close!
+endfunc
+
+" Test for starting visual mode with a count.
+" This test should be run without any previous visual modes. So this should be
+" run as a first test.
+func Test_AAA_start_visual_mode_with_count()
+ new
+ call setline(1, ['aaaaaaa', 'aaaaaaa', 'aaaaaaa', 'aaaaaaa'])
+ normal! gg2Vy
+ call assert_equal("aaaaaaa\naaaaaaa\n", @")
+ close!
+endfunc
+
+" Test for visually selecting an inner block (iB)
+func Test_visual_inner_block()
+ new
+ call setline(1, ['one', '{', 'two', '{', 'three', '}', 'four', '}', 'five'])
+ call cursor(5, 1)
+ " visually select all the lines in the block and then execute iB
+ call feedkeys("ViB\<C-C>", 'xt')
+ call assert_equal([0, 5, 1, 0], getpos("'<"))
+ call assert_equal([0, 5, 6, 0], getpos("'>"))
+ " visually select two inner blocks
+ call feedkeys("ViBiB\<C-C>", 'xt')
+ call assert_equal([0, 3, 1, 0], getpos("'<"))
+ call assert_equal([0, 7, 5, 0], getpos("'>"))
+ " try to select non-existing inner block
+ call cursor(5, 1)
+ call assert_beeps('normal ViBiBiB')
+ " try to select a unclosed inner block
+ 8,9d
+ call cursor(5, 1)
+ call assert_beeps('normal ViBiB')
+ close!
+endfunc
+
+func Test_visual_put_in_block()
+ new
+ call setline(1, ['xxxx', 'y∞yy', 'zzzz'])
+ normal 1G2yl
+ exe "normal 1G2l\<C-V>jjlp"
+ call assert_equal(['xxxx', 'y∞xx', 'zzxx'], getline(1, 3))
+ bwipe!
+endfunc
+
func Test_visual_put_in_block_using_zp()
new
" paste using zP
@@ -1090,6 +1316,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 +1337,217 @@ 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_block_append_invalid_char()
+ " this was going over the end of the line
+ set isprint=@,161-255
+ new
+ call setline(1, [' let xxx', 'xxxxxˆ', 'xxxxxxxxxxx'])
+ exe "normal 0\<C-V>jjA-\<Esc>"
+ call assert_equal([' - let xxx', 'xxxxx -ˆ', 'xxxxxxxx-xxx'], getline(1, 3))
+ bwipe!
+ set isprint&
+endfunc
+
+func Test_visual_block_with_substitute()
+ " this was reading beyond the end of the line
+ new
+ norm a0)
+ sil! norm  O
+ s/)
+ sil! norm 
+ bwipe!
+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
+
+func Test_visual_block_insert_round_off()
+ new
+ " The number of characters are tuned to fill a 4096 byte allocated block,
+ " so that valgrind reports going over the end.
+ call setline(1, ['xxxxx', repeat('0', 1350), "\t", repeat('x', 60)])
+ exe "normal gg0\<C-V>GI" .. repeat('0', 1320) .. "\<Esc>"
+ bwipe!
+endfunc
+
+" this was causing an ml_get error
+func Test_visual_exchange_windows()
+ enew!
+ new
+ call setline(1, ['foo', 'bar'])
+ exe "normal G\<C-V>gg\<C-W>\<C-X>OO\<Esc>"
+ bwipe!
+ bwipe!
+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
+
+func Test_visual_paste()
+ new
+
+ " v_p overwrites unnamed register.
+ call setline(1, ['xxxx'])
+ call setreg('"', 'foo')
+ call setreg('-', 'bar')
+ normal gg0vp
+ call assert_equal('x', @")
+ call assert_equal('x', @-)
+ call assert_equal('fooxxx', getline(1))
+ normal $vp
+ call assert_equal('x', @")
+ call assert_equal('x', @-)
+ call assert_equal('fooxxx', getline(1))
+ " Test with a different register as unnamed register.
+ call setline(2, ['baz'])
+ normal 2gg0"rD
+ call assert_equal('baz', @")
+ normal gg0vp
+ call assert_equal('f', @")
+ call assert_equal('f', @-)
+ call assert_equal('bazooxxx', getline(1))
+ normal $vp
+ call assert_equal('x', @")
+ call assert_equal('x', @-)
+ call assert_equal('bazooxxf', getline(1))
+
+ bwipe!
+endfunc
+
+func Test_visual_paste_clipboard()
+ CheckFeature clipboard_working
+
+ if has('gui')
+ " auto select feature breaks tests
+ set guioptions-=a
+ endif
+
+ " v_P does not overwrite unnamed register.
+ call setline(1, ['xxxx'])
+ call setreg('"', 'foo')
+ call setreg('-', 'bar')
+ normal gg0vP
+ call assert_equal('foo', @")
+ call assert_equal('bar', @-)
+ call assert_equal('fooxxx', getline(1))
+ normal $vP
+ call assert_equal('foo', @")
+ call assert_equal('bar', @-)
+ call assert_equal('fooxxfoo', getline(1))
+ " Test with a different register as unnamed register.
+ call setline(2, ['baz'])
+ normal 2gg0"rD
+ call assert_equal('baz', @")
+ normal gg0vP
+ call assert_equal('baz', @")
+ call assert_equal('bar', @-)
+ call assert_equal('bazooxxfoo', getline(1))
+ normal $vP
+ call assert_equal('baz', @")
+ call assert_equal('bar', @-)
+ call assert_equal('bazooxxfobaz', getline(1))
+
+ " Test for unnamed clipboard
+ set clipboard=unnamed
+ call setline(1, ['xxxx'])
+ call setreg('"', 'foo')
+ call setreg('-', 'bar')
+ call setreg('*', 'baz')
+ normal gg0vP
+ call assert_equal('foo', @")
+ call assert_equal('bar', @-)
+ call assert_equal('baz', @*)
+ call assert_equal('bazxxx', getline(1))
+
+ " Test for unnamedplus clipboard
+ if has('unnamedplus')
+ set clipboard=unnamedplus
+ call setline(1, ['xxxx'])
+ call setreg('"', 'foo')
+ call setreg('-', 'bar')
+ call setreg('+', 'baz')
+ normal gg0vP
+ call assert_equal('foo', @")
+ call assert_equal('bar', @-)
+ call assert_equal('baz', @+)
+ call assert_equal('bazxxx', getline(1))
+ endif
+
+ set clipboard&
+ if has('gui')
+ set guioptions&
+ endif
+ bwipe!
+endfunc
+
+func Test_visual_area_adjusted_when_hiding()
+ " The Visual area ended after the end of the line after :hide
+ call setline(1, 'xxx')
+ vsplit Xfile
+ call setline(1, 'xxxxxxxx')
+ norm! $o
+ hid
+ norm! zW
+ bwipe!
+ bwipe!
endfunc
diff --git a/src/nvim/testdir/test_winbuf_close.vim b/src/nvim/testdir/test_winbuf_close.vim
index 7f5b80e8d3..f4878c2397 100644
--- a/src/nvim/testdir/test_winbuf_close.vim
+++ b/src/nvim/testdir/test_winbuf_close.vim
@@ -194,3 +194,22 @@ func Test_tabwin_close()
call assert_true(v:true)
%bwipe!
endfunc
+
+" Test when closing a split window (above/below) restores space to the window
+" below when 'noequalalways' and 'splitright' are set.
+func Test_window_close_splitright_noequalalways()
+ set noequalalways
+ set splitright
+ new
+ let w1 = win_getid()
+ new
+ let w2 = win_getid()
+ execute "normal \<c-w>b"
+ let h = winheight(0)
+ let w = win_getid()
+ new
+ q
+ call assert_equal(h, winheight(0), "Window height does not match eight before opening and closing another window")
+ call assert_equal(w, win_getid(), "Did not return to original window after opening and closing a window")
+endfunc
+
diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim
index a200bf7d42..7decac2c36 100644
--- a/src/nvim/testdir/test_window_cmd.vim
+++ b/src/nvim/testdir/test_window_cmd.vim
@@ -34,7 +34,16 @@ func Test_window_cmd_cmdwin_with_vsp()
set ls&vim
endfunc
-function Test_window_cmd_wincmd_gf()
+" Test for jumping to windows
+func Test_window_jump()
+ new
+ " jumping to a window with a count greater than the max windows
+ exe "normal 4\<C-W>w"
+ call assert_equal(2, winnr())
+ only
+endfunc
+
+func Test_window_cmd_wincmd_gf()
let fname = 'test_gf.txt'
let swp_fname = '.' . fname . '.swp'
call writefile([], fname)
@@ -172,6 +181,35 @@ func Test_window_split_edit_bufnr()
%bw!
endfunc
+func Test_window_split_no_room()
+ " N horizontal windows need >= 2*N + 1 lines:
+ " - 1 line + 1 status line in each window
+ " - 1 Ex command line
+ "
+ " 2*N + 1 <= &lines
+ " N <= (lines - 1)/2
+ "
+ " Beyond that number of windows, E36: Not enough room is expected.
+ let hor_win_count = (&lines - 1)/2
+ let hor_split_count = hor_win_count - 1
+ for s in range(1, hor_split_count) | split | endfor
+ call assert_fails('split', 'E36:')
+
+ " N vertical windows need >= 2*(N - 1) + 1 columns:
+ " - 1 column + 1 separator for each window (except last window)
+ " - 1 column for the last window which does not have separator
+ "
+ " 2*(N - 1) + 1 <= &columns
+ " 2*N - 1 <= &columns
+ " N <= (&columns + 1)/2
+ let ver_win_count = (&columns + 1)/2
+ let ver_split_count = ver_win_count - 1
+ for s in range(1, ver_split_count) | vsplit | endfor
+ call assert_fails('vsplit', 'E36:')
+
+ %bw!
+endfunc
+
func Test_window_exchange()
e Xa
@@ -513,14 +551,15 @@ func Test_window_colon_command()
endfunc
func Test_access_freed_mem()
+ call assert_equal(&columns, winwidth(0))
" This was accessing freed memory (but with what events?)
au BufEnter,BufLeave,WinEnter,WinLeave 0 vs xxx
arg 0
argadd
- all
- all
+ call assert_fails("all", "E242:")
au!
bwipe xxx
+ call assert_equal(&columns, winwidth(0))
endfunc
func Test_visual_cleared_after_window_split()
@@ -575,7 +614,7 @@ func Test_winrestcmd()
only
endfunc
-function! Fun_RenewFile()
+func Fun_RenewFile()
" Need to wait a bit for the timestamp to be older.
let old_ftime = getftime("tmp.txt")
while getftime("tmp.txt") == old_ftime
@@ -585,7 +624,7 @@ function! Fun_RenewFile()
sp
wincmd p
edit! tmp.txt
-endfunction
+endfunc
func Test_window_prevwin()
" Can we make this work on MS-Windows?
@@ -611,7 +650,7 @@ func Test_window_prevwin()
" reset
q
call delete('tmp.txt')
- set nohidden autoread&vim
+ set hidden&vim autoread&vim
delfunc Fun_RenewFile
endfunc
@@ -885,7 +924,157 @@ func Test_floatwin_splitmove()
bwipe
endfunc
+" Test for the :only command
+func Test_window_only()
+ new
+ set modified
+ new
+ call assert_fails('only', 'E445:')
+ only!
+ " Test for :only with a count
+ let wid = win_getid()
+ new
+ new
+ 3only
+ call assert_equal(1, winnr('$'))
+ call assert_equal(wid, win_getid())
+ call assert_fails('close', 'E444:')
+ call assert_fails('%close', 'E16:')
+endfunc
+
+" Test for errors with :wincmd
+func Test_wincmd_errors()
+ call assert_fails('wincmd g', 'E474:')
+ call assert_fails('wincmd ab', 'E474:')
+endfunc
+
+" Test for errors with :winpos
+func Test_winpos_errors()
+ throw 'Skipped: Nvim does not have :winpos'
+ if !has("gui_running") && !has('win32')
+ call assert_fails('winpos', 'E188:')
+ endif
+ call assert_fails('winpos 10', 'E466:')
+endfunc
+
+" Test for +cmd in a :split command
+func Test_split_cmd()
+ split +set\ readonly
+ call assert_equal(1, &readonly)
+ call assert_equal(2, winnr('$'))
+ close
+endfunc
+
+" Create maximum number of horizontally or vertically split windows and then
+" run commands that create a new horizontally/vertically split window
+func Run_noroom_for_newwindow_test(dir_arg)
+ let dir = (a:dir_arg == 'v') ? 'vert ' : ''
+
+ " Open as many windows as possible
+ for i in range(500)
+ try
+ exe dir . 'new'
+ catch /E36:/
+ break
+ endtry
+ endfor
+
+ call writefile(['first', 'second', 'third'], 'Xfile1')
+ call writefile([], 'Xfile2')
+ call writefile([], 'Xfile3')
+
+ " Argument list related commands
+ args Xfile1 Xfile2 Xfile3
+ next
+ for cmd in ['sargument 2', 'snext', 'sprevious', 'sNext', 'srewind',
+ \ 'sfirst', 'slast']
+ call assert_fails(dir .. cmd, 'E36:')
+ endfor
+ %argdelete
+
+ " Buffer related commands
+ set modified
+ hide enew
+ for cmd in ['sbuffer Xfile1', 'sbnext', 'sbprevious', 'sbNext', 'sbrewind',
+ \ 'sbfirst', 'sblast', 'sball', 'sbmodified', 'sunhide']
+ call assert_fails(dir .. cmd, 'E36:')
+ endfor
+
+ " Window related commands
+ for cmd in ['split', 'split Xfile2', 'new', 'new Xfile3', 'sview Xfile1',
+ \ 'sfind runtest.vim']
+ call assert_fails(dir .. cmd, 'E36:')
+ endfor
+
+ " Help
+ call assert_fails(dir .. 'help', 'E36:')
+ call assert_fails(dir .. 'helpgrep window', 'E36:')
+
+ " Command-line window
+ if a:dir_arg == 'h'
+ " Cmd-line window is always a horizontally split window
+ call assert_beeps('call feedkeys("q:\<CR>", "xt")')
+ endif
+
+ " Quickfix and location list window
+ if has('quickfix')
+ cexpr ''
+ call assert_fails(dir .. 'copen', 'E36:')
+ lexpr ''
+ call assert_fails(dir .. 'lopen', 'E36:')
+
+ " Preview window
+ call assert_fails(dir .. 'pedit Xfile2', 'E36:')
+ call setline(1, 'abc')
+ call assert_fails(dir .. 'psearch abc', 'E36:')
+ endif
+
+ " Window commands (CTRL-W ^ and CTRL-W f)
+ if a:dir_arg == 'h'
+ call assert_fails('call feedkeys("\<C-W>^", "xt")', 'E36:')
+ call setline(1, 'Xfile1')
+ call assert_fails('call feedkeys("gg\<C-W>f", "xt")', 'E36:')
+ endif
+ enew!
+
+ " Tag commands (:stag, :stselect and :stjump)
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "second\tXfile1\t2",
+ \ "third\tXfile1\t3",],
+ \ 'Xtags')
+ set tags=Xtags
+ call assert_fails(dir .. 'stag second', 'E36:')
+ call assert_fails('call feedkeys(":" .. dir .. "stselect second\n1\n", "xt")', 'E36:')
+ call assert_fails(dir .. 'stjump second', 'E36:')
+ call assert_fails(dir .. 'ptag second', 'E36:')
+ set tags&
+ call delete('Xtags')
+
+ " :isplit and :dsplit
+ call setline(1, ['#define FOO 1', 'FOO'])
+ normal 2G
+ call assert_fails(dir .. 'isplit FOO', 'E36:')
+ call assert_fails(dir .. 'dsplit FOO', 'E36:')
+
+ " terminal
+ if has('terminal')
+ call assert_fails(dir .. 'terminal', 'E36:')
+ endif
+
+ %bwipe!
+ call delete('Xfile1')
+ call delete('Xfile2')
+ call delete('Xfile3')
+ only
+endfunc
+
+func Test_split_cmds_with_no_room()
+ call Run_noroom_for_newwindow_test('h')
+ call Run_noroom_for_newwindow_test('v')
+endfunc
+
func Test_window_resize()
+ throw 'Skipped: Nvim supports cmdheight=0'
" Vertical :resize (absolute, relative, min and max size).
vsplit
vert resize 8
@@ -939,4 +1128,304 @@ func Test_window_resize()
%bwipe!
endfunc
+" Test for adjusting the window width when a window is closed with some
+" windows using 'winfixwidth'
+func Test_window_width_adjust()
+ only
+ " Three vertical windows. Windows 1 and 2 have 'winfixwidth' set and close
+ " window 2.
+ wincmd v
+ vert resize 10
+ set winfixwidth
+ wincmd v
+ set winfixwidth
+ wincmd c
+ call assert_inrange(10, 12, winwidth(1))
+ " Three vertical windows. Windows 2 and 3 have 'winfixwidth' set and close
+ " window 3.
+ only
+ set winfixwidth
+ wincmd v
+ vert resize 10
+ set winfixwidth
+ wincmd v
+ set nowinfixwidth
+ wincmd b
+ wincmd c
+ call assert_inrange(10, 12, winwidth(2))
+
+ new | only
+endfunc
+
+" Test for jumping to a vertical/horizontal neighbor window based on the
+" current cursor position
+func Test_window_goto_neightbor()
+ %bw!
+
+ " Vertical window movement
+
+ " create the following window layout:
+ " +--+--+
+ " |w1|w3|
+ " +--+ |
+ " |w2| |
+ " +--+--+
+ " |w4 |
+ " +-----+
+ new
+ vsplit
+ split
+ " vertically jump from w4
+ wincmd b
+ call setline(1, repeat(' ', &columns))
+ call cursor(1, 1)
+ wincmd k
+ call assert_equal(2, winnr())
+ wincmd b
+ call cursor(1, &columns)
+ redraw!
+ wincmd k
+ call assert_equal(3, winnr())
+ %bw!
+
+ " create the following window layout:
+ " +--+--+--+
+ " |w1|w2|w3|
+ " +--+--+--+
+ " |w4 |
+ " +--------+
+ new
+ vsplit
+ vsplit
+ wincmd b
+ call setline(1, repeat(' ', &columns))
+ call cursor(1, 1)
+ wincmd k
+ call assert_equal(1, winnr())
+ wincmd b
+ call cursor(1, &columns / 2)
+ redraw!
+ wincmd k
+ call assert_equal(2, winnr())
+ wincmd b
+ call cursor(1, &columns)
+ redraw!
+ wincmd k
+ call assert_equal(3, winnr())
+ %bw!
+
+ " Horizontal window movement
+
+ " create the following window layout:
+ " +--+--+--+
+ " |w1|w2|w4|
+ " +--+--+ |
+ " |w3 | |
+ " +-----+--+
+ vsplit
+ split
+ vsplit
+ 4wincmd l
+ call setline(1, repeat([' '], &lines))
+ call cursor(1, 1)
+ redraw!
+ wincmd h
+ call assert_equal(2, winnr())
+ 4wincmd l
+ call cursor(&lines, 1)
+ redraw!
+ wincmd h
+ call assert_equal(3, winnr())
+ %bw!
+
+ " create the following window layout:
+ " +--+--+
+ " |w1|w4|
+ " +--+ +
+ " |w2| |
+ " +--+ +
+ " |w3| |
+ " +--+--+
+ vsplit
+ split
+ split
+ wincmd l
+ call setline(1, repeat([' '], &lines))
+ call cursor(1, 1)
+ redraw!
+ wincmd h
+ call assert_equal(1, winnr())
+ wincmd l
+ call cursor(&lines / 2, 1)
+ redraw!
+ wincmd h
+ call assert_equal(2, winnr())
+ wincmd l
+ call cursor(&lines, 1)
+ redraw!
+ wincmd h
+ call assert_equal(3, winnr())
+ %bw!
+endfunc
+
+" Test for an autocmd closing the destination window when jumping from one
+" window to another.
+func Test_close_dest_window()
+ split
+ edit Xfile
+
+ " Test for BufLeave
+ augroup T1
+ au!
+ au BufLeave Xfile $wincmd c
+ augroup END
+ wincmd b
+ call assert_equal(1, winnr('$'))
+ call assert_equal('Xfile', @%)
+ augroup T1
+ au!
+ augroup END
+
+ " Test for WinLeave
+ new
+ wincmd p
+ augroup T1
+ au!
+ au WinLeave * 1wincmd c
+ augroup END
+ wincmd t
+ call assert_equal(1, winnr('$'))
+ call assert_equal('Xfile', @%)
+ augroup T1
+ au!
+ augroup END
+ augroup! T1
+ %bw!
+endfunc
+
+func Test_win_move_separator()
+ edit a
+ leftabove vsplit b
+ let w = winwidth(0)
+ " check win_move_separator from left window on left window
+ call assert_equal(1, winnr())
+ for offset in range(5)
+ call assert_true(win_move_separator(0, offset))
+ call assert_equal(w + offset, winwidth(0))
+ call assert_true(0->win_move_separator(-offset))
+ call assert_equal(w, winwidth(0))
+ endfor
+ " check win_move_separator from right window on left window number
+ wincmd l
+ call assert_notequal(1, winnr())
+ for offset in range(5)
+ call assert_true(1->win_move_separator(offset))
+ call assert_equal(w + offset, winwidth(1))
+ call assert_true(win_move_separator(1, -offset))
+ call assert_equal(w, winwidth(1))
+ endfor
+ " check win_move_separator from right window on left window ID
+ let id = win_getid(1)
+ for offset in range(5)
+ call assert_true(win_move_separator(id, offset))
+ call assert_equal(w + offset, winwidth(id))
+ call assert_true(id->win_move_separator(-offset))
+ call assert_equal(w, winwidth(id))
+ endfor
+ " check win_move_separator from right window on right window is no-op
+ let w0 = winwidth(0)
+ call assert_true(win_move_separator(0, 1))
+ call assert_equal(w0, winwidth(0))
+ call assert_true(win_move_separator(0, -1))
+ call assert_equal(w0, winwidth(0))
+ " check that win_move_separator doesn't error with offsets beyond moving
+ " possibility
+ call assert_true(win_move_separator(id, 5000))
+ call assert_true(winwidth(id) > w)
+ call assert_true(win_move_separator(id, -5000))
+ call assert_true(winwidth(id) < w)
+ " check that win_move_separator returns false for an invalid window
+ wincmd =
+ let w = winwidth(0)
+ call assert_false(win_move_separator(-1, 1))
+ call assert_equal(w, winwidth(0))
+ " check that win_move_separator returns false for a floating window
+ let id = nvim_open_win(
+ \ 0, 0, #{relative: 'editor', row: 2, col: 2, width: 5, height: 3})
+ let w = winwidth(id)
+ call assert_false(win_move_separator(id, 1))
+ call assert_equal(w, winwidth(id))
+ call nvim_win_close(id, 1)
+ %bwipe!
+endfunc
+
+func Test_win_move_statusline()
+ redraw " This test fails in Nvim without a redraw to clear messages.
+ edit a
+ leftabove split b
+ let h = winheight(0)
+ " check win_move_statusline from top window on top window
+ call assert_equal(1, winnr())
+ for offset in range(5)
+ call assert_true(win_move_statusline(0, offset))
+ call assert_equal(h + offset, winheight(0))
+ call assert_true(0->win_move_statusline(-offset))
+ call assert_equal(h, winheight(0))
+ endfor
+ " check win_move_statusline from bottom window on top window number
+ wincmd j
+ call assert_notequal(1, winnr())
+ for offset in range(5)
+ call assert_true(1->win_move_statusline(offset))
+ call assert_equal(h + offset, winheight(1))
+ call assert_true(win_move_statusline(1, -offset))
+ call assert_equal(h, winheight(1))
+ endfor
+ " check win_move_statusline from bottom window on bottom window
+ let h0 = winheight(0)
+ for offset in range(5)
+ call assert_true(0->win_move_statusline(-offset))
+ call assert_equal(h0 - offset, winheight(0))
+ call assert_equal(1 + offset, &cmdheight)
+ call assert_true(win_move_statusline(0, offset))
+ call assert_equal(h0, winheight(0))
+ call assert_equal(1, &cmdheight)
+ endfor
+ " Nvim supports cmdheight=0
+ set cmdheight=0
+ call assert_true(win_move_statusline(0, 1))
+ "call assert_equal(h0, winheight(0))
+ "call assert_equal(1, &cmdheight)
+ call assert_equal(h0 + 1, winheight(0))
+ call assert_equal(0, &cmdheight)
+ set cmdheight&
+ " check win_move_statusline from bottom window on top window ID
+ let id = win_getid(1)
+ for offset in range(5)
+ call assert_true(win_move_statusline(id, offset))
+ call assert_equal(h + offset, winheight(id))
+ call assert_true(id->win_move_statusline(-offset))
+ call assert_equal(h, winheight(id))
+ endfor
+ " check that win_move_statusline doesn't error with offsets beyond moving
+ " possibility
+ call assert_true(win_move_statusline(id, 5000))
+ call assert_true(winheight(id) > h)
+ call assert_true(win_move_statusline(id, -5000))
+ call assert_true(winheight(id) < h)
+ " check that win_move_statusline returns false for an invalid window
+ wincmd =
+ let h = winheight(0)
+ call assert_false(win_move_statusline(-1, 1))
+ call assert_equal(h, winheight(0))
+ " check that win_move_statusline returns false for a floating window
+ let id = nvim_open_win(
+ \ 0, 0, #{relative: 'editor', row: 2, col: 2, width: 5, height: 3})
+ let h = winheight(id)
+ call assert_false(win_move_statusline(id, 1))
+ call assert_equal(h, winheight(id))
+ call nvim_win_close(id, 1)
+ %bwipe!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_writefile.vim b/src/nvim/testdir/test_writefile.vim
index aa7882d129..bfbba1f793 100644
--- a/src/nvim/testdir/test_writefile.vim
+++ b/src/nvim/testdir/test_writefile.vim
@@ -43,7 +43,7 @@ func Test_writefile_fails_gently()
endfunc
func Test_writefile_fails_conversion()
- if !has('iconv') || system('uname -s') =~ 'SunOS'
+ if !has('iconv') || has('sun')
return
endif
" Without a backup file the write won't happen if there is a conversion
@@ -169,9 +169,7 @@ endfunc
" Test for ':w !<cmd>' to pipe lines from the current buffer to an external
" command.
func Test_write_pipe_to_cmd()
- if !has('unix')
- return
- endif
+ CheckUnix
new
call setline(1, ['L1', 'L2', 'L3', 'L4'])
2,3w !cat > Xfile
@@ -208,6 +206,8 @@ func Test_write_errors()
call assert_fails('1,2write', 'E140:')
close!
+ call assert_fails('w > Xtest', 'E494:')
+
" Try to overwrite a directory
if has('unix')
call mkdir('Xdir1')
@@ -371,6 +371,154 @@ func Test_write_file_encoding()
%bw!
endfunc
+" Test for writing and reading a file starting with a BOM.
+" Byte Order Mark (BOM) character for various encodings is below:
+" UTF-8 : EF BB BF
+" UTF-16 (BE): FE FF
+" UTF-16 (LE): FF FE
+" UTF-32 (BE): 00 00 FE FF
+" UTF-32 (LE): FF FE 00 00
+func Test_readwrite_file_with_bom()
+ let utf8_bom = "\xEF\xBB\xBF"
+ let utf16be_bom = "\xFE\xFF"
+ let utf16le_bom = "\xFF\xFE"
+ let utf32be_bom = "\n\n\xFE\xFF"
+ let utf32le_bom = "\xFF\xFE\n\n"
+ let save_fileencoding = &fileencoding
+ set cpoptions+=S
+
+ " Check that editing a latin1 file doesn't see a BOM
+ call writefile(["\xFE\xFElatin-1"], 'Xtest1')
+ edit Xtest1
+ call assert_equal('latin1', &fileencoding)
+ call assert_equal(0, &bomb)
+ set fenc=latin1
+ write Xfile2
+ call assert_equal(["\xFE\xFElatin-1", ''], readfile('Xfile2', 'b'))
+ set bomb fenc=latin1
+ write Xtest3
+ call assert_equal(["\xFE\xFElatin-1", ''], readfile('Xtest3', 'b'))
+ set bomb&
+
+ " Check utf-8 BOM
+ %bw!
+ call writefile([utf8_bom .. "utf-8"], 'Xtest1')
+ edit! Xtest1
+ call assert_equal('utf-8', &fileencoding)
+ call assert_equal(1, &bomb)
+ call assert_equal('utf-8', getline(1))
+ set fenc=latin1
+ write! Xfile2
+ call assert_equal(['utf-8', ''], readfile('Xfile2', 'b'))
+ set fenc=utf-8
+ w! Xtest3
+ call assert_equal([utf8_bom .. "utf-8", ''], readfile('Xtest3', 'b'))
+
+ " Check utf-8 with an error (will fall back to latin-1)
+ %bw!
+ call writefile([utf8_bom .. "utf-8\x80err"], 'Xtest1')
+ edit! Xtest1
+ call assert_equal('latin1', &fileencoding)
+ call assert_equal(0, &bomb)
+ call assert_equal("\xC3\xAF\xC2\xBB\xC2\xBFutf-8\xC2\x80err", getline(1))
+ set fenc=latin1
+ write! Xfile2
+ call assert_equal([utf8_bom .. "utf-8\x80err", ''], readfile('Xfile2', 'b'))
+ set fenc=utf-8
+ w! Xtest3
+ call assert_equal(["\xC3\xAF\xC2\xBB\xC2\xBFutf-8\xC2\x80err", ''],
+ \ readfile('Xtest3', 'b'))
+
+ " Check ucs-2 BOM
+ %bw!
+ call writefile([utf16be_bom .. "\nu\nc\ns\n-\n2\n"], 'Xtest1')
+ edit! Xtest1
+ call assert_equal('utf-16', &fileencoding)
+ call assert_equal(1, &bomb)
+ call assert_equal('ucs-2', getline(1))
+ set fenc=latin1
+ write! Xfile2
+ call assert_equal(["ucs-2", ''], readfile('Xfile2', 'b'))
+ set fenc=ucs-2
+ w! Xtest3
+ call assert_equal([utf16be_bom .. "\nu\nc\ns\n-\n2\n", ''],
+ \ readfile('Xtest3', 'b'))
+
+ " Check ucs-2le BOM
+ %bw!
+ call writefile([utf16le_bom .. "u\nc\ns\n-\n2\nl\ne\n"], 'Xtest1')
+ " Need to add a NUL byte after the NL byte
+ call writefile(0z00, 'Xtest1', 'a')
+ edit! Xtest1
+ call assert_equal('utf-16le', &fileencoding)
+ call assert_equal(1, &bomb)
+ call assert_equal('ucs-2le', getline(1))
+ set fenc=latin1
+ write! Xfile2
+ call assert_equal(["ucs-2le", ''], readfile('Xfile2', 'b'))
+ set fenc=ucs-2le
+ w! Xtest3
+ call assert_equal([utf16le_bom .. "u\nc\ns\n-\n2\nl\ne\n", "\n"],
+ \ readfile('Xtest3', 'b'))
+
+ " Check ucs-4 BOM
+ %bw!
+ call writefile([utf32be_bom .. "\n\n\nu\n\n\nc\n\n\ns\n\n\n-\n\n\n4\n\n\n"], 'Xtest1')
+ edit! Xtest1
+ call assert_equal('ucs-4', &fileencoding)
+ call assert_equal(1, &bomb)
+ call assert_equal('ucs-4', getline(1))
+ set fenc=latin1
+ write! Xfile2
+ call assert_equal(["ucs-4", ''], readfile('Xfile2', 'b'))
+ set fenc=ucs-4
+ w! Xtest3
+ call assert_equal([utf32be_bom .. "\n\n\nu\n\n\nc\n\n\ns\n\n\n-\n\n\n4\n\n\n", ''], readfile('Xtest3', 'b'))
+
+ " Check ucs-4le BOM
+ %bw!
+ call writefile([utf32le_bom .. "u\n\n\nc\n\n\ns\n\n\n-\n\n\n4\n\n\nl\n\n\ne\n\n\n"], 'Xtest1')
+ " Need to add three NUL bytes after the NL byte
+ call writefile(0z000000, 'Xtest1', 'a')
+ edit! Xtest1
+ call assert_equal('ucs-4le', &fileencoding)
+ call assert_equal(1, &bomb)
+ call assert_equal('ucs-4le', getline(1))
+ set fenc=latin1
+ write! Xfile2
+ call assert_equal(["ucs-4le", ''], readfile('Xfile2', 'b'))
+ set fenc=ucs-4le
+ w! Xtest3
+ call assert_equal([utf32le_bom .. "u\n\n\nc\n\n\ns\n\n\n-\n\n\n4\n\n\nl\n\n\ne\n\n\n", "\n\n\n"], readfile('Xtest3', 'b'))
+
+ set cpoptions-=S
+ let &fileencoding = save_fileencoding
+ call delete('Xtest1')
+ call delete('Xfile2')
+ call delete('Xtest3')
+ %bw!
+endfunc
+
+func Test_read_write_bin()
+ " write file missing EOL
+ call writefile(['noeol'], "XNoEolSetEol", 'bS')
+ call assert_equal(0z6E6F656F6C, readfile('XNoEolSetEol', 'B'))
+
+ " when file is read 'eol' is off
+ set nofixeol
+ e! ++ff=unix XNoEolSetEol
+ call assert_equal(0, &eol)
+
+ " writing with 'eol' set adds the newline
+ setlocal eol
+ w
+ call assert_equal(0z6E6F656F6C0A, readfile('XNoEolSetEol', 'B'))
+
+ call delete('XNoEolSetEol')
+ set ff&
+ bwipe! XNoEolSetEol
+endfunc
+
" Check that buffer is written before triggering QuitPre
func Test_wq_quitpre_autocommand()
edit Xsomefile
diff --git a/src/nvim/testing.c b/src/nvim/testing.c
new file mode 100644
index 0000000000..69b687e44f
--- /dev/null
+++ b/src/nvim/testing.c
@@ -0,0 +1,625 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+// testing.c: Support for tests
+
+#include "nvim/eval.h"
+#include "nvim/eval/encode.h"
+#include "nvim/ex_docmd.h"
+#include "nvim/os/os.h"
+#include "nvim/testing.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "testing.c.generated.h"
+#endif
+
+/// Prepare "gap" for an assert error and add the sourcing position.
+static void prepare_assert_error(garray_T *gap)
+{
+ char buf[NUMBUFLEN];
+
+ ga_init(gap, 1, 100);
+ if (sourcing_name != NULL) {
+ ga_concat(gap, (char *)sourcing_name);
+ if (sourcing_lnum > 0) {
+ ga_concat(gap, " ");
+ }
+ }
+ if (sourcing_lnum > 0) {
+ vim_snprintf(buf, ARRAY_SIZE(buf), "line %" PRId64, (int64_t)sourcing_lnum);
+ ga_concat(gap, buf);
+ }
+ if (sourcing_name != NULL || sourcing_lnum > 0) {
+ ga_concat(gap, ": ");
+ }
+}
+
+/// Append "p[clen]" to "gap", escaping unprintable characters.
+/// Changes NL to \n, CR to \r, etc.
+static void ga_concat_esc(garray_T *gap, const char_u *p, int clen)
+ FUNC_ATTR_NONNULL_ALL
+{
+ char_u buf[NUMBUFLEN];
+
+ if (clen > 1) {
+ memmove(buf, p, (size_t)clen);
+ buf[clen] = NUL;
+ ga_concat(gap, (char *)buf);
+ } else {
+ switch (*p) {
+ case BS:
+ ga_concat(gap, "\\b"); break;
+ case ESC:
+ ga_concat(gap, "\\e"); break;
+ case FF:
+ ga_concat(gap, "\\f"); break;
+ case NL:
+ ga_concat(gap, "\\n"); break;
+ case TAB:
+ ga_concat(gap, "\\t"); break;
+ case CAR:
+ ga_concat(gap, "\\r"); break;
+ case '\\':
+ ga_concat(gap, "\\\\"); break;
+ default:
+ if (*p < ' ') {
+ vim_snprintf((char *)buf, NUMBUFLEN, "\\x%02x", *p);
+ ga_concat(gap, (char *)buf);
+ } else {
+ ga_append(gap, (char)(*p));
+ }
+ break;
+ }
+ }
+}
+
+/// Append "str" to "gap", escaping unprintable characters.
+/// Changes NL to \n, CR to \r, etc.
+static void ga_concat_shorten_esc(garray_T *gap, const char_u *str)
+ FUNC_ATTR_NONNULL_ARG(1)
+{
+ char_u buf[NUMBUFLEN];
+
+ if (str == NULL) {
+ ga_concat(gap, "NULL");
+ return;
+ }
+
+ for (const char_u *p = str; *p != NUL; p++) {
+ int same_len = 1;
+ const char_u *s = p;
+ const int c = mb_ptr2char_adv(&s);
+ const int clen = (int)(s - p);
+ while (*s != NUL && c == utf_ptr2char((char *)s)) {
+ same_len++;
+ s += clen;
+ }
+ if (same_len > 20) {
+ ga_concat(gap, "\\[");
+ ga_concat_esc(gap, p, clen);
+ ga_concat(gap, " occurs ");
+ vim_snprintf((char *)buf, NUMBUFLEN, "%d", same_len);
+ ga_concat(gap, (char *)buf);
+ ga_concat(gap, " times]");
+ p = s - 1;
+ } else {
+ ga_concat_esc(gap, p, clen);
+ }
+ }
+}
+
+/// Fill "gap" with information about an assert error.
+static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char_u *exp_str,
+ typval_T *exp_tv_arg, typval_T *got_tv_arg, assert_type_T atype)
+{
+ char_u *tofree;
+ typval_T *exp_tv = exp_tv_arg;
+ typval_T *got_tv = got_tv_arg;
+ bool did_copy = false;
+ int omitted = 0;
+
+ if (opt_msg_tv->v_type != VAR_UNKNOWN) {
+ tofree = (char_u *)encode_tv2echo(opt_msg_tv, NULL);
+ ga_concat(gap, (char *)tofree);
+ xfree(tofree);
+ ga_concat(gap, ": ");
+ }
+
+ if (atype == ASSERT_MATCH || atype == ASSERT_NOTMATCH) {
+ ga_concat(gap, "Pattern ");
+ } else if (atype == ASSERT_NOTEQUAL) {
+ ga_concat(gap, "Expected not equal to ");
+ } else {
+ ga_concat(gap, "Expected ");
+ }
+
+ if (exp_str == NULL) {
+ // When comparing dictionaries, drop the items that are equal, so that
+ // it's a lot easier to see what differs.
+ if (atype != ASSERT_NOTEQUAL
+ && exp_tv->v_type == VAR_DICT && got_tv->v_type == VAR_DICT
+ && exp_tv->vval.v_dict != NULL && got_tv->vval.v_dict != NULL) {
+ dict_T *exp_d = exp_tv->vval.v_dict;
+ dict_T *got_d = got_tv->vval.v_dict;
+
+ did_copy = true;
+ exp_tv->vval.v_dict = tv_dict_alloc();
+ got_tv->vval.v_dict = tv_dict_alloc();
+
+ int todo = (int)exp_d->dv_hashtab.ht_used;
+ for (const hashitem_T *hi = exp_d->dv_hashtab.ht_array; todo > 0; hi++) {
+ if (!HASHITEM_EMPTY(hi)) {
+ dictitem_T *item2 = tv_dict_find(got_d, (const char *)hi->hi_key, -1);
+ if (item2 == NULL
+ || !tv_equal(&TV_DICT_HI2DI(hi)->di_tv, &item2->di_tv, false, false)) {
+ // item of exp_d not present in got_d or values differ.
+ const size_t key_len = STRLEN(hi->hi_key);
+ tv_dict_add_tv(exp_tv->vval.v_dict, (const char *)hi->hi_key, key_len,
+ &TV_DICT_HI2DI(hi)->di_tv);
+ if (item2 != NULL) {
+ tv_dict_add_tv(got_tv->vval.v_dict, (const char *)hi->hi_key, key_len,
+ &item2->di_tv);
+ }
+ } else {
+ omitted++;
+ }
+ todo--;
+ }
+ }
+
+ // Add items only present in got_d.
+ todo = (int)got_d->dv_hashtab.ht_used;
+ for (const hashitem_T *hi = got_d->dv_hashtab.ht_array; todo > 0; hi++) {
+ if (!HASHITEM_EMPTY(hi)) {
+ dictitem_T *item2 = tv_dict_find(exp_d, (const char *)hi->hi_key, -1);
+ if (item2 == NULL) {
+ // item of got_d not present in exp_d
+ const size_t key_len = STRLEN(hi->hi_key);
+ tv_dict_add_tv(got_tv->vval.v_dict, (const char *)hi->hi_key, key_len,
+ &TV_DICT_HI2DI(hi)->di_tv);
+ }
+ todo--;
+ }
+ }
+ }
+
+ tofree = (char_u *)encode_tv2string(exp_tv, NULL);
+ ga_concat_shorten_esc(gap, tofree);
+ xfree(tofree);
+ } else {
+ ga_concat_shorten_esc(gap, exp_str);
+ }
+
+ if (atype != ASSERT_NOTEQUAL) {
+ if (atype == ASSERT_MATCH) {
+ ga_concat(gap, " does not match ");
+ } else if (atype == ASSERT_NOTMATCH) {
+ ga_concat(gap, " does match ");
+ } else {
+ ga_concat(gap, " but got ");
+ }
+ tofree = (char_u *)encode_tv2string(got_tv, NULL);
+ ga_concat_shorten_esc(gap, tofree);
+ xfree(tofree);
+
+ if (omitted != 0) {
+ char buf[100];
+ vim_snprintf(buf, sizeof(buf), " - %d equal item%s omitted", omitted,
+ omitted == 1 ? "" : "s");
+ ga_concat(gap, buf);
+ }
+ }
+
+ if (did_copy) {
+ tv_clear(exp_tv);
+ tv_clear(got_tv);
+ }
+}
+
+static int assert_equal_common(typval_T *argvars, assert_type_T atype)
+ FUNC_ATTR_NONNULL_ALL
+{
+ garray_T ga;
+
+ if (tv_equal(&argvars[0], &argvars[1], false, false)
+ != (atype == ASSERT_EQUAL)) {
+ prepare_assert_error(&ga);
+ fill_assert_error(&ga, &argvars[2], NULL,
+ &argvars[0], &argvars[1], atype);
+ assert_error(&ga);
+ ga_clear(&ga);
+ return 1;
+ }
+ return 0;
+}
+
+static int assert_match_common(typval_T *argvars, assert_type_T atype)
+ FUNC_ATTR_NONNULL_ALL
+{
+ char buf1[NUMBUFLEN];
+ char buf2[NUMBUFLEN];
+ const char *const pat = tv_get_string_buf_chk(&argvars[0], buf1);
+ const char *const text = tv_get_string_buf_chk(&argvars[1], buf2);
+
+ if (pat == NULL || text == NULL) {
+ emsg(_(e_invarg));
+ } else if (pattern_match((char *)pat, (char *)text, false)
+ != (atype == ASSERT_MATCH)) {
+ garray_T ga;
+ prepare_assert_error(&ga);
+ fill_assert_error(&ga, &argvars[2], NULL, &argvars[0], &argvars[1], atype);
+ assert_error(&ga);
+ ga_clear(&ga);
+ return 1;
+ }
+ return 0;
+}
+
+/// Common for assert_true() and assert_false().
+static int assert_bool(typval_T *argvars, bool is_true)
+ FUNC_ATTR_NONNULL_ALL
+{
+ bool error = false;
+ garray_T ga;
+
+ if ((argvars[0].v_type != VAR_NUMBER
+ || (tv_get_number_chk(&argvars[0], &error) == 0) == is_true
+ || error)
+ && (argvars[0].v_type != VAR_BOOL
+ || (argvars[0].vval.v_bool
+ != (BoolVarValue)(is_true
+ ? kBoolVarTrue
+ : kBoolVarFalse)))) {
+ prepare_assert_error(&ga);
+ fill_assert_error(&ga, &argvars[1],
+ (char_u *)(is_true ? "True" : "False"),
+ NULL, &argvars[0], ASSERT_OTHER);
+ assert_error(&ga);
+ ga_clear(&ga);
+ return 1;
+ }
+ return 0;
+}
+
+static void assert_append_cmd_or_arg(garray_T *gap, typval_T *argvars, const char *cmd)
+ FUNC_ATTR_NONNULL_ALL
+{
+ if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) {
+ char *const tofree = encode_tv2echo(&argvars[2], NULL);
+ ga_concat(gap, tofree);
+ xfree(tofree);
+ } else {
+ ga_concat(gap, cmd);
+ }
+}
+
+static int assert_beeps(typval_T *argvars, bool no_beep)
+ FUNC_ATTR_NONNULL_ALL
+{
+ const char *const cmd = tv_get_string_chk(&argvars[0]);
+ int ret = 0;
+
+ called_vim_beep = false;
+ suppress_errthrow = true;
+ emsg_silent = false;
+ do_cmdline_cmd(cmd);
+ if (no_beep ? called_vim_beep : !called_vim_beep) {
+ garray_T ga;
+ prepare_assert_error(&ga);
+ if (no_beep) {
+ ga_concat(&ga, "command did beep: ");
+ } else {
+ ga_concat(&ga, "command did not beep: ");
+ }
+ ga_concat(&ga, cmd);
+ assert_error(&ga);
+ ga_clear(&ga);
+ ret = 1;
+ }
+
+ suppress_errthrow = false;
+ emsg_on_display = false;
+ return ret;
+}
+
+/// "assert_beeps(cmd [, error])" function
+void f_assert_beeps(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ rettv->vval.v_number = assert_beeps(argvars, false);
+}
+
+/// "assert_nobeep(cmd [, error])" function
+void f_assert_nobeep(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ rettv->vval.v_number = assert_beeps(argvars, true);
+}
+
+/// "assert_equal(expected, actual[, msg])" function
+void f_assert_equal(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ rettv->vval.v_number = assert_equal_common(argvars, ASSERT_EQUAL);
+}
+
+static int assert_equalfile(typval_T *argvars)
+ FUNC_ATTR_NONNULL_ALL
+{
+ char buf1[NUMBUFLEN];
+ char buf2[NUMBUFLEN];
+ const char *const fname1 = tv_get_string_buf_chk(&argvars[0], buf1);
+ const char *const fname2 = tv_get_string_buf_chk(&argvars[1], buf2);
+ garray_T ga;
+
+ if (fname1 == NULL || fname2 == NULL) {
+ return 0;
+ }
+
+ IObuff[0] = NUL;
+ FILE *const fd1 = os_fopen(fname1, READBIN);
+ char line1[200];
+ char line2[200];
+ ptrdiff_t lineidx = 0;
+ if (fd1 == NULL) {
+ snprintf((char *)IObuff, IOSIZE, (char *)e_notread, fname1);
+ } else {
+ FILE *const fd2 = os_fopen(fname2, READBIN);
+ if (fd2 == NULL) {
+ fclose(fd1);
+ snprintf((char *)IObuff, IOSIZE, (char *)e_notread, fname2);
+ } else {
+ int64_t linecount = 1;
+ for (int64_t count = 0;; count++) {
+ const int c1 = fgetc(fd1);
+ const int c2 = fgetc(fd2);
+ if (c1 == EOF) {
+ if (c2 != EOF) {
+ STRCPY(IObuff, "first file is shorter");
+ }
+ break;
+ } else if (c2 == EOF) {
+ STRCPY(IObuff, "second file is shorter");
+ break;
+ } else {
+ line1[lineidx] = (char)c1;
+ line2[lineidx] = (char)c2;
+ lineidx++;
+ if (c1 != c2) {
+ snprintf((char *)IObuff, IOSIZE,
+ "difference at byte %" PRId64 ", line %" PRId64,
+ count, linecount);
+ break;
+ }
+ }
+ if (c1 == NL) {
+ linecount++;
+ lineidx = 0;
+ } else if (lineidx + 2 == (ptrdiff_t)sizeof(line1)) {
+ memmove(line1, line1 + 100, (size_t)(lineidx - 100));
+ memmove(line2, line2 + 100, (size_t)(lineidx - 100));
+ lineidx -= 100;
+ }
+ }
+ fclose(fd1);
+ fclose(fd2);
+ }
+ }
+ if (IObuff[0] != NUL) {
+ prepare_assert_error(&ga);
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ char *const tofree = encode_tv2echo(&argvars[2], NULL);
+ ga_concat(&ga, tofree);
+ xfree(tofree);
+ ga_concat(&ga, ": ");
+ }
+ ga_concat(&ga, (char *)IObuff);
+ if (lineidx > 0) {
+ line1[lineidx] = NUL;
+ line2[lineidx] = NUL;
+ ga_concat(&ga, " after \"");
+ ga_concat(&ga, line1);
+ if (STRCMP(line1, line2) != 0) {
+ ga_concat(&ga, "\" vs \"");
+ ga_concat(&ga, line2);
+ }
+ ga_concat(&ga, "\"");
+ }
+ assert_error(&ga);
+ ga_clear(&ga);
+ return 1;
+ }
+ return 0;
+}
+
+/// "assert_equalfile(fname-one, fname-two[, msg])" function
+void f_assert_equalfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ rettv->vval.v_number = assert_equalfile(argvars);
+}
+
+/// "assert_notequal(expected, actual[, msg])" function
+void f_assert_notequal(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ rettv->vval.v_number = assert_equal_common(argvars, ASSERT_NOTEQUAL);
+}
+
+/// "assert_exception(string[, msg])" function
+void f_assert_exception(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ garray_T ga;
+
+ const char *const error = tv_get_string_chk(&argvars[0]);
+ if (*get_vim_var_str(VV_EXCEPTION) == NUL) {
+ prepare_assert_error(&ga);
+ ga_concat(&ga, "v:exception is not set");
+ assert_error(&ga);
+ ga_clear(&ga);
+ rettv->vval.v_number = 1;
+ } else if (error != NULL
+ && strstr((char *)get_vim_var_str(VV_EXCEPTION), error) == NULL) {
+ prepare_assert_error(&ga);
+ fill_assert_error(&ga, &argvars[1], NULL, &argvars[0],
+ get_vim_var_tv(VV_EXCEPTION), ASSERT_OTHER);
+ assert_error(&ga);
+ ga_clear(&ga);
+ rettv->vval.v_number = 1;
+ }
+}
+
+/// "assert_fails(cmd [, error [, msg]])" function
+void f_assert_fails(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ const char *const cmd = tv_get_string_chk(&argvars[0]);
+ garray_T ga;
+ int save_trylevel = trylevel;
+ const int called_emsg_before = called_emsg;
+
+ // trylevel must be zero for a ":throw" command to be considered failed
+ trylevel = 0;
+ suppress_errthrow = true;
+ emsg_silent = true;
+
+ do_cmdline_cmd(cmd);
+ if (called_emsg == called_emsg_before) {
+ prepare_assert_error(&ga);
+ ga_concat(&ga, "command did not fail: ");
+ assert_append_cmd_or_arg(&ga, argvars, cmd);
+ assert_error(&ga);
+ ga_clear(&ga);
+ rettv->vval.v_number = 1;
+ } else if (argvars[1].v_type != VAR_UNKNOWN) {
+ char buf[NUMBUFLEN];
+ const char *const error = tv_get_string_buf_chk(&argvars[1], buf);
+
+ if (error == NULL
+ || strstr((char *)get_vim_var_str(VV_ERRMSG), error) == NULL) {
+ prepare_assert_error(&ga);
+ fill_assert_error(&ga, &argvars[2], NULL, &argvars[1],
+ get_vim_var_tv(VV_ERRMSG), ASSERT_OTHER);
+ ga_concat(&ga, ": ");
+ assert_append_cmd_or_arg(&ga, argvars, cmd);
+ assert_error(&ga);
+ ga_clear(&ga);
+ rettv->vval.v_number = 1;
+ }
+ }
+
+ trylevel = save_trylevel;
+ suppress_errthrow = false;
+ emsg_silent = false;
+ emsg_on_display = false;
+ set_vim_var_string(VV_ERRMSG, NULL, 0);
+}
+
+// "assert_false(actual[, msg])" function
+void f_assert_false(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ rettv->vval.v_number = assert_bool(argvars, false);
+}
+
+static int assert_inrange(typval_T *argvars)
+ FUNC_ATTR_NONNULL_ALL
+{
+ bool error = false;
+
+ if (argvars[0].v_type == VAR_FLOAT
+ || argvars[1].v_type == VAR_FLOAT
+ || argvars[2].v_type == VAR_FLOAT) {
+ const float_T flower = tv_get_float(&argvars[0]);
+ const float_T fupper = tv_get_float(&argvars[1]);
+ const float_T factual = tv_get_float(&argvars[2]);
+
+ if (factual < flower || factual > fupper) {
+ garray_T ga;
+ prepare_assert_error(&ga);
+ if (argvars[3].v_type != VAR_UNKNOWN) {
+ char_u *const tofree = (char_u *)encode_tv2string(&argvars[3], NULL);
+ ga_concat(&ga, (char *)tofree);
+ xfree(tofree);
+ } else {
+ char msg[80];
+ vim_snprintf(msg, sizeof(msg), "Expected range %g - %g, but got %g",
+ flower, fupper, factual);
+ ga_concat(&ga, msg);
+ }
+ assert_error(&ga);
+ ga_clear(&ga);
+ return 1;
+ }
+ } else {
+ const varnumber_T lower = tv_get_number_chk(&argvars[0], &error);
+ const varnumber_T upper = tv_get_number_chk(&argvars[1], &error);
+ const varnumber_T actual = tv_get_number_chk(&argvars[2], &error);
+
+ if (error) {
+ return 0;
+ }
+ if (actual < lower || actual > upper) {
+ garray_T ga;
+ prepare_assert_error(&ga);
+
+ char msg[55];
+ vim_snprintf(msg, sizeof(msg),
+ "range %" PRIdVARNUMBER " - %" PRIdVARNUMBER ",",
+ lower, upper); // -V576
+ fill_assert_error(&ga, &argvars[3], (char_u *)msg, NULL, &argvars[2],
+ ASSERT_INRANGE);
+ assert_error(&ga);
+ ga_clear(&ga);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/// "assert_inrange(lower, upper[, msg])" function
+void f_assert_inrange(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ rettv->vval.v_number = assert_inrange(argvars);
+}
+
+/// "assert_match(pattern, actual[, msg])" function
+void f_assert_match(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ rettv->vval.v_number = assert_match_common(argvars, ASSERT_MATCH);
+}
+
+/// "assert_notmatch(pattern, actual[, msg])" function
+void f_assert_notmatch(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ rettv->vval.v_number = assert_match_common(argvars, ASSERT_NOTMATCH);
+}
+
+/// "assert_report(msg)" function
+void f_assert_report(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ garray_T ga;
+
+ prepare_assert_error(&ga);
+ ga_concat(&ga, tv_get_string(&argvars[0]));
+ assert_error(&ga);
+ ga_clear(&ga);
+ rettv->vval.v_number = 1;
+}
+
+/// "assert_true(actual[, msg])" function
+void f_assert_true(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ rettv->vval.v_number = assert_bool(argvars, true);
+}
+
+/// "test_garbagecollect_now()" function
+void f_test_garbagecollect_now(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ // This is dangerous, any Lists and Dicts used internally may be freed
+ // while still in use.
+ garbage_collect(true);
+}
+
+/// "test_write_list_log()" function
+void f_test_write_list_log(typval_T *const argvars, typval_T *const rettv, FunPtr fptr)
+{
+ const char *const fname = tv_get_string_chk(&argvars[0]);
+ if (fname == NULL) {
+ return;
+ }
+ list_write_log(fname);
+}
diff --git a/src/nvim/testing.h b/src/nvim/testing.h
new file mode 100644
index 0000000000..1522ebc7b7
--- /dev/null
+++ b/src/nvim/testing.h
@@ -0,0 +1,10 @@
+#ifndef NVIM_TESTING_H
+#define NVIM_TESTING_H
+
+#include "nvim/eval/funcs.h"
+#include "nvim/eval/typval.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "testing.h.generated.h"
+#endif
+#endif // NVIM_TESTING_H
diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c
index 5fec41f9a5..61a59bcf06 100644
--- a/src/nvim/tui/input.c
+++ b/src/nvim/tui/input.c
@@ -1,11 +1,10 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
-
#include "nvim/api/private/helpers.h"
#include "nvim/api/vim.h"
#include "nvim/ascii.h"
-#include "nvim/aucmd.h"
+#include "nvim/autocmd.h"
#include "nvim/charset.h"
#include "nvim/ex_docmd.h"
#include "nvim/macros.h"
@@ -14,14 +13,101 @@
#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/tui/input.h"
+#include "nvim/tui/tui.h"
#include "nvim/vim.h"
#ifdef WIN32
# include "nvim/os/os_win_console.h"
#endif
#include "nvim/event/rstream.h"
+#include "nvim/msgpack_rpc/channel.h"
#define KEY_BUFFER_SIZE 0xfff
+static const struct kitty_key_map_entry {
+ KittyKey key;
+ const char *name;
+} kitty_key_map_entry[] = {
+ { KITTY_KEY_ESCAPE, "Esc" },
+ { KITTY_KEY_ENTER, "CR" },
+ { KITTY_KEY_TAB, "Tab" },
+ { KITTY_KEY_BACKSPACE, "BS" },
+ { KITTY_KEY_INSERT, "Insert" },
+ { KITTY_KEY_DELETE, "Del" },
+ { KITTY_KEY_LEFT, "Left" },
+ { KITTY_KEY_RIGHT, "Right" },
+ { KITTY_KEY_UP, "Up" },
+ { KITTY_KEY_DOWN, "Down" },
+ { KITTY_KEY_PAGE_UP, "PageUp" },
+ { KITTY_KEY_PAGE_DOWN, "PageDown" },
+ { KITTY_KEY_HOME, "Home" },
+ { KITTY_KEY_END, "End" },
+ { KITTY_KEY_F1, "F1" },
+ { KITTY_KEY_F2, "F2" },
+ { KITTY_KEY_F3, "F3" },
+ { KITTY_KEY_F4, "F4" },
+ { KITTY_KEY_F5, "F5" },
+ { KITTY_KEY_F6, "F6" },
+ { KITTY_KEY_F7, "F7" },
+ { KITTY_KEY_F8, "F8" },
+ { KITTY_KEY_F9, "F9" },
+ { KITTY_KEY_F10, "F10" },
+ { KITTY_KEY_F11, "F11" },
+ { KITTY_KEY_F12, "F12" },
+ { KITTY_KEY_F13, "F13" },
+ { KITTY_KEY_F14, "F14" },
+ { KITTY_KEY_F15, "F15" },
+ { KITTY_KEY_F16, "F16" },
+ { KITTY_KEY_F17, "F17" },
+ { KITTY_KEY_F18, "F18" },
+ { KITTY_KEY_F19, "F19" },
+ { KITTY_KEY_F20, "F20" },
+ { KITTY_KEY_F21, "F21" },
+ { KITTY_KEY_F22, "F22" },
+ { KITTY_KEY_F23, "F23" },
+ { KITTY_KEY_F24, "F24" },
+ { KITTY_KEY_F25, "F25" },
+ { KITTY_KEY_F26, "F26" },
+ { KITTY_KEY_F27, "F27" },
+ { KITTY_KEY_F28, "F28" },
+ { KITTY_KEY_F29, "F29" },
+ { KITTY_KEY_F30, "F30" },
+ { KITTY_KEY_F31, "F31" },
+ { KITTY_KEY_F32, "F32" },
+ { KITTY_KEY_F33, "F33" },
+ { KITTY_KEY_F34, "F34" },
+ { KITTY_KEY_F35, "F35" },
+ { KITTY_KEY_KP_0, "k0" },
+ { KITTY_KEY_KP_1, "k1" },
+ { KITTY_KEY_KP_2, "k2" },
+ { KITTY_KEY_KP_3, "k3" },
+ { KITTY_KEY_KP_4, "k4" },
+ { KITTY_KEY_KP_5, "k5" },
+ { KITTY_KEY_KP_6, "k6" },
+ { KITTY_KEY_KP_7, "k7" },
+ { KITTY_KEY_KP_8, "k8" },
+ { KITTY_KEY_KP_9, "k9" },
+ { KITTY_KEY_KP_DECIMAL, "kPoint" },
+ { KITTY_KEY_KP_DIVIDE, "kDivide" },
+ { KITTY_KEY_KP_MULTIPLY, "kMultiply" },
+ { KITTY_KEY_KP_SUBTRACT, "kMinus" },
+ { KITTY_KEY_KP_ADD, "kPlus" },
+ { KITTY_KEY_KP_ENTER, "kEnter" },
+ { KITTY_KEY_KP_EQUAL, "kEqual" },
+ { KITTY_KEY_KP_LEFT, "kLeft" },
+ { KITTY_KEY_KP_RIGHT, "kRight" },
+ { KITTY_KEY_KP_UP, "kUp" },
+ { KITTY_KEY_KP_DOWN, "kDown" },
+ { KITTY_KEY_KP_PAGE_UP, "kPageUp" },
+ { KITTY_KEY_KP_PAGE_DOWN, "kPageDown" },
+ { KITTY_KEY_KP_HOME, "kHome" },
+ { KITTY_KEY_KP_END, "kEnd" },
+ { KITTY_KEY_KP_INSERT, "kInsert" },
+ { KITTY_KEY_KP_DELETE, "kDel" },
+ { KITTY_KEY_KP_BEGIN, "kOrigin" },
+};
+
+static Map(KittyKey, cstr_t) kitty_key_map = MAP_INIT;
+
#ifndef UNIT_TESTING
typedef enum {
kIncomplete = -1,
@@ -40,6 +126,7 @@ void tinput_init(TermInput *input, Loop *loop)
input->paste = 0;
input->in_fd = STDIN_FILENO;
input->waiting_for_bg_response = 0;
+ input->extkeys_type = kExtkeysNone;
// The main thread is waiting for the UI thread to call CONTINUE, so it can
// safely access global variables.
input->ttimeout = (bool)p_ttimeout;
@@ -48,6 +135,11 @@ void tinput_init(TermInput *input, Loop *loop)
uv_mutex_init(&input->key_buffer_mutex);
uv_cond_init(&input->key_buffer_cond);
+ for (size_t i = 0; i < ARRAY_SIZE(kitty_key_map_entry); i++) {
+ map_put(KittyKey, cstr_t)(&kitty_key_map, kitty_key_map_entry[i].key,
+ kitty_key_map_entry[i].name);
+ }
+
// If stdin is not a pty, switch to stderr. For cases like:
// echo q | nvim -es
// ls *.md | xargs nvim
@@ -87,6 +179,7 @@ void tinput_init(TermInput *input, Loop *loop)
void tinput_destroy(TermInput *input)
{
+ map_destroy(KittyKey, cstr_t)(&kitty_key_map);
rbuffer_free(input->key_buffer);
uv_mutex_destroy(&input->key_buffer_mutex);
uv_cond_destroy(&input->key_buffer_cond);
@@ -114,26 +207,41 @@ static void tinput_done_event(void **argv)
static void tinput_wait_enqueue(void **argv)
{
TermInput *input = argv[0];
- if (rbuffer_size(input->key_buffer) == 0 && input->paste == 3) {
- const String keys = { .data = "", .size = 0 };
- String copy = copy_string(keys);
- multiqueue_put(main_loop.events, tinput_paste_event, 3,
- copy.data, copy.size, (intptr_t)input->paste);
- }
- RBUFFER_UNTIL_EMPTY(input->key_buffer, buf, len) {
- const String keys = { .data = buf, .size = len };
- if (input->paste) {
- String copy = copy_string(keys);
+ if (input->paste) { // produce exactly one paste event
+ const size_t len = rbuffer_size(input->key_buffer);
+ String keys = { .data = xmallocz(len), .size = len };
+ rbuffer_read(input->key_buffer, keys.data, len);
+ if (ui_client_channel_id) {
+ Array args = ARRAY_DICT_INIT;
+ ADD(args, STRING_OBJ(keys)); // 'data'
+ ADD(args, BOOLEAN_OBJ(true)); // 'crlf'
+ ADD(args, INTEGER_OBJ(input->paste)); // 'phase'
+ rpc_send_event(ui_client_channel_id, "nvim_paste", args);
+ } else {
multiqueue_put(main_loop.events, tinput_paste_event, 3,
- copy.data, copy.size, (intptr_t)input->paste);
- if (input->paste == 1) {
- // Paste phase: "continue"
- input->paste = 2;
+ keys.data, keys.size, (intptr_t)input->paste);
+ }
+ if (input->paste == 1) {
+ // Paste phase: "continue"
+ input->paste = 2;
+ }
+ rbuffer_reset(input->key_buffer);
+ } else { // enqueue input for the main thread or Nvim server
+ RBUFFER_UNTIL_EMPTY(input->key_buffer, buf, len) {
+ const String keys = { .data = buf, .size = len };
+ size_t consumed;
+ if (ui_client_channel_id) {
+ Array args = ARRAY_DICT_INIT;
+ Error err = ERROR_INIT;
+ ADD(args, STRING_OBJ(copy_string(keys)));
+ // TODO(bfredl): could be non-blocking now with paste?
+ ArenaMem res_mem = NULL;
+ Object result = rpc_send_call(ui_client_channel_id, "nvim_input", args, &res_mem, &err);
+ consumed = result.type == kObjectTypeInteger ? (size_t)result.data.integer : 0;
+ arena_mem_free(res_mem, NULL);
+ } else {
+ consumed = input_enqueue(keys);
}
- rbuffer_consumed(input->key_buffer, len);
- rbuffer_reset(input->key_buffer);
- } else {
- const size_t consumed = input_enqueue(keys);
if (consumed) {
rbuffer_consumed(input->key_buffer, consumed);
}
@@ -189,19 +297,46 @@ static void tinput_enqueue(TermInput *input, char *buf, size_t size)
rbuffer_write(input->key_buffer, buf, size);
}
+static void handle_kitty_key_protocol(TermInput *input, TermKeyKey *key)
+{
+ const char *name = map_get(KittyKey, cstr_t)(&kitty_key_map, (KittyKey)key->code.codepoint);
+ if (name) {
+ char buf[64];
+ size_t len = 0;
+ buf[len++] = '<';
+ if (key->modifiers & TERMKEY_KEYMOD_SHIFT) {
+ len += (size_t)snprintf(buf + len, sizeof(buf) - len, "S-");
+ }
+ if (key->modifiers & TERMKEY_KEYMOD_ALT) {
+ len += (size_t)snprintf(buf + len, sizeof(buf) - len, "A-");
+ }
+ if (key->modifiers & TERMKEY_KEYMOD_CTRL) {
+ len += (size_t)snprintf(buf + len, sizeof(buf) - len, "C-");
+ }
+ len += (size_t)snprintf(buf + len, sizeof(buf) - len, "%s>", name);
+ tinput_enqueue(input, buf, len);
+ }
+}
+
static void forward_simple_utf8(TermInput *input, TermKeyKey *key)
{
size_t len = 0;
char buf[64];
char *ptr = key->utf8;
- while (*ptr) {
- if (*ptr == '<') {
- len += (size_t)snprintf(buf + len, sizeof(buf) - len, "<lt>");
- } else {
- buf[len++] = *ptr;
+ if (key->code.codepoint >= 0xE000 && key->code.codepoint <= 0xF8FF
+ && map_has(KittyKey, cstr_t)(&kitty_key_map, (KittyKey)key->code.codepoint)) {
+ handle_kitty_key_protocol(input, key);
+ return;
+ } else {
+ while (*ptr) {
+ if (*ptr == '<') {
+ len += (size_t)snprintf(buf + len, sizeof(buf) - len, "<lt>");
+ } else {
+ buf[len++] = *ptr;
+ }
+ ptr++;
}
- ptr++;
}
tinput_enqueue(input, buf, len);
@@ -219,19 +354,26 @@ static void forward_modified_utf8(TermInput *input, TermKeyKey *key)
len = termkey_strfkey(input->tk, buf, sizeof(buf), key, TERMKEY_FORMAT_VIM);
} else {
assert(key->modifiers);
- // Termkey doesn't include the S- modifier for ASCII characters (e.g.,
- // ctrl-shift-l is <C-L> instead of <C-S-L>. Vim, on the other hand,
- // treats <C-L> and <C-l> the same, requiring the S- modifier.
- len = termkey_strfkey(input->tk, buf, sizeof(buf), key, TERMKEY_FORMAT_VIM);
- if ((key->modifiers & TERMKEY_KEYMOD_CTRL)
- && !(key->modifiers & TERMKEY_KEYMOD_SHIFT)
- && ASCII_ISUPPER(key->code.codepoint)) {
- assert(len <= 62);
- // Make remove for the S-
- memmove(buf + 3, buf + 1, len - 1);
- buf[1] = 'S';
- buf[2] = '-';
- len += 2;
+ if (key->code.codepoint >= 0xE000 && key->code.codepoint <= 0xF8FF
+ && map_has(KittyKey, cstr_t)(&kitty_key_map,
+ (KittyKey)key->code.codepoint)) {
+ handle_kitty_key_protocol(input, key);
+ return;
+ } else {
+ // Termkey doesn't include the S- modifier for ASCII characters (e.g.,
+ // ctrl-shift-l is <C-L> instead of <C-S-L>. Vim, on the other hand,
+ // treats <C-L> and <C-l> the same, requiring the S- modifier.
+ len = termkey_strfkey(input->tk, buf, sizeof(buf), key, TERMKEY_FORMAT_VIM);
+ if ((key->modifiers & TERMKEY_KEYMOD_CTRL)
+ && !(key->modifiers & TERMKEY_KEYMOD_SHIFT)
+ && ASCII_ISUPPER(key->code.codepoint)) {
+ assert(len <= 62);
+ // Make room for the S-
+ memmove(buf + 3, buf + 1, len - 1);
+ buf[1] = 'S';
+ buf[2] = '-';
+ len += 2;
+ }
}
}
@@ -315,8 +457,6 @@ static TermKeyResult tk_getkey(TermKey *tk, TermKeyKey *key, bool force)
return force ? termkey_getkey_force(tk, key) : termkey_getkey(tk, key);
}
-static void tinput_timer_cb(TimeWatcher *watcher, void *data);
-
static void tk_getkeys(TermInput *input, bool force)
{
TermKeyKey key;
@@ -331,6 +471,39 @@ static void tk_getkeys(TermInput *input, bool force)
forward_modified_utf8(input, &key);
} else if (key.type == TERMKEY_TYPE_MOUSE) {
forward_mouse_event(input, &key);
+ } else if (key.type == TERMKEY_TYPE_UNKNOWN_CSI) {
+ // There is no specified limit on the number of parameters a CSI sequence can contain, so just
+ // allocate enough space for a large upper bound
+ long args[16];
+ size_t nargs = 16;
+ unsigned long cmd;
+ if (termkey_interpret_csi(input->tk, &key, args, &nargs, &cmd) == TERMKEY_RES_KEY) {
+ uint8_t intermediate = (cmd >> 16) & 0xFF;
+ uint8_t initial = (cmd >> 8) & 0xFF;
+ uint8_t command = cmd & 0xFF;
+
+ // Currently unused
+ (void)intermediate;
+
+ if (input->waiting_for_csiu_response > 0) {
+ if (initial == '?' && command == 'u') {
+ // The first (and only) argument contains the current progressive
+ // enhancement flags. Only enable CSI u mode if the first bit
+ // (disambiguate escape codes) is not already set
+ if (nargs > 0 && (args[0] & 0x1) == 0) {
+ input->extkeys_type = kExtkeysCSIu;
+ } else {
+ input->extkeys_type = kExtkeysNone;
+ }
+ } else if (initial == '?' && command == 'c') {
+ // Received Primary Device Attributes response
+ input->waiting_for_csiu_response = 0;
+ tui_enable_extkeys(input->tui_data);
+ } else {
+ input->waiting_for_csiu_response--;
+ }
+ }
+ }
}
}
@@ -379,7 +552,7 @@ static bool handle_focus_event(TermInput *input)
bool focus_gained = *rbuffer_get(input->read_stream.buffer, 2) == 'I';
// Advance past the sequence
rbuffer_consumed(input->read_stream.buffer, 3);
- aucmd_schedule_focusgained(focus_gained);
+ autocmd_schedule_focusgained(focus_gained);
return true;
}
return false;
@@ -426,37 +599,10 @@ static HandleState handle_bracketed_paste(TermInput *input)
return kNotApplicable;
}
-// ESC NUL => <Esc>
-static bool handle_forced_escape(TermInput *input)
-{
- if (rbuffer_size(input->read_stream.buffer) > 1
- && !rbuffer_cmp(input->read_stream.buffer, "\x1b\x00", 2)) {
- // skip the ESC and NUL and push one <esc> to the input buffer
- size_t rcnt;
- termkey_push_bytes(input->tk, rbuffer_read_ptr(input->read_stream.buffer,
- &rcnt), 1);
- rbuffer_consumed(input->read_stream.buffer, 2);
- tk_getkeys(input, true);
- return true;
- }
- return false;
-}
-
static void set_bg_deferred(void **argv)
{
char *bgvalue = argv[0];
- if (!option_was_set("bg") && !strequal((char *)p_bg, bgvalue)) {
- // Value differs, apply it.
- if (starting) {
- // Wait until after startup, so OptionSet is triggered.
- do_cmdline_cmd((bgvalue[0] == 'l')
- ? "autocmd VimEnter * ++once ++nested set bg=light"
- : "autocmd VimEnter * ++once ++nested set bg=dark");
- } else {
- set_option_value("bg", 0L, bgvalue, 0);
- reset_option_was_set("bg");
- }
- }
+ set_tty_background(bgvalue);
}
// During startup, tui.c requests the background color (see `ext.get_bg`).
@@ -570,7 +716,6 @@ static void handle_raw_buffer(TermInput *input, bool force)
if (!force
&& (handle_focus_event(input)
|| (is_paste = handle_bracketed_paste(input)) != kNotApplicable
- || handle_forced_escape(input)
|| (is_bc = handle_background_color(input)) != kNotApplicable)) {
if (is_paste == kIncomplete || is_bc == kIncomplete) {
// Wait for the next input, leaving it in the raw buffer due to an
diff --git a/src/nvim/tui/input.h b/src/nvim/tui/input.h
index 2a8ea32a88..51df57938c 100644
--- a/src/nvim/tui/input.h
+++ b/src/nvim/tui/input.h
@@ -6,6 +6,14 @@
#include "nvim/event/stream.h"
#include "nvim/event/time.h"
+#include "nvim/tui/input_defs.h"
+#include "nvim/tui/tui.h"
+
+typedef enum {
+ kExtkeysNone,
+ kExtkeysCSIu,
+ kExtkeysXterm,
+} ExtkeysType;
typedef struct term_input {
int in_fd;
@@ -14,6 +22,8 @@ typedef struct term_input {
bool waiting;
bool ttimeout;
int8_t waiting_for_bg_response;
+ int8_t waiting_for_csiu_response;
+ ExtkeysType extkeys_type;
long ttimeoutlen;
TermKey *tk;
#if TERMKEY_VERSION_MAJOR > 0 || TERMKEY_VERSION_MINOR > 18
@@ -25,6 +35,7 @@ typedef struct term_input {
RBuffer *key_buffer;
uv_mutex_t key_buffer_mutex;
uv_cond_t key_buffer_cond;
+ TUIData *tui_data;
} TermInput;
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/tui/input_defs.h b/src/nvim/tui/input_defs.h
new file mode 100644
index 0000000000..846cf45350
--- /dev/null
+++ b/src/nvim/tui/input_defs.h
@@ -0,0 +1,118 @@
+#ifndef NVIM_TUI_INPUT_DEFS_H
+#define NVIM_TUI_INPUT_DEFS_H
+
+typedef enum {
+ KITTY_KEY_ESCAPE = 57344,
+ KITTY_KEY_ENTER = 57345,
+ KITTY_KEY_TAB = 57346,
+ KITTY_KEY_BACKSPACE = 57347,
+ KITTY_KEY_INSERT = 57348,
+ KITTY_KEY_DELETE = 57349,
+ KITTY_KEY_LEFT = 57350,
+ KITTY_KEY_RIGHT = 57351,
+ KITTY_KEY_UP = 57352,
+ KITTY_KEY_DOWN = 57353,
+ KITTY_KEY_PAGE_UP = 57354,
+ KITTY_KEY_PAGE_DOWN = 57355,
+ KITTY_KEY_HOME = 57356,
+ KITTY_KEY_END = 57357,
+ KITTY_KEY_CAPS_LOCK = 57358,
+ KITTY_KEY_SCROLL_LOCK = 57359,
+ KITTY_KEY_NUM_LOCK = 57360,
+ KITTY_KEY_PRINT_SCREEN = 57361,
+ KITTY_KEY_PAUSE = 57362,
+ KITTY_KEY_MENU = 57363,
+ KITTY_KEY_F1 = 57364,
+ KITTY_KEY_F2 = 57365,
+ KITTY_KEY_F3 = 57366,
+ KITTY_KEY_F4 = 57367,
+ KITTY_KEY_F5 = 57368,
+ KITTY_KEY_F6 = 57369,
+ KITTY_KEY_F7 = 57370,
+ KITTY_KEY_F8 = 57371,
+ KITTY_KEY_F9 = 57372,
+ KITTY_KEY_F10 = 57373,
+ KITTY_KEY_F11 = 57374,
+ KITTY_KEY_F12 = 57375,
+ KITTY_KEY_F13 = 57376,
+ KITTY_KEY_F14 = 57377,
+ KITTY_KEY_F15 = 57378,
+ KITTY_KEY_F16 = 57379,
+ KITTY_KEY_F17 = 57380,
+ KITTY_KEY_F18 = 57381,
+ KITTY_KEY_F19 = 57382,
+ KITTY_KEY_F20 = 57383,
+ KITTY_KEY_F21 = 57384,
+ KITTY_KEY_F22 = 57385,
+ KITTY_KEY_F23 = 57386,
+ KITTY_KEY_F24 = 57387,
+ KITTY_KEY_F25 = 57388,
+ KITTY_KEY_F26 = 57389,
+ KITTY_KEY_F27 = 57390,
+ KITTY_KEY_F28 = 57391,
+ KITTY_KEY_F29 = 57392,
+ KITTY_KEY_F30 = 57393,
+ KITTY_KEY_F31 = 57394,
+ KITTY_KEY_F32 = 57395,
+ KITTY_KEY_F33 = 57396,
+ KITTY_KEY_F34 = 57397,
+ KITTY_KEY_F35 = 57398,
+ KITTY_KEY_KP_0 = 57399,
+ KITTY_KEY_KP_1 = 57400,
+ KITTY_KEY_KP_2 = 57401,
+ KITTY_KEY_KP_3 = 57402,
+ KITTY_KEY_KP_4 = 57403,
+ KITTY_KEY_KP_5 = 57404,
+ KITTY_KEY_KP_6 = 57405,
+ KITTY_KEY_KP_7 = 57406,
+ KITTY_KEY_KP_8 = 57407,
+ KITTY_KEY_KP_9 = 57408,
+ KITTY_KEY_KP_DECIMAL = 57409,
+ KITTY_KEY_KP_DIVIDE = 57410,
+ KITTY_KEY_KP_MULTIPLY = 57411,
+ KITTY_KEY_KP_SUBTRACT = 57412,
+ KITTY_KEY_KP_ADD = 57413,
+ KITTY_KEY_KP_ENTER = 57414,
+ KITTY_KEY_KP_EQUAL = 57415,
+ KITTY_KEY_KP_SEPARATOR = 57416,
+ KITTY_KEY_KP_LEFT = 57417,
+ KITTY_KEY_KP_RIGHT = 57418,
+ KITTY_KEY_KP_UP = 57419,
+ KITTY_KEY_KP_DOWN = 57420,
+ KITTY_KEY_KP_PAGE_UP = 57421,
+ KITTY_KEY_KP_PAGE_DOWN = 57422,
+ KITTY_KEY_KP_HOME = 57423,
+ KITTY_KEY_KP_END = 57424,
+ KITTY_KEY_KP_INSERT = 57425,
+ KITTY_KEY_KP_DELETE = 57426,
+ KITTY_KEY_KP_BEGIN = 57427,
+ KITTY_KEY_MEDIA_PLAY = 57428,
+ KITTY_KEY_MEDIA_PAUSE = 57429,
+ KITTY_KEY_MEDIA_PLAY_PAUSE = 57430,
+ KITTY_KEY_MEDIA_REVERSE = 57431,
+ KITTY_KEY_MEDIA_STOP = 57432,
+ KITTY_KEY_MEDIA_FAST_FORWARD = 57433,
+ KITTY_KEY_MEDIA_REWIND = 57434,
+ KITTY_KEY_MEDIA_TRACK_NEXT = 57435,
+ KITTY_KEY_MEDIA_TRACK_PREVIOUS = 57436,
+ KITTY_KEY_MEDIA_RECORD = 57437,
+ KITTY_KEY_LOWER_VOLUME = 57438,
+ KITTY_KEY_RAISE_VOLUME = 57439,
+ KITTY_KEY_MUTE_VOLUME = 57440,
+ KITTY_KEY_LEFT_SHIFT = 57441,
+ KITTY_KEY_LEFT_CONTROL = 57442,
+ KITTY_KEY_LEFT_ALT = 57443,
+ KITTY_KEY_LEFT_SUPER = 57444,
+ KITTY_KEY_LEFT_HYPER = 57445,
+ KITTY_KEY_LEFT_META = 57446,
+ KITTY_KEY_RIGHT_SHIFT = 57447,
+ KITTY_KEY_RIGHT_CONTROL = 57448,
+ KITTY_KEY_RIGHT_ALT = 57449,
+ KITTY_KEY_RIGHT_SUPER = 57450,
+ KITTY_KEY_RIGHT_HYPER = 57451,
+ KITTY_KEY_RIGHT_META = 57452,
+ KITTY_KEY_ISO_LEVEL3_SHIFT = 57453,
+ KITTY_KEY_ISO_LEVEL5_SHIFT = 57454,
+} KittyKey;
+
+#endif // NVIM_TUI_INPUT_DEFS_H
diff --git a/src/nvim/tui/terminfo_defs.h b/src/nvim/tui/terminfo_defs.h
index 40261058dc..0bc4972dd3 100644
--- a/src/nvim/tui/terminfo_defs.h
+++ b/src/nvim/tui/terminfo_defs.h
@@ -4,7 +4,7 @@
// uncrustify:off
//
-// Generated by scripts/update_terminfo.sh and ncurses 6.1.20180127
+// Generated by scripts/update_terminfo.sh and ncurses 6.3.20211021
//
#ifndef NVIM_TUI_TERMINFO_DEFS_H
@@ -96,7 +96,7 @@
// user8=\E[?%[;0123456789]c,
// user9=\E[c,
static const int8_t ansi_terminfo[] = {
- 26,1,40,0,38,0,16,0,125,1,68,2,97,110,115,105,124,97,110,115,105,47,112,99,45,116,101,114,109,32,99,111,109,112,97,116,105,98,108,101,32,119,105,116,104,32,99,111,108,111,114,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,80,0,8,0,24,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,8,0,64,0,3,0,0,0,4,0,6,0,-1,-1,8,0,13,0,20,0,24,0,28,0,-1,-1,39,0,56,0,60,0,-1,-1,64,0,-1,-1,-1,-1,68,0,-1,-1,72,0,-1,-1,76,0,80,0,-1,-1,-1,-1,84,0,90,0,95,0,-1,-1,-1,-1,-1,-1,-1,-1,100,0,-1,-1,105,0,110,0,115,0,120,0,-127,0,-121,0,-1,-1,-1,-1,-1,-1,-113,0,-109,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-105,0,-1,-1,-101,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-99,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-95,0,-91,0,-1,-1,-87,0,-1,-1,-1,-1,-1,-1,-83,0,-1,-1,-1,-1,-1,-1,-79,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-75,0,-1,-1,-70,0,-61,0,-52,0,-43,0,-34,0,-25,0,-16,0,-7,0,2,1,11,1,-1,-1,-1,-1,-1,-1,-1,-1,20,1,25,1,30,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,50,1,-1,-1,61,1,-1,-1,63,1,-107,1,-1,-1,-104,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-100,1,-1,-1,-37,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-33,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-28,1,-17,1,-12,1,7,2,11,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,20,2,30,2,-1,-1,-1,-1,-1,-1,40,2,44,2,48,2,52,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,56,2,62,2,27,91,90,0,7,0,13,0,27,91,51,103,0,27,91,72,27,91,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,27,91,66,0,27,91,72,0,27,91,68,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,27,91,49,49,109,0,27,91,53,109,0,27,91,49,109,0,27,91,56,109,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,27,91,49,48,109,0,27,91,48,59,49,48,109,0,27,91,109,0,27,91,109,0,27,91,76,0,8,0,27,91,66,0,27,91,72,0,27,91,76,0,27,91,68,0,27,91,67,0,27,91,65,0,13,27,91,83,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,27,91,52,105,0,27,91,53,105,0,37,112,49,37,99,27,91,37,112,50,37,123,49,125,37,45,37,100,98,0,27,91,37,105,37,112,49,37,100,100,0,10,0,27,91,48,59,49,48,37,63,37,112,49,37,116,59,55,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,51,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,37,63,37,112,54,37,116,59,49,37,59,37,63,37,112,55,37,116,59,56,37,59,37,63,37,112,57,37,116,59,49,49,37,59,109,0,27,72,0,27,91,73,0,43,16,44,17,45,24,46,25,48,-37,96,4,97,-79,102,-8,103,-15,104,-80,106,-39,107,-65,108,-38,109,-64,110,-59,111,126,112,-60,113,-60,114,-60,115,95,116,-61,117,-76,118,-63,119,-62,120,-77,121,-13,122,-14,123,-29,124,-40,125,-100,126,-2,0,27,91,90,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,37,91,59,48,49,50,51,52,53,54,55,56,57,93,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,91,51,37,112,49,37,100,109,0,27,91,52,37,112,49,37,100,109,0,27,40,66,0,27,41,66,0,27,42,66,0,27,43,66,0,27,91,49,49,109,0,27,91,49,48,109,0,1,0,0,0,0,0,1,0,3,0,1,0,0,0,65,88,0 // NOLINT
+ 26,1,40,0,38,0,16,0,125,1,68,2,97,110,115,105,124,97,110,115,105,47,112,99,45,116,101,114,109,32,99,111,109,112,97,116,105,98,108,101,32,119,105,116,104,32,99,111,108,111,114,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,80,0,8,0,24,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,8,0,64,0,3,0,0,0,4,0,6,0,-1,-1,8,0,13,0,20,0,24,0,28,0,-1,-1,39,0,56,0,60,0,-1,-1,64,0,-1,-1,-1,-1,68,0,-1,-1,72,0,-1,-1,76,0,80,0,-1,-1,-1,-1,84,0,90,0,95,0,-1,-1,-1,-1,-1,-1,-1,-1,100,0,-1,-1,105,0,110,0,115,0,120,0,-127,0,-121,0,-1,-1,-1,-1,-1,-1,-113,0,-109,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-105,0,-1,-1,-101,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-99,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-95,0,-91,0,-1,-1,-87,0,-1,-1,-1,-1,-1,-1,-83,0,-1,-1,-1,-1,-1,-1,-79,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-75,0,-1,-1,-70,0,-61,0,-52,0,-43,0,-34,0,-25,0,-16,0,-7,0,2,1,11,1,-1,-1,-1,-1,-1,-1,-1,-1,20,1,25,1,30,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,50,1,-1,-1,61,1,-1,-1,63,1,-107,1,-1,-1,-104,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-100,1,-1,-1,-37,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-33,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-28,1,-17,1,-12,1,7,2,11,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,20,2,30,2,-1,-1,-1,-1,-1,-1,40,2,44,2,48,2,52,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,56,2,62,2,27,91,90,0,7,0,13,0,27,91,51,103,0,27,91,72,27,91,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,27,91,66,0,27,91,72,0,27,91,68,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,27,91,49,49,109,0,27,91,53,109,0,27,91,49,109,0,27,91,56,109,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,27,91,49,48,109,0,27,91,48,59,49,48,109,0,27,91,109,0,27,91,109,0,27,91,76,0,8,0,27,91,66,0,27,91,72,0,27,91,76,0,27,91,68,0,27,91,67,0,27,91,65,0,13,27,91,83,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,27,91,52,105,0,27,91,53,105,0,37,112,49,37,99,27,91,37,112,50,37,123,49,125,37,45,37,100,98,0,27,91,37,105,37,112,49,37,100,100,0,10,0,27,91,48,59,49,48,37,63,37,112,49,37,116,59,55,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,51,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,37,63,37,112,54,37,116,59,49,37,59,37,63,37,112,55,37,116,59,56,37,59,37,63,37,112,57,37,116,59,49,49,37,59,109,0,27,72,0,27,91,73,0,43,16,44,17,45,24,46,25,48,-37,96,4,97,-79,102,-8,103,-15,104,-80,106,-39,107,-65,108,-38,109,-64,110,-59,111,126,112,-60,113,-60,114,-60,115,95,116,-61,117,-76,118,-63,119,-62,120,-77,121,-13,122,-14,123,-29,124,-40,125,-100,126,-2,0,27,91,90,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,37,91,59,48,49,50,51,52,53,54,55,56,57,93,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,91,51,37,112,49,37,100,109,0,27,91,52,37,112,49,37,100,109,0,27,40,66,0,27,41,66,0,27,42,66,0,27,43,66,0,27,91,49,49,109,0,27,91,49,48,109,0,1,0,0,0,0,0,1,0,3,0,1,0,0,0,65,88,0
};
// conemu|ANIS X3.64 and Xterm 256 colors for ConEmu with libuv,
@@ -118,6 +118,7 @@ static const int8_t ansi_terminfo[] = {
// carriage_return=\r,
// change_scroll_region=\E[%i%p1%d;%p2%dr,
// clear_all_tabs@,
+// clear_margins=\E[?69l,
// clear_screen=\E[H\E[2J,
// clr_bol=\E[1K,
// clr_eol=\E[K,
@@ -159,9 +160,13 @@ static const int8_t ansi_terminfo[] = {
// init_2string@,
// initialize_color@,
// insert_line=\E[L,
+// key_a1=\EOw,
+// key_a3=\EOy,
// key_b2=\E[G,
// key_backspace=^H,
// key_btab=\E[Z,
+// key_c1=\EOq,
+// key_c3=\EOs,
// key_dc=\E[3~,
// key_down=\E[B,
// key_end=\E[4~,
@@ -253,6 +258,7 @@ static const int8_t ansi_terminfo[] = {
// memory_unlock@,
// meta_off@,
// meta_on@,
+// newline=\EE,
// orig_colors@,
// orig_pair=\E[39;49m,
// parm_dch=\E[%p1%dP,
@@ -279,6 +285,7 @@ static const int8_t ansi_terminfo[] = {
// set_a_background=\E[48;5;%p1%dm,
// set_a_foreground=\E[38;5;%p1%dm,
// set_attributes=\E[0%?%p1%p3%|%t;7%;%?%p2%t;4%;%?%p6%t;1%;m,
+// set_lr_margin@,
// set_tab@,
// tab=^I,
// user6@,
@@ -286,7 +293,7 @@ static const int8_t ansi_terminfo[] = {
// user8@,
// user9@,
static const int8_t conemu_terminfo[] = {
- 30,2,61,0,38,0,15,0,-99,1,31,3,99,111,110,101,109,117,124,65,78,73,83,32,88,51,46,54,52,32,97,110,100,32,88,116,101,114,109,32,50,53,54,32,99,111,108,111,114,115,32,102,111,114,32,67,111,110,69,109,117,32,119,105,116,104,32,108,105,98,117,118,0,0,1,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,-2,-1,0,0,2,0,4,0,-2,-1,21,0,29,0,33,0,37,0,-1,-1,48,0,65,0,69,0,73,0,80,0,-1,-1,82,0,89,0,-1,-1,93,0,-2,-1,97,0,101,0,-1,-1,-1,-1,-2,-1,-2,-1,105,0,110,0,-1,-1,-2,-1,-2,-1,-2,-1,-1,-1,119,0,124,0,-127,0,-122,0,-2,-1,-113,0,-108,0,-1,-1,-2,-1,-99,0,-93,0,-2,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-87,0,-1,-1,-83,0,-1,-1,-1,-1,-1,-1,-81,0,-1,-1,-76,0,-1,-1,-1,-1,-1,-1,-1,-1,-72,0,-67,0,-61,0,-56,0,-51,0,-46,0,-41,0,-35,0,-29,0,-23,0,-17,0,-12,0,-1,-1,-7,0,-1,-1,-3,0,2,1,7,1,11,1,18,1,-1,-1,25,1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,-1,-1,29,1,38,1,47,1,56,1,65,1,74,1,83,1,92,1,101,1,110,1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,119,1,-2,-1,-2,-1,-1,-1,-1,-1,-117,1,-114,1,-103,1,-100,1,-98,1,-95,1,-2,-1,-1,-1,-52,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-50,1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-46,1,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-42,1,-37,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-33,1,-1,-1,-1,-1,-26,1,-1,-1,-1,-1,-1,-1,-1,-1,-19,1,-12,1,-5,1,-1,-1,-1,-1,2,2,-1,-1,9,2,-1,-1,-1,-1,-1,-1,16,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,23,2,29,2,35,2,41,2,47,2,53,2,59,2,65,2,71,2,77,2,83,2,89,2,95,2,101,2,107,2,113,2,119,2,125,2,-125,2,-119,2,-113,2,-107,2,-101,2,-95,2,-89,2,-83,2,-77,2,-71,2,-65,2,-59,2,-52,2,-46,2,-40,2,-34,2,-28,2,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-22,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,-17,2,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-8,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-3,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,3,3,17,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,27,91,66,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,27,91,49,109,0,27,91,63,49,48,52,57,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,27,91,48,109,0,27,91,63,49,48,52,57,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,76,0,8,0,27,91,51,126,0,27,91,66,0,27,91,91,65,0,27,91,50,49,126,0,27,91,91,66,0,27,91,91,67,0,27,91,91,68,0,27,91,91,69,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,91,68,0,27,91,54,126,0,27,91,53,126,0,27,91,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,91,65,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,37,112,49,37,99,27,91,37,112,50,37,123,49,125,37,45,37,100,98,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,54,37,116,59,49,37,59,109,0,9,0,27,91,71,0,27,91,90,0,27,91,52,126,0,27,79,77,0,27,91,51,59,50,126,0,27,91,52,59,50,126,0,27,91,49,59,50,126,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,50,51,36,0,27,91,50,52,36,0,27,91,49,49,94,0,27,91,49,50,94,0,27,91,49,51,94,0,27,91,49,52,94,0,27,91,49,53,94,0,27,91,49,55,94,0,27,91,49,56,94,0,27,91,49,57,94,0,27,91,50,48,94,0,27,91,50,49,94,0,27,91,50,51,94,0,27,91,50,52,94,0,27,91,50,53,94,0,27,91,50,54,94,0,27,91,50,56,94,0,27,91,50,57,94,0,27,91,51,49,94,0,27,91,49,59,54,83,0,27,91,51,50,94,0,27,91,51,51,94,0,27,91,51,52,94,0,27,91,50,51,64,0,27,91,50,52,64,0,27,91,49,75,0,27,91,51,57,59,52,57,109,0,27,91,51,109,0,27,91,50,51,109,0,27,91,51,56,59,53,59,37,112,49,37,100,109,0,27,91,52,56,59,53,59,37,112,49,37,100,109,0,0,3,0,1,0,74,0,-104,0,-95,1,1,0,1,0,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,0,0,-2,-1,-1,-1,5,0,-1,-1,11,0,-1,-1,-2,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,21,0,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,30,0,33,0,39,0,42,0,45,0,48,0,54,0,60,0,65,0,70,0,75,0,80,0,85,0,89,0,94,0,99,0,104,0,109,0,114,0,120,0,126,0,-124,0,-118,0,-112,0,-106,0,-100,0,-94,0,-88,0,-82,0,-76,0,-70,0,-65,0,-60,0,-55,0,-50,0,-45,0,-39,0,-33,0,-27,0,-21,0,-15,0,-9,0,-3,0,3,1,9,1,15,1,21,1,27,1,33,1,39,1,45,1,51,1,57,1,63,1,69,1,75,1,79,1,84,1,89,1,94,1,99,1,104,1,108,1,112,1,116,1,120,1,125,1,-126,1,27,91,51,74,0,27,91,50,32,113,0,27,91,37,112,49,37,100,32,113,0,27,91,49,59,50,65,0,65,88,0,71,48,0,88,84,0,85,56,0,67,114,0,67,115,0,69,48,0,69,51,0,77,115,0,83,48,0,83,101,0,83,109,117,108,120,0,83,115,0,84,83,0,88,77,0,103,114,98,111,109,0,103,115,98,111,109,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,69,78,68,56,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,72,79,77,56,0,107,73,67,51,0,107,73,67,52,0,107,73,67,53,0,107,73,67,54,0,107,73,67,55,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,78,88,84,51,0,107,78,88,84,52,0,107,78,88,84,53,0,107,78,88,84,54,0,107,78,88,84,55,0,107,80,82,86,51,0,107,80,82,86,52,0,107,80,82,86,53,0,107,80,82,86,54,0,107,80,82,86,55,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,107,85,80,55,0,107,97,50,0,107,98,49,0,107,98,51,0,107,99,50,0,114,109,120,120,0,115,109,120,120,0,120,109,0 // NOLINT
+ 30,2,61,0,38,0,15,0,-99,1,57,3,99,111,110,101,109,117,124,65,78,73,83,32,88,51,46,54,52,32,97,110,100,32,88,116,101,114,109,32,50,53,54,32,99,111,108,111,114,115,32,102,111,114,32,67,111,110,69,109,117,32,119,105,116,104,32,108,105,98,117,118,0,0,1,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,-2,-1,0,0,2,0,4,0,-2,-1,21,0,29,0,33,0,37,0,-1,-1,48,0,65,0,69,0,73,0,80,0,-1,-1,82,0,89,0,-1,-1,93,0,-2,-1,97,0,101,0,-1,-1,-1,-1,-2,-1,-2,-1,105,0,110,0,-1,-1,-2,-1,-2,-1,-2,-1,-1,-1,119,0,124,0,-127,0,-122,0,-2,-1,-113,0,-108,0,-1,-1,-2,-1,-99,0,-93,0,-2,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-87,0,-1,-1,-83,0,-1,-1,-1,-1,-1,-1,-81,0,-1,-1,-76,0,-1,-1,-1,-1,-1,-1,-1,-1,-72,0,-67,0,-61,0,-56,0,-51,0,-46,0,-41,0,-35,0,-29,0,-23,0,-17,0,-12,0,-1,-1,-7,0,-1,-1,-3,0,2,1,7,1,11,1,18,1,-1,-1,25,1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,29,1,-1,-1,32,1,41,1,50,1,59,1,68,1,77,1,86,1,95,1,104,1,113,1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,122,1,-2,-1,-2,-1,-1,-1,-1,-1,-114,1,-111,1,-100,1,-97,1,-95,1,-92,1,-2,-1,-1,-1,-49,1,-1,-1,-1,-1,-1,-1,-1,-1,-47,1,-43,1,-39,1,-35,1,-31,1,-1,-1,-1,-1,-2,-1,-1,-1,-27,1,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-23,1,-18,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-14,1,-1,-1,-1,-1,-7,1,-1,-1,-1,-1,-1,-1,-1,-1,0,2,7,2,14,2,-1,-1,-1,-1,21,2,-1,-1,28,2,-1,-1,-1,-1,-1,-1,35,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,42,2,48,2,54,2,60,2,66,2,72,2,78,2,84,2,90,2,96,2,102,2,108,2,114,2,120,2,126,2,-124,2,-118,2,-112,2,-106,2,-100,2,-94,2,-88,2,-82,2,-76,2,-70,2,-64,2,-58,2,-52,2,-46,2,-40,2,-33,2,-27,2,-21,2,-15,2,-9,2,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-3,2,2,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,9,3,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,18,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,23,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,29,3,43,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,27,91,66,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,27,91,49,109,0,27,91,63,49,48,52,57,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,27,91,48,109,0,27,91,63,49,48,52,57,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,76,0,8,0,27,91,51,126,0,27,91,66,0,27,91,91,65,0,27,91,50,49,126,0,27,91,91,66,0,27,91,91,67,0,27,91,91,68,0,27,91,91,69,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,91,68,0,27,91,54,126,0,27,91,53,126,0,27,91,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,91,65,0,27,69,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,37,112,49,37,99,27,91,37,112,50,37,123,49,125,37,45,37,100,98,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,54,37,116,59,49,37,59,109,0,9,0,27,79,119,0,27,79,121,0,27,91,71,0,27,79,113,0,27,79,115,0,27,91,90,0,27,91,52,126,0,27,79,77,0,27,91,51,59,50,126,0,27,91,52,59,50,126,0,27,91,49,59,50,126,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,50,51,36,0,27,91,50,52,36,0,27,91,49,49,94,0,27,91,49,50,94,0,27,91,49,51,94,0,27,91,49,52,94,0,27,91,49,53,94,0,27,91,49,55,94,0,27,91,49,56,94,0,27,91,49,57,94,0,27,91,50,48,94,0,27,91,50,49,94,0,27,91,50,51,94,0,27,91,50,52,94,0,27,91,50,53,94,0,27,91,50,54,94,0,27,91,50,56,94,0,27,91,50,57,94,0,27,91,51,49,94,0,27,91,49,59,54,83,0,27,91,51,50,94,0,27,91,51,51,94,0,27,91,51,52,94,0,27,91,50,51,64,0,27,91,50,52,64,0,27,91,49,75,0,27,91,63,54,57,108,0,27,91,51,57,59,52,57,109,0,27,91,51,109,0,27,91,50,51,109,0,27,91,51,56,59,53,59,37,112,49,37,100,109,0,27,91,52,56,59,53,59,37,112,49,37,100,109,0,0,2,0,0,0,74,0,92,0,-46,1,1,1,-2,-1,-2,-1,0,0,-2,-1,5,0,11,0,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,21,0,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,28,0,32,0,36,0,40,0,44,0,48,0,52,0,56,0,60,0,64,0,68,0,72,0,-2,-1,-2,-1,-2,-1,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,32,0,37,0,42,0,47,0,52,0,56,0,61,0,66,0,71,0,76,0,81,0,87,0,93,0,99,0,105,0,111,0,117,0,123,0,-127,0,-121,0,-115,0,-110,0,-105,0,-100,0,-95,0,-90,0,-84,0,-78,0,-72,0,-66,0,-60,0,-54,0,-48,0,-42,0,-36,0,-30,0,-24,0,-18,0,-12,0,-6,0,0,1,6,1,12,1,18,1,24,1,30,1,34,1,39,1,44,1,49,1,54,1,59,1,63,1,67,1,71,1,75,1,79,1,85,1,91,1,97,1,103,1,109,1,115,1,121,1,126,1,-125,1,27,91,51,74,0,27,91,50,32,113,0,27,91,37,112,49,37,100,32,113,0,27,91,49,59,50,65,0,27,79,120,0,27,79,116,0,27,79,118,0,27,79,114,0,27,79,69,0,27,79,107,0,27,79,108,0,27,79,111,0,27,79,110,0,27,79,106,0,27,79,109,0,27,79,112,0,65,88,0,88,84,0,67,114,0,67,115,0,69,51,0,77,115,0,83,101,0,83,115,0,88,77,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,73,67,51,0,107,73,67,52,0,107,73,67,53,0,107,73,67,54,0,107,73,67,55,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,78,88,84,51,0,107,78,88,84,52,0,107,78,88,84,53,0,107,78,88,84,54,0,107,78,88,84,55,0,107,80,82,86,51,0,107,80,82,86,52,0,107,80,82,86,53,0,107,80,82,86,54,0,107,80,82,86,55,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,107,85,80,55,0,107,97,50,0,107,98,49,0,107,98,51,0,107,99,50,0,107,112,53,0,107,112,65,68,68,0,107,112,67,77,65,0,107,112,68,73,86,0,107,112,68,79,84,0,107,112,77,85,76,0,107,112,83,85,66,0,107,112,90,82,79,0,114,109,120,120,0,115,109,120,120,0,120,109,0
};
// cygwin|ANSI emulation for Cygwin,
@@ -392,7 +399,7 @@ static const int8_t conemu_terminfo[] = {
// user8=\E[?6c,
// user9=\E[c,
static const int8_t cygwin_terminfo[] = {
- 26,1,33,0,21,0,15,0,125,1,-108,2,99,121,103,119,105,110,124,65,78,83,73,32,101,109,117,108,97,116,105,111,110,32,102,111,114,32,67,121,103,119,105,110,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,1,0,0,0,0,0,1,-1,-1,8,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,8,0,64,0,-1,-1,0,0,2,0,-1,-1,-1,-1,4,0,11,0,15,0,19,0,-1,-1,30,0,47,0,51,0,-1,-1,55,0,-1,-1,-1,-1,57,0,-1,-1,61,0,-1,-1,65,0,69,0,-1,-1,-1,-1,73,0,-1,-1,79,0,84,0,-1,-1,-1,-1,93,0,98,0,-1,-1,103,0,108,0,113,0,-1,-1,118,0,124,0,-124,0,-1,-1,-111,0,-106,0,-100,0,-1,-1,-1,-1,-94,0,-1,-1,-1,-1,-1,-1,-1,-1,-92,0,-88,0,-1,-1,-84,0,-1,-1,-1,-1,-1,-1,-82,0,-1,-1,-77,0,-1,-1,-1,-1,-1,-1,-1,-1,-73,0,-68,0,-62,0,-57,0,-52,0,-47,0,-42,0,-36,0,-30,0,-24,0,-18,0,-13,0,-1,-1,-8,0,-1,-1,-4,0,1,1,6,1,-1,-1,-1,-1,-1,-1,10,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,14,1,-1,-1,17,1,26,1,35,1,44,1,-1,-1,53,1,62,1,71,1,-1,-1,80,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,89,1,-1,-1,-1,-1,-1,-1,95,1,98,1,109,1,112,1,114,1,117,1,-1,-1,-1,-1,-64,1,-62,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-58,1,-1,-1,-1,-1,-1,-1,-1,-1,-54,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,9,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,14,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,16,2,22,2,28,2,34,2,40,2,46,2,52,2,58,2,64,2,70,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,76,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,81,2,92,2,97,2,103,2,107,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,116,2,126,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-120,2,-114,2,7,0,13,0,27,91,72,27,91,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,27,91,66,0,27,91,72,0,8,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,27,91,49,49,109,0,27,91,49,109,0,27,55,27,91,63,52,55,104,0,27,91,52,104,0,27,91,56,109,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,49,48,109,0,27,91,48,59,49,48,109,0,27,91,50,74,27,91,63,52,55,108,27,56,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,7,0,27,91,64,0,27,91,76,0,8,0,27,91,51,126,0,27,91,66,0,27,91,91,65,0,27,91,50,49,126,0,27,91,91,66,0,27,91,91,67,0,27,91,91,68,0,27,91,91,69,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,91,68,0,27,91,54,126,0,27,91,53,126,0,27,91,67,0,27,91,65,0,13,10,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,65,0,27,99,27,93,82,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,59,49,48,37,63,37,112,49,37,116,59,55,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,51,37,116,59,55,37,59,37,63,37,112,54,37,116,59,49,37,59,37,63,37,112,55,37,116,59,56,37,59,37,63,37,112,57,37,116,59,49,49,37,59,109,0,9,0,27,93,59,0,27,91,71,0,43,16,44,17,45,24,46,25,48,-37,96,4,97,-79,102,-8,103,-15,104,-80,106,-39,107,-65,108,-38,109,-64,110,-59,111,126,112,-60,113,-60,114,-60,115,95,116,-61,117,-76,118,-63,119,-62,120,-77,121,-13,122,-14,123,-29,124,-40,125,-100,126,-2,0,27,91,52,126,0,26,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,54,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,91,51,37,112,49,37,100,109,0,27,91,52,37,112,49,37,100,109,0,27,91,49,49,109,0,27,91,49,48,109,0 // NOLINT
+ 26,1,33,0,21,0,15,0,125,1,-108,2,99,121,103,119,105,110,124,65,78,83,73,32,101,109,117,108,97,116,105,111,110,32,102,111,114,32,67,121,103,119,105,110,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,1,0,0,0,0,0,1,-1,-1,8,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,8,0,64,0,-1,-1,0,0,2,0,-1,-1,-1,-1,4,0,11,0,15,0,19,0,-1,-1,30,0,47,0,51,0,-1,-1,55,0,-1,-1,-1,-1,57,0,-1,-1,61,0,-1,-1,65,0,69,0,-1,-1,-1,-1,73,0,-1,-1,79,0,84,0,-1,-1,-1,-1,93,0,98,0,-1,-1,103,0,108,0,113,0,-1,-1,118,0,124,0,-124,0,-1,-1,-111,0,-106,0,-100,0,-1,-1,-1,-1,-94,0,-1,-1,-1,-1,-1,-1,-1,-1,-92,0,-88,0,-1,-1,-84,0,-1,-1,-1,-1,-1,-1,-82,0,-1,-1,-77,0,-1,-1,-1,-1,-1,-1,-1,-1,-73,0,-68,0,-62,0,-57,0,-52,0,-47,0,-42,0,-36,0,-30,0,-24,0,-18,0,-13,0,-1,-1,-8,0,-1,-1,-4,0,1,1,6,1,-1,-1,-1,-1,-1,-1,10,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,14,1,-1,-1,17,1,26,1,35,1,44,1,-1,-1,53,1,62,1,71,1,-1,-1,80,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,89,1,-1,-1,-1,-1,-1,-1,95,1,98,1,109,1,112,1,114,1,117,1,-1,-1,-1,-1,-64,1,-62,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-58,1,-1,-1,-1,-1,-1,-1,-1,-1,-54,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,9,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,14,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,16,2,22,2,28,2,34,2,40,2,46,2,52,2,58,2,64,2,70,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,76,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,81,2,92,2,97,2,103,2,107,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,116,2,126,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-120,2,-114,2,7,0,13,0,27,91,72,27,91,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,27,91,66,0,27,91,72,0,8,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,27,91,49,49,109,0,27,91,49,109,0,27,55,27,91,63,52,55,104,0,27,91,52,104,0,27,91,56,109,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,49,48,109,0,27,91,48,59,49,48,109,0,27,91,50,74,27,91,63,52,55,108,27,56,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,7,0,27,91,64,0,27,91,76,0,8,0,27,91,51,126,0,27,91,66,0,27,91,91,65,0,27,91,50,49,126,0,27,91,91,66,0,27,91,91,67,0,27,91,91,68,0,27,91,91,69,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,91,68,0,27,91,54,126,0,27,91,53,126,0,27,91,67,0,27,91,65,0,13,10,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,65,0,27,99,27,93,82,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,59,49,48,37,63,37,112,49,37,116,59,55,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,51,37,116,59,55,37,59,37,63,37,112,54,37,116,59,49,37,59,37,63,37,112,55,37,116,59,56,37,59,37,63,37,112,57,37,116,59,49,49,37,59,109,0,9,0,27,93,59,0,27,91,71,0,43,16,44,17,45,24,46,25,48,-37,96,4,97,-79,102,-8,103,-15,104,-80,106,-39,107,-65,108,-38,109,-64,110,-59,111,126,112,-60,113,-60,114,-60,115,95,116,-61,117,-76,118,-63,119,-62,120,-77,121,-13,122,-14,123,-29,124,-40,125,-100,126,-2,0,27,91,52,126,0,26,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,54,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,91,51,37,112,49,37,100,109,0,27,91,52,37,112,49,37,100,109,0,27,91,49,49,109,0,27,91,49,48,109,0
};
// interix|opennt|opennt-25|ntconsole|ntconsole-25|OpenNT-term compatible with color,
@@ -517,7 +524,6 @@ static const int8_t cygwin_terminfo[] = {
// parm_right_cursor=\E[%p1%dC,
// parm_rindex=\E[%p1%dT,
// parm_up_cursor=\E[%p1%dA,
-// repeat_char=%p1%c\E[%p2%{1}%-%db,
// reset_1string=\Ec,
// restore_cursor=\E[u,
// save_cursor=\E[s,
@@ -527,7 +533,7 @@ static const int8_t cygwin_terminfo[] = {
// set_a_foreground=\E[3%p1%dm,
// tab=^I,
static const int8_t interix_8colour_terminfo[] = {
- 26,1,82,0,29,0,16,0,105,1,116,2,105,110,116,101,114,105,120,124,111,112,101,110,110,116,124,111,112,101,110,110,116,45,50,53,124,110,116,99,111,110,115,111,108,101,124,110,116,99,111,110,115,111,108,101,45,50,53,124,79,112,101,110,78,84,45,116,101,114,109,32,99,111,109,112,97,116,105,98,108,101,32,119,105,116,104,32,99,111,108,111,114,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,80,0,8,0,25,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,8,0,64,0,3,0,-1,-1,0,0,2,0,-1,-1,-1,-1,4,0,9,0,13,0,-1,-1,-1,-1,17,0,34,0,36,0,-1,-1,40,0,-1,-1,-1,-1,44,0,48,0,52,0,-1,-1,-1,-1,56,0,-1,-1,-1,-1,-1,-1,-1,-1,60,0,65,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,73,0,78,0,83,0,-1,-1,-1,-1,88,0,93,0,-1,-1,-1,-1,105,0,109,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,113,0,-1,-1,117,0,-1,-1,-1,-1,-1,-1,119,0,-1,-1,121,0,-1,-1,-1,-1,-1,-1,125,0,-127,0,-123,0,-119,0,-115,0,-111,0,-107,0,-103,0,-99,0,-95,0,-91,0,-87,0,-83,0,-1,-1,-79,0,-75,0,-71,0,-67,0,-63,0,-59,0,-55,0,-1,-1,-51,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-47,0,-1,-1,-1,-1,-44,0,-35,0,-1,-1,-26,0,-17,0,-8,0,1,1,10,1,19,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,28,1,48,1,-1,-1,-1,-1,-1,-1,51,1,-1,-1,55,1,59,1,63,1,-1,-1,-1,-1,-1,-1,67,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,69,1,-1,-1,-124,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-120,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-116,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-112,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-108,1,-104,1,-100,1,-96,1,-92,1,-88,1,-84,1,-80,1,-76,1,-72,1,-68,1,-64,1,-60,1,-56,1,-52,1,-48,1,-44,1,-40,1,-36,1,-32,1,-28,1,-24,1,-20,1,-16,1,-12,1,-8,1,-4,1,0,2,4,2,8,2,12,2,16,2,20,2,24,2,28,2,32,2,36,2,40,2,44,2,48,2,52,2,56,2,60,2,64,2,68,2,72,2,76,2,80,2,84,2,88,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,92,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,96,2,106,2,7,0,13,0,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,68,0,27,91,67,0,27,91,85,0,27,91,65,0,27,91,77,0,27,91,49,109,0,27,91,115,27,91,49,98,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,48,109,0,27,91,50,98,27,91,117,13,27,91,75,0,27,91,109,0,27,91,109,0,27,91,76,0,8,0,127,0,27,91,66,0,27,70,65,0,27,70,49,0,27,70,65,0,27,70,50,0,27,70,51,0,27,70,52,0,27,70,53,0,27,70,54,0,27,70,55,0,27,70,56,0,27,70,57,0,27,91,72,0,27,91,76,0,27,91,68,0,27,91,85,0,27,91,84,0,27,91,83,0,27,91,67,0,27,70,43,0,27,70,45,0,27,91,65,0,13,10,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,37,112,49,37,99,27,91,37,112,50,37,123,49,125,37,45,37,100,98,0,27,99,0,27,91,117,0,27,91,115,0,27,91,83,0,27,91,84,0,9,0,43,16,44,17,45,24,46,25,48,-37,96,4,97,-79,102,-8,103,-15,104,-80,106,-39,107,-65,108,-38,109,-64,110,-59,111,126,112,-60,113,-60,114,-60,115,95,116,-61,117,-76,118,-63,119,-62,120,-77,121,-13,122,-14,123,-29,124,-40,125,-100,126,-2,0,27,91,90,0,27,91,85,0,27,70,94,0,27,70,36,0,27,70,66,0,27,70,67,0,27,70,68,0,27,70,69,0,27,70,70,0,27,70,71,0,27,70,72,0,27,70,73,0,27,70,74,0,27,70,75,0,27,70,76,0,27,70,77,0,27,70,78,0,27,70,79,0,27,70,80,0,27,70,81,0,27,70,82,0,27,70,83,0,27,70,84,0,27,70,85,0,27,70,86,0,27,70,87,0,27,70,88,0,27,70,89,0,27,70,90,0,27,70,97,0,27,70,98,0,27,70,99,0,27,70,100,0,27,70,101,0,27,70,102,0,27,70,103,0,27,70,104,0,27,70,105,0,27,70,106,0,27,70,107,0,27,70,109,0,27,70,110,0,27,70,111,0,27,70,112,0,27,70,113,0,27,70,114,0,27,70,115,0,27,70,116,0,27,70,117,0,27,70,118,0,27,70,119,0,27,70,120,0,27,70,121,0,27,70,122,0,27,91,109,0,27,91,51,37,112,49,37,100,109,0,27,91,52,37,112,49,37,100,109,0 // NOLINT
+ 26,1,82,0,29,0,16,0,105,1,96,2,105,110,116,101,114,105,120,124,111,112,101,110,110,116,124,111,112,101,110,110,116,45,50,53,124,110,116,99,111,110,115,111,108,101,124,110,116,99,111,110,115,111,108,101,45,50,53,124,79,112,101,110,78,84,45,116,101,114,109,32,99,111,109,112,97,116,105,98,108,101,32,119,105,116,104,32,99,111,108,111,114,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,80,0,8,0,25,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,8,0,64,0,3,0,-1,-1,0,0,2,0,-1,-1,-1,-1,4,0,9,0,13,0,-1,-1,-1,-1,17,0,34,0,36,0,-1,-1,40,0,-1,-1,-1,-1,44,0,48,0,52,0,-1,-1,-1,-1,56,0,-1,-1,-1,-1,-1,-1,-1,-1,60,0,65,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,73,0,78,0,83,0,-1,-1,-1,-1,88,0,93,0,-1,-1,-1,-1,105,0,109,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,113,0,-1,-1,117,0,-1,-1,-1,-1,-1,-1,119,0,-1,-1,121,0,-1,-1,-1,-1,-1,-1,125,0,-127,0,-123,0,-119,0,-115,0,-111,0,-107,0,-103,0,-99,0,-95,0,-91,0,-87,0,-83,0,-1,-1,-79,0,-75,0,-71,0,-67,0,-63,0,-59,0,-55,0,-1,-1,-51,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-47,0,-1,-1,-1,-1,-44,0,-35,0,-1,-1,-26,0,-17,0,-8,0,1,1,10,1,19,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,28,1,-1,-1,-1,-1,-1,-1,31,1,-1,-1,35,1,39,1,43,1,-1,-1,-1,-1,-1,-1,47,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,49,1,-1,-1,112,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,116,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,120,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,124,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-128,1,-124,1,-120,1,-116,1,-112,1,-108,1,-104,1,-100,1,-96,1,-92,1,-88,1,-84,1,-80,1,-76,1,-72,1,-68,1,-64,1,-60,1,-56,1,-52,1,-48,1,-44,1,-40,1,-36,1,-32,1,-28,1,-24,1,-20,1,-16,1,-12,1,-8,1,-4,1,0,2,4,2,8,2,12,2,16,2,20,2,24,2,28,2,32,2,36,2,40,2,44,2,48,2,52,2,56,2,60,2,64,2,68,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,72,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,76,2,86,2,7,0,13,0,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,68,0,27,91,67,0,27,91,85,0,27,91,65,0,27,91,77,0,27,91,49,109,0,27,91,115,27,91,49,98,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,48,109,0,27,91,50,98,27,91,117,13,27,91,75,0,27,91,109,0,27,91,109,0,27,91,76,0,8,0,127,0,27,91,66,0,27,70,65,0,27,70,49,0,27,70,65,0,27,70,50,0,27,70,51,0,27,70,52,0,27,70,53,0,27,70,54,0,27,70,55,0,27,70,56,0,27,70,57,0,27,91,72,0,27,91,76,0,27,91,68,0,27,91,85,0,27,91,84,0,27,91,83,0,27,91,67,0,27,70,43,0,27,70,45,0,27,91,65,0,13,10,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,27,99,0,27,91,117,0,27,91,115,0,27,91,83,0,27,91,84,0,9,0,43,16,44,17,45,24,46,25,48,-37,96,4,97,-79,102,-8,103,-15,104,-80,106,-39,107,-65,108,-38,109,-64,110,-59,111,126,112,-60,113,-60,114,-60,115,95,116,-61,117,-76,118,-63,119,-62,120,-77,121,-13,122,-14,123,-29,124,-40,125,-100,126,-2,0,27,91,90,0,27,91,85,0,27,70,94,0,27,70,36,0,27,70,66,0,27,70,67,0,27,70,68,0,27,70,69,0,27,70,70,0,27,70,71,0,27,70,72,0,27,70,73,0,27,70,74,0,27,70,75,0,27,70,76,0,27,70,77,0,27,70,78,0,27,70,79,0,27,70,80,0,27,70,81,0,27,70,82,0,27,70,83,0,27,70,84,0,27,70,85,0,27,70,86,0,27,70,87,0,27,70,88,0,27,70,89,0,27,70,90,0,27,70,97,0,27,70,98,0,27,70,99,0,27,70,100,0,27,70,101,0,27,70,102,0,27,70,103,0,27,70,104,0,27,70,105,0,27,70,106,0,27,70,107,0,27,70,109,0,27,70,110,0,27,70,111,0,27,70,112,0,27,70,113,0,27,70,114,0,27,70,115,0,27,70,116,0,27,70,117,0,27,70,118,0,27,70,119,0,27,70,120,0,27,70,121,0,27,70,122,0,27,91,109,0,27,91,51,37,112,49,37,100,109,0,27,91,52,37,112,49,37,100,109,0
};
// iTerm2.app|iterm2|terminal emulator for Mac OS X,
@@ -670,7 +676,7 @@ static const int8_t interix_8colour_terminfo[] = {
// user8=\E[?%[;0123456789]c,
// user9=\E[c,
static const int8_t iterm_256colour_terminfo[] = {
- 30,2,49,0,29,0,15,0,105,1,-29,3,105,84,101,114,109,50,46,97,112,112,124,105,116,101,114,109,50,124,116,101,114,109,105,110,97,108,32,101,109,117,108,97,116,111,114,32,102,111,114,32,77,97,99,32,79,83,32,88,0,0,1,0,0,1,0,0,0,0,1,0,0,0,1,1,0,0,0,0,0,1,0,0,0,0,1,0,0,1,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,50,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,25,0,30,0,37,0,41,0,45,0,-1,-1,56,0,73,0,75,0,79,0,86,0,-1,-1,88,0,95,0,-1,-1,99,0,-1,-1,103,0,107,0,111,0,-1,-1,117,0,119,0,124,0,-127,0,-1,-1,-109,0,-104,0,-1,-1,-1,-1,-99,0,-94,0,-89,0,-1,-1,-84,0,-82,0,-77,0,-1,-1,-59,0,-54,0,-48,0,-42,0,-1,-1,-24,0,-1,-1,-1,-1,-1,-1,-1,-1,-22,0,-18,0,-1,-1,-14,0,-1,-1,-1,-1,-1,-1,-12,0,-1,-1,-7,0,-1,-1,-1,-1,-1,-1,-1,-1,-3,0,1,1,7,1,11,1,15,1,19,1,25,1,31,1,37,1,43,1,49,1,-1,-1,-1,-1,53,1,-1,-1,57,1,62,1,67,1,71,1,78,1,-1,-1,85,1,89,1,97,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,105,1,-1,-1,108,1,117,1,126,1,-121,1,-112,1,-103,1,-94,1,-85,1,-76,1,-67,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-58,1,-1,-1,-1,-1,-32,1,-29,1,-18,1,-15,1,-13,1,-10,1,68,2,-1,-1,71,2,73,2,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-1,-1,-1,-1,78,2,-1,-1,-127,2,-1,-1,-1,-1,-123,2,-117,2,-1,-1,-1,-1,-111,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-104,2,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-100,2,-1,-1,-1,-1,-1,-1,-1,-1,-93,2,-1,-1,-86,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-79,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-72,2,-66,2,-60,2,-53,2,-46,2,-39,2,-32,2,-24,2,-16,2,-8,2,0,3,8,3,16,3,24,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,32,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,37,3,48,3,53,3,72,3,76,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,85,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,90,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,96,3,-1,-1,-1,-1,-1,-1,100,3,-93,3,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,27,93,50,59,7,0,14,0,27,91,53,109,0,27,91,49,109,0,27,91,63,49,48,52,57,104,27,91,50,50,59,48,59,48,116,0,27,91,50,109,0,27,91,52,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,15,0,27,91,109,15,0,27,91,63,49,48,52,57,108,27,91,50,51,59,48,59,48,116,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,63,53,104,36,60,50,48,48,47,62,27,91,63,53,108,0,7,0,27,91,64,0,27,91,76,0,127,0,27,91,51,126,0,27,79,66,0,27,79,80,0,27,91,50,49,126,0,27,79,81,0,27,79,82,0,27,79,83,0,27,91,49,53,126,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,79,72,0,27,79,68,0,27,91,54,126,0,27,91,53,126,0,27,79,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,79,65,0,27,91,63,49,108,27,62,0,27,91,63,49,104,27,61,0,27,69,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,27,91,33,112,27,91,63,51,59,52,108,27,91,52,108,27,62,27,91,63,49,48,48,48,108,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,54,37,116,59,49,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,37,63,37,112,53,37,116,59,50,37,59,109,37,63,37,112,57,37,116,14,37,101,15,37,59,0,27,72,0,9,0,27,93,50,59,0,96,96,97,97,102,102,103,103,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,91,90,0,27,91,63,55,104,0,27,91,63,55,108,0,27,40,66,27,41,48,0,27,79,70,0,27,91,49,59,50,70,0,27,91,49,59,50,72,0,27,91,49,59,50,68,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,49,59,50,80,0,27,91,49,59,50,81,0,27,91,49,59,50,82,0,27,91,49,59,50,83,0,27,91,49,53,59,50,126,0,27,91,49,55,59,50,126,0,27,91,49,56,59,50,126,0,27,91,49,57,59,50,126,0,27,91,50,48,59,50,126,0,27,91,50,49,59,50,126,0,27,91,50,51,59,50,126,0,27,91,50,52,59,50,126,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,37,91,59,48,49,50,51,52,53,54,55,56,57,93,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,91,51,109,0,27,91,50,51,109,0,27,91,77,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,0,1,0,0,0,33,0,67,0,-37,1,0,0,0,0,5,0,32,0,37,0,45,0,52,0,59,0,66,0,74,0,81,0,88,0,96,0,104,0,111,0,119,0,126,0,-123,0,-115,0,-107,0,-102,0,-94,0,-87,0,-80,0,-74,0,-68,0,-63,0,-55,0,-48,0,-41,0,-36,0,-28,0,-21,0,-14,0,0,0,3,0,6,0,9,0,14,0,19,0,24,0,29,0,35,0,41,0,47,0,53,0,59,0,65,0,71,0,77,0,83,0,89,0,95,0,101,0,107,0,113,0,119,0,125,0,-125,0,-119,0,-113,0,-107,0,-101,0,-95,0,-90,0,-85,0,-80,0,-75,0,27,93,50,59,0,27,91,63,49,48,48,48,37,63,37,112,49,37,123,49,125,37,61,37,116,104,37,101,108,37,59,0,27,27,91,66,0,27,91,49,59,49,48,66,0,27,91,49,59,53,66,0,27,91,49,59,54,66,0,27,91,49,59,57,70,0,27,91,49,59,49,48,70,0,27,91,49,59,53,70,0,27,91,49,59,54,70,0,27,91,49,59,49,51,70,0,27,91,49,59,49,52,70,0,27,91,49,59,57,72,0,27,91,49,59,49,48,72,0,27,91,49,59,53,72,0,27,91,49,59,54,72,0,27,91,49,59,49,51,72,0,27,91,49,59,49,52,72,0,27,27,91,68,0,27,91,49,59,49,48,68,0,27,91,49,59,53,68,0,27,91,49,59,54,68,0,27,27,91,54,126,0,27,27,91,53,126,0,27,27,91,67,0,27,91,49,59,49,48,67,0,27,91,49,59,53,67,0,27,91,49,59,54,67,0,27,27,91,65,0,27,91,49,59,49,48,65,0,27,91,49,59,53,65,0,27,91,49,59,54,65,0,27,91,77,37,63,37,112,52,37,116,51,37,101,37,112,51,37,39,32,39,37,43,37,99,37,59,37,112,50,37,39,33,39,37,43,37,99,37,112,49,37,39,33,39,37,43,37,99,0,65,88,0,84,83,0,88,77,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,69,78,68,56,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,72,79,77,56,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,78,88,84,51,0,107,80,82,86,51,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,120,109,0 // NOLINT
+ 30,2,49,0,29,0,15,0,105,1,-29,3,105,84,101,114,109,50,46,97,112,112,124,105,116,101,114,109,50,124,116,101,114,109,105,110,97,108,32,101,109,117,108,97,116,111,114,32,102,111,114,32,77,97,99,32,79,83,32,88,0,0,1,0,0,1,0,0,0,0,1,0,0,0,1,1,0,0,0,0,0,1,0,0,0,0,1,0,0,1,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,50,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,25,0,30,0,37,0,41,0,45,0,-1,-1,56,0,73,0,75,0,79,0,86,0,-1,-1,88,0,95,0,-1,-1,99,0,-1,-1,103,0,107,0,111,0,-1,-1,117,0,119,0,124,0,-127,0,-1,-1,-109,0,-104,0,-1,-1,-1,-1,-99,0,-94,0,-89,0,-1,-1,-84,0,-82,0,-77,0,-1,-1,-59,0,-54,0,-48,0,-42,0,-1,-1,-24,0,-1,-1,-1,-1,-1,-1,-1,-1,-22,0,-18,0,-1,-1,-14,0,-1,-1,-1,-1,-1,-1,-12,0,-1,-1,-7,0,-1,-1,-1,-1,-1,-1,-1,-1,-3,0,1,1,7,1,11,1,15,1,19,1,25,1,31,1,37,1,43,1,49,1,-1,-1,-1,-1,53,1,-1,-1,57,1,62,1,67,1,71,1,78,1,-1,-1,85,1,89,1,97,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,105,1,-1,-1,108,1,117,1,126,1,-121,1,-112,1,-103,1,-94,1,-85,1,-76,1,-67,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-58,1,-1,-1,-1,-1,-32,1,-29,1,-18,1,-15,1,-13,1,-10,1,68,2,-1,-1,71,2,73,2,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-1,-1,-1,-1,78,2,-1,-1,-127,2,-1,-1,-1,-1,-123,2,-117,2,-1,-1,-1,-1,-111,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-104,2,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-100,2,-1,-1,-1,-1,-1,-1,-1,-1,-93,2,-1,-1,-86,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-79,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-72,2,-66,2,-60,2,-53,2,-46,2,-39,2,-32,2,-24,2,-16,2,-8,2,0,3,8,3,16,3,24,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,32,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,37,3,48,3,53,3,72,3,76,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,85,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,90,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,96,3,-1,-1,-1,-1,-1,-1,100,3,-93,3,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,27,93,50,59,7,0,14,0,27,91,53,109,0,27,91,49,109,0,27,91,63,49,48,52,57,104,27,91,50,50,59,48,59,48,116,0,27,91,50,109,0,27,91,52,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,15,0,27,91,109,15,0,27,91,63,49,48,52,57,108,27,91,50,51,59,48,59,48,116,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,63,53,104,36,60,50,48,48,47,62,27,91,63,53,108,0,7,0,27,91,64,0,27,91,76,0,127,0,27,91,51,126,0,27,79,66,0,27,79,80,0,27,91,50,49,126,0,27,79,81,0,27,79,82,0,27,79,83,0,27,91,49,53,126,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,79,72,0,27,79,68,0,27,91,54,126,0,27,91,53,126,0,27,79,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,79,65,0,27,91,63,49,108,27,62,0,27,91,63,49,104,27,61,0,27,69,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,27,91,33,112,27,91,63,51,59,52,108,27,91,52,108,27,62,27,91,63,49,48,48,48,108,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,54,37,116,59,49,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,37,63,37,112,53,37,116,59,50,37,59,109,37,63,37,112,57,37,116,14,37,101,15,37,59,0,27,72,0,9,0,27,93,50,59,0,96,96,97,97,102,102,103,103,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,91,90,0,27,91,63,55,104,0,27,91,63,55,108,0,27,40,66,27,41,48,0,27,79,70,0,27,91,49,59,50,70,0,27,91,49,59,50,72,0,27,91,49,59,50,68,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,49,59,50,80,0,27,91,49,59,50,81,0,27,91,49,59,50,82,0,27,91,49,59,50,83,0,27,91,49,53,59,50,126,0,27,91,49,55,59,50,126,0,27,91,49,56,59,50,126,0,27,91,49,57,59,50,126,0,27,91,50,48,59,50,126,0,27,91,50,49,59,50,126,0,27,91,50,51,59,50,126,0,27,91,50,52,59,50,126,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,37,91,59,48,49,50,51,52,53,54,55,56,57,93,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,91,51,109,0,27,91,50,51,109,0,27,91,77,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,0,0,0,0,0,33,0,66,0,-37,1,0,0,5,0,32,0,37,0,45,0,52,0,59,0,66,0,74,0,81,0,88,0,96,0,104,0,111,0,119,0,126,0,-123,0,-115,0,-107,0,-102,0,-94,0,-87,0,-80,0,-74,0,-68,0,-63,0,-55,0,-48,0,-41,0,-36,0,-28,0,-21,0,-14,0,0,0,3,0,6,0,11,0,16,0,21,0,26,0,32,0,38,0,44,0,50,0,56,0,62,0,68,0,74,0,80,0,86,0,92,0,98,0,104,0,110,0,116,0,122,0,-128,0,-122,0,-116,0,-110,0,-104,0,-98,0,-93,0,-88,0,-83,0,-78,0,27,93,50,59,0,27,91,63,49,48,48,48,37,63,37,112,49,37,123,49,125,37,61,37,116,104,37,101,108,37,59,0,27,27,91,66,0,27,91,49,59,49,48,66,0,27,91,49,59,53,66,0,27,91,49,59,54,66,0,27,91,49,59,57,70,0,27,91,49,59,49,48,70,0,27,91,49,59,53,70,0,27,91,49,59,54,70,0,27,91,49,59,49,51,70,0,27,91,49,59,49,52,70,0,27,91,49,59,57,72,0,27,91,49,59,49,48,72,0,27,91,49,59,53,72,0,27,91,49,59,54,72,0,27,91,49,59,49,51,72,0,27,91,49,59,49,52,72,0,27,27,91,68,0,27,91,49,59,49,48,68,0,27,91,49,59,53,68,0,27,91,49,59,54,68,0,27,27,91,54,126,0,27,27,91,53,126,0,27,27,91,67,0,27,91,49,59,49,48,67,0,27,91,49,59,53,67,0,27,91,49,59,54,67,0,27,27,91,65,0,27,91,49,59,49,48,65,0,27,91,49,59,53,65,0,27,91,49,59,54,65,0,27,91,77,37,63,37,112,52,37,116,37,112,51,37,101,37,123,51,125,37,59,37,39,32,39,37,43,37,99,37,112,50,37,39,33,39,37,43,37,99,37,112,49,37,39,33,39,37,43,37,99,0,84,83,0,88,77,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,69,78,68,56,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,72,79,77,56,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,78,88,84,51,0,107,80,82,86,51,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,120,109,0
};
// linux|linux console,
@@ -686,7 +692,7 @@ static const int8_t iterm_256colour_terminfo[] = {
// max_colors#8,
// max_pairs#64,
// no_color_video#18,
-// acs_chars=++\054\054--..00__``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}c~~,
+// acs_chars=++\054\054--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,
// bell=^G,
// carriage_return=\r,
// change_scroll_region=\E[%i%p1%d;%p2%dr,
@@ -792,7 +798,7 @@ static const int8_t iterm_256colour_terminfo[] = {
// user8=\E[?6c,
// user9=\E[c,
static const int8_t linux_16colour_terminfo[] = {
- 26,1,20,0,29,0,16,0,125,1,69,3,108,105,110,117,120,124,108,105,110,117,120,32,99,111,110,115,111,108,101,0,0,1,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,1,1,0,-1,-1,8,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,8,0,64,0,18,0,-1,-1,0,0,2,0,4,0,21,0,26,0,33,0,37,0,41,0,-1,-1,52,0,69,0,71,0,75,0,87,0,-1,-1,89,0,101,0,-1,-1,105,0,109,0,121,0,125,0,-1,-1,-1,-1,-127,0,-125,0,-120,0,-1,-1,-1,-1,-115,0,-110,0,-1,-1,-1,-1,-105,0,-100,0,-95,0,-90,0,-81,0,-79,0,-1,-1,-1,-1,-74,0,-69,0,-63,0,-57,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-39,0,-35,0,-1,-1,-31,0,-1,-1,-1,-1,-1,-1,-29,0,-1,-1,-24,0,-1,-1,-1,-1,-1,-1,-1,-1,-20,0,-15,0,-9,0,-4,0,1,1,6,1,11,1,17,1,23,1,29,1,35,1,40,1,-1,-1,45,1,-1,-1,49,1,54,1,59,1,-1,-1,-1,-1,-1,-1,63,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,67,1,-1,-1,70,1,79,1,88,1,97,1,-1,-1,106,1,115,1,124,1,-1,-1,-123,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-114,1,-1,-1,-1,-1,-1,-1,-108,1,-105,1,-94,1,-91,1,-89,1,-86,1,1,2,-1,-1,4,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,6,2,-1,-1,-1,-1,-1,-1,-1,-1,10,2,-1,-1,77,2,-1,-1,-1,-1,81,2,87,2,-1,-1,-1,-1,93,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,97,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,102,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,104,2,110,2,116,2,122,2,-128,2,-122,2,-116,2,-110,2,-104,2,-98,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-92,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-87,2,-76,2,-71,2,-65,2,-61,2,-52,2,-48,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,33,3,-1,-1,-1,-1,-1,-1,37,3,47,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,57,3,63,3,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,27,91,63,49,99,0,8,0,27,91,63,50,53,104,27,91,63,48,99,0,27,91,67,0,27,91,65,0,27,91,63,50,53,104,27,91,63,56,99,0,27,91,80,0,27,91,77,0,14,0,27,91,53,109,0,27,91,49,109,0,27,91,50,109,0,27,91,52,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,15,0,27,91,109,15,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,63,53,104,36,60,50,48,48,47,62,27,91,63,53,108,0,27,91,64,0,27,91,76,0,127,0,27,91,51,126,0,27,91,66,0,27,91,91,65,0,27,91,50,49,126,0,27,91,91,66,0,27,91,91,67,0,27,91,91,68,0,27,91,91,69,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,91,68,0,27,91,54,126,0,27,91,53,126,0,27,91,67,0,27,91,65,0,13,10,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,65,0,27,99,27,93,82,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,59,49,48,37,63,37,112,49,37,116,59,55,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,51,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,37,63,37,112,53,37,116,59,50,37,59,37,63,37,112,54,37,116,59,49,37,59,109,37,63,37,112,57,37,116,14,37,101,15,37,59,0,27,72,0,9,0,27,91,71,0,43,43,44,44,45,45,46,46,48,48,95,95,96,96,97,97,102,102,103,103,104,104,105,105,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,99,126,126,0,27,91,90,0,27,91,63,55,104,0,27,91,63,55,108,0,27,41,48,0,27,91,52,126,0,26,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,54,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,93,82,0,27,93,80,37,112,49,37,120,37,112,50,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,48,50,120,37,112,51,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,48,50,120,37,112,52,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,48,50,120,0,27,91,77,0,27,91,51,37,112,49,37,100,109,0,27,91,52,37,112,49,37,100,109,0,27,91,49,49,109,0,27,91,49,48,109,0,0,3,0,1,0,12,0,28,0,63,0,1,0,0,0,1,0,-1,-1,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,33,0,39,0,43,0,47,0,51,0,55,0,27,91,51,74,0,65,88,0,71,48,0,88,84,0,85,56,0,69,48,0,69,51,0,83,48,0,84,83,0,88,77,0,107,69,78,68,53,0,107,72,79,77,53,0,107,97,50,0,107,98,49,0,107,98,51,0,107,99,50,0,120,109,0 // NOLINT
+ 26,1,20,0,29,0,16,0,125,1,67,3,108,105,110,117,120,124,108,105,110,117,120,32,99,111,110,115,111,108,101,0,0,1,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,1,1,0,-1,-1,8,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,8,0,64,0,18,0,-1,-1,0,0,2,0,4,0,21,0,26,0,33,0,37,0,41,0,-1,-1,52,0,69,0,71,0,75,0,87,0,-1,-1,89,0,101,0,-1,-1,105,0,109,0,121,0,125,0,-1,-1,-1,-1,-127,0,-125,0,-120,0,-1,-1,-1,-1,-115,0,-110,0,-1,-1,-1,-1,-105,0,-100,0,-95,0,-90,0,-81,0,-79,0,-1,-1,-1,-1,-74,0,-69,0,-63,0,-57,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-39,0,-35,0,-1,-1,-31,0,-1,-1,-1,-1,-1,-1,-29,0,-1,-1,-24,0,-1,-1,-1,-1,-1,-1,-1,-1,-20,0,-15,0,-9,0,-4,0,1,1,6,1,11,1,17,1,23,1,29,1,35,1,40,1,-1,-1,45,1,-1,-1,49,1,54,1,59,1,-1,-1,-1,-1,-1,-1,63,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,67,1,-1,-1,70,1,79,1,88,1,97,1,-1,-1,106,1,115,1,124,1,-1,-1,-123,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-114,1,-1,-1,-1,-1,-1,-1,-108,1,-105,1,-94,1,-91,1,-89,1,-86,1,1,2,-1,-1,4,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,6,2,-1,-1,-1,-1,-1,-1,-1,-1,10,2,-1,-1,75,2,-1,-1,-1,-1,79,2,85,2,-1,-1,-1,-1,91,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,95,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,100,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,102,2,108,2,114,2,120,2,126,2,-124,2,-118,2,-112,2,-106,2,-100,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-94,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-89,2,-78,2,-73,2,-67,2,-63,2,-54,2,-50,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,31,3,-1,-1,-1,-1,-1,-1,35,3,45,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,55,3,61,3,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,27,91,63,49,99,0,8,0,27,91,63,50,53,104,27,91,63,48,99,0,27,91,67,0,27,91,65,0,27,91,63,50,53,104,27,91,63,56,99,0,27,91,80,0,27,91,77,0,14,0,27,91,53,109,0,27,91,49,109,0,27,91,50,109,0,27,91,52,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,15,0,27,91,109,15,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,63,53,104,36,60,50,48,48,47,62,27,91,63,53,108,0,27,91,64,0,27,91,76,0,127,0,27,91,51,126,0,27,91,66,0,27,91,91,65,0,27,91,50,49,126,0,27,91,91,66,0,27,91,91,67,0,27,91,91,68,0,27,91,91,69,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,91,68,0,27,91,54,126,0,27,91,53,126,0,27,91,67,0,27,91,65,0,13,10,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,65,0,27,99,27,93,82,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,59,49,48,37,63,37,112,49,37,116,59,55,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,51,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,37,63,37,112,53,37,116,59,50,37,59,37,63,37,112,54,37,116,59,49,37,59,109,37,63,37,112,57,37,116,14,37,101,15,37,59,0,27,72,0,9,0,27,91,71,0,43,43,44,44,45,45,46,46,48,48,96,96,97,97,102,102,103,103,104,104,105,105,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,91,90,0,27,91,63,55,104,0,27,91,63,55,108,0,27,41,48,0,27,91,52,126,0,26,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,54,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,93,82,0,27,93,80,37,112,49,37,120,37,112,50,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,48,50,120,37,112,51,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,48,50,120,37,112,52,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,48,50,120,0,27,91,77,0,27,91,51,37,112,49,37,100,109,0,27,91,52,37,112,49,37,100,109,0,27,91,49,49,109,0,27,91,49,48,109,0,0,1,0,1,0,1,0,4,0,14,0,1,0,1,0,0,0,0,0,3,0,6,0,27,91,51,74,0,65,88,0,85,56,0,69,51,0
};
// putty-256color|PuTTY 0.58 with xterm 256-colors,
@@ -855,12 +861,18 @@ static const int8_t linux_16colour_terminfo[] = {
// from_status_line=^G,
// init_2string=\E7\E[r\E[m\E[?7h\E[?1;4;6l\E[4l\E8\E>\E]R,
// insert_line=\E[L,
-// key_b2=\E[G,
+// key_a1=\EOq,
+// key_a3=\EOs,
+// key_b2=\EOr,
// key_backspace=\177,
// key_btab=\E[Z,
+// key_c1=\EOp,
+// key_c3=\EOn,
// key_dc=\E[3~,
// key_down=\EOB,
// key_end=\E[4~,
+// key_enter=\EOM,
+// key_f0=\EOy,
// key_f1=\E[11~,
// key_f10=\E[21~,
// key_f11=\E[23~,
@@ -884,14 +896,12 @@ static const int8_t linux_16colour_terminfo[] = {
// key_home=\E[1~,
// key_ic=\E[2~,
// key_left=\EOD,
-// key_mouse=\E[M,
+// key_mouse=\E[<,
// key_npage=\E[6~,
// key_ppage=\E[5~,
// key_right=\EOC,
// key_sf=\E[B,
-// key_sleft=\E[D,
// key_sr=\E[A,
-// key_sright=\E[C,
// key_suspend=^Z,
// key_up=\EOA,
// keypad_local=\E[?1l\E>,
@@ -908,6 +918,7 @@ static const int8_t linux_16colour_terminfo[] = {
// parm_right_cursor=\E[%p1%dC,
// parm_rindex=\E[%p1%dT,
// parm_up_cursor=\E[%p1%dA,
+// repeat_char=%p1%c\E[%p2%{1}%-%db,
// reset_2string=\E<\E["p\E[50;6"p\Ec\E[?3l\E]R\E[?1000l,
// restore_cursor=\E8,
// row_address=\E[%i%p1%dd,
@@ -928,7 +939,7 @@ static const int8_t linux_16colour_terminfo[] = {
// user8=\E[?6c,
// user9=\E[c,
static const int8_t putty_256colour_terminfo[] = {
- 30,2,48,0,29,0,16,0,125,1,-106,4,112,117,116,116,121,45,50,53,54,99,111,108,111,114,124,80,117,84,84,89,32,48,46,53,56,32,119,105,116,104,32,120,116,101,114,109,32,50,53,54,45,99,111,108,111,114,115,0,1,1,0,0,1,0,0,0,0,1,0,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,-1,-1,-1,-1,8,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,22,0,0,0,0,0,4,0,6,0,8,0,25,0,30,0,37,0,41,0,45,0,-1,-1,56,0,73,0,76,0,80,0,87,0,-1,-1,89,0,96,0,-1,-1,100,0,-1,-1,103,0,107,0,111,0,-1,-1,117,0,119,0,124,0,-127,0,-1,-1,-1,-1,-120,0,-1,-1,-1,-1,-115,0,-110,0,-105,0,-100,0,-91,0,-89,0,-84,0,-1,-1,-73,0,-68,0,-62,0,-56,0,-1,-1,-38,0,-1,-1,-36,0,-1,-1,-1,-1,-1,-1,-2,0,-1,-1,2,1,-1,-1,-1,-1,-1,-1,4,1,-1,-1,9,1,-1,-1,-1,-1,-1,-1,-1,-1,13,1,19,1,25,1,31,1,37,1,43,1,49,1,55,1,61,1,67,1,73,1,78,1,-1,-1,83,1,-1,-1,87,1,92,1,97,1,101,1,105,1,-1,-1,109,1,113,1,121,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-127,1,-1,-1,-124,1,-115,1,-106,1,-1,-1,-97,1,-88,1,-79,1,-70,1,-61,1,-52,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-43,1,-1,-1,-1,-1,-10,1,-7,1,4,2,7,2,9,2,12,2,84,2,-1,-1,87,2,89,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,94,2,-1,-1,-1,-1,-1,-1,-1,-1,98,2,-1,-1,-107,2,-1,-1,-1,-1,-103,2,-97,2,-1,-1,-1,-1,-91,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-84,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-79,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-77,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-73,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-69,2,-63,2,-57,2,-51,2,-45,2,-39,2,-33,2,-27,2,-21,2,-15,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-9,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-4,2,7,3,12,3,18,3,22,3,31,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,35,3,-1,-1,-1,-1,-1,-1,39,3,102,3,-1,-1,-1,-1,-1,-1,-90,3,-84,3,-78,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-72,3,-118,4,-112,4,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,27,68,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,50,53,104,0,27,91,67,0,27,77,0,27,91,80,0,27,91,77,0,27,93,48,59,7,0,14,0,27,91,53,109,0,27,91,49,109,0,27,91,63,52,55,104,0,27,91,52,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,15,0,27,91,109,15,0,27,91,50,74,27,91,63,52,55,108,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,63,53,104,36,60,49,48,48,47,62,27,91,63,53,108,0,7,0,27,55,27,91,114,27,91,109,27,91,63,55,104,27,91,63,49,59,52,59,54,108,27,91,52,108,27,56,27,62,27,93,82,0,27,91,76,0,127,0,27,91,51,126,0,27,79,66,0,27,91,49,49,126,0,27,91,50,49,126,0,27,91,49,50,126,0,27,91,49,51,126,0,27,91,49,52,126,0,27,91,49,53,126,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,79,68,0,27,91,54,126,0,27,91,53,126,0,27,79,67,0,27,91,66,0,27,91,65,0,27,79,65,0,27,91,63,49,108,27,62,0,27,91,63,49,104,27,61,0,13,10,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,27,60,27,91,34,112,27,91,53,48,59,54,34,112,27,99,27,91,63,51,108,27,93,82,27,91,63,49,48,48,48,108,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,49,37,112,54,37,124,37,116,59,49,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,109,37,63,37,112,57,37,116,14,37,101,15,37,59,0,27,72,0,9,0,27,93,48,59,0,27,91,71,0,96,96,97,97,102,102,103,103,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,91,90,0,27,91,63,55,104,0,27,91,63,55,108,0,27,40,66,27,41,48,0,27,91,52,126,0,26,0,27,91,68,0,27,91,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,54,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,93,82,0,27,91,77,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,49,48,109,0,27,91,49,49,109,0,27,91,49,50,109,0,37,63,37,112,49,37,123,56,125,37,61,37,116,27,37,37,71,-30,-105,-104,27,37,37,64,37,101,37,112,49,37,123,49,48,125,37,61,37,116,27,37,37,71,-30,-105,-103,27,37,37,64,37,101,37,112,49,37,123,49,50,125,37,61,37,116,27,37,37,71,-30,-103,-128,27,37,37,64,37,101,37,112,49,37,123,49,51,125,37,61,37,116,27,37,37,71,-30,-103,-86,27,37,37,64,37,101,37,112,49,37,123,49,52,125,37,61,37,116,27,37,37,71,-30,-103,-85,27,37,37,64,37,101,37,112,49,37,123,49,53,125,37,61,37,116,27,37,37,71,-30,-104,-68,27,37,37,64,37,101,37,112,49,37,123,50,55,125,37,61,37,116,27,37,37,71,-30,-122,-112,27,37,37,64,37,101,37,112,49,37,123,49,53,53,125,37,61,37,116,27,37,37,71,-32,-126,-94,27,37,37,64,37,101,37,112,49,37,99,37,59,0,27,91,49,49,109,0,27,91,49,48,109,0,3,0,1,0,60,0,124,0,74,1,0,0,1,0,1,0,0,0,-1,-1,0,0,-1,-1,-1,-1,-1,-1,5,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,30,0,33,0,39,0,45,0,50,0,55,0,60,0,65,0,70,0,74,0,79,0,84,0,89,0,94,0,99,0,105,0,111,0,117,0,123,0,-127,0,-121,0,-115,0,-109,0,-103,0,-97,0,-91,0,-85,0,-80,0,-75,0,-69,0,-63,0,-57,0,-51,0,-45,0,-39,0,-33,0,-27,0,-21,0,-15,0,-9,0,-3,0,3,1,9,1,15,1,21,1,25,1,30,1,35,1,40,1,45,1,49,1,53,1,57,1,61,1,27,91,51,74,0,27,93,48,59,0,65,88,0,71,48,0,88,84,0,85,56,0,69,48,0,69,51,0,83,48,0,83,101,0,83,115,0,84,83,0,88,77,0,103,114,98,111,109,0,103,115,98,111,109,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,69,78,68,56,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,72,79,77,56,0,107,73,67,53,0,107,73,67,54,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,78,88,84,51,0,107,78,88,84,53,0,107,78,88,84,54,0,107,80,82,86,51,0,107,80,82,86,53,0,107,80,82,86,54,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,107,97,50,0,107,98,49,0,107,98,51,0,107,99,50,0,120,109,0 // NOLINT
+ 30,2,48,0,29,0,16,0,125,1,-70,4,112,117,116,116,121,45,50,53,54,99,111,108,111,114,124,80,117,84,84,89,32,48,46,53,56,32,119,105,116,104,32,120,116,101,114,109,32,50,53,54,45,99,111,108,111,114,115,0,1,1,0,0,1,0,0,0,0,1,0,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,-1,-1,-1,-1,8,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,22,0,0,0,0,0,4,0,6,0,8,0,25,0,30,0,37,0,41,0,45,0,-1,-1,56,0,73,0,76,0,80,0,87,0,-1,-1,89,0,96,0,-1,-1,100,0,-1,-1,103,0,107,0,111,0,-1,-1,117,0,119,0,124,0,-127,0,-1,-1,-1,-1,-120,0,-1,-1,-1,-1,-115,0,-110,0,-105,0,-100,0,-91,0,-89,0,-84,0,-1,-1,-73,0,-68,0,-62,0,-56,0,-1,-1,-38,0,-1,-1,-36,0,-1,-1,-1,-1,-1,-1,-2,0,-1,-1,2,1,-1,-1,-1,-1,-1,-1,4,1,-1,-1,9,1,-1,-1,-1,-1,-1,-1,13,1,17,1,23,1,29,1,35,1,41,1,47,1,53,1,59,1,65,1,71,1,77,1,82,1,-1,-1,87,1,-1,-1,91,1,96,1,101,1,105,1,109,1,-1,-1,113,1,117,1,125,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-123,1,-1,-1,-120,1,-111,1,-102,1,-1,-1,-93,1,-84,1,-75,1,-66,1,-57,1,-48,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-39,1,-1,-1,-19,1,-1,-1,-1,-1,14,2,17,2,28,2,31,2,33,2,36,2,108,2,-1,-1,111,2,113,2,-1,-1,-1,-1,-1,-1,118,2,122,2,126,2,-126,2,-122,2,-1,-1,-1,-1,-118,2,-1,-1,-67,2,-1,-1,-1,-1,-63,2,-57,2,-1,-1,-1,-1,-51,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-44,2,-39,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-35,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-33,2,-27,2,-21,2,-15,2,-9,2,-3,2,3,3,9,3,15,3,21,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,27,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,32,3,43,3,48,3,54,3,58,3,67,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,71,3,-1,-1,-1,-1,-1,-1,75,3,-118,3,-1,-1,-1,-1,-1,-1,-54,3,-48,3,-42,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-36,3,-82,4,-76,4,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,27,68,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,50,53,104,0,27,91,67,0,27,77,0,27,91,80,0,27,91,77,0,27,93,48,59,7,0,14,0,27,91,53,109,0,27,91,49,109,0,27,91,63,52,55,104,0,27,91,52,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,15,0,27,91,109,15,0,27,91,50,74,27,91,63,52,55,108,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,63,53,104,36,60,49,48,48,47,62,27,91,63,53,108,0,7,0,27,55,27,91,114,27,91,109,27,91,63,55,104,27,91,63,49,59,52,59,54,108,27,91,52,108,27,56,27,62,27,93,82,0,27,91,76,0,127,0,27,91,51,126,0,27,79,66,0,27,79,121,0,27,91,49,49,126,0,27,91,50,49,126,0,27,91,49,50,126,0,27,91,49,51,126,0,27,91,49,52,126,0,27,91,49,53,126,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,79,68,0,27,91,54,126,0,27,91,53,126,0,27,79,67,0,27,91,66,0,27,91,65,0,27,79,65,0,27,91,63,49,108,27,62,0,27,91,63,49,104,27,61,0,13,10,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,37,112,49,37,99,27,91,37,112,50,37,123,49,125,37,45,37,100,98,0,27,60,27,91,34,112,27,91,53,48,59,54,34,112,27,99,27,91,63,51,108,27,93,82,27,91,63,49,48,48,48,108,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,49,37,112,54,37,124,37,116,59,49,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,109,37,63,37,112,57,37,116,14,37,101,15,37,59,0,27,72,0,9,0,27,93,48,59,0,27,79,113,0,27,79,115,0,27,79,114,0,27,79,112,0,27,79,110,0,96,96,97,97,102,102,103,103,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,91,90,0,27,91,63,55,104,0,27,91,63,55,108,0,27,40,66,27,41,48,0,27,91,52,126,0,27,79,77,0,26,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,54,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,93,82,0,27,91,60,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,49,48,109,0,27,91,49,49,109,0,27,91,49,50,109,0,37,63,37,112,49,37,123,56,125,37,61,37,116,27,37,37,71,-30,-105,-104,27,37,37,64,37,101,37,112,49,37,123,49,48,125,37,61,37,116,27,37,37,71,-30,-105,-103,27,37,37,64,37,101,37,112,49,37,123,49,50,125,37,61,37,116,27,37,37,71,-30,-103,-128,27,37,37,64,37,101,37,112,49,37,123,49,51,125,37,61,37,116,27,37,37,71,-30,-103,-86,27,37,37,64,37,101,37,112,49,37,123,49,52,125,37,61,37,116,27,37,37,71,-30,-103,-85,27,37,37,64,37,101,37,112,49,37,123,49,53,125,37,61,37,116,27,37,37,71,-30,-104,-68,27,37,37,64,37,101,37,112,49,37,123,50,55,125,37,61,37,116,27,37,37,71,-30,-122,-112,27,37,37,64,37,101,37,112,49,37,123,49,53,53,125,37,61,37,116,27,37,37,71,-32,-126,-94,27,37,37,64,37,101,37,112,49,37,99,37,59,0,27,91,49,49,109,0,27,91,49,48,109,0,1,0,1,0,20,0,42,0,-17,0,1,0,1,0,0,0,0,0,5,0,10,0,42,0,46,0,50,0,54,0,58,0,62,0,66,0,70,0,74,0,78,0,82,0,86,0,90,0,94,0,98,0,102,0,106,0,0,0,3,0,6,0,9,0,12,0,15,0,19,0,23,0,27,0,31,0,35,0,39,0,43,0,47,0,51,0,57,0,63,0,69,0,75,0,81,0,87,0,93,0,27,91,51,74,0,27,93,48,59,0,27,91,63,49,48,48,54,59,49,48,48,48,37,63,37,112,49,37,123,49,125,37,61,37,116,104,37,101,108,37,59,0,27,79,113,0,27,79,114,0,27,79,115,0,27,79,116,0,27,79,117,0,27,79,118,0,27,79,119,0,27,79,120,0,27,79,121,0,27,79,108,0,27,79,81,0,27,79,110,0,27,79,82,0,27,79,80,0,27,79,83,0,27,79,112,0,27,91,60,37,105,37,112,51,37,100,59,37,112,49,37,100,59,37,112,50,37,100,59,37,63,37,112,52,37,116,77,37,101,109,37,59,0,88,84,0,85,56,0,69,51,0,84,83,0,88,77,0,107,112,49,0,107,112,50,0,107,112,51,0,107,112,52,0,107,112,53,0,107,112,54,0,107,112,55,0,107,112,56,0,107,112,57,0,107,112,65,68,68,0,107,112,68,73,86,0,107,112,68,79,84,0,107,112,77,85,76,0,107,112,78,85,77,0,107,112,83,85,66,0,107,112,90,82,79,0,120,109,0
};
// rxvt-256color|rxvt 2.7.9 with xterm 256-colors,
@@ -984,7 +995,6 @@ static const int8_t putty_256colour_terminfo[] = {
// init_1string=\E[?47l\E=\E[?1l,
// init_2string=\E[r\E[m\E[2J\E[H\E[?7h\E[?1;3;4;6l\E[4l,
// initialize_color=\E]4;%p1%d;rgb\072%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E\,
-// insert_character=\E[@,
// insert_line=\E[L,
// key_a1=\EOw,
// key_a3=\EOy,
@@ -1093,7 +1103,7 @@ static const int8_t putty_256colour_terminfo[] = {
// user8=\E[?1;2c,
// user9=\E[c,
static const int8_t rxvt_256colour_terminfo[] = {
- 30,2,47,0,38,0,15,0,110,1,-31,4,114,120,118,116,45,50,53,54,99,111,108,111,114,124,114,120,118,116,32,50,46,55,46,57,32,119,105,116,104,32,120,116,101,114,109,32,50,53,54,45,99,111,108,111,114,115,0,0,1,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,1,0,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,-1,-1,0,0,2,0,4,0,21,0,26,0,34,0,38,0,42,0,-1,-1,53,0,70,0,72,0,76,0,83,0,-1,-1,85,0,92,0,-1,-1,96,0,-1,-1,-1,-1,100,0,-1,-1,-1,-1,104,0,106,0,111,0,116,0,-1,-1,-1,-1,125,0,-1,-1,-1,-1,-126,0,-121,0,-116,0,-1,-1,-111,0,-109,0,-104,0,-1,-1,-91,0,-86,0,-80,0,-74,0,-1,-1,-1,-1,-56,0,-42,0,-1,-1,-1,-1,-8,0,-4,0,-1,-1,0,1,-1,-1,-1,-1,-1,-1,2,1,-1,-1,7,1,-1,-1,11,1,-1,-1,16,1,22,1,28,1,34,1,40,1,46,1,52,1,58,1,64,1,70,1,76,1,82,1,87,1,-1,-1,92,1,-1,-1,96,1,101,1,106,1,110,1,114,1,-1,-1,118,1,122,1,125,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-128,1,-119,1,-110,1,-1,-1,-101,1,-92,1,-83,1,-1,-1,-74,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-65,1,-32,1,-1,-1,-1,-1,18,2,21,2,32,2,35,2,37,2,40,2,107,2,-1,-1,110,2,-1,-1,-1,-1,-1,-1,-1,-1,112,2,116,2,120,2,124,2,-128,2,-1,-1,-1,-1,-124,2,-1,-1,-73,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-69,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-62,2,-57,2,-1,-1,-53,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-48,2,-1,-1,-43,2,-38,2,-1,-1,-1,-1,-1,-1,-1,-1,-33,2,-28,2,-23,2,-1,-1,-1,-1,-19,2,-1,-1,-14,2,-1,-1,-1,-1,-1,-1,-9,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-5,2,1,3,7,3,13,3,19,3,25,3,31,3,37,3,43,3,49,3,55,3,61,3,67,3,73,3,79,3,85,3,91,3,97,3,103,3,109,3,115,3,121,3,127,3,-123,3,-117,3,-111,3,-105,3,-99,3,-93,3,-87,3,-81,3,-75,3,-69,3,-63,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-57,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-52,3,-41,3,-36,3,-28,3,-24,3,-15,3,-8,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,86,4,-1,-1,-1,-1,-1,-1,90,4,-103,4,-1,-1,-1,-1,-1,-1,-39,4,-35,4,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,77,0,14,0,27,91,53,109,0,27,91,49,109,0,27,55,27,91,63,52,55,104,0,27,91,52,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,15,0,27,91,109,15,0,27,91,50,74,27,91,63,52,55,108,27,56,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,63,53,104,36,60,49,48,48,47,62,27,91,63,53,108,0,27,91,63,52,55,108,27,61,27,91,63,49,108,0,27,91,114,27,91,109,27,91,50,74,27,91,72,27,91,63,55,104,27,91,63,49,59,51,59,52,59,54,108,27,91,52,108,0,27,91,64,0,27,91,76,0,8,0,27,91,51,126,0,27,91,66,0,27,91,56,94,0,27,91,50,49,126,0,27,91,49,49,126,0,27,91,50,49,126,0,27,91,49,50,126,0,27,91,49,51,126,0,27,91,49,52,126,0,27,91,49,53,126,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,55,126,0,27,91,50,126,0,27,91,68,0,27,91,54,126,0,27,91,53,126,0,27,91,67,0,27,91,97,0,27,91,98,0,27,91,65,0,27,62,0,27,61,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,65,0,27,62,27,91,49,59,51,59,52,59,53,59,54,108,27,91,63,55,104,27,91,109,27,91,114,27,91,50,74,27,91,72,0,27,91,114,27,91,109,27,91,50,74,27,91,72,27,91,63,55,104,27,91,63,49,59,51,59,52,59,54,108,27,91,52,108,27,62,27,91,63,49,48,48,48,108,27,91,63,50,53,104,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,54,37,116,59,49,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,109,37,63,37,112,57,37,116,14,37,101,15,37,59,0,27,72,0,9,0,27,79,119,0,27,79,121,0,27,79,117,0,27,79,113,0,27,79,115,0,96,96,97,97,102,102,103,103,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,91,90,0,27,40,66,27,41,48,0,27,91,56,126,0,27,79,77,0,27,91,49,126,0,27,91,51,36,0,27,91,52,126,0,27,91,56,36,0,27,91,55,36,0,27,91,50,36,0,27,91,100,0,27,91,54,36,0,27,91,53,36,0,27,91,99,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,50,51,36,0,27,91,50,52,36,0,27,91,49,49,94,0,27,91,49,50,94,0,27,91,49,51,94,0,27,91,49,52,94,0,27,91,49,53,94,0,27,91,49,55,94,0,27,91,49,56,94,0,27,91,49,57,94,0,27,91,50,48,94,0,27,91,50,49,94,0,27,91,50,51,94,0,27,91,50,52,94,0,27,91,50,53,94,0,27,91,50,54,94,0,27,91,50,56,94,0,27,91,50,57,94,0,27,91,51,49,94,0,27,91,51,50,94,0,27,91,51,51,94,0,27,91,51,52,94,0,27,91,50,51,64,0,27,91,50,52,64,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,49,59,50,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,93,49,48,52,7,0,27,93,52,59,37,112,49,37,100,59,114,103,98,58,37,112,50,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,51,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,52,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,27,92,0,27,91,77,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,27,40,66,0,27,40,48,0,0,2,0,0,0,25,0,52,0,-27,0,1,1,-1,-1,-1,-1,0,0,5,0,10,0,14,0,18,0,23,0,28,0,33,0,38,0,43,0,48,0,52,0,57,0,62,0,67,0,72,0,76,0,80,0,84,0,88,0,92,0,96,0,-1,-1,0,0,3,0,6,0,9,0,12,0,17,0,22,0,26,0,31,0,37,0,43,0,49,0,55,0,60,0,65,0,71,0,77,0,83,0,89,0,95,0,101,0,105,0,110,0,114,0,118,0,122,0,126,0,27,91,51,94,0,27,91,51,64,0,27,91,98,0,27,79,98,0,27,91,56,94,0,27,91,56,64,0,27,91,55,94,0,27,91,55,64,0,27,91,50,94,0,27,91,50,64,0,27,79,100,0,27,91,54,94,0,27,91,54,64,0,27,91,53,94,0,27,91,53,64,0,27,79,99,0,27,91,97,0,27,79,97,0,27,79,120,0,27,79,116,0,27,79,118,0,27,79,114,0,65,88,0,88,84,0,84,83,0,88,77,0,107,68,67,53,0,107,68,67,54,0,107,68,78,0,107,68,78,53,0,107,69,78,68,53,0,107,69,78,68,54,0,107,72,79,77,53,0,107,72,79,77,54,0,107,73,67,53,0,107,73,67,54,0,107,76,70,84,53,0,107,78,88,84,53,0,107,78,88,84,54,0,107,80,82,86,53,0,107,80,82,86,54,0,107,82,73,84,53,0,107,85,80,0,107,85,80,53,0,107,97,50,0,107,98,49,0,107,98,51,0,107,99,50,0,120,109,0 // NOLINT
+ 30,2,47,0,38,0,15,0,110,1,-35,4,114,120,118,116,45,50,53,54,99,111,108,111,114,124,114,120,118,116,32,50,46,55,46,57,32,119,105,116,104,32,120,116,101,114,109,32,50,53,54,45,99,111,108,111,114,115,0,0,1,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,1,0,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,-1,-1,0,0,2,0,4,0,21,0,26,0,34,0,38,0,42,0,-1,-1,53,0,70,0,72,0,76,0,83,0,-1,-1,85,0,92,0,-1,-1,96,0,-1,-1,-1,-1,100,0,-1,-1,-1,-1,104,0,106,0,111,0,116,0,-1,-1,-1,-1,125,0,-1,-1,-1,-1,-126,0,-121,0,-116,0,-1,-1,-111,0,-109,0,-104,0,-1,-1,-91,0,-86,0,-80,0,-74,0,-1,-1,-1,-1,-56,0,-42,0,-1,-1,-1,-1,-1,-1,-8,0,-1,-1,-4,0,-1,-1,-1,-1,-1,-1,-2,0,-1,-1,3,1,-1,-1,7,1,-1,-1,12,1,18,1,24,1,30,1,36,1,42,1,48,1,54,1,60,1,66,1,72,1,78,1,83,1,-1,-1,88,1,-1,-1,92,1,97,1,102,1,106,1,110,1,-1,-1,114,1,118,1,121,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,124,1,-123,1,-114,1,-1,-1,-105,1,-96,1,-87,1,-1,-1,-78,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-69,1,-36,1,-1,-1,-1,-1,14,2,17,2,28,2,31,2,33,2,36,2,103,2,-1,-1,106,2,-1,-1,-1,-1,-1,-1,-1,-1,108,2,112,2,116,2,120,2,124,2,-1,-1,-1,-1,-128,2,-1,-1,-77,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-73,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-66,2,-61,2,-1,-1,-57,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-52,2,-1,-1,-47,2,-42,2,-1,-1,-1,-1,-1,-1,-1,-1,-37,2,-32,2,-27,2,-1,-1,-1,-1,-23,2,-1,-1,-18,2,-1,-1,-1,-1,-1,-1,-13,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-9,2,-3,2,3,3,9,3,15,3,21,3,27,3,33,3,39,3,45,3,51,3,57,3,63,3,69,3,75,3,81,3,87,3,93,3,99,3,105,3,111,3,117,3,123,3,-127,3,-121,3,-115,3,-109,3,-103,3,-97,3,-91,3,-85,3,-79,3,-73,3,-67,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-61,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-56,3,-45,3,-40,3,-32,3,-28,3,-19,3,-12,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,82,4,-1,-1,-1,-1,-1,-1,86,4,-107,4,-1,-1,-1,-1,-1,-1,-43,4,-39,4,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,77,0,14,0,27,91,53,109,0,27,91,49,109,0,27,55,27,91,63,52,55,104,0,27,91,52,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,15,0,27,91,109,15,0,27,91,50,74,27,91,63,52,55,108,27,56,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,63,53,104,36,60,49,48,48,47,62,27,91,63,53,108,0,27,91,63,52,55,108,27,61,27,91,63,49,108,0,27,91,114,27,91,109,27,91,50,74,27,91,72,27,91,63,55,104,27,91,63,49,59,51,59,52,59,54,108,27,91,52,108,0,27,91,76,0,8,0,27,91,51,126,0,27,91,66,0,27,91,56,94,0,27,91,50,49,126,0,27,91,49,49,126,0,27,91,50,49,126,0,27,91,49,50,126,0,27,91,49,51,126,0,27,91,49,52,126,0,27,91,49,53,126,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,55,126,0,27,91,50,126,0,27,91,68,0,27,91,54,126,0,27,91,53,126,0,27,91,67,0,27,91,97,0,27,91,98,0,27,91,65,0,27,62,0,27,61,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,65,0,27,62,27,91,49,59,51,59,52,59,53,59,54,108,27,91,63,55,104,27,91,109,27,91,114,27,91,50,74,27,91,72,0,27,91,114,27,91,109,27,91,50,74,27,91,72,27,91,63,55,104,27,91,63,49,59,51,59,52,59,54,108,27,91,52,108,27,62,27,91,63,49,48,48,48,108,27,91,63,50,53,104,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,54,37,116,59,49,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,109,37,63,37,112,57,37,116,14,37,101,15,37,59,0,27,72,0,9,0,27,79,119,0,27,79,121,0,27,79,117,0,27,79,113,0,27,79,115,0,96,96,97,97,102,102,103,103,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,91,90,0,27,40,66,27,41,48,0,27,91,56,126,0,27,79,77,0,27,91,49,126,0,27,91,51,36,0,27,91,52,126,0,27,91,56,36,0,27,91,55,36,0,27,91,50,36,0,27,91,100,0,27,91,54,36,0,27,91,53,36,0,27,91,99,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,50,51,36,0,27,91,50,52,36,0,27,91,49,49,94,0,27,91,49,50,94,0,27,91,49,51,94,0,27,91,49,52,94,0,27,91,49,53,94,0,27,91,49,55,94,0,27,91,49,56,94,0,27,91,49,57,94,0,27,91,50,48,94,0,27,91,50,49,94,0,27,91,50,51,94,0,27,91,50,52,94,0,27,91,50,53,94,0,27,91,50,54,94,0,27,91,50,56,94,0,27,91,50,57,94,0,27,91,51,49,94,0,27,91,51,50,94,0,27,91,51,51,94,0,27,91,51,52,94,0,27,91,50,51,64,0,27,91,50,52,64,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,49,59,50,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,93,49,48,52,7,0,27,93,52,59,37,112,49,37,100,59,114,103,98,58,37,112,50,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,51,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,52,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,27,92,0,27,91,77,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,27,40,66,0,27,40,48,0,0,2,0,0,0,22,0,46,0,-36,0,1,1,0,0,5,0,10,0,14,0,18,0,23,0,28,0,33,0,38,0,43,0,48,0,52,0,57,0,62,0,67,0,72,0,76,0,80,0,84,0,88,0,92,0,96,0,0,0,3,0,6,0,11,0,16,0,20,0,25,0,31,0,37,0,43,0,49,0,54,0,59,0,65,0,71,0,77,0,83,0,89,0,95,0,99,0,104,0,108,0,112,0,116,0,27,91,51,94,0,27,91,51,64,0,27,91,98,0,27,79,98,0,27,91,56,94,0,27,91,56,64,0,27,91,55,94,0,27,91,55,64,0,27,91,50,94,0,27,91,50,64,0,27,79,100,0,27,91,54,94,0,27,91,54,64,0,27,91,53,94,0,27,91,53,64,0,27,79,99,0,27,91,97,0,27,79,97,0,27,79,120,0,27,79,116,0,27,79,118,0,27,79,114,0,65,88,0,88,84,0,107,68,67,53,0,107,68,67,54,0,107,68,78,0,107,68,78,53,0,107,69,78,68,53,0,107,69,78,68,54,0,107,72,79,77,53,0,107,72,79,77,54,0,107,73,67,53,0,107,73,67,54,0,107,76,70,84,53,0,107,78,88,84,53,0,107,78,88,84,54,0,107,80,82,86,53,0,107,80,82,86,54,0,107,82,73,84,53,0,107,85,80,0,107,85,80,53,0,107,97,50,0,107,98,49,0,107,98,51,0,107,99,50,0
};
// screen-256color|GNU Screen with 256 colors,
@@ -1187,6 +1197,7 @@ static const int8_t rxvt_256colour_terminfo[] = {
// parm_insert_line=\E[%p1%dL,
// parm_left_cursor=\E[%p1%dD,
// parm_right_cursor=\E[%p1%dC,
+// parm_rindex=\E[%p1%dT,
// parm_up_cursor=\E[%p1%dA,
// reset_2string=\Ec\E[?1000l\E[?25h,
// restore_cursor=\E8,
@@ -1199,13 +1210,18 @@ static const int8_t rxvt_256colour_terminfo[] = {
// set_attributes=\E[0%?%p6%t;1%;%?%p1%t;3%;%?%p2%t;4%;%?%p3%t;7%;%?%p4%t;5%;%?%p5%t;2%;m%?%p9%t^N%e^O%;,
// set_tab=\EH,
// tab=^I,
+// user6=\E[%i%d;%dR,
+// user7=\E[6n,
+// user8=\E[?1;2c,
+// user9=\E[c,
static const int8_t screen_256colour_terminfo[] = {
- 30,2,43,0,43,0,15,0,105,1,4,3,115,99,114,101,101,110,45,50,53,54,99,111,108,111,114,124,71,78,85,32,83,99,114,101,101,110,32,119,105,116,104,32,50,53,54,32,99,111,108,111,114,115,0,0,1,0,0,1,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,25,0,30,0,37,0,41,0,45,0,-1,-1,56,0,73,0,75,0,79,0,86,0,-1,-1,88,0,100,0,-1,-1,104,0,107,0,113,0,117,0,-1,-1,-1,-1,121,0,123,0,-128,0,-123,0,-1,-1,-114,0,-109,0,-1,-1,-1,-1,-104,0,-99,0,-94,0,-1,-1,-89,0,-87,0,-82,0,-1,-1,-73,0,-68,0,-62,0,-56,0,-1,-1,-1,-1,-1,-1,-53,0,-1,-1,-1,-1,-1,-1,-49,0,-1,-1,-45,0,-1,-1,-1,-1,-1,-1,-43,0,-1,-1,-38,0,-1,-1,-1,-1,-1,-1,-1,-1,-34,0,-30,0,-24,0,-20,0,-16,0,-12,0,-6,0,0,1,6,1,12,1,18,1,23,1,-1,-1,28,1,-1,-1,32,1,37,1,42,1,-1,-1,-1,-1,-1,-1,46,1,50,1,58,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,66,1,-1,-1,69,1,78,1,87,1,96,1,105,1,114,1,123,1,-124,1,-1,-1,-115,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-106,1,-1,-1,-1,-1,-89,1,-86,1,-75,1,-72,1,-70,1,-67,1,17,2,-1,-1,20,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,22,2,-1,-1,87,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,91,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,98,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,103,2,109,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,115,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,120,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-127,2,-1,-1,-1,-1,-1,-1,-123,2,-60,2,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,51,52,104,27,91,63,50,53,104,0,27,91,67,0,27,77,0,27,91,51,52,108,0,27,91,80,0,27,91,77,0,14,0,27,91,53,109,0,27,91,49,109,0,27,91,63,49,48,52,57,104,0,27,91,50,109,0,27,91,52,104,0,27,91,55,109,0,27,91,51,109,0,27,91,52,109,0,15,0,27,91,109,15,0,27,91,63,49,48,52,57,108,0,27,91,52,108,0,27,91,50,51,109,0,27,91,50,52,109,0,27,103,0,27,41,48,0,27,91,76,0,8,0,27,91,51,126,0,27,79,66,0,27,79,80,0,27,91,50,49,126,0,27,79,81,0,27,79,82,0,27,79,83,0,27,91,49,53,126,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,79,68,0,27,91,54,126,0,27,91,53,126,0,27,79,67,0,27,79,65,0,27,91,63,49,108,27,62,0,27,91,63,49,104,27,61,0,27,69,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,65,0,27,99,27,91,63,49,48,48,48,108,27,91,63,50,53,104,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,54,37,116,59,49,37,59,37,63,37,112,49,37,116,59,51,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,51,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,37,63,37,112,53,37,116,59,50,37,59,109,37,63,37,112,57,37,116,14,37,101,15,37,59,0,27,72,0,9,0,43,43,44,44,45,45,46,46,48,48,96,96,97,97,102,102,103,103,104,104,105,105,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,91,90,0,27,40,66,27,41,48,0,27,91,52,126,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,49,75,0,27,91,51,57,59,52,57,109,0,27,91,77,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,3,0,1,0,33,0,70,0,-71,0,1,1,0,0,1,0,0,0,0,0,4,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,29,0,34,0,39,0,44,0,49,0,53,0,58,0,63,0,68,0,73,0,78,0,84,0,90,0,96,0,102,0,108,0,114,0,120,0,126,0,-124,0,-118,0,-112,0,-106,0,-102,0,-98,0,-94,0,-90,0,-86,0,27,40,66,0,27,40,37,112,49,37,99,0,65,88,0,71,48,0,88,84,0,85,56,0,69,48,0,83,48,0,84,83,0,88,77,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,53,0,107,72,79,77,53,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,97,50,0,107,98,49,0,107,98,51,0,107,99,50,0,120,109,0 // NOLINT
+ 30,2,43,0,43,0,15,0,105,1,41,3,115,99,114,101,101,110,45,50,53,54,99,111,108,111,114,124,71,78,85,32,83,99,114,101,101,110,32,119,105,116,104,32,50,53,54,32,99,111,108,111,114,115,0,0,1,0,0,1,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,25,0,30,0,37,0,41,0,45,0,-1,-1,56,0,73,0,75,0,79,0,86,0,-1,-1,88,0,100,0,-1,-1,104,0,107,0,113,0,117,0,-1,-1,-1,-1,121,0,123,0,-128,0,-123,0,-1,-1,-114,0,-109,0,-1,-1,-1,-1,-104,0,-99,0,-94,0,-1,-1,-89,0,-87,0,-82,0,-1,-1,-73,0,-68,0,-62,0,-56,0,-1,-1,-1,-1,-1,-1,-53,0,-1,-1,-1,-1,-1,-1,-49,0,-1,-1,-45,0,-1,-1,-1,-1,-1,-1,-43,0,-1,-1,-38,0,-1,-1,-1,-1,-1,-1,-1,-1,-34,0,-30,0,-24,0,-20,0,-16,0,-12,0,-6,0,0,1,6,1,12,1,18,1,23,1,-1,-1,28,1,-1,-1,32,1,37,1,42,1,-1,-1,-1,-1,-1,-1,46,1,50,1,58,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,66,1,-1,-1,69,1,78,1,87,1,96,1,105,1,114,1,123,1,-124,1,-115,1,-106,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-97,1,-1,-1,-1,-1,-80,1,-77,1,-66,1,-63,1,-61,1,-58,1,26,2,-1,-1,29,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,31,2,-1,-1,96,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,100,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,107,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,112,2,118,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,124,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-127,2,-116,2,-111,2,-103,2,-99,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-90,2,-1,-1,-1,-1,-1,-1,-86,2,-23,2,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,51,52,104,27,91,63,50,53,104,0,27,91,67,0,27,77,0,27,91,51,52,108,0,27,91,80,0,27,91,77,0,14,0,27,91,53,109,0,27,91,49,109,0,27,91,63,49,48,52,57,104,0,27,91,50,109,0,27,91,52,104,0,27,91,55,109,0,27,91,51,109,0,27,91,52,109,0,15,0,27,91,109,15,0,27,91,63,49,48,52,57,108,0,27,91,52,108,0,27,91,50,51,109,0,27,91,50,52,109,0,27,103,0,27,41,48,0,27,91,76,0,8,0,27,91,51,126,0,27,79,66,0,27,79,80,0,27,91,50,49,126,0,27,79,81,0,27,79,82,0,27,79,83,0,27,91,49,53,126,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,79,68,0,27,91,54,126,0,27,91,53,126,0,27,79,67,0,27,79,65,0,27,91,63,49,108,27,62,0,27,91,63,49,104,27,61,0,27,69,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,27,99,27,91,63,49,48,48,48,108,27,91,63,50,53,104,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,54,37,116,59,49,37,59,37,63,37,112,49,37,116,59,51,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,51,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,37,63,37,112,53,37,116,59,50,37,59,109,37,63,37,112,57,37,116,14,37,101,15,37,59,0,27,72,0,9,0,43,43,44,44,45,45,46,46,48,48,96,96,97,97,102,102,103,103,104,104,105,105,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,91,90,0,27,40,66,27,41,48,0,27,91,52,126,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,49,59,50,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,91,77,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,0,2,0,1,0,2,0,7,0,27,0,1,1,1,0,0,0,0,0,4,0,0,0,3,0,6,0,9,0,12,0,27,40,66,0,27,40,37,112,49,37,99,0,65,88,0,71,48,0,85,56,0,69,48,0,83,48,0
};
// st-256color|stterm-256color|simpleterm with 256 colors,
// auto_right_margin,
// back_color_erase,
+// can_change,
// eat_newline_glitch,
// has_status_line,
// move_insert_mode,
@@ -1232,10 +1248,9 @@ static const int8_t screen_256colour_terminfo[] = {
// cursor_home=\E[H,
// cursor_invisible=\E[?25l,
// cursor_left=^H,
-// cursor_normal=\E[?12l\E[?25h,
+// cursor_normal=\E[?25h,
// cursor_right=\E[C,
// cursor_up=\E[A,
-// cursor_visible=\E[?25h,
// delete_character=\E[P,
// delete_line=\E[M,
// dis_status_line=\E]0;^G,
@@ -1262,7 +1277,7 @@ static const int8_t screen_256colour_terminfo[] = {
// flash_screen=\E[?5h$<100/>\E[?5l,
// from_status_line=^G,
// init_2string=\E[4l\E>\E[?1034l,
-// initialize_color@,
+// initialize_color=\E]4;%p1%d;rgb\072%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E\,
// insert_line=\E[L,
// key_a1=\E[1~,
// key_a3=\E[5~,
@@ -1362,7 +1377,7 @@ static const int8_t screen_256colour_terminfo[] = {
// key_up=\EOA,
// keypad_local=\E[?1l\E>,
// keypad_xmit=\E[?1h\E=,
-// orig_colors@,
+// orig_colors=\E]104^G,
// orig_pair=\E[39;49m,
// parm_dch=\E[%p1%dP,
// parm_delete_line=\E[%p1%dM,
@@ -1372,6 +1387,7 @@ static const int8_t screen_256colour_terminfo[] = {
// parm_insert_line=\E[%p1%dL,
// parm_left_cursor=\E[%p1%dD,
// parm_right_cursor=\E[%p1%dC,
+// parm_rindex=\E[%p1%dT,
// parm_up_cursor=\E[%p1%dA,
// print_screen=\E[i,
// prtr_off=\E[4i,
@@ -1394,7 +1410,7 @@ static const int8_t screen_256colour_terminfo[] = {
// user8=\E[?1;2c,
// user9=\E[c,
static const int8_t st_256colour_terminfo[] = {
- 30,2,55,0,29,0,15,0,105,1,-125,5,115,116,45,50,53,54,99,111,108,111,114,124,115,116,116,101,114,109,45,50,53,54,99,111,108,111,114,124,115,105,109,112,108,101,116,101,114,109,32,119,105,116,104,32,50,53,54,32,99,111,108,111,114,115,0,0,1,0,0,1,0,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,25,0,30,0,38,0,42,0,46,0,-1,-1,57,0,74,0,76,0,80,0,87,0,-1,-1,89,0,102,0,-1,-1,106,0,110,0,117,0,121,0,125,0,-1,-1,-125,0,-121,0,-116,0,-111,0,-1,-1,-102,0,-97,0,-92,0,-1,-1,-87,0,-82,0,-77,0,-72,0,-63,0,-59,0,-54,0,-1,-1,-45,0,-40,0,-34,0,-28,0,-1,-1,-10,0,-1,-1,-8,0,-1,-1,-1,-1,-1,-1,7,1,-1,-1,11,1,-1,-1,13,1,-1,-1,20,1,25,1,32,1,36,1,43,1,50,1,-1,-1,57,1,61,1,67,1,71,1,75,1,79,1,85,1,91,1,97,1,103,1,109,1,114,1,119,1,126,1,-1,-1,-126,1,-121,1,-116,1,-112,1,-105,1,-1,-1,-98,1,-94,1,-86,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-78,1,-69,1,-60,1,-51,1,-42,1,-33,1,-24,1,-15,1,-1,-1,-6,1,-1,-1,-1,-1,-1,-1,3,2,7,2,12,2,-1,-1,17,2,20,2,-1,-1,-1,-1,35,2,38,2,49,2,52,2,54,2,57,2,-106,2,-1,-1,-103,2,-101,2,-1,-1,-1,-1,-1,-1,-96,2,-91,2,-86,2,-82,2,-77,2,-1,-1,-1,-1,-72,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-7,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-3,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,2,3,-1,-1,-1,-1,9,3,-1,-1,-1,-1,-1,-1,-1,-1,16,3,23,3,30,3,-1,-1,-1,-1,37,3,-1,-1,44,3,-1,-1,-1,-1,-1,-1,51,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,58,3,64,3,70,3,77,3,84,3,91,3,98,3,106,3,114,3,122,3,-126,3,-118,3,-110,3,-102,3,-94,3,-87,3,-80,3,-73,3,-66,3,-58,3,-50,3,-42,3,-34,3,-26,3,-18,3,-10,3,-2,3,5,4,12,4,19,4,26,4,34,4,42,4,50,4,58,4,66,4,74,4,82,4,90,4,97,4,104,4,111,4,118,4,126,4,-122,4,-114,4,-106,4,-98,4,-90,4,-82,4,-74,4,-67,4,-60,4,-53,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-48,4,-37,4,-32,4,-24,4,-20,4,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-11,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-6,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,5,-1,-1,-1,-1,-1,-1,4,5,67,5,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,49,50,108,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,63,50,53,104,0,27,91,80,0,27,91,77,0,27,93,48,59,7,0,27,40,48,0,27,91,53,109,0,27,91,49,109,0,27,91,63,49,48,52,57,104,0,27,91,50,109,0,27,91,52,104,0,27,91,56,109,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,27,40,66,0,27,91,48,109,0,27,91,63,49,48,52,57,108,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,63,53,104,36,60,49,48,48,47,62,27,91,63,53,108,0,7,0,27,91,52,108,27,62,27,91,63,49,48,51,52,108,0,27,91,76,0,127,0,27,91,51,59,53,126,0,27,91,51,126,0,27,91,51,59,50,126,0,27,79,66,0,27,91,50,59,50,126,0,27,91,49,59,50,70,0,27,91,49,59,53,70,0,27,79,80,0,27,91,50,49,126,0,27,79,81,0,27,79,82,0,27,79,83,0,27,91,49,53,126,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,91,50,59,53,126,0,27,79,68,0,27,91,54,126,0,27,91,53,126,0,27,79,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,79,65,0,27,91,63,49,108,27,62,0,27,91,63,49,104,27,61,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,65,0,27,91,105,0,27,91,52,105,0,27,91,53,105,0,27,99,0,27,91,52,108,27,62,27,91,63,49,48,51,52,108,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,37,63,37,112,57,37,116,27,40,48,37,101,27,40,66,37,59,27,91,48,37,63,37,112,54,37,116,59,49,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,37,63,37,112,53,37,116,59,50,37,59,37,63,37,112,55,37,116,59,56,37,59,109,0,27,72,0,9,0,27,93,48,59,0,27,91,49,126,0,27,91,53,126,0,27,79,117,0,27,91,52,126,0,27,91,54,126,0,43,67,44,68,45,65,46,66,48,69,96,96,97,97,102,102,103,103,104,70,105,71,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,41,48,0,27,91,52,126,0,27,91,51,59,50,126,0,27,91,49,59,50,70,0,27,91,49,59,50,72,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,49,59,50,80,0,27,91,49,59,50,81,0,27,91,49,59,50,82,0,27,91,49,59,50,83,0,27,91,49,53,59,50,126,0,27,91,49,55,59,50,126,0,27,91,49,56,59,50,126,0,27,91,49,57,59,50,126,0,27,91,50,48,59,50,126,0,27,91,50,49,59,50,126,0,27,91,50,51,59,50,126,0,27,91,50,52,59,50,126,0,27,91,49,59,53,80,0,27,91,49,59,53,81,0,27,91,49,59,53,82,0,27,91,49,59,53,83,0,27,91,49,53,59,53,126,0,27,91,49,55,59,53,126,0,27,91,49,56,59,53,126,0,27,91,49,57,59,53,126,0,27,91,50,48,59,53,126,0,27,91,50,49,59,53,126,0,27,91,50,51,59,53,126,0,27,91,50,52,59,53,126,0,27,91,49,59,54,80,0,27,91,49,59,54,81,0,27,91,49,59,54,82,0,27,91,49,59,54,83,0,27,91,49,53,59,54,126,0,27,91,49,55,59,54,126,0,27,91,49,56,59,54,126,0,27,91,49,57,59,54,126,0,27,91,50,48,59,54,126,0,27,91,50,49,59,54,126,0,27,91,50,51,59,54,126,0,27,91,50,52,59,54,126,0,27,91,49,59,51,80,0,27,91,49,59,51,81,0,27,91,49,59,51,82,0,27,91,49,59,51,83,0,27,91,49,53,59,51,126,0,27,91,49,55,59,51,126,0,27,91,49,56,59,51,126,0,27,91,49,57,59,51,126,0,27,91,50,48,59,51,126,0,27,91,50,49,59,51,126,0,27,91,50,51,59,51,126,0,27,91,50,52,59,51,126,0,27,91,49,59,52,80,0,27,91,49,59,52,81,0,27,91,49,59,52,82,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,49,59,50,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,91,51,109,0,27,91,50,51,109,0,27,91,77,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,0,3,0,1,0,71,0,-110,0,-1,1,0,0,1,0,-1,-1,-1,-1,-1,-1,-1,-1,0,0,-1,-1,18,0,24,0,34,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,39,0,-1,-1,46,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,53,0,-1,-1,60,0,-1,-1,-1,-1,67,0,-1,-1,74,0,-1,-1,-1,-1,81,0,-1,-1,88,0,-1,-1,-1,-1,95,0,-1,-1,102,0,-1,-1,-1,-1,-1,-1,109,0,-1,-1,116,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,123,0,-127,0,-1,-1,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,30,0,33,0,36,0,42,0,48,0,53,0,58,0,63,0,68,0,73,0,77,0,82,0,87,0,92,0,97,0,102,0,108,0,114,0,120,0,126,0,-124,0,-118,0,-112,0,-106,0,-100,0,-94,0,-88,0,-82,0,-77,0,-72,0,-67,0,-62,0,-57,0,-51,0,-45,0,-39,0,-33,0,-27,0,-21,0,-15,0,-9,0,-3,0,3,1,9,1,15,1,21,1,27,1,33,1,39,1,45,1,51,1,57,1,63,1,67,1,72,1,77,1,82,1,87,1,92,1,96,1,100,1,104,1,108,1,113,1,118,1,27,93,53,50,59,37,112,49,37,115,59,37,112,50,37,115,7,0,27,91,50,32,113,0,27,91,37,112,49,37,100,32,113,0,27,93,48,59,0,27,91,49,59,51,66,0,27,91,49,59,53,66,0,27,91,49,59,51,68,0,27,91,49,59,53,68,0,27,91,54,59,51,126,0,27,91,54,59,53,126,0,27,91,53,59,51,126,0,27,91,53,59,53,126,0,27,91,49,59,51,67,0,27,91,49,59,53,67,0,27,91,49,59,51,65,0,27,91,49,59,53,65,0,27,91,50,57,109,0,27,91,57,109,0,65,88,0,71,48,0,88,84,0,85,56,0,69,48,0,69,51,0,77,115,0,83,48,0,83,101,0,83,115,0,84,83,0,88,77,0,103,114,98,111,109,0,103,115,98,111,109,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,69,78,68,56,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,72,79,77,56,0,107,73,67,51,0,107,73,67,52,0,107,73,67,53,0,107,73,67,54,0,107,73,67,55,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,78,88,84,51,0,107,78,88,84,52,0,107,78,88,84,53,0,107,78,88,84,54,0,107,78,88,84,55,0,107,80,82,86,51,0,107,80,82,86,52,0,107,80,82,86,53,0,107,80,82,86,54,0,107,80,82,86,55,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,107,85,80,55,0,107,97,50,0,107,98,49,0,107,98,51,0,107,99,50,0,114,109,120,120,0,115,109,120,120,0,120,109,0 // NOLINT
+ 30,2,55,0,29,0,15,0,105,1,-28,5,115,116,45,50,53,54,99,111,108,111,114,124,115,116,116,101,114,109,45,50,53,54,99,111,108,111,114,124,115,105,109,112,108,101,116,101,114,109,32,119,105,116,104,32,50,53,54,32,99,111,108,111,114,115,0,0,1,0,0,1,0,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,0,1,1,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,25,0,30,0,38,0,42,0,46,0,-1,-1,57,0,74,0,76,0,80,0,87,0,-1,-1,89,0,96,0,-1,-1,100,0,-1,-1,104,0,108,0,112,0,-1,-1,118,0,122,0,127,0,-124,0,-1,-1,-115,0,-110,0,-105,0,-1,-1,-100,0,-95,0,-90,0,-85,0,-76,0,-72,0,-67,0,-1,-1,-58,0,-53,0,-47,0,-41,0,-1,-1,-23,0,-1,-1,-21,0,-1,-1,-1,-1,-1,-1,-6,0,-1,-1,-2,0,-1,-1,0,1,-1,-1,7,1,12,1,19,1,23,1,30,1,37,1,-1,-1,44,1,48,1,54,1,58,1,62,1,66,1,72,1,78,1,84,1,90,1,96,1,101,1,106,1,113,1,-1,-1,117,1,122,1,127,1,-125,1,-118,1,-1,-1,-111,1,-107,1,-99,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-91,1,-82,1,-73,1,-64,1,-55,1,-46,1,-37,1,-28,1,-19,1,-10,1,-1,-1,-1,-1,-1,-1,-1,1,3,2,8,2,-1,-1,13,2,16,2,-1,-1,-1,-1,31,2,34,2,45,2,48,2,50,2,53,2,-110,2,-1,-1,-107,2,-105,2,-1,-1,-1,-1,-1,-1,-100,2,-95,2,-90,2,-86,2,-81,2,-1,-1,-1,-1,-76,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-11,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-7,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,2,-1,-1,-1,-1,5,3,-1,-1,-1,-1,-1,-1,-1,-1,12,3,19,3,26,3,-1,-1,-1,-1,33,3,-1,-1,40,3,-1,-1,-1,-1,-1,-1,47,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,54,3,60,3,66,3,73,3,80,3,87,3,94,3,102,3,110,3,118,3,126,3,-122,3,-114,3,-106,3,-98,3,-91,3,-84,3,-77,3,-70,3,-62,3,-54,3,-46,3,-38,3,-30,3,-22,3,-14,3,-6,3,1,4,8,4,15,4,22,4,30,4,38,4,46,4,54,4,62,4,70,4,78,4,86,4,93,4,100,4,107,4,114,4,122,4,-126,4,-118,4,-110,4,-102,4,-94,4,-86,4,-78,4,-71,4,-64,4,-57,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-52,4,-41,4,-36,4,-28,4,-24,4,-15,4,-8,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,86,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,91,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,97,5,-1,-1,-1,-1,-1,-1,101,5,-92,5,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,27,93,48,59,7,0,27,40,48,0,27,91,53,109,0,27,91,49,109,0,27,91,63,49,48,52,57,104,0,27,91,50,109,0,27,91,52,104,0,27,91,56,109,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,27,40,66,0,27,91,48,109,0,27,91,63,49,48,52,57,108,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,63,53,104,36,60,49,48,48,47,62,27,91,63,53,108,0,7,0,27,91,52,108,27,62,27,91,63,49,48,51,52,108,0,27,91,76,0,127,0,27,91,51,59,53,126,0,27,91,51,126,0,27,91,51,59,50,126,0,27,79,66,0,27,91,50,59,50,126,0,27,91,49,59,50,70,0,27,91,49,59,53,70,0,27,79,80,0,27,91,50,49,126,0,27,79,81,0,27,79,82,0,27,79,83,0,27,91,49,53,126,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,91,50,59,53,126,0,27,79,68,0,27,91,54,126,0,27,91,53,126,0,27,79,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,79,65,0,27,91,63,49,108,27,62,0,27,91,63,49,104,27,61,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,27,91,105,0,27,91,52,105,0,27,91,53,105,0,27,99,0,27,91,52,108,27,62,27,91,63,49,48,51,52,108,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,37,63,37,112,57,37,116,27,40,48,37,101,27,40,66,37,59,27,91,48,37,63,37,112,54,37,116,59,49,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,37,63,37,112,53,37,116,59,50,37,59,37,63,37,112,55,37,116,59,56,37,59,109,0,27,72,0,9,0,27,93,48,59,0,27,91,49,126,0,27,91,53,126,0,27,79,117,0,27,91,52,126,0,27,91,54,126,0,43,67,44,68,45,65,46,66,48,69,96,96,97,97,102,102,103,103,104,70,105,71,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,41,48,0,27,91,52,126,0,27,91,51,59,50,126,0,27,91,49,59,50,70,0,27,91,49,59,50,72,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,49,59,50,80,0,27,91,49,59,50,81,0,27,91,49,59,50,82,0,27,91,49,59,50,83,0,27,91,49,53,59,50,126,0,27,91,49,55,59,50,126,0,27,91,49,56,59,50,126,0,27,91,49,57,59,50,126,0,27,91,50,48,59,50,126,0,27,91,50,49,59,50,126,0,27,91,50,51,59,50,126,0,27,91,50,52,59,50,126,0,27,91,49,59,53,80,0,27,91,49,59,53,81,0,27,91,49,59,53,82,0,27,91,49,59,53,83,0,27,91,49,53,59,53,126,0,27,91,49,55,59,53,126,0,27,91,49,56,59,53,126,0,27,91,49,57,59,53,126,0,27,91,50,48,59,53,126,0,27,91,50,49,59,53,126,0,27,91,50,51,59,53,126,0,27,91,50,52,59,53,126,0,27,91,49,59,54,80,0,27,91,49,59,54,81,0,27,91,49,59,54,82,0,27,91,49,59,54,83,0,27,91,49,53,59,54,126,0,27,91,49,55,59,54,126,0,27,91,49,56,59,54,126,0,27,91,49,57,59,54,126,0,27,91,50,48,59,54,126,0,27,91,50,49,59,54,126,0,27,91,50,51,59,54,126,0,27,91,50,52,59,54,126,0,27,91,49,59,51,80,0,27,91,49,59,51,81,0,27,91,49,59,51,82,0,27,91,49,59,51,83,0,27,91,49,53,59,51,126,0,27,91,49,55,59,51,126,0,27,91,49,56,59,51,126,0,27,91,49,57,59,51,126,0,27,91,50,48,59,51,126,0,27,91,50,49,59,51,126,0,27,91,50,51,59,51,126,0,27,91,50,52,59,51,126,0,27,91,49,59,52,80,0,27,91,49,59,52,81,0,27,91,49,59,52,82,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,49,59,50,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,93,49,48,52,7,0,27,93,52,59,37,112,49,37,100,59,114,103,98,58,37,112,50,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,51,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,52,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,27,92,0,27,91,51,109,0,27,91,50,51,109,0,27,91,77,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,1,0,0,0,18,0,37,0,-29,0,1,0,0,0,18,0,24,0,34,0,39,0,46,0,53,0,60,0,67,0,74,0,81,0,88,0,95,0,102,0,109,0,116,0,123,0,-127,0,0,0,3,0,6,0,9,0,12,0,15,0,20,0,25,0,31,0,37,0,43,0,49,0,55,0,61,0,67,0,73,0,78,0,83,0,88,0,27,93,53,50,59,37,112,49,37,115,59,37,112,50,37,115,7,0,27,91,50,32,113,0,27,91,37,112,49,37,100,32,113,0,27,93,48,59,0,27,91,49,59,51,66,0,27,91,49,59,53,66,0,27,91,49,59,51,68,0,27,91,49,59,53,68,0,27,91,54,59,51,126,0,27,91,54,59,53,126,0,27,91,53,59,51,126,0,27,91,53,59,53,126,0,27,91,49,59,51,67,0,27,91,49,59,53,67,0,27,91,49,59,51,65,0,27,91,49,59,53,65,0,27,91,50,57,109,0,27,91,57,109,0,88,84,0,77,115,0,83,101,0,83,115,0,84,83,0,107,68,78,51,0,107,68,78,53,0,107,76,70,84,51,0,107,76,70,84,53,0,107,78,88,84,51,0,107,78,88,84,53,0,107,80,82,86,51,0,107,80,82,86,53,0,107,82,73,84,51,0,107,82,73,84,53,0,107,85,80,51,0,107,85,80,53,0,114,109,120,120,0,115,109,120,120,0
};
// tmux-256color|tmux with 256 colors,
@@ -1457,7 +1473,7 @@ static const int8_t st_256colour_terminfo[] = {
// from_status_line=^G,
// init_2string=\E)0,
// insert_line=\E[L,
-// key_backspace=^H,
+// key_backspace=\177,
// key_btab=\E[Z,
// key_dc=\E[3~,
// key_down=\EOB,
@@ -1568,8 +1584,12 @@ static const int8_t st_256colour_terminfo[] = {
// set_tab=\EH,
// tab=^I,
// to_status_line=\E]0;,
+// user6=\E[%i%d;%dR,
+// user7=\E[6n,
+// user8=\E[?1;2c,
+// user9=\E[c,
static const int8_t tmux_256colour_terminfo[] = {
- 30,2,35,0,43,0,15,0,105,1,-15,4,116,109,117,120,45,50,53,54,99,111,108,111,114,124,116,109,117,120,32,119,105,116,104,32,50,53,54,32,99,111,108,111,114,115,0,0,1,0,0,1,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,25,0,30,0,37,0,41,0,45,0,-1,-1,56,0,73,0,75,0,79,0,86,0,-1,-1,88,0,100,0,-1,-1,104,0,107,0,113,0,117,0,121,0,-1,-1,127,0,-127,0,-122,0,-117,0,-1,-1,-108,0,-103,0,-98,0,-1,-1,-93,0,-88,0,-83,0,-1,-1,-78,0,-76,0,-71,0,-1,-1,-62,0,-57,0,-51,0,-45,0,-1,-1,-42,0,-1,-1,-40,0,-1,-1,-1,-1,-1,-1,-36,0,-1,-1,-32,0,-1,-1,-1,-1,-1,-1,-30,0,-1,-1,-25,0,-1,-1,-1,-1,-1,-1,-1,-1,-21,0,-17,0,-11,0,-7,0,-3,0,1,1,7,1,13,1,19,1,25,1,31,1,36,1,-1,-1,41,1,-1,-1,45,1,50,1,55,1,59,1,66,1,-1,-1,73,1,77,1,85,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,93,1,-1,-1,96,1,105,1,114,1,123,1,-124,1,-115,1,-106,1,-97,1,-1,-1,-88,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-79,1,-1,-1,-1,-1,-62,1,-59,1,-48,1,-45,1,-43,1,-40,1,49,2,-1,-1,52,2,54,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,59,2,-1,-1,124,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-128,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-121,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-116,2,-1,-1,-1,-1,-109,2,-1,-1,-1,-1,-1,-1,-1,-1,-102,2,-95,2,-88,2,-1,-1,-1,-1,-81,2,-1,-1,-74,2,-1,-1,-1,-1,-1,-1,-67,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-60,2,-54,2,-48,2,-41,2,-34,2,-27,2,-20,2,-12,2,-4,2,4,3,12,3,20,3,28,3,36,3,44,3,51,3,58,3,65,3,72,3,80,3,88,3,96,3,104,3,112,3,120,3,-128,3,-120,3,-113,3,-106,3,-99,3,-92,3,-84,3,-76,3,-68,3,-60,3,-52,3,-44,3,-36,3,-28,3,-21,3,-14,3,-7,3,0,4,8,4,16,4,24,4,32,4,40,4,48,4,56,4,64,4,71,4,78,4,85,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,90,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,99,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,104,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,110,4,-1,-1,-1,-1,-1,-1,114,4,-79,4,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,51,52,104,27,91,63,50,53,104,0,27,91,67,0,27,77,0,27,91,51,52,108,0,27,91,80,0,27,91,77,0,27,93,48,59,7,0,14,0,27,91,53,109,0,27,91,49,109,0,27,91,63,49,48,52,57,104,0,27,91,50,109,0,27,91,52,104,0,27,91,56,109,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,15,0,27,91,109,15,0,27,91,63,49,48,52,57,108,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,103,0,7,0,27,41,48,0,27,91,76,0,8,0,27,91,51,126,0,27,79,66,0,27,79,80,0,27,91,50,49,126,0,27,79,81,0,27,79,82,0,27,79,83,0,27,91,49,53,126,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,79,68,0,27,91,54,126,0,27,91,53,126,0,27,79,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,79,65,0,27,91,63,49,108,27,62,0,27,91,63,49,104,27,61,0,27,69,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,65,0,27,99,27,91,63,49,48,48,48,108,27,91,63,50,53,104,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,54,37,116,59,49,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,37,63,37,112,53,37,116,59,50,37,59,37,63,37,112,55,37,116,59,56,37,59,109,37,63,37,112,57,37,116,14,37,101,15,37,59,0,27,72,0,9,0,27,93,48,59,0,43,43,44,44,45,45,46,46,48,48,96,96,97,97,102,102,103,103,104,104,105,105,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,91,90,0,27,40,66,27,41,48,0,27,91,52,126,0,27,91,51,59,50,126,0,27,91,49,59,50,70,0,27,91,49,59,50,72,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,49,59,50,80,0,27,91,49,59,50,81,0,27,91,49,59,50,82,0,27,91,49,59,50,83,0,27,91,49,53,59,50,126,0,27,91,49,55,59,50,126,0,27,91,49,56,59,50,126,0,27,91,49,57,59,50,126,0,27,91,50,48,59,50,126,0,27,91,50,49,59,50,126,0,27,91,50,51,59,50,126,0,27,91,50,52,59,50,126,0,27,91,49,59,53,80,0,27,91,49,59,53,81,0,27,91,49,59,53,82,0,27,91,49,59,53,83,0,27,91,49,53,59,53,126,0,27,91,49,55,59,53,126,0,27,91,49,56,59,53,126,0,27,91,49,57,59,53,126,0,27,91,50,48,59,53,126,0,27,91,50,49,59,53,126,0,27,91,50,51,59,53,126,0,27,91,50,52,59,53,126,0,27,91,49,59,54,80,0,27,91,49,59,54,81,0,27,91,49,59,54,82,0,27,91,49,59,54,83,0,27,91,49,53,59,54,126,0,27,91,49,55,59,54,126,0,27,91,49,56,59,54,126,0,27,91,49,57,59,54,126,0,27,91,50,48,59,54,126,0,27,91,50,49,59,54,126,0,27,91,50,51,59,54,126,0,27,91,50,52,59,54,126,0,27,91,49,59,51,80,0,27,91,49,59,51,81,0,27,91,49,59,51,82,0,27,91,49,59,51,83,0,27,91,49,53,59,51,126,0,27,91,49,55,59,51,126,0,27,91,49,56,59,51,126,0,27,91,49,57,59,51,126,0,27,91,50,48,59,51,126,0,27,91,50,49,59,51,126,0,27,91,50,51,59,51,126,0,27,91,50,52,59,51,126,0,27,91,49,59,52,80,0,27,91,49,59,52,81,0,27,91,49,59,52,82,0,27,91,49,75,0,27,91,51,57,59,52,57,109,0,27,91,51,109,0,27,91,50,51,109,0,27,91,77,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,0,3,0,1,0,73,0,-106,0,65,3,1,1,0,0,1,0,0,0,0,0,7,0,19,0,23,0,28,0,46,0,54,0,60,0,70,0,-1,-1,-1,-1,-1,-1,75,0,82,0,89,0,96,0,103,0,110,0,117,0,124,0,-125,0,-118,0,-111,0,-104,0,-97,0,-90,0,-83,0,-76,0,-1,-1,-69,0,-62,0,-55,0,-48,0,-41,0,-1,-1,-34,0,-27,0,-20,0,-13,0,-6,0,1,1,8,1,15,1,22,1,29,1,36,1,43,1,50,1,57,1,64,1,71,1,78,1,85,1,92,1,99,1,106,1,113,1,120,1,127,1,-122,1,-115,1,-108,1,-101,1,-94,1,-87,1,-80,1,-1,-1,-1,-1,-1,-1,-1,-1,-73,1,-67,1,-1,-1,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,30,0,33,0,36,0,39,0,42,0,48,0,54,0,59,0,64,0,69,0,74,0,79,0,83,0,88,0,93,0,98,0,103,0,108,0,114,0,120,0,126,0,-124,0,-118,0,-112,0,-106,0,-100,0,-94,0,-88,0,-82,0,-76,0,-71,0,-66,0,-61,0,-56,0,-51,0,-45,0,-39,0,-33,0,-27,0,-21,0,-15,0,-9,0,-3,0,3,1,9,1,15,1,21,1,27,1,33,1,39,1,45,1,51,1,57,1,63,1,69,1,73,1,78,1,83,1,88,1,93,1,98,1,102,1,106,1,110,1,114,1,119,1,124,1,27,93,49,49,50,7,0,27,93,49,50,59,37,112,49,37,115,7,0,27,40,66,0,27,91,51,74,0,27,93,53,50,59,37,112,49,37,115,59,37,112,50,37,115,7,0,27,40,37,112,49,37,99,0,27,91,50,32,113,0,27,91,37,112,49,37,100,32,113,0,27,93,48,59,0,27,91,51,59,51,126,0,27,91,51,59,52,126,0,27,91,51,59,53,126,0,27,91,51,59,54,126,0,27,91,51,59,55,126,0,27,91,49,59,50,66,0,27,91,49,59,51,66,0,27,91,49,59,52,66,0,27,91,49,59,53,66,0,27,91,49,59,54,66,0,27,91,49,59,55,66,0,27,91,49,59,51,70,0,27,91,49,59,52,70,0,27,91,49,59,53,70,0,27,91,49,59,54,70,0,27,91,49,59,55,70,0,27,91,49,59,51,72,0,27,91,49,59,52,72,0,27,91,49,59,53,72,0,27,91,49,59,54,72,0,27,91,49,59,55,72,0,27,91,50,59,51,126,0,27,91,50,59,52,126,0,27,91,50,59,53,126,0,27,91,50,59,54,126,0,27,91,50,59,55,126,0,27,91,49,59,51,68,0,27,91,49,59,52,68,0,27,91,49,59,53,68,0,27,91,49,59,54,68,0,27,91,49,59,55,68,0,27,91,54,59,51,126,0,27,91,54,59,52,126,0,27,91,54,59,53,126,0,27,91,54,59,54,126,0,27,91,54,59,55,126,0,27,91,53,59,51,126,0,27,91,53,59,52,126,0,27,91,53,59,53,126,0,27,91,53,59,54,126,0,27,91,53,59,55,126,0,27,91,49,59,51,67,0,27,91,49,59,52,67,0,27,91,49,59,53,67,0,27,91,49,59,54,67,0,27,91,49,59,55,67,0,27,91,49,59,50,65,0,27,91,49,59,51,65,0,27,91,49,59,52,65,0,27,91,49,59,53,65,0,27,91,49,59,54,65,0,27,91,49,59,55,65,0,27,91,50,57,109,0,27,91,57,109,0,65,88,0,71,48,0,88,84,0,85,56,0,67,114,0,67,115,0,69,48,0,69,51,0,77,115,0,83,48,0,83,101,0,83,115,0,84,83,0,88,77,0,103,114,98,111,109,0,103,115,98,111,109,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,69,78,68,56,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,72,79,77,56,0,107,73,67,51,0,107,73,67,52,0,107,73,67,53,0,107,73,67,54,0,107,73,67,55,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,78,88,84,51,0,107,78,88,84,52,0,107,78,88,84,53,0,107,78,88,84,54,0,107,78,88,84,55,0,107,80,82,86,51,0,107,80,82,86,52,0,107,80,82,86,53,0,107,80,82,86,54,0,107,80,82,86,55,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,107,85,80,55,0,107,97,50,0,107,98,49,0,107,98,51,0,107,99,50,0,114,109,120,120,0,115,109,120,120,0,120,109,0 // NOLINT
+ 30,2,35,0,43,0,15,0,105,1,13,5,116,109,117,120,45,50,53,54,99,111,108,111,114,124,116,109,117,120,32,119,105,116,104,32,50,53,54,32,99,111,108,111,114,115,0,0,1,0,0,1,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,25,0,30,0,37,0,41,0,45,0,-1,-1,56,0,73,0,75,0,79,0,86,0,-1,-1,88,0,100,0,-1,-1,104,0,107,0,113,0,117,0,121,0,-1,-1,127,0,-127,0,-122,0,-117,0,-1,-1,-108,0,-103,0,-98,0,-1,-1,-93,0,-88,0,-83,0,-1,-1,-78,0,-76,0,-71,0,-1,-1,-62,0,-57,0,-51,0,-45,0,-1,-1,-42,0,-1,-1,-40,0,-1,-1,-1,-1,-1,-1,-36,0,-1,-1,-32,0,-1,-1,-1,-1,-1,-1,-30,0,-1,-1,-25,0,-1,-1,-1,-1,-1,-1,-1,-1,-21,0,-17,0,-11,0,-7,0,-3,0,1,1,7,1,13,1,19,1,25,1,31,1,36,1,-1,-1,41,1,-1,-1,45,1,50,1,55,1,59,1,66,1,-1,-1,73,1,77,1,85,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,93,1,-1,-1,96,1,105,1,114,1,123,1,-124,1,-115,1,-106,1,-97,1,-1,-1,-88,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-79,1,-1,-1,-1,-1,-62,1,-59,1,-48,1,-45,1,-43,1,-40,1,49,2,-1,-1,52,2,54,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,59,2,-1,-1,124,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-128,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-121,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-116,2,-1,-1,-1,-1,-109,2,-1,-1,-1,-1,-1,-1,-1,-1,-102,2,-95,2,-88,2,-1,-1,-1,-1,-81,2,-1,-1,-74,2,-1,-1,-1,-1,-1,-1,-67,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-60,2,-54,2,-48,2,-41,2,-34,2,-27,2,-20,2,-12,2,-4,2,4,3,12,3,20,3,28,3,36,3,44,3,51,3,58,3,65,3,72,3,80,3,88,3,96,3,104,3,112,3,120,3,-128,3,-120,3,-113,3,-106,3,-99,3,-92,3,-84,3,-76,3,-68,3,-60,3,-52,3,-44,3,-36,3,-28,3,-21,3,-14,3,-7,3,0,4,8,4,16,4,24,4,32,4,40,4,48,4,56,4,64,4,71,4,78,4,85,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,90,4,101,4,106,4,114,4,118,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,127,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-124,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-118,4,-1,-1,-1,-1,-1,-1,-114,4,-51,4,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,51,52,104,27,91,63,50,53,104,0,27,91,67,0,27,77,0,27,91,51,52,108,0,27,91,80,0,27,91,77,0,27,93,48,59,7,0,14,0,27,91,53,109,0,27,91,49,109,0,27,91,63,49,48,52,57,104,0,27,91,50,109,0,27,91,52,104,0,27,91,56,109,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,15,0,27,91,109,15,0,27,91,63,49,48,52,57,108,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,103,0,7,0,27,41,48,0,27,91,76,0,127,0,27,91,51,126,0,27,79,66,0,27,79,80,0,27,91,50,49,126,0,27,79,81,0,27,79,82,0,27,79,83,0,27,91,49,53,126,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,79,68,0,27,91,54,126,0,27,91,53,126,0,27,79,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,79,65,0,27,91,63,49,108,27,62,0,27,91,63,49,104,27,61,0,27,69,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,65,0,27,99,27,91,63,49,48,48,48,108,27,91,63,50,53,104,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,54,37,116,59,49,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,37,63,37,112,53,37,116,59,50,37,59,37,63,37,112,55,37,116,59,56,37,59,109,37,63,37,112,57,37,116,14,37,101,15,37,59,0,27,72,0,9,0,27,93,48,59,0,43,43,44,44,45,45,46,46,48,48,96,96,97,97,102,102,103,103,104,104,105,105,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,91,90,0,27,40,66,27,41,48,0,27,91,52,126,0,27,91,51,59,50,126,0,27,91,49,59,50,70,0,27,91,49,59,50,72,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,49,59,50,80,0,27,91,49,59,50,81,0,27,91,49,59,50,82,0,27,91,49,59,50,83,0,27,91,49,53,59,50,126,0,27,91,49,55,59,50,126,0,27,91,49,56,59,50,126,0,27,91,49,57,59,50,126,0,27,91,50,48,59,50,126,0,27,91,50,49,59,50,126,0,27,91,50,51,59,50,126,0,27,91,50,52,59,50,126,0,27,91,49,59,53,80,0,27,91,49,59,53,81,0,27,91,49,59,53,82,0,27,91,49,59,53,83,0,27,91,49,53,59,53,126,0,27,91,49,55,59,53,126,0,27,91,49,56,59,53,126,0,27,91,49,57,59,53,126,0,27,91,50,48,59,53,126,0,27,91,50,49,59,53,126,0,27,91,50,51,59,53,126,0,27,91,50,52,59,53,126,0,27,91,49,59,54,80,0,27,91,49,59,54,81,0,27,91,49,59,54,82,0,27,91,49,59,54,83,0,27,91,49,53,59,54,126,0,27,91,49,55,59,54,126,0,27,91,49,56,59,54,126,0,27,91,49,57,59,54,126,0,27,91,50,48,59,54,126,0,27,91,50,49,59,54,126,0,27,91,50,51,59,54,126,0,27,91,50,52,59,54,126,0,27,91,49,59,51,80,0,27,91,49,59,51,81,0,27,91,49,59,51,82,0,27,91,49,59,51,83,0,27,91,49,53,59,51,126,0,27,91,49,55,59,51,126,0,27,91,49,56,59,51,126,0,27,91,49,57,59,51,126,0,27,91,50,48,59,51,126,0,27,91,50,49,59,51,126,0,27,91,50,51,59,51,126,0,27,91,50,52,59,51,126,0,27,91,49,59,52,80,0,27,91,49,59,52,81,0,27,91,49,59,52,82,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,49,59,50,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,91,51,109,0,27,91,50,51,109,0,27,91,77,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,0,2,0,1,0,64,0,-125,0,33,3,1,1,1,0,0,0,0,0,7,0,19,0,23,0,28,0,46,0,54,0,60,0,71,0,81,0,86,0,93,0,100,0,107,0,114,0,121,0,-128,0,-121,0,-114,0,-107,0,-100,0,-93,0,-86,0,-79,0,-72,0,-65,0,-58,0,-51,0,-44,0,-37,0,-30,0,-23,0,-16,0,-9,0,-2,0,5,1,12,1,19,1,26,1,33,1,40,1,47,1,54,1,61,1,68,1,75,1,82,1,89,1,96,1,103,1,110,1,117,1,124,1,-125,1,-118,1,-111,1,-104,1,-97,1,-90,1,-83,1,-76,1,-69,1,-62,1,-56,1,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,30,0,36,0,39,0,42,0,47,0,52,0,57,0,62,0,67,0,71,0,76,0,81,0,86,0,91,0,96,0,102,0,108,0,114,0,120,0,126,0,-124,0,-118,0,-112,0,-106,0,-100,0,-95,0,-90,0,-85,0,-80,0,-75,0,-69,0,-63,0,-57,0,-51,0,-45,0,-39,0,-33,0,-27,0,-21,0,-15,0,-9,0,-3,0,3,1,9,1,15,1,21,1,27,1,33,1,39,1,45,1,49,1,54,1,59,1,64,1,69,1,74,1,79,1,27,93,49,49,50,7,0,27,93,49,50,59,37,112,49,37,115,7,0,27,40,66,0,27,91,51,74,0,27,93,53,50,59,37,112,49,37,115,59,37,112,50,37,115,7,0,27,40,37,112,49,37,99,0,27,91,50,32,113,0,27,91,52,58,37,112,49,37,100,109,0,27,91,37,112,49,37,100,32,113,0,27,93,48,59,0,27,91,51,59,51,126,0,27,91,51,59,52,126,0,27,91,51,59,53,126,0,27,91,51,59,54,126,0,27,91,51,59,55,126,0,27,91,49,59,50,66,0,27,91,49,59,51,66,0,27,91,49,59,52,66,0,27,91,49,59,53,66,0,27,91,49,59,54,66,0,27,91,49,59,55,66,0,27,91,49,59,51,70,0,27,91,49,59,52,70,0,27,91,49,59,53,70,0,27,91,49,59,54,70,0,27,91,49,59,55,70,0,27,91,49,59,51,72,0,27,91,49,59,52,72,0,27,91,49,59,53,72,0,27,91,49,59,54,72,0,27,91,49,59,55,72,0,27,91,50,59,51,126,0,27,91,50,59,52,126,0,27,91,50,59,53,126,0,27,91,50,59,54,126,0,27,91,50,59,55,126,0,27,91,49,59,51,68,0,27,91,49,59,52,68,0,27,91,49,59,53,68,0,27,91,49,59,54,68,0,27,91,49,59,55,68,0,27,91,54,59,51,126,0,27,91,54,59,52,126,0,27,91,54,59,53,126,0,27,91,54,59,54,126,0,27,91,54,59,55,126,0,27,91,53,59,51,126,0,27,91,53,59,52,126,0,27,91,53,59,53,126,0,27,91,53,59,54,126,0,27,91,53,59,55,126,0,27,91,49,59,51,67,0,27,91,49,59,52,67,0,27,91,49,59,53,67,0,27,91,49,59,54,67,0,27,91,49,59,55,67,0,27,91,49,59,50,65,0,27,91,49,59,51,65,0,27,91,49,59,52,65,0,27,91,49,59,53,65,0,27,91,49,59,54,65,0,27,91,49,59,55,65,0,27,91,50,57,109,0,27,91,57,109,0,65,88,0,71,48,0,85,56,0,67,114,0,67,115,0,69,48,0,69,51,0,77,115,0,83,48,0,83,101,0,83,109,117,108,120,0,83,115,0,84,83,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,73,67,51,0,107,73,67,52,0,107,73,67,53,0,107,73,67,54,0,107,73,67,55,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,78,88,84,51,0,107,78,88,84,52,0,107,78,88,84,53,0,107,78,88,84,54,0,107,78,88,84,55,0,107,80,82,86,51,0,107,80,82,86,52,0,107,80,82,86,53,0,107,80,82,86,54,0,107,80,82,86,55,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,107,85,80,55,0,114,109,120,120,0,115,109,120,120,0
};
// vte-256color|VTE with xterm 256-colors,
@@ -1754,7 +1774,7 @@ static const int8_t tmux_256colour_terminfo[] = {
// user8=\E[?%[;0123456789]c,
// user9=\E[c,
static const int8_t vte_256colour_terminfo[] = {
- 30,2,39,0,38,0,15,0,-99,1,-49,5,118,116,101,45,50,53,54,99,111,108,111,114,124,86,84,69,32,119,105,116,104,32,120,116,101,114,109,32,50,53,54,45,99,111,108,111,114,115,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,1,0,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,25,0,30,0,38,0,42,0,46,0,-1,-1,57,0,74,0,76,0,80,0,87,0,-1,-1,89,0,96,0,-1,-1,100,0,-1,-1,104,0,108,0,-1,-1,-1,-1,112,0,-1,-1,114,0,119,0,-1,-1,-128,0,-123,0,-118,0,-1,-1,-113,0,-108,0,-103,0,-98,0,-89,0,-87,0,-81,0,-1,-1,-68,0,-63,0,-57,0,-51,0,-1,-1,-1,-1,-1,-1,-33,0,-1,-1,-1,-1,-1,-1,0,1,-1,-1,4,1,-1,-1,-1,-1,-1,-1,6,1,-1,-1,11,1,-1,-1,-1,-1,-1,-1,-1,-1,15,1,19,1,25,1,29,1,33,1,37,1,43,1,49,1,55,1,61,1,67,1,71,1,-1,-1,76,1,-1,-1,80,1,85,1,90,1,94,1,101,1,-1,-1,108,1,112,1,120,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-128,1,-119,1,-110,1,-101,1,-92,1,-83,1,-74,1,-65,1,-56,1,-47,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-38,1,-35,1,-1,-1,-1,-1,16,2,19,2,30,2,33,2,35,2,38,2,116,2,-1,-1,119,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,121,2,-1,-1,-1,-1,-1,-1,-1,-1,125,2,-1,-1,-78,2,-1,-1,-1,-1,-74,2,-68,2,-1,-1,-1,-1,-62,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-58,2,-54,2,-1,-1,-50,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-45,2,-1,-1,-38,2,-33,2,-1,-1,-1,-1,-1,-1,-1,-1,-26,2,-19,2,-12,2,-1,-1,-1,-1,-5,2,-1,-1,2,3,-1,-1,-1,-1,-1,-1,9,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,16,3,22,3,28,3,35,3,42,3,49,3,56,3,64,3,72,3,80,3,88,3,96,3,104,3,112,3,120,3,127,3,-122,3,-115,3,-108,3,-100,3,-92,3,-84,3,-76,3,-68,3,-60,3,-52,3,-44,3,-37,3,-30,3,-23,3,-16,3,-8,3,0,4,8,4,16,4,24,4,32,4,40,4,48,4,55,4,62,4,69,4,76,4,84,4,92,4,100,4,108,4,116,4,124,4,-124,4,-116,4,-109,4,-102,4,-95,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-90,4,-79,4,-74,4,-55,4,-51,4,-42,4,-35,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,59,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,64,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,70,5,-1,-1,-1,-1,-1,-1,74,5,-119,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-55,5,-52,5,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,14,0,27,91,49,109,0,27,55,27,91,63,52,55,104,0,27,91,50,109,0,27,91,52,104,0,27,91,56,109,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,15,0,27,91,48,109,15,0,27,91,50,74,27,91,63,52,55,108,27,56,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,63,53,104,36,60,49,48,48,47,62,27,91,63,53,108,0,27,91,109,27,91,63,55,104,27,91,52,108,27,62,27,55,27,91,114,27,91,63,49,59,51,59,52,59,54,108,27,56,0,27,91,76,0,127,0,27,91,51,126,0,27,79,66,0,27,79,80,0,27,91,50,49,126,0,27,79,81,0,27,79,82,0,27,79,83,0,27,91,49,53,126,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,79,72,0,27,91,50,126,0,27,79,68,0,27,91,54,126,0,27,91,53,126,0,27,79,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,79,65,0,27,91,63,49,108,27,62,0,27,91,63,49,104,27,61,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,27,99,0,27,55,27,91,114,27,56,27,91,109,27,91,63,55,104,27,91,33,112,27,91,63,49,59,51,59,52,59,54,108,27,91,52,108,27,62,27,91,63,49,48,48,48,108,27,91,63,50,53,104,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,54,37,116,59,49,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,53,37,116,59,50,37,59,37,63,37,112,55,37,116,59,56,37,59,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,109,37,63,37,112,57,37,116,14,37,101,15,37,59,0,27,72,0,9,0,27,91,69,0,96,96,97,97,102,102,103,103,105,105,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,91,90,0,27,91,63,55,104,0,27,91,63,55,108,0,27,41,48,0,27,79,70,0,27,79,77,0,27,91,49,126,0,27,91,51,59,50,126,0,27,91,52,126,0,27,91,49,59,50,70,0,27,91,49,59,50,72,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,49,59,50,80,0,27,91,49,59,50,81,0,27,91,49,59,50,82,0,27,91,49,59,50,83,0,27,91,49,53,59,50,126,0,27,91,49,55,59,50,126,0,27,91,49,56,59,50,126,0,27,91,49,57,59,50,126,0,27,91,50,48,59,50,126,0,27,91,50,49,59,50,126,0,27,91,50,51,59,50,126,0,27,91,50,52,59,50,126,0,27,91,49,59,53,80,0,27,91,49,59,53,81,0,27,91,49,59,53,82,0,27,91,49,59,53,83,0,27,91,49,53,59,53,126,0,27,91,49,55,59,53,126,0,27,91,49,56,59,53,126,0,27,91,49,57,59,53,126,0,27,91,50,48,59,53,126,0,27,91,50,49,59,53,126,0,27,91,50,51,59,53,126,0,27,91,50,52,59,53,126,0,27,91,49,59,54,80,0,27,91,49,59,54,81,0,27,91,49,59,54,82,0,27,91,49,59,54,83,0,27,91,49,53,59,54,126,0,27,91,49,55,59,54,126,0,27,91,49,56,59,54,126,0,27,91,49,57,59,54,126,0,27,91,50,48,59,54,126,0,27,91,50,49,59,54,126,0,27,91,50,51,59,54,126,0,27,91,50,52,59,54,126,0,27,91,49,59,51,80,0,27,91,49,59,51,81,0,27,91,49,59,51,82,0,27,91,49,59,51,83,0,27,91,49,53,59,51,126,0,27,91,49,55,59,51,126,0,27,91,49,56,59,51,126,0,27,91,49,57,59,51,126,0,27,91,50,48,59,51,126,0,27,91,50,49,59,51,126,0,27,91,50,51,59,51,126,0,27,91,50,52,59,51,126,0,27,91,49,59,52,80,0,27,91,49,59,52,81,0,27,91,49,59,52,82,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,37,91,59,48,49,50,51,52,53,54,55,56,57,93,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,93,49,48,52,7,0,27,93,52,59,37,112,49,37,100,59,114,103,98,58,37,112,50,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,51,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,52,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,27,92,0,27,91,51,109,0,27,91,50,51,109,0,27,91,60,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,27,108,0,27,109,0,0,3,0,1,0,73,0,-106,0,57,3,0,0,1,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,0,-1,-1,-1,-1,32,0,39,0,46,0,53,0,60,0,67,0,74,0,81,0,88,0,95,0,102,0,109,0,116,0,123,0,-126,0,-119,0,-1,-1,-112,0,-105,0,-98,0,-91,0,-84,0,-1,-1,-77,0,-70,0,-63,0,-56,0,-49,0,-42,0,-35,0,-28,0,-21,0,-14,0,-7,0,0,1,7,1,14,1,21,1,28,1,35,1,42,1,49,1,56,1,63,1,70,1,77,1,84,1,91,1,98,1,105,1,112,1,119,1,126,1,-123,1,-1,-1,-1,-1,-1,-1,-1,-1,-116,1,-110,1,-105,1,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,30,0,33,0,36,0,39,0,42,0,48,0,54,0,59,0,64,0,69,0,74,0,79,0,83,0,88,0,93,0,98,0,103,0,108,0,114,0,120,0,126,0,-124,0,-118,0,-112,0,-106,0,-100,0,-94,0,-88,0,-82,0,-76,0,-71,0,-66,0,-61,0,-56,0,-51,0,-45,0,-39,0,-33,0,-27,0,-21,0,-15,0,-9,0,-3,0,3,1,9,1,15,1,21,1,27,1,33,1,39,1,45,1,51,1,57,1,63,1,69,1,73,1,78,1,83,1,88,1,93,1,98,1,102,1,106,1,110,1,114,1,119,1,124,1,27,91,63,49,48,48,54,59,49,48,48,48,37,63,37,112,49,37,123,49,125,37,61,37,116,104,37,101,108,37,59,0,27,91,51,59,51,126,0,27,91,51,59,52,126,0,27,91,51,59,53,126,0,27,91,51,59,54,126,0,27,91,51,59,55,126,0,27,91,49,59,50,66,0,27,91,49,59,51,66,0,27,91,49,59,52,66,0,27,91,49,59,53,66,0,27,91,49,59,54,66,0,27,91,49,59,55,66,0,27,91,49,59,51,70,0,27,91,49,59,52,70,0,27,91,49,59,53,70,0,27,91,49,59,54,70,0,27,91,49,59,55,70,0,27,91,49,59,51,72,0,27,91,49,59,52,72,0,27,91,49,59,53,72,0,27,91,49,59,54,72,0,27,91,49,59,55,72,0,27,91,50,59,51,126,0,27,91,50,59,52,126,0,27,91,50,59,53,126,0,27,91,50,59,54,126,0,27,91,50,59,55,126,0,27,91,49,59,51,68,0,27,91,49,59,52,68,0,27,91,49,59,53,68,0,27,91,49,59,54,68,0,27,91,49,59,55,68,0,27,91,54,59,51,126,0,27,91,54,59,52,126,0,27,91,54,59,53,126,0,27,91,54,59,54,126,0,27,91,54,59,55,126,0,27,91,53,59,51,126,0,27,91,53,59,52,126,0,27,91,53,59,53,126,0,27,91,53,59,54,126,0,27,91,53,59,55,126,0,27,91,49,59,51,67,0,27,91,49,59,52,67,0,27,91,49,59,53,67,0,27,91,49,59,54,67,0,27,91,49,59,55,67,0,27,91,49,59,50,65,0,27,91,49,59,51,65,0,27,91,49,59,52,65,0,27,91,49,59,53,65,0,27,91,49,59,54,65,0,27,91,49,59,55,65,0,27,91,50,57,109,0,27,91,57,109,0,27,91,60,37,112,49,37,100,59,37,112,50,37,100,59,37,112,51,37,100,59,37,63,37,112,52,37,116,77,37,101,109,37,59,0,65,88,0,71,48,0,88,84,0,85,56,0,67,114,0,67,115,0,69,48,0,69,51,0,77,115,0,83,48,0,83,101,0,83,115,0,84,83,0,88,77,0,103,114,98,111,109,0,103,115,98,111,109,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,69,78,68,56,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,72,79,77,56,0,107,73,67,51,0,107,73,67,52,0,107,73,67,53,0,107,73,67,54,0,107,73,67,55,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,78,88,84,51,0,107,78,88,84,52,0,107,78,88,84,53,0,107,78,88,84,54,0,107,78,88,84,55,0,107,80,82,86,51,0,107,80,82,86,52,0,107,80,82,86,53,0,107,80,82,86,54,0,107,80,82,86,55,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,107,85,80,55,0,107,97,50,0,107,98,49,0,107,98,51,0,107,99,50,0,114,109,120,120,0,115,109,120,120,0,120,109,0 // NOLINT
+ 30,2,39,0,38,0,15,0,-99,1,-49,5,118,116,101,45,50,53,54,99,111,108,111,114,124,86,84,69,32,119,105,116,104,32,120,116,101,114,109,32,50,53,54,45,99,111,108,111,114,115,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,1,0,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,25,0,30,0,38,0,42,0,46,0,-1,-1,57,0,74,0,76,0,80,0,87,0,-1,-1,89,0,96,0,-1,-1,100,0,-1,-1,104,0,108,0,-1,-1,-1,-1,112,0,-1,-1,114,0,119,0,-1,-1,-128,0,-123,0,-118,0,-1,-1,-113,0,-108,0,-103,0,-98,0,-89,0,-87,0,-81,0,-1,-1,-68,0,-63,0,-57,0,-51,0,-1,-1,-1,-1,-1,-1,-33,0,-1,-1,-1,-1,-1,-1,0,1,-1,-1,4,1,-1,-1,-1,-1,-1,-1,6,1,-1,-1,11,1,-1,-1,-1,-1,-1,-1,-1,-1,15,1,19,1,25,1,29,1,33,1,37,1,43,1,49,1,55,1,61,1,67,1,71,1,-1,-1,76,1,-1,-1,80,1,85,1,90,1,94,1,101,1,-1,-1,108,1,112,1,120,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-128,1,-119,1,-110,1,-101,1,-92,1,-83,1,-74,1,-65,1,-56,1,-47,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-38,1,-35,1,-1,-1,-1,-1,16,2,19,2,30,2,33,2,35,2,38,2,116,2,-1,-1,119,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,121,2,-1,-1,-1,-1,-1,-1,-1,-1,125,2,-1,-1,-78,2,-1,-1,-1,-1,-74,2,-68,2,-1,-1,-1,-1,-62,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-58,2,-54,2,-1,-1,-50,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-45,2,-1,-1,-38,2,-33,2,-1,-1,-1,-1,-1,-1,-1,-1,-26,2,-19,2,-12,2,-1,-1,-1,-1,-5,2,-1,-1,2,3,-1,-1,-1,-1,-1,-1,9,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,16,3,22,3,28,3,35,3,42,3,49,3,56,3,64,3,72,3,80,3,88,3,96,3,104,3,112,3,120,3,127,3,-122,3,-115,3,-108,3,-100,3,-92,3,-84,3,-76,3,-68,3,-60,3,-52,3,-44,3,-37,3,-30,3,-23,3,-16,3,-8,3,0,4,8,4,16,4,24,4,32,4,40,4,48,4,55,4,62,4,69,4,76,4,84,4,92,4,100,4,108,4,116,4,124,4,-124,4,-116,4,-109,4,-102,4,-95,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-90,4,-79,4,-74,4,-55,4,-51,4,-42,4,-35,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,59,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,64,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,70,5,-1,-1,-1,-1,-1,-1,74,5,-119,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-55,5,-52,5,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,14,0,27,91,49,109,0,27,55,27,91,63,52,55,104,0,27,91,50,109,0,27,91,52,104,0,27,91,56,109,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,15,0,27,91,48,109,15,0,27,91,50,74,27,91,63,52,55,108,27,56,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,63,53,104,36,60,49,48,48,47,62,27,91,63,53,108,0,27,91,109,27,91,63,55,104,27,91,52,108,27,62,27,55,27,91,114,27,91,63,49,59,51,59,52,59,54,108,27,56,0,27,91,76,0,127,0,27,91,51,126,0,27,79,66,0,27,79,80,0,27,91,50,49,126,0,27,79,81,0,27,79,82,0,27,79,83,0,27,91,49,53,126,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,79,72,0,27,91,50,126,0,27,79,68,0,27,91,54,126,0,27,91,53,126,0,27,79,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,79,65,0,27,91,63,49,108,27,62,0,27,91,63,49,104,27,61,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,27,99,0,27,55,27,91,114,27,56,27,91,109,27,91,63,55,104,27,91,33,112,27,91,63,49,59,51,59,52,59,54,108,27,91,52,108,27,62,27,91,63,49,48,48,48,108,27,91,63,50,53,104,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,54,37,116,59,49,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,53,37,116,59,50,37,59,37,63,37,112,55,37,116,59,56,37,59,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,109,37,63,37,112,57,37,116,14,37,101,15,37,59,0,27,72,0,9,0,27,91,69,0,96,96,97,97,102,102,103,103,105,105,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,91,90,0,27,91,63,55,104,0,27,91,63,55,108,0,27,41,48,0,27,79,70,0,27,79,77,0,27,91,49,126,0,27,91,51,59,50,126,0,27,91,52,126,0,27,91,49,59,50,70,0,27,91,49,59,50,72,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,49,59,50,80,0,27,91,49,59,50,81,0,27,91,49,59,50,82,0,27,91,49,59,50,83,0,27,91,49,53,59,50,126,0,27,91,49,55,59,50,126,0,27,91,49,56,59,50,126,0,27,91,49,57,59,50,126,0,27,91,50,48,59,50,126,0,27,91,50,49,59,50,126,0,27,91,50,51,59,50,126,0,27,91,50,52,59,50,126,0,27,91,49,59,53,80,0,27,91,49,59,53,81,0,27,91,49,59,53,82,0,27,91,49,59,53,83,0,27,91,49,53,59,53,126,0,27,91,49,55,59,53,126,0,27,91,49,56,59,53,126,0,27,91,49,57,59,53,126,0,27,91,50,48,59,53,126,0,27,91,50,49,59,53,126,0,27,91,50,51,59,53,126,0,27,91,50,52,59,53,126,0,27,91,49,59,54,80,0,27,91,49,59,54,81,0,27,91,49,59,54,82,0,27,91,49,59,54,83,0,27,91,49,53,59,54,126,0,27,91,49,55,59,54,126,0,27,91,49,56,59,54,126,0,27,91,49,57,59,54,126,0,27,91,50,48,59,54,126,0,27,91,50,49,59,54,126,0,27,91,50,51,59,54,126,0,27,91,50,52,59,54,126,0,27,91,49,59,51,80,0,27,91,49,59,51,81,0,27,91,49,59,51,82,0,27,91,49,59,51,83,0,27,91,49,53,59,51,126,0,27,91,49,55,59,51,126,0,27,91,49,56,59,51,126,0,27,91,49,57,59,51,126,0,27,91,50,48,59,51,126,0,27,91,50,49,59,51,126,0,27,91,50,51,59,51,126,0,27,91,50,52,59,51,126,0,27,91,49,59,52,80,0,27,91,49,59,52,81,0,27,91,49,59,52,82,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,37,91,59,48,49,50,51,52,53,54,55,56,57,93,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,93,49,48,52,7,0,27,93,52,59,37,112,49,37,100,59,114,103,98,58,37,112,50,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,51,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,52,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,27,92,0,27,91,51,109,0,27,91,50,51,109,0,27,91,60,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,27,108,0,27,109,0,0,1,0,0,0,59,0,119,0,22,3,1,0,0,0,6,0,12,0,23,0,55,0,62,0,69,0,76,0,83,0,90,0,97,0,104,0,111,0,118,0,125,0,-124,0,-117,0,-110,0,-103,0,-96,0,-89,0,-82,0,-75,0,-68,0,-61,0,-54,0,-47,0,-40,0,-33,0,-26,0,-19,0,-12,0,-5,0,2,1,9,1,16,1,23,1,30,1,37,1,44,1,51,1,58,1,65,1,72,1,79,1,86,1,93,1,100,1,107,1,114,1,121,1,-128,1,-121,1,-114,1,-107,1,-100,1,-93,1,-87,1,-82,1,0,0,3,0,8,0,13,0,19,0,22,0,27,0,32,0,37,0,42,0,47,0,51,0,56,0,61,0,66,0,71,0,76,0,82,0,88,0,94,0,100,0,106,0,112,0,118,0,124,0,-126,0,-120,0,-115,0,-110,0,-105,0,-100,0,-95,0,-89,0,-83,0,-77,0,-71,0,-65,0,-59,0,-53,0,-47,0,-41,0,-35,0,-29,0,-23,0,-17,0,-11,0,-5,0,1,1,7,1,13,1,19,1,25,1,29,1,34,1,39,1,44,1,49,1,54,1,59,1,64,1,27,91,53,53,109,0,27,91,53,51,109,0,27,91,52,58,37,112,49,37,100,109,0,27,91,63,49,48,48,54,59,49,48,48,48,37,63,37,112,49,37,123,49,125,37,61,37,116,104,37,101,108,37,59,0,27,91,51,59,51,126,0,27,91,51,59,52,126,0,27,91,51,59,53,126,0,27,91,51,59,54,126,0,27,91,51,59,55,126,0,27,91,49,59,50,66,0,27,91,49,59,51,66,0,27,91,49,59,52,66,0,27,91,49,59,53,66,0,27,91,49,59,54,66,0,27,91,49,59,55,66,0,27,91,49,59,51,70,0,27,91,49,59,52,70,0,27,91,49,59,53,70,0,27,91,49,59,54,70,0,27,91,49,59,55,70,0,27,91,49,59,51,72,0,27,91,49,59,52,72,0,27,91,49,59,53,72,0,27,91,49,59,54,72,0,27,91,49,59,55,72,0,27,91,50,59,51,126,0,27,91,50,59,52,126,0,27,91,50,59,53,126,0,27,91,50,59,54,126,0,27,91,50,59,55,126,0,27,91,49,59,51,68,0,27,91,49,59,52,68,0,27,91,49,59,53,68,0,27,91,49,59,54,68,0,27,91,49,59,55,68,0,27,91,54,59,51,126,0,27,91,54,59,52,126,0,27,91,54,59,53,126,0,27,91,54,59,54,126,0,27,91,54,59,55,126,0,27,91,53,59,51,126,0,27,91,53,59,52,126,0,27,91,53,59,53,126,0,27,91,53,59,54,126,0,27,91,53,59,55,126,0,27,91,49,59,51,67,0,27,91,49,59,52,67,0,27,91,49,59,53,67,0,27,91,49,59,54,67,0,27,91,49,59,55,67,0,27,91,49,59,50,65,0,27,91,49,59,51,65,0,27,91,49,59,52,65,0,27,91,49,59,53,65,0,27,91,49,59,54,65,0,27,91,49,59,55,65,0,27,91,50,57,109,0,27,91,57,109,0,27,91,60,37,105,37,112,51,37,100,59,37,112,49,37,100,59,37,112,50,37,100,59,37,63,37,112,52,37,116,77,37,101,109,37,59,0,88,84,0,82,109,111,108,0,83,109,111,108,0,83,109,117,108,120,0,88,77,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,73,67,51,0,107,73,67,52,0,107,73,67,53,0,107,73,67,54,0,107,73,67,55,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,78,88,84,51,0,107,78,88,84,52,0,107,78,88,84,53,0,107,78,88,84,54,0,107,78,88,84,55,0,107,80,82,86,51,0,107,80,82,86,52,0,107,80,82,86,53,0,107,80,82,86,54,0,107,80,82,86,55,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,107,85,80,55,0,114,109,120,120,0,115,109,120,120,0,120,109,0
};
// vtpcon|ANIS emulation for console virtual terminal sequence with libuv,
@@ -1776,6 +1796,7 @@ static const int8_t vte_256colour_terminfo[] = {
// carriage_return=\r,
// change_scroll_region=\E[%i%p1%d;%p2%dr,
// clear_all_tabs@,
+// clear_margins=\E[?69l,
// clear_screen=\E[H\E[2J,
// clr_bol=\E[1K,
// clr_eol=\E[K,
@@ -1817,9 +1838,13 @@ static const int8_t vte_256colour_terminfo[] = {
// init_2string=\E[\041p\E[?3l,
// initialize_color=\E]4;%p1%d;rgb\072%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E,
// insert_line=\E[L,
+// key_a1=\EOw,
+// key_a3=\EOy,
// key_b2=\E[G,
// key_backspace=^H,
// key_btab=\E[Z,
+// key_c1=\EOq,
+// key_c3=\EOs,
// key_dc=\E[3~,
// key_down=\E[B,
// key_end=\E[4~,
@@ -1911,6 +1936,7 @@ static const int8_t vte_256colour_terminfo[] = {
// memory_unlock@,
// meta_off@,
// meta_on@,
+// newline=\EE,
// orig_colors@,
// orig_pair=\E[39;49m,
// parm_dch=\E[%p1%dP,
@@ -1937,6 +1963,7 @@ static const int8_t vte_256colour_terminfo[] = {
// set_a_background=\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m,
// set_a_foreground=\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m,
// set_attributes=\E[0%?%p1%p3%|%t;7%;%?%p2%t;4%;%?%p6%t;1%;m,
+// set_lr_margin@,
// set_tab=\EH,
// tab=^I,
// user6@,
@@ -1944,7 +1971,7 @@ static const int8_t vte_256colour_terminfo[] = {
// user8@,
// user9@,
static const int8_t vtpcon_terminfo[] = {
- 30,2,71,0,38,0,15,0,-99,1,21,4,118,116,112,99,111,110,124,65,78,73,83,32,101,109,117,108,97,116,105,111,110,32,102,111,114,32,99,111,110,115,111,108,101,32,118,105,114,116,117,97,108,32,116,101,114,109,105,110,97,108,32,115,101,113,117,101,110,99,101,32,119,105,116,104,32,108,105,98,117,118,0,0,1,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,-2,-1,25,0,33,0,37,0,41,0,-1,-1,52,0,69,0,73,0,77,0,84,0,-1,-1,86,0,99,0,-1,-1,103,0,-2,-1,107,0,111,0,-1,-1,-1,-1,115,0,-2,-1,119,0,124,0,-1,-1,-2,-1,-2,-1,-2,-1,-1,-1,-123,0,-118,0,-113,0,-108,0,-99,0,-95,0,-90,0,-1,-1,-2,-1,-81,0,-75,0,-2,-1,-1,-1,-1,-1,-1,-1,-69,0,-1,-1,-1,-1,-1,-1,-59,0,-1,-1,-55,0,-1,-1,-1,-1,-1,-1,-53,0,-1,-1,-48,0,-1,-1,-1,-1,-1,-1,-1,-1,-44,0,-39,0,-33,0,-28,0,-23,0,-18,0,-13,0,-7,0,-1,0,5,1,11,1,16,1,-1,-1,21,1,-1,-1,25,1,30,1,35,1,39,1,46,1,-1,-1,53,1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,-1,-1,57,1,66,1,75,1,84,1,93,1,102,1,111,1,120,1,-127,1,-118,1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-109,1,-2,-1,-2,-1,-1,-1,-1,-1,-89,1,-86,1,-75,1,-72,1,-70,1,-67,1,-24,1,-1,-1,-21,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-19,1,-1,-1,-1,-1,-1,-1,-1,-1,-15,1,-1,-1,8,2,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,12,2,17,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,21,2,-1,-1,-1,-1,28,2,-1,-1,-1,-1,-1,-1,-1,-1,35,2,42,2,49,2,-1,-1,-1,-1,56,2,-1,-1,63,2,-1,-1,-1,-1,-1,-1,70,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,77,2,83,2,89,2,95,2,101,2,107,2,113,2,119,2,125,2,-125,2,-119,2,-113,2,-107,2,-101,2,-95,2,-89,2,-83,2,-77,2,-71,2,-65,2,-59,2,-53,2,-47,2,-41,2,-35,2,-29,2,-23,2,-17,2,-11,2,-5,2,2,3,8,3,14,3,20,3,26,3,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,32,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,37,3,-2,-1,46,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-117,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-112,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-106,3,-43,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,27,91,66,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,49,50,108,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,27,40,48,0,27,91,49,109,0,27,91,63,49,48,52,57,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,27,40,66,0,27,91,48,109,0,27,91,63,49,48,52,57,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,33,112,27,91,63,51,108,0,27,91,76,0,8,0,27,91,51,126,0,27,91,66,0,27,91,91,65,0,27,91,50,49,126,0,27,91,91,66,0,27,91,91,67,0,27,91,91,68,0,27,91,91,69,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,91,68,0,27,91,54,126,0,27,91,53,126,0,27,91,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,91,65,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,37,112,49,37,99,27,91,37,112,50,37,123,49,125,37,45,37,100,98,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,54,37,116,59,49,37,59,109,0,27,72,0,9,0,27,91,71,0,106,106,107,107,108,108,109,109,110,110,113,113,116,116,117,117,118,118,119,119,120,120,0,27,91,90,0,27,91,52,126,0,27,79,77,0,27,91,51,59,50,126,0,27,91,52,59,50,126,0,27,91,49,59,50,126,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,50,51,36,0,27,91,50,52,36,0,27,91,49,49,94,0,27,91,49,50,94,0,27,91,49,51,94,0,27,91,49,52,94,0,27,91,49,53,94,0,27,91,49,55,94,0,27,91,49,56,94,0,27,91,49,57,94,0,27,91,50,48,94,0,27,91,50,49,94,0,27,91,50,51,94,0,27,91,50,52,94,0,27,91,50,53,94,0,27,91,50,54,94,0,27,91,50,56,94,0,27,91,50,57,94,0,27,91,51,49,94,0,27,91,49,59,54,83,0,27,91,51,50,94,0,27,91,51,51,94,0,27,91,51,52,94,0,27,91,50,51,64,0,27,91,50,52,64,0,27,91,49,75,0,27,91,51,57,59,52,57,109,0,27,93,52,59,37,112,49,37,100,59,114,103,98,58,37,112,50,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,51,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,52,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,27,0,27,91,51,109,0,27,91,50,51,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,0,3,0,1,0,74,0,-104,0,-95,1,1,0,1,0,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,0,0,-2,-1,-1,-1,5,0,-1,-1,11,0,-1,-1,-2,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,21,0,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,30,0,33,0,39,0,42,0,45,0,48,0,54,0,60,0,65,0,70,0,75,0,80,0,85,0,89,0,94,0,99,0,104,0,109,0,114,0,120,0,126,0,-124,0,-118,0,-112,0,-106,0,-100,0,-94,0,-88,0,-82,0,-76,0,-70,0,-65,0,-60,0,-55,0,-50,0,-45,0,-39,0,-33,0,-27,0,-21,0,-15,0,-9,0,-3,0,3,1,9,1,15,1,21,1,27,1,33,1,39,1,45,1,51,1,57,1,63,1,69,1,75,1,79,1,84,1,89,1,94,1,99,1,104,1,108,1,112,1,116,1,120,1,125,1,-126,1,27,91,51,74,0,27,91,50,32,113,0,27,91,37,112,49,37,100,32,113,0,27,91,49,59,50,65,0,65,88,0,71,48,0,88,84,0,85,56,0,67,114,0,67,115,0,69,48,0,69,51,0,77,115,0,83,48,0,83,101,0,83,109,117,108,120,0,83,115,0,84,83,0,88,77,0,103,114,98,111,109,0,103,115,98,111,109,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,69,78,68,56,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,72,79,77,56,0,107,73,67,51,0,107,73,67,52,0,107,73,67,53,0,107,73,67,54,0,107,73,67,55,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,78,88,84,51,0,107,78,88,84,52,0,107,78,88,84,53,0,107,78,88,84,54,0,107,78,88,84,55,0,107,80,82,86,51,0,107,80,82,86,52,0,107,80,82,86,53,0,107,80,82,86,54,0,107,80,82,86,55,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,107,85,80,55,0,107,97,50,0,107,98,49,0,107,98,51,0,107,99,50,0,114,109,120,120,0,115,109,120,120,0,120,109,0 // NOLINT
+ 30,2,71,0,38,0,15,0,-99,1,47,4,118,116,112,99,111,110,124,65,78,73,83,32,101,109,117,108,97,116,105,111,110,32,102,111,114,32,99,111,110,115,111,108,101,32,118,105,114,116,117,97,108,32,116,101,114,109,105,110,97,108,32,115,101,113,117,101,110,99,101,32,119,105,116,104,32,108,105,98,117,118,0,0,1,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,-2,-1,25,0,33,0,37,0,41,0,-1,-1,52,0,69,0,73,0,77,0,84,0,-1,-1,86,0,99,0,-1,-1,103,0,-2,-1,107,0,111,0,-1,-1,-1,-1,115,0,-2,-1,119,0,124,0,-1,-1,-2,-1,-2,-1,-2,-1,-1,-1,-123,0,-118,0,-113,0,-108,0,-99,0,-95,0,-90,0,-1,-1,-2,-1,-81,0,-75,0,-2,-1,-1,-1,-1,-1,-1,-1,-69,0,-1,-1,-1,-1,-1,-1,-59,0,-1,-1,-55,0,-1,-1,-1,-1,-1,-1,-53,0,-1,-1,-48,0,-1,-1,-1,-1,-1,-1,-1,-1,-44,0,-39,0,-33,0,-28,0,-23,0,-18,0,-13,0,-7,0,-1,0,5,1,11,1,16,1,-1,-1,21,1,-1,-1,25,1,30,1,35,1,39,1,46,1,-1,-1,53,1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,57,1,-1,-1,60,1,69,1,78,1,87,1,96,1,105,1,114,1,123,1,-124,1,-115,1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-106,1,-2,-1,-2,-1,-1,-1,-1,-1,-86,1,-83,1,-72,1,-69,1,-67,1,-64,1,-21,1,-1,-1,-18,1,-1,-1,-1,-1,-1,-1,-1,-1,-16,1,-12,1,-8,1,-4,1,0,2,-1,-1,-1,-1,4,2,-1,-1,27,2,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,31,2,36,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,40,2,-1,-1,-1,-1,47,2,-1,-1,-1,-1,-1,-1,-1,-1,54,2,61,2,68,2,-1,-1,-1,-1,75,2,-1,-1,82,2,-1,-1,-1,-1,-1,-1,89,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,96,2,102,2,108,2,114,2,120,2,126,2,-124,2,-118,2,-112,2,-106,2,-100,2,-94,2,-88,2,-82,2,-76,2,-70,2,-64,2,-58,2,-52,2,-46,2,-40,2,-34,2,-28,2,-22,2,-16,2,-10,2,-4,2,2,3,8,3,14,3,21,3,27,3,33,3,39,3,45,3,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,51,3,56,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,63,3,-2,-1,72,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-91,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-86,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-80,3,-17,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,27,91,66,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,49,50,108,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,27,40,48,0,27,91,49,109,0,27,91,63,49,48,52,57,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,27,40,66,0,27,91,48,109,0,27,91,63,49,48,52,57,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,33,112,27,91,63,51,108,0,27,91,76,0,8,0,27,91,51,126,0,27,91,66,0,27,91,91,65,0,27,91,50,49,126,0,27,91,91,66,0,27,91,91,67,0,27,91,91,68,0,27,91,91,69,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,91,68,0,27,91,54,126,0,27,91,53,126,0,27,91,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,91,65,0,27,69,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,37,112,49,37,99,27,91,37,112,50,37,123,49,125,37,45,37,100,98,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,54,37,116,59,49,37,59,109,0,27,72,0,9,0,27,79,119,0,27,79,121,0,27,91,71,0,27,79,113,0,27,79,115,0,106,106,107,107,108,108,109,109,110,110,113,113,116,116,117,117,118,118,119,119,120,120,0,27,91,90,0,27,91,52,126,0,27,79,77,0,27,91,51,59,50,126,0,27,91,52,59,50,126,0,27,91,49,59,50,126,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,50,51,36,0,27,91,50,52,36,0,27,91,49,49,94,0,27,91,49,50,94,0,27,91,49,51,94,0,27,91,49,52,94,0,27,91,49,53,94,0,27,91,49,55,94,0,27,91,49,56,94,0,27,91,49,57,94,0,27,91,50,48,94,0,27,91,50,49,94,0,27,91,50,51,94,0,27,91,50,52,94,0,27,91,50,53,94,0,27,91,50,54,94,0,27,91,50,56,94,0,27,91,50,57,94,0,27,91,51,49,94,0,27,91,49,59,54,83,0,27,91,51,50,94,0,27,91,51,51,94,0,27,91,51,52,94,0,27,91,50,51,64,0,27,91,50,52,64,0,27,91,49,75,0,27,91,63,54,57,108,0,27,91,51,57,59,52,57,109,0,27,93,52,59,37,112,49,37,100,59,114,103,98,58,37,112,50,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,51,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,52,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,27,0,27,91,51,109,0,27,91,50,51,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,0,2,0,0,0,74,0,92,0,-46,1,1,1,-2,-1,-2,-1,0,0,-2,-1,5,0,11,0,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,21,0,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,28,0,32,0,36,0,40,0,44,0,48,0,52,0,56,0,60,0,64,0,68,0,72,0,-2,-1,-2,-1,-2,-1,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,32,0,37,0,42,0,47,0,52,0,56,0,61,0,66,0,71,0,76,0,81,0,87,0,93,0,99,0,105,0,111,0,117,0,123,0,-127,0,-121,0,-115,0,-110,0,-105,0,-100,0,-95,0,-90,0,-84,0,-78,0,-72,0,-66,0,-60,0,-54,0,-48,0,-42,0,-36,0,-30,0,-24,0,-18,0,-12,0,-6,0,0,1,6,1,12,1,18,1,24,1,30,1,34,1,39,1,44,1,49,1,54,1,59,1,63,1,67,1,71,1,75,1,79,1,85,1,91,1,97,1,103,1,109,1,115,1,121,1,126,1,-125,1,27,91,51,74,0,27,91,50,32,113,0,27,91,37,112,49,37,100,32,113,0,27,91,49,59,50,65,0,27,79,120,0,27,79,116,0,27,79,118,0,27,79,114,0,27,79,69,0,27,79,107,0,27,79,108,0,27,79,111,0,27,79,110,0,27,79,106,0,27,79,109,0,27,79,112,0,65,88,0,88,84,0,67,114,0,67,115,0,69,51,0,77,115,0,83,101,0,83,115,0,88,77,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,73,67,51,0,107,73,67,52,0,107,73,67,53,0,107,73,67,54,0,107,73,67,55,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,78,88,84,51,0,107,78,88,84,52,0,107,78,88,84,53,0,107,78,88,84,54,0,107,78,88,84,55,0,107,80,82,86,51,0,107,80,82,86,52,0,107,80,82,86,53,0,107,80,82,86,54,0,107,80,82,86,55,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,107,85,80,55,0,107,97,50,0,107,98,49,0,107,98,51,0,107,99,50,0,107,112,53,0,107,112,65,68,68,0,107,112,67,77,65,0,107,112,68,73,86,0,107,112,68,79,84,0,107,112,77,85,76,0,107,112,83,85,66,0,107,112,90,82,79,0,114,109,120,120,0,115,109,120,120,0,120,109,0
};
// win32con|ANSI emulation for libuv on legacy console,
@@ -2080,7 +2107,7 @@ static const int8_t vtpcon_terminfo[] = {
// user8@,
// user9@,
static const int8_t win32con_terminfo[] = {
- 26,1,52,0,15,0,15,0,125,1,106,2,119,105,110,51,50,99,111,110,124,65,78,83,73,32,101,109,117,108,97,116,105,111,110,32,102,111,114,32,108,105,98,117,118,32,111,110,32,108,101,103,97,99,121,32,99,111,110,115,111,108,101,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,1,0,-1,-1,8,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,8,0,64,0,-1,-1,0,0,2,0,-1,-1,-1,-1,4,0,11,0,15,0,19,0,-1,-1,30,0,47,0,51,0,-1,-1,55,0,-1,-1,-1,-1,57,0,-1,-1,61,0,-1,-1,-2,-1,-2,-1,-1,-1,-1,-1,-2,-1,-1,-1,65,0,70,0,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,79,0,84,0,-2,-1,-1,-1,-2,-1,89,0,94,0,-1,-1,-2,-1,107,0,-2,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,113,0,-1,-1,-1,-1,-1,-1,115,0,-1,-1,120,0,-1,-1,-1,-1,-1,-1,-1,-1,124,0,-127,0,-121,0,-116,0,-111,0,-106,0,-101,0,-95,0,-89,0,-83,0,-77,0,-72,0,-1,-1,-67,0,-1,-1,-63,0,-58,0,-53,0,-1,-1,-1,-1,-1,-1,-49,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-45,0,-1,-1,-2,-1,-2,-1,-42,0,-2,-1,-1,-1,-2,-1,-33,0,-24,0,-1,-1,-15,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-6,0,-3,0,8,1,-2,-1,-2,-1,11,1,-1,-1,-1,-1,49,1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,51,1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,55,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,60,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,1,-1,-1,-1,-1,69,1,-1,-1,-1,-1,-1,-1,-1,-1,76,1,83,1,90,1,-1,-1,-1,-1,97,1,-1,-1,104,1,-1,-1,-1,-1,-1,-1,111,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,118,1,124,1,-126,1,-120,1,-114,1,-108,1,-102,1,-96,1,-90,1,-84,1,-78,1,-72,1,-66,1,-60,1,-54,1,-48,1,-42,1,-36,1,-30,1,-24,1,-18,1,-12,1,-6,1,0,2,6,2,12,2,18,2,24,2,30,2,-1,-1,36,2,42,2,48,2,54,2,60,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,66,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,71,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,80,2,90,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,100,2,7,0,13,0,27,91,72,27,91,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,27,91,66,0,27,91,72,0,8,0,27,91,67,0,27,91,65,0,27,91,49,109,0,27,55,27,91,63,52,55,104,0,27,91,55,109,0,27,91,55,109,0,27,91,48,109,0,27,91,50,74,27,91,63,52,55,108,27,56,0,27,91,50,55,109,0,8,0,27,91,51,126,0,27,91,66,0,27,91,91,65,0,27,91,50,49,126,0,27,91,91,66,0,27,91,91,67,0,27,91,91,68,0,27,91,91,69,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,91,68,0,27,91,54,126,0,27,91,53,126,0,27,91,67,0,27,91,65,0,13,10,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,65,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,27,91,48,37,63,37,112,49,37,116,59,55,37,59,37,63,37,112,51,37,116,59,55,37,59,37,63,37,112,54,37,116,59,49,37,59,109,0,9,0,27,91,71,0,27,91,52,126,0,26,0,27,91,51,59,50,126,0,27,91,52,59,50,126,0,27,91,49,59,50,126,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,50,51,36,0,27,91,50,52,36,0,27,91,49,49,94,0,27,91,49,50,94,0,27,91,49,51,94,0,27,91,49,52,94,0,27,91,49,53,94,0,27,91,49,55,94,0,27,91,49,56,94,0,27,91,49,57,94,0,27,91,50,48,94,0,27,91,50,49,94,0,27,91,50,51,94,0,27,91,50,52,94,0,27,91,50,53,94,0,27,91,50,54,94,0,27,91,50,56,94,0,27,91,50,57,94,0,27,91,51,49,94,0,27,91,51,50,94,0,27,91,51,51,94,0,27,91,51,52,94,0,27,91,50,51,64,0,27,91,50,52,64,0,27,91,49,75,0,27,91,51,57,59,52,57,109,0,27,91,51,37,112,49,37,100,109,0,27,91,52,37,112,49,37,100,109,0,27,91,49,48,109,0,1,0,0,0,2,0,5,0,25,0,0,0,0,0,6,0,0,0,3,0,6,0,27,91,48,32,113,0,27,91,37,112,49,37,100,32,113,0,65,88,0,83,101,0,83,115,0 // NOLINT
+ 26,1,52,0,15,0,15,0,125,1,106,2,119,105,110,51,50,99,111,110,124,65,78,83,73,32,101,109,117,108,97,116,105,111,110,32,102,111,114,32,108,105,98,117,118,32,111,110,32,108,101,103,97,99,121,32,99,111,110,115,111,108,101,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,1,0,-1,-1,8,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,8,0,64,0,-1,-1,0,0,2,0,-1,-1,-1,-1,4,0,11,0,15,0,19,0,-1,-1,30,0,47,0,51,0,-1,-1,55,0,-1,-1,-1,-1,57,0,-1,-1,61,0,-1,-1,-2,-1,-2,-1,-1,-1,-1,-1,-2,-1,-1,-1,65,0,70,0,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,79,0,84,0,-2,-1,-1,-1,-2,-1,89,0,94,0,-1,-1,-2,-1,107,0,-2,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,113,0,-1,-1,-1,-1,-1,-1,115,0,-1,-1,120,0,-1,-1,-1,-1,-1,-1,-1,-1,124,0,-127,0,-121,0,-116,0,-111,0,-106,0,-101,0,-95,0,-89,0,-83,0,-77,0,-72,0,-1,-1,-67,0,-1,-1,-63,0,-58,0,-53,0,-1,-1,-1,-1,-1,-1,-49,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-45,0,-1,-1,-2,-1,-2,-1,-42,0,-2,-1,-1,-1,-2,-1,-33,0,-24,0,-1,-1,-15,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-6,0,-3,0,8,1,-2,-1,-2,-1,11,1,-1,-1,-1,-1,49,1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,51,1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,55,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,60,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,1,-1,-1,-1,-1,69,1,-1,-1,-1,-1,-1,-1,-1,-1,76,1,83,1,90,1,-1,-1,-1,-1,97,1,-1,-1,104,1,-1,-1,-1,-1,-1,-1,111,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,118,1,124,1,-126,1,-120,1,-114,1,-108,1,-102,1,-96,1,-90,1,-84,1,-78,1,-72,1,-66,1,-60,1,-54,1,-48,1,-42,1,-36,1,-30,1,-24,1,-18,1,-12,1,-6,1,0,2,6,2,12,2,18,2,24,2,30,2,-1,-1,36,2,42,2,48,2,54,2,60,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,66,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,71,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,80,2,90,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,100,2,7,0,13,0,27,91,72,27,91,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,27,91,66,0,27,91,72,0,8,0,27,91,67,0,27,91,65,0,27,91,49,109,0,27,55,27,91,63,52,55,104,0,27,91,55,109,0,27,91,55,109,0,27,91,48,109,0,27,91,50,74,27,91,63,52,55,108,27,56,0,27,91,50,55,109,0,8,0,27,91,51,126,0,27,91,66,0,27,91,91,65,0,27,91,50,49,126,0,27,91,91,66,0,27,91,91,67,0,27,91,91,68,0,27,91,91,69,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,91,68,0,27,91,54,126,0,27,91,53,126,0,27,91,67,0,27,91,65,0,13,10,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,65,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,27,91,48,37,63,37,112,49,37,116,59,55,37,59,37,63,37,112,51,37,116,59,55,37,59,37,63,37,112,54,37,116,59,49,37,59,109,0,9,0,27,91,71,0,27,91,52,126,0,26,0,27,91,51,59,50,126,0,27,91,52,59,50,126,0,27,91,49,59,50,126,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,50,51,36,0,27,91,50,52,36,0,27,91,49,49,94,0,27,91,49,50,94,0,27,91,49,51,94,0,27,91,49,52,94,0,27,91,49,53,94,0,27,91,49,55,94,0,27,91,49,56,94,0,27,91,49,57,94,0,27,91,50,48,94,0,27,91,50,49,94,0,27,91,50,51,94,0,27,91,50,52,94,0,27,91,50,53,94,0,27,91,50,54,94,0,27,91,50,56,94,0,27,91,50,57,94,0,27,91,51,49,94,0,27,91,51,50,94,0,27,91,51,51,94,0,27,91,51,52,94,0,27,91,50,51,64,0,27,91,50,52,64,0,27,91,49,75,0,27,91,51,57,59,52,57,109,0,27,91,51,37,112,49,37,100,109,0,27,91,52,37,112,49,37,100,109,0,27,91,49,48,109,0,0,0,0,0,2,0,4,0,22,0,0,0,6,0,0,0,3,0,27,91,48,32,113,0,27,91,37,112,49,37,100,32,113,0,83,101,0,83,115,0
};
// xterm-256color|xterm with 256 colors,
@@ -2105,6 +2132,7 @@ static const int8_t win32con_terminfo[] = {
// carriage_return=\r,
// change_scroll_region=\E[%i%p1%d;%p2%dr,
// clear_all_tabs=\E[3g,
+// clear_margins=\E[?69l,
// clear_screen=\E[H\E[2J,
// clr_bol=\E[1K,
// clr_eol=\E[K,
@@ -2146,9 +2174,13 @@ static const int8_t win32con_terminfo[] = {
// init_2string=\E[\041p\E[?3;4l\E[4l\E>,
// initialize_color=\E]4;%p1%d;rgb\072%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E\,
// insert_line=\E[L,
-// key_b2=\EOE,
+// key_a1=\EOw,
+// key_a3=\EOy,
+// key_b2=\EOu,
// key_backspace=^H,
// key_btab=\E[Z,
+// key_c1=\EOq,
+// key_c3=\EOs,
// key_dc=\E[3~,
// key_down=\EOB,
// key_end=\EOF,
@@ -2240,6 +2272,7 @@ static const int8_t win32con_terminfo[] = {
// memory_unlock=\Em,
// meta_off=\E[?1034l,
// meta_on=\E[?1034h,
+// newline=\EE,
// orig_colors=\E]104^G,
// orig_pair=\E[39;49m,
// parm_dch=\E[%p1%dP,
@@ -2266,6 +2299,7 @@ static const int8_t win32con_terminfo[] = {
// set_a_background=\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m,
// set_a_foreground=\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m,
// set_attributes=%?%p9%t\E(0%e\E(B%;\E[0%?%p6%t;1%;%?%p5%t;2%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m,
+// set_lr_margin=\E[?69h\E[%i%p1%d;%p2%ds,
// set_tab=\EH,
// tab=^I,
// user6=\E[%i%d;%dR,
@@ -2273,6 +2307,6 @@ static const int8_t win32con_terminfo[] = {
// user8=\E[?%[;0123456789]c,
// user9=\E[c,
static const int8_t xterm_256colour_terminfo[] = {
- 30,2,37,0,38,0,15,0,-99,1,2,6,120,116,101,114,109,45,50,53,54,99,111,108,111,114,124,120,116,101,114,109,32,119,105,116,104,32,50,53,54,32,99,111,108,111,114,115,0,0,1,0,0,1,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,1,0,1,1,0,0,0,0,0,0,0,0,1,0,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,25,0,30,0,38,0,42,0,46,0,-1,-1,57,0,74,0,76,0,80,0,87,0,-1,-1,89,0,102,0,-1,-1,106,0,110,0,120,0,124,0,-1,-1,-1,-1,-128,0,-124,0,-119,0,-114,0,-1,-1,-96,0,-91,0,-86,0,-1,-1,-81,0,-76,0,-71,0,-66,0,-57,0,-53,0,-46,0,-1,-1,-28,0,-23,0,-17,0,-11,0,-1,-1,-1,-1,-1,-1,7,1,-1,-1,-1,-1,-1,-1,25,1,-1,-1,29,1,-1,-1,-1,-1,-1,-1,31,1,-1,-1,36,1,-1,-1,-1,-1,-1,-1,-1,-1,40,1,44,1,50,1,54,1,58,1,62,1,68,1,74,1,80,1,86,1,92,1,96,1,-1,-1,101,1,-1,-1,105,1,110,1,115,1,119,1,126,1,-1,-1,-123,1,-119,1,-111,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-103,1,-94,1,-1,-1,-1,-1,-85,1,-76,1,-67,1,-58,1,-49,1,-40,1,-31,1,-22,1,-13,1,-4,1,-1,-1,-1,-1,-1,-1,5,2,9,2,14,2,19,2,39,2,48,2,-1,-1,-1,-1,66,2,69,2,80,2,83,2,85,2,88,2,-75,2,-1,-1,-72,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-70,2,-1,-1,-1,-1,-1,-1,-1,-1,-66,2,-1,-1,-13,2,-1,-1,-1,-1,-9,2,-3,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,3,3,7,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,11,3,-1,-1,-1,-1,18,3,-1,-1,-1,-1,-1,-1,-1,-1,25,3,32,3,39,3,-1,-1,-1,-1,46,3,-1,-1,53,3,-1,-1,-1,-1,-1,-1,60,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,67,3,73,3,79,3,86,3,93,3,100,3,107,3,115,3,123,3,-125,3,-117,3,-109,3,-101,3,-93,3,-85,3,-78,3,-71,3,-64,3,-57,3,-49,3,-41,3,-33,3,-25,3,-17,3,-9,3,-1,3,7,4,14,4,21,4,28,4,35,4,43,4,51,4,59,4,67,4,75,4,83,4,91,4,99,4,106,4,113,4,120,4,127,4,-121,4,-113,4,-105,4,-97,4,-89,4,-81,4,-73,4,-65,4,-58,4,-51,4,-44,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-39,4,-28,4,-23,4,-4,4,0,5,9,5,16,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,110,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,115,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,121,5,-1,-1,-1,-1,-1,-1,125,5,-68,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-4,5,-1,5,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,49,50,108,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,63,49,50,59,50,53,104,0,27,91,80,0,27,91,77,0,27,40,48,0,27,91,53,109,0,27,91,49,109,0,27,91,63,49,48,52,57,104,27,91,50,50,59,48,59,48,116,0,27,91,50,109,0,27,91,52,104,0,27,91,56,109,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,27,40,66,0,27,40,66,27,91,109,0,27,91,63,49,48,52,57,108,27,91,50,51,59,48,59,48,116,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,63,53,104,36,60,49,48,48,47,62,27,91,63,53,108,0,27,91,33,112,27,91,63,51,59,52,108,27,91,52,108,27,62,0,27,91,76,0,8,0,27,91,51,126,0,27,79,66,0,27,79,80,0,27,91,50,49,126,0,27,79,81,0,27,79,82,0,27,79,83,0,27,91,49,53,126,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,79,72,0,27,91,50,126,0,27,79,68,0,27,91,54,126,0,27,91,53,126,0,27,79,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,79,65,0,27,91,63,49,108,27,62,0,27,91,63,49,104,27,61,0,27,91,63,49,48,51,52,108,0,27,91,63,49,48,51,52,104,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,27,91,105,0,27,91,52,105,0,27,91,53,105,0,37,112,49,37,99,27,91,37,112,50,37,123,49,125,37,45,37,100,98,0,27,99,27,93,49,48,52,7,0,27,91,33,112,27,91,63,51,59,52,108,27,91,52,108,27,62,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,37,63,37,112,57,37,116,27,40,48,37,101,27,40,66,37,59,27,91,48,37,63,37,112,54,37,116,59,49,37,59,37,63,37,112,53,37,116,59,50,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,37,63,37,112,55,37,116,59,56,37,59,109,0,27,72,0,9,0,27,79,69,0,96,96,97,97,102,102,103,103,105,105,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,91,90,0,27,91,63,55,104,0,27,91,63,55,108,0,27,79,70,0,27,79,77,0,27,91,51,59,50,126,0,27,91,49,59,50,70,0,27,91,49,59,50,72,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,49,59,50,80,0,27,91,49,59,50,81,0,27,91,49,59,50,82,0,27,91,49,59,50,83,0,27,91,49,53,59,50,126,0,27,91,49,55,59,50,126,0,27,91,49,56,59,50,126,0,27,91,49,57,59,50,126,0,27,91,50,48,59,50,126,0,27,91,50,49,59,50,126,0,27,91,50,51,59,50,126,0,27,91,50,52,59,50,126,0,27,91,49,59,53,80,0,27,91,49,59,53,81,0,27,91,49,59,53,82,0,27,91,49,59,53,83,0,27,91,49,53,59,53,126,0,27,91,49,55,59,53,126,0,27,91,49,56,59,53,126,0,27,91,49,57,59,53,126,0,27,91,50,48,59,53,126,0,27,91,50,49,59,53,126,0,27,91,50,51,59,53,126,0,27,91,50,52,59,53,126,0,27,91,49,59,54,80,0,27,91,49,59,54,81,0,27,91,49,59,54,82,0,27,91,49,59,54,83,0,27,91,49,53,59,54,126,0,27,91,49,55,59,54,126,0,27,91,49,56,59,54,126,0,27,91,49,57,59,54,126,0,27,91,50,48,59,54,126,0,27,91,50,49,59,54,126,0,27,91,50,51,59,54,126,0,27,91,50,52,59,54,126,0,27,91,49,59,51,80,0,27,91,49,59,51,81,0,27,91,49,59,51,82,0,27,91,49,59,51,83,0,27,91,49,53,59,51,126,0,27,91,49,55,59,51,126,0,27,91,49,56,59,51,126,0,27,91,49,57,59,51,126,0,27,91,50,48,59,51,126,0,27,91,50,49,59,51,126,0,27,91,50,51,59,51,126,0,27,91,50,52,59,51,126,0,27,91,49,59,52,80,0,27,91,49,59,52,81,0,27,91,49,59,52,82,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,37,91,59,48,49,50,51,52,53,54,55,56,57,93,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,93,49,48,52,7,0,27,93,52,59,37,112,49,37,100,59,114,103,98,58,37,112,50,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,51,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,52,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,27,92,0,27,91,51,109,0,27,91,50,51,109,0,27,91,60,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,27,108,0,27,109,0,3,0,1,0,73,0,-106,0,115,3,1,0,1,0,-1,-1,-1,-1,0,0,7,0,-1,-1,19,0,24,0,-1,-1,42,0,48,0,-1,-1,58,0,-1,-1,-1,-1,90,0,97,0,104,0,111,0,118,0,125,0,-124,0,-117,0,-110,0,-103,0,-96,0,-89,0,-82,0,-75,0,-68,0,-61,0,-1,-1,-54,0,-47,0,-40,0,-33,0,-26,0,-1,-1,-19,0,-12,0,-5,0,2,1,9,1,16,1,23,1,30,1,37,1,44,1,51,1,58,1,65,1,72,1,79,1,86,1,93,1,100,1,107,1,114,1,121,1,-128,1,-121,1,-114,1,-107,1,-100,1,-93,1,-86,1,-79,1,-72,1,-65,1,-1,-1,-1,-1,-1,-1,-1,-1,-58,1,-52,1,-47,1,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,30,0,33,0,36,0,39,0,42,0,48,0,54,0,59,0,64,0,69,0,74,0,79,0,83,0,88,0,93,0,98,0,103,0,108,0,114,0,120,0,126,0,-124,0,-118,0,-112,0,-106,0,-100,0,-94,0,-88,0,-82,0,-76,0,-71,0,-66,0,-61,0,-56,0,-51,0,-45,0,-39,0,-33,0,-27,0,-21,0,-15,0,-9,0,-3,0,3,1,9,1,15,1,21,1,27,1,33,1,39,1,45,1,51,1,57,1,63,1,69,1,73,1,78,1,83,1,88,1,93,1,98,1,102,1,106,1,110,1,114,1,119,1,124,1,27,93,49,49,50,7,0,27,93,49,50,59,37,112,49,37,115,7,0,27,91,51,74,0,27,93,53,50,59,37,112,49,37,115,59,37,112,50,37,115,7,0,27,91,50,32,113,0,27,91,37,112,49,37,100,32,113,0,27,91,63,49,48,48,54,59,49,48,48,48,37,63,37,112,49,37,123,49,125,37,61,37,116,104,37,101,108,37,59,0,27,91,51,59,51,126,0,27,91,51,59,52,126,0,27,91,51,59,53,126,0,27,91,51,59,54,126,0,27,91,51,59,55,126,0,27,91,49,59,50,66,0,27,91,49,59,51,66,0,27,91,49,59,52,66,0,27,91,49,59,53,66,0,27,91,49,59,54,66,0,27,91,49,59,55,66,0,27,91,49,59,51,70,0,27,91,49,59,52,70,0,27,91,49,59,53,70,0,27,91,49,59,54,70,0,27,91,49,59,55,70,0,27,91,49,59,51,72,0,27,91,49,59,52,72,0,27,91,49,59,53,72,0,27,91,49,59,54,72,0,27,91,49,59,55,72,0,27,91,50,59,51,126,0,27,91,50,59,52,126,0,27,91,50,59,53,126,0,27,91,50,59,54,126,0,27,91,50,59,55,126,0,27,91,49,59,51,68,0,27,91,49,59,52,68,0,27,91,49,59,53,68,0,27,91,49,59,54,68,0,27,91,49,59,55,68,0,27,91,54,59,51,126,0,27,91,54,59,52,126,0,27,91,54,59,53,126,0,27,91,54,59,54,126,0,27,91,54,59,55,126,0,27,91,53,59,51,126,0,27,91,53,59,52,126,0,27,91,53,59,53,126,0,27,91,53,59,54,126,0,27,91,53,59,55,126,0,27,91,49,59,51,67,0,27,91,49,59,52,67,0,27,91,49,59,53,67,0,27,91,49,59,54,67,0,27,91,49,59,55,67,0,27,91,49,59,50,65,0,27,91,49,59,51,65,0,27,91,49,59,52,65,0,27,91,49,59,53,65,0,27,91,49,59,54,65,0,27,91,49,59,55,65,0,27,91,50,57,109,0,27,91,57,109,0,27,91,60,37,112,49,37,100,59,37,112,50,37,100,59,37,112,51,37,100,59,37,63,37,112,52,37,116,77,37,101,109,37,59,0,65,88,0,71,48,0,88,84,0,85,56,0,67,114,0,67,115,0,69,48,0,69,51,0,77,115,0,83,48,0,83,101,0,83,115,0,84,83,0,88,77,0,103,114,98,111,109,0,103,115,98,111,109,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,69,78,68,56,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,72,79,77,56,0,107,73,67,51,0,107,73,67,52,0,107,73,67,53,0,107,73,67,54,0,107,73,67,55,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,78,88,84,51,0,107,78,88,84,52,0,107,78,88,84,53,0,107,78,88,84,54,0,107,78,88,84,55,0,107,80,82,86,51,0,107,80,82,86,52,0,107,80,82,86,53,0,107,80,82,86,54,0,107,80,82,86,55,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,107,85,80,55,0,107,97,50,0,107,98,49,0,107,98,51,0,107,99,50,0,114,109,120,120,0,115,109,120,120,0,120,109,0 // NOLINT
+ 30,2,37,0,38,0,15,0,-99,1,51,6,120,116,101,114,109,45,50,53,54,99,111,108,111,114,124,120,116,101,114,109,32,119,105,116,104,32,50,53,54,32,99,111,108,111,114,115,0,0,1,0,0,1,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,1,0,1,1,0,0,0,0,0,0,0,0,1,0,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,25,0,30,0,38,0,42,0,46,0,-1,-1,57,0,74,0,76,0,80,0,87,0,-1,-1,89,0,102,0,-1,-1,106,0,110,0,120,0,124,0,-1,-1,-1,-1,-128,0,-124,0,-119,0,-114,0,-1,-1,-96,0,-91,0,-86,0,-1,-1,-81,0,-76,0,-71,0,-66,0,-57,0,-53,0,-46,0,-1,-1,-28,0,-23,0,-17,0,-11,0,-1,-1,-1,-1,-1,-1,7,1,-1,-1,-1,-1,-1,-1,25,1,-1,-1,29,1,-1,-1,-1,-1,-1,-1,31,1,-1,-1,36,1,-1,-1,-1,-1,-1,-1,-1,-1,40,1,44,1,50,1,54,1,58,1,62,1,68,1,74,1,80,1,86,1,92,1,96,1,-1,-1,101,1,-1,-1,105,1,110,1,115,1,119,1,126,1,-1,-1,-123,1,-119,1,-111,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-103,1,-94,1,-85,1,-1,-1,-82,1,-73,1,-64,1,-55,1,-46,1,-37,1,-28,1,-19,1,-10,1,-1,1,-1,-1,-1,-1,-1,-1,8,2,12,2,17,2,22,2,42,2,51,2,-1,-1,-1,-1,69,2,72,2,83,2,86,2,88,2,91,2,-72,2,-1,-1,-69,2,-1,-1,-1,-1,-1,-1,-1,-1,-67,2,-63,2,-59,2,-55,2,-51,2,-1,-1,-1,-1,-47,2,-1,-1,6,3,-1,-1,-1,-1,10,3,16,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,22,3,26,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,30,3,-1,-1,-1,-1,37,3,-1,-1,-1,-1,-1,-1,-1,-1,44,3,51,3,58,3,-1,-1,-1,-1,65,3,-1,-1,72,3,-1,-1,-1,-1,-1,-1,79,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,86,3,92,3,98,3,105,3,112,3,119,3,126,3,-122,3,-114,3,-106,3,-98,3,-90,3,-82,3,-74,3,-66,3,-59,3,-52,3,-45,3,-38,3,-30,3,-22,3,-14,3,-6,3,2,4,10,4,18,4,26,4,33,4,40,4,47,4,54,4,62,4,70,4,78,4,86,4,94,4,102,4,110,4,118,4,125,4,-124,4,-117,4,-110,4,-102,4,-94,4,-86,4,-78,4,-70,4,-62,4,-54,4,-46,4,-39,4,-32,4,-25,4,-20,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-13,4,-2,4,3,5,22,5,26,5,35,5,42,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-120,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-115,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-109,5,-1,-1,-1,-1,-1,-1,-105,5,-42,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,22,6,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,45,6,48,6,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,49,50,108,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,63,49,50,59,50,53,104,0,27,91,80,0,27,91,77,0,27,40,48,0,27,91,53,109,0,27,91,49,109,0,27,91,63,49,48,52,57,104,27,91,50,50,59,48,59,48,116,0,27,91,50,109,0,27,91,52,104,0,27,91,56,109,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,27,40,66,0,27,40,66,27,91,109,0,27,91,63,49,48,52,57,108,27,91,50,51,59,48,59,48,116,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,63,53,104,36,60,49,48,48,47,62,27,91,63,53,108,0,27,91,33,112,27,91,63,51,59,52,108,27,91,52,108,27,62,0,27,91,76,0,8,0,27,91,51,126,0,27,79,66,0,27,79,80,0,27,91,50,49,126,0,27,79,81,0,27,79,82,0,27,79,83,0,27,91,49,53,126,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,79,72,0,27,91,50,126,0,27,79,68,0,27,91,54,126,0,27,91,53,126,0,27,79,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,79,65,0,27,91,63,49,108,27,62,0,27,91,63,49,104,27,61,0,27,91,63,49,48,51,52,108,0,27,91,63,49,48,51,52,104,0,27,69,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,27,91,105,0,27,91,52,105,0,27,91,53,105,0,37,112,49,37,99,27,91,37,112,50,37,123,49,125,37,45,37,100,98,0,27,99,27,93,49,48,52,7,0,27,91,33,112,27,91,63,51,59,52,108,27,91,52,108,27,62,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,37,63,37,112,57,37,116,27,40,48,37,101,27,40,66,37,59,27,91,48,37,63,37,112,54,37,116,59,49,37,59,37,63,37,112,53,37,116,59,50,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,37,63,37,112,55,37,116,59,56,37,59,109,0,27,72,0,9,0,27,79,119,0,27,79,121,0,27,79,117,0,27,79,113,0,27,79,115,0,96,96,97,97,102,102,103,103,105,105,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,91,90,0,27,91,63,55,104,0,27,91,63,55,108,0,27,79,70,0,27,79,77,0,27,91,51,59,50,126,0,27,91,49,59,50,70,0,27,91,49,59,50,72,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,49,59,50,80,0,27,91,49,59,50,81,0,27,91,49,59,50,82,0,27,91,49,59,50,83,0,27,91,49,53,59,50,126,0,27,91,49,55,59,50,126,0,27,91,49,56,59,50,126,0,27,91,49,57,59,50,126,0,27,91,50,48,59,50,126,0,27,91,50,49,59,50,126,0,27,91,50,51,59,50,126,0,27,91,50,52,59,50,126,0,27,91,49,59,53,80,0,27,91,49,59,53,81,0,27,91,49,59,53,82,0,27,91,49,59,53,83,0,27,91,49,53,59,53,126,0,27,91,49,55,59,53,126,0,27,91,49,56,59,53,126,0,27,91,49,57,59,53,126,0,27,91,50,48,59,53,126,0,27,91,50,49,59,53,126,0,27,91,50,51,59,53,126,0,27,91,50,52,59,53,126,0,27,91,49,59,54,80,0,27,91,49,59,54,81,0,27,91,49,59,54,82,0,27,91,49,59,54,83,0,27,91,49,53,59,54,126,0,27,91,49,55,59,54,126,0,27,91,49,56,59,54,126,0,27,91,49,57,59,54,126,0,27,91,50,48,59,54,126,0,27,91,50,49,59,54,126,0,27,91,50,51,59,54,126,0,27,91,50,52,59,54,126,0,27,91,49,59,51,80,0,27,91,49,59,51,81,0,27,91,49,59,51,82,0,27,91,49,59,51,83,0,27,91,49,53,59,51,126,0,27,91,49,55,59,51,126,0,27,91,49,56,59,51,126,0,27,91,49,57,59,51,126,0,27,91,50,48,59,51,126,0,27,91,50,49,59,51,126,0,27,91,50,51,59,51,126,0,27,91,50,52,59,51,126,0,27,91,49,59,52,80,0,27,91,49,59,52,81,0,27,91,49,59,52,82,0,27,91,49,75,0,27,91,63,54,57,108,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,37,91,59,48,49,50,51,52,53,54,55,56,57,93,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,93,49,48,52,7,0,27,93,52,59,37,112,49,37,100,59,114,103,98,58,37,112,50,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,51,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,52,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,27,92,0,27,91,51,109,0,27,91,50,51,109,0,27,91,60,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,63,54,57,104,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,115,0,27,108,0,27,109,0,0,2,0,0,0,74,0,-106,0,-84,3,1,1,0,0,7,0,19,0,24,0,42,0,48,0,58,0,90,0,97,0,104,0,111,0,118,0,125,0,-124,0,-117,0,-110,0,-103,0,-96,0,-89,0,-82,0,-75,0,-68,0,-61,0,-54,0,-47,0,-40,0,-33,0,-26,0,-19,0,-12,0,-5,0,2,1,9,1,16,1,23,1,30,1,37,1,44,1,51,1,58,1,65,1,72,1,79,1,86,1,93,1,100,1,107,1,114,1,121,1,-128,1,-121,1,-114,1,-107,1,-100,1,-93,1,-86,1,-79,1,-72,1,-65,1,-58,1,-54,1,-50,1,-46,1,-42,1,-38,1,-34,1,-30,1,-26,1,-22,1,-18,1,-14,1,-10,1,-4,1,1,2,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,32,0,37,0,42,0,47,0,52,0,56,0,61,0,66,0,71,0,76,0,81,0,87,0,93,0,99,0,105,0,111,0,117,0,123,0,-127,0,-121,0,-115,0,-110,0,-105,0,-100,0,-95,0,-90,0,-84,0,-78,0,-72,0,-66,0,-60,0,-54,0,-48,0,-42,0,-36,0,-30,0,-24,0,-18,0,-12,0,-6,0,0,1,6,1,12,1,18,1,24,1,30,1,34,1,39,1,44,1,49,1,54,1,59,1,63,1,67,1,71,1,75,1,79,1,85,1,91,1,97,1,103,1,109,1,115,1,121,1,126,1,-125,1,27,93,49,49,50,7,0,27,93,49,50,59,37,112,49,37,115,7,0,27,91,51,74,0,27,93,53,50,59,37,112,49,37,115,59,37,112,50,37,115,7,0,27,91,50,32,113,0,27,91,37,112,49,37,100,32,113,0,27,91,63,49,48,48,54,59,49,48,48,48,37,63,37,112,49,37,123,49,125,37,61,37,116,104,37,101,108,37,59,0,27,91,51,59,51,126,0,27,91,51,59,52,126,0,27,91,51,59,53,126,0,27,91,51,59,54,126,0,27,91,51,59,55,126,0,27,91,49,59,50,66,0,27,91,49,59,51,66,0,27,91,49,59,52,66,0,27,91,49,59,53,66,0,27,91,49,59,54,66,0,27,91,49,59,55,66,0,27,91,49,59,51,70,0,27,91,49,59,52,70,0,27,91,49,59,53,70,0,27,91,49,59,54,70,0,27,91,49,59,55,70,0,27,91,49,59,51,72,0,27,91,49,59,52,72,0,27,91,49,59,53,72,0,27,91,49,59,54,72,0,27,91,49,59,55,72,0,27,91,50,59,51,126,0,27,91,50,59,52,126,0,27,91,50,59,53,126,0,27,91,50,59,54,126,0,27,91,50,59,55,126,0,27,91,49,59,51,68,0,27,91,49,59,52,68,0,27,91,49,59,53,68,0,27,91,49,59,54,68,0,27,91,49,59,55,68,0,27,91,54,59,51,126,0,27,91,54,59,52,126,0,27,91,54,59,53,126,0,27,91,54,59,54,126,0,27,91,54,59,55,126,0,27,91,53,59,51,126,0,27,91,53,59,52,126,0,27,91,53,59,53,126,0,27,91,53,59,54,126,0,27,91,53,59,55,126,0,27,91,49,59,51,67,0,27,91,49,59,52,67,0,27,91,49,59,53,67,0,27,91,49,59,54,67,0,27,91,49,59,55,67,0,27,91,49,59,50,65,0,27,91,49,59,51,65,0,27,91,49,59,52,65,0,27,91,49,59,53,65,0,27,91,49,59,54,65,0,27,91,49,59,55,65,0,27,79,120,0,27,79,116,0,27,79,118,0,27,79,114,0,27,79,69,0,27,79,107,0,27,79,108,0,27,79,111,0,27,79,110,0,27,79,106,0,27,79,109,0,27,79,112,0,27,91,50,57,109,0,27,91,57,109,0,27,91,60,37,105,37,112,51,37,100,59,37,112,49,37,100,59,37,112,50,37,100,59,37,63,37,112,52,37,116,77,37,101,109,37,59,0,65,88,0,88,84,0,67,114,0,67,115,0,69,51,0,77,115,0,83,101,0,83,115,0,88,77,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,73,67,51,0,107,73,67,52,0,107,73,67,53,0,107,73,67,54,0,107,73,67,55,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,78,88,84,51,0,107,78,88,84,52,0,107,78,88,84,53,0,107,78,88,84,54,0,107,78,88,84,55,0,107,80,82,86,51,0,107,80,82,86,52,0,107,80,82,86,53,0,107,80,82,86,54,0,107,80,82,86,55,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,107,85,80,55,0,107,97,50,0,107,98,49,0,107,98,51,0,107,99,50,0,107,112,53,0,107,112,65,68,68,0,107,112,67,77,65,0,107,112,68,73,86,0,107,112,68,79,84,0,107,112,77,85,76,0,107,112,83,85,66,0,107,112,90,82,79,0,114,109,120,120,0,115,109,120,120,0,120,109,0
};
#endif // NVIM_TUI_TERMINFO_DEFS_H
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index 58061f020d..e2289eb9ce 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -71,7 +71,7 @@ typedef struct {
int top, bot, left, right;
} Rect;
-typedef struct {
+struct TUIData {
UIBridgeData *bridge;
Loop *loop;
unibi_var_t params[9];
@@ -131,19 +131,19 @@ typedef struct {
int get_bg;
int set_underline_style;
int set_underline_color;
+ int enable_extended_keys, disable_extended_keys;
+ int get_extkeys;
} unibi_ext;
char *space_buf;
-} TUIData;
+};
-static bool volatile got_winch = false;
-static bool did_user_set_dimensions = false;
+static int got_winch = 0;
static bool cursor_style_enabled = false;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "tui/tui.c.generated.h"
#endif
-
UI *tui_start(void)
{
UI *ui = xcalloc(1, sizeof(UI)); // Freed by ui_bridge_stop().
@@ -168,7 +168,7 @@ UI *tui_start(void)
ui->set_title = tui_set_title;
ui->set_icon = tui_set_icon;
ui->screenshot = tui_screenshot;
- ui->option_set= tui_option_set;
+ ui->option_set = tui_option_set;
ui->raw_line = tui_raw_line;
memset(ui->ui_ext, 0, sizeof(ui->ui_ext));
@@ -178,6 +178,32 @@ UI *tui_start(void)
return ui_bridge_attach(ui, tui_main, tui_scheduler);
}
+void tui_enable_extkeys(TUIData *data)
+{
+ TermInput input = data->input;
+ unibi_term *ut = data->ut;
+ UI *ui = data->bridge->ui;
+
+ switch (input.extkeys_type) {
+ case kExtkeysCSIu:
+ data->unibi_ext.enable_extended_keys = (int)unibi_add_ext_str(ut, "ext.enable_extended_keys",
+ "\x1b[>1u");
+ data->unibi_ext.disable_extended_keys = (int)unibi_add_ext_str(ut, "ext.disable_extended_keys",
+ "\x1b[<1u");
+ break;
+ case kExtkeysXterm:
+ data->unibi_ext.enable_extended_keys = (int)unibi_add_ext_str(ut, "ext.enable_extended_keys",
+ "\x1b[>4;2m");
+ data->unibi_ext.disable_extended_keys = (int)unibi_add_ext_str(ut, "ext.disable_extended_keys",
+ "\x1b[>4;0m");
+ break;
+ default:
+ break;
+ }
+
+ unibi_out_ext(ui, data->unibi_ext.enable_extended_keys);
+}
+
static size_t unibi_pre_fmt_str(TUIData *data, unsigned int unibi_index, char *buf, size_t len)
{
const char *str = unibi_get_str(data->ut, unibi_index);
@@ -225,8 +251,12 @@ static void terminfo_start(UI *ui)
data->unibi_ext.reset_cursor_style = -1;
data->unibi_ext.get_bg = -1;
data->unibi_ext.set_underline_color = -1;
+ data->unibi_ext.enable_extended_keys = -1;
+ data->unibi_ext.disable_extended_keys = -1;
+ data->unibi_ext.get_extkeys = -1;
data->out_fd = STDOUT_FILENO;
data->out_isatty = os_isatty(data->out_fd);
+ data->input.tui_data = data;
const char *term = os_getenv("TERM");
#ifdef WIN32
@@ -308,6 +338,10 @@ static void terminfo_start(UI *ui)
// Enable bracketed paste
unibi_out_ext(ui, data->unibi_ext.enable_bracketed_paste);
+ // Query the terminal to see if it supports CSI u
+ data->input.waiting_for_csiu_response = 5;
+ unibi_out_ext(ui, data->unibi_ext.get_extkeys);
+
int ret;
uv_loop_init(&data->write_loop);
if (data->out_isatty) {
@@ -355,6 +389,8 @@ static void terminfo_stop(UI *ui)
// Reset cursor to normal before exiting alternate screen.
unibi_out(ui, unibi_cursor_normal);
unibi_out(ui, unibi_keypad_local);
+ // Disable extended keys before exiting alternate screen.
+ unibi_out_ext(ui, data->unibi_ext.disable_extended_keys);
unibi_out(ui, unibi_exit_ca_mode);
// Restore title/icon from the "stack". #4063
unibi_out_ext(ui, data->unibi_ext.restore_title);
@@ -498,7 +534,7 @@ static void sigcont_cb(SignalWatcher *watcher, int signum, void *data)
static void sigwinch_cb(SignalWatcher *watcher, int signum, void *data)
{
- got_winch = true;
+ got_winch++;
UI *ui = data;
if (tui_is_stopped(ui)) {
return;
@@ -528,7 +564,7 @@ static bool attrs_differ(UI *ui, int id1, int id2, bool rgb)
return a1.cterm_fg_color != a2.cterm_fg_color
|| a1.cterm_bg_color != a2.cterm_bg_color
|| a1.cterm_ae_attr != a2.cterm_ae_attr
- || (a1.cterm_ae_attr & (HL_UNDERLINE|HL_UNDERCURL)
+ || (a1.cterm_ae_attr & HL_ANY_UNDERLINE
&& a1.rgb_sp_color != a2.rgb_sp_color);
}
}
@@ -553,14 +589,26 @@ static void update_attrs(UI *ui, int attr_id)
bool underline;
bool undercurl;
+ bool underdouble;
+ bool underdotted;
+ bool underdashed;
if (data->unibi_ext.set_underline_style != -1) {
underline = attr & HL_UNDERLINE;
undercurl = attr & HL_UNDERCURL;
+ underdouble = attr & HL_UNDERDOUBLE;
+ underdashed = attr & HL_UNDERDASHED;
+ underdotted = attr & HL_UNDERDOTTED;
} else {
- underline = (attr & HL_UNDERLINE) || (attr & HL_UNDERCURL);
+ underline = attr & HL_ANY_UNDERLINE;
undercurl = false;
+ underdouble = false;
+ underdotted = false;
+ underdashed = false;
}
+ bool has_any_underline = undercurl || underline
+ || underdouble || underdotted || underdashed;
+
if (unibi_get_str(data->ut, unibi_set_attributes)) {
if (bold || reverse || underline || standout) {
UNIBI_SET_NUM_VAR(data->params[0], standout);
@@ -603,7 +651,20 @@ static void update_attrs(UI *ui, int attr_id)
UNIBI_SET_NUM_VAR(data->params[0], 3);
unibi_out_ext(ui, data->unibi_ext.set_underline_style);
}
- if ((undercurl || underline) && data->unibi_ext.set_underline_color != -1) {
+ if (underdouble && data->unibi_ext.set_underline_style != -1) {
+ UNIBI_SET_NUM_VAR(data->params[0], 2);
+ unibi_out_ext(ui, data->unibi_ext.set_underline_style);
+ }
+ if (underdotted && data->unibi_ext.set_underline_style != -1) {
+ UNIBI_SET_NUM_VAR(data->params[0], 4);
+ unibi_out_ext(ui, data->unibi_ext.set_underline_style);
+ }
+ if (underdashed && data->unibi_ext.set_underline_style != -1) {
+ UNIBI_SET_NUM_VAR(data->params[0], 5);
+ unibi_out_ext(ui, data->unibi_ext.set_underline_style);
+ }
+
+ if (has_any_underline && data->unibi_ext.set_underline_color != -1) {
int color = attrs.rgb_sp_color;
if (color != -1) {
UNIBI_SET_NUM_VAR(data->params[0], (color >> 16) & 0xff); // red
@@ -650,15 +711,14 @@ static void update_attrs(UI *ui, int attr_id)
}
}
-
data->default_attr = fg == -1 && bg == -1
- && !bold && !italic && !underline && !undercurl && !reverse && !standout
+ && !bold && !italic && !has_any_underline && !reverse && !standout
&& !strikethrough;
// Non-BCE terminals can't clear with non-default background color. Some BCE
// terminals don't support attributes either, so don't rely on it. But assume
// italic and bold has no effect if there is no text.
- data->can_clear_attr = !reverse && !standout && !underline && !undercurl
+ data->can_clear_attr = !reverse && !standout && !has_any_underline
&& !strikethrough && (data->bce || bg == -1);
}
@@ -834,7 +894,7 @@ static void clear_region(UI *ui, int top, int bot, int left, int right, int attr
unibi_out(ui, unibi_clr_eos);
}
} else {
- int width = right-left;
+ int width = right - left;
// iterate through each line and clear
for (int row = top; row < bot; row++) {
@@ -926,7 +986,7 @@ static void tui_grid_resize(UI *ui, Integer g, Integer width, Integer height)
r->right = MIN(r->right, grid->width);
}
- if (!got_winch && (!data->is_starting || did_user_set_dimensions)) {
+ if (!got_winch && !data->is_starting) {
// Resize the _host_ terminal.
UNIBI_SET_NUM_VAR(data->params[0], (int)height);
UNIBI_SET_NUM_VAR(data->params[1], (int)width);
@@ -936,7 +996,7 @@ static void tui_grid_resize(UI *ui, Integer g, Integer width, Integer height)
reset_scroll_region(ui, ui->width == grid->width);
}
} else { // Already handled the SIGWINCH signal; avoid double-resize.
- got_winch = false;
+ got_winch = got_winch > 0 ? got_winch - 1 : 0;
grid->row = -1;
}
}
@@ -1123,12 +1183,12 @@ static void tui_grid_scroll(UI *ui, Integer g, Integer startrow, // -V751
{
TUIData *data = ui->data;
UGrid *grid = &data->grid;
- int top = (int)startrow, bot = (int)endrow-1;
- int left = (int)startcol, right = (int)endcol-1;
+ int top = (int)startrow, bot = (int)endrow - 1;
+ int left = (int)startcol, right = (int)endcol - 1;
- bool fullwidth = left == 0 && right == ui->width-1;
+ bool fullwidth = left == 0 && right == ui->width - 1;
data->scroll_region_is_full_screen = fullwidth
- && top == 0 && bot == ui->height-1;
+ && top == 0 && bot == ui->height - 1;
ugrid_scroll(grid, top, bot, left, right, (int)rows);
@@ -1231,10 +1291,10 @@ static void tui_flush(UI *ui)
assert(r.bot <= grid->height && r.right <= grid->width);
for (int row = r.top; row < r.bot; row++) {
- int clear_attr = grid->cells[row][r.right-1].attr;
+ int clear_attr = grid->cells[row][r.right - 1].attr;
int clear_col;
for (clear_col = r.right; clear_col > 0; clear_col--) {
- UCell *cell = &grid->cells[row][clear_col-1];
+ UCell *cell = &grid->cells[row][clear_col - 1];
if (!(cell->data[0] == ' ' && cell->data[1] == NUL
&& cell->attr == clear_attr)) {
break;
@@ -1246,7 +1306,7 @@ static void tui_flush(UI *ui)
print_cell(ui, cell);
});
if (clear_col < r.right) {
- clear_region(ui, row, row+1, clear_col, r.right, clear_attr);
+ clear_region(ui, row, row + 1, clear_col, r.right, clear_attr);
}
}
}
@@ -1326,8 +1386,7 @@ static void tui_set_title(UI *ui, String title)
}
static void tui_set_icon(UI *ui, String icon)
-{
-}
+{}
static void tui_screenshot(UI *ui, String path)
{
@@ -1353,7 +1412,6 @@ static void tui_screenshot(UI *ui, String path)
fclose(f);
}
-
static void tui_option_set(UI *ui, String name, Object value)
{
TUIData *data = ui->data;
@@ -1362,11 +1420,9 @@ static void tui_option_set(UI *ui, String name, Object value)
data->print_attr_id = -1;
invalidate(ui, 0, data->grid.height, 0, data->grid.width);
- }
- if (strequal(name.data, "ttimeout")) {
+ } else if (strequal(name.data, "ttimeout")) {
data->input.ttimeout = value.data.boolean;
- }
- if (strequal(name.data, "ttimeoutlen")) {
+ } else if (strequal(name.data, "ttimeoutlen")) {
data->input.ttimeoutlen = (long)value.data.integer;
}
}
@@ -1378,9 +1434,9 @@ static void tui_raw_line(UI *ui, Integer g, Integer linerow, Integer startcol, I
TUIData *data = ui->data;
UGrid *grid = &data->grid;
for (Integer c = startcol; c < endcol; c++) {
- memcpy(grid->cells[linerow][c].data, chunk[c-startcol], sizeof(schar_T));
- assert((size_t)attrs[c-startcol] < kv_size(data->attrs));
- grid->cells[linerow][c].attr = attrs[c-startcol];
+ memcpy(grid->cells[linerow][c].data, chunk[c - startcol], sizeof(schar_T));
+ assert((size_t)attrs[c - startcol] < kv_size(data->attrs));
+ grid->cells[linerow][c].attr = attrs[c - startcol];
}
UGRID_FOREACH_CELL(grid, (int)linerow, (int)startcol, (int)endcol, {
cursor_goto(ui, (int)linerow, curcol);
@@ -1390,7 +1446,7 @@ static void tui_raw_line(UI *ui, Integer g, Integer linerow, Integer startcol, I
if (clearcol > endcol) {
ugrid_clear_chunk(grid, (int)linerow, (int)endcol, (int)clearcol,
(sattr_T)clearattr);
- clear_region(ui, (int)linerow, (int)linerow+1, (int)endcol, (int)clearcol,
+ clear_region(ui, (int)linerow, (int)linerow + 1, (int)endcol, (int)clearcol,
(int)clearattr);
}
@@ -1447,23 +1503,13 @@ static void tui_guess_size(UI *ui)
TUIData *data = ui->data;
int width = 0, height = 0;
- // 1 - look for non-default 'columns' and 'lines' options during startup
- if (data->is_starting && (Columns != DFLT_COLS || Rows != DFLT_ROWS)) {
- did_user_set_dimensions = true;
- assert(Columns >= INT_MIN && Columns <= INT_MAX);
- assert(Rows >= INT_MIN && Rows <= INT_MAX);
- width = Columns;
- height = Rows;
- goto end;
- }
-
- // 2 - try from a system call(ioctl/TIOCGWINSZ on unix)
+ // 1 - try from a system call(ioctl/TIOCGWINSZ on unix)
if (data->out_isatty
&& !uv_tty_get_winsize(&data->output_handle.tty, &width, &height)) {
goto end;
}
- // 3 - use $LINES/$COLUMNS if available
+ // 2 - use $LINES/$COLUMNS if available
const char *val;
int advance;
if ((val = os_getenv("LINES"))
@@ -1473,7 +1519,7 @@ static void tui_guess_size(UI *ui)
goto end;
}
- // 4 - read from terminfo if available
+ // 3 - read from terminfo if available
height = unibi_get_num(data->ut, unibi_lines);
width = unibi_get_num(data->ut, unibi_columns);
@@ -1596,6 +1642,7 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, const char *col
bool xterm = terminfo_is_term_family(term, "xterm")
// Treat Terminal.app as generic xterm-like, for now.
|| nsterm;
+ bool hterm = terminfo_is_term_family(term, "hterm");
bool kitty = terminfo_is_term_family(term, "xterm-kitty");
bool linuxvt = terminfo_is_term_family(term, "linux");
bool bsdvt = terminfo_is_bsd_console(term);
@@ -1659,7 +1706,7 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, const char *col
unibi_set_bool(ut, unibi_back_color_erase, false);
}
- if (xterm) {
+ if (xterm || hterm) {
// Termit, LXTerminal, GTKTerm2, GNOME Terminal, MATE Terminal, roxterm,
// and EvilVTE falsely claim to be xterm and do not support important xterm
// control sequences that we use. In an ideal world, these would have
@@ -1668,9 +1715,13 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, const char *col
// treatable as xterm.
// 2017-04 terminfo.src lacks these. Xterm-likes have them.
- unibi_set_if_empty(ut, unibi_to_status_line, "\x1b]0;");
- unibi_set_if_empty(ut, unibi_from_status_line, "\x07");
- unibi_set_if_empty(ut, unibi_set_tb_margin, "\x1b[%i%p1%d;%p2%dr");
+ if (!hterm) {
+ // hterm doesn't have a status line.
+ unibi_set_if_empty(ut, unibi_to_status_line, "\x1b]0;");
+ unibi_set_if_empty(ut, unibi_from_status_line, "\x07");
+ // TODO(aktau): patch this in when DECSTBM is fixed (https://crbug.com/1298796)
+ unibi_set_if_empty(ut, unibi_set_tb_margin, "\x1b[%i%p1%d;%p2%dr");
+ }
unibi_set_if_empty(ut, unibi_enter_italics_mode, "\x1b[3m");
unibi_set_if_empty(ut, unibi_exit_italics_mode, "\x1b[23m");
@@ -1681,6 +1732,9 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, const char *col
unibi_set_if_empty(ut, unibi_set_right_margin_parm, "\x1b[%i;%p2%ds");
} else {
// Fix things advertised via TERM=xterm, for non-xterm.
+ //
+ // TODO(aktau): stop patching this out for hterm when it gains support
+ // (https://crbug.com/1175065).
if (unibi_get_str(ut, unibi_set_lr_margin)) {
ILOG("Disabling smglr with TERM=xterm for non-xterm.");
unibi_set_str(ut, unibi_set_lr_margin, NULL);
@@ -1780,6 +1834,12 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, const char *col
data->unibi_ext.get_bg = (int)unibi_add_ext_str(ut, "ext.get_bg",
"\x1b]11;?\x07");
+ // Query the terminal to see if it supports CSI u key encoding by writing CSI
+ // ? u followed by a request for the primary device attributes (CSI c)
+ // See https://sw.kovidgoyal.net/kitty/keyboard-protocol/#detection-of-support-for-this-protocol
+ data->unibi_ext.get_extkeys = (int)unibi_add_ext_str(ut, "ext.get_extkeys",
+ "\x1b[?u\x1b[c");
+
// Terminals with 256-colour SGR support despite what terminfo says.
if (unibi_get_num(ut, unibi_max_colors) < 256) {
// See http://fedoraproject.org/wiki/Features/256_Color_Terminals
@@ -1823,6 +1883,8 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, const char *col
&& ((xterm && !vte_version) // anything claiming xterm compat
// per MinTTY 0.4.3-1 release notes from 2009
|| putty
+ // per https://chromium.googlesource.com/apps/libapps/+/a5fb83c190aa9d74f4a9bca233dac6be2664e9e9/hterm/doc/ControlSequences.md
+ || hterm
// per https://bugzilla.gnome.org/show_bug.cgi?id=720821
|| (vte_version >= 3900)
|| (konsolev >= 180770) // #9364
@@ -1907,6 +1969,7 @@ static void augment_terminfo(TUIData *data, const char *term, long vte_version,
bool xterm = terminfo_is_term_family(term, "xterm")
// Treat Terminal.app as generic xterm-like, for now.
|| nsterm;
+ bool hterm = terminfo_is_term_family(term, "hterm");
bool bsdvt = terminfo_is_bsd_console(term);
bool dtterm = terminfo_is_term_family(term, "dtterm");
bool rxvt = terminfo_is_term_family(term, "rxvt");
@@ -1919,6 +1982,7 @@ static void augment_terminfo(TUIData *data, const char *term, long vte_version,
|| terminfo_is_term_family(term, "iTerm.app")
|| terminfo_is_term_family(term, "iTerm2.app");
bool alacritty = terminfo_is_term_family(term, "alacritty");
+ bool kitty = terminfo_is_term_family(term, "xterm-kitty");
// None of the following work over SSH; see :help TERM .
bool iterm_pretending_xterm = xterm && iterm_env;
@@ -1935,7 +1999,7 @@ static void augment_terminfo(TUIData *data, const char *term, long vte_version,
"ext.resize_screen",
"\x1b[8;%p1%d;%p2%dt");
}
- if (putty || xterm || rxvt) {
+ if (putty || xterm || hterm || rxvt) {
data->unibi_ext.reset_scroll_region = (int)unibi_add_ext_str(ut,
"ext.reset_scroll_region",
"\x1b[r");
@@ -1984,22 +2048,27 @@ static void augment_terminfo(TUIData *data, const char *term, long vte_version,
}
}
- if (iterm || iterm_pretending_xterm) {
- // FIXME: Bypassing tmux like this affects the cursor colour globally, in
- // all panes, which is not particularly desirable. A better approach
- // would use a tmux control sequence and an extra if(screen) test.
- data->unibi_ext.set_cursor_color =
- (int)unibi_add_ext_str(ut, NULL, TMUX_WRAP(tmux, "\033]Pl%p1%06x\033\\"));
- } else if ((xterm || rxvt || tmux || alacritty)
- && (vte_version == 0 || vte_version >= 3900)) {
- // Supported in urxvt, newer VTE.
- data->unibi_ext.set_cursor_color = (int)unibi_add_ext_str(ut, "ext.set_cursor_color",
- "\033]12;#%p1%06x\007");
+ data->unibi_ext.set_cursor_color = unibi_find_ext_str(ut, "Cs");
+ if (-1 == data->unibi_ext.set_cursor_color) {
+ if (iterm || iterm_pretending_xterm) {
+ // FIXME: Bypassing tmux like this affects the cursor colour globally, in
+ // all panes, which is not particularly desirable. A better approach
+ // would use a tmux control sequence and an extra if(screen) test.
+ data->unibi_ext.set_cursor_color =
+ (int)unibi_add_ext_str(ut, NULL, TMUX_WRAP(tmux, "\033]Pl%p1%06x\033\\"));
+ } else if ((xterm || hterm || rxvt || tmux || alacritty)
+ && (vte_version == 0 || vte_version >= 3900)) {
+ // Supported in urxvt, newer VTE.
+ data->unibi_ext.set_cursor_color = (int)unibi_add_ext_str(ut, "ext.set_cursor_color",
+ "\033]12;#%p1%06x\007");
+ }
}
-
if (-1 != data->unibi_ext.set_cursor_color) {
- data->unibi_ext.reset_cursor_color = (int)unibi_add_ext_str(ut, "ext.reset_cursor_color",
- "\x1b]112\x07");
+ data->unibi_ext.reset_cursor_color = unibi_find_ext_str(ut, "Cr");
+ if (-1 == data->unibi_ext.reset_cursor_color) {
+ data->unibi_ext.reset_cursor_color = (int)unibi_add_ext_str(ut, "ext.reset_cursor_color",
+ "\x1b]112\x07");
+ }
}
data->unibi_ext.save_title = (int)unibi_add_ext_str(ut, "ext.save_title", "\x1b[22;0t");
@@ -2042,6 +2111,11 @@ static void augment_terminfo(TUIData *data, const char *term, long vte_version,
data->unibi_ext.set_underline_color = (int)unibi_add_ext_str(ut, "ext.set_underline_color",
"\x1b[58:2::%p1%d:%p2%d:%p3%dm");
}
+
+ if (!kitty && (vte_version == 0 || vte_version >= 5400)) {
+ // Fallback to Xterm's modifyOtherKeys if terminal does not support CSI u
+ data->input.extkeys_type = kExtkeysXterm;
+ }
}
static void flush_buf(UI *ui)
diff --git a/src/nvim/tui/tui.h b/src/nvim/tui/tui.h
index 996496ee60..88ea73e99c 100644
--- a/src/nvim/tui/tui.h
+++ b/src/nvim/tui/tui.h
@@ -4,6 +4,8 @@
#include "nvim/cursor_shape.h"
#include "nvim/ui.h"
+typedef struct TUIData TUIData;
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "tui/tui.h.generated.h"
#endif
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/ugrid.c b/src/nvim/ugrid.c
index ef84cdf334..d96da3f2bb 100644
--- a/src/nvim/ugrid.c
+++ b/src/nvim/ugrid.c
@@ -39,12 +39,12 @@ void ugrid_resize(UGrid *grid, int width, int height)
void ugrid_clear(UGrid *grid)
{
- clear_region(grid, 0, grid->height-1, 0, grid->width-1, 0);
+ clear_region(grid, 0, grid->height - 1, 0, grid->width - 1, 0);
}
void ugrid_clear_chunk(UGrid *grid, int row, int col, int endcol, sattr_T attr)
{
- clear_region(grid, row, row, col, endcol-1, attr);
+ clear_region(grid, row, row, col, endcol - 1, attr);
}
void ugrid_goto(UGrid *grid, int row, int col)
@@ -82,7 +82,7 @@ void ugrid_scroll(UGrid *grid, int top, int bot, int left, int right, int count)
static void clear_region(UGrid *grid, int top, int bot, int left, int right, sattr_T attr)
{
for (int row = top; row <= bot; row++) {
- UGRID_FOREACH_CELL(grid, row, left, right+1, {
+ UGRID_FOREACH_CELL(grid, row, left, right + 1, {
cell->data[0] = ' ';
cell->data[1] = 0;
cell->attr = attr;
@@ -99,4 +99,3 @@ static void destroy_cells(UGrid *grid)
XFREE_CLEAR(grid->cells);
}
}
-
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index 1aadaf5c9d..a49e9df9ee 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -8,7 +8,7 @@
#include <string.h>
#include "nvim/ascii.h"
-#include "nvim/aucmd.h"
+#include "nvim/autocmd.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/cursor_shape.h"
@@ -24,6 +24,7 @@
#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/move.h"
+#include "nvim/msgpack_rpc/channel.h"
#include "nvim/normal.h"
#include "nvim/option.h"
#include "nvim/os/input.h"
@@ -63,7 +64,9 @@ static handle_T cursor_grid_handle = DEFAULT_GRID_HANDLE;
static bool has_mouse = false;
static int pending_has_mouse = -1;
-#if MIN_LOG_LEVEL > DEBUG_LOG_LEVEL
+static Array call_buf = ARRAY_DICT_INIT;
+
+#if MIN_LOG_LEVEL > LOGLVL_DBG
# define UI_LOG(funname)
#else
static size_t uilog_seen = 0;
@@ -81,10 +84,10 @@ static char uilog_last_event[1024] = { 0 };
uilog_seen++; \
} else { \
if (uilog_seen > 0) { \
- logmsg(DEBUG_LOG_LEVEL, "UI: ", NULL, -1, true, \
+ logmsg(LOGLVL_DBG, "UI: ", NULL, -1, true, \
"%s (+%zu times...)", uilog_last_event, uilog_seen); \
} \
- logmsg(DEBUG_LOG_LEVEL, "UI: ", NULL, -1, true, STR(funname)); \
+ logmsg(LOGLVL_DBG, "UI: ", NULL, -1, true, STR(funname)); \
uilog_seen = 0; \
xstrlcpy(uilog_last_event, STR(funname), sizeof(uilog_last_event)); \
} \
@@ -122,6 +125,12 @@ void ui_init(void)
default_grid.handle = 1;
msg_grid_adj.target = &default_grid;
ui_comp_init();
+ kv_ensure_space(call_buf, 16);
+}
+
+void ui_free_all_mem(void)
+{
+ kv_destroy(call_buf);
}
void ui_builtin_start(void)
@@ -172,16 +181,6 @@ bool ui_active(void)
return ui_count > 1;
}
-void ui_event(char *name, Array args)
-{
- bool args_consumed = false;
- ui_call_event(name, args, &args_consumed);
- if (!args_consumed) {
- api_free_array(args);
- }
-}
-
-
void ui_refresh(void)
{
if (!ui_active()) {
@@ -222,10 +221,17 @@ void ui_refresh(void)
ui_default_colors_set();
- int save_p_lz = p_lz;
- p_lz = false; // convince redrawing() to return true ...
- screen_resize(width, height);
- p_lz = save_p_lz;
+ if (!ui_client_channel_id) {
+ int save_p_lz = p_lz;
+ p_lz = false; // convince redrawing() to return true ...
+ screen_resize(width, height);
+ p_lz = save_p_lz;
+ } else {
+ Array args = ARRAY_DICT_INIT;
+ ADD(args, INTEGER_OBJ((int)width));
+ ADD(args, INTEGER_OBJ((int)height));
+ rpc_send_event(ui_client_channel_id, "nvim_ui_try_resize", args);
+ }
if (ext_widgets[kUIMessages]) {
p_ch = 0;
@@ -330,7 +336,7 @@ void vim_beep(unsigned val)
// When 'debug' contains "beep" produce a message. If we are sourcing
// a script or executing a function give the user a hint where the beep
// comes from.
- if (vim_strchr(p_debug, 'e') != NULL) {
+ if (vim_strchr((char *)p_debug, 'e') != NULL) {
msg_source(HL_ATTR(HLF_W));
msg_attr(_("Beep!"), HL_ATTR(HLF_W));
}
@@ -422,7 +428,7 @@ void ui_set_ext_option(UI *ui, UIExtension ext, bool active)
void ui_line(ScreenGrid *grid, int row, int startcol, int endcol, int clearcol, int clearattr,
bool wrap)
{
- assert(0 <= row && row < grid->Rows);
+ assert(0 <= row && row < grid->rows);
LineFlags flags = wrap ? kLineFlagWrap : 0;
if (startcol == -1) {
startcol = 0;
@@ -439,7 +445,7 @@ void ui_line(ScreenGrid *grid, int row, int startcol, int endcol, int clearcol,
if (p_wd && !(rdb_flags & RDB_COMPOSITOR)) {
// If 'writedelay' is active, set the cursor to indicate what was drawn.
ui_call_grid_cursor_goto(grid->handle, row,
- MIN(clearcol, (int)grid->Columns-1));
+ MIN(clearcol, (int)grid->cols - 1));
ui_call_flush();
uint64_t wd = (uint64_t)labs(p_wd);
os_microdelay(wd * 1000u, true);
@@ -489,6 +495,11 @@ int ui_current_col(void)
return cursor_col;
}
+handle_T ui_cursor_grid(void)
+{
+ return cursor_grid_handle;
+}
+
void ui_flush(void)
{
cmdline_ui_flush();
@@ -501,13 +512,15 @@ void ui_flush(void)
pending_cursor_update = false;
}
if (pending_mode_info_update) {
- Array style = mode_style_array();
+ Arena arena = ARENA_EMPTY;
+ arena_start(&arena, &ui_ext_fixblk);
+ Array style = mode_style_array(&arena);
bool enabled = (*p_guicursor != NUL);
ui_call_mode_info_set(enabled, style);
- api_free_array(style);
+ arena_mem_free(arena_finish(&arena), &ui_ext_fixblk);
pending_mode_info_update = false;
}
- if (pending_mode_update) {
+ if (pending_mode_update && !starting) {
char *full_name = shape_table[ui_mode_idx].full_name;
ui_call_mode_change(cstr_as_string(full_name), ui_mode_idx);
pending_mode_update = false;
@@ -519,7 +532,6 @@ void ui_flush(void)
ui_call_flush();
}
-
/// Check if 'mouse' is active for the current mode
///
/// TODO(bfredl): precompute the State -> active mapping when 'mouse' changes,
@@ -535,13 +547,13 @@ void ui_check_mouse(void)
int checkfor = MOUSE_NORMAL; // assume normal mode
if (VIsual_active) {
checkfor = MOUSE_VISUAL;
- } else if (State == HITRETURN || State == ASKMORE || State == SETWSIZE) {
+ } else if (State == MODE_HITRETURN || State == MODE_ASKMORE || State == MODE_SETWSIZE) {
checkfor = MOUSE_RETURN;
- } else if (State & INSERT) {
+ } else if (State & MODE_INSERT) {
checkfor = MOUSE_INSERT;
- } else if (State & CMDLINE) {
+ } else if (State & MODE_CMDLINE) {
checkfor = MOUSE_COMMAND;
- } else if (State == CONFIRM || State == EXTERNCMD) {
+ } else if (State == MODE_CONFIRM || State == MODE_EXTERNCMD) {
checkfor = ' '; // don't use mouse for ":confirm" or ":!cmd"
}
@@ -553,7 +565,7 @@ void ui_check_mouse(void)
for (char_u *p = p_mouse; *p; p++) {
switch (*p) {
case 'a':
- if (vim_strchr((char_u *)MOUSE_A, checkfor) != NULL) {
+ if (vim_strchr(MOUSE_A, checkfor) != NULL) {
has_mouse = true;
return;
}
@@ -639,8 +651,8 @@ void ui_grid_resize(handle_T grid_handle, int width, int height, Error *error)
}
} else {
// non-positive indicates no request
- wp->w_height_request = (int)MAX(height, 0);
- wp->w_width_request = (int)MAX(width, 0);
+ wp->w_height_request = MAX(height, 0);
+ wp->w_width_request = MAX(width, 0);
win_set_inner_size(wp);
}
}
diff --git a/src/nvim/ui.h b/src/nvim/ui.h
index 7cc0bd9eff..7dd2f5bce3 100644
--- a/src/nvim/ui.h
+++ b/src/nvim/ui.h
@@ -8,6 +8,7 @@
#include "nvim/api/private/defs.h"
#include "nvim/globals.h"
#include "nvim/highlight_defs.h"
+#include "nvim/memory.h"
typedef enum {
kUICmdline = 0,
@@ -46,6 +47,8 @@ enum {
typedef int LineFlags;
+EXTERN ArenaMem ui_ext_fixblk INIT(= NULL);
+
struct ui_t {
bool rgb;
bool override; ///< Force highest-requested UI capabilities.
@@ -74,6 +77,5 @@ struct ui_t {
# include "ui_events_call.h.generated.h"
#endif
-
EXTERN MultiQueue *resize_events;
#endif // NVIM_UI_H
diff --git a/src/nvim/ui_bridge.c b/src/nvim/ui_bridge.c
index 3402df817a..84098e9476 100644
--- a/src/nvim/ui_bridge.c
+++ b/src/nvim/ui_bridge.c
@@ -166,7 +166,7 @@ static void ui_bridge_raw_line(UI *ui, Integer grid, Integer row, Integer startc
Integer clearcol, Integer clearattr, LineFlags flags,
const schar_T *chunk, const sattr_T *attrs)
{
- size_t ncol = (size_t)(endcol-startcol);
+ size_t ncol = (size_t)(endcol - startcol);
schar_T *c = xmemdup(chunk, ncol * sizeof(schar_T));
sattr_T *hl = xmemdup(attrs, ncol * sizeof(sattr_T));
UI_BRIDGE_CALL(ui, raw_line, 10, ui, INT2PTR(grid), INT2PTR(row),
diff --git a/src/nvim/ui_bridge.h b/src/nvim/ui_bridge.h
index a62ed15621..c18600a857 100644
--- a/src/nvim/ui_bridge.h
+++ b/src/nvim/ui_bridge.h
@@ -38,7 +38,6 @@ struct ui_bridge_data {
uv_mutex_unlock(&d->mutex); \
} while (0)
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ui_bridge.h.generated.h"
#endif
diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c
new file mode 100644
index 0000000000..be01538f67
--- /dev/null
+++ b/src/nvim/ui_client.c
@@ -0,0 +1,207 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "nvim/api/private/dispatch.h"
+#include "nvim/api/private/helpers.h"
+#include "nvim/highlight.h"
+#include "nvim/log.h"
+#include "nvim/map.h"
+#include "nvim/msgpack_rpc/channel.h"
+#include "nvim/screen.h"
+#include "nvim/ui.h"
+#include "nvim/ui_client.h"
+#include "nvim/vim.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "ui_client.c.generated.h"
+
+# include "ui_events_client.generated.h"
+#endif
+
+// Temporary buffer for converting a single grid_line event
+static size_t buf_size = 0;
+static schar_T *buf_char = NULL;
+static sattr_T *buf_attr = NULL;
+
+void ui_client_init(uint64_t chan)
+{
+ Array args = ARRAY_DICT_INIT;
+ int width = Columns;
+ int height = Rows;
+ Dictionary opts = ARRAY_DICT_INIT;
+
+ PUT(opts, "rgb", BOOLEAN_OBJ(true));
+ PUT(opts, "ext_linegrid", BOOLEAN_OBJ(true));
+ PUT(opts, "ext_termcolors", BOOLEAN_OBJ(true));
+
+ ADD(args, INTEGER_OBJ((int)width));
+ ADD(args, INTEGER_OBJ((int)height));
+ ADD(args, DICTIONARY_OBJ(opts));
+
+ rpc_send_event(chan, "nvim_ui_attach", args);
+ ui_client_channel_id = chan;
+}
+
+/// Handler for "redraw" events sent by the NVIM server
+///
+/// This function will be called by handle_request (in msgpack_rpc/channel.c)
+/// The individual ui_events sent by the server are individually handled
+/// by their respective handlers defined in ui_events_client.generated.h
+///
+/// @note The "flush" event is called only once and only after handling all
+/// the other events
+/// @param channel_id: The id of the rpc channel
+/// @param uidata: The dense array containing the ui_events sent by the server
+/// @param[out] err Error details, if any
+Object handle_ui_client_redraw(uint64_t channel_id, Array args, Error *error)
+{
+ for (size_t i = 0; i < args.size; i++) {
+ Array call = args.items[i].data.array;
+ String name = call.items[0].data.string;
+
+ int hash = ui_client_handler_hash(name.data, name.size);
+ if (hash < 0) {
+ ELOG("No ui client handler for %s", name.size ? name.data : "<empty>");
+ continue;
+ }
+ UIClientHandler handler = event_handlers[hash];
+
+ // fprintf(stderr, "%s: %zu\n", name.data, call.size-1);
+ DLOG("Invoke ui client handler for %s", name.data);
+ for (size_t j = 1; j < call.size; j++) {
+ handler.fn(call.items[j].data.array);
+ }
+ }
+
+ return NIL;
+}
+
+/// run the main thread in ui client mode
+///
+/// This is just a stub. the full version will handle input, resizing, etc
+void ui_client_execute(uint64_t chan)
+ FUNC_ATTR_NORETURN
+{
+ while (true) {
+ loop_poll_events(&main_loop, -1);
+ multiqueue_process_events(resize_events);
+ }
+
+ getout(0);
+}
+
+static HlAttrs ui_client_dict2hlattrs(Dictionary d, bool rgb)
+{
+ Error err = ERROR_INIT;
+ Dict(highlight) dict = { 0 };
+ if (!api_dict_to_keydict(&dict, KeyDict_highlight_get_field, d, &err)) {
+ // TODO(bfredl): log "err"
+ return HLATTRS_INIT;
+ }
+ return dict2hlattrs(&dict, true, NULL, &err);
+}
+
+void ui_client_event_grid_resize(Array args)
+{
+ if (args.size < 3
+ || args.items[0].type != kObjectTypeInteger
+ || args.items[1].type != kObjectTypeInteger
+ || args.items[2].type != kObjectTypeInteger) {
+ ELOG("Error handling ui event 'grid_resize'");
+ return;
+ }
+
+ Integer grid = args.items[0].data.integer;
+ Integer width = args.items[1].data.integer;
+ Integer height = args.items[2].data.integer;
+ ui_call_grid_resize(grid, width, height);
+
+ if (buf_size < (size_t)width) {
+ xfree(buf_char);
+ xfree(buf_attr);
+ buf_size = (size_t)width;
+ buf_char = xmalloc(buf_size * sizeof(schar_T));
+ buf_attr = xmalloc(buf_size * sizeof(sattr_T));
+ }
+}
+
+void ui_client_event_grid_line(Array args)
+{
+ if (args.size < 4
+ || args.items[0].type != kObjectTypeInteger
+ || args.items[1].type != kObjectTypeInteger
+ || args.items[2].type != kObjectTypeInteger
+ || args.items[3].type != kObjectTypeArray) {
+ goto error;
+ }
+
+ Integer grid = args.items[0].data.integer;
+ Integer row = args.items[1].data.integer;
+ Integer startcol = args.items[2].data.integer;
+ Array cells = args.items[3].data.array;
+
+ // TODO(hlpr98): Accommodate other LineFlags when included in grid_line
+ LineFlags lineflags = 0;
+
+ size_t j = 0;
+ int cur_attr = 0;
+ int clear_attr = 0;
+ int clear_width = 0;
+ for (size_t i = 0; i < cells.size; i++) {
+ if (cells.items[i].type != kObjectTypeArray) {
+ goto error;
+ }
+ Array cell = cells.items[i].data.array;
+
+ if (cell.size < 1 || cell.items[0].type != kObjectTypeString) {
+ goto error;
+ }
+ String sstring = cell.items[0].data.string;
+
+ char *schar = sstring.data;
+ int repeat = 1;
+ if (cell.size >= 2) {
+ if (cell.items[1].type != kObjectTypeInteger
+ || cell.items[1].data.integer < 0) {
+ goto error;
+ }
+ cur_attr = (int)cell.items[1].data.integer;
+ }
+
+ if (cell.size >= 3) {
+ if (cell.items[2].type != kObjectTypeInteger
+ || cell.items[2].data.integer < 0) {
+ goto error;
+ }
+ repeat = (int)cell.items[2].data.integer;
+ }
+
+ if (i == cells.size - 1 && sstring.size == 1 && sstring.data[0] == ' ' && repeat > 1) {
+ clear_width = repeat;
+ break;
+ }
+
+ for (int r = 0; r < repeat; r++) {
+ if (j >= buf_size) {
+ goto error; // _YIKES_
+ }
+ STRLCPY(buf_char[j], schar, sizeof(schar_T));
+ buf_attr[j++] = cur_attr;
+ }
+ }
+
+ Integer endcol = startcol + (int)j;
+ Integer clearcol = endcol + clear_width;
+ clear_attr = cur_attr;
+
+ ui_call_raw_line(grid, row, startcol, endcol, clearcol, clear_attr, lineflags,
+ (const schar_T *)buf_char, (const sattr_T *)buf_attr);
+ return;
+
+error:
+ ELOG("Error handling ui event 'grid_line'");
+}
diff --git a/src/nvim/ui_client.h b/src/nvim/ui_client.h
new file mode 100644
index 0000000000..41d9fa6227
--- /dev/null
+++ b/src/nvim/ui_client.h
@@ -0,0 +1,17 @@
+#ifndef NVIM_UI_CLIENT_H
+#define NVIM_UI_CLIENT_H
+
+#include "nvim/api/private/defs.h"
+
+typedef struct {
+ const char *name;
+ void (*fn)(Array args);
+} UIClientHandler;
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "ui_client.h.generated.h"
+
+# include "ui_events_client.h.generated.h"
+#endif
+
+#endif // NVIM_UI_CLIENT_H
diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c
index d7becb4fd4..5df70d0d8e 100644
--- a/src/nvim/ui_compositor.c
+++ b/src/nvim/ui_compositor.c
@@ -14,6 +14,7 @@
#include "nvim/api/private/helpers.h"
#include "nvim/ascii.h"
#include "nvim/highlight.h"
+#include "nvim/highlight_group.h"
#include "nvim/lib/kvec.h"
#include "nvim/log.h"
#include "nvim/lua/executor.h"
@@ -23,7 +24,6 @@
#include "nvim/os/os.h"
#include "nvim/popupmnu.h"
#include "nvim/screen.h"
-#include "nvim/syntax.h"
#include "nvim/ugrid.h"
#include "nvim/ui.h"
#include "nvim/ui_compositor.h"
@@ -138,19 +138,19 @@ bool ui_comp_put_grid(ScreenGrid *grid, int row, int col, int height, int width,
// use it.
grid->comp_disabled = true;
compose_area(grid->comp_row, row,
- grid->comp_col, grid->comp_col + grid->Columns);
+ grid->comp_col, grid->comp_col + grid->cols);
if (grid->comp_col < col) {
compose_area(MAX(row, grid->comp_row),
- MIN(row+height, grid->comp_row+grid->Rows),
+ MIN(row + height, grid->comp_row + grid->rows),
grid->comp_col, col);
}
- if (col+width < grid->comp_col+grid->Columns) {
+ if (col + width < grid->comp_col + grid->cols) {
compose_area(MAX(row, grid->comp_row),
- MIN(row+height, grid->comp_row+grid->Rows),
- col+width, grid->comp_col+grid->Columns);
+ MIN(row + height, grid->comp_row + grid->rows),
+ col + width, grid->comp_col + grid->cols);
}
- compose_area(row+height, grid->comp_row+grid->Rows,
- grid->comp_col, grid->comp_col + grid->Columns);
+ compose_area(row + height, grid->comp_row + grid->rows,
+ grid->comp_col, grid->comp_col + grid->cols);
grid->comp_disabled = false;
}
grid->comp_row = row;
@@ -166,19 +166,19 @@ bool ui_comp_put_grid(ScreenGrid *grid, int row, int col, int height, int width,
#endif
size_t insert_at = kv_size(layers);
- while (insert_at > 0 && kv_A(layers, insert_at-1)->zindex > grid->zindex) {
+ while (insert_at > 0 && kv_A(layers, insert_at - 1)->zindex > grid->zindex) {
insert_at--;
}
- if (curwin && kv_A(layers, insert_at-1) == &curwin->w_grid_alloc
- && kv_A(layers, insert_at-1)->zindex == grid->zindex
+ if (curwin && kv_A(layers, insert_at - 1) == &curwin->w_grid_alloc
+ && kv_A(layers, insert_at - 1)->zindex == grid->zindex
&& !on_top) {
insert_at--;
}
// not found: new grid
kv_pushp(layers);
- for (size_t i = kv_size(layers)-1; i > insert_at; i--) {
- kv_A(layers, i) = kv_A(layers, i-1);
+ for (size_t i = kv_size(layers) - 1; i > insert_at; i--) {
+ kv_A(layers, i) = kv_A(layers, i - 1);
kv_A(layers, i)->comp_index = i;
}
kv_A(layers, insert_at) = grid;
@@ -188,8 +188,8 @@ bool ui_comp_put_grid(ScreenGrid *grid, int row, int col, int height, int width,
grid->comp_index = insert_at;
}
if (moved && valid && ui_comp_should_draw()) {
- compose_area(grid->comp_row, grid->comp_row+grid->Rows,
- grid->comp_col, grid->comp_col+grid->Columns);
+ compose_area(grid->comp_row, grid->comp_row + grid->rows,
+ grid->comp_col, grid->comp_col + grid->cols);
}
return moved;
}
@@ -206,8 +206,8 @@ void ui_comp_remove_grid(ScreenGrid *grid)
curgrid = &default_grid;
}
- for (size_t i = grid->comp_index; i < kv_size(layers)-1; i++) {
- kv_A(layers, i) = kv_A(layers, i+1);
+ for (size_t i = grid->comp_index; i < kv_size(layers) - 1; i++) {
+ kv_A(layers, i) = kv_A(layers, i + 1);
kv_A(layers, i)->comp_index = i;
}
(void)kv_pop(layers);
@@ -241,7 +241,7 @@ static void ui_comp_raise_grid(ScreenGrid *grid, size_t new_index)
{
size_t old_index = grid->comp_index;
for (size_t i = old_index; i < new_index; i++) {
- kv_A(layers, i) = kv_A(layers, i+1);
+ kv_A(layers, i) = kv_A(layers, i + 1);
kv_A(layers, i)->comp_index = i;
}
kv_A(layers, new_index) = grid;
@@ -249,10 +249,10 @@ static void ui_comp_raise_grid(ScreenGrid *grid, size_t new_index)
for (size_t i = old_index; i < new_index; i++) {
ScreenGrid *grid2 = kv_A(layers, i);
int startcol = MAX(grid->comp_col, grid2->comp_col);
- int endcol = MIN(grid->comp_col+grid->Columns,
- grid2->comp_col+grid2->Columns);
+ int endcol = MIN(grid->comp_col + grid->cols,
+ grid2->comp_col + grid2->cols);
compose_area(MAX(grid->comp_row, grid2->comp_row),
- MIN(grid->comp_row+grid->Rows, grid2->comp_row+grid2->Rows),
+ MIN(grid->comp_row + grid->rows, grid2->comp_row + grid2->rows),
startcol, endcol);
}
}
@@ -262,13 +262,13 @@ static void ui_comp_grid_cursor_goto(UI *ui, Integer grid_handle, Integer r, Int
if (!ui_comp_should_draw() || !ui_comp_set_grid((int)grid_handle)) {
return;
}
- int cursor_row = curgrid->comp_row+(int)r;
- int cursor_col = curgrid->comp_col+(int)c;
+ int cursor_row = curgrid->comp_row + (int)r;
+ int cursor_col = curgrid->comp_col + (int)c;
// TODO(bfredl): maybe not the best time to do this, for efficiency we
// should configure all grids before entering win_update()
if (curgrid != &default_grid) {
- size_t new_index = kv_size(layers)-1;
+ size_t new_index = kv_size(layers) - 1;
while (new_index > 1 && kv_A(layers, new_index)->zindex > curgrid->zindex) {
new_index--;
@@ -279,7 +279,7 @@ static void ui_comp_grid_cursor_goto(UI *ui, Integer grid_handle, Integer r, Int
}
}
- if (cursor_col >= default_grid.Columns || cursor_row >= default_grid.Rows) {
+ if (cursor_col >= default_grid.cols || cursor_row >= default_grid.rows) {
// TODO(bfredl): this happens with 'writedelay', refactor?
// abort();
return;
@@ -289,17 +289,30 @@ static void ui_comp_grid_cursor_goto(UI *ui, Integer grid_handle, Integer r, Int
ScreenGrid *ui_comp_mouse_focus(int row, int col)
{
- for (ssize_t i = (ssize_t)kv_size(layers)-1; i > 0; i--) {
+ for (ssize_t i = (ssize_t)kv_size(layers) - 1; i > 0; i--) {
ScreenGrid *grid = kv_A(layers, i);
if (grid->focusable
- && row >= grid->comp_row && row < grid->comp_row+grid->Rows
- && col >= grid->comp_col && col < grid->comp_col+grid->Columns) {
+ && row >= grid->comp_row && row < grid->comp_row + grid->rows
+ && col >= grid->comp_col && col < grid->comp_col + grid->cols) {
return grid;
}
}
return NULL;
}
+/// Compute which grid is on top at supplied screen coordinates
+ScreenGrid *ui_comp_get_grid_at_coord(int row, int col)
+{
+ for (ssize_t i = (ssize_t)kv_size(layers) - 1; i > 0; i--) {
+ ScreenGrid *grid = kv_A(layers, i);
+ if (row >= grid->comp_row && row < grid->comp_row + grid->rows
+ && col >= grid->comp_col && col < grid->comp_col + grid->cols) {
+ return grid;
+ }
+ }
+ return &default_grid;
+}
+
/// Baseline implementation. This is always correct, but we can sometimes
/// do something more efficient (where efficiency means smaller deltas to
/// the downstream UI.)
@@ -315,7 +328,7 @@ static void compose_line(Integer row, Integer startcol, Integer endcol, LineFlag
startcol--;
skipstart = 1;
}
- if (endcol < default_grid.Columns && (flags & kLineFlagInvalid)) {
+ if (endcol < default_grid.cols && (flags & kLineFlagInvalid)) {
endcol++;
skipend = 1;
}
@@ -323,9 +336,9 @@ static void compose_line(Integer row, Integer startcol, Integer endcol, LineFlag
int col = (int)startcol;
ScreenGrid *grid = NULL;
schar_T *bg_line = &default_grid.chars[default_grid.line_offset[row]
- +(size_t)startcol];
+ + (size_t)startcol];
sattr_T *bg_attrs = &default_grid.attrs[default_grid.line_offset[row]
- +(size_t)startcol];
+ + (size_t)startcol];
int grid_width, grid_height;
while (col < endcol) {
@@ -337,8 +350,8 @@ static void compose_line(Integer row, Integer startcol, Integer endcol, LineFlag
// first check to see if any grids have pending updates to width/height,
// to ensure that we don't accidentally put any characters into `linebuf`
// that have been invalidated.
- grid_width = MIN(g->Columns, g->comp_width);
- grid_height = MIN(g->Rows, g->comp_height);
+ grid_width = MIN(g->cols, g->comp_width);
+ grid_height = MIN(g->rows, g->comp_height);
if (g->comp_row > row || row >= g->comp_row + grid_height
|| g->comp_disabled) {
continue;
@@ -354,8 +367,8 @@ static void compose_line(Integer row, Integer startcol, Integer endcol, LineFlag
assert(grid != NULL);
assert(until > col);
- assert(until <= default_grid.Columns);
- size_t n = (size_t)(until-col);
+ assert(until <= default_grid.cols);
+ size_t n = (size_t)(until - col);
if (row == msg_sep_row && grid->comp_index <= msg_grid.comp_index) {
// TODO(bfredl): when we implement borders around floating windows, then
@@ -363,18 +376,18 @@ static void compose_line(Integer row, Integer startcol, Integer endcol, LineFlag
grid = &msg_grid;
sattr_T msg_sep_attr = (sattr_T)HL_ATTR(HLF_MSGSEP);
for (int i = col; i < until; i++) {
- memcpy(linebuf[i-startcol], msg_sep_char, sizeof(*linebuf));
- attrbuf[i-startcol] = msg_sep_attr;
+ memcpy(linebuf[i - startcol], msg_sep_char, sizeof(*linebuf));
+ attrbuf[i - startcol] = msg_sep_attr;
}
} else {
- size_t off = grid->line_offset[row-grid->comp_row]
- + (size_t)(col-grid->comp_col);
- memcpy(linebuf+(col-startcol), grid->chars+off, n * sizeof(*linebuf));
- memcpy(attrbuf+(col-startcol), grid->attrs+off, n * sizeof(*attrbuf));
- if (grid->comp_col+grid->Columns > until
- && grid->chars[off+n][0] == NUL) {
- linebuf[until-1-startcol][0] = ' ';
- linebuf[until-1-startcol][1] = '\0';
+ size_t off = grid->line_offset[row - grid->comp_row]
+ + (size_t)(col - grid->comp_col);
+ memcpy(linebuf + (col - startcol), grid->chars + off, n * sizeof(*linebuf));
+ memcpy(attrbuf + (col - startcol), grid->attrs + off, n * sizeof(*attrbuf));
+ if (grid->comp_col + grid->cols > until
+ && grid->chars[off + n][0] == NUL) {
+ linebuf[until - 1 - startcol][0] = ' ';
+ linebuf[until - 1 - startcol][1] = '\0';
if (col == startcol && n == 1) {
skipstart = 0;
}
@@ -384,18 +397,18 @@ static void compose_line(Integer row, Integer startcol, Integer endcol, LineFlag
// 'pumblend' and 'winblend'
if (grid->blending) {
int width;
- for (int i = col-(int)startcol; i < until-startcol; i += width) {
+ for (int i = col - (int)startcol; i < until - startcol; i += width) {
width = 1;
// negative space
bool thru = strequal((char *)linebuf[i], " ") && bg_line[i][0] != NUL;
- if (i+1 < endcol-startcol && bg_line[i+1][0] == NUL) {
+ if (i + 1 < endcol - startcol && bg_line[i + 1][0] == NUL) {
width = 2;
- thru &= strequal((char *)linebuf[i+1], " ");
+ thru &= strequal((char *)linebuf[i + 1], " ");
}
attrbuf[i] = (sattr_T)hl_blend_attrs(bg_attrs[i], attrbuf[i], &thru);
if (width == 2) {
- attrbuf[i+1] = (sattr_T)hl_blend_attrs(bg_attrs[i+1],
- attrbuf[i+1], &thru);
+ attrbuf[i + 1] = (sattr_T)hl_blend_attrs(bg_attrs[i + 1],
+ attrbuf[i + 1], &thru);
}
if (thru) {
memcpy(linebuf + i, bg_line + i, (size_t)width * sizeof(linebuf[i]));
@@ -405,19 +418,19 @@ static void compose_line(Integer row, Integer startcol, Integer endcol, LineFlag
// Tricky: if overlap caused a doublewidth char to get cut-off, must
// replace the visible half with a space.
- if (linebuf[col-startcol][0] == NUL) {
- linebuf[col-startcol][0] = ' ';
- linebuf[col-startcol][1] = NUL;
- if (col == endcol-1) {
+ if (linebuf[col - startcol][0] == NUL) {
+ linebuf[col - startcol][0] = ' ';
+ linebuf[col - startcol][1] = NUL;
+ if (col == endcol - 1) {
skipend = 0;
}
- } else if (n > 1 && linebuf[col-startcol+1][0] == NUL) {
+ } else if (n > 1 && linebuf[col - startcol + 1][0] == NUL) {
skipstart = 0;
}
col = until;
}
- if (linebuf[endcol-startcol-1][0] == NUL) {
+ if (linebuf[endcol - startcol - 1][0] == NUL) {
skipend = 0;
}
@@ -430,7 +443,7 @@ static void compose_line(Integer row, Integer startcol, Integer endcol, LineFlag
flags = flags & ~kLineFlagWrap;
}
- for (int i = skipstart; i < (endcol-skipend)-startcol; i++) {
+ for (int i = skipstart; i < (endcol - skipend) - startcol; i++) {
if (attrbuf[i] < 0) {
if (rdb_flags & RDB_INVALID) {
abort();
@@ -439,10 +452,10 @@ static void compose_line(Integer row, Integer startcol, Integer endcol, LineFlag
}
}
}
- ui_composed_call_raw_line(1, row, startcol+skipstart,
- endcol-skipend, endcol-skipend, 0, flags,
- (const schar_T *)linebuf+skipstart,
- (const sattr_T *)attrbuf+skipstart);
+ ui_composed_call_raw_line(1, row, startcol + skipstart,
+ endcol - skipend, endcol - skipend, 0, flags,
+ (const schar_T *)linebuf + skipstart,
+ (const sattr_T *)attrbuf + skipstart);
}
static void compose_debug(Integer startrow, Integer endrow, Integer startcol, Integer endcol,
@@ -452,8 +465,8 @@ static void compose_debug(Integer startrow, Integer endrow, Integer startcol, In
return;
}
- endrow = MIN(endrow, default_grid.Rows);
- endcol = MIN(endcol, default_grid.Columns);
+ endrow = MIN(endrow, default_grid.rows);
+ endcol = MIN(endcol, default_grid.cols);
int attr = syn_id2attr(syn_id);
for (int row = (int)startrow; row < endrow; row++) {
@@ -462,9 +475,8 @@ static void compose_debug(Integer startrow, Integer endrow, Integer startcol, In
(const sattr_T *)attrbuf);
}
-
if (delay) {
- debug_delay(endrow-startrow);
+ debug_delay(endrow - startrow);
}
}
@@ -476,12 +488,11 @@ static void debug_delay(Integer lines)
os_microdelay(factor * wd * 1000u, true);
}
-
static void compose_area(Integer startrow, Integer endrow, Integer startcol, Integer endcol)
{
compose_debug(startrow, endrow, startcol, endcol, dbghl_recompose, true);
- endrow = MIN(endrow, default_grid.Rows);
- endcol = MIN(endcol, default_grid.Columns);
+ endrow = MIN(endrow, default_grid.rows);
+ endcol = MIN(endcol, default_grid.cols);
if (endcol <= startcol) {
return;
}
@@ -497,8 +508,8 @@ static void compose_area(Integer startrow, Integer endrow, Integer startcol, Int
void ui_comp_compose_grid(ScreenGrid *grid)
{
if (ui_comp_should_draw()) {
- compose_area(grid->comp_row, grid->comp_row+grid->Rows,
- grid->comp_col, grid->comp_col+grid->Columns);
+ compose_area(grid->comp_row, grid->comp_row + grid->rows,
+ grid->comp_col, grid->comp_col + grid->cols);
}
}
@@ -523,17 +534,17 @@ static void ui_comp_raw_line(UI *ui, Integer grid, Integer row, Integer startcol
// TODO(bfredl): this should not really be necessary. But on some condition
// when resizing nvim, a window will be attempted to be drawn on the older
// and possibly larger global screen size.
- if (row >= default_grid.Rows) {
+ if (row >= default_grid.rows) {
DLOG("compositor: invalid row %" PRId64 " on grid %" PRId64, row, grid);
return;
}
- if (clearcol > default_grid.Columns) {
+ if (clearcol > default_grid.cols) {
DLOG("compositor: invalid last column %" PRId64 " on grid %" PRId64,
clearcol, grid);
- if (startcol >= default_grid.Columns) {
+ if (startcol >= default_grid.cols) {
return;
}
- clearcol = default_grid.Columns;
+ clearcol = default_grid.cols;
endcol = MIN(endcol, clearcol);
}
@@ -541,13 +552,13 @@ static void ui_comp_raw_line(UI *ui, Integer grid, Integer row, Integer startcol
// TODO(bfredl): eventually should just fix compose_line to respect clearing
// and optimize it for uncovered lines.
if (flags & kLineFlagInvalid || covered || curgrid->blending) {
- compose_debug(row, row+1, startcol, clearcol, dbghl_composed, true);
+ compose_debug(row, row + 1, startcol, clearcol, dbghl_composed, true);
compose_line(row, startcol, clearcol, flags);
} else {
- compose_debug(row, row+1, startcol, endcol, dbghl_normal, false);
- compose_debug(row, row+1, endcol, clearcol, dbghl_clear, true);
+ compose_debug(row, row + 1, startcol, endcol, dbghl_normal, false);
+ compose_debug(row, row + 1, endcol, clearcol, dbghl_clear, true);
#ifndef NDEBUG
- for (int i = 0; i < endcol-startcol; i++) {
+ for (int i = 0; i < endcol - startcol; i++) {
assert(attrs[i] >= 0);
}
#endif
@@ -572,7 +583,7 @@ static void ui_comp_msg_set_pos(UI *ui, Integer grid, Integer row, Boolean scrol
{
msg_grid.comp_row = (int)row;
if (scrolled && row > 0) {
- msg_sep_row = (int)row-1;
+ msg_sep_row = (int)row - 1;
if (sep_char.data) {
STRLCPY(msg_sep_char, sep_char.data, sizeof(msg_sep_char));
}
@@ -581,19 +592,19 @@ static void ui_comp_msg_set_pos(UI *ui, Integer grid, Integer row, Boolean scrol
}
if (row > msg_current_row && ui_comp_should_draw()) {
- compose_area(MAX(msg_current_row-1, 0), row, 0, default_grid.Columns);
+ compose_area(MAX(msg_current_row - 1, 0), row, 0, default_grid.cols);
} else if (row < msg_current_row && ui_comp_should_draw()
&& msg_current_row < Rows) {
int delta = msg_current_row - (int)row;
if (msg_grid.blending) {
- int first_row = MAX((int)row-(scrolled?1:0), 0);
- compose_area(first_row, Rows-delta, 0, Columns);
+ int first_row = MAX((int)row - (scrolled?1:0), 0);
+ compose_area(first_row, Rows - delta, 0, Columns);
} else {
// scroll separator together with message text
- int first_row = MAX((int)row-(msg_was_scrolled?1:0), 0);
+ int first_row = MAX((int)row - (msg_was_scrolled?1:0), 0);
ui_composed_call_grid_scroll(1, first_row, Rows, 0, Columns, delta, 0);
if (scrolled && !msg_was_scrolled && row > 0) {
- compose_area(row-1, row, 0, Columns);
+ compose_area(row - 1, row, 0, Columns);
}
}
}
@@ -607,9 +618,9 @@ static void ui_comp_msg_set_pos(UI *ui, Integer grid, Integer row, Boolean scrol
/// TODO(bfredl): currently this only handles message row
static bool curgrid_covered_above(int row)
{
- bool above_msg = (kv_A(layers, kv_size(layers)-1) == &msg_grid
- && row < msg_current_row-(msg_was_scrolled?1:0));
- return kv_size(layers)-(above_msg?1:0) > curgrid->comp_index+1;
+ bool above_msg = (kv_A(layers, kv_size(layers) - 1) == &msg_grid
+ && row < msg_current_row - (msg_was_scrolled?1:0));
+ return kv_size(layers) - (above_msg?1:0) > curgrid->comp_index + 1;
}
static void ui_comp_grid_scroll(UI *ui, Integer grid, Integer top, Integer bot, Integer left,
@@ -634,8 +645,8 @@ static void ui_comp_grid_scroll(UI *ui, Integer grid, Integer top, Integer bot,
// row, where the latter might scroll invalid space created by the first.
// ideally win_update() should keep track of this itself and not scroll
// the invalid space.
- if (curgrid->attrs[curgrid->line_offset[r-curgrid->comp_row]
- +left-curgrid->comp_col] >= 0) {
+ if (curgrid->attrs[curgrid->line_offset[r - curgrid->comp_row]
+ + (size_t)left - (size_t)curgrid->comp_col] >= 0) {
compose_line(r, left, right, 0);
}
}
@@ -665,4 +676,3 @@ static void ui_comp_grid_resize(UI *ui, Integer grid, Integer width, Integer hei
}
}
}
-
diff --git a/src/nvim/undo.c b/src/nvim/undo.c
index d18f35a43a..8324db37c6 100644
--- a/src/nvim/undo.c
+++ b/src/nvim/undo.c
@@ -89,6 +89,7 @@
#include "nvim/change.h"
#include "nvim/cursor.h"
#include "nvim/edit.h"
+#include "nvim/ex_getln.h"
#include "nvim/extmark.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
@@ -272,7 +273,7 @@ int u_inssub(linenr_T lnum)
*/
int u_savedel(linenr_T lnum, long nlines)
{
- return u_savecommon(curbuf, lnum - 1, lnum + nlines,
+ return u_savecommon(curbuf, lnum - 1, lnum + (linenr_T)nlines,
nlines == curbuf->b_ml.ml_line_count ? 2 : lnum, false);
}
@@ -297,7 +298,7 @@ bool undo_allowed(buf_T *buf)
// Don't allow changes in the buffer while editing the cmdline. The
// caller of getcmdline() may get confused.
if (textlock != 0) {
- emsg(_(e_secure));
+ emsg(_(e_textlock));
return false;
}
@@ -611,7 +612,6 @@ int u_savecommon(buf_T *buf, linenr_T top, linenr_T bot, linenr_T newbot, int re
return OK;
}
-
// magic at start of undofile
#define UF_START_MAGIC "Vim\237UnDo\345"
#define UF_START_MAGIC_LEN 9
@@ -694,15 +694,14 @@ char *u_get_undo_file_name(const char *const buf_ffname, const bool reading)
// When not reading use the first directory that exists or ".".
dirp = (char *)p_udir;
while (*dirp != NUL) {
- size_t dir_len = copy_option_part((char_u **)&dirp, (char_u *)dir_name,
- MAXPATHL, ",");
+ size_t dir_len = copy_option_part(&dirp, dir_name, MAXPATHL, ",");
if (dir_len == 1 && dir_name[0] == '.') {
// Use same directory as the ffname,
// "dir/name" -> "dir/.name.un~"
const size_t ffname_len = strlen(ffname);
undo_file_name = xmalloc(ffname_len + 6);
memmove(undo_file_name, ffname, ffname_len + 1);
- char *const tail = (char *)path_tail((char_u *)undo_file_name);
+ char *const tail = path_tail(undo_file_name);
const size_t tail_len = strlen(tail);
memmove(tail + 1, tail, tail_len + 1);
*tail = '.';
@@ -1187,7 +1186,7 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf,
bufinfo_T bi;
if (name == NULL) {
- file_name = u_get_undo_file_name((char *)buf->b_ffname, false);
+ file_name = u_get_undo_file_name(buf->b_ffname, false);
if (file_name == NULL) {
if (p_verbose > 0) {
verbose_enter();
@@ -1292,7 +1291,7 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf,
FileInfo file_info_old;
FileInfo file_info_new;
if (buf->b_ffname != NULL
- && os_fileinfo((char *)buf->b_ffname, &file_info_old)
+ && os_fileinfo(buf->b_ffname, &file_info_old)
&& os_fileinfo(file_name, &file_info_new)
&& file_info_old.stat.st_gid != file_info_new.stat.st_gid
&& os_fchown(fd, (uv_uid_t)-1, (uv_gid_t)file_info_old.stat.st_gid)) {
@@ -1371,10 +1370,8 @@ write_error:
#ifdef HAVE_ACL
if (buf->b_ffname != NULL) {
- vim_acl_T acl;
-
// For systems that support ACL: get the ACL from the original file.
- acl = mch_get_acl(buf->b_ffname);
+ vim_acl_T acl = mch_get_acl((char_u *)buf->b_ffname);
mch_set_acl((char_u *)file_name, acl);
mch_free_acl(acl);
}
@@ -1399,7 +1396,7 @@ void u_read_undo(char *name, const char_u *hash, const char_u *orig_name FUNC_AT
char *file_name;
if (name == NULL) {
- file_name = u_get_undo_file_name((char *)curbuf->b_ffname, true);
+ file_name = u_get_undo_file_name(curbuf->b_ffname, true);
if (file_name == NULL) {
return;
}
@@ -1469,8 +1466,7 @@ void u_read_undo(char *name, const char_u *hash, const char_u *orig_name FUNC_AT
if (name == NULL) {
verbose_enter();
}
- give_warning((char_u *)
- _("File contents changed, cannot use undo info"), true);
+ give_warning(_("File contents changed, cannot use undo info"), true);
if (name == NULL) {
verbose_leave();
}
@@ -1959,6 +1955,11 @@ void undo_time(long step, bool sec, bool file, bool absolute)
bool above = false;
bool did_undo = true;
+ if (text_locked()) {
+ text_locked_msg();
+ return;
+ }
+
// First make sure the current undoable change is synced.
if (curbuf->b_u_synced == false) {
u_sync(true);
@@ -2338,7 +2339,7 @@ static void u_undoredo(int undo, bool do_buf_event)
}
oldsize = bot - top - 1; // number of lines before undo
- newsize = uep->ue_size; // number of lines after undo
+ newsize = (linenr_T)uep->ue_size; // number of lines after undo
if (top < newlnum) {
/* If the saved cursor is somewhere in this undo block, move it to
@@ -2352,8 +2353,8 @@ static void u_undoredo(int undo, bool do_buf_event)
/* Use the first line that actually changed. Avoids that
* undoing auto-formatting puts the cursor in the previous
* line. */
- for (i = 0; i < newsize && i < oldsize; ++i) {
- if (STRCMP(uep->ue_array[i], ml_get(top + 1 + i)) != 0) {
+ for (i = 0; i < newsize && i < oldsize; i++) {
+ if (STRCMP(uep->ue_array[i], ml_get(top + 1 + (linenr_T)i)) != 0) {
break;
}
}
@@ -2361,7 +2362,7 @@ static void u_undoredo(int undo, bool do_buf_event)
newlnum = top;
curwin->w_cursor.lnum = newlnum + 1;
} else if (i < newsize) {
- newlnum = top + i;
+ newlnum = top + (linenr_T)i;
curwin->w_cursor.lnum = newlnum + 1;
}
}
@@ -2395,9 +2396,9 @@ static void u_undoredo(int undo, bool do_buf_event)
* should get rid of, by replacing it with the new line
*/
if (empty_buffer && lnum == 0) {
- ml_replace((linenr_T)1, uep->ue_array[i], true);
+ ml_replace((linenr_T)1, (char *)uep->ue_array[i], true);
} else {
- ml_append(lnum, uep->ue_array[i], (colnr_T)0, false);
+ ml_append(lnum, (char *)uep->ue_array[i], (colnr_T)0, false);
}
xfree(uep->ue_array[i]);
}
@@ -2406,8 +2407,7 @@ static void u_undoredo(int undo, bool do_buf_event)
// Adjust marks
if (oldsize != newsize) {
- mark_adjust(top + 1, top + oldsize, (long)MAXLNUM,
- (long)newsize - (long)oldsize, kExtmarkNOOP);
+ mark_adjust(top + 1, top + oldsize, MAXLNUM, newsize - oldsize, kExtmarkNOOP);
if (curbuf->b_op_start.lnum > top + oldsize) {
curbuf->b_op_start.lnum += newsize - oldsize;
}
@@ -2418,10 +2418,11 @@ static void u_undoredo(int undo, bool do_buf_event)
changed_lines(top + 1, 0, bot, newsize - oldsize, do_buf_event);
- // set '[ and '] mark
+ // Set the '[ mark.
if (top + 1 < curbuf->b_op_start.lnum) {
curbuf->b_op_start.lnum = top + 1;
}
+ // Set the '] mark.
if (newsize == 0 && top + 1 > curbuf->b_op_end.lnum) {
curbuf->b_op_end.lnum = top + 1;
} else if (top + newsize > curbuf->b_op_end.lnum) {
@@ -2442,6 +2443,14 @@ static void u_undoredo(int undo, bool do_buf_event)
newlist = uep;
}
+ // Ensure the '[ and '] marks are within bounds.
+ if (curbuf->b_op_start.lnum > curbuf->b_ml.ml_line_count) {
+ curbuf->b_op_start.lnum = curbuf->b_ml.ml_line_count;
+ }
+ if (curbuf->b_op_end.lnum > curbuf->b_ml.ml_line_count) {
+ curbuf->b_op_end.lnum = curbuf->b_ml.ml_line_count;
+ }
+
// Adjust Extmarks
ExtmarkUndoObject undo_info;
if (undo) {
@@ -2463,7 +2472,6 @@ static void u_undoredo(int undo, bool do_buf_event)
}
// finish Adjusting extmarks
-
curhead->uh_entry = newlist;
curhead->uh_flags = new_flags;
if ((old_flags & UH_EMPTYBUF) && buf_is_empty(curbuf)) {
@@ -2633,6 +2641,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,
@@ -2905,7 +2917,7 @@ static void u_getbot(buf_T *buf)
* old line count subtracted from the current line count.
*/
extra = buf->b_ml.ml_line_count - uep->ue_lcount;
- uep->ue_bot = uep->ue_top + uep->ue_size + 1 + extra;
+ uep->ue_bot = uep->ue_top + (linenr_T)uep->ue_size + 1 + extra;
if (uep->ue_bot < 1 || uep->ue_bot > buf->b_ml.ml_line_count) {
iemsg(_("E440: undo line missing"));
uep->ue_bot = uep->ue_top + 1; // assume all lines deleted, will
@@ -3104,9 +3116,9 @@ void u_undoline(void)
}
oldp = u_save_line(curbuf->b_u_line_lnum);
- ml_replace(curbuf->b_u_line_lnum, curbuf->b_u_line_ptr, true);
+ ml_replace(curbuf->b_u_line_lnum, (char *)curbuf->b_u_line_ptr, true);
changed_bytes(curbuf->b_u_line_lnum, 0);
- extmark_splice_cols(curbuf, (int)curbuf->b_u_line_lnum-1, 0, (colnr_T)STRLEN(oldp),
+ extmark_splice_cols(curbuf, (int)curbuf->b_u_line_lnum - 1, 0, (colnr_T)STRLEN(oldp),
(colnr_T)STRLEN(curbuf->b_u_line_ptr), kExtmarkUndo);
xfree(curbuf->b_u_line_ptr);
curbuf->b_u_line_ptr = oldp;
diff --git a/src/nvim/undo_defs.h b/src/nvim/undo_defs.h
index 3267b2f71e..d8470b07b1 100644
--- a/src/nvim/undo_defs.h
+++ b/src/nvim/undo_defs.h
@@ -33,8 +33,8 @@ struct u_entry {
};
struct u_header {
- /* The following have a pointer and a number. The number is used when
- * reading the undo file in u_read_undo() */
+ // The following have a pointer and a number. The number is used when reading
+ // the undo file in u_read_undo()
union {
u_header_T *ptr; // pointer to next undo header in list
long seq;
@@ -80,4 +80,4 @@ typedef struct {
FILE *bi_fp;
} bufinfo_T;
-#endif // NVIM_UNDO_DEFS_H
+#endif // NVIM_UNDO_DEFS_H
diff --git a/src/nvim/version.c b/src/nvim/version.c
index 5e2a81795a..3ffae6592c 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -35,7 +35,6 @@
#endif
#define NVIM_VERSION_LONG "NVIM " NVIM_VERSION_MEDIUM
-
char *Version = VIM_VERSION_SHORT;
char *longVersion = NVIM_VERSION_LONG;
char *version_buildtype = "Build type: " NVIM_VERSION_BUILD_TYPE;
@@ -215,7 +214,7 @@ static const int included_patches[] = {
1709,
1708,
1707,
- // 1706,
+ 1706,
1705,
1704,
1703,
@@ -305,14 +304,14 @@ static const int included_patches[] = {
1619,
1618,
1617,
- // 1616,
+ 1616,
1615,
1614,
1613,
1612,
1611,
1610,
- // 1609,
+ 1609,
1608,
1607,
1606,
@@ -333,7 +332,7 @@ static const int included_patches[] = {
1591,
1590,
1589,
- // 1588,
+ 1588,
1587,
1586,
1585,
@@ -347,11 +346,11 @@ static const int included_patches[] = {
1577,
1576,
1575,
- // 1574,
+ 1574,
1573,
1572,
1571,
- // 1570,
+ 1570,
1569,
1568,
1567,
@@ -359,11 +358,11 @@ static const int included_patches[] = {
1565,
1564,
1563,
- // 1562,
+ 1562,
1561,
1560,
1559,
- // 1558,
+ 1558,
1557,
1556,
1555,
@@ -554,7 +553,7 @@ static const int included_patches[] = {
1370,
1369,
1368,
- // 1367,
+ 1367,
1366,
1365,
1364,
@@ -582,11 +581,11 @@ static const int included_patches[] = {
1342,
1341,
1340,
- // 1339,
+ 1339,
1338,
1337,
1336,
- // 1335,
+ 1335,
1334,
1333,
1332,
@@ -614,7 +613,7 @@ static const int included_patches[] = {
1310,
1309,
1308,
- // 1307,
+ 1307,
1306,
1305,
1304,
@@ -802,7 +801,7 @@ static const int included_patches[] = {
1122,
1121,
1120,
- // 1119,
+ 1119,
1118,
1117,
1116,
@@ -1998,6 +1997,7 @@ Dictionary version_dict(void)
PUT(d, "major", INTEGER_OBJ(NVIM_VERSION_MAJOR));
PUT(d, "minor", INTEGER_OBJ(NVIM_VERSION_MINOR));
PUT(d, "patch", INTEGER_OBJ(NVIM_VERSION_PATCH));
+ PUT(d, "prerelease", BOOLEAN_OBJ(NVIM_VERSION_PRERELEASE[0] != '\0'));
PUT(d, "api_level", INTEGER_OBJ(NVIM_API_LEVEL));
PUT(d, "api_compatible", INTEGER_OBJ(NVIM_API_LEVEL_COMPAT));
PUT(d, "api_prerelease", BOOLEAN_OBJ(NVIM_API_PRERELEASE));
@@ -2020,7 +2020,7 @@ void ex_version(exarg_T *eap)
/// @param wrap
static void version_msg_wrap(char *s, int wrap)
{
- int len = vim_strsize((char_u *)s) + (wrap ? 2 : 0);
+ int len = vim_strsize(s) + (wrap ? 2 : 0);
if (!got_int
&& (len < Columns)
@@ -2053,7 +2053,7 @@ static void list_features(void)
version_msg(_("\n\nFeatures: "));
for (int i = 0; features[i] != NULL; i++) {
version_msg(features[i]);
- if (features[i+1] != NULL) {
+ if (features[i + 1] != NULL) {
version_msg(" ");
}
}
@@ -2071,7 +2071,7 @@ void list_in_columns(char_u **items, int size, int current)
// Find the length of the longest item, use that + 1 as the column width.
int i;
for (i = 0; size < 0 ? items[i] != NULL : i < size; i++) {
- int l = vim_strsize(items[i]) + (i == current ? 2 : 0);
+ int l = vim_strsize((char *)items[i]) + (i == current ? 2 : 0);
if (l > width) {
width = l;
@@ -2093,7 +2093,7 @@ void list_in_columns(char_u **items, int size, int current)
// The rightmost column doesn't need a separator.
// Sacrifice it to fit in one more column if possible.
- int ncol = (int)(Columns + 1) / width;
+ int ncol = (Columns + 1) / width;
int nrow = item_count / ncol + (item_count % ncol ? 1 : 0);
int cur_row = 1;
@@ -2193,15 +2193,14 @@ void list_version(void)
version_msg("\nRun :checkhealth for more info");
}
-
/// Show the intro message when not editing a file.
void maybe_intro_message(void)
{
if (buf_is_empty(curbuf)
&& (curbuf->b_fname == NULL)
&& (firstwin->w_next == NULL)
- && (vim_strchr(p_shm, SHM_INTRO) == NULL)) {
- intro_message(FALSE);
+ && (vim_strchr((char *)p_shm, SHM_INTRO) == NULL)) {
+ intro_message(false);
}
}
@@ -2295,7 +2294,7 @@ static void do_intro_line(long row, char_u *mesg, int attr)
int clen;
// Center the message horizontally.
- col = vim_strsize(mesg);
+ col = vim_strsize((char *)mesg);
col = (Columns - col) / 2;
@@ -2310,8 +2309,8 @@ static void do_intro_line(long row, char_u *mesg, int attr)
for (l = 0;
p[l] != NUL && (l == 0 || (p[l] != '<' && p[l - 1] != '>'));
l++) {
- clen += ptr2cells(p + l);
- l += utfc_ptr2len(p + l) - 1;
+ clen += ptr2cells((char *)p + l);
+ l += utfc_ptr2len((char *)p + l) - 1;
}
assert(row <= INT_MAX && col <= INT_MAX);
grid_puts_len(&default_grid, p, l, (int)row, (int)col,
@@ -2329,4 +2328,3 @@ void ex_intro(exarg_T *eap)
intro_message(TRUE);
wait_return(TRUE);
}
-
diff --git a/src/nvim/vim.h b/src/nvim/vim.h
index 6e0e9922a6..31ac5a67ff 100644
--- a/src/nvim/vim.h
+++ b/src/nvim/vim.h
@@ -10,7 +10,6 @@
#define SYS_OPTWIN_FILE "$VIMRUNTIME/optwin.vim"
#define RUNTIME_DIRNAME "runtime"
-
#include "auto/config.h"
#define HAVE_PATHDEF
@@ -31,51 +30,46 @@ enum { NUMBUFLEN = 65, };
#define ROOT_UID 0
#include "nvim/gettext.h"
-#include "nvim/keymap.h"
+#include "nvim/keycodes.h"
#include "nvim/macros.h"
// special attribute addition: Put message in history
#define MSG_HIST 0x1000
-
-// values for State
+// Values for State
//
-// The lower bits up to 0x20 are used to distinguish normal/visual/op_pending
-// and cmdline/insert+replace mode. This is used for mapping. If none of
-// these bits are set, no mapping is done.
-// The upper bits are used to distinguish between other states.
-
-#define NORMAL 0x01 // Normal mode, command expected
-#define VISUAL 0x02 // Visual mode - use get_real_state()
-#define OP_PENDING 0x04 // Normal mode, operator is pending - use
- // get_real_state()
-#define CMDLINE 0x08 // Editing command line
-#define INSERT 0x10 // Insert mode
-#define LANGMAP 0x20 // Language mapping, can be combined with
- // INSERT and CMDLINE
-
-#define REPLACE_FLAG 0x40 // Replace mode flag
-#define REPLACE (REPLACE_FLAG + INSERT)
-#define VREPLACE_FLAG 0x80 // Virtual-replace mode flag
-#define VREPLACE (REPLACE_FLAG + VREPLACE_FLAG + INSERT)
-#define LREPLACE (REPLACE_FLAG + LANGMAP)
-
-#define NORMAL_BUSY (0x100 + NORMAL) // Normal mode, busy with a command
-#define HITRETURN (0x200 + NORMAL) // waiting for return or command
-#define ASKMORE 0x300 // Asking if you want --more--
-#define SETWSIZE 0x400 // window size has changed
-#define ABBREV 0x500 // abbreviation instead of mapping
-#define EXTERNCMD 0x600 // executing an external command
-#define SHOWMATCH (0x700 + INSERT) // show matching paren
-#define CONFIRM 0x800 // ":confirm" prompt
-#define SELECTMODE 0x1000 // Select mode, only for mappings
-#define TERM_FOCUS 0x2000 // Terminal focus mode
-#define CMDPREVIEW 0x4000 // Showing 'inccommand' command "live" preview.
-
-#define MODE_MAX_LENGTH 4 // max mode length returned in mode()
-
-// all mode bits used for mapping
-#define MAP_ALL_MODES (0x3f | SELECTMODE | TERM_FOCUS)
+// The lower bits up to 0x80 are used to distinguish normal/visual/op_pending
+// /cmdline/insert/replace/terminal mode. This is used for mapping. If none
+// of these bits are set, no mapping is done. See the comment above do_map().
+// The upper bits are used to distinguish between other states and variants of
+// the base modes.
+
+#define MODE_NORMAL 0x01 // Normal mode, command expected
+#define MODE_VISUAL 0x02 // Visual mode - use get_real_state()
+#define MODE_OP_PENDING 0x04 // Normal mode, operator is pending - use
+ // get_real_state()
+#define MODE_CMDLINE 0x08 // Editing the command line
+#define MODE_INSERT 0x10 // Insert mode, also for Replace mode
+#define MODE_LANGMAP 0x20 // Language mapping, can be combined with
+ // MODE_INSERT and MODE_CMDLINE
+#define MODE_SELECT 0x40 // Select mode, use get_real_state()
+#define MODE_TERMINAL 0x80 // Terminal mode
+
+#define MAP_ALL_MODES 0xff // all mode bits used for mapping
+
+#define REPLACE_FLAG 0x100 // Replace mode flag
+#define MODE_REPLACE (REPLACE_FLAG | MODE_INSERT)
+#define VREPLACE_FLAG 0x200 // Virtual-replace mode flag
+#define MODE_VREPLACE (REPLACE_FLAG | VREPLACE_FLAG | MODE_INSERT)
+#define MODE_LREPLACE (REPLACE_FLAG | MODE_LANGMAP)
+
+#define MODE_NORMAL_BUSY (0x1000 | MODE_NORMAL) // Normal mode, busy with a command
+#define MODE_HITRETURN (0x2000 | MODE_NORMAL) // waiting for return or command
+#define MODE_ASKMORE 0x3000 // Asking if you want --more--
+#define MODE_SETWSIZE 0x4000 // window size has changed
+#define MODE_EXTERNCMD 0x5000 // executing an external command
+#define MODE_SHOWMATCH (0x6000 | MODE_INSERT) // show matching paren
+#define MODE_CONFIRM 0x7000 // ":confirm" prompt
/// Directions.
typedef enum {
@@ -105,7 +99,6 @@ typedef enum {
#define VAR_TYPE_SPECIAL 7
#define VAR_TYPE_BLOB 10
-
// values for xp_context when doing command line completion
enum {
@@ -166,7 +159,6 @@ enum {
EXPAND_LUA,
};
-
// Minimal size for block 0 of a swap file.
// NOTE: This depends on size of struct block0! It's not done with a sizeof(),
// because struct block0 is defined in memline.c (Sorry).
@@ -175,7 +167,6 @@ enum {
#define MIN_SWAP_PAGE_SIZE 1048
#define MAX_SWAP_PAGE_SIZE 50000
-
// Boolean constants
#ifndef TRUE
@@ -188,7 +179,6 @@ enum {
#define STATUS_HEIGHT 1 // height of a status line under a window
#define QF_WINHEIGHT 10 // default height for quickfix window
-
// Buffer sizes
#ifndef CMDBUFFSIZE
@@ -201,7 +191,6 @@ enum {
enum { FOLD_TEXT_LEN = 51, }; //!< buffer size for get_foldtext()
-
// Maximum length of key sequence to be mapped.
// Must be able to hold an Amiga resize report.
@@ -217,9 +206,9 @@ enum { FOLD_TEXT_LEN = 51, }; //!< buffer size for get_foldtext()
#define STRLEN(s) strlen((char *)(s))
#ifdef HAVE_STRNLEN
-# define STRNLEN(s, n) strnlen((char *)(s), (size_t)(n))
+# define STRNLEN(s, n) strnlen((char *)(s), (size_t)(n))
#else
-# define STRNLEN(s, n) xstrnlen((char *)(s), (size_t)(n))
+# define STRNLEN(s, n) xstrnlen((char *)(s), (size_t)(n))
#endif
#define STRCPY(d, s) strcpy((char *)(d), (char *)(s))
#define STRNCPY(d, s, n) strncpy((char *)(d), (char *)(s), (size_t)(n))
@@ -255,8 +244,6 @@ enum { FOLD_TEXT_LEN = 51, }; //!< buffer size for get_foldtext()
#define STRNCAT(d, s, n) strncat((char *)(d), (char *)(s), (size_t)(n))
#define STRLCAT(d, s, n) xstrlcat((char *)(d), (char *)(s), (size_t)(n))
-#define vim_strpbrk(s, cs) (char_u *)strpbrk((char *)(s), (char *)(cs))
-
// Character used as separated in autoload function/variable names.
#define AUTOLOAD_CHAR '#'
@@ -264,7 +251,7 @@ enum { FOLD_TEXT_LEN = 51, }; //!< buffer size for get_foldtext()
// Prefer using semsg(), because perror() may send the output to the wrong
// destination and mess up the screen.
-#define PERROR(msg) (void)semsg("%s: %s", msg, strerror(errno))
+#define PERROR(msg) (void)semsg("%s: %s", (msg), strerror(errno))
#define SHOWCMD_COLS 10 // columns needed by shown command
@@ -282,12 +269,11 @@ enum { FOLD_TEXT_LEN = 51, }; //!< buffer size for get_foldtext()
/// @param[in] y Second file name to compare.
///
/// @return 0 for equal file names, non-zero otherwise.
-#define fnamecmp(x, y) path_fnamecmp((const char *)(x), (const char *)(y))
-#define fnamencmp(x, y, n) path_fnamencmp((const char *)(x), \
+#define FNAMECMP(x, y) path_fnamecmp((const char *)(x), (const char *)(y))
+#define FNAMENCMP(x, y, n) path_fnamencmp((const char *)(x), \
(const char *)(y), \
(size_t)(n))
-
// Enums need a typecast to be used as array index (for Ultrix).
#define HL_ATTR(n) highlight_attr[(int)(n)]
@@ -319,7 +305,7 @@ enum { FOLD_TEXT_LEN = 51, }; //!< buffer size for get_foldtext()
#endif
// Replacement for nchar used by nv_replace().
-#define REPLACE_CR_NCHAR -1
-#define REPLACE_NL_NCHAR -2
+#define REPLACE_CR_NCHAR (-1)
+#define REPLACE_NL_NCHAR (-2)
#endif // NVIM_VIM_H
diff --git a/src/nvim/viml/parser/expressions.c b/src/nvim/viml/parser/expressions.c
index 8a14710351..fd7dc17ee3 100644
--- a/src/nvim/viml/parser/expressions.c
+++ b/src/nvim/viml/parser/expressions.c
@@ -66,7 +66,7 @@
#include "nvim/viml/parser/expressions.h"
#include "nvim/viml/parser/parser.h"
-#define vim_str2nr(s, ...) vim_str2nr((const char_u *)(s), __VA_ARGS__)
+#define VIM_STR2NR(s, ...) vim_str2nr((const char_u *)(s), __VA_ARGS__)
typedef kvec_withinit_t(ExprASTNode **, 16) ExprASTStack;
@@ -371,7 +371,7 @@ LexExprToken viml_pexpr_next_token(ParserState *const pstate, const int flags)
significand_part = significand_part * 10 + (pline.data[i] - '0');
}
if (exp_start) {
- vim_str2nr(pline.data + exp_start, NULL, NULL, 0, NULL, &exp_part,
+ VIM_STR2NR(pline.data + exp_start, NULL, NULL, 0, NULL, &exp_part,
(int)(ret.len - exp_start), false);
}
if (exp_negative) {
@@ -389,7 +389,7 @@ LexExprToken viml_pexpr_next_token(ParserState *const pstate, const int flags)
} else {
int len;
int prep;
- vim_str2nr(pline.data, &prep, &len, STR2NR_ALL, NULL,
+ VIM_STR2NR(pline.data, &prep, &len, STR2NR_ALL, NULL,
&ret.data.num.val.integer, (int)pline.size, false);
ret.len = (size_t)len;
const uint8_t bases[] = {
@@ -541,8 +541,7 @@ LexExprToken viml_pexpr_next_token(ParserState *const pstate, const int flags)
ret.data.opt.len = 4;
ret.len += 4;
} else {
- for (; p < e && ASCII_ISALPHA(*p); p++) {
- }
+ for (; p < e && ASCII_ISALPHA(*p); p++) {}
ret.data.opt.len = (size_t)(p - ret.data.opt.name);
if (ret.data.opt.len == 0) {
OPTNAMEMISS(ret);
@@ -1592,7 +1591,7 @@ typedef struct {
/// string is a regex.
/// @param[in] is_invalid Whether currently processed token is not valid.
static void parse_quoted_string(ParserState *const pstate, ExprASTNode *const node,
- const LexExprToken token, const ExprASTStack ast_stack,
+ const LexExprToken token, const ExprASTStack *ast_stack,
const bool is_invalid)
FUNC_ATTR_NONNULL_ALL
{
@@ -1652,10 +1651,11 @@ static void parse_quoted_string(ParserState *const pstate, ExprASTNode *const no
}
switch (*p) {
// A "\<x>" form occupies at least 4 characters, and produces up to
- // 6 characters: reserve space for 2 extra, but do not compute actual
- // length just now, it would be costy.
+ // to 9 characters (6 for the char and 3 for a modifier):
+ // reserve space for 5 extra, but do not compute actual length
+ // just now, it would be costly.
case '<':
- size += 2;
+ size += 5;
break;
// Hexadecimal, always single byte, but at least three bytes each.
case 'x':
@@ -1788,7 +1788,7 @@ static void parse_quoted_string(ParserState *const pstate, ExprASTNode *const no
if (is_hex) {
*v_p++ = (char)nr;
} else {
- v_p += utf_char2bytes(nr, (char_u *)v_p);
+ v_p += utf_char2bytes(nr, v_p);
}
} else {
is_unknown = true;
@@ -1817,9 +1817,13 @@ static void parse_quoted_string(ParserState *const pstate, ExprASTNode *const no
}
// Special key, e.g.: "\<C-W>"
case '<': {
- const size_t special_len = (
- trans_special((const char_u **)&p, (size_t)(e - p),
- (char_u *)v_p, true, true));
+ int flags = FSK_KEYCODE | FSK_IN_STRING;
+
+ if (p[1] != '*') {
+ flags |= FSK_SIMPLIFY;
+ }
+ const size_t special_len = trans_special((const char_u **)&p, (size_t)(e - p),
+ (char_u *)v_p, flags, false, NULL);
if (special_len != 0) {
v_p += special_len;
} else {
@@ -2642,6 +2646,7 @@ viml_pexpr_parse_figure_brace_closing_error:
kvi_push(pt_stack, kEPTLambdaArguments);
lambda_node = cur_node;
} else {
+ // uncrustify:off
ADD_IDENT(do {
NEW_NODE_WITH_CUR_POS(cur_node,
kExprNodeCurlyBracesIdentifier);
@@ -2656,6 +2661,7 @@ viml_pexpr_parse_figure_brace_closing_error:
want_node = kENodeValue;
} while (0),
Curly);
+ // uncrustify:on
}
if (pt_is_assignment(cur_pt)
&& !pt_is_assignment(kv_last(pt_stack))) {
@@ -2733,6 +2739,7 @@ viml_pexpr_parse_figure_brace_closing_error:
: HL(IdentifierName)));
} else {
if (scope == kExprVarScopeMissing) {
+ // uncrustify:off
ADD_IDENT(do {
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodePlainIdentifier);
cur_node->data.var.scope = scope;
@@ -2741,6 +2748,7 @@ viml_pexpr_parse_figure_brace_closing_error:
want_node = kENodeOperator;
} while (0),
IdentifierName);
+ // uncrustify:on
} else {
OP_MISSING;
}
@@ -2907,7 +2915,7 @@ viml_pexpr_parse_no_paren_closing_error: {}
? kExprNodeDoubleQuotedString
: kExprNodeSingleQuotedString));
*top_node_p = cur_node;
- parse_quoted_string(pstate, cur_node, cur_token, ast_stack, is_invalid);
+ parse_quoted_string(pstate, cur_node, cur_token, &ast_stack, is_invalid);
want_node = kENodeOperator;
break;
}
@@ -3063,7 +3071,7 @@ viml_pexpr_parse_end:
}
kvi_destroy(ast_stack);
return ast;
-} // NOLINT(readability/fn_size)
+}
#undef NEW_NODE
#undef HL
diff --git a/src/nvim/viml/parser/parser.c b/src/nvim/viml/parser/parser.c
index 8d26d08ea7..a41b750e76 100644
--- a/src/nvim/viml/parser/parser.c
+++ b/src/nvim/viml/parser/parser.c
@@ -7,7 +7,6 @@
# include "viml/parser/parser.c.generated.h"
#endif
-
void parser_simple_get_line(void *cookie, ParserLine *ret_pline)
{
ParserLine **plines_p = (ParserLine **)cookie;
diff --git a/src/nvim/viml/parser/parser.h b/src/nvim/viml/parser/parser.h
index b2933c3781..b8835127e7 100644
--- a/src/nvim/viml/parser/parser.h
+++ b/src/nvim/viml/parser/parser.h
@@ -81,10 +81,8 @@ typedef struct {
bool can_continuate;
} ParserState;
-static inline void viml_parser_init(
- ParserState *const ret_pstate,
- const ParserLineGetter get_line, void *const cookie,
- ParserHighlight *const colors)
+static inline void viml_parser_init(ParserState *const ret_pstate, const ParserLineGetter get_line,
+ void *const cookie, ParserHighlight *const colors)
REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ARG(1, 2);
/// Initialize a new parser state instance
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 343d279b1d..2bffe2055f 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -26,7 +26,9 @@
#include "nvim/globals.h"
#include "nvim/hashtab.h"
#include "nvim/main.h"
+#include "nvim/mapping.h"
#include "nvim/mark.h"
+#include "nvim/match.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
@@ -52,15 +54,13 @@
#include "nvim/vim.h"
#include "nvim/window.h"
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "window.c.generated.h"
#endif
+#define NOWIN ((win_T *)-1) // non-existing window
-#define NOWIN (win_T *)-1 // non-existing window
-
-#define ROWS_AVAIL (Rows - p_ch - tabline_height())
+#define ROWS_AVAIL (Rows - p_ch - tabline_height() - global_stl_height())
/// flags for win_enter_ext()
typedef enum {
@@ -71,8 +71,49 @@ typedef enum {
WEE_TRIGGER_LEAVE_AUTOCMDS = 0x10,
} wee_flags_T;
+static char e_cannot_split_window_when_closing_buffer[]
+ = N_("E1159: Cannot split a window when closing the buffer");
+
static char *m_onlyone = N_("Already only one window");
+/// When non-zero splitting a window is forbidden. Used to avoid that nasty
+/// autocommands mess up the window structure.
+static int split_disallowed = 0;
+
+// #define WIN_DEBUG
+#ifdef WIN_DEBUG
+/// Call this method to log the current window layout.
+static void log_frame_layout(frame_T *frame)
+{
+ DLOG("layout %s, wi: %d, he: %d, wwi: %d, whe: %d, id: %d",
+ frame->fr_layout == FR_LEAF ? "LEAF" : frame->fr_layout == FR_ROW ? "ROW" : "COL",
+ frame->fr_width,
+ frame->fr_height,
+ frame->fr_win == NULL ? -1 : frame->fr_win->w_width,
+ frame->fr_win == NULL ? -1 : frame->fr_win->w_height,
+ frame->fr_win == NULL ? -1 : frame->fr_win->w_id);
+ if (frame->fr_child != NULL) {
+ DLOG("children");
+ log_frame_layout(frame->fr_child);
+ if (frame->fr_next != NULL) {
+ DLOG("END of children");
+ }
+ }
+ if (frame->fr_next != NULL) {
+ log_frame_layout(frame->fr_next);
+ }
+}
+#endif
+
+/// @return the current window, unless in the cmdline window and "prevwin" is
+/// set, then return "prevwin".
+win_T *prevwin_curwin(void)
+ FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ // In cmdwin, the alternative buffer should be used.
+ return is_in_cmdwin() && prevwin != NULL ? prevwin : curwin;
+}
+
/// all CTRL-W window commands are handled here, called from normal_cmd().
///
/// @param xchar extra char from ":wincmd gx" or NUL
@@ -290,7 +331,8 @@ newwindow:
// move window to new tab page
case 'T':
- if (one_window()) {
+ CHECK_CMDWIN;
+ if (one_window(curwin)) {
msg(_(m_onlyone));
} else {
tabpage_T *oldtab = curtab;
@@ -304,7 +346,7 @@ newwindow:
newtab = curtab;
goto_tabpage_tp(oldtab, true, true);
if (curwin == wp) {
- win_close(curwin, false);
+ win_close(curwin, false, false);
}
if (valid_tabpage(newtab)) {
goto_tabpage_tp(newtab, true, true);
@@ -387,7 +429,7 @@ newwindow:
// set current window height
case Ctrl__:
case '_':
- win_setheight(Prenum ? (int)Prenum : Rows-1);
+ win_setheight(Prenum ? (int)Prenum : Rows - 1);
break;
// increase current window width
@@ -447,9 +489,9 @@ wingotofile:
setpcmark();
if (win_split(0, 0) == OK) {
RESET_BINDING(curwin);
- if (do_ecmd(0, ptr, NULL, NULL, ECMD_LASTL, ECMD_HIDE, NULL) == FAIL) {
+ if (do_ecmd(0, (char *)ptr, NULL, NULL, ECMD_LASTL, ECMD_HIDE, NULL) == FAIL) {
// Failed to open the file, close the window opened for it.
- win_close(curwin, false);
+ win_close(curwin, false, false);
goto_tabpage_win(oldtab, oldwin);
} else if (nchar == 'F' && lnum >= 0) {
curwin->w_cursor.lnum = lnum;
@@ -473,9 +515,14 @@ wingotofile:
if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0) {
break;
}
+
+ // Make a copy, if the line was changed it will be freed.
+ ptr = vim_strnsave(ptr, len);
+
find_pattern_in_path(ptr, 0, len, true, Prenum == 0,
type, Prenum1, ACTION_SPLIT, 1, MAXLNUM);
- curwin->w_set_curswant = TRUE;
+ xfree(ptr);
+ curwin->w_set_curswant = true;
break;
// Quickfix window only: view the result under the cursor in a new split.
@@ -486,17 +533,18 @@ wingotofile:
}
break;
-
// CTRL-W g extended commands
case 'g':
case Ctrl_G:
CHECK_CMDWIN;
no_mapping++;
+ allow_keys++; // no mapping for xchar, but allow key codes
if (xchar == NUL) {
xchar = plain_vgetc();
}
LANGMAP_ADJUST(xchar, true);
no_mapping--;
+ allow_keys--;
(void)add_to_showcmd(xchar);
switch (xchar) {
case '}':
@@ -521,13 +569,9 @@ wingotofile:
do_nv_ident('g', xchar);
break;
- case TAB:
- goto_tabpage_lastused();
- break;
-
case 'f': // CTRL-W gf: "gf" in a new tab page
case 'F': // CTRL-W gF: "gF" in a new tab page
- cmdmod.tab = tabpage_index(curtab) + 1;
+ cmdmod.cmod_tab = tabpage_index(curtab) + 1;
nchar = xchar;
goto wingotofile;
case 't': // CTRL-W gt: go to next tab page
@@ -538,6 +582,12 @@ wingotofile:
goto_tabpage(-(int)Prenum1);
break;
+ case TAB: // CTRL-W g<Tab>: go to last used tab page
+ if (!goto_tabpage_lastused()) {
+ beep_flush();
+ }
+ break;
+
case 'e':
if (curwin->w_floating || !ui_has(kUIMultigrid)) {
beep_flush();
@@ -548,7 +598,7 @@ wingotofile:
config.height = curwin->w_height;
config.external = true;
Error err = ERROR_INIT;
- if (!win_new_float(curwin, config, &err)) {
+ if (!win_new_float(curwin, false, config, &err)) {
emsg(err.msg);
api_clear_error(&err);
beep_flush();
@@ -577,18 +627,19 @@ static void cmd_with_count(char *cmd, char_u *bufp, size_t bufsize, int64_t Pren
void win_set_buf(Window window, Buffer buffer, bool noautocmd, Error *err)
{
- win_T *win = find_window_by_handle(window, err), *save_curwin = curwin;
+ win_T *win = find_window_by_handle(window, err);
buf_T *buf = find_buffer_by_handle(buffer, err);
- tabpage_T *tab = win_find_tabpage(win), *save_curtab = curtab;
+ tabpage_T *tab = win_find_tabpage(win);
if (!win || !buf) {
return;
}
-
if (noautocmd) {
block_autocmds();
}
- if (switch_win_noblock(&save_curwin, &save_curtab, win, tab, false) == FAIL) {
+
+ switchwin_T switchwin;
+ if (switch_win_noblock(&switchwin, win, tab, false) == FAIL) {
api_set_error(err,
kErrorTypeException,
"Failed to switch to window %d",
@@ -608,7 +659,7 @@ void win_set_buf(Window window, Buffer buffer, bool noautocmd, Error *err)
// So do it now.
validate_cursor();
- restore_win_noblock(save_curwin, save_curtab, false);
+ restore_win_noblock(&switchwin, false);
if (noautocmd) {
unblock_autocmds();
}
@@ -616,16 +667,18 @@ void win_set_buf(Window window, Buffer buffer, bool noautocmd, Error *err)
/// Create a new float.
///
-/// if wp == NULL allocate a new window, otherwise turn existing window into a
-/// float. It must then already belong to the current tabpage!
-///
-/// config must already have been validated!
-win_T *win_new_float(win_T *wp, FloatConfig fconfig, Error *err)
+/// @param wp if NULL, allocate a new window, otherwise turn existing window into a float.
+/// It must then already belong to the current tabpage!
+/// @param last make the window the last one in the window list.
+/// Only used when allocating the autocommand window.
+/// @param config must already have been validated!
+win_T *win_new_float(win_T *wp, bool last, FloatConfig fconfig, Error *err)
{
if (wp == NULL) {
- wp = win_alloc(lastwin_nofloating(), false);
+ wp = win_alloc(last ? lastwin : lastwin_nofloating(), false);
win_init(wp, curwin, 0);
} else {
+ assert(!last);
assert(!wp->w_floating);
if (firstwin == wp && lastwin_nofloating() == wp) {
// last non-float
@@ -646,6 +699,8 @@ win_T *win_new_float(win_T *wp, FloatConfig fconfig, Error *err)
}
wp->w_floating = 1;
wp->w_status_height = 0;
+ wp->w_winbar_height = 0;
+ wp->w_hsep_height = 0;
wp->w_vsep_width = 0;
win_config_float(wp, fconfig);
@@ -717,7 +772,6 @@ void win_config_float(win_T *wp, FloatConfig fconfig)
wp->w_float_config.border_hl_ids,
sizeof fconfig.border_hl_ids));
-
wp->w_float_config = fconfig;
bool has_border = wp->w_floating && wp->w_float_config.border;
@@ -730,10 +784,8 @@ void win_config_float(win_T *wp, FloatConfig fconfig)
}
if (!ui_has(kUIMultigrid)) {
- wp->w_height = MIN(wp->w_height,
- Rows - 1 - (wp->w_border_adj[0] + wp->w_border_adj[2]));
- wp->w_width = MIN(wp->w_width,
- Columns - (wp->w_border_adj[1] + wp->w_border_adj[3]));
+ wp->w_height = MIN(wp->w_height, Rows - 1 - win_border_height(wp));
+ wp->w_width = MIN(wp->w_width, Columns - win_border_width(wp));
}
win_set_inner_size(wp);
@@ -756,7 +808,7 @@ void win_config_float(win_T *wp, FloatConfig fconfig)
col += parent->w_wincol;
ScreenGrid *grid = &parent->w_grid;
int row_off = 0, col_off = 0;
- screen_adjust_grid(&grid, &row_off, &col_off);
+ grid_adjust(&grid, &row_off, &col_off);
row += row_off;
col += col_off;
}
@@ -820,22 +872,22 @@ void ui_ext_win_position(win_T *wp)
FloatConfig c = wp->w_float_config;
if (!c.external) {
ScreenGrid *grid = &default_grid;
- float row = c.row, col = c.col;
+ Float row = c.row, col = c.col;
if (c.relative == kFloatRelativeWindow) {
Error dummy = ERROR_INIT;
win_T *win = find_window_by_handle(c.window, &dummy);
if (win) {
grid = &win->w_grid;
int row_off = 0, col_off = 0;
- screen_adjust_grid(&grid, &row_off, &col_off);
+ grid_adjust(&grid, &row_off, &col_off);
row += row_off;
col += col_off;
if (c.bufpos.lnum >= 0) {
- pos_T pos = { c.bufpos.lnum+1, c.bufpos.col, 0 };
+ pos_T pos = { c.bufpos.lnum + 1, c.bufpos.col, 0 };
int trow, tcol, tcolc, tcole;
textpos2screenpos(win, &pos, &trow, &tcol, &tcolc, &tcole, true);
- row += trow-1;
- col += tcol-1;
+ row += trow - 1;
+ col += tcol - 1;
}
}
api_clear_error(&dummy);
@@ -843,7 +895,7 @@ void ui_ext_win_position(win_T *wp)
wp->w_grid_alloc.zindex = wp->w_float_config.zindex;
if (ui_has(kUIMultigrid)) {
- String anchor = cstr_to_string(float_anchor_str[c.anchor]);
+ String anchor = cstr_as_string((char *)float_anchor_str[c.anchor]);
ui_call_win_float_pos(wp->w_grid_alloc.handle, wp->handle, anchor,
grid->handle, row, col, c.focusable,
wp->w_grid_alloc.zindex);
@@ -857,7 +909,7 @@ void ui_ext_win_position(win_T *wp)
int comp_col = (int)col - (east ? wp->w_width_outer : 0);
comp_row += grid->comp_row;
comp_col += grid->comp_col;
- comp_row = MAX(MIN(comp_row, Rows - wp->w_height_outer - 1), 0);
+ comp_row = MAX(MIN(comp_row, Rows - wp->w_height_outer - (p_ch > 0 ? 1 : 0)), 0);
comp_col = MAX(MIN(comp_col, Columns - wp->w_width_outer), 0);
wp->w_winrow = comp_row;
wp->w_wincol = comp_col;
@@ -881,18 +933,33 @@ void ui_ext_win_viewport(win_T *wp)
if ((wp == curwin || ui_has(kUIMultigrid)) && wp->w_viewport_invalid) {
int botline = wp->w_botline;
int line_count = wp->w_buffer->b_ml.ml_line_count;
- if (botline == line_count+1 && wp->w_empty_rows == 0) {
+ if (botline == line_count + 1 && wp->w_empty_rows == 0) {
// TODO(bfredl): The might be more cases to consider, like how does this
// interact with incomplete final line? Diff filler lines?
botline = wp->w_buffer->b_ml.ml_line_count;
}
- ui_call_win_viewport(wp->w_grid_alloc.handle, wp->handle, wp->w_topline-1,
- botline, wp->w_cursor.lnum-1, wp->w_cursor.col,
+ ui_call_win_viewport(wp->w_grid_alloc.handle, wp->handle, wp->w_topline - 1,
+ botline, wp->w_cursor.lnum - 1, wp->w_cursor.col,
line_count);
wp->w_viewport_invalid = false;
}
}
+/// If "split_disallowed" is set given an error and return FAIL.
+/// Otherwise return OK.
+static int check_split_disallowed(void)
+{
+ if (split_disallowed > 0) {
+ emsg(_("E242: Can't split a window while closing another"));
+ return FAIL;
+ }
+ if (curwin->w_buffer->b_locked_split) {
+ emsg(_(e_cannot_split_window_when_closing_buffer));
+ return FAIL;
+ }
+ return OK;
+}
+
/*
* split the current window, implements CTRL-W s and :split
*
@@ -902,21 +969,25 @@ void ui_ext_win_viewport(win_T *wp)
* "flags":
* WSP_ROOM: require enough room for new window
* WSP_VERT: vertical split.
- * WSP_TOP: open window at the top-left of the shell (help window).
- * WSP_BOT: open window at the bottom-right of the shell (quickfix window).
+ * WSP_TOP: open window at the top-left of the screen (help window).
+ * WSP_BOT: open window at the bottom-right of the screen (quickfix window).
* WSP_HELP: creating the help window, keep layout snapshot
*
* return FAIL for failure, OK otherwise
*/
int win_split(int size, int flags)
{
+ if (check_split_disallowed() == FAIL) {
+ return FAIL;
+ }
+
// When the ":tab" modifier was used open a new tab page instead.
if (may_open_tabpage() == OK) {
return OK;
}
// Add flags from ":vertical", ":topleft" and ":botright".
- flags |= cmdmod.split;
+ flags |= cmdmod.cmod_split;
if ((flags & WSP_TOP) && (flags & WSP_BOT)) {
emsg(_("E442: Can't split topleft and botright at the same time"));
return FAIL;
@@ -957,6 +1028,11 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
int wmh1;
bool did_set_fraction = false;
+ // aucmd_win should always remain floating
+ if (new_wp != NULL && new_wp == aucmd_win) {
+ return FAIL;
+ }
+
if (flags & WSP_TOP) {
oldwin = firstwin;
} else if (flags & WSP_BOT || curwin->w_floating) {
@@ -977,7 +1053,6 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
need_status = STATUS_HEIGHT;
}
-
if (flags & WSP_VERT) {
int wmw1;
int minwidth;
@@ -1064,15 +1139,15 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
} else {
layout = FR_COL;
- /*
- * Check if we are able to split the current window and compute its
- * height.
- */
- // Current window requires at least 1 space.
- wmh1 = p_wmh == 0 ? 1 : p_wmh;
+ // Check if we are able to split the current window and compute its height.
+ // Current window requires at least 1 space plus space for the window bar.
+ wmh1 = MAX(p_wmh, 1) + oldwin->w_winbar_height;
needed = wmh1 + STATUS_HEIGHT;
if (flags & WSP_ROOM) {
- needed += p_wh - wmh1;
+ needed += p_wh - wmh1 + oldwin->w_winbar_height;
+ }
+ if (p_ch < 1) {
+ needed += 1; // Adjust for cmdheight=0.
}
if (flags & (WSP_BOT | WSP_TOP)) {
minheight = frame_minheight(topframe, NOWIN) + need_status;
@@ -1081,8 +1156,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
} else if (p_ea) {
minheight = frame_minheight(oldwin->w_frame, NOWIN) + need_status;
prevfrp = oldwin->w_frame;
- for (frp = oldwin->w_frame->fr_parent; frp != NULL;
- frp = frp->fr_parent) {
+ for (frp = oldwin->w_frame->fr_parent; frp != NULL; frp = frp->fr_parent) {
if (frp->fr_layout == FR_COL) {
FOR_ALL_FRAMES(frp2, frp->fr_child) {
if (frp2 != prevfrp) {
@@ -1150,8 +1224,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
while (frp != NULL) {
if (frp->fr_win != oldwin && frp->fr_win != NULL
&& (frp->fr_win->w_height > new_size
- || frp->fr_win->w_height > oldwin_height - new_size
- - STATUS_HEIGHT)) {
+ || frp->fr_win->w_height > oldwin_height - new_size - STATUS_HEIGHT)) {
do_equal = true;
break;
}
@@ -1194,8 +1267,9 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
} else if (wp->w_floating) {
new_frame(wp);
wp->w_floating = false;
- // non-floating window doesn't store float config.
+ // non-floating window doesn't store float config or have a border.
wp->w_float_config = FLOAT_CONFIG_INIT;
+ memset(wp->w_border_adj, 0, sizeof(wp->w_border_adj));
}
/*
@@ -1277,13 +1351,15 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
if (flags & (WSP_TOP | WSP_BOT)) {
// set height and row of new window to full height
wp->w_winrow = tabline_height();
- win_new_height(wp, curfrp->fr_height - (p_ls > 0));
- wp->w_status_height = (p_ls > 0);
+ win_new_height(wp, curfrp->fr_height - (p_ls == 1 || p_ls == 2));
+ wp->w_status_height = (p_ls == 1 || p_ls == 2);
+ wp->w_hsep_height = 0;
} else {
// height and row of new window is same as current window
wp->w_winrow = oldwin->w_winrow;
win_new_height(wp, oldwin->w_height);
wp->w_status_height = oldwin->w_status_height;
+ wp->w_hsep_height = oldwin->w_hsep_height;
}
frp->fr_height = curfrp->fr_height;
@@ -1316,6 +1392,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
frame_fix_width(oldwin);
frame_fix_width(wp);
} else {
+ bool is_stl_global = global_stl_height() > 0;
// width and column of new window is same as current window
if (flags & (WSP_TOP | WSP_BOT)) {
wp->w_wincol = 0;
@@ -1331,28 +1408,52 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
// "new_size" of the current window goes to the new window, use
// one row for the status line
win_new_height(wp, new_size);
+ if (before) {
+ wp->w_hsep_height = is_stl_global ? 1 : 0;
+ } else {
+ wp->w_hsep_height = oldwin->w_hsep_height;
+ oldwin->w_hsep_height = is_stl_global ? 1 : 0;
+ }
if (flags & (WSP_TOP | WSP_BOT)) {
int new_fr_height = curfrp->fr_height - new_size;
- if (!((flags & WSP_BOT) && p_ls == 0)) {
+ if (!((flags & WSP_BOT) && p_ls == 0) && !is_stl_global) {
new_fr_height -= STATUS_HEIGHT;
+ } else if (is_stl_global) {
+ if (flags & WSP_BOT) {
+ frame_add_hsep(curfrp);
+ } else {
+ new_fr_height -= 1;
+ }
}
frame_new_height(curfrp, new_fr_height, flags & WSP_TOP, false);
} else {
win_new_height(oldwin, oldwin_height - (new_size + STATUS_HEIGHT));
}
+
if (before) { // new window above current one
wp->w_winrow = oldwin->w_winrow;
- wp->w_status_height = STATUS_HEIGHT;
- oldwin->w_winrow += wp->w_height + STATUS_HEIGHT;
+
+ if (is_stl_global) {
+ wp->w_status_height = 0;
+ oldwin->w_winrow += wp->w_height + 1;
+ } else {
+ wp->w_status_height = STATUS_HEIGHT;
+ oldwin->w_winrow += wp->w_height + STATUS_HEIGHT;
+ }
} else { // new window below current one
- wp->w_winrow = oldwin->w_winrow + oldwin->w_height + STATUS_HEIGHT;
- wp->w_status_height = oldwin->w_status_height;
- if (!(flags & WSP_BOT)) {
- oldwin->w_status_height = STATUS_HEIGHT;
+ if (is_stl_global) {
+ wp->w_winrow = oldwin->w_winrow + oldwin->w_height + 1;
+ wp->w_status_height = 0;
+ } else {
+ wp->w_winrow = oldwin->w_winrow + oldwin->w_height + STATUS_HEIGHT;
+ wp->w_status_height = oldwin->w_status_height;
+ if (!(flags & WSP_BOT)) {
+ oldwin->w_status_height = STATUS_HEIGHT;
+ }
}
}
- if (flags & WSP_BOT) {
+ if ((flags & WSP_BOT) && !is_stl_global) {
frame_add_statusline(curfrp);
}
frame_fix_height(wp);
@@ -1382,10 +1483,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
* equalize the window sizes.
*/
if (do_equal || dir != 0) {
- win_equal(wp, true,
- (flags & WSP_VERT) ? (dir == 'v' ? 'b' : 'h')
- : dir == 'h' ? 'b' :
- 'v');
+ win_equal(wp, true, (flags & WSP_VERT) ? (dir == 'v' ? 'b' : 'h') : (dir == 'h' ? 'b' : 'v'));
}
// Don't change the window height/width to 'winheight' / 'winwidth' if a
@@ -1414,17 +1512,14 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
p_wh = i;
}
- if (!win_valid(oldwin)) {
- return FAIL;
+ if (win_valid(oldwin)) {
+ // Send the window positions to the UI
+ oldwin->w_pos_changed = true;
}
- // Send the window positions to the UI
- oldwin->w_pos_changed = true;
-
return OK;
}
-
/*
* Initialize window "newp" from window "oldp".
* Used when splitting a window and when creating a new tab page.
@@ -1461,9 +1556,9 @@ static void win_init(win_T *newp, win_T *oldp, int flags)
copy_loclist_stack(oldp, newp);
}
newp->w_localdir = (oldp->w_localdir == NULL)
- ? NULL : vim_strsave(oldp->w_localdir);
+ ? NULL : xstrdup(oldp->w_localdir);
newp->w_prevdir = (oldp->w_prevdir == NULL)
- ? NULL : vim_strsave(oldp->w_prevdir);
+ ? NULL : xstrdup(oldp->w_prevdir);
// copy tagstack and folds
for (i = 0; i < oldp->w_tagstacklen; i++) {
@@ -1482,7 +1577,7 @@ static void win_init(win_T *newp, win_T *oldp, int flags)
win_init_some(newp, oldp);
- didset_window_options(newp);
+ newp->w_winbar_height = oldp->w_winbar_height;
}
/*
@@ -1579,10 +1674,10 @@ int win_count(void)
}
/// Make "count" windows on the screen.
-/// Must be called when there is just one window, filling the whole screen
+/// Must be called when there is just one window, filling the whole screen.
/// (excluding the command line).
///
-/// @param vertical split windows vertically if true
+/// @param vertical split windows vertically if true.
///
/// @return actual number of windows on the screen.
int make_windows(int count, bool vertical)
@@ -1591,15 +1686,15 @@ int make_windows(int count, bool vertical)
int todo;
if (vertical) {
- // Each window needs at least 'winminwidth' lines and a separator
- // column.
+ // Each window needs at least 'winminwidth' lines and a separator column.
maxcount = (curwin->w_width + curwin->w_vsep_width
- (p_wiw - p_wmw)) / (p_wmw + 1);
} else {
- // Each window needs at least 'winminheight' lines and a status line.
- maxcount = (curwin->w_height
- + curwin->w_status_height
- - (p_wh - p_wmh)) / (p_wmh + STATUS_HEIGHT);
+ // Each window needs at least 'winminheight' lines.
+ // If statusline isn't global, each window also needs a statusline.
+ // If 'winbar' is set, each window also needs a winbar.
+ maxcount = (curwin->w_height + curwin->w_hsep_height + curwin->w_status_height
+ - (p_wh - p_wmh)) / (p_wmh + STATUS_HEIGHT + global_winbar_height());
}
if (maxcount < 2) {
@@ -1609,17 +1704,13 @@ int make_windows(int count, bool vertical)
count = maxcount;
}
- /*
- * add status line now, otherwise first window will be too big
- */
+ // add status line now, otherwise first window will be too big
if (count > 1) {
last_status(true);
}
- /*
- * Don't execute autocommands while creating the windows. Must do that
- * when putting the buffers in the windows.
- */
+ // Don't execute autocommands while creating the windows. Must do that
+ // when putting the buffers in the windows.
block_autocmds();
// todo is number of windows left to create
@@ -1666,7 +1757,6 @@ static void win_exchange(long Prenum)
return;
}
-
/*
* find window to exchange with
*/
@@ -1694,7 +1784,7 @@ static void win_exchange(long Prenum)
* if wp != wp2
* 3. remove wp from the list
* 4. insert wp after wp2
- * 5. exchange the status line height and vsep width.
+ * 5. exchange the status line height, winbar height, hsep height and vsep width.
*/
wp2 = curwin->w_prev;
frp2 = curwin->w_frame->fr_prev;
@@ -1720,6 +1810,9 @@ static void win_exchange(long Prenum)
temp = curwin->w_vsep_width;
curwin->w_vsep_width = wp->w_vsep_width;
wp->w_vsep_width = temp;
+ temp = curwin->w_hsep_height;
+ curwin->w_hsep_height = wp->w_hsep_height;
+ wp->w_hsep_height = temp;
frame_fix_height(curwin);
frame_fix_height(wp);
@@ -1728,6 +1821,12 @@ static void win_exchange(long Prenum)
(void)win_comp_pos(); // recompute window positions
+ if (wp->w_buffer != curbuf) {
+ reset_VIsual_and_resel();
+ } else if (VIsual_active) {
+ wp->w_cursor = curwin->w_cursor;
+ }
+
win_enter(wp, true);
redraw_later(curwin, NOT_VALID);
redraw_later(wp, NOT_VALID);
@@ -1772,8 +1871,7 @@ static void win_rotate(bool upwards, int count)
assert(frp->fr_parent->fr_child);
// find last frame and append removed window/frame after it
- for (; frp->fr_next != NULL; frp = frp->fr_next) {
- }
+ for (; frp->fr_next != NULL; frp = frp->fr_next) {}
win_append(frp->fr_win, wp1);
frame_append(frp, wp1->w_frame);
@@ -1781,8 +1879,7 @@ static void win_rotate(bool upwards, int count)
} else { // last window becomes first window
// find last window/frame in the list and remove it
for (frp = curwin->w_frame; frp->fr_next != NULL;
- frp = frp->fr_next) {
- }
+ frp = frp->fr_next) {}
wp1 = frp->fr_win;
wp2 = wp1->w_prev; // will become last window
win_remove(wp1, NULL);
@@ -1794,10 +1891,13 @@ static void win_rotate(bool upwards, int count)
frame_insert(frp->fr_parent->fr_child, frp);
}
- // exchange status height and vsep width of old and new last window
+ // exchange status height, winbar height, hsep height and vsep width of old and new last window
n = wp2->w_status_height;
wp2->w_status_height = wp1->w_status_height;
wp1->w_status_height = n;
+ n = wp2->w_hsep_height;
+ wp2->w_hsep_height = wp1->w_hsep_height;
+ wp1->w_hsep_height = n;
frame_fix_height(wp1);
frame_fix_height(wp2);
n = wp2->w_vsep_width;
@@ -1828,6 +1928,12 @@ static void win_totop(int size, int flags)
beep_flush();
return;
}
+ if (curwin == aucmd_win) {
+ return;
+ }
+ if (check_split_disallowed() == FAIL) {
+ return;
+ }
if (curwin->w_floating) {
ui_comp_remove_grid(&curwin->w_grid_alloc);
@@ -1836,7 +1942,7 @@ static void win_totop(int size, int flags)
} else {
// No longer a float, a non-multigrid UI shouldn't draw it as such
ui_call_win_hide(curwin->w_grid_alloc.handle);
- win_free_grid(curwin, false);
+ win_free_grid(curwin, true);
}
} else {
// Remove the window and frame from the tree of frames.
@@ -1871,11 +1977,22 @@ void win_move_after(win_T *win1, win_T *win2)
// check if there is something to do
if (win2->w_next != win1) {
- // may need move the status line/vertical separator of the last window
+ if (win1->w_frame->fr_parent != win2->w_frame->fr_parent) {
+ iemsg("INTERNAL: trying to move a window into another frame");
+ return;
+ }
+
+ // may need to move the status line, window bar, horizontal or vertical separator of the last
+ // window
if (win1 == lastwin) {
height = win1->w_prev->w_status_height;
win1->w_prev->w_status_height = win1->w_status_height;
win1->w_status_height = height;
+
+ height = win1->w_prev->w_hsep_height;
+ win1->w_prev->w_hsep_height = win1->w_hsep_height;
+ win1->w_hsep_height = height;
+
if (win1->w_prev->w_vsep_width == 1) {
// Remove the vertical separator from the last-but-one window,
// add it to the last window. Adjust the frame widths.
@@ -1888,6 +2005,11 @@ void win_move_after(win_T *win1, win_T *win2)
height = win1->w_status_height;
win1->w_status_height = win2->w_status_height;
win2->w_status_height = height;
+
+ height = win1->w_hsep_height;
+ win1->w_hsep_height = win2->w_hsep_height;
+ win2->w_hsep_height = height;
+
if (win1->w_vsep_width == 1) {
// Remove the vertical separator from win1, add it to the last
// window, win2. Adjust the frame widths.
@@ -1911,6 +2033,37 @@ void win_move_after(win_T *win1, win_T *win2)
win2->w_pos_changed = true;
}
+/// Compute maximum number of windows that can fit within "height" in frame "fr".
+static int get_maximum_wincount(frame_T *fr, int height)
+{
+ if (fr->fr_layout != FR_COL) {
+ return (height / (p_wmh + STATUS_HEIGHT + frame2win(fr)->w_winbar_height));
+ } else if (global_winbar_height()) {
+ // If winbar is globally enabled, no need to check each window for it.
+ return (height / (p_wmh + STATUS_HEIGHT + 1));
+ }
+
+ frame_T *frp;
+ int total_wincount = 0;
+
+ // First, try to fit all child frames of "fr" into "height"
+ FOR_ALL_FRAMES(frp, fr->fr_child) {
+ win_T *wp = frame2win(frp);
+
+ if (height < (p_wmh + STATUS_HEIGHT + wp->w_winbar_height)) {
+ break;
+ }
+ height -= p_wmh + STATUS_HEIGHT + wp->w_winbar_height;
+ total_wincount += 1;
+ }
+
+ // If we still have enough room for more windows, just use the default winbar height (which is 0)
+ // in order to get the amount of windows that'd fit in the remaining space
+ total_wincount += height / (p_wmh + STATUS_HEIGHT);
+
+ return total_wincount;
+}
+
/// Make all windows the same height.
///'next_curwin' will soon be the current window, make sure it has enough rows.
///
@@ -2102,13 +2255,15 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int
if (dir != 'h') { // equalize frame heights
// Compute maximum number of windows vertically in this frame.
n = frame_minheight(topfr, NOWIN);
- // add one for the bottom window if it doesn't have a statusline
+ // add one for the bottom window if it doesn't have a statusline or separator
if (row + height == cmdline_row && p_ls == 0) {
+ extra_sep = STATUS_HEIGHT;
+ } else if (global_stl_height() > 0) {
extra_sep = 1;
} else {
extra_sep = 0;
}
- totwincount = (n + extra_sep) / (p_wmh + 1);
+ totwincount = get_maximum_wincount(topfr, n + extra_sep);
has_next_curwin = frame_has_win(topfr, next_curwin);
/*
@@ -2142,8 +2297,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int
}
} else {
// These windows don't use up room.
- totwincount -= (n + (fr->fr_next == NULL
- ? extra_sep : 0)) / (p_wmh + 1);
+ totwincount -= get_maximum_wincount(fr, (n + (fr->fr_next == NULL ? extra_sep : 0)));
}
room -= new_size - n;
if (room < 0) {
@@ -2188,8 +2342,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int
} else {
// Compute the maximum number of windows vert. in "fr".
n = frame_minheight(fr, NOWIN);
- wincount = (n + (fr->fr_next == NULL ? extra_sep : 0))
- / (p_wmh + 1);
+ wincount = get_maximum_wincount(fr, (n + (fr->fr_next == NULL ? extra_sep : 0)));
m = frame_minheight(fr, next_curwin);
if (has_next_curwin) {
hnc = frame_has_win(fr, next_curwin);
@@ -2247,7 +2400,7 @@ static void leaving_window(win_T *const win)
// When leaving the window (or closing the window) was done from a
// callback we need to break out of the Insert mode loop and restart Insert
// mode when entering the window again.
- if (State & INSERT) {
+ if (State & MODE_INSERT) {
stop_insert_mode = true;
if (win->w_buffer->b_prompt_insert == NUL) {
win->w_buffer->b_prompt_insert = 'A';
@@ -2271,33 +2424,59 @@ void entering_window(win_T *const win)
// When entering the prompt window restart Insert mode if we were in Insert
// mode when we left it and not already in Insert mode.
- if ((State & INSERT) == 0) {
+ if ((State & MODE_INSERT) == 0) {
restart_edit = win->w_buffer->b_prompt_insert;
}
}
-/// Closes all windows for buffer `buf`.
+void win_init_empty(win_T *wp)
+{
+ redraw_later(wp, NOT_VALID);
+ wp->w_lines_valid = 0;
+ wp->w_cursor.lnum = 1;
+ wp->w_curswant = wp->w_cursor.col = 0;
+ wp->w_cursor.coladd = 0;
+ wp->w_pcmark.lnum = 1; // pcmark not cleared but set to line 1
+ wp->w_pcmark.col = 0;
+ wp->w_prev_pcmark.lnum = 0;
+ wp->w_prev_pcmark.col = 0;
+ wp->w_topline = 1;
+ wp->w_topfill = 0;
+ wp->w_botline = 2;
+ wp->w_s = &wp->w_buffer->b_s;
+}
+
+/// Init the current window "curwin".
+/// Called when a new file is being edited.
+void curwin_init(void)
+{
+ win_init_empty(curwin);
+}
+
+/// Closes all windows for buffer `buf` unless there is only one non-floating window.
///
-/// @param keep_curwin don't close `curwin`
-void close_windows(buf_T *buf, int keep_curwin)
+/// @param keep_curwin don't close `curwin`
+void close_windows(buf_T *buf, bool keep_curwin)
{
tabpage_T *tp, *nexttp;
int h = tabline_height();
++RedrawingDisabled;
- for (win_T *wp = firstwin; wp != NULL && !ONE_WINDOW;) {
+ // Start from lastwin to close floating windows with the same buffer first.
+ // When the autocommand window is involved win_close() may need to print an error message.
+ for (win_T *wp = lastwin; wp != NULL && (lastwin == aucmd_win || !one_window(wp));) {
if (wp->w_buffer == buf && (!keep_curwin || wp != curwin)
&& !(wp->w_closing || wp->w_buffer->b_locked > 0)) {
- if (win_close(wp, false) == FAIL) {
+ if (win_close(wp, false, false) == FAIL) {
// If closing the window fails give up, to avoid looping forever.
break;
}
// Start all over, autocommands may change the window layout.
- wp = firstwin;
+ wp = lastwin;
} else {
- wp = wp->w_next;
+ wp = wp->w_prev;
}
}
@@ -2323,27 +2502,28 @@ void close_windows(buf_T *buf, int keep_curwin)
redraw_tabline = true;
if (h != tabline_height()) {
- shell_new_rows();
+ win_new_screen_rows();
}
}
-/// Check that current window is the last one.
+/// Check that the specified window is the last one.
+/// @param win counted even if floating
///
-/// @return true if the current window is the only window that exists, false if
-/// there is another, possibly in another tab page.
-static bool last_window(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
+/// @return true if the specified window is the only window that exists,
+/// false if there is another, possibly in another tab page.
+bool last_window(win_T *win) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- return one_window() && first_tabpage->tp_next == NULL;
+ return one_window(win) && first_tabpage->tp_next == NULL;
}
-/// Check that current tab page contains no more then one window other than
-/// "aucmd_win". Only counts floating window if it is current.
-bool one_window(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
+/// Check that current tab page contains no more then one window other than `aucmd_win`.
+/// @param counted_float counted even if floating, but not if it is `aucmd_win`
+bool one_window(win_T *counted_float) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
bool seen_one = false;
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp != aucmd_win && (!wp->w_floating || wp == curwin)) {
+ if (wp != aucmd_win && (!wp->w_floating || wp == counted_float)) {
if (seen_one) {
return false;
}
@@ -2367,6 +2547,24 @@ bool last_nonfloat(win_T *wp) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
return wp != NULL && firstwin == wp && !(wp->w_next && !wp->w_floating);
}
+/// Check if floating windows in the current tab can be closed.
+/// Do not call this when the autocommand window is in use!
+///
+/// @return true if all floating windows can be closed
+static bool can_close_floating_windows(void)
+{
+ assert(lastwin != aucmd_win);
+ for (win_T *wp = lastwin; wp->w_floating; wp = wp->w_prev) {
+ buf_T *buf = wp->w_buffer;
+ int need_hide = (bufIsChanged(buf) && buf->b_nwindows <= 1);
+
+ if (need_hide && !buf_hide(buf)) {
+ return false;
+ }
+ }
+ return true;
+}
+
/// Close the possibly last window in a tab page.
///
/// @param win window to close
@@ -2411,7 +2609,7 @@ static bool close_last_window_tabpage(win_T *win, bool free_buf, tabpage_T *prev
win_close_othertab(win, free_buf, prev_curtab);
if (h != tabline_height()) {
- shell_new_rows();
+ win_new_screen_rows();
}
}
entering_window(curwin);
@@ -2426,12 +2624,47 @@ static bool close_last_window_tabpage(win_T *win, bool free_buf, tabpage_T *prev
return true;
}
+/// Close the buffer of "win" and unload it if "free_buf" is true.
+/// "abort_if_last" is passed to close_buffer(): abort closing if all other
+/// windows are closed.
+static void win_close_buffer(win_T *win, bool free_buf, bool abort_if_last)
+{
+ // Free independent synblock before the buffer is freed.
+ if (win->w_buffer != NULL) {
+ reset_synblock(win);
+ }
+
+ // When a quickfix/location list window is closed and the buffer is
+ // displayed in only one window, then unlist the buffer.
+ if (win->w_buffer != NULL && bt_quickfix(win->w_buffer)
+ && win->w_buffer->b_nwindows == 1) {
+ win->w_buffer->b_p_bl = false;
+ }
+
+ // Close the link to the buffer.
+ if (win->w_buffer != NULL) {
+ bufref_T bufref;
+ set_bufref(&bufref, curbuf);
+ win->w_closing = true;
+ close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, abort_if_last, true);
+ if (win_valid_any_tab(win)) {
+ win->w_closing = false;
+ }
+
+ // Make sure curbuf is valid. It can become invalid if 'bufhidden' is
+ // "wipe".
+ if (!bufref_valid(&bufref)) {
+ curbuf = firstbuf;
+ }
+ }
+}
+
// Close window "win". Only works for the current tab page.
// If "free_buf" is true related buffer may be unloaded.
//
// Called by :quit, :close, :xit, :wq and findtag().
// Returns FAIL when the window was not closed.
-int win_close(win_T *win, bool free_buf)
+int win_close(win_T *win, bool free_buf, bool force)
{
win_T *wp;
bool other_buffer = false;
@@ -2442,7 +2675,7 @@ int win_close(win_T *win, bool free_buf)
frame_T *win_frame = win->w_floating ? NULL : win->w_frame->fr_parent;
const bool had_diffmode = win->w_p_diff;
- if (last_window() && !win->w_floating) {
+ if (last_window(win)) {
emsg(_("E444: Cannot close last window"));
return FAIL;
}
@@ -2455,15 +2688,24 @@ int win_close(win_T *win, bool free_buf)
emsg(_(e_autocmd_close));
return FAIL;
}
- if ((firstwin == aucmd_win || lastwin == aucmd_win) && one_window()) {
- emsg(_("E814: Cannot close window, only autocmd window would remain"));
- return FAIL;
- }
- if ((firstwin == win && lastwin_nofloating() == win)
- && lastwin->w_floating) {
- // TODO(bfredl): we might close the float also instead
- emsg(e_floatonly);
- return FAIL;
+ if (lastwin->w_floating && one_window(win)) {
+ if (lastwin == aucmd_win) {
+ emsg(_("E814: Cannot close window, only autocmd window would remain"));
+ return FAIL;
+ }
+ if (force || can_close_floating_windows()) {
+ // close the last window until the there are no floating windows
+ while (lastwin->w_floating) {
+ // `force` flag isn't actually used when closing a floating window.
+ if (win_close(lastwin, free_buf, true) == FAIL) {
+ // If closing the window fails give up, to avoid looping forever.
+ return FAIL;
+ }
+ }
+ } else {
+ emsg(e_floatonly);
+ return FAIL;
+ }
}
// When closing the last window in a tab page first go to another tab page
@@ -2501,6 +2743,8 @@ int win_close(win_T *win, bool free_buf)
* to be the last one left, return now.
*/
if (wp->w_buffer != curbuf) {
+ reset_VIsual_and_resel(); // stop Visual mode
+
other_buffer = true;
win->w_closing = true;
apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, false, curbuf);
@@ -2508,7 +2752,7 @@ int win_close(win_T *win, bool free_buf)
return FAIL;
}
win->w_closing = false;
- if (last_window()) {
+ if (last_window(win)) {
return FAIL;
}
}
@@ -2518,7 +2762,7 @@ int win_close(win_T *win, bool free_buf)
return FAIL;
}
win->w_closing = false;
- if (last_window()) {
+ if (last_window(win)) {
return FAIL;
}
// autocmds may abort script processing
@@ -2555,32 +2799,10 @@ int win_close(win_T *win, bool free_buf)
return OK;
}
- // Free independent synblock before the buffer is freed.
- if (win->w_buffer != NULL) {
- reset_synblock(win);
- }
-
- /*
- * Close the link to the buffer.
- */
- if (win->w_buffer != NULL) {
- bufref_T bufref;
- set_bufref(&bufref, curbuf);
- win->w_closing = true;
- close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, true);
- if (win_valid_any_tab(win)) {
- win->w_closing = false;
- }
-
- // Make sure curbuf is valid. It can become invalid if 'bufhidden' is
- // "wipe".
- if (!bufref_valid(&bufref)) {
- curbuf = firstbuf;
- }
- }
+ win_close_buffer(win, free_buf, true);
if (only_one_window() && win_valid(win) && win->w_buffer == NULL
- && (last_window() || curtab != prev_curtab
+ && (last_window(win) || curtab != prev_curtab
|| close_last_window_tabpage(win, free_buf, prev_curtab))
&& !win->w_floating) {
// Autocommands have closed all windows, quit now. Restore
@@ -2600,11 +2822,15 @@ int win_close(win_T *win, bool free_buf)
// Autocommands may have closed the window already, or closed the only
// other window or moved to another tab page.
- if (!win_valid(win) || (!win->w_floating && last_window())
+ if (!win_valid(win) || (!win->w_floating && last_window(win))
|| close_last_window_tabpage(win, free_buf, prev_curtab)) {
return FAIL;
}
+ // Now we are really going to close the window. Disallow any autocommand
+ // to split a window to avoid trouble.
+ split_disallowed++;
+
// let terminal buffers know that this window dimensions may be ignored
win->w_closing = true;
@@ -2613,10 +2839,11 @@ int win_close(win_T *win, bool free_buf)
wp = win_free_mem(win, &dir, NULL);
if (help_window) {
- // Closing the help window moves the cursor back to the original window.
- win_T *tmpwp = get_snapshot_focus(SNAP_HELP_IDX);
- if (tmpwp != NULL) {
- wp = tmpwp;
+ // Closing the help window moves the cursor back to the current window
+ // of the snapshot.
+ win_T *prev_win = get_snapshot_curwin(SNAP_HELP_IDX);
+ if (win_valid(prev_win)) {
+ wp = prev_win;
}
}
@@ -2672,6 +2899,8 @@ int win_close(win_T *win, bool free_buf)
}
}
+ split_disallowed--;
+
/*
* If last window has a status line now and we don't want one,
* remove the status line.
@@ -2713,8 +2942,8 @@ static void do_autocmd_winclosed(win_T *win)
return;
}
recursive = true;
- char_u winid[NUMBUFLEN];
- vim_snprintf((char *)winid, sizeof(winid), "%i", win->handle);
+ char winid[NUMBUFLEN];
+ vim_snprintf(winid, sizeof(winid), "%d", win->handle);
apply_autocmds(EVENT_WINCLOSED, winid, winid, false, win->w_buffer);
recursive = false;
}
@@ -2748,14 +2977,20 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp)
if (win->w_buffer != NULL) {
// Close the link to the buffer.
- close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, false);
+ close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, false, true);
}
// Careful: Autocommands may have closed the tab page or made it the
// current tab page.
- for (ptp = first_tabpage; ptp != NULL && ptp != tp; ptp = ptp->tp_next) {
- }
+ for (ptp = first_tabpage; ptp != NULL && ptp != tp; ptp = ptp->tp_next) {}
if (ptp == NULL || tp == curtab) {
+ // If the buffer was removed from the window we have to give it any
+ // buffer.
+ if (win_valid_any_tab(win) && win->w_buffer == NULL) {
+ win->w_buffer = firstbuf;
+ firstbuf->b_nwindows++;
+ win_init_empty(win);
+ }
return;
}
@@ -2775,9 +3010,9 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp)
// When closing the last window in a tab page remove the tab page.
if (tp->tp_firstwin == tp->tp_lastwin) {
- char_u prev_idx[NUMBUFLEN];
+ char prev_idx[NUMBUFLEN];
if (has_event(EVENT_TABCLOSED)) {
- vim_snprintf((char *)prev_idx, NUMBUFLEN, "%i", tabpage_index(tp));
+ vim_snprintf(prev_idx, NUMBUFLEN, "%i", tabpage_index(tp));
}
if (tp == first_tabpage) {
@@ -2857,6 +3092,9 @@ void win_free_all(void)
{
int dummy;
+ // avoid an error for switching tabpage with the cmdline window open
+ cmdwin_type = 0;
+
while (first_tabpage->tp_next != NULL) {
tabpage_close(TRUE);
}
@@ -3060,9 +3298,21 @@ static frame_T *win_altframe(win_T *win, tabpage_T *tp)
return frp->fr_prev;
}
+ // By default the next window will get the space that was abandoned by this
+ // window
frame_T *target_fr = frp->fr_next;
frame_T *other_fr = frp->fr_prev;
- if (p_spr || p_sb) {
+
+ // If this is part of a column of windows and 'splitbelow' is true then the
+ // previous window will get the space.
+ if (frp->fr_parent != NULL && frp->fr_parent->fr_layout == FR_COL && p_sb) {
+ target_fr = frp->fr_prev;
+ other_fr = frp->fr_next;
+ }
+
+ // If this is part of a row of windows, and 'splitright' is true then the
+ // previous window will get the space.
+ if (frp->fr_parent != NULL && frp->fr_parent->fr_layout == FR_ROW && p_spr) {
target_fr = frp->fr_prev;
other_fr = frp->fr_next;
}
@@ -3095,15 +3345,14 @@ static tabpage_T *alt_tabpage(void)
}
// Find the last but one tab page.
- for (tp = first_tabpage; tp->tp_next != curtab; tp = tp->tp_next) {
- }
+ for (tp = first_tabpage; tp->tp_next != curtab; tp = tp->tp_next) {}
return tp;
}
/*
* Find the left-upper window in frame "frp".
*/
-static win_T *frame2win(frame_T *frp)
+win_T *frame2win(frame_T *frp)
{
while (frp->fr_win == NULL) {
frp = frp->fr_child;
@@ -3130,23 +3379,40 @@ static bool frame_has_win(const frame_T *frp, const win_T *wp)
return false;
}
+/// Check if current window is at the bottom
+/// Returns true if there are no windows below current window
+static bool is_bottom_win(win_T *wp)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
+{
+ frame_T *frp;
+ for (frp = wp->w_frame; frp->fr_parent != NULL; frp = frp->fr_parent) {
+ if (frp->fr_parent->fr_layout == FR_COL && frp->fr_next != NULL) {
+ return false;
+ }
+ }
+ return true;
+}
/// Set a new height for a frame. Recursively sets the height for contained
/// frames and windows. Caller must take care of positions.
///
/// @param topfirst resize topmost contained frame first.
/// @param wfh obey 'winfixheight' when there is a choice;
/// may cause the height not to be set.
-static void frame_new_height(frame_T *topfrp, int height, bool topfirst, bool wfh)
+void frame_new_height(frame_T *topfrp, int height, bool topfirst, bool wfh)
FUNC_ATTR_NONNULL_ALL
{
frame_T *frp;
int extra_lines;
int h;
+ win_T *wp;
if (topfrp->fr_win != NULL) {
// Simple case: just one window.
- win_new_height(topfrp->fr_win,
- height - topfrp->fr_win->w_status_height);
+ wp = topfrp->fr_win;
+ if (is_bottom_win(wp)) {
+ wp->w_hsep_height = 0;
+ }
+ win_new_height(wp, height - wp->w_hsep_height - wp->w_status_height);
} else if (topfrp->fr_layout == FR_ROW) {
do {
// All frames in this row get the same new height.
@@ -3202,13 +3468,11 @@ static void frame_new_height(frame_T *topfrp, int height, bool topfirst, bool wf
if (topfirst) {
do {
frp = frp->fr_next;
- }
- while (wfh && frp != NULL && frame_fixed_height(frp));
+ } while (wfh && frp != NULL && frame_fixed_height(frp));
} else {
do {
frp = frp->fr_prev;
- }
- while (wfh && frp != NULL && frame_fixed_height(frp));
+ } while (wfh && frp != NULL && frame_fixed_height(frp));
}
// Increase "height" if we could not reduce enough frames.
if (frp == NULL) {
@@ -3302,8 +3566,8 @@ static void frame_add_statusline(frame_T *frp)
if (frp->fr_layout == FR_LEAF) {
wp = frp->fr_win;
if (wp->w_status_height == 0) {
- if (wp->w_height > 0) { // don't make it negative
- --wp->w_height;
+ if (wp->w_height - STATUS_HEIGHT >= 0) { // don't make it negative
+ wp->w_height -= STATUS_HEIGHT;
}
wp->w_status_height = STATUS_HEIGHT;
}
@@ -3315,8 +3579,7 @@ static void frame_add_statusline(frame_T *frp)
} else {
assert(frp->fr_layout == FR_COL);
// Only need to handle the last frame in the column.
- for (frp = frp->fr_child; frp->fr_next != NULL; frp = frp->fr_next) {
- }
+ for (frp = frp->fr_child; frp->fr_next != NULL; frp = frp->fr_next) {}
frame_add_statusline(frp);
}
}
@@ -3402,13 +3665,11 @@ static void frame_new_width(frame_T *topfrp, int width, bool leftfirst, bool wfw
if (leftfirst) {
do {
frp = frp->fr_next;
- }
- while (wfw && frp != NULL && frame_fixed_width(frp));
+ } while (wfw && frp != NULL && frame_fixed_width(frp));
} else {
do {
frp = frp->fr_prev;
- }
- while (wfw && frp != NULL && frame_fixed_width(frp));
+ } while (wfw && frp != NULL && frame_fixed_width(frp));
}
// Increase "width" if we could not reduce enough frames.
if (frp == NULL) {
@@ -3423,10 +3684,8 @@ static void frame_new_width(frame_T *topfrp, int width, bool leftfirst, bool wfw
topfrp->fr_width = width;
}
-/*
- * Add the vertical separator to windows at the right side of "frp".
- * Note: Does not check if there is room!
- */
+/// Add the vertical separator to windows at the right side of "frp".
+/// Note: Does not check if there is room!
static void frame_add_vsep(const frame_T *frp)
FUNC_ATTR_NONNULL_ARG(1)
{
@@ -3456,6 +3715,37 @@ static void frame_add_vsep(const frame_T *frp)
}
}
+/// Add the horizontal separator to windows at the bottom of "frp".
+/// Note: Does not check if there is room or whether the windows have a statusline!
+static void frame_add_hsep(const frame_T *frp)
+ FUNC_ATTR_NONNULL_ARG(1)
+{
+ win_T *wp;
+
+ if (frp->fr_layout == FR_LEAF) {
+ wp = frp->fr_win;
+ if (wp->w_hsep_height == 0) {
+ if (wp->w_height > 0) { // don't make it negative
+ wp->w_height++;
+ }
+ wp->w_hsep_height = 1;
+ }
+ } else if (frp->fr_layout == FR_ROW) {
+ // Handle all the frames in the row.
+ FOR_ALL_FRAMES(frp, frp->fr_child) {
+ frame_add_hsep(frp);
+ }
+ } else {
+ assert(frp->fr_layout == FR_COL);
+ // Only need to handle the last frame in the column.
+ frp = frp->fr_child;
+ while (frp->fr_next != NULL) {
+ frp = frp->fr_next;
+ }
+ frame_add_hsep(frp);
+ }
+}
+
/*
* Set frame width from the window it contains.
*/
@@ -3470,16 +3760,12 @@ static void frame_fix_width(win_T *wp)
static void frame_fix_height(win_T *wp)
FUNC_ATTR_NONNULL_ALL
{
- wp->w_frame->fr_height = wp->w_height + wp->w_status_height;
+ wp->w_frame->fr_height = wp->w_height + wp->w_hsep_height + wp->w_status_height;
}
-/*
- * Compute the minimal height for frame "topfrp".
- * Uses the 'winminheight' option.
- * When "next_curwin" isn't NULL, use p_wh for this window.
- * When "next_curwin" is NOWIN, don't use at least one line for the current
- * window.
- */
+/// Compute the minimal height for frame "topfrp". Uses the 'winminheight' option.
+/// When "next_curwin" isn't NULL, use p_wh for this window.
+/// When "next_curwin" is NOWIN, don't use at least one line for the current window.
static int frame_minheight(frame_T *topfrp, win_T *next_curwin)
{
frame_T *frp;
@@ -3487,11 +3773,14 @@ static int frame_minheight(frame_T *topfrp, win_T *next_curwin)
int n;
if (topfrp->fr_win != NULL) {
+ // Combined height of window bar and separator column or status line.
+ int extra_height = topfrp->fr_win->w_winbar_height + topfrp->fr_win->w_hsep_height
+ + topfrp->fr_win->w_status_height;
+
if (topfrp->fr_win == next_curwin) {
- m = p_wh + topfrp->fr_win->w_status_height;
+ m = p_wh + extra_height;
} else {
- // window: minimal height of the window plus status line
- m = p_wmh + topfrp->fr_win->w_status_height;
+ m = p_wmh + extra_height;
if (topfrp->fr_win == curwin && next_curwin == NULL) {
// Current window is minimal one line high.
if (p_wmh == 0) {
@@ -3561,7 +3850,6 @@ static int frame_minwidth(frame_T *topfrp, win_T *next_curwin)
return m;
}
-
/// Try to close all windows except current one.
/// Buffers in the other windows become hidden if 'hidden' is set, or '!' is
/// used and the buffer was modified.
@@ -3582,7 +3870,7 @@ void close_others(int message, int forceit)
return;
}
- if (one_window() && !lastwin->w_floating) {
+ if (one_nonfloat() && !lastwin->w_floating) {
if (message
&& !autocmd_busy) {
msg(_(m_onlyone));
@@ -3604,7 +3892,7 @@ void close_others(int message, int forceit)
continue;
}
if (!r) {
- if (message && (p_confirm || cmdmod.confirm) && p_write) {
+ if (message && (p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) && p_write) {
dialog_changed(wp->w_buffer, false);
if (!win_valid(wp)) { // autocommands messed wp up
nextwp = firstwin;
@@ -3615,7 +3903,9 @@ void close_others(int message, int forceit)
continue;
}
}
- win_close(wp, !buf_hide(wp->w_buffer) && !bufIsChanged(wp->w_buffer));
+ win_close(wp,
+ !buf_hide(wp->w_buffer) && !bufIsChanged(wp->w_buffer),
+ false);
}
if (message && !ONE_WINDOW) {
@@ -3623,33 +3913,6 @@ void close_others(int message, int forceit)
}
}
-
-/*
- * Init the current window "curwin".
- * Called when a new file is being edited.
- */
-void curwin_init(void)
-{
- win_init_empty(curwin);
-}
-
-void win_init_empty(win_T *wp)
-{
- redraw_later(wp, NOT_VALID);
- wp->w_lines_valid = 0;
- wp->w_cursor.lnum = 1;
- wp->w_curswant = wp->w_cursor.col = 0;
- wp->w_cursor.coladd = 0;
- wp->w_pcmark.lnum = 1; // pcmark not cleared but set to line 1
- wp->w_pcmark.col = 0;
- wp->w_prev_pcmark.lnum = 0;
- wp->w_prev_pcmark.col = 0;
- wp->w_topline = 1;
- wp->w_topfill = 0;
- wp->w_botline = 2;
- wp->w_s = &wp->w_buffer->b_s;
-}
-
/*
* Allocate the first window and put an empty buffer in it.
* Called from main().
@@ -3681,7 +3944,7 @@ void win_alloc_aucmd_win(void)
fconfig.width = Columns;
fconfig.height = 5;
fconfig.focusable = false;
- aucmd_win = win_new_float(NULL, fconfig, &err);
+ aucmd_win = win_new_float(NULL, true, fconfig, &err);
aucmd_win->w_buffer->b_nwindows--;
RESET_BINDING(aucmd_win);
}
@@ -3718,7 +3981,7 @@ static int win_alloc_firstwin(win_T *oldwin)
new_frame(curwin);
topframe = curwin->w_frame;
topframe->fr_width = Columns;
- topframe->fr_height = Rows - p_ch;
+ topframe->fr_height = Rows - p_ch - global_stl_height();
return OK;
}
@@ -3741,8 +4004,9 @@ static void new_frame(win_T *wp)
void win_init_size(void)
{
firstwin->w_height = ROWS_AVAIL;
- firstwin->w_height_inner = firstwin->w_height;
+ firstwin->w_height_inner = firstwin->w_height - firstwin->w_winbar_height;
firstwin->w_height_outer = firstwin->w_height;
+ firstwin->w_winrow_off = firstwin->w_winbar_height;
topframe->fr_height = ROWS_AVAIL;
firstwin->w_width = Columns;
firstwin->w_width_inner = firstwin->w_width;
@@ -3805,6 +4069,11 @@ int win_new_tabpage(int after, char_u *filename)
tabpage_T *newtp;
int n;
+ if (cmdwin_type != 0) {
+ emsg(_(e_cmdwin));
+ return FAIL;
+ }
+
newtp = alloc_tabpage();
// Remember the current windows in this Tab page.
@@ -3814,7 +4083,7 @@ int win_new_tabpage(int after, char_u *filename)
}
newtp->tp_localdir = old_curtab->tp_localdir
- ? vim_strsave(old_curtab->tp_localdir) : NULL;
+ ? xstrdup(old_curtab->tp_localdir) : NULL;
curtab = newtp;
@@ -3847,6 +4116,7 @@ int win_new_tabpage(int after, char_u *filename)
newtp->tp_topframe = topframe;
last_status(false);
+ set_winbar();
redraw_all_later(NOT_VALID);
@@ -3858,7 +4128,7 @@ int win_new_tabpage(int after, char_u *filename)
apply_autocmds(EVENT_WINNEW, NULL, NULL, false, curbuf);
apply_autocmds(EVENT_WINENTER, NULL, NULL, false, curbuf);
- apply_autocmds(EVENT_TABNEW, filename, filename, false, curbuf);
+ apply_autocmds(EVENT_TABNEW, (char *)filename, (char *)filename, false, curbuf);
apply_autocmds(EVENT_TABENTER, NULL, NULL, false, curbuf);
return OK;
@@ -3876,10 +4146,10 @@ int win_new_tabpage(int after, char_u *filename)
*/
int may_open_tabpage(void)
{
- int n = (cmdmod.tab == 0) ? postponed_split_tab : cmdmod.tab;
+ int n = (cmdmod.cmod_tab == 0) ? postponed_split_tab : cmdmod.cmod_tab;
if (n != 0) {
- cmdmod.tab = 0; // reset it to avoid doing it twice
+ cmdmod.cmod_tab = 0; // reset it to avoid doing it twice
postponed_split_tab = 0;
return win_new_tabpage(n, NULL);
}
@@ -4050,8 +4320,8 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, bool trigger_enter_a
{
int old_off = tp->tp_firstwin->w_winrow;
win_T *next_prevwin = tp->tp_prevwin;
-
tabpage_T *old_curtab = curtab;
+
curtab = tp;
firstwin = tp->tp_firstwin;
lastwin = tp->tp_lastwin;
@@ -4090,10 +4360,10 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, bool trigger_enter_a
}
if (curtab->tp_old_Rows != Rows || (old_off != firstwin->w_winrow)) {
- shell_new_rows();
+ win_new_screen_rows();
}
if (curtab->tp_old_Columns != Columns && starting == 0) {
- shell_new_columns(); // update window widths
+ win_new_screen_cols(); // update window widths
}
lastused_tabpage = old_curtab;
@@ -4176,14 +4446,12 @@ void goto_tabpage(int n)
ttp = curtab;
for (i = n; i < 0; ++i) {
for (tp = first_tabpage; tp->tp_next != ttp && tp->tp_next != NULL;
- tp = tp->tp_next) {
- }
+ tp = tp->tp_next) {}
ttp = tp;
}
} else if (n == 9999) {
// Go to last tab page.
- for (tp = first_tabpage; tp->tp_next != NULL; tp = tp->tp_next) {
- }
+ for (tp = first_tabpage; tp->tp_next != NULL; tp = tp->tp_next) {}
} else {
// Go to tab page "n".
tp = find_tabpage(n);
@@ -4203,6 +4471,10 @@ void goto_tabpage(int n)
/// @param trigger_leave_autocmds when true trigger *Leave autocommands.
void goto_tabpage_tp(tabpage_T *tp, bool trigger_enter_autocmds, bool trigger_leave_autocmds)
{
+ if (trigger_enter_autocmds || trigger_leave_autocmds) {
+ CHECK_CMDWIN;
+ }
+
// Don't repeat a message in another tab page.
set_keep_msg(NULL, 0);
@@ -4218,13 +4490,15 @@ void goto_tabpage_tp(tabpage_T *tp, bool trigger_enter_autocmds, bool trigger_le
}
}
-// Go to the last accessed tab page, if there is one.
-void goto_tabpage_lastused(void)
+/// Go to the last accessed tab page, if there is one.
+/// @return true if the tab page is valid, false otherwise.
+bool goto_tabpage_lastused(void)
{
- int index = tabpage_index(lastused_tabpage);
- if (index < tabpage_index(NULL)) {
- goto_tabpage(index);
+ if (valid_tabpage(lastused_tabpage)) {
+ goto_tabpage_tp(lastused_tabpage, true, true);
+ return true;
}
+ return false;
}
/*
@@ -4293,7 +4567,6 @@ void tabpage_move(int nr)
redraw_tabline = true;
}
-
/*
* Go to another window.
* When jumping to another buffer, stop Visual mode. Do this before
@@ -4305,12 +4578,8 @@ void win_goto(win_T *wp)
{
win_T *owp = curwin;
- if (text_locked()) {
+ if (text_or_buf_locked()) {
beep_flush();
- text_locked_msg();
- return;
- }
- if (curbuf_locked()) {
return;
}
@@ -4331,7 +4600,6 @@ void win_goto(win_T *wp)
}
}
-
/*
* Find the tabpage for window "win".
*/
@@ -4634,8 +4902,7 @@ static void win_enter_ext(win_T *const wp, const int flags)
void fix_current_dir(void)
{
// New directory is either the local directory of the window, tab or NULL.
- char *new_dir = (char *)(curwin->w_localdir
- ? curwin->w_localdir : curtab->tp_localdir);
+ char *new_dir = curwin->w_localdir ? curwin->w_localdir : curtab->tp_localdir;
char cwd[MAXPATHL];
if (os_dirname((char_u *)cwd, MAXPATHL) != OK) {
cwd[0] = NUL;
@@ -4646,23 +4913,32 @@ void fix_current_dir(void)
// (unless that was done already) and change to the local directory.
if (globaldir == NULL) {
if (cwd[0] != NUL) {
- globaldir = (char_u *)xstrdup(cwd);
+ globaldir = xstrdup(cwd);
}
}
+ bool dir_differs = pathcmp(new_dir, cwd, -1) != 0;
+ if (!p_acd && dir_differs) {
+ do_autocmd_dirchanged(new_dir, curwin->w_localdir ? kCdScopeWindow : kCdScopeTabpage,
+ kCdCauseWindow, true);
+ }
if (os_chdir(new_dir) == 0) {
- if (!p_acd && pathcmp(new_dir, cwd, -1) != 0) {
- do_autocmd_dirchanged(new_dir, curwin->w_localdir
- ? kCdScopeWindow : kCdScopeTabpage, kCdCauseWindow);
+ if (!p_acd && dir_differs) {
+ do_autocmd_dirchanged(new_dir, curwin->w_localdir ? kCdScopeWindow : kCdScopeTabpage,
+ kCdCauseWindow, false);
}
- last_chdir_reason = NULL;
- shorten_fnames(true);
}
+ last_chdir_reason = NULL;
+ shorten_fnames(true);
} else if (globaldir != NULL) {
// Window doesn't have a local directory and we are not in the global
// directory: Change to the global directory.
- if (os_chdir((char *)globaldir) == 0) {
- if (!p_acd && pathcmp((char *)globaldir, cwd, -1) != 0) {
- do_autocmd_dirchanged((char *)globaldir, kCdScopeGlobal, kCdCauseWindow);
+ bool dir_differs = pathcmp(globaldir, cwd, -1) != 0;
+ if (!p_acd && dir_differs) {
+ do_autocmd_dirchanged(globaldir, kCdScopeGlobal, kCdCauseWindow, true);
+ }
+ if (os_chdir(globaldir) == 0) {
+ if (!p_acd && dir_differs) {
+ do_autocmd_dirchanged(globaldir, kCdScopeGlobal, kCdCauseWindow, false);
}
}
XFREE_CLEAR(globaldir);
@@ -4785,7 +5061,6 @@ static win_T *win_alloc(win_T *after, bool hidden)
return new_wp;
}
-
// Free one wininfo_T.
void free_wininfo(wininfo_T *wip, buf_T *bp)
{
@@ -4796,7 +5071,6 @@ void free_wininfo(wininfo_T *wip, buf_T *bp)
xfree(wip);
}
-
/// Remove window 'wp' from the window list and free the structure.
///
/// @param tp tab page "win" is in, NULL for current
@@ -4819,6 +5093,7 @@ static void win_free(win_T *wp, tabpage_T *tp)
clear_winopt(&wp->w_allbuf_opt);
xfree(wp->w_p_lcs_chars.multispace);
+ xfree(wp->w_p_lcs_chars.leadmultispace);
vars_clear(&wp->w_vars->dv_hashtab); // free all w: variables
hash_init(&wp->w_vars->dv_hashtab);
@@ -4843,6 +5118,12 @@ static void win_free(win_T *wp, tabpage_T *tp)
xfree(wp->w_localdir);
xfree(wp->w_prevdir);
+ stl_clear_click_defs(wp->w_status_click_defs, wp->w_status_click_defs_size);
+ xfree(wp->w_status_click_defs);
+
+ stl_clear_click_defs(wp->w_winbar_click_defs, wp->w_winbar_click_defs_size);
+ xfree(wp->w_winbar_click_defs);
+
// Remove the window from the b_wininfo lists, it may happen that the
// freed memory is re-used for another window.
FOR_ALL_BUFFERS(buf) {
@@ -4904,8 +5185,7 @@ void win_free_grid(win_T *wp, bool reinit)
}
grid_free(&wp->w_grid_alloc);
if (reinit) {
- // if a float is turned into a split and back into a float, the grid
- // data structure will be reused
+ // if a float is turned into a split, the grid data structure will be reused
memset(&wp->w_grid_alloc, 0, sizeof(wp->w_grid_alloc));
}
}
@@ -5001,12 +5281,29 @@ static void frame_remove(frame_T *frp)
}
}
+void win_new_screensize(void)
+{
+ static long old_Rows = 0;
+ static long old_Columns = 0;
-/*
- * Called from win_new_shellsize() after Rows changed.
- * This only does the current tab page, others must be done when made active.
- */
-void shell_new_rows(void)
+ if (old_Rows != Rows) {
+ // If 'window' uses the whole screen, keep it using that.
+ // Don't change it when set with "-w size" on the command line.
+ if (p_window == old_Rows - 1 || (old_Rows == 0 && p_window == 0)) {
+ p_window = Rows - 1;
+ }
+ old_Rows = Rows;
+ win_new_screen_rows(); // update window sizes
+ }
+ if (old_Columns != Columns) {
+ old_Columns = Columns;
+ win_new_screen_cols(); // update window sizes
+ }
+}
+/// Called from win_new_screensize() after Rows changed.
+///
+/// This only does the current tab page, others must be done when made active.
+void win_new_screen_rows(void)
{
int h = (int)ROWS_AVAIL;
@@ -5030,10 +5327,8 @@ void shell_new_rows(void)
curtab->tp_ch_used = p_ch;
}
-/*
- * Called from win_new_shellsize() after Columns changed.
- */
-void shell_new_columns(void)
+/// Called from win_new_screensize() after Columns changed.
+void win_new_screen_cols(void)
{
if (firstwin == NULL) { // not initialized yet
return;
@@ -5050,25 +5345,35 @@ void shell_new_columns(void)
win_reconfig_floats(); // The size of floats might change
}
-/// Check if "wp" has scrolled since last time it was checked
-/// @param wp the window to check
-bool win_did_scroll(win_T *wp)
+/// Trigger WinScrolled for "curwin" if needed.
+void may_trigger_winscrolled(void)
{
- return (curwin->w_last_topline != curwin->w_topline
- || curwin->w_last_leftcol != curwin->w_leftcol
- || curwin->w_last_width != curwin->w_width
- || curwin->w_last_height != curwin->w_height);
-}
+ static bool recursive = false;
-/// Trigger WinScrolled autocmd
-void do_autocmd_winscrolled(win_T *wp)
-{
- apply_autocmds(EVENT_WINSCROLLED, NULL, NULL, false, curbuf);
+ if (recursive || !has_event(EVENT_WINSCROLLED)) {
+ return;
+ }
+
+ win_T *wp = curwin;
+ if (wp->w_last_topline != wp->w_topline
+ || wp->w_last_leftcol != wp->w_leftcol
+ || wp->w_last_width != wp->w_width
+ || wp->w_last_height != wp->w_height) {
+ char winid[NUMBUFLEN];
+ vim_snprintf(winid, sizeof(winid), "%d", wp->handle);
- wp->w_last_topline = wp->w_topline;
- wp->w_last_leftcol = wp->w_leftcol;
- wp->w_last_width = wp->w_width;
- wp->w_last_height = wp->w_height;
+ recursive = true;
+ apply_autocmds(EVENT_WINSCROLLED, winid, winid, false, wp->w_buffer);
+ recursive = false;
+
+ // an autocmd may close the window, "wp" may be invalid now
+ if (win_valid_any_tab(wp)) {
+ wp->w_last_topline = wp->w_topline;
+ wp->w_last_leftcol = wp->w_leftcol;
+ wp->w_last_width = wp->w_width;
+ wp->w_last_height = wp->w_height;
+ }
+ }
}
/*
@@ -5114,11 +5419,8 @@ void win_size_restore(garray_T *gap)
}
}
-/*
- * Update the position for all windows, using the width and height of the
- * frames.
- * Returns the row just after the last window.
- */
+// Update the position for all windows, using the width and height of the frames.
+// Returns the row just after the last window and global statusline (if there is one).
int win_comp_pos(void)
{
int row = tabline_height();
@@ -5133,7 +5435,7 @@ int win_comp_pos(void)
}
}
- return row;
+ return row + global_stl_height();
}
void win_reconfig_floats(void)
@@ -5167,7 +5469,7 @@ static void frame_comp_pos(frame_T *topfrp, int *row, int *col)
wp->w_redr_status = true;
wp->w_pos_changed = true;
}
- const int h = wp->w_height + wp->w_status_height;
+ const int h = wp->w_height + wp->w_hsep_height + wp->w_status_height;
*row += h > topfrp->fr_height ? topfrp->fr_height : h;
*col += wp->w_width + wp->w_vsep_width;
} else {
@@ -5184,7 +5486,6 @@ static void frame_comp_pos(frame_T *topfrp, int *row, int *col)
}
}
-
/*
* Set current window height and take care of repositioning other windows to
* fit around it.
@@ -5200,23 +5501,16 @@ void win_setheight(int height)
*/
void win_setheight_win(int height, win_T *win)
{
- if (win == curwin) {
- // Always keep current window at least one line high, even when
- // 'winminheight' is zero.
- if (height < p_wmh) {
- height = p_wmh;
- }
- if (height == 0) {
- height = 1;
- }
- }
+ // Always keep current window at least one line high, even when 'winminheight' is zero.
+ // Keep window at least two lines high if 'winbar' is enabled.
+ height = MAX(height, (win == curwin ? MAX(p_wmh, 1) : p_wmh) + win->w_winbar_height);
if (win->w_floating) {
win->w_float_config.height = height;
win_config_float(win, win->w_float_config);
redraw_later(win, NOT_VALID);
} else {
- frame_setheight(win->w_frame, height + win->w_status_height);
+ frame_setheight(win->w_frame, height + win->w_hsep_height + win->w_status_height);
// recompute the window positions
int row = win_comp_pos();
@@ -5225,15 +5519,19 @@ void win_setheight_win(int height, win_T *win)
// line, clear it.
if (full_screen && msg_scrolled == 0 && row < cmdline_row) {
grid_fill(&default_grid, row, cmdline_row, 0, Columns, ' ', ' ', 0);
+ if (msg_grid.chars) {
+ clear_cmdline = true;
+ }
}
cmdline_row = row;
+ p_ch = MAX(Rows - cmdline_row, 0);
+ curtab->tp_ch_used = p_ch;
msg_row = row;
msg_col = 0;
redraw_all_later(NOT_VALID);
}
}
-
/*
* Set the height of a frame to "height" and take care that all frames and
* windows inside it are resized. Also resize frames on the left and right if
@@ -5265,7 +5563,10 @@ static void frame_setheight(frame_T *curfrp, int height)
if (curfrp->fr_parent == NULL) {
// topframe: can only change the command line
if (height > ROWS_AVAIL) {
- height = ROWS_AVAIL;
+ // If height is greater than the available space, try to create space for
+ // the frame by reducing 'cmdheight' if possible, while making sure
+ // `cmdheight` doesn't go below 1.
+ height = MIN((p_ch > 0 ? ROWS_AVAIL + (p_ch - 1) : ROWS_AVAIL), height);
}
if (height > 0) {
frame_new_height(curfrp, height, false, false);
@@ -5307,8 +5608,8 @@ static void frame_setheight(frame_T *curfrp, int height)
room_cmdline = 0;
} else {
win_T *wp = lastwin_nofloating();
- room_cmdline = Rows - p_ch
- - (wp->w_winrow + wp->w_height + wp->w_status_height);
+ room_cmdline = Rows - p_ch - global_stl_height()
+ - (wp->w_winrow + wp->w_height + wp->w_hsep_height + wp->w_status_height);
if (room_cmdline < 0) {
room_cmdline = 0;
}
@@ -5671,10 +5972,8 @@ void win_drag_status_line(win_T *dragwin, int offset)
up = false;
// Only dragging the last status line can reduce p_ch.
room = Rows - cmdline_row;
- if (curfr->fr_next == NULL) {
- room -= 1;
- } else {
- room -= p_ch;
+ if (curfr->fr_next != NULL) {
+ room -= p_ch + global_stl_height();
}
if (room < 0) {
room = 0;
@@ -5730,10 +6029,7 @@ void win_drag_status_line(win_T *dragwin, int offset)
clear_cmdline = true;
}
cmdline_row = row;
- p_ch = Rows - cmdline_row;
- if (p_ch < 1) {
- p_ch = 1;
- }
+ p_ch = MAX(Rows - cmdline_row, 0);
curtab->tp_ch_used = p_ch;
redraw_all_later(SOME_VALID);
showmode();
@@ -5800,7 +6096,6 @@ void win_drag_vsep_line(win_T *dragwin, int offset)
}
fr = curfr; // put fr at window that grows
}
- assert(fr);
// Not enough room
if (room < offset) {
@@ -5813,7 +6108,9 @@ void win_drag_vsep_line(win_T *dragwin, int offset)
}
if (fr == NULL) {
- return; // Safety check, should not happen.
+ // This can happen when calling win_move_separator() on the rightmost
+ // window. Just don't do anything.
+ return;
}
// grow frame fr by offset lines
@@ -5844,7 +6141,6 @@ void win_drag_vsep_line(win_T *dragwin, int offset)
redraw_all_later(NOT_VALID);
}
-
#define FRACTION_MULT 16384L
// Set wp->w_fraction for the current w_wrow and w_height.
@@ -5996,7 +6292,7 @@ void win_set_inner_size(win_T *wp)
int prev_height = wp->w_height_inner;
int height = wp->w_height_request;
if (height == 0) {
- height = wp->w_height;
+ height = wp->w_height - wp->w_winbar_height;
}
if (height != prev_height) {
@@ -6013,8 +6309,8 @@ void win_set_inner_size(win_T *wp)
set_fraction(wp);
}
}
- wp->w_height_inner = height;
wp->w_skipcol = 0;
+ wp->w_height_inner = height;
// There is no point in adjusting the scroll position when exiting. Some
// values might be invalid.
@@ -6040,10 +6336,20 @@ void win_set_inner_size(win_T *wp)
terminal_check_size(wp->w_buffer->terminal);
}
- wp->w_height_outer = (wp->w_height_inner
- + wp->w_border_adj[0] + wp->w_border_adj[2]);
- wp->w_width_outer = (wp->w_width_inner
- + wp->w_border_adj[1] + wp->w_border_adj[3]);
+ wp->w_height_outer = (wp->w_height_inner + win_border_height(wp) + wp->w_winbar_height);
+ wp->w_width_outer = (wp->w_width_inner + win_border_width(wp));
+ wp->w_winrow_off = wp->w_border_adj[0] + wp->w_winbar_height;
+ wp->w_wincol_off = wp->w_border_adj[3];
+}
+
+static int win_border_height(win_T *wp)
+{
+ return wp->w_border_adj[0] + wp->w_border_adj[2];
+}
+
+static int win_border_width(win_T *wp)
+{
+ return wp->w_border_adj[1] + wp->w_border_adj[3];
}
/// Set the width of a window.
@@ -6179,7 +6485,7 @@ char_u *grab_file_name(long count, linenr_T *file_lnum)
*file_lnum = getdigits_long(&p, false, 0);
}
- return find_file_name_in_path(ptr, len, options, count, curbuf->b_ffname);
+ return find_file_name_in_path(ptr, len, options, count, (char_u *)curbuf->b_ffname);
}
return file_name_at_cursor(options | FNAME_HYP, count, file_lnum);
}
@@ -6200,7 +6506,7 @@ char_u *grab_file_name(long count, linenr_T *file_lnum)
char_u *file_name_at_cursor(int options, long count, linenr_T *file_lnum)
{
return file_name_in_line(get_cursor_line_ptr(),
- curwin->w_cursor.col, options, count, curbuf->b_ffname,
+ curwin->w_cursor.col, options, count, (char_u *)curbuf->b_ffname,
file_lnum);
}
@@ -6211,7 +6517,7 @@ char_u *file_name_at_cursor(int options, long count, linenr_T *file_lnum)
char_u *file_name_in_line(char_u *line, int col, int options, long count, char_u *rel_fname,
linenr_T *file_lnum)
{
- char_u *ptr;
+ char *ptr;
size_t len;
bool in_type = true;
bool is_url = false;
@@ -6219,7 +6525,7 @@ char_u *file_name_in_line(char_u *line, int col, int options, long count, char_u
/*
* search forward for what could be the start of a file name
*/
- ptr = line + col;
+ ptr = (char *)line + col;
while (*ptr != NUL && !vim_isfilec(*ptr)) {
MB_PTR_ADV(ptr);
}
@@ -6234,11 +6540,10 @@ char_u *file_name_in_line(char_u *line, int col, int options, long count, char_u
* Search backward for first char of the file name.
* Go one char back to ":" before "//" even when ':' is not in 'isfname'.
*/
- while (ptr > line) {
- if ((len = (size_t)(utf_head_off(line, ptr - 1))) > 0) {
+ while ((char_u *)ptr > line) {
+ if ((len = (size_t)(utf_head_off(line, (char_u *)ptr - 1))) > 0) {
ptr -= len + 1;
- } else if (vim_isfilec(ptr[-1])
- || ((options & FNAME_HYP) && path_is_url((char *)ptr - 1))) {
+ } else if (vim_isfilec(ptr[-1]) || ((options & FNAME_HYP) && path_is_url(ptr - 1))) {
ptr--;
} else {
break;
@@ -6251,13 +6556,13 @@ char_u *file_name_in_line(char_u *line, int col, int options, long count, char_u
*/
len = 0;
while (vim_isfilec(ptr[len]) || (ptr[len] == '\\' && ptr[len + 1] == ' ')
- || ((options & FNAME_HYP) && path_is_url((char *)ptr + len))
- || (is_url && vim_strchr((char_u *)":?&=", ptr[len]) != NULL)) {
+ || ((options & FNAME_HYP) && path_is_url(ptr + len))
+ || (is_url && vim_strchr(":?&=", ptr[len]) != NULL)) {
// After type:// we also include :, ?, & and = as valid characters, so that
// http://google.com:8080?q=this&that=ok works.
if ((ptr[len] >= 'A' && ptr[len] <= 'Z')
|| (ptr[len] >= 'a' && ptr[len] <= 'z')) {
- if (in_type && path_is_url((char *)ptr + len + 1)) {
+ if (in_type && path_is_url(ptr + len + 1)) {
is_url = true;
}
} else {
@@ -6275,13 +6580,13 @@ char_u *file_name_in_line(char_u *line, int col, int options, long count, char_u
* If there is trailing punctuation, remove it.
* But don't remove "..", could be a directory name.
*/
- if (len > 2 && vim_strchr((char_u *)".,:;!", ptr[len - 1]) != NULL
+ if (len > 2 && vim_strchr(".,:;!", ptr[len - 1]) != NULL
&& ptr[len - 2] != '.') {
--len;
}
if (file_lnum != NULL) {
- char_u *p;
+ char *p;
const char *line_english = " line ";
const char *line_transl = _(line_msg);
@@ -6302,80 +6607,177 @@ char_u *file_name_in_line(char_u *line, int col, int options, long count, char_u
}
p = skipwhite(p);
if (isdigit(*p)) {
- *file_lnum = getdigits_long(&p, false, 0);
+ *file_lnum = getdigits_long((char_u **)&p, false, 0);
}
}
}
- return find_file_name_in_path(ptr, len, options, count, rel_fname);
+ return find_file_name_in_path((char_u *)ptr, len, options, count, rel_fname);
}
-/// Add or remove a status line for the bottom window(s), according to the
+/// Add or remove a status line from window(s), according to the
/// value of 'laststatus'.
///
/// @param morewin pretend there are two or more windows if true.
void last_status(bool morewin)
{
// Don't make a difference between horizontal or vertical split.
- last_status_rec(topframe, (p_ls == 2
- || (p_ls == 1 && (morewin || !one_window()))));
+ last_status_rec(topframe, (p_ls == 2 || (p_ls == 1 && (morewin || !one_nonfloat()))),
+ global_stl_height() > 0);
+}
+
+// Remove status line from window, replacing it with a horizontal separator if needed.
+static void win_remove_status_line(win_T *wp, bool add_hsep)
+{
+ wp->w_status_height = 0;
+ if (add_hsep) {
+ wp->w_hsep_height = 1;
+ } else {
+ win_new_height(wp, wp->w_height + STATUS_HEIGHT);
+ }
+ comp_col();
+
+ stl_clear_click_defs(wp->w_status_click_defs, wp->w_status_click_defs_size);
+ xfree(wp->w_status_click_defs);
+ wp->w_status_click_defs_size = 0;
+ wp->w_status_click_defs = NULL;
+}
+
+// Look for a horizontally resizable frame, starting with frame "fr".
+// Returns NULL if there are no resizable frames.
+static frame_T *find_horizontally_resizable_frame(frame_T *fr)
+{
+ frame_T *fp = fr;
+
+ while (fp->fr_height <= frame_minheight(fp, NULL)) {
+ if (fp == topframe) {
+ return NULL;
+ }
+ // In a column of frames: go to frame above. If already at
+ // the top or in a row of frames: go to parent.
+ if (fp->fr_parent->fr_layout == FR_COL && fp->fr_prev != NULL) {
+ fp = fp->fr_prev;
+ } else {
+ fp = fp->fr_parent;
+ }
+ }
+
+ return fp;
}
-static void last_status_rec(frame_T *fr, bool statusline)
+// Look for resizable frames and take lines from them to make room for the statusline.
+// @return Success or failure.
+static bool resize_frame_for_status(frame_T *fr)
+{
+ win_T *wp = fr->fr_win;
+ frame_T *fp = find_horizontally_resizable_frame(fr);
+
+ if (fp == NULL) {
+ emsg(_(e_noroom));
+ return false;
+ } else if (fp != fr) {
+ frame_new_height(fp, fp->fr_height - 1, false, false);
+ frame_fix_height(wp);
+ (void)win_comp_pos();
+ } else {
+ win_new_height(wp, wp->w_height - 1);
+ }
+
+ return true;
+}
+
+// Look for resizable frames and take lines from them to make room for the winbar.
+// @return Success or failure.
+static bool resize_frame_for_winbar(frame_T *fr)
+{
+ win_T *wp = fr->fr_win;
+ frame_T *fp = find_horizontally_resizable_frame(fr);
+
+ if (fp == NULL || fp == fr) {
+ emsg(_(e_noroom));
+ return false;
+ } else {
+ frame_new_height(fp, fp->fr_height - 1, false, false);
+ win_new_height(wp, wp->w_height + 1);
+ frame_fix_height(wp);
+ (void)win_comp_pos();
+ }
+
+ return true;
+}
+
+static void last_status_rec(frame_T *fr, bool statusline, bool is_stl_global)
{
frame_T *fp;
win_T *wp;
if (fr->fr_layout == FR_LEAF) {
wp = fr->fr_win;
- if (wp->w_status_height != 0 && !statusline) {
- // remove status line
- win_new_height(wp, wp->w_height + 1);
- wp->w_status_height = 0;
- comp_col();
- } else if (wp->w_status_height == 0 && statusline) {
- // Find a frame to take a line from.
- fp = fr;
- while (fp->fr_height <= frame_minheight(fp, NULL)) {
- if (fp == topframe) {
- emsg(_(e_noroom));
+ bool is_last = is_bottom_win(wp);
+
+ if (is_last) {
+ if (wp->w_status_height != 0 && (!statusline || is_stl_global)) {
+ win_remove_status_line(wp, false);
+ } else if (wp->w_status_height == 0 && !is_stl_global && statusline) {
+ // Add statusline to window if needed
+ wp->w_status_height = STATUS_HEIGHT;
+ if (!resize_frame_for_status(fr)) {
return;
}
- // In a column of frames: go to frame above. If already at
- // the top or in a row of frames: go to parent.
- if (fp->fr_parent->fr_layout == FR_COL && fp->fr_prev != NULL) {
- fp = fp->fr_prev;
- } else {
- fp = fp->fr_parent;
- }
- }
- wp->w_status_height = 1;
- if (fp != fr) {
- frame_new_height(fp, fp->fr_height - 1, false, false);
- frame_fix_height(wp);
- (void)win_comp_pos();
- } else {
- win_new_height(wp, wp->w_height - 1);
- }
+ comp_col();
+ }
+ } else if (wp->w_status_height != 0 && is_stl_global) {
+ // If statusline is global and the window has a statusline, replace it with a horizontal
+ // separator
+ win_remove_status_line(wp, true);
+ } else if (wp->w_status_height == 0 && !is_stl_global) {
+ // If statusline isn't global and the window doesn't have a statusline, re-add it
+ wp->w_status_height = STATUS_HEIGHT;
+ wp->w_hsep_height = 0;
comp_col();
- redraw_all_later(SOME_VALID);
}
- } else if (fr->fr_layout == FR_ROW) {
- // vertically split windows, set status line for each one
+ redraw_all_later(SOME_VALID);
+ } else {
+ // For a column or row frame, recursively call this function for all child frames
FOR_ALL_FRAMES(fp, fr->fr_child) {
- last_status_rec(fp, statusline);
+ last_status_rec(fp, statusline, is_stl_global);
}
- } else {
- // horizontally split window, set status line for last one
- for (fp = fr->fr_child; fp->fr_next != NULL; fp = fp->fr_next) {
+ }
+}
+
+// Add or remove window bars from windows depending on the value of 'winbar'.
+void set_winbar(void)
+{
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ // Require the local value to be set in order to show winbar on a floating window.
+ int winbar_height = wp->w_floating ? ((*wp->w_p_wbr != NUL) ? 1 : 0)
+ : ((*p_wbr != NUL || *wp->w_p_wbr != NUL) ? 1 : 0);
+
+ if (wp->w_winbar_height != winbar_height) {
+ if (winbar_height == 1 && wp->w_height_inner <= 1) {
+ if (wp->w_floating) {
+ emsg(_(e_noroom));
+ continue;
+ } else if (!resize_frame_for_winbar(wp->w_frame)) {
+ return;
+ }
+ }
+ wp->w_winbar_height = winbar_height;
+ win_set_inner_size(wp);
+ wp->w_redr_status = wp->w_redr_status || winbar_height;
+
+ if (winbar_height == 0) {
+ // When removing winbar, deallocate the w_winbar_click_defs array
+ stl_clear_click_defs(wp->w_winbar_click_defs, wp->w_winbar_click_defs_size);
+ xfree(wp->w_winbar_click_defs);
+ wp->w_winbar_click_defs_size = 0;
+ wp->w_winbar_click_defs = NULL;
+ }
}
- last_status_rec(fp, statusline);
}
}
-/*
- * Return the number of lines used by the tab page line.
- */
+/// Return the number of lines used by the tab page line.
int tabline_height(void)
{
if (ui_has(kUITabline)) {
@@ -6391,10 +6793,20 @@ int tabline_height(void)
return 1;
}
-/*
- * Return the minimal number of rows that is needed on the screen to display
- * the current number of windows.
- */
+/// Return the number of lines used by default by the window bar.
+int global_winbar_height(void)
+{
+ return *p_wbr != NUL ? 1 : 0;
+}
+
+/// Return the number of lines used by the global statusline
+int global_stl_height(void)
+{
+ return (p_ls == 3) ? STATUS_HEIGHT : 0;
+}
+
+/// Return the minimal number of rows that is needed on the screen to display
+/// the current number of windows.
int min_rows(void)
{
if (firstwin == NULL) { // not initialized yet
@@ -6408,7 +6820,7 @@ int min_rows(void)
total = n;
}
}
- total += tabline_height();
+ total += tabline_height() + global_stl_height();
total += 1; // count the room for the command line
return total;
}
@@ -6533,6 +6945,35 @@ static void clear_snapshot_rec(frame_T *fr)
}
}
+/// Traverse a snapshot to find the previous curwin.
+static win_T *get_snapshot_curwin_rec(frame_T *ft)
+{
+ win_T *wp;
+
+ if (ft->fr_next != NULL) {
+ if ((wp = get_snapshot_curwin_rec(ft->fr_next)) != NULL) {
+ return wp;
+ }
+ }
+ if (ft->fr_child != NULL) {
+ if ((wp = get_snapshot_curwin_rec(ft->fr_child)) != NULL) {
+ return wp;
+ }
+ }
+
+ return ft->fr_win;
+}
+
+/// @return the current window stored in the snapshot or NULL.
+static win_T *get_snapshot_curwin(int idx)
+{
+ if (curtab->tp_snapshot[idx] == NULL) {
+ return NULL;
+ }
+
+ return get_snapshot_curwin_rec(curtab->tp_snapshot[idx]);
+}
+
/// Restore a previously created snapshot, if there is any.
/// This is only done if the screen size didn't change and the window layout is
/// still the same.
@@ -6605,28 +7046,6 @@ static win_T *restore_snapshot_rec(frame_T *sn, frame_T *fr)
return wp;
}
-/// Gets the focused window (the one holding the cursor) of the snapshot.
-static win_T *get_snapshot_focus(int idx)
-{
- if (curtab->tp_snapshot[idx] == NULL) {
- return NULL;
- }
-
- frame_T *sn = curtab->tp_snapshot[idx];
- // This should be equivalent to the recursive algorithm found in
- // restore_snapshot as far as traveling nodes go.
- while (sn->fr_child != NULL || sn->fr_next != NULL) {
- while (sn->fr_child != NULL) {
- sn = sn->fr_child;
- }
- if (sn->fr_next != NULL) {
- sn = sn->fr_next;
- }
- }
-
- return win_valid(sn->fr_win) ? sn->fr_win : NULL;
-}
-
/// Set "win" to be the curwin and "tp" to be the current tab page.
/// restore_win() MUST be called to undo, also when FAIL is returned.
/// No autocommands will be executed until restore_win() is called.
@@ -6635,20 +7054,27 @@ static win_T *get_snapshot_focus(int idx)
/// triggered, another tabpage access is limited.
///
/// @return FAIL if switching to "win" failed.
-int switch_win(win_T **save_curwin, tabpage_T **save_curtab, win_T *win, tabpage_T *tp,
- bool no_display)
+int switch_win(switchwin_T *switchwin, win_T *win, tabpage_T *tp, bool no_display)
{
block_autocmds();
- return switch_win_noblock(save_curwin, save_curtab, win, tp, no_display);
+ return switch_win_noblock(switchwin, win, tp, no_display);
}
// As switch_win() but without blocking autocommands.
-int switch_win_noblock(win_T **save_curwin, tabpage_T **save_curtab, win_T *win, tabpage_T *tp,
- bool no_display)
+int switch_win_noblock(switchwin_T *switchwin, win_T *win, tabpage_T *tp, bool no_display)
{
- *save_curwin = curwin;
+ memset(switchwin, 0, sizeof(switchwin_T));
+ switchwin->sw_curwin = curwin;
+ if (win == curwin) {
+ switchwin->sw_same_win = true;
+ } else {
+ // Disable Visual selection, because redrawing may fail.
+ switchwin->sw_visual_active = VIsual_active;
+ VIsual_active = false;
+ }
+
if (tp != NULL) {
- *save_curtab = curtab;
+ switchwin->sw_curtab = curtab;
if (no_display) {
curtab->tp_firstwin = firstwin;
curtab->tp_lastwin = lastwin;
@@ -6670,33 +7096,35 @@ int switch_win_noblock(win_T **save_curwin, tabpage_T **save_curtab, win_T *win,
// Restore current tabpage and window saved by switch_win(), if still valid.
// When "no_display" is true the display won't be affected, no redraw is
// triggered.
-void restore_win(win_T *save_curwin, tabpage_T *save_curtab, bool no_display)
+void restore_win(switchwin_T *switchwin, bool no_display)
{
- restore_win_noblock(save_curwin, save_curtab, no_display);
+ restore_win_noblock(switchwin, no_display);
unblock_autocmds();
}
// As restore_win() but without unblocking autocommands.
-void restore_win_noblock(win_T *save_curwin, tabpage_T *save_curtab, bool no_display)
+void restore_win_noblock(switchwin_T *switchwin, bool no_display)
{
- if (save_curtab != NULL && valid_tabpage(save_curtab)) {
+ if (switchwin->sw_curtab != NULL && valid_tabpage(switchwin->sw_curtab)) {
if (no_display) {
curtab->tp_firstwin = firstwin;
curtab->tp_lastwin = lastwin;
- curtab = save_curtab;
+ curtab = switchwin->sw_curtab;
firstwin = curtab->tp_firstwin;
lastwin = curtab->tp_lastwin;
} else {
- goto_tabpage_tp(save_curtab, false, false);
+ goto_tabpage_tp(switchwin->sw_curtab, false, false);
}
}
- if (win_valid(save_curwin)) {
- curwin = save_curwin;
+
+ if (!switchwin->sw_same_win) {
+ VIsual_active = switchwin->sw_visual_active;
+ }
+
+ if (win_valid(switchwin->sw_curwin)) {
+ curwin = switchwin->sw_curwin;
curbuf = curwin->w_buffer;
}
- // If called by win_execute() and executing the command changed the
- // directory, it now has to be restored.
- fix_current_dir();
}
/// Make "buf" the current buffer.
@@ -6726,286 +7154,6 @@ void restore_buffer(bufref_T *save_curbuf)
}
}
-
-/// Add match to the match list of window 'wp'. The pattern 'pat' will be
-/// highlighted with the group 'grp' with priority 'prio'.
-/// Optionally, a desired ID 'id' can be specified (greater than or equal to 1).
-///
-/// @param[in] id a desired ID 'id' can be specified
-/// (greater than or equal to 1). -1 must be specified if no
-/// particular ID is desired
-/// @param[in] conceal_char pointer to conceal replacement char
-/// @return ID of added match, -1 on failure.
-int match_add(win_T *wp, const char *const grp, const char *const pat, int prio, int id,
- list_T *pos_list, const char *const conceal_char)
- FUNC_ATTR_NONNULL_ARG(1, 2)
-{
- matchitem_T *cur;
- matchitem_T *prev;
- matchitem_T *m;
- int hlg_id;
- regprog_T *regprog = NULL;
- int rtype = SOME_VALID;
-
- if (*grp == NUL || (pat != NULL && *pat == NUL)) {
- return -1;
- }
- if (id < -1 || id == 0) {
- semsg(_("E799: Invalid ID: %" PRId64
- " (must be greater than or equal to 1)"),
- (int64_t)id);
- return -1;
- }
- if (id != -1) {
- cur = wp->w_match_head;
- while (cur != NULL) {
- if (cur->id == id) {
- semsg(_("E801: ID already taken: %" PRId64), (int64_t)id);
- return -1;
- }
- cur = cur->next;
- }
- }
- if ((hlg_id = syn_check_group(grp, strlen(grp))) == 0) {
- return -1;
- }
- if (pat != NULL && (regprog = vim_regcomp((char_u *)pat, RE_MAGIC)) == NULL) {
- semsg(_(e_invarg2), pat);
- return -1;
- }
-
- // Find available match ID.
- while (id == -1) {
- cur = wp->w_match_head;
- while (cur != NULL && cur->id != wp->w_next_match_id) {
- cur = cur->next;
- }
- if (cur == NULL) {
- id = wp->w_next_match_id;
- }
- wp->w_next_match_id++;
- }
-
- // Build new match.
- m = xcalloc(1, sizeof(matchitem_T));
- m->id = id;
- m->priority = prio;
- m->pattern = pat == NULL ? NULL: (char_u *)xstrdup(pat);
- m->hlg_id = hlg_id;
- m->match.regprog = regprog;
- m->match.rmm_ic = FALSE;
- m->match.rmm_maxcol = 0;
- m->conceal_char = 0;
- if (conceal_char != NULL) {
- m->conceal_char = utf_ptr2char((const char_u *)conceal_char);
- }
-
- // Set up position matches
- if (pos_list != NULL) {
- linenr_T toplnum = 0;
- linenr_T botlnum = 0;
-
- int i = 0;
- TV_LIST_ITER(pos_list, li, {
- linenr_T lnum = 0;
- colnr_T col = 0;
- int len = 1;
- bool error = false;
-
- if (TV_LIST_ITEM_TV(li)->v_type == VAR_LIST) {
- const list_T *const subl = TV_LIST_ITEM_TV(li)->vval.v_list;
- const listitem_T *subli = tv_list_first(subl);
- if (subli == NULL) {
- semsg(_("E5030: Empty list at position %d"),
- (int)tv_list_idx_of_item(pos_list, li));
- goto fail;
- }
- lnum = tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error);
- if (error) {
- goto fail;
- }
- if (lnum <= 0) {
- continue;
- }
- m->pos.pos[i].lnum = lnum;
- subli = TV_LIST_ITEM_NEXT(subl, subli);
- if (subli != NULL) {
- col = tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error);
- if (error) {
- goto fail;
- }
- if (col < 0) {
- continue;
- }
- subli = TV_LIST_ITEM_NEXT(subl, subli);
- if (subli != NULL) {
- len = tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error);
- if (len < 0) {
- continue;
- }
- if (error) {
- goto fail;
- }
- }
- }
- m->pos.pos[i].col = col;
- m->pos.pos[i].len = len;
- } else if (TV_LIST_ITEM_TV(li)->v_type == VAR_NUMBER) {
- if (TV_LIST_ITEM_TV(li)->vval.v_number <= 0) {
- continue;
- }
- m->pos.pos[i].lnum = TV_LIST_ITEM_TV(li)->vval.v_number;
- m->pos.pos[i].col = 0;
- m->pos.pos[i].len = 0;
- } else {
- semsg(_("E5031: List or number required at position %d"),
- (int)tv_list_idx_of_item(pos_list, li));
- goto fail;
- }
- if (toplnum == 0 || lnum < toplnum) {
- toplnum = lnum;
- }
- if (botlnum == 0 || lnum >= botlnum) {
- botlnum = lnum + 1;
- }
- i++;
- if (i >= MAXPOSMATCH) {
- break;
- }
- });
-
- // Calculate top and bottom lines for redrawing area
- if (toplnum != 0) {
- if (wp->w_buffer->b_mod_set) {
- if (wp->w_buffer->b_mod_top > toplnum) {
- wp->w_buffer->b_mod_top = toplnum;
- }
- if (wp->w_buffer->b_mod_bot < botlnum) {
- wp->w_buffer->b_mod_bot = botlnum;
- }
- } else {
- wp->w_buffer->b_mod_set = true;
- wp->w_buffer->b_mod_top = toplnum;
- wp->w_buffer->b_mod_bot = botlnum;
- wp->w_buffer->b_mod_xlines = 0;
- }
- m->pos.toplnum = toplnum;
- m->pos.botlnum = botlnum;
- rtype = VALID;
- }
- }
-
- // Insert new match. The match list is in ascending order with regard to
- // the match priorities.
- cur = wp->w_match_head;
- prev = cur;
- while (cur != NULL && prio >= cur->priority) {
- prev = cur;
- cur = cur->next;
- }
- if (cur == prev) {
- wp->w_match_head = m;
- } else {
- prev->next = m;
- }
- m->next = cur;
-
- redraw_later(wp, rtype);
- return id;
-
-fail:
- xfree(m);
- return -1;
-}
-
-
-/// Delete match with ID 'id' in the match list of window 'wp'.
-///
-/// @param perr print error messages if true.
-int match_delete(win_T *wp, int id, bool perr)
-{
- matchitem_T *cur = wp->w_match_head;
- matchitem_T *prev = cur;
- int rtype = SOME_VALID;
-
- if (id < 1) {
- if (perr) {
- semsg(_("E802: Invalid ID: %" PRId64
- " (must be greater than or equal to 1)"),
- (int64_t)id);
- }
- return -1;
- }
- while (cur != NULL && cur->id != id) {
- prev = cur;
- cur = cur->next;
- }
- if (cur == NULL) {
- if (perr) {
- semsg(_("E803: ID not found: %" PRId64), (int64_t)id);
- }
- return -1;
- }
- if (cur == prev) {
- wp->w_match_head = cur->next;
- } else {
- prev->next = cur->next;
- }
- vim_regfree(cur->match.regprog);
- xfree(cur->pattern);
- if (cur->pos.toplnum != 0) {
- if (wp->w_buffer->b_mod_set) {
- if (wp->w_buffer->b_mod_top > cur->pos.toplnum) {
- wp->w_buffer->b_mod_top = cur->pos.toplnum;
- }
- if (wp->w_buffer->b_mod_bot < cur->pos.botlnum) {
- wp->w_buffer->b_mod_bot = cur->pos.botlnum;
- }
- } else {
- wp->w_buffer->b_mod_set = true;
- wp->w_buffer->b_mod_top = cur->pos.toplnum;
- wp->w_buffer->b_mod_bot = cur->pos.botlnum;
- wp->w_buffer->b_mod_xlines = 0;
- }
- rtype = VALID;
- }
- xfree(cur);
- redraw_later(wp, rtype);
- return 0;
-}
-
-/*
- * Delete all matches in the match list of window 'wp'.
- */
-void clear_matches(win_T *wp)
-{
- matchitem_T *m;
-
- while (wp->w_match_head != NULL) {
- m = wp->w_match_head->next;
- vim_regfree(wp->w_match_head->match.regprog);
- xfree(wp->w_match_head->pattern);
- xfree(wp->w_match_head);
- wp->w_match_head = m;
- }
- redraw_later(wp, SOME_VALID);
-}
-
-/*
- * Get match from ID 'id' in window 'wp'.
- * Return NULL if match not found.
- */
-matchitem_T *get_match(win_T *wp, int id)
-{
- matchitem_T *cur = wp->w_match_head;
-
- while (cur != NULL && cur->id != id) {
- cur = cur->next;
- }
- return cur;
-}
-
-
/// Check that "topfrp" and its children are at the right height.
///
/// @param topfrp top frame pointer
@@ -7131,16 +7279,14 @@ void win_id2tabwin(typval_T *const argvars, typval_T *const rettv)
tv_list_append_number(list, winnr);
}
-win_T *win_id2wp(typval_T *argvars)
+win_T *win_id2wp(int id)
{
- return win_id2wp_tp(argvars, NULL);
+ return win_id2wp_tp(id, NULL);
}
// Return the window and tab pointer of window "id".
-win_T *win_id2wp_tp(typval_T *argvars, tabpage_T **tpp)
+win_T *win_id2wp_tp(int id, tabpage_T **tpp)
{
- int id = tv_get_number(&argvars[0]);
-
FOR_ALL_TAB_WINDOWS(tp, wp) {
if (wp->handle == id) {
if (tpp != NULL) {
@@ -7172,7 +7318,7 @@ void win_findbuf(typval_T *argvars, list_T *list)
int bufnr = tv_get_number(&argvars[0]);
FOR_ALL_TAB_WINDOWS(tp, wp) {
- if (!wp->w_closing && wp->w_buffer->b_fnum == bufnr) {
+ if (wp->w_buffer->b_fnum == bufnr) {
tv_list_append_number(list, wp->handle);
}
}
diff --git a/src/nvim/window.h b/src/nvim/window.h
index 7e465a9f08..b75b8abd9b 100644
--- a/src/nvim/window.h
+++ b/src/nvim/window.h
@@ -4,19 +4,19 @@
#include <stdbool.h>
#include "nvim/buffer_defs.h"
+#include "nvim/mark.h"
+#include "nvim/os/os.h"
// Values for file_name_in_line()
#define FNAME_MESS 1 // give error message
#define FNAME_EXP 2 // expand to path
#define FNAME_HYP 4 // check for hypertext link
#define FNAME_INCL 8 // apply 'includeexpr'
-#define FNAME_REL 16 /* ".." and "./" are relative to the (current)
- file instead of the current directory */
+#define FNAME_REL 16 // ".." and "./" are relative to the (current)
+ // file instead of the current directory
#define FNAME_UNESC 32 // remove backslashes used for escaping
-/*
- * arguments for win_split()
- */
+// arguments for win_split()
#define WSP_ROOM 1 // require enough room
#define WSP_VERT 2 // split vertically
#define WSP_TOP 4 // window at top-left of shell
@@ -26,12 +26,66 @@
#define WSP_ABOVE 64 // put new window above/left
#define WSP_NEWLOC 128 // don't copy location list
-/*
- * Minimum screen size
- */
+// Minimum screen size
#define MIN_COLUMNS 12 // minimal columns for screen
#define MIN_LINES 2 // minimal lines for screen
+/// Structure used by switch_win() to pass values to restore_win()
+typedef struct {
+ win_T *sw_curwin;
+ tabpage_T *sw_curtab;
+ bool sw_same_win; ///< VIsual_active was not reset
+ bool sw_visual_active;
+} switchwin_T;
+
+/// Execute a block of code in the context of window `wp` in tabpage `tp`.
+/// Ensures the status line is redrawn and cursor position is valid if it is moved.
+#define WIN_EXECUTE(wp, tp, block) \
+ do { \
+ win_T *const wp_ = (wp); \
+ const pos_T curpos_ = wp_->w_cursor; \
+ char_u cwd_[MAXPATHL]; \
+ char_u autocwd_[MAXPATHL]; \
+ bool apply_acd_ = false; \
+ int cwd_status_ = FAIL; \
+ /* Getting and setting directory can be slow on some systems, only do */ \
+ /* this when the current or target window/tab have a local directory or */ \
+ /* 'acd' is set. */ \
+ if (curwin != wp \
+ && (curwin->w_localdir != NULL || wp->w_localdir != NULL \
+ || (curtab != tp && (curtab->tp_localdir != NULL || tp->tp_localdir != NULL)) \
+ || p_acd)) { \
+ cwd_status_ = os_dirname(cwd_, MAXPATHL); \
+ } \
+ /* If 'acd' is set, check we are using that directory. If yes, then */ \
+ /* apply 'acd' afterwards, otherwise restore the current directory. */ \
+ if (cwd_status_ == OK && p_acd) { \
+ do_autochdir(); \
+ apply_acd_ = os_dirname(autocwd_, MAXPATHL) == OK && STRCMP(cwd_, autocwd_) == 0; \
+ } \
+ switchwin_T switchwin_; \
+ if (switch_win_noblock(&switchwin_, wp_, (tp), true) == OK) { \
+ check_cursor(); \
+ block; \
+ } \
+ restore_win_noblock(&switchwin_, true); \
+ if (apply_acd_) { \
+ do_autochdir(); \
+ } else if (cwd_status_ == OK) { \
+ os_chdir((char *)cwd_); \
+ } \
+ /* Update the status line if the cursor moved. */ \
+ if (win_valid(wp_) && !equalpos(curpos_, wp_->w_cursor)) { \
+ wp_->w_redr_status = true; \
+ } \
+ /* In case the command moved the cursor or changed the Visual area, */ \
+ /* check it is valid. */ \
+ check_cursor(); \
+ if (VIsual_active) { \
+ check_pos(curbuf, &VIsual); \
+ } \
+ } while (false)
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "window.h.generated.h"
#endif
diff --git a/src/uncrustify.cfg b/src/uncrustify.cfg
index 49ce394dc9..2b9396e635 100644
--- a/src/uncrustify.cfg
+++ b/src/uncrustify.cfg
@@ -1,4 +1,4 @@
-# Uncrustify-0.74.0
+# Uncrustify-0.75.1_f
#
# General options
@@ -7,7 +7,7 @@
# The type of line endings.
#
# Default: auto
-newlines = auto # lf/crlf/cr/auto
+newlines = lf # lf/crlf/cr/auto
# The original size of tabs in the input.
#
@@ -81,7 +81,7 @@ sp_arith = ignore # ignore/add/remove/force/not_defined
# Add or remove space around arithmetic operators '+' and '-'.
#
# Overrides sp_arith.
-sp_arith_additive = ignore # ignore/add/remove/force/not_defined
+sp_arith_additive = force # ignore/add/remove/force/not_defined
# Add or remove space around assignment operator '=', '+=', etc.
sp_assign = ignore # ignore/add/remove/force/not_defined
@@ -127,6 +127,11 @@ sp_before_assign = ignore # ignore/add/remove/force/not_defined
# Overrides sp_assign.
sp_after_assign = ignore # ignore/add/remove/force/not_defined
+# Add or remove space in 'enum {'.
+#
+# Default: add
+sp_enum_brace = add # ignore/add/remove/force/not_defined
+
# Add or remove space in 'NS_ENUM ('.
sp_enum_paren = ignore # ignore/add/remove/force/not_defined
@@ -172,7 +177,7 @@ sp_inside_paren = remove # ignore/add/remove/force/not_defined
sp_paren_paren = remove # ignore/add/remove/force/not_defined
# Add or remove space between back-to-back parentheses, i.e. ')(' vs. ') ('.
-sp_cparen_oparen = ignore # ignore/add/remove/force/not_defined
+sp_cparen_oparen = remove # ignore/add/remove/force/not_defined
# Whether to balance spaces inside nested parentheses.
sp_balance_nested_parens = false # true/false
@@ -218,6 +223,10 @@ sp_after_ptr_star_trailing = ignore # ignore/add/remove/force/not_defined
# in a function pointer definition.
sp_ptr_star_func_var = ignore # ignore/add/remove/force/not_defined
+# Add or remove space between the pointer star '*' and the name of the type
+# in a function pointer type definition.
+sp_ptr_star_func_type = ignore # ignore/add/remove/force/not_defined
+
# Add or remove space after a pointer star '*', if followed by an open
# parenthesis, as in 'void* (*)()'.
sp_ptr_star_paren = ignore # ignore/add/remove/force/not_defined
@@ -252,6 +261,10 @@ sp_after_byref_func = ignore # ignore/add/remove/force/not_defined
# prototype or function definition.
sp_before_byref_func = ignore # ignore/add/remove/force/not_defined
+# Add or remove space after a reference sign '&', if followed by an open
+# parenthesis, as in 'char& (*)()'.
+sp_byref_paren = ignore # ignore/add/remove/force/not_defined
+
# Add or remove space between type and word. In cases where total removal of
# whitespace would be a syntax error, a value of 'remove' is treated the same
# as 'force'.
@@ -322,7 +335,7 @@ sp_inside_sparen = remove # ignore/add/remove/force/not_defined
# Add or remove space after '(' of control statements other than 'for'.
#
# Overrides sp_inside_sparen.
-sp_inside_sparen_open = remove # ignore/add/remove/force/not_defined
+sp_inside_sparen_open = ignore # ignore/add/remove/force/not_defined
# Add or remove space before ')' of control statements other than 'for'.
#
@@ -343,13 +356,13 @@ sp_inside_for_open = ignore # ignore/add/remove/force/not_defined
sp_inside_for_close = ignore # ignore/add/remove/force/not_defined
# Add or remove space between '((' or '))' of control statements.
-sp_sparen_paren = ignore # ignore/add/remove/force/not_defined
+sp_sparen_paren = remove # ignore/add/remove/force/not_defined
# Add or remove space after ')' of control statements.
sp_after_sparen = ignore # ignore/add/remove/force/not_defined
# Add or remove space between ')' and '{' of control statements.
-sp_sparen_brace = ignore # ignore/add/remove/force/not_defined
+sp_sparen_brace = force # ignore/add/remove/force/not_defined
# Add or remove space between 'do' and '{'.
sp_do_brace_open = force # ignore/add/remove/force/not_defined
@@ -452,14 +465,17 @@ sp_between_mdatype_commas = ignore # ignore/add/remove/force/not_defined
# Default: force
sp_paren_comma = force # ignore/add/remove/force/not_defined
+# Add or remove space between a type and ':'.
+sp_type_colon = ignore # ignore/add/remove/force/not_defined
+
# Add or remove space after the variadic '...' when preceded by a
# non-punctuator.
-# The value REMOVE will be overriden with FORCE
+# The value REMOVE will be overridden with FORCE
sp_after_ellipsis = ignore # ignore/add/remove/force/not_defined
# Add or remove space before the variadic '...' when preceded by a
# non-punctuator.
-# The value REMOVE will be overriden with FORCE
+# The value REMOVE will be overridden with FORCE
sp_before_ellipsis = ignore # ignore/add/remove/force/not_defined
# Add or remove space between a type and '...'.
@@ -468,9 +484,6 @@ sp_type_ellipsis = ignore # ignore/add/remove/force/not_defined
# Add or remove space between a '*' and '...'.
sp_ptr_type_ellipsis = ignore # ignore/add/remove/force/not_defined
-# (D) Add or remove space between a type and '?'.
-sp_type_question = ignore # ignore/add/remove/force/not_defined
-
# Add or remove space between ')' and '...'.
sp_paren_ellipsis = ignore # ignore/add/remove/force/not_defined
@@ -578,7 +591,7 @@ sp_inside_type_brace_init_lst = ignore # ignore/add/remove/force/not_defined
sp_inside_braces = add # ignore/add/remove/force/not_defined
# Add or remove space inside '{}'.
-sp_inside_braces_empty = ignore # ignore/add/remove/force/not_defined
+sp_inside_braces_empty = remove # ignore/add/remove/force/not_defined
# Add or remove space around trailing return operator '->'.
sp_trailing_return = ignore # ignore/add/remove/force/not_defined
@@ -592,7 +605,7 @@ sp_type_func = ignore # ignore/add/remove/force/not_defined
sp_type_brace_init_lst = ignore # ignore/add/remove/force/not_defined
# Add or remove space between function name and '(' on function declaration.
-sp_func_proto_paren = ignore # ignore/add/remove/force/not_defined
+sp_func_proto_paren = remove # ignore/add/remove/force/not_defined
# Add or remove space between function name and '()' on function declaration
# without parameters.
@@ -678,10 +691,10 @@ sp_return_paren = force # ignore/add/remove/force/not_defined
sp_return_brace = ignore # ignore/add/remove/force/not_defined
# Add or remove space between '__attribute__' and '('.
-sp_attribute_paren = ignore # ignore/add/remove/force/not_defined
+sp_attribute_paren = remove # ignore/add/remove/force/not_defined
# Add or remove space between 'defined' and '(' in '#if defined (FOO)'.
-sp_defined_paren = ignore # ignore/add/remove/force/not_defined
+sp_defined_paren = remove # ignore/add/remove/force/not_defined
# Add or remove space between 'throw' and '(' in 'throw (something)'.
sp_throw_paren = ignore # ignore/add/remove/force/not_defined
@@ -790,6 +803,10 @@ sp_d_array_colon = ignore # ignore/add/remove/force/not_defined
# Default: remove
sp_not = remove # ignore/add/remove/force/not_defined
+# Add or remove space between two '!' (not) unary operators.
+# If set to ignore, sp_not will be used.
+sp_not_not = ignore # ignore/add/remove/force/not_defined
+
# Add or remove space after the '~' (invert) unary operator.
#
# Default: remove
@@ -1044,15 +1061,21 @@ force_tab_after_define = false # true/false
# Default: 8
indent_columns = 2 # unsigned number
+# Whether to ignore indent for the first continuation line. Subsequent
+# continuation lines will still be indented to match the first.
+indent_ignore_first_continue = false # true/false
+
# The continuation indent. If non-zero, this overrides the indent of '(', '['
# and '=' continuation indents. Negative values are OK; negative value is
# absolute and not increased for each '(' or '[' level.
#
# For FreeBSD, this is set to 4.
+# Requires indent_ignore_first_continue=false.
indent_continue = 0 # number
# The continuation indent, only for class header line(s). If non-zero, this
# overrides the indent of 'class' continuation indents.
+# Requires indent_ignore_first_continue=false.
indent_continue_class_head = 0 # unsigned number
# Whether to indent empty lines (i.e. lines which contain only spaces before
@@ -1128,16 +1151,23 @@ indent_namespace_level = 0 # unsigned number
# indented. Requires indent_namespace=true. 0 means no limit.
indent_namespace_limit = 0 # unsigned number
+# Whether to indent only in inner namespaces (nested in other namespaces).
+# Requires indent_namespace=true.
+indent_namespace_inner_only = false # true/false
+
# Whether the 'extern "C"' body is indented.
indent_extern = false # true/false
# Whether the 'class' body is indented.
indent_class = false # true/false
+# Whether to ignore indent for the leading base class colon.
+indent_ignore_before_class_colon = false # true/false
+
# Additional indent before the leading base class colon.
# Negative values decrease indent down to the first column.
-# Requires a newline break before colon (see pos_class_colon
-# and nl_class_colon)
+# Requires indent_ignore_before_class_colon=false and a newline break before
+# the colon (see pos_class_colon and nl_class_colon)
indent_before_class_colon = 0 # number
# Whether to indent the stuff after a leading base class colon.
@@ -1147,6 +1177,9 @@ indent_class_colon = false # true/false
# colon. Requires indent_class_colon=true.
indent_class_on_colon = false # true/false
+# Whether to ignore indent for a leading class initializer colon.
+indent_ignore_before_constr_colon = false # true/false
+
# Whether to indent the stuff after a leading class initializer colon.
indent_constr_colon = false # true/false
@@ -1177,9 +1210,12 @@ indent_var_def_blk = 0 # number
# Whether to indent continued variable declarations instead of aligning.
indent_var_def_cont = false # true/false
-# Whether to indent continued shift expressions ('<<' and '>>') instead of
-# aligning. Set align_left_shift=false when enabling this.
-indent_shift = false # true/false
+# How to indent continued shift expressions ('<<' and '>>').
+# Set align_left_shift=false when using this.
+# 0: Align shift operators instead of indenting them (default)
+# 1: Indent by one level
+# -1: Preserve original indentation
+indent_shift = 0 # number
# Whether to force indentation of function definitions to start in column 1.
indent_func_def_force_col1 = false # true/false
@@ -1266,6 +1302,9 @@ indent_switch_case = 0 # unsigned number
# Usually the same as indent_columns or indent_switch_case.
indent_switch_body = 0 # unsigned number
+# Whether to ignore indent for '{' following 'case'.
+indent_ignore_case_brace = false # true/false
+
# Spaces to indent '{' from 'case'. By default, the brace will appear under
# the 'c' in case. Usually set to 0 or indent_columns. Negative values are OK.
# It might be wise to choose the same value for the option indent_switch_case.
@@ -1334,10 +1373,11 @@ indent_paren_nl = false # true/false
# How to indent a close parenthesis after a newline.
#
-# 0: Indent to body level (default)
-# 1: Align under the open parenthesis
-# 2: Indent to the brace level
-indent_paren_close = 0 # unsigned number
+# 0: Indent to body level (default)
+# 1: Align under the open parenthesis
+# 2: Indent to the brace level
+# -1: Preserve original indentation
+indent_paren_close = 0 # number
# Whether to indent the open parenthesis of a function definition,
# if the parenthesis is on its own line.
@@ -1351,24 +1391,41 @@ indent_paren_after_func_decl = false # true/false
# if the parenthesis is on its own line.
indent_paren_after_func_call = true # true/false
-# Whether to indent a comma when inside a brace.
-# If true, aligns under the open brace.
-indent_comma_brace = false # true/false
+# How to indent a comma when inside braces.
+# 0: Indent by one level (default)
+# 1: Align under the open brace
+# -1: Preserve original indentation
+indent_comma_brace = 0 # number
-# Whether to indent a comma when inside a parenthesis.
-# If true, aligns under the open parenthesis.
-indent_comma_paren = false # true/false
+# How to indent a comma when inside parentheses.
+# 0: Indent by one level (default)
+# 1: Align under the open parenthesis
+# -1: Preserve original indentation
+indent_comma_paren = 0 # number
-# Whether to indent a Boolean operator when inside a parenthesis.
-# If true, aligns under the open parenthesis.
-indent_bool_paren = false # true/false
+# How to indent a Boolean operator when inside parentheses.
+# 0: Indent by one level (default)
+# 1: Align under the open parenthesis
+# -1: Preserve original indentation
+indent_bool_paren = 0 # number
+
+# Whether to ignore the indentation of a Boolean operator when outside
+# parentheses.
+indent_ignore_bool = false # true/false
+
+# Whether to ignore the indentation of an arithmetic operator.
+indent_ignore_arith = false # true/false
# Whether to indent a semicolon when inside a for parenthesis.
# If true, aligns under the open for parenthesis.
indent_semicolon_for_paren = false # true/false
+# Whether to ignore the indentation of a semicolon outside of a 'for'
+# statement.
+indent_ignore_semicolon = false # true/false
+
# Whether to align the first expression to following ones
-# if indent_bool_paren=true.
+# if indent_bool_paren=1.
indent_first_bool_expr = false # true/false
# Whether to align the first expression to following ones
@@ -1382,6 +1439,9 @@ indent_square_nl = false # true/false
# (ESQL/C) Whether to preserve the relative indent of 'EXEC SQL' bodies.
indent_preserve_sql = false # true/false
+# Whether to ignore the indentation of an assignment operator.
+indent_ignore_assign = false # true/false
+
# Whether to align continued statements at the '='. If false or if the '=' is
# followed by a newline, the next line is indent one tab.
#
@@ -1507,7 +1567,7 @@ donot_indent_func_def_close_paren = false # true/false
# Whether to collapse empty blocks between '{' and '}'.
# If true, overrides nl_inside_empty_func
-nl_collapse_empty_body = false # true/false
+nl_collapse_empty_body = true # true/false
# Don't split one-line braced assignments, as in 'foo_t f = { 1, 2 };'.
nl_assign_leave_one_liners = false # true/false
@@ -1575,11 +1635,11 @@ nl_start_of_file = ignore # ignore/add/remove/force/not_defined
nl_start_of_file_min = 0 # unsigned number
# Add or remove newline at the end of the file.
-nl_end_of_file = ignore # ignore/add/remove/force/not_defined
+nl_end_of_file = force # ignore/add/remove/force/not_defined
# The minimum number of newlines at the end of the file (only used if
# nl_end_of_file is 'add' or 'force').
-nl_end_of_file_min = 0 # unsigned number
+nl_end_of_file_min = 1 # unsigned number
# Add or remove newline between '=' and '{'.
nl_assign_brace = ignore # ignore/add/remove/force/not_defined
@@ -1599,7 +1659,7 @@ nl_after_square_assign = ignore # ignore/add/remove/force/not_defined
nl_fcall_brace = ignore # ignore/add/remove/force/not_defined
# Add or remove newline between 'enum' and '{'.
-nl_enum_brace = ignore # ignore/add/remove/force/not_defined
+nl_enum_brace = remove # ignore/add/remove/force/not_defined
# Add or remove newline between 'enum' and 'class'.
nl_enum_class = ignore # ignore/add/remove/force/not_defined
@@ -1617,7 +1677,7 @@ nl_enum_colon_type = ignore # ignore/add/remove/force/not_defined
nl_struct_brace = remove # ignore/add/remove/force/not_defined
# Add or remove newline between 'union' and '{'.
-nl_union_brace = ignore # ignore/add/remove/force/not_defined
+nl_union_brace = remove # ignore/add/remove/force/not_defined
# Add or remove newline between 'if' and '{'.
nl_if_brace = remove # ignore/add/remove/force/not_defined
@@ -1675,7 +1735,7 @@ nl_oc_brace_catch = ignore # ignore/add/remove/force/not_defined
nl_brace_square = ignore # ignore/add/remove/force/not_defined
# Add or remove newline between '}' and ')' in a function invocation.
-nl_brace_fparen = ignore # ignore/add/remove/force/not_defined
+nl_brace_fparen = remove # ignore/add/remove/force/not_defined
# Add or remove newline between 'while' and '{'.
nl_while_brace = ignore # ignore/add/remove/force/not_defined
@@ -1700,7 +1760,7 @@ nl_brace_brace = ignore # ignore/add/remove/force/not_defined
nl_do_brace = remove # ignore/add/remove/force/not_defined
# Add or remove newline between '}' and 'while' of 'do' statement.
-nl_brace_while = ignore # ignore/add/remove/force/not_defined
+nl_brace_while = remove # ignore/add/remove/force/not_defined
# Add or remove newline between 'switch' and '{'.
nl_switch_brace = ignore # ignore/add/remove/force/not_defined
@@ -1861,7 +1921,7 @@ nl_func_call_paren = ignore # ignore/add/remove/force/not_defined
nl_func_call_paren_empty = ignore # ignore/add/remove/force/not_defined
# Add or remove newline after '(' in a function declaration.
-nl_func_decl_start = ignore # ignore/add/remove/force/not_defined
+nl_func_decl_start = remove # ignore/add/remove/force/not_defined
# Add or remove newline after '(' in a function definition.
nl_func_def_start = remove # ignore/add/remove/force/not_defined
@@ -1982,7 +2042,8 @@ nl_after_semicolon = false # true/false
nl_paren_dbrace_open = ignore # ignore/add/remove/force/not_defined
# Whether to add a newline after the type in an unnamed temporary
-# direct-list-initialization.
+# direct-list-initialization, better:
+# before a direct-list-initialization.
nl_type_brace_init_lst = ignore # ignore/add/remove/force/not_defined
# Whether to add a newline after the open brace in an unnamed temporary
@@ -2158,7 +2219,7 @@ donot_add_nl_before_cpp_comment = false # true/false
#
# The maximum number of consecutive newlines (3 = 2 blank lines).
-nl_max = 3 # unsigned number
+nl_max = 2 # unsigned number
# The maximum number of consecutive newlines in a function.
nl_max_blank_in_func = 0 # unsigned number
@@ -2240,15 +2301,15 @@ nl_typedef_blk_end = 0 # unsigned number
# 0: No change (default).
nl_typedef_blk_in = 0 # unsigned number
-# The number of newlines before a block of variable definitions not at the top
-# of a function body. If nl_after_access_spec is non-zero, that option takes
-# precedence.
+# The number of empty newlines before a block of variable definitions
+# not at the top of a function body. If nl_after_access_spec is non-zero,
+# that option takes precedence.
#
# 0: No change (default).
nl_var_def_blk_start = 0 # unsigned number
-# The number of newlines after a block of variable definitions not at the top
-# of a function body.
+# The number of empty newlines after a block of variable definitions
+# not at the top of a function body.
#
# 0: No change (default).
nl_var_def_blk_end = 0 # unsigned number
@@ -2564,6 +2625,11 @@ align_var_def_inline = false # true/false
# 0: Don't align (default).
align_assign_span = 0 # unsigned number
+# The span for aligning on '{' in braced init list.
+#
+# 0: Don't align (default).
+align_braced_init_list_span = 0 # unsigned number
+
# The span for aligning on '=' in function prototype modifier.
#
# 0: Don't align (default).
@@ -2575,6 +2641,17 @@ align_assign_func_proto_span = 0 # unsigned number
# 0: No limit (default).
align_assign_thresh = 0 # number
+# Whether to align on the left most assignment when multiple
+# definitions are found on the same line.
+# Depends on 'align_assign_span' and 'align_assign_thresh' settings.
+align_assign_on_multi_var_defs = false # true/false
+
+# The threshold for aligning on '{' in braced init list.
+# Use a negative number for absolute thresholds.
+#
+# 0: No limit (default).
+align_braced_init_list_thresh = 0 # number
+
# How to apply align_assign_span to function declaration "assignments", i.e.
# 'virtual void foo() = 0' or '~foo() = {default|delete}'.
#
@@ -2780,7 +2857,7 @@ align_oc_decl_colon = false # true/false
# (OC) Whether to not align parameters in an Objectve-C message call if first
# colon is not on next line of the message call (the same way Xcode does
-# aligment)
+# alignment)
align_oc_msg_colon_xcode_like = false # true/false
#
@@ -2980,12 +3057,17 @@ mod_full_brace_function = ignore # ignore/add/remove/force/not_defined
mod_full_brace_if = add # ignore/add/remove/force/not_defined
# Whether to enforce that all blocks of an 'if'/'else if'/'else' chain either
-# have, or do not have, braces. If true, braces will be added if any block
-# needs braces, and will only be removed if they can be removed from all
-# blocks.
-#
-# Overrides mod_full_brace_if.
-mod_full_brace_if_chain = false # true/false
+# have, or do not have, braces. Overrides mod_full_brace_if.
+#
+# 0: Don't override mod_full_brace_if
+# 1: Add braces to all blocks if any block needs braces and remove braces if
+# they can be removed from all blocks
+# 2: Add braces to all blocks if any block already has braces, regardless of
+# whether it needs them
+# 3: Add braces to all blocks if any block needs braces and remove braces if
+# they can be removed from all blocks, except if all blocks have braces
+# despite none needing them
+mod_full_brace_if_chain = 0 # unsigned number
# Whether to add braces to all blocks of an 'if'/'else if'/'else' chain.
# If true, mod_full_brace_if_chain will only remove braces from an 'if' that
@@ -3027,6 +3109,14 @@ mod_pawn_semicolon = false # true/false
# statement, as in 'if (a && b > c)' => 'if (a && (b > c))'.
mod_full_paren_if_bool = false # true/false
+# Whether to fully parenthesize Boolean expressions after '='
+# statement, as in 'x = a && b > c;' => 'x = (a && (b > c));'.
+mod_full_paren_assign_bool = false # true/false
+
+# Whether to fully parenthesize Boolean expressions after '='
+# statement, as in 'return a && b > c;' => 'return (a && (b > c));'.
+mod_full_paren_return_bool = false # true/false
+
# Whether to remove superfluous semicolons.
mod_remove_extra_semicolon = true # true/false
@@ -3094,6 +3184,10 @@ mod_sort_incl_import_grouping_enabled = true # true/false
# the close brace, as in 'case X: { ... } break;' => 'case X: { ... break; }'.
mod_move_case_break = false # true/false
+# Whether to move a 'return' that appears after a fully braced 'case' before
+# the close brace, as in 'case X: { ... } return;' => 'case X: { ... return; }'.
+mod_move_case_return = false # true/false
+
# Add or remove braces around a fully braced case statement. Will only remove
# braces if there are no variable declarations in the block.
mod_case_brace = remove # ignore/add/remove/force/not_defined
@@ -3144,6 +3238,10 @@ pp_indent = remove # ignore/add/remove/force/not_defined
# indented from column 1.
pp_indent_at_level = false # true/false
+# Whether to indent #if/#else/#endif at the parenthesis level if the brace
+# level is 0. If false, these are indented from column 1.
+pp_indent_at_level0 = false # true/false
+
# Specifies the number of columns to indent preprocessors per level
# at brace level 0 (file-level). If pp_indent_at_level=false, also specifies
# the number of columns to indent preprocessors per level
@@ -3209,12 +3307,37 @@ pp_indent_func_def = true # true/false
# Default: true
pp_indent_extern = true # true/false
-# Whether to indent braces directly inside #if, #else, and #endif.
-# Only applies to the indent of the preprocesser that the braces are directly
-# inside of.
+# How to indent braces directly inside #if, #else, and #endif.
+# Requires pp_if_indent_code=true and only applies to the indent of the
+# preprocesser that the braces are directly inside of.
+# 0: No extra indent
+# 1: Indent by one level
+# -1: Preserve original indentation
#
-# Default: true
-pp_indent_brace = true # true/false
+# Default: 1
+pp_indent_brace = 1 # number
+
+# Whether to print warning messages for unbalanced #if and #else blocks.
+# This will print a message in the following cases:
+# - if an #ifdef block ends on a different indent level than
+# where it started from. Example:
+#
+# #ifdef TEST
+# int i;
+# {
+# int j;
+# #endif
+#
+# - an #elif/#else block ends on a different indent level than
+# the corresponding #ifdef block. Example:
+#
+# #ifdef TEST
+# int i;
+# #else
+# }
+# int j;
+# #endif
+pp_warn_unbalanced_if = false # true/false
#
# Sort includes options
@@ -3253,17 +3376,16 @@ use_indent_func_call_param = true # true/false
#
# true: indent_continue will be used only once
# false: indent_continue will be used every time (default)
+#
+# Requires indent_ignore_first_continue=false.
use_indent_continue_only_once = false # true/false
-# The value might be used twice:
-# - at the assignment
-# - at the opening brace
-#
-# To prevent the double use of the indentation value, use this option with the
-# value 'true'.
+# The indentation can be:
+# - after the assignment, at the '[' character
+# - at the begin of the lambda body
#
-# true: indentation will be used only once
-# false: indentation will be used every time (default)
+# true: indentation will be after the assignment
+# false: indentation will be at the begin of the lambda body (default)
indent_cpp_lambda_only_once = false # true/false
# Whether sp_after_angle takes precedence over sp_inside_fparen. This was the
@@ -3386,10 +3508,12 @@ set QUESTION FUNC_ATTR_PRINTF
set QUESTION FUNC_ATTR_PURE
set QUESTION FUNC_ATTR_UNUSED
set QUESTION FUNC_ATTR_WARN_UNUSED_RESULT
+set PP_PRAGMA PRAGMA_DIAG_PUSH_IGNORE_IMPLICIT_FALLTHROUGH
+set PP_PRAGMA PRAGMA_DIAG_PUSH_IGNORE_MISSING_PROTOTYPES
set QUESTION REAL_FATTR_ALWAYS_INLINE
set QUESTION REAL_FATTR_CONST
set QUESTION REAL_FATTR_NONNULL_ALL
set QUESTION REAL_FATTR_PURE
set QUESTION REAL_FATTR_WARN_UNUSED_RESULT
-# option(s) with 'not default' value: 86
+# option(s) with 'not default' value: 102
#
diff --git a/unicode/CaseFolding.txt b/src/unicode/CaseFolding.txt
index 033788b253..932ace29e6 100644
--- a/unicode/CaseFolding.txt
+++ b/src/unicode/CaseFolding.txt
@@ -1,6 +1,6 @@
-# CaseFolding-13.0.0.txt
-# Date: 2019-09-08, 23:30:59 GMT
-# © 2019 Unicode®, Inc.
+# CaseFolding-14.0.0.txt
+# Date: 2021-03-08, 19:35:41 GMT
+# © 2021 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
# For terms of use, see http://www.unicode.org/terms_of_use.html
#
@@ -1050,6 +1050,7 @@
2C2C; C; 2C5C; # GLAGOLITIC CAPITAL LETTER SHTAPIC
2C2D; C; 2C5D; # GLAGOLITIC CAPITAL LETTER TROKUTASTI A
2C2E; C; 2C5E; # GLAGOLITIC CAPITAL LETTER LATINATE MYSLITE
+2C2F; C; 2C5F; # GLAGOLITIC CAPITAL LETTER CAUDATE CHRIVI
2C60; C; 2C61; # LATIN CAPITAL LETTER L WITH DOUBLE BAR
2C62; C; 026B; # LATIN CAPITAL LETTER L WITH MIDDLE TILDE
2C63; C; 1D7D; # LATIN CAPITAL LETTER P WITH STROKE
@@ -1230,12 +1231,16 @@ A7B8; C; A7B9; # LATIN CAPITAL LETTER U WITH STROKE
A7BA; C; A7BB; # LATIN CAPITAL LETTER GLOTTAL A
A7BC; C; A7BD; # LATIN CAPITAL LETTER GLOTTAL I
A7BE; C; A7BF; # LATIN CAPITAL LETTER GLOTTAL U
+A7C0; C; A7C1; # LATIN CAPITAL LETTER OLD POLISH O
A7C2; C; A7C3; # LATIN CAPITAL LETTER ANGLICANA W
A7C4; C; A794; # LATIN CAPITAL LETTER C WITH PALATAL HOOK
A7C5; C; 0282; # LATIN CAPITAL LETTER S WITH HOOK
A7C6; C; 1D8E; # LATIN CAPITAL LETTER Z WITH PALATAL HOOK
A7C7; C; A7C8; # LATIN CAPITAL LETTER D WITH SHORT STROKE OVERLAY
A7C9; C; A7CA; # LATIN CAPITAL LETTER S WITH SHORT STROKE OVERLAY
+A7D0; C; A7D1; # LATIN CAPITAL LETTER CLOSED INSULAR G
+A7D6; C; A7D7; # LATIN CAPITAL LETTER MIDDLE SCOTS S
+A7D8; C; A7D9; # LATIN CAPITAL LETTER SIGMOID S
A7F5; C; A7F6; # LATIN CAPITAL LETTER REVERSED HALF H
AB70; C; 13A0; # CHEROKEE SMALL LETTER A
AB71; C; 13A1; # CHEROKEE SMALL LETTER E
@@ -1431,6 +1436,41 @@ FF3A; C; FF5A; # FULLWIDTH LATIN CAPITAL LETTER Z
104D1; C; 104F9; # OSAGE CAPITAL LETTER GHA
104D2; C; 104FA; # OSAGE CAPITAL LETTER ZA
104D3; C; 104FB; # OSAGE CAPITAL LETTER ZHA
+10570; C; 10597; # VITHKUQI CAPITAL LETTER A
+10571; C; 10598; # VITHKUQI CAPITAL LETTER BBE
+10572; C; 10599; # VITHKUQI CAPITAL LETTER BE
+10573; C; 1059A; # VITHKUQI CAPITAL LETTER CE
+10574; C; 1059B; # VITHKUQI CAPITAL LETTER CHE
+10575; C; 1059C; # VITHKUQI CAPITAL LETTER DE
+10576; C; 1059D; # VITHKUQI CAPITAL LETTER DHE
+10577; C; 1059E; # VITHKUQI CAPITAL LETTER EI
+10578; C; 1059F; # VITHKUQI CAPITAL LETTER E
+10579; C; 105A0; # VITHKUQI CAPITAL LETTER FE
+1057A; C; 105A1; # VITHKUQI CAPITAL LETTER GA
+1057C; C; 105A3; # VITHKUQI CAPITAL LETTER HA
+1057D; C; 105A4; # VITHKUQI CAPITAL LETTER HHA
+1057E; C; 105A5; # VITHKUQI CAPITAL LETTER I
+1057F; C; 105A6; # VITHKUQI CAPITAL LETTER IJE
+10580; C; 105A7; # VITHKUQI CAPITAL LETTER JE
+10581; C; 105A8; # VITHKUQI CAPITAL LETTER KA
+10582; C; 105A9; # VITHKUQI CAPITAL LETTER LA
+10583; C; 105AA; # VITHKUQI CAPITAL LETTER LLA
+10584; C; 105AB; # VITHKUQI CAPITAL LETTER ME
+10585; C; 105AC; # VITHKUQI CAPITAL LETTER NE
+10586; C; 105AD; # VITHKUQI CAPITAL LETTER NJE
+10587; C; 105AE; # VITHKUQI CAPITAL LETTER O
+10588; C; 105AF; # VITHKUQI CAPITAL LETTER PE
+10589; C; 105B0; # VITHKUQI CAPITAL LETTER QA
+1058A; C; 105B1; # VITHKUQI CAPITAL LETTER RE
+1058C; C; 105B3; # VITHKUQI CAPITAL LETTER SE
+1058D; C; 105B4; # VITHKUQI CAPITAL LETTER SHE
+1058E; C; 105B5; # VITHKUQI CAPITAL LETTER TE
+1058F; C; 105B6; # VITHKUQI CAPITAL LETTER THE
+10590; C; 105B7; # VITHKUQI CAPITAL LETTER U
+10591; C; 105B8; # VITHKUQI CAPITAL LETTER VE
+10592; C; 105B9; # VITHKUQI CAPITAL LETTER XE
+10594; C; 105BB; # VITHKUQI CAPITAL LETTER Y
+10595; C; 105BC; # VITHKUQI CAPITAL LETTER ZE
10C80; C; 10CC0; # OLD HUNGARIAN CAPITAL LETTER A
10C81; C; 10CC1; # OLD HUNGARIAN CAPITAL LETTER AA
10C82; C; 10CC2; # OLD HUNGARIAN CAPITAL LETTER EB
diff --git a/unicode/Copyright.txt b/src/unicode/Copyright.txt
index bfae4154b6..bfae4154b6 100644
--- a/unicode/Copyright.txt
+++ b/src/unicode/Copyright.txt
diff --git a/unicode/EastAsianWidth.txt b/src/unicode/EastAsianWidth.txt
index b43aec9273..e04f705178 100644
--- a/unicode/EastAsianWidth.txt
+++ b/src/unicode/EastAsianWidth.txt
@@ -1,11 +1,11 @@
-# EastAsianWidth-13.0.0.txt
-# Date: 2029-01-21, 18:14:00 GMT [KW, LI]
-# © 2020 Unicode®, Inc.
+# EastAsianWidth-14.0.0.txt
+# Date: 2021-07-06, 09:58:53 GMT [KW, LI]
+# © 2021 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
-# For terms of use, see http://www.unicode.org/terms_of_use.html
+# For terms of use, see https://www.unicode.org/terms_of_use.html
#
# Unicode Character Database
-# For documentation, see http://www.unicode.org/reports/tr44/
+# For documentation, see https://www.unicode.org/reports/tr44/
#
# East_Asian_Width Property
#
@@ -37,7 +37,7 @@
# with ranges of code points, the code point count in square brackets.
#
# For more information, see UAX #11: East Asian Width,
-# at http://www.unicode.org/reports/tr11/
+# at https://www.unicode.org/reports/tr11/
#
# @missing: 0000..10FFFF; N
0000..001F;N # Cc [32] <control-0000>..<control-001F>
@@ -273,7 +273,7 @@
0610..061A;N # Mn [11] ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM..ARABIC SMALL KASRA
061B;N # Po ARABIC SEMICOLON
061C;N # Cf ARABIC LETTER MARK
-061E..061F;N # Po [2] ARABIC TRIPLE DOT PUNCTUATION MARK..ARABIC QUESTION MARK
+061D..061F;N # Po [3] ARABIC END OF TEXT MARK..ARABIC QUESTION MARK
0620..063F;N # Lo [32] ARABIC LETTER KASHMIRI YEH..ARABIC LETTER FARSI YEH WITH THREE DOTS ABOVE
0640;N # Lm ARABIC TATWEEL
0641..064A;N # Lo [10] ARABIC LETTER FEH..ARABIC LETTER YEH
@@ -331,9 +331,14 @@
0859..085B;N # Mn [3] MANDAIC AFFRICATION MARK..MANDAIC GEMINATION MARK
085E;N # Po MANDAIC PUNCTUATION
0860..086A;N # Lo [11] SYRIAC LETTER MALAYALAM NGA..SYRIAC LETTER MALAYALAM SSA
-08A0..08B4;N # Lo [21] ARABIC LETTER BEH WITH SMALL V BELOW..ARABIC LETTER KAF WITH DOT BELOW
-08B6..08C7;N # Lo [18] ARABIC LETTER BEH WITH SMALL MEEM ABOVE..ARABIC LETTER LAM WITH SMALL ARABIC LETTER TAH ABOVE
-08D3..08E1;N # Mn [15] ARABIC SMALL LOW WAW..ARABIC SMALL HIGH SIGN SAFHA
+0870..0887;N # Lo [24] ARABIC LETTER ALEF WITH ATTACHED FATHA..ARABIC BASELINE ROUND DOT
+0888;N # Sk ARABIC RAISED ROUND DOT
+0889..088E;N # Lo [6] ARABIC LETTER NOON WITH INVERTED SMALL V..ARABIC VERTICAL TAIL
+0890..0891;N # Cf [2] ARABIC POUND MARK ABOVE..ARABIC PIASTRE MARK ABOVE
+0898..089F;N # Mn [8] ARABIC SMALL HIGH WORD AL-JUZ..ARABIC HALF MADDA OVER MADDA
+08A0..08C8;N # Lo [41] ARABIC LETTER BEH WITH SMALL V BELOW..ARABIC LETTER GRAF
+08C9;N # Lm ARABIC SMALL FARSI YEH
+08CA..08E1;N # Mn [24] ARABIC SMALL HIGH FARSI YEH..ARABIC SMALL HIGH SIGN SAFHA
08E2;N # Cf ARABIC DISPUTED END OF AYAH
08E3..08FF;N # Mn [29] ARABIC TURNED DAMMA BELOW..ARABIC MARK SIDEWAYS NOON GHUNNA
0900..0902;N # Mn [3] DEVANAGARI SIGN INVERTED CANDRABINDU..DEVANAGARI SIGN ANUSVARA
@@ -490,6 +495,7 @@
0C0E..0C10;N # Lo [3] TELUGU LETTER E..TELUGU LETTER AI
0C12..0C28;N # Lo [23] TELUGU LETTER O..TELUGU LETTER NA
0C2A..0C39;N # Lo [16] TELUGU LETTER PA..TELUGU LETTER HA
+0C3C;N # Mn TELUGU SIGN NUKTA
0C3D;N # Lo TELUGU SIGN AVAGRAHA
0C3E..0C40;N # Mn [3] TELUGU VOWEL SIGN AA..TELUGU VOWEL SIGN II
0C41..0C44;N # Mc [4] TELUGU VOWEL SIGN U..TELUGU VOWEL SIGN VOCALIC RR
@@ -497,6 +503,7 @@
0C4A..0C4D;N # Mn [4] TELUGU VOWEL SIGN O..TELUGU SIGN VIRAMA
0C55..0C56;N # Mn [2] TELUGU LENGTH MARK..TELUGU AI LENGTH MARK
0C58..0C5A;N # Lo [3] TELUGU LETTER TSA..TELUGU LETTER RRRA
+0C5D;N # Lo TELUGU LETTER NAKAARA POLLU
0C60..0C61;N # Lo [2] TELUGU LETTER VOCALIC RR..TELUGU LETTER VOCALIC LL
0C62..0C63;N # Mn [2] TELUGU VOWEL SIGN VOCALIC L..TELUGU VOWEL SIGN VOCALIC LL
0C66..0C6F;N # Nd [10] TELUGU DIGIT ZERO..TELUGU DIGIT NINE
@@ -522,7 +529,7 @@
0CCA..0CCB;N # Mc [2] KANNADA VOWEL SIGN O..KANNADA VOWEL SIGN OO
0CCC..0CCD;N # Mn [2] KANNADA VOWEL SIGN AU..KANNADA SIGN VIRAMA
0CD5..0CD6;N # Mc [2] KANNADA LENGTH MARK..KANNADA AI LENGTH MARK
-0CDE;N # Lo KANNADA LETTER FA
+0CDD..0CDE;N # Lo [2] KANNADA LETTER NAKAARA POLLU..KANNADA LETTER FA
0CE0..0CE1;N # Lo [2] KANNADA LETTER VOCALIC RR..KANNADA LETTER VOCALIC LL
0CE2..0CE3;N # Mn [2] KANNADA VOWEL SIGN VOCALIC L..KANNADA VOWEL SIGN VOCALIC LL
0CE6..0CEF;N # Nd [10] KANNADA DIGIT ZERO..KANNADA DIGIT NINE
@@ -709,11 +716,13 @@
16EB..16ED;N # Po [3] RUNIC SINGLE PUNCTUATION..RUNIC CROSS PUNCTUATION
16EE..16F0;N # Nl [3] RUNIC ARLAUG SYMBOL..RUNIC BELGTHOR SYMBOL
16F1..16F8;N # Lo [8] RUNIC LETTER K..RUNIC LETTER FRANKS CASKET AESC
-1700..170C;N # Lo [13] TAGALOG LETTER A..TAGALOG LETTER YA
-170E..1711;N # Lo [4] TAGALOG LETTER LA..TAGALOG LETTER HA
+1700..1711;N # Lo [18] TAGALOG LETTER A..TAGALOG LETTER HA
1712..1714;N # Mn [3] TAGALOG VOWEL SIGN I..TAGALOG SIGN VIRAMA
+1715;N # Mc TAGALOG SIGN PAMUDPOD
+171F;N # Lo TAGALOG LETTER ARCHAIC RA
1720..1731;N # Lo [18] HANUNOO LETTER A..HANUNOO LETTER HA
-1732..1734;N # Mn [3] HANUNOO VOWEL SIGN I..HANUNOO SIGN PAMUDPOD
+1732..1733;N # Mn [2] HANUNOO VOWEL SIGN I..HANUNOO VOWEL SIGN U
+1734;N # Mc HANUNOO SIGN PAMUDPOD
1735..1736;N # Po [2] PHILIPPINE SINGLE PUNCTUATION..PHILIPPINE DOUBLE PUNCTUATION
1740..1751;N # Lo [18] BUHID LETTER A..BUHID LETTER HA
1752..1753;N # Mn [2] BUHID VOWEL SIGN I..BUHID VOWEL SIGN U
@@ -741,6 +750,7 @@
1807..180A;N # Po [4] MONGOLIAN SIBE SYLLABLE BOUNDARY MARKER..MONGOLIAN NIRUGU
180B..180D;N # Mn [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE
180E;N # Cf MONGOLIAN VOWEL SEPARATOR
+180F;N # Mn MONGOLIAN FREE VARIATION SELECTOR FOUR
1810..1819;N # Nd [10] MONGOLIAN DIGIT ZERO..MONGOLIAN DIGIT NINE
1820..1842;N # Lo [35] MONGOLIAN LETTER A..MONGOLIAN LETTER CHI
1843;N # Lm MONGOLIAN LETTER TODO LONG VOWEL SIGN
@@ -796,7 +806,7 @@
1AA8..1AAD;N # Po [6] TAI THAM SIGN KAAN..TAI THAM SIGN CAANG
1AB0..1ABD;N # Mn [14] COMBINING DOUBLED CIRCUMFLEX ACCENT..COMBINING PARENTHESES BELOW
1ABE;N # Me COMBINING PARENTHESES OVERLAY
-1ABF..1AC0;N # Mn [2] COMBINING LATIN SMALL LETTER W BELOW..COMBINING LATIN SMALL LETTER TURNED W BELOW
+1ABF..1ACE;N # Mn [16] COMBINING LATIN SMALL LETTER W BELOW..COMBINING LATIN SMALL LETTER INSULAR T
1B00..1B03;N # Mn [4] BALINESE SIGN ULU RICEM..BALINESE SIGN SURANG
1B04;N # Mc BALINESE SIGN BISAH
1B05..1B33;N # Lo [47] BALINESE LETTER AKARA..BALINESE LETTER HA
@@ -808,12 +818,13 @@
1B3D..1B41;N # Mc [5] BALINESE VOWEL SIGN LA LENGA TEDUNG..BALINESE VOWEL SIGN TALING REPA TEDUNG
1B42;N # Mn BALINESE VOWEL SIGN PEPET
1B43..1B44;N # Mc [2] BALINESE VOWEL SIGN PEPET TEDUNG..BALINESE ADEG ADEG
-1B45..1B4B;N # Lo [7] BALINESE LETTER KAF SASAK..BALINESE LETTER ASYURA SASAK
+1B45..1B4C;N # Lo [8] BALINESE LETTER KAF SASAK..BALINESE LETTER ARCHAIC JNYA
1B50..1B59;N # Nd [10] BALINESE DIGIT ZERO..BALINESE DIGIT NINE
1B5A..1B60;N # Po [7] BALINESE PANTI..BALINESE PAMENENG
1B61..1B6A;N # So [10] BALINESE MUSICAL SYMBOL DONG..BALINESE MUSICAL SYMBOL DANG GEDE
1B6B..1B73;N # Mn [9] BALINESE MUSICAL SYMBOL COMBINING TEGEH..BALINESE MUSICAL SYMBOL COMBINING GONG
1B74..1B7C;N # So [9] BALINESE MUSICAL SYMBOL RIGHT-HAND OPEN DUG..BALINESE MUSICAL SYMBOL LEFT-HAND OPEN PING
+1B7D..1B7E;N # Po [2] BALINESE PANTI LANTANG..BALINESE PAMADA LANTANG
1B80..1B81;N # Mn [2] SUNDANESE SIGN PANYECEK..SUNDANESE SIGN PANGLAYAR
1B82;N # Mc SUNDANESE SIGN PANGWISAD
1B83..1BA0;N # Lo [30] SUNDANESE LETTER A..SUNDANESE LETTER HA
@@ -872,8 +883,7 @@
1D79..1D7F;N # Ll [7] LATIN SMALL LETTER INSULAR G..LATIN SMALL LETTER UPSILON WITH STROKE
1D80..1D9A;N # Ll [27] LATIN SMALL LETTER B WITH PALATAL HOOK..LATIN SMALL LETTER EZH WITH RETROFLEX HOOK
1D9B..1DBF;N # Lm [37] MODIFIER LETTER SMALL TURNED ALPHA..MODIFIER LETTER SMALL THETA
-1DC0..1DF9;N # Mn [58] COMBINING DOTTED GRAVE ACCENT..COMBINING WIDE INVERTED BRIDGE BELOW
-1DFB..1DFF;N # Mn [5] COMBINING DELETION MARK..COMBINING RIGHT ARROWHEAD AND DOWN ARROWHEAD BELOW
+1DC0..1DFF;N # Mn [64] COMBINING DOTTED GRAVE ACCENT..COMBINING RIGHT ARROWHEAD AND DOWN ARROWHEAD BELOW
1E00..1EFF;N # L& [256] LATIN CAPITAL LETTER A WITH RING BELOW..LATIN SMALL LETTER Y WITH LOOP
1F00..1F15;N # L& [22] GREEK SMALL LETTER ALPHA WITH PSILI..GREEK SMALL LETTER EPSILON WITH DASIA AND OXIA
1F18..1F1D;N # Lu [6] GREEK CAPITAL LETTER EPSILON WITH PSILI..GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA
@@ -965,7 +975,7 @@
20A9;H # Sc WON SIGN
20AA..20AB;N # Sc [2] NEW SHEQEL SIGN..DONG SIGN
20AC;A # Sc EURO SIGN
-20AD..20BF;N # Sc [19] KIP SIGN..BITCOIN SIGN
+20AD..20C0;N # Sc [20] KIP SIGN..SOM SIGN
20D0..20DC;N # Mn [13] COMBINING LEFT HARPOON ABOVE..COMBINING FOUR DOTS ABOVE
20DD..20E0;N # Me [4] COMBINING ENCLOSING CIRCLE..COMBINING ENCLOSING CIRCLE BACKSLASH
20E1;N # Mn COMBINING LEFT RIGHT ARROW ABOVE
@@ -1338,8 +1348,7 @@
2B5A..2B73;N # So [26] SLANTED NORTH ARROW WITH HOOKED HEAD..DOWNWARDS TRIANGLE-HEADED ARROW TO BAR
2B76..2B95;N # So [32] NORTH WEST TRIANGLE-HEADED ARROW TO BAR..RIGHTWARDS BLACK ARROW
2B97..2BFF;N # So [105] SYMBOL FOR TYPE A ELECTRONICS..HELLSCHREIBER PAUSE SYMBOL
-2C00..2C2E;N # Lu [47] GLAGOLITIC CAPITAL LETTER AZU..GLAGOLITIC CAPITAL LETTER LATINATE MYSLITE
-2C30..2C5E;N # Ll [47] GLAGOLITIC SMALL LETTER AZU..GLAGOLITIC SMALL LETTER LATINATE MYSLITE
+2C00..2C5F;N # L& [96] GLAGOLITIC CAPITAL LETTER AZU..GLAGOLITIC SMALL LETTER CAUDATE CHRIVI
2C60..2C7B;N # L& [28] LATIN CAPITAL LETTER L WITH DOUBLE BAR..LATIN LETTER SMALL CAPITAL TURNED E
2C7C..2C7D;N # Lm [2] LATIN SUBSCRIPT SMALL LETTER J..MODIFIER LETTER CAPITAL V
2C7E..2C7F;N # Lu [2] LATIN CAPITAL LETTER S WITH SWASH TAIL..LATIN CAPITAL LETTER Z WITH SWASH TAIL
@@ -1407,7 +1416,16 @@
2E42;N # Ps DOUBLE LOW-REVERSED-9 QUOTATION MARK
2E43..2E4F;N # Po [13] DASH WITH LEFT UPTURN..CORNISH VERSE DIVIDER
2E50..2E51;N # So [2] CROSS PATTY WITH RIGHT CROSSBAR..CROSS PATTY WITH LEFT CROSSBAR
-2E52;N # Po TIRONIAN SIGN CAPITAL ET
+2E52..2E54;N # Po [3] TIRONIAN SIGN CAPITAL ET..MEDIEVAL QUESTION MARK
+2E55;N # Ps LEFT SQUARE BRACKET WITH STROKE
+2E56;N # Pe RIGHT SQUARE BRACKET WITH STROKE
+2E57;N # Ps LEFT SQUARE BRACKET WITH DOUBLE STROKE
+2E58;N # Pe RIGHT SQUARE BRACKET WITH DOUBLE STROKE
+2E59;N # Ps TOP HALF LEFT PARENTHESIS
+2E5A;N # Pe TOP HALF RIGHT PARENTHESIS
+2E5B;N # Ps BOTTOM HALF LEFT PARENTHESIS
+2E5C;N # Pe BOTTOM HALF RIGHT PARENTHESIS
+2E5D;N # Pd OBLIQUE HYPHEN
2E80..2E99;W # So [26] CJK RADICAL REPEAT..CJK RADICAL RAP
2E9B..2EF3;W # So [89] CJK RADICAL CHOKE..CJK RADICAL C-SIMPLIFIED TURTLE
2F00..2FD5;W # So [214] KANGXI RADICAL ONE..KANGXI RADICAL FLUTE
@@ -1485,8 +1503,7 @@
3300..33FF;W # So [256] SQUARE APAATO..SQUARE GAL
3400..4DBF;W # Lo [6592] CJK UNIFIED IDEOGRAPH-3400..CJK UNIFIED IDEOGRAPH-4DBF
4DC0..4DFF;N # So [64] HEXAGRAM FOR THE CREATIVE HEAVEN..HEXAGRAM FOR BEFORE COMPLETION
-4E00..9FFC;W # Lo [20989] CJK UNIFIED IDEOGRAPH-4E00..CJK UNIFIED IDEOGRAPH-9FFC
-9FFD..9FFF;W # Cn [3] <reserved-9FFD>..<reserved-9FFF>
+4E00..9FFF;W # Lo [20992] CJK UNIFIED IDEOGRAPH-4E00..CJK UNIFIED IDEOGRAPH-9FFF
A000..A014;W # Lo [21] YI SYLLABLE IT..YI SYLLABLE E
A015;W # Lm YI SYLLABLE WU
A016..A48C;W # Lo [1143] YI SYLLABLE BIT..YI SYLLABLE YYR
@@ -1525,8 +1542,11 @@ A788;N # Lm MODIFIER LETTER LOW CIRCUMFLEX ACCENT
A789..A78A;N # Sk [2] MODIFIER LETTER COLON..MODIFIER LETTER SHORT EQUALS SIGN
A78B..A78E;N # L& [4] LATIN CAPITAL LETTER SALTILLO..LATIN SMALL LETTER L WITH RETROFLEX HOOK AND BELT
A78F;N # Lo LATIN LETTER SINOLOGICAL DOT
-A790..A7BF;N # L& [48] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN SMALL LETTER GLOTTAL U
-A7C2..A7CA;N # L& [9] LATIN CAPITAL LETTER ANGLICANA W..LATIN SMALL LETTER S WITH SHORT STROKE OVERLAY
+A790..A7CA;N # L& [59] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN SMALL LETTER S WITH SHORT STROKE OVERLAY
+A7D0..A7D1;N # L& [2] LATIN CAPITAL LETTER CLOSED INSULAR G..LATIN SMALL LETTER CLOSED INSULAR G
+A7D3;N # Ll LATIN SMALL LETTER DOUBLE THORN
+A7D5..A7D9;N # L& [5] LATIN SMALL LETTER DOUBLE WYNN..LATIN SMALL LETTER SIGMOID S
+A7F2..A7F4;N # Lm [3] MODIFIER LETTER CAPITAL C..MODIFIER LETTER CAPITAL Q
A7F5..A7F6;N # L& [2] LATIN CAPITAL LETTER REVERSED HALF H..LATIN SMALL LETTER REVERSED HALF H
A7F7;N # Lo LATIN EPIGRAPHIC LETTER SIDEWAYS I
A7F8..A7F9;N # Lm [2] MODIFIER LETTER CAPITAL H WITH STROKE..MODIFIER LETTER SMALL LIGATURE OE
@@ -1682,15 +1702,17 @@ FB40..FB41;N # Lo [2] HEBREW LETTER NUN WITH DAGESH..HEBREW LETTER SAMEK
FB43..FB44;N # Lo [2] HEBREW LETTER FINAL PE WITH DAGESH..HEBREW LETTER PE WITH DAGESH
FB46..FB4F;N # Lo [10] HEBREW LETTER TSADI WITH DAGESH..HEBREW LIGATURE ALEF LAMED
FB50..FBB1;N # Lo [98] ARABIC LETTER ALEF WASLA ISOLATED FORM..ARABIC LETTER YEH BARREE WITH HAMZA ABOVE FINAL FORM
-FBB2..FBC1;N # Sk [16] ARABIC SYMBOL DOT ABOVE..ARABIC SYMBOL SMALL TAH BELOW
+FBB2..FBC2;N # Sk [17] ARABIC SYMBOL DOT ABOVE..ARABIC SYMBOL WASLA ABOVE
FBD3..FD3D;N # Lo [363] ARABIC LETTER NG ISOLATED FORM..ARABIC LIGATURE ALEF WITH FATHATAN ISOLATED FORM
FD3E;N # Pe ORNATE LEFT PARENTHESIS
FD3F;N # Ps ORNATE RIGHT PARENTHESIS
+FD40..FD4F;N # So [16] ARABIC LIGATURE RAHIMAHU ALLAAH..ARABIC LIGATURE RAHIMAHUM ALLAAH
FD50..FD8F;N # Lo [64] ARABIC LIGATURE TEH WITH JEEM WITH MEEM INITIAL FORM..ARABIC LIGATURE MEEM WITH KHAH WITH MEEM INITIAL FORM
FD92..FDC7;N # Lo [54] ARABIC LIGATURE MEEM WITH JEEM WITH KHAH INITIAL FORM..ARABIC LIGATURE NOON WITH JEEM WITH YEH FINAL FORM
+FDCF;N # So ARABIC LIGATURE SALAAMUHU ALAYNAA
FDF0..FDFB;N # Lo [12] ARABIC LIGATURE SALLA USED AS KORANIC STOP SIGN ISOLATED FORM..ARABIC LIGATURE JALLAJALALOUHOU
FDFC;N # Sc RIAL SIGN
-FDFD;N # So ARABIC LIGATURE BISMILLAH AR-RAHMAN AR-RAHEEM
+FDFD..FDFF;N # So [3] ARABIC LIGATURE BISMILLAH AR-RAHMAN AR-RAHEEM..ARABIC LIGATURE AZZA WA JALL
FE00..FE0F;A # Mn [16] VARIATION SELECTOR-1..VARIATION SELECTOR-16
FE10..FE16;W # Po [7] PRESENTATION FORM FOR VERTICAL COMMA..PRESENTATION FORM FOR VERTICAL QUESTION MARK
FE17;W # Ps PRESENTATION FORM FOR VERTICAL LEFT WHITE LENTICULAR BRACKET
@@ -1839,9 +1861,20 @@ FFFD;A # So REPLACEMENT CHARACTER
10500..10527;N # Lo [40] ELBASAN LETTER A..ELBASAN LETTER KHE
10530..10563;N # Lo [52] CAUCASIAN ALBANIAN LETTER ALT..CAUCASIAN ALBANIAN LETTER KIW
1056F;N # Po CAUCASIAN ALBANIAN CITATION MARK
+10570..1057A;N # Lu [11] VITHKUQI CAPITAL LETTER A..VITHKUQI CAPITAL LETTER GA
+1057C..1058A;N # Lu [15] VITHKUQI CAPITAL LETTER HA..VITHKUQI CAPITAL LETTER RE
+1058C..10592;N # Lu [7] VITHKUQI CAPITAL LETTER SE..VITHKUQI CAPITAL LETTER XE
+10594..10595;N # Lu [2] VITHKUQI CAPITAL LETTER Y..VITHKUQI CAPITAL LETTER ZE
+10597..105A1;N # Ll [11] VITHKUQI SMALL LETTER A..VITHKUQI SMALL LETTER GA
+105A3..105B1;N # Ll [15] VITHKUQI SMALL LETTER HA..VITHKUQI SMALL LETTER RE
+105B3..105B9;N # Ll [7] VITHKUQI SMALL LETTER SE..VITHKUQI SMALL LETTER XE
+105BB..105BC;N # Ll [2] VITHKUQI SMALL LETTER Y..VITHKUQI SMALL LETTER ZE
10600..10736;N # Lo [311] LINEAR A SIGN AB001..LINEAR A SIGN A664
10740..10755;N # Lo [22] LINEAR A SIGN A701 A..LINEAR A SIGN A732 JE
10760..10767;N # Lo [8] LINEAR A SIGN A800..LINEAR A SIGN A807
+10780..10785;N # Lm [6] MODIFIER LETTER SMALL CAPITAL AA..MODIFIER LETTER SMALL B WITH HOOK
+10787..107B0;N # Lm [42] MODIFIER LETTER SMALL DZ DIGRAPH..MODIFIER LETTER SMALL V WITH RIGHT HOOK
+107B2..107BA;N # Lm [9] MODIFIER LETTER SMALL CAPITAL Y..MODIFIER LETTER SMALL S WITH CURL
10800..10805;N # Lo [6] CYPRIOT SYLLABLE A..CYPRIOT SYLLABLE JA
10808;N # Lo CYPRIOT SYLLABLE JO
1080A..10835;N # Lo [44] CYPRIOT SYLLABLE KA..CYPRIOT SYLLABLE WO
@@ -1920,6 +1953,9 @@ FFFD;A # So REPLACEMENT CHARACTER
10F46..10F50;N # Mn [11] SOGDIAN COMBINING DOT BELOW..SOGDIAN COMBINING STROKE BELOW
10F51..10F54;N # No [4] SOGDIAN NUMBER ONE..SOGDIAN NUMBER ONE HUNDRED
10F55..10F59;N # Po [5] SOGDIAN PUNCTUATION TWO VERTICAL BARS..SOGDIAN PUNCTUATION HALF CIRCLE WITH DOT
+10F70..10F81;N # Lo [18] OLD UYGHUR LETTER ALEPH..OLD UYGHUR LETTER LESH
+10F82..10F85;N # Mn [4] OLD UYGHUR COMBINING DOT ABOVE..OLD UYGHUR COMBINING TWO DOTS BELOW
+10F86..10F89;N # Po [4] OLD UYGHUR PUNCTUATION BAR..OLD UYGHUR PUNCTUATION FOUR DOTS
10FB0..10FC4;N # Lo [21] CHORASMIAN LETTER ALEPH..CHORASMIAN LETTER TAW
10FC5..10FCB;N # No [7] CHORASMIAN NUMBER ONE..CHORASMIAN NUMBER ONE HUNDRED
10FE0..10FF6;N # Lo [23] ELYMAIC LETTER ALEPH..ELYMAIC LIGATURE ZAYIN-YODH
@@ -1931,6 +1967,10 @@ FFFD;A # So REPLACEMENT CHARACTER
11047..1104D;N # Po [7] BRAHMI DANDA..BRAHMI PUNCTUATION LOTUS
11052..11065;N # No [20] BRAHMI NUMBER ONE..BRAHMI NUMBER ONE THOUSAND
11066..1106F;N # Nd [10] BRAHMI DIGIT ZERO..BRAHMI DIGIT NINE
+11070;N # Mn BRAHMI SIGN OLD TAMIL VIRAMA
+11071..11072;N # Lo [2] BRAHMI LETTER OLD TAMIL SHORT E..BRAHMI LETTER OLD TAMIL SHORT O
+11073..11074;N # Mn [2] BRAHMI VOWEL SIGN OLD TAMIL SHORT E..BRAHMI VOWEL SIGN OLD TAMIL SHORT O
+11075;N # Lo BRAHMI LETTER OLD TAMIL LLA
1107F;N # Mn BRAHMI NUMBER JOINER
11080..11081;N # Mn [2] KAITHI SIGN CANDRABINDU..KAITHI SIGN ANUSVARA
11082;N # Mc KAITHI SIGN VISARGA
@@ -1942,6 +1982,7 @@ FFFD;A # So REPLACEMENT CHARACTER
110BB..110BC;N # Po [2] KAITHI ABBREVIATION SIGN..KAITHI ENUMERATION SIGN
110BD;N # Cf KAITHI NUMBER SIGN
110BE..110C1;N # Po [4] KAITHI SECTION MARK..KAITHI DOUBLE DANDA
+110C2;N # Mn KAITHI VOWEL SIGN VOCALIC R
110CD;N # Cf KAITHI NUMBER SIGN ABOVE
110D0..110E8;N # Lo [25] SORA SOMPENG LETTER SAH..SORA SOMPENG LETTER MAE
110F0..110F9;N # Nd [10] SORA SOMPENG DIGIT ZERO..SORA SOMPENG DIGIT NINE
@@ -2076,6 +2117,7 @@ FFFD;A # So REPLACEMENT CHARACTER
116B6;N # Mc TAKRI SIGN VIRAMA
116B7;N # Mn TAKRI SIGN NUKTA
116B8;N # Lo TAKRI LETTER ARCHAIC KHA
+116B9;N # Po TAKRI ABBREVIATION SIGN
116C0..116C9;N # Nd [10] TAKRI DIGIT ZERO..TAKRI DIGIT NINE
11700..1171A;N # Lo [27] AHOM LETTER KA..AHOM LETTER ALTERNATE BA
1171D..1171F;N # Mn [3] AHOM CONSONANT SIGN MEDIAL LA..AHOM CONSONANT SIGN MEDIAL LIGATING RA
@@ -2087,6 +2129,7 @@ FFFD;A # So REPLACEMENT CHARACTER
1173A..1173B;N # No [2] AHOM NUMBER TEN..AHOM NUMBER TWENTY
1173C..1173E;N # Po [3] AHOM SIGN SMALL SECTION..AHOM SIGN RULAI
1173F;N # So AHOM SYMBOL VI
+11740..11746;N # Lo [7] AHOM LETTER CA..AHOM LETTER LLA
11800..1182B;N # Lo [44] DOGRA LETTER A..DOGRA LETTER RRA
1182C..1182E;N # Mc [3] DOGRA VOWEL SIGN AA..DOGRA VOWEL SIGN II
1182F..11837;N # Mn [9] DOGRA VOWEL SIGN U..DOGRA SIGN ANUSVARA
@@ -2145,6 +2188,7 @@ FFFD;A # So REPLACEMENT CHARACTER
11A9A..11A9C;N # Po [3] SOYOMBO MARK TSHEG..SOYOMBO MARK DOUBLE SHAD
11A9D;N # Lo SOYOMBO MARK PLUTA
11A9E..11AA2;N # Po [5] SOYOMBO HEAD MARK WITH MOON AND SUN AND TRIPLE FLAME..SOYOMBO TERMINAL MARK-2
+11AB0..11ABF;N # Lo [16] CANADIAN SYLLABICS NATTILIK HI..CANADIAN SYLLABICS SPA
11AC0..11AF8;N # Lo [57] PAU CIN HAU LETTER PA..PAU CIN HAU GLOTTAL STOP FINAL
11C00..11C08;N # Lo [9] BHAIKSUKI LETTER A..BHAIKSUKI LETTER VOCALIC L
11C0A..11C2E;N # Lo [37] BHAIKSUKI LETTER E..BHAIKSUKI LETTER HA
@@ -2201,6 +2245,8 @@ FFFD;A # So REPLACEMENT CHARACTER
12400..1246E;N # Nl [111] CUNEIFORM NUMERIC SIGN TWO ASH..CUNEIFORM NUMERIC SIGN NINE U VARIANT FORM
12470..12474;N # Po [5] CUNEIFORM PUNCTUATION SIGN OLD ASSYRIAN WORD DIVIDER..CUNEIFORM PUNCTUATION SIGN DIAGONAL QUADCOLON
12480..12543;N # Lo [196] CUNEIFORM SIGN AB TIMES NUN TENU..CUNEIFORM SIGN ZU5 TIMES THREE DISH TENU
+12F90..12FF0;N # Lo [97] CYPRO-MINOAN SIGN CM001..CYPRO-MINOAN SIGN CM114
+12FF1..12FF2;N # Po [2] CYPRO-MINOAN SIGN CM301..CYPRO-MINOAN SIGN CM302
13000..1342E;N # Lo [1071] EGYPTIAN HIEROGLYPH A001..EGYPTIAN HIEROGLYPH AA032
13430..13438;N # Cf [9] EGYPTIAN HIEROGLYPH VERTICAL JOINER..EGYPTIAN HIEROGLYPH END SEGMENT
14400..14646;N # Lo [583] ANATOLIAN HIEROGLYPH A001..ANATOLIAN HIEROGLYPH A530
@@ -2208,6 +2254,8 @@ FFFD;A # So REPLACEMENT CHARACTER
16A40..16A5E;N # Lo [31] MRO LETTER TA..MRO LETTER TEK
16A60..16A69;N # Nd [10] MRO DIGIT ZERO..MRO DIGIT NINE
16A6E..16A6F;N # Po [2] MRO DANDA..MRO DOUBLE DANDA
+16A70..16ABE;N # Lo [79] TANGSA LETTER OZ..TANGSA LETTER ZA
+16AC0..16AC9;N # Nd [10] TANGSA DIGIT ZERO..TANGSA DIGIT NINE
16AD0..16AED;N # Lo [30] BASSA VAH LETTER ENNI..BASSA VAH LETTER I
16AF0..16AF4;N # Mn [5] BASSA VAH COMBINING HIGH TONE..BASSA VAH COMBINING HIGH-LOW TONE
16AF5;N # Po BASSA VAH FULL STOP
@@ -2240,8 +2288,11 @@ FFFD;A # So REPLACEMENT CHARACTER
18800..18AFF;W # Lo [768] TANGUT COMPONENT-001..TANGUT COMPONENT-768
18B00..18CD5;W # Lo [470] KHITAN SMALL SCRIPT CHARACTER-18B00..KHITAN SMALL SCRIPT CHARACTER-18CD5
18D00..18D08;W # Lo [9] TANGUT IDEOGRAPH-18D00..TANGUT IDEOGRAPH-18D08
+1AFF0..1AFF3;W # Lm [4] KATAKANA LETTER MINNAN TONE-2..KATAKANA LETTER MINNAN TONE-5
+1AFF5..1AFFB;W # Lm [7] KATAKANA LETTER MINNAN TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-5
+1AFFD..1AFFE;W # Lm [2] KATAKANA LETTER MINNAN NASALIZED TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-8
1B000..1B0FF;W # Lo [256] KATAKANA LETTER ARCHAIC E..HENTAIGANA LETTER RE-2
-1B100..1B11E;W # Lo [31] HENTAIGANA LETTER RE-3..HENTAIGANA LETTER N-MU-MO-2
+1B100..1B122;W # Lo [35] HENTAIGANA LETTER RE-3..KATAKANA LETTER ARCHAIC WU
1B150..1B152;W # Lo [3] HIRAGANA LETTER SMALL WI..HIRAGANA LETTER SMALL WO
1B164..1B167;W # Lo [4] KATAKANA LETTER SMALL WI..KATAKANA LETTER SMALL N
1B170..1B2FB;W # Lo [396] NUSHU CHARACTER-1B170..NUSHU CHARACTER-1B2FB
@@ -2253,6 +2304,9 @@ FFFD;A # So REPLACEMENT CHARACTER
1BC9D..1BC9E;N # Mn [2] DUPLOYAN THICK LETTER SELECTOR..DUPLOYAN DOUBLE MARK
1BC9F;N # Po DUPLOYAN PUNCTUATION CHINOOK FULL STOP
1BCA0..1BCA3;N # Cf [4] SHORTHAND FORMAT LETTER OVERLAP..SHORTHAND FORMAT UP STEP
+1CF00..1CF2D;N # Mn [46] ZNAMENNY COMBINING MARK GORAZDO NIZKO S KRYZHEM ON LEFT..ZNAMENNY COMBINING MARK KRYZH ON LEFT
+1CF30..1CF46;N # Mn [23] ZNAMENNY COMBINING TONAL RANGE MARK MRACHNO..ZNAMENNY PRIZNAK MODIFIER ROG
+1CF50..1CFC3;N # So [116] ZNAMENNY NEUME KRYUK..ZNAMENNY NEUME PAUK
1D000..1D0F5;N # So [246] BYZANTINE MUSICAL SYMBOL PSILI..BYZANTINE MUSICAL SYMBOL GORGON NEO KATO
1D100..1D126;N # So [39] MUSICAL SYMBOL SINGLE BARLINE..MUSICAL SYMBOL DRUM CLEF-2
1D129..1D164;N # So [60] MUSICAL SYMBOL MULTIPLE MEASURE REST..MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH NOTE
@@ -2266,7 +2320,7 @@ FFFD;A # So REPLACEMENT CHARACTER
1D185..1D18B;N # Mn [7] MUSICAL SYMBOL COMBINING DOIT..MUSICAL SYMBOL COMBINING TRIPLE TONGUE
1D18C..1D1A9;N # So [30] MUSICAL SYMBOL RINFORZANDO..MUSICAL SYMBOL DEGREE SLASH
1D1AA..1D1AD;N # Mn [4] MUSICAL SYMBOL COMBINING DOWN BOW..MUSICAL SYMBOL COMBINING SNAP PIZZICATO
-1D1AE..1D1E8;N # So [59] MUSICAL SYMBOL PEDAL MARK..MUSICAL SYMBOL KIEVAN FLAT SIGN
+1D1AE..1D1EA;N # So [61] MUSICAL SYMBOL PEDAL MARK..MUSICAL SYMBOL KORON
1D200..1D241;N # So [66] GREEK VOCAL NOTATION SYMBOL-1..GREEK INSTRUMENTAL NOTATION SYMBOL-54
1D242..1D244;N # Mn [3] COMBINING GREEK MUSICAL TRISEME..COMBINING GREEK MUSICAL PENTASEME
1D245;N # So GREEK MUSICAL LEIMMA
@@ -2326,6 +2380,9 @@ FFFD;A # So REPLACEMENT CHARACTER
1DA87..1DA8B;N # Po [5] SIGNWRITING COMMA..SIGNWRITING PARENTHESIS
1DA9B..1DA9F;N # Mn [5] SIGNWRITING FILL MODIFIER-2..SIGNWRITING FILL MODIFIER-6
1DAA1..1DAAF;N # Mn [15] SIGNWRITING ROTATION MODIFIER-2..SIGNWRITING ROTATION MODIFIER-16
+1DF00..1DF09;N # Ll [10] LATIN SMALL LETTER FENG DIGRAPH WITH TRILL..LATIN SMALL LETTER T WITH HOOK AND RETROFLEX HOOK
+1DF0A;N # Lo LATIN LETTER RETROFLEX CLICK WITH RETROFLEX HOOK
+1DF0B..1DF1E;N # Ll [20] LATIN SMALL LETTER ESH WITH DOUBLE BAR..LATIN SMALL LETTER S WITH CURL
1E000..1E006;N # Mn [7] COMBINING GLAGOLITIC LETTER AZU..COMBINING GLAGOLITIC LETTER ZHIVETE
1E008..1E018;N # Mn [17] COMBINING GLAGOLITIC LETTER ZEMLJA..COMBINING GLAGOLITIC LETTER HERU
1E01B..1E021;N # Mn [7] COMBINING GLAGOLITIC LETTER SHTA..COMBINING GLAGOLITIC LETTER YATI
@@ -2337,10 +2394,16 @@ FFFD;A # So REPLACEMENT CHARACTER
1E140..1E149;N # Nd [10] NYIAKENG PUACHUE HMONG DIGIT ZERO..NYIAKENG PUACHUE HMONG DIGIT NINE
1E14E;N # Lo NYIAKENG PUACHUE HMONG LOGOGRAM NYAJ
1E14F;N # So NYIAKENG PUACHUE HMONG CIRCLED CA
+1E290..1E2AD;N # Lo [30] TOTO LETTER PA..TOTO LETTER A
+1E2AE;N # Mn TOTO SIGN RISING TONE
1E2C0..1E2EB;N # Lo [44] WANCHO LETTER AA..WANCHO LETTER YIH
1E2EC..1E2EF;N # Mn [4] WANCHO TONE TUP..WANCHO TONE KOINI
1E2F0..1E2F9;N # Nd [10] WANCHO DIGIT ZERO..WANCHO DIGIT NINE
1E2FF;N # Sc WANCHO NGUN SIGN
+1E7E0..1E7E6;N # Lo [7] ETHIOPIC SYLLABLE HHYA..ETHIOPIC SYLLABLE HHYO
+1E7E8..1E7EB;N # Lo [4] ETHIOPIC SYLLABLE GURAGE HHWA..ETHIOPIC SYLLABLE HHWE
+1E7ED..1E7EE;N # Lo [2] ETHIOPIC SYLLABLE GURAGE MWI..ETHIOPIC SYLLABLE GURAGE MWEE
+1E7F0..1E7FE;N # Lo [15] ETHIOPIC SYLLABLE GURAGE QWI..ETHIOPIC SYLLABLE GURAGE PWEE
1E800..1E8C4;N # Lo [197] MENDE KIKAKUI SYLLABLE M001 KI..MENDE KIKAKUI SYLLABLE M060 NYON
1E8C7..1E8CF;N # No [9] MENDE KIKAKUI DIGIT ONE..MENDE KIKAKUI DIGIT NINE
1E8D0..1E8D6;N # Mn [7] MENDE KIKAKUI COMBINING NUMBER TEENS..MENDE KIKAKUI COMBINING NUMBER MILLIONS
@@ -2465,6 +2528,7 @@ FFFD;A # So REPLACEMENT CHARACTER
1F6D0..1F6D2;W # So [3] PLACE OF WORSHIP..SHOPPING TROLLEY
1F6D3..1F6D4;N # So [2] STUPA..PAGODA
1F6D5..1F6D7;W # So [3] HINDU TEMPLE..ELEVATOR
+1F6DD..1F6DF;W # So [3] PLAYGROUND SLIDE..RING BUOY
1F6E0..1F6EA;N # So [11] HAMMER AND WRENCH..NORTHEAST-POINTING AIRPLANE
1F6EB..1F6EC;W # So [2] AIRPLANE DEPARTURE..AIRPLANE ARRIVING
1F6F0..1F6F3;N # So [4] SATELLITE..PASSENGER SHIP
@@ -2472,6 +2536,7 @@ FFFD;A # So REPLACEMENT CHARACTER
1F700..1F773;N # So [116] ALCHEMICAL SYMBOL FOR QUINTESSENCE..ALCHEMICAL SYMBOL FOR HALF OUNCE
1F780..1F7D8;N # So [89] BLACK LEFT-POINTING ISOSCELES RIGHT TRIANGLE..NEGATIVE CIRCLED SQUARE
1F7E0..1F7EB;W # So [12] LARGE ORANGE CIRCLE..LARGE BROWN SQUARE
+1F7F0;W # So HEAVY EQUALS SIGN
1F800..1F80B;N # So [12] LEFTWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD..DOWNWARDS ARROW WITH LARGE TRIANGLE ARROWHEAD
1F810..1F847;N # So [56] LEFTWARDS ARROW WITH SMALL EQUILATERAL ARROWHEAD..DOWNWARDS HEAVY ARROW
1F850..1F859;N # So [10] LEFTWARDS SANS-SERIF ARROW..UP DOWN SANS-SERIF ARROW
@@ -2483,25 +2548,25 @@ FFFD;A # So REPLACEMENT CHARACTER
1F93B;N # So MODERN PENTATHLON
1F93C..1F945;W # So [10] WRESTLERS..GOAL NET
1F946;N # So RIFLE
-1F947..1F978;W # So [50] FIRST PLACE MEDAL..DISGUISED FACE
-1F97A..1F9CB;W # So [82] FACE WITH PLEADING EYES..BUBBLE TEA
-1F9CD..1F9FF;W # So [51] STANDING PERSON..NAZAR AMULET
+1F947..1F9FF;W # So [185] FIRST PLACE MEDAL..NAZAR AMULET
1FA00..1FA53;N # So [84] NEUTRAL CHESS KING..BLACK CHESS KNIGHT-BISHOP
1FA60..1FA6D;N # So [14] XIANGQI RED GENERAL..XIANGQI BLACK SOLDIER
1FA70..1FA74;W # So [5] BALLET SHOES..THONG SANDAL
-1FA78..1FA7A;W # So [3] DROP OF BLOOD..STETHOSCOPE
+1FA78..1FA7C;W # So [5] DROP OF BLOOD..CRUTCH
1FA80..1FA86;W # So [7] YO-YO..NESTING DOLLS
-1FA90..1FAA8;W # So [25] RINGED PLANET..ROCK
-1FAB0..1FAB6;W # So [7] FLY..FEATHER
-1FAC0..1FAC2;W # So [3] ANATOMICAL HEART..PEOPLE HUGGING
-1FAD0..1FAD6;W # So [7] BLUEBERRIES..TEAPOT
+1FA90..1FAAC;W # So [29] RINGED PLANET..HAMSA
+1FAB0..1FABA;W # So [11] FLY..NEST WITH EGGS
+1FAC0..1FAC5;W # So [6] ANATOMICAL HEART..PERSON WITH CROWN
+1FAD0..1FAD9;W # So [10] BLUEBERRIES..JAR
+1FAE0..1FAE7;W # So [8] MELTING FACE..BUBBLES
+1FAF0..1FAF6;W # So [7] HAND WITH INDEX FINGER AND THUMB CROSSED..HEART HANDS
1FB00..1FB92;N # So [147] BLOCK SEXTANT-1..UPPER HALF INVERSE MEDIUM SHADE AND LOWER HALF BLOCK
1FB94..1FBCA;N # So [55] LEFT HALF INVERSE MEDIUM SHADE AND RIGHT HALF BLOCK..WHITE UP-POINTING CHEVRON
1FBF0..1FBF9;N # Nd [10] SEGMENTED DIGIT ZERO..SEGMENTED DIGIT NINE
-20000..2A6DD;W # Lo [42718] CJK UNIFIED IDEOGRAPH-20000..CJK UNIFIED IDEOGRAPH-2A6DD
-2A6DE..2A6FF;W # Cn [34] <reserved-2A6DE>..<reserved-2A6FF>
-2A700..2B734;W # Lo [4149] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B734
-2B735..2B73F;W # Cn [11] <reserved-2B735>..<reserved-2B73F>
+20000..2A6DF;W # Lo [42720] CJK UNIFIED IDEOGRAPH-20000..CJK UNIFIED IDEOGRAPH-2A6DF
+2A6E0..2A6FF;W # Cn [32] <reserved-2A6E0>..<reserved-2A6FF>
+2A700..2B738;W # Lo [4153] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B738
+2B739..2B73F;W # Cn [7] <reserved-2B739>..<reserved-2B73F>
2B740..2B81D;W # Lo [222] CJK UNIFIED IDEOGRAPH-2B740..CJK UNIFIED IDEOGRAPH-2B81D
2B81E..2B81F;W # Cn [2] <reserved-2B81E>..<reserved-2B81F>
2B820..2CEA1;W # Lo [5762] CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEA1
diff --git a/unicode/UnicodeData.txt b/src/unicode/UnicodeData.txt
index e22f967bba..b5abef7ed4 100644
--- a/unicode/UnicodeData.txt
+++ b/src/unicode/UnicodeData.txt
@@ -1525,6 +1525,7 @@
061A;ARABIC SMALL KASRA;Mn;32;NSM;;;;;N;;;;;
061B;ARABIC SEMICOLON;Po;0;AL;;;;;N;;;;;
061C;ARABIC LETTER MARK;Cf;0;AL;;;;;N;;;;;
+061D;ARABIC END OF TEXT MARK;Po;0;AL;;;;;N;;;;;
061E;ARABIC TRIPLE DOT PUNCTUATION MARK;Po;0;AL;;;;;N;;;;;
061F;ARABIC QUESTION MARK;Po;0;AL;;;;;N;;;;;
0620;ARABIC LETTER KASHMIRI YEH;Lo;0;AL;;;;;N;;;;;
@@ -2089,6 +2090,47 @@
0868;SYRIAC LETTER MALAYALAM LLA;Lo;0;AL;;;;;N;;;;;
0869;SYRIAC LETTER MALAYALAM LLLA;Lo;0;AL;;;;;N;;;;;
086A;SYRIAC LETTER MALAYALAM SSA;Lo;0;AL;;;;;N;;;;;
+0870;ARABIC LETTER ALEF WITH ATTACHED FATHA;Lo;0;AL;;;;;N;;;;;
+0871;ARABIC LETTER ALEF WITH ATTACHED TOP RIGHT FATHA;Lo;0;AL;;;;;N;;;;;
+0872;ARABIC LETTER ALEF WITH RIGHT MIDDLE STROKE;Lo;0;AL;;;;;N;;;;;
+0873;ARABIC LETTER ALEF WITH LEFT MIDDLE STROKE;Lo;0;AL;;;;;N;;;;;
+0874;ARABIC LETTER ALEF WITH ATTACHED KASRA;Lo;0;AL;;;;;N;;;;;
+0875;ARABIC LETTER ALEF WITH ATTACHED BOTTOM RIGHT KASRA;Lo;0;AL;;;;;N;;;;;
+0876;ARABIC LETTER ALEF WITH ATTACHED ROUND DOT ABOVE;Lo;0;AL;;;;;N;;;;;
+0877;ARABIC LETTER ALEF WITH ATTACHED RIGHT ROUND DOT;Lo;0;AL;;;;;N;;;;;
+0878;ARABIC LETTER ALEF WITH ATTACHED LEFT ROUND DOT;Lo;0;AL;;;;;N;;;;;
+0879;ARABIC LETTER ALEF WITH ATTACHED ROUND DOT BELOW;Lo;0;AL;;;;;N;;;;;
+087A;ARABIC LETTER ALEF WITH DOT ABOVE;Lo;0;AL;;;;;N;;;;;
+087B;ARABIC LETTER ALEF WITH ATTACHED TOP RIGHT FATHA AND DOT ABOVE;Lo;0;AL;;;;;N;;;;;
+087C;ARABIC LETTER ALEF WITH RIGHT MIDDLE STROKE AND DOT ABOVE;Lo;0;AL;;;;;N;;;;;
+087D;ARABIC LETTER ALEF WITH ATTACHED BOTTOM RIGHT KASRA AND DOT ABOVE;Lo;0;AL;;;;;N;;;;;
+087E;ARABIC LETTER ALEF WITH ATTACHED TOP RIGHT FATHA AND LEFT RING;Lo;0;AL;;;;;N;;;;;
+087F;ARABIC LETTER ALEF WITH RIGHT MIDDLE STROKE AND LEFT RING;Lo;0;AL;;;;;N;;;;;
+0880;ARABIC LETTER ALEF WITH ATTACHED BOTTOM RIGHT KASRA AND LEFT RING;Lo;0;AL;;;;;N;;;;;
+0881;ARABIC LETTER ALEF WITH ATTACHED RIGHT HAMZA;Lo;0;AL;;;;;N;;;;;
+0882;ARABIC LETTER ALEF WITH ATTACHED LEFT HAMZA;Lo;0;AL;;;;;N;;;;;
+0883;ARABIC TATWEEL WITH OVERSTRUCK HAMZA;Lo;0;AL;;;;;N;;;;;
+0884;ARABIC TATWEEL WITH OVERSTRUCK WAW;Lo;0;AL;;;;;N;;;;;
+0885;ARABIC TATWEEL WITH TWO DOTS BELOW;Lo;0;AL;;;;;N;;;;;
+0886;ARABIC LETTER THIN YEH;Lo;0;AL;;;;;N;;;;;
+0887;ARABIC BASELINE ROUND DOT;Lo;0;AL;;;;;N;;;;;
+0888;ARABIC RAISED ROUND DOT;Sk;0;AL;;;;;N;;;;;
+0889;ARABIC LETTER NOON WITH INVERTED SMALL V;Lo;0;AL;;;;;N;;;;;
+088A;ARABIC LETTER HAH WITH INVERTED SMALL V BELOW;Lo;0;AL;;;;;N;;;;;
+088B;ARABIC LETTER TAH WITH DOT BELOW;Lo;0;AL;;;;;N;;;;;
+088C;ARABIC LETTER TAH WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;;;;;
+088D;ARABIC LETTER KEHEH WITH TWO DOTS VERTICALLY BELOW;Lo;0;AL;;;;;N;;;;;
+088E;ARABIC VERTICAL TAIL;Lo;0;AL;;;;;N;;;;;
+0890;ARABIC POUND MARK ABOVE;Cf;0;AN;;;;;N;;;;;
+0891;ARABIC PIASTRE MARK ABOVE;Cf;0;AN;;;;;N;;;;;
+0898;ARABIC SMALL HIGH WORD AL-JUZ;Mn;230;NSM;;;;;N;;;;;
+0899;ARABIC SMALL LOW WORD ISHMAAM;Mn;220;NSM;;;;;N;;;;;
+089A;ARABIC SMALL LOW WORD IMAALA;Mn;220;NSM;;;;;N;;;;;
+089B;ARABIC SMALL LOW WORD TASHEEL;Mn;220;NSM;;;;;N;;;;;
+089C;ARABIC MADDA WAAJIB;Mn;230;NSM;;;;;N;;;;;
+089D;ARABIC SUPERSCRIPT ALEF MOKHASSAS;Mn;230;NSM;;;;;N;;;;;
+089E;ARABIC DOUBLED MADDA;Mn;230;NSM;;;;;N;;;;;
+089F;ARABIC HALF MADDA OVER MADDA;Mn;230;NSM;;;;;N;;;;;
08A0;ARABIC LETTER BEH WITH SMALL V BELOW;Lo;0;AL;;;;;N;;;;;
08A1;ARABIC LETTER BEH WITH HAMZA ABOVE;Lo;0;AL;;;;;N;;;;;
08A2;ARABIC LETTER JEEM WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
@@ -2110,6 +2152,7 @@
08B2;ARABIC LETTER ZAIN WITH INVERTED V ABOVE;Lo;0;AL;;;;;N;;;;;
08B3;ARABIC LETTER AIN WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;;;;;
08B4;ARABIC LETTER KAF WITH DOT BELOW;Lo;0;AL;;;;;N;;;;;
+08B5;ARABIC LETTER QAF WITH DOT BELOW AND NO DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
08B6;ARABIC LETTER BEH WITH SMALL MEEM ABOVE;Lo;0;AL;;;;;N;;;;;
08B7;ARABIC LETTER PEH WITH SMALL MEEM ABOVE;Lo;0;AL;;;;;N;;;;;
08B8;ARABIC LETTER TEH WITH SMALL TEH ABOVE;Lo;0;AL;;;;;N;;;;;
@@ -2128,6 +2171,17 @@
08C5;ARABIC LETTER JEEM WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
08C6;ARABIC LETTER JEEM WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;;;;;
08C7;ARABIC LETTER LAM WITH SMALL ARABIC LETTER TAH ABOVE;Lo;0;AL;;;;;N;;;;;
+08C8;ARABIC LETTER GRAF;Lo;0;AL;;;;;N;;;;;
+08C9;ARABIC SMALL FARSI YEH;Lm;0;AL;;;;;N;;;;;
+08CA;ARABIC SMALL HIGH FARSI YEH;Mn;230;NSM;;;;;N;;;;;
+08CB;ARABIC SMALL HIGH YEH BARREE WITH TWO DOTS BELOW;Mn;230;NSM;;;;;N;;;;;
+08CC;ARABIC SMALL HIGH WORD SAH;Mn;230;NSM;;;;;N;;;;;
+08CD;ARABIC SMALL HIGH ZAH;Mn;230;NSM;;;;;N;;;;;
+08CE;ARABIC LARGE ROUND DOT ABOVE;Mn;230;NSM;;;;;N;;;;;
+08CF;ARABIC LARGE ROUND DOT BELOW;Mn;220;NSM;;;;;N;;;;;
+08D0;ARABIC SUKUN BELOW;Mn;220;NSM;;;;;N;;;;;
+08D1;ARABIC LARGE CIRCLE BELOW;Mn;220;NSM;;;;;N;;;;;
+08D2;ARABIC LARGE ROUND DOT INSIDE CIRCLE BELOW;Mn;220;NSM;;;;;N;;;;;
08D3;ARABIC SMALL LOW WAW;Mn;220;NSM;;;;;N;;;;;
08D4;ARABIC SMALL HIGH WORD AR-RUB;Mn;230;NSM;;;;;N;;;;;
08D5;ARABIC SMALL HIGH SAD;Mn;230;NSM;;;;;N;;;;;
@@ -2786,6 +2840,7 @@
0C37;TELUGU LETTER SSA;Lo;0;L;;;;;N;;;;;
0C38;TELUGU LETTER SA;Lo;0;L;;;;;N;;;;;
0C39;TELUGU LETTER HA;Lo;0;L;;;;;N;;;;;
+0C3C;TELUGU SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
0C3D;TELUGU SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;;
0C3E;TELUGU VOWEL SIGN AA;Mn;0;NSM;;;;;N;;;;;
0C3F;TELUGU VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
@@ -2806,6 +2861,7 @@
0C58;TELUGU LETTER TSA;Lo;0;L;;;;;N;;;;;
0C59;TELUGU LETTER DZA;Lo;0;L;;;;;N;;;;;
0C5A;TELUGU LETTER RRRA;Lo;0;L;;;;;N;;;;;
+0C5D;TELUGU LETTER NAKAARA POLLU;Lo;0;L;;;;;N;;;;;
0C60;TELUGU LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
0C61;TELUGU LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
0C62;TELUGU VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;;
@@ -2901,6 +2957,7 @@
0CCD;KANNADA SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
0CD5;KANNADA LENGTH MARK;Mc;0;L;;;;;N;;;;;
0CD6;KANNADA AI LENGTH MARK;Mc;0;L;;;;;N;;;;;
+0CDD;KANNADA LETTER NAKAARA POLLU;Lo;0;L;;;;;N;;;;;
0CDE;KANNADA LETTER FA;Lo;0;L;;;;;N;;;;;
0CE0;KANNADA LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
0CE1;KANNADA LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
@@ -5258,6 +5315,7 @@
170A;TAGALOG LETTER BA;Lo;0;L;;;;;N;;;;;
170B;TAGALOG LETTER MA;Lo;0;L;;;;;N;;;;;
170C;TAGALOG LETTER YA;Lo;0;L;;;;;N;;;;;
+170D;TAGALOG LETTER RA;Lo;0;L;;;;;N;;;;;
170E;TAGALOG LETTER LA;Lo;0;L;;;;;N;;;;;
170F;TAGALOG LETTER WA;Lo;0;L;;;;;N;;;;;
1710;TAGALOG LETTER SA;Lo;0;L;;;;;N;;;;;
@@ -5265,6 +5323,8 @@
1712;TAGALOG VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
1713;TAGALOG VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
1714;TAGALOG SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+1715;TAGALOG SIGN PAMUDPOD;Mc;9;L;;;;;N;;;;;
+171F;TAGALOG LETTER ARCHAIC RA;Lo;0;L;;;;;N;;;;;
1720;HANUNOO LETTER A;Lo;0;L;;;;;N;;;;;
1721;HANUNOO LETTER I;Lo;0;L;;;;;N;;;;;
1722;HANUNOO LETTER U;Lo;0;L;;;;;N;;;;;
@@ -5285,7 +5345,7 @@
1731;HANUNOO LETTER HA;Lo;0;L;;;;;N;;;;;
1732;HANUNOO VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
1733;HANUNOO VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
-1734;HANUNOO SIGN PAMUDPOD;Mn;9;NSM;;;;;N;;;;;
+1734;HANUNOO SIGN PAMUDPOD;Mc;9;L;;;;;N;;;;;
1735;PHILIPPINE SINGLE PUNCTUATION;Po;0;L;;;;;N;;;;;
1736;PHILIPPINE DOUBLE PUNCTUATION;Po;0;L;;;;;N;;;;;
1740;BUHID LETTER A;Lo;0;L;;;;;N;;;;;
@@ -5455,6 +5515,7 @@
180C;MONGOLIAN FREE VARIATION SELECTOR TWO;Mn;0;NSM;;;;;N;;;;;
180D;MONGOLIAN FREE VARIATION SELECTOR THREE;Mn;0;NSM;;;;;N;;;;;
180E;MONGOLIAN VOWEL SEPARATOR;Cf;0;BN;;;;;N;;;;;
+180F;MONGOLIAN FREE VARIATION SELECTOR FOUR;Mn;0;NSM;;;;;N;;;;;
1810;MONGOLIAN DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
1811;MONGOLIAN DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
1812;MONGOLIAN DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
@@ -6059,6 +6120,20 @@
1ABE;COMBINING PARENTHESES OVERLAY;Me;0;NSM;;;;;N;;;;;
1ABF;COMBINING LATIN SMALL LETTER W BELOW;Mn;220;NSM;;;;;N;;;;;
1AC0;COMBINING LATIN SMALL LETTER TURNED W BELOW;Mn;220;NSM;;;;;N;;;;;
+1AC1;COMBINING LEFT PARENTHESIS ABOVE LEFT;Mn;230;NSM;;;;;N;;;;;
+1AC2;COMBINING RIGHT PARENTHESIS ABOVE RIGHT;Mn;230;NSM;;;;;N;;;;;
+1AC3;COMBINING LEFT PARENTHESIS BELOW LEFT;Mn;220;NSM;;;;;N;;;;;
+1AC4;COMBINING RIGHT PARENTHESIS BELOW RIGHT;Mn;220;NSM;;;;;N;;;;;
+1AC5;COMBINING SQUARE BRACKETS ABOVE;Mn;230;NSM;;;;;N;;;;;
+1AC6;COMBINING NUMBER SIGN ABOVE;Mn;230;NSM;;;;;N;;;;;
+1AC7;COMBINING INVERTED DOUBLE ARCH ABOVE;Mn;230;NSM;;;;;N;;;;;
+1AC8;COMBINING PLUS SIGN ABOVE;Mn;230;NSM;;;;;N;;;;;
+1AC9;COMBINING DOUBLE PLUS SIGN ABOVE;Mn;230;NSM;;;;;N;;;;;
+1ACA;COMBINING DOUBLE PLUS SIGN BELOW;Mn;220;NSM;;;;;N;;;;;
+1ACB;COMBINING TRIPLE ACUTE ACCENT;Mn;230;NSM;;;;;N;;;;;
+1ACC;COMBINING LATIN SMALL LETTER INSULAR G;Mn;230;NSM;;;;;N;;;;;
+1ACD;COMBINING LATIN SMALL LETTER INSULAR R;Mn;230;NSM;;;;;N;;;;;
+1ACE;COMBINING LATIN SMALL LETTER INSULAR T;Mn;230;NSM;;;;;N;;;;;
1B00;BALINESE SIGN ULU RICEM;Mn;0;NSM;;;;;N;;;;;
1B01;BALINESE SIGN ULU CANDRA;Mn;0;NSM;;;;;N;;;;;
1B02;BALINESE SIGN CECEK;Mn;0;NSM;;;;;N;;;;;
@@ -6135,6 +6210,7 @@
1B49;BALINESE LETTER VE SASAK;Lo;0;L;;;;;N;;;;;
1B4A;BALINESE LETTER ZAL SASAK;Lo;0;L;;;;;N;;;;;
1B4B;BALINESE LETTER ASYURA SASAK;Lo;0;L;;;;;N;;;;;
+1B4C;BALINESE LETTER ARCHAIC JNYA;Lo;0;L;;;;;N;;;;;
1B50;BALINESE DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
1B51;BALINESE DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
1B52;BALINESE DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
@@ -6180,6 +6256,8 @@
1B7A;BALINESE MUSICAL SYMBOL LEFT-HAND CLOSED PLAK;So;0;L;;;;;N;;;;;
1B7B;BALINESE MUSICAL SYMBOL LEFT-HAND CLOSED PLUK;So;0;L;;;;;N;;;;;
1B7C;BALINESE MUSICAL SYMBOL LEFT-HAND OPEN PING;So;0;L;;;;;N;;;;;
+1B7D;BALINESE PANTI LANTANG;Po;0;L;;;;;N;;;;;
+1B7E;BALINESE PAMADA LANTANG;Po;0;L;;;;;N;;;;;
1B80;SUNDANESE SIGN PANYECEK;Mn;0;NSM;;;;;N;;;;;
1B81;SUNDANESE SIGN PANGLAYAR;Mn;0;NSM;;;;;N;;;;;
1B82;SUNDANESE SIGN PANGWISAD;Mc;0;L;;;;;N;;;;;
@@ -6778,6 +6856,7 @@
1DF7;COMBINING KAVYKA ABOVE LEFT;Mn;228;NSM;;;;;N;;;;;
1DF8;COMBINING DOT ABOVE LEFT;Mn;228;NSM;;;;;N;;;;;
1DF9;COMBINING WIDE INVERTED BRIDGE BELOW;Mn;220;NSM;;;;;N;;;;;
+1DFA;COMBINING DOT BELOW LEFT;Mn;218;NSM;;;;;N;;;;;
1DFB;COMBINING DELETION MARK;Mn;230;NSM;;;;;N;;;;;
1DFC;COMBINING DOUBLE INVERTED BREVE BELOW;Mn;233;NSM;;;;;N;;;;;
1DFD;COMBINING ALMOST EQUAL TO BELOW;Mn;220;NSM;;;;;N;;;;;
@@ -7457,6 +7536,7 @@
20BD;RUBLE SIGN;Sc;0;ET;;;;;N;;;;;
20BE;LARI SIGN;Sc;0;ET;;;;;N;;;;;
20BF;BITCOIN SIGN;Sc;0;ET;;;;;N;;;;;
+20C0;SOM SIGN;Sc;0;ET;;;;;N;;;;;
20D0;COMBINING LEFT HARPOON ABOVE;Mn;230;NSM;;;;;N;NON-SPACING LEFT HARPOON ABOVE;;;;
20D1;COMBINING RIGHT HARPOON ABOVE;Mn;230;NSM;;;;;N;NON-SPACING RIGHT HARPOON ABOVE;;;;
20D2;COMBINING LONG VERTICAL LINE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING LONG VERTICAL BAR OVERLAY;;;;
@@ -10300,6 +10380,7 @@
2C2C;GLAGOLITIC CAPITAL LETTER SHTAPIC;Lu;0;L;;;;;N;;;;2C5C;
2C2D;GLAGOLITIC CAPITAL LETTER TROKUTASTI A;Lu;0;L;;;;;N;;;;2C5D;
2C2E;GLAGOLITIC CAPITAL LETTER LATINATE MYSLITE;Lu;0;L;;;;;N;;;;2C5E;
+2C2F;GLAGOLITIC CAPITAL LETTER CAUDATE CHRIVI;Lu;0;L;;;;;N;;;;2C5F;
2C30;GLAGOLITIC SMALL LETTER AZU;Ll;0;L;;;;;N;;;2C00;;2C00
2C31;GLAGOLITIC SMALL LETTER BUKY;Ll;0;L;;;;;N;;;2C01;;2C01
2C32;GLAGOLITIC SMALL LETTER VEDE;Ll;0;L;;;;;N;;;2C02;;2C02
@@ -10347,6 +10428,7 @@
2C5C;GLAGOLITIC SMALL LETTER SHTAPIC;Ll;0;L;;;;;N;;;2C2C;;2C2C
2C5D;GLAGOLITIC SMALL LETTER TROKUTASTI A;Ll;0;L;;;;;N;;;2C2D;;2C2D
2C5E;GLAGOLITIC SMALL LETTER LATINATE MYSLITE;Ll;0;L;;;;;N;;;2C2E;;2C2E
+2C5F;GLAGOLITIC SMALL LETTER CAUDATE CHRIVI;Ll;0;L;;;;;N;;;2C2F;;2C2F
2C60;LATIN CAPITAL LETTER L WITH DOUBLE BAR;Lu;0;L;;;;;N;;;;2C61;
2C61;LATIN SMALL LETTER L WITH DOUBLE BAR;Ll;0;L;;;;;N;;;2C60;;2C60
2C62;LATIN CAPITAL LETTER L WITH MIDDLE TILDE;Lu;0;L;;;;;N;;;;026B;
@@ -10795,6 +10877,17 @@
2E50;CROSS PATTY WITH RIGHT CROSSBAR;So;0;ON;;;;;N;;;;;
2E51;CROSS PATTY WITH LEFT CROSSBAR;So;0;ON;;;;;N;;;;;
2E52;TIRONIAN SIGN CAPITAL ET;Po;0;ON;;;;;N;;;;;
+2E53;MEDIEVAL EXCLAMATION MARK;Po;0;ON;;;;;N;;;;;
+2E54;MEDIEVAL QUESTION MARK;Po;0;ON;;;;;N;;;;;
+2E55;LEFT SQUARE BRACKET WITH STROKE;Ps;0;ON;;;;;Y;;;;;
+2E56;RIGHT SQUARE BRACKET WITH STROKE;Pe;0;ON;;;;;Y;;;;;
+2E57;LEFT SQUARE BRACKET WITH DOUBLE STROKE;Ps;0;ON;;;;;Y;;;;;
+2E58;RIGHT SQUARE BRACKET WITH DOUBLE STROKE;Pe;0;ON;;;;;Y;;;;;
+2E59;TOP HALF LEFT PARENTHESIS;Ps;0;ON;;;;;Y;;;;;
+2E5A;TOP HALF RIGHT PARENTHESIS;Pe;0;ON;;;;;Y;;;;;
+2E5B;BOTTOM HALF LEFT PARENTHESIS;Ps;0;ON;;;;;Y;;;;;
+2E5C;BOTTOM HALF RIGHT PARENTHESIS;Pe;0;ON;;;;;Y;;;;;
+2E5D;OBLIQUE HYPHEN;Pd;0;ON;;;;;N;;;;;
2E80;CJK RADICAL REPEAT;So;0;ON;;;;;N;;;;;
2E81;CJK RADICAL CLIFF;So;0;ON;;;;;N;;;;;
2E82;CJK RADICAL SECOND ONE;So;0;ON;;;;;N;;;;;
@@ -12204,7 +12297,7 @@
4DFE;HEXAGRAM FOR AFTER COMPLETION;So;0;ON;;;;;N;;;;;
4DFF;HEXAGRAM FOR BEFORE COMPLETION;So;0;ON;;;;;N;;;;;
4E00;<CJK Ideograph, First>;Lo;0;L;;;;;N;;;;;
-9FFC;<CJK Ideograph, Last>;Lo;0;L;;;;;N;;;;;
+9FFF;<CJK Ideograph, Last>;Lo;0;L;;;;;N;;;;;
A000;YI SYLLABLE IT;Lo;0;L;;;;;N;;;;;
A001;YI SYLLABLE IX;Lo;0;L;;;;;N;;;;;
A002;YI SYLLABLE I;Lo;0;L;;;;;N;;;;;
@@ -14149,6 +14242,8 @@ A7BC;LATIN CAPITAL LETTER GLOTTAL I;Lu;0;L;;;;;N;;;;A7BD;
A7BD;LATIN SMALL LETTER GLOTTAL I;Ll;0;L;;;;;N;;;A7BC;;A7BC
A7BE;LATIN CAPITAL LETTER GLOTTAL U;Lu;0;L;;;;;N;;;;A7BF;
A7BF;LATIN SMALL LETTER GLOTTAL U;Ll;0;L;;;;;N;;;A7BE;;A7BE
+A7C0;LATIN CAPITAL LETTER OLD POLISH O;Lu;0;L;;;;;N;;;;A7C1;
+A7C1;LATIN SMALL LETTER OLD POLISH O;Ll;0;L;;;;;N;;;A7C0;;A7C0
A7C2;LATIN CAPITAL LETTER ANGLICANA W;Lu;0;L;;;;;N;;;;A7C3;
A7C3;LATIN SMALL LETTER ANGLICANA W;Ll;0;L;;;;;N;;;A7C2;;A7C2
A7C4;LATIN CAPITAL LETTER C WITH PALATAL HOOK;Lu;0;L;;;;;N;;;;A794;
@@ -14158,6 +14253,17 @@ A7C7;LATIN CAPITAL LETTER D WITH SHORT STROKE OVERLAY;Lu;0;L;;;;;N;;;;A7C8;
A7C8;LATIN SMALL LETTER D WITH SHORT STROKE OVERLAY;Ll;0;L;;;;;N;;;A7C7;;A7C7
A7C9;LATIN CAPITAL LETTER S WITH SHORT STROKE OVERLAY;Lu;0;L;;;;;N;;;;A7CA;
A7CA;LATIN SMALL LETTER S WITH SHORT STROKE OVERLAY;Ll;0;L;;;;;N;;;A7C9;;A7C9
+A7D0;LATIN CAPITAL LETTER CLOSED INSULAR G;Lu;0;L;;;;;N;;;;A7D1;
+A7D1;LATIN SMALL LETTER CLOSED INSULAR G;Ll;0;L;;;;;N;;;A7D0;;A7D0
+A7D3;LATIN SMALL LETTER DOUBLE THORN;Ll;0;L;;;;;N;;;;;
+A7D5;LATIN SMALL LETTER DOUBLE WYNN;Ll;0;L;;;;;N;;;;;
+A7D6;LATIN CAPITAL LETTER MIDDLE SCOTS S;Lu;0;L;;;;;N;;;;A7D7;
+A7D7;LATIN SMALL LETTER MIDDLE SCOTS S;Ll;0;L;;;;;N;;;A7D6;;A7D6
+A7D8;LATIN CAPITAL LETTER SIGMOID S;Lu;0;L;;;;;N;;;;A7D9;
+A7D9;LATIN SMALL LETTER SIGMOID S;Ll;0;L;;;;;N;;;A7D8;;A7D8
+A7F2;MODIFIER LETTER CAPITAL C;Lm;0;L;<super> 0043;;;;N;;;;;
+A7F3;MODIFIER LETTER CAPITAL F;Lm;0;L;<super> 0046;;;;N;;;;;
+A7F4;MODIFIER LETTER CAPITAL Q;Lm;0;L;<super> 0051;;;;N;;;;;
A7F5;LATIN CAPITAL LETTER REVERSED HALF H;Lu;0;L;;;;;N;;;;A7F6;
A7F6;LATIN SMALL LETTER REVERSED HALF H;Ll;0;L;;;;;N;;;A7F5;;A7F5
A7F7;LATIN EPIGRAPHIC LETTER SIDEWAYS I;Lo;0;L;;;;;N;;;;;
@@ -15794,6 +15900,7 @@ FBBE;ARABIC SYMBOL TWO DOTS VERTICALLY BELOW;Sk;0;AL;;;;;N;;;;;
FBBF;ARABIC SYMBOL RING;Sk;0;AL;;;;;N;;;;;
FBC0;ARABIC SYMBOL SMALL TAH ABOVE;Sk;0;AL;;;;;N;;;;;
FBC1;ARABIC SYMBOL SMALL TAH BELOW;Sk;0;AL;;;;;N;;;;;
+FBC2;ARABIC SYMBOL WASLA ABOVE;Sk;0;AL;;;;;N;;;;;
FBD3;ARABIC LETTER NG ISOLATED FORM;Lo;0;AL;<isolated> 06AD;;;;N;;;;;
FBD4;ARABIC LETTER NG FINAL FORM;Lo;0;AL;<final> 06AD;;;;N;;;;;
FBD5;ARABIC LETTER NG INITIAL FORM;Lo;0;AL;<initial> 06AD;;;;N;;;;;
@@ -16159,6 +16266,22 @@ FD3C;ARABIC LIGATURE ALEF WITH FATHATAN FINAL FORM;Lo;0;AL;<final> 0627 064B;;;;
FD3D;ARABIC LIGATURE ALEF WITH FATHATAN ISOLATED FORM;Lo;0;AL;<isolated> 0627 064B;;;;N;;;;;
FD3E;ORNATE LEFT PARENTHESIS;Pe;0;ON;;;;;N;;;;;
FD3F;ORNATE RIGHT PARENTHESIS;Ps;0;ON;;;;;N;;;;;
+FD40;ARABIC LIGATURE RAHIMAHU ALLAAH;So;0;ON;;;;;N;;;;;
+FD41;ARABIC LIGATURE RADI ALLAAHU ANH;So;0;ON;;;;;N;;;;;
+FD42;ARABIC LIGATURE RADI ALLAAHU ANHAA;So;0;ON;;;;;N;;;;;
+FD43;ARABIC LIGATURE RADI ALLAAHU ANHUM;So;0;ON;;;;;N;;;;;
+FD44;ARABIC LIGATURE RADI ALLAAHU ANHUMAA;So;0;ON;;;;;N;;;;;
+FD45;ARABIC LIGATURE RADI ALLAAHU ANHUNNA;So;0;ON;;;;;N;;;;;
+FD46;ARABIC LIGATURE SALLALLAAHU ALAYHI WA-AALIH;So;0;ON;;;;;N;;;;;
+FD47;ARABIC LIGATURE ALAYHI AS-SALAAM;So;0;ON;;;;;N;;;;;
+FD48;ARABIC LIGATURE ALAYHIM AS-SALAAM;So;0;ON;;;;;N;;;;;
+FD49;ARABIC LIGATURE ALAYHIMAA AS-SALAAM;So;0;ON;;;;;N;;;;;
+FD4A;ARABIC LIGATURE ALAYHI AS-SALAATU WAS-SALAAM;So;0;ON;;;;;N;;;;;
+FD4B;ARABIC LIGATURE QUDDISA SIRRAH;So;0;ON;;;;;N;;;;;
+FD4C;ARABIC LIGATURE SALLALLAHU ALAYHI WAAALIHEE WA-SALLAM;So;0;ON;;;;;N;;;;;
+FD4D;ARABIC LIGATURE ALAYHAA AS-SALAAM;So;0;ON;;;;;N;;;;;
+FD4E;ARABIC LIGATURE TABAARAKA WA-TAAALAA;So;0;ON;;;;;N;;;;;
+FD4F;ARABIC LIGATURE RAHIMAHUM ALLAAH;So;0;ON;;;;;N;;;;;
FD50;ARABIC LIGATURE TEH WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062A 062C 0645;;;;N;;;;;
FD51;ARABIC LIGATURE TEH WITH HAH WITH JEEM FINAL FORM;Lo;0;AL;<final> 062A 062D 062C;;;;N;;;;;
FD52;ARABIC LIGATURE TEH WITH HAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 062A 062D 062C;;;;N;;;;;
@@ -16277,6 +16400,7 @@ FDC4;ARABIC LIGATURE AIN WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0639
FDC5;ARABIC LIGATURE SAD WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0635 0645 0645;;;;N;;;;;
FDC6;ARABIC LIGATURE SEEN WITH KHAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0633 062E 064A;;;;N;;;;;
FDC7;ARABIC LIGATURE NOON WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0646 062C 064A;;;;N;;;;;
+FDCF;ARABIC LIGATURE SALAAMUHU ALAYNAA;So;0;ON;;;;;N;;;;;
FDF0;ARABIC LIGATURE SALLA USED AS KORANIC STOP SIGN ISOLATED FORM;Lo;0;AL;<isolated> 0635 0644 06D2;;;;N;;;;;
FDF1;ARABIC LIGATURE QALA USED AS KORANIC STOP SIGN ISOLATED FORM;Lo;0;AL;<isolated> 0642 0644 06D2;;;;N;;;;;
FDF2;ARABIC LIGATURE ALLAH ISOLATED FORM;Lo;0;AL;<isolated> 0627 0644 0644 0647;;;;N;;;;;
@@ -16291,6 +16415,8 @@ FDFA;ARABIC LIGATURE SALLALLAHOU ALAYHE WASALLAM;Lo;0;AL;<isolated> 0635 0644 06
FDFB;ARABIC LIGATURE JALLAJALALOUHOU;Lo;0;AL;<isolated> 062C 0644 0020 062C 0644 0627 0644 0647;;;;N;ARABIC LETTER JALLAJALALOUHOU;;;;
FDFC;RIAL SIGN;Sc;0;AL;<isolated> 0631 06CC 0627 0644;;;;N;;;;;
FDFD;ARABIC LIGATURE BISMILLAH AR-RAHMAN AR-RAHEEM;So;0;ON;;;;;N;;;;;
+FDFE;ARABIC LIGATURE SUBHAANAHU WA TAAALAA;So;0;ON;;;;;N;;;;;
+FDFF;ARABIC LIGATURE AZZA WA JALL;So;0;ON;;;;;N;;;;;
FE00;VARIATION SELECTOR-1;Mn;0;NSM;;;;;N;;;;;
FE01;VARIATION SELECTOR-2;Mn;0;NSM;;;;;N;;;;;
FE02;VARIATION SELECTOR-3;Mn;0;NSM;;;;;N;;;;;
@@ -17798,6 +17924,76 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
10562;CAUCASIAN ALBANIAN LETTER PIWR;Lo;0;L;;;;;N;;;;;
10563;CAUCASIAN ALBANIAN LETTER KIW;Lo;0;L;;;;;N;;;;;
1056F;CAUCASIAN ALBANIAN CITATION MARK;Po;0;L;;;;;N;;;;;
+10570;VITHKUQI CAPITAL LETTER A;Lu;0;L;;;;;N;;;;10597;
+10571;VITHKUQI CAPITAL LETTER BBE;Lu;0;L;;;;;N;;;;10598;
+10572;VITHKUQI CAPITAL LETTER BE;Lu;0;L;;;;;N;;;;10599;
+10573;VITHKUQI CAPITAL LETTER CE;Lu;0;L;;;;;N;;;;1059A;
+10574;VITHKUQI CAPITAL LETTER CHE;Lu;0;L;;;;;N;;;;1059B;
+10575;VITHKUQI CAPITAL LETTER DE;Lu;0;L;;;;;N;;;;1059C;
+10576;VITHKUQI CAPITAL LETTER DHE;Lu;0;L;;;;;N;;;;1059D;
+10577;VITHKUQI CAPITAL LETTER EI;Lu;0;L;;;;;N;;;;1059E;
+10578;VITHKUQI CAPITAL LETTER E;Lu;0;L;;;;;N;;;;1059F;
+10579;VITHKUQI CAPITAL LETTER FE;Lu;0;L;;;;;N;;;;105A0;
+1057A;VITHKUQI CAPITAL LETTER GA;Lu;0;L;;;;;N;;;;105A1;
+1057C;VITHKUQI CAPITAL LETTER HA;Lu;0;L;;;;;N;;;;105A3;
+1057D;VITHKUQI CAPITAL LETTER HHA;Lu;0;L;;;;;N;;;;105A4;
+1057E;VITHKUQI CAPITAL LETTER I;Lu;0;L;;;;;N;;;;105A5;
+1057F;VITHKUQI CAPITAL LETTER IJE;Lu;0;L;;;;;N;;;;105A6;
+10580;VITHKUQI CAPITAL LETTER JE;Lu;0;L;;;;;N;;;;105A7;
+10581;VITHKUQI CAPITAL LETTER KA;Lu;0;L;;;;;N;;;;105A8;
+10582;VITHKUQI CAPITAL LETTER LA;Lu;0;L;;;;;N;;;;105A9;
+10583;VITHKUQI CAPITAL LETTER LLA;Lu;0;L;;;;;N;;;;105AA;
+10584;VITHKUQI CAPITAL LETTER ME;Lu;0;L;;;;;N;;;;105AB;
+10585;VITHKUQI CAPITAL LETTER NE;Lu;0;L;;;;;N;;;;105AC;
+10586;VITHKUQI CAPITAL LETTER NJE;Lu;0;L;;;;;N;;;;105AD;
+10587;VITHKUQI CAPITAL LETTER O;Lu;0;L;;;;;N;;;;105AE;
+10588;VITHKUQI CAPITAL LETTER PE;Lu;0;L;;;;;N;;;;105AF;
+10589;VITHKUQI CAPITAL LETTER QA;Lu;0;L;;;;;N;;;;105B0;
+1058A;VITHKUQI CAPITAL LETTER RE;Lu;0;L;;;;;N;;;;105B1;
+1058C;VITHKUQI CAPITAL LETTER SE;Lu;0;L;;;;;N;;;;105B3;
+1058D;VITHKUQI CAPITAL LETTER SHE;Lu;0;L;;;;;N;;;;105B4;
+1058E;VITHKUQI CAPITAL LETTER TE;Lu;0;L;;;;;N;;;;105B5;
+1058F;VITHKUQI CAPITAL LETTER THE;Lu;0;L;;;;;N;;;;105B6;
+10590;VITHKUQI CAPITAL LETTER U;Lu;0;L;;;;;N;;;;105B7;
+10591;VITHKUQI CAPITAL LETTER VE;Lu;0;L;;;;;N;;;;105B8;
+10592;VITHKUQI CAPITAL LETTER XE;Lu;0;L;;;;;N;;;;105B9;
+10594;VITHKUQI CAPITAL LETTER Y;Lu;0;L;;;;;N;;;;105BB;
+10595;VITHKUQI CAPITAL LETTER ZE;Lu;0;L;;;;;N;;;;105BC;
+10597;VITHKUQI SMALL LETTER A;Ll;0;L;;;;;N;;;10570;;10570
+10598;VITHKUQI SMALL LETTER BBE;Ll;0;L;;;;;N;;;10571;;10571
+10599;VITHKUQI SMALL LETTER BE;Ll;0;L;;;;;N;;;10572;;10572
+1059A;VITHKUQI SMALL LETTER CE;Ll;0;L;;;;;N;;;10573;;10573
+1059B;VITHKUQI SMALL LETTER CHE;Ll;0;L;;;;;N;;;10574;;10574
+1059C;VITHKUQI SMALL LETTER DE;Ll;0;L;;;;;N;;;10575;;10575
+1059D;VITHKUQI SMALL LETTER DHE;Ll;0;L;;;;;N;;;10576;;10576
+1059E;VITHKUQI SMALL LETTER EI;Ll;0;L;;;;;N;;;10577;;10577
+1059F;VITHKUQI SMALL LETTER E;Ll;0;L;;;;;N;;;10578;;10578
+105A0;VITHKUQI SMALL LETTER FE;Ll;0;L;;;;;N;;;10579;;10579
+105A1;VITHKUQI SMALL LETTER GA;Ll;0;L;;;;;N;;;1057A;;1057A
+105A3;VITHKUQI SMALL LETTER HA;Ll;0;L;;;;;N;;;1057C;;1057C
+105A4;VITHKUQI SMALL LETTER HHA;Ll;0;L;;;;;N;;;1057D;;1057D
+105A5;VITHKUQI SMALL LETTER I;Ll;0;L;;;;;N;;;1057E;;1057E
+105A6;VITHKUQI SMALL LETTER IJE;Ll;0;L;;;;;N;;;1057F;;1057F
+105A7;VITHKUQI SMALL LETTER JE;Ll;0;L;;;;;N;;;10580;;10580
+105A8;VITHKUQI SMALL LETTER KA;Ll;0;L;;;;;N;;;10581;;10581
+105A9;VITHKUQI SMALL LETTER LA;Ll;0;L;;;;;N;;;10582;;10582
+105AA;VITHKUQI SMALL LETTER LLA;Ll;0;L;;;;;N;;;10583;;10583
+105AB;VITHKUQI SMALL LETTER ME;Ll;0;L;;;;;N;;;10584;;10584
+105AC;VITHKUQI SMALL LETTER NE;Ll;0;L;;;;;N;;;10585;;10585
+105AD;VITHKUQI SMALL LETTER NJE;Ll;0;L;;;;;N;;;10586;;10586
+105AE;VITHKUQI SMALL LETTER O;Ll;0;L;;;;;N;;;10587;;10587
+105AF;VITHKUQI SMALL LETTER PE;Ll;0;L;;;;;N;;;10588;;10588
+105B0;VITHKUQI SMALL LETTER QA;Ll;0;L;;;;;N;;;10589;;10589
+105B1;VITHKUQI SMALL LETTER RE;Ll;0;L;;;;;N;;;1058A;;1058A
+105B3;VITHKUQI SMALL LETTER SE;Ll;0;L;;;;;N;;;1058C;;1058C
+105B4;VITHKUQI SMALL LETTER SHE;Ll;0;L;;;;;N;;;1058D;;1058D
+105B5;VITHKUQI SMALL LETTER TE;Ll;0;L;;;;;N;;;1058E;;1058E
+105B6;VITHKUQI SMALL LETTER THE;Ll;0;L;;;;;N;;;1058F;;1058F
+105B7;VITHKUQI SMALL LETTER U;Ll;0;L;;;;;N;;;10590;;10590
+105B8;VITHKUQI SMALL LETTER VE;Ll;0;L;;;;;N;;;10591;;10591
+105B9;VITHKUQI SMALL LETTER XE;Ll;0;L;;;;;N;;;10592;;10592
+105BB;VITHKUQI SMALL LETTER Y;Ll;0;L;;;;;N;;;10594;;10594
+105BC;VITHKUQI SMALL LETTER ZE;Ll;0;L;;;;;N;;;10595;;10595
10600;LINEAR A SIGN AB001;Lo;0;L;;;;;N;;;;;
10601;LINEAR A SIGN AB002;Lo;0;L;;;;;N;;;;;
10602;LINEAR A SIGN AB003;Lo;0;L;;;;;N;;;;;
@@ -18139,6 +18335,63 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
10765;LINEAR A SIGN A805;Lo;0;L;;;;;N;;;;;
10766;LINEAR A SIGN A806;Lo;0;L;;;;;N;;;;;
10767;LINEAR A SIGN A807;Lo;0;L;;;;;N;;;;;
+10780;MODIFIER LETTER SMALL CAPITAL AA;Lm;0;L;;;;;N;;;;;
+10781;MODIFIER LETTER SUPERSCRIPT TRIANGULAR COLON;Lm;0;L;<super> 02D0;;;;N;;;;;
+10782;MODIFIER LETTER SUPERSCRIPT HALF TRIANGULAR COLON;Lm;0;L;<super> 02D1;;;;N;;;;;
+10783;MODIFIER LETTER SMALL AE;Lm;0;L;<super> 00E6;;;;N;;;;;
+10784;MODIFIER LETTER SMALL CAPITAL B;Lm;0;L;<super> 0299;;;;N;;;;;
+10785;MODIFIER LETTER SMALL B WITH HOOK;Lm;0;L;<super> 0253;;;;N;;;;;
+10787;MODIFIER LETTER SMALL DZ DIGRAPH;Lm;0;L;<super> 02A3;;;;N;;;;;
+10788;MODIFIER LETTER SMALL DZ DIGRAPH WITH RETROFLEX HOOK;Lm;0;L;<super> AB66;;;;N;;;;;
+10789;MODIFIER LETTER SMALL DZ DIGRAPH WITH CURL;Lm;0;L;<super> 02A5;;;;N;;;;;
+1078A;MODIFIER LETTER SMALL DEZH DIGRAPH;Lm;0;L;<super> 02A4;;;;N;;;;;
+1078B;MODIFIER LETTER SMALL D WITH TAIL;Lm;0;L;<super> 0256;;;;N;;;;;
+1078C;MODIFIER LETTER SMALL D WITH HOOK;Lm;0;L;<super> 0257;;;;N;;;;;
+1078D;MODIFIER LETTER SMALL D WITH HOOK AND TAIL;Lm;0;L;<super> 1D91;;;;N;;;;;
+1078E;MODIFIER LETTER SMALL REVERSED E;Lm;0;L;<super> 0258;;;;N;;;;;
+1078F;MODIFIER LETTER SMALL CLOSED REVERSED OPEN E;Lm;0;L;<super> 025E;;;;N;;;;;
+10790;MODIFIER LETTER SMALL FENG DIGRAPH;Lm;0;L;<super> 02A9;;;;N;;;;;
+10791;MODIFIER LETTER SMALL RAMS HORN;Lm;0;L;<super> 0264;;;;N;;;;;
+10792;MODIFIER LETTER SMALL CAPITAL G;Lm;0;L;<super> 0262;;;;N;;;;;
+10793;MODIFIER LETTER SMALL G WITH HOOK;Lm;0;L;<super> 0260;;;;N;;;;;
+10794;MODIFIER LETTER SMALL CAPITAL G WITH HOOK;Lm;0;L;<super> 029B;;;;N;;;;;
+10795;MODIFIER LETTER SMALL H WITH STROKE;Lm;0;L;<super> 0127;;;;N;;;;;
+10796;MODIFIER LETTER SMALL CAPITAL H;Lm;0;L;<super> 029C;;;;N;;;;;
+10797;MODIFIER LETTER SMALL HENG WITH HOOK;Lm;0;L;<super> 0267;;;;N;;;;;
+10798;MODIFIER LETTER SMALL DOTLESS J WITH STROKE AND HOOK;Lm;0;L;<super> 0284;;;;N;;;;;
+10799;MODIFIER LETTER SMALL LS DIGRAPH;Lm;0;L;<super> 02AA;;;;N;;;;;
+1079A;MODIFIER LETTER SMALL LZ DIGRAPH;Lm;0;L;<super> 02AB;;;;N;;;;;
+1079B;MODIFIER LETTER SMALL L WITH BELT;Lm;0;L;<super> 026C;;;;N;;;;;
+1079C;MODIFIER LETTER SMALL CAPITAL L WITH BELT;Lm;0;L;<super> 1DF04;;;;N;;;;;
+1079D;MODIFIER LETTER SMALL L WITH RETROFLEX HOOK AND BELT;Lm;0;L;<super> A78E;;;;N;;;;;
+1079E;MODIFIER LETTER SMALL LEZH;Lm;0;L;<super> 026E;;;;N;;;;;
+1079F;MODIFIER LETTER SMALL LEZH WITH RETROFLEX HOOK;Lm;0;L;<super> 1DF05;;;;N;;;;;
+107A0;MODIFIER LETTER SMALL TURNED Y;Lm;0;L;<super> 028E;;;;N;;;;;
+107A1;MODIFIER LETTER SMALL TURNED Y WITH BELT;Lm;0;L;<super> 1DF06;;;;N;;;;;
+107A2;MODIFIER LETTER SMALL O WITH STROKE;Lm;0;L;<super> 00F8;;;;N;;;;;
+107A3;MODIFIER LETTER SMALL CAPITAL OE;Lm;0;L;<super> 0276;;;;N;;;;;
+107A4;MODIFIER LETTER SMALL CLOSED OMEGA;Lm;0;L;<super> 0277;;;;N;;;;;
+107A5;MODIFIER LETTER SMALL Q;Lm;0;L;<super> 0071;;;;N;;;;;
+107A6;MODIFIER LETTER SMALL TURNED R WITH LONG LEG;Lm;0;L;<super> 027A;;;;N;;;;;
+107A7;MODIFIER LETTER SMALL TURNED R WITH LONG LEG AND RETROFLEX HOOK;Lm;0;L;<super> 1DF08;;;;N;;;;;
+107A8;MODIFIER LETTER SMALL R WITH TAIL;Lm;0;L;<super> 027D;;;;N;;;;;
+107A9;MODIFIER LETTER SMALL R WITH FISHHOOK;Lm;0;L;<super> 027E;;;;N;;;;;
+107AA;MODIFIER LETTER SMALL CAPITAL R;Lm;0;L;<super> 0280;;;;N;;;;;
+107AB;MODIFIER LETTER SMALL TC DIGRAPH WITH CURL;Lm;0;L;<super> 02A8;;;;N;;;;;
+107AC;MODIFIER LETTER SMALL TS DIGRAPH;Lm;0;L;<super> 02A6;;;;N;;;;;
+107AD;MODIFIER LETTER SMALL TS DIGRAPH WITH RETROFLEX HOOK;Lm;0;L;<super> AB67;;;;N;;;;;
+107AE;MODIFIER LETTER SMALL TESH DIGRAPH;Lm;0;L;<super> 02A7;;;;N;;;;;
+107AF;MODIFIER LETTER SMALL T WITH RETROFLEX HOOK;Lm;0;L;<super> 0288;;;;N;;;;;
+107B0;MODIFIER LETTER SMALL V WITH RIGHT HOOK;Lm;0;L;<super> 2C71;;;;N;;;;;
+107B2;MODIFIER LETTER SMALL CAPITAL Y;Lm;0;L;<super> 028F;;;;N;;;;;
+107B3;MODIFIER LETTER GLOTTAL STOP WITH STROKE;Lm;0;L;<super> 02A1;;;;N;;;;;
+107B4;MODIFIER LETTER REVERSED GLOTTAL STOP WITH STROKE;Lm;0;L;<super> 02A2;;;;N;;;;;
+107B5;MODIFIER LETTER BILABIAL CLICK;Lm;0;L;<super> 0298;;;;N;;;;;
+107B6;MODIFIER LETTER DENTAL CLICK;Lm;0;L;<super> 01C0;;;;N;;;;;
+107B7;MODIFIER LETTER LATERAL CLICK;Lm;0;L;<super> 01C1;;;;N;;;;;
+107B8;MODIFIER LETTER ALVEOLAR CLICK;Lm;0;L;<super> 01C2;;;;N;;;;;
+107B9;MODIFIER LETTER RETROFLEX CLICK WITH RETROFLEX HOOK;Lm;0;L;<super> 1DF0A;;;;N;;;;;
+107BA;MODIFIER LETTER SMALL S WITH CURL;Lm;0;L;<super> 1DF1E;;;;N;;;;;
10800;CYPRIOT SYLLABLE A;Lo;0;R;;;;;N;;;;;
10801;CYPRIOT SYLLABLE E;Lo;0;R;;;;;N;;;;;
10802;CYPRIOT SYLLABLE I;Lo;0;R;;;;;N;;;;;
@@ -19222,6 +19475,32 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
10F57;SOGDIAN PUNCTUATION CIRCLE WITH DOT;Po;0;AL;;;;;N;;;;;
10F58;SOGDIAN PUNCTUATION TWO CIRCLES WITH DOTS;Po;0;AL;;;;;N;;;;;
10F59;SOGDIAN PUNCTUATION HALF CIRCLE WITH DOT;Po;0;AL;;;;;N;;;;;
+10F70;OLD UYGHUR LETTER ALEPH;Lo;0;R;;;;;N;;;;;
+10F71;OLD UYGHUR LETTER BETH;Lo;0;R;;;;;N;;;;;
+10F72;OLD UYGHUR LETTER GIMEL-HETH;Lo;0;R;;;;;N;;;;;
+10F73;OLD UYGHUR LETTER WAW;Lo;0;R;;;;;N;;;;;
+10F74;OLD UYGHUR LETTER ZAYIN;Lo;0;R;;;;;N;;;;;
+10F75;OLD UYGHUR LETTER FINAL HETH;Lo;0;R;;;;;N;;;;;
+10F76;OLD UYGHUR LETTER YODH;Lo;0;R;;;;;N;;;;;
+10F77;OLD UYGHUR LETTER KAPH;Lo;0;R;;;;;N;;;;;
+10F78;OLD UYGHUR LETTER LAMEDH;Lo;0;R;;;;;N;;;;;
+10F79;OLD UYGHUR LETTER MEM;Lo;0;R;;;;;N;;;;;
+10F7A;OLD UYGHUR LETTER NUN;Lo;0;R;;;;;N;;;;;
+10F7B;OLD UYGHUR LETTER SAMEKH;Lo;0;R;;;;;N;;;;;
+10F7C;OLD UYGHUR LETTER PE;Lo;0;R;;;;;N;;;;;
+10F7D;OLD UYGHUR LETTER SADHE;Lo;0;R;;;;;N;;;;;
+10F7E;OLD UYGHUR LETTER RESH;Lo;0;R;;;;;N;;;;;
+10F7F;OLD UYGHUR LETTER SHIN;Lo;0;R;;;;;N;;;;;
+10F80;OLD UYGHUR LETTER TAW;Lo;0;R;;;;;N;;;;;
+10F81;OLD UYGHUR LETTER LESH;Lo;0;R;;;;;N;;;;;
+10F82;OLD UYGHUR COMBINING DOT ABOVE;Mn;230;NSM;;;;;N;;;;;
+10F83;OLD UYGHUR COMBINING DOT BELOW;Mn;220;NSM;;;;;N;;;;;
+10F84;OLD UYGHUR COMBINING TWO DOTS ABOVE;Mn;230;NSM;;;;;N;;;;;
+10F85;OLD UYGHUR COMBINING TWO DOTS BELOW;Mn;220;NSM;;;;;N;;;;;
+10F86;OLD UYGHUR PUNCTUATION BAR;Po;0;R;;;;;N;;;;;
+10F87;OLD UYGHUR PUNCTUATION TWO BARS;Po;0;R;;;;;N;;;;;
+10F88;OLD UYGHUR PUNCTUATION TWO DOTS;Po;0;R;;;;;N;;;;;
+10F89;OLD UYGHUR PUNCTUATION FOUR DOTS;Po;0;R;;;;;N;;;;;
10FB0;CHORASMIAN LETTER ALEPH;Lo;0;R;;;;;N;;;;;
10FB1;CHORASMIAN LETTER SMALL ALEPH;Lo;0;R;;;;;N;;;;;
10FB2;CHORASMIAN LETTER BETH;Lo;0;R;;;;;N;;;;;
@@ -19381,6 +19660,12 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1106D;BRAHMI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
1106E;BRAHMI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
1106F;BRAHMI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+11070;BRAHMI SIGN OLD TAMIL VIRAMA;Mn;9;NSM;;;;;N;;;;;
+11071;BRAHMI LETTER OLD TAMIL SHORT E;Lo;0;L;;;;;N;;;;;
+11072;BRAHMI LETTER OLD TAMIL SHORT O;Lo;0;L;;;;;N;;;;;
+11073;BRAHMI VOWEL SIGN OLD TAMIL SHORT E;Mn;0;NSM;;;;;N;;;;;
+11074;BRAHMI VOWEL SIGN OLD TAMIL SHORT O;Mn;0;NSM;;;;;N;;;;;
+11075;BRAHMI LETTER OLD TAMIL LLA;Lo;0;L;;;;;N;;;;;
1107F;BRAHMI NUMBER JOINER;Mn;9;NSM;;;;;N;;;;;
11080;KAITHI SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
11081;KAITHI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
@@ -19448,6 +19733,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
110BF;KAITHI DOUBLE SECTION MARK;Po;0;L;;;;;N;;;;;
110C0;KAITHI DANDA;Po;0;L;;;;;N;;;;;
110C1;KAITHI DOUBLE DANDA;Po;0;L;;;;;N;;;;;
+110C2;KAITHI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
110CD;KAITHI NUMBER SIGN ABOVE;Cf;0;L;;;;;N;;;;;
110D0;SORA SOMPENG LETTER SAH;Lo;0;L;;;;;N;;;;;
110D1;SORA SOMPENG LETTER TAH;Lo;0;L;;;;;N;;;;;
@@ -20385,6 +20671,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
116B6;TAKRI SIGN VIRAMA;Mc;9;L;;;;;N;;;;;
116B7;TAKRI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
116B8;TAKRI LETTER ARCHAIC KHA;Lo;0;L;;;;;N;;;;;
+116B9;TAKRI ABBREVIATION SIGN;Po;0;L;;;;;N;;;;;
116C0;TAKRI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
116C1;TAKRI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
116C2;TAKRI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
@@ -20453,6 +20740,13 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1173D;AHOM SIGN SECTION;Po;0;L;;;;;N;;;;;
1173E;AHOM SIGN RULAI;Po;0;L;;;;;N;;;;;
1173F;AHOM SYMBOL VI;So;0;L;;;;;N;;;;;
+11740;AHOM LETTER CA;Lo;0;L;;;;;N;;;;;
+11741;AHOM LETTER TTA;Lo;0;L;;;;;N;;;;;
+11742;AHOM LETTER TTHA;Lo;0;L;;;;;N;;;;;
+11743;AHOM LETTER DDA;Lo;0;L;;;;;N;;;;;
+11744;AHOM LETTER DDHA;Lo;0;L;;;;;N;;;;;
+11745;AHOM LETTER NNA;Lo;0;L;;;;;N;;;;;
+11746;AHOM LETTER LLA;Lo;0;L;;;;;N;;;;;
11800;DOGRA LETTER A;Lo;0;L;;;;;N;;;;;
11801;DOGRA LETTER AA;Lo;0;L;;;;;N;;;;;
11802;DOGRA LETTER I;Lo;0;L;;;;;N;;;;;
@@ -20889,6 +21183,22 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
11AA0;SOYOMBO HEAD MARK WITH MOON AND SUN;Po;0;L;;;;;N;;;;;
11AA1;SOYOMBO TERMINAL MARK-1;Po;0;L;;;;;N;;;;;
11AA2;SOYOMBO TERMINAL MARK-2;Po;0;L;;;;;N;;;;;
+11AB0;CANADIAN SYLLABICS NATTILIK HI;Lo;0;L;;;;;N;;;;;
+11AB1;CANADIAN SYLLABICS NATTILIK HII;Lo;0;L;;;;;N;;;;;
+11AB2;CANADIAN SYLLABICS NATTILIK HO;Lo;0;L;;;;;N;;;;;
+11AB3;CANADIAN SYLLABICS NATTILIK HOO;Lo;0;L;;;;;N;;;;;
+11AB4;CANADIAN SYLLABICS NATTILIK HA;Lo;0;L;;;;;N;;;;;
+11AB5;CANADIAN SYLLABICS NATTILIK HAA;Lo;0;L;;;;;N;;;;;
+11AB6;CANADIAN SYLLABICS NATTILIK SHRI;Lo;0;L;;;;;N;;;;;
+11AB7;CANADIAN SYLLABICS NATTILIK SHRII;Lo;0;L;;;;;N;;;;;
+11AB8;CANADIAN SYLLABICS NATTILIK SHRO;Lo;0;L;;;;;N;;;;;
+11AB9;CANADIAN SYLLABICS NATTILIK SHROO;Lo;0;L;;;;;N;;;;;
+11ABA;CANADIAN SYLLABICS NATTILIK SHRA;Lo;0;L;;;;;N;;;;;
+11ABB;CANADIAN SYLLABICS NATTILIK SHRAA;Lo;0;L;;;;;N;;;;;
+11ABC;CANADIAN SYLLABICS SPE;Lo;0;L;;;;;N;;;;;
+11ABD;CANADIAN SYLLABICS SPI;Lo;0;L;;;;;N;;;;;
+11ABE;CANADIAN SYLLABICS SPO;Lo;0;L;;;;;N;;;;;
+11ABF;CANADIAN SYLLABICS SPA;Lo;0;L;;;;;N;;;;;
11AC0;PAU CIN HAU LETTER PA;Lo;0;L;;;;;N;;;;;
11AC1;PAU CIN HAU LETTER KA;Lo;0;L;;;;;N;;;;;
11AC2;PAU CIN HAU LETTER LA;Lo;0;L;;;;;N;;;;;
@@ -22560,6 +22870,105 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
12541;CUNEIFORM SIGN ZA7;Lo;0;L;;;;;N;;;;;
12542;CUNEIFORM SIGN ZU OVER ZU PLUS SAR;Lo;0;L;;;;;N;;;;;
12543;CUNEIFORM SIGN ZU5 TIMES THREE DISH TENU;Lo;0;L;;;;;N;;;;;
+12F90;CYPRO-MINOAN SIGN CM001;Lo;0;L;;;;;N;;;;;
+12F91;CYPRO-MINOAN SIGN CM002;Lo;0;L;;;;;N;;;;;
+12F92;CYPRO-MINOAN SIGN CM004;Lo;0;L;;;;;N;;;;;
+12F93;CYPRO-MINOAN SIGN CM005;Lo;0;L;;;;;N;;;;;
+12F94;CYPRO-MINOAN SIGN CM006;Lo;0;L;;;;;N;;;;;
+12F95;CYPRO-MINOAN SIGN CM007;Lo;0;L;;;;;N;;;;;
+12F96;CYPRO-MINOAN SIGN CM008;Lo;0;L;;;;;N;;;;;
+12F97;CYPRO-MINOAN SIGN CM009;Lo;0;L;;;;;N;;;;;
+12F98;CYPRO-MINOAN SIGN CM010;Lo;0;L;;;;;N;;;;;
+12F99;CYPRO-MINOAN SIGN CM011;Lo;0;L;;;;;N;;;;;
+12F9A;CYPRO-MINOAN SIGN CM012;Lo;0;L;;;;;N;;;;;
+12F9B;CYPRO-MINOAN SIGN CM012B;Lo;0;L;;;;;N;;;;;
+12F9C;CYPRO-MINOAN SIGN CM013;Lo;0;L;;;;;N;;;;;
+12F9D;CYPRO-MINOAN SIGN CM015;Lo;0;L;;;;;N;;;;;
+12F9E;CYPRO-MINOAN SIGN CM017;Lo;0;L;;;;;N;;;;;
+12F9F;CYPRO-MINOAN SIGN CM019;Lo;0;L;;;;;N;;;;;
+12FA0;CYPRO-MINOAN SIGN CM021;Lo;0;L;;;;;N;;;;;
+12FA1;CYPRO-MINOAN SIGN CM023;Lo;0;L;;;;;N;;;;;
+12FA2;CYPRO-MINOAN SIGN CM024;Lo;0;L;;;;;N;;;;;
+12FA3;CYPRO-MINOAN SIGN CM025;Lo;0;L;;;;;N;;;;;
+12FA4;CYPRO-MINOAN SIGN CM026;Lo;0;L;;;;;N;;;;;
+12FA5;CYPRO-MINOAN SIGN CM027;Lo;0;L;;;;;N;;;;;
+12FA6;CYPRO-MINOAN SIGN CM028;Lo;0;L;;;;;N;;;;;
+12FA7;CYPRO-MINOAN SIGN CM029;Lo;0;L;;;;;N;;;;;
+12FA8;CYPRO-MINOAN SIGN CM030;Lo;0;L;;;;;N;;;;;
+12FA9;CYPRO-MINOAN SIGN CM033;Lo;0;L;;;;;N;;;;;
+12FAA;CYPRO-MINOAN SIGN CM034;Lo;0;L;;;;;N;;;;;
+12FAB;CYPRO-MINOAN SIGN CM035;Lo;0;L;;;;;N;;;;;
+12FAC;CYPRO-MINOAN SIGN CM036;Lo;0;L;;;;;N;;;;;
+12FAD;CYPRO-MINOAN SIGN CM037;Lo;0;L;;;;;N;;;;;
+12FAE;CYPRO-MINOAN SIGN CM038;Lo;0;L;;;;;N;;;;;
+12FAF;CYPRO-MINOAN SIGN CM039;Lo;0;L;;;;;N;;;;;
+12FB0;CYPRO-MINOAN SIGN CM040;Lo;0;L;;;;;N;;;;;
+12FB1;CYPRO-MINOAN SIGN CM041;Lo;0;L;;;;;N;;;;;
+12FB2;CYPRO-MINOAN SIGN CM044;Lo;0;L;;;;;N;;;;;
+12FB3;CYPRO-MINOAN SIGN CM046;Lo;0;L;;;;;N;;;;;
+12FB4;CYPRO-MINOAN SIGN CM047;Lo;0;L;;;;;N;;;;;
+12FB5;CYPRO-MINOAN SIGN CM049;Lo;0;L;;;;;N;;;;;
+12FB6;CYPRO-MINOAN SIGN CM050;Lo;0;L;;;;;N;;;;;
+12FB7;CYPRO-MINOAN SIGN CM051;Lo;0;L;;;;;N;;;;;
+12FB8;CYPRO-MINOAN SIGN CM052;Lo;0;L;;;;;N;;;;;
+12FB9;CYPRO-MINOAN SIGN CM053;Lo;0;L;;;;;N;;;;;
+12FBA;CYPRO-MINOAN SIGN CM054;Lo;0;L;;;;;N;;;;;
+12FBB;CYPRO-MINOAN SIGN CM055;Lo;0;L;;;;;N;;;;;
+12FBC;CYPRO-MINOAN SIGN CM056;Lo;0;L;;;;;N;;;;;
+12FBD;CYPRO-MINOAN SIGN CM058;Lo;0;L;;;;;N;;;;;
+12FBE;CYPRO-MINOAN SIGN CM059;Lo;0;L;;;;;N;;;;;
+12FBF;CYPRO-MINOAN SIGN CM060;Lo;0;L;;;;;N;;;;;
+12FC0;CYPRO-MINOAN SIGN CM061;Lo;0;L;;;;;N;;;;;
+12FC1;CYPRO-MINOAN SIGN CM062;Lo;0;L;;;;;N;;;;;
+12FC2;CYPRO-MINOAN SIGN CM063;Lo;0;L;;;;;N;;;;;
+12FC3;CYPRO-MINOAN SIGN CM064;Lo;0;L;;;;;N;;;;;
+12FC4;CYPRO-MINOAN SIGN CM066;Lo;0;L;;;;;N;;;;;
+12FC5;CYPRO-MINOAN SIGN CM067;Lo;0;L;;;;;N;;;;;
+12FC6;CYPRO-MINOAN SIGN CM068;Lo;0;L;;;;;N;;;;;
+12FC7;CYPRO-MINOAN SIGN CM069;Lo;0;L;;;;;N;;;;;
+12FC8;CYPRO-MINOAN SIGN CM070;Lo;0;L;;;;;N;;;;;
+12FC9;CYPRO-MINOAN SIGN CM071;Lo;0;L;;;;;N;;;;;
+12FCA;CYPRO-MINOAN SIGN CM072;Lo;0;L;;;;;N;;;;;
+12FCB;CYPRO-MINOAN SIGN CM073;Lo;0;L;;;;;N;;;;;
+12FCC;CYPRO-MINOAN SIGN CM074;Lo;0;L;;;;;N;;;;;
+12FCD;CYPRO-MINOAN SIGN CM075;Lo;0;L;;;;;N;;;;;
+12FCE;CYPRO-MINOAN SIGN CM075B;Lo;0;L;;;;;N;;;;;
+12FCF;CYPRO-MINOAN SIGN CM076;Lo;0;L;;;;;N;;;;;
+12FD0;CYPRO-MINOAN SIGN CM078;Lo;0;L;;;;;N;;;;;
+12FD1;CYPRO-MINOAN SIGN CM079;Lo;0;L;;;;;N;;;;;
+12FD2;CYPRO-MINOAN SIGN CM080;Lo;0;L;;;;;N;;;;;
+12FD3;CYPRO-MINOAN SIGN CM081;Lo;0;L;;;;;N;;;;;
+12FD4;CYPRO-MINOAN SIGN CM082;Lo;0;L;;;;;N;;;;;
+12FD5;CYPRO-MINOAN SIGN CM083;Lo;0;L;;;;;N;;;;;
+12FD6;CYPRO-MINOAN SIGN CM084;Lo;0;L;;;;;N;;;;;
+12FD7;CYPRO-MINOAN SIGN CM085;Lo;0;L;;;;;N;;;;;
+12FD8;CYPRO-MINOAN SIGN CM086;Lo;0;L;;;;;N;;;;;
+12FD9;CYPRO-MINOAN SIGN CM087;Lo;0;L;;;;;N;;;;;
+12FDA;CYPRO-MINOAN SIGN CM088;Lo;0;L;;;;;N;;;;;
+12FDB;CYPRO-MINOAN SIGN CM089;Lo;0;L;;;;;N;;;;;
+12FDC;CYPRO-MINOAN SIGN CM090;Lo;0;L;;;;;N;;;;;
+12FDD;CYPRO-MINOAN SIGN CM091;Lo;0;L;;;;;N;;;;;
+12FDE;CYPRO-MINOAN SIGN CM092;Lo;0;L;;;;;N;;;;;
+12FDF;CYPRO-MINOAN SIGN CM094;Lo;0;L;;;;;N;;;;;
+12FE0;CYPRO-MINOAN SIGN CM095;Lo;0;L;;;;;N;;;;;
+12FE1;CYPRO-MINOAN SIGN CM096;Lo;0;L;;;;;N;;;;;
+12FE2;CYPRO-MINOAN SIGN CM097;Lo;0;L;;;;;N;;;;;
+12FE3;CYPRO-MINOAN SIGN CM098;Lo;0;L;;;;;N;;;;;
+12FE4;CYPRO-MINOAN SIGN CM099;Lo;0;L;;;;;N;;;;;
+12FE5;CYPRO-MINOAN SIGN CM100;Lo;0;L;;;;;N;;;;;
+12FE6;CYPRO-MINOAN SIGN CM101;Lo;0;L;;;;;N;;;;;
+12FE7;CYPRO-MINOAN SIGN CM102;Lo;0;L;;;;;N;;;;;
+12FE8;CYPRO-MINOAN SIGN CM103;Lo;0;L;;;;;N;;;;;
+12FE9;CYPRO-MINOAN SIGN CM104;Lo;0;L;;;;;N;;;;;
+12FEA;CYPRO-MINOAN SIGN CM105;Lo;0;L;;;;;N;;;;;
+12FEB;CYPRO-MINOAN SIGN CM107;Lo;0;L;;;;;N;;;;;
+12FEC;CYPRO-MINOAN SIGN CM108;Lo;0;L;;;;;N;;;;;
+12FED;CYPRO-MINOAN SIGN CM109;Lo;0;L;;;;;N;;;;;
+12FEE;CYPRO-MINOAN SIGN CM110;Lo;0;L;;;;;N;;;;;
+12FEF;CYPRO-MINOAN SIGN CM112;Lo;0;L;;;;;N;;;;;
+12FF0;CYPRO-MINOAN SIGN CM114;Lo;0;L;;;;;N;;;;;
+12FF1;CYPRO-MINOAN SIGN CM301;Po;0;L;;;;;N;;;;;
+12FF2;CYPRO-MINOAN SIGN CM302;Po;0;L;;;;;N;;;;;
13000;EGYPTIAN HIEROGLYPH A001;Lo;0;L;;;;;N;;;;;
13001;EGYPTIAN HIEROGLYPH A002;Lo;0;L;;;;;N;;;;;
13002;EGYPTIAN HIEROGLYPH A003;Lo;0;L;;;;;N;;;;;
@@ -24835,6 +25244,95 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
16A69;MRO DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
16A6E;MRO DANDA;Po;0;L;;;;;N;;;;;
16A6F;MRO DOUBLE DANDA;Po;0;L;;;;;N;;;;;
+16A70;TANGSA LETTER OZ;Lo;0;L;;;;;N;;;;;
+16A71;TANGSA LETTER OC;Lo;0;L;;;;;N;;;;;
+16A72;TANGSA LETTER OQ;Lo;0;L;;;;;N;;;;;
+16A73;TANGSA LETTER OX;Lo;0;L;;;;;N;;;;;
+16A74;TANGSA LETTER AZ;Lo;0;L;;;;;N;;;;;
+16A75;TANGSA LETTER AC;Lo;0;L;;;;;N;;;;;
+16A76;TANGSA LETTER AQ;Lo;0;L;;;;;N;;;;;
+16A77;TANGSA LETTER AX;Lo;0;L;;;;;N;;;;;
+16A78;TANGSA LETTER VZ;Lo;0;L;;;;;N;;;;;
+16A79;TANGSA LETTER VC;Lo;0;L;;;;;N;;;;;
+16A7A;TANGSA LETTER VQ;Lo;0;L;;;;;N;;;;;
+16A7B;TANGSA LETTER VX;Lo;0;L;;;;;N;;;;;
+16A7C;TANGSA LETTER EZ;Lo;0;L;;;;;N;;;;;
+16A7D;TANGSA LETTER EC;Lo;0;L;;;;;N;;;;;
+16A7E;TANGSA LETTER EQ;Lo;0;L;;;;;N;;;;;
+16A7F;TANGSA LETTER EX;Lo;0;L;;;;;N;;;;;
+16A80;TANGSA LETTER IZ;Lo;0;L;;;;;N;;;;;
+16A81;TANGSA LETTER IC;Lo;0;L;;;;;N;;;;;
+16A82;TANGSA LETTER IQ;Lo;0;L;;;;;N;;;;;
+16A83;TANGSA LETTER IX;Lo;0;L;;;;;N;;;;;
+16A84;TANGSA LETTER UZ;Lo;0;L;;;;;N;;;;;
+16A85;TANGSA LETTER UC;Lo;0;L;;;;;N;;;;;
+16A86;TANGSA LETTER UQ;Lo;0;L;;;;;N;;;;;
+16A87;TANGSA LETTER UX;Lo;0;L;;;;;N;;;;;
+16A88;TANGSA LETTER AWZ;Lo;0;L;;;;;N;;;;;
+16A89;TANGSA LETTER AWC;Lo;0;L;;;;;N;;;;;
+16A8A;TANGSA LETTER AWQ;Lo;0;L;;;;;N;;;;;
+16A8B;TANGSA LETTER AWX;Lo;0;L;;;;;N;;;;;
+16A8C;TANGSA LETTER UIZ;Lo;0;L;;;;;N;;;;;
+16A8D;TANGSA LETTER UIC;Lo;0;L;;;;;N;;;;;
+16A8E;TANGSA LETTER UIQ;Lo;0;L;;;;;N;;;;;
+16A8F;TANGSA LETTER UIX;Lo;0;L;;;;;N;;;;;
+16A90;TANGSA LETTER FINAL NG;Lo;0;L;;;;;N;;;;;
+16A91;TANGSA LETTER LONG UEX;Lo;0;L;;;;;N;;;;;
+16A92;TANGSA LETTER SHORT UEZ;Lo;0;L;;;;;N;;;;;
+16A93;TANGSA LETTER SHORT AWX;Lo;0;L;;;;;N;;;;;
+16A94;TANGSA LETTER UEC;Lo;0;L;;;;;N;;;;;
+16A95;TANGSA LETTER UEZ;Lo;0;L;;;;;N;;;;;
+16A96;TANGSA LETTER UEQ;Lo;0;L;;;;;N;;;;;
+16A97;TANGSA LETTER UEX;Lo;0;L;;;;;N;;;;;
+16A98;TANGSA LETTER UIUZ;Lo;0;L;;;;;N;;;;;
+16A99;TANGSA LETTER UIUC;Lo;0;L;;;;;N;;;;;
+16A9A;TANGSA LETTER UIUQ;Lo;0;L;;;;;N;;;;;
+16A9B;TANGSA LETTER UIUX;Lo;0;L;;;;;N;;;;;
+16A9C;TANGSA LETTER MZ;Lo;0;L;;;;;N;;;;;
+16A9D;TANGSA LETTER MC;Lo;0;L;;;;;N;;;;;
+16A9E;TANGSA LETTER MQ;Lo;0;L;;;;;N;;;;;
+16A9F;TANGSA LETTER MX;Lo;0;L;;;;;N;;;;;
+16AA0;TANGSA LETTER KA;Lo;0;L;;;;;N;;;;;
+16AA1;TANGSA LETTER KHA;Lo;0;L;;;;;N;;;;;
+16AA2;TANGSA LETTER GA;Lo;0;L;;;;;N;;;;;
+16AA3;TANGSA LETTER NGA;Lo;0;L;;;;;N;;;;;
+16AA4;TANGSA LETTER SA;Lo;0;L;;;;;N;;;;;
+16AA5;TANGSA LETTER YA;Lo;0;L;;;;;N;;;;;
+16AA6;TANGSA LETTER WA;Lo;0;L;;;;;N;;;;;
+16AA7;TANGSA LETTER PA;Lo;0;L;;;;;N;;;;;
+16AA8;TANGSA LETTER NYA;Lo;0;L;;;;;N;;;;;
+16AA9;TANGSA LETTER PHA;Lo;0;L;;;;;N;;;;;
+16AAA;TANGSA LETTER BA;Lo;0;L;;;;;N;;;;;
+16AAB;TANGSA LETTER MA;Lo;0;L;;;;;N;;;;;
+16AAC;TANGSA LETTER NA;Lo;0;L;;;;;N;;;;;
+16AAD;TANGSA LETTER HA;Lo;0;L;;;;;N;;;;;
+16AAE;TANGSA LETTER LA;Lo;0;L;;;;;N;;;;;
+16AAF;TANGSA LETTER HTA;Lo;0;L;;;;;N;;;;;
+16AB0;TANGSA LETTER TA;Lo;0;L;;;;;N;;;;;
+16AB1;TANGSA LETTER DA;Lo;0;L;;;;;N;;;;;
+16AB2;TANGSA LETTER RA;Lo;0;L;;;;;N;;;;;
+16AB3;TANGSA LETTER NHA;Lo;0;L;;;;;N;;;;;
+16AB4;TANGSA LETTER SHA;Lo;0;L;;;;;N;;;;;
+16AB5;TANGSA LETTER CA;Lo;0;L;;;;;N;;;;;
+16AB6;TANGSA LETTER TSA;Lo;0;L;;;;;N;;;;;
+16AB7;TANGSA LETTER GHA;Lo;0;L;;;;;N;;;;;
+16AB8;TANGSA LETTER HTTA;Lo;0;L;;;;;N;;;;;
+16AB9;TANGSA LETTER THA;Lo;0;L;;;;;N;;;;;
+16ABA;TANGSA LETTER XA;Lo;0;L;;;;;N;;;;;
+16ABB;TANGSA LETTER FA;Lo;0;L;;;;;N;;;;;
+16ABC;TANGSA LETTER DHA;Lo;0;L;;;;;N;;;;;
+16ABD;TANGSA LETTER CHA;Lo;0;L;;;;;N;;;;;
+16ABE;TANGSA LETTER ZA;Lo;0;L;;;;;N;;;;;
+16AC0;TANGSA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+16AC1;TANGSA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+16AC2;TANGSA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+16AC3;TANGSA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+16AC4;TANGSA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+16AC5;TANGSA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+16AC6;TANGSA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+16AC7;TANGSA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+16AC8;TANGSA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+16AC9;TANGSA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
16AD0;BASSA VAH LETTER ENNI;Lo;0;L;;;;;N;;;;;
16AD1;BASSA VAH LETTER KA;Lo;0;L;;;;;N;;;;;
16AD2;BASSA VAH LETTER SE;Lo;0;L;;;;;N;;;;;
@@ -26487,6 +26985,19 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
18CD5;KHITAN SMALL SCRIPT CHARACTER-18CD5;Lo;0;L;;;;;N;;;;;
18D00;<Tangut Ideograph Supplement, First>;Lo;0;L;;;;;N;;;;;
18D08;<Tangut Ideograph Supplement, Last>;Lo;0;L;;;;;N;;;;;
+1AFF0;KATAKANA LETTER MINNAN TONE-2;Lm;0;L;;;;;N;;;;;
+1AFF1;KATAKANA LETTER MINNAN TONE-3;Lm;0;L;;;;;N;;;;;
+1AFF2;KATAKANA LETTER MINNAN TONE-4;Lm;0;L;;;;;N;;;;;
+1AFF3;KATAKANA LETTER MINNAN TONE-5;Lm;0;L;;;;;N;;;;;
+1AFF5;KATAKANA LETTER MINNAN TONE-7;Lm;0;L;;;;;N;;;;;
+1AFF6;KATAKANA LETTER MINNAN TONE-8;Lm;0;L;;;;;N;;;;;
+1AFF7;KATAKANA LETTER MINNAN NASALIZED TONE-1;Lm;0;L;;;;;N;;;;;
+1AFF8;KATAKANA LETTER MINNAN NASALIZED TONE-2;Lm;0;L;;;;;N;;;;;
+1AFF9;KATAKANA LETTER MINNAN NASALIZED TONE-3;Lm;0;L;;;;;N;;;;;
+1AFFA;KATAKANA LETTER MINNAN NASALIZED TONE-4;Lm;0;L;;;;;N;;;;;
+1AFFB;KATAKANA LETTER MINNAN NASALIZED TONE-5;Lm;0;L;;;;;N;;;;;
+1AFFD;KATAKANA LETTER MINNAN NASALIZED TONE-7;Lm;0;L;;;;;N;;;;;
+1AFFE;KATAKANA LETTER MINNAN NASALIZED TONE-8;Lm;0;L;;;;;N;;;;;
1B000;KATAKANA LETTER ARCHAIC E;Lo;0;L;;;;;N;;;;;
1B001;HIRAGANA LETTER ARCHAIC YE;Lo;0;L;;;;;N;;;;;
1B002;HENTAIGANA LETTER A-1;Lo;0;L;;;;;N;;;;;
@@ -26774,6 +27285,10 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1B11C;HENTAIGANA LETTER WO-7;Lo;0;L;;;;;N;;;;;
1B11D;HENTAIGANA LETTER N-MU-MO-1;Lo;0;L;;;;;N;;;;;
1B11E;HENTAIGANA LETTER N-MU-MO-2;Lo;0;L;;;;;N;;;;;
+1B11F;HIRAGANA LETTER ARCHAIC WU;Lo;0;L;;;;;N;;;;;
+1B120;KATAKANA LETTER ARCHAIC YI;Lo;0;L;;;;;N;;;;;
+1B121;KATAKANA LETTER ARCHAIC YE;Lo;0;L;;;;;N;;;;;
+1B122;KATAKANA LETTER ARCHAIC WU;Lo;0;L;;;;;N;;;;;
1B150;HIRAGANA LETTER SMALL WI;Lo;0;L;;;;;N;;;;;
1B151;HIRAGANA LETTER SMALL WE;Lo;0;L;;;;;N;;;;;
1B152;HIRAGANA LETTER SMALL WO;Lo;0;L;;;;;N;;;;;
@@ -27324,6 +27839,191 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1BCA1;SHORTHAND FORMAT CONTINUING OVERLAP;Cf;0;BN;;;;;N;;;;;
1BCA2;SHORTHAND FORMAT DOWN STEP;Cf;0;BN;;;;;N;;;;;
1BCA3;SHORTHAND FORMAT UP STEP;Cf;0;BN;;;;;N;;;;;
+1CF00;ZNAMENNY COMBINING MARK GORAZDO NIZKO S KRYZHEM ON LEFT;Mn;0;NSM;;;;;N;;;;;
+1CF01;ZNAMENNY COMBINING MARK NIZKO S KRYZHEM ON LEFT;Mn;0;NSM;;;;;N;;;;;
+1CF02;ZNAMENNY COMBINING MARK TSATA ON LEFT;Mn;0;NSM;;;;;N;;;;;
+1CF03;ZNAMENNY COMBINING MARK GORAZDO NIZKO ON LEFT;Mn;0;NSM;;;;;N;;;;;
+1CF04;ZNAMENNY COMBINING MARK NIZKO ON LEFT;Mn;0;NSM;;;;;N;;;;;
+1CF05;ZNAMENNY COMBINING MARK SREDNE ON LEFT;Mn;0;NSM;;;;;N;;;;;
+1CF06;ZNAMENNY COMBINING MARK MALO POVYSHE ON LEFT;Mn;0;NSM;;;;;N;;;;;
+1CF07;ZNAMENNY COMBINING MARK POVYSHE ON LEFT;Mn;0;NSM;;;;;N;;;;;
+1CF08;ZNAMENNY COMBINING MARK VYSOKO ON LEFT;Mn;0;NSM;;;;;N;;;;;
+1CF09;ZNAMENNY COMBINING MARK MALO POVYSHE S KHOKHLOM ON LEFT;Mn;0;NSM;;;;;N;;;;;
+1CF0A;ZNAMENNY COMBINING MARK POVYSHE S KHOKHLOM ON LEFT;Mn;0;NSM;;;;;N;;;;;
+1CF0B;ZNAMENNY COMBINING MARK VYSOKO S KHOKHLOM ON LEFT;Mn;0;NSM;;;;;N;;;;;
+1CF0C;ZNAMENNY COMBINING MARK GORAZDO NIZKO S KRYZHEM ON RIGHT;Mn;0;NSM;;;;;N;;;;;
+1CF0D;ZNAMENNY COMBINING MARK NIZKO S KRYZHEM ON RIGHT;Mn;0;NSM;;;;;N;;;;;
+1CF0E;ZNAMENNY COMBINING MARK TSATA ON RIGHT;Mn;0;NSM;;;;;N;;;;;
+1CF0F;ZNAMENNY COMBINING MARK GORAZDO NIZKO ON RIGHT;Mn;0;NSM;;;;;N;;;;;
+1CF10;ZNAMENNY COMBINING MARK NIZKO ON RIGHT;Mn;0;NSM;;;;;N;;;;;
+1CF11;ZNAMENNY COMBINING MARK SREDNE ON RIGHT;Mn;0;NSM;;;;;N;;;;;
+1CF12;ZNAMENNY COMBINING MARK MALO POVYSHE ON RIGHT;Mn;0;NSM;;;;;N;;;;;
+1CF13;ZNAMENNY COMBINING MARK POVYSHE ON RIGHT;Mn;0;NSM;;;;;N;;;;;
+1CF14;ZNAMENNY COMBINING MARK VYSOKO ON RIGHT;Mn;0;NSM;;;;;N;;;;;
+1CF15;ZNAMENNY COMBINING MARK MALO POVYSHE S KHOKHLOM ON RIGHT;Mn;0;NSM;;;;;N;;;;;
+1CF16;ZNAMENNY COMBINING MARK POVYSHE S KHOKHLOM ON RIGHT;Mn;0;NSM;;;;;N;;;;;
+1CF17;ZNAMENNY COMBINING MARK VYSOKO S KHOKHLOM ON RIGHT;Mn;0;NSM;;;;;N;;;;;
+1CF18;ZNAMENNY COMBINING MARK TSATA S KRYZHEM;Mn;0;NSM;;;;;N;;;;;
+1CF19;ZNAMENNY COMBINING MARK MALO POVYSHE S KRYZHEM;Mn;0;NSM;;;;;N;;;;;
+1CF1A;ZNAMENNY COMBINING MARK STRANNO MALO POVYSHE;Mn;0;NSM;;;;;N;;;;;
+1CF1B;ZNAMENNY COMBINING MARK POVYSHE S KRYZHEM;Mn;0;NSM;;;;;N;;;;;
+1CF1C;ZNAMENNY COMBINING MARK POVYSHE STRANNO;Mn;0;NSM;;;;;N;;;;;
+1CF1D;ZNAMENNY COMBINING MARK VYSOKO S KRYZHEM;Mn;0;NSM;;;;;N;;;;;
+1CF1E;ZNAMENNY COMBINING MARK MALO POVYSHE STRANNO;Mn;0;NSM;;;;;N;;;;;
+1CF1F;ZNAMENNY COMBINING MARK GORAZDO VYSOKO;Mn;0;NSM;;;;;N;;;;;
+1CF20;ZNAMENNY COMBINING MARK ZELO;Mn;0;NSM;;;;;N;;;;;
+1CF21;ZNAMENNY COMBINING MARK ON;Mn;0;NSM;;;;;N;;;;;
+1CF22;ZNAMENNY COMBINING MARK RAVNO;Mn;0;NSM;;;;;N;;;;;
+1CF23;ZNAMENNY COMBINING MARK TIKHAYA;Mn;0;NSM;;;;;N;;;;;
+1CF24;ZNAMENNY COMBINING MARK BORZAYA;Mn;0;NSM;;;;;N;;;;;
+1CF25;ZNAMENNY COMBINING MARK UDARKA;Mn;0;NSM;;;;;N;;;;;
+1CF26;ZNAMENNY COMBINING MARK PODVERTKA;Mn;0;NSM;;;;;N;;;;;
+1CF27;ZNAMENNY COMBINING MARK LOMKA;Mn;0;NSM;;;;;N;;;;;
+1CF28;ZNAMENNY COMBINING MARK KUPNAYA;Mn;0;NSM;;;;;N;;;;;
+1CF29;ZNAMENNY COMBINING MARK KACHKA;Mn;0;NSM;;;;;N;;;;;
+1CF2A;ZNAMENNY COMBINING MARK ZEVOK;Mn;0;NSM;;;;;N;;;;;
+1CF2B;ZNAMENNY COMBINING MARK SKOBA;Mn;0;NSM;;;;;N;;;;;
+1CF2C;ZNAMENNY COMBINING MARK RAZSEKA;Mn;0;NSM;;;;;N;;;;;
+1CF2D;ZNAMENNY COMBINING MARK KRYZH ON LEFT;Mn;0;NSM;;;;;N;;;;;
+1CF30;ZNAMENNY COMBINING TONAL RANGE MARK MRACHNO;Mn;0;NSM;;;;;N;;;;;
+1CF31;ZNAMENNY COMBINING TONAL RANGE MARK SVETLO;Mn;0;NSM;;;;;N;;;;;
+1CF32;ZNAMENNY COMBINING TONAL RANGE MARK TRESVETLO;Mn;0;NSM;;;;;N;;;;;
+1CF33;ZNAMENNY COMBINING MARK ZADERZHKA;Mn;0;NSM;;;;;N;;;;;
+1CF34;ZNAMENNY COMBINING MARK DEMESTVENNY ZADERZHKA;Mn;0;NSM;;;;;N;;;;;
+1CF35;ZNAMENNY COMBINING MARK OTSECHKA;Mn;0;NSM;;;;;N;;;;;
+1CF36;ZNAMENNY COMBINING MARK PODCHASHIE;Mn;0;NSM;;;;;N;;;;;
+1CF37;ZNAMENNY COMBINING MARK PODCHASHIE WITH VERTICAL STROKE;Mn;0;NSM;;;;;N;;;;;
+1CF38;ZNAMENNY COMBINING MARK CHASHKA;Mn;0;NSM;;;;;N;;;;;
+1CF39;ZNAMENNY COMBINING MARK CHASHKA POLNAYA;Mn;0;NSM;;;;;N;;;;;
+1CF3A;ZNAMENNY COMBINING MARK OBLACHKO;Mn;0;NSM;;;;;N;;;;;
+1CF3B;ZNAMENNY COMBINING MARK SOROCHYA NOZHKA;Mn;0;NSM;;;;;N;;;;;
+1CF3C;ZNAMENNY COMBINING MARK TOCHKA;Mn;0;NSM;;;;;N;;;;;
+1CF3D;ZNAMENNY COMBINING MARK DVOETOCHIE;Mn;0;NSM;;;;;N;;;;;
+1CF3E;ZNAMENNY COMBINING ATTACHING VERTICAL OMET;Mn;0;NSM;;;;;N;;;;;
+1CF3F;ZNAMENNY COMBINING MARK CURVED OMET;Mn;0;NSM;;;;;N;;;;;
+1CF40;ZNAMENNY COMBINING MARK KRYZH;Mn;0;NSM;;;;;N;;;;;
+1CF41;ZNAMENNY COMBINING LOWER TONAL RANGE INDICATOR;Mn;0;NSM;;;;;N;;;;;
+1CF42;ZNAMENNY PRIZNAK MODIFIER LEVEL-2;Mn;0;NSM;;;;;N;;;;;
+1CF43;ZNAMENNY PRIZNAK MODIFIER LEVEL-3;Mn;0;NSM;;;;;N;;;;;
+1CF44;ZNAMENNY PRIZNAK MODIFIER DIRECTION FLIP;Mn;0;NSM;;;;;N;;;;;
+1CF45;ZNAMENNY PRIZNAK MODIFIER KRYZH;Mn;0;NSM;;;;;N;;;;;
+1CF46;ZNAMENNY PRIZNAK MODIFIER ROG;Mn;0;NSM;;;;;N;;;;;
+1CF50;ZNAMENNY NEUME KRYUK;So;0;L;;;;;N;;;;;
+1CF51;ZNAMENNY NEUME KRYUK TIKHY;So;0;L;;;;;N;;;;;
+1CF52;ZNAMENNY NEUME PARAKLIT;So;0;L;;;;;N;;;;;
+1CF53;ZNAMENNY NEUME DVA V CHELNU;So;0;L;;;;;N;;;;;
+1CF54;ZNAMENNY NEUME KLYUCH;So;0;L;;;;;N;;;;;
+1CF55;ZNAMENNY NEUME ZANOZHEK;So;0;L;;;;;N;;;;;
+1CF56;ZNAMENNY NEUME STOPITSA;So;0;L;;;;;N;;;;;
+1CF57;ZNAMENNY NEUME STOPITSA S OCHKOM;So;0;L;;;;;N;;;;;
+1CF58;ZNAMENNY NEUME PEREVODKA;So;0;L;;;;;N;;;;;
+1CF59;ZNAMENNY NEUME PEREVODKA NEPOSTOYANNAYA;So;0;L;;;;;N;;;;;
+1CF5A;ZNAMENNY NEUME STOPITSA WITH SOROCHYA NOZHKA;So;0;L;;;;;N;;;;;
+1CF5B;ZNAMENNY NEUME CHELYUSTKA;So;0;L;;;;;N;;;;;
+1CF5C;ZNAMENNY NEUME PALKA;So;0;L;;;;;N;;;;;
+1CF5D;ZNAMENNY NEUME ZAPYATAYA;So;0;L;;;;;N;;;;;
+1CF5E;ZNAMENNY NEUME GOLUBCHIK BORZY;So;0;L;;;;;N;;;;;
+1CF5F;ZNAMENNY NEUME GOLUBCHIK TIKHY;So;0;L;;;;;N;;;;;
+1CF60;ZNAMENNY NEUME GOLUBCHIK MRACHNY;So;0;L;;;;;N;;;;;
+1CF61;ZNAMENNY NEUME GOLUBCHIK SVETLY;So;0;L;;;;;N;;;;;
+1CF62;ZNAMENNY NEUME GOLUBCHIK TRESVETLY;So;0;L;;;;;N;;;;;
+1CF63;ZNAMENNY NEUME VRAKHIYA PROSTAYA;So;0;L;;;;;N;;;;;
+1CF64;ZNAMENNY NEUME VRAKHIYA MRACHNAYA;So;0;L;;;;;N;;;;;
+1CF65;ZNAMENNY NEUME VRAKHIYA SVETLAYA;So;0;L;;;;;N;;;;;
+1CF66;ZNAMENNY NEUME VRAKHIYA TRESVETLAYA;So;0;L;;;;;N;;;;;
+1CF67;ZNAMENNY NEUME VRAKHIYA KLYUCHEVAYA PROSTAYA;So;0;L;;;;;N;;;;;
+1CF68;ZNAMENNY NEUME VRAKHIYA KLYUCHEVAYA MRACHNAYA;So;0;L;;;;;N;;;;;
+1CF69;ZNAMENNY NEUME VRAKHIYA KLYUCHEVAYA SVETLAYA;So;0;L;;;;;N;;;;;
+1CF6A;ZNAMENNY NEUME VRAKHIYA KLYUCHEVAYA TRESVETLAYA;So;0;L;;;;;N;;;;;
+1CF6B;ZNAMENNY NEUME DOUBLE ZAPYATAYA;So;0;L;;;;;N;;;;;
+1CF6C;ZNAMENNY NEUME REVERSED CHELYUSTKA;So;0;L;;;;;N;;;;;
+1CF6D;ZNAMENNY NEUME DERBITSA;So;0;L;;;;;N;;;;;
+1CF6E;ZNAMENNY NEUME KHAMILO;So;0;L;;;;;N;;;;;
+1CF6F;ZNAMENNY NEUME CHASHKA;So;0;L;;;;;N;;;;;
+1CF70;ZNAMENNY NEUME PODCHASHIE;So;0;L;;;;;N;;;;;
+1CF71;ZNAMENNY NEUME SKAMEYTSA MRACHNAYA;So;0;L;;;;;N;;;;;
+1CF72;ZNAMENNY NEUME SKAMEYTSA SVETLAYA;So;0;L;;;;;N;;;;;
+1CF73;ZNAMENNY NEUME SKAMEYTSA TRESVETLAYA;So;0;L;;;;;N;;;;;
+1CF74;ZNAMENNY NEUME SKAMEYTSA TIKHAYA;So;0;L;;;;;N;;;;;
+1CF75;ZNAMENNY NEUME DEMESTVENNY KLYUCH;So;0;L;;;;;N;;;;;
+1CF76;ZNAMENNY NEUME SKAMEYTSA KLYUCHEVAYA SVETLAYA;So;0;L;;;;;N;;;;;
+1CF77;ZNAMENNY NEUME SKAMEYTSA KLYUCHENEPOSTOYANNAYA;So;0;L;;;;;N;;;;;
+1CF78;ZNAMENNY NEUME SKAMEYTSA KLYUCHEVAYA TIKHAYA;So;0;L;;;;;N;;;;;
+1CF79;ZNAMENNY NEUME SKAMEYTSA DVOECHELNAYA PROSTAYA;So;0;L;;;;;N;;;;;
+1CF7A;ZNAMENNY NEUME SKAMEYTSA DVOECHELNAYA SVETLAYA;So;0;L;;;;;N;;;;;
+1CF7B;ZNAMENNY NEUME SKAMEYTSA DVOECHELNAYA NEPOSTOYANNAYA;So;0;L;;;;;N;;;;;
+1CF7C;ZNAMENNY NEUME SKAMEYTSA DVOECHELNAYA KLYUCHEVAYA;So;0;L;;;;;N;;;;;
+1CF7D;ZNAMENNY NEUME SLOZHITIE;So;0;L;;;;;N;;;;;
+1CF7E;ZNAMENNY NEUME SLOZHITIE S ZAPYATOY;So;0;L;;;;;N;;;;;
+1CF7F;ZNAMENNY NEUME SLOZHITIE ZAKRYTOE;So;0;L;;;;;N;;;;;
+1CF80;ZNAMENNY NEUME SLOZHITIE S KRYZHEM;So;0;L;;;;;N;;;;;
+1CF81;ZNAMENNY NEUME KRYZH;So;0;L;;;;;N;;;;;
+1CF82;ZNAMENNY NEUME ROG;So;0;L;;;;;N;;;;;
+1CF83;ZNAMENNY NEUME FITA;So;0;L;;;;;N;;;;;
+1CF84;ZNAMENNY NEUME KOBYLA;So;0;L;;;;;N;;;;;
+1CF85;ZNAMENNY NEUME ZMEYTSA;So;0;L;;;;;N;;;;;
+1CF86;ZNAMENNY NEUME STATYA;So;0;L;;;;;N;;;;;
+1CF87;ZNAMENNY NEUME STATYA S ZAPYATOY;So;0;L;;;;;N;;;;;
+1CF88;ZNAMENNY NEUME STATYA S KRYZHEM;So;0;L;;;;;N;;;;;
+1CF89;ZNAMENNY NEUME STATYA S ZAPYATOY I KRYZHEM;So;0;L;;;;;N;;;;;
+1CF8A;ZNAMENNY NEUME STATYA S KRYZHEM I ZAPYATOY;So;0;L;;;;;N;;;;;
+1CF8B;ZNAMENNY NEUME STATYA ZAKRYTAYA;So;0;L;;;;;N;;;;;
+1CF8C;ZNAMENNY NEUME STATYA ZAKRYTAYA S ZAPYATOY;So;0;L;;;;;N;;;;;
+1CF8D;ZNAMENNY NEUME STATYA S ROGOM;So;0;L;;;;;N;;;;;
+1CF8E;ZNAMENNY NEUME STATYA S DVUMYA ZAPYATYMI;So;0;L;;;;;N;;;;;
+1CF8F;ZNAMENNY NEUME STATYA S ZAPYATOY I PODCHASHIEM;So;0;L;;;;;N;;;;;
+1CF90;ZNAMENNY NEUME POLKULIZMY;So;0;L;;;;;N;;;;;
+1CF91;ZNAMENNY NEUME STATYA NEPOSTOYANNAYA;So;0;L;;;;;N;;;;;
+1CF92;ZNAMENNY NEUME STRELA PROSTAYA;So;0;L;;;;;N;;;;;
+1CF93;ZNAMENNY NEUME STRELA MRACHNOTIKHAYA;So;0;L;;;;;N;;;;;
+1CF94;ZNAMENNY NEUME STRELA KRYZHEVAYA;So;0;L;;;;;N;;;;;
+1CF95;ZNAMENNY NEUME STRELA POLUPOVODNAYA;So;0;L;;;;;N;;;;;
+1CF96;ZNAMENNY NEUME STRELA POVODNAYA;So;0;L;;;;;N;;;;;
+1CF97;ZNAMENNY NEUME STRELA NEPOSTOYANNAYA;So;0;L;;;;;N;;;;;
+1CF98;ZNAMENNY NEUME STRELA KLYUCHEPOVODNAYA;So;0;L;;;;;N;;;;;
+1CF99;ZNAMENNY NEUME STRELA KLYUCHENEPOSTOYANNAYA;So;0;L;;;;;N;;;;;
+1CF9A;ZNAMENNY NEUME STRELA TIKHAYA PUTNAYA;So;0;L;;;;;N;;;;;
+1CF9B;ZNAMENNY NEUME STRELA DVOECHELNAYA;So;0;L;;;;;N;;;;;
+1CF9C;ZNAMENNY NEUME STRELA DVOECHELNOKRYZHEVAYA;So;0;L;;;;;N;;;;;
+1CF9D;ZNAMENNY NEUME STRELA DVOECHELNOPOVODNAYA;So;0;L;;;;;N;;;;;
+1CF9E;ZNAMENNY NEUME STRELA DVOECHELNAYA KLYUCHEVAYA;So;0;L;;;;;N;;;;;
+1CF9F;ZNAMENNY NEUME STRELA DVOECHELNOPOVODNAYA KLYUCHEVAYA;So;0;L;;;;;N;;;;;
+1CFA0;ZNAMENNY NEUME STRELA GROMNAYA WITH SINGLE ZAPYATAYA;So;0;L;;;;;N;;;;;
+1CFA1;ZNAMENNY NEUME STRELA GROMOPOVODNAYA WITH SINGLE ZAPYATAYA;So;0;L;;;;;N;;;;;
+1CFA2;ZNAMENNY NEUME STRELA GROMNAYA;So;0;L;;;;;N;;;;;
+1CFA3;ZNAMENNY NEUME STRELA GROMOPOVODNAYA;So;0;L;;;;;N;;;;;
+1CFA4;ZNAMENNY NEUME STRELA GROMOPOVODNAYA WITH DOUBLE ZAPYATAYA;So;0;L;;;;;N;;;;;
+1CFA5;ZNAMENNY NEUME STRELA GROMOKRYZHEVAYA;So;0;L;;;;;N;;;;;
+1CFA6;ZNAMENNY NEUME STRELA GROMOKRYZHEVAYA POVODNAYA;So;0;L;;;;;N;;;;;
+1CFA7;ZNAMENNY NEUME MECHIK;So;0;L;;;;;N;;;;;
+1CFA8;ZNAMENNY NEUME MECHIK POVODNY;So;0;L;;;;;N;;;;;
+1CFA9;ZNAMENNY NEUME MECHIK KLYUCHEVOY;So;0;L;;;;;N;;;;;
+1CFAA;ZNAMENNY NEUME MECHIK KLYUCHEPOVODNY;So;0;L;;;;;N;;;;;
+1CFAB;ZNAMENNY NEUME MECHIK KLYUCHENEPOSTOYANNY;So;0;L;;;;;N;;;;;
+1CFAC;ZNAMENNY NEUME STRELA TRYASOGLASNAYA;So;0;L;;;;;N;;;;;
+1CFAD;ZNAMENNY NEUME STRELA TRYASOPOVODNAYA;So;0;L;;;;;N;;;;;
+1CFAE;ZNAMENNY NEUME STRELA TRYASOSTRELNAYA;So;0;L;;;;;N;;;;;
+1CFAF;ZNAMENNY NEUME OSOKA;So;0;L;;;;;N;;;;;
+1CFB0;ZNAMENNY NEUME OSOKA SVETLAYA;So;0;L;;;;;N;;;;;
+1CFB1;ZNAMENNY NEUME OSOKA TRESVETLAYA;So;0;L;;;;;N;;;;;
+1CFB2;ZNAMENNY NEUME OSOKA KRYUKOVAYA SVETLAYA;So;0;L;;;;;N;;;;;
+1CFB3;ZNAMENNY NEUME OSOKA KLYUCHEVAYA SVETLAYA;So;0;L;;;;;N;;;;;
+1CFB4;ZNAMENNY NEUME OSOKA KLYUCHEVAYA NEPOSTOYANNAYA;So;0;L;;;;;N;;;;;
+1CFB5;ZNAMENNY NEUME STRELA KRYUKOVAYA;So;0;L;;;;;N;;;;;
+1CFB6;ZNAMENNY NEUME STRELA KRYUKOVAYA POVODNAYA;So;0;L;;;;;N;;;;;
+1CFB7;ZNAMENNY NEUME STRELA KRYUKOVAYA GROMNAYA WITH SINGLE ZAPYATAYA;So;0;L;;;;;N;;;;;
+1CFB8;ZNAMENNY NEUME STRELA KRYUKOVAYA GROMOPOVODNAYA WITH SINGLE ZAPYATAYA;So;0;L;;;;;N;;;;;
+1CFB9;ZNAMENNY NEUME STRELA KRYUKOVAYA GROMNAYA;So;0;L;;;;;N;;;;;
+1CFBA;ZNAMENNY NEUME STRELA KRYUKOVAYA GROMOPOVODNAYA;So;0;L;;;;;N;;;;;
+1CFBB;ZNAMENNY NEUME STRELA KRYUKOVAYA GROMOPOVODNAYA WITH DOUBLE ZAPYATAYA;So;0;L;;;;;N;;;;;
+1CFBC;ZNAMENNY NEUME STRELA KRYUKOVAYA GROMOKRYZHEVAYA;So;0;L;;;;;N;;;;;
+1CFBD;ZNAMENNY NEUME STRELA KRYUKOVAYA GROMOKRYZHEVAYA POVODNAYA;So;0;L;;;;;N;;;;;
+1CFBE;ZNAMENNY NEUME STRELA KRYUKOVAYA TRYASKA;So;0;L;;;;;N;;;;;
+1CFBF;ZNAMENNY NEUME KUFISMA;So;0;L;;;;;N;;;;;
+1CFC0;ZNAMENNY NEUME OBLAKO;So;0;L;;;;;N;;;;;
+1CFC1;ZNAMENNY NEUME DUDA;So;0;L;;;;;N;;;;;
+1CFC2;ZNAMENNY NEUME NEMKA;So;0;L;;;;;N;;;;;
+1CFC3;ZNAMENNY NEUME PAUK;So;0;L;;;;;N;;;;;
1D000;BYZANTINE MUSICAL SYMBOL PSILI;So;0;L;;;;;N;;;;;
1D001;BYZANTINE MUSICAL SYMBOL DASEIA;So;0;L;;;;;N;;;;;
1D002;BYZANTINE MUSICAL SYMBOL PERISPOMENI;So;0;L;;;;;N;;;;;
@@ -27801,6 +28501,8 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1D1E6;MUSICAL SYMBOL KIEVAN EIGHTH NOTE STEM DOWN;So;0;L;;;;;N;;;;;
1D1E7;MUSICAL SYMBOL KIEVAN EIGHTH NOTE STEM UP;So;0;L;;;;;N;;;;;
1D1E8;MUSICAL SYMBOL KIEVAN FLAT SIGN;So;0;L;;;;;N;;;;;
+1D1E9;MUSICAL SYMBOL SORI;So;0;ON;;;;;N;;;;;
+1D1EA;MUSICAL SYMBOL KORON;So;0;ON;;;;;N;;;;;
1D200;GREEK VOCAL NOTATION SYMBOL-1;So;0;ON;;;;;N;;;;;
1D201;GREEK VOCAL NOTATION SYMBOL-2;So;0;ON;;;;;N;;;;;
1D202;GREEK VOCAL NOTATION SYMBOL-3;So;0;ON;;;;;N;;;;;
@@ -29671,6 +30373,37 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1DAAD;SIGNWRITING ROTATION MODIFIER-14;Mn;0;NSM;;;;;N;;;;;
1DAAE;SIGNWRITING ROTATION MODIFIER-15;Mn;0;NSM;;;;;N;;;;;
1DAAF;SIGNWRITING ROTATION MODIFIER-16;Mn;0;NSM;;;;;N;;;;;
+1DF00;LATIN SMALL LETTER FENG DIGRAPH WITH TRILL;Ll;0;L;;;;;N;;;;;
+1DF01;LATIN SMALL LETTER REVERSED SCRIPT G;Ll;0;L;;;;;N;;;;;
+1DF02;LATIN LETTER SMALL CAPITAL TURNED G;Ll;0;L;;;;;N;;;;;
+1DF03;LATIN SMALL LETTER REVERSED K;Ll;0;L;;;;;N;;;;;
+1DF04;LATIN LETTER SMALL CAPITAL L WITH BELT;Ll;0;L;;;;;N;;;;;
+1DF05;LATIN SMALL LETTER LEZH WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
+1DF06;LATIN SMALL LETTER TURNED Y WITH BELT;Ll;0;L;;;;;N;;;;;
+1DF07;LATIN SMALL LETTER REVERSED ENG;Ll;0;L;;;;;N;;;;;
+1DF08;LATIN SMALL LETTER TURNED R WITH LONG LEG AND RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
+1DF09;LATIN SMALL LETTER T WITH HOOK AND RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
+1DF0A;LATIN LETTER RETROFLEX CLICK WITH RETROFLEX HOOK;Lo;0;L;;;;;N;;;;;
+1DF0B;LATIN SMALL LETTER ESH WITH DOUBLE BAR;Ll;0;L;;;;;N;;;;;
+1DF0C;LATIN SMALL LETTER ESH WITH DOUBLE BAR AND CURL;Ll;0;L;;;;;N;;;;;
+1DF0D;LATIN SMALL LETTER TURNED T WITH CURL;Ll;0;L;;;;;N;;;;;
+1DF0E;LATIN LETTER INVERTED GLOTTAL STOP WITH CURL;Ll;0;L;;;;;N;;;;;
+1DF0F;LATIN LETTER STRETCHED C WITH CURL;Ll;0;L;;;;;N;;;;;
+1DF10;LATIN LETTER SMALL CAPITAL TURNED K;Ll;0;L;;;;;N;;;;;
+1DF11;LATIN SMALL LETTER L WITH FISHHOOK;Ll;0;L;;;;;N;;;;;
+1DF12;LATIN SMALL LETTER DEZH DIGRAPH WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
+1DF13;LATIN SMALL LETTER L WITH BELT AND PALATAL HOOK;Ll;0;L;;;;;N;;;;;
+1DF14;LATIN SMALL LETTER ENG WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
+1DF15;LATIN SMALL LETTER TURNED R WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
+1DF16;LATIN SMALL LETTER R WITH FISHHOOK AND PALATAL HOOK;Ll;0;L;;;;;N;;;;;
+1DF17;LATIN SMALL LETTER TESH DIGRAPH WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
+1DF18;LATIN SMALL LETTER EZH WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
+1DF19;LATIN SMALL LETTER DEZH DIGRAPH WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
+1DF1A;LATIN SMALL LETTER I WITH STROKE AND RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
+1DF1B;LATIN SMALL LETTER O WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
+1DF1C;LATIN SMALL LETTER TESH DIGRAPH WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
+1DF1D;LATIN SMALL LETTER C WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
+1DF1E;LATIN SMALL LETTER S WITH CURL;Ll;0;L;;;;;N;;;;;
1E000;COMBINING GLAGOLITIC LETTER AZU;Mn;230;NSM;;;;;N;;;;;
1E001;COMBINING GLAGOLITIC LETTER BUKY;Mn;230;NSM;;;;;N;;;;;
1E002;COMBINING GLAGOLITIC LETTER VEDE;Mn;230;NSM;;;;;N;;;;;
@@ -29780,6 +30513,37 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1E149;NYIAKENG PUACHUE HMONG DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
1E14E;NYIAKENG PUACHUE HMONG LOGOGRAM NYAJ;Lo;0;L;;;;;N;;;;;
1E14F;NYIAKENG PUACHUE HMONG CIRCLED CA;So;0;L;;;;;N;;;;;
+1E290;TOTO LETTER PA;Lo;0;L;;;;;N;;;;;
+1E291;TOTO LETTER BA;Lo;0;L;;;;;N;;;;;
+1E292;TOTO LETTER TA;Lo;0;L;;;;;N;;;;;
+1E293;TOTO LETTER DA;Lo;0;L;;;;;N;;;;;
+1E294;TOTO LETTER KA;Lo;0;L;;;;;N;;;;;
+1E295;TOTO LETTER GA;Lo;0;L;;;;;N;;;;;
+1E296;TOTO LETTER MA;Lo;0;L;;;;;N;;;;;
+1E297;TOTO LETTER NA;Lo;0;L;;;;;N;;;;;
+1E298;TOTO LETTER NGA;Lo;0;L;;;;;N;;;;;
+1E299;TOTO LETTER SA;Lo;0;L;;;;;N;;;;;
+1E29A;TOTO LETTER CHA;Lo;0;L;;;;;N;;;;;
+1E29B;TOTO LETTER YA;Lo;0;L;;;;;N;;;;;
+1E29C;TOTO LETTER WA;Lo;0;L;;;;;N;;;;;
+1E29D;TOTO LETTER JA;Lo;0;L;;;;;N;;;;;
+1E29E;TOTO LETTER HA;Lo;0;L;;;;;N;;;;;
+1E29F;TOTO LETTER RA;Lo;0;L;;;;;N;;;;;
+1E2A0;TOTO LETTER LA;Lo;0;L;;;;;N;;;;;
+1E2A1;TOTO LETTER I;Lo;0;L;;;;;N;;;;;
+1E2A2;TOTO LETTER BREATHY I;Lo;0;L;;;;;N;;;;;
+1E2A3;TOTO LETTER IU;Lo;0;L;;;;;N;;;;;
+1E2A4;TOTO LETTER BREATHY IU;Lo;0;L;;;;;N;;;;;
+1E2A5;TOTO LETTER U;Lo;0;L;;;;;N;;;;;
+1E2A6;TOTO LETTER E;Lo;0;L;;;;;N;;;;;
+1E2A7;TOTO LETTER BREATHY E;Lo;0;L;;;;;N;;;;;
+1E2A8;TOTO LETTER EO;Lo;0;L;;;;;N;;;;;
+1E2A9;TOTO LETTER BREATHY EO;Lo;0;L;;;;;N;;;;;
+1E2AA;TOTO LETTER O;Lo;0;L;;;;;N;;;;;
+1E2AB;TOTO LETTER AE;Lo;0;L;;;;;N;;;;;
+1E2AC;TOTO LETTER BREATHY AE;Lo;0;L;;;;;N;;;;;
+1E2AD;TOTO LETTER A;Lo;0;L;;;;;N;;;;;
+1E2AE;TOTO SIGN RISING TONE;Mn;230;NSM;;;;;N;;;;;
1E2C0;WANCHO LETTER AA;Lo;0;L;;;;;N;;;;;
1E2C1;WANCHO LETTER A;Lo;0;L;;;;;N;;;;;
1E2C2;WANCHO LETTER BA;Lo;0;L;;;;;N;;;;;
@@ -29839,6 +30603,34 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1E2F8;WANCHO DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
1E2F9;WANCHO DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
1E2FF;WANCHO NGUN SIGN;Sc;0;ET;;;;;N;;;;;
+1E7E0;ETHIOPIC SYLLABLE HHYA;Lo;0;L;;;;;N;;;;;
+1E7E1;ETHIOPIC SYLLABLE HHYU;Lo;0;L;;;;;N;;;;;
+1E7E2;ETHIOPIC SYLLABLE HHYI;Lo;0;L;;;;;N;;;;;
+1E7E3;ETHIOPIC SYLLABLE HHYAA;Lo;0;L;;;;;N;;;;;
+1E7E4;ETHIOPIC SYLLABLE HHYEE;Lo;0;L;;;;;N;;;;;
+1E7E5;ETHIOPIC SYLLABLE HHYE;Lo;0;L;;;;;N;;;;;
+1E7E6;ETHIOPIC SYLLABLE HHYO;Lo;0;L;;;;;N;;;;;
+1E7E8;ETHIOPIC SYLLABLE GURAGE HHWA;Lo;0;L;;;;;N;;;;;
+1E7E9;ETHIOPIC SYLLABLE HHWI;Lo;0;L;;;;;N;;;;;
+1E7EA;ETHIOPIC SYLLABLE HHWEE;Lo;0;L;;;;;N;;;;;
+1E7EB;ETHIOPIC SYLLABLE HHWE;Lo;0;L;;;;;N;;;;;
+1E7ED;ETHIOPIC SYLLABLE GURAGE MWI;Lo;0;L;;;;;N;;;;;
+1E7EE;ETHIOPIC SYLLABLE GURAGE MWEE;Lo;0;L;;;;;N;;;;;
+1E7F0;ETHIOPIC SYLLABLE GURAGE QWI;Lo;0;L;;;;;N;;;;;
+1E7F1;ETHIOPIC SYLLABLE GURAGE QWEE;Lo;0;L;;;;;N;;;;;
+1E7F2;ETHIOPIC SYLLABLE GURAGE QWE;Lo;0;L;;;;;N;;;;;
+1E7F3;ETHIOPIC SYLLABLE GURAGE BWI;Lo;0;L;;;;;N;;;;;
+1E7F4;ETHIOPIC SYLLABLE GURAGE BWEE;Lo;0;L;;;;;N;;;;;
+1E7F5;ETHIOPIC SYLLABLE GURAGE KWI;Lo;0;L;;;;;N;;;;;
+1E7F6;ETHIOPIC SYLLABLE GURAGE KWEE;Lo;0;L;;;;;N;;;;;
+1E7F7;ETHIOPIC SYLLABLE GURAGE KWE;Lo;0;L;;;;;N;;;;;
+1E7F8;ETHIOPIC SYLLABLE GURAGE GWI;Lo;0;L;;;;;N;;;;;
+1E7F9;ETHIOPIC SYLLABLE GURAGE GWEE;Lo;0;L;;;;;N;;;;;
+1E7FA;ETHIOPIC SYLLABLE GURAGE GWE;Lo;0;L;;;;;N;;;;;
+1E7FB;ETHIOPIC SYLLABLE GURAGE FWI;Lo;0;L;;;;;N;;;;;
+1E7FC;ETHIOPIC SYLLABLE GURAGE FWEE;Lo;0;L;;;;;N;;;;;
+1E7FD;ETHIOPIC SYLLABLE GURAGE PWI;Lo;0;L;;;;;N;;;;;
+1E7FE;ETHIOPIC SYLLABLE GURAGE PWEE;Lo;0;L;;;;;N;;;;;
1E800;MENDE KIKAKUI SYLLABLE M001 KI;Lo;0;R;;;;;N;;;;;
1E801;MENDE KIKAKUI SYLLABLE M002 KA;Lo;0;R;;;;;N;;;;;
1E802;MENDE KIKAKUI SYLLABLE M003 KU;Lo;0;R;;;;;N;;;;;
@@ -31886,6 +32678,9 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F6D5;HINDU TEMPLE;So;0;ON;;;;;N;;;;;
1F6D6;HUT;So;0;ON;;;;;N;;;;;
1F6D7;ELEVATOR;So;0;ON;;;;;N;;;;;
+1F6DD;PLAYGROUND SLIDE;So;0;ON;;;;;N;;;;;
+1F6DE;WHEEL;So;0;ON;;;;;N;;;;;
+1F6DF;RING BUOY;So;0;ON;;;;;N;;;;;
1F6E0;HAMMER AND WRENCH;So;0;ON;;;;;N;;;;;
1F6E1;SHIELD;So;0;ON;;;;;N;;;;;
1F6E2;OIL DRUM;So;0;ON;;;;;N;;;;;
@@ -32129,6 +32924,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F7E9;LARGE GREEN SQUARE;So;0;ON;;;;;N;;;;;
1F7EA;LARGE PURPLE SQUARE;So;0;ON;;;;;N;;;;;
1F7EB;LARGE BROWN SQUARE;So;0;ON;;;;;N;;;;;
+1F7F0;HEAVY EQUALS SIGN;So;0;ON;;;;;N;;;;;
1F800;LEFTWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;;
1F801;UPWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;;
1F802;RIGHTWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;;
@@ -32400,6 +33196,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F976;FREEZING FACE;So;0;ON;;;;;N;;;;;
1F977;NINJA;So;0;ON;;;;;N;;;;;
1F978;DISGUISED FACE;So;0;ON;;;;;N;;;;;
+1F979;FACE HOLDING BACK TEARS;So;0;ON;;;;;N;;;;;
1F97A;FACE WITH PLEADING EYES;So;0;ON;;;;;N;;;;;
1F97B;SARI;So;0;ON;;;;;N;;;;;
1F97C;LAB COAT;So;0;ON;;;;;N;;;;;
@@ -32482,6 +33279,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F9C9;MATE DRINK;So;0;ON;;;;;N;;;;;
1F9CA;ICE CUBE;So;0;ON;;;;;N;;;;;
1F9CB;BUBBLE TEA;So;0;ON;;;;;N;;;;;
+1F9CC;TROLL;So;0;ON;;;;;N;;;;;
1F9CD;STANDING PERSON;So;0;ON;;;;;N;;;;;
1F9CE;KNEELING PERSON;So;0;ON;;;;;N;;;;;
1F9CF;DEAF PERSON;So;0;ON;;;;;N;;;;;
@@ -32639,6 +33437,8 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1FA78;DROP OF BLOOD;So;0;ON;;;;;N;;;;;
1FA79;ADHESIVE BANDAGE;So;0;ON;;;;;N;;;;;
1FA7A;STETHOSCOPE;So;0;ON;;;;;N;;;;;
+1FA7B;X-RAY;So;0;ON;;;;;N;;;;;
+1FA7C;CRUTCH;So;0;ON;;;;;N;;;;;
1FA80;YO-YO;So;0;ON;;;;;N;;;;;
1FA81;KITE;So;0;ON;;;;;N;;;;;
1FA82;PARACHUTE;So;0;ON;;;;;N;;;;;
@@ -32671,6 +33471,10 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1FAA6;HEADSTONE;So;0;ON;;;;;N;;;;;
1FAA7;PLACARD;So;0;ON;;;;;N;;;;;
1FAA8;ROCK;So;0;ON;;;;;N;;;;;
+1FAA9;MIRROR BALL;So;0;ON;;;;;N;;;;;
+1FAAA;IDENTIFICATION CARD;So;0;ON;;;;;N;;;;;
+1FAAB;LOW BATTERY;So;0;ON;;;;;N;;;;;
+1FAAC;HAMSA;So;0;ON;;;;;N;;;;;
1FAB0;FLY;So;0;ON;;;;;N;;;;;
1FAB1;WORM;So;0;ON;;;;;N;;;;;
1FAB2;BEETLE;So;0;ON;;;;;N;;;;;
@@ -32678,9 +33482,16 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1FAB4;POTTED PLANT;So;0;ON;;;;;N;;;;;
1FAB5;WOOD;So;0;ON;;;;;N;;;;;
1FAB6;FEATHER;So;0;ON;;;;;N;;;;;
+1FAB7;LOTUS;So;0;ON;;;;;N;;;;;
+1FAB8;CORAL;So;0;ON;;;;;N;;;;;
+1FAB9;EMPTY NEST;So;0;ON;;;;;N;;;;;
+1FABA;NEST WITH EGGS;So;0;ON;;;;;N;;;;;
1FAC0;ANATOMICAL HEART;So;0;ON;;;;;N;;;;;
1FAC1;LUNGS;So;0;ON;;;;;N;;;;;
1FAC2;PEOPLE HUGGING;So;0;ON;;;;;N;;;;;
+1FAC3;PREGNANT MAN;So;0;ON;;;;;N;;;;;
+1FAC4;PREGNANT PERSON;So;0;ON;;;;;N;;;;;
+1FAC5;PERSON WITH CROWN;So;0;ON;;;;;N;;;;;
1FAD0;BLUEBERRIES;So;0;ON;;;;;N;;;;;
1FAD1;BELL PEPPER;So;0;ON;;;;;N;;;;;
1FAD2;OLIVE;So;0;ON;;;;;N;;;;;
@@ -32688,6 +33499,24 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1FAD4;TAMALE;So;0;ON;;;;;N;;;;;
1FAD5;FONDUE;So;0;ON;;;;;N;;;;;
1FAD6;TEAPOT;So;0;ON;;;;;N;;;;;
+1FAD7;POURING LIQUID;So;0;ON;;;;;N;;;;;
+1FAD8;BEANS;So;0;ON;;;;;N;;;;;
+1FAD9;JAR;So;0;ON;;;;;N;;;;;
+1FAE0;MELTING FACE;So;0;ON;;;;;N;;;;;
+1FAE1;SALUTING FACE;So;0;ON;;;;;N;;;;;
+1FAE2;FACE WITH OPEN EYES AND HAND OVER MOUTH;So;0;ON;;;;;N;;;;;
+1FAE3;FACE WITH PEEKING EYE;So;0;ON;;;;;N;;;;;
+1FAE4;FACE WITH DIAGONAL MOUTH;So;0;ON;;;;;N;;;;;
+1FAE5;DOTTED LINE FACE;So;0;ON;;;;;N;;;;;
+1FAE6;BITING LIP;So;0;ON;;;;;N;;;;;
+1FAE7;BUBBLES;So;0;ON;;;;;N;;;;;
+1FAF0;HAND WITH INDEX FINGER AND THUMB CROSSED;So;0;ON;;;;;N;;;;;
+1FAF1;RIGHTWARDS HAND;So;0;ON;;;;;N;;;;;
+1FAF2;LEFTWARDS HAND;So;0;ON;;;;;N;;;;;
+1FAF3;PALM DOWN HAND;So;0;ON;;;;;N;;;;;
+1FAF4;PALM UP HAND;So;0;ON;;;;;N;;;;;
+1FAF5;INDEX POINTING AT THE VIEWER;So;0;ON;;;;;N;;;;;
+1FAF6;HEART HANDS;So;0;ON;;;;;N;;;;;
1FB00;BLOCK SEXTANT-1;So;0;ON;;;;;N;;;;;
1FB01;BLOCK SEXTANT-2;So;0;ON;;;;;N;;;;;
1FB02;BLOCK SEXTANT-12;So;0;ON;;;;;N;;;;;
@@ -32901,9 +33730,9 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1FBF8;SEGMENTED DIGIT EIGHT;Nd;0;EN;<font> 0038;8;8;8;N;;;;;
1FBF9;SEGMENTED DIGIT NINE;Nd;0;EN;<font> 0039;9;9;9;N;;;;;
20000;<CJK Ideograph Extension B, First>;Lo;0;L;;;;;N;;;;;
-2A6DD;<CJK Ideograph Extension B, Last>;Lo;0;L;;;;;N;;;;;
+2A6DF;<CJK Ideograph Extension B, Last>;Lo;0;L;;;;;N;;;;;
2A700;<CJK Ideograph Extension C, First>;Lo;0;L;;;;;N;;;;;
-2B734;<CJK Ideograph Extension C, Last>;Lo;0;L;;;;;N;;;;;
+2B738;<CJK Ideograph Extension C, Last>;Lo;0;L;;;;;N;;;;;
2B740;<CJK Ideograph Extension D, First>;Lo;0;L;;;;;N;;;;;
2B81D;<CJK Ideograph Extension D, Last>;Lo;0;L;;;;;N;;;;;
2B820;<CJK Ideograph Extension E, First>;Lo;0;L;;;;;N;;;;;
diff --git a/unicode/emoji-data.txt b/src/unicode/emoji-data.txt
index 5d7dc1b156..7806c7ab53 100644
--- a/unicode/emoji-data.txt
+++ b/src/unicode/emoji-data.txt
@@ -1,11 +1,11 @@
-# emoji-data.txt
-# Date: 2020-01-28, 20:52:38 GMT
-# © 2020 Unicode®, Inc.
+# emoji-data-14.0.0.txt
+# Date: 2021-08-26, 17:22:22 GMT
+# © 2021 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
# For terms of use, see http://www.unicode.org/terms_of_use.html
#
# Emoji Data for UTS #51
-# Version: 13.0
+# Used with Emoji Version 14.0 and subsequent minor revisions (if any)
#
# For documentation and usage, see http://www.unicode.org/reports/tr51
#
@@ -22,7 +22,7 @@
# All omitted code points have Emoji=No
# @missing: 0000..10FFFF ; Emoji ; No
-0023 ; Emoji # E0.0 [1] (#️) number sign
+0023 ; Emoji # E0.0 [1] (#️) hash sign
002A ; Emoji # E0.0 [1] (*️) asterisk
0030..0039 ; Emoji # E0.0 [10] (0️..9️) digit zero..digit nine
00A9 ; Emoji # E0.6 [1] (©️) copyright
@@ -119,8 +119,8 @@
2747 ; Emoji # E0.6 [1] (❇️) sparkle
274C ; Emoji # E0.6 [1] (❌) cross mark
274E ; Emoji # E0.6 [1] (❎) cross mark button
-2753..2755 ; Emoji # E0.6 [3] (❓..❕) question mark..white exclamation mark
-2757 ; Emoji # E0.6 [1] (❗) exclamation mark
+2753..2755 ; Emoji # E0.6 [3] (❓..❕) red question mark..white exclamation mark
+2757 ; Emoji # E0.6 [1] (❗) red exclamation mark
2763 ; Emoji # E1.0 [1] (❣️) heart exclamation
2764 ; Emoji # E0.6 [1] (❤️) red heart
2795..2797 ; Emoji # E0.6 [3] (➕..➗) plus..divide
@@ -239,7 +239,7 @@
1F509 ; Emoji # E1.0 [1] (🔉) speaker medium volume
1F50A..1F514 ; Emoji # E0.6 [11] (🔊..🔔) speaker high volume..bell
1F515 ; Emoji # E1.0 [1] (🔕) bell with slash
-1F516..1F52B ; Emoji # E0.6 [22] (🔖..🔫) bookmark..pistol
+1F516..1F52B ; Emoji # E0.6 [22] (🔖..🔫) bookmark..water pistol
1F52C..1F52D ; Emoji # E1.0 [2] (🔬..🔭) microscope..telescope
1F52E..1F53D ; Emoji # E0.6 [16] (🔮..🔽) crystal ball..downwards button
1F549..1F54A ; Emoji # E0.7 [2] (🕉️..🕊️) om..dove
@@ -294,7 +294,7 @@
1F62E..1F62F ; Emoji # E1.0 [2] (😮..😯) face with open mouth..hushed face
1F630..1F633 ; Emoji # E0.6 [4] (😰..😳) anxious face with sweat..flushed face
1F634 ; Emoji # E1.0 [1] (😴) sleeping face
-1F635 ; Emoji # E0.6 [1] (😵) dizzy face
+1F635 ; Emoji # E0.6 [1] (😵) face with crossed-out eyes
1F636 ; Emoji # E1.0 [1] (😶) face without mouth
1F637..1F640 ; Emoji # E0.6 [10] (😷..🙀) face with medical mask..weary cat
1F641..1F644 ; Emoji # E1.0 [4] (🙁..🙄) slightly frowning face..face with rolling eyes
@@ -341,6 +341,7 @@
1F6D1..1F6D2 ; Emoji # E3.0 [2] (🛑..🛒) stop sign..shopping cart
1F6D5 ; Emoji # E12.0 [1] (🛕) hindu temple
1F6D6..1F6D7 ; Emoji # E13.0 [2] (🛖..🛗) hut..elevator
+1F6DD..1F6DF ; Emoji # E14.0 [3] (🛝..🛟) playground slide..ring buoy
1F6E0..1F6E5 ; Emoji # E0.7 [6] (🛠️..🛥️) hammer and wrench..motor boat
1F6E9 ; Emoji # E0.7 [1] (🛩️) small airplane
1F6EB..1F6EC ; Emoji # E1.0 [2] (🛫..🛬) airplane departure..airplane arrival
@@ -352,6 +353,7 @@
1F6FA ; Emoji # E12.0 [1] (🛺) auto rickshaw
1F6FB..1F6FC ; Emoji # E13.0 [2] (🛻..🛼) pickup truck..roller skate
1F7E0..1F7EB ; Emoji # E12.0 [12] (🟠..🟫) orange circle..brown square
+1F7F0 ; Emoji # E14.0 [1] (🟰) heavy equals sign
1F90C ; Emoji # E13.0 [1] (🤌) pinched fingers
1F90D..1F90F ; Emoji # E12.0 [3] (🤍..🤏) white heart..pinching hand
1F910..1F918 ; Emoji # E1.0 [9] (🤐..🤘) zipper-mouth face..sign of the horns
@@ -375,6 +377,7 @@
1F972 ; Emoji # E13.0 [1] (🥲) smiling face with tear
1F973..1F976 ; Emoji # E11.0 [4] (🥳..🥶) partying face..cold face
1F977..1F978 ; Emoji # E13.0 [2] (🥷..🥸) ninja..disguised face
+1F979 ; Emoji # E14.0 [1] (🥹) face holding back tears
1F97A ; Emoji # E11.0 [1] (🥺) pleading face
1F97B ; Emoji # E12.0 [1] (🥻) sari
1F97C..1F97F ; Emoji # E11.0 [4] (🥼..🥿) lab coat..flat shoe
@@ -392,21 +395,29 @@
1F9C1..1F9C2 ; Emoji # E11.0 [2] (🧁..🧂) cupcake..salt
1F9C3..1F9CA ; Emoji # E12.0 [8] (🧃..🧊) beverage box..ice
1F9CB ; Emoji # E13.0 [1] (🧋) bubble tea
+1F9CC ; Emoji # E14.0 [1] (🧌) troll
1F9CD..1F9CF ; Emoji # E12.0 [3] (🧍..🧏) person standing..deaf person
1F9D0..1F9E6 ; Emoji # E5.0 [23] (🧐..🧦) face with monocle..socks
1F9E7..1F9FF ; Emoji # E11.0 [25] (🧧..🧿) red envelope..nazar amulet
1FA70..1FA73 ; Emoji # E12.0 [4] (🩰..🩳) ballet shoes..shorts
1FA74 ; Emoji # E13.0 [1] (🩴) thong sandal
1FA78..1FA7A ; Emoji # E12.0 [3] (🩸..🩺) drop of blood..stethoscope
+1FA7B..1FA7C ; Emoji # E14.0 [2] (🩻..🩼) x-ray..crutch
1FA80..1FA82 ; Emoji # E12.0 [3] (🪀..🪂) yo-yo..parachute
1FA83..1FA86 ; Emoji # E13.0 [4] (🪃..🪆) boomerang..nesting dolls
1FA90..1FA95 ; Emoji # E12.0 [6] (🪐..🪕) ringed planet..banjo
1FA96..1FAA8 ; Emoji # E13.0 [19] (🪖..🪨) military helmet..rock
+1FAA9..1FAAC ; Emoji # E14.0 [4] (🪩..🪬) mirror ball..hamsa
1FAB0..1FAB6 ; Emoji # E13.0 [7] (🪰..🪶) fly..feather
+1FAB7..1FABA ; Emoji # E14.0 [4] (🪷..🪺) lotus..nest with eggs
1FAC0..1FAC2 ; Emoji # E13.0 [3] (🫀..🫂) anatomical heart..people hugging
+1FAC3..1FAC5 ; Emoji # E14.0 [3] (🫃..🫅) pregnant man..person with crown
1FAD0..1FAD6 ; Emoji # E13.0 [7] (🫐..🫖) blueberries..teapot
+1FAD7..1FAD9 ; Emoji # E14.0 [3] (🫗..🫙) pouring liquid..jar
+1FAE0..1FAE7 ; Emoji # E14.0 [8] (🫠..🫧) melting face..bubbles
+1FAF0..1FAF6 ; Emoji # E14.0 [7] (🫰..🫶) hand with index finger and thumb crossed..heart hands
-# Total elements: 1367
+# Total elements: 1404
# ================================================
@@ -438,8 +449,8 @@
2728 ; Emoji_Presentation # E0.6 [1] (✨) sparkles
274C ; Emoji_Presentation # E0.6 [1] (❌) cross mark
274E ; Emoji_Presentation # E0.6 [1] (❎) cross mark button
-2753..2755 ; Emoji_Presentation # E0.6 [3] (❓..❕) question mark..white exclamation mark
-2757 ; Emoji_Presentation # E0.6 [1] (❗) exclamation mark
+2753..2755 ; Emoji_Presentation # E0.6 [3] (❓..❕) red question mark..white exclamation mark
+2757 ; Emoji_Presentation # E0.6 [1] (❗) red exclamation mark
2795..2797 ; Emoji_Presentation # E0.6 [3] (➕..➗) plus..divide
27B0 ; Emoji_Presentation # E0.6 [1] (➰) curly loop
27BF ; Emoji_Presentation # E1.0 [1] (➿) double curly loop
@@ -533,7 +544,7 @@
1F509 ; Emoji_Presentation # E1.0 [1] (🔉) speaker medium volume
1F50A..1F514 ; Emoji_Presentation # E0.6 [11] (🔊..🔔) speaker high volume..bell
1F515 ; Emoji_Presentation # E1.0 [1] (🔕) bell with slash
-1F516..1F52B ; Emoji_Presentation # E0.6 [22] (🔖..🔫) bookmark..pistol
+1F516..1F52B ; Emoji_Presentation # E0.6 [22] (🔖..🔫) bookmark..water pistol
1F52C..1F52D ; Emoji_Presentation # E1.0 [2] (🔬..🔭) microscope..telescope
1F52E..1F53D ; Emoji_Presentation # E0.6 [16] (🔮..🔽) crystal ball..downwards button
1F54B..1F54E ; Emoji_Presentation # E1.0 [4] (🕋..🕎) kaaba..menorah
@@ -569,7 +580,7 @@
1F62E..1F62F ; Emoji_Presentation # E1.0 [2] (😮..😯) face with open mouth..hushed face
1F630..1F633 ; Emoji_Presentation # E0.6 [4] (😰..😳) anxious face with sweat..flushed face
1F634 ; Emoji_Presentation # E1.0 [1] (😴) sleeping face
-1F635 ; Emoji_Presentation # E0.6 [1] (😵) dizzy face
+1F635 ; Emoji_Presentation # E0.6 [1] (😵) face with crossed-out eyes
1F636 ; Emoji_Presentation # E1.0 [1] (😶) face without mouth
1F637..1F640 ; Emoji_Presentation # E0.6 [10] (😷..🙀) face with medical mask..weary cat
1F641..1F644 ; Emoji_Presentation # E1.0 [4] (🙁..🙄) slightly frowning face..face with rolling eyes
@@ -614,6 +625,7 @@
1F6D1..1F6D2 ; Emoji_Presentation # E3.0 [2] (🛑..🛒) stop sign..shopping cart
1F6D5 ; Emoji_Presentation # E12.0 [1] (🛕) hindu temple
1F6D6..1F6D7 ; Emoji_Presentation # E13.0 [2] (🛖..🛗) hut..elevator
+1F6DD..1F6DF ; Emoji_Presentation # E14.0 [3] (🛝..🛟) playground slide..ring buoy
1F6EB..1F6EC ; Emoji_Presentation # E1.0 [2] (🛫..🛬) airplane departure..airplane arrival
1F6F4..1F6F6 ; Emoji_Presentation # E3.0 [3] (🛴..🛶) kick scooter..canoe
1F6F7..1F6F8 ; Emoji_Presentation # E5.0 [2] (🛷..🛸) sled..flying saucer
@@ -621,6 +633,7 @@
1F6FA ; Emoji_Presentation # E12.0 [1] (🛺) auto rickshaw
1F6FB..1F6FC ; Emoji_Presentation # E13.0 [2] (🛻..🛼) pickup truck..roller skate
1F7E0..1F7EB ; Emoji_Presentation # E12.0 [12] (🟠..🟫) orange circle..brown square
+1F7F0 ; Emoji_Presentation # E14.0 [1] (🟰) heavy equals sign
1F90C ; Emoji_Presentation # E13.0 [1] (🤌) pinched fingers
1F90D..1F90F ; Emoji_Presentation # E12.0 [3] (🤍..🤏) white heart..pinching hand
1F910..1F918 ; Emoji_Presentation # E1.0 [9] (🤐..🤘) zipper-mouth face..sign of the horns
@@ -644,6 +657,7 @@
1F972 ; Emoji_Presentation # E13.0 [1] (🥲) smiling face with tear
1F973..1F976 ; Emoji_Presentation # E11.0 [4] (🥳..🥶) partying face..cold face
1F977..1F978 ; Emoji_Presentation # E13.0 [2] (🥷..🥸) ninja..disguised face
+1F979 ; Emoji_Presentation # E14.0 [1] (🥹) face holding back tears
1F97A ; Emoji_Presentation # E11.0 [1] (🥺) pleading face
1F97B ; Emoji_Presentation # E12.0 [1] (🥻) sari
1F97C..1F97F ; Emoji_Presentation # E11.0 [4] (🥼..🥿) lab coat..flat shoe
@@ -661,21 +675,29 @@
1F9C1..1F9C2 ; Emoji_Presentation # E11.0 [2] (🧁..🧂) cupcake..salt
1F9C3..1F9CA ; Emoji_Presentation # E12.0 [8] (🧃..🧊) beverage box..ice
1F9CB ; Emoji_Presentation # E13.0 [1] (🧋) bubble tea
+1F9CC ; Emoji_Presentation # E14.0 [1] (🧌) troll
1F9CD..1F9CF ; Emoji_Presentation # E12.0 [3] (🧍..🧏) person standing..deaf person
1F9D0..1F9E6 ; Emoji_Presentation # E5.0 [23] (🧐..🧦) face with monocle..socks
1F9E7..1F9FF ; Emoji_Presentation # E11.0 [25] (🧧..🧿) red envelope..nazar amulet
1FA70..1FA73 ; Emoji_Presentation # E12.0 [4] (🩰..🩳) ballet shoes..shorts
1FA74 ; Emoji_Presentation # E13.0 [1] (🩴) thong sandal
1FA78..1FA7A ; Emoji_Presentation # E12.0 [3] (🩸..🩺) drop of blood..stethoscope
+1FA7B..1FA7C ; Emoji_Presentation # E14.0 [2] (🩻..🩼) x-ray..crutch
1FA80..1FA82 ; Emoji_Presentation # E12.0 [3] (🪀..🪂) yo-yo..parachute
1FA83..1FA86 ; Emoji_Presentation # E13.0 [4] (🪃..🪆) boomerang..nesting dolls
1FA90..1FA95 ; Emoji_Presentation # E12.0 [6] (🪐..🪕) ringed planet..banjo
1FA96..1FAA8 ; Emoji_Presentation # E13.0 [19] (🪖..🪨) military helmet..rock
+1FAA9..1FAAC ; Emoji_Presentation # E14.0 [4] (🪩..🪬) mirror ball..hamsa
1FAB0..1FAB6 ; Emoji_Presentation # E13.0 [7] (🪰..🪶) fly..feather
+1FAB7..1FABA ; Emoji_Presentation # E14.0 [4] (🪷..🪺) lotus..nest with eggs
1FAC0..1FAC2 ; Emoji_Presentation # E13.0 [3] (🫀..🫂) anatomical heart..people hugging
+1FAC3..1FAC5 ; Emoji_Presentation # E14.0 [3] (🫃..🫅) pregnant man..person with crown
1FAD0..1FAD6 ; Emoji_Presentation # E13.0 [7] (🫐..🫖) blueberries..teapot
+1FAD7..1FAD9 ; Emoji_Presentation # E14.0 [3] (🫗..🫙) pouring liquid..jar
+1FAE0..1FAE7 ; Emoji_Presentation # E14.0 [8] (🫠..🫧) melting face..bubbles
+1FAF0..1FAF6 ; Emoji_Presentation # E14.0 [7] (🫰..🫶) hand with index finger and thumb crossed..heart hands
-# Total elements: 1148
+# Total elements: 1185
# ================================================
@@ -738,15 +760,17 @@
1F9BB ; Emoji_Modifier_Base # E12.0 [1] (🦻) ear with hearing aid
1F9CD..1F9CF ; Emoji_Modifier_Base # E12.0 [3] (🧍..🧏) person standing..deaf person
1F9D1..1F9DD ; Emoji_Modifier_Base # E5.0 [13] (🧑..🧝) person..elf
+1FAC3..1FAC5 ; Emoji_Modifier_Base # E14.0 [3] (🫃..🫅) pregnant man..person with crown
+1FAF0..1FAF6 ; Emoji_Modifier_Base # E14.0 [7] (🫰..🫶) hand with index finger and thumb crossed..heart hands
-# Total elements: 122
+# Total elements: 132
# ================================================
# All omitted code points have Emoji_Component=No
# @missing: 0000..10FFFF ; Emoji_Component ; No
-0023 ; Emoji_Component # E0.0 [1] (#️) number sign
+0023 ; Emoji_Component # E0.0 [1] (#️) hash sign
002A ; Emoji_Component # E0.0 [1] (*️) asterisk
0030..0039 ; Emoji_Component # E0.0 [10] (0️..9️) digit zero..digit nine
200D ; Emoji_Component # E0.0 [1] (‍) zero width joiner
@@ -902,8 +926,8 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c
2747 ; Extended_Pictographic# E0.6 [1] (❇️) sparkle
274C ; Extended_Pictographic# E0.6 [1] (❌) cross mark
274E ; Extended_Pictographic# E0.6 [1] (❎) cross mark button
-2753..2755 ; Extended_Pictographic# E0.6 [3] (❓..❕) question mark..white exclamation mark
-2757 ; Extended_Pictographic# E0.6 [1] (❗) exclamation mark
+2753..2755 ; Extended_Pictographic# E0.6 [3] (❓..❕) red question mark..white exclamation mark
+2757 ; Extended_Pictographic# E0.6 [1] (❗) red exclamation mark
2763 ; Extended_Pictographic# E1.0 [1] (❣️) heart exclamation
2764 ; Extended_Pictographic# E0.6 [1] (❤️) red heart
2765..2767 ; Extended_Pictographic# E0.0 [3] (❥..❧) ROTATED HEAVY BLACK HEART BULLET..ROTATED FLORAL HEART BULLET
@@ -1041,7 +1065,7 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c
1F509 ; Extended_Pictographic# E1.0 [1] (🔉) speaker medium volume
1F50A..1F514 ; Extended_Pictographic# E0.6 [11] (🔊..🔔) speaker high volume..bell
1F515 ; Extended_Pictographic# E1.0 [1] (🔕) bell with slash
-1F516..1F52B ; Extended_Pictographic# E0.6 [22] (🔖..🔫) bookmark..pistol
+1F516..1F52B ; Extended_Pictographic# E0.6 [22] (🔖..🔫) bookmark..water pistol
1F52C..1F52D ; Extended_Pictographic# E1.0 [2] (🔬..🔭) microscope..telescope
1F52E..1F53D ; Extended_Pictographic# E0.6 [16] (🔮..🔽) crystal ball..downwards button
1F546..1F548 ; Extended_Pictographic# E0.0 [3] (🕆..🕈) WHITE LATIN CROSS..CELTIC CROSS
@@ -1117,7 +1141,7 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c
1F62E..1F62F ; Extended_Pictographic# E1.0 [2] (😮..😯) face with open mouth..hushed face
1F630..1F633 ; Extended_Pictographic# E0.6 [4] (😰..😳) anxious face with sweat..flushed face
1F634 ; Extended_Pictographic# E1.0 [1] (😴) sleeping face
-1F635 ; Extended_Pictographic# E0.6 [1] (😵) dizzy face
+1F635 ; Extended_Pictographic# E0.6 [1] (😵) face with crossed-out eyes
1F636 ; Extended_Pictographic# E1.0 [1] (😶) face without mouth
1F637..1F640 ; Extended_Pictographic# E0.6 [10] (😷..🙀) face with medical mask..weary cat
1F641..1F644 ; Extended_Pictographic# E1.0 [4] (🙁..🙄) slightly frowning face..face with rolling eyes
@@ -1166,7 +1190,8 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c
1F6D3..1F6D4 ; Extended_Pictographic# E0.0 [2] (🛓..🛔) STUPA..PAGODA
1F6D5 ; Extended_Pictographic# E12.0 [1] (🛕) hindu temple
1F6D6..1F6D7 ; Extended_Pictographic# E13.0 [2] (🛖..🛗) hut..elevator
-1F6D8..1F6DF ; Extended_Pictographic# E0.0 [8] (🛘..🛟) <reserved-1F6D8>..<reserved-1F6DF>
+1F6D8..1F6DC ; Extended_Pictographic# E0.0 [5] (🛘..🛜) <reserved-1F6D8>..<reserved-1F6DC>
+1F6DD..1F6DF ; Extended_Pictographic# E14.0 [3] (🛝..🛟) playground slide..ring buoy
1F6E0..1F6E5 ; Extended_Pictographic# E0.7 [6] (🛠️..🛥️) hammer and wrench..motor boat
1F6E6..1F6E8 ; Extended_Pictographic# E0.0 [3] (🛦..🛨) UP-POINTING MILITARY AIRPLANE..UP-POINTING SMALL AIRPLANE
1F6E9 ; Extended_Pictographic# E0.7 [1] (🛩️) small airplane
@@ -1185,7 +1210,9 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c
1F774..1F77F ; Extended_Pictographic# E0.0 [12] (🝴..🝿) <reserved-1F774>..<reserved-1F77F>
1F7D5..1F7DF ; Extended_Pictographic# E0.0 [11] (🟕..🟟) CIRCLED TRIANGLE..<reserved-1F7DF>
1F7E0..1F7EB ; Extended_Pictographic# E12.0 [12] (🟠..🟫) orange circle..brown square
-1F7EC..1F7FF ; Extended_Pictographic# E0.0 [20] (🟬..🟿) <reserved-1F7EC>..<reserved-1F7FF>
+1F7EC..1F7EF ; Extended_Pictographic# E0.0 [4] (🟬..🟯) <reserved-1F7EC>..<reserved-1F7EF>
+1F7F0 ; Extended_Pictographic# E14.0 [1] (🟰) heavy equals sign
+1F7F1..1F7FF ; Extended_Pictographic# E0.0 [15] (🟱..🟿) <reserved-1F7F1>..<reserved-1F7FF>
1F80C..1F80F ; Extended_Pictographic# E0.0 [4] (🠌..🠏) <reserved-1F80C>..<reserved-1F80F>
1F848..1F84F ; Extended_Pictographic# E0.0 [8] (🡈..🡏) <reserved-1F848>..<reserved-1F84F>
1F85A..1F85F ; Extended_Pictographic# E0.0 [6] (🡚..🡟) <reserved-1F85A>..<reserved-1F85F>
@@ -1214,7 +1241,7 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c
1F972 ; Extended_Pictographic# E13.0 [1] (🥲) smiling face with tear
1F973..1F976 ; Extended_Pictographic# E11.0 [4] (🥳..🥶) partying face..cold face
1F977..1F978 ; Extended_Pictographic# E13.0 [2] (🥷..🥸) ninja..disguised face
-1F979 ; Extended_Pictographic# E0.0 [1] (🥹) <reserved-1F979>
+1F979 ; Extended_Pictographic# E14.0 [1] (🥹) face holding back tears
1F97A ; Extended_Pictographic# E11.0 [1] (🥺) pleading face
1F97B ; Extended_Pictographic# E12.0 [1] (🥻) sari
1F97C..1F97F ; Extended_Pictographic# E11.0 [4] (🥼..🥿) lab coat..flat shoe
@@ -1232,7 +1259,7 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c
1F9C1..1F9C2 ; Extended_Pictographic# E11.0 [2] (🧁..🧂) cupcake..salt
1F9C3..1F9CA ; Extended_Pictographic# E12.0 [8] (🧃..🧊) beverage box..ice
1F9CB ; Extended_Pictographic# E13.0 [1] (🧋) bubble tea
-1F9CC ; Extended_Pictographic# E0.0 [1] (🧌) <reserved-1F9CC>
+1F9CC ; Extended_Pictographic# E14.0 [1] (🧌) troll
1F9CD..1F9CF ; Extended_Pictographic# E12.0 [3] (🧍..🧏) person standing..deaf person
1F9D0..1F9E6 ; Extended_Pictographic# E5.0 [23] (🧐..🧦) face with monocle..socks
1F9E7..1F9FF ; Extended_Pictographic# E11.0 [25] (🧧..🧿) red envelope..nazar amulet
@@ -1241,19 +1268,28 @@ E0020..E007F ; Emoji_Component # E0.0 [96] (󠀠..󠁿) tag space..c
1FA74 ; Extended_Pictographic# E13.0 [1] (🩴) thong sandal
1FA75..1FA77 ; Extended_Pictographic# E0.0 [3] (🩵..🩷) <reserved-1FA75>..<reserved-1FA77>
1FA78..1FA7A ; Extended_Pictographic# E12.0 [3] (🩸..🩺) drop of blood..stethoscope
-1FA7B..1FA7F ; Extended_Pictographic# E0.0 [5] (🩻..🩿) <reserved-1FA7B>..<reserved-1FA7F>
+1FA7B..1FA7C ; Extended_Pictographic# E14.0 [2] (🩻..🩼) x-ray..crutch
+1FA7D..1FA7F ; Extended_Pictographic# E0.0 [3] (🩽..🩿) <reserved-1FA7D>..<reserved-1FA7F>
1FA80..1FA82 ; Extended_Pictographic# E12.0 [3] (🪀..🪂) yo-yo..parachute
1FA83..1FA86 ; Extended_Pictographic# E13.0 [4] (🪃..🪆) boomerang..nesting dolls
1FA87..1FA8F ; Extended_Pictographic# E0.0 [9] (🪇..🪏) <reserved-1FA87>..<reserved-1FA8F>
1FA90..1FA95 ; Extended_Pictographic# E12.0 [6] (🪐..🪕) ringed planet..banjo
1FA96..1FAA8 ; Extended_Pictographic# E13.0 [19] (🪖..🪨) military helmet..rock
-1FAA9..1FAAF ; Extended_Pictographic# E0.0 [7] (🪩..🪯) <reserved-1FAA9>..<reserved-1FAAF>
+1FAA9..1FAAC ; Extended_Pictographic# E14.0 [4] (🪩..🪬) mirror ball..hamsa
+1FAAD..1FAAF ; Extended_Pictographic# E0.0 [3] (🪭..🪯) <reserved-1FAAD>..<reserved-1FAAF>
1FAB0..1FAB6 ; Extended_Pictographic# E13.0 [7] (🪰..🪶) fly..feather
-1FAB7..1FABF ; Extended_Pictographic# E0.0 [9] (🪷..🪿) <reserved-1FAB7>..<reserved-1FABF>
+1FAB7..1FABA ; Extended_Pictographic# E14.0 [4] (🪷..🪺) lotus..nest with eggs
+1FABB..1FABF ; Extended_Pictographic# E0.0 [5] (🪻..🪿) <reserved-1FABB>..<reserved-1FABF>
1FAC0..1FAC2 ; Extended_Pictographic# E13.0 [3] (🫀..🫂) anatomical heart..people hugging
-1FAC3..1FACF ; Extended_Pictographic# E0.0 [13] (🫃..🫏) <reserved-1FAC3>..<reserved-1FACF>
+1FAC3..1FAC5 ; Extended_Pictographic# E14.0 [3] (🫃..🫅) pregnant man..person with crown
+1FAC6..1FACF ; Extended_Pictographic# E0.0 [10] (🫆..🫏) <reserved-1FAC6>..<reserved-1FACF>
1FAD0..1FAD6 ; Extended_Pictographic# E13.0 [7] (🫐..🫖) blueberries..teapot
-1FAD7..1FAFF ; Extended_Pictographic# E0.0 [41] (🫗..🫿) <reserved-1FAD7>..<reserved-1FAFF>
+1FAD7..1FAD9 ; Extended_Pictographic# E14.0 [3] (🫗..🫙) pouring liquid..jar
+1FADA..1FADF ; Extended_Pictographic# E0.0 [6] (🫚..🫟) <reserved-1FADA>..<reserved-1FADF>
+1FAE0..1FAE7 ; Extended_Pictographic# E14.0 [8] (🫠..🫧) melting face..bubbles
+1FAE8..1FAEF ; Extended_Pictographic# E0.0 [8] (🫨..🫯) <reserved-1FAE8>..<reserved-1FAEF>
+1FAF0..1FAF6 ; Extended_Pictographic# E14.0 [7] (🫰..🫶) hand with index finger and thumb crossed..heart hands
+1FAF7..1FAFF ; Extended_Pictographic# E0.0 [9] (🫷..🫿) <reserved-1FAF7>..<reserved-1FAFF>
1FC00..1FFFD ; Extended_Pictographic# E0.0[1022] (🰀..🿽) <reserved-1FC00>..<reserved-1FFFD>
# Total elements: 3537
diff --git a/test/README.md b/test/README.md
index cc630cb8bf..a67040e68c 100644
--- a/test/README.md
+++ b/test/README.md
@@ -91,25 +91,33 @@ or:
Debugging tests
---------------
+- Each test gets a test id which looks like "T123". This also appears in the
+ log file. Child processes spawned from a test appear in the logs with the
+ *parent* name followed by "/c". Example:
+ ```
+ DBG 2022-06-15T18:37:45.226 T57.58016.0 UI: flush
+ DBG 2022-06-15T18:37:45.226 T57.58016.0 inbuf_poll:442: blocking... events_enabled=0 events_pending=0
+ DBG 2022-06-15T18:37:45.227 T57.58016.0/c UI: stop
+ INF 2022-06-15T18:37:45.227 T57.58016.0/c os_exit:595: Nvim exit: 0
+ DBG 2022-06-15T18:37:45.229 T57.58016.0 read_cb:118: closing Stream (0x7fd5d700ea18): EOF (end of file)
+ INF 2022-06-15T18:37:45.229 T57.58016.0 on_process_exit:400: exited: pid=58017 status=0 stoptime=0
+ ```
- You can set `$GDB` to [run tests under gdbserver](https://github.com/neovim/neovim/pull/1527).
And if `$VALGRIND` is set it will pass `--vgdb=yes` to valgrind instead of
starting gdbserver directly.
-- Hanging tests often happen due to unexpected `:h press-enter` prompts. The
+- Hanging tests can happen due to unexpected "press-enter" prompts. The
default screen width is 50 columns. Commands that try to print lines longer
than 50 columns in the command-line, e.g. `:edit very...long...path`, will
- trigger the prompt. In this case, a shorter path or `:silent edit` should be
- used.
+ trigger the prompt. Try using a shorter path, or `:silent edit`.
- If you can't figure out what is going on, try to visualize the screen. Put
this at the beginning of your test:
-
- ```lua
- local Screen = require('test.functional.ui.screen')
- local screen = Screen.new()
- screen:attach()
- ```
-
- Afterwards, put `screen:snapshot_util()` at any position in your test. See the
- comment at the top of `test/functional/ui/screen.lua` for more.
+ ```lua
+ local Screen = require('test.functional.ui.screen')
+ local screen = Screen.new()
+ screen:attach()
+ ```
+ Then put `screen:snapshot_util()` anywhere in your test. See the comments in
+ `test/functional/ui/screen.lua` for more info.
Filtering Tests
---------------
@@ -247,12 +255,17 @@ Number; !must be defined to function properly):
- `BUSTED_ARGS` (F) (U): arguments forwarded to `busted`.
+- `CC` (U) (S): specifies which C compiler to use to preprocess files.
+ Currently only compilers with gcc-compatible arguments are supported.
+
- `GDB` (F) (D): makes nvim instances to be run under `gdbserver`. It will be
accessible on `localhost:7777`: use `gdb build/bin/nvim`, type `target remote
:7777` inside.
- `GDBSERVER_PORT` (F) (I): overrides port used for `GDB`.
+- `LOG_DIR` (FU) (S!): specifies where to seek for valgrind and ASAN log files.
+
- `VALGRIND` (F) (D): makes nvim instances to be run under `valgrind`. Log
files are named `valgrind-%p.log` in this case. Note that non-empty valgrind
log may fail tests. Valgrind arguments may be seen in
@@ -269,11 +282,7 @@ Number; !must be defined to function properly):
- `NVIM_LUA_NOTRACK` (F) (D): disable reference counting of Lua objects
-- `NVIM_PROG`, `NVIM_PRG` (F) (S): override path to Neovim executable (default
- to `build/bin/nvim`).
-
-- `CC` (U) (S): specifies which C compiler to use to preprocess files.
- Currently only compilers with gcc-compatible arguments are supported.
+- `NVIM_PRG` (F) (S): path to Nvim executable (default: `build/bin/nvim`).
- `NVIM_TEST_MAIN_CDEFS` (U) (1): makes `ffi.cdef` run in main process. This
raises a possibility of bugs due to conflicts in header definitions, despite
@@ -295,8 +304,6 @@ Number; !must be defined to function properly):
- `NVIM_TEST_RUN_FAILING_TESTS` (U) (1): makes `itp` run tests which are known
to fail (marked by setting third argument to `true`).
-- `LOG_DIR` (FU) (S!): specifies where to seek for valgrind and ASAN log files.
-
- `NVIM_TEST_CORE_*` (FU) (S): a set of environment variables which specify
where to search for core files. Are supposed to be defined all at once.
diff --git a/test/benchmark/treesitter_spec.lua b/test/benchmark/treesitter_spec.lua
new file mode 100644
index 0000000000..5ce128c54a
--- /dev/null
+++ b/test/benchmark/treesitter_spec.lua
@@ -0,0 +1,53 @@
+local helpers = require('test.functional.helpers')(after_each)
+
+local clear = helpers.clear
+local exec_lua = helpers.exec_lua
+
+describe('treesitter perf', function()
+
+ setup(function()
+ clear()
+ end)
+
+ it('can handle large folds', function()
+ helpers.command'edit ./src/nvim/eval.c'
+ exec_lua[[
+ local parser = vim.treesitter.get_parser(0, "c", {})
+ vim.treesitter.highlighter.new(parser)
+
+ local function keys(k)
+ vim.api.nvim_feedkeys(k, 't', true)
+ end
+
+ vim.opt.foldmethod = "manual"
+ vim.opt.lazyredraw = false
+
+ vim.cmd '1000,7000fold'
+ vim.cmd '999'
+
+ local function mk_keys(n)
+ local acc = ""
+ for _ = 1, n do
+ acc = acc .. "j"
+ end
+ for _ = 1, n do
+ acc = acc .. "k"
+ end
+
+ return "qq" .. acc .. "q"
+ end
+
+ local start = vim.loop.hrtime()
+ keys(mk_keys(10))
+
+ for _ = 1, 100 do
+ keys "@q"
+ vim.cmd'redraw!'
+ end
+
+ return vim.loop.hrtime() - start
+ ]]
+
+ end)
+
+end)
diff --git a/test/busted/outputHandlers/nvim.lua b/test/busted/outputHandlers/nvim.lua
index 0e9801b94b..2ce32c3b7a 100644
--- a/test/busted/outputHandlers/nvim.lua
+++ b/test/busted/outputHandlers/nvim.lua
@@ -26,35 +26,35 @@ return function(options)
local repeatSuiteString = '\nRepeating all tests (run %d of %d) . . .\n\n'
local randomizeString = c.note('Note: Randomizing test order with a seed of %d.\n')
- local globalSetup = c.sect('[----------]') .. ' Global test environment setup.\n'
- local fileStartString = c.sect('[----------]') .. ' Running tests from ' .. c.file('%s') .. '\n'
- local runString = c.sect('[ RUN ]') .. ' ' .. c.test('%s') .. ': '
+ local globalSetup = c.sect('--------') .. ' Global test environment setup.\n'
+ local fileStartString = c.sect('--------') .. ' Running tests from ' .. c.file('%s') .. '\n'
+ local runString = c.sect('RUN ') .. ' ' .. c.test('%s') .. ': '
local successString = c.succ('OK') .. '\n'
local skippedString = c.skip('SKIP') .. '\n'
local failureString = c.fail('FAIL') .. '\n'
local errorString = c.errr('ERR') .. '\n'
- local fileEndString = c.sect('[----------]') .. ' '.. c.nmbr('%d') .. ' %s from ' .. c.file('%s') .. ' ' .. c.time('(%.2f ms total)') .. '\n\n'
- local globalTeardown = c.sect('[----------]') .. ' Global test environment teardown.\n'
- local suiteEndString = c.sect('[==========]') .. ' ' .. c.nmbr('%d') .. ' %s from ' .. c.nmbr('%d') .. ' test %s ran. ' .. c.time('(%.2f ms total)') .. '\n'
- local successStatus = c.succ('[ PASSED ]') .. ' ' .. c.nmbr('%d') .. ' %s.\n'
+ local fileEndString = c.sect('--------') .. ' '.. c.nmbr('%d') .. ' %s from ' .. c.file('%s') .. ' ' .. c.time('(%.2f ms total)') .. '\n\n'
+ local globalTeardown = c.sect('--------') .. ' Global test environment teardown.\n'
+ local suiteEndString = c.sect('========') .. ' ' .. c.nmbr('%d') .. ' %s from ' .. c.nmbr('%d') .. ' test %s ran. ' .. c.time('(%.2f ms total)') .. '\n'
+ local successStatus = c.succ('PASSED ') .. ' ' .. c.nmbr('%d') .. ' %s.\n'
local timeString = c.time('%.2f ms')
local summaryStrings = {
skipped = {
- header = c.skip('[ SKIPPED ]') .. ' ' .. c.nmbr('%d') .. ' %s, listed below:\n',
- test = c.skip('[ SKIPPED ]') .. ' %s\n',
+ header = c.skip('SKIPPED ') .. ' ' .. c.nmbr('%d') .. ' %s, listed below:\n',
+ test = c.skip('SKIPPED ') .. ' %s\n',
footer = ' ' .. c.nmbr('%d') .. ' SKIPPED %s\n',
},
failure = {
- header = c.fail('[ FAILED ]') .. ' ' .. c.nmbr('%d') .. ' %s, listed below:\n',
- test = c.fail('[ FAILED ]') .. ' %s\n',
+ header = c.fail('FAILED ') .. ' ' .. c.nmbr('%d') .. ' %s, listed below:\n',
+ test = c.fail('FAILED ') .. ' %s\n',
footer = ' ' .. c.nmbr('%d') .. ' FAILED %s\n',
},
error = {
- header = c.errr('[ ERROR ]') .. ' ' .. c.nmbr('%d') .. ' %s, listed below:\n',
- test = c.errr('[ ERROR ]') .. ' %s\n',
+ header = c.errr('ERROR ') .. ' ' .. c.nmbr('%d') .. ' %s, listed below:\n',
+ test = c.errr('ERROR ') .. ' %s\n',
footer = ' ' .. c.nmbr('%d') .. ' %s\n',
},
}
@@ -192,9 +192,11 @@ return function(options)
local tests = (testCount == 1 and 'test' or 'tests')
local files = (fileCount == 1 and 'file' or 'files')
io.write(globalTeardown)
- io.write(global_helpers.read_nvim_log(nil, true))
io.write(suiteEndString:format(testCount, tests, fileCount, files, elapsedTime_ms))
io.write(getSummaryString())
+ if failureCount > 0 or errorCount > 0 then
+ io.write(global_helpers.read_nvim_log(nil, true))
+ end
io.flush()
return nil, true
@@ -217,7 +219,9 @@ return function(options)
end
handler.testStart = function(element, _parent)
- io.write(runString:format(handler.getFullName(element)))
+ local testid = _G._nvim_test_id or ''
+ local desc = ('%s %s'):format(testid, handler.getFullName(element))
+ io.write(runString:format(desc))
io.flush()
return nil, true
diff --git a/test/config/paths.lua.in b/test/cmakeconfig/paths.lua.in
index e3979981ba..e3979981ba 100644
--- a/test/config/paths.lua.in
+++ b/test/cmakeconfig/paths.lua.in
diff --git a/test/functional/api/autocmd_spec.lua b/test/functional/api/autocmd_spec.lua
new file mode 100644
index 0000000000..a923f5df0e
--- /dev/null
+++ b/test/functional/api/autocmd_spec.lua
@@ -0,0 +1,1249 @@
+local helpers = require('test.functional.helpers')(after_each)
+
+local clear = helpers.clear
+local command = helpers.command
+local eq = helpers.eq
+local neq = helpers.neq
+local exec_lua = helpers.exec_lua
+local matches = helpers.matches
+local meths = helpers.meths
+local source = helpers.source
+local pcall_err = helpers.pcall_err
+
+before_each(clear)
+
+describe('autocmd api', function()
+ describe('nvim_create_autocmd', function()
+ it('does not allow "command" and "callback" in the same autocmd', function()
+ local ok, _ = pcall(meths.create_autocmd, "BufReadPost", {
+ pattern = "*.py,*.pyi",
+ command = "echo 'Should Have Errored",
+ callback = "not allowed",
+ })
+
+ eq(false, ok)
+ end)
+
+ it('doesnt leak when you use ++once', function()
+ eq(1, exec_lua([[
+ local count = 0
+
+ vim.api.nvim_create_autocmd("FileType", {
+ pattern = "*",
+ callback = function() count = count + 1 end,
+ once = true
+ })
+
+ vim.cmd "set filetype=txt"
+ vim.cmd "set filetype=python"
+
+ return count
+ ]], {}))
+ end)
+
+ it('allows passing buffer by key', function()
+ meths.set_var('called', 0)
+
+ meths.create_autocmd("FileType", {
+ command = "let g:called = g:called + 1",
+ buffer = 0,
+ })
+
+ meths.command "set filetype=txt"
+ eq(1, meths.get_var('called'))
+
+ -- switch to a new buffer
+ meths.command "new"
+ meths.command "set filetype=python"
+
+ eq(1, meths.get_var('called'))
+ end)
+
+ it('does not allow passing buffer and patterns', function()
+ local ok = pcall(meths.create_autocmd, "Filetype", {
+ command = "let g:called = g:called + 1",
+ buffer = 0,
+ pattern = "*.py",
+ })
+
+ eq(false, ok)
+ end)
+
+ it('does not allow passing invalid buffers', function()
+ local ok, msg = pcall(meths.create_autocmd, "Filetype", {
+ command = "let g:called = g:called + 1",
+ buffer = -1,
+ })
+
+ eq(false, ok)
+ matches('Invalid buffer id', msg)
+ end)
+
+ it('errors on non-functions for cb', function()
+ eq(false, pcall(exec_lua, [[
+ vim.api.nvim_create_autocmd("BufReadPost", {
+ pattern = "*.py,*.pyi",
+ callback = 5,
+ })
+ ]]))
+ end)
+
+ it('allow passing pattern and <buffer> in same pattern', function()
+ local ok = pcall(meths.create_autocmd, "BufReadPost", {
+ pattern = "*.py,<buffer>",
+ command = "echo 'Should Not Error'"
+ })
+
+ eq(true, ok)
+ end)
+
+ it('should handle multiple values as comma separated list', function()
+ meths.create_autocmd("BufReadPost", {
+ pattern = "*.py,*.pyi",
+ command = "echo 'Should Not Have Errored'"
+ })
+
+ -- We should have one autocmd for *.py and one for *.pyi
+ eq(2, #meths.get_autocmds { event = "BufReadPost" })
+ end)
+
+ it('should handle multiple values as array', function()
+ meths.create_autocmd("BufReadPost", {
+ pattern = { "*.py", "*.pyi", },
+ command = "echo 'Should Not Have Errored'"
+ })
+
+ -- We should have one autocmd for *.py and one for *.pyi
+ eq(2, #meths.get_autocmds { event = "BufReadPost" })
+ end)
+
+ describe('desc', function()
+ it('can add description to one autocmd', function()
+ local cmd = "echo 'Should Not Have Errored'"
+ local desc = "Can show description"
+ meths.create_autocmd("BufReadPost", {
+ pattern = "*.py",
+ command = cmd,
+ desc = desc,
+ })
+
+ eq(desc, meths.get_autocmds { event = "BufReadPost" }[1].desc)
+ eq(cmd, meths.get_autocmds { event = "BufReadPost" }[1].command)
+ end)
+
+ it('can add description to one autocmd that uses a callback', function()
+ local desc = 'Can show description'
+ meths.set_var('desc', desc)
+
+ local result = exec_lua([[
+ local callback = function() print 'Should Not Have Errored' end
+ vim.api.nvim_create_autocmd("BufReadPost", {
+ pattern = "*.py",
+ callback = callback,
+ desc = vim.g.desc,
+ })
+ local aus = vim.api.nvim_get_autocmds({ event = 'BufReadPost' })
+ local first = aus[1]
+ return {
+ desc = first.desc,
+ cbtype = type(first.callback)
+ }
+ ]])
+
+ eq({ desc = desc, cbtype = 'function' }, result)
+ end)
+
+ it('will not add a description unless it was provided', function()
+ exec_lua([[
+ local callback = function() print 'Should Not Have Errored' end
+ vim.api.nvim_create_autocmd("BufReadPost", {
+ pattern = "*.py",
+ callback = callback,
+ })
+ ]])
+
+ eq(nil, meths.get_autocmds({ event = 'BufReadPost' })[1].desc)
+ end)
+
+ it('can add description to multiple autocmd', function()
+ meths.create_autocmd("BufReadPost", {
+ pattern = {"*.py", "*.pyi"},
+ command = "echo 'Should Not Have Errored'",
+ desc = "Can show description",
+ })
+
+ local aus = meths.get_autocmds { event = "BufReadPost" }
+ eq(2, #aus)
+ eq("Can show description", aus[1].desc)
+ eq("Can show description", aus[2].desc)
+ end)
+ end)
+
+ pending('script and verbose settings', function()
+ it('marks API client', function()
+ meths.create_autocmd("BufReadPost", {
+ pattern = "*.py",
+ command = "echo 'Should Not Have Errored'",
+ desc = "Can show description",
+ })
+
+ local aus = meths.get_autocmds { event = "BufReadPost" }
+ eq(1, #aus, aus)
+ end)
+ end)
+
+ it('removes an autocommand if the callback returns true', function()
+ meths.set_var("some_condition", false)
+
+ exec_lua [[
+ vim.api.nvim_create_autocmd("User", {
+ pattern = "Test",
+ desc = "A test autocommand",
+ callback = function()
+ return vim.g.some_condition
+ end,
+ })
+ ]]
+
+ meths.exec_autocmds("User", {pattern = "Test"})
+
+ local aus = meths.get_autocmds({ event = 'User', pattern = 'Test' })
+ local first = aus[1]
+ eq(first.id, 1)
+
+ meths.set_var("some_condition", true)
+ meths.exec_autocmds("User", {pattern = "Test"})
+ eq({}, meths.get_autocmds({event = "User", pattern = "Test"}))
+ end)
+
+ it('receives an args table', function()
+ local res = exec_lua [[
+ local group_id = vim.api.nvim_create_augroup("TestGroup", {})
+ local autocmd_id = vim.api.nvim_create_autocmd("User", {
+ group = "TestGroup",
+ pattern = "Te*",
+ callback = function(args)
+ vim.g.autocmd_args = args
+ end,
+ })
+
+ return {group_id, autocmd_id}
+ ]]
+
+ meths.exec_autocmds("User", {pattern = "Test pattern"})
+ eq({
+ id = res[2],
+ group = res[1],
+ event = "User",
+ match = "Test pattern",
+ file = "Test pattern",
+ buf = 1,
+ }, meths.get_var("autocmd_args"))
+
+ -- Test without a group
+ res = exec_lua [[
+ local autocmd_id = vim.api.nvim_create_autocmd("User", {
+ pattern = "*",
+ callback = function(args)
+ vim.g.autocmd_args = args
+ end,
+ })
+
+ return {autocmd_id}
+ ]]
+
+ meths.exec_autocmds("User", {pattern = "some_pat"})
+ eq({
+ id = res[1],
+ group = nil,
+ event = "User",
+ match = "some_pat",
+ file = "some_pat",
+ buf = 1,
+ }, meths.get_var("autocmd_args"))
+
+ end)
+
+ it('can receive arbitrary data', function()
+ local function test(data)
+ eq(data, exec_lua([[
+ local input = ...
+ local output
+ vim.api.nvim_create_autocmd("User", {
+ pattern = "Test",
+ callback = function(args)
+ output = args.data
+ end,
+ })
+
+ vim.api.nvim_exec_autocmds("User", {
+ pattern = "Test",
+ data = input,
+ })
+
+ return output
+ ]], data))
+ end
+
+ test("Hello")
+ test(42)
+ test(true)
+ test({ "list" })
+ test({ foo = "bar" })
+ end)
+ end)
+
+ describe('nvim_get_autocmds', function()
+ describe('events', function()
+ it('should return one autocmd when there is only one for an event', function()
+ command [[au! InsertEnter]]
+ command [[au InsertEnter * :echo "1"]]
+
+ local aus = meths.get_autocmds { event = "InsertEnter" }
+ eq(1, #aus)
+ end)
+
+ it('should return two autocmds when there are two for an event', function()
+ command [[au! InsertEnter]]
+ command [[au InsertEnter * :echo "1"]]
+ command [[au InsertEnter * :echo "2"]]
+
+ local aus = meths.get_autocmds { event = "InsertEnter" }
+ eq(2, #aus)
+ end)
+
+ it('should return the same thing if you use string or list', function()
+ command [[au! InsertEnter]]
+ command [[au InsertEnter * :echo "1"]]
+ command [[au InsertEnter * :echo "2"]]
+
+ local string_aus = meths.get_autocmds { event = "InsertEnter" }
+ local array_aus = meths.get_autocmds { event = { "InsertEnter" } }
+ eq(string_aus, array_aus)
+ end)
+
+ it('should return two autocmds when there are two for an event', function()
+ command [[au! InsertEnter]]
+ command [[au! InsertLeave]]
+ command [[au InsertEnter * :echo "1"]]
+ command [[au InsertEnter * :echo "2"]]
+
+ local aus = meths.get_autocmds { event = { "InsertEnter", "InsertLeave" } }
+ eq(2, #aus)
+ end)
+
+ it('should return different IDs for different autocmds', function()
+ command [[au! InsertEnter]]
+ command [[au! InsertLeave]]
+ command [[au InsertEnter * :echo "1"]]
+ source [[
+ call nvim_create_autocmd("InsertLeave", #{
+ \ command: ":echo 2",
+ \ })
+ ]]
+
+ local aus = meths.get_autocmds { event = { "InsertEnter", "InsertLeave" } }
+ local first = aus[1]
+ eq(first.id, nil)
+
+ -- TODO: Maybe don't have this number, just assert it's not nil
+ local second = aus[2]
+ neq(second.id, nil)
+
+ meths.del_autocmd(second.id)
+ local new_aus = meths.get_autocmds { event = { "InsertEnter", "InsertLeave" } }
+ eq(1, #new_aus)
+ eq(first, new_aus[1])
+ end)
+
+ it('should return event name', function()
+ command [[au! InsertEnter]]
+ command [[au InsertEnter * :echo "1"]]
+
+ local aus = meths.get_autocmds { event = "InsertEnter" }
+ eq({ { buflocal = false, command = ':echo "1"', event = "InsertEnter", once = false, pattern = "*" } }, aus)
+ end)
+
+ it('should work with buffer numbers', function()
+ command [[new]]
+ command [[au! InsertEnter]]
+ command [[au InsertEnter <buffer=1> :echo "1"]]
+ command [[au InsertEnter <buffer=2> :echo "2"]]
+
+ local aus = meths.get_autocmds { event = "InsertEnter", buffer = 0 }
+ eq({{
+ buffer = 2,
+ buflocal = true,
+ command = ':echo "2"',
+ event = 'InsertEnter',
+ once = false,
+ pattern = '<buffer=2>',
+ }}, aus)
+
+ aus = meths.get_autocmds { event = "InsertEnter", buffer = 1 }
+ eq({{
+ buffer = 1,
+ buflocal = true,
+ command = ':echo "1"',
+ event = "InsertEnter",
+ once = false,
+ pattern = "<buffer=1>",
+ }}, aus)
+
+ aus = meths.get_autocmds { event = "InsertEnter", buffer = { 1, 2 } }
+ eq({{
+ buffer = 1,
+ buflocal = true,
+ command = ':echo "1"',
+ event = "InsertEnter",
+ once = false,
+ pattern = "<buffer=1>",
+ }, {
+ buffer = 2,
+ buflocal = true,
+ command = ':echo "2"',
+ event = "InsertEnter",
+ once = false,
+ pattern = "<buffer=2>",
+ }}, aus)
+
+ eq("Invalid value for 'buffer': must be an integer or array of integers", pcall_err(meths.get_autocmds, { event = "InsertEnter", buffer = "foo" }))
+ eq("Invalid value for 'buffer': must be an integer", pcall_err(meths.get_autocmds, { event = "InsertEnter", buffer = { "foo", 42 } }))
+ eq("Invalid buffer id: 42", pcall_err(meths.get_autocmds, { event = "InsertEnter", buffer = { 42 } }))
+
+ local bufs = {}
+ for _ = 1, 257 do
+ table.insert(bufs, meths.create_buf(true, false))
+ end
+
+ eq("Too many buffers. Please limit yourself to 256 or fewer", pcall_err(meths.get_autocmds, { event = "InsertEnter", buffer = bufs }))
+ end)
+
+ it('should return autocmds when group is specified by id', function()
+ local auid = meths.create_augroup("nvim_test_augroup", { clear = true })
+ meths.create_autocmd("FileType", { group = auid, command = 'echo "1"' })
+ meths.create_autocmd("FileType", { group = auid, command = 'echo "2"' })
+
+ local aus = meths.get_autocmds { group = auid }
+ eq(2, #aus)
+
+ local aus2 = meths.get_autocmds { group = auid, event = "InsertEnter" }
+ eq(0, #aus2)
+ end)
+
+ it('should return autocmds when group is specified by name', function()
+ local auname = "nvim_test_augroup"
+ meths.create_augroup(auname, { clear = true })
+ meths.create_autocmd("FileType", { group = auname, command = 'echo "1"' })
+ meths.create_autocmd("FileType", { group = auname, command = 'echo "2"' })
+
+ local aus = meths.get_autocmds { group = auname }
+ eq(2, #aus)
+
+ local aus2 = meths.get_autocmds { group = auname, event = "InsertEnter" }
+ eq(0, #aus2)
+ end)
+
+ it('should respect nested', function()
+ local bufs = exec_lua [[
+ local count = 0
+ vim.api.nvim_create_autocmd("BufNew", {
+ once = false,
+ nested = true,
+ callback = function()
+ count = count + 1
+ if count > 5 then
+ return true
+ end
+
+ vim.cmd(string.format("new README_%s.md", count))
+ end
+ })
+
+ vim.cmd "new First.md"
+
+ return vim.api.nvim_list_bufs()
+ ]]
+
+ -- 1 for the first buffer
+ -- 2 for First.md
+ -- 3-7 for the 5 we make in the autocmd
+ eq({1, 2, 3, 4, 5, 6, 7}, bufs)
+ end)
+
+ it('can retrieve a callback from an autocmd', function()
+ local content = 'I Am A Callback'
+ meths.set_var('content', content)
+
+ local result = exec_lua([[
+ local cb = function() return vim.g.content end
+ vim.api.nvim_create_autocmd("User", {
+ pattern = "TestTrigger",
+ desc = "A test autocommand with a callback",
+ callback = cb,
+ })
+ local aus = vim.api.nvim_get_autocmds({ event = 'User', pattern = 'TestTrigger'})
+ local first = aus[1]
+ return {
+ cb = {
+ type = type(first.callback),
+ can_retrieve = first.callback() == vim.g.content
+ }
+ }
+ ]])
+
+ eq("function", result.cb.type)
+ eq(true, result.cb.can_retrieve)
+ end)
+
+ it('will return an empty string as the command for an autocmd that uses a callback', function()
+ local result = exec_lua([[
+ local callback = function() print 'I Am A Callback' end
+ vim.api.nvim_create_autocmd("BufWritePost", {
+ pattern = "*.py",
+ callback = callback,
+ })
+ local aus = vim.api.nvim_get_autocmds({ event = 'BufWritePost' })
+ local first = aus[1]
+ return {
+ command = first.command,
+ cbtype = type(first.callback)
+ }
+ ]])
+
+ eq({ command = "", cbtype = 'function' }, result)
+ end)
+ end)
+
+ describe('groups', function()
+ before_each(function()
+ command [[au! InsertEnter]]
+
+ command [[au InsertEnter * :echo "No Group"]]
+
+ command [[augroup GroupOne]]
+ command [[ au InsertEnter * :echo "GroupOne:1"]]
+ command [[augroup END]]
+
+ command [[augroup GroupTwo]]
+ command [[ au InsertEnter * :echo "GroupTwo:2"]]
+ command [[ au InsertEnter * :echo "GroupTwo:3"]]
+ command [[augroup END]]
+ end)
+
+ it('should return all groups if no group is specified', function()
+ local aus = meths.get_autocmds { event = "InsertEnter" }
+ if #aus ~= 4 then
+ eq({}, aus)
+ end
+
+ eq(4, #aus)
+ end)
+
+ it('should return only the group specified', function()
+ local aus = meths.get_autocmds {
+ event = "InsertEnter",
+ group = "GroupOne",
+ }
+
+ eq(1, #aus)
+ eq([[:echo "GroupOne:1"]], aus[1].command)
+ eq("GroupOne", aus[1].group_name)
+ end)
+
+ it('should return only the group specified, multiple values', function()
+ local aus = meths.get_autocmds {
+ event = "InsertEnter",
+ group = "GroupTwo",
+ }
+
+ eq(2, #aus)
+ eq([[:echo "GroupTwo:2"]], aus[1].command)
+ eq("GroupTwo", aus[1].group_name)
+ eq([[:echo "GroupTwo:3"]], aus[2].command)
+ eq("GroupTwo", aus[2].group_name)
+ end)
+ end)
+
+ describe('groups: 2', function()
+ it('raises error for undefined augroup name', function()
+ local success, code = unpack(meths.exec_lua([[
+ return {pcall(function()
+ vim.api.nvim_create_autocmd("FileType", {
+ pattern = "*",
+ group = "NotDefined",
+ command = "echo 'hello'",
+ })
+ end)}
+ ]], {}))
+
+ eq(false, success)
+ matches('invalid augroup: NotDefined', code)
+ end)
+
+ it('raises error for undefined augroup id', function()
+ local success, code = unpack(meths.exec_lua([[
+ return {pcall(function()
+ -- Make sure the augroup is deleted
+ vim.api.nvim_del_augroup_by_id(1)
+
+ vim.api.nvim_create_autocmd("FileType", {
+ pattern = "*",
+ group = 1,
+ command = "echo 'hello'",
+ })
+ end)}
+ ]], {}))
+
+ eq(false, success)
+ matches('invalid augroup: 1', code)
+ end)
+
+ it('raises error for invalid group type', function()
+ local success, code = unpack(meths.exec_lua([[
+ return {pcall(function()
+ vim.api.nvim_create_autocmd("FileType", {
+ pattern = "*",
+ group = true,
+ command = "echo 'hello'",
+ })
+ end)}
+ ]], {}))
+
+ eq(false, success)
+ matches("'group' must be a string or an integer", code)
+ end)
+ end)
+
+ describe('patterns', function()
+ before_each(function()
+ command [[au! InsertEnter]]
+
+ command [[au InsertEnter * :echo "No Group"]]
+ command [[au InsertEnter *.one :echo "GroupOne:1"]]
+ command [[au InsertEnter *.two :echo "GroupTwo:2"]]
+ command [[au InsertEnter *.two :echo "GroupTwo:3"]]
+ command [[au InsertEnter <buffer> :echo "Buffer"]]
+ end)
+
+ it('should should return for literal match', function()
+ local aus = meths.get_autocmds {
+ event = "InsertEnter",
+ pattern = "*"
+ }
+
+ eq(1, #aus)
+ eq([[:echo "No Group"]], aus[1].command)
+ end)
+
+ it('should return for multiple matches', function()
+ -- vim.api.nvim_get_autocmds
+ local aus = meths.get_autocmds {
+ event = "InsertEnter",
+ pattern = { "*.one", "*.two" },
+ }
+
+ eq(3, #aus)
+ eq([[:echo "GroupOne:1"]], aus[1].command)
+ eq([[:echo "GroupTwo:2"]], aus[2].command)
+ eq([[:echo "GroupTwo:3"]], aus[3].command)
+ end)
+
+ it('should work for buffer autocmds', function()
+ local normalized_aus = meths.get_autocmds {
+ event = "InsertEnter",
+ pattern = "<buffer=1>",
+ }
+
+ local raw_aus = meths.get_autocmds {
+ event = "InsertEnter",
+ pattern = "<buffer>",
+ }
+
+ local zero_aus = meths.get_autocmds {
+ event = "InsertEnter",
+ pattern = "<buffer=0>",
+ }
+
+ eq(normalized_aus, raw_aus)
+ eq(normalized_aus, zero_aus)
+ eq([[:echo "Buffer"]], normalized_aus[1].command)
+ end)
+ end)
+ end)
+
+ describe('nvim_exec_autocmds', function()
+ it("can trigger builtin autocmds", function()
+ meths.set_var("autocmd_executed", false)
+
+ meths.create_autocmd("BufReadPost", {
+ pattern = "*",
+ command = "let g:autocmd_executed = v:true",
+ })
+
+ eq(false, meths.get_var("autocmd_executed"))
+ meths.exec_autocmds("BufReadPost", {})
+ eq(true, meths.get_var("autocmd_executed"))
+ end)
+
+ it("can trigger multiple patterns", function()
+ meths.set_var("autocmd_executed", 0)
+
+ meths.create_autocmd("BufReadPost", {
+ pattern = "*",
+ command = "let g:autocmd_executed += 1",
+ })
+
+ meths.exec_autocmds("BufReadPost", { pattern = { "*.lua", "*.vim" } })
+ eq(2, meths.get_var("autocmd_executed"))
+
+ meths.create_autocmd("BufReadPre", {
+ pattern = { "bar", "foo" },
+ command = "let g:autocmd_executed += 10",
+ })
+
+ meths.exec_autocmds("BufReadPre", { pattern = { "foo", "bar", "baz", "frederick" }})
+ eq(22, meths.get_var("autocmd_executed"))
+ end)
+
+ it("can pass the buffer", function()
+ meths.set_var("buffer_executed", -1)
+ eq(-1, meths.get_var("buffer_executed"))
+
+ meths.create_autocmd("BufLeave", {
+ pattern = "*",
+ command = 'let g:buffer_executed = +expand("<abuf>")',
+ })
+
+ -- Doesn't execute for other non-matching events
+ meths.exec_autocmds("CursorHold", { buffer = 1 })
+ eq(-1, meths.get_var("buffer_executed"))
+
+ meths.exec_autocmds("BufLeave", { buffer = 1 })
+ eq(1, meths.get_var("buffer_executed"))
+ end)
+
+ it("can pass the filename, pattern match", function()
+ meths.set_var("filename_executed", 'none')
+ eq('none', meths.get_var("filename_executed"))
+
+ meths.create_autocmd("BufEnter", {
+ pattern = "*.py",
+ command = 'let g:filename_executed = expand("<afile>")',
+ })
+
+ -- Doesn't execute for other non-matching events
+ meths.exec_autocmds("CursorHold", { buffer = 1 })
+ eq('none', meths.get_var("filename_executed"))
+
+ meths.command('edit __init__.py')
+ eq('__init__.py', meths.get_var("filename_executed"))
+ end)
+
+ it('cannot pass buf and fname', function()
+ local ok = pcall(meths.exec_autocmds, "BufReadPre", { pattern = "literally_cannot_error.rs", buffer = 1 })
+ eq(false, ok)
+ end)
+
+ it("can pass the filename, exact match", function()
+ meths.set_var("filename_executed", 'none')
+ eq('none', meths.get_var("filename_executed"))
+
+ meths.command('edit other_file.txt')
+ meths.command('edit __init__.py')
+ eq('none', meths.get_var("filename_executed"))
+
+ meths.create_autocmd("CursorHoldI", {
+ pattern = "__init__.py",
+ command = 'let g:filename_executed = expand("<afile>")',
+ })
+
+ -- Doesn't execute for other non-matching events
+ meths.exec_autocmds("CursorHoldI", { buffer = 1 })
+ eq('none', meths.get_var("filename_executed"))
+
+ meths.exec_autocmds("CursorHoldI", { buffer = meths.get_current_buf() })
+ eq('__init__.py', meths.get_var("filename_executed"))
+
+ -- Reset filename
+ meths.set_var("filename_executed", 'none')
+
+ meths.exec_autocmds("CursorHoldI", { pattern = '__init__.py' })
+ eq('__init__.py', meths.get_var("filename_executed"))
+ end)
+
+ it("works with user autocmds", function()
+ meths.set_var("matched", 'none')
+
+ meths.create_autocmd("User", {
+ pattern = "TestCommand",
+ command = 'let g:matched = "matched"'
+ })
+
+ meths.exec_autocmds("User", { pattern = "OtherCommand" })
+ eq('none', meths.get_var('matched'))
+ meths.exec_autocmds("User", { pattern = "TestCommand" })
+ eq('matched', meths.get_var('matched'))
+ end)
+
+ it('can pass group by id', function()
+ meths.set_var("group_executed", false)
+
+ local auid = meths.create_augroup("nvim_test_augroup", { clear = true })
+ meths.create_autocmd("FileType", {
+ group = auid,
+ command = 'let g:group_executed = v:true',
+ })
+
+ eq(false, meths.get_var("group_executed"))
+ meths.exec_autocmds("FileType", { group = auid })
+ eq(true, meths.get_var("group_executed"))
+ end)
+
+ it('can pass group by name', function()
+ meths.set_var("group_executed", false)
+
+ local auname = "nvim_test_augroup"
+ meths.create_augroup(auname, { clear = true })
+ meths.create_autocmd("FileType", {
+ group = auname,
+ command = 'let g:group_executed = v:true',
+ })
+
+ eq(false, meths.get_var("group_executed"))
+ meths.exec_autocmds("FileType", { group = auname })
+ eq(true, meths.get_var("group_executed"))
+ end)
+ end)
+
+ describe('nvim_create_augroup', function()
+ before_each(function()
+ clear()
+
+ meths.set_var('executed', 0)
+ end)
+
+ local make_counting_autocmd = function(opts)
+ opts = opts or {}
+
+ local resulting = {
+ pattern = "*",
+ command = "let g:executed = g:executed + 1",
+ }
+
+ resulting.group = opts.group
+ resulting.once = opts.once
+
+ meths.create_autocmd("FileType", resulting)
+ end
+
+ local set_ft = function(ft)
+ ft = ft or "txt"
+ source(string.format("set filetype=%s", ft))
+ end
+
+ local get_executed_count = function()
+ return meths.get_var('executed')
+ end
+
+ it('can be added in a group', function()
+ local augroup = "TestGroup"
+ meths.create_augroup(augroup, { clear = true })
+ make_counting_autocmd { group = augroup }
+
+ set_ft("txt")
+ set_ft("python")
+
+ eq(2, get_executed_count())
+ end)
+
+ it('works getting called multiple times', function()
+ make_counting_autocmd()
+ set_ft()
+ set_ft()
+ set_ft()
+
+ eq(3, get_executed_count())
+ end)
+
+ it('handles ++once', function()
+ make_counting_autocmd {once = true}
+ set_ft('txt')
+ set_ft('help')
+ set_ft('txt')
+ set_ft('help')
+
+ eq(1, get_executed_count())
+ end)
+
+ it('errors on unexpected keys', function()
+ local success, code = pcall(meths.create_autocmd, "FileType", {
+ pattern = "*",
+ not_a_valid_key = "NotDefined",
+ })
+
+ eq(false, success)
+ matches('not_a_valid_key', code)
+ end)
+
+ it('can execute simple callback', function()
+ exec_lua([[
+ vim.g.executed = false
+
+ vim.api.nvim_create_autocmd("FileType", {
+ pattern = "*",
+ callback = function() vim.g.executed = true end,
+ })
+ ]], {})
+
+
+ eq(true, exec_lua([[
+ vim.cmd "set filetype=txt"
+ return vim.g.executed
+ ]], {}))
+ end)
+
+ it('calls multiple lua callbacks for the same autocmd execution', function()
+ eq(4, exec_lua([[
+ local count = 0
+ local counter = function()
+ count = count + 1
+ end
+
+ vim.api.nvim_create_autocmd("FileType", {
+ pattern = "*",
+ callback = counter,
+ })
+
+ vim.api.nvim_create_autocmd("FileType", {
+ pattern = "*",
+ callback = counter,
+ })
+
+ vim.cmd "set filetype=txt"
+ vim.cmd "set filetype=txt"
+
+ return count
+ ]], {}))
+ end)
+
+ it('properly releases functions with ++once', function()
+ exec_lua([[
+ WeakTable = setmetatable({}, { __mode = "k" })
+
+ OnceCount = 0
+
+ MyVal = {}
+ WeakTable[MyVal] = true
+
+ vim.api.nvim_create_autocmd("FileType", {
+ pattern = "*",
+ callback = function()
+ OnceCount = OnceCount + 1
+ MyVal = {}
+ end,
+ once = true
+ })
+ ]])
+
+ command [[set filetype=txt]]
+ eq(1, exec_lua([[return OnceCount]], {}))
+
+ exec_lua([[collectgarbage()]], {})
+
+ command [[set filetype=txt]]
+ eq(1, exec_lua([[return OnceCount]], {}))
+
+ eq(0, exec_lua([[
+ local count = 0
+ for _ in pairs(WeakTable) do
+ count = count + 1
+ end
+
+ return count
+ ]]), "Should have no keys remaining")
+ end)
+
+ it('groups can be cleared', function()
+ local augroup = "TestGroup"
+ meths.create_augroup(augroup, { clear = true })
+ meths.create_autocmd("FileType", {
+ group = augroup,
+ command = "let g:executed = g:executed + 1"
+ })
+
+ set_ft("txt")
+ set_ft("txt")
+ eq(2, get_executed_count(), "should only count twice")
+
+ meths.create_augroup(augroup, { clear = true })
+ eq({}, meths.get_autocmds { group = augroup })
+
+ set_ft("txt")
+ set_ft("txt")
+ eq(2, get_executed_count(), "No additional counts")
+ end)
+
+ it('can delete non-existent groups with pcall', function()
+ eq(false, exec_lua[[return pcall(vim.api.nvim_del_augroup_by_name, 'noexist')]])
+ eq('Vim:E367: No such group: "noexist"', pcall_err(meths.del_augroup_by_name, 'noexist'))
+
+ eq(false, exec_lua[[return pcall(vim.api.nvim_del_augroup_by_id, -12342)]])
+ eq('Vim:E367: No such group: "--Deleted--"', pcall_err(meths.del_augroup_by_id, -12312))
+ end)
+
+ it('groups work with once', function()
+ local augroup = "TestGroup"
+
+ meths.create_augroup(augroup, { clear = true })
+ make_counting_autocmd { group = augroup, once = true }
+
+ set_ft("txt")
+ set_ft("python")
+
+ eq(1, get_executed_count())
+ end)
+
+ it('autocmds can be registered multiple times.', function()
+ local augroup = "TestGroup"
+
+ meths.create_augroup(augroup, { clear = true })
+ make_counting_autocmd { group = augroup, once = false }
+ make_counting_autocmd { group = augroup, once = false }
+ make_counting_autocmd { group = augroup, once = false }
+
+ set_ft("txt")
+ set_ft("python")
+
+ eq(3 * 2, get_executed_count())
+ end)
+
+ it('can be deleted', function()
+ local augroup = "WillBeDeleted"
+
+ meths.create_augroup(augroup, { clear = true })
+ meths.create_autocmd({"Filetype"}, {
+ pattern = "*",
+ command = "echo 'does not matter'",
+ })
+
+ -- Clears the augroup from before, which erases the autocmd
+ meths.create_augroup(augroup, { clear = true })
+
+ local result = #meths.get_autocmds { group = augroup }
+
+ eq(0, result)
+ end)
+
+ it('can be used for buffer local autocmds', function()
+ local augroup = "WillBeDeleted"
+
+ meths.set_var("value_set", false)
+
+ meths.create_augroup(augroup, { clear = true })
+ meths.create_autocmd("Filetype", {
+ pattern = "<buffer>",
+ command = "let g:value_set = v:true",
+ })
+
+ command "new"
+ command "set filetype=python"
+
+ eq(false, meths.get_var("value_set"))
+ end)
+
+ it('can accept vimscript functions', function()
+ source [[
+ let g:vimscript_executed = 0
+
+ function! MyVimscriptFunction() abort
+ let g:vimscript_executed = g:vimscript_executed + 1
+ endfunction
+
+ call nvim_create_autocmd("FileType", #{
+ \ pattern: ["python", "javascript"],
+ \ callback: "MyVimscriptFunction",
+ \ })
+
+ set filetype=txt
+ set filetype=python
+ set filetype=txt
+ set filetype=javascript
+ set filetype=txt
+ ]]
+
+ eq(2, meths.get_var("vimscript_executed"))
+ end)
+ end)
+
+ describe('augroup!', function()
+ it('legacy: should clear and not return any autocmds for delete groups', function()
+ command('augroup TEMP_A')
+ command(' autocmd! BufReadPost *.py :echo "Hello"')
+ command('augroup END')
+
+ command('augroup! TEMP_A')
+
+ eq(false, pcall(meths.get_autocmds, { group = 'TEMP_A' }))
+
+ -- For some reason, augroup! doesn't clear the autocmds themselves, which is just wild
+ -- but we managed to keep this behavior.
+ eq(1, #meths.get_autocmds { event = 'BufReadPost' })
+ end)
+
+ it('legacy: remove augroups that have no autocmds', function()
+ command('augroup TEMP_AB')
+ command('augroup END')
+
+ command('augroup! TEMP_AB')
+
+ eq(false, pcall(meths.get_autocmds, { group = 'TEMP_AB' }))
+ eq(0, #meths.get_autocmds { event = 'BufReadPost' })
+ end)
+
+ it('legacy: multiple remove and add augroup', function()
+ command('augroup TEMP_ABC')
+ command(' au!')
+ command(' autocmd BufReadPost *.py echo "Hello"')
+ command('augroup END')
+
+ command('augroup! TEMP_ABC')
+
+ -- Should still have one autocmd :'(
+ local aus = meths.get_autocmds { event = 'BufReadPost' }
+ eq(1, #aus, aus)
+
+ command('augroup TEMP_ABC')
+ command(' au!')
+ command(' autocmd BufReadPost *.py echo "Hello"')
+ command('augroup END')
+
+ -- Should now have two autocmds :'(
+ aus = meths.get_autocmds { event = 'BufReadPost' }
+ eq(2, #aus, aus)
+
+ command('augroup! TEMP_ABC')
+
+ eq(false, pcall(meths.get_autocmds, { group = 'TEMP_ABC' }))
+ eq(2, #meths.get_autocmds { event = 'BufReadPost' })
+ end)
+
+ it('api: should clear and not return any autocmds for delete groups by id', function()
+ command('augroup TEMP_ABCD')
+ command('autocmd! BufReadPost *.py :echo "Hello"')
+ command('augroup END')
+
+ local augroup_id = meths.create_augroup("TEMP_ABCD", { clear = false })
+ meths.del_augroup_by_id(augroup_id)
+
+ -- For good reason, we kill all the autocmds from del_augroup,
+ -- so now this works as expected
+ eq(false, pcall(meths.get_autocmds, { group = 'TEMP_ABCD' }))
+ eq(0, #meths.get_autocmds { event = 'BufReadPost' })
+ end)
+
+ it('api: should clear and not return any autocmds for delete groups by name', function()
+ command('augroup TEMP_ABCDE')
+ command('autocmd! BufReadPost *.py :echo "Hello"')
+ command('augroup END')
+
+ meths.del_augroup_by_name("TEMP_ABCDE")
+
+ -- For good reason, we kill all the autocmds from del_augroup,
+ -- so now this works as expected
+ eq(false, pcall(meths.get_autocmds, { group = 'TEMP_ABCDE' }))
+ eq(0, #meths.get_autocmds { event = 'BufReadPost' })
+ end)
+ end)
+
+ describe('nvim_clear_autocmds', function()
+ it('should clear based on event + pattern', function()
+ command('autocmd InsertEnter *.py :echo "Python can be cool sometimes"')
+ command('autocmd InsertEnter *.txt :echo "Text Files Are Cool"')
+
+ local search = { event = "InsertEnter", pattern = "*.txt" }
+ local before_delete = meths.get_autocmds(search)
+ eq(1, #before_delete)
+
+ local before_delete_all = meths.get_autocmds { event = search.event }
+ eq(2, #before_delete_all)
+
+ meths.clear_autocmds(search)
+ local after_delete = meths.get_autocmds(search)
+ eq(0, #after_delete)
+
+ local after_delete_all = meths.get_autocmds { event = search.event }
+ eq(1, #after_delete_all)
+ end)
+
+ it('should clear based on event', function()
+ command('autocmd InsertEnter *.py :echo "Python can be cool sometimes"')
+ command('autocmd InsertEnter *.txt :echo "Text Files Are Cool"')
+
+ local search = { event = "InsertEnter"}
+ local before_delete = meths.get_autocmds(search)
+ eq(2, #before_delete)
+
+ meths.clear_autocmds(search)
+ local after_delete = meths.get_autocmds(search)
+ eq(0, #after_delete)
+ end)
+
+ it('should clear based on pattern', function()
+ command('autocmd InsertEnter *.TestPat1 :echo "Enter 1"')
+ command('autocmd InsertLeave *.TestPat1 :echo "Leave 1"')
+ command('autocmd InsertEnter *.TestPat2 :echo "Enter 2"')
+ command('autocmd InsertLeave *.TestPat2 :echo "Leave 2"')
+
+ local search = { pattern = "*.TestPat1"}
+ local before_delete = meths.get_autocmds(search)
+ eq(2, #before_delete)
+ local before_delete_events = meths.get_autocmds { event = { "InsertEnter", "InsertLeave" } }
+ eq(4, #before_delete_events)
+
+ meths.clear_autocmds(search)
+ local after_delete = meths.get_autocmds(search)
+ eq(0, #after_delete)
+
+ local after_delete_events = meths.get_autocmds { event = { "InsertEnter", "InsertLeave" } }
+ eq(2, #after_delete_events)
+ end)
+
+ it('should allow clearing by buffer', function()
+ command('autocmd! InsertEnter')
+ command('autocmd InsertEnter <buffer> :echo "Enter Buffer"')
+ command('autocmd InsertEnter *.TestPat1 :echo "Enter Pattern"')
+
+ local search = { event = "InsertEnter" }
+ local before_delete = meths.get_autocmds(search)
+ eq(2, #before_delete)
+
+ meths.clear_autocmds { buffer = 0 }
+ local after_delete = meths.get_autocmds(search)
+ eq(1, #after_delete)
+ eq("*.TestPat1", after_delete[1].pattern)
+ end)
+
+ it('should allow clearing by buffer and group', function()
+ command('augroup TestNvimClearAutocmds')
+ command(' au!')
+ command(' autocmd InsertEnter <buffer> :echo "Enter Buffer"')
+ command(' autocmd InsertEnter *.TestPat1 :echo "Enter Pattern"')
+ command('augroup END')
+
+ local search = { event = "InsertEnter", group = "TestNvimClearAutocmds" }
+ local before_delete = meths.get_autocmds(search)
+ eq(2, #before_delete)
+
+ -- Doesn't clear without passing group.
+ meths.clear_autocmds { buffer = 0 }
+ local without_group = meths.get_autocmds(search)
+ eq(2, #without_group)
+
+ -- Doest clear with passing group.
+ meths.clear_autocmds { buffer = 0, group = search.group }
+ local with_group = meths.get_autocmds(search)
+ eq(1, #with_group)
+ end)
+ end)
+end)
diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua
index 688f3abba5..dc668e7201 100644
--- a/test/functional/api/buffer_spec.lua
+++ b/test/functional/api/buffer_spec.lua
@@ -227,8 +227,8 @@ describe('api/buf', function()
it('can get a single line with strict indexing', function()
set_lines(0, 1, true, {'line1.a'})
eq(1, line_count()) -- sanity
- eq(false, pcall(get_lines, 1, 2, true))
- eq(false, pcall(get_lines, -3, -2, true))
+ eq('Index out of bounds', pcall_err(get_lines, 1, 2, true))
+ eq('Index out of bounds', pcall_err(get_lines, -3, -2, true))
end)
it('can get a single line with non-strict indexing', function()
@@ -240,11 +240,11 @@ describe('api/buf', function()
it('can set and delete a single line with strict indexing', function()
set_lines(0, 1, true, {'line1.a'})
- eq(false, pcall(set_lines, 1, 2, true, {'line1.b'}))
- eq(false, pcall(set_lines, -3, -2, true, {'line1.c'}))
+ eq('Index out of bounds', pcall_err(set_lines, 1, 2, true, {'line1.b'}))
+ eq('Index out of bounds', pcall_err(set_lines, -3, -2, true, {'line1.c'}))
eq({'line1.a'}, get_lines(0, -1, true))
- eq(false, pcall(set_lines, 1, 2, true, {}))
- eq(false, pcall(set_lines, -3, -2, true, {}))
+ eq('Index out of bounds', pcall_err(set_lines, 1, 2, true, {}))
+ eq('Index out of bounds', pcall_err(set_lines, -3, -2, true, {}))
eq({'line1.a'}, get_lines(0, -1, true))
end)
@@ -302,9 +302,9 @@ describe('api/buf', function()
set_lines(0, -1, true, {'a', 'b', 'c'})
eq({'a', 'b', 'c'}, get_lines(0, -1, true)) --sanity
- eq(false, pcall(get_lines, 3, 4, true))
- eq(false, pcall(get_lines, 3, 10, true))
- eq(false, pcall(get_lines, -5, -5, true))
+ eq('Index out of bounds', pcall_err(get_lines, 3, 4, true))
+ eq('Index out of bounds', pcall_err(get_lines, 3, 10, true))
+ eq('Index out of bounds', pcall_err(get_lines, -5, -5, true))
-- empty or inverted ranges are not errors
eq({}, get_lines(3, -1, true))
eq({}, get_lines(-3, -4, true))
@@ -316,10 +316,10 @@ describe('api/buf', function()
eq({'c'}, get_lines(-2, 5, false))
eq({'a', 'b', 'c'}, get_lines(0, 6, false))
- eq(false, pcall(set_lines, 4, 6, true, {'d'}))
+ eq('Index out of bounds', pcall_err(set_lines, 4, 6, true, {'d'}))
set_lines(4, 6, false, {'d'})
eq({'a', 'b', 'c', 'd'}, get_lines(0, -1, true))
- eq(false, pcall(set_lines, -6, -6, true, {'e'}))
+ eq('Index out of bounds', pcall_err(set_lines, -6, -6, true, {'e'}))
set_lines(-6, -6, false, {'e'})
eq({'e', 'a', 'b', 'c', 'd'}, get_lines(0, -1, true))
end)
@@ -392,7 +392,7 @@ describe('api/buf', function()
end)
end)
- describe('nvim_buf_get_lines, nvim_buf_set_text', function()
+ describe('nvim_buf_set_text', function()
local get_lines, set_text = curbufmeths.get_lines, curbufmeths.set_text
it('works', function()
@@ -423,6 +423,17 @@ describe('api/buf', function()
-- will join multiple lines if needed
set_text(0, 6, 3, 4, {'bar'})
eq({'hello bar'}, get_lines(0, 1, true))
+
+ -- can use negative line numbers
+ set_text(-2, 0, -2, 5, {'goodbye'})
+ eq({'goodbye bar', ''}, get_lines(0, -1, true))
+
+ set_text(-1, 0, -1, 0, {'text'})
+ eq({'goodbye bar', 'text'}, get_lines(0, 2, true))
+
+ -- can append to a line
+ set_text(1, 4, -1, 4, {' and', 'more'})
+ eq({'goodbye bar', 'text and', 'more'}, get_lines(0, 3, true))
end)
it('works with undo', function()
@@ -506,12 +517,12 @@ describe('api/buf', function()
eq({0, 6}, curbufmeths.get_extmark_by_id(ns, id2, {}))
eq({0, 6}, curbufmeths.get_extmark_by_id(ns, id3, {}))
- -- marks should be shifted over by the correct number of bytes for multibyte
- -- chars
- set_text(0, 0, 0, 0, {'Ø'})
- eq({0, 3}, curbufmeths.get_extmark_by_id(ns, id1, {}))
- eq({0, 8}, curbufmeths.get_extmark_by_id(ns, id2, {}))
- eq({0, 8}, curbufmeths.get_extmark_by_id(ns, id3, {}))
+ -- marks should be shifted over by the correct number of bytes for multibyte
+ -- chars
+ set_text(0, 0, 0, 0, {'Ø'})
+ eq({0, 3}, curbufmeths.get_extmark_by_id(ns, id1, {}))
+ eq({0, 8}, curbufmeths.get_extmark_by_id(ns, id2, {}))
+ eq({0, 8}, curbufmeths.get_extmark_by_id(ns, id3, {}))
end)
it("correctly marks changed region for redraw #13890", function()
@@ -533,7 +544,60 @@ describe('api/buf', function()
|
]])
+ end)
+
+ it('errors on out-of-range', function()
+ insert([[
+ hello foo!
+ text]])
+ eq('start_row out of bounds', pcall_err(set_text, 2, 0, 3, 0, {}))
+ eq('start_row out of bounds', pcall_err(set_text, -3, 0, 0, 0, {}))
+ eq('end_row out of bounds', pcall_err(set_text, 0, 0, 2, 0, {}))
+ eq('end_row out of bounds', pcall_err(set_text, 0, 0, -3, 0, {}))
+ eq('start_col out of bounds', pcall_err(set_text, 1, 5, 1, 5, {}))
+ eq('end_col out of bounds', pcall_err(set_text, 1, 0, 1, 5, {}))
+ end)
+ it('errors when start is greater than end', function()
+ insert([[
+ hello foo!
+ text]])
+ eq('start is higher than end', pcall_err(set_text, 1, 0, 0, 0, {}))
+ eq('start is higher than end', pcall_err(set_text, 0, 1, 0, 0, {}))
+ end)
+ end)
+
+ describe('nvim_buf_get_text', function()
+ local get_text = curbufmeths.get_text
+
+ before_each(function()
+ insert([[
+ hello foo!
+ text]])
+ end)
+
+ it('works', function()
+ eq({'hello'}, get_text(0, 0, 0, 5, {}))
+ eq({'hello foo!'}, get_text(0, 0, 0, 42, {}))
+ eq({'foo!'}, get_text(0, 6, 0, 10, {}))
+ eq({'foo!', 'tex'}, get_text(0, 6, 1, 3, {}))
+ eq({'foo!', 'tex'}, get_text(-2, 6, -1, 3, {}))
+ eq({''}, get_text(0, 18, 0, 20, {}))
+ eq({'ext'}, get_text(-1, 1, -1, 4, {}))
+ end)
+
+ it('errors on out-of-range', function()
+ eq('Index out of bounds', pcall_err(get_text, 2, 0, 3, 0, {}))
+ eq('Index out of bounds', pcall_err(get_text, -3, 0, 0, 0, {}))
+ eq('Index out of bounds', pcall_err(get_text, 0, 0, 2, 0, {}))
+ eq('Index out of bounds', pcall_err(get_text, 0, 0, -3, 0, {}))
+ -- no ml_get errors should happen #19017
+ eq('', meths.get_vvar('errmsg'))
+ end)
+
+ it('errors when start is greater than end', function()
+ eq('start is higher than end', pcall_err(get_text, 1, 0, 0, 0, {}))
+ eq('start_col must be less than end_col', pcall_err(get_text, 0, 1, 0, 0, {}))
end)
end)
diff --git a/test/functional/api/buffer_updates_spec.lua b/test/functional/api/buffer_updates_spec.lua
index c9c9be5406..3d257e9477 100644
--- a/test/functional/api/buffer_updates_spec.lua
+++ b/test/functional/api/buffer_updates_spec.lua
@@ -96,6 +96,8 @@ local function reopenwithfolds(b)
end
describe('API: buffer events:', function()
+ before_each(clear)
+
it('when lines are added', function()
local b, tick = editoriginal(true)
@@ -785,7 +787,8 @@ describe('API: buffer events:', function()
local function lines_subset(first, second)
for i = 1,#first do
- if first[i] ~= second[i] then
+ -- need to ignore trailing spaces
+ if first[i]:gsub(' +$', '') ~= second[i]:gsub(' +$', '') then
return false
end
end
diff --git a/test/functional/api/command_spec.lua b/test/functional/api/command_spec.lua
index 6c2c136edc..7eb7ee73f9 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', preview=false, 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='?', preview=false, range=NIL, register=false, keepscript=false, script_id=0, }
before_each(clear)
it('gets empty list if no commands were defined', function()
@@ -59,12 +59,13 @@ 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', preview=false, 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='+', preview=false, 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='*', preview=false, 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', preview=false, 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', preview=false, range=NIL, register=true, keepscript=false, script_id=4, }
source([[
+ let s:foo = 1
command -complete=custom,ListUsers -nargs=+ Finger !finger <args>
]])
eq({Finger=cmd1}, meths.get_commands({builtin=false}))
@@ -72,12 +73,18 @@ describe('nvim_get_commands', function()
eq({Finger=cmd1, TestCmd=cmd0}, meths.get_commands({builtin=false}))
source([[
+ function! s:foo() abort
+ endfunction
command -bang -nargs=* Cmd2 call <SID>foo(<q-args>)
]])
source([[
+ function! s:ohyeah() abort
+ endfunction
command -bar -nargs=0 Cmd3 call <SID>ohyeah()
]])
source([[
+ function! s:just_great() abort
+ endfunction
command -register Cmd4 call <SID>just_great()
]])
-- TODO(justinmk): Order is stable but undefined. Sort before return?
@@ -85,11 +92,11 @@ describe('nvim_get_commands', function()
end)
end)
-describe('nvim_add_user_command', function()
+describe('nvim_create_user_command', function()
before_each(clear)
it('works with strings', function()
- meths.add_user_command('SomeCommand', 'let g:command_fired = <args>', {nargs = 1})
+ meths.create_user_command('SomeCommand', 'let g:command_fired = <args>', {nargs = 1})
meths.command('SomeCommand 42')
eq(42, meths.eval('g:command_fired'))
end)
@@ -97,7 +104,7 @@ describe('nvim_add_user_command', function()
it('works with Lua functions', function()
exec_lua [[
result = {}
- vim.api.nvim_add_user_command('CommandWithLuaCallback', function(opts)
+ vim.api.nvim_create_user_command('CommandWithLuaCallback', function(opts)
result = opts
end, {
nargs = "*",
@@ -107,51 +114,267 @@ describe('nvim_add_user_command', function()
]]
eq({
- args = "hello",
+ args = [[this is a\ test]],
+ fargs = {"this", "is", "a test"},
bang = false,
line1 = 1,
line2 = 1,
mods = "",
+ smods = {
+ browse = false,
+ confirm = false,
+ emsg_silent = false,
+ hide = false,
+ keepalt = false,
+ keepjumps = false,
+ keepmarks = false,
+ keeppatterns = false,
+ lockmarks = false,
+ noautocmd = false,
+ noswapfile = false,
+ sandbox = false,
+ silent = false,
+ split = "",
+ tab = 0,
+ unsilent = false,
+ verbose = -1,
+ vertical = false,
+ },
range = 0,
count = 2,
reg = "",
- }, exec_lua [[
- vim.api.nvim_command('CommandWithLuaCallback hello')
+ }, exec_lua [=[
+ vim.api.nvim_command([[CommandWithLuaCallback this is a\ test]])
return result
- ]])
+ ]=])
eq({
- args = "",
+ args = [[this includes\ a backslash: \\]],
+ fargs = {"this", "includes a", "backslash:", "\\"},
+ bang = false,
+ line1 = 1,
+ line2 = 1,
+ mods = "",
+ smods = {
+ browse = false,
+ confirm = false,
+ emsg_silent = false,
+ hide = false,
+ keepalt = false,
+ keepjumps = false,
+ keepmarks = false,
+ keeppatterns = false,
+ lockmarks = false,
+ noautocmd = false,
+ noswapfile = false,
+ sandbox = false,
+ silent = false,
+ split = "",
+ tab = 0,
+ unsilent = false,
+ verbose = -1,
+ vertical = false,
+ },
+ range = 0,
+ count = 2,
+ reg = "",
+ }, exec_lua [=[
+ vim.api.nvim_command([[CommandWithLuaCallback this includes\ a backslash: \\]])
+ return result
+ ]=])
+
+ eq({
+ args = "a\\b",
+ fargs = {"a\\b"},
+ bang = false,
+ line1 = 1,
+ line2 = 1,
+ mods = "",
+ smods = {
+ browse = false,
+ confirm = false,
+ emsg_silent = false,
+ hide = false,
+ keepalt = false,
+ keepjumps = false,
+ keepmarks = false,
+ keeppatterns = false,
+ lockmarks = false,
+ noautocmd = false,
+ noswapfile = false,
+ sandbox = false,
+ silent = false,
+ split = "",
+ tab = 0,
+ unsilent = false,
+ verbose = -1,
+ vertical = false,
+ },
+ range = 0,
+ count = 2,
+ reg = "",
+ }, exec_lua [=[
+ vim.api.nvim_command('CommandWithLuaCallback a\\b')
+ return result
+ ]=])
+
+ eq({
+ args = 'h\tey ',
+ fargs = {[[h]], [[ey]]},
bang = true,
line1 = 10,
line2 = 10,
- mods = "botright",
+ mods = "confirm unsilent botright",
+ smods = {
+ browse = false,
+ confirm = true,
+ emsg_silent = false,
+ hide = false,
+ keepalt = false,
+ keepjumps = false,
+ keepmarks = false,
+ keeppatterns = false,
+ lockmarks = false,
+ noautocmd = false,
+ noswapfile = false,
+ sandbox = false,
+ silent = false,
+ split = "botright",
+ tab = 0,
+ unsilent = true,
+ verbose = -1,
+ vertical = false,
+ },
range = 1,
count = 10,
reg = "",
- }, exec_lua [[
- vim.api.nvim_command('botright 10CommandWithLuaCallback!')
+ }, exec_lua [=[
+ vim.api.nvim_command('unsilent botright confirm 10CommandWithLuaCallback! h\tey ')
return result
- ]])
+ ]=])
eq({
- args = "",
+ args = "h",
+ fargs = {"h"},
bang = false,
line1 = 1,
line2 = 42,
mods = "",
+ smods = {
+ browse = false,
+ confirm = false,
+ emsg_silent = false,
+ hide = false,
+ keepalt = false,
+ keepjumps = false,
+ keepmarks = false,
+ keeppatterns = false,
+ lockmarks = false,
+ noautocmd = false,
+ noswapfile = false,
+ sandbox = false,
+ silent = false,
+ split = "",
+ tab = 0,
+ unsilent = false,
+ verbose = -1,
+ vertical = false,
+ },
range = 1,
count = 42,
reg = "",
}, exec_lua [[
- vim.api.nvim_command('CommandWithLuaCallback 42')
+ vim.api.nvim_command('CommandWithLuaCallback 42 h')
return result
]])
+
+ eq({
+ args = "",
+ fargs = {}, -- fargs works without args
+ bang = false,
+ line1 = 1,
+ line2 = 1,
+ mods = "",
+ smods = {
+ browse = false,
+ confirm = false,
+ emsg_silent = false,
+ hide = false,
+ keepalt = false,
+ keepjumps = false,
+ keepmarks = false,
+ keeppatterns = false,
+ lockmarks = false,
+ noautocmd = false,
+ noswapfile = false,
+ sandbox = false,
+ silent = false,
+ split = "",
+ tab = 0,
+ unsilent = false,
+ verbose = -1,
+ vertical = false,
+ },
+ range = 0,
+ count = 2,
+ reg = "",
+ }, exec_lua [[
+ vim.api.nvim_command('CommandWithLuaCallback')
+ return result
+ ]])
+
+ -- f-args doesn't split when command nargs is 1 or "?"
+ exec_lua [[
+ result = {}
+ vim.api.nvim_create_user_command('CommandWithOneArg', function(opts)
+ result = opts
+ end, {
+ nargs = "?",
+ bang = true,
+ count = 2,
+ })
+ ]]
+
+ eq({
+ args = "hello I'm one argument",
+ fargs = {"hello I'm one argument"}, -- Doesn't split args
+ bang = false,
+ line1 = 1,
+ line2 = 1,
+ mods = "",
+ smods = {
+ browse = false,
+ confirm = false,
+ emsg_silent = false,
+ hide = false,
+ keepalt = false,
+ keepjumps = false,
+ keepmarks = false,
+ keeppatterns = false,
+ lockmarks = false,
+ noautocmd = false,
+ noswapfile = false,
+ sandbox = false,
+ silent = false,
+ split = "",
+ tab = 0,
+ unsilent = false,
+ verbose = -1,
+ vertical = false,
+ },
+ range = 0,
+ count = 2,
+ reg = "",
+ }, exec_lua [[
+ vim.api.nvim_command('CommandWithOneArg hello I\'m one argument')
+ return result
+ ]])
+
end)
it('can define buffer-local commands', function()
local bufnr = meths.create_buf(false, false)
- bufmeths.add_user_command(bufnr, "Hello", "", {})
+ bufmeths.create_user_command(bufnr, "Hello", "", {})
matches("Not an editor command: Hello", pcall_err(meths.command, "Hello"))
meths.set_current_buf(bufnr)
meths.command("Hello")
@@ -160,7 +383,7 @@ describe('nvim_add_user_command', function()
it('can use a Lua complete function', function()
exec_lua [[
- vim.api.nvim_add_user_command('Test', '', {
+ vim.api.nvim_create_user_command('Test', '', {
nargs = "*",
complete = function(arg, cmdline, pos)
local options = {"aaa", "bbb", "ccc"}
@@ -180,20 +403,52 @@ describe('nvim_add_user_command', function()
feed('<C-U>Test b<Tab>')
eq('Test bbb', funcs.getcmdline())
end)
+
+ it('does not allow invalid command names', function()
+ matches("'name' must begin with an uppercase letter", pcall_err(exec_lua, [[
+ vim.api.nvim_create_user_command('test', 'echo "hi"', {})
+ ]]))
+
+ matches('Invalid command name', pcall_err(exec_lua, [[
+ vim.api.nvim_create_user_command('t@', 'echo "hi"', {})
+ ]]))
+
+ matches('Invalid command name', pcall_err(exec_lua, [[
+ vim.api.nvim_create_user_command('T@st', 'echo "hi"', {})
+ ]]))
+
+ matches('Invalid command name', pcall_err(exec_lua, [[
+ vim.api.nvim_create_user_command('Test!', 'echo "hi"', {})
+ ]]))
+
+ matches('Invalid command name', pcall_err(exec_lua, [[
+ vim.api.nvim_create_user_command('💩', 'echo "hi"', {})
+ ]]))
+ end)
+
+ it('smods can be used with nvim_cmd', function()
+ exec_lua[[
+ vim.api.nvim_create_user_command('MyEcho', function(opts)
+ vim.api.nvim_cmd({ cmd = 'echo', args = { '&verbose' }, mods = opts.smods }, {})
+ end, {})
+ ]]
+
+ eq("3", meths.cmd({ cmd = 'MyEcho', mods = { verbose = 3 } }, { output = true }))
+ end)
end)
describe('nvim_del_user_command', function()
before_each(clear)
it('can delete global commands', function()
- meths.add_user_command('Hello', 'echo "Hi"', {})
+ meths.create_user_command('Hello', 'echo "Hi"', {})
meths.command('Hello')
meths.del_user_command('Hello')
matches("Not an editor command: Hello", pcall_err(meths.command, "Hello"))
end)
it('can delete buffer-local commands', function()
- bufmeths.add_user_command(0, 'Hello', 'echo "Hi"', {})
+ bufmeths.create_user_command(0, 'Hello', 'echo "Hi"', {})
meths.command('Hello')
bufmeths.del_user_command(0, 'Hello')
matches("Not an editor command: Hello", pcall_err(meths.command, "Hello"))
diff --git a/test/functional/api/extmark_spec.lua b/test/functional/api/extmark_spec.lua
index a8f538b951..bc8d811c6d 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)
@@ -908,7 +924,7 @@ describe('API/extmarks', function()
eq(3, set_extmark(ns, 3, positions[2][1], positions[2][2]))
eq(4, set_extmark(ns, 0, positions[1][1], positions[1][2]))
- -- mixing manual and allocated id:s are not recommened, but it should
+ -- mixing manual and allocated id:s are not recommended, but it should
-- do something reasonable
eq(6, set_extmark(ns, 6, positions[2][1], positions[2][2]))
eq(7, set_extmark(ns, 0, positions[1][1], positions[1][2]))
@@ -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)
@@ -1537,3 +1606,209 @@ describe('Extmarks buffer api with many marks', function()
eq({}, get_marks(ns2))
end)
end)
+
+describe('API/win_extmark', function()
+ local screen
+ local marks, line1, line2
+ local ns
+
+ before_each(function()
+ -- Initialize some namespaces and insert text into a buffer
+ marks = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
+
+ line1 = "non ui-watched line"
+ line2 = "ui-watched line"
+
+ clear()
+
+ insert(line1)
+ feed("o<esc>")
+ insert(line2)
+ ns = request('nvim_create_namespace', "extmark-ui")
+ end)
+
+ it('sends and only sends ui-watched marks to ui', function()
+ screen = Screen.new(20, 4)
+ screen:attach()
+ -- should send this
+ set_extmark(ns, marks[1], 1, 0, { ui_watched = true })
+ -- should not send this
+ set_extmark(ns, marks[2], 0, 0, { ui_watched = false })
+ screen:expect({
+ grid = [[
+ non ui-watched line |
+ ui-watched lin^e |
+ ~ |
+ |
+ ]],
+ extmarks = {
+ [2] = {
+ -- positioned at the end of the 2nd line
+ { {id = 1000}, 1, 1, 1, 16 },
+ }
+ },
+ })
+ end)
+
+ it('sends multiple ui-watched marks to ui', function()
+ screen = Screen.new(20, 4)
+ screen:attach()
+ -- should send all of these
+ set_extmark(ns, marks[1], 1, 0, { ui_watched = true, virt_text_pos = "overlay" })
+ set_extmark(ns, marks[2], 1, 2, { ui_watched = true, virt_text_pos = "overlay" })
+ set_extmark(ns, marks[3], 1, 4, { ui_watched = true, virt_text_pos = "overlay" })
+ set_extmark(ns, marks[4], 1, 6, { ui_watched = true, virt_text_pos = "overlay" })
+ set_extmark(ns, marks[5], 1, 8, { ui_watched = true })
+ screen:expect({
+ grid = [[
+ non ui-watched line |
+ ui-watched lin^e |
+ ~ |
+ |
+ ]],
+ extmarks = {
+ [2] = {
+ -- earlier notifications
+ { {id = 1000}, 1, 1, 1, 0 },
+ { {id = 1000}, 1, 1, 1, 0 }, { {id = 1000}, 1, 2, 1, 2 },
+ { {id = 1000}, 1, 1, 1, 0 }, { {id = 1000}, 1, 2, 1, 2 }, { {id = 1000}, 1, 3, 1, 4 },
+ { {id = 1000}, 1, 1, 1, 0 }, { {id = 1000}, 1, 2, 1, 2 }, { {id = 1000}, 1, 3, 1, 4 }, { {id = 1000}, 1, 4, 1, 6 },
+ -- final
+ -- overlay
+ { {id = 1000}, 1, 1, 1, 0 },
+ { {id = 1000}, 1, 2, 1, 2 },
+ { {id = 1000}, 1, 3, 1, 4 },
+ { {id = 1000}, 1, 4, 1, 6 },
+ -- eol
+ { {id = 1000}, 1, 5, 1, 16 },
+ }
+ },
+ })
+ end)
+
+ it('updates ui-watched marks', function()
+ screen = Screen.new(20, 4)
+ screen:attach()
+ -- should send this
+ set_extmark(ns, marks[1], 1, 0, { ui_watched = true })
+ -- should not send this
+ set_extmark(ns, marks[2], 0, 0, { ui_watched = false })
+ -- make some changes
+ insert(" update")
+ screen:expect({
+ grid = [[
+ non ui-watched line |
+ ui-watched linupdat^e|
+ e |
+ |
+ ]],
+ extmarks = {
+ [2] = {
+ -- positioned at the end of the 2nd line
+ { {id = 1000}, 1, 1, 1, 16 },
+ -- updated and wrapped to 3rd line
+ { {id = 1000}, 1, 1, 2, 2 },
+ }
+ }
+ })
+ feed("<c-e>")
+ screen:expect({
+ grid = [[
+ ui-watched linupdat^e|
+ e |
+ ~ |
+ |
+ ]],
+ extmarks = {
+ [2] = {
+ -- positioned at the end of the 2nd line
+ { {id = 1000}, 1, 1, 1, 16 },
+ -- updated and wrapped to 3rd line
+ { {id = 1000}, 1, 1, 2, 2 },
+ -- scrolled up one line, should be handled by grid scroll
+ }
+ }
+ })
+ end)
+
+ it('sends ui-watched to splits', function()
+ screen = Screen.new(20, 8)
+ screen:attach({ext_multigrid=true})
+ -- should send this
+ set_extmark(ns, marks[1], 1, 0, { ui_watched = true })
+ -- should not send this
+ set_extmark(ns, marks[2], 0, 0, { ui_watched = false })
+ command('split')
+ screen:expect({
+ grid = [[
+ ## grid 1
+ [4:--------------------]|
+ [4:--------------------]|
+ [4:--------------------]|
+ [No Name] [+] |
+ [2:--------------------]|
+ [2:--------------------]|
+ [No Name] [+] |
+ [3:--------------------]|
+ ## grid 2
+ non ui-watched line |
+ ui-watched line |
+ ## grid 3
+ |
+ ## grid 4
+ non ui-watched line |
+ ui-watched lin^e |
+ ~ |
+ ]],
+ extmarks = {
+ [2] = {
+ -- positioned at the end of the 2nd line
+ { {id = 1000}, 1, 1, 1, 16 },
+ -- updated after split
+ { {id = 1000}, 1, 1, 1, 16 },
+ },
+ [4] = {
+ -- only after split
+ { {id = 1001}, 1, 1, 1, 16 },
+ }
+ }
+ })
+ -- make some changes
+ insert(" update")
+ screen:expect({
+ grid = [[
+ ## grid 1
+ [4:--------------------]|
+ [4:--------------------]|
+ [4:--------------------]|
+ [No Name] [+] |
+ [2:--------------------]|
+ [2:--------------------]|
+ [No Name] [+] |
+ [3:--------------------]|
+ ## grid 2
+ non ui-watched line |
+ ui-watched linupd@@@|
+ ## grid 3
+ |
+ ## grid 4
+ non ui-watched line |
+ ui-watched linupdat^e|
+ e |
+ ]],
+ extmarks = {
+ [2] = {
+ -- positioned at the end of the 2nd line
+ { {id = 1000}, 1, 1, 1, 16 },
+ -- updated after split
+ { {id = 1000}, 1, 1, 1, 16 },
+ },
+ [4] = {
+ { {id = 1001}, 1, 1, 1, 16 },
+ -- updated
+ { {id = 1001}, 1, 1, 2, 2 },
+ }
+ }
+ })
+ end)
+end)
diff --git a/test/functional/api/highlight_spec.lua b/test/functional/api/highlight_spec.lua
index 21e3094f8e..933103046c 100644
--- a/test/functional/api/highlight_spec.lua
+++ b/test/functional/api/highlight_spec.lua
@@ -3,6 +3,7 @@ local clear, nvim = helpers.clear, helpers.nvim
local Screen = require('test.functional.ui.screen')
local eq, eval = helpers.eq, helpers.eval
local command = helpers.command
+local exec_capture = helpers.exec_capture
local meths = helpers.meths
local funcs = helpers.funcs
local pcall_err = helpers.pcall_err
@@ -27,8 +28,11 @@ describe('API: highlight',function()
bold = true,
italic = true,
reverse = true,
- undercurl = true,
underline = true,
+ undercurl = true,
+ underdouble = true,
+ underdotted = true,
+ underdashed = true,
strikethrough = true,
}
@@ -51,7 +55,7 @@ describe('API: highlight',function()
eq('Invalid highlight id: 30000', string.match(emsg, 'Invalid.*'))
-- Test all highlight properties.
- command('hi NewHighlight gui=underline,bold,undercurl,italic,reverse,strikethrough')
+ command('hi NewHighlight gui=underline,bold,undercurl,underdouble,underdotted,underdashed,italic,reverse,strikethrough')
eq(expected_rgb2, nvim("get_hl_by_id", hl_id, true))
-- Test nil argument.
@@ -129,6 +133,13 @@ describe('API: highlight',function()
eq({ underline = true, standout = true, },
meths.get_hl_by_name('cursorline', 0));
+ -- Test cterm & Normal values. #18024 (tail) & #18980
+ -- Ensure Normal, and groups that match Normal return their fg & bg cterm values
+ meths.set_hl(0, 'Normal', {ctermfg = 17, ctermbg = 213})
+ meths.set_hl(0, 'NotNormal', {ctermfg = 17, ctermbg = 213})
+ -- Note colors are "cterm" values, not rgb-as-ints
+ eq({foreground = 17, background = 213}, nvim("get_hl_by_name", 'Normal', false))
+ eq({foreground = 17, background = 213}, nvim("get_hl_by_name", 'NotNormal', false))
end)
it('nvim_get_hl_id_by_name', function()
@@ -194,10 +205,15 @@ describe("API: set highlight", function()
reverse = true,
undercurl = true,
underline = true,
+ underdashed = true,
+ underdotted = true,
+ underdouble = true,
+ strikethrough = true,
cterm = {
italic = true,
reverse = true,
undercurl = true,
+ strikethrough = true,
}
}
local highlight3_result_gui = {
@@ -208,6 +224,10 @@ describe("API: set highlight", function()
reverse = true,
undercurl = true,
underline = true,
+ underdashed = true,
+ underdotted = true,
+ underdouble = true,
+ strikethrough = true,
}
local highlight3_result_cterm = {
background = highlight_color.ctermbg,
@@ -215,6 +235,7 @@ describe("API: set highlight", function()
italic = true,
reverse = true,
undercurl = true,
+ strikethrough = true,
}
local function get_ns()
@@ -237,6 +258,12 @@ describe("API: set highlight", function()
eq(highlight2_result, meths.get_hl_by_name('Test_hl', false))
end)
+ it ("can set empty cterm attr", function()
+ local ns = get_ns()
+ meths.set_hl(ns, 'Test_hl', { cterm = {} })
+ eq({}, meths.get_hl_by_name('Test_hl', false))
+ end)
+
it ("cterm attr defaults to gui attr", function()
local ns = get_ns()
meths.set_hl(ns, 'Test_hl', highlight1)
@@ -252,4 +279,76 @@ describe("API: set highlight", function()
eq(highlight3_result_gui, meths.get_hl_by_name('Test_hl', true))
eq(highlight3_result_cterm, meths.get_hl_by_name('Test_hl', false))
end)
+
+ it ("can set a highlight in the global namespace", function()
+ meths.set_hl(0, 'Test_hl', highlight2_config)
+ eq('Test_hl xxx cterm=underline,reverse ctermfg=8 ctermbg=15 gui=underline,reverse',
+ exec_capture('highlight Test_hl'))
+
+ meths.set_hl(0, 'Test_hl', { background = highlight_color.bg })
+ eq('Test_hl xxx guibg=#0032aa',
+ exec_capture('highlight Test_hl'))
+
+ meths.set_hl(0, 'Test_hl2', highlight3_config)
+ eq('Test_hl2 xxx cterm=undercurl,italic,reverse,strikethrough ctermfg=8 ctermbg=15 gui=bold,underline,undercurl,underdouble,underdotted,underdashed,italic,reverse,strikethrough guifg=#ff0000 guibg=#0032aa',
+ exec_capture('highlight Test_hl2'))
+
+ -- Colors are stored with the name they are defined, but
+ -- with canonical casing
+ meths.set_hl(0, 'Test_hl3', { bg = 'reD', fg = 'bLue'})
+ eq('Test_hl3 xxx guifg=Blue guibg=Red',
+ exec_capture('highlight Test_hl3'))
+ end)
+
+ it ("can modify a highlight in the global namespace", function()
+ meths.set_hl(0, 'Test_hl3', { bg = 'red', fg = 'blue'})
+ eq('Test_hl3 xxx guifg=Blue guibg=Red',
+ exec_capture('highlight Test_hl3'))
+
+ meths.set_hl(0, 'Test_hl3', { bg = 'red' })
+ eq('Test_hl3 xxx guibg=Red',
+ exec_capture('highlight Test_hl3'))
+
+ meths.set_hl(0, 'Test_hl3', { ctermbg = 9, ctermfg = 12})
+ eq('Test_hl3 xxx ctermfg=12 ctermbg=9',
+ exec_capture('highlight Test_hl3'))
+
+ meths.set_hl(0, 'Test_hl3', { ctermbg = 'red' , ctermfg = 'blue'})
+ eq('Test_hl3 xxx ctermfg=12 ctermbg=9',
+ exec_capture('highlight Test_hl3'))
+
+ meths.set_hl(0, 'Test_hl3', { ctermbg = 9 })
+ eq('Test_hl3 xxx ctermbg=9',
+ exec_capture('highlight Test_hl3'))
+
+ eq("'redd' is not a valid color",
+ pcall_err(meths.set_hl, 0, 'Test_hl3', {fg='redd'}))
+
+ eq("'bleu' is not a valid color",
+ pcall_err(meths.set_hl, 0, 'Test_hl3', {ctermfg='bleu'}))
+
+ meths.set_hl(0, 'Test_hl3', {fg='#FF00FF'})
+ eq('Test_hl3 xxx guifg=#ff00ff',
+ exec_capture('highlight Test_hl3'))
+
+ eq("'#FF00FF' is not a valid color",
+ pcall_err(meths.set_hl, 0, 'Test_hl3', {ctermfg='#FF00FF'}))
+
+ for _, fg_val in ipairs{ nil, 'NONE', 'nOnE', '', -1 } do
+ meths.set_hl(0, 'Test_hl3', {fg = fg_val})
+ eq('Test_hl3 xxx cleared',
+ exec_capture('highlight Test_hl3'))
+ end
+
+ meths.set_hl(0, 'Test_hl3', {fg='#FF00FF', blend=50})
+ eq('Test_hl3 xxx guifg=#ff00ff blend=50',
+ exec_capture('highlight Test_hl3'))
+
+ end)
+
+ it ("correctly sets 'Normal' internal properties", function()
+ -- Normal has some special handling internally. #18024
+ meths.set_hl(0, 'Normal', {fg='#000083', bg='#0000F3'})
+ eq({foreground = 131, background = 243}, nvim("get_hl_by_name", 'Normal', true))
+ end)
end)
diff --git a/test/functional/api/keymap_spec.lua b/test/functional/api/keymap_spec.lua
index 450a76ddac..6bc6651e04 100644
--- a/test/functional/api/keymap_spec.lua
+++ b/test/functional/api/keymap_spec.lua
@@ -15,6 +15,9 @@ local pcall_err = helpers.pcall_err
local shallowcopy = helpers.shallowcopy
local sleep = helpers.sleep
+local sid_api_client = -9
+local sid_lua = -8
+
describe('nvim_get_keymap', function()
before_each(clear)
@@ -318,7 +321,7 @@ describe('nvim_get_keymap', function()
eq({space_table}, meths.get_keymap('n'))
end)
- it('can handle lua keymaps', function()
+ it('can handle lua mappings', function()
eq(0, exec_lua [[
GlobalCount = 0
vim.api.nvim_set_keymap ('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end })
@@ -333,14 +336,14 @@ describe('nvim_get_keymap', function()
return GlobalCount
]])
local mapargs = meths.get_keymap('n')
- assert.Truthy(type(mapargs[1].callback) == 'number', 'callback is not luaref number')
+ assert(type(mapargs[1].callback) == 'number', 'callback is not luaref number')
mapargs[1].callback = nil
eq({
lhs='asdf',
script=0,
silent=0,
expr=0,
- sid=0,
+ sid=sid_lua,
buffer=0,
nowait=0,
mode='n',
@@ -357,7 +360,7 @@ describe('nvim_get_keymap', function()
script=0,
silent=0,
expr=0,
- sid=0,
+ sid=sid_api_client,
buffer=0,
nowait=0,
mode='n',
@@ -400,7 +403,7 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
to_return.silent = not opts.silent and 0 or 1
to_return.nowait = not opts.nowait and 0 or 1
to_return.expr = not opts.expr and 0 or 1
- to_return.sid = not opts.sid and 0 or opts.sid
+ to_return.sid = not opts.sid and sid_api_client or opts.sid
to_return.buffer = not opts.buffer and 0 or opts.buffer
to_return.lnum = not opts.lnum and 0 or opts.lnum
to_return.desc = opts.desc
@@ -579,7 +582,7 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
it('can set mappings containing literal keycodes', function()
meths.set_keymap('n', '\n\r\n', 'rhs', {})
local expected = generate_mapargs('n', '<NL><CR><NL>', 'rhs')
- eq(expected, get_mapargs('n', '<C-j><CR><C-j>'))
+ eq(expected, get_mapargs('n', '<NL><CR><NL>'))
end)
it('can set mappings whose RHS is a <Nop>', function()
@@ -603,6 +606,13 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
eq({''}, curbufmeths.get_lines(0, -1, 0))
eq(generate_mapargs('i', 'lhs', '<NOP>', {}),
get_mapargs('i', 'lhs'))
+
+ -- a single ^V in RHS is also <Nop> (see :h map-empty-rhs)
+ meths.set_keymap('i', 'lhs', '\022', {})
+ command('normal ilhs')
+ eq({''}, curbufmeths.get_lines(0, -1, 0))
+ eq(generate_mapargs('i', 'lhs', '\022', {}),
+ get_mapargs('i', 'lhs'))
end)
it('treats an empty RHS in a mapping like a <Nop>', function()
@@ -625,7 +635,7 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
it('interprets control sequences in expr-quotes correctly when called '
..'inside vim', function()
command([[call nvim_set_keymap('i', "\<space>", "\<tab>", {})]])
- eq(generate_mapargs('i', '<Space>', '\t', {}),
+ eq(generate_mapargs('i', '<Space>', '\t', {sid=0}),
get_mapargs('i', '<Space>'))
feed('i ')
eq({'\t'}, curbufmeths.get_lines(0, -1, 0))
@@ -782,7 +792,7 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
end)
- it (':map command shows lua keymap correctly', function()
+ it (':map command shows lua mapping correctly', function()
exec_lua [[
vim.api.nvim_set_keymap ('n', 'asdf', '', {callback = function() print('jkl;') end })
]]
@@ -790,7 +800,7 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
"^\nn asdf <Lua function %d+>"))
end)
- it ('mapcheck() returns lua keymap correctly', function()
+ it ('mapcheck() returns lua mapping correctly', function()
exec_lua [[
vim.api.nvim_set_keymap ('n', 'asdf', '', {callback = function() print('jkl;') end })
]]
@@ -798,16 +808,16 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
"^<Lua function %d+>"))
end)
- it ('maparg() returns lua keymap correctly', function()
+ it ('maparg() returns lua mapping correctly', function()
exec_lua [[
vim.api.nvim_set_keymap ('n', 'asdf', '', {callback = function() print('jkl;') end })
]]
assert.truthy(string.match(funcs.maparg('asdf', 'n'),
"^<Lua function %d+>"))
local mapargs = funcs.maparg('asdf', 'n', false, true)
- assert.Truthy(type(mapargs.callback) == 'number', 'callback is not luaref number')
+ assert(type(mapargs.callback) == 'number', 'callback is not luaref number')
mapargs.callback = nil
- eq(generate_mapargs('n', 'asdf', nil, {}), mapargs)
+ eq(generate_mapargs('n', 'asdf', nil, {sid=sid_lua}), mapargs)
end)
it('can make lua expr mappings', function()
@@ -871,7 +881,28 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
eq('\nNo mapping found', helpers.exec_capture('nmap asdf'))
end)
- it('can set descriptions on keymaps', function()
+ it('no double-free when unmapping simplifiable lua mappings', function()
+ eq(0, exec_lua [[
+ GlobalCount = 0
+ vim.api.nvim_set_keymap('n', '<C-I>', '', {callback = function() GlobalCount = GlobalCount + 1 end })
+ return GlobalCount
+ ]])
+
+ feed('<C-I>\n')
+
+ eq(1, exec_lua[[return GlobalCount]])
+
+ exec_lua [[
+ vim.api.nvim_del_keymap('n', '<C-I>')
+ ]]
+
+ feed('<C-I>\n')
+
+ eq(1, exec_lua[[return GlobalCount]])
+ eq('\nNo mapping found', helpers.exec_capture('nmap <C-I>'))
+ end)
+
+ it('can set descriptions on mappings', function()
meths.set_keymap('n', 'lhs', 'rhs', {desc="map description"})
eq(generate_mapargs('n', 'lhs', 'rhs', {desc="map description"}), get_mapargs('n', 'lhs'))
eq("\nn lhs rhs\n map description",
@@ -1037,4 +1068,25 @@ describe('nvim_buf_set_keymap, nvim_buf_del_keymap', function()
eq(1, exec_lua[[return GlobalCount]])
eq('\nNo mapping found', helpers.exec_capture('nmap asdf'))
end)
+
+ it('no double-free when unmapping simplifiable lua mappings', function()
+ eq(0, exec_lua [[
+ GlobalCount = 0
+ vim.api.nvim_buf_set_keymap(0, 'n', '<C-I>', '', {callback = function() GlobalCount = GlobalCount + 1 end })
+ return GlobalCount
+ ]])
+
+ feed('<C-I>\n')
+
+ eq(1, exec_lua[[return GlobalCount]])
+
+ exec_lua [[
+ vim.api.nvim_buf_del_keymap(0, 'n', '<C-I>')
+ ]]
+
+ feed('<C-I>\n')
+
+ eq(1, exec_lua[[return GlobalCount]])
+ eq('\nNo mapping found', helpers.exec_capture('nmap <C-I>'))
+ end)
end)
diff --git a/test/functional/api/proc_spec.lua b/test/functional/api/proc_spec.lua
index d828bdf948..0fbf58a8e7 100644
--- a/test/functional/api/proc_spec.lua
+++ b/test/functional/api/proc_spec.lua
@@ -63,9 +63,9 @@ describe('API', function()
local pid = funcs.getpid()
local pinfo = request('nvim_get_proc', pid)
eq((iswin() and 'nvim.exe' or 'nvim'), pinfo.name)
- eq(pinfo.pid, pid)
- eq(type(pinfo.ppid), 'number')
- neq(pinfo.ppid, pid)
+ eq(pid, pinfo.pid)
+ eq('number', type(pinfo.ppid))
+ neq(pid, pinfo.ppid)
end)
it('validates input', function()
diff --git a/test/functional/api/server_requests_spec.lua b/test/functional/api/server_requests_spec.lua
index 309d9084c8..00a4dd041d 100644
--- a/test/functional/api/server_requests_spec.lua
+++ b/test/functional/api/server_requests_spec.lua
@@ -170,7 +170,7 @@ describe('server -> client', function()
if method == "notification" then
eq('done!', eval('rpcrequest('..cid..', "nested")'))
elseif method == "nested_done" then
- ok(false, 'this should never have been sent')
+ ok(false, 'never sent', 'sent')
end
end
@@ -181,12 +181,6 @@ describe('server -> client', function()
end)
describe('recursive (child) nvim client', function()
- if helpers.isCI('travis') and helpers.is_os('mac') then
- -- XXX: Hangs Travis macOS since e9061117a5b8f195c3f26a5cb94e18ddd7752d86.
- pending("[Hangs on Travis macOS. #5002]", function() end)
- return
- end
-
before_each(function()
command("let vim = rpcstart('"..nvim_prog.."', ['-u', 'NONE', '-i', 'NONE', '--cmd', 'set noswapfile', '--embed', '--headless'])")
neq(0, eval('vim'))
diff --git a/test/functional/api/version_spec.lua b/test/functional/api/version_spec.lua
index bf67d4788b..771192e9ab 100644
--- a/test/functional/api/version_spec.lua
+++ b/test/functional/api/version_spec.lua
@@ -33,9 +33,11 @@ describe("api_info()['version']", function()
local major = version['major']
local minor = version['minor']
local patch = version['patch']
+ local prerelease = version['prerelease']
eq("number", type(major))
eq("number", type(minor))
eq("number", type(patch))
+ eq("boolean", type(prerelease))
eq(1, funcs.has("nvim-"..major.."."..minor.."."..patch))
eq(0, funcs.has("nvim-"..major.."."..minor.."."..(patch + 1)))
eq(0, funcs.has("nvim-"..major.."."..(minor + 1).."."..patch))
@@ -91,6 +93,7 @@ describe("api metadata", function()
local api, compat, stable, api_level
local old_api = {}
setup(function()
+ clear() -- Ensure a session before requesting api_info.
api = meths.get_api_info()[2]
compat = api.version.api_compatible
api_level = api.version.api_level
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua
index 22201e21a2..3724dbf820 100644
--- a/test/functional/api/vim_spec.lua
+++ b/test/functional/api/vim_spec.lua
@@ -1,11 +1,13 @@
local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
+local lfs = require('lfs')
local fmt = string.format
local assert_alive = helpers.assert_alive
local NIL = helpers.NIL
local clear, nvim, eq, neq = helpers.clear, helpers.nvim, helpers.eq, helpers.neq
local command = helpers.command
+local exec = helpers.exec
local eval = helpers.eval
local expect = helpers.expect
local funcs = helpers.funcs
@@ -23,6 +25,9 @@ local next_msg = helpers.next_msg
local tmpname = helpers.tmpname
local write_file = helpers.write_file
local exec_lua = helpers.exec_lua
+local exc_exec = helpers.exc_exec
+local insert = helpers.insert
+local expect_exit = helpers.expect_exit
local pcall_err = helpers.pcall_err
local format_string = helpers.format_string
@@ -120,9 +125,9 @@ describe('API', function()
-- Functions
nvim('exec', 'function Foo()\ncall setline(1,["xxx"])\nendfunction', false)
- eq(nvim('get_current_line'), '')
+ eq('', nvim('get_current_line'))
nvim('exec', 'call Foo()', false)
- eq(nvim('get_current_line'), 'xxx')
+ eq('xxx', nvim('get_current_line'))
-- Autocmds
nvim('exec','autocmd BufAdd * :let x1 = "Hello"', false)
@@ -333,6 +338,7 @@ describe('API', function()
describe('nvim_command_output', function()
it('does not induce hit-enter prompt', function()
+ nvim("ui_attach", 80, 20, {})
-- Induce a hit-enter prompt use nvim_input (non-blocking).
nvim('command', 'set cmdheight=1')
nvim('input', [[:echo "hi\nhi2"<CR>]])
@@ -535,6 +541,31 @@ describe('API', function()
end)
end)
+ describe('nvim_set_current_dir', function()
+ local start_dir
+
+ before_each(function()
+ clear()
+ funcs.mkdir("Xtestdir")
+ start_dir = funcs.getcwd()
+ end)
+
+ after_each(function()
+ helpers.rmdir("Xtestdir")
+ end)
+
+ it('works', function()
+ meths.set_current_dir("Xtestdir")
+ eq(funcs.getcwd(), start_dir .. helpers.get_pathsep() .. "Xtestdir")
+ end)
+
+ it('sets previous directory', function()
+ meths.set_current_dir("Xtestdir")
+ meths.exec('cd -', false)
+ eq(funcs.getcwd(), start_dir)
+ end)
+ end)
+
describe('nvim_exec_lua', function()
it('works', function()
meths.exec_lua('vim.api.nvim_set_var("test", 3)', {})
@@ -609,34 +640,374 @@ describe('API', function()
eq('Invalid phase: 4',
pcall_err(request, 'nvim_paste', 'foo', true, 4))
end)
- it('stream: multiple chunks form one undo-block', function()
- nvim('paste', '1/chunk 1 (start)\n', true, 1)
- nvim('paste', '1/chunk 2 (end)\n', true, 3)
- local expected1 = [[
- 1/chunk 1 (start)
- 1/chunk 2 (end)
- ]]
- expect(expected1)
- nvim('paste', '2/chunk 1 (start)\n', true, 1)
- nvim('paste', '2/chunk 2\n', true, 2)
- expect([[
- 1/chunk 1 (start)
- 1/chunk 2 (end)
- 2/chunk 1 (start)
- 2/chunk 2
- ]])
- nvim('paste', '2/chunk 3\n', true, 2)
- nvim('paste', '2/chunk 4 (end)\n', true, 3)
- expect([[
- 1/chunk 1 (start)
- 1/chunk 2 (end)
- 2/chunk 1 (start)
- 2/chunk 2
- 2/chunk 3
- 2/chunk 4 (end)
- ]])
- feed('u') -- Undo.
- expect(expected1)
+ local function run_streamed_paste_tests()
+ it('stream: multiple chunks form one undo-block', function()
+ nvim('paste', '1/chunk 1 (start)\n', true, 1)
+ nvim('paste', '1/chunk 2 (end)\n', true, 3)
+ local expected1 = [[
+ 1/chunk 1 (start)
+ 1/chunk 2 (end)
+ ]]
+ expect(expected1)
+ nvim('paste', '2/chunk 1 (start)\n', true, 1)
+ nvim('paste', '2/chunk 2\n', true, 2)
+ expect([[
+ 1/chunk 1 (start)
+ 1/chunk 2 (end)
+ 2/chunk 1 (start)
+ 2/chunk 2
+ ]])
+ nvim('paste', '2/chunk 3\n', true, 2)
+ nvim('paste', '2/chunk 4 (end)\n', true, 3)
+ expect([[
+ 1/chunk 1 (start)
+ 1/chunk 2 (end)
+ 2/chunk 1 (start)
+ 2/chunk 2
+ 2/chunk 3
+ 2/chunk 4 (end)
+ ]])
+ feed('u') -- Undo.
+ expect(expected1)
+ end)
+ it('stream: Insert mode', function()
+ -- If nvim_paste() calls :undojoin without making any changes, this makes it an error.
+ feed('afoo<Esc>u')
+ feed('i')
+ nvim('paste', 'aaaaaa', false, 1)
+ nvim('paste', 'bbbbbb', false, 2)
+ nvim('paste', 'cccccc', false, 2)
+ nvim('paste', 'dddddd', false, 3)
+ expect('aaaaaabbbbbbccccccdddddd')
+ feed('<Esc>u')
+ expect('')
+ end)
+ describe('stream: Normal mode', function()
+ describe('on empty line', function()
+ before_each(function()
+ -- If nvim_paste() calls :undojoin without making any changes, this makes it an error.
+ feed('afoo<Esc>u')
+ end)
+ after_each(function()
+ feed('u')
+ expect('')
+ end)
+ it('pasting one line', function()
+ nvim('paste', 'aaaaaa', false, 1)
+ nvim('paste', 'bbbbbb', false, 2)
+ nvim('paste', 'cccccc', false, 2)
+ nvim('paste', 'dddddd', false, 3)
+ expect('aaaaaabbbbbbccccccdddddd')
+ end)
+ it('pasting multiple lines', function()
+ nvim('paste', 'aaaaaa\n', false, 1)
+ nvim('paste', 'bbbbbb\n', false, 2)
+ nvim('paste', 'cccccc\n', false, 2)
+ nvim('paste', 'dddddd', false, 3)
+ expect([[
+ aaaaaa
+ bbbbbb
+ cccccc
+ dddddd]])
+ end)
+ end)
+ describe('not at the end of a line', function()
+ before_each(function()
+ feed('i||<Esc>')
+ -- If nvim_paste() calls :undojoin without making any changes, this makes it an error.
+ feed('afoo<Esc>u')
+ feed('0')
+ end)
+ after_each(function()
+ feed('u')
+ expect('||')
+ end)
+ it('pasting one line', function()
+ nvim('paste', 'aaaaaa', false, 1)
+ nvim('paste', 'bbbbbb', false, 2)
+ nvim('paste', 'cccccc', false, 2)
+ nvim('paste', 'dddddd', false, 3)
+ expect('|aaaaaabbbbbbccccccdddddd|')
+ end)
+ it('pasting multiple lines', function()
+ nvim('paste', 'aaaaaa\n', false, 1)
+ nvim('paste', 'bbbbbb\n', false, 2)
+ nvim('paste', 'cccccc\n', false, 2)
+ nvim('paste', 'dddddd', false, 3)
+ expect([[
+ |aaaaaa
+ bbbbbb
+ cccccc
+ dddddd|]])
+ end)
+ end)
+ describe('at the end of a line', function()
+ before_each(function()
+ feed('i||<Esc>')
+ -- If nvim_paste() calls :undojoin without making any changes, this makes it an error.
+ feed('afoo<Esc>u')
+ feed('2|')
+ end)
+ after_each(function()
+ feed('u')
+ expect('||')
+ end)
+ it('pasting one line', function()
+ nvim('paste', 'aaaaaa', false, 1)
+ nvim('paste', 'bbbbbb', false, 2)
+ nvim('paste', 'cccccc', false, 2)
+ nvim('paste', 'dddddd', false, 3)
+ expect('||aaaaaabbbbbbccccccdddddd')
+ end)
+ it('pasting multiple lines', function()
+ nvim('paste', 'aaaaaa\n', false, 1)
+ nvim('paste', 'bbbbbb\n', false, 2)
+ nvim('paste', 'cccccc\n', false, 2)
+ nvim('paste', 'dddddd', false, 3)
+ expect([[
+ ||aaaaaa
+ bbbbbb
+ cccccc
+ dddddd]])
+ end)
+ end)
+ end)
+ describe('stream: Visual mode', function()
+ describe('neither end at the end of a line', function()
+ before_each(function()
+ feed('i|xxx<CR>xxx|<Esc>')
+ -- If nvim_paste() calls :undojoin without making any changes, this makes it an error.
+ feed('afoo<Esc>u')
+ feed('3|vhk')
+ end)
+ after_each(function()
+ feed('u')
+ expect([[
+ |xxx
+ xxx|]])
+ end)
+ it('with non-empty chunks', function()
+ nvim('paste', 'aaaaaa', false, 1)
+ nvim('paste', 'bbbbbb', false, 2)
+ nvim('paste', 'cccccc', false, 2)
+ nvim('paste', 'dddddd', false, 3)
+ expect('|aaaaaabbbbbbccccccdddddd|')
+ end)
+ it('with empty first chunk', function()
+ nvim('paste', '', false, 1)
+ nvim('paste', 'bbbbbb', false, 2)
+ nvim('paste', 'cccccc', false, 2)
+ nvim('paste', 'dddddd', false, 3)
+ expect('|bbbbbbccccccdddddd|')
+ end)
+ it('with all chunks empty', function()
+ nvim('paste', '', false, 1)
+ nvim('paste', '', false, 2)
+ nvim('paste', '', false, 2)
+ nvim('paste', '', false, 3)
+ expect('||')
+ end)
+ end)
+ describe('cursor at the end of a line', function()
+ before_each(function()
+ feed('i||xxx<CR>xxx<Esc>')
+ -- If nvim_paste() calls :undojoin without making any changes, this makes it an error.
+ feed('afoo<Esc>u')
+ feed('3|vko')
+ end)
+ after_each(function()
+ feed('u')
+ expect([[
+ ||xxx
+ xxx]])
+ end)
+ it('with non-empty chunks', function()
+ nvim('paste', 'aaaaaa', false, 1)
+ nvim('paste', 'bbbbbb', false, 2)
+ nvim('paste', 'cccccc', false, 2)
+ nvim('paste', 'dddddd', false, 3)
+ expect('||aaaaaabbbbbbccccccdddddd')
+ end)
+ it('with empty first chunk', function()
+ nvim('paste', '', false, 1)
+ nvim('paste', 'bbbbbb', false, 2)
+ nvim('paste', 'cccccc', false, 2)
+ nvim('paste', 'dddddd', false, 3)
+ expect('||bbbbbbccccccdddddd')
+ end)
+ end)
+ describe('other end at the end of a line', function()
+ before_each(function()
+ feed('i||xxx<CR>xxx<Esc>')
+ -- If nvim_paste() calls :undojoin without making any changes, this makes it an error.
+ feed('afoo<Esc>u')
+ feed('3|vk')
+ end)
+ after_each(function()
+ feed('u')
+ expect([[
+ ||xxx
+ xxx]])
+ end)
+ it('with non-empty chunks', function()
+ nvim('paste', 'aaaaaa', false, 1)
+ nvim('paste', 'bbbbbb', false, 2)
+ nvim('paste', 'cccccc', false, 2)
+ nvim('paste', 'dddddd', false, 3)
+ expect('||aaaaaabbbbbbccccccdddddd')
+ end)
+ it('with empty first chunk', function()
+ nvim('paste', '', false, 1)
+ nvim('paste', 'bbbbbb', false, 2)
+ nvim('paste', 'cccccc', false, 2)
+ nvim('paste', 'dddddd', false, 3)
+ expect('||bbbbbbccccccdddddd')
+ end)
+ end)
+ end)
+ describe('stream: linewise Visual mode', function()
+ before_each(function()
+ feed('i123456789<CR>987654321<CR>123456789<Esc>')
+ -- If nvim_paste() calls :undojoin without making any changes, this makes it an error.
+ feed('afoo<Esc>u')
+ end)
+ after_each(function()
+ feed('u')
+ expect([[
+ 123456789
+ 987654321
+ 123456789]])
+ end)
+ describe('selecting the start of a file', function()
+ before_each(function()
+ feed('ggV')
+ end)
+ it('pasting text without final new line', function()
+ nvim('paste', 'aaaaaa\n', false, 1)
+ nvim('paste', 'bbbbbb\n', false, 2)
+ nvim('paste', 'cccccc\n', false, 2)
+ nvim('paste', 'dddddd', false, 3)
+ expect([[
+ aaaaaa
+ bbbbbb
+ cccccc
+ dddddd987654321
+ 123456789]])
+ end)
+ it('pasting text with final new line', function()
+ nvim('paste', 'aaaaaa\n', false, 1)
+ nvim('paste', 'bbbbbb\n', false, 2)
+ nvim('paste', 'cccccc\n', false, 2)
+ nvim('paste', 'dddddd\n', false, 3)
+ expect([[
+ aaaaaa
+ bbbbbb
+ cccccc
+ dddddd
+ 987654321
+ 123456789]])
+ end)
+ end)
+ describe('selecting the middle of a file', function()
+ before_each(function()
+ feed('2ggV')
+ end)
+ it('pasting text without final new line', function()
+ nvim('paste', 'aaaaaa\n', false, 1)
+ nvim('paste', 'bbbbbb\n', false, 2)
+ nvim('paste', 'cccccc\n', false, 2)
+ nvim('paste', 'dddddd', false, 3)
+ expect([[
+ 123456789
+ aaaaaa
+ bbbbbb
+ cccccc
+ dddddd123456789]])
+ end)
+ it('pasting text with final new line', function()
+ nvim('paste', 'aaaaaa\n', false, 1)
+ nvim('paste', 'bbbbbb\n', false, 2)
+ nvim('paste', 'cccccc\n', false, 2)
+ nvim('paste', 'dddddd\n', false, 3)
+ expect([[
+ 123456789
+ aaaaaa
+ bbbbbb
+ cccccc
+ dddddd
+ 123456789]])
+ end)
+ end)
+ describe('selecting the end of a file', function()
+ before_each(function()
+ feed('3ggV')
+ end)
+ it('pasting text without final new line', function()
+ nvim('paste', 'aaaaaa\n', false, 1)
+ nvim('paste', 'bbbbbb\n', false, 2)
+ nvim('paste', 'cccccc\n', false, 2)
+ nvim('paste', 'dddddd', false, 3)
+ expect([[
+ 123456789
+ 987654321
+ aaaaaa
+ bbbbbb
+ cccccc
+ dddddd]])
+ end)
+ it('pasting text with final new line', function()
+ nvim('paste', 'aaaaaa\n', false, 1)
+ nvim('paste', 'bbbbbb\n', false, 2)
+ nvim('paste', 'cccccc\n', false, 2)
+ nvim('paste', 'dddddd\n', false, 3)
+ expect([[
+ 123456789
+ 987654321
+ aaaaaa
+ bbbbbb
+ cccccc
+ dddddd
+ ]])
+ end)
+ end)
+ describe('selecting the whole file', function()
+ before_each(function()
+ feed('ggVG')
+ end)
+ it('pasting text without final new line', function()
+ nvim('paste', 'aaaaaa\n', false, 1)
+ nvim('paste', 'bbbbbb\n', false, 2)
+ nvim('paste', 'cccccc\n', false, 2)
+ nvim('paste', 'dddddd', false, 3)
+ expect([[
+ aaaaaa
+ bbbbbb
+ cccccc
+ dddddd]])
+ end)
+ it('pasting text with final new line', function()
+ nvim('paste', 'aaaaaa\n', false, 1)
+ nvim('paste', 'bbbbbb\n', false, 2)
+ nvim('paste', 'cccccc\n', false, 2)
+ nvim('paste', 'dddddd\n', false, 3)
+ expect([[
+ aaaaaa
+ bbbbbb
+ cccccc
+ dddddd
+ ]])
+ end)
+ end)
+ end)
+ end
+ describe('without virtualedit,', function()
+ run_streamed_paste_tests()
+ end)
+ describe('with virtualedit=onemore,', function()
+ before_each(function()
+ command('set virtualedit=onemore')
+ end)
+ run_streamed_paste_tests()
end)
it('non-streaming', function()
-- With final "\n".
@@ -711,6 +1082,43 @@ describe('API', function()
eeffgghh
iijjkkll]])
end)
+ it('when searching in Visual mode', function()
+ feed('v/')
+ nvim('paste', 'aabbccdd', true, -1)
+ eq('aabbccdd', funcs.getcmdline())
+ expect('')
+ end)
+ it('mappings are disabled in Cmdline mode', function()
+ command('cnoremap a b')
+ feed(':')
+ nvim('paste', 'a', true, -1)
+ eq('a', funcs.getcmdline())
+ end)
+ it('pasting with empty last chunk in Cmdline mode', function()
+ local screen = Screen.new(20, 4)
+ screen:attach()
+ feed(':')
+ nvim('paste', 'Foo', true, 1)
+ nvim('paste', '', true, 3)
+ screen:expect([[
+ |
+ ~ |
+ ~ |
+ :Foo^ |
+ ]])
+ end)
+ it('pasting text with control characters in Cmdline mode', function()
+ local screen = Screen.new(20, 4)
+ screen:attach()
+ feed(':')
+ nvim('paste', 'normal! \023\022\006\027', true, -1)
+ screen:expect([[
+ |
+ ~ |
+ ~ |
+ :normal! ^W^V^F^[^ |
+ ]])
+ end)
it('crlf=false does not break lines at CR, CRLF', function()
nvim('paste', 'line 1\r\n\r\rline 2\nline 3\rline 4\r', false, -1)
expect('line 1\r\n\r\rline 2\nline 3\rline 4\r')
@@ -872,6 +1280,30 @@ describe('API', function()
command('lockvar lua')
eq('Key is locked: lua', pcall_err(meths.del_var, 'lua'))
eq('Key is locked: lua', pcall_err(meths.set_var, 'lua', 1))
+
+ exec([[
+ function Test()
+ endfunction
+ function s:Test()
+ endfunction
+ let g:Unknown_func = function('Test')
+ let g:Unknown_script_func = function('s:Test')
+ ]])
+ eq(NIL, meths.get_var('Unknown_func'))
+ eq(NIL, meths.get_var('Unknown_script_func'))
+
+ -- Check if autoload works properly
+ local pathsep = helpers.get_pathsep()
+ local xconfig = 'Xhome' .. pathsep .. 'Xconfig'
+ local xdata = 'Xhome' .. pathsep .. 'Xdata'
+ local autoload_folder = table.concat({xconfig, 'nvim', 'autoload'}, pathsep)
+ local autoload_file = table.concat({autoload_folder , 'testload.vim'}, pathsep)
+ mkdir_p(autoload_folder)
+ write_file(autoload_file , [[let testload#value = 2]])
+
+ clear{ args_rm={'-u'}, env={ XDG_CONFIG_HOME=xconfig, XDG_DATA_HOME=xdata } }
+ eq(2, meths.get_var('testload#value'))
+ rmdir('Xhome')
end)
it('nvim_get_vvar, nvim_set_vvar', function()
@@ -1008,6 +1440,46 @@ describe('API', function()
nvim('set_option_value', 'autoread', NIL, {scope = 'local'})
eq(NIL, nvim('get_option_value', 'autoread', {scope = 'local'}))
end)
+
+ it('set window options', function()
+ -- Same as to nvim_win_set_option
+ nvim('set_option_value', 'colorcolumn', '4,3', {win=0})
+ eq('4,3', nvim('get_option_value', 'colorcolumn', {scope = 'local'}))
+ command("set modified hidden")
+ command("enew") -- edit new buffer, window option is preserved
+ eq('4,3', nvim('get_option_value', 'colorcolumn', {scope = 'local'}))
+ end)
+
+ it('set local window options', function()
+ -- Different to nvim_win_set_option
+ nvim('set_option_value', 'colorcolumn', '4,3', {win=0, scope='local'})
+ eq('4,3', nvim('get_option_value', 'colorcolumn', {win = 0, scope = 'local'}))
+ command("set modified hidden")
+ command("enew") -- edit new buffer, window option is reset
+ eq('', nvim('get_option_value', 'colorcolumn', {win = 0, scope = 'local'}))
+ end)
+
+ it('get buffer or window-local options', function()
+ nvim('command', 'new')
+ local buf = nvim('get_current_buf').id
+ nvim('buf_set_option', buf, 'tagfunc', 'foobar')
+ eq('foobar', nvim('get_option_value', 'tagfunc', {buf = buf}))
+
+ local win = nvim('get_current_win').id
+ nvim('win_set_option', win, 'number', true)
+ eq(true, nvim('get_option_value', 'number', {win = win}))
+ end)
+
+ it('getting current buffer option does not adjust cursor #19381', function()
+ nvim('command', 'new')
+ local buf = nvim('get_current_buf').id
+ local win = nvim('get_current_win').id
+ insert('some text')
+ feed('0v$')
+ eq({1, 9}, nvim('win_get_cursor', win))
+ nvim('get_option_value', 'filetype', {buf = buf})
+ eq({1, 9}, nvim('win_get_cursor', win))
+ end)
end)
describe('nvim_{get,set}_current_buf, nvim_list_bufs', function()
@@ -1093,7 +1565,20 @@ describe('API', function()
eq({mode='n', blocking=false}, nvim("get_mode"))
end)
+ it("during press-enter prompt without UI returns blocking=false", function()
+ eq({mode='n', blocking=false}, nvim("get_mode"))
+ command("echom 'msg1'")
+ command("echom 'msg2'")
+ command("echom 'msg3'")
+ command("echom 'msg4'")
+ command("echom 'msg5'")
+ eq({mode='n', blocking=false}, nvim("get_mode"))
+ nvim("input", ":messages<CR>")
+ eq({mode='n', blocking=false}, nvim("get_mode"))
+ end)
+
it("during press-enter prompt returns blocking=true", function()
+ nvim("ui_attach", 80, 20, {})
eq({mode='n', blocking=false}, nvim("get_mode"))
command("echom 'msg1'")
command("echom 'msg2'")
@@ -1117,6 +1602,7 @@ describe('API', function()
-- TODO: bug #6247#issuecomment-286403810
it("batched with input", function()
+ nvim("ui_attach", 80, 20, {})
eq({mode='n', blocking=false}, nvim("get_mode"))
command("echom 'msg1'")
command("echom 'msg2'")
@@ -1152,6 +1638,18 @@ describe('API', function()
feed(':digraphs<cr>')
eq({mode='rm', blocking=true}, nvim("get_mode"))
end)
+
+ it('after <Nop> mapping returns blocking=false #17257', function()
+ command('nnoremap <F2> <Nop>')
+ feed('<F2>')
+ eq({mode='n', blocking=false}, nvim("get_mode"))
+ end)
+
+ it('after empty string <expr> mapping returns blocking=false #17257', function()
+ command('nnoremap <expr> <F2> ""')
+ feed('<F2>')
+ eq({mode='n', blocking=false}, nvim("get_mode"))
+ end)
end)
describe('RPC (K_EVENT)', function()
@@ -1367,18 +1865,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
@@ -1386,10 +1884,10 @@ describe('API', function()
-- spin the loop a bit
helpers.run(nil, nil, on_setup)
- eq(nvim('get_var', 'x1'), '…')
+ eq('…', nvim('get_var', 'x1'))
-- Because of the double escaping this is neq
- neq(nvim('get_var', 'x2'), '…')
- eq(nvim('get_var', 'x3'), '…')
+ neq('…', nvim('get_var', 'x2'))
+ eq('…', nvim('get_var', 'x3'))
end)
end)
@@ -2142,7 +2640,7 @@ describe('API', function()
it('does not cause heap-use-after-free on exit while setting options', function()
command('au OptionSet * q')
- command('silent! call nvim_create_buf(0, 1)')
+ expect_exit(command, 'silent! call nvim_create_buf(0, 1)')
end)
end)
@@ -2187,6 +2685,14 @@ describe('API', function()
eq({}, meths.get_runtime_file("foobarlang/", true))
end)
+ it('can handle bad patterns', function()
+ if helpers.pending_win32(pending) then return end
+
+ eq("Vim:E220: Missing }.", pcall_err(meths.get_runtime_file, "{", false))
+
+ eq('Vim(echo):E5555: API call: Vim:E220: Missing }.',
+ exc_exec("echo nvim_get_runtime_file('{', v:false)"))
+ end)
end)
describe('nvim_get_all_options_info', function()
@@ -2551,6 +3057,38 @@ describe('API', function()
'Should be truncated%<',
{ maxwidth = 15 }))
end)
+ it('supports ASCII fillchar', function()
+ eq({ str = 'a~~~b', width = 5 },
+ meths.eval_statusline('a%=b', { fillchar = '~', maxwidth = 5 }))
+ end)
+ it('supports single-width multibyte fillchar', function()
+ eq({ str = 'a━━━b', width = 5 },
+ meths.eval_statusline('a%=b', { fillchar = '━', maxwidth = 5 }))
+ end)
+ it('treats double-width fillchar as single-width', function()
+ eq({ str = 'a哦哦哦b', width = 5 },
+ meths.eval_statusline('a%=b', { fillchar = '哦', maxwidth = 5 }))
+ end)
+ it('treats control character fillchar as single-width', function()
+ eq({ str = 'a\031\031\031b', width = 5 },
+ meths.eval_statusline('a%=b', { fillchar = '\031', maxwidth = 5 }))
+ end)
+ it('rejects multiple-character fillchar', function()
+ eq('fillchar must be a single character',
+ pcall_err(meths.eval_statusline, '', { fillchar = 'aa' }))
+ end)
+ it('rejects empty string fillchar', function()
+ eq('fillchar must be a single character',
+ pcall_err(meths.eval_statusline, '', { fillchar = '' }))
+ end)
+ it('rejects non-string fillchar', function()
+ eq('fillchar must be a single character',
+ pcall_err(meths.eval_statusline, '', { fillchar = 1 }))
+ end)
+ it('rejects invalid string', function()
+ eq('E539: Illegal character <}>',
+ pcall_err(meths.eval_statusline, '%{%}', {}))
+ end)
describe('highlight parsing', function()
it('works', function()
eq({
@@ -2605,6 +3143,678 @@ describe('API', function()
'TextWithNoHighlight%#WarningMsg#TextWithWarningHighlight',
{ use_tabline = true, highlights = true }))
end)
+ it('works with winbar', function()
+ eq({
+ str = 'TextWithNoHighlightTextWithWarningHighlight',
+ width = 43,
+ highlights = {
+ { start = 0, group = 'WinBar' },
+ { start = 19, group = 'WarningMsg' }
+ }
+ },
+ meths.eval_statusline(
+ 'TextWithNoHighlight%#WarningMsg#TextWithWarningHighlight',
+ { use_winbar = true, highlights = true }))
+ end)
+ end)
+ end)
+ describe('nvim_parse_cmd', function()
+ it('works', function()
+ eq({
+ cmd = 'echo',
+ args = { 'foo' },
+ bang = false,
+ range = {},
+ count = -1,
+ reg = '',
+ addr = 'none',
+ magic = {
+ file = false,
+ bar = false
+ },
+ nargs = '*',
+ nextcmd = '',
+ mods = {
+ browse = false,
+ confirm = false,
+ emsg_silent = false,
+ filter = {
+ pattern = "",
+ force = false
+ },
+ hide = false,
+ keepalt = false,
+ keepjumps = false,
+ keepmarks = false,
+ keeppatterns = false,
+ lockmarks = false,
+ noautocmd = false,
+ noswapfile = false,
+ sandbox = false,
+ silent = false,
+ split = "",
+ tab = 0,
+ unsilent = false,
+ verbose = -1,
+ vertical = false,
+ }
+ }, meths.parse_cmd('echo foo', {}))
+ end)
+ it('works with ranges', function()
+ eq({
+ cmd = 'substitute',
+ args = { '/math.random/math.max/' },
+ bang = false,
+ range = { 4, 6 },
+ count = -1,
+ reg = '',
+ addr = 'line',
+ magic = {
+ file = false,
+ bar = false
+ },
+ nargs = '*',
+ nextcmd = '',
+ mods = {
+ browse = false,
+ confirm = false,
+ emsg_silent = false,
+ filter = {
+ pattern = "",
+ force = false
+ },
+ hide = false,
+ keepalt = false,
+ keepjumps = false,
+ keepmarks = false,
+ keeppatterns = false,
+ lockmarks = false,
+ noautocmd = false,
+ noswapfile = false,
+ sandbox = false,
+ silent = false,
+ split = "",
+ tab = 0,
+ unsilent = false,
+ verbose = -1,
+ vertical = false,
+ }
+ }, meths.parse_cmd('4,6s/math.random/math.max/', {}))
+ end)
+ it('works with count', function()
+ eq({
+ cmd = 'buffer',
+ args = {},
+ bang = false,
+ range = { 1 },
+ count = 1,
+ reg = '',
+ addr = 'buf',
+ magic = {
+ file = false,
+ bar = true
+ },
+ nargs = '*',
+ nextcmd = '',
+ mods = {
+ browse = false,
+ confirm = false,
+ emsg_silent = false,
+ filter = {
+ pattern = "",
+ force = false
+ },
+ hide = false,
+ keepalt = false,
+ keepjumps = false,
+ keepmarks = false,
+ keeppatterns = false,
+ lockmarks = false,
+ noautocmd = false,
+ noswapfile = false,
+ sandbox = false,
+ silent = false,
+ split = "",
+ tab = 0,
+ unsilent = false,
+ verbose = -1,
+ vertical = false,
+ }
+ }, meths.parse_cmd('buffer 1', {}))
+ end)
+ it('works with register', function()
+ eq({
+ cmd = 'put',
+ args = {},
+ bang = false,
+ range = {},
+ count = -1,
+ reg = '+',
+ addr = 'line',
+ magic = {
+ file = false,
+ bar = true
+ },
+ nargs = '0',
+ nextcmd = '',
+ mods = {
+ browse = false,
+ confirm = false,
+ emsg_silent = false,
+ filter = {
+ pattern = "",
+ force = false
+ },
+ hide = false,
+ keepalt = false,
+ keepjumps = false,
+ keepmarks = false,
+ keeppatterns = false,
+ lockmarks = false,
+ noautocmd = false,
+ noswapfile = false,
+ sandbox = false,
+ silent = false,
+ split = "",
+ tab = 0,
+ unsilent = false,
+ verbose = -1,
+ vertical = false,
+ }
+ }, meths.parse_cmd('put +', {}))
+ end)
+ it('works with range, count and register', function()
+ eq({
+ cmd = 'delete',
+ args = {},
+ bang = false,
+ range = { 3, 7 },
+ count = 7,
+ reg = '*',
+ addr = 'line',
+ magic = {
+ file = false,
+ bar = true
+ },
+ nargs = '0',
+ nextcmd = '',
+ mods = {
+ browse = false,
+ confirm = false,
+ emsg_silent = false,
+ filter = {
+ pattern = "",
+ force = false
+ },
+ hide = false,
+ keepalt = false,
+ keepjumps = false,
+ keepmarks = false,
+ keeppatterns = false,
+ lockmarks = false,
+ noautocmd = false,
+ noswapfile = false,
+ sandbox = false,
+ silent = false,
+ split = "",
+ tab = 0,
+ unsilent = false,
+ verbose = -1,
+ vertical = false,
+ }
+ }, meths.parse_cmd('1,3delete * 5', {}))
+ end)
+ it('works with bang', function()
+ eq({
+ cmd = 'write',
+ args = {},
+ bang = true,
+ range = {},
+ count = -1,
+ reg = '',
+ addr = 'line',
+ magic = {
+ file = true,
+ bar = true
+ },
+ nargs = '?',
+ nextcmd = '',
+ mods = {
+ browse = false,
+ confirm = false,
+ emsg_silent = false,
+ filter = {
+ pattern = "",
+ force = false
+ },
+ hide = false,
+ keepalt = false,
+ keepjumps = false,
+ keepmarks = false,
+ keeppatterns = false,
+ lockmarks = false,
+ noautocmd = false,
+ noswapfile = false,
+ sandbox = false,
+ silent = false,
+ split = "",
+ tab = 0,
+ unsilent = false,
+ verbose = -1,
+ vertical = false,
+ },
+ }, meths.parse_cmd('w!', {}))
+ end)
+ it('works with modifiers', function()
+ eq({
+ cmd = 'split',
+ args = { 'foo.txt' },
+ bang = false,
+ range = {},
+ count = -1,
+ reg = '',
+ addr = '?',
+ magic = {
+ file = true,
+ bar = true
+ },
+ nargs = '?',
+ nextcmd = '',
+ mods = {
+ browse = false,
+ confirm = false,
+ emsg_silent = true,
+ filter = {
+ pattern = "foo",
+ force = false
+ },
+ hide = false,
+ keepalt = false,
+ keepjumps = false,
+ keepmarks = false,
+ keeppatterns = false,
+ lockmarks = false,
+ noautocmd = false,
+ noswapfile = false,
+ sandbox = false,
+ silent = true,
+ split = "topleft",
+ tab = 2,
+ unsilent = false,
+ verbose = 15,
+ vertical = false,
+ },
+ }, meths.parse_cmd('15verbose silent! aboveleft topleft tab filter /foo/ split foo.txt', {}))
+ eq({
+ cmd = 'split',
+ args = { 'foo.txt' },
+ bang = false,
+ range = {},
+ count = -1,
+ reg = '',
+ addr = '?',
+ magic = {
+ file = true,
+ bar = true
+ },
+ nargs = '?',
+ nextcmd = '',
+ mods = {
+ browse = false,
+ confirm = true,
+ emsg_silent = false,
+ filter = {
+ pattern = "foo",
+ force = true
+ },
+ hide = false,
+ keepalt = false,
+ keepjumps = false,
+ keepmarks = false,
+ keeppatterns = false,
+ lockmarks = false,
+ noautocmd = false,
+ noswapfile = false,
+ sandbox = false,
+ silent = false,
+ split = "botright",
+ tab = 0,
+ unsilent = true,
+ verbose = 0,
+ vertical = false,
+ },
+ }, meths.parse_cmd('0verbose unsilent botright confirm filter! /foo/ split foo.txt', {}))
+ end)
+ it('works with user commands', function()
+ command('command -bang -nargs=+ -range -addr=lines MyCommand echo foo')
+ eq({
+ cmd = 'MyCommand',
+ args = { 'test', 'it' },
+ bang = true,
+ range = { 4, 6 },
+ count = -1,
+ reg = '',
+ addr = 'line',
+ magic = {
+ file = false,
+ bar = false
+ },
+ nargs = '+',
+ nextcmd = '',
+ mods = {
+ browse = false,
+ confirm = false,
+ emsg_silent = false,
+ filter = {
+ pattern = "",
+ force = false
+ },
+ hide = false,
+ keepalt = false,
+ keepjumps = false,
+ keepmarks = false,
+ keeppatterns = false,
+ lockmarks = false,
+ noautocmd = false,
+ noswapfile = false,
+ sandbox = false,
+ silent = false,
+ split = "",
+ tab = 0,
+ unsilent = false,
+ verbose = -1,
+ vertical = false,
+ }
+ }, meths.parse_cmd('4,6MyCommand! test it', {}))
+ end)
+ it('works for commands separated by bar', function()
+ eq({
+ cmd = 'argadd',
+ args = { 'a.txt' },
+ bang = false,
+ range = {},
+ count = -1,
+ reg = '',
+ addr = 'arg',
+ magic = {
+ file = true,
+ bar = true
+ },
+ nargs = '*',
+ nextcmd = 'argadd b.txt',
+ mods = {
+ browse = false,
+ confirm = false,
+ emsg_silent = false,
+ filter = {
+ pattern = "",
+ force = false
+ },
+ hide = false,
+ keepalt = false,
+ keepjumps = false,
+ keepmarks = false,
+ keeppatterns = false,
+ lockmarks = false,
+ noautocmd = false,
+ noswapfile = false,
+ sandbox = false,
+ silent = false,
+ split = "",
+ tab = 0,
+ unsilent = false,
+ verbose = -1,
+ vertical = false,
+ }
+ }, meths.parse_cmd('argadd a.txt | argadd b.txt', {}))
+ end)
+ it('works for nargs=1', function()
+ command('command -nargs=1 MyCommand echo <q-args>')
+ eq({
+ cmd = 'MyCommand',
+ args = { 'test it' },
+ bang = false,
+ range = {},
+ count = -1,
+ reg = '',
+ addr = 'none',
+ magic = {
+ file = false,
+ bar = false
+ },
+ nargs = '1',
+ nextcmd = '',
+ mods = {
+ browse = false,
+ confirm = false,
+ emsg_silent = false,
+ filter = {
+ pattern = "",
+ force = false
+ },
+ hide = false,
+ keepalt = false,
+ keepjumps = false,
+ keepmarks = false,
+ keeppatterns = false,
+ lockmarks = false,
+ noautocmd = false,
+ noswapfile = false,
+ sandbox = false,
+ silent = false,
+ split = "",
+ tab = 0,
+ unsilent = false,
+ verbose = -1,
+ vertical = false,
+ }
+ }, meths.parse_cmd('MyCommand test it', {}))
+ end)
+ it('errors for invalid command', function()
+ eq('Error while parsing command line', pcall_err(meths.parse_cmd, '', {}))
+ eq('Error while parsing command line', pcall_err(meths.parse_cmd, '" foo', {}))
+ eq('Error while parsing command line: E492: Not an editor command: Fubar',
+ pcall_err(meths.parse_cmd, 'Fubar', {}))
+ command('command! Fubar echo foo')
+ eq('Error while parsing command line: E477: No ! allowed',
+ pcall_err(meths.parse_cmd, 'Fubar!', {}))
+ eq('Error while parsing command line: E481: No range allowed',
+ pcall_err(meths.parse_cmd, '4,6Fubar', {}))
+ command('command! Foobar echo foo')
+ eq('Error while parsing command line: E464: Ambiguous use of user-defined command',
+ pcall_err(meths.parse_cmd, 'F', {}))
+ end)
+ it('does not interfere with printing line in Ex mode #19400', function()
+ local screen = Screen.new(60, 7)
+ screen:set_default_attr_ids({
+ [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ [1] = {bold = true, reverse = true}, -- MsgSeparator
+ })
+ screen:attach()
+ insert([[
+ foo
+ bar]])
+ feed('gQ1')
+ screen:expect([[
+ foo |
+ bar |
+ {0:~ }|
+ {0:~ }|
+ {1: }|
+ Entering Ex mode. Type "visual" to go to Normal mode. |
+ :1^ |
+ ]])
+ eq('Error while parsing command line', pcall_err(meths.parse_cmd, '', {}))
+ feed('<CR>')
+ screen:expect([[
+ foo |
+ bar |
+ {1: }|
+ Entering Ex mode. Type "visual" to go to Normal mode. |
+ :1 |
+ foo |
+ :^ |
+ ]])
+ end)
+ end)
+ describe('nvim_cmd', function()
+ it('works', function ()
+ meths.cmd({ cmd = "set", args = { "cursorline" } }, {})
+ eq(true, meths.get_option_value("cursorline", {}))
+ end)
+ it('captures output', function()
+ eq("foo", meths.cmd({ cmd = "echo", args = { '"foo"' } }, { output = true }))
+ end)
+ it('sets correct script context', function()
+ meths.cmd({ cmd = "set", args = { "cursorline" } }, {})
+ local str = meths.exec([[verbose set cursorline?]], true)
+ neq(nil, str:find("cursorline\n\tLast set from API client %(channel id %d+%)"))
+ end)
+ it('works with range', function()
+ insert [[
+ line1
+ line2
+ line3
+ line4
+ you didn't expect this
+ line5
+ line6
+ ]]
+ meths.cmd({ cmd = "del", range = {2, 4} }, {})
+ expect [[
+ line1
+ you didn't expect this
+ line5
+ line6
+ ]]
+ end)
+ it('works with count', function()
+ insert [[
+ line1
+ line2
+ line3
+ line4
+ you didn't expect this
+ line5
+ line6
+ ]]
+ meths.cmd({ cmd = "del", range = { 2 }, count = 4 }, {})
+ expect [[
+ line1
+ line5
+ line6
+ ]]
+ end)
+ it('works with register', function()
+ insert [[
+ line1
+ line2
+ line3
+ line4
+ you didn't expect this
+ line5
+ line6
+ ]]
+ meths.cmd({ cmd = "del", range = { 2, 4 }, reg = 'a' }, {})
+ meths.exec("1put a", false)
+ expect [[
+ line1
+ line2
+ line3
+ line4
+ you didn't expect this
+ line5
+ line6
+ ]]
+ end)
+ it('works with bang', function ()
+ meths.create_user_command("Foo", 'echo "<bang>"', { bang = true })
+ eq("!", meths.cmd({ cmd = "Foo", bang = true }, { output = true }))
+ eq("", meths.cmd({ cmd = "Foo", bang = false }, { output = true }))
+ end)
+ it('works with modifiers', function()
+ meths.create_user_command("Foo", 'set verbose', {})
+ eq(" verbose=1", meths.cmd({ cmd = "Foo", mods = { verbose = 1 } }, { output = true }))
+ eq(0, meths.get_option_value("verbose", {}))
+ command('edit foo.txt | edit bar.txt')
+ eq(' 1 #h "foo.txt" line 1',
+ meths.cmd({ cmd = "buffers", mods = { filter = { pattern = "foo", force = false } } },
+ { output = true }))
+ eq(' 2 %a "bar.txt" line 1',
+ meths.cmd({ cmd = "buffers", mods = { filter = { pattern = "foo", force = true } } },
+ { output = true }))
+ end)
+ it('works with magic.file', function()
+ exec_lua([[
+ vim.api.nvim_create_user_command("Foo", function(opts)
+ vim.api.nvim_echo({{ opts.fargs[1] }}, false, {})
+ end, { nargs = 1 })
+ ]])
+ eq(lfs.currentdir(),
+ meths.cmd({ cmd = "Foo", args = { '%:p:h' }, magic = { file = true } },
+ { output = true }))
+ end)
+ it('splits arguments correctly', function()
+ meths.exec([[
+ function! FooFunc(...)
+ echo a:000
+ endfunction
+ ]], false)
+ meths.create_user_command("Foo", "call FooFunc(<f-args>)", { nargs = '+' })
+ eq([=[['a quick', 'brown fox', 'jumps over the', 'lazy dog']]=],
+ meths.cmd({ cmd = "Foo", args = { "a quick", "brown fox", "jumps over the", "lazy dog"}},
+ { output = true }))
+ eq([=[['test \ \\ \"""\', 'more\ tests\" ']]=],
+ meths.cmd({ cmd = "Foo", args = { [[test \ \\ \"""\]], [[more\ tests\" ]] } },
+ { output = true }))
+ end)
+ it('splits arguments correctly for Lua callback', function()
+ meths.exec_lua([[
+ local function FooFunc(opts)
+ vim.pretty_print(opts.fargs)
+ end
+
+ vim.api.nvim_create_user_command("Foo", FooFunc, { nargs = '+' })
+ ]], {})
+ eq([[{ "a quick", "brown fox", "jumps over the", "lazy dog" }]],
+ meths.cmd({ cmd = "Foo", args = { "a quick", "brown fox", "jumps over the", "lazy dog"}},
+ { output = true }))
+ eq([[{ 'test \\ \\\\ \\"""\\', 'more\\ tests\\" ' }]],
+ meths.cmd({ cmd = "Foo", args = { [[test \ \\ \"""\]], [[more\ tests\" ]] } },
+ { output = true }))
+ end)
+ it('works with buffer names', function()
+ command("edit foo.txt | edit bar.txt")
+ meths.cmd({ cmd = "buffer", args = { "foo.txt" } }, {})
+ eq("foo.txt", funcs.fnamemodify(meths.buf_get_name(0), ":t"))
+ meths.cmd({ cmd = "buffer", args = { "bar.txt" } }, {})
+ eq("bar.txt", funcs.fnamemodify(meths.buf_get_name(0), ":t"))
+ end)
+ it('triggers CmdUndefined event if command is not found', function()
+ meths.exec_lua([[
+ vim.api.nvim_create_autocmd("CmdUndefined",
+ { pattern = "Foo",
+ callback = function()
+ vim.api.nvim_create_user_command("Foo", "echo 'foo'", {})
+ end
+ })
+ ]], {})
+ eq("foo", meths.cmd({ cmd = "Foo" }, { output = true }))
+ end)
+ it('errors if command is not implemented', function()
+ eq("Command not implemented: winpos", pcall_err(meths.cmd, { cmd = "winpos" }, {}))
+ end)
+ it('works with empty arguments list', function()
+ meths.cmd({ cmd = "update" }, {})
+ meths.cmd({ cmd = "buffer", count = 0 }, {})
+ end)
+ it('doesn\'t suppress errors when used in keymapping', function()
+ meths.exec_lua([[
+ vim.keymap.set("n", "[l",
+ function() vim.api.nvim_cmd({ cmd = "echo", args = {"foo"} }, {}) end)
+ ]], {})
+ feed("[l")
+ neq(nil, string.find(eval("v:errmsg"), "E5108:"))
end)
end)
end)
diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua
index 4d2ffa316a..901d24327c 100644
--- a/test/functional/api/window_spec.lua
+++ b/test/functional/api/window_spec.lua
@@ -1,4 +1,5 @@
local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
local clear, nvim, curbuf, curbuf_contents, window, curwin, eq, neq,
ok, feed, insert, eval, tabpage = helpers.clear, helpers.nvim, helpers.curbuf,
helpers.curbuf_contents, helpers.window, helpers.curwin, helpers.eq,
@@ -73,8 +74,7 @@ describe('API/win', function()
eq('typing\n some dumb text', curbuf_contents())
end)
- it('does not leak memory when using invalid window ID with invalid pos',
- function()
+ it('does not leak memory when using invalid window ID with invalid pos', function()
eq('Invalid window id: 1', pcall_err(meths.win_set_cursor, 1, {"b\na"}))
end)
@@ -147,6 +147,46 @@ describe('API/win', function()
eq({2, 5}, window('get_cursor', win))
end)
+ it('updates cursorline and statusline ruler in non-current window', function()
+ local screen = Screen.new(60, 8)
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ [2] = {background = Screen.colors.Grey90}, -- CursorLine
+ [3] = {bold = true, reverse = true}, -- StatusLine
+ [4] = {reverse = true}, -- StatusLineNC
+ })
+ screen:attach()
+ command('set ruler')
+ command('set cursorline')
+ insert([[
+ aaa
+ bbb
+ ccc
+ ddd]])
+ local oldwin = curwin()
+ command('vsplit')
+ screen:expect([[
+ aaa │aaa |
+ bbb │bbb |
+ ccc │ccc |
+ {2:dd^d }│{2:ddd }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {3:[No Name] [+] 4,3 All }{4:[No Name] [+] 4,3 All}|
+ |
+ ]])
+ window('set_cursor', oldwin, {1, 0})
+ screen:expect([[
+ aaa │{2:aaa }|
+ bbb │bbb |
+ ccc │ccc |
+ {2:dd^d }│ddd |
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {3:[No Name] [+] 4,3 All }{4:[No Name] [+] 1,1 All}|
+ |
+ ]])
+ end)
end)
describe('{get,set}_height', function()
diff --git a/test/functional/autocmd/autocmd_oldtest_spec.lua b/test/functional/autocmd/autocmd_oldtest_spec.lua
new file mode 100644
index 0000000000..ad3687d7b0
--- /dev/null
+++ b/test/functional/autocmd/autocmd_oldtest_spec.lua
@@ -0,0 +1,86 @@
+local helpers = require('test.functional.helpers')(after_each)
+
+local clear = helpers.clear
+local eq = helpers.eq
+local meths = helpers.meths
+local funcs = helpers.funcs
+
+local exec = function(str)
+ meths.exec(str, false)
+end
+
+describe('oldtests', function()
+ before_each(clear)
+
+ local exec_lines = function(str)
+ return funcs.split(funcs.execute(str), "\n")
+ end
+
+ local add_an_autocmd = function()
+ exec [[
+ augroup vimBarTest
+ au BufReadCmd * echo 'hello'
+ augroup END
+ ]]
+
+ eq(3, #exec_lines('au vimBarTest'))
+ eq(1, #meths.get_autocmds({ group = 'vimBarTest' }))
+ end
+
+ it('should recognize a bar before the {event}', function()
+ -- Good spacing
+ add_an_autocmd()
+ exec [[ augroup vimBarTest | au! | augroup END ]]
+ eq(1, #exec_lines('au vimBarTest'))
+ eq({}, meths.get_autocmds({ group = 'vimBarTest' }))
+
+ -- Sad spacing
+ add_an_autocmd()
+ exec [[ augroup vimBarTest| au!| augroup END ]]
+ eq(1, #exec_lines('au vimBarTest'))
+
+
+ -- test that a bar is recognized after the {event}
+ add_an_autocmd()
+ exec [[ augroup vimBarTest| au!BufReadCmd| augroup END ]]
+ eq(1, #exec_lines('au vimBarTest'))
+
+ add_an_autocmd()
+ exec [[ au! vimBarTest|echo 'hello' ]]
+ eq(1, #exec_lines('au vimBarTest'))
+ end)
+
+ it('should fire on unload buf', function()
+ funcs.writefile({'Test file Xxx1'}, 'Xxx1')
+ funcs.writefile({'Test file Xxx2'}, 'Xxx2')
+
+ local content = [[
+ func UnloadAllBufs()
+ let i = 1
+ while i <= bufnr('$')
+ if i != bufnr('%') && bufloaded(i)
+ exe i . 'bunload'
+ endif
+ let i += 1
+ endwhile
+ endfunc
+ au BufUnload * call UnloadAllBufs()
+ au VimLeave * call writefile(['Test Finished'], 'Xout')
+ set nohidden
+ edit Xxx1
+ split Xxx2
+ q
+ ]]
+
+ funcs.writefile(funcs.split(content, "\n"), 'Xtest')
+
+ funcs.delete('Xout')
+ funcs.system(meths.get_vvar('progpath') .. ' -u NORC -i NONE -N -S Xtest')
+ eq(1, funcs.filereadable('Xout'))
+
+ funcs.delete('Xxx1')
+ funcs.delete('Xxx2')
+ funcs.delete('Xtest')
+ funcs.delete('Xout')
+ end)
+end)
diff --git a/test/functional/autocmd/autocmd_spec.lua b/test/functional/autocmd/autocmd_spec.lua
index 93d71a9e45..90254b7415 100644
--- a/test/functional/autocmd/autocmd_spec.lua
+++ b/test/functional/autocmd/autocmd_spec.lua
@@ -2,8 +2,10 @@ local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local assert_visible = helpers.assert_visible
+local assert_alive = helpers.assert_alive
local dedent = helpers.dedent
local eq = helpers.eq
+local neq = helpers.neq
local eval = helpers.eval
local feed = helpers.feed
local clear = helpers.clear
@@ -13,7 +15,9 @@ local funcs = helpers.funcs
local expect = helpers.expect
local command = helpers.command
local exc_exec = helpers.exc_exec
+local exec_lua = helpers.exec_lua
local curbufmeths = helpers.curbufmeths
+local retry = helpers.retry
local source = helpers.source
describe('autocmd', function()
@@ -56,6 +60,23 @@ describe('autocmd', function()
eq(expected, eval('g:evs'))
end)
+ it('first edit causes BufUnload on NoName', function()
+ local expected = {
+ {'BufUnload', ''},
+ {'BufDelete', ''},
+ {'BufWipeout', ''},
+ {'BufEnter', 'testfile1'},
+ }
+ command('let g:evs = []')
+ command('autocmd BufEnter * :call add(g:evs, ["BufEnter", expand("<afile>")])')
+ command('autocmd BufDelete * :call add(g:evs, ["BufDelete", expand("<afile>")])')
+ command('autocmd BufLeave * :call add(g:evs, ["BufLeave", expand("<afile>")])')
+ command('autocmd BufUnload * :call add(g:evs, ["BufUnload", expand("<afile>")])')
+ command('autocmd BufWipeout * :call add(g:evs, ["BufWipeout", expand("<afile>")])')
+ command('edit testfile1')
+ eq(expected, eval('g:evs'))
+ end)
+
it('WinClosed is non-recursive', function()
command('let g:triggered = 0')
command('autocmd WinClosed * :let g:triggered+=1 | :bdelete 2')
@@ -333,6 +354,87 @@ describe('autocmd', function()
pcall_err(command, "call nvim_set_current_win(g:winid)"))
end)
+ it("`aucmd_win` cannot be changed into a normal window #13699", function()
+ local screen = Screen.new(50, 10)
+ screen:attach()
+ screen:set_default_attr_ids {
+ [1] = {bold = true, foreground = Screen.colors.Blue1},
+ [2] = {reverse = true},
+ [3] = {bold = true, reverse = true},
+ }
+
+ -- Create specific layout and ensure it's left unchanged.
+ -- Use nvim_buf_call on a hidden buffer so aucmd_win is used.
+ exec_lua [[
+ vim.cmd "wincmd s | wincmd _"
+ _G.buf = vim.api.nvim_create_buf(true, true)
+ vim.api.nvim_buf_call(_G.buf, function() vim.cmd "wincmd J" end)
+ ]]
+ screen:expect [[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {3:[No Name] }|
+ |
+ {2:[No Name] }|
+ |
+ ]]
+ -- This used to crash after making aucmd_win a normal window via the above.
+ exec_lua [[
+ vim.cmd "tabnew | tabclose # | wincmd s | wincmd _"
+ vim.api.nvim_buf_call(_G.buf, function() vim.cmd "wincmd K" end)
+ ]]
+ assert_alive()
+ screen:expect_unchanged()
+
+ -- Ensure splitting still works from inside the aucmd_win.
+ exec_lua [[vim.api.nvim_buf_call(_G.buf, function() vim.cmd "split" end)]]
+ screen:expect [[
+ ^ |
+ {1:~ }|
+ {3:[No Name] }|
+ |
+ {1:~ }|
+ {2:[Scratch] }|
+ |
+ {1:~ }|
+ {2:[No Name] }|
+ |
+ ]]
+
+ -- After all of our messing around, aucmd_win should still be floating.
+ -- Use :only to ensure _G.buf is hidden again (so the aucmd_win is used).
+ eq("editor", exec_lua [[
+ vim.cmd "only"
+ vim.api.nvim_buf_call(_G.buf, function()
+ _G.config = vim.api.nvim_win_get_config(0)
+ end)
+ return _G.config.relative
+ ]])
+ end)
+
+ describe('closing last non-floating window in tab from `aucmd_win`', function()
+ before_each(function()
+ command('edit Xa.txt')
+ command('tabnew Xb.txt')
+ command('autocmd BufAdd Xa.txt 1close')
+ end)
+
+ it('gives E814 when there are no other floating windows', function()
+ eq('Vim(close):E814: Cannot close window, only autocmd window would remain',
+ pcall_err(command, 'doautoall BufAdd'))
+ end)
+
+ it('gives E814 when there are other floating windows', function()
+ meths.open_win(0, true, {width = 10, height = 10, relative = 'editor', row = 10, col = 10})
+ eq('Vim(close):E814: Cannot close window, only autocmd window would remain',
+ pcall_err(command, 'doautoall BufAdd'))
+ end)
+ end)
+
it(':doautocmd does not warn "No matching autocommands" #10689', function()
local screen = Screen.new(32, 3)
screen:attach()
@@ -354,4 +456,125 @@ describe('autocmd', function()
:doautocmd SessionLoadPost |
]]}
end)
+
+ describe('v:event is readonly #18063', function()
+ it('during ChanOpen event', function()
+ command('autocmd ChanOpen * let v:event.info.id = 0')
+ funcs.jobstart({'cat'})
+ retry(nil, nil, function()
+ eq('E46: Cannot change read-only variable "v:event.info"', meths.get_vvar('errmsg'))
+ end)
+ end)
+
+ it('during ChanOpen event', function()
+ command('autocmd ChanInfo * let v:event.info.id = 0')
+ meths.set_client_info('foo', {}, 'remote', {}, {})
+ retry(nil, nil, function()
+ eq('E46: Cannot change read-only variable "v:event.info"', meths.get_vvar('errmsg'))
+ end)
+ end)
+
+ it('during RecordingLeave event', function()
+ command([[autocmd RecordingLeave * let v:event.regname = '']])
+ eq('Vim(let):E46: Cannot change read-only variable "v:event.regname"',
+ pcall_err(command, 'normal! qqq'))
+ end)
+
+ it('during TermClose event', function()
+ command('autocmd TermClose * let v:event.status = 0')
+ command('terminal')
+ eq('Vim(let):E46: Cannot change read-only variable "v:event.status"',
+ pcall_err(command, 'bdelete!'))
+ end)
+ end)
+
+ describe('old_tests', function()
+ it('vimscript: WinNew ++once', function()
+ source [[
+ " Without ++once WinNew triggers twice
+ let g:did_split = 0
+ augroup Testing
+ au!
+ au WinNew * let g:did_split += 1
+ augroup END
+ split
+ split
+ call assert_equal(2, g:did_split)
+ call assert_true(exists('#WinNew'))
+ close
+ close
+
+ " With ++once WinNew triggers once
+ let g:did_split = 0
+ augroup Testing
+ au!
+ au WinNew * ++once let g:did_split += 1
+ augroup END
+ split
+ split
+ call assert_equal(1, g:did_split)
+ call assert_false(exists('#WinNew'))
+ close
+ close
+
+ call assert_fails('au WinNew * ++once ++once echo bad', 'E983:')
+ ]]
+
+ meths.set_var('did_split', 0)
+
+ source [[
+ augroup Testing
+ au!
+ au WinNew * let g:did_split += 1
+ augroup END
+
+ split
+ split
+ ]]
+
+ eq(2, meths.get_var('did_split'))
+ eq(1, funcs.exists('#WinNew'))
+
+ -- Now with once
+ meths.set_var('did_split', 0)
+
+ source [[
+ augroup Testing
+ au!
+ au WinNew * ++once let g:did_split += 1
+ augroup END
+
+ split
+ split
+ ]]
+
+ eq(1, meths.get_var('did_split'))
+ eq(0, funcs.exists('#WinNew'))
+
+ -- call assert_fails('au WinNew * ++once ++once echo bad', 'E983:')
+ local ok, msg = pcall(source, [[
+ au WinNew * ++once ++once echo bad
+ ]])
+
+ eq(false, ok)
+ eq(true, not not string.find(msg, 'E983:'))
+ end)
+
+ it('should have autocmds in filetypedetect group', function()
+ source [[filetype on]]
+ neq({}, meths.get_autocmds { group = "filetypedetect" })
+ end)
+
+ it('should allow comma-separated patterns', function()
+ source [[
+ augroup TestingPatterns
+ au!
+ autocmd BufReadCmd *.shada,*.shada.tmp.[a-z] echo 'hello'
+ autocmd BufReadCmd *.shada,*.shada.tmp.[a-z] echo 'hello'
+ augroup END
+ ]]
+
+ eq(4, #meths.get_autocmds { event = "BufReadCmd", group = "TestingPatterns" })
+ end)
+ end)
end)
diff --git a/test/functional/autocmd/cursormoved_spec.lua b/test/functional/autocmd/cursormoved_spec.lua
index 9641d4b096..85d8628d7e 100644
--- a/test/functional/autocmd/cursormoved_spec.lua
+++ b/test/functional/autocmd/cursormoved_spec.lua
@@ -35,6 +35,7 @@ describe('CursorMoved', function()
it("is not triggered by cursor movement prior to first CursorMoved instantiation", function()
source([[
let g:cursormoved = 0
+ autocmd! CursorMoved
autocmd CursorMoved * let g:cursormoved += 1
]])
eq(0, eval('g:cursormoved'))
diff --git a/test/functional/autocmd/dirchanged_spec.lua b/test/functional/autocmd/dirchanged_spec.lua
index f4a1642ebf..45dc06b39b 100644
--- a/test/functional/autocmd/dirchanged_spec.lua
+++ b/test/functional/autocmd/dirchanged_spec.lua
@@ -8,7 +8,7 @@ local eval = h.eval
local request = h.request
local iswin = h.iswin
-describe('autocmd DirChanged', function()
+describe('autocmd DirChanged and DirChangedPre', function()
local curdir = string.gsub(lfs.currentdir(), '\\', '/')
local dirs = {
curdir .. '/Xtest-functional-autocmd-dirchanged.dir1',
@@ -26,31 +26,43 @@ describe('autocmd DirChanged', function()
before_each(function()
clear()
+ command('autocmd DirChangedPre * let [g:evpre, g:amatchpre, g:cdprecount] '
+ ..'= [copy(v:event), expand("<amatch>"), 1 + get(g:, "cdprecount", 0)]')
command('autocmd DirChanged * let [g:getcwd, g:ev, g:amatch, g:cdcount] '
- ..' = [getcwd(), copy(v:event), expand("<amatch>"), 1 + get(g:, "cdcount", 0)]')
+ ..'= [getcwd(), copy(v:event), expand("<amatch>"), 1 + get(g:, "cdcount", 0)]')
-- Normalize path separators.
+ command([[autocmd DirChangedPre * let g:evpre['directory'] = substitute(g:evpre['directory'], '\\', '/', 'g')]])
command([[autocmd DirChanged * let g:ev['cwd'] = substitute(g:ev['cwd'], '\\', '/', 'g')]])
- command([[autocmd DirChanged * let g:getcwd = substitute(g:getcwd, '\\', '/', 'g')]])
+ command([[autocmd DirChanged * let g:getcwd = substitute(g:getcwd, '\\', '/', 'g')]])
end)
- it('sets v:event and <amatch>', function()
+ it('set v:event and <amatch>', function()
command('lcd '..dirs[1])
+ eq({directory=dirs[1], scope='window', changed_window=false}, eval('g:evpre'))
eq({cwd=dirs[1], scope='window', changed_window=false}, eval('g:ev'))
+ eq('window', eval('g:amatchpre'))
eq('window', eval('g:amatch'))
+ eq(1, eval('g:cdprecount'))
eq(1, eval('g:cdcount'))
command('tcd '..dirs[2])
+ eq({directory=dirs[2], scope='tabpage', changed_window=false}, eval('g:evpre'))
eq({cwd=dirs[2], scope='tabpage', changed_window=false}, eval('g:ev'))
+ eq('tabpage', eval('g:amatchpre'))
eq('tabpage', eval('g:amatch'))
+ eq(2, eval('g:cdprecount'))
eq(2, eval('g:cdcount'))
command('cd '..dirs[3])
+ eq({directory=dirs[3], scope='global', changed_window=false}, eval('g:evpre'))
eq({cwd=dirs[3], scope='global', changed_window=false}, eval('g:ev'))
+ eq('global', eval('g:amatchpre'))
eq('global', eval('g:amatch'))
+ eq(3, eval('g:cdprecount'))
eq(3, eval('g:cdcount'))
end)
- it('sets getcwd() during event #6260', function()
+ it('DirChanged set getcwd() during event #6260', function()
command('lcd '..dirs[1])
eq(dirs[1], eval('g:getcwd'))
@@ -61,7 +73,7 @@ describe('autocmd DirChanged', function()
eq(dirs[3], eval('g:getcwd'))
end)
- it('disallows recursion', function()
+ it('disallow recursion', function()
command('set shellslash')
-- Set up a _nested_ handler.
command('autocmd DirChanged * nested lcd '..dirs[3])
@@ -72,23 +84,36 @@ describe('autocmd DirChanged', function()
eq(dirs[3], eval('getcwd()'))
end)
- it('does not trigger if :cd fails', function()
+ it('only DirChangedPre is triggered if :cd fails', function()
command('let g:ev = {}')
+ command('let g:cdcount = 0')
local status1, err1 = pcall(function()
- command('lcd '..dirs[1] .. '/doesnotexist')
+ command('lcd '..dirs[1]..'/doesnotexist')
end)
+ eq({directory=dirs[1]..'/doesnotexist', scope='window', changed_window=false}, eval('g:evpre'))
eq({}, eval('g:ev'))
+ eq('window', eval('g:amatchpre'))
+ eq(1, eval('g:cdprecount'))
+ eq(0, eval('g:cdcount'))
local status2, err2 = pcall(function()
- command('lcd '..dirs[2] .. '/doesnotexist')
+ command('lcd '..dirs[2]..'/doesnotexist')
end)
+ eq({directory=dirs[2]..'/doesnotexist', scope='window', changed_window=false}, eval('g:evpre'))
eq({}, eval('g:ev'))
+ eq('window', eval('g:amatchpre'))
+ eq(2, eval('g:cdprecount'))
+ eq(0, eval('g:cdcount'))
local status3, err3 = pcall(function()
- command('lcd '..dirs[3] .. '/doesnotexist')
+ command('lcd '..dirs[3]..'/doesnotexist')
end)
+ eq({directory=dirs[3]..'/doesnotexist', scope='window', changed_window=false}, eval('g:evpre'))
eq({}, eval('g:ev'))
+ eq('window', eval('g:amatchpre'))
+ eq(3, eval('g:cdprecount'))
+ eq(0, eval('g:cdcount'))
eq(false, status1)
eq(false, status2)
@@ -99,85 +124,121 @@ describe('autocmd DirChanged', function()
eq('E344:', string.match(err3, "E%d*:"))
end)
- it("is triggered by 'autochdir'", function()
+ it("are triggered by 'autochdir'", function()
command('set autochdir')
command('split '..dirs[1]..'/foo')
+ eq({directory=dirs[1], scope='window', changed_window=false}, eval('g:evpre'))
eq({cwd=dirs[1], scope='window', changed_window=false}, eval('g:ev'))
+ eq('auto', eval('g:amatchpre'))
eq('auto', eval('g:amatch'))
+ eq(1, eval('g:cdprecount'))
+ eq(1, eval('g:cdcount'))
command('split '..dirs[2]..'/bar')
+ eq({directory=dirs[2], scope='window', changed_window=false}, eval('g:evpre'))
eq({cwd=dirs[2], scope='window', changed_window=false}, eval('g:ev'))
eq('auto', eval('g:amatch'))
-
+ eq(2, eval('g:cdprecount'))
eq(2, eval('g:cdcount'))
end)
- it('does not trigger if directory has not changed', function()
+ it('do not trigger if directory has not changed', function()
command('lcd '..dirs[1])
+ eq({directory=dirs[1], scope='window', changed_window=false}, eval('g:evpre'))
eq({cwd=dirs[1], scope='window', changed_window=false}, eval('g:ev'))
+ eq('window', eval('g:amatchpre'))
eq('window', eval('g:amatch'))
+ eq(1, eval('g:cdprecount'))
eq(1, eval('g:cdcount'))
+ command('let g:evpre = {}')
command('let g:ev = {}')
command('lcd '..dirs[1])
+ eq({}, eval('g:evpre'))
eq({}, eval('g:ev'))
+ eq(1, eval('g:cdprecount'))
eq(1, eval('g:cdcount'))
if iswin() then
command('lcd '..win_dirs[1])
+ eq({}, eval('g:evpre'))
eq({}, eval('g:ev'))
+ eq(1, eval('g:cdprecount'))
eq(1, eval('g:cdcount'))
end
command('tcd '..dirs[2])
+ eq({directory=dirs[2], scope='tabpage', changed_window=false}, eval('g:evpre'))
eq({cwd=dirs[2], scope='tabpage', changed_window=false}, eval('g:ev'))
+ eq('tabpage', eval('g:amatchpre'))
eq('tabpage', eval('g:amatch'))
+ eq(2, eval('g:cdprecount'))
eq(2, eval('g:cdcount'))
+ command('let g:evpre = {}')
command('let g:ev = {}')
command('tcd '..dirs[2])
+ eq({}, eval('g:evpre'))
eq({}, eval('g:ev'))
+ eq(2, eval('g:cdprecount'))
eq(2, eval('g:cdcount'))
if iswin() then
command('tcd '..win_dirs[2])
+ eq({}, eval('g:evpre'))
eq({}, eval('g:ev'))
+ eq(2, eval('g:cdprecount'))
eq(2, eval('g:cdcount'))
end
command('cd '..dirs[3])
+ eq({directory=dirs[3], scope='global', changed_window=false}, eval('g:evpre'))
eq({cwd=dirs[3], scope='global', changed_window=false}, eval('g:ev'))
eq('global', eval('g:amatch'))
+ eq(3, eval('g:cdprecount'))
eq(3, eval('g:cdcount'))
+ command('let g:evpre = {}')
command('let g:ev = {}')
command('cd '..dirs[3])
+ eq({}, eval('g:evpre'))
eq({}, eval('g:ev'))
+ eq(3, eval('g:cdprecount'))
eq(3, eval('g:cdcount'))
if iswin() then
command('cd '..win_dirs[3])
+ eq({}, eval('g:evpre'))
eq({}, eval('g:ev'))
+ eq(3, eval('g:cdprecount'))
eq(3, eval('g:cdcount'))
end
command('set autochdir')
command('split '..dirs[1]..'/foo')
+ eq({directory=dirs[1], scope='window', changed_window=false}, eval('g:evpre'))
eq({cwd=dirs[1], scope='window', changed_window=false}, eval('g:ev'))
+ eq('auto', eval('g:amatchpre'))
eq('auto', eval('g:amatch'))
+ eq(4, eval('g:cdprecount'))
eq(4, eval('g:cdcount'))
+ command('let g:evpre = {}')
command('let g:ev = {}')
command('split '..dirs[1]..'/bar')
+ eq({}, eval('g:evpre'))
eq({}, eval('g:ev'))
+ eq(4, eval('g:cdprecount'))
eq(4, eval('g:cdcount'))
if iswin() then
command('split '..win_dirs[1]..'/baz')
+ eq({}, eval('g:evpre'))
eq({}, eval('g:ev'))
+ eq(4, eval('g:cdprecount'))
eq(4, eval('g:cdcount'))
end
end)
- it("is triggered by switching to win/tab with different CWD #6054", function()
+ it("are triggered by switching to win/tab with different CWD #6054", function()
command('lcd '..dirs[3]) -- window 3
command('split '..dirs[2]..'/foo') -- window 2
command('lcd '..dirs[2])
@@ -185,72 +246,105 @@ describe('autocmd DirChanged', function()
command('lcd '..dirs[1])
command('2wincmd w') -- window 2
+ eq({directory=dirs[2], scope='window', changed_window=true}, eval('g:evpre'))
eq({cwd=dirs[2], scope='window', changed_window=true}, eval('g:ev'))
+ eq('window', eval('g:amatchpre'))
eq('window', eval('g:amatch'))
+ eq(4, eval('g:cdprecount'))
eq(4, eval('g:cdcount'))
command('tabnew') -- tab 2 (tab-local CWD)
+ eq(4, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
eq(4, eval('g:cdcount')) -- same CWD, no DirChanged event
command('tcd '..dirs[3])
command('tabnext') -- tab 1 (no tab-local CWD)
+ eq({directory=dirs[2], scope='window', changed_window=true}, eval('g:evpre'))
eq({cwd=dirs[2], scope='window', changed_window=true}, eval('g:ev'))
+ eq('window', eval('g:amatchpre'))
eq('window', eval('g:amatch'))
command('tabnext') -- tab 2
+ eq({directory=dirs[3], scope='tabpage', changed_window=true}, eval('g:evpre'))
eq({cwd=dirs[3], scope='tabpage', changed_window=true}, eval('g:ev'))
+ eq('tabpage', eval('g:amatchpre'))
eq('tabpage', eval('g:amatch'))
+ eq(7, eval('g:cdprecount'))
eq(7, eval('g:cdcount'))
command('tabnext') -- tab 1
command('3wincmd w') -- window 3
+ eq(9, eval('g:cdprecount'))
eq(9, eval('g:cdcount'))
command('tabnext') -- tab 2 (has the *same* CWD)
+ eq(9, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
if iswin() then
command('tabnew') -- tab 3
+ eq(9, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
command('tcd '..win_dirs[3])
+ eq(9, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
command('tabnext') -- tab 1
+ eq(9, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
command('tabprevious') -- tab 3
+ eq(9, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
command('tabprevious') -- tab 2
+ eq(9, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
command('tabprevious') -- tab 1
+ eq(9, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
command('lcd '..win_dirs[3]) -- window 3
+ eq(9, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
command('tabnext') -- tab 2
+ eq(9, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
command('tabnext') -- tab 3
+ eq(9, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
command('tabnext') -- tab 1
+ eq(9, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
command('tabprevious') -- tab 3
+ eq(9, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
end
end)
- it('is triggered by nvim_set_current_dir()', function()
+ it('are triggered by nvim_set_current_dir()', function()
request('nvim_set_current_dir', dirs[1])
+ eq({directory=dirs[1], scope='global', changed_window=false}, eval('g:evpre'))
eq({cwd=dirs[1], scope='global', changed_window=false}, eval('g:ev'))
+ eq(1, eval('g:cdprecount'))
+ eq(1, eval('g:cdcount'))
request('nvim_set_current_dir', dirs[2])
+ eq({directory=dirs[2], scope='global', changed_window=false}, eval('g:evpre'))
eq({cwd=dirs[2], scope='global', changed_window=false}, eval('g:ev'))
+ eq(2, eval('g:cdprecount'))
+ eq(2, eval('g:cdcount'))
local status, err = pcall(function()
request('nvim_set_current_dir', '/doesnotexist')
end)
eq(false, status)
eq('Failed to change directory', string.match(err, ': (.*)'))
- eq({cwd=dirs[2], scope='global', changed_window=false}, eval('g:ev'))
+ eq({directory='/doesnotexist', scope='global', changed_window=false}, eval('g:evpre'))
+ eq(3, eval('g:cdprecount'))
+ eq(2, eval('g:cdcount'))
end)
- it('works when local to buffer', function()
+ it('work when local to buffer', function()
+ command('let g:triggeredpre = 0')
command('let g:triggered = 0')
+ command('autocmd DirChangedPre <buffer> let g:triggeredpre = 1')
command('autocmd DirChanged <buffer> let g:triggered = 1')
command('cd '..dirs[1])
+ eq(1, eval('g:triggeredpre'))
eq(1, eval('g:triggered'))
end)
end)
diff --git a/test/functional/autocmd/focus_spec.lua b/test/functional/autocmd/focus_spec.lua
index 3f9a0ad09b..e3c9e1f9ee 100644
--- a/test/functional/autocmd/focus_spec.lua
+++ b/test/functional/autocmd/focus_spec.lua
@@ -56,7 +56,7 @@ describe('autoread TUI FocusGained/FocusLost', function()
line 3 |
line 4 |
{5:xtest-foo }|
- "xtest-foo" 4L, 28C |
+ "xtest-foo" 4L, 28B |
{3:-- TERMINAL --} |
]]}
end)
diff --git a/test/functional/autocmd/show_spec.lua b/test/functional/autocmd/show_spec.lua
new file mode 100644
index 0000000000..505bed834b
--- /dev/null
+++ b/test/functional/autocmd/show_spec.lua
@@ -0,0 +1,183 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+
+local clear = helpers.clear
+local command = helpers.command
+local dedent = helpers.dedent
+local eq = helpers.eq
+local funcs = helpers.funcs
+local eval = helpers.eval
+local exec = helpers.exec
+local feed = helpers.feed
+
+describe(":autocmd", function()
+ before_each(function()
+ clear({'-u', 'NONE'})
+ end)
+
+ it("should not segfault when you just do autocmd", function()
+ command ":autocmd"
+ end)
+
+ it("should filter based on ++once", function()
+ command "autocmd! BufEnter"
+ command "autocmd BufEnter * :echo 'Hello'"
+ command [[augroup TestingOne]]
+ command [[ autocmd BufEnter * :echo "Line 1"]]
+ command [[ autocmd BufEnter * :echo "Line 2"]]
+ command [[augroup END]]
+
+ eq(dedent([[
+
+ --- Autocommands ---
+ BufEnter
+ * :echo 'Hello'
+ TestingOne BufEnter
+ * :echo "Line 1"
+ :echo "Line 2"]]),
+ funcs.execute('autocmd BufEnter'))
+ end)
+
+ it('should not show group information if interrupted', function()
+ local screen = Screen.new(50, 6)
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue1}, -- NonText
+ [2] = {bold = true, foreground = Screen.colors.SeaGreen}, -- MoreMsg
+ [3] = {bold = true, foreground = Screen.colors.Magenta}, -- Title
+ })
+ screen:attach()
+ exec([[
+ set more
+ autocmd! BufEnter
+ augroup test_1
+ autocmd BufEnter A echo 'A'
+ autocmd BufEnter B echo 'B'
+ autocmd BufEnter C echo 'C'
+ autocmd BufEnter D echo 'D'
+ autocmd BufEnter E echo 'E'
+ autocmd BufEnter F echo 'F'
+ augroup END
+ autocmd! BufLeave
+ augroup test_1
+ autocmd BufLeave A echo 'A'
+ autocmd BufLeave B echo 'B'
+ autocmd BufLeave C echo 'C'
+ autocmd BufLeave D echo 'D'
+ autocmd BufLeave E echo 'E'
+ autocmd BufLeave F echo 'F'
+ augroup END
+ ]])
+ feed(':autocmd<CR>')
+ screen:expect([[
+ :autocmd |
+ {3:--- Autocommands ---} |
+ {3:test_1} {3:BufEnter} |
+ A echo 'A' |
+ B echo 'B' |
+ {2:-- More --}^ |
+ ]])
+ feed('q')
+ screen:expect([[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ end)
+
+ it('should not show group information for deleted pattern', function()
+ exec([[
+ autocmd! BufEnter
+ augroup test_1
+ autocmd BufEnter A echo 'A'
+ autocmd BufEnter B echo 'B'
+ autocmd BufEnter C echo 'C'
+ augroup END
+ augroup test_2
+ autocmd BufEnter foo echo 'foo'
+ augroup END
+ augroup test_3
+ autocmd BufEnter D echo 'D'
+ autocmd BufEnter E echo 'E'
+ autocmd BufEnter F echo 'F'
+ augroup END
+
+ func Func()
+ autocmd! test_2 BufEnter
+ let g:output = execute('autocmd BufEnter')
+ endfunc
+
+ autocmd User foo call Func()
+ doautocmd User foo
+ ]])
+ eq(dedent([[
+
+ --- Autocommands ---
+ test_1 BufEnter
+ A echo 'A'
+ B echo 'B'
+ C echo 'C'
+ test_3 BufEnter
+ D echo 'D'
+ E echo 'E'
+ F echo 'F']]), eval('g:output'))
+ end)
+
+ it('can filter by pattern #17973', function()
+ exec([[
+ autocmd! BufEnter
+ autocmd! User
+ augroup test_1
+ autocmd BufEnter A echo "A1"
+ autocmd BufEnter B echo "B1"
+ autocmd User A echo "A1"
+ autocmd User B echo "B1"
+ augroup END
+ augroup test_2
+ autocmd BufEnter A echo "A2"
+ autocmd BufEnter B echo "B2"
+ autocmd User A echo "A2"
+ autocmd User B echo "B2"
+ augroup END
+ augroup test_3
+ autocmd BufEnter A echo "A3"
+ autocmd BufEnter B echo "B3"
+ autocmd User A echo "A3"
+ autocmd User B echo "B3"
+ augroup END
+ ]])
+ eq(dedent([[
+
+ --- Autocommands ---
+ test_1 User
+ A echo "A1"
+ test_2 User
+ A echo "A2"
+ test_3 User
+ A echo "A3"]]), funcs.execute('autocmd User A'))
+ eq(dedent([[
+
+ --- Autocommands ---
+ test_1 BufEnter
+ B echo "B1"
+ test_2 BufEnter
+ B echo "B2"
+ test_3 BufEnter
+ B echo "B3"
+ test_1 User
+ B echo "B1"
+ test_2 User
+ B echo "B2"
+ test_3 User
+ B echo "B3"]]), funcs.execute('autocmd * B'))
+ eq(dedent([[
+
+ --- Autocommands ---
+ test_3 BufEnter
+ B echo "B3"
+ test_3 User
+ B echo "B3"]]), funcs.execute('autocmd test_3 * B'))
+ end)
+end)
diff --git a/test/functional/autocmd/signal_spec.lua b/test/functional/autocmd/signal_spec.lua
index 719adeaf1b..d4f65cc61d 100644
--- a/test/functional/autocmd/signal_spec.lua
+++ b/test/functional/autocmd/signal_spec.lua
@@ -30,6 +30,12 @@ describe('autocmd Signal', function()
eq({'notification', 'foo', {}}, next_msg())
end)
+ it('matches SIGWINCH', function()
+ command('autocmd Signal SIGWINCH call rpcnotify(1, "foo")')
+ posix_kill('WINCH', funcs.getpid())
+ eq({'notification', 'foo', {}}, next_msg())
+ end)
+
it('does not match unknown patterns', function()
command('autocmd Signal SIGUSR2 call rpcnotify(1, "foo")')
posix_kill('USR1', funcs.getpid())
diff --git a/test/functional/autocmd/termxx_spec.lua b/test/functional/autocmd/termxx_spec.lua
index 1e8f981437..859c2ebf44 100644
--- a/test/functional/autocmd/termxx_spec.lua
+++ b/test/functional/autocmd/termxx_spec.lua
@@ -1,21 +1,41 @@
local luv = require('luv')
local helpers = require('test.functional.helpers')(after_each)
-local clear, command, nvim, nvim_dir =
- helpers.clear, helpers.command, helpers.nvim, helpers.nvim_dir
+local clear, command, nvim, testprg =
+ helpers.clear, helpers.command, helpers.nvim, helpers.testprg
local eval, eq, neq, retry =
helpers.eval, helpers.eq, helpers.neq, helpers.retry
local ok = helpers.ok
local feed = helpers.feed
+local pcall_err = helpers.pcall_err
+local assert_alive = helpers.assert_alive
local iswin = helpers.iswin
describe('autocmd TermClose', function()
before_each(function()
clear()
- nvim('set_option', 'shell', nvim_dir .. '/shell-test')
+ nvim('set_option', 'shell', testprg('shell-test'))
command('set shellcmdflag=EXE shellredir= shellpipe= shellquote= shellxquote=')
end)
+
+ local function test_termclose_delete_own_buf()
+ command('autocmd TermClose * bdelete!')
+ command('terminal')
+ eq('Vim(bdelete):E937: Attempt to delete a buffer that is in use', pcall_err(command, 'bdelete!'))
+ assert_alive()
+ end
+
+ -- TODO: fixed after merging patches for `can_unload_buffer`?
+ pending('TermClose deleting its own buffer, altbuf = buffer 1 #10386', function()
+ test_termclose_delete_own_buf()
+ end)
+
+ it('TermClose deleting its own buffer, altbuf NOT buffer 1 #10386', function()
+ command('edit foo1')
+ test_termclose_delete_own_buf()
+ end)
+
it('triggers when fast-exiting terminal job stops', function()
command('autocmd TermClose * let g:test_termclose = 23')
command('terminal')
diff --git a/test/functional/autocmd/winscrolled_spec.lua b/test/functional/autocmd/winscrolled_spec.lua
index 1ef5a37479..5c1b758961 100644
--- a/test/functional/autocmd/winscrolled_spec.lua
+++ b/test/functional/autocmd/winscrolled_spec.lua
@@ -3,60 +3,83 @@ local helpers = require('test.functional.helpers')(after_each)
local clear = helpers.clear
local eq = helpers.eq
local eval = helpers.eval
-local source = helpers.source
+local command = helpers.command
+local feed = helpers.feed
+local meths = helpers.meths
+local assert_alive = helpers.assert_alive
+
+before_each(clear)
describe('WinScrolled', function()
- before_each(clear)
+ local win_id
+
+ before_each(function()
+ win_id = meths.get_current_win().id
+ command(string.format('autocmd WinScrolled %d let g:matched = v:true', win_id))
+ command('let g:scrolled = 0')
+ command('autocmd WinScrolled * let g:scrolled += 1')
+ command([[autocmd WinScrolled * let g:amatch = str2nr(expand('<amatch>'))]])
+ command([[autocmd WinScrolled * let g:afile = str2nr(expand('<afile>'))]])
+ end)
+
+ after_each(function()
+ eq(true, eval('g:matched'))
+ eq(win_id, eval('g:amatch'))
+ eq(win_id, eval('g:afile'))
+ end)
it('is triggered by scrolling vertically', function()
- source([[
- set nowrap
- let width = winwidth(0)
- let line = '123' . repeat('*', width * 2)
- let lines = [line, line]
- call nvim_buf_set_lines(0, 0, -1, v:true, lines)
-
- let g:scrolled = 0
- autocmd WinScrolled * let g:scrolled += 1
- execute "normal! \<C-e>"
- ]])
+ local lines = {'123', '123'}
+ meths.buf_set_lines(0, 0, -1, true, lines)
+ eq(0, eval('g:scrolled'))
+ feed('<C-E>')
eq(1, eval('g:scrolled'))
end)
it('is triggered by scrolling horizontally', function()
- source([[
- set nowrap
- let width = winwidth(0)
- let line = '123' . repeat('*', width * 2)
- let lines = [line, line]
- call nvim_buf_set_lines(0, 0, -1, v:true, lines)
-
- let g:scrolled = 0
- autocmd WinScrolled * let g:scrolled += 1
- execute "normal! zl"
- ]])
+ command('set nowrap')
+ local width = meths.win_get_width(0)
+ local line = '123' .. ('*'):rep(width * 2)
+ local lines = {line, line}
+ meths.buf_set_lines(0, 0, -1, true, lines)
+ eq(0, eval('g:scrolled'))
+ feed('zl')
eq(1, eval('g:scrolled'))
end)
- it('is triggered when the window scrolls in insert mode', function()
- source([[
- let height = winheight(0)
- let lines = map(range(height * 2), {_, i -> string(i)})
- call nvim_buf_set_lines(0, 0, -1, v:true, lines)
-
- let g:scrolled = 0
- autocmd WinScrolled * let g:scrolled += 1
- call feedkeys("LA\<CR><Esc>", "n")
- ]])
+ it('is triggered by horizontal scrolling from cursor move', function()
+ command('set nowrap')
+ local lines = {'', '', 'Foo'}
+ meths.buf_set_lines(0, 0, -1, true, lines)
+ meths.win_set_cursor(0, {3, 0})
+ eq(0, eval('g:scrolled'))
+ feed('zl')
+ eq(1, eval('g:scrolled'))
+ feed('zl')
eq(2, eval('g:scrolled'))
+ feed('h')
+ eq(3, eval('g:scrolled'))
end)
- it('is triggered when the window is resized', function()
- source([[
- let g:scrolled = 0
- autocmd WinScrolled * let g:scrolled += 1
- wincmd v
- ]])
+ it('is triggered when the window scrolls in Insert mode', function()
+ local height = meths.win_get_height(0)
+ local lines = {}
+ for i = 1, height * 2 do
+ lines[i] = tostring(i)
+ end
+ meths.buf_set_lines(0, 0, -1, true, lines)
+ feed('L')
+ eq(0, eval('g:scrolled'))
+ feed('A<CR><Esc>')
eq(1, eval('g:scrolled'))
end)
end)
+
+it('closing window in WinScrolled does not cause use-after-free #13265', function()
+ local lines = {'aaa', 'bbb'}
+ meths.buf_set_lines(0, 0, -1, true, lines)
+ command('vsplit')
+ command('autocmd WinScrolled * close')
+ feed('<C-E>')
+ assert_alive()
+end)
diff --git a/test/functional/core/channels_spec.lua b/test/functional/core/channels_spec.lua
index 93dec9fb35..ca52404d3b 100644
--- a/test/functional/core/channels_spec.lua
+++ b/test/functional/core/channels_spec.lua
@@ -10,6 +10,8 @@ local nvim_prog = helpers.nvim_prog
local is_os = helpers.is_os
local retry = helpers.retry
local expect_twostreams = helpers.expect_twostreams
+local assert_alive = helpers.assert_alive
+local pcall_err = helpers.pcall_err
describe('channels', function()
local init = [[
@@ -100,6 +102,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}
@@ -282,3 +316,22 @@ describe('channels', function()
eq({"notification", "exit", {id, 1, {''}}}, next_msg())
end)
end)
+
+describe('loopback', function()
+ before_each(function()
+ clear()
+ command("let chan = sockconnect('pipe', v:servername, {'rpc': v:true})")
+ end)
+
+ it('does not crash when sending raw data', function()
+ eq("Vim(call):Can't send raw data to rpc channel",
+ pcall_err(command, "call chansend(chan, 'test')"))
+ assert_alive()
+ end)
+
+ it('are released when closed', function()
+ local chans = eval('len(nvim_list_chans())')
+ command('call chanclose(chan)')
+ eq(chans - 1, eval('len(nvim_list_chans())'))
+ end)
+end)
diff --git a/test/functional/core/fileio_spec.lua b/test/functional/core/fileio_spec.lua
index c68bc18eed..a4d22685e8 100644
--- a/test/functional/core/fileio_spec.lua
+++ b/test/functional/core/fileio_spec.lua
@@ -1,27 +1,34 @@
local helpers = require('test.functional.helpers')(after_each)
+local assert_log = helpers.assert_log
+local assert_nolog = helpers.assert_nolog
local clear = helpers.clear
local command = helpers.command
local eq = helpers.eq
+local ok = helpers.ok
local feed = helpers.feed
local funcs = helpers.funcs
local nvim_prog = helpers.nvim_prog
local request = helpers.request
local retry = helpers.retry
local rmdir = helpers.rmdir
+local matches = helpers.matches
local mkdir = helpers.mkdir
local sleep = helpers.sleep
local read_file = helpers.read_file
+local tmpname = helpers.tmpname
local trim = helpers.trim
local currentdir = helpers.funcs.getcwd
local iswin = helpers.iswin
local assert_alive = helpers.assert_alive
+local expect_exit = helpers.expect_exit
+local write_file = helpers.write_file
describe('fileio', function()
before_each(function()
end)
after_each(function()
- command(':qall!')
+ expect_exit(command, ':qall!')
os.remove('Xtest_startup_shada')
os.remove('Xtest_startup_file1')
os.remove('Xtest_startup_file1~')
@@ -139,3 +146,69 @@ describe('fileio', function()
end)
end)
+describe('tmpdir', function()
+ local tmproot_pat = [=[.*[/\\]nvim%.[^/\\]+]=]
+ local testlog = 'Xtest_tmpdir_log'
+ local faketmp
+
+ before_each(function()
+ -- Fake /tmp dir so that we can mess it up.
+ faketmp = tmpname()
+ os.remove(faketmp)
+ mkdir(faketmp)
+ end)
+
+ after_each(function()
+ expect_exit(command, ':qall!')
+ os.remove(testlog)
+ end)
+
+ it('failure modes', function()
+ clear({ env={ NVIM_LOG_FILE=testlog, TMPDIR=faketmp, } })
+ assert_nolog('tempdir is not a directory', testlog)
+ assert_nolog('tempdir has invalid permissions', testlog)
+
+ -- Tempfiles typically look like: "…/nvim.<user>/xxx/0".
+ -- - "…/nvim.<user>/xxx/" is the per-process tmpdir, not shared with other Nvims.
+ -- - "…/nvim.<user>/" is the tmpdir root, shared by all Nvims (normally).
+ local tmproot = (funcs.tempname()):match(tmproot_pat)
+ ok(tmproot:len() > 4, 'tmproot like "nvim.foo"', tmproot)
+
+ -- Test how Nvim handles invalid tmpdir root (by hostile users or accidents).
+ --
+ -- "…/nvim.<user>/" is not a directory:
+ expect_exit(command, ':qall!')
+ rmdir(tmproot)
+ write_file(tmproot, '') -- Not a directory, vim_mktempdir() should skip it.
+ clear({ env={ NVIM_LOG_FILE=testlog, TMPDIR=faketmp, } })
+ matches(tmproot_pat, funcs.stdpath('run')) -- Tickle vim_mktempdir().
+ -- Assert that broken tmpdir root was handled.
+ retry(nil, 1000, function()
+ assert_log('tempdir root not a directory', testlog, 100)
+ end)
+
+ -- "…/nvim.<user>/" has wrong permissions:
+ if iswin() then
+ return -- TODO(justinmk): need setfperm/getfperm on Windows. #8244
+ end
+ os.remove(testlog)
+ os.remove(tmproot)
+ mkdir(tmproot)
+ funcs.setfperm(tmproot, 'rwxr--r--') -- Invalid permissions, vim_mktempdir() should skip it.
+ clear({ env={ NVIM_LOG_FILE=testlog, TMPDIR=faketmp, } })
+ matches(tmproot_pat, funcs.stdpath('run')) -- Tickle vim_mktempdir().
+ -- Assert that broken tmpdir root was handled.
+ retry(nil, 1000, function()
+ assert_log('tempdir root has invalid permissions', testlog, 100)
+ end)
+ end)
+
+ it('too long', function()
+ local bigname = ('%s/%s'):format(faketmp, ('x'):rep(666))
+ mkdir(bigname)
+ clear({ env={ NVIM_LOG_FILE=testlog, TMPDIR=bigname, } })
+ matches(tmproot_pat, funcs.stdpath('run')) -- Tickle vim_mktempdir().
+ local len = (funcs.tempname()):len()
+ ok(len > 4 and len < 256, '4 < len < 256', tostring(len))
+ end)
+end)
diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua
index 5e127bce26..04fbb807be 100644
--- a/test/functional/core/job_spec.lua
+++ b/test/functional/core/job_spec.lua
@@ -1,9 +1,9 @@
local helpers = require('test.functional.helpers')(after_each)
local clear, eq, eval, exc_exec, feed_command, feed, insert, neq, next_msg, nvim,
- nvim_dir, ok, source, write_file, mkdir, rmdir = helpers.clear,
+ testprg, ok, source, write_file, mkdir, rmdir = helpers.clear,
helpers.eq, helpers.eval, helpers.exc_exec, helpers.feed_command, helpers.feed,
helpers.insert, helpers.neq, helpers.next_msg, helpers.nvim,
- helpers.nvim_dir, helpers.ok, helpers.source,
+ helpers.testprg, helpers.ok, helpers.source,
helpers.write_file, helpers.mkdir, helpers.rmdir
local assert_alive = helpers.assert_alive
local command = helpers.command
@@ -16,6 +16,7 @@ local poke_eventloop = helpers.poke_eventloop
local iswin = helpers.iswin
local get_pathsep = helpers.get_pathsep
local pathroot = helpers.pathroot
+local exec_lua = helpers.exec_lua
local nvim_set = helpers.nvim_set
local expect_twostreams = helpers.expect_twostreams
local expect_msg_seq = helpers.expect_msg_seq
@@ -72,12 +73,20 @@ describe('jobs', function()
nvim('command', [[call jobstart('echo $TOTO $VAR', g:job_opts)]])
end
- expect_msg_seq({
- {'notification', 'stdout', {0, {'hello world abc', ''}}},
- })
+ expect_msg_seq(
+ {
+ {'notification', 'stdout', {0, {'hello world abc'}}},
+ {'notification', 'stdout', {0, {'', ''}}},
+ },
+ {
+ {'notification', 'stdout', {0, {'hello world abc', ''}}},
+ {'notification', 'stdout', {0, {''}}}
+ }
+ )
end)
it('append environment with pty #env', function()
+ if helpers.pending_win32(pending) then return end
nvim('command', "let $VAR = 'abc'")
nvim('command', "let $TOTO = 'goodbye world'")
nvim('command', "let g:job_opts.pty = v:true")
@@ -87,9 +96,16 @@ describe('jobs', function()
else
nvim('command', [[call jobstart('echo $TOTO $VAR', g:job_opts)]])
end
- expect_msg_seq({
- {'notification', 'stdout', {0, {'hello world abc', ''}}},
- })
+ expect_msg_seq(
+ {
+ {'notification', 'stdout', {0, {'hello world abc'}}},
+ {'notification', 'stdout', {0, {'', ''}}},
+ },
+ {
+ {'notification', 'stdout', {0, {'hello world abc', ''}}},
+ {'notification', 'stdout', {0, {''}}}
+ }
+ )
end)
it('replace environment #env', function()
@@ -207,7 +223,7 @@ describe('jobs', function()
ok(string.find(err, "E475: Invalid argument: expected valid directory$") ~= nil)
end)
- it('produces error when using non-executable `cwd`', function()
+ it('error on non-executable `cwd`', function()
if iswin() then return end -- N/A for Windows
local dir = 'Xtest_not_executable_dir'
@@ -248,7 +264,7 @@ describe('jobs', function()
eq({'notification', 'exit', {0, 0}}, next_msg())
end)
- it('allows interactive commands', function()
+ it('interactive commands', function()
nvim('command', "let j = jobstart(['cat', '-'], g:job_opts)")
neq(0, eval('j'))
nvim('command', 'call jobsend(j, "abc\\n")')
@@ -294,13 +310,7 @@ describe('jobs', function()
nvim('command', "call jobstop(j)")
end)
- it("will not buffer data if it doesn't end in newlines", function()
- if helpers.isCI('travis') and os.getenv('CC') == 'gcc-4.9'
- and helpers.is_os('mac') then
- -- XXX: Hangs Travis macOS since e9061117a5b8f195c3f26a5cb94e18ddd7752d86.
- pending("[Hangs on Travis macOS. #5002]")
- end
-
+ it("emits partial lines (does NOT buffer data lacking newlines)", function()
nvim('command', "let j = jobstart(['cat', '-'], g:job_opts)")
nvim('command', 'call jobsend(j, "abc\\nxyz")')
eq({'notification', 'stdout', {0, {'abc', 'xyz'}}}, next_msg())
@@ -383,7 +393,7 @@ describe('jobs', function()
eq(NIL, meths.get_proc(pid))
end)
- it("do not survive the exit of nvim", function()
+ it("disposed on Nvim exit", function()
-- use sleep, which doesn't die on stdin close
nvim('command', "let g:j = jobstart(has('win32') ? ['ping', '-n', '1001', '127.0.0.1'] : ['sleep', '1000'], g:job_opts)")
local pid = eval('jobpid(g:j)')
@@ -651,6 +661,44 @@ describe('jobs', function()
)
end)
+ it('jobstart() environment: $NVIM, $NVIM_LISTEN_ADDRESS #11009', function()
+ local function get_env_in_child_job(envname, env)
+ return exec_lua([[
+ local envname, env = ...
+ local join = function(s) return vim.fn.join(s, '') end
+ local stdout = {}
+ local stderr = {}
+ local opt = {
+ env = env,
+ stdout_buffered = true,
+ stderr_buffered = true,
+ on_stderr = function(chan, data, name) stderr = data end,
+ on_stdout = function(chan, data, name) stdout = data end,
+ }
+ local j1 = vim.fn.jobstart({ vim.v.progpath, '-es', '-V1',( '+echo "%s="..getenv("%s")'):format(envname, envname), '+qa!' }, opt)
+ vim.fn.jobwait({ j1 }, 10000)
+ return join({ join(stdout), join(stderr) })
+ ]],
+ envname,
+ env)
+ end
+
+ local addr = eval('v:servername')
+ ok((addr):len() > 0)
+ -- $NVIM is _not_ defined in the top-level Nvim process.
+ eq('', eval('$NVIM'))
+ -- jobstart() shares its v:servername with the child via $NVIM.
+ eq('NVIM='..addr, get_env_in_child_job('NVIM'))
+ -- $NVIM_LISTEN_ADDRESS is unset by server_init in the child.
+ eq('NVIM_LISTEN_ADDRESS=null', get_env_in_child_job('NVIM_LISTEN_ADDRESS'))
+ eq('NVIM_LISTEN_ADDRESS=null', get_env_in_child_job('NVIM_LISTEN_ADDRESS',
+ { NVIM_LISTEN_ADDRESS='Xtest_jobstart_env' }))
+ -- User can explicitly set $NVIM_LOG_FILE, $VIM, $VIMRUNTIME.
+ eq('NVIM_LOG_FILE=Xtest_jobstart_env',
+ get_env_in_child_job('NVIM_LOG_FILE', { NVIM_LOG_FILE='Xtest_jobstart_env' }))
+ os.remove('Xtest_jobstart_env')
+ end)
+
describe('jobwait', function()
before_each(function()
if iswin() then
@@ -1009,8 +1057,7 @@ describe('jobs', function()
return a:data
endfunction
]])
- local ext = iswin() and '.exe' or ''
- insert(nvim_dir..'/tty-test'..ext) -- Full path to tty-test.
+ insert(testprg('tty-test'))
nvim('command', 'let g:job_opts.pty = 1')
nvim('command', 'let exec = [expand("<cfile>:p")]')
nvim('command', "let j = jobstart(exec, g:job_opts)")
@@ -1043,7 +1090,7 @@ describe('jobs', function()
local other_jobid = eval("jobstart(['cat', '-'], g:job_opts)")
local other_pid = eval('jobpid(' .. other_jobid .. ')')
- -- Other job doesn't block first job from recieving SIGHUP on jobclose()
+ -- Other job doesn't block first job from receiving SIGHUP on jobclose()
command('call jobclose(j)')
-- Have to wait so that the SIGHUP can be processed by tty-test on time.
-- Can't wait for the next message in case this test fails, if it fails
diff --git a/test/functional/core/log_spec.lua b/test/functional/core/log_spec.lua
new file mode 100644
index 0000000000..3b1ccd9559
--- /dev/null
+++ b/test/functional/core/log_spec.lua
@@ -0,0 +1,57 @@
+local helpers = require('test.functional.helpers')(after_each)
+local assert_log = helpers.assert_log
+local clear = helpers.clear
+local command = helpers.command
+local eq = helpers.eq
+local exec_lua = helpers.exec_lua
+local expect_exit = helpers.expect_exit
+local request = helpers.request
+local retry = helpers.retry
+
+describe('log', function()
+ local testlog = 'Xtest_logging'
+
+ after_each(function()
+ expect_exit(command, 'qa!')
+ os.remove(testlog)
+ end)
+
+ it('skipped before log_init', function()
+ -- This test is for _visibility_: adjust as needed, after checking for regression.
+ --
+ -- During startup some components may try to log before logging is setup.
+ -- That should be uncommon (ideally never)--and if there are MANY such
+ -- calls, that needs investigation.
+ clear()
+ eq(0, request('nvim__stats').log_skip)
+ clear{env={CDPATH='~doesnotexist'}}
+ assert(request('nvim__stats').log_skip <= 13)
+ end)
+
+ it('messages are formatted with name or test id', function()
+ -- Examples:
+ -- ERR 2022-05-29T12:30:03.800 T2 log_init:110: test log message
+ -- ERR 2022-05-29T12:30:03.814 T2/child log_init:110: test log message
+
+ clear({env={
+ NVIM_LOG_FILE=testlog,
+ -- TODO: remove this after nvim_log #7062 is merged.
+ __NVIM_TEST_LOG='1'
+ }})
+
+ local tid = _G._nvim_test_id
+ retry(nil, 1000, function()
+ assert_log(tid..'%.%d+%.%d +server_init:%d+: test log message', testlog, 100)
+ end)
+
+ exec_lua([[
+ local j1 = vim.fn.jobstart({ vim.v.progpath, '-es', '-V1', '+foochild', '+qa!' }, vim.empty_dict())
+ vim.fn.jobwait({ j1 }, 10000)
+ ]])
+
+ -- Child Nvim spawned by jobstart() appends "/c" to parent name.
+ retry(nil, 1000, function()
+ assert_log('%.%d+%.%d/c +server_init:%d+: test log message', testlog, 100)
+ end)
+ end)
+end)
diff --git a/test/functional/core/main_spec.lua b/test/functional/core/main_spec.lua
index 37a9f0b836..f6fb859ccc 100644
--- a/test/functional/core/main_spec.lua
+++ b/test/functional/core/main_spec.lua
@@ -52,11 +52,15 @@ describe('Command-line option', function()
if helpers.pending_win32(pending) then return end
local screen = Screen.new(40, 8)
screen:attach()
- funcs.termopen({
+ local args = {
nvim_prog_abs(), '-u', 'NONE', '-i', 'NONE',
- '--cmd', 'set noswapfile shortmess+=IFW fileformats=unix',
- '-s', '-'
- })
+ '--cmd', 'set noswapfile shortmess+=IFW fileformats=unix',
+ '-s', '-'
+ }
+
+ -- Need to explicitly pipe to stdin so that the embedded Nvim instance doesn't try to read
+ -- data from the terminal #18181
+ funcs.termopen(string.format([[echo "" | %s]], table.concat(args, " ")))
screen:expect([[
^ |
{1:~ }|
diff --git a/test/functional/core/remote_spec.lua b/test/functional/core/remote_spec.lua
new file mode 100644
index 0000000000..d7bd075eb2
--- /dev/null
+++ b/test/functional/core/remote_spec.lua
@@ -0,0 +1,142 @@
+local helpers = require('test.functional.helpers')(after_each)
+
+local clear = helpers.clear
+local command = helpers.command
+local eq = helpers.eq
+local expect = helpers.expect
+local funcs = helpers.funcs
+local insert = helpers.insert
+local meths = helpers.meths
+local new_argv = helpers.new_argv
+local neq = helpers.neq
+local set_session = helpers.set_session
+local spawn = helpers.spawn
+local tmpname = helpers.tmpname
+local write_file = helpers.write_file
+
+describe('Remote', function()
+ local fname, other_fname
+ local contents = 'The call is coming from outside the process'
+ local other_contents = "A second file's contents"
+
+ before_each(function()
+ fname = tmpname() .. ' with spaces in the filename'
+ other_fname = tmpname()
+ write_file(fname, contents)
+ write_file(other_fname, other_contents)
+ end)
+
+ describe('connect to server and', function()
+ local server
+ before_each(function()
+ server = spawn(new_argv(), true)
+ set_session(server)
+ end)
+
+ after_each(function()
+ server:close()
+ end)
+
+ local function run_remote(...)
+ set_session(server)
+ local addr = funcs.serverlist()[1]
+ local client_argv = new_argv({args={'--server', addr, ...}})
+
+ -- Create an nvim instance just to run the remote-invoking nvim. We want
+ -- to wait for the remote instance to exit and calling jobwait blocks
+ -- the event loop. If the server event loop is blocked, it can't process
+ -- our incoming --remote calls.
+ local client_starter = spawn(new_argv(), false, nil, true)
+ set_session(client_starter)
+ local client_job_id = funcs.jobstart(client_argv)
+ eq({ 0 }, funcs.jobwait({client_job_id}))
+ client_starter:close()
+ set_session(server)
+ end
+
+ it('edit a single file', function()
+ run_remote('--remote', fname)
+ expect(contents)
+ eq(2, #funcs.getbufinfo())
+ end)
+
+ it('tab edit a single file with a non-changed buffer', function()
+ run_remote('--remote-tab', fname)
+ expect(contents)
+ eq(1, #funcs.gettabinfo())
+ end)
+
+ it('tab edit a single file with a changed buffer', function()
+ insert('hello')
+ run_remote('--remote-tab', fname)
+ expect(contents)
+ eq(2, #funcs.gettabinfo())
+ end)
+
+ it('edit multiple files', function()
+ run_remote('--remote', fname, other_fname)
+ expect(contents)
+ command('next')
+ expect(other_contents)
+ eq(3, #funcs.getbufinfo())
+ end)
+
+ it('send keys', function()
+ run_remote('--remote-send', ':edit '..fname..'<CR><C-W>v')
+ expect(contents)
+ eq(2, #funcs.getwininfo())
+ -- Only a single buffer as we're using edit and not drop like --remote does
+ eq(1, #funcs.getbufinfo())
+ end)
+
+ it('evaluate expressions', function()
+ run_remote('--remote-expr', 'setline(1, "Yo")')
+ expect('Yo')
+ end)
+ end)
+
+ it('creates server if not found', function()
+ clear('--remote', fname)
+ expect(contents)
+ eq(1, #funcs.getbufinfo())
+ -- Since we didn't pass silent, we should get a complaint
+ neq(nil, string.find(meths.exec('messages', true), 'E247'))
+ end)
+
+ it('creates server if not found with tabs', function()
+ clear('--remote-tab-silent', fname, other_fname)
+ expect(contents)
+ eq(2, #funcs.gettabinfo())
+ eq(2, #funcs.getbufinfo())
+ -- We passed silent, so no message should be issued about the server not being found
+ eq(nil, string.find(meths.exec('messages', true), 'E247'))
+ end)
+
+ pending('exits with error on', function()
+ local function run_and_check_exit_code(...)
+ local bogus_argv = new_argv(...)
+
+ -- Create an nvim instance just to run the remote-invoking nvim. We want
+ -- to wait for the remote instance to exit and calling jobwait blocks
+ -- the event loop. If the server event loop is blocked, it can't process
+ -- our incoming --remote calls.
+ clear()
+ local bogus_job_id = funcs.jobstart(bogus_argv)
+ eq({2}, funcs.jobwait({bogus_job_id}))
+ end
+ it('bogus subcommand', function()
+ run_and_check_exit_code('--remote-bogus')
+ end)
+
+ it('send without server', function()
+ run_and_check_exit_code('--remote-send', 'i')
+ end)
+
+ it('expr without server', function()
+ run_and_check_exit_code('--remote-expr', 'setline(1, "Yo")')
+ end)
+ it('wait subcommand', function()
+ run_and_check_exit_code('--remote-wait', fname)
+ end)
+ end)
+end)
diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua
index 2fa84e8313..4f9df4010e 100644
--- a/test/functional/core/startup_spec.lua
+++ b/test/functional/core/startup_spec.lua
@@ -2,6 +2,7 @@ local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local assert_alive = helpers.assert_alive
+local assert_log = helpers.assert_log
local clear = helpers.clear
local command = helpers.command
local ok = helpers.ok
@@ -23,6 +24,29 @@ local iswin = helpers.iswin
local startswith = helpers.startswith
local write_file = helpers.write_file
local meths = helpers.meths
+local alter_slashes = helpers.alter_slashes
+
+local testfile = 'Xtest_startuptime'
+after_each(function()
+ os.remove(testfile)
+end)
+
+describe('startup', function()
+ it('--clean', function()
+ clear()
+ ok(string.find(alter_slashes(meths.get_option('runtimepath')), funcs.stdpath('config'), 1, true) ~= nil)
+ clear('--clean')
+ ok(string.find(alter_slashes(meths.get_option('runtimepath')), funcs.stdpath('config'), 1, true) == nil)
+ end)
+
+ it('--startuptime', function()
+ clear({ args = {'--startuptime', testfile}})
+ retry(nil, 1000, function()
+ assert_log('sourcing', testfile, 100)
+ assert_log("require%('vim%._editor'%)", testfile, 100)
+ end)
+ end)
+end)
describe('startup', function()
before_each(function()
@@ -255,6 +279,7 @@ describe('startup', function()
it('does not crash when expanding cdpath during early_init', function()
clear{env={CDPATH='~doesnotexist'}}
+ assert_alive()
eq(',~doesnotexist', eval('&cdpath'))
end)
@@ -355,7 +380,7 @@ describe('startup', function()
end)
it("handles :packadd during startup", function()
- -- control group: opt/bonus is not availabe by default
+ -- control group: opt/bonus is not available by default
pack_clear [[
try
let g:x = bonus#secret()
@@ -398,7 +423,8 @@ describe('startup', function()
eq({'ordinary', 'FANCY', 'mittel', 'FANCY after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]])
local rtp = meths.get_option'rtp'
- ok(startswith(rtp, 'test/functional/fixtures/nvim,test/functional/fixtures/pack/*/start/*,test/functional/fixtures/start/*,test/functional/fixtures,test/functional/fixtures/middle,'), 'rtp='..rtp)
+ ok(startswith(rtp, 'test/functional/fixtures/nvim,test/functional/fixtures/pack/*/start/*,test/functional/fixtures/start/*,test/functional/fixtures,test/functional/fixtures/middle,'),
+ 'startswith(…)', 'rtp='..rtp)
end)
it("handles the correct order with opt packages and after/", function()
@@ -492,22 +518,24 @@ describe('sysinit', function()
it('fixed hang issue with -D (#12647)', function()
local screen
- screen = Screen.new(60, 6)
+ screen = Screen.new(60, 7)
screen:attach()
command([[let g:id = termopen('"]]..nvim_prog..
[[" -u NONE -i NONE --cmd "set noruler" -D')]])
screen:expect([[
^ |
Entering Debug mode. Type "cont" to continue. |
- cmd: augroup nvim_terminal |
+ nvim_exec() |
+ cmd: aunmenu * |
> |
- <" -u NONE -i NONE --cmd "set noruler" -D 1,0-1 All|
+ <" -u NONE -i NONE --cmd "set noruler" -D 1,1 All|
|
]])
command([[call chansend(g:id, "cont\n")]])
screen:expect([[
^ |
~ |
+ ~ |
[No Name] |
|
<" -u NONE -i NONE --cmd "set noruler" -D 1,0-1 All|
@@ -516,13 +544,6 @@ describe('sysinit', function()
end)
end)
-describe('clean', function()
- clear()
- ok(string.find(meths.get_option('runtimepath'), funcs.stdpath('config'), 1, true) ~= nil)
- clear('--clean')
- ok(string.find(meths.get_option('runtimepath'), funcs.stdpath('config'), 1, true) == nil)
-end)
-
describe('user config init', function()
local xhome = 'Xhome'
local pathsep = helpers.get_pathsep()
@@ -577,7 +598,7 @@ describe('user config init', function()
it('loads default lua config, but shows an error', function()
clear{ args_rm={'-u'}, env=xenv }
- feed('<cr>') -- confirm "Conflicting config ..." message
+ feed('<cr><c-c>') -- Dismiss "Conflicting config …" message.
eq(1, eval('g:lua_rc'))
matches('^E5422: Conflicting configs', meths.exec('messages', true))
end)
@@ -629,13 +650,13 @@ describe('runtime:', function()
eq(2, eval('g:lua_plugin'))
-- Check if plugin_file_path is listed in :scriptname
local scripts = meths.exec(':scriptnames', true)
- assert.Truthy(scripts:find(plugin_file_path))
+ assert(scripts:find(plugin_file_path))
-- Check if plugin_file_path is listed in startup profile
local profile_reader = io.open(profiler_file, 'r')
local profile_log = profile_reader:read('*a')
profile_reader:close()
- assert.Truthy(profile_log :find(plugin_file_path))
+ assert(profile_log:find(plugin_file_path))
os.remove(profiler_file)
rmdir(plugin_path)
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/completion_spec.lua b/test/functional/editor/completion_spec.lua
index befad29922..e27da0947f 100644
--- a/test/functional/editor/completion_spec.lua
+++ b/test/functional/editor/completion_spec.lua
@@ -1193,4 +1193,64 @@ describe('completion', function()
eq('foobar', eval('g:word'))
feed('<esc>')
end)
+
+ it('is stopped by :stopinsert from timer #12976', function()
+ screen:try_resize(32,14)
+ command([[call setline(1, ['hello', 'hullo', 'heeee', ''])]])
+ feed('Gah<c-x><c-n>')
+ screen:expect([[
+ hello |
+ hullo |
+ heeee |
+ hello^ |
+ {2:hello }{0: }|
+ {1:hullo }{0: }|
+ {1:heeee }{0: }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {3:-- }{4: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 |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ end)
+
+ it('does not crash if text is changed by first call to complete function #17489', function()
+ source([[
+ func Complete(findstart, base) abort
+ if a:findstart
+ let col = col('.')
+ call complete_add('#')
+ return col - 1
+ else
+ return []
+ endif
+ endfunc
+
+ set completeopt=longest
+ set completefunc=Complete
+ ]])
+ feed('ifoo#<C-X><C-U>')
+ assert_alive()
+ end)
end)
diff --git a/test/functional/ex_cmds/ctrl_c_spec.lua b/test/functional/editor/ctrl_c_spec.lua
index f65d9f0d01..60131bf2a4 100644
--- a/test/functional/ex_cmds/ctrl_c_spec.lua
+++ b/test/functional/editor/ctrl_c_spec.lua
@@ -2,16 +2,20 @@ local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local clear, feed, source = helpers.clear, helpers.feed, helpers.source
local command = helpers.command
+local sleep = helpers.sleep
describe("CTRL-C (mapped)", function()
+ local screen
+
before_each(function()
clear()
+ screen = Screen.new(52, 6)
+ screen:attach()
end)
it("interrupts :global", function()
-- Crashes luajit.
- if helpers.skip_fragile(pending,
- helpers.isCI('travis') or helpers.isCI('appveyor')) then
+ if helpers.skip_fragile(pending) then
return
end
@@ -21,14 +25,6 @@ describe("CTRL-C (mapped)", function()
]])
command("silent edit! test/functional/fixtures/bigfile.txt")
- local screen = Screen.new(52, 6)
- screen:attach()
- screen:set_default_attr_ids({
- [0] = {foreground = Screen.colors.White,
- background = Screen.colors.Red},
- [1] = {bold = true,
- foreground = Screen.colors.SeaGreen}
- })
screen:expect([[
^0000;<control>;Cc;0;BN;;;;;N;NULL;;;; |
@@ -57,4 +53,42 @@ describe("CTRL-C (mapped)", function()
end
end
end)
+
+ it('interrupts :sleep', function()
+ command('nnoremap <C-C> <Nop>')
+ feed(':sleep 100<CR>')
+ -- wait for :sleep to start
+ sleep(10)
+ feed('foo<C-C>')
+ -- wait for input buffer to be flushed
+ sleep(10)
+ feed('i')
+ screen:expect([[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ -- INSERT -- |
+ ]])
+ end)
+
+ it('interrupts recursive mapping', function()
+ command('nnoremap <C-C> <Nop>')
+ command('nmap <F2> <Ignore><F2>')
+ feed('<F2>')
+ sleep(10)
+ feed('foo<C-C>')
+ -- wait for input buffer to be flushed
+ sleep(10)
+ feed('i')
+ screen:expect([[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ -- INSERT -- |
+ ]])
+ end)
end)
diff --git a/test/functional/editor/jump_spec.lua b/test/functional/editor/jump_spec.lua
index d09c20f226..63f522fe6e 100644
--- a/test/functional/editor/jump_spec.lua
+++ b/test/functional/editor/jump_spec.lua
@@ -1,4 +1,5 @@
local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
local clear = helpers.clear
local command = helpers.command
@@ -7,6 +8,7 @@ local funcs = helpers.funcs
local feed = helpers.feed
local exec_capture = helpers.exec_capture
local write_file = helpers.write_file
+local curbufmeths = helpers.curbufmeths
describe('jumplist', function()
local fname1 = 'Xtest-functional-normal-jump'
@@ -137,3 +139,144 @@ describe("jumpoptions=stack behaves like 'tagstack'", function()
exec_capture('jumps'))
end)
end)
+
+describe("jumpoptions=view", function()
+ local file1 = 'Xtestfile-functional-editor-jumps'
+ local file2 = 'Xtestfile-functional-editor-jumps-2'
+ local function content()
+ local c = {}
+ for i=1,30 do
+ c[i] = i .. " line"
+ end
+ return table.concat(c, "\n")
+ end
+ before_each(function()
+ clear()
+ write_file(file1, content(), false, false)
+ write_file(file2, content(), false, false)
+ command('set jumpoptions=view')
+ end)
+ after_each(function()
+ os.remove(file1)
+ os.remove(file2)
+ end)
+
+ it('restores the view', function()
+ local screen = Screen.new(5, 8)
+ screen:attach()
+ command("edit " .. file1)
+ feed("12Gztj")
+ feed("gg<C-o>")
+ screen:expect([[
+ 12 line |
+ ^13 line |
+ 14 line |
+ 15 line |
+ 16 line |
+ 17 line |
+ 18 line |
+ |
+ ]])
+ end)
+
+ it('restores the view across files', function()
+ local screen = Screen.new(5, 5)
+ screen:attach()
+ command("args " .. file1 .. " " .. file2)
+ feed("12Gzt")
+ command("next")
+ feed("G")
+ screen:expect([[
+ 27 line |
+ 28 line |
+ 29 line |
+ ^30 line |
+ |
+ ]])
+ feed("<C-o><C-o>")
+ screen:expect([[
+ ^12 line |
+ 13 line |
+ 14 line |
+ 15 line |
+ |
+ ]])
+ end)
+
+ it('restores the view across files with <C-^>', function()
+ local screen = Screen.new(5, 5)
+ screen:attach()
+ command("args " .. file1 .. " " .. file2)
+ feed("12Gzt")
+ command("next")
+ feed("G")
+ screen:expect([[
+ 27 line |
+ 28 line |
+ 29 line |
+ ^30 line |
+ |
+ ]])
+ feed("<C-^>")
+ screen:expect([[
+ ^12 line |
+ 13 line |
+ 14 line |
+ 15 line |
+ |
+ ]])
+ end)
+
+ it('falls back to standard behavior when view can\'t be recovered', function()
+ local screen = Screen.new(5, 8)
+ screen:attach()
+ command("edit " .. file1)
+ feed("7GzbG")
+ curbufmeths.set_lines(0, 2, true, {})
+ -- Move to line 7, and set it as the last line visible on the view with zb, meaning to recover
+ -- the view it needs to put the cursor 7 lines from the top line. Then go to the end of the
+ -- file, delete 2 lines before line 7, meaning the jump/mark is moved 2 lines up to line 5.
+ -- Therefore when trying to jump back to it it's not possible to set a 7 line offset from the
+ -- mark position to the top line, since there's only 5 lines from the mark position to line 0.
+ -- Therefore falls back to standard behavior which is centering the view/line.
+ feed("<C-o>")
+ screen:expect([[
+ 4 line |
+ 5 line |
+ 6 line |
+ ^7 line |
+ 8 line |
+ 9 line |
+ 10 line |
+ |
+ ]])
+ end)
+
+ it('falls back to standard behavior for a mark without a view', function()
+ local screen = Screen.new(5, 8)
+ screen:attach()
+ command('edit ' .. file1)
+ feed('10ggzzvwy')
+ screen:expect([[
+ 7 line |
+ 8 line |
+ 9 line |
+ ^10 line |
+ 11 line |
+ 12 line |
+ 13 line |
+ |
+ ]])
+ feed('`]')
+ screen:expect([[
+ 7 line |
+ 8 line |
+ 9 line |
+ 10 ^line |
+ 11 line |
+ 12 line |
+ 13 line |
+ |
+ ]])
+ end)
+end)
diff --git a/test/functional/editor/langmap_spec.lua b/test/functional/editor/langmap_spec.lua
index e4349a22e7..b1070ecddc 100644
--- a/test/functional/editor/langmap_spec.lua
+++ b/test/functional/editor/langmap_spec.lua
@@ -30,7 +30,7 @@ describe("'langmap'", function()
command('nmapclear')
end)
it("'langnoremap' is by default ON", function()
- eq(eval('&langnoremap'), 1)
+ eq(1, eval('&langnoremap'))
end)
it("Results of maps are not converted when 'langnoremap' ON.",
function()
@@ -71,19 +71,19 @@ describe("'langmap'", function()
feed('<C-w>s')
local origwin = curwin()
feed('<C-w>i')
- neq(curwin(), origwin)
+ neq(origwin, curwin())
-- Works when setting a mark
feed('yy3p3gg0mwgg0mi')
- eq(call('getpos', "'i"), {0, 3, 1, 0})
- eq(call('getpos', "'w"), {0, 1, 1, 0})
+ eq({0, 3, 1, 0}, call('getpos', "'i"))
+ eq({0, 1, 1, 0}, call('getpos', "'w"))
feed('3dd')
-- Works when moving to a mark
feed("'i")
- eq(call('getpos', '.'), {0, 1, 1, 0})
+ eq({0, 1, 1, 0}, call('getpos', '.'))
-- Works when selecting a register
feed('qillqqwhhq')
- eq(eval('@i'), 'hh')
- eq(eval('@w'), 'll')
+ eq('hh', eval('@i'))
+ eq('ll', eval('@w'))
feed('a<C-r>i<esc>')
expect('illii www')
feed('"ip')
@@ -107,7 +107,7 @@ describe("'langmap'", function()
expect('wwi www')
feed('u@a')
expect('wwi www')
- eq(eval('@a'), ':s/i/w/gc\ryyn')
+ eq(':s/i/w/gc\ryyn', eval('@a'))
end)
it('insert-mode CTRL-G', function()
command('set langmap=jk,kj')
@@ -127,7 +127,7 @@ describe("'langmap'", function()
helhellolo
helxlo
hello]])
- eq(eval('@a'), 'gg3|ahellojx')
+ eq('gg3|ahellojx', eval('@a'))
end)
it('command-line CTRL-\\', function()
command('set langmap=en,ne')
@@ -145,8 +145,8 @@ describe("'langmap'", function()
set langmap=ij,ji
]])
feed(':let <C-R>i=1<CR>')
- eq(eval('i_value'), 1)
- eq(eval('j_value'), 0)
+ eq(1, eval('i_value'))
+ eq(0, eval('j_value'))
end)
-- it('-- More -- prompt', function()
-- -- The 'b' 'j' 'd' 'f' commands at the -- More -- prompt
@@ -186,17 +186,17 @@ describe("'langmap'", function()
nnoremap x :call Map()<CR>
]])
feed('x1<CR>')
- eq(eval('gotten_one'), 1)
+ eq(1, eval('gotten_one'))
command('let g:gotten_one = 0')
feed_command('call Map()')
feed('1<CR>')
- eq(eval('gotten_one'), 1)
+ eq(1, eval('gotten_one'))
end)
end)
it('conversions are not applied during setreg()',
function()
call('setreg', 'i', 'ww')
- eq(eval('@i'), 'ww')
+ eq('ww', eval('@i'))
end)
it('conversions not applied in insert mode', function()
feed('aiiiwww')
@@ -213,11 +213,11 @@ describe("'langmap'", function()
iii]])
end)
- local function testrecording(command_string, expect_string, setup_function)
+ local function testrecording(command_string, expect_string, setup_function, expect_macro)
if setup_function then setup_function() end
feed('qa' .. command_string .. 'q')
expect(expect_string)
- eq(helpers.funcs.nvim_replace_termcodes(command_string, true, true, true),
+ eq(expect_macro or helpers.funcs.nvim_replace_termcodes(command_string, true, true, true),
eval('@a'))
if setup_function then setup_function() end
-- n.b. may need nvim_replace_termcodes() here.
@@ -273,8 +273,8 @@ describe("'langmap'", function()
it('treats control modified keys as characters', function()
command('nnoremap <C-w> iw<esc>')
command('nnoremap <C-i> ii<esc>')
- testrecording('<C-w>', 'whello', local_setup)
- testrecording('<C-i>', 'ihello', local_setup)
+ testrecording('<C-w>', 'whello', local_setup, eval([["\<*C-w>"]]))
+ testrecording('<C-i>', 'ihello', local_setup, eval([["\<*C-i>"]]))
end)
end)
diff --git a/test/functional/editor/macro_spec.lua b/test/functional/editor/macro_spec.lua
index c0c9256af2..53be7dcc62 100644
--- a/test/functional/editor/macro_spec.lua
+++ b/test/functional/editor/macro_spec.lua
@@ -6,28 +6,31 @@ local feed = helpers.feed
local clear = helpers.clear
local expect = helpers.expect
local command = helpers.command
+local funcs = helpers.funcs
+local meths = helpers.meths
local insert = helpers.insert
local curbufmeths = helpers.curbufmeths
+before_each(clear)
+
describe('macros', function()
- before_each(clear)
it('can be recorded and replayed', function()
feed('qiahello<esc>q')
expect('hello')
- eq(eval('@i'), 'ahello')
+ eq('ahello', eval('@i'))
feed('@i')
expect('hellohello')
- eq(eval('@i'), 'ahello')
+ eq('ahello', eval('@i'))
end)
it('applies maps', function()
command('imap x l')
command('nmap l a')
feed('qilxxx<esc>q')
expect('lll')
- eq(eval('@i'), 'lxxx')
+ eq('lxxx', eval('@i'))
feed('@i')
expect('llllll')
- eq(eval('@i'), 'lxxx')
+ eq('lxxx', eval('@i'))
end)
it('can be replayed with Q', function()
@@ -47,9 +50,47 @@ hello]]
end)
end)
-describe('reg_recorded()', function()
- before_each(clear)
+describe('immediately after a macro has finished executing,', function()
+ before_each(function()
+ command([[let @a = 'gg0']])
+ end)
+
+ describe('reg_executing() from RPC returns an empty string', function()
+ it('if the macro does not end with a <Nop> mapping', function()
+ feed('@a')
+ eq('', funcs.reg_executing())
+ end)
+
+ it('if the macro ends with a <Nop> mapping', function()
+ command('nnoremap 0 <Nop>')
+ feed('@a')
+ eq('', funcs.reg_executing())
+ end)
+ end)
+ describe('characters from a mapping are not treated as a part of the macro #18015', function()
+ before_each(function()
+ command('nnoremap s qa')
+ end)
+
+ it('if the macro does not end with a <Nop> mapping', function()
+ feed('@asq') -- "q" from "s" mapping should start recording a macro instead of being no-op
+ eq({mode = 'n', blocking = false}, meths.get_mode())
+ expect('')
+ eq('', eval('@a'))
+ end)
+
+ it('if the macro ends with a <Nop> mapping', function()
+ command('nnoremap 0 <Nop>')
+ feed('@asq') -- "q" from "s" mapping should start recording a macro instead of being no-op
+ eq({mode = 'n', blocking = false}, meths.get_mode())
+ expect('')
+ eq('', eval('@a'))
+ end)
+ end)
+end)
+
+describe('reg_recorded()', function()
it('returns the correct value', function()
feed [[qqyyq]]
eq('q', eval('reg_recorded()'))
diff --git a/test/functional/editor/mark_spec.lua b/test/functional/editor/mark_spec.lua
new file mode 100644
index 0000000000..1eb76aa628
--- /dev/null
+++ b/test/functional/editor/mark_spec.lua
@@ -0,0 +1,401 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local meths = helpers.meths
+local curbufmeths = helpers.curbufmeths
+local clear = helpers.clear
+local command = helpers.command
+local funcs = helpers.funcs
+local eq = helpers.eq
+local feed = helpers.feed
+local write_file = helpers.write_file
+local pcall_err = helpers.pcall_err
+local cursor = function() return helpers.meths.win_get_cursor(0) end
+
+describe('named marks', function()
+ local file1 = 'Xtestfile-functional-editor-marks'
+ local file2 = 'Xtestfile-functional-editor-marks-2'
+ before_each(function()
+ clear()
+ write_file(file1, '1test1\n1test2\n1test3\n1test4', false, false)
+ write_file(file2, '2test1\n2test2\n2test3\n2test4', false, false)
+ end)
+ after_each(function()
+ os.remove(file1)
+ os.remove(file2)
+ end)
+
+
+ it("can be set", function()
+ command("edit " .. file1)
+ command("mark a")
+ eq({1, 0}, curbufmeths.get_mark("a"))
+ feed("jmb")
+ eq({2, 0}, curbufmeths.get_mark("b"))
+ feed("jmB")
+ eq({3, 0}, curbufmeths.get_mark("B"))
+ command("4kc")
+ eq({4, 0}, curbufmeths.get_mark("c"))
+ end)
+
+ it("errors when set out of range with :mark", function()
+ command("edit " .. file1)
+ local err = pcall_err(helpers.exec_capture, "1000mark x")
+ eq("Vim(mark):E16: Invalid range: 1000mark x", err)
+ end)
+
+ it("errors when set out of range with :k", function()
+ command("edit " .. file1)
+ local err = pcall_err(helpers.exec_capture, "1000kx")
+ eq("Vim(k):E16: Invalid range: 1000kx", err)
+ end)
+
+ it("errors on unknown mark name with :mark", function()
+ command("edit " .. file1)
+ local err = pcall_err(helpers.exec_capture, "mark #")
+ eq("Vim(mark):E191: Argument must be a letter or forward/backward quote", err)
+ end)
+
+ it("errors on unknown mark name with '", function()
+ command("edit " .. file1)
+ local err = pcall_err(helpers.exec_capture, "normal! '#")
+ eq("Vim(normal):E78: Unknown mark", err)
+ end)
+
+ it("errors on unknown mark name with `", function()
+ command("edit " .. file1)
+ local err = pcall_err(helpers.exec_capture, "normal! `#")
+ eq("Vim(normal):E78: Unknown mark", err)
+ end)
+
+ it("errors when moving to a mark that is not set with '", function()
+ command("edit " .. file1)
+ local err = pcall_err(helpers.exec_capture, "normal! 'z")
+ eq("Vim(normal):E20: Mark not set", err)
+ err = pcall_err(helpers.exec_capture, "normal! '.")
+ eq("Vim(normal):E20: Mark not set", err)
+ end)
+
+ it("errors when moving to a mark that is not set with `", function()
+ command("edit " .. file1)
+ local err = pcall_err(helpers.exec_capture, "normal! `z")
+ eq("Vim(normal):E20: Mark not set", err)
+ err = pcall_err(helpers.exec_capture, "normal! `>")
+ eq("Vim(normal):E20: Mark not set", err)
+ end)
+
+ it("errors when moving to a global mark that is not set with '", function()
+ command("edit " .. file1)
+ local err = pcall_err(helpers.exec_capture, "normal! 'Z")
+ eq("Vim(normal):E20: Mark not set", err)
+ end)
+
+ it("errors when moving to a global mark that is not set with `", function()
+ command("edit " .. file1)
+ local err = pcall_err(helpers.exec_capture, "normal! `Z")
+ eq("Vim(normal):E20: Mark not set", err)
+ end)
+
+ it("can move to them using '", function()
+ command("args " .. file1 .. " " .. file2)
+ feed("j")
+ feed("ma")
+ feed("G'a")
+ eq({2, 0}, cursor())
+ feed("mA")
+ command("next")
+ feed("'A")
+ eq(1, meths.get_current_buf().id)
+ eq({2, 0}, cursor())
+ end)
+
+ it("can move to them using `", function()
+ command("args " .. file1 .. " " .. file2)
+ feed("jll")
+ feed("ma")
+ feed("G`a")
+ eq({2, 2}, cursor())
+ feed("mA")
+ command("next")
+ feed("`A")
+ eq(1, meths.get_current_buf().id)
+ eq({2, 2}, cursor())
+ end)
+
+ it("can move to them using g'", function()
+ command("args " .. file1 .. " " .. file2)
+ feed("jll")
+ feed("ma")
+ feed("Gg'a")
+ eq({2, 0}, cursor())
+ feed("mA")
+ command("next")
+ feed("g'A")
+ eq(1, meths.get_current_buf().id)
+ eq({2, 0}, cursor())
+ end)
+
+ it("can move to them using g`", function()
+ command("args " .. file1 .. " " .. file2)
+ feed("jll")
+ feed("ma")
+ feed("Gg`a")
+ eq({2, 2}, cursor())
+ feed("mA")
+ command("next")
+ feed("g`A")
+ eq(1, meths.get_current_buf().id)
+ eq({2, 2}, cursor())
+ end)
+
+ it("errors when it can't find the buffer", function()
+ command("args " .. file1 .. " " .. file2)
+ feed("mA")
+ command("next")
+ command("bw! " .. file1 )
+ local err = pcall_err(helpers.exec_capture, "normal! 'A")
+ eq("Vim(normal):E92: Buffer 1 not found", err)
+ os.remove(file1)
+ end)
+
+ it("leave a context mark when moving with '", function()
+ command("edit " .. file1)
+ feed("llmamA")
+ feed("10j0") -- first col, last line
+ local pos = cursor()
+ feed("'a")
+ feed("<C-o>")
+ eq(pos, cursor())
+ feed("'A")
+ feed("<C-o>")
+ eq(pos, cursor())
+ end)
+
+ it("leave a context mark when moving with `", function()
+ command("edit " .. file1)
+ feed("llmamA")
+ feed("10j0") -- first col, last line
+ local pos = cursor()
+ feed("`a")
+ feed("<C-o>")
+ eq(pos, cursor())
+ feed("`A")
+ feed("<C-o>")
+ eq(pos, cursor())
+ end)
+
+ it("leave a context mark when the mark changes buffer with g'", function()
+ command("args " .. file1 .. " " .. file2)
+ local pos
+ feed("GmA")
+ command("next")
+ pos = cursor()
+ command("clearjumps")
+ feed("g'A") -- since the mark is in another buffer, it leaves a context mark
+ feed("<C-o>")
+ eq(pos, cursor())
+ end)
+
+ it("leave a context mark when the mark changes buffer with g`", function()
+ command("args " .. file1 .. " " .. file2)
+ local pos
+ feed("GmA")
+ command("next")
+ pos = cursor()
+ command("clearjumps")
+ feed("g`A") -- since the mark is in another buffer, it leaves a context mark
+ feed("<C-o>")
+ eq(pos, cursor())
+ end)
+
+ it("do not leave a context mark when moving with g'", function()
+ command("edit " .. file1)
+ local pos
+ feed("ma")
+ pos = cursor() -- Mark pos
+ feed("10j0") -- first col, last line
+ feed("g'a")
+ feed("<C-o>") -- should do nothing
+ eq(pos, cursor())
+ feed("mA")
+ pos = cursor() -- Mark pos
+ feed("10j0") -- first col, last line
+ feed("g'a")
+ feed("<C-o>") -- should do nothing
+ eq(pos, cursor())
+ end)
+
+ it("do not leave a context mark when moving with g`", function()
+ command("edit " .. file1)
+ local pos
+ feed("ma")
+ pos = cursor() -- Mark pos
+ feed("10j0") -- first col, last line
+ feed("g`a")
+ feed("<C-o>") -- should do nothing
+ eq(pos, cursor())
+ feed("mA")
+ pos = cursor() -- Mark pos
+ feed("10j0") -- first col, last line
+ feed("g'a")
+ feed("<C-o>") -- should do nothing
+ eq(pos, cursor())
+ end)
+
+ it("open folds when moving to them", function()
+ command("edit " .. file1)
+ feed("jzfG") -- Fold from the second line to the end
+ command("3mark a")
+ feed("G") -- On top of the fold
+ assert(funcs.foldclosed('.') ~= -1) -- folded
+ feed("'a")
+ eq(-1, funcs.foldclosed('.'))
+
+ feed("zc")
+ assert(funcs.foldclosed('.') ~= -1) -- folded
+ -- TODO: remove this workaround after fixing #15873
+ feed("k`a")
+ eq(-1, funcs.foldclosed('.'))
+
+ feed("zc")
+ assert(funcs.foldclosed('.') ~= -1) -- folded
+ feed("kg'a")
+ eq(-1, funcs.foldclosed('.'))
+
+ feed("zc")
+ assert(funcs.foldclosed('.') ~= -1) -- folded
+ feed("kg`a")
+ eq(-1, funcs.foldclosed('.'))
+ end)
+
+ it("do not open folds when moving to them doesn't move the cursor", function()
+ command("edit " .. file1)
+ feed("jzfG") -- Fold from the second line to the end
+ assert(funcs.foldclosed('.') == 2) -- folded
+ feed("ma")
+ feed("'a")
+ feed("`a")
+ feed("g'a")
+ feed("g`a")
+ -- should still be folded
+ eq(2, funcs.foldclosed('.'))
+ end)
+
+ it("getting '{ '} '( ') does not move cursor", function()
+ meths.buf_set_lines(0, 0, 0, true, {'aaaaa', 'bbbbb', 'ccccc', 'ddddd', 'eeeee'})
+ meths.win_set_cursor(0, {2, 0})
+ funcs.getpos("'{")
+ eq({2, 0}, meths.win_get_cursor(0))
+ funcs.getpos("'}")
+ eq({2, 0}, meths.win_get_cursor(0))
+ funcs.getpos("'(")
+ eq({2, 0}, meths.win_get_cursor(0))
+ funcs.getpos("')")
+ eq({2, 0}, meths.win_get_cursor(0))
+ end)
+
+ it('in command range does not move cursor #19248', function()
+ meths.create_user_command('Test', ':', {range = true})
+ meths.buf_set_lines(0, 0, 0, true, {'aaaaa', 'bbbbb', 'ccccc', 'ddddd', 'eeeee'})
+ meths.win_set_cursor(0, {2, 0})
+ command([['{,'}Test]])
+ eq({2, 0}, meths.win_get_cursor(0))
+ end)
+end)
+
+describe('named marks view', function()
+ local file1 = 'Xtestfile-functional-editor-marks'
+ local file2 = 'Xtestfile-functional-editor-marks-2'
+ local function content()
+ local c = {}
+ for i=1,30 do
+ c[i] = i .. " line"
+ end
+ return table.concat(c, "\n")
+ end
+ before_each(function()
+ clear()
+ write_file(file1, content(), false, false)
+ write_file(file2, content(), false, false)
+ command("set jumpoptions+=view")
+ end)
+ after_each(function()
+ os.remove(file1)
+ os.remove(file2)
+ end)
+
+ it('is restored', function()
+ local screen = Screen.new(5, 8)
+ screen:attach()
+ command("edit " .. file1)
+ feed("<C-e>jWma")
+ feed("G'a")
+ local expected = [[
+ 2 line |
+ ^3 line |
+ 4 line |
+ 5 line |
+ 6 line |
+ 7 line |
+ 8 line |
+ |
+ ]]
+ screen:expect({grid=expected})
+ feed("G`a")
+ screen:expect([[
+ 2 line |
+ 3 ^line |
+ 4 line |
+ 5 line |
+ 6 line |
+ 7 line |
+ 8 line |
+ |
+ ]])
+ end)
+
+ it('is restored across files', function()
+ local screen = Screen.new(5, 5)
+ screen:attach()
+ command("args " .. file1 .. " " .. file2)
+ feed("<C-e>mA")
+ local mark_view = [[
+ ^2 line |
+ 3 line |
+ 4 line |
+ 5 line |
+ |
+ ]]
+ screen:expect(mark_view)
+ command("next")
+ screen:expect([[
+ ^1 line |
+ 2 line |
+ 3 line |
+ 4 line |
+ |
+ ]])
+ feed("'A")
+ screen:expect(mark_view)
+ end)
+
+ it('fallback to standard behavior when view can\'t be recovered', function()
+ local screen = Screen.new(10, 10)
+ screen:attach()
+ command("edit " .. file1)
+ feed("7GzbmaG") -- Seven lines from the top
+ command("new") -- Screen size for window is now half the height can't be restored
+ feed("<C-w>p'a")
+ screen:expect([[
+ |
+ ~ |
+ ~ |
+ ~ |
+ [No Name] |
+ 6 line |
+ ^7 line |
+ 8 line |
+ {MATCH:.*} |
+ |
+ ]])
+ end)
+end)
diff --git a/test/functional/editor/meta_key_spec.lua b/test/functional/editor/meta_key_spec.lua
index 2280f5bb24..23964ca10f 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()
@@ -89,4 +104,41 @@ describe('meta-keys #8226 #13042', function()
eq({ 0, 2, 1, 0, }, funcs.getpos('.'))
eq('nt', eval('mode(1)'))
end)
+
+ it('ALT/META when recording a macro #13235', function()
+ command('inoremap <M-Esc> <lt>M-ESC>')
+ feed('ifoo<CR>bar<CR>baz<Esc>gg0')
+ -- <M-"> is reinterpreted as <Esc>"
+ feed('qrviw"ayC// This is some text: <M-">apq')
+ expect([[
+ // This is some text: foo
+ bar
+ baz]])
+ -- Should not insert an extra double quote or trigger <M-Esc> when replaying
+ feed('j0@rj0@@')
+ expect([[
+ // This is some text: foo
+ // This is some text: bar
+ // This is some text: baz]])
+ command('%delete')
+ end)
+
+ it('ALT/META with special key when recording a macro', function()
+ command('inoremap <M-Esc> <lt>M-ESC>')
+ command('noremap <S-Tab> "')
+ command('noremap! <S-Tab> "')
+ feed('ifoo<CR>bar<CR>baz<Esc>gg0')
+ -- <M-S-Tab> is reinterpreted as <Esc><S-Tab>
+ feed('qrviw<S-Tab>ayC// This is some text: <M-S-Tab>apq')
+ expect([[
+ // This is some text: foo
+ bar
+ baz]])
+ -- Should not insert an extra double quote or trigger <M-Esc> when replaying
+ feed('j0@rj0@@')
+ expect([[
+ // This is some text: foo
+ // This is some text: bar
+ // This is some text: baz]])
+ end)
end)
diff --git a/test/functional/editor/mode_cmdline_spec.lua b/test/functional/editor/mode_cmdline_spec.lua
index 0f7d821bb5..50cc5e17ee 100644
--- a/test/functional/editor/mode_cmdline_spec.lua
+++ b/test/functional/editor/mode_cmdline_spec.lua
@@ -3,67 +3,74 @@
local helpers = require('test.functional.helpers')(after_each)
local clear, insert, funcs, eq, feed =
helpers.clear, helpers.insert, helpers.funcs, helpers.eq, helpers.feed
+local eval = helpers.eval
local meths = helpers.meths
-describe('cmdline CTRL-R', function()
+describe('cmdline', function()
before_each(clear)
- it('pasting non-special register inserts <CR> *between* lines', function()
- insert([[
- line1abc
- line2somemoretext
- ]])
- -- Yank 2 lines linewise, then paste to cmdline.
- feed([[<C-\><C-N>gg0yj:<C-R>0]])
- -- <CR> inserted between lines, NOT after the final line.
- eq('line1abc\rline2somemoretext', funcs.getcmdline())
+ describe('Ctrl-R', function()
+ it('pasting non-special register inserts <CR> *between* lines', function()
+ insert([[
+ line1abc
+ line2somemoretext
+ ]])
+ -- Yank 2 lines linewise, then paste to cmdline.
+ feed([[<C-\><C-N>gg0yj:<C-R>0]])
+ -- <CR> inserted between lines, NOT after the final line.
+ eq('line1abc\rline2somemoretext', funcs.getcmdline())
- -- Yank 2 lines charwise, then paste to cmdline.
- feed([[<C-\><C-N>gg05lyvj:<C-R>0]])
- -- <CR> inserted between lines, NOT after the final line.
- eq('abc\rline2', funcs.getcmdline())
+ -- Yank 2 lines charwise, then paste to cmdline.
+ feed([[<C-\><C-N>gg05lyvj:<C-R>0]])
+ -- <CR> inserted between lines, NOT after the final line.
+ eq('abc\rline2', funcs.getcmdline())
- -- Yank 1 line linewise, then paste to cmdline.
- feed([[<C-\><C-N>ggyy:<C-R>0]])
- -- No <CR> inserted.
- eq('line1abc', funcs.getcmdline())
- end)
+ -- Yank 1 line linewise, then paste to cmdline.
+ feed([[<C-\><C-N>ggyy:<C-R>0]])
+ -- No <CR> inserted.
+ eq('line1abc', funcs.getcmdline())
+ end)
- it('pasting special register inserts <CR>, <NL>', function()
- feed([[:<C-R>="foo\nbar\rbaz"<CR>]])
- eq('foo\nbar\rbaz', funcs.getcmdline())
+ it('pasting special register inserts <CR>, <NL>', function()
+ feed([[:<C-R>="foo\nbar\rbaz"<CR>]])
+ eq('foo\nbar\rbaz', funcs.getcmdline())
+ end)
end)
-end)
-describe('cmdline history', function()
- before_each(clear)
+ it('Ctrl-Shift-V supports entering unsimplified key notations', function()
+ feed(':"<C-S-V><C-J><C-S-V><C-@><C-S-V><C-[><C-S-V><C-S-M><C-S-V><M-C-I><C-S-V><C-D-J><CR>')
- it('correctly clears start of the history', function()
- -- Regression test: check absence of the memory leak when clearing start of
- -- the history using ex_getln.c/clr_history().
- eq(1, funcs.histadd(':', 'foo'))
- eq(1, funcs.histdel(':'))
- eq('', funcs.histget(':', -1))
+ eq('"<C-J><C-@><C-[><C-S-M><M-C-I><C-D-J>', eval('@:'))
end)
- it('correctly clears end of the history', function()
- -- Regression test: check absence of the memory leak when clearing end of
- -- the history using ex_getln.c/clr_history().
- meths.set_option('history', 1)
- eq(1, funcs.histadd(':', 'foo'))
- eq(1, funcs.histdel(':'))
- eq('', funcs.histget(':', -1))
- end)
+ describe('history', function()
+ it('correctly clears start of the history', function()
+ -- Regression test: check absence of the memory leak when clearing start of
+ -- the history using ex_getln.c/clr_history().
+ eq(1, funcs.histadd(':', 'foo'))
+ eq(1, funcs.histdel(':'))
+ eq('', funcs.histget(':', -1))
+ end)
+
+ it('correctly clears end of the history', function()
+ -- Regression test: check absence of the memory leak when clearing end of
+ -- the history using ex_getln.c/clr_history().
+ meths.set_option('history', 1)
+ eq(1, funcs.histadd(':', 'foo'))
+ eq(1, funcs.histdel(':'))
+ eq('', funcs.histget(':', -1))
+ end)
- it('correctly removes item from history', function()
- -- Regression test: check that ex_getln.c/del_history_idx() correctly clears
- -- history index after removing history entry. If it does not then deleting
- -- history will result in a double free.
- eq(1, funcs.histadd(':', 'foo'))
- eq(1, funcs.histadd(':', 'bar'))
- eq(1, funcs.histadd(':', 'baz'))
- eq(1, funcs.histdel(':', -2))
- eq(1, funcs.histdel(':'))
- eq('', funcs.histget(':', -1))
+ it('correctly removes item from history', function()
+ -- Regression test: check that ex_getln.c/del_history_idx() correctly clears
+ -- history index after removing history entry. If it does not then deleting
+ -- history will result in a double free.
+ eq(1, funcs.histadd(':', 'foo'))
+ eq(1, funcs.histadd(':', 'bar'))
+ eq(1, funcs.histadd(':', 'baz'))
+ eq(1, funcs.histdel(':', -2))
+ eq(1, funcs.histdel(':'))
+ eq('', funcs.histget(':', -1))
+ end)
end)
end)
diff --git a/test/functional/editor/mode_insert_spec.lua b/test/functional/editor/mode_insert_spec.lua
index f03508035d..e3d3cdbd85 100644
--- a/test/functional/editor/mode_insert_spec.lua
+++ b/test/functional/editor/mode_insert_spec.lua
@@ -75,4 +75,62 @@ 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)
+
+ it('Ctrl-Shift-V supports entering unsimplified key notations', function()
+ feed('i<C-S-V><C-J><C-S-V><C-@><C-S-V><C-[><C-S-V><C-S-M><C-S-V><M-C-I><C-S-V><C-D-J><Esc>')
+ expect('<C-J><C-@><C-[><C-S-M><M-C-I><C-D-J>')
+ end)
end)
diff --git a/test/functional/editor/put_spec.lua b/test/functional/editor/put_spec.lua
index 26967ecbba..cc9fce8f67 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
@@ -138,9 +138,9 @@ describe('put command', function()
end -- create_test_defs() }}}
local function find_cursor_position(expect_string) -- {{{
- -- There must only be one occurance of the character 'x' in
+ -- There must only be one occurrence of the character 'x' in
-- expect_string.
- -- This function removes that occurance, and returns the position that
+ -- This function removes that occurrence, and returns the position that
-- it was in.
-- This returns the cursor position that would leave the 'x' in that
-- place if we feed 'ix<esc>' and the string existed before it.
@@ -507,9 +507,11 @@ describe('put command', function()
return function(exception_table, after_redo)
test_expect(exception_table, after_redo)
if selection_string then
- eq(getreg('"'), selection_string)
+ if not conversion_table.put_backwards then
+ eq(selection_string, getreg('"'))
+ end
else
- eq(getreg('"'), 'test_string"')
+ eq('test_string"', getreg('"'))
end
end
end
@@ -714,7 +716,9 @@ 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')
+ if not conversion_table.put_backwards then
+ eq('Line of words 1\n', getreg('"'))
+ end
end
end
local base_expect_string = [[
@@ -748,7 +752,9 @@ describe('put command', function()
end, expect_base, conversion_table)
return function(e,c)
test_expect(e,c)
- eq(getreg('"'), 'Lin\nLin')
+ if not conversion_table.put_backwards then
+ eq('Lin\nLin', getreg('"'))
+ end
end
end
@@ -800,9 +806,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/editor/tabpage_spec.lua b/test/functional/editor/tabpage_spec.lua
index d1d6854b07..3b2c1db350 100644
--- a/test/functional/editor/tabpage_spec.lua
+++ b/test/functional/editor/tabpage_spec.lua
@@ -3,8 +3,11 @@ local helpers = require('test.functional.helpers')(after_each)
local clear = helpers.clear
local command = helpers.command
local eq = helpers.eq
+local neq = helpers.neq
local feed = helpers.feed
local eval = helpers.eval
+local exec = helpers.exec
+local funcs = helpers.funcs
describe('tabpage', function()
before_each(clear)
@@ -34,5 +37,28 @@ describe('tabpage', function()
eq(3, eval('tabpagenr()'))
end)
-end)
+ it('does not crash or loop 999 times if BufWipeout autocommand switches window #17868', function()
+ exec([[
+ tabedit
+ let s:window_id = win_getid()
+ botright new
+ setlocal bufhidden=wipe
+ let g:win_closed = 0
+ autocmd WinClosed * let g:win_closed += 1
+ autocmd BufWipeout <buffer> call win_gotoid(s:window_id)
+ tabprevious
+ +tabclose
+ ]])
+ neq(999, eval('g:win_closed'))
+ end)
+
+ it(":tabmove handles modifiers and addr", function()
+ command('tabnew | tabnew | tabnew')
+ eq(4, funcs.nvim_tabpage_get_number(0))
+ command(' silent :keepalt :: ::: silent! - tabmove')
+ eq(3, funcs.nvim_tabpage_get_number(0))
+ command(' silent :keepalt :: ::: silent! -2 tabmove')
+ eq(1, funcs.nvim_tabpage_get_number(0))
+ end)
+end)
diff --git a/test/functional/editor/undo_spec.lua b/test/functional/editor/undo_spec.lua
index a023ca3d90..a041428cdc 100644
--- a/test/functional/editor/undo_spec.lua
+++ b/test/functional/editor/undo_spec.lua
@@ -2,9 +2,18 @@ local helpers = require('test.functional.helpers')(after_each)
local clear = helpers.clear
local command = helpers.command
+local eval = helpers.eval
local expect = helpers.expect
+local eq = helpers.eq
local feed = helpers.feed
+local feed_command = helpers.feed_command
local insert = helpers.insert
+local funcs = helpers.funcs
+
+local function lastmessage()
+ local messages = funcs.split(funcs.execute('messages'), '\n')
+ return messages[#messages]
+end
describe('u CTRL-R g- g+', function()
before_each(clear)
@@ -59,3 +68,61 @@ describe('u CTRL-R g- g+', function()
undo_and_redo(4, 'g-', 'g+', '1')
end)
end)
+
+describe(':undo! command', function()
+ before_each(function()
+ clear()
+ feed('i1 little bug in the code<Esc>')
+ feed('o1 little bug in the code<Esc>')
+ feed('oTake 1 down, patch it around<Esc>')
+ feed('o99 little bugs in the code<Esc>')
+ end)
+ it('works', function()
+ feed_command('undo!')
+ expect([[
+ 1 little bug in the code
+ 1 little bug in the code
+ Take 1 down, patch it around]])
+ feed('<C-r>')
+ eq('Already at newest change', lastmessage())
+ end)
+ it('works with arguments', function()
+ feed_command('undo! 2')
+ expect([[
+ 1 little bug in the code
+ 1 little bug in the code]])
+ feed('<C-r>')
+ eq('Already at newest change', lastmessage())
+ end)
+ it('correctly sets alternative redo', function()
+ feed('uo101 little bugs in the code<Esc>')
+ feed_command('undo!')
+ feed('<C-r>')
+ expect([[
+ 1 little bug in the code
+ 1 little bug in the code
+ Take 1 down, patch it around
+ 99 little bugs in the code]])
+
+ feed('uuoTake 2 down, patch them around<Esc>')
+ feed('o101 little bugs in the code<Esc>')
+ feed_command('undo! 2')
+ feed('<C-r><C-r>')
+ expect([[
+ 1 little bug in the code
+ 1 little bug in the code
+ Take 1 down, patch it around
+ 99 little bugs in the code]])
+ end)
+ it('fails when attempting to redo or move to different undo branch', function()
+ feed_command('undo! 4')
+ eq('E5767: Cannot use :undo! to redo or move to a different undo branch', eval('v:errmsg'))
+ feed('u')
+ feed_command('undo! 4')
+ eq('E5767: Cannot use :undo! to redo or move to a different undo branch', eval('v:errmsg'))
+ feed('o101 little bugs in the code<Esc>')
+ feed('o101 little bugs in the code<Esc>')
+ feed_command('undo! 4')
+ eq('E5767: Cannot use :undo! to redo or move to a different undo branch', eval('v:errmsg'))
+ end)
+end)
diff --git a/test/functional/ex_cmds/cd_spec.lua b/test/functional/ex_cmds/cd_spec.lua
index f9cce0deb6..42a811f5da 100644
--- a/test/functional/ex_cmds/cd_spec.lua
+++ b/test/functional/ex_cmds/cd_spec.lua
@@ -173,7 +173,7 @@ for _, cmd in ipairs {'cd', 'chdir'} do
-- Change tab-local working directory and verify it is different
command('silent t' .. cmd .. ' ' .. directories.tab)
eq(globalDir .. pathsep .. directories.tab, cwd())
- eq(cwd(), tcwd()) -- working directory maches tab directory
+ eq(cwd(), tcwd()) -- working directory matches tab directory
eq(1, tlwd())
eq(cwd(), wcwd()) -- still no window-directory
eq(0, wlwd())
diff --git a/test/functional/ex_cmds/cmd_map_spec.lua b/test/functional/ex_cmds/cmd_map_spec.lua
index 0b2190bbcf..919d167712 100644
--- a/test/functional/ex_cmds/cmd_map_spec.lua
+++ b/test/functional/ex_cmds/cmd_map_spec.lua
@@ -1,21 +1,23 @@
local helpers = require('test.functional.helpers')(after_each)
local clear = helpers.clear
-local feed_command = helpers.feed_command
local feed = helpers.feed
local eq = helpers.eq
local expect = helpers.expect
local eval = helpers.eval
local funcs = helpers.funcs
local insert = helpers.insert
+local write_file = helpers.write_file
local exc_exec = helpers.exc_exec
-local source = helpers.source
+local command = helpers.command
local Screen = require('test.functional.ui.screen')
describe('mappings with <Cmd>', function()
local screen
+ local tmpfile = 'X_ex_cmds_cmd_map'
+
local function cmdmap(lhs, rhs)
- feed_command('noremap '..lhs..' <Cmd>'..rhs..'<cr>')
- feed_command('noremap! '..lhs..' <Cmd>'..rhs..'<cr>')
+ command('noremap '..lhs..' <Cmd>'..rhs..'<cr>')
+ command('noremap! '..lhs..' <Cmd>'..rhs..'<cr>')
end
before_each(function()
@@ -39,7 +41,7 @@ describe('mappings with <Cmd>', function()
cmdmap('<F4>', 'normal! ww')
cmdmap('<F5>', 'normal! "ay')
cmdmap('<F6>', 'throw "very error"')
- feed_command([[
+ command([[
function! TextObj()
if mode() !=# "v"
normal! v
@@ -55,11 +57,15 @@ describe('mappings with <Cmd>', function()
feed('gg')
cmdmap('<F8>', 'startinsert')
cmdmap('<F9>', 'stopinsert')
- feed_command("abbr foo <Cmd>let g:y = 17<cr>bar")
+ command("abbr foo <Cmd>let g:y = 17<cr>bar")
+ end)
+
+ after_each(function()
+ os.remove(tmpfile)
end)
it('can be displayed', function()
- feed_command('map <F3>')
+ command('map <F3>')
screen:expect([[
^some short lines |
of test text |
@@ -73,8 +79,8 @@ describe('mappings with <Cmd>', function()
end)
it('handles invalid mappings', function()
- feed_command('let x = 0')
- feed_command('noremap <F3> <Cmd><Cmd>let x = 1<cr>')
+ command('let x = 0')
+ command('noremap <F3> <Cmd><Cmd>let x = 1<cr>')
feed('<F3>')
screen:expect([[
^some short lines |
@@ -87,7 +93,7 @@ describe('mappings with <Cmd>', function()
{2:E5521: <Cmd> mapping must end with <CR> before second <Cmd>} |
]])
- feed_command('noremap <F3> <Cmd><F3>let x = 2<cr>')
+ command('noremap <F3> <Cmd>let x = 3')
feed('<F3>')
screen:expect([[
^some short lines |
@@ -97,22 +103,50 @@ describe('mappings with <Cmd>', function()
{1:~ }|
{1:~ }|
{1:~ }|
- {2:E5522: <Cmd> mapping must not include <F3> key} |
+ {2:E5520: <Cmd> mapping must end with <CR>} |
]])
+ eq(0, eval('x'))
+ end)
- feed_command('noremap <F3> <Cmd>let x = 3')
+ it('allows special keys and modifiers', function()
+ command('noremap <F3> <Cmd>normal! <Down><CR>')
feed('<F3>')
screen:expect([[
- ^some short lines |
- of test text |
+ some short lines |
+ ^of test text |
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
- {2:E5520: <Cmd> mapping must end with <CR>} |
+ |
]])
- eq(0, eval('x'))
+
+ command('noremap <F3> <Cmd>normal! <C-Right><CR>')
+ feed('<F3>')
+ screen:expect([[
+ some short lines |
+ of ^test text |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ end)
+
+ it('handles string containing K_SPECIAL (0x80) bytes correctly', function()
+ command([[noremap <F3> <Cmd>let g:str = 'foo…bar'<CR>]])
+ feed('<F3>')
+ eq('foo…bar', eval('g:str'))
+ local str = eval([["foo\<D-…>bar"]])
+ command([[noremap <F3> <Cmd>let g:str = ']]..str..[['<CR>]])
+ feed('<F3>')
+ eq(str, eval('g:str'))
+ command([[noremap <F3> <Cmd>let g:str = 'foo<D-…>bar'<CR>]])
+ feed('<F3>')
+ eq(str, eval('g:str'))
end)
it('works in various modes and sees correct `mode()` value', function()
@@ -137,7 +171,7 @@ describe('mappings with <Cmd>', function()
eq('n', eval('mode(1)'))
-- select mode mapping
- feed_command('snoremap <F3> <Cmd>let m = mode(1)<cr>')
+ command('snoremap <F3> <Cmd>let m = mode(1)<cr>')
feed('gh<F3>')
eq('s', eval('m'))
-- didn't leave select mode
@@ -184,8 +218,8 @@ describe('mappings with <Cmd>', function()
eq('n', eval('mode(1)'))
-- terminal mode
- feed_command('tnoremap <F3> <Cmd>let m = mode(1)<cr>')
- feed_command('split | terminal')
+ command('tnoremap <F3> <Cmd>let m = mode(1)<cr>')
+ command('split | terminal')
feed('i')
eq('t', eval('mode(1)'))
feed('<F3>')
@@ -264,11 +298,11 @@ describe('mappings with <Cmd>', function()
end)
it('works in :normal command', function()
- feed_command('noremap ,x <Cmd>call append(1, "xx")\\| call append(1, "aa")<cr>')
- feed_command('noremap ,f <Cmd>nosuchcommand<cr>')
- feed_command('noremap ,e <Cmd>throw "very error"\\| call append(1, "yy")<cr>')
- feed_command('noremap ,m <Cmd>echoerr "The message."\\| call append(1, "zz")<cr>')
- feed_command('noremap ,w <Cmd>for i in range(5)\\|if i==1\\|echoerr "Err"\\|endif\\|call append(1, i)\\|endfor<cr>')
+ command('noremap ,x <Cmd>call append(1, "xx")\\| call append(1, "aa")<cr>')
+ command('noremap ,f <Cmd>nosuchcommand<cr>')
+ command('noremap ,e <Cmd>throw "very error"\\| call append(1, "yy")<cr>')
+ command('noremap ,m <Cmd>echoerr "The message."\\| call append(1, "zz")<cr>')
+ command('noremap ,w <Cmd>for i in range(5)\\|if i==1\\|echoerr "Err"\\|endif\\|call append(1, i)\\|endfor<cr>')
feed(":normal ,x<cr>")
screen:expect([[
@@ -297,7 +331,7 @@ describe('mappings with <Cmd>', function()
:normal ,x |
]])
- feed_command(':%d')
+ command(':%d')
eq('Vim(echoerr):Err', exc_exec("normal ,w"))
screen:expect([[
^ |
@@ -310,8 +344,8 @@ describe('mappings with <Cmd>', function()
--No lines in buffer-- |
]])
- feed_command(':%d')
- feed_command(':normal ,w')
+ command(':%d')
+ feed(':normal ,w<cr>')
screen:expect([[
^ |
4 |
@@ -401,8 +435,8 @@ describe('mappings with <Cmd>', function()
end)
it('works in select mode', function()
- feed_command('snoremap <F1> <cmd>throw "very error"<cr>')
- feed_command('snoremap <F2> <cmd>normal! <c-g>"by<cr>')
+ command('snoremap <F1> <cmd>throw "very error"<cr>')
+ command('snoremap <F2> <cmd>normal! <c-g>"by<cr>')
-- can extend select mode
feed('gh<F4>')
screen:expect([[
@@ -830,12 +864,14 @@ describe('mappings with <Cmd>', function()
end)
it("works with <SID> mappings", function()
- source([[
+ command('new!')
+ write_file(tmpfile, [[
map <f2> <Cmd>call <SID>do_it()<Cr>
function! s:do_it()
let g:x = 10
endfunction
]])
+ command('source '..tmpfile)
feed('<f2>')
eq('', eval('v:errmsg'))
eq(10, eval('g:x'))
diff --git a/test/functional/ex_cmds/drop_spec.lua b/test/functional/ex_cmds/drop_spec.lua
index 9d84a2d4f6..2537ab9cdc 100644
--- a/test/functional/ex_cmds/drop_spec.lua
+++ b/test/functional/ex_cmds/drop_spec.lua
@@ -41,14 +41,14 @@ describe(":drop", function()
feed_command("edit tmp2")
feed_command("drop tmp1")
screen:expect([[
- {2:│}^ |
- {0:~ }{2:│}{0:~ }|
- {0:~ }{2:│}{0:~ }|
- {0:~ }{2:│}{0:~ }|
- {0:~ }{2:│}{0:~ }|
- {0:~ }{2:│}{0:~ }|
- {0:~ }{2:│}{0:~ }|
- {0:~ }{2:│}{0:~ }|
+ │^ |
+ {0:~ }│{0:~ }|
+ {0:~ }│{0:~ }|
+ {0:~ }│{0:~ }|
+ {0:~ }│{0:~ }|
+ {0:~ }│{0:~ }|
+ {0:~ }│{0:~ }|
+ {0:~ }│{0:~ }|
{2:tmp2 }{1:tmp1 }|
:drop tmp1 |
]])
@@ -62,14 +62,14 @@ describe(":drop", function()
feed("iABC<esc>")
feed_command("drop tmp3")
screen:expect([[
- ^ {2:│} |
- {0:~ }{2:│}{0:~ }|
- {0:~ }{2:│}{0:~ }|
- {0:~ }{2:│}{0:~ }|
- {1:tmp3 }{2:│}{0:~ }|
- ABC {2:│}{0:~ }|
- {0:~ }{2:│}{0:~ }|
- {0:~ }{2:│}{0:~ }|
+ ^ │ |
+ {0:~ }│{0:~ }|
+ {0:~ }│{0:~ }|
+ {0:~ }│{0:~ }|
+ {1:tmp3 }│{0:~ }|
+ ABC │{0:~ }|
+ {0:~ }│{0:~ }|
+ {0:~ }│{0:~ }|
{2:tmp2 [+] tmp1 }|
"tmp3" [New] |
]])
diff --git a/test/functional/ex_cmds/echo_spec.lua b/test/functional/ex_cmds/echo_spec.lua
index d320425de1..a6be04138b 100644
--- a/test/functional/ex_cmds/echo_spec.lua
+++ b/test/functional/ex_cmds/echo_spec.lua
@@ -181,9 +181,9 @@ describe(':echo :echon :echomsg :echoerr', function()
end)
it('dumps references to script functions', function()
- eq('<SNR>2_Test2', eval('String(Test2_f)'))
- eq("function('<SNR>2_Test2')", eval('StringMsg(Test2_f)'))
- eq("function('<SNR>2_Test2')", eval('StringErr(Test2_f)'))
+ eq('<SNR>1_Test2', eval('String(Test2_f)'))
+ eq("function('<SNR>1_Test2')", eval('StringMsg(Test2_f)'))
+ eq("function('<SNR>1_Test2')", eval('StringErr(Test2_f)'))
end)
it('dump references to lambdas', function()
@@ -205,11 +205,11 @@ describe(':echo :echon :echomsg :echoerr', function()
it('dumps automatically created partials', function()
assert_same_echo_dump(
- "function('<SNR>2_Test2', {'f': function('<SNR>2_Test2')})",
+ "function('<SNR>1_Test2', {'f': function('<SNR>1_Test2')})",
'{"f": Test2_f}.f',
true)
assert_same_echo_dump(
- "function('<SNR>2_Test2', [1], {'f': function('<SNR>2_Test2', [1])})",
+ "function('<SNR>1_Test2', [1], {'f': function('<SNR>1_Test2', [1])})",
'{"f": function(Test2_f, [1])}.f',
true)
end)
@@ -227,7 +227,7 @@ describe(':echo :echon :echomsg :echoerr', function()
function()
meths.set_var('d', {v=true})
eq(dedent([[
- {'p': function('<SNR>2_Test2', {...@0}), 'f': function('<SNR>2_Test2'), 'v': v:true}]]),
+ {'p': function('<SNR>1_Test2', {...@0}), 'f': function('<SNR>1_Test2'), 'v': v:true}]]),
exec_capture('echo String(extend(extend(g:d, {"f": g:Test2_f}), {"p": g:d.f}))'))
end)
@@ -264,7 +264,7 @@ describe(':echo :echon :echomsg :echoerr', function()
eval('add(l, function("Test1", l))')
eval('add(l, function("Test1", d))')
eq(dedent([=[
- {'p': function('<SNR>2_Test2', [[[...@3], function('Test1', [[...@3]]), function('Test1', {...@0})], function('Test1', [[[...@5], function('Test1', [[...@5]]), function('Test1', {...@0})]]), function('Test1', {...@0})], {...@0}), 'f': function('<SNR>2_Test2'), 'v': v:true}]=]),
+ {'p': function('<SNR>1_Test2', [[[...@3], function('Test1', [[...@3]]), function('Test1', {...@0})], function('Test1', [[[...@5], function('Test1', [[...@5]]), function('Test1', {...@0})]]), function('Test1', {...@0})], {...@0}), 'f': function('<SNR>1_Test2'), 'v': v:true}]=]),
exec_capture('echo String(extend(extend(g:d, {"f": g:Test2_f}), {"p": function(g:d.f, l)}))'))
end)
end)
diff --git a/test/functional/ex_cmds/ls_spec.lua b/test/functional/ex_cmds/ls_spec.lua
index 9853084c47..2583d80269 100644
--- a/test/functional/ex_cmds/ls_spec.lua
+++ b/test/functional/ex_cmds/ls_spec.lua
@@ -5,7 +5,7 @@ local eq = helpers.eq
local eval = helpers.eval
local feed = helpers.feed
local nvim = helpers.nvim
-local nvim_dir = helpers.nvim_dir
+local testprg = helpers.testprg
local retry = helpers.retry
describe(':ls', function()
@@ -14,7 +14,7 @@ describe(':ls', function()
end)
it('R, F for :terminal buffers', function()
- nvim('set_option', 'shell', string.format('"%s" INTERACT', nvim_dir..'/shell-test'))
+ nvim('set_option', 'shell', string.format('"%s" INTERACT', testprg('shell-test')))
command('edit foo')
command('set hidden')
diff --git a/test/functional/ex_cmds/make_spec.lua b/test/functional/ex_cmds/make_spec.lua
index 3b4d22ab38..bf585ee44c 100644
--- a/test/functional/ex_cmds/make_spec.lua
+++ b/test/functional/ex_cmds/make_spec.lua
@@ -4,7 +4,7 @@ local eval = helpers.eval
local has_powershell = helpers.has_powershell
local matches = helpers.matches
local nvim = helpers.nvim
-local nvim_dir = helpers.nvim_dir
+local testprg = helpers.testprg
describe(':make', function()
clear()
@@ -22,7 +22,7 @@ describe(':make', function()
end)
it('captures stderr & non zero exit code #14349', function ()
- nvim('set_option', 'makeprg', nvim_dir..'/shell-test foo')
+ nvim('set_option', 'makeprg', testprg('shell-test')..' foo')
local out = eval('execute("make")')
-- Make program exit code correctly captured
matches('\nshell returned 3', out)
@@ -31,7 +31,7 @@ describe(':make', function()
end)
it('captures stderr & zero exit code #14349', function ()
- nvim('set_option', 'makeprg', nvim_dir..'/shell-test')
+ nvim('set_option', 'makeprg', testprg('shell-test'))
local out = eval('execute("make")')
-- Ensure there are no "shell returned X" messages between
-- command and last line (indicating zero exit)
diff --git a/test/functional/ex_cmds/map_spec.lua b/test/functional/ex_cmds/map_spec.lua
index 84d5bc2335..c6bdd017bd 100644
--- a/test/functional/ex_cmds/map_spec.lua
+++ b/test/functional/ex_cmds/map_spec.lua
@@ -1,11 +1,15 @@
local helpers = require("test.functional.helpers")(after_each)
+local Screen = require('test.functional.ui.screen')
local eq = helpers.eq
+local exec = helpers.exec
local feed = helpers.feed
local meths = helpers.meths
local clear = helpers.clear
local command = helpers.command
local expect = helpers.expect
+local insert = helpers.insert
+local eval = helpers.eval
describe(':*map', function()
before_each(clear)
@@ -25,4 +29,233 @@ describe(':*map', function()
feed('i-<M-">-')
expect('-foo-')
end)
+
+ it('shows <nop> as mapping rhs', function()
+ command('nmap asdf <Nop>')
+ eq([[
+
+n asdf <Nop>]],
+ helpers.exec_capture('nmap asdf'))
+ end)
+
+ it('mappings with description can be filtered', function()
+ meths.set_keymap('n', 'asdf1', 'qwert', {desc='do the one thing'})
+ meths.set_keymap('n', 'asdf2', 'qwert', {desc='doesnot really do anything'})
+ meths.set_keymap('n', 'asdf3', 'qwert', {desc='do the other thing'})
+ eq([[
+
+n asdf3 qwert
+ do the other thing
+n asdf1 qwert
+ do the one thing]],
+ helpers.exec_capture('filter the nmap'))
+ end)
+
+ it('<Plug> mappings ignore nore', function()
+ command('let x = 0')
+ eq(0, meths.eval('x'))
+ command [[
+ nnoremap <Plug>(Increase_x) <cmd>let x+=1<cr>
+ nmap increase_x_remap <Plug>(Increase_x)
+ nnoremap increase_x_noremap <Plug>(Increase_x)
+ ]]
+ feed('increase_x_remap')
+ eq(1, meths.eval('x'))
+ feed('increase_x_noremap')
+ eq(2, meths.eval('x'))
+ end)
+
+ it("Doesn't auto ignore nore for keys before or after <Plug> mapping", function()
+ command('let x = 0')
+ eq(0, meths.eval('x'))
+ command [[
+ nnoremap x <nop>
+ nnoremap <Plug>(Increase_x) <cmd>let x+=1<cr>
+ nmap increase_x_remap x<Plug>(Increase_x)x
+ nnoremap increase_x_noremap x<Plug>(Increase_x)x
+ ]]
+ insert("Some text")
+ eq('Some text', eval("getline('.')"))
+
+ feed('increase_x_remap')
+ eq(1, meths.eval('x'))
+ eq('Some text', eval("getline('.')"))
+ feed('increase_x_noremap')
+ eq(2, meths.eval('x'))
+ eq('Some te', eval("getline('.')"))
+ end)
+end)
+
+describe('Screen', function()
+ local screen
+ before_each(function()
+ clear()
+ screen = Screen.new(20, 5)
+ screen:attach()
+ end)
+
+ it('cursor is restored after :map <expr> which calls input()', function()
+ command('map <expr> x input("> ")')
+ screen:expect([[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ |
+ ]])
+ feed('x')
+ screen:expect([[
+ |
+ ~ |
+ ~ |
+ ~ |
+ > ^ |
+ ]])
+ feed('\n')
+ screen:expect([[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ > |
+ ]])
+ end)
+
+ it('cursor is restored after :imap <expr> which calls input()', function()
+ command('imap <expr> x input("> ")')
+ feed('i')
+ screen:expect([[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ -- INSERT -- |
+ ]])
+ feed('x')
+ screen:expect([[
+ |
+ ~ |
+ ~ |
+ ~ |
+ > ^ |
+ ]])
+ feed('\n')
+ screen:expect([[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ > |
+ ]])
+ end)
+
+ it('cursor position does not move after empty-string :cmap <expr> #19046', function()
+ command([[cnoremap <expr> <F2> '']])
+ feed(':<F2>')
+ screen:expect([[
+ |
+ ~ |
+ ~ |
+ ~ |
+ :^ |
+ ]])
+ end)
+
+ it('cursor is restored after :map <expr> which redraws statusline vim-patch:8.1.2336', function()
+ exec([[
+ call setline(1, ['one', 'two', 'three'])
+ 2
+ set ls=2
+ hi! link StatusLine ErrorMsg
+ noremap <expr> <C-B> Func()
+ func Func()
+ let g:on = !get(g:, 'on', 0)
+ redraws
+ return ''
+ endfunc
+ func Status()
+ return get(g:, 'on', 0) ? '[on]' : ''
+ endfunc
+ set stl=%{Status()}
+ ]])
+ feed('<C-B>')
+ screen:expect([[
+ one |
+ ^two |
+ three |
+ [on] |
+ |
+ ]])
+ end)
+
+ it('error in :nmap <expr> does not mess up display vim-patch:4.2.4338', function()
+ screen:try_resize(40, 5)
+ command('nmap <expr> <F2> execute("throw 42")')
+ feed('<F2>')
+ screen:expect([[
+ |
+ |
+ Error detected while processing : |
+ E605: Exception not caught: 42 |
+ Press ENTER or type command to continue^ |
+ ]])
+ feed('<CR>')
+ screen:expect([[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ |
+ ]])
+ end)
+
+ it('error in :cmap <expr> handled correctly vim-patch:4.2.4338', function()
+ screen:try_resize(40, 5)
+ command('cmap <expr> <F2> execute("throw 42")')
+ feed(':echo "foo')
+ screen:expect([[
+ |
+ ~ |
+ ~ |
+ ~ |
+ :echo "foo^ |
+ ]])
+ feed('<F2>')
+ screen:expect([[
+ |
+ :echo "foo |
+ Error detected while processing : |
+ E605: Exception not caught: 42 |
+ :echo "foo^ |
+ ]])
+ feed('"')
+ screen:expect([[
+ |
+ :echo "foo |
+ Error detected while processing : |
+ E605: Exception not caught: 42 |
+ :echo "foo"^ |
+ ]])
+ feed('\n')
+ screen:expect([[
+ :echo "foo |
+ Error detected while processing : |
+ E605: Exception not caught: 42 |
+ foo |
+ Press ENTER or type command to continue^ |
+ ]])
+ end)
+
+ it('listing mappings clears command line vim-patch:8.2.4401', function()
+ screen:try_resize(40, 5)
+ command('nmap a b')
+ feed(': nmap a<CR>')
+ screen:expect([[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ n a b |
+ ]])
+ end)
end)
diff --git a/test/functional/ex_cmds/menu_spec.lua b/test/functional/ex_cmds/menu_spec.lua
index 0cd32df27c..b9ed32c328 100644
--- a/test/functional/ex_cmds/menu_spec.lua
+++ b/test/functional/ex_cmds/menu_spec.lua
@@ -64,6 +64,8 @@ describe('menu_get', function()
before_each(function()
clear()
command([=[
+ aunmenu *
+
nnoremenu &Test.Test inormal<ESC>
inoremenu Test.Test insert
vnoremenu Test.Test x
@@ -396,6 +398,7 @@ describe('menu_get', function()
before_each(function()
clear()
+ command('aunmenu *')
end)
it('returns <keycode> representation of special keys', function()
diff --git a/test/functional/ex_cmds/mksession_spec.lua b/test/functional/ex_cmds/mksession_spec.lua
index 09eaa36686..1553de4432 100644
--- a/test/functional/ex_cmds/mksession_spec.lua
+++ b/test/functional/ex_cmds/mksession_spec.lua
@@ -1,14 +1,20 @@
local lfs = require('lfs')
local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
local clear = helpers.clear
local command = helpers.command
local get_pathsep = helpers.get_pathsep
+local iswin = helpers.iswin
local eq = helpers.eq
+local neq = helpers.neq
local funcs = helpers.funcs
local matches = helpers.matches
local pesc = helpers.pesc
local rmdir = helpers.rmdir
+local sleep = helpers.sleep
+local meths = helpers.meths
+local expect_exit = helpers.expect_exit
local file_prefix = 'Xtest-functional-ex_cmds-mksession_spec'
@@ -30,7 +36,8 @@ describe(':mksession', function()
-- If the same :terminal is displayed in multiple windows, :mksession
-- should restore it as such.
- -- Create two windows showing the same :terminal buffer.
+ -- Create three windows: first two from top show same terminal, third -
+ -- another one (created earlier).
command('terminal')
command('split')
command('terminal')
@@ -38,13 +45,13 @@ describe(':mksession', function()
command('mksession '..session_file)
-- Create a new test instance of Nvim.
- command('qall!')
+ expect_exit(command, 'qall!')
clear()
-- Restore session.
command('source '..session_file)
- eq({2,2,4},
- {funcs.winbufnr(1), funcs.winbufnr(2), funcs.winbufnr(3)})
+ eq(funcs.winbufnr(1), funcs.winbufnr(2))
+ neq(funcs.winbufnr(1), funcs.winbufnr(3))
end)
it('restores tab-local working directories', function()
@@ -91,12 +98,7 @@ describe(':mksession', function()
command('tabnext 1')
eq(cwd_dir .. get_pathsep() .. tmpfile_base .. '1', funcs.expand('%:p'))
command('tabnext 2')
- -- :mksession stores paths using unix slashes, but Nvim doesn't adjust these
- -- for absolute paths in all cases yet. Absolute paths are used in the
- -- session file after :tcd, so we need to expect unix slashes here for now
- -- eq(cwd_dir .. get_pathsep() .. tmpfile_base .. '2', funcs.expand('%:p'))
- eq(cwd_dir:gsub([[\]], '/') .. '/' .. tmpfile_base .. '2',
- funcs.expand('%:p'))
+ eq(cwd_dir .. get_pathsep() .. tmpfile_base .. '2', funcs.expand('%:p'))
end)
it('restores CWD for :terminal buffers #11288', function()
@@ -105,10 +107,14 @@ describe(':mksession', function()
local session_path = cwd_dir..'/'..session_file
command('cd '..tab_dir)
- command('terminal echo $PWD')
+ command('terminal')
command('cd '..cwd_dir)
command('mksession '..session_path)
- command('qall!')
+ command('bdelete!')
+ if iswin() then
+ sleep(100) -- Make sure all child processes have exited.
+ end
+ expect_exit(command, 'qall!')
-- Create a new test instance of Nvim.
clear()
@@ -116,6 +122,83 @@ describe(':mksession', function()
local expected_cwd = cwd_dir..'/'..tab_dir
matches('^term://'..pesc(expected_cwd)..'//%d+:', funcs.expand('%'))
- command('qall!')
+ command('bdelete!')
+ if iswin() then
+ sleep(100) -- Make sure all child processes have exited.
+ end
+ end)
+
+ it('restores CWD for :terminal buffer at root directory #16988', function()
+ if iswin() then
+ pending('N/A for Windows')
+ return
+ end
+
+ local screen
+ local cwd_dir = funcs.fnamemodify('.', ':p:~'):gsub([[[\/]*$]], '')
+ local session_path = cwd_dir..'/'..session_file
+
+ screen = Screen.new(50, 6)
+ screen:attach({rgb=false})
+ local expected_screen = [[
+ ^/ |
+ |
+ [Process exited 0] |
+ |
+ |
+ |
+ ]]
+
+ command('cd /')
+ command('terminal echo $PWD')
+
+ -- Verify that the terminal's working directory is "/".
+ screen:expect(expected_screen)
+
+ command('cd '..cwd_dir)
+ command('mksession '..session_path)
+ expect_exit(command, 'qall!')
+
+ -- Create a new test instance of Nvim.
+ clear()
+ screen = Screen.new(50, 6)
+ screen:attach({rgb=false})
+ command('silent source '..session_path)
+
+ -- Verify that the terminal's working directory is "/".
+ screen:expect(expected_screen)
+ end)
+
+ it('restores a session when there is a float #18432', function()
+ local tmpfile = file_prefix .. '-tmpfile-float'
+
+ command('edit ' .. tmpfile)
+ local buf = meths.create_buf(false, true)
+ local config = {
+ relative = 'editor',
+ focusable = false,
+ width = 10,
+ height = 3,
+ row = 0,
+ col = 1,
+ style = 'minimal'
+ }
+ meths.open_win(buf, false, config)
+ local cmdheight = meths.get_option('cmdheight')
+ command('mksession ' .. session_file)
+
+ -- Create a new test instance of Nvim.
+ clear()
+
+ command('source ' .. session_file)
+
+ eq(tmpfile, funcs.expand('%'))
+ -- Check that there is only a single window, which indicates the floating
+ -- window was not restored.
+ eq(1, funcs.winnr('$'))
+ -- The command-line height should remain the same as it was.
+ eq(cmdheight, meths.get_option('cmdheight'))
+
+ os.remove(tmpfile)
end)
end)
diff --git a/test/functional/ex_cmds/normal_spec.lua b/test/functional/ex_cmds/normal_spec.lua
new file mode 100644
index 0000000000..f6e7dd2b3a
--- /dev/null
+++ b/test/functional/ex_cmds/normal_spec.lua
@@ -0,0 +1,27 @@
+local helpers = require('test.functional.helpers')(after_each)
+local clear = helpers.clear
+local command = helpers.command
+local feed = helpers.feed
+local expect = helpers.expect
+local eq = helpers.eq
+local eval = helpers.eval
+
+before_each(clear)
+
+describe(':normal', function()
+ it('can get out of Insert mode if called from Ex mode #17924', function()
+ feed('gQnormal! Ifoo<CR>')
+ expect('foo')
+ end)
+
+ it('normal! does not execute command in Ex mode when running out of characters', function()
+ command('let g:var = 0')
+ command('normal! gQlet g:var = 1')
+ eq(0, eval('g:var'))
+ end)
+
+ it('normal! gQinsert does not hang #17980', function()
+ command('normal! gQinsert')
+ expect('')
+ end)
+end)
diff --git a/test/functional/ex_cmds/quickfix_commands_spec.lua b/test/functional/ex_cmds/quickfix_commands_spec.lua
index c956a2df2d..94b7fa1a84 100644
--- a/test/functional/ex_cmds/quickfix_commands_spec.lua
+++ b/test/functional/ex_cmds/quickfix_commands_spec.lua
@@ -109,4 +109,17 @@ describe('quickfix', function()
]])
eq({0, 6, 1, 0, 1}, funcs.getcurpos())
end)
+
+ it('BufAdd does not cause E16 when reusing quickfix buffer #18135', function()
+ local file = file_base .. '_reuse_qfbuf_BufAdd'
+ write_file(file, ('\n'):rep(100) .. 'foo')
+ source([[
+ set grepprg=internal
+ autocmd BufAdd * call and(0, 0)
+ autocmd QuickFixCmdPost grep ++nested cclose | cwindow
+ ]])
+ command('grep foo ' .. file)
+ command('grep foo ' .. file)
+ os.remove(file)
+ end)
end)
diff --git a/test/functional/ex_cmds/script_spec.lua b/test/functional/ex_cmds/script_spec.lua
index 0a772c559b..bf69ada820 100644
--- a/test/functional/ex_cmds/script_spec.lua
+++ b/test/functional/ex_cmds/script_spec.lua
@@ -2,18 +2,30 @@ local helpers = require('test.functional.helpers')(after_each)
local eq = helpers.eq
local neq = helpers.neq
+local command = helpers.command
+local write_file = helpers.write_file
local meths = helpers.meths
local clear = helpers.clear
local dedent = helpers.dedent
-local source = helpers.source
local exc_exec = helpers.exc_exec
local missing_provider = helpers.missing_provider
+local tmpfile = 'X_ex_cmds_script'
+
before_each(clear)
+local function source(code)
+ write_file(tmpfile, code)
+ command('source '..tmpfile)
+end
+
describe('script_get-based command', function()
local garbage = ')}{+*({}]*[;(+}{&[]}{*])('
+ after_each(function()
+ os.remove(tmpfile)
+ end)
+
local function test_garbage_exec(cmd, check_neq)
describe(cmd, function()
it('works correctly when skipping oneline variant', function()
@@ -62,10 +74,10 @@ describe('script_get-based command', function()
-- Provider-based scripts
test_garbage_exec('ruby', not missing_provider('ruby'))
- test_garbage_exec('python', not missing_provider('python'))
test_garbage_exec('python3', not missing_provider('python3'))
-- Missing scripts
+ test_garbage_exec('python', false)
test_garbage_exec('tcl', false)
test_garbage_exec('mzscheme', false)
test_garbage_exec('perl', false)
diff --git a/test/functional/ex_cmds/sign_spec.lua b/test/functional/ex_cmds/sign_spec.lua
index 891cfe1670..f280a45174 100644
--- a/test/functional/ex_cmds/sign_spec.lua
+++ b/test/functional/ex_cmds/sign_spec.lua
@@ -1,5 +1,5 @@
local helpers = require('test.functional.helpers')(after_each)
-local clear, nvim, eq = helpers.clear, helpers.nvim, helpers.eq
+local clear, nvim, eq, assert_alive = helpers.clear, helpers.nvim, helpers.eq, helpers.assert_alive
describe('sign', function()
before_each(clear)
@@ -21,4 +21,11 @@ describe('sign', function()
end)
end)
end)
+
+ describe('define {id}', function()
+ it ('does not leak memory when specifying multiple times the same argument', function()
+ nvim('command', 'sign define Foo culhl=Normal culhl=Normal')
+ assert_alive()
+ end)
+ end)
end)
diff --git a/test/functional/ex_cmds/source_spec.lua b/test/functional/ex_cmds/source_spec.lua
index fa650d611b..13a40fcc53 100644
--- a/test/functional/ex_cmds/source_spec.lua
+++ b/test/functional/ex_cmds/source_spec.lua
@@ -19,6 +19,26 @@ describe(':source', function()
clear()
end)
+ it('sourcing a file that is deleted and recreated is consistent vim-patch:8.1.0151', function()
+ local test_file = 'Xfile.vim'
+ local other_file = 'Xfoobar'
+ local script = [[
+ func Func()
+ endfunc
+ ]]
+ write_file(test_file, script)
+ command('source ' .. test_file)
+ os.remove(test_file)
+ write_file(test_file, script)
+ command('source ' .. test_file)
+ os.remove(test_file)
+ write_file(other_file, '')
+ write_file(test_file, script)
+ command('source ' .. test_file)
+ os.remove(other_file)
+ os.remove(test_file)
+ end)
+
it('current buffer', function()
insert([[
let a = 2
diff --git a/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua b/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua
index d91feb4bc1..4d984af41e 100644
--- a/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua
+++ b/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua
@@ -10,6 +10,7 @@ local feed = helpers.feed
local nvim_prog = helpers.nvim_prog
local ok = helpers.ok
local rmdir = helpers.rmdir
+local os_kill = helpers.os_kill
local set_session = helpers.set_session
local spawn = helpers.spawn
local nvim_async = helpers.nvim_async
@@ -62,6 +63,7 @@ describe(':preserve', function()
local swappath1 = eval('g:swapname')
+ os_kill(eval('getpid()'))
-- Start another Nvim instance.
local nvim2 = spawn({nvim_prog, '-u', 'NONE', '-i', 'NONE', '--embed'},
true)
@@ -122,6 +124,7 @@ describe('swapfile detection', function()
feed('isometext<esc>')
command('preserve')
+ os_kill(eval('getpid()'))
-- Start another Nvim instance.
local nvim2 = spawn({nvim_prog, '-u', 'NONE', '-i', 'NONE', '--embed'},
true)
diff --git a/test/functional/ex_cmds/verbose_spec.lua b/test/functional/ex_cmds/verbose_spec.lua
new file mode 100644
index 0000000000..e6f67ef18e
--- /dev/null
+++ b/test/functional/ex_cmds/verbose_spec.lua
@@ -0,0 +1,168 @@
+local helpers = require('test.functional.helpers')(after_each)
+
+local clear = helpers.clear
+local eq = helpers.eq
+local exec = helpers.exec
+local exec_capture = helpers.exec_capture
+local write_file = helpers.write_file
+local call_viml_function = helpers.meths.call_function
+
+describe('lua :verbose', function()
+ local script_location, script_file
+ -- All test cases below use the same nvim instance.
+ setup(function()
+ clear{args={'-V1'}}
+ script_file = 'test_verbose.lua'
+ local current_dir = call_viml_function('getcwd', {})
+ current_dir = call_viml_function('fnamemodify', {current_dir, ':~'})
+ script_location = table.concat{current_dir, helpers.get_pathsep(), script_file}
+
+ write_file(script_file, [[
+vim.api.nvim_set_option('hlsearch', false)
+vim.bo.expandtab = true
+vim.opt.number = true
+vim.api.nvim_set_keymap('n', '<leader>key1', ':echo "test"<cr>', {noremap = true})
+vim.keymap.set('n', '<leader>key2', ':echo "test"<cr>')
+
+vim.api.nvim_exec("augroup test_group\
+ autocmd!\
+ autocmd FileType c setl cindent\
+ augroup END\
+ ", false)
+
+vim.api.nvim_command("command Bdelete :bd")
+vim.api.nvim_create_user_command("TestCommand", ":echo 'Hello'", {})
+
+vim.api.nvim_exec ("\
+function Close_Window() abort\
+ wincmd -\
+endfunction\
+", false)
+
+local ret = vim.api.nvim_exec ("\
+function! s:return80()\
+ return 80\
+endfunction\
+let &tw = s:return80()\
+", true)
+]])
+ exec(':source '..script_file)
+ end)
+
+ teardown(function()
+ os.remove(script_file)
+ end)
+
+ it('"Last set" for option set by Lua', function()
+ local result = exec_capture(':verbose set hlsearch?')
+ eq(string.format([[
+nohlsearch
+ Last set from %s line 1]],
+ script_location), result)
+ end)
+
+ it('"Last set" for option set by vim.o', function()
+ local result = exec_capture(':verbose set expandtab?')
+ eq(string.format([[
+ expandtab
+ Last set from %s line 2]],
+ script_location), result)
+ end)
+
+ it('"Last set" for option set by vim.opt', function()
+ local result = exec_capture(':verbose set number?')
+ eq(string.format([[
+ number
+ Last set from %s line 3]],
+ script_location), result)
+ end)
+
+ it('"Last set" for keymap set by Lua', function()
+ local result = exec_capture(':verbose map <leader>key1')
+ eq(string.format([[
+
+n \key1 * :echo "test"<CR>
+ Last set from %s line 4]],
+ script_location), result)
+ end)
+
+ it('"Last set" for keymap set by vim.keymap', function()
+ local result = exec_capture(':verbose map <leader>key2')
+ eq(string.format([[
+
+n \key2 * :echo "test"<CR>
+ Last set from %s line 5]],
+ script_location), result)
+ end)
+
+ it('"Last set" for autocmd by vim.api.nvim_exec', function()
+ local result = exec_capture(':verbose autocmd test_group Filetype c')
+ eq(string.format([[
+--- Autocommands ---
+test_group FileType
+ c setl cindent
+ Last set from %s line 7]],
+ script_location), result)
+ end)
+
+ it('"Last set" for command defined by nvim_command', function()
+ local result = exec_capture(':verbose command Bdelete')
+ eq(string.format([[
+ Name Args Address Complete Definition
+ Bdelete 0 :bd
+ Last set from %s line 13]],
+ script_location), result)
+ end)
+
+ it('"Last set" for command defined by nvim_create_user_command', function()
+ local result = exec_capture(':verbose command TestCommand')
+ eq(string.format([[
+ Name Args Address Complete Definition
+ TestCommand 0 :echo 'Hello'
+ Last set from %s line 14]],
+ script_location), result)
+ end)
+
+ it('"Last set for function', function()
+ local result = exec_capture(':verbose function Close_Window')
+ eq(string.format([[
+ function Close_Window() abort
+ Last set from %s line 16
+1 wincmd -
+ endfunction]],
+ script_location), result)
+ end)
+
+ it('"Last set" works with anonymous sid', function()
+ local result = exec_capture(':verbose set tw?')
+ eq(string.format([[
+ textwidth=80
+ Last set from %s line 22]],
+ script_location), result)
+ end)
+end)
+
+describe('lua verbose:', function()
+ local script_file
+
+ setup(function()
+ clear()
+ script_file = 'test_luafile.lua'
+ write_file(script_file, [[
+ vim.api.nvim_set_option('hlsearch', false)
+ ]])
+ exec(':source '..script_file)
+ end)
+
+ teardown(function()
+ os.remove(script_file)
+ end)
+
+ it('is disabled when verbose = 0', function()
+ local result = exec_capture(':verbose set hlsearch?')
+ eq([[
+nohlsearch
+ Last set from Lua]], result)
+ end)
+end)
+
diff --git a/test/functional/fixtures/CMakeLists.txt b/test/functional/fixtures/CMakeLists.txt
index 270540de2e..a5410c2f8c 100644
--- a/test/functional/fixtures/CMakeLists.txt
+++ b/test/functional/fixtures/CMakeLists.txt
@@ -2,9 +2,11 @@ add_executable(tty-test EXCLUDE_FROM_ALL tty-test.c)
target_link_libraries(tty-test ${LIBUV_LIBRARIES})
add_executable(shell-test EXCLUDE_FROM_ALL shell-test.c)
+# Fake pwsh (powershell) for testing make_filter_cmd(). #16271
+add_executable(pwsh-test EXCLUDE_FROM_ALL shell-test.c)
add_executable(printargs-test EXCLUDE_FROM_ALL printargs-test.c)
add_executable(printenv-test EXCLUDE_FROM_ALL printenv-test.c)
-if(WIN32)
+if(MINGW)
set_target_properties(printenv-test PROPERTIES LINK_FLAGS -municode)
endif()
diff --git a/test/functional/fixtures/api_level_9.mpack b/test/functional/fixtures/api_level_9.mpack
new file mode 100644
index 0000000000..650d7a6a4d
--- /dev/null
+++ b/test/functional/fixtures/api_level_9.mpack
Binary files differ
diff --git a/test/functional/fixtures/autoload/provider/python.vim b/test/functional/fixtures/autoload/provider/python3.vim
index d68360ac30..8ed4330a35 100644
--- a/test/functional/fixtures/autoload/provider/python.vim
+++ b/test/functional/fixtures/autoload/provider/python3.vim
@@ -1,6 +1,6 @@
" Dummy test provider, missing this required variable:
" let g:loaded_brokenenabled_provider = 0
-function! provider#python#Call(method, args)
+function! provider#python3#Call(method, args)
return 42
endfunction
diff --git a/test/functional/fixtures/fake-lsp-server.lua b/test/functional/fixtures/fake-lsp-server.lua
index 5c0de50731..0dc0c8c2db 100644
--- a/test/functional/fixtures/fake-lsp-server.lua
+++ b/test/functional/fixtures/fake-lsp-server.lua
@@ -28,7 +28,10 @@ local function assert_eq(a, b, ...)
if not vim.deep_equal(a, b) then
error(message_parts(": ",
..., "assert_eq failed",
- string.format("left == %q, right == %q", vim.inspect(a), vim.inspect(b))
+ string.format("left == %q, right == %q",
+ table.concat(vim.split(vim.inspect(a), "\n"), ""),
+ table.concat(vim.split(vim.inspect(b), "\n"), "")
+ )
))
end
end
@@ -100,8 +103,12 @@ local tests = {}
function tests.basic_init()
skeleton {
- on_init = function(_params)
- return { capabilities = {} }
+ on_init = function(_)
+ return {
+ capabilities = {
+ textDocumentSync = protocol.TextDocumentSyncKind.None;
+ }
+ }
end;
body = function()
notify('test')
@@ -109,6 +116,19 @@ function tests.basic_init()
}
end
+function tests.basic_init_did_change_configuration()
+ skeleton({
+ on_init = function(_)
+ return {
+ capabilities = {},
+ }
+ end,
+ body = function()
+ expect_notification('workspace/didChangeConfiguration', { settings = { dummy = 1 } })
+ end,
+ })
+end
+
function tests.check_workspace_configuration()
skeleton {
on_init = function(_params)
@@ -119,8 +139,10 @@ function tests.check_workspace_configuration()
notify('workspace/configuration', { items = {
{ section = "testSetting1" };
{ section = "testSetting2" };
+ { section = "test.Setting3" };
+ { section = "test.Setting4" };
} })
- expect_notification('workspace/configuration', { true; vim.NIL})
+ expect_notification('workspace/configuration', { true; false; 'nested'; vim.NIL})
notify('shutdown')
end;
}
@@ -130,8 +152,11 @@ function tests.prepare_rename_nil()
skeleton {
on_init = function()
return { capabilities = {
- renameProvider = true,
- } }
+ renameProvider = {
+ prepareProvider = true
+ }
+ }
+ }
end;
body = function()
notify('start')
@@ -147,8 +172,11 @@ function tests.prepare_rename_placeholder()
skeleton {
on_init = function()
return { capabilities = {
- renameProvider = true,
- } }
+ renameProvider = {
+ prepareProvider = true
+ }
+ }
+ }
end;
body = function()
notify('start')
@@ -168,8 +196,11 @@ function tests.prepare_rename_range()
skeleton {
on_init = function()
return { capabilities = {
- renameProvider = true,
- } }
+ renameProvider = {
+ prepareProvider = true
+ }
+ }
+ }
end;
body = function()
notify('start')
@@ -191,19 +222,19 @@ end
function tests.prepare_rename_error()
skeleton {
on_init = function()
- return { capabilities = {
- renameProvider = true,
- } }
+ return {
+ capabilities = {
+ renameProvider = {
+ prepareProvider = true
+ },
+ }
+ }
end;
body = function()
notify('start')
expect_request('textDocument/prepareRename', function()
return {}, nil
end)
- expect_request('textDocument/rename', function(params)
- assert_eq(params.newName, 'renameto')
- return nil, nil
- end)
notify('shutdown')
end;
}
@@ -217,6 +248,7 @@ function tests.basic_check_capabilities()
return {
capabilities = {
textDocumentSync = protocol.TextDocumentSyncKind.Full;
+ codeLensProvider = false
}
}
end;
@@ -225,6 +257,51 @@ function tests.basic_check_capabilities()
}
end
+function tests.text_document_sync_save_bool()
+ skeleton {
+ on_init = function()
+ return {
+ capabilities = {
+ textDocumentSync = {
+ save = true
+ }
+ }
+ }
+ end;
+ body = function()
+ notify('start')
+ expect_notification('textDocument/didSave', {textDocument = { uri = "file://" }})
+ notify('shutdown')
+ end;
+ }
+end
+
+function tests.text_document_sync_save_includeText()
+ skeleton {
+ on_init = function()
+ return {
+ capabilities = {
+ textDocumentSync = {
+ save = {
+ includeText = true
+ }
+ }
+ }
+ }
+ end;
+ body = function()
+ notify('start')
+ expect_notification('textDocument/didSave', {
+ textDocument = {
+ uri = "file://"
+ },
+ text = "help me\n"
+ })
+ notify('shutdown')
+ end;
+ }
+end
+
function tests.capabilities_for_client_supports_method()
skeleton {
on_init = function(params)
@@ -235,6 +312,7 @@ function tests.capabilities_for_client_supports_method()
textDocumentSync = protocol.TextDocumentSyncKind.Full;
completionProvider = true;
hoverProvider = true;
+ renameProvider = false;
definitionProvider = false;
referencesProvider = false;
codeLensProvider = { resolveProvider = true; };
@@ -542,7 +620,15 @@ function tests.basic_check_buffer_open_and_change_incremental()
assert_eq(params.capabilities, expected_capabilities)
return {
capabilities = {
- textDocumentSync = protocol.TextDocumentSyncKind.Incremental;
+ textDocumentSync = {
+ openClose = true,
+ change = protocol.TextDocumentSyncKind.Incremental,
+ willSave = true,
+ willSaveWaitUntil = true,
+ save = {
+ includeText = true,
+ }
+ }
}
}
end;
@@ -671,6 +757,78 @@ function tests.code_action_with_resolve()
}
end
+function tests.code_action_server_side_command()
+ skeleton({
+ on_init = function()
+ return {
+ capabilities = {
+ codeActionProvider = {
+ resolveProvider = false,
+ },
+ },
+ }
+ end,
+ body = function()
+ notify('start')
+ local cmd = {
+ title = 'Command 1',
+ command = 'dummy1',
+ }
+ expect_request('textDocument/codeAction', function()
+ return nil, { cmd }
+ end)
+ expect_request('workspace/executeCommand', function()
+ return nil, cmd
+ end)
+ notify('shutdown')
+ end,
+ })
+end
+
+
+function tests.code_action_filter()
+ skeleton {
+ on_init = function()
+ return {
+ capabilities = {
+ codeActionProvider = {
+ resolveProvider = false
+ }
+ }
+ }
+ end;
+ body = function()
+ notify('start')
+ local action = {
+ title = 'Action 1',
+ command = 'command'
+ }
+ local preferred_action = {
+ title = 'Action 2',
+ isPreferred = true,
+ command = 'preferred_command',
+ }
+ local quickfix_action = {
+ title = 'Action 3',
+ kind = 'quickfix',
+ command = 'quickfix_command',
+ }
+ local quickfix_foo_action = {
+ title = 'Action 4',
+ kind = 'quickfix.foo',
+ command = 'quickfix_foo_command',
+ }
+ expect_request('textDocument/codeAction', function()
+ return nil, { action, preferred_action, quickfix_action, quickfix_foo_action, }
+ end)
+ expect_request('textDocument/codeAction', function()
+ return nil, { action, preferred_action, quickfix_action, quickfix_foo_action, }
+ end)
+ notify('shutdown')
+ end;
+ }
+end
+
function tests.clientside_commands()
skeleton {
on_init = function()
@@ -685,6 +843,68 @@ function tests.clientside_commands()
}
end
+function tests.codelens_refresh_lock()
+ skeleton {
+ on_init = function()
+ return {
+ capabilities = {
+ codeLensProvider = { resolveProvider = true; };
+ }
+ }
+ end;
+ body = function()
+ notify('start')
+ expect_request("textDocument/codeLens", function ()
+ return {code = -32002, message = "ServerNotInitialized"}, nil
+ end)
+ expect_request("textDocument/codeLens", function ()
+ local lenses = {
+ {
+ range = {
+ start = { line = 0, character = 0, },
+ ['end'] = { line = 0, character = 3 }
+ },
+ command = { title = 'Lens1', command = 'Dummy' }
+ },
+ }
+ return nil, lenses
+ end)
+ expect_request("textDocument/codeLens", function ()
+ local lenses = {
+ {
+ range = {
+ start = { line = 0, character = 0, },
+ ['end'] = { line = 0, character = 3 }
+ },
+ command = { title = 'Lens2', command = 'Dummy' }
+ },
+ }
+ return nil, lenses
+ end)
+ notify('shutdown')
+ end;
+ }
+end
+
+function tests.basic_formatting()
+ skeleton {
+ on_init = function()
+ return {
+ capabilities = {
+ documentFormattingProvider = true,
+ }
+ }
+ end;
+ body = function()
+ notify('start')
+ expect_request('textDocument/formatting', function()
+ return nil, {}
+ end)
+ notify('shutdown')
+ end;
+ }
+end
+
-- Tests will be indexed by TEST_NAME
local kill_timer = vim.loop.new_timer()
diff --git a/test/functional/fixtures/lua/test_plug/health/init.lua b/test/functional/fixtures/lua/test_plug/health/init.lua
index d07632cff4..58162d4515 100644
--- a/test/functional/fixtures/lua/test_plug/health/init.lua
+++ b/test/functional/fixtures/lua/test_plug/health/init.lua
@@ -1,11 +1,10 @@
local M = {}
-local health = require("health")
M.check = function()
- health.report_start("report 1")
- health.report_ok("everything is fine")
- health.report_start("report 2")
- health.report_ok("nothing to see here")
+ vim.health.report_start("report 1")
+ vim.health.report_ok("everything is fine")
+ vim.health.report_start("report 2")
+ vim.health.report_ok("nothing to see here")
end
return M
diff --git a/test/functional/fixtures/lua/test_plug/submodule/health.lua b/test/functional/fixtures/lua/test_plug/submodule/health.lua
index d07632cff4..58162d4515 100644
--- a/test/functional/fixtures/lua/test_plug/submodule/health.lua
+++ b/test/functional/fixtures/lua/test_plug/submodule/health.lua
@@ -1,11 +1,10 @@
local M = {}
-local health = require("health")
M.check = function()
- health.report_start("report 1")
- health.report_ok("everything is fine")
- health.report_start("report 2")
- health.report_ok("nothing to see here")
+ vim.health.report_start("report 1")
+ vim.health.report_ok("everything is fine")
+ vim.health.report_start("report 2")
+ vim.health.report_ok("nothing to see here")
end
return M
diff --git a/test/functional/fixtures/lua/test_plug/submodule_empty/health.lua b/test/functional/fixtures/lua/test_plug/submodule_empty/health.lua
new file mode 100644
index 0000000000..d2cf86e4f0
--- /dev/null
+++ b/test/functional/fixtures/lua/test_plug/submodule_empty/health.lua
@@ -0,0 +1,7 @@
+local M = {}
+
+M.check = function()
+ return {}
+end
+
+return M
diff --git a/test/functional/fixtures/lua/test_plug/submodule_failed/health.lua b/test/functional/fixtures/lua/test_plug/submodule_failed/health.lua
index 3a8af6ebb2..ee5f4e404b 100644
--- a/test/functional/fixtures/lua/test_plug/submodule_failed/health.lua
+++ b/test/functional/fixtures/lua/test_plug/submodule_failed/health.lua
@@ -1,10 +1,9 @@
local M = {}
-local health = require("health")
M.check = function()
- health.report_start("report 1")
- health.report_ok("everything is fine")
- health.report_warn("About to add a number to nil")
+ vim.health.report_start("report 1")
+ vim.health.report_ok("everything is fine")
+ vim.health.report_warn("About to add a number to nil")
local a = nil + 2
return a
end
diff --git a/test/functional/fixtures/pack/foo/start/bar/lua/baz-quux.lua b/test/functional/fixtures/pack/foo/start/bar/lua/baz-quux.lua
new file mode 100644
index 0000000000..c1c33d787e
--- /dev/null
+++ b/test/functional/fixtures/pack/foo/start/bar/lua/baz-quux.lua
@@ -0,0 +1 @@
+return {doit=function() return 9004 end}
diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua
index 1845786c4b..0c616e73fb 100644
--- a/test/functional/helpers.lua
+++ b/test/functional/helpers.lua
@@ -21,7 +21,6 @@ local map = global_helpers.tbl_map
local ok = global_helpers.ok
local sleep = global_helpers.sleep
local tbl_contains = global_helpers.tbl_contains
-local write_file = global_helpers.write_file
local fail = global_helpers.fail
local module = {
@@ -42,10 +41,8 @@ module.nvim_set = (
module.nvim_argv = {
module.nvim_prog, '-u', 'NONE', '-i', 'NONE',
'--cmd', module.nvim_set,
- '--cmd', 'unmap Y',
- '--cmd', 'unmap <C-L>',
- '--cmd', 'iunmap <C-U>',
- '--cmd', 'iunmap <C-W>',
+ '--cmd', 'mapclear',
+ '--cmd', 'mapclear!',
'--embed'}
-- Directory containing nvim.
@@ -54,7 +51,6 @@ if module.nvim_dir == module.nvim_prog then
module.nvim_dir = "."
end
-local tmpname = global_helpers.tmpname
local iswin = global_helpers.iswin
local prepend_argv
@@ -274,6 +270,13 @@ function module.command(cmd)
module.request('nvim_command', cmd)
end
+
+-- use for commands which expect nvim to quit
+function module.expect_exit(...)
+ eq("EOF was received from Nvim. Likely the Nvim process crashed.",
+ module.pcall_err(...))
+end
+
-- Evaluates a VimL expression.
-- Fails on VimL error, but does not update v:errmsg.
function module.eval(expr)
@@ -363,14 +366,15 @@ local function remove_args(args, args_rm)
return new_args
end
-function module.spawn(argv, merge, env, keep)
+--- @param io_extra used for stdin_fd, see :help ui-option
+function module.spawn(argv, merge, env, keep, io_extra)
if session and not keep then
session:close()
end
local child_stream = ChildProcessStream.spawn(
merge and module.merge_args(prepend_argv, argv) or argv,
- env)
+ env, io_extra)
return Session.new(child_stream)
end
@@ -417,8 +421,8 @@ end
-- clear('-e')
-- clear{args={'-e'}, args_rm={'-i'}, env={TERM=term}}
function module.clear(...)
- local argv, env = module.new_argv(...)
- module.set_session(module.spawn(argv, nil, env))
+ local argv, env, io_extra = module.new_argv(...)
+ module.set_session(module.spawn(argv, nil, env, nil, io_extra))
end
-- Builds an argument list for use in clear().
@@ -427,17 +431,25 @@ end
function module.new_argv(...)
local args = {unpack(module.nvim_argv)}
table.insert(args, '--headless')
+ if _G._nvim_test_id then
+ -- Set the server name to the test-id for logging. #8519
+ table.insert(args, '--listen')
+ table.insert(args, _G._nvim_test_id)
+ end
local new_args
+ local io_extra
local env = nil
local opts = select(1, ...)
- if type(opts) == 'table' then
+ if type(opts) ~= 'table' then
+ new_args = {...}
+ else
args = remove_args(args, opts.args_rm)
if opts.env then
- local env_tbl = {}
+ local env_opt = {}
for k, v in pairs(opts.env) do
assert(type(k) == 'string')
assert(type(v) == 'string')
- env_tbl[k] = v
+ env_opt[k] = v
end
for _, k in ipairs({
'HOME',
@@ -453,23 +465,23 @@ function module.new_argv(...)
'TMPDIR',
'VIMRUNTIME',
}) do
- if not env_tbl[k] then
- env_tbl[k] = os.getenv(k)
+ -- Set these from the environment unless the caller defined them.
+ if not env_opt[k] then
+ env_opt[k] = os.getenv(k)
end
end
env = {}
- for k, v in pairs(env_tbl) do
+ for k, v in pairs(env_opt) do
env[#env + 1] = k .. '=' .. v
end
end
new_args = opts.args or {}
- else
- new_args = {...}
+ io_extra = opts.io_extra
end
for _, arg in ipairs(new_args) do
table.insert(args, arg)
end
- return args, env
+ return args, env, io_extra
end
function module.insert(...)
@@ -494,44 +506,38 @@ function module.feed_command(...)
end
end
-local sourced_fnames = {}
+-- @deprecated use nvim_exec()
function module.source(code)
- local fname = tmpname()
- write_file(fname, code)
- module.command('source '..fname)
- -- DO NOT REMOVE FILE HERE.
- -- do_source() has a habit of checking whether files are “same” by using inode
- -- and device IDs. If you run two source() calls in quick succession there is
- -- a good chance that underlying filesystem will reuse the inode, making files
- -- appear as “symlinks” to do_source when it checks FileIDs. With current
- -- setup linux machines (both QB, travis and mine(ZyX-I) with XFS) do reuse
- -- inodes, Mac OS machines (again, both QB and travis) do not.
- --
- -- Files appearing as “symlinks” mean that both the first and the second
- -- source() calls will use same SID, which may fail some tests which check for
- -- exact numbers after `<SNR>` in e.g. function names.
- sourced_fnames[#sourced_fnames + 1] = fname
- return fname
+ module.exec(dedent(code))
end
function module.has_powershell()
return module.eval('executable("'..(iswin() and 'powershell' or 'pwsh')..'")') == 1
end
-function module.set_shell_powershell()
- local shell = iswin() and 'powershell' or 'pwsh'
- assert(module.has_powershell())
+--- Sets Nvim shell to powershell.
+---
+--- @param fake (boolean) If true, a fake will be used if powershell is not
+--- found on the system.
+--- @returns true if powershell was found on the system, else false.
+function module.set_shell_powershell(fake)
+ local found = module.has_powershell()
+ if not fake then
+ assert(found)
+ end
+ local shell = found and (iswin() and 'powershell' or 'pwsh') or module.testprg('pwsh-test')
local set_encoding = '[Console]::InputEncoding=[Console]::OutputEncoding=[System.Text.Encoding]::UTF8;'
local cmd = set_encoding..'Remove-Item -Force '..table.concat(iswin()
and {'alias:cat', 'alias:echo', 'alias:sleep'}
or {'alias:echo'}, ',')..';'
- module.source([[
+ module.exec([[
let &shell = ']]..shell..[['
set shellquote= shellxquote=
- let &shellpipe = '2>&1 | Out-File -Encoding UTF8 %s; exit $LastExitCode'
- let &shellredir = '2>&1 | Out-File -Encoding UTF8 %s; exit $LastExitCode'
let &shellcmdflag = '-NoLogo -NoProfile -ExecutionPolicy RemoteSigned -Command ]]..cmd..[['
+ let &shellpipe = '2>&1 | Out-File -Encoding UTF8 %s; exit $LastExitCode'
+ let &shellredir = '-RedirectStandardOutput %s -NoNewWindow -Wait'
]])
+ return found
end
function module.nvim(method, ...)
@@ -736,14 +742,10 @@ function module.pending_win32(pending_fn)
end
function module.pending_c_parser(pending_fn)
- local status, msg = unpack(module.exec_lua([[ return {pcall(vim.treesitter.require_language, 'c')} ]]))
+ local status, _ = unpack(module.exec_lua([[ return {pcall(vim.treesitter.require_language, 'c')} ]]))
if not status then
- if module.isCI() then
- error("treesitter C parser not found, required on CI: " .. msg)
- else
- pending_fn 'no C parser, skipping'
- return true
- end
+ pending_fn 'no C parser, skipping'
+ return true
end
return false
end
@@ -791,12 +793,22 @@ function module.get_pathsep()
return iswin() and '\\' or '/'
end
+--- Gets the filesystem root dir, namely "/" or "C:/".
function module.pathroot()
local pathsep = package.config:sub(1,1)
return iswin() and (module.nvim_dir:sub(1,2)..pathsep) or '/'
end
--- Returns a valid, platform-independent $NVIM_LISTEN_ADDRESS.
+--- Gets the full `…/build/bin/{name}` path of a test program produced by
+--- `test/functional/fixtures/CMakeLists.txt`.
+---
+--- @param name (string) Name of the test program.
+function module.testprg(name)
+ local ext = module.iswin() and '.exe' or ''
+ return ('%s/%s%s'):format(module.nvim_dir, name, ext)
+end
+
+-- Returns a valid, platform-independent Nvim listen address.
-- Useful for communicating with child instances.
function module.new_pipename()
-- HACK: Start a server temporarily, get the name, then stop it.
@@ -887,9 +899,6 @@ module = global_helpers.tbl_extend('error', module, global_helpers)
return function(after_each)
if after_each then
after_each(function()
- for _, fname in ipairs(sourced_fnames) do
- os.remove(fname)
- end
check_logs()
check_cores('build/bin/nvim')
if session then
diff --git a/test/functional/legacy/003_cindent_spec.lua b/test/functional/legacy/003_cindent_spec.lua
deleted file mode 100644
index 061904c42f..0000000000
--- a/test/functional/legacy/003_cindent_spec.lua
+++ /dev/null
@@ -1,4774 +0,0 @@
--- Test for 'cindent'.
--- For new tests, consider putting them in test_cindent.vim.
---
--- There are 50+ test command blocks (the stuff between STARTTEST and ENDTEST)
--- in the original test. These have been converted to "it" test cases here.
-
-local helpers = require('test.functional.helpers')(after_each)
-local feed, insert = helpers.feed, helpers.insert
-local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect
-
--- Inserts text as usual, and additionally positions the cursor on line 1 and
--- sets 'cindent' and tab settings. (In the original "test3.in" the modeline at
--- the top of the file takes care of this.)
-local function insert_(content)
- insert(content)
- feed_command('1', 'set cin ts=4 sw=4')
-end
-
--- luacheck: ignore 621 (Indentation)
--- luacheck: ignore 613 (Trailing whitespace in a string)
-describe('cindent', function()
- before_each(clear)
-
- it('1 is working', function()
- insert_([=[
-
- /* start of AUTO matically checked vim: set ts=4 : */
- {
- if (test)
- cmd1;
- cmd2;
- }
-
- {
- if (test)
- cmd1;
- else
- cmd2;
- }
-
- {
- if (test)
- {
- cmd1;
- cmd2;
- }
- }
-
- {
- if (test)
- {
- cmd1;
- else
- }
- }
-
- {
- while (this)
- if (test)
- cmd1;
- cmd2;
- }
-
- {
- while (this)
- if (test)
- cmd1;
- else
- cmd2;
- }
-
- {
- if (test)
- {
- cmd;
- }
-
- if (test)
- cmd;
- }
-
- {
- if (test) {
- cmd;
- }
-
- if (test) cmd;
- }
-
- {
- cmd1;
- for (blah)
- while (this)
- if (test)
- cmd2;
- cmd3;
- }
-
- {
- cmd1;
- for (blah)
- while (this)
- if (test)
- cmd2;
- cmd3;
-
- if (test)
- {
- cmd1;
- cmd2;
- cmd3;
- }
- }
-
-
- /* Test for 'cindent' do/while mixed with if/else: */
-
- {
- do
- if (asdf)
- asdfasd;
- while (cond);
-
- do
- if (asdf)
- while (asdf)
- asdf;
- while (asdf);
- }
-
- /* Test for 'cindent' with two ) on a continuation line */
- {
- if (asdfasdf;asldkfj asdlkfj as;ldkfj sal;d
- aal;sdkjf ( ;asldfkja;sldfk
- al;sdjfka ;slkdf ) sa;ldkjfsa dlk;)
- line up here;
- }
-
-
- /* C++ tests: */
-
- // foo() these three lines should remain in column 0
- // {
- // }
-
- /* Test for continuation and unterminated lines: */
- {
- i = 99 + 14325 +
- 21345 +
- 21345 +
- 21345 + ( 21345 +
- 21345) +
- 2345 +
- 1234;
- c = 1;
- }
-
- /*
- testje for indent with empty line
-
- here */
-
- {
- if (testing &&
- not a joke ||
- line up here)
- hay;
- if (testing &&
- (not a joke || testing
- )line up here)
- hay;
- if (testing &&
- (not a joke || testing
- line up here))
- hay;
- }
-
-
- {
- switch (c)
- {
- case xx:
- do
- if (asdf)
- do
- asdfasdf;
- while (asdf);
- else
- asdfasdf;
- while (cond);
- case yy:
- case xx:
- case zz:
- testing;
- }
- }
-
- {
- if (cond) {
- foo;
- }
- else
- {
- bar;
- }
- }
-
- {
- if (alskdfj ;alsdkfjal;skdjf (;sadlkfsa ;dlkf j;alksdfj ;alskdjf
- alsdkfj (asldk;fj
- awith cino=(0 ;lf this one goes to below the paren with ==
- ;laksjfd ;lsakdjf ;alskdf asd)
- asdfasdf;)))
- asdfasdf;
- }
-
- int
- func(a, b)
- int a;
- int c;
- {
- if (c1 && (c2 ||
- c3))
- foo;
- if (c1 &&
- (c2 || c3)
- )
- }
-
- {
- while (asd)
- {
- if (asdf)
- if (test)
- if (that)
- {
- if (asdf)
- do
- cdasd;
- while (as
- df);
- }
- else
- if (asdf)
- asdf;
- else
- asdf;
- asdf;
- }
- }
-
- {
- s = "/*"; b = ';'
- s = "/*"; b = ';';
- a = b;
- }
-
- {
- switch (a)
- {
- case a:
- switch (t)
- {
- case 1:
- cmd;
- break;
- case 2:
- cmd;
- break;
- }
- cmd;
- break;
- case b:
- {
- int i;
- cmd;
- }
- break;
- case c: {
- int i;
- cmd;
- }
- case d: if (cond &&
- test) { /* this line doesn't work right */
- int i;
- cmd;
- }
- break;
- }
- }
-
- {
- if (!(vim_strchr(p_cpo, CPO_BUFOPTGLOB) != NULL && entering) &&
- (bp_to->b_p_initialized ||
- (!entering && vim_strchr(p_cpo, CPO_BUFOPT) != NULL)))
- return;
- label :
- asdf = asdf ?
- asdf : asdf;
- asdf = asdf ?
- asdf: asdf;
- }
-
- /* Special Comments : This function has the added complexity (compared */
- /* : to addtolist) of having to check for a detail */
- /* : texture and add that to the list first. */
-
- char *(array[100]) = {
- "testje",
- "foo",
- "bar",
- }
-
- enum soppie
- {
- yes = 0,
- no,
- maybe
- };
-
- typedef enum soppie
- {
- yes = 0,
- no,
- maybe
- };
-
- static enum
- {
- yes = 0,
- no,
- maybe
- } soppie;
-
- public static enum
- {
- yes = 0,
- no,
- maybe
- } soppie;
-
- static private enum
- {
- yes = 0,
- no,
- maybe
- } soppie;
-
- {
- int a,
- b;
- }
-
- {
- struct Type
- {
- int i;
- char *str;
- } var[] =
- {
- 0, "zero",
- 1, "one",
- 2, "two",
- 3, "three"
- };
-
- float matrix[3][3] =
- {
- {
- 0,
- 1,
- 2
- },
- {
- 3,
- 4,
- 5
- },
- {
- 6,
- 7,
- 8
- }
- };
- }
-
- {
- /* blah ( blah */
- /* where does this go? */
-
- /* blah ( blah */
- cmd;
-
- func(arg1,
- /* comment */
- arg2);
- a;
- {
- b;
- {
- c; /* Hey, NOW it indents?! */
- }
- }
-
- {
- func(arg1,
- arg2,
- arg3);
- /* Hey, what am I doing here? Is this coz of the ","? */
- }
- }
-
- main ()
- {
- if (cond)
- {
- a = b;
- }
- if (cond) {
- a = c;
- }
- if (cond)
- a = d;
- return;
- }
-
- {
- case 2: if (asdf &&
- asdfasdf)
- aasdf;
- a = 9;
- case 3: if (asdf)
- aasdf;
- a = 9;
- case 4: x = 1;
- y = 2;
-
- label: if (asdf)
- here;
-
- label: if (asdf &&
- asdfasdf)
- {
- }
-
- label: if (asdf &&
- asdfasdf) {
- there;
- }
-
- label: if (asdf &&
- asdfasdf)
- there;
- }
-
- {
- /*
- hello with ":set comments= cino=c5"
- */
-
- /*
- hello with ":set comments= cino="
- */
- }
-
-
- {
- if (a < b) {
- a = a + 1;
- } else
- a = a + 2;
-
- if (a)
- do {
- testing;
- } while (asdfasdf);
- a = b + 1;
- asdfasdf
- }
-
- {
- for ( int i = 0;
- i < 10; i++ )
- {
- }
- i = 0;
- }
-
- class bob
- {
- int foo() {return 1;}
- int bar;
- }
-
- main()
- {
- while(1)
- if (foo)
- {
- bar;
- }
- else {
- asdf;
- }
- misplacedline;
- }
-
- {
- if (clipboard.state == SELECT_DONE
- && ((row == clipboard.start.lnum
- && col >= clipboard.start.col)
- || row > clipboard.start.lnum))
- }
-
- {
- if (1) {i += 4;}
- where_am_i;
- return 0;
- }
-
- {
- {
- } // sdf(asdf
- if (asdf)
- asd;
- }
-
- {
- label1:
- label2:
- }
-
- {
- int fooRet = foo(pBar1, false /*fKB*/,
- true /*fPTB*/, 3 /*nT*/, false /*fDF*/);
- f() {
- for ( i = 0;
- i < m;
- /* c */ i++ ) {
- a = b;
- }
- }
- }
-
- {
- f1(/*comment*/);
- f2();
- }
-
- {
- do {
- if (foo) {
- } else
- ;
- } while (foo);
- foo(); // was wrong
- }
-
- int x; // no extra indent because of the ;
- void func()
- {
- }
-
- char *tab[] = {"aaa",
- "};", /* }; */ NULL}
- int indented;
- {}
-
- char *a[] = {"aaa", "bbb",
- "ccc", NULL};
- // here
-
- char *tab[] = {"aaa",
- "xx", /* xx */}; /* asdf */
- int not_indented;
-
- {
- do {
- switch (bla)
- {
- case 1: if (foo)
- bar;
- }
- } while (boo);
- wrong;
- }
-
- int foo,
- bar;
- int foo;
-
- #if defined(foo) \
- && defined(bar)
- char * xx = "asdf\
- foo\
- bor";
- int x;
-
- char *foo = "asdf\
- asdf\
- asdf",
- *bar;
-
- void f()
- {
- #if defined(foo) \
- && defined(bar)
- char *foo = "asdf\
- asdf\
- asdf",
- *bar;
- {
- int i;
- char *foo = "asdf\
- asdf\
- asdf",
- *bar;
- }
- #endif
- }
- #endif
-
- int y; // comment
- // comment
-
- // comment
-
- {
- Constructor(int a,
- int b ) : BaseClass(a)
- {
- }
- }
-
- void foo()
- {
- char one,
- two;
- struct bla piet,
- jan;
- enum foo kees,
- jannie;
- static unsigned sdf,
- krap;
- unsigned int piet,
- jan;
- int
- kees,
- jan;
- }
-
- {
- t(int f,
- int d); // )
- d();
- }
-
- Constructor::Constructor(int a,
- int b
- ) :
- BaseClass(a,
- b,
- c),
- mMember(b),
- {
- }
-
- Constructor::Constructor(int a,
- int b ) :
- BaseClass(a)
- {
- }
-
- Constructor::Constructor(int a,
- int b ) /*x*/ : /*x*/ BaseClass(a),
- member(b)
- {
- }
-
- A::A(int a, int b)
- : aa(a),
- bb(b),
- cc(c)
- {
- }
-
- class CAbc :
- public BaseClass1,
- protected BaseClass2
- {
- int Test() { return FALSE; }
- int Test1() { return TRUE; }
-
- CAbc(int a, int b ) :
- BaseClass(a)
- {
- switch(xxx)
- {
- case abc:
- asdf();
- break;
-
- case 999:
- baer();
- break;
- }
- }
-
- public: // <-- this was incoreectly indented before!!
- void testfall();
- protected:
- void testfall();
- };
-
- class CAbc : public BaseClass1,
- protected BaseClass2
- {
- };
-
- static struct
- {
- int a;
- int b;
- } variable[COUNT] =
- {
- {
- 123,
- 456
- },
- {
- 123,
- 456
- }
- };
-
- static struct
- {
- int a;
- int b;
- } variable[COUNT] =
- {
- { 123, 456 },
- { 123, 456 }
- };
-
- void asdf() /* ind_maxparen may cause trouble here */
- {
- if ((0
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1)) break;
- }
-
- foo()
- {
- a = cond ? foo() : asdf
- + asdf;
-
- a = cond ?
- foo() : asdf
- + asdf;
- }
-
- int main(void)
- {
- if (a)
- if (b)
- 2;
- else 3;
- next_line_of_code();
- }
-
- barry()
- {
- Foo::Foo (int one,
- int two)
- : something(4)
- {}
- }
-
- barry()
- {
- Foo::Foo (int one, int two)
- : something(4)
- {}
- }
-
- Constructor::Constructor(int a,
- int b
- ) :
- BaseClass(a,
- b,
- c),
- mMember(b)
- {
- }
- int main ()
- {
- if (lala)
- do
- ++(*lolo);
- while (lili
- && lele);
- lulu;
- }
-
- int main ()
- {
- switch (c)
- {
- case 'c': if (cond)
- {
- }
- }
- }
-
- main()
- {
- (void) MyFancyFuasdfadsfnction(
- argument);
- }
-
- main()
- {
- char foo[] = "/*";
- /* as
- df */
- hello
- }
-
- /* valid namespaces with normal indent */
- namespace
- {
- {
- 111111111111;
- }
- }
- namespace /* test */
- {
- 11111111111111111;
- }
- namespace // test
- {
- 111111111111111111;
- }
- namespace
- {
- 111111111111111111;
- }
- namespace test
- {
- 111111111111111111;
- }
- namespace{
- 111111111111111111;
- }
- namespace test{
- 111111111111111111;
- }
- namespace {
- 111111111111111111;
- }
- namespace test {
- 111111111111111111;
- namespace test2 {
- 22222222222222222;
- }
- }
-
- /* invalid namespaces use block indent */
- namespace test test2 {
- 111111111111111111111;
- }
- namespace11111111111 {
- 111111111111;
- }
- namespace() {
- 1111111111111;
- }
- namespace()
- {
- 111111111111111111;
- }
- namespace test test2
- {
- 1111111111111111111;
- }
- namespace111111111
- {
- 111111111111111111;
- }
- void getstring() {
- /* Raw strings */
- const char* s = R"(
- test {
- # comment
- field: 123
- }
- )";
- }
- void getstring() {
- const char* s = R"foo(
- test {
- # comment
- field: 123
- }
- )foo";
- }
-
- {
- int a[4] = {
- [0] = 0,
- [1] = 1,
- [2] = 2,
- [3] = 3,
- };
- }
-
- {
- a = b[2]
- + 3;
- }
-
- {
- if (1)
- /* aaaaa
- * bbbbb
- */
- a = 1;
- }
-
- void func()
- {
- switch (foo)
- {
- case (bar):
- if (baz())
- quux();
- break;
- case (shmoo):
- if (!bar)
- {
- }
- case (foo1):
- switch (bar)
- {
- case baz:
- baz_f();
- break;
- }
- break;
- default:
- baz();
- baz();
- break;
- }
- }
-
- /* end of AUTO */
- ]=])
-
- feed_command('/start of AUTO')
- feed('=/end of AUTO<cr>')
-
- expect([=[
-
- /* start of AUTO matically checked vim: set ts=4 : */
- {
- if (test)
- cmd1;
- cmd2;
- }
-
- {
- if (test)
- cmd1;
- else
- cmd2;
- }
-
- {
- if (test)
- {
- cmd1;
- cmd2;
- }
- }
-
- {
- if (test)
- {
- cmd1;
- else
- }
- }
-
- {
- while (this)
- if (test)
- cmd1;
- cmd2;
- }
-
- {
- while (this)
- if (test)
- cmd1;
- else
- cmd2;
- }
-
- {
- if (test)
- {
- cmd;
- }
-
- if (test)
- cmd;
- }
-
- {
- if (test) {
- cmd;
- }
-
- if (test) cmd;
- }
-
- {
- cmd1;
- for (blah)
- while (this)
- if (test)
- cmd2;
- cmd3;
- }
-
- {
- cmd1;
- for (blah)
- while (this)
- if (test)
- cmd2;
- cmd3;
-
- if (test)
- {
- cmd1;
- cmd2;
- cmd3;
- }
- }
-
-
- /* Test for 'cindent' do/while mixed with if/else: */
-
- {
- do
- if (asdf)
- asdfasd;
- while (cond);
-
- do
- if (asdf)
- while (asdf)
- asdf;
- while (asdf);
- }
-
- /* Test for 'cindent' with two ) on a continuation line */
- {
- if (asdfasdf;asldkfj asdlkfj as;ldkfj sal;d
- aal;sdkjf ( ;asldfkja;sldfk
- al;sdjfka ;slkdf ) sa;ldkjfsa dlk;)
- line up here;
- }
-
-
- /* C++ tests: */
-
- // foo() these three lines should remain in column 0
- // {
- // }
-
- /* Test for continuation and unterminated lines: */
- {
- i = 99 + 14325 +
- 21345 +
- 21345 +
- 21345 + ( 21345 +
- 21345) +
- 2345 +
- 1234;
- c = 1;
- }
-
- /*
- testje for indent with empty line
-
- here */
-
- {
- if (testing &&
- not a joke ||
- line up here)
- hay;
- if (testing &&
- (not a joke || testing
- )line up here)
- hay;
- if (testing &&
- (not a joke || testing
- line up here))
- hay;
- }
-
-
- {
- switch (c)
- {
- case xx:
- do
- if (asdf)
- do
- asdfasdf;
- while (asdf);
- else
- asdfasdf;
- while (cond);
- case yy:
- case xx:
- case zz:
- testing;
- }
- }
-
- {
- if (cond) {
- foo;
- }
- else
- {
- bar;
- }
- }
-
- {
- if (alskdfj ;alsdkfjal;skdjf (;sadlkfsa ;dlkf j;alksdfj ;alskdjf
- alsdkfj (asldk;fj
- awith cino=(0 ;lf this one goes to below the paren with ==
- ;laksjfd ;lsakdjf ;alskdf asd)
- asdfasdf;)))
- asdfasdf;
- }
-
- int
- func(a, b)
- int a;
- int c;
- {
- if (c1 && (c2 ||
- c3))
- foo;
- if (c1 &&
- (c2 || c3)
- )
- }
-
- {
- while (asd)
- {
- if (asdf)
- if (test)
- if (that)
- {
- if (asdf)
- do
- cdasd;
- while (as
- df);
- }
- else
- if (asdf)
- asdf;
- else
- asdf;
- asdf;
- }
- }
-
- {
- s = "/*"; b = ';'
- s = "/*"; b = ';';
- a = b;
- }
-
- {
- switch (a)
- {
- case a:
- switch (t)
- {
- case 1:
- cmd;
- break;
- case 2:
- cmd;
- break;
- }
- cmd;
- break;
- case b:
- {
- int i;
- cmd;
- }
- break;
- case c: {
- int i;
- cmd;
- }
- case d: if (cond &&
- test) { /* this line doesn't work right */
- int i;
- cmd;
- }
- break;
- }
- }
-
- {
- if (!(vim_strchr(p_cpo, CPO_BUFOPTGLOB) != NULL && entering) &&
- (bp_to->b_p_initialized ||
- (!entering && vim_strchr(p_cpo, CPO_BUFOPT) != NULL)))
- return;
- label :
- asdf = asdf ?
- asdf : asdf;
- asdf = asdf ?
- asdf: asdf;
- }
-
- /* Special Comments : This function has the added complexity (compared */
- /* : to addtolist) of having to check for a detail */
- /* : texture and add that to the list first. */
-
- char *(array[100]) = {
- "testje",
- "foo",
- "bar",
- }
-
- enum soppie
- {
- yes = 0,
- no,
- maybe
- };
-
- typedef enum soppie
- {
- yes = 0,
- no,
- maybe
- };
-
- static enum
- {
- yes = 0,
- no,
- maybe
- } soppie;
-
- public static enum
- {
- yes = 0,
- no,
- maybe
- } soppie;
-
- static private enum
- {
- yes = 0,
- no,
- maybe
- } soppie;
-
- {
- int a,
- b;
- }
-
- {
- struct Type
- {
- int i;
- char *str;
- } var[] =
- {
- 0, "zero",
- 1, "one",
- 2, "two",
- 3, "three"
- };
-
- float matrix[3][3] =
- {
- {
- 0,
- 1,
- 2
- },
- {
- 3,
- 4,
- 5
- },
- {
- 6,
- 7,
- 8
- }
- };
- }
-
- {
- /* blah ( blah */
- /* where does this go? */
-
- /* blah ( blah */
- cmd;
-
- func(arg1,
- /* comment */
- arg2);
- a;
- {
- b;
- {
- c; /* Hey, NOW it indents?! */
- }
- }
-
- {
- func(arg1,
- arg2,
- arg3);
- /* Hey, what am I doing here? Is this coz of the ","? */
- }
- }
-
- main ()
- {
- if (cond)
- {
- a = b;
- }
- if (cond) {
- a = c;
- }
- if (cond)
- a = d;
- return;
- }
-
- {
- case 2: if (asdf &&
- asdfasdf)
- aasdf;
- a = 9;
- case 3: if (asdf)
- aasdf;
- a = 9;
- case 4: x = 1;
- y = 2;
-
- label: if (asdf)
- here;
-
- label: if (asdf &&
- asdfasdf)
- {
- }
-
- label: if (asdf &&
- asdfasdf) {
- there;
- }
-
- label: if (asdf &&
- asdfasdf)
- there;
- }
-
- {
- /*
- hello with ":set comments= cino=c5"
- */
-
- /*
- hello with ":set comments= cino="
- */
- }
-
-
- {
- if (a < b) {
- a = a + 1;
- } else
- a = a + 2;
-
- if (a)
- do {
- testing;
- } while (asdfasdf);
- a = b + 1;
- asdfasdf
- }
-
- {
- for ( int i = 0;
- i < 10; i++ )
- {
- }
- i = 0;
- }
-
- class bob
- {
- int foo() {return 1;}
- int bar;
- }
-
- main()
- {
- while(1)
- if (foo)
- {
- bar;
- }
- else {
- asdf;
- }
- misplacedline;
- }
-
- {
- if (clipboard.state == SELECT_DONE
- && ((row == clipboard.start.lnum
- && col >= clipboard.start.col)
- || row > clipboard.start.lnum))
- }
-
- {
- if (1) {i += 4;}
- where_am_i;
- return 0;
- }
-
- {
- {
- } // sdf(asdf
- if (asdf)
- asd;
- }
-
- {
- label1:
- label2:
- }
-
- {
- int fooRet = foo(pBar1, false /*fKB*/,
- true /*fPTB*/, 3 /*nT*/, false /*fDF*/);
- f() {
- for ( i = 0;
- i < m;
- /* c */ i++ ) {
- a = b;
- }
- }
- }
-
- {
- f1(/*comment*/);
- f2();
- }
-
- {
- do {
- if (foo) {
- } else
- ;
- } while (foo);
- foo(); // was wrong
- }
-
- int x; // no extra indent because of the ;
- void func()
- {
- }
-
- char *tab[] = {"aaa",
- "};", /* }; */ NULL}
- int indented;
- {}
-
- char *a[] = {"aaa", "bbb",
- "ccc", NULL};
- // here
-
- char *tab[] = {"aaa",
- "xx", /* xx */}; /* asdf */
- int not_indented;
-
- {
- do {
- switch (bla)
- {
- case 1: if (foo)
- bar;
- }
- } while (boo);
- wrong;
- }
-
- int foo,
- bar;
- int foo;
-
- #if defined(foo) \
- && defined(bar)
- char * xx = "asdf\
- foo\
- bor";
- int x;
-
- char *foo = "asdf\
- asdf\
- asdf",
- *bar;
-
- void f()
- {
- #if defined(foo) \
- && defined(bar)
- char *foo = "asdf\
- asdf\
- asdf",
- *bar;
- {
- int i;
- char *foo = "asdf\
- asdf\
- asdf",
- *bar;
- }
- #endif
- }
- #endif
-
- int y; // comment
- // comment
-
- // comment
-
- {
- Constructor(int a,
- int b ) : BaseClass(a)
- {
- }
- }
-
- void foo()
- {
- char one,
- two;
- struct bla piet,
- jan;
- enum foo kees,
- jannie;
- static unsigned sdf,
- krap;
- unsigned int piet,
- jan;
- int
- kees,
- jan;
- }
-
- {
- t(int f,
- int d); // )
- d();
- }
-
- Constructor::Constructor(int a,
- int b
- ) :
- BaseClass(a,
- b,
- c),
- mMember(b),
- {
- }
-
- Constructor::Constructor(int a,
- int b ) :
- BaseClass(a)
- {
- }
-
- Constructor::Constructor(int a,
- int b ) /*x*/ : /*x*/ BaseClass(a),
- member(b)
- {
- }
-
- A::A(int a, int b)
- : aa(a),
- bb(b),
- cc(c)
- {
- }
-
- class CAbc :
- public BaseClass1,
- protected BaseClass2
- {
- int Test() { return FALSE; }
- int Test1() { return TRUE; }
-
- CAbc(int a, int b ) :
- BaseClass(a)
- {
- switch(xxx)
- {
- case abc:
- asdf();
- break;
-
- case 999:
- baer();
- break;
- }
- }
-
- public: // <-- this was incoreectly indented before!!
- void testfall();
- protected:
- void testfall();
- };
-
- class CAbc : public BaseClass1,
- protected BaseClass2
- {
- };
-
- static struct
- {
- int a;
- int b;
- } variable[COUNT] =
- {
- {
- 123,
- 456
- },
- {
- 123,
- 456
- }
- };
-
- static struct
- {
- int a;
- int b;
- } variable[COUNT] =
- {
- { 123, 456 },
- { 123, 456 }
- };
-
- void asdf() /* ind_maxparen may cause trouble here */
- {
- if ((0
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1
- && 1)) break;
- }
-
- foo()
- {
- a = cond ? foo() : asdf
- + asdf;
-
- a = cond ?
- foo() : asdf
- + asdf;
- }
-
- int main(void)
- {
- if (a)
- if (b)
- 2;
- else 3;
- next_line_of_code();
- }
-
- barry()
- {
- Foo::Foo (int one,
- int two)
- : something(4)
- {}
- }
-
- barry()
- {
- Foo::Foo (int one, int two)
- : something(4)
- {}
- }
-
- Constructor::Constructor(int a,
- int b
- ) :
- BaseClass(a,
- b,
- c),
- mMember(b)
- {
- }
- int main ()
- {
- if (lala)
- do
- ++(*lolo);
- while (lili
- && lele);
- lulu;
- }
-
- int main ()
- {
- switch (c)
- {
- case 'c': if (cond)
- {
- }
- }
- }
-
- main()
- {
- (void) MyFancyFuasdfadsfnction(
- argument);
- }
-
- main()
- {
- char foo[] = "/*";
- /* as
- df */
- hello
- }
-
- /* valid namespaces with normal indent */
- namespace
- {
- {
- 111111111111;
- }
- }
- namespace /* test */
- {
- 11111111111111111;
- }
- namespace // test
- {
- 111111111111111111;
- }
- namespace
- {
- 111111111111111111;
- }
- namespace test
- {
- 111111111111111111;
- }
- namespace{
- 111111111111111111;
- }
- namespace test{
- 111111111111111111;
- }
- namespace {
- 111111111111111111;
- }
- namespace test {
- 111111111111111111;
- namespace test2 {
- 22222222222222222;
- }
- }
-
- /* invalid namespaces use block indent */
- namespace test test2 {
- 111111111111111111111;
- }
- namespace11111111111 {
- 111111111111;
- }
- namespace() {
- 1111111111111;
- }
- namespace()
- {
- 111111111111111111;
- }
- namespace test test2
- {
- 1111111111111111111;
- }
- namespace111111111
- {
- 111111111111111111;
- }
- void getstring() {
- /* Raw strings */
- const char* s = R"(
- test {
- # comment
- field: 123
- }
- )";
- }
- void getstring() {
- const char* s = R"foo(
- test {
- # comment
- field: 123
- }
- )foo";
- }
-
- {
- int a[4] = {
- [0] = 0,
- [1] = 1,
- [2] = 2,
- [3] = 3,
- };
- }
-
- {
- a = b[2]
- + 3;
- }
-
- {
- if (1)
- /* aaaaa
- * bbbbb
- */
- a = 1;
- }
-
- void func()
- {
- switch (foo)
- {
- case (bar):
- if (baz())
- quux();
- break;
- case (shmoo):
- if (!bar)
- {
- }
- case (foo1):
- switch (bar)
- {
- case baz:
- baz_f();
- break;
- }
- break;
- default:
- baz();
- baz();
- break;
- }
- }
-
- /* end of AUTO */
- ]=])
- end)
-
- it('2 is working', function()
- insert_([=[
-
- {
-
- /* this is
- * a real serious important big
- * comment
- */
- /* insert " about life, the universe, and the rest" after "serious" */
- }
- ]=])
-
- feed_command('set tw=0 noai fo=croq')
- feed_command('let &wm = &columns - 20')
- feed_command('/serious/e')
- feed('a about life, the universe, and the rest<esc>')
-
- expect([=[
-
- {
-
- /* this is
- * a real serious
- * about life, the
- * universe, and the
- * rest important big
- * comment
- */
- /* insert " about life, the universe, and the rest" after "serious" */
- }
- ]=])
- end)
-
- it('3 is working', function()
- insert_([=[
-
- {
- /*
- * Testing for comments, without 'cin' set
- */
-
- /*
- * what happens here?
- */
-
- /*
- the end of the comment, try inserting a line below */
-
- /* how about
- this one */
- }
- ]=])
-
- feed_command('set nocin')
- feed_command('/comments')
- feed('joabout life<esc>/happens<cr>')
- feed('jothere<esc>/below<cr>')
- feed('oline<esc>/this<cr>')
- feed('Ohello<esc>')
-
- expect([=[
-
- {
- /*
- * Testing for comments, without 'cin' set
- */
- about life
-
- /*
- * what happens here?
- */
- there
-
- /*
- the end of the comment, try inserting a line below */
- line
-
- /* how about
- hello
- this one */
- }
- ]=])
- end)
-
- it('4 is working', function()
- insert_([=[
-
- {
- var = this + that + vec[0] * vec[0]
- + vec[1] * vec[1]
- + vec2[2] * vec[2];
- }
- ]=])
- feed_command('set cin')
- feed_command('/vec2')
- feed('==<cr>')
-
- expect([=[
-
- {
- var = this + that + vec[0] * vec[0]
- + vec[1] * vec[1]
- + vec2[2] * vec[2];
- }
- ]=])
- end)
-
- it('5 is working', function()
- insert_([=[
-
- {
- asdf asdflkajds f;
- if (tes & ting) {
- asdf asdf asdf ;
- asdfa sdf asdf;
- }
- testing1;
- if (tes & ting)
- {
- asdf asdf asdf ;
- asdfa sdf asdf;
- }
- testing2;
- }
- ]=])
-
- feed_command('set cin')
- feed_command('set cino=}4')
- feed_command('/testing1')
- feed('k2==/testing2<cr>')
- feed('k2==<cr>')
-
- expect([=[
-
- {
- asdf asdflkajds f;
- if (tes & ting) {
- asdf asdf asdf ;
- asdfa sdf asdf;
- }
- testing1;
- if (tes & ting)
- {
- asdf asdf asdf ;
- asdfa sdf asdf;
- }
- testing2;
- }
- ]=])
- end)
-
- it('6 is working', function()
- insert_([=[
-
- main ( int first_par, /*
- * Comment for
- * first par
- */
- int second_par /*
- * Comment for
- * second par
- */
- )
- {
- func( first_par, /*
- * Comment for
- * first par
- */
- second_par /*
- * Comment for
- * second par
- */
- );
-
- }
- ]=])
-
- feed_command('set cin')
- feed_command('set cino=(0,)20')
- feed_command('/main')
- feed('=][<cr>')
-
- expect([=[
-
- main ( int first_par, /*
- * Comment for
- * first par
- */
- int second_par /*
- * Comment for
- * second par
- */
- )
- {
- func( first_par, /*
- * Comment for
- * first par
- */
- second_par /*
- * Comment for
- * second par
- */
- );
-
- }
- ]=])
- end)
-
- it('7 is working', function()
- insert_([=[
-
- main(void)
- {
- /* Make sure that cino=X0s is not parsed like cino=Xs. */
- if (cond)
- foo();
- else
- {
- bar();
- }
- }
- ]=])
-
- feed_command('set cin')
- feed_command('set cino=es,n0s')
- feed_command('/main')
- feed('=][<cr>')
-
- expect([=[
-
- main(void)
- {
- /* Make sure that cino=X0s is not parsed like cino=Xs. */
- if (cond)
- foo();
- else
- {
- bar();
- }
- }
- ]=])
- end)
-
- it('8 is working', function()
- insert_([=[
-
- {
- do
- {
- if ()
- {
- if ()
- asdf;
- else
- asdf;
- }
- } while ();
- cmd; /* this should go under the } */
- }
- ]=])
-
- feed_command('set cin')
- feed_command('set cino=')
- feed(']]=][<cr>')
-
- expect([=[
-
- {
- do
- {
- if ()
- {
- if ()
- asdf;
- else
- asdf;
- }
- } while ();
- cmd; /* this should go under the } */
- }
- ]=])
- end)
-
- it('9 is working', function()
- insert_([=[
-
- void f()
- {
- if ( k() ) {
- l();
-
- } else { /* Start (two words) end */
- m();
- }
-
- n();
- }
- ]=])
-
- feed(']]=][<cr>')
-
- expect([=[
-
- void f()
- {
- if ( k() ) {
- l();
-
- } else { /* Start (two words) end */
- m();
- }
-
- n();
- }
- ]=])
- end)
-
- it('10 is working', function()
- -- This is nasty. This is the only test case where the buffer content
- -- differs from the original. Why? Proper behaviour of this test depends on
- -- the fact that the setup code contains an (unbalanced) opening curly
- -- bracket in "set cino={s,e-s". This bracket actually affects the outcome
- -- of the test: without it the curly bracket under "void f()" would not be
- -- indented properly. And that's why we've had to add one explicitly.
- insert_([=[
- { <= THIS IS THE CURLY BRACKET EXPLAINED IN THE COMMENT.
-
- void f()
- {
- if ( k() )
- {
- l();
- } else { /* Start (two words) end */
- m();
- }
- n(); /* should be under the if () */
- }
- ]=])
-
- feed_command('set cino={s,e-s')
- feed(']]=][<cr>')
-
- expect([=[
- { <= THIS IS THE CURLY BRACKET EXPLAINED IN THE COMMENT.
-
- void f()
- {
- if ( k() )
- {
- l();
- } else { /* Start (two words) end */
- m();
- }
- n(); /* should be under the if () */
- }
- ]=])
- end)
-
- it('11 is working', function()
- insert_([=[
-
- void bar(void)
- {
- static array[2][2] =
- {
- { 1, 2 },
- { 3, 4 },
- }
-
- while (a)
- {
- foo(&a);
- }
-
- {
- int a;
- {
- a = a + 1;
- }
- }
- b = a;
- }
-
- void func(void)
- {
- a = 1;
- {
- b = 2;
- }
- c = 3;
- d = 4;
- }
- /* foo */
- ]=])
-
- feed_command('set cino={s,fs')
- feed(']]=/ foo<cr>')
-
- expect([=[
-
- void bar(void)
- {
- static array[2][2] =
- {
- { 1, 2 },
- { 3, 4 },
- }
-
- while (a)
- {
- foo(&a);
- }
-
- {
- int a;
- {
- a = a + 1;
- }
- }
- b = a;
- }
-
- void func(void)
- {
- a = 1;
- {
- b = 2;
- }
- c = 3;
- d = 4;
- }
- /* foo */
- ]=])
- end)
-
- it('12 is working', function()
- insert_([=[
-
- a()
- {
- do {
- a = a +
- a;
- } while ( a ); /* add text under this line */
- if ( a )
- a;
- }
- ]=])
-
- feed_command('set cino=')
- feed_command('/while')
- feed('ohere<esc>')
-
- expect([=[
-
- a()
- {
- do {
- a = a +
- a;
- } while ( a ); /* add text under this line */
- here
- if ( a )
- a;
- }
- ]=])
- end)
-
- it('13 is working', function()
- insert_([=[
-
- a()
- {
- label1:
- /* hmm */
- // comment
- }
- ]=])
-
- feed_command('set cino= com=')
- feed_command('/comment')
- feed('olabel2: b();<cr>label3 /* post */:<cr>/* pre */ label4:<cr>f(/*com*/);<cr>if (/*com*/)<cr>cmd();<esc>')
-
- expect([=[
-
- a()
- {
- label1:
- /* hmm */
- // comment
- label2: b();
- label3 /* post */:
- /* pre */ label4:
- f(/*com*/);
- if (/*com*/)
- cmd();
- }
- ]=])
- end)
-
- it('14 is working', function()
- insert_([=[
-
- /*
- * A simple comment
- */
-
- /*
- ** A different comment
- */
- ]=])
-
- feed_command('set comments& comments^=s:/*,m:**,ex:*/')
- feed_command('/simple')
- feed('=5j<cr>')
-
- expect([=[
-
- /*
- * A simple comment
- */
-
- /*
- ** A different comment
- */
- ]=])
- end)
-
- it('15 is working', function()
- insert_([=[
-
-
- void f()
- {
-
- /*********
- A comment.
- *********/
- }
- ]=])
-
- feed_command('set cino=c0')
- feed_command('set comments& comments-=s1:/* comments^=s0:/*')
- feed('2kdd]]=][<cr>')
-
- expect([=[
-
- void f()
- {
-
- /*********
- A comment.
- *********/
- }
- ]=])
- end)
-
- it('16 is working', function()
- insert_([=[
-
-
- void f()
- {
-
- /*********
- A comment.
- *********/
- }
- ]=])
-
- feed_command('set cino=c0,C1')
- feed_command('set comments& comments-=s1:/* comments^=s0:/*')
- feed('2kdd]]=][<cr>')
-
- expect([=[
-
- void f()
- {
-
- /*********
- A comment.
- *********/
- }
- ]=])
- end)
-
- it('17 is working', function()
- insert_([=[
-
- void f()
- {
- c = c1 &&
- (
- c2 ||
- c3
- ) && c4;
- }
- ]=])
-
- feed_command('set cino=')
- feed(']]=][<cr>')
-
- expect([=[
-
- void f()
- {
- c = c1 &&
- (
- c2 ||
- c3
- ) && c4;
- }
- ]=])
- end)
-
- it('18 is working', function()
- insert_([=[
-
-
- void f()
- {
- c = c1 &&
- (
- c2 ||
- c3
- ) && c4;
- }
- ]=])
-
- feed_command('set cino=(s')
- feed('2kdd]]=][<cr>')
-
- expect([=[
-
- void f()
- {
- c = c1 &&
- (
- c2 ||
- c3
- ) && c4;
- }
- ]=])
- end)
-
- it('19 is working', function()
- insert_([=[
-
-
- void f()
- {
- c = c1 &&
- (
- c2 ||
- c3
- ) && c4;
- }
- ]=])
-
- feed_command('set cino=(s,U1 ')
- feed('2kdd]]=][<cr>')
-
- expect([=[
-
- void f()
- {
- c = c1 &&
- (
- c2 ||
- c3
- ) && c4;
- }
- ]=])
- end)
-
- it('20 is working', function()
- insert_([=[
-
-
- void f()
- {
- if ( c1
- && ( c2
- || c3))
- foo;
- }
- ]=])
-
- feed_command('set cino=(0')
- feed('2kdd]]=][<cr>')
-
- expect([=[
-
- void f()
- {
- if ( c1
- && ( c2
- || c3))
- foo;
- }
- ]=])
- end)
-
- it('21 is working', function()
- insert_([=[
-
-
- void f()
- {
- if ( c1
- && ( c2
- || c3))
- foo;
- }
- ]=])
-
- feed_command('set cino=(0,w1 ')
- feed('2kdd]]=][<cr>')
-
- expect([=[
-
- void f()
- {
- if ( c1
- && ( c2
- || c3))
- foo;
- }
- ]=])
- end)
-
- it('22 is working', function()
- insert_([=[
-
-
- void f()
- {
- c = c1 && (
- c2 ||
- c3
- ) && c4;
- if (
- c1 && c2
- )
- foo;
- }
- ]=])
-
- feed_command('set cino=(s')
- feed('2kdd]]=][<cr>')
-
- expect([=[
-
- void f()
- {
- c = c1 && (
- c2 ||
- c3
- ) && c4;
- if (
- c1 && c2
- )
- foo;
- }
- ]=])
- end)
-
- it('23 is working', function()
- insert_([=[
-
-
- void f()
- {
- c = c1 && (
- c2 ||
- c3
- ) && c4;
- if (
- c1 && c2
- )
- foo;
- }
- ]=])
-
- feed_command('set cino=(s,m1 ')
- feed('2kdd]]=][<cr>')
-
- expect([=[
-
- void f()
- {
- c = c1 && (
- c2 ||
- c3
- ) && c4;
- if (
- c1 && c2
- )
- foo;
- }
- ]=])
- end)
-
- it('24 is working', function()
- insert_([=[
-
-
- void f()
- {
- switch (x)
- {
- case 1:
- a = b;
- break;
- default:
- a = 0;
- break;
- }
- }
- ]=])
-
- feed_command('set cino=b1')
- feed('2kdd]]=][<cr>')
-
- expect([=[
-
- void f()
- {
- switch (x)
- {
- case 1:
- a = b;
- break;
- default:
- a = 0;
- break;
- }
- }
- ]=])
- end)
-
- it('25 is working', function()
- insert_([=[
-
-
- void f()
- {
- invokeme(
- argu,
- ment);
- invokeme(
- argu,
- ment
- );
- invokeme(argu,
- ment
- );
- }
- ]=])
-
- feed_command('set cino=(0,W5')
- feed('2kdd]]=][<cr>')
-
- expect([=[
-
- void f()
- {
- invokeme(
- argu,
- ment);
- invokeme(
- argu,
- ment
- );
- invokeme(argu,
- ment
- );
- }
- ]=])
- end)
-
- it('26 is working', function()
- insert_([=[
-
-
- void f()
- {
- statement;
- // comment 1
- // comment 2
- }
- ]=])
-
- feed_command('set cino=/6')
- feed('2kdd]]=][<cr>')
-
- expect([=[
-
- void f()
- {
- statement;
- // comment 1
- // comment 2
- }
- ]=])
- end)
-
- it('27 is working', function()
- insert_([=[
-
-
- void f()
- {
- statement;
- // comment 1
- // comment 2
- }
- ]=])
-
- feed_command('set cino=')
- feed('2kdd]]/comment 1/+1<cr>')
- feed('==<cr>')
-
- expect([=[
-
- void f()
- {
- statement;
- // comment 1
- // comment 2
- }
- ]=])
- end)
-
- it('28 is working', function()
- insert_([=[
-
-
- class CAbc
- {
- int Test() { return FALSE; }
-
- public: // comment
- void testfall();
- protected:
- void testfall();
- };
- ]=])
-
- feed_command('set cino=g0')
- feed('2kdd]]=][<cr>')
-
- expect([=[
-
- class CAbc
- {
- int Test() { return FALSE; }
-
- public: // comment
- void testfall();
- protected:
- void testfall();
- };
- ]=])
- end)
-
- it('29 is working', function()
- insert_([=[
-
-
- class Foo : public Bar
- {
- public:
- virtual void method1(void) = 0;
- virtual void method2(int arg1,
- int arg2,
- int arg3) = 0;
- };
- ]=])
-
- feed_command('set cino=(0,gs,hs')
- feed('2kdd]]=][<cr>')
-
- expect([=[
-
- class Foo : public Bar
- {
- public:
- virtual void method1(void) = 0;
- virtual void method2(int arg1,
- int arg2,
- int arg3) = 0;
- };
- ]=])
- end)
-
- it('30 is working', function()
- insert_([=[
-
-
- void
- foo()
- {
- if (a)
- {
- } else
- asdf;
- }
- ]=])
-
- feed_command('set cino=+20')
- feed('2kdd]]=][<cr>')
-
- expect([=[
-
- void
- foo()
- {
- if (a)
- {
- } else
- asdf;
- }
- ]=])
- end)
-
- it('31 is working', function()
- insert_([=[
-
-
- {
- averylongfunctionnamelongfunctionnameaverylongfunctionname()->asd(
- asdasdf,
- func(asdf,
- asdfadsf),
- asdfasdf
- );
-
- /* those are ugly, but consequent */
-
- func()->asd(asdasdf,
- averylongfunctionname(
- abc,
- dec)->averylongfunctionname(
- asdfadsf,
- asdfasdf,
- asdfasdf,
- ),
- func(asdfadf,
- asdfasdf
- ),
- asdasdf
- );
-
- averylongfunctionnameaverylongfunctionnameavery()->asd(fasdf(
- abc,
- dec)->asdfasdfasdf(
- asdfadsf,
- asdfasdf,
- asdfasdf,
- ),
- func(asdfadf,
- asdfasdf),
- asdasdf
- );
- }
- ]=])
-
- feed_command('set cino=(0,W2s')
- feed('2kdd]]=][<cr>')
-
- expect([=[
-
- {
- averylongfunctionnamelongfunctionnameaverylongfunctionname()->asd(
- asdasdf,
- func(asdf,
- asdfadsf),
- asdfasdf
- );
-
- /* those are ugly, but consequent */
-
- func()->asd(asdasdf,
- averylongfunctionname(
- abc,
- dec)->averylongfunctionname(
- asdfadsf,
- asdfasdf,
- asdfasdf,
- ),
- func(asdfadf,
- asdfasdf
- ),
- asdasdf
- );
-
- averylongfunctionnameaverylongfunctionnameavery()->asd(fasdf(
- abc,
- dec)->asdfasdfasdf(
- asdfadsf,
- asdfasdf,
- asdfasdf,
- ),
- func(asdfadf,
- asdfasdf),
- asdasdf
- );
- }
- ]=])
- end)
-
- it('32 is working', function()
- insert_([=[
-
-
- int main ()
- {
- if (cond1 &&
- cond2
- )
- foo;
- }
- ]=])
-
- feed_command('set cino=M1')
- feed('2kdd]]=][<cr>')
-
- expect([=[
-
- int main ()
- {
- if (cond1 &&
- cond2
- )
- foo;
- }
- ]=])
- end)
-
- it('33 is working', function()
- insert_([=[
-
-
- void func(int a
- #if defined(FOO)
- , int b
- , int c
- #endif
- )
- {
- }
- ]=])
-
- feed_command('set cino=(0,ts')
- feed('2kdd2j=][<cr>')
-
- expect([=[
-
- void func(int a
- #if defined(FOO)
- , int b
- , int c
- #endif
- )
- {
- }
- ]=])
- end)
-
- it('34 is working', function()
- insert_([=[
-
-
-
- void
- func(int a
- #if defined(FOO)
- , int b
- , int c
- #endif
- )
- {
- }
- ]=])
-
- feed_command('set cino=(0')
- feed('2kdd2j=][<cr>')
-
- expect([=[
-
-
- void
- func(int a
- #if defined(FOO)
- , int b
- , int c
- #endif
- )
- {
- }
- ]=])
- end)
-
- it('35 is working', function()
- insert_([=[
-
-
- void func(void)
- {
- if(x==y)
- if(y==z)
- foo=1;
- else { bar=1;
- baz=2;
- }
- printf("Foo!\n");
- }
-
- void func1(void)
- {
- char* tab[] = {"foo", "bar",
- "baz", "quux",
- "this line used", "to be indented incorrectly"};
- foo();
- }
-
- void func2(void)
- {
- int tab[] =
- {1, 2,
- 3, 4,
- 5, 6};
-
- printf("This line used to be indented incorrectly.\n");
- }
-
- int foo[]
- #ifdef BAR
-
- = { 1, 2, 3,
- 4, 5, 6 }
-
- #endif
- ;
- int baz;
-
- void func3(void)
- {
- int tab[] = {
- 1, 2,
- 3, 4,
- 5, 6};
-
- printf("Don't you dare indent this line incorrectly!\n");
- }
-
- void
- func4(a, b,
- c)
- int a;
- int b;
- int c;
- {
- }
-
- void
- func5(
- int a,
- int b)
- {
- }
-
- void
- func6(
- int a)
- {
- }
- ]=])
-
- feed_command('set cino&')
- feed('2kdd2j=7][<cr>')
-
- expect([=[
-
- void func(void)
- {
- if(x==y)
- if(y==z)
- foo=1;
- else { bar=1;
- baz=2;
- }
- printf("Foo!\n");
- }
-
- void func1(void)
- {
- char* tab[] = {"foo", "bar",
- "baz", "quux",
- "this line used", "to be indented incorrectly"};
- foo();
- }
-
- void func2(void)
- {
- int tab[] =
- {1, 2,
- 3, 4,
- 5, 6};
-
- printf("This line used to be indented incorrectly.\n");
- }
-
- int foo[]
- #ifdef BAR
-
- = { 1, 2, 3,
- 4, 5, 6 }
-
- #endif
- ;
- int baz;
-
- void func3(void)
- {
- int tab[] = {
- 1, 2,
- 3, 4,
- 5, 6};
-
- printf("Don't you dare indent this line incorrectly!\n");
- }
-
- void
- func4(a, b,
- c)
- int a;
- int b;
- int c;
- {
- }
-
- void
- func5(
- int a,
- int b)
- {
- }
-
- void
- func6(
- int a)
- {
- }
- ]=])
- end)
-
- it('36 is working', function()
- insert_([=[
-
-
- void func(void)
- {
- int tab[] =
- {
- 1, 2, 3,
- 4, 5, 6};
-
- printf("Indent this line correctly!\n");
-
- switch (foo)
- {
- case bar:
- printf("bar");
- break;
- case baz: {
- printf("baz");
- break;
- }
- case quux:
- printf("But don't break the indentation of this instruction\n");
- break;
- }
- }
- ]=])
-
- feed_command('set cino&')
- feed_command('set cino+=l1')
- feed('2kdd2j=][<cr>')
-
- expect([=[
-
- void func(void)
- {
- int tab[] =
- {
- 1, 2, 3,
- 4, 5, 6};
-
- printf("Indent this line correctly!\n");
-
- switch (foo)
- {
- case bar:
- printf("bar");
- break;
- case baz: {
- printf("baz");
- break;
- }
- case quux:
- printf("But don't break the indentation of this instruction\n");
- break;
- }
- }
- ]=])
- end)
-
- it('37 is working', function()
- insert_([=[
-
-
- void func(void)
- {
- cout << "a"
- << "b"
- << ") :"
- << "c";
- }
- ]=])
-
- feed_command('set cino&')
- feed('2kdd2j=][<cr>')
-
- expect([=[
-
- void func(void)
- {
- cout << "a"
- << "b"
- << ") :"
- << "c";
- }
- ]=])
- end)
-
- it('38 is working', function()
- insert_([=[
-
- void func(void)
- {
- /*
- * This is a comment.
- */
- }
- ]=])
-
- feed_command('set com=s1:/*,m:*,ex:*/')
- feed(']]3jofoo();<esc>')
-
- expect([=[
-
- void func(void)
- {
- /*
- * This is a comment.
- */
- foo();
- }
- ]=])
- end)
-
- it('39 is working', function()
- insert_([=[
-
-
- void func(void)
- {
- for (int i = 0; i < 10; ++i)
- if (i & 1) {
- foo(1);
- } else
- foo(0);
- baz();
- }
- ]=])
-
- feed_command('set cino&')
- feed('2kdd2j=][<cr>')
-
- expect([=[
-
- void func(void)
- {
- for (int i = 0; i < 10; ++i)
- if (i & 1) {
- foo(1);
- } else
- foo(0);
- baz();
- }
- ]=])
- end)
-
- it('40 is working', function()
- insert_([=[
-
-
- void func(void)
- {
- if (condition1
- && condition2)
- action();
- function(argument1
- && argument2);
-
- if (c1 && (c2 ||
- c3))
- foo;
- if (c1 &&
- (c2 || c3))
- {
- }
-
- if ( c1
- && ( c2
- || c3))
- foo;
- func( c1
- && ( c2
- || c3))
- foo;
- }
- ]=])
-
- feed_command('set cino=k2s,(0')
- feed('2kdd3j=][<cr>')
-
- expect([=[
-
- void func(void)
- {
- if (condition1
- && condition2)
- action();
- function(argument1
- && argument2);
-
- if (c1 && (c2 ||
- c3))
- foo;
- if (c1 &&
- (c2 || c3))
- {
- }
-
- if ( c1
- && ( c2
- || c3))
- foo;
- func( c1
- && ( c2
- || c3))
- foo;
- }
- ]=])
- end)
-
- it('41 is working', function()
- insert_([=[
-
-
- void func(void)
- {
- if (condition1
- && condition2)
- action();
- function(argument1
- && argument2);
-
- if (c1 && (c2 ||
- c3))
- foo;
- if (c1 &&
- (c2 || c3))
- {
- }
-
- if ( c1
- && ( c2
- || c3))
- foo;
- func( c1
- && ( c2
- || c3))
- foo;
- }
- ]=])
-
- feed_command('set cino=k2s,(s')
- feed('2kdd3j=][<cr>')
-
- expect([=[
-
- void func(void)
- {
- if (condition1
- && condition2)
- action();
- function(argument1
- && argument2);
-
- if (c1 && (c2 ||
- c3))
- foo;
- if (c1 &&
- (c2 || c3))
- {
- }
-
- if ( c1
- && ( c2
- || c3))
- foo;
- func( c1
- && ( c2
- || c3))
- foo;
- }
- ]=])
- end)
-
- it('42 is working', function()
- insert_([=[
-
-
- void func(void)
- {
- if (condition1
- && condition2)
- action();
- function(argument1
- && argument2);
-
- if (c1 && (c2 ||
- c3))
- foo;
- if (c1 &&
- (c2 || c3))
- {
- }
- if (c123456789
- && (c22345
- || c3))
- printf("foo\n");
-
- c = c1 &&
- (
- c2 ||
- c3
- ) && c4;
- }
- ]=])
-
- feed_command('set cino=k2s,(s,U1')
- feed('2kdd3j=][<cr>')
-
- expect([=[
-
- void func(void)
- {
- if (condition1
- && condition2)
- action();
- function(argument1
- && argument2);
-
- if (c1 && (c2 ||
- c3))
- foo;
- if (c1 &&
- (c2 || c3))
- {
- }
- if (c123456789
- && (c22345
- || c3))
- printf("foo\n");
-
- c = c1 &&
- (
- c2 ||
- c3
- ) && c4;
- }
- ]=])
- end)
-
- it('43 is working', function()
- insert_([=[
-
-
- void func(void)
- {
- if (condition1
- && condition2)
- action();
- function(argument1
- && argument2);
-
- if (c1 && (c2 ||
- c3))
- foo;
- if (c1 &&
- (c2 || c3))
- {
- }
- if (c123456789
- && (c22345
- || c3))
- printf("foo\n");
-
- if ( c1
- && ( c2
- || c3))
- foo;
-
- a_long_line(
- argument,
- argument);
- a_short_line(argument,
- argument);
- }
- ]=])
-
- feed_command('set cino=k2s,(0,W4')
- feed('2kdd3j=][<cr>')
-
- expect([=[
-
- void func(void)
- {
- if (condition1
- && condition2)
- action();
- function(argument1
- && argument2);
-
- if (c1 && (c2 ||
- c3))
- foo;
- if (c1 &&
- (c2 || c3))
- {
- }
- if (c123456789
- && (c22345
- || c3))
- printf("foo\n");
-
- if ( c1
- && ( c2
- || c3))
- foo;
-
- a_long_line(
- argument,
- argument);
- a_short_line(argument,
- argument);
- }
- ]=])
- end)
-
- it('44 is working', function()
- insert_([=[
-
-
- void func(void)
- {
- if (condition1
- && condition2)
- action();
- function(argument1
- && argument2);
-
- if (c1 && (c2 ||
- c3))
- foo;
- if (c1 &&
- (c2 || c3))
- {
- }
- if (c123456789
- && (c22345
- || c3))
- printf("foo\n");
- }
- ]=])
-
- feed_command('set cino=k2s,u2')
- feed('2kdd3j=][<cr>')
-
- expect([=[
-
- void func(void)
- {
- if (condition1
- && condition2)
- action();
- function(argument1
- && argument2);
-
- if (c1 && (c2 ||
- c3))
- foo;
- if (c1 &&
- (c2 || c3))
- {
- }
- if (c123456789
- && (c22345
- || c3))
- printf("foo\n");
- }
- ]=])
- end)
-
- it('45 is working', function()
- insert_([=[
-
-
- void func(void)
- {
- if (condition1
- && condition2)
- action();
- function(argument1
- && argument2);
-
- if (c1 && (c2 ||
- c3))
- foo;
- if (c1 &&
- (c2 || c3))
- {
- }
- if (c123456789
- && (c22345
- || c3))
- printf("foo\n");
-
- if ( c1
- && ( c2
- || c3))
- foo;
- func( c1
- && ( c2
- || c3))
- foo;
- }
- ]=])
-
- feed_command('set cino=k2s,(0,w1')
- feed('2kdd3j=][<cr>')
-
- expect([=[
-
- void func(void)
- {
- if (condition1
- && condition2)
- action();
- function(argument1
- && argument2);
-
- if (c1 && (c2 ||
- c3))
- foo;
- if (c1 &&
- (c2 || c3))
- {
- }
- if (c123456789
- && (c22345
- || c3))
- printf("foo\n");
-
- if ( c1
- && ( c2
- || c3))
- foo;
- func( c1
- && ( c2
- || c3))
- foo;
- }
- ]=])
- end)
-
- it('46 is working', function()
- insert_([=[
-
-
- void func(void)
- {
- if (condition1
- && condition2)
- action();
- function(argument1
- && argument2);
-
- if (c1 && (c2 ||
- c3))
- foo;
- if (c1 &&
- (c2 || c3))
- {
- }
- }
- ]=])
-
- feed_command('set cino=k2,(s')
- feed('2kdd3j=][<cr>')
-
- expect([=[
-
- void func(void)
- {
- if (condition1
- && condition2)
- action();
- function(argument1
- && argument2);
-
- if (c1 && (c2 ||
- c3))
- foo;
- if (c1 &&
- (c2 || c3))
- {
- }
- }
- ]=])
- end)
-
- it('47 is working', function()
- insert_([=[
-
- NAMESPACESTART
- /* valid namespaces with normal indent */
- namespace
- {
- {
- 111111111111;
- }
- }
- namespace /* test */
- {
- 11111111111111111;
- }
- namespace // test
- {
- 111111111111111111;
- }
- namespace
- {
- 111111111111111111;
- }
- namespace test
- {
- 111111111111111111;
- }
- namespace test::cpp17
- {
- 111111111111111111;
- }
- namespace ::incorrectcpp17
- {
- 111111111111111111;
- }
- namespace test::incorrectcpp17::
- {
- 111111111111111111;
- }
- namespace test:incorrectcpp17
- {
- 111111111111111111;
- }
- namespace test:::incorrectcpp17
- {
- 111111111111111111;
- }
- namespace{
- 111111111111111111;
- }
- namespace test{
- 111111111111111111;
- }
- namespace {
- 111111111111111111;
- }
- namespace test {
- 111111111111111111;
- namespace test2 {
- 22222222222222222;
- }
- }
-
- /* invalid namespaces use block indent */
- namespace test test2 {
- 111111111111111111111;
- }
- namespace11111111111 {
- 111111111111;
- }
- namespace() {
- 1111111111111;
- }
- namespace()
- {
- 111111111111111111;
- }
- namespace test test2
- {
- 1111111111111111111;
- }
- namespace111111111
- {
- 111111111111111111;
- }
- NAMESPACEEND
- ]=])
-
- feed_command('set cino=N-s')
- feed_command('/^NAMESPACESTART')
- feed('=/^NAMESPACEEND<cr>')
-
- expect([=[
-
- NAMESPACESTART
- /* valid namespaces with normal indent */
- namespace
- {
- {
- 111111111111;
- }
- }
- namespace /* test */
- {
- 11111111111111111;
- }
- namespace // test
- {
- 111111111111111111;
- }
- namespace
- {
- 111111111111111111;
- }
- namespace test
- {
- 111111111111111111;
- }
- namespace test::cpp17
- {
- 111111111111111111;
- }
- namespace ::incorrectcpp17
- {
- 111111111111111111;
- }
- namespace test::incorrectcpp17::
- {
- 111111111111111111;
- }
- namespace test:incorrectcpp17
- {
- 111111111111111111;
- }
- namespace test:::incorrectcpp17
- {
- 111111111111111111;
- }
- namespace{
- 111111111111111111;
- }
- namespace test{
- 111111111111111111;
- }
- namespace {
- 111111111111111111;
- }
- namespace test {
- 111111111111111111;
- namespace test2 {
- 22222222222222222;
- }
- }
-
- /* invalid namespaces use block indent */
- namespace test test2 {
- 111111111111111111111;
- }
- namespace11111111111 {
- 111111111111;
- }
- namespace() {
- 1111111111111;
- }
- namespace()
- {
- 111111111111111111;
- }
- namespace test test2
- {
- 1111111111111111111;
- }
- namespace111111111
- {
- 111111111111111111;
- }
- NAMESPACEEND
- ]=])
- end)
-
- it('48 is working', function()
- insert_([=[
-
- JSSTART
- var bar = {
- foo: {
- that: this,
- some: ok,
- },
- "bar":{
- a : 2,
- b: "123abc",
- x: 4,
- "y": 5
- }
- }
- JSEND
- ]=])
-
- feed_command('set cino=j1,J1')
- feed_command('/^JSSTART')
- feed('=/^JSEND<cr>')
-
- expect([=[
-
- JSSTART
- var bar = {
- foo: {
- that: this,
- some: ok,
- },
- "bar":{
- a : 2,
- b: "123abc",
- x: 4,
- "y": 5
- }
- }
- JSEND
- ]=])
- end)
-
- it('49 is working', function()
- insert_([=[
-
- JSSTART
- var foo = [
- 1,
- 2,
- 3
- ];
- JSEND
- ]=])
-
- feed_command('set cino=j1,J1')
- feed_command('/^JSSTART')
- feed('=/^JSEND<cr>')
-
- expect([=[
-
- JSSTART
- var foo = [
- 1,
- 2,
- 3
- ];
- JSEND
- ]=])
- end)
-
- it('50 is working', function()
- insert_([=[
-
- JSSTART
- function bar() {
- var foo = [
- 1,
- 2,
- 3
- ];
- }
- JSEND
- ]=])
-
- feed_command('set cino=j1,J1')
- feed_command('/^JSSTART')
- feed('=/^JSEND<cr>')
-
- expect([=[
-
- JSSTART
- function bar() {
- var foo = [
- 1,
- 2,
- 3
- ];
- }
- JSEND
- ]=])
- end)
-
- it('51 is working', function()
- insert_([=[
-
- JSSTART
- (function($){
-
- if (cond &&
- cond) {
- stmt;
- }
- window.something.left =
- (width - 50 + offset) + "px";
- var class_name='myclass';
-
- function private_method() {
- }
-
- var public_method={
- method: function(options,args){
- private_method();
- }
- }
-
- function init(options) {
-
- $(this).data(class_name+'_public',$.extend({},{
- foo: 'bar',
- bar: 2,
- foobar: [
- 1,
- 2,
- 3
- ],
- callback: function(){
- return true;
- }
- }, options||{}));
- }
-
- $.fn[class_name]=function() {
-
- var _arguments=arguments;
- return this.each(function(){
-
- var options=$(this).data(class_name+'_public');
- if (!options) {
- init.apply(this,_arguments);
-
- } else {
- var method=public_method[_arguments[0]];
-
- if (typeof(method)!='function') {
- console.log(class_name+' has no method "'+_arguments[0]+'"');
- return false;
- }
- _arguments[0]=options;
- method.apply(this,_arguments);
- }
- });
- }
-
- })(jQuery);
- JSEND
- ]=])
-
- feed_command('set cino=j1,J1')
- feed_command('/^JSSTART')
- feed('=/^JSEND<cr>')
-
- expect([=[
-
- JSSTART
- (function($){
-
- if (cond &&
- cond) {
- stmt;
- }
- window.something.left =
- (width - 50 + offset) + "px";
- var class_name='myclass';
-
- function private_method() {
- }
-
- var public_method={
- method: function(options,args){
- private_method();
- }
- }
-
- function init(options) {
-
- $(this).data(class_name+'_public',$.extend({},{
- foo: 'bar',
- bar: 2,
- foobar: [
- 1,
- 2,
- 3
- ],
- callback: function(){
- return true;
- }
- }, options||{}));
- }
-
- $.fn[class_name]=function() {
-
- var _arguments=arguments;
- return this.each(function(){
-
- var options=$(this).data(class_name+'_public');
- if (!options) {
- init.apply(this,_arguments);
-
- } else {
- var method=public_method[_arguments[0]];
-
- if (typeof(method)!='function') {
- console.log(class_name+' has no method "'+_arguments[0]+'"');
- return false;
- }
- _arguments[0]=options;
- method.apply(this,_arguments);
- }
- });
- }
-
- })(jQuery);
- JSEND
- ]=])
- end)
-
- it('52 is working', function()
- insert_([=[
-
- JSSTART
- function init(options) {
- $(this).data(class_name+'_public',$.extend({},{
- foo: 'bar',
- bar: 2,
- foobar: [
- 1,
- 2,
- 3
- ],
- callback: function(){
- return true;
- }
- }, options||{}));
- }
- JSEND
- ]=])
-
- feed_command('set cino=j1,J1')
- feed_command('/^JSSTART')
- feed('=/^JSEND<cr>')
-
- expect([=[
-
- JSSTART
- function init(options) {
- $(this).data(class_name+'_public',$.extend({},{
- foo: 'bar',
- bar: 2,
- foobar: [
- 1,
- 2,
- 3
- ],
- callback: function(){
- return true;
- }
- }, options||{}));
- }
- JSEND
- ]=])
- end)
-
- it('53 is working', function()
- insert_([=[
-
- JSSTART
- (function($){
- function init(options) {
- $(this).data(class_name+'_public',$.extend({},{
- foo: 'bar',
- bar: 2,
- foobar: [
- 1,
- 2,
- 3
- ],
- callback: function(){
- return true;
- }
- }, options||{}));
- }
- })(jQuery);
- JSEND
- ]=])
-
- feed_command('set cino=j1,J1')
- feed_command('/^JSSTART')
- feed('=/^JSEND<cr>')
-
- expect([=[
-
- JSSTART
- (function($){
- function init(options) {
- $(this).data(class_name+'_public',$.extend({},{
- foo: 'bar',
- bar: 2,
- foobar: [
- 1,
- 2,
- 3
- ],
- callback: function(){
- return true;
- }
- }, options||{}));
- }
- })(jQuery);
- JSEND
- ]=])
- end)
-
- it('javascript indent / vim-patch 7.4.670', function()
- insert_([=[
-
- JSSTART
- // Results of JavaScript indent
- // 1
- (function(){
- var a = [
- 'a',
- 'b',
- 'c',
- 'd',
- 'e',
- 'f',
- 'g',
- 'h',
- 'i'
- ];
- }())
-
- // 2
- (function(){
- var a = [
- 0 +
- 5 *
- 9 *
- 'a',
- 'b',
- 0 +
- 5 *
- 9 *
- 'c',
- 'd',
- 'e',
- 'f',
- 'g',
- 'h',
- 'i'
- ];
- }())
-
- // 3
- (function(){
- var a = [
- 0 +
- // comment 1
- 5 *
- /* comment 2 */
- 9 *
- 'a',
- 'b',
- 0 +
- 5 *
- 9 *
- 'c',
- 'd',
- 'e',
- 'f',
- 'g',
- 'h',
- 'i'
- ];
- }())
-
- // 4
- {
- var a = [
- 0,
- 1
- ];
- var b;
- var c;
- }
-
- // 5
- {
- var a = [
- [
- 0
- ],
- 2,
- 3
- ];
- }
-
- // 6
- {
- var a = [
- [
- 0,
- 1
- ],
- 2,
- 3
- ];
- }
-
- // 7
- {
- var a = [
- // [
- 0,
- // 1
- // ],
- 2,
- 3
- ];
- }
-
- // 8
- var x = [
- (function(){
- var a,
- b,
- c,
- d,
- e,
- f,
- g,
- h,
- i;
- })
- ];
-
- // 9
- var a = [
- 0 +
- 5 *
- 9 *
- 'a',
- 'b',
- 0 +
- 5 *
- 9 *
- 'c',
- 'd',
- 'e',
- 'f',
- 'g',
- 'h',
- 'i'
- ];
-
- // 10
- var a,
- b,
- c,
- d,
- e,
- f,
- g,
- h,
- i;
- JSEND
- ]=])
-
- -- :set cino=j1,J1,+2
- feed_command('set cino=j1,J1,+2')
- feed_command('/^JSSTART')
- feed('=/^JSEND<cr>')
-
- expect([=[
-
- JSSTART
- // Results of JavaScript indent
- // 1
- (function(){
- var a = [
- 'a',
- 'b',
- 'c',
- 'd',
- 'e',
- 'f',
- 'g',
- 'h',
- 'i'
- ];
- }())
-
- // 2
- (function(){
- var a = [
- 0 +
- 5 *
- 9 *
- 'a',
- 'b',
- 0 +
- 5 *
- 9 *
- 'c',
- 'd',
- 'e',
- 'f',
- 'g',
- 'h',
- 'i'
- ];
- }())
-
- // 3
- (function(){
- var a = [
- 0 +
- // comment 1
- 5 *
- /* comment 2 */
- 9 *
- 'a',
- 'b',
- 0 +
- 5 *
- 9 *
- 'c',
- 'd',
- 'e',
- 'f',
- 'g',
- 'h',
- 'i'
- ];
- }())
-
- // 4
- {
- var a = [
- 0,
- 1
- ];
- var b;
- var c;
- }
-
- // 5
- {
- var a = [
- [
- 0
- ],
- 2,
- 3
- ];
- }
-
- // 6
- {
- var a = [
- [
- 0,
- 1
- ],
- 2,
- 3
- ];
- }
-
- // 7
- {
- var a = [
- // [
- 0,
- // 1
- // ],
- 2,
- 3
- ];
- }
-
- // 8
- var x = [
- (function(){
- var a,
- b,
- c,
- d,
- e,
- f,
- g,
- h,
- i;
- })
- ];
-
- // 9
- var a = [
- 0 +
- 5 *
- 9 *
- 'a',
- 'b',
- 0 +
- 5 *
- 9 *
- 'c',
- 'd',
- 'e',
- 'f',
- 'g',
- 'h',
- 'i'
- ];
-
- // 10
- var a,
- b,
- c,
- d,
- e,
- f,
- g,
- h,
- i;
- JSEND
- ]=])
- end)
-
- it('line continuations in macros / vim-patch 8.0.0148', function()
- insert_([=[
- /* start of define */
- {
- }
- #define AAA \
- BBB\
- CCC
-
- #define CNT \
- 1 + \
- 2 + \
- 4
- /* end of define */]=])
-
- feed_command('set cino&')
- feed_command('/start of define')
- feed('=/end of define<cr>')
-
- expect([=[
- /* start of define */
- {
- }
- #define AAA \
- BBB\
- CCC
-
- #define CNT \
- 1 + \
- 2 + \
- 4
- /* end of define */]=])
- end)
-
- it('* immediately follows comment / vim-patch 8.0.1291', function()
- insert_([=[
- {
- a = second/*bug*/*line;
- }]=])
-
- feed_command('set cin cino&')
- feed_command('/a = second')
- feed('ox')
-
- expect([=[
- {
- a = second/*bug*/*line;
- x
- }]=])
- end)
-end)
diff --git a/test/functional/legacy/008_autocommands_spec.lua b/test/functional/legacy/008_autocommands_spec.lua
index 002f037d09..4088cd1644 100644
--- a/test/functional/legacy/008_autocommands_spec.lua
+++ b/test/functional/legacy/008_autocommands_spec.lua
@@ -6,6 +6,7 @@ local source = helpers.source
local clear, command, expect, eq, eval = helpers.clear, helpers.command, helpers.expect, helpers.eq, helpers.eval
local write_file, dedent = helpers.write_file, helpers.dedent
local read_file = helpers.read_file
+local expect_exit = helpers.expect_exit
describe('autocommands that delete and unload buffers:', function()
local test_file = 'Xtest-008_autocommands.out'
@@ -78,7 +79,7 @@ describe('autocommands that delete and unload buffers:', function()
command('silent! edit Xxx1')
command('silent! edit Makefile') -- an existing file
command('silent! split new2')
- command('silent! quit')
+ expect_exit(command, 'silent! quit')
eq('VimLeave done',
string.match(read_file(test_file), "^%s*(.-)%s*$"))
end)
diff --git a/test/functional/legacy/012_directory_spec.lua b/test/functional/legacy/012_directory_spec.lua
index f666e51469..dd207ca0b4 100644
--- a/test/functional/legacy/012_directory_spec.lua
+++ b/test/functional/legacy/012_directory_spec.lua
@@ -16,6 +16,7 @@ local insert = helpers.insert
local command = helpers.command
local write_file = helpers.write_file
local curbufmeths = helpers.curbufmeths
+local expect_exit = helpers.expect_exit
local function ls_dir_sorted(dirname)
local files = {}
@@ -43,7 +44,7 @@ describe("'directory' option", function()
clear()
end)
teardown(function()
- command('qall!')
+ expect_exit(command, 'qall!')
helpers.rmdir('Xtest.je')
helpers.rmdir('Xtest2')
os.remove('Xtest1')
diff --git a/test/functional/legacy/031_close_commands_spec.lua b/test/functional/legacy/031_close_commands_spec.lua
index 64c67c9882..d02b1a2049 100644
--- a/test/functional/legacy/031_close_commands_spec.lua
+++ b/test/functional/legacy/031_close_commands_spec.lua
@@ -17,6 +17,7 @@ local source = helpers.source
local insert = helpers.insert
local expect = helpers.expect
local feed_command = helpers.feed_command
+local expect_exit = helpers.expect_exit
describe('Commands that close windows and/or buffers', function()
local function cleanup()
@@ -118,7 +119,7 @@ describe('Commands that close windows and/or buffers', function()
feed_command('q!')
feed('<CR>')
expect('testtext 1')
- source([[
+ expect_exit(source, [[
q!
" Now nvim should have exited
throw "Oh, Not finished yet."]])
diff --git a/test/functional/legacy/arglist_spec.lua b/test/functional/legacy/arglist_spec.lua
index 6a2e86ccb4..8379e426e0 100644
--- a/test/functional/legacy/arglist_spec.lua
+++ b/test/functional/legacy/arglist_spec.lua
@@ -1,8 +1,11 @@
-- Test argument list commands
local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
local clear, command, eq = helpers.clear, helpers.command, helpers.eq
local eval, exc_exec, neq = helpers.eval, helpers.exc_exec, helpers.neq
+local feed = helpers.feed
+local pcall_err = helpers.pcall_err
describe('argument list commands', function()
before_each(clear)
@@ -17,7 +20,7 @@ describe('argument list commands', function()
end
local function assert_fails(cmd, err)
- neq(exc_exec(cmd):find(err), nil)
+ neq(nil, exc_exec(cmd):find(err))
end
it('test that argidx() works', function()
@@ -206,7 +209,6 @@ describe('argument list commands', function()
command('%argd')
end)
-
it('test for autocommand that redefines the argument list, when doing ":all"', function()
command('autocmd BufReadPost Xxx2 next Xxx2 Xxx1')
command("call writefile(['test file Xxx1'], 'Xxx1')")
@@ -234,4 +236,45 @@ describe('argument list commands', function()
command('argdelete Xxx*')
command('bwipe! Xxx1 Xxx2 Xxx3')
end)
+
+ it('quitting Vim with unedited files in the argument list throws E173', function()
+ command('set nomore')
+ command('args a b c')
+ eq('Vim(quit):E173: 2 more files to edit', pcall_err(command, 'quit'))
+ end)
+
+ it(':confirm quit with unedited files in arglist', function()
+ local screen = Screen.new(60, 6)
+ screen:attach()
+ command('set nomore')
+ command('args a b c')
+ feed(':confirm quit\n')
+ screen:expect([[
+ |
+ ~ |
+ |
+ :confirm quit |
+ 2 more files to edit. Quit anyway? |
+ [Y]es, (N)o: ^ |
+ ]])
+ feed('N')
+ screen:expect([[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ |
+ ]])
+ feed(':confirm quit\n')
+ screen:expect([[
+ |
+ ~ |
+ |
+ :confirm quit |
+ 2 more files to edit. Quit anyway? |
+ [Y]es, (N)o: ^ |
+ ]])
+ feed('Y')
+ end)
end)
diff --git a/test/functional/legacy/assert_spec.lua b/test/functional/legacy/assert_spec.lua
index c2b22472bf..4829a0bbe1 100644
--- a/test/functional/legacy/assert_spec.lua
+++ b/test/functional/legacy/assert_spec.lua
@@ -19,36 +19,10 @@ describe('assert function:', function()
clear()
end)
- describe('assert_beeps', function()
- it('works', function()
- call('assert_beeps', 'normal h')
- expected_empty()
- call('assert_beeps', 'normal 0')
- expected_errors({'command did not beep: normal 0'})
- end)
-
- it('can be used as a method', function()
- local tmpname = source [[
- call assert_equal(0, 'normal h'->assert_beeps())
- call assert_equal(1, 'normal 0'->assert_beeps())
- ]]
- expected_errors({tmpname .. ' line 2: command did not beep: normal 0'})
- end)
- end)
-
-- assert_equal({expected}, {actual}, [, {msg}])
describe('assert_equal', function()
it('should not change v:errors when expected is equal to actual', function()
source([[
- let s = 'foo'
- call assert_equal('foo', s)
- let n = 4
- call assert_equal(4, n)
- let l = [1, 2, 3]
- call assert_equal([1, 2, 3], l)
- call assert_equal(v:_null_list, v:_null_list)
- call assert_equal(v:_null_list, [])
- call assert_equal([], v:_null_list)
fu Func()
endfu
let F1 = function('Func')
@@ -98,30 +72,6 @@ describe('assert function:', function()
eq('Vim(call):E724: unable to correctly dump variable with self-referencing container',
exc_exec('call CheckAssert()'))
end)
-
- it('can specify a message and get a message about what failed', function()
- call('assert_equal', 'foo', 'bar', 'testing')
- expected_errors({"testing: Expected 'foo' but got 'bar'"})
- end)
-
- it('should shorten a long message', function()
- call ('assert_equal', 'XxxxxxxxxxxxxxxxxxxxxxX', 'XyyyyyyyyyyyyyyyyyyyyyyyyyX')
- expected_errors({"Expected 'X\\[x occurs 21 times]X' but got 'X\\[y occurs 25 times]X'"})
- end)
- end)
-
- -- assert_notequal({expected}, {actual}[, {msg}])
- describe('assert_notequal', function()
- it('should not change v:errors when expected differs from actual', function()
- eq(0, call('assert_notequal', 'foo', 4))
- eq(0, call('assert_notequal', {1, 2, 3}, 'foo'))
- expected_empty()
- end)
-
- it('should change v:errors when expected is equal to actual', function()
- eq(1, call('assert_notequal', 'foo', 'foo'))
- expected_errors({"Expected not equal to 'foo'"})
- end)
end)
-- assert_false({actual}, [, {msg}])
@@ -141,14 +91,6 @@ describe('assert function:', function()
call('assert_false', {})
expected_errors({'Expected False but got []'})
end)
-
- it('can be used as a method', function()
- local tmpname = source [[
- call assert_equal(0, v:false->assert_false())
- call assert_equal(1, 123->assert_false())
- ]]
- expected_errors({tmpname .. ' line 2: Expected False but got 123'})
- end)
end)
-- assert_true({actual}, [, {msg}])
@@ -164,14 +106,6 @@ describe('assert function:', function()
eq(1, call('assert_true', 1.5))
expected_errors({'Expected True but got 1.5'})
end)
-
- it('can be used as a method', function()
- local tmpname = source [[
- call assert_equal(0, v:true->assert_true())
- call assert_equal(1, 0->assert_true())
- ]]
- expected_errors({tmpname .. ' line 2: Expected True but got 0'})
- end)
end)
describe('v:errors', function()
@@ -202,91 +136,25 @@ describe('assert function:', function()
end)
it('should have file names and passed messages', function()
- local tmpname_one = source([[
+ source([[
call assert_equal(1, 100, 'equal assertion failed')
call assert_false('true', 'true assertion failed')
call assert_true('false', 'false assertion failed')
]])
- local tmpname_two = source([[
- call assert_true('', 'file two')
- ]])
- expected_errors({
- tmpname_one .. " line 1: equal assertion failed: Expected 1 but got 100",
- tmpname_one .. " line 2: true assertion failed: Expected False but got 'true'",
- tmpname_one .. " line 3: false assertion failed: Expected True but got 'false'",
- tmpname_two .. " line 1: file two: Expected True but got ''",
- })
- end)
-
- it('is reset to a list by assert functions', function()
source([[
- let save_verrors = v:errors
- let v:['errors'] = {'foo': 3}
- call assert_equal('yes', 'no')
- let verrors = v:errors
- let v:errors = save_verrors
- call assert_equal(type([]), type(verrors))
+ call assert_true('', 'file two')
]])
- expected_empty()
- end)
- end)
-
- -- assert_match({pat}, {text}[, {msg}])
- describe('assert_match', function()
- it('should not change v:errors when pat matches text', function()
- call('assert_match', '^f.*b.*r$', 'foobar')
- expected_empty()
- end)
-
- it('should change v:errors when pat does not match text', function()
- call('assert_match', 'bar.*foo', 'foobar')
- expected_errors({"Pattern 'bar.*foo' does not match 'foobar'"})
- end)
-
- it('should set v:errors to msg when given and match fails', function()
- call('assert_match', 'bar.*foo', 'foobar', 'wrong')
- expected_errors({"wrong: Pattern 'bar.*foo' does not match 'foobar'"})
- end)
-
- it('can be used as a method', function()
- local tmpname = source [[
- call assert_equal(1, 'foobar'->assert_match('bar.*foo', 'wrong'))
- ]]
expected_errors({
- tmpname .. " line 1: wrong: Pattern 'bar.*foo' does not match 'foobar'"
+ "nvim_exec(): equal assertion failed: Expected 1 but got 100",
+ "nvim_exec(): true assertion failed: Expected False but got 'true'",
+ "nvim_exec(): false assertion failed: Expected True but got 'false'",
+ "nvim_exec(): file two: Expected True but got ''",
})
end)
end)
- -- assert_notmatch({pat}, {text}[, {msg}])
- describe('assert_notmatch', function()
- it('should not change v:errors when pat does not match text', function()
- call('assert_notmatch', 'foo', 'bar')
- call('assert_notmatch', '^foobar$', 'foobars')
- expected_empty()
- end)
-
- it('should change v:errors when pat matches text', function()
- call('assert_notmatch', 'foo', 'foobar')
- expected_errors({"Pattern 'foo' does match 'foobar'"})
- end)
-
- it('can be used as a method', function()
- local tmpname = source [[
- call assert_equal(1, 'foobar'->assert_notmatch('foo'))
- ]]
- expected_errors({tmpname .. " line 1: Pattern 'foo' does match 'foobar'"})
- end)
- end)
-
-- assert_fails({cmd}, [, {error}])
describe('assert_fails', function()
- it('should change v:errors when error does not match v:errmsg', function()
- eq(1, eval([[assert_fails('xxx', 'E12345')]]))
- command([[call assert_match("Expected 'E12345' but got 'E492:", v:errors[0])]])
- expected_errors({"Expected 'E12345' but got 'E492: Not an editor command: xxx': xxx"})
- end)
-
it('should not change v:errors when cmd errors', function()
eq(0, eval([[assert_fails('NonexistentCmd')]]))
expected_empty()
@@ -296,106 +164,5 @@ describe('assert function:', function()
eq(1, eval([[assert_fails('call empty("")', '')]]))
expected_errors({'command did not fail: call empty("")'})
end)
-
- it('can specify and get a message about what failed', function()
- eq(1, eval([[assert_fails('xxx', 'E9876', 'stupid')]]))
- command([[call assert_match("stupid: Expected 'E9876' but got 'E492:", v:errors[0])]])
- expected_errors({"stupid: Expected 'E9876' but got 'E492: Not an editor command: xxx': stupid"})
- end)
-
- it('can specify and get a message even when cmd succeeds', function()
- eq(1, eval([[assert_fails('echo', '', 'echo command')]]))
- expected_errors({'command did not fail: echo command'})
- end)
-
- it('can be used as a method', function()
- local tmpname = source [[
- call assert_equal(1, 'echo'->assert_fails('', 'echo command'))
- ]]
- expected_errors({
- tmpname .. ' line 1: command did not fail: echo command'
- })
- end)
- end)
-
- -- assert_inrange({lower}, {upper}, {actual}[, {msg}])
- describe('assert_inrange()', function()
- it('should not change v:errors when actual is in range', function()
- call('assert_inrange', 7, 7, 7)
- call('assert_inrange', 5, 7, 5)
- call('assert_inrange', 5, 7, 6)
- call('assert_inrange', 5, 7, 7)
- expected_empty()
- end)
-
- it('should change v:errors when actual is not in range', function()
- call('assert_inrange', 5, 7, 4)
- call('assert_inrange', 5, 7, 8)
- expected_errors({
- "Expected range 5 - 7, but got 4",
- "Expected range 5 - 7, but got 8",
- })
- end)
-
- it('assert_inrange(1, 1) returns E119', function()
- eq('Vim(call):E119: Not enough arguments for function: assert_inrange',
- exc_exec("call assert_inrange(1, 1)"))
- end)
-
- it('can be used as a method', function()
- local tmpname = source [[
- call assert_equal(0, 5->assert_inrange(5, 7))
- call assert_equal(0, 7->assert_inrange(5, 7))
- call assert_equal(1, 8->assert_inrange(5, 7))
- ]]
- expected_errors({tmpname .. ' line 3: Expected range 5 - 7, but got 8'})
- end)
- end)
-
- -- assert_report({msg})
- describe('assert_report()', function()
- it('should add a message to v:errors', function()
- eq(1, call('assert_report', 'something is wrong'))
- command("call assert_match('something is wrong', v:errors[0])")
- command('call remove(v:errors, 0)')
- expected_empty()
- end)
-
- it('can be used as a method', function()
- local tmpname = source [[
- call assert_equal(1, 'also wrong'->assert_report())
- ]]
- expected_errors({tmpname .. ' line 1: also wrong'})
- end)
- end)
-
- -- assert_exception({cmd}, [, {error}])
- describe('assert_exception()', function()
- it('should assert thrown exceptions properly', function()
- source([[
- try
- nocommand
- catch
- call assert_equal(0, assert_exception('E492'))
- endtry
- ]])
- expected_empty()
- end)
-
- it('should work properly when nested', function()
- source([[
- try
- nocommand
- catch
- try
- " illegal argument, get NULL for error
- call assert_equal(1, assert_exception([]))
- catch
- call assert_equal(0, assert_exception('E730'))
- endtry
- endtry
- ]])
- expected_empty()
- end)
end)
end)
diff --git a/test/functional/legacy/autochdir_spec.lua b/test/functional/legacy/autochdir_spec.lua
index 37a94476a0..13cb6cd287 100644
--- a/test/functional/legacy/autochdir_spec.lua
+++ b/test/functional/legacy/autochdir_spec.lua
@@ -1,8 +1,12 @@
local lfs = require('lfs')
local helpers = require('test.functional.helpers')(after_each)
local clear, eq, matches = helpers.clear, helpers.eq, helpers.matches
-local eval, command, call = helpers.eval, helpers.command, helpers.call
-local exec_capture = helpers.exec_capture
+local eval, command, call, meths = helpers.eval, helpers.command, helpers.call, helpers.meths
+local source, exec_capture = helpers.source, helpers.exec_capture
+
+local function expected_empty()
+ eq({}, meths.get_vvar('errors'))
+end
describe('autochdir behavior', function()
local dir = 'Xtest_functional_legacy_autochdir'
@@ -10,19 +14,66 @@ describe('autochdir behavior', function()
before_each(function()
lfs.mkdir(dir)
clear()
+ command('set shellslash')
end)
after_each(function()
helpers.rmdir(dir)
end)
- -- Tests vim/vim/777 without test_autochdir().
+ -- Tests vim/vim#777 without test_autochdir().
it('sets filename', function()
command('set acd')
command('new')
command('w '..dir..'/Xtest')
eq('Xtest', eval("expand('%')"))
- eq(dir, eval([[substitute(getcwd(), '.*[/\\]\(\k*\)', '\1', '')]]))
+ eq(dir, eval([[substitute(getcwd(), '.*/\(\k*\)', '\1', '')]]))
+ end)
+
+ it(':file in win_execute() does not cause wrong directory', function()
+ command('cd '..dir)
+ source([[
+ func Test_set_filename_other_window()
+ let cwd = getcwd()
+ call mkdir('Xa')
+ call mkdir('Xb')
+ call mkdir('Xc')
+ try
+ args Xa/aaa.txt Xb/bbb.txt
+ set acd
+ let winid = win_getid()
+ snext
+ call assert_equal('Xb', substitute(getcwd(), '.*/\([^/]*\)$', '\1', ''))
+ call win_execute(winid, 'file ' .. cwd .. '/Xc/ccc.txt')
+ call assert_equal('Xb', substitute(getcwd(), '.*/\([^/]*\)$', '\1', ''))
+ finally
+ set noacd
+ call chdir(cwd)
+ call delete('Xa', 'rf')
+ call delete('Xb', 'rf')
+ call delete('Xc', 'rf')
+ bwipe! aaa.txt
+ bwipe! bbb.txt
+ bwipe! ccc.txt
+ endtry
+ endfunc
+ ]])
+ call('Test_set_filename_other_window')
+ expected_empty()
+ end)
+
+ it('win_execute() does not change directory', function()
+ local subdir = 'Xfile'
+ command('cd '..dir)
+ command('set acd')
+ call('mkdir', subdir)
+ local winid = eval('win_getid()')
+ command('new '..subdir..'/file')
+ matches(dir..'/'..subdir..'$', eval('getcwd()'))
+ command('cd ..')
+ matches(dir..'$', eval('getcwd()'))
+ call('win_execute', winid, 'echo')
+ matches(dir..'$', eval('getcwd()'))
end)
it(':verbose pwd shows whether autochdir is used', function()
@@ -30,29 +81,36 @@ describe('autochdir behavior', function()
command('cd '..dir)
local cwd = eval('getcwd()')
command('edit global.txt')
- matches('%[global%].*'..dir, exec_capture('verbose pwd'))
+ matches('%[global%].*'..dir..'$', exec_capture('verbose pwd'))
call('mkdir', subdir)
command('split '..subdir..'/local.txt')
command('lcd '..subdir)
- matches('%[window%].*'..dir..'[/\\]'..subdir, exec_capture('verbose pwd'))
- command('set autochdir')
+ matches('%[window%].*'..dir..'/'..subdir..'$', exec_capture('verbose pwd'))
+ command('set acd')
command('wincmd w')
- matches('%[autochdir%].*'..dir, exec_capture('verbose pwd'))
- command('lcd '..cwd)
- matches('%[window%].*'..dir, exec_capture('verbose pwd'))
+ matches('%[autochdir%].*'..dir..'$', exec_capture('verbose pwd'))
command('tcd '..cwd)
- matches('%[tabpage%].*'..dir, exec_capture('verbose pwd'))
+ matches('%[tabpage%].*'..dir..'$', exec_capture('verbose pwd'))
command('cd '..cwd)
- matches('%[global%].*'..dir, exec_capture('verbose pwd'))
+ matches('%[global%].*'..dir..'$', exec_capture('verbose pwd'))
+ command('lcd '..cwd)
+ matches('%[window%].*'..dir..'$', exec_capture('verbose pwd'))
command('edit')
- matches('%[autochdir%].*'..dir, exec_capture('verbose pwd'))
+ matches('%[autochdir%].*'..dir..'$', exec_capture('verbose pwd'))
+ command('enew')
command('wincmd w')
- matches('%[autochdir%].*'..dir..'[/\\]'..subdir, exec_capture('verbose pwd'))
- command('set noautochdir')
- matches('%[autochdir%].*'..dir..'[/\\]'..subdir, exec_capture('verbose pwd'))
+ matches('%[autochdir%].*'..dir..'/'..subdir..'$', exec_capture('verbose pwd'))
command('wincmd w')
- matches('%[global%].*'..dir, exec_capture('verbose pwd'))
+ matches('%[window%].*'..dir..'$', exec_capture('verbose pwd'))
+ command('wincmd w')
+ matches('%[autochdir%].*'..dir..'/'..subdir..'$', exec_capture('verbose pwd'))
+ command('set noacd')
+ matches('%[autochdir%].*'..dir..'/'..subdir..'$', exec_capture('verbose pwd'))
+ command('wincmd w')
+ matches('%[window%].*'..dir..'$', exec_capture('verbose pwd'))
+ command('cd '..cwd)
+ matches('%[global%].*'..dir..'$', exec_capture('verbose pwd'))
command('wincmd w')
- matches('%[window%].*'..dir..'[/\\]'..subdir, exec_capture('verbose pwd'))
+ matches('%[window%].*'..dir..'/'..subdir..'$', exec_capture('verbose pwd'))
end)
end)
diff --git a/test/functional/legacy/cdo_spec.lua b/test/functional/legacy/cdo_spec.lua
deleted file mode 100644
index 8b3216cbfd..0000000000
--- a/test/functional/legacy/cdo_spec.lua
+++ /dev/null
@@ -1,228 +0,0 @@
--- Tests for the :cdo, :cfdo, :ldo and :lfdo commands
-
-local helpers = require('test.functional.helpers')(after_each)
-local nvim, clear = helpers.meths, helpers.clear
-local call, feed = helpers.call, helpers.feed
-local source, eq = helpers.source, helpers.eq
-
-local function expected_empty()
- eq({}, nvim.get_vvar('errors'))
-end
-
-describe('cdo', function()
- before_each(function()
- clear()
-
- call('writefile', {'Line1', 'Line2', 'Line3'}, 'Xtestfile1')
- call('writefile', {'Line1', 'Line2', 'Line3'}, 'Xtestfile2')
- call('writefile', {'Line1', 'Line2', 'Line3'}, 'Xtestfile3')
-
- source([=[
- " Returns the current line in '<filename> <linenum>L <column>C' format
- function GetRuler()
- return expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C'
- endfunction
-
- " Tests for the :cdo and :ldo commands
- function XdoTests(cchar)
- enew
-
- " Shortcuts for calling the cdo and ldo commands
- let Xdo = a:cchar . 'do'
- let Xgetexpr = a:cchar . 'getexpr'
- let Xprev = a:cchar. 'prev'
- let XdoCmd = Xdo . ' call add(l, GetRuler())'
-
- " Try with an empty list
- let l = []
- exe XdoCmd
- call assert_equal([], l)
-
- " Populate the list and then try
- exe Xgetexpr . " ['non-error 1', 'Xtestfile1:1:3:Line1', 'non-error 2', 'Xtestfile2:2:2:Line2', 'non-error 3', 'Xtestfile3:3:1:Line3']"
-
- let l = []
- exe XdoCmd
- call assert_equal(['Xtestfile1 1L 3C', 'Xtestfile2 2L 2C', 'Xtestfile3 3L 1C'], l)
-
- " Run command only on selected error lines
- let l = []
- enew
- exe "2,3" . XdoCmd
- call assert_equal(['Xtestfile2 2L 2C', 'Xtestfile3 3L 1C'], l)
-
- " Boundary condition tests
- let l = []
- enew
- exe "1,1" . XdoCmd
- call assert_equal(['Xtestfile1 1L 3C'], l)
-
- let l = []
- enew
- exe "3" . XdoCmd
- call assert_equal(['Xtestfile3 3L 1C'], l)
-
- " Range test commands
- let l = []
- enew
- exe "%" . XdoCmd
- call assert_equal(['Xtestfile1 1L 3C', 'Xtestfile2 2L 2C', 'Xtestfile3 3L 1C'], l)
-
- let l = []
- enew
- exe "1,$" . XdoCmd
- call assert_equal(['Xtestfile1 1L 3C', 'Xtestfile2 2L 2C', 'Xtestfile3 3L 1C'], l)
-
- let l = []
- enew
- exe Xprev
- exe "." . XdoCmd
- call assert_equal(['Xtestfile2 2L 2C'], l)
-
- let l = []
- enew
- exe "+" . XdoCmd
- call assert_equal(['Xtestfile3 3L 1C'], l)
-
- " Invalid error lines test
- let l = []
- enew
- exe "silent! 27" . XdoCmd
- exe "silent! 4,5" . XdoCmd
- call assert_equal([], l)
-
- " Run commands from an unsaved buffer when 'hidden' is unset
- set nohidden
- let v:errmsg=''
- let l = []
- enew
- setlocal modified
- exe "silent! 2,2" . XdoCmd
- if v:errmsg !~# 'No write since last change'
- call add(v:errors, 'Unsaved file change test failed')
- endif
-
- " If the executed command fails, then the operation should be aborted
- enew!
- let subst_count = 0
- exe "silent!" . Xdo . " s/Line/xLine/ | let subst_count += 1"
- if subst_count != 1 || getline('.') != 'xLine1'
- call add(v:errors, 'Abort command on error test failed')
- endif
- set hidden
-
- let l = []
- exe "2,2" . Xdo . "! call add(l, GetRuler())"
- call assert_equal(['Xtestfile2 2L 2C'], l)
-
- " List with no valid error entries
- let l = []
- edit! +2 Xtestfile1
- exe Xgetexpr . " ['non-error 1', 'non-error 2', 'non-error 3']"
- exe XdoCmd
- call assert_equal([], l)
- exe "silent! 2" . XdoCmd
- call assert_equal([], l)
- let v:errmsg=''
- exe "%" . XdoCmd
- exe "1,$" . XdoCmd
- exe "." . XdoCmd
- call assert_equal('', v:errmsg)
-
- " List with only one valid entry
- let l = []
- exe Xgetexpr . " ['Xtestfile3:3:1:Line3']"
- exe XdoCmd
- call assert_equal(['Xtestfile3 3L 1C'], l)
-
- endfunction
-
- " Tests for the :cfdo and :lfdo commands
- function XfdoTests(cchar)
- enew
-
- " Shortcuts for calling the cfdo and lfdo commands
- let Xfdo = a:cchar . 'fdo'
- let Xgetexpr = a:cchar . 'getexpr'
- let XfdoCmd = Xfdo . ' call add(l, GetRuler())'
- let Xpfile = a:cchar. 'pfile'
-
- " Clear the quickfix/location list
- exe Xgetexpr . " []"
-
- " Try with an empty list
- let l = []
- exe XfdoCmd
- call assert_equal([], l)
-
- " Populate the list and then try
- exe Xgetexpr . " ['non-error 1', 'Xtestfile1:1:3:Line1', 'Xtestfile1:2:1:Line2', 'non-error 2', 'Xtestfile2:2:2:Line2', 'non-error 3', 'Xtestfile3:2:3:Line2', 'Xtestfile3:3:1:Line3']"
-
- let l = []
- exe XfdoCmd
- call assert_equal(['Xtestfile1 1L 3C', 'Xtestfile2 2L 2C', 'Xtestfile3 2L 3C'], l)
-
- " Run command only on selected error lines
- let l = []
- exe "2,3" . XfdoCmd
- call assert_equal(['Xtestfile2 2L 2C', 'Xtestfile3 2L 3C'], l)
-
- " Boundary condition tests
- let l = []
- exe "3" . XfdoCmd
- call assert_equal(['Xtestfile3 2L 3C'], l)
-
- " Range test commands
- let l = []
- exe "%" . XfdoCmd
- call assert_equal(['Xtestfile1 1L 3C', 'Xtestfile2 2L 2C', 'Xtestfile3 2L 3C'], l)
-
- let l = []
- exe "1,$" . XfdoCmd
- call assert_equal(['Xtestfile1 1L 3C', 'Xtestfile2 2L 2C', 'Xtestfile3 2L 3C'], l)
-
- let l = []
- exe Xpfile
- exe "." . XfdoCmd
- call assert_equal(['Xtestfile2 2L 2C'], l)
-
- " List with only one valid entry
- let l = []
- exe Xgetexpr . " ['Xtestfile2:2:5:Line2']"
- exe XfdoCmd
- call assert_equal(['Xtestfile2 2L 5C'], l)
-
- endfunction
- ]=])
- end)
-
- after_each(function()
- os.remove('Xtestfile1')
- os.remove('Xtestfile2')
- os.remove('Xtestfile3')
- end)
-
- it('works for :cdo', function()
- -- call('XdoTests', 'c')
- feed(":call XdoTests('c')<CR><C-l>")
- expected_empty()
- end)
-
- it('works for :cfdo', function()
- -- call('XfdoTests', 'c')
- feed(":call XfdoTests('c')<CR><C-l>")
- expected_empty()
- end)
-
- it('works for :ldo', function()
- -- call('XdoTests', 'l')
- feed(":call XdoTests('l')<CR><C-l>")
- expected_empty()
- end)
-
- it('works for :lfdo', function()
- -- call('XfdoTests', 'l')
- feed(":call XfdoTests('l')<CR><C-l>")
- expected_empty()
- end)
-end)
diff --git a/test/functional/legacy/cmdline_spec.lua b/test/functional/legacy/cmdline_spec.lua
index 9ebe9aeb91..d8d849271b 100644
--- a/test/functional/legacy/cmdline_spec.lua
+++ b/test/functional/legacy/cmdline_spec.lua
@@ -11,6 +11,15 @@ describe('cmdline', function()
it('is cleared when switching tabs', function()
local screen = Screen.new(30, 10)
screen:attach()
+ screen:set_default_attr_ids {
+ [1] = {underline = true, background = Screen.colors.LightGrey};
+ [2] = {bold = true};
+ [3] = {reverse = true};
+ [4] = {bold = true, foreground = Screen.colors.Blue1};
+ }
+ -- TODO(bfredl): redraw with tabs is severly broken. fix it
+ feed_command [[ set display-=msgsep ]]
+
feed_command([[call setline(1, range(30))]])
screen:expect([[
^0 |
@@ -24,18 +33,61 @@ describe('cmdline', function()
8 |
:call setline(1, range(30)) |
]])
- feed([[:tabnew<cr><C-w>-<C-w>-gtgt]])
- screen:expect([[
- + [No Name] [No Name] X|
+
+ feed [[:tabnew<cr>]]
+ screen:expect{grid=[[
+ {1: + [No Name] }{2: [No Name] }{3: }{1:X}|
+ ^ |
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ :tabnew |
+ ]]}
+
+ feed [[<C-w>-<C-w>-]]
+ screen:expect{grid=[[
+ {1: + [No Name] }{2: [No Name] }{3: }{1:X}|
^ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ |
+ |
+ :tabnew |
+ ]]}
+
+ feed [[gt]]
+ screen:expect{grid=[[
+ {2: + [No Name] }{1: [No Name] }{3: }{1:X}|
+ ^0 |
+ 1 |
+ 2 |
+ 3 |
+ 4 |
+ 5 |
6 |
7 |
|
+ ]]}
+
+ feed [[gt]]
+ screen:expect([[
+ {1: + [No Name] }{2: [No Name] }{3: }{1:X}|
+ ^ |
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ |
+ |
+ |
]])
end)
diff --git a/test/functional/legacy/cpoptions_spec.lua b/test/functional/legacy/cpoptions_spec.lua
new file mode 100644
index 0000000000..d2f382ec12
--- /dev/null
+++ b/test/functional/legacy/cpoptions_spec.lua
@@ -0,0 +1,34 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local clear = helpers.clear
+local command = helpers.command
+local feed = helpers.feed
+
+before_each(clear)
+
+describe('cpoptions', function()
+ it('$', function()
+ local screen = Screen.new(30, 6)
+ screen:attach()
+ command('set cpo+=$')
+ command([[call setline(1, 'one two three')]])
+ feed('c2w')
+ screen:expect([[
+ ^one tw$ three |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ -- INSERT -- |
+ ]])
+ feed('vim<Esc>')
+ screen:expect([[
+ vi^m three |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ |
+ ]])
+ end)
+end)
diff --git a/test/functional/legacy/delete_spec.lua b/test/functional/legacy/delete_spec.lua
index 141d9583e6..4ba4c8d356 100644
--- a/test/functional/legacy/delete_spec.lua
+++ b/test/functional/legacy/delete_spec.lua
@@ -25,25 +25,6 @@ describe('Test for delete()', function()
eq(0, eval("isdirectory('Xdir1')"))
eq(-1, eval("delete('Xdir1', 'd')"))
end)
- it('recursive delete', function()
- command("call mkdir('Xdir1')")
- command("call mkdir('Xdir1/subdir')")
- command("call mkdir('Xdir1/empty')")
- command('split Xdir1/Xfile')
- command("call setline(1, ['a', 'b'])")
- command('w')
- command('w Xdir1/subdir/Xfile')
- command('close')
-
- eq(1, eval("isdirectory('Xdir1')"))
- eq(eval("['a', 'b']"), eval("readfile('Xdir1/Xfile')"))
- eq(1, eval("isdirectory('Xdir1/subdir')"))
- eq(eval("['a', 'b']"), eval("readfile('Xdir1/subdir/Xfile')"))
- eq(1, eval("'Xdir1/empty'->isdirectory()"))
- eq(0, eval("delete('Xdir1', 'rf')"))
- eq(0, eval("isdirectory('Xdir1')"))
- eq(-1, eval("delete('Xdir1', 'd')"))
- end)
it('symlink delete', function()
source([[
@@ -80,45 +61,8 @@ describe('Test for delete()', function()
eq(0, eval("delete('Xdir1', 'd')"))
end)
- it('symlink recursive delete', function()
- source([[
- call mkdir('Xdir3')
- call mkdir('Xdir3/subdir')
- call mkdir('Xdir4')
- split Xdir3/Xfile
- call setline(1, ['a', 'b'])
- w
- w Xdir3/subdir/Xfile
- w Xdir4/Xfile
- close
- if has('win32')
- silent !mklink /j Xdir3\Xlink Xdir4
- else
- silent !ln -s ../Xdir4 Xdir3/Xlink
- endif
- ]])
-
- eq(1, eval("isdirectory('Xdir3')"))
- eq(eval("['a', 'b']"), eval("readfile('Xdir3/Xfile')"))
- eq(1, eval("isdirectory('Xdir3/subdir')"))
- eq(eval("['a', 'b']"), eval("readfile('Xdir3/subdir/Xfile')"))
- eq(1, eval("isdirectory('Xdir4')"))
- eq(1, eval("isdirectory('Xdir3/Xlink')"))
- eq(eval("['a', 'b']"), eval("readfile('Xdir4/Xfile')"))
-
- eq(0, eval("delete('Xdir3', 'rf')"))
- eq(0, eval("isdirectory('Xdir3')"))
- eq(-1, eval("delete('Xdir3', 'd')"))
- -- symlink is deleted, not the directory it points to
- eq(1, eval("isdirectory('Xdir4')"))
- eq(eval("['a', 'b']"), eval("readfile('Xdir4/Xfile')"))
- eq(0, eval("delete('Xdir4/Xfile')"))
- eq(0, eval("delete('Xdir4', 'd')"))
- end)
-
it('gives correct emsgs', function()
eq('Vim(call):E474: Invalid argument', exc_exec("call delete('')"))
- eq('Vim(call):E15: Invalid expression: 0',
- exc_exec("call delete('foo', 0)"))
+ eq('Vim(call):E15: Invalid expression: 0', exc_exec("call delete('foo', 0)"))
end)
end)
diff --git a/test/functional/legacy/display_spec.lua b/test/functional/legacy/display_spec.lua
index 3fbbe96947..f0ffaf2c48 100644
--- a/test/functional/legacy/display_spec.lua
+++ b/test/functional/legacy/display_spec.lua
@@ -2,23 +2,21 @@ local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local clear = helpers.clear
-local poke_eventloop = helpers.poke_eventloop
+local exec = helpers.exec
local feed = helpers.feed
-local feed_command = helpers.feed_command
+local command = helpers.command
describe('display', function()
- local screen
+ before_each(clear)
- it('scroll when modified at topline', function()
- clear()
- screen = Screen.new(20, 4)
+ it('scroll when modified at topline vim-patch:8.2.1488', function()
+ local screen = Screen.new(20, 4)
screen:attach()
screen:set_default_attr_ids({
[1] = {bold = true},
})
- feed_command([[call setline(1, repeat('a', 21))]])
- poke_eventloop()
+ command([[call setline(1, repeat('a', 21))]])
feed('O')
screen:expect([[
^ |
@@ -27,5 +25,79 @@ describe('display', function()
{1:-- INSERT --} |
]])
end)
-end)
+ it('scrolling when modified at topline in Visual mode vim-patch:8.2.4626', function()
+ local screen = Screen.new(60, 8)
+ screen:attach()
+ screen:set_default_attr_ids({
+ [1] = {bold = true}, -- ModeMsg
+ [2] = {background = Screen.colors.LightGrey}, -- Visual
+ [3] = {background = Screen.colors.Grey, foreground = Screen.colors.DarkBlue}, -- SignColumn
+ })
+
+ exec([[
+ set scrolloff=0
+ call setline(1, repeat(['foo'], 10))
+ call sign_define('foo', { 'text': '>' })
+ call sign_place(1, 'bar', 'foo', bufnr(), { 'lnum': 2 })
+ call sign_place(2, 'bar', 'foo', bufnr(), { 'lnum': 1 })
+ autocmd CursorMoved * if getcurpos()[1] == 2 | call sign_unplace('bar', { 'id': 1 }) | endif
+ ]])
+ feed('VG7kk')
+ screen:expect([[
+ {3: }^f{2:oo} |
+ {3: }foo |
+ {3: }foo |
+ {3: }foo |
+ {3: }foo |
+ {3: }foo |
+ {3: }foo |
+ {1:-- VISUAL LINE --} |
+ ]])
+ end)
+
+ it('@@@ in the last line shows correctly in a narrow window vim-patch:8.2.4718', function()
+ local screen = Screen.new(60, 10)
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ [2] = {bold = true, reverse = true}, -- StatusLine
+ [3] = {reverse = true}, -- StatusLineNC
+ })
+ screen:attach()
+ exec([[
+ call setline(1, ['aaa', 'b'->repeat(100)])
+ set display=truncate
+ vsplit
+ 100wincmd <
+ ]])
+ screen:expect([[
+ ^a│aaa |
+ a│bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
+ a│bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb |
+ b│{1:~ }|
+ b│{1:~ }|
+ b│{1:~ }|
+ b│{1:~ }|
+ {1:@}│{1:~ }|
+ {2:< }{3:[No Name] [+] }|
+ |
+ ]])
+ command('set display=lastline')
+ screen:expect_unchanged()
+ command('100wincmd >')
+ screen:expect([[
+ ^aaa │a|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│a|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb │a|
+ {1:~ }│b|
+ {1:~ }│b|
+ {1:~ }│b|
+ {1:~ }│b|
+ {1:~ }│{1:@}|
+ {2:[No Name] [+] }{3:<}|
+ |
+ ]])
+ command('set display=truncate')
+ screen:expect_unchanged()
+ end)
+end)
diff --git a/test/functional/legacy/edit_spec.lua b/test/functional/legacy/edit_spec.lua
index 91d602924c..7fc5f11a79 100644
--- a/test/functional/legacy/edit_spec.lua
+++ b/test/functional/legacy/edit_spec.lua
@@ -1,25 +1,26 @@
--- Test for edit functions
--- See also: src/nvim/testdir/test_edit.vim
-
local helpers = require('test.functional.helpers')(after_each)
-local source = helpers.source
-local eq, eval = helpers.eq, helpers.eval
-local funcs = helpers.funcs
local clear = helpers.clear
+local command = helpers.command
+local expect = helpers.expect
+local feed = helpers.feed
+local sleep = helpers.sleep
-describe('edit', function()
- before_each(clear)
-
- it('reset insertmode from i_ctrl-r_=', function()
- source([=[
- call setline(1, ['abc'])
- call cursor(1, 4)
- call feedkeys(":set im\<cr>ZZZ\<c-r>=setbufvar(1,'&im', 0)\<cr>",'tnix')
- ]=])
- eq({'abZZZc'}, funcs.getline(1,'$'))
- eq({0, 1, 1, 0}, funcs.getpos('.'))
- eq(0, eval('&im'))
- end)
+before_each(clear)
+-- oldtest: Test_autoindent_remove_indent()
+it('autoindent removes indent when Insert mode is stopped', function()
+ command('set autoindent')
+ -- leaving insert mode in a new line with indent added by autoindent, should
+ -- remove the indent.
+ feed('i<Tab>foo<CR><Esc>')
+ -- Need to delay for sometime, otherwise the code in getchar.c will not be
+ -- exercised.
+ sleep(50)
+ -- when a line is wrapped and the cursor is at the start of the second line,
+ -- leaving insert mode, should move the cursor back to the first line.
+ feed('o' .. ('x'):rep(20) .. '<Esc>')
+ -- Need to delay for sometime, otherwise the code in getchar.c will not be
+ -- exercised.
+ sleep(50)
+ expect('\tfoo\n\n' .. ('x'):rep(20))
end)
-
diff --git a/test/functional/legacy/eval_spec.lua b/test/functional/legacy/eval_spec.lua
index b5de5cd232..05d853622e 100644
--- a/test/functional/legacy/eval_spec.lua
+++ b/test/functional/legacy/eval_spec.lua
@@ -666,7 +666,7 @@ describe('eval', function()
source([[
" Vim script used in test_eval.in. Needed for script-local function.
- func! s:Testje()
+ func s:Testje()
return "foo"
endfunc
diff --git a/test/functional/legacy/ex_mode_spec.lua b/test/functional/legacy/ex_mode_spec.lua
new file mode 100644
index 0000000000..98f113bbd0
--- /dev/null
+++ b/test/functional/legacy/ex_mode_spec.lua
@@ -0,0 +1,125 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local clear = helpers.clear
+local command = helpers.command
+local eq = helpers.eq
+local eval = helpers.eval
+local feed = helpers.feed
+local meths = helpers.meths
+
+before_each(clear)
+
+describe('Ex mode', function()
+ it('supports command line editing', function()
+ local function test_ex_edit(expected, cmd)
+ feed('gQ' .. cmd .. '<C-b>"<CR>')
+ local ret = eval('@:[1:]') -- Remove leading quote.
+ feed('visual<CR>')
+ eq(meths.replace_termcodes(expected, true, true, true), ret)
+ end
+ command('set sw=2')
+ test_ex_edit('bar', 'foo bar<C-u>bar')
+ test_ex_edit('1<C-u>2', '1<C-v><C-u>2')
+ test_ex_edit('213', '1<C-b>2<C-e>3')
+ test_ex_edit('2013', '01<Home>2<End>3')
+ test_ex_edit('0213', '01<Left>2<Right>3')
+ test_ex_edit('0342', '012<Left><Left><Insert>3<Insert>4')
+ test_ex_edit('foo ', 'foo bar<C-w>')
+ test_ex_edit('foo', 'fooba<Del><Del>')
+ test_ex_edit('foobar', 'foo<Tab>bar')
+ test_ex_edit('abbreviate', 'abbrev<Tab>')
+ test_ex_edit('1<C-t><C-t>', '1<C-t><C-t>')
+ test_ex_edit('1<C-t><C-t>', '1<C-t><C-t><C-d>')
+ test_ex_edit(' foo', ' foo<C-d>')
+ test_ex_edit(' foo0', ' foo0<C-d>')
+ test_ex_edit(' foo^', ' foo^<C-d>')
+ test_ex_edit('foo', '<BS><C-H><Del><kDel>foo')
+ -- default wildchar <Tab> interferes with this test
+ command('set wildchar=<c-e>')
+ test_ex_edit('a\tb', 'a\t\t<C-H>b')
+ test_ex_edit('\tm<C-T>n', '\tm<C-T>n')
+ command('set wildchar&')
+ end)
+
+ it('substitute confirmation prompt', function()
+ command('set noincsearch nohlsearch inccommand=')
+ local screen = Screen.new(60, 6)
+ screen:set_default_attr_ids({
+ [0] = {bold = true, reverse = true}, -- MsgSeparator
+ [1] = {foreground = Screen.colors.Brown}, -- LineNr
+ [2] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ })
+ screen:attach()
+ command([[call setline(1, ['foo foo', 'foo foo', 'foo foo'])]])
+ command([[set number]])
+ feed('gQ')
+ screen:expect([[
+ {1: 1 }foo foo |
+ {1: 2 }foo foo |
+ {1: 3 }foo foo |
+ {0: }|
+ Entering Ex mode. Type "visual" to go to Normal mode. |
+ :^ |
+ ]])
+
+ feed('%s/foo/bar/gc<CR>')
+ screen:expect([[
+ {1: 1 }foo foo |
+ {0: }|
+ Entering Ex mode. Type "visual" to go to Normal mode. |
+ :%s/foo/bar/gc |
+ {1: 1 }foo foo |
+ ^^^^ |
+ ]])
+ feed('N<CR>')
+ screen:expect([[
+ Entering Ex mode. Type "visual" to go to Normal mode. |
+ :%s/foo/bar/gc |
+ {1: 1 }foo foo |
+ ^^^N |
+ {1: 1 }foo foo |
+ ^^^^ |
+ ]])
+ feed('n<CR>')
+ screen:expect([[
+ {1: 1 }foo foo |
+ ^^^N |
+ {1: 1 }foo foo |
+ ^^^n |
+ {1: 1 }foo foo |
+ ^^^^ |
+ ]])
+ feed('y<CR>')
+
+ feed('q<CR>')
+ screen:expect([[
+ {1: 1 }foo foo |
+ ^^^y |
+ {1: 2 }foo foo |
+ ^^^q |
+ {1: 2 }foo foo |
+ :^ |
+ ]])
+
+ -- Pressing enter in ex mode should print the current line
+ feed('<CR>')
+ screen:expect([[
+ ^^^y |
+ {1: 2 }foo foo |
+ ^^^q |
+ {1: 2 }foo foo |
+ {1: 3 }foo foo |
+ :^ |
+ ]])
+
+ feed(':vi<CR>')
+ screen:expect([[
+ {1: 1 }foo bar |
+ {1: 2 }foo foo |
+ {1: 3 }^foo foo |
+ {2:~ }|
+ {2:~ }|
+ |
+ ]])
+ end)
+end)
diff --git a/test/functional/legacy/excmd_spec.lua b/test/functional/legacy/excmd_spec.lua
new file mode 100644
index 0000000000..6b3b265579
--- /dev/null
+++ b/test/functional/legacy/excmd_spec.lua
@@ -0,0 +1,188 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local clear = helpers.clear
+local command = helpers.command
+local exec_lua = helpers.exec_lua
+local feed = helpers.feed
+local meths = helpers.meths
+local poke_eventloop = helpers.poke_eventloop
+local read_file = helpers.read_file
+local source = helpers.source
+local eq = helpers.eq
+local write_file = helpers.write_file
+
+local function sizeoflong()
+ if not exec_lua('return pcall(require, "ffi")') then
+ pending('missing LuaJIT FFI')
+ end
+ return exec_lua('return require("ffi").sizeof(require("ffi").typeof("long"))')
+end
+
+describe('Ex command', function()
+ before_each(clear)
+ after_each(function() eq({}, meths.get_vvar('errors')) end)
+
+ it('checks for address line overflow', function()
+ if sizeoflong() < 8 then
+ pending('Skipped: only works with 64 bit long ints')
+ end
+
+ source [[
+ new
+ call setline(1, 'text')
+ call assert_fails('|.44444444444444444444444', 'E1247:')
+ call assert_fails('|.9223372036854775806', 'E1247:')
+ bwipe!
+ ]]
+ end)
+end)
+
+it(':confirm command dialog', function()
+ local screen
+
+ local function start_new()
+ clear()
+ screen = Screen.new(60, 20)
+ screen:attach()
+ end
+
+ write_file('foo', 'foo1\n')
+ write_file('bar', 'bar1\n')
+
+ -- Test for saving all the modified buffers
+ start_new()
+ command("set nomore")
+ command("new foo")
+ command("call setline(1, 'foo2')")
+ command("new bar")
+ command("call setline(1, 'bar2')")
+ command("wincmd b")
+ feed(':confirm qall\n')
+ screen:expect([[
+ bar2 |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ bar [+] |
+ foo2 |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ foo [+] |
+ |
+ ~ |
+ ~ |
+ |
+ :confirm qall |
+ Save changes to "bar"? |
+ [Y]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: ^ |
+ ]])
+ feed('A')
+ poke_eventloop()
+
+ eq('foo2\n', read_file('foo'))
+ eq('bar2\n', read_file('bar'))
+
+ -- Test for discarding all the changes to modified buffers
+ start_new()
+ command("set nomore")
+ command("new foo")
+ command("call setline(1, 'foo3')")
+ command("new bar")
+ command("call setline(1, 'bar3')")
+ command("wincmd b")
+ feed(':confirm qall\n')
+ screen:expect([[
+ bar3 |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ bar [+] |
+ foo3 |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ foo [+] |
+ |
+ ~ |
+ ~ |
+ |
+ :confirm qall |
+ Save changes to "bar"? |
+ [Y]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: ^ |
+ ]])
+ feed('D')
+ poke_eventloop()
+
+ eq('foo2\n', read_file('foo'))
+ eq('bar2\n', read_file('bar'))
+
+ -- Test for saving and discarding changes to some buffers
+ start_new()
+ command("set nomore")
+ command("new foo")
+ command("call setline(1, 'foo4')")
+ command("new bar")
+ command("call setline(1, 'bar4')")
+ command("wincmd b")
+ feed(':confirm qall\n')
+ screen:expect([[
+ bar4 |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ bar [+] |
+ foo4 |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ foo [+] |
+ |
+ ~ |
+ ~ |
+ |
+ :confirm qall |
+ Save changes to "bar"? |
+ [Y]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: ^ |
+ ]])
+ feed('N')
+ screen:expect([[
+ bar4 |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ bar [+] |
+ foo4 |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ foo [+] |
+ |
+ |
+ :confirm qall |
+ Save changes to "bar"? |
+ |
+ Save changes to "foo"? |
+ [Y]es, (N)o, (C)ancel: ^ |
+ ]])
+ feed('Y')
+ poke_eventloop()
+
+ eq('foo4\n', read_file('foo'))
+ eq('bar2\n', read_file('bar'))
+
+ os.remove('foo')
+ os.remove('bar')
+end)
diff --git a/test/functional/legacy/expand_spec.lua b/test/functional/legacy/expand_spec.lua
deleted file mode 100644
index cd3713eabe..0000000000
--- a/test/functional/legacy/expand_spec.lua
+++ /dev/null
@@ -1,129 +0,0 @@
--- Test for expanding file names
-
-local helpers = require('test.functional.helpers')(after_each)
-local eq = helpers.eq
-local call = helpers.call
-local nvim = helpers.meths
-local clear = helpers.clear
-local source = helpers.source
-
-local function expected_empty()
- eq({}, nvim.get_vvar('errors'))
-end
-
-describe('expand file name', function()
- after_each(function()
- helpers.rmdir('Xdir1')
- helpers.rmdir('Xdir2')
- helpers.rmdir('Xdir3')
- helpers.rmdir('Xdir4')
- end)
-
- before_each(function()
- clear()
-
- source([[
- func Test_with_directories()
- call mkdir('Xdir1')
- call mkdir('Xdir2')
- call mkdir('Xdir3')
- cd Xdir3
- call mkdir('Xdir4')
- cd ..
-
- split Xdir1/file
- call setline(1, ['a', 'b'])
- w
- w Xdir3/Xdir4/file
- close
-
- next Xdir?/*/file
- call assert_equal('Xdir3/Xdir4/file', expand('%'))
- if has('unix')
- next! Xdir?/*/nofile
- call assert_equal('Xdir?/*/nofile', expand('%'))
- endif
- " Edit another file, on MS-Windows the swap file would be in use and can't
- " be deleted
- edit foo
-
- call assert_equal(0, delete('Xdir1', 'rf'))
- call assert_equal(0, delete('Xdir2', 'rf'))
- call assert_equal(0, delete('Xdir3', 'rf'))
- endfunc
-
- func Test_with_tilde()
- let dir = getcwd()
- call mkdir('Xdir ~ dir')
- call assert_true(isdirectory('Xdir ~ dir'))
- cd Xdir\ ~\ dir
- call assert_true(getcwd() =~ 'Xdir \~ dir')
- exe 'cd ' . fnameescape(dir)
- call delete('Xdir ~ dir', 'd')
- call assert_false(isdirectory('Xdir ~ dir'))
- endfunc
-
- func Test_expand_tilde_filename()
- split ~
- call assert_equal('~', expand('%'))
- call assert_notequal(expand('%:p'), expand('~/'))
- call assert_match('\~', expand('%:p'))
- bwipe!
- endfunc
-
- func Test_expandcmd()
- let $FOO = 'Test'
- call assert_equal('e x/Test/y', expandcmd('e x/$FOO/y'))
- unlet $FOO
-
- new
- edit Xfile1
- call assert_equal('e Xfile1', expandcmd('e %'))
- edit Xfile2
- edit Xfile1
- call assert_equal('e Xfile2', 'e #'->expandcmd())
- edit Xfile2
- edit Xfile3
- edit Xfile4
- let bnum = bufnr('Xfile2')
- call assert_equal('e Xfile2', expandcmd('e #' . bnum))
- call setline('.', 'Vim!@#')
- call assert_equal('e Vim', expandcmd('e <cword>'))
- call assert_equal('e Vim!@#', expandcmd('e <cWORD>'))
- enew!
- edit Xfile.java
- call assert_equal('e Xfile.py', expandcmd('e %:r.py'))
- call assert_equal('make abc.java', expandcmd('make abc.%:e'))
- call assert_equal('make Xabc.java', expandcmd('make %:s?file?abc?'))
- edit a1a2a3.rb
- call assert_equal('make b1b2b3.rb a1a2a3 Xfile.o', expandcmd('make %:gs?a?b? %< #<.o'))
-
- call assert_fails('call expandcmd("make <afile>")', 'E495:')
- call assert_fails('call expandcmd("make <afile>")', 'E495:')
- enew
- call assert_fails('call expandcmd("make %")', 'E499:')
- close
- endfunc
- ]])
- end)
-
- it('works with directories', function()
- call('Test_with_directories')
- expected_empty()
- end)
-
- it('works with tilde', function()
- call('Test_with_tilde')
- expected_empty()
- end)
-
- it('does not expand tilde if it is a filename', function()
- call('Test_expand_tilde_filename')
- expected_empty()
- end)
-
- it('works with expandcmd()', function()
- call('Test_expandcmd')
- expected_empty()
- end)
-end)
diff --git a/test/functional/legacy/file_perm_spec.lua b/test/functional/legacy/file_perm_spec.lua
deleted file mode 100644
index ccdbfe0534..0000000000
--- a/test/functional/legacy/file_perm_spec.lua
+++ /dev/null
@@ -1,43 +0,0 @@
--- Test getting and setting file permissions.
-require('os')
-
-local helpers = require('test.functional.helpers')(after_each)
-local clear, call, eq = helpers.clear, helpers.call, helpers.eq
-local neq, exc_exec, eval = helpers.neq, helpers.exc_exec, helpers.eval
-
-describe('Test getting and setting file permissions', function()
- local tempfile = helpers.tmpname()
-
- before_each(function()
- os.remove(tempfile)
- clear()
- end)
-
- it('file permissions', function()
- -- eval() is used to test VimL method syntax for setfperm() and getfperm()
- eq('', call('getfperm', tempfile))
- eq(0, eval("'" .. tempfile .. "'->setfperm('r--------')"))
-
- call('writefile', {'one'}, tempfile)
- eq(9, eval("len('" .. tempfile .. "'->getfperm())"))
-
- eq(1, call('setfperm', tempfile, 'rwx------'))
- if helpers.is_os('win') then
- eq('rw-rw-rw-', call('getfperm', tempfile))
- else
- eq('rwx------', call('getfperm', tempfile))
- end
-
- eq(1, call('setfperm', tempfile, 'r--r--r--'))
- eq('r--r--r--', call('getfperm', tempfile))
-
- local err = exc_exec(('call setfperm("%s", "---")'):format(tempfile))
- neq(err:find('E475:'), nil)
-
- eq(1, call('setfperm', tempfile, 'rwx------'))
- end)
-
- after_each(function()
- os.remove(tempfile)
- end)
-end)
diff --git a/test/functional/legacy/filechanged_spec.lua b/test/functional/legacy/filechanged_spec.lua
new file mode 100644
index 0000000000..ecb861098c
--- /dev/null
+++ b/test/functional/legacy/filechanged_spec.lua
@@ -0,0 +1,131 @@
+local helpers = require('test.functional.helpers')(after_each)
+local clear, source = helpers.clear, helpers.source
+local call, eq, meths = helpers.call, helpers.eq, helpers.meths
+
+local function expected_empty()
+ eq({}, meths.get_vvar('errors'))
+end
+
+describe('file changed dialog', function()
+ before_each(function()
+ clear()
+ meths.ui_attach(80, 24, {})
+ meths.set_option('autoread', false)
+ meths.set_option('fsync', true)
+ end)
+
+ it('works', function()
+ if helpers.pending_win32(pending) then return end
+ source([[
+ func Test_file_changed_dialog()
+ au! FileChangedShell
+
+ new Xchanged_d
+ call setline(1, 'reload this')
+ write
+ " Need to wait until the timestamp would change by at least a second.
+ sleep 2
+ silent !echo 'extra line' >>Xchanged_d
+ call nvim_input('L')
+ checktime
+ call assert_match('W11:', v:warningmsg)
+ call assert_equal(2, line('$'))
+ call assert_equal('reload this', getline(1))
+ call assert_equal('extra line', getline(2))
+
+ " delete buffer, only shows an error, no prompt
+ silent !rm Xchanged_d
+ checktime
+ call assert_match('E211:', v:warningmsg)
+ call assert_equal(2, line('$'))
+ call assert_equal('extra line', getline(2))
+ let v:warningmsg = 'empty'
+
+ " change buffer, recreate the file and reload
+ call setline(1, 'buffer is changed')
+ silent !echo 'new line' >Xchanged_d
+ call nvim_input('L')
+ checktime
+ call assert_match('W12:', v:warningmsg)
+ call assert_equal(1, line('$'))
+ call assert_equal('new line', getline(1))
+
+ " Only mode changed, reload
+ silent !chmod +x Xchanged_d
+ call nvim_input('L')
+ checktime
+ call assert_match('W16:', v:warningmsg)
+ call assert_equal(1, line('$'))
+ call assert_equal('new line', getline(1))
+
+ " Only time changed, no prompt
+ sleep 2
+ silent !touch Xchanged_d
+ let v:warningmsg = ''
+ checktime Xchanged_d
+ call assert_equal('', v:warningmsg)
+ call assert_equal(1, line('$'))
+ call assert_equal('new line', getline(1))
+
+ bwipe!
+ call delete('Xchanged_d')
+ endfunc
+ ]])
+ call('Test_file_changed_dialog')
+ expected_empty()
+ end)
+
+ it('works with FileChangedShell', function()
+ source([[
+ func Test_FileChangedShell_edit_dialog()
+ new Xchanged_r
+ call setline(1, 'reload this')
+ set fileformat=unix
+ silent write " Use :silent to prevent a hit-enter prompt
+
+ " File format changed, reload (content only) via prompt
+ augroup testreload
+ au!
+ au FileChangedShell Xchanged_r let g:reason = v:fcs_reason | let v:fcs_choice = 'ask'
+ augroup END
+ call assert_equal(&fileformat, 'unix')
+ sleep 10m " make the test less flaky in Nvim
+ call writefile(["line1\r", "line2\r"], 'Xchanged_r')
+ let g:reason = ''
+ call nvim_input('L') " load file content only
+ checktime
+ call assert_equal('changed', g:reason)
+ call assert_equal(&fileformat, 'unix')
+ call assert_equal("line1\r", getline(1))
+ call assert_equal("line2\r", getline(2))
+ %s/\r
+ silent write " Use :silent to prevent a hit-enter prompt
+
+ " File format changed, reload (file and options) via prompt
+ augroup testreload
+ au!
+ au FileChangedShell Xchanged_r let g:reason = v:fcs_reason | let v:fcs_choice = 'ask'
+ augroup END
+ call assert_equal(&fileformat, 'unix')
+ sleep 10m " make the test less flaky in Nvim
+ call writefile(["line1\r", "line2\r"], 'Xchanged_r')
+ let g:reason = ''
+ call nvim_input('a') " load file content and options
+ checktime
+ call assert_equal('changed', g:reason)
+ call assert_equal(&fileformat, 'dos')
+ call assert_equal("line1", getline(1))
+ call assert_equal("line2", getline(2))
+ set fileformat=unix
+ silent write " Use :silent to prevent a hit-enter prompt
+
+ au! testreload
+ bwipe!
+ call delete(undofile('Xchanged_r'))
+ call delete('Xchanged_r')
+ endfunc
+ ]])
+ call('Test_FileChangedShell_edit_dialog')
+ expected_empty()
+ end)
+end)
diff --git a/test/functional/legacy/fixeol_spec.lua b/test/functional/legacy/fixeol_spec.lua
index d3ff86d349..3cc9d54e2b 100644
--- a/test/functional/legacy/fixeol_spec.lua
+++ b/test/functional/legacy/fixeol_spec.lua
@@ -6,12 +6,11 @@ local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers
describe('fixeol', function()
local function rmtestfiles()
- feed_command('%bwipeout!')
- feed_command('call delete("test.out")')
- feed_command('call delete("XXEol")')
- feed_command('call delete("XXNoEol")')
- feed_command('call delete("XXTestEol")')
- feed_command('call delete("XXTestNoEol")')
+ os.remove("test.out")
+ os.remove("XXEol")
+ os.remove("XXNoEol")
+ os.remove("XXTestEol")
+ os.remove("XXTestNoEol")
end
setup(function()
clear()
diff --git a/test/functional/legacy/function_sort_spec.lua b/test/functional/legacy/function_sort_spec.lua
index 12875460e0..414953aacc 100644
--- a/test/functional/legacy/function_sort_spec.lua
+++ b/test/functional/legacy/function_sort_spec.lua
@@ -52,6 +52,6 @@ describe('sort', function()
eq({'2', 'A', 'AA', 'a', 1, 3.3}, eval([[sort([3.3, 1, "2", "A", "a", "AA"], '')]]))
eq({'2', 'A', 'AA', 'a', 1, 3.3}, eval('sort([3.3, 1, "2", "A", "a", "AA"], 0)'))
eq({'2', 'A', 'a', 'AA', 1, 3.3}, eval('sort([3.3, 1, "2", "A", "a", "AA"], 1)'))
- neq(exc_exec('call sort([3.3, 1, "2"], 3)'):find('E474:'), nil)
+ neq(nil, exc_exec('call sort([3.3, 1, "2"], 3)'):find('E474:'))
end)
end)
diff --git a/test/functional/legacy/listchars_spec.lua b/test/functional/legacy/listchars_spec.lua
index dc6ccd3628..a9aa238d4e 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,126 @@ 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:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ [2] = {bold = true, reverse = true}, -- StatusLine
+ [3] = {reverse = true}, -- StatusLineNC
+ [4] = {background = Screen.colors.Grey, foreground = Screen.colors.DarkBlue}, -- FoldColumn, SignColumn
+ })
+ 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([[
+ {4: }aaa │{4: }a{1:>}│{4: }^aaa |
+ {4: } │{4: } │{4: } |
+ {4: }a │{4: }a │{4: }a |
+ {4: }aaaaaa │{4: }a{1:>}│{4: }aaaaaa |
+ {1:~ }│{1:~ }│{1:~ }|
+ {1:~ }│{1:~ }│{1:~ }|
+ {1:~ }│{1:~ }│{1:~ }|
+ {1:~ }│{1:~ }│{1:~ }|
+ {3:[No Name] [+] <[+] }{2:[No Name] [+] }|
+ |
+ ]])
+ feed('<C-W>>')
+ screen:expect([[
+ {4: }aaa │{4: }{1:>}│{4: }^aaa |
+ {4: } │{4: } │{4: } |
+ {4: }a │{4: }a│{4: }a |
+ {4: }aaaaaa │{4: }{1:>}│{4: }aaaaaa |
+ {1:~ }│{1:~ }│{1:~ }|
+ {1:~ }│{1:~ }│{1:~ }|
+ {1:~ }│{1:~ }│{1:~ }|
+ {1:~ }│{1:~ }│{1:~ }|
+ {3:[No Name] [+] <+] }{2:[No Name] [+] }|
+ |
+ ]])
+ feed('<C-W>>')
+ screen:expect([[
+ {4: }aaa │{4: }│{4: }^aaa |
+ {4: } │{4: }│{4: } |
+ {4: }a │{4: }│{4: }a |
+ {4: }aaaaaa │{4: }│{4: }aaaaaa |
+ {1:~ }│{1:~ }│{1:~ }|
+ {1:~ }│{1:~ }│{1:~ }|
+ {1:~ }│{1:~ }│{1:~ }|
+ {1:~ }│{1:~ }│{1:~ }|
+ {3:[No Name] [+] <] }{2:[No Name] [+] }|
+ |
+ ]])
+ feed('<C-W>>')
+ screen:expect([[
+ {4: }aaa │{4: }│{4: }^aaa |
+ {4: } │{4: }│{4: } |
+ {4: }a │{4: }│{4: }a |
+ {4: }aaaaaa │{4: }│{4: }aaaaaa |
+ {1:~ }│{1:~ }│{1:~ }|
+ {1:~ }│{1:~ }│{1:~ }|
+ {1:~ }│{1:~ }│{1:~ }|
+ {1:~ }│{1:~ }│{1:~ }|
+ {3:[No Name] [+] < }{2:[No Name] [+] }|
+ |
+ ]])
+ feed('<C-W>>')
+ screen:expect([[
+ {4: }aaa │{4: }│{4: }^aaa |
+ {4: } │{4: }│{4: } |
+ {4: }a │{4: }│{4: }a |
+ {4: }aaaaaa │{4: }│{4: }aaaaaa |
+ {1:~ }│{1:~}│{1:~ }|
+ {1:~ }│{1:~}│{1:~ }|
+ {1:~ }│{1:~}│{1:~ }|
+ {1:~ }│{1:~}│{1:~ }|
+ {3:[No Name] [+] < }{2:[No Name] [+] }|
+ |
+ ]])
+ feed('<C-W>h')
+ feed_command('set nowrap foldcolumn=4')
+ screen:expect([[
+ {4: }aaa │{4: }^aaa │{4: }aaa |
+ {4: } │{4: } │{4: } |
+ {4: }a │{4: }a │{4: }a |
+ {4: }aaaaaa │{4: }aaaaaa │{4: }aaaaaa |
+ {1:~ }│{1:~ }│{1:~ }|
+ {1:~ }│{1:~ }│{1:~ }|
+ {1:~ }│{1:~ }│{1:~ }|
+ {1:~ }│{1:~ }│{1:~ }|
+ {3:[No Name] [+] }{2:[No Name] [+] }{3:[No Name] [+] }|
+ :set nowrap foldcolumn=4 |
+ ]])
+ feed('15<C-W><lt>')
+ screen:expect([[
+ {4: }aaa │{4: }│{4: }aaa |
+ {4: } │{4: }│{4: } |
+ {4: }a │{4: }│{4: }a |
+ {4: }aaaaaa │{4: ^ }│{4: }aaaaaa |
+ {1:~ }│{1:~ }│{1:~ }|
+ {1:~ }│{1:~ }│{1:~ }|
+ {1:~ }│{1:~ }│{1:~ }|
+ {1:~ }│{1:~ }│{1:~ }|
+ {3:[No Name] [+] }{2:<[+] }{3:[No Name] [+] }|
+ :set nowrap foldcolumn=4 |
+ ]])
+ feed('4<C-W><lt>')
+ screen:expect([[
+ {4: }aaa │{4: }│{4: }aaa |
+ {4: } │{4: }│{4: } |
+ {4: }a │{4: }│{4: }a |
+ {4: }aaaaaa │{4:^ }│{4: }aaaaaa |
+ {1:~ }│{1:~}│{1:~ }|
+ {1:~ }│{1:~}│{1:~ }|
+ {1:~ }│{1:~}│{1:~ }|
+ {1:~ }│{1:~}│{1:~ }|
+ {3:[No Name] [+] }{2:< }{3:[No Name] [+] }|
+ :set nowrap foldcolumn=4 |
+ ]])
+ end)
end)
diff --git a/test/functional/legacy/mapping_spec.lua b/test/functional/legacy/mapping_spec.lua
index 92a757ca85..456acc12b5 100644
--- a/test/functional/legacy/mapping_spec.lua
+++ b/test/functional/legacy/mapping_spec.lua
@@ -2,7 +2,9 @@
local helpers = require('test.functional.helpers')(after_each)
local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert
-local feed_command, expect, poke_eventloop = helpers.feed_command, helpers.expect, helpers.poke_eventloop
+local expect, poke_eventloop = helpers.expect, helpers.poke_eventloop
+local command, eq, eval, meths = helpers.command, helpers.eq, helpers.eval, helpers.meths
+local sleep = helpers.sleep
describe('mapping', function()
before_each(clear)
@@ -13,7 +15,7 @@ describe('mapping', function()
]])
-- Abbreviations with р (0x80) should work.
- feed_command('inoreab чкпр vim')
+ command('inoreab чкпр vim')
feed('GAчкпр <esc>')
expect([[
@@ -23,17 +25,15 @@ describe('mapping', function()
it('Ctrl-c works in Insert mode', function()
-- Mapping of ctrl-c in insert mode
- feed_command('set cpo-=< cpo-=k')
- feed_command('inoremap <c-c> <ctrl-c>')
- feed_command('cnoremap <c-c> dummy')
- feed_command('cunmap <c-c>')
+ command('set cpo-=< cpo-=k')
+ command('inoremap <c-c> <ctrl-c>')
+ command('cnoremap <c-c> dummy')
+ command('cunmap <c-c>')
feed('GA<cr>')
- feed('TEST2: CTRL-C |')
+ -- XXX: editor must be in Insert mode before <C-C> is put into input buffer
poke_eventloop()
- feed('<c-c>A|<cr><esc>')
- poke_eventloop()
- feed_command('unmap <c-c>')
- feed_command('unmap! <c-c>')
+ feed('TEST2: CTRL-C |<c-c>A|<cr><esc>')
+ command('unmap! <c-c>')
expect([[
@@ -42,13 +42,12 @@ describe('mapping', function()
end)
it('Ctrl-c works in Visual mode', function()
- feed_command([[vnoremap <c-c> :<C-u>$put ='vmap works'<cr>]])
+ command([[vnoremap <c-c> :<C-u>$put ='vmap works'<cr>]])
feed('GV')
- -- XXX: For some reason the mapping is only triggered
- -- when <C-c> is in a separate feed command.
+ -- XXX: editor must be in Visual mode before <C-C> is put into input buffer
poke_eventloop()
- feed('<c-c>')
- feed_command('vunmap <c-c>')
+ feed('vV<c-c>')
+ command('vunmap <c-c>')
expect([[
@@ -57,23 +56,23 @@ describe('mapping', function()
it('langmap', function()
-- langmap should not get remapped in insert mode.
- feed_command('inoremap { FAIL_ilangmap')
- feed_command('set langmap=+{ langnoremap')
+ command('inoremap { FAIL_ilangmap')
+ command('set langmap=+{ langnoremap')
feed('o+<esc>')
-- Insert mode expr mapping with langmap.
- feed_command('inoremap <expr> { "FAIL_iexplangmap"')
+ command('inoremap <expr> { "FAIL_iexplangmap"')
feed('o+<esc>')
-- langmap should not get remapped in cmdline mode.
- feed_command('cnoremap { FAIL_clangmap')
+ command('cnoremap { FAIL_clangmap')
feed('o+<esc>')
- feed_command('cunmap {')
+ command('cunmap {')
-- cmdline mode expr mapping with langmap.
- feed_command('cnoremap <expr> { "FAIL_cexplangmap"')
+ command('cnoremap <expr> { "FAIL_cexplangmap"')
feed('o+<esc>')
- feed_command('cunmap {')
+ command('cunmap {')
-- Assert buffer contents.
expect([[
@@ -91,10 +90,10 @@ describe('mapping', function()
]])
-- Vim's issue #212 (feedkeys insert mapping at current position)
- feed_command('nnoremap . :call feedkeys(".", "in")<cr>')
+ command('nnoremap . :call feedkeys(".", "in")<cr>')
feed('/^a b<cr>')
feed('0qqdw.ifoo<esc>qj0@q<esc>')
- feed_command('unmap .')
+ command('unmap .')
expect([[
fooc d
fooc d
@@ -103,15 +102,15 @@ describe('mapping', function()
it('i_CTRL-G_U', function()
-- <c-g>U<cursor> works only within a single line
- feed_command('imapclear')
- feed_command('imap ( ()<c-g>U<left>')
+ command('imapclear')
+ command('imap ( ()<c-g>U<left>')
feed('G2o<esc>ki<cr>Test1: text with a (here some more text<esc>k.')
-- test undo
feed('G2o<esc>ki<cr>Test2: text wit a (here some more text [und undo]<c-g>u<esc>k.u')
- feed_command('imapclear')
- feed_command('set whichwrap=<,>,[,]')
+ command('imapclear')
+ command('set whichwrap=<,>,[,]')
feed('G3o<esc>2k')
- feed_command([[:exe ":norm! iTest3: text with a (parenthesis here\<C-G>U\<Right>new line here\<esc>\<up>\<up>."]])
+ command([[:exe ":norm! iTest3: text with a (parenthesis here\<C-G>U\<Right>new line here\<esc>\<up>\<up>."]])
expect([[
@@ -126,4 +125,58 @@ describe('mapping', function()
new line here
]])
end)
+
+ it('dragging starts Select mode even if coming from mapping vim-patch:8.2.4806', function()
+ command('set mouse=a')
+ command('set selectmode=mouse')
+
+ command('nnoremap <LeftDrag> <LeftDrag><Cmd><CR>')
+ sleep(10)
+ meths.input_mouse('left', 'press', '', 0, 0, 0)
+ sleep(10)
+ meths.input_mouse('left', 'drag', '', 0, 0, 1)
+ sleep(10)
+ eq('s', eval('mode()'))
+ end)
+
+ it('<LeftDrag> mapping in Insert mode works correctly vim-patch:8.2.4692', function()
+ command('set mouse=a')
+
+ command('inoremap <LeftDrag> <LeftDrag><Cmd>let g:dragged = 1<CR>')
+ feed('i')
+ sleep(10)
+ meths.input_mouse('left', 'press', '', 0, 0, 0)
+ sleep(10)
+ meths.input_mouse('left', 'drag', '', 0, 0, 1)
+ sleep(10)
+ eq(1, eval('g:dragged'))
+ eq('v', eval('mode()'))
+ feed([[<C-\><C-N>]])
+
+ command([[inoremap <LeftDrag> <LeftDrag><C-\><C-N>]])
+ feed('i')
+ sleep(10)
+ meths.input_mouse('left', 'press', '', 0, 0, 0)
+ sleep(10)
+ meths.input_mouse('left', 'drag', '', 0, 0, 1)
+ sleep(10)
+ eq('n', eval('mode()'))
+ end)
+
+ it('timeout works after an <Nop> mapping is triggered on timeout vim-patch:8.1.0052', function()
+ command('set timeout timeoutlen=400')
+ command('inoremap ab TEST')
+ command('inoremap a <Nop>')
+ -- Enter Insert mode
+ feed('i')
+ -- Wait for the "a" mapping to time out
+ feed('a')
+ sleep(500)
+ -- Send "a" and wait for a period shorter than 'timeoutlen'
+ feed('a')
+ sleep(100)
+ -- Send "b", should trigger the "ab" mapping
+ feed('b')
+ expect('TEST')
+ end)
end)
diff --git a/test/functional/legacy/memory_usage_spec.lua b/test/functional/legacy/memory_usage_spec.lua
index 8d25b9d927..eec89aa919 100644
--- a/test/functional/legacy/memory_usage_spec.lua
+++ b/test/functional/legacy/memory_usage_spec.lua
@@ -10,6 +10,7 @@ local source = helpers.source
local poke_eventloop = helpers.poke_eventloop
local uname = helpers.uname
local load_adjust = helpers.load_adjust
+local write_file = helpers.write_file
local isCI = helpers.isCI
local function isasan()
@@ -84,6 +85,12 @@ setmetatable(monitor_memory_usage,
end})
describe('memory usage', function()
+ local tmpfile = 'X_memory_usage'
+
+ after_each(function()
+ os.remove(tmpfile)
+ end)
+
local function check_result(tbl, status, result)
if not status then
print('')
@@ -103,7 +110,7 @@ describe('memory usage', function()
it('function capture vargs', function()
local pid = eval('getpid()')
local before = monitor_memory_usage(pid)
- source([[
+ write_file(tmpfile, [[
func s:f(...)
let x = a:000
endfunc
@@ -111,6 +118,8 @@ describe('memory usage', function()
call s:f(0)
endfor
]])
+ -- TODO: check_result fails if command() is used here. Why? #16064
+ feed_command('source '..tmpfile)
poke_eventloop()
local after = monitor_memory_usage(pid)
-- Estimate the limit of max usage as 2x initial usage.
@@ -136,7 +145,7 @@ describe('memory usage', function()
it('function capture lvars', function()
local pid = eval('getpid()')
local before = monitor_memory_usage(pid)
- local fname = source([[
+ write_file(tmpfile, [[
if !exists('s:defined_func')
func s:f()
let x = l:
@@ -147,10 +156,12 @@ describe('memory usage', function()
call s:f()
endfor
]])
+ feed_command('source '..tmpfile)
poke_eventloop()
local after = monitor_memory_usage(pid)
for _ = 1, 3 do
- feed_command('so '..fname)
+ -- TODO: check_result fails if command() is used here. Why? #16064
+ feed_command('source '..tmpfile)
poke_eventloop()
end
local last = monitor_memory_usage(pid)
diff --git a/test/functional/legacy/messages_spec.lua b/test/functional/legacy/messages_spec.lua
new file mode 100644
index 0000000000..b296ac909d
--- /dev/null
+++ b/test/functional/legacy/messages_spec.lua
@@ -0,0 +1,421 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local clear = helpers.clear
+local command = helpers.command
+local exec = helpers.exec
+local feed = helpers.feed
+
+before_each(clear)
+
+describe('messages', function()
+ local screen
+
+ describe('more prompt', function()
+ before_each(function()
+ screen = Screen.new(75, 6)
+ screen:set_default_attr_ids({
+ [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ [1] = {bold = true, foreground = Screen.colors.SeaGreen}, -- MoreMsg
+ [2] = {foreground = Screen.colors.Brown}, -- LineNr
+ [3] = {foreground = Screen.colors.Blue}, -- SpecialKey
+ })
+ screen:attach()
+ command('set more')
+ end)
+
+ -- oldtest: Test_message_more()
+ it('works', function()
+ command('call setline(1, range(1, 100))')
+
+ feed(':%p#\n')
+ screen:expect([[
+ {2: 1 }1 |
+ {2: 2 }2 |
+ {2: 3 }3 |
+ {2: 4 }4 |
+ {2: 5 }5 |
+ {1:-- More --}^ |
+ ]])
+
+ feed('?')
+ screen:expect([[
+ {2: 1 }1 |
+ {2: 2 }2 |
+ {2: 3 }3 |
+ {2: 4 }4 |
+ {2: 5 }5 |
+ {1:-- More -- SPACE/d/j: screen/page/line down, b/u/k: up, q: quit }^ |
+ ]])
+
+ -- Down a line with j, <CR>, <NL> or <Down>.
+ feed('j')
+ screen:expect([[
+ {2: 2 }2 |
+ {2: 3 }3 |
+ {2: 4 }4 |
+ {2: 5 }5 |
+ {2: 6 }6 |
+ {1:-- More --}^ |
+ ]])
+ feed('<NL>')
+ screen:expect([[
+ {2: 3 }3 |
+ {2: 4 }4 |
+ {2: 5 }5 |
+ {2: 6 }6 |
+ {2: 7 }7 |
+ {1:-- More --}^ |
+ ]])
+ feed('<CR>')
+ screen:expect([[
+ {2: 4 }4 |
+ {2: 5 }5 |
+ {2: 6 }6 |
+ {2: 7 }7 |
+ {2: 8 }8 |
+ {1:-- More --}^ |
+ ]])
+ feed('<Down>')
+ screen:expect([[
+ {2: 5 }5 |
+ {2: 6 }6 |
+ {2: 7 }7 |
+ {2: 8 }8 |
+ {2: 9 }9 |
+ {1:-- More --}^ |
+ ]])
+
+ -- Down a screen with <Space>, f, or <PageDown>.
+ feed('f')
+ screen:expect([[
+ {2: 10 }10 |
+ {2: 11 }11 |
+ {2: 12 }12 |
+ {2: 13 }13 |
+ {2: 14 }14 |
+ {1:-- More --}^ |
+ ]])
+ feed('<Space>')
+ screen:expect([[
+ {2: 15 }15 |
+ {2: 16 }16 |
+ {2: 17 }17 |
+ {2: 18 }18 |
+ {2: 19 }19 |
+ {1:-- More --}^ |
+ ]])
+ feed('<PageDown>')
+ screen:expect([[
+ {2: 20 }20 |
+ {2: 21 }21 |
+ {2: 22 }22 |
+ {2: 23 }23 |
+ {2: 24 }24 |
+ {1:-- More --}^ |
+ ]])
+
+ -- Down a page (half a screen) with d.
+ feed('d')
+ screen:expect([[
+ {2: 23 }23 |
+ {2: 24 }24 |
+ {2: 25 }25 |
+ {2: 26 }26 |
+ {2: 27 }27 |
+ {1:-- More --}^ |
+ ]])
+
+ -- Down all the way with 'G'.
+ feed('G')
+ screen:expect([[
+ {2: 96 }96 |
+ {2: 97 }97 |
+ {2: 98 }98 |
+ {2: 99 }99 |
+ {2:100 }100 |
+ {1:Press ENTER or type command to continue}^ |
+ ]])
+
+ -- Up a line k, <BS> or <Up>.
+ feed('k')
+ screen:expect([[
+ {2: 95 }95 |
+ {2: 96 }96 |
+ {2: 97 }97 |
+ {2: 98 }98 |
+ {2: 99 }99 |
+ {1:-- More --}^ |
+ ]])
+ feed('<BS>')
+ screen:expect([[
+ {2: 94 }94 |
+ {2: 95 }95 |
+ {2: 96 }96 |
+ {2: 97 }97 |
+ {2: 98 }98 |
+ {1:-- More --}^ |
+ ]])
+ feed('<Up>')
+ screen:expect([[
+ {2: 93 }93 |
+ {2: 94 }94 |
+ {2: 95 }95 |
+ {2: 96 }96 |
+ {2: 97 }97 |
+ {1:-- More --}^ |
+ ]])
+
+ -- Up a screen with b or <PageUp>.
+ feed('b')
+ screen:expect([[
+ {2: 88 }88 |
+ {2: 89 }89 |
+ {2: 90 }90 |
+ {2: 91 }91 |
+ {2: 92 }92 |
+ {1:-- More --}^ |
+ ]])
+ feed('<PageUp>')
+ screen:expect([[
+ {2: 83 }83 |
+ {2: 84 }84 |
+ {2: 85 }85 |
+ {2: 86 }86 |
+ {2: 87 }87 |
+ {1:-- More --}^ |
+ ]])
+
+ -- Up a page (half a screen) with u.
+ feed('u')
+ screen:expect([[
+ {2: 80 }80 |
+ {2: 81 }81 |
+ {2: 82 }82 |
+ {2: 83 }83 |
+ {2: 84 }84 |
+ {1:-- More --}^ |
+ ]])
+
+ -- Up all the way with 'g'.
+ feed('g')
+ screen:expect([[
+ {2: 1 }1 |
+ {2: 2 }2 |
+ {2: 3 }3 |
+ {2: 4 }4 |
+ {2: 5 }5 |
+ {1:-- More --}^ |
+ ]])
+
+ -- All the way down. Pressing f should do nothing but pressing
+ -- space should end the more prompt.
+ feed('G')
+ screen:expect([[
+ {2: 96 }96 |
+ {2: 97 }97 |
+ {2: 98 }98 |
+ {2: 99 }99 |
+ {2:100 }100 |
+ {1:Press ENTER or type command to continue}^ |
+ ]])
+ feed('f')
+ screen:expect_unchanged()
+ feed('<Space>')
+ screen:expect([[
+ 96 |
+ 97 |
+ 98 |
+ 99 |
+ ^100 |
+ |
+ ]])
+
+ -- Pressing g< shows the previous command output.
+ feed('g<lt>')
+ screen:expect([[
+ {2: 96 }96 |
+ {2: 97 }97 |
+ {2: 98 }98 |
+ {2: 99 }99 |
+ {2:100 }100 |
+ {1:Press ENTER or type command to continue}^ |
+ ]])
+
+ feed(':%p#\n')
+ screen:expect([[
+ {2: 1 }1 |
+ {2: 2 }2 |
+ {2: 3 }3 |
+ {2: 4 }4 |
+ {2: 5 }5 |
+ {1:-- More --}^ |
+ ]])
+
+ -- Stop command output with q, <Esc> or CTRL-C.
+ feed('q')
+ screen:expect([[
+ 96 |
+ 97 |
+ 98 |
+ 99 |
+ ^100 |
+ |
+ ]])
+
+ -- Execute a : command from the more prompt
+ feed(':%p#\n')
+ screen:expect([[
+ {2: 1 }1 |
+ {2: 2 }2 |
+ {2: 3 }3 |
+ {2: 4 }4 |
+ {2: 5 }5 |
+ {1:-- More --}^ |
+ ]])
+ feed(':')
+ screen:expect([[
+ {2: 1 }1 |
+ {2: 2 }2 |
+ {2: 3 }3 |
+ {2: 4 }4 |
+ {2: 5 }5 |
+ :^ |
+ ]])
+ feed("echo 'Hello'\n")
+ screen:expect([[
+ {2: 2 }2 |
+ {2: 3 }3 |
+ {2: 4 }4 |
+ {2: 5 }5 |
+ Hello |
+ {1:Press ENTER or type command to continue}^ |
+ ]])
+ end)
+
+ -- oldtest: Test_quit_long_message()
+ it('with control characters can be quit vim-patch:8.2.1844', function()
+ screen:try_resize(40, 6)
+ feed([[:echom range(9999)->join("\x01")<CR>]])
+ screen:expect([[
+ 0{3:^A}1{3:^A}2{3:^A}3{3:^A}4{3:^A}5{3:^A}6{3:^A}7{3:^A}8{3:^A}9{3:^A}10{3:^A}11{3:^A}12|
+ {3:^A}13{3:^A}14{3:^A}15{3:^A}16{3:^A}17{3:^A}18{3:^A}19{3:^A}20{3:^A}21{3:^A}22|
+ {3:^A}23{3:^A}24{3:^A}25{3:^A}26{3:^A}27{3:^A}28{3:^A}29{3:^A}30{3:^A}31{3:^A}32|
+ {3:^A}33{3:^A}34{3:^A}35{3:^A}36{3:^A}37{3:^A}38{3:^A}39{3:^A}40{3:^A}41{3:^A}42|
+ {3:^A}43{3:^A}44{3:^A}45{3:^A}46{3:^A}47{3:^A}48{3:^A}49{3:^A}50{3:^A}51{3:^A}52|
+ {1:-- More --}^ |
+ ]])
+ feed('q')
+ screen:expect([[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ end)
+ end)
+
+ -- oldtest: Test_ask_yesno()
+ it('y/n prompt works', function()
+ screen = Screen.new(75, 6)
+ screen:set_default_attr_ids({
+ [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ [1] = {bold = true, foreground = Screen.colors.SeaGreen}, -- MoreMsg
+ [2] = {bold = true, reverse = true}, -- MsgSeparator
+ })
+ screen:attach()
+ command('set noincsearch nohlsearch inccommand=')
+ command('call setline(1, range(1, 2))')
+
+ feed(':2,1s/^/n/\n')
+ screen:expect([[
+ 1 |
+ 2 |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {1:Backwards range given, OK to swap (y/n)?}^ |
+ ]])
+ feed('n')
+ screen:expect([[
+ ^1 |
+ 2 |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {1:Backwards range given, OK to swap (y/n)?}n |
+ ]])
+
+ feed(':2,1s/^/Esc/\n')
+ screen:expect([[
+ 1 |
+ 2 |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {1:Backwards range given, OK to swap (y/n)?}^ |
+ ]])
+ feed('<Esc>')
+ screen:expect([[
+ ^1 |
+ 2 |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {1:Backwards range given, OK to swap (y/n)?}n |
+ ]])
+
+ feed(':2,1s/^/y/\n')
+ screen:expect([[
+ 1 |
+ 2 |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {1:Backwards range given, OK to swap (y/n)?}^ |
+ ]])
+ feed('y')
+ screen:expect([[
+ y1 |
+ ^y2 |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {1:Backwards range given, OK to swap (y/n)?}y |
+ ]])
+ end)
+
+ -- oldtest: Test_fileinfo_after_echo()
+ it('fileinfo does not overwrite echo message vim-patch:8.2.4156', function()
+ screen = Screen.new(40, 6)
+ screen:set_default_attr_ids({
+ [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ })
+ screen:attach()
+ exec([[
+ set shortmess-=F
+
+ file a.txt
+
+ hide edit b.txt
+ call setline(1, "hi")
+ setlocal modified
+
+ hide buffer a.txt
+
+ autocmd CursorHold * buf b.txt | w | echo "'b' written"
+ ]])
+ command('set updatetime=50')
+ feed('0$')
+ screen:expect([[
+ ^hi |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ 'b' written |
+ ]])
+ os.remove('b.txt')
+ end)
+end)
diff --git a/test/functional/legacy/packadd_spec.lua b/test/functional/legacy/packadd_spec.lua
deleted file mode 100644
index 4f9f5a0237..0000000000
--- a/test/functional/legacy/packadd_spec.lua
+++ /dev/null
@@ -1,507 +0,0 @@
--- Tests for 'packpath' and :packadd
-
-local helpers = require('test.functional.helpers')(after_each)
-local clear, source, command = helpers.clear, helpers.source, helpers.command
-local call, eq, nvim = helpers.call, helpers.eq, helpers.meths
-local feed = helpers.feed
-
-local function expected_empty()
- eq({}, nvim.get_vvar('errors'))
-end
-
-describe('packadd', function()
- before_each(function()
- clear()
-
- source([=[
- func Escape(s)
- return escape(a:s, '\~')
- endfunc
-
- func SetUp()
- let s:topdir = expand(getcwd() . '/Xdir')
- if isdirectory(s:topdir)
- call delete(s:topdir, 'rf')
- endif
- exe 'set packpath=' . s:topdir
- let s:plugdir = expand(s:topdir . '/pack/mine/opt/mytest')
- endfunc
-
- func TearDown()
- call delete(s:topdir, 'rf')
- endfunc
-
- func Test_packadd()
- if !exists('s:plugdir')
- echomsg 'when running this test manually, call SetUp() first'
- return
- endif
-
- call mkdir(s:plugdir . '/plugin/also', 'p')
- call mkdir(s:plugdir . '/ftdetect', 'p')
- call mkdir(s:plugdir . '/after', 'p')
- set rtp&
- let rtp = &rtp
- filetype on
-
- let rtp_entries = split(rtp, ',')
- for entry in rtp_entries
- if entry =~? '\<after\>'
- let first_after_entry = entry
- break
- endif
- endfor
-
- exe 'split ' . s:plugdir . '/plugin/test.vim'
- call setline(1, 'let g:plugin_works = 42')
- wq
-
- exe 'split ' . s:plugdir . '/plugin/also/loaded.vim'
- call setline(1, 'let g:plugin_also_works = 77')
- wq
-
- exe 'split ' . s:plugdir . '/ftdetect/test.vim'
- call setline(1, 'let g:ftdetect_works = 17')
- wq
-
- packadd mytest
-
- call assert_true(42, g:plugin_works)
- call assert_equal(77, g:plugin_also_works)
- call assert_true(17, g:ftdetect_works)
- call assert_true(len(&rtp) > len(rtp))
- call assert_match(Escape(s:plugdir) . '\($\|,\)', &rtp)
-
- let new_after = match(&rtp, Escape(expand(s:plugdir . '/after') . ','))
- let forwarded = substitute(first_after_entry, '\\', '[/\\\\]', 'g')
- let old_after = match(&rtp, ',' . escape(forwarded, '~') . '\>')
- call assert_true(new_after > 0, 'rtp is ' . &rtp)
- call assert_true(old_after > 0, 'match ' . forwarded . ' in ' . &rtp)
- call assert_true(new_after < old_after, 'rtp is ' . &rtp)
-
- " NOTE: '/.../opt/myte' forwardly matches with '/.../opt/mytest'
- call mkdir(fnamemodify(s:plugdir, ':h') . '/myte', 'p')
- let rtp = &rtp
- packadd myte
-
- " Check the path of 'myte' is added
- call assert_true(len(&rtp) > len(rtp))
- call assert_match(Escape(s:plugdir) . '\($\|,\)', &rtp)
-
- " Check exception
- call assert_fails("packadd directorynotfound", 'E919:')
- call assert_fails("packadd", 'E471:')
- endfunc
-
- func Test_packadd_start()
- let plugdir = expand(s:topdir . '/pack/mine/start/other')
- call mkdir(plugdir . '/plugin', 'p')
- set rtp&
- let rtp = &rtp
- filetype on
-
- exe 'split ' . plugdir . '/plugin/test.vim'
- call setline(1, 'let g:plugin_works = 24')
- wq
-
- exe 'split ' . plugdir . '/plugin/test.lua'
- call setline(1, 'vim.g.plugin_lua_works = 24')
- wq
-
- packadd other
-
- call assert_equal(24, g:plugin_works)
- call assert_equal(24, g:plugin_lua_works)
- call assert_true(len(&rtp) > len(rtp))
- call assert_match(Escape(plugdir) . '\($\|,\)', &rtp)
- endfunc
-
- func Test_packadd_noload()
- call mkdir(s:plugdir . '/plugin', 'p')
- call mkdir(s:plugdir . '/syntax', 'p')
- set rtp&
- let rtp = &rtp
-
- exe 'split ' . s:plugdir . '/plugin/test.vim'
- call setline(1, 'let g:plugin_works = 42')
- wq
- exe 'split ' . s:plugdir . '/plugin/test.lua'
- call setline(1, 'let g:plugin_lua_works = 42')
- wq
- let g:plugin_works = 0
- let g:plugin_lua_works = 0
-
- packadd! mytest
-
- call assert_true(len(&rtp) > len(rtp))
- call assert_match(Escape(s:plugdir) . '\($\|,\)', &rtp)
- call assert_equal(0, g:plugin_works)
- call assert_equal(0, g:plugin_lua_works)
-
- " check the path is not added twice
- let new_rtp = &rtp
- packadd! mytest
- call assert_equal(new_rtp, &rtp)
- endfunc
-
- func Test_packadd_symlink_dir()
- let top2_dir = expand(s:topdir . '/Xdir2')
- let real_dir = expand(s:topdir . '/Xsym')
- call mkdir(real_dir, 'p')
- if has('win32')
- exec "silent! !mklink /d" top2_dir "Xsym"
- else
- exec "silent! !ln -s Xsym" top2_dir
- endif
- let &rtp = top2_dir . ',' . expand(top2_dir . '/after')
- let &packpath = &rtp
-
- let s:plugdir = expand(top2_dir . '/pack/mine/opt/mytest')
- call mkdir(s:plugdir . '/plugin', 'p')
-
- exe 'split ' . s:plugdir . '/plugin/test.vim'
- call setline(1, 'let g:plugin_works = 44')
- wq
- let g:plugin_works = 0
-
- packadd mytest
-
- " Must have been inserted in the middle, not at the end
- call assert_match(Escape(expand('/pack/mine/opt/mytest').','), &rtp)
- call assert_equal(44, g:plugin_works)
-
- " No change when doing it again.
- let rtp_before = &rtp
- packadd mytest
- call assert_equal(rtp_before, &rtp)
-
- set rtp&
- let rtp = &rtp
- exec "silent !" (has('win32') ? "rd /q/s" : "rm") top2_dir
- endfunc
-
- func Test_packadd_symlink_dir2()
- let top2_dir = expand(s:topdir . '/Xdir2')
- let real_dir = expand(s:topdir . '/Xsym/pack')
- call mkdir(top2_dir, 'p')
- call mkdir(real_dir, 'p')
- let &rtp = top2_dir . ',' . top2_dir . '/after'
- let &packpath = &rtp
-
- if has('win32')
- exec "silent! !mklink /d" top2_dir "Xsym"
- else
- exec "silent !ln -s ../Xsym/pack" top2_dir . '/pack'
- endif
- let s:plugdir = expand(top2_dir . '/pack/mine/opt/mytest')
- call mkdir(s:plugdir . '/plugin', 'p')
-
- exe 'split ' . s:plugdir . '/plugin/test.vim'
- call setline(1, 'let g:plugin_works = 48')
- wq
- let g:plugin_works = 0
-
- packadd mytest
-
- " Must have been inserted in the middle, not at the end
- call assert_match(Escape(expand('/Xdir2/pack/mine/opt/mytest').','), &rtp)
- call assert_equal(48, g:plugin_works)
-
- " No change when doing it again.
- let rtp_before = &rtp
- packadd mytest
- call assert_equal(rtp_before, &rtp)
-
- set rtp&
- let rtp = &rtp
- if has('win32')
- exec "silent !rd /q/s" top2_dir
- else
- exec "silent !rm" top2_dir . '/pack'
- exec "silent !rmdir" top2_dir
- endif
- endfunc
-
- func Test_packloadall()
- " plugin foo with an autoload directory
- let fooplugindir = &packpath . '/pack/mine/start/foo/plugin'
- call mkdir(fooplugindir, 'p')
- call writefile(['let g:plugin_foo_number = 1234',
- \ 'let g:plugin_foo_auto = bbb#value',
- \ 'let g:plugin_extra_auto = extra#value'], fooplugindir . '/bar.vim')
- let fooautodir = &packpath . '/pack/mine/start/foo/autoload'
- call mkdir(fooautodir, 'p')
- call writefile(['let bar#value = 77'], fooautodir . '/bar.vim')
-
- " plugin aaa with an autoload directory
- let aaaplugindir = &packpath . '/pack/mine/start/aaa/plugin'
- call mkdir(aaaplugindir, 'p')
- call writefile(['let g:plugin_aaa_number = 333',
- \ 'let g:plugin_aaa_auto = bar#value'], aaaplugindir . '/bbb.vim')
- let aaaautodir = &packpath . '/pack/mine/start/aaa/autoload'
- call mkdir(aaaautodir, 'p')
- call writefile(['let bbb#value = 55'], aaaautodir . '/bbb.vim')
-
- " plugin extra with only an autoload directory
- let extraautodir = &packpath . '/pack/mine/start/extra/autoload'
- call mkdir(extraautodir, 'p')
- call writefile(['let extra#value = 99'], extraautodir . '/extra.vim')
-
- packloadall
- call assert_equal(1234, g:plugin_foo_number)
- call assert_equal(55, g:plugin_foo_auto)
- call assert_equal(99, g:plugin_extra_auto)
- call assert_equal(333, g:plugin_aaa_number)
- call assert_equal(77, g:plugin_aaa_auto)
-
- " only works once
- call writefile(['let g:plugin_bar_number = 4321'],
- \ fooplugindir . '/bar2.vim')
- packloadall
- call assert_false(exists('g:plugin_bar_number'))
-
- " works when ! used
- packloadall!
- call assert_equal(4321, g:plugin_bar_number)
- endfunc
-
- func Test_helptags()
- let docdir1 = &packpath . '/pack/mine/start/foo/doc'
- let docdir2 = &packpath . '/pack/mine/start/bar/doc'
- call mkdir(docdir1, 'p')
- call mkdir(docdir2, 'p')
- call writefile(['look here: *look-here*'], docdir1 . '/bar.txt')
- call writefile(['look away: *look-away*'], docdir2 . '/foo.txt')
- exe 'set rtp=' . &packpath . '/pack/mine/start/foo,' . &packpath . '/pack/mine/start/bar'
-
- helptags ALL
-
- let tags1 = readfile(docdir1 . '/tags')
- call assert_match('look-here', tags1[0])
- let tags2 = readfile(docdir2 . '/tags')
- call assert_match('look-away', tags2[0])
-
- call assert_fails('helptags abcxyz', 'E150:')
- endfunc
-
- func Test_colorscheme()
- let colordirrun = &packpath . '/runtime/colors'
- let colordirstart = &packpath . '/pack/mine/start/foo/colors'
- let colordiropt = &packpath . '/pack/mine/opt/bar/colors'
- call mkdir(colordirrun, 'p')
- call mkdir(colordirstart, 'p')
- call mkdir(colordiropt, 'p')
- call writefile(['let g:found_one = 1'], colordirrun . '/one.vim')
- call writefile(['let g:found_two = 1'], colordirstart . '/two.vim')
- call writefile(['let g:found_three = 1'], colordiropt . '/three.vim')
- exe 'set rtp=' . &packpath . '/runtime'
-
- colorscheme one
- call assert_equal(1, g:found_one)
- colorscheme two
- call assert_equal(1, g:found_two)
- colorscheme three
- call assert_equal(1, g:found_three)
- endfunc
-
- func Test_runtime()
- let rundir = &packpath . '/runtime/extra'
- let startdir = &packpath . '/pack/mine/start/foo/extra'
- let optdir = &packpath . '/pack/mine/opt/bar/extra'
- call mkdir(rundir, 'p')
- call mkdir(startdir, 'p')
- call mkdir(optdir, 'p')
- call writefile(['let g:sequence .= "run"'], rundir . '/bar.vim')
- call writefile(['let g:sequence .= "start"'], startdir . '/bar.vim')
- call writefile(['let g:sequence .= "foostart"'], startdir . '/foo.vim')
- call writefile(['let g:sequence .= "opt"'], optdir . '/bar.vim')
- call writefile(['let g:sequence .= "xxxopt"'], optdir . '/xxx.vim')
- exe 'set rtp=' . &packpath . '/runtime'
-
- let g:sequence = ''
- runtime extra/bar.vim
- call assert_equal('run', g:sequence)
- let g:sequence = ''
- runtime START extra/bar.vim
- call assert_equal('start', g:sequence)
- let g:sequence = ''
- runtime OPT extra/bar.vim
- call assert_equal('opt', g:sequence)
- let g:sequence = ''
- runtime PACK extra/bar.vim
- call assert_equal('start', g:sequence)
- let g:sequence = ''
- runtime! PACK extra/bar.vim
- call assert_equal('startopt', g:sequence)
- let g:sequence = ''
- runtime PACK extra/xxx.vim
- call assert_equal('xxxopt', g:sequence)
-
- let g:sequence = ''
- runtime ALL extra/bar.vim
- call assert_equal('run', g:sequence)
- let g:sequence = ''
- runtime ALL extra/foo.vim
- call assert_equal('foostart', g:sequence)
- let g:sequence = ''
- runtime! ALL extra/xxx.vim
- call assert_equal('xxxopt', g:sequence)
- let g:sequence = ''
- runtime! ALL extra/bar.vim
- call assert_equal('runstartopt', g:sequence)
- endfunc
- ]=])
- call('SetUp')
- end)
-
- after_each(function()
- call('TearDown')
- end)
-
- it('is working', function()
- call('Test_packadd')
- expected_empty()
- end)
-
- it('works with packadd!', function()
- call('Test_packadd_noload')
- expected_empty()
- end)
-
- it('works with symlinks', function()
- call('Test_packadd_symlink_dir')
- expected_empty()
- end)
-
- it('works with :packloadall', function()
- call('Test_packloadall')
- expected_empty()
- end)
-
- it('works with helptags', function()
- call('Test_helptags')
- expected_empty()
- end)
-
- it('works with colorschemes', function()
- call('Test_colorscheme')
- expected_empty()
- end)
-
- it('works with :runtime [what]', function()
- call('Test_runtime')
- expected_empty()
- end)
-
- it('loads packages from "start" directory', function()
- call('Test_packadd_start')
- expected_empty()
- end)
-
- describe('command line completion', function()
- local Screen = require('test.functional.ui.screen')
- local screen
-
- before_each(function()
- screen = Screen.new(30, 5)
- screen:attach()
- screen:set_default_attr_ids({
- [0] = {bold=true, foreground=Screen.colors.Blue},
- [1] = {
- foreground = Screen.colors.Black,
- background = Screen.colors.Yellow,
- },
- [2] = {bold = true, reverse = true}
- })
-
- command([[let optdir1 = &packpath . '/pack/mine/opt']])
- command([[let optdir2 = &packpath . '/pack/candidate/opt']])
- command([[call mkdir(optdir1 . '/pluginA', 'p')]])
- command([[call mkdir(optdir1 . '/pluginC', 'p')]])
- command([[call mkdir(optdir2 . '/pluginB', 'p')]])
- command([[call mkdir(optdir2 . '/pluginC', 'p')]])
- end)
-
- it('works', function()
- feed(':packadd <Tab>')
- screen:expect([=[
- |
- {0:~ }|
- {0:~ }|
- {1:pluginA}{2: pluginB pluginC }|
- :packadd pluginA^ |
- ]=])
- feed('<Tab>')
- screen:expect([=[
- |
- {0:~ }|
- {0:~ }|
- {2:pluginA }{1:pluginB}{2: pluginC }|
- :packadd pluginB^ |
- ]=])
- feed('<Tab>')
- screen:expect([=[
- |
- {0:~ }|
- {0:~ }|
- {2:pluginA pluginB }{1:pluginC}{2: }|
- :packadd pluginC^ |
- ]=])
- feed('<Tab>')
- screen:expect([=[
- |
- {0:~ }|
- {0:~ }|
- {2:pluginA pluginB pluginC }|
- :packadd ^ |
- ]=])
- end)
-
- it('works for colorschemes', function()
- source([[
- let colordirrun = &packpath . '/runtime/colors'
- let colordirstart = &packpath . '/pack/mine/start/foo/colors'
- let colordiropt = &packpath . '/pack/mine/opt/bar/colors'
- call mkdir(colordirrun, 'p')
- call mkdir(colordirstart, 'p')
- call mkdir(colordiropt, 'p')
- call writefile(['let g:found_one = 1'], colordirrun . '/one.vim')
- call writefile(['let g:found_two = 1'], colordirstart . '/two.vim')
- call writefile(['let g:found_three = 1'], colordiropt . '/three.vim')
- exe 'set rtp=' . &packpath . '/runtime']])
-
- feed(':colorscheme <Tab>')
- screen:expect([=[
- |
- {0:~ }|
- {0:~ }|
- {1:one}{2: three two }|
- :colorscheme one^ |
- ]=])
- feed('<Tab>')
- screen:expect([=[
- |
- {0:~ }|
- {0:~ }|
- {2:one }{1:three}{2: two }|
- :colorscheme three^ |
- ]=])
- feed('<Tab>')
- screen:expect([=[
- |
- {0:~ }|
- {0:~ }|
- {2:one three }{1:two}{2: }|
- :colorscheme two^ |
- ]=])
- feed('<Tab>')
- screen:expect([=[
- |
- {0:~ }|
- {0:~ }|
- {2:one three two }|
- :colorscheme ^ |
- ]=])
- end)
- end)
-end)
diff --git a/test/functional/legacy/prompt_buffer_spec.lua b/test/functional/legacy/prompt_buffer_spec.lua
index 47eca19de3..63338b8dba 100644
--- a/test/functional/legacy/prompt_buffer_spec.lua
+++ b/test/functional/legacy/prompt_buffer_spec.lua
@@ -23,7 +23,7 @@ describe('prompt buffer', function()
close
else
call append(line("$") - 1, 'Command: "' . a:text . '"')
- set nomodfied
+ set nomodified
call timer_start(20, {id -> TimerFunc(a:text)})
endif
endfunc
@@ -33,7 +33,7 @@ describe('prompt buffer', function()
endfunc
func SwitchWindows()
- call timer_start(0, {-> execute("wincmd p|wincmd p", "")})
+ call timer_start(0, {-> execute("wincmd p", "")})
endfunc
]])
feed_command("set noshowmode | set laststatus=0")
@@ -187,7 +187,19 @@ describe('prompt buffer', function()
-- INSERT -- |
]])
feed("<C-O>:call SwitchWindows()<CR>")
- poke_eventloop()
+ screen:expect{grid=[[
+ cmd: |
+ ~ |
+ ~ |
+ ~ |
+ [Prompt] [+] |
+ ^other buffer |
+ ~ |
+ ~ |
+ ~ |
+ |
+ ]]}
+ feed("<C-O>:call SwitchWindows()<CR>")
screen:expect([[
cmd: ^ |
~ |
@@ -201,7 +213,6 @@ describe('prompt buffer', function()
-- INSERT -- |
]])
feed("<Esc>")
- poke_eventloop()
screen:expect([[
cmd:^ |
~ |
diff --git a/test/functional/legacy/put_spec.lua b/test/functional/legacy/put_spec.lua
new file mode 100644
index 0000000000..3ddf65490e
--- /dev/null
+++ b/test/functional/legacy/put_spec.lua
@@ -0,0 +1,45 @@
+local helpers = require('test.functional.helpers')(after_each)
+local clear = helpers.clear
+local exec_lua = helpers.exec_lua
+local meths = helpers.meths
+local source = helpers.source
+local eq = helpers.eq
+
+local function sizeoflong()
+ if not exec_lua('return pcall(require, "ffi")') then
+ pending('missing LuaJIT FFI')
+ end
+ return exec_lua('return require("ffi").sizeof(require("ffi").typeof("long"))')
+end
+
+describe('put', function()
+ before_each(clear)
+ after_each(function() eq({}, meths.get_vvar('errors')) end)
+
+ it('very large count 64-bit', function()
+ if sizeoflong() < 8 then
+ pending('Skipped: only works with 64 bit long ints')
+ end
+
+ source [[
+ new
+ let @" = repeat('x', 100)
+ call assert_fails('norm 999999999p', 'E1240:')
+ bwipe!
+ ]]
+ end)
+
+ it('very large count (visual block) 64-bit', function()
+ if sizeoflong() < 8 then
+ pending('Skipped: only works with 64 bit long ints')
+ end
+
+ source [[
+ new
+ call setline(1, repeat('x', 100))
+ exe "norm \<C-V>$y"
+ call assert_fails('norm 999999999p', 'E1240:')
+ bwipe!
+ ]]
+ end)
+end)
diff --git a/test/functional/legacy/search_spec.lua b/test/functional/legacy/search_spec.lua
index 4ed08881de..67991f5d48 100644
--- a/test/functional/legacy/search_spec.lua
+++ b/test/functional/legacy/search_spec.lua
@@ -7,6 +7,7 @@ local eval = helpers.eval
local feed = helpers.feed
local funcs = helpers.funcs
local poke_eventloop = helpers.poke_eventloop
+local exec = helpers.exec
describe('search cmdline', function()
local screen
@@ -640,3 +641,34 @@ describe('search cmdline', function()
feed('<esc>')
end)
end)
+
+describe('Search highlight', function()
+ before_each(clear)
+ it('Search highlight is combined with Visual highlight vim-patch:8.2.2797', function()
+ local screen = Screen.new(40, 6)
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ [2] = {bold = true}, -- ModeMsg, Search
+ [3] = {background = Screen.colors.LightGrey}, -- Visual
+ [4] = {background = Screen.colors.Yellow, bold = true}, -- Search
+ [5] = {background = Screen.colors.LightGrey, bold = true}, -- Visual + Search
+ })
+ screen:attach()
+ exec([[
+ set hlsearch noincsearch
+ call setline(1, repeat(["xxx yyy zzz"], 3))
+ hi Search gui=bold
+ /yyy
+ call cursor(1, 6)
+ ]])
+ feed('vjj')
+ screen:expect([[
+ xxx {4:y}{5:yy}{3: zzz} |
+ {3:xxx }{5:yyy}{3: zzz} |
+ {3:xxx }{5:y}{4:^yy} zzz |
+ {1:~ }|
+ {1:~ }|
+ {2:-- VISUAL --} |
+ ]])
+ end)
+end)
diff --git a/test/functional/legacy/search_stat_spec.lua b/test/functional/legacy/search_stat_spec.lua
new file mode 100644
index 0000000000..c2ca393a56
--- /dev/null
+++ b/test/functional/legacy/search_stat_spec.lua
@@ -0,0 +1,184 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local clear, feed, exec, command = helpers.clear, helpers.feed, helpers.exec, helpers.command
+local poke_eventloop = helpers.poke_eventloop
+
+describe('search stat', function()
+ local screen
+ before_each(function()
+ clear()
+ screen = Screen.new(30, 10)
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ [2] = {background = Screen.colors.Yellow}, -- Search
+ [3] = {foreground = Screen.colors.Blue4, background = Screen.colors.LightGrey}, -- Folded
+ [4] = {reverse = true}, -- IncSearch, TabLineFill
+ })
+ screen:attach()
+ end)
+
+ it('right spacing with silent mapping vim-patch:8.1.1970', function()
+ exec([[
+ set shortmess-=S
+ " Append 50 lines with text to search for, "foobar" appears 20 times
+ call append(0, repeat(['foobar', 'foo', 'fooooobar', 'foba', 'foobar'], 20))
+ call setline(2, 'find this')
+ call setline(70, 'find this')
+ nnoremap n n
+ let @/ = 'find this'
+ call cursor(1,1)
+ norm n
+ ]])
+ screen:expect([[
+ foobar |
+ {2:^find this} |
+ fooooobar |
+ foba |
+ foobar |
+ foobar |
+ foo |
+ fooooobar |
+ foba |
+ /find this [1/2] |
+ ]])
+ command('nnoremap <silent> n n')
+ feed('gg0n')
+ screen:expect([[
+ foobar |
+ {2:^find this} |
+ fooooobar |
+ foba |
+ foobar |
+ foobar |
+ foo |
+ fooooobar |
+ foba |
+ [1/2] |
+ ]])
+ end)
+
+ it('when only match is in fold vim-patch:8.2.0840', function()
+ exec([[
+ set shortmess-=S
+ setl foldenable foldmethod=indent foldopen-=search
+ call append(0, ['if', "\tfoo", "\tfoo", 'endif'])
+ let @/ = 'foo'
+ call cursor(1,1)
+ norm n
+ ]])
+ screen:expect([[
+ if |
+ {3:^+-- 2 lines: foo·············}|
+ endif |
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ /foo [1/2] |
+ ]])
+ feed('n')
+ poke_eventloop()
+ screen:expect_unchanged()
+ feed('n')
+ poke_eventloop()
+ screen:expect_unchanged()
+ end)
+
+ it('is cleared by gd and gD vim-patch:8.2.3583', function()
+ exec([[
+ call setline(1, ['int cat;', 'int dog;', 'cat = dog;'])
+ set shortmess-=S
+ set hlsearch
+ ]])
+ feed('/dog<CR>')
+ screen:expect([[
+ int cat; |
+ int {2:^dog}; |
+ cat = {2:dog}; |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ /dog [1/2] |
+ ]])
+ feed('G0gD')
+ screen:expect([[
+ int {2:^cat}; |
+ int dog; |
+ {2:cat} = dog; |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ end)
+
+ it('is not broken by calling searchcount() in tabline vim-patch:8.2.4378', function()
+ exec([[
+ call setline(1, ['abc--c', '--------abc', '--abc'])
+ set hlsearch
+ set incsearch
+ set showtabline=2
+
+ function MyTabLine()
+ try
+ let a=searchcount(#{recompute: 1, maxcount: -1})
+ return a.current .. '/' .. a.total
+ catch
+ return ''
+ endtry
+ endfunction
+
+ set tabline=%!MyTabLine()
+ ]])
+
+ feed('/abc')
+ screen:expect([[
+ {4: }|
+ {2:abc}--c |
+ --------{4:abc} |
+ --{2:abc} |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ /abc^ |
+ ]])
+
+ feed('<C-G>')
+ screen:expect([[
+ {4:3/3 }|
+ {2:abc}--c |
+ --------{2:abc} |
+ --{4:abc} |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ /abc^ |
+ ]])
+
+ feed('<C-G>')
+ screen:expect([[
+ {4:1/3 }|
+ {4:abc}--c |
+ --------{2:abc} |
+ --{2:abc} |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ /abc^ |
+ ]])
+ end)
+end)
diff --git a/test/functional/legacy/searchpos_spec.lua b/test/functional/legacy/searchpos_spec.lua
deleted file mode 100644
index fc18341c38..0000000000
--- a/test/functional/legacy/searchpos_spec.lua
+++ /dev/null
@@ -1,35 +0,0 @@
-local helpers = require('test.functional.helpers')(after_each)
-local call = helpers.call
-local clear = helpers.clear
-local command = helpers.command
-local eq = helpers.eq
-local eval = helpers.eval
-local insert = helpers.insert
-
-describe('searchpos', function()
- before_each(clear)
-
- it('is working', function()
- insert([[
- 1a3
- 123xyz]])
-
- call('cursor', 1, 1)
- eq({1, 1, 2}, eval([[searchpos('\%(\([a-z]\)\|\_.\)\{-}xyz', 'pcW')]]))
- call('cursor', 1, 2)
- eq({2, 1, 1}, eval([['\%(\([a-z]\)\|\_.\)\{-}xyz'->searchpos('pcW')]]))
-
- command('set cpo-=c')
- call('cursor', 1, 2)
- eq({1, 2, 2}, eval([[searchpos('\%(\([a-z]\)\|\_.\)\{-}xyz', 'pcW')]]))
- call('cursor', 1, 3)
- eq({1, 3, 1}, eval([[searchpos('\%(\([a-z]\)\|\_.\)\{-}xyz', 'pcW')]]))
-
- -- Now with \zs, first match is in column 0, "a" is matched.
- call('cursor', 1, 3)
- eq({2, 4, 2}, eval([[searchpos('\%(\([a-z]\)\|\_.\)\{-}\zsxyz', 'pcW')]]))
- -- With z flag start at cursor column, don't see the "a".
- call('cursor', 1, 3)
- eq({2, 4, 1}, eval([[searchpos('\%(\([a-z]\)\|\_.\)\{-}\zsxyz', 'pcWz')]]))
- end)
-end)
diff --git a/test/functional/legacy/set_spec.lua b/test/functional/legacy/set_spec.lua
deleted file mode 100644
index deb268b1e8..0000000000
--- a/test/functional/legacy/set_spec.lua
+++ /dev/null
@@ -1,30 +0,0 @@
--- Tests for :set
-
-local helpers = require('test.functional.helpers')(after_each)
-local clear, command, eval, eq =
- helpers.clear, helpers.command, helpers.eval, helpers.eq
-
-describe(':set', function()
- before_each(clear)
-
- it('handles backslash properly', function()
- command('set iskeyword=a,b,c')
- command('set iskeyword+=d')
- eq('a,b,c,d', eval('&iskeyword'))
-
- command([[set iskeyword+=\\,e]])
- eq([[a,b,c,d,\,e]], eval('&iskeyword'))
-
- command('set iskeyword-=e')
- eq([[a,b,c,d,\]], eval('&iskeyword'))
-
- command([[set iskeyword-=\]])
- eq('a,b,c,d', eval('&iskeyword'))
- end)
-
- it('recognizes a trailing comma with +=', function()
- command('set wildignore=*.png,')
- command('set wildignore+=*.jpg')
- eq('*.png,*.jpg', eval('&wildignore'))
- end)
-end)
diff --git a/test/functional/legacy/statusline_spec.lua b/test/functional/legacy/statusline_spec.lua
new file mode 100644
index 0000000000..e2b30a7c82
--- /dev/null
+++ b/test/functional/legacy/statusline_spec.lua
@@ -0,0 +1,71 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local clear = helpers.clear
+local exec = helpers.exec
+local feed = helpers.feed
+
+before_each(clear)
+
+describe('statusline', function()
+ local screen
+
+ before_each(function()
+ screen = Screen.new(50, 7)
+ screen:attach()
+ end)
+
+ it('is updated in cmdline mode when using window-local statusline vim-patch:8.2.2737', function()
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ [2] = {bold = true, reverse = true}, -- StatusLine
+ [3] = {reverse = true}, -- StatusLineNC
+ })
+ exec([[
+ setlocal statusline=-%{mode()}-
+ split
+ setlocal statusline=+%{mode()}+
+ ]])
+ screen:expect([[
+ ^ |
+ {1:~ }|
+ {2:+n+ }|
+ |
+ {1:~ }|
+ {3:-n- }|
+ |
+ ]])
+ feed(':')
+ screen:expect([[
+ |
+ {1:~ }|
+ {2:+c+ }|
+ |
+ {1:~ }|
+ {3:-c- }|
+ :^ |
+ ]])
+ end)
+
+ it('truncated item does not cause off-by-one highlight vim-patch:8.2.4929', function()
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ [2] = {foreground = Screen.colors.Blue}, -- User1
+ [3] = {background = Screen.colors.Red, foreground = Screen.colors.White}, -- User2
+ })
+ exec([[
+ set laststatus=2
+ hi! link User1 Directory
+ hi! link User2 ErrorMsg
+ set statusline=%.5(%1*ABC%2*DEF%1*GHI%)
+ ]])
+ screen:expect([[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {3:<F}{2:GHI }|
+ |
+ ]])
+ end)
+end)
diff --git a/test/functional/legacy/utf8_spec.lua b/test/functional/legacy/utf8_spec.lua
index 8b5fc02d11..67a4bec4c5 100644
--- a/test/functional/legacy/utf8_spec.lua
+++ b/test/functional/legacy/utf8_spec.lua
@@ -28,7 +28,7 @@ describe('utf8', function()
expect([[
start:
axaa
- xあああ
+ xあああ
bxbb]])
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/legacy/wordcount_spec.lua b/test/functional/legacy/wordcount_spec.lua
index 826743b0ca..21f96628c0 100644
--- a/test/functional/legacy/wordcount_spec.lua
+++ b/test/functional/legacy/wordcount_spec.lua
@@ -50,44 +50,44 @@ describe('wordcount', function()
]=])
-- Test 1: empty window
- eq(eval('DoRecordWin()'),
- eval([=[
+ eq(eval([=[
[[''], {'chars': 0, 'cursor_chars': 0, 'words': 0, 'cursor_words': 0, 'bytes': 0, 'cursor_bytes': 0}]
- ]=])
+ ]=]),
+ eval('DoRecordWin()')
)
-- Test 2: some words, cursor at start
command([[call PutInWindow('one two three')]])
- eq(eval('DoRecordWin([1, 1, 0])'),
- eval([=[
+ eq(eval([=[
[['', 'one two three'], {'chars': 15, 'cursor_chars': 1, 'words': 3, 'cursor_words': 0, 'bytes': 15, 'cursor_bytes': 1}]
- ]=])
+ ]=]),
+ eval('DoRecordWin([1, 1, 0])')
)
-- Test 3: some words, cursor at end
command([[call PutInWindow('one two three')]])
- eq(eval('DoRecordWin([2, 99, 0])'),
- eval([=[
+ eq(eval([=[
[['', 'one two three'], {'chars': 15, 'cursor_chars': 14, 'words': 3, 'cursor_words': 3, 'bytes': 15, 'cursor_bytes': 14}]
- ]=])
+ ]=]),
+ eval('DoRecordWin([2, 99, 0])')
)
-- Test 4: some words, cursor at end, ve=all
command('set ve=all')
command([[call PutInWindow('one two three')]])
- eq(eval('DoRecordWin([2,99,0])'),
- eval([=[
+ eq(eval([=[
[['', 'one two three'], {'chars': 15, 'cursor_chars': 15, 'words': 3, 'cursor_words': 3, 'bytes': 15, 'cursor_bytes': 15}]
- ]=])
+ ]=]),
+ eval('DoRecordWin([2,99,0])')
)
command('set ve=')
-- Test 5: several lines with words
command([=[call PutInWindow(['one two three', 'one two three', 'one two three'])]=])
- eq(eval('DoRecordWin([4,99,0])'),
- eval([=[
+ eq(eval([=[
[['', 'one two three', 'one two three', 'one two three'], {'chars': 43, 'cursor_chars': 42, 'words': 9, 'cursor_words': 9, 'bytes': 43, 'cursor_bytes': 42}]
- ]=])
+ ]=]),
+ eval('DoRecordWin([4,99,0])')
)
-- Test 6: one line with BOM set
@@ -95,10 +95,10 @@ describe('wordcount', function()
command('wincmd k')
command('set bomb')
command('wincmd j')
- eq(eval('DoRecordWin([2,99,0])'),
- eval([=[
+ eq(eval([=[
[['', 'one two three'], {'chars': 15, 'cursor_chars': 14, 'words': 3, 'cursor_words': 3, 'bytes': 18, 'cursor_bytes': 14}]
- ]=])
+ ]=]),
+ eval('DoRecordWin([2,99,0])')
)
command('wincmd k')
command('set nobomb')
@@ -106,18 +106,18 @@ describe('wordcount', function()
-- Test 7: one line with multibyte words
command([=[call PutInWindow(['Äne M¤ne Müh'])]=])
- eq(eval('DoRecordWin([2,99,0])'),
- eval([=[
+ eq(eval([=[
[['', 'Äne M¤ne Müh'], {'chars': 14, 'cursor_chars': 13, 'words': 3, 'cursor_words': 3, 'bytes': 17, 'cursor_bytes': 16}]
- ]=])
+ ]=]),
+ eval('DoRecordWin([2,99,0])')
)
-- Test 8: several lines with multibyte words
command([=[call PutInWindow(['Äne M¤ne Müh', 'und raus bist dü!'])]=])
- eq(eval('DoRecordWin([3,99,0])'),
- eval([=[
+ eq(eval([=[
[['', 'Äne M¤ne Müh', 'und raus bist dü!'], {'chars': 32, 'cursor_chars': 31, 'words': 7, 'cursor_words': 7, 'bytes': 36, 'cursor_bytes': 35}]
- ]=])
+ ]=]),
+ eval('DoRecordWin([3,99,0])')
)
-- Test 9: visual mode, complete buffer
@@ -131,10 +131,10 @@ describe('wordcount', function()
command('set stl= ls=1')
command('let log=DoRecordWin([3,99,0])')
command('let log[1]=g:visual_stat')
- eq(eval('log'),
- eval([=[
+ eq(eval([=[
[['', 'Äne M¤ne Müh', 'und raus bist dü!'], {'chars': 32, 'words': 7, 'bytes': 36, 'visual_chars': 32, 'visual_words': 7, 'visual_bytes': 36}]
- ]=])
+ ]=]),
+ eval('log')
)
-- Test 10: visual mode (empty)
@@ -148,10 +148,10 @@ describe('wordcount', function()
command('set stl= ls=1')
command('let log=DoRecordWin([3,99,0])')
command('let log[1]=g:visual_stat')
- eq(eval('log'),
- eval([=[
+ eq(eval([=[
[['', 'Äne M¤ne Müh', 'und raus bist dü!'], {'chars': 32, 'words': 7, 'bytes': 36, 'visual_chars': 1, 'visual_words': 0, 'visual_bytes': 1}]
- ]=])
+ ]=]),
+ eval('log')
)
-- Test 11: visual mode, single line
@@ -165,10 +165,10 @@ describe('wordcount', function()
command('set stl= ls=1')
command('let log=DoRecordWin([3,99,0])')
command('let log[1]=g:visual_stat')
- eq(eval('log'),
- eval([=[
+ eq(eval([=[
[['', 'Äne M¤ne Müh', 'und raus bist dü!'], {'chars': 32, 'words': 7, 'bytes': 36, 'visual_chars': 13, 'visual_words': 3, 'visual_bytes': 16}]
- ]=])
+ ]=]),
+ eval('log')
)
end)
end)
diff --git a/test/functional/lua/api_spec.lua b/test/functional/lua/api_spec.lua
index cb37fb9a1c..f173a15d32 100644
--- a/test/functional/lua/api_spec.lua
+++ b/test/functional/lua/api_spec.lua
@@ -102,6 +102,13 @@ describe('luaeval(vim.api.…)', function()
eq(false, funcs.luaeval('vim.api.nvim__id(false)'))
eq(NIL, funcs.luaeval('vim.api.nvim__id(nil)'))
+ -- API strings from Blobs can work as NUL-terminated C strings
+ eq('Vim(call):E5555: API call: Vim:E15: Invalid expression: ',
+ exc_exec('call nvim_eval(v:_null_blob)'))
+ eq('Vim(call):E5555: API call: Vim:E15: Invalid expression: ',
+ exc_exec('call nvim_eval(0z)'))
+ eq(1, eval('nvim_eval(0z31)'))
+
eq(0, eval([[type(luaeval('vim.api.nvim__id(1)'))]]))
eq(1, eval([[type(luaeval('vim.api.nvim__id("1")'))]]))
eq(3, eval([[type(luaeval('vim.api.nvim__id({1})'))]]))
diff --git a/test/functional/lua/buffer_updates_spec.lua b/test/functional/lua/buffer_updates_spec.lua
index c83a50b78b..10de45274c 100644
--- a/test/functional/lua/buffer_updates_spec.lua
+++ b/test/functional/lua/buffer_updates_spec.lua
@@ -252,9 +252,8 @@ describe('lua buffer event callbacks: on_lines', function()
eq(2, meths.win_get_cursor(0)[1])
end)
- it('does not SEGFAULT when calling win_findbuf in on_detach', function()
-
- exec_lua[[
+ it('does not SEGFAULT when accessing window buffer info in on_detach #14998', function()
+ local code = [[
local buf = vim.api.nvim_create_buf(false, false)
vim.cmd"split"
@@ -262,13 +261,19 @@ describe('lua buffer event callbacks: on_lines', function()
vim.api.nvim_buf_attach(buf, false, {
on_detach = function(_, buf)
+ vim.fn.tabpagebuflist()
vim.fn.win_findbuf(buf)
end
})
]]
+ exec_lua(code)
command("q!")
helpers.assert_alive()
+
+ exec_lua(code)
+ command("bd!")
+ helpers.assert_alive()
end)
it('#12718 lnume', function()
@@ -612,7 +617,15 @@ describe('lua: nvim_buf_attach on_bytes', function()
}
feed("<esc>")
- -- replacing with escaped characters
+ -- replacing with expression register
+ feed([[:%s/b/\=5+5]])
+ check_events {
+ { "test1", "bytes", 1, 3, 0, 1, 1, 0, 1, 1, 0, 2, 2 };
+ { "test1", "bytes", 1, 5, 0, 1, 1, 0, 2, 2, 0, 1, 1 };
+ }
+
+ feed("<esc>")
+ -- replacing with backslash
feed([[:%s/b/\\]])
check_events {
{ "test1", "bytes", 1, 3, 0, 1, 1, 0, 1, 1, 0, 1, 1 };
@@ -620,8 +633,24 @@ describe('lua: nvim_buf_attach on_bytes', function()
}
feed("<esc>")
- -- replacing with expression register
- feed([[:%s/b/\=5+5]])
+ -- replacing with backslash from expression register
+ feed([[:%s/b/\='\']])
+ check_events {
+ { "test1", "bytes", 1, 3, 0, 1, 1, 0, 1, 1, 0, 1, 1 };
+ { "test1", "bytes", 1, 5, 0, 1, 1, 0, 1, 1, 0, 1, 1 };
+ }
+
+ feed("<esc>")
+ -- replacing with backslash followed by another character
+ feed([[:%s/b/\\!]])
+ check_events {
+ { "test1", "bytes", 1, 3, 0, 1, 1, 0, 1, 1, 0, 2, 2 };
+ { "test1", "bytes", 1, 5, 0, 1, 1, 0, 2, 2, 0, 1, 1 };
+ }
+
+ feed("<esc>")
+ -- replacing with backslash followed by another character from expression register
+ feed([[:%s/b/\='\!']])
check_events {
{ "test1", "bytes", 1, 3, 0, 1, 1, 0, 1, 1, 0, 2, 2 };
{ "test1", "bytes", 1, 5, 0, 1, 1, 0, 2, 2, 0, 1, 1 };
@@ -1104,6 +1133,15 @@ describe('lua: nvim_buf_attach on_bytes', function()
check_events { }
end)
+ it("works with accepting spell suggestions", function()
+ local check_events = setup_eventcheck(verify, {"hallo"})
+
+ feed("gg0z=4<cr><cr>") -- accepts 'Hello'
+ check_events {
+ { "test1", "bytes", 1, 3, 0, 0, 0, 0, 2, 2, 0, 2, 2 };
+ }
+ end)
+
local function test_lockmarks(mode)
local description = (mode ~= "") and mode or "(baseline)"
it("test_lockmarks " .. description .. " %delete _", function()
diff --git a/test/functional/lua/command_line_completion_spec.lua b/test/functional/lua/command_line_completion_spec.lua
index 3ba7e1589f..3a5966755e 100644
--- a/test/functional/lua/command_line_completion_spec.lua
+++ b/test/functional/lua/command_line_completion_spec.lua
@@ -106,6 +106,14 @@ describe('nlua_expand_pat', function()
)
end)
+ it('should work with lazy submodules of "vim" global', function()
+ eq({{ 'inspect' }, 4 },
+ get_completions('vim.inspec'))
+
+ eq({{ 'set' }, 11 },
+ get_completions('vim.keymap.se'))
+ end)
+
it('should be able to interpolate globals', function()
eq(
{{
diff --git a/test/functional/lua/diagnostic_spec.lua b/test/functional/lua/diagnostic_spec.lua
index 7d260f2e29..f9647f5b6a 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)
@@ -729,6 +729,19 @@ describe('vim.diagnostic', function()
return vim.diagnostic.get_next_pos { namespace = diagnostic_ns }
]])
end)
+
+ it('works with diagnostics before the start of the line', function()
+ eq({4, 0}, exec_lua [[
+ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
+ make_error('Diagnostic #1', 3, 9001, 3, 9001),
+ make_error('Diagnostic #2', 4, -1, 4, -1),
+ })
+ vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
+ vim.api.nvim_win_set_cursor(0, {1, 1})
+ vim.diagnostic.goto_next { float = false }
+ return vim.diagnostic.get_next_pos { namespace = diagnostic_ns }
+ ]])
+end)
end)
describe('get_prev_pos()', function()
@@ -787,7 +800,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 +811,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 +833,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),
})
@@ -1939,24 +1952,31 @@ describe('vim.diagnostic', function()
end)
it('triggers the autocommand when diagnostics are set', function()
- eq(1, exec_lua [[
+ eq(true, exec_lua [[
+ -- Set a different buffer as current to test that <abuf> is being set properly in
+ -- DiagnosticChanged callbacks
+ local tmp = vim.api.nvim_create_buf(false, true)
+ vim.api.nvim_set_current_buf(tmp)
+
vim.g.diagnostic_autocmd_triggered = 0
- vim.cmd('autocmd DiagnosticChanged * let g:diagnostic_autocmd_triggered = 1')
+ vim.cmd('autocmd DiagnosticChanged * let g:diagnostic_autocmd_triggered = +expand("<abuf>")')
vim.api.nvim_buf_set_name(diagnostic_bufnr, "test | test")
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
make_error('Diagnostic', 0, 0, 0, 0)
})
- return vim.g.diagnostic_autocmd_triggered
+ return vim.g.diagnostic_autocmd_triggered == diagnostic_bufnr
]])
end)
it('triggers the autocommand when diagnostics are cleared', function()
- eq(1, exec_lua [[
+ eq(true, exec_lua [[
+ local tmp = vim.api.nvim_create_buf(false, true)
+ vim.api.nvim_set_current_buf(tmp)
vim.g.diagnostic_autocmd_triggered = 0
- vim.cmd('autocmd DiagnosticChanged * let g:diagnostic_autocmd_triggered = 1')
+ vim.cmd('autocmd DiagnosticChanged * let g:diagnostic_autocmd_triggered = +expand("<abuf>")')
vim.api.nvim_buf_set_name(diagnostic_bufnr, "test | test")
vim.diagnostic.reset(diagnostic_ns, diagnostic_bufnr)
- return vim.g.diagnostic_autocmd_triggered
+ return vim.g.diagnostic_autocmd_triggered == diagnostic_bufnr
]])
end)
end)
diff --git a/test/functional/lua/filetype_spec.lua b/test/functional/lua/filetype_spec.lua
index 1b2cb61cc2..be57b2db31 100644
--- a/test/functional/lua/filetype_spec.lua
+++ b/test/functional/lua/filetype_spec.lua
@@ -3,6 +3,7 @@ local exec_lua = helpers.exec_lua
local eq = helpers.eq
local clear = helpers.clear
local pathroot = helpers.pathroot
+local command = helpers.command
local root = pathroot()
@@ -23,8 +24,7 @@ describe('vim.filetype', function()
rs = 'radicalscript',
},
})
- vim.filetype.match('main.rs')
- return vim.bo.filetype
+ return vim.filetype.match({ filename = 'main.rs' })
]])
end)
@@ -38,8 +38,7 @@ describe('vim.filetype', function()
['main.rs'] = 'somethingelse',
},
})
- vim.filetype.match('main.rs')
- return vim.bo.filetype
+ return vim.filetype.match({ filename = 'main.rs' })
]])
end)
@@ -50,8 +49,7 @@ describe('vim.filetype', function()
['s_O_m_e_F_i_l_e'] = 'nim',
},
})
- vim.filetype.match('s_O_m_e_F_i_l_e')
- return vim.bo.filetype
+ return vim.filetype.match({ filename = 's_O_m_e_F_i_l_e' })
]])
eq('dosini', exec_lua([[
@@ -62,25 +60,26 @@ describe('vim.filetype', function()
[root .. '/.config/fun/config'] = 'dosini',
},
})
- vim.filetype.match(root .. '/.config/fun/config')
- return vim.bo.filetype
+ return vim.filetype.match({ filename = root .. '/.config/fun/config' })
]], root))
end)
it('works with patterns', function()
eq('markdown', exec_lua([[
local root = ...
+ vim.env.HOME = '/a-funky+home%dir'
vim.filetype.add({
pattern = {
- [root .. '/blog/.*%.txt'] = 'markdown',
+ ['~/blog/.*%.txt'] = 'markdown',
}
})
- vim.filetype.match(root .. '/blog/why_neovim_is_awesome.txt')
- return vim.bo.filetype
+ return vim.filetype.match({ filename = '~/blog/why_neovim_is_awesome.txt' })
]], root))
end)
it('works with functions', function()
+ command('new')
+ command('file relevant_to_me')
eq('foss', exec_lua [[
vim.filetype.add({
pattern = {
@@ -91,8 +90,7 @@ describe('vim.filetype', function()
end,
}
})
- vim.filetype.match('relevant_to_me')
- return vim.bo.filetype
+ return vim.filetype.match({ buf = 0 })
]])
end)
end)
diff --git a/test/functional/lua/fs_spec.lua b/test/functional/lua/fs_spec.lua
new file mode 100644
index 0000000000..2bcc84db0f
--- /dev/null
+++ b/test/functional/lua/fs_spec.lua
@@ -0,0 +1,101 @@
+local helpers = require('test.functional.helpers')(after_each)
+
+local clear = helpers.clear
+local exec_lua = helpers.exec_lua
+local eq = helpers.eq
+local mkdir_p = helpers.mkdir_p
+local rmdir = helpers.rmdir
+local nvim_dir = helpers.nvim_dir
+local test_build_dir = helpers.test_build_dir
+local iswin = helpers.iswin
+local nvim_prog = helpers.nvim_prog
+
+local nvim_prog_basename = iswin() and 'nvim.exe' or 'nvim'
+
+before_each(clear)
+
+describe('vim.fs', function()
+ describe('parents()', function()
+ it('works', function()
+ local test_dir = nvim_dir .. '/test'
+ mkdir_p(test_dir)
+ local dirs = exec_lua([[
+ local test_dir, test_build_dir = ...
+ local dirs = {}
+ for dir in vim.fs.parents(test_dir .. "/foo.txt") do
+ dirs[#dirs + 1] = dir
+ if dir == test_build_dir then
+ break
+ end
+ end
+ return dirs
+ ]], test_dir, test_build_dir)
+ eq({test_dir, nvim_dir, test_build_dir}, dirs)
+ rmdir(test_dir)
+ end)
+ end)
+
+ describe('dirname()', function()
+ it('works', function()
+ eq(test_build_dir, exec_lua([[
+ local nvim_dir = ...
+ return vim.fs.dirname(nvim_dir)
+ ]], nvim_dir))
+ end)
+ end)
+
+ describe('basename()', function()
+ it('works', function()
+ eq(nvim_prog_basename, exec_lua([[
+ local nvim_prog = ...
+ return vim.fs.basename(nvim_prog)
+ ]], nvim_prog))
+ end)
+ end)
+
+ describe('dir()', function()
+ it('works', function()
+ eq(true, exec_lua([[
+ local dir, nvim = ...
+ for name, type in vim.fs.dir(dir) do
+ if name == nvim and type == 'file' then
+ return true
+ end
+ end
+ return false
+ ]], nvim_dir, nvim_prog_basename))
+ end)
+ end)
+
+ describe('find()', function()
+ it('works', function()
+ eq({test_build_dir}, exec_lua([[
+ local dir = ...
+ return vim.fs.find('build', { path = dir, upward = true, type = 'directory' })
+ ]], nvim_dir))
+ eq({nvim_prog}, exec_lua([[
+ local dir, nvim = ...
+ return vim.fs.find(nvim, { path = dir, type = 'file' })
+ ]], test_build_dir, nvim_prog_basename))
+ end)
+ end)
+
+ describe('normalize()', function()
+ it('works with backward slashes', function()
+ eq('C:/Users/jdoe', exec_lua [[ return vim.fs.normalize('C:\\Users\\jdoe') ]])
+ end)
+ it('works with ~', function()
+ if iswin() then
+ pending([[$HOME does not exist on Windows ¯\_(ツ)_/¯]])
+ end
+ eq(os.getenv('HOME') .. '/src/foo', exec_lua [[ return vim.fs.normalize('~/src/foo') ]])
+ end)
+ it('works with environment variables', function()
+ local xdg_config_home = test_build_dir .. '/.config'
+ eq(xdg_config_home .. '/nvim', exec_lua([[
+ vim.env.XDG_CONFIG_HOME = ...
+ return vim.fs.normalize('$XDG_CONFIG_HOME/nvim')
+ ]], xdg_config_home))
+ end)
+ end)
+end)
diff --git a/test/functional/lua/highlight_spec.lua b/test/functional/lua/highlight_spec.lua
index 50eecb5d09..60d0ed5017 100644
--- a/test/functional/lua/highlight_spec.lua
+++ b/test/functional/lua/highlight_spec.lua
@@ -6,20 +6,29 @@ local command = helpers.command
local clear = helpers.clear
describe('vim.highlight.on_yank', function()
-
before_each(function()
clear()
end)
it('does not show errors even if buffer is wiped before timeout', function()
command('new')
- exec_lua[[
+ exec_lua([[
vim.highlight.on_yank({timeout = 10, on_macro = true, event = {operator = "y", regtype = "v"}})
vim.cmd('bwipeout!')
- ]]
+ ]])
helpers.sleep(10)
helpers.feed('<cr>') -- avoid hang if error message exists
eq('', eval('v:errmsg'))
end)
+ it('does not close timer twice', function()
+ exec_lua([[
+ vim.highlight.on_yank({timeout = 10, on_macro = true, event = {operator = "y"}})
+ vim.loop.sleep(10)
+ vim.schedule(function()
+ vim.highlight.on_yank({timeout = 0, on_macro = true, event = {operator = "y"}})
+ end)
+ ]])
+ eq('', eval('v:errmsg'))
+ end)
end)
diff --git a/test/functional/lua/luaeval_spec.lua b/test/functional/lua/luaeval_spec.lua
index c543dd1995..1322155595 100644
--- a/test/functional/lua/luaeval_spec.lua
+++ b/test/functional/lua/luaeval_spec.lua
@@ -532,6 +532,7 @@ describe('v:lua', function()
command('set pp+=test/functional/fixtures')
eq('\tbadval', eval("v:lua.require'leftpad'('badval')"))
eq(9003, eval("v:lua.require'bar'.doit()"))
+ eq(9004, eval("v:lua.require'baz-quux'.doit()"))
end)
it('throw errors for invalid use', function()
diff --git a/test/functional/lua/overrides_spec.lua b/test/functional/lua/overrides_spec.lua
index b0712ff366..9b51af1eec 100644
--- a/test/functional/lua/overrides_spec.lua
+++ b/test/functional/lua/overrides_spec.lua
@@ -61,6 +61,44 @@ describe('print', function()
eq('Vim(lua):E5108: Error executing lua E5114: Error while converting print argument #2: <Unknown error: lua_tolstring returned NULL for tostring result>',
pcall_err(command, 'lua print("foo", v_tblout, "bar")'))
end)
+ it('coerces error values into strings', function()
+ write_file(fname, [[
+ function string_error() error("my mistake") end
+ function number_error() error(1234) end
+ function nil_error() error(nil) end
+ function table_error() error({message = "my mistake"}) end
+ function custom_error()
+ local err = {message = "my mistake", code = 11234}
+ setmetatable(err, {
+ __tostring = function(t)
+ return "Internal Error [" .. t.code .. "] " .. t.message
+ end
+ })
+ error(err)
+ end
+ function bad_custom_error()
+ local err = {message = "my mistake", code = 11234}
+ setmetatable(err, {
+ -- intentionally not a function, downstream programmer has made an mistake
+ __tostring = "Internal Error [" .. err.code .. "] " .. err.message
+ })
+ error(err)
+ end
+ ]])
+ eq('', exec_capture('luafile ' .. fname))
+ eq('Vim(lua):E5108: Error executing lua Xtest-functional-lua-overrides-luafile:0: my mistake',
+ pcall_err(command, 'lua string_error()'))
+ eq('Vim(lua):E5108: Error executing lua Xtest-functional-lua-overrides-luafile:0: 1234',
+ pcall_err(command, 'lua number_error()'))
+ eq('Vim(lua):E5108: Error executing lua [NULL]',
+ pcall_err(command, 'lua nil_error()'))
+ eq('Vim(lua):E5108: Error executing lua [NULL]',
+ pcall_err(command, 'lua table_error()'))
+ eq('Vim(lua):E5108: Error executing lua Internal Error [11234] my mistake',
+ pcall_err(command, 'lua custom_error()'))
+ eq('Vim(lua):E5108: Error executing lua [NULL]',
+ pcall_err(command, 'lua bad_custom_error()'))
+ end)
it('prints strings with NULs and NLs correctly', function()
meths.set_option('more', true)
eq('abc ^@ def\nghi^@^@^@jkl\nTEST\n\n\nT\n',
diff --git a/test/functional/lua/thread_spec.lua b/test/functional/lua/thread_spec.lua
new file mode 100644
index 0000000000..e183ce3a57
--- /dev/null
+++ b/test/functional/lua/thread_spec.lua
@@ -0,0 +1,408 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local assert_alive = helpers.assert_alive
+local clear = helpers.clear
+local feed = helpers.feed
+local eq = helpers.eq
+local exec_lua = helpers.exec_lua
+local next_msg = helpers.next_msg
+local NIL = helpers.NIL
+local pcall_err = helpers.pcall_err
+
+describe('thread', function()
+ local screen
+
+ before_each(function()
+ clear()
+ screen = Screen.new(50, 10)
+ screen:attach()
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue1},
+ [2] = {bold = true, reverse = true},
+ [3] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
+ [4] = {bold = true, foreground = Screen.colors.SeaGreen4},
+ [5] = {bold = true},
+ })
+ end)
+
+ it('entry func is executed in protected mode', function()
+ exec_lua [[
+ local thread = vim.loop.new_thread(function()
+ error('Error in thread entry func')
+ end)
+ vim.loop.thread_join(thread)
+ ]]
+
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2: }|
+ {3:Error in luv thread:} |
+ {3:[string "<nvim>"]:2: Error in thread entry func} |
+ {4:Press ENTER or type command to continue}^ |
+ ]])
+ feed('<cr>')
+ assert_alive()
+ end)
+
+ it('callback is executed in protected mode', function()
+ exec_lua [[
+ local thread = vim.loop.new_thread(function()
+ local timer = vim.loop.new_timer()
+ local function ontimeout()
+ timer:stop()
+ timer:close()
+ error('Error in thread callback')
+ end
+ timer:start(10, 0, ontimeout)
+ vim.loop.run()
+ end)
+ vim.loop.thread_join(thread)
+ ]]
+
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2: }|
+ {3:Error in luv callback, thread:} |
+ {3:[string "<nvim>"]:6: Error in thread callback} |
+ {4:Press ENTER or type command to continue}^ |
+ ]])
+ feed('<cr>')
+ assert_alive()
+ end)
+
+ describe('print', function()
+ it('works', function()
+ exec_lua [[
+ local thread = vim.loop.new_thread(function()
+ print('print in thread')
+ end)
+ vim.loop.thread_join(thread)
+ ]]
+
+ screen:expect([[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ print in thread |
+ ]])
+ end)
+
+ it('vim.inspect', function()
+ exec_lua [[
+ local thread = vim.loop.new_thread(function()
+ print(vim.inspect({1,2}))
+ end)
+ vim.loop.thread_join(thread)
+ ]]
+
+ screen:expect([[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ { 1, 2 } |
+ ]])
+ end)
+ end)
+
+ describe('vim.*', function()
+ before_each(function()
+ clear()
+ exec_lua [[
+ Thread_Test = {}
+
+ Thread_Test.entry_func = function(async, entry_str, args)
+ local decoded_args = vim.mpack.decode(args)
+ assert(loadstring(entry_str))(async, decoded_args)
+ end
+
+ function Thread_Test:do_test()
+ local async
+ local on_async = self.on_async
+ async = vim.loop.new_async(function(ret)
+ on_async(ret)
+ async:close()
+ end)
+ local thread =
+ vim.loop.new_thread(self.entry_func, async, self.entry_str, self.args)
+ vim.loop.thread_join(thread)
+ end
+
+ Thread_Test.new = function(entry, on_async, ...)
+ self = {}
+ setmetatable(self, {__index = Thread_Test})
+ self.args = vim.mpack.encode({...})
+ self.entry_str = string.dump(entry)
+ self.on_async = on_async
+ return self
+ end
+ ]]
+ end)
+
+ it('is_thread', function()
+ exec_lua [[
+ local entry = function(async)
+ async:send(vim.is_thread())
+ end
+ local on_async = function(ret)
+ vim.rpcnotify(1, 'result', ret)
+ end
+ local thread_test = Thread_Test.new(entry, on_async)
+ thread_test:do_test()
+ ]]
+
+ eq({'notification', 'result', {true}}, next_msg())
+ end)
+
+ it('loop', function()
+ exec_lua [[
+ local entry = function(async)
+ async:send(vim.loop.version())
+ end
+ local on_async = function(ret)
+ vim.rpcnotify(1, ret)
+ end
+ local thread_test = Thread_Test.new(entry, on_async)
+ thread_test:do_test()
+ ]]
+
+ local msg = next_msg()
+ eq(msg[1], 'notification')
+ assert(tonumber(msg[2]) >= 72961)
+ end)
+
+ it('mpack', function()
+ exec_lua [[
+ local entry = function(async)
+ async:send(vim.mpack.encode({33, vim.NIL, 'text'}))
+ end
+ local on_async = function(ret)
+ vim.rpcnotify(1, 'result', vim.mpack.decode(ret))
+ end
+ local thread_test = Thread_Test.new(entry, on_async)
+ thread_test:do_test()
+ ]]
+
+ eq({'notification', 'result', {{33, NIL, 'text'}}}, next_msg())
+ end)
+
+ it('json', function()
+ exec_lua [[
+ local entry = function(async)
+ async:send(vim.json.encode({33, vim.NIL, 'text'}))
+ end
+ local on_async = function(ret)
+ vim.rpcnotify(1, 'result', vim.json.decode(ret))
+ end
+ local thread_test = Thread_Test.new(entry, on_async)
+ thread_test:do_test()
+ ]]
+
+ eq({'notification', 'result', {{33, NIL, 'text'}}}, next_msg())
+ end)
+
+ it('diff', function()
+ exec_lua [[
+ local entry = function(async)
+ async:send(vim.diff('Hello\n', 'Helli\n'))
+ end
+ local on_async = function(ret)
+ vim.rpcnotify(1, 'result', ret)
+ end
+ local thread_test = Thread_Test.new(entry, on_async)
+ thread_test:do_test()
+ ]]
+
+ eq({'notification', 'result',
+ {table.concat({
+ '@@ -1 +1 @@',
+ '-Hello',
+ '+Helli',
+ ''
+ }, '\n')}},
+ next_msg())
+ end)
+ end)
+end)
+
+describe('threadpool', function()
+ before_each(clear)
+
+ it('is_thread', function()
+ eq(false, exec_lua [[return vim.is_thread()]])
+
+ exec_lua [[
+ local work_fn = function()
+ return vim.is_thread()
+ end
+ local after_work_fn = function(ret)
+ vim.rpcnotify(1, 'result', ret)
+ end
+ local work = vim.loop.new_work(work_fn, after_work_fn)
+ work:queue()
+ ]]
+
+ eq({'notification', 'result', {true}}, next_msg())
+ end)
+
+ it('with invalid argument', function()
+ local status = pcall_err(exec_lua, [[
+ local work = vim.loop.new_thread(function() end, function() end)
+ work:queue({})
+ ]])
+
+ eq([[Error executing lua: [string "<nvim>"]:0: Error: thread arg not support type 'function' at 1]],
+ status)
+ end)
+
+ it('with invalid return value', function()
+ local screen = Screen.new(50, 10)
+ screen:attach()
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue1},
+ [2] = {bold = true, reverse = true},
+ [3] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
+ [4] = {bold = true, foreground = Screen.colors.SeaGreen4},
+ [5] = {bold = true},
+ })
+
+ exec_lua [[
+ local work = vim.loop.new_work(function() return {} end, function() end)
+ work:queue()
+ ]]
+
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2: }|
+ {3:Error in luv thread:} |
+ {3:Error: thread arg not support type 'table' at 1} |
+ {4:Press ENTER or type command to continue}^ |
+ ]])
+ end)
+
+ describe('vim.*', function()
+ before_each(function()
+ clear()
+ exec_lua [[
+ Threadpool_Test = {}
+
+ Threadpool_Test.work_fn = function(work_fn_str, args)
+ local decoded_args = vim.mpack.decode(args)
+ return assert(loadstring(work_fn_str))(decoded_args)
+ end
+
+ function Threadpool_Test:do_test()
+ local work =
+ vim.loop.new_work(self.work_fn, self.after_work)
+ work:queue(self.work_fn_str, self.args)
+ end
+
+ Threadpool_Test.new = function(work_fn, after_work, ...)
+ self = {}
+ setmetatable(self, {__index = Threadpool_Test})
+ self.args = vim.mpack.encode({...})
+ self.work_fn_str = string.dump(work_fn)
+ self.after_work = after_work
+ return self
+ end
+ ]]
+ end)
+
+ it('loop', function()
+ exec_lua [[
+ local work_fn = function()
+ return vim.loop.version()
+ end
+ local after_work_fn = function(ret)
+ vim.rpcnotify(1, ret)
+ end
+ local threadpool_test = Threadpool_Test.new(work_fn, after_work_fn)
+ threadpool_test:do_test()
+ ]]
+
+ local msg = next_msg()
+ eq(msg[1], 'notification')
+ assert(tonumber(msg[2]) >= 72961)
+ end)
+
+ it('mpack', function()
+ exec_lua [[
+ local work_fn = function()
+ local var = vim.mpack.encode({33, vim.NIL, 'text'})
+ return var
+ end
+ local after_work_fn = function(ret)
+ vim.rpcnotify(1, 'result', vim.mpack.decode(ret))
+ end
+ local threadpool_test = Threadpool_Test.new(work_fn, after_work_fn)
+ threadpool_test:do_test()
+ ]]
+
+ eq({'notification', 'result', {{33, NIL, 'text'}}}, next_msg())
+ end)
+
+ it('json', function()
+ exec_lua [[
+ local work_fn = function()
+ local var = vim.json.encode({33, vim.NIL, 'text'})
+ return var
+ end
+ local after_work_fn = function(ret)
+ vim.rpcnotify(1, 'result', vim.json.decode(ret))
+ end
+ local threadpool_test = Threadpool_Test.new(work_fn, after_work_fn)
+ threadpool_test:do_test()
+ ]]
+
+ eq({'notification', 'result', {{33, NIL, 'text'}}}, next_msg())
+ end)
+
+ it('work', function()
+ exec_lua [[
+ local work_fn = function()
+ return vim.diff('Hello\n', 'Helli\n')
+ end
+ local after_work_fn = function(ret)
+ vim.rpcnotify(1, 'result', ret)
+ end
+ local threadpool_test = Threadpool_Test.new(work_fn, after_work_fn)
+ threadpool_test:do_test()
+ ]]
+
+ eq({'notification', 'result',
+ {table.concat({
+ '@@ -1 +1 @@',
+ '-Hello',
+ '+Helli',
+ ''
+ }, '\n')}},
+ next_msg())
+ end)
+ end)
+end)
diff --git a/test/functional/lua/ui_spec.lua b/test/functional/lua/ui_spec.lua
index 2371939204..3fcb2dec8d 100644
--- a/test/functional/lua/ui_spec.lua
+++ b/test/functional/lua/ui_spec.lua
@@ -2,6 +2,8 @@ local helpers = require('test.functional.helpers')(after_each)
local eq = helpers.eq
local exec_lua = helpers.exec_lua
local clear = helpers.clear
+local feed = helpers.feed
+local eval = helpers.eval
describe('vim.ui', function()
before_each(function()
@@ -67,5 +69,19 @@ describe('vim.ui', function()
eq('Inputted text', result[1])
eq('Input: ', result[2])
end)
+
+ it('can input text on nil opt', function()
+ feed(':lua vim.ui.input(nil, function(input) result = input end)<cr>')
+ eq('', eval('v:errmsg'))
+ feed('Inputted text<cr>')
+ eq('Inputted text', exec_lua('return result'))
+ end)
+
+ it('can input text on {} opt', function()
+ feed(':lua vim.ui.input({}, function(input) result = input end)<cr>')
+ eq('', eval('v:errmsg'))
+ feed('abcdefg<cr>')
+ eq('abcdefg', exec_lua('return result'))
+ end)
end)
end)
diff --git a/test/functional/lua/uri_spec.lua b/test/functional/lua/uri_spec.lua
index fa11fdf794..4635f17557 100644
--- a/test/functional/lua/uri_spec.lua
+++ b/test/functional/lua/uri_spec.lua
@@ -101,7 +101,7 @@ describe('URI methods', function()
eq('C:\\Foo\\Bar\\Baz.txt', exec_lua(test_case))
end)
- it('file path includes only ascii charactors with encoded colon character', function()
+ it('file path includes only ascii characters with encoded colon character', function()
local test_case = [[
local uri = 'file:///C%3A/Foo/Bar/Baz.txt'
return vim.uri_to_fname(uri)
diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua
index 642dc59bbc..883e0e373b 100644
--- a/test/functional/lua/vim_spec.lua
+++ b/test/functional/lua/vim_spec.lua
@@ -14,15 +14,19 @@ local feed = helpers.feed
local pcall_err = helpers.pcall_err
local exec_lua = helpers.exec_lua
local matches = helpers.matches
-local source = helpers.source
+local exec = helpers.exec
local NIL = helpers.NIL
local retry = helpers.retry
local next_msg = helpers.next_msg
local remove_trace = helpers.remove_trace
-
-before_each(clear)
+local mkdir_p = helpers.mkdir_p
+local rmdir = helpers.rmdir
+local write_file = helpers.write_file
+local expect_exit = helpers.expect_exit
+local poke_eventloop = helpers.poke_eventloop
describe('lua stdlib', function()
+ before_each(clear)
-- İ: `tolower("İ")` is `i` which has length 1 while `İ` itself has
-- length 2 (in bytes).
-- Ⱥ: `tolower("Ⱥ")` is `ⱥ` which has length 2 while `Ⱥ` itself has
@@ -487,6 +491,16 @@ describe('lua stdlib', function()
eq(false, exec_lua("return vim.tbl_isempty({a=1, b=2, c=3})"))
end)
+ it('vim.tbl_get', function()
+ eq(true, exec_lua("return vim.tbl_get({ test = { nested_test = true }}, 'test', 'nested_test')"))
+ eq(NIL, exec_lua("return vim.tbl_get({ unindexable = true }, 'unindexable', 'missing_key')"))
+ eq(NIL, exec_lua("return vim.tbl_get({ unindexable = 1 }, 'unindexable', 'missing_key')"))
+ eq(NIL, exec_lua("return vim.tbl_get({ unindexable = coroutine.create(function () end) }, 'unindexable', 'missing_key')"))
+ eq(NIL, exec_lua("return vim.tbl_get({ unindexable = function () end }, 'unindexable', 'missing_key')"))
+ eq(NIL, exec_lua("return vim.tbl_get({}, 'missing_key')"))
+ eq(NIL, exec_lua("return vim.tbl_get({})"))
+ end)
+
it('vim.tbl_extend', function()
ok(exec_lua([[
local a = {x = 1}
@@ -636,17 +650,17 @@ describe('lua stdlib', function()
return vim.tbl_islist(c) and count == 0
]]))
- eq(exec_lua([[
+ eq({a = {b = 1}}, exec_lua([[
local a = { a = { b = 1 } }
local b = { a = {} }
return vim.tbl_deep_extend("force", a, b)
- ]]), {a = {b = 1}})
+ ]]))
- eq(exec_lua([[
+ eq({a = {b = 1}}, exec_lua([[
local a = { a = 123 }
local b = { a = { b = 1} }
return vim.tbl_deep_extend("force", a, b)
- ]]), {a = {b = 1}})
+ ]]))
ok(exec_lua([[
local a = { a = {[2] = 3} }
@@ -655,11 +669,11 @@ describe('lua stdlib', function()
return vim.deep_equal(c, {a = {[3] = 3}})
]]))
- eq(exec_lua([[
+ eq({a = 123}, exec_lua([[
local a = { a = { b = 1} }
local b = { a = 123 }
return vim.tbl_deep_extend("force", a, b)
- ]]), {a = 123 })
+ ]]))
matches('invalid "behavior": nil',
pcall_err(exec_lua, [[
@@ -740,7 +754,7 @@ describe('lua stdlib', function()
-- compat: nvim_call_function uses "special" value for vimL float
eq(false, exec_lua([[return vim.api.nvim_call_function('sin', {0.0}) == 0.0 ]]))
- source([[
+ exec([[
func! FooFunc(test)
let g:test = a:test
return {}
@@ -768,6 +782,12 @@ describe('lua stdlib', function()
-- error handling
eq({false, 'Vim:E897: List or Blob required'}, exec_lua([[return {pcall(vim.fn.add, "aa", "bb")}]]))
+
+ -- conversion between LuaRef and Vim Funcref
+ eq(true, exec_lua([[
+ local x = vim.fn.VarArg(function() return 'foo' end, function() return 'bar' end)
+ return #x == 2 and x[1]() == 'foo' and x[2]() == 'bar'
+ ]]))
end)
it('vim.fn should error when calling API function', function()
@@ -775,6 +795,20 @@ describe('lua stdlib', function()
pcall_err(exec_lua, "vim.fn.nvim_get_current_line()"))
end)
+ it('vim.fn is allowed in "fast" context by some functions #18306', function()
+ exec_lua([[
+ local timer = vim.loop.new_timer()
+ timer:start(0, 0, function()
+ timer:close()
+ assert(vim.in_fast_event())
+ vim.g.fnres = vim.fn.iconv('hello', 'utf-8', 'utf-8')
+ end)
+ ]])
+
+ helpers.poke_eventloop()
+ eq('hello', exec_lua[[return vim.g.fnres]])
+ end)
+
it('vim.rpcrequest and vim.rpcnotify', function()
exec_lua([[
chan = vim.fn.jobstart({'cat'}, {rpc=true})
@@ -987,6 +1021,77 @@ describe('lua stdlib', function()
matches([[attempt to index .* nil value]],
pcall_err(exec_lua, 'return vim.g[0].testing'))
+
+ exec_lua [[
+ local counter = 0
+ local function add_counter() counter = counter + 1 end
+ local function get_counter() return counter end
+ vim.g.AddCounter = add_counter
+ vim.g.GetCounter = get_counter
+ vim.g.funcs = {add = add_counter, get = get_counter}
+ ]]
+
+ 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([[vim.g.funcs.add()]])
+ eq(5, exec_lua([[return vim.g.funcs.get()]]))
+ exec_lua([[vim.api.nvim_get_var('funcs').add()]])
+ eq(6, exec_lua([[return vim.api.nvim_get_var('funcs').get()]]))
+
+ exec_lua [[
+ local counter = 0
+ local function add_counter() counter = counter + 1 end
+ local function get_counter() return counter end
+ vim.api.nvim_set_var('AddCounter', add_counter)
+ vim.api.nvim_set_var('GetCounter', get_counter)
+ vim.api.nvim_set_var('funcs', {add = add_counter, get = get_counter})
+ ]]
+
+ 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([[vim.g.funcs.add()]])
+ eq(5, exec_lua([[return vim.g.funcs.get()]]))
+ exec_lua([[vim.api.nvim_get_var('funcs').add()]])
+ eq(6, exec_lua([[return vim.api.nvim_get_var('funcs').get()]]))
+
+ exec([[
+ function Test()
+ endfunction
+ function s:Test()
+ endfunction
+ let g:Unknown_func = function('Test')
+ let g:Unknown_script_func = function('s:Test')
+ ]])
+ eq(NIL, exec_lua([[return vim.g.Unknown_func]]))
+ eq(NIL, exec_lua([[return vim.g.Unknown_script_func]]))
+
+ -- Check if autoload works properly
+ local pathsep = helpers.get_pathsep()
+ local xconfig = 'Xhome' .. pathsep .. 'Xconfig'
+ local xdata = 'Xhome' .. pathsep .. 'Xdata'
+ local autoload_folder = table.concat({xconfig, 'nvim', 'autoload'}, pathsep)
+ local autoload_file = table.concat({autoload_folder , 'testload.vim'}, pathsep)
+ mkdir_p(autoload_folder)
+ write_file(autoload_file , [[let testload#value = 2]])
+
+ clear{ args_rm={'-u'}, env={ XDG_CONFIG_HOME=xconfig, XDG_DATA_HOME=xdata } }
+
+ eq(2, exec_lua("return vim.g['testload#value']"))
+ rmdir('Xhome')
end)
it('vim.b', function()
@@ -1022,6 +1127,63 @@ describe('lua stdlib', function()
eq(NIL, funcs.luaeval "vim.b.to_delete")
exec_lua [[
+ local counter = 0
+ local function add_counter() counter = counter + 1 end
+ local function get_counter() return counter end
+ vim.b.AddCounter = add_counter
+ vim.b.GetCounter = get_counter
+ vim.b.funcs = {add = add_counter, get = get_counter}
+ ]]
+
+ 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.b.funcs.add()]])
+ eq(5, exec_lua([[return vim.b.funcs.get()]]))
+ exec_lua([[vim.api.nvim_buf_get_var(0, 'funcs').add()]])
+ eq(6, exec_lua([[return vim.api.nvim_buf_get_var(0, 'funcs').get()]]))
+
+ exec_lua [[
+ local counter = 0
+ local function add_counter() counter = counter + 1 end
+ local function get_counter() return counter end
+ vim.api.nvim_buf_set_var(0, 'AddCounter', add_counter)
+ vim.api.nvim_buf_set_var(0, 'GetCounter', get_counter)
+ vim.api.nvim_buf_set_var(0, 'funcs', {add = add_counter, get = get_counter})
+ ]]
+
+ 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.b.funcs.add()]])
+ eq(5, exec_lua([[return vim.b.funcs.get()]]))
+ exec_lua([[vim.api.nvim_buf_get_var(0, 'funcs').add()]])
+ eq(6, exec_lua([[return vim.api.nvim_buf_get_var(0, 'funcs').get()]]))
+
+ exec([[
+ function Test()
+ endfunction
+ function s:Test()
+ endfunction
+ let b:Unknown_func = function('Test')
+ let b:Unknown_script_func = function('s:Test')
+ ]])
+ eq(NIL, exec_lua([[return vim.b.Unknown_func]]))
+ eq(NIL, exec_lua([[return vim.b.Unknown_script_func]]))
+
+ exec_lua [[
vim.cmd "vnew"
]]
@@ -1059,6 +1221,63 @@ describe('lua stdlib', function()
eq(NIL, funcs.luaeval "vim.w.to_delete")
exec_lua [[
+ local counter = 0
+ local function add_counter() counter = counter + 1 end
+ local function get_counter() return counter end
+ vim.w.AddCounter = add_counter
+ vim.w.GetCounter = get_counter
+ vim.w.funcs = {add = add_counter, get = get_counter}
+ ]]
+
+ 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.w.funcs.add()]])
+ eq(5, exec_lua([[return vim.w.funcs.get()]]))
+ exec_lua([[vim.api.nvim_win_get_var(0, 'funcs').add()]])
+ eq(6, exec_lua([[return vim.api.nvim_win_get_var(0, 'funcs').get()]]))
+
+ exec_lua [[
+ local counter = 0
+ local function add_counter() counter = counter + 1 end
+ local function get_counter() return counter end
+ vim.api.nvim_win_set_var(0, 'AddCounter', add_counter)
+ vim.api.nvim_win_set_var(0, 'GetCounter', get_counter)
+ vim.api.nvim_win_set_var(0, 'funcs', {add = add_counter, get = get_counter})
+ ]]
+
+ 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.w.funcs.add()]])
+ eq(5, exec_lua([[return vim.w.funcs.get()]]))
+ exec_lua([[vim.api.nvim_win_get_var(0, 'funcs').add()]])
+ eq(6, exec_lua([[return vim.api.nvim_win_get_var(0, 'funcs').get()]]))
+
+ exec([[
+ function Test()
+ endfunction
+ function s:Test()
+ endfunction
+ let w:Unknown_func = function('Test')
+ let w:Unknown_script_func = function('s:Test')
+ ]])
+ eq(NIL, exec_lua([[return vim.w.Unknown_func]]))
+ eq(NIL, exec_lua([[return vim.w.Unknown_script_func]]))
+
+ exec_lua [[
vim.cmd "vnew"
]]
@@ -1091,6 +1310,52 @@ describe('lua stdlib', function()
eq(NIL, funcs.luaeval "vim.t.to_delete")
exec_lua [[
+ local counter = 0
+ local function add_counter() counter = counter + 1 end
+ local function get_counter() return counter end
+ vim.t.AddCounter = add_counter
+ vim.t.GetCounter = get_counter
+ vim.t.funcs = {add = add_counter, get = get_counter}
+ ]]
+
+ 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.t.funcs.add()]])
+ eq(5, exec_lua([[return vim.t.funcs.get()]]))
+ exec_lua([[vim.api.nvim_tabpage_get_var(0, 'funcs').add()]])
+ eq(6, exec_lua([[return vim.api.nvim_tabpage_get_var(0, 'funcs').get()]]))
+
+ exec_lua [[
+ local counter = 0
+ local function add_counter() counter = counter + 1 end
+ local function get_counter() return counter end
+ vim.api.nvim_tabpage_set_var(0, 'AddCounter', add_counter)
+ vim.api.nvim_tabpage_set_var(0, 'GetCounter', get_counter)
+ vim.api.nvim_tabpage_set_var(0, 'funcs', {add = add_counter, get = get_counter})
+ ]]
+
+ 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.t.funcs.add()]])
+ eq(5, exec_lua([[return vim.t.funcs.get()]]))
+ exec_lua([[vim.api.nvim_tabpage_get_var(0, 'funcs').add()]])
+ eq(6, exec_lua([[return vim.api.nvim_tabpage_get_var(0, 'funcs').get()]]))
+
+ exec_lua [[
vim.cmd "tabnew"
]]
@@ -1131,10 +1396,12 @@ describe('lua stdlib', function()
]]
eq('', funcs.luaeval "vim.bo.filetype")
eq(true, funcs.luaeval "vim.bo[BUF].modifiable")
- matches("Invalid option name: 'nosuchopt'$",
+ matches("unknown option 'nosuchopt'$",
pcall_err(exec_lua, 'return vim.bo.nosuchopt'))
matches("Expected lua string$",
pcall_err(exec_lua, 'return vim.bo[0][0].autoread'))
+ matches("Invalid buffer id: %-1$",
+ pcall_err(exec_lua, 'return vim.bo[-1].filetype'))
end)
it('vim.wo', function()
@@ -1150,15 +1417,24 @@ describe('lua stdlib', function()
eq(0, funcs.luaeval "vim.wo.cole")
eq(0, funcs.luaeval "vim.wo[0].cole")
eq(0, funcs.luaeval "vim.wo[1001].cole")
- matches("Invalid option name: 'notanopt'$",
+ matches("unknown option 'notanopt'$",
pcall_err(exec_lua, 'return vim.wo.notanopt'))
matches("Expected lua string$",
pcall_err(exec_lua, 'return vim.wo[0][0].list'))
+ matches("Invalid window id: %-1$",
+ pcall_err(exec_lua, 'return vim.wo[-1].list'))
eq(2, funcs.luaeval "vim.wo[1000].cole")
exec_lua [[
vim.wo[1000].cole = 0
]]
eq(0, funcs.luaeval "vim.wo[1000].cole")
+
+ -- Can handle global-local values
+ exec_lua [[vim.o.scrolloff = 100]]
+ exec_lua [[vim.wo.scrolloff = 200]]
+ eq(200, funcs.luaeval "vim.wo.scrolloff")
+ exec_lua [[vim.wo.scrolloff = -1]]
+ eq(100, funcs.luaeval "vim.wo.scrolloff")
end)
describe('vim.opt', function()
@@ -2004,6 +2280,22 @@ describe('lua stdlib', function()
eq('iworld<ESC>', exec_lua[[return table.concat(keys, '')]])
end)
+
+ it('can call vim.fn functions on Ctrl-C #17273', function()
+ exec_lua([[
+ _G.ctrl_c_cmdtype = ''
+
+ vim.on_key(function(c)
+ if c == '\3' then
+ _G.ctrl_c_cmdtype = vim.fn.getcmdtype()
+ end
+ end)
+ ]])
+ feed('/')
+ poke_eventloop() -- This is needed because Ctrl-C flushes input
+ feed('<C-C>')
+ eq('/', exec_lua([[return _G.ctrl_c_cmdtype]]))
+ end)
end)
describe('vim.wait', function()
@@ -2278,6 +2570,17 @@ describe('lua stdlib', function()
eq(buf1, meths.get_current_buf())
eq(buf2, val)
end)
+
+ it('does not cause ml_get errors with invalid visual selection', function()
+ -- Should be fixed by vim-patch:8.2.4028.
+ exec_lua [[
+ local a = vim.api
+ local t = function(s) return a.nvim_replace_termcodes(s, true, true, true) end
+ a.nvim_buf_set_lines(0, 0, -1, true, {"a", "b", "c"})
+ a.nvim_feedkeys(t "G<C-V>", "txn", false)
+ a.nvim_buf_call(a.nvim_create_buf(false, true), function() vim.cmd "redraw" end)
+ ]]
+ end)
end)
describe('vim.api.nvim_win_call', function()
@@ -2306,12 +2609,109 @@ describe('lua stdlib', function()
eq(win1, meths.get_current_win())
eq(win2, val)
end)
+
+ it('does not cause ml_get errors with invalid visual selection', function()
+ -- Add lines to the current buffer and make another window looking into an empty buffer.
+ exec_lua [[
+ _G.a = vim.api
+ _G.t = function(s) return a.nvim_replace_termcodes(s, true, true, true) end
+ _G.win_lines = a.nvim_get_current_win()
+ vim.cmd "new"
+ _G.win_empty = a.nvim_get_current_win()
+ a.nvim_set_current_win(win_lines)
+ a.nvim_buf_set_lines(0, 0, -1, true, {"a", "b", "c"})
+ ]]
+
+ -- Start Visual in current window, redraw in other window with fewer lines.
+ -- Should be fixed by vim-patch:8.2.4018.
+ exec_lua [[
+ a.nvim_feedkeys(t "G<C-V>", "txn", false)
+ a.nvim_win_call(win_empty, function() vim.cmd "redraw" end)
+ ]]
+
+ -- Start Visual in current window, extend it in other window with more lines.
+ -- Fixed for win_execute by vim-patch:8.2.4026, but nvim_win_call should also not be affected.
+ exec_lua [[
+ a.nvim_feedkeys(t "<Esc>gg", "txn", false)
+ a.nvim_set_current_win(win_empty)
+ a.nvim_feedkeys(t "gg<C-V>", "txn", false)
+ a.nvim_win_call(win_lines, function() a.nvim_feedkeys(t "G<C-V>", "txn", false) end)
+ vim.cmd "redraw"
+ ]]
+ end)
+
+ it('updates ruler if cursor moved', function()
+ -- Fixed for win_execute in vim-patch:8.1.2124, but should've applied to nvim_win_call too!
+ local screen = Screen.new(30, 5)
+ screen:set_default_attr_ids {
+ [1] = {reverse = true},
+ [2] = {bold = true, reverse = true},
+ }
+ screen:attach()
+ exec_lua [[
+ _G.a = vim.api
+ vim.opt.ruler = true
+ local lines = {}
+ for i = 0, 499 do lines[#lines + 1] = tostring(i) end
+ a.nvim_buf_set_lines(0, 0, -1, true, lines)
+ a.nvim_win_set_cursor(0, {20, 0})
+ vim.cmd "split"
+ _G.win = a.nvim_get_current_win()
+ vim.cmd "wincmd w | redraw"
+ ]]
+ screen:expect [[
+ 19 |
+ {1:[No Name] [+] 20,1 3%}|
+ ^19 |
+ {2:[No Name] [+] 20,1 3%}|
+ |
+ ]]
+ exec_lua [[
+ a.nvim_win_call(win, function() a.nvim_win_set_cursor(0, {100, 0}) end)
+ vim.cmd "redraw"
+ ]]
+ screen:expect [[
+ 99 |
+ {1:[No Name] [+] 100,1 19%}|
+ ^19 |
+ {2:[No Name] [+] 20,1 3%}|
+ |
+ ]]
+ end)
+ end)
+end)
+
+describe('lua: builtin modules', function()
+ local function do_tests()
+ eq(2, exec_lua[[return vim.tbl_count {x=1,y=2}]])
+ eq('{ 10, "spam" }', exec_lua[[return vim.inspect {10, 'spam'}]])
+ end
+
+ it('works', function()
+ clear()
+ do_tests()
+ end)
+
+ it('works when disabled', function()
+ clear('--luamod-dev')
+ do_tests()
+ end)
+
+ it('works without runtime', function()
+ clear{env={VIMRUNTIME='fixtures/a'}}
+ do_tests()
+ end)
+
+
+ it('does not work when disabled without runtime', function()
+ clear{args={'--luamod-dev'}, env={VIMRUNTIME='fixtures/a'}}
+ expect_exit(exec_lua, [[return vim.tbl_count {x=1,y=2}]])
end)
end)
describe('lua: require("mod") from packages', function()
before_each(function()
- command('set rtp+=test/functional/fixtures pp+=test/functional/fixtures')
+ clear('--cmd', 'set rtp+=test/functional/fixtures pp+=test/functional/fixtures')
end)
it('propagates syntax error', function()
@@ -2334,6 +2734,8 @@ describe('lua: require("mod") from packages', function()
end)
describe('vim.keymap', function()
+ before_each(clear)
+
it('can make a mapping', function()
eq(0, exec_lua [[
GlobalCount = 0
@@ -2397,6 +2799,39 @@ describe('vim.keymap', function()
eq('\nNo mapping found', helpers.exec_capture('nmap asdf'))
end)
+ it('works with buffer-local mappings', function()
+ eq(0, exec_lua [[
+ GlobalCount = 0
+ vim.keymap.set('n', 'asdf', function() GlobalCount = GlobalCount + 1 end, {buffer=true})
+ return GlobalCount
+ ]])
+
+ feed('asdf\n')
+
+ eq(1, exec_lua[[return GlobalCount]])
+
+ exec_lua [[
+ vim.keymap.del('n', 'asdf', {buffer=true})
+ ]]
+
+ feed('asdf\n')
+
+ eq(1, exec_lua[[return GlobalCount]])
+ eq('\nNo mapping found', helpers.exec_capture('nmap asdf'))
+ end)
+
+ it('does not mutate the opts parameter', function()
+ eq(true, exec_lua [[
+ opts = {buffer=true}
+ vim.keymap.set('n', 'asdf', function() end, opts)
+ return opts.buffer
+ ]])
+ eq(true, exec_lua [[
+ vim.keymap.del('n', 'asdf', opts)
+ return opts.buffer
+ ]])
+ end)
+
it('can do <Plug> mappings', function()
eq(0, exec_lua [[
GlobalCount = 0
diff --git a/test/functional/lua/xdiff_spec.lua b/test/functional/lua/xdiff_spec.lua
index 4f28f84c01..d55268fc78 100644
--- a/test/functional/lua/xdiff_spec.lua
+++ b/test/functional/lua/xdiff_spec.lua
@@ -90,6 +90,48 @@ describe('xdiff bindings', function()
exec_lua([[return vim.diff(a2, b2, {result_type = 'indices'})]]))
end)
+ it('can run different algorithms', function()
+ local a = table.concat({
+ '.foo1 {',
+ ' margin: 0;',
+ '}',
+ '',
+ '.bar {',
+ ' margin: 0;',
+ '}',
+ ''}, '\n')
+
+ local b = table.concat({
+ '.bar {',
+ ' margin: 0;',
+ '}',
+ '',
+ '.foo1 {',
+ ' margin: 0;',
+ ' color: green;',
+ '}',
+ ''}, '\n')
+
+ eq(
+ table.concat({'@@ -1,4 +0,0 @@',
+ '-.foo1 {',
+ '- margin: 0;',
+ '-}',
+ '-',
+ '@@ -7,0 +4,5 @@',
+ '+',
+ '+.foo1 {',
+ '+ margin: 0;',
+ '+ color: green;',
+ '+}',
+ ''}, '\n'),
+ exec_lua([[
+ local args = {...}
+ return vim.diff(args[1], args[2], {
+ algorithm = 'patience'
+ })
+ ]], a, b))
+ end)
end)
it('can handle bad args', function()
diff --git a/test/functional/options/autochdir_spec.lua b/test/functional/options/autochdir_spec.lua
index 2fce0a5ed9..74959a8e76 100644
--- a/test/functional/options/autochdir_spec.lua
+++ b/test/functional/options/autochdir_spec.lua
@@ -1,7 +1,9 @@
+local lfs = require('lfs')
local helpers = require('test.functional.helpers')(after_each)
local clear = helpers.clear
local eq = helpers.eq
-local getcwd = helpers.funcs.getcwd
+local funcs = helpers.funcs
+local command = helpers.command
describe("'autochdir'", function()
it('given on the shell gets processed properly', function()
@@ -9,11 +11,34 @@ describe("'autochdir'", function()
-- By default 'autochdir' is off, thus getcwd() returns the repo root.
clear(targetdir..'/tty-test.c')
- local rootdir = getcwd()
+ local rootdir = funcs.getcwd()
local expected = rootdir .. '/' .. targetdir
-- With 'autochdir' on, we should get the directory of tty-test.c.
clear('--cmd', 'set autochdir', targetdir..'/tty-test.c')
- eq(helpers.iswin() and expected:gsub('/', '\\') or expected, getcwd())
+ eq(helpers.iswin() and expected:gsub('/', '\\') or expected, funcs.getcwd())
+ end)
+
+ it('is not overwritten by getwinvar() call #17609',function()
+ local curdir = string.gsub(lfs.currentdir(), '\\', '/')
+ local dir_a = curdir..'/Xtest-functional-options-autochdir.dir_a'
+ local dir_b = curdir..'/Xtest-functional-options-autochdir.dir_b'
+ lfs.mkdir(dir_a)
+ lfs.mkdir(dir_b)
+ clear()
+ command('set shellslash')
+ command('set autochdir')
+ command('edit '..dir_a..'/file1')
+ eq(dir_a, funcs.getcwd())
+ command('lcd '..dir_b)
+ eq(dir_b, funcs.getcwd())
+ command('botright vnew ../file2')
+ eq(curdir, funcs.getcwd())
+ command('wincmd w')
+ eq(dir_a, funcs.getcwd())
+ funcs.getwinvar(2, 'foo')
+ eq(dir_a, funcs.getcwd())
+ helpers.rmdir(dir_a)
+ helpers.rmdir(dir_b)
end)
end)
diff --git a/test/functional/options/cursorbind_spec.lua b/test/functional/options/cursorbind_spec.lua
new file mode 100644
index 0000000000..1a03ed099a
--- /dev/null
+++ b/test/functional/options/cursorbind_spec.lua
@@ -0,0 +1,91 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local clear = helpers.clear
+local command = helpers.command
+local exec = helpers.exec
+local feed = helpers.feed
+
+before_each(clear)
+
+describe("'cursorbind'", function()
+ it("behaves consistently whether 'cursorline' is set or not vim-patch:8.2.4795", function()
+ local screen = Screen.new(60, 8)
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ [2] = {bold = true, reverse = true}, -- StatusLine
+ [3] = {reverse = true}, -- StatusLineNC
+ [4] = {background = Screen.colors.Grey90}, -- CursorLine, CursorColumn
+ })
+ screen:attach()
+ exec([[
+ call setline(1, 'aa bb cc dd ee ff gg hh ii jj kk ll mm' ..
+ \ ' nn oo pp qq rr ss tt uu vv ww xx yy zz')
+ set nowrap
+ " The following makes the cursor apparent on the screen dump
+ set sidescroll=1 cursorcolumn
+ " add empty lines, required for cursorcolumn
+ call append(1, ['','','',''])
+ 20vsp
+ windo :set cursorbind
+ ]])
+ feed('20l')
+ screen:expect([[
+ a bb cc dd ee ff gg │aa bb cc dd ee ff gg^ hh ii jj kk ll mm |
+ {4: }│ {4: } |
+ {4: }│ {4: } |
+ {4: }│ {4: } |
+ {4: }│ {4: } |
+ {1:~ }│{1:~ }|
+ {3:[No Name] [+] }{2:[No Name] [+] }|
+ |
+ ]])
+ feed('10l')
+ screen:expect([[
+ hh ii jj kk ll mm n│aa bb cc dd ee ff gg hh ii jj ^kk ll mm |
+ {4: } │ {4: } |
+ {4: } │ {4: } |
+ {4: } │ {4: } |
+ {4: } │ {4: } |
+ {1:~ }│{1:~ }|
+ {3:[No Name] [+] }{2:[No Name] [+] }|
+ |
+ ]])
+ command('windo :set cursorline')
+ feed('0')
+ feed('20l')
+ screen:expect([[
+ {4:a bb cc dd ee ff gg }│{4:aa bb cc dd ee ff gg^ hh ii jj kk ll mm }|
+ {4: }│ {4: } |
+ {4: }│ {4: } |
+ {4: }│ {4: } |
+ {4: }│ {4: } |
+ {1:~ }│{1:~ }|
+ {3:[No Name] [+] }{2:[No Name] [+] }|
+ |
+ ]])
+ feed('10l')
+ screen:expect([[
+ {4: hh ii jj kk ll mm n}│{4:aa bb cc dd ee ff gg hh ii jj ^kk ll mm }|
+ {4: } │ {4: } |
+ {4: } │ {4: } |
+ {4: } │ {4: } |
+ {4: } │ {4: } |
+ {1:~ }│{1:~ }|
+ {3:[No Name] [+] }{2:[No Name] [+] }|
+ |
+ ]])
+ command('windo :set nocursorline nocursorcolumn')
+ feed('0')
+ feed('40l')
+ screen:expect([[
+ kk ll mm nn oo pp qq│ bb cc dd ee ff gg hh ii jj kk ll mm n^n|
+ │ |
+ │ |
+ │ |
+ │ |
+ {1:~ }│{1:~ }|
+ {3:[No Name] [+] }{2:[No Name] [+] }|
+ |
+ ]])
+ end)
+end)
diff --git a/test/functional/options/defaults_spec.lua b/test/functional/options/defaults_spec.lua
index 6620c9acef..9244ca0974 100644
--- a/test/functional/options/defaults_spec.lua
+++ b/test/functional/options/defaults_spec.lua
@@ -9,6 +9,7 @@ local clear = helpers.clear
local exc_exec = helpers.exc_exec
local eval = helpers.eval
local eq = helpers.eq
+local ok = helpers.ok
local funcs = helpers.funcs
local insert = helpers.insert
local iswin = helpers.iswin
@@ -17,6 +18,7 @@ local mkdir = helpers.mkdir
local rmdir = helpers.rmdir
local alter_slashes = helpers.alter_slashes
local tbl_contains = helpers.tbl_contains
+local expect_exit = helpers.expect_exit
describe('startup defaults', function()
describe(':filetype', function()
@@ -163,7 +165,7 @@ describe('startup defaults', function()
end)
it("'shadafile' ('viminfofile')", function()
- local env = {XDG_DATA_HOME='Xtest-userdata', XDG_CONFIG_HOME='Xtest-userconfig'}
+ local env = {XDG_DATA_HOME='Xtest-userdata', XDG_STATE_HOME='Xtest-userstate', XDG_CONFIG_HOME='Xtest-userconfig'}
clear{args={}, args_rm={'-i'}, env=env}
-- Default 'shadafile' is empty.
-- This means use the default location. :help shada-file-name
@@ -174,11 +176,11 @@ describe('startup defaults', function()
command('write')
local f = eval('fnamemodify(@%,":p")')
assert(string.len(f) > 3)
- command('qall')
+ expect_exit(command, 'qall')
clear{args={}, args_rm={'-i'}, env=env}
eq({ f }, eval('v:oldfiles'))
os.remove('Xtest-foo')
- rmdir('Xtest-userdata')
+ rmdir('Xtest-userstate')
-- Handles viminfo/viminfofile as alias for shada/shadafile.
eq('\n shadafile=', eval('execute("set shadafile?")'))
@@ -206,7 +208,7 @@ describe('startup defaults', function()
describe('$NVIM_LOG_FILE', function()
local xdgdir = 'Xtest-startup-xdg-logpath'
- local xdgcachedir = xdgdir..'/nvim'
+ local xdgstatedir = iswin() and xdgdir..'/nvim-data' or xdgdir..'/nvim'
after_each(function()
os.remove('Xtest-logpath')
rmdir(xdgdir)
@@ -218,26 +220,26 @@ describe('startup defaults', function()
}})
eq('Xtest-logpath', eval('$NVIM_LOG_FILE'))
end)
- it('defaults to stdpath("cache")/log if empty', function()
- eq(true, mkdir(xdgdir) and mkdir(xdgcachedir))
+ it('defaults to stdpath("log")/log if empty', function()
+ eq(true, mkdir(xdgdir) and mkdir(xdgstatedir))
clear({env={
- XDG_CACHE_HOME=xdgdir,
+ XDG_STATE_HOME=xdgdir,
NVIM_LOG_FILE='', -- Empty is invalid.
}})
- eq(xdgcachedir..'/log', string.gsub(eval('$NVIM_LOG_FILE'), '\\', '/'))
+ eq(xdgstatedir..'/log', string.gsub(eval('$NVIM_LOG_FILE'), '\\', '/'))
end)
- it('defaults to stdpath("cache")/log if invalid', function()
- eq(true, mkdir(xdgdir) and mkdir(xdgcachedir))
+ it('defaults to stdpath("log")/log if invalid', function()
+ eq(true, mkdir(xdgdir) and mkdir(xdgstatedir))
clear({env={
- XDG_CACHE_HOME=xdgdir,
+ XDG_STATE_HOME=xdgdir,
NVIM_LOG_FILE='.', -- Any directory is invalid.
}})
- eq(xdgcachedir..'/log', string.gsub(eval('$NVIM_LOG_FILE'), '\\', '/'))
+ eq(xdgstatedir..'/log', string.gsub(eval('$NVIM_LOG_FILE'), '\\', '/'))
end)
end)
end)
-describe('XDG-based defaults', function()
+describe('XDG defaults', function()
-- Need separate describe() blocks to not run clear() twice.
-- Do not put before_each() here for the same reasons.
@@ -264,6 +266,7 @@ describe('XDG-based defaults', function()
XDG_CONFIG_HOME=nil,
XDG_DATA_HOME=nil,
XDG_CACHE_HOME=nil,
+ XDG_STATE_HOME=nil,
XDG_RUNTIME_DIR=nil,
XDG_CONFIG_DIRS=nil,
XDG_DATA_DIRS=nil,
@@ -280,6 +283,7 @@ describe('XDG-based defaults', function()
eq('.', meths.get_option('viewdir'))
eq('.', meths.get_option('directory'))
eq('.', meths.get_option('undodir'))
+ ok((funcs.tempname()):len() > 4)
end)
end)
@@ -293,6 +297,7 @@ describe('XDG-based defaults', function()
local env_sep = iswin() and ';' or ':'
local data_dir = iswin() and 'nvim-data' or 'nvim'
+ local state_dir = iswin() and 'nvim-data' or 'nvim'
local root_path = iswin() and 'C:' or ''
describe('with too long XDG variables', function()
@@ -303,6 +308,8 @@ describe('XDG-based defaults', function()
.. env_sep.. root_path .. ('/b'):rep(2048)
.. (env_sep .. root_path .. '/c'):rep(512)),
XDG_DATA_HOME=(root_path .. ('/X'):rep(4096)),
+ XDG_RUNTIME_DIR=(root_path .. ('/X'):rep(4096)),
+ XDG_STATE_HOME=(root_path .. ('/X'):rep(4096)),
XDG_DATA_DIRS=(root_path .. ('/A'):rep(2048)
.. env_sep .. root_path .. ('/B'):rep(2048)
.. (env_sep .. root_path .. '/C'):rep(512)),
@@ -355,13 +362,13 @@ describe('XDG-based defaults', function()
.. ',' .. root_path .. ('/a'):rep(2048) .. '/nvim/after'
.. ',' .. root_path .. ('/x'):rep(4096) .. '/nvim/after'
):gsub('\\', '/')), (meths.get_option('runtimepath')):gsub('\\', '/'))
- eq('.,' .. root_path .. ('/X'):rep(4096).. '/' .. data_dir .. '/backup//',
+ eq('.,' .. root_path .. ('/X'):rep(4096).. '/' .. state_dir .. '/backup//',
(meths.get_option('backupdir'):gsub('\\', '/')))
- eq(root_path .. ('/X'):rep(4096) .. '/' .. data_dir .. '/swap//',
+ eq(root_path .. ('/X'):rep(4096) .. '/' .. state_dir .. '/swap//',
(meths.get_option('directory')):gsub('\\', '/'))
- eq(root_path .. ('/X'):rep(4096) .. '/' .. data_dir .. '/undo//',
+ eq(root_path .. ('/X'):rep(4096) .. '/' .. state_dir .. '/undo//',
(meths.get_option('undodir')):gsub('\\', '/'))
- eq(root_path .. ('/X'):rep(4096) .. '/' .. data_dir .. '/view//',
+ eq(root_path .. ('/X'):rep(4096) .. '/' .. state_dir .. '/view//',
(meths.get_option('viewdir')):gsub('\\', '/'))
end)
end)
@@ -372,6 +379,8 @@ describe('XDG-based defaults', function()
XDG_CONFIG_HOME='$XDG_DATA_HOME',
XDG_CONFIG_DIRS='$XDG_DATA_DIRS',
XDG_DATA_HOME='$XDG_CONFIG_HOME',
+ XDG_RUNTIME_DIR='$XDG_RUNTIME_DIR',
+ XDG_STATE_HOME='$XDG_CONFIG_HOME',
XDG_DATA_DIRS='$XDG_CONFIG_DIRS',
}})
end)
@@ -405,13 +414,13 @@ describe('XDG-based defaults', function()
.. ',$XDG_DATA_DIRS/nvim/after'
.. ',$XDG_DATA_HOME/nvim/after'
):gsub('\\', '/')), (meths.get_option('runtimepath')):gsub('\\', '/'))
- eq(('.,$XDG_CONFIG_HOME/' .. data_dir .. '/backup//'),
+ eq(('.,$XDG_CONFIG_HOME/' .. state_dir .. '/backup//'),
meths.get_option('backupdir'):gsub('\\', '/'))
- eq(('$XDG_CONFIG_HOME/' .. data_dir .. '/swap//'),
+ eq(('$XDG_CONFIG_HOME/' .. state_dir .. '/swap//'),
meths.get_option('directory'):gsub('\\', '/'))
- eq(('$XDG_CONFIG_HOME/' .. data_dir .. '/undo//'),
+ eq(('$XDG_CONFIG_HOME/' .. state_dir .. '/undo//'),
meths.get_option('undodir'):gsub('\\', '/'))
- eq(('$XDG_CONFIG_HOME/' .. data_dir .. '/view//'),
+ eq(('$XDG_CONFIG_HOME/' .. state_dir .. '/view//'),
meths.get_option('viewdir'):gsub('\\', '/'))
meths.command('set all&')
eq(('$XDG_DATA_HOME/nvim'
@@ -425,14 +434,15 @@ describe('XDG-based defaults', function()
.. ',$XDG_DATA_DIRS/nvim/after'
.. ',$XDG_DATA_HOME/nvim/after'
):gsub('\\', '/'), (meths.get_option('runtimepath')):gsub('\\', '/'))
- eq(('.,$XDG_CONFIG_HOME/' .. data_dir .. '/backup//'),
+ eq(('.,$XDG_CONFIG_HOME/' .. state_dir .. '/backup//'),
meths.get_option('backupdir'):gsub('\\', '/'))
- eq(('$XDG_CONFIG_HOME/' .. data_dir .. '/swap//'),
+ eq(('$XDG_CONFIG_HOME/' .. state_dir .. '/swap//'),
meths.get_option('directory'):gsub('\\', '/'))
- eq(('$XDG_CONFIG_HOME/' .. data_dir .. '/undo//'),
+ eq(('$XDG_CONFIG_HOME/' .. state_dir .. '/undo//'),
meths.get_option('undodir'):gsub('\\', '/'))
- eq(('$XDG_CONFIG_HOME/' .. data_dir .. '/view//'),
+ eq(('$XDG_CONFIG_HOME/' .. state_dir .. '/view//'),
meths.get_option('viewdir'):gsub('\\', '/'))
+ eq(nil, (funcs.tempname()):match('XDG_RUNTIME_DIR'))
end)
end)
@@ -442,6 +452,7 @@ describe('XDG-based defaults', function()
XDG_CONFIG_HOME=', , ,',
XDG_CONFIG_DIRS=',-,-,' .. env_sep .. '-,-,-',
XDG_DATA_HOME=',=,=,',
+ XDG_STATE_HOME=',=,=,',
XDG_DATA_DIRS=',≡,≡,' .. env_sep .. '≡,≡,≡',
}})
end)
@@ -484,13 +495,13 @@ describe('XDG-based defaults', function()
.. ',\\,-\\,-\\,' .. path_sep ..'nvim' .. path_sep ..'after'
.. ',\\, \\, \\,' .. path_sep ..'nvim' .. path_sep ..'after'
), meths.get_option('runtimepath'))
- eq('.,\\,=\\,=\\,' .. path_sep .. data_dir .. '' .. path_sep ..'backup' .. (path_sep):rep(2),
+ eq('.,\\,=\\,=\\,' .. path_sep .. state_dir .. '' .. path_sep ..'backup' .. (path_sep):rep(2),
meths.get_option('backupdir'))
- eq('\\,=\\,=\\,' .. path_sep ..'' .. data_dir .. '' .. path_sep ..'swap' .. (path_sep):rep(2),
+ eq('\\,=\\,=\\,' .. path_sep ..'' .. state_dir .. '' .. path_sep ..'swap' .. (path_sep):rep(2),
meths.get_option('directory'))
- eq('\\,=\\,=\\,' .. path_sep ..'' .. data_dir .. '' .. path_sep ..'undo' .. (path_sep):rep(2),
+ eq('\\,=\\,=\\,' .. path_sep ..'' .. state_dir .. '' .. path_sep ..'undo' .. (path_sep):rep(2),
meths.get_option('undodir'))
- eq('\\,=\\,=\\,' .. path_sep ..'' .. data_dir .. '' .. path_sep ..'view' .. (path_sep):rep(2),
+ eq('\\,=\\,=\\,' .. path_sep ..'' .. state_dir .. '' .. path_sep ..'view' .. (path_sep):rep(2),
meths.get_option('viewdir'))
end)
end)
@@ -499,8 +510,9 @@ end)
describe('stdpath()', function()
-- Windows appends 'nvim-data' instead of just 'nvim' to prevent collisions
- -- due to XDG_CONFIG_HOME and XDG_DATA_HOME being the same.
+ -- due to XDG_CONFIG_HOME, XDG_DATA_HOME and XDG_STATE_HOME being the same.
local datadir = iswin() and 'nvim-data' or 'nvim'
+ local statedir = iswin() and 'nvim-data' or 'nvim'
local env_sep = iswin() and ';' or ':'
it('acceptance', function()
@@ -509,8 +521,10 @@ describe('stdpath()', function()
eq('nvim', funcs.fnamemodify(funcs.stdpath('cache'), ':t'))
eq('nvim', funcs.fnamemodify(funcs.stdpath('config'), ':t'))
eq(datadir, funcs.fnamemodify(funcs.stdpath('data'), ':t'))
+ eq(statedir, funcs.fnamemodify(funcs.stdpath('state'), ':t'))
eq('table', type(funcs.stdpath('config_dirs')))
eq('table', type(funcs.stdpath('data_dirs')))
+ eq('string', type(funcs.stdpath('run')))
assert_alive() -- Check for crash. #8393
end)
@@ -582,6 +596,39 @@ describe('stdpath()', function()
end)
end)
+ describe('with "state"' , function ()
+ it('knows XDG_STATE_HOME', function()
+ clear({env={
+ XDG_STATE_HOME=alter_slashes('/home/docwhat/.local'),
+ }})
+ eq(alter_slashes('/home/docwhat/.local/'..statedir), funcs.stdpath('state'))
+ end)
+
+ it('handles changes during runtime', function()
+ clear({env={
+ XDG_STATE_HOME=alter_slashes('/home/original'),
+ }})
+ eq(alter_slashes('/home/original/'..statedir), funcs.stdpath('state'))
+ command("let $XDG_STATE_HOME='"..alter_slashes('/home/new').."'")
+ eq(alter_slashes('/home/new/'..statedir), funcs.stdpath('state'))
+ end)
+
+ it("doesn't expand $VARIABLES", function()
+ clear({env={
+ XDG_STATE_HOME='$VARIABLES',
+ VARIABLES='this-should-not-happen',
+ }})
+ eq(alter_slashes('$VARIABLES/'..statedir), funcs.stdpath('state'))
+ end)
+
+ it("doesn't expand ~/", function()
+ clear({env={
+ XDG_STATE_HOME=alter_slashes('~/frobnitz'),
+ }})
+ eq(alter_slashes('~/frobnitz/'..statedir), funcs.stdpath('state'))
+ end)
+ end)
+
describe('with "cache"' , function ()
it('knows XDG_CACHE_HOME', function()
clear({env={
diff --git a/test/functional/options/keymap_spec.lua b/test/functional/options/keymap_spec.lua
index 52a714f7a8..a814c35a39 100644
--- a/test/functional/options/keymap_spec.lua
+++ b/test/functional/options/keymap_spec.lua
@@ -120,9 +120,9 @@ describe("'keymap' / :lmap", function()
it("Can be toggled with <C-^> in insert mode", function()
feed('i<C-^>l<C-^>l<esc>')
expect('lalllaaa')
- eq(eval('&iminsert'), 1)
+ eq(1, eval('&iminsert'))
feed('i<C-^><esc>')
- eq(eval('&iminsert'), 0)
+ eq(0, eval('&iminsert'))
end)
end)
describe("'imsearch' option", function()
@@ -136,36 +136,36 @@ describe("'keymap' / :lmap", function()
expect('aaa')
end)
it("Can be toggled with C-^", function()
- eq(eval('&imsearch'), 1)
+ eq(1, eval('&imsearch'))
feed('/<C-^>lll<cr>3x')
expect('aaa')
- eq(eval('&imsearch'), 0)
+ eq(0, eval('&imsearch'))
feed('u0/<C-^>lll<cr>3x')
expect('lll')
- eq(eval('&imsearch'), 1)
+ eq(1, eval('&imsearch'))
end)
it("can follow 'iminsert'", function()
command('set imsearch=-1')
feed('/lll<cr>3x')
expect('lll')
- eq(eval('&imsearch'), -1)
- eq(eval('&iminsert'), 1)
+ eq(-1, eval('&imsearch'))
+ eq(1, eval('&iminsert'))
feed('u/<C-^>lll<cr>3x')
expect('aaa')
- eq(eval('&imsearch'), -1)
- eq(eval('&iminsert'), 0)
+ eq(-1, eval('&imsearch'))
+ eq(0, eval('&iminsert'))
end)
end)
it(":lmap not applied to macros", function()
command("call setreg('a', 'il')")
feed('@a')
expect('llllaaa')
- eq(call('getreg', 'a'), 'il')
+ eq('il', call('getreg', 'a'))
end)
it(":lmap applied to macro recording", function()
feed('qail<esc>q@a')
expect('aalllaaa')
- eq(call('getreg', 'a'), 'ia')
+ eq('ia', call('getreg', 'a'))
end)
it(":lmap not applied to mappings", function()
command('imap t l')
diff --git a/test/functional/options/mousescroll_spec.lua b/test/functional/options/mousescroll_spec.lua
new file mode 100644
index 0000000000..2c9b2d175e
--- /dev/null
+++ b/test/functional/options/mousescroll_spec.lua
@@ -0,0 +1,151 @@
+local helpers = require('test.functional.helpers')(after_each)
+local command = helpers.command
+local clear = helpers.clear
+local eval = helpers.eval
+local eq = helpers.eq
+local exc_exec = helpers.exc_exec
+local feed = helpers.feed
+
+local scroll = function(direction)
+ return helpers.request('nvim_input_mouse', 'wheel', direction, '', 0, 2, 2)
+end
+
+local screenrow = function()
+ return helpers.call('screenrow')
+end
+
+local screencol = function()
+ return helpers.call('screencol')
+end
+
+describe("'mousescroll'", function()
+ local invalid_arg = 'Vim(set):E474: Invalid argument: mousescroll='
+ local digit_expected = 'Vim(set):E548: digit expected: mousescroll='
+
+ local function should_fail(val, errorstr)
+ eq(errorstr..val, exc_exec('set mousescroll='..val))
+ end
+
+ local function should_succeed(val)
+ eq(0, exc_exec('set mousescroll='..val))
+ end
+
+ before_each(function()
+ clear()
+ command('set nowrap lines=20 columns=20 virtualedit=all')
+ feed('100o<Esc>50G10|')
+ end)
+
+ it('handles invalid values', function()
+ should_fail('', invalid_arg) -- empty string
+ should_fail('foo:123', invalid_arg) -- unknown direction
+ should_fail('hor:1,hor:2', invalid_arg) -- duplicate direction
+ should_fail('ver:99999999999999999999', invalid_arg) -- integer overflow
+ should_fail('ver:bar', digit_expected) -- expected digit
+ should_fail('ver:-1', digit_expected) -- negative count
+ end)
+
+ it('handles valid values', function()
+ should_succeed('hor:1,ver:1') -- both directions set
+ should_succeed('hor:1') -- only horizontal
+ should_succeed('ver:1') -- only vertical
+ should_succeed('hor:0,ver:0') -- zero
+ should_succeed('hor:2147483647') -- large count
+ end)
+
+ it('default set correctly', function()
+ eq('ver:3,hor:6', eval('&mousescroll'))
+
+ eq(10, screenrow())
+ scroll('up')
+ eq(13, screenrow())
+ scroll('down')
+ eq(10, screenrow())
+
+ eq(10, screencol())
+ scroll('right')
+ eq(4, screencol())
+ scroll('left')
+ eq(10, screencol())
+ end)
+
+ it('vertical scrolling falls back to default value', function()
+ command('set mousescroll=hor:1')
+ eq(10, screenrow())
+ scroll('up')
+ eq(13, screenrow())
+ end)
+
+ it('horizontal scrolling falls back to default value', function()
+ command('set mousescroll=ver:1')
+ eq(10, screencol())
+ scroll('right')
+ eq(4, screencol())
+ end)
+
+ it('count of zero disables mouse scrolling', function()
+ command('set mousescroll=hor:0,ver:0')
+
+ eq(10, screenrow())
+ scroll('up')
+ eq(10, screenrow())
+ scroll('down')
+ eq(10, screenrow())
+
+ eq(10, screencol())
+ scroll('right')
+ eq(10, screencol())
+ scroll('left')
+ eq(10, screencol())
+ end)
+
+ local test_vertical_scrolling = function()
+ eq(10, screenrow())
+
+ command('set mousescroll=ver:1')
+ scroll('up')
+ eq(11, screenrow())
+
+ command('set mousescroll=ver:2')
+ scroll('down')
+ eq(9, screenrow())
+
+ command('set mousescroll=ver:5')
+ scroll('up')
+ eq(14, screenrow())
+ end
+
+ it('controls vertical scrolling in normal mode', function()
+ test_vertical_scrolling()
+ end)
+
+ it('controls vertical scrolling in insert mode', function()
+ feed('i')
+ test_vertical_scrolling()
+ end)
+
+ local test_horizontal_scrolling = function()
+ eq(10, screencol())
+
+ command('set mousescroll=hor:1')
+ scroll('right')
+ eq(9, screencol())
+
+ command('set mousescroll=hor:3')
+ scroll('right')
+ eq(6, screencol())
+
+ command('set mousescroll=hor:2')
+ scroll('left')
+ eq(8, screencol())
+ end
+
+ it('controls horizontal scrolling in normal mode', function()
+ test_horizontal_scrolling()
+ end)
+
+ it('controls horizontal scrolling in insert mode', function()
+ feed('i')
+ test_horizontal_scrolling()
+ end)
+end)
diff --git a/test/functional/options/num_options_spec.lua b/test/functional/options/num_options_spec.lua
index 4754c14f5b..f343e2da75 100644
--- a/test/functional/options/num_options_spec.lua
+++ b/test/functional/options/num_options_spec.lua
@@ -54,7 +54,7 @@ describe(':set validation', function()
should_fail('iminsert', 3, 'E474')
should_fail('imsearch', 3, 'E474')
should_fail('titlelen', -1, 'E487')
- should_fail('cmdheight', 0, 'E487')
+ should_fail('cmdheight', -1, 'E487')
should_fail('updatecount', -1, 'E487')
should_fail('textwidth', -1, 'E487')
should_fail('tabstop', 0, 'E487')
diff --git a/test/functional/options/pastetoggle_spec.lua b/test/functional/options/pastetoggle_spec.lua
index a1f86f73ff..40c14fa187 100644
--- a/test/functional/options/pastetoggle_spec.lua
+++ b/test/functional/options/pastetoggle_spec.lua
@@ -4,16 +4,14 @@ local clear = helpers.clear
local feed = helpers.feed
local command = helpers.command
local eq = helpers.eq
+local expect = helpers.expect
local eval = helpers.eval
+local insert = helpers.insert
+local meths = helpers.meths
local sleep = helpers.sleep
-local expect = helpers.expect
describe("'pastetoggle' option", function()
- before_each(function()
- clear()
- command('set nopaste')
- end)
-
+ before_each(clear)
it("toggles 'paste'", function()
command('set pastetoggle=a')
eq(0, eval('&paste'))
@@ -22,19 +20,71 @@ describe("'pastetoggle' option", function()
feed('j')
eq(1, eval('&paste'))
end)
+ describe("multiple key 'pastetoggle'", function()
+ before_each(function()
+ eq(0, eval('&paste'))
+ command('set timeoutlen=1 ttimeoutlen=10000')
+ end)
+ it('is waited for when chars are typed', function()
+ local pastetoggle = 'lllll'
+ command('set pastetoggle=' .. pastetoggle)
+ feed(pastetoggle:sub(0, 2))
+ -- sleep() for long enough that vgetorpeek() is gotten into, but short
+ -- enough that ttimeoutlen is not reached.
+ sleep(200)
+ feed(pastetoggle:sub(3, -1))
+ -- Need another key so that the vgetorpeek() function returns.
+ feed('j')
+ eq(1, eval('&paste'))
+ end)
+ it('is not waited for when there are no typed chars after mapped chars', function()
+ command('set pastetoggle=abc')
+ command('imap d a')
+ meths.feedkeys('id', 't', true)
+ -- sleep() for long enough that vgetorpeek() is gotten into, but short
+ -- enough that ttimeoutlen is not reached.
+ sleep(200)
+ feed('bc')
+ -- Need another key so that the vgetorpeek() function returns.
+ feed('j')
+ -- 'ttimeoutlen' should NOT apply
+ eq(0, eval('&paste'))
+ end)
- it('does not wait for timeout', function()
- command('set pastetoggle=abc')
- command('set ttimeoutlen=9999999')
- eq(0, eval('&paste'))
- -- n.b. need <esc> to return from vgetorpeek()
- feed('abc<esc>')
- eq(1, eval('&paste'))
- feed('ab')
- sleep(10)
- feed('c<esc>')
- expect('bc')
- eq(1, eval('&paste'))
+ it('is waited for when there are typed chars after mapped chars', function()
+ command('set pastetoggle=abc')
+ command('imap d a')
+ meths.feedkeys('idb', 't', true)
+ -- sleep() for long enough that vgetorpeek() is gotten into, but short
+ -- enough that ttimeoutlen is not reached.
+ sleep(200)
+ feed('c')
+ -- Need another key so that the vgetorpeek() function returns.
+ feed('j')
+ -- 'ttimeoutlen' should apply
+ eq(1, eval('&paste'))
+ end)
+
+ it('is waited for when there are typed chars after noremapped chars', function()
+ command('set pastetoggle=abc')
+ command('inoremap d a')
+ meths.feedkeys('idb', 't', true)
+ -- sleep() for long enough that vgetorpeek() is gotten into, but short
+ -- enough that ttimeoutlen is not reached.
+ sleep(200)
+ feed('c')
+ -- Need another key so that the vgetorpeek() function returns.
+ feed('j')
+ -- 'ttimeoutlen' should apply
+ eq(1, eval('&paste'))
+ end)
+ end)
+ it('does not interfere with character-find', function()
+ insert('foo,bar')
+ feed('0')
+ command('set pastetoggle=,sp')
+ feed('dt,')
+ expect(',bar')
end)
end)
diff --git a/test/functional/plugin/health_spec.lua b/test/functional/plugin/health_spec.lua
index 37de5d0ce6..ba66117fb1 100644
--- a/test/functional/plugin/health_spec.lua
+++ b/test/functional/plugin/health_spec.lua
@@ -5,7 +5,7 @@ local Screen = require('test.functional.ui.screen')
local clear = helpers.clear
local curbuf_contents = helpers.curbuf_contents
local command = helpers.command
-local eq = helpers.eq
+local eq, neq = helpers.eq, helpers.neq
local getcompletion = helpers.funcs.getcompletion
describe(':checkhealth', function()
@@ -37,6 +37,7 @@ describe(':checkhealth', function()
eq('nvim', getcompletion('nvim', 'checkhealth')[1])
eq('provider', getcompletion('prov', 'checkhealth')[1])
eq('vim.lsp', getcompletion('vim.ls', 'checkhealth')[1])
+ neq('vim', getcompletion('^vim', 'checkhealth')[1]) -- should not complete vim.health
end)
end)
@@ -153,6 +154,10 @@ describe('health.vim', function()
## report 2
- OK: nothing to see here
+ test_plug.submodule_empty: require("test_plug.submodule_empty.health").check()
+ ========================================================================
+ - ERROR: The healthcheck report for "test_plug.submodule_empty" plugin is empty.
+
test_plug.submodule_failed: require("test_plug.submodule_failed.health").check()
========================================================================
- ERROR: Failed to run healthcheck for "test_plug.submodule_failed" plugin. Exception:
@@ -172,6 +177,16 @@ describe('health.vim', function()
]])
end)
+ it("... including empty reports", function()
+ command("checkhealth test_plug.submodule_empty")
+ helpers.expect([[
+
+ test_plug.submodule_empty: require("test_plug.submodule_empty.health").check()
+ ========================================================================
+ - ERROR: The healthcheck report for "test_plug.submodule_empty" plugin is empty.
+ ]])
+ end)
+
it("gracefully handles broken lua healthcheck", function()
command("checkhealth test_plug.submodule_failed")
local buf_lines = helpers.curbuf('get_lines', 0, -1, true)
@@ -228,5 +243,23 @@ describe('health.vim', function()
- ERROR: No healthcheck found for "non_existent_healthcheck" plugin.
]])
end)
+
+ it("does not use vim.health as a healtcheck", function()
+ -- vim.health is not a healthcheck
+ command("checkhealth vim")
+ helpers.expect([[
+ ERROR: No healthchecks found.]])
+ end)
+ end)
+end)
+
+describe(':checkhealth provider', function()
+ it("works correctly with a wrongly configured 'shell'", function()
+ clear()
+ command([[set shell=echo\ WRONG!!!]])
+ command('let g:loaded_perl_provider = 0')
+ command('let g:loaded_python3_provider = 0')
+ command('checkhealth provider')
+ eq(nil, string.match(curbuf_contents(), 'WRONG!!!'))
end)
end)
diff --git a/test/functional/plugin/lsp/diagnostic_spec.lua b/test/functional/plugin/lsp/diagnostic_spec.lua
index 1269a2350c..f73ffc29b0 100644
--- a/test/functional/plugin/lsp/diagnostic_spec.lua
+++ b/test/functional/plugin/lsp/diagnostic_spec.lua
@@ -3,6 +3,7 @@ local helpers = require('test.functional.helpers')(after_each)
local clear = helpers.clear
local exec_lua = helpers.exec_lua
local eq = helpers.eq
+local neq = require('test.helpers').neq
describe('vim.lsp.diagnostic', function()
local fake_uri
@@ -110,6 +111,7 @@ describe('vim.lsp.diagnostic', function()
}
]]
eq({code = 42, tags = {"foo", "bar"}, data = "Hello world"}, result[1].user_data.lsp)
+ eq(42, result[1].code)
eq(42, result[2].code)
eq({"foo", "bar"}, result[2].tags)
eq("Hello world", result[2].data)
@@ -219,12 +221,50 @@ describe('vim.lsp.diagnostic', function()
local diags = vim.diagnostic.get(diagnostic_bufnr)
vim.lsp.stop_client(client_id)
- vim.lsp._vim_exit_handler()
+ vim.api.nvim_exec_autocmds('VimLeavePre', { modeline = false })
return diags
]], line)
eq(1, #result)
eq(exec_lua([[return vim.str_byteindex(..., 7, true)]], line), result[1].col)
eq(exec_lua([[return vim.str_byteindex(..., 8, true)]], line), result[1].end_col)
end)
+
+ it('does not create buffer on empty diagnostics', function()
+ local bufnr
+
+ -- No buffer is created without diagnostics
+ bufnr = exec_lua [[
+ vim.lsp.diagnostic.on_publish_diagnostics(nil, {
+ uri = "file:///fake/uri2",
+ diagnostics = {},
+ }, {client_id=client_id})
+ return vim.fn.bufnr(vim.uri_to_fname("file:///fake/uri2"))
+ ]]
+ eq(bufnr, -1)
+
+ -- Create buffer on diagnostics
+ bufnr = exec_lua [[
+ vim.lsp.diagnostic.on_publish_diagnostics(nil, {
+ uri = "file:///fake/uri2",
+ diagnostics = {
+ make_error('Diagnostic', 0, 0, 0, 0),
+ },
+ }, {client_id=client_id})
+ return vim.fn.bufnr(vim.uri_to_fname("file:///fake/uri2"))
+ ]]
+ neq(bufnr, -1)
+ eq(exec_lua([[return #vim.diagnostic.get(...)]], bufnr), 1)
+
+ -- Clear diagnostics after buffer was created
+ bufnr = exec_lua [[
+ vim.lsp.diagnostic.on_publish_diagnostics(nil, {
+ uri = "file:///fake/uri2",
+ diagnostics = {},
+ }, {client_id=client_id})
+ return vim.fn.bufnr(vim.uri_to_fname("file:///fake/uri2"))
+ ]]
+ neq(bufnr, -1)
+ eq(exec_lua([[return #vim.diagnostic.get(...)]], bufnr), 0)
+ end)
end)
end)
diff --git a/test/functional/plugin/lsp/incremental_sync_spec.lua b/test/functional/plugin/lsp/incremental_sync_spec.lua
index 4e3eddb960..4985da9cd7 100644
--- a/test/functional/plugin/lsp/incremental_sync_spec.lua
+++ b/test/functional/plugin/lsp/incremental_sync_spec.lua
@@ -207,16 +207,16 @@ describe('incremental synchronization', function()
{
range = {
['start'] = {
- character = 0,
- line = 1
+ character = 11,
+ line = 0,
},
['end'] = {
character = 0,
line = 1
}
},
- rangeLength = 0,
- text = 'hello world\n'
+ rangeLength = 1,
+ text = '\nhello world\n'
}
}
test_edit({"hello world"}, {"yyp"}, expected_text_changes, 'utf-16', '\n')
@@ -226,19 +226,57 @@ describe('incremental synchronization', function()
{
range = {
['start'] = {
+ character = 11,
+ line = 0
+ },
+ ['end'] = {
character = 0,
line = 1
+ }
+ },
+ rangeLength = 1,
+ text = '\n\n'
+ }
+ }
+ test_edit({"hello world"}, {"o"}, expected_text_changes, 'utf-16', '\n')
+ end)
+ it('adding a line to an empty buffer', function()
+ local expected_text_changes = {
+ {
+ range = {
+ ['start'] = {
+ character = 0,
+ line = 0
},
['end'] = {
character = 0,
line = 1
}
},
+ rangeLength = 1,
+ text = '\n\n'
+ }
+ }
+ test_edit({""}, {"o"}, expected_text_changes, 'utf-16', '\n')
+ end)
+ it('insert a line above the current line', function()
+ local expected_text_changes = {
+ {
+ range = {
+ ['start'] = {
+ character = 0,
+ line = 0
+ },
+ ['end'] = {
+ character = 0,
+ line = 0
+ }
+ },
rangeLength = 0,
text = '\n'
}
}
- test_edit({"hello world"}, {"o"}, expected_text_changes, 'utf-16', '\n')
+ test_edit({""}, {"O"}, expected_text_changes, 'utf-16', '\n')
end)
end)
describe('multi line edit', function()
diff --git a/test/functional/plugin/lsp/snippet_spec.lua b/test/functional/plugin/lsp/snippet_spec.lua
index 4e127743eb..7903885420 100644
--- a/test/functional/plugin/lsp/snippet_spec.lua
+++ b/test/functional/plugin/lsp/snippet_spec.lua
@@ -19,9 +19,9 @@ describe('vim.lsp._snippet', function()
{
type = snippet.NodeType.TEXT,
raw = 'TE\\$\\}XT',
- esc = 'TE$}XT'
- }
- }
+ esc = 'TE$}XT',
+ },
+ },
}, parse('TE\\$\\}XT'))
end)
@@ -36,8 +36,8 @@ describe('vim.lsp._snippet', function()
{
type = snippet.NodeType.TABSTOP,
tabstop = 2,
- }
- }
+ },
+ },
}, parse('$1${2}'))
end)
@@ -56,7 +56,7 @@ describe('vim.lsp._snippet', function()
{
type = snippet.NodeType.TEXT,
raw = 'TE\\$\\}XT',
- esc = 'TE$}XT'
+ esc = 'TE$}XT',
},
{
type = snippet.NodeType.TABSTOP,
@@ -73,21 +73,21 @@ describe('vim.lsp._snippet', function()
{
type = snippet.NodeType.FORMAT,
capture_index = 1,
- modifier = 'upcase'
- }
- }
+ modifier = 'upcase',
+ },
+ },
},
},
{
type = snippet.NodeType.TEXT,
raw = 'TE\\$\\}XT',
- esc = 'TE$}XT'
+ esc = 'TE$}XT',
},
- }
- }
- }
+ },
+ },
+ },
},
- }
+ },
}, parse('${1:${2:TE\\$\\}XT$3${1/regex/${1:/upcase}/i}TE\\$\\}XT}}'))
end)
@@ -110,8 +110,8 @@ describe('vim.lsp._snippet', function()
{
type = snippet.NodeType.TABSTOP,
tabstop = 1,
- }
- }
+ },
+ },
},
{
type = snippet.NodeType.VARIABLE,
@@ -124,11 +124,11 @@ describe('vim.lsp._snippet', function()
type = snippet.NodeType.FORMAT,
capture_index = 1,
modifier = 'upcase',
- }
- }
- }
+ },
+ },
+ },
},
- }
+ },
}, parse('$VAR${VAR}${VAR:$1}${VAR/regex/${1:/upcase}/}'))
end)
@@ -141,12 +141,96 @@ describe('vim.lsp._snippet', function()
tabstop = 1,
items = {
',',
- '|'
- }
- }
- }
+ '|',
+ },
+ },
+ },
}, parse('${1|\\,,\\||}'))
end)
-end)
+ it('should parse format', function()
+ eq({
+ type = snippet.NodeType.SNIPPET,
+ children = {
+ {
+ type = snippet.NodeType.VARIABLE,
+ name = 'VAR',
+ transform = {
+ type = snippet.NodeType.TRANSFORM,
+ pattern = 'regex',
+ format = {
+ {
+ type = snippet.NodeType.FORMAT,
+ capture_index = 1,
+ modifier = 'upcase',
+ },
+ {
+ type = snippet.NodeType.FORMAT,
+ capture_index = 1,
+ if_text = 'if_text',
+ else_text = '',
+ },
+ {
+ type = snippet.NodeType.FORMAT,
+ capture_index = 1,
+ if_text = '',
+ else_text = 'else_text',
+ },
+ {
+ type = snippet.NodeType.FORMAT,
+ capture_index = 1,
+ else_text = 'else_text',
+ if_text = 'if_text',
+ },
+ {
+ type = snippet.NodeType.FORMAT,
+ capture_index = 1,
+ if_text = '',
+ else_text = 'else_text',
+ },
+ },
+ },
+ },
+ },
+ }, parse('${VAR/regex/${1:/upcase}${1:+if_text}${1:-else_text}${1:?if_text:else_text}${1:else_text}/}'))
+ end)
+ it('should parse empty strings', function()
+ eq({
+ children = {
+ {
+ children = { {
+ esc = '',
+ raw = '',
+ type = 7,
+ } },
+ tabstop = 1,
+ type = 2,
+ },
+ {
+ esc = ' ',
+ raw = ' ',
+ type = 7,
+ },
+ {
+ name = 'VAR',
+ transform = {
+ format = {
+ {
+ capture_index = 1,
+ else_text = '',
+ if_text = '',
+ type = 6,
+ },
+ },
+ option = 'g',
+ pattern = 'erg',
+ type = 5,
+ },
+ type = 3,
+ },
+ },
+ type = 0,
+ }, parse('${1:} ${VAR/erg/${1:?:}/g}'))
+ end)
+end)
diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua
index 7ad1a52fe3..c166982052 100644
--- a/test/functional/plugin/lsp_spec.lua
+++ b/test/functional/plugin/lsp_spec.lua
@@ -18,6 +18,7 @@ local NIL = helpers.NIL
local read_file = require('test.helpers').read_file
local write_file = require('test.helpers').write_file
local isCI = helpers.isCI
+local meths = helpers.meths
-- Use these to get access to a coroutine so that I can run async tests and use
-- yield.
@@ -44,10 +45,10 @@ local function clear_notrace()
end
-local function fake_lsp_server_setup(test_name, timeout_ms, options)
+local function fake_lsp_server_setup(test_name, timeout_ms, options, settings)
exec_lua([=[
lsp = require('vim.lsp')
- local test_name, fixture_filename, logfile, timeout, options = ...
+ local test_name, fixture_filename, logfile, timeout, options, settings = ...
TEST_RPC_CLIENT_ID = lsp.start_client {
cmd_env = {
NVIM_LOG_FILE = logfile;
@@ -78,17 +79,18 @@ local function fake_lsp_server_setup(test_name, timeout_ms, options)
allow_incremental_sync = options.allow_incremental_sync or false;
debounce_text_changes = options.debounce_text_changes or 0;
};
+ settings = settings;
on_exit = function(...)
vim.rpcnotify(1, "exit", ...)
end;
}
- ]=], test_name, fake_lsp_code, fake_lsp_logfile, timeout_ms or 1e3, options or {})
+ ]=], test_name, fake_lsp_code, fake_lsp_logfile, timeout_ms or 1e3, options or {}, settings or {})
end
local function test_rpc_server(config)
if config.test_name then
clear_notrace()
- fake_lsp_server_setup(config.test_name, config.timeout_ms or 1e3, config.options)
+ fake_lsp_server_setup(config.test_name, config.timeout_ms or 1e3, config.options, config.settings)
end
local client = setmetatable({}, {
__index = function(_, name)
@@ -135,7 +137,7 @@ local function test_rpc_server(config)
end
stop()
if config.test_name then
- exec_lua("lsp._vim_exit_handler()")
+ exec_lua("vim.api.nvim_exec_autocmds('VimLeavePre', { modeline = false })")
end
end
@@ -170,7 +172,7 @@ describe('LSP', function()
end)
after_each(function()
- exec_lua("lsp._vim_exit_handler()")
+ exec_lua("vim.api.nvim_exec_autocmds('VimLeavePre', { modeline = false })")
-- exec_lua("lsp.stop_all_clients(true)")
end)
@@ -221,11 +223,33 @@ describe('LSP', function()
end)
end)
+ describe('lsp._cmd_parts test', function()
+ local function _cmd_parts(input)
+ return exec_lua([[
+ lsp = require('vim.lsp')
+ return lsp._cmd_parts(...)
+ ]], input)
+ end
+ it('should valid cmd argument', function()
+ eq(true, pcall(_cmd_parts, {"nvim"}))
+ eq(true, pcall(_cmd_parts, {"nvim", "--head"}))
+ end)
+
+ it('should invalid cmd argument', function()
+ eq('Error executing lua: .../lsp.lua:0: cmd: expected list, got nvim',
+ pcall_err(_cmd_parts, 'nvim'))
+ eq('Error executing lua: .../lsp.lua:0: cmd argument: expected string, got number',
+ pcall_err(_cmd_parts, {'nvim', 1}))
+ end)
+ end)
+end)
+
+describe('LSP', function()
describe('basic_init test', function()
after_each(function()
stop()
- exec_lua("lsp.stop_client(lsp.get_active_clients())")
- exec_lua("lsp._vim_exit_handler()")
+ exec_lua("lsp.stop_client(lsp.get_active_clients(), true)")
+ exec_lua("vim.api.nvim_exec_autocmds('VimLeavePre', { modeline = false })")
end)
it('should run correctly', function()
@@ -242,8 +266,8 @@ describe('LSP', function()
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)
+ eq(0, code, "exit code")
+ eq(0, signal, "exit signal")
end;
-- Note that NIL must be used here.
-- on_handler(err, method, result, client_id)
@@ -264,8 +288,8 @@ describe('LSP', function()
client.stop()
end;
on_exit = function(code, signal)
- eq(101, code, "exit code", fake_lsp_logfile) -- See fake-lsp-server.lua
- eq(0, signal, "exit signal", fake_lsp_logfile)
+ eq(101, code, "exit code") -- See fake-lsp-server.lua
+ eq(0, signal, "exit signal")
assert_log(pesc([[assert_eq failed: left == "\"shutdown\"", right == "\"test\""]]),
fake_lsp_logfile)
end;
@@ -275,6 +299,22 @@ describe('LSP', function()
}
end)
+ it('should send didChangeConfiguration after initialize if there are settings', function()
+ test_rpc_server({
+ test_name = 'basic_init_did_change_configuration',
+ on_init = function(client, _)
+ client.stop()
+ end,
+ on_exit = function(code, signal)
+ eq(0, code, 'exit code', fake_lsp_logfile)
+ eq(0, signal, 'exit signal', fake_lsp_logfile)
+ end,
+ settings = {
+ dummy = 1,
+ },
+ })
+ end)
+
it('should succeed with manual shutdown', function()
if isCI() then
pending('hangs the build on CI #14028, re-enable with freeze timeout #14204')
@@ -289,14 +329,14 @@ describe('LSP', function()
test_rpc_server {
test_name = "basic_init";
on_init = function(client)
- eq(0, client.resolved_capabilities().text_document_did_change)
+ eq(0, client.server_capabilities().textDocumentSync.change)
client.request('shutdown')
client.notify('exit')
client.stop()
end;
on_exit = function(code, signal)
- eq(0, code, "exit code", fake_lsp_logfile)
- eq(0, signal, "exit signal", fake_lsp_logfile)
+ eq(0, code, "exit code")
+ eq(0, signal, "exit signal")
end;
on_handler = function(...)
eq(table.remove(expected_handlers), {...}, "expected handler")
@@ -327,8 +367,8 @@ describe('LSP', function()
client.notify('finish')
end;
on_exit = function(code, signal)
- eq(0, code, "exit code", fake_lsp_logfile)
- eq(0, signal, "exit signal", fake_lsp_logfile)
+ eq(0, code, "exit code")
+ eq(0, signal, "exit signal")
end;
on_handler = function(err, result, ctx)
eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
@@ -341,12 +381,51 @@ describe('LSP', function()
}
end)
+ it('should fire autocommands on attach and detach', function()
+ local client
+ test_rpc_server {
+ test_name = "basic_init";
+ on_setup = function()
+ exec_lua [[
+ BUFFER = vim.api.nvim_create_buf(false, true)
+ vim.api.nvim_create_autocmd('LspAttach', {
+ callback = function(args)
+ local client = vim.lsp.get_client_by_id(args.data.client_id)
+ vim.g.lsp_attached = client.name
+ end,
+ })
+ vim.api.nvim_create_autocmd('LspDetach', {
+ callback = function(args)
+ local client = vim.lsp.get_client_by_id(args.data.client_id)
+ vim.g.lsp_detached = client.name
+ end,
+ })
+ ]]
+ end;
+ on_init = function(_client)
+ client = _client
+ eq(true, exec_lua("return lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)"))
+ client.notify('finish')
+ end;
+ on_handler = function(_, _, ctx)
+ if ctx.method == 'finish' then
+ eq('basic_init', meths.get_var('lsp_attached'))
+ exec_lua("return lsp.buf_detach_client(BUFFER, TEST_RPC_CLIENT_ID)")
+ eq('basic_init', meths.get_var('lsp_detached'))
+ client.stop()
+ end
+ end;
+ }
+ end)
+
it('client should return settings via workspace/configuration handler', function()
local expected_handlers = {
{NIL, {}, {method="shutdown", client_id=1}};
{NIL, { items = {
{ section = "testSetting1" };
{ section = "testSetting2" };
+ { section = "test.Setting3" };
+ { section = "test.Setting4" };
}}, { method="workspace/configuration", client_id=1}};
{NIL, {}, {method="start", client_id=1}};
}
@@ -357,8 +436,8 @@ describe('LSP', function()
client = _client
end;
on_exit = function(code, signal)
- eq(0, code, "exit code", fake_lsp_logfile)
- eq(0, signal, "exit signal", fake_lsp_logfile)
+ eq(0, code, "exit code")
+ eq(0, signal, "exit signal")
end;
on_handler = function(err, result, ctx)
eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
@@ -368,6 +447,7 @@ describe('LSP', function()
client.config.settings = {
testSetting1 = true;
testSetting2 = false;
+ test = {Setting3 = 'nested' };
}]=])
end
if ctx.method == 'workspace/configuration' then
@@ -383,16 +463,23 @@ describe('LSP', function()
}
end)
it('workspace/configuration returns NIL per section if client was started without config.settings', function()
- fake_lsp_server_setup('workspace/configuration no settings')
- eq({ NIL, NIL, }, exec_lua [[
- local result = {
- items = {
- {section = 'foo'},
- {section = 'bar'},
- }
- }
- return vim.lsp.handlers['workspace/configuration'](nil, result, {client_id=TEST_RPC_CLIENT_ID})
- ]])
+ local result = nil
+ test_rpc_server {
+ test_name = 'basic_init';
+ on_init = function(c) c.stop() end,
+ on_setup = function()
+ result = exec_lua [[
+ local result = {
+ items = {
+ {section = 'foo'},
+ {section = 'bar'},
+ }
+ }
+ return vim.lsp.handlers['workspace/configuration'](nil, result, {client_id=TEST_RPC_CLIENT_ID})
+ ]]
+ end
+ }
+ eq({ NIL, NIL }, result)
end)
it('should verify capabilities sent', function()
@@ -404,14 +491,13 @@ describe('LSP', function()
on_init = function(client)
client.stop()
local full_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Full")
- eq(full_kind, client.resolved_capabilities().text_document_did_change)
- eq(true, client.resolved_capabilities().text_document_save)
- eq(false, client.resolved_capabilities().code_lens)
- eq(false, client.resolved_capabilities().code_lens_resolve)
+ eq(full_kind, client.server_capabilities().textDocumentSync.change)
+ eq({includeText = false}, client.server_capabilities().textDocumentSync.save)
+ eq(false, client.server_capabilities().codeLensProvider)
end;
on_exit = function(code, signal)
- eq(0, code, "exit code", fake_lsp_logfile)
- eq(0, signal, "exit signal", fake_lsp_logfile)
+ eq(0, code, "exit code")
+ eq(0, signal, "exit signal")
end;
on_handler = function(...)
eq(table.remove(expected_handlers), {...}, "expected handler")
@@ -419,6 +505,67 @@ describe('LSP', function()
}
end)
+ it('BufWritePost sends didSave with bool textDocumentSync.save', function()
+ local expected_handlers = {
+ {NIL, {}, {method="shutdown", client_id=1}};
+ {NIL, {}, {method="start", client_id=1}};
+ }
+ local client
+ test_rpc_server {
+ test_name = "text_document_sync_save_bool";
+ on_init = function(c)
+ client = c
+ end;
+ on_exit = function(code, signal)
+ eq(0, code, "exit code")
+ eq(0, signal, "exit signal")
+ end;
+ on_handler = function(err, result, ctx)
+ eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
+ if ctx.method == "start" then
+ exec_lua([=[
+ BUFFER = vim.api.nvim_get_current_buf()
+ lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)
+ vim.api.nvim_exec_autocmds('BufWritePost', { buffer = BUFFER, modeline = false })
+ ]=])
+ else
+ client.stop()
+ end
+ end;
+ }
+ end)
+
+ it('BufWritePost sends didSave including text if server capability is set', function()
+ local expected_handlers = {
+ {NIL, {}, {method="shutdown", client_id=1}};
+ {NIL, {}, {method="start", client_id=1}};
+ }
+ local client
+ test_rpc_server {
+ test_name = "text_document_sync_save_includeText";
+ on_init = function(c)
+ client = c
+ end;
+ on_exit = function(code, signal)
+ eq(0, code, "exit code")
+ eq(0, signal, "exit signal")
+ end;
+ on_handler = function(err, result, ctx)
+ eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
+ if ctx.method == "start" then
+ exec_lua([=[
+ BUFFER = vim.api.nvim_get_current_buf()
+ vim.api.nvim_buf_set_lines(BUFFER, 0, -1, true, {"help me"})
+ lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)
+ vim.api.nvim_exec_autocmds('BufWritePost', { buffer = BUFFER, modeline = false })
+ ]=])
+ else
+ client.stop()
+ end
+ end;
+ }
+ end)
+
it('client.supports_methods() should validate capabilities', function()
local expected_handlers = {
{NIL, {}, {method="shutdown", client_id=1}};
@@ -427,14 +574,19 @@ describe('LSP', function()
test_name = "capabilities_for_client_supports_method";
on_init = function(client)
client.stop()
- local full_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Full")
- eq(full_kind, client.resolved_capabilities().text_document_did_change)
- eq(true, client.resolved_capabilities().completion)
- eq(true, client.resolved_capabilities().hover)
- eq(false, client.resolved_capabilities().goto_definition)
- eq(false, client.resolved_capabilities().rename)
- eq(true, client.resolved_capabilities().code_lens)
- eq(true, client.resolved_capabilities().code_lens_resolve)
+ local expected_sync_capabilities = {
+ change = 1,
+ openClose = true,
+ save = { includeText = false },
+ willSave = false,
+ willSaveWaitUntil = false,
+ }
+ eq(expected_sync_capabilities, client.server_capabilities().textDocumentSync)
+ eq(true, client.server_capabilities().completionProvider)
+ eq(true, client.server_capabilities().hoverProvider)
+ eq(false, client.server_capabilities().definitionProvider)
+ eq(false, client.server_capabilities().renameProvider)
+ eq(true, client.server_capabilities().codeLensProvider.resolveProvider)
-- known methods for resolved capabilities
eq(true, client.supports_method("textDocument/hover"))
@@ -444,8 +596,8 @@ describe('LSP', function()
eq(true, client.supports_method("unknown-method"))
end;
on_exit = function(code, signal)
- eq(0, code, "exit code", fake_lsp_logfile)
- eq(0, signal, "exit signal", fake_lsp_logfile)
+ eq(0, code, "exit code")
+ eq(0, signal, "exit signal")
end;
on_handler = function(...)
eq(table.remove(expected_handlers), {...}, "expected handler")
@@ -474,8 +626,8 @@ describe('LSP', function()
]]
end;
on_exit = function(code, signal)
- eq(0, code, "exit code", fake_lsp_logfile)
- eq(0, signal, "exit signal", fake_lsp_logfile)
+ eq(0, code, "exit code")
+ eq(0, signal, "exit signal")
end;
on_handler = function(...)
eq(table.remove(expected_handlers), {...}, "expected handler")
@@ -499,8 +651,8 @@ describe('LSP', function()
exec_lua("vim.lsp.buf.type_definition()")
end;
on_exit = function(code, signal)
- eq(0, code, "exit code", fake_lsp_logfile)
- eq(0, signal, "exit signal", fake_lsp_logfile)
+ eq(0, code, "exit code")
+ eq(0, signal, "exit signal")
end;
on_handler = function(...)
eq(table.remove(expected_handlers), {...}, "expected handler")
@@ -520,8 +672,8 @@ describe('LSP', function()
client = _client
end;
on_exit = function(code, signal)
- eq(0, code, "exit code", fake_lsp_logfile)
- eq(0, signal, "exit signal", fake_lsp_logfile)
+ eq(0, code, "exit code")
+ eq(0, signal, "exit signal")
eq(0, #expected_handlers, "did not call expected handler")
end;
on_handler = function(err, _, ctx)
@@ -544,8 +696,8 @@ describe('LSP', function()
client = _client
end;
on_exit = function(code, signal)
- eq(0, code, "exit code", fake_lsp_logfile)
- eq(0, signal, "exit signal", fake_lsp_logfile)
+ eq(0, code, "exit code")
+ eq(0, signal, "exit signal")
eq(0, #expected_handlers, "did not call expected handler")
end;
on_handler = function(err, _, ctx)
@@ -574,8 +726,8 @@ describe('LSP', function()
client.notify("release")
end;
on_exit = function(code, signal)
- eq(0, code, "exit code", fake_lsp_logfile)
- eq(0, signal, "exit signal", fake_lsp_logfile)
+ eq(0, code, "exit code")
+ eq(0, signal, "exit signal")
eq(0, #expected_handlers, "did not call expected handler")
end;
on_handler = function(err, _, ctx)
@@ -607,8 +759,8 @@ describe('LSP', function()
client.notify("release")
end;
on_exit = function(code, signal)
- eq(0, code, "exit code", fake_lsp_logfile)
- eq(0, signal, "exit signal", fake_lsp_logfile)
+ eq(0, code, "exit code")
+ eq(0, signal, "exit signal")
eq(0, #expected_handlers, "did not call expected handler")
end;
on_handler = function(err, _, ctx)
@@ -641,8 +793,8 @@ describe('LSP', function()
client.notify("release")
end;
on_exit = function(code, signal)
- eq(0, code, "exit code", fake_lsp_logfile)
- eq(0, signal, "exit signal", fake_lsp_logfile)
+ eq(0, code, "exit code")
+ eq(0, signal, "exit signal")
eq(0, #expected_handlers, "did not call expected handler")
end;
on_handler = function(err, _, ctx)
@@ -676,8 +828,8 @@ describe('LSP', function()
client.notify("release")
end;
on_exit = function(code, signal)
- eq(0, code, "exit code", fake_lsp_logfile)
- eq(0, signal, "exit signal", fake_lsp_logfile)
+ eq(0, code, "exit code")
+ eq(0, signal, "exit signal")
eq(0, #expected_handlers, "did not call expected handler")
eq(3, eval('g:requests'))
end;
@@ -717,13 +869,13 @@ describe('LSP', function()
on_init = function(_client)
client = _client
local full_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Full")
- eq(full_kind, client.resolved_capabilities().text_document_did_change)
- eq(true, client.resolved_capabilities().text_document_open_close)
+ eq(full_kind, client.server_capabilities().textDocumentSync.change)
+ eq(true, client.server_capabilities().textDocumentSync.openClose)
client.notify('finish')
end;
on_exit = function(code, signal)
- eq(0, code, "exit code", fake_lsp_logfile)
- eq(0, signal, "exit signal", fake_lsp_logfile)
+ eq(0, code, "exit code")
+ eq(0, signal, "exit signal")
end;
on_handler = function(err, result, ctx)
eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
@@ -758,15 +910,15 @@ describe('LSP', function()
on_init = function(_client)
client = _client
local full_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Full")
- eq(full_kind, client.resolved_capabilities().text_document_did_change)
- eq(true, client.resolved_capabilities().text_document_open_close)
+ eq(full_kind, client.server_capabilities().textDocumentSync.change)
+ eq(true, client.server_capabilities().textDocumentSync.openClose)
exec_lua [[
assert(not lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID), "Shouldn't attach twice")
]]
end;
on_exit = function(code, signal)
- eq(0, code, "exit code", fake_lsp_logfile)
- eq(0, signal, "exit signal", fake_lsp_logfile)
+ eq(0, code, "exit code")
+ eq(0, signal, "exit signal")
end;
on_handler = function(err, result, ctx)
if ctx.method == 'start' then
@@ -801,15 +953,15 @@ describe('LSP', function()
on_init = function(_client)
client = _client
local full_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Full")
- eq(full_kind, client.resolved_capabilities().text_document_did_change)
- eq(true, client.resolved_capabilities().text_document_open_close)
+ eq(full_kind, client.server_capabilities().textDocumentSync.change)
+ eq(true, client.server_capabilities().textDocumentSync.openClose)
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)
+ eq(0, code, "exit code")
+ eq(0, signal, "exit signal")
end;
on_handler = function(err, result, ctx)
if ctx.method == 'start' then
@@ -844,15 +996,15 @@ describe('LSP', function()
on_init = function(_client)
client = _client
local full_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Full")
- eq(full_kind, client.resolved_capabilities().text_document_did_change)
- eq(true, client.resolved_capabilities().text_document_open_close)
+ eq(full_kind, client.server_capabilities().textDocumentSync.change)
+ eq(true, client.server_capabilities().textDocumentSync.openClose)
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)
+ eq(0, code, "exit code")
+ eq(0, signal, "exit signal")
end;
on_handler = function(err, result, ctx)
if ctx.method == 'start' then
@@ -893,15 +1045,15 @@ describe('LSP', function()
on_init = function(_client)
client = _client
local full_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Full")
- eq(full_kind, client.resolved_capabilities().text_document_did_change)
- eq(true, client.resolved_capabilities().text_document_open_close)
+ eq(full_kind, client.server_capabilities().textDocumentSync.change)
+ eq(true, client.server_capabilities().textDocumentSync.openClose)
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)
+ eq(0, code, "exit code")
+ eq(0, signal, "exit signal")
end;
on_handler = function(err, result, ctx)
if ctx.method == 'start' then
@@ -944,15 +1096,15 @@ describe('LSP', function()
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)
+ eq(sync_kind, client.server_capabilities().textDocumentSync.change)
+ eq(true, client.server_capabilities().textDocumentSync.openClose)
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)
+ eq(0, code, "exit code")
+ eq(0, signal, "exit signal")
end;
on_handler = function(err, result, ctx)
if ctx.method == 'start' then
@@ -995,15 +1147,15 @@ describe('LSP', function()
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)
+ eq(sync_kind, client.server_capabilities().textDocumentSync.change)
+ eq(true, client.server_capabilities().textDocumentSync.openClose)
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)
+ eq(0, code, "exit code")
+ eq(0, signal, "exit signal")
end;
on_handler = function(err, result, ctx)
if ctx.method == 'start' then
@@ -1044,15 +1196,15 @@ describe('LSP', function()
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)
+ eq(sync_kind, client.server_capabilities().textDocumentSync.change)
+ eq(true, client.server_capabilities().textDocumentSync.openClose)
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)
+ eq(0, code, "exit code")
+ eq(0, signal, "exit signal")
end;
on_handler = function(err, result, ctx)
if ctx.method == 'start' then
@@ -1088,15 +1240,15 @@ describe('LSP', function()
on_init = function(_client)
client = _client
local sync_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Full")
- eq(sync_kind, client.resolved_capabilities().text_document_did_change)
- eq(true, client.resolved_capabilities().text_document_open_close)
+ eq(sync_kind, client.server_capabilities().textDocumentSync.change)
+ eq(true, client.server_capabilities().textDocumentSync.openClose)
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)
+ eq(0, code, "exit code")
+ eq(0, signal, "exit signal")
end;
on_handler = function(err, result, ctx)
if ctx.method == 'start' then
@@ -1139,15 +1291,15 @@ describe('LSP', function()
on_init = function(_client)
client = _client
local sync_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Full")
- eq(sync_kind, client.resolved_capabilities().text_document_did_change)
- eq(true, client.resolved_capabilities().text_document_open_close)
+ eq(sync_kind, client.server_capabilities().textDocumentSync.change)
+ eq(true, client.server_capabilities().textDocumentSync.openClose)
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)
+ eq(0, code, "exit code")
+ eq(0, signal, "exit signal")
end;
on_handler = function(err, result,ctx)
if ctx.method == 'start' then
@@ -1188,8 +1340,8 @@ describe('LSP', function()
client.stop(true)
end;
on_exit = function(code, signal)
- eq(0, code, "exit code", fake_lsp_logfile)
- eq(0, signal, "exit signal", fake_lsp_logfile)
+ eq(0, code, "exit code")
+ eq(0, signal, "exit signal")
end;
on_handler = function(err, result, ctx)
eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
@@ -1227,8 +1379,8 @@ describe('LSP', function()
]]
end;
on_exit = function(code, signal)
- eq(0, code, "exit code", fake_lsp_logfile)
- eq(0, signal, "exit signal", fake_lsp_logfile)
+ eq(0, code, "exit code")
+ eq(0, signal, "exit signal")
end;
on_handler = function(err, result, ctx)
eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
@@ -1239,25 +1391,6 @@ describe('LSP', function()
}
end)
end)
- describe('lsp._cmd_parts test', function()
- local function _cmd_parts(input)
- return exec_lua([[
- lsp = require('vim.lsp')
- return lsp._cmd_parts(...)
- ]], input)
- end
- it('should valid cmd argument', function()
- eq(true, pcall(_cmd_parts, {"nvim"}))
- eq(true, pcall(_cmd_parts, {"nvim", "--head"}))
- end)
-
- it('should invalid cmd argument', function()
- eq('Error executing lua: .../lsp.lua:0: cmd: expected list, got nvim',
- pcall_err(_cmd_parts, 'nvim'))
- eq('Error executing lua: .../lsp.lua:0: cmd argument: expected string, got number',
- pcall_err(_cmd_parts, {'nvim', 1}))
- end)
- end)
end)
describe('LSP', function()
@@ -1291,7 +1424,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';
@@ -1311,7 +1444,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';
@@ -1335,7 +1468,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';
@@ -1352,7 +1485,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';
@@ -1365,7 +1498,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';
@@ -1375,6 +1508,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()
@@ -1382,7 +1529,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';
@@ -1399,7 +1546,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';
'';
@@ -1416,7 +1563,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))
@@ -1428,7 +1575,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';
@@ -1443,7 +1590,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';
@@ -1459,7 +1606,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';
@@ -1475,21 +1622,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)
@@ -1517,7 +1664,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';
@@ -1529,7 +1676,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';
@@ -1542,7 +1689,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
@@ -1568,17 +1715,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")
+ eq(0, signal, "exit signal")
+ 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)
@@ -1652,7 +1818,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))
@@ -1674,7 +1840,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))
@@ -1691,7 +1857,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()
@@ -1709,7 +1875,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)
@@ -1729,7 +1895,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)
@@ -1750,7 +1916,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)
@@ -1911,7 +2077,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)
@@ -1941,7 +2107,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)
@@ -2245,7 +2411,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('.')]],
@@ -2319,19 +2485,38 @@ 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)
+ local function test_tabstop(tabsize, shiftwidth)
exec_lua(string.format([[
- vim.api.nvim_buf_set_option(0, 'softtabstop', %d)
+ vim.api.nvim_buf_set_option(0, 'shiftwidth', %d)
vim.api.nvim_buf_set_option(0, 'tabstop', 2)
- vim.api.nvim_buf_set_option(0, 'shiftwidth', 3)
- ]], softtabstop))
+ ]], shiftwidth))
eq(tabsize, exec_lua('return vim.lsp.util.get_effective_tabstop()'))
end
- it('with softtabstop = 1', function() test_tabstop(1, 1) end)
- it('with softtabstop = 0', function() test_tabstop(2, 0) end)
- it('with softtabstop = -1', function() test_tabstop(3, -1) end)
+ it('with shiftwidth = 1', function() test_tabstop(1, 1) end)
+ it('with shiftwidth = 0', function() test_tabstop(2, 0) end)
end)
describe('vim.lsp.buf.outgoing_calls', function()
@@ -2516,10 +2701,8 @@ describe('LSP', function()
name = "prepare_rename_error",
expected_handlers = {
{NIL, {}, {method="shutdown", client_id=1}};
- {NIL, NIL, {method="textDocument/rename", client_id=1, bufnr=1}};
{NIL, {}, {method="start", client_id=1}};
},
- expected_text = "two", -- see test case
},
}) do
it(test.it, function()
@@ -2528,7 +2711,7 @@ describe('LSP', function()
test_name = test.name;
on_init = function(_client)
client = _client
- eq(true, client.resolved_capabilities().rename)
+ eq(true, client.server_capabilities().renameProvider.prepareProvider)
end;
on_setup = function()
exec_lua([=[
@@ -2545,8 +2728,8 @@ describe('LSP', function()
]=])
end;
on_exit = function(code, signal)
- eq(0, code, "exit code", fake_lsp_logfile)
- eq(0, signal, "exit signal", fake_lsp_logfile)
+ eq(0, code, "exit code")
+ eq(0, signal, "exit signal")
end;
on_handler = function(err, result, ctx)
-- Don't compare & assert params, they're not relevant for the testcase
@@ -2585,8 +2768,8 @@ describe('LSP', function()
on_setup = function()
end,
on_exit = function(code, signal)
- eq(0, code, "exit code", fake_lsp_logfile)
- eq(0, signal, "exit signal", fake_lsp_logfile)
+ eq(0, code, "exit code")
+ eq(0, signal, "exit signal")
end,
on_handler = function(err, result, ctx)
eq(table.remove(expected_handlers), {err, result, ctx})
@@ -2610,6 +2793,102 @@ describe('LSP', function()
end
}
end)
+ it('Calls workspace/executeCommand if no client side command', function()
+ local client
+ local expected_handlers = {
+ { NIL, {}, { method = 'shutdown', client_id = 1 } },
+ {
+ NIL,
+ { command = 'dummy1', title = 'Command 1' },
+ { bufnr = 1, method = 'workspace/executeCommand', client_id = 1 },
+ },
+ { NIL, {}, { method = 'start', client_id = 1 } },
+ }
+ test_rpc_server({
+ test_name = 'code_action_server_side_command',
+ on_init = function(client_)
+ client = client_
+ end,
+ on_setup = function() 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)
+ ctx.params = nil -- don't compare in assert
+ eq(table.remove(expected_handlers), { err, result, ctx })
+ if ctx.method == 'start' then
+ exec_lua([[
+ local bufnr = vim.api.nvim_get_current_buf()
+ vim.lsp.buf_attach_client(bufnr, TEST_RPC_CLIENT_ID)
+ vim.fn.inputlist = function()
+ return 1
+ end
+ vim.lsp.buf.code_action()
+ ]])
+ elseif ctx.method == 'shutdown' then
+ client.stop()
+ end
+ end,
+ })
+ end)
+ it('Filters and automatically applies action if requested', function()
+ local client
+ local expected_handlers = {
+ {NIL, {}, {method="shutdown", client_id=1}};
+ {NIL, {}, {method="start", client_id=1}};
+ }
+ test_rpc_server {
+ test_name = 'code_action_filter',
+ on_init = function(client_)
+ client = client_
+ end,
+ on_setup = function()
+ end,
+ on_exit = function(code, signal)
+ eq(0, code, "exit code")
+ eq(0, signal, "exit signal")
+ end,
+ on_handler = function(err, result, ctx)
+ eq(table.remove(expected_handlers), {err, result, ctx})
+ if ctx.method == 'start' then
+ exec_lua([[
+ vim.lsp.commands['preferred_command'] = function(cmd)
+ vim.lsp.commands['executed_preferred'] = function()
+ end
+ end
+ vim.lsp.commands['quickfix_command'] = function(cmd)
+ vim.lsp.commands['executed_quickfix'] = function()
+ end
+ end
+ local bufnr = vim.api.nvim_get_current_buf()
+ vim.lsp.buf_attach_client(bufnr, TEST_RPC_CLIENT_ID)
+ vim.lsp.buf.code_action({ filter = function(a) return a.isPreferred end, apply = true, })
+ vim.lsp.buf.code_action({
+ -- expect to be returned actions 'quickfix' and 'quickfix.foo'
+ context = { only = {'quickfix'}, },
+ apply = true,
+ filter = function(a)
+ if a.kind == 'quickfix.foo' then
+ vim.lsp.commands['filtered_quickfix_foo'] = function() end
+ return false
+ elseif a.kind == 'quickfix' then
+ return true
+ else
+ assert(nil, 'unreachable')
+ end
+ end,
+ })
+ ]])
+ elseif ctx.method == 'shutdown' then
+ eq('function', exec_lua[[return type(vim.lsp.commands['executed_preferred'])]])
+ eq('function', exec_lua[[return type(vim.lsp.commands['filtered_quickfix_foo'])]])
+ eq('function', exec_lua[[return type(vim.lsp.commands['executed_quickfix'])]])
+ client.stop()
+ end
+ end
+ }
+ end)
end)
describe('vim.lsp.commands', function()
it('Accepts only string keys', function()
@@ -2640,8 +2919,8 @@ describe('LSP', function()
on_setup = function()
end,
on_exit = function(code, signal)
- eq(0, code, "exit code", fake_lsp_logfile)
- eq(0, signal, "exit signal", fake_lsp_logfile)
+ eq(0, code, "exit code")
+ eq(0, signal, "exit signal")
end,
on_handler = function(err, result, ctx)
eq(table.remove(expected_handlers), {err, result, ctx})
@@ -2677,5 +2956,189 @@ describe('LSP', function()
end
}
end)
+
+ it('releases buffer refresh lock', function()
+ local client
+ local expected_handlers = {
+ {NIL, {}, {method="shutdown", client_id=1}};
+ {NIL, {}, {method="start", client_id=1}};
+ }
+ test_rpc_server {
+ test_name = 'codelens_refresh_lock',
+ on_init = function(client_)
+ client = client_
+ end,
+ on_setup = function()
+ exec_lua([=[
+ local bufnr = vim.api.nvim_get_current_buf()
+ vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, {'One line'})
+ vim.lsp.buf_attach_client(bufnr, TEST_RPC_CLIENT_ID)
+
+ CALLED = false
+ RESPONSE = nil
+ local on_codelens = vim.lsp.codelens.on_codelens
+ vim.lsp.codelens.on_codelens = function (err, result, ...)
+ CALLED = true
+ RESPONSE = { err = err, result = result }
+ return on_codelens(err, result, ...)
+ end
+ ]=])
+ end,
+ on_exit = function(code, signal)
+ eq(0, code, "exit code")
+ eq(0, signal, "exit signal")
+ end,
+ on_handler = function(err, result, ctx)
+ eq(table.remove(expected_handlers), {err, result, ctx})
+ if ctx.method == 'start' then
+ -- 1. first codelens request errors
+ local response = exec_lua([=[
+ CALLED = false
+ vim.lsp.codelens.refresh()
+ vim.wait(100, function () return CALLED end)
+ return RESPONSE
+ ]=])
+ eq( { err = { code = -32002, message = "ServerNotInitialized" } }, response)
+
+ -- 2. second codelens request runs
+ response = exec_lua([=[
+ CALLED = false
+ local cmd_called = nil
+ vim.lsp.commands["Dummy"] = function (command)
+ cmd_called = command
+ end
+ vim.lsp.codelens.refresh()
+ vim.wait(100, function () return CALLED end)
+ vim.lsp.codelens.run()
+ vim.wait(100, function () return cmd_called end)
+ return cmd_called
+ ]=])
+ eq( { command = "Dummy", title = "Lens1" }, response)
+
+ -- 3. third codelens request runs
+ response = exec_lua([=[
+ CALLED = false
+ local cmd_called = nil
+ vim.lsp.commands["Dummy"] = function (command)
+ cmd_called = command
+ end
+ vim.lsp.codelens.refresh()
+ vim.wait(100, function () return CALLED end)
+ vim.lsp.codelens.run()
+ vim.wait(100, function () return cmd_called end)
+ return cmd_called
+ ]=])
+ eq( { command = "Dummy", title = "Lens2" }, response)
+ elseif ctx.method == 'shutdown' then
+ client.stop()
+ end
+ end
+ }
+ end)
+ end)
+
+ describe("vim.lsp.buf.format", function()
+ it("Aborts with notify if no client matches filter", function()
+ local client
+ test_rpc_server {
+ test_name = "basic_init",
+ on_init = function(c)
+ client = c
+ end,
+ on_handler = function()
+ local notify_msg = exec_lua([[
+ local bufnr = vim.api.nvim_get_current_buf()
+ vim.lsp.buf_attach_client(bufnr, TEST_RPC_CLIENT_ID)
+ local notify_msg
+ local notify = vim.notify
+ vim.notify = function(msg, log_level)
+ notify_msg = msg
+ end
+ vim.lsp.buf.format({ name = 'does-not-exist' })
+ vim.notify = notify
+ return notify_msg
+ ]])
+ eq("[LSP] Format request failed, no matching language servers.", notify_msg)
+ client.stop()
+ end,
+ }
+ end)
+ it("Sends textDocument/formatting request to format buffer", function()
+ local expected_handlers = {
+ {NIL, {}, {method="shutdown", client_id=1}};
+ {NIL, {}, {method="start", client_id=1}};
+ }
+ local client
+ test_rpc_server {
+ test_name = "basic_formatting",
+ on_init = function(c)
+ client = c
+ end,
+ on_handler = function(_, _, ctx)
+ table.remove(expected_handlers)
+ if ctx.method == "start" then
+ local notify_msg = exec_lua([[
+ local bufnr = vim.api.nvim_get_current_buf()
+ vim.lsp.buf_attach_client(bufnr, TEST_RPC_CLIENT_ID)
+ local notify_msg
+ local notify = vim.notify
+ vim.notify = function(msg, log_level)
+ notify_msg = msg
+ end
+ vim.lsp.buf.format({ bufnr = bufnr })
+ vim.notify = notify
+ return notify_msg
+ ]])
+ eq(NIL, notify_msg)
+ elseif ctx.method == "shutdown" then
+ client.stop()
+ end
+ end,
+ }
+ end)
+ it('Can format async', function()
+ local expected_handlers = {
+ {NIL, {}, {method="shutdown", client_id=1}};
+ {NIL, {}, {method="start", client_id=1}};
+ }
+ local client
+ test_rpc_server {
+ test_name = "basic_formatting",
+ on_init = function(c)
+ client = c
+ end,
+ on_handler = function(_, _, ctx)
+ table.remove(expected_handlers)
+ if ctx.method == "start" then
+ local result = exec_lua([[
+ local bufnr = vim.api.nvim_get_current_buf()
+ vim.lsp.buf_attach_client(bufnr, TEST_RPC_CLIENT_ID)
+
+ local notify_msg
+ local notify = vim.notify
+ vim.notify = function(msg, log_level)
+ notify_msg = msg
+ end
+
+ local handler = vim.lsp.handlers['textDocument/formatting']
+ local handler_called = false
+ vim.lsp.handlers['textDocument/formatting'] = function(...)
+ handler_called = true
+ end
+
+ vim.lsp.buf.format({ bufnr = bufnr, async = true })
+ vim.wait(1000, function() return handler_called end)
+
+ vim.notify = notify
+ vim.lsp.handlers['textDocument/formatting'] = handler
+ return {notify = notify_msg, handler_called = handler_called}
+ ]])
+ eq({handler_called=true}, result)
+ elseif ctx.method == "shutdown" then
+ client.stop()
+ end
+ end,
+ }
+ end)
end)
end)
diff --git a/test/functional/plugin/man_spec.lua b/test/functional/plugin/man_spec.lua
index e5b2e7dc1f..c8da5a711f 100644
--- a/test/functional/plugin/man_spec.lua
+++ b/test/functional/plugin/man_spec.lua
@@ -2,13 +2,19 @@ local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local command, eval, rawfeed = helpers.command, helpers.eval, helpers.rawfeed
local clear = helpers.clear
+local funcs = helpers.funcs
+local nvim_prog = helpers.nvim_prog
+local matches = helpers.matches
describe(':Man', function()
+ before_each(function()
+ clear()
+ end)
+
describe('man.lua: highlight_line()', function()
local screen
before_each(function()
- clear()
command('syntax on')
command('set filetype=man')
command('syntax off') -- Ignore syntax groups
@@ -137,4 +143,10 @@ describe(':Man', function()
]])
end)
end)
+
+ it('q quits in "$MANPAGER mode" (:Man!) #18281', function()
+ -- This will hang if #18281 regresses.
+ local args = {nvim_prog, '--headless', '+autocmd VimLeave * echo "quit works!!"', '+Man!', '+call nvim_input("q")'}
+ matches('quit works!!', funcs.system(args, {'manpage contents'}))
+ end)
end)
diff --git a/test/functional/plugin/matchparen_spec.lua b/test/functional/plugin/matchparen_spec.lua
index 13e1283e2c..2670734c1a 100644
--- a/test/functional/plugin/matchparen_spec.lua
+++ b/test/functional/plugin/matchparen_spec.lua
@@ -27,7 +27,7 @@ describe('matchparen', function()
feed('{<cr>')
feed('}')
- -- critical part: up + cr should result in an empty line inbetween the
+ -- critical part: up + cr should result in an empty line in between the
-- brackets... if the bug is there, the empty line will be before the '{'
feed('<up>')
feed('<cr>')
diff --git a/test/functional/plugin/shada_spec.lua b/test/functional/plugin/shada_spec.lua
index a4d78682ad..6f22f865e6 100644
--- a/test/functional/plugin/shada_spec.lua
+++ b/test/functional/plugin/shada_spec.lua
@@ -1,7 +1,7 @@
local helpers = require('test.functional.helpers')(after_each)
local clear = helpers.clear
-local eq, nvim_eval, nvim_command, nvim, exc_exec, funcs, nvim_feed, curbuf =
- helpers.eq, helpers.eval, helpers.command, helpers.nvim, helpers.exc_exec,
+local eq, meths, nvim_eval, nvim_command, nvim, exc_exec, funcs, nvim_feed, curbuf =
+ helpers.eq, helpers.meths, helpers.eval, helpers.command, helpers.nvim, helpers.exc_exec,
helpers.funcs, helpers.feed, helpers.curbuf
local neq = helpers.neq
local read_file = helpers.read_file
@@ -2162,6 +2162,10 @@ describe('plugin/shada.vim', function()
wshada('\004\000\009\147\000\196\002ab\196\001a')
wshada_tmp('\004\000\009\147\000\196\002ab\196\001b')
+
+ local bufread_commands = meths.get_autocmds({ group = "ShaDaCommands", event = "BufReadCmd" })
+ eq(2, #bufread_commands--[[, vim.inspect(bufread_commands) ]])
+
-- Need to set nohidden so that the buffer containing 'fname' is not unloaded
-- after loading 'fname_tmp', otherwise the '++opt not supported' test below
-- won't work since the BufReadCmd autocmd won't be triggered.
diff --git a/test/functional/preload.lua b/test/functional/preload.lua
index 24a3977e6b..74f03eaecf 100644
--- a/test/functional/preload.lua
+++ b/test/functional/preload.lua
@@ -1,8 +1,9 @@
--- Modules loaded here will not be cleared and reloaded by Busted.
+-- Modules loaded here will NOT be cleared and reloaded by Busted.
-- Busted started doing this to help provide more isolation. See issue #62
-- for more information about this.
local helpers = require('test.functional.helpers')(nil)
local iswin = helpers.iswin
+local busted = require("busted")
if iswin() then
local ffi = require('ffi')
@@ -12,3 +13,28 @@ if iswin() then
]]
ffi.C._set_fmode(0x8000)
end
+
+local testid = (function()
+ local id = 0
+ return (function()
+ id = id + 1
+ return id
+ end)
+end)()
+
+-- Global before_each. https://github.com/Olivine-Labs/busted/issues/613
+local function before_each(_element, _parent)
+ local id = ('T%d'):format(testid())
+ _G._nvim_test_id = id
+ return nil, true
+end
+busted.subscribe({ 'test', 'start' },
+ before_each,
+ {
+ -- Ensure our --helper is handled before --output (see busted/runner.lua).
+ priority = 1,
+ -- Don't generate a test-id for skipped tests. /shrug
+ predicate = function (element, _, status)
+ return not ((element.descriptor == 'pending' or status == 'pending'))
+ end
+ })
diff --git a/test/functional/provider/clipboard_spec.lua b/test/functional/provider/clipboard_spec.lua
index 986db96a18..5bdfec574e 100644
--- a/test/functional/provider/clipboard_spec.lua
+++ b/test/functional/provider/clipboard_spec.lua
@@ -50,29 +50,33 @@ local function basic_register_test(noblock)
text, stuff and some more
some some text, stuff and some more]])
- -- deleting a word to named ("a) updates "1 (and not "-)
+ -- deleting a word to named ("a) doesn't update "1 or "-
feed('gg"adwj"1P^"-P')
expect([[
, stuff and some more
- some textsome some text, stuff and some more]])
+ some some random text
+ some some text, stuff and some more]])
-- deleting a line does update ""
feed('ggdd""P')
expect([[
, stuff and some more
- some textsome some text, stuff and some more]])
+ some some random text
+ some some text, stuff and some more]])
feed('ggw<c-v>jwyggP')
if noblock then
expect([[
stuf
- me t
+ me s
, stuff and some more
- some textsome some text, stuff and some more]])
+ some some random text
+ some some text, stuff and some more]])
else
expect([[
stuf, stuff and some more
- me tsome textsome some text, stuff and some more]])
+ me ssome some random text
+ some some text, stuff and some more]])
end
-- pasting in visual does unnamed delete of visual selection
diff --git a/test/functional/provider/nodejs_spec.lua b/test/functional/provider/nodejs_spec.lua
index 661a6f4f94..187f1c0412 100644
--- a/test/functional/provider/nodejs_spec.lua
+++ b/test/functional/provider/nodejs_spec.lua
@@ -29,7 +29,7 @@ describe('nodejs host', function()
local fname = 'Xtest-nodejs-hello.js'
write_file(fname, [[
const neovim = require('neovim');
- const nvim = neovim.attach({socket: process.env.NVIM_LISTEN_ADDRESS});
+ const nvim = neovim.attach({socket: process.env.NVIM});
nvim.command('let g:job_out = "hello"');
]])
command('let g:job_id = jobstart(["node", "'..fname..'"])')
@@ -39,7 +39,7 @@ describe('nodejs host', function()
local fname = 'Xtest-nodejs-hello-plugin.js'
write_file(fname, [[
const neovim = require('neovim');
- const nvim = neovim.attach({socket: process.env.NVIM_LISTEN_ADDRESS});
+ const nvim = neovim.attach({socket: process.env.NVIM});
class TestPlugin {
hello() {
diff --git a/test/functional/provider/perl_spec.lua b/test/functional/provider/perl_spec.lua
index 125674660b..aff5e36e24 100644
--- a/test/functional/provider/perl_spec.lua
+++ b/test/functional/provider/perl_spec.lua
@@ -83,7 +83,7 @@ describe('perl provider', function()
use Neovim::Ext;
use Neovim::Ext::MsgPack::RPC;
- my $session = Neovim::Ext::MsgPack::RPC::socket_session($ENV{NVIM_LISTEN_ADDRESS});
+ my $session = Neovim::Ext::MsgPack::RPC::socket_session($ENV{NVIM});
my $nvim = Neovim::Ext::from_session($session);
$nvim->command('let g:job_out = "hello"');
1;
@@ -116,7 +116,7 @@ describe('perl provider', function()
use Neovim::Ext;
use Neovim::Ext::MsgPack::RPC;
- my $session = Neovim::Ext::MsgPack::RPC::socket_session($ENV{NVIM_LISTEN_ADDRESS});
+ my $session = Neovim::Ext::MsgPack::RPC::socket_session($ENV{NVIM});
my $nvim = Neovim::Ext::from_session($session);
my $plugin = TestPlugin->new($nvim);
$plugin->test_command();
diff --git a/test/functional/provider/provider_spec.lua b/test/functional/provider/provider_spec.lua
index 78bc4a4edb..3895b8613f 100644
--- a/test/functional/provider/provider_spec.lua
+++ b/test/functional/provider/provider_spec.lua
@@ -14,8 +14,8 @@ describe('providers', function()
command('set loadplugins')
-- Using test-fixture with broken impl:
-- test/functional/fixtures/autoload/provider/python.vim
- eq('Vim:provider: python: missing required variable g:loaded_python_provider',
- pcall_err(eval, "has('python')"))
+ eq('Vim:provider: python3: missing required variable g:loaded_python3_provider',
+ pcall_err(eval, "has('python3')"))
end)
it('with g:loaded_xx_provider, missing #Call()', function()
diff --git a/test/functional/provider/python3_spec.lua b/test/functional/provider/python3_spec.lua
index d100db8de2..d9c44c3315 100644
--- a/test/functional/provider/python3_spec.lua
+++ b/test/functional/provider/python3_spec.lua
@@ -8,6 +8,8 @@ local source = helpers.source
local missing_provider = helpers.missing_provider
local matches = helpers.matches
local pcall_err = helpers.pcall_err
+local funcs = helpers.funcs
+local dedent = helpers.dedent
do
clear()
@@ -48,7 +50,12 @@ describe('python3 provider', function()
local very_long_symbol = string.rep('a', 1200)
feed_command(':silent! py3 print('..very_long_symbol..' b)')
-- Error message will contain this (last) line.
- eq('Error invoking \'python_execute\' on channel 3 (python3-script-host):\n File "<string>", line 1\n print('..very_long_symbol..' b)\n '..string.rep(' ',1200)..' ^\nSyntaxError: invalid syntax', eval('v:errmsg'))
+ matches(string.format(dedent([[
+ ^Error invoking 'python_execute' on channel 3 %%(python3%%-script%%-host%%):
+ File "<string>", line 1
+ print%%(%s b%%)
+ %%C*
+ SyntaxError: invalid syntax%%C*$]]), very_long_symbol), eval('v:errmsg'))
end)
it('python3_execute with nested commands', function()
@@ -93,16 +100,40 @@ describe('python3 provider', function()
ghi]])
end)
- it('py3eval', function()
- eq({1, 2, {['key'] = 'val'}}, eval([[py3eval('[1, 2, {"key": "val"}]')]]))
+ describe('py3eval()', function()
+ it('works', function()
+ eq({1, 2, {['key'] = 'val'}}, funcs.py3eval('[1, 2, {"key": "val"}]'))
+ end)
+
+ it('errors out when given non-string', function()
+ eq('Vim:E474: Invalid argument', pcall_err(eval, 'py3eval(10)'))
+ eq('Vim:E474: Invalid argument', pcall_err(eval, 'py3eval(v:_null_dict)'))
+ eq('Vim:E474: Invalid argument', pcall_err(eval, 'py3eval(v:_null_list)'))
+ eq('Vim:E474: Invalid argument', pcall_err(eval, 'py3eval(0.0)'))
+ eq('Vim:E474: Invalid argument', pcall_err(eval, 'py3eval(function("tr"))'))
+ eq('Vim:E474: Invalid argument', pcall_err(eval, 'py3eval(v:true)'))
+ eq('Vim:E474: Invalid argument', pcall_err(eval, 'py3eval(v:false)'))
+ eq('Vim:E474: Invalid argument', pcall_err(eval, 'py3eval(v:null)'))
+ end)
+
+ it('accepts NULL string', function()
+ matches('.*SyntaxError.*', pcall_err(eval, 'py3eval($XXX_NONEXISTENT_VAR_XXX)'))
+ end)
end)
it('pyxeval #10758', function()
- eq(0, eval([[&pyxversion]]))
+ eq(3, eval([[&pyxversion]]))
eq(3, eval([[pyxeval('sys.version_info[:3][0]')]]))
eq(3, eval([[&pyxversion]]))
end)
+ it("setting 'pyxversion'", function()
+ command 'set pyxversion=3' -- no error
+ eq('Vim(set):E474: Invalid argument: pyxversion=2', pcall_err(command, 'set pyxversion=2'))
+ command 'set pyxversion=0' -- allowed, but equivalent to pyxversion=3
+ eq(3, eval'&pyxversion')
+ end)
+
it('RPC call to expand("<afile>") during BufDelete #5245 #5617', function()
helpers.add_builddir_to_rtp()
source([=[
@@ -120,3 +151,15 @@ describe('python3 provider', function()
assert_alive()
end)
end)
+
+describe('python2 feature test', function()
+ -- python2 is not supported, so correct behaviour is to return 0
+ it('works', function()
+ eq(0, funcs.has('python2'))
+ eq(0, funcs.has('python'))
+ eq(0, funcs.has('python_compiled'))
+ eq(0, funcs.has('python_dynamic'))
+ eq(0, funcs.has('python_dynamic_'))
+ eq(0, funcs.has('python_'))
+ end)
+end)
diff --git a/test/functional/provider/python_spec.lua b/test/functional/provider/python_spec.lua
deleted file mode 100644
index d60d8d1001..0000000000
--- a/test/functional/provider/python_spec.lua
+++ /dev/null
@@ -1,123 +0,0 @@
-local helpers = require('test.functional.helpers')(after_each)
-
-local eq = helpers.eq
-local neq = helpers.neq
-local feed = helpers.feed
-local clear = helpers.clear
-local funcs = helpers.funcs
-local meths = helpers.meths
-local insert = helpers.insert
-local expect = helpers.expect
-local command = helpers.command
-local exc_exec = helpers.exc_exec
-local write_file = helpers.write_file
-local curbufmeths = helpers.curbufmeths
-local missing_provider = helpers.missing_provider
-local matches = helpers.matches
-local pcall_err = helpers.pcall_err
-
-do
- clear()
- local reason = missing_provider('python')
- if reason then
- it(':python reports E319 if provider is missing', function()
- local expected = [[Vim%(py.*%):E319: No "python" provider found.*]]
- matches(expected, pcall_err(command, 'py print("foo")'))
- matches(expected, pcall_err(command, 'pyfile foo'))
- end)
- pending(string.format('Python 2 (or the pynvim module) is broken/missing (%s)', reason), function() end)
- return
- end
-end
-
-before_each(function()
- clear()
- command('python import vim')
-end)
-
-describe('python feature test', function()
- it('works', function()
- eq(1, funcs.has('python'))
- eq(1, funcs.has('python_compiled'))
- eq(1, funcs.has('python_dynamic'))
- eq(0, funcs.has('python_dynamic_'))
- eq(0, funcs.has('python_'))
- end)
-end)
-
-describe(':python command', function()
- it('works with a line', function()
- command('python vim.vars["set_by_python"] = [100, 0]')
- eq({100, 0}, meths.get_var('set_by_python'))
- end)
-
- -- TODO(ZyX-I): works with << EOF
- -- TODO(ZyX-I): works with execute 'python' line1."\n".line2."\n"…
-
- it('supports nesting', function()
- command([[python vim.command('python vim.command("python vim.command(\'let set_by_nested_python = 555\')")')]])
- eq(555, meths.get_var('set_by_nested_python'))
- end)
-
- it('supports range', function()
- insert([[
- line1
- line2
- line3
- line4]])
- feed('ggjvj:python vim.vars["range"] = vim.current.range[:]<CR>')
- eq({'line2', 'line3'}, meths.get_var('range'))
- end)
-end)
-
-describe(':pyfile command', function()
- it('works', function()
- local fname = 'pyfile.py'
- write_file(fname, 'vim.command("let set_by_pyfile = 123")')
- command('pyfile pyfile.py')
- eq(123, meths.get_var('set_by_pyfile'))
- os.remove(fname)
- end)
-end)
-
-describe(':pydo command', function()
- it('works', function()
- -- :pydo 42 returns None for all lines,
- -- the buffer should not be changed
- command('normal :pydo 42')
- eq(false, curbufmeths.get_option('modified'))
- -- insert some text
- insert('abc\ndef\nghi')
- expect([[
- abc
- def
- ghi]])
- -- go to top and select and replace the first two lines
- feed('ggvj:pydo return str(linenr)<CR>')
- expect([[
- 1
- 2
- ghi]])
- end)
-end)
-
-describe('pyeval()', function()
- it('works', function()
- eq({1, 2, {['key'] = 'val'}}, funcs.pyeval('[1, 2, {"key": "val"}]'))
- end)
-
- it('errors out when given non-string', function()
- eq('Vim(call):E474: Invalid argument', exc_exec('call pyeval(10)'))
- eq('Vim(call):E474: Invalid argument', exc_exec('call pyeval(v:_null_dict)'))
- eq('Vim(call):E474: Invalid argument', exc_exec('call pyeval(v:_null_list)'))
- eq('Vim(call):E474: Invalid argument', exc_exec('call pyeval(0.0)'))
- eq('Vim(call):E474: Invalid argument', exc_exec('call pyeval(function("tr"))'))
- eq('Vim(call):E474: Invalid argument', exc_exec('call pyeval(v:true)'))
- eq('Vim(call):E474: Invalid argument', exc_exec('call pyeval(v:false)'))
- eq('Vim(call):E474: Invalid argument', exc_exec('call pyeval(v:null)'))
- end)
-
- it('accepts NULL string', function()
- neq(0, exc_exec('call pyeval($XXX_NONEXISTENT_VAR_XXX)'))
- end)
-end)
diff --git a/test/functional/shada/buffers_spec.lua b/test/functional/shada/buffers_spec.lua
index 04c9c01d7c..26a4569fce 100644
--- a/test/functional/shada/buffers_spec.lua
+++ b/test/functional/shada/buffers_spec.lua
@@ -2,6 +2,7 @@
local helpers = require('test.functional.helpers')(after_each)
local nvim_command, funcs, eq, curbufmeths =
helpers.command, helpers.funcs, helpers.eq, helpers.curbufmeths
+local expect_exit = helpers.expect_exit
local shada_helpers = require('test.functional.shada.helpers')
local reset, clear = shada_helpers.reset, shada_helpers.clear
@@ -15,7 +16,7 @@ describe('shada support code', function()
reset('set shada+=%')
nvim_command('edit ' .. testfilename)
nvim_command('edit ' .. testfilename_2)
- nvim_command('qall')
+ expect_exit(nvim_command, 'qall')
reset('set shada+=%')
eq(3, funcs.bufnr('$'))
eq('', funcs.bufname(1))
@@ -27,7 +28,7 @@ describe('shada support code', function()
reset('set shada+=%')
nvim_command('edit ' .. testfilename)
nvim_command('edit ' .. testfilename_2)
- nvim_command('qall')
+ expect_exit(nvim_command, 'qall')
reset()
eq(1, funcs.bufnr('$'))
eq('', funcs.bufname(1))
@@ -37,7 +38,7 @@ describe('shada support code', function()
reset()
nvim_command('edit ' .. testfilename)
nvim_command('edit ' .. testfilename_2)
- nvim_command('qall')
+ expect_exit(nvim_command, 'qall')
reset('set shada+=%')
eq(1, funcs.bufnr('$'))
eq('', funcs.bufname(1))
@@ -48,7 +49,7 @@ describe('shada support code', function()
nvim_command('edit ' .. testfilename)
nvim_command('edit ' .. testfilename_2)
curbufmeths.set_option('buflisted', false)
- nvim_command('qall')
+ expect_exit(nvim_command, 'qall')
reset('set shada+=%')
eq(2, funcs.bufnr('$'))
eq('', funcs.bufname(1))
@@ -60,7 +61,7 @@ describe('shada support code', function()
nvim_command('edit ' .. testfilename)
nvim_command('edit ' .. testfilename_2)
curbufmeths.set_option('buftype', 'quickfix')
- nvim_command('qall')
+ expect_exit(nvim_command, 'qall')
reset('set shada+=%')
eq(2, funcs.bufnr('$'))
eq('', funcs.bufname(1))
@@ -73,7 +74,7 @@ describe('shada support code', function()
nvim_command('enew')
curbufmeths.set_lines(0, 1, true, {'bar'})
eq(2, funcs.bufnr('$'))
- nvim_command('qall!')
+ expect_exit(nvim_command, 'qall!')
reset('set shada+=% hidden')
eq(1, funcs.bufnr('$'))
eq('', funcs.bufname(1))
@@ -83,7 +84,7 @@ describe('shada support code', function()
reset('set shada+=%1')
nvim_command('edit ' .. testfilename)
nvim_command('edit ' .. testfilename_2)
- nvim_command('qall')
+ expect_exit(nvim_command, 'qall')
reset('set shada+=%1')
eq(2, funcs.bufnr('$'))
eq('', funcs.bufname(1))
diff --git a/test/functional/shada/history_spec.lua b/test/functional/shada/history_spec.lua
index 84cc34c7cc..d1daf6c7cc 100644
--- a/test/functional/shada/history_spec.lua
+++ b/test/functional/shada/history_spec.lua
@@ -3,6 +3,7 @@ local helpers = require('test.functional.helpers')(after_each)
local nvim_command, funcs, meths, nvim_feed, eq =
helpers.command, helpers.funcs, helpers.meths, helpers.feed, helpers.eq
local assert_alive = helpers.assert_alive
+local expect_exit = helpers.expect_exit
local shada_helpers = require('test.functional.shada.helpers')
local reset, clear = shada_helpers.reset, shada_helpers.clear
@@ -25,13 +26,13 @@ describe('ShaDa support code', function()
nvim_command('set shada=\'0 history=2')
nvim_feed(':" Test\n')
nvim_feed(':" Test 2\n')
- nvim_command('qall')
+ expect_exit(nvim_command, 'qall')
reset()
nvim_command('set shada=\'0 history=2')
nvim_command('rshada')
eq('" Test 2', funcs.histget(':', -1))
eq('" Test', funcs.histget(':', -2))
- nvim_command('qall')
+ expect_exit(nvim_command, 'qall')
end)
it('respects &history when dumping',
@@ -138,7 +139,7 @@ describe('ShaDa support code', function()
nvim_command('set hlsearch shada-=h')
nvim_feed('/test\n')
eq(1, meths.get_vvar('hlsearch'))
- nvim_command('qall')
+ expect_exit(nvim_command, 'qall')
reset()
eq(1, meths.get_vvar('hlsearch'))
end)
@@ -147,7 +148,7 @@ describe('ShaDa support code', function()
nvim_command('set hlsearch shada-=h')
nvim_feed('/test\n')
nvim_command('nohlsearch')
- nvim_command('qall')
+ expect_exit(nvim_command, 'qall')
reset()
eq(0, meths.get_vvar('hlsearch'))
end)
@@ -156,7 +157,7 @@ describe('ShaDa support code', function()
nvim_command('set hlsearch')
nvim_feed('/test\n')
eq(1, meths.get_vvar('hlsearch'))
- nvim_command('qall')
+ expect_exit(nvim_command, 'qall')
reset()
eq(0, meths.get_vvar('hlsearch'))
end)
@@ -175,7 +176,7 @@ describe('ShaDa support code', function()
it('dumps and loads history with UTF-8 characters', function()
reset()
nvim_feed(':echo "«"\n')
- nvim_command('qall')
+ expect_exit(nvim_command, 'qall')
reset()
eq('echo "«"', funcs.histget(':', -1))
end)
@@ -183,7 +184,7 @@ describe('ShaDa support code', function()
it('dumps and loads replacement with UTF-8 characters',
function()
nvim_command('substitute/./«/ge')
- nvim_command('qall!')
+ expect_exit(nvim_command, 'qall!')
reset()
funcs.setline('.', {'.'})
nvim_command('&')
@@ -193,7 +194,7 @@ describe('ShaDa support code', function()
it('dumps and loads substitute pattern with UTF-8 characters',
function()
nvim_command('substitute/«/./ge')
- nvim_command('qall!')
+ expect_exit(nvim_command, 'qall!')
reset()
funcs.setline('.', {'«\171'})
nvim_command('&')
@@ -204,7 +205,7 @@ describe('ShaDa support code', function()
function()
nvim_command('silent! /«/')
nvim_command('set shada+=/0')
- nvim_command('qall!')
+ expect_exit(nvim_command, 'qall!')
reset()
funcs.setline('.', {'\171«'})
nvim_command('~&')
@@ -217,7 +218,7 @@ describe('ShaDa support code', function()
-- \171 is U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK in latin1
nvim_command('silent! /\171/')
nvim_command('set shada+=/0')
- nvim_command('qall!')
+ expect_exit(nvim_command, 'qall!')
reset()
funcs.setline('.', {'\171«'})
nvim_command('~&')
diff --git a/test/functional/shada/marks_spec.lua b/test/functional/shada/marks_spec.lua
index 153a1c346f..a91be18841 100644
--- a/test/functional/shada/marks_spec.lua
+++ b/test/functional/shada/marks_spec.lua
@@ -4,6 +4,7 @@ local meths, curwinmeths, curbufmeths, nvim_command, funcs, eq =
helpers.meths, helpers.curwinmeths, helpers.curbufmeths, helpers.command,
helpers.funcs, helpers.eq
local exc_exec, exec_capture = helpers.exc_exec, helpers.exec_capture
+local expect_exit = helpers.expect_exit
local shada_helpers = require('test.functional.shada.helpers')
local reset, clear = shada_helpers.reset, shada_helpers.clear
@@ -79,7 +80,7 @@ describe('ShaDa support code', function()
nvim_command('mark a')
nvim_command('2')
nvim_command('kb')
- nvim_command('qall')
+ expect_exit(nvim_command, 'qall')
reset()
nvim_command('edit ' .. testfilename)
nvim_command('normal! `a')
@@ -92,7 +93,7 @@ describe('ShaDa support code', function()
it('is able to dump and read back mark "', function()
nvim_command('edit ' .. testfilename)
nvim_command('2')
- nvim_command('qall')
+ expect_exit(nvim_command, 'qall')
reset()
nvim_command('edit ' .. testfilename)
nvim_command('normal! `"')
@@ -104,7 +105,7 @@ describe('ShaDa support code', function()
nvim_command('tabedit ' .. testfilename_2)
nvim_command('2')
nvim_command('q!')
- nvim_command('qall')
+ expect_exit(nvim_command, 'qall')
reset()
nvim_command('edit ' .. testfilename_2)
nvim_command('normal! `"')
@@ -116,7 +117,7 @@ describe('ShaDa support code', function()
local tf_full = curbufmeths.get_name()
nvim_command('edit ' .. testfilename_2)
local tf_full_2 = curbufmeths.get_name()
- nvim_command('qall')
+ expect_exit(nvim_command, 'qall')
reset()
local oldfiles = meths.get_vvar('oldfiles')
table.sort(oldfiles)
@@ -145,7 +146,7 @@ describe('ShaDa support code', function()
nvim_command('enew')
nvim_command('normal! gg')
local saved = exec_capture('jumps')
- nvim_command('qall')
+ expect_exit(nvim_command, 'qall')
reset()
eq(saved, exec_capture('jumps'))
end)
@@ -176,7 +177,7 @@ describe('ShaDa support code', function()
nvim_command('normal! G')
nvim_command('sleep 2')
nvim_command('normal! gg')
- nvim_command('qall')
+ expect_exit(nvim_command, 'qall')
reset()
nvim_command('redraw')
nvim_command('edit ' .. testfilename)
@@ -200,7 +201,7 @@ describe('ShaDa support code', function()
nvim_command('edit ' .. testfilename)
nvim_command('normal! Gra')
nvim_command('normal! ggrb')
- nvim_command('qall!')
+ expect_exit(nvim_command, 'qall!')
reset()
nvim_command('edit ' .. testfilename)
nvim_command('normal! Gg;')
diff --git a/test/functional/shada/registers_spec.lua b/test/functional/shada/registers_spec.lua
index 1f06cbe350..6aaa54cce8 100644
--- a/test/functional/shada/registers_spec.lua
+++ b/test/functional/shada/registers_spec.lua
@@ -4,6 +4,7 @@ local nvim_command, funcs, eq = helpers.command, helpers.funcs, helpers.eq
local shada_helpers = require('test.functional.shada.helpers')
local reset, clear = shada_helpers.reset, shada_helpers.clear
+local expect_exit = helpers.expect_exit
local setreg = function(name, contents, typ)
if type(contents) == 'string' then
@@ -27,7 +28,7 @@ describe('ShaDa support code', function()
setreg('c', {'d', 'e', ''}, 'c')
setreg('l', {'a', 'b', 'cde'}, 'l')
setreg('b', {'bca', 'abc', 'cba'}, 'b3')
- nvim_command('qall')
+ expect_exit(nvim_command, 'qall')
reset()
eq({{'d', 'e', ''}, 'v'}, getreg('c'))
eq({{'a', 'b', 'cde'}, 'V'}, getreg('l'))
@@ -39,7 +40,7 @@ describe('ShaDa support code', function()
setreg('c', {'d', 'e', ''}, 'c')
setreg('l', {'a', 'b', 'cde'}, 'l')
setreg('b', {'bca', 'abc', 'cba'}, 'b3')
- nvim_command('qall')
+ expect_exit(nvim_command, 'qall')
reset()
eq({{}, ''}, getreg('c'))
eq({{}, ''}, getreg('l'))
@@ -50,7 +51,7 @@ describe('ShaDa support code', function()
setreg('c', {'d', 'e', ''}, 'c')
setreg('l', {'a', 'b', 'cde'}, 'l')
setreg('b', {'bca', 'abc', 'cba'}, 'b3')
- nvim_command('qall')
+ expect_exit(nvim_command, 'qall')
reset('set shada=\'0,<0')
eq({{'d', 'e', ''}, 'v'}, getreg('c'))
eq({{'a', 'b', 'cde'}, 'V'}, getreg('l'))
@@ -62,7 +63,7 @@ describe('ShaDa support code', function()
setreg('c', {'d', 'e', ''}, 'c')
setreg('l', {'a', 'b', 'cde'}, 'l')
setreg('b', {'bca', 'abc', 'cba'}, 'b3')
- nvim_command('qall')
+ expect_exit(nvim_command, 'qall')
reset()
eq({{}, ''}, getreg('c'))
eq({{}, ''}, getreg('l'))
@@ -73,7 +74,7 @@ describe('ShaDa support code', function()
setreg('c', {'d', 'e', ''}, 'c')
setreg('l', {'a', 'b', 'cde'}, 'l')
setreg('b', {'bca', 'abc', 'cba'}, 'b3')
- nvim_command('qall')
+ expect_exit(nvim_command, 'qall')
reset('set shada=\'0,\\"0')
eq({{'d', 'e', ''}, 'v'}, getreg('c'))
eq({{'a', 'b', 'cde'}, 'V'}, getreg('l'))
@@ -85,7 +86,7 @@ describe('ShaDa support code', function()
setreg('c', {'d', 'e', ''}, 'c')
setreg('l', {'a', 'b', 'cde'}, 'l')
setreg('b', {'bca', 'abc', 'cba'}, 'b3')
- nvim_command('qall')
+ expect_exit(nvim_command, 'qall')
reset()
eq({{'d', 'e', ''}, 'v'}, getreg('c'))
eq({{'a', 'b', 'cde'}, 'V'}, getreg('l'))
@@ -96,7 +97,7 @@ describe('ShaDa support code', function()
nvim_command('set shada=\'0,<2')
setreg('o', {'d'}, 'c')
setreg('t', {'a', 'b', 'cde'}, 'l')
- nvim_command('qall')
+ expect_exit(nvim_command, 'qall')
reset()
eq({{'d'}, 'v'}, getreg('o'))
eq({{}, ''}, getreg('t'))
@@ -106,7 +107,7 @@ describe('ShaDa support code', function()
nvim_command('set shada=\'0,\\"2')
setreg('o', {'d'}, 'c')
setreg('t', {'a', 'b', 'cde'}, 'l')
- nvim_command('qall')
+ expect_exit(nvim_command, 'qall')
reset()
eq({{'d'}, 'v'}, getreg('o'))
eq({{}, ''}, getreg('t'))
@@ -117,7 +118,7 @@ describe('ShaDa support code', function()
setreg('o', {'d'}, 'c')
setreg('t', {'a', 'b', 'cde'}, 'l')
setreg('h', {'abc', 'acb', 'bac', 'bca', 'cab', 'cba'}, 'b3')
- nvim_command('qall')
+ expect_exit(nvim_command, 'qall')
reset()
eq({{'d'}, 'v'}, getreg('o'))
eq({{'a', 'b', 'cde'}, 'V'}, getreg('t'))
@@ -128,7 +129,7 @@ describe('ShaDa support code', function()
function()
reset()
setreg('e', {'«'}, 'c')
- nvim_command('qall')
+ expect_exit(nvim_command, 'qall')
reset()
eq({{'«'}, 'v'}, getreg('e'))
end)
@@ -138,7 +139,7 @@ describe('ShaDa support code', function()
reset()
-- \171 is U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK in latin1
setreg('e', {'\171«'}, 'c')
- nvim_command('qall')
+ expect_exit(nvim_command, 'qall')
reset()
eq({{'\171«'}, 'v'}, getreg('e'))
end)
@@ -148,7 +149,7 @@ describe('ShaDa support code', function()
setreg('1', {'one'}, 'c')
setreg('2', {'two'}, 'c')
setreg('a', {'a'}, 'c')
- nvim_command('qall')
+ expect_exit(nvim_command, 'qall')
reset()
eq({{}, ''}, getreg('0'))
eq({{'one'}, 'v'}, getreg('1'))
@@ -161,7 +162,7 @@ describe('ShaDa support code', function()
setreg('0', {'zero'}, 'c')
setreg('1', {'one'}, 'c')
setreg('2', {'two'}, 'c')
- nvim_command('qall')
+ expect_exit(nvim_command, 'qall')
reset()
eq({{'zero'}, 'v'}, getreg('0'))
eq({{'one'}, 'v'}, getreg('1'))
@@ -173,7 +174,7 @@ describe('ShaDa support code', function()
setreg('0', {'zero'}, 'c')
setreg('1', {'one'}, 'cu')
setreg('2', {'two'}, 'c')
- nvim_command('qall')
+ expect_exit(nvim_command, 'qall')
reset()
eq({{'zero'}, 'v'}, getreg('0'))
eq({{'one'}, 'v'}, getreg('1'))
diff --git a/test/functional/shada/shada_spec.lua b/test/functional/shada/shada_spec.lua
index ff63aed235..d10a2facbb 100644
--- a/test/functional/shada/shada_spec.lua
+++ b/test/functional/shada/shada_spec.lua
@@ -7,7 +7,7 @@ local write_file, spawn, set_session, nvim_prog, exc_exec =
helpers.exc_exec
local lfs = require('lfs')
-local paths = require('test.config.paths')
+local paths = require('test.cmakeconfig.paths')
local mpack = require('mpack')
diff --git a/test/functional/shada/variables_spec.lua b/test/functional/shada/variables_spec.lua
index 854add1363..a91b7eb193 100644
--- a/test/functional/shada/variables_spec.lua
+++ b/test/functional/shada/variables_spec.lua
@@ -2,6 +2,7 @@
local helpers = require('test.functional.helpers')(after_each)
local meths, funcs, nvim_command, eq, eval =
helpers.meths, helpers.funcs, helpers.command, helpers.eq, helpers.eval
+local expect_exit = helpers.expect_exit
local shada_helpers = require('test.functional.shada.helpers')
local reset, clear = shada_helpers.reset, shada_helpers.clear
@@ -33,7 +34,7 @@ describe('ShaDa support code', function()
local vartype = eval('type(g:' .. varname .. ')')
-- Exit during `reset` is not a regular exit: it does not write shada
-- automatically
- nvim_command('qall')
+ expect_exit(nvim_command, 'qall')
reset('set shada+=!')
eq(vartype, eval('type(g:' .. varname .. ')'))
eq(varval, meths.get_var(varname))
@@ -98,7 +99,7 @@ describe('ShaDa support code', function()
meths.set_var('LSTVAR', {'«'})
meths.set_var('DCTVAR', {['«']='«'})
meths.set_var('NESTEDVAR', {['«']={{'«'}, {['«']='«'}, {a='Test'}}})
- nvim_command('qall')
+ expect_exit(nvim_command, 'qall')
reset()
eq('«', meths.get_var('STRVAR'))
eq({'«'}, meths.get_var('LSTVAR'))
@@ -116,7 +117,7 @@ describe('ShaDa support code', function()
meths.set_var('DCTVAR', {['«\171']='«\171'})
meths.set_var('NESTEDVAR', {['\171']={{'\171«'}, {['\171']='\171'},
{a='Test'}}})
- nvim_command('qall')
+ expect_exit(nvim_command, 'qall')
reset()
eq('\171', meths.get_var('STRVAR'))
eq({'\171'}, meths.get_var('LSTVAR'))
diff --git a/test/functional/terminal/api_spec.lua b/test/functional/terminal/api_spec.lua
index e28cc03597..5305b8af9c 100644
--- a/test/functional/terminal/api_spec.lua
+++ b/test/functional/terminal/api_spec.lua
@@ -6,7 +6,7 @@ if helpers.pending_win32(pending) then return end
describe('api', function()
local screen
- local socket_name = "Xtest_functional_api.sock"
+ local socket_name = "./Xtest_functional_api.sock"
before_each(function()
helpers.clear()
@@ -29,7 +29,7 @@ describe('api', function()
{4:~ }|
{4:~ }|
{4:~ }|
- ]]..socket_name..[[ |
+ ]]..socket_name..[[ |
{3:-- TERMINAL --} |
]])
@@ -48,8 +48,8 @@ describe('api', function()
{3:-- TERMINAL --} |
]])
- ok(socket_session1:request("nvim_ui_attach", 42, 6, {rgb=true}))
- ok(socket_session2:request("nvim_ui_attach", 25, 30, {rgb=true}))
+ ok((socket_session1:request("nvim_ui_attach", 42, 6, {rgb=true})))
+ ok((socket_session2:request("nvim_ui_attach", 25, 30, {rgb=true})))
socket_session1:notify("nvim_input", "\n[socket 1] this is more than 25 columns")
socket_session2:notify("nvim_input", "\n[socket 2] input")
diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua
index beb43e0271..1cef771f0d 100644
--- a/test/functional/terminal/buffer_spec.lua
+++ b/test/functional/terminal/buffer_spec.lua
@@ -258,6 +258,7 @@ describe(':terminal buffer', function()
end)
it('it works with set rightleft #11438', function()
+ if helpers.pending_win32(pending) then return end
local columns = eval('&columns')
feed(string.rep('a', columns))
command('set rightleft')
diff --git a/test/functional/terminal/channel_spec.lua b/test/functional/terminal/channel_spec.lua
index 7223f5ba61..b5f3c2bd31 100644
--- a/test/functional/terminal/channel_spec.lua
+++ b/test/functional/terminal/channel_spec.lua
@@ -1,45 +1,94 @@
local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
local clear = helpers.clear
local eq = helpers.eq
+local eval = helpers.eval
local command = helpers.command
local pcall_err = helpers.pcall_err
local feed = helpers.feed
-local sleep = helpers.sleep
local poke_eventloop = helpers.poke_eventloop
-describe('associated channel is closed and later freed for terminal', function()
- before_each(clear)
+describe('terminal channel is closed and later released if', function()
+ local screen
+
+ before_each(function()
+ clear()
+ screen = Screen.new()
+ screen:attach()
+ end)
it('opened by nvim_open_term() and deleted by :bdelete!', function()
command([[let id = nvim_open_term(0, {})]])
- -- channel hasn't been freed yet
- eq("Vim(call):Can't send data to closed stream", pcall_err(command, [[bdelete! | call chansend(id, 'test')]]))
- -- channel has been freed after one main loop iteration
- eq("Vim(call):E900: Invalid channel id", pcall_err(command, [[call chansend(id, 'test')]]))
+ local chans = eval('len(nvim_list_chans())')
+ -- channel hasn't been released yet
+ eq("Vim(call):Can't send data to closed stream",
+ pcall_err(command, [[bdelete! | call chansend(id, 'test')]]))
+ -- channel has been released after one main loop iteration
+ eq(chans - 1, eval('len(nvim_list_chans())'))
+ end)
+
+ it('opened by nvim_open_term(), closed by chanclose(), and deleted by pressing a key', function()
+ command('let id = nvim_open_term(0, {})')
+ local chans = eval('len(nvim_list_chans())')
+ -- channel has been closed but not released
+ eq("Vim(call):Can't send data to closed stream",
+ pcall_err(command, [[call chanclose(id) | call chansend(id, 'test')]]))
+ screen:expect({any='%[Terminal closed%]'})
+ eq(chans, eval('len(nvim_list_chans())'))
+ -- delete terminal
+ feed('i<CR>')
+ -- need to first process input
+ poke_eventloop()
+ -- channel has been released after another main loop iteration
+ eq(chans - 1, eval('len(nvim_list_chans())'))
+ end)
+
+ it('opened by nvim_open_term(), closed by chanclose(), and deleted by :bdelete', function()
+ command('let id = nvim_open_term(0, {})')
+ local chans = eval('len(nvim_list_chans())')
+ -- channel has been closed but not released
+ eq("Vim(call):Can't send data to closed stream",
+ pcall_err(command, [[call chanclose(id) | call chansend(id, 'test')]]))
+ screen:expect({any='%[Terminal closed%]'})
+ eq(chans, eval('len(nvim_list_chans())'))
+ -- channel still hasn't been released yet
+ eq("Vim(call):Can't send data to closed stream",
+ pcall_err(command, [[bdelete | call chansend(id, 'test')]]))
+ -- channel has been released after one main loop iteration
+ eq(chans - 1, eval('len(nvim_list_chans())'))
end)
it('opened by termopen(), exited, and deleted by pressing a key', function()
command([[let id = termopen('echo')]])
- sleep(500)
- -- process has exited
- eq("Vim(call):Can't send data to closed stream", pcall_err(command, [[call chansend(id, 'test')]]))
+ local chans = eval('len(nvim_list_chans())')
+ -- wait for process to exit
+ screen:expect({any='%[Process exited 0%]'})
+ -- process has exited but channel has't been released
+ eq("Vim(call):Can't send data to closed stream",
+ pcall_err(command, [[call chansend(id, 'test')]]))
+ eq(chans, eval('len(nvim_list_chans())'))
-- delete terminal
feed('i<CR>')
-- need to first process input
poke_eventloop()
- -- channel has been freed after another main loop iteration
- eq("Vim(call):E900: Invalid channel id", pcall_err(command, [[call chansend(id, 'test')]]))
+ -- channel has been released after another main loop iteration
+ eq(chans - 1, eval('len(nvim_list_chans())'))
end)
-- This indirectly covers #16264
it('opened by termopen(), exited, and deleted by :bdelete', function()
command([[let id = termopen('echo')]])
- sleep(500)
- -- process has exited
- eq("Vim(call):Can't send data to closed stream", pcall_err(command, [[call chansend(id, 'test')]]))
- -- channel hasn't been freed yet
- eq("Vim(call):Can't send data to closed stream", pcall_err(command, [[bdelete | call chansend(id, 'test')]]))
- -- channel has been freed after one main loop iteration
- eq("Vim(call):E900: Invalid channel id", pcall_err(command, [[call chansend(id, 'test')]]))
+ local chans = eval('len(nvim_list_chans())')
+ -- wait for process to exit
+ screen:expect({any='%[Process exited 0%]'})
+ -- process has exited but channel hasn't been released
+ eq("Vim(call):Can't send data to closed stream",
+ pcall_err(command, [[call chansend(id, 'test')]]))
+ eq(chans, eval('len(nvim_list_chans())'))
+ -- channel still hasn't been released yet
+ eq("Vim(call):Can't send data to closed stream",
+ pcall_err(command, [[bdelete | call chansend(id, 'test')]]))
+ -- channel has been released after one main loop iteration
+ eq(chans - 1, eval('len(nvim_list_chans())'))
end)
end)
diff --git a/test/functional/terminal/cursor_spec.lua b/test/functional/terminal/cursor_spec.lua
index e9495f45a2..2d1c790d2f 100644
--- a/test/functional/terminal/cursor_spec.lua
+++ b/test/functional/terminal/cursor_spec.lua
@@ -2,9 +2,10 @@ local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local thelpers = require('test.functional.terminal.helpers')
local feed, clear, nvim = helpers.feed, helpers.clear, helpers.nvim
-local nvim_dir, command = helpers.nvim_dir, helpers.command
+local testprg, command = helpers.testprg, helpers.command
local nvim_prog = helpers.nvim_prog
local eq, eval = helpers.eq, helpers.eval
+local matches = helpers.matches
local feed_command = helpers.feed_command
local hide_cursor = thelpers.hide_cursor
local show_cursor = thelpers.show_cursor
@@ -87,6 +88,7 @@ describe(':terminal cursor', function()
describe('when invisible', function()
it('is not highlighted and is detached from screen cursor', function()
+ if helpers.pending_win32(pending) then return end
hide_cursor()
screen:expect([[
tty ready |
@@ -148,7 +150,7 @@ describe('cursor with customized highlighting', function()
[3] = {bold = true},
})
screen:attach({rgb=false})
- command('call termopen(["'..nvim_dir..'/tty-test"])')
+ command('call termopen(["'..testprg('tty-test')..'"])')
feed_command('startinsert')
end)
@@ -523,6 +525,33 @@ describe('buffer cursor position is correct in terminal without number column',
eq({6, 1}, eval('nvim_win_get_cursor(0)'))
end)
end)
+
+ it('at the end of a line with trailing spaces #16234', function()
+ setup_ex_register('aaaaaaaa ')
+ feed('<C-R>r')
+ screen:expect([[
+ |
+ |
+ |
+ |
+ Entering Ex mode. Type "visual" to go to Normal mode. |
+ :aaaaaaaa {1: } |
+ {3:-- TERMINAL --} |
+ ]])
+ matches('^:aaaaaaaa [ ]*$', eval('nvim_get_current_line()'))
+ eq({6, 13}, eval('nvim_win_get_cursor(0)'))
+ feed([[<C-\><C-N>]])
+ screen:expect([[
+ |
+ |
+ |
+ |
+ Entering Ex mode. Type "visual" to go to Normal mode. |
+ :aaaaaaaa ^ {2: } |
+ |
+ ]])
+ eq({6, 12}, eval('nvim_win_get_cursor(0)'))
+ end)
end)
describe('buffer cursor position is correct in terminal with number column', function()
@@ -876,4 +905,31 @@ describe('buffer cursor position is correct in terminal with number column', fun
eq({6, 1}, eval('nvim_win_get_cursor(0)'))
end)
end)
+
+ it('at the end of a line with trailing spaces #16234', function()
+ setup_ex_register('aaaaaaaa ')
+ feed('<C-R>r')
+ screen:expect([[
+ {7: 1 } |
+ {7: 2 } |
+ {7: 3 } |
+ {7: 4 } |
+ {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
+ {7: 6 }:aaaaaaaa {1: } |
+ {3:-- TERMINAL --} |
+ ]])
+ matches('^:aaaaaaaa [ ]*$', eval('nvim_get_current_line()'))
+ eq({6, 13}, eval('nvim_win_get_cursor(0)'))
+ feed([[<C-\><C-N>]])
+ screen:expect([[
+ {7: 1 } |
+ {7: 2 } |
+ {7: 3 } |
+ {7: 4 } |
+ {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
+ {7: 6 }:aaaaaaaa ^ {2: } |
+ |
+ ]])
+ eq({6, 12}, eval('nvim_win_get_cursor(0)'))
+ end)
end)
diff --git a/test/functional/terminal/edit_spec.lua b/test/functional/terminal/edit_spec.lua
index fabc5524ed..aeb4b7cc2e 100644
--- a/test/functional/terminal/edit_spec.lua
+++ b/test/functional/terminal/edit_spec.lua
@@ -3,7 +3,7 @@ local screen = require('test.functional.ui.screen')
local curbufmeths = helpers.curbufmeths
local curwinmeths = helpers.curwinmeths
-local nvim_dir = helpers.nvim_dir
+local testprg = helpers.testprg
local command = helpers.command
local funcs = helpers.funcs
local meths = helpers.meths
@@ -21,7 +21,7 @@ describe(':edit term://*', function()
before_each(function()
clear()
- meths.set_option('shell', nvim_dir .. '/shell-test')
+ meths.set_option('shell', testprg('shell-test'))
meths.set_option('shellcmdflag', 'EXE')
end)
@@ -36,6 +36,7 @@ describe(':edit term://*', function()
end)
it("runs TermOpen early enough to set buffer-local 'scrollback'", function()
+ if helpers.pending_win32(pending) then return end
local columns, lines = 20, 4
local scr = get_screen(columns, lines)
local rep = 97
diff --git a/test/functional/terminal/ex_terminal_spec.lua b/test/functional/terminal/ex_terminal_spec.lua
index 065dd72485..23b69319f0 100644
--- a/test/functional/terminal/ex_terminal_spec.lua
+++ b/test/functional/terminal/ex_terminal_spec.lua
@@ -2,7 +2,7 @@ local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local assert_alive = helpers.assert_alive
local clear, poke_eventloop, nvim = helpers.clear, helpers.poke_eventloop, helpers.nvim
-local nvim_dir, source, eq = helpers.nvim_dir, helpers.source, helpers.eq
+local testprg, source, eq = helpers.testprg, helpers.source, helpers.eq
local feed = helpers.feed
local feed_command, eval = helpers.feed_command, helpers.eval
local funcs = helpers.funcs
@@ -28,7 +28,7 @@ describe(':terminal', function()
echomsg "msg3"
]])
-- Invoke a command that emits frequent terminal activity.
- feed([[:terminal "]]..nvim_dir..[[/shell-test" REP 9999 !terminal_output!<cr>]])
+ feed([[:terminal "]]..testprg('shell-test')..[[" REP 9999 !terminal_output!<cr>]])
feed([[<C-\><C-N>]])
poke_eventloop()
-- Wait for some terminal activity.
@@ -131,7 +131,7 @@ describe(':terminal (with fake shell)', function()
screen = Screen.new(50, 4)
screen:attach({rgb=false})
-- shell-test.c is a fake shell that prints its arguments and exits.
- nvim('set_option', 'shell', nvim_dir..'/shell-test')
+ nvim('set_option', 'shell', testprg('shell-test'))
nvim('set_option', 'shellcmdflag', 'EXE')
end)
@@ -142,6 +142,7 @@ describe(':terminal (with fake shell)', function()
end
it('with no argument, acts like termopen()', function()
+ if helpers.pending_win32(pending) then return end
terminal_with_fake_shell()
retry(nil, 4 * screen.timeout, function()
screen:expect([[
@@ -165,7 +166,8 @@ describe(':terminal (with fake shell)', function()
end)
it("with no argument, but 'shell' has arguments, acts like termopen()", function()
- nvim('set_option', 'shell', nvim_dir..'/shell-test -t jeff')
+ if helpers.pending_win32(pending) then return end
+ nvim('set_option', 'shell', testprg('shell-test')..' -t jeff')
terminal_with_fake_shell()
screen:expect([[
^jeff $ |
@@ -176,6 +178,7 @@ describe(':terminal (with fake shell)', function()
end)
it('executes a given command through the shell', function()
+ if helpers.pending_win32(pending) then return end
command('set shellxquote=') -- win: avoid extra quotes
terminal_with_fake_shell('echo hi')
screen:expect([[
@@ -187,7 +190,8 @@ describe(':terminal (with fake shell)', function()
end)
it("executes a given command through the shell, when 'shell' has arguments", function()
- nvim('set_option', 'shell', nvim_dir..'/shell-test -t jeff')
+ if helpers.pending_win32(pending) then return end
+ nvim('set_option', 'shell', testprg('shell-test')..' -t jeff')
command('set shellxquote=') -- win: avoid extra quotes
terminal_with_fake_shell('echo hi')
screen:expect([[
@@ -199,6 +203,7 @@ describe(':terminal (with fake shell)', function()
end)
it('allows quotes and slashes', function()
+ if helpers.pending_win32(pending) then return end
command('set shellxquote=') -- win: avoid extra quotes
terminal_with_fake_shell([[echo 'hello' \ "world"]])
screen:expect([[
@@ -235,6 +240,7 @@ describe(':terminal (with fake shell)', function()
end)
it('works with :find', function()
+ if helpers.pending_win32(pending) then return end
terminal_with_fake_shell()
screen:expect([[
^ready $ |
@@ -253,6 +259,7 @@ describe(':terminal (with fake shell)', function()
end)
it('works with gf', function()
+ if helpers.pending_win32(pending) then return end
command('set shellxquote=') -- win: avoid extra quotes
terminal_with_fake_shell([[echo "scripts/shadacat.py"]])
retry(nil, 4 * screen.timeout, function()
diff --git a/test/functional/terminal/helpers.lua b/test/functional/terminal/helpers.lua
index d909888613..bcfd3559e6 100644
--- a/test/functional/terminal/helpers.lua
+++ b/test/functional/terminal/helpers.lua
@@ -3,11 +3,13 @@
-- operate on the _host_ session, _not_ the child session.
local helpers = require('test.functional.helpers')(nil)
local Screen = require('test.functional.ui.screen')
-local nvim_dir = helpers.nvim_dir
+local testprg = helpers.testprg
local feed_command, nvim = helpers.feed_command, helpers.nvim
local function feed_data(data)
- nvim('set_var', 'term_data', data)
+ -- A string containing NUL bytes is not converted to a Blob when
+ -- calling nvim_set_var() API, so convert it using Lua instead.
+ nvim('exec_lua', 'vim.g.term_data = ...', {data})
nvim('command', 'call jobsend(b:terminal_job_id, term_data)')
end
@@ -35,7 +37,7 @@ local function clear_attrs() feed_termcode('[0;10m') end
local function enable_mouse() feed_termcode('[?1002h') end
local function disable_mouse() feed_termcode('[?1002l') end
-local default_command = '["'..nvim_dir..'/tty-test'..'"]'
+local default_command = '["'..testprg('tty-test')..'"]'
local function screen_setup(extra_rows, command, cols, opts)
extra_rows = extra_rows and extra_rows or 0
@@ -94,7 +96,7 @@ local function screen_setup(extra_rows, command, cols, opts)
table.insert(expected, '{3:-- TERMINAL --}' .. ((' '):rep(cols - 14)))
screen:expect(table.concat(expected, '|\n')..'|')
else
- -- This eval also acts as a wait().
+ -- This eval also acts as a poke_eventloop().
if 0 == nvim('eval', "exists('b:terminal_job_id')") then
error("terminal job failed to start")
end
diff --git a/test/functional/terminal/highlight_spec.lua b/test/functional/terminal/highlight_spec.lua
index 8d3f0218af..28ca07d815 100644
--- a/test/functional/terminal/highlight_spec.lua
+++ b/test/functional/terminal/highlight_spec.lua
@@ -2,7 +2,7 @@ local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local thelpers = require('test.functional.terminal.helpers')
local feed, clear, nvim = helpers.feed, helpers.clear, helpers.nvim
-local nvim_dir, command = helpers.nvim_dir, helpers.command
+local testprg, command = helpers.testprg, helpers.command
local nvim_prog_abs = helpers.nvim_prog_abs
local eq, eval = helpers.eq, helpers.eval
local funcs = helpers.funcs
@@ -28,7 +28,7 @@ describe(':terminal highlight', function()
[11] = {background = 11},
})
screen:attach({rgb=false})
- command('enew | call termopen(["'..nvim_dir..'/tty-test"])')
+ command(("enew | call termopen(['%s'])"):format(testprg('tty-test')))
feed('i')
screen:expect([[
tty ready |
@@ -173,7 +173,7 @@ describe(':terminal highlight forwarding', function()
[4] = {{foreground = tonumber('0xff8000')}, {}},
})
screen:attach()
- command('enew | call termopen(["'..nvim_dir..'/tty-test"])')
+ command(("enew | call termopen(['%s'])"):format(testprg('tty-test')))
feed('i')
screen:expect([[
tty ready |
@@ -214,7 +214,7 @@ describe(':terminal highlight with custom palette', function()
clear()
screen = Screen.new(50, 7)
screen:set_default_attr_ids({
- [1] = {foreground = tonumber('0x123456')}, -- no fg_indexed when overriden
+ [1] = {foreground = tonumber('0x123456')}, -- no fg_indexed when overridden
[2] = {foreground = 12},
[3] = {bold = true, reverse = true},
[5] = {background = 11},
@@ -225,7 +225,7 @@ describe(':terminal highlight with custom palette', function()
})
screen:attach({rgb=true})
nvim('set_var', 'terminal_color_3', '#123456')
- command('enew | call termopen(["'..nvim_dir..'/tty-test"])')
+ command(("enew | call termopen(['%s'])"):format(testprg('tty-test')))
feed('i')
screen:expect([[
tty ready |
@@ -304,10 +304,17 @@ describe('synIDattr()', function()
eq('79', eval('synIDattr(hlID("Keyword"), "fg")'))
end)
- it('returns "1" if group has "strikethrough" attribute', function()
- eq('', eval('synIDattr(hlID("Normal"), "strikethrough")'))
- eq('1', eval('synIDattr(hlID("Keyword"), "strikethrough")'))
- eq('1', eval('synIDattr(hlID("Keyword"), "strikethrough", "gui")'))
+ it('returns "1" if group has given highlight attribute', function()
+ local hl_attrs = {
+ 'underline', 'undercurl', 'underdouble', 'underdotted', 'underdashed', 'strikethrough'
+ }
+ for _,hl_attr in ipairs(hl_attrs) do
+ local context = 'using ' .. hl_attr .. ' attr'
+ command('highlight Keyword cterm=' .. hl_attr .. ' gui=' .. hl_attr)
+ eq('', eval('synIDattr(hlID("Normal"), "'.. hl_attr .. '")'), context)
+ eq('1', eval('synIDattr(hlID("Keyword"), "' .. hl_attr .. '")'), context)
+ eq('1', eval('synIDattr(hlID("Keyword"), "' .. hl_attr .. '", "gui")'), context)
+ end
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/terminal/scrollback_spec.lua b/test/functional/terminal/scrollback_spec.lua
index 11bdc73a47..b491cb2735 100644
--- a/test/functional/terminal/scrollback_spec.lua
+++ b/test/functional/terminal/scrollback_spec.lua
@@ -2,10 +2,11 @@ local Screen = require('test.functional.ui.screen')
local helpers = require('test.functional.helpers')(after_each)
local thelpers = require('test.functional.terminal.helpers')
local clear, eq, curbuf = helpers.clear, helpers.eq, helpers.curbuf
-local feed, nvim_dir, feed_command = helpers.feed, helpers.nvim_dir, helpers.feed_command
+local feed, testprg, feed_command = helpers.feed, helpers.testprg, helpers.feed_command
local iswin = helpers.iswin
local eval = helpers.eval
local command = helpers.command
+local matches = helpers.matches
local poke_eventloop = helpers.poke_eventloop
local retry = helpers.retry
local curbufmeths = helpers.curbufmeths
@@ -345,10 +346,11 @@ end)
describe(':terminal prints more lines than the screen height and exits', function()
it('will push extra lines to scrollback', function()
+ if helpers.pending_win32(pending) then return end
clear()
local screen = Screen.new(30, 7)
screen:attach({rgb=false})
- feed_command('call termopen(["'..nvim_dir..'/tty-test", "10"]) | startinsert')
+ feed_command(("call termopen(['%s', '10']) | startinsert"):format(testprg('tty-test')))
poke_eventloop()
screen:expect([[
line6 |
@@ -380,7 +382,7 @@ describe("'scrollback' option", function()
local function set_fake_shell()
-- shell-test.c is a fake shell that prints its arguments and exits.
- nvim('set_option', 'shell', nvim_dir..'/shell-test')
+ nvim('set_option', 'shell', testprg('shell-test'))
nvim('set_option', 'shellcmdflag', 'EXE')
end
@@ -401,7 +403,7 @@ describe("'scrollback' option", function()
end
curbufmeths.set_option('scrollback', 0)
- feed_data(nvim_dir..'/shell-test REP 31 line'..(iswin() and '\r' or '\n'))
+ feed_data(('%s REP 31 line%s'):format(testprg('shell-test'), iswin() and '\r' or '\n'))
screen:expect{any='30: line '}
retry(nil, nil, function() expect_lines(7) end)
end)
@@ -421,7 +423,7 @@ describe("'scrollback' option", function()
-- Wait for prompt.
screen:expect{any='%$'}
- feed_data(nvim_dir.."/shell-test REP 31 line"..(iswin() and '\r' or '\n'))
+ feed_data(('%s REP 31 line%s'):format(testprg('shell-test'), iswin() and '\r' or '\n'))
screen:expect{any='30: line '}
retry(nil, nil, function() expect_lines(33, 2) end)
@@ -434,7 +436,7 @@ describe("'scrollback' option", function()
-- 'scrollback' option is synchronized with the internal sb_buffer.
command('sleep 100m')
- feed_data(nvim_dir.."/shell-test REP 41 line"..(iswin() and '\r' or '\n'))
+ feed_data(('%s REP 41 line%s'):format(testprg('shell-test'), iswin() and '\r' or '\n'))
if iswin() then
screen:expect{grid=[[
37: line |
@@ -459,8 +461,36 @@ describe("'scrollback' option", function()
expect_lines(58)
-- Verify off-screen state
- eq((iswin() and '36: line' or '35: line'), eval("getline(line('w0') - 1)"))
- eq((iswin() and '27: line' or '26: line'), eval("getline(line('w0') - 10)"))
+ matches((iswin() and '^36: line[ ]*$' or '^35: line[ ]*$'), eval("getline(line('w0') - 1)"))
+ matches((iswin() and '^27: line[ ]*$' or '^26: line[ ]*$'), eval("getline(line('w0') - 10)"))
+ end)
+
+ it('deletes extra lines immediately', function()
+ -- Scrollback is 10 on screen_setup
+ local screen = thelpers.screen_setup(nil, nil, 30)
+ local lines = {}
+ for i = 1, 30 do
+ table.insert(lines, 'line'..tostring(i))
+ end
+ table.insert(lines, '')
+ feed_data(lines)
+ screen:expect([[
+ line26 |
+ line27 |
+ line28 |
+ line29 |
+ line30 |
+ {1: } |
+ {3:-- TERMINAL --} |
+ ]])
+ local term_height = 6 -- Actual terminal screen height, not the scrollback
+ -- Initial
+ local scrollback = curbufmeths.get_option('scrollback')
+ eq(scrollback + term_height, eval('line("$")'))
+ -- Reduction
+ scrollback = scrollback - 2
+ curbufmeths.set_option('scrollback', scrollback)
+ eq(scrollback + term_height, eval('line("$")'))
end)
it('defaults to 10000 in :terminal buffers', function()
@@ -576,6 +606,7 @@ describe("pending scrollback line handling", function()
end)
it("does not crash after nvim_buf_call #14891", function()
+ if helpers.pending_win32(pending) then return end
exec_lua [[
local a = vim.api
local bufnr = a.nvim_create_buf(false, true)
diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua
index faf44fa01d..dd88379344 100644
--- a/test/functional/terminal/tui_spec.lua
+++ b/test/functional/terminal/tui_spec.lua
@@ -14,12 +14,13 @@ local feed_command = helpers.feed_command
local feed_data = thelpers.feed_data
local clear = helpers.clear
local command = helpers.command
-local nvim_dir = helpers.nvim_dir
+local testprg = helpers.testprg
local retry = helpers.retry
local nvim_prog = helpers.nvim_prog
local nvim_set = helpers.nvim_set
local ok = helpers.ok
local read_file = helpers.read_file
+local funcs = helpers.funcs
if helpers.pending_win32(pending) then return end
@@ -85,6 +86,24 @@ describe('TUI', function()
assert_alive()
end)
+ it('resize at startup', function()
+ -- Issues: #17285 #15044 #11330
+ screen:try_resize(50, 10)
+ feed_command([[call termopen([v:progpath, '--clean', '--cmd', 'let start = reltime() | while v:true | if reltimefloat(reltime(start)) > 2 | break | endif | endwhile']) | sleep 500m | vs new]])
+ screen:expect([[
+ {1: } │ |
+ {4:~ }│{4:~ }|
+ {4:~ }│{4:~ }|
+ {4:~ }│{4:~ }|
+ {4:~ }│{4:~ }|
+ {4:~ }│{5:[No Name] 0,0-1 All}|
+ {4:~ }│ |
+ {5:new }{MATCH:<.*[/\]nvim }|
+ |
+ {3:-- TERMINAL --} |
+ ]])
+ end)
+
it('accepts resize while pager is active', function()
child_session:request("nvim_command", [[
set more
@@ -214,7 +233,7 @@ describe('TUI', function()
]])
end)
- it('interprets ESC+key as ALT chord', function()
+ it('interprets ESC+key as ALT chord in i_CTRL-V', function()
-- Vim represents ALT/META by setting the "high bit" of the modified key:
-- ALT+j inserts "ê". Nvim does not (#3982).
feed_data('i\022\027j')
@@ -229,6 +248,38 @@ describe('TUI', function()
]])
end)
+ it('interprets <Esc>[27u as <Esc>', function()
+ feed_command('nnoremap <M-;> <Nop>')
+ feed_command('nnoremap <Esc> AESC<Esc>')
+ feed_command('nnoremap ; Asemicolon<Esc>')
+ feed_data('\027[27u;')
+ screen:expect([[
+ ESCsemicolo{1:n} |
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]])
+ -- <Esc>; should be recognized as <M-;> when <M-;> is mapped
+ feed_data('\027;')
+ screen:expect_unchanged()
+ end)
+
+ it('interprets <Esc><Nul> as <M-C-Space> #17198', function()
+ feed_data('i\022\027\000')
+ screen:expect([[
+ <M-C-Space>{1: } |
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] [+] }|
+ {3:-- INSERT --} |
+ {3:-- TERMINAL --} |
+ ]])
+ end)
+
it('accepts ASCII control sequences', function()
feed_data('i')
feed_data('\022\007') -- ctrl+g
@@ -247,6 +298,179 @@ describe('TUI', function()
]], attrs)
end)
+ it('accepts keypad keys from kitty keyboard protocol #19180', function()
+ feed_data('i')
+ feed_data(funcs.nr2char(57399)) -- KP_0
+ feed_data(funcs.nr2char(57400)) -- KP_1
+ feed_data(funcs.nr2char(57401)) -- KP_2
+ feed_data(funcs.nr2char(57402)) -- KP_3
+ feed_data(funcs.nr2char(57403)) -- KP_4
+ feed_data(funcs.nr2char(57404)) -- KP_5
+ feed_data(funcs.nr2char(57405)) -- KP_6
+ feed_data(funcs.nr2char(57406)) -- KP_7
+ feed_data(funcs.nr2char(57407)) -- KP_8
+ feed_data(funcs.nr2char(57408)) -- KP_9
+ feed_data(funcs.nr2char(57409)) -- KP_DECIMAL
+ feed_data(funcs.nr2char(57410)) -- KP_DIVIDE
+ feed_data(funcs.nr2char(57411)) -- KP_MULTIPLY
+ feed_data(funcs.nr2char(57412)) -- KP_SUBTRACT
+ feed_data(funcs.nr2char(57413)) -- KP_ADD
+ feed_data(funcs.nr2char(57414)) -- KP_ENTER
+ feed_data(funcs.nr2char(57415)) -- KP_EQUAL
+ screen:expect([[
+ 0123456789./*-+ |
+ ={1: } |
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] [+] }|
+ {3:-- INSERT --} |
+ {3:-- TERMINAL --} |
+ ]])
+ feed_data(funcs.nr2char(57417)) -- KP_LEFT
+ screen:expect([[
+ 0123456789./*-+ |
+ {1:=} |
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] [+] }|
+ {3:-- INSERT --} |
+ {3:-- TERMINAL --} |
+ ]])
+ feed_data(funcs.nr2char(57418)) -- KP_RIGHT
+ screen:expect([[
+ 0123456789./*-+ |
+ ={1: } |
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] [+] }|
+ {3:-- INSERT --} |
+ {3:-- TERMINAL --} |
+ ]])
+ feed_data(funcs.nr2char(57419)) -- KP_UP
+ screen:expect([[
+ 0{1:1}23456789./*-+ |
+ = |
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] [+] }|
+ {3:-- INSERT --} |
+ {3:-- TERMINAL --} |
+ ]])
+ feed_data(funcs.nr2char(57420)) -- KP_DOWN
+ screen:expect([[
+ 0123456789./*-+ |
+ ={1: } |
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] [+] }|
+ {3:-- INSERT --} |
+ {3:-- TERMINAL --} |
+ ]])
+ feed_data(funcs.nr2char(57425)) -- KP_INSERT
+ screen:expect([[
+ 0123456789./*-+ |
+ ={1: } |
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] [+] }|
+ {3:-- REPLACE --} |
+ {3:-- TERMINAL --} |
+ ]])
+ feed_data('\027[27u') -- ESC
+ screen:expect([[
+ 0123456789./*-+ |
+ {1:=} |
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]])
+ feed_data('\027[57417;5u') -- CTRL + KP_LEFT
+ screen:expect([[
+ {1:0}123456789./*-+ |
+ = |
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]])
+ feed_data('\027[57418;2u') -- SHIFT + KP_RIGHT
+ screen:expect([[
+ 0123456789{1:.}/*-+ |
+ = |
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]])
+ feed_data(funcs.nr2char(57426)) -- KP_DELETE
+ screen:expect([[
+ 0123456789{1:/}*-+ |
+ = |
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]])
+ feed_data(funcs.nr2char(57423)) -- KP_HOME
+ screen:expect([[
+ {1:0}123456789/*-+ |
+ = |
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]])
+ feed_data(funcs.nr2char(57424)) -- KP_END
+ screen:expect([[
+ 0123456789/*-{1:+} |
+ = |
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]])
+ feed_data(':tab split\r:tabnew\r')
+ feed_data(':highlight Tabline ctermbg=NONE ctermfg=NONE cterm=underline\r')
+ local attrs = screen:get_default_attr_ids()
+ attrs[11] = {underline = true}
+ screen:expect([[
+ {11: + [No Name] + [No Name] }{3: [No Name] }{1: }{11:X}|
+ {1: } |
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] }|
+ |
+ {3:-- TERMINAL --} |
+ ]], attrs)
+ feed_data('\027[57421;5u') -- CTRL + KP_PAGE_UP
+ screen:expect([[
+ {11: + [No Name] }{3: + [No Name] }{11: [No Name] }{1: }{11:X}|
+ 0123456789/*-{1:+} |
+ = |
+ {4:~ }|
+ {5:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]], attrs)
+ feed_data('\027[57422;5u') -- CTRL + KP_PAGE_DOWN
+ screen:expect([[
+ {11: + [No Name] + [No Name] }{3: [No Name] }{1: }{11:X}|
+ {1: } |
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] }|
+ |
+ {3:-- TERMINAL --} |
+ ]], attrs)
+ end)
+
it('paste: Insert mode', function()
-- "bracketed paste"
feed_data('i""\027i\027[200~')
@@ -271,7 +495,7 @@ describe('TUI', function()
{3:-- TERMINAL --} |
]])
feed_data('\027[201~') -- End paste.
- feed_data('\027\000') -- ESC: go to Normal mode.
+ feed_data('\027[27u') -- ESC: go to Normal mode.
wait_for_mode('n')
screen:expect([[
"pasted from termina{1:l}" |
@@ -323,7 +547,7 @@ describe('TUI', function()
feed_data('just paste it™')
feed_data('\027[201~')
screen:expect{grid=[[
- thisjust paste it™{1:3} is here |
+ thisjust paste it{1:™}3 is here |
|
{4:~ }|
{4:~ }|
@@ -353,7 +577,7 @@ describe('TUI', function()
return
end
feed_data(':set statusline=^^^^^^^\n')
- feed_data(':terminal '..nvim_dir..'/tty-test\n')
+ feed_data(':terminal '..testprg('tty-test')..'\n')
feed_data('i')
screen:expect{grid=[[
tty ready |
@@ -379,7 +603,7 @@ describe('TUI', function()
end)
it('paste: normal-mode (+CRLF #10872)', function()
- feed_data(':set ruler')
+ feed_data(':set ruler | echo')
wait_for_mode('c')
feed_data('\n')
wait_for_mode('n')
@@ -423,13 +647,13 @@ describe('TUI', function()
expect_child_buf_lines(expected_crlf)
feed_data('u')
expect_child_buf_lines({''})
+ feed_data(':echo')
+ wait_for_mode('c')
+ feed_data('\n')
+ wait_for_mode('n')
-- CRLF input
feed_data('\027[200~'..table.concat(expected_lf,'\r\n')..'\027[201~')
- screen:expect{
- grid=expected_grid1:gsub(
- ':set ruler *',
- '3 fewer lines; before #1 0 seconds ago '),
- attr_ids=expected_attr}
+ screen:expect{grid=expected_grid1, attr_ids=expected_attr}
expect_child_buf_lines(expected_crlf)
end)
@@ -453,7 +677,7 @@ describe('TUI', function()
{3:-- TERMINAL --} |
]]}
-- Dot-repeat/redo.
- feed_data('\027\000')
+ feed_data('\027[27u')
wait_for_mode('n')
feed_data('.')
screen:expect{grid=[[
@@ -499,7 +723,7 @@ describe('TUI', function()
vim.paste = function(lines, phase) error("fake fail") end
]], {})
-- Prepare something for dot-repeat/redo.
- feed_data('ifoo\n\027\000')
+ feed_data('ifoo\n\027[27u')
wait_for_mode('n')
screen:expect{grid=[[
foo |
@@ -541,7 +765,7 @@ describe('TUI', function()
{3:-- TERMINAL --} |
]]}
-- Editor should still work after failed/drained paste.
- feed_data('ityped input...\027\000')
+ feed_data('ityped input...\027[27u')
screen:expect{grid=[[
foo |
foo |
@@ -575,7 +799,7 @@ describe('TUI', function()
vim.paste = function(lines, phase) return false end
]], {})
feed_data('\027[200~line A\nline B\n\027[201~')
- feed_data('ifoo\n\027\000')
+ feed_data('ifoo\n\027[27u')
expect_child_buf_lines({'foo',''})
end)
@@ -669,7 +893,7 @@ describe('TUI', function()
{3:-- INSERT --} |
{3:-- TERMINAL --} |
]])
- feed_data('\027\000') -- ESC: go to Normal mode.
+ feed_data('\027[27u') -- ESC: go to Normal mode.
wait_for_mode('n')
-- Dot-repeat/redo.
feed_data('.')
@@ -871,7 +1095,7 @@ describe('TUI', function()
feed_data(':set statusline=^^^^^^^\n')
feed_data(':set termguicolors\n')
- feed_data(':terminal '..nvim_dir..'/tty-test\n')
+ feed_data(':terminal '..testprg('tty-test')..'\n')
-- Depending on platform the above might or might not fit in the cmdline
-- so clear it for consistent behavior.
feed_data(':\027')
@@ -1098,7 +1322,7 @@ describe('TUI FocusGained/FocusLost', function()
end)
it('in terminal-mode', function()
- feed_data(':set shell='..nvim_dir..'/shell-test\n')
+ feed_data(':set shell='..testprg('shell-test')..'\n')
feed_data(':set noshowmode laststatus=0\n')
feed_data(':terminal\n')
diff --git a/test/functional/terminal/window_spec.lua b/test/functional/terminal/window_spec.lua
index 9f278fd157..0d3295cf32 100644
--- a/test/functional/terminal/window_spec.lua
+++ b/test/functional/terminal/window_spec.lua
@@ -18,6 +18,7 @@ describe(':terminal window', function()
end)
it('sets topline correctly #8556', function()
+ if helpers.pending_win32(pending) then return end
-- Test has hardcoded assumptions of dimensions.
eq(7, eval('&lines'))
feed_data('\n\n\n') -- Add blank lines.
diff --git a/test/functional/treesitter/highlight_spec.lua b/test/functional/treesitter/highlight_spec.lua
index 175525b3f2..5ec0a8a060 100644
--- a/test/functional/treesitter/highlight_spec.lua
+++ b/test/functional/treesitter/highlight_spec.lua
@@ -23,7 +23,7 @@ local hl_query = [[
"enum" @type
"extern" @type
- (string_literal) @string
+ (string_literal) @string.nonexistent-specializer-for-string.should-fallback-to-string
(number_literal) @number
(char_literal) @string
@@ -613,4 +613,131 @@ describe('treesitter highlighting', function()
[12] = {background = Screen.colors.Red, bold = true, foreground = Screen.colors.Grey100};
}}
end)
+
+ it("allows to use captures with dots (don't use fallback when specialization of foo exists)", function()
+ if pending_c_parser(pending) then return end
+
+ insert([[
+ char* x = "Will somebody ever read this?";
+ ]])
+
+ screen:expect{grid=[[
+ char* x = "Will somebody ever read this?"; |
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+
+ exec_lua [[
+ local parser = vim.treesitter.get_parser(0, "c", {})
+ local highlighter = vim.treesitter.highlighter
+ highlighter.hl_map['foo.bar'] = 'Type'
+ highlighter.hl_map['foo'] = 'String'
+ test_hl = highlighter.new(parser, {queries = {c = "(primitive_type) @foo.bar (string_literal) @foo"}})
+ ]]
+
+ screen:expect{grid=[[
+ {3:char}* x = {5:"Will somebody ever read this?"}; |
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+ end)
+
+ it("supports conceal attribute", function()
+ if pending_c_parser(pending) then return end
+ insert(hl_text)
+
+ -- conceal can be empty or a single cchar.
+ exec_lua [=[
+ vim.opt.cole = 2
+ local parser = vim.treesitter.get_parser(0, "c")
+ test_hl = vim.treesitter.highlighter.new(parser, {queries = {c = [[
+ ("static" @keyword
+ (set! conceal "R"))
+
+ ((identifier) @Identifier
+ (set! conceal "")
+ (eq? @Identifier "lstate"))
+ ]]}})
+ ]=]
+
+ screen:expect{grid=[[
+ /// Schedule Lua callback on main loop's event queue |
+ {4:R} int nlua_schedule(lua_State *const ) |
+ { |
+ if (lua_type(, 1) != LUA_TFUNCTION |
+ || != ) { |
+ lua_pushliteral(, "vim.schedule: expected function"); |
+ return lua_error(); |
+ } |
+ |
+ LuaRef cb = nlua_ref(, 1); |
+ |
+ multiqueue_put(main_loop.events, nlua_schedule_event, |
+ 1, (void *)(ptrdiff_t)cb); |
+ return 0; |
+ ^} |
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+ end)
+
+ it("hl_map has the correct fallback behavior", function()
+ exec_lua [[
+ local hl_map = vim.treesitter.highlighter.hl_map
+ hl_map["foo"] = 1
+ hl_map["foo.bar"] = 2
+ hl_map["foo.bar.baz"] = 3
+
+ assert(hl_map["foo"] == 1)
+ assert(hl_map["foo.a.b.c.d"] == 1)
+ assert(hl_map["foo.bar"] == 2)
+ assert(hl_map["foo.bar.a.b.c.d"] == 2)
+ assert(hl_map["foo.bar.baz"] == 3)
+ assert(hl_map["foo.bar.baz.d"] == 3)
+
+ hl_map["FOO"] = 1
+ hl_map["FOO.BAR"] = 2
+ assert(hl_map["FOO.BAR.BAZ"] == 2)
+
+ hl_map["foo.missing.exists"] = 3
+ assert(hl_map["foo.missing"] == 1)
+ assert(hl_map["foo.missing.exists"] == 3)
+ assert(hl_map["foo.missing.exists.bar"] == 3)
+ assert(hl_map["total.nonsense.but.a.lot.of.dots"] == nil)
+ -- It will not perform a second look up of this variable but return a sentinel value
+ assert(hl_map["total.nonsense.but.a.lot.of.dots"] == "__notfound")
+ ]]
+
+ end)
end)
diff --git a/test/functional/treesitter/language_spec.lua b/test/functional/treesitter/language_spec.lua
index afb17dd2cf..30585be328 100644
--- a/test/functional/treesitter/language_spec.lua
+++ b/test/functional/treesitter/language_spec.lua
@@ -2,6 +2,7 @@ local helpers = require('test.functional.helpers')(after_each)
local clear = helpers.clear
local eq = helpers.eq
+local command = helpers.command
local exec_lua = helpers.exec_lua
local pcall_err = helpers.pcall_err
local matches = helpers.matches
@@ -67,5 +68,16 @@ describe('treesitter API', function()
end
eq({true,true}, {has_named,has_anonymous})
end)
+
+ it('checks if vim.treesitter.get_parser tries to create a new parser on filetype change', function ()
+ if pending_c_parser(pending) then return end
+ command("set filetype=c")
+ -- Should not throw an error when filetype is c
+ eq('c', exec_lua("return vim.treesitter.get_parser(0):lang()"))
+ command("set filetype=borklang")
+ -- Should throw an error when filetype changes to borklang
+ eq("Error executing lua: .../language.lua:0: no parser for 'borklang' language, see :help treesitter-parsers",
+ pcall_err(exec_lua, "new_parser = vim.treesitter.get_parser(0)"))
+ end)
end)
diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua
index 911fa017ab..7f3b0e770a 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 function q(n)
+ return exec_lua ([[
+ local query, n = ...
+ local before = vim.loop.hrtime()
+ for i=1,n,1 do
+ cquery = vim.treesitter.parse_query("c", ...)
+ end
+ local after = vim.loop.hrtime()
+ return after - before
+ ]], long_query, n)
+ end
+
+ local firstrun = q(1)
+ local manyruns = q(100)
+
+ -- First run should be at least 4x slower.
+ assert(400 * manyruns < firstrun, ('firstrun: %d ms, manyruns: %d ms'):format(firstrun / 1000, manyruns / 1000))
+ end)
+
it('support query and iter by capture', function()
insert(test_text)
@@ -264,6 +285,25 @@ end]]
eq(true, result)
end)
+ it('support getting empty text if node range is zero width', function()
+ local text = [[
+```lua
+{}
+```]]
+ insert(text)
+ local result = exec_lua([[
+ local fake_node = {}
+ function fake_node:start()
+ return 1, 0, 7
+ end
+ function fake_node:end_()
+ return 1, 0, 7
+ end
+ return vim.treesitter.get_node_text(fake_node, 0) == ''
+ ]])
+ eq(true, result)
+ end)
+
it('can match special regex characters like \\ * + ( with `vim-match?`', function()
insert('char* astring = "\\n"; (1 + 1) * 2 != 2;')
@@ -645,6 +685,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..7c0831bd09 100644
--- a/test/functional/ui/bufhl_spec.lua
+++ b/test/functional/ui/bufhl_spec.lua
@@ -564,6 +564,16 @@ describe('Buffer highlighting', function()
]]
clear_namespace(id, 0, -1)
+ screen:expect{grid=[[
+ fooba^r |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
set_extmark(id, 0, 0, {
end_line = 0,
@@ -755,12 +765,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/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua
index 9c746b99bd..384761ab17 100644
--- a/test/functional/ui/cmdline_highlight_spec.lua
+++ b/test/functional/ui/cmdline_highlight_spec.lua
@@ -33,7 +33,7 @@ before_each(function()
let g:NUM_LVLS = 4
function Redraw()
mode
- return ''
+ return "\<Ignore>"
endfunction
let g:id = ''
cnoremap <expr> {REDRAW} Redraw()
@@ -42,7 +42,7 @@ before_each(function()
let Cb = g:Nvim_color_input{g:id}
let out = input({'prompt': ':', 'highlight': Cb})
let g:out{id} = out
- return (a:do_return ? out : '')
+ return (a:do_return ? out : "\<Ignore>")
endfunction
nnoremap <expr> {PROMPT} DoPrompt(0)
cnoremap <expr> {PROMPT} DoPrompt(1)
@@ -410,7 +410,7 @@ describe('Command-line coloring', function()
end)
it('stops executing callback after a number of errors', function()
set_color_cb('SplittedMultibyteStart')
- start_prompt('let x = "«»«»«»«»«»"\n')
+ start_prompt('let x = "«»«»«»«»«»"')
screen:expect([[
{EOB:~ }|
{EOB:~ }|
@@ -419,7 +419,7 @@ describe('Command-line coloring', function()
:let x = " |
{ERR:E5405: Chunk 0 start 10 splits multibyte}|
{ERR: character} |
- ^:let x = "«»«»«»«»«»" |
+ :let x = "«»«»«»«»«»"^ |
]])
feed('\n')
screen:expect([[
@@ -432,6 +432,7 @@ describe('Command-line coloring', function()
{EOB:~ }|
|
]])
+ feed('\n')
eq('let x = "«»«»«»«»«»"', meths.get_var('out'))
local msg = '\nE5405: Chunk 0 start 10 splits multibyte character'
eq(msg:rep(1), funcs.execute('messages'))
@@ -474,14 +475,14 @@ describe('Command-line coloring', function()
]])
feed('\n')
screen:expect([[
- |
+ ^ |
{EOB:~ }|
{EOB:~ }|
{EOB:~ }|
{EOB:~ }|
{EOB:~ }|
{EOB:~ }|
- ^:echo 42 |
+ :echo 42 |
]])
feed('\n')
eq('echo 42', meths.get_var('out'))
diff --git a/test/functional/ui/cmdline_spec.lua b/test/functional/ui/cmdline_spec.lua
index ad23402ff9..eb0a14da31 100644
--- a/test/functional/ui/cmdline_spec.lua
+++ b/test/functional/ui/cmdline_spec.lua
@@ -4,6 +4,9 @@ local clear, feed = helpers.clear, helpers.feed
local source = helpers.source
local command = helpers.command
local assert_alive = helpers.assert_alive
+local uname = helpers.uname
+local eval = helpers.eval
+local eq = helpers.eq
local function new_screen(opt)
local screen = Screen.new(25, 5)
@@ -16,6 +19,8 @@ local function new_screen(opt)
[5] = {bold = true, foreground = Screen.colors.SeaGreen4},
[6] = {foreground = Screen.colors.Magenta},
[7] = {bold = true, foreground = Screen.colors.Brown},
+ [8] = {background = Screen.colors.LightGrey},
+ [9] = {bold = true},
})
return screen
end
@@ -824,7 +829,7 @@ describe('cmdline redraw', function()
end)
it('with <Cmd>', function()
- if 'openbsd' == helpers.uname() then
+ if string.find(uname(), 'bsd') then
pending('FIXME #10804')
end
command('cmap a <Cmd>call sin(0)<CR>') -- no-op
@@ -845,6 +850,37 @@ describe('cmdline redraw', function()
456789^ |
]], unchanged=true}
end)
+
+ it('after pressing Ctrl-C in cmdwin in Visual mode #18967', function()
+ screen:try_resize(40, 10)
+ command('set cmdwinheight=3')
+ feed('q:iabc<Esc>vhh')
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:[No Name] }|
+ {1::}^a{8:bc} |
+ {1:~ }|
+ {1:~ }|
+ {3:[Command Line] }|
+ {9:-- VISUAL --} |
+ ]])
+ feed('<C-C>')
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:[No Name] }|
+ {1::}a{8:bc} |
+ {1:~ }|
+ {1:~ }|
+ {3:[Command Line] }|
+ :^abc |
+ ]])
+ end)
end)
describe("cmdline height", function()
@@ -857,3 +893,228 @@ describe("cmdline height", function()
assert_alive()
end)
end)
+
+describe('cmdheight=0', function()
+ local screen
+ before_each(function()
+ clear()
+ screen = Screen.new(25, 5)
+ screen:attach()
+ end)
+
+ it("with cmdheight=1 noruler laststatus=2", function()
+ command("set cmdheight=1 noruler laststatus=2")
+ screen:expect{grid=[[
+ ^ |
+ ~ |
+ ~ |
+ [No Name] |
+ |
+ ]]}
+ end)
+
+ it("with cmdheight=0 noruler laststatus=2", function()
+ command("set cmdheight=0 noruler laststatus=2")
+ screen:expect{grid=[[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ [No Name] |
+ ]]}
+ end)
+
+ it("with cmdheight=0 ruler laststatus=0", function()
+ command("set cmdheight=0 ruler laststatus=0")
+ screen:expect{grid=[[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ]]}
+ end)
+
+ it("with cmdheight=0 ruler laststatus=0", function()
+ command("set cmdheight=0 noruler laststatus=0 showmode")
+ feed('i')
+ screen:expect{grid=[[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ]], showmode={}}
+ feed('<Esc>')
+ eq(0, eval('&cmdheight'))
+ end)
+
+ it("with cmdheight=0 ruler rulerformat laststatus=0", function()
+ command("set cmdheight=0 noruler laststatus=0 rulerformat=%l,%c%= showmode")
+ feed('i')
+ screen:expect{grid=[[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ]], showmode={}}
+ feed('<Esc>')
+ eq(0, eval('&cmdheight'))
+ end)
+
+ it("with showmode", function()
+ command("set cmdheight=1 noruler laststatus=0 showmode")
+ feed('i')
+ screen:expect{grid=[[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ -- INSERT -- |
+ ]]}
+ feed('<Esc>')
+ eq(1, eval('&cmdheight'))
+ end)
+
+ it("when using command line", function()
+ command("set cmdheight=0 noruler laststatus=0")
+ feed(':')
+ screen:expect{grid=[[
+ |
+ ~ |
+ ~ |
+ ~ |
+ :^ |
+ ]]}
+ eq(1, eval('&cmdheight'))
+ feed('<cr>')
+ screen:expect{grid=[[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ]], showmode={}}
+ eq(0, eval('&cmdheight'))
+ end)
+
+ it("when using input()", function()
+ command("set cmdheight=0 noruler laststatus=0")
+ feed(':call input("foo >")<cr>')
+ screen:expect{grid=[[
+ |
+ ~ |
+ ~ |
+ ~ |
+ foo >^ |
+ ]]}
+ eq(1, eval('&cmdheight'))
+ feed('<cr>')
+ screen:expect{grid=[[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ]], showmode={}}
+ eq(0, eval('&cmdheight'))
+ end)
+
+ it("with winbar and splits", function()
+ command("set cmdheight=0 noruler laststatus=3 winbar=foo")
+ feed(':split<CR>')
+ screen:expect{grid=[[
+ foo |
+ |
+ E36: Not enough room |
+ Press ENTER or type comma|
+ nd to continue^ |
+ ]]}
+ feed('<CR>')
+ screen:expect{grid=[[
+ foo |
+ ^ |
+ ~ |
+ ~ |
+ [No Name] |
+ ]]}
+ feed(':')
+ screen:expect{grid=[[
+ foo |
+ |
+ ~ |
+ [No Name] |
+ :^ |
+ ]]}
+ feed('<Esc>')
+ screen:expect{grid=[[
+ foo |
+ ^ |
+ ~ |
+ ~ |
+ [No Name] |
+ ]], showmode={}}
+ eq(0, eval('&cmdheight'))
+
+ assert_alive()
+ end)
+
+ it("when macro with lastline", function()
+ command("set cmdheight=0 display=lastline")
+ feed('qq')
+ screen:expect{grid=[[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ]], showmode={}}
+ feed('q')
+ screen:expect{grid=[[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ]], showmode={}}
+ end)
+
+ it("when substitute text", function()
+ command("set cmdheight=0 noruler laststatus=3")
+ feed('ifoo<ESC>')
+ screen:expect{grid=[[
+ fo^o |
+ ~ |
+ ~ |
+ ~ |
+ [No Name] [+] |
+ ]]}
+
+ feed(':%s/foo/bar/gc<CR>')
+ screen:expect{grid=[[
+ foo |
+ ~ |
+ ~ |
+ [No Name] [+] |
+ replace wi...q/l/^E/^Y)?^ |
+ ]]}
+
+ feed('y')
+ screen:expect{grid=[[
+ ^bar |
+ ~ |
+ ~ |
+ ~ |
+ [No Name] [+] |
+ ]]}
+
+ assert_alive()
+ end)
+
+ it("when window resize", function()
+ command("set cmdheight=0")
+ feed('<C-w>+')
+ eq(0, eval('&cmdheight'))
+ end)
+end)
diff --git a/test/functional/ui/cursor_spec.lua b/test/functional/ui/cursor_spec.lua
index 03cd4bfd06..a1423c98a8 100644
--- a/test/functional/ui/cursor_spec.lua
+++ b/test/functional/ui/cursor_spec.lua
@@ -215,7 +215,7 @@ describe('ui/cursor', function()
m.hl_id = 60
m.attr = {background = Screen.colors.DarkGray}
end
- if m.id_lm then m.id_lm = 61 end
+ if m.id_lm then m.id_lm = 65 end
end
-- Assert the new expectation.
diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua
index 1575cab591..9af5d386db 100644
--- a/test/functional/ui/decorations_spec.lua
+++ b/test/functional/ui/decorations_spec.lua
@@ -30,6 +30,7 @@ describe('decorations providers', function()
[11] = {foreground = Screen.colors.Red, background = tonumber('0x005028')};
[12] = {foreground = tonumber('0x990000')};
[13] = {background = Screen.colors.LightBlue};
+ [14] = {background = Screen.colors.WebGray, foreground = Screen.colors.DarkBlue};
}
end)
@@ -404,6 +405,50 @@ describe('decorations providers', function()
|
]]}
end)
+
+ it('can create and remove signs when CursorMoved autocommand validates botline #18661', function()
+ exec_lua([[
+ local lines = {}
+ for i = 1, 200 do
+ lines[i] = 'hello' .. tostring(i)
+ end
+ vim.api.nvim_buf_set_lines(0, 0, -1, false, lines)
+ ]])
+ setup_provider([[
+ local function on_do(kind, winid, bufnr, topline, botline_guess)
+ if kind == 'win' then
+ if topline < 100 and botline_guess > 100 then
+ vim.api.nvim_buf_set_extmark(bufnr, ns1, 99, -1, { sign_text = 'X' })
+ else
+ vim.api.nvim_buf_clear_namespace(bufnr, ns1, 0, -1)
+ end
+ end
+ end
+ ]])
+ command([[autocmd CursorMoved * call line('w$')]])
+ meths.win_set_cursor(0, {100, 0})
+ screen:expect([[
+ {14: }hello97 |
+ {14: }hello98 |
+ {14: }hello99 |
+ X ^hello100 |
+ {14: }hello101 |
+ {14: }hello102 |
+ {14: }hello103 |
+ |
+ ]])
+ meths.win_set_cursor(0, {1, 0})
+ screen:expect([[
+ ^hello1 |
+ hello2 |
+ hello3 |
+ hello4 |
+ hello5 |
+ hello6 |
+ hello7 |
+ |
+ ]])
+ end)
end)
describe('extmark decorations', function()
@@ -437,6 +482,8 @@ describe('extmark decorations', function()
[22] = {foreground = tonumber('0xb20000'), background = tonumber('0xf13f3f')};
[23] = {foreground = Screen.colors.Magenta1, background = Screen.colors.LightGrey};
[24] = {bold = true};
+ [25] = {background = Screen.colors.LightRed};
+ [26] = {background=Screen.colors.DarkGrey, foreground=Screen.colors.LightGrey};
}
ns = meths.create_namespace 'test'
@@ -456,6 +503,31 @@ for _,item in ipairs(items) do
end
end]]
+ it('empty virtual text at eol should not break colorcolumn #17860', function()
+ insert(example_text)
+ feed('gg')
+ command('set colorcolumn=40')
+ screen:expect([[
+ ^for _,item in ipairs(items) do {25: } |
+ local text, hl_id_cell, count = unp{25:a}ck(item) |
+ if hl_id_cell ~= nil then {25: } |
+ hl_id = hl_id_cell {25: } |
+ end {25: } |
+ for _ = 1, (count or 1) do {25: } |
+ local cell = line[colpos] {25: } |
+ cell.text = text {25: } |
+ cell.hl_id = hl_id {25: } |
+ colpos = colpos+1 {25: } |
+ end {25: } |
+ end {25: } |
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ meths.buf_set_extmark(0, ns, 4, 0, { virt_text={{''}}, virt_text_pos='eol'})
+ screen:expect_unchanged()
+ end)
+
it('can have virtual text of overlay position', function()
insert(example_text)
feed 'gg'
@@ -471,7 +543,9 @@ end]]
-- can "float" beyond end of line
meths.buf_set_extmark(0, ns, 5, 28, { virt_text={{'loopy', 'ErrorMsg'}}, virt_text_pos='overlay'})
-- bound check: right edge of window
- meths.buf_set_extmark(0, ns, 2, 26, { virt_text={{'bork bork bork ' }, {'bork bork bork', 'ErrorMsg'}}, virt_text_pos='overlay'})
+ meths.buf_set_extmark(0, ns, 2, 26, { virt_text={{'bork bork bork '}, {'bork bork bork', 'ErrorMsg'}}, virt_text_pos='overlay'})
+ -- empty virt_text should not change anything
+ meths.buf_set_extmark(0, ns, 6, 16, { virt_text={{''}}, virt_text_pos='overlay'})
screen:expect{grid=[[
^for _,item in ipairs(items) do |
@@ -621,6 +695,8 @@ end]]
meths.buf_set_extmark(0, ns, 2, 10, { virt_text={{'Much', 'ErrorMsg'}}, virt_text_win_col=31, hl_mode='blend'})
meths.buf_set_extmark(0, ns, 3, 15, { virt_text={{'Error', 'ErrorMsg'}}, virt_text_win_col=31, hl_mode='blend'})
meths.buf_set_extmark(0, ns, 7, 21, { virt_text={{'-', 'NonText'}}, virt_text_win_col=4, hl_mode='blend'})
+ -- empty virt_text should not change anything
+ meths.buf_set_extmark(0, ns, 8, 0, { virt_text={{''}}, virt_text_win_col=14, hl_mode='blend'})
screen:expect{grid=[[
^for _,item in ipairs(items) do |
@@ -698,7 +774,7 @@ end]]
]]}
end)
- it('can have virtual text which combines foreground and backround groups', function()
+ it('can have virtual text which combines foreground and background groups', function()
screen:set_default_attr_ids {
[1] = {bold=true, foreground=Screen.colors.Blue};
[2] = {background = tonumber('0x123456'), foreground = tonumber('0xbbbbbb')};
@@ -789,6 +865,20 @@ end]]
]]}
helpers.assert_alive()
end)
+
+ it('conceal #19007', function()
+ screen:try_resize(50, 5)
+ insert('foo\n')
+ command('let &conceallevel=2')
+ meths.buf_set_extmark(0, ns, 0, 0, {end_col=0, end_row=2, conceal='X'})
+ screen:expect([[
+ {26:X} |
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ end)
end)
describe('decorations: virtual lines', function()
@@ -1111,6 +1201,27 @@ if (h->n_buckets < new_n_buckets) { // expand
]]}
end)
+ it('does not cause syntax ml_get error at the end of a buffer #17816', function()
+ command([[syntax region foo keepend start='^foo' end='^$']])
+ command('syntax sync minlines=100')
+ insert('foo')
+ meths.buf_set_extmark(0, ns, 0, 0, {virt_lines = {{{'bar', 'Comment'}}}})
+ screen:expect([[
+ fo^o |
+ {6:bar} |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ end)
+
it('works with a block scrolling up', function()
screen:try_resize(30, 7)
insert("aa\nbb\ncc\ndd\nee\nff\ngg\nhh")
@@ -1362,3 +1473,372 @@ if (h->n_buckets < new_n_buckets) { // expand
end)
end)
+
+describe('decorations: signs', function()
+ local screen, ns
+ before_each(function()
+ clear()
+ screen = Screen.new(50, 10)
+ screen:attach()
+ screen:set_default_attr_ids {
+ [1] = {foreground = Screen.colors.Blue4, background = Screen.colors.Grey};
+ [2] = {foreground = Screen.colors.Blue1, bold = true};
+ [3] = {background = Screen.colors.Yellow1, foreground = Screen.colors.Blue1};
+ }
+
+ ns = meths.create_namespace 'test'
+ meths.win_set_option(0, 'signcolumn', 'auto:9')
+ end)
+
+ local example_text = [[
+l1
+l2
+l3
+l4
+l5
+]]
+
+ it('can add a single sign (no end row)', function()
+ insert(example_text)
+ feed 'gg'
+
+ meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S'})
+
+ screen:expect{grid=[[
+ {1: }^l1 |
+ S l2 |
+ {1: }l3 |
+ {1: }l4 |
+ {1: }l5 |
+ {1: } |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ |
+ ]]}
+
+ end)
+
+ it('can add a single sign (with end row)', function()
+ insert(example_text)
+ feed 'gg'
+
+ meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S', end_row=1})
+
+ screen:expect{grid=[[
+ {1: }^l1 |
+ S l2 |
+ {1: }l3 |
+ {1: }l4 |
+ {1: }l5 |
+ {1: } |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ |
+ ]]}
+
+ end)
+
+ it('can add multiple signs (single extmark)', function()
+ pending('TODO(lewis6991): Support ranged signs')
+ insert(example_text)
+ feed 'gg'
+
+ meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S', end_row = 2})
+
+ screen:expect{grid=[[
+ {1: }^l1 |
+ S l2 |
+ S l3 |
+ {1: }l4 |
+ {1: }l5 |
+ {1: } |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ |
+ ]]}
+
+ end)
+
+ it('can add multiple signs (multiple extmarks)', function()
+ pending('TODO(lewis6991): Support ranged signs')
+ insert(example_text)
+ feed'gg'
+
+ meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S1'})
+ meths.buf_set_extmark(0, ns, 3, -1, {sign_text='S2', end_row = 4})
+
+ screen:expect{grid=[[
+ {1: }^l1 |
+ S1l2 |
+ {1: }l3 |
+ S2l4 |
+ S2l5 |
+ {1: } |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ |
+ ]]}
+
+ end)
+
+ it('can add multiple signs (multiple extmarks) 2', function()
+ insert(example_text)
+ feed 'gg'
+
+ meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S1'})
+ meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S2'})
+
+ screen:expect{grid=[[
+ {1: }^l1 |
+ S2S1l2 |
+ {1: }l3 |
+ {1: }l4 |
+ {1: }l5 |
+ {1: } |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ |
+ ]]}
+
+ -- TODO(lewis6991): Support ranged signs
+ -- meths.buf_set_extmark(1, ns, 1, -1, {sign_text='S3', end_row = 2})
+
+ -- screen:expect{grid=[[
+ -- {1: }^l1 |
+ -- S3S2S1l2 |
+ -- S3{1: }l3 |
+ -- {1: }l4 |
+ -- {1: }l5 |
+ -- {1: } |
+ -- {2:~ }|
+ -- {2:~ }|
+ -- {2:~ }|
+ -- |
+ -- ]]}
+
+ end)
+
+ it('can add multiple signs (multiple extmarks) 3', function()
+ pending('TODO(lewis6991): Support ranged signs')
+
+ insert(example_text)
+ feed 'gg'
+
+ meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S1', end_row=2})
+ meths.buf_set_extmark(0, ns, 2, -1, {sign_text='S2', end_row=3})
+
+ screen:expect{grid=[[
+ {1: }^l1 |
+ S1{1: }l2 |
+ S2S1l3 |
+ S2{1: }l4 |
+ {1: }l5 |
+ {1: } |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ |
+ ]]}
+ end)
+
+ it('can add multiple signs (multiple extmarks) 4', function()
+ insert(example_text)
+ feed 'gg'
+
+ meths.buf_set_extmark(0, ns, 0, -1, {sign_text='S1', end_row=0})
+ meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S2', end_row=1})
+
+ screen:expect{grid=[[
+ S1^l1 |
+ S2l2 |
+ {1: }l3 |
+ {1: }l4 |
+ {1: }l5 |
+ {1: } |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ |
+ ]]}
+ end)
+
+ it('works with old signs', function()
+ insert(example_text)
+ feed 'gg'
+
+ helpers.command('sign define Oldsign text=x')
+ helpers.command([[exe 'sign place 42 line=2 name=Oldsign buffer=' . bufnr('')]])
+
+ meths.buf_set_extmark(0, ns, 0, -1, {sign_text='S1'})
+ meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S2'})
+ meths.buf_set_extmark(0, ns, 0, -1, {sign_text='S4'})
+ meths.buf_set_extmark(0, ns, 2, -1, {sign_text='S5'})
+
+ screen:expect{grid=[[
+ S4S1^l1 |
+ S2x l2 |
+ S5{1: }l3 |
+ {1: }l4 |
+ {1: }l5 |
+ {1: } |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ |
+ ]]}
+ end)
+
+ it('works with old signs (with range)', function()
+ pending('TODO(lewis6991): Support ranged signs')
+ insert(example_text)
+ feed 'gg'
+
+ helpers.command('sign define Oldsign text=x')
+ helpers.command([[exe 'sign place 42 line=2 name=Oldsign buffer=' . bufnr('')]])
+
+ meths.buf_set_extmark(0, ns, 0, -1, {sign_text='S1'})
+ meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S2'})
+ meths.buf_set_extmark(0, ns, 0, -1, {sign_text='S3', end_row = 4})
+ meths.buf_set_extmark(0, ns, 0, -1, {sign_text='S4'})
+ meths.buf_set_extmark(0, ns, 2, -1, {sign_text='S5'})
+
+ screen:expect{grid=[[
+ S3S4S1^l1 |
+ S2S3x l2 |
+ S5S3{1: }l3 |
+ S3{1: }l4 |
+ S3{1: }l5 |
+ {1: } |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ |
+ ]]}
+ end)
+
+ it('can add a ranged sign (with start out of view)', function()
+ pending('TODO(lewis6991): Support ranged signs')
+
+ insert(example_text)
+ command 'set signcolumn=yes:2'
+ feed 'gg'
+ feed '2<C-e>'
+
+ meths.buf_set_extmark(0, ns, 1, -1, {sign_text='X', end_row=3})
+
+ screen:expect{grid=[[
+ X {1: }^l3 |
+ X {1: }l4 |
+ {1: }l5 |
+ {1: } |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ |
+ ]]}
+
+ end)
+
+ it('can add lots of signs', function()
+ screen:try_resize(40, 10)
+ command 'normal 10oa b c d e f g h'
+
+ for i = 1, 10 do
+ meths.buf_set_extmark(0, ns, i, 0, { end_col = 1, hl_group='Todo' })
+ meths.buf_set_extmark(0, ns, i, 2, { end_col = 3, hl_group='Todo' })
+ meths.buf_set_extmark(0, ns, i, 4, { end_col = 5, hl_group='Todo' })
+ meths.buf_set_extmark(0, ns, i, 6, { end_col = 7, hl_group='Todo' })
+ meths.buf_set_extmark(0, ns, i, 8, { end_col = 9, hl_group='Todo' })
+ meths.buf_set_extmark(0, ns, i, 10, { end_col = 11, hl_group='Todo' })
+ meths.buf_set_extmark(0, ns, i, 12, { end_col = 13, hl_group='Todo' })
+ meths.buf_set_extmark(0, ns, i, 14, { end_col = 15, hl_group='Todo' })
+ meths.buf_set_extmark(0, ns, i, -1, { sign_text='W' })
+ meths.buf_set_extmark(0, ns, i, -1, { sign_text='X' })
+ meths.buf_set_extmark(0, ns, i, -1, { sign_text='Y' })
+ meths.buf_set_extmark(0, ns, i, -1, { sign_text='Z' })
+ end
+
+ screen:expect{grid=[[
+ X Y Z W {3:a} {3:b} {3:c} {3:d} {3:e} {3:f} {3:g} {3:h} |
+ X Y Z W {3:a} {3:b} {3:c} {3:d} {3:e} {3:f} {3:g} {3:h} |
+ X Y Z W {3:a} {3:b} {3:c} {3:d} {3:e} {3:f} {3:g} {3:h} |
+ X Y Z W {3:a} {3:b} {3:c} {3:d} {3:e} {3:f} {3:g} {3:h} |
+ X Y Z W {3:a} {3:b} {3:c} {3:d} {3:e} {3:f} {3:g} {3:h} |
+ X Y Z W {3:a} {3:b} {3:c} {3:d} {3:e} {3:f} {3:g} {3:h} |
+ X Y Z W {3:a} {3:b} {3:c} {3:d} {3:e} {3:f} {3:g} {3:h} |
+ X Y Z W {3:a} {3:b} {3:c} {3:d} {3:e} {3:f} {3:g} {3:h} |
+ X Y Z W {3:a} {3:b} {3:c} {3:d} {3:e} {3:f} {3:g} {3:^h} |
+ |
+ ]]}
+ end)
+
+end)
+
+describe('decorations: virt_text', function()
+ local screen
+
+ before_each(function()
+ clear()
+ screen = Screen.new(50, 10)
+ screen:attach()
+ screen:set_default_attr_ids {
+ [1] = {foreground = Screen.colors.Brown};
+ [2] = {foreground = Screen.colors.Fuchsia};
+ [3] = {bold = true, foreground = Screen.colors.Blue1};
+ }
+ end)
+
+ it('avoids regression in #17638', function()
+ exec_lua[[
+ vim.wo.number = true
+ vim.wo.relativenumber = true
+ ]]
+
+ command 'normal 4ohello'
+ command 'normal aVIRTUAL'
+
+ local ns = meths.create_namespace('test')
+
+ meths.buf_set_extmark(0, ns, 2, 0, {
+ virt_text = {{"hello", "String"}},
+ virt_text_win_col = 20,
+ })
+
+ screen:expect{grid=[[
+ {1: 4 } |
+ {1: 3 }hello |
+ {1: 2 }hello {2:hello} |
+ {1: 1 }hello |
+ {1:5 }helloVIRTUA^L |
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ |
+ ]]}
+
+ -- Trigger a screen update
+ feed('k')
+
+ screen:expect{grid=[[
+ {1: 3 } |
+ {1: 2 }hello |
+ {1: 1 }hello {2:hello} |
+ {1:4 }hell^o |
+ {1: 1 }helloVIRTUAL |
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ |
+ ]]}
+ end)
+
+end)
diff --git a/test/functional/ui/diff_spec.lua b/test/functional/ui/diff_spec.lua
index bd2692d19a..5d056cd6c3 100644
--- a/test/functional/ui/diff_spec.lua
+++ b/test/functional/ui/diff_spec.lua
@@ -6,7 +6,7 @@ local clear = helpers.clear
local command = helpers.command
local insert = helpers.insert
local write_file = helpers.write_file
-local source = helpers.source
+local exec = helpers.exec
describe('Diff mode screen', function()
local fname = 'Xtest-functional-diff-screen-1'
@@ -57,40 +57,40 @@ describe('Diff mode screen', function()
feed(':set diffopt=filler<cr>')
screen:expect([[
- {1: }{2:------------------}{3:│}{1: }{4:0 }|
- {1: }^1 {3:│}{1: }1 |
- {1: }2 {3:│}{1: }2 |
- {1: }3 {3:│}{1: }3 |
- {1: }4 {3:│}{1: }4 |
- {1: }5 {3:│}{1: }5 |
- {1: }6 {3:│}{1: }6 |
- {1:+ }{5:+-- 4 lines: 7···}{3:│}{1:+ }{5:+-- 4 lines: 7··}|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
+ {1: }{2:------------------}│{1: }{4:0 }|
+ {1: }^1 │{1: }1 |
+ {1: }2 │{1: }2 |
+ {1: }3 │{1: }3 |
+ {1: }4 │{1: }4 |
+ {1: }5 │{1: }5 |
+ {1: }6 │{1: }6 |
+ {1:+ }{5:+-- 4 lines: 7···}│{1:+ }{5:+-- 4 lines: 7··}|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt=filler |
]])
feed(':set diffopt+=internal<cr>')
screen:expect([[
- {1: }{2:------------------}{3:│}{1: }{4:0 }|
- {1: }^1 {3:│}{1: }1 |
- {1: }2 {3:│}{1: }2 |
- {1: }3 {3:│}{1: }3 |
- {1: }4 {3:│}{1: }4 |
- {1: }5 {3:│}{1: }5 |
- {1: }6 {3:│}{1: }6 |
- {1:+ }{5:+-- 4 lines: 7···}{3:│}{1:+ }{5:+-- 4 lines: 7··}|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
+ {1: }{2:------------------}│{1: }{4:0 }|
+ {1: }^1 │{1: }1 |
+ {1: }2 │{1: }2 |
+ {1: }3 │{1: }3 |
+ {1: }4 │{1: }4 |
+ {1: }5 │{1: }5 |
+ {1: }6 │{1: }6 |
+ {1:+ }{5:+-- 4 lines: 7···}│{1:+ }{5:+-- 4 lines: 7··}|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt+=internal |
]])
@@ -103,40 +103,40 @@ describe('Diff mode screen', function()
feed(":set diffopt=filler<cr>")
screen:expect([[
- {1: }{4:^0 }{3:│}{1: }{2:-----------------}|
- {1: }1 {3:│}{1: }1 |
- {1: }2 {3:│}{1: }2 |
- {1: }3 {3:│}{1: }3 |
- {1: }4 {3:│}{1: }4 |
- {1: }5 {3:│}{1: }5 |
- {1: }6 {3:│}{1: }6 |
- {1:+ }{5:+-- 4 lines: 7···}{3:│}{1:+ }{5:+-- 4 lines: 7··}|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
+ {1: }{4:^0 }│{1: }{2:-----------------}|
+ {1: }1 │{1: }1 |
+ {1: }2 │{1: }2 |
+ {1: }3 │{1: }3 |
+ {1: }4 │{1: }4 |
+ {1: }5 │{1: }5 |
+ {1: }6 │{1: }6 |
+ {1:+ }{5:+-- 4 lines: 7···}│{1:+ }{5:+-- 4 lines: 7··}|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt=filler |
]])
feed(":set diffopt+=internal<cr>")
screen:expect([[
- {1: }{4:^0 }{3:│}{1: }{2:-----------------}|
- {1: }1 {3:│}{1: }1 |
- {1: }2 {3:│}{1: }2 |
- {1: }3 {3:│}{1: }3 |
- {1: }4 {3:│}{1: }4 |
- {1: }5 {3:│}{1: }5 |
- {1: }6 {3:│}{1: }6 |
- {1:+ }{5:+-- 4 lines: 7···}{3:│}{1:+ }{5:+-- 4 lines: 7··}|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
+ {1: }{4:^0 }│{1: }{2:-----------------}|
+ {1: }1 │{1: }1 |
+ {1: }2 │{1: }2 |
+ {1: }3 │{1: }3 |
+ {1: }4 │{1: }4 |
+ {1: }5 │{1: }5 |
+ {1: }6 │{1: }6 |
+ {1:+ }{5:+-- 4 lines: 7···}│{1:+ }{5:+-- 4 lines: 7··}|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt+=internal |
]])
@@ -149,53 +149,53 @@ describe('Diff mode screen', function()
feed(":set diffopt=filler<cr>")
screen:expect([[
- {1:+ }{5:^+-- 4 lines: 1···}{3:│}{1:+ }{5:+-- 4 lines: 1··}|
- {1: }5 {3:│}{1: }5 |
- {1: }6 {3:│}{1: }6 |
- {1: }7 {3:│}{1: }7 |
- {1: }8 {3:│}{1: }8 |
- {1: }9 {3:│}{1: }9 |
- {1: }10 {3:│}{1: }10 |
- {1: }{2:------------------}{3:│}{1: }{4:11 }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
+ {1:+ }{5:^+-- 4 lines: 1···}│{1:+ }{5:+-- 4 lines: 1··}|
+ {1: }5 │{1: }5 |
+ {1: }6 │{1: }6 |
+ {1: }7 │{1: }7 |
+ {1: }8 │{1: }8 |
+ {1: }9 │{1: }9 |
+ {1: }10 │{1: }10 |
+ {1: }{2:------------------}│{1: }{4:11 }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt=filler |
]])
feed(":set diffopt+=internal<cr>")
screen:expect([[
- {1:+ }{5:^+-- 4 lines: 1···}{3:│}{1:+ }{5:+-- 4 lines: 1··}|
- {1: }5 {3:│}{1: }5 |
- {1: }6 {3:│}{1: }6 |
- {1: }7 {3:│}{1: }7 |
- {1: }8 {3:│}{1: }8 |
- {1: }9 {3:│}{1: }9 |
- {1: }10 {3:│}{1: }10 |
- {1: }{2:------------------}{3:│}{1: }{4:11 }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
+ {1:+ }{5:^+-- 4 lines: 1···}│{1:+ }{5:+-- 4 lines: 1··}|
+ {1: }5 │{1: }5 |
+ {1: }6 │{1: }6 |
+ {1: }7 │{1: }7 |
+ {1: }8 │{1: }8 |
+ {1: }9 │{1: }9 |
+ {1: }10 │{1: }10 |
+ {1: }{2:------------------}│{1: }{4:11 }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt+=internal |
]])
screen:try_resize(40, 9)
screen:expect([[
- {1:+ }{5:^+-- 4 lines: 1···}{3:│}{1:+ }{5:+-- 4 lines: 1··}|
- {1: }5 {3:│}{1: }5 |
- {1: }6 {3:│}{1: }6 |
- {1: }7 {3:│}{1: }7 |
- {1: }8 {3:│}{1: }8 |
- {1: }9 {3:│}{1: }9 |
- {1: }10 {3:│}{1: }10 |
+ {1:+ }{5:^+-- 4 lines: 1···}│{1:+ }{5:+-- 4 lines: 1··}|
+ {1: }5 │{1: }5 |
+ {1: }6 │{1: }6 |
+ {1: }7 │{1: }7 |
+ {1: }8 │{1: }8 |
+ {1: }9 │{1: }9 |
+ {1: }10 │{1: }10 |
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
|
]])
@@ -208,53 +208,53 @@ describe('Diff mode screen', function()
feed(":set diffopt=filler<cr>")
screen:expect([[
- {1:+ }{5:^+-- 4 lines: 1···}{3:│}{1:+ }{5:+-- 4 lines: 1··}|
- {1: }5 {3:│}{1: }5 |
- {1: }6 {3:│}{1: }6 |
- {1: }7 {3:│}{1: }7 |
- {1: }8 {3:│}{1: }8 |
- {1: }9 {3:│}{1: }9 |
- {1: }10 {3:│}{1: }10 |
- {1: }{4:11 }{3:│}{1: }{2:-----------------}|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
+ {1:+ }{5:^+-- 4 lines: 1···}│{1:+ }{5:+-- 4 lines: 1··}|
+ {1: }5 │{1: }5 |
+ {1: }6 │{1: }6 |
+ {1: }7 │{1: }7 |
+ {1: }8 │{1: }8 |
+ {1: }9 │{1: }9 |
+ {1: }10 │{1: }10 |
+ {1: }{4:11 }│{1: }{2:-----------------}|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt=filler |
]])
feed(":set diffopt+=internal<cr>")
screen:expect([[
- {1:+ }{5:^+-- 4 lines: 1···}{3:│}{1:+ }{5:+-- 4 lines: 1··}|
- {1: }5 {3:│}{1: }5 |
- {1: }6 {3:│}{1: }6 |
- {1: }7 {3:│}{1: }7 |
- {1: }8 {3:│}{1: }8 |
- {1: }9 {3:│}{1: }9 |
- {1: }10 {3:│}{1: }10 |
- {1: }{4:11 }{3:│}{1: }{2:-----------------}|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
+ {1:+ }{5:^+-- 4 lines: 1···}│{1:+ }{5:+-- 4 lines: 1··}|
+ {1: }5 │{1: }5 |
+ {1: }6 │{1: }6 |
+ {1: }7 │{1: }7 |
+ {1: }8 │{1: }8 |
+ {1: }9 │{1: }9 |
+ {1: }10 │{1: }10 |
+ {1: }{4:11 }│{1: }{2:-----------------}|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt+=internal |
]])
screen:try_resize(40, 9)
screen:expect([[
- {1:+ }{5:^+-- 4 lines: 1···}{3:│}{1:+ }{5:+-- 4 lines: 1··}|
- {1: }5 {3:│}{1: }5 |
- {1: }6 {3:│}{1: }6 |
- {1: }7 {3:│}{1: }7 |
- {1: }8 {3:│}{1: }8 |
- {1: }9 {3:│}{1: }9 |
- {1: }10 {3:│}{1: }10 |
+ {1:+ }{5:^+-- 4 lines: 1···}│{1:+ }{5:+-- 4 lines: 1··}|
+ {1: }5 │{1: }5 |
+ {1: }6 │{1: }6 |
+ {1: }7 │{1: }7 |
+ {1: }8 │{1: }8 |
+ {1: }9 │{1: }9 |
+ {1: }10 │{1: }10 |
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
|
]])
@@ -267,40 +267,40 @@ describe('Diff mode screen', function()
feed(':set diffopt=filler<cr>')
screen:expect([[
- {1: }^1 {3:│}{1: }1 |
- {1: }2 {3:│}{1: }2 |
- {1: }3 {3:│}{1: }3 |
- {1: }4 {3:│}{1: }4 |
- {1: }{2:------------------}{3:│}{1: }{4:4 }|
- {1: }5 {3:│}{1: }5 |
- {1: }6 {3:│}{1: }6 |
- {1: }7 {3:│}{1: }7 |
- {1: }8 {3:│}{1: }8 |
- {1: }9 {3:│}{1: }9 |
- {1: }10 {3:│}{1: }10 |
- {1: }{4:11 }{3:│}{1: }{2:-----------------}|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
+ {1: }^1 │{1: }1 |
+ {1: }2 │{1: }2 |
+ {1: }3 │{1: }3 |
+ {1: }4 │{1: }4 |
+ {1: }{2:------------------}│{1: }{4:4 }|
+ {1: }5 │{1: }5 |
+ {1: }6 │{1: }6 |
+ {1: }7 │{1: }7 |
+ {1: }8 │{1: }8 |
+ {1: }9 │{1: }9 |
+ {1: }10 │{1: }10 |
+ {1: }{4:11 }│{1: }{2:-----------------}|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt=filler |
]])
feed(':set diffopt+=internal<cr>')
screen:expect([[
- {1: }^1 {3:│}{1: }1 |
- {1: }2 {3:│}{1: }2 |
- {1: }3 {3:│}{1: }3 |
- {1: }4 {3:│}{1: }4 |
- {1: }{2:------------------}{3:│}{1: }{4:4 }|
- {1: }5 {3:│}{1: }5 |
- {1: }6 {3:│}{1: }6 |
- {1: }7 {3:│}{1: }7 |
- {1: }8 {3:│}{1: }8 |
- {1: }9 {3:│}{1: }9 |
- {1: }10 {3:│}{1: }10 |
- {1: }{4:11 }{3:│}{1: }{2:-----------------}|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
+ {1: }^1 │{1: }1 |
+ {1: }2 │{1: }2 |
+ {1: }3 │{1: }3 |
+ {1: }4 │{1: }4 |
+ {1: }{2:------------------}│{1: }{4:4 }|
+ {1: }5 │{1: }5 |
+ {1: }6 │{1: }6 |
+ {1: }7 │{1: }7 |
+ {1: }8 │{1: }8 |
+ {1: }9 │{1: }9 |
+ {1: }10 │{1: }10 |
+ {1: }{4:11 }│{1: }{2:-----------------}|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt+=internal |
]])
@@ -313,40 +313,40 @@ describe('Diff mode screen', function()
feed(':set diffopt=filler<cr>')
screen:expect([[
- {1: }^1 {3:│}{1: }1 |
- {1: }2 {3:│}{1: }2 |
- {1: }3 {3:│}{1: }3 |
- {1: }4 {3:│}{1: }4 |
- {1: }{4:4 }{3:│}{1: }{2:-----------------}|
- {1: }5 {3:│}{1: }5 |
- {1: }6 {3:│}{1: }6 |
- {1: }7 {3:│}{1: }7 |
- {1: }8 {3:│}{1: }8 |
- {1: }9 {3:│}{1: }9 |
- {1: }10 {3:│}{1: }10 |
- {1: }{2:------------------}{3:│}{1: }{4:11 }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
+ {1: }^1 │{1: }1 |
+ {1: }2 │{1: }2 |
+ {1: }3 │{1: }3 |
+ {1: }4 │{1: }4 |
+ {1: }{4:4 }│{1: }{2:-----------------}|
+ {1: }5 │{1: }5 |
+ {1: }6 │{1: }6 |
+ {1: }7 │{1: }7 |
+ {1: }8 │{1: }8 |
+ {1: }9 │{1: }9 |
+ {1: }10 │{1: }10 |
+ {1: }{2:------------------}│{1: }{4:11 }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt=filler |
]])
feed(':set diffopt+=internal<cr>')
screen:expect([[
- {1: }^1 {3:│}{1: }1 |
- {1: }2 {3:│}{1: }2 |
- {1: }3 {3:│}{1: }3 |
- {1: }4 {3:│}{1: }4 |
- {1: }{4:4 }{3:│}{1: }{2:-----------------}|
- {1: }5 {3:│}{1: }5 |
- {1: }6 {3:│}{1: }6 |
- {1: }7 {3:│}{1: }7 |
- {1: }8 {3:│}{1: }8 |
- {1: }9 {3:│}{1: }9 |
- {1: }10 {3:│}{1: }10 |
- {1: }{2:------------------}{3:│}{1: }{4:11 }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
+ {1: }^1 │{1: }1 |
+ {1: }2 │{1: }2 |
+ {1: }3 │{1: }3 |
+ {1: }4 │{1: }4 |
+ {1: }{4:4 }│{1: }{2:-----------------}|
+ {1: }5 │{1: }5 |
+ {1: }6 │{1: }6 |
+ {1: }7 │{1: }7 |
+ {1: }8 │{1: }8 |
+ {1: }9 │{1: }9 |
+ {1: }10 │{1: }10 |
+ {1: }{2:------------------}│{1: }{4:11 }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt+=internal |
]])
@@ -413,40 +413,40 @@ int main(int argc, char **argv)
reread()
feed(':set diffopt=internal,filler<cr>')
screen:expect([[
- {1: }^#include <stdio.h>{3:│}{1: }#include <stdio.h|
- {1: } {3:│}{1: } |
- {1: }{8:// Frobs foo heart}{3:│}{1: }{8:int fib(int n)}{9: }|
- {1: }{4:int frobnitz(int f}{3:│}{1: }{2:-----------------}|
- {1: }{ {3:│}{1: }{ |
- {1: }{9: i}{8:nt i;}{9: }{3:│}{1: }{9: i}{8:f(n > 2)}{9: }|
- {1: }{4: for(i = 0; i <}{3:│}{1: }{2:-----------------}|
- {1: } { {3:│}{1: } { |
- {1: }{9: }{8:printf("Yo}{3:│}{1: }{9: }{8:return fi}|
- {1: }{4: printf("%d}{3:│}{1: }{2:-----------------}|
- {1: } } {3:│}{1: } } |
- {1: }{2:------------------}{3:│}{1: }{4: return 1; }|
- {1: }} {3:│}{1: }} |
- {1: } {3:│}{1: } |
+ {1: }^#include <stdio.h>│{1: }#include <stdio.h|
+ {1: } │{1: } |
+ {1: }{8:// Frobs foo heart}│{1: }{8:int fib(int n)}{9: }|
+ {1: }{4:int frobnitz(int f}│{1: }{2:-----------------}|
+ {1: }{ │{1: }{ |
+ {1: }{9: i}{8:nt i;}{9: }│{1: }{9: i}{8:f(n > 2)}{9: }|
+ {1: }{4: for(i = 0; i <}│{1: }{2:-----------------}|
+ {1: } { │{1: } { |
+ {1: }{9: }{8:printf("Yo}│{1: }{9: }{8:return fi}|
+ {1: }{4: printf("%d}│{1: }{2:-----------------}|
+ {1: } } │{1: } } |
+ {1: }{2:------------------}│{1: }{4: return 1; }|
+ {1: }} │{1: }} |
+ {1: } │{1: } |
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt=internal,filler |
]])
feed('G')
screen:expect([[
- {1: }{2:------------------}{3:│}{1: }{4:int frobnitz(int }|
- {1: }{ {3:│}{1: }{ |
- {1: }{9: i}{8:f(n > 1)}{9: }{3:│}{1: }{9: i}{8:nt i;}{9: }|
- {1: }{2:------------------}{3:│}{1: }{4: for(i = 0; i }|
- {1: } { {3:│}{1: } { |
- {1: }{9: }{8:return fac}{3:│}{1: }{9: }{8:printf("%}|
- {1: } } {3:│}{1: } } |
- {1: }{4: return 1; }{3:│}{1: }{2:-----------------}|
- {1: }} {3:│}{1: }} |
- {1: } {3:│}{1: } |
- {1: }int main(int argc,{3:│}{1: }int main(int argc|
- {1: }{ {3:│}{1: }{ |
- {1: }{9: frobnitz(f}{8:act}{9:(}{3:│}{1: }{9: frobnitz(f}{8:ib}{9:(}|
- {1: }^} {3:│}{1: }} |
+ {1: }{2:------------------}│{1: }{4:int frobnitz(int }|
+ {1: }{ │{1: }{ |
+ {1: }{9: i}{8:f(n > 1)}{9: }│{1: }{9: i}{8:nt i;}{9: }|
+ {1: }{2:------------------}│{1: }{4: for(i = 0; i }|
+ {1: } { │{1: } { |
+ {1: }{9: }{8:return fac}│{1: }{9: }{8:printf("%}|
+ {1: } } │{1: } } |
+ {1: }{4: return 1; }│{1: }{2:-----------------}|
+ {1: }} │{1: }} |
+ {1: } │{1: } |
+ {1: }int main(int argc,│{1: }int main(int argc|
+ {1: }{ │{1: }{ |
+ {1: }{9: frobnitz(f}{8:act}{9:(}│{1: }{9: frobnitz(f}{8:ib}{9:(}|
+ {1: }^} │{1: }} |
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt=internal,filler |
]])
@@ -456,40 +456,40 @@ int main(int argc, char **argv)
reread()
feed(':set diffopt=internal,filler,algorithm:patience<cr>')
screen:expect([[
- {1: }^#include <stdio.h>{3:│}{1: }#include <stdio.h|
- {1: } {3:│}{1: } |
- {1: }{2:------------------}{3:│}{1: }{4:int fib(int n) }|
- {1: }{2:------------------}{3:│}{1: }{4:{ }|
- {1: }{2:------------------}{3:│}{1: }{4: if(n > 2) }|
- {1: }{2:------------------}{3:│}{1: }{4: { }|
- {1: }{2:------------------}{3:│}{1: }{4: return fi}|
- {1: }{2:------------------}{3:│}{1: }{4: } }|
- {1: }{2:------------------}{3:│}{1: }{4: return 1; }|
- {1: }{2:------------------}{3:│}{1: }{4:} }|
- {1: }{2:------------------}{3:│}{1: }{4: }|
- {1: }// Frobs foo heart{3:│}{1: }// Frobs foo hear|
- {1: }int frobnitz(int f{3:│}{1: }int frobnitz(int |
- {1: }{ {3:│}{1: }{ |
+ {1: }^#include <stdio.h>│{1: }#include <stdio.h|
+ {1: } │{1: } |
+ {1: }{2:------------------}│{1: }{4:int fib(int n) }|
+ {1: }{2:------------------}│{1: }{4:{ }|
+ {1: }{2:------------------}│{1: }{4: if(n > 2) }|
+ {1: }{2:------------------}│{1: }{4: { }|
+ {1: }{2:------------------}│{1: }{4: return fi}|
+ {1: }{2:------------------}│{1: }{4: } }|
+ {1: }{2:------------------}│{1: }{4: return 1; }|
+ {1: }{2:------------------}│{1: }{4:} }|
+ {1: }{2:------------------}│{1: }{4: }|
+ {1: }// Frobs foo heart│{1: }// Frobs foo hear|
+ {1: }int frobnitz(int f│{1: }int frobnitz(int |
+ {1: }{ │{1: }{ |
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
|
]])
feed('G')
screen:expect([[
- {1: } {3:│}{1: } |
- {1: }{4:int fact(int n) }{3:│}{1: }{2:-----------------}|
- {1: }{4:{ }{3:│}{1: }{2:-----------------}|
- {1: }{4: if(n > 1) }{3:│}{1: }{2:-----------------}|
- {1: }{4: { }{3:│}{1: }{2:-----------------}|
- {1: }{4: return fac}{3:│}{1: }{2:-----------------}|
- {1: }{4: } }{3:│}{1: }{2:-----------------}|
- {1: }{4: return 1; }{3:│}{1: }{2:-----------------}|
- {1: }{4:} }{3:│}{1: }{2:-----------------}|
- {1: }{4: }{3:│}{1: }{2:-----------------}|
- {1: }int main(int argc,{3:│}{1: }int main(int argc|
- {1: }{ {3:│}{1: }{ |
- {1: }{9: frobnitz(f}{8:act}{9:(}{3:│}{1: }{9: frobnitz(f}{8:ib}{9:(}|
- {1: }^} {3:│}{1: }} |
+ {1: } │{1: } |
+ {1: }{4:int fact(int n) }│{1: }{2:-----------------}|
+ {1: }{4:{ }│{1: }{2:-----------------}|
+ {1: }{4: if(n > 1) }│{1: }{2:-----------------}|
+ {1: }{4: { }│{1: }{2:-----------------}|
+ {1: }{4: return fac}│{1: }{2:-----------------}|
+ {1: }{4: } }│{1: }{2:-----------------}|
+ {1: }{4: return 1; }│{1: }{2:-----------------}|
+ {1: }{4:} }│{1: }{2:-----------------}|
+ {1: }{4: }│{1: }{2:-----------------}|
+ {1: }int main(int argc,│{1: }int main(int argc|
+ {1: }{ │{1: }{ |
+ {1: }{9: frobnitz(f}{8:act}{9:(}│{1: }{9: frobnitz(f}{8:ib}{9:(}|
+ {1: }^} │{1: }} |
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
|
]])
@@ -499,40 +499,40 @@ int main(int argc, char **argv)
reread()
feed(':set diffopt=internal,filler,algorithm:histogram<cr>')
screen:expect([[
- {1: }^#include <stdio.h>{3:│}{1: }#include <stdio.h|
- {1: } {3:│}{1: } |
- {1: }{2:------------------}{3:│}{1: }{4:int fib(int n) }|
- {1: }{2:------------------}{3:│}{1: }{4:{ }|
- {1: }{2:------------------}{3:│}{1: }{4: if(n > 2) }|
- {1: }{2:------------------}{3:│}{1: }{4: { }|
- {1: }{2:------------------}{3:│}{1: }{4: return fi}|
- {1: }{2:------------------}{3:│}{1: }{4: } }|
- {1: }{2:------------------}{3:│}{1: }{4: return 1; }|
- {1: }{2:------------------}{3:│}{1: }{4:} }|
- {1: }{2:------------------}{3:│}{1: }{4: }|
- {1: }// Frobs foo heart{3:│}{1: }// Frobs foo hear|
- {1: }int frobnitz(int f{3:│}{1: }int frobnitz(int |
- {1: }{ {3:│}{1: }{ |
+ {1: }^#include <stdio.h>│{1: }#include <stdio.h|
+ {1: } │{1: } |
+ {1: }{2:------------------}│{1: }{4:int fib(int n) }|
+ {1: }{2:------------------}│{1: }{4:{ }|
+ {1: }{2:------------------}│{1: }{4: if(n > 2) }|
+ {1: }{2:------------------}│{1: }{4: { }|
+ {1: }{2:------------------}│{1: }{4: return fi}|
+ {1: }{2:------------------}│{1: }{4: } }|
+ {1: }{2:------------------}│{1: }{4: return 1; }|
+ {1: }{2:------------------}│{1: }{4:} }|
+ {1: }{2:------------------}│{1: }{4: }|
+ {1: }// Frobs foo heart│{1: }// Frobs foo hear|
+ {1: }int frobnitz(int f│{1: }int frobnitz(int |
+ {1: }{ │{1: }{ |
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
|
]])
feed('G')
screen:expect([[
- {1: } {3:│}{1: } |
- {1: }{4:int fact(int n) }{3:│}{1: }{2:-----------------}|
- {1: }{4:{ }{3:│}{1: }{2:-----------------}|
- {1: }{4: if(n > 1) }{3:│}{1: }{2:-----------------}|
- {1: }{4: { }{3:│}{1: }{2:-----------------}|
- {1: }{4: return fac}{3:│}{1: }{2:-----------------}|
- {1: }{4: } }{3:│}{1: }{2:-----------------}|
- {1: }{4: return 1; }{3:│}{1: }{2:-----------------}|
- {1: }{4:} }{3:│}{1: }{2:-----------------}|
- {1: }{4: }{3:│}{1: }{2:-----------------}|
- {1: }int main(int argc,{3:│}{1: }int main(int argc|
- {1: }{ {3:│}{1: }{ |
- {1: }{9: frobnitz(f}{8:act}{9:(}{3:│}{1: }{9: frobnitz(f}{8:ib}{9:(}|
- {1: }^} {3:│}{1: }} |
+ {1: } │{1: } |
+ {1: }{4:int fact(int n) }│{1: }{2:-----------------}|
+ {1: }{4:{ }│{1: }{2:-----------------}|
+ {1: }{4: if(n > 1) }│{1: }{2:-----------------}|
+ {1: }{4: { }│{1: }{2:-----------------}|
+ {1: }{4: return fac}│{1: }{2:-----------------}|
+ {1: }{4: } }│{1: }{2:-----------------}|
+ {1: }{4: return 1; }│{1: }{2:-----------------}|
+ {1: }{4:} }│{1: }{2:-----------------}|
+ {1: }{4: }│{1: }{2:-----------------}|
+ {1: }int main(int argc,│{1: }int main(int argc|
+ {1: }{ │{1: }{ |
+ {1: }{9: frobnitz(f}{8:act}{9:(}│{1: }{9: frobnitz(f}{8:ib}{9:(}|
+ {1: }^} │{1: }} |
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
|
]])
@@ -566,20 +566,20 @@ int main(int argc, char **argv)
reread()
feed(":set diffopt=internal,filler<cr>")
screen:expect([[
- {1: }^def finalize(value{3:│}{1: }def finalize(valu|
- {1: } {3:│}{1: } |
- {1: } values.each do |{3:│}{1: } values.each do |
- {1: }{2:------------------}{3:│}{1: }{4: v.prepare }|
- {1: }{2:------------------}{3:│}{1: }{4: end }|
- {1: }{2:------------------}{3:│}{1: }{4: }|
- {1: }{2:------------------}{3:│}{1: }{4: values.each do }|
- {1: } v.finalize {3:│}{1: } v.finalize |
- {1: } end {3:│}{1: } end |
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
+ {1: }^def finalize(value│{1: }def finalize(valu|
+ {1: } │{1: } |
+ {1: } values.each do |│{1: } values.each do |
+ {1: }{2:------------------}│{1: }{4: v.prepare }|
+ {1: }{2:------------------}│{1: }{4: end }|
+ {1: }{2:------------------}│{1: }{4: }|
+ {1: }{2:------------------}│{1: }{4: values.each do }|
+ {1: } v.finalize │{1: } v.finalize |
+ {1: } end │{1: } end |
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt=internal,filler |
]])
@@ -589,20 +589,20 @@ int main(int argc, char **argv)
reread()
feed(':set diffopt=internal,filler,indent-heuristic<cr>')
screen:expect([[
- {1: }^def finalize(value{3:│}{1: }def finalize(valu|
- {1: } {3:│}{1: } |
- {1: }{2:------------------}{3:│}{1: }{4: values.each do }|
- {1: }{2:------------------}{3:│}{1: }{4: v.prepare }|
- {1: }{2:------------------}{3:│}{1: }{4: end }|
- {1: }{2:------------------}{3:│}{1: }{4: }|
- {1: } values.each do |{3:│}{1: } values.each do |
- {1: } v.finalize {3:│}{1: } v.finalize |
- {1: } end {3:│}{1: } end |
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
+ {1: }^def finalize(value│{1: }def finalize(valu|
+ {1: } │{1: } |
+ {1: }{2:------------------}│{1: }{4: values.each do }|
+ {1: }{2:------------------}│{1: }{4: v.prepare }|
+ {1: }{2:------------------}│{1: }{4: end }|
+ {1: }{2:------------------}│{1: }{4: }|
+ {1: } values.each do |│{1: } values.each do |
+ {1: } v.finalize │{1: } v.finalize |
+ {1: } end │{1: } end |
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
|
]])
@@ -613,20 +613,20 @@ int main(int argc, char **argv)
feed(':set diffopt=internal,filler,indent-heuristic,algorithm:patience<cr>')
feed(':<cr>')
screen:expect([[
- {1: }^def finalize(value{3:│}{1: }def finalize(valu|
- {1: } {3:│}{1: } |
- {1: }{2:------------------}{3:│}{1: }{4: values.each do }|
- {1: }{2:------------------}{3:│}{1: }{4: v.prepare }|
- {1: }{2:------------------}{3:│}{1: }{4: end }|
- {1: }{2:------------------}{3:│}{1: }{4: }|
- {1: } values.each do |{3:│}{1: } values.each do |
- {1: } v.finalize {3:│}{1: } v.finalize |
- {1: } end {3:│}{1: } end |
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
+ {1: }^def finalize(value│{1: }def finalize(valu|
+ {1: } │{1: } |
+ {1: }{2:------------------}│{1: }{4: values.each do }|
+ {1: }{2:------------------}│{1: }{4: v.prepare }|
+ {1: }{2:------------------}│{1: }{4: end }|
+ {1: }{2:------------------}│{1: }{4: }|
+ {1: } values.each do |│{1: } values.each do |
+ {1: } v.finalize │{1: } v.finalize |
+ {1: } end │{1: } end |
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
: |
]])
@@ -640,40 +640,40 @@ int main(int argc, char **argv)
feed(':set diffopt=filler<cr>')
screen:expect([[
- {1:+ }{5:^+-- 10 lines: 1···}{3:│}{1:+ }{5:+-- 10 lines: 1··}|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
+ {1:+ }{5:^+-- 10 lines: 1···}│{1:+ }{5:+-- 10 lines: 1··}|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt=filler |
]])
feed(':set diffopt+=internal<cr>')
screen:expect([[
- {1:+ }{5:^+-- 10 lines: 1···}{3:│}{1:+ }{5:+-- 10 lines: 1··}|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
+ {1:+ }{5:^+-- 10 lines: 1···}│{1:+ }{5:+-- 10 lines: 1··}|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt+=internal |
]])
@@ -686,40 +686,40 @@ int main(int argc, char **argv)
feed(':set diffopt=filler<cr>')
screen:expect([[
- {1:- }^ {3:│}{1:- } |
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
+ {1:- }^ │{1:- } |
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt=filler |
]])
feed(':set diffopt+=internal<cr>')
screen:expect([[
- {1:- }^ {3:│}{1:- } |
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
+ {1:- }^ │{1:- } |
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt+=internal |
]])
@@ -732,40 +732,40 @@ int main(int argc, char **argv)
feed(':set diffopt=filler,icase<cr>')
screen:expect([[
- {1: }^a {3:│}{1: }A |
- {1: }b {3:│}{1: }b |
- {1: }{9:cd }{3:│}{1: }{9:cD}{8:e}{9: }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
+ {1: }^a │{1: }A |
+ {1: }b │{1: }b |
+ {1: }{9:cd }│{1: }{9:cD}{8:e}{9: }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt=filler,icase |
]])
feed(':set diffopt+=internal<cr>')
screen:expect([[
- {1: }^a {3:│}{1: }A |
- {1: }b {3:│}{1: }b |
- {1: }{9:cd }{3:│}{1: }{9:cD}{8:e}{9: }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
+ {1: }^a │{1: }A |
+ {1: }b │{1: }b |
+ {1: }{9:cd }│{1: }{9:cD}{8:e}{9: }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt+=internal |
]])
@@ -784,20 +784,20 @@ int main(int argc, char **argv)
reread()
feed(':set diffopt=filler,iwhite<cr>')
screen:expect([[
- {1: }^int main() {3:│}{1: }int main() |
- {1: }{ {3:│}{1: }{ |
- {1: }{2:------------------}{3:│}{1: }{4: if (0) }|
- {1: }{2:------------------}{3:│}{1: }{4: { }|
- {1: } printf("Hello, {3:│}{1: } printf("Hel|
- {1: } return 0; {3:│}{1: } return 0; |
- {1: }{2:------------------}{3:│}{1: }{4: } }|
- {1: }} {3:│}{1: }} |
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
+ {1: }^int main() │{1: }int main() |
+ {1: }{ │{1: }{ |
+ {1: }{2:------------------}│{1: }{4: if (0) }|
+ {1: }{2:------------------}│{1: }{4: { }|
+ {1: } printf("Hello, │{1: } printf("Hel|
+ {1: } return 0; │{1: } return 0; |
+ {1: }{2:------------------}│{1: }{4: } }|
+ {1: }} │{1: }} |
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt=filler,iwhite |
]])
@@ -807,20 +807,20 @@ int main(int argc, char **argv)
reread()
feed(':set diffopt=filler,iwhite,internal<cr>')
screen:expect([[
- {1: }^int main() {3:│}{1: }int main() |
- {1: }{ {3:│}{1: }{ |
- {1: }{2:------------------}{3:│}{1: }{4: if (0) }|
- {1: }{2:------------------}{3:│}{1: }{4: { }|
- {1: } printf("Hello, {3:│}{1: } printf("Hel|
- {1: } return 0; {3:│}{1: } return 0; |
- {1: }{2:------------------}{3:│}{1: }{4: } }|
- {1: }} {3:│}{1: }} |
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
+ {1: }^int main() │{1: }int main() |
+ {1: }{ │{1: }{ |
+ {1: }{2:------------------}│{1: }{4: if (0) }|
+ {1: }{2:------------------}│{1: }{4: { }|
+ {1: } printf("Hello, │{1: } printf("Hel|
+ {1: } return 0; │{1: } return 0; |
+ {1: }{2:------------------}│{1: }{4: } }|
+ {1: }} │{1: }} |
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt=filler,iwhite,internal |
]])
@@ -838,20 +838,20 @@ int main(int argc, char **argv)
reread()
feed(':set diffopt=internal,filler,iblank<cr>')
screen:expect([[
- {1: }^a {3:│}{1: }a |
- {1: }{4: }{3:│}{1: }{2:-----------------}|
- {1: }{4: }{3:│}{1: }{2:-----------------}|
- {1: }cd {3:│}{1: }cd |
- {1: }ef {3:│}{1: } |
- {1: }{8:xxx}{9: }{3:│}{1: }ef |
- {6:~ }{3:│}{1: }{8:yyy}{9: }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
+ {1: }^a │{1: }a |
+ {1: }{4: }│{1: }{2:-----------------}|
+ {1: }{4: }│{1: }{2:-----------------}|
+ {1: }cd │{1: }cd |
+ {1: }ef │{1: } |
+ {1: }{8:xxx}{9: }│{1: }ef |
+ {6:~ }│{1: }{8:yyy}{9: }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt=internal,filler,iblank |
]])
@@ -862,20 +862,20 @@ int main(int argc, char **argv)
feed(':set diffopt=internal,filler,iblank,iwhite<cr>')
feed(':<cr>')
screen:expect([[
- {1: }^a {3:│}{1: }a |
- {1: } {3:│}{1: }cd |
- {1: } {3:│}{1: } |
- {1: }cd {3:│}{1: }ef |
- {1: }ef {3:│}{1: }{8:yyy}{9: }|
- {1: }{8:xxx}{9: }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
+ {1: }^a │{1: }a |
+ {1: } │{1: }cd |
+ {1: } │{1: } |
+ {1: }cd │{1: }ef |
+ {1: }ef │{1: }{8:yyy}{9: }|
+ {1: }{8:xxx}{9: }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
: |
]])
@@ -886,20 +886,20 @@ int main(int argc, char **argv)
feed(':set diffopt=internal,filler,iblank,iwhiteall<cr>')
feed(':<cr>')
screen:expect([[
- {1: }^a {3:│}{1: }a |
- {1: } {3:│}{1: }cd |
- {1: } {3:│}{1: } |
- {1: }cd {3:│}{1: }ef |
- {1: }ef {3:│}{1: }{8:yyy}{9: }|
- {1: }{8:xxx}{9: }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
+ {1: }^a │{1: }a |
+ {1: } │{1: }cd |
+ {1: } │{1: } |
+ {1: }cd │{1: }ef |
+ {1: }ef │{1: }{8:yyy}{9: }|
+ {1: }{8:xxx}{9: }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
: |
]])
@@ -910,20 +910,20 @@ int main(int argc, char **argv)
feed(':set diffopt=internal,filler,iblank,iwhiteeol<cr>')
feed(':<cr>')
screen:expect([[
- {1: }^a {3:│}{1: }a |
- {1: } {3:│}{1: }cd |
- {1: } {3:│}{1: } |
- {1: }cd {3:│}{1: }ef |
- {1: }ef {3:│}{1: }{8:yyy}{9: }|
- {1: }{8:xxx}{9: }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
+ {1: }^a │{1: }a |
+ {1: } │{1: }cd |
+ {1: } │{1: } |
+ {1: }cd │{1: }ef |
+ {1: }ef │{1: }{8:yyy}{9: }|
+ {1: }{8:xxx}{9: }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
: |
]])
@@ -942,20 +942,20 @@ int main(int argc, char **argv)
feed(':set diffopt=internal,filler,iwhiteeol<cr>')
feed(':<cr>')
screen:expect([[
- {1: }^a {3:│}{1: }a |
- {1: }x {3:│}{1: }x |
- {1: }{9:cd }{3:│}{1: }{9:c}{8: }{9:d }|
- {1: }{9:ef }{3:│}{1: }{8: }{9:ef }|
- {1: }{9:xx }{8: }{9:xx }{3:│}{1: }{9:xx xx }|
- {1: }foo {3:│}{1: }foo |
- {1: }{2:------------------}{3:│}{1: }{4: }|
- {1: }bar {3:│}{1: }bar |
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
+ {1: }^a │{1: }a |
+ {1: }x │{1: }x |
+ {1: }{9:cd }│{1: }{9:c}{8: }{9:d }|
+ {1: }{9:ef }│{1: }{8: }{9:ef }|
+ {1: }{9:xx }{8: }{9:xx }│{1: }{9:xx xx }|
+ {1: }foo │{1: }foo |
+ {1: }{2:------------------}│{1: }{4: }|
+ {1: }bar │{1: }bar |
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
: |
]])
@@ -966,20 +966,20 @@ int main(int argc, char **argv)
feed(':set diffopt=internal,filler,iwhiteall<cr>')
feed(':<cr>')
screen:expect([[
- {1: }^a {3:│}{1: }a |
- {1: }x {3:│}{1: }x |
- {1: }cd {3:│}{1: }c d |
- {1: }ef {3:│}{1: } ef |
- {1: }xx xx {3:│}{1: }xx xx |
- {1: }foo {3:│}{1: }foo |
- {1: }{2:------------------}{3:│}{1: }{4: }|
- {1: }bar {3:│}{1: }bar |
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
- {6:~ }{3:│}{6:~ }|
+ {1: }^a │{1: }a |
+ {1: }x │{1: }x |
+ {1: }cd │{1: }c d |
+ {1: }ef │{1: } ef |
+ {1: }xx xx │{1: }xx xx |
+ {1: }foo │{1: }foo |
+ {1: }{2:------------------}│{1: }{4: }|
+ {1: }bar │{1: }bar |
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
: |
]])
@@ -1029,14 +1029,14 @@ it('win_update redraws lines properly', function()
command("windo diffthis")
command("windo 1")
screen:expect{grid=[[
- {13: }{16:-----------------------}{14:│}{13: }{15:^1 }|
- {13: }{16:-----------------------}{14:│}{13: }{15: }|
- {13: }{16:-----------------------}{14:│}{13: }{15: }|
- {13: }2 {14:│}{13: }2 |
- {13: }{17:2}{18:a }{14:│}{13: }{17:1}{18:a }|
- {13: }{15:2b }{14:│}{13: }{16:----------------------}|
- {13: } {14:│}{13: } |
- {1:~ }{14:│}{1:~ }|
+ {13: }{16:-----------------------}│{13: }{15:^1 }|
+ {13: }{16:-----------------------}│{13: }{15: }|
+ {13: }{16:-----------------------}│{13: }{15: }|
+ {13: }2 │{13: }2 |
+ {13: }{17:2}{18:a }│{13: }{17:1}{18:a }|
+ {13: }{15:2b }│{13: }{16:----------------------}|
+ {13: } │{13: } |
+ {1:~ }│{1:~ }|
{14:left [+] }{12:[No Name] [+] }|
|
]]}
@@ -1046,14 +1046,14 @@ it('win_update redraws lines properly', function()
feed('<C-y>')
feed('<C-y>')
screen:expect{grid=[[
- {13: }{16:-----------------------}{14:│}{13: }{15:1 }|
- {13: }{16:-----------------------}{14:│}{13: }{15: }|
- {13: }{16:-----------------------}{14:│}{13: }{15:^ }|
- {13: }2 {14:│}{13: }2 |
- {13: }{17:2}{18:a }{14:│}{13: }{17:1}{18:a }|
- {13: }{15:2b }{14:│}{13: }{16:----------------------}|
- {13: } {14:│}{13: } |
- {1:~ }{14:│}{1:~ }|
+ {13: }{16:-----------------------}│{13: }{15:1 }|
+ {13: }{16:-----------------------}│{13: }{15: }|
+ {13: }{16:-----------------------}│{13: }{15:^ }|
+ {13: }2 │{13: }2 |
+ {13: }{17:2}{18:a }│{13: }{17:1}{18:a }|
+ {13: }{15:2b }│{13: }{16:----------------------}|
+ {13: } │{13: } |
+ {1:~ }│{1:~ }|
{14:left [+] }{12:[No Name] [+] }|
|
]]}
@@ -1075,10 +1075,8 @@ it('diff updates line numbers below filler lines', function()
[9] = {background = Screen.colors.LightMagenta},
[10] = {bold = true, foreground = Screen.colors.Brown},
[11] = {foreground = Screen.colors.Brown},
- [12] = {foreground = Screen.colors.Brown, bold = true, background = Screen.colors.Red};
- [13] = {background = Screen.colors.Gray90};
})
- source([[
+ exec([[
call setline(1, ['a', 'a', 'a', 'y', 'b', 'b', 'b', 'b', 'b'])
vnew
call setline(1, ['a', 'a', 'a', 'x', 'x', 'x', 'b', 'b', 'b', 'b', 'b'])
@@ -1086,73 +1084,55 @@ it('diff updates line numbers below filler lines', function()
setlocal number rnu cursorline cursorlineopt=number foldcolumn=0
]])
screen:expect([[
- {1: }a {3:│}{10:1 }^a |
- {1: }a {3:│}{11: 1 }a |
- {1: }a {3:│}{11: 2 }a |
- {1: }{8:x}{9: }{3:│}{11: 3 }{8:y}{9: }|
- {1: }{4:x }{3:│}{11: }{2:----------------}|
- {1: }{4:x }{3:│}{11: }{2:----------------}|
- {1: }b {3:│}{11: 4 }b |
- {1: }b {3:│}{11: 5 }b |
- {1: }b {3:│}{11: 6 }b |
- {1: }b {3:│}{11: 7 }b |
- {1: }b {3:│}{11: 8 }b |
- {6:~ }{3:│}{6:~ }|
+ {1: }a │{10:1 }^a |
+ {1: }a │{11: 1 }a |
+ {1: }a │{11: 2 }a |
+ {1: }{8:x}{9: }│{11: 3 }{8:y}{9: }|
+ {1: }{4:x }│{11: }{2:----------------}|
+ {1: }{4:x }│{11: }{2:----------------}|
+ {1: }b │{11: 4 }b |
+ {1: }b │{11: 5 }b |
+ {1: }b │{11: 6 }b |
+ {1: }b │{11: 7 }b |
+ {1: }b │{11: 8 }b |
+ {6:~ }│{6:~ }|
{3:[No Name] [+] }{7:[No Name] [+] }|
|
]])
feed('j')
screen:expect([[
- {1: }a {3:│}{11: 1 }a |
- {1: }a {3:│}{10:2 }^a |
- {1: }a {3:│}{11: 1 }a |
- {1: }{8:x}{9: }{3:│}{11: 2 }{8:y}{9: }|
- {1: }{4:x }{3:│}{11: }{2:----------------}|
- {1: }{4:x }{3:│}{11: }{2:----------------}|
- {1: }b {3:│}{11: 3 }b |
- {1: }b {3:│}{11: 4 }b |
- {1: }b {3:│}{11: 5 }b |
- {1: }b {3:│}{11: 6 }b |
- {1: }b {3:│}{11: 7 }b |
- {6:~ }{3:│}{6:~ }|
+ {1: }a │{11: 1 }a |
+ {1: }a │{10:2 }^a |
+ {1: }a │{11: 1 }a |
+ {1: }{8:x}{9: }│{11: 2 }{8:y}{9: }|
+ {1: }{4:x }│{11: }{2:----------------}|
+ {1: }{4:x }│{11: }{2:----------------}|
+ {1: }b │{11: 3 }b |
+ {1: }b │{11: 4 }b |
+ {1: }b │{11: 5 }b |
+ {1: }b │{11: 6 }b |
+ {1: }b │{11: 7 }b |
+ {6:~ }│{6:~ }|
{3:[No Name] [+] }{7:[No Name] [+] }|
|
]])
feed('j')
screen:expect([[
- {1: }a {3:│}{11: 2 }a |
- {1: }a {3:│}{11: 1 }a |
- {1: }a {3:│}{10:3 }^a |
- {1: }{8:x}{9: }{3:│}{11: 1 }{8:y}{9: }|
- {1: }{4:x }{3:│}{11: }{2:----------------}|
- {1: }{4:x }{3:│}{11: }{2:----------------}|
- {1: }b {3:│}{11: 2 }b |
- {1: }b {3:│}{11: 3 }b |
- {1: }b {3:│}{11: 4 }b |
- {1: }b {3:│}{11: 5 }b |
- {1: }b {3:│}{11: 6 }b |
- {6:~ }{3:│}{6:~ }|
+ {1: }a │{11: 2 }a |
+ {1: }a │{11: 1 }a |
+ {1: }a │{10:3 }^a |
+ {1: }{8:x}{9: }│{11: 1 }{8:y}{9: }|
+ {1: }{4:x }│{11: }{2:----------------}|
+ {1: }{4:x }│{11: }{2:----------------}|
+ {1: }b │{11: 2 }b |
+ {1: }b │{11: 3 }b |
+ {1: }b │{11: 4 }b |
+ {1: }b │{11: 5 }b |
+ {1: }b │{11: 6 }b |
+ {6:~ }│{6:~ }|
{3:[No Name] [+] }{7:[No Name] [+] }|
|
]])
- command("set signcolumn number tgc cursorline cursorlineopt=number,line")
- command("hi CursorLineNr guibg=red")
- screen:expect{grid=[[
- {1: }a {3:│}{11: 2 }a |
- {1: }a {3:│}{11: 1 }a |
- {1: }a {3:│}{12:3 }{13:^a }|
- {1: }{8:x}{9: }{3:│}{11: 1 }{8:y}{9: }|
- {1: }{4:x }{3:│}{11: }{2:----------------}|
- {1: }{4:x }{3:│}{11: }{2:----------------}|
- {1: }b {3:│}{11: 2 }b |
- {1: }b {3:│}{11: 3 }b |
- {1: }b {3:│}{11: 4 }b |
- {1: }b {3:│}{11: 5 }b |
- {1: }b {3:│}{11: 6 }b |
- {6:~ }{3:│}{6:~ }|
- {3:[No Name] [+] }{7:[No Name] [+] }|
- signcolumn=auto |
- ]]}
end)
it('Align the filler lines when changing text in diff mode', function()
@@ -1169,7 +1149,7 @@ it('Align the filler lines when changing text in diff mode', function()
[7] = {foreground = Screen.colors.Blue1, bold = true};
[8] = {reverse = true, bold = true};
})
- source([[
+ exec([[
call setline(1, range(1, 15))
vnew
call setline(1, range(9, 15))
@@ -1178,71 +1158,198 @@ it('Align the filler lines when changing text in diff mode', function()
exe "normal Gl5\<C-E>"
]])
screen:expect{grid=[[
- {1: }{2:------------------}{3:│}{1: }{4:6 }|
- {1: }{2:------------------}{3:│}{1: }{4:7 }|
- {1: }{2:------------------}{3:│}{1: }{4:8 }|
- {1: }9 {3:│}{1: }9 |
- {1: }10 {3:│}{1: }10 |
- {1: }11 {3:│}{1: }11 |
- {1: }12 {3:│}{1: }12 |
- {1: }13 {3:│}{1: }13 |
- {1: }14 {3:│}{1: }14 |
- {1:- }1^5 {3:│}{1:- }15 |
- {7:~ }{3:│}{7:~ }|
- {7:~ }{3:│}{7:~ }|
- {7:~ }{3:│}{7:~ }|
- {7:~ }{3:│}{7:~ }|
- {7:~ }{3:│}{7:~ }|
- {7:~ }{3:│}{7:~ }|
- {7:~ }{3:│}{7:~ }|
- {7:~ }{3:│}{7:~ }|
+ {1: }{2:------------------}│{1: }{4:6 }|
+ {1: }{2:------------------}│{1: }{4:7 }|
+ {1: }{2:------------------}│{1: }{4:8 }|
+ {1: }9 │{1: }9 |
+ {1: }10 │{1: }10 |
+ {1: }11 │{1: }11 |
+ {1: }12 │{1: }12 |
+ {1: }13 │{1: }13 |
+ {1: }14 │{1: }14 |
+ {1:- }1^5 │{1:- }15 |
+ {7:~ }│{7:~ }|
+ {7:~ }│{7:~ }|
+ {7:~ }│{7:~ }|
+ {7:~ }│{7:~ }|
+ {7:~ }│{7:~ }|
+ {7:~ }│{7:~ }|
+ {7:~ }│{7:~ }|
+ {7:~ }│{7:~ }|
{8:[No Name] [+] }{3:[No Name] [+] }|
|
]]}
feed('ax<Esc>')
screen:expect{grid=[[
- {1: }{2:------------------}{3:│}{1: }{4:6 }|
- {1: }{2:------------------}{3:│}{1: }{4:7 }|
- {1: }{2:------------------}{3:│}{1: }{4:8 }|
- {1: }9 {3:│}{1: }9 |
- {1: }10 {3:│}{1: }10 |
- {1: }11 {3:│}{1: }11 |
- {1: }12 {3:│}{1: }12 |
- {1: }13 {3:│}{1: }13 |
- {1: }14 {3:│}{1: }14 |
- {1: }{5:15}{6:^x}{5: }{3:│}{1: }{5:15 }|
- {7:~ }{3:│}{7:~ }|
- {7:~ }{3:│}{7:~ }|
- {7:~ }{3:│}{7:~ }|
- {7:~ }{3:│}{7:~ }|
- {7:~ }{3:│}{7:~ }|
- {7:~ }{3:│}{7:~ }|
- {7:~ }{3:│}{7:~ }|
- {7:~ }{3:│}{7:~ }|
+ {1: }{2:------------------}│{1: }{4:6 }|
+ {1: }{2:------------------}│{1: }{4:7 }|
+ {1: }{2:------------------}│{1: }{4:8 }|
+ {1: }9 │{1: }9 |
+ {1: }10 │{1: }10 |
+ {1: }11 │{1: }11 |
+ {1: }12 │{1: }12 |
+ {1: }13 │{1: }13 |
+ {1: }14 │{1: }14 |
+ {1: }{5:15}{6:^x}{5: }│{1: }{5:15 }|
+ {7:~ }│{7:~ }|
+ {7:~ }│{7:~ }|
+ {7:~ }│{7:~ }|
+ {7:~ }│{7:~ }|
+ {7:~ }│{7:~ }|
+ {7:~ }│{7:~ }|
+ {7:~ }│{7:~ }|
+ {7:~ }│{7:~ }|
{8:[No Name] [+] }{3:[No Name] [+] }|
|
]]}
feed('<C-W>lay<Esc>')
screen:expect{grid=[[
- {1: }{2:-----------------}{3:│}{1: }{4:6 }|
- {1: }{2:-----------------}{3:│}{1: }{4:7 }|
- {1: }{2:-----------------}{3:│}{1: }{4:8 }|
- {1: }9 {3:│}{1: }9 |
- {1: }10 {3:│}{1: }10 |
- {1: }11 {3:│}{1: }11 |
- {1: }12 {3:│}{1: }12 |
- {1: }13 {3:│}{1: }13 |
- {1: }14 {3:│}{1: }14 |
- {1: }{5:15}{6:x}{5: }{3:│}{1: }{5:15}{6:^y}{5: }|
- {7:~ }{3:│}{7:~ }|
- {7:~ }{3:│}{7:~ }|
- {7:~ }{3:│}{7:~ }|
- {7:~ }{3:│}{7:~ }|
- {7:~ }{3:│}{7:~ }|
- {7:~ }{3:│}{7:~ }|
- {7:~ }{3:│}{7:~ }|
- {7:~ }{3:│}{7:~ }|
+ {1: }{2:-----------------}│{1: }{4:6 }|
+ {1: }{2:-----------------}│{1: }{4:7 }|
+ {1: }{2:-----------------}│{1: }{4:8 }|
+ {1: }9 │{1: }9 |
+ {1: }10 │{1: }10 |
+ {1: }11 │{1: }11 |
+ {1: }12 │{1: }12 |
+ {1: }13 │{1: }13 |
+ {1: }14 │{1: }14 |
+ {1: }{5:15}{6:x}{5: }│{1: }{5:15}{6:^y}{5: }|
+ {7:~ }│{7:~ }|
+ {7:~ }│{7:~ }|
+ {7:~ }│{7:~ }|
+ {7:~ }│{7:~ }|
+ {7:~ }│{7:~ }|
+ {7:~ }│{7:~ }|
+ {7:~ }│{7:~ }|
+ {7:~ }│{7:~ }|
{3:[No Name] [+] }{8:[No Name] [+] }|
|
]]}
end)
+
+it('diff mode works properly if file contains NUL bytes vim-patch:8.2.3925', function()
+ clear()
+ local screen = Screen.new(40, 20)
+ screen:set_default_attr_ids({
+ [1] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.Gray};
+ [2] = {reverse = true};
+ [3] = {background = Screen.colors.LightBlue};
+ [4] = {background = Screen.colors.LightMagenta};
+ [5] = {background = Screen.colors.Red, bold = true};
+ [6] = {foreground = Screen.colors.Blue, bold = true};
+ [7] = {background = Screen.colors.Red, foreground = Screen.colors.Blue, bold = true};
+ [8] = {reverse = true, bold = true};
+ })
+ screen:attach()
+ exec([[
+ call setline(1, ['a', 'b', "c\n", 'd', 'e', 'f', 'g'])
+ vnew
+ call setline(1, ['A', 'b', 'c', 'd', 'E', 'f', 'g'])
+ windo diffthis
+ wincmd p
+ norm! gg0
+ redraw!
+ ]])
+
+ -- Test using internal diff
+ screen:expect([[
+ {1: }{5:^A}{4: }│{1: }{5:a}{4: }|
+ {1: }b │{1: }b |
+ {1: }{4:c }│{1: }{4:c}{7:^@}{4: }|
+ {1: }d │{1: }d |
+ {1: }{5:E}{4: }│{1: }{5:e}{4: }|
+ {1: }f │{1: }f |
+ {1: }g │{1: }g |
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {8:[No Name] [+] }{2:[No Name] [+] }|
+ |
+ ]])
+
+ -- Test using internal diff and case folding
+ command('set diffopt+=icase')
+ feed('<C-L>')
+ screen:expect([[
+ {1: }^A │{1: }a |
+ {1: }b │{1: }b |
+ {1: }{4:c }│{1: }{4:c}{7:^@}{4: }|
+ {1: }d │{1: }d |
+ {1: }E │{1: }e |
+ {1: }f │{1: }f |
+ {1: }g │{1: }g |
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {8:[No Name] [+] }{2:[No Name] [+] }|
+ |
+ ]])
+
+ -- Test using external diff
+ command('set diffopt=filler')
+ feed('<C-L>')
+ screen:expect([[
+ {1: }{5:^A}{4: }│{1: }{5:a}{4: }|
+ {1: }b │{1: }b |
+ {1: }{4:c }│{1: }{4:c}{7:^@}{4: }|
+ {1: }d │{1: }d |
+ {1: }{5:E}{4: }│{1: }{5:e}{4: }|
+ {1: }f │{1: }f |
+ {1: }g │{1: }g |
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {8:[No Name] [+] }{2:[No Name] [+] }|
+ |
+ ]])
+
+ -- Test using external diff and case folding
+ command('set diffopt+=filler,icase')
+ feed('<C-L>')
+ screen:expect([[
+ {1: }^A │{1: }a |
+ {1: }b │{1: }b |
+ {1: }{4:c }│{1: }{4:c}{7:^@}{4: }|
+ {1: }d │{1: }d |
+ {1: }E │{1: }e |
+ {1: }f │{1: }f |
+ {1: }g │{1: }g |
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {6:~ }│{6:~ }|
+ {8:[No Name] [+] }{2:[No Name] [+] }|
+ |
+ ]])
+end)
diff --git a/test/functional/ui/embed_spec.lua b/test/functional/ui/embed_spec.lua
index 8218c8e12d..92f5beebf5 100644
--- a/test/functional/ui/embed_spec.lua
+++ b/test/functional/ui/embed_spec.lua
@@ -1,3 +1,5 @@
+local uv = require'luv'
+
local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
@@ -98,3 +100,49 @@ end
describe('--embed UI on startup (ext_linegrid=true)', function() test_embed(true) end)
describe('--embed UI on startup (ext_linegrid=false)', function() test_embed(false) end)
+
+describe('--embed UI', function()
+ it('can pass stdin', function()
+ local pipe = assert(uv.pipe())
+
+ local writer = assert(uv.new_pipe(false))
+ writer:open(pipe.write)
+
+ clear {args_rm={'--headless'}, io_extra=pipe.read}
+
+ -- attach immediately after startup, for early UI
+ local screen = Screen.new(40, 8)
+ screen:attach {stdin_fd=3}
+ screen:set_default_attr_ids {
+ [1] = {bold = true, foreground = Screen.colors.Blue1};
+ [2] = {bold = true};
+ }
+
+ writer:write "hello nvim\nfrom external input\n"
+ writer:shutdown(function() writer:close() end)
+
+ screen:expect{grid=[[
+ ^hello nvim |
+ from external input |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+
+ -- stdin (rpc input) still works
+ feed 'o'
+ screen:expect{grid=[[
+ hello nvim |
+ ^ |
+ from external input |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]]}
+ end)
+end)
diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua
index 5f29261b17..50247ed214 100644
--- a/test/functional/ui/float_spec.lua
+++ b/test/functional/ui/float_spec.lua
@@ -8,6 +8,7 @@ local command, feed_command = helpers.command, helpers.feed_command
local eval = helpers.eval
local eq = helpers.eq
local neq = helpers.neq
+local expect = helpers.expect
local exec_lua = helpers.exec_lua
local insert = helpers.insert
local meths = helpers.meths
@@ -16,10 +17,12 @@ local funcs = helpers.funcs
local run = helpers.run
local pcall_err = helpers.pcall_err
local tbl_contains = global_helpers.tbl_contains
+local curbuf, curwin, curtab = helpers.curbuf, helpers.curwin, helpers.curtab
describe('float window', function()
before_each(function()
clear()
+ command('hi VertSplit gui=reverse')
end)
local attrs = {
[0] = {bold=true, foreground=Screen.colors.Blue},
@@ -83,8 +86,8 @@ describe('float window', function()
local buf = meths.create_buf(false, false)
meths.buf_set_lines(buf, 0, -1, true, {'the floatwin'})
local win = meths.open_win(buf, true, {relative='win', width=16, height=1, row=0, col=10})
- eq(pcall_err(funcs.win_execute, win, 'close'), 'Vim(close):E37: No write since last change (add ! to override)')
- eq(pcall_err(funcs.win_execute, win, 'bdelete'), 'Vim(bdelete):E89: No write since last change for buffer 2 (add ! to override)')
+ eq('Vim(close):E37: No write since last change (add ! to override)', pcall_err(funcs.win_execute, win, 'close'))
+ eq('Vim(bdelete):E89: No write since last change for buffer 2 (add ! to override)', pcall_err(funcs.win_execute, win, 'bdelete'))
funcs.win_execute(win, 'bwipe!')
end)
@@ -365,7 +368,7 @@ describe('float window', function()
return vim.api.nvim_open_win(bufnr, false, opts)
]])
command('windo echo')
- neq(eval('win_getid()'), winid)
+ neq(winid, eval('win_getid()'))
end)
it('is active after windo when focusable', function()
@@ -382,7 +385,7 @@ describe('float window', function()
return vim.api.nvim_open_win(bufnr, false, opts)
]])
command('windo echo')
- eq(eval('win_getid()'), winid)
+ eq(winid, eval('win_getid()'))
end)
it('supports windo with focusable and non-focusable floats', function()
@@ -417,6 +420,287 @@ describe('float window', function()
eq(winids, eval('winids'))
end)
+ describe('with only one tabpage,', function()
+ local float_opts = {relative = 'editor', row = 1, col = 1, width = 1, height = 1}
+ local old_buf, old_win
+ before_each(function()
+ insert('foo')
+ old_buf = curbuf().id
+ old_win = curwin().id
+ end)
+ describe('closing the last non-floating window gives E444', function()
+ before_each(function()
+ meths.open_win(old_buf, true, float_opts)
+ end)
+ it('if called from non-floating window', function()
+ meths.set_current_win(old_win)
+ eq('Vim:E444: Cannot close last window',
+ pcall_err(meths.win_close, old_win, false))
+ end)
+ it('if called from floating window', function()
+ eq('Vim:E444: Cannot close last window',
+ pcall_err(meths.win_close, old_win, false))
+ end)
+ end)
+ describe("deleting the last non-floating window's buffer", function()
+ describe('leaves one window with an empty buffer when there is only one buffer', function()
+ local same_buf_float
+ before_each(function()
+ same_buf_float = meths.open_win(old_buf, false, float_opts).id
+ end)
+ after_each(function()
+ eq(old_win, curwin().id)
+ expect('')
+ eq(1, #meths.list_wins())
+ end)
+ it('if called from non-floating window', function()
+ meths.buf_delete(old_buf, {force = true})
+ end)
+ it('if called from floating window', function()
+ meths.set_current_win(same_buf_float)
+ command('autocmd WinLeave * let g:win_leave = nvim_get_current_win()')
+ command('autocmd WinEnter * let g:win_enter = nvim_get_current_win()')
+ meths.buf_delete(old_buf, {force = true})
+ eq(same_buf_float, eval('g:win_leave'))
+ eq(old_win, eval('g:win_enter'))
+ end)
+ end)
+ describe('closes other windows with that buffer when there are other buffers', function()
+ local same_buf_float, other_buf, other_buf_float
+ before_each(function()
+ same_buf_float = meths.open_win(old_buf, false, float_opts).id
+ other_buf = meths.create_buf(true, false).id
+ other_buf_float = meths.open_win(other_buf, true, float_opts).id
+ insert('bar')
+ meths.set_current_win(old_win)
+ end)
+ after_each(function()
+ eq(other_buf, curbuf().id)
+ expect('bar')
+ eq(2, #meths.list_wins())
+ end)
+ it('if called from non-floating window', function()
+ meths.buf_delete(old_buf, {force = true})
+ eq(old_win, curwin().id)
+ end)
+ it('if called from floating window with the same buffer', function()
+ meths.set_current_win(same_buf_float)
+ command('autocmd WinLeave * let g:win_leave = nvim_get_current_win()')
+ command('autocmd WinEnter * let g:win_enter = nvim_get_current_win()')
+ meths.buf_delete(old_buf, {force = true})
+ eq(same_buf_float, eval('g:win_leave'))
+ eq(old_win, eval('g:win_enter'))
+ eq(old_win, curwin().id)
+ end)
+ -- TODO: this case is too hard to deal with
+ pending('if called from floating window with another buffer', function()
+ meths.set_current_win(other_buf_float)
+ meths.buf_delete(old_buf, {force = true})
+ end)
+ end)
+ describe('creates an empty buffer when there is only one listed buffer', function()
+ local same_buf_float, unlisted_buf_float
+ before_each(function()
+ same_buf_float = meths.open_win(old_buf, false, float_opts).id
+ local unlisted_buf = meths.create_buf(true, false).id
+ unlisted_buf_float = meths.open_win(unlisted_buf, true, float_opts).id
+ insert('unlisted')
+ command('set nobuflisted')
+ meths.set_current_win(old_win)
+ end)
+ after_each(function()
+ expect('')
+ eq(2, #meths.list_wins())
+ end)
+ it('if called from non-floating window', function()
+ meths.buf_delete(old_buf, {force = true})
+ eq(old_win, curwin().id)
+ end)
+ it('if called from floating window with the same buffer', function()
+ meths.set_current_win(same_buf_float)
+ command('autocmd WinLeave * let g:win_leave = nvim_get_current_win()')
+ command('autocmd WinEnter * let g:win_enter = nvim_get_current_win()')
+ meths.buf_delete(old_buf, {force = true})
+ eq(same_buf_float, eval('g:win_leave'))
+ eq(old_win, eval('g:win_enter'))
+ eq(old_win, curwin().id)
+ end)
+ -- TODO: this case is too hard to deal with
+ pending('if called from floating window with an unlisted buffer', function()
+ meths.set_current_win(unlisted_buf_float)
+ meths.buf_delete(old_buf, {force = true})
+ end)
+ end)
+ end)
+ describe('with splits, deleting the last listed buffer creates an empty buffer', function()
+ describe('when a non-floating window has an unlisted buffer', function()
+ local same_buf_float
+ before_each(function()
+ command('botright vnew')
+ insert('unlisted')
+ command('set nobuflisted')
+ meths.set_current_win(old_win)
+ same_buf_float = meths.open_win(old_buf, false, float_opts).id
+ end)
+ after_each(function()
+ expect('')
+ eq(2, #meths.list_wins())
+ end)
+ it('if called from non-floating window with the deleted buffer', function()
+ meths.buf_delete(old_buf, {force = true})
+ eq(old_win, curwin().id)
+ end)
+ it('if called from floating window with the deleted buffer', function()
+ meths.set_current_win(same_buf_float)
+ meths.buf_delete(old_buf, {force = true})
+ eq(same_buf_float, curwin().id)
+ end)
+ end)
+ end)
+ end)
+
+ describe('with mulitple tabpages but only one listed buffer,', function()
+ local float_opts = {relative = 'editor', row = 1, col = 1, width = 1, height = 1}
+ local unlisted_buf, old_buf, old_win
+ before_each(function()
+ insert('unlisted')
+ command('set nobuflisted')
+ unlisted_buf = curbuf().id
+ command('tabnew')
+ insert('foo')
+ old_buf = curbuf().id
+ old_win = curwin().id
+ end)
+ describe('without splits, deleting the last listed buffer creates an empty buffer', function()
+ local same_buf_float
+ before_each(function()
+ meths.set_current_win(old_win)
+ same_buf_float = meths.open_win(old_buf, false, float_opts).id
+ end)
+ after_each(function()
+ expect('')
+ eq(2, #meths.list_wins())
+ eq(2, #meths.list_tabpages())
+ end)
+ it('if called from non-floating window', function()
+ meths.buf_delete(old_buf, {force = true})
+ eq(old_win, curwin().id)
+ end)
+ it('if called from floating window with the same buffer', function()
+ meths.set_current_win(same_buf_float)
+ command('autocmd WinLeave * let g:win_leave = nvim_get_current_win()')
+ command('autocmd WinEnter * let g:win_enter = nvim_get_current_win()')
+ meths.buf_delete(old_buf, {force = true})
+ eq(same_buf_float, eval('g:win_leave'))
+ eq(old_win, eval('g:win_enter'))
+ eq(old_win, curwin().id)
+ end)
+ end)
+ describe('with splits, deleting the last listed buffer creates an empty buffer', function()
+ local same_buf_float
+ before_each(function()
+ command('botright vsplit')
+ meths.set_current_buf(unlisted_buf)
+ meths.set_current_win(old_win)
+ same_buf_float = meths.open_win(old_buf, false, float_opts).id
+ end)
+ after_each(function()
+ expect('')
+ eq(3, #meths.list_wins())
+ eq(2, #meths.list_tabpages())
+ end)
+ it('if called from non-floating window with the deleted buffer', function()
+ meths.buf_delete(old_buf, {force = true})
+ eq(old_win, curwin().id)
+ end)
+ it('if called from floating window with the deleted buffer', function()
+ meths.set_current_win(same_buf_float)
+ meths.buf_delete(old_buf, {force = true})
+ eq(same_buf_float, curwin().id)
+ end)
+ end)
+ end)
+
+ describe('with multiple tabpages and multiple listed buffers,', function()
+ local float_opts = {relative = 'editor', row = 1, col = 1, width = 1, height = 1}
+ local old_tabpage, old_buf, old_win
+ before_each(function()
+ old_tabpage = curtab().id
+ insert('oldtab')
+ command('tabnew')
+ old_buf = curbuf().id
+ old_win = curwin().id
+ end)
+ describe('closing the last non-floating window', function()
+ describe('closes the tabpage when all floating windows are closeable', function()
+ local same_buf_float
+ before_each(function()
+ same_buf_float = meths.open_win(old_buf, false, float_opts).id
+ end)
+ after_each(function()
+ eq(old_tabpage, curtab().id)
+ expect('oldtab')
+ eq(1, #meths.list_tabpages())
+ end)
+ it('if called from non-floating window', function()
+ meths.win_close(old_win, false)
+ end)
+ it('if called from floating window', function()
+ meths.set_current_win(same_buf_float)
+ meths.win_close(old_win, false)
+ end)
+ end)
+ describe('gives E5601 when there are non-closeable floating windows', function()
+ local other_buf_float
+ before_each(function()
+ command('set nohidden')
+ local other_buf = meths.create_buf(true, false).id
+ other_buf_float = meths.open_win(other_buf, true, float_opts).id
+ insert('foo')
+ meths.set_current_win(old_win)
+ end)
+ it('if called from non-floating window', function()
+ eq('Vim:E5601: Cannot close window, only floating window would remain',
+ pcall_err(meths.win_close, old_win, false))
+ end)
+ it('if called from floating window', function()
+ meths.set_current_win(other_buf_float)
+ eq('Vim:E5601: Cannot close window, only floating window would remain',
+ pcall_err(meths.win_close, old_win, false))
+ end)
+ end)
+ end)
+ describe("deleting the last non-floating window's buffer", function()
+ describe('closes the tabpage when all floating windows are closeable', function()
+ local same_buf_float, other_buf, other_buf_float
+ before_each(function()
+ same_buf_float = meths.open_win(old_buf, false, float_opts).id
+ other_buf = meths.create_buf(true, false).id
+ other_buf_float = meths.open_win(other_buf, true, float_opts).id
+ meths.set_current_win(old_win)
+ end)
+ after_each(function()
+ eq(old_tabpage, curtab().id)
+ expect('oldtab')
+ eq(1, #meths.list_tabpages())
+ end)
+ it('if called from non-floating window', function()
+ meths.buf_delete(old_buf, {force = false})
+ end)
+ it('if called from floating window with the same buffer', function()
+ meths.set_current_win(same_buf_float)
+ meths.buf_delete(old_buf, {force = false})
+ end)
+ -- TODO: this case is too hard to deal with
+ pending('if called from floating window with another buffer', function()
+ meths.set_current_win(other_buf_float)
+ meths.buf_delete(old_buf, {force = false})
+ end)
+ end)
+ -- TODO: what to do when there are non-closeable floating windows?
+ end)
+ end)
+
local function with_ext_multigrid(multigrid)
local screen
before_each(function()
@@ -3376,10 +3660,10 @@ describe('float window', function()
screen:expect{grid=[[
## grid 1
[2:----------------------------------------]|
- {5:[No Name] }|
- [5:----------------------------------------]|
- [5:----------------------------------------]|
- [5:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
{5:[Preview] }|
[3:----------------------------------------]|
## grid 2
@@ -3390,10 +3674,6 @@ describe('float window', function()
{17:f}{1:oo }|
{17:b}{1:ar }|
{1: }|
- ## grid 5
- |1| {17:f}oo |
- |2| {17:b}ar |
- {0:~ }|
]], float_pos=expected_pos}
else
screen:expect([[
@@ -4169,7 +4449,7 @@ describe('float window', function()
## grid 4
{1:y }|
{2:~ }|
- ]], float_pos=expected_pos}
+ ]], float_pos=expected_pos}
else
screen:expect([[
^x |
@@ -4207,7 +4487,7 @@ describe('float window', function()
## grid 4
{1:^y }|
{2:~ }|
- ]], float_pos=expected_pos}
+ ]], float_pos=expected_pos}
else
screen:expect([[
x |
@@ -4243,7 +4523,7 @@ describe('float window', function()
## grid 4
{1:y }|
{2:~ }|
- ]], float_pos=expected_pos}
+ ]], float_pos=expected_pos}
else
screen:expect([[
^x |
@@ -4283,7 +4563,7 @@ describe('float window', function()
## grid 4
{1:y }|
{2:~ }|
- ]], float_pos=expected_pos}
+ ]], float_pos=expected_pos}
else
screen:expect([[
^x |
@@ -4319,7 +4599,7 @@ describe('float window', function()
## grid 4
{1:y }|
{2:~ }|
- ]], float_pos=expected_pos}
+ ]], float_pos=expected_pos}
else
screen:expect([[
^x |
@@ -4357,7 +4637,7 @@ describe('float window', function()
## grid 4
{1:^y }|
{2:~ }|
- ]], float_pos=expected_pos}
+ ]], float_pos=expected_pos}
else
screen:expect([[
x |
@@ -4393,7 +4673,7 @@ describe('float window', function()
## grid 4
{1:y }|
{2:~ }|
- ]], float_pos=expected_pos}
+ ]], float_pos=expected_pos}
else
screen:expect([[
^x |
@@ -4431,7 +4711,7 @@ describe('float window', function()
## grid 4
{1:^y }|
{2:~ }|
- ]], float_pos=expected_pos}
+ ]], float_pos=expected_pos}
else
meths.input_mouse('left', 'press', '', 0, 2, 5)
screen:expect([[
@@ -4468,7 +4748,7 @@ describe('float window', function()
## grid 4
{1:y }|
{2:~ }|
- ]], float_pos=expected_pos}
+ ]], float_pos=expected_pos}
else
meths.input_mouse('left', 'press', '', 0, 0, 0)
screen:expect([[
@@ -4510,7 +4790,7 @@ describe('float window', function()
## grid 4
{1:y }|
{2:~ }|
- ]], float_pos=expected_pos}
+ ]], float_pos=expected_pos}
else
meths.input_mouse('left', 'press', '', 0, 2, 5)
screen:expect([[
@@ -4547,7 +4827,7 @@ describe('float window', function()
## grid 4
{1:y }|
{2:~ }|
- ]], float_pos=expected_pos, unchanged=true}
+ ]], float_pos=expected_pos, unchanged=true}
else
meths.input_mouse('left', 'press', '', 0, 0, 0)
screen:expect([[
@@ -4562,7 +4842,6 @@ describe('float window', function()
end
end)
-
it("j", function()
feed("<c-w>ji") -- INSERT to trigger screen change
if multigrid then
@@ -4587,7 +4866,7 @@ describe('float window', function()
## grid 4
{1:y }|
{2:~ }|
- ]], float_pos=expected_pos}
+ ]], float_pos=expected_pos}
else
screen:expect([[
^x |
@@ -4623,7 +4902,7 @@ describe('float window', function()
## grid 4
{1:^y }|
{2:~ }|
- ]], float_pos=expected_pos}
+ ]], float_pos=expected_pos}
else
screen:expect([[
x |
@@ -4659,7 +4938,7 @@ describe('float window', function()
## grid 4
{1:y }|
{2:~ }|
- ]], float_pos=expected_pos}
+ ]], float_pos=expected_pos}
else
screen:expect([[
^x |
@@ -4698,7 +4977,7 @@ describe('float window', function()
## grid 4
{1:^y }|
{2:~ }|
- ]], float_pos=expected_pos}
+ ]], float_pos=expected_pos}
else
screen:expect([[
x |
@@ -4886,7 +5165,7 @@ describe('float window', function()
## grid 4
{1:^y }|
{2:~ }|
- ]], float_pos=expected_pos}
+ ]], float_pos=expected_pos}
else
screen:expect([[
x |
@@ -5067,7 +5346,7 @@ describe('float window', function()
## grid 5
^x |
{0:~ }|
- ]], float_pos=expected_pos}
+ ]], float_pos=expected_pos}
else
screen:expect([[
^x |
@@ -5102,7 +5381,7 @@ describe('float window', function()
## grid 5
x |
{0:~ }|
- ]], float_pos=expected_pos}
+ ]], float_pos=expected_pos}
else
screen:expect([[
x |
@@ -5137,7 +5416,7 @@ describe('float window', function()
## grid 5
x |
{0:~ }|
- ]], float_pos=expected_pos}
+ ]], float_pos=expected_pos}
else
screen:expect([[
x |
@@ -5173,7 +5452,7 @@ describe('float window', function()
## grid 5
^x |
{0:~ }|
- ]], float_pos=expected_pos}
+ ]], float_pos=expected_pos}
else
screen:expect([[
^x |
@@ -5210,7 +5489,7 @@ describe('float window', function()
## grid 5
^y |
{0:~ }|
- ]], float_pos=expected_pos}
+ ]], float_pos=expected_pos}
else
screen:expect([[
^y |
@@ -5245,7 +5524,7 @@ describe('float window', function()
## grid 5
y |
{0:~ }|
- ]], float_pos=expected_pos}
+ ]], float_pos=expected_pos}
else
screen:expect([[
y |
@@ -5280,7 +5559,7 @@ describe('float window', function()
## grid 5
y |
{0:~ }|
- ]], float_pos=expected_pos}
+ ]], float_pos=expected_pos}
else
screen:expect([[
y |
@@ -5317,7 +5596,7 @@ describe('float window', function()
## grid 5
^ |
{0:~ }|
- ]], float_pos=expected_pos}
+ ]], float_pos=expected_pos}
else
screen:expect([[
^ |
@@ -5354,7 +5633,7 @@ describe('float window', function()
## grid 5
^ |
{0:~ }|
- ]], float_pos=expected_pos}
+ ]], float_pos=expected_pos}
else
screen:expect([[
^ |
@@ -5397,7 +5676,7 @@ describe('float window', function()
{0:~ }|
{0:~ }|
{0:~ }|
- ]], float_pos=expected_pos}
+ ]], float_pos=expected_pos}
else
screen:expect([[
^x {5:│}x |
@@ -5440,17 +5719,17 @@ describe('float window', function()
{0:~ }|
{0:~ }|
{0:~ }|
- ]], float_pos=expected_pos}
+ ]], float_pos=expected_pos}
else
- screen:expect([[
- ^ {5:│}x |
- {0:~ }{5:│}{0:~ }|
- {0:~ }{1:y }{0: }|
- {0:~ }{2:~ }{0: }|
- {0:~ }{5:│}{0:~ }|
- {4:[No Name] }{5:[No Name] [+] }|
- :vnew |
- ]])
+ screen:expect([[
+ ^ {5:│}x |
+ {0:~ }{5:│}{0:~ }|
+ {0:~ }{1:y }{0: }|
+ {0:~ }{2:~ }{0: }|
+ {0:~ }{5:│}{0:~ }|
+ {4:[No Name] }{5:[No Name] [+] }|
+ :vnew |
+ ]])
end
end)
@@ -5483,7 +5762,7 @@ describe('float window', function()
{0:~ }|
{0:~ }|
{0:~ }|
- ]], float_pos=expected_pos}
+ ]], float_pos=expected_pos}
else
screen:expect([[
^ {5:│}x |
@@ -5553,7 +5832,7 @@ describe('float window', function()
[4] = {{id = 1001}, "NW", 1, 2, 5, true},
[5] = {{id = 1002}, "NW", 1, 4, 8, true}
}}
- else
+ else
screen:expect([[
x |
{0:~ }|
@@ -5574,7 +5853,7 @@ describe('float window', function()
[2:----------------------------------------]|
[2:----------------------------------------]|
[2:----------------------------------------]|
- {5:[No Name] [+] }|
+ [2:----------------------------------------]|
[3:----------------------------------------]|
## grid 2
x |
@@ -5582,6 +5861,7 @@ describe('float window', function()
{0:~ }|
{0:~ }|
{0:~ }|
+ {0:~ }|
## grid 3
:quit |
## grid 4
@@ -5590,14 +5870,14 @@ describe('float window', function()
]], float_pos={
[4] = {{id = 1001}, "NW", 1, 2, 5, true},
}}
- else
+ else
screen:expect([[
x |
{0:~ }|
{0:~ }{1:^y }{0: }|
{0:~ }{2:~ }{0: }|
{0:~ }|
- {5:[No Name] [+] }|
+ {0:~ }|
:quit |
]])
end
@@ -5659,7 +5939,7 @@ describe('float window', function()
{0:~ }|
## grid 3
|
- ]]}
+ ]]}
else
screen:expect([[
^x |
@@ -5699,7 +5979,7 @@ describe('float window', function()
## grid 4
{1:y }|
{2:~ }|
- ]], float_pos=expected_pos}
+ ]], float_pos=expected_pos}
else
screen:expect([[
x |
@@ -5736,7 +6016,7 @@ describe('float window', function()
## grid 4
{1:^y }|
{2:~ }|
- ]], float_pos=expected_pos}
+ ]], float_pos=expected_pos}
else
screen:expect([[
x |
@@ -5806,7 +6086,7 @@ describe('float window', function()
{0:~ }|
{0:~ }|
{0:~ }|
- ]]}
+ ]]}
else
screen:expect([[
^x |
@@ -5843,7 +6123,7 @@ describe('float window', function()
## grid 5
x |
{0:~ }|
- ]], float_pos=expected_pos}
+ ]], float_pos=expected_pos}
else
screen:expect([[
x |
@@ -5880,7 +6160,7 @@ describe('float window', function()
## grid 5
x |
{0:~ }|
- ]], float_pos=expected_pos}
+ ]], float_pos=expected_pos}
else
screen:expect([[
x |
@@ -5914,7 +6194,7 @@ describe('float window', function()
## grid 4
^y |
{0:~ }|
- ]]}
+ ]]}
else
screen:expect([[
x |
@@ -5950,7 +6230,7 @@ describe('float window', function()
## grid 4
^y |
{0:~ }|
- ]], float_pos=expected_pos}
+ ]], float_pos=expected_pos}
else
eq("UI doesn't support external windows",
pcall_err(meths.win_set_config, 0, {external=true, width=30, height=2}))
@@ -5980,6 +6260,78 @@ describe('float window', function()
end
end)
+ it('J (float with border)', function()
+ meths.win_set_config(win, {relative='editor', width=20, height=2, row=2, col=5, border='single'})
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [3:----------------------------------------]|
+ ## grid 2
+ ^x |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ |
+ ## grid 4
+ {5:┌────────────────────┐}|
+ {5:│}{1:y }{5:│}|
+ {5:│}{2:~ }{5:│}|
+ {5:└────────────────────┘}|
+ ]], float_pos=expected_pos}
+ else
+ screen:expect([[
+ ^x |
+ {0:~ }|
+ {0:~ }{5:┌────────────────────┐}{0: }|
+ {0:~ }{5:│}{1:y }{5:│}{0: }|
+ {0:~ }{5:│}{2:~ }{5:│}{0: }|
+ {0:~ }{5:└────────────────────┘}{0: }|
+ |
+ ]])
+ end
+
+ feed("<c-w>w<c-w>J")
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ {5:[No Name] [+] }|
+ [4:----------------------------------------]|
+ [4:----------------------------------------]|
+ {4:[No Name] [+] }|
+ [3:----------------------------------------]|
+ ## grid 2
+ x |
+ {0:~ }|
+ ## grid 3
+ |
+ ## grid 4
+ ^y |
+ {0:~ }|
+ ]]}
+ else
+ screen:expect([[
+ x |
+ {0:~ }|
+ {5:[No Name] [+] }|
+ ^y |
+ {0:~ }|
+ {4:[No Name] [+] }|
+ |
+ ]])
+ end
+ end)
+
it('movements with nested split layout', function()
command("set hidden")
feed("<c-w>s<c-w>v<c-w>b<c-w>v")
@@ -6342,7 +6694,7 @@ describe('float window', function()
it("left drag changes visual selection in float window", function()
local buf = meths.create_buf(false,false)
- meths.buf_set_lines(buf, 0, -1, true, {'foo', 'bar'})
+ meths.buf_set_lines(buf, 0, -1, true, {'foo', 'bar', 'baz'})
meths.open_win(buf, false, {relative='editor', width=20, height=3, row=2, col=5})
if multigrid then
screen:expect{grid=[[
@@ -6366,13 +6718,14 @@ describe('float window', function()
## grid 5
{1:foo }|
{1:bar }|
- {2:~ }|
+ {1:baz }|
]], float_pos={
[5] = {{id = 1002}, "NW", 1, 2, 5, true, 50};
}, win_viewport={
[2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1};
- [5] = {win = {id = 1002}, topline = 0, botline = 3, curline = 0, curcol = 0, linecount = 2};
+ [5] = {win = {id = 1002}, topline = 0, botline = 3, curline = 0, curcol = 0, linecount = 3};
}}
+
meths.input_mouse('left', 'press', '', 5, 0, 0)
screen:expect{grid=[[
## grid 1
@@ -6395,13 +6748,14 @@ describe('float window', function()
## grid 5
{1:^foo }|
{1:bar }|
- {2:~ }|
+ {1:baz }|
]], float_pos={
[5] = {{id = 1002}, "NW", 1, 2, 5, true, 50};
}, win_viewport={
[2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1};
- [5] = {win = {id = 1002}, topline = 0, botline = 3, curline = 0, curcol = 0, linecount = 2};
+ [5] = {win = {id = 1002}, topline = 0, botline = 3, curline = 0, curcol = 0, linecount = 3};
}}
+
meths.input_mouse('left', 'drag', '', 5, 1, 2)
screen:expect{grid=[[
## grid 1
@@ -6424,12 +6778,12 @@ describe('float window', function()
## grid 5
{27:foo}{1: }|
{27:ba}{1:^r }|
- {2:~ }|
+ {1:baz }|
]], float_pos={
[5] = {{id = 1002}, "NW", 1, 2, 5, true, 50};
}, win_viewport={
[2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1};
- [5] = {win = {id = 1002}, topline = 0, botline = 3, curline = 1, curcol = 2, linecount = 2};
+ [5] = {win = {id = 1002}, topline = 0, botline = 3, curline = 1, curcol = 2, linecount = 3};
}}
else
screen:expect{grid=[[
@@ -6437,7 +6791,7 @@ describe('float window', function()
{0:~ }|
{0:~ }{1:foo }{0: }|
{0:~ }{1:bar }{0: }|
- {0:~ }{2:~ }{0: }|
+ {0:~ }{1:baz }{0: }|
{0:~ }|
|
]]}
@@ -6448,7 +6802,7 @@ describe('float window', function()
{0:~ }|
{0:~ }{1:^foo }{0: }|
{0:~ }{1:bar }{0: }|
- {0:~ }{2:~ }{0: }|
+ {0:~ }{1:baz }{0: }|
{0:~ }|
|
]]}
@@ -6459,13 +6813,396 @@ describe('float window', function()
{0:~ }|
{0:~ }{27:foo}{1: }{0: }|
{0:~ }{27:ba}{1:^r }{0: }|
- {0:~ }{2:~ }{0: }|
+ {0:~ }{1:baz }{0: }|
{0:~ }|
{3:-- VISUAL --} |
]]}
end
end)
+ it("left drag changes visual selection in float window with border", function()
+ local buf = meths.create_buf(false,false)
+ meths.buf_set_lines(buf, 0, -1, true, {'foo', 'bar', 'baz'})
+ meths.open_win(buf, false, {relative='editor', width=20, height=3, row=0, col=5, border='single'})
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [3:----------------------------------------]|
+ ## grid 2
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ |
+ ## grid 5
+ {5:┌────────────────────┐}|
+ {5:│}{1:foo }{5:│}|
+ {5:│}{1:bar }{5:│}|
+ {5:│}{1:baz }{5:│}|
+ {5:└────────────────────┘}|
+ ]], float_pos={
+ [5] = {{id = 1002}, "NW", 1, 0, 5, true, 50};
+ }, win_viewport={
+ [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1};
+ [5] = {win = {id = 1002}, topline = 0, botline = 3, curline = 0, curcol = 0, linecount = 3};
+ }}
+
+ meths.input_mouse('left', 'press', '', 5, 1, 1)
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [3:----------------------------------------]|
+ ## grid 2
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ |
+ ## grid 5
+ {5:┌────────────────────┐}|
+ {5:│}{1:^foo }{5:│}|
+ {5:│}{1:bar }{5:│}|
+ {5:│}{1:baz }{5:│}|
+ {5:└────────────────────┘}|
+ ]], float_pos={
+ [5] = {{id = 1002}, "NW", 1, 0, 5, true, 50};
+ }, win_viewport={
+ [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1};
+ [5] = {win = {id = 1002}, topline = 0, botline = 3, curline = 0, curcol = 0, linecount = 3};
+ }}
+
+ meths.input_mouse('left', 'drag', '', 5, 2, 3)
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [3:----------------------------------------]|
+ ## grid 2
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ {3:-- VISUAL --} |
+ ## grid 5
+ {5:┌────────────────────┐}|
+ {5:│}{27:foo}{1: }{5:│}|
+ {5:│}{27:ba}{1:^r }{5:│}|
+ {5:│}{1:baz }{5:│}|
+ {5:└────────────────────┘}|
+ ]], float_pos={
+ [5] = {{id = 1002}, "NW", 1, 0, 5, true, 50};
+ }, win_viewport={
+ [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1};
+ [5] = {win = {id = 1002}, topline = 0, botline = 3, curline = 1, curcol = 2, linecount = 3};
+ }}
+ else
+ screen:expect{grid=[[
+ ^ {5:┌────────────────────┐} |
+ {0:~ }{5:│}{1:foo }{5:│}{0: }|
+ {0:~ }{5:│}{1:bar }{5:│}{0: }|
+ {0:~ }{5:│}{1:baz }{5:│}{0: }|
+ {0:~ }{5:└────────────────────┘}{0: }|
+ {0:~ }|
+ |
+ ]]}
+
+ meths.input_mouse('left', 'press', '', 0, 1, 6)
+ screen:expect{grid=[[
+ {5:┌────────────────────┐} |
+ {0:~ }{5:│}{1:^foo }{5:│}{0: }|
+ {0:~ }{5:│}{1:bar }{5:│}{0: }|
+ {0:~ }{5:│}{1:baz }{5:│}{0: }|
+ {0:~ }{5:└────────────────────┘}{0: }|
+ {0:~ }|
+ |
+ ]]}
+
+ meths.input_mouse('left', 'drag', '', 0, 2, 8)
+ screen:expect{grid=[[
+ {5:┌────────────────────┐} |
+ {0:~ }{5:│}{27:foo}{1: }{5:│}{0: }|
+ {0:~ }{5:│}{27:ba}{1:^r }{5:│}{0: }|
+ {0:~ }{5:│}{1:baz }{5:│}{0: }|
+ {0:~ }{5:└────────────────────┘}{0: }|
+ {0:~ }|
+ {3:-- VISUAL --} |
+ ]]}
+ end
+ end)
+
+ it("left drag changes visual selection in float window with winbar", function()
+ local buf = meths.create_buf(false,false)
+ meths.buf_set_lines(buf, 0, -1, true, {'foo', 'bar', 'baz'})
+ local float_win = meths.open_win(buf, false, {relative='editor', width=20, height=4, row=1, col=5})
+ meths.win_set_option(float_win, 'winbar', 'floaty bar')
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [3:----------------------------------------]|
+ ## grid 2
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ |
+ ## grid 5
+ {3:floaty bar }|
+ {1:foo }|
+ {1:bar }|
+ {1:baz }|
+ ]], float_pos={
+ [5] = {{id = 1002}, "NW", 1, 1, 5, true, 50};
+ }, win_viewport={
+ [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1};
+ [5] = {win = {id = 1002}, topline = 0, botline = 3, curline = 0, curcol = 0, linecount = 3};
+ }}
+
+ meths.input_mouse('left', 'press', '', 5, 1, 0)
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [3:----------------------------------------]|
+ ## grid 2
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ |
+ ## grid 5
+ {3:floaty bar }|
+ {1:^foo }|
+ {1:bar }|
+ {1:baz }|
+ ]], float_pos={
+ [5] = {{id = 1002}, "NW", 1, 1, 5, true, 50};
+ }, win_viewport={
+ [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1};
+ [5] = {win = {id = 1002}, topline = 0, botline = 3, curline = 0, curcol = 0, linecount = 3};
+ }}
+
+ meths.input_mouse('left', 'drag', '', 5, 2, 2)
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [3:----------------------------------------]|
+ ## grid 2
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ {3:-- VISUAL --} |
+ ## grid 5
+ {3:floaty bar }|
+ {27:foo}{1: }|
+ {27:ba}{1:^r }|
+ {1:baz }|
+ ]], float_pos={
+ [5] = {{id = 1002}, "NW", 1, 1, 5, true, 50};
+ }, win_viewport={
+ [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1};
+ [5] = {win = {id = 1002}, topline = 0, botline = 3, curline = 1, curcol = 2, linecount = 3};
+ }}
+ else
+ screen:expect{grid=[[
+ ^ |
+ {0:~ }{3:floaty bar }{0: }|
+ {0:~ }{1:foo }{0: }|
+ {0:~ }{1:bar }{0: }|
+ {0:~ }{1:baz }{0: }|
+ {0:~ }|
+ |
+ ]]}
+
+ meths.input_mouse('left', 'press', '', 0, 2, 5)
+ screen:expect{grid=[[
+ |
+ {0:~ }{3:floaty bar }{0: }|
+ {0:~ }{1:^foo }{0: }|
+ {0:~ }{1:bar }{0: }|
+ {0:~ }{1:baz }{0: }|
+ {0:~ }|
+ |
+ ]]}
+
+ meths.input_mouse('left', 'drag', '', 0, 3, 7)
+ screen:expect{grid=[[
+ |
+ {0:~ }{3:floaty bar }{0: }|
+ {0:~ }{27:foo}{1: }{0: }|
+ {0:~ }{27:ba}{1:^r }{0: }|
+ {0:~ }{1:baz }{0: }|
+ {0:~ }|
+ {3:-- VISUAL --} |
+ ]]}
+ end
+ end)
+
+ it('left drag changes visual selection if float window is turned into a split', function()
+ local buf = meths.create_buf(false,false)
+ meths.buf_set_lines(buf, 0, -1, true, {'foo', 'bar', 'baz'})
+ meths.open_win(buf, true, {relative='editor', width=20, height=3, row=2, col=5})
+ command('wincmd L')
+ if multigrid then
+ screen:expect([[
+ ## grid 1
+ [2:-------------------]{5:│}[5:--------------------]|
+ [2:-------------------]{5:│}[5:--------------------]|
+ [2:-------------------]{5:│}[5:--------------------]|
+ [2:-------------------]{5:│}[5:--------------------]|
+ [2:-------------------]{5:│}[5:--------------------]|
+ {5:[No Name] }{4:[No Name] [+] }|
+ [3:----------------------------------------]|
+ ## grid 2
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ |
+ ## grid 5
+ ^foo |
+ bar |
+ baz |
+ {0:~ }|
+ {0:~ }|
+ ]])
+
+ meths.input_mouse('left', 'press', '', 5, 2, 2)
+ screen:expect([[
+ ## grid 1
+ [2:-------------------]{5:│}[5:--------------------]|
+ [2:-------------------]{5:│}[5:--------------------]|
+ [2:-------------------]{5:│}[5:--------------------]|
+ [2:-------------------]{5:│}[5:--------------------]|
+ [2:-------------------]{5:│}[5:--------------------]|
+ {5:[No Name] }{4:[No Name] [+] }|
+ [3:----------------------------------------]|
+ ## grid 2
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ |
+ ## grid 5
+ foo |
+ bar |
+ ba^z |
+ {0:~ }|
+ {0:~ }|
+ ]])
+
+ meths.input_mouse('left', 'drag', '', 5, 1, 1)
+ screen:expect([[
+ ## grid 1
+ [2:-------------------]{5:│}[5:--------------------]|
+ [2:-------------------]{5:│}[5:--------------------]|
+ [2:-------------------]{5:│}[5:--------------------]|
+ [2:-------------------]{5:│}[5:--------------------]|
+ [2:-------------------]{5:│}[5:--------------------]|
+ {5:[No Name] }{4:[No Name] [+] }|
+ [3:----------------------------------------]|
+ ## grid 2
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ {3:-- VISUAL --} |
+ ## grid 5
+ foo |
+ b^a{27:r} |
+ {27:baz} |
+ {0:~ }|
+ {0:~ }|
+ ]])
+ else
+ screen:expect([[
+ {5:│}^foo |
+ {0:~ }{5:│}bar |
+ {0:~ }{5:│}baz |
+ {0:~ }{5:│}{0:~ }|
+ {0:~ }{5:│}{0:~ }|
+ {5:[No Name] }{4:[No Name] [+] }|
+ |
+ ]])
+
+ meths.input_mouse('left', 'press', '', 0, 2, 22)
+ screen:expect([[
+ {5:│}foo |
+ {0:~ }{5:│}bar |
+ {0:~ }{5:│}ba^z |
+ {0:~ }{5:│}{0:~ }|
+ {0:~ }{5:│}{0:~ }|
+ {5:[No Name] }{4:[No Name] [+] }|
+ |
+ ]])
+
+ meths.input_mouse('left', 'drag', '', 0, 1, 21)
+ screen:expect([[
+ {5:│}foo |
+ {0:~ }{5:│}b^a{27:r} |
+ {0:~ }{5:│}{27:baz} |
+ {0:~ }{5:│}{0:~ }|
+ {0:~ }{5:│}{0:~ }|
+ {5:[No Name] }{4:[No Name] [+] }|
+ {3:-- VISUAL --} |
+ ]])
+ end
+ end)
+
it("'winblend' option", function()
screen:try_resize(50,9)
screen:set_default_attr_ids({
@@ -7306,6 +8043,100 @@ describe('float window', function()
]]}
end
end)
+
+ it('can use winbar', function()
+ local buf = meths.create_buf(false,false)
+ local win1 = meths.open_win(buf, false, {relative='editor', width=15, height=3, row=1, col=5})
+ meths.win_set_option(win1, 'winbar', 'floaty bar')
+
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [3:----------------------------------------]|
+ ## grid 2
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ |
+ ## grid 4
+ {3:floaty bar }|
+ {1: }|
+ {2:~ }|
+ ]], float_pos={
+ [4] = {{id = 1001}, "NW", 1, 1, 5, true, 50};
+ }, win_viewport={
+ [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1};
+ [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1};
+ }}
+ else
+ screen:expect{grid=[[
+ ^ |
+ {0:~ }{3:floaty bar }{0: }|
+ {0:~ }{1: }{0: }|
+ {0:~ }{2:~ }{0: }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]]}
+ end
+
+ -- resize and add a border
+ meths.win_set_config(win1, {relative='editor', width=15, height=4, row=0, col=4, border = 'single'})
+
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [3:----------------------------------------]|
+ ## grid 2
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ |
+ ## grid 4
+ {5:┌───────────────┐}|
+ {5:│}{3:floaty bar }{5:│}|
+ {5:│}{1: }{5:│}|
+ {5:│}{2:~ }{5:│}|
+ {5:│}{2:~ }{5:│}|
+ {5:└───────────────┘}|
+ ]], float_pos={
+ [4] = {{id = 1001}, "NW", 1, 0, 4, true, 50};
+ }, win_viewport={
+ [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1};
+ [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1};
+ }}
+ else
+ screen:expect{grid=[[
+ ^ {5:┌───────────────┐} |
+ {0:~ }{5:│}{3:floaty bar }{5:│}{0: }|
+ {0:~ }{5:│}{1: }{5:│}{0: }|
+ {0:~ }{5:│}{2:~ }{5:│}{0: }|
+ {0:~ }{5:│}{2:~ }{5:│}{0: }|
+ {0:~ }{5:└───────────────┘}{0: }|
+ |
+ ]]}
+ end
+ end)
end
describe('with ext_multigrid', function()
diff --git a/test/functional/ui/fold_spec.lua b/test/functional/ui/fold_spec.lua
index 3e0e15c2b7..394f2f5f49 100644
--- a/test/functional/ui/fold_spec.lua
+++ b/test/functional/ui/fold_spec.lua
@@ -21,12 +21,14 @@ local content1 = [[
describe("folded lines", function()
before_each(function()
clear()
+ command('hi VertSplit gui=reverse')
end)
local function with_ext_multigrid(multigrid)
local screen
before_each(function()
clear()
+ command('hi VertSplit gui=reverse')
screen = Screen.new(45, 8)
screen:attach({rgb=true, ext_multigrid=multigrid})
screen:set_default_attr_ids({
@@ -1672,7 +1674,7 @@ describe("folded lines", function()
end
-- relax the maximum fdc thus fdc should expand to
- -- accomodate the current number of folds
+ -- accommodate the current number of folds
command("set foldcolumn=auto:4")
if multigrid then
screen:expect([[
@@ -1755,6 +1757,67 @@ describe("folded lines", function()
end
assert_alive()
end)
+
+ it('work correctly with :move #18668', function()
+ screen:try_resize(45, 12)
+ source([[
+ set foldmethod=expr foldexpr=indent(v:lnum)
+ let content = ['', '', 'Line1', ' Line2', ' Line3',
+ \ 'Line4', ' Line5', ' Line6',
+ \ 'Line7', ' Line8', ' Line9']
+ call append(0, content)
+ normal! zM
+ call cursor(4, 1)
+ move 2
+ move 1
+ ]])
+ if multigrid then
+ screen:expect([[
+ ## grid 1
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [3:---------------------------------------------]|
+ ## grid 2
+ |
+ {5:^+-- 2 lines: Line2··························}|
+ |
+ Line1 |
+ Line4 |
+ {5:+-- 2 lines: Line5··························}|
+ Line7 |
+ {5:+-- 2 lines: Line8··························}|
+ |
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ |
+ ]])
+ else
+ screen:expect([[
+ |
+ {5:^+-- 2 lines: Line2··························}|
+ |
+ Line1 |
+ Line4 |
+ {5:+-- 2 lines: Line5··························}|
+ Line7 |
+ {5:+-- 2 lines: Line8··························}|
+ |
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ end
+ end)
end
describe("with ext_multigrid", function()
diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua
index 255180bba8..946129b082 100644
--- a/test/functional/ui/highlight_spec.lua
+++ b/test/functional/ui/highlight_spec.lua
@@ -2,7 +2,7 @@ local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local os = require('os')
local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert
-local command = helpers.command
+local command, exec = helpers.command, helpers.exec
local eval, exc_exec = helpers.eval, helpers.exc_exec
local feed_command, eq = helpers.feed_command, helpers.eq
local curbufmeths = helpers.curbufmeths
@@ -104,12 +104,12 @@ describe('highlight defaults', function()
})
feed_command('sp', 'vsp', 'vsp')
screen:expect([[
- ^ {2:│} {2:│} |
- {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
- {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
- {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
- {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
- {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
+ ^ │ │ |
+ {0:~ }│{0:~ }│{0:~ }|
+ {0:~ }│{0:~ }│{0:~ }|
+ {0:~ }│{0:~ }│{0:~ }|
+ {0:~ }│{0:~ }│{0:~ }|
+ {0:~ }│{0:~ }│{0:~ }|
{1:[No Name] }{2:[No Name] [No Name] }|
|
{0:~ }|
@@ -122,12 +122,12 @@ describe('highlight defaults', function()
-- navigate to verify that the attributes are properly moved
feed('<c-w>j')
screen:expect([[
- {2:│} {2:│} |
- {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
- {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
- {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
- {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
- {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
+ │ │ |
+ {0:~ }│{0:~ }│{0:~ }|
+ {0:~ }│{0:~ }│{0:~ }|
+ {0:~ }│{0:~ }│{0:~ }|
+ {0:~ }│{0:~ }│{0:~ }|
+ {0:~ }│{0:~ }│{0:~ }|
{2:[No Name] [No Name] [No Name] }|
^ |
{0:~ }|
@@ -142,12 +142,12 @@ describe('highlight defaults', function()
-- (upstream vim has the same behavior)
feed('<c-w>k<c-w>l')
screen:expect([[
- {2:│}^ {2:│} |
- {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
- {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
- {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
- {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
- {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
+ │^ │ |
+ {0:~ }│{0:~ }│{0:~ }|
+ {0:~ }│{0:~ }│{0:~ }|
+ {0:~ }│{0:~ }│{0:~ }|
+ {0:~ }│{0:~ }│{0:~ }|
+ {0:~ }│{0:~ }│{0:~ }|
{2:[No Name] }{1:[No Name] }{2:[No Name] }|
|
{0:~ }|
@@ -159,12 +159,12 @@ describe('highlight defaults', function()
]])
feed('<c-w>l')
screen:expect([[
- {2:│} {2:│}^ |
- {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
- {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
- {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
- {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
- {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
+ │ │^ |
+ {0:~ }│{0:~ }│{0:~ }|
+ {0:~ }│{0:~ }│{0:~ }|
+ {0:~ }│{0:~ }│{0:~ }|
+ {0:~ }│{0:~ }│{0:~ }|
+ {0:~ }│{0:~ }│{0:~ }|
{2:[No Name] [No Name] }{1:[No Name] }|
|
{0:~ }|
@@ -176,12 +176,12 @@ describe('highlight defaults', function()
]])
feed('<c-w>h<c-w>h')
screen:expect([[
- ^ {2:│} {2:│} |
- {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
- {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
- {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
- {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
- {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
+ ^ │ │ |
+ {0:~ }│{0:~ }│{0:~ }|
+ {0:~ }│{0:~ }│{0:~ }|
+ {0:~ }│{0:~ }│{0:~ }|
+ {0:~ }│{0:~ }│{0:~ }|
+ {0:~ }│{0:~ }│{0:~ }|
{1:[No Name] }{2:[No Name] [No Name] }|
|
{0:~ }|
@@ -840,7 +840,7 @@ describe("'listchars' highlight", function()
end)
end)
-describe('CursorLine highlight', function()
+describe('CursorLine and CursorLineNr highlights', function()
before_each(clear)
it('overridden by Error, ColorColumn if fg not set', function()
@@ -938,17 +938,18 @@ describe('CursorLine highlight', function()
[2] = {foreground = Screen.colors.Yellow};
[3] = {foreground = Screen.colors.Red, background = Screen.colors.Green};
[4] = {foreground = Screen.colors.Green, background = Screen.colors.Red};
+ [5] = {bold = true}, -- ModeMsg
})
screen:attach()
- feed_command('set wrap cursorline cursorlineopt=screenline')
- feed_command('set showbreak=>>>')
- feed_command('highlight clear NonText')
- feed_command('highlight clear CursorLine')
- feed_command('highlight NonText guifg=Yellow gui=NONE')
- feed_command('highlight LineNr guifg=Red guibg=Green gui=NONE')
- feed_command('highlight CursorLine guifg=Black guibg=White gui=NONE')
- feed_command('highlight CursorLineNr guifg=Green guibg=Red gui=NONE')
+ command('set wrap cursorline cursorlineopt=screenline')
+ command('set showbreak=>>>')
+ command('highlight clear NonText')
+ command('highlight clear CursorLine')
+ command('highlight NonText guifg=Yellow gui=NONE')
+ command('highlight LineNr guifg=Red guibg=Green gui=NONE')
+ command('highlight CursorLine guifg=Black guibg=White gui=NONE')
+ command('highlight CursorLineNr guifg=Green guibg=Red gui=NONE')
feed('30iø<esc>o<esc>30ia<esc>')
@@ -978,7 +979,7 @@ describe('CursorLine highlight', function()
]])
-- CursorLineNr should not apply to line number when 'cursorlineopt' does not contain "number"
- feed_command('set relativenumber numberwidth=2')
+ command('set relativenumber numberwidth=2')
screen:expect([[
{3:0 }{1:øøøøøøøøøøøø^øøøøøø}|
{3: }{2:>>>}øøøøøøøøøøøø |
@@ -988,7 +989,7 @@ describe('CursorLine highlight', function()
]])
-- CursorLineNr should apply to line number when 'cursorlineopt' contains "number"
- feed_command('set cursorlineopt+=number')
+ command('set cursorlineopt+=number')
screen:expect([[
{4:0 }{1:øøøøøøøøøøøø^øøøøøø}|
{3: }{2:>>>}øøøøøøøøøøøø |
@@ -1020,6 +1021,44 @@ describe('CursorLine highlight', function()
{3: }{2:>>>}{1:aaaaaaaaa^aaa }|
|
]])
+
+ -- updated in Insert mode
+ feed('I')
+ screen:expect([[
+ {3:1 }øøøøøøøøøøøøøøøøøø|
+ {3: }{2:>>>}øøøøøøøøøøøø |
+ {4:0 }{1:^aaaaaaaaaaaaaaaaaa}|
+ {3: }{2:>>>}aaaaaaaaaaaa |
+ {5:-- INSERT --} |
+ ]])
+
+ feed('<Esc>gg')
+ screen:expect([[
+ {4:0 }{1:^øøøøøøøøøøøøøøøøøø}|
+ {3: }{2:>>>}øøøøøøøøøøøø |
+ {3:1 }aaaaaaaaaaaaaaaaaa|
+ {3: }{2:>>>}aaaaaaaaaaaa |
+ |
+ ]])
+
+ command('inoremap <F2> <Cmd>call cursor(1, 1)<CR>')
+ feed('A')
+ screen:expect([[
+ {4:0 }øøøøøøøøøøøøøøøøøø|
+ {3: }{2:>>>}{1:øøøøøøøøøøøø^ }|
+ {3:1 }aaaaaaaaaaaaaaaaaa|
+ {3: }{2:>>>}aaaaaaaaaaaa |
+ {5:-- INSERT --} |
+ ]])
+
+ feed('<F2>')
+ screen:expect([[
+ {4:0 }{1:^øøøøøøøøøøøøøøøøøø}|
+ {3: }{2:>>>}øøøøøøøøøøøø |
+ {3:1 }aaaaaaaaaaaaaaaaaa|
+ {3: }{2:>>>}aaaaaaaaaaaa |
+ {5:-- INSERT --} |
+ ]])
end)
it('always updated. vim-patch:8.1.0849', function()
@@ -1082,7 +1121,47 @@ describe('CursorLine highlight', function()
]])
end)
- it('with split-windows in diff-mode', function()
+ it('is updated if cursor is moved up from timer vim-patch:8.2.4591', function()
+ local screen = Screen.new(50, 8)
+ screen:set_default_attr_ids({
+ [1] = {background = Screen.colors.Gray90}, -- CursorLine
+ [2] = {bold = true, foreground = Screen.colors.Blue1}, -- NonText
+ })
+ screen:attach()
+ exec([[
+ call setline(1, ['aaaaa', 'bbbbb', 'ccccc', 'ddddd'])
+ set cursorline
+ call cursor(4, 1)
+
+ func Func(timer)
+ call cursor(2, 1)
+ endfunc
+
+ call timer_start(300, 'Func')
+ ]])
+ screen:expect({grid = [[
+ aaaaa |
+ bbbbb |
+ ccccc |
+ {1:^ddddd }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ |
+ ]], timeout = 100})
+ screen:expect({grid = [[
+ aaaaa |
+ {1:^bbbbb }|
+ ccccc |
+ ddddd |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ |
+ ]]})
+ end)
+
+ it('with split windows in diff mode', function()
local screen = Screen.new(50,12)
screen:set_default_attr_ids({
[1] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.WebGray},
@@ -1094,7 +1173,6 @@ describe('CursorLine highlight', function()
[7] = {background = Screen.colors.Red, foreground = Screen.colors.White},
[8] = {bold = true, foreground = Screen.colors.Blue1},
[9] = {bold = true, reverse = true},
- [10] = {bold = true},
})
screen:attach()
@@ -1108,31 +1186,31 @@ describe('CursorLine highlight', function()
feed('<esc>gg')
command('windo diffthis')
screen:expect([[
- {1: }{7:line 1 some text }{4:│}{1: }{7:^line 1 some text }|
- {1: }{3:line 2 mo}{2:Re text!}{3: }{4:│}{1: }{3:line 2 mo}{2:re text}{3: }|
- {1: }{5:extra line! }{4:│}{1: }{6:----------------------}|
- {1: }extra line! {4:│}{1: }extra line! |
- {1: }extra line! {4:│}{1: }extra line! |
- {1: }last line ... {4:│}{1: }last line ... |
- {1: } {4:│}{1: } |
- {8:~ }{4:│}{8:~ }|
- {8:~ }{4:│}{8:~ }|
- {8:~ }{4:│}{8:~ }|
+ {1: }{7:line 1 some text }│{1: }{7:^line 1 some text }|
+ {1: }{3:line 2 mo}{2:Re text!}{3: }│{1: }{3:line 2 mo}{2:re text}{3: }|
+ {1: }{5:extra line! }│{1: }{6:----------------------}|
+ {1: }extra line! │{1: }extra line! |
+ {1: }extra line! │{1: }extra line! |
+ {1: }last line ... │{1: }last line ... |
+ {1: } │{1: } |
+ {8:~ }│{8:~ }|
+ {8:~ }│{8:~ }|
+ {8:~ }│{8:~ }|
{4:[No Name] [+] }{9:[No Name] [+] }|
|
]])
feed('jjjjj')
screen:expect([[
- {1: }line 1 some text {4:│}{1: }line 1 some text |
- {1: }{3:line 2 mo}{2:Re text!}{3: }{4:│}{1: }{3:line 2 mo}{2:re text}{3: }|
- {1: }{5:extra line! }{4:│}{1: }{6:----------------------}|
- {1: }extra line! {4:│}{1: }extra line! |
- {1: }extra line! {4:│}{1: }extra line! |
- {1: }last line ... {4:│}{1: }last line ... |
- {1: }{7: }{4:│}{1: }{7:^ }|
- {8:~ }{4:│}{8:~ }|
- {8:~ }{4:│}{8:~ }|
- {8:~ }{4:│}{8:~ }|
+ {1: }line 1 some text │{1: }line 1 some text |
+ {1: }{3:line 2 mo}{2:Re text!}{3: }│{1: }{3:line 2 mo}{2:re text}{3: }|
+ {1: }{5:extra line! }│{1: }{6:----------------------}|
+ {1: }extra line! │{1: }extra line! |
+ {1: }extra line! │{1: }extra line! |
+ {1: }last line ... │{1: }last line ... |
+ {1: }{7: }│{1: }{7:^ }|
+ {8:~ }│{8:~ }|
+ {8:~ }│{8:~ }|
+ {8:~ }│{8:~ }|
{4:[No Name] [+] }{9:[No Name] [+] }|
|
]])
@@ -1142,16 +1220,16 @@ describe('CursorLine highlight', function()
command('hi CursorLine ctermbg=red ctermfg=NONE guibg=red guifg=NONE')
feed('kkkk')
screen:expect([[
- {1: }line 1 some text {4:│}{1: }line 1 some text |
- {1: }{11:line 2 mo}{12:Re text!}{11: }{4:│}{1: }{11:^line 2 mo}{12:re text}{11: }|
- {1: }{5:extra line! }{4:│}{1: }{6:----------------------}|
- {1: }extra line! {4:│}{1: }extra line! |
- {1: }extra line! {4:│}{1: }extra line! |
- {1: }last line ... {4:│}{1: }last line ... |
- {1: } {4:│}{1: } |
- {8:~ }{4:│}{8:~ }|
- {8:~ }{4:│}{8:~ }|
- {8:~ }{4:│}{8:~ }|
+ {1: }line 1 some text │{1: }line 1 some text |
+ {1: }{11:line 2 mo}{12:Re text!}{11: }│{1: }{11:^line 2 mo}{12:re text}{11: }|
+ {1: }{5:extra line! }│{1: }{6:----------------------}|
+ {1: }extra line! │{1: }extra line! |
+ {1: }extra line! │{1: }extra line! |
+ {1: }last line ... │{1: }last line ... |
+ {1: } │{1: } |
+ {8:~ }│{8:~ }|
+ {8:~ }│{8:~ }|
+ {8:~ }│{8:~ }|
{4:[No Name] [+] }{9:[No Name] [+] }|
|
]], {
@@ -1171,8 +1249,261 @@ describe('CursorLine highlight', function()
background = Screen.colors.Red},
})
end)
+
+ it('CursorLineNr shows correctly just below filler lines', function()
+ local screen = Screen.new(50,12)
+ screen:set_default_attr_ids({
+ [1] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.WebGray},
+ [2] = {background = Screen.colors.LightCyan1, bold = true, foreground = Screen.colors.Blue1},
+ [3] = {reverse = true},
+ [4] = {background = Screen.colors.LightBlue},
+ [5] = {background = Screen.colors.Red, foreground = Screen.colors.White},
+ [6] = {background = Screen.colors.White, bold = true, foreground = Screen.colors.Black},
+ [7] = {bold = true, foreground = Screen.colors.Blue1},
+ [8] = {bold = true, reverse = true},
+ [9] = {foreground = Screen.colors.Brown},
+ })
+ screen:attach()
+
+ command('hi CursorLine guibg=red guifg=white')
+ command('hi CursorLineNr guibg=white guifg=black gui=bold')
+ command('set cursorline number')
+ command('call setline(1, ["baz", "foo", "foo", "bar"])')
+ feed('2gg0')
+ command('vnew')
+ command('call setline(1, ["foo", "foo", "bar"])')
+ command('windo diffthis')
+ command('1wincmd w')
+ screen:expect([[
+ {1: }{9: }{2:-------------------}│{1: }{9: 1 }{4:baz }|
+ {1: }{6: 1 }{5:^foo }│{1: }{6: 2 }{5:foo }|
+ {1: }{9: 2 }foo │{1: }{9: 3 }foo |
+ {1: }{9: 3 }bar │{1: }{9: 4 }bar |
+ {7:~ }│{7:~ }|
+ {7:~ }│{7:~ }|
+ {7:~ }│{7:~ }|
+ {7:~ }│{7:~ }|
+ {7:~ }│{7:~ }|
+ {7:~ }│{7:~ }|
+ {8:[No Name] [+] }{3:[No Name] [+] }|
+ |
+ ]])
+ command('set cursorlineopt=number')
+ screen:expect([[
+ {1: }{9: }{2:-------------------}│{1: }{9: 1 }{4:baz }|
+ {1: }{6: 1 }^foo │{1: }{6: 2 }{5:foo }|
+ {1: }{9: 2 }foo │{1: }{9: 3 }foo |
+ {1: }{9: 3 }bar │{1: }{9: 4 }bar |
+ {7:~ }│{7:~ }|
+ {7:~ }│{7:~ }|
+ {7:~ }│{7:~ }|
+ {7:~ }│{7:~ }|
+ {7:~ }│{7:~ }|
+ {7:~ }│{7:~ }|
+ {8:[No Name] [+] }{3:[No Name] [+] }|
+ |
+ ]])
+ end)
+end)
+
+describe('CursorColumn highlight', function()
+ local screen
+ before_each(function()
+ clear()
+ screen = Screen.new(50, 8)
+ screen:set_default_attr_ids({
+ [1] = {background = Screen.colors.Gray90}, -- CursorColumn
+ [2] = {bold = true, foreground = Screen.colors.Blue1}, -- NonText
+ [3] = {bold = true}, -- ModeMsg
+ })
+ screen:attach()
+ end)
+
+ it('is updated when pressing "i" on a TAB character', function()
+ exec([[
+ call setline(1, ['123456789', "a\tb"])
+ set cursorcolumn
+ call cursor(2, 2)
+ ]])
+ screen:expect([[
+ 1234567{1:8}9 |
+ a ^ b |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ |
+ ]])
+ feed('i')
+ screen:expect([[
+ 1{1:2}3456789 |
+ a^ b |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {3:-- INSERT --} |
+ ]])
+ feed('<C-O>')
+ screen:expect([[
+ 1234567{1:8}9 |
+ a ^ b |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {3:-- (insert) --} |
+ ]])
+ feed('i')
+ screen:expect([[
+ 1{1:2}3456789 |
+ a^ b |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {3:-- INSERT --} |
+ ]])
+ end)
+
+ it('is updated if cursor is moved from timer', function()
+ exec([[
+ call setline(1, ['aaaaa', 'bbbbb', 'ccccc', 'ddddd'])
+ set cursorcolumn
+ call cursor(4, 5)
+
+ func Func(timer)
+ call cursor(1, 1)
+ endfunc
+
+ call timer_start(300, 'Func')
+ ]])
+ screen:expect({grid = [[
+ aaaa{1:a} |
+ bbbb{1:b} |
+ cccc{1:c} |
+ dddd^d |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ |
+ ]], timeout = 100})
+ screen:expect({grid = [[
+ ^aaaaa |
+ {1:b}bbbb |
+ {1:c}cccc |
+ {1:d}dddd |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ |
+ ]]})
+ end)
end)
+describe('ColorColumn highlight', function()
+ local screen
+
+ before_each(function()
+ clear()
+ screen = Screen.new(40, 15)
+ Screen:set_default_attr_ids({
+ [1] = {background = Screen.colors.LightRed}, -- ColorColumn
+ [2] = {background = Screen.colors.Grey90}, -- CursorLine
+ [3] = {foreground = Screen.colors.Brown}, -- LineNr
+ [4] = {foreground = Screen.colors.Brown, bold = true}, -- CursorLineNr
+ [5] = {foreground = Screen.colors.Blue, bold = true}, -- NonText
+ -- NonText and ColorColumn
+ [6] = {foreground = Screen.colors.Blue, background = Screen.colors.LightRed, bold = true},
+ [7] = {reverse = true, bold = true}, -- StatusLine
+ [8] = {reverse = true}, -- StatusLineNC
+ })
+ screen:attach()
+ end)
+
+ it('when entering a buffer vim-patch:8.1.2073', function()
+ exec([[
+ set nohidden
+ split
+ edit X
+ call setline(1, ["1111111111","22222222222","3333333333"])
+ set nomodified
+ set colorcolumn=3,9
+ set number cursorline cursorlineopt=number
+ wincmd w
+ buf X
+ ]])
+ screen:expect([[
+ {4: 1 }11{1:1}11111{1:1}1 |
+ {3: 2 }22{1:2}22222{1:2}22 |
+ {3: 3 }33{1:3}33333{1:3}3 |
+ {5:~ }|
+ {5:~ }|
+ {5:~ }|
+ {8:X }|
+ {4: 1 }^11{1:1}11111{1:1}1 |
+ {3: 2 }22{1:2}22222{1:2}22 |
+ {3: 3 }33{1:3}33333{1:3}3 |
+ {5:~ }|
+ {5:~ }|
+ {5:~ }|
+ {7:X }|
+ |
+ ]])
+ end)
+
+ it("in 'breakindent' vim-patch:8.2.1689", function()
+ exec([[
+ call setline(1, 'The quick brown fox jumped over the lazy dogs')
+ set co=40 linebreak bri briopt=shift:2 cc=40,41,43
+ ]])
+ screen:expect([[
+ ^The quick brown fox jumped over the {1: }|
+ {1: } {1:l}azy dogs |
+ {5:~ }|
+ {5:~ }|
+ {5:~ }|
+ {5:~ }|
+ {5:~ }|
+ {5:~ }|
+ {5:~ }|
+ {5:~ }|
+ {5:~ }|
+ {5:~ }|
+ {5:~ }|
+ {5:~ }|
+ |
+ ]])
+ end)
+
+ it("in 'showbreak' vim-patch:8.2.1689", function()
+ exec([[
+ call setline(1, 'The quick brown fox jumped over the lazy dogs')
+ set co=40 showbreak=+++>\\ cc=40,41,43
+ ]])
+ screen:expect([[
+ ^The quick brown fox jumped over the laz{1:y}|
+ {6:+}{5:+}{6:+}{5:>\} dogs |
+ {5:~ }|
+ {5:~ }|
+ {5:~ }|
+ {5:~ }|
+ {5:~ }|
+ {5:~ }|
+ {5:~ }|
+ {5:~ }|
+ {5:~ }|
+ {5:~ }|
+ {5:~ }|
+ {5:~ }|
+ |
+ ]])
+ end)
+end)
describe("MsgSeparator highlight and msgsep fillchar", function()
local screen
@@ -1378,6 +1709,46 @@ describe("'number' and 'relativenumber' highlight", function()
|
]])
end)
+
+ it('relative number highlight is updated if cursor is moved from timer', function()
+ local screen = Screen.new(50, 8)
+ screen:set_default_attr_ids({
+ [1] = {foreground = Screen.colors.Brown}, -- LineNr
+ [2] = {bold = true, foreground = Screen.colors.Blue1}, -- NonText
+ })
+ screen:attach()
+ exec([[
+ call setline(1, ['aaaaa', 'bbbbb', 'ccccc', 'ddddd'])
+ set relativenumber
+ call cursor(4, 1)
+
+ func Func(timer)
+ call cursor(1, 1)
+ endfunc
+
+ call timer_start(300, 'Func')
+ ]])
+ screen:expect({grid = [[
+ {1: 3 }aaaaa |
+ {1: 2 }bbbbb |
+ {1: 1 }ccccc |
+ {1: 0 }^ddddd |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ |
+ ]], timeout = 100})
+ screen:expect({grid = [[
+ {1: 0 }^aaaaa |
+ {1: 1 }bbbbb |
+ {1: 2 }ccccc |
+ {1: 3 }ddddd |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ |
+ ]]})
+ end)
end)
describe("'winhighlight' highlight", function()
@@ -1478,8 +1849,7 @@ describe("'winhighlight' highlight", function()
]], unchanged=true}
end)
-
- it('works local to the buffer', function()
+ it('works local to the window', function()
insert("aa")
command("split")
command("setlocal winhl=Normal:Background1")
@@ -1870,4 +2240,35 @@ describe("'winhighlight' highlight", function()
|
]]}
end)
+
+ it('can override StatusLine and StatusLineNC', function()
+ command('set winhighlight=StatusLine:Background1,StatusLineNC:Background2')
+ command('split')
+ screen:expect([[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {1:[No Name] }|
+ |
+ {0:~ }|
+ {5:[No Name] }|
+ |
+ ]])
+ end)
+
+ it('can override WinBar and WinBarNC #19345', function()
+ command('setlocal winbar=foobar')
+ command('set winhighlight=WinBar:Background1,WinBarNC:Background2')
+ command('split')
+ screen:expect([[
+ {1:foobar }|
+ ^ |
+ {0:~ }|
+ {3:[No Name] }|
+ {5:foobar }|
+ |
+ {4:[No Name] }|
+ |
+ ]])
+ end)
end)
diff --git a/test/functional/ui/hlstate_spec.lua b/test/functional/ui/hlstate_spec.lua
index 2a567b28ee..df7f34aa7f 100644
--- a/test/functional/ui/hlstate_spec.lua
+++ b/test/functional/ui/hlstate_spec.lua
@@ -5,7 +5,7 @@ local clear, insert = helpers.clear, helpers.insert
local command = helpers.command
local meths = helpers.meths
local iswin = helpers.iswin
-local nvim_dir = helpers.nvim_dir
+local testprg = helpers.testprg
local thelpers = require('test.functional.terminal.helpers')
describe('ext_hlstate detailed highlights', function()
@@ -14,6 +14,7 @@ describe('ext_hlstate detailed highlights', function()
before_each(function()
clear()
command('syntax on')
+ command('hi VertSplit gui=reverse')
screen = Screen.new(40, 8)
screen:attach({ext_hlstate=true})
end)
@@ -59,7 +60,7 @@ describe('ext_hlstate detailed highlights', function()
it('work with cleared UI highlights', function()
screen:set_default_attr_ids({
- [1] = {{}, {{hi_name = "VertSplit", ui_name = "VertSplit", kind = "ui"}}},
+ [1] = {{}, {{hi_name = "Normal", ui_name = "WinSeparator", kind = "ui"}}},
[2] = {{bold = true, foreground = Screen.colors.Blue1},
{{hi_name = "NonText", ui_name = "EndOfBuffer", kind = "ui"}}},
[3] = {{bold = true, reverse = true},
@@ -179,6 +180,8 @@ describe('ext_hlstate detailed highlights', function()
end)
it("work with :terminal", function()
+ if helpers.pending_win32(pending) then return end
+
screen:set_default_attr_ids({
[1] = {{}, {{hi_name = "TermCursorNC", ui_name = "TermCursorNC", kind = "ui"}}},
[2] = {{foreground = tonumber('0x00ccff'), fg_indexed=true}, {{kind = "term"}}},
@@ -188,7 +191,7 @@ describe('ext_hlstate detailed highlights', function()
[6] = {{foreground = tonumber('0x40ffff'), fg_indexed=true}, {5, 1}},
[7] = {{}, {{hi_name = "MsgArea", ui_name = "MsgArea", kind = "ui"}}},
})
- command('enew | call termopen(["'..nvim_dir..'/tty-test"])')
+ command(("enew | call termopen(['%s'])"):format(testprg('tty-test')))
screen:expect([[
^tty ready |
{1: } |
diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua
index b6e2f2311f..d8dd546a8d 100644
--- a/test/functional/ui/inccommand_spec.lua
+++ b/test/functional/ui/inccommand_spec.lua
@@ -17,7 +17,7 @@ local source = helpers.source
local poke_eventloop = helpers.poke_eventloop
local nvim = helpers.nvim
local sleep = helpers.sleep
-local nvim_dir = helpers.nvim_dir
+local testprg = helpers.testprg
local assert_alive = helpers.assert_alive
local default_text = [[
@@ -123,13 +123,11 @@ describe(":substitute, inccommand=split interactivity", function()
it("no preview if invoked by feedkeys()", function()
-- in a script...
source([[:call feedkeys(":%s/tw/MO/g\<CR>")]])
- poke_eventloop()
-- or interactively...
- feed([[:call feedkeys(":%s/tw/MO/g\<CR>")<CR>]])
- poke_eventloop()
+ feed([[:call feedkeys(":%s/bs/BUU/g\<lt>CR>")<CR>]])
eq(1, eval("bufnr('$')"))
-- sanity check: assert the buffer state
- expect(default_text:gsub("tw", "MO"))
+ expect(default_text:gsub("tw", "MO"):gsub("bs", "BUU"))
end)
end)
@@ -257,42 +255,70 @@ describe(":substitute, 'inccommand' preserves", function()
end)
end
- for _, case in pairs{"", "split", "nosplit"} do
- it("visual selection for non-previewable command (inccommand="..case..") #5888", function()
+ for _, case in ipairs({'', 'split', 'nosplit'}) do
+ it('previous substitute string ~ (inccommand='..case..') #12109', function()
local screen = Screen.new(30,10)
common_setup(screen, case, default_text)
- feed('1G2V')
- feed(':s')
- screen:expect([[
- {vis:Inc substitution on} |
- t{vis:wo lines} |
- |
- {15:~ }|
- {15:~ }|
- {15:~ }|
- {15:~ }|
- {15:~ }|
- {15:~ }|
- :'<,'>s^ |
- ]])
+ feed(':%s/Inc/SUB<CR>')
+ expect([[
+ SUB substitution on
+ two lines
+ ]])
- feed('o')
- screen:expect([[
- {vis:Inc substitution on} |
- t{vis:wo lines} |
- |
- {15:~ }|
- {15:~ }|
- {15:~ }|
- {15:~ }|
- {15:~ }|
- {15:~ }|
- :'<,'>so^ |
- ]])
+ feed(':%s/line/')
+ poke_eventloop()
+ feed('~')
+ poke_eventloop()
+ feed('<CR>')
+ expect([[
+ SUB substitution on
+ two SUBs
+ ]])
+
+ feed(':%s/sti/')
+ poke_eventloop()
+ feed('~')
+ poke_eventloop()
+ feed('B')
+ poke_eventloop()
+ feed('<CR>')
+ expect([[
+ SUB subSUBBtution on
+ two SUBs
+ ]])
+
+ feed(':%s/ion/NEW<CR>')
+ expect([[
+ SUB subSUBBtutNEW on
+ two SUBs
+ ]])
+
+ feed(':%s/two/')
+ poke_eventloop()
+ feed('N')
+ poke_eventloop()
+ feed('~')
+ poke_eventloop()
+ feed('<CR>')
+ expect([[
+ SUB subSUBBtutNEW on
+ NNEW SUBs
+ ]])
+
+ feed(':%s/bS/')
+ poke_eventloop()
+ feed('~')
+ poke_eventloop()
+ feed('W')
+ poke_eventloop()
+ feed('<CR>')
+ expect([[
+ SUB suNNEWWUBBtutNEW on
+ NNEW SUBs
+ ]])
end)
end
-
end)
describe(":substitute, 'inccommand' preserves undo", function()
@@ -317,7 +343,7 @@ describe(":substitute, 'inccommand' preserves undo", function()
}
local function test_sub(substring, split, redoable)
- clear()
+ command('bwipe!')
feed_command("set inccommand=" .. split)
insert("1")
@@ -343,7 +369,7 @@ describe(":substitute, 'inccommand' preserves undo", function()
end
local function test_notsub(substring, split, redoable)
- clear()
+ command('bwipe!')
feed_command("set inccommand=" .. split)
insert("1")
@@ -377,7 +403,7 @@ describe(":substitute, 'inccommand' preserves undo", function()
local function test_threetree(substring, split)
- clear()
+ command('bwipe!')
feed_command("set inccommand=" .. split)
insert("1")
@@ -429,6 +455,8 @@ describe(":substitute, 'inccommand' preserves undo", function()
2]])
end
+ before_each(clear)
+
it("at a non-leaf of the undo tree", function()
for _, case in pairs(cases) do
for _, str in pairs(substrings) do
@@ -1279,6 +1307,108 @@ describe(":substitute, inccommand=split", function()
]])
end)
+ it([[preview changes correctly with c_CTRL-R_= and c_CTRL-\_e]], function()
+ feed('gg')
+ feed(":1,2s/t/X")
+ screen:expect([[
+ Inc subs{12:X}itution on |
+ {12:X}wo lines |
+ Inc substitution on |
+ two lines |
+ |
+ {11:[No Name] [+] }|
+ |1| Inc subs{12:X}itution on |
+ |2| {12:X}wo lines |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {10:[Preview] }|
+ :1,2s/t/X^ |
+ ]])
+
+ feed([[<C-R>='Y']])
+ -- preview should be unchanged during c_CTRL-R_= editing
+ screen:expect([[
+ Inc subs{12:X}itution on |
+ {12:X}wo lines |
+ Inc substitution on |
+ two lines |
+ |
+ {11:[No Name] [+] }|
+ |1| Inc subs{12:X}itution on |
+ |2| {12:X}wo lines |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {10:[Preview] }|
+ ={1:'Y'}^ |
+ ]])
+
+ feed('<CR>')
+ -- preview should be changed by the result of the expression
+ screen:expect([[
+ Inc subs{12:XY}itution on |
+ {12:XY}wo lines |
+ Inc substitution on |
+ two lines |
+ |
+ {11:[No Name] [+] }|
+ |1| Inc subs{12:XY}itution on |
+ |2| {12:XY}wo lines |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {10:[Preview] }|
+ :1,2s/t/XY^ |
+ ]])
+
+ feed([[<C-\>e'echo']])
+ -- preview should be unchanged during c_CTRL-\_e editing
+ screen:expect([[
+ Inc subs{12:XY}itution on |
+ {12:XY}wo lines |
+ Inc substitution on |
+ two lines |
+ |
+ {11:[No Name] [+] }|
+ |1| Inc subs{12:XY}itution on |
+ |2| {12:XY}wo lines |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {10:[Preview] }|
+ ={1:'echo'}^ |
+ ]])
+
+ feed('<CR>')
+ -- preview should be cleared if command is changed to a non-previewable one
+ screen:expect([[
+ Inc substitution on |
+ two lines |
+ Inc substitution on |
+ two lines |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ :echo^ |
+ ]])
+ end)
+
end)
describe("inccommand=nosplit", function()
@@ -1582,10 +1712,12 @@ end)
describe("'inccommand' and :cnoremap", function()
local cases = { "", "split", "nosplit" }
+ local screen
- local function refresh(case)
+ local function refresh(case, visual)
clear()
- common_setup(nil, case, default_text)
+ screen = visual and Screen.new(50,10) or nil
+ common_setup(screen, case, default_text)
end
it('work with remapped characters', function()
@@ -1642,13 +1774,15 @@ describe("'inccommand' and :cnoremap", function()
it('still works with a broken mapping', function()
for _, case in pairs(cases) do
- refresh(case)
+ refresh(case, true)
feed_command("cnoremap <expr> x execute('bwipeout!')[-1].'x'")
feed(":%s/tw/tox<enter>")
+ screen:expect{any=[[{14:^E565:]]}
+ feed('<c-c>')
-- error thrown b/c of the mapping
- neq(nil, eval('v:errmsg'):find('^E523:'))
+ neq(nil, eval('v:errmsg'):find('^E565:'))
expect([[
Inc substitution on
toxo lines
@@ -1794,26 +1928,26 @@ describe("'inccommand' split windows", function()
feed_command("split")
feed(":%s/tw")
screen:expect([[
- Inc substitution on {10:│}Inc substitution on|
- {12:tw}o lines {10:│}{12:tw}o lines |
- {10:│} |
- {15:~ }{10:│}{15:~ }|
- {15:~ }{10:│}{15:~ }|
- {15:~ }{10:│}{15:~ }|
- {15:~ }{10:│}{15:~ }|
- {15:~ }{10:│}{15:~ }|
- {15:~ }{10:│}{15:~ }|
- {15:~ }{10:│}{15:~ }|
- {15:~ }{10:│}{15:~ }|
- {15:~ }{10:│}{15:~ }|
- {15:~ }{10:│}{15:~ }|
- {15:~ }{10:│}{15:~ }|
- {11:[No Name] [+] }{10:│}{15:~ }|
- Inc substitution on {10:│}{15:~ }|
- {12:tw}o lines {10:│}{15:~ }|
- {10:│}{15:~ }|
- {15:~ }{10:│}{15:~ }|
- {15:~ }{10:│}{15:~ }|
+ Inc substitution on │Inc substitution on|
+ {12:tw}o lines │{12:tw}o lines |
+ │ |
+ {15:~ }│{15:~ }|
+ {15:~ }│{15:~ }|
+ {15:~ }│{15:~ }|
+ {15:~ }│{15:~ }|
+ {15:~ }│{15:~ }|
+ {15:~ }│{15:~ }|
+ {15:~ }│{15:~ }|
+ {15:~ }│{15:~ }|
+ {15:~ }│{15:~ }|
+ {15:~ }│{15:~ }|
+ {15:~ }│{15:~ }|
+ {11:[No Name] [+] }│{15:~ }|
+ Inc substitution on │{15:~ }|
+ {12:tw}o lines │{15:~ }|
+ │{15:~ }|
+ {15:~ }│{15:~ }|
+ {15:~ }│{15:~ }|
{10:[No Name] [+] [No Name] [+] }|
|2| {12:tw}o lines |
{15:~ }|
@@ -1833,20 +1967,20 @@ describe("'inccommand' split windows", function()
feed(":%s/tw")
screen:expect([[
- Inc substitution on {10:│}Inc substitution on|
- {12:tw}o lines {10:│}{12:tw}o lines |
- {10:│} |
- {15:~ }{10:│}{15:~ }|
- {15:~ }{10:│}{15:~ }|
- {15:~ }{10:│}{15:~ }|
- {15:~ }{10:│}{15:~ }|
- {15:~ }{10:│}{15:~ }|
- {15:~ }{10:│}{15:~ }|
- {15:~ }{10:│}{15:~ }|
- {15:~ }{10:│}{15:~ }|
- {15:~ }{10:│}{15:~ }|
- {15:~ }{10:│}{15:~ }|
- {15:~ }{10:│}{15:~ }|
+ Inc substitution on │Inc substitution on|
+ {12:tw}o lines │{12:tw}o lines |
+ │ |
+ {15:~ }│{15:~ }|
+ {15:~ }│{15:~ }|
+ {15:~ }│{15:~ }|
+ {15:~ }│{15:~ }|
+ {15:~ }│{15:~ }|
+ {15:~ }│{15:~ }|
+ {15:~ }│{15:~ }|
+ {15:~ }│{15:~ }|
+ {15:~ }│{15:~ }|
+ {15:~ }│{15:~ }|
+ {15:~ }│{15:~ }|
{11:[No Name] [+] }{10:[No Name] [+] }|
Inc substitution on |
{12:tw}o lines |
@@ -1917,6 +2051,16 @@ describe("'inccommand' split windows", function()
end
end)
+ it("don't open if there's not enough room", function()
+ refresh()
+ screen:try_resize(40, 3)
+ feed("gg:%s/tw")
+ screen:expect([[
+ Inc substitution on |
+ {12:tw}o lines |
+ :%s/tw^ |
+ ]])
+ end)
end)
describe("'inccommand' with 'gdefault'", function()
@@ -2731,7 +2875,7 @@ it(':substitute with inccommand during :terminal activity', function()
clear()
command("set cmdwinheight=3")
- feed([[:terminal "]]..nvim_dir..[[/shell-test" REP 5000 xxx<cr>]])
+ feed(([[:terminal "%s" REP 5000 xxx<cr>]]):format(testprg('shell-test')))
command('file term')
feed('G') -- Follow :terminal output.
command('new')
@@ -2773,6 +2917,62 @@ it(':substitute with inccommand, timer-induced :redraw #9777', function()
]])
end)
+it(':substitute with inccommand, allows :redraw before first separator is typed #18857', function()
+ local screen = Screen.new(30,6)
+ clear()
+ common_setup(screen, 'split', 'foo bar baz\nbar baz fox\nbar foo baz')
+ command('hi! link NormalFloat CursorLine')
+ local float_buf = meths.create_buf(false, true)
+ meths.open_win(float_buf, false, {
+ relative = 'editor', height = 1, width = 5, row = 3, col = 0, focusable = false,
+ })
+ feed(':%s')
+ screen:expect([[
+ foo bar baz |
+ bar baz fox |
+ bar foo baz |
+ {16: }{15: }|
+ {15:~ }|
+ :%s^ |
+ ]])
+ meths.buf_set_lines(float_buf, 0, -1, true, {'foo'})
+ command('redraw')
+ screen:expect([[
+ foo bar baz |
+ bar baz fox |
+ bar foo baz |
+ {16:foo }{15: }|
+ {15:~ }|
+ :%s^ |
+ ]])
+end)
+
+it(':substitute with inccommand, does not crash if range contains invalid marks', function()
+ local screen = Screen.new(30, 6)
+ clear()
+ common_setup(screen, 'split', 'test')
+ feed([[:'a,'bs]])
+ screen:expect([[
+ test |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ :'a,'bs^ |
+ ]])
+ -- v:errmsg shouldn't be set either before the first separator is typed
+ eq('', eval('v:errmsg'))
+ feed('/')
+ screen:expect([[
+ test |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ :'a,'bs/^ |
+ ]])
+end)
+
it(":substitute doesn't crash with inccommand, if undo is empty #12932", function()
local screen = Screen.new(10,5)
clear()
@@ -2808,3 +3008,16 @@ it('long :%s/ with inccommand does not collapse cmdline', function()
AAAAAAA^ |
]])
end)
+
+it("with 'inccommand' typing :filter doesn't segfault or leak memory #19057", function()
+ clear()
+ common_setup(nil, 'nosplit')
+ feed(':filter s')
+ assert_alive()
+ feed(' ')
+ assert_alive()
+ feed('h')
+ assert_alive()
+ feed('i')
+ assert_alive()
+end)
diff --git a/test/functional/ui/inccommand_user_spec.lua b/test/functional/ui/inccommand_user_spec.lua
new file mode 100644
index 0000000000..b5816f6fe6
--- /dev/null
+++ b/test/functional/ui/inccommand_user_spec.lua
@@ -0,0 +1,356 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local clear = helpers.clear
+local exec_lua = helpers.exec_lua
+local insert = helpers.insert
+local feed = helpers.feed
+local command = helpers.command
+local assert_alive = helpers.assert_alive
+
+-- Implements a :Replace command that works like :substitute.
+local setup_replace_cmd = [[
+ local function show_replace_preview(buf, use_preview_win, preview_ns, preview_buf, matches)
+ -- Find the width taken by the largest line number, used for padding the line numbers
+ local highest_lnum = math.max(matches[#matches][1], 1)
+ local highest_lnum_width = math.floor(math.log10(highest_lnum))
+ local preview_buf_line = 0
+
+ vim.g.prevns = preview_ns
+ vim.g.prevbuf = preview_buf
+
+ for _, match in ipairs(matches) do
+ local lnum = match[1]
+ local line_matches = match[2]
+ local prefix
+
+ if use_preview_win then
+ prefix = string.format(
+ '|%s%d| ',
+ string.rep(' ', highest_lnum_width - math.floor(math.log10(lnum))),
+ lnum
+ )
+
+ vim.api.nvim_buf_set_lines(
+ preview_buf,
+ preview_buf_line,
+ preview_buf_line,
+ 0,
+ { prefix .. vim.api.nvim_buf_get_lines(buf, lnum - 1, lnum, false)[1] }
+ )
+ end
+
+ for _, line_match in ipairs(line_matches) do
+ vim.api.nvim_buf_add_highlight(
+ buf,
+ preview_ns,
+ 'Substitute',
+ lnum - 1,
+ line_match[1],
+ line_match[2]
+ )
+
+ if use_preview_win then
+ vim.api.nvim_buf_add_highlight(
+ preview_buf,
+ preview_ns,
+ 'Substitute',
+ preview_buf_line,
+ #prefix + line_match[1],
+ #prefix + line_match[2]
+ )
+ end
+ end
+
+ preview_buf_line = preview_buf_line + 1
+ end
+
+ if use_preview_win then
+ return 2
+ else
+ return 1
+ end
+ end
+
+ local function do_replace(opts, preview, preview_ns, preview_buf)
+ local pat1 = opts.fargs[1] or ''
+ local pat2 = opts.fargs[2] or ''
+ local line1 = opts.line1
+ local line2 = opts.line2
+
+ local buf = vim.api.nvim_get_current_buf()
+ local lines = vim.api.nvim_buf_get_lines(buf, line1 - 1, line2, 0)
+ local matches = {}
+
+ for i, line in ipairs(lines) do
+ local startidx, endidx = 0, 0
+ local line_matches = {}
+ local num = 1
+
+ while startidx ~= -1 do
+ local match = vim.fn.matchstrpos(line, pat1, 0, num)
+ startidx, endidx = match[2], match[3]
+
+ if startidx ~= -1 then
+ line_matches[#line_matches+1] = { startidx, endidx }
+ end
+
+ num = num + 1
+ end
+
+ if #line_matches > 0 then
+ matches[#matches+1] = { line1 + i - 1, line_matches }
+ end
+ end
+
+ local new_lines = {}
+
+ for _, match in ipairs(matches) do
+ local lnum = match[1]
+ local line_matches = match[2]
+ local line = lines[lnum - line1 + 1]
+ local pat_width_differences = {}
+
+ -- If previewing, only replace the text in current buffer if pat2 isn't empty
+ -- Otherwise, always replace the text
+ if pat2 ~= '' or not preview then
+ if preview then
+ for _, line_match in ipairs(line_matches) do
+ local startidx, endidx = unpack(line_match)
+ local pat_match = line:sub(startidx + 1, endidx)
+
+ pat_width_differences[#pat_width_differences+1] =
+ #vim.fn.substitute(pat_match, pat1, pat2, 'g') - #pat_match
+ end
+ end
+
+ new_lines[lnum] = vim.fn.substitute(line, pat1, pat2, 'g')
+ end
+
+ -- Highlight the matches if previewing
+ if preview then
+ local idx_offset = 0
+ for i, line_match in ipairs(line_matches) do
+ local startidx, endidx = unpack(line_match)
+ -- Starting index of replacement text
+ local repl_startidx = startidx + idx_offset
+ -- Ending index of the replacement text (if pat2 isn't empty)
+ local repl_endidx
+
+ if pat2 ~= '' then
+ repl_endidx = endidx + idx_offset + pat_width_differences[i]
+ else
+ repl_endidx = endidx + idx_offset
+ end
+
+ if pat2 ~= '' then
+ idx_offset = idx_offset + pat_width_differences[i]
+ end
+
+ line_matches[i] = { repl_startidx, repl_endidx }
+ end
+ end
+ end
+
+ for lnum, line in pairs(new_lines) do
+ vim.api.nvim_buf_set_lines(buf, lnum - 1, lnum, false, { line })
+ end
+
+ if preview then
+ local lnum = vim.api.nvim_win_get_cursor(0)[1]
+ -- Use preview window only if preview buffer is provided and range isn't just the current line
+ local use_preview_win = (preview_buf ~= nil) and (line1 ~= lnum or line2 ~= lnum)
+ return show_replace_preview(buf, use_preview_win, preview_ns, preview_buf, matches)
+ end
+ end
+
+ local function replace(opts)
+ do_replace(opts, false)
+ end
+
+ local function replace_preview(opts, preview_ns, preview_buf)
+ return do_replace(opts, true, preview_ns, preview_buf)
+ end
+
+ -- ":<range>Replace <pat1> <pat2>"
+ -- Replaces all occurences of <pat1> in <range> with <pat2>
+ vim.api.nvim_create_user_command(
+ 'Replace',
+ replace,
+ { nargs = '*', range = '%', addr = 'lines',
+ preview = replace_preview }
+ )
+]]
+
+describe("'inccommand' for user commands", function()
+ local screen
+
+ before_each(function()
+ clear()
+ screen = Screen.new(40, 17)
+ screen:set_default_attr_ids({
+ [1] = {background = Screen.colors.Yellow1},
+ [2] = {foreground = Screen.colors.Blue1, bold = true},
+ [3] = {reverse = true},
+ [4] = {reverse = true, bold = true}
+ })
+ screen:attach()
+ exec_lua(setup_replace_cmd)
+ command('set cmdwinheight=5')
+ insert[[
+ text on line 1
+ more text on line 2
+ oh no, even more text
+ will the text ever stop
+ oh well
+ did the text stop
+ why won't it stop
+ make the text stop
+ ]]
+ end)
+
+ it('works with inccommand=nosplit', function()
+ command('set inccommand=nosplit')
+ feed(':Replace text cats')
+ screen:expect([[
+ {1:cats} on line 1 |
+ more {1:cats} on line 2 |
+ oh no, even more {1:cats} |
+ will the {1:cats} ever stop |
+ oh well |
+ did the {1:cats} stop |
+ why won't it stop |
+ make the {1:cats} stop |
+ |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ :Replace text cats^ |
+ ]])
+ end)
+
+ it('works with inccommand=split', function()
+ command('set inccommand=split')
+ feed(':Replace text cats')
+ screen:expect([[
+ {1:cats} on line 1 |
+ more {1:cats} on line 2 |
+ oh no, even more {1:cats} |
+ will the {1:cats} ever stop |
+ oh well |
+ did the {1:cats} stop |
+ why won't it stop |
+ make the {1:cats} stop |
+ |
+ {4:[No Name] [+] }|
+ |1| {1:cats} on line 1 |
+ |2| more {1:cats} on line 2 |
+ |3| oh no, even more {1:cats} |
+ |4| will the {1:cats} ever stop |
+ |6| did the {1:cats} stop |
+ {3:[Preview] }|
+ :Replace text cats^ |
+ ]])
+ end)
+
+ it('properly closes preview when inccommand=split', function()
+ command('set inccommand=split')
+ feed(':Replace text cats<Esc>')
+ screen:expect([[
+ text on line 1 |
+ more text on line 2 |
+ oh no, even more text |
+ will the text ever stop |
+ oh well |
+ did the text stop |
+ why won't it stop |
+ make the text stop |
+ ^ |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ |
+ ]])
+ end)
+
+ it('properly executes command when inccommand=split', function()
+ command('set inccommand=split')
+ feed(':Replace text cats<CR>')
+ screen:expect([[
+ cats on line 1 |
+ more cats on line 2 |
+ oh no, even more cats |
+ will the cats ever stop |
+ oh well |
+ did the cats stop |
+ why won't it stop |
+ make the cats stop |
+ ^ |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ :Replace text cats |
+ ]])
+ end)
+
+ it('shows preview window only when range is not current line', function()
+ command('set inccommand=split')
+ feed('gg:.Replace text cats')
+ screen:expect([[
+ {1:cats} on line 1 |
+ more text on line 2 |
+ oh no, even more text |
+ will the text ever stop |
+ oh well |
+ did the text stop |
+ why won't it stop |
+ make the text stop |
+ |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ :.Replace text cats^ |
+ ]])
+ end)
+
+ it('does not crash on ambiguous command #18825', function()
+ command('set inccommand=split')
+ command('command Reply echo 1')
+ feed(':R')
+ assert_alive()
+ feed('e')
+ assert_alive()
+ end)
+
+ it('no crash if preview callback changes inccommand option', function()
+ command('set inccommand=nosplit')
+ exec_lua([[
+ vim.api.nvim_create_user_command('Replace', function() end, {
+ nargs = '*',
+ preview = function()
+ vim.api.nvim_set_option('inccommand', 'split')
+ return 2
+ end,
+ })
+ ]])
+ feed(':R')
+ assert_alive()
+ feed('e')
+ assert_alive()
+ end)
+end)
diff --git a/test/functional/ui/input_spec.lua b/test/functional/ui/input_spec.lua
index ea8968a653..0f4e97088c 100644
--- a/test/functional/ui/input_spec.lua
+++ b/test/functional/ui/input_spec.lua
@@ -3,9 +3,12 @@ local clear, feed_command = helpers.clear, helpers.feed_command
local feed, next_msg, eq = helpers.feed, helpers.next_msg, helpers.eq
local command = helpers.command
local expect = helpers.expect
+local curbuf_contents = helpers.curbuf_contents
local meths = helpers.meths
local exec_lua = helpers.exec_lua
local write_file = helpers.write_file
+local funcs = helpers.funcs
+local eval = helpers.eval
local Screen = require('test.functional.ui.screen')
before_each(clear)
@@ -50,6 +53,8 @@ describe('mappings', function()
add_mapping('<kenter>','<kenter>')
add_mapping('<kcomma>','<kcomma>')
add_mapping('<kequal>','<kequal>')
+ add_mapping('<f38>','<f38>')
+ add_mapping('<f63>','<f63>')
end)
it('ok', function()
@@ -106,6 +111,8 @@ describe('mappings', function()
check_mapping('<KPComma>','<kcomma>')
check_mapping('<kequal>','<kequal>')
check_mapping('<KPEquals>','<kequal>')
+ check_mapping('<f38>','<f38>')
+ check_mapping('<f63>','<f63>')
end)
it('support meta + multibyte char mapping', function()
@@ -114,11 +121,196 @@ 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 split utf sequences', function()
+ it('ok', function()
+ local str = '►'
+ feed('i' .. str:sub(1, 1))
+ helpers.sleep(10)
+ feed(str:sub(2, 3))
+ expect('►')
+ end)
+
+ it('can be mapped', function()
+ command('inoremap ► E296BA')
+ local str = '►'
+ feed('i' .. str:sub(1, 1))
+ helpers.sleep(10)
+ feed(str:sub(2, 3))
+ expect('E296BA')
+ end)
+end)
+
+describe('input pairs', function()
+ describe('<tab> / <c-i>', function()
+ it('ok', function()
+ feed('i<tab><c-i><esc>')
+ eq('\t\t', curbuf_contents())
+ end)
+
+ describe('can be mapped separately', function()
+ it('if <tab> is mapped after <c-i>', function()
+ command('inoremap <c-i> CTRL-I!')
+ command('inoremap <tab> TAB!')
+ feed('i<tab><c-i><esc>')
+ eq('TAB!CTRL-I!', curbuf_contents())
+ end)
+
+ it('if <tab> is mapped before <c-i>', function()
+ command('inoremap <tab> TAB!')
+ command('inoremap <c-i> CTRL-I!')
+ feed('i<tab><c-i><esc>')
+ eq('TAB!CTRL-I!', curbuf_contents())
+ end)
+ end)
+ end)
+
+ describe('<cr> / <c-m>', function()
+ it('ok', function()
+ feed('iunos<c-m>dos<cr>tres<esc>')
+ eq('unos\ndos\ntres', curbuf_contents())
+ end)
+
+ describe('can be mapped separately', function()
+ it('if <cr> is mapped after <c-m>', function()
+ command('inoremap <c-m> SNIPPET!')
+ command('inoremap <cr> , and then<cr>')
+ feed('iunos<c-m>dos<cr>tres<esc>')
+ eq('unosSNIPPET!dos, and then\ntres', curbuf_contents())
+ end)
+
+ it('if <cr> is mapped before <c-m>', function()
+ command('inoremap <cr> , and then<cr>')
+ command('inoremap <c-m> SNIPPET!')
+ feed('iunos<c-m>dos<cr>tres<esc>')
+ eq('unosSNIPPET!dos, and then\ntres', curbuf_contents())
+ end)
+ end)
+ end)
+
+ describe('<esc> / <c-[>', function()
+ it('ok', function()
+ feed('2adouble<c-[>asingle<esc>')
+ eq('doubledoublesingle', curbuf_contents())
+ end)
+
+ describe('can be mapped separately', function()
+ it('if <esc> is mapped after <c-[>', function()
+ command('inoremap <c-[> HALLOJ!')
+ command('inoremap <esc> ,<esc>')
+ feed('2adubbel<c-[>upp<esc>')
+ eq('dubbelHALLOJ!upp,dubbelHALLOJ!upp,', curbuf_contents())
+ end)
+
+ it('if <esc> is mapped before <c-[>', function()
+ command('inoremap <esc> ,<esc>')
+ command('inoremap <c-[> HALLOJ!')
+ feed('2adubbel<c-[>upp<esc>')
+ eq('dubbelHALLOJ!upp,dubbelHALLOJ!upp,', curbuf_contents())
+ end)
+ end)
+ end)
+end)
+
+it('Ctrl-6 is Ctrl-^ vim-patch:8.1.2333', function()
+ command('split aaa')
+ command('edit bbb')
+ feed('<C-6>')
+ eq('aaa', funcs.bufname())
+end)
+
+it('c_CTRL-R_CTRL-R, i_CTRL-R_CTRL-R, i_CTRL-G_CTRL-K work properly vim-patch:8.1.2346', function()
+ command('set timeoutlen=10')
+
+ command([[let @a = 'aaa']])
+ feed([[:let x = '<C-R><C-R>a'<CR>]])
+ eq([[let x = 'aaa']], eval('@:'))
+
+ feed('a<C-R><C-R>a<Esc>')
+ expect('aaa')
+ command('bwipe!')
+
+ feed('axx<CR>yy<C-G><C-K>a<Esc>')
+ expect([[
+ axx
+ yy]])
+end)
+
+it('typing a simplifiable key at hit-enter prompt triggers mapping vim-patch:8.2.0839', function()
+ local screen = Screen.new(60,8)
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ [2] = {bold = true, reverse = true}, -- MsgSeparator
+ [3] = {bold = true, foreground = Screen.colors.SeaGreen}, -- MoreMsg
+ })
+ screen:attach()
+ command([[nnoremap <C-6> <Cmd>echo 'hit ctrl-6'<CR>]])
+ feed_command('ls')
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2: }|
+ :ls |
+ 1 %a "[No Name]" line 1 |
+ {3:Press ENTER or type command to continue}^ |
+ ]])
+ feed('<C-6>')
+ screen:expect([[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ hit ctrl-6 |
+ ]])
+end)
+
+it('mixing simplified and unsimplified keys can trigger mapping vim-patch:8.2.0916', function()
+ command('set timeoutlen=10')
+ command([[imap ' <C-W>]])
+ command('imap <C-W><C-A> c-a')
+ feed([[a'<C-A>]])
+ expect('c-a')
+end)
+
+it('unsimplified mapping works when there was a partial match vim-patch:8.2.4504', function()
+ command('set timeoutlen=10')
+ command('nnoremap <C-J> a')
+ command('nnoremap <NL> x')
+ command('nnoremap <C-J>x <Nop>')
+ funcs.setline(1, 'x')
+ -- CTRL-J b should have trigger the <C-J> mapping and then insert "b"
+ feed('<C-J>b<Esc>')
+ expect('xb')
end)
describe('input non-printable chars', function()
@@ -146,7 +338,7 @@ describe('input non-printable chars', function()
{1:~ }|
{1:~ }|
{1:~ }|
- "Xtest-overwrite" [noeol] 1L, 6C |
+ "Xtest-overwrite" [noeol] 1L, 6B |
]])
-- The timestamp is in second resolution, wait two seconds to be sure.
@@ -237,3 +429,47 @@ describe("event processing and input", function()
eq({'notification', 'stop', {}}, next_msg())
end)
end)
+
+describe('display is updated', function()
+ local screen
+ before_each(function()
+ screen = Screen.new(60, 8)
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue1}, -- NonText
+ [2] = {bold = true}, -- ModeMsg
+ })
+ screen:attach()
+ end)
+
+ it('in Insert mode after <Nop> mapping #17911', function()
+ command('imap <Plug>test <Nop>')
+ command('imap <F2> abc<CR><Plug>test')
+ feed('i<F2>')
+ screen:expect([[
+ abc |
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]])
+ end)
+
+ it('in Insert mode after empty string <expr> mapping #17911', function()
+ command('imap <expr> <Plug>test ""')
+ command('imap <F2> abc<CR><Plug>test')
+ feed('i<F2>')
+ screen:expect([[
+ abc |
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]])
+ end)
+end)
diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua
index f038348253..00f126a1f2 100644
--- a/test/functional/ui/messages_spec.lua
+++ b/test/functional/ui/messages_spec.lua
@@ -12,6 +12,7 @@ local nvim_prog = helpers.nvim_prog
local iswin = helpers.iswin
local exc_exec = helpers.exc_exec
local exec_lua = helpers.exec_lua
+local poke_eventloop = helpers.poke_eventloop
describe('ui/ext_messages', function()
local screen
@@ -30,6 +31,7 @@ describe('ui/ext_messages', function()
[7] = {background = Screen.colors.Yellow},
[8] = {foreground = Screen.colors.Red},
[9] = {special = Screen.colors.Red, undercurl = true},
+ [10] = {foreground = Screen.colors.Brown};
})
end)
after_each(function()
@@ -303,13 +305,25 @@ describe('ui/ext_messages', function()
{1:~ }|
{1:~ }|
{1:~ }|
- ]], messages={
+ ]], msg_history={
{kind="echoerr", content={{"raa", 2}}},
{kind="echoerr", content={{"bork", 2}}},
{kind="echoerr", content={{"fail", 2}}},
{kind="echoerr", content={{"extrafail", 2}}},
{kind="echoerr", content={{"problem", 2}}}
- }}
+ }, messages={{
+ content = {{ "Press ENTER or type command to continue", 4 }},
+ kind = "return_prompt"
+ }}}
+
+ feed '<cr>'
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]]}
end)
it('shortmess-=S', function()
@@ -454,11 +468,14 @@ describe('ui/ext_messages', function()
alphpabe^t |
{1:~ }|
{1:~ }|
- ]], messages={
- {kind="echomsg", content={{"stuff"}}},
- }, showmode={
- { "-- INSERT --", 3 }
- }}
+ ]], msg_history={{
+ content = {{ "stuff" }},
+ kind = "echomsg",
+ }}, showmode={{ "-- INSERT --", 3 }},
+ messages={{
+ content = {{ "Press ENTER or type command to continue", 4}},
+ kind = "return_prompt"
+ }}}
end)
it('&showmode with macro-recording message', function()
@@ -684,12 +701,15 @@ describe('ui/ext_messages', function()
{1:~ }|
{1:~ }|
{1:~ }|
- ]], messages={
+ ]], msg_history={
{kind="echomsg", content={{"howdy"}}},
{kind="", content={{"Type :qa and press <Enter> to exit Nvim"}}},
{kind="echoerr", content={{"bork", 2}}},
{kind="emsg", content={{"E117: Unknown function: nosuchfunction", 2}}}
- }}
+ }, messages={{
+ content = {{ "Press ENTER or type command to continue", 4}},
+ kind = "return_prompt"
+ }}}
end)
it('implies ext_cmdline and ignores cmdheight', function()
@@ -717,7 +737,6 @@ describe('ui/ext_messages', function()
]])
eq(0, eval('&cmdheight'))
- -- normally this would be an error
feed(':set cmdheight=0')
screen:expect{grid=[[
^ |
@@ -839,9 +858,53 @@ stack traceback:
{1:~ }|
{1:~ }|
]]}
-
end)
+ it('supports nvim_echo messages with multiple attrs', function()
+ async_meths.echo({{'wow, ',"Search"}, {"such\n\nvery ", "ErrorMsg"}, {"color", "LineNr"}}, true, {})
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], messages={
+ { content = { { "wow, ", 7 }, { "such\n\nvery ", 2 }, { "color", 10 } }, kind = "" }
+ }}
+
+ feed ':ls<cr>'
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], messages={
+ { content = { { '\n 1 %a "[No Name]" line 1' } }, kind = "echomsg" }
+ }}
+
+ feed ':messages<cr>'
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], messages={
+ { content = { { "Press ENTER or type command to continue", 4 } }, kind = "return_prompt" }
+ }, msg_history={
+ { content = { { "wow, ", 7 }, { "such\n\nvery ", 2 }, { "color", 10 } }, kind = "echomsg" }
+ }}
+
+ feed '<cr>'
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]]}
+ end)
end)
describe('ui/builtin messages', function()
@@ -850,17 +913,19 @@ describe('ui/builtin messages', function()
clear()
screen = Screen.new(60, 7)
screen:attach({rgb=true, ext_popupmenu=true})
- screen:set_default_attr_ids({
- [1] = {bold = true, foreground = Screen.colors.Blue1},
- [2] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
- [3] = {bold = true, reverse = true},
- [4] = {bold = true, foreground = Screen.colors.SeaGreen4},
- [5] = {foreground = Screen.colors.Blue1},
- [6] = {bold = true, foreground = Screen.colors.Magenta},
- [7] = {background = Screen.colors.Grey20},
- [8] = {reverse = true},
- [9] = {background = Screen.colors.LightRed}
- })
+ screen:set_default_attr_ids {
+ [1] = {bold = true, foreground = Screen.colors.Blue1};
+ [2] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red};
+ [3] = {bold = true, reverse = true};
+ [4] = {bold = true, foreground = Screen.colors.SeaGreen4};
+ [5] = {foreground = Screen.colors.Blue1};
+ [6] = {bold = true, foreground = Screen.colors.Magenta};
+ [7] = {background = Screen.colors.Grey20};
+ [8] = {reverse = true};
+ [9] = {background = Screen.colors.LightRed};
+ [10] = {background = Screen.colors.Yellow};
+ [11] = {foreground = Screen.colors.Brown};
+ }
end)
it('supports multiline messages from rpc', function()
@@ -1013,7 +1078,7 @@ vimComment xxx match /\s"[^\-:.%#=*].*$/ms=s+1,lc=1 excludenl contains=@vim
end)
it('redraws NOT_VALID correctly after message', function()
- -- edge case: only one window was set NOT_VALID. Orginal report
+ -- edge case: only one window was set NOT_VALID. Original report
-- used :make, but fake it using one command to set the current
-- window NOT_VALID and another to show a long message.
command("set more")
@@ -1096,6 +1161,88 @@ vimComment xxx match /\s"[^\-:.%#=*].*$/ms=s+1,lc=1 excludenl contains=@vim
{4:Press ENTER or type command to continue}^ |
]]}
end)
+
+ it('supports nvim_echo messages with multiple attrs', function()
+ async_meths.echo({{'wow, ',"Search"}, {"such\n\nvery ", "ErrorMsg"}, {"color", "LineNr"}}, true, {})
+ screen:expect{grid=[[
+ |
+ {1:~ }|
+ {3: }|
+ {10:wow, }{2:such} |
+ |
+ {2:very }{11:color} |
+ {4:Press ENTER or type command to continue}^ |
+ ]]}
+
+ feed '<cr>'
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+
+ feed ':messages<cr>'
+ screen:expect{grid=[[
+ |
+ {1:~ }|
+ {3: }|
+ {10:wow, }{2:such} |
+ |
+ {2:very }{11:color} |
+ {4:Press ENTER or type command to continue}^ |
+ ]]}
+ end)
+
+ it('prints lines in Ex mode correctly with a burst of carriage returns #19341', function()
+ command('set number')
+ meths.buf_set_lines(0, 0, 0, true, {'aaa', 'bbb', 'ccc'})
+ command('set display-=msgsep')
+ feed('gggQ<CR><CR>1<CR><CR>vi')
+ screen:expect([[
+ Entering Ex mode. Type "visual" to go to Normal mode. |
+ {11: 2 }bbb |
+ {11: 3 }ccc |
+ :1 |
+ {11: 1 }aaa |
+ {11: 2 }bbb |
+ :vi^ |
+ ]])
+ feed('<CR>')
+ screen:expect([[
+ {11: 1 }aaa |
+ {11: 2 }^bbb |
+ {11: 3 }ccc |
+ {11: 4 } |
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ command('set display+=msgsep')
+ feed('gggQ<CR><CR>1<CR><CR>vi')
+ screen:expect([[
+ Entering Ex mode. Type "visual" to go to Normal mode. |
+ {11: 2 }bbb |
+ {11: 3 }ccc |
+ :1 |
+ {11: 1 }aaa |
+ {11: 2 }bbb |
+ :vi^ |
+ ]])
+ feed('<CR>')
+ screen:expect([[
+ {11: 1 }aaa |
+ {11: 2 }^bbb |
+ {11: 3 }ccc |
+ {11: 4 } |
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ end)
end)
describe('ui/ext_messages', function()
@@ -1111,6 +1258,8 @@ describe('ui/ext_messages', function()
[3] = {bold = true},
[4] = {bold = true, foreground = Screen.colors.SeaGreen4},
[5] = {foreground = Screen.colors.Blue1},
+ [6] = {reverse = true},
+ [7] = {bold = true, reverse = true},
})
end)
@@ -1202,6 +1351,107 @@ describe('ui/ext_messages', function()
{content = { { "Press ENTER or type command to continue", 4 } }, kind = "return_prompt" }
}}
end)
+
+ it('supports global statusline', function()
+ feed(":set laststatus=3<cr>")
+ feed(":sp<cr>")
+ feed(":set cmdheight<cr>")
+ screen:expect({grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ────────────────────────────────────────────────────────────────────────────────|
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {7:[No Name] }|
+ ]], messages={
+ {content = { { " cmdheight=0" } }, kind = "" }
+ }})
+
+ feed("<c-w>+")
+ feed(":set laststatus<cr>")
+ screen:expect({grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ────────────────────────────────────────────────────────────────────────────────|
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {7:[No Name] }|
+ ]], messages={
+ {content = { { " laststatus=3" } }, kind = "" }
+ }})
+
+ feed(":set mouse=a<cr>")
+ meths.input_mouse('left', 'press', '', 0, 12, 10)
+ poke_eventloop()
+ meths.input_mouse('left', 'drag', '', 0, 12, 10)
+ meths.input_mouse('left', 'drag', '', 0, 11, 10)
+ feed("<c-l>")
+ feed(":set cmdheight<cr>")
+ screen:expect({grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ────────────────────────────────────────────────────────────────────────────────|
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {7:[No Name] }|
+ ]], messages={
+ {content = { { " cmdheight=0" } }, kind = "" }
+ }})
+ end)
end)
describe('ui/msg_puts_printf', function()
@@ -1226,7 +1476,7 @@ describe('ui/msg_puts_printf', function()
pending('Locale ja_JP.UTF-8 not supported', function() end)
return
elseif helpers.isCI() then
- -- Fails non--Windows CI. Message catalog direcotry issue?
+ -- Fails non--Windows CI. Message catalog directory issue?
pending('fails on unix CI', function() end)
return
end
@@ -1281,7 +1531,7 @@ ullamco laboris nisi ut
aliquip ex ea commodo consequat.]])
end)
- it('can be quit', function()
+ it('can be quit with echon', function()
screen:try_resize(25,5)
feed(':echon join(map(range(0, &lines*10), "v:val"), "\\n")<cr>')
screen:expect{grid=[[
@@ -1301,6 +1551,45 @@ aliquip ex ea commodo consequat.]])
]]}
end)
+ it('can be quit with Lua #11224 #16537', function()
+ -- NOTE: adds "4" to message history, although not displayed initially
+ -- (triggered the more prompt).
+ screen:try_resize(40,5)
+ feed(':lua for i=0,10 do print(i) end<cr>')
+ screen:expect{grid=[[
+ 0 |
+ 1 |
+ 2 |
+ 3 |
+ {4:-- More --}^ |
+ ]]}
+ feed('q')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+ feed(':mess<cr>')
+ screen:expect{grid=[[
+ 0 |
+ 1 |
+ 2 |
+ 3 |
+ {4:-- More --}^ |
+ ]]}
+ feed('j')
+ screen:expect{grid=[[
+ 1 |
+ 2 |
+ 3 |
+ 4 |
+ {4:Press ENTER or type command to continue}^ |
+ ]]}
+ feed('<cr>')
+ end)
+
it('handles wrapped lines with line scroll', function()
feed(':lua error(_G.x)<cr>')
screen:expect{grid=[[
diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua
index baacef358f..69b0d1ecec 100644
--- a/test/functional/ui/mouse_spec.lua
+++ b/test/functional/ui/mouse_spec.lua
@@ -32,7 +32,7 @@ describe('ui/mouse/input', function()
[6] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
[7] = {bold = true, foreground = Screen.colors.SeaGreen4},
})
- command("set display-=msgsep")
+ command("set display-=msgsep mousemodel=extend")
feed('itesting<cr>mouse<cr>support and selection<esc>')
screen:expect([[
testing |
@@ -595,54 +595,54 @@ describe('ui/mouse/input', function()
feed('ifoo\nbar<esc>')
screen:expect{grid=[[
- testing {4:│}testing |
- mouse {4:│}mouse |
- support and selection {4:│}support and selection |
- {0:~ }{4:│}{0:~ }|
- {0:~ }{4:│}{0:~ }|
- {0:~ }{4:│[No Name] [+] }|
- {0:~ }{4:│}foo{0:$} |
- {0:~ }{4:│}ba^r{0:$} |
- {0:~ }{4:│}{0:~ }|
- {0:~ }{4:│}{0:~ }|
- {0:~ }{4:│}{0:~ }|
- {0:~ }{4:│}{0:~ }|
+ testing │testing |
+ mouse │mouse |
+ support and selection │support and selection |
+ {0:~ }│{0:~ }|
+ {0:~ }│{0:~ }|
+ {0:~ }│{4:[No Name] [+] }|
+ {0:~ }│foo{0:$} |
+ {0:~ }│ba^r{0:$} |
+ {0:~ }│{0:~ }|
+ {0:~ }│{0:~ }|
+ {0:~ }│{0:~ }|
+ {0:~ }│{0:~ }|
{4:[No Name] [+] }{5:[No Name] [+] }|
|
]]}
meths.input_mouse('left', 'press', '', 0, 6, 27)
screen:expect{grid=[[
- testing {4:│}testing |
- mouse {4:│}mouse |
- support and selection {4:│}support and selection |
- {0:~ }{4:│}{0:~ }|
- {0:~ }{4:│}{0:~ }|
- {0:~ }{4:│[No Name] [+] }|
- {0:~ }{4:│}^foo{0:$} |
- {0:~ }{4:│}bar{0:$} |
- {0:~ }{4:│}{0:~ }|
- {0:~ }{4:│}{0:~ }|
- {0:~ }{4:│}{0:~ }|
- {0:~ }{4:│}{0:~ }|
+ testing │testing |
+ mouse │mouse |
+ support and selection │support and selection |
+ {0:~ }│{0:~ }|
+ {0:~ }│{0:~ }|
+ {0:~ }│{4:[No Name] [+] }|
+ {0:~ }│^foo{0:$} |
+ {0:~ }│bar{0:$} |
+ {0:~ }│{0:~ }|
+ {0:~ }│{0:~ }|
+ {0:~ }│{0:~ }|
+ {0:~ }│{0:~ }|
{4:[No Name] [+] }{5:[No Name] [+] }|
|
]]}
meths.input_mouse('left', 'drag', '', 0, 7, 30)
screen:expect{grid=[[
- testing {4:│}testing |
- mouse {4:│}mouse |
- support and selection {4:│}support and selection |
- {0:~ }{4:│}{0:~ }|
- {0:~ }{4:│}{0:~ }|
- {0:~ }{4:│[No Name] [+] }|
- {0:~ }{4:│}{1:foo}{3:$} |
- {0:~ }{4:│}{1:bar}{0:^$} |
- {0:~ }{4:│}{0:~ }|
- {0:~ }{4:│}{0:~ }|
- {0:~ }{4:│}{0:~ }|
- {0:~ }{4:│}{0:~ }|
+ testing │testing |
+ mouse │mouse |
+ support and selection │support and selection |
+ {0:~ }│{0:~ }|
+ {0:~ }│{0:~ }|
+ {0:~ }│{4:[No Name] [+] }|
+ {0:~ }│{1:foo}{3:$} |
+ {0:~ }│{1:bar}{0:^$} |
+ {0:~ }│{0:~ }|
+ {0:~ }│{0:~ }|
+ {0:~ }│{0:~ }|
+ {0:~ }│{0:~ }|
{4:[No Name] [+] }{5:[No Name] [+] }|
{2:-- VISUAL --} |
]]}
@@ -729,12 +729,12 @@ describe('ui/mouse/input', function()
feed('k')
feed_command('sp', 'vsp')
screen:expect([[
- lines {4:│}lines |
- to {4:│}to |
- test {4:│}test |
- ^mouse scrolling {4:│}mouse scrolling |
- {4:│} |
- {0:~ }{4:│}{0:~ }|
+ lines │lines |
+ to │to |
+ test │test |
+ ^mouse scrolling │mouse scrolling |
+ │ |
+ {0:~ }│{0:~ }|
{5:[No Name] [+] }{4:[No Name] [+] }|
to |
test |
@@ -750,12 +750,12 @@ describe('ui/mouse/input', function()
feed('<ScrollWheelDown><0,0>')
end
screen:expect([[
- ^mouse scrolling {4:│}lines |
- {4:│}to |
- {0:~ }{4:│}test |
- {0:~ }{4:│}mouse scrolling |
- {0:~ }{4:│} |
- {0:~ }{4:│}{0:~ }|
+ ^mouse scrolling │lines |
+ │to |
+ {0:~ }│test |
+ {0:~ }│mouse scrolling |
+ {0:~ }│ |
+ {0:~ }│{0:~ }|
{5:[No Name] [+] }{4:[No Name] [+] }|
to |
test |
@@ -771,12 +771,12 @@ describe('ui/mouse/input', function()
feed('<ScrollWheelUp><27,0>')
end
screen:expect([[
- ^mouse scrolling {4:│}text |
- {4:│}with |
- {0:~ }{4:│}many |
- {0:~ }{4:│}lines |
- {0:~ }{4:│}to |
- {0:~ }{4:│}test |
+ ^mouse scrolling │text |
+ │with |
+ {0:~ }│many |
+ {0:~ }│lines |
+ {0:~ }│to |
+ {0:~ }│test |
{5:[No Name] [+] }{4:[No Name] [+] }|
to |
test |
@@ -793,12 +793,12 @@ describe('ui/mouse/input', function()
feed('<ScrollWheelUp><27,7><ScrollWheelUp>')
end
screen:expect([[
- ^mouse scrolling {4:│}text |
- {4:│}with |
- {0:~ }{4:│}many |
- {0:~ }{4:│}lines |
- {0:~ }{4:│}to |
- {0:~ }{4:│}test |
+ ^mouse scrolling │text |
+ │with |
+ {0:~ }│many |
+ {0:~ }│lines |
+ {0:~ }│to |
+ {0:~ }│test |
{5:[No Name] [+] }{4:[No Name] [+] }|
Inserting |
text |
@@ -1571,4 +1571,23 @@ describe('ui/mouse/input', function()
meths.set_option('winwidth', winwidth)
meths.input_mouse('left', 'release', '', 0, 0, 0)
end)
+
+ it('scroll keys are not translated into multiclicks #6211 #6989', function()
+ meths.set_var('mouse_up', 0)
+ meths.set_var('mouse_up2', 0)
+ meths.set_var('mouse_up3', 0)
+ meths.set_var('mouse_up4', 0)
+ command('nnoremap <ScrollWheelUp> <Cmd>let g:mouse_up += 1<CR>')
+ command('nnoremap <2-ScrollWheelUp> <Cmd>let g:mouse_up2 += 1<CR>')
+ command('nnoremap <3-ScrollWheelUp> <Cmd>let g:mouse_up3 += 1<CR>')
+ command('nnoremap <4-ScrollWheelUp> <Cmd>let g:mouse_up4 += 1<CR>')
+ meths.input_mouse('wheel', 'up', '', 0, 0, 0)
+ meths.input_mouse('wheel', 'up', '', 0, 0, 0)
+ meths.input_mouse('wheel', 'up', '', 0, 0, 0)
+ meths.input_mouse('wheel', 'up', '', 0, 0, 0)
+ eq(4, meths.get_var('mouse_up'))
+ eq(0, meths.get_var('mouse_up2'))
+ eq(0, meths.get_var('mouse_up3'))
+ eq(0, meths.get_var('mouse_up4'))
+ end)
end)
diff --git a/test/functional/ui/multibyte_spec.lua b/test/functional/ui/multibyte_spec.lua
index e6a79feadc..d4e237bcb4 100644
--- a/test/functional/ui/multibyte_spec.lua
+++ b/test/functional/ui/multibyte_spec.lua
@@ -6,6 +6,9 @@ local feed = helpers.feed
local feed_command = helpers.feed_command
local insert = helpers.insert
local funcs = helpers.funcs
+local meths = helpers.meths
+local split = helpers.split
+local dedent = helpers.dedent
describe("multibyte rendering", function()
local screen
@@ -26,7 +29,7 @@ describe("multibyte rendering", function()
̊
x]])
feed("gg")
- -- verify the modifier infact is alone
+ -- verify the modifier in fact is alone
feed_command("ascii")
screen:expect([[
^ ̊ |
@@ -115,6 +118,35 @@ describe("multibyte rendering", function()
{4:-- INSERT --} |
]])
end)
+
+ it('works with a lot of unicode (zalgo) text', function()
+ screen:try_resize(65, 10)
+ meths.buf_set_lines(0,0,-1,true, split(dedent [[
+ L̓̉̑̒̌̚ơ̗̌̒̄̀ŕ̈̈̎̐̕è̇̅̄̄̐m̖̟̟̅̄̚ ̛̓̑̆̇̍i̗̟̞̜̅̐p̗̞̜̉̆̕s̟̜̘̍̑̏ū̟̞̎̃̉ḿ̘̙́́̐ ̖̍̌̇̉̚d̞̄̃̒̉̎ò́̌̌̂̐l̞̀̄̆̌̚ȯ̖̞̋̀̐r̓̇̌̃̃̚ ̗̘̀̏̍́s̜̀̎̎̑̕i̟̗̐̄̄̚t̝̎̆̓̐̒ ̘̇̔̓̊̚ȃ̛̟̗̏̅m̜̟̙̞̈̓é̘̞̟̔̆t̝̂̂̈̑̔,̜̜̖̅̄̍ ̛̗̊̓̆̚c̟̍̆̍̈̔ȯ̖̖̝̑̀n̜̟̎̊̃̚s̟̏̇̎̒̚e̙̐̈̓̌̚c̙̍̈̏̅̕ť̇̄̇̆̓e̛̓̌̈̓̈t̟̍̀̉̆̅u̝̞̎̂̄̚r̘̀̅̈̅̐ ̝̞̓́̇̉ã̏̀̆̅̕d̛̆̐̉̆̋ȉ̞̟̍̃̚p̛̜̊̍̂̓ȋ̏̅̃̋̚ṥ̛̏̃̕č̛̞̝̀̂í̗̘̌́̎n̔̎́̒̂̕ǧ̗̜̋̇̂ ̛̜̔̄̎̃ê̛̔̆̇̕l̘̝̏̐̊̏ĩ̛̍̏̏̄t̟̐́̀̐̎,̙̘̍̆̉̐ ̋̂̏̄̌̅s̙̓̌̈́̇e̛̗̋̒̎̏d̜̗̊̍̊̚
+ ď̘̋̌̌̕ǒ̝̗̔̇̕ ̙̍́̄̄̉è̛̛̞̌̌i̜̖̐̈̆̚ȕ̇̈̓̃̓ŝ̛̞̙̉̋m̜̐̂̄̋̂ȯ̈̎̎̅̕d̜̙̓̔̋̑ ̞̗̄̂̂̚t̝̊́̃́̄e̛̘̜̞̓̑m̊̅̏̉̌̕p̛̈̂̇̀̐ỏ̙̘̈̉̔r̘̞̋̍̃̚ ̝̄̀̇̅̇ỉ̛̖̍̓̈n̛̛̝̎̕̕c̛̛̊̅́̐ĭ̗̓̀̍̐d̞̜̋̐̅̚i̟̙̇̄̊̄d̞̊̂̀̇̚ủ̝̉̑̃̕n̜̏̇̄̐̋ť̗̜̞̋̉ ̝̒̓̌̓̚ȕ̖̙̀̚̕t̖̘̎̉̂̌ ̛̝̄̍̌̂l̛̟̝̃̑̋á̛̝̝̔̅b̝̙̜̗̅̒ơ̖̌̒̄̆r̒̇̓̎̈̄e̛̛̖̅̏̇ ̖̗̜̝̃́e̛̛̘̅̔̌ẗ̛̙̗̐̕ ̖̟̇̋̌̈d̞̙̀̉̑̕ŏ̝̂́̐̑l̞̟̗̓̓̀ơ̘̎̃̄̂r̗̗̖̔̆̍ẻ̖̝̞̋̅ ̜̌̇̍̈̊m̈̉̇̄̒̀a̜̞̘̔̅̆g̗̖̈̃̈̉n̙̖̄̈̉̄â̛̝̜̄̃ ̛́̎̕̕̚ā̊́́̆̌l̟̙̞̃̒́i̖̇̎̃̀̋q̟̇̒̆́̊ủ́̌̇̑̚ã̛̘̉̐̚.̛́̏̐̍̊
+ U̝̙̎̈̐̆t̜̍̌̀̔̏ ̞̉̍̇̈̃e̟̟̊̄̕̕n̝̜̒̓̆̕i̖̒̌̅̇̚m̞̊̃̔̊̂ ̛̜̊̎̄̂a̘̜̋̒̚̚d̟̊̎̇̂̍ ̜̖̏̑̉̕m̜̒̎̅̄̚i̝̖̓̂̍̕n̙̉̒̑̀̔ỉ̖̝̌̒́m̛̖̘̅̆̎ ̖̉̎̒̌̕v̖̞̀̔́̎e̖̙̗̒̎̉n̛̗̝̎̀̂ȉ̞̗̒̕̚ȧ̟̜̝̅̚m̆̉̐̐̇̈,̏̐̎́̍́ ̜̞̙̘̏̆q̙̖̙̅̓̂ủ̇́̀̔̚í̙̟̟̏̐s̖̝̍̏̂̇ ̛̘̋̈̕̕ń̛̞̜̜̎o̗̜̔̔̈̆s̞̘̘̄̒̋t̛̅̋́̔̈ȓ̓̒́̇̅ủ̜̄̃̒̍d̙̝̘̊̏̚ ̛̟̞̄́̔e̛̗̝̍̃̀x̞̖̃̄̂̅e̖̅̇̐̔̃r̗̞̖̔̎̚c̘̜̖̆̊̏ï̙̝̙̂̕t̖̏́̓̋̂ă̖̄̆̑̒t̜̟̍̉̑̏i̛̞̞̘̒̑ǒ̜̆̅̃̉ṅ̖̜̒̎̚
+ u̗̞̓̔̈̏ĺ̟̝́̎̚l̛̜̅̌̎̆a̒̑̆̔̇̃m̜̗̈̊̎̚ċ̘̋̇̂̚ơ̟̖̊́̕ ̖̟̍̉̏̚l̙̔̓̀̅̏ä̞̗̘̙̅ḃ̟̎̄̃̕o̞̎̓̓̓̚r̗̜̊̓̈̒ï̗̜̃̃̅s̀̒̌̂̎̂ ̖̗̗̋̎̐n̝̟̝̘̄̚i̜̒̀̒̐̕s̘̘̄̊̃̀ī̘̜̏̌̕ ̗̖̞̐̈̒ư̙̞̄́̌t̟̘̖̙̊̚ ̌̅̋̆̚̚ä̇̊̇̕̕l̝̞̘̋̔̅i̍̋́̆̑̈q̛̆̐̈̐̚ư̏̆̊́̚î̜̝̑́̊p̗̓̅̑̆̏ ̆́̓̔̋̋e̟̊̋̏̓̚x̗̍̑̊̎̈ ̟̞̆̄̂̍ë̄̎̄̃̅a̛̜̅́̃̈ ̔̋̀̎̐̀c̖̖̍̀̒̂ơ̛̙̖̄̒m̘̔̍̏̆̕ḿ̖̙̝̏̂ȍ̓̋̈̀̕d̆̂̊̅̓̚o̖̔̌̑̚̕ ̙̆́̔̊̒c̖̘̖̀̄̍o̓̄̑̐̓̒ñ̞̒̎̈̚s̞̜̘̈̄̄e̙̊̀̇̌̋q̐̒̓́̔̃ư̗̟̔̔̚å̖̙̞̄̏t̛̙̟̒̇̏.̙̗̓̃̓̎
+ D̜̖̆̏̌̌ư̑̃̌̍̕i̝̊̊̊̊̄s̛̙̒́̌̇ ̛̃̔̄̆̌ă̘̔̅̅̀ú̟̟̟̃̃t̟̂̄̈̈̃e̘̅̌̒̂̆ ̖̟̐̉̉̌î̟̟̙̜̇r̛̙̞̗̄̌ú̗̗̃̌̎r̛̙̘̉̊̕e̒̐̔̃̓̋ ̊̊̍̋̑̉d̛̝̙̉̀̓o̘̜̐̐̓̐l̞̋̌̆̍́o̊̊̐̃̃̚ṙ̛̖̘̃̕ ̞̊̀̍̒̕ȉ́̑̐̇̅ǹ̜̗̜̞̏ ̛̜̐̄̄̚r̜̖̈̇̅̋ĕ̗̉̃̔̚p̟̝̀̓̔̆r̜̈̆̇̃̃e̘̔̔̏̎̓h̗̒̉̑̆̚ė̛̘̘̈̐n̘̂̀̒̕̕d̗̅̂̋̅́ê̗̜̜̜̕r̟̋̄̐̅̂i̛̔̌̒̂̕t̛̗̓̎̀̎ ̙̗̀̉̂̚ȉ̟̗̐̓̚n̙̂̍̏̓̉ ̙̘̊̋̍̕v̜̖̀̎̆̐ő̜̆̉̃̎l̑̋̒̉̔̆ư̙̓̓́̚p̝̘̖̎̏̒t̛̘̝̞̂̓ȁ̘̆̔́̊t̖̝̉̒̐̎e̞̟̋̀̅̄ ̆̌̃̀̑̔v̝̘̝̍̀̇ȅ̝̊̄̓̕l̞̝̑̔̂̋ĭ̝̄̅̆̍t̝̜̉̂̈̇
+ ē̟̊̇̕̚s̖̘̘̒̄̑s̛̘̀̊̆̇e̛̝̘̒̏̚ ̉̅̑̂̐̎c̛̟̙̎̋̓i̜̇̒̏̆̆l̟̄́̆̊̌l̍̊̋̃̆̌ủ̗̙̒̔̚m̛̘̘̖̅̍ ̖̙̈̎̂̕d̞̟̏̋̈̔ơ̟̝̌̃̄l̗̙̝̂̉̒õ̒̃̄̄̚ŕ̗̏̏̊̍ê̞̝̞̋̈ ̜̔̒̎̃̚e̞̟̞̒̃̄ư̖̏̄̑̃ ̛̗̜̄̓̎f̛̖̞̅̓̃ü̞̏̆̋̕g̜̝̞̑̑̆i̛̘̐̐̅̚à̜̖̌̆̎t̙̙̎̉̂̍ ̋̔̈̎̎̉n̞̓́̔̊̕ư̘̅̋̔̚l̗̍̒̄̀̚l̞̗̘̙̓̍â̘̔̒̎̚ ̖̓̋̉̃̆p̛̛̘̋̌̀ä̙̔́̒̕r̟̟̖̋̐̋ì̗̙̎̓̓ȃ̔̋̑̚̕t̄́̎̓̂̋ư̏̈̂̑̃r̖̓̋̊̚̚.̒̆̑̆̊̎ ̘̜̍̐̂̚E̞̅̐̇́̂x̄́̈̌̉̕ć̘̃̉̃̕è̘̂̑̏̑p̝̘̑̂̌̆t̔̐̅̍̌̂ȇ̞̈̐̚̕ű̝̞̜́̚ŕ̗̝̉̆́
+ š̟́̔̏̀ȉ̝̟̝̏̅n̑̆̇̒̆̚t̝̒́̅̋̏ ̗̑̌̋̇̚ơ̙̗̟̆̅c̙̞̙̎̊̎c̘̟̍̔̊̊a̛̒̓̉́̐e̜̘̙̒̅̇ć̝̝̂̇̕ả̓̍̎̂̚t̗̗̗̟̒̃ ̘̒̓̐̇́c̟̞̉̐̓̄ȕ̙̗̅́̏p̛̍̋̈́̅i̖̓̒̍̈̄d̞̃̈̌̆̐a̛̗̝̎̋̉t̞̙̀̊̆̇a̛̙̒̆̉̚t̜̟̘̉̓̚ ̝̘̗̐̇̕n̛̘̑̏̂́ō̑̋̉̏́ň̞̊̆̄̃ ̙̙̙̜̄̏p̒̆̋̋̓̏r̖̖̅̉́̚ơ̜̆̑̈̚i̟̒̀̃̂̌d̛̏̃̍̋̚ë̖̞̙̗̓n̛̘̓̒̅̎t̟̗̙̊̆̚,̘̙̔̊̚̕ ̟̗̘̜̑̔s̜̝̍̀̓̌û̞̙̅̇́n̘̗̝̒̃̎t̗̅̀̅̊̈ ̗̖̅̅̀̄i̛̖̍̅̋̂n̙̝̓̓̎̚ ̞̋̅̋̃̚c̗̒̀̆̌̎ū̞̂̑̌̓ĺ̛̐̍̑́p̝̆̌̎̈̚a̖̙̒̅̈̌ ̝̝̜̂̈̀q̝̖̔̍̒̚ư̔̐̂̎̊ǐ̛̟̖̘̕
+ o̖̜̔̋̅̚f̛̊̀̉́̕f̏̉̀̔̃̃i̘̍̎̐̔̎c̙̅̑̂̐̅ȋ̛̜̀̒̚a̋̍̇̏̀̋ ̖̘̒̅̃̒d̗̘̓̈̇̋é̝́̎̒̄š̙̒̊̉̋e̖̓̐̀̍̕r̗̞̂̅̇̄ù̘̇̐̉̀n̐̑̀̄̍̐t̟̀̂̊̄̚ ̟̝̂̍̏́m̜̗̈̂̏̚ő̞̊̑̇̒l̘̑̏́̔̄l̛̛̇̃̋̊i̓̋̒̃̉̌t̛̗̜̏̀̋ ̙̟̒̂̌̐a̙̝̔̆̏̅n̝̙̙̗̆̅i̍̔́̊̃̕m̖̝̟̒̍̚ ̛̃̃̑̌́ǐ̘̉̔̅̚d̝̗̀̌̏̒ ̖̝̓̑̊̚ȇ̞̟̖̌̕š̙̙̈̔̀t̂̉̒̍̄̄ ̝̗̊̋̌̄l̛̞̜̙̘̔å̝̍̂̍̅b̜̆̇̈̉̌ǒ̜̙̎̃̆r̝̀̄̍́̕ư̋̊́̊̕m̜̗̒̐̕̚.̟̘̀̒̌̚]],
+ '\n'))
+
+ -- tests that we can handle overflow of the buffer
+ -- for redraw events (4096 bytes) gracefully
+ screen:expect{grid=[[
+ ^L̓̉̑̒̌̚ơ̗̌̒̄̀ŕ̈̈̎̐̕è̇̅̄̄̐m̖̟̟̅̄̚ ̛̓̑̆̇̍i̗̟̞̜̅̐p̗̞̜̉̆̕s̟̜̘̍̑̏ū̟̞̎̃̉ḿ̘̙́́̐ ̖̍̌̇̉̚d̞̄̃̒̉̎ò́̌̌̂̐l̞̀̄̆̌̚ȯ̖̞̋̀̐r̓̇̌̃̃̚ ̗̘̀̏̍́s̜̀̎̎̑̕i̟̗̐̄̄̚t̝̎̆̓̐̒ ̘̇̔̓̊̚ȃ̛̟̗̏̅m̜̟̙̞̈̓é̘̞̟̔̆t̝̂̂̈̑̔,̜̜̖̅̄̍ ̛̗̊̓̆̚c̟̍̆̍̈̔ȯ̖̖̝̑̀n̜̟̎̊̃̚s̟̏̇̎̒̚e̙̐̈̓̌̚c̙̍̈̏̅̕ť̇̄̇̆̓e̛̓̌̈̓̈t̟̍̀̉̆̅u̝̞̎̂̄̚r̘̀̅̈̅̐ ̝̞̓́̇̉ã̏̀̆̅̕d̛̆̐̉̆̋ȉ̞̟̍̃̚p̛̜̊̍̂̓ȋ̏̅̃̋̚ṥ̛̏̃̕č̛̞̝̀̂í̗̘̌́̎n̔̎́̒̂̕ǧ̗̜̋̇̂ ̛̜̔̄̎̃ê̛̔̆̇̕l̘̝̏̐̊̏ĩ̛̍̏̏̄t̟̐́̀̐̎,̙̘̍̆̉̐ ̋̂̏̄̌̅s̙̓̌̈́̇e̛̗̋̒̎̏d̜̗̊̍̊̚ |
+ ď̘̋̌̌̕ǒ̝̗̔̇̕ ̙̍́̄̄̉è̛̛̞̌̌i̜̖̐̈̆̚ȕ̇̈̓̃̓ŝ̛̞̙̉̋m̜̐̂̄̋̂ȯ̈̎̎̅̕d̜̙̓̔̋̑ ̞̗̄̂̂̚t̝̊́̃́̄e̛̘̜̞̓̑m̊̅̏̉̌̕p̛̈̂̇̀̐ỏ̙̘̈̉̔r̘̞̋̍̃̚ ̝̄̀̇̅̇ỉ̛̖̍̓̈n̛̛̝̎̕̕c̛̛̊̅́̐ĭ̗̓̀̍̐d̞̜̋̐̅̚i̟̙̇̄̊̄d̞̊̂̀̇̚ủ̝̉̑̃̕n̜̏̇̄̐̋ť̗̜̞̋̉ ̝̒̓̌̓̚ȕ̖̙̀̚̕t̖̘̎̉̂̌ ̛̝̄̍̌̂l̛̟̝̃̑̋á̛̝̝̔̅b̝̙̜̗̅̒ơ̖̌̒̄̆r̒̇̓̎̈̄e̛̛̖̅̏̇ ̖̗̜̝̃́e̛̛̘̅̔̌ẗ̛̙̗̐̕ ̖̟̇̋̌̈d̞̙̀̉̑̕ŏ̝̂́̐̑l̞̟̗̓̓̀ơ̘̎̃̄̂r̗̗̖̔̆̍ẻ̖̝̞̋̅ ̜̌̇̍̈̊m̈̉̇̄̒̀a̜̞̘̔̅̆g̗̖̈̃̈̉n̙̖̄̈̉̄â̛̝̜̄̃ ̛́̎̕̕̚ā̊́́̆̌l̟̙̞̃̒́i̖̇̎̃̀̋q̟̇̒̆́̊ủ́̌̇̑̚ã̛̘̉̐̚.̛́̏̐̍̊ |
+ U̝̙̎̈̐̆t̜̍̌̀̔̏ ̞̉̍̇̈̃e̟̟̊̄̕̕n̝̜̒̓̆̕i̖̒̌̅̇̚m̞̊̃̔̊̂ ̛̜̊̎̄̂a̘̜̋̒̚̚d̟̊̎̇̂̍ ̜̖̏̑̉̕m̜̒̎̅̄̚i̝̖̓̂̍̕n̙̉̒̑̀̔ỉ̖̝̌̒́m̛̖̘̅̆̎ ̖̉̎̒̌̕v̖̞̀̔́̎e̖̙̗̒̎̉n̛̗̝̎̀̂ȉ̞̗̒̕̚ȧ̟̜̝̅̚m̆̉̐̐̇̈,̏̐̎́̍́ ̜̞̙̘̏̆q̙̖̙̅̓̂ủ̇́̀̔̚í̙̟̟̏̐s̖̝̍̏̂̇ ̛̘̋̈̕̕ń̛̞̜̜̎o̗̜̔̔̈̆s̞̘̘̄̒̋t̛̅̋́̔̈ȓ̓̒́̇̅ủ̜̄̃̒̍d̙̝̘̊̏̚ ̛̟̞̄́̔e̛̗̝̍̃̀x̞̖̃̄̂̅e̖̅̇̐̔̃r̗̞̖̔̎̚c̘̜̖̆̊̏ï̙̝̙̂̕t̖̏́̓̋̂ă̖̄̆̑̒t̜̟̍̉̑̏i̛̞̞̘̒̑ǒ̜̆̅̃̉ṅ̖̜̒̎̚ |
+ u̗̞̓̔̈̏ĺ̟̝́̎̚l̛̜̅̌̎̆a̒̑̆̔̇̃m̜̗̈̊̎̚ċ̘̋̇̂̚ơ̟̖̊́̕ ̖̟̍̉̏̚l̙̔̓̀̅̏ä̞̗̘̙̅ḃ̟̎̄̃̕o̞̎̓̓̓̚r̗̜̊̓̈̒ï̗̜̃̃̅s̀̒̌̂̎̂ ̖̗̗̋̎̐n̝̟̝̘̄̚i̜̒̀̒̐̕s̘̘̄̊̃̀ī̘̜̏̌̕ ̗̖̞̐̈̒ư̙̞̄́̌t̟̘̖̙̊̚ ̌̅̋̆̚̚ä̇̊̇̕̕l̝̞̘̋̔̅i̍̋́̆̑̈q̛̆̐̈̐̚ư̏̆̊́̚î̜̝̑́̊p̗̓̅̑̆̏ ̆́̓̔̋̋e̟̊̋̏̓̚x̗̍̑̊̎̈ ̟̞̆̄̂̍ë̄̎̄̃̅a̛̜̅́̃̈ ̔̋̀̎̐̀c̖̖̍̀̒̂ơ̛̙̖̄̒m̘̔̍̏̆̕ḿ̖̙̝̏̂ȍ̓̋̈̀̕d̆̂̊̅̓̚o̖̔̌̑̚̕ ̙̆́̔̊̒c̖̘̖̀̄̍o̓̄̑̐̓̒ñ̞̒̎̈̚s̞̜̘̈̄̄e̙̊̀̇̌̋q̐̒̓́̔̃ư̗̟̔̔̚å̖̙̞̄̏t̛̙̟̒̇̏.̙̗̓̃̓̎ |
+ D̜̖̆̏̌̌ư̑̃̌̍̕i̝̊̊̊̊̄s̛̙̒́̌̇ ̛̃̔̄̆̌ă̘̔̅̅̀ú̟̟̟̃̃t̟̂̄̈̈̃e̘̅̌̒̂̆ ̖̟̐̉̉̌î̟̟̙̜̇r̛̙̞̗̄̌ú̗̗̃̌̎r̛̙̘̉̊̕e̒̐̔̃̓̋ ̊̊̍̋̑̉d̛̝̙̉̀̓o̘̜̐̐̓̐l̞̋̌̆̍́o̊̊̐̃̃̚ṙ̛̖̘̃̕ ̞̊̀̍̒̕ȉ́̑̐̇̅ǹ̜̗̜̞̏ ̛̜̐̄̄̚r̜̖̈̇̅̋ĕ̗̉̃̔̚p̟̝̀̓̔̆r̜̈̆̇̃̃e̘̔̔̏̎̓h̗̒̉̑̆̚ė̛̘̘̈̐n̘̂̀̒̕̕d̗̅̂̋̅́ê̗̜̜̜̕r̟̋̄̐̅̂i̛̔̌̒̂̕t̛̗̓̎̀̎ ̙̗̀̉̂̚ȉ̟̗̐̓̚n̙̂̍̏̓̉ ̙̘̊̋̍̕v̜̖̀̎̆̐ő̜̆̉̃̎l̑̋̒̉̔̆ư̙̓̓́̚p̝̘̖̎̏̒t̛̘̝̞̂̓ȁ̘̆̔́̊t̖̝̉̒̐̎e̞̟̋̀̅̄ ̆̌̃̀̑̔v̝̘̝̍̀̇ȅ̝̊̄̓̕l̞̝̑̔̂̋ĭ̝̄̅̆̍t̝̜̉̂̈̇ |
+ ē̟̊̇̕̚s̖̘̘̒̄̑s̛̘̀̊̆̇e̛̝̘̒̏̚ ̉̅̑̂̐̎c̛̟̙̎̋̓i̜̇̒̏̆̆l̟̄́̆̊̌l̍̊̋̃̆̌ủ̗̙̒̔̚m̛̘̘̖̅̍ ̖̙̈̎̂̕d̞̟̏̋̈̔ơ̟̝̌̃̄l̗̙̝̂̉̒õ̒̃̄̄̚ŕ̗̏̏̊̍ê̞̝̞̋̈ ̜̔̒̎̃̚e̞̟̞̒̃̄ư̖̏̄̑̃ ̛̗̜̄̓̎f̛̖̞̅̓̃ü̞̏̆̋̕g̜̝̞̑̑̆i̛̘̐̐̅̚à̜̖̌̆̎t̙̙̎̉̂̍ ̋̔̈̎̎̉n̞̓́̔̊̕ư̘̅̋̔̚l̗̍̒̄̀̚l̞̗̘̙̓̍â̘̔̒̎̚ ̖̓̋̉̃̆p̛̛̘̋̌̀ä̙̔́̒̕r̟̟̖̋̐̋ì̗̙̎̓̓ȃ̔̋̑̚̕t̄́̎̓̂̋ư̏̈̂̑̃r̖̓̋̊̚̚.̒̆̑̆̊̎ ̘̜̍̐̂̚E̞̅̐̇́̂x̄́̈̌̉̕ć̘̃̉̃̕è̘̂̑̏̑p̝̘̑̂̌̆t̔̐̅̍̌̂ȇ̞̈̐̚̕ű̝̞̜́̚ŕ̗̝̉̆́ |
+ š̟́̔̏̀ȉ̝̟̝̏̅n̑̆̇̒̆̚t̝̒́̅̋̏ ̗̑̌̋̇̚ơ̙̗̟̆̅c̙̞̙̎̊̎c̘̟̍̔̊̊a̛̒̓̉́̐e̜̘̙̒̅̇ć̝̝̂̇̕ả̓̍̎̂̚t̗̗̗̟̒̃ ̘̒̓̐̇́c̟̞̉̐̓̄ȕ̙̗̅́̏p̛̍̋̈́̅i̖̓̒̍̈̄d̞̃̈̌̆̐a̛̗̝̎̋̉t̞̙̀̊̆̇a̛̙̒̆̉̚t̜̟̘̉̓̚ ̝̘̗̐̇̕n̛̘̑̏̂́ō̑̋̉̏́ň̞̊̆̄̃ ̙̙̙̜̄̏p̒̆̋̋̓̏r̖̖̅̉́̚ơ̜̆̑̈̚i̟̒̀̃̂̌d̛̏̃̍̋̚ë̖̞̙̗̓n̛̘̓̒̅̎t̟̗̙̊̆̚,̘̙̔̊̚̕ ̟̗̘̜̑̔s̜̝̍̀̓̌û̞̙̅̇́n̘̗̝̒̃̎t̗̅̀̅̊̈ ̗̖̅̅̀̄i̛̖̍̅̋̂n̙̝̓̓̎̚ ̞̋̅̋̃̚c̗̒̀̆̌̎ū̞̂̑̌̓ĺ̛̐̍̑́p̝̆̌̎̈̚a̖̙̒̅̈̌ ̝̝̜̂̈̀q̝̖̔̍̒̚ư̔̐̂̎̊ǐ̛̟̖̘̕ |
+ o̖̜̔̋̅̚f̛̊̀̉́̕f̏̉̀̔̃̃i̘̍̎̐̔̎c̙̅̑̂̐̅ȋ̛̜̀̒̚a̋̍̇̏̀̋ ̖̘̒̅̃̒d̗̘̓̈̇̋é̝́̎̒̄š̙̒̊̉̋e̖̓̐̀̍̕r̗̞̂̅̇̄ù̘̇̐̉̀n̐̑̀̄̍̐t̟̀̂̊̄̚ ̟̝̂̍̏́m̜̗̈̂̏̚ő̞̊̑̇̒l̘̑̏́̔̄l̛̛̇̃̋̊i̓̋̒̃̉̌t̛̗̜̏̀̋ ̙̟̒̂̌̐a̙̝̔̆̏̅n̝̙̙̗̆̅i̍̔́̊̃̕m̖̝̟̒̍̚ ̛̃̃̑̌́ǐ̘̉̔̅̚d̝̗̀̌̏̒ ̖̝̓̑̊̚ȇ̞̟̖̌̕š̙̙̈̔̀t̂̉̒̍̄̄ ̝̗̊̋̌̄l̛̞̜̙̘̔å̝̍̂̍̅b̜̆̇̈̉̌ǒ̜̙̎̃̆r̝̀̄̍́̕ư̋̊́̊̕m̜̗̒̐̕̚.̟̘̀̒̌̚ |
+ {1:~ }|
+ |
+ ]]}
+ end)
end)
describe('multibyte rendering: statusline', function()
diff --git a/test/functional/ui/multigrid_spec.lua b/test/functional/ui/multigrid_spec.lua
index 4e5e9c3a71..b30aa67fd3 100644
--- a/test/functional/ui/multigrid_spec.lua
+++ b/test/functional/ui/multigrid_spec.lua
@@ -77,18 +77,18 @@ describe('ext_multigrid', function()
command('vsplit')
screen:expect{grid=[[
## grid 1
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
{11:[No Name] }{12:[No Name] }|
[3:-----------------------------------------------------]|
## grid 2
@@ -129,18 +129,18 @@ describe('ext_multigrid', function()
command('split')
screen:expect{grid=[[
## grid 1
- [4:--------------------------]{12:│}[5:--------------------------]|
- [4:--------------------------]{12:│}[5:--------------------------]|
- [4:--------------------------]{12:│}[5:--------------------------]|
- [4:--------------------------]{12:│}[5:--------------------------]|
- [4:--------------------------]{12:│}[5:--------------------------]|
- [4:--------------------------]{12:│}[5:--------------------------]|
- [4:--------------------------]{12:│}{11:[No Name] }|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
+ [4:--------------------------]│[5:--------------------------]|
+ [4:--------------------------]│[5:--------------------------]|
+ [4:--------------------------]│[5:--------------------------]|
+ [4:--------------------------]│[5:--------------------------]|
+ [4:--------------------------]│[5:--------------------------]|
+ [4:--------------------------]│[5:--------------------------]|
+ [4:--------------------------]│{11:[No Name] }|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
{12:[No Name] [No Name] }|
[3:-----------------------------------------------------]|
## grid 2
@@ -300,12 +300,12 @@ describe('ext_multigrid', function()
command('vsp')
screen:expect{grid=[[
## grid 1
- [6:--------------------]{12:│}[5:----------------]{12:│}[4:---------------]|
- [6:--------------------]{12:│}[5:----------------]{12:│}[4:---------------]|
- [6:--------------------]{12:│}[5:----------------]{12:│}[4:---------------]|
- [6:--------------------]{12:│}[5:----------------]{12:│}[4:---------------]|
- [6:--------------------]{12:│}[5:----------------]{12:│}[4:---------------]|
- [6:--------------------]{12:│}[5:----------------]{12:│}[4:---------------]|
+ [6:--------------------]│[5:----------------]│[4:---------------]|
+ [6:--------------------]│[5:----------------]│[4:---------------]|
+ [6:--------------------]│[5:----------------]│[4:---------------]|
+ [6:--------------------]│[5:----------------]│[4:---------------]|
+ [6:--------------------]│[5:----------------]│[4:---------------]|
+ [6:--------------------]│[5:----------------]│[4:---------------]|
{11:[No Name] }{12:[No Name] [No Name] }|
[2:-----------------------------------------------------]|
[2:-----------------------------------------------------]|
@@ -347,12 +347,12 @@ describe('ext_multigrid', function()
insert('hello')
screen:expect{grid=[[
## grid 1
- [6:--------------------]{12:│}[5:----------------]{12:│}[4:---------------]|
- [6:--------------------]{12:│}[5:----------------]{12:│}[4:---------------]|
- [6:--------------------]{12:│}[5:----------------]{12:│}[4:---------------]|
- [6:--------------------]{12:│}[5:----------------]{12:│}[4:---------------]|
- [6:--------------------]{12:│}[5:----------------]{12:│}[4:---------------]|
- [6:--------------------]{12:│}[5:----------------]{12:│}[4:---------------]|
+ [6:--------------------]│[5:----------------]│[4:---------------]|
+ [6:--------------------]│[5:----------------]│[4:---------------]|
+ [6:--------------------]│[5:----------------]│[4:---------------]|
+ [6:--------------------]│[5:----------------]│[4:---------------]|
+ [6:--------------------]│[5:----------------]│[4:---------------]|
+ [6:--------------------]│[5:----------------]│[4:---------------]|
{11:[No Name] [+] }{12:[No Name] [+] [No Name] [+] }|
[2:-----------------------------------------------------]|
[2:-----------------------------------------------------]|
@@ -467,18 +467,18 @@ describe('ext_multigrid', function()
command('vsp')
screen:expect{grid=[[
## grid 1
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
{11:[No Name] }{12:[No Name] }|
[3:-----------------------------------------------------]|
## grid 2
@@ -516,18 +516,18 @@ describe('ext_multigrid', function()
command('vertical resize 10')
screen:expect{grid=[[
## grid 1
- [4:----------]{12:│}[2:------------------------------------------]|
- [4:----------]{12:│}[2:------------------------------------------]|
- [4:----------]{12:│}[2:------------------------------------------]|
- [4:----------]{12:│}[2:------------------------------------------]|
- [4:----------]{12:│}[2:------------------------------------------]|
- [4:----------]{12:│}[2:------------------------------------------]|
- [4:----------]{12:│}[2:------------------------------------------]|
- [4:----------]{12:│}[2:------------------------------------------]|
- [4:----------]{12:│}[2:------------------------------------------]|
- [4:----------]{12:│}[2:------------------------------------------]|
- [4:----------]{12:│}[2:------------------------------------------]|
- [4:----------]{12:│}[2:------------------------------------------]|
+ [4:----------]│[2:------------------------------------------]|
+ [4:----------]│[2:------------------------------------------]|
+ [4:----------]│[2:------------------------------------------]|
+ [4:----------]│[2:------------------------------------------]|
+ [4:----------]│[2:------------------------------------------]|
+ [4:----------]│[2:------------------------------------------]|
+ [4:----------]│[2:------------------------------------------]|
+ [4:----------]│[2:------------------------------------------]|
+ [4:----------]│[2:------------------------------------------]|
+ [4:----------]│[2:------------------------------------------]|
+ [4:----------]│[2:------------------------------------------]|
+ [4:----------]│[2:------------------------------------------]|
{11:<No Name] }{12:[No Name] }|
[3:-----------------------------------------------------]|
## grid 2
@@ -565,18 +565,18 @@ describe('ext_multigrid', function()
command('sp')
screen:expect{grid=[[
## grid 1
- [5:--------------------------]{12:│}[2:--------------------------]|
- [5:--------------------------]{12:│}[2:--------------------------]|
- [5:--------------------------]{12:│}[2:--------------------------]|
- [5:--------------------------]{12:│}[2:--------------------------]|
- [5:--------------------------]{12:│}[2:--------------------------]|
- [5:--------------------------]{12:│}[2:--------------------------]|
- {11:[No Name] }{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
+ [5:--------------------------]│[2:--------------------------]|
+ [5:--------------------------]│[2:--------------------------]|
+ [5:--------------------------]│[2:--------------------------]|
+ [5:--------------------------]│[2:--------------------------]|
+ [5:--------------------------]│[2:--------------------------]|
+ [5:--------------------------]│[2:--------------------------]|
+ {11:[No Name] }│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
{12:[No Name] [No Name] }|
[3:-----------------------------------------------------]|
## grid 2
@@ -611,18 +611,18 @@ describe('ext_multigrid', function()
insert('hello')
screen:expect{grid=[[
## grid 1
- [5:--------------------------]{12:│}[2:--------------------------]|
- [5:--------------------------]{12:│}[2:--------------------------]|
- [5:--------------------------]{12:│}[2:--------------------------]|
- [5:--------------------------]{12:│}[2:--------------------------]|
- [5:--------------------------]{12:│}[2:--------------------------]|
- [5:--------------------------]{12:│}[2:--------------------------]|
- {11:[No Name] [+] }{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
+ [5:--------------------------]│[2:--------------------------]|
+ [5:--------------------------]│[2:--------------------------]|
+ [5:--------------------------]│[2:--------------------------]|
+ [5:--------------------------]│[2:--------------------------]|
+ [5:--------------------------]│[2:--------------------------]|
+ [5:--------------------------]│[2:--------------------------]|
+ {11:[No Name] [+] }│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
{12:[No Name] [+] [No Name] [+] }|
[3:-----------------------------------------------------]|
## grid 2
@@ -659,18 +659,18 @@ describe('ext_multigrid', function()
command('vsp')
screen:expect{grid=[[
## grid 1
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
{11:[No Name] }{12:[No Name] }|
[3:-----------------------------------------------------]|
## grid 2
@@ -1056,12 +1056,12 @@ describe('ext_multigrid', function()
command('vsp')
screen:expect{grid=[[
## grid 1
- [5:--------------------------]{12:│}[4:--------------------------]|
- [5:--------------------------]{12:│}[4:--------------------------]|
- [5:--------------------------]{12:│}[4:--------------------------]|
- [5:--------------------------]{12:│}[4:--------------------------]|
- [5:--------------------------]{12:│}[4:--------------------------]|
- [5:--------------------------]{12:│}[4:--------------------------]|
+ [5:--------------------------]│[4:--------------------------]|
+ [5:--------------------------]│[4:--------------------------]|
+ [5:--------------------------]│[4:--------------------------]|
+ [5:--------------------------]│[4:--------------------------]|
+ [5:--------------------------]│[4:--------------------------]|
+ [5:--------------------------]│[4:--------------------------]|
{11:[No Name] }{12:[No Name] }|
[2:-----------------------------------------------------]|
[2:-----------------------------------------------------]|
@@ -1097,12 +1097,12 @@ describe('ext_multigrid', function()
feed(":echoerr 'very' | echoerr 'much' | echoerr 'fail'<cr>")
screen:expect{grid=[[
## grid 1
- [5:--------------------------]{12:│}[4:--------------------------]|
- [5:--------------------------]{12:│}[4:--------------------------]|
- [5:--------------------------]{12:│}[4:--------------------------]|
- [5:--------------------------]{12:│}[4:--------------------------]|
- [5:--------------------------]{12:│}[4:--------------------------]|
- [5:--------------------------]{12:│}[4:--------------------------]|
+ [5:--------------------------]│[4:--------------------------]|
+ [5:--------------------------]│[4:--------------------------]|
+ [5:--------------------------]│[4:--------------------------]|
+ [5:--------------------------]│[4:--------------------------]|
+ [5:--------------------------]│[4:--------------------------]|
+ [5:--------------------------]│[4:--------------------------]|
{11:[No Name] }{12:[No Name] }|
[2:-----------------------------------------------------]|
[2:-----------------------------------------------------]|
@@ -1141,12 +1141,12 @@ describe('ext_multigrid', function()
feed('<cr>')
screen:expect{grid=[[
## grid 1
- [5:--------------------------]{12:│}[4:--------------------------]|
- [5:--------------------------]{12:│}[4:--------------------------]|
- [5:--------------------------]{12:│}[4:--------------------------]|
- [5:--------------------------]{12:│}[4:--------------------------]|
- [5:--------------------------]{12:│}[4:--------------------------]|
- [5:--------------------------]{12:│}[4:--------------------------]|
+ [5:--------------------------]│[4:--------------------------]|
+ [5:--------------------------]│[4:--------------------------]|
+ [5:--------------------------]│[4:--------------------------]|
+ [5:--------------------------]│[4:--------------------------]|
+ [5:--------------------------]│[4:--------------------------]|
+ [5:--------------------------]│[4:--------------------------]|
{11:[No Name] }{12:[No Name] }|
[2:-----------------------------------------------------]|
[2:-----------------------------------------------------]|
@@ -1242,12 +1242,12 @@ describe('ext_multigrid', function()
feed("<c-c>")
screen:expect{grid=[[
## grid 1
- [5:--------------------------]{12:│}[4:--------------------------]|
- [5:--------------------------]{12:│}[4:--------------------------]|
- [5:--------------------------]{12:│}[4:--------------------------]|
- [5:--------------------------]{12:│}[4:--------------------------]|
- [5:--------------------------]{12:│}[4:--------------------------]|
- [5:--------------------------]{12:│}[4:--------------------------]|
+ [5:--------------------------]│[4:--------------------------]|
+ [5:--------------------------]│[4:--------------------------]|
+ [5:--------------------------]│[4:--------------------------]|
+ [5:--------------------------]│[4:--------------------------]|
+ [5:--------------------------]│[4:--------------------------]|
+ [5:--------------------------]│[4:--------------------------]|
{11:[No Name] }{12:[No Name] }|
[2:-----------------------------------------------------]|
[2:-----------------------------------------------------]|
@@ -1285,18 +1285,18 @@ describe('ext_multigrid', function()
command('vsp')
screen:expect{grid=[[
## grid 1
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
{11:[No Name] }{12:[No Name] }|
[3:-----------------------------------------------------]|
## grid 2
@@ -1453,17 +1453,17 @@ describe('ext_multigrid', function()
screen:expect{grid=[[
## grid 1
{7: }{18:2}{7: [No Name] }{16: }{17:2}{16: [No Name] }{12: }{16:X}|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
{11:[No Name] }{12:[No Name] }|
[3:-----------------------------------------------------]|
## grid 2
@@ -1637,18 +1637,18 @@ describe('ext_multigrid', function()
command('tabclose')
screen:expect{grid=[[
## grid 1
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
{11:[No Name] }{12:[No Name] }|
[3:-----------------------------------------------------]|
## grid 2
@@ -1915,7 +1915,7 @@ describe('ext_multigrid', function()
{1:~ }|
]]}
- meths.input_mouse('left', 'press', '', 1,6, 20)
+ meths.input_mouse('left', 'press', '', 1, 6, 20)
-- TODO(bfredl): "batching" input_mouse is formally not supported yet.
-- Normally it should work fine in async context when nvim is not blocked,
-- but add a poke_eventloop be sure.
@@ -1960,13 +1960,13 @@ describe('ext_multigrid', function()
[4:-----------------------------------------------------]|
[4:-----------------------------------------------------]|
{12:[No Name] [+] }|
- [5:--------------------------]{12:│}[2:--------------------------]|
- [5:--------------------------]{12:│}[2:--------------------------]|
- [5:--------------------------]{12:│}[2:--------------------------]|
- [5:--------------------------]{12:│}[2:--------------------------]|
- [5:--------------------------]{12:│}[2:--------------------------]|
- [5:--------------------------]{12:│}[2:--------------------------]|
- [5:--------------------------]{12:│}[2:--------------------------]|
+ [5:--------------------------]│[2:--------------------------]|
+ [5:--------------------------]│[2:--------------------------]|
+ [5:--------------------------]│[2:--------------------------]|
+ [5:--------------------------]│[2:--------------------------]|
+ [5:--------------------------]│[2:--------------------------]|
+ [5:--------------------------]│[2:--------------------------]|
+ [5:--------------------------]│[2:--------------------------]|
{11:[No Name] [+] }{12:[No Name] [+] }|
[3:-----------------------------------------------------]|
## grid 2
@@ -2002,13 +2002,13 @@ describe('ext_multigrid', function()
[4:-----------------------------------------------------]|
[4:-----------------------------------------------------]|
{12:[No Name] [+] }|
- [5:------------------------------]{12:│}[2:----------------------]|
- [5:------------------------------]{12:│}[2:----------------------]|
- [5:------------------------------]{12:│}[2:----------------------]|
- [5:------------------------------]{12:│}[2:----------------------]|
- [5:------------------------------]{12:│}[2:----------------------]|
- [5:------------------------------]{12:│}[2:----------------------]|
- [5:------------------------------]{12:│}[2:----------------------]|
+ [5:------------------------------]│[2:----------------------]|
+ [5:------------------------------]│[2:----------------------]|
+ [5:------------------------------]│[2:----------------------]|
+ [5:------------------------------]│[2:----------------------]|
+ [5:------------------------------]│[2:----------------------]|
+ [5:------------------------------]│[2:----------------------]|
+ [5:------------------------------]│[2:----------------------]|
{11:[No Name] [+] }{12:[No Name] [+] }|
[3:-----------------------------------------------------]|
## grid 2
@@ -2049,18 +2049,18 @@ describe('ext_multigrid', function()
screen:expect{grid=[[
## grid 1
- [4:--------------------------]{12:│}[5:--------------------------]|
- [4:--------------------------]{12:│}[5:--------------------------]|
- [4:--------------------------]{12:│}[5:--------------------------]|
- [4:--------------------------]{12:│}[5:--------------------------]|
- [4:--------------------------]{12:│}[5:--------------------------]|
- [4:--------------------------]{12:│}[5:--------------------------]|
- [4:--------------------------]{12:│}{11:[No Name] [+] }|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
- [4:--------------------------]{12:│}[2:--------------------------]|
+ [4:--------------------------]│[5:--------------------------]|
+ [4:--------------------------]│[5:--------------------------]|
+ [4:--------------------------]│[5:--------------------------]|
+ [4:--------------------------]│[5:--------------------------]|
+ [4:--------------------------]│[5:--------------------------]|
+ [4:--------------------------]│[5:--------------------------]|
+ [4:--------------------------]│{11:[No Name] [+] }|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
+ [4:--------------------------]│[2:--------------------------]|
{12:[No Name] [No Name] }|
[3:-----------------------------------------------------]|
## grid 2
@@ -2092,7 +2092,6 @@ describe('ext_multigrid', function()
{1:~ }|
{1:~ }|
]]}
-
end)
it('has viewport information', function()
@@ -2369,4 +2368,223 @@ describe('ext_multigrid', function()
[2] = {win = {id = 1000}, topline = 6, botline = 12, curline = 10, curcol = 1, linecount = 11},
}}
end)
+
+ it('with winbar', function()
+ command 'split'
+ command 'setlocal winbar=very\\ bar'
+ screen:expect{grid=[[
+ ## grid 1
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ {11:[No Name] }|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {12:[No Name] }|
+ [3:-----------------------------------------------------]|
+ ## grid 2
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ |
+ ## grid 4
+ {7:very bar }|
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], win_viewport={
+ [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1};
+ [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1};
+ }}
+ end)
+
+ it('with winbar dragging statusline with mouse works correctly', function()
+ meths.set_option('winbar', 'Set Up The Bars')
+ command('split')
+ screen:expect([[
+ ## grid 1
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ {11:[No Name] }|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {12:[No Name] }|
+ [3:-----------------------------------------------------]|
+ ## grid 2
+ {7:Set Up The Bars }|
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ |
+ ## grid 4
+ {7:Set Up The Bars }|
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+
+ meths.input_mouse('left', 'press', '', 1, 6, 20)
+ poke_eventloop()
+ meths.input_mouse('left', 'drag', '', 1, 7, 20)
+ screen:expect([[
+ ## grid 1
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ {11:[No Name] }|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {12:[No Name] }|
+ [3:-----------------------------------------------------]|
+ ## grid 2
+ {7:Set Up The Bars }|
+ |
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ |
+ ## grid 4
+ {7:Set Up The Bars }|
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+
+ meths.input_mouse('left', 'drag', '', 1, 4, 20)
+ screen:expect([[
+ ## grid 1
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ {11:[No Name] }|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {12:[No Name] }|
+ [3:-----------------------------------------------------]|
+ ## grid 2
+ {7:Set Up The Bars }|
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ |
+ ## grid 4
+ {7:Set Up The Bars }|
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ ]])
+
+ meths.input_mouse('left', 'press', '', 1, 12, 10)
+ poke_eventloop()
+ meths.input_mouse('left', 'drag', '', 1, 10, 10)
+ screen:expect([[
+ ## grid 1
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ {11:[No Name] }|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {12:[No Name] }|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ ## grid 2
+ {7:Set Up The Bars }|
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ |
+ |
+ |
+ ## grid 4
+ {7:Set Up The Bars }|
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ ]])
+ eq(3, meths.get_option('cmdheight'))
+
+ meths.input_mouse('left', 'drag', '', 1, 12, 10)
+ screen:expect([[
+ ## grid 1
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ {11:[No Name] }|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {12:[No Name] }|
+ [3:-----------------------------------------------------]|
+ ## grid 2
+ {7:Set Up The Bars }|
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ |
+ ## grid 4
+ {7:Set Up The Bars }|
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ ]])
+ eq(1, meths.get_option('cmdheight'))
+ end)
end)
diff --git a/test/functional/ui/options_spec.lua b/test/functional/ui/options_spec.lua
index 2f113f6ac6..c2b0bcdb64 100644
--- a/test/functional/ui/options_spec.lua
+++ b/test/functional/ui/options_spec.lua
@@ -4,6 +4,7 @@ local clear = helpers.clear
local command = helpers.command
local eq = helpers.eq
local shallowcopy = helpers.shallowcopy
+local eval = helpers.eval
describe('UI receives option updates', function()
local screen
@@ -62,17 +63,18 @@ describe('UI receives option updates', function()
end
screen:attach()
screen:expect(function()
- eq({'mouse_off'}, evs)
+ eq({'mouse_on'}, evs)
end)
- command("set mouse=nvi")
+ command("set mouse=")
+ command("set mouse&")
screen:expect(function()
- eq({'mouse_off','mouse_on'}, evs)
+ eq({'mouse_on','mouse_off', 'mouse_on'}, evs)
end)
screen:detach()
- eq({'mouse_off','mouse_on'}, evs)
+ eq({'mouse_on','mouse_off', 'mouse_on'}, evs)
screen:attach()
screen:expect(function()
- eq({'mouse_off','mouse_on','mouse_on'}, evs)
+ eq({'mouse_on','mouse_off','mouse_on', 'mouse_on'}, evs)
end)
end)
@@ -86,6 +88,12 @@ describe('UI receives option updates', function()
eq(expected, screen.options)
end)
+ command("set pumblend=-1")
+ expected.pumblend = 0
+ screen:expect(function()
+ eq(expected, screen.options)
+ end)
+
command("set guifont=Comic\\ Sans")
expected.guifont = "Comic Sans"
screen:expect(function()
@@ -168,3 +176,42 @@ describe('UI receives option updates', function()
it('from startup options with --headless', function() startup_test(true) end)
it('from startup options with --embed', function() startup_test(false) end)
end)
+
+describe('UI can set terminal option', function()
+ local screen
+ before_each(function()
+ -- by default we implicity "--cmd 'set bg=light'" which ruins everything
+ clear{args_rm={'--cmd'}}
+ screen = Screen.new(20,5)
+ end)
+
+ it('term_background', function()
+ eq('dark', eval '&background')
+
+ screen:attach {term_background='light'}
+ eq('light', eval '&background')
+ end)
+
+ it("term_background but not if 'background' already set by user", function()
+ eq('dark', eval '&background')
+ command 'set background=dark'
+
+ screen:attach {term_background='light'}
+
+ eq('dark', eval '&background')
+ end)
+
+ it('term_name', function()
+ eq('nvim', eval '&term')
+
+ screen:attach {term_name='xterm'}
+ eq('xterm', eval '&term')
+ end)
+
+ it('term_colors', function()
+ eq('256', eval '&t_Co')
+
+ screen:attach {term_colors=8}
+ eq('8', eval '&t_Co')
+ end)
+end)
diff --git a/test/functional/ui/output_spec.lua b/test/functional/ui/output_spec.lua
index 50e5dfac84..71c6410013 100644
--- a/test/functional/ui/output_spec.lua
+++ b/test/functional/ui/output_spec.lua
@@ -9,6 +9,7 @@ local feed_command = helpers.feed_command
local iswin = helpers.iswin
local clear = helpers.clear
local command = helpers.command
+local testprg = helpers.testprg
local nvim_dir = helpers.nvim_dir
local has_powershell = helpers.has_powershell
local set_shell_powershell = helpers.set_shell_powershell
@@ -54,7 +55,7 @@ describe("shell command :!", function()
if 'openbsd' == helpers.uname() then
pending('FIXME #10804')
end
- child_session.feed_data(":!"..nvim_dir.."/shell-test REP 30001 foo\n")
+ child_session.feed_data((":!%s REP 30001 foo\n"):format(testprg('shell-test')))
-- If we observe any line starting with a dot, then throttling occurred.
-- Avoid false failure on slow systems.
@@ -207,12 +208,7 @@ describe("shell command :!", function()
it('handles multibyte sequences split over buffer boundaries', function()
command('cd '..nvim_dir)
- local cmd
- if iswin() then
- cmd = '!shell-test UTF-8 '
- else
- cmd = '!./shell-test UTF-8'
- end
+ local cmd = iswin() and '!shell-test UTF-8 ' or '!./shell-test UTF-8'
feed_command(cmd)
-- Note: only the first example of split composed char works
screen:expect([[
diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua
index 4fc5c389e5..7b0005bcf1 100644
--- a/test/functional/ui/popupmenu_spec.lua
+++ b/test/functional/ui/popupmenu_spec.lua
@@ -10,6 +10,8 @@ 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
+local exec = helpers.exec
describe('ui/ext_popupmenu', function()
local screen
@@ -25,6 +27,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 +372,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()
@@ -846,72 +954,72 @@ describe('builtin popupmenu', function()
insert('aaa aab aac\n')
feed(':vsplit<cr>')
screen:expect([[
- aaa aab aac {3:│}aaa aab aac|
- ^ {3:│} |
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
+ aaa aab aac │aaa aab aac|
+ ^ │ |
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
{4:[No Name] [+] }{3:<Name] [+] }|
:vsplit |
]])
feed('ibbb a<c-x><c-n>')
screen:expect([[
- aaa aab aac {3:│}aaa aab aac|
- bbb aaa^ {3:│}bbb aaa |
- {1:~ }{s: aaa }{1: }{3:│}{1:~ }|
- {1:~ }{n: aab }{1: }{3:│}{1:~ }|
- {1:~ }{n: aac }{1: }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
+ aaa aab aac │aaa aab aac|
+ bbb aaa^ │bbb aaa |
+ {1:~ }{s: aaa }{1: }│{1:~ }|
+ {1:~ }{n: aab }{1: }│{1:~ }|
+ {1:~ }{n: aac }{1: }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
{4:[No Name] [+] }{3:<Name] [+] }|
{2:-- }{5:match 1 of 3} |
]])
feed('<esc><c-w><c-w>oc a<c-x><c-n>')
screen:expect([[
- aaa aab aac{3:│}aaa aab aac |
- bbb aaa {3:│}bbb aaa |
- c aaa {3:│}c aaa^ |
- {1:~ }{3:│}{1:~}{s: aaa }{1: }|
- {1:~ }{3:│}{1:~}{n: aab }{1: }|
- {1:~ }{3:│}{1:~}{n: aac }{1: }|
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
- {1:~ }{3:│}{1:~ }|
+ aaa aab aac│aaa aab aac |
+ bbb aaa │bbb aaa |
+ c aaa │c aaa^ |
+ {1:~ }│{1:~}{s: aaa }{1: }|
+ {1:~ }│{1:~}{n: aab }{1: }|
+ {1:~ }│{1:~}{n: aac }{1: }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
{3:<Name] [+] }{4:[No Name] [+] }|
{2:-- }{5:match 1 of 3} |
]])
@@ -1845,7 +1953,7 @@ describe('builtin popupmenu', function()
]])
end)
- it('wildoptions=pum with scrolled mesages ', function()
+ it('wildoptions=pum with scrolled messages ', function()
screen:try_resize(40,10)
command('set wildmenu')
command('set wildoptions=pum')
@@ -2084,7 +2192,7 @@ describe('builtin popupmenu', function()
{20:-- Keyword Local completion (^N^P) }{21:match 1 of 65} |
]])
- -- can disable blending for indiviual attribute. For instance current
+ -- can disable blending for individual attribute. For instance current
-- selected item. (also tests that `hi Pmenu*` take immediate effect)
command('hi PMenuSel blend=0')
screen:expect([[
@@ -2252,6 +2360,103 @@ describe('builtin popupmenu', function()
{2:-- INSERT --} |
]])
end)
+
+ it('supports mousemodel=popup', function()
+ screen:try_resize(32, 6)
+ exec([[
+ call setline(1, 'popup menu test')
+ set mouse=a mousemodel=popup
+
+ aunmenu PopUp
+ menu PopUp.foo :let g:menustr = 'foo'<CR>
+ menu PopUp.bar :let g:menustr = 'bar'<CR>
+ menu PopUp.baz :let g:menustr = 'baz'<CR>
+ ]])
+ meths.input_mouse('right', 'press', '', 0, 0, 4)
+ screen:expect([[
+ ^popup menu test |
+ {1:~ }{n: foo }{1: }|
+ {1:~ }{n: bar }{1: }|
+ {1:~ }{n: baz }{1: }|
+ {1:~ }|
+ |
+ ]])
+ feed('<Down>')
+ screen:expect([[
+ ^popup menu test |
+ {1:~ }{s: foo }{1: }|
+ {1:~ }{n: bar }{1: }|
+ {1:~ }{n: baz }{1: }|
+ {1:~ }|
+ |
+ ]])
+ feed('<Down>')
+ screen:expect([[
+ ^popup menu test |
+ {1:~ }{n: foo }{1: }|
+ {1:~ }{s: bar }{1: }|
+ {1:~ }{n: baz }{1: }|
+ {1:~ }|
+ |
+ ]])
+ feed('<CR>')
+ screen:expect([[
+ ^popup menu test |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ :let g:menustr = 'bar' |
+ ]])
+ eq('bar', meths.get_var('menustr'))
+ meths.input_mouse('right', 'press', '', 0, 1, 20)
+ screen:expect([[
+ ^popup menu test |
+ {1:~ }|
+ {1:~ }{n: foo }{1: }|
+ {1:~ }{n: bar }{1: }|
+ {1:~ }{n: baz }{1: }|
+ :let g:menustr = 'bar' |
+ ]])
+ meths.input_mouse('left', 'press', '', 0, 4, 22)
+ screen:expect([[
+ ^popup menu test |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ :let g:menustr = 'baz' |
+ ]])
+ eq('baz', meths.get_var('menustr'))
+ meths.input_mouse('right', 'press', '', 0, 0, 4)
+ screen:expect([[
+ ^popup menu test |
+ {1:~ }{n: foo }{1: }|
+ {1:~ }{n: bar }{1: }|
+ {1:~ }{n: baz }{1: }|
+ {1:~ }|
+ :let g:menustr = 'baz' |
+ ]])
+ meths.input_mouse('right', 'drag', '', 0, 3, 6)
+ screen:expect([[
+ ^popup menu test |
+ {1:~ }{n: foo }{1: }|
+ {1:~ }{n: bar }{1: }|
+ {1:~ }{s: baz }{1: }|
+ {1:~ }|
+ :let g:menustr = 'baz' |
+ ]])
+ meths.input_mouse('right', 'release', '', 0, 1, 6)
+ screen:expect([[
+ ^popup menu test |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ :let g:menustr = 'foo' |
+ ]])
+ eq('foo', meths.get_var('menustr'))
+ end)
end)
describe('builtin popupmenu with ui/ext_multigrid', function()
@@ -2343,4 +2548,121 @@ describe('builtin popupmenu with ui/ext_multigrid', function()
{n: 哦哦哦哦哦哦哦哦哦>}{s: }|
]], float_pos={[4] = {{id = -1}, 'NW', 2, 1, 11, false, 100}}})
end)
+
+ it('supports mousemodel=popup', function()
+ screen:try_resize(32, 6)
+ exec([[
+ call setline(1, 'popup menu test')
+ set mouse=a mousemodel=popup
+
+ aunmenu PopUp
+ menu PopUp.foo :let g:menustr = 'foo'<CR>
+ menu PopUp.bar :let g:menustr = 'bar'<CR>
+ menu PopUp.baz :let g:menustr = 'baz'<CR>
+ ]])
+ meths.input_mouse('right', 'press', '', 2, 1, 20)
+ screen:expect({grid=[[
+ ## grid 1
+ [2:--------------------------------]|
+ [2:--------------------------------]|
+ [2:--------------------------------]|
+ [2:--------------------------------]|
+ [2:--------------------------------]|
+ [3:--------------------------------]|
+ ## grid 2
+ ^popup menu test |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ |
+ ## grid 4
+ {n: foo }|
+ {n: bar }|
+ {n: baz }|
+ ]], float_pos={[4] = {{id = -1}, 'NW', 2, 2, 19, false, 100}}})
+ meths.input_mouse('left', 'press', '', 4, 2, 2)
+ screen:expect({grid=[[
+ ## grid 1
+ [2:--------------------------------]|
+ [2:--------------------------------]|
+ [2:--------------------------------]|
+ [2:--------------------------------]|
+ [2:--------------------------------]|
+ [3:--------------------------------]|
+ ## grid 2
+ ^popup menu test |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ :let g:menustr = 'baz' |
+ ]]})
+ eq('baz', meths.get_var('menustr'))
+ meths.input_mouse('right', 'press', '', 2, 0, 4)
+ screen:expect({grid=[[
+ ## grid 1
+ [2:--------------------------------]|
+ [2:--------------------------------]|
+ [2:--------------------------------]|
+ [2:--------------------------------]|
+ [2:--------------------------------]|
+ [3:--------------------------------]|
+ ## grid 2
+ ^popup menu test |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ :let g:menustr = 'baz' |
+ ## grid 4
+ {n: foo }|
+ {n: bar }|
+ {n: baz }|
+ ]], float_pos={[4] = {{id = -1}, 'NW', 2, 1, 3, false, 100}}})
+ meths.input_mouse('right', 'drag', '', 2, 3, 6)
+ screen:expect({grid=[[
+ ## grid 1
+ [2:--------------------------------]|
+ [2:--------------------------------]|
+ [2:--------------------------------]|
+ [2:--------------------------------]|
+ [2:--------------------------------]|
+ [3:--------------------------------]|
+ ## grid 2
+ ^popup menu test |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ :let g:menustr = 'baz' |
+ ## grid 4
+ {n: foo }|
+ {n: bar }|
+ {s: baz }|
+ ]], float_pos={[4] = {{id = -1}, 'NW', 2, 1, 3, false, 100}}})
+ meths.input_mouse('right', 'release', '', 2, 1, 6)
+ screen:expect({grid=[[
+ ## grid 1
+ [2:--------------------------------]|
+ [2:--------------------------------]|
+ [2:--------------------------------]|
+ [2:--------------------------------]|
+ [2:--------------------------------]|
+ [3:--------------------------------]|
+ ## grid 2
+ ^popup menu test |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ :let g:menustr = 'foo' |
+ ]]})
+ eq('foo', meths.get_var('menustr'))
+ end)
end)
diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua
index 61f19c3794..ea98705394 100644
--- a/test/functional/ui/screen.lua
+++ b/test/functional/ui/screen.lua
@@ -75,7 +75,7 @@ local busted = require('busted')
local deepcopy = helpers.deepcopy
local shallowcopy = helpers.shallowcopy
local concat_tables = helpers.concat_tables
-local request, run_session = helpers.request, helpers.run_session
+local run_session = helpers.run_session
local eq = helpers.eq
local dedent = helpers.dedent
local get_session = helpers.get_session
@@ -90,8 +90,6 @@ end
local Screen = {}
Screen.__index = Screen
-local debug_screen
-
local default_timeout_factor = 1
if os.getenv('VALGRIND') then
default_timeout_factor = default_timeout_factor * 3
@@ -123,18 +121,6 @@ do
Screen.colornames = colornames
end
-function Screen.debug(command)
- if not command then
- command = 'pynvim -n -c '
- end
- command = command .. request('vim_eval', '$NVIM_LISTEN_ADDRESS')
- if debug_screen then
- debug_screen:close()
- end
- debug_screen = io.popen(command, 'r')
- debug_screen:read()
-end
-
function Screen.new(width, height)
if not width then
width = 53
@@ -179,6 +165,7 @@ function Screen.new(width, height)
_width = width,
_height = height,
_grids = {},
+ _grid_win_extmarks = {},
_cursor = {
grid = 1, row = 1, col = 1
},
@@ -255,7 +242,7 @@ end
-- canonical order of ext keys, used to generate asserts
local ext_keys = {
'popupmenu', 'cmdline', 'cmdline_block', 'wildmenu_items', 'wildmenu_pos',
- 'messages', 'showmode', 'showcmd', 'ruler', 'float_pos', 'win_viewport'
+ 'messages', 'msg_history', 'showmode', 'showcmd', 'ruler', 'float_pos', 'win_viewport'
}
-- Asserts that the screen state eventually matches an expected state.
@@ -278,6 +265,8 @@ local ext_keys = {
-- attributes in the final state are an error.
-- Use screen:set_default_attr_ids() to define attributes for many
-- expect() calls.
+-- extmarks: Expected win_extmarks accumulated for the grids. For each grid,
+-- the win_extmark messages are accumulated into an array.
-- condition: Function asserting some arbitrary condition. Return value is
-- ignored, throw an error (use eq() or similar) to signal failure.
-- any: Lua pattern string expected to match a screen line. NB: the
@@ -320,7 +309,7 @@ function Screen:expect(expected, attr_ids, ...)
assert(not (attr_ids ~= nil))
local is_key = {grid=true, attr_ids=true, condition=true, mouse_enabled=true,
any=true, mode=true, unchanged=true, intermediate=true,
- reset=true, timeout=true, request_cb=true, hl_groups=true}
+ reset=true, timeout=true, request_cb=true, hl_groups=true, extmarks=true}
for _, v in ipairs(ext_keys) do
is_key[v] = true
end
@@ -394,7 +383,7 @@ function Screen:expect(expected, attr_ids, ...)
for i, row in ipairs(expected_rows) do
msg_expected_rows[i] = row
local m = (row ~= actual_rows[i] and row:match('{MATCH:(.*)}') or nil)
- if row ~= actual_rows[i] and (not m or not actual_rows[i]:match(m)) then
+ if row ~= actual_rows[i] and (not m or not (actual_rows[i] and actual_rows[i]:match(m))) then
msg_expected_rows[i] = '*' .. msg_expected_rows[i]
if i <= #actual_rows then
actual_rows[i] = '*' .. actual_rows[i]
@@ -459,6 +448,25 @@ screen:redraw_debug() to show all intermediate screen states. ]])
end
end
end
+
+ if expected.extmarks ~= nil then
+ for gridid, expected_marks in pairs(expected.extmarks) do
+ local stored_marks = self._grid_win_extmarks[gridid]
+ if stored_marks == nil then
+ return 'no win_extmark for grid '..tostring(gridid)
+ end
+ local status, res = pcall(eq, expected_marks, stored_marks, "extmarks for grid "..tostring(gridid))
+ if not status then
+ return tostring(res)
+ end
+ end
+ for gridid, _ in pairs(self._grid_win_extmarks) do
+ local expected_marks = expected.extmarks[gridid]
+ if expected_marks == nil then
+ return 'unexpected win_extmark for grid '..tostring(gridid)
+ end
+ end
+ end
end, expected)
end
@@ -544,7 +552,7 @@ function Screen:_wait(check, flags)
elseif not checked then
err = check()
if not err and flags.unchanged then
- -- expecting NO screen change: use a shorter timout
+ -- expecting NO screen change: use a shorter timeout
success_seen = true
end
end
@@ -576,16 +584,16 @@ to the test if they make sense.
print([[
warning: Screen changes were received after the expected state. This indicates
-indeterminism in the test. Try adding screen:expect(...) (or wait()) between
-asynchronous (feed(), nvim_input()) and synchronous API calls.
+indeterminism in the test. Try adding screen:expect(...) (or poke_eventloop())
+between asynchronous (feed(), nvim_input()) and synchronous API calls.
- Use screen:redraw_debug() to investigate; it may find relevant intermediate
states that should be added to the test to make it more robust.
- If the purpose of the test is to assert state after some user input sent
with feed(), adding screen:expect() before the feed() will help to ensure
the input is sent when Nvim is in a predictable state. This is preferable
- to wait(), for being closer to real user interaction.
- - wait() can trigger redraws and consequently generate more indeterminism.
- Try removing wait().
+ to poke_eventloop(), for being closer to real user interaction.
+ - poke_eventloop() can trigger redraws and thus generate more indeterminism.
+ Try removing poke_eventloop().
]])
did_warn = true
end
@@ -703,6 +711,7 @@ function Screen:_reset()
self.cmdline_block = {}
self.wildmenu_items = nil
self.wildmenu_pos = nil
+ self._grid_win_extmarks = {}
end
function Screen:_handle_mode_info_set(cursor_style_enabled, mode_info)
@@ -803,6 +812,13 @@ function Screen:_handle_win_close(grid)
self.float_pos[grid] = nil
end
+function Screen:_handle_win_extmark(grid, ...)
+ if self._grid_win_extmarks[grid] == nil then
+ self._grid_win_extmarks[grid] = {}
+ end
+ table.insert(self._grid_win_extmarks[grid], {...})
+end
+
function Screen:_handle_busy_start()
self._busy = true
end
@@ -1067,6 +1083,10 @@ function Screen:_handle_msg_history_show(entries)
self.msg_history = entries
end
+function Screen:_handle_msg_history_clear()
+ self.msg_history = {}
+end
+
function Screen:_clear_block(grid, top, bot, left, right)
for i = top, bot do
self:_clear_row_section(grid, i, left, right)
@@ -1155,7 +1175,7 @@ function Screen:_extstate_repr(attr_state)
local msg_history = {}
for i, entry in ipairs(self.msg_history) do
- messages[i] = {kind=entry[1], content=self:_chunks_repr(entry[2], attr_state)}
+ msg_history[i] = {kind=entry[1], content=self:_chunks_repr(entry[2], attr_state)}
end
local win_viewport = (next(self.win_viewport) and self.win_viewport) or nil
@@ -1560,9 +1580,10 @@ end
function Screen:_equal_attrs(a, b)
return a.bold == b.bold and a.standout == b.standout and
a.underline == b.underline and a.undercurl == b.undercurl and
- a.italic == b.italic and a.reverse == b.reverse and
- a.foreground == b.foreground and a.background == b.background and
- a.special == b.special and a.blend == b.blend and
+ a.underdouble == b.underdouble and a.underdotted == b.underdotted and
+ a.underdashed == b.underdashed and a.italic == b.italic and
+ a.reverse == b.reverse and a.foreground == b.foreground and
+ a.background == b.background and a.special == b.special and a.blend == b.blend and
a.strikethrough == b.strikethrough and
a.fg_indexed == b.fg_indexed and a.bg_indexed == b.bg_indexed
end
diff --git a/test/functional/ui/screen_basic_spec.lua b/test/functional/ui/screen_basic_spec.lua
index 958e137f65..6c872e52d3 100644
--- a/test/functional/ui/screen_basic_spec.lua
+++ b/test/functional/ui/screen_basic_spec.lua
@@ -6,6 +6,7 @@ local insert = helpers.insert
local eq = helpers.eq
local eval = helpers.eval
local iswin = helpers.iswin
+local funcs, meths, exec_lua = helpers.funcs, helpers.meths, helpers.exec_lua
describe('screen', function()
local screen
@@ -127,14 +128,67 @@ local function screen_tests(linegrid)
end)
it('has correct default title with named file', function()
- local expected = (iswin() and 'myfile (C:\\mydir) - NVIM'
- or 'myfile (/mydir) - NVIM')
+ local expected = (iswin() and 'myfile (C:\\mydir) - NVIM' or 'myfile (/mydir) - NVIM')
command('set title')
command(iswin() and 'file C:\\mydir\\myfile' or 'file /mydir/myfile')
screen:expect(function()
eq(expected, screen.title)
end)
end)
+
+ describe('is not changed by', function()
+ local file1 = iswin() and 'C:\\mydir\\myfile1' or '/mydir/myfile1'
+ local file2 = iswin() and 'C:\\mydir\\myfile2' or '/mydir/myfile2'
+ local expected = (iswin() and 'myfile1 (C:\\mydir) - NVIM' or 'myfile1 (/mydir) - NVIM')
+ local buf2
+
+ before_each(function()
+ command('edit '..file1)
+ buf2 = funcs.bufadd(file2)
+ command('set title')
+ end)
+
+ it('calling setbufvar() to set an option in a hidden buffer from i_CTRL-R', function()
+ command([[inoremap <F2> <C-R>=setbufvar(]]..buf2..[[, '&autoindent', 1) ? '' : ''<CR>]])
+ feed('i<F2><Esc>')
+ command('redraw!')
+ screen:expect(function()
+ eq(expected, screen.title)
+ end)
+ end)
+
+ it('an RPC call to nvim_buf_set_option in a hidden buffer', function()
+ meths.buf_set_option(buf2, 'autoindent', true)
+ command('redraw!')
+ screen:expect(function()
+ eq(expected, screen.title)
+ end)
+ end)
+
+ it('a Lua callback calling nvim_buf_set_option in a hidden buffer', function()
+ exec_lua(string.format([[
+ vim.schedule(function()
+ vim.api.nvim_buf_set_option(%d, 'autoindent', true)
+ end)
+ ]], buf2))
+ command('redraw!')
+ screen:expect(function()
+ eq(expected, screen.title)
+ end)
+ end)
+
+ it('a Lua callback calling nvim_buf_call in a hidden buffer', function()
+ exec_lua(string.format([[
+ vim.schedule(function()
+ vim.api.nvim_buf_call(%d, function() end)
+ end)
+ ]], buf2))
+ command('redraw!')
+ screen:expect(function()
+ eq(expected, screen.title)
+ end)
+ end)
+ end)
end)
describe(':set icon', function()
@@ -272,12 +326,12 @@ local function screen_tests(linegrid)
command('vsp')
command('vsp')
screen:expect([[
- ^ {3:│} {3:│} |
- {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
- {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
- {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
- {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
- {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
+ ^ │ │ |
+ {0:~ }│{0:~ }│{0:~ }|
+ {0:~ }│{0:~ }│{0:~ }|
+ {0:~ }│{0:~ }│{0:~ }|
+ {0:~ }│{0:~ }│{0:~ }|
+ {0:~ }│{0:~ }│{0:~ }|
{1:[No Name] }{3:[No Name] [No Name] }|
|
{0:~ }|
@@ -289,12 +343,12 @@ local function screen_tests(linegrid)
]])
insert('hello')
screen:expect([[
- hell^o {3:│}hello {3:│}hello |
- {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
- {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
- {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
- {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
- {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
+ hell^o │hello │hello |
+ {0:~ }│{0:~ }│{0:~ }|
+ {0:~ }│{0:~ }│{0:~ }|
+ {0:~ }│{0:~ }│{0:~ }|
+ {0:~ }│{0:~ }│{0:~ }|
+ {0:~ }│{0:~ }│{0:~ }|
{1:[No Name] [+] }{3:[No Name] [+] [No Name] [+] }|
hello |
{0:~ }|
@@ -315,12 +369,12 @@ local function screen_tests(linegrid)
command('vsp')
insert('hello')
screen:expect([[
- hell^o {3:│}hello {3:│}hello |
- {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
- {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
- {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
- {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
- {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
+ hell^o │hello │hello |
+ {0:~ }│{0:~ }│{0:~ }|
+ {0:~ }│{0:~ }│{0:~ }|
+ {0:~ }│{0:~ }│{0:~ }|
+ {0:~ }│{0:~ }│{0:~ }|
+ {0:~ }│{0:~ }│{0:~ }|
{1:[No Name] [+] }{3:[No Name] [+] [No Name] [+] }|
hello |
{0:~ }|
@@ -352,12 +406,12 @@ local function screen_tests(linegrid)
command('tabprevious')
screen:expect([[
{2: }{6:4}{2:+ [No Name] }{4: + [No Name] }{3: }{4:X}|
- hell^o {3:│}hello {3:│}hello |
- {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
- {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
- {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
- {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
- {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
+ hell^o │hello │hello |
+ {0:~ }│{0:~ }│{0:~ }|
+ {0:~ }│{0:~ }│{0:~ }|
+ {0:~ }│{0:~ }│{0:~ }|
+ {0:~ }│{0:~ }│{0:~ }|
+ {0:~ }│{0:~ }│{0:~ }|
{1:[No Name] [+] }{3:[No Name] [+] [No Name] [+] }|
hello |
{0:~ }|
@@ -466,36 +520,36 @@ local function screen_tests(linegrid)
command('vsplit')
screen:expect([[
- ^foo {3:│}foo |
- foo {3:│}foo |
- foo {3:│}foo |
- foo {3:│}foo |
- foo {3:│}foo |
- foo {3:│}foo |
- foo {3:│}foo |
- foo {3:│}foo |
- foo {3:│}foo |
- foo {3:│}foo |
- foo {3:│}foo |
- foo {3:│}foo |
+ ^foo │foo |
+ foo │foo |
+ foo │foo |
+ foo │foo |
+ foo │foo |
+ foo │foo |
+ foo │foo |
+ foo │foo |
+ foo │foo |
+ foo │foo |
+ foo │foo |
+ foo │foo |
{1:[No Name] [+] }{3:[No Name] [+] }|
|
]])
feed('<PageDown>')
screen:expect([[
- ^foo {3:│}foo |
- foo {3:│}foo |
- foo {3:│}foo |
- foo {3:│}foo |
- bar {3:│}foo |
- bar {3:│}foo |
- bar {3:│}foo |
- bar {3:│}foo |
- bar {3:│}foo |
- bar {3:│}foo |
- bar {3:│}foo |
- bar {3:│}foo |
+ ^foo │foo |
+ foo │foo |
+ foo │foo |
+ foo │foo |
+ bar │foo |
+ bar │foo |
+ bar │foo |
+ bar │foo |
+ bar │foo |
+ bar │foo |
+ bar │foo |
+ bar │foo |
{1:[No Name] [+] }{3:[No Name] [+] }|
|
]])
@@ -694,12 +748,12 @@ local function screen_tests(linegrid)
command('vsp')
command('vsp')
screen:expect([[
- and {3:│}and {3:│}and |
- clearing {3:│}clearing {3:│}clearing |
- in {3:│}in {3:│}in |
- split {3:│}split {3:│}split |
- windows {3:│}windows {3:│}windows |
- ^ {3:│} {3:│} |
+ and │and │and |
+ clearing │clearing │clearing |
+ in │in │in |
+ split │split │split |
+ windows │windows │windows |
+ ^ │ │ |
{1:[No Name] [+] }{3:[No Name] [+] [No Name] [+] }|
clearing |
in |
@@ -714,12 +768,12 @@ local function screen_tests(linegrid)
it('only affects the current scroll region', function()
feed('6k')
screen:expect([[
- ^scrolling {3:│}and {3:│}and |
- and {3:│}clearing {3:│}clearing |
- clearing {3:│}in {3:│}in |
- in {3:│}split {3:│}split |
- split {3:│}windows {3:│}windows |
- windows {3:│} {3:│} |
+ ^scrolling │and │and |
+ and │clearing │clearing |
+ clearing │in │in |
+ in │split │split |
+ split │windows │windows |
+ windows │ │ |
{1:[No Name] [+] }{3:[No Name] [+] [No Name] [+] }|
clearing |
in |
@@ -731,12 +785,12 @@ local function screen_tests(linegrid)
]])
feed('<c-w>l')
screen:expect([[
- scrolling {3:│}and {3:│}and |
- and {3:│}clearing {3:│}clearing |
- clearing {3:│}in {3:│}in |
- in {3:│}split {3:│}split |
- split {3:│}windows {3:│}windows |
- windows {3:│}^ {3:│} |
+ scrolling │and │and |
+ and │clearing │clearing |
+ clearing │in │in |
+ in │split │split |
+ split │windows │windows |
+ windows │^ │ |
{3:[No Name] [+] }{1:[No Name] [+] }{3:<Name] [+] }|
clearing |
in |
@@ -748,12 +802,12 @@ local function screen_tests(linegrid)
]])
feed('gg')
screen:expect([[
- scrolling {3:│}^Inserting {3:│}and |
- and {3:│}text {3:│}clearing |
- clearing {3:│}with {3:│}in |
- in {3:│}many {3:│}split |
- split {3:│}lines {3:│}windows |
- windows {3:│}to {3:│} |
+ scrolling │^Inserting │and |
+ and │text │clearing |
+ clearing │with │in |
+ in │many │split |
+ split │lines │windows |
+ windows │to │ |
{3:[No Name] [+] }{1:[No Name] [+] }{3:<Name] [+] }|
clearing |
in |
@@ -765,12 +819,12 @@ local function screen_tests(linegrid)
]])
feed('7j')
screen:expect([[
- scrolling {3:│}with {3:│}and |
- and {3:│}many {3:│}clearing |
- clearing {3:│}lines {3:│}in |
- in {3:│}to {3:│}split |
- split {3:│}test {3:│}windows |
- windows {3:│}^scrolling {3:│} |
+ scrolling │with │and |
+ and │many │clearing |
+ clearing │lines │in |
+ in │to │split |
+ split │test │windows |
+ windows │^scrolling │ |
{3:[No Name] [+] }{1:[No Name] [+] }{3:<Name] [+] }|
clearing |
in |
@@ -782,12 +836,12 @@ local function screen_tests(linegrid)
]])
feed('2j')
screen:expect([[
- scrolling {3:│}lines {3:│}and |
- and {3:│}to {3:│}clearing |
- clearing {3:│}test {3:│}in |
- in {3:│}scrolling {3:│}split |
- split {3:│}and {3:│}windows |
- windows {3:│}^clearing {3:│} |
+ scrolling │lines │and |
+ and │to │clearing |
+ clearing │test │in |
+ in │scrolling │split |
+ split │and │windows |
+ windows │^clearing │ |
{3:[No Name] [+] }{1:[No Name] [+] }{3:<Name] [+] }|
clearing |
in |
@@ -799,12 +853,12 @@ local function screen_tests(linegrid)
]])
feed('5k')
screen:expect([[
- scrolling {3:│}^lines {3:│}and |
- and {3:│}to {3:│}clearing |
- clearing {3:│}test {3:│}in |
- in {3:│}scrolling {3:│}split |
- split {3:│}and {3:│}windows |
- windows {3:│}clearing {3:│} |
+ scrolling │^lines │and |
+ and │to │clearing |
+ clearing │test │in |
+ in │scrolling │split |
+ split │and │windows |
+ windows │clearing │ |
{3:[No Name] [+] }{1:[No Name] [+] }{3:<Name] [+] }|
clearing |
in |
@@ -816,12 +870,12 @@ local function screen_tests(linegrid)
]])
feed('k')
screen:expect([[
- scrolling {3:│}^many {3:│}and |
- and {3:│}lines {3:│}clearing |
- clearing {3:│}to {3:│}in |
- in {3:│}test {3:│}split |
- split {3:│}scrolling {3:│}windows |
- windows {3:│}and {3:│} |
+ scrolling │^many │and |
+ and │lines │clearing |
+ clearing │to │in |
+ in │test │split |
+ split │scrolling │windows |
+ windows │and │ |
{3:[No Name] [+] }{1:[No Name] [+] }{3:<Name] [+] }|
clearing |
in |
diff --git a/test/functional/ui/searchhl_spec.lua b/test/functional/ui/searchhl_spec.lua
index 5540b3c2dc..c5c88323a2 100644
--- a/test/functional/ui/searchhl_spec.lua
+++ b/test/functional/ui/searchhl_spec.lua
@@ -5,7 +5,8 @@ local command = helpers.command
local feed_command = helpers.feed_command
local eq = helpers.eq
local eval = helpers.eval
-local nvim_dir = helpers.nvim_dir
+local funcs = helpers.funcs
+local testprg = helpers.testprg
describe('search highlighting', function()
local screen
@@ -43,7 +44,7 @@ describe('search highlighting', function()
insert("some text\nmore text")
feed_command('1,2fold')
feed("gg/text")
- screen:expect([[
+ screen:expect{grid=[[
{6:+-- 2 lines: some text·················}|
{1:~ }|
{1:~ }|
@@ -51,7 +52,9 @@ describe('search highlighting', function()
{1:~ }|
{1:~ }|
/text^ |
- ]])
+ ]], win_viewport={
+ [2] = {win = {id = 1000}, topline = 0, botline = 3, curline = 0, curcol = 9, linecount = 2};
+ }}
end)
it('works', function()
@@ -109,6 +112,146 @@ describe('search highlighting', function()
]])
end)
+ describe('CurSearch highlight', function()
+ before_each(function()
+ screen:set_default_attr_ids({
+ [1] = {background = Screen.colors.Yellow}, -- Search
+ [2] = {foreground = Screen.colors.White, background = Screen.colors.Black}, -- CurSearch
+ [3] = {foreground = Screen.colors.Red}, -- WarningMsg
+ })
+ command('highlight CurSearch guibg=Black guifg=White')
+ end)
+
+ it('works for match under cursor', function()
+ insert([[
+ There is no way that a bee should be
+ able to fly. Its wings are too small
+ to get its fat little body off the
+ ground. The bee, of course, flies
+ anyway because bees don't care what
+ humans think is impossible.]])
+
+ feed('/bee<CR>')
+ screen:expect{grid=[[
+ There is no way that a {2:^bee} should be |
+ able to fly. Its wings are too small |
+ to get its fat little body off the |
+ ground. The {1:bee}, of course, flies |
+ anyway because {1:bee}s don't care what |
+ humans think is impossible. |
+ {3:search hit BOTTOM, continuing at TOP} |
+ ]]}
+
+ feed('nn')
+ screen:expect{grid=[[
+ There is no way that a {1:bee} should be |
+ able to fly. Its wings are too small |
+ to get its fat little body off the |
+ ground. The {1:bee}, of course, flies |
+ anyway because {2:^bee}s don't care what |
+ humans think is impossible. |
+ /bee |
+ ]]}
+
+ feed('N')
+ screen:expect{grid=[[
+ There is no way that a {1:bee} should be |
+ able to fly. Its wings are too small |
+ to get its fat little body off the |
+ ground. The {2:^bee}, of course, flies |
+ anyway because {1:bee}s don't care what |
+ humans think is impossible. |
+ ?bee |
+ ]]}
+ end)
+
+ it('works for multiline match', function()
+ command([[call setline(1, ['one', 'foo', 'bar', 'baz', 'foo the foo and foo', 'bar'])]])
+ feed('gg/foo<CR>')
+ screen:expect([[
+ one |
+ {2:^foo} |
+ bar |
+ baz |
+ {1:foo} the {1:foo} and {1:foo} |
+ bar |
+ /foo |
+ ]])
+ feed('n')
+ screen:expect([[
+ one |
+ {1:foo} |
+ bar |
+ baz |
+ {2:^foo} the {1:foo} and {1:foo} |
+ bar |
+ /foo |
+ ]])
+ feed('n')
+ screen:expect([[
+ one |
+ {1:foo} |
+ bar |
+ baz |
+ {1:foo} the {2:^foo} and {1:foo} |
+ bar |
+ /foo |
+ ]])
+ feed('n')
+ screen:expect([[
+ one |
+ {1:foo} |
+ bar |
+ baz |
+ {1:foo} the {1:foo} and {2:^foo} |
+ bar |
+ /foo |
+ ]])
+ command([[call setline(5, 'foo')]])
+ feed('0?<CR>')
+ screen:expect([[
+ one |
+ {2:^foo} |
+ bar |
+ baz |
+ {1:foo} |
+ bar |
+ ?foo |
+ ]])
+ feed('gg/foo\\nbar<CR>')
+ screen:expect([[
+ one |
+ {2:^foo} |
+ {2:bar} |
+ baz |
+ {1:foo} |
+ {1:bar} |
+ /foo\nbar |
+ ]])
+ command([[call setline(1, ['---', 'abcdefg', 'hijkl', '---', 'abcdefg', 'hijkl'])]])
+ feed('gg/efg\\nhij<CR>')
+ screen:expect([[
+ --- |
+ abcd{2:^efg} |
+ {2:hij}kl |
+ --- |
+ abcd{1:efg} |
+ {1:hij}kl |
+ /efg\nhij |
+ ]])
+ feed('n')
+ screen:expect([[
+ --- |
+ abcd{1:efg} |
+ {1:hij}kl |
+ --- |
+ abcd{2:^efg} |
+ {2:hij}kl |
+ /efg\nhij |
+ ]])
+ end)
+ end)
+
it('highlights after EOL', function()
insert("\n\n\n\n\n\n")
@@ -163,7 +306,7 @@ describe('search highlighting', function()
end)
it('is preserved during :terminal activity', function()
- feed([[:terminal "]]..nvim_dir..[[/shell-test" REP 5000 foo<cr>]])
+ feed((':terminal "%s" REP 5000 foo<cr>'):format(testprg('shell-test')))
feed(':file term<CR>')
feed('G') -- Follow :terminal output.
@@ -179,100 +322,101 @@ describe('search highlighting', function()
end)
it('works with incsearch', function()
- feed_command('set hlsearch')
- feed_command('set incsearch')
+ command('set hlsearch')
+ command('set incsearch')
+ command('set laststatus=0')
insert([[
the first line
- in a little file
- ]])
+ in a little file]])
+ command('vsplit')
feed("gg/li")
screen:expect([[
- the first {3:li}ne |
- in a {2:li}ttle file |
- |
- {1:~ }|
- {1:~ }|
- {1:~ }|
+ the first {3:li}ne │the first {2:li}ne |
+ in a {2:li}ttle file │in a {2:li}ttle file |
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
/li^ |
]])
-- check that consecutive matches are caught by C-g/C-t
feed("<C-g>")
screen:expect([[
- the first {2:li}ne |
- in a {3:li}ttle file |
- |
- {1:~ }|
- {1:~ }|
- {1:~ }|
+ the first {2:li}ne │the first {2:li}ne |
+ in a {3:li}ttle file │in a {2:li}ttle file |
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
/li^ |
]])
feed("<C-t>")
screen:expect([[
- the first {3:li}ne |
- in a {2:li}ttle file |
- |
- {1:~ }|
- {1:~ }|
- {1:~ }|
+ the first {3:li}ne │the first {2:li}ne |
+ in a {2:li}ttle file │in a {2:li}ttle file |
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
/li^ |
]])
feed("t")
screen:expect([[
- the first line |
- in a {3:lit}tle file |
- |
- {1:~ }|
- {1:~ }|
- {1:~ }|
+ the first line │the first line |
+ in a {3:lit}tle file │in a {2:lit}tle file |
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
/lit^ |
]])
feed("<cr>")
screen:expect([[
- the first line |
- in a {2:^lit}tle file |
- |
- {1:~ }|
- {1:~ }|
- {1:~ }|
+ the first line │the first line |
+ in a {2:^lit}tle file │in a {2:lit}tle file |
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
/lit |
]])
feed("/fir")
screen:expect([[
- the {3:fir}st line |
- in a little file |
- |
- {1:~ }|
- {1:~ }|
- {1:~ }|
+ the {3:fir}st line │the {2:fir}st line |
+ in a little file │in a little file |
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
/fir^ |
]])
-- incsearch have priority over hlsearch
feed("<esc>/ttle")
screen:expect([[
- the first line |
- in a li{3:ttle} file |
- |
- {1:~ }|
- {1:~ }|
- {1:~ }|
+ the first line │the first line |
+ in a li{3:ttle} file │in a li{2:ttle} file |
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
/ttle^ |
]])
-- cancelling search resets to the old search term
feed('<esc>')
screen:expect([[
- the first line |
- in a {2:^lit}tle file |
- |
- {1:~ }|
- {1:~ }|
- {1:~ }|
+ the first line │the first line |
+ in a {2:^lit}tle file │in a {2:lit}tle file |
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
|
]])
eq('lit', eval('@/'))
@@ -280,91 +424,78 @@ describe('search highlighting', function()
-- cancelling inc search restores the hl state
feed(':noh<cr>')
screen:expect([[
- the first line |
- in a ^little file |
- |
- {1:~ }|
- {1:~ }|
- {1:~ }|
+ the first line │the first line |
+ in a ^little file │in a little file |
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
:noh |
]])
feed('/first')
screen:expect([[
- the {3:first} line |
- in a little file |
- |
- {1:~ }|
- {1:~ }|
- {1:~ }|
+ the {3:first} line │the {2:first} line |
+ in a little file │in a little file |
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
/first^ |
]])
feed('<esc>')
screen:expect([[
- the first line |
- in a ^little file |
- |
- {1:~ }|
- {1:~ }|
- {1:~ }|
+ the first line │the first line |
+ in a ^little file │in a little file |
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
|
]])
-- test that pressing C-g in an empty command line does not move the cursor
- feed('/<C-g>')
- screen:expect([[
- the first line |
- in a little file |
- |
- {1:~ }|
- {1:~ }|
- {1:~ }|
- /^ |
- ]])
-
- -- same, for C-t
- feed('<ESC>')
- screen:expect([[
- the first line |
- in a ^little file |
- |
- {1:~ }|
- {1:~ }|
- {1:~ }|
- |
- ]])
- feed('/<C-t>')
- screen:expect([[
- the first line |
- in a little file |
- |
- {1:~ }|
- {1:~ }|
- {1:~ }|
- /^ |
- ]])
+ feed('gg0')
+ command([[let @/ = 'i']])
+ -- moves to next match of previous search pattern, just like /<cr>
+ feed('/<c-g><cr>')
+ eq({0, 1, 6, 0}, funcs.getpos('.'))
+ -- moves to next match of previous search pattern, just like /<cr>
+ feed('/<cr>')
+ eq({0, 1, 12, 0}, funcs.getpos('.'))
+ -- moves to next match of previous search pattern, just like /<cr>
+ feed('/<c-t><cr>')
+ eq({0, 2, 1, 0}, funcs.getpos('.'))
-- 8.0.1304, test that C-g and C-t works with incsearch and empty pattern
feed('<esc>/fi<CR>')
+ screen:expect([[
+ the {2:fi}rst line │the {2:fi}rst line |
+ in a little {2:^fi}le │in a little {2:fi}le |
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ /fi |
+ ]])
feed('//')
screen:expect([[
- the {3:fi}rst line |
- in a little {2:fi}le |
- |
- {1:~ }|
- {1:~ }|
- {1:~ }|
+ the {3:fi}rst line │the {2:fi}rst line |
+ in a little {2:fi}le │in a little {2:fi}le |
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
//^ |
]])
-
feed('<C-g>')
screen:expect([[
- the {2:fi}rst line |
- in a little {3:fi}le |
- |
- {1:~ }|
- {1:~ }|
- {1:~ }|
+ the {2:fi}rst line │the {2:fi}rst line |
+ in a little {3:fi}le │in a little {2:fi}le |
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
//^ |
]])
end)
@@ -439,19 +570,20 @@ describe('search highlighting', function()
end)
it('works with matchadd and syntax', function()
- screen:set_default_attr_ids( {
- [1] = {bold=true, foreground=Screen.colors.Blue},
- [2] = {background = colors.Yellow},
- [3] = {reverse = true},
- [4] = {foreground = colors.Red},
- [5] = {bold = true, background = colors.Green},
- [6] = {italic = true, background = colors.Magenta},
- [7] = {bold = true, background = colors.Yellow},
- } )
+ screen:set_default_attr_ids {
+ [1] = {bold=true, foreground=Screen.colors.Blue};
+ [2] = {background = colors.Yellow};
+ [3] = {reverse = true};
+ [4] = {foreground = colors.Red};
+ [5] = {bold = true, background = colors.Green};
+ [6] = {italic = true, background = colors.Magenta};
+ [7] = {bold = true, background = colors.Yellow};
+ [8] = {foreground = Screen.colors.Blue4, background = Screen.colors.LightGray};
+ }
feed_command('set hlsearch')
- insert([[
+ insert [[
very special text
- ]])
+ ]]
feed_command("syntax on")
feed_command("highlight MyGroup guibg=Green gui=bold")
feed_command("highlight MyGroup2 guibg=Magenta gui=italic")
@@ -461,7 +593,7 @@ describe('search highlighting', function()
-- searchhl and matchadd matches are exclusive, only the highest priority
-- is used (and matches with lower priorities are not combined)
feed_command("/ial te")
- screen:expect([[
+ screen:expect{grid=[[
very {5:spec^ial}{2: te}{6:xt} |
|
{1:~ }|
@@ -469,10 +601,21 @@ describe('search highlighting', function()
{1:~ }|
{1:~ }|
{4:search hit BOTTOM, continuing at TOP} |
- ]])
+ ]], win_viewport={
+ [2] = {win = {id = 1000}, topline = 0, botline = 3, curline = 0, curcol = 11, linecount = 2};
+ }}
- -- check hilights work also in folds
+ -- check highlights work also in folds
feed("zf4j")
+ screen:expect{grid=[[
+ {8:^+-- 2 lines: very special text·········}|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {4:search hit BOTTOM, continuing at TOP} |
+ ]]}
command("%foldopen")
screen:expect([[
very {5:spec^ial}{2: te}{6:xt} |
diff --git a/test/functional/ui/sign_spec.lua b/test/functional/ui/sign_spec.lua
index dcd31cfdb7..dbc92ca222 100644
--- a/test/functional/ui/sign_spec.lua
+++ b/test/functional/ui/sign_spec.lua
@@ -446,7 +446,7 @@ describe('Signs', function()
{1:>>>>>>>>}{6: 1 }a |
{2: }{6: 2 }b |
{2: }{6: 3 }c |
- {2: }{6:^ 4 } |
+ {2: }{6: 4 }^ |
{0:~ }|
{0:~ }|
{0:~ }|
@@ -468,7 +468,7 @@ describe('Signs', function()
{1:>>>>>>>>>>}{6: 1 }a |
{2: }{6: 2 }b |
{2: }{6: 3 }c |
- {2: ^ }{6: 4 } |
+ {2: }{6: 4 }^ |
{0:~ }|
{0:~ }|
{0:~ }|
@@ -482,7 +482,7 @@ describe('Signs', function()
]])
end)
- it('ignores signs with no icon and text when calculting the signcolumn width', function()
+ it('ignores signs with no icon and text when calculating the signcolumn width', function()
feed('ia<cr>b<cr>c<cr><esc>')
command('set number')
command('set signcolumn=auto:2')
@@ -512,7 +512,33 @@ describe('Signs', function()
{1:>>}{6: 1 }a |
{2: }{6: 2 }b |
{2: }{6: 3 }c |
- {2: }{6: ^4 } |
+ {2: }{6: 4 }^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ end)
+
+ it('shows the line number when signcolumn=number but no marks on a line have text', function()
+ feed('ia<cr>b<cr>c<cr><esc>')
+ command('set number signcolumn=number')
+ command('sign define pietSearch text=>> texthl=Search numhl=Error')
+ command('sign define pietError text= texthl=Search numhl=Error')
+ command('sign place 1 line=1 name=pietSearch buffer=1')
+ command('sign place 2 line=2 name=pietError buffer=1')
+ -- no signcolumn, line number for "a" is Search, for "b" is Error, for "c" is LineNr
+ screen:expect([[
+ {1: >> }a |
+ {8: 2 }b |
+ {6: 3 }c |
+ {6: 4 }^ |
{0:~ }|
{0:~ }|
{0:~ }|
diff --git a/test/functional/ui/statusline_spec.lua b/test/functional/ui/statusline_spec.lua
new file mode 100644
index 0000000000..1e1066d48a
--- /dev/null
+++ b/test/functional/ui/statusline_spec.lua
@@ -0,0 +1,342 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local clear = helpers.clear
+local command = helpers.command
+local feed = helpers.feed
+local eq = helpers.eq
+local funcs = helpers.funcs
+local meths = helpers.meths
+local exec = helpers.exec
+local exec_lua = helpers.exec_lua
+local eval = helpers.eval
+
+describe('statusline clicks', function()
+ local screen
+
+ before_each(function()
+ clear()
+ screen = Screen.new(40, 8)
+ screen:attach()
+ command('set laststatus=2 mousemodel=extend')
+ exec([=[
+ function! MyClickFunc(minwid, clicks, button, mods)
+ let mods = trim(a:mods)
+ if mods ==# ''
+ let g:testvar = printf("%d %d %s", a:minwid, a:clicks, a:button)
+ else
+ let g:testvar = printf("%d %d %s %s", a:minwid, a:clicks, a:button, mods)
+ endif
+ endfunction
+ ]=])
+ end)
+
+ it('works', function()
+ meths.set_option('statusline', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T')
+ meths.input_mouse('left', 'press', '', 0, 6, 17)
+ eq('0 1 l', eval("g:testvar"))
+ meths.input_mouse('right', 'press', '', 0, 6, 17)
+ eq('0 1 r', eval("g:testvar"))
+ end)
+
+ it('works for winbar', function()
+ meths.set_option('winbar', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T')
+ meths.input_mouse('left', 'press', '', 0, 0, 17)
+ eq('0 1 l', eval("g:testvar"))
+ meths.input_mouse('right', 'press', '', 0, 0, 17)
+ eq('0 1 r', eval("g:testvar"))
+ end)
+
+ it('works for winbar in floating window', function()
+ meths.open_win(0, true, { width=30, height=4, relative='editor', row=1, col=5,
+ border = "single" })
+ meths.set_option_value('winbar', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T',
+ { scope = 'local' })
+ meths.input_mouse('left', 'press', '', 0, 2, 23)
+ eq('0 1 l', eval("g:testvar"))
+ end)
+
+ it('works when there are multiple windows', function()
+ command('split')
+ meths.set_option('statusline', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T')
+ meths.set_option('winbar', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T')
+ meths.input_mouse('left', 'press', '', 0, 0, 17)
+ eq('0 1 l', eval("g:testvar"))
+ meths.input_mouse('right', 'press', '', 0, 4, 17)
+ eq('0 1 r', eval("g:testvar"))
+ meths.input_mouse('middle', 'press', '', 0, 3, 17)
+ eq('0 1 m', eval("g:testvar"))
+ meths.input_mouse('left', 'press', '', 0, 6, 17)
+ eq('0 1 l', eval("g:testvar"))
+ end)
+
+ it('works with Lua function', function()
+ exec_lua([[
+ function clicky_func(minwid, clicks, button, mods)
+ vim.g.testvar = string.format("%d %d %s", minwid, clicks, button)
+ end
+ ]])
+ meths.set_option('statusline', 'Not clicky stuff %0@v:lua.clicky_func@Clicky stuff%T')
+ meths.input_mouse('left', 'press', '', 0, 6, 17)
+ eq('0 1 l', eval("g:testvar"))
+ end)
+
+ it('ignores unsupported click items', function()
+ command('tabnew | tabprevious')
+ meths.set_option('statusline', '%2TNot clicky stuff%T')
+ meths.input_mouse('left', 'press', '', 0, 6, 0)
+ eq(1, meths.get_current_tabpage().id)
+ meths.set_option('statusline', '%2XNot clicky stuff%X')
+ meths.input_mouse('left', 'press', '', 0, 6, 0)
+ eq(2, #meths.list_tabpages())
+ end)
+
+ it("right click works when statusline isn't focused #18994", function()
+ meths.set_option('statusline', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T')
+ meths.input_mouse('right', 'press', '', 0, 6, 17)
+ eq('0 1 r', eval("g:testvar"))
+ meths.input_mouse('right', 'press', '', 0, 6, 17)
+ eq('0 2 r', eval("g:testvar"))
+ end)
+
+ it("click works with modifiers #18994", function()
+ meths.set_option('statusline', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T')
+ meths.input_mouse('right', 'press', 's', 0, 6, 17)
+ eq('0 1 r s', eval("g:testvar"))
+ meths.input_mouse('left', 'press', 's', 0, 6, 17)
+ eq('0 1 l s', eval("g:testvar"))
+ end)
+end)
+
+describe('global statusline', function()
+ local screen
+
+ before_each(function()
+ clear()
+ screen = Screen.new(60, 16)
+ screen:attach()
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue};
+ [2] = {bold = true, reverse = true};
+ [3] = {bold = true};
+ [4] = {reverse = true};
+ })
+ command('set laststatus=3')
+ command('set ruler')
+ end)
+
+ it('works', function()
+ screen:expect([[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:[No Name] 0,0-1 All}|
+ |
+ ]])
+
+ feed('i<CR><CR>')
+ screen:expect([[
+ |
+ |
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:[No Name] [+] 3,1 All}|
+ {3:-- INSERT --} |
+ ]])
+ end)
+
+ it('works with splits', function()
+ command('vsplit | split | vsplit | vsplit | wincmd l | split | 2wincmd l | split')
+ screen:expect([[
+ │ │ │^ |
+ {1:~ }│{1:~ }│{1:~}│{1:~ }|
+ {1:~ }│{1:~ }│{1:~}│{1:~ }|
+ {1:~ }│{1:~ }│{1:~}│{1:~ }|
+ {1:~ }├────────────────┤{1:~}│{1:~ }|
+ {1:~ }│ │{1:~}│{1:~ }|
+ {1:~ }│{1:~ }│{1:~}│{1:~ }|
+ {1:~ }│{1:~ }│{1:~}├────────────────────|
+ {1:~ }│{1:~ }│{1:~}│ |
+ ────────────────────┴────────────────┴─┤{1:~ }|
+ │{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {2:[No Name] 0,0-1 All}|
+ |
+ ]])
+ end)
+
+ it('works when switching between values of laststatus', function()
+ command('set laststatus=1')
+ screen:expect([[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ 0,0-1 All |
+ ]])
+
+ command('set laststatus=3')
+ screen:expect([[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:[No Name] 0,0-1 All}|
+ |
+ ]])
+
+ command('vsplit | split | vsplit | vsplit | wincmd l | split | 2wincmd l | split')
+ command('set laststatus=2')
+ screen:expect([[
+ │ │ │^ |
+ {1:~ }│{1:~ }│{1:~}│{1:~ }|
+ {1:~ }│{1:~ }│{1:~}│{1:~ }|
+ {1:~ }│{1:~ }│{1:~}│{1:~ }|
+ {1:~ }│{4:< Name] 0,0-1 }│{1:~}│{1:~ }|
+ {1:~ }│ │{1:~}│{1:~ }|
+ {1:~ }│{1:~ }│{1:~}│{1:~ }|
+ {1:~ }│{1:~ }│{1:~}│{2:<No Name] 0,0-1 All}|
+ {1:~ }│{1:~ }│{1:~}│ |
+ {4:<No Name] 0,0-1 All < Name] 0,0-1 <}│{1:~ }|
+ │{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {4:[No Name] 0,0-1 All <No Name] 0,0-1 All}|
+ |
+ ]])
+
+ command('set laststatus=3')
+ screen:expect([[
+ │ │ │^ |
+ {1:~ }│{1:~ }│{1:~}│{1:~ }|
+ {1:~ }│{1:~ }│{1:~}│{1:~ }|
+ {1:~ }│{1:~ }│{1:~}│{1:~ }|
+ {1:~ }├────────────────┤{1:~}│{1:~ }|
+ {1:~ }│ │{1:~}│{1:~ }|
+ {1:~ }│{1:~ }│{1:~}│{1:~ }|
+ {1:~ }│{1:~ }│{1:~}├────────────────────|
+ {1:~ }│{1:~ }│{1:~}│ |
+ ────────────────────┴────────────────┴─┤{1:~ }|
+ │{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {2:[No Name] 0,0-1 All}|
+ |
+ ]])
+
+ command('set laststatus=0')
+ screen:expect([[
+ │ │ │^ |
+ {1:~ }│{1:~ }│{1:~}│{1:~ }|
+ {1:~ }│{1:~ }│{1:~}│{1:~ }|
+ {1:~ }│{1:~ }│{1:~}│{1:~ }|
+ {1:~ }│{4:< Name] 0,0-1 }│{1:~}│{1:~ }|
+ {1:~ }│ │{1:~}│{1:~ }|
+ {1:~ }│{1:~ }│{1:~}│{1:~ }|
+ {1:~ }│{1:~ }│{1:~}│{2:<No Name] 0,0-1 All}|
+ {1:~ }│{1:~ }│{1:~}│ |
+ {4:<No Name] 0,0-1 All < Name] 0,0-1 <}│{1:~ }|
+ │{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ 0,0-1 All |
+ ]])
+
+ command('set laststatus=3')
+ screen:expect([[
+ │ │ │^ |
+ {1:~ }│{1:~ }│{1:~}│{1:~ }|
+ {1:~ }│{1:~ }│{1:~}│{1:~ }|
+ {1:~ }│{1:~ }│{1:~}│{1:~ }|
+ {1:~ }├────────────────┤{1:~}│{1:~ }|
+ {1:~ }│ │{1:~}│{1:~ }|
+ {1:~ }│{1:~ }│{1:~}│{1:~ }|
+ {1:~ }│{1:~ }│{1:~}├────────────────────|
+ {1:~ }│{1:~ }│{1:~}│ |
+ ────────────────────┴────────────────┴─┤{1:~ }|
+ │{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {1:~ }│{1:~ }|
+ {2:[No Name] 0,0-1 All}|
+ |
+ ]])
+ end)
+
+ it('win_move_statusline() can reduce cmdheight to 1', function()
+ eq(1, meths.get_option('cmdheight'))
+ funcs.win_move_statusline(0, -1)
+ eq(2, meths.get_option('cmdheight'))
+ funcs.win_move_statusline(0, -1)
+ eq(3, meths.get_option('cmdheight'))
+ funcs.win_move_statusline(0, 1)
+ eq(2, meths.get_option('cmdheight'))
+ funcs.win_move_statusline(0, 1)
+ eq(1, meths.get_option('cmdheight'))
+ end)
+
+ it('mouse dragging can reduce cmdheight to 1', function()
+ command('set mouse=a')
+ meths.input_mouse('left', 'press', '', 0, 14, 10)
+ eq(1, meths.get_option('cmdheight'))
+ meths.input_mouse('left', 'drag', '', 0, 13, 10)
+ eq(2, meths.get_option('cmdheight'))
+ meths.input_mouse('left', 'drag', '', 0, 12, 10)
+ eq(3, meths.get_option('cmdheight'))
+ meths.input_mouse('left', 'drag', '', 0, 13, 10)
+ eq(2, meths.get_option('cmdheight'))
+ meths.input_mouse('left', 'drag', '', 0, 14, 10)
+ eq(1, meths.get_option('cmdheight'))
+ meths.input_mouse('left', 'drag', '', 0, 15, 10)
+ eq(0, meths.get_option('cmdheight'))
+ meths.input_mouse('left', 'drag', '', 0, 14, 10)
+ eq(1, meths.get_option('cmdheight'))
+ end)
+end)
diff --git a/test/functional/ui/syntax_conceal_spec.lua b/test/functional/ui/syntax_conceal_spec.lua
index 4e1852162f..f790597140 100644
--- a/test/functional/ui/syntax_conceal_spec.lua
+++ b/test/functional/ui/syntax_conceal_spec.lua
@@ -3,6 +3,8 @@ local Screen = require('test.functional.ui.screen')
local clear, feed, command = helpers.clear, helpers.feed, helpers.command
local eq = helpers.eq
local insert = helpers.insert
+local poke_eventloop = helpers.poke_eventloop
+local expect_exit = helpers.expect_exit
describe('Screen', function()
local screen
@@ -255,6 +257,40 @@ describe('Screen', function()
]])
end)
end) -- a region of text (implicit concealing)
+
+ it("cursor position is correct when entering Insert mode with cocu=ni #13916", function()
+ insert([[foobarfoobarfoobar]])
+ -- move to end of line
+ feed("$")
+ command("set concealcursor=ni")
+ command("syn match Foo /foobar/ conceal cchar=&")
+ screen:expect([[
+ {1:&&&}^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ feed("i")
+ -- cursor should stay in place, not jump to column 16
+ screen:expect([[
+ {1:&&&}^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {4:-- INSERT --} |
+ ]])
+ end)
end) -- match and conceal
describe("let the conceal level be", function()
@@ -911,14 +947,64 @@ describe('Screen', function()
{0:~ }|
|
]]}
- eq(grid_lines, {{2, 0, {{'c', 0, 3}}}})
+ eq({{2, 0, {{'c', 0, 3}}}}, grid_lines)
+ end)
+
+ it('K_EVENT should not cause extra redraws with concealcursor #13196', function()
+ command('set conceallevel=1')
+ command('set concealcursor=nv')
+ command('set redrawdebug+=nodelta')
+
+ insert([[
+ aaa
+ bbb
+ ccc
+ ]])
+ screen:expect{grid=[[
+ aaa |
+ bbb |
+ ccc |
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]]}
+
+ -- XXX: hack to get notifications, and check only a single line is
+ -- updated. Could use next_msg() also.
+ local orig_handle_grid_line = screen._handle_grid_line
+ local grid_lines = {}
+ function screen._handle_grid_line(self, grid, row, col, items)
+ table.insert(grid_lines, {row, col, items})
+ orig_handle_grid_line(self, grid, row, col, items)
+ end
+ feed('k')
+ screen:expect{grid=[[
+ aaa |
+ bbb |
+ ^ccc |
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]]}
+ eq({{2, 0, {{'c', 0, 3}}}}, grid_lines)
+ poke_eventloop() -- causes K_EVENT key
+ screen:expect_unchanged()
+ eq({{2, 0, {{'c', 0, 3}}}}, grid_lines)
end)
-- Copy of Test_cursor_column_in_concealed_line_after_window_scroll in
-- test/functional/ui/syntax_conceal_spec.lua.
describe('concealed line after window scroll', function()
after_each(function()
- command(':qall!')
+ expect_exit(command, ':qall!')
os.remove('Xcolesearch')
end)
diff --git a/test/functional/ui/wildmode_spec.lua b/test/functional/ui/wildmode_spec.lua
index 65c6fabfa8..98398bc7a1 100644
--- a/test/functional/ui/wildmode_spec.lua
+++ b/test/functional/ui/wildmode_spec.lua
@@ -7,7 +7,7 @@ local meths = helpers.meths
local eq = helpers.eq
local eval = helpers.eval
local retry = helpers.retry
-local nvim_dir = helpers.nvim_dir
+local testprg = helpers.testprg
describe("'wildmenu'", function()
local screen
@@ -114,7 +114,7 @@ describe("'wildmenu'", function()
it('is preserved during :terminal activity', function()
command('set wildmenu wildmode=full')
command('set scrollback=4')
- feed([[:terminal "]]..nvim_dir..[[/shell-test" REP 5000 !terminal_output!<cr>]])
+ feed((':terminal "%s" REP 5000 !terminal_output!<cr>'):format(testprg('shell-test')))
feed('G') -- Follow :terminal output.
feed([[:sign <Tab>]]) -- Invoke wildmenu.
-- NB: in earlier versions terminal output was redrawn during cmdline mode.
diff --git a/test/functional/ui/winbar_spec.lua b/test/functional/ui/winbar_spec.lua
new file mode 100644
index 0000000000..92a6ab2e84
--- /dev/null
+++ b/test/functional/ui/winbar_spec.lua
@@ -0,0 +1,580 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local clear = helpers.clear
+local command = helpers.command
+local insert = helpers.insert
+local meths = helpers.meths
+local eq = helpers.eq
+local poke_eventloop = helpers.poke_eventloop
+local feed = helpers.feed
+local pcall_err = helpers.pcall_err
+
+describe('winbar', function()
+ local screen
+
+ before_each(function()
+ clear()
+ screen = Screen.new(60, 13)
+ screen:attach()
+ screen:set_default_attr_ids({
+ [1] = {bold = true},
+ [2] = {reverse = true},
+ [3] = {bold = true, foreground = Screen.colors.Blue},
+ [4] = {bold = true, reverse = true},
+ [5] = {bold = true, foreground = Screen.colors.Red},
+ [6] = {foreground = Screen.colors.Blue},
+ [7] = {background = Screen.colors.LightGrey},
+ [8] = {background = Screen.colors.LightMagenta},
+ [9] = {bold = true, foreground = Screen.colors.Blue, background = Screen.colors.LightMagenta},
+ [10] = {background = Screen.colors.LightGrey, underline = true},
+ [11] = {background = Screen.colors.LightGrey, underline = true, bold = true, foreground = Screen.colors.Magenta},
+ })
+ meths.set_option('winbar', 'Set Up The Bars')
+ end)
+
+ it('works', function()
+ screen:expect([[
+ {1:Set Up The Bars }|
+ ^ |
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ |
+ ]])
+ end)
+
+ it('works with custom \'fillchars\' value', function()
+ command('set fillchars=wbr:+')
+ screen:expect([[
+ {1:Set Up The Bars+++++++++++++++++++++++++++++++++++++++++++++}|
+ ^ |
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ |
+ ]])
+ end)
+
+ it('works with custom highlight', function()
+ command('hi WinBar guifg=red')
+ screen:expect([[
+ {5:Set Up The Bars }|
+ ^ |
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ |
+ ]])
+ end)
+
+ it('works with splits', function()
+ command('hi WinBar guifg=red')
+ command('hi WinBarNC guifg=blue')
+ command('belowright vsplit | split | split')
+ screen:expect([[
+ {6:Set Up The Bars }│{5:Set Up The Bars }|
+ │^ |
+ {3:~ }│{3:~ }|
+ {3:~ }│{4:[No Name] }|
+ {3:~ }│{6:Set Up The Bars }|
+ {3:~ }│ |
+ {3:~ }│{3:~ }|
+ {3:~ }│{2:[No Name] }|
+ {3:~ }│{6:Set Up The Bars }|
+ {3:~ }│ |
+ {3:~ }│{3:~ }|
+ {2:[No Name] [No Name] }|
+ |
+ ]])
+ end)
+
+ it('works when switching value of \'winbar\'', function()
+ command('belowright vsplit | split | split | set winbar=')
+ screen:expect([[
+ │^ |
+ {3:~ }│{3:~ }|
+ {3:~ }│{3:~ }|
+ {3:~ }│{4:[No Name] }|
+ {3:~ }│ |
+ {3:~ }│{3:~ }|
+ {3:~ }│{3:~ }|
+ {3:~ }│{2:[No Name] }|
+ {3:~ }│ |
+ {3:~ }│{3:~ }|
+ {3:~ }│{3:~ }|
+ {2:[No Name] [No Name] }|
+ |
+ ]])
+ command('set winbar=All\\ Your\\ Bar\\ Are\\ Belong\\ To\\ Us')
+ screen:expect([[
+ {1:All Your Bar Are Belong To Us}│{1:All Your Bar Are Belong To Us }|
+ │^ |
+ {3:~ }│{3:~ }|
+ {3:~ }│{4:[No Name] }|
+ {3:~ }│{1:All Your Bar Are Belong To Us }|
+ {3:~ }│ |
+ {3:~ }│{3:~ }|
+ {3:~ }│{2:[No Name] }|
+ {3:~ }│{1:All Your Bar Are Belong To Us }|
+ {3:~ }│ |
+ {3:~ }│{3:~ }|
+ {2:[No Name] [No Name] }|
+ |
+ ]])
+ command('set winbar=Changed\\ winbar')
+ screen:expect([[
+ {1:Changed winbar }│{1:Changed winbar }|
+ │^ |
+ {3:~ }│{3:~ }|
+ {3:~ }│{4:[No Name] }|
+ {3:~ }│{1:Changed winbar }|
+ {3:~ }│ |
+ {3:~ }│{3:~ }|
+ {3:~ }│{2:[No Name] }|
+ {3:~ }│{1:Changed winbar }|
+ {3:~ }│ |
+ {3:~ }│{3:~ }|
+ {2:[No Name] [No Name] }|
+ |
+ ]])
+ end)
+
+ it('can be ruler', function()
+ insert [[
+ just some
+ random text]]
+ meths.set_option('winbar', 'Hello, I am a ruler: %l,%c')
+ screen:expect{grid=[[
+ {1:Hello, I am a ruler: 2,11 }|
+ just some |
+ random tex^t |
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ |
+ ]]}
+ feed 'b'
+ screen:expect{grid=[[
+ {1:Hello, I am a ruler: 2,8 }|
+ just some |
+ random ^text |
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ |
+ ]]}
+ feed 'k'
+ screen:expect{grid=[[
+ {1:Hello, I am a ruler: 1,8 }|
+ just so^me |
+ random text |
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ |
+ ]]}
+ end)
+
+ it('works with laststatus=3', function()
+ command('set laststatus=3')
+ screen:expect([[
+ {1:Set Up The Bars }|
+ ^ |
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {4:[No Name] }|
+ |
+ ]])
+ command('belowright vsplit | split | split')
+ screen:expect([[
+ {1:Set Up The Bars }│{1:Set Up The Bars }|
+ │^ |
+ {3:~ }│{3:~ }|
+ {3:~ }├──────────────────────────────|
+ {3:~ }│{1:Set Up The Bars }|
+ {3:~ }│ |
+ {3:~ }│{3:~ }|
+ {3:~ }├──────────────────────────────|
+ {3:~ }│{1:Set Up The Bars }|
+ {3:~ }│ |
+ {3:~ }│{3:~ }|
+ {4:[No Name] }|
+ |
+ ]])
+ -- Test for issue #18791
+ command('tabnew')
+ screen:expect([[
+ {10: }{11:4}{10: [No Name] }{1: [No Name] }{2: }{10:X}|
+ {1:Set Up The Bars }|
+ ^ |
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {4:[No Name] }|
+ |
+ ]])
+ end)
+
+ it('mouse click and drag work correctly in buffer', function()
+ insert([[
+ line 1
+ line 2
+ line 3
+ line 4
+ line -42
+ line i
+ line sin(theta)
+ line 8]])
+
+ meths.input_mouse('left', 'press', '', 0, 5, 1)
+ screen:expect([[
+ {1:Set Up The Bars }|
+ line 1 |
+ line 2 |
+ line 3 |
+ line 4 |
+ l^ine -42 |
+ line i |
+ line sin(theta) |
+ line 8 |
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ |
+ ]])
+ eq({5, 1}, meths.win_get_cursor(0))
+
+ meths.input_mouse('left', 'drag', '', 0, 6, 2)
+ screen:expect([[
+ {1:Set Up The Bars }|
+ line 1 |
+ line 2 |
+ line 3 |
+ line 4 |
+ l{7:ine -42} |
+ {7:li}^ne i |
+ line sin(theta) |
+ line 8 |
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {1:-- VISUAL --} |
+ ]])
+ eq({6, 2}, meths.win_get_cursor(0))
+
+ meths.input_mouse('left', 'drag', '', 0, 1, 2)
+ screen:expect([[
+ {1:Set Up The Bars }|
+ li^n{7:e 1} |
+ {7:line 2} |
+ {7:line 3} |
+ {7:line 4} |
+ {7:li}ne -42 |
+ line i |
+ line sin(theta) |
+ line 8 |
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {1:-- VISUAL --} |
+ ]])
+ eq({1, 2}, meths.win_get_cursor(0))
+
+ meths.input_mouse('left', 'drag', '', 0, 0, 2)
+ screen:expect_unchanged()
+ eq({1, 2}, meths.win_get_cursor(0))
+ end)
+
+ it('dragging statusline with mouse works correctly', function()
+ command('split')
+ screen:expect([[
+ {1:Set Up The Bars }|
+ ^ |
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {4:[No Name] }|
+ {1:Set Up The Bars }|
+ |
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {2:[No Name] }|
+ |
+ ]])
+
+ meths.input_mouse('left', 'press', '', 1, 5, 10)
+ poke_eventloop()
+ meths.input_mouse('left', 'drag', '', 1, 6, 10)
+ screen:expect([[
+ {1:Set Up The Bars }|
+ ^ |
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {4:[No Name] }|
+ {1:Set Up The Bars }|
+ |
+ {3:~ }|
+ {3:~ }|
+ {2:[No Name] }|
+ |
+ ]])
+
+ meths.input_mouse('left', 'drag', '', 1, 4, 10)
+ screen:expect([[
+ {1:Set Up The Bars }|
+ ^ |
+ {3:~ }|
+ {3:~ }|
+ {4:[No Name] }|
+ {1:Set Up The Bars }|
+ |
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {2:[No Name] }|
+ |
+ ]])
+
+ meths.input_mouse('left', 'press', '', 1, 11, 10)
+ poke_eventloop()
+ meths.input_mouse('left', 'drag', '', 1, 9, 10)
+ screen:expect([[
+ {1:Set Up The Bars }|
+ ^ |
+ {3:~ }|
+ {3:~ }|
+ {4:[No Name] }|
+ {1:Set Up The Bars }|
+ |
+ {3:~ }|
+ {3:~ }|
+ {2:[No Name] }|
+ |
+ |
+ |
+ ]])
+ eq(3, meths.get_option('cmdheight'))
+
+ meths.input_mouse('left', 'drag', '', 1, 11, 10)
+ screen:expect([[
+ {1:Set Up The Bars }|
+ ^ |
+ {3:~ }|
+ {3:~ }|
+ {4:[No Name] }|
+ {1:Set Up The Bars }|
+ |
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {2:[No Name] }|
+ |
+ ]])
+ eq(1, meths.get_option('cmdheight'))
+ end)
+
+ it('properly equalizes window height for window-local value', function()
+ command('set equalalways | set winbar= | setlocal winbar=a | split')
+ command('setlocal winbar= | split')
+ command('setlocal winbar=b | split')
+ screen:expect([[
+ {1:b }|
+ ^ |
+ {4:[No Name] }|
+ {1:b }|
+ |
+ {2:[No Name] }|
+ |
+ {3:~ }|
+ {2:[No Name] }|
+ {1:a }|
+ |
+ {2:[No Name] }|
+ |
+ ]])
+ end)
+
+ it('requires window-local value for floating windows', function()
+ local win = meths.open_win(0, false, { relative = 'editor', row = 2, col = 10, height = 7,
+ width = 30 })
+ meths.set_option_value('winbar', 'bar', {})
+ screen:expect{grid=[[
+ {1:bar }|
+ ^ |
+ {3:~ }{8: }{3: }|
+ {3:~ }{9:~ }{3: }|
+ {3:~ }{9:~ }{3: }|
+ {3:~ }{9:~ }{3: }|
+ {3:~ }{9:~ }{3: }|
+ {3:~ }{9:~ }{3: }|
+ {3:~ }{9:~ }{3: }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ |
+ ]]}
+ meths.set_option_value('winbar', 'floaty bar', { scope = 'local', win = win.id })
+ screen:expect{grid=[[
+ {1:bar }|
+ ^ |
+ {3:~ }{1:floaty bar }{3: }|
+ {3:~ }{8: }{3: }|
+ {3:~ }{9:~ }{3: }|
+ {3:~ }{9:~ }{3: }|
+ {3:~ }{9:~ }{3: }|
+ {3:~ }{9:~ }{3: }|
+ {3:~ }{9:~ }{3: }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ |
+ ]]}
+ end)
+
+ it('works correctly when moving a split', function()
+ screen:try_resize(45, 6)
+ command('set winbar=')
+ command('vsplit')
+ command('setlocal winbar=foo')
+ screen:expect([[
+ {1:foo }│ |
+ ^ │{3:~ }|
+ {3:~ }│{3:~ }|
+ {3:~ }│{3:~ }|
+ {4:[No Name] }{2:[No Name] }|
+ |
+ ]])
+
+ command('wincmd L')
+ screen:expect([[
+ │{1:foo }|
+ {3:~ }│^ |
+ {3:~ }│{3:~ }|
+ {3:~ }│{3:~ }|
+ {2:[No Name] }{4:[No Name] }|
+ |
+ ]])
+
+ command('wincmd w')
+ command('wincmd L')
+ screen:expect([[
+ {1:foo }│^ |
+ │{3:~ }|
+ {3:~ }│{3:~ }|
+ {3:~ }│{3:~ }|
+ {2:[No Name] }{4:[No Name] }|
+ |
+ ]])
+ end)
+
+ it('properly resizes window when there is no space in it', function()
+ command('set winbar= | 1split')
+ screen:expect([[
+ ^ |
+ {4:[No Name] }|
+ |
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {2:[No Name] }|
+ |
+ ]])
+ command('set winbar=a')
+ screen:expect([[
+ {1:a }|
+ ^ |
+ {4:[No Name] }|
+ {1:a }|
+ |
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {2:[No Name] }|
+ |
+ ]])
+ end)
+
+ it('cannot be added unless there is room', function()
+ command('set winbar= | split | split | split | split | split')
+ screen:expect([[
+ ^ |
+ {4:[No Name] }|
+ |
+ {2:[No Name] }|
+ |
+ {2:[No Name] }|
+ |
+ {2:[No Name] }|
+ |
+ {2:[No Name] }|
+ |
+ {2:[No Name] }|
+ |
+ ]])
+ eq('Vim(set):E36: Not enough room', pcall_err(command, 'set winbar=test'))
+ end)
+end)
diff --git a/test/functional/vimscript/api_functions_spec.lua b/test/functional/vimscript/api_functions_spec.lua
index d07e74d40e..8ca245f61a 100644
--- a/test/functional/vimscript/api_functions_spec.lua
+++ b/test/functional/vimscript/api_functions_spec.lua
@@ -49,7 +49,8 @@ describe('eval-API', function()
it('cannot change texts if textlocked', function()
command("autocmd TextYankPost <buffer> ++once call nvim_buf_set_lines(0, 0, -1, v:false, [])")
- eq('Vim(call):E5555: API call: E523: Not allowed here', pcall_err(command, "normal! yy"))
+ eq('Vim(call):E5555: API call: E565: Not allowed to change text or change window',
+ pcall_err(command, "normal! yy"))
end)
it("use buffer numbers and windows ids as handles", function()
diff --git a/test/functional/vimscript/eval_spec.lua b/test/functional/vimscript/eval_spec.lua
index e1459ab5b8..0c2ca8de78 100644
--- a/test/functional/vimscript/eval_spec.lua
+++ b/test/functional/vimscript/eval_spec.lua
@@ -5,7 +5,7 @@
-- null_spec.lua
-- operators_spec.lua
--
--- Tests for the Vimscript |functions| library should live in:
+-- Tests for the Vimscript |builtin-functions| library should live in:
-- test/functional/vimscript/<funcname>_spec.lua
-- test/functional/vimscript/functions_spec.lua
diff --git a/test/functional/vimscript/executable_spec.lua b/test/functional/vimscript/executable_spec.lua
index 048a65188d..b4162b2336 100644
--- a/test/functional/vimscript/executable_spec.lua
+++ b/test/functional/vimscript/executable_spec.lua
@@ -17,6 +17,21 @@ describe('executable()', function()
eq(1, call('executable', 'false'))
end)
+ if iswin() then
+ it('exepath respects shellslash', function()
+ command('let $PATH = fnamemodify("./test/functional/fixtures/bin", ":p")')
+ eq([[test\functional\fixtures\bin\null.CMD]], call('fnamemodify', call('exepath', 'null'), ':.'))
+ command('set shellslash')
+ eq('test/functional/fixtures/bin/null.CMD', call('fnamemodify', call('exepath', 'null'), ':.'))
+ end)
+
+ it('stdpath respects shellslash', function()
+ eq([[build\Xtest_xdg\share\nvim-data]], call('fnamemodify', call('stdpath', 'data'), ':.'))
+ command('set shellslash')
+ eq('build/Xtest_xdg/share/nvim-data', call('fnamemodify', call('stdpath', 'data'), ':.'))
+ end)
+ end
+
it('fails for invalid values', function()
for _, input in ipairs({'v:null', 'v:true', 'v:false', '{}', '[]'}) do
eq('Vim(call):E928: String required', exc_exec('call executable('..input..')'))
diff --git a/test/functional/vimscript/execute_spec.lua b/test/functional/vimscript/execute_spec.lua
index e21c71dc7f..a733b098f5 100644
--- a/test/functional/vimscript/execute_spec.lua
+++ b/test/functional/vimscript/execute_spec.lua
@@ -153,7 +153,7 @@ describe('execute()', function()
function! Test3()
echo 1234
let x = execute('echoerr "abcdef"', 'silent!')
- echon 'ABCD'
+ echon 'ABCDXZYZ'
endfunction
" test 4: silenced echoerr goes as usual
@@ -214,7 +214,7 @@ describe('execute()', function()
~ |
~ |
~ |
- 1234ABCD |
+ 1234ABCDXZYZ |
]])
feed([[:call Test4()<cr>]])
diff --git a/test/functional/vimscript/functions_spec.lua b/test/functional/vimscript/functions_spec.lua
index 0ad7fd8010..20c1400030 100644
--- a/test/functional/vimscript/functions_spec.lua
+++ b/test/functional/vimscript/functions_spec.lua
@@ -1,4 +1,4 @@
--- Tests for misc Vimscript |functions|.
+-- Tests for misc Vimscript |builtin-functions|.
--
-- If a function is non-trivial, consider moving its spec to:
-- test/functional/vimscript/<funcname>_spec.lua
diff --git a/test/functional/vimscript/has_spec.lua b/test/functional/vimscript/has_spec.lua
index c03fd13e0c..4d9b226434 100644
--- a/test/functional/vimscript/has_spec.lua
+++ b/test/functional/vimscript/has_spec.lua
@@ -68,4 +68,11 @@ describe('has()', function()
eq(0, funcs.has('wsl'))
end
end)
+
+ it('does not change v:shell_error', function()
+ local nvim_prog = helpers.nvim_prog
+ funcs.system({nvim_prog, '-es', '+73cquit'})
+ funcs.has('python3') -- use a call whose implementation shells out
+ eq(73, funcs.eval('v:shell_error'))
+ end)
end)
diff --git a/test/functional/vimscript/input_spec.lua b/test/functional/vimscript/input_spec.lua
index 14c02f9eb2..554d15e550 100644
--- a/test/functional/vimscript/input_spec.lua
+++ b/test/functional/vimscript/input_spec.lua
@@ -9,6 +9,7 @@ local source = helpers.source
local command = helpers.command
local exc_exec = helpers.exc_exec
local nvim_async = helpers.nvim_async
+local NIL = helpers.NIL
local screen
@@ -200,6 +201,15 @@ describe('input()', function()
feed(':let var = input({"cancelreturn": "BAR"})<CR>')
feed('<Esc>')
eq('BAR', meths.get_var('var'))
+ feed(':let var = input({"cancelreturn": []})<CR>')
+ feed('<Esc>')
+ eq({}, meths.get_var('var'))
+ feed(':let var = input({"cancelreturn": v:false})<CR>')
+ feed('<Esc>')
+ eq(false, meths.get_var('var'))
+ feed(':let var = input({"cancelreturn": v:null})<CR>')
+ feed('<Esc>')
+ eq(NIL, meths.get_var('var'))
end)
it('supports default string', function()
feed(':let var = input("", "DEF1")<CR>')
@@ -220,8 +230,6 @@ describe('input()', function()
eq('Vim(call):E730: using List as a String',
exc_exec('call input({"prompt": []})'))
eq('Vim(call):E730: using List as a String',
- exc_exec('call input({"cancelreturn": []})'))
- eq('Vim(call):E730: using List as a String',
exc_exec('call input({"default": []})'))
eq('Vim(call):E730: using List as a String',
exc_exec('call input({"completion": []})'))
@@ -418,8 +426,6 @@ describe('inputdialog()', function()
eq('Vim(call):E730: using List as a String',
exc_exec('call inputdialog({"prompt": []})'))
eq('Vim(call):E730: using List as a String',
- exc_exec('call inputdialog({"cancelreturn": []})'))
- eq('Vim(call):E730: using List as a String',
exc_exec('call inputdialog({"default": []})'))
eq('Vim(call):E730: using List as a String',
exc_exec('call inputdialog({"completion": []})'))
diff --git a/test/functional/vimscript/lang_spec.lua b/test/functional/vimscript/lang_spec.lua
index d5254986ab..90437f2ee1 100644
--- a/test/functional/vimscript/lang_spec.lua
+++ b/test/functional/vimscript/lang_spec.lua
@@ -11,6 +11,7 @@ describe('vimscript', function()
return
end
source([[
+ let s:foo = 1
func! <sid>_dummy_function()
echo 1
endfunc
diff --git a/test/functional/vimscript/let_spec.lua b/test/functional/vimscript/let_spec.lua
index 4ff4090a18..ca1b5e8907 100644
--- a/test/functional/vimscript/let_spec.lua
+++ b/test/functional/vimscript/let_spec.lua
@@ -5,9 +5,10 @@ local clear = helpers.clear
local command = helpers.command
local eval = helpers.eval
local meths = helpers.meths
+local exec = helpers.exec
local exec_capture = helpers.exec_capture
local source = helpers.source
-local nvim_dir = helpers.nvim_dir
+local testprg = helpers.testprg
before_each(clear)
@@ -47,33 +48,33 @@ describe(':let', function()
end)
it("multibyte env var #8398 #9267", function()
- command("let $NVIM_TEST = 'AìaB'")
- eq('AìaB', eval('$NVIM_TEST'))
- command("let $NVIM_TEST = 'AaあB'")
- eq('AaあB', eval('$NVIM_TEST'))
+ command("let $NVIM_TEST_LET = 'AìaB'")
+ eq('AìaB', eval('$NVIM_TEST_LET'))
+ command("let $NVIM_TEST_LET = 'AaあB'")
+ eq('AaあB', eval('$NVIM_TEST_LET'))
local mbyte = [[\p* .ม .ม .ม .ม่ .ม่ .ม่ ֹ ֹ ֹ .ֹ .ֹ .ֹ ֹֻ ֹֻ ֹֻ
.ֹֻ .ֹֻ .ֹֻ ֹֻ ֹֻ ֹֻ .ֹֻ .ֹֻ .ֹֻ ֹ ֹ ֹ .ֹ .ֹ .ֹ ֹ ֹ ֹ .ֹ .ֹ .ֹ ֹֻ ֹֻ
.ֹֻ .ֹֻ .ֹֻ a a a ca ca ca à à à]]
- command("let $NVIM_TEST = '"..mbyte.."'")
- eq(mbyte, eval('$NVIM_TEST'))
+ command("let $NVIM_TEST_LET = '"..mbyte.."'")
+ eq(mbyte, eval('$NVIM_TEST_LET'))
end)
it("multibyte env var to child process #8398 #9267", function()
- local cmd_get_child_env = "let g:env_from_child = system(['"..nvim_dir.."/printenv-test', 'NVIM_TEST'])"
- command("let $NVIM_TEST = 'AìaB'")
+ local cmd_get_child_env = ("let g:env_from_child = system(['%s', 'NVIM_TEST_LET'])"):format(testprg('printenv-test'))
+ command("let $NVIM_TEST_LET = 'AìaB'")
command(cmd_get_child_env)
- eq(eval('$NVIM_TEST'), eval('g:env_from_child'))
+ eq(eval('$NVIM_TEST_LET'), eval('g:env_from_child'))
- command("let $NVIM_TEST = 'AaあB'")
+ command("let $NVIM_TEST_LET = 'AaあB'")
command(cmd_get_child_env)
- eq(eval('$NVIM_TEST'), eval('g:env_from_child'))
+ eq(eval('$NVIM_TEST_LET'), eval('g:env_from_child'))
local mbyte = [[\p* .ม .ม .ม .ม่ .ม่ .ม่ ֹ ֹ ֹ .ֹ .ֹ .ֹ ֹֻ ֹֻ ֹֻ
.ֹֻ .ֹֻ .ֹֻ ֹֻ ֹֻ ֹֻ .ֹֻ .ֹֻ .ֹֻ ֹ ֹ ֹ .ֹ .ֹ .ֹ ֹ ֹ ֹ .ֹ .ֹ .ֹ ֹֻ ֹֻ
.ֹֻ .ֹֻ .ֹֻ a a a ca ca ca à à à]]
- command("let $NVIM_TEST = '"..mbyte.."'")
+ command("let $NVIM_TEST_LET = '"..mbyte.."'")
command(cmd_get_child_env)
- eq(eval('$NVIM_TEST'), eval('g:env_from_child'))
+ eq(eval('$NVIM_TEST_LET'), eval('g:env_from_child'))
end)
it("release of list assigned to l: variable does not trigger assertion #12387, #12430", function()
@@ -91,3 +92,19 @@ describe(':let', function()
eq(1, eval('1'))
end)
end)
+
+describe(':let and :const', function()
+ it('have the same output when called without arguments', function()
+ eq(exec_capture('let'), exec_capture('const'))
+ end)
+
+ it('can be used in sandbox', function()
+ exec([[
+ func Func()
+ let l:foo = 'foo'
+ const l:bar = 'bar'
+ endfunc
+ sandbox call Func()
+ ]])
+ end)
+end)
diff --git a/test/functional/vimscript/msgpack_functions_spec.lua b/test/functional/vimscript/msgpack_functions_spec.lua
index 837b629858..cab67d77e4 100644
--- a/test/functional/vimscript/msgpack_functions_spec.lua
+++ b/test/functional/vimscript/msgpack_functions_spec.lua
@@ -5,6 +5,7 @@ local eval, eq = helpers.eval, helpers.eq
local command = helpers.command
local nvim = helpers.nvim
local exc_exec = helpers.exc_exec
+local iswin = helpers.iswin
describe('msgpack*() functions', function()
before_each(clear)
@@ -466,6 +467,11 @@ describe('msgpackparse() function', function()
eval(cmd)
eval(cmd) -- do it again (try to force segfault)
local api_info = eval(cmd) -- do it again
+ if iswin() then
+ helpers.assert_alive()
+ pending('msgpackparse() has a bug on windows')
+ return
+ end
eq({'error_types', 'functions', 'types',
'ui_events', 'ui_options', 'version'}, api_info)
end)
diff --git a/test/functional/vimscript/reltime_spec.lua b/test/functional/vimscript/reltime_spec.lua
index d87943e485..6d661402a6 100644
--- a/test/functional/vimscript/reltime_spec.lua
+++ b/test/functional/vimscript/reltime_spec.lua
@@ -12,7 +12,7 @@ describe('reltimestr(), reltimefloat()', function()
local later = reltime()
local elapsed = reltime(now)
- neq(reltimestr(elapsed), '0.0')
+ neq('0.0', reltimestr(elapsed))
ok(reltimefloat(elapsed) > 0.0)
-- original vim test for < 0.1, but easily fails on travis
ok(nil ~= string.match(reltimestr(elapsed), "0%."))
@@ -26,7 +26,7 @@ describe('reltimestr(), reltimefloat()', function()
eq(0.0, reltimefloat(same))
local differs = reltime(now, later)
- neq(reltimestr(differs), '0.0')
+ neq('0.0', reltimestr(differs))
ok(reltimefloat(differs) > 0.0)
-- original vim test for < 0.1, but easily fails on travis
ok(nil ~= string.match(reltimestr(differs), "0%."))
diff --git a/test/functional/vimscript/screenchar_spec.lua b/test/functional/vimscript/screenchar_spec.lua
new file mode 100644
index 0000000000..767e3c57ef
--- /dev/null
+++ b/test/functional/vimscript/screenchar_spec.lua
@@ -0,0 +1,69 @@
+local helpers = require('test.functional.helpers')(after_each)
+local clear, eq, neq = helpers.clear, helpers.eq, helpers.neq
+local command, meths, funcs = helpers.command, helpers.meths, helpers.funcs
+local tbl_deep_extend = helpers.tbl_deep_extend
+
+-- Set up two overlapping floating windows
+local setup_floating_windows = function()
+ local base_opts = {
+ relative = 'editor',
+ height = 1,
+ width = 2,
+ anchor = 'NW',
+ style = 'minimal',
+ border = 'none',
+ }
+
+ local bufnr_1 = meths.create_buf(false, true)
+ meths.buf_set_lines(bufnr_1, 0, -1, true, { 'aa' })
+ local opts_1 = tbl_deep_extend('force', { row = 0, col = 0, zindex = 11 }, base_opts)
+ meths.open_win(bufnr_1, false, opts_1)
+
+ local bufnr_2 = meths.create_buf(false, true)
+ meths.buf_set_lines(bufnr_2, 0, -1, true, { 'bb' })
+ local opts_2 = tbl_deep_extend('force', { row = 0, col = 1, zindex = 10 }, base_opts)
+ meths.open_win(bufnr_2, false, opts_2)
+
+ command('redraw')
+end
+
+describe('screenchar() and family respect floating windows', function()
+ before_each(function()
+ clear()
+ -- These commands result into visible text `aabc`.
+ -- `aab` - from floating windows, `c` - from text in regular window.
+ meths.buf_set_lines(0, 0, -1, true, { 'cccc' })
+ setup_floating_windows()
+ end)
+
+ it('screenattr()', function()
+ local attr_1 = funcs.screenattr(1, 1)
+ local attr_2 = funcs.screenattr(1, 2)
+ local attr_3 = funcs.screenattr(1, 3)
+ local attr_4 = funcs.screenattr(1, 4)
+ eq(attr_1, attr_2)
+ eq(attr_1, attr_3)
+ neq(attr_1, attr_4)
+ end)
+
+ it('screenchar()', function()
+ eq(97, funcs.screenchar(1, 1))
+ eq(97, funcs.screenchar(1, 2))
+ eq(98, funcs.screenchar(1, 3))
+ eq(99, funcs.screenchar(1, 4))
+ end)
+
+ it('screenchars()', function()
+ eq({ 97 }, funcs.screenchars(1, 1))
+ eq({ 97 }, funcs.screenchars(1, 2))
+ eq({ 98 }, funcs.screenchars(1, 3))
+ eq({ 99 }, funcs.screenchars(1, 4))
+ end)
+
+ it('screenstring()', function()
+ eq('a', funcs.screenstring(1, 1))
+ eq('a', funcs.screenstring(1, 2))
+ eq('b', funcs.screenstring(1, 3))
+ eq('c', funcs.screenstring(1, 4))
+ end)
+end)
diff --git a/test/functional/vimscript/server_spec.lua b/test/functional/vimscript/server_spec.lua
index 238d1aeb0f..6e95459630 100644
--- a/test/functional/vimscript/server_spec.lua
+++ b/test/functional/vimscript/server_spec.lua
@@ -1,11 +1,11 @@
local helpers = require('test.functional.helpers')(after_each)
local eq, neq, eval = helpers.eq, helpers.neq, helpers.eval
-local command = helpers.command
local clear, funcs, meths = helpers.clear, helpers.funcs, helpers.meths
local iswin = helpers.iswin
local ok = helpers.ok
local matches = helpers.matches
local pcall_err = helpers.pcall_err
+local mkdir = helpers.mkdir
local function clear_serverlist()
for _, server in pairs(funcs.serverlist()) do
@@ -14,29 +14,38 @@ local function clear_serverlist()
end
describe('server', function()
- before_each(clear)
+ it('serverstart() stores sockets in $XDG_RUNTIME_DIR', function()
+ local dir = 'Xtest_xdg_run'
+ mkdir(dir)
+ clear({ env={ XDG_RUNTIME_DIR=dir } })
+ matches(dir, funcs.stdpath('run'))
+ if not iswin() then
+ matches(dir, funcs.serverstart())
+ end
+ end)
- it('serverstart() sets $NVIM_LISTEN_ADDRESS on first invocation', function()
- -- Unset $NVIM_LISTEN_ADDRESS
- command('let $NVIM_LISTEN_ADDRESS = ""')
+ it('serverstart(), serverstop() does not set $NVIM', function()
+ clear()
local s = eval('serverstart()')
assert(s ~= nil and s:len() > 0, "serverstart() returned empty")
- eq(s, eval('$NVIM_LISTEN_ADDRESS'))
+ eq('', eval('$NVIM'))
+ eq('', eval('$NVIM_LISTEN_ADDRESS'))
eq(1, eval("serverstop('"..s.."')"))
eq('', eval('$NVIM_LISTEN_ADDRESS'))
end)
it('sets new v:servername if $NVIM_LISTEN_ADDRESS is invalid', function()
clear({env={NVIM_LISTEN_ADDRESS='.'}})
- eq('.', eval('$NVIM_LISTEN_ADDRESS'))
+ -- Cleared on startup.
+ eq('', eval('$NVIM_LISTEN_ADDRESS'))
local servers = funcs.serverlist()
eq(1, #servers)
- ok(string.len(servers[1]) > 4) -- Like /tmp/nvim…/… or \\.\pipe\…
+ ok(string.len(servers[1]) > 4) -- "~/.local/state/nvim…/…" or "\\.\pipe\…"
end)
- it('sets v:servername at startup or if all servers were stopped',
- function()
+ it('sets v:servername at startup or if all servers were stopped', function()
+ clear()
local initial_server = meths.get_vvar('servername')
assert(initial_server ~= nil and initial_server:len() > 0,
'v:servername was not initialized')
@@ -55,19 +64,23 @@ describe('server', function()
eq(1, funcs.serverstop(funcs.serverlist()[1]))
eq('', meths.get_vvar('servername'))
- -- v:servername will take the next available server.
+ -- v:servername and $NVIM take the next available server.
local servername = (iswin() and [[\\.\pipe\Xtest-functional-server-pipe]]
- or 'Xtest-functional-server-socket')
+ or './Xtest-functional-server-socket')
funcs.serverstart(servername)
eq(servername, meths.get_vvar('servername'))
+ -- Not set in the current process, only in children.
+ eq('', eval('$NVIM'))
end)
it('serverstop() returns false for invalid input', function()
+ clear()
eq(0, eval("serverstop('')"))
eq(0, eval("serverstop('bogus-socket-name')"))
end)
- it('parses endpoints correctly', function()
+ it('parses endpoints', function()
+ clear()
clear_serverlist()
eq({}, funcs.serverlist())
@@ -102,19 +115,24 @@ describe('server', function()
eq(expected, funcs.serverlist())
clear_serverlist()
+ -- Address without slashes is a "name" which is appended to a generated path. #8519
+ matches([[.*[/\\]xtest1%.2%.3%.4[^/\\]*]], funcs.serverstart('xtest1.2.3.4'))
+ clear_serverlist()
+
eq('Vim:Failed to start server: invalid argument',
pcall_err(funcs.serverstart, '127.0.0.1:65536')) -- invalid port
eq({}, funcs.serverlist())
end)
it('serverlist() returns the list of servers', function()
+ clear()
-- There should already be at least one server.
local n = eval('len(serverlist())')
-- Add some servers.
local servs = (iswin()
and { [[\\.\pipe\Xtest-pipe0934]], [[\\.\pipe\Xtest-pipe4324]] }
- or { [[Xtest-pipe0934]], [[Xtest-pipe4324]] })
+ or { [[./Xtest-pipe0934]], [[./Xtest-pipe4324]] })
for _, s in ipairs(servs) do
eq(s, eval("serverstart('"..s.."')"))
end
@@ -136,7 +154,6 @@ end)
describe('startup --listen', function()
it('validates', function()
clear()
-
local cmd = { unpack(helpers.nvim_argv) }
table.insert(cmd, '--listen')
matches('nvim.*: Argument missing after: "%-%-listen"', funcs.system(cmd))
@@ -148,9 +165,13 @@ describe('startup --listen', function()
it('sets v:servername, overrides $NVIM_LISTEN_ADDRESS', function()
local addr = (iswin() and [[\\.\pipe\Xtest-listen-pipe]]
- or 'Xtest-listen-pipe')
- clear({ env={ NVIM_LISTEN_ADDRESS='Xtest-env-pipe' },
+ or './Xtest-listen-pipe')
+ clear({ env={ NVIM_LISTEN_ADDRESS='./Xtest-env-pipe' },
args={ '--listen', addr } })
eq(addr, meths.get_vvar('servername'))
+
+ -- Address without slashes is a "name" which is appended to a generated path. #8519
+ clear({ args={ '--listen', 'test-name' } })
+ matches([[.*[/\\]test%-name[^/\\]*]], meths.get_vvar('servername'))
end)
end)
diff --git a/test/functional/vimscript/setpos_spec.lua b/test/functional/vimscript/setpos_spec.lua
index 935f387bcc..02e550dcc0 100644
--- a/test/functional/vimscript/setpos_spec.lua
+++ b/test/functional/vimscript/setpos_spec.lua
@@ -24,41 +24,41 @@ describe('setpos() function', function()
end)
it('can set the current cursor position', function()
setpos(".", {0, 2, 1, 0})
- eq(getpos("."), {0, 2, 1, 0})
+ eq({0, 2, 1, 0}, getpos("."))
setpos(".", {2, 1, 1, 0})
- eq(getpos("."), {0, 1, 1, 0})
+ eq({0, 1, 1, 0}, getpos("."))
local ret = exc_exec('call setpos(".", [1, 1, 1, 0])')
eq(0, ret)
end)
it('can set lowercase marks in the current buffer', function()
setpos("'d", {0, 2, 1, 0})
- eq(getpos("'d"), {0, 2, 1, 0})
+ eq({0, 2, 1, 0}, getpos("'d"))
command('undo')
command('call setpos("\'d", [2, 3, 1, 0])')
- eq(getpos("'d"), {0, 3, 1, 0})
+ eq({0, 3, 1, 0}, getpos("'d"))
end)
it('can set lowercase marks in other buffers', function()
local retval = setpos("'d", {1, 2, 1, 0})
eq(0, retval)
setpos("'d", {1, 2, 1, 0})
- eq(getpos("'d"), {0, 0, 0, 0})
+ eq({0, 0, 0, 0}, getpos("'d"))
command('wincmd w')
- eq(eval('bufnr("%")'), 1)
- eq(getpos("'d"), {0, 2, 1, 0})
+ eq(1, eval('bufnr("%")'))
+ eq({0, 2, 1, 0}, getpos("'d"))
end)
it("fails when setting a mark in a buffer that doesn't exist", function()
local retval = setpos("'d", {3, 2, 1, 0})
eq(-1, retval)
- eq(getpos("'d"), {0, 0, 0, 0})
+ eq({0, 0, 0, 0}, getpos("'d"))
retval = setpos("'D", {3, 2, 1, 0})
eq(-1, retval)
- eq(getpos("'D"), {0, 0, 0, 0})
+ eq({0, 0, 0, 0}, getpos("'D"))
end)
it('can set uppercase marks', function()
setpos("'D", {2, 2, 3, 0})
- eq(getpos("'D"), {2, 2, 3, 0})
+ eq({2, 2, 3, 0}, getpos("'D"))
-- Can set a mark in another buffer
setpos("'D", {1, 2, 2, 0})
- eq(getpos("'D"), {1, 2, 2, 0})
+ eq({1, 2, 2, 0}, getpos("'D"))
end)
end)
diff --git a/test/functional/vimscript/system_spec.lua b/test/functional/vimscript/system_spec.lua
index 24a1f05390..c915556c57 100644
--- a/test/functional/vimscript/system_spec.lua
+++ b/test/functional/vimscript/system_spec.lua
@@ -1,11 +1,13 @@
local helpers = require('test.functional.helpers')(after_each)
local assert_alive = helpers.assert_alive
-local nvim_dir = helpers.nvim_dir
+local testprg = helpers.testprg
local eq, call, clear, eval, feed_command, feed, nvim =
helpers.eq, helpers.call, helpers.clear, helpers.eval, helpers.feed_command,
helpers.feed, helpers.nvim
local command = helpers.command
+local insert = helpers.insert
+local expect = helpers.expect
local exc_exec = helpers.exc_exec
local iswin = helpers.iswin
local os_kill = helpers.os_kill
@@ -30,10 +32,6 @@ describe('system()', function()
before_each(clear)
describe('command passed as a List', function()
- local function printargs_path()
- return nvim_dir..'/printargs-test' .. (iswin() and '.exe' or '')
- end
-
it('throws error if cmd[0] is not executable', function()
eq("Vim:E475: Invalid value for argument cmd: 'this-should-not-exist' is not executable",
pcall_err(call, 'system', { 'this-should-not-exist' }))
@@ -66,23 +64,23 @@ describe('system()', function()
it('quotes arguments correctly #5280', function()
local out = call('system',
- { printargs_path(), [[1]], [[2 "3]], [[4 ' 5]], [[6 ' 7']] })
+ { testprg('printargs-test'), [[1]], [[2 "3]], [[4 ' 5]], [[6 ' 7']] })
eq(0, eval('v:shell_error'))
eq([[arg1=1;arg2=2 "3;arg3=4 ' 5;arg4=6 ' 7';]], out)
- out = call('system', { printargs_path(), [['1]], [[2 "3]] })
+ out = call('system', { testprg('printargs-test'), [['1]], [[2 "3]] })
eq(0, eval('v:shell_error'))
eq([[arg1='1;arg2=2 "3;]], out)
- out = call('system', { printargs_path(), "A\nB" })
+ out = call('system', { testprg('printargs-test'), "A\nB" })
eq(0, eval('v:shell_error'))
eq("arg1=A\nB;", out)
end)
it('calls executable in $PATH', function()
- if 0 == eval("executable('python')") then pending("missing `python`") end
- eq("foo\n", eval([[system(['python', '-c', 'print("foo")'])]]))
+ if 0 == eval("executable('python3')") then pending("missing `python3`") end
+ eq("foo\n", eval([[system(['python3', '-c', 'print("foo")'])]]))
eq(0, eval('v:shell_error'))
end)
@@ -167,7 +165,7 @@ describe('system()', function()
end
end)
- it('works with powershell', function()
+ it('with powershell', function()
helpers.set_shell_powershell()
eq('a\nb\n', eval([[system('Write-Output a b')]]))
eq('C:\\\n', eval([[system('cd c:\; (Get-Location).Path')]]))
@@ -175,12 +173,11 @@ describe('system()', function()
end)
end
- it('works with powershell w/ UTF-8 text (#13713)', function()
+ it('powershell w/ UTF-8 text #13713', function()
if not helpers.has_powershell() then
- pending("not tested; powershell was not found", function() end)
+ pending("powershell not found", function() end)
return
end
- -- Should work with recommended config used in helper
helpers.set_shell_powershell()
eq('ああ\n', eval([[system('Write-Output "ああ"')]]))
-- Sanity test w/ default encoding
@@ -268,7 +265,7 @@ describe('system()', function()
:call system("for /L %I in (1,0,2) do @echo y") |]]
or [[
:call system("yes") |]]))
- feed('<c-c>')
+ feed('foo<c-c>')
screen:expect([[
^ |
~ |
@@ -286,6 +283,49 @@ describe('system()', function()
Type :qa and press <Enter> to exit Nvim |
]])
end)
+
+ it('`yes` interrupted with mapped CTRL-C', function()
+ command('nnoremap <C-C> i')
+ feed(':call system("' .. (iswin()
+ and 'for /L %I in (1,0,2) do @echo y'
+ or 'yes') .. '")<cr>')
+ screen:expect([[
+ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+]] .. (iswin()
+ and [[
+ :call system("for /L %I in (1,0,2) do @echo y") |]]
+ or [[
+ :call system("yes") |]]))
+ feed('foo<c-c>')
+ screen:expect([[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ -- INSERT -- |
+ ]])
+ end)
end)
describe('passing no input', function()
@@ -387,7 +427,7 @@ describe('system()', function()
end)
it("with a program that doesn't close stdout will exit properly after passing input", function()
- local out = eval(string.format("system('%s', 'clip-data')", nvim_dir..'/streams-test'))
+ local out = eval(string.format("system('%s', 'clip-data')", testprg('streams-test')))
assert(out:sub(0, 5) == 'pid: ', out)
os_kill(out:match("%d+"))
end)
@@ -527,7 +567,7 @@ describe('systemlist()', function()
end)
-- Unlike `system()` which uses SOH to represent NULs, with `systemlist()`
- -- input and ouput are the same.
+ -- input and output are the same.
describe('with linefeed characters inside list items', function()
it('converts linefeed characters to NULs', function()
eq({'l1\np2', 'line2\na\nb', 'l3'},
@@ -566,17 +606,16 @@ describe('systemlist()', function()
end)
it("with a program that doesn't close stdout will exit properly after passing input", function()
- local out = eval(string.format("systemlist('%s', 'clip-data')", nvim_dir..'/streams-test'))
+ local out = eval(string.format("systemlist('%s', 'clip-data')", testprg('streams-test')))
assert(out[1]:sub(0, 5) == 'pid: ', out)
os_kill(out[1]:match("%d+"))
end)
- it('works with powershell w/ UTF-8 text (#13713)', function()
+ it('powershell w/ UTF-8 text #13713', function()
if not helpers.has_powershell() then
- pending("not tested; powershell was not found", function() end)
+ pending("powershell not found", function() end)
return
end
- -- Should work with recommended config used in helper
helpers.set_shell_powershell()
eq({iswin() and 'あ\r' or 'あ'}, eval([[systemlist('Write-Output あ')]]))
-- Sanity test w/ default encoding
@@ -587,3 +626,31 @@ describe('systemlist()', function()
end)
end)
+
+describe('shell :!', function()
+ before_each(clear)
+
+ it(':{range}! with powershell filter/redirect #16271', function()
+ local screen = Screen.new(500, 8)
+ screen:attach()
+ local found = helpers.set_shell_powershell(true)
+ insert([[
+ 3
+ 1
+ 4
+ 2]])
+ feed(':4verbose %!sort<cr>')
+ screen:expect{
+ any=[[Executing command: .?Start%-Process sort %-RedirectStandardInput .* %-RedirectStandardOutput .* %-NoNewWindow %-Wait]]
+ }
+ feed('<CR>')
+ if found then
+ -- Not using fake powershell, so we can test the result.
+ expect([[
+ 1
+ 2
+ 3
+ 4]])
+ end
+ end)
+end)
diff --git a/test/functional/vimscript/timer_spec.lua b/test/functional/vimscript/timer_spec.lua
index e45b64422f..5463cfb234 100644
--- a/test/functional/vimscript/timer_spec.lua
+++ b/test/functional/vimscript/timer_spec.lua
@@ -96,7 +96,7 @@ describe('timers', function()
nvim_async("command", "let g:val = 0 | let g:c = getchar()")
retry(nil, nil, function()
local val = eval("g:val")
- ok(val >= 2, "expected >= 2, got: "..tostring(val))
+ ok(val >= 2, '>= 2', tostring(val))
eq(0, eval("getchar(1)"))
end)
feed("c")
@@ -272,4 +272,12 @@ describe('timers', function()
]]
eq("Vim(call):E48: Not allowed in sandbox", exc_exec("sandbox call timer_start(0, 'Scary')"))
end)
+
+ it('can be triggered after an empty string <expr> mapping #17257', function()
+ local screen = Screen.new(40, 6)
+ screen:attach()
+ command([=[imap <expr> <F2> [timer_start(0, { _ -> execute("throw 'x'", "") }), ''][-1]]=])
+ feed('i<F2>')
+ screen:expect({any='E605: Exception not caught: x'})
+ end)
end)
diff --git a/test/helpers.lua b/test/helpers.lua
index 87431e4342..7ec9beea92 100644
--- a/test/helpers.lua
+++ b/test/helpers.lua
@@ -4,7 +4,7 @@ local assert = require('luassert')
local luv = require('luv')
local lfs = require('lfs')
local relpath = require('pl.path').relpath
-local Paths = require('test.config.paths')
+local Paths = require('test.cmakeconfig.paths')
assert:set_parameter('TableFormatLevel', 100)
@@ -40,10 +40,6 @@ function module.popen_r(...)
return io.popen(module.argss_to_cmd(...), 'r')
end
-function module.popen_w(...)
- return io.popen(module.argss_to_cmd(...), 'w')
-end
-
-- sleeps the test runner (_not_ the nvim instance)
function module.sleep(ms)
luv.sleep(ms)
@@ -55,42 +51,31 @@ local check_logs_useless_lines = {
['See README_MISSING_SYSCALL_OR_IOCTL for guidance']=3,
}
---- Invokes `fn` and includes the tail of `logfile` in the error message if it
---- fails.
----
----@param logfile Log file, defaults to $NVIM_LOG_FILE or '.nvimlog'
----@param fn Function to invoke
----@param ... Function arguments
-local function dumplog(logfile, fn, ...)
- -- module.validate({
- -- logfile={logfile,'s',true},
- -- fn={fn,'f',false},
- -- })
- local status, rv = pcall(fn, ...)
- if status == false then
- logfile = logfile or os.getenv('NVIM_LOG_FILE') or '.nvimlog'
- local logtail = module.read_nvim_log(logfile)
- error(string.format('%s\n%s', tostring(rv), logtail))
- end
+function module.eq(expected, actual, context)
+ return assert.are.same(expected, actual, context)
end
-function module.eq(expected, actual, context, logfile)
- return dumplog(logfile, assert.are.same, expected, actual, context)
+function module.neq(expected, actual, context)
+ return assert.are_not.same(expected, actual, context)
end
-function module.neq(expected, actual, context, logfile)
- return dumplog(logfile, assert.are_not.same, expected, actual, context)
-end
-function module.ok(res, msg, logfile)
- return dumplog(logfile, assert.is_true, res, msg)
+
+--- Asserts that `cond` is true, or prints a message.
+---
+--- @param cond (boolean) expression to assert
+--- @param expected (any) description of expected result
+--- @param actual (any) description of actual result
+function module.ok(cond, expected, actual)
+ assert((not expected and not actual) or (expected and actual), 'if "expected" is given, "actual" is also required')
+ local msg = expected and ('expected %s, got: %s'):format(expected, tostring(actual)) or nil
+ return assert(cond, msg)
end
--- TODO(bfredl): this should "failure" not "error" (issue with dumplog() )
local function epicfail(state, arguments, _)
state.failure_message = arguments[1]
return false
end
assert:register("assertion", "epicfail", epicfail)
-function module.fail(msg, logfile)
- return dumplog(logfile, assert.epicfail, msg)
+function module.fail(msg)
+ return assert.epicfail(msg)
end
function module.matches(pat, actual)
@@ -100,20 +85,33 @@ function module.matches(pat, actual)
error(string.format('Pattern does not match.\nPattern:\n%s\nActual:\n%s', pat, actual))
end
---- Asserts that `pat` matches one or more lines in the tail of $NVIM_LOG_FILE.
+--- Asserts that `pat` matches (or *not* if inverse=true) any line in the tail of `logfile`.
---
----@param pat (string) Lua pattern to search for in the log file.
----@param logfile (string, default=$NVIM_LOG_FILE) full path to log file.
-function module.assert_log(pat, logfile)
+---@param pat (string) Lua pattern to match lines in the log file
+---@param logfile (string) Full path to log file (default=$NVIM_LOG_FILE)
+---@param nrlines (number) Search up to this many log lines
+---@param inverse (boolean) Assert that the pattern does NOT match.
+function module.assert_log(pat, logfile, nrlines, inverse)
logfile = logfile or os.getenv('NVIM_LOG_FILE') or '.nvimlog'
- local nrlines = 10
+ assert(logfile ~= nil, 'no logfile')
+ nrlines = nrlines or 10
+ inverse = inverse or false
local lines = module.read_file_list(logfile, -nrlines) or {}
+ local msg = string.format('Pattern %q %sfound in log (last %d lines): %s:\n%s',
+ pat, (inverse and '' or 'not '), nrlines, logfile, ' '..table.concat(lines, '\n '))
for _,line in ipairs(lines) do
- if line:match(pat) then return end
+ if line:match(pat) then
+ if inverse then error(msg) else return end
+ end
end
- local logtail = module.read_nvim_log(logfile)
- error(string.format('Pattern %q not found in log (last %d lines): %s:\n%s',
- pat, nrlines, logfile, logtail))
+ if not inverse then error(msg) end
+end
+
+--- Asserts that `pat` does NOT matche any line in the tail of `logfile`.
+---
+--- @see assert_log
+function module.assert_nolog(pat, logfile, nrlines)
+ return module.assert_log(pat, logfile, nrlines, true)
end
-- Invokes `fn` and returns the error string (with truncated paths), or raises
@@ -271,7 +269,7 @@ module.uname = (function()
return platform
end
- if os.getenv("SYSTEM_NAME") then -- From CMAKE_SYSTEM_NAME.
+ if os.getenv("SYSTEM_NAME") then -- From CMAKE_HOST_SYSTEM_NAME.
platform = string.lower(os.getenv("SYSTEM_NAME"))
return platform
end
@@ -307,6 +305,7 @@ local function tmpdir_is_local(dir)
return not not (dir and string.find(dir, 'Xtest'))
end
+--- Creates a new temporary file for use by tests.
module.tmpname = (function()
local seq = 0
local tmpdir = tmpdir_get()
@@ -314,7 +313,8 @@ module.tmpname = (function()
if tmpdir_is_local(tmpdir) then
-- Cannot control os.tmpname() dir, so hack our own tmpname() impl.
seq = seq + 1
- local fname = tmpdir..'/nvim-test-lua-'..seq
+ -- "…/Xtest_tmpdir/T42.7"
+ local fname = ('%s/%s.%d'):format(tmpdir, (_G._nvim_test_id or 'nvim-test'), seq)
io.open(fname, 'w'):close()
return fname
else
@@ -409,17 +409,6 @@ function module.check_cores(app, force)
end
end
-function module.which(exe)
- local pipe = module.popen_r('which', exe)
- local ret = pipe:read('*a')
- pipe:close()
- if ret == '' then
- return nil
- else
- return ret:sub(1, -2)
- end
-end
-
function module.repeated_read_cmd(...)
for _ = 1, 10 do
local stream = module.popen_r(...)
@@ -801,12 +790,10 @@ end
function module.isCI(name)
local any = (name == nil)
- assert(any or name == 'appveyor' or name == 'travis' or name == 'sourcehut' or name == 'github')
- local av = ((any or name == 'appveyor') and nil ~= os.getenv('APPVEYOR'))
- local tr = ((any or name == 'travis') and nil ~= os.getenv('TRAVIS'))
+ assert(any or name == 'sourcehut' or name == 'github')
local sh = ((any or name == 'sourcehut') and nil ~= os.getenv('SOURCEHUT'))
local gh = ((any or name == 'github') and nil ~= os.getenv('GITHUB_ACTIONS'))
- return tr or av or sh or gh
+ return sh or gh
end
@@ -815,7 +802,7 @@ end
function module.read_nvim_log(logfile, ci_rename)
logfile = logfile or os.getenv('NVIM_LOG_FILE') or '.nvimlog'
local is_ci = module.isCI()
- local keep = is_ci and 999 or 10
+ local keep = is_ci and 100 or 10
local lines = module.read_file_list(logfile, -keep) or {}
local log = (('-'):rep(78)..'\n'
..string.format('$NVIM_LOG_FILE: %s\n', logfile)
diff --git a/test/symbolic/klee/nvim/keymap.c b/test/symbolic/klee/nvim/keymap.c
index 8bc597d7fe..26cdd9db47 100644
--- a/test/symbolic/klee/nvim/keymap.c
+++ b/test/symbolic/klee/nvim/keymap.c
@@ -1,7 +1,7 @@
#include <stdbool.h>
#include "nvim/types.h"
-#include "nvim/keymap.h"
+#include "nvim/keycodes.h"
#include "nvim/ascii.h"
#include "nvim/eval/typval.h"
diff --git a/test/symbolic/klee/run.sh b/test/symbolic/klee/run.sh
index 0234a935b5..97ce42c31b 100755
--- a/test/symbolic/klee/run.sh
+++ b/test/symbolic/klee/run.sh
@@ -54,7 +54,7 @@ main() {
includes="$includes -I$PROJECT_SOURCE_DIR/src"
includes="$includes -I$PROJECT_BINARY_DIR/src/nvim/auto"
includes="$includes -I$PROJECT_BINARY_DIR/include"
- includes="$includes -I$PROJECT_BINARY_DIR/config"
+ includes="$includes -I$PROJECT_BINARY_DIR/cmake.config"
includes="$includes -I/host-includes"
local defines=
diff --git a/test/symbolic/klee/viml_expressions_lexer.c b/test/symbolic/klee/viml_expressions_lexer.c
index ee7dc312e9..03c9d66ca4 100644
--- a/test/symbolic/klee/viml_expressions_lexer.c
+++ b/test/symbolic/klee/viml_expressions_lexer.c
@@ -17,7 +17,7 @@
#include "nvim/charset.c"
#include "nvim/garray.c"
#include "nvim/gettext.c"
-#include "nvim/keymap.c"
+#include "nvim/keycodes.c"
#include "nvim/viml/parser/expressions.c"
#define INPUT_SIZE 7
diff --git a/test/symbolic/klee/viml_expressions_parser.c b/test/symbolic/klee/viml_expressions_parser.c
index 9a876ed3fa..b0e1d71127 100644
--- a/test/symbolic/klee/viml_expressions_parser.c
+++ b/test/symbolic/klee/viml_expressions_parser.c
@@ -17,7 +17,7 @@
#include "nvim/garray.c"
#include "nvim/gettext.c"
#include "nvim/viml/parser/expressions.c"
-#include "nvim/keymap.c"
+#include "nvim/keycodes.c"
#define INPUT_SIZE 50
diff --git a/test/unit/buffer_spec.lua b/test/unit/buffer_spec.lua
index 3692e19379..204d713fb7 100644
--- a/test/unit/buffer_spec.lua
+++ b/test/unit/buffer_spec.lua
@@ -17,22 +17,22 @@ describe('buffer functions', function()
return buffer.buflist_new(c_file, c_file, 1, flags)
end
- local close_buffer = function(win, buf, action, abort_if_last)
- return buffer.close_buffer(win, buf, action, abort_if_last)
+ local close_buffer = function(win, buf, action, abort_if_last, ignore_abort)
+ return buffer.close_buffer(win, buf, action, abort_if_last, ignore_abort)
end
local path1 = 'test_file_path'
local path2 = 'file_path_test'
local path3 = 'path_test_file'
- before_each(function()
+ setup(function()
-- create the files
io.open(path1, 'w').close()
io.open(path2, 'w').close()
io.open(path3, 'w').close()
end)
- after_each(function()
+ teardown(function()
os.remove(path1)
os.remove(path2)
os.remove(path3)
@@ -53,7 +53,7 @@ describe('buffer functions', function()
itp('should view a closed and hidden buffer as valid', function()
local buf = buflist_new(path1, buffer.BLN_LISTED)
- close_buffer(NULL, buf, 0, 0)
+ close_buffer(NULL, buf, 0, 0, 0)
eq(true, buffer.buf_valid(buf))
end)
@@ -61,7 +61,7 @@ describe('buffer functions', function()
itp('should view a closed and unloaded buffer as valid', function()
local buf = buflist_new(path1, buffer.BLN_LISTED)
- close_buffer(NULL, buf, buffer.DOBUF_UNLOAD, 0)
+ close_buffer(NULL, buf, buffer.DOBUF_UNLOAD, 0, 0)
eq(true, buffer.buf_valid(buf))
end)
@@ -69,7 +69,7 @@ describe('buffer functions', function()
itp('should view a closed and wiped buffer as invalid', function()
local buf = buflist_new(path1, buffer.BLN_LISTED)
- close_buffer(NULL, buf, buffer.DOBUF_WIPE, 0)
+ close_buffer(NULL, buf, buffer.DOBUF_WIPE, 0, 0)
eq(false, buffer.buf_valid(buf))
end)
@@ -90,7 +90,7 @@ describe('buffer functions', function()
eq(buf.handle, buflist_findpat(path1, ONLY_LISTED))
- close_buffer(NULL, buf, buffer.DOBUF_WIPE, 0)
+ close_buffer(NULL, buf, buffer.DOBUF_WIPE, 0, 0)
end)
itp('should prefer to match the start of a file path', function()
@@ -102,9 +102,9 @@ describe('buffer functions', function()
eq(buf2.handle, buflist_findpat("file", ONLY_LISTED))
eq(buf3.handle, buflist_findpat("path", ONLY_LISTED))
- close_buffer(NULL, buf1, buffer.DOBUF_WIPE, 0)
- close_buffer(NULL, buf2, buffer.DOBUF_WIPE, 0)
- close_buffer(NULL, buf3, buffer.DOBUF_WIPE, 0)
+ close_buffer(NULL, buf1, buffer.DOBUF_WIPE, 0, 0)
+ close_buffer(NULL, buf2, buffer.DOBUF_WIPE, 0, 0)
+ close_buffer(NULL, buf3, buffer.DOBUF_WIPE, 0, 0)
end)
itp('should prefer to match the end of a file over the middle', function()
@@ -118,7 +118,7 @@ describe('buffer functions', function()
--}
--{ When: We close buf2
- close_buffer(NULL, buf2, buffer.DOBUF_WIPE, 0)
+ close_buffer(NULL, buf2, buffer.DOBUF_WIPE, 0, 0)
-- And: Open buf1, which has 'file' in the middle of its name
local buf1 = buflist_new(path1, buffer.BLN_LISTED)
@@ -127,8 +127,8 @@ describe('buffer functions', function()
eq(buf3.handle, buflist_findpat("file", ONLY_LISTED))
--}
- close_buffer(NULL, buf1, buffer.DOBUF_WIPE, 0)
- close_buffer(NULL, buf3, buffer.DOBUF_WIPE, 0)
+ close_buffer(NULL, buf1, buffer.DOBUF_WIPE, 0, 0)
+ close_buffer(NULL, buf3, buffer.DOBUF_WIPE, 0, 0)
end)
itp('should match a unique fragment of a file path', function()
@@ -138,9 +138,9 @@ describe('buffer functions', function()
eq(buf3.handle, buflist_findpat("_test_", ONLY_LISTED))
- close_buffer(NULL, buf1, buffer.DOBUF_WIPE, 0)
- close_buffer(NULL, buf2, buffer.DOBUF_WIPE, 0)
- close_buffer(NULL, buf3, buffer.DOBUF_WIPE, 0)
+ close_buffer(NULL, buf1, buffer.DOBUF_WIPE, 0, 0)
+ close_buffer(NULL, buf2, buffer.DOBUF_WIPE, 0, 0)
+ close_buffer(NULL, buf3, buffer.DOBUF_WIPE, 0, 0)
end)
itp('should include / ignore unlisted buffers based on the flag.', function()
@@ -152,7 +152,7 @@ describe('buffer functions', function()
--}
--{ When: We unlist the buffer
- close_buffer(NULL, buf3, buffer.DOBUF_DEL, 0)
+ close_buffer(NULL, buf3, buffer.DOBUF_DEL, 0, 0)
-- Then: It should not find the buffer when searching only listed buffers
eq(-1, buflist_findpat("_test_", ONLY_LISTED))
@@ -162,7 +162,7 @@ describe('buffer functions', function()
--}
--{ When: We wipe the buffer
- close_buffer(NULL, buf3, buffer.DOBUF_WIPE, 0)
+ close_buffer(NULL, buf3, buffer.DOBUF_WIPE, 0, 0)
-- Then: It should not find the buffer at all
eq(-1, buflist_findpat("_test_", ONLY_LISTED))
@@ -180,7 +180,7 @@ describe('buffer functions', function()
--}
--{ When: The first buffer is unlisted
- close_buffer(NULL, buf1, buffer.DOBUF_DEL, 0)
+ close_buffer(NULL, buf1, buffer.DOBUF_DEL, 0, 0)
-- Then: The second buffer is preferred because
-- unlisted buffers are not allowed
@@ -194,7 +194,7 @@ describe('buffer functions', function()
--}
--{ When: We unlist the second buffer
- close_buffer(NULL, buf2, buffer.DOBUF_DEL, 0)
+ close_buffer(NULL, buf2, buffer.DOBUF_DEL, 0, 0)
-- Then: The first buffer is preferred again
-- because buf1 matches better which takes precedence
@@ -205,8 +205,8 @@ describe('buffer functions', function()
eq(-1, buflist_findpat("test", ONLY_LISTED))
--}
- close_buffer(NULL, buf1, buffer.DOBUF_WIPE, 0)
- close_buffer(NULL, buf2, buffer.DOBUF_WIPE, 0)
+ close_buffer(NULL, buf1, buffer.DOBUF_WIPE, 0, 0)
+ close_buffer(NULL, buf2, buffer.DOBUF_WIPE, 0, 0)
end)
end)
@@ -249,7 +249,7 @@ describe('buffer functions', function()
--
-- @param arg Options can be placed in an optional dictionary as the last parameter
-- .expected_cell_count The expected number of cells build_stl_str_hl will return
- -- .expected_byte_length The expected byte length of the string
+ -- .expected_byte_length The expected byte length of the string (defaults to byte length of expected_stl)
-- .file_name The name of the file to be tested (useful in %f type tests)
-- .fillchar The character that will be used to fill any 'extra' space in the stl
local function statusline_test (description,
@@ -264,7 +264,7 @@ describe('buffer functions', function()
local fillchar = option.fillchar or (' '):byte()
local expected_cell_count = option.expected_cell_count or statusline_cell_count
- local expected_byte_length = option.expected_byte_length or expected_cell_count
+ local expected_byte_length = option.expected_byte_length or #expected_stl
itp(description, function()
if option.file_name then
@@ -312,12 +312,12 @@ describe('buffer functions', function()
statusline_test('should put fillchar `~` in between text', 10,
'abc%=def', 'abc~~~~def',
{fillchar=('~'):byte()})
+ statusline_test('should put fillchar `━` in between text', 10,
+ 'abc%=def', 'abc━━━━def',
+ {fillchar=0x2501})
statusline_test('should handle zero-fillchar as a space', 10,
'abcde%=', 'abcde ',
{fillchar=0})
- statusline_test('should handle multibyte-fillchar as a dash', 10,
- 'abcde%=', 'abcde-----',
- {fillchar=0x80})
statusline_test('should print the tail file name', 80,
'%t', 'buffer_spec.lua',
{file_name='test/unit/buffer_spec.lua', expected_cell_count=15})
@@ -366,70 +366,86 @@ describe('buffer functions', function()
statusline_test('should ignore trailing %', 3, 'abc%', 'abc')
- -- alignment testing
- statusline_test('should right align when using =', 20,
- 'neo%=vim', 'neo vim')
- statusline_test('should, when possible, center text when using %=text%=', 20,
- 'abc%=neovim%=def', 'abc neovim def')
- statusline_test('should handle uneven spacing in the buffer when using %=text%=', 20,
- 'abc%=neo_vim%=def', 'abc neo_vim def')
- statusline_test('should have equal spaces even with non-equal sides when using =', 20,
- 'foobar%=test%=baz', 'foobar test baz')
- statusline_test('should have equal spaces even with longer right side when using =', 20,
- 'a%=test%=longtext', 'a test longtext')
- statusline_test('should handle an empty left side when using ==', 20,
- '%=test%=baz', ' test baz')
- statusline_test('should handle an empty right side when using ==', 20,
- 'foobar%=test%=', 'foobar test ')
- statusline_test('should handle consecutive empty ==', 20,
- '%=%=test%=', ' test ')
- statusline_test('should handle an = alone', 20,
- '%=', ' ')
- statusline_test('should right align text when it is alone with =', 20,
- '%=foo', ' foo')
- statusline_test('should left align text when it is alone with =', 20,
- 'foo%=', 'foo ')
-
- statusline_test('should approximately center text when using %=text%=', 21,
- 'abc%=neovim%=def', 'abc neovim def')
- statusline_test('should completely fill the buffer when using %=text%=', 21,
- 'abc%=neo_vim%=def', 'abc neo_vim def')
- statusline_test('should have equal spaces even with non-equal sides when using =', 21,
- 'foobar%=test%=baz', 'foobar test baz')
- statusline_test('should have equal spaces even with longer right side when using =', 21,
- 'a%=test%=longtext', 'a test longtext')
- statusline_test('should handle an empty left side when using ==', 21,
- '%=test%=baz', ' test baz')
- statusline_test('should handle an empty right side when using ==', 21,
- 'foobar%=test%=', 'foobar test ')
-
- statusline_test('should quadrant the text when using 3 %=', 40,
- 'abcd%=n%=eovim%=ef', 'abcd n eovim ef')
- statusline_test('should work well with %t', 40,
- '%t%=right_aligned', 'buffer_spec.lua right_aligned',
+ -- alignment testing with fillchar
+ local function statusline_test_align (description,
+ statusline_cell_count,
+ input_stl,
+ expected_stl,
+ arg)
+ arg = arg or {}
+ statusline_test(description .. ' without fillchar',
+ statusline_cell_count, input_stl, expected_stl:gsub('%~', ' '), arg)
+ arg.fillchar = ('!'):byte()
+ statusline_test(description .. ' with fillchar `!`',
+ statusline_cell_count, input_stl, expected_stl:gsub('%~', '!'), arg)
+ arg.fillchar = 0x2501
+ statusline_test(description .. ' with fillchar `━`',
+ statusline_cell_count, input_stl, expected_stl:gsub('%~', '━'), arg)
+ end
+
+ statusline_test_align('should right align when using =', 20,
+ 'neo%=vim', 'neo~~~~~~~~~~~~~~vim')
+ statusline_test_align('should, when possible, center text when using %=text%=', 20,
+ 'abc%=neovim%=def', 'abc~~~~neovim~~~~def')
+ statusline_test_align('should handle uneven spacing in the buffer when using %=text%=', 20,
+ 'abc%=neo_vim%=def', 'abc~~~neo_vim~~~~def')
+ statusline_test_align('should have equal spaces even with non-equal sides when using =', 20,
+ 'foobar%=test%=baz', 'foobar~~~test~~~~baz')
+ statusline_test_align('should have equal spaces even with longer right side when using =', 20,
+ 'a%=test%=longtext', 'a~~~test~~~~longtext')
+ statusline_test_align('should handle an empty left side when using ==', 20,
+ '%=test%=baz', '~~~~~~test~~~~~~~baz')
+ statusline_test_align('should handle an empty right side when using ==', 20,
+ 'foobar%=test%=', 'foobar~~~~~test~~~~~')
+ statusline_test_align('should handle consecutive empty ==', 20,
+ '%=%=test%=', '~~~~~~~~~~test~~~~~~')
+ statusline_test_align('should handle an = alone', 20,
+ '%=', '~~~~~~~~~~~~~~~~~~~~')
+ statusline_test_align('should right align text when it is alone with =', 20,
+ '%=foo', '~~~~~~~~~~~~~~~~~foo')
+ statusline_test_align('should left align text when it is alone with =', 20,
+ 'foo%=', 'foo~~~~~~~~~~~~~~~~~')
+
+ statusline_test_align('should approximately center text when using %=text%=', 21,
+ 'abc%=neovim%=def', 'abc~~~~neovim~~~~~def')
+ statusline_test_align('should completely fill the buffer when using %=text%=', 21,
+ 'abc%=neo_vim%=def', 'abc~~~~neo_vim~~~~def')
+ statusline_test_align('should have equal spacing even with non-equal sides when using =', 21,
+ 'foobar%=test%=baz', 'foobar~~~~test~~~~baz')
+ statusline_test_align('should have equal spacing even with longer right side when using =', 21,
+ 'a%=test%=longtext', 'a~~~~test~~~~longtext')
+ statusline_test_align('should handle an empty left side when using ==', 21,
+ '%=test%=baz', '~~~~~~~test~~~~~~~baz')
+ statusline_test_align('should handle an empty right side when using ==', 21,
+ 'foobar%=test%=', 'foobar~~~~~test~~~~~~')
+
+ statusline_test_align('should quadrant the text when using 3 %=', 40,
+ 'abcd%=n%=eovim%=ef', 'abcd~~~~~~~~~n~~~~~~~~~eovim~~~~~~~~~~ef')
+ statusline_test_align('should work well with %t', 40,
+ '%t%=right_aligned', 'buffer_spec.lua~~~~~~~~~~~~right_aligned',
{file_name='test/unit/buffer_spec.lua'})
- statusline_test('should work well with %t and regular text', 40,
- 'l%=m_l %t m_r%=r', 'l m_l buffer_spec.lua m_r r',
+ statusline_test_align('should work well with %t and regular text', 40,
+ 'l%=m_l %t m_r%=r', 'l~~~~~~~m_l buffer_spec.lua m_r~~~~~~~~r',
{file_name='test/unit/buffer_spec.lua'})
- statusline_test('should work well with %=, %t, %L, and %l', 40,
- '%t %= %L %= %l', 'buffer_spec.lua 1 0',
+ statusline_test_align('should work well with %=, %t, %L, and %l', 40,
+ '%t %= %L %= %l', 'buffer_spec.lua ~~~~~~~~~ 1 ~~~~~~~~~~ 0',
{file_name='test/unit/buffer_spec.lua'})
- statusline_test('should quadrant the text when using 3 %=', 41,
- 'abcd%=n%=eovim%=ef', 'abcd n eovim ef')
- statusline_test('should work well with %t', 41,
- '%t%=right_aligned', 'buffer_spec.lua right_aligned',
+ statusline_test_align('should quadrant the text when using 3 %=', 41,
+ 'abcd%=n%=eovim%=ef', 'abcd~~~~~~~~~n~~~~~~~~~eovim~~~~~~~~~~~ef')
+ statusline_test_align('should work well with %t', 41,
+ '%t%=right_aligned', 'buffer_spec.lua~~~~~~~~~~~~~right_aligned',
{file_name='test/unit/buffer_spec.lua'})
- statusline_test('should work well with %t and regular text', 41,
- 'l%=m_l %t m_r%=r', 'l m_l buffer_spec.lua m_r r',
+ statusline_test_align('should work well with %t and regular text', 41,
+ 'l%=m_l %t m_r%=r', 'l~~~~~~~~m_l buffer_spec.lua m_r~~~~~~~~r',
{file_name='test/unit/buffer_spec.lua'})
- statusline_test('should work well with %=, %t, %L, and %l', 41,
- '%t %= %L %= %l', 'buffer_spec.lua 1 0',
+ statusline_test_align('should work well with %=, %t, %L, and %l', 41,
+ '%t %= %L %= %l', 'buffer_spec.lua ~~~~~~~~~~ 1 ~~~~~~~~~~ 0',
{file_name='test/unit/buffer_spec.lua'})
- statusline_test('should work with 10 %=', 50,
+ statusline_test_align('should work with 10 %=', 50,
'aaaa%=b%=c%=d%=e%=fg%=hi%=jk%=lmnop%=qrstuv%=wxyz',
- 'aaaa b c d e fg hi jk lmnop qrstuv wxyz')
+ 'aaaa~~b~~c~~d~~e~~fg~~hi~~jk~~lmnop~~qrstuv~~~wxyz')
-- stl item testing
local tabline = ''
@@ -452,11 +468,10 @@ describe('buffer functions', function()
-- multi-byte testing
statusline_test('should handle multibyte characters', 10,
- 'Ĉ%=x', 'Ĉ x',
- {expected_byte_length=11})
+ 'Ĉ%=x', 'Ĉ x')
statusline_test('should handle multibyte characters and different fillchars', 10,
'Ą%=mid%=end', 'Ą@mid@@end',
- {fillchar=('@'):byte(), expected_byte_length=11})
+ {fillchar=('@'):byte()})
-- escaping % testing
statusline_test('should handle escape of %', 4, 'abc%%', 'abc%')
diff --git a/test/unit/fixtures/rbuffer.c b/test/unit/fixtures/rbuffer.c
index 3f4062fa18..efa7ab1986 100644
--- a/test/unit/fixtures/rbuffer.c
+++ b/test/unit/fixtures/rbuffer.c
@@ -15,7 +15,7 @@ void ut_rbuffer_each_read_chunk(RBuffer *buf, each_ptr_cb cb)
void ut_rbuffer_each_write_chunk(RBuffer *buf, each_ptr_cb cb)
{
- RBUFFER_UNTIL_FULL(buf, wptr, wcnt) {
+ RBUFFER_UNTIL_FULL(buf, wptr, wcnt) { // -V1044
cb(wptr, wcnt);
rbuffer_produced(buf, wcnt);
}
diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua
index 465b553693..29ea0235be 100644
--- a/test/unit/helpers.lua
+++ b/test/unit/helpers.lua
@@ -2,7 +2,7 @@ local ffi = require('ffi')
local formatc = require('test.unit.formatc')
local Set = require('test.unit.set')
local Preprocess = require('test.unit.preprocess')
-local Paths = require('test.config.paths')
+local Paths = require('test.cmakeconfig.paths')
local global_helpers = require('test.helpers')
local assert = require('luassert')
local say = require('say')
@@ -96,6 +96,7 @@ local init = only_separate(function()
c.func(unpack(c.args))
end
libnvim.time_init()
+ libnvim.fs_init()
libnvim.event_init()
libnvim.early_init(nil)
if child_calls_mod then
@@ -778,7 +779,8 @@ local function cppimport(path)
return cimport(Paths.test_source_path .. '/test/includes/pre/' .. path)
end
-cimport('./src/nvim/types.h', './src/nvim/main.h', './src/nvim/os/time.h')
+cimport('./src/nvim/types.h', './src/nvim/main.h', './src/nvim/os/time.h',
+ './src/nvim/os/fs.h')
local function conv_enum(etab, eval)
local n = tonumber(eval)
diff --git a/test/unit/keymap_spec.lua b/test/unit/keycodes_spec.lua
index 595a19eb17..5bf27c9232 100644
--- a/test/unit/keymap_spec.lua
+++ b/test/unit/keycodes_spec.lua
@@ -5,9 +5,10 @@ local ffi = helpers.ffi
local eq = helpers.eq
local neq = helpers.neq
-local keymap = helpers.cimport("./src/nvim/keymap.h")
+local keymap = helpers.cimport('./src/nvim/keycodes.h')
+local NULL = helpers.NULL
-describe('keymap.c', function()
+describe('keycodes.c', function()
describe('find_special_key()', function()
local srcp = ffi.new('const unsigned char *[1]')
@@ -15,12 +16,12 @@ describe('keymap.c', function()
itp('no keycode', function()
srcp[0] = 'abc'
- eq(0, keymap.find_special_key(srcp, 3, modp, false, false, false))
+ eq(0, keymap.find_special_key(srcp, 3, modp, 0, NULL))
end)
itp('keycode with multiple modifiers', function()
srcp[0] = '<C-M-S-A>'
- neq(0, keymap.find_special_key(srcp, 9, modp, false, false, false))
+ neq(0, keymap.find_special_key(srcp, 9, modp, 0, NULL))
neq(0, modp[0])
end)
@@ -28,22 +29,22 @@ describe('keymap.c', function()
-- Compare other capitalizations to this.
srcp[0] = '<C-A>'
local all_caps_key =
- keymap.find_special_key(srcp, 5, modp, false, false, false)
+ keymap.find_special_key(srcp, 5, modp, 0, NULL)
local all_caps_mod = modp[0]
srcp[0] = '<C-a>'
eq(all_caps_key,
- keymap.find_special_key(srcp, 5, modp, false, false, false))
+ keymap.find_special_key(srcp, 5, modp, 0, NULL))
eq(all_caps_mod, modp[0])
srcp[0] = '<c-A>'
eq(all_caps_key,
- keymap.find_special_key(srcp, 5, modp, false, false, false))
+ keymap.find_special_key(srcp, 5, modp, 0, NULL))
eq(all_caps_mod, modp[0])
srcp[0] = '<c-a>'
eq(all_caps_key,
- keymap.find_special_key(srcp, 5, modp, false, false, false))
+ keymap.find_special_key(srcp, 5, modp, 0, NULL))
eq(all_caps_mod, modp[0])
end)
@@ -51,20 +52,20 @@ describe('keymap.c', function()
-- Unescaped with in_string=false
srcp[0] = '<C-">'
eq(string.byte('"'),
- keymap.find_special_key(srcp, 5, modp, false, false, false))
+ keymap.find_special_key(srcp, 5, modp, 0, NULL))
-- Unescaped with in_string=true
- eq(0, keymap.find_special_key(srcp, 5, modp, false, false, true))
+ eq(0, keymap.find_special_key(srcp, 5, modp, keymap.FSK_IN_STRING, NULL))
-- Escaped with in_string=false
srcp[0] = '<C-\\">'
-- Should fail because the key is invalid
-- (more than 1 non-modifier character).
- eq(0, keymap.find_special_key(srcp, 6, modp, false, false, false))
+ eq(0, keymap.find_special_key(srcp, 6, modp, 0, NULL))
-- Escaped with in_string=true
eq(string.byte('"'),
- keymap.find_special_key(srcp, 6, modp, false, false, true))
+ keymap.find_special_key(srcp, 6, modp, keymap.FSK_IN_STRING, NULL))
end)
end)
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/os/env_spec.lua b/test/unit/os/env_spec.lua
index a0e02b6624..71177f4c65 100644
--- a/test/unit/os/env_spec.lua
+++ b/test/unit/os/env_spec.lua
@@ -154,7 +154,7 @@ describe('env.c', function()
local value = 'TESTVALUE'
os_setenv(name, value, 1)
eq(OK, os_unsetenv(name))
- neq(os_getenv(name), value)
+ neq(value, os_getenv(name))
-- Depending on the platform the var might be unset or set as ''
assert.True(os_getenv(name) == nil or os_getenv(name) == '')
if os_getenv(name) == nil then
@@ -266,7 +266,7 @@ describe('env.c', function()
itp('does not crash #3725', function()
local name_out = ffi.new('char[100]')
- cimp.os_get_user_name(name_out, 100)
+ cimp.os_get_username(name_out, 100)
local curuser = ffi.string(name_out)
local src = to_cstr("~"..curuser.."/Vcs/django-rest-framework/rest_framework/renderers.py")
diff --git a/test/unit/os/users_spec.lua b/test/unit/os/users_spec.lua
index f92413c7de..679e76fae1 100644
--- a/test/unit/os/users_spec.lua
+++ b/test/unit/os/users_spec.lua
@@ -48,10 +48,10 @@ describe('users function', function()
end)
end)
- describe('os_get_user_name', function()
+ describe('os_get_username', function()
itp('should write the username into the buffer and return OK', function()
local name_out = ffi.new('char[100]')
- eq(OK, users.os_get_user_name(name_out, 100))
+ eq(OK, users.os_get_username(name_out, 100))
eq(current_username, ffi.string(name_out))
end)
end)
@@ -73,18 +73,18 @@ describe('users function', function()
end)
end)
- describe('os_get_user_directory', function()
+ describe('os_get_userdir', function()
itp('should return NULL if called with NULL', function()
- eq(NULL, users.os_get_user_directory(NULL))
+ eq(NULL, users.os_get_userdir(NULL))
end)
itp('should return $HOME for the current user', function()
local home = os.getenv('HOME')
- eq(home, ffi.string((users.os_get_user_directory(current_username))))
+ eq(home, ffi.string((users.os_get_userdir(current_username))))
end)
itp('should return NULL if the user is not found', function()
- eq(NULL, users.os_get_user_directory('neovim_user_not_found_test'))
+ eq(NULL, users.os_get_userdir('neovim_user_not_found_test'))
end)
end)
end)
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/test/unit/search_spec.lua b/test/unit/search_spec.lua
index 3c2d485e0e..ce37ebfc3a 100644
--- a/test/unit/search_spec.lua
+++ b/test/unit/search_spec.lua
@@ -5,6 +5,8 @@ local to_cstr = helpers.to_cstr
local eq = helpers.eq
local search = helpers.cimport("./src/nvim/search.h")
+local globals = helpers.cimport('./src/nvim/globals.h')
+local ffi = helpers.ffi
itp('pat_has_uppercase', function()
-- works on empty string
@@ -31,3 +33,25 @@ itp('pat_has_uppercase', function()
eq(false, search.pat_has_uppercase(to_cstr("aa\\%Ab")))
eq(true, search.pat_has_uppercase(to_cstr("aab\\%AU")))
end)
+
+describe('search_regcomp', function()
+ local search_regcomp = function(pat, pat_save, pat_use, options )
+ local regmatch = ffi.new("regmmatch_T")
+ local fail = search.search_regcomp(to_cstr(pat), pat_save, pat_use, options, regmatch)
+ return fail, regmatch
+ end
+
+ local get_search_pat = function()
+ return helpers.internalize(search.get_search_pat())
+ end
+
+ itp("accepts regexp pattern with invalid utf", function()
+ --crafted to call reverse_text with invalid utf
+ globals.curwin.w_onebuf_opt.wo_rl = 1
+ globals.curwin.w_onebuf_opt.wo_rlc = to_cstr('s')
+ globals.cmdmod.cmod_flags = globals.CMOD_KEEPPATTERNS
+ local fail = search_regcomp("a\192", 0,0,0)
+ eq(1, fail)
+ eq("\192a", get_search_pat())
+ end)
+end)
diff --git a/test/unit/strings_spec.lua b/test/unit/strings_spec.lua
index e54c82b26a..b2c839f25c 100644
--- a/test/unit/strings_spec.lua
+++ b/test/unit/strings_spec.lua
@@ -138,3 +138,50 @@ describe('vim_strchr()', function()
eq(nil, vim_strchr('«\237\175\191\237\188\128»', 0x10FF00))
end)
end)
+
+describe('strcase_save()' , function()
+ local strcase_save = function(input_string, upper)
+ local res = strings.strcase_save(to_cstr(input_string), upper)
+ return ffi.string(res)
+ end
+
+ itp('decodes overlong encoded characters.', function()
+ eq("A", strcase_save("\xc1\x81", true))
+ eq("a", strcase_save("\xc1\x81", false))
+ end)
+end)
+
+describe("reverse_text", function()
+ local reverse_text = function(str)
+ return helpers.internalize(strings.reverse_text(to_cstr(str)))
+ end
+
+ itp("handles empty string", function()
+ eq("", reverse_text(""))
+ end)
+
+ itp("handles simple cases", function()
+ eq("a", reverse_text("a"))
+ eq("ba", reverse_text("ab"))
+ end)
+
+ itp("handles multibyte characters", function()
+ eq("bα", reverse_text("αb"))
+ eq("Yötön yö", reverse_text("öy nötöY"))
+ end)
+
+ itp("handles combining chars", function()
+ local utf8_COMBINING_RING_ABOVE = "\204\138"
+ local utf8_COMBINING_RING_BELOW = "\204\165"
+ eq("bba" .. utf8_COMBINING_RING_ABOVE .. utf8_COMBINING_RING_BELOW .. "aa",
+ reverse_text("aaa" .. utf8_COMBINING_RING_ABOVE .. utf8_COMBINING_RING_BELOW .. "bb"))
+ end)
+
+ itp("treats invalid utf as separate characters", function()
+ eq("\192ba", reverse_text("ab\192"))
+ end)
+
+ itp("treats an incomplete utf continuation sequence as valid", function()
+ eq("\194ba", reverse_text("ab\194"))
+ end)
+end)
diff --git a/test/unit/tempfile_spec.lua b/test/unit/tempfile_spec.lua
index c05abfd640..44bd19c1d2 100644
--- a/test/unit/tempfile_spec.lua
+++ b/test/unit/tempfile_spec.lua
@@ -24,7 +24,7 @@ describe('tempfile related functions', function()
end
describe('vim_gettempdir', function()
- itp('returns path to Neovim own temp directory', function()
+ itp('returns path to Nvim own temp directory', function()
local dir = vim_gettempdir()
assert.True(dir ~= nil and dir:len() > 0)
-- os_file_is_writable returns 2 for a directory which we have rights
@@ -36,9 +36,7 @@ describe('tempfile related functions', function()
end)
itp('returns the same directory on each call', function()
- local dir1 = vim_gettempdir()
- local dir2 = vim_gettempdir()
- eq(dir1, dir2)
+ eq(vim_gettempdir(), vim_gettempdir())
end)
end)
@@ -54,12 +52,10 @@ describe('tempfile related functions', function()
end)
itp('generate different names on each call', function()
- local fst = vim_tempname()
- local snd = vim_tempname()
- neq(fst, snd)
+ neq(vim_tempname(), vim_tempname())
end)
- itp('generate file name in Neovim own temp directory', function()
+ itp('generate file name in Nvim own temp directory', function()
local dir = vim_gettempdir()
local file = vim_tempname()
eq(string.sub(file, 1, string.len(dir)), dir)
diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua
index 8342044b5e..51a703b593 100644
--- a/test/unit/viml/expressions/parser_spec.lua
+++ b/test/unit/viml/expressions/parser_spec.lua
@@ -48,6 +48,7 @@ local predefined_hl_defs = {
TermCursor=true,
VertSplit=true,
WildMenu=true,
+ WinSeparator=true,
EndOfBuffer=true,
QuickFixLine=true,
Substitute=true,
diff --git a/third-party/cmake/BuildGperf.cmake b/third-party/cmake/BuildGperf.cmake
deleted file mode 100644
index 71c3cc1eef..0000000000
--- a/third-party/cmake/BuildGperf.cmake
+++ /dev/null
@@ -1,67 +0,0 @@
-# Gperf recipe. Gperf is only required when building Neovim, when
-# cross compiling we still want to build for the HOST system, whenever
-# writing a recipe that is meant for cross-compile, use the HOSTDEPS_* variables
-# instead of DEPS_* - check the main CMakeLists.txt for a list.
-
-# BuildGperf(CONFIGURE_COMMAND ... BUILD_COMMAND ... INSTALL_COMMAND ...)
-# Reusable function to build Gperf, wraps ExternalProject_Add.
-# Failing to pass a command argument will result in no command being run
-function(BuildGperf)
- cmake_parse_arguments(_gperf
- ""
- ""
- "CONFIGURE_COMMAND;BUILD_COMMAND;INSTALL_COMMAND"
- ${ARGN})
-
- if(NOT _gperf_CONFIGURE_COMMAND AND NOT _gperf_BUILD_COMMAND
- AND NOT _gperf_INSTALL_COMMAND)
- message(FATAL_ERROR "Must pass at least one of CONFIGURE_COMMAND, BUILD_COMMAND, INSTALL_COMMAND")
- endif()
-
- ExternalProject_Add(gperf
- PREFIX ${DEPS_BUILD_DIR}
- URL ${GPERF_URL}
- DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/gperf
- DOWNLOAD_COMMAND ${CMAKE_COMMAND}
- -DPREFIX=${DEPS_BUILD_DIR}
- -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/gperf
- -DURL=${GPERF_URL}
- -DEXPECTED_SHA256=${GPERF_SHA256}
- -DTARGET=gperf
- -DUSE_EXISTING_SRC_DIR=${USE_EXISTING_SRC_DIR}
- -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake
- BUILD_IN_SOURCE 1
- CONFIGURE_COMMAND "${_gperf_CONFIGURE_COMMAND}"
- BUILD_COMMAND "${_gperf_BUILD_COMMAND}"
- INSTALL_COMMAND "${_gperf_INSTALL_COMMAND}")
-endfunction()
-
-set(GPERF_BUILDARGS CC=${HOSTDEPS_C_COMPILER} CXX=${HOSTDEPS_CXX_COMPILER}
- LD=${HOSTDEPS_C_COMPILER})
-
-if(UNIX OR (MINGW AND CMAKE_CROSSCOMPILING))
-
- BuildGperf(
- CONFIGURE_COMMAND ${DEPS_BUILD_DIR}/src/gperf/configure
- --prefix=${HOSTDEPS_INSTALL_DIR}
- MAKE=${MAKE_PRG} ${GPERF_BUILDARGS}
- INSTALL_COMMAND ${MAKE_PRG} install)
-
-elseif(MSVC OR MINGW)
-
- BuildGperf(
- CONFIGURE_COMMAND ${CMAKE_COMMAND} -E copy
- ${CMAKE_CURRENT_SOURCE_DIR}/cmake/GperfCMakeLists.txt
- ${DEPS_BUILD_DIR}/src/gperf/CMakeLists.txt
- COMMAND ${CMAKE_COMMAND} ${DEPS_BUILD_DIR}/src/gperf/CMakeLists.txt
- -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
- -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
- -DCMAKE_GENERATOR=${CMAKE_GENERATOR}
- -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
- -DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
- BUILD_COMMAND ${CMAKE_COMMAND} --build . --config ${CMAKE_BUILD_TYPE}
- INSTALL_COMMAND ${CMAKE_COMMAND} --build . --target install --config ${CMAKE_BUILD_TYPE})
-
-else()
- message(FATAL_ERROR "Trying to build gperf in an unsupported system ${CMAKE_SYSTEM_NAME}/${CMAKE_C_COMPILER_ID}")
-endif()
diff --git a/third-party/cmake/GperfCMakeLists.txt b/third-party/cmake/GperfCMakeLists.txt
deleted file mode 100644
index 15ae305ba8..0000000000
--- a/third-party/cmake/GperfCMakeLists.txt
+++ /dev/null
@@ -1,30 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12)
-project(gperf LANGUAGES C CXX)
-
-add_executable(gperf
- lib/getline.cc
- lib/hash.cc
- lib/getopt.c
- lib/getopt1.c
- src/version.cc
- src/positions.cc
- src/options.cc
- src/keyword.cc
- src/keyword-list.cc
- src/input.cc
- src/bool-array.cc
- src/hash-table.cc
- src/search.cc
- src/output.cc
- src/main.cc)
-
-include_directories(lib)
-
-# Copy the config.h template without modifying it
-# because none of the definitions are necessary
-execute_process(COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/src/config.h.in ${CMAKE_BINARY_DIR}/config/config.h)
-include_directories(${CMAKE_BINARY_DIR}/config)
-
-include(GNUInstallDirs)
-install(TARGETS gperf
- RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/third-party/patches/libuv-disable-typedef-MinGW.patch b/third-party/patches/libuv-disable-typedef-MinGW.patch
deleted file mode 100644
index a47893cede..0000000000
--- a/third-party/patches/libuv-disable-typedef-MinGW.patch
+++ /dev/null
@@ -1,19 +0,0 @@
-diff --git a/include/uv/win.h b/include/uv/win.h
-index f5f1d3a3..64a0dfd9 100644
---- a/include/uv/win.h
-+++ b/include/uv/win.h
-@@ -45,7 +45,14 @@ typedef struct pollfd {
- #endif
-
- #include <mswsock.h>
-+// Disable the typedef in mstcpip.h of MinGW.
-+#define _TCP_INITIAL_RTO_PARAMETERS _TCP_INITIAL_RTO_PARAMETERS__
-+#define TCP_INITIAL_RTO_PARAMETERS TCP_INITIAL_RTO_PARAMETERS__
-+#define PTCP_INITIAL_RTO_PARAMETERS PTCP_INITIAL_RTO_PARAMETERS__
- #include <ws2tcpip.h>
-+#undef _TCP_INITIAL_RTO_PARAMETERS
-+#undef TCP_INITIAL_RTO_PARAMETERS
-+#undef PTCP_INITIAL_RTO_PARAMETERS
- #include <windows.h>
-
- #include <process.h>